From ea2df2601e81aa9e490459ad9c65b03de2be61d0 Mon Sep 17 00:00:00 2001 From: fifty-six Date: Thu, 13 Jan 2022 05:21:42 -0500 Subject: [PATCH 0001/2031] std/os/uefi: Use `usingnamespace` to re-export symbols `uefi/protocols.zig` and `uefi/tables.zig` just re-exported all the public symbols, which is basically the purpose of `usingnamespace` import-wise. --- lib/std/os/uefi/protocols.zig | 124 ++++++++++------------------------ lib/std/os/uefi/tables.zig | 19 ++---- 2 files changed, 39 insertions(+), 104 deletions(-) diff --git a/lib/std/os/uefi/protocols.zig b/lib/std/os/uefi/protocols.zig index 4192b4a545..0c027f046b 100644 --- a/lib/std/os/uefi/protocols.zig +++ b/lib/std/os/uefi/protocols.zig @@ -1,100 +1,44 @@ -pub const LoadedImageProtocol = @import("protocols/loaded_image_protocol.zig").LoadedImageProtocol; -pub const loaded_image_device_path_protocol_guid = @import("protocols/loaded_image_protocol.zig").loaded_image_device_path_protocol_guid; +// Misc +pub usingnamespace @import("protocols/loaded_image_protocol.zig"); +pub usingnamespace @import("protocols/device_path_protocol.zig"); +pub usingnamespace @import("protocols/rng_protocol.zig"); +pub usingnamespace @import("protocols/shell_parameters_protocol.zig"); -pub const AcpiDevicePath = @import("protocols/device_path_protocol.zig").AcpiDevicePath; -pub const BiosBootSpecificationDevicePath = @import("protocols/device_path_protocol.zig").BiosBootSpecificationDevicePath; -pub const DevicePath = @import("protocols/device_path_protocol.zig").DevicePath; -pub const DevicePathProtocol = @import("protocols/device_path_protocol.zig").DevicePathProtocol; -pub const DevicePathType = @import("protocols/device_path_protocol.zig").DevicePathType; -pub const EndDevicePath = @import("protocols/device_path_protocol.zig").EndDevicePath; -pub const HardwareDevicePath = @import("protocols/device_path_protocol.zig").HardwareDevicePath; -pub const MediaDevicePath = @import("protocols/device_path_protocol.zig").MediaDevicePath; -pub const MessagingDevicePath = @import("protocols/device_path_protocol.zig").MessagingDevicePath; +// Files +pub usingnamespace @import("protocols/simple_file_system_protocol.zig"); +pub usingnamespace @import("protocols/file_protocol.zig"); -pub const SimpleFileSystemProtocol = @import("protocols/simple_file_system_protocol.zig").SimpleFileSystemProtocol; -pub const FileProtocol = @import("protocols/file_protocol.zig").FileProtocol; -pub const FileInfo = @import("protocols/file_protocol.zig").FileInfo; -pub const FileSystemInfo = @import("protocols/file_protocol.zig").FileSystemInfo; +// Text +pub usingnamespace @import("protocols/simple_text_input_protocol.zig"); +pub usingnamespace @import("protocols/simple_text_input_ex_protocol.zig"); +pub usingnamespace @import("protocols/simple_text_output_protocol.zig"); -pub const InputKey = @import("protocols/simple_text_input_ex_protocol.zig").InputKey; -pub const KeyData = @import("protocols/simple_text_input_ex_protocol.zig").KeyData; -pub const KeyState = @import("protocols/simple_text_input_ex_protocol.zig").KeyState; -pub const SimpleTextInputProtocol = @import("protocols/simple_text_input_protocol.zig").SimpleTextInputProtocol; -pub const SimpleTextInputExProtocol = @import("protocols/simple_text_input_ex_protocol.zig").SimpleTextInputExProtocol; +// Pointer +pub usingnamespace @import("protocols/simple_pointer_protocol.zig"); +pub usingnamespace @import("protocols/absolute_pointer_protocol.zig"); -pub const SimpleTextOutputMode = @import("protocols/simple_text_output_protocol.zig").SimpleTextOutputMode; -pub const SimpleTextOutputProtocol = @import("protocols/simple_text_output_protocol.zig").SimpleTextOutputProtocol; +pub usingnamespace @import("protocols/graphics_output_protocol.zig"); -pub const SimplePointerMode = @import("protocols/simple_pointer_protocol.zig").SimplePointerMode; -pub const SimplePointerProtocol = @import("protocols/simple_pointer_protocol.zig").SimplePointerProtocol; -pub const SimplePointerState = @import("protocols/simple_pointer_protocol.zig").SimplePointerState; +// edid +pub usingnamespace @import("protocols/edid_discovered_protocol.zig"); +pub usingnamespace @import("protocols/edid_active_protocol.zig"); +pub usingnamespace @import("protocols/edid_override_protocol.zig"); -pub const AbsolutePointerMode = @import("protocols/absolute_pointer_protocol.zig").AbsolutePointerMode; -pub const AbsolutePointerProtocol = @import("protocols/absolute_pointer_protocol.zig").AbsolutePointerProtocol; -pub const AbsolutePointerState = @import("protocols/absolute_pointer_protocol.zig").AbsolutePointerState; +// Network +pub usingnamespace @import("protocols/simple_network_protocol.zig"); +pub usingnamespace @import("protocols/managed_network_service_binding_protocol.zig"); +pub usingnamespace @import("protocols/managed_network_protocol.zig"); -pub const GraphicsOutputBltPixel = @import("protocols/graphics_output_protocol.zig").GraphicsOutputBltPixel; -pub const GraphicsOutputBltOperation = @import("protocols/graphics_output_protocol.zig").GraphicsOutputBltOperation; -pub const GraphicsOutputModeInformation = @import("protocols/graphics_output_protocol.zig").GraphicsOutputModeInformation; -pub const GraphicsOutputProtocol = @import("protocols/graphics_output_protocol.zig").GraphicsOutputProtocol; -pub const GraphicsOutputProtocolMode = @import("protocols/graphics_output_protocol.zig").GraphicsOutputProtocolMode; -pub const GraphicsPixelFormat = @import("protocols/graphics_output_protocol.zig").GraphicsPixelFormat; -pub const PixelBitmask = @import("protocols/graphics_output_protocol.zig").PixelBitmask; +// ip6 +pub usingnamespace @import("protocols/ip6_service_binding_protocol.zig"); +pub usingnamespace @import("protocols/ip6_protocol.zig"); +pub usingnamespace @import("protocols/ip6_config_protocol.zig"); -pub const EdidDiscoveredProtocol = @import("protocols/edid_discovered_protocol.zig").EdidDiscoveredProtocol; - -pub const EdidActiveProtocol = @import("protocols/edid_active_protocol.zig").EdidActiveProtocol; - -pub const EdidOverrideProtocol = @import("protocols/edid_override_protocol.zig").EdidOverrideProtocol; -pub const EdidOverrideProtocolAttributes = @import("protocols/edid_override_protocol.zig").EdidOverrideProtocolAttributes; - -pub const SimpleNetworkProtocol = @import("protocols/simple_network_protocol.zig").SimpleNetworkProtocol; -pub const MacAddress = @import("protocols/simple_network_protocol.zig").MacAddress; -pub const SimpleNetworkMode = @import("protocols/simple_network_protocol.zig").SimpleNetworkMode; -pub const SimpleNetworkReceiveFilter = @import("protocols/simple_network_protocol.zig").SimpleNetworkReceiveFilter; -pub const SimpleNetworkState = @import("protocols/simple_network_protocol.zig").SimpleNetworkState; -pub const NetworkStatistics = @import("protocols/simple_network_protocol.zig").NetworkStatistics; -pub const SimpleNetworkInterruptStatus = @import("protocols/simple_network_protocol.zig").SimpleNetworkInterruptStatus; - -pub const ManagedNetworkServiceBindingProtocol = @import("protocols/managed_network_service_binding_protocol.zig").ManagedNetworkServiceBindingProtocol; -pub const ManagedNetworkProtocol = @import("protocols/managed_network_protocol.zig").ManagedNetworkProtocol; -pub const ManagedNetworkConfigData = @import("protocols/managed_network_protocol.zig").ManagedNetworkConfigData; -pub const ManagedNetworkCompletionToken = @import("protocols/managed_network_protocol.zig").ManagedNetworkCompletionToken; -pub const ManagedNetworkReceiveData = @import("protocols/managed_network_protocol.zig").ManagedNetworkReceiveData; -pub const ManagedNetworkTransmitData = @import("protocols/managed_network_protocol.zig").ManagedNetworkTransmitData; -pub const ManagedNetworkFragmentData = @import("protocols/managed_network_protocol.zig").ManagedNetworkFragmentData; - -pub const Ip6ServiceBindingProtocol = @import("protocols/ip6_service_binding_protocol.zig").Ip6ServiceBindingProtocol; -pub const Ip6Protocol = @import("protocols/ip6_protocol.zig").Ip6Protocol; -pub const Ip6ModeData = @import("protocols/ip6_protocol.zig").Ip6ModeData; -pub const Ip6ConfigData = @import("protocols/ip6_protocol.zig").Ip6ConfigData; -pub const Ip6Address = @import("protocols/ip6_protocol.zig").Ip6Address; -pub const Ip6AddressInfo = @import("protocols/ip6_protocol.zig").Ip6AddressInfo; -pub const Ip6RouteTable = @import("protocols/ip6_protocol.zig").Ip6RouteTable; -pub const Ip6NeighborState = @import("protocols/ip6_protocol.zig").Ip6NeighborState; -pub const Ip6NeighborCache = @import("protocols/ip6_protocol.zig").Ip6NeighborCache; -pub const Ip6IcmpType = @import("protocols/ip6_protocol.zig").Ip6IcmpType; -pub const Ip6CompletionToken = @import("protocols/ip6_protocol.zig").Ip6CompletionToken; - -pub const Ip6ConfigProtocol = @import("protocols/ip6_config_protocol.zig").Ip6ConfigProtocol; -pub const Ip6ConfigDataType = @import("protocols/ip6_config_protocol.zig").Ip6ConfigDataType; - -pub const Udp6ServiceBindingProtocol = @import("protocols/udp6_service_binding_protocol.zig").Udp6ServiceBindingProtocol; -pub const Udp6Protocol = @import("protocols/udp6_protocol.zig").Udp6Protocol; -pub const Udp6ConfigData = @import("protocols/udp6_protocol.zig").Udp6ConfigData; -pub const Udp6CompletionToken = @import("protocols/udp6_protocol.zig").Udp6CompletionToken; -pub const Udp6ReceiveData = @import("protocols/udp6_protocol.zig").Udp6ReceiveData; -pub const Udp6TransmitData = @import("protocols/udp6_protocol.zig").Udp6TransmitData; -pub const Udp6SessionData = @import("protocols/udp6_protocol.zig").Udp6SessionData; -pub const Udp6FragmentData = @import("protocols/udp6_protocol.zig").Udp6FragmentData; +// udp6 +pub usingnamespace @import("protocols/udp6_service_binding_protocol.zig"); +pub usingnamespace @import("protocols/udp6_protocol.zig"); +// hii pub const hii = @import("protocols/hii.zig"); -pub const HIIDatabaseProtocol = @import("protocols/hii_database_protocol.zig").HIIDatabaseProtocol; -pub const HIIPopupProtocol = @import("protocols/hii_popup_protocol.zig").HIIPopupProtocol; -pub const HIIPopupStyle = @import("protocols/hii_popup_protocol.zig").HIIPopupStyle; -pub const HIIPopupType = @import("protocols/hii_popup_protocol.zig").HIIPopupType; -pub const HIIPopupSelection = @import("protocols/hii_popup_protocol.zig").HIIPopupSelection; - -pub const RNGProtocol = @import("protocols/rng_protocol.zig").RNGProtocol; - -pub const ShellParametersProtocol = @import("protocols/shell_parameters_protocol.zig").ShellParametersProtocol; +pub usingnamespace @import("protocols/hii_database_protocol.zig"); +pub usingnamespace @import("protocols/hii_popup_protocol.zig"); diff --git a/lib/std/os/uefi/tables.zig b/lib/std/os/uefi/tables.zig index 0011c80a9c..a65e5789c7 100644 --- a/lib/std/os/uefi/tables.zig +++ b/lib/std/os/uefi/tables.zig @@ -1,14 +1,5 @@ -pub const AllocateType = @import("tables/boot_services.zig").AllocateType; -pub const BootServices = @import("tables/boot_services.zig").BootServices; -pub const ConfigurationTable = @import("tables/configuration_table.zig").ConfigurationTable; -pub const global_variable align(8) = @import("tables/runtime_services.zig").global_variable; -pub const LocateSearchType = @import("tables/boot_services.zig").LocateSearchType; -pub const MemoryDescriptor = @import("tables/boot_services.zig").MemoryDescriptor; -pub const MemoryType = @import("tables/boot_services.zig").MemoryType; -pub const OpenProtocolAttributes = @import("tables/boot_services.zig").OpenProtocolAttributes; -pub const ProtocolInformationEntry = @import("tables/boot_services.zig").ProtocolInformationEntry; -pub const ResetType = @import("tables/runtime_services.zig").ResetType; -pub const RuntimeServices = @import("tables/runtime_services.zig").RuntimeServices; -pub const SystemTable = @import("tables/system_table.zig").SystemTable; -pub const TableHeader = @import("tables/table_header.zig").TableHeader; -pub const TimerDelay = @import("tables/boot_services.zig").TimerDelay; +pub usingnamespace @import("tables/boot_services.zig"); +pub usingnamespace @import("tables/runtime_services.zig"); +pub usingnamespace @import("tables/configuration_table.zig"); +pub usingnamespace @import("tables/system_table.zig"); +pub usingnamespace @import("tables/table_header.zig"); From 649b872450689511ac3c5526d063c89fff7d898a Mon Sep 17 00:00:00 2001 From: fifty-six Date: Thu, 13 Jan 2022 05:23:39 -0500 Subject: [PATCH 0002/2031] std/builtin: improve panic handler for uefi Writes the panic message to stderr as well as passing it to boot_services.exit when boot_services is available. --- lib/std/builtin.zig | 47 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 7fcd9a369b..b69b31ae17 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -769,7 +769,52 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn std.os.abort(); }, .uefi => { - // TODO look into using the debug info and logging helpful messages + const uefi = std.os.uefi; + + const ExitData = struct { + pub fn create_exit_data(exit_msg: []const u8, exit_size: *usize) ![*:0]u16 { + // Need boot services for pool allocation + if (uefi.system_table.boot_services == null) { + return error.BootServicesUnavailable; + } + + // ExitData buffer must be allocated using boot_services.allocatePool + var utf16: []u16 = try uefi.raw_pool_allocator.alloc(u16, 256); + errdefer uefi.raw_pool_allocator.free(utf16); + + if (exit_msg.len > 255) { + return error.MessageTooLong; + } + + var fmt: [256]u8 = undefined; + var slice = try std.fmt.bufPrint(&fmt, "\r\nerr: {s}\r\n", .{exit_msg}); + + var len = try std.unicode.utf8ToUtf16Le(utf16, slice); + + utf16[len] = 0; + + exit_size.* = 256; + + return @ptrCast([*:0]u16, utf16.ptr); + } + }; + + var exit_size: usize = 0; + var exit_data = ExitData.create_exit_data(msg, &exit_size) catch null; + + if (exit_data) |data| { + if (uefi.system_table.std_err) |out| { + _ = out.setAttribute(uefi.protocols.SimpleTextOutputProtocol.red); + _ = out.outputString(data); + _ = out.setAttribute(uefi.protocols.SimpleTextOutputProtocol.white); + } + } + + if (uefi.system_table.boot_services) |bs| { + _ = bs.exit(uefi.handle, .Aborted, exit_size, exit_data); + } + + // Didn't have boot_services, just fallback to whatever. std.os.abort(); }, else => { From fe28cb8261eeb7e3094d38f2c5fb2012fbf205d6 Mon Sep 17 00:00:00 2001 From: fifty-six Date: Thu, 13 Jan 2022 05:54:24 -0500 Subject: [PATCH 0003/2031] std/os/uefi: Fill out remaining function signatures and docs on boot_services --- lib/std/os/uefi/tables/boot_services.zig | 44 ++++++++++++++++++------ 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/lib/std/os/uefi/tables/boot_services.zig b/lib/std/os/uefi/tables/boot_services.zig index 75daf6feb2..a41f2dcdb9 100644 --- a/lib/std/os/uefi/tables/boot_services.zig +++ b/lib/std/os/uefi/tables/boot_services.zig @@ -59,23 +59,34 @@ pub const BootServices = extern struct { /// Checks whether an event is in the signaled state. checkEvent: fn (Event) callconv(.C) Status, - installProtocolInterface: Status, // TODO - reinstallProtocolInterface: Status, // TODO - uninstallProtocolInterface: Status, // TODO + /// Installs a protocol interface on a device handle. If the handle does not exist, it is created + /// and added to the list of handles in the system. installMultipleProtocolInterfaces() + /// performs more error checking than installProtocolInterface(), so its use is recommended over this. + installProtocolInterface: fn (Handle, *align(8) const Guid, EfiInterfaceType, *anyopaque) callconv(.C) Status, + + /// Reinstalls a protocol interface on a device handle + reinstallProtocolInterface: fn (Handle, *align(8) const Guid, *anyopaque, *anyopaque) callconv(.C) Status, + + /// Removes a protocol interface from a device handle. Usage of + /// uninstallMultipleProtocolInterfaces is recommended over this. + uninstallProtocolInterface: fn (Handle, *align(8) const Guid, *anyopaque) callconv(.C) Status, /// Queries a handle to determine if it supports a specified protocol. handleProtocol: fn (Handle, *align(8) const Guid, *?*anyopaque) callconv(.C) Status, reserved: *anyopaque, - registerProtocolNotify: Status, // TODO + /// Creates an event that is to be signaled whenever an interface is installed for a specified protocol. + registerProtocolNotify: fn (*align(8) const Guid, Event, **anyopaque) callconv(.C) Status, /// Returns an array of handles that support a specified protocol. locateHandle: fn (LocateSearchType, ?*align(8) const Guid, ?*const anyopaque, *usize, [*]Handle) callconv(.C) Status, /// Locates the handle to a device on the device path that supports the specified protocol locateDevicePath: fn (*align(8) const Guid, **const DevicePathProtocol, *?Handle) callconv(.C) Status, - installConfigurationTable: Status, // TODO + + /// Adds, updates, or removes a configuration table entry from the EFI System Table. + installConfigurationTable: fn (*align(8) const Guid, ?*anyopaque) callconv(.C) Status, /// Loads an EFI image into memory. loadImage: fn (bool, Handle, ?*const DevicePathProtocol, ?[*]const u8, usize, *?Handle) callconv(.C) Status, @@ -101,8 +112,11 @@ pub const BootServices = extern struct { /// Sets the system's watchdog timer. setWatchdogTimer: fn (usize, u64, usize, ?[*]const u16) callconv(.C) Status, - connectController: Status, // TODO - disconnectController: Status, // TODO + /// Connects one or more drives to a controller. + connectController: fn (Handle, ?Handle, ?*DevicePathProtocol, bool) callconv(.C) Status, + + // Disconnects one or more drivers from a controller + disconnectController: fn (Handle, ?Handle, ?Handle) callconv(.C) Status, /// Queries a handle to determine if it supports a specified protocol. openProtocol: fn (Handle, *align(8) const Guid, *?*anyopaque, ?Handle, ?Handle, OpenProtocolAttributes) callconv(.C) Status, @@ -122,8 +136,11 @@ pub const BootServices = extern struct { /// Returns the first protocol instance that matches the given protocol. locateProtocol: fn (*align(8) const Guid, ?*const anyopaque, *?*anyopaque) callconv(.C) Status, - installMultipleProtocolInterfaces: Status, // TODO - uninstallMultipleProtocolInterfaces: Status, // TODO + /// Installs one or more protocol interfaces into the boot services environment + installMultipleProtocolInterfaces: fn (*Handle, ...) callconv(.C) Status, + + /// Removes one or more protocol interfaces into the boot services environment + uninstallMultipleProtocolInterfaces: fn (*Handle, ...) callconv(.C) Status, /// Computes and returns a 32-bit CRC for a data buffer. calculateCrc32: fn ([*]const u8, usize, *u32) callconv(.C) Status, @@ -134,7 +151,8 @@ pub const BootServices = extern struct { /// Fills a buffer with a specified value setMem: fn ([*]u8, usize, u8) callconv(.C) void, - createEventEx: Status, // TODO + /// Creates an event in a group. + createEventEx: fn (u32, usize, EfiEventNotify, *const anyopaque, *align(8) const Guid, *Event) callconv(.C) Status, pub const signature: u64 = 0x56524553544f4f42; @@ -151,6 +169,8 @@ pub const BootServices = extern struct { pub const tpl_high_level: usize = 31; }; +pub const EfiEventNotify = fn (event: Event, ctx: *anyopaque) callconv(.C) void; + pub const TimerDelay = enum(u32) { TimerCancel, TimerPeriodic, @@ -231,6 +251,10 @@ pub const ProtocolInformationEntry = extern struct { open_count: u32, }; +pub const EfiInterfaceType = enum(u32) { + EfiNativeInterface, +}; + pub const AllocateType = enum(u32) { AllocateAnyPages, AllocateMaxAddress, From 88687645b2a5c34304c36ded31d5164784aa207a Mon Sep 17 00:00:00 2001 From: fifty-six Date: Thu, 13 Jan 2022 11:19:22 -0500 Subject: [PATCH 0004/2031] std/os/uefi: Fill out remaining runtime services and add parameter names --- lib/std/os/uefi/tables/runtime_services.zig | 60 ++++++++++++++++----- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/lib/std/os/uefi/tables/runtime_services.zig b/lib/std/os/uefi/tables/runtime_services.zig index 1250894180..f238951553 100644 --- a/lib/std/os/uefi/tables/runtime_services.zig +++ b/lib/std/os/uefi/tables/runtime_services.zig @@ -18,39 +18,71 @@ pub const RuntimeServices = extern struct { hdr: TableHeader, /// Returns the current time and date information, and the time-keeping capabilities of the hardware platform. - getTime: fn (*uefi.Time, ?*TimeCapabilities) callconv(.C) Status, + getTime: fn (time: *uefi.Time, capabilities: ?*TimeCapabilities) callconv(.C) Status, - setTime: Status, // TODO - getWakeupTime: Status, // TODO - setWakeupTime: Status, // TODO + /// Sets the current local time and date information + setTime: fn (time: *uefi.Time) callconv(.C) Status, + + /// Returns the current wakeup alarm clock setting + getWakeupTime: fn (enabled: *bool, pending: *bool, time: *uefi.Time) callconv(.C) Status, + + /// Sets the system wakeup alarm clock time + setWakeupTime: fn (enable: *bool, time: ?*uefi.Time) callconv(.C) Status, /// Changes the runtime addressing mode of EFI firmware from physical to virtual. - setVirtualAddressMap: fn (usize, usize, u32, [*]MemoryDescriptor) callconv(.C) Status, + setVirtualAddressMap: fn (mmap_size: usize, descriptor_size: usize, descriptor_version: u32, virtual_map: [*]MemoryDescriptor) callconv(.C) Status, /// Determines the new virtual address that is to be used on subsequent memory accesses. - convertPointer: fn (usize, **anyopaque) callconv(.C) Status, + convertPointer: fn (debug_disposition: usize, address: **anyopaque) callconv(.C) Status, /// Returns the value of a variable. - getVariable: fn ([*:0]const u16, *align(8) const Guid, ?*u32, *usize, ?*anyopaque) callconv(.C) Status, + getVariable: fn (var_name: [*:0]const u16, vendor_guid: *align(8) const Guid, attributes: ?*u32, data_size: *usize, data: ?*anyopaque) callconv(.C) Status, /// Enumerates the current variable names. - getNextVariableName: fn (*usize, [*:0]u16, *align(8) Guid) callconv(.C) Status, + getNextVariableName: fn (var_name_size: *usize, var_name: [*:0]u16, vendor_guid: *align(8) Guid) callconv(.C) Status, /// Sets the value of a variable. - setVariable: fn ([*:0]const u16, *align(8) const Guid, u32, usize, *anyopaque) callconv(.C) Status, + setVariable: fn (var_name: [*:0]const u16, vendor_guid: *align(8) const Guid, attributes: u32, data_size: usize, data: *anyopaque) callconv(.C) Status, - getNextHighMonotonicCount: Status, // TODO + /// Return the next high 32 bits of the platform's monotonic counter + getNextHighMonotonicCount: fn (high_count: *u32) callconv(.C) Status, /// Resets the entire platform. - resetSystem: fn (ResetType, Status, usize, ?*const anyopaque) callconv(.C) noreturn, + resetSystem: fn (reset_type: ResetType, reset_status: Status, data_size: usize, reset_data: ?*const anyopaque) callconv(.C) noreturn, - updateCapsule: Status, // TODO - queryCapsuleCapabilities: Status, // TODO - queryVariableInfo: Status, // TODO + /// Passes capsules to the firmware with both virtual and physical mapping. + /// Depending on the intended consumption, the firmware may process the capsule immediately. + /// If the payload should persist across a system reset, the reset value returned from + /// `queryCapsuleCapabilities` must be passed into resetSystem and will cause the capsule + /// to be processed by the firmware as part of the reset process. + updateCapsule: fn (capsule_header_array: **CapsuleHeader, capsule_count: usize, scatter_gather_list: EfiPhysicalAddress) callconv(.C) Status, + + /// Returns if the capsule can be supported via `updateCapsule` + queryCapsuleCapabilities: fn (capsule_header_array: **CapsuleHeader, capsule_count: usize, maximum_capsule_size: *usize, resetType: ResetType) callconv(.C) Status, + + /// Returns information about the EFI variables + queryVariableInfo: fn (attributes: *u32, maximum_variable_storage_size: *u64, remaining_variable_storage_size: *u64, maximum_variable_size: *u64) callconv(.C) Status, pub const signature: u64 = 0x56524553544e5552; }; +const EfiPhysicalAddress = u64; + +pub const CapsuleHeader = extern struct { + capsuleGuid: Guid align(8), + headerSize: u32, + flags: u32, + capsuleImageSize: u32, +}; + +pub const UefiCapsuleBlockDescriptor = extern struct { + length: u64, + address: union { + dataBlock: EfiPhysicalAddress, + continuationPointer: EfiPhysicalAddress, + }, +}; + pub const ResetType = enum(u32) { ResetCold, ResetWarm, From 322757c4e13acbe866e6b3431cd3b6b8ce68efb7 Mon Sep 17 00:00:00 2001 From: fifty-six Date: Thu, 13 Jan 2022 11:48:25 -0500 Subject: [PATCH 0005/2031] std/os/uefi: Add parameter names to boot_services --- lib/std/os/uefi/tables/boot_services.zig | 86 ++++++++++++------------ 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/lib/std/os/uefi/tables/boot_services.zig b/lib/std/os/uefi/tables/boot_services.zig index a41f2dcdb9..7da007b05d 100644 --- a/lib/std/os/uefi/tables/boot_services.zig +++ b/lib/std/os/uefi/tables/boot_services.zig @@ -21,138 +21,138 @@ pub const BootServices = extern struct { hdr: TableHeader, /// Raises a task's priority level and returns its previous level. - raiseTpl: fn (usize) callconv(.C) usize, + raiseTpl: fn (new_tpl: usize) callconv(.C) usize, /// Restores a task's priority level to its previous value. - restoreTpl: fn (usize) callconv(.C) void, + restoreTpl: fn (old_tpl: usize) callconv(.C) void, /// Allocates memory pages from the system. - allocatePages: fn (AllocateType, MemoryType, usize, *[*]align(4096) u8) callconv(.C) Status, + allocatePages: fn (alloc_type: AllocateType, mem_type: MemoryType, pages: usize, memory: *[*]align(4096) u8) callconv(.C) Status, /// Frees memory pages. - freePages: fn ([*]align(4096) u8, usize) callconv(.C) Status, + freePages: fn (memory: [*]align(4096) u8, pages: usize) callconv(.C) Status, /// Returns the current memory map. - getMemoryMap: fn (*usize, [*]MemoryDescriptor, *usize, *usize, *u32) callconv(.C) Status, + getMemoryMap: fn (mmap_size: *usize, mmap: [*]MemoryDescriptor, mapKey: *usize, descriptor_size: *usize, descriptor_version: *u32) callconv(.C) Status, /// Allocates pool memory. - allocatePool: fn (MemoryType, usize, *[*]align(8) u8) callconv(.C) Status, + allocatePool: fn (pool_type: MemoryType, size: usize, buffer: *[*]align(8) u8) callconv(.C) Status, /// Returns pool memory to the system. - freePool: fn ([*]align(8) u8) callconv(.C) Status, + freePool: fn (buffer: [*]align(8) u8) callconv(.C) Status, /// Creates an event. - createEvent: fn (u32, usize, ?fn (Event, ?*anyopaque) callconv(.C) void, ?*const anyopaque, *Event) callconv(.C) Status, + createEvent: fn (type: u32, notify_tpl: usize, notify_func: ?fn (Event, ?*anyopaque) callconv(.C) void, notifyCtx: ?*const anyopaque, event: *Event) callconv(.C) Status, /// Sets the type of timer and the trigger time for a timer event. - setTimer: fn (Event, TimerDelay, u64) callconv(.C) Status, + setTimer: fn (event: Event, type: TimerDelay, triggerTime: u64) callconv(.C) Status, /// Stops execution until an event is signaled. - waitForEvent: fn (usize, [*]const Event, *usize) callconv(.C) Status, + waitForEvent: fn (event_len: usize, events: [*]const Event, index: *usize) callconv(.C) Status, /// Signals an event. - signalEvent: fn (Event) callconv(.C) Status, + signalEvent: fn (event: Event) callconv(.C) Status, /// Closes an event. - closeEvent: fn (Event) callconv(.C) Status, + closeEvent: fn (event: Event) callconv(.C) Status, /// Checks whether an event is in the signaled state. - checkEvent: fn (Event) callconv(.C) Status, + checkEvent: fn (event: Event) callconv(.C) Status, /// Installs a protocol interface on a device handle. If the handle does not exist, it is created /// and added to the list of handles in the system. installMultipleProtocolInterfaces() /// performs more error checking than installProtocolInterface(), so its use is recommended over this. - installProtocolInterface: fn (Handle, *align(8) const Guid, EfiInterfaceType, *anyopaque) callconv(.C) Status, + installProtocolInterface: fn (handle: Handle, protocol: *align(8) const Guid, interface_type: EfiInterfaceType, interface: *anyopaque) callconv(.C) Status, /// Reinstalls a protocol interface on a device handle - reinstallProtocolInterface: fn (Handle, *align(8) const Guid, *anyopaque, *anyopaque) callconv(.C) Status, + reinstallProtocolInterface: fn (handle: Handle, protocol: *align(8) const Guid, old_interface: *anyopaque, new_interface: *anyopaque) callconv(.C) Status, /// Removes a protocol interface from a device handle. Usage of /// uninstallMultipleProtocolInterfaces is recommended over this. - uninstallProtocolInterface: fn (Handle, *align(8) const Guid, *anyopaque) callconv(.C) Status, + uninstallProtocolInterface: fn (handle: Handle, protocol: *align(8) const Guid, interface: *anyopaque) callconv(.C) Status, /// Queries a handle to determine if it supports a specified protocol. - handleProtocol: fn (Handle, *align(8) const Guid, *?*anyopaque) callconv(.C) Status, + handleProtocol: fn (handle: Handle, protocol: *align(8) const Guid, interface: *?*anyopaque) callconv(.C) Status, reserved: *anyopaque, /// Creates an event that is to be signaled whenever an interface is installed for a specified protocol. - registerProtocolNotify: fn (*align(8) const Guid, Event, **anyopaque) callconv(.C) Status, + registerProtocolNotify: fn (protocol: *align(8) const Guid, event: Event, registration: **anyopaque) callconv(.C) Status, /// Returns an array of handles that support a specified protocol. - locateHandle: fn (LocateSearchType, ?*align(8) const Guid, ?*const anyopaque, *usize, [*]Handle) callconv(.C) Status, + locateHandle: fn (search_type: LocateSearchType, protocol: ?*align(8) const Guid, search_key: ?*const anyopaque, bufferSize: *usize, buffer: [*]Handle) callconv(.C) Status, /// Locates the handle to a device on the device path that supports the specified protocol - locateDevicePath: fn (*align(8) const Guid, **const DevicePathProtocol, *?Handle) callconv(.C) Status, + locateDevicePath: fn (protocols: *align(8) const Guid, device_path: **const DevicePathProtocol, device: *?Handle) callconv(.C) Status, /// Adds, updates, or removes a configuration table entry from the EFI System Table. - installConfigurationTable: fn (*align(8) const Guid, ?*anyopaque) callconv(.C) Status, + installConfigurationTable: fn (guid: *align(8) const Guid, table: ?*anyopaque) callconv(.C) Status, /// Loads an EFI image into memory. - loadImage: fn (bool, Handle, ?*const DevicePathProtocol, ?[*]const u8, usize, *?Handle) callconv(.C) Status, + loadImage: fn (boot_policy: bool, parent_image_handle: Handle, device_path: ?*const DevicePathProtocol, source_buffer: ?[*]const u8, source_size: usize, imageHandle: *?Handle) callconv(.C) Status, /// Transfers control to a loaded image's entry point. - startImage: fn (Handle, ?*usize, ?*[*]u16) callconv(.C) Status, + startImage: fn (image_handle: Handle, exit_data_size: ?*usize, exit_data: ?*[*]u16) callconv(.C) Status, /// Terminates a loaded EFI image and returns control to boot services. - exit: fn (Handle, Status, usize, ?*const anyopaque) callconv(.C) Status, + exit: fn (image_handle: Handle, exit_status: Status, exit_data_size: usize, exit_data: ?*const anyopaque) callconv(.C) Status, /// Unloads an image. - unloadImage: fn (Handle) callconv(.C) Status, + unloadImage: fn (image_handle: Handle) callconv(.C) Status, /// Terminates all boot services. - exitBootServices: fn (Handle, usize) callconv(.C) Status, + exitBootServices: fn (image_handle: Handle, map_key: usize) callconv(.C) Status, /// Returns a monotonically increasing count for the platform. - getNextMonotonicCount: fn (*u64) callconv(.C) Status, + getNextMonotonicCount: fn (count: *u64) callconv(.C) Status, /// Induces a fine-grained stall. - stall: fn (usize) callconv(.C) Status, + stall: fn (microseconds: usize) callconv(.C) Status, /// Sets the system's watchdog timer. - setWatchdogTimer: fn (usize, u64, usize, ?[*]const u16) callconv(.C) Status, + setWatchdogTimer: fn (timeout: usize, watchdogCode: u64, data_size: usize, watchdog_data: ?[*]const u16) callconv(.C) Status, /// Connects one or more drives to a controller. - connectController: fn (Handle, ?Handle, ?*DevicePathProtocol, bool) callconv(.C) Status, + connectController: fn (controller_handle: Handle, driver_image_handle: ?Handle, remaining_device_path: ?*DevicePathProtocol, recursive: bool) callconv(.C) Status, // Disconnects one or more drivers from a controller - disconnectController: fn (Handle, ?Handle, ?Handle) callconv(.C) Status, + disconnectController: fn (controller_handle: Handle, driver_image_handle: ?Handle, child_handle: ?Handle) callconv(.C) Status, /// Queries a handle to determine if it supports a specified protocol. - openProtocol: fn (Handle, *align(8) const Guid, *?*anyopaque, ?Handle, ?Handle, OpenProtocolAttributes) callconv(.C) Status, + openProtocol: fn (handle: Handle, protocol: *align(8) const Guid, interface: *?*anyopaque, agent_handle: ?Handle, controller_handle: ?Handle, attributes: OpenProtocolAttributes) callconv(.C) Status, /// Closes a protocol on a handle that was opened using openProtocol(). - closeProtocol: fn (Handle, *align(8) const Guid, Handle, ?Handle) callconv(.C) Status, + closeProtocol: fn (handle: Handle, protocol: *align(8) const Guid, agentHandle: Handle, controller_handle: ?Handle) callconv(.C) Status, /// Retrieves the list of agents that currently have a protocol interface opened. - openProtocolInformation: fn (Handle, *align(8) const Guid, *[*]ProtocolInformationEntry, *usize) callconv(.C) Status, + openProtocolInformation: fn (handle: Handle, protocol: *align(8) const Guid, entry_buffer: *[*]ProtocolInformationEntry, entry_count: *usize) callconv(.C) Status, /// Retrieves the list of protocol interface GUIDs that are installed on a handle in a buffer allocated from pool. - protocolsPerHandle: fn (Handle, *[*]*align(8) const Guid, *usize) callconv(.C) Status, + protocolsPerHandle: fn (handle: Handle, protocol_buffer: *[*]*align(8) const Guid, protocol_buffer_count: *usize) callconv(.C) Status, /// Returns an array of handles that support the requested protocol in a buffer allocated from pool. - locateHandleBuffer: fn (LocateSearchType, ?*align(8) const Guid, ?*const anyopaque, *usize, *[*]Handle) callconv(.C) Status, + locateHandleBuffer: fn (search_type: LocateSearchType, protocol: ?*align(8) const Guid, search_key: ?*const anyopaque, num_handles: *usize, buffer: *[*]Handle) callconv(.C) Status, /// Returns the first protocol instance that matches the given protocol. - locateProtocol: fn (*align(8) const Guid, ?*const anyopaque, *?*anyopaque) callconv(.C) Status, + locateProtocol: fn (protocol: *align(8) const Guid, registration: ?*const anyopaque, interface: *?*anyopaque) callconv(.C) Status, /// Installs one or more protocol interfaces into the boot services environment - installMultipleProtocolInterfaces: fn (*Handle, ...) callconv(.C) Status, + installMultipleProtocolInterfaces: fn (handle: *Handle, ...) callconv(.C) Status, /// Removes one or more protocol interfaces into the boot services environment - uninstallMultipleProtocolInterfaces: fn (*Handle, ...) callconv(.C) Status, + uninstallMultipleProtocolInterfaces: fn (handle: *Handle, ...) callconv(.C) Status, /// Computes and returns a 32-bit CRC for a data buffer. - calculateCrc32: fn ([*]const u8, usize, *u32) callconv(.C) Status, + calculateCrc32: fn (data: [*]const u8, data_size: usize, *u32) callconv(.C) Status, /// Copies the contents of one buffer to another buffer - copyMem: fn ([*]u8, [*]const u8, usize) callconv(.C) void, + copyMem: fn (dest: [*]u8, src: [*]const u8, len: usize) callconv(.C) void, /// Fills a buffer with a specified value - setMem: fn ([*]u8, usize, u8) callconv(.C) void, + setMem: fn (buffer: [*]u8, size: usize, value: u8) callconv(.C) void, /// Creates an event in a group. - createEventEx: fn (u32, usize, EfiEventNotify, *const anyopaque, *align(8) const Guid, *Event) callconv(.C) Status, + createEventEx: fn (type: u32, notify_tpl: usize, notify_func: EfiEventNotify, notify_ctx: *const anyopaque, event_group: *align(8) const Guid, event: *Event) callconv(.C) Status, pub const signature: u64 = 0x56524553544f4f42; From ae084c5f59b0436a8fcb1b924008a16d90a9fe86 Mon Sep 17 00:00:00 2001 From: fifty-six Date: Thu, 13 Jan 2022 15:44:29 -0500 Subject: [PATCH 0006/2031] std/os/uefi: Complete AcpiDevicePath and HardwareDevicePaths --- .../uefi/protocols/device_path_protocol.zig | 61 +++++++++++++++---- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/lib/std/os/uefi/protocols/device_path_protocol.zig b/lib/std/os/uefi/protocols/device_path_protocol.zig index df3812451c..7a31b923ac 100644 --- a/lib/std/os/uefi/protocols/device_path_protocol.zig +++ b/lib/std/os/uefi/protocols/device_path_protocol.zig @@ -87,7 +87,10 @@ pub const DevicePathProtocol = packed struct { }, .Acpi => blk: { const acpi: ?AcpiDevicePath = switch (@intToEnum(AcpiDevicePath.Subtype, self.subtype)) { - else => null, // TODO + .Acpi => .{ .Acpi = @ptrCast(*const AcpiDevicePath.BaseAcpiDevicePath, self) }, + .ExpandedAcpi => .{ .ExpandedAcpi = @ptrCast(*const AcpiDevicePath.ExpandedAcpiDevicePath, self) }, + .Adr => .{ .Adr = @ptrCast(*const AcpiDevicePath.AdrDevicePath, self) }, + _ => null, }; break :blk if (acpi) |a| .{ .Acpi = a } else null; }, @@ -173,58 +176,92 @@ pub const HardwareDevicePath = union(Subtype) { type: DevicePathType, subtype: Subtype, length: u16, - // TODO + function: u8, + device: u8, }; pub const PcCardDevicePath = packed struct { type: DevicePathType, subtype: Subtype, length: u16, - // TODO + function_number: u8, }; pub const MemoryMappedDevicePath = packed struct { type: DevicePathType, subtype: Subtype, length: u16, - // TODO + memory_type: u32, + start_address: u64, + end_address: u64, }; pub const VendorDevicePath = packed struct { type: DevicePathType, subtype: Subtype, length: u16, - // TODO + vendor_guid: Guid, }; pub const ControllerDevicePath = packed struct { type: DevicePathType, subtype: Subtype, length: u16, - // TODO + controller_number: u32, }; pub const BmcDevicePath = packed struct { type: DevicePathType, subtype: Subtype, length: u16, - // TODO + interface_type: u8, + base_address: usize, }; }; pub const AcpiDevicePath = union(Subtype) { - Acpi: void, // TODO - ExpandedAcpi: void, // TODO - Adr: void, // TODO - Nvdimm: void, // TODO + Acpi: *const BaseAcpiDevicePath, + ExpandedAcpi: *const ExpandedAcpiDevicePath, + Adr: *const AdrDevicePath, pub const Subtype = enum(u8) { Acpi = 1, ExpandedAcpi = 2, Adr = 3, - Nvdimm = 4, _, }; + + pub const BaseAcpiDevicePath = packed struct { + type: DevicePathType, + subtype: Subtype, + length: u16, + hid: u32, + uid: u32, + }; + + pub const ExpandedAcpiDevicePath = packed struct { + type: DevicePathType, + subtype: Subtype, + length: u16, + hid: u32, + uid: u32, + cid: u32, + // variable length u16[*:0] strings + // hid_str, uid_str, cid_str + }; + + pub const AdrDevicePath = packed struct { + type: DevicePathType, + subtype: Subtype, + length: u16, + adr: u32, + // multiple adr entries can optionally follow + pub fn adrs(self: *const AdrDevicePath) []const u32 { + // self.length is a minimum of 8 with one adr which is size 4. + var entries = (self.length - 4) / @sizeOf(u32); + return @ptrCast([*]const u32, &self.adr)[0..entries]; + } + }; }; pub const MessagingDevicePath = union(Subtype) { From a2a2601da577dd424ea585da0449288a439871fe Mon Sep 17 00:00:00 2001 From: fifty-six Date: Fri, 14 Jan 2022 07:02:34 -0500 Subject: [PATCH 0007/2031] std/os/uefi: Complete DevicePathProtocol types --- lib/std/os/uefi.zig | 12 + .../uefi/protocols/device_path_protocol.zig | 294 ++++++++++++++++-- 2 files changed, 283 insertions(+), 23 deletions(-) diff --git a/lib/std/os/uefi.zig b/lib/std/os/uefi.zig index 4c034ed456..1bceab5b2a 100644 --- a/lib/std/os/uefi.zig +++ b/lib/std/os/uefi.zig @@ -23,6 +23,18 @@ pub var system_table: *tables.SystemTable = undefined; /// A handle to an event structure. pub const Event = *opaque {}; +pub const MacAddress = extern struct { + address: [32]u8, +}; + +pub const Ipv4Address = extern struct { + address: [4]u8, +}; + +pub const Ipv6Address = extern struct { + address: [16]u8, +}; + /// GUIDs must be align(8) pub const Guid = extern struct { time_low: u32, diff --git a/lib/std/os/uefi/protocols/device_path_protocol.zig b/lib/std/os/uefi/protocols/device_path_protocol.zig index 7a31b923ac..a6509bb14f 100644 --- a/lib/std/os/uefi/protocols/device_path_protocol.zig +++ b/lib/std/os/uefi/protocols/device_path_protocol.zig @@ -265,24 +265,24 @@ pub const AcpiDevicePath = union(Subtype) { }; pub const MessagingDevicePath = union(Subtype) { - Atapi: void, // TODO - Scsi: void, // TODO - FibreChannel: void, // TODO - FibreChannelEx: void, // TODO - @"1394": void, // TODO - Usb: void, // TODO - Sata: void, // TODO - UsbWwid: void, // TODO - Lun: void, // TODO - UsbClass: void, // TODO - I2o: void, // TODO - MacAddress: void, // TODO - Ipv4: void, // TODO - Ipv6: void, // TODO - Vlan: void, // TODO - InfiniBand: void, // TODO - Uart: void, // TODO - Vendor: void, // TODO + Atapi: *const AtapiDevicePath, + Scsi: *const ScsiDevicePath, + FibreChannel: *const FibreChannelDevicePath, + FibreChannelEx: FibreChannelExDevicePath, + @"1394": *const F1394DevicePath, + Usb: *const UsbDevicePath, + Sata: *const SataDevicePath, + UsbWwid: *const UsbWwidDevicePath, + Lun: *const DeviceLogicalUnitDevicePath, + UsbClass: *const UsbClassDevicePath, + I2o: *const I2oDevicePath, + MacAddress: *const MacAddressDevicePath, + Ipv4: *const Ipv4DevicePath, + Ipv6: *const Ipv6DevicePath, + Vlan: *const VlanDevicePath, + InfiniBand: *const InfiniBandDevicePath, + Uart: *const UartDevicePath, + Vendor: *const VendorDefinedDevicePath, pub const Subtype = enum(u8) { Atapi = 1, @@ -305,6 +305,232 @@ pub const MessagingDevicePath = union(Subtype) { Vendor = 10, _, }; + + pub const AtapiDevicePath = packed struct { + const Role = enum(u8) { + Master = 0, + Slave = 1, + }; + + const Rank = enum(u8) { + Primary = 0, + Secondary = 1, + }; + + type: DevicePathType, + subtype: Subtype, + length: u16, + primary_secondary: Rank, + slave_master: Role, + logical_unit_number: u16, + }; + + pub const ScsiDevicePath = packed struct { + type: DevicePathType, + subtype: Subtype, + length: u16, + target_id: u16, + logical_unit_number: u16, + }; + + pub const FibreChannelDevicePath = packed struct { + type: DevicePathType, + subtype: Subtype, + length: u16, + reserved: u32, + world_wide_name: u64, + logical_unit_number: u64, + }; + + pub const FibreChannelExDevicePath = packed struct { + type: DevicePathType, + subtype: Subtype, + length: u16, + reserved: u32, + world_wide_name: [8]u8, + logical_unit_number: [8]u8, + }; + + pub const F1394DevicePath = packed struct { + type: DevicePathType, + subtype: Subtype, + length: u16, + reserved: u32, + guid: u64, + }; + + pub const UsbDevicePath = packed struct { + type: DevicePathType, + subtype: Subtype, + length: u16, + parent_port_number: u8, + interface_number: u8, + }; + + pub const SataDevicePath = packed struct { + type: DevicePathType, + subtype: Subtype, + length: u16, + hba_port_number: u16, + port_multiplier_port_number: u16, + logical_unit_number: u16, + }; + + pub const UsbWwidDevicePath = packed struct { + type: DevicePathType, + subtype: Subtype, + length: u16, + interface_number: u16, + device_vendor_id: u16, + device_product_id: u16, + + pub fn serial_number(self: *const UsbWwidDevicePath) []const u16 { + var serial_len = (self.length - @sizeOf(UsbWwidDevicePath)) / @sizeOf(u16); + return @ptrCast([*]u16, @ptrCast([*]u8, self) + @sizeOf(UsbWwidDevicePath))[0..serial_len]; + } + }; + + pub const DeviceLogicalUnitDevicePath = packed struct { + type: DevicePathType, + subtype: Subtype, + length: u16, + lun: u8, + }; + + pub const UsbClassDevicePath = packed struct { + type: DevicePathType, + subtype: Subtype, + length: u16, + vendor_id: u16, + product_id: u16, + device_class: u8, + device_subclass: u8, + device_protocol: u8, + }; + + pub const I2oDevicePath = packed struct { + type: DevicePathType, + subtype: Subtype, + length: u16, + tid: u32, + }; + + pub const MacAddressDevicePath = packed struct { + type: DevicePathType, + subtype: Subtype, + length: u16, + mac_address: uefi.MacAddress, + if_type: u8, + }; + + pub const Ipv4DevicePath = packed struct { + pub const IpType = enum(u8) { + Dhcp = 0, + Static = 1, + }; + + type: DevicePathType, + subtype: Subtype, + length: u16, + local_ip_address: uefi.Ipv4Address, + remote_ip_address: uefi.Ipv4Address, + local_port: u16, + remote_port: u16, + network_protocol: u16, + static_ip_address: IpType, + gateway_ip_address: u32, + subnet_mask: u32, + }; + + pub const Ipv6DevicePath = packed struct { + pub const Origin = enum(u8) { + Manual = 0, + AssignedStateless = 1, + AssignedStateful = 2, + }; + + type: DevicePathType, + subtype: Subtype, + length: u16, + local_ip_address: uefi.Ipv6Address, + remote_ip_address: uefi.Ipv6Address, + local_port: u16, + remote_port: u16, + protocol: u16, + ip_address_origin: Origin, + prefix_length: u8, + gateway_ip_address: uefi.Ipv6Address, + }; + + pub const VlanDevicePath = packed struct { + type: DevicePathType, + subtype: Subtype, + length: u16, + vlan_id: u16, + }; + + pub const InfiniBandDevicePath = packed struct { + pub const ResourceFlags = packed struct { + pub const ControllerType = enum(u1) { + Ioc = 0, + Service = 1, + }; + + ioc_or_service: ControllerType, + extend_boot_environment: bool, + console_protocol: bool, + storage_protocol: bool, + network_protocol: bool, + + // u1 + 4 * bool = 5 bits, we need a total of 32 bits + reserved: u27, + }; + + type: DevicePathType, + subtype: Subtype, + length: u16, + resource_flags: ResourceFlags, + port_gid: [16]u8, + service_id: u64, + target_port_id: u64, + device_id: u64, + }; + + pub const UartDevicePath = packed struct { + pub const Parity = enum(u8) { + Default = 0, + None = 1, + Even = 2, + Odd = 3, + Mark = 4, + Space = 5, + _, + }; + + pub const StopBits = enum(u8) { + Default = 0, + One = 1, + OneAndAHalf = 2, + Two = 3, + _, + }; + + type: DevicePathType, + subtype: Subtype, + length: u16, + reserved: u16, + baud_rate: u32, + data_bits: u8, + parity: Parity, + stop_bits: StopBits, + }; + + pub const VendorDefinedDevicePath = packed struct { + type: DevicePathType, + subtype: Subtype, + length: u16, + vendor_guid: Guid, + }; }; pub const MediaDevicePath = union(Subtype) { @@ -332,24 +558,44 @@ pub const MediaDevicePath = union(Subtype) { }; pub const HardDriveDevicePath = packed struct { + pub const Format = enum(u8) { + LegacyMbr = 0x01, + GuidPartitionTable = 0x02, + }; + + pub const SignatureType = enum(u8) { + NoSignature = 0x00, + /// "32-bit signature from address 0x1b8 of the type 0x01 MBR" + MbrSignature = 0x01, + GuidSignature = 0x02, + }; + type: DevicePathType, subtype: Subtype, length: u16, - // TODO + partition_number: u32, + partition_start: u64, + partition_size: u64, + partition_signature: [16]u8, + partition_format: Format, + signature_type: SignatureType, }; pub const CdromDevicePath = packed struct { type: DevicePathType, subtype: Subtype, length: u16, - // TODO + boot_entry: u32, + partition_start: u64, + partition_size: u64, }; pub const VendorDevicePath = packed struct { type: DevicePathType, subtype: Subtype, length: u16, - // TODO + guid: Guid, + // vendor-defined variable data }; pub const FilePathDevicePath = packed struct { @@ -366,19 +612,21 @@ pub const MediaDevicePath = union(Subtype) { type: DevicePathType, subtype: Subtype, length: u16, - // TODO + guid: Guid, }; pub const PiwgFirmwareFileDevicePath = packed struct { type: DevicePathType, subtype: Subtype, length: u16, + fv_filename: Guid, }; pub const PiwgFirmwareVolumeDevicePath = packed struct { type: DevicePathType, subtype: Subtype, length: u16, + fv_name: Guid, }; pub const RelativeOffsetRangeDevicePath = packed struct { @@ -396,7 +644,7 @@ pub const MediaDevicePath = union(Subtype) { length: u16, start: u64, end: u64, - disk_type: uefi.Guid, + disk_type: Guid, instance: u16, }; }; From dab4c63684ca951049fb8e8f6b2415857eb6652b Mon Sep 17 00:00:00 2001 From: fifty-six Date: Fri, 14 Jan 2022 08:53:56 -0500 Subject: [PATCH 0008/2031] std/os/uefi: Refactor getDevicePath() Uses comptime loops over the types instead of writing out a large switch. --- .../uefi/protocols/device_path_protocol.zig | 95 +++++++------------ 1 file changed, 34 insertions(+), 61 deletions(-) diff --git a/lib/std/os/uefi/protocols/device_path_protocol.zig b/lib/std/os/uefi/protocols/device_path_protocol.zig index a6509bb14f..ae5822ba35 100644 --- a/lib/std/os/uefi/protocols/device_path_protocol.zig +++ b/lib/std/os/uefi/protocols/device_path_protocol.zig @@ -72,66 +72,39 @@ pub const DevicePathProtocol = packed struct { } pub fn getDevicePath(self: *const DevicePathProtocol) ?DevicePath { - return switch (self.type) { - .Hardware => blk: { - const hardware: ?HardwareDevicePath = switch (@intToEnum(HardwareDevicePath.Subtype, self.subtype)) { - .Pci => .{ .Pci = @ptrCast(*const HardwareDevicePath.PciDevicePath, self) }, - .PcCard => .{ .PcCard = @ptrCast(*const HardwareDevicePath.PcCardDevicePath, self) }, - .MemoryMapped => .{ .MemoryMapped = @ptrCast(*const HardwareDevicePath.MemoryMappedDevicePath, self) }, - .Vendor => .{ .Vendor = @ptrCast(*const HardwareDevicePath.VendorDevicePath, self) }, - .Controller => .{ .Controller = @ptrCast(*const HardwareDevicePath.ControllerDevicePath, self) }, - .Bmc => .{ .Bmc = @ptrCast(*const HardwareDevicePath.BmcDevicePath, self) }, - _ => null, - }; - break :blk if (hardware) |h| .{ .Hardware = h } else null; - }, - .Acpi => blk: { - const acpi: ?AcpiDevicePath = switch (@intToEnum(AcpiDevicePath.Subtype, self.subtype)) { - .Acpi => .{ .Acpi = @ptrCast(*const AcpiDevicePath.BaseAcpiDevicePath, self) }, - .ExpandedAcpi => .{ .ExpandedAcpi = @ptrCast(*const AcpiDevicePath.ExpandedAcpiDevicePath, self) }, - .Adr => .{ .Adr = @ptrCast(*const AcpiDevicePath.AdrDevicePath, self) }, - _ => null, - }; - break :blk if (acpi) |a| .{ .Acpi = a } else null; - }, - .Messaging => blk: { - const messaging: ?MessagingDevicePath = switch (@intToEnum(MessagingDevicePath.Subtype, self.subtype)) { - else => null, // TODO - }; - break :blk if (messaging) |m| .{ .Messaging = m } else null; - }, - .Media => blk: { - const media: ?MediaDevicePath = switch (@intToEnum(MediaDevicePath.Subtype, self.subtype)) { - .HardDrive => .{ .HardDrive = @ptrCast(*const MediaDevicePath.HardDriveDevicePath, self) }, - .Cdrom => .{ .Cdrom = @ptrCast(*const MediaDevicePath.CdromDevicePath, self) }, - .Vendor => .{ .Vendor = @ptrCast(*const MediaDevicePath.VendorDevicePath, self) }, - .FilePath => .{ .FilePath = @ptrCast(*const MediaDevicePath.FilePathDevicePath, self) }, - .MediaProtocol => .{ .MediaProtocol = @ptrCast(*const MediaDevicePath.MediaProtocolDevicePath, self) }, - .PiwgFirmwareFile => .{ .PiwgFirmwareFile = @ptrCast(*const MediaDevicePath.PiwgFirmwareFileDevicePath, self) }, - .PiwgFirmwareVolume => .{ .PiwgFirmwareVolume = @ptrCast(*const MediaDevicePath.PiwgFirmwareVolumeDevicePath, self) }, - .RelativeOffsetRange => .{ .RelativeOffsetRange = @ptrCast(*const MediaDevicePath.RelativeOffsetRangeDevicePath, self) }, - .RamDisk => .{ .RamDisk = @ptrCast(*const MediaDevicePath.RamDiskDevicePath, self) }, - _ => null, - }; - break :blk if (media) |m| .{ .Media = m } else null; - }, - .BiosBootSpecification => blk: { - const bbs: ?BiosBootSpecificationDevicePath = switch (@intToEnum(BiosBootSpecificationDevicePath.Subtype, self.subtype)) { - .BBS101 => .{ .BBS101 = @ptrCast(*const BiosBootSpecificationDevicePath.BBS101DevicePath, self) }, - _ => null, - }; - break :blk if (bbs) |b| .{ .BiosBootSpecification = b } else null; - }, - .End => blk: { - const end: ?EndDevicePath = switch (@intToEnum(EndDevicePath.Subtype, self.subtype)) { - .EndEntire => .{ .EndEntire = @ptrCast(*const EndDevicePath.EndEntireDevicePath, self) }, - .EndThisInstance => .{ .EndThisInstance = @ptrCast(*const EndDevicePath.EndThisInstanceDevicePath, self) }, - _ => null, - }; - break :blk if (end) |e| .{ .End = e } else null; - }, - _ => null, - }; + inline for (@typeInfo(DevicePath).Union.fields) |ufield| { + const enum_value = std.meta.stringToEnum(DevicePathType, ufield.name); + + // Got the associated union type for self.type, now + // we need to initialize it and its subtype + if (self.type == enum_value) { + var subtype = self.initSubtype(ufield.field_type); + + if (subtype) |sb| { + // e.g. return .{ .Hardware = .{ .Pci = @ptrCast(...) } } + return @unionInit(DevicePath, ufield.name, sb); + } + } + } + + return null; + } + + pub fn initSubtype(self: *const DevicePathProtocol, comptime TUnion: type) ?TUnion { + const type_info = @typeInfo(TUnion).Union; + const TTag = type_info.tag_type.?; + + inline for (type_info.fields) |subtype| { + // The tag names match the union names, so just grab that off the enum + const tag_val: u8 = @enumToInt(@field(TTag, subtype.name)); + + if (self.subtype == tag_val) { + // e.g. expr = .{ .Pci = @ptrCast(...) } + return @unionInit(TUnion, subtype.name, @ptrCast(subtype.field_type, self)); + } + } + + return null; } }; @@ -268,7 +241,7 @@ pub const MessagingDevicePath = union(Subtype) { Atapi: *const AtapiDevicePath, Scsi: *const ScsiDevicePath, FibreChannel: *const FibreChannelDevicePath, - FibreChannelEx: FibreChannelExDevicePath, + FibreChannelEx: *const FibreChannelExDevicePath, @"1394": *const F1394DevicePath, Usb: *const UsbDevicePath, Sata: *const SataDevicePath, From 3c2eddae5a4e89ec428a7b3b9f089c32f57b17af Mon Sep 17 00:00:00 2001 From: viri Date: Sat, 15 Jan 2022 17:27:17 -0600 Subject: [PATCH 0009/2031] std.os.windows: fix casing for `ntdll.lib` I've seen having this be wrong break some cross-compilers, and it's also how it is in other files so it's best to be consistent. It's also just the actual casing of the file. --- lib/std/os/windows/ntdll.zig | 44 ++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/std/os/windows/ntdll.zig b/lib/std/os/windows/ntdll.zig index 41c1a905ec..1154558d4d 100644 --- a/lib/std/os/windows/ntdll.zig +++ b/lib/std/os/windows/ntdll.zig @@ -23,23 +23,23 @@ const FILE_BASIC_INFORMATION = windows.FILE_BASIC_INFORMATION; const SIZE_T = windows.SIZE_T; const CURDIR = windows.CURDIR; -pub extern "NtDll" fn RtlGetVersion( +pub extern "ntdll" fn RtlGetVersion( lpVersionInformation: *RTL_OSVERSIONINFOW, ) callconv(WINAPI) NTSTATUS; -pub extern "NtDll" fn RtlCaptureStackBackTrace( +pub extern "ntdll" fn RtlCaptureStackBackTrace( FramesToSkip: DWORD, FramesToCapture: DWORD, BackTrace: **anyopaque, BackTraceHash: ?*DWORD, ) callconv(WINAPI) WORD; -pub extern "NtDll" fn NtQueryInformationFile( +pub extern "ntdll" fn NtQueryInformationFile( FileHandle: HANDLE, IoStatusBlock: *IO_STATUS_BLOCK, FileInformation: *anyopaque, Length: ULONG, FileInformationClass: FILE_INFORMATION_CLASS, ) callconv(WINAPI) NTSTATUS; -pub extern "NtDll" fn NtSetInformationFile( +pub extern "ntdll" fn NtSetInformationFile( FileHandle: HANDLE, IoStatusBlock: *IO_STATUS_BLOCK, FileInformation: PVOID, @@ -47,12 +47,12 @@ pub extern "NtDll" fn NtSetInformationFile( FileInformationClass: FILE_INFORMATION_CLASS, ) callconv(WINAPI) NTSTATUS; -pub extern "NtDll" fn NtQueryAttributesFile( +pub extern "ntdll" fn NtQueryAttributesFile( ObjectAttributes: *OBJECT_ATTRIBUTES, FileAttributes: *FILE_BASIC_INFORMATION, ) callconv(WINAPI) NTSTATUS; -pub extern "NtDll" fn NtCreateFile( +pub extern "ntdll" fn NtCreateFile( FileHandle: *HANDLE, DesiredAccess: ACCESS_MASK, ObjectAttributes: *OBJECT_ATTRIBUTES, @@ -65,7 +65,7 @@ pub extern "NtDll" fn NtCreateFile( EaBuffer: ?*anyopaque, EaLength: ULONG, ) callconv(WINAPI) NTSTATUS; -pub extern "NtDll" fn NtDeviceIoControlFile( +pub extern "ntdll" fn NtDeviceIoControlFile( FileHandle: HANDLE, Event: ?HANDLE, ApcRoutine: ?IO_APC_ROUTINE, @@ -77,7 +77,7 @@ pub extern "NtDll" fn NtDeviceIoControlFile( OutputBuffer: ?PVOID, OutputBufferLength: ULONG, ) callconv(WINAPI) NTSTATUS; -pub extern "NtDll" fn NtFsControlFile( +pub extern "ntdll" fn NtFsControlFile( FileHandle: HANDLE, Event: ?HANDLE, ApcRoutine: ?IO_APC_ROUTINE, @@ -89,16 +89,16 @@ pub extern "NtDll" fn NtFsControlFile( OutputBuffer: ?PVOID, OutputBufferLength: ULONG, ) callconv(WINAPI) NTSTATUS; -pub extern "NtDll" fn NtClose(Handle: HANDLE) callconv(WINAPI) NTSTATUS; -pub extern "NtDll" fn RtlDosPathNameToNtPathName_U( +pub extern "ntdll" fn NtClose(Handle: HANDLE) callconv(WINAPI) NTSTATUS; +pub extern "ntdll" fn RtlDosPathNameToNtPathName_U( DosPathName: [*:0]const u16, NtPathName: *UNICODE_STRING, NtFileNamePart: ?*?[*:0]const u16, DirectoryInfo: ?*CURDIR, ) callconv(WINAPI) BOOL; -pub extern "NtDll" fn RtlFreeUnicodeString(UnicodeString: *UNICODE_STRING) callconv(WINAPI) void; +pub extern "ntdll" fn RtlFreeUnicodeString(UnicodeString: *UNICODE_STRING) callconv(WINAPI) void; -pub extern "NtDll" fn NtQueryDirectoryFile( +pub extern "ntdll" fn NtQueryDirectoryFile( FileHandle: HANDLE, Event: ?HANDLE, ApcRoutine: ?IO_APC_ROUTINE, @@ -112,30 +112,30 @@ pub extern "NtDll" fn NtQueryDirectoryFile( RestartScan: BOOLEAN, ) callconv(WINAPI) NTSTATUS; -pub extern "NtDll" fn NtCreateKeyedEvent( +pub extern "ntdll" fn NtCreateKeyedEvent( KeyedEventHandle: *HANDLE, DesiredAccess: ACCESS_MASK, ObjectAttributes: ?PVOID, Flags: ULONG, ) callconv(WINAPI) NTSTATUS; -pub extern "NtDll" fn NtReleaseKeyedEvent( +pub extern "ntdll" fn NtReleaseKeyedEvent( EventHandle: ?HANDLE, Key: ?*const anyopaque, Alertable: BOOLEAN, Timeout: ?*const LARGE_INTEGER, ) callconv(WINAPI) NTSTATUS; -pub extern "NtDll" fn NtWaitForKeyedEvent( +pub extern "ntdll" fn NtWaitForKeyedEvent( EventHandle: ?HANDLE, Key: ?*const anyopaque, Alertable: BOOLEAN, Timeout: ?*const LARGE_INTEGER, ) callconv(WINAPI) NTSTATUS; -pub extern "NtDll" fn RtlSetCurrentDirectory_U(PathName: *UNICODE_STRING) callconv(WINAPI) NTSTATUS; +pub extern "ntdll" fn RtlSetCurrentDirectory_U(PathName: *UNICODE_STRING) callconv(WINAPI) NTSTATUS; -pub extern "NtDll" fn NtQueryObject( +pub extern "ntdll" fn NtQueryObject( Handle: HANDLE, ObjectInformationClass: OBJECT_INFORMATION_CLASS, ObjectInformation: PVOID, @@ -143,22 +143,22 @@ pub extern "NtDll" fn NtQueryObject( ReturnLength: ?*ULONG, ) callconv(WINAPI) NTSTATUS; -pub extern "NtDll" fn RtlWakeAddressAll( +pub extern "ntdll" fn RtlWakeAddressAll( Address: ?*const anyopaque, ) callconv(WINAPI) void; -pub extern "NtDll" fn RtlWakeAddressSingle( +pub extern "ntdll" fn RtlWakeAddressSingle( Address: ?*const anyopaque, ) callconv(WINAPI) void; -pub extern "NtDll" fn RtlWaitOnAddress( +pub extern "ntdll" fn RtlWaitOnAddress( Address: ?*const anyopaque, CompareAddress: ?*const anyopaque, AddressSize: SIZE_T, Timeout: ?*const LARGE_INTEGER, ) callconv(WINAPI) NTSTATUS; -pub extern "NtDll" fn NtLockFile( +pub extern "ntdll" fn NtLockFile( FileHandle: HANDLE, Event: ?HANDLE, ApcRoutine: ?*IO_APC_ROUTINE, @@ -171,7 +171,7 @@ pub extern "NtDll" fn NtLockFile( ExclusiveLock: BOOLEAN, ) callconv(WINAPI) NTSTATUS; -pub extern "NtDll" fn NtUnlockFile( +pub extern "ntdll" fn NtUnlockFile( FileHandle: HANDLE, IoStatusBlock: *IO_STATUS_BLOCK, ByteOffset: *const LARGE_INTEGER, From 12d6bcec029dbb11bd533d0d0f0cc723aa71bc9d Mon Sep 17 00:00:00 2001 From: viri Date: Sat, 15 Jan 2022 17:35:30 -0600 Subject: [PATCH 0010/2031] std.os.windows: add ntdll thread information APIs --- lib/std/os/windows/ntdll.zig | 65 ++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/lib/std/os/windows/ntdll.zig b/lib/std/os/windows/ntdll.zig index 1154558d4d..26cc19935f 100644 --- a/lib/std/os/windows/ntdll.zig +++ b/lib/std/os/windows/ntdll.zig @@ -23,6 +23,71 @@ const FILE_BASIC_INFORMATION = windows.FILE_BASIC_INFORMATION; const SIZE_T = windows.SIZE_T; const CURDIR = windows.CURDIR; +pub const THREADINFOCLASS = enum(c_int) { + ThreadBasicInformation, + ThreadTimes, + ThreadPriority, + ThreadBasePriority, + ThreadAffinityMask, + ThreadImpersonationToken, + ThreadDescriptorTableEntry, + ThreadEnableAlignmentFaultFixup, + ThreadEventPair_Reusable, + ThreadQuerySetWin32StartAddress, + ThreadZeroTlsCell, + ThreadPerformanceCount, + ThreadAmILastThread, + ThreadIdealProcessor, + ThreadPriorityBoost, + ThreadSetTlsArrayAddress, + ThreadIsIoPending, + // Windows 2000+ from here + ThreadHideFromDebugger, + // Windows XP+ from here + ThreadBreakOnTermination, + ThreadSwitchLegacyState, + ThreadIsTerminated, + // Windows Vista+ from here + ThreadLastSystemCall, + ThreadIoPriority, + ThreadCycleTime, + ThreadPagePriority, + ThreadActualBasePriority, + ThreadTebInformation, + ThreadCSwitchMon, + // Windows 7+ from here + ThreadCSwitchPmu, + ThreadWow64Context, + ThreadGroupInformation, + ThreadUmsInformation, + ThreadCounterProfiling, + ThreadIdealProcessorEx, + // Windows 8+ from here + ThreadCpuAccountingInformation, + // Windows 8.1+ from here + ThreadSuspendCount, + // Windows 10+ from here + ThreadHeterogeneousCpuPolicy, + ThreadContainerId, + ThreadNameInformation, + ThreadSelectedCpuSets, + ThreadSystemThreadInformation, + ThreadActualGroupAffinity, +}; +pub extern "ntdll" fn NtQueryInformationThread( + ThreadHandle: HANDLE, + ThreadInformationClass: THREADINFOCLASS, + ThreadInformation: *anyopaque, + ThreadInformationLength: ULONG, + ReturnLength: ?*ULONG, +) callconv(WINAPI) NTSTATUS; +pub extern "ntdll" fn NtSetInformationThread( + ThreadHandle: HANDLE, + ThreadInformationClass: THREADINFOCLASS, + ThreadInformation: *const anyopaque, + ThreadInformationLength: ULONG, +) callconv(WINAPI) NTSTATUS; + pub extern "ntdll" fn RtlGetVersion( lpVersionInformation: *RTL_OSVERSIONINFOW, ) callconv(WINAPI) NTSTATUS; From 4771ac298b0c692750524cb7e94eaf0b4343ce2b Mon Sep 17 00:00:00 2001 From: fifty-six Date: Sun, 16 Jan 2022 01:44:15 -0500 Subject: [PATCH 0011/2031] std/os/uefi: Simplify packed struct padding and default zero-initialize Beyond adding default zero-initialization, this commit changes undefined initialization to zero, as some cases reserved the padding and on other cases, I've found some systems act strange when giving uninit instead of zero even when it shouldn't be an issue, one example being FileProtocol.Open's attributes, which *should* be ignored when not creating a file, but ended up giving an unrelated error. --- lib/std/os/uefi.zig | 2 -- lib/std/os/uefi/protocols/absolute_pointer_protocol.zig | 8 ++------ lib/std/os/uefi/protocols/edid_override_protocol.zig | 4 +--- lib/std/os/uefi/protocols/hii.zig | 4 ++-- lib/std/os/uefi/protocols/simple_network_protocol.zig | 8 ++------ .../os/uefi/protocols/simple_text_input_ex_protocol.zig | 4 ++-- lib/std/os/uefi/tables/boot_services.zig | 4 +--- 7 files changed, 10 insertions(+), 24 deletions(-) diff --git a/lib/std/os/uefi.zig b/lib/std/os/uefi.zig index 1bceab5b2a..4644130e7f 100644 --- a/lib/std/os/uefi.zig +++ b/lib/std/os/uefi.zig @@ -98,7 +98,6 @@ pub const Time = extern struct { /// 0 - 59 second: u8, - _pad1: u8, /// 0 - 999999999 nanosecond: u32, @@ -115,7 +114,6 @@ pub const Time = extern struct { /// If true, the time is affected by daylight savings time. adjust_daylight: bool, }, - _pad2: u8, /// Time is to be interpreted as local time pub const unspecified_timezone: i16 = 0x7ff; diff --git a/lib/std/os/uefi/protocols/absolute_pointer_protocol.zig b/lib/std/os/uefi/protocols/absolute_pointer_protocol.zig index ee79569233..11a5008d9a 100644 --- a/lib/std/os/uefi/protocols/absolute_pointer_protocol.zig +++ b/lib/std/os/uefi/protocols/absolute_pointer_protocol.zig @@ -40,9 +40,7 @@ pub const AbsolutePointerMode = extern struct { attributes: packed struct { supports_alt_active: bool, supports_pressure_as_z: bool, - _pad1: u6, - _pad2: u8, - _pad3: u16, + _pad: u30 = 0, }, }; @@ -53,8 +51,6 @@ pub const AbsolutePointerState = extern struct { active_buttons: packed struct { touch_active: bool, alt_active: bool, - _pad1: u6, - _pad2: u8, - _pad3: u16, + _pad: u30 = 0, }, }; diff --git a/lib/std/os/uefi/protocols/edid_override_protocol.zig b/lib/std/os/uefi/protocols/edid_override_protocol.zig index 8bf848c59a..9540cdc5d4 100644 --- a/lib/std/os/uefi/protocols/edid_override_protocol.zig +++ b/lib/std/os/uefi/protocols/edid_override_protocol.zig @@ -26,7 +26,5 @@ pub const EdidOverrideProtocol = extern struct { pub const EdidOverrideProtocolAttributes = packed struct { dont_override: bool, enable_hot_plug: bool, - _pad1: u6, - _pad2: u8, - _pad3: u16, + _pad: u30 = 0, }; diff --git a/lib/std/os/uefi/protocols/hii.zig b/lib/std/os/uefi/protocols/hii.zig index 5e3c23d22a..b0b0418611 100644 --- a/lib/std/os/uefi/protocols/hii.zig +++ b/lib/std/os/uefi/protocols/hii.zig @@ -48,7 +48,7 @@ pub const NarrowGlyph = extern struct { attributes: packed struct { non_spacing: bool, wide: bool, - _pad: u6, + _pad: u6 = 0, }, glyph_col_1: [19]u8, }; @@ -62,7 +62,7 @@ pub const WideGlyph = extern struct { }, glyph_col_1: [19]u8, glyph_col_2: [19]u8, - _pad: [3]u8, + _pad: [3]u8 = [_]u8{0} ** 3, }; pub const HIIStringPackage = extern struct { diff --git a/lib/std/os/uefi/protocols/simple_network_protocol.zig b/lib/std/os/uefi/protocols/simple_network_protocol.zig index 01e5986c2d..283d944d34 100644 --- a/lib/std/os/uefi/protocols/simple_network_protocol.zig +++ b/lib/std/os/uefi/protocols/simple_network_protocol.zig @@ -126,9 +126,7 @@ pub const SimpleNetworkReceiveFilter = packed struct { receive_broadcast: bool, receive_promiscuous: bool, receive_promiscuous_multicast: bool, - _pad1: u3 = undefined, - _pad2: u8 = undefined, - _pad3: u16 = undefined, + _pad: u27 = 0, }; pub const SimpleNetworkState = enum(u32) { @@ -171,7 +169,5 @@ pub const SimpleNetworkInterruptStatus = packed struct { transmit_interrupt: bool, command_interrupt: bool, software_interrupt: bool, - _pad1: u4, - _pad2: u8, - _pad3: u16, + _pad: u28 = 0, }; diff --git a/lib/std/os/uefi/protocols/simple_text_input_ex_protocol.zig b/lib/std/os/uefi/protocols/simple_text_input_ex_protocol.zig index d816deb8cb..fbcebf1121 100644 --- a/lib/std/os/uefi/protocols/simple_text_input_ex_protocol.zig +++ b/lib/std/os/uefi/protocols/simple_text_input_ex_protocol.zig @@ -64,14 +64,14 @@ pub const KeyState = extern struct { left_logo_pressed: bool, menu_key_pressed: bool, sys_req_pressed: bool, - _pad1: u21, + _pad: u21 = 0, shift_state_valid: bool, }, key_toggle_state: packed struct { scroll_lock_active: bool, num_lock_active: bool, caps_lock_active: bool, - _pad1: u3, + _pad: u3 = 0, key_state_exposed: bool, toggle_state_valid: bool, }, diff --git a/lib/std/os/uefi/tables/boot_services.zig b/lib/std/os/uefi/tables/boot_services.zig index 7da007b05d..468aaf4cf7 100644 --- a/lib/std/os/uefi/tables/boot_services.zig +++ b/lib/std/os/uefi/tables/boot_services.zig @@ -239,9 +239,7 @@ pub const OpenProtocolAttributes = packed struct { by_child_controller: bool = false, by_driver: bool = false, exclusive: bool = false, - _pad1: u2 = undefined, - _pad2: u8 = undefined, - _pad3: u16 = undefined, + _pad: u26 = 0, }; pub const ProtocolInformationEntry = extern struct { From d276a1189de15f85523a0e6844d0fd316306838a Mon Sep 17 00:00:00 2001 From: fifty-six Date: Sun, 16 Jan 2022 01:50:02 -0500 Subject: [PATCH 0012/2031] std/os/uefi: Align first field of EdidOverrideProtocolAttributes to 4 This makes the struct align(4), which allows it to be passed as flags more easily. --- lib/std/os/uefi/protocols/edid_override_protocol.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/std/os/uefi/protocols/edid_override_protocol.zig b/lib/std/os/uefi/protocols/edid_override_protocol.zig index 9540cdc5d4..800c269c67 100644 --- a/lib/std/os/uefi/protocols/edid_override_protocol.zig +++ b/lib/std/os/uefi/protocols/edid_override_protocol.zig @@ -8,7 +8,6 @@ pub const EdidOverrideProtocol = extern struct { _get_edid: fn (*const EdidOverrideProtocol, Handle, *u32, *usize, *?[*]u8) callconv(.C) Status, /// Returns policy information and potentially a replacement EDID for the specified video output device. - /// attributes must be align(4) pub fn getEdid(self: *const EdidOverrideProtocol, handle: Handle, attributes: *EdidOverrideProtocolAttributes, edid_size: *usize, edid: *?[*]u8) Status { return self._get_edid(self, handle, attributes, edid_size, edid); } @@ -24,7 +23,7 @@ pub const EdidOverrideProtocol = extern struct { }; pub const EdidOverrideProtocolAttributes = packed struct { - dont_override: bool, + dont_override: bool align(4), enable_hot_plug: bool, _pad: u30 = 0, }; From 628a7f85deb500624fb75f83014d3bc1a1c03cf4 Mon Sep 17 00:00:00 2001 From: fifty-six Date: Sun, 16 Jan 2022 02:35:22 -0500 Subject: [PATCH 0013/2031] std/os/uefi: Add conversion from Status to EfiError Allows handling uefi function errors in a more zig-style way with try and catch, using `try f().err()` when a `Status` is returned. --- lib/std/os/uefi/status.zig | 69 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/lib/std/os/uefi/status.zig b/lib/std/os/uefi/status.zig index 09bc5030eb..e6f2024f7d 100644 --- a/lib/std/os/uefi/status.zig +++ b/lib/std/os/uefi/status.zig @@ -1,3 +1,5 @@ +const testing = @import("std").testing; + const high_bit = 1 << @typeInfo(usize).Int.bits - 1; pub const Status = enum(usize) { @@ -139,4 +141,71 @@ pub const Status = enum(usize) { WarnResetRequired = 7, _, + + pub const EfiError = error{ + LoadError, + InvalidParameter, + Unsupported, + BadBufferSize, + BufferTooSmall, + NotReady, + DeviceError, + WriteProtected, + OutOfResources, + VolumeCorrupted, + VolumeFull, + NoMedia, + MediaChanged, + NotFound, + AccessDenied, + NoResponse, + NoMapping, + Timeout, + NotStarted, + AlreadyStarted, + Aborted, + IcmpError, + TftpError, + ProtocolError, + IncompatibleVersion, + SecurityViolation, + CrcError, + EndOfMedia, + EndOfFile, + InvalidLanguage, + CompromisedData, + IpAddressConflict, + HttpError, + NetworkUnreachable, + HostUnreachable, + ProtocolUnreachable, + PortUnreachable, + ConnectionFin, + ConnectionReset, + ConnectionRefused, + WarnUnknownGlyph, + WarnDeleteFailure, + WarnWriteFailure, + WarnBufferTooSmall, + WarnStaleData, + WarnFileSystem, + WarnResetRequired, + }; + + pub fn err(self: Status) EfiError!void { + inline for (@typeInfo(EfiError).ErrorSet.?) |efi_err| { + if (self == @field(Status, efi_err.name)) { + return @field(EfiError, efi_err.name); + } + } + // self is .Success + } }; + +test "status" { + var st: Status = .DeviceError; + try testing.expectError(error.DeviceError, st.err()); + + st = .Success; + try st.err(); +} From c727bd1bb68917f39f03f68765c5db7572069729 Mon Sep 17 00:00:00 2001 From: fifty-six Date: Sun, 16 Jan 2022 02:52:45 -0500 Subject: [PATCH 0014/2031] std/os/uefi: Fix parameter type mismatch in edid_override_protocol --- lib/std/os/uefi/protocols/edid_override_protocol.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/os/uefi/protocols/edid_override_protocol.zig b/lib/std/os/uefi/protocols/edid_override_protocol.zig index 800c269c67..3eb39330f4 100644 --- a/lib/std/os/uefi/protocols/edid_override_protocol.zig +++ b/lib/std/os/uefi/protocols/edid_override_protocol.zig @@ -9,7 +9,7 @@ pub const EdidOverrideProtocol = extern struct { /// Returns policy information and potentially a replacement EDID for the specified video output device. pub fn getEdid(self: *const EdidOverrideProtocol, handle: Handle, attributes: *EdidOverrideProtocolAttributes, edid_size: *usize, edid: *?[*]u8) Status { - return self._get_edid(self, handle, attributes, edid_size, edid); + return self._get_edid(self, handle, @ptrCast(*u32, attributes), edid_size, edid); } pub const guid align(8) = Guid{ From 03347114c3e5f6238d7871e878e1d2295bdf0260 Mon Sep 17 00:00:00 2001 From: fifty-six Date: Sun, 16 Jan 2022 03:04:37 -0500 Subject: [PATCH 0015/2031] std/os/uefi: Add util function for opening protocols --- lib/std/os/uefi/tables/boot_services.zig | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/std/os/uefi/tables/boot_services.zig b/lib/std/os/uefi/tables/boot_services.zig index 468aaf4cf7..ee1e72f094 100644 --- a/lib/std/os/uefi/tables/boot_services.zig +++ b/lib/std/os/uefi/tables/boot_services.zig @@ -154,6 +154,27 @@ pub const BootServices = extern struct { /// Creates an event in a group. createEventEx: fn (type: u32, notify_tpl: usize, notify_func: EfiEventNotify, notify_ctx: *const anyopaque, event_group: *align(8) const Guid, event: *Event) callconv(.C) Status, + /// Opens a protocol with a structure as the loaded image for a UEFI application + pub fn openProtocolSt(self: *BootServices, comptime protocol: type, handle: Handle) !*protocol { + if (!@hasDecl(protocol, "guid")) + @compileError("Protocol is missing guid!"); + + var ptr: ?*protocol = undefined; + + try self.openProtocol( + handle, + &protocol.guid, + @ptrCast(*?*anyopaque, &ptr), + // Invoking handle (loaded image) + uefi.handle, + // Control handle (null as not a driver) + null, + uefi.tables.OpenProtocolAttributes{ .by_handle_protocol = true }, + ).err(); + + return ptr.?; + } + pub const signature: u64 = 0x56524553544f4f42; pub const event_timer: u32 = 0x80000000; From ff6ece3811a1cb44a90986d543d1583a40629edf Mon Sep 17 00:00:00 2001 From: fifty-six Date: Sun, 16 Jan 2022 03:50:50 -0500 Subject: [PATCH 0016/2031] std/os/uefi: Don't treat efi status warnings as errors --- lib/std/os/uefi/status.zig | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/std/os/uefi/status.zig b/lib/std/os/uefi/status.zig index e6f2024f7d..e975b92a15 100644 --- a/lib/std/os/uefi/status.zig +++ b/lib/std/os/uefi/status.zig @@ -183,13 +183,6 @@ pub const Status = enum(usize) { ConnectionFin, ConnectionReset, ConnectionRefused, - WarnUnknownGlyph, - WarnDeleteFailure, - WarnWriteFailure, - WarnBufferTooSmall, - WarnStaleData, - WarnFileSystem, - WarnResetRequired, }; pub fn err(self: Status) EfiError!void { @@ -198,7 +191,7 @@ pub const Status = enum(usize) { return @field(EfiError, efi_err.name); } } - // self is .Success + // self is .Success or Warning } }; From b7c7fba5b4f3cf80b36b75bacd401e12a25cf6f8 Mon Sep 17 00:00:00 2001 From: Stephen Gregoratto Date: Sat, 22 Jan 2022 18:14:07 +1100 Subject: [PATCH 0017/2031] Add classic BPF library This library contains: - The global constants as used by C code. - An Insn struct that implements can generate all the BPF instructions. - A simple BPF virtual machine implementation that can be used for testing programs. This has complete code-coverage and has been extensively fuzzed. --- lib/std/x.zig | 1 + lib/std/x/net/bpf.zig | 1008 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1009 insertions(+) create mode 100644 lib/std/x/net/bpf.zig diff --git a/lib/std/x.zig b/lib/std/x.zig index be0ab25e7a..64caf324ed 100644 --- a/lib/std/x.zig +++ b/lib/std/x.zig @@ -9,6 +9,7 @@ pub const os = struct { pub const net = struct { pub const ip = @import("x/net/ip.zig"); pub const tcp = @import("x/net/tcp.zig"); + pub const bpf = @import("x/net/bpf.zig"); }; test { diff --git a/lib/std/x/net/bpf.zig b/lib/std/x/net/bpf.zig new file mode 100644 index 0000000000..a99390ab62 --- /dev/null +++ b/lib/std/x/net/bpf.zig @@ -0,0 +1,1008 @@ +//! This package provides instrumentation for creating Berkeley Packet Filter[1] +//! (BPF) programs, along with a simulator for running them. +//! +//! BPF is a mechanism for cheap, in-kernel packet filtering. Programs are +//! attached to a network device and executed for every packet that flows +//! through it. The program must then return a verdict: the amount of packet +//! bytes that the kernel should copy into userspace. Execution speed is +//! achieved by having programs run in a limited virtual machine, which has the +//! added benefit of graceful failure in the face of buggy programs. +//! +//! The BPF virtual machine has a 32-bit word length and a small number of +//! word-sized registers: +//! +//! - The accumulator, `a`: The source/destination of arithmetic and logic +//! operations. +//! - The index register, `x`: Used as an offset for indirect memory access and +//! as a comparison value for conditional jumps. +//! - The scratch memory store, `M[0]..M[15]`: Used for saving the value of a/x +//! for later use. +//! +//! The packet being examined is an array of bytes, and is addressed using plain +//! array subscript notation, e.g. [10] for the byte at offset 10. An implicit +//! program counter, `pc`, is intialized to zero and incremented for each instruction. +//! +//! The machine has a fixed instruction set with the following form, where the +//! numbers represent bit length: +//! +//! ``` +//! ┌───────────┬──────┬──────┐ +//! │ opcode:16 │ jt:8 │ jt:8 │ +//! ├───────────┴──────┴──────┤ +//! │ k:32 │ +//! └─────────────────────────┘ +//! ``` +//! +//! The `opcode` indicates the instruction class and its addressing mode. +//! Opcodes are generated by performing binary addition on the 8-bit class and +//! mode constants. For example, the opcode for loading a byte from the packet +//! at X + 2, (`ldb [x + 2]`), is: +//! +//! ``` +//! LD | IND | B = 0x00 | 0x40 | 0x20 +//! = 0x60 +//! ``` +//! +//! `jt` is an offset used for conditional jumps, and increments the program +//! counter by its amount if the comparison was true. Conversely, `jf` +//! increments the counter if it was false. These fields are ignored in all +//! other cases. `k` is a generic variable used for various purposes, most +//! commonly as some sort of constant. +//! +//! This package contains opcode extensions used by different implementations, +//! where "extension" is anything outside of the original that was imported into +//! 4.4BSD[2]. These are marked with "EXTENSION", along with a list of +//! implementations that use them. +//! +//! Most of the doc-comments use the BPF assembly syntax as described in the +//! original paper[1]. For the sake of completeness, here is the complete +//! instruction set, along with the extensions: +//! +//!``` +//! opcode addressing modes +//! ld #k #len M[k] [k] [x + k] +//! ldh [k] [x + k] +//! ldb [k] [x + k] +//! ldx #k #len M[k] 4 * ([k] & 0xf) arc4random() +//! st M[k] +//! stx M[k] +//! jmp L +//! jeq #k, Lt, Lf +//! jgt #k, Lt, Lf +//! jge #k, Lt, Lf +//! jset #k, Lt, Lf +//! add #k x +//! sub #k x +//! mul #k x +//! div #k x +//! or #k x +//! and #k x +//! lsh #k x +//! rsh #k x +//! neg #k x +//! mod #k x +//! xor #k x +//! ret #k a +//! tax +//! txa +//! ``` +//! +//! Finally, a note on program design. The lack of backwards jumps leads to a +//! "return early, return often" control flow. Take for example the program +//! generated from the tcpdump filter `ip`: +//! +//! ``` +//! (000) ldh [12] ; Ethernet Packet Type +//! (001) jeq #0x86dd, 2, 7 ; ETHERTYPE_IPV6 +//! (002) ldb [20] ; IPv6 Next Header +//! (003) jeq #0x6, 10, 4 ; TCP +//! (004) jeq #0x2c, 5, 11 ; IPv6 Fragment Header +//! (005) ldb [54] ; TCP Source Port +//! (006) jeq #0x6, 10, 11 ; IPPROTO_TCP +//! (007) jeq #0x800, 8, 11 ; ETHERTYPE_IP +//! (008) ldb [23] ; IPv4 Protocol +//! (009) jeq #0x6, 10, 11 ; IPPROTO_TCP +//! (010) ret #262144 ; copy 0x40000 +//! (011) ret #0 ; skip packet +//! ``` +//! +//! Here we can make a few observations: +//! +//! - The problem "filter only tcp packets" has essentially been transformed +//! into a series of layer checks. +//! - There are two distinct branches in the code, one for validating IPv4 +//! headers and one for IPv6 headers. +//! - Most conditional jumps in these branches lead directly to the last two +//! instructions, a pass or fail. Thus the goal of a program is to find the +//! fastest route to a pass/fail comparison. +//! +//! [1]: S. McCanne and V. Jacobson, "The BSD Packet Filter: A New Architecture +//! for User-level Packet Capture", Proceedings of the 1993 Winter USENIX. +//! [2]: https://minnie.tuhs.org/cgi-bin/utree.pl?file=4.4BSD/usr/src/sys/net/bpf.h +const std = @import("std"); +const builtin = @import("builtin"); +const native_endian = builtin.target.cpu.arch.endian(); +const mem = std.mem; +const math = std.math; +const random = std.crypto.random; +const assert = std.debug.assert; +const expectEqual = std.testing.expectEqual; +const expectError = std.testing.expectError; +const expect = std.testing.expect; + +// instruction classes +/// ld, ldh, ldb: Load data into a. +pub const LD = 0x00; +/// ldx: Load data into x. +pub const LDX = 0x01; +/// st: Store into scratch memory the value of a. +pub const ST = 0x02; +/// st: Store into scratch memory the value of x. +pub const STX = 0x03; +/// alu: Wrapping arithmetic/bitwise operations on a using the value of k/x. +pub const ALU = 0x04; +/// jmp, jeq, jgt, je, jset: Increment the program counter based on a comparison +/// between k/x and the accumulator. +pub const JMP = 0x05; +/// ret: Return a verdict using the value of k/the accumulator. +pub const RET = 0x06; +/// tax, txa: Register value copying between X and a. +pub const MISC = 0x07; + +// Size of data to be loaded from the packet. +/// ld: 32-bit full word. +pub const W = 0x00; +/// ldh: 16-bit half word. +pub const H = 0x08; +/// ldb: Single byte. +pub const B = 0x10; + +// Addressing modes used for loads to a/x. +/// #k: The immediate value stored in k. +pub const IMM = 0x00; +/// [k]: The value at offset k in the packet. +pub const ABS = 0x20; +/// [x + k]: The value at offset x + k in the packet. +pub const IND = 0x40; +/// M[k]: The value of the k'th scratch memory register. +pub const MEM = 0x60; +/// #len: The size of the packet. +pub const LEN = 0x80; +/// 4 * ([k] & 0xf): Four times the low four bits of the byte at offset k in the +/// packet. This is used for efficiently loading the header length of an IP +/// packet. +pub const MSH = 0xa0; +/// arc4random: 32-bit integer generated from a CPRNG (see arc4random(3)) loaded into a. +/// EXTENSION. Defined for: +/// - OpenBSD. +pub const RND = 0xc0; + +// Modifiers for different instruction classes. +/// Use the value of k for alu operations (add #k). +/// Compare against the value of k for jumps (jeq #k, Lt, Lf). +/// Return the value of k for returns (ret #k). +pub const K = 0x00; +/// Use the value of x for alu operations (add x). +/// Compare against the value of X for jumps (jeq x, Lt, Lf). +pub const X = 0x08; +/// Return the value of a for returns (ret a). +pub const A = 0x10; + +// ALU Operations on a using the value of k/x. +// All arithmetic operations are defined to overflow the value of a. +/// add: a = a + k +/// a = a + x. +pub const ADD = 0x00; +/// sub: a = a - k +/// a = a - x. +pub const SUB = 0x10; +/// mul: a = a * k +/// a = a * x. +pub const MUL = 0x20; +/// div: a = a / k +/// a = a / x. +/// Truncated division. +pub const DIV = 0x30; +/// or: a = a | k +/// a = a | x. +pub const OR = 0x40; +/// and: a = a & k +/// a = a & x. +pub const AND = 0x50; +/// lsh: a = a << k +/// a = a << x. +/// a = a << k, a = a << x. +pub const LSH = 0x60; +/// rsh: a = a >> k +/// a = a >> x. +pub const RSH = 0x70; +/// neg: a = -a. +/// Note that this isn't a binary negation, rather the value of `~a + 1`. +pub const NEG = 0x80; +/// mod: a = a % k +/// a = a % x. +/// EXTENSION. Defined for: +/// - Linux. +/// - NetBSD + Minix 3. +/// - FreeBSD and derivitives. +pub const MOD = 0x90; +/// xor: a = a ^ k +/// a = a ^ x. +/// EXTENSION. Defined for: +/// - Linux. +/// - NetBSD + Minix 3. +/// - FreeBSD and derivitives. +pub const XOR = 0xa0; + +// Jump operations using a comparison between a and x/k. +/// jmp L: pc += k. +/// No comparison done here. +pub const JA = 0x00; +/// jeq #k, Lt, Lf: pc += (a == k) ? jt : jf. +/// jeq x, Lt, Lf: pc += (a == x) ? jt : jf. +pub const JEQ = 0x10; +/// jgt #k, Lt, Lf: pc += (a > k) ? jt : jf. +/// jgt x, Lt, Lf: pc += (a > x) ? jt : jf. +pub const JGT = 0x20; +/// jge #k, Lt, Lf: pc += (a >= k) ? jt : jf. +/// jge x, Lt, Lf: pc += (a >= x) ? jt : jf. +pub const JGE = 0x30; +/// jset #k, Lt, Lf: pc += (a & k > 0) ? jt : jf. +/// jset x, Lt, Lf: pc += (a & x > 0) ? jt : jf. +pub const JSET = 0x40; + +// Miscellaneous operations/register copy. +/// tax: x = a. +pub const TAX = 0x00; +/// txa: a = x. +pub const TXA = 0x80; + +/// The 16 registers in the scratch memory store as named enums. +pub const Scratch = enum(u4) { m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15 }; +pub const MEMWORDS = 16; +pub const MAXINSNS = switch (builtin.os.tag) { + .linux => 4096, + else => 512, +}; +pub const MINBUFSIZE = 32; +pub const MAXBUFSIZE = 1 << 21; + +pub const Insn = extern struct { + opcode: u16, + jt: u8, + jf: u8, + k: u32, + + /// Implements the `std.fmt.format` API. + /// The formatting is similar to the output of tcpdump -dd. + pub fn format( + self: Insn, + comptime layout: []const u8, + opts: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = opts; + if (comptime layout.len != 0 and layout[0] != 's') + @compileError("Unsupported format specifier for BPF Insn type '" ++ layout ++ "'."); + + try std.fmt.format( + writer, + "Insn{{ 0x{X:0<2}, {d}, {d}, 0x{X:0<8} }}", + .{ self.opcode, self.jt, self.jf, self.k }, + ); + } + + const Size = enum(u8) { + word = W, + half_word = H, + byte = B, + }; + + fn stmt(opcode: u16, k: u32) Insn { + return .{ + .opcode = opcode, + .jt = 0, + .jf = 0, + .k = k, + }; + } + + pub fn ld_imm(value: u32) Insn { + return stmt(LD | IMM, value); + } + + pub fn ld_abs(size: Size, offset: u32) Insn { + return stmt(LD | ABS | @enumToInt(size), offset); + } + + pub fn ld_ind(size: Size, offset: u32) Insn { + return stmt(LD | IND | @enumToInt(size), offset); + } + + pub fn ld_mem(reg: Scratch) Insn { + return stmt(LD | MEM, @enumToInt(reg)); + } + + pub fn ld_len() Insn { + return stmt(LD | LEN | W, 0); + } + + pub fn ld_rnd() Insn { + return stmt(LD | RND | W, 0); + } + + pub fn ldx_imm(value: u32) Insn { + return stmt(LDX | IMM, value); + } + + pub fn ldx_mem(reg: Scratch) Insn { + return stmt(LDX | MEM, @enumToInt(reg)); + } + + pub fn ldx_len() Insn { + return stmt(LDX | LEN | W, 0); + } + + pub fn ldx_msh(offset: u32) Insn { + return stmt(LDX | MSH | B, offset); + } + + pub fn st(reg: Scratch) Insn { + return stmt(ST, @enumToInt(reg)); + } + pub fn stx(reg: Scratch) Insn { + return stmt(STX, @enumToInt(reg)); + } + + const AluOp = enum(u16) { + add = ADD, + sub = SUB, + mul = MUL, + div = DIV, + @"or" = OR, + @"and" = AND, + lsh = LSH, + rsh = RSH, + mod = MOD, + xor = XOR, + }; + + const Source = enum(u16) { + k = K, + x = X, + }; + const KOrX = union(Source) { + k: u32, + x: void, + }; + + pub fn alu_neg() Insn { + return stmt(ALU | NEG, 0); + } + + pub fn alu(op: AluOp, source: KOrX) Insn { + return stmt( + ALU | @enumToInt(op) | @enumToInt(source), + if (source == .k) source.k else 0, + ); + } + + const JmpOp = enum(u16) { + jeq = JEQ, + jgt = JGT, + jge = JGE, + jset = JSET, + }; + + pub fn jmp_ja(location: u32) Insn { + return stmt(JMP | JA, location); + } + + pub fn jmp(op: JmpOp, source: KOrX, jt: u8, jf: u8) Insn { + return Insn{ + .opcode = JMP | @enumToInt(op) | @enumToInt(source), + .jt = jt, + .jf = jf, + .k = if (source == .k) source.k else 0, + }; + } + + const Verdict = enum(u16) { + k = K, + a = A, + }; + const KOrA = union(Verdict) { + k: u32, + a: void, + }; + + pub fn ret(verdict: KOrA) Insn { + return stmt( + RET | @enumToInt(verdict), + if (verdict == .k) verdict.k else 0, + ); + } + + pub fn tax() Insn { + return stmt(MISC | TAX, 0); + } + + pub fn txa() Insn { + return stmt(MISC | TXA, 0); + } +}; + +fn opcodeEqual(opcode: u16, insn: Insn) !void { + try expectEqual(opcode, insn.opcode); +} + +test "opcodes" { + try opcodeEqual(0x00, Insn.ld_imm(0)); + try opcodeEqual(0x20, Insn.ld_abs(.word, 0)); + try opcodeEqual(0x28, Insn.ld_abs(.half_word, 0)); + try opcodeEqual(0x30, Insn.ld_abs(.byte, 0)); + try opcodeEqual(0x40, Insn.ld_ind(.word, 0)); + try opcodeEqual(0x48, Insn.ld_ind(.half_word, 0)); + try opcodeEqual(0x50, Insn.ld_ind(.byte, 0)); + try opcodeEqual(0x60, Insn.ld_mem(.m0)); + try opcodeEqual(0x80, Insn.ld_len()); + try opcodeEqual(0xc0, Insn.ld_rnd()); + + try opcodeEqual(0x01, Insn.ldx_imm(0)); + try opcodeEqual(0x61, Insn.ldx_mem(.m0)); + try opcodeEqual(0x81, Insn.ldx_len()); + try opcodeEqual(0xb1, Insn.ldx_msh(0)); + + try opcodeEqual(0x02, Insn.st(.m0)); + try opcodeEqual(0x03, Insn.stx(.m0)); + + try opcodeEqual(0x04, Insn.alu(.add, .{ .k = 0 })); + try opcodeEqual(0x14, Insn.alu(.sub, .{ .k = 0 })); + try opcodeEqual(0x24, Insn.alu(.mul, .{ .k = 0 })); + try opcodeEqual(0x34, Insn.alu(.div, .{ .k = 0 })); + try opcodeEqual(0x44, Insn.alu(.@"or", .{ .k = 0 })); + try opcodeEqual(0x54, Insn.alu(.@"and", .{ .k = 0 })); + try opcodeEqual(0x64, Insn.alu(.lsh, .{ .k = 0 })); + try opcodeEqual(0x74, Insn.alu(.rsh, .{ .k = 0 })); + try opcodeEqual(0x94, Insn.alu(.mod, .{ .k = 0 })); + try opcodeEqual(0xa4, Insn.alu(.xor, .{ .k = 0 })); + try opcodeEqual(0x84, Insn.alu_neg()); + try opcodeEqual(0x0c, Insn.alu(.add, .x)); + try opcodeEqual(0x1c, Insn.alu(.sub, .x)); + try opcodeEqual(0x2c, Insn.alu(.mul, .x)); + try opcodeEqual(0x3c, Insn.alu(.div, .x)); + try opcodeEqual(0x4c, Insn.alu(.@"or", .x)); + try opcodeEqual(0x5c, Insn.alu(.@"and", .x)); + try opcodeEqual(0x6c, Insn.alu(.lsh, .x)); + try opcodeEqual(0x7c, Insn.alu(.rsh, .x)); + try opcodeEqual(0x9c, Insn.alu(.mod, .x)); + try opcodeEqual(0xac, Insn.alu(.xor, .x)); + + try opcodeEqual(0x05, Insn.jmp_ja(0)); + try opcodeEqual(0x15, Insn.jmp(.jeq, .{ .k = 0 }, 0, 0)); + try opcodeEqual(0x25, Insn.jmp(.jgt, .{ .k = 0 }, 0, 0)); + try opcodeEqual(0x35, Insn.jmp(.jge, .{ .k = 0 }, 0, 0)); + try opcodeEqual(0x45, Insn.jmp(.jset, .{ .k = 0 }, 0, 0)); + try opcodeEqual(0x1d, Insn.jmp(.jeq, .x, 0, 0)); + try opcodeEqual(0x2d, Insn.jmp(.jgt, .x, 0, 0)); + try opcodeEqual(0x3d, Insn.jmp(.jge, .x, 0, 0)); + try opcodeEqual(0x4d, Insn.jmp(.jset, .x, 0, 0)); + + try opcodeEqual(0x06, Insn.ret(.{ .k = 0 })); + try opcodeEqual(0x16, Insn.ret(.a)); + + try opcodeEqual(0x07, Insn.tax()); + try opcodeEqual(0x87, Insn.txa()); +} + +pub const Error = error{ + InvalidOpcode, + InvalidOffset, + InvalidLocation, + DivisionByZero, + NoReturn, +}; + +/// A simple implementation of the BPF virtual-machine. +/// Use this to run/debug programs. +pub fn simulate( + packet: []const u8, + filter: []const Insn, + byte_order: std.builtin.Endian, +) Error!u32 { + assert(filter.len > 0 and filter.len < MAXINSNS); + assert(packet.len < MAXBUFSIZE); + const len = @intCast(u32, packet.len); + + var a: u32 = 0; + var x: u32 = 0; + var m = mem.zeroes([MEMWORDS]u32); + var pc: usize = 0; + + while (pc < filter.len) : (pc += 1) { + const i = filter[pc]; + // Cast to a wider type to protect against overflow. + const k = @as(u64, i.k); + const remaining = filter.len - (pc + 1); + + // Do validation/error checking here to compress the second switch. + switch (i.opcode) { + LD | ABS | W => if (k + @sizeOf(u32) - 1 >= packet.len) return error.InvalidOffset, + LD | ABS | H => if (k + @sizeOf(u16) - 1 >= packet.len) return error.InvalidOffset, + LD | ABS | B => if (k >= packet.len) return error.InvalidOffset, + LD | IND | W => if (k + x + @sizeOf(u32) - 1 >= packet.len) return error.InvalidOffset, + LD | IND | H => if (k + x + @sizeOf(u16) - 1 >= packet.len) return error.InvalidOffset, + LD | IND | B => if (k + x >= packet.len) return error.InvalidOffset, + + LDX | MSH | B => if (k >= packet.len) return error.InvalidOffset, + ST, STX, LD | MEM, LDX | MEM => if (i.k >= MEMWORDS) return error.InvalidOffset, + + JMP | JA => if (remaining <= i.k) return error.InvalidOffset, + JMP | JEQ | K, + JMP | JGT | K, + JMP | JGE | K, + JMP | JSET | K, + JMP | JEQ | X, + JMP | JGT | X, + JMP | JGE | X, + JMP | JSET | X, + => if (remaining <= i.jt or remaining <= i.jf) return error.InvalidLocation, + else => {}, + } + switch (i.opcode) { + LD | IMM => a = i.k, + LD | MEM => a = m[i.k], + LD | LEN | W => a = len, + LD | RND | W => a = random.int(u32), + LD | ABS | W => a = mem.readInt(u32, packet[i.k..][0..@sizeOf(u32)], byte_order), + LD | ABS | H => a = mem.readInt(u16, packet[i.k..][0..@sizeOf(u16)], byte_order), + LD | ABS | B => a = packet[i.k], + LD | IND | W => a = mem.readInt(u32, packet[i.k + x ..][0..@sizeOf(u32)], byte_order), + LD | IND | H => a = mem.readInt(u16, packet[i.k + x ..][0..@sizeOf(u16)], byte_order), + LD | IND | B => a = packet[i.k + x], + + LDX | IMM => x = i.k, + LDX | MEM => x = m[i.k], + LDX | LEN | W => x = len, + LDX | MSH | B => x = @as(u32, @truncate(u4, packet[i.k])) << 2, + + ST => m[i.k] = a, + STX => m[i.k] = x, + + ALU | ADD | K => a +%= i.k, + ALU | SUB | K => a -%= i.k, + ALU | MUL | K => a *%= i.k, + ALU | DIV | K => a = try math.divTrunc(u32, a, i.k), + ALU | OR | K => a |= i.k, + ALU | AND | K => a &= i.k, + ALU | LSH | K => a = math.shl(u32, a, i.k), + ALU | RSH | K => a = math.shr(u32, a, i.k), + ALU | MOD | K => a = try math.mod(u32, a, i.k), + ALU | XOR | K => a ^= i.k, + ALU | ADD | X => a +%= x, + ALU | SUB | X => a -%= x, + ALU | MUL | X => a *%= x, + ALU | DIV | X => a = try math.divTrunc(u32, a, x), + ALU | OR | X => a |= x, + ALU | AND | X => a &= x, + ALU | LSH | X => a = math.shl(u32, a, x), + ALU | RSH | X => a = math.shr(u32, a, x), + ALU | MOD | X => a = try math.mod(u32, a, x), + ALU | XOR | X => a ^= x, + ALU | NEG => a = @bitCast(u32, -%@bitCast(i32, a)), + + JMP | JA => pc += i.k, + JMP | JEQ | K => pc += if (a == i.k) i.jt else i.jf, + JMP | JGT | K => pc += if (a > i.k) i.jt else i.jf, + JMP | JGE | K => pc += if (a >= i.k) i.jt else i.jf, + JMP | JSET | K => pc += if (a & i.k > 0) i.jt else i.jf, + JMP | JEQ | X => pc += if (a == x) i.jt else i.jf, + JMP | JGT | X => pc += if (a > x) i.jt else i.jf, + JMP | JGE | X => pc += if (a >= x) i.jt else i.jf, + JMP | JSET | X => pc += if (a & x > 0) i.jt else i.jf, + + RET | K => return i.k, + RET | A => return a, + + MISC | TAX => x = a, + MISC | TXA => a = x, + else => return error.InvalidOpcode, + } + } + + return error.NoReturn; +} + +// This program is the BPF form of the tcpdump filter: +// +// tcpdump -dd 'ip host mirror.internode.on.net and tcp port ftp-data' +// +// As of January 2022, mirror.internode.on.net resolves to 150.101.135.3 +// +// For reference, here's what it looks like in BPF assembler. +// Note that the jumps are used for TCP/IP layer checks. +// +// ``` +// ldh [12] (#proto) +// jeq #0x0800 (ETHERTYPE_IP), L1, fail +// L1: ld [26] +// jeq #150.101.135.3, L2, dest +// dest: ld [30] +// jeq #150.101.135.3, L2, fail +// L2: ldb [23] +// jeq #0x6 (IPPROTO_TCP), L3, fail +// L3: ldh [20] +// jset #0x1fff, fail, plen +// plen: ldx 4 * ([14] & 0xf) +// ldh [x + 14] +// jeq #0x14 (FTP), pass, dstp +// dstp: ldh [x + 16] +// jeq #0x14 (FTP), pass, fail +// pass: ret #0x40000 +// fail: ret #0 +// ``` +const tcpdump_filter = [_]Insn{ + Insn.ld_abs(.half_word, 12), + Insn.jmp(.jeq, .{ .k = 0x800 }, 0, 14), + Insn.ld_abs(.word, 26), + Insn.jmp(.jeq, .{ .k = 0x96658703 }, 2, 0), + Insn.ld_abs(.word, 30), + Insn.jmp(.jeq, .{ .k = 0x96658703 }, 0, 10), + Insn.ld_abs(.byte, 23), + Insn.jmp(.jeq, .{ .k = 0x6 }, 0, 8), + Insn.ld_abs(.half_word, 20), + Insn.jmp(.jset, .{ .k = 0x1fff }, 6, 0), + Insn.ldx_msh(14), + Insn.ld_ind(.half_word, 14), + Insn.jmp(.jeq, .{ .k = 0x14 }, 2, 0), + Insn.ld_ind(.half_word, 16), + Insn.jmp(.jeq, .{ .k = 0x14 }, 0, 1), + Insn.ret(.{ .k = 0x40000 }), + Insn.ret(.{ .k = 0 }), +}; + +// This packet is the output of `ls` on mirror.internode.on.net:/, captured +// using the filter above. +// +// zig fmt: off +const ftp_data = [_]u8{ + // ethernet - 14 bytes: IPv4(0x0800) from a4:71:74:ad:4b:f0 -> de:ad:be:ef:f0:0f + 0xde, 0xad, 0xbe, 0xef, 0xf0, 0x0f, 0xa4, 0x71, 0x74, 0xad, 0x4b, 0xf0, 0x08, 0x00, + // IPv4 - 20 bytes: TCP data from 150.101.135.3 -> 192.168.1.3 + 0x45, 0x00, 0x01, 0xf2, 0x70, 0x3b, 0x40, 0x00, 0x37, 0x06, 0xf2, 0xb6, + 0x96, 0x65, 0x87, 0x03, 0xc0, 0xa8, 0x01, 0x03, + // TCP - 32 bytes: Source port: 20 (FTP). Payload = 446 bytes + 0x00, 0x14, 0x80, 0x6d, 0x35, 0x81, 0x2d, 0x40, 0x4f, 0x8a, 0x29, 0x9e, 0x80, 0x18, 0x00, 0x2e, + 0x88, 0x8d, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x0b, 0x59, 0x5d, 0x09, 0x32, 0x8b, 0x51, 0xa0 +} ++ + // Raw line-based FTP data - 446 bytes + "lrwxrwxrwx 1 root root 12 Feb 14 2012 debian -> .pub2/debian\r\n" ++ + "lrwxrwxrwx 1 root root 15 Feb 14 2012 debian-cd -> .pub2/debian-cd\r\n" ++ + "lrwxrwxrwx 1 root root 9 Mar 9 2018 linux -> pub/linux\r\n" ++ + "drwxr-xr-X 3 mirror mirror 4096 Sep 20 08:10 pub\r\n" ++ + "lrwxrwxrwx 1 root root 12 Feb 14 2012 ubuntu -> .pub2/ubuntu\r\n" ++ + "-rw-r--r-- 1 root root 1044 Jan 20 2015 welcome.msg\r\n"; +// zig fmt: on + +test "tcpdump filter" { + try expectEqual( + @as(u32, 0x40000), + try simulate(ftp_data, &tcpdump_filter, .Big), + ); +} + +fn expectPass(data: anytype, filter: []Insn) !void { + try expectEqual( + @as(u32, 0), + try simulate(mem.asBytes(data), filter, .Big), + ); +} + +fn expectFail(expected_error: anyerror, data: anytype, filter: []Insn) !void { + try expectError( + expected_error, + simulate(mem.asBytes(data), filter, native_endian), + ); +} + +test "simulator coverage" { + const some_data: packed struct { + foo: u32, + bar: u8, + } = .{ + .foo = mem.nativeToBig(u32, 0xaabbccdd), + .bar = 0x7f, + }; + + try expectPass(&some_data, &.{ + // ld #10 + // ldx #1 + // st M[0] + // stx M[1] + // fail if A != 10 + Insn.ld_imm(10), + Insn.ldx_imm(1), + Insn.st(.m0), + Insn.stx(.m1), + Insn.jmp(.jeq, .{ .k = 10 }, 1, 0), + Insn.ret(.{ .k = 1 }), + // ld [0] + // fail if A != 0xaabbccdd + Insn.ld_abs(.word, 0), + Insn.jmp(.jeq, .{ .k = 0xaabbccdd }, 1, 0), + Insn.ret(.{ .k = 2 }), + // ldh [0] + // fail if A != 0xaabb + Insn.ld_abs(.half_word, 0), + Insn.jmp(.jeq, .{ .k = 0xaabb }, 1, 0), + Insn.ret(.{ .k = 3 }), + // ldb [0] + // fail if A != 0xaa + Insn.ld_abs(.byte, 0), + Insn.jmp(.jeq, .{ .k = 0xaa }, 1, 0), + Insn.ret(.{ .k = 4 }), + // ld [x + 0] + // fail if A != 0xbbccdd7f + Insn.ld_ind(.word, 0), + Insn.jmp(.jeq, .{ .k = 0xbbccdd7f }, 1, 0), + Insn.ret(.{ .k = 5 }), + // ldh [x + 0] + // fail if A != 0xbbcc + Insn.ld_ind(.half_word, 0), + Insn.jmp(.jeq, .{ .k = 0xbbcc }, 1, 0), + Insn.ret(.{ .k = 6 }), + // ldb [x + 0] + // fail if A != 0xbb + Insn.ld_ind(.byte, 0), + Insn.jmp(.jeq, .{ .k = 0xbb }, 1, 0), + Insn.ret(.{ .k = 7 }), + // ld M[0] + // fail if A != 10 + Insn.ld_mem(.m0), + Insn.jmp(.jeq, .{ .k = 10 }, 1, 0), + Insn.ret(.{ .k = 8 }), + // ld #len + // fail if A != 5 + Insn.ld_len(), + Insn.jmp(.jeq, .{ .k = @sizeOf(@TypeOf(some_data)) }, 1, 0), + Insn.ret(.{ .k = 9 }), + // ld #0 + // ld arc4random() + // fail if A == 0 + Insn.ld_imm(0), + Insn.ld_rnd(), + Insn.jmp(.jgt, .{ .k = 0 }, 1, 0), + Insn.ret(.{ .k = 10 }), + // ld #3 + // ldx #10 + // st M[2] + // txa + // fail if a != x + Insn.ld_imm(3), + Insn.ldx_imm(10), + Insn.st(.m2), + Insn.txa(), + Insn.jmp(.jeq, .x, 1, 0), + Insn.ret(.{ .k = 11 }), + // ldx M[2] + // fail if A <= X + Insn.ldx_mem(.m2), + Insn.jmp(.jgt, .x, 1, 0), + Insn.ret(.{ .k = 12 }), + // ldx #len + // fail if a <= x + Insn.ldx_len(), + Insn.jmp(.jgt, .x, 1, 0), + Insn.ret(.{ .k = 13 }), + // a = 4 * (0x7f & 0xf) + // x = 4 * ([4] & 0xf) + // fail if a != x + Insn.ld_imm(4 * (0x7f & 0xf)), + Insn.ldx_msh(4), + Insn.jmp(.jeq, .x, 1, 0), + Insn.ret(.{ .k = 14 }), + // ld #(u32)-1 + // ldx #2 + // add #1 + // fail if a != 0 + Insn.ld_imm(0xffffffff), + Insn.ldx_imm(2), + Insn.alu(.add, .{ .k = 1 }), + Insn.jmp(.jeq, .{ .k = 0 }, 1, 0), + Insn.ret(.{ .k = 15 }), + // sub #1 + // fail if a != (u32)-1 + Insn.alu(.sub, .{ .k = 1 }), + Insn.jmp(.jeq, .{ .k = 0xffffffff }, 1, 0), + Insn.ret(.{ .k = 16 }), + // add x + // fail if a != 1 + Insn.alu(.add, .x), + Insn.jmp(.jeq, .{ .k = 1 }, 1, 0), + Insn.ret(.{ .k = 17 }), + // sub x + // fail if a != (u32)-1 + Insn.alu(.sub, .x), + Insn.jmp(.jeq, .{ .k = 0xffffffff }, 1, 0), + Insn.ret(.{ .k = 18 }), + // ld #16 + // mul #2 + // fail if a != 32 + Insn.ld_imm(16), + Insn.alu(.mul, .{ .k = 2 }), + Insn.jmp(.jeq, .{ .k = 32 }, 1, 0), + Insn.ret(.{ .k = 19 }), + // mul x + // fail if a != 64 + Insn.alu(.mul, .x), + Insn.jmp(.jeq, .{ .k = 64 }, 1, 0), + Insn.ret(.{ .k = 20 }), + // div #2 + // fail if a != 32 + Insn.alu(.div, .{ .k = 2 }), + Insn.jmp(.jeq, .{ .k = 32 }, 1, 0), + Insn.ret(.{ .k = 21 }), + // div x + // fail if a != 16 + Insn.alu(.div, .x), + Insn.jmp(.jeq, .{ .k = 16 }, 1, 0), + Insn.ret(.{ .k = 22 }), + // or #4 + // fail if a != 20 + Insn.alu(.@"or", .{ .k = 4 }), + Insn.jmp(.jeq, .{ .k = 20 }, 1, 0), + Insn.ret(.{ .k = 23 }), + // or x + // fail if a != 22 + Insn.alu(.@"or", .x), + Insn.jmp(.jeq, .{ .k = 22 }, 1, 0), + Insn.ret(.{ .k = 24 }), + // and #6 + // fail if a != 6 + Insn.alu(.@"and", .{ .k = 0b110 }), + Insn.jmp(.jeq, .{ .k = 6 }, 1, 0), + Insn.ret(.{ .k = 25 }), + // and x + // fail if a != 2 + Insn.alu(.@"and", .x), + Insn.jmp(.jeq, .x, 1, 0), + Insn.ret(.{ .k = 26 }), + // xor #15 + // fail if a != 13 + Insn.alu(.xor, .{ .k = 0b1111 }), + Insn.jmp(.jeq, .{ .k = 0b1101 }, 1, 0), + Insn.ret(.{ .k = 27 }), + // xor x + // fail if a != 15 + Insn.alu(.xor, .x), + Insn.jmp(.jeq, .{ .k = 0b1111 }, 1, 0), + Insn.ret(.{ .k = 28 }), + // rsh #1 + // fail if a != 7 + Insn.alu(.rsh, .{ .k = 1 }), + Insn.jmp(.jeq, .{ .k = 0b0111 }, 1, 0), + Insn.ret(.{ .k = 29 }), + // rsh x + // fail if a != 1 + Insn.alu(.rsh, .x), + Insn.jmp(.jeq, .{ .k = 0b0001 }, 1, 0), + Insn.ret(.{ .k = 30 }), + // lsh #1 + // fail if a != 2 + Insn.alu(.lsh, .{ .k = 1 }), + Insn.jmp(.jeq, .{ .k = 0b0010 }, 1, 0), + Insn.ret(.{ .k = 31 }), + // lsh x + // fail if a != 8 + Insn.alu(.lsh, .x), + Insn.jmp(.jeq, .{ .k = 0b1000 }, 1, 0), + Insn.ret(.{ .k = 32 }), + // mod 6 + // fail if a != 2 + Insn.alu(.mod, .{ .k = 6 }), + Insn.jmp(.jeq, .{ .k = 2 }, 1, 0), + Insn.ret(.{ .k = 33 }), + // mod x + // fail if a != 0 + Insn.alu(.mod, .x), + Insn.jmp(.jeq, .{ .k = 0 }, 1, 0), + Insn.ret(.{ .k = 34 }), + // tax + // neg + // fail if a != (u32)-2 + Insn.txa(), + Insn.alu_neg(), + Insn.jmp(.jeq, .{ .k = ~@as(u32, 2) + 1 }, 1, 0), + Insn.ret(.{ .k = 35 }), + // ja #1 (skip the next instruction) + Insn.jmp_ja(1), + Insn.ret(.{ .k = 36 }), + // ld #20 + // tax + // fail if a != 20 + // fail if a != x + Insn.ld_imm(20), + Insn.tax(), + Insn.jmp(.jeq, .{ .k = 20 }, 1, 0), + Insn.ret(.{ .k = 37 }), + Insn.jmp(.jeq, .x, 1, 0), + Insn.ret(.{ .k = 38 }), + // ld #19 + // fail if a == 20 + // fail if a == x + // fail if a >= 20 + // fail if a >= X + Insn.ld_imm(19), + Insn.jmp(.jeq, .{ .k = 20 }, 0, 1), + Insn.ret(.{ .k = 39 }), + Insn.jmp(.jeq, .x, 0, 1), + Insn.ret(.{ .k = 40 }), + Insn.jmp(.jgt, .{ .k = 20 }, 0, 1), + Insn.ret(.{ .k = 41 }), + Insn.jmp(.jgt, .x, 0, 1), + Insn.ret(.{ .k = 42 }), + // ld #21 + // fail if a < 20 + // fail if a < x + Insn.ld_imm(21), + Insn.jmp(.jgt, .{ .k = 20 }, 1, 0), + Insn.ret(.{ .k = 43 }), + Insn.jmp(.jgt, .x, 1, 0), + Insn.ret(.{ .k = 44 }), + // ldx #22 + // fail if a < 22 + // fail if a < x + Insn.ldx_imm(22), + Insn.jmp(.jge, .{ .k = 22 }, 0, 1), + Insn.ret(.{ .k = 45 }), + Insn.jmp(.jge, .x, 0, 1), + Insn.ret(.{ .k = 46 }), + // ld #23 + // fail if a >= 22 + // fail if a >= x + Insn.ld_imm(23), + Insn.jmp(.jge, .{ .k = 22 }, 1, 0), + Insn.ret(.{ .k = 47 }), + Insn.jmp(.jge, .x, 1, 0), + Insn.ret(.{ .k = 48 }), + // ldx #0b10100 + // fail if a & 0b10100 == 0 + // fail if a & x == 0 + Insn.ldx_imm(0b10100), + Insn.jmp(.jset, .{ .k = 0b10100 }, 1, 0), + Insn.ret(.{ .k = 47 }), + Insn.jmp(.jset, .x, 1, 0), + Insn.ret(.{ .k = 48 }), + // ldx #0 + // fail if a & 0 > 0 + // fail if a & x > 0 + Insn.ldx_imm(0), + Insn.jmp(.jset, .{ .k = 0 }, 0, 1), + Insn.ret(.{ .k = 49 }), + Insn.jmp(.jset, .x, 0, 1), + Insn.ret(.{ .k = 50 }), + Insn.ret(.{ .k = 0 }), + }); + try expectPass(&some_data, &.{ + Insn.ld_imm(35), + Insn.ld_imm(0), + Insn.ret(.a), + }); + + // Errors + try expectFail(error.NoReturn, &some_data, &.{ + Insn.ld_imm(10), + }); + try expectFail(error.InvalidOpcode, &some_data, &.{ + Insn.stmt(0x7f, 0xdeadbeef), + }); + try expectFail(error.InvalidOffset, &some_data, &.{ + Insn.stmt(LD | ABS | W, 10), + }); + try expectFail(error.InvalidLocation, &some_data, &.{ + Insn.jmp(.jeq, .{ .k = 0 }, 10, 0), + }); + try expectFail(error.InvalidLocation, &some_data, &.{ + Insn.jmp(.jeq, .{ .k = 0 }, 0, 10), + }); +} From a49f2d9f8d45ea5d556ebcb24c6f38a13ec3e94b Mon Sep 17 00:00:00 2001 From: Stephen Gregoratto Date: Sat, 29 Jan 2022 13:27:18 +1100 Subject: [PATCH 0018/2031] Add bits for the Linux Auditing System Also adds the _CSKY and _FRV ELF machines that are defined in `` --- lib/std/elf.zig | 6 +++++ lib/std/os/linux.zig | 52 ++++++++++++++++++++++++++++++++++++++++++++ lib/std/target.zig | 2 +- 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 6c3b748c58..1fbad2837b 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -1480,6 +1480,12 @@ pub const EM = enum(u16) { /// Linux kernel bpf virtual machine _BPF = 247, + /// C-SKY + _CSKY = 252, + + /// Fujitsu FR-V + _FRV = 0x5441, + _, }; diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index c1591f7ea1..fe0b71b570 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -5369,3 +5369,55 @@ pub const PERF = struct { pub const IOC_FLAG_GROUP = 1; }; + +// TODO: Add the rest of the AUDIT defines? +pub const AUDIT = struct { + pub const ARCH = enum(u32) { + const _64BIT = 0x80000000; + const _LE = 0x40000000; + + pub const current = switch (native_arch) { + .i386 => .I386, + .x86_64 => .X86_64, + .aarch64 => .AARCH64, + .arm, .thumb => .ARM, + .riscv64 => .RISCV64, + .sparcv9 => .SPARC64, + .mips => .MIPS, + .mipsel => .MIPSEL, + .powerpc => .PPC, + .powerpc64 => .PPC64, + .powerpc64le => .PPC64LE, + else => undefined, + }; + + AARCH64 = toAudit(.aarch64), + ARM = toAudit(.arm), + ARMEB = toAudit(.armeb), + CSKY = toAudit(.csky), + HEXAGON = @enumToInt(std.elf.EM._HEXAGON), + I386 = toAudit(.i386), + M68K = toAudit(.m68k), + MIPS = toAudit(.mips), + MIPSEL = toAudit(.mips) | _LE, + MIPS64 = toAudit(.mips64), + MIPSEL64 = toAudit(.mips64) | _LE, + PPC = toAudit(.powerpc), + PPC64 = toAudit(.powerpc64), + PPC64LE = toAudit(.powerpc64le), + RISCV32 = toAudit(.riscv32), + RISCV64 = toAudit(.riscv64), + S390X = toAudit(.s390x), + SPARC = toAudit(.sparc), + SPARC64 = toAudit(.sparcv9), + X86_64 = toAudit(.x86_64), + + fn toAudit(arch: std.Target.Cpu.Arch) u32 { + var res: u32 = @enumToInt(arch.toElfMachine()); + if (arch.endian() == .Little) res |= _LE; + if (arch.ptrBitWidth() == 64) res |= _64BIT; + + return res; + } + }; +}; diff --git a/lib/std/target.zig b/lib/std/target.zig index ca1f668ca4..5f22923108 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -962,7 +962,7 @@ pub const Target = struct { .amdgcn => ._NONE, .bpfel => ._BPF, .bpfeb => ._BPF, - .csky => ._NONE, + .csky => ._CSKY, .sparcv9 => ._SPARCV9, .s390x => ._S390, .ve => ._NONE, From 29013220d95f60669c4a181d157157aea9f137b5 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 30 Jan 2022 15:24:03 +0100 Subject: [PATCH 0019/2031] wasm: Implement elem_ptr This implements lowering elem_ptr for decl's and constants. To generate the correct pointer, we perform a relocation by using the addend that represents the offset. The offset is calculated by taking the element's size and multiplying that by the index. For constants this generates a single immediate instruction, and for decl's this generates a single pointer address. --- src/arch/wasm/CodeGen.zig | 73 ++++++++++++++++++++++++++++++++++++--- src/arch/wasm/Emit.zig | 10 +++--- src/arch/wasm/Mir.zig | 7 ++++ src/link/Wasm.zig | 12 ++++++- 4 files changed, 92 insertions(+), 10 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 7d2046b90b..2d0cf57fd4 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -39,6 +39,14 @@ const WValue = union(enum) { /// Note: The value contains the symbol index, rather than the actual address /// as we use this to perform the relocation. memory: u32, + /// A value that represents a parent pointer and an offset + /// from that pointer. i.e. when slicing with constant values. + memory_offset: struct { + /// The symbol of the parent pointer + pointer: u32, + /// Offset will be set as 'addend' when relocating + offset: u32, + }, /// Represents a function pointer /// In wasm function pointers are indexes into a function table, /// rather than an address in the data section. @@ -754,7 +762,14 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void { .imm64 => |val| try self.addImm64(val), .float32 => |val| try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }), .float64 => |val| try self.addFloat64(val), - .memory => |ptr| try self.addLabel(.memory_address, ptr), // write sybol address and generate relocation + .memory => |ptr| { + const extra_index = try self.addExtra(Mir.Memory{ .pointer = ptr, .offset = 0 }); + try self.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } }); + }, + .memory_offset => |mem_off| { + const extra_index = try self.addExtra(Mir.Memory{ .pointer = mem_off.pointer, .offset = mem_off.offset }); + try self.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } }); + }, .function_index => |index| try self.addLabel(.function_index, index), // write function index and generate relocation } } @@ -927,7 +942,7 @@ pub const DeclGen = struct { .function => val.castTag(.function).?.data.owner_decl, else => unreachable, }; - return try self.lowerDeclRef(ty, val, fn_decl); + return try self.lowerDeclRefValue(ty, val, fn_decl, 0); }, .Optional => { var opt_buf: Type.Payload.ElemType = undefined; @@ -1115,11 +1130,11 @@ pub const DeclGen = struct { .Pointer => switch (val.tag()) { .variable => { const decl = val.castTag(.variable).?.data.owner_decl; - return self.lowerDeclRef(ty, val, decl); + return self.lowerDeclRefValue(ty, val, decl, 0); }, .decl_ref => { const decl = val.castTag(.decl_ref).?.data; - return self.lowerDeclRef(ty, val, decl); + return self.lowerDeclRefValue(ty, val, decl, writer, 0); }, .slice => { const slice = val.castTag(.slice).?.data; @@ -1139,6 +1154,13 @@ pub const DeclGen = struct { try writer.writeByteNTimes(0, @divExact(self.target().cpu.arch.ptrBitWidth(), 8)); return Result{ .appended = {} }; }, + .elem_ptr => { + const elem_ptr = val.castTag(.elem_ptr).?.data; + const elem_size = ty.childType().abiSize(self.target()); + const offset = elem_ptr.index * elem_size; + return self.lowerParentPtr(elem_ptr.array_ptr, writer, offset); + }, + .int_u64 => return self.genTypedValue(Type.usize, val, writer), else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}), }, .ErrorUnion => { @@ -1179,7 +1201,36 @@ pub const DeclGen = struct { } } - fn lowerDeclRef(self: *DeclGen, ty: Type, val: Value, decl: *Module.Decl) InnerError!Result { + fn lowerParentPtr(self: *DeclGen, ptr_value: Value, offset: usize) InnerError!Result { + switch (ptr_value.tag()) { + .decl_ref => { + const decl = ptr_value.castTag(.decl_ref).?.data; + return self.lowerParentPtrDecl(ptr_value, decl, offset); + }, + else => |tag| return self.fail("TODO: Implement lowerParentPtr for pointer value tag: {s}", .{tag}), + } + } + + fn lowerParentPtrDecl(self: *DeclGen, ptr_val: Value, decl: *Module.Decl, offset: usize) InnerError!Result { + decl.markAlive(); + var ptr_ty_payload: Type.Payload.ElemType = .{ + .base = .{ .tag = .single_mut_pointer }, + .data = decl.ty, + }; + const ptr_ty = Type.initPayload(&ptr_ty_payload.base); + return self.lowerDeclRefValue(ptr_ty, ptr_val, decl, offset); + } + + fn lowerDeclRefValue( + self: *DeclGen, + ty: Type, + val: Value, + /// The target decl that is being pointed to + decl: *Module.Decl, + /// When lowering to an indexed pointer, we can specify the offset + /// which will then be used as 'addend' to the relocation. + offset: usize, + ) InnerError!Result { const writer = self.code.writer(); if (ty.isSlice()) { var buf: Type.SlicePtrFieldTypeBuffer = undefined; @@ -1202,6 +1253,7 @@ pub const DeclGen = struct { self.symbol_index, // source symbol index decl.link.wasm.sym_index, // target symbol index @intCast(u32, self.code.items.len), // offset + @intCast(u32, offset), // addend )); return Result{ .appended = {} }; } @@ -1974,6 +2026,17 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { return WValue{ .function_index = target_sym_index }; } else return WValue{ .memory = target_sym_index }; }, + .elem_ptr => { + const elem_ptr = val.castTag(.elem_ptr).?.data; + const index = elem_ptr.index; + const offset = index * ty.childType().abiSize(self.target); + const array_ptr = try self.lowerConstant(elem_ptr.array_ptr, ty); + + return WValue{ .memory_offset = .{ + .pointer = array_ptr.memory, + .offset = @intCast(u32, offset), + } }; + }, .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) }, .zero, .null_value => return WValue{ .imm32 = 0 }, else => return self.fail("Wasm TODO: lowerConstant for other const pointer tag {s}", .{val.tag()}), diff --git a/src/arch/wasm/Emit.zig b/src/arch/wasm/Emit.zig index 9283a0e0b7..8cae78caf1 100644 --- a/src/arch/wasm/Emit.zig +++ b/src/arch/wasm/Emit.zig @@ -326,25 +326,27 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void { } fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void { - const symbol_index = emit.mir.instructions.items(.data)[inst].label; + const extra_index = emit.mir.instructions.items(.data)[inst].payload; + const mem = emit.mir.extraData(Mir.Memory, extra_index).data; const mem_offset = emit.offset() + 1; const is_wasm32 = emit.bin_file.options.target.cpu.arch == .wasm32; if (is_wasm32) { try emit.code.append(std.wasm.opcode(.i32_const)); var buf: [5]u8 = undefined; - leb128.writeUnsignedFixed(5, &buf, symbol_index); + leb128.writeUnsignedFixed(5, &buf, mem.pointer); try emit.code.appendSlice(&buf); } else { try emit.code.append(std.wasm.opcode(.i64_const)); var buf: [10]u8 = undefined; - leb128.writeUnsignedFixed(10, &buf, symbol_index); + leb128.writeUnsignedFixed(10, &buf, mem.pointer); try emit.code.appendSlice(&buf); } try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{ .offset = mem_offset, - .index = symbol_index, + .index = mem.pointer, .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64, + .addend = mem.offset, }); } diff --git a/src/arch/wasm/Mir.zig b/src/arch/wasm/Mir.zig index 07696f0dd3..ed0867e583 100644 --- a/src/arch/wasm/Mir.zig +++ b/src/arch/wasm/Mir.zig @@ -546,3 +546,10 @@ pub const MemArg = struct { offset: u32, alignment: u32, }; + +/// Represents a memory address, which holds both the pointer +/// or the parent pointer and the offset to it. +pub const Memory = struct { + pointer: u32, + offset: u32, +}; diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index a5d4630378..b047e4b68a 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -345,10 +345,19 @@ pub fn updateLocalSymbolCode(self: *Wasm, decl: *Module.Decl, symbol_index: u32, /// For a given decl, find the given symbol index's atom, and create a relocation for the type. /// Returns the given pointer address -pub fn getDeclVAddr(self: *Wasm, decl: *Module.Decl, ty: Type, symbol_index: u32, target_symbol_index: u32, offset: u32) !u32 { +pub fn getDeclVAddr( + self: *Wasm, + decl: *Module.Decl, + ty: Type, + symbol_index: u32, + target_symbol_index: u32, + offset: u32, + addend: u32, +) !u32 { const atom = decl.link.wasm.symbolAtom(symbol_index); const is_wasm32 = self.base.options.target.cpu.arch == .wasm32; if (ty.zigTypeTag() == .Fn) { + std.debug.assert(addend == 0); // addend not allowed for function relocations // We found a function pointer, so add it to our table, // as function pointers are not allowed to be stored inside the data section. // They are instead stored in a function table which are called by index. @@ -363,6 +372,7 @@ pub fn getDeclVAddr(self: *Wasm, decl: *Module.Decl, ty: Type, symbol_index: u32 .index = target_symbol_index, .offset = offset, .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64, + .addend = addend, }); } // we do not know the final address at this point, From ae1e3c8f9bc86eeefb5a83233884a134f7b974f4 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Mon, 31 Jan 2022 21:12:30 +0100 Subject: [PATCH 0020/2031] wasm: Implement vector_init for array & structs Implements the instruction `vector_init` for structs and arrays. For arrays, it checks if the element must be passed by reference or not. When not, it can simply use the `offset` field of a store instruction to copy the values into the array. When it is byref, it will move the pointer by the element size, and then perform a store operation. This ensures types like structs will be moved into the right position. For structs we will always move the pointer, as we currently cannot verify if all fields are not by ref. --- src/arch/wasm/CodeGen.zig | 116 +++++++++++++++++++++++++++++--------- 1 file changed, 89 insertions(+), 27 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 2d0cf57fd4..420fbdf4ab 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -44,7 +44,7 @@ const WValue = union(enum) { memory_offset: struct { /// The symbol of the parent pointer pointer: u32, - /// Offset will be set as 'addend' when relocating + /// Offset will be set as addend when relocating offset: u32, }, /// Represents a function pointer @@ -606,7 +606,10 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue { // means we must generate it from a constant. const val = self.air.value(ref).?; const ty = self.air.typeOf(ref); - if (!ty.hasRuntimeBits() and !ty.isInt()) return WValue{ .none = {} }; + if (!ty.hasRuntimeBits() and !ty.isInt()) { + gop.value_ptr.* = WValue{ .none = {} }; + return gop.value_ptr.*; + } // When we need to pass the value by reference (such as a struct), we will // leverage `genTypedValue` to lower the constant to bytes and emit it @@ -1644,6 +1647,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); + // result must be stored in the stack and we return a pointer // to the stack instead if (self.return_value != .none) { @@ -1653,7 +1657,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } try self.restoreStackPointer(); try self.addTag(.@"return"); - return .none; + return WValue{ .none = {} }; } fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -1793,11 +1797,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro const err_ty = ty.errorUnionSet(); const pl_ty = ty.errorUnionPayload(); if (!pl_ty.hasRuntimeBits()) { - const err_val = try self.load(rhs, err_ty, 0); - return self.store(lhs, err_val, err_ty, 0); + return self.store(lhs, rhs, err_ty, 0); } - return try self.memCopy(ty, lhs, rhs); + return self.memCopy(ty, lhs, rhs); }, .Optional => { if (ty.isPtrLikeOptional()) { @@ -1812,7 +1815,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro return self.memCopy(ty, lhs, rhs); }, .Struct, .Array, .Union => { - return try self.memCopy(ty, lhs, rhs); + return self.memCopy(ty, lhs, rhs); }, .Pointer => { if (ty.isSlice()) { @@ -1827,7 +1830,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro } }, .Int => if (ty.intInfo(self.target).bits > 64) { - return try self.memCopy(ty, lhs, rhs); + return self.memCopy(ty, lhs, rhs); }, else => {}, } @@ -2587,11 +2590,11 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue if (isByRef(payload_ty, self.target)) { return self.buildPointerOffset(operand, offset, .new); } - return try self.load(operand, payload_ty, offset); + return self.load(operand, payload_ty, offset); } fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue { - if (self.liveness.isUnused(inst)) return WValue.none; + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); @@ -2601,11 +2604,12 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return operand; } - return try self.load(operand, err_ty.errorUnionSet(), 0); + return self.load(operand, err_ty.errorUnionSet(), 0); } fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { - if (self.liveness.isUnused(inst)) return WValue.none; + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); @@ -2627,11 +2631,14 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { - if (self.liveness.isUnused(inst)) return WValue.none; + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); const err_ty = self.air.getRefType(ty_op.ty); + if (!err_ty.errorUnionPayload().hasRuntimeBits()) return operand; + const err_union = try self.allocStack(err_ty); // TODO: Also write 'undefined' to the payload try self.store(err_union, operand, err_ty.errorUnionSet(), 0); @@ -2813,16 +2820,16 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue { - if (self.liveness.isUnused(inst)) return WValue.none; + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); - return try self.load(operand, Type.usize, self.ptrSize()); + return self.load(operand, Type.usize, self.ptrSize()); } fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { - if (self.liveness.isUnused(inst)) return WValue.none; + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; const bin_op = self.air.instructions.items(.data)[inst].bin_op; const slice_ty = self.air.typeOf(bin_op.lhs); @@ -2847,7 +2854,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (isByRef(elem_ty, self.target)) { return result; } - return try self.load(result, elem_ty, 0); + return self.load(result, elem_ty, 0); } fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -2875,10 +2882,10 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { - if (self.liveness.isUnused(inst)) return WValue.none; + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); - return try self.load(operand, Type.usize, 0); + return self.load(operand, Type.usize, 0); } fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -2943,7 +2950,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const un_op = self.air.instructions.items(.data)[inst].un_op; - return try self.resolveInst(un_op); + return self.resolveInst(un_op); } fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -2975,7 +2982,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue { fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; const un_op = self.air.instructions.items(.data)[inst].un_op; - return try self.resolveInst(un_op); + return self.resolveInst(un_op); } fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -2990,7 +2997,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { // load pointer onto the stack if (ptr_ty.isSlice()) { - const ptr_local = try self.load(pointer, ptr_ty, 0); + const ptr_local = try self.load(pointer, Type.usize, 0); try self.addLabel(.local_get, ptr_local.local); } else { try self.emitWValue(pointer); @@ -3007,7 +3014,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (isByRef(elem_ty, self.target)) { return result; } - return try self.load(result, elem_ty, 0); + return self.load(result, elem_ty, 0); } fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -3023,7 +3030,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { // load pointer onto the stack if (ptr_ty.isSlice()) { - const ptr_local = try self.load(ptr, ptr_ty, 0); + const ptr_local = try self.load(ptr, Type.usize, 0); try self.addLabel(.local_get, ptr_local.local); } else { try self.emitWValue(ptr); @@ -3157,7 +3164,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (isByRef(elem_ty, self.target)) { return result; } - return try self.load(result, elem_ty, 0); + return self.load(result, elem_ty, 0); } fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -3201,8 +3208,63 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); - _ = elements; - return self.fail("TODO: Wasm backend: implement airVectorInit", .{}); + switch (vector_ty.zigTypeTag()) { + .Vector => return self.fail("TODO: Wasm backend: implement airVectorInit for vectors", .{}), + .Array => { + const result = try self.allocStack(vector_ty); + const elem_ty = vector_ty.childType(); + const elem_size = @intCast(u32, elem_ty.abiSize(self.target)); + + // When the element type is by reference, we must copy the entire + // value. It is therefore safer to move the offset pointer and store + // each value individually, instead of using store offsets. + if (isByRef(elem_ty, self.target)) { + // copy stack pointer into a temporary local, which is + // moved for each element to store each value in the right position. + const offset = try self.allocLocal(Type.usize); + try self.emitWValue(result); + try self.addLabel(.local_set, offset.local); + for (elements) |elem, elem_index| { + const elem_val = try self.resolveInst(elem); + try self.store(offset, elem_val, elem_ty, 0); + + if (elem_index < elements.len - 1) { + _ = try self.buildPointerOffset(offset, elem_size, .modify); + } + } + } else { + var offset: u32 = 0; + for (elements) |elem| { + const elem_val = try self.resolveInst(elem); + try self.store(result, elem_val, elem_ty, offset); + offset += elem_size; + } + } + return result; + }, + .Struct => { + const tuple = vector_ty.castTag(.tuple).?.data; + const result = try self.allocStack(vector_ty); + const offset = try self.allocLocal(Type.usize); // pointer to offset + try self.emitWValue(result); + try self.addLabel(.local_set, offset.local); + for (elements) |elem, elem_index| { + if (tuple.values[elem_index].tag() != .unreachable_value) continue; + + const elem_ty = tuple.types[elem_index]; + const elem_size = @intCast(u32, elem_ty.abiSize(self.target)); + const value = try self.resolveInst(elem); + try self.store(offset, value, elem_ty, 0); + + if (elem_index < elements.len - 1) { + _ = try self.buildPointerOffset(offset, elem_size, .modify); + } + } + + return result; + }, + else => unreachable, + } } fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue { From e35414bf5c356798f201be85303101f59220326c Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 3 Feb 2022 21:31:35 +0100 Subject: [PATCH 0021/2031] wasm: Refactor stack to account for alignment We now calculate the total stack size required for the current frame. The default alignment of the stack is 16 bytes, and will be overwritten when the alignment of a given type is larger than that. After we have generated all instructions for the body, we calculate the total stack size by forward aligning the stack size while accounting for the max alignment. We then insert a prologue into the body, where we substract this size from the stack pointer and save it inside a bottom stackframe local. We use this local then, to calculate the stack pointer locals of all variables we allocate into the stack. In a future iteration we can improve this further by storing the offsets as a new `stack_offset` `WValue`. This has the benefit of not having to spend runtime cost of storing those offsets, but instead we append those offsets whenever we need the value that lives in the stack. --- src/arch/wasm/CodeGen.zig | 168 +++++++++++++++++++++++++------------- 1 file changed, 110 insertions(+), 58 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 420fbdf4ab..d2db5fd92b 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -560,6 +560,9 @@ mir_extra: std.ArrayListUnmanaged(u32) = .{}, /// When a function is executing, we store the the current stack pointer's value within this local. /// This value is then used to restore the stack pointer to the original value at the return of the function. initial_stack_value: WValue = .none, +/// The current stack pointer substracted with the stack size. From this value, we will calculate +/// all offsets of the stack values. +bottom_stack_value: WValue = .none, /// Arguments of this function declaration /// This will be set after `resolveCallingConventionValues` args: []WValue = &.{}, @@ -567,6 +570,14 @@ args: []WValue = &.{}, /// When it returns a pointer to the stack, the `.local` tag will be active and must be populated /// before this function returns its execution to the caller. return_value: WValue = .none, +/// The size of the stack this function occupies. In the function prologue +/// we will move the stack pointer by this number, forward aligned with the `stack_alignment`. +stack_size: u32 = 0, +/// The stack alignment, which is 16 bytes by default. This is specified by the +/// tool-conventions: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md +/// and also what the llvm backend will emit. +/// However, local variables or the usage of `@setAlignStack` can overwrite this default. +stack_alignment: u32 = 16, const InnerError = error{ OutOfMemory, @@ -654,13 +665,6 @@ fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!void { try self.mir_instructions.append(self.gpa, inst); } -/// Inserts a Mir instruction at the given `offset`. -/// Asserts offset is within bound. -fn addInstAt(self: *Self, offset: usize, inst: Mir.Inst) error{OutOfMemory}!void { - try self.mir_instructions.ensureUnusedCapacity(self.gpa, 1); - self.mir_instructions.insertAssumeCapacity(offset, inst); -} - fn addTag(self: *Self, tag: Mir.Inst.Tag) error{OutOfMemory}!void { try self.addInst(.{ .tag = tag, .data = .{ .tag = {} } }); } @@ -845,10 +849,43 @@ pub fn genFunc(self: *Self) InnerError!void { try self.addTag(.@"unreachable"); } } - // End of function body try self.addTag(.end); + // check if we have to initialize and allocate anything into the stack frame. + // If so, create enough stack space and insert the instructions at the front of the list. + if (self.stack_size > 0) { + var prologue = std.ArrayList(Mir.Inst).init(self.gpa); + defer prologue.deinit(); + + // load stack pointer + try prologue.append(.{ .tag = .global_get, .data = .{ .label = 0 } }); + // store stack pointer so we can restore it when we return from the function + try prologue.append(.{ .tag = .local_tee, .data = .{ .label = self.initial_stack_value.local } }); + // get the total stack size + const aligned_stack = std.mem.alignForwardGeneric(u32, self.stack_size, self.stack_alignment); + try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @intCast(i32, aligned_stack) } }); + // substract it from the current stack pointer + try prologue.append(.{ .tag = .i32_sub, .data = .{ .tag = {} } }); + // Get negative stack aligment + try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @intCast(i32, self.stack_alignment) * -1 } }); + // Bit and the value to get the new stack pointer to ensure the pointers are aligned with the abi alignment + try prologue.append(.{ .tag = .i32_and, .data = .{ .tag = {} } }); + // store the current stack pointer as the bottom, which will be used to calculate all stack pointer offsets + try prologue.append(.{ .tag = .local_tee, .data = .{ .label = self.bottom_stack_value.local } }); + // Store the current stack pointer value into the global stack pointer so other function calls will + // start from this value instead and not overwrite the current stack. + try prologue.append(.{ .tag = .global_set, .data = .{ .label = 0 } }); + + // reserve space and insert all prologue instructions at the front of the instruction list + // We insert them in reserve order as there is no insertSlice in multiArrayList. + try self.mir_instructions.ensureUnusedCapacity(self.gpa, prologue.items.len); + for (prologue.items) |_, index| { + const inst = prologue.items[prologue.items.len - 1 - index]; + self.mir_instructions.insertAssumeCapacity(0, inst); + } + } + var mir: Mir = .{ .instructions = self.mir_instructions.toOwnedSlice(), .extra = self.mir_extra.toOwnedSlice(self.gpa), @@ -1137,7 +1174,7 @@ pub const DeclGen = struct { }, .decl_ref => { const decl = val.castTag(.decl_ref).?.data; - return self.lowerDeclRefValue(ty, val, decl, writer, 0); + return self.lowerDeclRefValue(ty, val, decl, 0); }, .slice => { const slice = val.castTag(.slice).?.data; @@ -1161,9 +1198,9 @@ pub const DeclGen = struct { const elem_ptr = val.castTag(.elem_ptr).?.data; const elem_size = ty.childType().abiSize(self.target()); const offset = elem_ptr.index * elem_size; - return self.lowerParentPtr(elem_ptr.array_ptr, writer, offset); + return self.lowerParentPtr(elem_ptr.array_ptr, offset); }, - .int_u64 => return self.genTypedValue(Type.usize, val, writer), + .int_u64 => return self.genTypedValue(Type.usize, val), else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}), }, .ErrorUnion => { @@ -1309,22 +1346,16 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu return result; } -/// Retrieves the stack pointer's value from the global variable and stores -/// it in a local +/// Creates a local for the initial stack value /// Asserts `initial_stack_value` is `.none` fn initializeStack(self: *Self) !void { assert(self.initial_stack_value == .none); - // reserve space for immediate value - // get stack pointer global - try self.addLabel(.global_get, 0); - // Reserve a local to store the current stack pointer // We can later use this local to set the stack pointer back to the value // we have stored here. - self.initial_stack_value = try self.allocLocal(Type.initTag(.i32)); - - // save the value to the local - try self.addLabel(.local_set, self.initial_stack_value.local); + self.initial_stack_value = try self.allocLocal(Type.usize); + // Also reserve a local to store the bottom stack value + self.bottom_stack_value = try self.allocLocal(Type.usize); } /// Reads the stack pointer from `Context.initial_stack_value` and writes it @@ -1339,36 +1370,75 @@ fn restoreStackPointer(self: *Self) !void { try self.addLabel(.global_set, 0); } -/// Moves the stack pointer by given `offset` -/// It does this by retrieving the stack pointer, subtracting `offset` and storing -/// the result back into the stack pointer. -fn moveStack(self: *Self, offset: u32, local: u32) !void { - if (offset == 0) return; - try self.addLabel(.global_get, 0); - try self.addImm32(@bitCast(i32, offset)); - try self.addTag(.i32_sub); - try self.addLabel(.local_tee, local); - try self.addLabel(.global_set, 0); +/// Saves the current stack size's stack pointer position into a given local +/// It does this by retrieving the bottom stack pointer, adding `self.stack_size` and storing +/// the result back into the local. +fn saveStack(self: *Self) !WValue { + const local = try self.allocLocal(Type.usize); + try self.addLabel(.local_get, self.bottom_stack_value.local); + try self.addImm32(@intCast(i32, self.stack_size)); + try self.addTag(.i32_add); + try self.addLabel(.local_set, local.local); + return local; } /// From a given type, will create space on the virtual stack to store the value of such type. /// This returns a `WValue` with its active tag set to `local`, containing the index to the local /// that points to the position on the virtual stack. This function should be used instead of -/// moveStack unless a local was already created to store the point. +/// moveStack unless a local was already created to store the pointer. /// /// Asserts Type has codegenbits fn allocStack(self: *Self, ty: Type) !WValue { assert(ty.hasRuntimeBits()); + if (self.initial_stack_value == .none) { + try self.initializeStack(); + } - // calculate needed stack space const abi_size = std.math.cast(u32, ty.abiSize(self.target)) catch { - return self.fail("Given type '{}' too big to fit into stack frame", .{ty}); + return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ ty, ty.abiSize(self.target) }); }; + const abi_align = ty.abiAlignment(self.target); - // allocate a local using wasm's pointer size - const local = try self.allocLocal(Type.@"usize"); - try self.moveStack(abi_size, local.local); - return local; + if (abi_align > self.stack_alignment) { + self.stack_alignment = abi_align; + } + + const offset = std.mem.alignForwardGeneric(u32, self.stack_size, abi_align); + defer self.stack_size = offset + abi_size; + + // store the stack pointer and return a local to it + return self.saveStack(); +} + +/// From a given AIR instruction generates a pointer to the stack where +/// the value of its type will live. +/// This is different from allocStack where this will use the pointer's alignment +/// if it is set, to ensure the stack alignment will be set correctly. +fn allocStackPtr(self: *Self, inst: Air.Inst.Index) !WValue { + const ptr_ty = self.air.typeOfIndex(inst); + const pointee_ty = ptr_ty.childType(); + + if (self.initial_stack_value == .none) { + try self.initializeStack(); + } + + if (!pointee_ty.hasRuntimeBits()) { + return self.allocStack(Type.usize); // create a value containing just the stack pointer. + } + + const abi_alignment = ptr_ty.ptrAlignment(self.target); + const abi_size = std.math.cast(u32, pointee_ty.abiSize(self.target)) catch { + return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ pointee_ty, pointee_ty.abiSize(self.target) }); + }; + if (abi_alignment > self.stack_alignment) { + self.stack_alignment = abi_alignment; + } + + const offset = std.mem.alignForwardGeneric(u32, self.stack_size, abi_alignment); + defer self.stack_size = offset + abi_size; + + // store the stack pointer and return a local to it + return self.saveStack(); } /// From given zig bitsize, returns the wasm bitsize @@ -1667,12 +1737,7 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (isByRef(child_type, self.target)) { return self.return_value; } - - // Initialize the stack - if (self.initial_stack_value == .none) { - try self.initializeStack(); - } - return self.allocStack(child_type); + return self.allocStackPtr(inst); } fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -1764,20 +1829,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { - const pointee_type = self.air.typeOfIndex(inst).childType(); - - // Initialize the stack - if (self.initial_stack_value == .none) { - try self.initializeStack(); - } - - if (!pointee_type.hasRuntimeBits()) { - // when the pointee is zero-sized, we still want to create a pointer. - // but instead use a default pointer type as storage. - const zero_ptr = try self.allocStack(Type.usize); - return zero_ptr; - } - return self.allocStack(pointee_type); + return self.allocStackPtr(inst); } fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { From 588b88b98753f02061e562a9c15c2396bcd95dee Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 3 Feb 2022 22:25:46 +0100 Subject: [PATCH 0022/2031] Move passing behavior tests Singular tests (such as in the bug ones) are moved to top level with exclusions for non-passing backends. The big behavior tests such as array_llvm and slice are moved to the inner scope with the C backend disabled. They all pass for the wasm backend now --- src/arch/wasm/CodeGen.zig | 2 +- test/behavior.zig | 12 ++++++------ test/behavior/array_llvm.zig | 18 ++++++++++++++++++ test/behavior/bugs/1025.zig | 4 ++++ test/behavior/bugs/1741.zig | 3 +++ test/behavior/bugs/1914.zig | 7 +++++++ test/behavior/slice.zig | 13 +++++++++++++ 7 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index d2db5fd92b..67aa9a6c88 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1198,7 +1198,7 @@ pub const DeclGen = struct { const elem_ptr = val.castTag(.elem_ptr).?.data; const elem_size = ty.childType().abiSize(self.target()); const offset = elem_ptr.index * elem_size; - return self.lowerParentPtr(elem_ptr.array_ptr, offset); + return self.lowerParentPtr(elem_ptr.array_ptr, @intCast(usize, offset)); }, .int_u64 => return self.genTypedValue(Type.usize, val), else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}), diff --git a/test/behavior.zig b/test/behavior.zig index a0db9b9f57..0f74ed7d59 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -10,6 +10,7 @@ test { _ = @import("behavior/bugs/655.zig"); _ = @import("behavior/bugs/656.zig"); _ = @import("behavior/bugs/679.zig"); + _ = @import("behavior/bugs/1025.zig"); _ = @import("behavior/bugs/1111.zig"); _ = @import("behavior/bugs/1277.zig"); _ = @import("behavior/bugs/1310.zig"); @@ -17,6 +18,8 @@ test { _ = @import("behavior/bugs/1486.zig"); _ = @import("behavior/bugs/1500.zig"); _ = @import("behavior/bugs/1735.zig"); + _ = @import("behavior/bugs/1741.zig"); + _ = @import("behavior/bugs/1914.zig"); _ = @import("behavior/bugs/2006.zig"); _ = @import("behavior/bugs/2346.zig"); _ = @import("behavior/bugs/3112.zig"); @@ -38,7 +41,8 @@ test { _ = @import("behavior/struct.zig"); if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_x86_64) { - // Tests that pass for stage1, llvm backend, C backend, wasm backend. + // Tests that pass (partly) for stage1, llvm backend, C backend, wasm backend. + _ = @import("behavior/array_llvm.zig"); _ = @import("behavior/basic.zig"); _ = @import("behavior/bitcast.zig"); _ = @import("behavior/bugs/624.zig"); @@ -69,6 +73,7 @@ test { _ = @import("behavior/pointers.zig"); _ = @import("behavior/ptrcast.zig"); _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); + _ = @import("behavior/slice.zig"); _ = @import("behavior/src.zig"); _ = @import("behavior/this.zig"); _ = @import("behavior/try.zig"); @@ -88,11 +93,7 @@ test { if (builtin.zig_backend != .stage2_c) { // Tests that pass for stage1 and the llvm backend. - _ = @import("behavior/array_llvm.zig"); _ = @import("behavior/atomics.zig"); - _ = @import("behavior/bugs/1025.zig"); - _ = @import("behavior/bugs/1741.zig"); - _ = @import("behavior/bugs/1914.zig"); _ = @import("behavior/bugs/2578.zig"); _ = @import("behavior/bugs/3007.zig"); _ = @import("behavior/bugs/9584.zig"); @@ -108,7 +109,6 @@ test { _ = @import("behavior/popcount.zig"); _ = @import("behavior/saturating_arithmetic.zig"); _ = @import("behavior/sizeof_and_typeof.zig"); - _ = @import("behavior/slice.zig"); _ = @import("behavior/struct_llvm.zig"); _ = @import("behavior/switch.zig"); _ = @import("behavior/widening.zig"); diff --git a/test/behavior/array_llvm.zig b/test/behavior/array_llvm.zig index 5be5974fff..c3df5ba837 100644 --- a/test/behavior/array_llvm.zig +++ b/test/behavior/array_llvm.zig @@ -7,6 +7,7 @@ var s_array: [8]Sub = undefined; const Sub = struct { b: u8 }; const Str = struct { a: []Sub }; test "set global var array via slice embedded in struct" { + if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO var s = Str{ .a = s_array[0..] }; s.a[0].b = 1; @@ -19,6 +20,7 @@ test "set global var array via slice embedded in struct" { } test "read/write through global variable array of struct fields initialized via array mult" { + if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { try expect(storage[0].term == 1); @@ -36,6 +38,7 @@ test "read/write through global variable array of struct fields initialized via } test "implicit cast single-item pointer" { + if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO try testImplicitCastSingleItemPtr(); comptime try testImplicitCastSingleItemPtr(); } @@ -52,6 +55,7 @@ fn testArrayByValAtComptime(b: [2]u8) u8 { } test "comptime evaluating function that takes array by value" { + if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO const arr = [_]u8{ 1, 2 }; const x = comptime testArrayByValAtComptime(arr); const y = comptime testArrayByValAtComptime(arr); @@ -60,12 +64,14 @@ test "comptime evaluating function that takes array by value" { } test "runtime initialize array elem and then implicit cast to slice" { + if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO var two: i32 = 2; const x: []const i32 = &[_]i32{two}; try expect(x[0] == 2); } test "array literal as argument to function" { + if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn entry(two: i32) !void { try foo(&[_]i32{ 1, 2, 3 }); @@ -90,6 +96,7 @@ test "array literal as argument to function" { } test "double nested array to const slice cast in array literal" { + if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn entry(two: i32) !void { const cases = [_][]const []const i32{ @@ -147,6 +154,7 @@ test "double nested array to const slice cast in array literal" { } test "anonymous literal in array" { + if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { const Foo = struct { a: usize = 2, @@ -168,6 +176,7 @@ test "anonymous literal in array" { } test "access the null element of a null terminated array" { + if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { var array: [4:0]u8 = .{ 'a', 'o', 'e', 'u' }; @@ -181,6 +190,7 @@ test "access the null element of a null terminated array" { } test "type deduction for array subscript expression" { + if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { var array = [_]u8{ 0x55, 0xAA }; @@ -196,6 +206,8 @@ test "type deduction for array subscript expression" { test "sentinel element count towards the ABI size calculation" { if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO + if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -218,6 +230,8 @@ test "sentinel element count towards the ABI size calculation" { test "zero-sized array with recursive type definition" { if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO + if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO const U = struct { fn foo(comptime T: type, comptime n: usize) type { @@ -237,6 +251,7 @@ test "zero-sized array with recursive type definition" { } test "type coercion of anon struct literal to array" { + if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { const U = union { a: u32, @@ -253,6 +268,7 @@ test "type coercion of anon struct literal to array" { try expect(arr1[2] == 54); if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO + if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO var x2: U = .{ .a = 42 }; const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } }; @@ -268,6 +284,8 @@ test "type coercion of anon struct literal to array" { test "type coercion of pointer to anon struct literal to pointer to array" { if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO + if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { const U = union { diff --git a/test/behavior/bugs/1025.zig b/test/behavior/bugs/1025.zig index 69ee77eea1..fa72e522de 100644 --- a/test/behavior/bugs/1025.zig +++ b/test/behavior/bugs/1025.zig @@ -1,3 +1,5 @@ +const builtin = @import("builtin"); + const A = struct { B: type, }; @@ -7,6 +9,8 @@ fn getA() A { } test "bug 1025" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const a = getA(); try @import("std").testing.expect(a.B == u8); } diff --git a/test/behavior/bugs/1741.zig b/test/behavior/bugs/1741.zig index 8873de9b49..280aafc52e 100644 --- a/test/behavior/bugs/1741.zig +++ b/test/behavior/bugs/1741.zig @@ -1,6 +1,9 @@ const std = @import("std"); +const builtin = @import("builtin"); test "fixed" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const x: f32 align(128) = 12.34; try std.testing.expect(@ptrToInt(&x) % 128 == 0); } diff --git a/test/behavior/bugs/1914.zig b/test/behavior/bugs/1914.zig index 2c9e836e6a..6462937351 100644 --- a/test/behavior/bugs/1914.zig +++ b/test/behavior/bugs/1914.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); const A = struct { b_list_pointer: *const []B, @@ -11,6 +12,9 @@ const b_list: []B = &[_]B{}; const a = A{ .b_list_pointer = &b_list }; test "segfault bug" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const assert = std.debug.assert; const obj = B{ .a_pointer = &a }; assert(obj.a_pointer == &a); // this makes zig crash @@ -27,5 +31,8 @@ pub const B2 = struct { var b_value = B2{ .pointer_array = &[_]*A2{} }; test "basic stuff" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO std.debug.assert(&b_value == &b_value); } diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 01ae10ee4e..0b01139800 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -27,6 +27,7 @@ comptime { } test "slicing" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var array: [20]i32 = undefined; array[5] = 1234; @@ -43,6 +44,7 @@ test "slicing" { } test "const slice" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO comptime { const a = "1234567890"; try expect(a.len == 10); @@ -53,6 +55,7 @@ test "const slice" { } test "comptime slice of undefined pointer of length 0" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const slice1 = @as([*]i32, undefined)[0..0]; try expect(slice1.len == 0); const slice2 = @as([*]i32, undefined)[100..100]; @@ -60,6 +63,7 @@ test "comptime slice of undefined pointer of length 0" { } test "implicitly cast array of size 0 to slice" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var msg = [_]u8{}; try assertLenIsZero(&msg); } @@ -69,6 +73,7 @@ fn assertLenIsZero(msg: []const u8) !void { } test "access len index of sentinel-terminated slice" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { var slice: [:0]const u8 = "hello"; @@ -82,6 +87,7 @@ test "access len index of sentinel-terminated slice" { } test "comptime slice of slice preserves comptime var" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO comptime { var buff: [10]u8 = undefined; buff[0..][0..][0] = 1; @@ -90,6 +96,7 @@ test "comptime slice of slice preserves comptime var" { } test "slice of type" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO comptime { var types_array = [_]type{ i32, f64, type }; for (types_array) |T, i| { @@ -112,6 +119,7 @@ test "slice of type" { } test "generic malloc free" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const a = memAlloc(u8, 10) catch unreachable; memFree(u8, a); } @@ -124,6 +132,7 @@ fn memFree(comptime T: type, memory: []T) void { } test "slice of hardcoded address to pointer" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { const pointer = @intToPtr([*]u8, 0x04)[0..2]; @@ -138,6 +147,7 @@ test "slice of hardcoded address to pointer" { } test "comptime slice of pointer preserves comptime var" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO comptime { var buff: [10]u8 = undefined; var a = @ptrCast([*]u8, &buff); @@ -147,6 +157,7 @@ test "comptime slice of pointer preserves comptime var" { } test "comptime pointer cast array and then slice" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; const ptrA: [*]const u8 = @ptrCast([*]const u8, &array); @@ -160,6 +171,7 @@ test "comptime pointer cast array and then slice" { } test "slicing zero length array" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const s1 = ""[0..]; const s2 = ([_]u32{})[0..]; try expect(s1.len == 0); @@ -171,6 +183,7 @@ test "slicing zero length array" { const x = @intToPtr([*]i32, 0x1000)[0..0x500]; const y = x[0x100..]; test "compile time slice of pointer to hard coded address" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage1) return error.SkipZigTest; try expect(@ptrToInt(x) == 0x1000); From 4ca9a8d192f4c800f10cdb3bd39c94922b6fb9b8 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 3 Feb 2022 19:19:48 +0100 Subject: [PATCH 0023/2031] x64: implement storing to MCValue.memory for PIE targets --- src/arch/x86_64/CodeGen.zig | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index e05a66228b..b3a292a7f4 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1811,17 +1811,29 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type } }, .memory => |addr| { - if (self.bin_file.options.pie) { - return self.fail("TODO implement storing to memory when targeting PIE", .{}); - } - - // TODO: in case the address fits in an imm32 we can use [ds:imm32] - // instead of wasting an instruction copying the address to a register - value.freezeIfRegister(&self.register_manager); defer value.unfreezeIfRegister(&self.register_manager); - const addr_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = addr }); + const addr_reg: Register = blk: { + if (self.bin_file.options.pie) { + const addr_reg = try self.register_manager.allocReg(null); + _ = try self.addInst(.{ + .tag = .lea, + .ops = (Mir.Ops{ + .reg1 = addr_reg.to64(), + .flags = 0b10, + }).encode(), + .data = .{ .got_entry = @truncate(u32, addr) }, + }); + break :blk addr_reg; + } else { + // TODO: in case the address fits in an imm32 we can use [ds:imm32] + // instead of wasting an instruction copying the address to a register + const addr_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = addr }); + break :blk addr_reg; + } + }; + // to get the actual address of the value we want to modify we have to go through the GOT // mov reg, [reg] _ = try self.addInst(.{ From 0893326e0ea9b261c5d334067c294c7d8972d5a1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 3 Feb 2022 21:05:10 -0700 Subject: [PATCH 0024/2031] Sema: slice improvements * resolve_inferred_alloc now gives a proper mutability attribute to the corresponding alloc instruction. Previously, it would fail to mark things const. * slicing: fix the detection for when the end index equals the length of the underlying object. Previously it was using `end - start` but it should just use the end index directly. It also takes into account when slicing a comptime-known slice. * `Type.sentinel`: fix not handling all slice tags --- src/Sema.zig | 98 ++++++++++++++++++++++++---------- src/type.zig | 3 ++ test/behavior/slice.zig | 12 +++++ test/behavior/slice_stage1.zig | 12 ----- 4 files changed, 85 insertions(+), 40 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 94b5a7f1d1..a3dccf1d7d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2503,6 +2503,13 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com try sema.requireRuntimeBlock(block, src); try sema.resolveTypeLayout(block, ty_src, final_elem_ty); + const final_ptr_ty = try Type.ptr(sema.arena, .{ + .pointee_type = final_elem_ty, + .mutable = var_is_mut, + .@"align" = inferred_alloc.data.alignment, + .@"addrspace" = target_util.defaultAddressSpace(target, .local), + }); + if (var_is_mut) { try sema.validateVarType(block, ty_src, final_elem_ty, false); } else ct: { @@ -2534,8 +2541,6 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com if (store_op.lhs != Air.indexToRef(bitcast_inst)) break :ct; if (air_datas[bitcast_inst].ty_op.operand != Air.indexToRef(const_inst)) break :ct; - const bitcast_ty_ref = air_datas[bitcast_inst].ty_op.ty; - const new_decl = d: { var anon_decl = try block.startAnonDecl(src); defer anon_decl.deinit(); @@ -2551,17 +2556,15 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com // block so that codegen does not see it. block.instructions.shrinkRetainingCapacity(block.instructions.items.len - 3); sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, new_decl); - air_datas[ptr_inst].ty_pl.ty = bitcast_ty_ref; + // Would be nice if we could just assign `bitcast_ty_ref` to + // `air_datas[ptr_inst].ty_pl.ty`, wouldn't it? Alas, that is almost correct, + // except that the pointer is mutable and we need to make it constant here. + air_datas[ptr_inst].ty_pl.ty = try sema.addType(final_ptr_ty); return; } // Change it to a normal alloc. - const final_ptr_ty = try Type.ptr(sema.arena, .{ - .pointee_type = final_elem_ty, - .@"align" = inferred_alloc.data.alignment, - .@"addrspace" = target_util.defaultAddressSpace(target, .local), - }); sema.air_instructions.set(ptr_inst, .{ .tag = .alloc, .data = .{ .ty = final_ptr_ty }, @@ -15609,12 +15612,16 @@ fn analyzeSlice( var slice_ty = ptr_ptr_ty; var ptr_or_slice = ptr_ptr; var elem_ty = ptr_ptr_child_ty.childType(); + var ptr_sentinel: ?Value = null; switch (ptr_ptr_child_ty.zigTypeTag()) { - .Array => {}, + .Array => { + ptr_sentinel = ptr_ptr_child_ty.sentinel(); + }, .Pointer => switch (ptr_ptr_child_ty.ptrSize()) { .One => { const double_child_ty = ptr_ptr_child_ty.childType(); if (double_child_ty.zigTypeTag() == .Array) { + ptr_sentinel = double_child_ty.sentinel(); ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); slice_ty = ptr_ptr_child_ty; array_ty = double_child_ty; @@ -15624,12 +15631,14 @@ fn analyzeSlice( } }, .Many, .C => { + ptr_sentinel = ptr_ptr_child_ty.sentinel(); ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); slice_ty = ptr_ptr_child_ty; array_ty = ptr_ptr_child_ty; elem_ty = ptr_ptr_child_ty.childType(); }, .Slice => { + ptr_sentinel = ptr_ptr_child_ty.sentinel(); ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); slice_ty = ptr_ptr_child_ty; array_ty = ptr_ptr_child_ty; @@ -15647,29 +15656,67 @@ fn analyzeSlice( const start = try sema.coerce(block, Type.usize, uncasted_start, start_src); const new_ptr = try analyzePtrArithmetic(sema, block, src, ptr, start, .ptr_add, ptr_src, start_src); + // true if and only if the end index of the slice, implicitly or explicitly, equals + // the length of the underlying object being sliced. we might learn the length of the + // underlying object because it is an array (which has the length in the type), or + // we might learn of the length because it is a comptime-known slice value. + var end_is_len = uncasted_end_opt == .none; const end = e: { - if (uncasted_end_opt != .none) { - break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); - } - if (array_ty.zigTypeTag() == .Array) { - break :e try sema.addConstant( - Type.usize, - try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen()), - ); + const len_val = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen()); + + if (!end_is_len) { + const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); + if (try sema.resolveMaybeUndefVal(block, end_src, end)) |end_val| { + if (end_val.eql(len_val, Type.usize)) { + end_is_len = true; + } + } + break :e end; + } + + break :e try sema.addConstant(Type.usize, len_val); } else if (slice_ty.isSlice()) { + if (!end_is_len) { + const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); + if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { + if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| { + var int_payload: Value.Payload.U64 = .{ + .base = .{ .tag = .int_u64 }, + .data = slice_val.sliceLen(), + }; + const slice_len_val = Value.initPayload(&int_payload.base); + if (end_val.eql(slice_len_val, Type.usize)) { + end_is_len = true; + } + } + } + break :e end; + } break :e try sema.analyzeSliceLen(block, src, ptr_or_slice); } + if (!end_is_len) { + break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); + } return sema.fail(block, end_src, "slice of pointer must include end value", .{}); }; - const slice_sentinel = if (sentinel_opt != .none) blk: { - const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src); - break :blk try sema.resolveConstValue(block, sentinel_src, casted); - } else null; + const sentinel = s: { + if (sentinel_opt != .none) { + const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src); + break :s try sema.resolveConstValue(block, sentinel_src, casted); + } + // If we are slicing to the end of something that is sentinel-terminated + // then the resulting slice type is also sentinel-terminated. + if (end_is_len) { + if (ptr_sentinel) |sent| { + break :s sent; + } + } + break :s null; + }; const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src); - const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len); const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data; @@ -15678,11 +15725,6 @@ fn analyzeSlice( if (opt_new_len_val) |new_len_val| { const new_len_int = new_len_val.toUnsignedInt(); - const sentinel = if (array_ty.zigTypeTag() == .Array and new_len_int == array_ty.arrayLen()) - array_ty.sentinel() - else - slice_sentinel; - const return_ty = try Type.ptr(sema.arena, .{ .pointee_type = try Type.array(sema.arena, new_len_int, sentinel, elem_ty), .sentinel = null, @@ -15713,7 +15755,7 @@ fn analyzeSlice( const return_ty = try Type.ptr(sema.arena, .{ .pointee_type = elem_ty, - .sentinel = slice_sentinel, + .sentinel = sentinel, .@"align" = new_ptr_ty_info.@"align", .@"addrspace" = new_ptr_ty_info.@"addrspace", .mutable = new_ptr_ty_info.mutable, diff --git a/src/type.zig b/src/type.zig index 5632629bff..272d09a921 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3042,6 +3042,9 @@ pub const Type = extern union { .array_u8, .manyptr_u8, .manyptr_const_u8, + .const_slice_u8, + .const_slice, + .mut_slice, => return null, .pointer => return self.castTag(.pointer).?.data.sentinel, diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 0b01139800..d4e8284751 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -192,3 +192,15 @@ test "compile time slice of pointer to hard coded address" { try expect(@ptrToInt(y) == 0x1400); try expect(y.len == 0x400); } + +test "slice string literal has correct type" { + comptime { + try expect(@TypeOf("aoeu"[0..]) == *const [4:0]u8); + const array = [_]i32{ 1, 2, 3, 4 }; + try expect(@TypeOf(array[0..]) == *const [4]i32); + } + var runtime_zero: usize = 0; + comptime try expect(@TypeOf("aoeu"[runtime_zero..]) == [:0]const u8); + const array = [_]i32{ 1, 2, 3, 4 }; + comptime try expect(@TypeOf(array[runtime_zero..]) == []const i32); +} diff --git a/test/behavior/slice_stage1.zig b/test/behavior/slice_stage1.zig index 3df7a75e10..cb7c0f5223 100644 --- a/test/behavior/slice_stage1.zig +++ b/test/behavior/slice_stage1.zig @@ -4,18 +4,6 @@ const expectEqualSlices = std.testing.expectEqualSlices; const expectEqual = std.testing.expectEqual; const mem = std.mem; -test "slice string literal has correct type" { - comptime { - try expect(@TypeOf("aoeu"[0..]) == *const [4:0]u8); - const array = [_]i32{ 1, 2, 3, 4 }; - try expect(@TypeOf(array[0..]) == *const [4]i32); - } - var runtime_zero: usize = 0; - comptime try expect(@TypeOf("aoeu"[runtime_zero..]) == [:0]const u8); - const array = [_]i32{ 1, 2, 3, 4 }; - comptime try expect(@TypeOf(array[runtime_zero..]) == []const i32); -} - test "result location zero sized array inside struct field implicit cast to slice" { const E = struct { entries: []u32, From 1b6a1e691fab75ce40c9d0c4015c9f4a46b72aa4 Mon Sep 17 00:00:00 2001 From: Mateusz Radomski <33978857+m-radomski@users.noreply.github.com> Date: Fri, 4 Feb 2022 06:58:27 +0100 Subject: [PATCH 0025/2031] Sema: check for NaNs in cmp (#10760) --- src/Sema.zig | 7 +++++++ test/behavior/math.zig | 6 ++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index a3dccf1d7d..7666b2c9ec 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -15816,6 +15816,13 @@ fn cmpNumeric( if (lhs_val.isUndef() or rhs_val.isUndef()) { return sema.addConstUndef(Type.bool); } + if (lhs_val.isNan() or rhs_val.isNan()) { + if (op == std.math.CompareOperator.neq) { + return Air.Inst.Ref.bool_true; + } else { + return Air.Inst.Ref.bool_false; + } + } if (Value.compareHetero(lhs_val, op, rhs_val)) { return Air.Inst.Ref.bool_true; } else { diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 77820ac0fb..8f947e2829 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -979,18 +979,16 @@ test "vector integer addition" { } test "NaN comparison" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - try testNanEqNan(f16); try testNanEqNan(f32); try testNanEqNan(f64); try testNanEqNan(f128); - if (has_f80_rt) try testNanEqNan(f80); + if (has_f80_rt and (builtin.zig_backend == .stage1)) try testNanEqNan(f80); // TODO comptime try testNanEqNan(f16); comptime try testNanEqNan(f32); comptime try testNanEqNan(f64); comptime try testNanEqNan(f128); - // comptime try testNanEqNan(f80); + // comptime try testNanEqNan(f80); // TODO } fn testNanEqNan(comptime F: type) !void { From 64f7231f86d4b8a155f48087b3f173d8e41b620c Mon Sep 17 00:00:00 2001 From: Kazuki Sakamoto Date: Thu, 3 Feb 2022 21:12:36 -0800 Subject: [PATCH 0026/2031] stage1: Fix missing LLD library --- cmake/Findlld.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/Findlld.cmake b/cmake/Findlld.cmake index 5b5fbcb468..8a46888531 100644 --- a/cmake/Findlld.cmake +++ b/cmake/Findlld.cmake @@ -49,6 +49,7 @@ else() FIND_AND_ADD_LLD_LIB(lldELF) FIND_AND_ADD_LLD_LIB(lldCOFF) FIND_AND_ADD_LLD_LIB(lldWasm) + FIND_AND_ADD_LLD_LIB(lldMachO) FIND_AND_ADD_LLD_LIB(lldReaderWriter) FIND_AND_ADD_LLD_LIB(lldCore) FIND_AND_ADD_LLD_LIB(lldYAML) From 95fbce2b958395a367a82ce33170edd93e686173 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 3 Feb 2022 23:57:05 -0700 Subject: [PATCH 0027/2031] Sema: fixes to fieldVal, resolveStructFully, Type.eql fieldVal handles pointer to pointer to array. This can happen for example, if a pointer to an array is used as the condition expression of a for loop. resolveStructFully handles tuples (by doing nothing). fixed Type comparison for tuples to handle comptime fields properly. --- src/Sema.zig | 62 +++--- src/type.zig | 14 +- test/behavior.zig | 1 - test/behavior/slice.zig | 352 +++++++++++++++++++++++++++++++++ test/behavior/slice_stage1.zig | 347 -------------------------------- 5 files changed, 405 insertions(+), 371 deletions(-) delete mode 100644 test/behavior/slice_stage1.zig diff --git a/src/Sema.zig b/src/Sema.zig index 7666b2c9ec..c4b3ad8c33 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12998,7 +12998,7 @@ fn fieldVal( .Array => { if (mem.eql(u8, field_name, "len")) { return sema.addConstant( - Type.initTag(.comptime_int), + Type.comptime_int, try Value.Tag.int_u64.create(arena, inner_ty.arrayLen()), ); } else { @@ -13010,26 +13010,43 @@ fn fieldVal( ); } }, - .Pointer => if (inner_ty.isSlice()) { - if (mem.eql(u8, field_name, "ptr")) { - const slice = if (is_pointer_to) - try sema.analyzeLoad(block, src, object, object_src) - else - object; - return sema.analyzeSlicePtr(block, src, slice, inner_ty, object_src); - } else if (mem.eql(u8, field_name, "len")) { - const slice = if (is_pointer_to) - try sema.analyzeLoad(block, src, object, object_src) - else - object; - return sema.analyzeSliceLen(block, src, slice); - } else { - return sema.fail( - block, - field_name_src, - "no member named '{s}' in '{}'", - .{ field_name, object_ty }, - ); + .Pointer => { + const ptr_info = inner_ty.ptrInfo().data; + if (ptr_info.size == .Slice) { + if (mem.eql(u8, field_name, "ptr")) { + const slice = if (is_pointer_to) + try sema.analyzeLoad(block, src, object, object_src) + else + object; + return sema.analyzeSlicePtr(block, src, slice, inner_ty, object_src); + } else if (mem.eql(u8, field_name, "len")) { + const slice = if (is_pointer_to) + try sema.analyzeLoad(block, src, object, object_src) + else + object; + return sema.analyzeSliceLen(block, src, slice); + } else { + return sema.fail( + block, + field_name_src, + "no member named '{s}' in '{}'", + .{ field_name, object_ty }, + ); + } + } else if (ptr_info.pointee_type.zigTypeTag() == .Array) { + if (mem.eql(u8, field_name, "len")) { + return sema.addConstant( + Type.comptime_int, + try Value.Tag.int_u64.create(arena, ptr_info.pointee_type.arrayLen()), + ); + } else { + return sema.fail( + block, + field_name_src, + "no member named '{s}' in '{}'", + .{ field_name, ptr_info.pointee_type }, + ); + } } }, .Type => { @@ -16371,7 +16388,8 @@ fn resolveStructFully( try resolveStructLayout(sema, block, src, ty); const resolved_ty = try sema.resolveTypeFields(block, src, ty); - const struct_obj = resolved_ty.castTag(.@"struct").?.data; + const payload = resolved_ty.castTag(.@"struct") orelse return; + const struct_obj = payload.data; switch (struct_obj.status) { .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {}, .fully_resolved_wip, .fully_resolved => return, diff --git a/src/type.zig b/src/type.zig index 272d09a921..e3a4b3d60a 100644 --- a/src/type.zig +++ b/src/type.zig @@ -634,7 +634,19 @@ pub const Type = extern union { for (a_payload.data.values) |a_val, i| { const ty = a_payload.data.types[i]; const b_val = b_payload.data.values[i]; - if (!Value.eql(a_val, b_val, ty)) return false; + if (a_val.tag() == .unreachable_value) { + if (b_val.tag() == .unreachable_value) { + continue; + } else { + return false; + } + } else { + if (b_val.tag() == .unreachable_value) { + return false; + } else { + if (!Value.eql(a_val, b_val, ty)) return false; + } + } } return true; diff --git a/test/behavior.zig b/test/behavior.zig index 0f74ed7d59..7b6cb6b402 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -163,7 +163,6 @@ test { _ = @import("behavior/select.zig"); _ = @import("behavior/shuffle.zig"); _ = @import("behavior/sizeof_and_typeof_stage1.zig"); - _ = @import("behavior/slice_stage1.zig"); _ = @import("behavior/struct_contains_null_ptr_itself.zig"); _ = @import("behavior/struct_contains_slice_of_itself.zig"); _ = @import("behavior/switch_prong_err_enum.zig"); diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index d4e8284751..4ec5f11817 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -204,3 +204,355 @@ test "slice string literal has correct type" { const array = [_]i32{ 1, 2, 3, 4 }; comptime try expect(@TypeOf(array[runtime_zero..]) == []const i32); } + +test "result location zero sized array inside struct field implicit cast to slice" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + + const E = struct { + entries: []u32, + }; + var foo = E{ .entries = &[_]u32{} }; + try expect(foo.entries.len == 0); +} + +test "runtime safety lets us slice from len..len" { + var an_array = [_]u8{ 1, 2, 3 }; + try expect(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), "")); +} + +fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { + return a_slice[start..end]; +} + +test "C pointer" { + var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf"; + var len: u32 = 10; + var slice = buf[0..len]; + try expect(mem.eql(u8, "kjdhfkjdhf", slice)); +} + +test "C pointer slice access" { + var buf: [10]u32 = [1]u32{42} ** 10; + const c_ptr = @ptrCast([*c]const u32, &buf); + + var runtime_zero: usize = 0; + comptime try expectEqual([]const u32, @TypeOf(c_ptr[runtime_zero..1])); + comptime try expectEqual(*const [1]u32, @TypeOf(c_ptr[0..1])); + + for (c_ptr[0..5]) |*cl| { + try expect(@as(u32, 42) == cl.*); + } +} + +test "comptime slices are disambiguated" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + try expect(sliceSum(&[_]u8{ 1, 2 }) == 3); + try expect(sliceSum(&[_]u8{ 3, 4 }) == 7); +} + +fn sliceSum(comptime q: []const u8) i32 { + comptime var result = 0; + inline for (q) |item| { + result += item; + } + return result; +} + +test "slice type with custom alignment" { + const LazilyResolvedType = struct { + anything: i32, + }; + var slice: []align(32) LazilyResolvedType = undefined; + var array: [10]LazilyResolvedType align(32) = undefined; + slice = &array; + slice[1].anything = 42; + try expect(array[1].anything == 42); +} + +test "obtaining a null terminated slice" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + + // here we have a normal array + var buf: [50]u8 = undefined; + + buf[0] = 'a'; + buf[1] = 'b'; + buf[2] = 'c'; + buf[3] = 0; + + // now we obtain a null terminated slice: + const ptr = buf[0..3 :0]; + _ = ptr; + + var runtime_len: usize = 3; + const ptr2 = buf[0..runtime_len :0]; + // ptr2 is a null-terminated slice + comptime try expect(@TypeOf(ptr2) == [:0]u8); + comptime try expect(@TypeOf(ptr2[0..2]) == *[2]u8); + var runtime_zero: usize = 0; + comptime try expect(@TypeOf(ptr2[runtime_zero..2]) == []u8); +} + +test "empty array to slice" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + const empty: []align(16) u8 = &[_]u8{}; + const align_1: []align(1) u8 = empty; + const align_4: []align(4) u8 = empty; + const align_16: []align(16) u8 = empty; + try expectEqual(1, @typeInfo(@TypeOf(align_1)).Pointer.alignment); + try expectEqual(4, @typeInfo(@TypeOf(align_4)).Pointer.alignment); + try expectEqual(16, @typeInfo(@TypeOf(align_16)).Pointer.alignment); + } + }; + + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "@ptrCast slice to pointer" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var array align(@alignOf(u16)) = [5]u8{ 0xff, 0xff, 0xff, 0xff, 0xff }; + var slice: []u8 = &array; + var ptr = @ptrCast(*u16, slice); + try expect(ptr.* == 65535); + } + }; + + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "slice syntax resulting in pointer-to-array" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + try testArray(); + try testArrayZ(); + try testArray0(); + try testArrayAlign(); + try testPointer(); + try testPointerZ(); + try testPointer0(); + try testPointerAlign(); + try testSlice(); + try testSliceZ(); + try testSlice0(); + try testSliceOpt(); + try testSliceAlign(); + } + + fn testArray() !void { + var array = [5]u8{ 1, 2, 3, 4, 5 }; + var slice = array[1..3]; + comptime try expect(@TypeOf(slice) == *[2]u8); + try expect(slice[0] == 2); + try expect(slice[1] == 3); + } + + fn testArrayZ() !void { + var array = [5:0]u8{ 1, 2, 3, 4, 5 }; + comptime try expect(@TypeOf(array[1..3]) == *[2]u8); + comptime try expect(@TypeOf(array[1..5]) == *[4:0]u8); + comptime try expect(@TypeOf(array[1..]) == *[4:0]u8); + comptime try expect(@TypeOf(array[1..3 :4]) == *[2:4]u8); + } + + fn testArray0() !void { + { + var array = [0]u8{}; + var slice = array[0..0]; + comptime try expect(@TypeOf(slice) == *[0]u8); + } + { + var array = [0:0]u8{}; + var slice = array[0..0]; + comptime try expect(@TypeOf(slice) == *[0:0]u8); + try expect(slice[0] == 0); + } + } + + fn testArrayAlign() !void { + var array align(4) = [5]u8{ 1, 2, 3, 4, 5 }; + var slice = array[4..5]; + comptime try expect(@TypeOf(slice) == *align(4) [1]u8); + try expect(slice[0] == 5); + comptime try expect(@TypeOf(array[0..2]) == *align(4) [2]u8); + } + + fn testPointer() !void { + var array = [5]u8{ 1, 2, 3, 4, 5 }; + var pointer: [*]u8 = &array; + var slice = pointer[1..3]; + comptime try expect(@TypeOf(slice) == *[2]u8); + try expect(slice[0] == 2); + try expect(slice[1] == 3); + } + + fn testPointerZ() !void { + var array = [5:0]u8{ 1, 2, 3, 4, 5 }; + var pointer: [*:0]u8 = &array; + comptime try expect(@TypeOf(pointer[1..3]) == *[2]u8); + comptime try expect(@TypeOf(pointer[1..3 :4]) == *[2:4]u8); + } + + fn testPointer0() !void { + var pointer: [*]const u0 = &[1]u0{0}; + var slice = pointer[0..1]; + comptime try expect(@TypeOf(slice) == *const [1]u0); + try expect(slice[0] == 0); + } + + fn testPointerAlign() !void { + var array align(4) = [5]u8{ 1, 2, 3, 4, 5 }; + var pointer: [*]align(4) u8 = &array; + var slice = pointer[4..5]; + comptime try expect(@TypeOf(slice) == *align(4) [1]u8); + try expect(slice[0] == 5); + comptime try expect(@TypeOf(pointer[0..2]) == *align(4) [2]u8); + } + + fn testSlice() !void { + var array = [5]u8{ 1, 2, 3, 4, 5 }; + var src_slice: []u8 = &array; + var slice = src_slice[1..3]; + comptime try expect(@TypeOf(slice) == *[2]u8); + try expect(slice[0] == 2); + try expect(slice[1] == 3); + } + + fn testSliceZ() !void { + var array = [5:0]u8{ 1, 2, 3, 4, 5 }; + var slice: [:0]u8 = &array; + comptime try expect(@TypeOf(slice[1..3]) == *[2]u8); + comptime try expect(@TypeOf(slice[1..]) == [:0]u8); + comptime try expect(@TypeOf(slice[1..3 :4]) == *[2:4]u8); + } + + fn testSliceOpt() !void { + var array: [2]u8 = [2]u8{ 1, 2 }; + var slice: ?[]u8 = &array; + comptime try expect(@TypeOf(&array, slice) == ?[]u8); + comptime try expect(@TypeOf(slice.?[0..2]) == *[2]u8); + } + + fn testSlice0() !void { + { + var array = [0]u8{}; + var src_slice: []u8 = &array; + var slice = src_slice[0..0]; + comptime try expect(@TypeOf(slice) == *[0]u8); + } + { + var array = [0:0]u8{}; + var src_slice: [:0]u8 = &array; + var slice = src_slice[0..0]; + comptime try expect(@TypeOf(slice) == *[0]u8); + } + } + + fn testSliceAlign() !void { + var array align(4) = [5]u8{ 1, 2, 3, 4, 5 }; + var src_slice: []align(4) u8 = &array; + var slice = src_slice[4..5]; + comptime try expect(@TypeOf(slice) == *align(4) [1]u8); + try expect(slice[0] == 5); + comptime try expect(@TypeOf(src_slice[0..2]) == *align(4) [2]u8); + } + + fn testConcatStrLiterals() !void { + try expectEqualSlices("a"[0..] ++ "b"[0..], "ab"); + try expectEqualSlices("a"[0.. :0] ++ "b"[0.. :0], "ab"); + } + }; + + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "type coercion of pointer to anon struct literal to pointer to slice" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + const U = union { + a: u32, + b: bool, + c: []const u8, + }; + + fn doTheTest() !void { + var x1: u8 = 42; + const t1 = &.{ x1, 56, 54 }; + var slice1: []const u8 = t1; + try expect(slice1.len == 3); + try expect(slice1[0] == 42); + try expect(slice1[1] == 56); + try expect(slice1[2] == 54); + + var x2: []const u8 = "hello"; + const t2 = &.{ x2, ", ", "world!" }; + // @compileLog(@TypeOf(t2)); + var slice2: []const []const u8 = t2; + try expect(slice2.len == 3); + try expect(mem.eql(u8, slice2[0], "hello")); + try expect(mem.eql(u8, slice2[1], ", ")); + try expect(mem.eql(u8, slice2[2], "world!")); + } + }; + // try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "array concat of slices gives slice" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + comptime { + var a: []const u8 = "aoeu"; + var b: []const u8 = "asdf"; + const c = a ++ b; + try expect(std.mem.eql(u8, c, "aoeuasdf")); + } +} + +test "slice bounds in comptime concatenation" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const bs = comptime blk: { + const b = "........1........"; + break :blk b[8..9]; + }; + const str = "" ++ bs; + try expect(str.len == 1); + try expect(std.mem.eql(u8, str, "1")); + + const str2 = bs ++ ""; + try expect(str2.len == 1); + try expect(std.mem.eql(u8, str2, "1")); +} + +test "slice sentinel access at comptime" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + { + const str0 = &[_:0]u8{ '1', '2', '3' }; + const slice0: [:0]const u8 = str0; + + try expect(slice0.len == 3); + try expect(slice0[slice0.len] == 0); + } + { + const str0 = "123"; + _ = &str0[0]; + const slice0: [:0]const u8 = str0; + + try expect(slice0.len == 3); + try expect(slice0[slice0.len] == 0); + } +} diff --git a/test/behavior/slice_stage1.zig b/test/behavior/slice_stage1.zig deleted file mode 100644 index cb7c0f5223..0000000000 --- a/test/behavior/slice_stage1.zig +++ /dev/null @@ -1,347 +0,0 @@ -const std = @import("std"); -const expect = std.testing.expect; -const expectEqualSlices = std.testing.expectEqualSlices; -const expectEqual = std.testing.expectEqual; -const mem = std.mem; - -test "result location zero sized array inside struct field implicit cast to slice" { - const E = struct { - entries: []u32, - }; - var foo = E{ .entries = &[_]u32{} }; - try expect(foo.entries.len == 0); -} - -const x = @intToPtr([*]i32, 0x1000)[0..0x500]; -const y = x[0x100..]; -test "compile time slice of pointer to hard coded address" { - try expect(@ptrToInt(x) == 0x1000); - try expect(x.len == 0x500); - - try expect(@ptrToInt(y) == 0x1100); - try expect(y.len == 0x400); -} - -test "runtime safety lets us slice from len..len" { - var an_array = [_]u8{ 1, 2, 3 }; - try expect(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), "")); -} - -fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { - return a_slice[start..end]; -} - -test "C pointer" { - var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf"; - var len: u32 = 10; - var slice = buf[0..len]; - try expectEqualSlices(u8, "kjdhfkjdhf", slice); -} - -test "C pointer slice access" { - var buf: [10]u32 = [1]u32{42} ** 10; - const c_ptr = @ptrCast([*c]const u32, &buf); - - var runtime_zero: usize = 0; - comptime try expectEqual([]const u32, @TypeOf(c_ptr[runtime_zero..1])); - comptime try expectEqual(*const [1]u32, @TypeOf(c_ptr[0..1])); - - for (c_ptr[0..5]) |*cl| { - try expectEqual(@as(u32, 42), cl.*); - } -} - -fn sliceSum(comptime q: []const u8) i32 { - comptime var result = 0; - inline for (q) |item| { - result += item; - } - return result; -} - -test "comptime slices are disambiguated" { - try expect(sliceSum(&[_]u8{ 1, 2 }) == 3); - try expect(sliceSum(&[_]u8{ 3, 4 }) == 7); -} - -test "slice type with custom alignment" { - const LazilyResolvedType = struct { - anything: i32, - }; - var slice: []align(32) LazilyResolvedType = undefined; - var array: [10]LazilyResolvedType align(32) = undefined; - slice = &array; - slice[1].anything = 42; - try expect(array[1].anything == 42); -} - -test "obtaining a null terminated slice" { - // here we have a normal array - var buf: [50]u8 = undefined; - - buf[0] = 'a'; - buf[1] = 'b'; - buf[2] = 'c'; - buf[3] = 0; - - // now we obtain a null terminated slice: - const ptr = buf[0..3 :0]; - _ = ptr; - - var runtime_len: usize = 3; - const ptr2 = buf[0..runtime_len :0]; - // ptr2 is a null-terminated slice - comptime try expect(@TypeOf(ptr2) == [:0]u8); - comptime try expect(@TypeOf(ptr2[0..2]) == *[2]u8); - var runtime_zero: usize = 0; - comptime try expect(@TypeOf(ptr2[runtime_zero..2]) == []u8); -} - -test "empty array to slice" { - const S = struct { - fn doTheTest() !void { - const empty: []align(16) u8 = &[_]u8{}; - const align_1: []align(1) u8 = empty; - const align_4: []align(4) u8 = empty; - const align_16: []align(16) u8 = empty; - try expectEqual(1, @typeInfo(@TypeOf(align_1)).Pointer.alignment); - try expectEqual(4, @typeInfo(@TypeOf(align_4)).Pointer.alignment); - try expectEqual(16, @typeInfo(@TypeOf(align_16)).Pointer.alignment); - } - }; - - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "@ptrCast slice to pointer" { - const S = struct { - fn doTheTest() !void { - var array align(@alignOf(u16)) = [5]u8{ 0xff, 0xff, 0xff, 0xff, 0xff }; - var slice: []u8 = &array; - var ptr = @ptrCast(*u16, slice); - try expect(ptr.* == 65535); - } - }; - - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "slice syntax resulting in pointer-to-array" { - const S = struct { - fn doTheTest() !void { - try testArray(); - try testArrayZ(); - try testArray0(); - try testArrayAlign(); - try testPointer(); - try testPointerZ(); - try testPointer0(); - try testPointerAlign(); - try testSlice(); - try testSliceZ(); - try testSlice0(); - try testSliceOpt(); - try testSliceAlign(); - } - - fn testArray() !void { - var array = [5]u8{ 1, 2, 3, 4, 5 }; - var slice = array[1..3]; - comptime try expect(@TypeOf(slice) == *[2]u8); - try expect(slice[0] == 2); - try expect(slice[1] == 3); - } - - fn testArrayZ() !void { - var array = [5:0]u8{ 1, 2, 3, 4, 5 }; - comptime try expect(@TypeOf(array[1..3]) == *[2]u8); - comptime try expect(@TypeOf(array[1..5]) == *[4:0]u8); - comptime try expect(@TypeOf(array[1..]) == *[4:0]u8); - comptime try expect(@TypeOf(array[1..3 :4]) == *[2:4]u8); - } - - fn testArray0() !void { - { - var array = [0]u8{}; - var slice = array[0..0]; - comptime try expect(@TypeOf(slice) == *[0]u8); - } - { - var array = [0:0]u8{}; - var slice = array[0..0]; - comptime try expect(@TypeOf(slice) == *[0:0]u8); - try expect(slice[0] == 0); - } - } - - fn testArrayAlign() !void { - var array align(4) = [5]u8{ 1, 2, 3, 4, 5 }; - var slice = array[4..5]; - comptime try expect(@TypeOf(slice) == *align(4) [1]u8); - try expect(slice[0] == 5); - comptime try expect(@TypeOf(array[0..2]) == *align(4) [2]u8); - } - - fn testPointer() !void { - var array = [5]u8{ 1, 2, 3, 4, 5 }; - var pointer: [*]u8 = &array; - var slice = pointer[1..3]; - comptime try expect(@TypeOf(slice) == *[2]u8); - try expect(slice[0] == 2); - try expect(slice[1] == 3); - } - - fn testPointerZ() !void { - var array = [5:0]u8{ 1, 2, 3, 4, 5 }; - var pointer: [*:0]u8 = &array; - comptime try expect(@TypeOf(pointer[1..3]) == *[2]u8); - comptime try expect(@TypeOf(pointer[1..3 :4]) == *[2:4]u8); - } - - fn testPointer0() !void { - var pointer: [*]const u0 = &[1]u0{0}; - var slice = pointer[0..1]; - comptime try expect(@TypeOf(slice) == *const [1]u0); - try expect(slice[0] == 0); - } - - fn testPointerAlign() !void { - var array align(4) = [5]u8{ 1, 2, 3, 4, 5 }; - var pointer: [*]align(4) u8 = &array; - var slice = pointer[4..5]; - comptime try expect(@TypeOf(slice) == *align(4) [1]u8); - try expect(slice[0] == 5); - comptime try expect(@TypeOf(pointer[0..2]) == *align(4) [2]u8); - } - - fn testSlice() !void { - var array = [5]u8{ 1, 2, 3, 4, 5 }; - var src_slice: []u8 = &array; - var slice = src_slice[1..3]; - comptime try expect(@TypeOf(slice) == *[2]u8); - try expect(slice[0] == 2); - try expect(slice[1] == 3); - } - - fn testSliceZ() !void { - var array = [5:0]u8{ 1, 2, 3, 4, 5 }; - var slice: [:0]u8 = &array; - comptime try expect(@TypeOf(slice[1..3]) == *[2]u8); - comptime try expect(@TypeOf(slice[1..]) == [:0]u8); - comptime try expect(@TypeOf(slice[1..3 :4]) == *[2:4]u8); - } - - fn testSliceOpt() !void { - var array: [2]u8 = [2]u8{ 1, 2 }; - var slice: ?[]u8 = &array; - comptime try expect(@TypeOf(&array, slice) == ?[]u8); - comptime try expect(@TypeOf(slice.?[0..2]) == *[2]u8); - } - - fn testSlice0() !void { - { - var array = [0]u8{}; - var src_slice: []u8 = &array; - var slice = src_slice[0..0]; - comptime try expect(@TypeOf(slice) == *[0]u8); - } - { - var array = [0:0]u8{}; - var src_slice: [:0]u8 = &array; - var slice = src_slice[0..0]; - comptime try expect(@TypeOf(slice) == *[0]u8); - } - } - - fn testSliceAlign() !void { - var array align(4) = [5]u8{ 1, 2, 3, 4, 5 }; - var src_slice: []align(4) u8 = &array; - var slice = src_slice[4..5]; - comptime try expect(@TypeOf(slice) == *align(4) [1]u8); - try expect(slice[0] == 5); - comptime try expect(@TypeOf(src_slice[0..2]) == *align(4) [2]u8); - } - - fn testConcatStrLiterals() !void { - try expectEqualSlices("a"[0..] ++ "b"[0..], "ab"); - try expectEqualSlices("a"[0.. :0] ++ "b"[0.. :0], "ab"); - } - }; - - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "type coercion of pointer to anon struct literal to pointer to slice" { - const S = struct { - const U = union { - a: u32, - b: bool, - c: []const u8, - }; - - fn doTheTest() !void { - var x1: u8 = 42; - const t1 = &.{ x1, 56, 54 }; - var slice1: []const u8 = t1; - try expect(slice1.len == 3); - try expect(slice1[0] == 42); - try expect(slice1[1] == 56); - try expect(slice1[2] == 54); - - var x2: []const u8 = "hello"; - const t2 = &.{ x2, ", ", "world!" }; - // @compileLog(@TypeOf(t2)); - var slice2: []const []const u8 = t2; - try expect(slice2.len == 3); - try expect(mem.eql(u8, slice2[0], "hello")); - try expect(mem.eql(u8, slice2[1], ", ")); - try expect(mem.eql(u8, slice2[2], "world!")); - } - }; - // try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "array concat of slices gives slice" { - comptime { - var a: []const u8 = "aoeu"; - var b: []const u8 = "asdf"; - const c = a ++ b; - try expect(std.mem.eql(u8, c, "aoeuasdf")); - } -} - -test "slice bounds in comptime concatenation" { - const bs = comptime blk: { - const b = "........1........"; - break :blk b[8..9]; - }; - const str = "" ++ bs; - try expect(str.len == 1); - try expect(std.mem.eql(u8, str, "1")); - - const str2 = bs ++ ""; - try expect(str2.len == 1); - try expect(std.mem.eql(u8, str2, "1")); -} - -test "slice sentinel access at comptime" { - { - const str0 = &[_:0]u8{ '1', '2', '3' }; - const slice0: [:0]const u8 = str0; - - try expect(slice0.len == 3); - try expect(slice0[slice0.len] == 0); - } - { - const str0 = "123"; - _ = &str0[0]; - const slice0: [:0]const u8 = str0; - - try expect(slice0.len == 3); - try expect(slice0[slice0.len] == 0); - } -} From 71321b694195a87ab7394a25badf5295eb01875e Mon Sep 17 00:00:00 2001 From: Kirk Scheibelhut Date: Fri, 4 Feb 2022 11:27:50 -0800 Subject: [PATCH 0028/2031] Various documentation fixes Co-authored-by: Kirk Scheibelhut Co-authored-by: extrasharp --- doc/langref.html.in | 190 +++++++++++++++++++++++++++----------------- 1 file changed, 119 insertions(+), 71 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 955d17f253..fd104db6da 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1295,13 +1295,39 @@ test "expectError demo" { A variable is a unit of {#link|Memory#} storage.

- Variables are never allowed to shadow identifiers from an outer scope. -

-

It is generally preferable to use {#syntax#}const{#endsyntax#} rather than {#syntax#}var{#endsyntax#} when declaring a variable. This causes less work for both humans and computers to do when reading code, and creates more optimization opportunities.

+ + {#header_open|Identifiers#} +

+ Variable identifiers are never allowed to shadow identifiers from an outer scope. +

+

+ Identifiers must start with an alphabetic character or underscore and may be followed + by any number of alphanumeric characters or underscores. + They must not overlap with any keywords. See {#link|Keyword Reference#}. +

+

+ If a name that does not fit these requirements is needed, such as for linking with external libraries, the {#syntax#}@""{#endsyntax#} syntax may be used. +

+ {#code_begin|syntax#} +const @"identifier with spaces in it" = 0xff; +const @"1SmallStep4Man" = 112358; + +const c = @import("std").c; +pub extern "c" fn @"error"() anyopaque; +pub extern "c" fn @"fstat$INODE64"(fd: c.fd_t, buf: *c.Stat) c_int; + +const Color = enum { + red, + @"really red", +}; +const color: Color = .@"really red"; + {#code_end#} + {#header_close#} + {#header_open|Container Level Variables#}

Container level variables have static lifetime and are order-independent and lazily analyzed. @@ -1486,7 +1512,7 @@ fn divide(a: i32, b: i32) i32 {

Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause undefined behavior on - integer overflow. Alternative operators are provided for wrapping and saturating arithmetic on all targets. + integer overflow. Alternative operators are provided for wrapping and saturating arithmetic on all targets. {#syntax#}+%{#endsyntax#} and {#syntax#}-%{#endsyntax#} perform wrapping arithmetic while {#syntax#}+|{#endsyntax#} and {#syntax#}-|{#endsyntax#} perform saturating arithmetic.

@@ -2494,32 +2520,32 @@ test "null terminated array" { or using the shorthand function {#syntax#}std.meta.Vector{#endsyntax#}.

- Vectors support the same builtin operators as their underlying base types. These operations are performed + Vectors support the same builtin operators as their underlying base types. These operations are performed element-wise, and return a vector of the same length as the input vectors. This includes:

    -
  • Arithmetic ({#syntax#}+{#endsyntax#}, {#syntax#}-{#endsyntax#}, {#syntax#}/{#endsyntax#}, {#syntax#}*{#endsyntax#}, - {#syntax#}@divFloor{#endsyntax#}, {#syntax#}@sqrt{#endsyntax#}, {#syntax#}@ceil{#endsyntax#}, +
  • Arithmetic ({#syntax#}+{#endsyntax#}, {#syntax#}-{#endsyntax#}, {#syntax#}/{#endsyntax#}, {#syntax#}*{#endsyntax#}, + {#syntax#}@divFloor{#endsyntax#}, {#syntax#}@sqrt{#endsyntax#}, {#syntax#}@ceil{#endsyntax#}, {#syntax#}@log{#endsyntax#}, etc.)
  • -
  • Bitwise operators ({#syntax#}>>{#endsyntax#}, {#syntax#}<<{#endsyntax#}, {#syntax#}&{#endsyntax#}, +
  • Bitwise operators ({#syntax#}>>{#endsyntax#}, {#syntax#}<<{#endsyntax#}, {#syntax#}&{#endsyntax#}, {#syntax#}|{#endsyntax#}, {#syntax#}~{#endsyntax#}, etc.)
  • Comparison operators ({#syntax#}<{#endsyntax#}, {#syntax#}>{#endsyntax#}, {#syntax#}=={#endsyntax#}, etc.)

- It is prohibited to use a math operator on a mixture of scalars (individual numbers) and vectors. - Zig provides the {#link|@splat#} builtin to easily convert from scalars to vectors, and it supports {#link|@reduce#} - and array indexing syntax to convert from vectors to scalars. Vectors also support assignment to and from + It is prohibited to use a math operator on a mixture of scalars (individual numbers) and vectors. + Zig provides the {#link|@splat#} builtin to easily convert from scalars to vectors, and it supports {#link|@reduce#} + and array indexing syntax to convert from vectors to scalars. Vectors also support assignment to and from fixed-length arrays with comptime known length.

For rearranging elements within and between vectors, Zig provides the {#link|@shuffle#} and {#link|@select#} functions.

- Operations on vectors shorter than the target machine's native SIMD size will typically compile to single SIMD - instructions, while vectors longer than the target machine's native SIMD size will compile to multiple SIMD - instructions. If a given operation doesn't have SIMD support on the target architecture, the compiler will default - to operating on each vector element one at a time. Zig supports any comptime-known vector length up to 2^32-1, - although small powers of two (2-64) are most typical. Note that excessively long vector lengths (e.g. 2^20) may + Operations on vectors shorter than the target machine's native SIMD size will typically compile to single SIMD + instructions, while vectors longer than the target machine's native SIMD size will compile to multiple SIMD + instructions. If a given operation doesn't have SIMD support on the target architecture, the compiler will default + to operating on each vector element one at a time. Zig supports any comptime-known vector length up to 2^32-1, + although small powers of two (2-64) are most typical. Note that excessively long vector lengths (e.g. 2^20) may result in compiler crashes on current versions of Zig.

{#code_begin|test|vector_example#} @@ -2569,7 +2595,7 @@ test "Conversion between vectors, arrays, and slices" { TODO consider suggesting std.MultiArrayList

{#see_also|@splat|@shuffle|@select|@reduce#} - + {#header_close#} {#header_open|Pointers#} @@ -2987,8 +3013,8 @@ test "null terminated slice" { } {#code_end#}

- Sentinel-terminated slices can also be created using a variation of the slice syntax - {#syntax#}data[start..end :x]{#endsyntax#}, where {#syntax#}data{#endsyntax#} is a many-item pointer, + Sentinel-terminated slices can also be created using a variation of the slice syntax + {#syntax#}data[start..end :x]{#endsyntax#}, where {#syntax#}data{#endsyntax#} is a many-item pointer, array or slice and {#syntax#}x{#endsyntax#} is the sentinel value.

{#code_begin|test|null_terminated_slicing#} @@ -3005,7 +3031,7 @@ test "null terminated slicing" { } {#code_end#}

- Sentinel-terminated slicing asserts that the element in the sentinel position of the backing data is + Sentinel-terminated slicing asserts that the element in the sentinel position of the backing data is actually the sentinel value. If this is not the case, safety-protected {#link|Undefined Behavior#} results.

{#code_begin|test_safety|sentinel mismatch#} @@ -3014,10 +3040,10 @@ const expect = std.testing.expect; test "sentinel mismatch" { var array = [_]u8{ 3, 2, 1, 0 }; - - // Creating a sentinel-terminated slice from the array with a length of 2 - // will result in the value `1` occupying the sentinel element position. - // This does not match the indicated sentinel value of `0` and will lead + + // Creating a sentinel-terminated slice from the array with a length of 2 + // will result in the value `1` occupying the sentinel element position. + // This does not match the indicated sentinel value of `0` and will lead // to a runtime panic. var runtime_length: usize = 2; const slice = array[0..runtime_length :0]; @@ -3165,7 +3191,7 @@ test "linked list" { .last = &node, .len = 1, }; - + // When using a pointer to a struct, fields can be accessed directly, // without explicitly dereferencing the pointer. // So you can do @@ -3497,7 +3523,7 @@ fn dump(args: anytype) !void {

The fields are implicitly named using numbers starting from 0. Because their names are integers, - the {#syntax#}@"0"{#endsyntax#} syntax must be used to access them. Names inside {#syntax#}@""{#endsyntax#} are always recognised as identifiers. + the {#syntax#}@"0"{#endsyntax#} syntax must be used to access them. Names inside {#syntax#}@""{#endsyntax#} are always recognised as {#link|identifiers|Identifiers#}.

Like arrays, tuples have a .len field, can be indexed and work with the ++ and ** operators. They can also be iterated over with {#link|inline for#}. @@ -3986,7 +4012,7 @@ test "labeled break from labeled block expression" { {#see_also|Labeled while|Labeled for#} {#header_open|Shadowing#} -

Identifiers are never allowed to "hide" other identifiers by using the same name:

+

{#link|Identifiers#} are never allowed to "hide" other identifiers by using the same name:

{#code_begin|test_err|local shadows declaration#} const pi = 3.14; @@ -3998,8 +4024,8 @@ test "inside test block" { } {#code_end#}

- Because of this, when you read Zig code you can always rely on an identifier to consistently mean - the same thing within the scope it is defined. Note that you can, however, use the same name if + Because of this, when you read Zig code you can always rely on an identifier to consistently mean + the same thing within the scope it is defined. Note that you can, however, use the same name if the scopes are separate:

{#code_begin|test|test_scopes#} @@ -4037,7 +4063,7 @@ test "switch simple" { 1, 2, 3 => 0, // Ranges can be specified using the ... syntax. These are inclusive - // both ends. + // of both ends. 5...100 => 1, // Branches can be arbitrarily complex. @@ -4809,7 +4835,7 @@ test "errdefer unwinding" {

{#header_open|Basics#} {#code_begin|test|test_unreachable#} -// unreachable is used to assert that control flow will never happen upon a +// unreachable is used to assert that control flow will never reach a // particular location: test "basic math" { const x = 1; @@ -6777,8 +6803,7 @@ test "variable values" { generic data structure.

- Here is an example of a generic {#syntax#}List{#endsyntax#} data structure, that we will instantiate with - the type {#syntax#}i32{#endsyntax#}. In Zig we refer to the type as {#syntax#}List(i32){#endsyntax#}. + Here is an example of a generic {#syntax#}List{#endsyntax#} data structure.

{#code_begin|syntax#} fn List(comptime T: type) type { @@ -6787,27 +6812,46 @@ fn List(comptime T: type) type { len: usize, }; } - {#code_end#} -

- That's it. It's a function that returns an anonymous {#syntax#}struct{#endsyntax#}. For the purposes of error messages - and debugging, Zig infers the name {#syntax#}"List(i32)"{#endsyntax#} from the function name and parameters invoked when creating - the anonymous struct. -

-

- To keep the language small and uniform, all aggregate types in Zig are anonymous. To give a type - a name, we assign it to a constant: -

- {#code_begin|syntax#} -const Node = struct { - next: *Node, - name: []u8, + +// The generic List data structure can be instantiated by passing in a type: +var buffer: [10]i32 = undefined; +var list = List(i32){ + .items = &buffer, + .len = 0, }; {#code_end#}

- This works because all top level declarations are order-independent, and as long as there isn't - an actual infinite regression, values can refer to themselves, directly or indirectly. In this case, - {#syntax#}Node{#endsyntax#} refers to itself as a pointer, which is not actually an infinite regression, so - it works fine. + That's it. It's a function that returns an anonymous {#syntax#}struct{#endsyntax#}. + To keep the language small and uniform, all aggregate types in Zig are anonymous. + For the purposes of error messages and debugging, Zig infers the name + {#syntax#}"List(i32)"{#endsyntax#} from the function name and parameters invoked when creating + the anonymous struct. +

+

+ To explicitly give a type a name, we assign it to a constant. +

+ {#code_begin|syntax#} +const Node = struct { + next: ?*Node, + name: []const u8, +}; + +var node_a = Node{ + .next = null, + .name = &"Node A", +}; + +var node_b = Node{ + .next = &node_a, + .name = &"Node B", +}; + {#code_end#} +

+ In this example, the {#syntax#}Node{#endsyntax#} struct refers to itself. + This works because all top level declarations are order-independent. + As long as the compiler can determine the size of the struct, it is free to refer to itself. + In this case, {#syntax#}Node{#endsyntax#} refers to itself as a pointer, which has a + well-defined size at compile time, so it works fine.

{#header_close#} {#header_open|Case Study: print in Zig#} @@ -7220,10 +7264,10 @@ test "global assembly" { provided explicitly by the caller, and it can be suspended and resumed any number of times.

- The code following the {#syntax#}async{#endsyntax#} callsite runs immediately after the async - function first suspends. When the return value of the async function is needed, - the calling code can {#syntax#}await{#endsyntax#} on the async function frame. - This will suspend the calling code until the async function completes, at which point + The code following the {#syntax#}async{#endsyntax#} callsite runs immediately after the async + function first suspends. When the return value of the async function is needed, + the calling code can {#syntax#}await{#endsyntax#} on the async function frame. + This will suspend the calling code until the async function completes, at which point execution resumes just after the {#syntax#}await{#endsyntax#} callsite.

@@ -7333,8 +7377,8 @@ fn testResumeFromSuspend(my_result: *i32) void { in standard code.

- However, it is possible to have an {#syntax#}async{#endsyntax#} call - without a matching {#syntax#}await{#endsyntax#}. Upon completion of the async function, + However, it is possible to have an {#syntax#}async{#endsyntax#} call + without a matching {#syntax#}await{#endsyntax#}. Upon completion of the async function, execution would continue at the most recent {#syntax#}async{#endsyntax#} callsite or {#syntax#}resume{#endsyntax#} callsite, and the return value of the async function would be lost.

@@ -7371,8 +7415,8 @@ fn func() void {

{#syntax#}await{#endsyntax#} is a suspend point, and takes as an operand anything that - coerces to {#syntax#}anyframe->T{#endsyntax#}. Calling {#syntax#}await{#endsyntax#} on - the frame of an async function will cause execution to continue at the + coerces to {#syntax#}anyframe->T{#endsyntax#}. Calling {#syntax#}await{#endsyntax#} on + the frame of an async function will cause execution to continue at the {#syntax#}await{#endsyntax#} callsite once the target function completes.

@@ -8297,8 +8341,8 @@ fn internalName() callconv(.C) void {} {#code_begin|obj#} export fn foo() void {} {#code_end#} -

Note that even when using {#syntax#}export{#endsyntax#}, {#syntax#}@"foo"{#endsyntax#} syntax can - be used to choose any string for the symbol name:

+

Note that even when using {#syntax#}export{#endsyntax#}, the {#syntax#}@"foo"{#endsyntax#} syntax for + {#link|identifiers|Identifiers#} can be used to choose any string for the symbol name:

{#code_begin|obj#} export fn @"A function name that is a complete sentence."() void {} {#code_end#} @@ -8597,7 +8641,9 @@ test "integer cast panic" { {#header_open|@intToPtr#}
{#syntax#}@intToPtr(comptime DestType: type, address: usize) DestType{#endsyntax#}

- Converts an integer to a {#link|pointer|Pointers#}. To convert the other way, use {#link|@ptrToInt#}. + Converts an integer to a {#link|pointer|Pointers#}. To convert the other way, use {#link|@ptrToInt#}. Casting an address of 0 to a destination type + which in not {#link|optional|Optional Pointers#} and does not have the {#syntax#}allowzero{#endsyntax#} attribute will result in a + {#link|Pointer Cast Invalid Null#} panic when runtime safety checks are enabled.

If the destination pointer type does not allow address zero and {#syntax#}address{#endsyntax#} @@ -8711,7 +8757,8 @@ test "@wasmMemoryGrow" {

{#syntax#}@mod(numerator: T, denominator: T) T{#endsyntax#}

Modulus division. For unsigned integers this is the same as - {#syntax#}numerator % denominator{#endsyntax#}. Caller guarantees {#syntax#}denominator > 0{#endsyntax#}. + {#syntax#}numerator % denominator{#endsyntax#}. Caller guarantees {#syntax#}denominator > 0{#endsyntax#}, otherwise the + operation will result in a {#link|Remainder Division by Zero#} when runtime safety checks are enabled.

  • {#syntax#}@mod(-5, 3) == 1{#endsyntax#}
  • @@ -8729,7 +8776,7 @@ test "@wasmMemoryGrow" { If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.

    {#header_close#} - + {#header_open|@panic#}
    {#syntax#}@panic(message: []const u8) noreturn{#endsyntax#}

    @@ -8836,7 +8883,8 @@ pub const PrefetchOptions = struct {

    {#syntax#}@rem(numerator: T, denominator: T) T{#endsyntax#}

    Remainder division. For unsigned integers this is the same as - {#syntax#}numerator % denominator{#endsyntax#}. Caller guarantees {#syntax#}denominator > 0{#endsyntax#}. + {#syntax#}numerator % denominator{#endsyntax#}. Caller guarantees {#syntax#}denominator > 0{#endsyntax#}, otherwise the + operation will result in a {#link|Remainder Division by Zero#} when runtime safety checks are enabled.

    • {#syntax#}@rem(-5, 3) == -2{#endsyntax#}
    • @@ -8878,14 +8926,14 @@ pub const PrefetchOptions = struct { {#header_close#} {#header_open|@setCold#} -
      {#syntax#}@setCold(is_cold: bool){#endsyntax#}
      +
      {#syntax#}@setCold(comptime is_cold: bool){#endsyntax#}

      Tells the optimizer that a function is rarely called.

      {#header_close#} {#header_open|@setEvalBranchQuota#} -
      {#syntax#}@setEvalBranchQuota(new_quota: u32){#endsyntax#}
      +
      {#syntax#}@setEvalBranchQuota(comptime new_quota: u32){#endsyntax#}

      Changes the maximum number of backwards branches that compile-time code execution can use before giving up and making a compile error. @@ -8920,7 +8968,7 @@ test "foo" { {#header_close#} {#header_open|@setFloatMode#} -

      {#syntax#}@setFloatMode(mode: @import("std").builtin.FloatMode){#endsyntax#}
      +
      {#syntax#}@setFloatMode(comptime mode: @import("std").builtin.FloatMode){#endsyntax#}

      Sets the floating point mode of the current scope. Possible values are:

      @@ -8955,7 +9003,7 @@ pub const FloatMode = enum { {#header_close#} {#header_open|@setRuntimeSafety#} -
      {#syntax#}@setRuntimeSafety(safety_on: bool) void{#endsyntax#}
      +
      {#syntax#}@setRuntimeSafety(comptime safety_on: bool) void{#endsyntax#}

      Sets whether runtime safety checks are enabled for the scope that contains the function call.

      @@ -9016,7 +9064,7 @@ test "@setRuntimeSafety" {

      {#see_also|@shlExact|@shrExact#} {#header_close#} - + {#header_open|@shrExact#}
      {#syntax#}@shrExact(value: T, shift_amt: Log2T) T{#endsyntax#}

      @@ -9347,7 +9395,7 @@ fn doTheTest() !void { If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.

      {#header_close#} - + {#header_open|@tagName#}
      {#syntax#}@tagName(value: anytype) [:0]const u8{#endsyntax#}

      From 04f379dd414184a42412f4497b0573d7612d6730 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Thu, 3 Feb 2022 20:31:01 +0100 Subject: [PATCH 0029/2031] stage2 ARM: optimize airSliceElemVal for elem_size 1 or 4 In these cases, the AIR inst can be lowered to only one ldr instruction. Also fixes shifts in arm.bits.Offset --- src/arch/arm/CodeGen.zig | 160 ++++++++++++++++++++------------------- src/arch/arm/bits.zig | 84 +++++++++++++------- 2 files changed, 139 insertions(+), 105 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index c7e80dbe24..3d334656a1 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1222,9 +1222,16 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { fn airSliceElemVal(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 result: { + + if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + const result: MCValue = result: { const slice_mcv = try self.resolveInst(bin_op.lhs); + // TODO optimize for the case where the index is a constant, + // i.e. index_mcv == .immediate + const index_mcv = try self.resolveInst(bin_op.rhs); + const index_is_register = index_mcv == .register; + const slice_ty = self.air.typeOf(bin_op.lhs); const elem_ty = slice_ty.childType(); const elem_size = elem_ty.abiSize(self.target.*); @@ -1232,12 +1239,8 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { var buf: Type.SlicePtrFieldTypeBuffer = undefined; const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); - // TODO optimize this for the case when elem_size is a power - // of two (includes elem_size == 1) - const offset_mcv = try self.genArmMulConstant(inst, bin_op.rhs, 1, @intCast(u32, elem_size)); - assert(offset_mcv == .register); // result of multiplication should always be register - self.register_manager.freezeRegs(&.{offset_mcv.register}); - defer self.register_manager.unfreezeRegs(&.{offset_mcv.register}); + if (index_is_register) self.register_manager.freezeRegs(&.{index_mcv.register}); + defer if (index_is_register) self.register_manager.unfreezeRegs(&.{index_mcv.register}); const base_mcv: MCValue = switch (slice_mcv) { .stack_offset => .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, slice_mcv) }, @@ -1246,61 +1249,67 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { self.register_manager.freezeRegs(&.{base_mcv.register}); defer self.register_manager.unfreezeRegs(&.{base_mcv.register}); - if (elem_size <= 4) { - const dst_reg = try self.register_manager.allocReg(inst); - self.register_manager.freezeRegs(&.{dst_reg}); - defer self.register_manager.unfreezeRegs(&.{dst_reg}); + switch (elem_size) { + 1, 4 => { + const dst_reg = try self.register_manager.allocReg(inst); + const dst_mcv = MCValue{ .register = dst_reg }; + self.register_manager.freezeRegs(&.{dst_reg}); + defer self.register_manager.unfreezeRegs(&.{dst_reg}); - switch (elem_size) { - 1, 4 => { - const tag: Mir.Inst.Tag = switch (elem_size) { - 1 => .ldrb, - 4 => .ldr, - else => unreachable, - }; + const index_reg: Register = switch (index_mcv) { + .register => |reg| reg, + else => try self.copyToTmpRegister(Type.usize, index_mcv), + }; + self.register_manager.freezeRegs(&.{index_reg}); + defer self.register_manager.unfreezeRegs(&.{index_reg}); - _ = try self.addInst(.{ - .tag = tag, - .data = .{ .rr_offset = .{ - .rt = dst_reg, - .rn = base_mcv.register, - .offset = .{ .offset = Instruction.Offset.reg(offset_mcv.register, 0) }, - } }, - }); - }, - 2 => { - _ = try self.addInst(.{ - .tag = .ldrh, - .data = .{ .rr_extra_offset = .{ - .rt = dst_reg, - .rn = base_mcv.register, - .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.reg(offset_mcv.register) }, - } }, - }); - }, - else => unreachable, - } + const tag: Mir.Inst.Tag = switch (elem_size) { + 1 => .ldrb, + 4 => .ldr, + else => unreachable, + }; + const shift: u5 = switch (elem_size) { + 1 => 0, + 4 => 2, + else => unreachable, + }; - break :result MCValue{ .register = dst_reg }; - } else { - const dst_mcv = try self.allocRegOrMem(inst, false); + _ = try self.addInst(.{ + .tag = tag, + .data = .{ .rr_offset = .{ + .rt = dst_reg, + .rn = base_mcv.register, + .offset = .{ .offset = Instruction.Offset.reg(index_reg, .{ .lsl = shift }) }, + } }, + }); - const addr_reg = try self.register_manager.allocReg(null); - self.register_manager.freezeRegs(&.{addr_reg}); - defer self.register_manager.unfreezeRegs(&.{addr_reg}); + break :result dst_mcv; + }, + else => { + const dst_mcv = try self.allocRegOrMem(inst, true); - try self.genArmBinOpCode(addr_reg, base_mcv, offset_mcv, false, .add, .unsigned); + const offset_mcv = try self.genArmMulConstant(bin_op.rhs, @intCast(u32, elem_size)); + assert(offset_mcv == .register); // result of multiplication should always be register + self.register_manager.freezeRegs(&.{offset_mcv.register}); + defer self.register_manager.unfreezeRegs(&.{offset_mcv.register}); - // I know we will unfreeze these registers at the end of - // the scope of :result. However, at this point in time, - // neither the base register nor the offset register - // contains any valuable data anymore. In order to reduce - // register pressure, unfreeze them prematurely - self.register_manager.unfreezeRegs(&.{ base_mcv.register, offset_mcv.register }); + const addr_reg = try self.register_manager.allocReg(null); + self.register_manager.freezeRegs(&.{addr_reg}); + defer self.register_manager.unfreezeRegs(&.{addr_reg}); - try self.load(dst_mcv, .{ .register = addr_reg }, slice_ptr_field_type); + try self.genArmBinOpCode(addr_reg, base_mcv, offset_mcv, false, .add, .unsigned); - break :result dst_mcv; + // I know we will unfreeze these registers at the end of + // the scope of :result. However, at this point in time, + // neither the base register nor the offset register + // contains any valuable data anymore. In order to reduce + // register pressure, unfreeze them prematurely + self.register_manager.unfreezeRegs(&.{ base_mcv.register, offset_mcv.register }); + + try self.load(dst_mcv, .{ .register = addr_reg }, slice_ptr_field_type); + + break :result dst_mcv; + }, } }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); @@ -1931,8 +1940,8 @@ fn genArmBinOpCode( .shl, .shr => { assert(!swap_lhs_and_rhs); const shift_amount = switch (operand) { - .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)), - .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)), + .register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)), + .immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)), }; const tag: Mir.Inst.Tag = switch (op) { @@ -2036,12 +2045,11 @@ fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Ai return dst_mcv; } -fn genArmMulConstant(self: *Self, inst: Air.Inst.Index, op: Air.Inst.Ref, op_index: Liveness.OperandInt, imm: u32) !MCValue { +fn genArmMulConstant(self: *Self, op: Air.Inst.Ref, imm: u32) !MCValue { const lhs = try self.resolveInst(op); const rhs = MCValue{ .immediate = imm }; const lhs_is_register = lhs == .register; - const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op, op_index, lhs); if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register}); @@ -2054,23 +2062,17 @@ fn genArmMulConstant(self: *Self, inst: Air.Inst.Index, op: Air.Inst.Ref, op_ind var rhs_mcv: MCValue = rhs; // Allocate registers for operands and/or destination - if (reuse_lhs) { - // Allocate 1 register - rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(null) }; - dst_mcv = lhs; + // Allocate 1 or 2 registers + if (lhs_is_register) { + // Move RHS to register + dst_mcv = MCValue{ .register = try self.register_manager.allocReg(null) }; + rhs_mcv = dst_mcv; } else { - // Allocate 1 or 2 registers - if (lhs_is_register) { - // Move RHS to register - dst_mcv = MCValue{ .register = try self.register_manager.allocReg(null) }; - rhs_mcv = dst_mcv; - } else { - // Move LHS and RHS to register - const regs = try self.register_manager.allocRegs(2, .{ null, null }); - lhs_mcv = MCValue{ .register = regs[0] }; - rhs_mcv = MCValue{ .register = regs[1] }; - dst_mcv = lhs_mcv; - } + // Move LHS and RHS to register + const regs = try self.register_manager.allocRegs(2, .{ null, null }); + lhs_mcv = MCValue{ .register = regs[0] }; + rhs_mcv = MCValue{ .register = regs[1] }; + dst_mcv = lhs_mcv; } // Move the operands to the newly allocated registers @@ -2132,7 +2134,7 @@ fn genArmInlineMemcpy( .data = .{ .rr_offset = .{ .rt = tmp, .rn = src, - .offset = .{ .offset = Instruction.Offset.reg(count, 0) }, + .offset = .{ .offset = Instruction.Offset.reg(count, .none) }, } }, }); @@ -2142,7 +2144,7 @@ fn genArmInlineMemcpy( .data = .{ .rr_offset = .{ .rt = tmp, .rn = dst, - .offset = .{ .offset = Instruction.Offset.reg(count, 0) }, + .offset = .{ .offset = Instruction.Offset.reg(count, .none) }, } }, }); @@ -3126,7 +3128,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro 1, 4 => { const offset = if (math.cast(u12, adj_off)) |imm| blk: { break :blk Instruction.Offset.imm(imm); - } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0); + } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), .none); const tag: Mir.Inst.Tag = switch (abi_size) { 1 => .strb, @@ -3450,7 +3452,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void 1, 4 => { const offset = if (adj_off <= math.maxInt(u12)) blk: { break :blk Instruction.Offset.imm(@intCast(u12, adj_off)); - } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0); + } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), .none); const tag: Mir.Inst.Tag = switch (abi_size) { 1 => .ldrb, @@ -3536,7 +3538,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I 1, 4 => { const offset = if (math.cast(u12, adj_off)) |imm| blk: { break :blk Instruction.Offset.imm(imm); - } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0); + } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), .none); const tag: Mir.Inst.Tag = switch (abi_size) { 1 => .strb, diff --git a/src/arch/arm/bits.zig b/src/arch/arm/bits.zig index 279ce58005..792bf0dc05 100644 --- a/src/arch/arm/bits.zig +++ b/src/arch/arm/bits.zig @@ -343,11 +343,11 @@ pub const Instruction = union(enum) { /// which can either be content from a register or an immediate /// value pub const Operand = union(enum) { - Register: packed struct { + register: packed struct { rm: u4, shift: u8, }, - Immediate: packed struct { + immediate: packed struct { imm: u8, rotate: u4, }, @@ -356,12 +356,12 @@ pub const Instruction = union(enum) { /// register can be shifted by a specific immediate value or /// by the contents of another register pub const Shift = union(enum) { - Immediate: packed struct { + immediate: packed struct { fixed: u1 = 0b0, typ: u2, amount: u5, }, - Register: packed struct { + register: packed struct { fixed_1: u1 = 0b1, typ: u2, fixed_2: u1 = 0b0, @@ -376,7 +376,7 @@ pub const Instruction = union(enum) { }; pub const none = Shift{ - .Immediate = .{ + .immediate = .{ .amount = 0, .typ = 0, }, @@ -384,14 +384,14 @@ pub const Instruction = union(enum) { pub fn toU8(self: Shift) u8 { return switch (self) { - .Register => |v| @bitCast(u8, v), - .Immediate => |v| @bitCast(u8, v), + .register => |v| @bitCast(u8, v), + .immediate => |v| @bitCast(u8, v), }; } pub fn reg(rs: Register, typ: Type) Shift { return Shift{ - .Register = .{ + .register = .{ .rs = rs.id(), .typ = @enumToInt(typ), }, @@ -400,7 +400,7 @@ pub const Instruction = union(enum) { pub fn imm(amount: u5, typ: Type) Shift { return Shift{ - .Immediate = .{ + .immediate = .{ .amount = amount, .typ = @enumToInt(typ), }, @@ -410,14 +410,14 @@ pub const Instruction = union(enum) { pub fn toU12(self: Operand) u12 { return switch (self) { - .Register => |v| @bitCast(u12, v), - .Immediate => |v| @bitCast(u12, v), + .register => |v| @bitCast(u12, v), + .immediate => |v| @bitCast(u12, v), }; } pub fn reg(rm: Register, shift: Shift) Operand { return Operand{ - .Register = .{ + .register = .{ .rm = rm.id(), .shift = shift.toU8(), }, @@ -426,7 +426,7 @@ pub const Instruction = union(enum) { pub fn imm(immediate: u8, rotate: u4) Operand { return Operand{ - .Immediate = .{ + .immediate = .{ .imm = immediate, .rotate = rotate, }, @@ -447,7 +447,7 @@ pub const Instruction = union(enum) { return for (masks) |mask, i| { if (x & mask == x) { break Operand{ - .Immediate = .{ + .immediate = .{ .imm = @intCast(u8, std.math.rotl(u32, x, 2 * i)), .rotate = @intCast(u4, i), }, @@ -461,35 +461,67 @@ pub const Instruction = union(enum) { /// instruction. Data can be loaded from memory with either an /// immediate offset or an offset that is stored in some register. pub const Offset = union(enum) { - Immediate: u12, - Register: packed struct { + immediate: u12, + register: packed struct { rm: u4, - shift: u8, + fixed: u1 = 0b0, + stype: u2, + imm5: u5, }, + pub const Shift = union(enum) { + /// No shift + none, + /// Logical shift left + lsl: u5, + /// Logical shift right + lsr: u5, + /// Arithmetic shift right + asr: u5, + /// Rotate right + ror: u5, + /// Rotate right one bit, with extend + rrx, + }; + pub const none = Offset{ - .Immediate = 0, + .immediate = 0, }; pub fn toU12(self: Offset) u12 { return switch (self) { - .Register => |v| @bitCast(u12, v), - .Immediate => |v| v, + .register => |v| @bitCast(u12, v), + .immediate => |v| v, }; } - pub fn reg(rm: Register, shift: u8) Offset { + pub fn reg(rm: Register, shift: Shift) Offset { return Offset{ - .Register = .{ + .register = .{ .rm = rm.id(), - .shift = shift, + .stype = switch (shift) { + .none => 0b00, + .lsl => 0b00, + .lsr => 0b01, + .asr => 0b10, + .ror => 0b11, + .rrx => 0b11, + }, + .imm5 = switch (shift) { + .none => 0, + .lsl => |n| n, + .lsr => |n| n, + .asr => |n| n, + .ror => |n| n, + .rrx => 0, + }, }, }; } pub fn imm(immediate: u12) Offset { return Offset{ - .Immediate = immediate, + .immediate = immediate, }; } }; @@ -567,7 +599,7 @@ pub const Instruction = union(enum) { return Instruction{ .data_processing = .{ .cond = @enumToInt(cond), - .i = @boolToInt(op2 == .Immediate), + .i = @boolToInt(op2 == .immediate), .opcode = @enumToInt(opcode), .s = s, .rn = rn.id(), @@ -681,7 +713,7 @@ pub const Instruction = union(enum) { .byte_word = byte_word, .up_down = @boolToInt(positive), .pre_post = @boolToInt(pre_index), - .imm = @boolToInt(offset != .Immediate), + .imm = @boolToInt(offset != .immediate), }, }; } From 5c4ef1a64ca71c2a43e362a5ad29a10bd880716c Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 20 Jan 2022 13:50:30 +0200 Subject: [PATCH 0030/2031] compiler-rt: add extend functions for f80 --- lib/std/math.zig | 2 +- lib/std/special/compiler_rt.zig | 12 +- lib/std/special/compiler_rt/extendXfYf2.zig | 5 - lib/std/special/compiler_rt/extend_f80.zig | 131 ++++++++++++++++++++ 4 files changed, 141 insertions(+), 9 deletions(-) create mode 100644 lib/std/special/compiler_rt/extend_f80.zig diff --git a/lib/std/math.zig b/lib/std/math.zig index 59532d7ab2..43ad49889d 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -43,7 +43,7 @@ pub const f128_max = @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000)); pub const f128_toint = 1.0 / f128_epsilon; -const F80Repr = if (@import("builtin").cpu.arch.endian() == .Little) extern struct { +pub const F80Repr = if (@import("builtin").cpu.arch.endian() == .Little) extern struct { fraction: u64, exp: u16, } else extern struct { diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 24bca128de..acb0d13baf 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -39,6 +39,15 @@ comptime { const __extendhftf2 = @import("compiler_rt/extendXfYf2.zig").__extendhftf2; @export(__extendhftf2, .{ .name = "__extendhftf2", .linkage = linkage }); + const __extendhfxf2 = @import("compiler_rt/extend_f80.zig").__extendhfxf2; + @export(__extendhfxf2, .{ .name = "__extendhfxf2", .linkage = linkage }); + const __extendffxf2 = @import("compiler_rt/extend_f80.zig").__extendffxf2; + @export(__extendffxf2, .{ .name = "__extendffxf2", .linkage = linkage }); + const __extenddfxf2 = @import("compiler_rt/extend_f80.zig").__extenddfxf2; + @export(__extenddfxf2, .{ .name = "__extenddfxf2", .linkage = linkage }); + const __extendxftf2 = @import("compiler_rt/extend_f80.zig").__extendxftf2; + @export(__extendxftf2, .{ .name = "__extendxftf2", .linkage = linkage }); + const __lesf2 = @import("compiler_rt/compareXf2.zig").__lesf2; @export(__lesf2, .{ .name = "__lesf2", .linkage = linkage }); const __ledf2 = @import("compiler_rt/compareXf2.zig").__ledf2; @@ -181,9 +190,6 @@ comptime { if (!long_double_is_f128) { // TODO implement these - //const __extendxftf2 = @import("compiler_rt/extendXfYf2.zig").__extendxftf2; - //@export(__extendxftf2, .{ .name = "__extendxftf2", .linkage = linkage }); - //const __trunctfxf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfxf2; //@export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage }); } diff --git a/lib/std/special/compiler_rt/extendXfYf2.zig b/lib/std/special/compiler_rt/extendXfYf2.zig index 2c3f0c88fc..8622fe1513 100644 --- a/lib/std/special/compiler_rt/extendXfYf2.zig +++ b/lib/std/special/compiler_rt/extendXfYf2.zig @@ -27,11 +27,6 @@ pub fn __extendhftf2(a: F16T) callconv(.C) f128 { return extendXfYf2(f128, f16, @bitCast(u16, a)); } -pub fn __extendxftf2(a: c_longdouble) callconv(.C) f128 { - _ = a; - @panic("TODO implement"); -} - pub fn __aeabi_h2f(arg: u16) callconv(.AAPCS) f32 { @setRuntimeSafety(false); return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f32, f16, arg }); diff --git a/lib/std/special/compiler_rt/extend_f80.zig b/lib/std/special/compiler_rt/extend_f80.zig new file mode 100644 index 0000000000..29ba8560ce --- /dev/null +++ b/lib/std/special/compiler_rt/extend_f80.zig @@ -0,0 +1,131 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const is_test = builtin.is_test; +const native_arch = builtin.cpu.arch; + +// AArch64 is the only ABI (at the moment) to support f16 arguments without the +// need for extending them to wider fp types. +pub const F16T = if (native_arch.isAARCH64()) f16 else u16; + +pub fn __extendhfxf2(a: F16T) callconv(.C) f80 { + return extendF80(f16, @bitCast(u16, a)); +} + +pub fn __extendffxf2(a: f32) callconv(.C) f80 { + return extendF80(f32, @bitCast(u32, a)); +} + +pub fn __extenddfxf2(a: f64) callconv(.C) f80 { + return extendF80(f64, @bitCast(u64, a)); +} + +inline fn extendF80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits)) f80 { + @setRuntimeSafety(builtin.is_test); + + const src_rep_t = std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits); + const src_sig_bits = std.math.floatMantissaBits(src_t); + const dst_int_bit = 0x8000000000000000; + const dst_sig_bits = std.math.floatMantissaBits(f80) - 1; // -1 for the integer bit + + const dst_exp_bias = 16383; + + const src_bits = @bitSizeOf(src_t); + const src_exp_bits = src_bits - src_sig_bits - 1; + const src_inf_exp = (1 << src_exp_bits) - 1; + const src_exp_bias = src_inf_exp >> 1; + + const src_min_normal = 1 << src_sig_bits; + const src_inf = src_inf_exp << src_sig_bits; + const src_sign_mask = 1 << (src_sig_bits + src_exp_bits); + const src_abs_mask = src_sign_mask - 1; + const src_qnan = 1 << (src_sig_bits - 1); + const src_nan_code = src_qnan - 1; + + var dst: std.math.F80Repr align(16) = undefined; + + // Break a into a sign and representation of the absolute value + const a_abs = a & src_abs_mask; + const sign: u16 = if (a & src_sign_mask != 0) 0x8000 else 0; + + if (a_abs -% src_min_normal < src_inf - src_min_normal) { + // a is a normal number. + // Extend to the destination type by shifting the significand and + // exponent into the proper position and rebiasing the exponent. + dst.exp = @intCast(u16, a_abs >> src_sig_bits); + dst.exp += dst_exp_bias - src_exp_bias; + dst.fraction = @as(u64, a_abs) << (dst_sig_bits - src_sig_bits); + dst.fraction |= dst_int_bit; // bit 64 is always set for normal numbers + } else if (a_abs >= src_inf) { + // a is NaN or infinity. + // Conjure the result by beginning with infinity, then setting the qNaN + // bit (if needed) and right-aligning the rest of the trailing NaN + // payload field. + dst.exp = 0x7fff; + dst.fraction = dst_int_bit; + dst.fraction |= @as(u64, a_abs & src_qnan) << (dst_sig_bits - src_sig_bits); + dst.fraction |= @as(u64, a_abs & src_nan_code) << (dst_sig_bits - src_sig_bits); + } else if (a_abs != 0) { + // a is denormal. + // renormalize the significand and clear the leading bit, then insert + // the correct adjusted exponent in the destination type. + const scale: u16 = @clz(src_rep_t, a_abs) - + @clz(src_rep_t, @as(src_rep_t, src_min_normal)); + + dst.fraction = @as(u64, a_abs) << @intCast(u6, dst_sig_bits - src_sig_bits + scale); + dst.fraction |= dst_int_bit; // bit 64 is always set for normal numbers + dst.exp = @truncate(u16, a_abs >> @intCast(u4, src_sig_bits - scale)); + dst.exp ^= 1; + dst.exp |= dst_exp_bias - src_exp_bias - scale + 1; + } else { + // a is zero. + dst.exp = 0; + dst.fraction = 0; + } + + dst.exp |= sign; + return @ptrCast(*const f80, &dst).*; +} + +pub fn __extendxftf2(a: f80) callconv(.C) f128 { + @setRuntimeSafety(builtin.is_test); + + const src_int_bit: u64 = 0x8000000000000000; + const src_sig_mask = ~src_int_bit; + const src_sig_bits = std.math.floatMantissaBits(f80) - 1; // -1 for the integer bit + const dst_sig_bits = std.math.floatMantissaBits(f128); + + const dst_bits = @bitSizeOf(f128); + + const dst_min_normal = @as(u128, 1) << dst_sig_bits; + + // Break a into a sign and representation of the absolute value + var a_rep = @ptrCast(*const std.math.F80Repr, &a).*; + const sign = a_rep.exp & 0x8000; + a_rep.exp &= 0x7FFF; + var abs_result: u128 = undefined; + + if (a_rep.exp == 0 and a_rep.fraction == 0) { + // zero + abs_result = 0; + } else if (a_rep.exp == 0x7FFF) { + // a is nan or infinite + abs_result = @as(u128, a_rep.fraction) << (dst_sig_bits - src_sig_bits); + abs_result |= @as(u128, a_rep.exp) << dst_sig_bits; + } else if (a_rep.fraction & src_int_bit != 0) { + // a is a normal value + abs_result = @as(u128, a_rep.fraction & src_sig_mask) << (dst_sig_bits - src_sig_bits); + abs_result |= @as(u128, a_rep.exp) << dst_sig_bits; + } else { + // a is denormal + // renormalize the significand and clear the leading bit and integer part, + // then insert the correct adjusted exponent in the destination type. + const scale: u32 = @clz(u64, a_rep.fraction); + abs_result = @as(u128, a_rep.fraction) << @intCast(u7, dst_sig_bits - src_sig_bits + scale + 1); + abs_result ^= dst_min_normal; + abs_result |= @as(u128, scale + 1) << dst_sig_bits; + } + + // Apply the signbit to (dst_t)abs(a). + const result: u128 align(@alignOf(f128)) = abs_result | @as(u128, sign) << (dst_bits - 16); + return @bitCast(f128, result); +} From 72cef17b1a23c4704b3931540b7f10f4297870b9 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 21 Jan 2022 12:41:09 +0200 Subject: [PATCH 0031/2031] compiler-rt: add trunc functions for f80 --- lib/std/special/compiler_rt.zig | 13 +- lib/std/special/compiler_rt/truncXfYf2.zig | 5 - lib/std/special/compiler_rt/trunc_f80.zig | 159 +++++++++++++++++++++ 3 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 lib/std/special/compiler_rt/trunc_f80.zig diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index acb0d13baf..555a7a49d3 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -188,11 +188,14 @@ comptime { const __truncdfsf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfsf2; @export(__truncdfsf2, .{ .name = "__truncdfsf2", .linkage = linkage }); - if (!long_double_is_f128) { - // TODO implement these - //const __trunctfxf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfxf2; - //@export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage }); - } + const __truncxfhf2 = @import("compiler_rt/trunc_f80.zig").__truncxfhf2; + @export(__truncxfhf2, .{ .name = "__truncxfhf2", .linkage = linkage }); + const __truncxfff2 = @import("compiler_rt/trunc_f80.zig").__truncxfff2; + @export(__truncxfff2, .{ .name = "__truncxfff2", .linkage = linkage }); + const __truncxfdf2 = @import("compiler_rt/trunc_f80.zig").__truncxfdf2; + @export(__truncxfdf2, .{ .name = "__truncxfdf2", .linkage = linkage }); + const __trunctfxf2 = @import("compiler_rt/trunc_f80.zig").__trunctfxf2; + @export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage }); if (builtin.zig_backend == .stage1) { switch (arch) { diff --git a/lib/std/special/compiler_rt/truncXfYf2.zig b/lib/std/special/compiler_rt/truncXfYf2.zig index 4cded15abc..fea1aeb60a 100644 --- a/lib/std/special/compiler_rt/truncXfYf2.zig +++ b/lib/std/special/compiler_rt/truncXfYf2.zig @@ -26,11 +26,6 @@ pub fn __trunctfdf2(a: f128) callconv(.C) f64 { return truncXfYf2(f64, f128, a); } -pub fn __trunctfxf2(a: f128) callconv(.C) c_longdouble { - _ = a; - @panic("TODO implement"); -} - pub fn __truncdfsf2(a: f64) callconv(.C) f32 { return truncXfYf2(f32, f64, a); } diff --git a/lib/std/special/compiler_rt/trunc_f80.zig b/lib/std/special/compiler_rt/trunc_f80.zig new file mode 100644 index 0000000000..567d03be63 --- /dev/null +++ b/lib/std/special/compiler_rt/trunc_f80.zig @@ -0,0 +1,159 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const native_arch = builtin.cpu.arch; + +// AArch64 is the only ABI (at the moment) to support f16 arguments without the +// need for extending them to wider fp types. +pub const F16T = if (native_arch.isAARCH64()) f16 else u16; + +pub fn __truncxfhf2(a: f80) callconv(.C) F16T { + return @bitCast(F16T, trunc(f16, a)); +} + +pub fn __truncxfff2(a: f80) callconv(.C) f32 { + return trunc(f32, a); +} + +pub fn __truncxfdf2(a: f80) callconv(.C) f64 { + return trunc(f64, a); +} + +inline fn trunc(comptime dst_t: type, a: f80) dst_t { + @setRuntimeSafety(builtin.is_test); + + const dst_rep_t = std.meta.Int(.unsigned, @typeInfo(dst_t).Float.bits); + const src_sig_bits = std.math.floatMantissaBits(f80) - 1; // -1 for the integer bit + const dst_sig_bits = std.math.floatMantissaBits(dst_t); + + const src_exp_bias = 16383; + + const round_mask = (1 << (src_sig_bits - dst_sig_bits)) - 1; + const halfway = 1 << (src_sig_bits - dst_sig_bits - 1); + + const dst_bits = @typeInfo(dst_t).Float.bits; + const dst_exp_bits = dst_bits - dst_sig_bits - 1; + const dst_inf_exp = (1 << dst_exp_bits) - 1; + const dst_exp_bias = dst_inf_exp >> 1; + + const underflow = src_exp_bias + 1 - dst_exp_bias; + const overflow = src_exp_bias + dst_inf_exp - dst_exp_bias; + + const dst_qnan = 1 << (dst_sig_bits - 1); + const dst_nan_mask = dst_qnan - 1; + + // Break a into a sign and representation of the absolute value + var a_rep = @ptrCast(*const std.math.F80Repr, &a).*; + const sign = a_rep.exp & 0x8000; + a_rep.exp &= 0x7FFF; + a_rep.fraction &= 0x7FFFFFFFFFFFFFFF; + var abs_result: dst_rep_t = undefined; + + if (a_rep.exp -% underflow < a_rep.exp -% overflow) { + // The exponent of a is within the range of normal numbers in the + // destination format. We can convert by simply right-shifting with + // rounding and adjusting the exponent. + abs_result = @as(dst_rep_t, a_rep.exp) << dst_sig_bits; + abs_result |= @truncate(dst_rep_t, a_rep.fraction >> (src_sig_bits - dst_sig_bits)); + abs_result -%= @as(dst_rep_t, src_exp_bias - dst_exp_bias) << dst_sig_bits; + + const round_bits = a_rep.fraction & round_mask; + if (round_bits > halfway) { + // Round to nearest + abs_result += 1; + } else if (round_bits == halfway) { + // Ties to even + abs_result += abs_result & 1; + } + } else if (a_rep.exp == 0x7FFF and a_rep.fraction != 0) { + // a is NaN. + // Conjure the result by beginning with infinity, setting the qNaN + // bit and inserting the (truncated) trailing NaN field. + abs_result = @intCast(dst_rep_t, dst_inf_exp) << dst_sig_bits; + abs_result |= dst_qnan; + abs_result |= @intCast(dst_rep_t, (a_rep.fraction >> (src_sig_bits - dst_sig_bits)) & dst_nan_mask); + } else if (a_rep.exp >= overflow) { + // a overflows to infinity. + abs_result = @intCast(dst_rep_t, dst_inf_exp) << dst_sig_bits; + } else { + // a underflows on conversion to the destination type or is an exact + // zero. The result may be a denormal or zero. Extract the exponent + // to get the shift amount for the denormalization. + const shift = src_exp_bias - dst_exp_bias - a_rep.exp; + + // Right shift by the denormalization amount with sticky. + if (shift > src_sig_bits) { + abs_result = 0; + } else { + const sticky = @boolToInt(a_rep.fraction << @intCast(u6, shift) != 0); + const denormalized_significand = a_rep.fraction >> @intCast(u6, shift) | sticky; + abs_result = @intCast(dst_rep_t, denormalized_significand >> (src_sig_bits - dst_sig_bits)); + const round_bits = denormalized_significand & round_mask; + if (round_bits > halfway) { + // Round to nearest + abs_result += 1; + } else if (round_bits == halfway) { + // Ties to even + abs_result += abs_result & 1; + } + } + } + + const result align(@alignOf(dst_t)) = abs_result | @as(dst_rep_t, sign) << dst_bits - 16; + return @bitCast(dst_t, result); +} + +pub fn __trunctfxf2(a: f128) callconv(.C) f80 { + const src_sig_bits = std.math.floatMantissaBits(f128); + const dst_sig_bits = std.math.floatMantissaBits(f80) - 1; // -1 for the integer bit + + // Various constants whose values follow from the type parameters. + // Any reasonable optimizer will fold and propagate all of these. + const src_bits = @typeInfo(f128).Float.bits; + const src_exp_bits = src_bits - src_sig_bits - 1; + const src_inf_exp = 0x7FFF; + + const src_inf = src_inf_exp << src_sig_bits; + const src_sign_mask = 1 << (src_sig_bits + src_exp_bits); + const src_abs_mask = src_sign_mask - 1; + const round_mask = (1 << (src_sig_bits - dst_sig_bits)) - 1; + const halfway = 1 << (src_sig_bits - dst_sig_bits - 1); + const src_qnan = 1 << (src_sig_bits - 1); + const src_nan_mask = src_qnan - 1; + + // Break a into a sign and representation of the absolute value + const a_rep = @bitCast(u128, a); + const a_abs = a_rep & src_abs_mask; + const sign: u16 = if (a_rep & src_sign_mask != 0) 0x8000 else 0; + + var res: std.math.F80Repr align(16) = undefined; + + if (a_abs > src_inf) { + // a is NaN. + // Conjure the result by beginning with infinity, setting the qNaN + // bit and inserting the (truncated) trailing NaN field. + res.exp = 0x7fff; + res.fraction = 0x8000000000000000; + res.fraction |= @truncate(u64, (a_abs & src_qnan) << (src_sig_bits - dst_sig_bits)); + res.fraction |= @truncate(u64, (a_abs & src_nan_mask) << (src_sig_bits - dst_sig_bits)); + } else { + // The exponent of a is within the range of normal numbers in the + // destination format. We can convert by simply right-shifting with + // rounding and adjusting the exponent. + res.fraction = @truncate(u64, a_abs >> (src_sig_bits - dst_sig_bits)); + res.exp = @truncate(u16, a_abs >> src_sig_bits); + + const round_bits = a_abs & round_mask; + if (round_bits > halfway) { + // Round to nearest + const exp = @addWithOverflow(u64, res.fraction, 1, &res.fraction); + res.exp += @boolToInt(exp); + } else if (round_bits == halfway) { + // Ties to even + const exp = @addWithOverflow(u64, res.fraction, res.fraction & 1, &res.fraction); + res.exp += @boolToInt(exp); + } + } + + res.exp |= sign; + return @ptrCast(*const f80, &res).*; +} From 9bbd3ab257137c97f695d187436e14c622f877c8 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 21 Jan 2022 15:26:43 +0200 Subject: [PATCH 0032/2031] compiler-rt: add comparison functions for f80 --- lib/std/special/compiler_rt.zig | 12 ++++ lib/std/special/compiler_rt/compareXf2.zig | 67 ++++++++++++++++++++++ src/stage1/codegen.cpp | 62 +++++++++++++++++++- 3 files changed, 140 insertions(+), 1 deletion(-) diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 555a7a49d3..d83e94be8f 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -54,6 +54,8 @@ comptime { @export(__ledf2, .{ .name = "__ledf2", .linkage = linkage }); const __letf2 = @import("compiler_rt/compareXf2.zig").__letf2; @export(__letf2, .{ .name = "__letf2", .linkage = linkage }); + const __lexf2 = @import("compiler_rt/compareXf2.zig").__lexf2; + @export(__lexf2, .{ .name = "__lexf2", .linkage = linkage }); const __gesf2 = @import("compiler_rt/compareXf2.zig").__gesf2; @export(__gesf2, .{ .name = "__gesf2", .linkage = linkage }); @@ -61,26 +63,36 @@ comptime { @export(__gedf2, .{ .name = "__gedf2", .linkage = linkage }); const __getf2 = @import("compiler_rt/compareXf2.zig").__getf2; @export(__getf2, .{ .name = "__getf2", .linkage = linkage }); + const __gexf2 = @import("compiler_rt/compareXf2.zig").__gexf2; + @export(__gexf2, .{ .name = "__gexf2", .linkage = linkage }); const __eqsf2 = @import("compiler_rt/compareXf2.zig").__eqsf2; @export(__eqsf2, .{ .name = "__eqsf2", .linkage = linkage }); const __eqdf2 = @import("compiler_rt/compareXf2.zig").__eqdf2; @export(__eqdf2, .{ .name = "__eqdf2", .linkage = linkage }); + const __eqxf2 = @import("compiler_rt/compareXf2.zig").__eqxf2; + @export(__eqxf2, .{ .name = "__eqxf2", .linkage = linkage }); const __ltsf2 = @import("compiler_rt/compareXf2.zig").__ltsf2; @export(__ltsf2, .{ .name = "__ltsf2", .linkage = linkage }); const __ltdf2 = @import("compiler_rt/compareXf2.zig").__ltdf2; @export(__ltdf2, .{ .name = "__ltdf2", .linkage = linkage }); + const __ltxf2 = @import("compiler_rt/compareXf2.zig").__ltxf2; + @export(__ltxf2, .{ .name = "__ltxf2", .linkage = linkage }); const __nesf2 = @import("compiler_rt/compareXf2.zig").__nesf2; @export(__nesf2, .{ .name = "__nesf2", .linkage = linkage }); const __nedf2 = @import("compiler_rt/compareXf2.zig").__nedf2; @export(__nedf2, .{ .name = "__nedf2", .linkage = linkage }); + const __nexf2 = @import("compiler_rt/compareXf2.zig").__nexf2; + @export(__nexf2, .{ .name = "__nexf2", .linkage = linkage }); const __gtsf2 = @import("compiler_rt/compareXf2.zig").__gtsf2; @export(__gtsf2, .{ .name = "__gtsf2", .linkage = linkage }); const __gtdf2 = @import("compiler_rt/compareXf2.zig").__gtdf2; @export(__gtdf2, .{ .name = "__gtdf2", .linkage = linkage }); + const __gtxf2 = @import("compiler_rt/compareXf2.zig").__gtxf2; + @export(__gtxf2, .{ .name = "__gtxf2", .linkage = linkage }); if (!is_test) { @export(__lesf2, .{ .name = "__cmpsf2", .linkage = linkage }); diff --git a/lib/std/special/compiler_rt/compareXf2.zig b/lib/std/special/compiler_rt/compareXf2.zig index 9f3750094e..36f6f5f1c1 100644 --- a/lib/std/special/compiler_rt/compareXf2.zig +++ b/lib/std/special/compiler_rt/compareXf2.zig @@ -144,6 +144,73 @@ pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 { return __gedf2(a, b); } +// Comparison between f80 + +pub inline fn cmp_f80(comptime RT: type, a: f80, b: f80) RT { + const a_rep = @ptrCast(*const std.math.F80Repr, &a).*; + const b_rep = @ptrCast(*const std.math.F80Repr, &b).*; + const sig_bits = std.math.floatMantissaBits(f80); + const int_bit = 0x8000000000000000; + const sign_bit = 0x8000; + const special_exp = 0x7FFF; + + // If either a or b is NaN, they are unordered. + if ((a_rep.exp & special_exp == special_exp and a_rep.fraction ^ int_bit != 0) or + (b_rep.exp & special_exp == special_exp and b_rep.fraction ^ int_bit != 0)) + return RT.Unordered; + + // If a and b are both zeros, they are equal. + if ((a_rep.fraction | b_rep.fraction) | ((a_rep.exp | b_rep.exp) & special_exp) == 0) + return .Equal; + + if (@boolToInt(a_rep.exp == b_rep.exp) & @boolToInt(a_rep.fraction == b_rep.fraction) != 0) { + return .Equal; + } else if (a_rep.exp & sign_bit != b_rep.exp & sign_bit) { + // signs are different + if (@bitCast(i16, a_rep.exp) < @bitCast(i16, b_rep.exp)) { + return .Less; + } else { + return .Greater; + } + } else { + const a_fraction = a_rep.fraction | (@as(u80, a_rep.exp) << sig_bits); + const b_fraction = b_rep.fraction | (@as(u80, b_rep.exp) << sig_bits); + if (a_fraction < b_fraction) { + return .Less; + } else { + return .Greater; + } + } +} + +pub fn __lexf2(a: f80, b: f80) callconv(.C) i32 { + @setRuntimeSafety(builtin.is_test); + const float = cmp_f80(LE, a, b); + return @bitCast(i32, float); +} + +pub fn __gexf2(a: f80, b: f80) callconv(.C) i32 { + @setRuntimeSafety(builtin.is_test); + const float = cmp_f80(GE, a, b); + return @bitCast(i32, float); +} + +pub fn __eqxf2(a: f80, b: f80) callconv(.C) i32 { + return __lexf2(a, b); +} + +pub fn __ltxf2(a: f80, b: f80) callconv(.C) i32 { + return __lexf2(a, b); +} + +pub fn __nexf2(a: f80, b: f80) callconv(.C) i32 { + return __lexf2(a, b); +} + +pub fn __gtxf2(a: f80, b: f80) callconv(.C) i32 { + return __gexf2(a, b); +} + // Comparison between f128 pub fn __letf2(a: f128, b: f128) callconv(.C) i32 { diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index b97f009d62..a62daf0d63 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -3234,6 +3234,49 @@ static LLVMValueRef get_soft_f80_bin_op_func(CodeGen *g, const char *name, int p return LLVMAddFunction(g->module, name, fn_type); } +enum SoftF80Icmp { + NONE, + EQ_ZERO, + NE_ZERO, + LE_ZERO, + EQ_NEG, + GE_ZERO, + EQ_ONE, +}; + +static LLVMValueRef add_f80_icmp(CodeGen *g, LLVMValueRef val, SoftF80Icmp kind) { + switch (kind) { + case NONE: + return val; + case EQ_ZERO: { + LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 0, true); + return LLVMBuildICmp(g->builder, LLVMIntEQ, val, zero, ""); + } + case NE_ZERO: { + LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 0, true); + return LLVMBuildICmp(g->builder, LLVMIntNE, val, zero, ""); + } + case LE_ZERO: { + LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 0, true); + return LLVMBuildICmp(g->builder, LLVMIntSLE, val, zero, ""); + } + case EQ_NEG: { + LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, -1, true); + return LLVMBuildICmp(g->builder, LLVMIntEQ, val, zero, ""); + } + case GE_ZERO: { + LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 0, true); + return LLVMBuildICmp(g->builder, LLVMIntSGE, val, zero, ""); + } + case EQ_ONE: { + LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 1, true); + return LLVMBuildICmp(g->builder, LLVMIntEQ, val, zero, ""); + } + default: + zig_unreachable(); + } +} + static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, Stage1AirInstBinOp *bin_op_instruction) { @@ -3249,6 +3292,7 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, LLVMTypeRef return_type = g->builtin_types.entry_f80->llvm_type; int param_count = 2; const char *func_name; + SoftF80Icmp res_icmp = NONE; switch (op_id) { case IrBinOpInvalid: case IrBinOpArrayCat: @@ -3274,20 +3318,32 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, case IrBinOpCmpEq: return_type = g->builtin_types.entry_i32->llvm_type; func_name = "__eqxf2"; + res_icmp = EQ_ZERO; break; case IrBinOpCmpNotEq: return_type = g->builtin_types.entry_i32->llvm_type; func_name = "__nexf2"; + res_icmp = NE_ZERO; break; case IrBinOpCmpLessOrEq: + return_type = g->builtin_types.entry_i32->llvm_type; + func_name = "__lexf2"; + res_icmp = LE_ZERO; + break; case IrBinOpCmpLessThan: return_type = g->builtin_types.entry_i32->llvm_type; func_name = "__lexf2"; + res_icmp = EQ_NEG; break; case IrBinOpCmpGreaterOrEq: + return_type = g->builtin_types.entry_i32->llvm_type; + func_name = "__gexf2"; + res_icmp = GE_ZERO; + break; case IrBinOpCmpGreaterThan: return_type = g->builtin_types.entry_i32->llvm_type; func_name = "__gexf2"; + res_icmp = EQ_ONE; break; case IrBinOpMaximum: func_name = "__fmaxx"; @@ -3338,8 +3394,11 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, if (vector_len == 0) { LLVMValueRef params[2] = {op1_value, op2_value}; result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); + result = add_f80_icmp(g, result, res_icmp); } else { - result = build_alloca(g, op1->value->type, "", 0); + ZigType *alloca_ty = op1->value->type; + if (res_icmp != NONE) alloca_ty = get_vector_type(g, vector_len, g->builtin_types.entry_bool); + result = build_alloca(g, alloca_ty, "", 0); } LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; @@ -3350,6 +3409,7 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, LLVMBuildExtractElement(g->builder, op2_value, index_value, ""), }; LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); + call_result = add_f80_icmp(g, call_result, res_icmp); LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), call_result, index_value, ""); } From 6a736f0c8c187f2fcaeed4b60bf9e54aa719ae02 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 21 Jan 2022 21:49:02 +0200 Subject: [PATCH 0033/2031] compiler-rt: add add/sub for f80 --- lib/std/special/compiler_rt.zig | 4 + lib/std/special/compiler_rt/addXf3.zig | 171 +++++++++++++++++++++++++ 2 files changed, 175 insertions(+) diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index d83e94be8f..da21745cce 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -237,12 +237,16 @@ comptime { @export(__adddf3, .{ .name = "__adddf3", .linkage = linkage }); const __addtf3 = @import("compiler_rt/addXf3.zig").__addtf3; @export(__addtf3, .{ .name = "__addtf3", .linkage = linkage }); + const __addxf3 = @import("compiler_rt/addXf3.zig").__addxf3; + @export(__addxf3, .{ .name = "__addxf3", .linkage = linkage }); const __subsf3 = @import("compiler_rt/addXf3.zig").__subsf3; @export(__subsf3, .{ .name = "__subsf3", .linkage = linkage }); const __subdf3 = @import("compiler_rt/addXf3.zig").__subdf3; @export(__subdf3, .{ .name = "__subdf3", .linkage = linkage }); const __subtf3 = @import("compiler_rt/addXf3.zig").__subtf3; @export(__subtf3, .{ .name = "__subtf3", .linkage = linkage }); + const __subxf3 = @import("compiler_rt/addXf3.zig").__subxf3; + @export(__subxf3, .{ .name = "__subxf3", .linkage = linkage }); const __mulsf3 = @import("compiler_rt/mulXf3.zig").__mulsf3; @export(__mulsf3, .{ .name = "__mulsf3", .linkage = linkage }); diff --git a/lib/std/special/compiler_rt/addXf3.zig b/lib/std/special/compiler_rt/addXf3.zig index 4c74110310..41ff00e95d 100644 --- a/lib/std/special/compiler_rt/addXf3.zig +++ b/lib/std/special/compiler_rt/addXf3.zig @@ -225,6 +225,177 @@ fn addXf3(comptime T: type, a: T, b: T) T { return @bitCast(T, result); } +fn normalize_f80(exp: *i32, significand: *u80) void { + const shift = @clz(u64, @truncate(u64, significand.*)); + significand.* = (significand.* << shift); + exp.* += -@as(i8, shift); +} + +pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { + var a_rep align(16) = @ptrCast(*const std.math.F80Repr, &a).*; + var b_rep align(16) = @ptrCast(*const std.math.F80Repr, &b).*; + var a_exp: i32 = a_rep.exp & 0x7FFF; + var b_exp: i32 = b_rep.exp & 0x7FFF; + + const significand_bits = std.math.floatMantissaBits(f80); + const int_bit = 0x8000000000000000; + const significand_mask = 0x7FFFFFFFFFFFFFFF; + const qnan_bit = 0xC000000000000000; + const max_exp = 0x7FFF; + const sign_bit = 0x8000; + + // Detect if a or b is infinity, or NaN. + if (a_exp == max_exp) { + if (a_rep.fraction ^ int_bit == 0) { + if (b_exp == max_exp and (b_rep.fraction ^ int_bit == 0)) { + // +/-infinity + -/+infinity = qNaN + return std.math.qnan_f80; + } + // +/-infinity + anything = +/- infinity + return a; + } else { + std.debug.assert(a_rep.fraction & significand_mask != 0); + // NaN + anything = qNaN + a_rep.fraction |= qnan_bit; + return @ptrCast(*const f80, &a_rep).*; + } + } + if (b_exp == max_exp) { + if (b_rep.fraction ^ int_bit == 0) { + // anything + +/-infinity = +/-infinity + return b; + } else { + std.debug.assert(b_rep.fraction & significand_mask != 0); + // anything + NaN = qNaN + b_rep.fraction |= qnan_bit; + return @ptrCast(*const f80, &b_rep).*; + } + } + + const a_zero = (a_rep.fraction | @bitCast(u32, a_exp)) == 0; + const b_zero = (b_rep.fraction | @bitCast(u32, b_exp)) == 0; + if (a_zero) { + // zero + anything = anything + if (b_zero) { + // but we need to get the sign right for zero + zero + a_rep.exp &= b_rep.exp; + return @ptrCast(*const f80, &a_rep).*; + } else { + return b; + } + } else if (b_zero) { + // anything + zero = anything + return a; + } + + var a_int: u80 = a_rep.fraction | (@as(u80, a_rep.exp & max_exp) << significand_bits); + var b_int: u80 = b_rep.fraction | (@as(u80, b_rep.exp & max_exp) << significand_bits); + + // Swap a and b if necessary so that a has the larger absolute value. + if (b_int > a_int) { + const temp = a_rep; + a_rep = b_rep; + b_rep = temp; + } + + // Extract the exponent and significand from the (possibly swapped) a and b. + a_exp = a_rep.exp & max_exp; + b_exp = b_rep.exp & max_exp; + a_int = a_rep.fraction; + b_int = b_rep.fraction; + + // Normalize any denormals, and adjust the exponent accordingly. + normalize_f80(&a_exp, &a_int); + normalize_f80(&b_exp, &b_int); + + // The sign of the result is the sign of the larger operand, a. If they + // have opposite signs, we are performing a subtraction; otherwise addition. + const result_sign = a_rep.exp & sign_bit; + const subtraction = (a_rep.exp ^ b_rep.exp) & sign_bit != 0; + + // Shift the significands to give us round, guard and sticky, and or in the + // implicit significand bit. (If we fell through from the denormal path it + // was already set by normalize( ), but setting it twice won't hurt + // anything.) + a_int = a_int << 3; + b_int = b_int << 3; + + // Shift the significand of b by the difference in exponents, with a sticky + // bottom bit to get rounding correct. + const @"align" = @intCast(u80, a_exp - b_exp); + if (@"align" != 0) { + if (@"align" < 80) { + const sticky = if (b_int << @intCast(u7, 80 - @"align") != 0) @as(u80, 1) else 0; + b_int = (b_int >> @truncate(u7, @"align")) | sticky; + } else { + b_int = 1; // sticky; b is known to be non-zero. + } + } + if (subtraction) { + a_int -= b_int; + // If a == -b, return +zero. + if (a_int == 0) return 0.0; + + // If partial cancellation occurred, we need to left-shift the result + // and adjust the exponent: + if (a_int < int_bit << 3) { + const shift = @intCast(i32, @clz(u80, a_int)) - @intCast(i32, @clz(u80, int_bit << 3)); + a_int <<= @intCast(u7, shift); + a_exp -= shift; + } + } else { // addition + a_int += b_int; + + // If the addition carried up, we need to right-shift the result and + // adjust the exponent: + if (a_int & (int_bit << 4) != 0) { + const sticky = a_int & 1; + a_int = a_int >> 1 | sticky; + a_exp += 1; + } + } + + // If we have overflowed the type, return +/- infinity: + if (a_exp >= max_exp) { + a_rep.exp = max_exp | result_sign; + a_rep.fraction = int_bit; // integer bit is set for +/-inf + return @ptrCast(*const f80, &a_rep).*; + } + + if (a_exp <= 0) { + // Result is denormal before rounding; the exponent is zero and we + // need to shift the significand. + const shift = @intCast(u80, 1 - a_exp); + const sticky = if (a_int << @intCast(u7, 80 - shift) != 0) @as(u1, 1) else 0; + a_int = a_int >> @intCast(u7, shift | sticky); + a_exp = 0; + } + + // Low three bits are round, guard, and sticky. + const round_guard_sticky = @truncate(u3, a_int); + + // Shift the significand into place. + a_int = @truncate(u64, a_int >> 3); + + // // Insert the exponent and sign. + a_int |= (@intCast(u80, a_exp) | result_sign) << significand_bits; + + // Final rounding. The result may overflow to infinity, but that is the + // correct result in that case. + if (round_guard_sticky > 0x4) a_int += 1; + if (round_guard_sticky == 0x4) a_int += a_int & 1; + + a_rep.fraction = @truncate(u64, a_int); + a_rep.exp = @truncate(u16, a_int >> significand_bits); + return @ptrCast(*const f80, &a_rep).*; +} + +pub fn __subxf3(a: f80, b: f80) callconv(.C) f80 { + var b_rep align(16) = @ptrCast(*const std.math.F80Repr, &b).*; + b_rep.exp ^= 0x8000; + return __addxf3(a, @ptrCast(*const f80, &b_rep).*); +} + test { _ = @import("addXf3_test.zig"); } From b2f84c6714c30589c35fce72bab530cef4b05eca Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 22 Jan 2022 14:07:15 +0200 Subject: [PATCH 0034/2031] stage1: implement f80 negation on non native targets --- src/stage1/codegen.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index a62daf0d63..1b11f32397 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -4106,12 +4106,47 @@ static LLVMValueRef ir_render_binary_not(CodeGen *g, Stage1Air *executable, return LLVMBuildNot(g->builder, operand, ""); } +static LLVMValueRef ir_gen_soft_f80_neg(CodeGen *g, ZigType *op_type, LLVMValueRef operand) { + uint32_t vector_len = op_type->id == ZigTypeIdVector ? op_type->data.vector.len : 0; + + uint64_t buf[2] = {0, 0}; + if (g->is_big_endian != native_is_big_endian) { + buf[1] = 0x8000000000000000; + } else { + buf[1] = 0x8000; + } + LLVMValueRef sign_mask = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, buf); + + LLVMValueRef result; + if (vector_len == 0) { + result = LLVMBuildXor(g->builder, operand, sign_mask, ""); + } else { + result = build_alloca(g, op_type, "", 0); + } + + LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; + for (uint32_t i = 0; i < vector_len; i++) { + LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); + LLVMValueRef xor_operand = LLVMBuildExtractElement(g->builder, operand, index_value, ""); + LLVMValueRef xor_result = LLVMBuildXor(g->builder, xor_operand, sign_mask, ""); + LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), + xor_result, index_value, ""); + } + if (vector_len != 0) { + result = LLVMBuildLoad(g->builder, result, ""); + } + return result; +} + static LLVMValueRef ir_gen_negation(CodeGen *g, Stage1AirInst *inst, Stage1AirInst *operand, bool wrapping) { LLVMValueRef llvm_operand = ir_llvm_value(g, operand); ZigType *operand_type = operand->value->type; ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; + if (scalar_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) + return ir_gen_soft_f80_neg(g, operand_type, llvm_operand); + if (scalar_type->id == ZigTypeIdFloat) { ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, inst)); return LLVMBuildFNeg(g->builder, llvm_operand, ""); From 5a7d43df23664385d6841ef98b17ff9447be1ec6 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 29 Jan 2022 17:30:18 +0200 Subject: [PATCH 0035/2031] stage1: make f80 always size 16, align 16 --- lib/std/math.zig | 2 ++ src/stage1/codegen.cpp | 15 +++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/std/math.zig b/lib/std/math.zig index 43ad49889d..6802d420fd 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -46,8 +46,10 @@ pub const f128_toint = 1.0 / f128_epsilon; pub const F80Repr = if (@import("builtin").cpu.arch.endian() == .Little) extern struct { fraction: u64, exp: u16, + _pad: u32 = undefined, } else extern struct { exp: u16, + _pad: u32 = undefined, // TODO verify compatibility with hardware fraction: u64, }; diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 1b11f32397..96576f1721 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -8197,6 +8197,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n buf[1] = tmp; #endif LLVMValueRef as_i128 = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, buf); + if (!target_has_f80(g->zig_target)) return as_i128; LLVMValueRef as_int = LLVMConstTrunc(as_i128, LLVMIntType(80)); return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry)); } @@ -9420,13 +9421,15 @@ static void define_builtin_types(CodeGen *g) { add_fp_entry(g, "f64", 64, LLVMDoubleType(), &g->builtin_types.entry_f64); add_fp_entry(g, "f128", 128, LLVMFP128Type(), &g->builtin_types.entry_f128); - if (target_has_f80(g->zig_target)) { - add_fp_entry(g, "f80", 80, LLVMX86FP80Type(), &g->builtin_types.entry_f80); - } else { + { ZigType *entry = new_type_table_entry(ZigTypeIdFloat); - entry->llvm_type = get_int_type(g, false, 128)->llvm_type; - entry->size_in_bits = 8 * LLVMStoreSizeOfType(g->target_data_ref, entry->llvm_type); - entry->abi_size = LLVMABISizeOfType(g->target_data_ref, entry->llvm_type); + if (target_has_f80(g->zig_target)) { + entry->llvm_type = LLVMX86FP80Type(); + } else { + entry->llvm_type = get_int_type(g, false, 128)->llvm_type; + } + entry->size_in_bits = 8 * 16; + entry->abi_size = 16; entry->abi_align = 16; buf_init_from_str(&entry->name, "f80"); entry->data.floating.bit_count = 80; From 01d48e55a5aa683828dcb88fee2d811c8262d3e9 Mon Sep 17 00:00:00 2001 From: Jan Philipp Hafer Date: Sat, 5 Feb 2022 03:32:29 +0100 Subject: [PATCH 0036/2031] compiler_rt: optimize mulo - use usize to decide if register size is big enough to store multiplication result or if division is necessary - multiplication routine with check of integer bounds - wrapping multipliation and division routine from Hacker's Delight --- lib/std/special/compiler_rt/mulo.zig | 97 ++++++++++++++-------------- 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/lib/std/special/compiler_rt/mulo.zig b/lib/std/special/compiler_rt/mulo.zig index 9fa5d3830b..df4c98134c 100644 --- a/lib/std/special/compiler_rt/mulo.zig +++ b/lib/std/special/compiler_rt/mulo.zig @@ -1,67 +1,68 @@ const builtin = @import("builtin"); +const std = @import("std"); +const math = std.math; // mulo - multiplication overflow -// - muloXi4_generic for unoptimized version +// * return a*b. +// * return if a*b overflows => 1 else => 0 +// - muloXi4_genericSmall as default +// - muloXi4_genericFast for 2*bitsize <= usize -// return a*b. -// return if a*b overflows => 1 else => 0 -// see https://stackoverflow.com/a/26320664 for possible implementations - -inline fn muloXi4_generic(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST { +inline fn muloXi4_genericSmall(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST { @setRuntimeSafety(builtin.is_test); - const BSIZE = @bitSizeOf(ST); - comptime var UT = switch (ST) { - i32 => u32, - i64 => u64, - i128 => u128, + overflow.* = 0; + const min = math.minInt(ST); + var res: ST = a *% b; + // Hacker's Delight section Overflow subsection Multiplication + // case a=-2^{31}, b=-1 problem, because + // on some machines a*b = -2^{31} with overflow + // Then -2^{31}/-1 overflows and any result is possible. + // => check with a<0 and b=-2^{31} + if ((a < 0 and b == min) or (a != 0 and @divTrunc(res, a) != b)) + overflow.* = 1; + return res; +} + +inline fn muloXi4_genericFast(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST { + @setRuntimeSafety(builtin.is_test); + overflow.* = 0; + const EST = switch (ST) { + i32 => i64, + i64 => i128, + i128 => i256, else => unreachable, }; - const min = @bitCast(ST, @as(UT, 1 << (BSIZE - 1))); - const max = ~min; - overflow.* = 0; - const result = a *% b; - - // edge cases - if (a == min) { - if (b != 0 and b != 1) overflow.* = 1; - return result; - } - if (b == min) { - if (a != 0 and a != 1) overflow.* = 1; - return result; - } - - // take sign of x sx - const sa = a >> (BSIZE - 1); - const sb = b >> (BSIZE - 1); - // take absolute value of a and b via - // abs(x) = (x^sx)) - sx - const abs_a = (a ^ sa) -% sa; - const abs_b = (b ^ sb) -% sb; - - // unitary magnitude, cannot have overflow - if (abs_a < 2 or abs_b < 2) return result; - - // compare the signs of operands - if ((a ^ b) >> (BSIZE - 1) != 0) { - if (abs_a > @divTrunc(max, abs_b)) overflow.* = 1; - } else { - if (abs_a > @divTrunc(min, -abs_b)) overflow.* = 1; - } - - return result; + const min = math.minInt(ST); + const max = math.maxInt(ST); + var res: EST = @as(EST, a) * @as(EST, b); + //invariant: -2^{bitwidth(EST)} < res < 2^{bitwidth(EST)-1} + if (res < min or max < res) + overflow.* = 1; + return @truncate(ST, res); } pub fn __mulosi4(a: i32, b: i32, overflow: *c_int) callconv(.C) i32 { - return muloXi4_generic(i32, a, b, overflow); + if (2 * @bitSizeOf(i32) <= @bitSizeOf(usize)) { + return muloXi4_genericFast(i32, a, b, overflow); + } else { + return muloXi4_genericSmall(i32, a, b, overflow); + } } pub fn __mulodi4(a: i64, b: i64, overflow: *c_int) callconv(.C) i64 { - return muloXi4_generic(i64, a, b, overflow); + if (2 * @bitSizeOf(i64) <= @bitSizeOf(usize)) { + return muloXi4_genericFast(i64, a, b, overflow); + } else { + return muloXi4_genericSmall(i64, a, b, overflow); + } } pub fn __muloti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 { - return muloXi4_generic(i128, a, b, overflow); + if (2 * @bitSizeOf(i128) <= @bitSizeOf(usize)) { + return muloXi4_genericFast(i128, a, b, overflow); + } else { + return muloXi4_genericSmall(i128, a, b, overflow); + } } test { From 7d04ab1f14269b25a7ac03c3f787b3f3ee3453c3 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 4 Feb 2022 19:55:32 +0200 Subject: [PATCH 0037/2031] std.process: add option to support single quotes to ArgIteratorGeneral --- lib/std/process.zig | 62 +++++++++++++++++++++++++++++---------------- src/main.zig | 2 +- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/lib/std/process.zig b/lib/std/process.zig index 96bfed3718..699c994abf 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -303,7 +303,8 @@ pub const ArgIteratorWasi = struct { /// Optional parameters for `ArgIteratorGeneral` pub const ArgIteratorGeneralOptions = struct { - comments_supported: bool = false, + comments: bool = false, + single_quotes: bool = false, }; /// A general Iterator to parse a string into a set of arguments @@ -387,7 +388,7 @@ pub fn ArgIteratorGeneral(comptime options: ArgIteratorGeneralOptions) type { 0 => return false, ' ', '\t', '\r', '\n' => continue, '#' => { - if (options.comments_supported) { + if (options.comments) { while (true) : (self.index += 1) { switch (self.cmd_line[self.index]) { '\n' => break, @@ -417,7 +418,11 @@ pub fn ArgIteratorGeneral(comptime options: ArgIteratorGeneralOptions) type { const character = if (self.index != self.cmd_line.len) self.cmd_line[self.index] else 0; switch (character) { 0 => return true, - '"' => { + '"', '\'' => { + if (!options.single_quotes and character == '\'') { + backslash_count = 0; + continue; + } const quote_is_real = backslash_count % 2 == 0; if (quote_is_real) { in_quote = !in_quote; @@ -460,7 +465,13 @@ pub fn ArgIteratorGeneral(comptime options: ArgIteratorGeneralOptions) type { self.start = self.end; return token; }, - '"' => { + '"', '\'' => { + if (!options.single_quotes and character == '\'') { + self.emitBackslashes(backslash_count); + backslash_count = 0; + self.emitCharacter(character); + continue; + } const quote_is_real = backslash_count % 2 == 0; self.emitBackslashes(backslash_count / 2); backslash_count = 0; @@ -522,7 +533,7 @@ pub fn ArgIteratorGeneral(comptime options: ArgIteratorGeneralOptions) type { /// Cross-platform command line argument iterator. pub const ArgIterator = struct { const InnerType = switch (builtin.os.tag) { - .windows => ArgIteratorGeneral(.{ .comments_supported = false }), + .windows => ArgIteratorGeneral(.{}), .wasi => if (builtin.link_libc) ArgIteratorPosix else ArgIteratorWasi, else => ArgIteratorPosix, }; @@ -664,27 +675,30 @@ pub fn argsFree(allocator: mem.Allocator, args_alloc: []const [:0]u8) void { } test "general arg parsing" { - try testGeneralCmdLine("a b\tc d", &[_][]const u8{ "a", "b", "c", "d" }); - try testGeneralCmdLine("\"abc\" d e", &[_][]const u8{ "abc", "d", "e" }); - try testGeneralCmdLine("a\\\\\\b d\"e f\"g h", &[_][]const u8{ "a\\\\\\b", "de fg", "h" }); - try testGeneralCmdLine("a\\\\\\\"b c d", &[_][]const u8{ "a\\\"b", "c", "d" }); - try testGeneralCmdLine("a\\\\\\\\\"b c\" d e", &[_][]const u8{ "a\\\\b c", "d", "e" }); - try testGeneralCmdLine("a b\tc \"d f", &[_][]const u8{ "a", "b", "c", "d f" }); - try testGeneralCmdLine("j k l\\", &[_][]const u8{ "j", "k", "l\\" }); - try testGeneralCmdLine("\"\" x y z\\\\", &[_][]const u8{ "", "x", "y", "z\\\\" }); + try testGeneralCmdLine("a b\tc d", &.{ "a", "b", "c", "d" }); + try testGeneralCmdLine("\"abc\" d e", &.{ "abc", "d", "e" }); + try testGeneralCmdLine("a\\\\\\b d\"e f\"g h", &.{ "a\\\\\\b", "de fg", "h" }); + try testGeneralCmdLine("a\\\\\\\"b c d", &.{ "a\\\"b", "c", "d" }); + try testGeneralCmdLine("a\\\\\\\\\"b c\" d e", &.{ "a\\\\b c", "d", "e" }); + try testGeneralCmdLine("a b\tc \"d f", &.{ "a", "b", "c", "d f" }); + try testGeneralCmdLine("j k l\\", &.{ "j", "k", "l\\" }); + try testGeneralCmdLine("\"\" x y z\\\\", &.{ "", "x", "y", "z\\\\" }); - try testGeneralCmdLine("\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", &[_][]const u8{ + try testGeneralCmdLine("\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", &.{ ".\\..\\zig-cache\\build", "bin\\zig.exe", ".\\..", ".\\..\\zig-cache", "--help", }); + + try testGeneralCmdLine( + \\ 'foo' "bar" + , &.{ "'foo'", "bar" }); } fn testGeneralCmdLine(input_cmd_line: []const u8, expected_args: []const []const u8) !void { - var it = try ArgIteratorGeneral(.{ .comments_supported = false }) - .init(std.testing.allocator, input_cmd_line); + var it = try ArgIteratorGeneral(.{}).init(std.testing.allocator, input_cmd_line); defer it.deinit(); for (expected_args) |expected_arg| { const arg = it.next().?; @@ -697,30 +711,34 @@ test "response file arg parsing" { try testResponseFileCmdLine( \\a b \\c d\ - , &[_][]const u8{ "a", "b", "c", "d\\" }); - try testResponseFileCmdLine("a b c d\\", &[_][]const u8{ "a", "b", "c", "d\\" }); + , &.{ "a", "b", "c", "d\\" }); + try testResponseFileCmdLine("a b c d\\", &.{ "a", "b", "c", "d\\" }); try testResponseFileCmdLine( \\j \\ k l # this is a comment \\ \\\ \\\\ "none" "\\" "\\\" \\ "m" #another comment \\ - , &[_][]const u8{ "j", "k", "l", "m" }); + , &.{ "j", "k", "l", "m" }); try testResponseFileCmdLine( \\ "" q "" \\ "r s # t" "u\" v" #another comment \\ - , &[_][]const u8{ "", "q", "", "r s # t", "u\" v" }); + , &.{ "", "q", "", "r s # t", "u\" v" }); try testResponseFileCmdLine( \\ -l"advapi32" a# b#c d# \\e\\\ - , &[_][]const u8{ "-ladvapi32", "a#", "b#c", "d#", "e\\\\\\" }); + , &.{ "-ladvapi32", "a#", "b#c", "d#", "e\\\\\\" }); + + try testResponseFileCmdLine( + \\ 'foo' "bar" + , &.{ "foo", "bar" }); } fn testResponseFileCmdLine(input_cmd_line: []const u8, expected_args: []const []const u8) !void { - var it = try ArgIteratorGeneral(.{ .comments_supported = true }) + var it = try ArgIteratorGeneral(.{ .comments = true, .single_quotes = true }) .init(std.testing.allocator, input_cmd_line); defer it.deinit(); for (expected_args) |expected_arg| { diff --git a/src/main.zig b/src/main.zig index bd25c5d234..bbdb948c90 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4230,7 +4230,7 @@ pub const ClangArgIterator = struct { }; } - const ArgIteratorResponseFile = process.ArgIteratorGeneral(.{ .comments_supported = true }); + const ArgIteratorResponseFile = process.ArgIteratorGeneral(.{ .comments = true, .single_quotes = true }); /// Initialize the arguments from a Response File. "*.rsp" fn initArgIteratorResponseFile(allocator: Allocator, resp_file_path: []const u8) !ArgIteratorResponseFile { From fbc06f9c9151205896fb167b087506d6580946c4 Mon Sep 17 00:00:00 2001 From: rohlem Date: Fri, 4 Feb 2022 19:44:38 +0100 Subject: [PATCH 0038/2031] std.build.TranslateCStep: add C macro support The string construction code is moved out of std.build.LibExeObjStep into std.build.constructCMacroArg, to allow reusing it elsewhere. --- lib/std/build.zig | 26 +++++++++++++++++--------- lib/std/build/TranslateCStep.zig | 19 +++++++++++++++++++ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 395ccc5cb4..104aceea76 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1862,15 +1862,7 @@ pub const LibExeObjStep = struct { /// If the value is omitted, it is set to 1. /// `name` and `value` need not live longer than the function call. pub fn defineCMacro(self: *LibExeObjStep, name: []const u8, value: ?[]const u8) void { - var macro = self.builder.allocator.alloc( - u8, - name.len + if (value) |value_slice| value_slice.len + 1 else 0, - ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable; - mem.copy(u8, macro, name); - if (value) |value_slice| { - macro[name.len] = '='; - mem.copy(u8, macro[name.len + 1 ..], value_slice); - } + const macro = constructCMacro(self.builder.allocator, name, value); self.c_macros.append(macro) catch unreachable; } @@ -2934,6 +2926,22 @@ pub const LibExeObjStep = struct { } }; +/// Allocates a new string for assigning a value to a named macro. +/// If the value is omitted, it is set to 1. +/// `name` and `value` need not live longer than the function call. +pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 { + var macro = allocator.alloc( + u8, + name.len + if (value) |value_slice| value_slice.len + 1 else 0, + ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable; + mem.copy(u8, macro, name); + if (value) |value_slice| { + macro[name.len] = '='; + mem.copy(u8, macro[name.len + 1 ..], value_slice); + } + return macro; +} + pub const InstallArtifactStep = struct { pub const base_id = .install_artifact; diff --git a/lib/std/build/TranslateCStep.zig b/lib/std/build/TranslateCStep.zig index 0d44ebd80a..1f9bee463c 100644 --- a/lib/std/build/TranslateCStep.zig +++ b/lib/std/build/TranslateCStep.zig @@ -16,6 +16,7 @@ step: Step, builder: *Builder, source: build.FileSource, include_dirs: std.ArrayList([]const u8), +c_macros: std.ArrayList([]const u8), output_dir: ?[]const u8, out_basename: []const u8, target: CrossTarget = CrossTarget{}, @@ -28,6 +29,7 @@ pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep { .builder = builder, .source = source, .include_dirs = std.ArrayList([]const u8).init(builder.allocator), + .c_macros = std.ArrayList([]const u8).init(builder.allocator), .output_dir = null, .out_basename = undefined, .output_file = build.GeneratedFile{ .step = &self.step }, @@ -53,6 +55,18 @@ pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches)); } +/// If the value is omitted, it is set to 1. +/// `name` and `value` need not live longer than the function call. +pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void { + const macro = build.constructCMacro(self.builder.allocator, name, value); + self.c_macros.append(macro) catch unreachable; +} + +/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. +pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void { + self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable; +} + fn make(step: *Step) !void { const self = @fieldParentPtr(TranslateCStep, "step", step); @@ -73,6 +87,11 @@ fn make(step: *Step) !void { try argv_list.append(include_dir); } + for (self.c_macros.items) |c_macro| { + try argv_list.append("-D"); + try argv_list.append(c_macro); + } + try argv_list.append(self.source.getPath(self.builder)); const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step); From 0e1afb4d986c3316c6f024be50612b0a6c66777b Mon Sep 17 00:00:00 2001 From: gwenzek Date: Sat, 5 Feb 2022 15:33:00 +0100 Subject: [PATCH 0039/2031] stage2: add support for Nvptx target sample command: /home/guw/github/zig/stage2/bin/zig build-obj cuda_kernel.zig -target nvptx64-cuda -O ReleaseSafe this will create a kernel.ptx expose PtxKernel call convention from LLVM kernels are `export fn f() callconv(.PtxKernel)` --- lib/std/builtin.zig | 1 + lib/std/target.zig | 4 ++ lib/std/zig.zig | 1 + src/Module.zig | 6 +- src/Sema.zig | 1 + src/codegen/llvm.zig | 6 +- src/link.zig | 30 ++++++++-- src/link/NvPtx.zig | 122 +++++++++++++++++++++++++++++++++++++++ src/stage1/all_types.hpp | 3 +- src/stage1/analyze.cpp | 12 ++++ src/stage1/codegen.cpp | 6 ++ src/stage1/ir.cpp | 1 + 12 files changed, 185 insertions(+), 8 deletions(-) create mode 100644 src/link/NvPtx.zig diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 9e7bfc99ba..39e849fb5e 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -147,6 +147,7 @@ pub const CallingConvention = enum { AAPCS, AAPCSVFP, SysV, + PtxKernel, }; /// This data structure is used by the Zig language code generation and diff --git a/lib/std/target.zig b/lib/std/target.zig index 182690484e..9a2dcfcc66 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -579,6 +579,8 @@ pub const Target = struct { raw, /// Plan 9 from Bell Labs plan9, + /// Nvidia PTX format + nvptx, pub fn fileExt(of: ObjectFormat, cpu_arch: Cpu.Arch) [:0]const u8 { return switch (of) { @@ -589,6 +591,7 @@ pub const Target = struct { .hex => ".ihex", .raw => ".bin", .plan9 => plan9Ext(cpu_arch), + .nvptx => ".ptx", }; } }; @@ -1388,6 +1391,7 @@ pub const Target = struct { else => return switch (cpu_arch) { .wasm32, .wasm64 => .wasm, .spirv32, .spirv64 => .spirv, + .nvptx, .nvptx64 => .nvptx, else => .elf, }, }; diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 1420db8ec2..9b8e2294f2 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -181,6 +181,7 @@ pub fn binNameAlloc(allocator: std.mem.Allocator, options: BinNameOptions) error .Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, ofmt.fileExt(target.cpu.arch) }), .Lib => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{ target.libPrefix(), root_name }), }, + .nvptx => return std.fmt.allocPrint(allocator, "{s}", .{root_name}), } } diff --git a/src/Module.zig b/src/Module.zig index e2e2505927..eeed6b2dc9 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4242,7 +4242,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi // in `Decl` to notice that the line number did not change. mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); }, - .c, .wasm, .spirv => {}, + .c, .wasm, .spirv, .nvptx => {}, } } } @@ -4316,6 +4316,7 @@ pub fn clearDecl( .c => .{ .c = {} }, .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty }, .spirv => .{ .spirv = {} }, + .nvptx => .{ .nvptx = {} }, }; decl.fn_link = switch (mod.comp.bin_file.tag) { .coff => .{ .coff = {} }, @@ -4325,6 +4326,7 @@ pub fn clearDecl( .c => .{ .c = {} }, .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, .spirv => .{ .spirv = .{} }, + .nvptx => .{ .nvptx = .{} }, }; } if (decl.getInnerNamespace()) |namespace| { @@ -4652,6 +4654,7 @@ pub fn allocateNewDecl( .c => .{ .c = {} }, .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty }, .spirv => .{ .spirv = {} }, + .nvptx => .{ .nvptx = {} }, }, .fn_link = switch (mod.comp.bin_file.tag) { .coff => .{ .coff = {} }, @@ -4661,6 +4664,7 @@ pub fn allocateNewDecl( .c => .{ .c = {} }, .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, .spirv => .{ .spirv = .{} }, + .nvptx => .{ .nvptx = .{} }, }, .generation = 0, .is_pub = false, diff --git a/src/Sema.zig b/src/Sema.zig index c4b3ad8c33..934fa4064b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3724,6 +3724,7 @@ pub fn analyzeExport( .c => .{ .c = {} }, .wasm => .{ .wasm = {} }, .spirv => .{ .spirv = {} }, + .nvptx => .{ .nvptx = {} }, }, .owner_decl = owner_decl, .src_decl = src_decl, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 81742d4866..08fc3879a9 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -378,7 +378,7 @@ pub const Object = struct { const mod = comp.bin_file.options.module.?; const cache_dir = mod.zig_cache_artifact_directory; - const emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit| + var emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit| try emit.basenamePath(arena, try arena.dupeZ(u8, comp.bin_file.intermediary_basename.?)) else null; @@ -5078,6 +5078,10 @@ fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) llvm.Ca }, .Signal => .AVR_SIGNAL, .SysV => .X86_64_SysV, + .PtxKernel => return switch (target.cpu.arch) { + .nvptx, .nvptx64 => .PTX_Kernel, + else => unreachable, + }, }; } diff --git a/src/link.zig b/src/link.zig index 883d79de34..51e7082aa7 100644 --- a/src/link.zig +++ b/src/link.zig @@ -215,6 +215,7 @@ pub const File = struct { c: void, wasm: Wasm.DeclBlock, spirv: void, + nvptx: void, }; pub const LinkFn = union { @@ -225,6 +226,7 @@ pub const File = struct { c: void, wasm: Wasm.FnData, spirv: SpirV.FnData, + nvptx: void, }; pub const Export = union { @@ -235,6 +237,7 @@ pub const File = struct { c: void, wasm: void, spirv: void, + nvptx: void, }; /// For DWARF .debug_info. @@ -274,6 +277,7 @@ pub const File = struct { .plan9 => return &(try Plan9.createEmpty(allocator, options)).base, .c => unreachable, // Reported error earlier. .spirv => &(try SpirV.createEmpty(allocator, options)).base, + .nvptx => &(try NvPtx.createEmpty(allocator, options)).base, .hex => return error.HexObjectFormatUnimplemented, .raw => return error.RawObjectFormatUnimplemented, }; @@ -292,6 +296,7 @@ pub const File = struct { .wasm => &(try Wasm.createEmpty(allocator, options)).base, .c => unreachable, // Reported error earlier. .spirv => &(try SpirV.createEmpty(allocator, options)).base, + .nvptx => &(try NvPtx.createEmpty(allocator, options)).base, .hex => return error.HexObjectFormatUnimplemented, .raw => return error.RawObjectFormatUnimplemented, }; @@ -312,6 +317,7 @@ pub const File = struct { .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base, .c => &(try C.openPath(allocator, sub_path, options)).base, .spirv => &(try SpirV.openPath(allocator, sub_path, options)).base, + .nvptx => &(try NvPtx.openPath(allocator, sub_path, options)).base, .hex => return error.HexObjectFormatUnimplemented, .raw => return error.RawObjectFormatUnimplemented, }; @@ -344,7 +350,7 @@ pub const File = struct { .mode = determineMode(base.options), }); }, - .c, .wasm, .spirv => {}, + .c, .wasm, .spirv, .nvptx => {}, } } @@ -389,7 +395,7 @@ pub const File = struct { f.close(); base.file = null; }, - .c, .wasm, .spirv => {}, + .c, .wasm, .spirv, .nvptx => {}, } } @@ -437,6 +443,7 @@ pub const File = struct { .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl), .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl), .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDecl(module, decl), + .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDecl(module, decl), // zig fmt: on } } @@ -456,6 +463,7 @@ pub const File = struct { .wasm => return @fieldParentPtr(Wasm, "base", base).updateFunc(module, func, air, liveness), .spirv => return @fieldParentPtr(SpirV, "base", base).updateFunc(module, func, air, liveness), .plan9 => return @fieldParentPtr(Plan9, "base", base).updateFunc(module, func, air, liveness), + .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateFunc(module, func, air, liveness), // zig fmt: on } } @@ -471,7 +479,7 @@ pub const File = struct { .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl), .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl), .plan9 => @panic("TODO: implement updateDeclLineNumber for plan9"), - .wasm, .spirv => {}, + .wasm, .spirv, .nvptx => {}, } } @@ -493,7 +501,7 @@ pub const File = struct { }, .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl), .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl), - .c, .spirv => {}, + .c, .spirv, .nvptx => {}, } } @@ -551,6 +559,11 @@ pub const File = struct { parent.deinit(); base.allocator.destroy(parent); }, + .nvptx => { + const parent = @fieldParentPtr(NvPtx, "base", base); + parent.deinit(); + base.allocator.destroy(parent); + }, } } @@ -584,6 +597,7 @@ pub const File = struct { .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp), .spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp), .plan9 => return @fieldParentPtr(Plan9, "base", base).flush(comp), + .nvptx => return @fieldParentPtr(NvPtx, "base", base).flush(comp), } } @@ -598,6 +612,7 @@ pub const File = struct { .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp), .spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp), .plan9 => return @fieldParentPtr(Plan9, "base", base).flushModule(comp), + .nvptx => return @fieldParentPtr(NvPtx, "base", base).flushModule(comp), } } @@ -612,6 +627,7 @@ pub const File = struct { .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl), .spirv => @fieldParentPtr(SpirV, "base", base).freeDecl(decl), .plan9 => @fieldParentPtr(Plan9, "base", base).freeDecl(decl), + .nvptx => @fieldParentPtr(NvPtx, "base", base).freeDecl(decl), } } @@ -622,7 +638,7 @@ pub const File = struct { .macho => return @fieldParentPtr(MachO, "base", base).error_flags, .plan9 => return @fieldParentPtr(Plan9, "base", base).error_flags, .c => return .{ .no_entry_point_found = false }, - .wasm, .spirv => return ErrorFlags{}, + .wasm, .spirv, .nvptx => return ErrorFlags{}, } } @@ -644,6 +660,7 @@ pub const File = struct { .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports), .spirv => return @fieldParentPtr(SpirV, "base", base).updateDeclExports(module, decl, exports), .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclExports(module, decl, exports), + .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDeclExports(module, decl, exports), } } @@ -656,6 +673,7 @@ pub const File = struct { .c => unreachable, .wasm => unreachable, .spirv => unreachable, + .nvptx => unreachable, } } @@ -851,6 +869,7 @@ pub const File = struct { wasm, spirv, plan9, + nvptx, }; pub const ErrorFlags = struct { @@ -864,6 +883,7 @@ pub const File = struct { pub const MachO = @import("link/MachO.zig"); pub const SpirV = @import("link/SpirV.zig"); pub const Wasm = @import("link/Wasm.zig"); + pub const NvPtx = @import("link/NvPtx.zig"); }; pub fn determineMode(options: Options) fs.File.Mode { diff --git a/src/link/NvPtx.zig b/src/link/NvPtx.zig new file mode 100644 index 0000000000..77613cdc1d --- /dev/null +++ b/src/link/NvPtx.zig @@ -0,0 +1,122 @@ +//! NVidia PTX (Paralle Thread Execution) +//! https://docs.nvidia.com/cuda/parallel-thread-execution/index.html +//! For this we rely on the nvptx backend of LLVM +//! Kernel functions need to be marked both as "export" and "callconv(.PtxKernel)" + +const NvPtx = @This(); + +const std = @import("std"); +const builtin = @import("builtin"); + +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; +const log = std.log.scoped(.link); + +const Module = @import("../Module.zig"); +const Compilation = @import("../Compilation.zig"); +const link = @import("../link.zig"); +const trace = @import("../tracy.zig").trace; +const build_options = @import("build_options"); +const Air = @import("../Air.zig"); +const Liveness = @import("../Liveness.zig"); +const LlvmObject = @import("../codegen/llvm.zig").Object; + +base: link.File, +llvm_object: *LlvmObject, + +pub fn createEmpty(gpa: Allocator, options: link.Options) !*NvPtx { + if (!build_options.have_llvm) return error.TODOArchNotSupported; + + const nvptx = try gpa.create(NvPtx); + nvptx.* = .{ + .base = .{ + .tag = .nvptx, + .options = options, + .file = null, + .allocator = gpa, + }, + .llvm_object = undefined, + }; + + switch (options.target.cpu.arch) { + .nvptx, .nvptx64 => {}, + else => return error.TODOArchNotSupported, + } + + switch (options.target.os.tag) { + // TODO: does it also work with nvcl ? + .cuda => {}, + else => return error.TODOOsNotSupported, + } + + return nvptx; +} + +pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*NvPtx { + if (!build_options.have_llvm) @panic("nvptx target requires a zig compiler with llvm enabled."); + if (!options.use_llvm) return error.TODOArchNotSupported; + assert(options.object_format == .nvptx); + + const nvptx = try createEmpty(allocator, options); + errdefer nvptx.base.destroy(); + log.info("Opening .ptx target file {s}", .{sub_path}); + nvptx.llvm_object = try LlvmObject.create(allocator, options); + return nvptx; +} + +pub fn deinit(self: *NvPtx) void { + if (!build_options.have_llvm) return; + self.llvm_object.destroy(self.base.allocator); +} + +pub fn updateFunc(self: *NvPtx, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { + if (!build_options.have_llvm) return; + try self.llvm_object.updateFunc(module, func, air, liveness); +} + +pub fn updateDecl(self: *NvPtx, module: *Module, decl: *Module.Decl) !void { + if (!build_options.have_llvm) return; + return self.llvm_object.updateDecl(module, decl); +} + +pub fn updateDeclExports( + self: *NvPtx, + module: *Module, + decl: *const Module.Decl, + exports: []const *Module.Export, +) !void { + if (!build_options.have_llvm) return; + if (build_options.skip_non_native and builtin.object_format != .nvptx) { + @panic("Attempted to compile for object format that was disabled by build configuration"); + } + return self.llvm_object.updateDeclExports(module, decl, exports); +} + +pub fn freeDecl(self: *NvPtx, decl: *Module.Decl) void { + if (!build_options.have_llvm) return; + return self.llvm_object.freeDecl(decl); +} + +pub fn flush(self: *NvPtx, comp: *Compilation) !void { + return self.flushModule(comp); +} + +pub fn flushModule(self: *NvPtx, comp: *Compilation) !void { + if (!build_options.have_llvm) return; + if (build_options.skip_non_native) { + @panic("Attempted to compile for architecture that was disabled by build configuration"); + } + const tracy = trace(@src()); + defer tracy.end(); + + var hack_comp = comp; + if (comp.bin_file.options.emit) |emit| { + hack_comp.emit_asm = .{ + .directory = emit.directory, + .basename = comp.bin_file.intermediary_basename.?, + }; + hack_comp.bin_file.options.emit = null; + } + + return try self.llvm_object.flushModule(hack_comp); +} diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp index b3c578d95a..36f136c77f 100644 --- a/src/stage1/all_types.hpp +++ b/src/stage1/all_types.hpp @@ -83,7 +83,8 @@ enum CallingConvention { CallingConventionAPCS, CallingConventionAAPCS, CallingConventionAAPCSVFP, - CallingConventionSysV + CallingConventionSysV, + CallingConventionPtxKernel }; // Stage 1 supports only the generic address space diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp index dfe7452cfc..0dcf1fcc06 100644 --- a/src/stage1/analyze.cpp +++ b/src/stage1/analyze.cpp @@ -991,6 +991,7 @@ const char *calling_convention_name(CallingConvention cc) { case CallingConventionAAPCSVFP: return "AAPCSVFP"; case CallingConventionInline: return "Inline"; case CallingConventionSysV: return "SysV"; + case CallingConventionPtxKernel: return "PtxKernel"; } zig_unreachable(); } @@ -1000,6 +1001,7 @@ bool calling_convention_allows_zig_types(CallingConvention cc) { case CallingConventionUnspecified: case CallingConventionAsync: case CallingConventionInline: + case CallingConventionPtxKernel: return true; case CallingConventionC: case CallingConventionNaked: @@ -2006,6 +2008,15 @@ Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_ case CallingConventionSysV: if (g->zig_target->arch != ZigLLVM_x86_64) allowed_platforms = "x86_64"; + break; + case CallingConventionPtxKernel: + if (g->zig_target->arch != ZigLLVM_nvptx + && g->zig_target->arch != ZigLLVM_nvptx64) + { + allowed_platforms = "nvptx and nvptx64"; + } + break; + } if (allowed_platforms != nullptr) { add_node_error(g, source_node, buf_sprintf( @@ -3827,6 +3838,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { case CallingConventionAAPCS: case CallingConventionAAPCSVFP: case CallingConventionSysV: + case CallingConventionPtxKernel: add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name), GlobalLinkageIdStrong, fn_cc); break; diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 154e982ff9..4e9d6313db 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -209,6 +209,11 @@ static ZigLLVM_CallingConv get_llvm_cc(CodeGen *g, CallingConvention cc) { case CallingConventionSysV: assert(g->zig_target->arch == ZigLLVM_x86_64); return ZigLLVM_X86_64_SysV; + case CallingConventionPtxKernel: + assert(g->zig_target->arch == ZigLLVM_nvptx || + g->zig_target->arch == ZigLLVM_nvptx64); + return ZigLLVM_PTX_Kernel; + } zig_unreachable(); } @@ -354,6 +359,7 @@ static bool cc_want_sret_attr(CallingConvention cc) { case CallingConventionAAPCS: case CallingConventionAAPCSVFP: case CallingConventionSysV: + case CallingConventionPtxKernel: return true; case CallingConventionAsync: case CallingConventionUnspecified: diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 5694db22ee..be6226313f 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -11666,6 +11666,7 @@ static Stage1AirInst *ir_analyze_instruction_export(IrAnalyze *ira, Stage1ZirIns case CallingConventionAAPCS: case CallingConventionAAPCSVFP: case CallingConventionSysV: + case CallingConventionPtxKernel: add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc); fn_entry->section_name = section_name; break; From f2a82bafae414e9b454b77f6d0bcf768cfa9089b Mon Sep 17 00:00:00 2001 From: praschke Date: Sat, 27 Nov 2021 16:40:26 +0000 Subject: [PATCH 0040/2031] std: allow tests to use cache and setOutputDir --- lib/std/build.zig | 50 ++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 104aceea76..ca1c708583 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -2862,40 +2862,36 @@ pub const LibExeObjStep = struct { }); } - if (self.kind == .@"test") { - _ = try builder.execFromStep(zig_args.items, step); - } else { - try zig_args.append("--enable-cache"); + try zig_args.append("--enable-cache"); - const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step); - const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n"); + const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step); + const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n"); - if (self.output_dir) |output_dir| { - var src_dir = try std.fs.cwd().openDir(build_output_dir, .{ .iterate = true }); - defer src_dir.close(); + if (self.output_dir) |output_dir| { + var src_dir = try std.fs.cwd().openDir(build_output_dir, .{ .iterate = true }); + defer src_dir.close(); - // Create the output directory if it doesn't exist. - try std.fs.cwd().makePath(output_dir); + // Create the output directory if it doesn't exist. + try std.fs.cwd().makePath(output_dir); - var dest_dir = try std.fs.cwd().openDir(output_dir, .{}); - defer dest_dir.close(); + var dest_dir = try std.fs.cwd().openDir(output_dir, .{}); + defer dest_dir.close(); - var it = src_dir.iterate(); - while (try it.next()) |entry| { - // The compiler can put these files into the same directory, but we don't - // want to copy them over. - if (mem.eql(u8, entry.name, "stage1.id") or - mem.eql(u8, entry.name, "llvm-ar.id") or - mem.eql(u8, entry.name, "libs.txt") or - mem.eql(u8, entry.name, "builtin.zig") or - mem.eql(u8, entry.name, "zld.id") or - mem.eql(u8, entry.name, "lld.id")) continue; + var it = src_dir.iterate(); + while (try it.next()) |entry| { + // The compiler can put these files into the same directory, but we don't + // want to copy them over. + if (mem.eql(u8, entry.name, "stage1.id") or + mem.eql(u8, entry.name, "llvm-ar.id") or + mem.eql(u8, entry.name, "libs.txt") or + mem.eql(u8, entry.name, "builtin.zig") or + mem.eql(u8, entry.name, "zld.id") or + mem.eql(u8, entry.name, "lld.id")) continue; - _ = try src_dir.updateFile(entry.name, dest_dir, entry.name, .{}); - } - } else { - self.output_dir = build_output_dir; + _ = try src_dir.updateFile(entry.name, dest_dir, entry.name, .{}); } + } else { + self.output_dir = build_output_dir; } // This will ensure all output filenames will now have the output_dir available! From d4c3475f3dce8ecf81ba2baaf97b6d25426919ec Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 5 Feb 2022 12:44:32 +0100 Subject: [PATCH 0041/2031] stage2 ARM: clarify usage of unfreezeRegs in airSliceElemVal --- src/arch/arm/CodeGen.zig | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 3d334656a1..3ab395f069 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1247,7 +1247,6 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}), }; self.register_manager.freezeRegs(&.{base_mcv.register}); - defer self.register_manager.unfreezeRegs(&.{base_mcv.register}); switch (elem_size) { 1, 4 => { @@ -1283,6 +1282,8 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { } }, }); + self.register_manager.unfreezeRegs(&.{base_mcv.register}); + break :result dst_mcv; }, else => { @@ -1291,7 +1292,6 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { const offset_mcv = try self.genArmMulConstant(bin_op.rhs, @intCast(u32, elem_size)); assert(offset_mcv == .register); // result of multiplication should always be register self.register_manager.freezeRegs(&.{offset_mcv.register}); - defer self.register_manager.unfreezeRegs(&.{offset_mcv.register}); const addr_reg = try self.register_manager.allocReg(null); self.register_manager.freezeRegs(&.{addr_reg}); @@ -1299,11 +1299,9 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { try self.genArmBinOpCode(addr_reg, base_mcv, offset_mcv, false, .add, .unsigned); - // I know we will unfreeze these registers at the end of - // the scope of :result. However, at this point in time, - // neither the base register nor the offset register - // contains any valuable data anymore. In order to reduce - // register pressure, unfreeze them prematurely + // At this point in time, neither the base register + // nor the offset register contains any valuable data + // anymore. self.register_manager.unfreezeRegs(&.{ base_mcv.register, offset_mcv.register }); try self.load(dst_mcv, .{ .register = addr_reg }, slice_ptr_field_type); From 4b3b487627d71fa082b0316383344b49c95bab0e Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 5 Feb 2022 17:13:11 +0100 Subject: [PATCH 0042/2031] stage2 regalloc: Introduce error.OutOfRegisters --- src/arch/aarch64/CodeGen.zig | 7 +++ src/arch/arm/CodeGen.zig | 7 +++ src/arch/riscv64/CodeGen.zig | 7 +++ src/arch/x86_64/CodeGen.zig | 7 +++ src/register_manager.zig | 101 +++++++++++++++++++++++++---------- 5 files changed, 100 insertions(+), 29 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 6202d2e74f..2b8c5e62d4 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -31,6 +31,7 @@ const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; const InnerError = error{ OutOfMemory, CodegenFail, + OutOfRegisters, }; gpa: Allocator, @@ -274,6 +275,9 @@ pub fn generate( var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, + error.OutOfRegisters => return FnResult{ + .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + }, else => |e| return e, }; defer call_info.deinit(&function); @@ -285,6 +289,9 @@ pub fn generate( function.gen() catch |err| switch (err) { error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, + error.OutOfRegisters => return FnResult{ + .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + }, else => |e| return e, }; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 3ab395f069..c87f750831 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -31,6 +31,7 @@ const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; const InnerError = error{ OutOfMemory, CodegenFail, + OutOfRegisters, }; gpa: Allocator, @@ -279,6 +280,9 @@ pub fn generate( var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, + error.OutOfRegisters => return FnResult{ + .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + }, else => |e| return e, }; defer call_info.deinit(&function); @@ -290,6 +294,9 @@ pub fn generate( function.gen() catch |err| switch (err) { error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, + error.OutOfRegisters => return FnResult{ + .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + }, else => |e| return e, }; diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 9e850bd751..612ff78bd6 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -31,6 +31,7 @@ const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; const InnerError = error{ OutOfMemory, CodegenFail, + OutOfRegisters, }; gpa: Allocator, @@ -280,6 +281,9 @@ pub fn generate( var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, + error.OutOfRegisters => return FnResult{ + .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + }, else => |e| return e, }; defer call_info.deinit(&function); @@ -291,6 +295,9 @@ pub fn generate( function.gen() catch |err| switch (err) { error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, + error.OutOfRegisters => return FnResult{ + .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + }, else => |e| return e, }; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index b3a292a7f4..e464dd3cf9 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -31,6 +31,7 @@ const Zir = @import("../../Zir.zig"); const InnerError = error{ OutOfMemory, CodegenFail, + OutOfRegisters, }; const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs); @@ -308,6 +309,9 @@ pub fn generate( var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, + error.OutOfRegisters => return FnResult{ + .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + }, else => |e| return e, }; defer call_info.deinit(&function); @@ -319,6 +323,9 @@ pub fn generate( function.gen() catch |err| switch (err) { error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, + error.OutOfRegisters => return FnResult{ + .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + }, else => |e| return e, }; diff --git a/src/register_manager.zig b/src/register_manager.zig index 63a0efbad8..81c9fa5734 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -12,6 +12,17 @@ const expectEqualSlices = std.testing.expectEqualSlices; const log = std.log.scoped(.register_manager); +pub const AllocateRegistersError = error{ + /// No registers are available anymore + OutOfRegisters, + /// Can happen when spilling an instruction in codegen runs out of + /// memory, so we propagate that error + OutOfMemory, + /// Can happen when spilling an instruction triggers a codegen + /// error, so we propagate that error + CodegenFail, +}; + pub fn RegisterManager( comptime Function: type, comptime Register: type, @@ -168,8 +179,9 @@ pub fn RegisterManager( self: *Self, comptime count: comptime_int, insts: [count]?Air.Inst.Index, - ) ![count]Register { + ) AllocateRegistersError![count]Register { comptime assert(count > 0 and count <= callee_preserved_regs.len); + if (count > callee_preserved_regs.len - @popCount(FreeRegInt, self.frozen_registers)) return error.OutOfRegisters; const result = self.tryAllocRegs(count, insts) orelse blk: { // We'll take over the first count registers. Spill @@ -214,14 +226,14 @@ pub fn RegisterManager( /// Allocates a register and optionally tracks it with a /// corresponding instruction. - pub fn allocReg(self: *Self, inst: ?Air.Inst.Index) !Register { + pub fn allocReg(self: *Self, inst: ?Air.Inst.Index) AllocateRegistersError!Register { return (try self.allocRegs(1, .{inst}))[0]; } /// Spills the register if it is currently allocated. If a /// corresponding instruction is passed, will also track this /// register. - pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) !void { + pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocateRegistersError!void { const index = reg.allocIndex() orelse return; self.markRegAllocated(reg); @@ -317,6 +329,13 @@ fn MockFunction(comptime Register: type) type { _ = inst; try self.spilled.append(self.allocator, reg); } + + pub fn genAdd(self: *Self, res: Register, lhs: Register, rhs: Register) !void { + _ = self; + _ = res; + _ = lhs; + _ = rhs; + } }; } @@ -431,7 +450,9 @@ test "tryAllocRegs" { try expect(function.register_manager.isRegAllocated(.r3)); } -test "allocRegs" { +test "allocRegs: normal usage" { + // TODO: convert this into a decltest once that is supported + const allocator = std.testing.allocator; var function = MockFunction2{ @@ -439,35 +460,57 @@ test "allocRegs" { }; defer function.deinit(); - const mock_instruction: Air.Inst.Index = 1; - - try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, try function.register_manager.allocRegs(3, .{ - mock_instruction, - mock_instruction, - mock_instruction, - })); - - try expect(function.register_manager.isRegAllocated(.r0)); - try expect(function.register_manager.isRegAllocated(.r1)); - try expect(function.register_manager.isRegAllocated(.r2)); - try expect(!function.register_manager.isRegAllocated(.r3)); - - // Frozen registers - function.register_manager.freeReg(.r0); - function.register_manager.freeReg(.r2); - function.register_manager.freeReg(.r3); { - function.register_manager.freezeRegs(&.{.r1}); - defer function.register_manager.unfreezeRegs(&.{.r1}); + const result_reg: MockRegister2 = .r1; - try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, try function.register_manager.allocRegs(3, .{ null, null, null })); + // The result register is known and fixed at this point, we + // don't want to accidentally allocate lhs or rhs to the + // result register, this is why we freeze it. + // + // Using defer unfreeze right after freeze is a good idea in + // most cases as you probably are using the frozen registers + // in the remainder of this scope and don't need to use it + // after the end of this scope. However, in some situations, + // it may make sense to manually unfreeze registers before the + // end of the scope when you are certain that they don't + // contain any valuable data anymore and can be reused. For an + // example of that, see `selectively reducing register + // pressure`. + function.register_manager.freezeRegs(&.{result_reg}); + defer function.register_manager.unfreezeRegs(&.{result_reg}); + + const regs = try function.register_manager.allocRegs(2, .{ null, null }); + try function.genAdd(result_reg, regs[0], regs[1]); } - try expect(!function.register_manager.frozenRegsExist()); +} - try expect(function.register_manager.isRegAllocated(.r0)); - try expect(function.register_manager.isRegAllocated(.r1)); - try expect(function.register_manager.isRegAllocated(.r2)); - try expect(function.register_manager.isRegAllocated(.r3)); +test "allocRegs: selectively reducing register pressure" { + // TODO: convert this into a decltest once that is supported + + const allocator = std.testing.allocator; + + var function = MockFunction2{ + .allocator = allocator, + }; + defer function.deinit(); + + { + const result_reg: MockRegister2 = .r1; + + function.register_manager.freezeRegs(&.{result_reg}); + defer function.register_manager.unfreezeRegs(&.{result_reg}); + + // Here, we don't defer unfreeze because we manually unfreeze + // after genAdd + const regs = try function.register_manager.allocRegs(2, .{ null, null }); + function.register_manager.freezeRegs(&.{result_reg}); + + try function.genAdd(result_reg, regs[0], regs[1]); + function.register_manager.unfreezeRegs(®s); + + const extra_summand_reg = try function.register_manager.allocReg(null); + try function.genAdd(result_reg, result_reg, extra_summand_reg); + } } test "getReg" { From f132f426b9a11774323eafc669da0c9731deb85b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 5 Feb 2022 19:09:20 +0100 Subject: [PATCH 0043/2031] x86_64: add distinct MCValue representing symbol index in the linker For PIE targets, we defer getting an address of value until the linker has allocated all atoms and performed the relocations. In codegen, we represent this via `MCValue.linker_sym_index` value. --- src/arch/x86_64/CodeGen.zig | 123 ++++++++++++++++++++++-------------- 1 file changed, 76 insertions(+), 47 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index e464dd3cf9..e5c7c99501 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -118,6 +118,10 @@ pub const MCValue = union(enum) { /// The value is in memory at a hard-coded address. /// If the type is a pointer, it means the pointer address is at this memory location. memory: u64, + /// The value is in memory but not allocated an address yet by the linker, so we store + /// the symbol index instead. + /// If the type is a pointer, it means the pointer is the symbol. + linker_sym_index: u32, /// The value is one of the stack variables. /// If the type is a pointer, it means the pointer address is in the stack at this offset. stack_offset: i32, @@ -1686,8 +1690,10 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo else => return self.fail("TODO implement loading from register into {}", .{dst_mcv}), } }, - .memory => |addr| { - const reg = try self.copyToTmpRegister(ptr_ty, .{ .memory = addr }); + .memory, + .linker_sym_index, + => { + const reg = try self.copyToTmpRegister(ptr_ty, ptr); try self.load(dst_mcv, .{ .register = reg }, ptr_ty); }, .stack_offset => { @@ -1817,27 +1823,33 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type }, } }, - .memory => |addr| { + .linker_sym_index, + .memory, + => { value.freezeIfRegister(&self.register_manager); defer value.unfreezeIfRegister(&self.register_manager); const addr_reg: Register = blk: { - if (self.bin_file.options.pie) { - const addr_reg = try self.register_manager.allocReg(null); - _ = try self.addInst(.{ - .tag = .lea, - .ops = (Mir.Ops{ - .reg1 = addr_reg.to64(), - .flags = 0b10, - }).encode(), - .data = .{ .got_entry = @truncate(u32, addr) }, - }); - break :blk addr_reg; - } else { - // TODO: in case the address fits in an imm32 we can use [ds:imm32] - // instead of wasting an instruction copying the address to a register - const addr_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = addr }); - break :blk addr_reg; + switch (ptr) { + .linker_sym_index => |sym_index| { + const addr_reg = try self.register_manager.allocReg(null); + _ = try self.addInst(.{ + .tag = .lea, + .ops = (Mir.Ops{ + .reg1 = addr_reg.to64(), + .flags = 0b10, + }).encode(), + .data = .{ .got_entry = sym_index }, + }); + break :blk addr_reg; + }, + .memory => |addr| { + // TODO: in case the address fits in an imm32 we can use [ds:imm32] + // instead of wasting an instruction copying the address to a register + const addr_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = addr }); + break :blk addr_reg; + }, + else => unreachable, } }; @@ -2148,6 +2160,9 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .embedded_in_code, .memory => { return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{}); }, + .linker_sym_index => { + return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{}); + }, .stack_offset => |off| { if (off > math.maxInt(i32)) { return self.fail("stack offset too large", .{}); @@ -2232,6 +2247,9 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .embedded_in_code, .memory, .stack_offset => { return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{}); }, + .linker_sym_index => { + return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{}); + }, .compare_flags_unsigned => { return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{}); }, @@ -2243,6 +2261,9 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .embedded_in_code, .memory => { return self.fail("TODO implement x86 ADD/SUB/CMP destination memory", .{}); }, + .linker_sym_index => { + return self.fail("TODO implement x86 ADD/SUB/CMP destination symbol at index", .{}); + }, } } @@ -2296,6 +2317,9 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! .embedded_in_code, .memory, .stack_offset => { return self.fail("TODO implement x86 multiply source memory", .{}); }, + .linker_sym_index => { + return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); + }, .compare_flags_unsigned => { return self.fail("TODO implement x86 multiply source compare flag (unsigned)", .{}); }, @@ -2334,6 +2358,9 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! .embedded_in_code, .memory, .stack_offset => { return self.fail("TODO implement x86 multiply source memory", .{}); }, + .linker_sym_index => { + return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); + }, .compare_flags_unsigned => { return self.fail("TODO implement x86 multiply source compare flag (unsigned)", .{}); }, @@ -2345,6 +2372,9 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! .embedded_in_code, .memory => { return self.fail("TODO implement x86 multiply destination memory", .{}); }, + .linker_sym_index => { + return self.fail("TODO implement x86 multiply destination symbol at index in linker", .{}); + }, } } @@ -2448,6 +2478,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { .dead => unreachable, .embedded_in_code => unreachable, .memory => unreachable, + .linker_sym_index => unreachable, .compare_flags_signed => unreachable, .compare_flags_unsigned => unreachable, } @@ -2508,10 +2539,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { if (self.air.value(callee)) |func_value| { if (func_value.castTag(.function)) |func_payload| { const func = func_payload.data; - // TODO I'm hacking my way through here by repurposing .memory for storing - // index to the GOT target symbol index. try self.genSetReg(Type.initTag(.usize), .rax, .{ - .memory = func.owner_decl.link.macho.local_sym_index, + .linker_sym_index = func.owner_decl.link.macho.local_sym_index, }); // callq *%rax _ = try self.addInst(.{ @@ -3547,6 +3576,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro }, .memory, .embedded_in_code, + .linker_sym_index, => { if (ty.abiSize(self.target.*) <= 8) { const reg = try self.copyToTmpRegister(ty, mcv); @@ -3952,29 +3982,28 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .data = undefined, }); }, + .linker_sym_index => |sym_index| { + _ = try self.addInst(.{ + .tag = .lea, + .ops = (Mir.Ops{ + .reg1 = reg, + .flags = 0b10, + }).encode(), + .data = .{ .got_entry = sym_index }, + }); + // MOV reg, [reg] + _ = try self.addInst(.{ + .tag = .mov, + .ops = (Mir.Ops{ + .reg1 = reg, + .reg2 = reg, + .flags = 0b01, + }).encode(), + .data = .{ .imm = 0 }, + }); + }, .memory => |x| { - // TODO can we move this entire logic into Emit.zig like with aarch64? - if (self.bin_file.options.pie) { - // TODO we should flag up `x` as GOT symbol entry explicitly rather than as a hack. - _ = try self.addInst(.{ - .tag = .lea, - .ops = (Mir.Ops{ - .reg1 = reg, - .flags = 0b10, - }).encode(), - .data = .{ .got_entry = @truncate(u32, x) }, - }); - // MOV reg, [reg] - _ = try self.addInst(.{ - .tag = .mov, - .ops = (Mir.Ops{ - .reg1 = reg, - .reg2 = reg, - .flags = 0b01, - }).encode(), - .data = .{ .imm = 0 }, - }); - } else if (x <= math.maxInt(i32)) { + if (x <= math.maxInt(i32)) { // mov reg, [ds:imm32] _ = try self.addInst(.{ .tag = .mov, @@ -4285,9 +4314,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; return MCValue{ .memory = got_addr }; } else if (self.bin_file.cast(link.File.MachO)) |_| { - // TODO I'm hacking my way through here by repurposing .memory for storing - // index to the GOT target symbol index. - return MCValue{ .memory = decl.link.macho.local_sym_index }; + // Because MachO is PIE-always-on, we defer memory address resolution until + // the linker has enough info to perform relocations. + return MCValue{ .linker_sym_index = decl.link.macho.local_sym_index }; } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; return MCValue{ .memory = got_addr }; From 6f87f49f3ddf4fb56f8b4e2ff86d1020ab2dd690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20L=C3=B6thberg?= Date: Sun, 6 Feb 2022 03:37:55 +0100 Subject: [PATCH 0044/2031] CLI: remove remainders of --verbose-ast and --verbose-tokenize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These options were removed in 5e63baae8 (CLI: remove --verbose-ast and --verbose-tokenize, 2021-06-09) but some remainders were left in. Signed-off-by: Johannes Löthberg --- lib/std/build.zig | 6 ------ lib/std/special/build_runner.zig | 6 ------ src/stage1/zig0.cpp | 2 -- 3 files changed, 14 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index ca1c708583..05f36f5714 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -34,8 +34,6 @@ pub const Builder = struct { available_options_map: AvailableOptionsMap, available_options_list: ArrayList(AvailableOption), verbose: bool, - verbose_tokenize: bool, - verbose_ast: bool, verbose_link: bool, verbose_cc: bool, verbose_air: bool, @@ -172,8 +170,6 @@ pub const Builder = struct { .cache_root = try fs.path.relative(allocator, build_root, cache_root), .global_cache_root = global_cache_root, .verbose = false, - .verbose_tokenize = false, - .verbose_ast = false, .verbose_link = false, .verbose_cc = false, .verbose_air = false, @@ -2405,8 +2401,6 @@ pub const LibExeObjStep = struct { try zig_args.append(log_scope); } - if (builder.verbose_tokenize) zig_args.append("--verbose-tokenize") catch unreachable; - if (builder.verbose_ast) zig_args.append("--verbose-ast") catch unreachable; if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable; if (builder.verbose_air) zig_args.append("--verbose-air") catch unreachable; if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable; diff --git a/lib/std/special/build_runner.zig b/lib/std/special/build_runner.zig index 2a64861cf9..eb83ef8fcd 100644 --- a/lib/std/special/build_runner.zig +++ b/lib/std/special/build_runner.zig @@ -147,10 +147,6 @@ pub fn main() !void { std.debug.print("Expected argument after --glibc-runtimes\n\n", .{}); return usageAndErr(builder, false, stderr_stream); }; - } else if (mem.eql(u8, arg, "--verbose-tokenize")) { - builder.verbose_tokenize = true; - } else if (mem.eql(u8, arg, "--verbose-ast")) { - builder.verbose_ast = true; } else if (mem.eql(u8, arg, "--verbose-link")) { builder.verbose_link = true; } else if (mem.eql(u8, arg, "--verbose-air")) { @@ -310,8 +306,6 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void \\ --cache-dir [path] Override path to zig cache directory \\ --zig-lib-dir [arg] Override path to Zig lib directory \\ --debug-log [scope] Enable debugging the compiler - \\ --verbose-tokenize Enable compiler debug output for tokenization - \\ --verbose-ast Enable compiler debug output for parsing into an AST \\ --verbose-link Enable compiler debug output for linking \\ --verbose-air Enable compiler debug output for Zig AIR \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR diff --git a/src/stage1/zig0.cpp b/src/stage1/zig0.cpp index 439f2bd1d4..f675fa1061 100644 --- a/src/stage1/zig0.cpp +++ b/src/stage1/zig0.cpp @@ -51,8 +51,6 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " --strip exclude debug symbols\n" " -target [name] -- see the targets command\n" " -mcpu [cpu] specify target CPU and feature set\n" - " --verbose-tokenize enable compiler debug output for tokenization\n" - " --verbose-ast enable compiler debug output for AST parsing\n" " --verbose-ir enable compiler debug output for Zig IR\n" " --verbose-llvm-ir enable compiler debug output for LLVM IR\n" " --verbose-cimport enable compiler debug output for C imports\n" From 4468abfc424ba645413ee076e5e2e370aa807bcc Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 5 Feb 2022 23:17:34 +0100 Subject: [PATCH 0045/2031] stage2 ARM: enable a handful of passing behavior tests --- test/behavior/align.zig | 16 ---------------- test/behavior/alignof.zig | 1 - test/behavior/bitcast.zig | 6 ------ test/behavior/bugs/1025.zig | 1 - test/behavior/bugs/1500.zig | 1 - test/behavior/generics.zig | 2 -- .../namespace_depends_on_compile_var.zig | 1 - 7 files changed, 28 deletions(-) diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 40b0304371..7871dec24b 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -22,14 +22,10 @@ test "global variable alignment" { } test "default alignment allows unspecified in type syntax" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - try expect(*u32 == *align(@alignOf(u32)) u32); } test "implicitly decreasing pointer alignment" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - const a: u32 align(4) = 3; const b: u32 align(8) = 4; try expect(addUnaligned(&a, &b) == 7); @@ -40,8 +36,6 @@ fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 { } test "@alignCast pointers" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - var x: u32 align(4) = 1; expectsOnly1(&x); try expect(x == 2); @@ -54,8 +48,6 @@ fn expects4(x: *align(4) u32) void { } test "alignment of structs" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - try expect(@alignOf(struct { a: i32, b: *i32, @@ -63,15 +55,11 @@ test "alignment of structs" { } test "alignment of >= 128-bit integer type" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - try expect(@alignOf(u128) == 16); try expect(@alignOf(u129) == 16); } test "alignment of struct with 128-bit field" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - try expect(@alignOf(struct { x: u128, }) == 16); @@ -84,8 +72,6 @@ test "alignment of struct with 128-bit field" { } test "size of extern struct with 128-bit field" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - try expect(@sizeOf(extern struct { x: u128, y: u8, @@ -100,8 +86,6 @@ test "size of extern struct with 128-bit field" { } test "@ptrCast preserves alignment of bigger source" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - var x: u32 align(16) = 1234; const ptr = @ptrCast(*u8, &x); try expect(@TypeOf(ptr) == *align(16) u8); diff --git a/test/behavior/alignof.zig b/test/behavior/alignof.zig index 54e09877e1..749855db52 100644 --- a/test/behavior/alignof.zig +++ b/test/behavior/alignof.zig @@ -22,7 +22,6 @@ test "@alignOf(T) before referencing T" { test "comparison of @alignOf(T) against zero" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; { const T = struct { x: u32 }; diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index a4a555057e..d56e3c1c53 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -76,7 +76,6 @@ test "@bitCast packed structs at runtime and comptime" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const Full = packed struct { number: u16, @@ -113,7 +112,6 @@ test "@bitCast extern structs at runtime and comptime" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const Full = extern struct { number: u16, @@ -147,7 +145,6 @@ test "bitcast packed struct to integer and back" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const LevelUpMove = packed struct { move_id: u9, @@ -184,7 +181,6 @@ test "bitcast packed struct literal to byte" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const Foo = packed struct { value: u8, @@ -198,7 +194,6 @@ test "comptime bitcast used in expression has the correct type" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const Foo = packed struct { value: u8, @@ -211,7 +206,6 @@ test "bitcast passed as tuple element" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { fn foo(args: anytype) !void { diff --git a/test/behavior/bugs/1025.zig b/test/behavior/bugs/1025.zig index fa72e522de..33ceb9fedf 100644 --- a/test/behavior/bugs/1025.zig +++ b/test/behavior/bugs/1025.zig @@ -10,7 +10,6 @@ fn getA() A { test "bug 1025" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const a = getA(); try @import("std").testing.expect(a.B == u8); } diff --git a/test/behavior/bugs/1500.zig b/test/behavior/bugs/1500.zig index d224bfcc4a..eb2a06b7fb 100644 --- a/test/behavior/bugs/1500.zig +++ b/test/behavior/bugs/1500.zig @@ -7,7 +7,6 @@ const B = *const fn (A) void; test "allow these dependencies" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; var a: A = undefined; var b: B = undefined; diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index 72f5fd9594..1942e82340 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -166,7 +166,6 @@ test "generic fn keeps non-generic parameter types" { test "array of generic fns" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; try expect(foos[0](true)); try expect(!foos[1](true)); @@ -186,7 +185,6 @@ fn foo2(arg: anytype) bool { test "generic struct" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; var a1 = GenNode(i32){ .value = 13, diff --git a/test/behavior/namespace_depends_on_compile_var.zig b/test/behavior/namespace_depends_on_compile_var.zig index 6b79df1a81..db1dfaf308 100644 --- a/test/behavior/namespace_depends_on_compile_var.zig +++ b/test/behavior/namespace_depends_on_compile_var.zig @@ -4,7 +4,6 @@ const expect = std.testing.expect; test "namespace depends on compile var" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (some_namespace.a_bool) { try expect(some_namespace.a_bool); From 556f0ce5bfb962764bb68d9d9c16765cf9e1b9ba Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 5 Feb 2022 17:41:03 +0100 Subject: [PATCH 0046/2031] stage2: add new Decl subtype, ExternFn `ExternFn` will contain a maybe-lib-name if it was defined with the `extern` keyword like so ```zig extern "c" fn write(usize, usize, usize) usize; ``` `lib_name` will live as long as `ExternFn` decl does. --- src/Module.zig | 36 ++++++++++++++++++++++++++++++++---- src/Sema.zig | 22 ++++++++++++++++++---- src/arch/aarch64/CodeGen.zig | 11 +++++++++-- src/arch/wasm/CodeGen.zig | 6 +++--- src/arch/x86_64/CodeGen.zig | 11 +++++++++-- src/codegen/c.zig | 8 ++++---- src/codegen/llvm.zig | 4 ++-- src/value.zig | 19 +++++++++++++------ 8 files changed, 90 insertions(+), 27 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index e2e2505927..4fd3ee5c01 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -501,6 +501,10 @@ pub const Decl = struct { } pub fn clearValues(decl: *Decl, gpa: Allocator) void { + if (decl.getExternFn()) |extern_fn| { + extern_fn.deinit(gpa); + gpa.destroy(extern_fn); + } if (decl.getFunction()) |func| { func.deinit(gpa); gpa.destroy(func); @@ -690,6 +694,13 @@ pub const Decl = struct { return func; } + pub fn getExternFn(decl: *const Decl) ?*ExternFn { + if (!decl.owns_tv) return null; + const extern_fn = (decl.val.castTag(.extern_fn) orelse return null).data; + assert(extern_fn.owner_decl == decl); + return extern_fn; + } + pub fn getVariable(decl: *Decl) ?*Var { if (!decl.owns_tv) return null; const variable = (decl.val.castTag(.variable) orelse return null).data; @@ -1320,9 +1331,26 @@ pub const Opaque = struct { } }; +/// Some extern function struct memory is owned by the Decl's TypedValue.Managed +/// arena allocator. +pub const ExternFn = struct { + /// The Decl that corresponds to the function itself. + owner_decl: *Decl, + /// Library name if specified. + /// For example `extern "c" fn write(...) usize` would have 'c' as library name. + /// Allocated with Module's allocator; outlives the ZIR code. + lib_name: ?[*:0]const u8, + + pub fn deinit(extern_fn: *ExternFn, gpa: Allocator) void { + if (extern_fn.lib_name) |lib_name| { + gpa.free(mem.sliceTo(lib_name, 0)); + } + } +}; + /// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. -/// Extern functions do not have this data structure; they are represented by -/// the `Decl` only, with a `Value` tag of `extern_fn`. +/// Extern functions do not have this data structure; they are represented by `ExternFn` +/// instead. pub const Fn = struct { /// The Decl that corresponds to the function itself. owner_decl: *Decl, @@ -3768,8 +3796,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { } }, .extern_fn => { - const owner_decl = decl_tv.val.castTag(.extern_fn).?.data; - if (decl == owner_decl) { + const extern_fn = decl_tv.val.castTag(.extern_fn).?.data; + if (extern_fn.owner_decl == decl) { decl.owns_tv = true; queue_linker_work = true; is_extern = true; diff --git a/src/Sema.zig b/src/Sema.zig index c4b3ad8c33..48d7545a75 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5614,10 +5614,24 @@ fn funcCommon( } if (is_extern) { - return sema.addConstant( - fn_ty, - try Value.Tag.extern_fn.create(sema.arena, sema.owner_decl), - ); + const new_extern_fn = try sema.gpa.create(Module.ExternFn); + errdefer sema.gpa.destroy(new_extern_fn); + + const lib_name: ?[*:0]const u8 = if (opt_lib_name) |lib_name| blk: { + break :blk try sema.gpa.dupeZ(u8, lib_name); + } else null; + + new_extern_fn.* = Module.ExternFn{ + .owner_decl = sema.owner_decl, + .lib_name = lib_name, + }; + + const extern_fn_payload = try sema.arena.create(Value.Payload.ExternFn); + extern_fn_payload.* = .{ + .base = .{ .tag = .extern_fn }, + .data = new_extern_fn, + }; + return sema.addConstant(fn_ty, Value.initPayload(&extern_fn_payload.base)); } if (!has_body) { diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 6202d2e74f..0292497df7 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -1574,8 +1574,15 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { .data = .{ .reg = .x30 }, }); } else if (func_value.castTag(.extern_fn)) |func_payload| { - const decl = func_payload.data; - const n_strx = try macho_file.addExternFn(mem.sliceTo(decl.name, 0)); + const extern_fn = func_payload.data; + const decl_name = extern_fn.owner_decl.name; + if (extern_fn.lib_name) |lib_name| { + log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{ + decl_name, + lib_name, + }); + } + const n_strx = try macho_file.addExternFn(mem.sliceTo(decl_name, 0)); _ = try self.addInst(.{ .tag = .call_extern, diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 67aa9a6c88..8e0ffac76b 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -952,7 +952,7 @@ pub const DeclGen = struct { _ = func_payload; return self.fail("TODO wasm backend genDecl function pointer", .{}); } else if (decl.val.castTag(.extern_fn)) |extern_fn| { - const ext_decl = extern_fn.data; + const ext_decl = extern_fn.data.owner_decl; var func_type = try genFunctype(self.gpa, ext_decl.ty, self.target()); func_type.deinit(self.gpa); ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); @@ -978,7 +978,7 @@ pub const DeclGen = struct { switch (ty.zigTypeTag()) { .Fn => { const fn_decl = switch (val.tag()) { - .extern_fn => val.castTag(.extern_fn).?.data, + .extern_fn => val.castTag(.extern_fn).?.data.owner_decl, .function => val.castTag(.function).?.data.owner_decl, else => unreachable, }; @@ -1776,7 +1776,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (func_val.castTag(.function)) |func| { break :blk func.data.owner_decl; } else if (func_val.castTag(.extern_fn)) |ext_fn| { - break :blk ext_fn.data; + break :blk ext_fn.data.owner_decl; } else if (func_val.castTag(.decl_ref)) |decl_ref| { break :blk decl_ref.data; } diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index b3a292a7f4..1104a94112 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2516,8 +2516,15 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { .data = undefined, }); } else if (func_value.castTag(.extern_fn)) |func_payload| { - const decl = func_payload.data; - const n_strx = try macho_file.addExternFn(mem.sliceTo(decl.name, 0)); + const extern_fn = func_payload.data; + const decl_name = extern_fn.owner_decl.name; + if (extern_fn.lib_name) |lib_name| { + log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{ + decl_name, + lib_name, + }); + } + const n_strx = try macho_file.addExternFn(mem.sliceTo(decl_name, 0)); _ = try self.addInst(.{ .tag = .call_extern, .ops = undefined, diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 299581cd48..44b904f031 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -542,8 +542,8 @@ pub const DeclGen = struct { try dg.renderDeclName(func.owner_decl, writer); }, .extern_fn => { - const decl = val.castTag(.extern_fn).?.data; - try dg.renderDeclName(decl, writer); + const extern_fn = val.castTag(.extern_fn).?.data; + try dg.renderDeclName(extern_fn.owner_decl, writer); }, .int_u64, .one => { try writer.writeAll("(("); @@ -681,7 +681,7 @@ pub const DeclGen = struct { return dg.renderDeclValue(writer, ty, val, decl); }, .extern_fn => { - const decl = val.castTag(.extern_fn).?.data; + const decl = val.castTag(.extern_fn).?.data.owner_decl; return dg.renderDeclValue(writer, ty, val, decl); }, else => unreachable, @@ -2442,7 +2442,7 @@ fn airCall(f: *Function, inst: Air.Inst.Index) !CValue { const fn_decl = fn_decl: { const callee_val = f.air.value(pl_op.operand) orelse break :known; break :fn_decl switch (callee_val.tag()) { - .extern_fn => callee_val.castTag(.extern_fn).?.data, + .extern_fn => callee_val.castTag(.extern_fn).?.data.owner_decl, .function => callee_val.castTag(.function).?.data.owner_decl, .decl_ref => callee_val.castTag(.decl_ref).?.data, else => break :known, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 81742d4866..e9457be783 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -622,7 +622,7 @@ pub const DeclGen = struct { _ = func_payload; @panic("TODO llvm backend genDecl function pointer"); } else if (decl.val.castTag(.extern_fn)) |extern_fn| { - _ = try dg.resolveLlvmFunction(extern_fn.data); + _ = try dg.resolveLlvmFunction(extern_fn.data.owner_decl); } else { const target = dg.module.getTarget(); const global = try dg.resolveGlobalDecl(decl); @@ -1410,7 +1410,7 @@ pub const DeclGen = struct { }, .Fn => { const fn_decl = switch (tv.val.tag()) { - .extern_fn => tv.val.castTag(.extern_fn).?.data, + .extern_fn => tv.val.castTag(.extern_fn).?.data.owner_decl, .function => tv.val.castTag(.function).?.data.owner_decl, else => unreachable, }; diff --git a/src/value.zig b/src/value.zig index e444e2daf1..38cba74b0b 100644 --- a/src/value.zig +++ b/src/value.zig @@ -262,9 +262,9 @@ pub const Value = extern union { .int_big_negative, => Payload.BigInt, - .extern_fn, - .decl_ref, - => Payload.Decl, + .extern_fn => Payload.ExternFn, + + .decl_ref => Payload.Decl, .repeated, .eu_payload, @@ -475,7 +475,7 @@ pub const Value = extern union { return Value{ .ptr_otherwise = &new_payload.base }; }, .function => return self.copyPayloadShallow(arena, Payload.Function), - .extern_fn => return self.copyPayloadShallow(arena, Payload.Decl), + .extern_fn => return self.copyPayloadShallow(arena, Payload.ExternFn), .variable => return self.copyPayloadShallow(arena, Payload.Variable), .decl_ref => return self.copyPayloadShallow(arena, Payload.Decl), .decl_ref_mut => return self.copyPayloadShallow(arena, Payload.DeclRefMut), @@ -1803,9 +1803,10 @@ pub const Value = extern union { pub fn pointerDecl(val: Value) ?*Module.Decl { return switch (val.tag()) { .decl_ref_mut => val.castTag(.decl_ref_mut).?.data.decl, - .extern_fn, .decl_ref => val.cast(Payload.Decl).?.data, + .extern_fn => val.castTag(.extern_fn).?.data.owner_decl, .function => val.castTag(.function).?.data.owner_decl, .variable => val.castTag(.variable).?.data.owner_decl, + .decl_ref => val.cast(Payload.Decl).?.data, else => null, }; } @@ -1872,9 +1873,10 @@ pub const Value = extern union { pub fn markReferencedDeclsAlive(val: Value) void { switch (val.tag()) { .decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.markAlive(), - .extern_fn, .decl_ref => return val.cast(Payload.Decl).?.data.markAlive(), + .extern_fn => return val.castTag(.extern_fn).?.data.owner_decl.markAlive(), .function => return val.castTag(.function).?.data.owner_decl.markAlive(), .variable => return val.castTag(.variable).?.data.owner_decl.markAlive(), + .decl_ref => return val.cast(Payload.Decl).?.data.markAlive(), .repeated, .eu_payload, @@ -3148,6 +3150,11 @@ pub const Value = extern union { data: *Module.Fn, }; + pub const ExternFn = struct { + base: Payload, + data: *Module.ExternFn, + }; + pub const Decl = struct { base: Payload, data: *Module.Decl, From db9500a31401c65327a4fd556f50d74ce75fb858 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 6 Feb 2022 09:14:15 +0100 Subject: [PATCH 0047/2031] stage2: handle extern lib name annotation for vars For example, a situation like this is allowed ```zig extern "c" var stderrp: c_int; ``` In this case, `Module.Var` wrapping `stderrp` will have `lib_name` populated with the library name where this import is expected. --- src/Module.zig | 16 ++++++ src/Sema.zig | 134 ++++++++++++++++++++++++++++--------------------- 2 files changed, 93 insertions(+), 57 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 4fd3ee5c01..76997fe95b 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -510,6 +510,7 @@ pub const Decl = struct { gpa.destroy(func); } if (decl.getVariable()) |variable| { + variable.deinit(gpa); gpa.destroy(variable); } if (decl.value_arena) |arena_state| { @@ -694,6 +695,8 @@ pub const Decl = struct { return func; } + /// If the Decl has a value and it is an extern function, returns it, + /// otherwise null. pub fn getExternFn(decl: *const Decl) ?*ExternFn { if (!decl.owns_tv) return null; const extern_fn = (decl.val.castTag(.extern_fn) orelse return null).data; @@ -701,6 +704,8 @@ pub const Decl = struct { return extern_fn; } + /// If the Decl has a value and it is a variable, returns it, + /// otherwise null. pub fn getVariable(decl: *Decl) ?*Var { if (!decl.owns_tv) return null; const variable = (decl.val.castTag(.variable) orelse return null).data; @@ -1469,9 +1474,20 @@ pub const Var = struct { init: Value, owner_decl: *Decl, + /// Library name if specified. + /// For example `extern "c" var stderrp = ...` would have 'c' as library name. + /// Allocated with Module's allocator; outlives the ZIR code. + lib_name: ?[*:0]const u8, + is_extern: bool, is_mutable: bool, is_threadlocal: bool, + + pub fn deinit(variable: *Var, gpa: Allocator) void { + if (variable.lib_name) |lib_name| { + gpa.free(mem.sliceTo(lib_name, 0)); + } + } }; /// The container that structs, enums, unions, and opaques have. diff --git a/src/Sema.zig b/src/Sema.zig index 48d7545a75..0e43a9f522 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5430,6 +5430,69 @@ fn zirFunc( ); } +/// Given a library name, examines if the library name should end up in +/// `link.File.Options.system_libs` table (for example, libc is always +/// specified via dedicated flag `link.File.Options.link_libc` instead), +/// and puts it there if it doesn't exist. +/// It also dupes the library name which can then be saved as part of the +/// respective `Decl` (either `ExternFn` or `Var`). +/// The liveness of the duped library name is tied to liveness of `Module`. +/// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`). +fn handleExternLibName( + sema: *Sema, + block: *Block, + src_loc: LazySrcLoc, + lib_name: []const u8, +) CompileError![:0]u8 { + blk: { + const mod = sema.mod; + const target = mod.getTarget(); + log.debug("extern fn symbol expected in lib '{s}'", .{lib_name}); + if (target_util.is_libc_lib_name(target, lib_name)) { + if (!mod.comp.bin_file.options.link_libc) { + return sema.fail( + block, + src_loc, + "dependency on libc must be explicitly specified in the build command", + .{}, + ); + } + mod.comp.bin_file.options.link_libc = true; + break :blk; + } + if (target_util.is_libcpp_lib_name(target, lib_name)) { + if (!mod.comp.bin_file.options.link_libcpp) { + return sema.fail( + block, + src_loc, + "dependency on libc++ must be explicitly specified in the build command", + .{}, + ); + } + mod.comp.bin_file.options.link_libcpp = true; + break :blk; + } + if (mem.eql(u8, lib_name, "unwind")) { + mod.comp.bin_file.options.link_libunwind = true; + break :blk; + } + if (!target.isWasm() and !mod.comp.bin_file.options.pic) { + return sema.fail( + block, + src_loc, + "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", + .{ lib_name, lib_name }, + ); + } + mod.comp.stage1AddLinkLib(lib_name) catch |err| { + return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{ + lib_name, @errorName(err), + }); + }; + } + return sema.gpa.dupeZ(u8, lib_name); +} + fn funcCommon( sema: *Sema, block: *Block, @@ -5567,65 +5630,21 @@ fn funcCommon( }); }; - if (opt_lib_name) |lib_name| blk: { - const lib_name_src: LazySrcLoc = .{ .node_offset_lib_name = src_node_offset }; - log.debug("extern fn symbol expected in lib '{s}'", .{lib_name}); - if (target_util.is_libc_lib_name(target, lib_name)) { - if (!mod.comp.bin_file.options.link_libc) { - return sema.fail( - block, - lib_name_src, - "dependency on libc must be explicitly specified in the build command", - .{}, - ); - } - mod.comp.bin_file.options.link_libc = true; - break :blk; - } - if (target_util.is_libcpp_lib_name(target, lib_name)) { - if (!mod.comp.bin_file.options.link_libcpp) { - return sema.fail( - block, - lib_name_src, - "dependency on libc++ must be explicitly specified in the build command", - .{}, - ); - } - mod.comp.bin_file.options.link_libcpp = true; - break :blk; - } - if (mem.eql(u8, lib_name, "unwind")) { - mod.comp.bin_file.options.link_libunwind = true; - break :blk; - } - if (!target.isWasm() and !mod.comp.bin_file.options.pic) { - return sema.fail( - block, - lib_name_src, - "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", - .{ lib_name, lib_name }, - ); - } - mod.comp.stage1AddLinkLib(lib_name) catch |err| { - return sema.fail(block, lib_name_src, "unable to add link lib '{s}': {s}", .{ - lib_name, @errorName(err), - }); - }; - } - if (is_extern) { const new_extern_fn = try sema.gpa.create(Module.ExternFn); errdefer sema.gpa.destroy(new_extern_fn); - const lib_name: ?[*:0]const u8 = if (opt_lib_name) |lib_name| blk: { - break :blk try sema.gpa.dupeZ(u8, lib_name); - } else null; - new_extern_fn.* = Module.ExternFn{ .owner_decl = sema.owner_decl, - .lib_name = lib_name, + .lib_name = null, }; + if (opt_lib_name) |lib_name| { + new_extern_fn.lib_name = try sema.handleExternLibName(block, .{ + .node_offset_lib_name = src_node_offset, + }, lib_name); + } + const extern_fn_payload = try sema.arena.create(Value.Payload.ExternFn); extern_fn_payload.* = .{ .base = .{ .tag = .extern_fn }, @@ -12456,13 +12475,8 @@ fn zirVarExtended( try sema.validateVarType(block, mut_src, var_ty, small.is_extern); - if (lib_name != null) { - // Look at the sema code for functions which has this logic, it just needs to - // be extracted and shared by both var and func - return sema.fail(block, src, "TODO: handle var with lib_name in Sema", .{}); - } - const new_var = try sema.gpa.create(Module.Var); + errdefer sema.gpa.destroy(new_var); log.debug("created variable {*} owner_decl: {*} ({s})", .{ new_var, sema.owner_decl, sema.owner_decl.name, @@ -12474,7 +12488,13 @@ fn zirVarExtended( .is_extern = small.is_extern, .is_mutable = true, // TODO get rid of this unused field .is_threadlocal = small.is_threadlocal, + .lib_name = null, }; + + if (lib_name) |lname| { + new_var.lib_name = try sema.handleExternLibName(block, ty_src, lname); + } + const result = try sema.addConstant( var_ty, try Value.Tag.variable.create(sema.arena, new_var), From adc9a282d8b3cbe58e07c965fe40fb1dd8666bd7 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sun, 6 Feb 2022 18:18:53 +0100 Subject: [PATCH 0048/2031] stage2 ARM: fix load and store for abi_size < 4 Previously, in these cases, we would emit the ldr instruction even though ldrb oder ldrh are the correct instructions. --- src/arch/arm/CodeGen.zig | 157 ++++++++++++++++++++++++--------------- 1 file changed, 98 insertions(+), 59 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index c87f750831..804aedb6cc 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -647,11 +647,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { } } -fn writeInt(self: *Self, comptime T: type, buf: *[@divExact(@typeInfo(T).Int.bits, 8)]u8, value: T) void { - const endian = self.target.cpu.arch.endian(); - std.mem.writeInt(T, buf, value, endian); -} - /// Asserts there is already capacity to insert into top branch inst_table. fn processDeath(self: *Self, inst: Air.Inst.Index) void { const air_tags = self.air.instructions.items(.tag); @@ -906,7 +901,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { break :result r; }, else => { - break :result try self.genArmBinOp(inst, ty_op.operand, .bool_true, .not); + break :result try self.genBinOp(inst, ty_op.operand, .bool_true, .not); }, } }; @@ -934,7 +929,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { fn airAdd(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 try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .add); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .add); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -952,7 +947,7 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { fn airSub(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 try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .sub); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .sub); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -970,7 +965,7 @@ fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { fn airMul(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 try self.genArmMul(inst, bin_op.lhs, bin_op.rhs); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genMul(inst, bin_op.lhs, bin_op.rhs); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1026,25 +1021,25 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void { fn airBitAnd(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 try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_and); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_and); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } fn airBitOr(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 try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_or); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_or); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } fn airXor(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 try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .xor); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .xor); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } fn airShl(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 try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .shl); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .shl); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1056,7 +1051,7 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { fn airShr(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 try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .shr); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .shr); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1296,7 +1291,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { else => { const dst_mcv = try self.allocRegOrMem(inst, true); - const offset_mcv = try self.genArmMulConstant(bin_op.rhs, @intCast(u32, elem_size)); + const offset_mcv = try self.genMulConstant(bin_op.rhs, @intCast(u32, elem_size)); assert(offset_mcv == .register); // result of multiplication should always be register self.register_manager.freezeRegs(&.{offset_mcv.register}); @@ -1304,7 +1299,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { self.register_manager.freezeRegs(&.{addr_reg}); defer self.register_manager.unfreezeRegs(&.{addr_reg}); - try self.genArmBinOpCode(addr_reg, base_mcv, offset_mcv, false, .add, .unsigned); + try self.genBinOpCode(addr_reg, base_mcv, offset_mcv, false, .add, .unsigned); // At this point in time, neither the base register // nor the offset register contains any valuable data @@ -1415,6 +1410,8 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { const elem_ty = ptr_ty.elemType(); + const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*)); + switch (ptr) { .none => unreachable, .undef => unreachable, @@ -1440,24 +1437,17 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .compare_flags_signed, .compare_flags_unsigned => unreachable, .embedded_in_code => unreachable, .register => |dst_reg| { - _ = try self.addInst(.{ - .tag = .ldr, - .data = .{ .rr_offset = .{ - .rt = dst_reg, - .rn = reg, - .offset = .{ .offset = Instruction.Offset.none }, - } }, - }); + try self.genLdrRegister(dst_reg, reg, elem_size); }, .stack_offset => |off| { - if (elem_ty.abiSize(self.target.*) <= 4) { + if (elem_size <= 4) { const tmp_reg = try self.register_manager.allocReg(null); self.register_manager.freezeRegs(&.{tmp_reg}); defer self.register_manager.unfreezeRegs(&.{tmp_reg}); try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); - } else if (elem_ty.abiSize(self.target.*) == 8) { + } else if (elem_size == 8) { // TODO generalize this: maybe add a // genArmMemcpy function which manually copies // data if the size is below a certain @@ -1500,7 +1490,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo const tmp_reg = regs[3]; // sub dst_reg, fp, #off - const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*)); const adj_off = off + elem_size; const offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_off)) |x| x else { return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); @@ -1528,7 +1517,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo }); // memcpy(src, dst, len) - try self.genArmInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); + try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); } }, else => return self.fail("TODO load from register into {}", .{dst_mcv}), @@ -1600,14 +1589,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type switch (value) { .register => |value_reg| { - _ = try self.addInst(.{ - .tag = .str, - .data = .{ .rr_offset = .{ - .rt = value_reg, - .rn = addr_reg, - .offset = .{ .offset = Instruction.Offset.none }, - } }, - }); + try self.genStrRegister(value_reg, addr_reg, @intCast(u32, value_ty.abiSize(self.target.*))); }, else => { if (value_ty.abiSize(self.target.*) <= 4) { @@ -1723,7 +1705,7 @@ fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool { }; } -fn genArmBinOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, op: Air.Inst.Tag) !MCValue { +fn genBinOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, op: Air.Inst.Tag) !MCValue { // In the case of bitshifts, the type of rhs is different // from the resulting type const ty = self.air.typeOf(op_lhs); @@ -1732,17 +1714,17 @@ fn genArmBinOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: .Float => return self.fail("TODO ARM binary operations on floats", .{}), .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), .Bool => { - return self.genArmBinIntOp(inst, op_lhs, op_rhs, op, 1, .unsigned); + return self.genBinIntOp(inst, op_lhs, op_rhs, op, 1, .unsigned); }, .Int => { const int_info = ty.intInfo(self.target.*); - return self.genArmBinIntOp(inst, op_lhs, op_rhs, op, int_info.bits, int_info.signedness); + return self.genBinIntOp(inst, op_lhs, op_rhs, op, int_info.bits, int_info.signedness); }, else => unreachable, } } -fn genArmBinIntOp( +fn genBinIntOp( self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, @@ -1852,7 +1834,7 @@ fn genArmBinIntOp( try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs); } - try self.genArmBinOpCode( + try self.genBinOpCode( dst_mcv.register, lhs_mcv, rhs_mcv, @@ -1863,7 +1845,7 @@ fn genArmBinIntOp( return dst_mcv; } -fn genArmBinOpCode( +fn genBinOpCode( self: *Self, dst_reg: Register, lhs_mcv: MCValue, @@ -1971,7 +1953,7 @@ fn genArmBinOpCode( } } -fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue { +fn genMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue { const lhs = try self.resolveInst(op_lhs); const rhs = try self.resolveInst(op_rhs); @@ -2050,7 +2032,7 @@ fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Ai return dst_mcv; } -fn genArmMulConstant(self: *Self, op: Air.Inst.Ref, imm: u32) !MCValue { +fn genMulConstant(self: *Self, op: Air.Inst.Ref, imm: u32) !MCValue { const lhs = try self.resolveInst(op); const rhs = MCValue{ .immediate = imm }; @@ -2097,7 +2079,71 @@ fn genArmMulConstant(self: *Self, op: Air.Inst.Ref, imm: u32) !MCValue { return dst_mcv; } -fn genArmInlineMemcpy( +fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, abi_size: u32) !void { + switch (abi_size) { + 1, 3, 4 => { + const tag: Mir.Inst.Tag = switch (abi_size) { + 1 => .ldrb, + 3, 4 => .ldr, + else => unreachable, + }; + + _ = try self.addInst(.{ + .tag = tag, + .data = .{ .rr_offset = .{ + .rt = dest_reg, + .rn = addr_reg, + .offset = .{ .offset = Instruction.Offset.none }, + } }, + }); + }, + 2 => { + _ = try self.addInst(.{ + .tag = .ldrh, + .data = .{ .rr_extra_offset = .{ + .rt = dest_reg, + .rn = addr_reg, + .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.none }, + } }, + }); + }, + else => unreachable, // invalid abi_size for a register + } +} + +fn genStrRegister(self: *Self, source_reg: Register, addr_reg: Register, abi_size: u32) !void { + switch (abi_size) { + 1, 3, 4 => { + const tag: Mir.Inst.Tag = switch (abi_size) { + 1 => .strb, + 3, 4 => .str, + else => unreachable, + }; + + _ = try self.addInst(.{ + .tag = tag, + .data = .{ .rr_offset = .{ + .rt = source_reg, + .rn = addr_reg, + .offset = .{ .offset = Instruction.Offset.none }, + } }, + }); + }, + 2 => { + _ = try self.addInst(.{ + .tag = .strh, + .data = .{ .rr_extra_offset = .{ + .rt = source_reg, + .rn = addr_reg, + .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.none }, + } }, + }); + }, + else => unreachable, // invalid abi_size for a register + } +} + +fn genInlineMemcpy( self: *Self, src: Register, dst: Register, @@ -2469,7 +2515,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { // The destination register is not present in the cmp instruction // The signedness of the integer does not matter for the cmp instruction - try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq, undefined); + try self.genBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq, undefined); break :result switch (signedness) { .signed => MCValue{ .compare_flags_signed = op }, @@ -2701,7 +2747,7 @@ fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue { else => .{ .register = try self.copyToTmpRegister(ty, operand) }, }; - try self.genArmBinOpCode(undefined, reg_mcv, .{ .immediate = 0 }, false, .cmp_eq, undefined); + try self.genBinOpCode(undefined, reg_mcv, .{ .immediate = 0 }, false, .cmp_eq, undefined); return MCValue{ .compare_flags_unsigned = .eq }; } else { @@ -2731,7 +2777,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { else => .{ .register = try self.copyToTmpRegister(error_type, operand) }, }; - try self.genArmBinOpCode(undefined, reg_mcv, .{ .immediate = 0 }, false, .cmp_eq, undefined); + try self.genBinOpCode(undefined, reg_mcv, .{ .immediate = 0 }, false, .cmp_eq, undefined); return MCValue{ .compare_flags_unsigned = .gt }; } else { @@ -2946,8 +2992,8 @@ fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const air_tags = self.air.instructions.items(.tag); const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (air_tags[inst]) { - .bool_and => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_and), - .bool_or => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_or), + .bool_and => try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_and), + .bool_or => try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_or), else => unreachable, // Not a boolean operation }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); @@ -3242,7 +3288,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro }); // memcpy(src, dst, len) - try self.genArmInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); + try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); } }, } @@ -3439,14 +3485,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void // The value is in memory at a hard-coded address. // If the type is a pointer, it means the pointer address is at this memory location. try self.genSetReg(ty, reg, .{ .immediate = @intCast(u32, addr) }); - _ = try self.addInst(.{ - .tag = .ldr, - .data = .{ .rr_offset = .{ - .rt = reg, - .rn = reg, - .offset = .{ .offset = Instruction.Offset.none }, - } }, - }); + try self.genLdrRegister(reg, reg, @intCast(u32, ty.abiSize(self.target.*))); }, .stack_offset => |unadjusted_off| { // TODO: maybe addressing from sp instead of fp From 53d8a25dab5ddcea16ac70cdcdf28cb3e4944cbb Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sat, 5 Feb 2022 18:59:09 -0700 Subject: [PATCH 0049/2031] child_process: collectOutputWindows handle broken_pipe from ReadFile This was found on a user's machine when calling "git" as a child process from msys. Instead of getting BROKEN_PIPE on GetOverlappedREsult, it would occur on ReadFile which would then cause the function to hang because the async operation was never started. --- lib/std/child_process.zig | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 7808dcd1e5..3e12312d5d 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -277,10 +277,19 @@ pub const ChildProcess = struct { const new_capacity = std.math.min(outs[i].items.len + bump_amt, max_output_bytes); try outs[i].ensureTotalCapacity(new_capacity); const buf = outs[i].unusedCapacitySlice(); - _ = windows.kernel32.ReadFile(handles[i], buf.ptr, math.cast(u32, buf.len) catch maxInt(u32), null, &overlapped[i]); - wait_objects[wait_object_count] = handles[i]; - wait_object_count += 1; + const read_result = windows.kernel32.ReadFile(handles[i], buf.ptr, math.cast(u32, buf.len) catch maxInt(u32), null, &overlapped[i]); + std.debug.assert(read_result == 0); + switch (windows.kernel32.GetLastError()) { + .IO_PENDING => { + wait_objects[wait_object_count] = handles[i]; + wait_object_count += 1; + }, + .BROKEN_PIPE => {}, // don't add to the wait_objects list + else => |err| return windows.unexpectedError(err), + } } + if (wait_object_count == 0) + return; while (true) { const status = windows.kernel32.WaitForMultipleObjects(wait_object_count, &wait_objects, 0, windows.INFINITE); @@ -320,9 +329,16 @@ pub const ChildProcess = struct { try outs[i].ensureTotalCapacity(new_capacity); const buf = outs[i].unusedCapacitySlice(); if (buf.len == 0) return if (i == 0) error.StdoutStreamTooLong else error.StderrStreamTooLong; - _ = windows.kernel32.ReadFile(handles[i], buf.ptr, math.cast(u32, buf.len) catch maxInt(u32), null, &overlapped[i]); - wait_objects[wait_object_count] = handles[i]; - wait_object_count += 1; + const read_result = windows.kernel32.ReadFile(handles[i], buf.ptr, math.cast(u32, buf.len) catch maxInt(u32), null, &overlapped[i]); + std.debug.assert(read_result == 0); + switch (windows.kernel32.GetLastError()) { + .IO_PENDING => { + wait_objects[wait_object_count] = handles[i]; + wait_object_count += 1; + }, + .BROKEN_PIPE => {}, // don't add to the wait_objects list + else => |err| return windows.unexpectedError(err), + } } } From 8f830207c47987de9767130d54abff79c8ec257d Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sun, 6 Feb 2022 06:05:40 -0700 Subject: [PATCH 0050/2031] fix bug I think I found while manually reviewing --- lib/std/child_process.zig | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 3e12312d5d..4ccfcb4029 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -288,10 +288,8 @@ pub const ChildProcess = struct { else => |err| return windows.unexpectedError(err), } } - if (wait_object_count == 0) - return; - while (true) { + while (wait_object_count > 0) { const status = windows.kernel32.WaitForMultipleObjects(wait_object_count, &wait_objects, 0, windows.INFINITE); if (status == windows.WAIT_FAILED) { switch (windows.kernel32.GetLastError()) { @@ -315,11 +313,7 @@ pub const ChildProcess = struct { var read_bytes: u32 = undefined; if (windows.kernel32.GetOverlappedResult(handles[i], &overlapped[i], &read_bytes, 0) == 0) { switch (windows.kernel32.GetLastError()) { - .BROKEN_PIPE => { - if (wait_object_count == 0) - break; - continue; - }, + .BROKEN_PIPE => continue, else => |err| return windows.unexpectedError(err), } } From 4fddb591e2377186ef942084cae2e93d5de448b3 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sun, 6 Feb 2022 15:20:15 -0700 Subject: [PATCH 0051/2031] rework to allow ReadFile to complete synchronously --- lib/std/child_process.zig | 55 +++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 4ccfcb4029..10aeacf755 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -252,6 +252,33 @@ pub const ChildProcess = struct { } } + const WindowsAsyncReadResult = enum { + pending, + closed, + full, + }; + + fn windowsAsyncRead( + handle: windows.HANDLE, + overlapped: *windows.OVERLAPPED, + buf: *std.ArrayList(u8), + bump_amt: usize, + max_output_bytes: usize, + ) !WindowsAsyncReadResult { + while (true) { + const new_capacity = std.math.min(buf.items.len + bump_amt, max_output_bytes); + try buf.ensureTotalCapacity(new_capacity); + const next_buf = buf.unusedCapacitySlice(); + if (next_buf.len == 0) return .full; + const read_result = windows.kernel32.ReadFile(handle, next_buf.ptr, math.cast(u32, next_buf.len) catch maxInt(u32), null, overlapped); + if (read_result == 0) return switch (windows.kernel32.GetLastError()) { + .IO_PENDING => .pending, + .BROKEN_PIPE => .closed, + else => |err| windows.unexpectedError(err), + }; + } + } + fn collectOutputWindows(child: *const ChildProcess, outs: [2]*std.ArrayList(u8), max_output_bytes: usize) !void { const bump_amt = 512; const handles = [_]windows.HANDLE{ @@ -274,18 +301,13 @@ pub const ChildProcess = struct { // Windows Async IO requires an initial call to ReadFile before waiting on the handle for ([_]u1{ 0, 1 }) |i| { - const new_capacity = std.math.min(outs[i].items.len + bump_amt, max_output_bytes); - try outs[i].ensureTotalCapacity(new_capacity); - const buf = outs[i].unusedCapacitySlice(); - const read_result = windows.kernel32.ReadFile(handles[i], buf.ptr, math.cast(u32, buf.len) catch maxInt(u32), null, &overlapped[i]); - std.debug.assert(read_result == 0); - switch (windows.kernel32.GetLastError()) { - .IO_PENDING => { + switch (try windowsAsyncRead(handles[i], &overlapped[i], outs[i], bump_amt, max_output_bytes)) { + .pending => { wait_objects[wait_object_count] = handles[i]; wait_object_count += 1; }, - .BROKEN_PIPE => {}, // don't add to the wait_objects list - else => |err| return windows.unexpectedError(err), + .closed => {}, // don't add to the wait_objects list + .full => return if (i == 0) error.StdoutStreamTooLong else error.StderrStreamTooLong, } } @@ -319,19 +341,14 @@ pub const ChildProcess = struct { } outs[i].items.len += read_bytes; - const new_capacity = std.math.min(outs[i].items.len + bump_amt, max_output_bytes); - try outs[i].ensureTotalCapacity(new_capacity); - const buf = outs[i].unusedCapacitySlice(); - if (buf.len == 0) return if (i == 0) error.StdoutStreamTooLong else error.StderrStreamTooLong; - const read_result = windows.kernel32.ReadFile(handles[i], buf.ptr, math.cast(u32, buf.len) catch maxInt(u32), null, &overlapped[i]); - std.debug.assert(read_result == 0); - switch (windows.kernel32.GetLastError()) { - .IO_PENDING => { + + switch (try windowsAsyncRead(handles[i], &overlapped[i], outs[i], bump_amt, max_output_bytes)) { + .pending => { wait_objects[wait_object_count] = handles[i]; wait_object_count += 1; }, - .BROKEN_PIPE => {}, // don't add to the wait_objects list - else => |err| return windows.unexpectedError(err), + .closed => {}, // don't add to the wait_objects list + .full => return if (i == 0) error.StdoutStreamTooLong else error.StderrStreamTooLong, } } } From fd1284ebd07ded1c67bbaff4c14f093051e56f59 Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sun, 6 Feb 2022 22:11:41 +0100 Subject: [PATCH 0052/2031] stage2: apply type coercion in if expressions When setting the break value in an if expression we must explicitly check if a result location type coercion that needs to happen. This was already done for switch expression, so let's just imitate that check and fix for if expressions. To make this possible, we now also propagate `rl_ty_inst` to sub scopes. --- src/AstGen.zig | 42 +++++++++++++++++++++++++++++++++-------- test/behavior/basic.zig | 6 ++++++ 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 59878f940c..228937fffa 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -5118,8 +5118,10 @@ fn setCondBrPayloadElideBlockStorePtr( const astgen = then_scope.astgen; const then_body = then_scope.instructionsSliceUpto(else_scope); const else_body = else_scope.instructionsSlice(); - const then_body_len = @intCast(u32, then_body.len + @boolToInt(then_break != 0)); - const else_body_len = @intCast(u32, else_body.len + @boolToInt(else_break != 0)); + const has_then_break = then_break != 0; + const has_else_break = else_break != 0; + const then_body_len = @intCast(u32, then_body.len + @boolToInt(has_then_break)); + const else_body_len = @intCast(u32, else_body.len + @boolToInt(has_else_break)); try astgen.extra.ensureUnusedCapacity(astgen.gpa, @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_body_len + else_body_len); @@ -5135,26 +5137,49 @@ fn setCondBrPayloadElideBlockStorePtr( const then_body_len_index = condbr_pl + 1; const else_body_len_index = condbr_pl + 2; + // The break instructions need to have their operands coerced if the + // switch's result location is a `ty`. In this case we overwrite the + // `store_to_block_ptr` instruction with an `as` instruction and repurpose + // it as the break operand. for (then_body) |src_inst| { - if (zir_tags[src_inst] == .store_to_block_ptr) { - if (zir_datas[src_inst].bin.lhs == block_ptr) { + if (zir_tags[src_inst] == .store_to_block_ptr and + zir_datas[src_inst].bin.lhs == block_ptr) + { + if (then_scope.rl_ty_inst != .none and has_then_break) { + zir_tags[src_inst] = .as; + zir_datas[src_inst].bin = .{ + .lhs = then_scope.rl_ty_inst, + .rhs = zir_datas[then_break].@"break".operand, + }; + zir_datas[then_break].@"break".operand = indexToRef(src_inst); + } else { astgen.extra.items[then_body_len_index] -= 1; continue; } } astgen.extra.appendAssumeCapacity(src_inst); } - if (then_break != 0) astgen.extra.appendAssumeCapacity(then_break); + if (has_then_break) astgen.extra.appendAssumeCapacity(then_break); + for (else_body) |src_inst| { - if (zir_tags[src_inst] == .store_to_block_ptr) { - if (zir_datas[src_inst].bin.lhs == block_ptr) { + if (zir_tags[src_inst] == .store_to_block_ptr and + zir_datas[src_inst].bin.lhs == block_ptr) + { + if (else_scope.rl_ty_inst != .none and has_else_break) { + zir_tags[src_inst] = .as; + zir_datas[src_inst].bin = .{ + .lhs = else_scope.rl_ty_inst, + .rhs = zir_datas[else_break].@"break".operand, + }; + zir_datas[else_break].@"break".operand = indexToRef(src_inst); + } else { astgen.extra.items[else_body_len_index] -= 1; continue; } } astgen.extra.appendAssumeCapacity(src_inst); } - if (else_break != 0) astgen.extra.appendAssumeCapacity(else_break); + if (has_else_break) astgen.extra.appendAssumeCapacity(else_break); } fn whileExpr( @@ -9460,6 +9485,7 @@ const GenZir = struct { .decl_node_index = gz.decl_node_index, .decl_line = gz.decl_line, .parent = scope, + .rl_ty_inst = gz.rl_ty_inst, .astgen = gz.astgen, .suspend_node = gz.suspend_node, .nosuspend_node = gz.nosuspend_node, diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 186418b69c..0c2cfbc3d5 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -693,3 +693,9 @@ test "variable name containing underscores does not shadow int primitive" { _ = u6__4; _ = i2_04_8; } + +test "if expression type coercion" { + var cond: bool = true; + const x: u16 = if (cond) 1 else 0; + try expect(@as(u16, x) == 1); +} From 287ff4ab58f8af70383b6e334c7c862c8b8fbeec Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 30 Jan 2022 17:23:14 -0700 Subject: [PATCH 0053/2031] stage2: add more float arithmetic and f80 support AstGen: Fixed bug where f80 types in source were triggering illegal behavior. Value: handle f80 in floating point arithmetic functions. Value: implement floatRem and floatMod This commit introduces dependencies on compiler-rt that are not implemented. Those are a prerequisite to merging this branch. --- src/AstGen.zig | 6 +- src/Sema.zig | 24 ++--- src/value.zig | 215 +++++++++++++++++++++++++++++++---------- test/behavior/math.zig | 2 - 4 files changed, 178 insertions(+), 69 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 228937fffa..4133d3d364 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -8409,10 +8409,11 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In .c_ushort_type, .comptime_float_type, .comptime_int_type, - .f128_type, .f16_type, .f32_type, .f64_type, + .f80_type, + .f128_type, .i16_type, .i32_type, .i64_type, @@ -8648,10 +8649,11 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool { .c_ulong_type, .c_ulonglong_type, .c_ushort_type, - .f128_type, .f16_type, .f32_type, .f64_type, + .f80_type, + .f128_type, .i16_type, .i32_type, .i64_type, diff --git a/src/Sema.zig b/src/Sema.zig index 934fa4064b..75492e2ae7 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -8187,7 +8187,7 @@ fn analyzeArithmetic( } else { return sema.addConstant( scalar_type, - try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena), + try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .add }; @@ -8280,7 +8280,7 @@ fn analyzeArithmetic( } else { return sema.addConstant( scalar_type, - try lhs_val.floatSub(rhs_val, scalar_type, sema.arena), + try lhs_val.floatSub(rhs_val, scalar_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .sub }; @@ -8396,7 +8396,7 @@ fn analyzeArithmetic( } else { return sema.addConstant( scalar_type, - try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena), + try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena, target), ); } } else { @@ -8471,7 +8471,7 @@ fn analyzeArithmetic( } else { return sema.addConstant( scalar_type, - try lhs_val.floatDivTrunc(rhs_val, scalar_type, sema.arena), + try lhs_val.floatDivTrunc(rhs_val, scalar_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .div_trunc }; @@ -8534,7 +8534,7 @@ fn analyzeArithmetic( } else { return sema.addConstant( scalar_type, - try lhs_val.floatDivFloor(rhs_val, scalar_type, sema.arena), + try lhs_val.floatDivFloor(rhs_val, scalar_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .div_floor }; @@ -8586,7 +8586,7 @@ fn analyzeArithmetic( // TODO: emit compile error if there is a remainder return sema.addConstant( scalar_type, - try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena), + try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .div_exact }; @@ -8641,7 +8641,7 @@ fn analyzeArithmetic( } else { return sema.addConstant( scalar_type, - try lhs_val.floatMul(rhs_val, scalar_type, sema.arena), + try lhs_val.floatMul(rhs_val, scalar_type, sema.arena, target), ); } } else break :rs .{ .src = lhs_src, .air_tag = .mul }; @@ -8797,7 +8797,7 @@ fn analyzeArithmetic( } return sema.addConstant( scalar_type, - try lhs_val.floatRem(rhs_val, sema.arena), + try lhs_val.floatRem(rhs_val, scalar_type, sema.arena, target), ); } else { return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); @@ -8858,7 +8858,7 @@ fn analyzeArithmetic( if (maybe_rhs_val) |rhs_val| { return sema.addConstant( scalar_type, - try lhs_val.floatRem(rhs_val, sema.arena), + try lhs_val.floatRem(rhs_val, scalar_type, sema.arena, target), ); } else break :rs .{ .src = rhs_src, .air_tag = .rem }; } else break :rs .{ .src = lhs_src, .air_tag = .rem }; @@ -8915,7 +8915,7 @@ fn analyzeArithmetic( if (maybe_rhs_val) |rhs_val| { return sema.addConstant( scalar_type, - try lhs_val.floatMod(rhs_val, sema.arena), + try lhs_val.floatMod(rhs_val, scalar_type, sema.arena, target), ); } else break :rs .{ .src = rhs_src, .air_tag = .mod }; } else break :rs .{ .src = lhs_src, .air_tag = .mod }; @@ -14195,12 +14195,12 @@ fn coerce( .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag()) { .ComptimeFloat => { const val = try sema.resolveConstValue(block, inst_src, inst); - const result_val = try val.floatCast(sema.arena, dest_ty); + const result_val = try val.floatCast(sema.arena, dest_ty, target); return try sema.addConstant(dest_ty, result_val); }, .Float => { if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| { - const result_val = try val.floatCast(sema.arena, dest_ty); + const result_val = try val.floatCast(sema.arena, dest_ty, target); if (!val.eql(result_val, dest_ty)) { return sema.fail( block, diff --git a/src/value.zig b/src/value.zig index e444e2daf1..18c8357b6b 100644 --- a/src/value.zig +++ b/src/value.zig @@ -138,6 +138,7 @@ pub const Value = extern union { float_16, float_32, float_64, + float_80, float_128, enum_literal, /// A specific enum tag, indicated by the field index (declaration order). @@ -295,6 +296,7 @@ pub const Value = extern union { .float_16 => Payload.Float_16, .float_32 => Payload.Float_32, .float_64 => Payload.Float_64, + .float_80 => Payload.Float_80, .float_128 => Payload.Float_128, .@"error" => Payload.Error, .inferred_alloc => Payload.InferredAlloc, @@ -546,6 +548,7 @@ pub const Value = extern union { .float_16 => return self.copyPayloadShallow(arena, Payload.Float_16), .float_32 => return self.copyPayloadShallow(arena, Payload.Float_32), .float_64 => return self.copyPayloadShallow(arena, Payload.Float_64), + .float_80 => return self.copyPayloadShallow(arena, Payload.Float_80), .float_128 => return self.copyPayloadShallow(arena, Payload.Float_128), .enum_literal => { const payload = self.castTag(.enum_literal).?; @@ -733,6 +736,7 @@ pub const Value = extern union { .float_16 => return out_stream.print("{}", .{val.castTag(.float_16).?.data}), .float_32 => return out_stream.print("{}", .{val.castTag(.float_32).?.data}), .float_64 => return out_stream.print("{}", .{val.castTag(.float_64).?.data}), + .float_80 => return out_stream.print("{}", .{val.castTag(.float_80).?.data}), .float_128 => return out_stream.print("{}", .{val.castTag(.float_128).?.data}), .@"error" => return out_stream.print("error.{s}", .{val.castTag(.@"error").?.data.name}), // TODO to print this it should be error{ Set, Items }!T(val), but we need the type for that @@ -1083,6 +1087,7 @@ pub const Value = extern union { 16 => return Value.Tag.float_16.create(arena, floatReadFromMemory(f16, target, buffer)), 32 => return Value.Tag.float_32.create(arena, floatReadFromMemory(f32, target, buffer)), 64 => return Value.Tag.float_64.create(arena, floatReadFromMemory(f64, target, buffer)), + 80 => return Value.Tag.float_80.create(arena, floatReadFromMemory(f80, target, buffer)), 128 => return Value.Tag.float_128.create(arena, floatReadFromMemory(f128, target, buffer)), else => unreachable, }, @@ -1100,6 +1105,12 @@ pub const Value = extern union { } fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F { + if (F == f80) { + // TODO: use std.math.F80Repr + const big_int = std.mem.readInt(u128, buffer[0..16], target.cpu.arch.endian()); + const int = @truncate(u80, big_int); + return @bitCast(F, int); + } const Int = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = @typeInfo(F).Float.bits, @@ -1114,6 +1125,7 @@ pub const Value = extern union { .float_16 => @floatCast(T, val.castTag(.float_16).?.data), .float_32 => @floatCast(T, val.castTag(.float_32).?.data), .float_64 => @floatCast(T, val.castTag(.float_64).?.data), + .float_80 => @floatCast(T, val.castTag(.float_80).?.data), .float_128 => @floatCast(T, val.castTag(.float_128).?.data), .zero => 0, @@ -1367,14 +1379,13 @@ pub const Value = extern union { /// Converts an integer or a float to a float. May result in a loss of information. /// Caller can find out by equality checking the result against the operand. - pub fn floatCast(self: Value, arena: Allocator, dest_ty: Type) !Value { - switch (dest_ty.tag()) { - .f16 => return Value.Tag.float_16.create(arena, self.toFloat(f16)), - .f32 => return Value.Tag.float_32.create(arena, self.toFloat(f32)), - .f64 => return Value.Tag.float_64.create(arena, self.toFloat(f64)), - .f128, .comptime_float, .c_longdouble => { - return Value.Tag.float_128.create(arena, self.toFloat(f128)); - }, + pub fn floatCast(self: Value, arena: Allocator, dest_ty: Type, target: Target) !Value { + switch (dest_ty.floatBits(target)) { + 16 => return Value.Tag.float_16.create(arena, self.toFloat(f16)), + 32 => return Value.Tag.float_32.create(arena, self.toFloat(f32)), + 64 => return Value.Tag.float_64.create(arena, self.toFloat(f64)), + 80 => return Value.Tag.float_80.create(arena, self.toFloat(f80)), + 128 => return Value.Tag.float_128.create(arena, self.toFloat(f128)), else => unreachable, } } @@ -1389,8 +1400,8 @@ pub const Value = extern union { .float_16 => @rem(self.castTag(.float_16).?.data, 1) != 0, .float_32 => @rem(self.castTag(.float_32).?.data, 1) != 0, .float_64 => @rem(self.castTag(.float_64).?.data, 1) != 0, - // .float_128 => @rem(self.castTag(.float_128).?.data, 1) != 0, - .float_128 => @panic("TODO lld: error: undefined symbol: fmodl"), + .float_80 => @rem(self.castTag(.float_80).?.data, 1) != 0, + .float_128 => @rem(self.castTag(.float_128).?.data, 1) != 0, else => unreachable, }; @@ -1408,6 +1419,7 @@ pub const Value = extern union { .float_16 => self.castTag(.float_16).?.data == 0, .float_32 => self.castTag(.float_32).?.data == 0, .float_64 => self.castTag(.float_64).?.data == 0, + .float_80 => self.castTag(.float_80).?.data == 0, .float_128 => self.castTag(.float_128).?.data == 0, .int_big_positive => self.castTag(.int_big_positive).?.asBigInt().eqZero(), @@ -1440,6 +1452,7 @@ pub const Value = extern union { .float_16 => std.math.order(lhs.castTag(.float_16).?.data, 0), .float_32 => std.math.order(lhs.castTag(.float_32).?.data, 0), .float_64 => std.math.order(lhs.castTag(.float_64).?.data, 0), + .float_80 => std.math.order(lhs.castTag(.float_80).?.data, 0), .float_128 => std.math.order(lhs.castTag(.float_128).?.data, 0), else => unreachable, @@ -1471,6 +1484,7 @@ pub const Value = extern union { .float_16 => return std.math.order(lhs.castTag(.float_16).?.data, rhs.castTag(.float_16).?.data), .float_32 => return std.math.order(lhs.castTag(.float_32).?.data, rhs.castTag(.float_32).?.data), .float_64 => return std.math.order(lhs.castTag(.float_64).?.data, rhs.castTag(.float_64).?.data), + .float_80 => return std.math.order(lhs.castTag(.float_80).?.data, rhs.castTag(.float_80).?.data), .float_128 => return std.math.order(lhs.castTag(.float_128).?.data, rhs.castTag(.float_128).?.data), else => unreachable, }; @@ -2139,6 +2153,7 @@ pub const Value = extern union { .float_16, .float_32, .float_64, + .float_80, .float_128, => true, else => false, @@ -2174,6 +2189,7 @@ pub const Value = extern union { 16 => return Value.Tag.float_16.create(arena, @intToFloat(f16, x)), 32 => return Value.Tag.float_32.create(arena, @intToFloat(f32, x)), 64 => return Value.Tag.float_64.create(arena, @intToFloat(f64, x)), + 80 => return Value.Tag.float_80.create(arena, @intToFloat(f80, x)), 128 => return Value.Tag.float_128.create(arena, @intToFloat(f128, x)), else => unreachable, } @@ -2184,6 +2200,7 @@ pub const Value = extern union { 16 => return Value.Tag.float_16.create(arena, @floatCast(f16, float)), 32 => return Value.Tag.float_32.create(arena, @floatCast(f32, float)), 64 => return Value.Tag.float_64.create(arena, @floatCast(f64, float)), + 80 => return Value.Tag.float_80.create(arena, @floatCast(f80, float)), 128 => return Value.Tag.float_128.create(arena, float), else => unreachable, } @@ -2281,7 +2298,7 @@ pub const Value = extern union { } if (ty.isAnyFloat()) { - return floatAdd(lhs, rhs, ty, arena); + return floatAdd(lhs, rhs, ty, arena, target); } const overflow_result = try intAddWithOverflow(lhs, rhs, ty, arena, target); @@ -2371,7 +2388,7 @@ pub const Value = extern union { } if (ty.isAnyFloat()) { - return floatSub(lhs, rhs, ty, arena); + return floatSub(lhs, rhs, ty, arena, target); } const overflow_result = try intSubWithOverflow(lhs, rhs, ty, arena, target); @@ -2454,7 +2471,7 @@ pub const Value = extern union { } if (ty.isAnyFloat()) { - return floatMul(lhs, rhs, ty, arena); + return floatMul(lhs, rhs, ty, arena, target); } const overflow_result = try intMulWithOverflow(lhs, rhs, ty, arena, target); @@ -2753,23 +2770,72 @@ pub const Value = extern union { .float_16 => std.math.isNan(val.castTag(.float_16).?.data), .float_32 => std.math.isNan(val.castTag(.float_32).?.data), .float_64 => std.math.isNan(val.castTag(.float_64).?.data), + .float_80 => std.math.isNan(val.castTag(.float_80).?.data), .float_128 => std.math.isNan(val.castTag(.float_128).?.data), else => false, }; } - pub fn floatRem(lhs: Value, rhs: Value, allocator: Allocator) !Value { - _ = lhs; - _ = rhs; - _ = allocator; - @panic("TODO implement Value.floatRem"); + pub fn floatRem(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, target: Target) !Value { + switch (float_type.floatBits(target)) { + 16 => { + const lhs_val = lhs.toFloat(f16); + const rhs_val = rhs.toFloat(f16); + return Value.Tag.float_16.create(arena, @rem(lhs_val, rhs_val)); + }, + 32 => { + const lhs_val = lhs.toFloat(f32); + const rhs_val = rhs.toFloat(f32); + return Value.Tag.float_32.create(arena, @rem(lhs_val, rhs_val)); + }, + 64 => { + const lhs_val = lhs.toFloat(f64); + const rhs_val = rhs.toFloat(f64); + return Value.Tag.float_64.create(arena, @rem(lhs_val, rhs_val)); + }, + 80 => { + const lhs_val = lhs.toFloat(f80); + const rhs_val = rhs.toFloat(f80); + return Value.Tag.float_80.create(arena, @rem(lhs_val, rhs_val)); + }, + 128 => { + const lhs_val = lhs.toFloat(f128); + const rhs_val = rhs.toFloat(f128); + return Value.Tag.float_128.create(arena, @rem(lhs_val, rhs_val)); + }, + else => unreachable, + } } - pub fn floatMod(lhs: Value, rhs: Value, allocator: Allocator) !Value { - _ = lhs; - _ = rhs; - _ = allocator; - @panic("TODO implement Value.floatMod"); + pub fn floatMod(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, target: Target) !Value { + switch (float_type.floatBits(target)) { + 16 => { + const lhs_val = lhs.toFloat(f16); + const rhs_val = rhs.toFloat(f16); + return Value.Tag.float_16.create(arena, @mod(lhs_val, rhs_val)); + }, + 32 => { + const lhs_val = lhs.toFloat(f32); + const rhs_val = rhs.toFloat(f32); + return Value.Tag.float_32.create(arena, @mod(lhs_val, rhs_val)); + }, + 64 => { + const lhs_val = lhs.toFloat(f64); + const rhs_val = rhs.toFloat(f64); + return Value.Tag.float_64.create(arena, @mod(lhs_val, rhs_val)); + }, + 80 => { + const lhs_val = lhs.toFloat(f80); + const rhs_val = rhs.toFloat(f80); + return Value.Tag.float_80.create(arena, @mod(lhs_val, rhs_val)); + }, + 128 => { + const lhs_val = lhs.toFloat(f128); + const rhs_val = rhs.toFloat(f128); + return Value.Tag.float_128.create(arena, @mod(lhs_val, rhs_val)); + }, + else => unreachable, + } } pub fn intMul(lhs: Value, rhs: Value, allocator: Allocator) !Value { @@ -2929,24 +2995,30 @@ pub const Value = extern union { rhs: Value, float_type: Type, arena: Allocator, + target: Target, ) !Value { - switch (float_type.tag()) { - .f16 => { + switch (float_type.floatBits(target)) { + 16 => { const lhs_val = lhs.toFloat(f16); const rhs_val = rhs.toFloat(f16); return Value.Tag.float_16.create(arena, lhs_val + rhs_val); }, - .f32 => { + 32 => { const lhs_val = lhs.toFloat(f32); const rhs_val = rhs.toFloat(f32); return Value.Tag.float_32.create(arena, lhs_val + rhs_val); }, - .f64 => { + 64 => { const lhs_val = lhs.toFloat(f64); const rhs_val = rhs.toFloat(f64); return Value.Tag.float_64.create(arena, lhs_val + rhs_val); }, - .f128, .comptime_float, .c_longdouble => { + 80 => { + const lhs_val = lhs.toFloat(f80); + const rhs_val = rhs.toFloat(f80); + return Value.Tag.float_80.create(arena, lhs_val + rhs_val); + }, + 128 => { const lhs_val = lhs.toFloat(f128); const rhs_val = rhs.toFloat(f128); return Value.Tag.float_128.create(arena, lhs_val + rhs_val); @@ -2960,24 +3032,30 @@ pub const Value = extern union { rhs: Value, float_type: Type, arena: Allocator, + target: Target, ) !Value { - switch (float_type.tag()) { - .f16 => { + switch (float_type.floatBits(target)) { + 16 => { const lhs_val = lhs.toFloat(f16); const rhs_val = rhs.toFloat(f16); return Value.Tag.float_16.create(arena, lhs_val - rhs_val); }, - .f32 => { + 32 => { const lhs_val = lhs.toFloat(f32); const rhs_val = rhs.toFloat(f32); return Value.Tag.float_32.create(arena, lhs_val - rhs_val); }, - .f64 => { + 64 => { const lhs_val = lhs.toFloat(f64); const rhs_val = rhs.toFloat(f64); return Value.Tag.float_64.create(arena, lhs_val - rhs_val); }, - .f128, .comptime_float, .c_longdouble => { + 80 => { + const lhs_val = lhs.toFloat(f80); + const rhs_val = rhs.toFloat(f80); + return Value.Tag.float_80.create(arena, lhs_val - rhs_val); + }, + 128 => { const lhs_val = lhs.toFloat(f128); const rhs_val = rhs.toFloat(f128); return Value.Tag.float_128.create(arena, lhs_val - rhs_val); @@ -2991,24 +3069,30 @@ pub const Value = extern union { rhs: Value, float_type: Type, arena: Allocator, + target: Target, ) !Value { - switch (float_type.tag()) { - .f16 => { + switch (float_type.floatBits(target)) { + 16 => { const lhs_val = lhs.toFloat(f16); const rhs_val = rhs.toFloat(f16); return Value.Tag.float_16.create(arena, lhs_val / rhs_val); }, - .f32 => { + 32 => { const lhs_val = lhs.toFloat(f32); const rhs_val = rhs.toFloat(f32); return Value.Tag.float_32.create(arena, lhs_val / rhs_val); }, - .f64 => { + 64 => { const lhs_val = lhs.toFloat(f64); const rhs_val = rhs.toFloat(f64); return Value.Tag.float_64.create(arena, lhs_val / rhs_val); }, - .f128, .comptime_float, .c_longdouble => { + 80 => { + const lhs_val = lhs.toFloat(f80); + const rhs_val = rhs.toFloat(f80); + return Value.Tag.float_80.create(arena, lhs_val / rhs_val); + }, + 128 => { const lhs_val = lhs.toFloat(f128); const rhs_val = rhs.toFloat(f128); return Value.Tag.float_128.create(arena, lhs_val / rhs_val); @@ -3022,24 +3106,30 @@ pub const Value = extern union { rhs: Value, float_type: Type, arena: Allocator, + target: Target, ) !Value { - switch (float_type.tag()) { - .f16 => { + switch (float_type.floatBits(target)) { + 16 => { const lhs_val = lhs.toFloat(f16); const rhs_val = rhs.toFloat(f16); return Value.Tag.float_16.create(arena, @divFloor(lhs_val, rhs_val)); }, - .f32 => { + 32 => { const lhs_val = lhs.toFloat(f32); const rhs_val = rhs.toFloat(f32); return Value.Tag.float_32.create(arena, @divFloor(lhs_val, rhs_val)); }, - .f64 => { + 64 => { const lhs_val = lhs.toFloat(f64); const rhs_val = rhs.toFloat(f64); return Value.Tag.float_64.create(arena, @divFloor(lhs_val, rhs_val)); }, - .f128, .comptime_float, .c_longdouble => { + 80 => { + const lhs_val = lhs.toFloat(f80); + const rhs_val = rhs.toFloat(f80); + return Value.Tag.float_80.create(arena, @divFloor(lhs_val, rhs_val)); + }, + 128 => { const lhs_val = lhs.toFloat(f128); const rhs_val = rhs.toFloat(f128); return Value.Tag.float_128.create(arena, @divFloor(lhs_val, rhs_val)); @@ -3053,24 +3143,30 @@ pub const Value = extern union { rhs: Value, float_type: Type, arena: Allocator, + target: Target, ) !Value { - switch (float_type.tag()) { - .f16 => { + switch (float_type.floatBits(target)) { + 16 => { const lhs_val = lhs.toFloat(f16); const rhs_val = rhs.toFloat(f16); return Value.Tag.float_16.create(arena, @divTrunc(lhs_val, rhs_val)); }, - .f32 => { + 32 => { const lhs_val = lhs.toFloat(f32); const rhs_val = rhs.toFloat(f32); return Value.Tag.float_32.create(arena, @divTrunc(lhs_val, rhs_val)); }, - .f64 => { + 64 => { const lhs_val = lhs.toFloat(f64); const rhs_val = rhs.toFloat(f64); return Value.Tag.float_64.create(arena, @divTrunc(lhs_val, rhs_val)); }, - .f128, .comptime_float, .c_longdouble => { + 80 => { + const lhs_val = lhs.toFloat(f80); + const rhs_val = rhs.toFloat(f80); + return Value.Tag.float_80.create(arena, @divTrunc(lhs_val, rhs_val)); + }, + 128 => { const lhs_val = lhs.toFloat(f128); const rhs_val = rhs.toFloat(f128); return Value.Tag.float_128.create(arena, @divTrunc(lhs_val, rhs_val)); @@ -3084,24 +3180,30 @@ pub const Value = extern union { rhs: Value, float_type: Type, arena: Allocator, + target: Target, ) !Value { - switch (float_type.tag()) { - .f16 => { + switch (float_type.floatBits(target)) { + 16 => { const lhs_val = lhs.toFloat(f16); const rhs_val = rhs.toFloat(f16); return Value.Tag.float_16.create(arena, lhs_val * rhs_val); }, - .f32 => { + 32 => { const lhs_val = lhs.toFloat(f32); const rhs_val = rhs.toFloat(f32); return Value.Tag.float_32.create(arena, lhs_val * rhs_val); }, - .f64 => { + 64 => { const lhs_val = lhs.toFloat(f64); const rhs_val = rhs.toFloat(f64); return Value.Tag.float_64.create(arena, lhs_val * rhs_val); }, - .f128, .comptime_float, .c_longdouble => { + 80 => { + const lhs_val = lhs.toFloat(f80); + const rhs_val = rhs.toFloat(f80); + return Value.Tag.float_80.create(arena, lhs_val * rhs_val); + }, + 128 => { const lhs_val = lhs.toFloat(f128); const rhs_val = rhs.toFloat(f128); return Value.Tag.float_128.create(arena, lhs_val * rhs_val); @@ -3250,6 +3352,13 @@ pub const Value = extern union { data: f64, }; + pub const Float_80 = struct { + pub const base_tag = Tag.float_80; + + base: Payload = .{ .tag = base_tag }, + data: f80, + }; + pub const Float_128 = struct { pub const base_tag = Tag.float_128; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 8f947e2829..a1243eb7c1 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -768,8 +768,6 @@ test "shift left/right on u0 operand" { } test "comptime float rem int" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - comptime { var x = @as(f32, 1) % 2; try expect(x == 1.0); From 545aa790a430bd2c8390435ee52f5fbe147f6c54 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 6 Feb 2022 12:30:30 +0100 Subject: [PATCH 0054/2031] Sema: Fix memory leak --- src/Sema.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Sema.zig b/src/Sema.zig index 934fa4064b..07e71c4bff 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7058,6 +7058,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError sema.air_extra.appendSliceAssumeCapacity(prev_then_body); sema.air_extra.appendSliceAssumeCapacity(cond_body); } + gpa.free(prev_then_body); prev_then_body = case_block.instructions.toOwnedSlice(gpa); prev_cond_br = new_cond_br; } From 53e6c719efe5073307ee58436b17ee80f574b984 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Sat, 5 Feb 2022 20:36:48 +1300 Subject: [PATCH 0055/2031] std/math: optimize division with divisors less than a half-limb This adds a new path which avoids using compiler_rt generated div udivmod instructions in the case that a divisor is less than half the max usize value. Two half-limb divisions are performed instead which ensures that non-emulated division instructions are actually used. This does not improve the udivmod code which should still be reviewed independently of this issue. Notably this improves the performance of the toString implementation of non-power-of-two bases considerably. Division performance is improved ~1000% based on some coarse testing. The following test code is used to provide a rough comparison between the old vs. new method. ``` const std = @import("std"); const Managed = std.math.big.int.Managed; const allocator = std.heap.c_allocator; fn fib(a: *Managed, n: usize) !void { var b = try Managed.initSet(allocator, 1); defer b.deinit(); var c = try Managed.init(allocator); defer c.deinit(); var i: usize = 0; while (i < n) : (i += 1) { try c.add(a.toConst(), b.toConst()); a.swap(&b); b.swap(&c); } } pub fn main() !void { var a = try Managed.initSet(allocator, 0); defer a.deinit(); try fib(&a, 1_000_000); // Note: Next two lines (and printed digit count) omitted on no-print version. const as = try a.toString(allocator, 10, .lower); defer allocator.free(as); std.debug.print("fib: digit count: {}, limb count: {}\n", .{ as.len, a.limbs.len }); } ``` ``` ==> time.no-print <== limb count: 10849 ________________________________________________________ Executed in 10.60 secs fish external usr time 10.44 secs 0.00 millis 10.44 secs sys time 0.02 secs 1.12 millis 0.02 secs ==> time.old <== fib: digit count: 208988, limb count: 10849 ________________________________________________________ Executed in 22.78 secs fish external usr time 22.43 secs 1.01 millis 22.43 secs sys time 0.03 secs 0.13 millis 0.03 secs ==> time.optimized <== fib: digit count: 208988, limb count: 10849 ________________________________________________________ Executed in 11.59 secs fish external usr time 11.56 secs 1.03 millis 11.56 secs sys time 0.03 secs 0.12 millis 0.03 secs ``` Perf data for non-optimized and optimized, verifying no udivmod is generated by the new code. ``` $ perf report -i perf.data.old --stdio - Total Lost Samples: 0 - - Samples: 90K of event 'cycles:u' - Event count (approx.): 71603695208 - - Overhead Command Shared Object Symbol - ........ ....... ................ ........................................... - 52.97% t t [.] compiler_rt.udivmod.udivmod 45.97% t t [.] std.math.big.int.Mutable.addCarry 0.83% t t [.] main 0.08% t libc-2.33.so [.] __memmove_avx_unaligned_erms 0.08% t t [.] __udivti3 0.03% t [unknown] [k] 0xffffffff9a0010a7 0.02% t t [.] std.math.big.int.Managed.ensureCapacity 0.01% t libc-2.33.so [.] _int_malloc 0.00% t libc-2.33.so [.] __malloc_usable_size 0.00% t libc-2.33.so [.] _int_free 0.00% t t [.] 0x0000000000004a80 0.00% t t [.] std.heap.CAllocator.resize 0.00% t libc-2.33.so [.] _mid_memalign 0.00% t libc-2.33.so [.] sysmalloc 0.00% t libc-2.33.so [.] __posix_memalign 0.00% t t [.] std.heap.CAllocator.alloc 0.00% t ld-2.33.so [.] do_lookup_x $ perf report -i perf.data.optimized --stdio - Total Lost Samples: 0 - - Samples: 46K of event 'cycles:u' - Event count (approx.): 36790112336 - - Overhead Command Shared Object Symbol - ........ ....... ................ ........................................... - 79.98% t t [.] std.math.big.int.Mutable.addCarry 15.14% t t [.] main 4.58% t t [.] std.math.big.int.Managed.ensureCapacity 0.21% t libc-2.33.so [.] __memmove_avx_unaligned_erms 0.05% t [unknown] [k] 0xffffffff9a0010a7 0.02% t libc-2.33.so [.] _int_malloc 0.01% t t [.] std.heap.CAllocator.alloc 0.01% t libc-2.33.so [.] __malloc_usable_size 0.00% t libc-2.33.so [.] systrim.constprop.0 0.00% t libc-2.33.so [.] _mid_memalign 0.00% t t [.] 0x0000000000000c7d 0.00% t libc-2.33.so [.] malloc 0.00% t ld-2.33.so [.] check_match ``` Closes #10630. --- lib/std/math/big.zig | 1 + lib/std/math/big/int.zig | 40 +++++++++++++++++++++++++++++++++-- lib/std/math/big/int_test.zig | 37 ++++++++++++++++++++++++++++++-- 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/lib/std/math/big.zig b/lib/std/math/big.zig index e7f8a7fb34..c7fc0b17f5 100644 --- a/lib/std/math/big.zig +++ b/lib/std/math/big.zig @@ -7,6 +7,7 @@ pub const Limb = usize; const limb_info = @typeInfo(Limb).Int; pub const SignedLimb = std.meta.Int(.signed, limb_info.bits); pub const DoubleLimb = std.meta.Int(.unsigned, 2 * limb_info.bits); +pub const HalfLimb = std.meta.Int(.unsigned, limb_info.bits / 2); pub const SignedDoubleLimb = std.meta.Int(.signed, 2 * limb_info.bits); pub const Log2Limb = std.math.Log2Int(Limb); diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index ec0143a3d7..87a62bf66c 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -2,6 +2,8 @@ const std = @import("../../std.zig"); const math = std.math; const Limb = std.math.big.Limb; const limb_bits = @typeInfo(Limb).Int.bits; +const HalfLimb = std.math.big.HalfLimb; +const half_limb_bits = @typeInfo(HalfLimb).Int.bits; const DoubleLimb = std.math.big.DoubleLimb; const SignedDoubleLimb = std.math.big.SignedDoubleLimb; const Log2Limb = std.math.big.Log2Limb; @@ -1335,7 +1337,16 @@ pub const Mutable = struct { const xy_trailing = math.min(x_trailing, y_trailing); if (y.len - xy_trailing == 1) { - lldiv1(q.limbs, &r.limbs[0], x.limbs[xy_trailing..x.len], y.limbs[y.len - 1]); + const divisor = y.limbs[y.len - 1]; + + // Optimization for small divisor. By using a half limb we can avoid requiring DoubleLimb + // divisions in the hot code path. This may often require compiler_rt software-emulation. + if (divisor < maxInt(HalfLimb)) { + lldiv0p5(q.limbs, &r.limbs[0], x.limbs[xy_trailing..x.len], @intCast(HalfLimb, divisor)); + } else { + lldiv1(q.limbs, &r.limbs[0], x.limbs[xy_trailing..x.len], divisor); + } + q.normalize(x.len - xy_trailing); q.positive = q_positive; @@ -1939,7 +1950,8 @@ pub const Const = struct { } } else { // Non power-of-two: batch divisions per word size. - const digits_per_limb = math.log(Limb, base, maxInt(Limb)); + // We use a HalfLimb here so the division uses the faster lldiv0p5 over lldiv1 codepath. + const digits_per_limb = math.log(HalfLimb, base, maxInt(HalfLimb)); var limb_base: Limb = 1; var j: usize = 0; while (j < digits_per_limb) : (j += 1) { @@ -3208,6 +3220,30 @@ fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void { } } +fn lldiv0p5(quo: []Limb, rem: *Limb, a: []const Limb, b: HalfLimb) void { + @setRuntimeSafety(debug_safety); + assert(a.len > 1 or a[0] >= b); + assert(quo.len >= a.len); + + rem.* = 0; + for (a) |_, ri| { + const i = a.len - ri - 1; + const ai_high = a[i] >> half_limb_bits; + const ai_low = a[i] & ((1 << half_limb_bits) - 1); + + // Split the division into two divisions acting on half a limb each. Carry remainder. + const ai_high_with_carry = (rem.* << half_limb_bits) | ai_high; + const ai_high_quo = ai_high_with_carry / b; + rem.* = ai_high_with_carry % b; + + const ai_low_with_carry = (rem.* << half_limb_bits) | ai_low; + const ai_low_quo = ai_low_with_carry / b; + rem.* = ai_low_with_carry % b; + + quo[i] = (ai_high_quo << half_limb_bits) | ai_low_quo; + } +} + fn llshl(r: []Limb, a: []const Limb, shift: usize) void { @setRuntimeSafety(debug_safety); assert(a.len >= 1); diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index 4c1d12116e..70a9b97a38 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -1064,7 +1064,7 @@ test "big.int mulWrap large" { try testing.expect(b.eq(c)); } -test "big.int div single-single no rem" { +test "big.int div single-half no rem" { var a = try Managed.initSet(testing.allocator, 50); defer a.deinit(); var b = try Managed.initSet(testing.allocator, 5); @@ -1080,7 +1080,7 @@ test "big.int div single-single no rem" { try testing.expect((try r.to(u32)) == 0); } -test "big.int div single-single with rem" { +test "big.int div single-half with rem" { var a = try Managed.initSet(testing.allocator, 49); defer a.deinit(); var b = try Managed.initSet(testing.allocator, 5); @@ -1096,6 +1096,39 @@ test "big.int div single-single with rem" { try testing.expect((try r.to(u32)) == 4); } +test "big.int div single-single no rem" { + // assumes usize is <= 64 bits. + var a = try Managed.initSet(testing.allocator, 1 << 52); + defer a.deinit(); + var b = try Managed.initSet(testing.allocator, 1 << 35); + defer b.deinit(); + + var q = try Managed.init(testing.allocator); + defer q.deinit(); + var r = try Managed.init(testing.allocator); + defer r.deinit(); + try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + + try testing.expect((try q.to(u32)) == 131072); + try testing.expect((try r.to(u32)) == 0); +} + +test "big.int div single-single with rem" { + var a = try Managed.initSet(testing.allocator, (1 << 52) | (1 << 33)); + defer a.deinit(); + var b = try Managed.initSet(testing.allocator, (1 << 35)); + defer b.deinit(); + + var q = try Managed.init(testing.allocator); + defer q.deinit(); + var r = try Managed.init(testing.allocator); + defer r.deinit(); + try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + + try testing.expect((try q.to(u64)) == 131072); + try testing.expect((try r.to(u64)) == 8589934592); +} + test "big.int div multi-single no rem" { const op1 = 0xffffeeeeddddcccc; const op2 = 34; From 495fd4ee3e81bd9d26768cdb8fc639afac79d9dc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 6 Feb 2022 19:45:49 -0700 Subject: [PATCH 0056/2031] AstGen: refactor redundant expressions This is a non-functional change. --- src/AstGen.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 4133d3d364..5f6d05b7f5 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2601,8 +2601,8 @@ fn varDecl( var resolve_inferred_alloc: Zir.Inst.Ref = .none; var opt_type_inst: Zir.Inst.Ref = .none; - if (var_decl.ast.type_node != 0) { - const type_inst = try typeExpr(gz, &init_scope.base, var_decl.ast.type_node); + if (type_node != 0) { + const type_inst = try typeExpr(gz, &init_scope.base, type_node); opt_type_inst = type_inst; if (align_inst == .none) { init_scope.instructions_top = gz.instructions.items.len; @@ -2683,7 +2683,7 @@ fn varDecl( const src_inst = gz.instructions.items[src]; if (zir_tags[src_inst] == .store_to_block_ptr) { if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) { - if (var_decl.ast.type_node != 0) { + if (type_node != 0) { zir_tags[src_inst] = .store; } else { zir_tags[src_inst] = .store_to_inferred_ptr; From d4805472c3c98c488c17bce0ee28b6c44e93793c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 6 Feb 2022 20:06:00 -0700 Subject: [PATCH 0057/2031] compiler_rt: addXf3: add coercion to `@clz` We're going to remove the first parameter from this function in the future. Stage2 already ignores the first parameter. So we put an `@as` in here to make it work for both. --- lib/std/special/compiler_rt/addXf3.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/special/compiler_rt/addXf3.zig b/lib/std/special/compiler_rt/addXf3.zig index 41ff00e95d..1339cc340d 100644 --- a/lib/std/special/compiler_rt/addXf3.zig +++ b/lib/std/special/compiler_rt/addXf3.zig @@ -339,7 +339,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { // If partial cancellation occurred, we need to left-shift the result // and adjust the exponent: if (a_int < int_bit << 3) { - const shift = @intCast(i32, @clz(u80, a_int)) - @intCast(i32, @clz(u80, int_bit << 3)); + const shift = @intCast(i32, @clz(u80, a_int)) - @intCast(i32, @clz(u80, @as(u80, int_bit) << 3)); a_int <<= @intCast(u7, shift); a_exp -= shift; } From 3bcce5f6d1f48e20dd177a7e440ddea1c451e779 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 6 Feb 2022 20:07:43 -0700 Subject: [PATCH 0058/2031] Sema: implement writing structs to memory at comptime --- src/value.zig | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/value.zig b/src/value.zig index 18c8357b6b..ac0344bf34 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1033,6 +1033,11 @@ pub const Value = extern union { } pub fn writeToMemory(val: Value, ty: Type, target: Target, buffer: []u8) void { + if (val.isUndef()) { + const size = @intCast(usize, ty.abiSize(target)); + std.mem.set(u8, buffer[0..size], 0xaa); + return; + } switch (ty.zigTypeTag()) { .Int => { var bigint_buffer: BigIntSpace = undefined; @@ -1068,6 +1073,14 @@ pub const Value = extern union { buf_off += elem_size; } }, + .Struct => { + const fields = ty.structFields().values(); + const field_vals = val.castTag(.@"struct").?.data; + for (fields) |field, i| { + const off = @intCast(usize, ty.structFieldOffset(i, target)); + writeToMemory(field_vals[i], field.ty, target, buffer[off..]); + } + }, else => @panic("TODO implement writeToMemory for more types"), } } @@ -1106,7 +1119,7 @@ pub const Value = extern union { fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F { if (F == f80) { - // TODO: use std.math.F80Repr + // TODO: use std.math.F80Repr? const big_int = std.mem.readInt(u128, buffer[0..16], target.cpu.arch.endian()); const int = @truncate(u80, big_int); return @bitCast(F, int); From 65b6faa0485253b284f7a63601dc7d0f5858515a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 6 Feb 2022 20:23:40 -0700 Subject: [PATCH 0059/2031] Sema: avoid `@intToFloat` for f80 which breaks on non-x86 targets Currently Zig lowers `@intToFloat` for f80 incorrectly on non-x86 targets: ``` broken LLVM module found: UIToFP result must be FP or FP vector %62 = uitofp i64 %61 to i128 SIToFP result must be FP or FP vector %66 = sitofp i64 %65 to i128 ``` This happens because on such targets, we use i128 instead of x86_fp80 in order to avoid "LLVM ERROR: Cannot select". `@intToFloat` must be lowered differently to account for this difference as well. --- src/stage1/codegen.cpp | 7 +++++-- src/value.zig | 22 +++++++++++++++++----- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index ec1454ce4f..02f84beeab 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -9432,11 +9432,14 @@ static void define_builtin_types(CodeGen *g) { if (target_has_f80(g->zig_target)) { entry->llvm_type = LLVMX86FP80Type(); } else { + // We use i128 here instead of x86_fp80 because on targets such as arm, + // LLVM will give "ERROR: Cannot select" for any instructions involving + // the x86_fp80 type. entry->llvm_type = get_int_type(g, false, 128)->llvm_type; } entry->size_in_bits = 8 * 16; - entry->abi_size = 16; - entry->abi_align = 16; + entry->abi_size = 16; // matches LLVMABISizeOfType(LLVMX86FP80Type()) + entry->abi_align = 16; // matches LLVMABIAlignmentOfType(LLVMX86FP80Type()) buf_init_from_str(&entry->name, "f80"); entry->data.floating.bit_count = 80; diff --git a/src/value.zig b/src/value.zig index ac0344bf34..6d551d9eba 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1120,8 +1120,8 @@ pub const Value = extern union { fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F { if (F == f80) { // TODO: use std.math.F80Repr? - const big_int = std.mem.readInt(u128, buffer[0..16], target.cpu.arch.endian()); - const int = @truncate(u80, big_int); + const int = std.mem.readInt(u128, buffer[0..16], target.cpu.arch.endian()); + // TODO shouldn't this be a bitcast from u80 to f80 instead of u128 to f80? return @bitCast(F, int); } const Int = @Type(.{ .Int = .{ @@ -1143,8 +1143,18 @@ pub const Value = extern union { .zero => 0, .one => 1, - .int_u64 => @intToFloat(T, val.castTag(.int_u64).?.data), - .int_i64 => @intToFloat(T, val.castTag(.int_i64).?.data), + .int_u64 => { + if (T == f80) { + @panic("TODO we can't lower this properly on non-x86 llvm backend yet"); + } + return @intToFloat(T, val.castTag(.int_u64).?.data); + }, + .int_i64 => { + if (T == f80) { + @panic("TODO we can't lower this properly on non-x86 llvm backend yet"); + } + return @intToFloat(T, val.castTag(.int_i64).?.data); + }, .int_big_positive => @floatCast(T, bigIntToFloat(val.castTag(.int_big_positive).?.data, true)), .int_big_negative => @floatCast(T, bigIntToFloat(val.castTag(.int_big_negative).?.data, false)), @@ -2202,7 +2212,9 @@ pub const Value = extern union { 16 => return Value.Tag.float_16.create(arena, @intToFloat(f16, x)), 32 => return Value.Tag.float_32.create(arena, @intToFloat(f32, x)), 64 => return Value.Tag.float_64.create(arena, @intToFloat(f64, x)), - 80 => return Value.Tag.float_80.create(arena, @intToFloat(f80, x)), + // We can't lower this properly on non-x86 llvm backends yet + //80 => return Value.Tag.float_80.create(arena, @intToFloat(f80, x)), + 80 => @panic("TODO f80 intToFloat"), 128 => return Value.Tag.float_128.create(arena, @intToFloat(f128, x)), else => unreachable, } From eb82fdf96c3392ec9eabacbaa98c976f9ccd264e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 6 Feb 2022 20:38:57 -0700 Subject: [PATCH 0060/2031] Sema: panic instead of lowering to unavailable compiler-rt functions Once the relevant compiler_rt functions are implemented, these panics can be removed. --- src/value.zig | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/value.zig b/src/value.zig index 6d551d9eba..1a7f51ecd5 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1423,8 +1423,10 @@ pub const Value = extern union { .float_16 => @rem(self.castTag(.float_16).?.data, 1) != 0, .float_32 => @rem(self.castTag(.float_32).?.data, 1) != 0, .float_64 => @rem(self.castTag(.float_64).?.data, 1) != 0, - .float_80 => @rem(self.castTag(.float_80).?.data, 1) != 0, - .float_128 => @rem(self.castTag(.float_128).?.data, 1) != 0, + //.float_80 => @rem(self.castTag(.float_80).?.data, 1) != 0, + .float_80 => @panic("TODO implement __remx in compiler-rt"), + //.float_128 => @rem(self.castTag(.float_128).?.data, 1) != 0, + .float_128 => @panic("TODO implement fmodl in compiler-rt"), else => unreachable, }; @@ -2819,11 +2821,17 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @rem(lhs_val, rhs_val)); }, 80 => { + if (true) { + @panic("TODO implement compiler_rt __remx"); + } const lhs_val = lhs.toFloat(f80); const rhs_val = rhs.toFloat(f80); return Value.Tag.float_80.create(arena, @rem(lhs_val, rhs_val)); }, 128 => { + if (true) { + @panic("TODO implement compiler_rt fmodl"); + } const lhs_val = lhs.toFloat(f128); const rhs_val = rhs.toFloat(f128); return Value.Tag.float_128.create(arena, @rem(lhs_val, rhs_val)); @@ -2850,11 +2858,17 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @mod(lhs_val, rhs_val)); }, 80 => { + if (true) { + @panic("TODO implement compiler_rt __modx"); + } const lhs_val = lhs.toFloat(f80); const rhs_val = rhs.toFloat(f80); return Value.Tag.float_80.create(arena, @mod(lhs_val, rhs_val)); }, 128 => { + if (true) { + @panic("TODO implement compiler_rt fmodl"); + } const lhs_val = lhs.toFloat(f128); const rhs_val = rhs.toFloat(f128); return Value.Tag.float_128.create(arena, @mod(lhs_val, rhs_val)); @@ -3113,6 +3127,9 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, lhs_val / rhs_val); }, 80 => { + if (true) { + @panic("TODO implement compiler_rt __divxf3"); + } const lhs_val = lhs.toFloat(f80); const rhs_val = rhs.toFloat(f80); return Value.Tag.float_80.create(arena, lhs_val / rhs_val); @@ -3150,6 +3167,9 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @divFloor(lhs_val, rhs_val)); }, 80 => { + if (true) { + @panic("TODO implement compiler_rt __floorx"); + } const lhs_val = lhs.toFloat(f80); const rhs_val = rhs.toFloat(f80); return Value.Tag.float_80.create(arena, @divFloor(lhs_val, rhs_val)); @@ -3187,6 +3207,9 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @divTrunc(lhs_val, rhs_val)); }, 80 => { + if (true) { + @panic("TODO implement compiler_rt __truncx"); + } const lhs_val = lhs.toFloat(f80); const rhs_val = rhs.toFloat(f80); return Value.Tag.float_80.create(arena, @divTrunc(lhs_val, rhs_val)); @@ -3224,6 +3247,9 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, lhs_val * rhs_val); }, 80 => { + if (true) { + @panic("TODO implement compiler_rt __mulxf3"); + } const lhs_val = lhs.toFloat(f80); const rhs_val = rhs.toFloat(f80); return Value.Tag.float_80.create(arena, lhs_val * rhs_val); From 5065830aa007c374c382be9e80ba924df6cecc78 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Thu, 3 Feb 2022 15:27:01 -0700 Subject: [PATCH 0061/2031] Avoid depending on child process execution when not supported by host OS In accordance with the requesting issue (#10750): - `zig test` skips any tests that it cannot spawn, returning success - `zig run` and `zig build` exit with failure, reporting the command the cannot be run - `zig clang`, `zig ar`, etc. already punt directly to the appropriate clang/lld main(), even before this change - Native `libc` Detection is not supported Additionally, `exec()` and related Builder functions error at run-time, reporting the command that cannot be run --- build.zig | 7 ++ lib/std/build.zig | 38 +++++++++-- lib/std/build/RunStep.zig | 20 ++++++ lib/std/child_process.zig | 4 ++ lib/std/process.zig | 8 ++- src/Compilation.zig | 107 ++++++++++++++++------------- src/ThreadPool.zig | 3 + src/libc_installation.zig | 4 +- src/link/Coff.zig | 112 +++++++++++++++++-------------- src/link/Elf.zig | 112 +++++++++++++++++-------------- src/link/Wasm.zig | 112 +++++++++++++++++-------------- src/main.zig | 137 +++++++++++++++++++++++--------------- src/mingw.zig | 57 ++++++++-------- src/test.zig | 11 +++ test/tests.zig | 20 ++++++ 15 files changed, 470 insertions(+), 282 deletions(-) diff --git a/build.zig b/build.zig index f2d154c702..d53d823868 100644 --- a/build.zig +++ b/build.zig @@ -229,6 +229,10 @@ pub fn build(b: *Builder) !void { const version = if (opt_version_string) |version| version else v: { const version_string = b.fmt("{d}.{d}.{d}", .{ zig_version.major, zig_version.minor, zig_version.patch }); + if (!std.process.can_spawn) { + std.debug.print("error: version info cannot be retrieved from git. Zig version must be provided using -Dversion-string\n", .{}); + std.process.exit(1); + } var code: u8 = undefined; const git_describe_untrimmed = b.execAllowFail(&[_][]const u8{ "git", "-C", b.build_root, "describe", "--match", "*.*.*", "--tags", @@ -542,6 +546,9 @@ fn addCxxKnownPath( errtxt: ?[]const u8, need_cpp_includes: bool, ) !void { + if (!std.process.can_spawn) + return error.RequiredLibraryNotFound; + const path_padded = try b.exec(&[_][]const u8{ ctx.cxx_compiler, b.fmt("-print-file-name={s}", .{objname}), diff --git a/lib/std/build.zig b/lib/std/build.zig index 05f36f5714..fcaa115ccb 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -88,7 +88,14 @@ pub const Builder = struct { /// Information about the native target. Computed before build() is invoked. host: NativeTargetInfo, - const PkgConfigError = error{ + pub const ExecError = error{ + ReadFailure, + ExitCodeFailure, + ProcessTerminated, + ExecNotSupported, + } || std.ChildProcess.SpawnError; + + pub const PkgConfigError = error{ PkgConfigCrashed, PkgConfigFailed, PkgConfigNotInstalled, @@ -959,6 +966,9 @@ pub const Builder = struct { printCmd(cwd, argv); } + if (!std.process.can_spawn) + return error.ExecNotSupported; + const child = std.ChildProcess.init(argv, self.allocator) catch unreachable; defer child.deinit(); @@ -1168,9 +1178,12 @@ pub const Builder = struct { argv: []const []const u8, out_code: *u8, stderr_behavior: std.ChildProcess.StdIo, - ) ![]u8 { + ) ExecError![]u8 { assert(argv.len != 0); + if (!std.process.can_spawn) + return error.ExecNotSupported; + const max_output_size = 400 * 1024; const child = try std.ChildProcess.init(argv, self.allocator); defer child.deinit(); @@ -1182,7 +1195,9 @@ pub const Builder = struct { try child.spawn(); - const stdout = try child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size); + const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch { + return error.ReadFailure; + }; errdefer self.allocator.free(stdout); const term = try child.wait(); @@ -1208,8 +1223,21 @@ pub const Builder = struct { printCmd(null, argv); } + if (!std.process.can_spawn) { + if (src_step) |s| warn("{s}...", .{s.name}); + warn("Unable to spawn the following command: cannot spawn child process\n", .{}); + printCmd(null, argv); + std.os.abort(); + } + var code: u8 = undefined; return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) { + error.ExecNotSupported => { + if (src_step) |s| warn("{s}...", .{s.name}); + warn("Unable to spawn the following command: cannot spawn child process\n", .{}); + printCmd(null, argv); + std.os.abort(); + }, error.FileNotFound => { if (src_step) |s| warn("{s}...", .{s.name}); warn("Unable to spawn the following command: file not found\n", .{}); @@ -1260,7 +1288,7 @@ pub const Builder = struct { ) catch unreachable; } - fn execPkgConfigList(self: *Builder, out_code: *u8) ![]const PkgConfigPkg { + fn execPkgConfigList(self: *Builder, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg { const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore); var list = ArrayList(PkgConfigPkg).init(self.allocator); errdefer list.deinit(); @@ -1287,6 +1315,7 @@ pub const Builder = struct { } else |err| { const result = switch (err) { error.ProcessTerminated => error.PkgConfigCrashed, + error.ExecNotSupported => error.PkgConfigFailed, error.ExitCodeFailure => error.PkgConfigFailed, error.FileNotFound => error.PkgConfigNotInstalled, error.InvalidName => error.PkgConfigNotInstalled, @@ -1929,6 +1958,7 @@ pub const LibExeObjStep = struct { "--libs", }, &code, .Ignore)) |stdout| stdout else |err| switch (err) { error.ProcessTerminated => return error.PkgConfigCrashed, + error.ExecNotSupported => return error.PkgConfigFailed, error.ExitCodeFailure => return error.PkgConfigFailed, error.FileNotFound => return error.PkgConfigNotInstalled, else => return err, diff --git a/lib/std/build/RunStep.zig b/lib/std/build/RunStep.zig index 4e18d5d738..6bd1bda952 100644 --- a/lib/std/build/RunStep.zig +++ b/lib/std/build/RunStep.zig @@ -10,6 +10,8 @@ const mem = std.mem; const process = std.process; const ArrayList = std.ArrayList; const BufMap = std.BufMap; +const Allocator = mem.Allocator; +const ExecError = build.Builder.ExecError; const max_stdout_size = 1 * 1024 * 1024; // 1 MiB @@ -136,6 +138,17 @@ pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8 ) catch unreachable; } +fn argvCmd(allocator: Allocator, argv: []const []const u8) ![]u8 { + var cmd = std.ArrayList(u8).init(allocator); + defer cmd.deinit(); + for (argv[0 .. argv.len - 1]) |arg| { + try cmd.appendSlice(arg); + try cmd.append(' '); + } + try cmd.appendSlice(argv[argv.len - 1]); + return cmd.toOwnedSlice(); +} + pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) }; } @@ -175,6 +188,13 @@ fn make(step: *Step) !void { const argv = argv_list.items; + if (!std.process.can_spawn) { + const cmd = try argvCmd(self.builder.allocator, argv); + std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd }); + self.builder.allocator.free(cmd); + return ExecError.ExecNotSupported; + } + const child = std.ChildProcess.init(argv, self.builder.allocator) catch unreachable; defer child.deinit(); diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 7808dcd1e5..4cbe840bba 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -124,6 +124,10 @@ pub const ChildProcess = struct { /// On success must call `kill` or `wait`. pub fn spawn(self: *ChildProcess) SpawnError!void { + if (!std.process.can_spawn) { + @compileError("the target operating system cannot spawn processes"); + } + if (builtin.os.tag == .windows) { return self.spawnWindows(); } else { diff --git a/lib/std/process.zig b/lib/std/process.zig index 699c994abf..c0f11b22ce 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -950,7 +950,13 @@ pub fn getSelfExeSharedLibPaths(allocator: Allocator) error{OutOfMemory}![][:0]u /// Tells whether calling the `execv` or `execve` functions will be a compile error. pub const can_execv = switch (builtin.os.tag) { - .windows, .haiku => false, + .windows, .haiku, .wasi => false, + else => true, +}; + +/// Tells whether spawning child processes is supported (e.g. via ChildProcess) +pub const can_spawn = switch (builtin.os.tag) { + .wasi => false, else => true, }; diff --git a/src/Compilation.zig b/src/Compilation.zig index 21a0c6fe58..f07a7c9dd7 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -25,6 +25,7 @@ const libunwind = @import("libunwind.zig"); const libcxx = @import("libcxx.zig"); const wasi_libc = @import("wasi_libc.zig"); const fatal = @import("main.zig").fatal; +const clangMain = @import("main.zig").clangMain; const Module = @import("Module.zig"); const Cache = @import("Cache.zig"); const stage1 = @import("stage1.zig"); @@ -3667,55 +3668,71 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P dump_argv(argv.items); } - const child = try std.ChildProcess.init(argv.items, arena); - defer child.deinit(); + if (std.process.can_spawn) { + const child = try std.ChildProcess.init(argv.items, arena); + defer child.deinit(); - if (comp.clang_passthrough_mode) { - child.stdin_behavior = .Inherit; - child.stdout_behavior = .Inherit; - child.stderr_behavior = .Inherit; + if (comp.clang_passthrough_mode) { + child.stdin_behavior = .Inherit; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; - const term = child.spawnAndWait() catch |err| { - return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - }; - switch (term) { - .Exited => |code| { - if (code != 0) { - std.process.exit(code); - } - if (comp.clang_preprocessor_mode == .stdout) - std.process.exit(0); - }, - else => std.process.abort(), + const term = child.spawnAndWait() catch |err| { + return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); + }; + switch (term) { + .Exited => |code| { + if (code != 0) { + std.process.exit(code); + } + if (comp.clang_preprocessor_mode == .stdout) + std.process.exit(0); + }, + else => std.process.abort(), + } + } else { + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Ignore; + child.stderr_behavior = .Pipe; + + try child.spawn(); + + const stderr_reader = child.stderr.?.reader(); + + const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); + + const term = child.wait() catch |err| { + return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); + }; + + switch (term) { + .Exited => |code| { + if (code != 0) { + // TODO parse clang stderr and turn it into an error message + // and then call failCObjWithOwnedErrorMsg + log.err("clang failed with stderr: {s}", .{stderr}); + return comp.failCObj(c_object, "clang exited with code {d}", .{code}); + } + }, + else => { + log.err("clang terminated with stderr: {s}", .{stderr}); + return comp.failCObj(c_object, "clang terminated unexpectedly", .{}); + }, + } } } else { - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Ignore; - child.stderr_behavior = .Pipe; - - try child.spawn(); - - const stderr_reader = child.stderr.?.reader(); - - const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); - - const term = child.wait() catch |err| { - return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - }; - - switch (term) { - .Exited => |code| { - if (code != 0) { - // TODO parse clang stderr and turn it into an error message - // and then call failCObjWithOwnedErrorMsg - log.err("clang failed with stderr: {s}", .{stderr}); - return comp.failCObj(c_object, "clang exited with code {d}", .{code}); - } - }, - else => { - log.err("clang terminated with stderr: {s}", .{stderr}); - return comp.failCObj(c_object, "clang terminated unexpectedly", .{}); - }, + const exit_code = try clangMain(arena, argv.items); + if (exit_code != 0) { + if (comp.clang_passthrough_mode) { + std.process.exit(exit_code); + } else { + return comp.failCObj(c_object, "clang exited with code {d}", .{exit_code}); + } + } + if (comp.clang_passthrough_mode and + comp.clang_preprocessor_mode == .stdout) + { + std.process.exit(0); } } diff --git a/src/ThreadPool.zig b/src/ThreadPool.zig index 4f9d8dc015..813d67db66 100644 --- a/src/ThreadPool.zig +++ b/src/ThreadPool.zig @@ -82,6 +82,9 @@ pub fn init(self: *ThreadPool, allocator: std.mem.Allocator) !void { } fn destroyWorkers(self: *ThreadPool, spawned: usize) void { + if (builtin.single_threaded) + return; + for (self.workers[0..spawned]) |*worker| { worker.thread.join(); worker.idle_node.data.deinit(); diff --git a/src/libc_installation.zig b/src/libc_installation.zig index 4cd43c7567..fe1a2b2ca5 100644 --- a/src/libc_installation.zig +++ b/src/libc_installation.zig @@ -216,7 +216,7 @@ pub const LibCInstallation = struct { self.crt_dir = try args.allocator.dupeZ(u8, "/system/develop/lib"); break :blk batch.wait(); }; - } else { + } else if (std.process.can_spawn) { try blk: { var batch = Batch(FindError!void, 2, .auto_async).init(); errdefer batch.wait() catch {}; @@ -229,6 +229,8 @@ pub const LibCInstallation = struct { } break :blk batch.wait(); }; + } else { + return error.LibCRuntimeNotFound; } return self; } diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 894d5dd8f7..8426e5d50c 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -9,6 +9,7 @@ const fs = std.fs; const allocPrint = std.fmt.allocPrint; const mem = std.mem; +const lldMain = @import("../main.zig").lldMain; const trace = @import("../tracy.zig").trace; const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); @@ -1358,60 +1359,71 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { Compilation.dump_argv(argv.items[1..]); } - // Sadly, we must run LLD as a child process because it does not behave - // properly as a library. - const child = try std.ChildProcess.init(argv.items, arena); - defer child.deinit(); + if (std.process.can_spawn) { + // If possible, we run LLD as a child process because it does not always + // behave properly as a library, unfortunately. + // https://github.com/ziglang/zig/issues/3825 + const child = try std.ChildProcess.init(argv.items, arena); + defer child.deinit(); - if (comp.clang_passthrough_mode) { - child.stdin_behavior = .Inherit; - child.stdout_behavior = .Inherit; - child.stderr_behavior = .Inherit; + if (comp.clang_passthrough_mode) { + child.stdin_behavior = .Inherit; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; - const term = child.spawnAndWait() catch |err| { - log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - return error.UnableToSpawnSelf; - }; - switch (term) { - .Exited => |code| { - if (code != 0) { - // TODO https://github.com/ziglang/zig/issues/6342 - std.process.exit(1); - } - }, - else => std.process.abort(), + const term = child.spawnAndWait() catch |err| { + log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); + return error.UnableToSpawnSelf; + }; + switch (term) { + .Exited => |code| { + if (code != 0) { + std.process.exit(code); + } + }, + else => std.process.abort(), + } + } else { + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Ignore; + child.stderr_behavior = .Pipe; + + try child.spawn(); + + const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); + + const term = child.wait() catch |err| { + log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); + return error.UnableToSpawnSelf; + }; + + switch (term) { + .Exited => |code| { + if (code != 0) { + // TODO parse this output and surface with the Compilation API rather than + // directly outputting to stderr here. + std.debug.print("{s}", .{stderr}); + return error.LLDReportedFailure; + } + }, + else => { + log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); + return error.LLDCrashed; + }, + } + + if (stderr.len != 0) { + log.warn("unexpected LLD stderr:\n{s}", .{stderr}); + } } } else { - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Ignore; - child.stderr_behavior = .Pipe; - - try child.spawn(); - - const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); - - const term = child.wait() catch |err| { - log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - return error.UnableToSpawnSelf; - }; - - switch (term) { - .Exited => |code| { - if (code != 0) { - // TODO parse this output and surface with the Compilation API rather than - // directly outputting to stderr here. - std.debug.print("{s}", .{stderr}); - return error.LLDReportedFailure; - } - }, - else => { - log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); - return error.LLDCrashed; - }, - } - - if (stderr.len != 0) { - log.warn("unexpected LLD stderr:\n{s}", .{stderr}); + const exit_code = try lldMain(arena, argv.items); + if (exit_code != 0) { + if (comp.clang_passthrough_mode) { + std.process.exit(exit_code); + } else { + return error.LLDReportedFailure; + } } } } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 37afd68c82..2a550d26e6 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -14,6 +14,7 @@ const leb128 = std.leb; const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); const codegen = @import("../codegen.zig"); +const lldMain = @import("../main.zig").lldMain; const trace = @import("../tracy.zig").trace; const Package = @import("../Package.zig"); const Value = @import("../value.zig").Value; @@ -1950,60 +1951,71 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { Compilation.dump_argv(argv.items[1..]); } - // Sadly, we must run LLD as a child process because it does not behave - // properly as a library. - const child = try std.ChildProcess.init(argv.items, arena); - defer child.deinit(); + if (std.process.can_spawn) { + // If possible, we run LLD as a child process because it does not always + // behave properly as a library, unfortunately. + // https://github.com/ziglang/zig/issues/3825 + const child = try std.ChildProcess.init(argv.items, arena); + defer child.deinit(); - if (comp.clang_passthrough_mode) { - child.stdin_behavior = .Inherit; - child.stdout_behavior = .Inherit; - child.stderr_behavior = .Inherit; + if (comp.clang_passthrough_mode) { + child.stdin_behavior = .Inherit; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; - const term = child.spawnAndWait() catch |err| { - log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - return error.UnableToSpawnSelf; - }; - switch (term) { - .Exited => |code| { - if (code != 0) { - // TODO https://github.com/ziglang/zig/issues/6342 - std.process.exit(1); - } - }, - else => std.process.abort(), + const term = child.spawnAndWait() catch |err| { + log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); + return error.UnableToSpawnSelf; + }; + switch (term) { + .Exited => |code| { + if (code != 0) { + std.process.exit(code); + } + }, + else => std.process.abort(), + } + } else { + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Ignore; + child.stderr_behavior = .Pipe; + + try child.spawn(); + + const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); + + const term = child.wait() catch |err| { + log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); + return error.UnableToSpawnSelf; + }; + + switch (term) { + .Exited => |code| { + if (code != 0) { + // TODO parse this output and surface with the Compilation API rather than + // directly outputting to stderr here. + std.debug.print("{s}", .{stderr}); + return error.LLDReportedFailure; + } + }, + else => { + log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); + return error.LLDCrashed; + }, + } + + if (stderr.len != 0) { + log.warn("unexpected LLD stderr:\n{s}", .{stderr}); + } } } else { - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Ignore; - child.stderr_behavior = .Pipe; - - try child.spawn(); - - const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); - - const term = child.wait() catch |err| { - log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - return error.UnableToSpawnSelf; - }; - - switch (term) { - .Exited => |code| { - if (code != 0) { - // TODO parse this output and surface with the Compilation API rather than - // directly outputting to stderr here. - std.debug.print("{s}", .{stderr}); - return error.LLDReportedFailure; - } - }, - else => { - log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); - return error.LLDCrashed; - }, - } - - if (stderr.len != 0) { - log.warn("unexpected LLD stderr:\n{s}", .{stderr}); + const exit_code = try lldMain(arena, argv.items); + if (exit_code != 0) { + if (comp.clang_passthrough_mode) { + std.process.exit(exit_code); + } else { + return error.LLDReportedFailure; + } } } } diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index b047e4b68a..91952468cc 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -15,6 +15,7 @@ const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); const CodeGen = @import("../arch/wasm/CodeGen.zig"); const link = @import("../link.zig"); +const lldMain = @import("../main.zig").lldMain; const trace = @import("../tracy.zig").trace; const build_options = @import("build_options"); const wasi_libc = @import("../wasi_libc.zig"); @@ -1486,60 +1487,71 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { Compilation.dump_argv(argv.items[1..]); } - // Sadly, we must run LLD as a child process because it does not behave - // properly as a library. - const child = try std.ChildProcess.init(argv.items, arena); - defer child.deinit(); + if (std.process.can_spawn) { + // If possible, we run LLD as a child process because it does not always + // behave properly as a library, unfortunately. + // https://github.com/ziglang/zig/issues/3825 + const child = try std.ChildProcess.init(argv.items, arena); + defer child.deinit(); - if (comp.clang_passthrough_mode) { - child.stdin_behavior = .Inherit; - child.stdout_behavior = .Inherit; - child.stderr_behavior = .Inherit; + if (comp.clang_passthrough_mode) { + child.stdin_behavior = .Inherit; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; - const term = child.spawnAndWait() catch |err| { - log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - return error.UnableToSpawnSelf; - }; - switch (term) { - .Exited => |code| { - if (code != 0) { - // TODO https://github.com/ziglang/zig/issues/6342 - std.process.exit(1); - } - }, - else => std.process.abort(), + const term = child.spawnAndWait() catch |err| { + log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); + return error.UnableToSpawnSelf; + }; + switch (term) { + .Exited => |code| { + if (code != 0) { + std.process.exit(code); + } + }, + else => std.process.abort(), + } + } else { + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Ignore; + child.stderr_behavior = .Pipe; + + try child.spawn(); + + const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); + + const term = child.wait() catch |err| { + log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); + return error.UnableToSpawnSelf; + }; + + switch (term) { + .Exited => |code| { + if (code != 0) { + // TODO parse this output and surface with the Compilation API rather than + // directly outputting to stderr here. + std.debug.print("{s}", .{stderr}); + return error.LLDReportedFailure; + } + }, + else => { + log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); + return error.LLDCrashed; + }, + } + + if (stderr.len != 0) { + log.warn("unexpected LLD stderr:\n{s}", .{stderr}); + } } } else { - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Ignore; - child.stderr_behavior = .Pipe; - - try child.spawn(); - - const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); - - const term = child.wait() catch |err| { - log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); - return error.UnableToSpawnSelf; - }; - - switch (term) { - .Exited => |code| { - if (code != 0) { - // TODO parse this output and surface with the Compilation API rather than - // directly outputting to stderr here. - std.debug.print("{s}", .{stderr}); - return error.LLDReportedFailure; - } - }, - else => { - log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); - return error.LLDCrashed; - }, - } - - if (stderr.len != 0) { - log.warn("unexpected LLD stderr:\n{s}", .{stderr}); + const exit_code = try lldMain(arena, argv.items); + if (exit_code != 0) { + if (comp.clang_passthrough_mode) { + std.process.exit(exit_code); + } else { + return error.LLDReportedFailure; + } } } } diff --git a/src/main.zig b/src/main.zig index bbdb948c90..d6688081f1 100644 --- a/src/main.zig +++ b/src/main.zig @@ -221,7 +221,7 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi mem.eql(u8, cmd, "lib") or mem.eql(u8, cmd, "ar")) { - return punt_to_llvm_ar(arena, args); + return process.exit(try llvmArMain(arena, args)); } else if (mem.eql(u8, cmd, "cc")) { return buildOutputType(gpa, arena, args, .cc); } else if (mem.eql(u8, cmd, "c++")) { @@ -231,12 +231,12 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi } else if (mem.eql(u8, cmd, "clang") or mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as")) { - return punt_to_clang(arena, args); + return process.exit(try clangMain(arena, args)); } else if (mem.eql(u8, cmd, "ld.lld") or mem.eql(u8, cmd, "lld-link") or mem.eql(u8, cmd, "wasm-ld")) { - return punt_to_lld(arena, args); + return process.exit(try lldMain(arena, args)); } else if (mem.eql(u8, cmd, "build")) { return cmdBuild(gpa, arena, cmd_args); } else if (mem.eql(u8, cmd, "fmt")) { @@ -1347,7 +1347,7 @@ fn buildOutputType( .ignore => {}, .driver_punt => { // Never mind what we're doing, just pass the args directly. For example --help. - return punt_to_clang(arena, all_args); + return process.exit(try clangMain(arena, all_args)); }, .pic => want_pic = true, .no_pic => want_pic = false, @@ -1866,7 +1866,7 @@ fn buildOutputType( // An error message is generated when there is more than 1 C source file. if (c_source_files.items.len != 1) { // For example `zig cc` and no args should print the "no input files" message. - return punt_to_clang(arena, all_args); + return process.exit(try clangMain(arena, all_args)); } if (out_path) |p| { emit_bin = .{ .yes = p }; @@ -1882,7 +1882,7 @@ fn buildOutputType( { // For example `zig cc` and no args should print the "no input files" message. // There could be other reasons to punt to clang, for example, --help. - return punt_to_clang(arena, all_args); + return process.exit(try clangMain(arena, all_args)); } }, } @@ -2883,7 +2883,7 @@ fn runOrTest( try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info, link_libc); const cmd = try argvCmd(arena, argv.items); fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd }); - } else { + } else if (std.process.can_spawn) { const child = try std.ChildProcess.init(argv.items, gpa); defer child.deinit(); @@ -2943,6 +2943,9 @@ fn runOrTest( }, else => unreachable, } + } else { + const cmd = try argvCmd(arena, argv.items); + fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd }); } } @@ -3553,29 +3556,35 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi break :argv child_argv.items; }; - const child = try std.ChildProcess.init(child_argv, gpa); - defer child.deinit(); - child.stdin_behavior = .Inherit; - child.stdout_behavior = .Inherit; - child.stderr_behavior = .Inherit; + if (std.process.can_spawn) { + const child = try std.ChildProcess.init(child_argv, gpa); + defer child.deinit(); - const term = try child.spawnAndWait(); - switch (term) { - .Exited => |code| { - if (code == 0) return cleanExit(); + child.stdin_behavior = .Inherit; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; - if (prominent_compile_errors) { - fatal("the build command failed with exit code {d}", .{code}); - } else { + const term = try child.spawnAndWait(); + switch (term) { + .Exited => |code| { + if (code == 0) return cleanExit(); + + if (prominent_compile_errors) { + fatal("the build command failed with exit code {d}", .{code}); + } else { + const cmd = try argvCmd(arena, child_argv); + fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); + } + }, + else => { const cmd = try argvCmd(arena, child_argv); - fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); - } - }, - else => { - const cmd = try argvCmd(arena, child_argv); - fatal("the following build command crashed:\n{s}", .{cmd}); - }, + fatal("the following build command crashed:\n{s}", .{cmd}); + }, + } + } else { + const cmd = try argvCmd(arena, child_argv); + fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd }); } } @@ -4080,51 +4089,69 @@ pub const info_zen = extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; -/// TODO https://github.com/ziglang/zig/issues/3257 -fn punt_to_clang(arena: Allocator, args: []const []const u8) error{OutOfMemory} { - if (!build_options.have_llvm) - fatal("`zig cc` and `zig c++` unavailable: compiler built without LLVM extensions", .{}); - // Convert the args to the format Clang expects. - const argv = try arena.alloc(?[*:0]u8, args.len + 1); +fn argsCopyZ(alloc: Allocator, args: []const []const u8) ![:null]?[*:0]u8 { + var argv = try alloc.allocSentinel(?[*:0]u8, args.len, null); for (args) |arg, i| { - argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation. + argv[i] = try alloc.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation. } - argv[args.len] = null; - const exit_code = ZigClang_main(@intCast(c_int, args.len), argv[0..args.len :null].ptr); - process.exit(@bitCast(u8, @truncate(i8, exit_code))); + return argv; } -/// TODO https://github.com/ziglang/zig/issues/3257 -fn punt_to_llvm_ar(arena: Allocator, args: []const []const u8) error{OutOfMemory} { +pub fn clangMain(alloc: Allocator, args: []const []const u8) error{OutOfMemory}!u8 { + if (!build_options.have_llvm) + fatal("`zig cc` and `zig c++` unavailable: compiler built without LLVM extensions", .{}); + + var arena_instance = std.heap.ArenaAllocator.init(alloc); + defer arena_instance.deinit(); + const arena = arena_instance.allocator(); + + // Convert the args to the null-terminated format Clang expects. + const argv = try argsCopyZ(arena, args); + const exit_code = ZigClang_main(@intCast(c_int, argv.len), argv.ptr); + return @bitCast(u8, @truncate(i8, exit_code)); +} + +pub fn llvmArMain(alloc: Allocator, args: []const []const u8) error{OutOfMemory}!u8 { if (!build_options.have_llvm) fatal("`zig ar`, `zig dlltool`, `zig ranlib', and `zig lib` unavailable: compiler built without LLVM extensions", .{}); + var arena_instance = std.heap.ArenaAllocator.init(alloc); + defer arena_instance.deinit(); + const arena = arena_instance.allocator(); + // Convert the args to the format llvm-ar expects. - // We subtract 1 to shave off the zig binary from args[0]. - const argv = try arena.allocSentinel(?[*:0]u8, args.len - 1, null); - for (args[1..]) |arg, i| { - // TODO If there was an argsAllocZ we could avoid this allocation. - argv[i] = try arena.dupeZ(u8, arg); - } - const argc = @intCast(c_int, argv.len); - const exit_code = ZigLlvmAr_main(argc, argv.ptr); - process.exit(@bitCast(u8, @truncate(i8, exit_code))); + // We intentionally shave off the zig binary at args[0]. + const argv = try argsCopyZ(arena, args[1..]); + const exit_code = ZigLlvmAr_main(@intCast(c_int, argv.len), argv.ptr); + return @bitCast(u8, @truncate(i8, exit_code)); } /// The first argument determines which backend is invoked. The options are: /// * `ld.lld` - ELF /// * `lld-link` - COFF /// * `wasm-ld` - WebAssembly -/// TODO https://github.com/ziglang/zig/issues/3257 -pub fn punt_to_lld(arena: Allocator, args: []const []const u8) error{OutOfMemory} { +pub fn lldMain(alloc: Allocator, args: []const []const u8) error{OutOfMemory}!u8 { if (!build_options.have_llvm) fatal("`zig {s}` unavailable: compiler built without LLVM extensions", .{args[0]}); - // Convert the args to the format LLD expects. - // We subtract 1 to shave off the zig binary from args[0]. - const argv = try arena.allocSentinel(?[*:0]const u8, args.len - 1, null); - for (args[1..]) |arg, i| { - argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation. + + // Print a warning if lld is called multiple times in the same process, + // since it may misbehave + // https://github.com/ziglang/zig/issues/3825 + const CallCounter = struct { + var count: usize = 0; + }; + if (CallCounter.count == 1) { // Issue the warning on the first repeat call + warn("calling lldMain repeatedly within the same process can have side effects (https://github.com/ziglang/zig/issues/3825)", .{}); } + CallCounter.count += 1; + + var arena_instance = std.heap.ArenaAllocator.init(alloc); + defer arena_instance.deinit(); + const arena = arena_instance.allocator(); + + // Convert the args to the format llvm-ar expects. + // We intentionally shave off the zig binary at args[0]. + const argv = try argsCopyZ(arena, args[1..]); const exit_code = rc: { const llvm = @import("codegen/llvm/bindings.zig"); const argc = @intCast(c_int, argv.len); @@ -4138,7 +4165,7 @@ pub fn punt_to_lld(arena: Allocator, args: []const []const u8) error{OutOfMemory unreachable; } }; - process.exit(@bitCast(u8, @truncate(i8, exit_code))); + return @bitCast(u8, @truncate(i8, exit_code)); } const clang_args = @import("clang_options.zig").list; diff --git a/src/mingw.zig b/src/mingw.zig index f555634459..84ec0795f1 100644 --- a/src/mingw.zig +++ b/src/mingw.zig @@ -5,6 +5,7 @@ const path = std.fs.path; const assert = std.debug.assert; const log = std.log.scoped(.mingw); +const builtin = @import("builtin"); const target_util = @import("target.zig"); const Compilation = @import("Compilation.zig"); const build_options = @import("build_options"); @@ -367,39 +368,43 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { Compilation.dump_argv(&args); } - const child = try std.ChildProcess.init(&args, arena); - defer child.deinit(); + if (std.process.can_spawn) { + const child = try std.ChildProcess.init(&args, arena); + defer child.deinit(); - child.stdin_behavior = .Ignore; - child.stdout_behavior = .Pipe; - child.stderr_behavior = .Pipe; + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Pipe; + child.stderr_behavior = .Pipe; - try child.spawn(); + try child.spawn(); - const stderr_reader = child.stderr.?.reader(); + const stderr_reader = child.stderr.?.reader(); - // TODO https://github.com/ziglang/zig/issues/6343 - const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); + // TODO https://github.com/ziglang/zig/issues/6343 + const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); - const term = child.wait() catch |err| { - // TODO surface a proper error here - log.err("unable to spawn {s}: {s}", .{ args[0], @errorName(err) }); - return error.ClangPreprocessorFailed; - }; - - switch (term) { - .Exited => |code| { - if (code != 0) { - // TODO surface a proper error here - log.err("clang exited with code {d} and stderr: {s}", .{ code, stderr }); - return error.ClangPreprocessorFailed; - } - }, - else => { + const term = child.wait() catch |err| { // TODO surface a proper error here - log.err("clang terminated unexpectedly with stderr: {s}", .{stderr}); + log.err("unable to spawn {s}: {s}", .{ args[0], @errorName(err) }); return error.ClangPreprocessorFailed; - }, + }; + switch (term) { + .Exited => |code| { + if (code != 0) { + // TODO surface a proper error here + log.err("clang exited with code {d} and stderr: {s}", .{ code, stderr }); + return error.ClangPreprocessorFailed; + } + }, + else => { + // TODO surface a proper error here + log.err("clang terminated unexpectedly with stderr: {s}", .{stderr}); + return error.ClangPreprocessorFailed; + }, + } + } else { + log.err("unable to spawn {s}: spawning child process not supported on {s}", .{ args[0], @tagName(builtin.os.tag) }); + return error.ClangPreprocessorFailed; } const lib_final_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{ diff --git a/src/test.zig b/src/test.zig index e02ea04f1c..b73e11d7f5 100644 --- a/src/test.zig +++ b/src/test.zig @@ -730,6 +730,12 @@ pub const TestContext = struct { // * cannot handle updates // because of this we must spawn a child process rather than // using Compilation directly. + + if (!std.process.can_spawn) { + print("Unable to spawn child processes on {s}, skipping test.\n", .{@tagName(builtin.os.tag)}); + return; // Pass test. + } + assert(case.updates.items.len == 1); const update = case.updates.items[0]; try tmp.dir.writeFile(tmp_src_path, update.src); @@ -1104,6 +1110,11 @@ pub const TestContext = struct { } }, .Execution => |expected_stdout| { + if (!std.process.can_spawn) { + print("Unable to spawn child processes on {s}, skipping test.\n", .{@tagName(builtin.os.tag)}); + return; // Pass test. + } + update_node.setEstimatedTotalItems(4); var argv = std.ArrayList([]const u8).init(allocator); diff --git a/test/tests.zig b/test/tests.zig index 70387b8d47..bd6c82cbb7 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -10,6 +10,8 @@ const fmt = std.fmt; const ArrayList = std.ArrayList; const Mode = std.builtin.Mode; const LibExeObjStep = build.LibExeObjStep; +const Allocator = mem.Allocator; +const ExecError = build.Builder.ExecError; // Cases const compare_output = @import("compare_output.zig"); @@ -26,6 +28,17 @@ pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext; pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTranslatedCContext; pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutputContext; +fn argvCmd(allocator: Allocator, argv: []const []const u8) ![]u8 { + var cmd = std.ArrayList(u8).init(allocator); + defer cmd.deinit(); + for (argv[0 .. argv.len - 1]) |arg| { + try cmd.appendSlice(arg); + try cmd.append(' '); + } + try cmd.appendSlice(argv[argv.len - 1]); + return cmd.toOwnedSlice(); +} + const TestTarget = struct { target: CrossTarget = @as(CrossTarget, .{}), mode: std.builtin.Mode = .Debug, @@ -722,6 +735,13 @@ pub const StackTracesContext = struct { std.debug.print("Test {d}/{d} {s}...", .{ self.test_index + 1, self.context.test_index, self.name }); + if (!std.process.can_spawn) { + const cmd = try argvCmd(b.allocator, args.items); + std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd }); + b.allocator.free(cmd); + return ExecError.ExecNotSupported; + } + const child = std.ChildProcess.init(args.items, b.allocator) catch unreachable; defer child.deinit(); From c1cf158729f4d726639a5695754957f9f45f89da Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Sat, 5 Feb 2022 09:09:55 -0700 Subject: [PATCH 0062/2031] Replace argvCmd with std.mem.join --- lib/std/build/RunStep.zig | 13 +------------ src/main.zig | 27 ++++++++------------------- test/tests.zig | 13 +------------ 3 files changed, 10 insertions(+), 43 deletions(-) diff --git a/lib/std/build/RunStep.zig b/lib/std/build/RunStep.zig index 6bd1bda952..e8544921d9 100644 --- a/lib/std/build/RunStep.zig +++ b/lib/std/build/RunStep.zig @@ -138,17 +138,6 @@ pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8 ) catch unreachable; } -fn argvCmd(allocator: Allocator, argv: []const []const u8) ![]u8 { - var cmd = std.ArrayList(u8).init(allocator); - defer cmd.deinit(); - for (argv[0 .. argv.len - 1]) |arg| { - try cmd.appendSlice(arg); - try cmd.append(' '); - } - try cmd.appendSlice(argv[argv.len - 1]); - return cmd.toOwnedSlice(); -} - pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) }; } @@ -189,7 +178,7 @@ fn make(step: *Step) !void { const argv = argv_list.items; if (!std.process.can_spawn) { - const cmd = try argvCmd(self.builder.allocator, argv); + const cmd = try std.mem.join(self.builder.allocator, " ", argv); std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd }); self.builder.allocator.free(cmd); return ExecError.ExecNotSupported; diff --git a/src/main.zig b/src/main.zig index d6688081f1..6a1416cd6b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2881,7 +2881,7 @@ fn runOrTest( // execv releases the locks; no need to destroy the Compilation here. const err = std.process.execv(gpa, argv.items); try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info, link_libc); - const cmd = try argvCmd(arena, argv.items); + const cmd = try std.mem.join(arena, " ", argv.items); fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd }); } else if (std.process.can_spawn) { const child = try std.ChildProcess.init(argv.items, gpa); @@ -2900,7 +2900,7 @@ fn runOrTest( const term = child.spawnAndWait() catch |err| { try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info, link_libc); - const cmd = try argvCmd(arena, argv.items); + const cmd = try std.mem.join(arena, " ", argv.items); fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd }); }; switch (arg_mode) { @@ -2931,12 +2931,12 @@ fn runOrTest( if (code == 0) { if (!watch) return cleanExit(); } else { - const cmd = try argvCmd(arena, argv.items); + const cmd = try std.mem.join(arena, " ", argv.items); fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd }); } }, else => { - const cmd = try argvCmd(arena, argv.items); + const cmd = try std.mem.join(arena, " ", argv.items); fatal("the following test command crashed:\n{s}", .{cmd}); }, } @@ -2944,7 +2944,7 @@ fn runOrTest( else => unreachable, } } else { - const cmd = try argvCmd(arena, argv.items); + const cmd = try std.mem.join(arena, " ", argv.items); fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd }); } } @@ -3573,32 +3573,21 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi if (prominent_compile_errors) { fatal("the build command failed with exit code {d}", .{code}); } else { - const cmd = try argvCmd(arena, child_argv); + const cmd = try std.mem.join(arena, " ", child_argv); fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); } }, else => { - const cmd = try argvCmd(arena, child_argv); + const cmd = try std.mem.join(arena, " ", child_argv); fatal("the following build command crashed:\n{s}", .{cmd}); }, } } else { - const cmd = try argvCmd(arena, child_argv); + const cmd = try std.mem.join(arena, " ", child_argv); fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd }); } } -fn argvCmd(allocator: Allocator, argv: []const []const u8) ![]u8 { - var cmd = std.ArrayList(u8).init(allocator); - defer cmd.deinit(); - for (argv[0 .. argv.len - 1]) |arg| { - try cmd.appendSlice(arg); - try cmd.append(' '); - } - try cmd.appendSlice(argv[argv.len - 1]); - return cmd.toOwnedSlice(); -} - fn readSourceFileToEndAlloc( allocator: mem.Allocator, input: *const fs.File, diff --git a/test/tests.zig b/test/tests.zig index bd6c82cbb7..5b15da2bcb 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -28,17 +28,6 @@ pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext; pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTranslatedCContext; pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutputContext; -fn argvCmd(allocator: Allocator, argv: []const []const u8) ![]u8 { - var cmd = std.ArrayList(u8).init(allocator); - defer cmd.deinit(); - for (argv[0 .. argv.len - 1]) |arg| { - try cmd.appendSlice(arg); - try cmd.append(' '); - } - try cmd.appendSlice(argv[argv.len - 1]); - return cmd.toOwnedSlice(); -} - const TestTarget = struct { target: CrossTarget = @as(CrossTarget, .{}), mode: std.builtin.Mode = .Debug, @@ -736,7 +725,7 @@ pub const StackTracesContext = struct { std.debug.print("Test {d}/{d} {s}...", .{ self.test_index + 1, self.context.test_index, self.name }); if (!std.process.can_spawn) { - const cmd = try argvCmd(b.allocator, args.items); + const cmd = try std.mem.join(b.allocator, " ", args.items); std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd }); b.allocator.free(cmd); return ExecError.ExecNotSupported; From 33fa29601921d88097a1ee3c0d92b93047a5186d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 6 Feb 2022 22:29:40 -0700 Subject: [PATCH 0063/2031] stage2: pass proper can_exit_early value to LLD and adjust the warning message for invoking LLD twice in the same process. --- src/link/Coff.zig | 2 +- src/link/Elf.zig | 2 +- src/link/Wasm.zig | 2 +- src/main.zig | 16 ++++++++++------ 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 8426e5d50c..bc5837fe47 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -1417,7 +1417,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { } } } else { - const exit_code = try lldMain(arena, argv.items); + const exit_code = try lldMain(arena, argv.items, false); if (exit_code != 0) { if (comp.clang_passthrough_mode) { std.process.exit(exit_code); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 2a550d26e6..afaf41a2f9 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2009,7 +2009,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { } } } else { - const exit_code = try lldMain(arena, argv.items); + const exit_code = try lldMain(arena, argv.items, false); if (exit_code != 0) { if (comp.clang_passthrough_mode) { std.process.exit(exit_code); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 91952468cc..e6988e9232 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -1545,7 +1545,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { } } } else { - const exit_code = try lldMain(arena, argv.items); + const exit_code = try lldMain(arena, argv.items, false); if (exit_code != 0) { if (comp.clang_passthrough_mode) { std.process.exit(exit_code); diff --git a/src/main.zig b/src/main.zig index 6a1416cd6b..3f38fd1f78 100644 --- a/src/main.zig +++ b/src/main.zig @@ -236,7 +236,7 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi mem.eql(u8, cmd, "lld-link") or mem.eql(u8, cmd, "wasm-ld")) { - return process.exit(try lldMain(arena, args)); + return process.exit(try lldMain(arena, args, true)); } else if (mem.eql(u8, cmd, "build")) { return cmdBuild(gpa, arena, cmd_args); } else if (mem.eql(u8, cmd, "fmt")) { @@ -4119,7 +4119,11 @@ pub fn llvmArMain(alloc: Allocator, args: []const []const u8) error{OutOfMemory} /// * `ld.lld` - ELF /// * `lld-link` - COFF /// * `wasm-ld` - WebAssembly -pub fn lldMain(alloc: Allocator, args: []const []const u8) error{OutOfMemory}!u8 { +pub fn lldMain( + alloc: Allocator, + args: []const []const u8, + can_exit_early: bool, +) error{OutOfMemory}!u8 { if (!build_options.have_llvm) fatal("`zig {s}` unavailable: compiler built without LLVM extensions", .{args[0]}); @@ -4130,7 +4134,7 @@ pub fn lldMain(alloc: Allocator, args: []const []const u8) error{OutOfMemory}!u8 var count: usize = 0; }; if (CallCounter.count == 1) { // Issue the warning on the first repeat call - warn("calling lldMain repeatedly within the same process can have side effects (https://github.com/ziglang/zig/issues/3825)", .{}); + warn("invoking LLD for the second time within the same process because the host OS ({s}) does not support spawning child processes. This sometimes activates LLD bugs", .{@tagName(builtin.os.tag)}); } CallCounter.count += 1; @@ -4145,11 +4149,11 @@ pub fn lldMain(alloc: Allocator, args: []const []const u8) error{OutOfMemory}!u8 const llvm = @import("codegen/llvm/bindings.zig"); const argc = @intCast(c_int, argv.len); if (mem.eql(u8, args[1], "ld.lld")) { - break :rc llvm.LinkELF(argc, argv.ptr, true); + break :rc llvm.LinkELF(argc, argv.ptr, can_exit_early); } else if (mem.eql(u8, args[1], "lld-link")) { - break :rc llvm.LinkCOFF(argc, argv.ptr, true); + break :rc llvm.LinkCOFF(argc, argv.ptr, can_exit_early); } else if (mem.eql(u8, args[1], "wasm-ld")) { - break :rc llvm.LinkWasm(argc, argv.ptr, true); + break :rc llvm.LinkWasm(argc, argv.ptr, can_exit_early); } else { unreachable; } From 5944e89016219138f6d5d9c818c7ce323eb64c1d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 5 Feb 2022 15:55:17 +0100 Subject: [PATCH 0064/2031] stage2: lower unnamed constants in Elf and MachO * link: add a virtual function `lowerUnnamedConsts`, similar to `updateFunc` or `updateDecl` which needs to be implemented by the linker backend in order to be used with the `CodeGen` code * elf: implement `lowerUnnamedConsts` specialization where we lower unnamed constants to `.rodata` section. We keep track of the atoms encompassing the lowered unnamed consts in a global table indexed by parent `Decl`. When the `Decl` is updated or destroyed, we clear the unnamed consts referenced within the `Decl`. * macho: implement `lowerUnnamedConsts` specialization where we lower unnamed constants to `__TEXT,__const` section. We keep track of the atoms encompassing the lowered unnamed consts in a global table indexed by parent `Decl`. When the `Decl` is updated or destroyed, we clear the unnamed consts referenced within the `Decl`. * x64: change `MCValue.linker_sym_index` into two `MCValue`s: `.got_load` and `.direct_load`. The former signifies to the emitter that it should emit a GOT load relocation, while the latter that it should emit a direct load (`SIGNED`) relocation. * x64: lower `struct` instantiations --- src/arch/x86_64/CodeGen.zig | 91 ++++++--- src/arch/x86_64/Emit.zig | 71 ++++--- src/arch/x86_64/Mir.zig | 17 +- src/arch/x86_64/PrintMir.zig | 38 ++-- src/link.zig | 20 ++ src/link/Coff.zig | 9 + src/link/Elf.zig | 189 +++++++++++++++++-- src/link/MachO.zig | 351 +++++++++++++++++++++++++++-------- src/link/MachO/Atom.zig | 88 ++------- src/link/Plan9.zig | 9 + test/behavior/align.zig | 1 + test/behavior/cast.zig | 23 ++- test/behavior/struct.zig | 21 +++ test/stage2/x86_64.zig | 88 +++++++++ 14 files changed, 770 insertions(+), 246 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index e5c7c99501..535e65000a 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -118,10 +118,14 @@ pub const MCValue = union(enum) { /// The value is in memory at a hard-coded address. /// If the type is a pointer, it means the pointer address is at this memory location. memory: u64, - /// The value is in memory but not allocated an address yet by the linker, so we store - /// the symbol index instead. - /// If the type is a pointer, it means the pointer is the symbol. - linker_sym_index: u32, + /// The value is in memory referenced indirectly via a GOT entry index. + /// If the type is a pointer, it means the pointer is referenced indirectly via GOT. + /// When lowered, linker will emit a relocation of type X86_64_RELOC_GOT. + got_load: u32, + /// The value is in memory referenced directly via symbol index. + /// If the type is a pointer, it means the pointer is referenced directly via symbol index. + /// When lowered, linker will emit a relocation of type X86_64_RELOC_SIGNED. + direct_load: u32, /// The value is one of the stack variables. /// If the type is a pointer, it means the pointer address is in the stack at this offset. stack_offset: i32, @@ -1691,7 +1695,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo } }, .memory, - .linker_sym_index, + .got_load, + .direct_load, => { const reg = try self.copyToTmpRegister(ptr_ty, ptr); try self.load(dst_mcv, .{ .register = reg }, ptr_ty); @@ -1823,7 +1828,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type }, } }, - .linker_sym_index, + .got_load, + .direct_load, .memory, => { value.freezeIfRegister(&self.register_manager); @@ -1831,15 +1837,22 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type const addr_reg: Register = blk: { switch (ptr) { - .linker_sym_index => |sym_index| { + .got_load, + .direct_load, + => |sym_index| { + const flags: u2 = switch (ptr) { + .got_load => 0b00, + .direct_load => 0b01, + else => unreachable, + }; const addr_reg = try self.register_manager.allocReg(null); _ = try self.addInst(.{ - .tag = .lea, + .tag = .lea_pie, .ops = (Mir.Ops{ .reg1 = addr_reg.to64(), - .flags = 0b10, + .flags = flags, }).encode(), - .data = .{ .got_entry = sym_index }, + .data = .{ .linker_sym_index = sym_index }, }); break :blk addr_reg; }, @@ -2160,7 +2173,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .embedded_in_code, .memory => { return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{}); }, - .linker_sym_index => { + .got_load, .direct_load => { return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{}); }, .stack_offset => |off| { @@ -2247,7 +2260,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .embedded_in_code, .memory, .stack_offset => { return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{}); }, - .linker_sym_index => { + .got_load, .direct_load => { return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{}); }, .compare_flags_unsigned => { @@ -2261,7 +2274,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .embedded_in_code, .memory => { return self.fail("TODO implement x86 ADD/SUB/CMP destination memory", .{}); }, - .linker_sym_index => { + .got_load, .direct_load => { return self.fail("TODO implement x86 ADD/SUB/CMP destination symbol at index", .{}); }, } @@ -2317,7 +2330,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! .embedded_in_code, .memory, .stack_offset => { return self.fail("TODO implement x86 multiply source memory", .{}); }, - .linker_sym_index => { + .got_load, .direct_load => { return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); }, .compare_flags_unsigned => { @@ -2358,7 +2371,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! .embedded_in_code, .memory, .stack_offset => { return self.fail("TODO implement x86 multiply source memory", .{}); }, - .linker_sym_index => { + .got_load, .direct_load => { return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); }, .compare_flags_unsigned => { @@ -2372,7 +2385,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! .embedded_in_code, .memory => { return self.fail("TODO implement x86 multiply destination memory", .{}); }, - .linker_sym_index => { + .got_load, .direct_load => { return self.fail("TODO implement x86 multiply destination symbol at index in linker", .{}); }, } @@ -2478,7 +2491,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { .dead => unreachable, .embedded_in_code => unreachable, .memory => unreachable, - .linker_sym_index => unreachable, + .got_load => unreachable, + .direct_load => unreachable, .compare_flags_signed => unreachable, .compare_flags_unsigned => unreachable, } @@ -2540,7 +2554,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { if (func_value.castTag(.function)) |func_payload| { const func = func_payload.data; try self.genSetReg(Type.initTag(.usize), .rax, .{ - .linker_sym_index = func.owner_decl.link.macho.local_sym_index, + .got_load = func.owner_decl.link.macho.local_sym_index, }); // callq *%rax _ = try self.addInst(.{ @@ -3576,7 +3590,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro }, .memory, .embedded_in_code, - .linker_sym_index, + .got_load, + .direct_load, => { if (ty.abiSize(self.target.*) <= 8) { const reg = try self.copyToTmpRegister(ty, mcv); @@ -3982,14 +3997,21 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .data = undefined, }); }, - .linker_sym_index => |sym_index| { + .got_load, + .direct_load, + => |sym_index| { + const flags: u2 = switch (mcv) { + .got_load => 0b00, + .direct_load => 0b01, + else => unreachable, + }; _ = try self.addInst(.{ - .tag = .lea, + .tag = .lea_pie, .ops = (Mir.Ops{ .reg1 = reg, - .flags = 0b10, + .flags = flags, }).encode(), - .data = .{ .got_entry = sym_index }, + .data = .{ .linker_sym_index = sym_index }, }); // MOV reg, [reg] _ = try self.addInst(.{ @@ -4316,7 +4338,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa } else if (self.bin_file.cast(link.File.MachO)) |_| { // Because MachO is PIE-always-on, we defer memory address resolution until // the linker has enough info to perform relocations. - return MCValue{ .linker_sym_index = decl.link.macho.local_sym_index }; + return MCValue{ .got_load = decl.link.macho.local_sym_index }; } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; return MCValue{ .memory = got_addr }; @@ -4331,6 +4353,24 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa _ = tv; } +fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { + const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| { + return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); + }; + if (self.bin_file.cast(link.File.Elf)) |elf_file| { + const vaddr = elf_file.local_symbols.items[local_sym_index].st_value; + return MCValue{ .memory = vaddr }; + } else if (self.bin_file.cast(link.File.MachO)) |_| { + return MCValue{ .direct_load = local_sym_index }; + } else if (self.bin_file.cast(link.File.Coff)) |_| { + return self.fail("TODO lower unnamed const in COFF", .{}); + } else if (self.bin_file.cast(link.File.Plan9)) |_| { + return self.fail("TODO lower unnamed const in Plan9", .{}); + } else { + return self.fail("TODO lower unnamed const", .{}); + } +} + fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { if (typed_value.val.isUndef()) return MCValue{ .undef = {} }; @@ -4446,6 +4486,9 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty}); }, + .Struct => { + return self.lowerUnnamedConst(typed_value); + }, else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}), } } diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 274d1e86b5..be26354402 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -131,6 +131,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .movabs => try emit.mirMovabs(inst), .lea => try emit.mirLea(inst), + .lea_pie => try emit.mirLeaPie(inst), .imul_complex => try emit.mirIMulComplex(inst), @@ -706,36 +707,6 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { mem.writeIntLittle(i32, emit.code.items[end_offset - 4 ..][0..4], disp); }, 0b10 => { - // lea reg1, [rip + reloc] - // RM - try lowerToRmEnc( - .lea, - ops.reg1, - RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0), - emit.code, - ); - const end_offset = emit.code.items.len; - const got_entry = emit.mir.instructions.items(.data)[inst].got_entry; - if (emit.bin_file.cast(link.File.MachO)) |macho_file| { - // TODO I think the reloc might be in the wrong place. - const decl = macho_file.active_decl.?; - try decl.link.macho.relocs.append(emit.bin_file.allocator, .{ - .offset = @intCast(u32, end_offset - 4), - .target = .{ .local = got_entry }, - .addend = 0, - .subtractor = null, - .pcrel = true, - .length = 2, - .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), - }); - } else { - return emit.fail( - "TODO implement lea reg, [rip + reloc] for linking backends different than MachO", - .{}, - ); - } - }, - 0b11 => { // lea reg, [rbp + rcx + imm32] const imm = emit.mir.instructions.items(.data)[inst].imm; const src_reg: ?Register = if (ops.reg2 == .none) null else ops.reg2; @@ -754,6 +725,46 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { emit.code, ); }, + 0b11 => return emit.fail("TODO unused LEA variant 0b11", .{}), + } +} + +fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; + assert(tag == .lea_pie); + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + + // lea reg1, [rip + reloc] + // RM + try lowerToRmEnc( + .lea, + ops.reg1, + RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0), + emit.code, + ); + const end_offset = emit.code.items.len; + const reloc_type = switch (ops.flags) { + 0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), + 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), + else => return emit.fail("TODO unused LEA PIE variants 0b10 and 0b11", .{}), + }; + const sym_index = emit.mir.instructions.items(.data)[inst].linker_sym_index; + if (emit.bin_file.cast(link.File.MachO)) |macho_file| { + const decl = macho_file.active_decl.?; + try decl.link.macho.relocs.append(emit.bin_file.allocator, .{ + .offset = @intCast(u32, end_offset - 4), + .target = .{ .local = sym_index }, + .addend = 0, + .subtractor = null, + .pcrel = true, + .length = 2, + .@"type" = reloc_type, + }); + } else { + return emit.fail( + "TODO implement lea reg, [rip + reloc] for linking backends different than MachO", + .{}, + ); } } diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 181d4c92e0..2e8a9cf332 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -202,13 +202,16 @@ pub const Inst = struct { /// 0b00 reg1, [reg2 + imm32] /// 0b00 reg1, [ds:imm32] /// 0b01 reg1, [rip + imm32] - /// 0b10 reg1, [rip + reloc] - /// 0b11 reg1, [reg2 + rcx + imm32] - /// Notes: - /// * if flags are 0b10, `Data` contains `got_entry` for the linker to generate - /// a valid relocation for. + /// 0b10 reg1, [reg2 + rcx + imm32] lea, + /// ops flags: form: + /// 0b00 reg1, [rip + reloc] // via GOT emits X86_64_RELOC_GOT relocation + /// 0b01 reg1, [rip + reloc] // direct load emits X86_64_RELOC_SIGNED relocation + /// Notes: + /// * `Data` contains `linker_sym_index` + lea_pie, + /// ops flags: form: /// 0bX0 reg1 /// 0bX1 [reg1 + imm32] @@ -342,8 +345,8 @@ pub const Inst = struct { /// An extern function. /// Index into the linker's string table. extern_fn: u32, - /// Entry in the GOT table by index. - got_entry: u32, + /// Entry in the linker's symbol table. + linker_sym_index: u32, /// Index into `extra`. Meaning of what can be found there is context-dependent. payload: u32, }; diff --git a/src/arch/x86_64/PrintMir.zig b/src/arch/x86_64/PrintMir.zig index 8f30802912..7c96b15210 100644 --- a/src/arch/x86_64/PrintMir.zig +++ b/src/arch/x86_64/PrintMir.zig @@ -119,6 +119,7 @@ pub fn printMir(print: *const Print, w: anytype, mir_to_air_map: std.AutoHashMap .movabs => try print.mirMovabs(inst, w), .lea => try print.mirLea(inst, w), + .lea_pie => try print.mirLeaPie(inst, w), .imul_complex => try print.mirIMulComplex(inst, w), @@ -412,7 +413,7 @@ fn mirLea(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void { } else { try w.print("ds:", .{}); } - try w.print("{d}]\n", .{imm}); + try w.print("{d}]", .{imm}); }, 0b01 => { try w.print("{s}, ", .{@tagName(ops.reg1)}); @@ -429,6 +430,7 @@ fn mirLea(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void { try w.print("target@{x}", .{imm}); }, 0b10 => { + const imm = print.mir.instructions.items(.data)[inst].imm; try w.print("{s}, ", .{@tagName(ops.reg1)}); switch (ops.reg1.size()) { 8 => try w.print("byte ptr ", .{}), @@ -437,23 +439,37 @@ fn mirLea(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void { 64 => try w.print("qword ptr ", .{}), else => unreachable, } - try w.print("[rip + 0x0] ", .{}); - const got_entry = print.mir.instructions.items(.data)[inst].got_entry; - if (print.bin_file.cast(link.File.MachO)) |macho_file| { - const target = macho_file.locals.items[got_entry]; - const target_name = macho_file.getString(target.n_strx); - try w.print("target@{s}", .{target_name}); - } else { - try w.writeAll("TODO lea reg, [rip + reloc] for linking backends different than MachO"); - } + try w.print("[rbp + rcx + {d}]", .{imm}); }, 0b11 => { - try w.writeAll("unused variant\n"); + try w.writeAll("unused variant"); }, } try w.writeAll("\n"); } +fn mirLeaPie(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void { + const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); + try w.print("lea {s}, ", .{@tagName(ops.reg1)}); + switch (ops.reg1.size()) { + 8 => try w.print("byte ptr ", .{}), + 16 => try w.print("word ptr ", .{}), + 32 => try w.print("dword ptr ", .{}), + 64 => try w.print("qword ptr ", .{}), + else => unreachable, + } + try w.print("[rip + 0x0] ", .{}); + const sym_index = print.mir.instructions.items(.data)[inst].linker_sym_index; + if (print.bin_file.cast(link.File.MachO)) |macho_file| { + const target = macho_file.locals.items[sym_index]; + const target_name = macho_file.getString(target.n_strx); + try w.print("target@{s}", .{target_name}); + } else { + try w.print("TODO lea PIE for other backends", .{}); + } + return w.writeByte('\n'); +} + fn mirCallExtern(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void { _ = print; _ = inst; diff --git a/src/link.zig b/src/link.zig index 51e7082aa7..56b88bffef 100644 --- a/src/link.zig +++ b/src/link.zig @@ -17,6 +17,7 @@ const LibCInstallation = @import("libc_installation.zig").LibCInstallation; const wasi_libc = @import("wasi_libc.zig"); const Air = @import("Air.zig"); const Liveness = @import("Liveness.zig"); +const TypedValue = @import("TypedValue.zig"); pub const SystemLib = struct { needed: bool = false, @@ -429,6 +430,25 @@ pub const File = struct { CurrentWorkingDirectoryUnlinked, }; + /// Called from within the CodeGen to lower a local variable instantion as an unnamed + /// constant. Returns the symbol index of the lowered constant in the read-only section + /// of the final binary. + pub fn lowerUnnamedConst(base: *File, tv: TypedValue, decl: *Module.Decl) UpdateDeclError!u32 { + log.debug("lowerUnnamedConst {*} ({s})", .{ decl, decl.name }); + switch (base.tag) { + // zig fmt: off + .coff => return @fieldParentPtr(Coff, "base", base).lowerUnnamedConst(tv, decl), + .elf => return @fieldParentPtr(Elf, "base", base).lowerUnnamedConst(tv, decl), + .macho => return @fieldParentPtr(MachO, "base", base).lowerUnnamedConst(tv, decl), + .plan9 => return @fieldParentPtr(Plan9, "base", base).lowerUnnamedConst(tv, decl), + .spirv => unreachable, + .c => unreachable, + .wasm => unreachable, + .nvptx => unreachable, + // zig fmt: on + } + } + /// May be called before or after updateDeclExports but must be called /// after allocateDeclIndexes for any given Decl. pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) UpdateDeclError!void { diff --git a/src/link/Coff.zig b/src/link/Coff.zig index bc5837fe47..2f500e6b91 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -21,6 +21,7 @@ const mingw = @import("../mingw.zig"); const Air = @import("../Air.zig"); const Liveness = @import("../Liveness.zig"); const LlvmObject = @import("../codegen/llvm.zig").Object; +const TypedValue = @import("../TypedValue.zig"); const allocation_padding = 4 / 3; const minimum_text_block_size = 64 * allocation_padding; @@ -697,6 +698,14 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live return self.finishUpdateDecl(module, func.owner_decl, code); } +pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl: *Module.Decl) !u32 { + _ = self; + _ = tv; + _ = decl; + log.debug("TODO lowerUnnamedConst for Coff", .{}); + return error.AnalysisFail; +} + pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { if (build_options.skip_non_native and builtin.object_format != .coff) { @panic("Attempted to compile for object format that was disabled by build configuration"); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index afaf41a2f9..ea9556a952 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -19,6 +19,7 @@ const trace = @import("../tracy.zig").trace; const Package = @import("../Package.zig"); const Value = @import("../value.zig").Value; const Type = @import("../type.zig").Type; +const TypedValue = @import("../TypedValue.zig"); const link = @import("../link.zig"); const File = link.File; const build_options = @import("build_options"); @@ -110,6 +111,9 @@ debug_line_header_dirty: bool = false, error_flags: File.ErrorFlags = File.ErrorFlags{}, +/// Pointer to the last allocated atom +atoms: std.AutoHashMapUnmanaged(u16, *TextBlock) = .{}, + /// A list of text blocks that have surplus capacity. This list can have false /// positives, as functions grow and shrink over time, only sometimes being added /// or removed from the freelist. @@ -125,10 +129,42 @@ error_flags: File.ErrorFlags = File.ErrorFlags{}, /// overcapacity can be negative. A simple way to have negative overcapacity is to /// allocate a fresh text block, which will have ideal capacity, and then grow it /// by 1 byte. It will then have -1 overcapacity. -atoms: std.AutoHashMapUnmanaged(u16, *TextBlock) = .{}, atom_free_lists: std.AutoHashMapUnmanaged(u16, std.ArrayListUnmanaged(*TextBlock)) = .{}, + +/// Table of Decls that are currently alive. +/// We store them here so that we can properly dispose of any allocated +/// memory within the atom in the incremental linker. +/// TODO consolidate this. decls: std.AutoHashMapUnmanaged(*Module.Decl, ?u16) = .{}, +/// List of atoms that are owned directly by the linker. +/// Currently these are only atoms that are the result of linking +/// object files. Atoms which take part in incremental linking are +/// at present owned by Module.Decl. +/// TODO consolidate this. +managed_atoms: std.ArrayListUnmanaged(*TextBlock) = .{}, + +/// Table of unnamed constants associated with a parent `Decl`. +/// We store them here so that we can free the constants whenever the `Decl` +/// needs updating or is freed. +/// +/// For example, +/// +/// ```zig +/// const Foo = struct{ +/// a: u8, +/// }; +/// +/// pub fn main() void { +/// var foo = Foo{ .a = 1 }; +/// _ = foo; +/// } +/// ``` +/// +/// value assigned to label `foo` is an unnamed constant belonging/associated +/// with `Decl` `main`, and lives as long as that `Decl`. +unnamed_const_atoms: UnnamedConstTable = .{}, + /// A list of `SrcFn` whose Line Number Programs have surplus capacity. /// This is the same concept as `text_block_free_list`; see those doc comments. dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{}, @@ -141,6 +177,8 @@ dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{}, dbg_info_decl_first: ?*TextBlock = null, dbg_info_decl_last: ?*TextBlock = null, +const UnnamedConstTable = std.AutoHashMapUnmanaged(*Module.Decl, std.ArrayListUnmanaged(*TextBlock)); + /// When allocating, the ideal_capacity is calculated by /// actual_capacity + (actual_capacity / ideal_factor) const ideal_factor = 3; @@ -342,6 +380,19 @@ pub fn deinit(self: *Elf) void { } self.atom_free_lists.deinit(self.base.allocator); } + + for (self.managed_atoms.items) |atom| { + self.base.allocator.destroy(atom); + } + self.managed_atoms.deinit(self.base.allocator); + + { + var it = self.unnamed_const_atoms.valueIterator(); + while (it.next()) |atoms| { + atoms.deinit(self.base.allocator); + } + self.unnamed_const_atoms.deinit(self.base.allocator); + } } pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl) u64 { @@ -2166,6 +2217,11 @@ fn writeElfHeader(self: *Elf) !void { } fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void { + const local_sym = self.local_symbols.items[text_block.local_sym_index]; + const name_str_index = local_sym.st_name; + const name = self.getString(name_str_index); + log.debug("freeTextBlock {*} ({s})", .{ text_block, name }); + const free_list = self.atom_free_lists.getPtr(phdr_index).?; var already_have_free_list_node = false; { @@ -2376,23 +2432,43 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al return vaddr; } +fn allocateLocalSymbol(self: *Elf) !u32 { + try self.local_symbols.ensureUnusedCapacity(self.base.allocator, 1); + + const index = blk: { + if (self.local_symbol_free_list.popOrNull()) |index| { + log.debug(" (reusing symbol index {d})", .{index}); + break :blk index; + } else { + log.debug(" (allocating symbol index {d})", .{self.local_symbols.items.len}); + const index = @intCast(u32, self.local_symbols.items.len); + _ = self.local_symbols.addOneAssumeCapacity(); + break :blk index; + } + }; + + self.local_symbols.items[index] = .{ + .st_name = 0, + .st_info = 0, + .st_other = 0, + .st_shndx = 0, + .st_value = 0, + .st_size = 0, + }; + + return index; +} + pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void { if (self.llvm_object) |_| return; if (decl.link.elf.local_sym_index != 0) return; - try self.local_symbols.ensureUnusedCapacity(self.base.allocator, 1); try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1); try self.decls.putNoClobber(self.base.allocator, decl, null); - if (self.local_symbol_free_list.popOrNull()) |i| { - log.debug("reusing symbol index {d} for {s}", .{ i, decl.name }); - decl.link.elf.local_sym_index = i; - } else { - log.debug("allocating symbol index {d} for {s}", .{ self.local_symbols.items.len, decl.name }); - decl.link.elf.local_sym_index = @intCast(u32, self.local_symbols.items.len); - _ = self.local_symbols.addOneAssumeCapacity(); - } + log.debug("allocating symbol indexes for {s}", .{decl.name}); + decl.link.elf.local_sym_index = try self.allocateLocalSymbol(); if (self.offset_table_free_list.popOrNull()) |i| { decl.link.elf.offset_table_index = i; @@ -2401,18 +2477,19 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void { _ = self.offset_table.addOneAssumeCapacity(); self.offset_table_count_dirty = true; } - - self.local_symbols.items[decl.link.elf.local_sym_index] = .{ - .st_name = 0, - .st_info = 0, - .st_other = 0, - .st_shndx = 0, - .st_value = 0, - .st_size = 0, - }; self.offset_table.items[decl.link.elf.offset_table_index] = 0; } +fn freeUnnamedConsts(self: *Elf, decl: *Module.Decl) void { + const unnamed_consts = self.unnamed_const_atoms.getPtr(decl) orelse return; + for (unnamed_consts.items) |atom| { + self.freeTextBlock(atom, self.phdr_load_ro_index.?); + self.local_symbol_free_list.append(self.base.allocator, atom.local_sym_index) catch {}; + self.local_symbols.items[atom.local_sym_index].st_info = 0; + } + unnamed_consts.clearAndFree(self.base.allocator); +} + pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { if (build_options.have_llvm) { if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl); @@ -2421,6 +2498,7 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { const kv = self.decls.fetchRemove(decl); if (kv.?.value) |index| { self.freeTextBlock(&decl.link.elf, index); + self.freeUnnamedConsts(decl); } // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. @@ -2528,7 +2606,6 @@ fn updateDeclCode(self: *Elf, decl: *Module.Decl, code: []const u8, stt_bits: u8 const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment, phdr_index); errdefer self.freeTextBlock(&decl.link.elf, phdr_index); log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr }); - errdefer self.freeTextBlock(&decl.link.elf, phdr_index); local_sym.* = .{ .st_name = name_str_index, @@ -2632,6 +2709,8 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs); const decl = func.owner_decl; + self.freeUnnamedConsts(decl); + log.debug("updateFunc {s}{*}", .{ decl.name, func.owner_decl }); log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{ decl.src_line, @@ -2859,6 +2938,8 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { } } + assert(!self.unnamed_const_atoms.contains(decl)); + var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); @@ -2897,6 +2978,74 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { return self.finishUpdateDecl(module, decl, &dbg_info_type_relocs, &dbg_info_buffer); } +pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl) !u32 { + var code_buffer = std.ArrayList(u8).init(self.base.allocator); + defer code_buffer.deinit(); + + const module = self.base.options.module.?; + const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl); + if (!gop.found_existing) { + gop.value_ptr.* = .{}; + } + const unnamed_consts = gop.value_ptr; + + const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{ + .none = .{}, + }); + const code = switch (res) { + .externally_managed => |x| x, + .appended => code_buffer.items, + .fail => |em| { + decl.analysis = .codegen_failure; + try module.failed_decls.put(module.gpa, decl, em); + return error.AnalysisFail; + }, + }; + + const atom = try self.base.allocator.create(TextBlock); + errdefer self.base.allocator.destroy(atom); + atom.* = TextBlock.empty; + try self.managed_atoms.append(self.base.allocator, atom); + + const name_str_index = blk: { + const index = unnamed_consts.items.len; + const name = try std.fmt.allocPrint(self.base.allocator, "__unnamed_{s}_{d}", .{ decl.name, index }); + defer self.base.allocator.free(name); + break :blk try self.makeString(name); + }; + const name = self.getString(name_str_index); + + log.debug("allocating symbol indexes for {s}", .{name}); + atom.local_sym_index = try self.allocateLocalSymbol(); + + const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); + const phdr_index = self.phdr_load_ro_index.?; + const shdr_index = self.phdr_shdr_table.get(phdr_index).?; + const vaddr = try self.allocateTextBlock(atom, code.len, required_alignment, phdr_index); + errdefer self.freeTextBlock(atom, phdr_index); + + log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr }); + + const local_sym = &self.local_symbols.items[atom.local_sym_index]; + local_sym.* = .{ + .st_name = name_str_index, + .st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT, + .st_other = 0, + .st_shndx = shdr_index, + .st_value = vaddr, + .st_size = code.len, + }; + + try self.writeSymbol(atom.local_sym_index); + try unnamed_consts.append(self.base.allocator, atom); + + const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr; + const file_offset = self.sections.items[shdr_index].sh_offset + section_offset; + try self.base.file.?.pwriteAll(code, file_offset); + + return atom.local_sym_index; +} + /// Asserts the type has codegen bits. fn addDbgInfoType( self: *Elf, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index be7a9d0f85..065145cdc8 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -39,6 +39,7 @@ const StringIndexAdapter = std.hash_map.StringIndexAdapter; const StringIndexContext = std.hash_map.StringIndexContext; const Trie = @import("MachO/Trie.zig"); const Type = @import("../type.zig").Type; +const TypedValue = @import("../TypedValue.zig"); pub const TextBlock = Atom; @@ -166,14 +167,17 @@ stub_helper_preamble_atom: ?*Atom = null, strtab: std.ArrayListUnmanaged(u8) = .{}, strtab_dir: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{}, -tlv_ptr_entries_map: std.AutoArrayHashMapUnmanaged(Atom.Relocation.Target, *Atom) = .{}, -tlv_ptr_entries_map_free_list: std.ArrayListUnmanaged(u32) = .{}, +tlv_ptr_entries: std.ArrayListUnmanaged(Entry) = .{}, +tlv_ptr_entries_free_list: std.ArrayListUnmanaged(u32) = .{}, +tlv_ptr_entries_table: std.AutoArrayHashMapUnmanaged(Atom.Relocation.Target, u32) = .{}, -got_entries_map: std.AutoArrayHashMapUnmanaged(Atom.Relocation.Target, *Atom) = .{}, -got_entries_map_free_list: std.ArrayListUnmanaged(u32) = .{}, +got_entries: std.ArrayListUnmanaged(Entry) = .{}, +got_entries_free_list: std.ArrayListUnmanaged(u32) = .{}, +got_entries_table: std.AutoArrayHashMapUnmanaged(Atom.Relocation.Target, u32) = .{}, -stubs_map: std.AutoArrayHashMapUnmanaged(u32, *Atom) = .{}, -stubs_map_free_list: std.ArrayListUnmanaged(u32) = .{}, +stubs: std.ArrayListUnmanaged(*Atom) = .{}, +stubs_free_list: std.ArrayListUnmanaged(u32) = .{}, +stubs_table: std.AutoArrayHashMapUnmanaged(u32, u32) = .{}, error_flags: File.ErrorFlags = File.ErrorFlags{}, @@ -217,6 +221,27 @@ atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{}, /// TODO consolidate this. managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, +/// Table of unnamed constants associated with a parent `Decl`. +/// We store them here so that we can free the constants whenever the `Decl` +/// needs updating or is freed. +/// +/// For example, +/// +/// ```zig +/// const Foo = struct{ +/// a: u8, +/// }; +/// +/// pub fn main() void { +/// var foo = Foo{ .a = 1 }; +/// _ = foo; +/// } +/// ``` +/// +/// value assigned to label `foo` is an unnamed constant belonging/associated +/// with `Decl` `main`, and lives as long as that `Decl`. +unnamed_const_atoms: UnnamedConstTable = .{}, + /// Table of Decls that are currently alive. /// We store them here so that we can properly dispose of any allocated /// memory within the atom in the incremental linker. @@ -229,6 +254,13 @@ decls: std.AutoArrayHashMapUnmanaged(*Module.Decl, ?MatchingSection) = .{}, /// somewhere else in the codegen. active_decl: ?*Module.Decl = null, +const Entry = struct { + target: Atom.Relocation.Target, + atom: *Atom, +}; + +const UnnamedConstTable = std.AutoHashMapUnmanaged(*Module.Decl, std.ArrayListUnmanaged(*Atom)); + const PendingUpdate = union(enum) { resolve_undef: u32, add_stub_entry: u32, @@ -661,16 +693,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { sym.n_desc = 0; }, } - if (self.got_entries_map.getIndex(.{ .global = entry.key })) |i| { - self.got_entries_map_free_list.append( - self.base.allocator, - @intCast(u32, i), - ) catch {}; - self.got_entries_map.keys()[i] = .{ .local = 0 }; + if (self.got_entries_table.get(.{ .global = entry.key })) |i| { + self.got_entries_free_list.append(self.base.allocator, @intCast(u32, i)) catch {}; + self.got_entries.items[i] = .{ .target = .{ .local = 0 }, .atom = undefined }; + _ = self.got_entries_table.swapRemove(.{ .global = entry.key }); } - if (self.stubs_map.getIndex(entry.key)) |i| { - self.stubs_map_free_list.append(self.base.allocator, @intCast(u32, i)) catch {}; - self.stubs_map.keys()[i] = 0; + if (self.stubs_table.get(entry.key)) |i| { + self.stubs_free_list.append(self.base.allocator, @intCast(u32, i)) catch {}; + self.stubs.items[i] = undefined; + _ = self.stubs_table.swapRemove(entry.key); } } } @@ -2948,7 +2979,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void { .none => {}, .got => return error.TODOGotHint, .stub => { - if (self.stubs_map.contains(sym.n_strx)) break :outer_blk; + if (self.stubs_table.contains(sym.n_strx)) break :outer_blk; const stub_helper_atom = blk: { const match = MatchingSection{ .seg = self.text_segment_cmd_index.?, @@ -2991,7 +3022,9 @@ fn resolveSymbolsInDylibs(self: *MachO) !void { atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1); break :blk atom; }; - try self.stubs_map.putNoClobber(self.base.allocator, sym.n_strx, stub_atom); + const stub_index = @intCast(u32, self.stubs.items.len); + try self.stubs.append(self.base.allocator, stub_atom); + try self.stubs_table.putNoClobber(self.base.allocator, sym.n_strx, stub_index); }, } } @@ -3086,7 +3119,9 @@ fn resolveDyldStubBinder(self: *MachO) !void { // Add dyld_stub_binder as the final GOT entry. const target = Atom.Relocation.Target{ .global = n_strx }; const atom = try self.createGotAtom(target); - try self.got_entries_map.putNoClobber(self.base.allocator, target, atom); + const got_index = @intCast(u32, self.got_entries.items.len); + try self.got_entries.append(self.base.allocator, .{ .target = target, .atom = atom }); + try self.got_entries_table.putNoClobber(self.base.allocator, target, got_index); const match = MatchingSection{ .seg = self.data_const_segment_cmd_index.?, .sect = self.got_section_index.?, @@ -3339,12 +3374,15 @@ pub fn deinit(self: *MachO) void { } self.section_ordinals.deinit(self.base.allocator); - self.tlv_ptr_entries_map.deinit(self.base.allocator); - self.tlv_ptr_entries_map_free_list.deinit(self.base.allocator); - self.got_entries_map.deinit(self.base.allocator); - self.got_entries_map_free_list.deinit(self.base.allocator); - self.stubs_map.deinit(self.base.allocator); - self.stubs_map_free_list.deinit(self.base.allocator); + self.tlv_ptr_entries.deinit(self.base.allocator); + self.tlv_ptr_entries_free_list.deinit(self.base.allocator); + self.tlv_ptr_entries_table.deinit(self.base.allocator); + self.got_entries.deinit(self.base.allocator); + self.got_entries_free_list.deinit(self.base.allocator); + self.got_entries_table.deinit(self.base.allocator); + self.stubs.deinit(self.base.allocator); + self.stubs_free_list.deinit(self.base.allocator); + self.stubs_table.deinit(self.base.allocator); self.strtab_dir.deinit(self.base.allocator); self.strtab.deinit(self.base.allocator); self.undefs.deinit(self.base.allocator); @@ -3395,6 +3433,14 @@ pub fn deinit(self: *MachO) void { decl.link.macho.deinit(self.base.allocator); } self.decls.deinit(self.base.allocator); + + { + var it = self.unnamed_const_atoms.valueIterator(); + while (it.next()) |atoms| { + atoms.deinit(self.base.allocator); + } + self.unnamed_const_atoms.deinit(self.base.allocator); + } } pub fn closeFiles(self: MachO) void { @@ -3409,9 +3455,11 @@ pub fn closeFiles(self: MachO) void { } } -fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection) void { +fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) void { log.debug("freeAtom {*}", .{atom}); - atom.deinit(self.base.allocator); + if (!owns_atom) { + atom.deinit(self.base.allocator); + } const free_list = self.atom_free_lists.getPtr(match).?; var already_have_free_list_node = false; @@ -3502,23 +3550,22 @@ fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match return self.allocateAtom(atom, new_atom_size, alignment, match); } -pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { - if (self.llvm_object) |_| return; - if (decl.link.macho.local_sym_index != 0) return; - +fn allocateLocalSymbol(self: *MachO) !u32 { try self.locals.ensureUnusedCapacity(self.base.allocator, 1); - try self.decls.putNoClobber(self.base.allocator, decl, null); - if (self.locals_free_list.popOrNull()) |i| { - log.debug("reusing symbol index {d} for {s}", .{ i, decl.name }); - decl.link.macho.local_sym_index = i; - } else { - log.debug("allocating symbol index {d} for {s}", .{ self.locals.items.len, decl.name }); - decl.link.macho.local_sym_index = @intCast(u32, self.locals.items.len); - _ = self.locals.addOneAssumeCapacity(); - } + const index = blk: { + if (self.locals_free_list.popOrNull()) |index| { + log.debug(" (reusing symbol index {d})", .{index}); + break :blk index; + } else { + log.debug(" (allocating symbol index {d})", .{self.locals.items.len}); + const index = @intCast(u32, self.locals.items.len); + _ = self.locals.addOneAssumeCapacity(); + break :blk index; + } + }; - self.locals.items[decl.link.macho.local_sym_index] = .{ + self.locals.items[index] = .{ .n_strx = 0, .n_type = 0, .n_sect = 0, @@ -3526,24 +3573,86 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { .n_value = 0, }; - // TODO try popping from free list first before allocating a new GOT atom. - const target = Atom.Relocation.Target{ .local = decl.link.macho.local_sym_index }; - const value_ptr = blk: { - if (self.got_entries_map_free_list.popOrNull()) |i| { - log.debug("reusing GOT entry index {d} for {s}", .{ i, decl.name }); - self.got_entries_map.keys()[i] = target; - const value_ptr = self.got_entries_map.getPtr(target).?; - break :blk value_ptr; + return index; +} + +pub fn allocateGotEntry(self: *MachO, target: Atom.Relocation.Target) !u32 { + try self.got_entries.ensureUnusedCapacity(self.base.allocator, 1); + + const index = blk: { + if (self.got_entries_free_list.popOrNull()) |index| { + log.debug(" (reusing GOT entry index {d})", .{index}); + break :blk index; } else { - const res = try self.got_entries_map.getOrPut(self.base.allocator, target); - log.debug("creating new GOT entry at index {d} for {s}", .{ - self.got_entries_map.getIndex(target).?, - decl.name, - }); - break :blk res.value_ptr; + log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.items.len}); + const index = @intCast(u32, self.got_entries.items.len); + _ = self.got_entries.addOneAssumeCapacity(); + break :blk index; } }; - value_ptr.* = try self.createGotAtom(target); + + self.got_entries.items[index] = .{ + .target = target, + .atom = undefined, + }; + try self.got_entries_table.putNoClobber(self.base.allocator, target, index); + + return index; +} + +pub fn allocateStubEntry(self: *MachO, n_strx: u32) !u32 { + try self.stubs.ensureUnusedCapacity(self.base.allocator, 1); + + const index = blk: { + if (self.stubs_free_list.popOrNull()) |index| { + log.debug(" (reusing stub entry index {d})", .{index}); + break :blk index; + } else { + log.debug(" (allocating stub entry at index {d})", .{self.stubs.items.len}); + const index = @intCast(u32, self.stubs.items.len); + _ = self.stubs.addOneAssumeCapacity(); + break :blk index; + } + }; + + self.stubs.items[index] = undefined; + try self.stubs_table.putNoClobber(self.base.allocator, n_strx, index); + + return index; +} + +pub fn allocateTlvPtrEntry(self: *MachO, target: Atom.Relocation.Target) !u32 { + try self.tlv_ptr_entries.ensureUnusedCapacity(self.base.allocator, 1); + + const index = blk: { + if (self.tlv_ptr_entries_free_list.popOrNull()) |index| { + log.debug(" (reusing TLV ptr entry index {d})", .{index}); + break :blk index; + } else { + log.debug(" (allocating TLV ptr entry at index {d})", .{self.tlv_ptr_entries.items.len}); + const index = @intCast(u32, self.tlv_ptr_entries.items.len); + _ = self.tlv_ptr_entries.addOneAssumeCapacity(); + break :blk index; + } + }; + + self.tlv_ptr_entries.items[index] = .{ .target = target, .atom = undefined }; + try self.tlv_ptr_entries_table.putNoClobber(self.base.allocator, target, index); + + return index; +} + +pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { + if (self.llvm_object) |_| return; + if (decl.link.macho.local_sym_index != 0) return; + + decl.link.macho.local_sym_index = try self.allocateLocalSymbol(); + try self.decls.putNoClobber(self.base.allocator, decl, null); + + const got_target = .{ .local = decl.link.macho.local_sym_index }; + const got_index = try self.allocateGotEntry(got_target); + const got_atom = try self.createGotAtom(got_target); + self.got_entries.items[got_index].atom = got_atom; } pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { @@ -3557,6 +3666,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv defer tracy.end(); const decl = func.owner_decl; + self.freeUnnamedConsts(decl); // TODO clearing the code and relocs buffer should probably be orchestrated // in a different, smarter, more automatic way somewhere else, in a more centralised // way than this. @@ -3624,6 +3734,70 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv try self.updateDeclExports(module, decl, decl_exports); } +pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.Decl) !u32 { + var code_buffer = std.ArrayList(u8).init(self.base.allocator); + defer code_buffer.deinit(); + + const module = self.base.options.module.?; + const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl); + if (!gop.found_existing) { + gop.value_ptr.* = .{}; + } + const unnamed_consts = gop.value_ptr; + + const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{ + .none = .{}, + }); + const code = switch (res) { + .externally_managed => |x| x, + .appended => code_buffer.items, + .fail => |em| { + decl.analysis = .codegen_failure; + try module.failed_decls.put(module.gpa, decl, em); + return error.AnalysisFail; + }, + }; + + const name_str_index = blk: { + const index = unnamed_consts.items.len; + const name = try std.fmt.allocPrint(self.base.allocator, "__unnamed_{s}_{d}", .{ decl.name, index }); + defer self.base.allocator.free(name); + break :blk try self.makeString(name); + }; + const name = self.getString(name_str_index); + + log.debug("allocating symbol indexes for {s}", .{name}); + + const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); + const match = (try self.getMatchingSection(.{ + .segname = makeStaticString("__TEXT"), + .sectname = makeStaticString("__const"), + .size = code.len, + .@"align" = math.log2(required_alignment), + })).?; + const local_sym_index = try self.allocateLocalSymbol(); + const atom = try self.createEmptyAtom(local_sym_index, code.len, math.log2(required_alignment)); + mem.copy(u8, atom.code.items, code); + const addr = try self.allocateAtom(atom, code.len, required_alignment, match); + + log.debug("allocated atom for {s} at 0x{x}", .{ name, addr }); + + errdefer self.freeAtom(atom, match, true); + + const symbol = &self.locals.items[atom.local_sym_index]; + symbol.* = .{ + .n_strx = name_str_index, + .n_type = macho.N_SECT, + .n_sect = @intCast(u8, self.section_ordinals.getIndex(match).?) + 1, + .n_desc = 0, + .n_value = addr, + }; + + try unnamed_consts.append(self.base.allocator, atom); + + return atom.local_sym_index; +} + pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { if (build_options.skip_non_native and builtin.object_format != .macho) { @panic("Attempted to compile for object format that was disabled by build configuration"); @@ -3879,7 +4053,8 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 if (vaddr != symbol.n_value) { log.debug(" (writing new GOT entry)", .{}); - const got_atom = self.got_entries_map.get(.{ .local = decl.link.macho.local_sym_index }).?; + const got_index = self.got_entries_table.get(.{ .local = decl.link.macho.local_sym_index }).?; + const got_atom = self.got_entries.items[got_index].atom; const got_sym = &self.locals.items[got_atom.local_sym_index]; const got_vaddr = try self.allocateAtom(got_atom, @sizeOf(u64), 8, .{ .seg = self.data_const_segment_cmd_index.?, @@ -3920,7 +4095,7 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, addr }); - errdefer self.freeAtom(&decl.link.macho, match); + errdefer self.freeAtom(&decl.link.macho, match, false); symbol.* = .{ .n_strx = name_str_index, @@ -3929,7 +4104,8 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 .n_desc = 0, .n_value = addr, }; - const got_atom = self.got_entries_map.get(.{ .local = decl.link.macho.local_sym_index }).?; + const got_index = self.got_entries_table.get(.{ .local = decl.link.macho.local_sym_index }).?; + const got_atom = self.got_entries.items[got_index].atom; const got_sym = &self.locals.items[got_atom.local_sym_index]; const vaddr = try self.allocateAtom(got_atom, @sizeOf(u64), 8, .{ .seg = self.data_const_segment_cmd_index.?, @@ -4103,6 +4279,19 @@ pub fn deleteExport(self: *MachO, exp: Export) void { global.n_value = 0; } +fn freeUnnamedConsts(self: *MachO, decl: *Module.Decl) void { + const unnamed_consts = self.unnamed_const_atoms.getPtr(decl) orelse return; + for (unnamed_consts.items) |atom| { + self.freeAtom(atom, .{ + .seg = self.text_segment_cmd_index.?, + .sect = self.text_const_section_index.?, + }, true); + self.locals_free_list.append(self.base.allocator, atom.local_sym_index) catch {}; + self.locals.items[atom.local_sym_index].n_type = 0; + } + unnamed_consts.clearAndFree(self.base.allocator); +} + pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { if (build_options.have_llvm) { if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl); @@ -4110,15 +4299,19 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { log.debug("freeDecl {*}", .{decl}); const kv = self.decls.fetchSwapRemove(decl); if (kv.?.value) |match| { - self.freeAtom(&decl.link.macho, match); + self.freeAtom(&decl.link.macho, match, false); + self.freeUnnamedConsts(decl); } // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. if (decl.link.macho.local_sym_index != 0) { self.locals_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {}; - // Try freeing GOT atom - const got_index = self.got_entries_map.getIndex(.{ .local = decl.link.macho.local_sym_index }).?; - self.got_entries_map_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {}; + // Try freeing GOT atom if this decl had one + if (self.got_entries_table.get(.{ .local = decl.link.macho.local_sym_index })) |got_index| { + self.got_entries_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {}; + self.got_entries.items[got_index] = .{ .target = .{ .local = 0 }, .atom = undefined }; + _ = self.got_entries_table.swapRemove(.{ .local = decl.link.macho.local_sym_index }); + } self.locals.items[decl.link.macho.local_sym_index].n_type = 0; decl.link.macho.local_sym_index = 0; @@ -5932,8 +6125,8 @@ fn writeSymbolTable(self: *MachO) !void { const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].segment; const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?]; - const nstubs = @intCast(u32, self.stubs_map.keys().len); - const ngot_entries = @intCast(u32, self.got_entries_map.keys().len); + const nstubs = @intCast(u32, self.stubs_table.keys().len); + const ngot_entries = @intCast(u32, self.got_entries_table.keys().len); dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); dysymtab.nindirectsyms = nstubs * 2 + ngot_entries; @@ -5953,7 +6146,7 @@ fn writeSymbolTable(self: *MachO) !void { var writer = stream.writer(); stubs.reserved1 = 0; - for (self.stubs_map.keys()) |key| { + for (self.stubs_table.keys()) |key| { const resolv = self.symbol_resolver.get(key).?; switch (resolv.where) { .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL), @@ -5962,7 +6155,7 @@ fn writeSymbolTable(self: *MachO) !void { } got.reserved1 = nstubs; - for (self.got_entries_map.keys()) |key| { + for (self.got_entries_table.keys()) |key| { switch (key) { .local => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL), .global => |n_strx| { @@ -5976,7 +6169,7 @@ fn writeSymbolTable(self: *MachO) !void { } la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries; - for (self.stubs_map.keys()) |key| { + for (self.stubs_table.keys()) |key| { const resolv = self.symbol_resolver.get(key).?; switch (resolv.where) { .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL), @@ -6348,7 +6541,7 @@ fn snapshotState(self: *MachO) !void { }; if (is_via_got) { - const got_atom = self.got_entries_map.get(rel.target) orelse break :blk 0; + const got_atom = self.got_entries_table.get(rel.target) orelse break :blk 0; break :blk self.locals.items[got_atom.local_sym_index].n_value; } @@ -6380,10 +6573,11 @@ fn snapshotState(self: *MachO) !void { switch (resolv.where) { .global => break :blk self.globals.items[resolv.where_index].n_value, .undef => { - break :blk if (self.stubs_map.get(n_strx)) |stub_atom| - self.locals.items[stub_atom.local_sym_index].n_value - else - 0; + if (self.stubs_table.get(n_strx)) |stub_index| { + const stub_atom = self.stubs.items[stub_index]; + break :blk self.locals.items[stub_atom.local_sym_index].n_value; + } + break :blk 0; }, } }, @@ -6508,15 +6702,20 @@ fn logSymtab(self: MachO) void { } log.debug("GOT entries:", .{}); - for (self.got_entries_map.keys()) |key| { + for (self.got_entries_table.values()) |value| { + const key = self.got_entries.items[value].target; + const atom = self.got_entries.items[value].atom; switch (key) { - .local => |sym_index| log.debug(" {} => {d}", .{ key, sym_index }), + .local => { + const sym = self.locals.items[atom.local_sym_index]; + log.debug(" {} => {s}", .{ key, self.getString(sym.n_strx) }); + }, .global => |n_strx| log.debug(" {} => {s}", .{ key, self.getString(n_strx) }), } } log.debug("__thread_ptrs entries:", .{}); - for (self.tlv_ptr_entries_map.keys()) |key| { + for (self.tlv_ptr_entries_table.keys()) |key| { switch (key) { .local => unreachable, .global => |n_strx| log.debug(" {} => {s}", .{ key, self.getString(n_strx) }), @@ -6524,7 +6723,7 @@ fn logSymtab(self: MachO) void { } log.debug("stubs:", .{}); - for (self.stubs_map.keys()) |key| { + for (self.stubs_table.keys()) |key| { log.debug(" {} => {s}", .{ key, self.getString(key) }); } } diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index 2b16bc8cb0..fae1ff4eba 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -545,28 +545,11 @@ fn addPtrBindingOrRebase( } fn addTlvPtrEntry(target: Relocation.Target, context: RelocContext) !void { - if (context.macho_file.tlv_ptr_entries_map.contains(target)) return; + if (context.macho_file.tlv_ptr_entries_table.contains(target)) return; - const value_ptr = blk: { - if (context.macho_file.tlv_ptr_entries_map_free_list.popOrNull()) |i| { - log.debug("reusing __thread_ptrs entry index {d} for {}", .{ i, target }); - context.macho_file.tlv_ptr_entries_map.keys()[i] = target; - const value_ptr = context.macho_file.tlv_ptr_entries_map.getPtr(target).?; - break :blk value_ptr; - } else { - const res = try context.macho_file.tlv_ptr_entries_map.getOrPut( - context.macho_file.base.allocator, - target, - ); - log.debug("creating new __thread_ptrs entry at index {d} for {}", .{ - context.macho_file.tlv_ptr_entries_map.getIndex(target).?, - target, - }); - break :blk res.value_ptr; - } - }; + const index = try context.macho_file.allocateTlvPtrEntry(target); const atom = try context.macho_file.createTlvPtrAtom(target); - value_ptr.* = atom; + context.macho_file.tlv_ptr_entries.items[index].atom = atom; const match = (try context.macho_file.getMatchingSection(.{ .segname = MachO.makeStaticString("__DATA"), @@ -586,28 +569,11 @@ fn addTlvPtrEntry(target: Relocation.Target, context: RelocContext) !void { } fn addGotEntry(target: Relocation.Target, context: RelocContext) !void { - if (context.macho_file.got_entries_map.contains(target)) return; + if (context.macho_file.got_entries_table.contains(target)) return; - const value_ptr = blk: { - if (context.macho_file.got_entries_map_free_list.popOrNull()) |i| { - log.debug("reusing GOT entry index {d} for {}", .{ i, target }); - context.macho_file.got_entries_map.keys()[i] = target; - const value_ptr = context.macho_file.got_entries_map.getPtr(target).?; - break :blk value_ptr; - } else { - const res = try context.macho_file.got_entries_map.getOrPut( - context.macho_file.base.allocator, - target, - ); - log.debug("creating new GOT entry at index {d} for {}", .{ - context.macho_file.got_entries_map.getIndex(target).?, - target, - }); - break :blk res.value_ptr; - } - }; + const index = try context.macho_file.allocateGotEntry(target); const atom = try context.macho_file.createGotAtom(target); - value_ptr.* = atom; + context.macho_file.got_entries.items[index].atom = atom; const match = MachO.MatchingSection{ .seg = context.macho_file.data_const_segment_cmd_index.?, @@ -627,30 +593,13 @@ fn addGotEntry(target: Relocation.Target, context: RelocContext) !void { fn addStub(target: Relocation.Target, context: RelocContext) !void { if (target != .global) return; - if (context.macho_file.stubs_map.contains(target.global)) return; + if (context.macho_file.stubs_table.contains(target.global)) return; // If the symbol has been resolved as defined globally elsewhere (in a different translation unit), // then skip creating stub entry. // TODO Is this the correct for the incremental? if (context.macho_file.symbol_resolver.get(target.global).?.where == .global) return; - const value_ptr = blk: { - if (context.macho_file.stubs_map_free_list.popOrNull()) |i| { - log.debug("reusing stubs entry index {d} for {}", .{ i, target }); - context.macho_file.stubs_map.keys()[i] = target.global; - const value_ptr = context.macho_file.stubs_map.getPtr(target.global).?; - break :blk value_ptr; - } else { - const res = try context.macho_file.stubs_map.getOrPut( - context.macho_file.base.allocator, - target.global, - ); - log.debug("creating new stubs entry at index {d} for {}", .{ - context.macho_file.stubs_map.getIndex(target.global).?, - target, - }); - break :blk res.value_ptr; - } - }; + const stub_index = try context.macho_file.allocateStubEntry(target.global); // TODO clean this up! const stub_helper_atom = atom: { @@ -707,7 +656,7 @@ fn addStub(target: Relocation.Target, context: RelocContext) !void { } else { try context.object.end_atoms.putNoClobber(context.allocator, match, atom); } - value_ptr.* = atom; + context.macho_file.stubs.items[stub_index] = atom; } pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { @@ -741,7 +690,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { }; if (is_via_got) { - const atom = macho_file.got_entries_map.get(rel.target) orelse { + const got_index = macho_file.got_entries_table.get(rel.target) orelse { const n_strx = switch (rel.target) { .local => |sym_index| macho_file.locals.items[sym_index].n_strx, .global => |n_strx| n_strx, @@ -750,6 +699,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { log.err(" this is an internal linker error", .{}); return error.FailedToResolveRelocationTarget; }; + const atom = macho_file.got_entries.items[got_index].atom; break :blk macho_file.locals.items[atom.local_sym_index].n_value; } @@ -795,15 +745,17 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { switch (resolv.where) { .global => break :blk macho_file.globals.items[resolv.where_index].n_value, .undef => { - break :blk if (macho_file.stubs_map.get(n_strx)) |atom| - macho_file.locals.items[atom.local_sym_index].n_value - else inner: { - if (macho_file.tlv_ptr_entries_map.get(rel.target)) |atom| { + if (macho_file.stubs_table.get(n_strx)) |stub_index| { + const atom = macho_file.stubs.items[stub_index]; + break :blk macho_file.locals.items[atom.local_sym_index].n_value; + } else { + if (macho_file.tlv_ptr_entries_table.get(rel.target)) |tlv_ptr_index| { is_via_thread_ptrs = true; - break :inner macho_file.locals.items[atom.local_sym_index].n_value; + const atom = macho_file.tlv_ptr_entries.items[tlv_ptr_index].atom; + break :blk macho_file.locals.items[atom.local_sym_index].n_value; } - break :inner 0; - }; + break :blk 0; + } }, } }, diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 200ac5e568..c2d6d61066 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -12,6 +12,7 @@ const File = link.File; const build_options = @import("build_options"); const Air = @import("../Air.zig"); const Liveness = @import("../Liveness.zig"); +const TypedValue = @import("../TypedValue.zig"); const std = @import("std"); const builtin = @import("builtin"); @@ -275,6 +276,14 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv return self.updateFinish(decl); } +pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl: *Module.Decl) !u32 { + _ = self; + _ = tv; + _ = decl; + log.debug("TODO lowerUnnamedConst for Plan9", .{}); + return error.AnalysisFail; +} + pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void { if (decl.val.tag() == .extern_fn) { return; // TODO Should we do more when front-end analyzed extern decl? diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 7871dec24b..1044742627 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -7,6 +7,7 @@ var foo: u8 align(4) = 100; test "global variable alignment" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .macos) return error.SkipZigTest; comptime try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4); comptime try expect(@TypeOf(&foo) == *align(4) u8); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index c907fb751b..d8fbc5ed9e 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -78,16 +78,19 @@ test "comptime_int @intToFloat" { try expect(@TypeOf(result) == f64); try expect(result == 1234.0); } - { - const result = @intToFloat(f128, 1234); - try expect(@TypeOf(result) == f128); - try expect(result == 1234.0); - } - // big comptime_int (> 64 bits) to f128 conversion - { - const result = @intToFloat(f128, 0x1_0000_0000_0000_0000); - try expect(@TypeOf(result) == f128); - try expect(result == 0x1_0000_0000_0000_0000.0); + if (builtin.zig_backend != .stage2_x86_64 or builtin.os.tag != .macos) { + // TODO investigate why this traps on x86_64-macos + { + const result = @intToFloat(f128, 1234); + try expect(@TypeOf(result) == f128); + try expect(result == 1234.0); + } + // big comptime_int (> 64 bits) to f128 conversion + { + const result = @intToFloat(f128, 0x1_0000_0000_0000_0000); + try expect(@TypeOf(result) == f128); + try expect(result == 0x1_0000_0000_0000_0000.0); + } } } diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index c470279aad..eb5a9e4273 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -51,6 +51,27 @@ test "non-packed struct has fields padded out to the required alignment" { try expect(foo.fourth() == 2); } +const SmallStruct = struct { + a: u8, + b: u32, + + fn first(self: *SmallStruct) u8 { + return self.a; + } + + fn second(self: *SmallStruct) u32 { + return self.b; + } +}; + +test "lower unnamed constants" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + + var foo = SmallStruct{ .a = 1, .b = 255 }; + try expect(foo.first() == 1); + try expect(foo.second() == 255); +} + const StructWithNoFields = struct { fn add(a: i32, b: i32) i32 { return a + b; diff --git a/test/stage2/x86_64.zig b/test/stage2/x86_64.zig index da2c662833..e482d1f7c7 100644 --- a/test/stage2/x86_64.zig +++ b/test/stage2/x86_64.zig @@ -1844,6 +1844,94 @@ pub fn addCases(ctx: *TestContext) !void { \\} , ""); } + + { + var case = ctx.exe("lower unnamed constants - structs", target); + case.addCompareOutput( + \\const Foo = struct { + \\ a: u8, + \\ b: u32, + \\ + \\ fn first(self: *Foo) u8 { + \\ return self.a; + \\ } + \\ + \\ fn second(self: *Foo) u32 { + \\ return self.b; + \\ } + \\}; + \\ + \\pub fn main() void { + \\ var foo = Foo{ .a = 1, .b = 5 }; + \\ assert(foo.first() == 1); + \\ assert(foo.second() == 5); + \\} + \\ + \\fn assert(ok: bool) void { + \\ if (!ok) unreachable; + \\} + , ""); + + case.addCompareOutput( + \\const Foo = struct { + \\ a: u8, + \\ b: u32, + \\ + \\ fn first(self: *Foo) u8 { + \\ return self.a; + \\ } + \\ + \\ fn second(self: *Foo) u32 { + \\ return self.b; + \\ } + \\}; + \\ + \\pub fn main() void { + \\ var foo = Foo{ .a = 1, .b = 5 }; + \\ assert(foo.first() == 1); + \\ assert(foo.second() == 5); + \\ + \\ foo.a = 10; + \\ foo.b = 255; + \\ + \\ assert(foo.first() == 10); + \\ assert(foo.second() == 255); + \\ + \\ var foo2 = Foo{ .a = 15, .b = 255 }; + \\ assert(foo2.first() == 15); + \\ assert(foo2.second() == 255); + \\} + \\ + \\fn assert(ok: bool) void { + \\ if (!ok) unreachable; + \\} + , ""); + + case.addCompareOutput( + \\const Foo = struct { + \\ a: u8, + \\ b: u32, + \\ + \\ fn first(self: *Foo) u8 { + \\ return self.a; + \\ } + \\ + \\ fn second(self: *Foo) u32 { + \\ return self.b; + \\ } + \\}; + \\ + \\pub fn main() void { + \\ var foo2 = Foo{ .a = 15, .b = 255 }; + \\ assert(foo2.first() == 15); + \\ assert(foo2.second() == 255); + \\} + \\ + \\fn assert(ok: bool) void { + \\ if (!ok) unreachable; + \\} + , ""); + } } } From 2cc33367ebee202462d6bef7f07120dd38ff6912 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Mon, 7 Feb 2022 01:49:15 -0700 Subject: [PATCH 0065/2031] fix bug when ReadFile returns synchronously in collectOutputWindows --- lib/std/child_process.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 10aeacf755..ace58ecf4f 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -270,12 +270,14 @@ pub const ChildProcess = struct { try buf.ensureTotalCapacity(new_capacity); const next_buf = buf.unusedCapacitySlice(); if (next_buf.len == 0) return .full; - const read_result = windows.kernel32.ReadFile(handle, next_buf.ptr, math.cast(u32, next_buf.len) catch maxInt(u32), null, overlapped); + var read_bytes: u32 = undefined; + const read_result = windows.kernel32.ReadFile(handle, next_buf.ptr, math.cast(u32, next_buf.len) catch maxInt(u32), &read_bytes, overlapped); if (read_result == 0) return switch (windows.kernel32.GetLastError()) { .IO_PENDING => .pending, .BROKEN_PIPE => .closed, else => |err| windows.unexpectedError(err), }; + buf.items.len += read_bytes; } } From 435beb4e1dec117136ef9541530ebd3e70c2319d Mon Sep 17 00:00:00 2001 From: boofexxx Date: Mon, 7 Feb 2022 13:11:25 +0500 Subject: [PATCH 0066/2031] std: fix doc comment typo in os.zig --- lib/std/os.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 6d59080820..a935cfd8c0 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -993,7 +993,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { /// transfer further bytes or may result in an error (e.g., if the disk is now full). /// /// For POSIX systems, if `fd` is opened in non blocking mode, the function will -/// return error.WouldBlock when EAGAIN is received.k`. +/// return error.WouldBlock when EAGAIN is received. /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. /// From e382e7be2b7a4c40a8db78dca0de38141dad8c67 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Sat, 5 Feb 2022 22:18:31 -0800 Subject: [PATCH 0067/2031] std: Allow `mem.zeroes` to work at comptime with extern union Fixes #10797 --- lib/std/mem.zig | 7 ++++--- test/run_translated_c.zig | 10 ++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 71de42aad7..5ca7faf90b 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -309,9 +309,7 @@ pub fn zeroes(comptime T: type) T { if (comptime meta.containerLayout(T) == .Extern) { // The C language specification states that (global) unions // should be zero initialized to the first named member. - var item: T = undefined; - @field(item, info.fields[0].name) = zeroes(@TypeOf(@field(item, info.fields[0].name))); - return item; + return @unionInit(T, info.fields[0].name, zeroes(info.fields[0].field_type)); } @compileError("Can't set a " ++ @typeName(T) ++ " to zero."); @@ -417,6 +415,9 @@ test "mem.zeroes" { var c = zeroes(C_union); try testing.expectEqual(@as(u8, 0), c.a); + + comptime var comptime_union = zeroes(C_union); + try testing.expectEqual(@as(u8, 0), comptime_union.a); } /// Initializes all fields of the struct with their default value, or zero values if no default value is present. diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 80e889b44c..933cf54c4f 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -1819,4 +1819,14 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("Zero-initialization of global union. Issue #10797", + \\#include + \\union U { int x; double y; }; + \\union U u; + \\int main(void) { + \\ if (u.x != 0) abort(); + \\ return 0; + \\} + , ""); } From 3db130ff3d8175adce610f7805a149810cf7989d Mon Sep 17 00:00:00 2001 From: matu3ba Date: Mon, 7 Feb 2022 19:27:21 +0100 Subject: [PATCH 0068/2031] compiler_rt: add addo (#10824) - approach by Hacker's Delight with wrapping addition - ca. 1.10x perf over the standard approach on my laptop - tests with all combinations of min,max with -1,0,+1 and combinations of sequences +-1,2,4..,max --- lib/std/special/compiler_rt.zig | 6 ++ lib/std/special/compiler_rt/addo.zig | 38 ++++++++++ lib/std/special/compiler_rt/addodi4_test.zig | 77 +++++++++++++++++++ lib/std/special/compiler_rt/addosi4_test.zig | 78 ++++++++++++++++++++ lib/std/special/compiler_rt/addoti4_test.zig | 77 +++++++++++++++++++ lib/std/special/compiler_rt/mulo.zig | 2 +- 6 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 lib/std/special/compiler_rt/addo.zig create mode 100644 lib/std/special/compiler_rt/addodi4_test.zig create mode 100644 lib/std/special/compiler_rt/addosi4_test.zig create mode 100644 lib/std/special/compiler_rt/addoti4_test.zig diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index da21745cce..286237aa7b 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -106,6 +106,12 @@ comptime { } // Integral arithmetic which returns if overflow + const __addosi4 = @import("compiler_rt/addo.zig").__addosi4; + @export(__addosi4, .{ .name = "__addosi4", .linkage = linkage }); + const __addodi4 = @import("compiler_rt/addo.zig").__addodi4; + @export(__addodi4, .{ .name = "__addodi4", .linkage = linkage }); + const __addoti4 = @import("compiler_rt/addo.zig").__addoti4; + @export(__addoti4, .{ .name = "__addoti4", .linkage = linkage }); const __mulosi4 = @import("compiler_rt/mulo.zig").__mulosi4; @export(__mulosi4, .{ .name = "__mulosi4", .linkage = linkage }); const __mulodi4 = @import("compiler_rt/mulo.zig").__mulodi4; diff --git a/lib/std/special/compiler_rt/addo.zig b/lib/std/special/compiler_rt/addo.zig new file mode 100644 index 0000000000..966c74cb8e --- /dev/null +++ b/lib/std/special/compiler_rt/addo.zig @@ -0,0 +1,38 @@ +const builtin = @import("builtin"); + +// addo - add overflow +// * return a+%b. +// * return if a+b overflows => 1 else => 0 +// - addoXi4_generic as default + +inline fn addoXi4_generic(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST { + @setRuntimeSafety(builtin.is_test); + overflow.* = 0; + var sum: ST = a +% b; + // Hackers Delight: section Overflow Detection, subsection Signed Add/Subtract + // Let sum = a +% b == a + b + carry == wraparound addition. + // Overflow in a+b+carry occurs, iff a and b have opposite signs + // and the sign of a+b+carry is the same as a (or equivalently b). + // Slower routine: res = ~(a ^ b) & ((sum ^ a) + // Faster routine: res = (sum ^ a) & (sum ^ b) + // Oerflow occured, iff (res < 0) + if (((sum ^ a) & (sum ^ b)) < 0) + overflow.* = 1; + return sum; +} + +pub fn __addosi4(a: i32, b: i32, overflow: *c_int) callconv(.C) i32 { + return addoXi4_generic(i32, a, b, overflow); +} +pub fn __addodi4(a: i64, b: i64, overflow: *c_int) callconv(.C) i64 { + return addoXi4_generic(i64, a, b, overflow); +} +pub fn __addoti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 { + return addoXi4_generic(i128, a, b, overflow); +} + +test { + _ = @import("addosi4_test.zig"); + _ = @import("addodi4_test.zig"); + _ = @import("addoti4_test.zig"); +} diff --git a/lib/std/special/compiler_rt/addodi4_test.zig b/lib/std/special/compiler_rt/addodi4_test.zig new file mode 100644 index 0000000000..f70a80a5b2 --- /dev/null +++ b/lib/std/special/compiler_rt/addodi4_test.zig @@ -0,0 +1,77 @@ +const addv = @import("addo.zig"); +const std = @import("std"); +const testing = std.testing; +const math = std.math; + +fn test__addodi4(a: i64, b: i64) !void { + var result_ov: c_int = undefined; + var expected_ov: c_int = undefined; + var result = addv.__addodi4(a, b, &result_ov); + var expected: i64 = simple_addodi4(a, b, &expected_ov); + try testing.expectEqual(expected, result); + try testing.expectEqual(expected_ov, result_ov); +} + +fn simple_addodi4(a: i64, b: i64, overflow: *c_int) i64 { + overflow.* = 0; + const min: i64 = math.minInt(i64); + const max: i64 = math.maxInt(i64); + if (((a > 0) and (b > max - a)) or + ((a < 0) and (b < min - a))) + overflow.* = 1; + return a +% b; +} + +test "addodi4" { + const min: i64 = math.minInt(i64); + const max: i64 = math.maxInt(i64); + var i: i64 = 1; + while (i < max) : (i *|= 2) { + try test__addodi4(i, i); + try test__addodi4(-i, -i); + try test__addodi4(i, -i); + try test__addodi4(-i, i); + } + + // edge cases + // 0 + 0 = 0 + // MIN + MIN overflow + // MAX + MAX overflow + // 0 + MIN MIN + // 0 + MAX MAX + // MIN + 0 MIN + // MAX + 0 MAX + // MIN + MAX -1 + // MAX + MIN -1 + try test__addodi4(0, 0); + try test__addodi4(min, min); + try test__addodi4(max, max); + try test__addodi4(0, min); + try test__addodi4(0, max); + try test__addodi4(min, 0); + try test__addodi4(max, 0); + try test__addodi4(min, max); + try test__addodi4(max, min); + + // derived edge cases + // MIN+1 + MIN overflow + // MAX-1 + MAX overflow + // 1 + MIN = MIN+1 + // -1 + MIN overflow + // -1 + MAX = MAX-1 + // +1 + MAX overflow + // MIN + 1 = MIN+1 + // MIN + -1 overflow + // MAX + 1 overflow + // MAX + -1 = MAX-1 + try test__addodi4(min + 1, min); + try test__addodi4(max - 1, max); + try test__addodi4(1, min); + try test__addodi4(-1, min); + try test__addodi4(-1, max); + try test__addodi4(1, max); + try test__addodi4(min, 1); + try test__addodi4(min, -1); + try test__addodi4(max, -1); + try test__addodi4(max, 1); +} diff --git a/lib/std/special/compiler_rt/addosi4_test.zig b/lib/std/special/compiler_rt/addosi4_test.zig new file mode 100644 index 0000000000..a8f81d70d1 --- /dev/null +++ b/lib/std/special/compiler_rt/addosi4_test.zig @@ -0,0 +1,78 @@ +const addv = @import("addo.zig"); +const testing = @import("std").testing; + +fn test__addosi4(a: i32, b: i32) !void { + var result_ov: c_int = undefined; + var expected_ov: c_int = undefined; + var result = addv.__addosi4(a, b, &result_ov); + var expected: i32 = simple_addosi4(a, b, &expected_ov); + try testing.expectEqual(expected, result); + try testing.expectEqual(expected_ov, result_ov); +} + +fn simple_addosi4(a: i32, b: i32, overflow: *c_int) i32 { + overflow.* = 0; + const min: i32 = -2147483648; + const max: i32 = 2147483647; + if (((a > 0) and (b > max - a)) or + ((a < 0) and (b < min - a))) + overflow.* = 1; + return a +% b; +} + +test "addosi4" { + // -2^31 <= i32 <= 2^31-1 + // 2^31 = 2147483648 + // 2^31-1 = 2147483647 + const min: i32 = -2147483648; + const max: i32 = 2147483647; + var i: i32 = 1; + while (i < max) : (i *|= 2) { + try test__addosi4(i, i); + try test__addosi4(-i, -i); + try test__addosi4(i, -i); + try test__addosi4(-i, i); + } + + // edge cases + // 0 + 0 = 0 + // MIN + MIN overflow + // MAX + MAX overflow + // 0 + MIN MIN + // 0 + MAX MAX + // MIN + 0 MIN + // MAX + 0 MAX + // MIN + MAX -1 + // MAX + MIN -1 + try test__addosi4(0, 0); + try test__addosi4(min, min); + try test__addosi4(max, max); + try test__addosi4(0, min); + try test__addosi4(0, max); + try test__addosi4(min, 0); + try test__addosi4(max, 0); + try test__addosi4(min, max); + try test__addosi4(max, min); + + // derived edge cases + // MIN+1 + MIN overflow + // MAX-1 + MAX overflow + // 1 + MIN = MIN+1 + // -1 + MIN overflow + // -1 + MAX = MAX-1 + // +1 + MAX overflow + // MIN + 1 = MIN+1 + // MIN + -1 overflow + // MAX + 1 overflow + // MAX + -1 = MAX-1 + try test__addosi4(min + 1, min); + try test__addosi4(max - 1, max); + try test__addosi4(1, min); + try test__addosi4(-1, min); + try test__addosi4(-1, max); + try test__addosi4(1, max); + try test__addosi4(min, 1); + try test__addosi4(min, -1); + try test__addosi4(max, -1); + try test__addosi4(max, 1); +} diff --git a/lib/std/special/compiler_rt/addoti4_test.zig b/lib/std/special/compiler_rt/addoti4_test.zig new file mode 100644 index 0000000000..dd0f4e3d3c --- /dev/null +++ b/lib/std/special/compiler_rt/addoti4_test.zig @@ -0,0 +1,77 @@ +const addv = @import("addo.zig"); +const std = @import("std"); +const testing = std.testing; +const math = std.math; + +fn test__addoti4(a: i128, b: i128) !void { + var result_ov: c_int = undefined; + var expected_ov: c_int = undefined; + var result = addv.__addoti4(a, b, &result_ov); + var expected: i128 = simple_addoti4(a, b, &expected_ov); + try testing.expectEqual(expected, result); + try testing.expectEqual(expected_ov, result_ov); +} + +fn simple_addoti4(a: i128, b: i128, overflow: *c_int) i128 { + overflow.* = 0; + const min: i128 = math.minInt(i128); + const max: i128 = math.maxInt(i128); + if (((a > 0) and (b > max - a)) or + ((a < 0) and (b < min - a))) + overflow.* = 1; + return a +% b; +} + +test "addoti4" { + const min: i128 = math.minInt(i128); + const max: i128 = math.maxInt(i128); + var i: i128 = 1; + while (i < max) : (i *|= 2) { + try test__addoti4(i, i); + try test__addoti4(-i, -i); + try test__addoti4(i, -i); + try test__addoti4(-i, i); + } + + // edge cases + // 0 + 0 = 0 + // MIN + MIN overflow + // MAX + MAX overflow + // 0 + MIN MIN + // 0 + MAX MAX + // MIN + 0 MIN + // MAX + 0 MAX + // MIN + MAX -1 + // MAX + MIN -1 + try test__addoti4(0, 0); + try test__addoti4(min, min); + try test__addoti4(max, max); + try test__addoti4(0, min); + try test__addoti4(0, max); + try test__addoti4(min, 0); + try test__addoti4(max, 0); + try test__addoti4(min, max); + try test__addoti4(max, min); + + // derived edge cases + // MIN+1 + MIN overflow + // MAX-1 + MAX overflow + // 1 + MIN = MIN+1 + // -1 + MIN overflow + // -1 + MAX = MAX-1 + // +1 + MAX overflow + // MIN + 1 = MIN+1 + // MIN + -1 overflow + // MAX + 1 overflow + // MAX + -1 = MAX-1 + try test__addoti4(min + 1, min); + try test__addoti4(max - 1, max); + try test__addoti4(1, min); + try test__addoti4(-1, min); + try test__addoti4(-1, max); + try test__addoti4(1, max); + try test__addoti4(min, 1); + try test__addoti4(min, -1); + try test__addoti4(max, -1); + try test__addoti4(max, 1); +} diff --git a/lib/std/special/compiler_rt/mulo.zig b/lib/std/special/compiler_rt/mulo.zig index df4c98134c..78590e5ce1 100644 --- a/lib/std/special/compiler_rt/mulo.zig +++ b/lib/std/special/compiler_rt/mulo.zig @@ -3,7 +3,7 @@ const std = @import("std"); const math = std.math; // mulo - multiplication overflow -// * return a*b. +// * return a*%b. // * return if a*b overflows => 1 else => 0 // - muloXi4_genericSmall as default // - muloXi4_genericFast for 2*bitsize <= usize From 0a7801236cf3601ff20b3c1c16cd27c1d089157e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 7 Feb 2022 15:29:14 +0100 Subject: [PATCH 0069/2031] stage2,arm: add lowering of unnamed consts * implement `struct_field_ptr` when `MCValue == .stack_argument_offset` * enable simple `struct` test for ARM --- src/arch/arm/CodeGen.zig | 46 +++++++++++++++++++++++++++++++++++++++- test/behavior/struct.zig | 6 ++---- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 804aedb6cc..1859ce874f 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1643,7 +1643,8 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void { fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue { return if (self.liveness.isUnused(inst)) .dead else result: { const mcv = try self.resolveInst(operand); - const struct_ty = self.air.typeOf(operand).childType(); + const ptr_ty = self.air.typeOf(operand); + const struct_ty = ptr_ty.childType(); const struct_size = @intCast(u32, struct_ty.abiSize(self.target.*)); const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); const struct_field_ty = struct_ty.structFieldType(index); @@ -1652,6 +1653,28 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde .ptr_stack_offset => |off| { break :result MCValue{ .ptr_stack_offset = off + struct_size - struct_field_offset - struct_field_size }; }, + .stack_argument_offset => { + const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ + .immediate = struct_field_offset, + }); + self.register_manager.freezeRegs(&.{offset_reg}); + defer self.register_manager.unfreezeRegs(&.{offset_reg}); + + const addr_reg = try self.copyToTmpRegister(ptr_ty, mcv); + self.register_manager.freezeRegs(&.{addr_reg}); + defer self.register_manager.unfreezeRegs(&.{addr_reg}); + + const dst_reg = try self.register_manager.allocReg(inst); + try self.genBinOpCode( + dst_reg, + .{ .register = addr_reg }, + .{ .register = offset_reg }, + false, + .add, + .unsigned, + ); + break :result MCValue{ .register = dst_reg }; + }, else => return self.fail("TODO implement codegen struct_field_ptr for {}", .{mcv}), } }; @@ -3841,6 +3864,24 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa _ = tv; } +fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { + const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| { + return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); + }; + if (self.bin_file.cast(link.File.Elf)) |elf_file| { + const vaddr = elf_file.local_symbols.items[local_sym_index].st_value; + return MCValue{ .memory = vaddr }; + } else if (self.bin_file.cast(link.File.MachO)) |_| { + unreachable; + } else if (self.bin_file.cast(link.File.Coff)) |_| { + return self.fail("TODO lower unnamed const in COFF", .{}); + } else if (self.bin_file.cast(link.File.Plan9)) |_| { + return self.fail("TODO lower unnamed const in Plan9", .{}); + } else { + return self.fail("TODO lower unnamed const", .{}); + } +} + fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { if (typed_value.val.isUndef()) return MCValue{ .undef = {} }; @@ -3953,6 +3994,9 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty}); } }, + .Struct => { + return self.lowerUnnamedConst(typed_value); + }, else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}), } } diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index eb5a9e4273..5cf3776889 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -53,20 +53,18 @@ test "non-packed struct has fields padded out to the required alignment" { const SmallStruct = struct { a: u8, - b: u32, + b: u8, fn first(self: *SmallStruct) u8 { return self.a; } - fn second(self: *SmallStruct) u32 { + fn second(self: *SmallStruct) u8 { return self.b; } }; test "lower unnamed constants" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - var foo = SmallStruct{ .a = 1, .b = 255 }; try expect(foo.first() == 1); try expect(foo.second() == 255); From bbd7969c87e03c9312eeafd9940ef8287a3ad689 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 7 Feb 2022 16:02:43 +0100 Subject: [PATCH 0070/2031] stage2,x64: implement genSetStack for memory operand --- src/arch/x86_64/CodeGen.zig | 43 ++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index a60b8c78f0..7baf74c9c2 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -3487,6 +3487,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE } fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerError!void { + const abi_size = ty.abiSize(self.target.*); switch (mcv) { .dead => unreachable, .ptr_embedded_in_code => unreachable, @@ -3510,7 +3511,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro return self.genSetStack(ty, stack_offset, .{ .register = reg }); }, .immediate => |x_big| { - const abi_size = ty.abiSize(self.target.*); const adj_off = stack_offset + @intCast(i32, abi_size); if (adj_off > 128) { return self.fail("TODO implement set stack variable with large stack offset", .{}); @@ -3583,7 +3583,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro if (stack_offset > math.maxInt(i32)) { return self.fail("stack offset too large", .{}); } - const abi_size = ty.abiSize(self.target.*); const adj_off = stack_offset + @intCast(i32, abi_size); _ = try self.addInst(.{ .tag = .mov, @@ -3600,11 +3599,46 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro .got_load, .direct_load, => { - if (ty.abiSize(self.target.*) <= 8) { + if (abi_size <= 8) { const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); } - return self.fail("TODO implement memcpy for setting stack from {}", .{mcv}); + + try self.register_manager.getReg(.rax, null); + try self.register_manager.getReg(.rcx, null); + + self.register_manager.freezeRegs(&.{ .rax, .rcx, .rbp }); + defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx, .rbp }); + + const addr_reg: Register = blk: { + switch (mcv) { + .memory => |addr| { + const reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = addr }); + break :blk reg; + }, + else => { + return self.fail("TODO implement memcpy for setting stack from {}", .{mcv}); + }, + } + }; + + self.register_manager.freezeRegs(&.{addr_reg}); + defer self.register_manager.unfreezeRegs(&.{addr_reg}); + + const regs = try self.register_manager.allocRegs(2, .{ null, null }); + const count_reg = regs[0]; + const tmp_reg = regs[1]; + + // TODO allow for abi_size to be u64 + try self.genSetReg(Type.u32, count_reg, .{ .immediate = @intCast(u32, abi_size) }); + + return self.genInlineMemcpy( + -(stack_offset + @intCast(i32, abi_size)), + .rbp, + addr_reg.to64(), + count_reg.to64(), + tmp_reg.to8(), + ); }, .ptr_stack_offset => { const reg = try self.copyToTmpRegister(ty, mcv); @@ -3616,7 +3650,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro return; } - const abi_size = ty.abiSize(self.target.*); if (abi_size <= 8) { const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); From ac36fe71147a78e5f9299428eee776efcc9e4afb Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 7 Feb 2022 16:23:37 +0100 Subject: [PATCH 0071/2031] stage2,x64: fix registerAlias helper function --- src/arch/x86_64/CodeGen.zig | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 7baf74c9c2..1aac404346 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -4692,11 +4692,17 @@ fn parseRegName(name: []const u8) ?Register { fn registerAlias(reg: Register, size_bytes: u32) Register { // For x86_64 we have to pick a smaller register alias depending on abi size. - switch (size_bytes) { - 1 => return reg.to8(), - 2 => return reg.to16(), - 4 => return reg.to32(), - 8 => return reg.to64(), - else => unreachable, + if (size_bytes == 0) { + unreachable; // should be comptime known + } else if (size_bytes <= 1) { + return reg.to8(); + } else if (size_bytes <= 2) { + return reg.to16(); + } else if (size_bytes <= 4) { + return reg.to32(); + } else if (size_bytes <= 8) { + return reg.to64(); + } else { + unreachable; // TODO handle floating-point registers } } From 7a9b9df80e9799421393f476ea10332ba35ec258 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 7 Feb 2022 17:08:33 +0100 Subject: [PATCH 0072/2031] stage2,x64: impl masking reg for struct_field_val --- src/arch/x86_64/CodeGen.zig | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 1aac404346..e2ae339bae 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2025,16 +2025,35 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const mcv = try self.resolveInst(operand); const struct_ty = self.air.typeOf(operand); - const struct_size = @intCast(i32, struct_ty.abiSize(self.target.*)); - const struct_field_offset = @intCast(i32, struct_ty.structFieldOffset(index, self.target.*)); + const struct_size = struct_ty.abiSize(self.target.*); + const struct_field_offset = struct_ty.structFieldOffset(index, self.target.*); const struct_field_ty = struct_ty.structFieldType(index); - const struct_field_size = @intCast(i32, struct_field_ty.abiSize(self.target.*)); + const struct_field_size = struct_field_ty.abiSize(self.target.*); switch (mcv) { .stack_offset => |off| { - const stack_offset = off + struct_size - struct_field_offset - struct_field_size; + const offset_to_field = struct_size - struct_field_offset - struct_field_size; + const stack_offset = off + @intCast(i32, offset_to_field); break :result MCValue{ .stack_offset = stack_offset }; }, + .register => |reg| { + // 1. Shift by struct_field_offset. + // 2. Mask with reg.size() - struct_field_size + // 3. Return in register + + // TODO check if register can be re-used + self.register_manager.freezeRegs(&.{reg}); + defer self.register_manager.unfreezeRegs(&.{reg}); + const dst_mcv = try self.copyToNewRegister(inst, Type.usize, .{ .register = reg.to64() }); + + // TODO shift here + + const mask_shift = @intCast(u6, (64 - struct_field_ty.bitSize(self.target.*))); + const mask = (~@as(u64, 0)) >> mask_shift; + try self.genBinMathOpMir(.@"and", Type.usize, dst_mcv, .{ .immediate = mask }); + + break :result dst_mcv; + }, else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), } }; From becbf446d3d33fd73a7c1567e89a98a9b191d4e8 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 7 Feb 2022 20:11:42 +0100 Subject: [PATCH 0073/2031] stage2,x64: impl lowering of shift ops in Emit --- src/arch/x86_64/CodeGen.zig | 39 +++++++-- src/arch/x86_64/Emit.zig | 156 ++++++++++++++++++++++++++++++++---- src/arch/x86_64/Mir.zig | 55 +++++++------ src/codegen.zig | 1 - 4 files changed, 203 insertions(+), 48 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index e2ae339bae..2642d5ce15 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2037,17 +2037,42 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { break :result MCValue{ .stack_offset = stack_offset }; }, .register => |reg| { - // 1. Shift by struct_field_offset. - // 2. Mask with reg.size() - struct_field_size - // 3. Return in register - - // TODO check if register can be re-used self.register_manager.freezeRegs(&.{reg}); defer self.register_manager.unfreezeRegs(&.{reg}); - const dst_mcv = try self.copyToNewRegister(inst, Type.usize, .{ .register = reg.to64() }); - // TODO shift here + const dst_mcv = blk: { + if (self.reuseOperand(inst, operand, 0, mcv)) { + break :blk mcv; + } else { + const dst_mcv = try self.copyToNewRegister(inst, Type.usize, .{ .register = reg.to64() }); + break :blk dst_mcv; + } + }; + // Shift by struct_field_offset. + const shift_amount = @intCast(u8, struct_field_offset * 8); + if (shift_amount > 0) { + if (shift_amount == 1) { + _ = try self.addInst(.{ + .tag = .shr, + .ops = (Mir.Ops{ + .reg1 = dst_mcv.register, + }).encode(), + .data = undefined, + }); + } else { + _ = try self.addInst(.{ + .tag = .shr, + .ops = (Mir.Ops{ + .reg1 = dst_mcv.register, + .flags = 0b10, + }).encode(), + .data = .{ .imm = shift_amount }, + }); + } + } + + // Mask with reg.size() - struct_field_size const mask_shift = @intCast(u6, (64 - struct_field_ty.bitSize(self.target.*))); const mask = (~@as(u64, 0)) >> mask_shift; try self.genBinMathOpMir(.@"and", Type.usize, dst_mcv, .{ .immediate = mask }); diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index be26354402..3f221f0f19 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -133,6 +133,11 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .lea => try emit.mirLea(inst), .lea_pie => try emit.mirLeaPie(inst), + .shl => try emit.mirShift(.shl, inst), + .sal => try emit.mirShift(.sal, inst), + .shr => try emit.mirShift(.shr, inst), + .sar => try emit.mirShift(.sar, inst), + .imul_complex => try emit.mirIMulComplex(inst), .push => try emit.mirPushPop(.push, inst), @@ -653,6 +658,31 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { return lowerToFdEnc(.mov, ops.reg1, imm, emit.code); } +fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + switch (ops.flags) { + 0b00 => { + // sal reg1, 1 + // M1 + return lowerToM1Enc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); + }, + 0b01 => { + // sal reg1, .cl + // MC + return lowerToMcEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); + }, + 0b10 => { + // sal reg1, imm8 + // MI + const imm = @truncate(u8, emit.mir.instructions.items(.data)[inst].imm); + return lowerToMiImm8Enc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code); + }, + 0b11 => { + return emit.fail("TODO unused variant: SHIFT reg1, 0b11", .{}); + }, + } +} + fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .imul_complex); @@ -743,13 +773,13 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { emit.code, ); const end_offset = emit.code.items.len; - const reloc_type = switch (ops.flags) { - 0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), - 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), - else => return emit.fail("TODO unused LEA PIE variants 0b10 and 0b11", .{}), - }; const sym_index = emit.mir.instructions.items(.data)[inst].linker_sym_index; if (emit.bin_file.cast(link.File.MachO)) |macho_file| { + const reloc_type = switch (ops.flags) { + 0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), + 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), + else => return emit.fail("TODO unused LEA PIE variants 0b10 and 0b11", .{}), + }; const decl = macho_file.active_decl.?; try decl.link.macho.relocs.append(emit.bin_file.allocator, .{ .offset = @intCast(u32, end_offset - 4), @@ -1064,6 +1094,10 @@ const Tag = enum { setng, setnle, setg, + shl, + sal, + shr, + sar, fn isSetCC(tag: Tag) bool { return switch (tag) { @@ -1119,9 +1153,18 @@ const Encoding = enum { /// OP imm32 i, + /// OP r/m64, 1 + m1, + + /// OP r/m64, .cl + mc, + /// OP r/m64, imm32 mi, + /// OP r/m64, imm8 + mi8, + /// OP r/m64, r64 mr, @@ -1230,12 +1273,25 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .ret_far => OpCode.oneByte(0xca), else => null, }, + .m1 => return switch (tag) { + .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd0 else 0xd1), + else => null, + }, + .mc => return switch (tag) { + .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd2 else 0xd3), + else => null, + }, .mi => return switch (tag) { .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(if (is_one_byte) 0x80 else 0x81), .mov => OpCode.oneByte(if (is_one_byte) 0xc6 else 0xc7), .@"test" => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7), else => null, }, + .mi8 => return switch (tag) { + .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(0x83), + .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xc0 else 0xc1), + else => null, + }, .mr => return switch (tag) { .adc => OpCode.oneByte(if (is_one_byte) 0x10 else 0x11), .add => OpCode.oneByte(if (is_one_byte) 0x00 else 0x01), @@ -1331,6 +1387,11 @@ inline fn getModRmExt(tag: Tag) ?u3 { .setnle, .setg, => 0x0, + .shl, + .sal, + => 0x4, + .shr => 0x5, + .sar => 0x7, else => null, }; } @@ -1528,8 +1589,8 @@ fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void { encoder.imm32(@bitCast(i32, imm)); } -fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { - const opc = getOpCode(tag, .m, false).?; +fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *std.ArrayList(u8)) InnerError!void { + const opc = getOpCode(tag, enc, reg_or_mem.size() == 8).?; const modrm_ext = getModRmExt(tag).?; switch (reg_or_mem) { .register => |reg| { @@ -1537,11 +1598,9 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) if (reg.size() == 16) { encoder.prefix16BitMode(); } + const wide = if (tag == .jmp_near) false else setRexWRegister(reg); encoder.rex(.{ - .w = switch (reg) { - .ah, .bh, .ch, .dh => true, - else => false, - }, + .w = wide, .b = reg.isExtended(), }); opc.encode(encoder); @@ -1553,8 +1612,9 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) encoder.prefix16BitMode(); } if (mem_op.base) |base| { + const wide = if (tag == .jmp_near) false else mem_op.ptr_size == .qword_ptr; encoder.rex(.{ - .w = false, + .w = wide, .b = base.isExtended(), }); } @@ -1564,6 +1624,18 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) } } +fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { + return lowerToMxEnc(tag, reg_or_mem, .m, code); +} + +fn lowerToM1Enc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { + return lowerToMxEnc(tag, reg_or_mem, .m1, code); +} + +fn lowerToMcEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { + return lowerToMxEnc(tag, reg_or_mem, .mc, code); +} + fn lowerToTdEnc(tag: Tag, moffs: u64, reg: Register, code: *std.ArrayList(u8)) InnerError!void { return lowerToTdFdEnc(tag, reg, moffs, code, true); } @@ -1614,9 +1686,15 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) Inn } } -fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.ArrayList(u8)) InnerError!void { +fn lowerToMiXEnc( + tag: Tag, + reg_or_mem: RegisterOrMemory, + imm: u32, + enc: Encoding, + code: *std.ArrayList(u8), +) InnerError!void { const modrm_ext = getModRmExt(tag).?; - const opc = getOpCode(tag, .mi, reg_or_mem.size() == 8).?; + const opc = getOpCode(tag, enc, reg_or_mem.size() == 8).?; switch (reg_or_mem) { .register => |dst_reg| { const encoder = try Encoder.init(code, 7); @@ -1632,7 +1710,7 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.Arr }); opc.encode(encoder); encoder.modRm_direct(modrm_ext, dst_reg.lowId()); - encodeImm(encoder, imm, dst_reg.size()); + encodeImm(encoder, imm, if (enc == .mi8) 8 else dst_reg.size()); }, .memory => |dst_mem| { const encoder = try Encoder.init(code, 12); @@ -1651,11 +1729,19 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.Arr } opc.encode(encoder); dst_mem.encode(encoder, modrm_ext); - encodeImm(encoder, imm, dst_mem.ptr_size.size()); + encodeImm(encoder, imm, if (enc == .mi8) 8 else dst_mem.ptr_size.size()); }, } } +fn lowerToMiImm8Enc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u8, code: *std.ArrayList(u8)) InnerError!void { + return lowerToMiXEnc(tag, reg_or_mem, imm, .mi8, code); +} + +fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.ArrayList(u8)) InnerError!void { + return lowerToMiXEnc(tag, reg_or_mem, imm, .mi, code); +} + fn lowerToRmEnc( tag: Tag, reg: Register, @@ -1902,6 +1988,9 @@ test "lower MI encoding" { emit.lowered(), "mov qword ptr [rcx*2 + 0x10000000], 0x10", ); + + try lowerToMiImm8Enc(.add, RegisterOrMemory.reg(.rax), 0x10, emit.code()); + try expectEqualHexStrings("\x48\x83\xC0\x10", emit.lowered(), "add rax, 0x10"); } test "lower RM encoding" { @@ -2100,6 +2189,41 @@ test "lower M encoding" { try expectEqualHexStrings("\x41\x0F\x97\xC3", emit.lowered(), "seta r11b"); } +test "lower M1 and MC encodings" { + var emit = TestEmit.init(); + defer emit.deinit(); + try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12), emit.code()); + try expectEqualHexStrings("\x49\xD1\xE4", emit.lowered(), "sal r12, 1"); + try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12d), emit.code()); + try expectEqualHexStrings("\x41\xD1\xE4", emit.lowered(), "sal r12d, 1"); + try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12w), emit.code()); + try expectEqualHexStrings("\x66\x41\xD1\xE4", emit.lowered(), "sal r12w, 1"); + try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12b), emit.code()); + try expectEqualHexStrings("\x41\xD0\xE4", emit.lowered(), "sal r12b, 1"); + try lowerToM1Enc(.sal, RegisterOrMemory.reg(.rax), emit.code()); + try expectEqualHexStrings("\x48\xD1\xE0", emit.lowered(), "sal rax, 1"); + try lowerToM1Enc(.sal, RegisterOrMemory.reg(.eax), emit.code()); + try expectEqualHexStrings("\xD1\xE0", emit.lowered(), "sal eax, 1"); + try lowerToM1Enc(.sal, RegisterOrMemory.mem(.qword_ptr, .{ + .disp = @bitCast(u32, @as(i32, -0x10)), + .base = .rbp, + }), emit.code()); + try expectEqualHexStrings("\x48\xD1\x65\xF0", emit.lowered(), "sal qword ptr [rbp - 0x10], 1"); + try lowerToM1Enc(.sal, RegisterOrMemory.mem(.dword_ptr, .{ + .disp = @bitCast(u32, @as(i32, -0x10)), + .base = .rbp, + }), emit.code()); + try expectEqualHexStrings("\xD1\x65\xF0", emit.lowered(), "sal dword ptr [rbp - 0x10], 1"); + + try lowerToMcEnc(.shr, RegisterOrMemory.reg(.r12), emit.code()); + try expectEqualHexStrings("\x49\xD3\xEC", emit.lowered(), "shr r12, cl"); + try lowerToMcEnc(.shr, RegisterOrMemory.reg(.rax), emit.code()); + try expectEqualHexStrings("\x48\xD3\xE8", emit.lowered(), "shr rax, cl"); + + try lowerToMcEnc(.sar, RegisterOrMemory.reg(.rsi), emit.code()); + try expectEqualHexStrings("\x48\xD3\xFE", emit.lowered(), "sar rsi, cl"); +} + test "lower O encoding" { var emit = TestEmit.init(); defer emit.deinit(); diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 2e8a9cf332..aaabcab04d 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -142,30 +142,6 @@ pub const Inst = struct { rcr_scale_dst, rcr_scale_imm, rcr_mem_index_imm, - shl, - shl_mem_imm, - shl_scale_src, - shl_scale_dst, - shl_scale_imm, - shl_mem_index_imm, - sal, - sal_mem_imm, - sal_scale_src, - sal_scale_dst, - sal_scale_imm, - sal_mem_index_imm, - shr, - shr_mem_imm, - shr_scale_src, - shr_scale_dst, - shr_scale_imm, - shr_mem_index_imm, - sar, - sar_mem_imm, - sar_scale_src, - sar_scale_dst, - sar_scale_imm, - sar_mem_index_imm, sbb, sbb_mem_imm, sbb_scale_src, @@ -212,6 +188,37 @@ pub const Inst = struct { /// * `Data` contains `linker_sym_index` lea_pie, + /// ops flags: form: + /// 0b00 reg1, 1 + /// 0b01 reg1, .cl + /// 0b10 reg1, imm8 + /// Notes: + /// * If flags == 0b10, uses `imm`. + shl, + shl_mem_imm, + shl_scale_src, + shl_scale_dst, + shl_scale_imm, + shl_mem_index_imm, + sal, + sal_mem_imm, + sal_scale_src, + sal_scale_dst, + sal_scale_imm, + sal_mem_index_imm, + shr, + shr_mem_imm, + shr_scale_src, + shr_scale_dst, + shr_scale_imm, + shr_mem_index_imm, + sar, + sar_mem_imm, + sar_scale_src, + sar_scale_dst, + sar_scale_imm, + sar_mem_index_imm, + /// ops flags: form: /// 0bX0 reg1 /// 0bX1 [reg1 + imm32] diff --git a/src/codegen.zig b/src/codegen.zig index bcd36358b1..059d2adc14 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -372,7 +372,6 @@ pub fn generateSymbol( return Result{ .appended = {} }; }, .Struct => { - // TODO debug info const struct_obj = typed_value.ty.castTag(.@"struct").?.data; if (struct_obj.layout == .Packed) { return Result{ From 8a94971980001d29d7c8cdfe6ca25aa834552405 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 Feb 2022 12:19:43 -0700 Subject: [PATCH 0074/2031] std: fix i386-openbsd failing to build from source closes #9705 --- lib/std/c/openbsd.zig | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/std/c/openbsd.zig b/lib/std/c/openbsd.zig index d2a36fc5df..6ba11e8e5a 100644 --- a/lib/std/c/openbsd.zig +++ b/lib/std/c/openbsd.zig @@ -982,7 +982,7 @@ comptime { std.debug.assert(@sizeOf(siginfo_t) == 136); } -const arch_bits = switch (builtin.cpu.arch) { +pub usingnamespace switch (builtin.cpu.arch) { .x86_64 => struct { pub const ucontext_t = extern struct { sc_rdi: c_long, @@ -1012,7 +1012,7 @@ const arch_bits = switch (builtin.cpu.arch) { sc_rsp: c_long, sc_ss: c_long, - sc_fpstate: arch_bits.fxsave64, + sc_fpstate: fxsave64, __sc_unused: c_int, sc_mask: c_int, sc_cookie: c_long, @@ -1035,8 +1035,6 @@ const arch_bits = switch (builtin.cpu.arch) { }, else => struct {}, }; -pub const ucontext_t = arch_bits.ucontext_t; -pub const fxsave64 = arch_bits.fxsave64; pub const sigset_t = c_uint; pub const empty_sigset: sigset_t = 0; From 785bccd4cea63b3f2266e7f8928000e2d5110b3b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 7 Feb 2022 20:48:31 +0100 Subject: [PATCH 0075/2031] stage2: pass more struct tests --- test/behavior/struct.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 5cf3776889..e4b64a39d3 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -9,7 +9,7 @@ const maxInt = std.math.maxInt; top_level_field: i32, test "top level fields" { - if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var instance = @This(){ .top_level_field = 1234, @@ -176,7 +176,7 @@ const MemberFnTestFoo = struct { }; test "call member function directly" { - if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const instance = MemberFnTestFoo{ .x = 1234 }; const result = MemberFnTestFoo.member(instance); @@ -184,7 +184,7 @@ test "call member function directly" { } test "store member function in variable" { - if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const instance = MemberFnTestFoo{ .x = 1234 }; const memberFn = MemberFnTestFoo.member; @@ -206,7 +206,7 @@ const MemberFnRand = struct { }; test "return struct byval from function" { - if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const bar = makeBar2(1234, 5678); try expect(bar.y == 5678); From 2a415a033cce07b579a490eca2556e7d700c04b1 Mon Sep 17 00:00:00 2001 From: Arnavion Date: Wed, 22 Dec 2021 16:48:46 -0800 Subject: [PATCH 0076/2031] std.bit_set: add setRangeValue(Range, bool) For large ranges, this is faster than having the caller call setValue() for each index in the range. Masks wholly covered by the range can be set to the new mask value in one go, and the two masks at either end that are partially covered can each set the covered range of bits in one go. --- lib/std/bit_set.zig | 174 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) diff --git a/lib/std/bit_set.zig b/lib/std/bit_set.zig index 5101f934bc..d839512c07 100644 --- a/lib/std/bit_set.zig +++ b/lib/std/bit_set.zig @@ -110,6 +110,31 @@ pub fn IntegerBitSet(comptime size: u16) type { self.mask |= maskBit(index); } + /// Changes the value of all bits in the specified range to + /// match the passed boolean. + pub fn setRangeValue(self: *Self, range: Range, value: bool) void { + assert(range.end <= bit_length); + assert(range.start <= range.end); + if (range.start == range.end) return; + if (MaskInt == u0) return; + + const start_bit = @intCast(ShiftInt, range.start); + + var mask = std.math.boolMask(MaskInt, true) << start_bit; + if (range.end != bit_length) { + const end_bit = @intCast(ShiftInt, range.end); + mask &= std.math.boolMask(MaskInt, true) >> @truncate(ShiftInt, @as(usize, @bitSizeOf(MaskInt)) - @as(usize, end_bit)); + } + self.mask &= ~mask; + + mask = std.math.boolMask(MaskInt, value) << start_bit; + if (range.end != bit_length) { + const end_bit = @intCast(ShiftInt, range.end); + mask &= std.math.boolMask(MaskInt, value) >> @truncate(ShiftInt, @as(usize, @bitSizeOf(MaskInt)) - @as(usize, end_bit)); + } + self.mask |= mask; + } + /// Removes a specific bit from the bit set pub fn unset(self: *Self, index: usize) void { assert(index < bit_length); @@ -345,6 +370,51 @@ pub fn ArrayBitSet(comptime MaskIntType: type, comptime size: usize) type { self.masks[maskIndex(index)] |= maskBit(index); } + /// Changes the value of all bits in the specified range to + /// match the passed boolean. + pub fn setRangeValue(self: *Self, range: Range, value: bool) void { + assert(range.end <= bit_length); + assert(range.start <= range.end); + if (range.start == range.end) return; + if (num_masks == 0) return; + + const start_mask_index = maskIndex(range.start); + const start_bit = @truncate(ShiftInt, range.start); + + const end_mask_index = maskIndex(range.end); + const end_bit = @truncate(ShiftInt, range.end); + + if (start_mask_index == end_mask_index) { + var mask1 = std.math.boolMask(MaskInt, true) << start_bit; + var mask2 = std.math.boolMask(MaskInt, true) >> (mask_len - 1) - (end_bit - 1); + self.masks[start_mask_index] &= ~(mask1 & mask2); + + mask1 = std.math.boolMask(MaskInt, value) << start_bit; + mask2 = std.math.boolMask(MaskInt, value) >> (mask_len - 1) - (end_bit - 1); + self.masks[start_mask_index] |= mask1 & mask2; + } else { + var bulk_mask_index: usize = undefined; + if (start_bit > 0) { + self.masks[start_mask_index] = + (self.masks[start_mask_index] & ~(std.math.boolMask(MaskInt, true) << start_bit)) | + (std.math.boolMask(MaskInt, value) << start_bit); + bulk_mask_index = start_mask_index + 1; + } else { + bulk_mask_index = start_mask_index; + } + + while (bulk_mask_index < end_mask_index) : (bulk_mask_index += 1) { + self.masks[bulk_mask_index] = std.math.boolMask(MaskInt, value); + } + + if (end_bit > 0) { + self.masks[end_mask_index] = + (self.masks[end_mask_index] & (std.math.boolMask(MaskInt, true) << end_bit)) | + (std.math.boolMask(MaskInt, value) >> ((@bitSizeOf(MaskInt) - 1) - (end_bit - 1))); + } + } + } + /// Removes a specific bit from the bit set pub fn unset(self: *Self, index: usize) void { assert(index < bit_length); @@ -608,6 +678,50 @@ pub const DynamicBitSetUnmanaged = struct { self.masks[maskIndex(index)] |= maskBit(index); } + /// Changes the value of all bits in the specified range to + /// match the passed boolean. + pub fn setRangeValue(self: *Self, range: Range, value: bool) void { + assert(range.end <= self.bit_length); + assert(range.start <= range.end); + if (range.start == range.end) return; + + const start_mask_index = maskIndex(range.start); + const start_bit = @truncate(ShiftInt, range.start); + + const end_mask_index = maskIndex(range.end); + const end_bit = @truncate(ShiftInt, range.end); + + if (start_mask_index == end_mask_index) { + var mask1 = std.math.boolMask(MaskInt, true) << start_bit; + var mask2 = std.math.boolMask(MaskInt, true) >> (@bitSizeOf(MaskInt) - 1) - (end_bit - 1); + self.masks[start_mask_index] &= ~(mask1 & mask2); + + mask1 = std.math.boolMask(MaskInt, value) << start_bit; + mask2 = std.math.boolMask(MaskInt, value) >> (@bitSizeOf(MaskInt) - 1) - (end_bit - 1); + self.masks[start_mask_index] |= mask1 & mask2; + } else { + var bulk_mask_index: usize = undefined; + if (start_bit > 0) { + self.masks[start_mask_index] = + (self.masks[start_mask_index] & ~(std.math.boolMask(MaskInt, true) << start_bit)) | + (std.math.boolMask(MaskInt, value) << start_bit); + bulk_mask_index = start_mask_index + 1; + } else { + bulk_mask_index = start_mask_index; + } + + while (bulk_mask_index < end_mask_index) : (bulk_mask_index += 1) { + self.masks[bulk_mask_index] = std.math.boolMask(MaskInt, value); + } + + if (end_bit > 0) { + self.masks[end_mask_index] = + (self.masks[end_mask_index] & (std.math.boolMask(MaskInt, true) << end_bit)) | + (std.math.boolMask(MaskInt, value) >> ((@bitSizeOf(MaskInt) - 1) - (end_bit - 1))); + } + } + } + /// Removes a specific bit from the bit set pub fn unset(self: *Self, index: usize) void { assert(index < self.bit_length); @@ -811,6 +925,12 @@ pub const DynamicBitSet = struct { self.unmanaged.set(index); } + /// Changes the value of all bits in the specified range to + /// match the passed boolean. + pub fn setRangeValue(self: *Self, range: Range, value: bool) void { + self.unmanaged.setRangeValue(range, value); + } + /// Removes a specific bit from the bit set pub fn unset(self: *Self, index: usize) void { self.unmanaged.unset(index); @@ -990,6 +1110,14 @@ fn BitSetIterator(comptime MaskInt: type, comptime options: IteratorOptions) typ }; } +/// A range of indices within a bitset. +pub const Range = struct { + /// The index of the first bit of interest. + start: usize, + /// The index immediately after the last bit of interest. + end: usize, +}; + // ---------------- Tests ----------------- const testing = std.testing; @@ -1144,6 +1272,52 @@ fn testBitSet(a: anytype, b: anytype, len: usize) !void { try testing.expectEqual(@as(?usize, null), a.findFirstSet()); try testing.expectEqual(@as(?usize, null), a.toggleFirstSet()); try testing.expectEqual(@as(usize, 0), a.count()); + + a.setRangeValue(.{ .start = 0, .end = len }, false); + try testing.expectEqual(@as(usize, 0), a.count()); + + a.setRangeValue(.{ .start = 0, .end = len }, true); + try testing.expectEqual(len, a.count()); + + a.setRangeValue(.{ .start = 0, .end = len }, false); + a.setRangeValue(.{ .start = 0, .end = 0 }, true); + try testing.expectEqual(@as(usize, 0), a.count()); + + a.setRangeValue(.{ .start = len, .end = len }, true); + try testing.expectEqual(@as(usize, 0), a.count()); + + if (len >= 1) { + a.setRangeValue(.{ .start = 0, .end = len }, false); + a.setRangeValue(.{ .start = 0, .end = 1 }, true); + try testing.expectEqual(@as(usize, 1), a.count()); + try testing.expect(a.isSet(0)); + + a.setRangeValue(.{ .start = 0, .end = len }, false); + a.setRangeValue(.{ .start = 0, .end = len - 1 }, true); + try testing.expectEqual(len - 1, a.count()); + try testing.expect(!a.isSet(len - 1)); + + a.setRangeValue(.{ .start = 0, .end = len }, false); + a.setRangeValue(.{ .start = 1, .end = len }, true); + try testing.expectEqual(@as(usize, len - 1), a.count()); + try testing.expect(!a.isSet(0)); + + a.setRangeValue(.{ .start = 0, .end = len }, false); + a.setRangeValue(.{ .start = len - 1, .end = len }, true); + try testing.expectEqual(@as(usize, 1), a.count()); + try testing.expect(a.isSet(len - 1)); + + if (len >= 4) { + a.setRangeValue(.{ .start = 0, .end = len }, false); + a.setRangeValue(.{ .start = 1, .end = len - 2 }, true); + try testing.expectEqual(@as(usize, len - 3), a.count()); + try testing.expect(!a.isSet(0)); + try testing.expect(a.isSet(1)); + try testing.expect(a.isSet(len - 3)); + try testing.expect(!a.isSet(len - 2)); + try testing.expect(!a.isSet(len - 1)); + } + } } fn testStaticBitSet(comptime Set: type) !void { From 05cf69209e44c59f838f94ab355485d2d3a0432a Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Mon, 7 Feb 2022 18:20:33 +0100 Subject: [PATCH 0077/2031] debug: implement segfault handler for macOS aarch64 Tested on a M1 MacBook Pro, macOS Monterey 12.2. --- lib/std/c/darwin.zig | 65 +++++++++++------------------------- lib/std/c/darwin/aarch64.zig | 18 ++++++++++ lib/std/c/darwin/x86_64.zig | 30 +++++++++++++++++ lib/std/debug.zig | 20 ++++++++--- 4 files changed, 83 insertions(+), 50 deletions(-) create mode 100644 lib/std/c/darwin/aarch64.zig create mode 100644 lib/std/c/darwin/x86_64.zig diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index b10b582dc2..f4ca9cd6dd 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -6,6 +6,26 @@ const native_arch = builtin.target.cpu.arch; const maxInt = std.math.maxInt; const iovec_const = std.os.iovec_const; +const arch_bits = switch (native_arch) { + .aarch64 => @import("darwin/aarch64.zig"), + .x86_64 => @import("darwin/x86_64.zig"), + else => struct {}, +}; + +pub const ucontext_t = extern struct { + onstack: c_int, + sigmask: sigset_t, + stack: stack_t, + link: ?*ucontext_t, + mcsize: u64, + mcontext: *mcontext_t, +}; + +pub const mcontext_t = extern struct { + es: arch_bits.exception_state, + ss: arch_bits.thread_state, +}; + extern "c" fn __error() *c_int; pub extern "c" fn NSVersionOfRunTimeLibrary(library_name: [*:0]const u8) u32; pub extern "c" fn _NSGetExecutablePath(buf: [*:0]u8, bufsize: *u32) c_int; @@ -478,51 +498,6 @@ pub const SIG = struct { pub const USR2 = 31; }; -pub const ucontext_t = extern struct { - onstack: c_int, - sigmask: sigset_t, - stack: stack_t, - link: ?*ucontext_t, - mcsize: u64, - mcontext: *mcontext_t, -}; - -pub const exception_state = extern struct { - trapno: u16, - cpu: u16, - err: u32, - faultvaddr: u64, -}; - -pub const thread_state = extern struct { - rax: u64, - rbx: u64, - rcx: u64, - rdx: u64, - rdi: u64, - rsi: u64, - rbp: u64, - rsp: u64, - r8: u64, - r9: u64, - r10: u64, - r11: u64, - r12: u64, - r13: u64, - r14: u64, - r15: u64, - rip: u64, - rflags: u64, - cs: u64, - fs: u64, - gs: u64, -}; - -pub const mcontext_t = extern struct { - es: exception_state, - ss: thread_state, -}; - pub const siginfo_t = extern struct { signo: c_int, errno: c_int, diff --git a/lib/std/c/darwin/aarch64.zig b/lib/std/c/darwin/aarch64.zig new file mode 100644 index 0000000000..70153b5dfb --- /dev/null +++ b/lib/std/c/darwin/aarch64.zig @@ -0,0 +1,18 @@ +// See C headers in +// lib/libc/include/aarch64-macos.12-gnu/mach/arm/_structs.h + +pub const exception_state = extern struct { + far: u64, // Virtual Fault Address + esr: u32, // Exception syndrome + exception: u32, // Number of arm exception taken +}; + +pub const thread_state = extern struct { + regs: [29]u64, // General purpose registers + fp: u64, // Frame pointer x29 + lr: u64, // Link register x30 + sp: u64, // Stack pointer x31 + pc: u64, // Program counter + cpsr: u32, // Current program status register + __pad: u32, +}; diff --git a/lib/std/c/darwin/x86_64.zig b/lib/std/c/darwin/x86_64.zig new file mode 100644 index 0000000000..a7f2c509c7 --- /dev/null +++ b/lib/std/c/darwin/x86_64.zig @@ -0,0 +1,30 @@ +pub const exception_state = extern struct { + trapno: u16, + cpu: u16, + err: u32, + faultvaddr: u64, +}; + +pub const thread_state = extern struct { + rax: u64, + rbx: u64, + rcx: u64, + rdx: u64, + rdi: u64, + rsi: u64, + rbp: u64, + rsp: u64, + r8: u64, + r9: u64, + r10: u64, + r11: u64, + r12: u64, + r13: u64, + r14: u64, + r15: u64, + rip: u64, + rflags: u64, + cs: u64, + fs: u64, + gs: u64, +}; diff --git a/lib/std/debug.zig b/lib/std/debug.zig index a7bfda1cd2..69a68faad6 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1610,9 +1610,13 @@ fn getDebugInfoAllocator() mem.Allocator { /// Whether or not the current target can print useful debug information when a segfault occurs. pub const have_segfault_handling_support = switch (native_os) { - .linux, .netbsd, .solaris => true, - .macos => native_arch == .x86_64, - .windows => true, + .linux, + .macos, + .netbsd, + .solaris, + .windows, + => true, + .freebsd, .openbsd => @hasDecl(os.system, "ucontext_t"), else => false, }; @@ -1726,9 +1730,15 @@ fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any }, .aarch64 => { const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); - const ip = @intCast(usize, ctx.mcontext.pc); + const ip = switch (native_os) { + .macos => @intCast(usize, ctx.mcontext.ss.pc), + else => @intCast(usize, ctx.mcontext.pc), + }; // x29 is the ABI-designated frame pointer - const bp = @intCast(usize, ctx.mcontext.regs[29]); + const bp = switch (native_os) { + .macos => @intCast(usize, ctx.mcontext.ss.fp), + else => @intCast(usize, ctx.mcontext.regs[29]), + }; dumpStackTraceFromBase(bp, ip); }, else => {}, From 722d4a11bbba4052558f6f69b7e710d1206f3355 Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Fri, 4 Feb 2022 20:21:15 +0100 Subject: [PATCH 0078/2031] stage2: implement @sqrt for f{16,32,64} Support for f128, comptime_float, and c_longdouble require improvements to compiler_rt and will implemented in a later PR. Some of the code in this commit could be made more generic, for instance `llvm.airSqrt` could probably be `llvm.airUnaryMath`, but let's cross that bridge when we get to it. --- src/Air.zig | 6 +++ src/Liveness.zig | 1 + src/Sema.zig | 84 ++++++++++++++++++++++++++------ src/arch/aarch64/CodeGen.zig | 11 +++++ src/arch/arm/CodeGen.zig | 11 +++++ src/arch/riscv64/CodeGen.zig | 11 +++++ src/arch/wasm/CodeGen.zig | 2 + src/arch/x86_64/CodeGen.zig | 11 +++++ src/codegen/c.zig | 8 +++ src/codegen/llvm.zig | 16 ++++++ src/print_air.zig | 1 + src/value.zig | 22 +++++++++ test/behavior/floatop.zig | 42 ++++++++++++++++ test/behavior/floatop_stage1.zig | 34 ------------- test/behavior/math.zig | 12 ++--- 15 files changed, 217 insertions(+), 55 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 14f8f96d38..6888f51963 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -237,6 +237,10 @@ pub const Inst = struct { /// Uses the `ty_op` field. popcount, + /// Computes the square root of a floating point number. + /// Uses the `un_op` field. + sqrt, + /// `<`. Result type is always bool. /// Uses the `bin_op` field. cmp_lt, @@ -749,6 +753,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .max, => return air.typeOf(datas[inst].bin_op.lhs), + .sqrt => return air.typeOf(datas[inst].un_op), + .cmp_lt, .cmp_lte, .cmp_eq, diff --git a/src/Liveness.zig b/src/Liveness.zig index f07e438246..bed7de1507 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -338,6 +338,7 @@ fn analyzeInst( .ret_load, .tag_name, .error_name, + .sqrt, => { const operand = inst_datas[inst].un_op; return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none }); diff --git a/src/Sema.zig b/src/Sema.zig index 1dba136a48..b1772502bf 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -745,19 +745,19 @@ fn analyzeBodyInner( .clz => try sema.zirClzCtz(block, inst, .clz, Value.clz), .ctz => try sema.zirClzCtz(block, inst, .ctz, Value.ctz), - .sqrt => try sema.zirUnaryMath(block, inst), - .sin => try sema.zirUnaryMath(block, inst), - .cos => try sema.zirUnaryMath(block, inst), - .exp => try sema.zirUnaryMath(block, inst), - .exp2 => try sema.zirUnaryMath(block, inst), - .log => try sema.zirUnaryMath(block, inst), - .log2 => try sema.zirUnaryMath(block, inst), - .log10 => try sema.zirUnaryMath(block, inst), - .fabs => try sema.zirUnaryMath(block, inst), - .floor => try sema.zirUnaryMath(block, inst), - .ceil => try sema.zirUnaryMath(block, inst), - .trunc => try sema.zirUnaryMath(block, inst), - .round => try sema.zirUnaryMath(block, inst), + .sqrt => try sema.zirUnaryMath(block, inst, .sqrt), + .sin => try sema.zirUnaryMath(block, inst, .sin), + .cos => try sema.zirUnaryMath(block, inst, .cos), + .exp => try sema.zirUnaryMath(block, inst, .exp), + .exp2 => try sema.zirUnaryMath(block, inst, .exp2), + .log => try sema.zirUnaryMath(block, inst, .log), + .log2 => try sema.zirUnaryMath(block, inst, .log2), + .log10 => try sema.zirUnaryMath(block, inst, .log10), + .fabs => try sema.zirUnaryMath(block, inst, .fabs), + .floor => try sema.zirUnaryMath(block, inst, .floor), + .ceil => try sema.zirUnaryMath(block, inst, .ceil), + .trunc => try sema.zirUnaryMath(block, inst, .trunc), + .round => try sema.zirUnaryMath(block, inst, .round), .error_set_decl => try sema.zirErrorSetDecl(block, inst, .parent), .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon), @@ -11010,10 +11010,64 @@ fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A return block.addUnOp(.error_name, operand); } -fn zirUnaryMath(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { +fn zirUnaryMath( + sema: *Sema, + block: *Block, + inst: Zir.Inst.Index, + zir_tag: Zir.Inst.Tag, +) CompileError!Air.Inst.Ref { + const tracy = trace(@src()); + defer tracy.end(); + const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - return sema.fail(block, src, "TODO: Sema.zirUnaryMath", .{}); + const operand = sema.resolveInst(inst_data.operand); + const operand_ty = sema.typeOf(operand); + const operand_zig_ty_tag = operand_ty.zigTypeTag(); + + const is_float = operand_zig_ty_tag == .Float or operand_zig_ty_tag == .ComptimeFloat; + if (!is_float) { + return sema.fail(block, src, "expected float type, found '{s}'", .{@tagName(operand_zig_ty_tag)}); + } + + switch (zir_tag) { + .sqrt => { + switch (operand_ty.tag()) { + .f128, + .comptime_float, + .c_longdouble, + => |t| return sema.fail(block, src, "TODO implement @sqrt for type '{s}'", .{@tagName(t)}), + else => {}, + } + + const maybe_operand_val = try sema.resolveMaybeUndefVal(block, src, operand); + if (maybe_operand_val) |val| { + if (val.isUndef()) + return sema.addConstUndef(operand_ty); + const result_val = try val.sqrt(operand_ty, sema.arena); + return sema.addConstant(operand_ty, result_val); + } + + try sema.requireRuntimeBlock(block, src); + return block.addUnOp(.sqrt, operand); + }, + + .sin, + .cos, + .exp, + .exp2, + .log, + .log2, + .log10, + .fabs, + .floor, + .ceil, + .trunc, + .round, + => return sema.fail(block, src, "TODO: implement zirUnaryMath for ZIR tag '{s}'", .{@tagName(zir_tag)}), + + else => unreachable, + } } fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 6e8f88a2a7..d0413af02f 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -528,6 +528,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .max => try self.airMax(inst), .slice => try self.airSlice(inst), + .sqrt => try self.airUnaryMath(inst), + .add_with_overflow => try self.airAddWithOverflow(inst), .sub_with_overflow => try self.airSubWithOverflow(inst), .mul_with_overflow => try self.airMulWithOverflow(inst), @@ -1223,6 +1225,15 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { if (!self.liveness.operandDies(inst, op_index)) return false; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 1859ce874f..fb473ef412 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -520,6 +520,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .max => try self.airMax(inst), .slice => try self.airSlice(inst), + .sqrt => try self.airUnaryMath(inst), + .add_with_overflow => try self.airAddWithOverflow(inst), .sub_with_overflow => try self.airSubWithOverflow(inst), .mul_with_overflow => try self.airMulWithOverflow(inst), @@ -1377,6 +1379,15 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { if (!self.liveness.operandDies(inst, op_index)) return false; diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 612ff78bd6..ce5dc39bf8 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -507,6 +507,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .max => try self.airMax(inst), .slice => try self.airSlice(inst), + .sqrt => try self.airUnaryMath(inst), + .add_with_overflow => try self.airAddWithOverflow(inst), .sub_with_overflow => try self.airSubWithOverflow(inst), .mul_with_overflow => try self.airMulWithOverflow(inst), @@ -1166,6 +1168,15 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { if (!self.liveness.operandDies(inst, op_index)) return false; diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 8e0ffac76b..b1e7a0d3a1 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1681,6 +1681,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .unwrap_errunion_payload_ptr, .unwrap_errunion_err_ptr, + .sqrt, + .ptr_slice_len_ptr, .ptr_slice_ptr_ptr, .int_to_float, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index a60b8c78f0..10a6120556 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -599,6 +599,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .max => try self.airMax(inst), .slice => try self.airSlice(inst), + .sqrt => try self.airUnaryMath(inst), + .add_with_overflow => try self.airAddWithOverflow(inst), .sub_with_overflow => try self.airSubWithOverflow(inst), .mul_with_overflow => try self.airMulWithOverflow(inst), @@ -1578,6 +1580,15 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { if (!self.liveness.operandDies(inst, op_index)) return false; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 44b904f031..fc3848df21 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1446,6 +1446,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .mul_sat => try airSatOp(f, inst, "muls_"), .shl_sat => try airSatOp(f, inst, "shls_"), + .sqrt => try airSqrt(f, inst), + .add_with_overflow => try airAddWithOverflow(f, inst), .sub_with_overflow => try airSubWithOverflow(f, inst), .mul_with_overflow => try airMulWithOverflow(f, inst), @@ -3393,6 +3395,12 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { return CValue.none; } +fn airSqrt(f: *Function, inst: Air.Inst.Index) !CValue { + _ = f; + _ = inst; + return f.fail("TODO: C backend: implement sqrt", .{}); +} + fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { return switch (order) { .Unordered => "memory_order_relaxed", diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index abf371c85b..5a2c21eece 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2050,6 +2050,8 @@ pub const FuncGen = struct { .shr => try self.airShr(inst, false), .shr_exact => try self.airShr(inst, true), + .sqrt => try self.airSqrt(inst), + .cmp_eq => try self.airCmp(inst, .eq), .cmp_gt => try self.airCmp(inst, .gt), .cmp_gte => try self.airCmp(inst, .gte), @@ -4211,6 +4213,20 @@ pub const FuncGen = struct { } } + fn airSqrt(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) return null; + + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + const operand_ty = self.air.typeOf(un_op); + + const operand_llvm_ty = try self.dg.llvmType(operand_ty); + const fn_val = self.getIntrinsic("llvm.sqrt", &.{operand_llvm_ty}); + const params = [_]*const llvm.Value{operand}; + + return self.builder.buildCall(fn_val, ¶ms, params.len, .C, .Auto, ""); + } + fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, prefix: [*:0]const u8) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; diff --git a/src/print_air.zig b/src/print_air.zig index 6e1ed3f3d7..341e736b91 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -158,6 +158,7 @@ const Writer = struct { .ret_load, .tag_name, .error_name, + .sqrt, => try w.writeUnOp(s, inst), .breakpoint, diff --git a/src/value.zig b/src/value.zig index cc6827b0cc..4bb0a58aed 100644 --- a/src/value.zig +++ b/src/value.zig @@ -3265,6 +3265,28 @@ pub const Value = extern union { } } + pub fn sqrt(val: Value, float_type: Type, arena: Allocator) !Value { + switch (float_type.tag()) { + .f16 => { + const f = val.toFloat(f16); + return Value.Tag.float_16.create(arena, @sqrt(f)); + }, + .f32 => { + const f = val.toFloat(f32); + return Value.Tag.float_32.create(arena, @sqrt(f)); + }, + .f64 => { + const f = val.toFloat(f64); + return Value.Tag.float_64.create(arena, @sqrt(f)); + }, + + // TODO: implement @sqrt for these types + .f128, .comptime_float, .c_longdouble => unreachable, + + else => unreachable, + } + } + /// This type is not copyable since it may contain pointers to its inner data. pub const Payload = struct { tag: Tag, diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 20ef4ce68d..7807c690f6 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -72,3 +72,45 @@ test "negative f128 floatToInt at compile-time" { var b = @floatToInt(i64, a); try expect(@as(i64, -2) == b); } + +test "@sqrt" { + comptime try testSqrt(); + try testSqrt(); +} + +fn testSqrt() !void { + { + var a: f16 = 4; + try expect(@sqrt(a) == 2); + } + { + var a: f32 = 9; + try expect(@sqrt(a) == 3); + var b: f32 = 1.1; + try expect(math.approxEqAbs(f32, @sqrt(b), 1.0488088481701516, epsilon)); + } + { + var a: f64 = 25; + try expect(@sqrt(a) == 5); + } +} + +test "more @sqrt f16 tests" { + // TODO these are not all passing at comptime + try expect(@sqrt(@as(f16, 0.0)) == 0.0); + try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 2.0)), 1.414214, epsilon)); + try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 3.6)), 1.897367, epsilon)); + try expect(@sqrt(@as(f16, 4.0)) == 2.0); + try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 7.539840)), 2.745877, epsilon)); + try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 19.230934)), 4.385309, epsilon)); + try expect(@sqrt(@as(f16, 64.0)) == 8.0); + try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 64.1)), 8.006248, epsilon)); + try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 8942.230469)), 94.563370, epsilon)); + + // special cases + try expect(math.isPositiveInf(@sqrt(@as(f16, math.inf(f16))))); + try expect(@sqrt(@as(f16, 0.0)) == 0.0); + try expect(@sqrt(@as(f16, -0.0)) == -0.0); + try expect(math.isNan(@sqrt(@as(f16, -1.0)))); + try expect(math.isNan(@sqrt(@as(f16, math.nan(f16))))); +} diff --git a/test/behavior/floatop_stage1.zig b/test/behavior/floatop_stage1.zig index 303288a118..cd11f41b40 100644 --- a/test/behavior/floatop_stage1.zig +++ b/test/behavior/floatop_stage1.zig @@ -14,20 +14,6 @@ test "@sqrt" { } fn testSqrt() !void { - { - var a: f16 = 4; - try expect(@sqrt(a) == 2); - } - { - var a: f32 = 9; - try expect(@sqrt(a) == 3); - var b: f32 = 1.1; - try expect(math.approxEqAbs(f32, @sqrt(b), 1.0488088481701516, epsilon)); - } - { - var a: f64 = 25; - try expect(@sqrt(a) == 5); - } if (has_f80_rt) { var a: f80 = 25; try expect(@sqrt(a) == 5); @@ -51,26 +37,6 @@ fn testSqrt() !void { } } -test "more @sqrt f16 tests" { - // TODO these are not all passing at comptime - try expect(@sqrt(@as(f16, 0.0)) == 0.0); - try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 2.0)), 1.414214, epsilon)); - try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 3.6)), 1.897367, epsilon)); - try expect(@sqrt(@as(f16, 4.0)) == 2.0); - try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 7.539840)), 2.745877, epsilon)); - try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 19.230934)), 4.385309, epsilon)); - try expect(@sqrt(@as(f16, 64.0)) == 8.0); - try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 64.1)), 8.006248, epsilon)); - try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 8942.230469)), 94.563370, epsilon)); - - // special cases - try expect(math.isPositiveInf(@sqrt(@as(f16, math.inf(f16))))); - try expect(@sqrt(@as(f16, 0.0)) == 0.0); - try expect(@sqrt(@as(f16, -0.0)) == -0.0); - try expect(math.isNan(@sqrt(@as(f16, -1.0)))); - try expect(math.isNan(@sqrt(@as(f16, math.nan(f16))))); -} - test "@sin" { comptime try testSin(); try testSin(); diff --git a/test/behavior/math.zig b/test/behavior/math.zig index a1243eb7c1..c23e8ebe3e 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -792,8 +792,6 @@ fn remdiv(comptime T: type) !void { } test "@sqrt" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - try testSqrt(f64, 12.0); comptime try testSqrt(f64, 12.0); try testSqrt(f32, 13.0); @@ -801,10 +799,12 @@ test "@sqrt" { try testSqrt(f16, 13.0); comptime try testSqrt(f16, 13.0); - const x = 14.0; - const y = x * x; - const z = @sqrt(y); - comptime try expect(z == x); + if (builtin.zig_backend == .stage1) { + const x = 14.0; + const y = x * x; + const z = @sqrt(y); + comptime try expect(z == x); + } } fn testSqrt(comptime T: type, x: T) !void { From a028488384c599aa997ba04bbd5ed98f2172630c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 Feb 2022 16:48:37 -0700 Subject: [PATCH 0079/2031] Sema: clean up zirUnaryMath * pass air_tag instead of zir_tag * also pass eval function so that the branch only happens once and the body of zirUnaryMath is simplified * Value.sqrt: update to handle f80 and f128 in the normalized way that includes handling c_longdouble. Semi-related change: fix incorrect sqrt builtin name for f80 in stage1. --- src/Sema.zig | 81 +++++++++++++----------------------------- src/stage1/codegen.cpp | 2 +- src/value.zig | 28 ++++++++++----- 3 files changed, 44 insertions(+), 67 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index b1772502bf..72dfb4420b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -745,19 +745,19 @@ fn analyzeBodyInner( .clz => try sema.zirClzCtz(block, inst, .clz, Value.clz), .ctz => try sema.zirClzCtz(block, inst, .ctz, Value.ctz), - .sqrt => try sema.zirUnaryMath(block, inst, .sqrt), - .sin => try sema.zirUnaryMath(block, inst, .sin), - .cos => try sema.zirUnaryMath(block, inst, .cos), - .exp => try sema.zirUnaryMath(block, inst, .exp), - .exp2 => try sema.zirUnaryMath(block, inst, .exp2), - .log => try sema.zirUnaryMath(block, inst, .log), - .log2 => try sema.zirUnaryMath(block, inst, .log2), - .log10 => try sema.zirUnaryMath(block, inst, .log10), - .fabs => try sema.zirUnaryMath(block, inst, .fabs), - .floor => try sema.zirUnaryMath(block, inst, .floor), - .ceil => try sema.zirUnaryMath(block, inst, .ceil), - .trunc => try sema.zirUnaryMath(block, inst, .trunc), - .round => try sema.zirUnaryMath(block, inst, .round), + .sqrt => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt), + .sin => @panic("TODO"), + .cos => @panic("TODO"), + .exp => @panic("TODO"), + .exp2 => @panic("TODO"), + .log => @panic("TODO"), + .log2 => @panic("TODO"), + .log10 => @panic("TODO"), + .fabs => @panic("TODO"), + .floor => @panic("TODO"), + .ceil => @panic("TODO"), + .trunc => @panic("TODO"), + .round => @panic("TODO"), .error_set_decl => try sema.zirErrorSetDecl(block, inst, .parent), .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon), @@ -11014,60 +11014,27 @@ fn zirUnaryMath( sema: *Sema, block: *Block, inst: Zir.Inst.Index, - zir_tag: Zir.Inst.Tag, + air_tag: Air.Inst.Tag, + eval: fn (Value, Type, Allocator, std.Target) Allocator.Error!Value, ) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const src = inst_data.src(); const operand = sema.resolveInst(inst_data.operand); + const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_ty = sema.typeOf(operand); - const operand_zig_ty_tag = operand_ty.zigTypeTag(); + try sema.checkFloatType(block, operand_src, operand_ty); - const is_float = operand_zig_ty_tag == .Float or operand_zig_ty_tag == .ComptimeFloat; - if (!is_float) { - return sema.fail(block, src, "expected float type, found '{s}'", .{@tagName(operand_zig_ty_tag)}); + if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| { + if (operand_val.isUndef()) return sema.addConstUndef(operand_ty); + const target = sema.mod.getTarget(); + const result_val = try eval(operand_val, operand_ty, sema.arena, target); + return sema.addConstant(operand_ty, result_val); } - switch (zir_tag) { - .sqrt => { - switch (operand_ty.tag()) { - .f128, - .comptime_float, - .c_longdouble, - => |t| return sema.fail(block, src, "TODO implement @sqrt for type '{s}'", .{@tagName(t)}), - else => {}, - } - - const maybe_operand_val = try sema.resolveMaybeUndefVal(block, src, operand); - if (maybe_operand_val) |val| { - if (val.isUndef()) - return sema.addConstUndef(operand_ty); - const result_val = try val.sqrt(operand_ty, sema.arena); - return sema.addConstant(operand_ty, result_val); - } - - try sema.requireRuntimeBlock(block, src); - return block.addUnOp(.sqrt, operand); - }, - - .sin, - .cos, - .exp, - .exp2, - .log, - .log2, - .log10, - .fabs, - .floor, - .ceil, - .trunc, - .round, - => return sema.fail(block, src, "TODO: implement zirUnaryMath for ZIR tag '{s}'", .{@tagName(zir_tag)}), - - else => unreachable, - } + try sema.requireRuntimeBlock(block, operand_src); + return block.addUnOp(air_tag, operand); } fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 02f84beeab..c06f71e834 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -6996,7 +6996,7 @@ static LLVMValueRef ir_render_soft_f80_float_op(CodeGen *g, Stage1Air *executabl const char *func_name; switch (instruction->fn_id) { case BuiltinFnIdSqrt: - func_name = "__sqrt"; + func_name = "__sqrtx"; break; case BuiltinFnIdSin: func_name = "__sinx"; diff --git a/src/value.zig b/src/value.zig index 4bb0a58aed..23a04f2e5a 100644 --- a/src/value.zig +++ b/src/value.zig @@ -3265,24 +3265,34 @@ pub const Value = extern union { } } - pub fn sqrt(val: Value, float_type: Type, arena: Allocator) !Value { - switch (float_type.tag()) { - .f16 => { + pub fn sqrt(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + switch (float_type.floatBits(target)) { + 16 => { const f = val.toFloat(f16); return Value.Tag.float_16.create(arena, @sqrt(f)); }, - .f32 => { + 32 => { const f = val.toFloat(f32); return Value.Tag.float_32.create(arena, @sqrt(f)); }, - .f64 => { + 64 => { const f = val.toFloat(f64); return Value.Tag.float_64.create(arena, @sqrt(f)); }, - - // TODO: implement @sqrt for these types - .f128, .comptime_float, .c_longdouble => unreachable, - + 80 => { + if (true) { + @panic("TODO implement compiler_rt __sqrtx"); + } + const f = val.toFloat(f80); + return Value.Tag.float_80.create(arena, @sqrt(f)); + }, + 128 => { + if (true) { + @panic("TODO implement compiler_rt sqrtq"); + } + const f = val.toFloat(f128); + return Value.Tag.float_128.create(arena, @sqrt(f)); + }, else => unreachable, } } From a15d2d582b4015af0f72caa2b5f09b7e665e2c1e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 Feb 2022 17:11:26 -0800 Subject: [PATCH 0080/2031] stage2: fix crash_report segfault compile error Regressed in 05cf69209e44c59f838f94ab355485d2d3a0432a. --- src/crash_report.zig | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/crash_report.zig b/src/crash_report.zig index 724269556b..7e72c64800 100644 --- a/src/crash_report.zig +++ b/src/crash_report.zig @@ -4,6 +4,7 @@ const debug = std.debug; const os = std.os; const io = std.io; const print_zir = @import("print_zir.zig"); +const native_os = builtin.os.tag; const Module = @import("Module.zig"); const Sema = @import("Sema.zig"); @@ -233,9 +234,15 @@ fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any }, .aarch64 => ctx: { const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); - const ip = @intCast(usize, ctx.mcontext.pc); + const ip = switch (native_os) { + .macos => @intCast(usize, ctx.mcontext.ss.pc), + else => @intCast(usize, ctx.mcontext.pc), + }; // x29 is the ABI-designated frame pointer - const bp = @intCast(usize, ctx.mcontext.regs[29]); + const bp = switch (native_os) { + .macos => @intCast(usize, ctx.mcontext.ss.fp), + else => @intCast(usize, ctx.mcontext.regs[29]), + }; break :ctx StackContext{ .exception = .{ .bp = bp, .ip = ip } }; }, else => .not_supported, From fc59a0406157dbd0704cf9f05cd04b6a8c87d7df Mon Sep 17 00:00:00 2001 From: Jan Philipp Hafer Date: Mon, 7 Feb 2022 00:20:13 +0100 Subject: [PATCH 0081/2031] compiler_rt: add subo - approach by Hacker's Delight with wrapping subtraction - performance expected to be similar to addo - tests with all relevant combinations of min,max with -1,0,+1 and all combinations of sequences +-1,2,4..,max --- lib/std/special/compiler_rt.zig | 6 ++ lib/std/special/compiler_rt/addo.zig | 2 +- lib/std/special/compiler_rt/subo.zig | 38 +++++++++ lib/std/special/compiler_rt/subodi4_test.zig | 81 +++++++++++++++++++ lib/std/special/compiler_rt/subosi4_test.zig | 82 ++++++++++++++++++++ lib/std/special/compiler_rt/suboti4_test.zig | 81 +++++++++++++++++++ 6 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 lib/std/special/compiler_rt/subo.zig create mode 100644 lib/std/special/compiler_rt/subodi4_test.zig create mode 100644 lib/std/special/compiler_rt/subosi4_test.zig create mode 100644 lib/std/special/compiler_rt/suboti4_test.zig diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 286237aa7b..36f703464a 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -112,6 +112,12 @@ comptime { @export(__addodi4, .{ .name = "__addodi4", .linkage = linkage }); const __addoti4 = @import("compiler_rt/addo.zig").__addoti4; @export(__addoti4, .{ .name = "__addoti4", .linkage = linkage }); + const __subosi4 = @import("compiler_rt/subo.zig").__subosi4; + @export(__subosi4, .{ .name = "__subosi4", .linkage = linkage }); + const __subodi4 = @import("compiler_rt/subo.zig").__subodi4; + @export(__subodi4, .{ .name = "__subodi4", .linkage = linkage }); + const __suboti4 = @import("compiler_rt/subo.zig").__suboti4; + @export(__suboti4, .{ .name = "__suboti4", .linkage = linkage }); const __mulosi4 = @import("compiler_rt/mulo.zig").__mulosi4; @export(__mulosi4, .{ .name = "__mulosi4", .linkage = linkage }); const __mulodi4 = @import("compiler_rt/mulo.zig").__mulodi4; diff --git a/lib/std/special/compiler_rt/addo.zig b/lib/std/special/compiler_rt/addo.zig index 966c74cb8e..91ed15747c 100644 --- a/lib/std/special/compiler_rt/addo.zig +++ b/lib/std/special/compiler_rt/addo.zig @@ -15,7 +15,7 @@ inline fn addoXi4_generic(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST // and the sign of a+b+carry is the same as a (or equivalently b). // Slower routine: res = ~(a ^ b) & ((sum ^ a) // Faster routine: res = (sum ^ a) & (sum ^ b) - // Oerflow occured, iff (res < 0) + // Overflow occured, iff (res < 0) if (((sum ^ a) & (sum ^ b)) < 0) overflow.* = 1; return sum; diff --git a/lib/std/special/compiler_rt/subo.zig b/lib/std/special/compiler_rt/subo.zig new file mode 100644 index 0000000000..af28c6eead --- /dev/null +++ b/lib/std/special/compiler_rt/subo.zig @@ -0,0 +1,38 @@ +const builtin = @import("builtin"); + +// subo - subtract overflow +// * return a-%b. +// * return if a-b overflows => 1 else => 0 +// - suboXi4_generic as default + +inline fn suboXi4_generic(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST { + @setRuntimeSafety(builtin.is_test); + overflow.* = 0; + var sum: ST = a -% b; + // Hackers Delight: section Overflow Detection, subsection Signed Add/Subtract + // Let sum = a -% b == a - b - carry == wraparound subtraction. + // Overflow in a-b-carry occurs, iff a and b have opposite signs + // and the sign of a-b-carry is opposite of a (or equivalently same as b). + // Faster routine: res = (a ^ b) & (sum ^ a) + // Slower routine: res = (sum^a) & ~(sum^b) + // Overflow occured, iff (res < 0) + if (((a ^ b) & (sum ^ a)) < 0) + overflow.* = 1; + return sum; +} + +pub fn __subosi4(a: i32, b: i32, overflow: *c_int) callconv(.C) i32 { + return suboXi4_generic(i32, a, b, overflow); +} +pub fn __subodi4(a: i64, b: i64, overflow: *c_int) callconv(.C) i64 { + return suboXi4_generic(i64, a, b, overflow); +} +pub fn __suboti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 { + return suboXi4_generic(i128, a, b, overflow); +} + +test { + _ = @import("subosi4_test.zig"); + _ = @import("subodi4_test.zig"); + _ = @import("suboti4_test.zig"); +} diff --git a/lib/std/special/compiler_rt/subodi4_test.zig b/lib/std/special/compiler_rt/subodi4_test.zig new file mode 100644 index 0000000000..687e97c71c --- /dev/null +++ b/lib/std/special/compiler_rt/subodi4_test.zig @@ -0,0 +1,81 @@ +const subo = @import("subo.zig"); +const std = @import("std"); +const testing = std.testing; +const math = std.math; + +fn test__subodi4(a: i64, b: i64) !void { + var result_ov: c_int = undefined; + var expected_ov: c_int = undefined; + var result = subo.__subodi4(a, b, &result_ov); + var expected: i64 = simple_subodi4(a, b, &expected_ov); + try testing.expectEqual(expected, result); + try testing.expectEqual(expected_ov, result_ov); +} + +// 2 cases on evaluating `a-b`: +// 1. `a-b` may underflow, iff b>0 && a<0 and a-b < min <=> a0 and a-b > max <=> a>max+b +// `-b` evaluation may overflow, iff b==min, but this is handled by the hardware +pub fn simple_subodi4(a: i64, b: i64, overflow: *c_int) i64 { + overflow.* = 0; + const min: i64 = math.minInt(i64); + const max: i64 = math.maxInt(i64); + if (((b > 0) and (a < min + b)) or + ((b < 0) and (a > max + b))) + overflow.* = 1; + return a -% b; +} + +test "subodi3" { + const min: i64 = math.minInt(i64); + const max: i64 = math.maxInt(i64); + var i: i64 = 1; + while (i < max) : (i *|= 2) { + try test__subodi4(i, i); + try test__subodi4(-i, -i); + try test__subodi4(i, -i); + try test__subodi4(-i, i); + } + + // edge cases + // 0 - 0 = 0 + // MIN - MIN = 0 + // MAX - MAX = 0 + // 0 - MIN overflow + // 0 - MAX = MIN+1 + // MIN - 0 = MIN + // MAX - 0 = MAX + // MIN - MAX overflow + // MAX - MIN overflow + try test__subodi4(0, 0); + try test__subodi4(min, min); + try test__subodi4(max, max); + try test__subodi4(0, min); + try test__subodi4(0, max); + try test__subodi4(min, 0); + try test__subodi4(max, 0); + try test__subodi4(min, max); + try test__subodi4(max, min); + + // derived edge cases + // MIN+1 - MIN = 1 + // MAX-1 - MAX = -1 + // 1 - MIN overflow + // -1 - MIN = MAX + // -1 - MAX = MIN + // +1 - MAX = MIN+2 + // MIN - 1 overflow + // MIN - -1 = MIN+1 + // MAX - 1 = MAX-1 + // MAX - -1 overflow + try test__subodi4(min + 1, min); + try test__subodi4(max - 1, max); + try test__subodi4(1, min); + try test__subodi4(-1, min); + try test__subodi4(-1, max); + try test__subodi4(1, max); + try test__subodi4(min, 1); + try test__subodi4(min, -1); + try test__subodi4(max, -1); + try test__subodi4(max, 1); +} diff --git a/lib/std/special/compiler_rt/subosi4_test.zig b/lib/std/special/compiler_rt/subosi4_test.zig new file mode 100644 index 0000000000..6c7ae97c25 --- /dev/null +++ b/lib/std/special/compiler_rt/subosi4_test.zig @@ -0,0 +1,82 @@ +const subo = @import("subo.zig"); +const testing = @import("std").testing; + +fn test__subosi4(a: i32, b: i32) !void { + var result_ov: c_int = undefined; + var expected_ov: c_int = undefined; + var result = subo.__subosi4(a, b, &result_ov); + var expected: i32 = simple_subosi4(a, b, &expected_ov); + try testing.expectEqual(expected, result); + try testing.expectEqual(expected_ov, result_ov); +} + +// 2 cases on evaluating `a-b`: +// 1. `a-b` may underflow, iff b>0 && a<0 and a-b < min <=> a0 and a-b > max <=> a>max+b +// `-b` evaluation may overflow, iff b==min, but this is handled by the hardware +pub fn simple_subosi4(a: i32, b: i32, overflow: *c_int) i32 { + overflow.* = 0; + const min: i32 = -2147483648; + const max: i32 = 2147483647; + if (((b > 0) and (a < min + b)) or + ((b < 0) and (a > max + b))) + overflow.* = 1; + return a -% b; +} + +test "subosi3" { + // -2^31 <= i32 <= 2^31-1 + // 2^31 = 2147483648 + // 2^31-1 = 2147483647 + const min: i32 = -2147483648; + const max: i32 = 2147483647; + var i: i32 = 1; + while (i < max) : (i *|= 2) { + try test__subosi4(i, i); + try test__subosi4(-i, -i); + try test__subosi4(i, -i); + try test__subosi4(-i, i); + } + + // edge cases + // 0 - 0 = 0 + // MIN - MIN = 0 + // MAX - MAX = 0 + // 0 - MIN overflow + // 0 - MAX = MIN+1 + // MIN - 0 = MIN + // MAX - 0 = MAX + // MIN - MAX overflow + // MAX - MIN overflow + try test__subosi4(0, 0); + try test__subosi4(min, min); + try test__subosi4(max, max); + try test__subosi4(0, min); + try test__subosi4(0, max); + try test__subosi4(min, 0); + try test__subosi4(max, 0); + try test__subosi4(min, max); + try test__subosi4(max, min); + + // derived edge cases + // MIN+1 - MIN = 1 + // MAX-1 - MAX = -1 + // 1 - MIN overflow + // -1 - MIN = MAX + // -1 - MAX = MIN + // +1 - MAX = MIN+2 + // MIN - 1 overflow + // MIN - -1 = MIN+1 + // MAX - 1 = MAX-1 + // MAX - -1 overflow + try test__subosi4(min + 1, min); + try test__subosi4(max - 1, max); + try test__subosi4(1, min); + try test__subosi4(-1, min); + try test__subosi4(-1, max); + try test__subosi4(1, max); + try test__subosi4(min, 1); + try test__subosi4(min, -1); + try test__subosi4(max, -1); + try test__subosi4(max, 1); +} diff --git a/lib/std/special/compiler_rt/suboti4_test.zig b/lib/std/special/compiler_rt/suboti4_test.zig new file mode 100644 index 0000000000..f42fe3edce --- /dev/null +++ b/lib/std/special/compiler_rt/suboti4_test.zig @@ -0,0 +1,81 @@ +const subo = @import("subo.zig"); +const std = @import("std"); +const testing = std.testing; +const math = std.math; + +fn test__suboti4(a: i128, b: i128) !void { + var result_ov: c_int = undefined; + var expected_ov: c_int = undefined; + var result = subo.__suboti4(a, b, &result_ov); + var expected: i128 = simple_suboti4(a, b, &expected_ov); + try testing.expectEqual(expected, result); + try testing.expectEqual(expected_ov, result_ov); +} + +// 2 cases on evaluating `a-b`: +// 1. `a-b` may underflow, iff b>0 && a<0 and a-b < min <=> a0 and a-b > max <=> a>max+b +// `-b` evaluation may overflow, iff b==min, but this is handled by the hardware +pub fn simple_suboti4(a: i128, b: i128, overflow: *c_int) i128 { + overflow.* = 0; + const min: i128 = math.minInt(i128); + const max: i128 = math.maxInt(i128); + if (((b > 0) and (a < min + b)) or + ((b < 0) and (a > max + b))) + overflow.* = 1; + return a -% b; +} + +test "suboti3" { + const min: i128 = math.minInt(i128); + const max: i128 = math.maxInt(i128); + var i: i128 = 1; + while (i < max) : (i *|= 2) { + try test__suboti4(i, i); + try test__suboti4(-i, -i); + try test__suboti4(i, -i); + try test__suboti4(-i, i); + } + + // edge cases + // 0 - 0 = 0 + // MIN - MIN = 0 + // MAX - MAX = 0 + // 0 - MIN overflow + // 0 - MAX = MIN+1 + // MIN - 0 = MIN + // MAX - 0 = MAX + // MIN - MAX overflow + // MAX - MIN overflow + try test__suboti4(0, 0); + try test__suboti4(min, min); + try test__suboti4(max, max); + try test__suboti4(0, min); + try test__suboti4(0, max); + try test__suboti4(min, 0); + try test__suboti4(max, 0); + try test__suboti4(min, max); + try test__suboti4(max, min); + + // derived edge cases + // MIN+1 - MIN = 1 + // MAX-1 - MAX = -1 + // 1 - MIN overflow + // -1 - MIN = MAX + // -1 - MAX = MIN + // +1 - MAX = MIN+2 + // MIN - 1 overflow + // MIN - -1 = MIN+1 + // MAX - 1 = MAX-1 + // MAX - -1 overflow + try test__suboti4(min + 1, min); + try test__suboti4(max - 1, max); + try test__suboti4(1, min); + try test__suboti4(-1, min); + try test__suboti4(-1, max); + try test__suboti4(1, max); + try test__suboti4(min, 1); + try test__suboti4(min, -1); + try test__suboti4(max, -1); + try test__suboti4(max, 1); +} From f50203c83667ed3ad0c57fdc953322a5f9c221ac Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 6 Feb 2022 12:49:42 +0100 Subject: [PATCH 0082/2031] wasm: update test runner This updates the test runner for stage2 to emit to stdout with the passed, skipped and failed tests similar to the LLVM backend. Another change to this is the start function, as it's now more in line with stage1's. The stage2 test infrastructure for wasm/wasi has been updated to reflect this as well. --- lib/std/special/test_runner.zig | 2 +- lib/std/start.zig | 8 +- src/arch/wasm/CodeGen.zig | 2 +- src/link/Wasm.zig | 16 ++- src/link/Wasm/Symbol.zig | 5 +- test/stage2/wasm.zig | 225 ++++++++++++++++---------------- 6 files changed, 128 insertions(+), 130 deletions(-) diff --git a/lib/std/special/test_runner.zig b/lib/std/special/test_runner.zig index fe20e10141..9848cb5a3e 100644 --- a/lib/std/special/test_runner.zig +++ b/lib/std/special/test_runner.zig @@ -144,7 +144,7 @@ pub fn main2() anyerror!void { } }; } - if (builtin.zig_backend == .stage2_llvm) { + if (builtin.zig_backend == .stage2_llvm or builtin.zig_backend == .stage2_wasm) { const passed = builtin.test_functions.len - skipped - failed; const stderr = std.io.getStdErr(); writeInt(stderr, passed) catch {}; diff --git a/lib/std/start.zig b/lib/std/start.zig index a3cc3d00a8..6e28ca61a3 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -31,7 +31,7 @@ comptime { } else if (builtin.os.tag == .windows) { @export(wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" }); } else if (builtin.os.tag == .wasi and @hasDecl(root, "main")) { - @export(wasmMain2, .{ .name = "_start" }); + @export(wasiMain2, .{ .name = "_start" }); } else { if (!@hasDecl(root, "_start")) { @export(_start2, .{ .name = "_start" }); @@ -100,17 +100,17 @@ fn callMain2() noreturn { exit2(0); } -fn wasmMain2() u8 { +fn wasiMain2() noreturn { switch (@typeInfo(@typeInfo(@TypeOf(root.main)).Fn.return_type.?)) { .Void => { root.main(); - return 0; + std.os.wasi.proc_exit(0); }, .Int => |info| { if (info.bits != 8 or info.signedness == .signed) { @compileError(bad_main_ret); } - return root.main(); + std.os.wasi.proc_exit(root.main()); }, else => @compileError("Bad return type main"), } diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index b1e7a0d3a1..9f0736f055 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -954,7 +954,7 @@ pub const DeclGen = struct { } else if (decl.val.castTag(.extern_fn)) |extern_fn| { const ext_decl = extern_fn.data.owner_decl; var func_type = try genFunctype(self.gpa, ext_decl.ty, self.target()); - func_type.deinit(self.gpa); + defer func_type.deinit(self.gpa); ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); return Result{ .appended = {} }; } else { diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index e6988e9232..d62f3a4201 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -260,7 +260,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { if (build_options.have_llvm) { if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl); } - if (!decl.ty.hasRuntimeBits()) return; + assert(decl.link.wasm.sym_index != 0); // Must call allocateDeclIndexes() decl.link.wasm.clear(); @@ -297,8 +297,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void { if (decl.isExtern()) { - try self.addOrUpdateImport(decl); - return; + return self.addOrUpdateImport(decl); } if (code.len == 0) return; @@ -407,16 +406,18 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { self.symbols.items[atom.sym_index].tag = .dead; // to ensure it does not end in the names section for (atom.locals.items) |local_atom| { self.symbols.items[local_atom.sym_index].tag = .dead; // also for any local symbol + self.symbols_free_list.append(self.base.allocator, local_atom.sym_index) catch {}; } - atom.deinit(self.base.allocator); if (decl.isExtern()) { - const import = self.imports.fetchRemove(decl.link.wasm.sym_index).?.value; + const import = self.imports.fetchRemove(atom.sym_index).?.value; switch (import.kind) { .function => self.imported_functions_count -= 1, else => unreachable, } } + + atom.deinit(self.base.allocator); } /// Appends a new entry to the indirect function table @@ -441,10 +442,13 @@ fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void { switch (decl.ty.zigTypeTag()) { .Fn => { const gop = try self.imports.getOrPut(self.base.allocator, symbol_index); + const module_name = if (decl.getExternFn().?.lib_name) |lib_name| blk: { + break :blk std.mem.sliceTo(lib_name, 0); + } else self.host_name; if (!gop.found_existing) { self.imported_functions_count += 1; gop.value_ptr.* = .{ - .module_name = self.host_name, + .module_name = module_name, .name = std.mem.span(symbol.name), .kind = .{ .function = decl.fn_link.wasm.type_index }, }; diff --git a/src/link/Wasm/Symbol.zig b/src/link/Wasm/Symbol.zig index 0f2247f5d1..53d96d6382 100644 --- a/src/link/Wasm/Symbol.zig +++ b/src/link/Wasm/Symbol.zig @@ -142,19 +142,20 @@ pub fn format(self: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOpt _ = fmt; _ = options; - const kind_fmt: u8 = switch (self.kind) { + const kind_fmt: u8 = switch (self.tag) { .function => 'F', .data => 'D', .global => 'G', .section => 'S', .event => 'E', .table => 'T', + .dead => '-', }; const visible: []const u8 = if (self.isVisible()) "yes" else "no"; const binding: []const u8 = if (self.isLocal()) "local" else "global"; try writer.print( "{c} binding={s} visible={s} id={d} name={s}", - .{ kind_fmt, binding, visible, self.index(), self.name }, + .{ kind_fmt, binding, visible, self.index, self.name }, ); } diff --git a/test/stage2/wasm.zig b/test/stage2/wasm.zig index 20ff65e0a7..7c7e203b40 100644 --- a/test/stage2/wasm.zig +++ b/test/stage2/wasm.zig @@ -11,37 +11,31 @@ pub fn addCases(ctx: *TestContext) !void { var case = ctx.exe("wasm function calls", wasi); case.addCompareOutput( - \\pub fn main() u8 { + \\pub fn main() void { \\ foo(); \\ bar(); - \\ return 42; \\} \\fn foo() void { \\ bar(); \\ bar(); \\} \\fn bar() void {} - , - "42\n", - ); + , ""); case.addCompareOutput( - \\pub fn main() u8 { + \\pub fn main() void { \\ bar(); \\ foo(); \\ foo(); \\ bar(); \\ foo(); \\ bar(); - \\ return 42; \\} \\fn foo() void { \\ bar(); \\} \\fn bar() void {} - , - "42\n", - ); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -56,23 +50,22 @@ pub fn addCases(ctx: *TestContext) !void { \\} \\fn bar() void {} , - "0\n", + "", ); case.addCompareOutput( - \\pub fn main() u8 { + \\pub fn main() void { \\ foo(10, 20); - \\ return 5; \\} \\fn foo(x: u8, y: u8) void { _ = x; _ = y; } - , "5\n"); + , ""); } { var case = ctx.exe("wasm locals", wasi); case.addCompareOutput( - \\pub fn main() u8 { + \\pub fn main() void { \\ var i: u8 = 5; \\ var y: f32 = 42.0; \\ var x: u8 = 10; @@ -80,38 +73,38 @@ pub fn addCases(ctx: *TestContext) !void { \\ y; \\ x; \\ } - \\ return i; + \\ if (i != 5) unreachable; \\} - , "5\n"); + , ""); case.addCompareOutput( - \\pub fn main() u8 { + \\pub fn main() void { \\ var i: u8 = 5; \\ var y: f32 = 42.0; \\ _ = y; \\ var x: u8 = 10; \\ foo(i, x); \\ i = x; - \\ return i; + \\ if (i != 10) unreachable; \\} \\fn foo(x: u8, y: u8) void { \\ _ = y; \\ var i: u8 = 10; \\ i = x; \\} - , "10\n"); + , ""); } { var case = ctx.exe("wasm binary operands", wasi); case.addCompareOutput( - \\pub fn main() u8 { + \\pub fn main() void { \\ var i: u8 = 5; \\ i += 20; - \\ return i; + \\ if (i != 25) unreachable; \\} - , "25\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -119,7 +112,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (i +% 1 != -2147483648) unreachable; \\ return; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -127,34 +120,34 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (i +% 1 != 0) unreachable; \\ return; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { \\ var i: u8 = 255; \\ return i +% 1; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { \\ var i: u8 = 5; \\ i += 20; \\ var result: u8 = foo(i, 10); - \\ return result; + \\ return result - 35; \\} \\fn foo(x: u8, y: u8) u8 { \\ return x + y; \\} - , "35\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { \\ var i: u8 = 20; \\ i -= 5; - \\ return i; + \\ return i - 15; \\} - , "15\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -162,7 +155,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (i -% 1 != 2147483647) unreachable; \\ return; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -170,26 +163,26 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (i -% 1 != 63) unreachable; \\ return; \\} - , "0\n"); + , ""); case.addCompareOutput( - \\pub fn main() u8 { + \\pub fn main() void { \\ var i: u4 = 0; - \\ return i -% 1; + \\ if(i -% 1 != 15) unreachable; \\} - , "15\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { \\ var i: u8 = 5; \\ i -= 3; \\ var result: u8 = foo(i, 10); - \\ return result; + \\ return result - 8; \\} \\fn foo(x: u8, y: u8) u8 { \\ return y - x; \\} - , "8\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -202,7 +195,7 @@ pub fn addCases(ctx: *TestContext) !void { \\fn foo(x: u32, y: u32) u32 { \\ return x * y; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -211,7 +204,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (result != -2) unreachable; \\ return; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -219,7 +212,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (i *% 3 != 1) unreachable; \\ return; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -227,7 +220,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (i *% 3 != 1) unreachable; \\ return; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -240,31 +233,31 @@ pub fn addCases(ctx: *TestContext) !void { \\fn foo(x: u32, y: u32) u32 { \\ return x / y; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { \\ var i: u8 = 5; \\ i &= 6; - \\ return i; + \\ return i - 4; \\} - , "4\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { \\ var i: u8 = 5; \\ i |= 6; - \\ return i; + \\ return i - 7; \\} - , "7\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { \\ var i: u8 = 5; \\ i ^= 6; - \\ return i; + \\ return i - 3; \\} - , "3\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -273,7 +266,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (b) unreachable; \\ return; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -282,7 +275,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (!b) unreachable; \\ return; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -291,7 +284,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (!b) unreachable; \\ return; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -300,7 +293,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (!b) unreachable; \\ return; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -309,7 +302,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (b) unreachable; \\ return; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -318,7 +311,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (b) unreachable; \\ return; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -327,7 +320,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (b) unreachable; \\ return; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -336,7 +329,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (!b) unreachable; \\ return; \\} - , "0\n"); + , ""); } { @@ -348,9 +341,9 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (i > @as(u8, 4)) { \\ i += 10; \\ } - \\ return i; + \\ return i - 15; \\} - , "15\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { @@ -360,9 +353,9 @@ pub fn addCases(ctx: *TestContext) !void { \\ } else { \\ i = 2; \\ } - \\ return i; + \\ return i - 2; \\} - , "2\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { @@ -372,9 +365,9 @@ pub fn addCases(ctx: *TestContext) !void { \\ } else if(i == @as(u8, 5)) { \\ i = 20; \\ } - \\ return i; + \\ return i - 20; \\} - , "20\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { @@ -388,9 +381,9 @@ pub fn addCases(ctx: *TestContext) !void { \\ i = 20; \\ } \\ } - \\ return i; + \\ return i - 31; \\} - , "31\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -405,7 +398,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ const x = if(ok) @as(i32, 20) else @as(i32, 10); \\ return x; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() void { @@ -425,7 +418,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ }; \\ return val + 10; \\} - , "0\n"); + , ""); } { @@ -438,9 +431,9 @@ pub fn addCases(ctx: *TestContext) !void { \\ i += 1; \\ } \\ - \\ return i; + \\ return i - 5; \\} - , "5\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { @@ -449,9 +442,9 @@ pub fn addCases(ctx: *TestContext) !void { \\ var x: u8 = 1; \\ i += x; \\ } - \\ return i; + \\ return i - 10; \\} - , "10\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { @@ -461,9 +454,9 @@ pub fn addCases(ctx: *TestContext) !void { \\ i += x; \\ if (i == @as(u8, 5)) break; \\ } - \\ return i; + \\ return i - 5; \\} - , "5\n"); + , ""); } { @@ -485,7 +478,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ } \\ return; \\} - , "0\n"); + , ""); case.addCompareOutput( \\const Number = enum { One, Two, Three }; @@ -507,7 +500,7 @@ pub fn addCases(ctx: *TestContext) !void { \\fn assert(val: bool) void { \\ if(!val) unreachable; \\} - , "0\n"); + , ""); } { @@ -518,9 +511,9 @@ pub fn addCases(ctx: *TestContext) !void { \\ \\pub fn main() u8 { \\ var example: Example = .{ .x = 5 }; - \\ return example.x; + \\ return example.x - 5; \\} - , "5\n"); + , ""); case.addCompareOutput( \\const Example = struct { x: u8 }; @@ -528,18 +521,18 @@ pub fn addCases(ctx: *TestContext) !void { \\pub fn main() u8 { \\ var example: Example = .{ .x = 5 }; \\ example.x = 10; - \\ return example.x; + \\ return example.x - 10; \\} - , "10\n"); + , ""); case.addCompareOutput( \\const Example = struct { x: u8, y: u8 }; \\ \\pub fn main() u8 { \\ var example: Example = .{ .x = 5, .y = 10 }; - \\ return example.y + example.x; + \\ return example.y + example.x - 15; \\} - , "15\n"); + , ""); case.addCompareOutput( \\const Example = struct { x: u8, y: u8 }; @@ -549,9 +542,9 @@ pub fn addCases(ctx: *TestContext) !void { \\ var example2: Example = .{ .x = 10, .y = 20 }; \\ \\ example = example2; - \\ return example.y + example.x; + \\ return example.y + example.x - 30; \\} - , "30\n"); + , ""); case.addCompareOutput( \\const Example = struct { x: u8, y: u8 }; @@ -560,9 +553,9 @@ pub fn addCases(ctx: *TestContext) !void { \\ var example: Example = .{ .x = 5, .y = 10 }; \\ \\ example = .{ .x = 10, .y = 20 }; - \\ return example.y + example.x; + \\ return example.y + example.x - 30; \\} - , "30\n"); + , ""); } { @@ -578,9 +571,9 @@ pub fn addCases(ctx: *TestContext) !void { \\ else => 5, \\ }; \\ - \\ return a; + \\ return a - 2; \\} - , "2\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { @@ -592,9 +585,9 @@ pub fn addCases(ctx: *TestContext) !void { \\ else => 5, \\ }; \\ - \\ return a; + \\ return a - 3; \\} - , "3\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { @@ -606,9 +599,9 @@ pub fn addCases(ctx: *TestContext) !void { \\ else => 5, \\ }; \\ - \\ return a; + \\ return a - 5; \\} - , "5\n"); + , ""); case.addCompareOutput( \\const MyEnum = enum { One, Two, Three }; @@ -621,9 +614,9 @@ pub fn addCases(ctx: *TestContext) !void { \\ .Three => 3, \\ }; \\ - \\ return a; + \\ return a - 2; \\} - , "2\n"); + , ""); } { @@ -641,35 +634,35 @@ pub fn addCases(ctx: *TestContext) !void { \\fn assert(b: bool) void { \\ if (!b) unreachable; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { \\ var e: anyerror!u8 = 5; \\ const i = e catch 10; - \\ return i; + \\ return i - 5; \\} - , "5\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { \\ var e: anyerror!u8 = error.Foo; \\ const i = e catch 10; - \\ return i; + \\ return i - 10; \\} - , "10\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { \\ var e = foo(); \\ const i = e catch 69; - \\ return i; + \\ return i - 5; \\} \\ \\fn foo() anyerror!u8 { \\ return 5; \\} - , "5\n"); + , ""); } { @@ -679,24 +672,24 @@ pub fn addCases(ctx: *TestContext) !void { \\pub fn main() u8 { \\ var e = foo(); \\ const i = e catch 69; - \\ return i; + \\ return i - 69; \\} \\ \\fn foo() anyerror!u8 { \\ return error.Bruh; \\} - , "69\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { \\ var e = foo(); \\ const i = e catch 42; - \\ return i; + \\ return i - 42; \\} \\ \\fn foo() anyerror!u8 { \\ return error.Dab; \\} - , "42\n"); + , ""); } { @@ -709,7 +702,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ _ = y; \\ return; \\} - , "0\n"); + , ""); } { @@ -722,9 +715,9 @@ pub fn addCases(ctx: *TestContext) !void { \\ if (x) |val| { \\ y = val; \\ } - \\ return y; + \\ return y - 5; \\} - , "5\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { @@ -735,22 +728,22 @@ pub fn addCases(ctx: *TestContext) !void { \\ } \\ return y; \\} - , "0\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { \\ var x: ?u8 = 5; - \\ return x.?; + \\ return x.? - 5; \\} - , "5\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { \\ var x: u8 = 5; \\ var y: ?u8 = x; - \\ return y.?; + \\ return y.? - 5; \\} - , "5\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { @@ -763,7 +756,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ } \\ return 0; \\} - , "0\n"); + , ""); } { @@ -774,13 +767,13 @@ pub fn addCases(ctx: *TestContext) !void { \\ var x: u8 = 0; \\ \\ foo(&x); - \\ return x; + \\ return x - 2; \\} \\ \\fn foo(x: *u8)void { \\ x.* = 2; \\} - , "2\n"); + , ""); case.addCompareOutput( \\pub fn main() u8 { @@ -788,7 +781,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ \\ foo(&x); \\ bar(&x); - \\ return x; + \\ return x - 4; \\} \\ \\fn foo(x: *u8)void { @@ -798,6 +791,6 @@ pub fn addCases(ctx: *TestContext) !void { \\fn bar(x: *u8) void { \\ x.* += 2; \\} - , "4\n"); + , ""); } } From 9981b3fd2f7ab85146efa9feebe08a795411d131 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 8 Feb 2022 18:05:54 +0100 Subject: [PATCH 0083/2031] stage2: tiny improvements all over the place * pass more x64 behavior tests * return with a TODO error when lowering a decl with no runtime bits * insert some debug logs for tracing recursive descent down the type-value tree when lowering types * print `Decl`'s name when print debugging `decl_ref` value --- src/arch/wasm/CodeGen.zig | 2 ++ src/arch/x86_64/CodeGen.zig | 11 +++++++---- src/codegen.zig | 14 ++++++++++++++ src/value.zig | 5 ++++- test/behavior/bugs/1025.zig | 1 - test/behavior/bugs/1277.zig | 1 - test/behavior/bugs/1310.zig | 1 - test/behavior/bugs/1500.zig | 1 - 8 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 9f0736f055..0361d4ffdd 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -970,6 +970,8 @@ pub const DeclGen = struct { /// Generates the wasm bytecode for the declaration belonging to `Context` fn genTypedValue(self: *DeclGen, ty: Type, val: Value) InnerError!Result { + log.debug("genTypedValue: ty = {}, val = {}", .{ ty, val }); + const writer = self.code.writer(); if (val.isUndef()) { try writer.writeByteNTimes(0xaa, @intCast(usize, ty.abiSize(self.target()))); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index a745a6df33..9abeadad5d 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1973,6 +1973,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde if (self.liveness.isUnused(inst)) { return MCValue.dead; } + const mcv = try self.resolveInst(operand); const ptr_ty = self.air.typeOf(operand); const struct_ty = ptr_ty.childType(); @@ -2190,6 +2191,7 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: } fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { + const abi_size = dst_ty.abiSize(self.target.*); switch (dst_mcv) { .none => unreachable, .undef => unreachable, @@ -2216,7 +2218,6 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC }); }, .immediate => |imm| { - const abi_size = dst_ty.abiSize(self.target.*); _ = try self.addInst(.{ .tag = mir_tag, .ops = (Mir.Ops{ @@ -2226,7 +2227,11 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC }); }, .embedded_in_code, .memory => { - return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{}); + assert(abi_size <= 8); + self.register_manager.freezeRegs(&.{dst_reg}); + defer self.register_manager.unfreezeRegs(&.{dst_reg}); + const reg = try self.copyToTmpRegister(dst_ty, src_mcv); + return self.genBinMathOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); }, .got_load, .direct_load => { return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{}); @@ -2235,7 +2240,6 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC if (off > math.maxInt(i32)) { return self.fail("stack offset too large", .{}); } - const abi_size = dst_ty.abiSize(self.target.*); const adj_off = off + @intCast(i32, abi_size); _ = try self.addInst(.{ .tag = mir_tag, @@ -2259,7 +2263,6 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC if (off > math.maxInt(i32)) { return self.fail("stack offset too large", .{}); } - const abi_size = dst_ty.abiSize(self.target.*); if (abi_size > 8) { return self.fail("TODO implement ADD/SUB/CMP for stack dst with large ABI", .{}); } diff --git a/src/codegen.zig b/src/codegen.zig index 059d2adc14..5873fd439c 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -150,6 +150,8 @@ pub fn generateSymbol( const tracy = trace(@src()); defer tracy.end(); + log.debug("generateSymbol: ty = {}, val = {}", .{ typed_value.ty, typed_value.val }); + if (typed_value.val.isUndefDeep()) { const target = bin_file.options.target; const abi_size = try math.cast(usize, typed_value.ty.abiSize(target)); @@ -485,6 +487,18 @@ fn lowerDeclRef( return Result{ .appended = {} }; } + const is_fn_body = decl.ty.zigTypeTag() == .Fn; + if (!is_fn_body and !decl.ty.hasRuntimeBits()) { + return Result{ + .fail = try ErrorMsg.create( + bin_file.allocator, + src_loc, + "TODO handle void types when lowering decl ref", + .{}, + ), + }; + } + if (decl.analysis != .complete) return error.AnalysisFail; decl.markAlive(); const vaddr = vaddr: { diff --git a/src/value.zig b/src/value.zig index 23a04f2e5a..acc3fa3d74 100644 --- a/src/value.zig +++ b/src/value.zig @@ -711,7 +711,10 @@ pub const Value = extern union { const decl = val.castTag(.decl_ref_mut).?.data.decl; return out_stream.print("(decl_ref_mut '{s}')", .{decl.name}); }, - .decl_ref => return out_stream.writeAll("(decl ref)"), + .decl_ref => { + const decl = val.castTag(.decl_ref).?.data; + return out_stream.print("(decl ref '{s}')", .{decl.name}); + }, .elem_ptr => { const elem_ptr = val.castTag(.elem_ptr).?.data; try out_stream.print("&[{}] ", .{elem_ptr.index}); diff --git a/test/behavior/bugs/1025.zig b/test/behavior/bugs/1025.zig index 33ceb9fedf..62d3687ddb 100644 --- a/test/behavior/bugs/1025.zig +++ b/test/behavior/bugs/1025.zig @@ -9,7 +9,6 @@ fn getA() A { } test "bug 1025" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const a = getA(); try @import("std").testing.expect(a.B == u8); } diff --git a/test/behavior/bugs/1277.zig b/test/behavior/bugs/1277.zig index f6d7b91928..3b59ea36e8 100644 --- a/test/behavior/bugs/1277.zig +++ b/test/behavior/bugs/1277.zig @@ -13,6 +13,5 @@ fn f() i32 { test "don't emit an LLVM global for a const function when it's in an optional in a struct" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; try std.testing.expect(s.f.?() == 1234); } diff --git a/test/behavior/bugs/1310.zig b/test/behavior/bugs/1310.zig index 40845ddb03..25509299fb 100644 --- a/test/behavior/bugs/1310.zig +++ b/test/behavior/bugs/1310.zig @@ -24,6 +24,5 @@ fn agent_callback(_vm: [*]VM, options: [*]u8) callconv(.C) i32 { test "fixed" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; try expect(agent_callback(undefined, undefined) == 11); } diff --git a/test/behavior/bugs/1500.zig b/test/behavior/bugs/1500.zig index eb2a06b7fb..6a41617d2b 100644 --- a/test/behavior/bugs/1500.zig +++ b/test/behavior/bugs/1500.zig @@ -7,7 +7,6 @@ const B = *const fn (A) void; test "allow these dependencies" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; var a: A = undefined; var b: B = undefined; if (false) { From 37fea3e3ddc1f7d266d95789c3b1005291bcb96b Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 8 Feb 2022 19:12:43 +0100 Subject: [PATCH 0084/2031] wasm: Store stack-offset as WValue Rather than using runtime to perform pointer arithmetic to set the stack offset as a pointer into a local, we now store the offset as a WValue from the bottom of the stack. This has the benefit of less instructions, few locals, and less performance impact when we allocate a value on the virtual stack. --- src/arch/wasm/CodeGen.zig | 217 ++++++++++++++++++++++---------------- 1 file changed, 126 insertions(+), 91 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 0361d4ffdd..5186f0231b 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -51,6 +51,22 @@ const WValue = union(enum) { /// In wasm function pointers are indexes into a function table, /// rather than an address in the data section. function_index: u32, + /// Offset from the bottom of the stack, with the offset + /// pointing to where the value lives. + stack_offset: u32, + + /// Returns the offset from the bottom of the stack. This is useful when + /// we use the load or store instruction to ensure we retrieve the value + /// from the correct position, rather than the value that lives at the + /// bottom of the stack. For instances where `WValue` is not `stack_value` + /// this will return 0, which allows us to simply call this function for all + /// loads and stores without requiring checks everywhere. + fn offset(self: WValue) u32 { + switch (self) { + .stack_offset => |offset| return offset, + else => return 0, + } + } }; /// Wasm ops, but without input/output/signedness information @@ -778,6 +794,7 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void { try self.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } }); }, .function_index => |index| try self.addLabel(.function_index, index), // write function index and generate relocation + .stack_offset => try self.addLabel(.local_get, self.bottom_stack_value.local), // caller must ensure to address the offset } } @@ -1372,18 +1389,6 @@ fn restoreStackPointer(self: *Self) !void { try self.addLabel(.global_set, 0); } -/// Saves the current stack size's stack pointer position into a given local -/// It does this by retrieving the bottom stack pointer, adding `self.stack_size` and storing -/// the result back into the local. -fn saveStack(self: *Self) !WValue { - const local = try self.allocLocal(Type.usize); - try self.addLabel(.local_get, self.bottom_stack_value.local); - try self.addImm32(@intCast(i32, self.stack_size)); - try self.addTag(.i32_add); - try self.addLabel(.local_set, local.local); - return local; -} - /// From a given type, will create space on the virtual stack to store the value of such type. /// This returns a `WValue` with its active tag set to `local`, containing the index to the local /// that points to the position on the virtual stack. This function should be used instead of @@ -1408,8 +1413,7 @@ fn allocStack(self: *Self, ty: Type) !WValue { const offset = std.mem.alignForwardGeneric(u32, self.stack_size, abi_align); defer self.stack_size = offset + abi_size; - // store the stack pointer and return a local to it - return self.saveStack(); + return WValue{ .stack_offset = offset }; } /// From a given AIR instruction generates a pointer to the stack where @@ -1439,8 +1443,7 @@ fn allocStackPtr(self: *Self, inst: Air.Inst.Index) !WValue { const offset = std.mem.alignForwardGeneric(u32, self.stack_size, abi_alignment); defer self.stack_size = offset + abi_size; - // store the stack pointer and return a local to it - return self.saveStack(); + return WValue{ .stack_offset = offset }; } /// From given zig bitsize, returns the wasm bitsize @@ -1458,14 +1461,16 @@ fn toWasmIntBits(bits: u16) ?u16 { fn memCopy(self: *Self, ty: Type, lhs: WValue, rhs: WValue) !void { const abi_size = ty.abiSize(self.target); var offset: u32 = 0; + const lhs_base = lhs.offset(); + const rhs_base = rhs.offset(); while (offset < abi_size) : (offset += 1) { // get lhs' address to store the result try self.emitWValue(lhs); // load byte from rhs' adress try self.emitWValue(rhs); - try self.addMemArg(.i32_load8_u, .{ .offset = offset, .alignment = 1 }); + try self.addMemArg(.i32_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 }); // store the result in lhs (we already have its address on the stack) - try self.addMemArg(.i32_store8, .{ .offset = offset, .alignment = 1 }); + try self.addMemArg(.i32_store8, .{ .offset = lhs_base + offset, .alignment = 1 }); } } @@ -1533,19 +1538,19 @@ fn isByRef(ty: Type, target: std.Target) bool { /// local value to store the pointer. This allows for local re-use and improves binary size. fn buildPointerOffset(self: *Self, ptr_value: WValue, offset: u64, action: enum { modify, new }) InnerError!WValue { // do not perform arithmetic when offset is 0. - if (offset == 0) return ptr_value; + if (offset == 0 and ptr_value.offset() == 0) return ptr_value; const result_ptr: WValue = switch (action) { .new => try self.allocLocal(Type.usize), .modify => ptr_value, }; try self.emitWValue(ptr_value); - switch (self.target.cpu.arch.ptrBitWidth()) { - 32 => { - try self.addImm32(@bitCast(i32, @intCast(u32, offset))); + switch (self.arch()) { + .wasm32 => { + try self.addImm32(@bitCast(i32, @intCast(u32, offset + ptr_value.offset()))); try self.addTag(.i32_add); }, - 64 => { - try self.addImm64(offset); + .wasm64 => { + try self.addImm64(offset + ptr_value.offset()); try self.addTag(.i64_add); }, else => unreachable, @@ -1554,16 +1559,6 @@ fn buildPointerOffset(self: *Self, ptr_value: WValue, offset: u64, action: enum return result_ptr; } -/// Creates a new local and sets its value to the given `value` local. -/// User must ensure `ty` matches that of given `value`. -/// Asserts `value` is a `local`. -fn copyLocal(self: *Self, value: WValue, ty: Type) InnerError!WValue { - const copy = try self.allocLocal(ty); - try self.addLabel(.local_get, value.local); - try self.addLabel(.local_set, copy.local); - return copy; -} - fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { const air_tags = self.air.instructions.items(.tag); return switch (air_tags[inst]) { @@ -1789,7 +1784,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const sret = if (first_param_sret) blk: { const sret_local = try self.allocStack(ret_ty); - try self.emitWValue(sret_local); + const ptr_offset = try self.buildPointerOffset(sret_local, 0, .new); + try self.emitWValue(ptr_offset); break :blk sret_local; } else WValue{ .none = {} }; @@ -1799,7 +1795,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const arg_ty = self.air.typeOf(arg_ref); if (!arg_ty.hasRuntimeBits()) continue; - try self.emitWValue(arg_val); + + switch (arg_val) { + .stack_offset => try self.emitWValue(try self.buildPointerOffset(arg_val, 0, .new)), + else => try self.emitWValue(arg_val), + } } if (target) |direct| { @@ -1865,7 +1865,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro var buf: Type.Payload.ElemType = undefined; const pl_ty = ty.optionalChild(&buf); if (!pl_ty.hasRuntimeBits()) { - return self.store(lhs, rhs, Type.initTag(.u8), 0); + return self.store(lhs, rhs, Type.u8, 0); } return self.memCopy(ty, lhs, rhs); @@ -1891,7 +1891,13 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro else => {}, } try self.emitWValue(lhs); - try self.emitWValue(rhs); + // In this case we're actually interested in storing the stack position + // into lhs, so we calculate that and emit that instead + if (rhs == .stack_offset) { + try self.emitWValue(try self.buildPointerOffset(rhs, 0, .new)); + } else { + try self.emitWValue(rhs); + } const valtype = typeToValtype(ty, self.target); const abi_size = @intCast(u8, ty.abiSize(self.target)); @@ -1904,7 +1910,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro // store rhs value at stack pointer's location in memory try self.addMemArg( Mir.Inst.Tag.fromOpcode(opcode), - .{ .offset = offset, .alignment = ty.abiAlignment(self.target) }, + .{ .offset = offset + lhs.offset(), .alignment = ty.abiAlignment(self.target) }, ); } @@ -1947,7 +1953,7 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { try self.addMemArg( Mir.Inst.Tag.fromOpcode(opcode), - .{ .offset = offset, .alignment = ty.abiAlignment(self.target) }, + .{ .offset = offset + operand.offset(), .alignment = ty.abiAlignment(self.target) }, ); // store the result in a local @@ -2358,7 +2364,12 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { // if operand has codegen bits we should break with a value if (self.air.typeOf(br.operand).hasRuntimeBits()) { - try self.emitWValue(try self.resolveInst(br.operand)); + const operand = try self.resolveInst(br.operand); + const op = switch (operand) { + .stack_offset => try self.buildPointerOffset(operand, 0, .new), + else => operand, + }; + try self.emitWValue(op); if (block.value != .none) { try self.addLabel(.local_set, block.value.local); @@ -2381,8 +2392,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { // wasm does not have booleans nor the `not` instruction, therefore compare with 0 // to create the same logic - try self.addImm32(0); - try self.addTag(.i32_eq); + try self.addTag(.i32_eqz); // save the result in the local const not_tmp = try self.allocLocal(Type.initTag(.i32)); @@ -2406,8 +2416,7 @@ fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!WValue { fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - const operand = try self.resolveInst(ty_op.operand); - return operand; + return self.resolveInst(ty_op.operand); } fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -2437,7 +2446,12 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr } fn structFieldPtr(self: *Self, struct_ptr: WValue, offset: u32) InnerError!WValue { - return self.buildPointerOffset(struct_ptr, offset, .new); + switch (struct_ptr) { + .stack_offset => |stack_offset| { + return WValue{ .stack_offset = stack_offset + offset }; + }, + else => return self.buildPointerOffset(struct_ptr, offset, .new), + } } fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -2455,7 +2469,12 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { }; if (isByRef(field_ty, self.target)) { - return self.buildPointerOffset(operand, offset, .new); + switch (operand) { + .stack_offset => |stack_offset| { + return WValue{ .stack_offset = stack_offset + offset }; + }, + else => return self.buildPointerOffset(operand, offset, .new), + } } return self.load(operand, field_ty, offset); @@ -2621,7 +2640,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W try self.emitWValue(operand); if (pl_ty.hasRuntimeBits()) { try self.addMemArg(.i32_load16_u, .{ - .offset = 0, + .offset = operand.offset(), .alignment = err_ty.errorUnionSet().abiAlignment(self.target), }); } @@ -2679,9 +2698,9 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.store(payload_ptr, operand, op_ty, 0); // ensure we also write '0' to the error part, so any present stack value gets overwritten by it. - try self.addLabel(.local_get, err_union.local); + try self.emitWValue(err_union); try self.addImm32(0); - try self.addMemArg(.i32_store16, .{ .offset = 0, .alignment = 2 }); + try self.addMemArg(.i32_store16, .{ .offset = err_union.offset(), .alignment = 2 }); return err_union; } @@ -2752,7 +2771,7 @@ fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) // When payload is zero-bits, we can treat operand as a value, rather than // a pointer to the stack value if (payload_ty.hasRuntimeBits()) { - try self.addMemArg(.i32_load8_u, .{ .offset = 0, .alignment = 1 }); + try self.addMemArg(.i32_load8_u, .{ .offset = operand.offset(), .alignment = 1 }); } } @@ -2820,7 +2839,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue try self.emitWValue(operand); try self.addImm32(1); - try self.addMemArg(.i32_store8, .{ .offset = 0, .alignment = 1 }); + try self.addMemArg(.i32_store8, .{ .offset = operand.offset(), .alignment = 1 }); return self.buildPointerOffset(operand, offset, .new); } @@ -2832,9 +2851,9 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const payload_ty = self.air.typeOf(ty_op.operand); if (!payload_ty.hasRuntimeBits()) { const non_null_bit = try self.allocStack(Type.initTag(.u1)); - try self.addLabel(.local_get, non_null_bit.local); + try self.emitWValue(non_null_bit); try self.addImm32(1); - try self.addMemArg(.i32_store8, .{ .offset = 0, .alignment = 1 }); + try self.addMemArg(.i32_store8, .{ .offset = non_null_bit.offset(), .alignment = 1 }); return non_null_bit; } @@ -2849,9 +2868,9 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { // Create optional type, set the non-null bit, and store the operand inside the optional type const result = try self.allocStack(op_ty); - try self.addLabel(.local_get, result.local); + try self.emitWValue(result); try self.addImm32(1); - try self.addMemArg(.i32_store8, .{ .offset = 0, .alignment = 1 }); + try self.addMemArg(.i32_store8, .{ .offset = result.offset(), .alignment = 1 }); const payload_ptr = try self.buildPointerOffset(result, offset, .new); try self.store(payload_ptr, operand, payload_ty, 0); @@ -3013,8 +3032,6 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); const array_ty = self.air.typeOf(ty_op.operand).childType(); - const ty = Type.@"usize"; - const ptr_width = @intCast(u32, ty.abiSize(self.target)); const slice_ty = self.air.getRefType(ty_op.ty); // create a slice on the stack @@ -3022,15 +3039,12 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue { // store the array ptr in the slice if (array_ty.hasRuntimeBits()) { - try self.store(slice_local, operand, ty, 0); + try self.store(slice_local, operand, Type.usize, 0); } // store the length of the array in the slice - const len = array_ty.arrayLen(); - try self.addImm32(@bitCast(i32, @intCast(u32, len))); - const len_local = try self.allocLocal(ty); - try self.addLabel(.local_set, len_local.local); - try self.store(slice_local, len_local, ty, ptr_width); + const len = WValue{ .imm32 = @intCast(u32, array_ty.arrayLen()) }; + try self.store(slice_local, len, Type.usize, self.ptrSize()); return slice_local; } @@ -3038,7 +3052,13 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue { fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; const un_op = self.air.instructions.items(.data)[inst].un_op; - return self.resolveInst(un_op); + const operand = try self.resolveInst(un_op); + + switch (operand) { + // for stack offset, return a pointer to this offset. + .stack_offset => return self.buildPointerOffset(operand, 0, .new), + else => return operand, + } } fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -3046,16 +3066,20 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const ptr_ty = self.air.typeOf(bin_op.lhs); - const pointer = try self.resolveInst(bin_op.lhs); + const ptr = try self.resolveInst(bin_op.lhs); const index = try self.resolveInst(bin_op.rhs); const elem_ty = ptr_ty.childType(); const elem_size = elem_ty.abiSize(self.target); // load pointer onto the stack if (ptr_ty.isSlice()) { - const ptr_local = try self.load(pointer, Type.usize, 0); + const ptr_local = try self.load(ptr, Type.usize, 0); try self.addLabel(.local_get, ptr_local.local); } else { + const pointer = switch (ptr) { + .stack_offset => try self.buildPointerOffset(ptr, 0, .new), + else => ptr, + }; try self.emitWValue(pointer); } @@ -3089,7 +3113,11 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ptr_local = try self.load(ptr, Type.usize, 0); try self.addLabel(.local_get, ptr_local.local); } else { - try self.emitWValue(ptr); + const pointer = switch (ptr) { + .stack_offset => try self.buildPointerOffset(ptr, 0, .new), + else => ptr, + }; + try self.emitWValue(pointer); } // calculate index into ptr @@ -3118,7 +3146,11 @@ fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul }); const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op }); - try self.emitWValue(ptr); + const pointer = switch (ptr) { + .stack_offset => try self.buildPointerOffset(ptr, 0, .new), + else => ptr, + }; + try self.emitWValue(pointer); try self.emitWValue(offset); try self.addImm32(@bitCast(i32, @intCast(u32, pointee_ty.abiSize(self.target)))); try self.addTag(Mir.Inst.Tag.fromOpcode(mul_opcode)); @@ -3138,7 +3170,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const len = try self.resolveInst(bin_op.rhs); try self.memSet(ptr, len, value); - return WValue.none; + return WValue{ .none = {} }; } /// Sets a region of memory at `ptr` to the value of `value` @@ -3149,7 +3181,10 @@ fn memSet(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void // When bulk_memory is enabled, we lower it to wasm's memset instruction. // If not, we lower it ourselves if (std.Target.wasm.featureSetHas(self.target.cpu.features, .bulk_memory)) { - try self.emitWValue(ptr); + switch (ptr) { + .stack_offset => try self.emitWValue(try self.buildPointerOffset(ptr, 0, .new)), + else => try self.emitWValue(ptr), + } try self.emitWValue(value); try self.emitWValue(len); try self.addExtended(.memory_fill); @@ -3172,18 +3207,18 @@ fn memSet(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void try self.addLabel(.br_if, 1); // jump out of loop into outer block (finished) try self.emitWValue(ptr); try self.emitWValue(offset); - switch (self.ptrSize()) { - 4 => try self.addTag(.i32_add), - 8 => try self.addTag(.i64_add), + switch (self.arch()) { + .wasm32 => try self.addTag(.i32_add), + .wasm64 => try self.addTag(.i64_add), else => unreachable, } try self.emitWValue(value); - const mem_store_op: Mir.Inst.Tag = switch (self.ptrSize()) { - 4 => .i32_store8, - 8 => .i64_store8, + const mem_store_op: Mir.Inst.Tag = switch (self.arch()) { + .wasm32 => .i32_store8, + .wasm64 => .i64_store8, else => unreachable, }; - try self.addMemArg(mem_store_op, .{ .offset = 0, .alignment = 1 }); + try self.addMemArg(mem_store_op, .{ .offset = ptr.offset(), .alignment = 1 }); try self.emitWValue(offset); try self.addImm32(1); switch (self.ptrSize()) { @@ -3207,14 +3242,18 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const elem_ty = array_ty.childType(); const elem_size = elem_ty.abiSize(self.target); - // calculate index into slice - try self.emitWValue(array); + const array_ptr = switch (array) { + .stack_offset => try self.buildPointerOffset(array, 0, .new), + else => array, + }; + + try self.emitWValue(array_ptr); try self.emitWValue(index); try self.addImm32(@bitCast(i32, @intCast(u32, elem_size))); try self.addTag(.i32_mul); try self.addTag(.i32_add); - const result = try self.allocLocal(elem_ty); + const result = try self.allocLocal(Type.usize); try self.addLabel(.local_set, result.local); if (isByRef(elem_ty, self.target)) { @@ -3277,9 +3316,7 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (isByRef(elem_ty, self.target)) { // copy stack pointer into a temporary local, which is // moved for each element to store each value in the right position. - const offset = try self.allocLocal(Type.usize); - try self.emitWValue(result); - try self.addLabel(.local_set, offset.local); + const offset = try self.buildPointerOffset(result, 0, .new); for (elements) |elem, elem_index| { const elem_val = try self.resolveInst(elem); try self.store(offset, elem_val, elem_ty, 0); @@ -3301,9 +3338,7 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue { .Struct => { const tuple = vector_ty.castTag(.tuple).?.data; const result = try self.allocStack(vector_ty); - const offset = try self.allocLocal(Type.usize); // pointer to offset - try self.emitWValue(result); - try self.addLabel(.local_set, offset.local); + const offset = try self.buildPointerOffset(result, 0, .new); // pointer to offset for (elements) |elem, elem_index| { if (tuple.values[elem_index].tag() != .unreachable_value) continue; @@ -3379,10 +3414,10 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma const result = try self.allocLocal(Type.initTag(.i32)); { try self.startBlock(.block, wasm.block_empty); - const lhs_high_bit = try self.load(lhs, Type.initTag(.u64), 0); - const lhs_low_bit = try self.load(lhs, Type.initTag(.u64), 8); - const rhs_high_bit = try self.load(rhs, Type.initTag(.u64), 0); - const rhs_low_bit = try self.load(rhs, Type.initTag(.u64), 8); + const lhs_high_bit = try self.load(lhs, Type.u64, 0); + const lhs_low_bit = try self.load(lhs, Type.u64, 8); + const rhs_high_bit = try self.load(rhs, Type.u64, 0); + const rhs_low_bit = try self.load(rhs, Type.u64, 8); try self.emitWValue(lhs_high_bit); try self.emitWValue(rhs_high_bit); try self.addTag(.i64_ne); From 6b0c950cb873f5fe65c0029140b91dfd8a7b1adb Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Tue, 8 Feb 2022 12:56:50 +0100 Subject: [PATCH 0085/2031] stage2 ARM: support all integer types in genTypedValue --- src/arch/arm/CodeGen.zig | 24 +++++++++++++++++------- test/behavior/align.zig | 2 -- test/behavior/bugs/1277.zig | 1 - test/behavior/bugs/1310.zig | 1 - test/behavior/bugs/2006.zig | 1 - test/behavior/cast.zig | 4 ---- test/behavior/struct.zig | 6 ------ 7 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index fb473ef412..9e1e3d7f43 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -3180,7 +3180,6 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { switch (mcv) { .dead => unreachable, - .ptr_stack_offset => unreachable, .ptr_embedded_in_code => unreachable, .unreach, .none => return, // Nothing to do. .undef => { @@ -3194,6 +3193,10 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro else => return self.fail("TODO implement memset", .{}), } }, + .ptr_stack_offset => { + const reg = try self.copyToTmpRegister(ty, mcv); + return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); + }, .compare_flags_unsigned, .compare_flags_signed, .immediate, @@ -3858,9 +3861,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; return MCValue{ .memory = got_addr }; } else if (self.bin_file.cast(link.File.MachO)) |_| { - // TODO I'm hacking my way through here by repurposing .memory for storing - // index to the GOT target symbol index. - return MCValue{ .memory = decl.link.macho.local_sym_index }; + unreachable; // unsupported architecture for MachO } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; return MCValue{ .memory = got_addr }; @@ -3929,10 +3930,19 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { }, .Int => { const info = typed_value.ty.intInfo(self.target.*); - if (info.bits > ptr_bits or info.signedness == .signed) { - return self.fail("TODO const int bigger than ptr and signed int", .{}); + if (info.bits <= ptr_bits) { + const unsigned = switch (info.signedness) { + .signed => blk: { + const signed = @intCast(i32, typed_value.val.toSignedInt()); + break :blk @bitCast(u32, signed); + }, + .unsigned => @intCast(u32, typed_value.val.toUnsignedInt()), + }; + + return MCValue{ .immediate = unsigned }; + } else { + return self.lowerUnnamedConst(typed_value); } - return MCValue{ .immediate = @intCast(u32, typed_value.val.toUnsignedInt()) }; }, .Bool => { return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 1044742627..96278524c0 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -93,8 +93,6 @@ test "@ptrCast preserves alignment of bigger source" { } test "alignstack" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - try expect(fnWithAlignedStack() == 1234); } diff --git a/test/behavior/bugs/1277.zig b/test/behavior/bugs/1277.zig index 3b59ea36e8..46fa1d27d4 100644 --- a/test/behavior/bugs/1277.zig +++ b/test/behavior/bugs/1277.zig @@ -12,6 +12,5 @@ fn f() i32 { } test "don't emit an LLVM global for a const function when it's in an optional in a struct" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; try std.testing.expect(s.f.?() == 1234); } diff --git a/test/behavior/bugs/1310.zig b/test/behavior/bugs/1310.zig index 25509299fb..1f19ec20c2 100644 --- a/test/behavior/bugs/1310.zig +++ b/test/behavior/bugs/1310.zig @@ -23,6 +23,5 @@ fn agent_callback(_vm: [*]VM, options: [*]u8) callconv(.C) i32 { } test "fixed" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; try expect(agent_callback(undefined, undefined) == 11); } diff --git a/test/behavior/bugs/2006.zig b/test/behavior/bugs/2006.zig index 15f74b4485..3719271bdf 100644 --- a/test/behavior/bugs/2006.zig +++ b/test/behavior/bugs/2006.zig @@ -7,7 +7,6 @@ const S = struct { }; test "bug 2006" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; var a: S = undefined; a = S{ .p = undefined }; diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index d8fbc5ed9e..79f75f773c 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -35,8 +35,6 @@ fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { } test "resolve undefined with integer" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - try testResolveUndefWithInt(true, 1234); comptime try testResolveUndefWithInt(true, 1234); } @@ -205,8 +203,6 @@ test "implicit cast from *[N]T to [*c]T" { } test "*usize to *void" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - var i = @as(usize, 0); var v = @ptrCast(*void, &i); v.* = {}; diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index e4b64a39d3..6a0ebb5123 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -114,8 +114,6 @@ test "struct byval assign" { } test "call struct static method" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - const result = StructWithNoFields.add(3, 4); try expect(result == 7); } @@ -193,8 +191,6 @@ test "store member function in variable" { } test "member functions" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - const r = MemberFnRand{ .seed = 1234 }; try expect(r.getSeed() == 1234); } @@ -244,8 +240,6 @@ test "call method with mutable reference to struct with no fields" { } test "usingnamespace within struct scope" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - const S = struct { usingnamespace struct { pub fn inner() i32 { From 8fe9d2f9867101fc8d6a91c6e10c6f3b644ce6a8 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Tue, 8 Feb 2022 13:21:54 +0100 Subject: [PATCH 0086/2031] stage2 ARM: airStructFieldVal for more MCValues --- src/arch/arm/CodeGen.zig | 11 ++++++++++- test/behavior/struct.zig | 4 ---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 9e1e3d7f43..2116717cd1 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1703,9 +1703,18 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); const struct_field_ty = struct_ty.structFieldType(index); const struct_field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*)); + const adjusted_field_offset = struct_size - struct_field_offset - struct_field_size; + switch (mcv) { + .dead, .unreach => unreachable, + .stack_argument_offset => |off| { + break :result MCValue{ .stack_argument_offset = off + adjusted_field_offset }; + }, .stack_offset => |off| { - break :result MCValue{ .stack_offset = off + struct_size - struct_field_offset - struct_field_size }; + break :result MCValue{ .stack_offset = off + adjusted_field_offset }; + }, + .memory => |addr| { + break :result MCValue{ .memory = addr + adjusted_field_offset }; }, else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), } diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 6a0ebb5123..03be28b9d1 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -174,16 +174,12 @@ const MemberFnTestFoo = struct { }; test "call member function directly" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - const instance = MemberFnTestFoo{ .x = 1234 }; const result = MemberFnTestFoo.member(instance); try expect(result == 1234); } test "store member function in variable" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - const instance = MemberFnTestFoo{ .x = 1234 }; const memberFn = MemberFnTestFoo.member; const result = memberFn(instance); From e42b5e76bacaf221f3da3f4ffe769f603a51cf44 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 8 Feb 2022 22:35:34 +0100 Subject: [PATCH 0087/2031] stage2: handle void type in Elf DWARF gen Enable more behavior tests on both x64 and arm --- src/link/Elf.zig | 4 +- test/behavior.zig | 2 +- test/behavior/basic.zig | 79 +++++++++++++++++++++++++++++++++++++ test/behavior/bugs/2006.zig | 1 - test/behavior/bugs/3367.zig | 1 - 5 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index ea9556a952..2a756b3347 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -3057,8 +3057,10 @@ fn addDbgInfoType( var relocs = std.ArrayList(struct { ty: Type, reloc: u32 }).init(arena); switch (ty.zigTypeTag()) { - .Void => unreachable, .NoReturn => unreachable, + .Void => { + try dbg_info_buffer.append(abbrev_pad1); + }, .Bool => { try dbg_info_buffer.appendSlice(&[_]u8{ abbrev_base_type, diff --git a/test/behavior.zig b/test/behavior.zig index 7b6cb6b402..c15de44597 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -4,6 +4,7 @@ test { _ = @import("behavior/align.zig"); _ = @import("behavior/alignof.zig"); _ = @import("behavior/array.zig"); + _ = @import("behavior/basic.zig"); _ = @import("behavior/bit_shifting.zig"); _ = @import("behavior/bool.zig"); _ = @import("behavior/bugs/394.zig"); @@ -43,7 +44,6 @@ test { if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_x86_64) { // Tests that pass (partly) for stage1, llvm backend, C backend, wasm backend. _ = @import("behavior/array_llvm.zig"); - _ = @import("behavior/basic.zig"); _ = @import("behavior/bitcast.zig"); _ = @import("behavior/bugs/624.zig"); _ = @import("behavior/bugs/704.zig"); diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 0c2cfbc3d5..5fc26d15bb 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -15,6 +15,8 @@ test "empty function with comments" { } test "truncate" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + try expect(testTruncate(0x10fd) == 0xfd); comptime try expect(testTruncate(0x10fd) == 0xfd); } @@ -23,6 +25,9 @@ fn testTruncate(x: u32) u8 { } test "truncate to non-power-of-two integers" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + try testTrunc(u32, u1, 0b10101, 0b1); try testTrunc(u32, u1, 0b10110, 0b0); try testTrunc(u32, u2, 0b10101, 0b01); @@ -108,14 +113,23 @@ fn first4KeysOfHomeRow() []const u8 { } test "return string from function" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); } test "hex escape" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + try expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); } test "multiline string" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const s1 = \\one \\two) @@ -126,6 +140,9 @@ test "multiline string" { } test "multiline string comments at start" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const s1 = //\\one \\two) @@ -136,6 +153,9 @@ test "multiline string comments at start" { } test "multiline string comments at end" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const s1 = \\one \\two) @@ -146,6 +166,9 @@ test "multiline string comments at end" { } test "multiline string comments in middle" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const s1 = \\one //\\two) @@ -156,6 +179,9 @@ test "multiline string comments in middle" { } test "multiline string comments at multiple places" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const s1 = \\one //\\two @@ -172,6 +198,9 @@ test "string concatenation" { } test "array mult operator" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + try expect(mem.eql(u8, "ab" ** 5, "ababababab")); } @@ -195,6 +224,9 @@ test "compile time global reinterpret" { } test "cast undefined" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const array: [100]u8 = undefined; const slice = @as([]const u8, &array); testCastUndefined(slice); @@ -204,6 +236,8 @@ fn testCastUndefined(x: []const u8) void { } test "implicit cast after unreachable" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + try expect(outer() == 1234); } fn inner() i32 { @@ -259,6 +293,9 @@ fn fB() []const u8 { } test "call function pointer in struct" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage1) return error.SkipZigTest; try expect(mem.eql(u8, f3(true), "a")); @@ -282,6 +319,8 @@ const FnPtrWrapper = struct { }; test "const ptr from var variable" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + var x: u64 = undefined; var y: u64 = undefined; @@ -296,6 +335,8 @@ fn copy(src: *const u64, dst: *u64) void { } test "call result of if else expression" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO @@ -307,6 +348,8 @@ fn f2(x: bool) []const u8 { } test "memcpy and memset intrinsics" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO try testMemcpyMemset(); @@ -327,6 +370,8 @@ fn testMemcpyMemset() !void { } test "variable is allowed to be a pointer to an opaque type" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var x: i32 = 1234; @@ -338,6 +383,9 @@ fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA { } test "take address of parameter" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + try testTakeAddressOfParameter(12.34); } fn testTakeAddressOfParameter(f: f32) !void { @@ -360,6 +408,9 @@ fn testPointerToVoidReturnType2() *const void { } test "array 2D const double ptr" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const rect_2d_vertexes = [_][1]f32{ @@ -376,6 +427,9 @@ fn testArray2DConstDoublePtr(ptr: *const f32) !void { } test "double implicit cast in same expression" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + var x = @as(i32, @as(u16, nine())); try expect(x == 9); } @@ -384,6 +438,8 @@ fn nine() u8 { } test "struct inside function" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + try testStructInFn(); comptime try testStructInFn(); } @@ -411,6 +467,8 @@ fn getNull() ?*i32 { } test "global variable assignment with optional unwrapping with var initialized to undefined" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + const S = struct { var data: i32 = 1234; fn foo() ?*i32 { @@ -426,6 +484,9 @@ test "global variable assignment with optional unwrapping with var initialized t var global_foo: *i32 = undefined; test "peer result location with typed parent, runtime condition, comptime prongs" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const S = struct { fn doTheTest(arg: i32) i32 { const st = Structy{ @@ -523,6 +584,9 @@ test "self reference through fn ptr field" { } test "global variable initialized to global variable array element" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; try expect(global_ptr == &gdt[0]); @@ -537,6 +601,8 @@ var gdt = [_]GDTEntry{ var global_ptr = &gdt[0]; test "global constant is loaded with a runtime-known index" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + const S = struct { fn doTheTest() !void { var index: usize = 1; @@ -552,6 +618,9 @@ test "global constant is loaded with a runtime-known index" { } test "multiline string literal is null terminated" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const s1 = \\one \\two) @@ -582,6 +651,9 @@ test "explicit cast optional pointers" { } test "pointer comparison" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const a = @as([]const u8, "a"); const b = &a; try expect(ptrEql(b, b)); @@ -591,6 +663,9 @@ fn ptrEql(a: *const []const u8, b: *const []const u8) bool { } test "string concatenation" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const a = "OK" ++ " IT " ++ "WORKED"; const b = "OK IT WORKED"; @@ -610,6 +685,8 @@ test "string concatenation" { } test "thread local variable" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO const S = struct { @@ -634,6 +711,8 @@ fn maybe(x: bool) anyerror!?u32 { } test "pointer to thread local array" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO const s = "Hello world"; diff --git a/test/behavior/bugs/2006.zig b/test/behavior/bugs/2006.zig index 3719271bdf..4d76230c88 100644 --- a/test/behavior/bugs/2006.zig +++ b/test/behavior/bugs/2006.zig @@ -7,7 +7,6 @@ const S = struct { }; test "bug 2006" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; var a: S = undefined; a = S{ .p = undefined }; try expect(@sizeOf(S) != 0); diff --git a/test/behavior/bugs/3367.zig b/test/behavior/bugs/3367.zig index 0607263f9a..f540fdf6df 100644 --- a/test/behavior/bugs/3367.zig +++ b/test/behavior/bugs/3367.zig @@ -12,7 +12,6 @@ const Mixin = struct { test "container member access usingnamespace decls" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; var foo = Foo{}; foo.two(); } From f1d2141849e0ae01910bef52cada0de0d0322e72 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 8 Feb 2022 23:48:42 +0100 Subject: [PATCH 0088/2031] stage2: handle direct and got load for stack args --- src/arch/x86_64/CodeGen.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 9abeadad5d..de8f907dea 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -3482,6 +3482,8 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE }, .memory, .embedded_in_code, + .direct_load, + .got_load, => { if (abi_size <= 8) { const reg = try self.copyToTmpRegister(ty, mcv); From c689df1215fbe27485eb57751a407e6d5a991ca1 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 8 Feb 2022 23:59:10 +0100 Subject: [PATCH 0089/2031] stage2: disable some tests on x86_64-macos --- test/behavior/basic.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 5fc26d15bb..13d7e833d5 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -194,6 +194,8 @@ test "multiline string comments at multiple places" { } test "string concatenation" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + try expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); } @@ -395,6 +397,7 @@ fn testTakeAddressOfParameter(f: f32) !void { test "pointer to void return type" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and builtin.os.tag == .macos) return error.SkipZigTest; try testPointerToVoidReturnType(); } From fd6c351263e99af4dca73af953711e6d1b57f4e4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Feb 2022 17:22:11 -0700 Subject: [PATCH 0090/2031] cmake: fix -DZIG_SINGLE_THREADED option It was still passing --single-threaded instead of -fsingle-threaded. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e12e040fe4..8fd1960518 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -834,7 +834,7 @@ else() set(ZIG1_RELEASE_ARG -OReleaseFast --strip) endif() if(ZIG_SINGLE_THREADED) - set(ZIG1_SINGLE_THREADED_ARG "--single-threaded") + set(ZIG1_SINGLE_THREADED_ARG "-fsingle-threaded") else() set(ZIG1_SINGLE_THREADED_ARG "") endif() From 5a00e249632716b86edac088f69d19d82e307a28 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Feb 2022 17:26:55 -0700 Subject: [PATCH 0091/2031] std.Progress: make the API infallible by handling `error.TimerUnsupported`. In this case, only explicit calls to refresh() will cause the progress line to be printed. --- lib/std/Progress.zig | 32 ++++++++++++++++++-------------- lib/std/special/test_runner.zig | 5 +---- src/Compilation.zig | 2 +- src/stage1.zig | 5 +---- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 682171366b..24b66c1162 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -35,7 +35,7 @@ root: Node = undefined, /// Keeps track of how much time has passed since the beginning. /// Used to compare with `initial_delay_ms` and `refresh_rate_ms`. -timer: std.time.Timer = undefined, +timer: ?std.time.Timer = null, /// When the previous refresh was written to the terminal. /// Used to compare with `refresh_rate_ms`. @@ -139,7 +139,7 @@ pub const Node = struct { /// TODO solve https://github.com/ziglang/zig/issues/2765 and then change this /// API to return Progress rather than accept it as a parameter. /// `estimated_total_items` value of 0 means unknown. -pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !*Node { +pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) *Node { const stderr = std.io.getStdErr(); self.terminal = null; if (stderr.supportsAnsiEscapeCodes()) { @@ -161,22 +161,24 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !* }; self.columns_written = 0; self.prev_refresh_timestamp = 0; - self.timer = try std.time.Timer.start(); + self.timer = std.time.Timer.start() catch null; self.done = false; return &self.root; } /// Updates the terminal if enough time has passed since last update. Thread-safe. pub fn maybeRefresh(self: *Progress) void { - const now = self.timer.read(); - if (now < self.initial_delay_ns) return; - if (!self.update_mutex.tryLock()) return; - defer self.update_mutex.unlock(); - // TODO I have observed this to happen sometimes. I think we need to follow Rust's - // lead and guarantee monotonically increasing times in the std lib itself. - if (now < self.prev_refresh_timestamp) return; - if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return; - return self.refreshWithHeldLock(); + if (self.timer) |*timer| { + const now = timer.read(); + if (now < self.initial_delay_ns) return; + if (!self.update_mutex.tryLock()) return; + defer self.update_mutex.unlock(); + // TODO I have observed this to happen sometimes. I think we need to follow Rust's + // lead and guarantee monotonically increasing times in the std lib itself. + if (now < self.prev_refresh_timestamp) return; + if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return; + return self.refreshWithHeldLock(); + } } /// Updates the terminal and resets `self.next_refresh_timestamp`. Thread-safe. @@ -285,7 +287,9 @@ fn refreshWithHeldLock(self: *Progress) void { // Stop trying to write to this file once it errors. self.terminal = null; }; - self.prev_refresh_timestamp = self.timer.read(); + if (self.timer) |*timer| { + self.prev_refresh_timestamp = timer.read(); + } } pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void { @@ -327,7 +331,7 @@ test "basic functionality" { return error.SkipZigTest; } var progress = Progress{}; - const root_node = try progress.start("", 100); + const root_node = progress.start("", 100); defer root_node.end(); const sub_task_names = [_][]const u8{ diff --git a/lib/std/special/test_runner.zig b/lib/std/special/test_runner.zig index 9848cb5a3e..fb00a9dc30 100644 --- a/lib/std/special/test_runner.zig +++ b/lib/std/special/test_runner.zig @@ -34,10 +34,7 @@ pub fn main() void { var progress = std.Progress{ .dont_print_on_dumb = true, }; - const root_node = progress.start("Test", test_fn_list.len) catch |err| switch (err) { - // TODO still run tests in this case - error.TimerUnsupported => @panic("timer unsupported"), - }; + const root_node = progress.start("Test", test_fn_list.len); const have_tty = progress.terminal != null and progress.supports_ansi_escape_codes; var async_frame_buffer: []align(std.Target.stack_align) u8 = undefined; diff --git a/src/Compilation.zig b/src/Compilation.zig index f07a7c9dd7..bd7581863b 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2586,7 +2586,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor // If the terminal is dumb, we dont want to show the user all the // output. var progress: std.Progress = .{ .dont_print_on_dumb = true }; - var main_progress_node = try progress.start("", 0); + var main_progress_node = progress.start("", 0); defer main_progress_node.end(); if (self.color == .off) progress.terminal = null; diff --git a/src/stage1.zig b/src/stage1.zig index b716a9c954..005dc312ba 100644 --- a/src/stage1.zig +++ b/src/stage1.zig @@ -305,10 +305,7 @@ export fn stage2_progress_start_root( name_len: usize, estimated_total_items: usize, ) *std.Progress.Node { - return progress.start( - name_ptr[0..name_len], - estimated_total_items, - ) catch @panic("timer unsupported"); + return progress.start(name_ptr[0..name_len], estimated_total_items); } // ABI warning From 846eb701821a3f2af514bbad770478e3276b2d89 Mon Sep 17 00:00:00 2001 From: Hadrien Dorio Date: Fri, 4 Feb 2022 10:27:18 +0100 Subject: [PATCH 0092/2031] ci: azure: split build-and-test step replace the .bat script by a pwsh script --- CMakeLists.txt | 2 +- ci/azure/pipelines.yml | 132 ++++++++++++++++++++++++++----- ci/azure/windows_msvc_install | 16 ---- ci/azure/windows_msvc_script.bat | 39 --------- ci/azure/windows_upload | 46 ----------- 5 files changed, 115 insertions(+), 120 deletions(-) delete mode 100644 ci/azure/windows_msvc_install delete mode 100644 ci/azure/windows_msvc_script.bat delete mode 100755 ci/azure/windows_upload diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fd1960518..533e03383f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,7 +63,7 @@ if("${ZIG_VERSION}" STREQUAL "") endif() endif() endif() -message("Configuring zig version ${ZIG_VERSION}") +message(STATUS "Configuring zig version ${ZIG_VERSION}") set(ZIG_STATIC off CACHE BOOL "Attempt to build a static zig executable (not compatible with glibc)") set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries") diff --git a/ci/azure/pipelines.yml b/ci/azure/pipelines.yml index 4524ee9fb1..f0df4558c2 100644 --- a/ci/azure/pipelines.yml +++ b/ci/azure/pipelines.yml @@ -10,6 +10,7 @@ jobs: - script: ci/azure/macos_script name: main displayName: 'Build and test' + - job: BuildMacOS_arm64 pool: vmImage: 'macOS-10.15' @@ -21,30 +22,125 @@ jobs: - script: ci/azure/macos_arm64_script name: main displayName: 'Build' + - job: BuildWindows + timeoutInMinutes: 360 pool: vmImage: 'windows-2019' - timeoutInMinutes: 360 + variables: + LLVM_CLANG_LLD_URL: 'https://ziglang.org/deps/llvm+clang+lld-13.0.0-x86_64-windows-msvc-release-mt.tar.xz' + LLVM_CLANG_LLD_DIR: 'llvm+clang+lld-13.0.0-x86_64-windows-msvc-release-mt' steps: - - powershell: | - (New-Object Net.WebClient).DownloadFile("https://github.com/msys2/msys2-installer/releases/download/2022-01-28/msys2-base-x86_64-20220128.sfx.exe", "sfx.exe") - .\sfx.exe -y -o\ - displayName: Download/Extract/Install MSYS2 - - script: | - @REM install updated filesystem package first without dependency checking - @REM because of: https://github.com/msys2/MSYS2-packages/issues/2021 - %CD:~0,2%\msys64\usr\bin\bash -lc "pacman --noconfirm -Sydd filesystem" - displayName: Workaround filesystem dash MSYS2 dependency issue - - script: | - %CD:~0,2%\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu" - %CD:~0,2%\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu" - displayName: Update MSYS2 + - pwsh: | + (New-Object Net.WebClient).DownloadFile("$(LLVM_CLANG_LLD_URL)", "${LLVM_CLANG_LLD_DIR}.tar.xz") + & 'C:\Program Files\7-Zip\7z.exe' x "${LLVM_CLANG_LLD_DIR}.tar.xz" + & 'C:\Program Files\7-Zip\7z.exe' x "${LLVM_CLANG_LLD_DIR}.tar" + name: install + displayName: 'Install LLVM/CLANG/LLD' + + - pwsh: | + Set-Variable -Name ZIGBUILDDIR -Value "$(Get-Location)\build" + Set-Variable -Name ZIGINSTALLDIR -Value "$ZIGBUILDDIR\dist" + Set-Variable -Name ZIGPREFIXPATH -Value "$(Get-Location)\$(LLVM_CLANG_LLD_DIR)" + + # Make the `zig version` number consistent. + # This will affect the cmake command below. + git config core.abbrev 9 + git fetch --tags + + mkdir $ZIGBUILDDIR + cd $ZIGBUILDDIR + & "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 + cmake .. ` + -Thost=x64 ` + -G "Visual Studio 16 2019" ` + -A x64 ` + -DCMAKE_INSTALL_PREFIX="$ZIGINSTALLDIR" ` + -DCMAKE_PREFIX_PATH="$ZIGPREFIXPATH" ` + -DCMAKE_BUILD_TYPE=Release ` + -DZIG_OMIT_STAGE2=ON 2> out-null + & "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe" ` + /maxcpucount /p:Configuration=Release INSTALL.vcxproj + name: build + displayName: 'Build' + + - pwsh: | + Set-Variable -Name ZIGINSTALLDIR -Value "$(Get-Location)\build\dist" + + # Sadly, stage2 is omitted from this build to save memory on the CI server. Once self-hosted is + # built with itself and does not gobble as much memory, we can enable these tests. + #& "$ZIGINSTALLDIR\bin\zig.exe" test "..\test\behavior.zig" -fno-stage1 -fLLVM -I "..\test" 2>&1 + + & "$ZIGINSTALLDIR\bin\zig.exe" build test-toolchain -Dskip-non-native -Dskip-stage2-tests 2>&1 + & "$ZIGINSTALLDIR\bin\zig.exe" build test-std -Dskip-non-native 2>&1 + name: test + displayName: 'Test' + + - pwsh: | + Set-Variable -Name ZIGINSTALLDIR -Value "$(Get-Location)\build\dist" + + & "$ZIGINSTALLDIR\bin\zig.exe" build docs + timeoutInMinutes: 60 + name: doc + displayName: 'Documentation' + - task: DownloadSecureFile@1 inputs: - secureFile: s3cfg - - script: ci/azure/windows_msvc_script.bat - name: main - displayName: 'Build and test' + name: aws_credentials + secureFile: aws_credentials + + - pwsh: | + Set-Variable -Name ZIGBUILDDIR -Value "$(Get-Location)\build" + $Env:AWS_SHARED_CREDENTIALS_FILE = "$(aws_credentials.secureFilePath)" + + cd "$ZIGBUILDDIR" + mv ../LICENSE dist/ + mv ../zig-cache/langref.html dist/ + mv dist/bin/zig.exe dist/ + rmdir dist/bin + + # Remove the unnecessary zig dir in $prefix/lib/zig/std/std.zig + mv dist/lib/zig dist/lib2 + rmdir dist/lib + mv dist/lib2 dist/lib + + Set-Variable -Name VERSION -Value $(./dist/zig.exe version) + Set-Variable -Name DIRNAME -Value "zig-windows-x86_64-$VERSION" + Set-Variable -Name TARBALL -Value "$DIRNAME.zip" + mv dist "$DIRNAME" + 7z a "$TARBALL" "$DIRNAME" + + aws s3 cp ` + "$TARBALL" ` + s3://ziglang.org/builds/ ` + --cache-control 'public, max-age=31536000, immutable' + + Set-Variable -Name SHASUM -Value (Get-FileHash "$TARBALL" -Algorithm SHA256 | select-object -ExpandProperty Hash) + Set-Variable -Name BYTESIZE -Value (Get-Item "$TARBALL").length + + Set-Variable -Name JSONFILE -Value "windows-${GITBRANCH}.json" + echo $null > $JSONFILE + echo ('{"tarball": "' + $TARBALL + '",') >> $JSONFILE + echo ('"shasum": "' + $SHASUM + '",') >> $JSONFILE + echo ('"size": ' + $BYTESIZE + '}' ) >> $JSONFILE + + aws s3 cp ` + "$JSONFILE" ` + s3://ziglang.org/builds/ ` + --cache-control 'max-age=0, must-revalidate' + + aws s3 cp ` + "$JSONFILE" ` + "s3://ziglang.org/builds/x86_64-windows-${VERSION}.json" + + echo "##vso[task.setvariable variable=tarball;isOutput=true]$TARBALL" + echo "##vso[task.setvariable variable=shasum;isOutput=true]$SHASUM" + echo "##vso[task.setvariable variable=bytesize;isOutput=true]$BYTESIZE" + + name: upload + condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) + displayName: 'Upload' + - job: OnMasterSuccess dependsOn: - BuildMacOS diff --git a/ci/azure/windows_msvc_install b/ci/azure/windows_msvc_install deleted file mode 100644 index 2df445fe12..0000000000 --- a/ci/azure/windows_msvc_install +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -set -x -set -e - -pacman -Suy --needed --noconfirm -pacman -S --needed --noconfirm wget p7zip python3-pip tar xz - -TARBALL="llvm+clang+lld-13.0.0-x86_64-windows-msvc-release-mt.tar.xz" - -pip install s3cmd -wget -nv "https://ziglang.org/deps/$TARBALL" -# If the first extraction fails, re-try it once; this can happen if the tarball -# contains symlinks that are in the table of contents before the files that -# they point to. -tar -xf $TARBALL || tar --overwrite -xf $TARBALL diff --git a/ci/azure/windows_msvc_script.bat b/ci/azure/windows_msvc_script.bat deleted file mode 100644 index c61c88093c..0000000000 --- a/ci/azure/windows_msvc_script.bat +++ /dev/null @@ -1,39 +0,0 @@ -@echo on -SET "SRCROOT=%cd%" -SET "PREVPATH=%PATH%" -SET "PREVMSYSTEM=%MSYSTEM%" - -set "PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem" -SET "MSYSTEM=MINGW64" -bash -lc "cd ${SRCROOT} && ci/azure/windows_msvc_install" || exit /b -SET "PATH=%PREVPATH%" -SET "MSYSTEM=%PREVMSYSTEM%" - -SET "ZIGBUILDDIR=%SRCROOT%\build" -SET "ZIGINSTALLDIR=%ZIGBUILDDIR%\dist" -SET "ZIGPREFIXPATH=%SRCROOT%\llvm+clang+lld-13.0.0-x86_64-windows-msvc-release-mt" - -call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 - -REM Make the `zig version` number consistent. -REM This will affect the cmake command below. -git.exe config core.abbrev 9 -git.exe fetch --unshallow -git.exe fetch --tags - -mkdir %ZIGBUILDDIR% -cd %ZIGBUILDDIR% -cmake.exe .. -Thost=x64 -G"Visual Studio 16 2019" -A x64 "-DCMAKE_INSTALL_PREFIX=%ZIGINSTALLDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=Release -DZIG_OMIT_STAGE2=ON || exit /b -msbuild /maxcpucount /p:Configuration=Release INSTALL.vcxproj || exit /b - -REM Sadly, stage2 is omitted from this build to save memory on the CI server. Once self-hosted is -REM built with itself and does not gobble as much memory, we can enable these tests. -REM "%ZIGINSTALLDIR%\bin\zig.exe" test "..\test\behavior.zig" -fno-stage1 -fLLVM -I "..\test" || exit /b - -"%ZIGINSTALLDIR%\bin\zig.exe" build test-toolchain -Dskip-non-native -Dskip-stage2-tests || exit /b -"%ZIGINSTALLDIR%\bin\zig.exe" build test-std -Dskip-non-native || exit /b -"%ZIGINSTALLDIR%\bin\zig.exe" build docs || exit /b - -set "PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem" -SET "MSYSTEM=MINGW64" -bash -lc "cd ${SRCROOT} && ci/azure/windows_upload" || exit /b diff --git a/ci/azure/windows_upload b/ci/azure/windows_upload deleted file mode 100755 index 9c5e07e5f9..0000000000 --- a/ci/azure/windows_upload +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh - -set -x -set -e - -if [ "${BUILD_REASON}" != "PullRequest" ]; then - cd "$ZIGBUILDDIR" - - mv ../LICENSE dist/ - mv ../zig-cache/langref.html dist/ - mv dist/bin/zig.exe dist/ - rmdir dist/bin - - # Remove the unnecessary zig dir in $prefix/lib/zig/std/std.zig - mv dist/lib/zig dist/lib2 - rmdir dist/lib - mv dist/lib2 dist/lib - - VERSION=$(dist/zig.exe version) - DIRNAME="zig-windows-x86_64-$VERSION" - TARBALL="$DIRNAME.zip" - mv dist "$DIRNAME" - 7z a "$TARBALL" "$DIRNAME" - - # mv "$DOWNLOADSECUREFILE_SECUREFILEPATH" "$HOME/.s3cfg" - s3cmd -c "$DOWNLOADSECUREFILE_SECUREFILEPATH" put -P --add-header="cache-control: public, max-age=31536000, immutable" "$TARBALL" s3://ziglang.org/builds/ - - SHASUM=$(sha256sum $TARBALL | cut '-d ' -f1) - BYTESIZE=$(wc -c < $TARBALL) - - JSONFILE="windows-$GITBRANCH.json" - touch $JSONFILE - echo "{\"tarball\": \"$TARBALL\"," >>$JSONFILE - echo "\"shasum\": \"$SHASUM\"," >>$JSONFILE - echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE - - s3cmd -c "$DOWNLOADSECUREFILE_SECUREFILEPATH" put -P --add-header="Cache-Control: max-age=0, must-revalidate" "$JSONFILE" "s3://ziglang.org/builds/$JSONFILE" - s3cmd -c "$DOWNLOADSECUREFILE_SECUREFILEPATH" put -P "$JSONFILE" "s3://ziglang.org/builds/x86_64-windows-$VERSION.json" - - # `set -x` causes these variables to be mangled. - # See https://developercommunity.visualstudio.com/content/problem/375679/pipeline-variable-incorrectly-inserts-single-quote.html - set +x - echo "##vso[task.setvariable variable=tarball;isOutput=true]$TARBALL" - echo "##vso[task.setvariable variable=shasum;isOutput=true]$SHASUM" - echo "##vso[task.setvariable variable=bytesize;isOutput=true]$BYTESIZE" -fi From 210ee1067b06c14692432b7887077003e52b2137 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Feb 2022 17:49:40 -0700 Subject: [PATCH 0093/2031] update more API usage of std.Progress fixes regression introduced in 5a00e249632716b86edac088f69d19d82e307a28 --- doc/docgen.zig | 2 +- src/test.zig | 2 +- tools/update-license-headers.zig | 2 +- tools/update_cpu_features.zig | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/docgen.zig b/doc/docgen.zig index 760ebb71d3..3dd58a012d 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -1196,7 +1196,7 @@ fn genHtml( do_code_tests: bool, ) !void { var progress = Progress{}; - const root_node = try progress.start("Generating docgen examples", toc.nodes.len); + const root_node = progress.start("Generating docgen examples", toc.nodes.len); defer root_node.end(); var env_map = try process.getEnvMap(allocator); diff --git a/src/test.zig b/src/test.zig index b73e11d7f5..a540d566eb 100644 --- a/src/test.zig +++ b/src/test.zig @@ -614,7 +614,7 @@ pub const TestContext = struct { const host = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, .{}); var progress = std.Progress{}; - const root_node = try progress.start("compiler", self.cases.items.len); + const root_node = progress.start("compiler", self.cases.items.len); defer root_node.end(); var zig_lib_directory = try introspect.findZigLibDir(std.testing.allocator); diff --git a/tools/update-license-headers.zig b/tools/update-license-headers.zig index 4e415784f8..b2aed7ccdb 100644 --- a/tools/update-license-headers.zig +++ b/tools/update-license-headers.zig @@ -6,7 +6,7 @@ const new_header = ""; pub fn main() !void { var progress = std.Progress{}; - const root_node = try progress.start("", 0); + const root_node = progress.start("", 0); defer root_node.end(); var arena_allocator = std.heap.ArenaAllocator.init(std.heap.page_allocator); diff --git a/tools/update_cpu_features.zig b/tools/update_cpu_features.zig index 73c05d8cf1..933d268e2b 100644 --- a/tools/update_cpu_features.zig +++ b/tools/update_cpu_features.zig @@ -801,7 +801,7 @@ pub fn main() anyerror!void { defer zig_src_dir.close(); var progress = std.Progress{}; - const root_progress = try progress.start("", llvm_targets.len); + const root_progress = progress.start("", llvm_targets.len); defer root_progress.end(); if (builtin.single_threaded) { From 7c1061784b8b126633cfe84f46280f7bf72beffc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Feb 2022 20:03:17 -0700 Subject: [PATCH 0094/2031] stage2: fix inferred comptime constant locals `const` declarations inside comptime blocks were not getting properly evaluated at compile-time. To accomplish this there is a new ZIR instruction, `alloc_inferred_comptime`. Actually we already had one named that, but it got renamed to `alloc_inferred_comptime_mut` to match the naming convention with the other similar instructions. --- src/AstGen.zig | 25 +++++++++++++----- src/Sema.zig | 20 ++++++++++----- src/Zir.zig | 14 +++++++--- src/print_zir.zig | 3 ++- test/behavior.zig | 2 -- test/behavior/null.zig | 48 +++++++++++++++++++++++++++++++++++ test/behavior/null_llvm.zig | 36 -------------------------- test/behavior/null_stage1.zig | 37 --------------------------- 8 files changed, 91 insertions(+), 94 deletions(-) delete mode 100644 test/behavior/null_llvm.zig delete mode 100644 test/behavior/null_stage1.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index 5f6d05b7f5..9bc10f25e8 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2084,10 +2084,11 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .param_anytype_comptime, .alloc, .alloc_mut, - .alloc_comptime, + .alloc_comptime_mut, .alloc_inferred, .alloc_inferred_mut, .alloc_inferred_comptime, + .alloc_inferred_comptime_mut, .array_cat, .array_mul, .array_type, @@ -2613,7 +2614,7 @@ fn varDecl( .type_inst = type_inst, .align_inst = align_inst, .is_const = true, - .is_comptime = false, + .is_comptime = gz.force_comptime, }); init_scope.instructions_top = gz.instructions.items.len; } @@ -2621,14 +2622,18 @@ fn varDecl( } else { const alloc = if (align_inst == .none) alloc: { init_scope.instructions_top = gz.instructions.items.len; - break :alloc try init_scope.addNode(.alloc_inferred, node); + const tag: Zir.Inst.Tag = if (gz.force_comptime) + .alloc_inferred_comptime + else + .alloc_inferred; + break :alloc try init_scope.addNode(tag, node); } else alloc: { const ref = try gz.addAllocExtended(.{ .node = node, .type_inst = .none, .align_inst = align_inst, .is_const = true, - .is_comptime = false, + .is_comptime = gz.force_comptime, }); init_scope.instructions_top = gz.instructions.items.len; break :alloc ref; @@ -2716,7 +2721,10 @@ fn varDecl( const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node); const alloc = alloc: { if (align_inst == .none) { - const tag: Zir.Inst.Tag = if (is_comptime) .alloc_comptime else .alloc_mut; + const tag: Zir.Inst.Tag = if (is_comptime) + .alloc_comptime_mut + else + .alloc_mut; break :alloc try gz.addUnNode(tag, type_inst, node); } else { break :alloc try gz.addAllocExtended(.{ @@ -2732,7 +2740,10 @@ fn varDecl( } else a: { const alloc = alloc: { if (align_inst == .none) { - const tag: Zir.Inst.Tag = if (is_comptime) .alloc_inferred_comptime else .alloc_inferred_mut; + const tag: Zir.Inst.Tag = if (is_comptime) + .alloc_inferred_comptime_mut + else + .alloc_inferred_mut; break :alloc try gz.addNode(tag, node); } else { break :alloc try gz.addAllocExtended(.{ @@ -5441,7 +5452,7 @@ fn forExpr( const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr); const index_ptr = blk: { - const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime else .alloc; + const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc; const index_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node); // initialize to zero _ = try parent_gz.addBin(.store, index_ptr, .zero_usize); diff --git a/src/Sema.zig b/src/Sema.zig index 72dfb4420b..69b9adc54b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -584,9 +584,10 @@ fn analyzeBodyInner( .alloc => try sema.zirAlloc(block, inst), .alloc_inferred => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_const)), .alloc_inferred_mut => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_mut)), - .alloc_inferred_comptime => try sema.zirAllocInferredComptime(inst), + .alloc_inferred_comptime => try sema.zirAllocInferredComptime(inst, Type.initTag(.inferred_alloc_const)), + .alloc_inferred_comptime_mut => try sema.zirAllocInferredComptime(inst, Type.initTag(.inferred_alloc_mut)), .alloc_mut => try sema.zirAllocMut(block, inst), - .alloc_comptime => try sema.zirAllocComptime(block, inst), + .alloc_comptime_mut => try sema.zirAllocComptime(block, inst), .anyframe_type => try sema.zirAnyframeType(block, inst), .array_cat => try sema.zirArrayCat(block, inst), .array_mul => try sema.zirArrayMul(block, inst), @@ -2368,12 +2369,16 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr return sema.analyzeComptimeAlloc(block, var_ty, 0, ty_src); } -fn zirAllocInferredComptime(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { +fn zirAllocInferredComptime( + sema: *Sema, + inst: Zir.Inst.Index, + inferred_alloc_ty: Type, +) CompileError!Air.Inst.Ref { const src_node = sema.code.instructions.items(.data)[inst].node; const src: LazySrcLoc = .{ .node_offset = src_node }; sema.src = src; return sema.addConstant( - Type.initTag(.inferred_alloc_mut), + inferred_alloc_ty, try Value.Tag.inferred_alloc_comptime.create(sema.arena, undefined), ); } @@ -2480,6 +2485,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com const final_elem_ty = try decl.ty.copy(sema.arena); const final_ptr_ty = try Type.ptr(sema.arena, .{ .pointee_type = final_elem_ty, + .mutable = var_is_mut, .@"align" = iac.data.alignment, .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); @@ -2500,9 +2506,6 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com const peer_inst_list = inferred_alloc.data.stored_inst_list.items; const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none); - try sema.requireRuntimeBlock(block, src); - try sema.resolveTypeLayout(block, ty_src, final_elem_ty); - const final_ptr_ty = try Type.ptr(sema.arena, .{ .pointee_type = final_elem_ty, .mutable = var_is_mut, @@ -2564,6 +2567,9 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com return; } + try sema.requireRuntimeBlock(block, src); + try sema.resolveTypeLayout(block, ty_src, final_elem_ty); + // Change it to a normal alloc. sema.air_instructions.set(ptr_inst, .{ .tag = .alloc, diff --git a/src/Zir.zig b/src/Zir.zig index dbdaa0f5fb..b7e3e60916 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -909,14 +909,18 @@ pub const Inst = struct { /// Allocates comptime-mutable memory. /// Uses the `un_node` union field. The operand is the type of the allocated object. /// The node source location points to a var decl node. - alloc_comptime, + alloc_comptime_mut, /// Same as `alloc` except the type is inferred. /// Uses the `node` union field. alloc_inferred, /// Same as `alloc_inferred` except mutable. alloc_inferred_mut, - /// Same as `alloc_comptime` except the type is inferred. + /// Allocates comptime const memory. + /// Uses the `node` union field. The type of the allocated object is inferred. + /// The node source location points to a var decl node. alloc_inferred_comptime, + /// Same as `alloc_comptime_mut` except the type is inferred. + alloc_inferred_comptime_mut, /// Each `store_to_inferred_ptr` puts the type of the stored value into a set, /// and then `resolve_inferred_alloc` triggers peer type resolution on the set. /// The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which @@ -957,10 +961,11 @@ pub const Inst = struct { .add_sat, .alloc, .alloc_mut, - .alloc_comptime, + .alloc_comptime_mut, .alloc_inferred, .alloc_inferred_mut, .alloc_inferred_comptime, + .alloc_inferred_comptime_mut, .array_cat, .array_mul, .array_type, @@ -1446,10 +1451,11 @@ pub const Inst = struct { .alloc = .un_node, .alloc_mut = .un_node, - .alloc_comptime = .un_node, + .alloc_comptime_mut = .un_node, .alloc_inferred = .node, .alloc_inferred_mut = .node, .alloc_inferred_comptime = .node, + .alloc_inferred_comptime_mut = .node, .resolve_inferred_alloc = .un_node, .@"resume" = .un_node, diff --git a/src/print_zir.zig b/src/print_zir.zig index 1954772e37..9c79ad1a37 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -155,7 +155,7 @@ const Writer = struct { .alloc, .alloc_mut, - .alloc_comptime, + .alloc_comptime_mut, .indexable_ptr_len, .anyframe_type, .bit_not, @@ -401,6 +401,7 @@ const Writer = struct { .alloc_inferred, .alloc_inferred_mut, .alloc_inferred_comptime, + .alloc_inferred_comptime_mut, => try self.writeNode(stream, inst), .error_value, diff --git a/test/behavior.zig b/test/behavior.zig index c15de44597..c177dd8634 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -105,7 +105,6 @@ test { _ = @import("behavior/math.zig"); _ = @import("behavior/maximum_minimum.zig"); _ = @import("behavior/merge_error_sets.zig"); - _ = @import("behavior/null_llvm.zig"); _ = @import("behavior/popcount.zig"); _ = @import("behavior/saturating_arithmetic.zig"); _ = @import("behavior/sizeof_and_typeof.zig"); @@ -156,7 +155,6 @@ test { _ = @import("behavior/ir_block_deps.zig"); _ = @import("behavior/misc.zig"); _ = @import("behavior/muladd.zig"); - _ = @import("behavior/null_stage1.zig"); _ = @import("behavior/optional_stage1.zig"); _ = @import("behavior/popcount_stage1.zig"); _ = @import("behavior/reflection.zig"); diff --git a/test/behavior/null.zig b/test/behavior/null.zig index 861921d39c..35ecafff80 100644 --- a/test/behavior/null.zig +++ b/test/behavior/null.zig @@ -1,3 +1,4 @@ +const builtin = @import("builtin"); const std = @import("std"); const expect = std.testing.expect; @@ -140,3 +141,50 @@ const Particle = struct { c: u64, d: u64, }; + +test "null literal outside function" { + const is_null = here_is_a_null_literal.context == null; + try expect(is_null); + + const is_non_null = here_is_a_null_literal.context != null; + try expect(!is_non_null); +} + +const SillyStruct = struct { + context: ?i32, +}; + +const here_is_a_null_literal = SillyStruct{ .context = null }; + +test "unwrap optional which is field of global var" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + struct_with_optional.field = null; + if (struct_with_optional.field) |payload| { + _ = payload; + unreachable; + } + struct_with_optional.field = 1234; + if (struct_with_optional.field) |payload| { + try expect(payload == 1234); + } else { + unreachable; + } +} +const StructWithOptional = struct { + field: ?i32, +}; + +var struct_with_optional: StructWithOptional = undefined; + +test "optional types" { + comptime { + const opt_type_struct = StructWithOptionalType{ .t = u8 }; + try expect(opt_type_struct.t != null and opt_type_struct.t.? == u8); + } +} + +const StructWithOptionalType = struct { + t: ?type, +}; diff --git a/test/behavior/null_llvm.zig b/test/behavior/null_llvm.zig deleted file mode 100644 index 25d3c0ca1c..0000000000 --- a/test/behavior/null_llvm.zig +++ /dev/null @@ -1,36 +0,0 @@ -const std = @import("std"); -const expect = std.testing.expect; - -test "null literal outside function" { - const is_null = here_is_a_null_literal.context == null; - try expect(is_null); - - const is_non_null = here_is_a_null_literal.context != null; - try expect(!is_non_null); -} - -const SillyStruct = struct { - context: ?i32, -}; - -const here_is_a_null_literal = SillyStruct{ .context = null }; - -const StructWithOptional = struct { - field: ?i32, -}; - -var struct_with_optional: StructWithOptional = undefined; - -test "unwrap optional which is field of global var" { - struct_with_optional.field = null; - if (struct_with_optional.field) |payload| { - _ = payload; - unreachable; - } - struct_with_optional.field = 1234; - if (struct_with_optional.field) |payload| { - try expect(payload == 1234); - } else { - unreachable; - } -} diff --git a/test/behavior/null_stage1.zig b/test/behavior/null_stage1.zig deleted file mode 100644 index 2b8feea242..0000000000 --- a/test/behavior/null_stage1.zig +++ /dev/null @@ -1,37 +0,0 @@ -const expect = @import("std").testing.expect; - -test "if var maybe pointer" { - try expect(shouldBeAPlus1(Particle{ - .a = 14, - .b = 1, - .c = 1, - .d = 1, - }) == 15); -} -fn shouldBeAPlus1(p: Particle) u64 { - var maybe_particle: ?Particle = p; - if (maybe_particle) |*particle| { - particle.a += 1; - } - if (maybe_particle) |particle| { - return particle.a; - } - return 0; -} -const Particle = struct { - a: u64, - b: u64, - c: u64, - d: u64, -}; - -test "optional types" { - comptime { - const opt_type_struct = StructWithOptionalType{ .t = u8 }; - try expect(opt_type_struct.t != null and opt_type_struct.t.? == u8); - } -} - -const StructWithOptionalType = struct { - t: ?type, -}; From 61ed4fe07ad660f3cb4372937ccd5188ce404a44 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 Feb 2022 14:32:24 -0700 Subject: [PATCH 0095/2031] stage1: fix x86 i128 C ABI for extern structs closes #10445 --- src/stage1/analyze.cpp | 13 +++++++++++-- test/stage1/c_abi/cfuncs.c | 38 ++++++++++++++++++++++++++++++++++++++ test/stage1/c_abi/main.zig | 27 +++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp index 0dcf1fcc06..116a205df2 100644 --- a/src/stage1/analyze.cpp +++ b/src/stage1/analyze.cpp @@ -8684,14 +8684,23 @@ static Error resolve_llvm_c_abi_type(CodeGen *g, ZigType *ty) { if (ty->data.structure.fields[i]->offset >= 8) { eightbyte_index = 1; } - X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields[i]->type_entry); + ZigType *field_ty = ty->data.structure.fields[i]->type_entry; + X64CABIClass field_class = type_c_abi_x86_64_class(g, field_ty); if (field_class == X64CABIClass_INTEGER) { type_classes[eightbyte_index] = X64CABIClass_INTEGER; } else if (type_classes[eightbyte_index] == X64CABIClass_Unknown) { type_classes[eightbyte_index] = field_class; } - type_sizes[eightbyte_index] += ty->data.structure.fields[i]->type_entry->abi_size; + if (field_ty->abi_size > 8) { + assert(eightbyte_index == 0); + type_sizes[0] = 8; + type_sizes[1] = field_ty->abi_size - 8; + type_classes[1] = type_classes[0]; + eightbyte_index = 1; + } else { + type_sizes[eightbyte_index] += field_ty->abi_size; + } } LLVMTypeRef return_elem_types[] = { diff --git a/test/stage1/c_abi/cfuncs.c b/test/stage1/c_abi/cfuncs.c index a2a5895ab4..28009e6fc7 100644 --- a/test/stage1/c_abi/cfuncs.c +++ b/test/stage1/c_abi/cfuncs.c @@ -11,14 +11,26 @@ static void assert_or_panic(bool ok) { } } +struct i128 { + __int128 value; +}; + +struct u128 { + unsigned __int128 value; +}; + void zig_u8(uint8_t); void zig_u16(uint16_t); void zig_u32(uint32_t); void zig_u64(uint64_t); +void zig_u128(unsigned __int128); +void zig_struct_u128(struct u128); void zig_i8(int8_t); void zig_i16(int16_t); void zig_i32(int32_t); void zig_i64(int64_t); +void zig_i128(__int128); +void zig_struct_i128(struct i128); void zig_five_integers(int32_t, int32_t, int32_t, int32_t, int32_t); void zig_f32(float); @@ -130,11 +142,21 @@ void run_c_tests(void) { zig_u16(0xfffe); zig_u32(0xfffffffd); zig_u64(0xfffffffffffffffc); + zig_u128(0xfffffffffffffffc); + { + struct u128 s = {0xfffffffffffffffc}; + zig_struct_u128(s); + } zig_i8(-1); zig_i16(-2); zig_i32(-3); zig_i64(-4); + zig_i128(-5); + { + struct i128 s = {-6}; + zig_struct_i128(s); + } zig_five_integers(12, 34, 56, 78, 90); zig_f32(12.34f); @@ -221,6 +243,14 @@ void c_u64(uint64_t x) { assert_or_panic(x == 0xfffffffffffffffcULL); } +void c_u128(unsigned __int128 x) { + assert_or_panic(x == 0xfffffffffffffffcULL); +} + +void c_struct_u128(struct u128 x) { + assert_or_panic(x.value == 0xfffffffffffffffcULL); +} + void c_i8(int8_t x) { assert_or_panic(x == -1); } @@ -237,6 +267,14 @@ void c_i64(int64_t x) { assert_or_panic(x == -4); } +void c_i128(__int128 x) { + assert_or_panic(x == -5); +} + +void c_struct_i128(struct i128 x) { + assert_or_panic(x.value == -6); +} + void c_f32(float x) { assert_or_panic(x == 12.34f); } diff --git a/test/stage1/c_abi/main.zig b/test/stage1/c_abi/main.zig index abd9be4922..132f440dfc 100644 --- a/test/stage1/c_abi/main.zig +++ b/test/stage1/c_abi/main.zig @@ -16,10 +16,14 @@ extern fn c_u8(u8) void; extern fn c_u16(u16) void; extern fn c_u32(u32) void; extern fn c_u64(u64) void; +extern fn c_u128(u128) void; +extern fn c_struct_u128(U128) void; extern fn c_i8(i8) void; extern fn c_i16(i16) void; extern fn c_i32(i32) void; extern fn c_i64(i64) void; +extern fn c_i128(i128) void; +extern fn c_struct_i128(I128) void; // On windows x64, the first 4 are passed via registers, others on the stack. extern fn c_five_integers(i32, i32, i32, i32, i32) void; @@ -37,11 +41,15 @@ test "C ABI integers" { c_u16(0xfffe); c_u32(0xfffffffd); c_u64(0xfffffffffffffffc); + c_u128(0xfffffffffffffffc); + c_struct_u128(.{ .value = 0xfffffffffffffffc }); c_i8(-1); c_i16(-2); c_i32(-3); c_i64(-4); + c_i128(-5); + c_struct_i128(.{ .value = -6 }); c_five_integers(12, 34, 56, 78, 90); } @@ -57,6 +65,9 @@ export fn zig_u32(x: u32) void { export fn zig_u64(x: u64) void { expect(x == 0xfffffffffffffffc) catch @panic("test failure"); } +export fn zig_u128(x: u128) void { + expect(x == 0xfffffffffffffffc) catch @panic("test failure"); +} export fn zig_i8(x: i8) void { expect(x == -1) catch @panic("test failure"); } @@ -69,6 +80,22 @@ export fn zig_i32(x: i32) void { export fn zig_i64(x: i64) void { expect(x == -4) catch @panic("test failure"); } +export fn zig_i128(x: i128) void { + expect(x == -5) catch @panic("test failure"); +} + +const I128 = extern struct { + value: i128, +}; +const U128 = extern struct { + value: u128, +}; +export fn zig_struct_i128(a: I128) void { + expect(a.value == -6) catch @panic("test failure"); +} +export fn zig_struct_u128(a: U128) void { + expect(a.value == 0xfffffffffffffffc) catch @panic("test failure"); +} extern fn c_f32(f32) void; extern fn c_f64(f64) void; From e06ac9537e94cf4863de7ca64112d7a63af60008 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Feb 2022 21:10:29 -0700 Subject: [PATCH 0096/2031] stage2: fix x86_64-windows C ABI It didn't return integer status for pointers and also it incorrectly returned memory for optionals sometimes. --- src/arch/x86_64/abi.zig | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index a6c71d398f..b0ab1acefd 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -18,10 +18,34 @@ pub fn classifyWindows(ty: Type, target: Target) Class { else => return .memory, } return switch (ty.zigTypeTag()) { - .Int, .Bool, .Enum, .Void, .NoReturn, .ErrorSet, .Struct, .Union => .integer, - .Optional => if (ty.isPtrLikeOptional()) return .integer else return .memory, + .Pointer, + .Int, + .Bool, + .Enum, + .Void, + .NoReturn, + .ErrorSet, + .Struct, + .Union, + .Optional, + .Array, + .ErrorUnion, + .AnyFrame, + .Frame, + => .integer, + .Float, .Vector => .sse, - else => unreachable, + + .Type, + .ComptimeFloat, + .ComptimeInt, + .Undefined, + .Null, + .BoundFn, + .Fn, + .Opaque, + .EnumLiteral, + => unreachable, }; } From a67893b0e124b95f5e1fade0245fef7ebb28b190 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Feb 2022 21:11:53 -0700 Subject: [PATCH 0097/2031] stage1: fix x86_64-windows C ABI classification logic 16 bytes vectors are special cased because compiler-rt currently relies on this. --- src/stage1/analyze.cpp | 64 +++++++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp index 116a205df2..ff925f265f 100644 --- a/src/stage1/analyze.cpp +++ b/src/stage1/analyze.cpp @@ -8279,23 +8279,53 @@ Error file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents_buf) { static X64CABIClass type_windows_abi_x86_64_class(CodeGen *g, ZigType *ty, size_t ty_size) { // https://docs.microsoft.com/en-gb/cpp/build/x64-calling-convention?view=vs-2017 + switch (ty_size) { + case 1: + case 2: + case 4: + case 8: + break; + case 16: + return (ty->id == ZigTypeIdVector) ? X64CABIClass_SSE : X64CABIClass_MEMORY; + default: + return X64CABIClass_MEMORY; + } switch (ty->id) { - case ZigTypeIdEnum: + case ZigTypeIdInvalid: + case ZigTypeIdMetaType: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdNull: + case ZigTypeIdUndefined: + case ZigTypeIdBoundFn: + case ZigTypeIdOpaque: + case ZigTypeIdEnumLiteral: + zig_unreachable(); + + case ZigTypeIdFn: + case ZigTypeIdPointer: case ZigTypeIdInt: case ZigTypeIdBool: + case ZigTypeIdEnum: + case ZigTypeIdVoid: + case ZigTypeIdUnreachable: + case ZigTypeIdErrorSet: + case ZigTypeIdErrorUnion: + case ZigTypeIdStruct: + case ZigTypeIdUnion: + case ZigTypeIdOptional: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: return X64CABIClass_INTEGER; + case ZigTypeIdFloat: case ZigTypeIdVector: return X64CABIClass_SSE; - case ZigTypeIdStruct: - case ZigTypeIdUnion: { - if (ty_size <= 8) - return X64CABIClass_INTEGER; - return X64CABIClass_MEMORY; - } - default: + + case ZigTypeIdArray: return X64CABIClass_Unknown; } + zig_unreachable(); } static X64CABIClass type_system_V_abi_x86_64_class(CodeGen *g, ZigType *ty, size_t ty_size) { @@ -8374,17 +8404,19 @@ static X64CABIClass type_system_V_abi_x86_64_class(CodeGen *g, ZigType *ty, size X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) { Error err; - const size_t ty_size = type_size(g, ty); + + if (g->zig_target->os == OsWindows || g->zig_target->os == OsUefi) { + return type_windows_abi_x86_64_class(g, ty, ty_size); + } + ZigType *ptr_type; if ((err = get_codegen_ptr_type(g, ty, &ptr_type))) return X64CABIClass_Unknown; if (ptr_type != nullptr) return X64CABIClass_INTEGER; - if (g->zig_target->os == OsWindows || g->zig_target->os == OsUefi) { - return type_windows_abi_x86_64_class(g, ty, ty_size); - } else if (g->zig_target->arch == ZigLLVM_aarch64 || - g->zig_target->arch == ZigLLVM_aarch64_be) + if (g->zig_target->arch == ZigLLVM_aarch64 || + g->zig_target->arch == ZigLLVM_aarch64_be) { X64CABIClass result = type_system_V_abi_x86_64_class(g, ty, ty_size); return (result == X64CABIClass_MEMORY) ? X64CABIClass_MEMORY_nobyval : result; @@ -8989,8 +9021,12 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS struct_type->data.structure.llvm_full_type_queue_index = SIZE_MAX; } - if (struct_type->abi_size <= 16 && (struct_type->data.structure.layout == ContainerLayoutExtern || struct_type->data.structure.layout == ContainerLayoutPacked)) + if (struct_type->abi_size <= 16 && + (struct_type->data.structure.layout == ContainerLayoutExtern || + struct_type->data.structure.layout == ContainerLayoutPacked)) + { resolve_llvm_c_abi_type(g, struct_type); + } } // This is to be used instead of void for debug info types, to avoid tripping From a35eb9fe8ae95353155423901089cbc03d036bf3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Feb 2022 21:12:32 -0700 Subject: [PATCH 0098/2031] c_abi tests: allow passing standard target options --- test/stage1/c_abi/build.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/stage1/c_abi/build.zig b/test/stage1/c_abi/build.zig index cf21d403f7..b9151f6dda 100644 --- a/test/stage1/c_abi/build.zig +++ b/test/stage1/c_abi/build.zig @@ -2,15 +2,18 @@ const Builder = @import("std").build.Builder; pub fn build(b: *Builder) void { const rel_opts = b.standardReleaseOptions(); + const target = b.standardTargetOptions(.{}); const c_obj = b.addObject("cfuncs", null); c_obj.addCSourceFile("cfuncs.c", &[_][]const u8{"-std=c99"}); c_obj.setBuildMode(rel_opts); c_obj.linkSystemLibrary("c"); + c_obj.target = target; const main = b.addTest("main.zig"); main.setBuildMode(rel_opts); main.addObject(c_obj); + main.target = target; const test_step = b.step("test", "Test the program"); test_step.dependOn(&main.step); From 08808701d2b70de41535bb35e4724c8279ea07b7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Feb 2022 21:12:50 -0700 Subject: [PATCH 0099/2031] C ABI tests: give a clue in addition to "test failure" --- test/stage1/c_abi/main.zig | 72 +++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/test/stage1/c_abi/main.zig b/test/stage1/c_abi/main.zig index 132f440dfc..bd3fec15a8 100644 --- a/test/stage1/c_abi/main.zig +++ b/test/stage1/c_abi/main.zig @@ -29,11 +29,11 @@ extern fn c_struct_i128(I128) void; extern fn c_five_integers(i32, i32, i32, i32, i32) void; export fn zig_five_integers(a: i32, b: i32, c: i32, d: i32, e: i32) void { - expect(a == 12) catch @panic("test failure"); - expect(b == 34) catch @panic("test failure"); - expect(c == 56) catch @panic("test failure"); - expect(d == 78) catch @panic("test failure"); - expect(e == 90) catch @panic("test failure"); + expect(a == 12) catch @panic("test failure: zig_five_integers 12"); + expect(b == 34) catch @panic("test failure: zig_five_integers 34"); + expect(c == 56) catch @panic("test failure: zig_five_integers 56"); + expect(d == 78) catch @panic("test failure: zig_five_integers 78"); + expect(e == 90) catch @panic("test failure: zig_five_integers 90"); } test "C ABI integers" { @@ -54,34 +54,34 @@ test "C ABI integers" { } export fn zig_u8(x: u8) void { - expect(x == 0xff) catch @panic("test failure"); + expect(x == 0xff) catch @panic("test failure: zig_u8"); } export fn zig_u16(x: u16) void { - expect(x == 0xfffe) catch @panic("test failure"); + expect(x == 0xfffe) catch @panic("test failure: zig_u16"); } export fn zig_u32(x: u32) void { - expect(x == 0xfffffffd) catch @panic("test failure"); + expect(x == 0xfffffffd) catch @panic("test failure: zig_u32"); } export fn zig_u64(x: u64) void { - expect(x == 0xfffffffffffffffc) catch @panic("test failure"); + expect(x == 0xfffffffffffffffc) catch @panic("test failure: zig_u64"); } export fn zig_u128(x: u128) void { - expect(x == 0xfffffffffffffffc) catch @panic("test failure"); + expect(x == 0xfffffffffffffffc) catch @panic("test failure: zig_u128"); } export fn zig_i8(x: i8) void { - expect(x == -1) catch @panic("test failure"); + expect(x == -1) catch @panic("test failure: zig_i8"); } export fn zig_i16(x: i16) void { - expect(x == -2) catch @panic("test failure"); + expect(x == -2) catch @panic("test failure: zig_i16"); } export fn zig_i32(x: i32) void { - expect(x == -3) catch @panic("test failure"); + expect(x == -3) catch @panic("test failure: zig_i32"); } export fn zig_i64(x: i64) void { - expect(x == -4) catch @panic("test failure"); + expect(x == -4) catch @panic("test failure: zig_i64"); } export fn zig_i128(x: i128) void { - expect(x == -5) catch @panic("test failure"); + expect(x == -5) catch @panic("test failure: zig_i128"); } const I128 = extern struct { @@ -91,10 +91,10 @@ const U128 = extern struct { value: u128, }; export fn zig_struct_i128(a: I128) void { - expect(a.value == -6) catch @panic("test failure"); + expect(a.value == -6) catch @panic("test failure: zig_struct_i128"); } export fn zig_struct_u128(a: U128) void { - expect(a.value == 0xfffffffffffffffc) catch @panic("test failure"); + expect(a.value == 0xfffffffffffffffc) catch @panic("test failure: zig_struct_u128"); } extern fn c_f32(f32) void; @@ -104,11 +104,11 @@ extern fn c_f64(f64) void; extern fn c_five_floats(f32, f32, f32, f32, f32) void; export fn zig_five_floats(a: f32, b: f32, c: f32, d: f32, e: f32) void { - expect(a == 1.0) catch @panic("test failure"); - expect(b == 2.0) catch @panic("test failure"); - expect(c == 3.0) catch @panic("test failure"); - expect(d == 4.0) catch @panic("test failure"); - expect(e == 5.0) catch @panic("test failure"); + expect(a == 1.0) catch @panic("test failure: zig_five_floats 1.0"); + expect(b == 2.0) catch @panic("test failure: zig_five_floats 2.0"); + expect(c == 3.0) catch @panic("test failure: zig_five_floats 3.0"); + expect(d == 4.0) catch @panic("test failure: zig_five_floats 4.0"); + expect(e == 5.0) catch @panic("test failure: zig_five_floats 5.0"); } test "C ABI floats" { @@ -118,10 +118,10 @@ test "C ABI floats" { } export fn zig_f32(x: f32) void { - expect(x == 12.34) catch @panic("test failure"); + expect(x == 12.34) catch @panic("test failure: zig_f32"); } export fn zig_f64(x: f64) void { - expect(x == 56.78) catch @panic("test failure"); + expect(x == 56.78) catch @panic("test failure: zig_f64"); } extern fn c_ptr(*anyopaque) void; @@ -131,7 +131,7 @@ test "C ABI pointer" { } export fn zig_ptr(x: *anyopaque) void { - expect(@ptrToInt(x) == 0xdeadbeef) catch @panic("test failure"); + expect(@ptrToInt(x) == 0xdeadbeef) catch @panic("test failure: zig_ptr"); } extern fn c_bool(bool) void; @@ -141,7 +141,7 @@ test "C ABI bool" { } export fn zig_bool(x: bool) void { - expect(x) catch @panic("test failure"); + expect(x) catch @panic("test failure: zig_bool"); } const BigStruct = extern struct { @@ -165,11 +165,11 @@ test "C ABI big struct" { } export fn zig_big_struct(x: BigStruct) void { - expect(x.a == 1) catch @panic("test failure"); - expect(x.b == 2) catch @panic("test failure"); - expect(x.c == 3) catch @panic("test failure"); - expect(x.d == 4) catch @panic("test failure"); - expect(x.e == 5) catch @panic("test failure"); + expect(x.a == 1) catch @panic("test failure: zig_big_struct 1"); + expect(x.b == 2) catch @panic("test failure: zig_big_struct 2"); + expect(x.c == 3) catch @panic("test failure: zig_big_struct 3"); + expect(x.d == 4) catch @panic("test failure: zig_big_struct 4"); + expect(x.e == 5) catch @panic("test failure: zig_big_struct 5"); } const BigUnion = extern union { @@ -191,11 +191,11 @@ test "C ABI big union" { } export fn zig_big_union(x: BigUnion) void { - expect(x.a.a == 1) catch @panic("test failure"); - expect(x.a.b == 2) catch @panic("test failure"); - expect(x.a.c == 3) catch @panic("test failure"); - expect(x.a.d == 4) catch @panic("test failure"); - expect(x.a.e == 5) catch @panic("test failure"); + expect(x.a.a == 1) catch @panic("test failure: zig_big_union a"); + expect(x.a.b == 2) catch @panic("test failure: zig_big_union b"); + expect(x.a.c == 3) catch @panic("test failure: zig_big_union c"); + expect(x.a.d == 4) catch @panic("test failure: zig_big_union d"); + expect(x.a.e == 5) catch @panic("test failure: zig_big_union e"); } const MedStructMixed = extern struct { From 77a6031edbd17dfdf654f24d139f27ac7adeb82f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Feb 2022 21:15:00 -0700 Subject: [PATCH 0100/2031] C ABI: these tests are not passing yet on Windows I was too greedy --- test/stage1/c_abi/cfuncs.c | 12 ------------ test/stage1/c_abi/main.zig | 10 ---------- 2 files changed, 22 deletions(-) diff --git a/test/stage1/c_abi/cfuncs.c b/test/stage1/c_abi/cfuncs.c index 28009e6fc7..f5c90adba0 100644 --- a/test/stage1/c_abi/cfuncs.c +++ b/test/stage1/c_abi/cfuncs.c @@ -23,13 +23,11 @@ void zig_u8(uint8_t); void zig_u16(uint16_t); void zig_u32(uint32_t); void zig_u64(uint64_t); -void zig_u128(unsigned __int128); void zig_struct_u128(struct u128); void zig_i8(int8_t); void zig_i16(int16_t); void zig_i32(int32_t); void zig_i64(int64_t); -void zig_i128(__int128); void zig_struct_i128(struct i128); void zig_five_integers(int32_t, int32_t, int32_t, int32_t, int32_t); @@ -142,7 +140,6 @@ void run_c_tests(void) { zig_u16(0xfffe); zig_u32(0xfffffffd); zig_u64(0xfffffffffffffffc); - zig_u128(0xfffffffffffffffc); { struct u128 s = {0xfffffffffffffffc}; zig_struct_u128(s); @@ -152,7 +149,6 @@ void run_c_tests(void) { zig_i16(-2); zig_i32(-3); zig_i64(-4); - zig_i128(-5); { struct i128 s = {-6}; zig_struct_i128(s); @@ -243,10 +239,6 @@ void c_u64(uint64_t x) { assert_or_panic(x == 0xfffffffffffffffcULL); } -void c_u128(unsigned __int128 x) { - assert_or_panic(x == 0xfffffffffffffffcULL); -} - void c_struct_u128(struct u128 x) { assert_or_panic(x.value == 0xfffffffffffffffcULL); } @@ -267,10 +259,6 @@ void c_i64(int64_t x) { assert_or_panic(x == -4); } -void c_i128(__int128 x) { - assert_or_panic(x == -5); -} - void c_struct_i128(struct i128 x) { assert_or_panic(x.value == -6); } diff --git a/test/stage1/c_abi/main.zig b/test/stage1/c_abi/main.zig index bd3fec15a8..71a53bedea 100644 --- a/test/stage1/c_abi/main.zig +++ b/test/stage1/c_abi/main.zig @@ -16,13 +16,11 @@ extern fn c_u8(u8) void; extern fn c_u16(u16) void; extern fn c_u32(u32) void; extern fn c_u64(u64) void; -extern fn c_u128(u128) void; extern fn c_struct_u128(U128) void; extern fn c_i8(i8) void; extern fn c_i16(i16) void; extern fn c_i32(i32) void; extern fn c_i64(i64) void; -extern fn c_i128(i128) void; extern fn c_struct_i128(I128) void; // On windows x64, the first 4 are passed via registers, others on the stack. @@ -41,14 +39,12 @@ test "C ABI integers" { c_u16(0xfffe); c_u32(0xfffffffd); c_u64(0xfffffffffffffffc); - c_u128(0xfffffffffffffffc); c_struct_u128(.{ .value = 0xfffffffffffffffc }); c_i8(-1); c_i16(-2); c_i32(-3); c_i64(-4); - c_i128(-5); c_struct_i128(.{ .value = -6 }); c_five_integers(12, 34, 56, 78, 90); } @@ -65,9 +61,6 @@ export fn zig_u32(x: u32) void { export fn zig_u64(x: u64) void { expect(x == 0xfffffffffffffffc) catch @panic("test failure: zig_u64"); } -export fn zig_u128(x: u128) void { - expect(x == 0xfffffffffffffffc) catch @panic("test failure: zig_u128"); -} export fn zig_i8(x: i8) void { expect(x == -1) catch @panic("test failure: zig_i8"); } @@ -80,9 +73,6 @@ export fn zig_i32(x: i32) void { export fn zig_i64(x: i64) void { expect(x == -4) catch @panic("test failure: zig_i64"); } -export fn zig_i128(x: i128) void { - expect(x == -5) catch @panic("test failure: zig_i128"); -} const I128 = extern struct { value: i128, From 470c8ca48c32d34da06e4741b8f81b6eb0d72fd7 Mon Sep 17 00:00:00 2001 From: billzez <77312308+billzez@users.noreply.github.com> Date: Tue, 8 Feb 2022 23:35:48 -0500 Subject: [PATCH 0101/2031] update RwLock to use static initialization (#10838) --- lib/std/Thread.zig | 1 + lib/std/Thread/RwLock.zig | 77 +++++++-------------------------------- 2 files changed, 14 insertions(+), 64 deletions(-) diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 182f7ccb6b..1e54053146 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -16,6 +16,7 @@ pub const StaticResetEvent = @import("Thread/StaticResetEvent.zig"); pub const Mutex = @import("Thread/Mutex.zig"); pub const Semaphore = @import("Thread/Semaphore.zig"); pub const Condition = @import("Thread/Condition.zig"); +pub const RwLock = @import("Thread/RwLock.zig"); pub const use_pthreads = target.os.tag != .windows and target.os.tag != .wasi and builtin.link_libc; const is_gnu = target.abi.isGnu(); diff --git a/lib/std/Thread/RwLock.zig b/lib/std/Thread/RwLock.zig index cfe06c76e8..6cce7d1217 100644 --- a/lib/std/Thread/RwLock.zig +++ b/lib/std/Thread/RwLock.zig @@ -3,15 +3,12 @@ //! This API requires being initialized at runtime, and initialization //! can fail. Once initialized, the core operations cannot fail. -impl: Impl, +impl: Impl = .{}, const RwLock = @This(); const std = @import("../std.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; -const Mutex = std.Thread.Mutex; -const Semaphore = std.Semaphore; -const CondVar = std.CondVar; pub const Impl = if (builtin.single_threaded) SingleThreadedRwLock @@ -20,14 +17,6 @@ else if (std.Thread.use_pthreads) else DefaultRwLock; -pub fn init(rwl: *RwLock) void { - return rwl.impl.init(); -} - -pub fn deinit(rwl: *RwLock) void { - return rwl.impl.deinit(); -} - /// Attempts to obtain exclusive lock ownership. /// Returns `true` if the lock is obtained, `false` otherwise. pub fn tryLock(rwl: *RwLock) bool { @@ -64,20 +53,8 @@ pub fn unlockShared(rwl: *RwLock) void { /// Single-threaded applications use this for deadlock checks in /// debug mode, and no-ops in release modes. pub const SingleThreadedRwLock = struct { - state: enum { unlocked, locked_exclusive, locked_shared }, - shared_count: usize, - - pub fn init(rwl: *SingleThreadedRwLock) void { - rwl.* = .{ - .state = .unlocked, - .shared_count = 0, - }; - } - - pub fn deinit(rwl: *SingleThreadedRwLock) void { - assert(rwl.state == .unlocked); - assert(rwl.shared_count == 0); - } + state: enum { unlocked, locked_exclusive, locked_shared } = .unlocked, + shared_count: usize = 0, /// Attempts to obtain exclusive lock ownership. /// Returns `true` if the lock is obtained, `false` otherwise. @@ -152,55 +129,41 @@ pub const SingleThreadedRwLock = struct { }; pub const PthreadRwLock = struct { - rwlock: pthread_rwlock_t, - - pub fn init(rwl: *PthreadRwLock) void { - rwl.* = .{ .rwlock = .{} }; - } - - pub fn deinit(rwl: *PthreadRwLock) void { - const safe_rc: std.os.E = switch (builtin.os.tag) { - .dragonfly, .netbsd => .AGAIN, - else => .SUCCESS, - }; - const rc = std.c.pthread_rwlock_destroy(&rwl.rwlock); - assert(rc == .SUCCESS or rc == safe_rc); - rwl.* = undefined; - } + rwlock: std.c.pthread_rwlock_t = .{}, pub fn tryLock(rwl: *PthreadRwLock) bool { - return pthread_rwlock_trywrlock(&rwl.rwlock) == .SUCCESS; + return std.c.pthread_rwlock_trywrlock(&rwl.rwlock) == .SUCCESS; } pub fn lock(rwl: *PthreadRwLock) void { - const rc = pthread_rwlock_wrlock(&rwl.rwlock); + const rc = std.c.pthread_rwlock_wrlock(&rwl.rwlock); assert(rc == .SUCCESS); } pub fn unlock(rwl: *PthreadRwLock) void { - const rc = pthread_rwlock_unlock(&rwl.rwlock); + const rc = std.c.pthread_rwlock_unlock(&rwl.rwlock); assert(rc == .SUCCESS); } pub fn tryLockShared(rwl: *PthreadRwLock) bool { - return pthread_rwlock_tryrdlock(&rwl.rwlock) == .SUCCESS; + return std.c.pthread_rwlock_tryrdlock(&rwl.rwlock) == .SUCCESS; } pub fn lockShared(rwl: *PthreadRwLock) void { - const rc = pthread_rwlock_rdlock(&rwl.rwlock); + const rc = std.c.pthread_rwlock_rdlock(&rwl.rwlock); assert(rc == .SUCCESS); } pub fn unlockShared(rwl: *PthreadRwLock) void { - const rc = pthread_rwlock_unlock(&rwl.rwlock); + const rc = std.c.pthread_rwlock_unlock(&rwl.rwlock); assert(rc == .SUCCESS); } }; pub const DefaultRwLock = struct { - state: usize, - mutex: Mutex, - semaphore: Semaphore, + state: usize = 0, + mutex: std.Thread.Mutex = .{}, + semaphore: std.Thread.Semaphore = .{}, const IS_WRITING: usize = 1; const WRITER: usize = 1 << 1; @@ -209,20 +172,6 @@ pub const DefaultRwLock = struct { const READER_MASK: usize = std.math.maxInt(Count) << @ctz(usize, READER); const Count = std.meta.Int(.unsigned, @divFloor(std.meta.bitCount(usize) - 1, 2)); - pub fn init(rwl: *DefaultRwLock) void { - rwl.* = .{ - .state = 0, - .mutex = Mutex.init(), - .semaphore = Semaphore.init(0), - }; - } - - pub fn deinit(rwl: *DefaultRwLock) void { - rwl.semaphore.deinit(); - rwl.mutex.deinit(); - rwl.* = undefined; - } - pub fn tryLock(rwl: *DefaultRwLock) bool { if (rwl.mutex.tryLock()) { const state = @atomicLoad(usize, &rwl.state, .SeqCst); From 59418d1bf6f82866a1c8f3b35fd815bb7add5129 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Feb 2022 22:09:41 -0700 Subject: [PATCH 0102/2031] Sema: fix Value.intFitsInType for comptime int --- src/value.zig | 34 +++++++++++++++++++++------------- test/behavior/slice.zig | 2 -- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/value.zig b/src/value.zig index acc3fa3d74..9e1f4c0ed6 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1331,12 +1331,16 @@ pub const Value = extern union { .one, .bool_true, - => { - const info = ty.intInfo(target); - return switch (info.signedness) { - .signed => info.bits >= 2, - .unsigned => info.bits >= 1, - }; + => switch (ty.zigTypeTag()) { + .Int => { + const info = ty.intInfo(target); + return switch (info.signedness) { + .signed => info.bits >= 2, + .unsigned => info.bits >= 1, + }; + }, + .ComptimeInt => return true, + else => unreachable, }, .int_u64 => switch (ty.zigTypeTag()) { @@ -1390,13 +1394,17 @@ pub const Value = extern union { .decl_ref, .function, .variable, - => { - const info = ty.intInfo(target); - const ptr_bits = target.cpu.arch.ptrBitWidth(); - return switch (info.signedness) { - .signed => info.bits > ptr_bits, - .unsigned => info.bits >= ptr_bits, - }; + => switch (ty.zigTypeTag()) { + .Int => { + const info = ty.intInfo(target); + const ptr_bits = target.cpu.arch.ptrBitWidth(); + return switch (info.signedness) { + .signed => info.bits > ptr_bits, + .unsigned => info.bits >= ptr_bits, + }; + }, + .ComptimeInt => return true, + else => unreachable, }, else => unreachable, diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 4ec5f11817..902ba49a6f 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -245,8 +245,6 @@ test "C pointer slice access" { } test "comptime slices are disambiguated" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - try expect(sliceSum(&[_]u8{ 1, 2 }) == 3); try expect(sliceSum(&[_]u8{ 3, 4 }) == 7); } From 1678825c1450b29a1016bba62511388b3e539cd8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Feb 2022 22:51:46 -0700 Subject: [PATCH 0103/2031] Sema: fix `@ptrCast` from slices Also, fix allocations in comptime contexts with alignments. --- src/Sema.zig | 31 +++++++++++++++++++++---------- test/behavior/slice.zig | 2 -- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 69b9adc54b..b202cb696f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2318,7 +2318,7 @@ fn zirAllocExtended( else Type.initTag(.inferred_alloc_mut); - if (small.is_comptime) { + if (block.is_comptime or small.is_comptime) { if (small.has_type) { return sema.analyzeComptimeAlloc(block, var_ty, alignment, ty_src); } else { @@ -2379,7 +2379,10 @@ fn zirAllocInferredComptime( sema.src = src; return sema.addConstant( inferred_alloc_ty, - try Value.Tag.inferred_alloc_comptime.create(sema.arena, undefined), + try Value.Tag.inferred_alloc_comptime.create(sema.arena, .{ + .decl = undefined, + .alignment = 0, + }), ); } @@ -2440,7 +2443,10 @@ fn zirAllocInferred( if (block.is_comptime) { return sema.addConstant( inferred_alloc_ty, - try Value.Tag.inferred_alloc_comptime.create(sema.arena, undefined), + try Value.Tag.inferred_alloc_comptime.create(sema.arena, .{ + .decl = undefined, + .alignment = 0, + }), ); } @@ -11341,7 +11347,14 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const operand_ty = sema.typeOf(operand); try sema.checkPtrType(block, dest_ty_src, dest_ty); try sema.checkPtrOperand(block, operand_src, operand_ty); - return sema.coerceCompatiblePtrs(block, dest_ty, operand, operand_src); + if (dest_ty.isSlice()) { + return sema.fail(block, dest_ty_src, "illegal pointer cast to slice", .{}); + } + const ptr = if (operand_ty.isSlice()) + try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty) + else + operand; + return sema.coerceCompatiblePtrs(block, dest_ty, ptr, operand_src); } fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -13081,7 +13094,7 @@ fn fieldVal( try sema.analyzeLoad(block, src, object, object_src) else object; - return sema.analyzeSlicePtr(block, src, slice, inner_ty, object_src); + return sema.analyzeSlicePtr(block, object_src, slice, inner_ty); } else if (mem.eql(u8, field_name, "len")) { const slice = if (is_pointer_to) try sema.analyzeLoad(block, src, object, object_src) @@ -15584,19 +15597,17 @@ fn analyzeLoad( fn analyzeSlicePtr( sema: *Sema, block: *Block, - src: LazySrcLoc, + slice_src: LazySrcLoc, slice: Air.Inst.Ref, slice_ty: Type, - slice_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { const buf = try sema.arena.create(Type.SlicePtrFieldTypeBuffer); const result_ty = slice_ty.slicePtrFieldType(buf); - if (try sema.resolveMaybeUndefVal(block, slice_src, slice)) |val| { if (val.isUndef()) return sema.addConstUndef(result_ty); return sema.addConstant(result_ty, val.slicePtr()); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, slice_src); return block.addTyOp(.slice_ptr, result_ty, slice); } @@ -15729,7 +15740,7 @@ fn analyzeSlice( } const ptr = if (slice_ty.isSlice()) - try sema.analyzeSlicePtr(block, src, ptr_or_slice, slice_ty, ptr_src) + try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty) else ptr_or_slice; diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 902ba49a6f..64bd972ead 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -312,8 +312,6 @@ test "empty array to slice" { } test "@ptrCast slice to pointer" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - const S = struct { fn doTheTest() !void { var array align(@alignOf(u16)) = [5]u8{ 0xff, 0xff, 0xff, 0xff, 0xff }; From f4fa32a63219917e8fb26f43cbd2d97b17e0aeee Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Feb 2022 23:02:13 -0700 Subject: [PATCH 0104/2031] Sema: fix `@typeInfo` for pointers returning 0 alignment --- src/Sema.zig | 7 ++++++- src/type.zig | 2 +- test/behavior/type_info.zig | 10 ---------- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index b202cb696f..df5013fbaf 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9651,6 +9651,11 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }, .Pointer => { const info = ty.ptrInfo().data; + const alignment = if (info.@"align" != 0) + info.@"align" + else + info.pointee_type.abiAlignment(target); + const field_values = try sema.arena.alloc(Value, 8); // size: Size, field_values[0] = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.size)); @@ -9659,7 +9664,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // is_volatile: bool, field_values[2] = if (info.@"volatile") Value.@"true" else Value.@"false"; // alignment: comptime_int, - field_values[3] = try Value.Tag.int_u64.create(sema.arena, info.@"align"); + field_values[3] = try Value.Tag.int_u64.create(sema.arena, alignment); // address_space: AddressSpace field_values[4] = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.@"addrspace")); // child: type, diff --git a/src/type.zig b/src/type.zig index e3a4b3d60a..769e48ccc5 100644 --- a/src/type.zig +++ b/src/type.zig @@ -4687,7 +4687,7 @@ pub const Type = extern union { pub const Data = struct { pointee_type: Type, sentinel: ?Value = null, - /// If zero use pointee_type.AbiAlign() + /// If zero use pointee_type.abiAlignment() @"align": u32 = 0, /// See src/target.zig defaultAddressSpace function for how to obtain /// an appropriate value for this field. diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index 14adc4dad5..9f90088ed9 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -71,8 +71,6 @@ fn testBasic() !void { } test "type info: pointer type info" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - try testPointer(); comptime try testPointer(); } @@ -89,8 +87,6 @@ fn testPointer() !void { } test "type info: unknown length pointer type info" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - try testUnknownLenPtr(); comptime try testUnknownLenPtr(); } @@ -125,8 +121,6 @@ fn testNullTerminatedPtr() !void { } test "type info: slice type info" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - try testSlice(); comptime try testSlice(); } @@ -306,8 +300,6 @@ const TestStruct = packed struct { }; test "type info: opaque info" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - try testOpaque(); comptime try testOpaque(); } @@ -417,8 +409,6 @@ test "type info: TypeId -> TypeInfo impl cast" { } test "sentinel of opaque pointer type" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - const c_void_info = @typeInfo(*anyopaque); try expect(c_void_info.Pointer.sentinel == null); } From 97019bc56d27349e0aeb44faa9d3f738887abe7f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 9 Feb 2022 00:10:53 -0700 Subject: [PATCH 0105/2031] Sema: handle inferred error set tail call When Sema sees a store_node instruction, it now checks for the possibility of this pattern: %a = ret_ptr %b = store(%a, %c) Where %c is an error union. In such case we need to add to the current function's inferred error set, if any. Coercion from error union to error union will be handled ideally if the operand is comptime known. In such case it does the appropriate unwrapping, then wraps again. In the future, coercion from error union to error union should do the same thing for a runtime value; emitting a runtime branch to check if the value is an error or not. `Value.arrayLen` for structs returns the number of fields. This is so that Liveness can use it for the `vector_init` instruction (soon to be renamed to `aggregate_init`). --- src/Air.zig | 3 +- src/Module.zig | 4 +-- src/Sema.zig | 85 +++++++++++++++++++++++++++++++++++++++----------- src/type.zig | 3 +- 4 files changed, 73 insertions(+), 22 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 6888f51963..a044dd6294 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -521,7 +521,8 @@ pub const Inst = struct { /// Some of the elements may be comptime-known. /// Uses the `ty_pl` field, payload is index of an array of elements, each of which /// is a `Ref`. Length of the array is given by the vector type. - /// TODO rename this to `array_init` and make it support array values too. + /// TODO rename this to `aggregate_init` and make it support array values and + /// struct values too. vector_init, /// Communicates an intent to load memory. diff --git a/src/Module.zig b/src/Module.zig index bc806cfb9c..3631e41f25 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3547,7 +3547,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { .code = file.zir, .owner_decl = new_decl, .func = null, - .fn_ret_ty = Type.initTag(.void), + .fn_ret_ty = Type.void, .owner_func = null, }; defer sema.deinit(); @@ -3628,7 +3628,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { .code = zir, .owner_decl = decl, .func = null, - .fn_ret_ty = Type.initTag(.void), + .fn_ret_ty = Type.void, .owner_func = null, }; defer sema.deinit(); diff --git a/src/Sema.zig b/src/Sema.zig index df5013fbaf..38adfb4798 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3187,12 +3187,32 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v const tracy = trace(@src()); defer tracy.end(); - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const zir_tags = sema.code.instructions.items(.tag); + const zir_datas = sema.code.instructions.items(.data); + const inst_data = zir_datas[inst].pl_node; const src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const ptr = sema.resolveInst(extra.lhs); - const value = sema.resolveInst(extra.rhs); - return sema.storePtr(block, src, ptr, value); + const operand = sema.resolveInst(extra.rhs); + + // Check for the possibility of this pattern: + // %a = ret_ptr + // %b = store(%a, %c) + // Where %c is an error union. In such case we need to add to the current function's + // inferred error set, if any. + if (sema.typeOf(operand).zigTypeTag() == .ErrorUnion and + sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) + { + if (Zir.refToIndex(extra.lhs)) |ptr_index| { + if (zir_tags[ptr_index] == .extended and + zir_datas[ptr_index].extended.opcode == .ret_ptr) + { + try sema.addToInferredErrorSet(operand); + } + } + } + + return sema.storePtr(block, src, ptr, operand); } fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -10400,6 +10420,23 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir return always_noreturn; } +fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void { + assert(sema.fn_ret_ty.zigTypeTag() == .ErrorUnion); + + if (sema.fn_ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| { + const op_ty = sema.typeOf(uncasted_operand); + switch (op_ty.zigTypeTag()) { + .ErrorSet => { + try payload.data.addErrorSet(sema.gpa, op_ty); + }, + .ErrorUnion => { + try payload.data.addErrorSet(sema.gpa, op_ty.errorUnionSet()); + }, + else => {}, + } + } +} + fn analyzeRet( sema: *Sema, block: *Block, @@ -10410,18 +10447,7 @@ fn analyzeRet( // add the error tag to the inferred error set of the in-scope function, so // that the coercion below works correctly. if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) { - if (sema.fn_ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| { - const op_ty = sema.typeOf(uncasted_operand); - switch (op_ty.zigTypeTag()) { - .ErrorSet => { - try payload.data.addErrorSet(sema.gpa, op_ty); - }, - .ErrorUnion => { - try payload.data.addErrorSet(sema.gpa, op_ty.errorUnionSet()); - }, - else => {}, - } - } + try sema.addToInferredErrorSet(uncasted_operand); } const operand = try sema.coerce(block, sema.fn_ret_ty, uncasted_operand, src); @@ -14355,9 +14381,32 @@ fn coerce( }, else => {}, }, - .ErrorUnion => { - // T to E!T or E to E!T - return sema.wrapErrorUnion(block, dest_ty, inst, inst_src); + .ErrorUnion => switch (inst_ty.zigTypeTag()) { + .ErrorUnion => { + if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| { + switch (inst_val.tag()) { + .undef => return sema.addConstUndef(dest_ty), + .eu_payload => { + const payload = try sema.addConstant( + inst_ty.errorUnionPayload(), + inst_val.castTag(.eu_payload).?.data, + ); + return sema.wrapErrorUnion(block, dest_ty, payload, inst_src); + }, + else => { + const error_set = try sema.addConstant( + inst_ty.errorUnionSet(), + inst_val, + ); + return sema.wrapErrorUnion(block, dest_ty, error_set, inst_src); + }, + } + } + }, + else => { + // T to E!T or E to E!T + return sema.wrapErrorUnion(block, dest_ty, inst, inst_src); + }, }, .Union => switch (inst_ty.zigTypeTag()) { .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src), diff --git a/src/type.zig b/src/type.zig index 769e48ccc5..0827b2e2d7 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3013,7 +3013,7 @@ pub const Type = extern union { } } - /// Asserts the type is an array or vector. + /// Asserts the type is an array or vector or struct. pub fn arrayLen(ty: Type) u64 { return switch (ty.tag()) { .vector => ty.castTag(.vector).?.data.len, @@ -3022,6 +3022,7 @@ pub const Type = extern union { .array_u8 => ty.castTag(.array_u8).?.data, .array_u8_sentinel_0 => ty.castTag(.array_u8_sentinel_0).?.data, .tuple => ty.castTag(.tuple).?.data.types.len, + .@"struct" => ty.castTag(.@"struct").?.data.fields.count(), else => unreachable, }; From e5ce87f1b198bfcb022e9ea91f2a9a58b1b75026 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 8 Feb 2022 20:33:45 +0100 Subject: [PATCH 0106/2031] stage2: handle decl ref to void types Fixes behavior test 1914 --- src/codegen.zig | 17 ++++++----------- test/behavior/bugs/1914.zig | 4 ---- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index 5873fd439c..d1c249d99d 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -487,19 +487,14 @@ fn lowerDeclRef( return Result{ .appended = {} }; } + const target = bin_file.options.target; + const ptr_width = target.cpu.arch.ptrBitWidth(); const is_fn_body = decl.ty.zigTypeTag() == .Fn; if (!is_fn_body and !decl.ty.hasRuntimeBits()) { - return Result{ - .fail = try ErrorMsg.create( - bin_file.allocator, - src_loc, - "TODO handle void types when lowering decl ref", - .{}, - ), - }; + try code.writer().writeByteNTimes(0xaa, @divExact(ptr_width, 8)); + return Result{ .appended = {} }; } - if (decl.analysis != .complete) return error.AnalysisFail; decl.markAlive(); const vaddr = vaddr: { if (bin_file.cast(link.File.MachO)) |macho_file| { @@ -510,8 +505,8 @@ fn lowerDeclRef( break :vaddr bin_file.getDeclVAddr(decl); }; - const endian = bin_file.options.target.cpu.arch.endian(); - switch (bin_file.options.target.cpu.arch.ptrBitWidth()) { + const endian = target.cpu.arch.endian(); + switch (ptr_width) { 16 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(u16, vaddr), endian), 32 => mem.writeInt(u32, try code.addManyAsArray(4), @intCast(u32, vaddr), endian), 64 => mem.writeInt(u64, try code.addManyAsArray(8), vaddr, endian), diff --git a/test/behavior/bugs/1914.zig b/test/behavior/bugs/1914.zig index 6462937351..4ac2b929a2 100644 --- a/test/behavior/bugs/1914.zig +++ b/test/behavior/bugs/1914.zig @@ -13,8 +13,6 @@ const a = A{ .b_list_pointer = &b_list }; test "segfault bug" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const assert = std.debug.assert; const obj = B{ .a_pointer = &a }; assert(obj.a_pointer == &a); // this makes zig crash @@ -31,8 +29,6 @@ pub const B2 = struct { var b_value = B2{ .pointer_array = &[_]*A2{} }; test "basic stuff" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO std.debug.assert(&b_value == &b_value); } From b28e9e42e07ae553fc35fb47c1ace619405c2b5c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 8 Feb 2022 23:28:39 +0100 Subject: [PATCH 0107/2031] stage2: resolve struct type when lowering struct_field_* --- src/Sema.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Sema.zig b/src/Sema.zig index 38adfb4798..4ff535d86e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -13647,6 +13647,7 @@ fn structFieldPtr( assert(unresolved_struct_ty.zigTypeTag() == .Struct); const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty); + try sema.resolveStructLayout(block, src, struct_ty); const struct_obj = struct_ty.castTag(.@"struct").?.data; const field_index_big = struct_obj.fields.getIndex(field_name) orelse From e588e3873cdb4a7f8e03085d83453f9f3d5e36a7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 8 Feb 2022 23:30:23 +0100 Subject: [PATCH 0108/2031] stage2: export trunc, truncf and truncl --- lib/std/special/c.zig | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index bc6d03bffd..a4aa4f66b2 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -6,7 +6,9 @@ const std = @import("std"); const builtin = @import("builtin"); +const math = std.math; const native_os = builtin.os.tag; +const long_double_is_f128 = builtin.target.longDoubleIsF128(); comptime { // When the self-hosted compiler is further along, all the logic from c_stage1.zig will @@ -15,6 +17,9 @@ comptime { if (builtin.zig_backend != .stage1) { @export(memset, .{ .name = "memset", .linkage = .Strong }); @export(memcpy, .{ .name = "memcpy", .linkage = .Strong }); + @export(trunc, .{ .name = "trunc", .linkage = .Strong }); + @export(truncf, .{ .name = "truncf", .linkage = .Strong }); + @export(truncl, .{ .name = "truncl", .linkage = .Strong }); } else { _ = @import("c_stage1.zig"); } @@ -74,3 +79,18 @@ fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, len: usize) callconv( return dest; } + +fn trunc(a: f64) f64 { + return math.trunc(a); +} + +fn truncf(a: f32) f32 { + return math.trunc(a); +} + +fn truncl(a: c_longdouble) c_longdouble { + if (!long_double_is_f128) { + @panic("TODO implement this"); + } + return math.trunc(a); +} From ec3e638b97c638a6d292902b18a6a685854d60b4 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 9 Feb 2022 13:22:50 +0100 Subject: [PATCH 0109/2031] elf: fix unaligned file offset of moved phdr containing GOT section --- src/link/Elf.zig | 54 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 2a756b3347..9ab84de1ce 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2,6 +2,7 @@ const Elf = @This(); const std = @import("std"); const builtin = @import("builtin"); +const math = std.math; const mem = std.mem; const assert = std.debug.assert; const Allocator = std.mem.Allocator; @@ -64,6 +65,7 @@ phdr_load_rw_index: ?u16 = null, phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{}, entry_addr: ?u64 = null, +page_size: u16, debug_strtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, @@ -334,6 +336,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { }; const self = try gpa.create(Elf); errdefer gpa.destroy(self); + const page_size: u16 = 0x1000; // TODO ppc64le requires 64KB self.* = .{ .base = .{ @@ -343,6 +346,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { .file = null, }, .ptr_width = ptr_width, + .page_size = page_size, }; const use_llvm = build_options.have_llvm and options.use_llvm; const use_stage1 = build_options.is_stage1 and options.use_stage1; @@ -523,10 +527,11 @@ pub fn populateMissingMetadata(self: *Elf) !void { .p64 => false, }; const ptr_size: u8 = self.ptrWidthBytes(); + if (self.phdr_load_re_index == null) { self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len); const file_size = self.base.options.program_code_size_hint; - const p_align = 0x1000; + const p_align = self.page_size; const off = self.findFreeSpace(file_size, p_align); log.debug("found PT_LOAD RE free space 0x{x} to 0x{x}", .{ off, off + file_size }); const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr; @@ -544,12 +549,13 @@ pub fn populateMissingMetadata(self: *Elf) !void { self.entry_addr = null; self.phdr_table_dirty = true; } + if (self.phdr_got_index == null) { self.phdr_got_index = @intCast(u16, self.program_headers.items.len); const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint; // We really only need ptr alignment but since we are using PROGBITS, linux requires // page align. - const p_align = if (self.base.options.target.os.tag == .linux) 0x1000 else @as(u16, ptr_size); + const p_align = if (self.base.options.target.os.tag == .linux) self.page_size else @as(u16, ptr_size); const off = self.findFreeSpace(file_size, p_align); log.debug("found PT_LOAD GOT free space 0x{x} to 0x{x}", .{ off, off + file_size }); // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at. @@ -568,16 +574,17 @@ pub fn populateMissingMetadata(self: *Elf) !void { }); self.phdr_table_dirty = true; } + if (self.phdr_load_ro_index == null) { self.phdr_load_ro_index = @intCast(u16, self.program_headers.items.len); // TODO Find a hint about how much data need to be in rodata ? const file_size = 1024; // Same reason as for GOT - const p_align = if (self.base.options.target.os.tag == .linux) 0x1000 else @as(u16, ptr_size); + const p_align = if (self.base.options.target.os.tag == .linux) self.page_size else @as(u16, ptr_size); const off = self.findFreeSpace(file_size, p_align); - log.debug("found PT_LOAD RO free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); + log.debug("found PT_LOAD RO free space 0x{x} to 0x{x}", .{ off, off + file_size }); // TODO Same as for GOT - const rodata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x5000000 else 0xa000; + const rodata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0xc000000 else 0xa000; try self.program_headers.append(self.base.allocator, .{ .p_type = elf.PT_LOAD, .p_offset = off, @@ -591,16 +598,17 @@ pub fn populateMissingMetadata(self: *Elf) !void { try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_ro_index.?, .{}); self.phdr_table_dirty = true; } + if (self.phdr_load_rw_index == null) { self.phdr_load_rw_index = @intCast(u16, self.program_headers.items.len); // TODO Find a hint about how much data need to be in data ? const file_size = 1024; // Same reason as for GOT - const p_align = if (self.base.options.target.os.tag == .linux) 0x1000 else @as(u16, ptr_size); + const p_align = if (self.base.options.target.os.tag == .linux) self.page_size else @as(u16, ptr_size); const off = self.findFreeSpace(file_size, p_align); - log.debug("found PT_LOAD RW free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); + log.debug("found PT_LOAD RW free space 0x{x} to 0x{x}", .{ off, off + file_size }); // TODO Same as for GOT - const rwdata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x6000000 else 0xc000; + const rwdata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x10000000 else 0xc000; try self.program_headers.append(self.base.allocator, .{ .p_type = elf.PT_LOAD, .p_offset = off, @@ -614,6 +622,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_rw_index.?, .{}); self.phdr_table_dirty = true; } + if (self.shstrtab_index == null) { self.shstrtab_index = @intCast(u16, self.sections.items.len); assert(self.shstrtab.items.len == 0); @@ -635,6 +644,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { self.shstrtab_dirty = true; self.shdr_table_dirty = true; } + if (self.text_section_index == null) { self.text_section_index = @intCast(u16, self.sections.items.len); const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; @@ -648,7 +658,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { .sh_size = phdr.p_filesz, .sh_link = 0, .sh_info = 0, - .sh_addralign = phdr.p_align, + .sh_addralign = 1, .sh_entsize = 0, }); try self.phdr_shdr_table.putNoClobber( @@ -658,6 +668,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { ); self.shdr_table_dirty = true; } + if (self.got_section_index == null) { self.got_section_index = @intCast(u16, self.sections.items.len); const phdr = &self.program_headers.items[self.phdr_got_index.?]; @@ -671,7 +682,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { .sh_size = phdr.p_filesz, .sh_link = 0, .sh_info = 0, - .sh_addralign = phdr.p_align, + .sh_addralign = @as(u16, ptr_size), .sh_entsize = 0, }); try self.phdr_shdr_table.putNoClobber( @@ -681,6 +692,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { ); self.shdr_table_dirty = true; } + if (self.rodata_section_index == null) { self.rodata_section_index = @intCast(u16, self.sections.items.len); const phdr = &self.program_headers.items[self.phdr_load_ro_index.?]; @@ -694,7 +706,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { .sh_size = phdr.p_filesz, .sh_link = 0, .sh_info = 0, - .sh_addralign = phdr.p_align, + .sh_addralign = 1, .sh_entsize = 0, }); try self.phdr_shdr_table.putNoClobber( @@ -704,6 +716,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { ); self.shdr_table_dirty = true; } + if (self.data_section_index == null) { self.data_section_index = @intCast(u16, self.sections.items.len); const phdr = &self.program_headers.items[self.phdr_load_rw_index.?]; @@ -717,7 +730,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { .sh_size = phdr.p_filesz, .sh_link = 0, .sh_info = 0, - .sh_addralign = phdr.p_align, + .sh_addralign = @as(u16, ptr_size), .sh_entsize = 0, }); try self.phdr_shdr_table.putNoClobber( @@ -727,6 +740,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { ); self.shdr_table_dirty = true; } + if (self.symtab_section_index == null) { self.symtab_section_index = @intCast(u16, self.sections.items.len); const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); @@ -751,6 +765,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { self.shdr_table_dirty = true; try self.writeSymbol(0); } + if (self.debug_str_section_index == null) { self.debug_str_section_index = @intCast(u16, self.sections.items.len); assert(self.debug_strtab.items.len == 0); @@ -769,6 +784,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { self.debug_strtab_dirty = true; self.shdr_table_dirty = true; } + if (self.debug_info_section_index == null) { self.debug_info_section_index = @intCast(u16, self.sections.items.len); @@ -794,6 +810,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { self.shdr_table_dirty = true; self.debug_info_header_dirty = true; } + if (self.debug_abbrev_section_index == null) { self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len); @@ -819,6 +836,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { self.shdr_table_dirty = true; self.debug_abbrev_section_dirty = true; } + if (self.debug_aranges_section_index == null) { self.debug_aranges_section_index = @intCast(u16, self.sections.items.len); @@ -844,6 +862,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { self.shdr_table_dirty = true; self.debug_aranges_section_dirty = true; } + if (self.debug_line_section_index == null) { self.debug_line_section_index = @intCast(u16, self.sections.items.len); @@ -869,6 +888,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { self.shdr_table_dirty = true; self.debug_line_header_dirty = true; } + const shsize: u64 = switch (self.ptr_width) { .p32 => @sizeOf(elf.Elf32_Shdr), .p64 => @sizeOf(elf.Elf64_Shdr), @@ -881,6 +901,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign); self.shdr_table_dirty = true; } + const phsize: u64 = switch (self.ptr_width) { .p32 => @sizeOf(elf.Elf32_Phdr), .p64 => @sizeOf(elf.Elf64_Phdr), @@ -893,6 +914,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign); self.phdr_table_dirty = true; } + { // Iterate over symbols, populating free_list and last_text_block. if (self.local_symbols.items.len != 1) { @@ -2378,12 +2400,13 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al const text_capacity = self.allocatedSize(shdr.sh_offset); const needed_size = (vaddr + new_block_size) - phdr.p_vaddr; if (needed_size > text_capacity) { - // Must move the entire text section. - const new_offset = self.findFreeSpace(needed_size, 0x1000); + // Must move the entire section. + const new_offset = self.findFreeSpace(needed_size, self.page_size); const text_size = if (self.atoms.get(phdr_index)) |last| blk: { const sym = self.local_symbols.items[last.local_sym_index]; break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr; } else 0; + log.debug("new PT_LOAD file offset 0x{x} to 0x{x}", .{ new_offset, new_offset + text_size }); const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, text_size); if (amt != text_size) return error.InputOutput; shdr.sh_offset = new_offset; @@ -2407,6 +2430,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al self.phdr_table_dirty = true; // TODO look into making only the one program header dirty self.shdr_table_dirty = true; // TODO look into making only the one section dirty } + shdr.sh_addralign = math.max(shdr.sh_addralign, alignment); // This function can also reallocate a text block. // In this case we need to "unplug" it from its previous location before @@ -3478,7 +3502,7 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { const needed_size = self.offset_table.items.len * entry_size; if (needed_size > allocated_size) { // Must move the entire got section. - const new_offset = self.findFreeSpace(needed_size, entry_size); + const new_offset = self.findFreeSpace(needed_size, self.page_size); const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, shdr.sh_size); if (amt != shdr.sh_size) return error.InputOutput; shdr.sh_offset = new_offset; From 92cb17a331b0f2f3a89b8e5e6995e8e36b9f3679 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 9 Feb 2022 10:40:59 -0700 Subject: [PATCH 0110/2031] CI: windows: update env var names --- ci/azure/pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/azure/pipelines.yml b/ci/azure/pipelines.yml index f0df4558c2..b1b99288e8 100644 --- a/ci/azure/pipelines.yml +++ b/ci/azure/pipelines.yml @@ -91,7 +91,7 @@ jobs: - pwsh: | Set-Variable -Name ZIGBUILDDIR -Value "$(Get-Location)\build" - $Env:AWS_SHARED_CREDENTIALS_FILE = "$(aws_credentials.secureFilePath)" + $Env:AWS_SHARED_CREDENTIALS_FILE = "$Env:DOWNLOADSECUREFILE_SECUREFILEPATH" cd "$ZIGBUILDDIR" mv ../LICENSE dist/ @@ -118,7 +118,7 @@ jobs: Set-Variable -Name SHASUM -Value (Get-FileHash "$TARBALL" -Algorithm SHA256 | select-object -ExpandProperty Hash) Set-Variable -Name BYTESIZE -Value (Get-Item "$TARBALL").length - Set-Variable -Name JSONFILE -Value "windows-${GITBRANCH}.json" + Set-Variable -Name JSONFILE -Value "windows-${Env:BUILD_SOURCEBRANCHNAME}.json" echo $null > $JSONFILE echo ('{"tarball": "' + $TARBALL + '",') >> $JSONFILE echo ('"shasum": "' + $SHASUM + '",') >> $JSONFILE From 2836cd5fbdcbb22b1e03c01005e0e09777c5475f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 9 Feb 2022 11:36:30 -0700 Subject: [PATCH 0111/2031] CLI: ignore -lgcc_s when it is redundant with compiler-rt For some projects, they can't help themselves, -lgcc_s ends up on the compiler command line even though it does not belong there. In Zig we know what -lgcc_s does. It's an alternative to compiler-rt. With this commit we emit a warning telling that it is unnecessary to put such thing on the command line, and happily ignore it, since we will fulfill the dependency with compiler-rt. --- src/main.zig | 5 +++++ src/target.zig | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/main.zig b/src/main.zig index 3f38fd1f78..75655d6a2a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2010,6 +2010,11 @@ fn buildOutputType( _ = system_libs.orderedRemove(lib_name); continue; } + if (target_util.is_compiler_rt_lib_name(target_info.target, lib_name)) { + std.log.warn("ignoring superfluous library '{s}': this dependency is fulfilled instead by compiler-rt which zig unconditionally provides", .{lib_name}); + _ = system_libs.orderedRemove(lib_name); + continue; + } if (std.fs.path.isAbsolute(lib_name)) { fatal("cannot use absolute path as a system library: {s}", .{lib_name}); } diff --git a/src/target.zig b/src/target.zig index 2c21fb5c61..63bd1db0b5 100644 --- a/src/target.zig +++ b/src/target.zig @@ -427,6 +427,16 @@ pub fn is_libcpp_lib_name(target: std.Target, name: []const u8) bool { eqlIgnoreCase(ignore_case, name, "c++abi"); } +pub fn is_compiler_rt_lib_name(target: std.Target, name: []const u8) bool { + if (target.abi.isGnu() and std.mem.eql(u8, name, "gcc_s")) { + return true; + } + if (std.mem.eql(u8, name, "compiler_rt")) { + return true; + } + return false; +} + pub fn hasDebugInfo(target: std.Target) bool { return !target.cpu.arch.isWasm(); } From 274b9d5c1d01c9549f2f12e76654a850df94a057 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 9 Feb 2022 16:42:15 -0700 Subject: [PATCH 0112/2031] ci: work around azure networking issue --- ci/azure/pipelines.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ci/azure/pipelines.yml b/ci/azure/pipelines.yml index b1b99288e8..6bb54e0402 100644 --- a/ci/azure/pipelines.yml +++ b/ci/azure/pipelines.yml @@ -93,6 +93,11 @@ jobs: Set-Variable -Name ZIGBUILDDIR -Value "$(Get-Location)\build" $Env:AWS_SHARED_CREDENTIALS_FILE = "$Env:DOWNLOADSECUREFILE_SECUREFILEPATH" + # Workaround Azure networking issue + # https://github.com/aws/aws-cli/issues/5749 + $Env:AWS_EC2_METADATA_DISABLED = "true" + $Env:AWS_REGION = "us-west-2" + cd "$ZIGBUILDDIR" mv ../LICENSE dist/ mv ../zig-cache/langref.html dist/ From 44b5fdf3266f11607313bc9990a876b5a7f9e174 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 9 Feb 2022 18:26:56 -0700 Subject: [PATCH 0113/2031] Revert "ci: azure: split build-and-test step" This reverts commit 846eb701821a3f2af514bbad770478e3276b2d89. This did not properly translate the upload portion of the CI script to powershell which broke our CI pipeline. --- CMakeLists.txt | 2 +- ci/azure/pipelines.yml | 137 ++++--------------------------- ci/azure/windows_msvc_install | 16 ++++ ci/azure/windows_msvc_script.bat | 39 +++++++++ ci/azure/windows_upload | 46 +++++++++++ 5 files changed, 120 insertions(+), 120 deletions(-) create mode 100644 ci/azure/windows_msvc_install create mode 100644 ci/azure/windows_msvc_script.bat create mode 100755 ci/azure/windows_upload diff --git a/CMakeLists.txt b/CMakeLists.txt index 533e03383f..8fd1960518 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,7 +63,7 @@ if("${ZIG_VERSION}" STREQUAL "") endif() endif() endif() -message(STATUS "Configuring zig version ${ZIG_VERSION}") +message("Configuring zig version ${ZIG_VERSION}") set(ZIG_STATIC off CACHE BOOL "Attempt to build a static zig executable (not compatible with glibc)") set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries") diff --git a/ci/azure/pipelines.yml b/ci/azure/pipelines.yml index 6bb54e0402..4524ee9fb1 100644 --- a/ci/azure/pipelines.yml +++ b/ci/azure/pipelines.yml @@ -10,7 +10,6 @@ jobs: - script: ci/azure/macos_script name: main displayName: 'Build and test' - - job: BuildMacOS_arm64 pool: vmImage: 'macOS-10.15' @@ -22,130 +21,30 @@ jobs: - script: ci/azure/macos_arm64_script name: main displayName: 'Build' - - job: BuildWindows - timeoutInMinutes: 360 pool: vmImage: 'windows-2019' - variables: - LLVM_CLANG_LLD_URL: 'https://ziglang.org/deps/llvm+clang+lld-13.0.0-x86_64-windows-msvc-release-mt.tar.xz' - LLVM_CLANG_LLD_DIR: 'llvm+clang+lld-13.0.0-x86_64-windows-msvc-release-mt' + timeoutInMinutes: 360 steps: - - pwsh: | - (New-Object Net.WebClient).DownloadFile("$(LLVM_CLANG_LLD_URL)", "${LLVM_CLANG_LLD_DIR}.tar.xz") - & 'C:\Program Files\7-Zip\7z.exe' x "${LLVM_CLANG_LLD_DIR}.tar.xz" - & 'C:\Program Files\7-Zip\7z.exe' x "${LLVM_CLANG_LLD_DIR}.tar" - name: install - displayName: 'Install LLVM/CLANG/LLD' - - - pwsh: | - Set-Variable -Name ZIGBUILDDIR -Value "$(Get-Location)\build" - Set-Variable -Name ZIGINSTALLDIR -Value "$ZIGBUILDDIR\dist" - Set-Variable -Name ZIGPREFIXPATH -Value "$(Get-Location)\$(LLVM_CLANG_LLD_DIR)" - - # Make the `zig version` number consistent. - # This will affect the cmake command below. - git config core.abbrev 9 - git fetch --tags - - mkdir $ZIGBUILDDIR - cd $ZIGBUILDDIR - & "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 - cmake .. ` - -Thost=x64 ` - -G "Visual Studio 16 2019" ` - -A x64 ` - -DCMAKE_INSTALL_PREFIX="$ZIGINSTALLDIR" ` - -DCMAKE_PREFIX_PATH="$ZIGPREFIXPATH" ` - -DCMAKE_BUILD_TYPE=Release ` - -DZIG_OMIT_STAGE2=ON 2> out-null - & "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe" ` - /maxcpucount /p:Configuration=Release INSTALL.vcxproj - name: build - displayName: 'Build' - - - pwsh: | - Set-Variable -Name ZIGINSTALLDIR -Value "$(Get-Location)\build\dist" - - # Sadly, stage2 is omitted from this build to save memory on the CI server. Once self-hosted is - # built with itself and does not gobble as much memory, we can enable these tests. - #& "$ZIGINSTALLDIR\bin\zig.exe" test "..\test\behavior.zig" -fno-stage1 -fLLVM -I "..\test" 2>&1 - - & "$ZIGINSTALLDIR\bin\zig.exe" build test-toolchain -Dskip-non-native -Dskip-stage2-tests 2>&1 - & "$ZIGINSTALLDIR\bin\zig.exe" build test-std -Dskip-non-native 2>&1 - name: test - displayName: 'Test' - - - pwsh: | - Set-Variable -Name ZIGINSTALLDIR -Value "$(Get-Location)\build\dist" - - & "$ZIGINSTALLDIR\bin\zig.exe" build docs - timeoutInMinutes: 60 - name: doc - displayName: 'Documentation' - + - powershell: | + (New-Object Net.WebClient).DownloadFile("https://github.com/msys2/msys2-installer/releases/download/2022-01-28/msys2-base-x86_64-20220128.sfx.exe", "sfx.exe") + .\sfx.exe -y -o\ + displayName: Download/Extract/Install MSYS2 + - script: | + @REM install updated filesystem package first without dependency checking + @REM because of: https://github.com/msys2/MSYS2-packages/issues/2021 + %CD:~0,2%\msys64\usr\bin\bash -lc "pacman --noconfirm -Sydd filesystem" + displayName: Workaround filesystem dash MSYS2 dependency issue + - script: | + %CD:~0,2%\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu" + %CD:~0,2%\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu" + displayName: Update MSYS2 - task: DownloadSecureFile@1 inputs: - name: aws_credentials - secureFile: aws_credentials - - - pwsh: | - Set-Variable -Name ZIGBUILDDIR -Value "$(Get-Location)\build" - $Env:AWS_SHARED_CREDENTIALS_FILE = "$Env:DOWNLOADSECUREFILE_SECUREFILEPATH" - - # Workaround Azure networking issue - # https://github.com/aws/aws-cli/issues/5749 - $Env:AWS_EC2_METADATA_DISABLED = "true" - $Env:AWS_REGION = "us-west-2" - - cd "$ZIGBUILDDIR" - mv ../LICENSE dist/ - mv ../zig-cache/langref.html dist/ - mv dist/bin/zig.exe dist/ - rmdir dist/bin - - # Remove the unnecessary zig dir in $prefix/lib/zig/std/std.zig - mv dist/lib/zig dist/lib2 - rmdir dist/lib - mv dist/lib2 dist/lib - - Set-Variable -Name VERSION -Value $(./dist/zig.exe version) - Set-Variable -Name DIRNAME -Value "zig-windows-x86_64-$VERSION" - Set-Variable -Name TARBALL -Value "$DIRNAME.zip" - mv dist "$DIRNAME" - 7z a "$TARBALL" "$DIRNAME" - - aws s3 cp ` - "$TARBALL" ` - s3://ziglang.org/builds/ ` - --cache-control 'public, max-age=31536000, immutable' - - Set-Variable -Name SHASUM -Value (Get-FileHash "$TARBALL" -Algorithm SHA256 | select-object -ExpandProperty Hash) - Set-Variable -Name BYTESIZE -Value (Get-Item "$TARBALL").length - - Set-Variable -Name JSONFILE -Value "windows-${Env:BUILD_SOURCEBRANCHNAME}.json" - echo $null > $JSONFILE - echo ('{"tarball": "' + $TARBALL + '",') >> $JSONFILE - echo ('"shasum": "' + $SHASUM + '",') >> $JSONFILE - echo ('"size": ' + $BYTESIZE + '}' ) >> $JSONFILE - - aws s3 cp ` - "$JSONFILE" ` - s3://ziglang.org/builds/ ` - --cache-control 'max-age=0, must-revalidate' - - aws s3 cp ` - "$JSONFILE" ` - "s3://ziglang.org/builds/x86_64-windows-${VERSION}.json" - - echo "##vso[task.setvariable variable=tarball;isOutput=true]$TARBALL" - echo "##vso[task.setvariable variable=shasum;isOutput=true]$SHASUM" - echo "##vso[task.setvariable variable=bytesize;isOutput=true]$BYTESIZE" - - name: upload - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - displayName: 'Upload' - + secureFile: s3cfg + - script: ci/azure/windows_msvc_script.bat + name: main + displayName: 'Build and test' - job: OnMasterSuccess dependsOn: - BuildMacOS diff --git a/ci/azure/windows_msvc_install b/ci/azure/windows_msvc_install new file mode 100644 index 0000000000..2df445fe12 --- /dev/null +++ b/ci/azure/windows_msvc_install @@ -0,0 +1,16 @@ +#!/bin/sh + +set -x +set -e + +pacman -Suy --needed --noconfirm +pacman -S --needed --noconfirm wget p7zip python3-pip tar xz + +TARBALL="llvm+clang+lld-13.0.0-x86_64-windows-msvc-release-mt.tar.xz" + +pip install s3cmd +wget -nv "https://ziglang.org/deps/$TARBALL" +# If the first extraction fails, re-try it once; this can happen if the tarball +# contains symlinks that are in the table of contents before the files that +# they point to. +tar -xf $TARBALL || tar --overwrite -xf $TARBALL diff --git a/ci/azure/windows_msvc_script.bat b/ci/azure/windows_msvc_script.bat new file mode 100644 index 0000000000..c61c88093c --- /dev/null +++ b/ci/azure/windows_msvc_script.bat @@ -0,0 +1,39 @@ +@echo on +SET "SRCROOT=%cd%" +SET "PREVPATH=%PATH%" +SET "PREVMSYSTEM=%MSYSTEM%" + +set "PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem" +SET "MSYSTEM=MINGW64" +bash -lc "cd ${SRCROOT} && ci/azure/windows_msvc_install" || exit /b +SET "PATH=%PREVPATH%" +SET "MSYSTEM=%PREVMSYSTEM%" + +SET "ZIGBUILDDIR=%SRCROOT%\build" +SET "ZIGINSTALLDIR=%ZIGBUILDDIR%\dist" +SET "ZIGPREFIXPATH=%SRCROOT%\llvm+clang+lld-13.0.0-x86_64-windows-msvc-release-mt" + +call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 + +REM Make the `zig version` number consistent. +REM This will affect the cmake command below. +git.exe config core.abbrev 9 +git.exe fetch --unshallow +git.exe fetch --tags + +mkdir %ZIGBUILDDIR% +cd %ZIGBUILDDIR% +cmake.exe .. -Thost=x64 -G"Visual Studio 16 2019" -A x64 "-DCMAKE_INSTALL_PREFIX=%ZIGINSTALLDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=Release -DZIG_OMIT_STAGE2=ON || exit /b +msbuild /maxcpucount /p:Configuration=Release INSTALL.vcxproj || exit /b + +REM Sadly, stage2 is omitted from this build to save memory on the CI server. Once self-hosted is +REM built with itself and does not gobble as much memory, we can enable these tests. +REM "%ZIGINSTALLDIR%\bin\zig.exe" test "..\test\behavior.zig" -fno-stage1 -fLLVM -I "..\test" || exit /b + +"%ZIGINSTALLDIR%\bin\zig.exe" build test-toolchain -Dskip-non-native -Dskip-stage2-tests || exit /b +"%ZIGINSTALLDIR%\bin\zig.exe" build test-std -Dskip-non-native || exit /b +"%ZIGINSTALLDIR%\bin\zig.exe" build docs || exit /b + +set "PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem" +SET "MSYSTEM=MINGW64" +bash -lc "cd ${SRCROOT} && ci/azure/windows_upload" || exit /b diff --git a/ci/azure/windows_upload b/ci/azure/windows_upload new file mode 100755 index 0000000000..9c5e07e5f9 --- /dev/null +++ b/ci/azure/windows_upload @@ -0,0 +1,46 @@ +#!/bin/sh + +set -x +set -e + +if [ "${BUILD_REASON}" != "PullRequest" ]; then + cd "$ZIGBUILDDIR" + + mv ../LICENSE dist/ + mv ../zig-cache/langref.html dist/ + mv dist/bin/zig.exe dist/ + rmdir dist/bin + + # Remove the unnecessary zig dir in $prefix/lib/zig/std/std.zig + mv dist/lib/zig dist/lib2 + rmdir dist/lib + mv dist/lib2 dist/lib + + VERSION=$(dist/zig.exe version) + DIRNAME="zig-windows-x86_64-$VERSION" + TARBALL="$DIRNAME.zip" + mv dist "$DIRNAME" + 7z a "$TARBALL" "$DIRNAME" + + # mv "$DOWNLOADSECUREFILE_SECUREFILEPATH" "$HOME/.s3cfg" + s3cmd -c "$DOWNLOADSECUREFILE_SECUREFILEPATH" put -P --add-header="cache-control: public, max-age=31536000, immutable" "$TARBALL" s3://ziglang.org/builds/ + + SHASUM=$(sha256sum $TARBALL | cut '-d ' -f1) + BYTESIZE=$(wc -c < $TARBALL) + + JSONFILE="windows-$GITBRANCH.json" + touch $JSONFILE + echo "{\"tarball\": \"$TARBALL\"," >>$JSONFILE + echo "\"shasum\": \"$SHASUM\"," >>$JSONFILE + echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE + + s3cmd -c "$DOWNLOADSECUREFILE_SECUREFILEPATH" put -P --add-header="Cache-Control: max-age=0, must-revalidate" "$JSONFILE" "s3://ziglang.org/builds/$JSONFILE" + s3cmd -c "$DOWNLOADSECUREFILE_SECUREFILEPATH" put -P "$JSONFILE" "s3://ziglang.org/builds/x86_64-windows-$VERSION.json" + + # `set -x` causes these variables to be mangled. + # See https://developercommunity.visualstudio.com/content/problem/375679/pipeline-variable-incorrectly-inserts-single-quote.html + set +x + echo "##vso[task.setvariable variable=tarball;isOutput=true]$TARBALL" + echo "##vso[task.setvariable variable=shasum;isOutput=true]$SHASUM" + echo "##vso[task.setvariable variable=bytesize;isOutput=true]$BYTESIZE" +fi From 7f0cf395aa74eb5ea250bd28f7525b3036790a6a Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Fri, 4 Feb 2022 20:21:15 +0100 Subject: [PATCH 0114/2031] stage2: implement all builtin floatops for f{16,32,64} - Merge `floatop.zig` and `floatop_stage1.zig` since most tests now pass on stage2. - Add more behavior tests for a bunch of functions. --- src/Air.zig | 53 +++- src/Liveness.zig | 12 + src/Sema.zig | 24 +- src/arch/aarch64/CodeGen.zig | 15 +- src/arch/arm/CodeGen.zig | 15 +- src/arch/riscv64/CodeGen.zig | 15 +- src/arch/wasm/CodeGen.zig | 12 + src/arch/x86_64/CodeGen.zig | 15 +- src/codegen/c.zig | 21 +- src/codegen/llvm.zig | 18 +- src/print_air.zig | 12 + src/value.zig | 384 ++++++++++++++++++++++++++ test/behavior.zig | 1 - test/behavior/floatop.zig | 362 ++++++++++++++++++++++++- test/behavior/floatop_stage1.zig | 452 ------------------------------- 15 files changed, 917 insertions(+), 494 deletions(-) delete mode 100644 test/behavior/floatop_stage1.zig diff --git a/src/Air.zig b/src/Air.zig index a044dd6294..623da26255 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -237,9 +237,45 @@ pub const Inst = struct { /// Uses the `ty_op` field. popcount, - /// Computes the square root of a floating point number. + /// Square root of a floating point number. /// Uses the `un_op` field. sqrt, + /// Sine a floating point number. + /// Uses the `un_op` field. + sin, + /// Cosine a floating point number. + /// Uses the `un_op` field. + cos, + /// Base e exponential of a floating point number. + /// Uses the `un_op` field. + exp, + /// Base 2 exponential of a floating point number. + /// Uses the `un_op` field. + exp2, + /// Natural (base e) logarithm of a floating point number. + /// Uses the `un_op` field. + log, + /// Base 2 logarithm of a floating point number. + /// Uses the `un_op` field. + log2, + /// Base 10 logarithm of a floating point number. + /// Uses the `un_op` field. + log10, + /// Aboslute value of a floating point number. + /// Uses the `un_op` field. + fabs, + /// Floor: rounds a floating pointer number down to the nearest integer. + /// Uses the `un_op` field. + floor, + /// Ceiling: rounds a floating pointer number up to the nearest integer. + /// Uses the `un_op` field. + ceil, + /// Rounds a floating pointer number to the nearest integer. + /// Uses the `un_op` field. + round, + /// Rounds a floating pointer number to the nearest integer towards zero. + /// Uses the `un_op` field. + trunc_float, /// `<`. Result type is always bool. /// Uses the `bin_op` field. @@ -754,7 +790,20 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .max, => return air.typeOf(datas[inst].bin_op.lhs), - .sqrt => return air.typeOf(datas[inst].un_op), + .sqrt, + .sin, + .cos, + .exp, + .exp2, + .log, + .log2, + .log10, + .fabs, + .floor, + .ceil, + .round, + .trunc_float, + => return air.typeOf(datas[inst].un_op), .cmp_lt, .cmp_lte, diff --git a/src/Liveness.zig b/src/Liveness.zig index bed7de1507..12ba63fc00 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -339,6 +339,18 @@ fn analyzeInst( .tag_name, .error_name, .sqrt, + .sin, + .cos, + .exp, + .exp2, + .log, + .log2, + .log10, + .fabs, + .floor, + .ceil, + .round, + .trunc_float, => { const operand = inst_datas[inst].un_op; return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none }); diff --git a/src/Sema.zig b/src/Sema.zig index 4ff535d86e..4d38c6b7f7 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -747,18 +747,18 @@ fn analyzeBodyInner( .ctz => try sema.zirClzCtz(block, inst, .ctz, Value.ctz), .sqrt => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt), - .sin => @panic("TODO"), - .cos => @panic("TODO"), - .exp => @panic("TODO"), - .exp2 => @panic("TODO"), - .log => @panic("TODO"), - .log2 => @panic("TODO"), - .log10 => @panic("TODO"), - .fabs => @panic("TODO"), - .floor => @panic("TODO"), - .ceil => @panic("TODO"), - .trunc => @panic("TODO"), - .round => @panic("TODO"), + .sin => try sema.zirUnaryMath(block, inst, .sin, Value.sin), + .cos => try sema.zirUnaryMath(block, inst, .cos, Value.cos), + .exp => try sema.zirUnaryMath(block, inst, .exp, Value.exp), + .exp2 => try sema.zirUnaryMath(block, inst, .exp2, Value.exp2), + .log => try sema.zirUnaryMath(block, inst, .log, Value.log), + .log2 => try sema.zirUnaryMath(block, inst, .log2, Value.log2), + .log10 => try sema.zirUnaryMath(block, inst, .log10, Value.log10), + .fabs => try sema.zirUnaryMath(block, inst, .fabs, Value.fabs), + .floor => try sema.zirUnaryMath(block, inst, .floor, Value.floor), + .ceil => try sema.zirUnaryMath(block, inst, .ceil, Value.ceil), + .round => try sema.zirUnaryMath(block, inst, .round, Value.round), + .trunc => try sema.zirUnaryMath(block, inst, .trunc_float, Value.trunc), .error_set_decl => try sema.zirErrorSetDecl(block, inst, .parent), .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon), diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index d0413af02f..42f2c66df1 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -528,7 +528,20 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .max => try self.airMax(inst), .slice => try self.airSlice(inst), - .sqrt => try self.airUnaryMath(inst), + .sqrt, + .sin, + .cos, + .exp, + .exp2, + .log, + .log2, + .log10, + .fabs, + .floor, + .ceil, + .round, + .trunc_float + => try self.airUnaryMath(inst), .add_with_overflow => try self.airAddWithOverflow(inst), .sub_with_overflow => try self.airSubWithOverflow(inst), diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 2116717cd1..711e2a96f0 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -520,7 +520,20 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .max => try self.airMax(inst), .slice => try self.airSlice(inst), - .sqrt => try self.airUnaryMath(inst), + .sqrt, + .sin, + .cos, + .exp, + .exp2, + .log, + .log2, + .log10, + .fabs, + .floor, + .ceil, + .round, + .trunc_float, + => try self.airUnaryMath(inst), .add_with_overflow => try self.airAddWithOverflow(inst), .sub_with_overflow => try self.airSubWithOverflow(inst), diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index ce5dc39bf8..09ca92f229 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -507,7 +507,20 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .max => try self.airMax(inst), .slice => try self.airSlice(inst), - .sqrt => try self.airUnaryMath(inst), + .sqrt, + .sin, + .cos, + .exp, + .exp2, + .log, + .log2, + .log10, + .fabs, + .floor, + .ceil, + .round, + .trunc_float, + => try self.airUnaryMath(inst), .add_with_overflow => try self.airAddWithOverflow(inst), .sub_with_overflow => try self.airSubWithOverflow(inst), diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 5186f0231b..b0c24be03b 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1679,6 +1679,18 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .unwrap_errunion_err_ptr, .sqrt, + .sin, + .cos, + .exp, + .exp2, + .log, + .log2, + .log10, + .fabs, + .floor, + .ceil, + .round, + .trunc_float, .ptr_slice_len_ptr, .ptr_slice_ptr_ptr, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index de8f907dea..d78245b094 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -599,7 +599,20 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .max => try self.airMax(inst), .slice => try self.airSlice(inst), - .sqrt => try self.airUnaryMath(inst), + .sqrt, + .sin, + .cos, + .exp, + .exp2, + .log, + .log2, + .log10, + .fabs, + .floor, + .ceil, + .round, + .trunc_float, + => try self.airUnaryMath(inst), .add_with_overflow => try self.airAddWithOverflow(inst), .sub_with_overflow => try self.airSubWithOverflow(inst), diff --git a/src/codegen/c.zig b/src/codegen/c.zig index fc3848df21..5d7bdcee55 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1446,7 +1446,20 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .mul_sat => try airSatOp(f, inst, "muls_"), .shl_sat => try airSatOp(f, inst, "shls_"), - .sqrt => try airSqrt(f, inst), + .sqrt, + .sin, + .cos, + .exp, + .exp2, + .log, + .log2, + .log10, + .fabs, + .floor, + .ceil, + .round, + .trunc_float, + => |tag| return f.fail("TODO: C backend: implement unary op for tag '{s}'", .{@tagName(tag)}), .add_with_overflow => try airAddWithOverflow(f, inst), .sub_with_overflow => try airSubWithOverflow(f, inst), @@ -3395,12 +3408,6 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { return CValue.none; } -fn airSqrt(f: *Function, inst: Air.Inst.Index) !CValue { - _ = f; - _ = inst; - return f.fail("TODO: C backend: implement sqrt", .{}); -} - fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { return switch (order) { .Unordered => "memory_order_relaxed", diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 5a2c21eece..9c9bf0d0f2 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2050,7 +2050,19 @@ pub const FuncGen = struct { .shr => try self.airShr(inst, false), .shr_exact => try self.airShr(inst, true), - .sqrt => try self.airSqrt(inst), + .sqrt => try self.airUnaryOp(inst, "llvm.sqrt"), + .sin => try self.airUnaryOp(inst, "llvm.sin"), + .cos => try self.airUnaryOp(inst, "llvm.cos"), + .exp => try self.airUnaryOp(inst, "llvm.exp"), + .exp2 => try self.airUnaryOp(inst, "llvm.exp2"), + .log => try self.airUnaryOp(inst, "llvm.log"), + .log2 => try self.airUnaryOp(inst, "llvm.log2"), + .log10 => try self.airUnaryOp(inst, "llvm.log10"), + .fabs => try self.airUnaryOp(inst, "llvm.fabs"), + .floor => try self.airUnaryOp(inst, "llvm.floor"), + .ceil => try self.airUnaryOp(inst, "llvm.ceil"), + .round => try self.airUnaryOp(inst, "llvm.round"), + .trunc_float => try self.airUnaryOp(inst, "llvm.trunc"), .cmp_eq => try self.airCmp(inst, .eq), .cmp_gt => try self.airCmp(inst, .gt), @@ -4213,7 +4225,7 @@ pub const FuncGen = struct { } } - fn airSqrt(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + fn airUnaryOp(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; const un_op = self.air.instructions.items(.data)[inst].un_op; @@ -4221,7 +4233,7 @@ pub const FuncGen = struct { const operand_ty = self.air.typeOf(un_op); const operand_llvm_ty = try self.dg.llvmType(operand_ty); - const fn_val = self.getIntrinsic("llvm.sqrt", &.{operand_llvm_ty}); + const fn_val = self.getIntrinsic(llvm_fn_name, &.{operand_llvm_ty}); const params = [_]*const llvm.Value{operand}; return self.builder.buildCall(fn_val, ¶ms, params.len, .C, .Auto, ""); diff --git a/src/print_air.zig b/src/print_air.zig index 341e736b91..bcadf31e74 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -159,6 +159,18 @@ const Writer = struct { .tag_name, .error_name, .sqrt, + .sin, + .cos, + .exp, + .exp2, + .log, + .log2, + .log10, + .fabs, + .floor, + .ceil, + .round, + .trunc_float, => try w.writeUnOp(s, inst), .breakpoint, diff --git a/src/value.zig b/src/value.zig index 9e1f4c0ed6..33a75e08bb 100644 --- a/src/value.zig +++ b/src/value.zig @@ -3308,6 +3308,390 @@ pub const Value = extern union { } } + pub fn sin(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + switch (float_type.floatBits(target)) { + 16 => { + const f = val.toFloat(f16); + return Value.Tag.float_16.create(arena, @sin(f)); + }, + 32 => { + const f = val.toFloat(f32); + return Value.Tag.float_32.create(arena, @sin(f)); + }, + 64 => { + const f = val.toFloat(f64); + return Value.Tag.float_64.create(arena, @sin(f)); + }, + 80 => { + if (true) { + @panic("TODO implement compiler_rt sin for f80"); + } + const f = val.toFloat(f80); + return Value.Tag.float_80.create(arena, @sin(f)); + }, + 128 => { + if (true) { + @panic("TODO implement compiler_rt sin for f128"); + } + const f = val.toFloat(f128); + return Value.Tag.float_128.create(arena, @sin(f)); + }, + else => unreachable, + } + } + + pub fn cos(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + switch (float_type.floatBits(target)) { + 16 => { + const f = val.toFloat(f16); + return Value.Tag.float_16.create(arena, @cos(f)); + }, + 32 => { + const f = val.toFloat(f32); + return Value.Tag.float_32.create(arena, @cos(f)); + }, + 64 => { + const f = val.toFloat(f64); + return Value.Tag.float_64.create(arena, @cos(f)); + }, + 80 => { + if (true) { + @panic("TODO implement compiler_rt cos for f80"); + } + const f = val.toFloat(f80); + return Value.Tag.float_80.create(arena, @cos(f)); + }, + 128 => { + if (true) { + @panic("TODO implement compiler_rt cos for f128"); + } + const f = val.toFloat(f128); + return Value.Tag.float_128.create(arena, @cos(f)); + }, + else => unreachable, + } + } + + pub fn exp(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + switch (float_type.floatBits(target)) { + 16 => { + const f = val.toFloat(f16); + return Value.Tag.float_16.create(arena, @exp(f)); + }, + 32 => { + const f = val.toFloat(f32); + return Value.Tag.float_32.create(arena, @exp(f)); + }, + 64 => { + const f = val.toFloat(f64); + return Value.Tag.float_64.create(arena, @exp(f)); + }, + 80 => { + if (true) { + @panic("TODO implement compiler_rt exp for f80"); + } + const f = val.toFloat(f80); + return Value.Tag.float_80.create(arena, @exp(f)); + }, + 128 => { + if (true) { + @panic("TODO implement compiler_rt exp for f128"); + } + const f = val.toFloat(f128); + return Value.Tag.float_128.create(arena, @exp(f)); + }, + else => unreachable, + } + } + + pub fn exp2(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + switch (float_type.floatBits(target)) { + 16 => { + const f = val.toFloat(f16); + return Value.Tag.float_16.create(arena, @exp2(f)); + }, + 32 => { + const f = val.toFloat(f32); + return Value.Tag.float_32.create(arena, @exp2(f)); + }, + 64 => { + const f = val.toFloat(f64); + return Value.Tag.float_64.create(arena, @exp2(f)); + }, + 80 => { + if (true) { + @panic("TODO implement compiler_rt exp2 for f80"); + } + const f = val.toFloat(f80); + return Value.Tag.float_80.create(arena, @exp2(f)); + }, + 128 => { + if (true) { + @panic("TODO implement compiler_rt exp2 for f128"); + } + const f = val.toFloat(f128); + return Value.Tag.float_128.create(arena, @exp2(f)); + }, + else => unreachable, + } + } + + pub fn log(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + switch (float_type.floatBits(target)) { + 16 => { + const f = val.toFloat(f16); + return Value.Tag.float_16.create(arena, @log(f)); + }, + 32 => { + const f = val.toFloat(f32); + return Value.Tag.float_32.create(arena, @log(f)); + }, + 64 => { + const f = val.toFloat(f64); + return Value.Tag.float_64.create(arena, @log(f)); + }, + 80 => { + if (true) { + @panic("TODO implement compiler_rt log for f80"); + } + const f = val.toFloat(f80); + return Value.Tag.float_80.create(arena, @log(f)); + }, + 128 => { + if (true) { + @panic("TODO implement compiler_rt log for f128"); + } + const f = val.toFloat(f128); + return Value.Tag.float_128.create(arena, @log(f)); + }, + else => unreachable, + } + } + + pub fn log2(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + switch (float_type.floatBits(target)) { + 16 => { + const f = val.toFloat(f16); + return Value.Tag.float_16.create(arena, @log2(f)); + }, + 32 => { + const f = val.toFloat(f32); + return Value.Tag.float_32.create(arena, @log2(f)); + }, + 64 => { + const f = val.toFloat(f64); + return Value.Tag.float_64.create(arena, @log2(f)); + }, + 80 => { + if (true) { + @panic("TODO implement compiler_rt log2 for f80"); + } + const f = val.toFloat(f80); + return Value.Tag.float_80.create(arena, @log2(f)); + }, + 128 => { + if (true) { + @panic("TODO implement compiler_rt log2 for f128"); + } + const f = val.toFloat(f128); + return Value.Tag.float_128.create(arena, @log2(f)); + }, + else => unreachable, + } + } + + pub fn log10(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + switch (float_type.floatBits(target)) { + 16 => { + const f = val.toFloat(f16); + return Value.Tag.float_16.create(arena, @log10(f)); + }, + 32 => { + const f = val.toFloat(f32); + return Value.Tag.float_32.create(arena, @log10(f)); + }, + 64 => { + const f = val.toFloat(f64); + return Value.Tag.float_64.create(arena, @log10(f)); + }, + 80 => { + if (true) { + @panic("TODO implement compiler_rt log10 for f80"); + } + const f = val.toFloat(f80); + return Value.Tag.float_80.create(arena, @log10(f)); + }, + 128 => { + if (true) { + @panic("TODO implement compiler_rt log10 for f128"); + } + const f = val.toFloat(f128); + return Value.Tag.float_128.create(arena, @log10(f)); + }, + else => unreachable, + } + } + + pub fn fabs(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + switch (float_type.floatBits(target)) { + 16 => { + const f = val.toFloat(f16); + return Value.Tag.float_16.create(arena, @fabs(f)); + }, + 32 => { + const f = val.toFloat(f32); + return Value.Tag.float_32.create(arena, @fabs(f)); + }, + 64 => { + const f = val.toFloat(f64); + return Value.Tag.float_64.create(arena, @fabs(f)); + }, + 80 => { + if (true) { + @panic("TODO implement compiler_rt fabs for f80"); + } + const f = val.toFloat(f80); + return Value.Tag.float_80.create(arena, @fabs(f)); + }, + 128 => { + if (true) { + @panic("TODO implement compiler_rt fabs for f128"); + } + const f = val.toFloat(f128); + return Value.Tag.float_128.create(arena, @fabs(f)); + }, + else => unreachable, + } + } + + pub fn floor(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + switch (float_type.floatBits(target)) { + 16 => { + const f = val.toFloat(f16); + return Value.Tag.float_16.create(arena, @floor(f)); + }, + 32 => { + const f = val.toFloat(f32); + return Value.Tag.float_32.create(arena, @floor(f)); + }, + 64 => { + const f = val.toFloat(f64); + return Value.Tag.float_64.create(arena, @floor(f)); + }, + 80 => { + if (true) { + @panic("TODO implement compiler_rt floor for f80"); + } + const f = val.toFloat(f80); + return Value.Tag.float_80.create(arena, @floor(f)); + }, + 128 => { + if (true) { + @panic("TODO implement compiler_rt floor for f128"); + } + const f = val.toFloat(f128); + return Value.Tag.float_128.create(arena, @floor(f)); + }, + else => unreachable, + } + } + + pub fn ceil(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + switch (float_type.floatBits(target)) { + 16 => { + const f = val.toFloat(f16); + return Value.Tag.float_16.create(arena, @ceil(f)); + }, + 32 => { + const f = val.toFloat(f32); + return Value.Tag.float_32.create(arena, @ceil(f)); + }, + 64 => { + const f = val.toFloat(f64); + return Value.Tag.float_64.create(arena, @ceil(f)); + }, + 80 => { + if (true) { + @panic("TODO implement compiler_rt ceil for f80"); + } + const f = val.toFloat(f80); + return Value.Tag.float_80.create(arena, @ceil(f)); + }, + 128 => { + if (true) { + @panic("TODO implement compiler_rt ceil for f128"); + } + const f = val.toFloat(f128); + return Value.Tag.float_128.create(arena, @ceil(f)); + }, + else => unreachable, + } + } + + pub fn round(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + switch (float_type.floatBits(target)) { + 16 => { + const f = val.toFloat(f16); + return Value.Tag.float_16.create(arena, @round(f)); + }, + 32 => { + const f = val.toFloat(f32); + return Value.Tag.float_32.create(arena, @round(f)); + }, + 64 => { + const f = val.toFloat(f64); + return Value.Tag.float_64.create(arena, @round(f)); + }, + 80 => { + if (true) { + @panic("TODO implement compiler_rt round for f80"); + } + const f = val.toFloat(f80); + return Value.Tag.float_80.create(arena, @round(f)); + }, + 128 => { + if (true) { + @panic("TODO implement compiler_rt round for f128"); + } + const f = val.toFloat(f128); + return Value.Tag.float_128.create(arena, @round(f)); + }, + else => unreachable, + } + } + + pub fn trunc(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + switch (float_type.floatBits(target)) { + 16 => { + const f = val.toFloat(f16); + return Value.Tag.float_16.create(arena, @trunc(f)); + }, + 32 => { + const f = val.toFloat(f32); + return Value.Tag.float_32.create(arena, @trunc(f)); + }, + 64 => { + const f = val.toFloat(f64); + return Value.Tag.float_64.create(arena, @trunc(f)); + }, + 80 => { + if (true) { + @panic("TODO implement compiler_rt trunc for f80"); + } + const f = val.toFloat(f80); + return Value.Tag.float_80.create(arena, @trunc(f)); + }, + 128 => { + if (true) { + @panic("TODO implement compiler_rt trunc for f128"); + } + const f = val.toFloat(f128); + return Value.Tag.float_128.create(arena, @trunc(f)); + }, + else => unreachable, + } + } + /// This type is not copyable since it may contain pointers to its inner data. pub const Payload = struct { tag: Tag, diff --git a/test/behavior.zig b/test/behavior.zig index c177dd8634..b2ffbabde2 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -150,7 +150,6 @@ test { _ = @import("behavior/const_slice_child.zig"); _ = @import("behavior/export_self_referential_type_info.zig"); _ = @import("behavior/field_parent_ptr.zig"); - _ = @import("behavior/floatop_stage1.zig"); _ = @import("behavior/fn_delegation.zig"); _ = @import("behavior/ir_block_deps.zig"); _ = @import("behavior/misc.zig"); diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 7807c690f6..ed632c26c5 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -1,12 +1,22 @@ const std = @import("std"); +const builtin = @import("builtin"); const expect = std.testing.expect; const math = std.math; const pi = std.math.pi; const e = std.math.e; const Vector = std.meta.Vector; +const has_f80_rt = @import("builtin").cpu.arch == .x86_64; +const epsilon_16 = 0.001; const epsilon = 0.000001; +fn epsForType(comptime T: type) T { + return switch (T) { + f16 => @as(f16, epsilon_16), + else => @as(T, epsilon), + }; +} + test "floating point comparisons" { try testFloatComparisons(); comptime try testFloatComparisons(); @@ -79,19 +89,37 @@ test "@sqrt" { } fn testSqrt() !void { - { - var a: f16 = 4; - try expect(@sqrt(a) == 2); - } - { - var a: f32 = 9; - try expect(@sqrt(a) == 3); - var b: f32 = 1.1; - try expect(math.approxEqAbs(f32, @sqrt(b), 1.0488088481701516, epsilon)); - } - { - var a: f64 = 25; - try expect(@sqrt(a) == 5); + try expect(@sqrt(@as(f16, 4)) == 2); + try expect(@sqrt(@as(f32, 9)) == 3); + try expect(@sqrt(@as(f64, 25)) == 5); + try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 1.1)), 1.0488088481701516, epsilon)); + try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 2.0)), 1.4142135623730950, epsilon)); + + if (builtin.zig_backend == .stage1) { + if (has_f80_rt) { + var a: f80 = 25; + try expect(@sqrt(a) == 5); + } + { + const a: comptime_float = 25.0; + try expect(@sqrt(a) == 5.0); + } + // TODO test f128, and c_longdouble + // https://github.com/ziglang/zig/issues/4026 + //{ + // var a: f128 = 49; + //try expect(@sqrt(a) == 7); + //} + + // TODO Implement Vector support for stage2 + { + var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 }; + var result = @sqrt(v); + try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 1.1)), result[0], epsilon)); + try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 2.2)), result[1], epsilon)); + try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 3.3)), result[2], epsilon)); + try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 4.4)), result[3], epsilon)); + } } } @@ -114,3 +142,311 @@ test "more @sqrt f16 tests" { try expect(math.isNan(@sqrt(@as(f16, -1.0)))); try expect(math.isNan(@sqrt(@as(f16, math.nan(f16))))); } + +test "@sin" { + comptime try testSin(); + try testSin(); +} + +fn testSin() !void { + // TODO: Implement Vector support for other backends + if (builtin.zig_backend == .stage1) { + var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 }; + var result = @sin(v); + try expect(math.approxEqAbs(f32, @sin(@as(f32, 1.1)), result[0], epsilon)); + try expect(math.approxEqAbs(f32, @sin(@as(f32, 2.2)), result[1], epsilon)); + try expect(math.approxEqAbs(f32, @sin(@as(f32, 3.3)), result[2], epsilon)); + try expect(math.approxEqAbs(f32, @sin(@as(f32, 4.4)), result[3], epsilon)); + + // stage1 emits an incorrect compile error for `@as(ty, std.math.pi / 2)` + // so skip the rest of the tests. + return; + } + + inline for ([_]type{ f16, f32, f64 }) |ty| { + const eps = epsForType(ty); + try expect(@sin(@as(ty, 0)) == 0); + try expect(math.approxEqAbs(ty, @sin(@as(ty, std.math.pi)), 0, eps)); + try expect(math.approxEqAbs(ty, @sin(@as(ty, std.math.pi / 2)), 1, eps)); + try expect(math.approxEqAbs(ty, @sin(@as(ty, std.math.pi / 4)), 0.7071067811865475, eps)); + } +} + +test "@cos" { + comptime try testCos(); + try testCos(); +} + +fn testCos() !void { + // TODO: Implement Vector support for other backends + if (builtin.zig_backend == .stage1) { + var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 }; + var result = @cos(v); + try expect(math.approxEqAbs(f32, @cos(@as(f32, 1.1)), result[0], epsilon)); + try expect(math.approxEqAbs(f32, @cos(@as(f32, 2.2)), result[1], epsilon)); + try expect(math.approxEqAbs(f32, @cos(@as(f32, 3.3)), result[2], epsilon)); + try expect(math.approxEqAbs(f32, @cos(@as(f32, 4.4)), result[3], epsilon)); + + // stage1 emits an incorrect compile error for `@as(ty, std.math.pi / 2)` + // so skip the rest of the tests. + return; + } + + inline for ([_]type{ f16, f32, f64 }) |ty| { + const eps = epsForType(ty); + try expect(@cos(@as(ty, 0)) == 1); + try expect(math.approxEqAbs(ty, @cos(@as(ty, std.math.pi)), -1, eps)); + try expect(math.approxEqAbs(ty, @cos(@as(ty, std.math.pi / 2)), 0, eps)); + try expect(math.approxEqAbs(ty, @cos(@as(ty, std.math.pi / 4)), 0.7071067811865475, eps)); + } +} + +test "@exp" { + comptime try testExp(); + try testExp(); +} + +fn testExp() !void { + inline for ([_]type{ f16, f32, f64 }) |ty| { + const eps = epsForType(ty); + try expect(@exp(@as(ty, 0)) == 1); + try expect(math.approxEqAbs(ty, @exp(@as(ty, 2)), 7.389056098930650, eps)); + try expect(math.approxEqAbs(ty, @exp(@as(ty, 5)), 148.4131591025766, eps)); + } + + // TODO: Implement Vector support for other backends + if (builtin.zig_backend == .stage1) { + var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; + var result = @exp(v); + try expect(math.approxEqAbs(f32, @exp(@as(f32, 1.1)), result[0], epsilon)); + try expect(math.approxEqAbs(f32, @exp(@as(f32, 2.2)), result[1], epsilon)); + try expect(math.approxEqAbs(f32, @exp(@as(f32, 0.3)), result[2], epsilon)); + try expect(math.approxEqAbs(f32, @exp(@as(f32, 0.4)), result[3], epsilon)); + } +} + +test "@exp2" { + comptime try testExp2(); + try testExp2(); +} + +fn testExp2() !void { + inline for ([_]type{ f16, f32, f64 }) |ty| { + const eps = epsForType(ty); + try expect(@exp2(@as(ty, 2)) == 4); + try expect(math.approxEqAbs(ty, @exp2(@as(ty, 1.5)), 2.8284271247462, eps)); + try expect(math.approxEqAbs(ty, @exp2(@as(ty, 4.5)), 22.627416997969, eps)); + } + + // TODO: Implement Vector support for other backends + if (builtin.zig_backend == .stage1) { + var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; + var result = @exp2(v); + try expect(math.approxEqAbs(f32, @exp2(@as(f32, 1.1)), result[0], epsilon)); + try expect(math.approxEqAbs(f32, @exp2(@as(f32, 2.2)), result[1], epsilon)); + try expect(math.approxEqAbs(f32, @exp2(@as(f32, 0.3)), result[2], epsilon)); + try expect(math.approxEqAbs(f32, @exp2(@as(f32, 0.4)), result[3], epsilon)); + } +} + +test "@log" { + // Old musl (and glibc?), and our current math.ln implementation do not return 1 + // so also accept those values. + comptime try testLog(); + try testLog(); +} + +fn testLog() !void { + { + var a: f16 = e; + try expect(math.approxEqAbs(f16, @log(a), 1, epsilon)); + } + { + var a: f32 = e; + try expect(@log(a) == 1 or @log(a) == @bitCast(f32, @as(u32, 0x3f7fffff))); + } + { + var a: f64 = e; + try expect(@log(a) == 1 or @log(a) == @bitCast(f64, @as(u64, 0x3ff0000000000000))); + } + inline for ([_]type{ f16, f32, f64 }) |ty| { + const eps = epsForType(ty); + try expect(math.approxEqAbs(ty, @log(@as(ty, 2)), 0.6931471805599, eps)); + try expect(math.approxEqAbs(ty, @log(@as(ty, 5)), 1.6094379124341, eps)); + } + + // TODO: Implement Vector support for other backends + if (builtin.zig_backend == .stage1) { + var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; + var result = @log(v); + try expect(math.approxEqAbs(f32, @log(@as(f32, 1.1)), result[0], epsilon)); + try expect(math.approxEqAbs(f32, @log(@as(f32, 2.2)), result[1], epsilon)); + try expect(math.approxEqAbs(f32, @log(@as(f32, 0.3)), result[2], epsilon)); + try expect(math.approxEqAbs(f32, @log(@as(f32, 0.4)), result[3], epsilon)); + } +} + +test "@log2" { + comptime try testLog2(); + try testLog2(); +} + +fn testLog2() !void { + inline for ([_]type{ f16, f32, f64 }) |ty| { + const eps = epsForType(ty); + try expect(@log2(@as(ty, 4)) == 2); + try expect(math.approxEqAbs(ty, @log2(@as(ty, 6)), 2.5849625007212, eps)); + try expect(math.approxEqAbs(ty, @log2(@as(ty, 10)), 3.3219280948874, eps)); + } + + // TODO: Implement Vector support for other backends + if (builtin.zig_backend == .stage1) { + var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; + var result = @log2(v); + try expect(math.approxEqAbs(f32, @log2(@as(f32, 1.1)), result[0], epsilon)); + try expect(math.approxEqAbs(f32, @log2(@as(f32, 2.2)), result[1], epsilon)); + try expect(math.approxEqAbs(f32, @log2(@as(f32, 0.3)), result[2], epsilon)); + try expect(math.approxEqAbs(f32, @log2(@as(f32, 0.4)), result[3], epsilon)); + } +} + +test "@log10" { + comptime try testLog10(); + try testLog10(); +} + +fn testLog10() !void { + inline for ([_]type{ f16, f32, f64 }) |ty| { + const eps = epsForType(ty); + try expect(@log10(@as(ty, 100)) == 2); + try expect(math.approxEqAbs(ty, @log10(@as(ty, 15)), 1.176091259056, eps)); + try expect(math.approxEqAbs(ty, @log10(@as(ty, 50)), 1.698970004336, eps)); + } + + // TODO: Implement Vector support for other backends + if (builtin.zig_backend == .stage1) { + var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; + var result = @log10(v); + try expect(math.approxEqAbs(f32, @log10(@as(f32, 1.1)), result[0], epsilon)); + try expect(math.approxEqAbs(f32, @log10(@as(f32, 2.2)), result[1], epsilon)); + try expect(math.approxEqAbs(f32, @log10(@as(f32, 0.3)), result[2], epsilon)); + try expect(math.approxEqAbs(f32, @log10(@as(f32, 0.4)), result[3], epsilon)); + } +} + +test "@fabs" { + comptime try testFabs(); + try testFabs(); +} + +fn testFabs() !void { + try expect(@fabs(@as(f16, -2.5)) == 2.5); + try expect(@fabs(@as(f16, 2.5)) == 2.5); + try expect(@fabs(@as(f32, -2.5)) == 2.5); + try expect(@fabs(@as(f32, 2.5)) == 2.5); + try expect(@fabs(@as(f64, -2.5)) == 2.5); + try expect(@fabs(@as(f64, 2.5)) == 2.5); + + // TODO test f128, and c_longdouble + // https://github.com/ziglang/zig/issues/4026 + // { + // var a: f80 = -2.5; + // var b: f80 = 2.5; + // try expect(@fabs(a) == 2.5); + // try expect(@fabs(b) == 2.5); + // } + + // TODO: Implement Vector support for other backends + if (builtin.zig_backend == .stage1) { + var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 }; + var result = @fabs(v); + try expect(math.approxEqAbs(f32, @fabs(@as(f32, 1.1)), result[0], epsilon)); + try expect(math.approxEqAbs(f32, @fabs(@as(f32, -2.2)), result[1], epsilon)); + try expect(math.approxEqAbs(f32, @fabs(@as(f32, 0.3)), result[2], epsilon)); + try expect(math.approxEqAbs(f32, @fabs(@as(f32, -0.4)), result[3], epsilon)); + } +} + +test "@floor" { + comptime try testFloor(); + try testFloor(); +} + +fn testFloor() !void { + try expect(@floor(@as(f16, 2.1)) == 2); + try expect(@floor(@as(f32, 2.1)) == 2); + try expect(@floor(@as(f64, 3.5)) == 3); + + // TODO test f128, and c_longdouble + // https://github.com/ziglang/zig/issues/4026 + // { + // var a: f80 = 3.5; + // try expect(@floor(a) == 3); + // } + + // TODO: Implement Vector support for other backends + if (builtin.zig_backend == .stage1) { + var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 }; + var result = @floor(v); + try expect(math.approxEqAbs(f32, @floor(@as(f32, 1.1)), result[0], epsilon)); + try expect(math.approxEqAbs(f32, @floor(@as(f32, -2.2)), result[1], epsilon)); + try expect(math.approxEqAbs(f32, @floor(@as(f32, 0.3)), result[2], epsilon)); + try expect(math.approxEqAbs(f32, @floor(@as(f32, -0.4)), result[3], epsilon)); + } +} + +test "@ceil" { + comptime try testCeil(); + try testCeil(); +} + +fn testCeil() !void { + try expect(@ceil(@as(f16, 2.1)) == 3); + try expect(@ceil(@as(f32, 2.1)) == 3); + try expect(@ceil(@as(f64, 3.5)) == 4); + + // TODO test f128, and c_longdouble + // https://github.com/ziglang/zig/issues/4026 + // { + // var a: f80 = 3.5; + // try expect(@ceil(a) == 4); + // } + + // TODO: Implement Vector support for other backends + if (builtin.zig_backend == .stage1) { + var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 }; + var result = @ceil(v); + try expect(math.approxEqAbs(f32, @ceil(@as(f32, 1.1)), result[0], epsilon)); + try expect(math.approxEqAbs(f32, @ceil(@as(f32, -2.2)), result[1], epsilon)); + try expect(math.approxEqAbs(f32, @ceil(@as(f32, 0.3)), result[2], epsilon)); + try expect(math.approxEqAbs(f32, @ceil(@as(f32, -0.4)), result[3], epsilon)); + } +} + +test "@trunc" { + comptime try testTrunc(); + try testTrunc(); +} + +fn testTrunc() !void { + try expect(@trunc(@as(f16, 2.1)) == 2); + try expect(@trunc(@as(f32, 2.1)) == 2); + try expect(@trunc(@as(f64, -3.5)) == -3); + + // TODO test f128, and c_longdouble + // https://github.com/ziglang/zig/issues/4026 + // { + // var a: f80 = -3.5; + // try expect(@trunc(a) == -3); + // } + + // TODO: Implement Vector support for other backends + if (builtin.zig_backend == .stage1) { + var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 }; + var result = @trunc(v); + try expect(math.approxEqAbs(f32, @trunc(@as(f32, 1.1)), result[0], epsilon)); + try expect(math.approxEqAbs(f32, @trunc(@as(f32, -2.2)), result[1], epsilon)); + try expect(math.approxEqAbs(f32, @trunc(@as(f32, 0.3)), result[2], epsilon)); + try expect(math.approxEqAbs(f32, @trunc(@as(f32, -0.4)), result[3], epsilon)); + } +} diff --git a/test/behavior/floatop_stage1.zig b/test/behavior/floatop_stage1.zig deleted file mode 100644 index cd11f41b40..0000000000 --- a/test/behavior/floatop_stage1.zig +++ /dev/null @@ -1,452 +0,0 @@ -const std = @import("std"); -const expect = std.testing.expect; -const math = std.math; -const pi = std.math.pi; -const e = std.math.e; -const Vector = std.meta.Vector; -const has_f80_rt = @import("builtin").cpu.arch == .x86_64; - -const epsilon = 0.000001; - -test "@sqrt" { - comptime try testSqrt(); - try testSqrt(); -} - -fn testSqrt() !void { - if (has_f80_rt) { - var a: f80 = 25; - try expect(@sqrt(a) == 5); - } - { - const a: comptime_float = 25.0; - try expect(@sqrt(a) == 5.0); - } - // TODO https://github.com/ziglang/zig/issues/4026 - //{ - // var a: f128 = 49; - //try expect(@sqrt(a) == 7); - //} - { - var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 }; - var result = @sqrt(v); - try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 1.1)), result[0], epsilon)); - try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 2.2)), result[1], epsilon)); - try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 3.3)), result[2], epsilon)); - try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 4.4)), result[3], epsilon)); - } -} - -test "@sin" { - comptime try testSin(); - try testSin(); -} - -fn testSin() !void { - // TODO test f128, and c_longdouble - // https://github.com/ziglang/zig/issues/4026 - { - var a: f16 = 0; - try expect(@sin(a) == 0); - } - { - var a: f32 = 0; - try expect(@sin(a) == 0); - } - { - var a: f64 = 0; - try expect(@sin(a) == 0); - } - // { - // var a: f80 = 0; - // try expect(@sin(a) == 0); - // } - { - var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 }; - var result = @sin(v); - try expect(math.approxEqAbs(f32, @sin(@as(f32, 1.1)), result[0], epsilon)); - try expect(math.approxEqAbs(f32, @sin(@as(f32, 2.2)), result[1], epsilon)); - try expect(math.approxEqAbs(f32, @sin(@as(f32, 3.3)), result[2], epsilon)); - try expect(math.approxEqAbs(f32, @sin(@as(f32, 4.4)), result[3], epsilon)); - } -} - -test "@cos" { - comptime try testCos(); - try testCos(); -} - -fn testCos() !void { - // TODO test f128, and c_longdouble - // https://github.com/ziglang/zig/issues/4026 - { - var a: f16 = 0; - try expect(@cos(a) == 1); - } - { - var a: f32 = 0; - try expect(@cos(a) == 1); - } - { - var a: f64 = 0; - try expect(@cos(a) == 1); - } - // { - // var a: f80 = 0; - // try expect(@cos(a) == 1); - // } - { - var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 }; - var result = @cos(v); - try expect(math.approxEqAbs(f32, @cos(@as(f32, 1.1)), result[0], epsilon)); - try expect(math.approxEqAbs(f32, @cos(@as(f32, 2.2)), result[1], epsilon)); - try expect(math.approxEqAbs(f32, @cos(@as(f32, 3.3)), result[2], epsilon)); - try expect(math.approxEqAbs(f32, @cos(@as(f32, 4.4)), result[3], epsilon)); - } -} - -test "@exp" { - comptime try testExp(); - try testExp(); -} - -fn testExp() !void { - // TODO test f128, and c_longdouble - // https://github.com/ziglang/zig/issues/4026 - { - var a: f16 = 0; - try expect(@exp(a) == 1); - } - { - var a: f32 = 0; - try expect(@exp(a) == 1); - } - { - var a: f64 = 0; - try expect(@exp(a) == 1); - } - // { - // var a: f80 = 0; - // try expect(@exp(a) == 1); - // } - { - var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; - var result = @exp(v); - try expect(math.approxEqAbs(f32, @exp(@as(f32, 1.1)), result[0], epsilon)); - try expect(math.approxEqAbs(f32, @exp(@as(f32, 2.2)), result[1], epsilon)); - try expect(math.approxEqAbs(f32, @exp(@as(f32, 0.3)), result[2], epsilon)); - try expect(math.approxEqAbs(f32, @exp(@as(f32, 0.4)), result[3], epsilon)); - } -} - -test "@exp2" { - comptime try testExp2(); - try testExp2(); -} - -fn testExp2() !void { - // TODO test f128, and c_longdouble - // https://github.com/ziglang/zig/issues/4026 - { - var a: f16 = 2; - try expect(@exp2(a) == 4); - } - { - var a: f32 = 2; - try expect(@exp2(a) == 4); - } - { - var a: f64 = 2; - try expect(@exp2(a) == 4); - } - // { - // var a: f80 = 2; - // try expect(@exp2(a) == 4); - // } - { - var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; - var result = @exp2(v); - try expect(math.approxEqAbs(f32, @exp2(@as(f32, 1.1)), result[0], epsilon)); - try expect(math.approxEqAbs(f32, @exp2(@as(f32, 2.2)), result[1], epsilon)); - try expect(math.approxEqAbs(f32, @exp2(@as(f32, 0.3)), result[2], epsilon)); - try expect(math.approxEqAbs(f32, @exp2(@as(f32, 0.4)), result[3], epsilon)); - } -} - -test "@log" { - // Old musl (and glibc?), and our current math.ln implementation do not return 1 - // so also accept those values. - comptime try testLog(); - try testLog(); -} - -fn testLog() !void { - // TODO test f128, and c_longdouble - // https://github.com/ziglang/zig/issues/4026 - { - var a: f16 = e; - try expect(math.approxEqAbs(f16, @log(a), 1, epsilon)); - } - { - var a: f32 = e; - try expect(@log(a) == 1 or @log(a) == @bitCast(f32, @as(u32, 0x3f7fffff))); - } - { - var a: f64 = e; - try expect(@log(a) == 1 or @log(a) == @bitCast(f64, @as(u64, 0x3ff0000000000000))); - } - // { - // var a: f80 = e; - // try expect(@log(a) == 1); - // } - { - var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; - var result = @log(v); - try expect(math.approxEqAbs(f32, @log(@as(f32, 1.1)), result[0], epsilon)); - try expect(math.approxEqAbs(f32, @log(@as(f32, 2.2)), result[1], epsilon)); - try expect(math.approxEqAbs(f32, @log(@as(f32, 0.3)), result[2], epsilon)); - try expect(math.approxEqAbs(f32, @log(@as(f32, 0.4)), result[3], epsilon)); - } -} - -test "@log2" { - comptime try testLog2(); - try testLog2(); -} - -fn testLog2() !void { - // TODO test f128, and c_longdouble - // https://github.com/ziglang/zig/issues/4026 - { - var a: f16 = 4; - try expect(@log2(a) == 2); - } - { - var a: f32 = 4; - try expect(@log2(a) == 2); - } - { - var a: f64 = 4; - try expect(@log2(a) == 2); - } - // { - // var a: f80 = 4; - // try expect(@log2(a) == 2); - // } - { - var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; - var result = @log2(v); - try expect(math.approxEqAbs(f32, @log2(@as(f32, 1.1)), result[0], epsilon)); - try expect(math.approxEqAbs(f32, @log2(@as(f32, 2.2)), result[1], epsilon)); - try expect(math.approxEqAbs(f32, @log2(@as(f32, 0.3)), result[2], epsilon)); - try expect(math.approxEqAbs(f32, @log2(@as(f32, 0.4)), result[3], epsilon)); - } -} - -test "@log10" { - comptime try testLog10(); - try testLog10(); -} - -fn testLog10() !void { - // TODO test f128, and c_longdouble - // https://github.com/ziglang/zig/issues/4026 - { - var a: f16 = 100; - try expect(@log10(a) == 2); - } - { - var a: f32 = 100; - try expect(@log10(a) == 2); - } - { - var a: f64 = 1000; - try expect(@log10(a) == 3); - } - // { - // var a: f80 = 1000; - // try expect(@log10(a) == 3); - // } - { - var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; - var result = @log10(v); - try expect(math.approxEqAbs(f32, @log10(@as(f32, 1.1)), result[0], epsilon)); - try expect(math.approxEqAbs(f32, @log10(@as(f32, 2.2)), result[1], epsilon)); - try expect(math.approxEqAbs(f32, @log10(@as(f32, 0.3)), result[2], epsilon)); - try expect(math.approxEqAbs(f32, @log10(@as(f32, 0.4)), result[3], epsilon)); - } -} - -test "@fabs" { - comptime try testFabs(); - try testFabs(); -} - -fn testFabs() !void { - // TODO test f128, and c_longdouble - // https://github.com/ziglang/zig/issues/4026 - { - var a: f16 = -2.5; - var b: f16 = 2.5; - try expect(@fabs(a) == 2.5); - try expect(@fabs(b) == 2.5); - } - { - var a: f32 = -2.5; - var b: f32 = 2.5; - try expect(@fabs(a) == 2.5); - try expect(@fabs(b) == 2.5); - } - { - var a: f64 = -2.5; - var b: f64 = 2.5; - try expect(@fabs(a) == 2.5); - try expect(@fabs(b) == 2.5); - } - // { - // var a: f80 = -2.5; - // var b: f80 = 2.5; - // try expect(@fabs(a) == 2.5); - // try expect(@fabs(b) == 2.5); - // } - { - var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 }; - var result = @fabs(v); - try expect(math.approxEqAbs(f32, @fabs(@as(f32, 1.1)), result[0], epsilon)); - try expect(math.approxEqAbs(f32, @fabs(@as(f32, -2.2)), result[1], epsilon)); - try expect(math.approxEqAbs(f32, @fabs(@as(f32, 0.3)), result[2], epsilon)); - try expect(math.approxEqAbs(f32, @fabs(@as(f32, -0.4)), result[3], epsilon)); - } -} - -test "@floor" { - comptime try testFloor(); - try testFloor(); -} - -fn testFloor() !void { - // TODO test f128, and c_longdouble - // https://github.com/ziglang/zig/issues/4026 - { - var a: f16 = 2.1; - try expect(@floor(a) == 2); - } - { - var a: f32 = 2.1; - try expect(@floor(a) == 2); - } - { - var a: f64 = 3.5; - try expect(@floor(a) == 3); - } - // { - // var a: f80 = 3.5; - // try expect(@floor(a) == 3); - // } - { - var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 }; - var result = @floor(v); - try expect(math.approxEqAbs(f32, @floor(@as(f32, 1.1)), result[0], epsilon)); - try expect(math.approxEqAbs(f32, @floor(@as(f32, -2.2)), result[1], epsilon)); - try expect(math.approxEqAbs(f32, @floor(@as(f32, 0.3)), result[2], epsilon)); - try expect(math.approxEqAbs(f32, @floor(@as(f32, -0.4)), result[3], epsilon)); - } -} - -test "@ceil" { - comptime try testCeil(); - try testCeil(); -} - -fn testCeil() !void { - // TODO test f128, and c_longdouble - // https://github.com/ziglang/zig/issues/4026 - { - var a: f16 = 2.1; - try expect(@ceil(a) == 3); - } - { - var a: f32 = 2.1; - try expect(@ceil(a) == 3); - } - { - var a: f64 = 3.5; - try expect(@ceil(a) == 4); - } - // { - // var a: f80 = 3.5; - // try expect(@ceil(a) == 4); - // } - { - var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 }; - var result = @ceil(v); - try expect(math.approxEqAbs(f32, @ceil(@as(f32, 1.1)), result[0], epsilon)); - try expect(math.approxEqAbs(f32, @ceil(@as(f32, -2.2)), result[1], epsilon)); - try expect(math.approxEqAbs(f32, @ceil(@as(f32, 0.3)), result[2], epsilon)); - try expect(math.approxEqAbs(f32, @ceil(@as(f32, -0.4)), result[3], epsilon)); - } -} - -test "@trunc" { - comptime try testTrunc(); - try testTrunc(); -} - -fn testTrunc() !void { - // TODO test f128, and c_longdouble - // https://github.com/ziglang/zig/issues/4026 - { - var a: f16 = 2.1; - try expect(@trunc(a) == 2); - } - { - var a: f32 = 2.1; - try expect(@trunc(a) == 2); - } - { - var a: f64 = -3.5; - try expect(@trunc(a) == -3); - } - // { - // var a: f80 = -3.5; - // try expect(@trunc(a) == -3); - // } - { - var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 }; - var result = @trunc(v); - try expect(math.approxEqAbs(f32, @trunc(@as(f32, 1.1)), result[0], epsilon)); - try expect(math.approxEqAbs(f32, @trunc(@as(f32, -2.2)), result[1], epsilon)); - try expect(math.approxEqAbs(f32, @trunc(@as(f32, 0.3)), result[2], epsilon)); - try expect(math.approxEqAbs(f32, @trunc(@as(f32, -0.4)), result[3], epsilon)); - } -} - -test "floating point comparisons" { - if (has_f80_rt) try testFloatComparisons(); - comptime try testFloatComparisons(); -} - -fn testFloatComparisons() !void { - inline for ([_]type{ f16, f32, f64, f80, f128 }) |ty| { - // No decimal part - { - const x: ty = 1.0; - try expect(x == 1); - try expect(x != 0); - try expect(x > 0); - try expect(x < 2); - try expect(x >= 1); - try expect(x <= 1); - } - // Non-zero decimal part - { - const x: ty = 1.5; - try expect(x != 1); - try expect(x != 2); - try expect(x > 1); - try expect(x < 2); - try expect(x >= 1); - try expect(x <= 2); - } - } -} From 1e5a494603d287ea3005dc35f0528c0311f43515 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 9 Feb 2022 18:19:03 -0700 Subject: [PATCH 0115/2031] Sema: implement comptime ptr store to optional payload and error union payload --- src/Sema.zig | 70 ++++++++++++- test/behavior.zig | 4 +- test/behavior/optional.zig | 163 ++++++++++++++++++++++++++++++ test/behavior/optional_llvm.zig | 27 ----- test/behavior/optional_stage1.zig | 108 -------------------- 5 files changed, 232 insertions(+), 140 deletions(-) delete mode 100644 test/behavior/optional_llvm.zig delete mode 100644 test/behavior/optional_stage1.zig diff --git a/src/Sema.zig b/src/Sema.zig index 4d38c6b7f7..3b4f1c6f55 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -15069,8 +15069,74 @@ fn beginComptimePtrMutation( else => unreachable, } }, - .eu_payload_ptr => return sema.fail(block, src, "TODO comptime store to eu_payload_ptr", .{}), - .opt_payload_ptr => return sema.fail(block, src, "TODO comptime store opt_payload_ptr", .{}), + .eu_payload_ptr => { + const eu_ptr_val = ptr_val.castTag(.eu_payload_ptr).?.data; + var parent = try beginComptimePtrMutation(sema, block, src, eu_ptr_val); + const payload_ty = parent.ty.errorUnionPayload(); + switch (parent.val.tag()) { + else => { + // An error union has been initialized to undefined at comptime and now we + // are for the first time setting the payload. We must change the + // representation of the error union from `undef` to `opt_payload`. + const arena = parent.beginArena(sema.gpa); + defer parent.finishArena(); + + const payload = try arena.create(Value.Payload.SubValue); + payload.* = .{ + .base = .{ .tag = .eu_payload }, + .data = Value.undef, + }; + + parent.val.* = Value.initPayload(&payload.base); + + return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .val = &payload.data, + .ty = payload_ty, + }; + }, + .eu_payload => return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .val = &parent.val.castTag(.eu_payload).?.data, + .ty = payload_ty, + }, + } + }, + .opt_payload_ptr => { + const opt_ptr_val = ptr_val.castTag(.opt_payload_ptr).?.data; + var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr_val); + const payload_ty = try parent.ty.optionalChildAlloc(sema.arena); + switch (parent.val.tag()) { + .undef, .null_value => { + // An optional has been initialized to undefined at comptime and now we + // are for the first time setting the payload. We must change the + // representation of the optional from `undef` to `opt_payload`. + const arena = parent.beginArena(sema.gpa); + defer parent.finishArena(); + + const payload = try arena.create(Value.Payload.SubValue); + payload.* = .{ + .base = .{ .tag = .opt_payload }, + .data = Value.undef, + }; + + parent.val.* = Value.initPayload(&payload.base); + + return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .val = &payload.data, + .ty = payload_ty, + }; + }, + .opt_payload => return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .val = &parent.val.castTag(.opt_payload).?.data, + .ty = payload_ty, + }, + + else => unreachable, + } + }, .decl_ref => unreachable, // isComptimeMutablePtr() has been checked already else => unreachable, } diff --git a/test/behavior.zig b/test/behavior.zig index b2ffbabde2..525ae5b2a1 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -33,7 +33,7 @@ test { _ = @import("behavior/hasdecl.zig"); _ = @import("behavior/hasfield.zig"); _ = @import("behavior/namespace_depends_on_compile_var.zig"); - _ = @import("behavior/optional_llvm.zig"); + _ = @import("behavior/optional.zig"); _ = @import("behavior/prefetch.zig"); _ = @import("behavior/pub_enum.zig"); _ = @import("behavior/slice_sentinel_comptime.zig"); @@ -69,7 +69,6 @@ test { _ = @import("behavior/inttoptr.zig"); _ = @import("behavior/member_func.zig"); _ = @import("behavior/null.zig"); - _ = @import("behavior/optional.zig"); _ = @import("behavior/pointers.zig"); _ = @import("behavior/ptrcast.zig"); _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); @@ -154,7 +153,6 @@ test { _ = @import("behavior/ir_block_deps.zig"); _ = @import("behavior/misc.zig"); _ = @import("behavior/muladd.zig"); - _ = @import("behavior/optional_stage1.zig"); _ = @import("behavior/popcount_stage1.zig"); _ = @import("behavior/reflection.zig"); _ = @import("behavior/select.zig"); diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index d6d6249c6e..3caf777195 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -1,9 +1,13 @@ +const builtin = @import("builtin"); const std = @import("std"); const testing = std.testing; const expect = testing.expect; const expectEqual = testing.expectEqual; test "passing an optional integer as a parameter" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + const S = struct { fn entry() bool { var x: i32 = 1234; @@ -21,12 +25,18 @@ test "passing an optional integer as a parameter" { pub const EmptyStruct = struct {}; test "optional pointer to size zero struct" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + var e = EmptyStruct{}; var o: ?*EmptyStruct = &e; try expect(o != null); } test "equality compare optional pointers" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + try testNullPtrsEql(); comptime try testNullPtrsEql(); } @@ -48,6 +58,9 @@ fn testNullPtrsEql() !void { } test "optional with void type" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + const Foo = struct { x: ?void, }; @@ -56,6 +69,9 @@ test "optional with void type" { } test "address of unwrap optional" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + const S = struct { const Foo = struct { a: i32, @@ -73,6 +89,9 @@ test "address of unwrap optional" { } test "nested optional field in struct" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + const S2 = struct { y: u8, }; @@ -86,6 +105,9 @@ test "nested optional field in struct" { } test "equality compare optional with non-optional" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + try test_cmp_optional_non_optional(); comptime try test_cmp_optional_non_optional(); } @@ -120,6 +142,9 @@ fn test_cmp_optional_non_optional() !void { } test "unwrap function call with optional pointer return value" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + const S = struct { fn entry() !void { try expect(foo().?.* == 1234); @@ -138,6 +163,9 @@ test "unwrap function call with optional pointer return value" { } test "nested orelse" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + const S = struct { fn entry() !void { try expect(func() == null); @@ -159,3 +187,138 @@ test "nested orelse" { try S.entry(); comptime try S.entry(); } + +test "self-referential struct through a slice of optional" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const S = struct { + const Node = struct { + children: []?Node, + data: ?u8, + + fn new() Node { + return Node{ + .children = undefined, + .data = null, + }; + } + }; + }; + + var n = S.Node.new(); + try expect(n.data == null); +} + +test "assigning to an unwrapped optional field in an inline loop" { + comptime var maybe_pos_arg: ?comptime_int = null; + inline for ("ab") |x| { + _ = x; + maybe_pos_arg = 0; + if (maybe_pos_arg.? != 0) { + @compileError("bad"); + } + maybe_pos_arg.? = 10; + } +} + +test "coerce an anon struct literal to optional struct" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + const Struct = struct { + field: u32, + }; + fn doTheTest() !void { + var maybe_dims: ?Struct = null; + maybe_dims = .{ .field = 1 }; + try expect(maybe_dims.?.field == 1); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "0-bit child type coerced to optional return ptr result location" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var y = Foo{}; + var z = y.thing(); + try expect(z != null); + } + + const Foo = struct { + pub const Bar = struct { + field: *Foo, + }; + + pub fn thing(self: *Foo) ?Bar { + return Bar{ .field = self }; + } + }; + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "0-bit child type coerced to optional" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var it: Foo = .{ + .list = undefined, + }; + try expect(it.foo() != null); + } + + const Empty = struct {}; + const Foo = struct { + list: [10]Empty, + + fn foo(self: *Foo) ?*Empty { + const data = &self.list[0]; + return data; + } + }; + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "array of optional unaligned types" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const Enum = enum { one, two, three }; + + const SomeUnion = union(enum) { + Num: Enum, + Other: u32, + }; + + const values = [_]?SomeUnion{ + SomeUnion{ .Num = .one }, + SomeUnion{ .Num = .two }, + SomeUnion{ .Num = .three }, + SomeUnion{ .Num = .one }, + SomeUnion{ .Num = .two }, + SomeUnion{ .Num = .three }, + }; + + // The index must be a runtime value + var i: usize = 0; + try expectEqual(Enum.one, values[i].?.Num); + i += 1; + try expectEqual(Enum.two, values[i].?.Num); + i += 1; + try expectEqual(Enum.three, values[i].?.Num); + i += 1; + try expectEqual(Enum.one, values[i].?.Num); + i += 1; + try expectEqual(Enum.two, values[i].?.Num); + i += 1; + try expectEqual(Enum.three, values[i].?.Num); +} diff --git a/test/behavior/optional_llvm.zig b/test/behavior/optional_llvm.zig deleted file mode 100644 index 9fdf703a42..0000000000 --- a/test/behavior/optional_llvm.zig +++ /dev/null @@ -1,27 +0,0 @@ -const std = @import("std"); -const testing = std.testing; -const expect = testing.expect; -const expectEqual = testing.expectEqual; -const builtin = @import("builtin"); - -test "self-referential struct through a slice of optional" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - const S = struct { - const Node = struct { - children: []?Node, - data: ?u8, - - fn new() Node { - return Node{ - .children = undefined, - .data = null, - }; - } - }; - }; - - var n = S.Node.new(); - try expect(n.data == null); -} diff --git a/test/behavior/optional_stage1.zig b/test/behavior/optional_stage1.zig deleted file mode 100644 index 04c51f8d9d..0000000000 --- a/test/behavior/optional_stage1.zig +++ /dev/null @@ -1,108 +0,0 @@ -const std = @import("std"); -const testing = std.testing; -const expect = testing.expect; -const expectEqual = testing.expectEqual; - -test "assigning to an unwrapped optional field in an inline loop" { - comptime var maybe_pos_arg: ?comptime_int = null; - inline for ("ab") |x| { - _ = x; - maybe_pos_arg = 0; - if (maybe_pos_arg.? != 0) { - @compileError("bad"); - } - maybe_pos_arg.? = 10; - } -} - -test "coerce an anon struct literal to optional struct" { - const S = struct { - const Struct = struct { - field: u32, - }; - fn doTheTest() !void { - var maybe_dims: ?Struct = null; - maybe_dims = .{ .field = 1 }; - try expect(maybe_dims.?.field == 1); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "0-bit child type coerced to optional return ptr result location" { - const S = struct { - fn doTheTest() !void { - var y = Foo{}; - var z = y.thing(); - try expect(z != null); - } - - const Foo = struct { - pub const Bar = struct { - field: *Foo, - }; - - pub fn thing(self: *Foo) ?Bar { - return Bar{ .field = self }; - } - }; - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "0-bit child type coerced to optional" { - const S = struct { - fn doTheTest() !void { - var it: Foo = .{ - .list = undefined, - }; - try expect(it.foo() != null); - } - - const Empty = struct {}; - const Foo = struct { - list: [10]Empty, - - fn foo(self: *Foo) ?*Empty { - const data = &self.list[0]; - return data; - } - }; - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "array of optional unaligned types" { - const Enum = enum { one, two, three }; - - const SomeUnion = union(enum) { - Num: Enum, - Other: u32, - }; - - const values = [_]?SomeUnion{ - SomeUnion{ .Num = .one }, - SomeUnion{ .Num = .two }, - SomeUnion{ .Num = .three }, - SomeUnion{ .Num = .one }, - SomeUnion{ .Num = .two }, - SomeUnion{ .Num = .three }, - }; - - // The index must be a runtime value - var i: usize = 0; - try expectEqual(Enum.one, values[i].?.Num); - i += 1; - try expectEqual(Enum.two, values[i].?.Num); - i += 1; - try expectEqual(Enum.three, values[i].?.Num); - i += 1; - try expectEqual(Enum.one, values[i].?.Num); - i += 1; - try expectEqual(Enum.two, values[i].?.Num); - i += 1; - try expectEqual(Enum.three, values[i].?.Num); -} From f5471299d81e809c706e147d2ea79c83aeb8b650 Mon Sep 17 00:00:00 2001 From: Sebsatian Keller Date: Thu, 10 Feb 2022 02:35:53 +0100 Subject: [PATCH 0116/2031] stage 1: improve error message if error union is cast to payload (#10770) Also: Added special error message for for `?T` to `T` casting --- src/stage1/ir.cpp | 28 ++++++++++++++++++++++++++++ test/compile_errors.zig | 14 +++++++------- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index be6226313f..0b6332f480 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -8242,6 +8242,34 @@ static Stage1AirInst *ir_analyze_cast(IrAnalyze *ira, Scope *scope, AstNode *sou return ir_implicit_cast2(ira, scope, source_node, cast1, wanted_type); } + // E!T to T + if (actual_type->id == ZigTypeIdErrorUnion) { + if (types_match_const_cast_only(ira, actual_type->data.error_union.payload_type, wanted_type, + source_node, false).id == ConstCastResultIdOk) + { + ErrorMsg *parent_msg = ir_add_error_node(ira, source_node, + buf_sprintf("cannot convert error union to payload type. consider using `try`, `catch`, or `if`. expected type '%s', found '%s'", + buf_ptr(&wanted_type->name), + buf_ptr(&actual_type->name))); + report_recursive_error(ira, source_node, &const_cast_result, parent_msg); + return ira->codegen->invalid_inst_gen; + } + } + + //?T to T + if (actual_type->id == ZigTypeIdOptional) { + if (types_match_const_cast_only(ira, actual_type->data.maybe.child_type, wanted_type, + source_node, false).id == ConstCastResultIdOk) + { + ErrorMsg *parent_msg = ir_add_error_node(ira, source_node, + buf_sprintf("cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type '%s', found '%s'", + buf_ptr(&wanted_type->name), + buf_ptr(&actual_type->name))); + report_recursive_error(ira, source_node, &const_cast_result, parent_msg); + return ira->codegen->invalid_inst_gen; + } + } + ErrorMsg *parent_msg = ir_add_error_node(ira, source_node, buf_sprintf("expected type '%s', found '%s'", buf_ptr(&wanted_type->name), diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 252ec1496b..3c224013c9 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -790,9 +790,9 @@ pub fn addCases(ctx: *TestContext) !void { "tmp.zig:1:17: note: function cannot return an error", "tmp.zig:8:5: error: expected type 'void', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set'", "tmp.zig:7:17: note: function cannot return an error", - "tmp.zig:11:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set!u32'", + "tmp.zig:11:15: error: cannot convert error union to payload type. consider using `try`, `catch`, or `if`. expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set!u32'", "tmp.zig:10:17: note: function cannot return an error", - "tmp.zig:15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set!u32'", + "tmp.zig:15:14: error: cannot convert error union to payload type. consider using `try`, `catch`, or `if`. expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set!u32'", "tmp.zig:14:5: note: cannot store an error in type 'u32'", }); @@ -1879,7 +1879,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ _ = afoo; \\} , &[_][]const u8{ - "tmp.zig:12:25: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(get_uval)).Fn.return_type.?).ErrorUnion.error_set!u32'", + "tmp.zig:12:25: error: cannot convert error union to payload type. consider using `try`, `catch`, or `if`. expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(get_uval)).Fn.return_type.?).ErrorUnion.error_set!u32'", }); ctx.objErrStage1("assigning to struct or union fields that are not optionals with a function that returns an optional", @@ -1899,7 +1899,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ _ = s; \\} , &[_][]const u8{ - "tmp.zig:11:27: error: expected type 'u8', found '?u8'", + "tmp.zig:11:27: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type 'u8', found '?u8'", }); ctx.objErrStage1("missing result type for phi node", @@ -2308,7 +2308,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ not_optional: i32, \\}; , &[_][]const u8{ - "tmp.zig:3:36: error: expected type 'i32', found '?i32'", + "tmp.zig:3:36: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type 'i32', found '?i32'", }); ctx.objErrStage1("result location incompatibility mismatching handle_is_ptr", @@ -2325,7 +2325,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ not_optional: i32, \\}; , &[_][]const u8{ - "tmp.zig:3:36: error: expected type 'i32', found '?i32'", + "tmp.zig:3:36: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type 'i32', found '?i32'", }); ctx.objErrStage1("const frame cast to anyframe", @@ -8828,7 +8828,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ v = u; \\} , &[_][]const u8{ - "tmp.zig:4:9: error: expected type '*anyopaque', found '?*anyopaque'", + "tmp.zig:4:9: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type '*anyopaque', found '?*anyopaque'", }); ctx.objErrStage1("Issue #6823: don't allow .* to be followed by **", From f2f1c63daff8e47b6b901d53fd58805e54fcdf78 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 9 Feb 2022 18:52:32 -0700 Subject: [PATCH 0117/2031] stage2: add log and logf to freestanding libc --- lib/std/special/c.zig | 18 +++++++++++++++--- lib/std/special/c_stage1.zig | 8 -------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index a4aa4f66b2..8db26eb5a5 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -17,9 +17,13 @@ comptime { if (builtin.zig_backend != .stage1) { @export(memset, .{ .name = "memset", .linkage = .Strong }); @export(memcpy, .{ .name = "memcpy", .linkage = .Strong }); + @export(trunc, .{ .name = "trunc", .linkage = .Strong }); @export(truncf, .{ .name = "truncf", .linkage = .Strong }); @export(truncl, .{ .name = "truncl", .linkage = .Strong }); + + @export(log, .{ .name = "log", .linkage = .Strong }); + @export(logf, .{ .name = "logf", .linkage = .Strong }); } else { _ = @import("c_stage1.zig"); } @@ -80,17 +84,25 @@ fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, len: usize) callconv( return dest; } -fn trunc(a: f64) f64 { +fn trunc(a: f64) callconv(.C) f64 { return math.trunc(a); } -fn truncf(a: f32) f32 { +fn truncf(a: f32) callconv(.C) f32 { return math.trunc(a); } -fn truncl(a: c_longdouble) c_longdouble { +fn truncl(a: c_longdouble) callconv(.C) c_longdouble { if (!long_double_is_f128) { @panic("TODO implement this"); } return math.trunc(a); } + +fn log(a: f64) callconv(.C) f64 { + return math.ln(a); +} + +fn logf(a: f32) callconv(.C) f32 { + return math.ln(a); +} diff --git a/lib/std/special/c_stage1.zig b/lib/std/special/c_stage1.zig index 3ae93c2bdb..14bc2ac2de 100644 --- a/lib/std/special/c_stage1.zig +++ b/lib/std/special/c_stage1.zig @@ -708,14 +708,6 @@ export fn exp2f(a: f32) f32 { return math.exp2(a); } -export fn log(a: f64) f64 { - return math.ln(a); -} - -export fn logf(a: f32) f32 { - return math.ln(a); -} - export fn log2(a: f64) f64 { return math.log2(a); } From 65c812842dfb0db9a4c6f5af04719ebf18f2d169 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 9 Feb 2022 19:13:53 -0700 Subject: [PATCH 0118/2031] freestanding libc: fix missing functions In the previous commit I got mixed up and cut-pasted instead of copy-pasting. In this commit I made c_stage1.zig additionally included for stage1 and everything else included for both. So moving forward we move stuff over from c_stage1.zig to c.zig instead of copying. --- lib/std/special/c.zig | 31 ++++++++++++++++----------- lib/std/special/c_stage1.zig | 41 ------------------------------------ 2 files changed, 19 insertions(+), 53 deletions(-) diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index 8db26eb5a5..d101c9f7e9 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -14,19 +14,20 @@ comptime { // When the self-hosted compiler is further along, all the logic from c_stage1.zig will // be migrated to this file and then c_stage1.zig will be deleted. Until then we have a // simpler implementation of c.zig that only uses features already implemented in self-hosted. - if (builtin.zig_backend != .stage1) { - @export(memset, .{ .name = "memset", .linkage = .Strong }); - @export(memcpy, .{ .name = "memcpy", .linkage = .Strong }); - - @export(trunc, .{ .name = "trunc", .linkage = .Strong }); - @export(truncf, .{ .name = "truncf", .linkage = .Strong }); - @export(truncl, .{ .name = "truncl", .linkage = .Strong }); - - @export(log, .{ .name = "log", .linkage = .Strong }); - @export(logf, .{ .name = "logf", .linkage = .Strong }); - } else { + if (builtin.zig_backend == .stage1) { _ = @import("c_stage1.zig"); } + + @export(memset, .{ .name = "memset", .linkage = .Strong }); + @export(__memset, .{ .name = "__memset", .linkage = .Strong }); + @export(memcpy, .{ .name = "memcpy", .linkage = .Strong }); + + @export(trunc, .{ .name = "trunc", .linkage = .Strong }); + @export(truncf, .{ .name = "truncf", .linkage = .Strong }); + @export(truncl, .{ .name = "truncl", .linkage = .Strong }); + + @export(log, .{ .name = "log", .linkage = .Strong }); + @export(logf, .{ .name = "logf", .linkage = .Strong }); } // Avoid dragging in the runtime safety mechanisms into this .o file, @@ -65,6 +66,12 @@ fn memset(dest: ?[*]u8, c: u8, len: usize) callconv(.C) ?[*]u8 { return dest; } +fn __memset(dest: ?[*]u8, c: u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 { + if (dest_n < n) + @panic("buffer overflow"); + return memset(dest, c, n); +} + fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, len: usize) callconv(.C) ?[*]u8 { @setRuntimeSafety(false); @@ -73,7 +80,7 @@ fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, len: usize) callconv( var s = src.?; var n = len; while (true) { - d.* = s.*; + d[0] = s[0]; n -= 1; if (n == 0) break; d += 1; diff --git a/lib/std/special/c_stage1.zig b/lib/std/special/c_stage1.zig index 14bc2ac2de..9e1c5a288c 100644 --- a/lib/std/special/c_stage1.zig +++ b/lib/std/special/c_stage1.zig @@ -161,32 +161,6 @@ test "strncmp" { try std.testing.expect(strncmp("\xff", "\x02", 1) == 253); } -export fn memset(dest: ?[*]u8, c: u8, n: usize) callconv(.C) ?[*]u8 { - @setRuntimeSafety(false); - - var index: usize = 0; - while (index != n) : (index += 1) - dest.?[index] = c; - - return dest; -} - -export fn __memset(dest: ?[*]u8, c: u8, n: usize, dest_n: usize) callconv(.C) ?[*]u8 { - if (dest_n < n) - @panic("buffer overflow"); - return memset(dest, c, n); -} - -export fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8 { - @setRuntimeSafety(false); - - var index: usize = 0; - while (index != n) : (index += 1) - dest.?[index] = src.?[index]; - - return dest; -} - export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) callconv(.C) ?[*]u8 { @setRuntimeSafety(false); @@ -732,21 +706,6 @@ export fn fabsf(a: f32) f32 { return math.fabs(a); } -export fn trunc(a: f64) f64 { - return math.trunc(a); -} - -export fn truncf(a: f32) f32 { - return math.trunc(a); -} - -export fn truncl(a: c_longdouble) c_longdouble { - if (!long_double_is_f128) { - @panic("TODO implement this"); - } - return math.trunc(a); -} - export fn round(a: f64) f64 { return math.round(a); } From 57357c43e3b56fd636cd08af591c50a08223b654 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 9 Feb 2022 23:45:50 +0100 Subject: [PATCH 0119/2031] elf: pad out file to the required size when init data We need to pad out the file to the required maximum size equal the final section's offset plus the section's size. We only need to this when populating initial metadata and only when section header was updated. --- src/link/Elf.zig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 9ab84de1ce..23bd5bb2dd 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -922,6 +922,21 @@ pub fn populateMissingMetadata(self: *Elf) !void { } // We are starting with an empty file. The default values are correct, null and empty list. } + + if (self.shdr_table_dirty) { + // We need to find out what the max file offset is according to section headers. + // Otherwise, we may end up with an ELF binary with file size not matching the final section's + // offset + it's filesize. + var max_file_offset: u64 = 0; + + for (self.sections.items) |shdr| { + if (shdr.sh_offset + shdr.sh_size > max_file_offset) { + max_file_offset = shdr.sh_offset + shdr.sh_size; + } + } + + try self.base.file.?.pwriteAll(&[_]u8{0}, max_file_offset); + } } pub const abbrev_compile_unit = 1; From c10fdde5a64a46bc514500e97b8c87d19f86e431 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 10 Feb 2022 00:24:52 -0700 Subject: [PATCH 0120/2031] stage2: LLVM backend: make unnamed struct globals LLVM union globals have to be lowered as unnamed structs if the non-most-aligned field is the active tag. In this case it bubbles up so that structs containing unions have the same restriction. This fix needs to be applied to optionals and other callsites of createNamedStruct. The bug fixed in this commit was revealed in searching for the cause of #10837. --- src/codegen/llvm.zig | 45 +++- test/behavior.zig | 1 - test/behavior/union.zig | 480 +++++++++++++++++++++++++++++++++ test/behavior/union_stage1.zig | 421 ----------------------------- 4 files changed, 514 insertions(+), 433 deletions(-) delete mode 100644 test/behavior/union_stage1.zig diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 9c9bf0d0f2..d85a16d16f 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -809,7 +809,16 @@ pub const DeclGen = struct { }; } - fn llvmType(dg: *DeclGen, t: Type) Error!*const llvm.Type { + fn isUnnamedType(dg: *DeclGen, ty: Type, val: *const llvm.Value) bool { + // Once `llvmType` succeeds, successive calls to it with the same Zig type + // are guaranteed to succeed. So if a call to `llvmType` fails here it means + // it is the first time lowering the type, which means the value can't possible + // have that type. + const llvm_ty = dg.llvmType(ty) catch return true; + return val.typeOf() != llvm_ty; + } + + fn llvmType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type { const gpa = dg.gpa; switch (t.zigTypeTag()) { .Void, .NoReturn => return dg.context.voidType(), @@ -1168,9 +1177,8 @@ pub const DeclGen = struct { .BoundFn => @panic("TODO remove BoundFn from the language"), - .Frame, - .AnyFrame, - => return dg.todo("implement llvmType for type '{}'", .{t}), + .Frame => @panic("TODO implement llvmType for Frame types"), + .AnyFrame => @panic("TODO implement llvmType for AnyFrame types"), } } @@ -1299,7 +1307,8 @@ pub const DeclGen = struct { llvm_u32.constInt(0, .False), llvm_u32.constInt(field_ptr.field_index, .False), }; - return parent_ptr.constInBoundsGEP(&indices, indices.len); + const uncasted = parent_ptr.constInBoundsGEP(&indices, indices.len); + return uncasted.constBitCast(try dg.llvmType(tv.ty)); }, .elem_ptr => { const elem_ptr = tv.val.castTag(.elem_ptr).?.data; @@ -1463,6 +1472,7 @@ pub const DeclGen = struct { var llvm_fields = try std.ArrayListUnmanaged(*const llvm.Value).initCapacity(gpa, llvm_field_count); defer llvm_fields.deinit(gpa); + var make_unnamed_struct = false; const struct_obj = tv.ty.castTag(.@"struct").?.data; if (struct_obj.layout == .Packed) { const target = dg.module.getTarget(); @@ -1558,17 +1568,30 @@ pub const DeclGen = struct { const field_ty = tv.ty.structFieldType(i); if (!field_ty.hasRuntimeBits()) continue; - llvm_fields.appendAssumeCapacity(try dg.genTypedValue(.{ + const field_llvm_val = try dg.genTypedValue(.{ .ty = field_ty, .val = field_val, - })); + }); + + make_unnamed_struct = make_unnamed_struct or + dg.isUnnamedType(field_ty, field_llvm_val); + + llvm_fields.appendAssumeCapacity(field_llvm_val); } } - return llvm_struct_ty.constNamedStruct( - llvm_fields.items.ptr, - @intCast(c_uint, llvm_fields.items.len), - ); + if (make_unnamed_struct) { + return dg.context.constStruct( + llvm_fields.items.ptr, + @intCast(c_uint, llvm_fields.items.len), + .False, + ); + } else { + return llvm_struct_ty.constNamedStruct( + llvm_fields.items.ptr, + @intCast(c_uint, llvm_fields.items.len), + ); + } }, .Union => { const llvm_union_ty = try dg.llvmType(tv.ty); diff --git a/test/behavior.zig b/test/behavior.zig index 525ae5b2a1..3e4ae36bfc 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -166,7 +166,6 @@ test { _ = @import("behavior/tuple.zig"); _ = @import("behavior/type_stage1.zig"); _ = @import("behavior/typename.zig"); - _ = @import("behavior/union_stage1.zig"); _ = @import("behavior/union_with_members.zig"); _ = @import("behavior/var_args.zig"); _ = @import("behavior/vector.zig"); diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 1b6d0ff9cb..cdd63df44e 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -490,3 +490,483 @@ test "tagged union with all void fields but a meaningful tag" { // TODO enable the test at comptime too //comptime try S.doTheTest(); } + +test "union(enum(u32)) with specified and unspecified tag values" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + comptime try expect(Tag(Tag(MultipleChoice2)) == u32); + try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); + comptime try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); +} + +const MultipleChoice2 = union(enum(u32)) { + Unspecified1: i32, + A: f32 = 20, + Unspecified2: void, + B: bool = 40, + Unspecified3: i32, + C: i8 = 60, + Unspecified4: void, + D: void = 1000, + Unspecified5: i32, +}; + +fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void { + try expect(@enumToInt(@as(Tag(MultipleChoice2), x)) == 60); + try expect(1123 == switch (x) { + MultipleChoice2.A => 1, + MultipleChoice2.B => 2, + MultipleChoice2.C => |v| @as(i32, 1000) + v, + MultipleChoice2.D => 4, + MultipleChoice2.Unspecified1 => 5, + MultipleChoice2.Unspecified2 => 6, + MultipleChoice2.Unspecified3 => 7, + MultipleChoice2.Unspecified4 => 8, + MultipleChoice2.Unspecified5 => 9, + }); +} + +test "switch on union with only 1 field" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + var r: PartialInst = undefined; + r = PartialInst.Compiled; + switch (r) { + PartialInst.Compiled => { + var z: PartialInstWithPayload = undefined; + z = PartialInstWithPayload{ .Compiled = 1234 }; + switch (z) { + PartialInstWithPayload.Compiled => |x| { + try expect(x == 1234); + return; + }, + } + }, + } + unreachable; +} + +const PartialInst = union(enum) { + Compiled, +}; + +const PartialInstWithPayload = union(enum) { + Compiled: i32, +}; + +test "union with only 1 field casted to its enum type which has enum value specified" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const Literal = union(enum) { + Number: f64, + Bool: bool, + }; + + const ExprTag = enum(comptime_int) { + Literal = 33, + }; + + const Expr = union(ExprTag) { + Literal: Literal, + }; + + var e = Expr{ .Literal = Literal{ .Bool = true } }; + comptime try expect(Tag(ExprTag) == comptime_int); + var t = @as(ExprTag, e); + try expect(t == Expr.Literal); + try expect(@enumToInt(t) == 33); + comptime try expect(@enumToInt(t) == 33); +} + +test "@enumToInt works on unions" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const Bar = union(enum) { + A: bool, + B: u8, + C, + }; + + const a = Bar{ .A = true }; + var b = Bar{ .B = undefined }; + var c = Bar.C; + try expect(@enumToInt(a) == 0); + try expect(@enumToInt(b) == 1); + try expect(@enumToInt(c) == 2); +} + +test "comptime union field value equality" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const a0 = Setter(Attribute{ .A = false }); + const a1 = Setter(Attribute{ .A = true }); + const a2 = Setter(Attribute{ .A = false }); + + const b0 = Setter(Attribute{ .B = 5 }); + const b1 = Setter(Attribute{ .B = 9 }); + const b2 = Setter(Attribute{ .B = 5 }); + + try expect(a0 == a0); + try expect(a1 == a1); + try expect(a0 == a2); + + try expect(b0 == b0); + try expect(b1 == b1); + try expect(b0 == b2); + + try expect(a0 != b0); + try expect(a0 != a1); + try expect(b0 != b1); +} + +const Attribute = union(enum) { + A: bool, + B: u8, +}; + +fn setAttribute(attr: Attribute) void { + _ = attr; +} + +fn Setter(attr: Attribute) type { + return struct { + fn set() void { + setAttribute(attr); + } + }; +} + +test "return union init with void payload" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + fn entry() !void { + try expect(func().state == State.one); + } + const Outer = union(enum) { + state: State, + }; + const State = union(enum) { + one: void, + two: u32, + }; + fn func() Outer { + return Outer{ .state = State{ .one = {} } }; + } + }; + try S.entry(); + comptime try S.entry(); +} + +test "@unionInit can modify a union type" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const UnionInitEnum = union(enum) { + Boolean: bool, + Byte: u8, + }; + + var value: UnionInitEnum = undefined; + + value = @unionInit(UnionInitEnum, "Boolean", true); + try expect(value.Boolean == true); + value.Boolean = false; + try expect(value.Boolean == false); + + value = @unionInit(UnionInitEnum, "Byte", 2); + try expect(value.Byte == 2); + value.Byte = 3; + try expect(value.Byte == 3); +} + +test "@unionInit can modify a pointer value" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const UnionInitEnum = union(enum) { + Boolean: bool, + Byte: u8, + }; + + var value: UnionInitEnum = undefined; + var value_ptr = &value; + + value_ptr.* = @unionInit(UnionInitEnum, "Boolean", true); + try expect(value.Boolean == true); + + value_ptr.* = @unionInit(UnionInitEnum, "Byte", 2); + try expect(value.Byte == 2); +} + +test "union no tag with struct member" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const Struct = struct {}; + const Union = union { + s: Struct, + pub fn foo(self: *@This()) void { + _ = self; + } + }; + var u = Union{ .s = Struct{} }; + u.foo(); +} + +test "union with comptime_int tag" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const Union = union(enum(comptime_int)) { + X: u32, + Y: u16, + Z: u8, + }; + comptime try expect(Tag(Tag(Union)) == comptime_int); +} + +test "extern union doesn't trigger field check at comptime" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const U = extern union { + x: u32, + y: u8, + }; + + const x = U{ .x = 0x55AAAA55 }; + comptime try expect(x.y == 0x55); +} + +test "anonymous union literal syntax" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + const Number = union { + int: i32, + float: f64, + }; + + fn doTheTest() !void { + var i: Number = .{ .int = 42 }; + var f = makeNumber(); + try expect(i.int == 42); + try expect(f.float == 12.34); + } + + fn makeNumber() Number { + return .{ .float = 12.34 }; + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "function call result coerces from tagged union to the tag" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + const Arch = union(enum) { + One, + Two: usize, + }; + + const ArchTag = Tag(Arch); + + fn doTheTest() !void { + var x: ArchTag = getArch1(); + try expect(x == .One); + + var y: ArchTag = getArch2(); + try expect(y == .Two); + } + + pub fn getArch1() Arch { + return .One; + } + + pub fn getArch2() Arch { + return .{ .Two = 99 }; + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "cast from anonymous struct to union" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + const U = union(enum) { + A: u32, + B: []const u8, + C: void, + }; + fn doTheTest() !void { + var y: u32 = 42; + const t0 = .{ .A = 123 }; + const t1 = .{ .B = "foo" }; + const t2 = .{ .C = {} }; + const t3 = .{ .A = y }; + const x0: U = t0; + var x1: U = t1; + const x2: U = t2; + var x3: U = t3; + try expect(x0.A == 123); + try expect(std.mem.eql(u8, x1.B, "foo")); + try expect(x2 == .C); + try expect(x3.A == y); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "cast from pointer to anonymous struct to pointer to union" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + const U = union(enum) { + A: u32, + B: []const u8, + C: void, + }; + fn doTheTest() !void { + var y: u32 = 42; + const t0 = &.{ .A = 123 }; + const t1 = &.{ .B = "foo" }; + const t2 = &.{ .C = {} }; + const t3 = &.{ .A = y }; + const x0: *const U = t0; + var x1: *const U = t1; + const x2: *const U = t2; + var x3: *const U = t3; + try expect(x0.A == 123); + try expect(std.mem.eql(u8, x1.B, "foo")); + try expect(x2.* == .C); + try expect(x3.A == y); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "switching on non exhaustive union" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + const E = enum(u8) { + a, + b, + _, + }; + const U = union(E) { + a: i32, + b: u32, + }; + fn doTheTest() !void { + var a = U{ .a = 2 }; + switch (a) { + .a => |val| try expect(val == 2), + .b => unreachable, + } + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "containers with single-field enums" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + const A = union(enum) { f1 }; + const B = union(enum) { f1: void }; + const C = struct { a: A }; + const D = struct { a: B }; + + fn doTheTest() !void { + var array1 = [1]A{A{ .f1 = {} }}; + var array2 = [1]B{B{ .f1 = {} }}; + try expect(array1[0] == .f1); + try expect(array2[0] == .f1); + + var struct1 = C{ .a = A{ .f1 = {} } }; + var struct2 = D{ .a = B{ .f1 = {} } }; + try expect(struct1.a == .f1); + try expect(struct2.a == .f1); + } + }; + + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "@unionInit on union w/ tag but no fields" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + const Type = enum(u8) { no_op = 105 }; + + const Data = union(Type) { + no_op: void, + + pub fn decode(buf: []const u8) Data { + _ = buf; + return @unionInit(Data, "no_op", {}); + } + }; + + comptime { + std.debug.assert(@sizeOf(Data) != 0); + } + + fn doTheTest() !void { + var data: Data = .{ .no_op = .{} }; + _ = data; + var o = Data.decode(&[_]u8{}); + try expectEqual(Type.no_op, o); + } + }; + + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "union enum type gets a separate scope" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + const U = union(enum) { + a: u8, + const foo = 1; + }; + + fn doTheTest() !void { + try expect(!@hasDecl(Tag(U), "foo")); + } + }; + + try S.doTheTest(); +} + +test "global variable struct contains union initialized to non-most-aligned field" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + const T = struct { + const U = union(enum) { + a: i32, + b: f64, + }; + + const S = struct { + u: U, + }; + + var s: S = .{ + .u = .{ + .a = 3, + }, + }; + }; + + T.s.u.a += 1; + try expect(T.s.u.a == 4); +} diff --git a/test/behavior/union_stage1.zig b/test/behavior/union_stage1.zig deleted file mode 100644 index b71a7ac25d..0000000000 --- a/test/behavior/union_stage1.zig +++ /dev/null @@ -1,421 +0,0 @@ -const std = @import("std"); -const expect = std.testing.expect; -const expectEqual = std.testing.expectEqual; -const Tag = std.meta.Tag; - -const MultipleChoice2 = union(enum(u32)) { - Unspecified1: i32, - A: f32 = 20, - Unspecified2: void, - B: bool = 40, - Unspecified3: i32, - C: i8 = 60, - Unspecified4: void, - D: void = 1000, - Unspecified5: i32, -}; - -test "union(enum(u32)) with specified and unspecified tag values" { - comptime try expect(Tag(Tag(MultipleChoice2)) == u32); - try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); - comptime try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); -} - -fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void { - try expect(@enumToInt(@as(Tag(MultipleChoice2), x)) == 60); - try expect(1123 == switch (x) { - MultipleChoice2.A => 1, - MultipleChoice2.B => 2, - MultipleChoice2.C => |v| @as(i32, 1000) + v, - MultipleChoice2.D => 4, - MultipleChoice2.Unspecified1 => 5, - MultipleChoice2.Unspecified2 => 6, - MultipleChoice2.Unspecified3 => 7, - MultipleChoice2.Unspecified4 => 8, - MultipleChoice2.Unspecified5 => 9, - }); -} - -test "switch on union with only 1 field" { - var r: PartialInst = undefined; - r = PartialInst.Compiled; - switch (r) { - PartialInst.Compiled => { - var z: PartialInstWithPayload = undefined; - z = PartialInstWithPayload{ .Compiled = 1234 }; - switch (z) { - PartialInstWithPayload.Compiled => |x| { - try expect(x == 1234); - return; - }, - } - }, - } - unreachable; -} - -const PartialInst = union(enum) { - Compiled, -}; - -const PartialInstWithPayload = union(enum) { - Compiled: i32, -}; - -test "union with only 1 field casted to its enum type which has enum value specified" { - const Literal = union(enum) { - Number: f64, - Bool: bool, - }; - - const ExprTag = enum(comptime_int) { - Literal = 33, - }; - - const Expr = union(ExprTag) { - Literal: Literal, - }; - - var e = Expr{ .Literal = Literal{ .Bool = true } }; - comptime try expect(Tag(ExprTag) == comptime_int); - var t = @as(ExprTag, e); - try expect(t == Expr.Literal); - try expect(@enumToInt(t) == 33); - comptime try expect(@enumToInt(t) == 33); -} - -test "@enumToInt works on unions" { - const Bar = union(enum) { - A: bool, - B: u8, - C, - }; - - const a = Bar{ .A = true }; - var b = Bar{ .B = undefined }; - var c = Bar.C; - try expect(@enumToInt(a) == 0); - try expect(@enumToInt(b) == 1); - try expect(@enumToInt(c) == 2); -} - -const Attribute = union(enum) { - A: bool, - B: u8, -}; - -fn setAttribute(attr: Attribute) void { - _ = attr; -} - -fn Setter(attr: Attribute) type { - return struct { - fn set() void { - setAttribute(attr); - } - }; -} - -test "comptime union field value equality" { - const a0 = Setter(Attribute{ .A = false }); - const a1 = Setter(Attribute{ .A = true }); - const a2 = Setter(Attribute{ .A = false }); - - const b0 = Setter(Attribute{ .B = 5 }); - const b1 = Setter(Attribute{ .B = 9 }); - const b2 = Setter(Attribute{ .B = 5 }); - - try expect(a0 == a0); - try expect(a1 == a1); - try expect(a0 == a2); - - try expect(b0 == b0); - try expect(b1 == b1); - try expect(b0 == b2); - - try expect(a0 != b0); - try expect(a0 != a1); - try expect(b0 != b1); -} - -test "return union init with void payload" { - const S = struct { - fn entry() !void { - try expect(func().state == State.one); - } - const Outer = union(enum) { - state: State, - }; - const State = union(enum) { - one: void, - two: u32, - }; - fn func() Outer { - return Outer{ .state = State{ .one = {} } }; - } - }; - try S.entry(); - comptime try S.entry(); -} - -test "@unionInit can modify a union type" { - const UnionInitEnum = union(enum) { - Boolean: bool, - Byte: u8, - }; - - var value: UnionInitEnum = undefined; - - value = @unionInit(UnionInitEnum, "Boolean", true); - try expect(value.Boolean == true); - value.Boolean = false; - try expect(value.Boolean == false); - - value = @unionInit(UnionInitEnum, "Byte", 2); - try expect(value.Byte == 2); - value.Byte = 3; - try expect(value.Byte == 3); -} - -test "@unionInit can modify a pointer value" { - const UnionInitEnum = union(enum) { - Boolean: bool, - Byte: u8, - }; - - var value: UnionInitEnum = undefined; - var value_ptr = &value; - - value_ptr.* = @unionInit(UnionInitEnum, "Boolean", true); - try expect(value.Boolean == true); - - value_ptr.* = @unionInit(UnionInitEnum, "Byte", 2); - try expect(value.Byte == 2); -} - -test "union no tag with struct member" { - const Struct = struct {}; - const Union = union { - s: Struct, - pub fn foo(self: *@This()) void { - _ = self; - } - }; - var u = Union{ .s = Struct{} }; - u.foo(); -} - -test "union with comptime_int tag" { - const Union = union(enum(comptime_int)) { - X: u32, - Y: u16, - Z: u8, - }; - comptime try expect(Tag(Tag(Union)) == comptime_int); -} - -test "extern union doesn't trigger field check at comptime" { - const U = extern union { - x: u32, - y: u8, - }; - - const x = U{ .x = 0x55AAAA55 }; - comptime try expect(x.y == 0x55); -} - -test "anonymous union literal syntax" { - const S = struct { - const Number = union { - int: i32, - float: f64, - }; - - fn doTheTest() !void { - var i: Number = .{ .int = 42 }; - var f = makeNumber(); - try expect(i.int == 42); - try expect(f.float == 12.34); - } - - fn makeNumber() Number { - return .{ .float = 12.34 }; - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "function call result coerces from tagged union to the tag" { - const S = struct { - const Arch = union(enum) { - One, - Two: usize, - }; - - const ArchTag = Tag(Arch); - - fn doTheTest() !void { - var x: ArchTag = getArch1(); - try expect(x == .One); - - var y: ArchTag = getArch2(); - try expect(y == .Two); - } - - pub fn getArch1() Arch { - return .One; - } - - pub fn getArch2() Arch { - return .{ .Two = 99 }; - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "cast from anonymous struct to union" { - const S = struct { - const U = union(enum) { - A: u32, - B: []const u8, - C: void, - }; - fn doTheTest() !void { - var y: u32 = 42; - const t0 = .{ .A = 123 }; - const t1 = .{ .B = "foo" }; - const t2 = .{ .C = {} }; - const t3 = .{ .A = y }; - const x0: U = t0; - var x1: U = t1; - const x2: U = t2; - var x3: U = t3; - try expect(x0.A == 123); - try expect(std.mem.eql(u8, x1.B, "foo")); - try expect(x2 == .C); - try expect(x3.A == y); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "cast from pointer to anonymous struct to pointer to union" { - const S = struct { - const U = union(enum) { - A: u32, - B: []const u8, - C: void, - }; - fn doTheTest() !void { - var y: u32 = 42; - const t0 = &.{ .A = 123 }; - const t1 = &.{ .B = "foo" }; - const t2 = &.{ .C = {} }; - const t3 = &.{ .A = y }; - const x0: *const U = t0; - var x1: *const U = t1; - const x2: *const U = t2; - var x3: *const U = t3; - try expect(x0.A == 123); - try expect(std.mem.eql(u8, x1.B, "foo")); - try expect(x2.* == .C); - try expect(x3.A == y); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "switching on non exhaustive union" { - const S = struct { - const E = enum(u8) { - a, - b, - _, - }; - const U = union(E) { - a: i32, - b: u32, - }; - fn doTheTest() !void { - var a = U{ .a = 2 }; - switch (a) { - .a => |val| try expect(val == 2), - .b => unreachable, - } - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "containers with single-field enums" { - const S = struct { - const A = union(enum) { f1 }; - const B = union(enum) { f1: void }; - const C = struct { a: A }; - const D = struct { a: B }; - - fn doTheTest() !void { - var array1 = [1]A{A{ .f1 = {} }}; - var array2 = [1]B{B{ .f1 = {} }}; - try expect(array1[0] == .f1); - try expect(array2[0] == .f1); - - var struct1 = C{ .a = A{ .f1 = {} } }; - var struct2 = D{ .a = B{ .f1 = {} } }; - try expect(struct1.a == .f1); - try expect(struct2.a == .f1); - } - }; - - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "@unionInit on union w/ tag but no fields" { - const S = struct { - const Type = enum(u8) { no_op = 105 }; - - const Data = union(Type) { - no_op: void, - - pub fn decode(buf: []const u8) Data { - _ = buf; - return @unionInit(Data, "no_op", {}); - } - }; - - comptime { - std.debug.assert(@sizeOf(Data) != 0); - } - - fn doTheTest() !void { - var data: Data = .{ .no_op = .{} }; - _ = data; - var o = Data.decode(&[_]u8{}); - try expectEqual(Type.no_op, o); - } - }; - - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "union enum type gets a separate scope" { - const S = struct { - const U = union(enum) { - a: u8, - const foo = 1; - }; - - fn doTheTest() !void { - try expect(!@hasDecl(Tag(U), "foo")); - } - }; - - try S.doTheTest(); -} From e139c41fd8955f873615b2c2434d162585c0e44c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 10 Feb 2022 11:27:38 +0100 Subject: [PATCH 0121/2031] stage2: handle truncate to signed non-pow-two integers --- src/arch/x86_64/CodeGen.zig | 47 +++++++++++++++++++++++++++---------- test/behavior/basic.zig | 1 - 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index d78245b094..09cd09b12d 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -958,16 +958,17 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement trunc for abi sizes larger than 8", .{}); } - const dst_mcv = blk: { - const reg = switch (operand) { - .register => |reg| reg, - else => inner: { - const reg = try self.register_manager.allocReg(inst); - try self.genSetReg(src_ty, reg, operand); - break :inner reg; - }, - }; - break :blk .{ .register = registerAlias(reg, @intCast(u32, dst_ty_size)) }; + operand.freezeIfRegister(&self.register_manager); + defer operand.unfreezeIfRegister(&self.register_manager); + + const reg: Register = blk: { + if (operand.isRegister()) { + if (self.reuseOperand(inst, ty_op.operand, 0, operand)) { + break :blk operand.register; + } + } + const mcv = try self.copyToNewRegister(inst, src_ty, operand); + break :blk mcv.register.to64(); }; // when truncating a `u16` to `u5`, for example, those top 3 bits in the result @@ -975,11 +976,31 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { const dst_bit_size = dst_ty.bitSize(self.target.*); const is_power_of_two = (dst_bit_size & (dst_bit_size - 1)) == 0; if (!is_power_of_two or dst_bit_size < 8) { - const mask = (~@as(u64, 0)) >> @intCast(u6, (64 - dst_ty.bitSize(self.target.*))); - try self.genBinMathOpMir(.@"and", dst_ty, dst_mcv, .{ .immediate = mask }); + const shift = @intCast(u6, 64 - dst_ty.bitSize(self.target.*)); + const mask = (~@as(u64, 0)) >> shift; + try self.genBinMathOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask }); + + if (src_ty.intInfo(self.target.*).signedness == .signed) { + _ = try self.addInst(.{ + .tag = .sal, + .ops = (Mir.Ops{ + .reg1 = reg, + .flags = 0b10, + }).encode(), + .data = .{ .imm = shift }, + }); + _ = try self.addInst(.{ + .tag = .sar, + .ops = (Mir.Ops{ + .reg1 = reg, + .flags = 0b10, + }).encode(), + .data = .{ .imm = shift }, + }); + } } - return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); + return self.finishAir(inst, .{ .register = reg }, .{ ty_op.operand, .none, .none }); } fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void { diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 13d7e833d5..d87d04e246 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -26,7 +26,6 @@ fn testTruncate(x: u32) u8 { test "truncate to non-power-of-two integers" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; try testTrunc(u32, u1, 0b10101, 0b1); try testTrunc(u32, u1, 0b10110, 0b0); From 0e2fcab334083d3cbc786e891be6c97e9fd81595 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 10 Feb 2022 21:06:16 +0100 Subject: [PATCH 0122/2031] wasm: Implement 'field_ptr' constants This implements the `field_ptr` value for pointers. As the value only provides us with the index, we must calculate the offset from the container type using said index. (i.e. the offset from a struct field at index 2). Besides this, small miscellaneous fixes/updates were done to get remaining behavior tests passing: - We start the function table index at 1, so unresolved function pointers don't can be null-checked properly. - Implement genTypedValue for floats up to f64. - Fix zero-sized arguments by only creating `args` for non-zero-sized types. - lowerConstant now works for all decl_ref's. - lowerConstant properly lowers optional pointers, so `null` pointers are lowered to `0`. --- src/arch/wasm/CodeGen.zig | 95 ++++++++++++++++++++++++++++----------- src/link/Wasm.zig | 6 +-- 2 files changed, 73 insertions(+), 28 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index b0c24be03b..bcad7cac19 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1106,6 +1106,20 @@ pub const DeclGen = struct { } return Result{ .appended = {} }; }, + .Float => { + const float_bits = ty.floatBits(self.target()); + if (float_bits > 64) { + return self.fail("Wasm TODO: Implement f80 and f128", .{}); + } + + switch (float_bits) { + 16, 32 => try writer.writeIntLittle(u32, @bitCast(u32, val.toFloat(f32))), + 64 => try writer.writeIntLittle(u64, @bitCast(u64, val.toFloat(f64))), + else => unreachable, + } + + return Result{ .appended = {} }; + }, .Enum => { var int_buffer: Value.Payload.U64 = undefined; const int_val = val.enumToInt(ty, &int_buffer); @@ -1334,10 +1348,12 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu defer self.gpa.free(param_types); fn_ty.fnParamTypes(param_types); var result: CallWValues = .{ - .args = try self.gpa.alloc(WValue, param_types.len), + .args = &.{}, .return_value = .none, }; - errdefer self.gpa.free(result.args); + var args = std.ArrayList(WValue).init(self.gpa); + defer args.deinit(); + const ret_ty = fn_ty.fnReturnType(); // Check if we store the result as a pointer to the stack rather than // by value @@ -1350,18 +1366,18 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu switch (cc) { .Naked => return result, .Unspecified, .C => { - for (param_types) |ty, ty_index| { + for (param_types) |ty| { if (!ty.hasRuntimeBits()) { - result.args[ty_index] = .{ .none = {} }; continue; } - result.args[ty_index] = .{ .local = self.local_index }; + try args.append(.{ .local = self.local_index }); self.local_index += 1; } }, else => return self.fail("TODO implement function parameters for cc '{}' on wasm", .{cc}), } + result.args = args.toOwnedSlice(); return result; } @@ -2060,6 +2076,26 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { if (val.isUndefDeep()) return self.emitUndefined(ty); + if (val.castTag(.decl_ref)) |decl_ref| { + const decl = decl_ref.data; + decl.markAlive(); + const target_sym_index = decl.link.wasm.sym_index; + if (ty.isSlice()) { + var slice_len: Value.Payload.U64 = .{ + .base = .{ .tag = .int_u64 }, + .data = val.sliceLen(), + }; + var slice_val: Value.Payload.Slice = .{ + .base = .{ .tag = .slice }, + .data = .{ .ptr = val.slicePtr(), .len = Value.initPayload(&slice_len.base) }, + }; + return self.lowerConstant(Value.initPayload(&slice_val.base), ty); + } else if (decl.ty.zigTypeTag() == .Fn) { + try self.bin_file.addTableFunction(target_sym_index); + return WValue{ .function_index = target_sym_index }; + } else return WValue{ .memory = target_sym_index }; + } + switch (ty.zigTypeTag()) { .Int => { const int_info = ty.intInfo(self.target); @@ -2084,25 +2120,6 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { else => unreachable, }, .Pointer => switch (val.tag()) { - .decl_ref => { - const decl = val.castTag(.decl_ref).?.data; - decl.markAlive(); - const target_sym_index = decl.link.wasm.sym_index; - if (ty.isSlice()) { - var slice_len: Value.Payload.U64 = .{ - .base = .{ .tag = .int_u64 }, - .data = val.sliceLen(), - }; - var slice_val: Value.Payload.Slice = .{ - .base = .{ .tag = .slice }, - .data = .{ .ptr = val.slicePtr(), .len = Value.initPayload(&slice_len.base) }, - }; - return self.lowerConstant(Value.initPayload(&slice_val.base), ty); - } else if (decl.ty.zigTypeTag() == .Fn) { - try self.bin_file.addTableFunction(target_sym_index); - return WValue{ .function_index = target_sym_index }; - } else return WValue{ .memory = target_sym_index }; - }, .elem_ptr => { const elem_ptr = val.castTag(.elem_ptr).?.data; const index = elem_ptr.index; @@ -2114,6 +2131,27 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { .offset = @intCast(u32, offset), } }; }, + .field_ptr => { + const field_ptr = val.castTag(.field_ptr).?.data; + const container = field_ptr.container_ptr; + const parent_ptr = try self.lowerConstant(container, ty); + + const offset = switch (container.tag()) { + .decl_ref => blk: { + const decl_ref = container.castTag(.decl_ref).?.data; + if (decl_ref.ty.castTag(.@"struct")) |_| { + const offset = decl_ref.ty.structFieldOffset(field_ptr.field_index, self.target); + break :blk offset; + } + return self.fail("Wasm TODO: field_ptr decl_ref for type '{}'", .{decl_ref.ty}); + }, + else => |tag| return self.fail("Wasm TODO: Implement field_ptr for value tag: '{s}'", .{tag}), + }; + return WValue{ .memory_offset = .{ + .pointer = parent_ptr.memory, + .offset = @intCast(u32, offset), + } }; + }, .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) }, .zero, .null_value => return WValue{ .imm32 = 0 }, else => return self.fail("Wasm TODO: lowerConstant for other const pointer tag {s}", .{val.tag()}), @@ -2160,7 +2198,14 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { }, .Optional => if (ty.isPtrLikeOptional()) { var buf: Type.Payload.ElemType = undefined; - return self.lowerConstant(val, ty.optionalChild(&buf)); + const pl_ty = ty.optionalChild(&buf); + if (val.castTag(.opt_payload)) |payload| { + return self.lowerConstant(payload.data, pl_ty); + } else if (val.isNull()) { + return WValue{ .imm32 = 0 }; + } else { + return self.lowerConstant(val, pl_ty); + } } else { const is_pl = val.tag() == .opt_payload; return WValue{ .imm32 = if (is_pl) @as(u32, 1) else 0 }; diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index d62f3a4201..81d77d5b66 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -428,7 +428,7 @@ pub fn addTableFunction(self: *Wasm, symbol_index: u32) !void { fn mapFunctionTable(self: *Wasm) void { var it = self.function_table.valueIterator(); - var index: u32 = 0; + var index: u32 = 1; while (it.next()) |value_ptr| : (index += 1) { value_ptr.* = index; } @@ -821,7 +821,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { try leb.writeULEB128(writer, wasm.reftype(.funcref)); try emitLimits(writer, .{ - .min = @intCast(u32, self.function_table.count()), + .min = @intCast(u32, self.function_table.count()) + 1, .max = null, }); @@ -931,7 +931,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { var flags: u32 = 0x2; // Yes we have a table try leb.writeULEB128(writer, flags); try leb.writeULEB128(writer, @as(u32, 0)); // index of that table. TODO: Store synthetic symbols - try emitInit(writer, .{ .i32_const = 0 }); + try emitInit(writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid try leb.writeULEB128(writer, @as(u8, 0)); try leb.writeULEB128(writer, @intCast(u32, self.function_table.count())); var symbol_it = self.function_table.keyIterator(); From 9c6d416bec10b9edd88545287133921274af1d2f Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 10 Feb 2022 21:30:26 +0100 Subject: [PATCH 0123/2031] Activate passing behavior tests This moves the single bugs behavior tests to the outer branch and disables the test cases for all non-passing backends. For the larger files, we move it up a single branch and disable it for the c backend. All test cases that do pass for the c backend however, are enabled. --- test/behavior.zig | 8 ++++---- test/behavior/bugs/2578.zig | 6 ++++++ test/behavior/bugs/3007.zig | 5 +++++ test/behavior/cast_llvm.zig | 24 ++++++++++++++++++++++++ test/behavior/fn.zig | 5 +++++ 5 files changed, 44 insertions(+), 4 deletions(-) diff --git a/test/behavior.zig b/test/behavior.zig index 3e4ae36bfc..a1d8e9bef9 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -23,6 +23,8 @@ test { _ = @import("behavior/bugs/1914.zig"); _ = @import("behavior/bugs/2006.zig"); _ = @import("behavior/bugs/2346.zig"); + _ = @import("behavior/bugs/2578.zig"); + _ = @import("behavior/bugs/3007.zig"); _ = @import("behavior/bugs/3112.zig"); _ = @import("behavior/bugs/3367.zig"); _ = @import("behavior/bugs/6850.zig"); @@ -58,9 +60,11 @@ test { _ = @import("behavior/bugs/4954.zig"); _ = @import("behavior/byval_arg_var.zig"); _ = @import("behavior/call.zig"); + _ = @import("behavior/cast_llvm.zig"); _ = @import("behavior/defer.zig"); _ = @import("behavior/enum.zig"); _ = @import("behavior/error.zig"); + _ = @import("behavior/fn.zig"); _ = @import("behavior/for.zig"); _ = @import("behavior/generics.zig"); _ = @import("behavior/if.zig"); @@ -93,14 +97,10 @@ test { if (builtin.zig_backend != .stage2_c) { // Tests that pass for stage1 and the llvm backend. _ = @import("behavior/atomics.zig"); - _ = @import("behavior/bugs/2578.zig"); - _ = @import("behavior/bugs/3007.zig"); _ = @import("behavior/bugs/9584.zig"); - _ = @import("behavior/cast_llvm.zig"); _ = @import("behavior/error_llvm.zig"); _ = @import("behavior/eval.zig"); _ = @import("behavior/floatop.zig"); - _ = @import("behavior/fn.zig"); _ = @import("behavior/math.zig"); _ = @import("behavior/maximum_minimum.zig"); _ = @import("behavior/merge_error_sets.zig"); diff --git a/test/behavior/bugs/2578.zig b/test/behavior/bugs/2578.zig index b27d73415e..15f5bf0e53 100644 --- a/test/behavior/bugs/2578.zig +++ b/test/behavior/bugs/2578.zig @@ -1,3 +1,5 @@ +const builtin = @import("builtin"); + const Foo = struct { y: u8, }; @@ -10,5 +12,9 @@ fn bar(pointer: ?*anyopaque) void { } test "fixed" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + bar(t); } diff --git a/test/behavior/bugs/3007.zig b/test/behavior/bugs/3007.zig index c08be3676a..0b3cbdc56d 100644 --- a/test/behavior/bugs/3007.zig +++ b/test/behavior/bugs/3007.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); const Foo = struct { free: bool, @@ -18,6 +19,10 @@ fn get_foo() Foo.FooError!*Foo { } test "fixed" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + default_foo = get_foo() catch null; // This Line try std.testing.expect(!default_foo.?.free); } diff --git a/test/behavior/cast_llvm.zig b/test/behavior/cast_llvm.zig index 79c2243a50..6f9b77b8f2 100644 --- a/test/behavior/cast_llvm.zig +++ b/test/behavior/cast_llvm.zig @@ -6,6 +6,8 @@ const maxInt = std.math.maxInt; const native_endian = builtin.target.cpu.arch.endian(); test "pointer reinterpret const float to int" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + // The hex representation is 0x3fe3333333333303. const float: f64 = 5.99999999999994648725e-01; const float_ptr = &float; @@ -18,6 +20,8 @@ test "pointer reinterpret const float to int" { } test "@floatToInt" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + try testFloatToInts(); comptime try testFloatToInts(); } @@ -33,6 +37,8 @@ fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void { } test "implicit cast from [*]T to ?*anyopaque" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + var a = [_]u8{ 3, 2, 1 }; var runtime_zero: usize = 0; incrementVoidPtrArray(a[runtime_zero..].ptr, 3); @@ -49,6 +55,7 @@ fn incrementVoidPtrArray(array: ?*anyopaque, len: usize) void { test "compile time int to ptr of function" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try foobar(FUNCTION_CONSTANT); } @@ -61,6 +68,8 @@ fn foobar(func: PFN_void) !void { } test "implicit ptr to *anyopaque" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + var a: u32 = 1; var ptr: *align(@alignOf(u32)) anyopaque = &a; var b: *u32 = @ptrCast(*u32, ptr); @@ -87,6 +96,7 @@ fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A { } test "peer type resolution: [0]u8 and []const u8" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); comptime { @@ -103,6 +113,7 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { } test "implicitly cast from [N]T to ?[]const T" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); comptime try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); } @@ -112,6 +123,7 @@ fn castToOptionalSlice() ?[]const u8 { } test "cast u128 to f128 and back" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO comptime try testCast128(); try testCast128(); } @@ -129,6 +141,7 @@ fn cast128Float(x: u128) f128 { } test "implicit cast from *[N]T to ?[*]T" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var x: ?[*]u16 = null; var y: [4]u16 = [4]u16{ 0, 1, 2, 3 }; @@ -140,6 +153,7 @@ test "implicit cast from *[N]T to ?[*]T" { } test "implicit cast from *T to ?*anyopaque" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var a: u8 = 1; incrementVoidPtrValue(&a); try std.testing.expect(a == 2); @@ -150,6 +164,7 @@ fn incrementVoidPtrValue(value: ?*anyopaque) void { } test "implicit cast *[0]T to E![]const u8" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var x = @as(anyerror![]const u8, &[0]u8{}); try expect((x catch unreachable).len == 0); } @@ -160,11 +175,13 @@ test "cast from array reference to fn: comptime fn ptr" { try expect(@ptrToInt(f) == @ptrToInt(&global_array)); } test "cast from array reference to fn: runtime fn ptr" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var f = @ptrCast(*const fn () callconv(.C) void, &global_array); try expect(@ptrToInt(f) == @ptrToInt(&global_array)); } test "*const [N]null u8 to ?[]const u8" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { var a = "Hello"; @@ -196,6 +213,7 @@ test "cast between [*c]T and ?[*:0]T on fn parameter" { var global_struct: struct { f0: usize } = undefined; test "assignment to optional pointer result loc" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var foo: struct { ptr: ?*anyopaque } = .{ .ptr = &global_struct }; try expect(foo.ptr.? == @ptrCast(*anyopaque, &global_struct)); } @@ -217,6 +235,8 @@ fn boolToStr(b: bool) []const u8 { } test "cast f16 to wider types" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { var x: f16 = 1234.0; @@ -230,6 +250,9 @@ test "cast f16 to wider types" { } test "cast f128 to narrower types" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { var x: f128 = 1234.0; @@ -243,6 +266,7 @@ test "cast f128 to narrower types" { } test "peer type resolution: unreachable, null, slice" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest(num: usize, word: []const u8) !void { const result = switch (num) { diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index ebbbfda67b..17a3d9a93b 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -75,6 +75,7 @@ test "return inner function which references comptime variable of outer function } test "discard the result of a function that returns a struct" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn entry() void { _ = func(); @@ -94,6 +95,7 @@ test "discard the result of a function that returns a struct" { } test "inline function call that calls optional function pointer, return pointer at callsite interacts correctly with callsite return type" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage1) return error.SkipZigTest; const S = struct { @@ -139,6 +141,7 @@ fn fnWithUnreachable() noreturn { } test "extern struct with stdcallcc fn pointer" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage1) return error.SkipZigTest; const S = extern struct { @@ -252,6 +255,7 @@ test "implicit cast fn call result to optional in field result" { } test "void parameters" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try voidFun(1, void{}, 2, {}); } fn voidFun(a: i32, b: void, c: i32, d: void) !void { @@ -306,6 +310,7 @@ fn numberLiteralArg(a: anytype) !void { } test "function call with anon list literal" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { try consumeVec(.{ 9, 8, 7 }); From 08e2f5d08390d41b58c92707693385e5e2968fc8 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 10 Feb 2022 13:01:23 +0100 Subject: [PATCH 0124/2031] codegen: handle lowering of const slice pointers --- src/arch/x86_64/CodeGen.zig | 87 ++++++++++++++++++++++++++++++------- src/codegen.zig | 1 - 2 files changed, 71 insertions(+), 17 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 09cd09b12d..c0bb233499 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -3514,8 +3514,14 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE else => return self.fail("TODO implement args on stack for {} with abi size > 8", .{mcv}), } }, + .embedded_in_code => { + if (abi_size <= 8) { + const reg = try self.copyToTmpRegister(ty, mcv); + return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); + } + return self.fail("TODO implement args on stack for {} with abi size > 8", .{mcv}); + }, .memory, - .embedded_in_code, .direct_load, .got_load, => { @@ -3523,7 +3529,58 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); } - return self.fail("TODO implement memcpy for setting args on stack from {}", .{mcv}); + + self.register_manager.freezeRegs(&.{ .rax, .rcx }); + defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx }); + + const addr_reg: Register = blk: { + switch (mcv) { + .got_load, + .direct_load, + => |sym_index| { + const flags: u2 = switch (mcv) { + .got_load => 0b00, + .direct_load => 0b01, + else => unreachable, + }; + const addr_reg = try self.register_manager.allocReg(null); + _ = try self.addInst(.{ + .tag = .lea_pie, + .ops = (Mir.Ops{ + .reg1 = addr_reg.to64(), + .flags = flags, + }).encode(), + .data = .{ .linker_sym_index = sym_index }, + }); + break :blk addr_reg; + }, + .memory => |addr| { + const addr_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = addr }); + break :blk addr_reg; + }, + else => unreachable, + } + }; + + self.register_manager.freezeRegs(&.{addr_reg}); + defer self.register_manager.unfreezeRegs(&.{addr_reg}); + + const regs = try self.register_manager.allocRegs(2, .{ null, null }); + const count_reg = regs[0]; + const tmp_reg = regs[1]; + + try self.register_manager.getReg(.rax, null); + try self.register_manager.getReg(.rcx, null); + + // TODO allow for abi_size to be u64 + try self.genSetReg(Type.u32, count_reg, .{ .immediate = @intCast(u32, abi_size) }); + try self.genInlineMemcpy( + -(stack_offset + @intCast(i32, abi_size)), + .rsp, + addr_reg.to64(), + count_reg.to64(), + tmp_reg.to8(), + ); }, .register => |reg| { _ = try self.addInst(.{ @@ -4488,6 +4545,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa } fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { + log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty, tv.val }); const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| { return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); }; @@ -4520,23 +4578,20 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { switch (typed_value.ty.zigTypeTag()) { .Pointer => switch (typed_value.ty.ptrSize()) { .Slice => { - var buf: Type.SlicePtrFieldTypeBuffer = undefined; - const ptr_type = typed_value.ty.slicePtrFieldType(&buf); - const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val }); - const slice_len = typed_value.val.sliceLen(); - // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean - // the Sema code needs to use anonymous Decls or alloca instructions to store data. - const ptr_imm = ptr_mcv.memory; - _ = slice_len; - _ = ptr_imm; - // We need more general support for const data being stored in memory to make this work. - return self.fail("TODO codegen for const slices", .{}); + return self.lowerUnnamedConst(typed_value); }, else => { - if (typed_value.val.tag() == .int_u64) { - return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; + switch (typed_value.val.tag()) { + .int_u64 => { + return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; + }, + .slice => { + return self.lowerUnnamedConst(typed_value); + }, + else => { + return self.fail("TODO codegen more kinds of const pointers: {}", .{typed_value.val.tag()}); + }, } - return self.fail("TODO codegen more kinds of const pointers: {}", .{typed_value.val.tag()}); }, }, .Int => { diff --git a/src/codegen.zig b/src/codegen.zig index d1c249d99d..389f38a020 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -230,7 +230,6 @@ pub fn generateSymbol( return lowerDeclRef(bin_file, src_loc, typed_value, decl, code, debug_output); }, .slice => { - // TODO populate .debug_info for the slice const slice = typed_value.val.castTag(.slice).?.data; // generate ptr From b9b1ab024063105a9adfe3828692867c91015dc6 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 10 Feb 2022 14:41:07 +0100 Subject: [PATCH 0125/2031] elf: store pointer relocations indexed by containing atom In `getDeclVAddr`, it may happen that the target `Decl` has not been allocated space in virtual memory. In this case, we store a relocation in the linker-global table which we will iterate over when flushing the module, and fill in any missing address in the final binary. Note that for optimisation, if the address was resolved at the time of a call to `getDeclVAddr`, we skip relocating this atom. This commit also adds the glue code for lowering const slices in the ARM backend. --- src/arch/arm/CodeGen.zig | 25 ++++----- src/codegen.zig | 30 +++++------ src/link.zig | 14 +++-- src/link/Coff.zig | 8 +-- src/link/Elf.zig | 113 ++++++++++++++++++++++++++++++++------- src/link/MachO.zig | 53 +++++++++--------- src/link/Plan9.zig | 6 ++- 7 files changed, 163 insertions(+), 86 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 711e2a96f0..2c60027d97 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -3931,23 +3931,20 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { switch (typed_value.ty.zigTypeTag()) { .Pointer => switch (typed_value.ty.ptrSize()) { .Slice => { - var buf: Type.SlicePtrFieldTypeBuffer = undefined; - const ptr_type = typed_value.ty.slicePtrFieldType(&buf); - const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val }); - const slice_len = typed_value.val.sliceLen(); - // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean - // the Sema code needs to use anonymous Decls or alloca instructions to store data. - const ptr_imm = ptr_mcv.memory; - _ = slice_len; - _ = ptr_imm; - // We need more general support for const data being stored in memory to make this work. - return self.fail("TODO codegen for const slices", .{}); + return self.lowerUnnamedConst(typed_value); }, else => { - if (typed_value.val.tag() == .int_u64) { - return MCValue{ .immediate = @intCast(u32, typed_value.val.toUnsignedInt()) }; + switch (typed_value.val.tag()) { + .int_u64 => { + return MCValue{ .immediate = @intCast(u32, typed_value.val.toUnsignedInt()) }; + }, + .slice => { + return self.lowerUnnamedConst(typed_value); + }, + else => { + return self.fail("TODO codegen more kinds of const pointers", .{}); + }, } - return self.fail("TODO codegen more kinds of const pointers", .{}); }, }, .Int => { diff --git a/src/codegen.zig b/src/codegen.zig index 389f38a020..fd4321fee9 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -142,6 +142,7 @@ pub fn generateFunction( pub fn generateSymbol( bin_file: *link.File, + parent_atom_index: u32, src_loc: Module.SrcLoc, typed_value: TypedValue, code: *std.ArrayList(u8), @@ -177,7 +178,7 @@ pub fn generateSymbol( if (typed_value.ty.sentinel()) |sentinel| { try code.ensureUnusedCapacity(payload.data.len + 1); code.appendSliceAssumeCapacity(payload.data); - switch (try generateSymbol(bin_file, src_loc, .{ + switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{ .ty = typed_value.ty.elemType(), .val = sentinel, }, code, debug_output)) { @@ -197,7 +198,7 @@ pub fn generateSymbol( const elem_vals = typed_value.val.castTag(.array).?.data; const elem_ty = typed_value.ty.elemType(); for (elem_vals) |elem_val| { - switch (try generateSymbol(bin_file, src_loc, .{ + switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{ .ty = elem_ty, .val = elem_val, }, code, debug_output)) { @@ -223,11 +224,11 @@ pub fn generateSymbol( .Pointer => switch (typed_value.val.tag()) { .variable => { const decl = typed_value.val.castTag(.variable).?.data.owner_decl; - return lowerDeclRef(bin_file, src_loc, typed_value, decl, code, debug_output); + return lowerDeclRef(bin_file, parent_atom_index, src_loc, typed_value, decl, code, debug_output); }, .decl_ref => { const decl = typed_value.val.castTag(.decl_ref).?.data; - return lowerDeclRef(bin_file, src_loc, typed_value, decl, code, debug_output); + return lowerDeclRef(bin_file, parent_atom_index, src_loc, typed_value, decl, code, debug_output); }, .slice => { const slice = typed_value.val.castTag(.slice).?.data; @@ -235,7 +236,7 @@ pub fn generateSymbol( // generate ptr var buf: Type.SlicePtrFieldTypeBuffer = undefined; const slice_ptr_field_type = typed_value.ty.slicePtrFieldType(&buf); - switch (try generateSymbol(bin_file, src_loc, .{ + switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{ .ty = slice_ptr_field_type, .val = slice.ptr, }, code, debug_output)) { @@ -247,7 +248,7 @@ pub fn generateSymbol( } // generate length - switch (try generateSymbol(bin_file, src_loc, .{ + switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{ .ty = Type.initTag(.usize), .val = slice.len, }, code, debug_output)) { @@ -391,7 +392,7 @@ pub fn generateSymbol( const field_ty = typed_value.ty.structFieldType(index); if (!field_ty.hasRuntimeBits()) continue; - switch (try generateSymbol(bin_file, src_loc, .{ + switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{ .ty = field_ty, .val = field_val, }, code, debug_output)) { @@ -446,6 +447,7 @@ pub fn generateSymbol( fn lowerDeclRef( bin_file: *link.File, + parent_atom_index: u32, src_loc: Module.SrcLoc, typed_value: TypedValue, decl: *Module.Decl, @@ -456,7 +458,7 @@ fn lowerDeclRef( // generate ptr var buf: Type.SlicePtrFieldTypeBuffer = undefined; const slice_ptr_field_type = typed_value.ty.slicePtrFieldType(&buf); - switch (try generateSymbol(bin_file, src_loc, .{ + switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{ .ty = slice_ptr_field_type, .val = typed_value.val, }, code, debug_output)) { @@ -472,7 +474,7 @@ fn lowerDeclRef( .base = .{ .tag = .int_u64 }, .data = typed_value.val.sliceLen(), }; - switch (try generateSymbol(bin_file, src_loc, .{ + switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{ .ty = Type.initTag(.usize), .val = Value.initPayload(&slice_len.base), }, code, debug_output)) { @@ -495,15 +497,7 @@ fn lowerDeclRef( } decl.markAlive(); - const vaddr = vaddr: { - if (bin_file.cast(link.File.MachO)) |macho_file| { - break :vaddr try macho_file.getDeclVAddrWithReloc(decl, code.items.len); - } - // TODO handle the dependency of this symbol on the decl's vaddr. - // If the decl changes vaddr, then this symbol needs to get regenerated. - break :vaddr bin_file.getDeclVAddr(decl); - }; - + const vaddr = try bin_file.getDeclVAddr(decl, parent_atom_index, code.items.len); const endian = target.cpu.arch.endian(); switch (ptr_width) { 16 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(u16, vaddr), endian), diff --git a/src/link.zig b/src/link.zig index 56b88bffef..c5d14eb75a 100644 --- a/src/link.zig +++ b/src/link.zig @@ -684,12 +684,16 @@ pub const File = struct { } } - pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 { + /// Get allocated `Decl`'s address in virtual memory. + /// The linker is passed information about the containing atom, `parent_atom_index`, and offset within it's + /// memory buffer, `offset`, so that it can make a note of potential relocation sites, should the + /// `Decl`'s address was not yet resolved, or the containing atom gets moved in virtual memory. + pub fn getDeclVAddr(base: *File, decl: *const Module.Decl, parent_atom_index: u32, offset: u64) !u64 { switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl), - .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl), - .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl), - .plan9 => return @fieldParentPtr(Plan9, "base", base).getDeclVAddr(decl), + .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl, parent_atom_index, offset), + .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl, parent_atom_index, offset), + .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl, parent_atom_index, offset), + .plan9 => return @fieldParentPtr(Plan9, "base", base).getDeclVAddr(decl, parent_atom_index, offset), .c => unreachable, .wasm => unreachable, .spirv => unreachable, diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 2f500e6b91..32d4d38235 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -726,7 +726,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); - const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ + const res = try codegen.generateSymbol(&self.base, 0, decl.srcLoc(), .{ .ty = decl.ty, .val = decl.val, }, &code_buffer, .none); @@ -751,7 +751,7 @@ fn finishUpdateDecl(self: *Coff, module: *Module, decl: *Module.Decl, code: []co const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment); if (need_realloc) { - const curr_vaddr = self.getDeclVAddr(decl); + const curr_vaddr = self.text_section_virtual_address + decl.link.coff.text_offset; const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment); log.debug("growing {s} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr }); if (vaddr != curr_vaddr) { @@ -1465,7 +1465,9 @@ fn findLib(self: *Coff, arena: Allocator, name: []const u8) !?[]const u8 { return null; } -pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 { +pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl, parent_atom_index: u32, offset: u64) !u64 { + _ = parent_atom_index; + _ = offset; assert(self.llvm_object == null); return self.text_section_virtual_address + decl.link.coff.text_offset; } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 23bd5bb2dd..467bbeee54 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -145,6 +145,7 @@ decls: std.AutoHashMapUnmanaged(*Module.Decl, ?u16) = .{}, /// at present owned by Module.Decl. /// TODO consolidate this. managed_atoms: std.ArrayListUnmanaged(*TextBlock) = .{}, +atom_by_index_table: std.AutoHashMapUnmanaged(u32, *TextBlock) = .{}, /// Table of unnamed constants associated with a parent `Decl`. /// We store them here so that we can free the constants whenever the `Decl` @@ -179,6 +180,18 @@ dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{}, dbg_info_decl_first: ?*TextBlock = null, dbg_info_decl_last: ?*TextBlock = null, +/// A table of relocations indexed by the owning them `TextBlock`. +/// Note that once we refactor `TextBlock`'s lifetime and ownership rules, +/// this will be a table indexed by index into the list of Atoms. +relocs: RelocTable = .{}, + +const Reloc = struct { + target: u32, + offset: u64, + prev_vaddr: u64, +}; + +const RelocTable = std.AutoHashMapUnmanaged(*TextBlock, std.ArrayListUnmanaged(Reloc)); const UnnamedConstTable = std.AutoHashMapUnmanaged(*Module.Decl, std.ArrayListUnmanaged(*TextBlock)); /// When allocating, the ideal_capacity is calculated by @@ -397,12 +410,36 @@ pub fn deinit(self: *Elf) void { } self.unnamed_const_atoms.deinit(self.base.allocator); } + + { + var it = self.relocs.valueIterator(); + while (it.next()) |relocs| { + relocs.deinit(self.base.allocator); + } + self.relocs.deinit(self.base.allocator); + } + + self.atom_by_index_table.deinit(self.base.allocator); } -pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl) u64 { +pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl, parent_atom_index: u32, offset: u64) !u64 { assert(self.llvm_object == null); assert(decl.link.elf.local_sym_index != 0); - return self.local_symbols.items[decl.link.elf.local_sym_index].st_value; + + const target = decl.link.elf.local_sym_index; + const vaddr = self.local_symbols.items[target].st_value; + const atom = self.atom_by_index_table.get(parent_atom_index).?; + const gop = try self.relocs.getOrPut(self.base.allocator, atom); + if (!gop.found_existing) { + gop.value_ptr.* = .{}; + } + try gop.value_ptr.append(self.base.allocator, .{ + .target = target, + .offset = offset, + .prev_vaddr = vaddr, + }); + + return vaddr; } fn getDebugLineProgramOff(self: Elf) u32 { @@ -991,6 +1028,41 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { .p64 => 12, }; + { + var it = self.relocs.iterator(); + while (it.next()) |entry| { + const atom = entry.key_ptr.*; + const relocs = entry.value_ptr.*; + const source_sym = self.local_symbols.items[atom.local_sym_index]; + const source_shdr = self.sections.items[source_sym.st_shndx]; + + log.debug("relocating '{s}'", .{self.getString(source_sym.st_name)}); + + for (relocs.items) |*reloc| { + const target_sym = self.local_symbols.items[reloc.target]; + const target_vaddr = target_sym.st_value; + + if (target_vaddr == reloc.prev_vaddr) continue; + + const section_offset = (source_sym.st_value + reloc.offset) - source_shdr.sh_addr; + const file_offset = source_shdr.sh_offset + section_offset; + + log.debug(" ({x}: [() => 0x{x}] ({s}))", .{ + reloc.offset, + target_vaddr, + self.getString(target_sym.st_name), + }); + + switch (self.ptr_width) { + .p32 => try self.base.file.?.pwriteAll(mem.asBytes(&@intCast(u32, target_vaddr)), file_offset), + .p64 => try self.base.file.?.pwriteAll(mem.asBytes(&target_vaddr), file_offset), + } + + reloc.prev_vaddr = target_vaddr; + } + } + } + // Unfortunately these have to be buffered and done at the end because ELF does not allow // mixing local and global symbols within a symbol table. try self.writeAllGlobalSymbols(); @@ -2508,6 +2580,7 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void { log.debug("allocating symbol indexes for {s}", .{decl.name}); decl.link.elf.local_sym_index = try self.allocateLocalSymbol(); + try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.elf.local_sym_index, &decl.link.elf); if (self.offset_table_free_list.popOrNull()) |i| { decl.link.elf.offset_table_index = i; @@ -2525,6 +2598,7 @@ fn freeUnnamedConsts(self: *Elf, decl: *Module.Decl) void { self.freeTextBlock(atom, self.phdr_load_ro_index.?); self.local_symbol_free_list.append(self.base.allocator, atom.local_sym_index) catch {}; self.local_symbols.items[atom.local_sym_index].st_info = 0; + _ = self.atom_by_index_table.remove(atom.local_sym_index); } unnamed_consts.clearAndFree(self.base.allocator); } @@ -2543,11 +2617,11 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. if (decl.link.elf.local_sym_index != 0) { self.local_symbol_free_list.append(self.base.allocator, decl.link.elf.local_sym_index) catch {}; - self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {}; - self.local_symbols.items[decl.link.elf.local_sym_index].st_info = 0; - + _ = self.atom_by_index_table.remove(decl.link.elf.local_sym_index); decl.link.elf.local_sym_index = 0; + + self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {}; } // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing // is desired for both. @@ -2993,7 +3067,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { // TODO implement .debug_info for global variables const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; - const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ + const res = try codegen.generateSymbol(&self.base, decl.link.elf.local_sym_index, decl.srcLoc(), .{ .ty = decl.ty, .val = decl_val, }, &code_buffer, .{ @@ -3028,19 +3102,6 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl } const unnamed_consts = gop.value_ptr; - const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{ - .none = .{}, - }); - const code = switch (res) { - .externally_managed => |x| x, - .appended => code_buffer.items, - .fail => |em| { - decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, em); - return error.AnalysisFail; - }, - }; - const atom = try self.base.allocator.create(TextBlock); errdefer self.base.allocator.destroy(atom); atom.* = TextBlock.empty; @@ -3056,6 +3117,20 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl log.debug("allocating symbol indexes for {s}", .{name}); atom.local_sym_index = try self.allocateLocalSymbol(); + try self.atom_by_index_table.putNoClobber(self.base.allocator, atom.local_sym_index, atom); + + const res = try codegen.generateSymbol(&self.base, atom.local_sym_index, decl.srcLoc(), typed_value, &code_buffer, .{ + .none = .{}, + }); + const code = switch (res) { + .externally_managed => |x| x, + .appended => code_buffer.items, + .fail => |em| { + decl.analysis = .codegen_failure; + try module.failed_decls.put(module.gpa, decl, em); + return error.AnalysisFail; + }, + }; const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); const phdr_index = self.phdr_load_ro_index.?; diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 065145cdc8..37040f267f 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -3745,19 +3745,6 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.De } const unnamed_consts = gop.value_ptr; - const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{ - .none = .{}, - }); - const code = switch (res) { - .externally_managed => |x| x, - .appended => code_buffer.items, - .fail => |em| { - decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, em); - return error.AnalysisFail; - }, - }; - const name_str_index = blk: { const index = unnamed_consts.items.len; const name = try std.fmt.allocPrint(self.base.allocator, "__unnamed_{s}_{d}", .{ decl.name, index }); @@ -3772,12 +3759,27 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.De const match = (try self.getMatchingSection(.{ .segname = makeStaticString("__TEXT"), .sectname = makeStaticString("__const"), - .size = code.len, + .size = @sizeOf(u64), .@"align" = math.log2(required_alignment), })).?; const local_sym_index = try self.allocateLocalSymbol(); - const atom = try self.createEmptyAtom(local_sym_index, code.len, math.log2(required_alignment)); - mem.copy(u8, atom.code.items, code); + const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), math.log2(required_alignment)); + + const res = try codegen.generateSymbol(&self.base, local_sym_index, decl.srcLoc(), typed_value, &code_buffer, .{ + .none = .{}, + }); + const code = switch (res) { + .externally_managed => |x| x, + .appended => code_buffer.items, + .fail => |em| { + decl.analysis = .codegen_failure; + try module.failed_decls.put(module.gpa, decl, em); + return error.AnalysisFail; + }, + }; + + atom.code.clearRetainingCapacity(); + try atom.code.appendSlice(self.base.allocator, code); const addr = try self.allocateAtom(atom, code.len, required_alignment, match); log.debug("allocated atom for {s} at 0x{x}", .{ name, addr }); @@ -3841,7 +3843,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; const res = if (debug_buffers) |dbg| - try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ + try codegen.generateSymbol(&self.base, decl.link.elf.local_sym_index, decl.srcLoc(), .{ .ty = decl.ty, .val = decl_val, }, &code_buffer, .{ @@ -3852,7 +3854,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { }, }) else - try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ + try codegen.generateSymbol(&self.base, decl.link.elf.local_sym_index, decl.srcLoc(), .{ .ty = decl.ty, .val = decl_val, }, &code_buffer, .none); @@ -4341,16 +4343,17 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { } } -pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 { +pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl, parent_atom_index: u32, offset: u64) !u64 { + assert(self.llvm_object == null); assert(decl.link.macho.local_sym_index != 0); - return self.locals.items[decl.link.macho.local_sym_index].n_value; -} -pub fn getDeclVAddrWithReloc(self: *MachO, decl: *const Module.Decl, offset: u64) !u64 { - assert(decl.link.macho.local_sym_index != 0); - assert(self.active_decl != null); + // TODO cache local_sym_index => atom!!! + const atom: *Atom = blk: for (self.managed_atoms.items) |atom| { + if (atom.local_sym_index == parent_atom_index) { + break :blk atom; + } + } else unreachable; - const atom = &self.active_decl.?.link.macho; try atom.relocs.append(self.base.allocator, .{ .offset = @intCast(u32, offset), .target = .{ .local = decl.link.macho.local_sym_index }, diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index c2d6d61066..ee7272ca8d 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -302,7 +302,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void { var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; - const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ + const res = try codegen.generateSymbol(&self.base, @intCast(u32, decl.link.plan9.sym_index.?), decl.srcLoc(), .{ .ty = decl.ty, .val = decl_val, }, &code_buffer, .{ .none = .{} }); @@ -749,7 +749,9 @@ pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void { _ = self; _ = decl; } -pub fn getDeclVAddr(self: *Plan9, decl: *const Module.Decl) u64 { +pub fn getDeclVAddr(self: *Plan9, decl: *const Module.Decl, parent_atom_index: u32, offset: u64) !u64 { + _ = parent_atom_index; + _ = offset; if (decl.ty.zigTypeTag() == .Fn) { var start = self.bases.text; var it_file = self.fn_decl_table.iterator(); From 066758b1a296aa6f01d505f7b90d5aee2f387d30 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 11 Feb 2022 12:16:32 +0100 Subject: [PATCH 0126/2031] macho: correctly lower slices incl reloc and rebase tracking Match changes required to `Elf` linker, which enable lowering of const slices on `MachO` targets. Expand `Mir` instructions requiring the knowledge of the containing atom - pass the symbol index into the linker's table from codegen via mir to emitter, to then utilise it in the linker. --- src/arch/aarch64/CodeGen.zig | 16 ++++++++++- src/arch/aarch64/Emit.zig | 14 +++++----- src/arch/aarch64/Mir.zig | 8 +++++- src/arch/x86_64/CodeGen.zig | 52 ++++++++++++++++++++++++++++++++--- src/arch/x86_64/Emit.zig | 19 ++++++++----- src/arch/x86_64/Mir.zig | 21 ++++++++++---- src/arch/x86_64/PrintMir.zig | 4 +-- src/link/MachO.zig | 53 ++++++++++++++---------------------- src/link/Plan9.zig | 4 ++- 9 files changed, 130 insertions(+), 61 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 42f2c66df1..79fa38e275 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -1617,7 +1617,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { _ = try self.addInst(.{ .tag = .call_extern, - .data = .{ .extern_fn = n_strx }, + .data = .{ + .extern_fn = .{ + .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index, + .sym_name = n_strx, + }, + }, }); } else { return self.fail("TODO implement calling bitcasted functions", .{}); @@ -2485,9 +2490,18 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }); }, .memory => |addr| { + const owner_decl = self.mod_fn.owner_decl; + // TODO when refactoring LinkBlock, make this into a generic function. + const atom_index = switch (self.bin_file.tag) { + .macho => owner_decl.link.macho.local_sym_index, + .elf => owner_decl.link.elf.local_sym_index, + .plan9 => @intCast(u32, owner_decl.link.plan9.sym_index orelse 0), + else => return self.fail("TODO handle aarch64 load memory in {}", .{self.bin_file.tag}), + }; _ = try self.addInst(.{ .tag = .load_memory, .data = .{ .payload = try self.addExtra(Mir.LoadMemory{ + .atom_index = atom_index, .register = @enumToInt(reg), .addr = @intCast(u32, addr), }) }, diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 3528bae709..5b2610f508 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -537,7 +537,7 @@ fn mirDebugEpilogueBegin(self: *Emit) !void { fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void { assert(emit.mir.instructions.items(.tag)[inst] == .call_extern); - const n_strx = emit.mir.instructions.items(.data)[inst].extern_fn; + const extern_fn = emit.mir.instructions.items(.data)[inst].extern_fn; if (emit.bin_file.cast(link.File.MachO)) |macho_file| { const offset = blk: { @@ -547,9 +547,10 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void { break :blk offset; }; // Add relocation to the decl. - try macho_file.active_decl.?.link.macho.relocs.append(emit.bin_file.allocator, .{ + const atom = macho_file.atom_by_index_table.get(extern_fn.atom_index).?; + try atom.relocs.append(emit.bin_file.allocator, .{ .offset = offset, - .target = .{ .global = n_strx }, + .target = .{ .global = extern_fn.sym_name }, .addend = 0, .subtractor = null, .pcrel = true, @@ -613,10 +614,9 @@ fn mirLoadMemory(emit: *Emit, inst: Mir.Inst.Index) !void { )); if (emit.bin_file.cast(link.File.MachO)) |macho_file| { - // TODO I think the reloc might be in the wrong place. - const decl = macho_file.active_decl.?; + const atom = macho_file.atom_by_index_table.get(load_memory.atom_index).?; // Page reloc for adrp instruction. - try decl.link.macho.relocs.append(emit.bin_file.allocator, .{ + try atom.relocs.append(emit.bin_file.allocator, .{ .offset = offset, .target = .{ .local = addr }, .addend = 0, @@ -626,7 +626,7 @@ fn mirLoadMemory(emit: *Emit, inst: Mir.Inst.Index) !void { .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21), }); // Pageoff reloc for adrp instruction. - try decl.link.macho.relocs.append(emit.bin_file.allocator, .{ + try atom.relocs.append(emit.bin_file.allocator, .{ .offset = offset + 4, .target = .{ .local = addr }, .addend = 0, diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index 5b232a08c0..5546b32652 100644 --- a/src/arch/aarch64/Mir.zig +++ b/src/arch/aarch64/Mir.zig @@ -134,7 +134,12 @@ pub const Inst = struct { /// An extern function /// /// Used by e.g. call_extern - extern_fn: u32, + extern_fn: struct { + /// Index of the containing atom. + atom_index: u32, + /// Index into the linker's string table. + sym_name: u32, + }, /// A 16-bit immediate value. /// /// Used by e.g. svc @@ -278,6 +283,7 @@ pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end } pub const LoadMemory = struct { + atom_index: u32, register: u32, addr: u32, }; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index c0bb233499..f9235512a7 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1897,7 +1897,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .reg1 = addr_reg.to64(), .flags = flags, }).encode(), - .data = .{ .linker_sym_index = sym_index }, + .data = .{ + .load_reloc = .{ + .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index, + .sym_index = sym_index, + }, + }, }); break :blk addr_reg; }, @@ -2670,7 +2675,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { _ = try self.addInst(.{ .tag = .call_extern, .ops = undefined, - .data = .{ .extern_fn = n_strx }, + .data = .{ + .extern_fn = .{ + .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index, + .sym_name = n_strx, + }, + }, }); } else { return self.fail("TODO implement calling bitcasted functions", .{}); @@ -3550,7 +3560,12 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE .reg1 = addr_reg.to64(), .flags = flags, }).encode(), - .data = .{ .linker_sym_index = sym_index }, + .data = .{ + .load_reloc = .{ + .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index, + .sym_index = sym_index, + }, + }, }); break :blk addr_reg; }, @@ -3767,6 +3782,30 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro const reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = addr }); break :blk reg; }, + .direct_load, + .got_load, + => |sym_index| { + const flags: u2 = switch (mcv) { + .got_load => 0b00, + .direct_load => 0b01, + else => unreachable, + }; + const addr_reg = try self.register_manager.allocReg(null); + _ = try self.addInst(.{ + .tag = .lea_pie, + .ops = (Mir.Ops{ + .reg1 = addr_reg.to64(), + .flags = flags, + }).encode(), + .data = .{ + .load_reloc = .{ + .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index, + .sym_index = sym_index, + }, + }, + }); + break :blk addr_reg; + }, else => { return self.fail("TODO implement memcpy for setting stack from {}", .{mcv}); }, @@ -4202,7 +4241,12 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .reg1 = reg, .flags = flags, }).encode(), - .data = .{ .linker_sym_index = sym_index }, + .data = .{ + .load_reloc = .{ + .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index, + .sym_index = sym_index, + }, + }, }); // MOV reg, [reg] _ = try self.addInst(.{ diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 3f221f0f19..128ea52847 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -763,6 +763,7 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .lea_pie); const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const load_reloc = emit.mir.instructions.items(.data)[inst].load_reloc; // lea reg1, [rip + reloc] // RM @@ -772,18 +773,19 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0), emit.code, ); + const end_offset = emit.code.items.len; - const sym_index = emit.mir.instructions.items(.data)[inst].linker_sym_index; + if (emit.bin_file.cast(link.File.MachO)) |macho_file| { const reloc_type = switch (ops.flags) { 0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), else => return emit.fail("TODO unused LEA PIE variants 0b10 and 0b11", .{}), }; - const decl = macho_file.active_decl.?; - try decl.link.macho.relocs.append(emit.bin_file.allocator, .{ + const atom = macho_file.atom_by_index_table.get(load_reloc.atom_index).?; + try atom.relocs.append(emit.bin_file.allocator, .{ .offset = @intCast(u32, end_offset - 4), - .target = .{ .local = sym_index }, + .target = .{ .local = load_reloc.sym_index }, .addend = 0, .subtractor = null, .pcrel = true, @@ -801,17 +803,20 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .call_extern); - const n_strx = emit.mir.instructions.items(.data)[inst].extern_fn; + const extern_fn = emit.mir.instructions.items(.data)[inst].extern_fn; + const offset = blk: { // callq try lowerToDEnc(.call_near, 0, emit.code); break :blk @intCast(u32, emit.code.items.len) - 4; }; + if (emit.bin_file.cast(link.File.MachO)) |macho_file| { // Add relocation to the decl. - try macho_file.active_decl.?.link.macho.relocs.append(emit.bin_file.allocator, .{ + const atom = macho_file.atom_by_index_table.get(extern_fn.atom_index).?; + try atom.relocs.append(emit.bin_file.allocator, .{ .offset = offset, - .target = .{ .global = n_strx }, + .target = .{ .global = extern_fn.sym_name }, .addend = 0, .subtractor = null, .pcrel = true, diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index aaabcab04d..046cb0e9f6 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -185,7 +185,7 @@ pub const Inst = struct { /// 0b00 reg1, [rip + reloc] // via GOT emits X86_64_RELOC_GOT relocation /// 0b01 reg1, [rip + reloc] // direct load emits X86_64_RELOC_SIGNED relocation /// Notes: - /// * `Data` contains `linker_sym_index` + /// * `Data` contains `load_reloc` lea_pie, /// ops flags: form: @@ -350,10 +350,19 @@ pub const Inst = struct { /// A 32-bit immediate value. imm: u32, /// An extern function. - /// Index into the linker's string table. - extern_fn: u32, - /// Entry in the linker's symbol table. - linker_sym_index: u32, + extern_fn: struct { + /// Index of the containing atom. + atom_index: u32, + /// Index into the linker's string table. + sym_name: u32, + }, + /// PIE load relocation. + load_reloc: struct { + /// Index of the containing atom. + atom_index: u32, + /// Index into the linker's symbol table. + sym_index: u32, + }, /// Index into `extra`. Meaning of what can be found there is context-dependent. payload: u32, }; @@ -362,7 +371,7 @@ pub const Inst = struct { // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks. comptime { if (builtin.mode != .Debug) { - assert(@sizeOf(Inst) == 8); + assert(@sizeOf(Data) == 8); } } }; diff --git a/src/arch/x86_64/PrintMir.zig b/src/arch/x86_64/PrintMir.zig index 7c96b15210..4ab32878be 100644 --- a/src/arch/x86_64/PrintMir.zig +++ b/src/arch/x86_64/PrintMir.zig @@ -450,6 +450,7 @@ fn mirLea(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void { fn mirLeaPie(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void { const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); + const load_reloc = print.mir.instructions.items(.data)[inst].load_reloc; try w.print("lea {s}, ", .{@tagName(ops.reg1)}); switch (ops.reg1.size()) { 8 => try w.print("byte ptr ", .{}), @@ -459,9 +460,8 @@ fn mirLeaPie(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void { else => unreachable, } try w.print("[rip + 0x0] ", .{}); - const sym_index = print.mir.instructions.items(.data)[inst].linker_sym_index; if (print.bin_file.cast(link.File.MachO)) |macho_file| { - const target = macho_file.locals.items[sym_index]; + const target = macho_file.locals.items[load_reloc.sym_index]; const target_name = macho_file.getString(target.n_strx); try w.print("target@{s}", .{target_name}); } else { diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 37040f267f..4aa627ca39 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -40,6 +40,7 @@ const StringIndexContext = std.hash_map.StringIndexContext; const Trie = @import("MachO/Trie.zig"); const Type = @import("../type.zig").Type; const TypedValue = @import("../TypedValue.zig"); +const Value = @import("../value.zig").Value; pub const TextBlock = Atom; @@ -220,6 +221,7 @@ atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{}, /// at present owned by Module.Decl. /// TODO consolidate this. managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, +atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{}, /// Table of unnamed constants associated with a parent `Decl`. /// We store them here so that we can free the constants whenever the `Decl` @@ -248,12 +250,6 @@ unnamed_const_atoms: UnnamedConstTable = .{}, /// TODO consolidate this. decls: std.AutoArrayHashMapUnmanaged(*Module.Decl, ?MatchingSection) = .{}, -/// Currently active Module.Decl. -/// TODO this might not be necessary if we figure out how to pass Module.Decl instance -/// to codegen.genSetReg() or alternatively move PIE displacement for MCValue{ .memory = x } -/// somewhere else in the codegen. -active_decl: ?*Module.Decl = null, - const Entry = struct { target: Atom.Relocation.Target, atom: *Atom, @@ -3441,6 +3437,8 @@ pub fn deinit(self: *MachO) void { } self.unnamed_const_atoms.deinit(self.base.allocator); } + + self.atom_by_index_table.deinit(self.base.allocator); } pub fn closeFiles(self: MachO) void { @@ -3647,6 +3645,7 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { if (decl.link.macho.local_sym_index != 0) return; decl.link.macho.local_sym_index = try self.allocateLocalSymbol(); + try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.macho.local_sym_index, &decl.link.macho); try self.decls.putNoClobber(self.base.allocator, decl, null); const got_target = .{ .local = decl.link.macho.local_sym_index }; @@ -3693,8 +3692,6 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv } } - self.active_decl = decl; - const res = if (debug_buffers) |dbg| try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ .dwarf = .{ @@ -3756,14 +3753,9 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.De log.debug("allocating symbol indexes for {s}", .{name}); const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); - const match = (try self.getMatchingSection(.{ - .segname = makeStaticString("__TEXT"), - .sectname = makeStaticString("__const"), - .size = @sizeOf(u64), - .@"align" = math.log2(required_alignment), - })).?; const local_sym_index = try self.allocateLocalSymbol(); const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), math.log2(required_alignment)); + try self.atom_by_index_table.putNoClobber(self.base.allocator, local_sym_index, atom); const res = try codegen.generateSymbol(&self.base, local_sym_index, decl.srcLoc(), typed_value, &code_buffer, .{ .none = .{}, @@ -3780,6 +3772,8 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.De atom.code.clearRetainingCapacity(); try atom.code.appendSlice(self.base.allocator, code); + + const match = try self.getMatchingSectionAtom(atom, typed_value.ty, typed_value.val); const addr = try self.allocateAtom(atom, code.len, required_alignment, match); log.debug("allocated atom for {s} at 0x{x}", .{ name, addr }); @@ -3839,11 +3833,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { } } - self.active_decl = decl; - const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; const res = if (debug_buffers) |dbg| - try codegen.generateSymbol(&self.base, decl.link.elf.local_sym_index, decl.srcLoc(), .{ + try codegen.generateSymbol(&self.base, decl.link.macho.local_sym_index, decl.srcLoc(), .{ .ty = decl.ty, .val = decl_val, }, &code_buffer, .{ @@ -3854,7 +3846,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { }, }) else - try codegen.generateSymbol(&self.base, decl.link.elf.local_sym_index, decl.srcLoc(), .{ + try codegen.generateSymbol(&self.base, decl.link.macho.local_sym_index, decl.srcLoc(), .{ .ty = decl.ty, .val = decl_val, }, &code_buffer, .none); @@ -3908,13 +3900,11 @@ fn isElemTyPointer(ty: Type) bool { } } -fn getMatchingSectionDecl(self: *MachO, decl: *Module.Decl) !MatchingSection { - const code = decl.link.macho.code.items; - const alignment = decl.ty.abiAlignment(self.base.options.target); +fn getMatchingSectionAtom(self: *MachO, atom: *Atom, ty: Type, val: Value) !MatchingSection { + const code = atom.code.items; + const alignment = ty.abiAlignment(self.base.options.target); const align_log_2 = math.log2(alignment); - const ty = decl.ty; const zig_ty = ty.zigTypeTag(); - const val = decl.val; const mode = self.base.options.optimize_mode; const match: MatchingSection = blk: { // TODO finish and audit this function @@ -4023,9 +4013,11 @@ fn getMatchingSectionDecl(self: *MachO, decl: *Module.Decl) !MatchingSection { }, } }; + const local = self.locals.items[atom.local_sym_index]; const seg = self.load_commands.items[match.seg].segment; const sect = seg.sections.items[match.sect]; - log.debug(" allocating atom in '{s},{s}' ({d},{d})", .{ + log.debug(" allocating atom '{s}' in '{s},{s}' ({d},{d})", .{ + self.getString(local.n_strx), sect.segName(), sect.sectName(), match.seg, @@ -4041,7 +4033,7 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 const decl_ptr = self.decls.getPtr(decl).?; if (decl_ptr.* == null) { - decl_ptr.* = try self.getMatchingSectionDecl(decl); + decl_ptr.* = try self.getMatchingSectionAtom(&decl.link.macho, decl.ty, decl.val); } const match = decl_ptr.*.?; @@ -4290,6 +4282,8 @@ fn freeUnnamedConsts(self: *MachO, decl: *Module.Decl) void { }, true); self.locals_free_list.append(self.base.allocator, atom.local_sym_index) catch {}; self.locals.items[atom.local_sym_index].n_type = 0; + _ = self.atom_by_index_table.remove(atom.local_sym_index); + atom.local_sym_index = 0; } unnamed_consts.clearAndFree(self.base.allocator); } @@ -4316,6 +4310,7 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { } self.locals.items[decl.link.macho.local_sym_index].n_type = 0; + _ = self.atom_by_index_table.remove(decl.link.macho.local_sym_index); decl.link.macho.local_sym_index = 0; } if (self.d_sym) |*ds| { @@ -4347,13 +4342,7 @@ pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl, parent_atom_index: u assert(self.llvm_object == null); assert(decl.link.macho.local_sym_index != 0); - // TODO cache local_sym_index => atom!!! - const atom: *Atom = blk: for (self.managed_atoms.items) |atom| { - if (atom.local_sym_index == parent_atom_index) { - break :blk atom; - } - } else unreachable; - + const atom = self.atom_by_index_table.get(parent_atom_index).?; try atom.relocs.append(self.base.allocator, .{ .offset = @intCast(u32, offset), .target = .{ .local = decl.link.macho.local_sym_index }, diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index ee7272ca8d..4eaa6ed26b 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -302,7 +302,9 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void { var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; - const res = try codegen.generateSymbol(&self.base, @intCast(u32, decl.link.plan9.sym_index.?), decl.srcLoc(), .{ + // TODO we need the symbol index for symbol in the table of locals for the containing atom + const sym_index = decl.link.plan9.sym_index orelse 0; + const res = try codegen.generateSymbol(&self.base, @intCast(u32, sym_index), decl.srcLoc(), .{ .ty = decl.ty, .val = decl_val, }, &code_buffer, .{ .none = .{} }); From cad3e3e63a238902cdd80eb2504c879d6637a4d5 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 11 Feb 2022 12:35:31 +0100 Subject: [PATCH 0127/2031] x64: enable more behavior tests --- test/behavior.zig | 2 +- test/behavior/basic.zig | 9 ------- test/behavior/slice.zig | 58 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/test/behavior.zig b/test/behavior.zig index a1d8e9bef9..86e48f1797 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -38,6 +38,7 @@ test { _ = @import("behavior/optional.zig"); _ = @import("behavior/prefetch.zig"); _ = @import("behavior/pub_enum.zig"); + _ = @import("behavior/slice.zig"); _ = @import("behavior/slice_sentinel_comptime.zig"); _ = @import("behavior/type.zig"); _ = @import("behavior/truncate.zig"); @@ -76,7 +77,6 @@ test { _ = @import("behavior/pointers.zig"); _ = @import("behavior/ptrcast.zig"); _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); - _ = @import("behavior/slice.zig"); _ = @import("behavior/src.zig"); _ = @import("behavior/this.zig"); _ = @import("behavior/try.zig"); diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index d87d04e246..5d7bb3b9a7 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -120,14 +120,12 @@ test "return string from function" { test "hex escape" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; try expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); } test "multiline string" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const s1 = \\one @@ -140,7 +138,6 @@ test "multiline string" { test "multiline string comments at start" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const s1 = //\\one @@ -153,7 +150,6 @@ test "multiline string comments at start" { test "multiline string comments at end" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const s1 = \\one @@ -166,7 +162,6 @@ test "multiline string comments at end" { test "multiline string comments in middle" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const s1 = \\one @@ -179,7 +174,6 @@ test "multiline string comments in middle" { test "multiline string comments at multiple places" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const s1 = \\one @@ -193,14 +187,11 @@ test "multiline string comments at multiple places" { } test "string concatenation" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); } test "array mult operator" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; try expect(mem.eql(u8, "ab" ** 5, "ababababab")); } diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 64bd972ead..327b8f4f76 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -27,7 +27,10 @@ comptime { } test "slicing" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + var array: [20]i32 = undefined; array[5] = 1234; @@ -45,6 +48,8 @@ test "slicing" { test "const slice" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + comptime { const a = "1234567890"; try expect(a.len == 10); @@ -56,6 +61,8 @@ test "const slice" { test "comptime slice of undefined pointer of length 0" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + const slice1 = @as([*]i32, undefined)[0..0]; try expect(slice1.len == 0); const slice2 = @as([*]i32, undefined)[100..100]; @@ -64,6 +71,8 @@ test "comptime slice of undefined pointer of length 0" { test "implicitly cast array of size 0 to slice" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + var msg = [_]u8{}; try assertLenIsZero(&msg); } @@ -74,6 +83,8 @@ fn assertLenIsZero(msg: []const u8) !void { test "access len index of sentinel-terminated slice" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + const S = struct { fn doTheTest() !void { var slice: [:0]const u8 = "hello"; @@ -88,6 +99,8 @@ test "access len index of sentinel-terminated slice" { test "comptime slice of slice preserves comptime var" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + comptime { var buff: [10]u8 = undefined; buff[0..][0..][0] = 1; @@ -97,6 +110,8 @@ test "comptime slice of slice preserves comptime var" { test "slice of type" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + comptime { var types_array = [_]type{ i32, f64, type }; for (types_array) |T, i| { @@ -120,6 +135,9 @@ test "slice of type" { test "generic malloc free" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const a = memAlloc(u8, 10) catch unreachable; memFree(u8, a); } @@ -133,6 +151,8 @@ fn memFree(comptime T: type, memory: []T) void { test "slice of hardcoded address to pointer" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + const S = struct { fn doTheTest() !void { const pointer = @intToPtr([*]u8, 0x04)[0..2]; @@ -148,6 +168,8 @@ test "slice of hardcoded address to pointer" { test "comptime slice of pointer preserves comptime var" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + comptime { var buff: [10]u8 = undefined; var a = @ptrCast([*]u8, &buff); @@ -158,6 +180,8 @@ test "comptime slice of pointer preserves comptime var" { test "comptime pointer cast array and then slice" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; const ptrA: [*]const u8 = @ptrCast([*]const u8, &array); @@ -172,6 +196,9 @@ test "comptime pointer cast array and then slice" { test "slicing zero length array" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const s1 = ""[0..]; const s2 = ([_]u32{})[0..]; try expect(s1.len == 0); @@ -185,6 +212,8 @@ const y = x[0x100..]; test "compile time slice of pointer to hard coded address" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; try expect(@ptrToInt(x) == 0x1000); try expect(x.len == 0x500); @@ -194,6 +223,9 @@ test "compile time slice of pointer to hard coded address" { } test "slice string literal has correct type" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + comptime { try expect(@TypeOf("aoeu"[0..]) == *const [4:0]u8); const array = [_]i32{ 1, 2, 3, 4 }; @@ -207,6 +239,7 @@ test "slice string literal has correct type" { test "result location zero sized array inside struct field implicit cast to slice" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const E = struct { entries: []u32, @@ -216,6 +249,9 @@ test "result location zero sized array inside struct field implicit cast to slic } test "runtime safety lets us slice from len..len" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + var an_array = [_]u8{ 1, 2, 3 }; try expect(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), "")); } @@ -225,6 +261,9 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { } test "C pointer" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf"; var len: u32 = 10; var slice = buf[0..len]; @@ -232,6 +271,9 @@ test "C pointer" { } test "C pointer slice access" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + var buf: [10]u32 = [1]u32{42} ** 10; const c_ptr = @ptrCast([*c]const u32, &buf); @@ -245,6 +287,8 @@ test "C pointer slice access" { } test "comptime slices are disambiguated" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + try expect(sliceSum(&[_]u8{ 1, 2 }) == 3); try expect(sliceSum(&[_]u8{ 3, 4 }) == 7); } @@ -258,6 +302,9 @@ fn sliceSum(comptime q: []const u8) i32 { } test "slice type with custom alignment" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const LazilyResolvedType = struct { anything: i32, }; @@ -269,6 +316,8 @@ test "slice type with custom alignment" { } test "obtaining a null terminated slice" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO // here we have a normal array @@ -294,6 +343,7 @@ test "obtaining a null terminated slice" { test "empty array to slice" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -312,6 +362,9 @@ test "empty array to slice" { } test "@ptrCast slice to pointer" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const S = struct { fn doTheTest() !void { var array align(@alignOf(u16)) = [5]u8{ 0xff, 0xff, 0xff, 0xff, 0xff }; @@ -327,6 +380,7 @@ test "@ptrCast slice to pointer" { test "slice syntax resulting in pointer-to-array" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -475,6 +529,7 @@ test "slice syntax resulting in pointer-to-array" { test "type coercion of pointer to anon struct literal to pointer to slice" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { const U = union { @@ -508,6 +563,7 @@ test "type coercion of pointer to anon struct literal to pointer to slice" { test "array concat of slices gives slice" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; comptime { var a: []const u8 = "aoeu"; @@ -519,6 +575,7 @@ test "array concat of slices gives slice" { test "slice bounds in comptime concatenation" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const bs = comptime blk: { const b = "........1........"; @@ -535,6 +592,7 @@ test "slice bounds in comptime concatenation" { test "slice sentinel access at comptime" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; { const str0 = &[_:0]u8{ '1', '2', '3' }; From bd8d6a8342914974ca163fa75db0562d181f6d27 Mon Sep 17 00:00:00 2001 From: m Date: Fri, 11 Feb 2022 15:28:36 +0100 Subject: [PATCH 0128/2031] std: validate frame-pointer address in stack walking --- lib/std/c.zig | 1 + lib/std/c/linux.zig | 1 + lib/std/debug.zig | 24 +++++++++++++++++++++++- lib/std/os.zig | 14 ++++++++++++++ lib/std/os/linux.zig | 10 ++++++++++ 5 files changed, 49 insertions(+), 1 deletion(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 6d904c8b98..396a6f6702 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -123,6 +123,7 @@ pub extern "c" fn write(fd: c.fd_t, buf: [*]const u8, nbyte: usize) isize; pub extern "c" fn pwrite(fd: c.fd_t, buf: [*]const u8, nbyte: usize, offset: c.off_t) isize; pub extern "c" fn mmap(addr: ?*align(page_size) anyopaque, len: usize, prot: c_uint, flags: c_uint, fd: c.fd_t, offset: c.off_t) *anyopaque; pub extern "c" fn munmap(addr: *align(page_size) const anyopaque, len: usize) c_int; +pub extern "c" fn msync(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int; pub extern "c" fn mprotect(addr: *align(page_size) anyopaque, len: usize, prot: c_uint) c_int; pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: c_int) c_int; pub extern "c" fn linkat(oldfd: c.fd_t, oldpath: [*:0]const u8, newfd: c.fd_t, newpath: [*:0]const u8, flags: c_int) c_int; diff --git a/lib/std/c/linux.zig b/lib/std/c/linux.zig index db55b5d850..be136bcbea 100644 --- a/lib/std/c/linux.zig +++ b/lib/std/c/linux.zig @@ -30,6 +30,7 @@ pub const MAP = struct { /// Only used by libc to communicate failure. pub const FAILED = @intToPtr(*anyopaque, maxInt(usize)); }; +pub const MSF = linux.MSF; pub const MMAP2_UNIT = linux.MMAP2_UNIT; pub const MSG = linux.MSG; pub const NAME_MAX = linux.NAME_MAX; diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 69a68faad6..be47985041 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -424,6 +424,28 @@ pub const StackIterator = struct { return address; } + fn isValidMemory(address: u64) bool { + if (native_os != .windows) { + var res = true; + const length = 2 * mem.page_size; + const aligned_address = address & ~@intCast(u64, (mem.page_size - 1)); + const aligned_memory = @intToPtr([*]align(mem.page_size) u8, aligned_address)[0..length]; + + os.msync(aligned_memory, os.MSF.ASYNC) catch |err| { + switch (err) { + os.MSyncError.UnmappedMemory => { + res = false; + }, + else => unreachable, + } + }; + return res; + } else { + // TODO: Using windows memory API check if a page is mapped + return true; + } + } + fn next_internal(self: *StackIterator) ?usize { const fp = if (comptime native_arch.isSPARC()) // On SPARC the offset is positive. (!) @@ -432,7 +454,7 @@ pub const StackIterator = struct { math.sub(usize, self.fp, fp_offset) catch return null; // Sanity check. - if (fp == 0 or !mem.isAligned(fp, @alignOf(usize))) + if (fp == 0 or !mem.isAligned(fp, @alignOf(usize)) or !isValidMemory(fp)) return null; const new_fp = math.add(usize, @intToPtr(*const usize, fp).*, fp_bias) catch return null; diff --git a/lib/std/os.zig b/lib/std/os.zig index a935cfd8c0..16a32766dc 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -88,6 +88,7 @@ pub const Kevent = system.Kevent; pub const LOCK = system.LOCK; pub const MADV = system.MADV; pub const MAP = system.MAP; +pub const MSF = system.MSF; pub const MAX_ADDR_LEN = system.MAX_ADDR_LEN; pub const MMAP2_UNIT = system.MMAP2_UNIT; pub const MSG = system.MSG; @@ -4016,6 +4017,19 @@ pub fn munmap(memory: []align(mem.page_size) const u8) void { } } +pub const MSyncError = error{ + UnmappedMemory, +} || UnexpectedError; + +pub fn msync(memory: []align(mem.page_size) u8, flags: i32) MSyncError!void { + switch (errno(system.msync(memory.ptr, memory.len, flags))) { + .SUCCESS => return, + .NOMEM => return error.UnmappedMemory, // Unsuccessful, provided pointer does not point mapped memory + .INVAL => unreachable, // Invalid parameters. + else => unreachable, + } +} + pub const AccessError = error{ PermissionDenied, FileNotFound, diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index c1591f7ea1..608db08a9c 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -406,6 +406,16 @@ pub fn mprotect(address: [*]const u8, length: usize, protection: usize) usize { return syscall3(.mprotect, @ptrToInt(address), length, protection); } +pub const MSF = struct { + pub const ASYNC = 1; + pub const INVALIDATE = 2; + pub const SYNC = 4; +}; + +pub fn msync(address: [*]const u8, length: usize, flags: u32) usize { + return syscall3(.msync, @ptrToInt(address), length, flags); +} + pub fn munmap(address: [*]const u8, length: usize) usize { return syscall2(.munmap, @ptrToInt(address), length); } From 70d7f87be00aa1a372c856759948fd62666be295 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Thu, 10 Feb 2022 13:29:48 -0700 Subject: [PATCH 0129/2031] Fix up sign handling and add arbitrary-length integer support to @bitCast() --- lib/std/math/big/int.zig | 174 +++++++++++++++++++++++++++------- lib/std/math/big/int_test.zig | 25 +++++ src/value.zig | 5 +- test/behavior/bitcast.zig | 107 ++++++++++++++++++++- 4 files changed, 270 insertions(+), 41 deletions(-) diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 87a62bf66c..1c6404fb3c 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -1,4 +1,5 @@ const std = @import("../../std.zig"); +const builtin = @import("builtin"); const math = std.math; const Limb = std.math.big.Limb; const limb_bits = @typeInfo(Limb).Int.bits; @@ -14,6 +15,7 @@ const minInt = std.math.minInt; const assert = std.debug.assert; const Endian = std.builtin.Endian; const Signedness = std.builtin.Signedness; +const native_endian = builtin.cpu.arch.endian(); const debug_safety = false; @@ -1621,6 +1623,15 @@ pub const Mutable = struct { } } + /// Read the value of `x` from `buffer` + /// Asserts that `buffer` and `bit_count` are large enough to store the value. + /// + /// For integers with a well-defined layout (e.g. all power-of-two integers), this function + /// reads from `buffer` as if it were the contents of @ptrCast([]const u8, &x), where the + /// slice length is taken to be @sizeOf(std.meta.Int(signedness, )) + /// + /// For integers with a non-well-defined layout, `buffer` must have been created by + /// writeTwosComplement. pub fn readTwosComplement( x: *Mutable, buffer: []const u8, @@ -1634,26 +1645,77 @@ pub const Mutable = struct { x.positive = true; return; } - // zig fmt: off - switch (signedness) { - .signed => { - if (bit_count <= 8) return x.set(mem.readInt( i8, buffer[0.. 1], endian)); - if (bit_count <= 16) return x.set(mem.readInt( i16, buffer[0.. 2], endian)); - if (bit_count <= 32) return x.set(mem.readInt( i32, buffer[0.. 4], endian)); - if (bit_count <= 64) return x.set(mem.readInt( i64, buffer[0.. 8], endian)); - if (bit_count <= 128) return x.set(mem.readInt(i128, buffer[0..16], endian)); - }, - .unsigned => { - if (bit_count <= 8) return x.set(mem.readInt( u8, buffer[0.. 1], endian)); - if (bit_count <= 16) return x.set(mem.readInt( u16, buffer[0.. 2], endian)); - if (bit_count <= 32) return x.set(mem.readInt( u32, buffer[0.. 4], endian)); - if (bit_count <= 64) return x.set(mem.readInt( u64, buffer[0.. 8], endian)); - if (bit_count <= 128) return x.set(mem.readInt(u128, buffer[0..16], endian)); - }, - } - // zig fmt: on - @panic("TODO implement std lib big int readTwosComplement"); + // byte_count is the total amount of bytes to read from buffer + var byte_count = @sizeOf(Limb) * (bit_count / @bitSizeOf(Limb)); + if (bit_count % @bitSizeOf(Limb) != 0) { // Round up to a power-of-two integer <= Limb + byte_count += (std.math.ceilPowerOfTwoAssert(usize, bit_count % @bitSizeOf(Limb)) + 7) / 8; + } + + const limb_count = calcTwosCompLimbCount(8 * byte_count); + + // Check whether the input is negative + var positive = true; + if (signedness == .signed) { + var last_byte = switch (endian) { + .Little => ((bit_count + 7) / 8) - 1, + .Big => byte_count - ((bit_count + 7) / 8), + }; + + const sign_bit = @as(u8, 1) << @intCast(u3, (bit_count - 1) % 8); + positive = ((buffer[last_byte] & sign_bit) == 0); + } + + // Copy all complete limbs + var carry: u1 = if (positive) 0 else 1; + var limb_index: usize = 0; + while (limb_index < bit_count / @bitSizeOf(Limb)) : (limb_index += 1) { + var buf_index = switch (endian) { + .Little => @sizeOf(Limb) * limb_index, + .Big => byte_count - (limb_index + 1) * @sizeOf(Limb), + }; + + const limb_buf = @ptrCast(*const [@sizeOf(Limb)]u8, buffer[buf_index..]); + var limb = mem.readInt(Limb, limb_buf, endian); + + // 2's complement (bitwise not, then add carry bit) + if (!positive) carry = @boolToInt(@addWithOverflow(Limb, ~limb, carry, &limb)); + x.limbs[limb_index] = limb; + } + + // Copy any remaining bytes, using the nearest power-of-two integer that is large enough + const bits_left = @intCast(Log2Limb, bit_count % @bitSizeOf(Limb)); + if (bits_left != 0) { + const bytes_read = limb_index * @sizeOf(Limb); + const bytes_left = byte_count - bytes_read; + var buffer_left = switch (endian) { + .Little => buffer[bytes_read..], + .Big => buffer[0..], + }; + + var limb = @intCast(Limb, blk: { + // zig fmt: off + if (bytes_left == 1) break :blk mem.readInt( u8, buffer_left[0.. 1], endian); + if (bytes_left == 2) break :blk mem.readInt( u16, buffer_left[0.. 2], endian); + if (bytes_left == 4) break :blk mem.readInt( u32, buffer_left[0.. 4], endian); + if (bytes_left == 8) break :blk mem.readInt( u64, buffer_left[0.. 8], endian); + if (bytes_left == 16) break :blk mem.readInt(u128, buffer_left[0..16], endian); + // zig fmt: on + unreachable; + }); + + // 2's complement (bitwise not, then add carry bit) + if (!positive) _ = @addWithOverflow(Limb, ~limb, carry, &limb); + + // Mask off any unused bits + const mask = (@as(Limb, 1) << bits_left) -% 1; // 0b0..01..1 with (bits_left) trailing ones + limb &= mask; + + x.limbs[limb_count - 1] = limb; + } + x.positive = positive; + x.len = limb_count; + x.normalize(x.len); } /// Normalize a possible sequence of leading zeros. @@ -1806,7 +1868,7 @@ pub const Const = struct { .Int => |info| { const UT = std.meta.Int(.unsigned, info.bits); - if (self.bitCountTwosComp() > info.bits) { + if (!self.fitsInTwosComp(info.signedness, info.bits)) { return error.TargetTooSmall; } @@ -2013,27 +2075,69 @@ pub const Const = struct { return s.len; } + /// Write the value of `x` into `buffer` /// Asserts that `buffer` and `bit_count` are large enough to store the value. + /// + /// For integers with a well-defined layout (e.g. all power-of-two integers), this function + /// can be thought of as writing to `buffer` the contents of @ptrCast([]const u8, &x), + /// where the slice length is taken to be @sizeOf(std.meta.Int(_,)) + /// + /// For integers with a non-well-defined layout, the only requirement is that readTwosComplement + /// on the same buffer creates an equivalent big integer. pub fn writeTwosComplement(x: Const, buffer: []u8, bit_count: usize, endian: Endian) void { if (bit_count == 0) return; - // zig fmt: off - if (x.positive) { - if (bit_count <= 8) return mem.writeInt( u8, buffer[0.. 1], x.to( u8) catch unreachable, endian); - if (bit_count <= 16) return mem.writeInt( u16, buffer[0.. 2], x.to( u16) catch unreachable, endian); - if (bit_count <= 32) return mem.writeInt( u32, buffer[0.. 4], x.to( u32) catch unreachable, endian); - if (bit_count <= 64) return mem.writeInt( u64, buffer[0.. 8], x.to( u64) catch unreachable, endian); - if (bit_count <= 128) return mem.writeInt(u128, buffer[0..16], x.to(u128) catch unreachable, endian); - } else { - if (bit_count <= 8) return mem.writeInt( i8, buffer[0.. 1], x.to( i8) catch unreachable, endian); - if (bit_count <= 16) return mem.writeInt( i16, buffer[0.. 2], x.to( i16) catch unreachable, endian); - if (bit_count <= 32) return mem.writeInt( i32, buffer[0.. 4], x.to( i32) catch unreachable, endian); - if (bit_count <= 64) return mem.writeInt( i64, buffer[0.. 8], x.to( i64) catch unreachable, endian); - if (bit_count <= 128) return mem.writeInt(i128, buffer[0..16], x.to(i128) catch unreachable, endian); + var byte_count = @sizeOf(Limb) * (bit_count / @bitSizeOf(Limb)); + if (bit_count % @bitSizeOf(Limb) != 0) { + byte_count += (std.math.ceilPowerOfTwoAssert(usize, bit_count % @bitSizeOf(Limb)) + 7) / 8; } - // zig fmt: on + assert(buffer.len >= byte_count); + assert(x.fitsInTwosComp(if (x.positive) .unsigned else .signed, bit_count)); - @panic("TODO implement std lib big int writeTwosComplement for larger than 128 bits"); + // Copy all complete limbs + var carry: u1 = if (x.positive) 0 else 1; + var limb_index: usize = 0; + while (limb_index < byte_count / @sizeOf(Limb)) : (limb_index += 1) { + var buf_index = switch (endian) { + .Little => @sizeOf(Limb) * limb_index, + .Big => byte_count - (limb_index + 1) * @sizeOf(Limb), + }; + + var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0; + // 2's complement (bitwise not, then add carry bit) + if (!x.positive) carry = @boolToInt(@addWithOverflow(Limb, ~limb, carry, &limb)); + + var limb_buf = @ptrCast(*[@sizeOf(Limb)]u8, buffer[buf_index..]); + mem.writeInt(Limb, limb_buf, limb, endian); + } + + // Copy any remaining bytes + if (byte_count % @sizeOf(Limb) != 0) { + const bytes_read = limb_index * @sizeOf(Limb); + const bytes_left = byte_count - bytes_read; + var buffer_left = switch (endian) { + .Little => buffer[bytes_read..], + .Big => buffer[0..], + }; + + var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0; + // 2's complement (bitwise not, then add carry bit) + if (!x.positive) _ = @addWithOverflow(Limb, ~limb, carry, &limb); + + if (bytes_left == 1) { + mem.writeInt(u8, buffer_left[0..1], @truncate(u8, limb), endian); + } else if (@sizeOf(Limb) > 1 and bytes_left == 2) { + mem.writeInt(u16, buffer_left[0..2], @truncate(u16, limb), endian); + } else if (@sizeOf(Limb) > 2 and bytes_left == 4) { + mem.writeInt(u32, buffer_left[0..4], @truncate(u32, limb), endian); + } else if (@sizeOf(Limb) > 4 and bytes_left == 8) { + mem.writeInt(u64, buffer_left[0..8], @truncate(u64, limb), endian); + } else if (@sizeOf(Limb) > 8 and bytes_left == 16) { + mem.writeInt(u128, buffer_left[0..16], @truncate(u128, limb), endian); + } else if (@sizeOf(Limb) > 16) { + @compileError("@sizeOf(Limb) exceeded supported range"); + } else unreachable; + } } /// Returns `math.Order.lt`, `math.Order.eq`, `math.Order.gt` if diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index 70a9b97a38..f6f210f56c 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -2486,3 +2486,28 @@ test "big int popcount" { try testing.expect(a.toConst().orderAgainstScalar(16) == .eq); } + +test "big int conversion read/write twos complement" { + var a = try Managed.initSet(testing.allocator, (1 << 493) - 1); + defer a.deinit(); + var b = try Managed.initSet(testing.allocator, (1 << 493) - 1); + defer b.deinit(); + var m = b.toMutable(); + + var buffer1 = try testing.allocator.alloc(u8, 64); + defer testing.allocator.free(buffer1); + + const endians = [_]std.builtin.Endian{ .Little, .Big }; + + for (endians) |endian| { + // Writing to buffer and back should not change anything + a.toConst().writeTwosComplement(buffer1, 493, endian); + m.readTwosComplement(buffer1, 493, endian, .unsigned); + try testing.expect(m.toConst().order(a.toConst()) == .eq); + + // Equivalent to @bitCast(i493, @as(u493, intMax(u493)) + a.toConst().writeTwosComplement(buffer1, 493, endian); + m.readTwosComplement(buffer1, 493, endian, .signed); + try testing.expect(m.toConst().orderAgainstScalar(-1) == .eq); + } +} diff --git a/src/value.zig b/src/value.zig index 33a75e08bb..2018eb3df3 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1093,8 +1093,9 @@ pub const Value = extern union { .Int => { const int_info = ty.intInfo(target); const endian = target.cpu.arch.endian(); - // TODO use a correct amount of limbs - const limbs_buffer = try arena.alloc(std.math.big.Limb, 2); + const Limb = std.math.big.Limb; + const limb_count = (buffer.len + @sizeOf(Limb) - 1) / @sizeOf(Limb); + const limbs_buffer = try arena.alloc(Limb, limb_count); var bigint = BigIntMutable.init(limbs_buffer, 0); bigint.readTwosComplement(buffer, int_info.bits, endian, int_info.signedness); return fromBigInt(arena, bigint.toConst()); diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index d56e3c1c53..43d6524a4e 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -3,6 +3,7 @@ const builtin = @import("builtin"); const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; const maxInt = std.math.maxInt; +const minInt = std.math.minInt; const native_endian = builtin.target.cpu.arch.endian(); test "@bitCast i32 -> u32" { @@ -11,21 +12,119 @@ test "@bitCast i32 -> u32" { } fn testBitCast_i32_u32() !void { - try expect(conv(-1) == maxInt(u32)); - try expect(conv2(maxInt(u32)) == -1); + try expect(conv_i32(-1) == maxInt(u32)); + try expect(conv_u32(maxInt(u32)) == -1); + try expect(conv_u32(0x8000_0000) == minInt(i32)); + try expect(conv_i32(minInt(i32)) == 0x8000_0000); } -fn conv(x: i32) u32 { +fn conv_i32(x: i32) u32 { return @bitCast(u32, x); } -fn conv2(x: u32) i32 { +fn conv_u32(x: u32) i32 { return @bitCast(i32, x); } +test "@bitCast i48 -> u48" { + try testBitCast_i48_u48(); + comptime try testBitCast_i48_u48(); +} + +fn testBitCast_i48_u48() !void { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + try expect(conv_i48(-1) == maxInt(u48)); + try expect(conv_u48(maxInt(u48)) == -1); + try expect(conv_u48(0x8000_0000_0000) == minInt(i48)); + try expect(conv_i48(minInt(i48)) == 0x8000_0000_0000); +} + +fn conv_i48(x: i48) u48 { + return @bitCast(u48, x); +} + +fn conv_u48(x: u48) i48 { + return @bitCast(i48, x); +} + +test "@bitCast i27 -> u27" { + try testBitCast_i27_u27(); + comptime try testBitCast_i27_u27(); +} + +fn testBitCast_i27_u27() !void { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + try expect(conv_i27(-1) == maxInt(u27)); + try expect(conv_u27(maxInt(u27)) == -1); + try expect(conv_u27(0x400_0000) == minInt(i27)); + try expect(conv_i27(minInt(i27)) == 0x400_0000); +} + +fn conv_i27(x: i27) u27 { + return @bitCast(u27, x); +} + +fn conv_u27(x: u27) i27 { + return @bitCast(i27, x); +} + +test "@bitCast i512 -> u512" { + try testBitCast_i512_u512(); + comptime try testBitCast_i512_u512(); +} + +fn testBitCast_i512_u512() !void { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + try expect(conv_i512(-1) == maxInt(u512)); + try expect(conv_u512(maxInt(u512)) == -1); + try expect(conv_u512(@as(u512, 1) << 511) == minInt(i512)); + try expect(conv_i512(minInt(i512)) == (@as(u512, 1) << 511)); +} + +fn conv_i512(x: i512) u512 { + return @bitCast(u512, x); +} + +fn conv_u512(x: u512) i512 { + return @bitCast(i512, x); +} + test "bitcast result to _" { _ = @bitCast(u8, @as(i8, 1)); } +test "@bitCast i493 -> u493" { + try testBitCast_i493_u493(); + comptime try testBitCast_i493_u493(); +} + +fn testBitCast_i493_u493() !void { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + try expect(conv_i493(-1) == maxInt(u493)); + try expect(conv_u493(maxInt(u493)) == -1); + try expect(conv_u493(@as(u493, 1) << 492) == minInt(i493)); + try expect(conv_i493(minInt(i493)) == (@as(u493, 1) << 492)); +} + +fn conv_i493(x: i493) u493 { + return @bitCast(u493, x); +} + +fn conv_u493(x: u493) i493 { + return @bitCast(i493, x); +} + test "nested bitcast" { const S = struct { fn moo(x: isize) !void { From eeb043f5833db03ac3250f9943ba8be0b518432f Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Fri, 11 Feb 2022 08:46:01 -0700 Subject: [PATCH 0130/2031] Fix big-endian handling in stage1 bigint_write_twos_complement --- src/stage1/bigint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stage1/bigint.cpp b/src/stage1/bigint.cpp index eab0f037cf..e10af08bc4 100644 --- a/src/stage1/bigint.cpp +++ b/src/stage1/bigint.cpp @@ -313,12 +313,12 @@ void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bi } if (digit_index == 0) break; - digit_index -= 1; if (digit_index == last_digit_index) { buf_index += bytes_in_last_digit; } else { buf_index += 8; } + digit_index -= 1; } } else { size_t digit_count = (bit_count + 63) / 64; From 65299c37d1b4b4395616d6f86b5f064000951cf6 Mon Sep 17 00:00:00 2001 From: m Date: Fri, 11 Feb 2022 20:11:23 +0100 Subject: [PATCH 0131/2031] validate in Windows using VirtualQuery --- lib/std/c/darwin.zig | 6 ++++ lib/std/c/dragonfly.zig | 6 ++++ lib/std/c/freebsd.zig | 6 ++++ lib/std/c/netbsd.zig | 6 ++++ lib/std/c/openbsd.zig | 6 ++++ lib/std/c/solaris.zig | 6 ++++ lib/std/debug.zig | 56 +++++++++++++++++++++++---------- lib/std/os/linux.zig | 4 +-- lib/std/os/windows.zig | 32 +++++++++++++++++++ lib/std/os/windows/kernel32.zig | 2 ++ 10 files changed, 112 insertions(+), 18 deletions(-) diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index f4ca9cd6dd..091f77e937 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -636,6 +636,12 @@ pub const MAP = struct { pub const FAILED = @intToPtr(*anyopaque, maxInt(usize)); }; +pub const MSF = struct { + pub const ASYNC = 1; + pub const INVALIDATE = 2; + pub const SYNC = 4; +}; + pub const SA = struct { /// take signal on signal stack pub const ONSTACK = 0x0001; diff --git a/lib/std/c/dragonfly.zig b/lib/std/c/dragonfly.zig index d43c8dd239..75e84f637d 100644 --- a/lib/std/c/dragonfly.zig +++ b/lib/std/c/dragonfly.zig @@ -185,6 +185,12 @@ pub const MAP = struct { pub const SIZEALIGN = 262144; }; +pub const MSF = struct { + pub const ASYNC = 1; + pub const INVALIDATE = 2; + pub const SYNC = 4; +}; + pub const W = struct { pub const NOHANG = 0x0001; pub const UNTRACED = 0x0002; diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index a19ecd3bac..7b74c237ce 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -410,6 +410,12 @@ pub const MAP = struct { pub const @"32BIT" = 0x00080000; }; +pub const MSF = struct { + pub const ASYNC = 1; + pub const INVALIDATE = 2; + pub const SYNC = 4; +}; + pub const W = struct { pub const NOHANG = 1; pub const UNTRACED = 2; diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig index 4fbc7594b0..6507134d13 100644 --- a/lib/std/c/netbsd.zig +++ b/lib/std/c/netbsd.zig @@ -575,6 +575,12 @@ pub const MAP = struct { pub const STACK = 0x2000; }; +pub const MSF = struct { + pub const ASYNC = 1; + pub const INVALIDATE = 2; + pub const SYNC = 4; +}; + pub const W = struct { pub const NOHANG = 0x00000001; pub const UNTRACED = 0x00000002; diff --git a/lib/std/c/openbsd.zig b/lib/std/c/openbsd.zig index 6ba11e8e5a..17187cf203 100644 --- a/lib/std/c/openbsd.zig +++ b/lib/std/c/openbsd.zig @@ -363,6 +363,12 @@ pub const MAP = struct { pub const CONCEAL = 0x8000; }; +pub const MSF = struct { + pub const ASYNC = 1; + pub const INVALIDATE = 2; + pub const SYNC = 4; +}; + pub const W = struct { pub const NOHANG = 1; pub const UNTRACED = 2; diff --git a/lib/std/c/solaris.zig b/lib/std/c/solaris.zig index 5abb34d2ff..5f44dc5350 100644 --- a/lib/std/c/solaris.zig +++ b/lib/std/c/solaris.zig @@ -534,6 +534,12 @@ pub const MAP = struct { pub const INITDATA = 0x0800; }; +pub const MSF = struct { + pub const ASYNC = 1; + pub const INVALIDATE = 2; + pub const SYNC = 4; +}; + pub const MADV = struct { /// no further special treatment pub const NORMAL = 0; diff --git a/lib/std/debug.zig b/lib/std/debug.zig index be47985041..c1571a70c6 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -424,24 +424,48 @@ pub const StackIterator = struct { return address; } - fn isValidMemory(address: u64) bool { - if (native_os != .windows) { - var res = true; - const length = 2 * mem.page_size; - const aligned_address = address & ~@intCast(u64, (mem.page_size - 1)); - const aligned_memory = @intToPtr([*]align(mem.page_size) u8, aligned_address)[0..length]; + fn isValidMemory(address: usize) bool { + const aligned_address = address & ~@intCast(usize, (mem.page_size - 1)); - os.msync(aligned_memory, os.MSF.ASYNC) catch |err| { - switch (err) { - os.MSyncError.UnmappedMemory => { - res = false; - }, - else => unreachable, - } - }; - return res; + // If the address does not span 2 pages, query only the first one + const length: usize = if (aligned_address == address) mem.page_size else 2 * mem.page_size; + + const aligned_memory = @intToPtr([*]align(mem.page_size) u8, aligned_address)[0..length]; + + if (native_os != .windows) { + if (native_os != .wasi) { + os.msync(aligned_memory, os.MSF.ASYNC) catch |err| { + switch (err) { + os.MSyncError.UnmappedMemory => { + return false; + }, + else => unreachable, + } + }; + } + + return true; } else { - // TODO: Using windows memory API check if a page is mapped + const w = os.windows; + var memory_info: w.MEMORY_BASIC_INFORMATION = undefined; + //const memory_info_ptr = @ptrCast(w.PMEMORY_BASIC_INFORMATION, buffer); + + // The only error this function can throw is ERROR_INVALID_PARAMETER. + // supply an address that invalid i'll be thrown. + const rc = w.VirtualQuery(aligned_memory.ptr, &memory_info, aligned_memory.len) catch { + return false; + }; + + // Result code has to be bigger than zero (number of bytes written) + if (rc == 0) { + return false; + } + + // Free pages cannot be read, they are unmapped + if (memory_info.State == w.MEM_FREE) { + return false; + } + return true; } } diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 608db08a9c..b6c8285b52 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -412,8 +412,8 @@ pub const MSF = struct { pub const SYNC = 4; }; -pub fn msync(address: [*]const u8, length: usize, flags: u32) usize { - return syscall3(.msync, @ptrToInt(address), length, flags); +pub fn msync(address: [*]const u8, length: usize, flags: i32) usize { + return syscall3(.msync, @ptrToInt(address), length, @bitCast(u32, flags)); } pub fn munmap(address: [*]const u8, length: usize) usize { diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index c832149577..31745cfe82 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -1495,6 +1495,19 @@ pub fn VirtualFree(lpAddress: ?LPVOID, dwSize: usize, dwFreeType: DWORD) void { assert(kernel32.VirtualFree(lpAddress, dwSize, dwFreeType) != 0); } +pub const VirtualQuerryError = error{Unexpected}; + +pub fn VirtualQuery(lpAddress: ?LPVOID, lpBuffer: PMEMORY_BASIC_INFORMATION, dwLength: SIZE_T) VirtualQuerryError!SIZE_T { + const rc = kernel32.VirtualQuery(lpAddress, lpBuffer, dwLength); + if (rc == 0) { + switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + } + } + + return rc; +} + pub const SetConsoleTextAttributeError = error{Unexpected}; pub fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) SetConsoleTextAttributeError!void { @@ -2586,6 +2599,11 @@ pub const CREATE_EVENT_MANUAL_RESET = 0x00000001; pub const EVENT_ALL_ACCESS = 0x1F0003; pub const EVENT_MODIFY_STATE = 0x0002; +// MEMORY_BASIC_INFORMATION.Type flags for VirtualQuery +pub const MEM_IMAGE = 0x1000000; +pub const MEM_MAPPED = 0x40000; +pub const MEM_PRIVATE = 0x20000; + pub const PROCESS_INFORMATION = extern struct { hProcess: HANDLE, hThread: HANDLE, @@ -2661,6 +2679,7 @@ pub const HEAP_NO_SERIALIZE = 0x00000001; // AllocationType values pub const MEM_COMMIT = 0x1000; pub const MEM_RESERVE = 0x2000; +pub const MEM_FREE = 0x10000; pub const MEM_RESET = 0x80000; pub const MEM_RESET_UNDO = 0x1000000; pub const MEM_LARGE_PAGES = 0x20000000; @@ -2960,6 +2979,19 @@ pub const COINIT = enum(c_int) { COINIT_SPEED_OVER_MEMORY = 8, }; +pub const MEMORY_BASIC_INFORMATION = extern struct { + BaseAddress: PVOID, + AllocationBase: PVOID, + AllocationProtect: DWORD, + PartitionId: WORD, + RegionSize: SIZE_T, + State: DWORD, + Protect: DWORD, + Type: DWORD, +}; + +pub const PMEMORY_BASIC_INFORMATION = *MEMORY_BASIC_INFORMATION; + /// > The maximum path of 32,767 characters is approximate, because the "\\?\" /// > prefix may be expanded to a longer string by the system at run time, and /// > this expansion applies to the total length. diff --git a/lib/std/os/windows/kernel32.zig b/lib/std/os/windows/kernel32.zig index b602921648..e947d1e505 100644 --- a/lib/std/os/windows/kernel32.zig +++ b/lib/std/os/windows/kernel32.zig @@ -56,6 +56,7 @@ const LPOVERLAPPED_COMPLETION_ROUTINE = windows.LPOVERLAPPED_COMPLETION_ROUTINE; const UCHAR = windows.UCHAR; const FARPROC = windows.FARPROC; const INIT_ONCE_FN = windows.INIT_ONCE_FN; +const PMEMORY_BASIC_INFORMATION = windows.PMEMORY_BASIC_INFORMATION; pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(WINAPI) ?*anyopaque; pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(WINAPI) c_ulong; @@ -245,6 +246,7 @@ pub extern "kernel32" fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*co pub extern "kernel32" fn VirtualAlloc(lpAddress: ?LPVOID, dwSize: SIZE_T, flAllocationType: DWORD, flProtect: DWORD) callconv(WINAPI) ?LPVOID; pub extern "kernel32" fn VirtualFree(lpAddress: ?LPVOID, dwSize: SIZE_T, dwFreeType: DWORD) callconv(WINAPI) BOOL; +pub extern "kernel32" fn VirtualQuery(lpAddress: ?LPVOID, lpBuffer: PMEMORY_BASIC_INFORMATION, dwLength: SIZE_T) callconv(WINAPI) SIZE_T; pub extern "kernel32" fn LocalFree(hMem: HLOCAL) callconv(WINAPI) ?HLOCAL; From a5a7f0ff0069d68ad60391dadce52321dd316138 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 11 Feb 2022 15:57:23 -0700 Subject: [PATCH 0132/2031] CI: upgrade x86_64-macos tarball to llvm 13.0.1 --- ci/azure/macos_script | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/azure/macos_script b/ci/azure/macos_script index da0e279500..80fcf430cc 100755 --- a/ci/azure/macos_script +++ b/ci/azure/macos_script @@ -9,7 +9,7 @@ ZIGDIR="$(pwd)" ARCH="x86_64" TARGET="$ARCH-macos-gnu" MCPU="baseline" -CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.9.0-dev.1249+210ef5af8" +CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.9.1-dev.90+d2398cf00" PREFIX="$HOME/$CACHE_BASENAME" JOBS="-j2" From 2262640e8bf4cbcc4e6a46405d7a5a20562b8e47 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Fri, 11 Feb 2022 19:06:46 +0100 Subject: [PATCH 0133/2031] stage2 ARM: lower const slices Follow-up to e1a535360fb9ed08fc48018571b9702ab12a5876 for ARM This also fixes some stack offset calculation bugs --- src/arch/arm/CodeGen.zig | 207 ++++++++++++++++++++++----------------- test/behavior/basic.zig | 3 - test/behavior/slice.zig | 17 ---- 3 files changed, 119 insertions(+), 108 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 2c60027d97..e211f9d7bc 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1170,10 +1170,10 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { .dead, .unreach => unreachable, .register => unreachable, // a slice doesn't fit in one register .stack_argument_offset => |off| { - break :result MCValue{ .stack_argument_offset = off }; + break :result MCValue{ .stack_argument_offset = off + 4 }; }, .stack_offset => |off| { - break :result MCValue{ .stack_offset = off }; + break :result MCValue{ .stack_offset = off + 4 }; }, .memory => |addr| { break :result MCValue{ .memory = addr }; @@ -1192,10 +1192,10 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { .dead, .unreach => unreachable, .register => unreachable, // a slice doesn't fit in one register .stack_argument_offset => |off| { - break :result MCValue{ .stack_argument_offset = off + 4 }; + break :result MCValue{ .stack_argument_offset = off }; }, .stack_offset => |off| { - break :result MCValue{ .stack_offset = off + 4 }; + break :result MCValue{ .stack_offset = off }; }, .memory => |addr| { break :result MCValue{ .memory = addr + 4 }; @@ -1260,7 +1260,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { defer if (index_is_register) self.register_manager.unfreezeRegs(&.{index_mcv.register}); const base_mcv: MCValue = switch (slice_mcv) { - .stack_offset => .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, slice_mcv) }, + .stack_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_offset = off + 4 }) }, else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}), }; self.register_manager.freezeRegs(&.{base_mcv.register}); @@ -1471,36 +1471,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); - } else if (elem_size == 8) { - // TODO generalize this: maybe add a - // genArmMemcpy function which manually copies - // data if the size is below a certain - // threshold and calls "memcpy" if the size is - // larger - - const usize_ty = Type.initTag(.usize); - const tmp_regs = try self.register_manager.allocRegs(2, .{ null, null }); - self.register_manager.freezeRegs(&tmp_regs); - defer self.register_manager.unfreezeRegs(&tmp_regs); - - _ = try self.addInst(.{ - .tag = .ldr, - .data = .{ .rr_offset = .{ - .rt = tmp_regs[0], - .rn = reg, - .offset = .{ .offset = Instruction.Offset.none }, - } }, - }); - _ = try self.addInst(.{ - .tag = .ldr, - .data = .{ .rr_offset = .{ - .rt = tmp_regs[1], - .rn = reg, - .offset = .{ .offset = Instruction.Offset.imm(4) }, - } }, - }); - try self.genSetStack(usize_ty, off, MCValue{ .register = tmp_regs[0] }); - try self.genSetStack(usize_ty, off + 4, MCValue{ .register = tmp_regs[1] }); } else { // TODO optimize the register allocation const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }); @@ -1677,7 +1647,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde .ptr_stack_offset => |off| { break :result MCValue{ .ptr_stack_offset = off + struct_size - struct_field_offset - struct_field_size }; }, - .stack_argument_offset => { + else => { const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = struct_field_offset, }); @@ -1699,7 +1669,6 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde ); break :result MCValue{ .register = dst_reg }; }, - else => return self.fail("TODO implement codegen struct_field_ptr for {}", .{mcv}), } }; } @@ -3200,9 +3169,9 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { } fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { + const abi_size = @intCast(u32, ty.abiSize(self.target.*)); switch (mcv) { .dead => unreachable, - .ptr_embedded_in_code => unreachable, .unreach, .none => return, // Nothing to do. .undef => { if (!self.wantSafety()) @@ -3215,23 +3184,16 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro else => return self.fail("TODO implement memset", .{}), } }, - .ptr_stack_offset => { - const reg = try self.copyToTmpRegister(ty, mcv); - return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); - }, .compare_flags_unsigned, .compare_flags_signed, .immediate, + .ptr_stack_offset, + .ptr_embedded_in_code, => { const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); }, - .embedded_in_code => |code_offset| { - _ = code_offset; - return self.fail("TODO implement set stack variable from embedded_in_code", .{}); - }, .register => |reg| { - const abi_size = @intCast(u32, ty.abiSize(self.target.*)); const adj_off = stack_offset + abi_size; switch (abi_size) { @@ -3279,24 +3241,23 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro } }, .memory, + .embedded_in_code, .stack_argument_offset, + .stack_offset, => { - if (ty.abiSize(self.target.*) <= 4) { - const reg = try self.copyToTmpRegister(ty, mcv); - return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); - } else { - return self.fail("TODO implement memcpy", .{}); + switch (mcv) { + .stack_offset => |off| { + if (stack_offset == off) + return; // Copy stack variable to itself; nothing to do. + }, + else => {}, } - }, - .stack_offset => |off| { - if (stack_offset == off) - return; // Copy stack variable to itself; nothing to do. - if (ty.abiSize(self.target.*) <= 4) { + if (abi_size <= 4) { const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); } else { - // TODO optimize the register allocation + // TODO call extern memcpy const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }); const src_reg = regs[0]; const dst_reg = regs[1]; @@ -3304,22 +3265,31 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro const count_reg = regs[3]; const tmp_reg = regs[4]; - // sub src_reg, fp, #off - const adj_src_offset = off + @intCast(u32, ty.abiSize(self.target.*)); - const src_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_src_offset)) |x| x else { - return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); - }; - _ = try self.addInst(.{ - .tag = .sub, - .data = .{ .rr_op = .{ - .rd = src_reg, - .rn = .fp, - .op = src_offset_op, - } }, - }); + switch (mcv) { + .stack_offset => |off| { + // sub src_reg, fp, #off + const adj_src_offset = off + @intCast(u32, abi_size); + const src_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_src_offset)) |x| x else { + return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); + }; + _ = try self.addInst(.{ + .tag = .sub, + .data = .{ .rr_op = .{ + .rd = src_reg, + .rn = .fp, + .op = src_offset_op, + } }, + }); + }, + .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = @intCast(u32, addr) }), + .embedded_in_code, + .stack_argument_offset, + => return self.fail("TODO genSetStack with src={}", .{mcv}), + else => unreachable, + } // sub dst_reg, fp, #stack_offset - const adj_dst_offset = stack_offset + @intCast(u32, ty.abiSize(self.target.*)); + const adj_dst_offset = stack_offset + abi_size; const dst_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_dst_offset)) |x| x else { return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); }; @@ -3332,9 +3302,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro } }, }); - // mov len, #elem_size - const elem_size = @intCast(u32, ty.abiSize(self.target.*)); - const len_op: Instruction.Operand = if (Instruction.Operand.fromU32(elem_size)) |x| x else { + // mov len, #abi_size + const len_op: Instruction.Operand = if (Instruction.Operand.fromU32(abi_size)) |x| x else { return self.fail("TODO load: set reg to elem_size with all possible sizes", .{}); }; _ = try self.addInst(.{ @@ -3619,6 +3588,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void } fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { + const abi_size = @intCast(u32, ty.abiSize(self.target.*)); switch (mcv) { .dead => unreachable, .none, .unreach => return, @@ -3634,7 +3604,6 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I } }, .register => |reg| { - const abi_size = @intCast(u32, ty.abiSize(self.target.*)); const adj_off = stack_offset - abi_size; switch (abi_size) { @@ -3675,28 +3644,86 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), } }, - .immediate, - .compare_flags_signed, - .compare_flags_unsigned, .stack_offset, .memory, .stack_argument_offset, .embedded_in_code, => { - if (ty.abiSize(self.target.*) <= 4) { + if (abi_size <= 4) { const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg }); } else { - return self.fail("TODO implement memcpy", .{}); + // TODO call extern memcpy + const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }); + const src_reg = regs[0]; + const dst_reg = regs[1]; + const len_reg = regs[2]; + const count_reg = regs[3]; + const tmp_reg = regs[4]; + + switch (mcv) { + .stack_offset => |off| { + // sub src_reg, fp, #off + const adj_src_offset = off + abi_size; + const src_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_src_offset)) |x| x else { + return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); + }; + _ = try self.addInst(.{ + .tag = .sub, + .data = .{ .rr_op = .{ + .rd = src_reg, + .rn = .fp, + .op = src_offset_op, + } }, + }); + }, + .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = @intCast(u32, addr) }), + .stack_argument_offset, + .embedded_in_code, + => return self.fail("TODO genSetStackArgument src={}", .{mcv}), + else => unreachable, + } + + // add dst_reg, sp, #stack_offset + const adj_dst_offset = stack_offset - abi_size; + const dst_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_dst_offset)) |x| x else { + return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); + }; + _ = try self.addInst(.{ + .tag = .add, + .data = .{ .rr_op = .{ + .rd = dst_reg, + .rn = .sp, + .op = dst_offset_op, + } }, + }); + + // mov len, #abi_size + const len_op: Instruction.Operand = if (Instruction.Operand.fromU32(abi_size)) |x| x else { + return self.fail("TODO load: set reg to elem_size with all possible sizes", .{}); + }; + _ = try self.addInst(.{ + .tag = .mov, + .data = .{ .rr_op = .{ + .rd = len_reg, + .rn = .r0, + .op = len_op, + } }, + }); + + // memcpy(src, dst, len) + try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); } }, - .ptr_stack_offset => { + .compare_flags_unsigned, + .compare_flags_signed, + .immediate, + .ptr_stack_offset, + .ptr_embedded_in_code, + => { const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg }); }, - .ptr_embedded_in_code => { - return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); - }, } } @@ -4114,9 +4141,13 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { var stack_offset: u32 = 0; for (param_types) |ty, i| { - stack_offset = std.mem.alignForwardGeneric(u32, stack_offset, ty.abiAlignment(self.target.*)); - result.args[i] = .{ .stack_argument_offset = stack_offset }; - stack_offset += @intCast(u32, ty.abiSize(self.target.*)); + if (ty.abiSize(self.target.*) > 0) { + stack_offset = std.mem.alignForwardGeneric(u32, stack_offset, ty.abiAlignment(self.target.*)); + result.args[i] = .{ .stack_argument_offset = stack_offset }; + stack_offset += @intCast(u32, ty.abiSize(self.target.*)); + } else { + result.args[i] = .{ .none = {} }; + } } result.stack_byte_count = stack_offset; diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 5d7bb3b9a7..18a24f9b3a 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -216,7 +216,6 @@ test "compile time global reinterpret" { } test "cast undefined" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const array: [100]u8 = undefined; @@ -678,7 +677,6 @@ test "string concatenation" { } test "thread local variable" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO @@ -704,7 +702,6 @@ fn maybe(x: bool) anyerror!?u32 { } test "pointer to thread local array" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 327b8f4f76..badaf7ef03 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -48,7 +48,6 @@ test "slicing" { test "const slice" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; comptime { const a = "1234567890"; @@ -61,7 +60,6 @@ test "const slice" { test "comptime slice of undefined pointer of length 0" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const slice1 = @as([*]i32, undefined)[0..0]; try expect(slice1.len == 0); @@ -83,7 +81,6 @@ fn assertLenIsZero(msg: []const u8) !void { test "access len index of sentinel-terminated slice" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -99,7 +96,6 @@ test "access len index of sentinel-terminated slice" { test "comptime slice of slice preserves comptime var" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; comptime { var buff: [10]u8 = undefined; @@ -110,7 +106,6 @@ test "comptime slice of slice preserves comptime var" { test "slice of type" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; comptime { var types_array = [_]type{ i32, f64, type }; @@ -151,7 +146,6 @@ fn memFree(comptime T: type, memory: []T) void { test "slice of hardcoded address to pointer" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -168,7 +162,6 @@ test "slice of hardcoded address to pointer" { test "comptime slice of pointer preserves comptime var" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; comptime { var buff: [10]u8 = undefined; @@ -180,7 +173,6 @@ test "comptime slice of pointer preserves comptime var" { test "comptime pointer cast array and then slice" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; @@ -239,7 +231,6 @@ test "slice string literal has correct type" { test "result location zero sized array inside struct field implicit cast to slice" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const E = struct { entries: []u32, @@ -287,8 +278,6 @@ test "C pointer slice access" { } test "comptime slices are disambiguated" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - try expect(sliceSum(&[_]u8{ 1, 2 }) == 3); try expect(sliceSum(&[_]u8{ 3, 4 }) == 7); } @@ -343,7 +332,6 @@ test "obtaining a null terminated slice" { test "empty array to slice" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -380,7 +368,6 @@ test "@ptrCast slice to pointer" { test "slice syntax resulting in pointer-to-array" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -529,7 +516,6 @@ test "slice syntax resulting in pointer-to-array" { test "type coercion of pointer to anon struct literal to pointer to slice" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { const U = union { @@ -563,7 +549,6 @@ test "type coercion of pointer to anon struct literal to pointer to slice" { test "array concat of slices gives slice" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; comptime { var a: []const u8 = "aoeu"; @@ -575,7 +560,6 @@ test "array concat of slices gives slice" { test "slice bounds in comptime concatenation" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const bs = comptime blk: { const b = "........1........"; @@ -592,7 +576,6 @@ test "slice bounds in comptime concatenation" { test "slice sentinel access at comptime" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; { const str0 = &[_:0]u8{ '1', '2', '3' }; From 3fdbc3bba859596d70251d05ccafd2ad1bfc962d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 12 Feb 2022 01:06:20 -0700 Subject: [PATCH 0134/2031] CI: upgrade x86_64-macos tarball to llvm 13.0.1 --- ci/azure/macos_arm64_script | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/azure/macos_arm64_script b/ci/azure/macos_arm64_script index b8d1437e78..835ee59c90 100755 --- a/ci/azure/macos_arm64_script +++ b/ci/azure/macos_arm64_script @@ -10,7 +10,7 @@ ZIGDIR="$(pwd)" HOST_ARCH="x86_64" HOST_TARGET="$HOST_ARCH-macos-gnu" HOST_MCPU="baseline" -HOST_CACHE_BASENAME="zig+llvm+lld+clang-$HOST_TARGET-0.9.0-dev.1249+210ef5af8" +HOST_CACHE_BASENAME="zig+llvm+lld+clang-$HOST_TARGET-0.9.1-dev.90+d2398cf00" HOST_PREFIX="$HOME/$HOST_CACHE_BASENAME" ARCH="aarch64" From 1072d8a0658d76415124ef6aac6842283316f243 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 12 Feb 2022 11:01:10 +0100 Subject: [PATCH 0135/2031] CI: upgrade both host and target tarballs to llvm 13.0.1 x86_64-macos --- ci/azure/macos_arm64_script | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/azure/macos_arm64_script b/ci/azure/macos_arm64_script index 835ee59c90..a37faba001 100755 --- a/ci/azure/macos_arm64_script +++ b/ci/azure/macos_arm64_script @@ -16,7 +16,7 @@ HOST_PREFIX="$HOME/$HOST_CACHE_BASENAME" ARCH="aarch64" TARGET="$ARCH-macos-gnu" MCPU="apple_a14" -CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.9.0-dev.1249+210ef5af8" +CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.9.1-dev.90+d2398cf00" PREFIX="$HOME/$CACHE_BASENAME" JOBS="-j2" From beb275b371cd50ee1d57528d2792f2f267e6013d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 12 Feb 2022 11:17:41 +0100 Subject: [PATCH 0136/2031] Revert "CI: upgrade both host and target tarballs to llvm 13.0.1 x86_64-macos" This reverts commit 1072d8a0658d76415124ef6aac6842283316f243. --- ci/azure/macos_arm64_script | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/azure/macos_arm64_script b/ci/azure/macos_arm64_script index a37faba001..835ee59c90 100755 --- a/ci/azure/macos_arm64_script +++ b/ci/azure/macos_arm64_script @@ -16,7 +16,7 @@ HOST_PREFIX="$HOME/$HOST_CACHE_BASENAME" ARCH="aarch64" TARGET="$ARCH-macos-gnu" MCPU="apple_a14" -CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.9.1-dev.90+d2398cf00" +CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.9.0-dev.1249+210ef5af8" PREFIX="$HOME/$CACHE_BASENAME" JOBS="-j2" From f293fbbeaf6c48e6ce1410743181f89f359eb697 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 10 Feb 2022 15:42:57 -0700 Subject: [PATCH 0137/2031] stage2: LLVM backend: adjust replaceAllUsesWith usage replaceAllUsesWith requires the type to be unchanged. So we bitcast the new global to the old type and use that as the thing to replace old uses. Fixes an LLVM assertion found while troubleshooting #10837. --- src/codegen/llvm.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index d85a16d16f..425808efb1 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -661,7 +661,11 @@ pub const DeclGen = struct { new_global.setUnnamedAddr(global.getUnnamedAddress()); new_global.setAlignment(global.getAlignment()); new_global.setInitializer(llvm_init); - global.replaceAllUsesWith(new_global); + // replaceAllUsesWith requires the type to be unchanged. So we bitcast + // the new global to the old type and use that as the thing to replace + // old uses. + const new_global_ptr = new_global.constBitCast(global.typeOf()); + global.replaceAllUsesWith(new_global_ptr); dg.object.decl_map.putAssumeCapacity(decl, new_global); new_global.takeName(global); global.deleteGlobal(); From 166db1a3ed7eca9b04b0626eaea8de0634ab9667 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 10 Feb 2022 17:36:31 -0700 Subject: [PATCH 0138/2031] stage1: fix f80 size and alignment on x86 and arm * F80Repr extern struct needs no explicit padding; let's match the target padding. * stage2: fix lowering of f80 constants. * stage1: decide ABI size and alignment of f80 based on alignment of u64. x86 has alignof u64 equal to 4 but arm has it as 8. * stage2: fix Value.floatReadFromMemory to use F80Repr --- lib/std/math.zig | 7 ++----- src/codegen/llvm.zig | 34 ++++++++++++++++++++++------------ src/stage1/codegen.cpp | 22 +++++++++++++++++----- src/value.zig | 30 ++++++++++++++++++++++++++---- 4 files changed, 67 insertions(+), 26 deletions(-) diff --git a/lib/std/math.zig b/lib/std/math.zig index 6802d420fd..8398842e28 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -36,7 +36,6 @@ pub const sqrt2 = 1.414213562373095048801688724209698079; /// 1/sqrt(2) pub const sqrt1_2 = 0.707106781186547524400844362104849039; -// From a small c++ [program using boost float128](https://github.com/winksaville/cpp_boost_float128) pub const f128_true_min = @bitCast(f128, @as(u128, 0x00000000000000000000000000000001)); pub const f128_min = @bitCast(f128, @as(u128, 0x00010000000000000000000000000000)); pub const f128_max = @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF)); @@ -44,12 +43,10 @@ pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F000000000000000000000000 pub const f128_toint = 1.0 / f128_epsilon; pub const F80Repr = if (@import("builtin").cpu.arch.endian() == .Little) extern struct { - fraction: u64, + fraction: u64 align(@alignOf(f80)), exp: u16, - _pad: u32 = undefined, } else extern struct { - exp: u16, - _pad: u32 = undefined, // TODO verify compatibility with hardware + exp: u16 align(@alignOf(f80)), fraction: u64, }; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 425808efb1..54468162ad 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1257,19 +1257,29 @@ pub const DeclGen = struct { }, .Float => { const llvm_ty = try dg.llvmType(tv.ty); - if (tv.ty.floatBits(dg.module.getTarget()) <= 64) { - return llvm_ty.constReal(tv.val.toFloat(f64)); + switch (tv.ty.floatBits(dg.module.getTarget())) { + 16, 32, 64 => return llvm_ty.constReal(tv.val.toFloat(f64)), + 80 => { + const float = tv.val.toFloat(f80); + const repr = @ptrCast(*const std.math.F80Repr, &float); + const llvm_i80 = dg.context.intType(80); + var x = llvm_i80.constInt(repr.exp, .False); + x = x.constShl(llvm_i80.constInt(64, .False)); + x = x.constOr(llvm_i80.constInt(repr.fraction, .False)); + return x.constBitCast(llvm_ty); + }, + 128 => { + var buf: [2]u64 = @bitCast([2]u64, tv.val.toFloat(f128)); + // LLVM seems to require that the lower half of the f128 be placed first + // in the buffer. + if (native_endian == .Big) { + std.mem.swap(u64, &buf[0], &buf[1]); + } + const int = dg.context.intType(128).constIntOfArbitraryPrecision(buf.len, &buf); + return int.constBitCast(llvm_ty); + }, + else => unreachable, } - - var buf: [2]u64 = @bitCast([2]u64, tv.val.toFloat(f128)); - // LLVM seems to require that the lower half of the f128 be placed first - // in the buffer. - if (native_endian == .Big) { - std.mem.swap(u64, &buf[0], &buf[1]); - } - - const int = dg.context.intType(128).constIntOfArbitraryPrecision(buf.len, &buf); - return int.constBitCast(llvm_ty); }, .Pointer => switch (tv.val.tag()) { .decl_ref_mut => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref_mut).?.data.decl), diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index c06f71e834..7b0bcbe2f5 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -9429,17 +9429,29 @@ static void define_builtin_types(CodeGen *g) { { ZigType *entry = new_type_table_entry(ZigTypeIdFloat); + unsigned u64_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMInt64Type()); + + if (u64_alignment >= 8) { + entry->size_in_bits = 128; + entry->abi_size = 16; + entry->abi_align = 16; + } else if (u64_alignment >= 4) { + entry->size_in_bits = 96; + entry->abi_size = 12; + entry->abi_align = 4; + } else { + entry->size_in_bits = 80; + entry->abi_size = 10; + entry->abi_align = 2; + } if (target_has_f80(g->zig_target)) { entry->llvm_type = LLVMX86FP80Type(); } else { - // We use i128 here instead of x86_fp80 because on targets such as arm, + // We use an int here instead of x86_fp80 because on targets such as arm, // LLVM will give "ERROR: Cannot select" for any instructions involving // the x86_fp80 type. - entry->llvm_type = get_int_type(g, false, 128)->llvm_type; + entry->llvm_type = get_int_type(g, false, entry->size_in_bits)->llvm_type; } - entry->size_in_bits = 8 * 16; - entry->abi_size = 16; // matches LLVMABISizeOfType(LLVMX86FP80Type()) - entry->abi_align = 16; // matches LLVMABIAlignmentOfType(LLVMX86FP80Type()) buf_init_from_str(&entry->name, "f80"); entry->data.floating.bit_count = 80; diff --git a/src/value.zig b/src/value.zig index 33a75e08bb..aefb0a3e20 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1122,10 +1122,32 @@ pub const Value = extern union { fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F { if (F == f80) { - // TODO: use std.math.F80Repr? - const int = std.mem.readInt(u128, buffer[0..16], target.cpu.arch.endian()); - // TODO shouldn't this be a bitcast from u80 to f80 instead of u128 to f80? - return @bitCast(F, int); + switch (target.cpu.arch.endian()) { + .Little => { + const TargetF80Repr = extern struct { + fraction: u64, + exp: u16, + }; + const target_repr = @ptrCast(*align(1) const TargetF80Repr, buffer.ptr); + const real_repr: std.math.F80Repr = .{ + .fraction = target_repr.fraction, + .exp = target_repr.exp, + }; + return @ptrCast(*const f80, &real_repr).*; + }, + .Big => { + const TargetF80Repr = extern struct { + exp: u16, + fraction: u64, + }; + const target_repr = @ptrCast(*align(1) const TargetF80Repr, buffer.ptr); + const real_repr: std.math.F80Repr = .{ + .fraction = target_repr.fraction, + .exp = target_repr.exp, + }; + return @ptrCast(*const f80, &real_repr).*; + }, + } } const Int = @Type(.{ .Int = .{ .signedness = .unsigned, From 1c23321d03a48558b02c2faf818b82811b8ab11d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 10 Feb 2022 21:47:18 -0700 Subject: [PATCH 0139/2031] stage1: fix softfloat not getting correct endianness needed to include platform.h in more places so that LITTLEENDIAN will be defined appropriately. closes #10860 --- CMakeLists.txt | 3 +++ deps/SoftFloat-3e/source/include/primitiveTypes.h | 1 + deps/SoftFloat-3e/source/include/softfloat_types.h | 1 + 3 files changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fd1960518..a6edfa04ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -791,6 +791,9 @@ add_library(opt_c_util STATIC ${OPTIMIZED_C_SOURCES}) set_target_properties(opt_c_util PROPERTIES COMPILE_FLAGS "${OPTIMIZED_C_FLAGS}" ) +target_include_directories(opt_c_util PRIVATE + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e-prebuilt" +) add_library(zigstage1 STATIC ${STAGE1_SOURCES}) set_target_properties(zigstage1 PROPERTIES diff --git a/deps/SoftFloat-3e/source/include/primitiveTypes.h b/deps/SoftFloat-3e/source/include/primitiveTypes.h index a4a6dd11c6..4407f5e7cb 100644 --- a/deps/SoftFloat-3e/source/include/primitiveTypes.h +++ b/deps/SoftFloat-3e/source/include/primitiveTypes.h @@ -37,6 +37,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef primitiveTypes_h #define primitiveTypes_h 1 +#include "platform.h" #include #ifdef SOFTFLOAT_FAST_INT64 diff --git a/deps/SoftFloat-3e/source/include/softfloat_types.h b/deps/SoftFloat-3e/source/include/softfloat_types.h index bc30e31440..27507741af 100644 --- a/deps/SoftFloat-3e/source/include/softfloat_types.h +++ b/deps/SoftFloat-3e/source/include/softfloat_types.h @@ -37,6 +37,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef softfloat_types_h #define softfloat_types_h 1 +#include "platform.h" #include /*---------------------------------------------------------------------------- From a024aff9324e827d6595e44f922d87f8ed2dbd0d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 10 Feb 2022 22:06:43 -0700 Subject: [PATCH 0140/2031] make f80 less hacky; lower as u80 on non-x86 Get rid of `std.math.F80Repr`. Instead of trying to match the memory layout of f80, we treat it as a value, same as the other floating point types. The functions `make_f80` and `break_f80` are introduced to compose an f80 value out of its parts, and the inverse operation. stage2 LLVM backend: fix pointer to zero length array tripping LLVM assertion. It now checks for when the element type is a zero-bit type and lowers such thing the same way that pointers to other zero-bit types are lowered. Both stage1 and stage2 LLVM backends are adjusted so that f80 is lowered as x86_fp80 on x86_64 and i386 architectures, and identical to a u80 on others. LLVM constants are lowered in a less hacky way now that #10860 is fixed, by using the expression `(exp << 64) | fraction` using llvm constants. Sema is improved to handle c_longdouble by recursively handling it correctly for whatever the float bit width is. In both stage1 and stage2. --- lib/std/math.zig | 40 ++++++++----- lib/std/special/compiler_rt/addXf3.zig | 18 +++--- lib/std/special/compiler_rt/compareXf2.zig | 4 +- lib/std/special/compiler_rt/extend_f80.zig | 6 +- lib/std/special/compiler_rt/trunc_f80.zig | 6 +- src/codegen/llvm.zig | 41 ++++++++----- src/stage1/codegen.cpp | 68 +++++++++++----------- src/type.zig | 55 +++++++++++++++-- src/value.zig | 67 ++++++++++++--------- test/behavior/floatop.zig | 5 +- 10 files changed, 200 insertions(+), 110 deletions(-) diff --git a/lib/std/math.zig b/lib/std/math.zig index 8398842e28..4b8bcf2287 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -42,19 +42,11 @@ pub const f128_max = @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000)); pub const f128_toint = 1.0 / f128_epsilon; -pub const F80Repr = if (@import("builtin").cpu.arch.endian() == .Little) extern struct { - fraction: u64 align(@alignOf(f80)), - exp: u16, -} else extern struct { - exp: u16 align(@alignOf(f80)), - fraction: u64, -}; - // float.h details -pub const f80_true_min = @ptrCast(*const f80, &F80Repr{ .fraction = 1, .exp = 0 }).*; -pub const f80_min = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 1 }).*; -pub const f80_max = @ptrCast(*const f80, &F80Repr{ .fraction = 0xFFFFFFFFFFFFFFFF, .exp = 0x7FFE }).*; -pub const f80_epsilon = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 0x3FC0 }).*; +pub const f80_true_min = make_f80(.{ .fraction = 1, .exp = 0 }); +pub const f80_min = make_f80(.{ .fraction = 0x8000000000000000, .exp = 1 }); +pub const f80_max = make_f80(.{ .fraction = 0xFFFFFFFFFFFFFFFF, .exp = 0x7FFE }); +pub const f80_epsilon = make_f80(.{ .fraction = 0x8000000000000000, .exp = 0x3FC0 }); pub const f80_toint = 1.0 / f80_epsilon; pub const f64_true_min = 4.94065645841246544177e-324; @@ -104,9 +96,9 @@ pub const qnan_f64 = @bitCast(f64, qnan_u64); pub const inf_u64 = @as(u64, 0x7FF << 52); pub const inf_f64 = @bitCast(f64, inf_u64); -pub const inf_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0x8000000000000000, .exp = 0x7fff }).*; -pub const nan_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0xA000000000000000, .exp = 0x7fff }).*; -pub const qnan_f80 = @ptrCast(*const f80, &F80Repr{ .fraction = 0xC000000000000000, .exp = 0x7fff }).*; +pub const inf_f80 = make_f80(F80{ .fraction = 0x8000000000000000, .exp = 0x7fff }); +pub const nan_f80 = make_f80(F80{ .fraction = 0xA000000000000000, .exp = 0x7fff }); +pub const qnan_f80 = make_f80(F80{ .fraction = 0xC000000000000000, .exp = 0x7fff }); pub const nan_u128 = @as(u128, 0x7fff0000000000000000000000000001); pub const nan_f128 = @bitCast(f128, nan_u128); @@ -1501,3 +1493,21 @@ test "boolMask" { pub fn comptimeMod(num: anytype, denom: comptime_int) IntFittingRange(0, denom - 1) { return @intCast(IntFittingRange(0, denom - 1), @mod(num, denom)); } + +pub const F80 = struct { + fraction: u64, + exp: u16, +}; + +pub fn make_f80(repr: F80) f80 { + const int = (@as(u80, repr.exp) << 64) | repr.fraction; + return @bitCast(f80, int); +} + +pub fn break_f80(x: f80) F80 { + const int = @bitCast(u80, x); + return .{ + .fraction = @truncate(u64, int), + .exp = @truncate(u16, int >> 64), + }; +} diff --git a/lib/std/special/compiler_rt/addXf3.zig b/lib/std/special/compiler_rt/addXf3.zig index 1339cc340d..13758afce7 100644 --- a/lib/std/special/compiler_rt/addXf3.zig +++ b/lib/std/special/compiler_rt/addXf3.zig @@ -232,8 +232,8 @@ fn normalize_f80(exp: *i32, significand: *u80) void { } pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { - var a_rep align(16) = @ptrCast(*const std.math.F80Repr, &a).*; - var b_rep align(16) = @ptrCast(*const std.math.F80Repr, &b).*; + var a_rep = std.math.break_f80(a); + var b_rep = std.math.break_f80(b); var a_exp: i32 = a_rep.exp & 0x7FFF; var b_exp: i32 = b_rep.exp & 0x7FFF; @@ -257,7 +257,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { std.debug.assert(a_rep.fraction & significand_mask != 0); // NaN + anything = qNaN a_rep.fraction |= qnan_bit; - return @ptrCast(*const f80, &a_rep).*; + return std.math.make_f80(a_rep); } } if (b_exp == max_exp) { @@ -268,7 +268,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { std.debug.assert(b_rep.fraction & significand_mask != 0); // anything + NaN = qNaN b_rep.fraction |= qnan_bit; - return @ptrCast(*const f80, &b_rep).*; + return std.math.make_f80(b_rep); } } @@ -279,7 +279,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { if (b_zero) { // but we need to get the sign right for zero + zero a_rep.exp &= b_rep.exp; - return @ptrCast(*const f80, &a_rep).*; + return std.math.make_f80(a_rep); } else { return b; } @@ -359,7 +359,7 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { if (a_exp >= max_exp) { a_rep.exp = max_exp | result_sign; a_rep.fraction = int_bit; // integer bit is set for +/-inf - return @ptrCast(*const f80, &a_rep).*; + return std.math.make_f80(a_rep); } if (a_exp <= 0) { @@ -387,13 +387,13 @@ pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { a_rep.fraction = @truncate(u64, a_int); a_rep.exp = @truncate(u16, a_int >> significand_bits); - return @ptrCast(*const f80, &a_rep).*; + return std.math.make_f80(a_rep); } pub fn __subxf3(a: f80, b: f80) callconv(.C) f80 { - var b_rep align(16) = @ptrCast(*const std.math.F80Repr, &b).*; + var b_rep = std.math.break_f80(b); b_rep.exp ^= 0x8000; - return __addxf3(a, @ptrCast(*const f80, &b_rep).*); + return __addxf3(a, std.math.make_f80(b_rep)); } test { diff --git a/lib/std/special/compiler_rt/compareXf2.zig b/lib/std/special/compiler_rt/compareXf2.zig index 36f6f5f1c1..9640298f8f 100644 --- a/lib/std/special/compiler_rt/compareXf2.zig +++ b/lib/std/special/compiler_rt/compareXf2.zig @@ -147,8 +147,8 @@ pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 { // Comparison between f80 pub inline fn cmp_f80(comptime RT: type, a: f80, b: f80) RT { - const a_rep = @ptrCast(*const std.math.F80Repr, &a).*; - const b_rep = @ptrCast(*const std.math.F80Repr, &b).*; + const a_rep = std.math.break_f80(a); + const b_rep = std.math.break_f80(b); const sig_bits = std.math.floatMantissaBits(f80); const int_bit = 0x8000000000000000; const sign_bit = 0x8000; diff --git a/lib/std/special/compiler_rt/extend_f80.zig b/lib/std/special/compiler_rt/extend_f80.zig index 29ba8560ce..4686421db0 100644 --- a/lib/std/special/compiler_rt/extend_f80.zig +++ b/lib/std/special/compiler_rt/extend_f80.zig @@ -41,7 +41,7 @@ inline fn extendF80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(s const src_qnan = 1 << (src_sig_bits - 1); const src_nan_code = src_qnan - 1; - var dst: std.math.F80Repr align(16) = undefined; + var dst: std.math.F80 = undefined; // Break a into a sign and representation of the absolute value const a_abs = a & src_abs_mask; @@ -83,7 +83,7 @@ inline fn extendF80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(s } dst.exp |= sign; - return @ptrCast(*const f80, &dst).*; + return std.math.make_f80(dst); } pub fn __extendxftf2(a: f80) callconv(.C) f128 { @@ -99,7 +99,7 @@ pub fn __extendxftf2(a: f80) callconv(.C) f128 { const dst_min_normal = @as(u128, 1) << dst_sig_bits; // Break a into a sign and representation of the absolute value - var a_rep = @ptrCast(*const std.math.F80Repr, &a).*; + var a_rep = std.math.break_f80(a); const sign = a_rep.exp & 0x8000; a_rep.exp &= 0x7FFF; var abs_result: u128 = undefined; diff --git a/lib/std/special/compiler_rt/trunc_f80.zig b/lib/std/special/compiler_rt/trunc_f80.zig index 567d03be63..19e8d44b86 100644 --- a/lib/std/special/compiler_rt/trunc_f80.zig +++ b/lib/std/special/compiler_rt/trunc_f80.zig @@ -42,7 +42,7 @@ inline fn trunc(comptime dst_t: type, a: f80) dst_t { const dst_nan_mask = dst_qnan - 1; // Break a into a sign and representation of the absolute value - var a_rep = @ptrCast(*const std.math.F80Repr, &a).*; + var a_rep = std.math.break_f80(a); const sign = a_rep.exp & 0x8000; a_rep.exp &= 0x7FFF; a_rep.fraction &= 0x7FFFFFFFFFFFFFFF; @@ -125,7 +125,7 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 { const a_abs = a_rep & src_abs_mask; const sign: u16 = if (a_rep & src_sign_mask != 0) 0x8000 else 0; - var res: std.math.F80Repr align(16) = undefined; + var res: std.math.F80 = undefined; if (a_abs > src_inf) { // a is NaN. @@ -155,5 +155,5 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 { } res.exp |= sign; - return @ptrCast(*const f80, &res).*; + return std.math.make_f80(res); } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 54468162ad..b2adb898d3 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -824,23 +824,24 @@ pub const DeclGen = struct { fn llvmType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type { const gpa = dg.gpa; + const target = dg.module.getTarget(); switch (t.zigTypeTag()) { .Void, .NoReturn => return dg.context.voidType(), .Int => { - const info = t.intInfo(dg.module.getTarget()); + const info = t.intInfo(target); return dg.context.intType(info.bits); }, .Enum => { var buffer: Type.Payload.Bits = undefined; const int_ty = t.intTagType(&buffer); - const bit_count = int_ty.intInfo(dg.module.getTarget()).bits; + const bit_count = int_ty.intInfo(target).bits; return dg.context.intType(bit_count); }, - .Float => switch (t.floatBits(dg.module.getTarget())) { + .Float => switch (t.floatBits(target)) { 16 => return dg.context.halfType(), 32 => return dg.context.floatType(), 64 => return dg.context.doubleType(), - 80 => return dg.context.x86FP80Type(), + 80 => return if (backendSupportsF80(target)) dg.context.x86FP80Type() else dg.context.intType(80), 128 => return dg.context.fp128Type(), else => unreachable, }, @@ -859,7 +860,8 @@ pub const DeclGen = struct { const llvm_addrspace = dg.llvmAddressSpace(t.ptrAddressSpace()); const elem_ty = t.childType(); const lower_elem_ty = switch (elem_ty.zigTypeTag()) { - .Opaque, .Array, .Fn => true, + .Opaque, .Fn => true, + .Array => elem_ty.childType().hasRuntimeBits(), else => elem_ty.hasRuntimeBits(), }; const llvm_elem_ty = if (lower_elem_ty) @@ -889,9 +891,11 @@ pub const DeclGen = struct { else => unreachable, }, .Array => { - const elem_type = try dg.llvmType(t.childType()); + const elem_ty = t.childType(); + assert(elem_ty.onePossibleValue() == null); + const elem_llvm_ty = try dg.llvmType(elem_ty); const total_len = t.arrayLen() + @boolToInt(t.sentinel() != null); - return elem_type.arrayType(@intCast(c_uint, total_len)); + return elem_llvm_ty.arrayType(@intCast(c_uint, total_len)); }, .Vector => { const elem_type = try dg.llvmType(t.childType()); @@ -978,7 +982,6 @@ pub const DeclGen = struct { if (struct_obj.layout == .Packed) { try llvm_field_types.ensureUnusedCapacity(gpa, struct_obj.fields.count() * 2); - const target = dg.module.getTarget(); comptime assert(Type.packed_struct_layout_version == 1); var offset: u64 = 0; var big_align: u32 = 0; @@ -1073,7 +1076,6 @@ pub const DeclGen = struct { gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator()); const union_obj = t.cast(Type.Payload.Union).?.data; - const target = dg.module.getTarget(); if (t.unionTagType()) |enum_tag_ty| { const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty); const layout = union_obj.getLayout(target, true); @@ -1141,7 +1143,6 @@ pub const DeclGen = struct { }, .Fn => { const fn_info = t.fnInfo(); - const target = dg.module.getTarget(); const sret = firstParamSRet(fn_info, target); const return_type = fn_info.return_type; const raw_llvm_ret_ty = try dg.llvmType(return_type); @@ -1257,16 +1258,21 @@ pub const DeclGen = struct { }, .Float => { const llvm_ty = try dg.llvmType(tv.ty); - switch (tv.ty.floatBits(dg.module.getTarget())) { + const target = dg.module.getTarget(); + switch (tv.ty.floatBits(target)) { 16, 32, 64 => return llvm_ty.constReal(tv.val.toFloat(f64)), 80 => { const float = tv.val.toFloat(f80); - const repr = @ptrCast(*const std.math.F80Repr, &float); + const repr = std.math.break_f80(float); const llvm_i80 = dg.context.intType(80); var x = llvm_i80.constInt(repr.exp, .False); x = x.constShl(llvm_i80.constInt(64, .False)); x = x.constOr(llvm_i80.constInt(repr.fraction, .False)); - return x.constBitCast(llvm_ty); + if (backendSupportsF80(target)) { + return x.constBitCast(llvm_ty); + } else { + return x; + } }, 128 => { var buf: [2]u64 = @bitCast([2]u64, tv.val.toFloat(f128)); @@ -5353,3 +5359,12 @@ fn isByRef(ty: Type) bool { }, } } + +/// This function returns true if we expect LLVM to lower x86_fp80 correctly +/// and false if we expect LLVM to crash if it counters an x86_fp80 type. +fn backendSupportsF80(target: std.Target) bool { + return switch (target.cpu.arch) { + .x86_64, .i386 => true, + else => false, + }; +} diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 7b0bcbe2f5..f1a94b9bb9 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -8195,17 +8195,15 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n case 64: return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f64); case 80: { - uint64_t buf[2]; - memcpy(&buf, &const_val->data.x_f80, 16); -#if ZIG_BYTE_ORDER == ZIG_BIG_ENDIAN - uint64_t tmp = buf[0]; - buf[0] = buf[1]; - buf[1] = tmp; -#endif - LLVMValueRef as_i128 = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, buf); - if (!target_has_f80(g->zig_target)) return as_i128; - LLVMValueRef as_int = LLVMConstTrunc(as_i128, LLVMIntType(80)); - return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry)); + LLVMTypeRef llvm_i80 = LLVMIntType(80); + LLVMValueRef x = LLVMConstInt(llvm_i80, const_val->data.x_f80.signExp, false); + x = LLVMConstShl(x, LLVMConstInt(llvm_i80, 64, false)); + x = LLVMConstOr(x, LLVMConstInt(llvm_i80, const_val->data.x_f80.signif, false)); + if (target_has_f80(g->zig_target)) { + return LLVMConstBitCast(x, LLVMX86FP80Type()); + } else { + return x; + } } case 128: { @@ -9429,32 +9427,36 @@ static void define_builtin_types(CodeGen *g) { { ZigType *entry = new_type_table_entry(ZigTypeIdFloat); - unsigned u64_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMInt64Type()); + entry->size_in_bits = 80; - if (u64_alignment >= 8) { - entry->size_in_bits = 128; - entry->abi_size = 16; - entry->abi_align = 16; - } else if (u64_alignment >= 4) { - entry->size_in_bits = 96; - entry->abi_size = 12; - entry->abi_align = 4; - } else { - entry->size_in_bits = 80; - entry->abi_size = 10; - entry->abi_align = 2; - } - if (target_has_f80(g->zig_target)) { - entry->llvm_type = LLVMX86FP80Type(); - } else { - // We use an int here instead of x86_fp80 because on targets such as arm, - // LLVM will give "ERROR: Cannot select" for any instructions involving - // the x86_fp80 type. - entry->llvm_type = get_int_type(g, false, entry->size_in_bits)->llvm_type; - } buf_init_from_str(&entry->name, "f80"); entry->data.floating.bit_count = 80; + switch (g->zig_target->arch) { + case ZigLLVM_x86_64: + entry->llvm_type = LLVMX86FP80Type(); + entry->abi_size = 16; + entry->abi_align = 16; + break; + case ZigLLVM_x86: + entry->llvm_type = LLVMX86FP80Type(); + entry->abi_size = 12; + entry->abi_align = 4; + break; + default: { + // We use an int here instead of x86_fp80 because on targets such as arm, + // LLVM will give "ERROR: Cannot select" for any instructions involving + // the x86_fp80 type. + ZigType *u80_ty = get_int_type(g, false, 80); + assert(!target_has_f80(g->zig_target)); + assert(u80_ty->size_in_bits == entry->size_in_bits); + entry->llvm_type = get_llvm_type(g, u80_ty); + entry->abi_size = u80_ty->abi_size; + entry->abi_align = u80_ty->abi_align; + break; + } + } + entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), entry->size_in_bits, ZigLLVMEncoding_DW_ATE_unsigned()); diff --git a/src/type.zig b/src/type.zig index 0827b2e2d7..27fdb0abc8 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1877,9 +1877,28 @@ pub const Type = extern union { .f16 => return 2, .f32 => return 4, .f64 => return 8, - .f80 => return 16, .f128 => return 16, - .c_longdouble => return 16, + + .f80 => switch (target.cpu.arch) { + .i386 => return 4, + .x86_64 => return 16, + else => { + var payload: Payload.Bits = .{ + .base = .{ .tag = .int_unsigned }, + .data = 80, + }; + const u80_ty = initPayload(&payload.base); + return abiAlignment(u80_ty, target); + }, + }, + .c_longdouble => switch (CType.longdouble.sizeInBits(target)) { + 16 => return abiAlignment(Type.f16, target), + 32 => return abiAlignment(Type.f32, target), + 64 => return abiAlignment(Type.f64, target), + 80 => return abiAlignment(Type.f80, target), + 128 => return abiAlignment(Type.f128, target), + else => unreachable, + }, .error_set, .error_set_single, @@ -2158,9 +2177,28 @@ pub const Type = extern union { .f16 => return 2, .f32 => return 4, .f64 => return 8, - .f80 => return 16, .f128 => return 16, - .c_longdouble => return 16, + + .f80 => switch (target.cpu.arch) { + .i386 => return 12, + .x86_64 => return 16, + else => { + var payload: Payload.Bits = .{ + .base = .{ .tag = .int_unsigned }, + .data = 80, + }; + const u80_ty = initPayload(&payload.base); + return abiSize(u80_ty, target); + }, + }, + .c_longdouble => switch (CType.longdouble.sizeInBits(target)) { + 16 => return abiSize(Type.f16, target), + 32 => return abiSize(Type.f32, target), + 64 => return abiSize(Type.f64, target), + 80 => return abiSize(Type.f80, target), + 128 => return abiSize(Type.f128, target), + else => unreachable, + }, .error_set, .error_set_single, @@ -2349,7 +2387,7 @@ pub const Type = extern union { .c_ulong => return CType.ulong.sizeInBits(target), .c_longlong => return CType.longlong.sizeInBits(target), .c_ulonglong => return CType.ulonglong.sizeInBits(target), - .c_longdouble => 128, + .c_longdouble => return CType.longdouble.sizeInBits(target), .error_set, .error_set_single, @@ -4772,6 +4810,13 @@ pub const Type = extern union { pub const @"u8" = initTag(.u8); pub const @"u32" = initTag(.u32); pub const @"u64" = initTag(.u64); + + pub const @"f16" = initTag(.f16); + pub const @"f32" = initTag(.f32); + pub const @"f64" = initTag(.f64); + pub const @"f80" = initTag(.f80); + pub const @"f128" = initTag(.f128); + pub const @"bool" = initTag(.bool); pub const @"usize" = initTag(.usize); pub const @"isize" = initTag(.isize); diff --git a/src/value.zig b/src/value.zig index aefb0a3e20..3479819160 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1112,6 +1112,19 @@ pub const Value = extern union { } fn floatWriteToMemory(comptime F: type, f: F, target: Target, buffer: []u8) void { + if (F == f80) { + switch (target.cpu.arch) { + .i386, .x86_64 => { + const repr = std.math.break_f80(f); + std.mem.writeIntLittle(u64, buffer[0..8], repr.fraction); + std.mem.writeIntLittle(u16, buffer[8..10], repr.exp); + // TODO set the rest of the bytes to undefined. should we use 0xaa + // or is there a different way? + return; + }, + else => {}, + } + } const Int = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = @typeInfo(F).Float.bits, @@ -1122,41 +1135,43 @@ pub const Value = extern union { fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F { if (F == f80) { - switch (target.cpu.arch.endian()) { - .Little => { - const TargetF80Repr = extern struct { - fraction: u64, - exp: u16, - }; - const target_repr = @ptrCast(*align(1) const TargetF80Repr, buffer.ptr); - const real_repr: std.math.F80Repr = .{ - .fraction = target_repr.fraction, - .exp = target_repr.exp, - }; - return @ptrCast(*const f80, &real_repr).*; - }, - .Big => { - const TargetF80Repr = extern struct { - exp: u16, - fraction: u64, - }; - const target_repr = @ptrCast(*align(1) const TargetF80Repr, buffer.ptr); - const real_repr: std.math.F80Repr = .{ - .fraction = target_repr.fraction, - .exp = target_repr.exp, - }; - return @ptrCast(*const f80, &real_repr).*; - }, + switch (target.cpu.arch) { + .i386, .x86_64 => return std.math.make_f80(.{ + .fraction = std.mem.readIntLittle(u64, buffer[0..8]), + .exp = std.mem.readIntLittle(u16, buffer[8..10]), + }), + else => {}, } } const Int = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = @typeInfo(F).Float.bits, } }); - const int = std.mem.readInt(Int, buffer[0..@sizeOf(Int)], target.cpu.arch.endian()); + const int = readInt(Int, buffer[0..@sizeOf(Int)], target.cpu.arch.endian()); return @bitCast(F, int); } + fn readInt(comptime Int: type, buffer: *const [@sizeOf(Int)]u8, endian: std.builtin.Endian) Int { + var result: Int = 0; + switch (endian) { + .Big => { + for (buffer) |byte| { + result <<= 8; + result |= byte; + } + }, + .Little => { + var i: usize = buffer.len; + while (i != 0) { + i -= 1; + result <<= 8; + result |= buffer[i]; + } + }, + } + return result; + } + /// Asserts that the value is a float or an integer. pub fn toFloat(val: Value, comptime T: type) T { return switch (val.tag()) { diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index ed632c26c5..00f4cff6e5 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -5,7 +5,10 @@ const math = std.math; const pi = std.math.pi; const e = std.math.e; const Vector = std.meta.Vector; -const has_f80_rt = @import("builtin").cpu.arch == .x86_64; +const has_f80_rt = switch (builtin.cpu.arch) { + .x86_64, .i386 => true, + else => false, +}; const epsilon_16 = 0.001; const epsilon = 0.000001; From 1a8987fe7c0e8ee43eb8f3dcccb932b7b2afcee2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 10 Feb 2022 22:19:44 -0700 Subject: [PATCH 0141/2031] CI: additionally test stage2 LLVM backend targeting aarch64-linux --- ci/zinc/linux_test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/zinc/linux_test.sh b/ci/zinc/linux_test.sh index d9f42e6876..8f3eaacc7e 100755 --- a/ci/zinc/linux_test.sh +++ b/ci/zinc/linux_test.sh @@ -5,6 +5,7 @@ ZIG=$DEBUG_STAGING/bin/zig $ZIG test test/behavior.zig -fno-stage1 -I test -fLLVM +$ZIG test test/behavior.zig -fno-stage1 -I test -fLLVM -target aarch64-linux --test-cmd qemu-aarch64 --test-cmd-bin $ZIG test test/behavior.zig -fno-stage1 -I test -ofmt=c $ZIG test test/behavior.zig -fno-stage1 -I test -target wasm32-wasi --test-cmd wasmtime --test-cmd-bin $ZIG test test/behavior.zig -fno-stage1 -I test -target arm-linux --test-cmd qemu-arm --test-cmd-bin From 335c680cde670776280c3a812adb64abd1311d97 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 10 Feb 2022 22:28:53 -0700 Subject: [PATCH 0142/2031] LLVM backend: fix union with only 1 tag tripping llvm assertion --- src/codegen/llvm.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index b2adb898d3..ec0ead94e4 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -835,6 +835,7 @@ pub const DeclGen = struct { var buffer: Type.Payload.Bits = undefined; const int_ty = t.intTagType(&buffer); const bit_count = int_ty.intInfo(target).bits; + assert(bit_count != 0); return dg.context.intType(bit_count); }, .Float => switch (t.floatBits(target)) { @@ -1077,10 +1078,10 @@ pub const DeclGen = struct { const union_obj = t.cast(Type.Payload.Union).?.data; if (t.unionTagType()) |enum_tag_ty| { - const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty); const layout = union_obj.getLayout(target, true); if (layout.payload_size == 0) { + const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty); gop.value_ptr.* = enum_tag_llvm_ty; return enum_tag_llvm_ty; } @@ -1111,6 +1112,7 @@ pub const DeclGen = struct { llvm_union_ty.structSetBody(&llvm_fields, llvm_fields.len, .False); return llvm_union_ty; } + const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty); // Put the tag before or after the payload depending on which one's // alignment is greater. From d72f832b1ebab0db106e64bc9f59eba90c414311 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 10 Feb 2022 23:03:13 -0700 Subject: [PATCH 0143/2031] LLVM backend: call constPtrToInt instead of constBitCast when appropriate. Avoids tripping an LLVM assertion. --- src/codegen/llvm.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index ec0ead94e4..85cb808996 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1858,7 +1858,11 @@ pub const DeclGen = struct { try self.resolveGlobalDecl(decl); const llvm_type = try self.llvmType(tv.ty); - return llvm_val.constBitCast(llvm_type); + if (tv.ty.zigTypeTag() == .Int) { + return llvm_val.constPtrToInt(llvm_type); + } else { + return llvm_val.constBitCast(llvm_type); + } } fn lowerPtrToVoid(dg: *DeclGen, ptr_ty: Type) !*const llvm.Value { From b92e1ab8ccf4e450467dfd5ee8ff9ea87c148c26 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 10 Feb 2022 23:51:09 -0700 Subject: [PATCH 0144/2031] stage1: override f80 alignment for i386-windows Comment reproduced here: Note the following u64 alignments: x86-linux: 4 x86-windows: 8 LLVM makes x86_fp80 have the following alignment and sizes regardless of operating system: x86_64: size=16, align=16 x86: size=12, align=4 However in Zig we override x86-windows to have size=16, align=16 in order for the property to hold that u80 and f80 have the same ABI size. Fixes "error: destination type 'f80' has size 12 but source type 'u80' has size 16" when trying to bitcast between f80 and u80 on i386-windows. --- src/stage1/codegen.cpp | 47 ++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index f1a94b9bb9..dc8f5e590e 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -9432,29 +9432,40 @@ static void define_builtin_types(CodeGen *g) { buf_init_from_str(&entry->name, "f80"); entry->data.floating.bit_count = 80; - switch (g->zig_target->arch) { - case ZigLLVM_x86_64: - entry->llvm_type = LLVMX86FP80Type(); + if (target_has_f80(g->zig_target)) { + entry->llvm_type = LLVMX86FP80Type(); + + // Note the following u64 alignments: + // x86-linux: 4 + // x86-windows: 8 + // LLVM makes x86_fp80 have the following alignment and sizes regardless + // of operating system: + // x86_64: size=16, align=16 + // x86: size=12, align=4 + // However in Zig we override x86-windows to have size=16, align=16 + // in order for the property to hold that u80 and f80 have the same ABI size. + unsigned u64_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMInt64Type()); + + if (u64_alignment >= 8) { entry->abi_size = 16; entry->abi_align = 16; - break; - case ZigLLVM_x86: - entry->llvm_type = LLVMX86FP80Type(); + } else if (u64_alignment >= 4) { entry->abi_size = 12; entry->abi_align = 4; - break; - default: { - // We use an int here instead of x86_fp80 because on targets such as arm, - // LLVM will give "ERROR: Cannot select" for any instructions involving - // the x86_fp80 type. - ZigType *u80_ty = get_int_type(g, false, 80); - assert(!target_has_f80(g->zig_target)); - assert(u80_ty->size_in_bits == entry->size_in_bits); - entry->llvm_type = get_llvm_type(g, u80_ty); - entry->abi_size = u80_ty->abi_size; - entry->abi_align = u80_ty->abi_align; - break; + } else { + entry->abi_size = 10; + entry->abi_align = u64_alignment; } + } else { + // We use an int here instead of x86_fp80 because on targets such as arm, + // LLVM will give "ERROR: Cannot select" for any instructions involving + // the x86_fp80 type. + ZigType *u80_ty = get_int_type(g, false, 80); + assert(!target_has_f80(g->zig_target)); + assert(u80_ty->size_in_bits == entry->size_in_bits); + entry->llvm_type = get_llvm_type(g, u80_ty); + entry->abi_size = u80_ty->abi_size; + entry->abi_align = u80_ty->abi_align; } entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), From ba31a9469f48065c9ebf935160faffd0f6c9bfdc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 10 Feb 2022 23:56:09 -0700 Subject: [PATCH 0145/2031] Sema: int casting to u0 returns const value Also shift left with u0 rhs returns lhs even when lhs is runtime known. --- src/Sema.zig | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 3b4f1c6f55..41c841e298 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5977,8 +5977,13 @@ fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air return sema.fail(block, src, "unable to cast runtime value to 'comptime_int'", .{}); } - try sema.requireRuntimeBlock(block, operand_src); // TODO insert safety check to make sure the value fits in the dest type + + if ((try sema.typeHasOnePossibleValue(block, dest_ty_src, dest_ty))) |opv| { + return sema.addConstant(dest_ty, opv); + } + + try sema.requireRuntimeBlock(block, operand_src); return block.addTyOp(.intcast, dest_ty, operand); } @@ -7537,17 +7542,21 @@ fn zirShl( const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs); const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs); + if (maybe_rhs_val) |rhs_val| { + if (rhs_val.isUndef()) { + return sema.addConstUndef(sema.typeOf(lhs)); + } + if (rhs_val.compareWithZero(.eq)) { + return lhs; + } + } + const runtime_src = if (maybe_lhs_val) |lhs_val| rs: { const lhs_ty = sema.typeOf(lhs); if (lhs_val.isUndef()) return sema.addConstUndef(lhs_ty); const rhs_val = maybe_rhs_val orelse break :rs rhs_src; - if (rhs_val.isUndef()) return sema.addConstUndef(lhs_ty); - // If rhs is 0, return lhs without doing any calculations. - if (rhs_val.compareWithZero(.eq)) { - return sema.addConstant(lhs_ty, lhs_val); - } const target = sema.mod.getTarget(); const val = switch (air_tag) { .shl_exact => val: { @@ -7577,12 +7586,7 @@ fn zirShl( }; return sema.addConstant(lhs_ty, val); - } else rs: { - if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) return sema.addConstUndef(sema.typeOf(lhs)); - } - break :rs lhs_src; - }; + } else lhs_src; // TODO: insert runtime safety check for shl_exact From 38236533f1d031d31c6d10cbe7d0f8c59a9e3520 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 11 Feb 2022 00:03:53 -0700 Subject: [PATCH 0146/2031] LLVM backend: avoid creating invalid LLVM types Fixes assertions from creating i0 types which are not allowed in LLVM. --- src/codegen/llvm.zig | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 85cb808996..3c1f0c9737 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -687,9 +687,6 @@ pub const DeclGen = struct { const target = dg.module.getTarget(); const sret = firstParamSRet(fn_info, target); - const return_type = fn_info.return_type; - const raw_llvm_ret_ty = try dg.llvmType(return_type); - const fn_type = try dg.llvmType(zig_fn_type); const fqn = try decl.getFullyQualifiedName(dg.gpa); @@ -708,6 +705,8 @@ pub const DeclGen = struct { if (sret) { dg.addArgAttr(llvm_fn, 0, "nonnull"); // Sret pointers must not be address 0 dg.addArgAttr(llvm_fn, 0, "noalias"); + + const raw_llvm_ret_ty = try dg.llvmType(fn_info.return_type); llvm_fn.addSretAttr(0, raw_llvm_ret_ty); } @@ -737,7 +736,7 @@ pub const DeclGen = struct { // Function attributes that are independent of analysis results of the function body. dg.addCommonFnAttributes(llvm_fn); - if (return_type.isNoReturn()) { + if (fn_info.return_type.isNoReturn()) { dg.addFnAttr(llvm_fn, "noreturn"); } @@ -829,6 +828,7 @@ pub const DeclGen = struct { .Void, .NoReturn => return dg.context.voidType(), .Int => { const info = t.intInfo(target); + assert(info.bits != 0); return dg.context.intType(info.bits); }, .Enum => { @@ -1147,17 +1147,17 @@ pub const DeclGen = struct { const fn_info = t.fnInfo(); const sret = firstParamSRet(fn_info, target); const return_type = fn_info.return_type; - const raw_llvm_ret_ty = try dg.llvmType(return_type); - const llvm_ret_ty = if (!return_type.hasRuntimeBits() or sret) - dg.context.voidType() + const llvm_sret_ty = if (return_type.hasRuntimeBits()) + try dg.llvmType(return_type) else - raw_llvm_ret_ty; + dg.context.voidType(); + const llvm_ret_ty = if (sret) dg.context.voidType() else llvm_sret_ty; var llvm_params = std.ArrayList(*const llvm.Type).init(dg.gpa); defer llvm_params.deinit(); if (sret) { - try llvm_params.append(raw_llvm_ret_ty.pointerType(0)); + try llvm_params.append(llvm_sret_ty.pointerType(0)); } for (fn_info.param_types) |param_ty| { @@ -1210,6 +1210,7 @@ pub const DeclGen = struct { const bigint = tv.val.toBigInt(&bigint_space); const target = dg.module.getTarget(); const int_info = tv.ty.intInfo(target); + assert(int_info.bits != 0); const llvm_type = dg.context.intType(int_info.bits); const unsigned_val = v: { @@ -2241,7 +2242,6 @@ pub const FuncGen = struct { }; const fn_info = zig_fn_ty.fnInfo(); const return_type = fn_info.return_type; - const llvm_ret_ty = try self.dg.llvmType(return_type); const llvm_fn = try self.resolveInst(pl_op.operand); const target = self.dg.module.getTarget(); const sret = firstParamSRet(fn_info, target); @@ -2250,6 +2250,7 @@ pub const FuncGen = struct { defer llvm_args.deinit(); const ret_ptr = if (!sret) null else blk: { + const llvm_ret_ty = try self.dg.llvmType(return_type); const ret_ptr = self.buildAlloca(llvm_ret_ty); ret_ptr.setAlignment(return_type.abiAlignment(target)); try llvm_args.append(ret_ptr); @@ -2284,6 +2285,7 @@ pub const FuncGen = struct { } else if (self.liveness.isUnused(inst) or !return_type.hasRuntimeBits()) { return null; } else if (sret) { + const llvm_ret_ty = try self.dg.llvmType(return_type); call.setCallSret(llvm_ret_ty); return ret_ptr; } else { From 91508e10abe4ab82e4f7a4dcdfac178ebd9e52d3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 11 Feb 2022 00:33:51 -0700 Subject: [PATCH 0147/2031] LLVM backend: handle unnamed structs when lowering array values LLVM doesn't support lowering union values, so we have to use unnamed structs to do it, which means any type that contains a union as an element, even if it is nested in another type, has to have a mechanism to detect when it can't be lowered normally and has to resort itself to an unnamed struct. This includes arrays. --- src/codegen/llvm.zig | 66 ++++++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 3c1f0c9737..3b9bac09ec 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1371,14 +1371,24 @@ pub const DeclGen = struct { const gpa = dg.gpa; const llvm_elems = try gpa.alloc(*const llvm.Value, elem_vals.len); defer gpa.free(llvm_elems); + var need_unnamed = false; for (elem_vals) |elem_val, i| { llvm_elems[i] = try dg.genTypedValue(.{ .ty = elem_ty, .val = elem_val }); + need_unnamed = need_unnamed or dg.isUnnamedType(elem_ty, llvm_elems[i]); + } + if (need_unnamed) { + return dg.context.constStruct( + llvm_elems.ptr, + @intCast(c_uint, llvm_elems.len), + .True, + ); + } else { + const llvm_elem_ty = try dg.llvmType(elem_ty); + return llvm_elem_ty.constArray( + llvm_elems.ptr, + @intCast(c_uint, llvm_elems.len), + ); } - const llvm_elem_ty = try dg.llvmType(elem_ty); - return llvm_elem_ty.constArray( - llvm_elems.ptr, - @intCast(c_uint, llvm_elems.len), - ); }, .repeated => { const val = tv.val.castTag(.repeated).?.data; @@ -1389,25 +1399,46 @@ pub const DeclGen = struct { const gpa = dg.gpa; const llvm_elems = try gpa.alloc(*const llvm.Value, len_including_sent); defer gpa.free(llvm_elems); - for (llvm_elems[0..len]) |*elem| { - elem.* = try dg.genTypedValue(.{ .ty = elem_ty, .val = val }); + + var need_unnamed = false; + if (len != 0) { + for (llvm_elems[0..len]) |*elem| { + elem.* = try dg.genTypedValue(.{ .ty = elem_ty, .val = val }); + } + need_unnamed = need_unnamed or dg.isUnnamedType(elem_ty, llvm_elems[0]); } + if (sentinel) |sent| { llvm_elems[len] = try dg.genTypedValue(.{ .ty = elem_ty, .val = sent }); + need_unnamed = need_unnamed or dg.isUnnamedType(elem_ty, llvm_elems[len]); + } + + if (need_unnamed) { + return dg.context.constStruct( + llvm_elems.ptr, + @intCast(c_uint, llvm_elems.len), + .True, + ); + } else { + const llvm_elem_ty = try dg.llvmType(elem_ty); + return llvm_elem_ty.constArray( + llvm_elems.ptr, + @intCast(c_uint, llvm_elems.len), + ); } - const llvm_elem_ty = try dg.llvmType(elem_ty); - return llvm_elem_ty.constArray( - llvm_elems.ptr, - @intCast(c_uint, llvm_elems.len), - ); }, .empty_array_sentinel => { const elem_ty = tv.ty.elemType(); const sent_val = tv.ty.sentinel().?; const sentinel = try dg.genTypedValue(.{ .ty = elem_ty, .val = sent_val }); const llvm_elems: [1]*const llvm.Value = .{sentinel}; - const llvm_elem_ty = try dg.llvmType(elem_ty); - return llvm_elem_ty.constArray(&llvm_elems, llvm_elems.len); + const need_unnamed = dg.isUnnamedType(elem_ty, llvm_elems[0]); + if (need_unnamed) { + return dg.context.constStruct(&llvm_elems, llvm_elems.len, .True); + } else { + const llvm_elem_ty = try dg.llvmType(elem_ty); + return llvm_elem_ty.constArray(&llvm_elems, llvm_elems.len); + } }, else => unreachable, }, @@ -1495,7 +1526,7 @@ pub const DeclGen = struct { var llvm_fields = try std.ArrayListUnmanaged(*const llvm.Value).initCapacity(gpa, llvm_field_count); defer llvm_fields.deinit(gpa); - var make_unnamed_struct = false; + var need_unnamed = false; const struct_obj = tv.ty.castTag(.@"struct").?.data; if (struct_obj.layout == .Packed) { const target = dg.module.getTarget(); @@ -1596,14 +1627,13 @@ pub const DeclGen = struct { .val = field_val, }); - make_unnamed_struct = make_unnamed_struct or - dg.isUnnamedType(field_ty, field_llvm_val); + need_unnamed = need_unnamed or dg.isUnnamedType(field_ty, field_llvm_val); llvm_fields.appendAssumeCapacity(field_llvm_val); } } - if (make_unnamed_struct) { + if (need_unnamed) { return dg.context.constStruct( llvm_fields.items.ptr, @intCast(c_uint, llvm_fields.items.len), From 774f9bdb79cc1047b4b6f86c6030817810466151 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 11 Feb 2022 01:07:24 -0700 Subject: [PATCH 0148/2031] LLVM backend: disable failing aarch64 behavior test --- test/behavior/math.zig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/behavior/math.zig b/test/behavior/math.zig index c23e8ebe3e..7cf7ca4c5d 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -126,6 +126,13 @@ fn testOneCtz(comptime T: type, x: T) u32 { } test "@ctz vectors" { + if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) { + // TODO this is tripping an LLVM assert: + // zig: /home/andy/Downloads/llvm-project-13/llvm/lib/CodeGen/GlobalISel/LegalizerInfo.cpp:198: llvm::LegalizeActionStep llvm::LegalizeRuleSet::apply(const llvm::LegalityQuery&) const: Assertion `mutationIsSane(Rule, Query, Mutation) && "legality mutation invalid for match"' failed. + // I need to report a zig issue and an llvm issue + return error.SkipZigTest; + } + try testCtzVectors(); comptime try testCtzVectors(); } From 16076964d6e7c8f17117614d5a7d83070d5b8902 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 11 Feb 2022 01:27:00 -0700 Subject: [PATCH 0149/2031] disable NaN f80 behavior tests Let's get all these other bug fixes in and figure out f80 NaN later. Looks like it's not working at comptime in master branch anyway. --- test/behavior/math.zig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 7cf7ca4c5d..a9000353b8 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -988,12 +988,14 @@ test "NaN comparison" { try testNanEqNan(f32); try testNanEqNan(f64); try testNanEqNan(f128); - if (has_f80_rt and (builtin.zig_backend == .stage1)) try testNanEqNan(f80); // TODO comptime try testNanEqNan(f16); comptime try testNanEqNan(f32); comptime try testNanEqNan(f64); comptime try testNanEqNan(f128); - // comptime try testNanEqNan(f80); // TODO + + // TODO make this pass on all targets + // try testNanEqNan(f80); + // comptime try testNanEqNan(f80); } fn testNanEqNan(comptime F: type) !void { From d542a588c625b737f1680848c9862bd83c30a7c5 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 12 Feb 2022 19:09:58 +0100 Subject: [PATCH 0150/2031] Skip @sqrt f80 test on freebsd --- test/behavior/floatop.zig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 00f4cff6e5..4de8f5a5fc 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -100,8 +100,11 @@ fn testSqrt() !void { if (builtin.zig_backend == .stage1) { if (has_f80_rt) { - var a: f80 = 25; - try expect(@sqrt(a) == 5); + // TODO https://github.com/ziglang/zig/issues/10875 + if (builtin.os.tag != .freebsd) { + var a: f80 = 25; + try expect(@sqrt(a) == 5); + } } { const a: comptime_float = 25.0; From 16ec848d2ab5702ad3794d30ed5d776b5abb60ce Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 12 Feb 2022 18:54:08 +0100 Subject: [PATCH 0151/2031] macho: put linker symlink for cache invalidation in zig-cache Due to differences in where the output gets emitted in stage1 and stage2, we were putting the symlink next to the binary rather than in `zig-cache` directory when building with stage2. --- src/link/MachO.zig | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 4aa627ca39..b8e2ae0840 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -507,6 +507,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { const allow_undef = is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false); const id_symlink_basename = "zld.id"; + const cache_dir_handle = blk: { + if (use_stage1) { + break :blk directory.handle; + } + if (self.base.options.module) |module| { + break :blk module.zig_cache_artifact_directory.handle; + } + break :blk directory.handle; + }; var man: Cache.Manifest = undefined; defer if (!self.base.options.disable_lld_caching) man.deinit(); @@ -552,7 +561,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { var prev_digest_buf: [digest.len]u8 = undefined; const prev_digest: []u8 = Cache.readSmallFile( - directory.handle, + cache_dir_handle, id_symlink_basename, &prev_digest_buf, ) catch |err| blk: { @@ -588,7 +597,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { }); // We are about to change the output file to be different, so we invalidate the build hash now. - directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { + cache_dir_handle.deleteFile(id_symlink_basename) catch |err| switch (err) { error.FileNotFound => {}, else => |e| return e, }; @@ -621,7 +630,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { } else { if (use_stage1) { const sub_path = self.base.options.emit.?.sub_path; - self.base.file = try directory.handle.createFile(sub_path, .{ + self.base.file = try cache_dir_handle.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(self.base.options), @@ -1080,7 +1089,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { if (use_stage1 and self.base.options.disable_lld_caching) break :cache; // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. - Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { + Cache.writeSmallFile(cache_dir_handle, id_symlink_basename, &digest) catch |err| { log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)}); }; // Again failure here only means an unnecessary cache miss. From a005ac9d3c884c6254074ec150fe536881fe31b5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 12 Feb 2022 20:44:30 -0700 Subject: [PATCH 0152/2031] stage2: implement `@popCount` for SIMD vectors --- src/Sema.zig | 36 +++---------------------- src/codegen/llvm.zig | 15 +++++++---- src/value.zig | 45 ++++++++++++++++++------------- test/behavior.zig | 1 - test/behavior/popcount.zig | 21 ++++++++++++++- test/behavior/popcount_stage1.zig | 24 ----------------- 6 files changed, 61 insertions(+), 81 deletions(-) delete mode 100644 test/behavior/popcount_stage1.zig diff --git a/src/Sema.zig b/src/Sema.zig index 41c841e298..7dbc36af37 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -720,7 +720,6 @@ fn analyzeBodyInner( .align_cast => try sema.zirAlignCast(block, inst), .has_decl => try sema.zirHasDecl(block, inst), .has_field => try sema.zirHasField(block, inst), - .pop_count => try sema.zirPopCount(block, inst), .byte_swap => try sema.zirByteSwap(block, inst), .bit_reverse => try sema.zirBitReverse(block, inst), .bit_offset_of => try sema.zirBitOffsetOf(block, inst), @@ -743,8 +742,9 @@ fn analyzeBodyInner( .await_nosuspend => try sema.zirAwait(block, inst, true), .extended => try sema.zirExtended(block, inst), - .clz => try sema.zirClzCtz(block, inst, .clz, Value.clz), - .ctz => try sema.zirClzCtz(block, inst, .ctz, Value.ctz), + .clz => try sema.zirBitCount(block, inst, .clz, Value.clz), + .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz), + .pop_count => try sema.zirBitCount(block, inst, .popcount, Value.popCount), .sqrt => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt), .sin => try sema.zirUnaryMath(block, inst, .sin, Value.sin), @@ -11487,7 +11487,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A return sema.coerceCompatiblePtrs(block, dest_ty, ptr, ptr_src); } -fn zirClzCtz( +fn zirBitCount( sema: *Sema, block: *Block, inst: Zir.Inst.Index, @@ -11550,34 +11550,6 @@ fn zirClzCtz( } } -fn zirPopCount(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; - const operand = sema.resolveInst(inst_data.operand); - const operand_ty = sema.typeOf(operand); - // TODO implement support for vectors - if (operand_ty.zigTypeTag() != .Int) { - return sema.fail(block, ty_src, "expected integer type, found '{}'", .{ - operand_ty, - }); - } - const target = sema.mod.getTarget(); - const bits = operand_ty.intInfo(target).bits; - if (bits == 0) return Air.Inst.Ref.zero; - - const result_ty = try Type.smallestUnsignedInt(sema.arena, bits); - - const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { - if (val.isUndef()) return sema.addConstUndef(result_ty); - const result_val = try val.popCount(operand_ty, target, sema.arena); - return sema.addConstant(result_ty, result_val); - } else operand_src; - - try sema.requireRuntimeBlock(block, runtime_src); - return block.addTyOp(.popcount, result_ty, operand); -} - fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 3b9bac09ec..493b895d5d 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2205,7 +2205,7 @@ pub const FuncGen = struct { .get_union_tag => try self.airGetUnionTag(inst), .clz => try self.airClzCtz(inst, "ctlz"), .ctz => try self.airClzCtz(inst, "cttz"), - .popcount => try self.airPopCount(inst, "ctpop"), + .popcount => try self.airPopCount(inst), .tag_name => try self.airTagName(inst), .error_name => try self.airErrorName(inst), .splat => try self.airSplat(inst), @@ -4364,7 +4364,7 @@ pub const FuncGen = struct { } } - fn airPopCount(self: *FuncGen, inst: Air.Inst.Index, prefix: [*:0]const u8) !?*const llvm.Value { + fn airPopCount(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; @@ -4372,11 +4372,16 @@ pub const FuncGen = struct { const operand = try self.resolveInst(ty_op.operand); const target = self.dg.module.getTarget(); const bits = operand_ty.intInfo(target).bits; + const vec_len: ?u32 = switch (operand_ty.zigTypeTag()) { + .Vector => operand_ty.vectorLen(), + else => null, + }; var fn_name_buf: [100]u8 = undefined; - const llvm_fn_name = std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.i{d}", .{ - prefix, bits, - }) catch unreachable; + const llvm_fn_name = if (vec_len) |len| + std.fmt.bufPrintZ(&fn_name_buf, "llvm.ctpop.v{d}i{d}", .{ len, bits }) catch unreachable + else + std.fmt.bufPrintZ(&fn_name_buf, "llvm.ctpop.i{d}", .{bits}) catch unreachable; const fn_val = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: { const operand_llvm_ty = try self.dg.llvmType(operand_ty); const param_types = [_]*const llvm.Type{operand_llvm_ty}; diff --git a/src/value.zig b/src/value.zig index 3479819160..1f93a828aa 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1303,6 +1303,33 @@ pub const Value = extern union { } } + pub fn popCount(val: Value, ty: Type, target: Target) u64 { + assert(!val.isUndef()); + switch (val.tag()) { + .zero, .bool_false => return 0, + .one, .bool_true => return 1, + + .int_u64 => return @popCount(u64, val.castTag(.int_u64).?.data), + + else => { + const info = ty.intInfo(target); + + var buffer: Value.BigIntSpace = undefined; + const operand_bigint = val.toBigInt(&buffer); + + var limbs_buffer: [4]std.math.big.Limb = undefined; + var result_bigint = BigIntMutable{ + .limbs = &limbs_buffer, + .positive = undefined, + .len = undefined, + }; + result_bigint.popCount(operand_bigint, info.bits); + + return result_bigint.toConst().to(u64) catch unreachable; + }, + } + } + /// Asserts the value is an integer and not undefined. /// Returns the number of bits the value requires to represent stored in twos complement form. pub fn intBitCountTwosComp(self: Value, target: Target) usize { @@ -1340,24 +1367,6 @@ pub const Value = extern union { } } - pub fn popCount(val: Value, ty: Type, target: Target, arena: Allocator) !Value { - assert(!val.isUndef()); - - const info = ty.intInfo(target); - - var buffer: Value.BigIntSpace = undefined; - const operand_bigint = val.toBigInt(&buffer); - - const limbs = try arena.alloc( - std.math.big.Limb, - std.math.big.int.calcTwosCompLimbCount(info.bits), - ); - var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; - result_bigint.popCount(operand_bigint, info.bits); - - return fromBigInt(arena, result_bigint.toConst()); - } - /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. pub fn intFitsInType(self: Value, ty: Type, target: Target) bool { switch (self.tag()) { diff --git a/test/behavior.zig b/test/behavior.zig index 86e48f1797..8188f2baaa 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -153,7 +153,6 @@ test { _ = @import("behavior/ir_block_deps.zig"); _ = @import("behavior/misc.zig"); _ = @import("behavior/muladd.zig"); - _ = @import("behavior/popcount_stage1.zig"); _ = @import("behavior/reflection.zig"); _ = @import("behavior/select.zig"); _ = @import("behavior/shuffle.zig"); diff --git a/test/behavior/popcount.zig b/test/behavior/popcount.zig index eb3e378058..d906c1bcc0 100644 --- a/test/behavior/popcount.zig +++ b/test/behavior/popcount.zig @@ -1,7 +1,6 @@ const std = @import("std"); const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; -const Vector = std.meta.Vector; test "@popCount integers" { comptime try testPopCountIntegers(); @@ -44,3 +43,23 @@ fn testPopCountIntegers() !void { try expect(@popCount(i128, @as(i128, 0b11111111000110001100010000100001000011000011100101010001)) == 24); } } + +test "@popCount vectors" { + comptime try testPopCountVectors(); + try testPopCountVectors(); +} + +fn testPopCountVectors() !void { + { + var x: @Vector(8, u32) = [1]u32{0xffffffff} ** 8; + const expected = [1]u6{32} ** 8; + const result: [8]u6 = @popCount(u32, x); + try expect(std.mem.eql(u6, &expected, &result)); + } + { + var x: @Vector(8, i16) = [1]i16{-1} ** 8; + const expected = [1]u5{16} ** 8; + const result: [8]u5 = @popCount(i16, x); + try expect(std.mem.eql(u5, &expected, &result)); + } +} diff --git a/test/behavior/popcount_stage1.zig b/test/behavior/popcount_stage1.zig deleted file mode 100644 index 3783fdfe2f..0000000000 --- a/test/behavior/popcount_stage1.zig +++ /dev/null @@ -1,24 +0,0 @@ -const std = @import("std"); -const expect = std.testing.expect; -const expectEqual = std.testing.expectEqual; -const Vector = std.meta.Vector; - -test "@popCount vectors" { - comptime try testPopCountVectors(); - try testPopCountVectors(); -} - -fn testPopCountVectors() !void { - { - var x: Vector(8, u32) = [1]u32{0xffffffff} ** 8; - const expected = [1]u6{32} ** 8; - const result: [8]u6 = @popCount(u32, x); - try expect(std.mem.eql(u6, &expected, &result)); - } - { - var x: Vector(8, i16) = [1]i16{-1} ** 8; - const expected = [1]u5{16} ** 8; - const result: [8]u5 = @popCount(i16, x); - try expect(std.mem.eql(u5, &expected, &result)); - } -} From c349191b75811f8a21e26f8b175483449fae1638 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 12 Feb 2022 21:13:07 -0700 Subject: [PATCH 0153/2031] organize behavior tests moving towards disabling failing tests on an individual basis --- test/behavior.zig | 7 - test/behavior/array.zig | 350 ++++++++ test/behavior/array_llvm.zig | 315 -------- test/behavior/cast.zig | 340 +++++++- test/behavior/cast_llvm.zig | 299 ------- test/behavior/error.zig | 26 + test/behavior/error_llvm.zig | 24 - test/behavior/sizeof_and_typeof.zig | 113 +++ test/behavior/sizeof_and_typeof_stage1.zig | 105 --- test/behavior/struct.zig | 894 ++++++++++++++++++++- test/behavior/struct_llvm.zig | 802 ------------------ test/behavior/truncate.zig | 13 + test/behavior/truncate_stage1.zig | 13 - test/behavior/type.zig | 386 +++++++++ test/behavior/type_stage1.zig | 362 --------- 15 files changed, 2111 insertions(+), 1938 deletions(-) delete mode 100644 test/behavior/array_llvm.zig delete mode 100644 test/behavior/cast_llvm.zig delete mode 100644 test/behavior/error_llvm.zig delete mode 100644 test/behavior/sizeof_and_typeof_stage1.zig delete mode 100644 test/behavior/struct_llvm.zig delete mode 100644 test/behavior/truncate_stage1.zig delete mode 100644 test/behavior/type_stage1.zig diff --git a/test/behavior.zig b/test/behavior.zig index 8188f2baaa..404ce376a2 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -46,7 +46,6 @@ test { if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_x86_64) { // Tests that pass (partly) for stage1, llvm backend, C backend, wasm backend. - _ = @import("behavior/array_llvm.zig"); _ = @import("behavior/bitcast.zig"); _ = @import("behavior/bugs/624.zig"); _ = @import("behavior/bugs/704.zig"); @@ -61,7 +60,6 @@ test { _ = @import("behavior/bugs/4954.zig"); _ = @import("behavior/byval_arg_var.zig"); _ = @import("behavior/call.zig"); - _ = @import("behavior/cast_llvm.zig"); _ = @import("behavior/defer.zig"); _ = @import("behavior/enum.zig"); _ = @import("behavior/error.zig"); @@ -98,7 +96,6 @@ test { // Tests that pass for stage1 and the llvm backend. _ = @import("behavior/atomics.zig"); _ = @import("behavior/bugs/9584.zig"); - _ = @import("behavior/error_llvm.zig"); _ = @import("behavior/eval.zig"); _ = @import("behavior/floatop.zig"); _ = @import("behavior/math.zig"); @@ -107,7 +104,6 @@ test { _ = @import("behavior/popcount.zig"); _ = @import("behavior/saturating_arithmetic.zig"); _ = @import("behavior/sizeof_and_typeof.zig"); - _ = @import("behavior/struct_llvm.zig"); _ = @import("behavior/switch.zig"); _ = @import("behavior/widening.zig"); @@ -156,14 +152,11 @@ test { _ = @import("behavior/reflection.zig"); _ = @import("behavior/select.zig"); _ = @import("behavior/shuffle.zig"); - _ = @import("behavior/sizeof_and_typeof_stage1.zig"); _ = @import("behavior/struct_contains_null_ptr_itself.zig"); _ = @import("behavior/struct_contains_slice_of_itself.zig"); _ = @import("behavior/switch_prong_err_enum.zig"); _ = @import("behavior/switch_prong_implicit_cast.zig"); - _ = @import("behavior/truncate_stage1.zig"); _ = @import("behavior/tuple.zig"); - _ = @import("behavior/type_stage1.zig"); _ = @import("behavior/typename.zig"); _ = @import("behavior/union_with_members.zig"); _ = @import("behavior/var_args.zig"); diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 0450d0781b..23820e71b5 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -222,3 +222,353 @@ test "anonymous list literal syntax" { try S.doTheTest(); comptime try S.doTheTest(); } + +var s_array: [8]Sub = undefined; +const Sub = struct { b: u8 }; +const Str = struct { a: []Sub }; +test "set global var array via slice embedded in struct" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + var s = Str{ .a = s_array[0..] }; + + s.a[0].b = 1; + s.a[1].b = 2; + s.a[2].b = 3; + + try expect(s_array[0].b == 1); + try expect(s_array[1].b == 2); + try expect(s_array[2].b == 3); +} + +test "read/write through global variable array of struct fields initialized via array mult" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + try expect(storage[0].term == 1); + storage[0] = MyStruct{ .term = 123 }; + try expect(storage[0].term == 123); + } + + pub const MyStruct = struct { + term: usize, + }; + + var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1; + }; + try S.doTheTest(); +} + +test "implicit cast single-item pointer" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + try testImplicitCastSingleItemPtr(); + comptime try testImplicitCastSingleItemPtr(); +} + +fn testImplicitCastSingleItemPtr() !void { + var byte: u8 = 100; + const slice = @as(*[1]u8, &byte)[0..]; + slice[0] += 1; + try expect(byte == 101); +} + +fn testArrayByValAtComptime(b: [2]u8) u8 { + return b[0]; +} + +test "comptime evaluating function that takes array by value" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const arr = [_]u8{ 1, 2 }; + const x = comptime testArrayByValAtComptime(arr); + const y = comptime testArrayByValAtComptime(arr); + try expect(x == 1); + try expect(y == 1); +} + +test "runtime initialize array elem and then implicit cast to slice" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + var two: i32 = 2; + const x: []const i32 = &[_]i32{two}; + try expect(x[0] == 2); +} + +test "array literal as argument to function" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + 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 }); + } + fn foo(x: []const i32) !void { + try expect(x[0] == 1); + try expect(x[1] == 2); + try expect(x[2] == 3); + } + fn foo2(trash: bool, x: []const i32) !void { + try expect(trash); + try expect(x[0] == 1); + try expect(x[1] == 2); + try expect(x[2] == 3); + } + }; + try S.entry(2); + comptime try S.entry(2); +} + +test "double nested array to const slice cast in array literal" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn entry(two: i32) !void { + const cases = [_][]const []const i32{ + &[_][]const i32{&[_]i32{1}}, + &[_][]const i32{&[_]i32{ 2, 3 }}, + &[_][]const i32{ + &[_]i32{4}, + &[_]i32{ 5, 6, 7 }, + }, + }; + try check(&cases); + + const cases2 = [_][]const i32{ + &[_]i32{1}, + &[_]i32{ two, 3 }, + }; + try expect(cases2.len == 2); + try expect(cases2[0].len == 1); + try expect(cases2[0][0] == 1); + try expect(cases2[1].len == 2); + try expect(cases2[1][0] == 2); + try expect(cases2[1][1] == 3); + + const cases3 = [_][]const []const i32{ + &[_][]const i32{&[_]i32{1}}, + &[_][]const i32{&[_]i32{ two, 3 }}, + &[_][]const i32{ + &[_]i32{4}, + &[_]i32{ 5, 6, 7 }, + }, + }; + try check(&cases3); + } + + fn check(cases: []const []const []const i32) !void { + try expect(cases.len == 3); + try expect(cases[0].len == 1); + try expect(cases[0][0].len == 1); + try expect(cases[0][0][0] == 1); + try expect(cases[1].len == 1); + try expect(cases[1][0].len == 2); + try expect(cases[1][0][0] == 2); + try expect(cases[1][0][1] == 3); + try expect(cases[2].len == 2); + try expect(cases[2][0].len == 1); + try expect(cases[2][0][0] == 4); + try expect(cases[2][1].len == 3); + try expect(cases[2][1][0] == 5); + try expect(cases[2][1][1] == 6); + try expect(cases[2][1][2] == 7); + } + }; + try S.entry(2); + comptime try S.entry(2); +} + +test "anonymous literal in array" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + const Foo = struct { + a: usize = 2, + b: usize = 4, + }; + fn doTheTest() !void { + var array: [2]Foo = .{ + .{ .a = 3 }, + .{ .b = 3 }, + }; + try expect(array[0].a == 3); + try expect(array[0].b == 4); + try expect(array[1].a == 2); + try expect(array[1].b == 3); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "access the null element of a null terminated array" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var array: [4:0]u8 = .{ 'a', 'o', 'e', 'u' }; + try expect(array[4] == 0); + var len: usize = 4; + try expect(array[len] == 0); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "type deduction for array subscript expression" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var array = [_]u8{ 0x55, 0xAA }; + var v0 = true; + try expect(@as(u8, 0xAA) == array[if (v0) 1 else 0]); + var v1 = false; + try expect(@as(u8, 0x55) == array[if (v1) 1 else 0]); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "sentinel element count towards the ABI size calculation" { + if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + const T = packed struct { + fill_pre: u8 = 0x55, + data: [0:0]u8 = undefined, + fill_post: u8 = 0xAA, + }; + var x = T{}; + var as_slice = mem.asBytes(&x); + try expect(@as(usize, 3) == as_slice.len); + try expect(@as(u8, 0x55) == as_slice[0]); + try expect(@as(u8, 0xAA) == as_slice[2]); + } + }; + + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "zero-sized array with recursive type definition" { + if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const U = struct { + fn foo(comptime T: type, comptime n: usize) type { + return struct { + s: [n]T, + x: usize = n, + }; + } + }; + + const S = struct { + list: U.foo(@This(), 0), + }; + + var t: S = .{ .list = .{ .s = undefined } }; + try expect(@as(usize, 0) == t.list.x); +} + +test "type coercion of anon struct literal to array" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + const U = union { + a: u32, + b: bool, + c: []const u8, + }; + + fn doTheTest() !void { + var x1: u8 = 42; + const t1 = .{ x1, 56, 54 }; + var arr1: [3]u8 = t1; + try expect(arr1[0] == 42); + try expect(arr1[1] == 56); + try expect(arr1[2] == 54); + + if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO + if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + var x2: U = .{ .a = 42 }; + const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } }; + var arr2: [3]U = t2; + try expect(arr2[0].a == 42); + try expect(arr2[1].b == true); + try expect(mem.eql(u8, arr2[2].c, "hello")); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "type coercion of pointer to anon struct literal to pointer to array" { + if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + const U = union { + a: u32, + b: bool, + c: []const u8, + }; + + fn doTheTest() !void { + var x1: u8 = 42; + const t1 = &.{ x1, 56, 54 }; + var arr1: *const [3]u8 = t1; + try expect(arr1[0] == 42); + try expect(arr1[1] == 56); + try expect(arr1[2] == 54); + + var x2: U = .{ .a = 42 }; + const t2 = &.{ x2, .{ .b = true }, .{ .c = "hello" } }; + var arr2: *const [3]U = t2; + try expect(arr2[0].a == 42); + try expect(arr2[1].b == true); + try expect(mem.eql(u8, arr2[2].c, "hello")); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} diff --git a/test/behavior/array_llvm.zig b/test/behavior/array_llvm.zig deleted file mode 100644 index c3df5ba837..0000000000 --- a/test/behavior/array_llvm.zig +++ /dev/null @@ -1,315 +0,0 @@ -const std = @import("std"); -const testing = std.testing; -const expect = testing.expect; -const mem = std.mem; - -var s_array: [8]Sub = undefined; -const Sub = struct { b: u8 }; -const Str = struct { a: []Sub }; -test "set global var array via slice embedded in struct" { - if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO - var s = Str{ .a = s_array[0..] }; - - s.a[0].b = 1; - s.a[1].b = 2; - s.a[2].b = 3; - - try expect(s_array[0].b == 1); - try expect(s_array[1].b == 2); - try expect(s_array[2].b == 3); -} - -test "read/write through global variable array of struct fields initialized via array mult" { - if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const S = struct { - fn doTheTest() !void { - try expect(storage[0].term == 1); - storage[0] = MyStruct{ .term = 123 }; - try expect(storage[0].term == 123); - } - - pub const MyStruct = struct { - term: usize, - }; - - var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1; - }; - try S.doTheTest(); -} - -test "implicit cast single-item pointer" { - if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO - try testImplicitCastSingleItemPtr(); - comptime try testImplicitCastSingleItemPtr(); -} - -fn testImplicitCastSingleItemPtr() !void { - var byte: u8 = 100; - const slice = @as(*[1]u8, &byte)[0..]; - slice[0] += 1; - try expect(byte == 101); -} - -fn testArrayByValAtComptime(b: [2]u8) u8 { - return b[0]; -} - -test "comptime evaluating function that takes array by value" { - if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const arr = [_]u8{ 1, 2 }; - const x = comptime testArrayByValAtComptime(arr); - const y = comptime testArrayByValAtComptime(arr); - try expect(x == 1); - try expect(y == 1); -} - -test "runtime initialize array elem and then implicit cast to slice" { - if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO - var two: i32 = 2; - const x: []const i32 = &[_]i32{two}; - try expect(x[0] == 2); -} - -test "array literal as argument to function" { - if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO - 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 }); - } - fn foo(x: []const i32) !void { - try expect(x[0] == 1); - try expect(x[1] == 2); - try expect(x[2] == 3); - } - fn foo2(trash: bool, x: []const i32) !void { - try expect(trash); - try expect(x[0] == 1); - try expect(x[1] == 2); - try expect(x[2] == 3); - } - }; - try S.entry(2); - comptime try S.entry(2); -} - -test "double nested array to const slice cast in array literal" { - if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const S = struct { - fn entry(two: i32) !void { - const cases = [_][]const []const i32{ - &[_][]const i32{&[_]i32{1}}, - &[_][]const i32{&[_]i32{ 2, 3 }}, - &[_][]const i32{ - &[_]i32{4}, - &[_]i32{ 5, 6, 7 }, - }, - }; - try check(&cases); - - const cases2 = [_][]const i32{ - &[_]i32{1}, - &[_]i32{ two, 3 }, - }; - try expect(cases2.len == 2); - try expect(cases2[0].len == 1); - try expect(cases2[0][0] == 1); - try expect(cases2[1].len == 2); - try expect(cases2[1][0] == 2); - try expect(cases2[1][1] == 3); - - const cases3 = [_][]const []const i32{ - &[_][]const i32{&[_]i32{1}}, - &[_][]const i32{&[_]i32{ two, 3 }}, - &[_][]const i32{ - &[_]i32{4}, - &[_]i32{ 5, 6, 7 }, - }, - }; - try check(&cases3); - } - - fn check(cases: []const []const []const i32) !void { - try expect(cases.len == 3); - try expect(cases[0].len == 1); - try expect(cases[0][0].len == 1); - try expect(cases[0][0][0] == 1); - try expect(cases[1].len == 1); - try expect(cases[1][0].len == 2); - try expect(cases[1][0][0] == 2); - try expect(cases[1][0][1] == 3); - try expect(cases[2].len == 2); - try expect(cases[2][0].len == 1); - try expect(cases[2][0][0] == 4); - try expect(cases[2][1].len == 3); - try expect(cases[2][1][0] == 5); - try expect(cases[2][1][1] == 6); - try expect(cases[2][1][2] == 7); - } - }; - try S.entry(2); - comptime try S.entry(2); -} - -test "anonymous literal in array" { - if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const S = struct { - const Foo = struct { - a: usize = 2, - b: usize = 4, - }; - fn doTheTest() !void { - var array: [2]Foo = .{ - .{ .a = 3 }, - .{ .b = 3 }, - }; - try expect(array[0].a == 3); - try expect(array[0].b == 4); - try expect(array[1].a == 2); - try expect(array[1].b == 3); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "access the null element of a null terminated array" { - if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const S = struct { - fn doTheTest() !void { - var array: [4:0]u8 = .{ 'a', 'o', 'e', 'u' }; - try expect(array[4] == 0); - var len: usize = 4; - try expect(array[len] == 0); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "type deduction for array subscript expression" { - if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const S = struct { - fn doTheTest() !void { - var array = [_]u8{ 0x55, 0xAA }; - var v0 = true; - try expect(@as(u8, 0xAA) == array[if (v0) 1 else 0]); - var v1 = false; - try expect(@as(u8, 0x55) == array[if (v1) 1 else 0]); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "sentinel element count towards the ABI size calculation" { - if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO - if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO - - const S = struct { - fn doTheTest() !void { - const T = packed struct { - fill_pre: u8 = 0x55, - data: [0:0]u8 = undefined, - fill_post: u8 = 0xAA, - }; - var x = T{}; - var as_slice = mem.asBytes(&x); - try expect(@as(usize, 3) == as_slice.len); - try expect(@as(u8, 0x55) == as_slice[0]); - try expect(@as(u8, 0xAA) == as_slice[2]); - } - }; - - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "zero-sized array with recursive type definition" { - if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO - if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO - - const U = struct { - fn foo(comptime T: type, comptime n: usize) type { - return struct { - s: [n]T, - x: usize = n, - }; - } - }; - - const S = struct { - list: U.foo(@This(), 0), - }; - - var t: S = .{ .list = .{ .s = undefined } }; - try expect(@as(usize, 0) == t.list.x); -} - -test "type coercion of anon struct literal to array" { - if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const S = struct { - const U = union { - a: u32, - b: bool, - c: []const u8, - }; - - fn doTheTest() !void { - var x1: u8 = 42; - const t1 = .{ x1, 56, 54 }; - var arr1: [3]u8 = t1; - try expect(arr1[0] == 42); - try expect(arr1[1] == 56); - try expect(arr1[2] == 54); - - if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO - if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - - var x2: U = .{ .a = 42 }; - const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } }; - var arr2: [3]U = t2; - try expect(arr2[0].a == 42); - try expect(arr2[1].b == true); - try expect(mem.eql(u8, arr2[2].c, "hello")); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "type coercion of pointer to anon struct literal to pointer to array" { - if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO - if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO - - const S = struct { - const U = union { - a: u32, - b: bool, - c: []const u8, - }; - - fn doTheTest() !void { - var x1: u8 = 42; - const t1 = &.{ x1, 56, 54 }; - var arr1: *const [3]u8 = t1; - try expect(arr1[0] == 42); - try expect(arr1[1] == 56); - try expect(arr1[2] == 54); - - var x2: U = .{ .a = 42 }; - const t2 = &.{ x2, .{ .b = true }, .{ .c = "hello" } }; - var arr2: *const [3]U = t2; - try expect(arr2[0].a == 42); - try expect(arr2[1].b == true); - try expect(mem.eql(u8, arr2[2].c, "hello")); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 79f75f773c..4028d8c5f1 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1,8 +1,9 @@ +const builtin = @import("builtin"); const std = @import("std"); const expect = std.testing.expect; const mem = std.mem; const maxInt = std.math.maxInt; -const builtin = @import("builtin"); +const native_endian = builtin.target.cpu.arch.endian(); test "int to ptr cast" { const x = @as(usize, 13); @@ -93,7 +94,8 @@ test "comptime_int @intToFloat" { } test "@floatToInt" { - if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO try testFloatToInts(); comptime try testFloatToInts(); @@ -802,3 +804,337 @@ test "comptime float casts" { try expectFloatToInt(comptime_int, 1234, i16, 1234); try expectFloatToInt(comptime_float, 12.3, comptime_int, 12); } + +test "pointer reinterpret const float to int" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + // The hex representation is 0x3fe3333333333303. + const float: f64 = 5.99999999999994648725e-01; + const float_ptr = &float; + const int_ptr = @ptrCast(*const i32, float_ptr); + const int_val = int_ptr.*; + if (native_endian == .Little) + try expect(int_val == 0x33333303) + else + try expect(int_val == 0x3fe33333); +} + +test "implicit cast from [*]T to ?*anyopaque" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + var a = [_]u8{ 3, 2, 1 }; + var runtime_zero: usize = 0; + incrementVoidPtrArray(a[runtime_zero..].ptr, 3); + try expect(std.mem.eql(u8, &a, &[_]u8{ 4, 3, 2 })); +} + +fn incrementVoidPtrArray(array: ?*anyopaque, len: usize) void { + var n: usize = 0; + while (n < len) : (n += 1) { + @ptrCast([*]u8, array.?)[n] += 1; + } +} + +test "compile time int to ptr of function" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + try foobar(FUNCTION_CONSTANT); +} + +pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize)); +pub const PFN_void = *const fn (*anyopaque) callconv(.C) void; + +fn foobar(func: PFN_void) !void { + try std.testing.expect(@ptrToInt(func) == maxInt(usize)); +} + +test "implicit ptr to *anyopaque" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + var a: u32 = 1; + var ptr: *align(@alignOf(u32)) anyopaque = &a; + var b: *u32 = @ptrCast(*u32, ptr); + try expect(b.* == 1); + var ptr2: ?*align(@alignOf(u32)) anyopaque = &a; + var c: *u32 = @ptrCast(*u32, ptr2.?); + try expect(c.* == 1); +} + +test "return null from fn() anyerror!?&T" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const a = returnNullFromOptionalTypeErrorRef(); + const b = returnNullLitFromOptionalTypeErrorRef(); + try expect((try a) == null and (try b) == null); +} +fn returnNullFromOptionalTypeErrorRef() anyerror!?*A { + const a: ?*A = null; + return a; +} +fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A { + return null; +} + +test "peer type resolution: [0]u8 and []const u8" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); + try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); + comptime { + try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); + try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); + } +} +fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { + if (a) { + return &[_]u8{}; + } + + return slice[0..1]; +} + +test "implicitly cast from [N]T to ?[]const T" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); + comptime try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); +} + +fn castToOptionalSlice() ?[]const u8 { + return "hi"; +} + +test "cast u128 to f128 and back" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + comptime try testCast128(); + try testCast128(); +} + +fn testCast128() !void { + try expect(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000); +} + +fn cast128Int(x: f128) u128 { + return @bitCast(u128, x); +} + +fn cast128Float(x: u128) f128 { + return @bitCast(f128, x); +} + +test "implicit cast from *[N]T to ?[*]T" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + var x: ?[*]u16 = null; + var y: [4]u16 = [4]u16{ 0, 1, 2, 3 }; + + x = &y; + try expect(std.mem.eql(u16, x.?[0..4], y[0..4])); + x.?[0] = 8; + y[3] = 6; + try expect(std.mem.eql(u16, x.?[0..4], y[0..4])); +} + +test "implicit cast from *T to ?*anyopaque" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + var a: u8 = 1; + incrementVoidPtrValue(&a); + try std.testing.expect(a == 2); +} + +fn incrementVoidPtrValue(value: ?*anyopaque) void { + @ptrCast(*u8, value.?).* += 1; +} + +test "implicit cast *[0]T to E![]const u8" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + var x = @as(anyerror![]const u8, &[0]u8{}); + try expect((x catch unreachable).len == 0); +} + +var global_array: [4]u8 = undefined; +test "cast from array reference to fn: comptime fn ptr" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const f = @ptrCast(*const fn () callconv(.C) void, &global_array); + try expect(@ptrToInt(f) == @ptrToInt(&global_array)); +} +test "cast from array reference to fn: runtime fn ptr" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + var f = @ptrCast(*const fn () callconv(.C) void, &global_array); + try expect(@ptrToInt(f) == @ptrToInt(&global_array)); +} + +test "*const [N]null u8 to ?[]const u8" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var a = "Hello"; + var b: ?[]const u8 = a; + try expect(mem.eql(u8, b.?, "Hello")); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "cast between [*c]T and ?[*:0]T on fn parameter" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const S = struct { + const Handler = ?fn ([*c]const u8) callconv(.C) void; + fn addCallback(handler: Handler) void { + _ = handler; + } + + fn myCallback(cstr: ?[*:0]const u8) callconv(.C) void { + _ = cstr; + } + + fn doTheTest() void { + addCallback(myCallback); + } + }; + S.doTheTest(); +} + +var global_struct: struct { f0: usize } = undefined; +test "assignment to optional pointer result loc" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + var foo: struct { ptr: ?*anyopaque } = .{ .ptr = &global_struct }; + try expect(foo.ptr.? == @ptrCast(*anyopaque, &global_struct)); +} + +test "cast between *[N]void and []void" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + var a: [4]void = undefined; + var b: []void = &a; + try expect(b.len == 4); +} + +test "peer resolve arrays of different size to const slice" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + try expect(mem.eql(u8, boolToStr(true), "true")); + try expect(mem.eql(u8, boolToStr(false), "false")); + comptime try expect(mem.eql(u8, boolToStr(true), "true")); + comptime try expect(mem.eql(u8, boolToStr(false), "false")); +} +fn boolToStr(b: bool) []const u8 { + return if (b) "true" else "false"; +} + +test "cast f16 to wider types" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var x: f16 = 1234.0; + try expect(@as(f32, 1234.0) == x); + try expect(@as(f64, 1234.0) == x); + try expect(@as(f128, 1234.0) == x); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "cast f128 to narrower types" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var x: f128 = 1234.0; + try expect(@as(f16, 1234.0) == @floatCast(f16, x)); + try expect(@as(f32, 1234.0) == @floatCast(f32, x)); + try expect(@as(f64, 1234.0) == @floatCast(f64, x)); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "peer type resolution: unreachable, null, slice" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest(num: usize, word: []const u8) !void { + const result = switch (num) { + 0 => null, + 1 => word, + else => unreachable, + }; + try expect(mem.eql(u8, result.?, "hi")); + } + }; + try S.doTheTest(1, "hi"); +} + +test "cast i8 fn call peers to i32 result" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var cond = true; + const value: i32 = if (cond) smallBoi() else bigBoi(); + try expect(value == 123); + } + fn smallBoi() i8 { + return 123; + } + fn bigBoi() i16 { + return 1234; + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} diff --git a/test/behavior/cast_llvm.zig b/test/behavior/cast_llvm.zig deleted file mode 100644 index 6f9b77b8f2..0000000000 --- a/test/behavior/cast_llvm.zig +++ /dev/null @@ -1,299 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const expect = std.testing.expect; -const mem = std.mem; -const maxInt = std.math.maxInt; -const native_endian = builtin.target.cpu.arch.endian(); - -test "pointer reinterpret const float to int" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - - // The hex representation is 0x3fe3333333333303. - const float: f64 = 5.99999999999994648725e-01; - const float_ptr = &float; - const int_ptr = @ptrCast(*const i32, float_ptr); - const int_val = int_ptr.*; - if (native_endian == .Little) - try expect(int_val == 0x33333303) - else - try expect(int_val == 0x3fe33333); -} - -test "@floatToInt" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - - try testFloatToInts(); - comptime try testFloatToInts(); -} - -fn testFloatToInts() !void { - try expectFloatToInt(f16, 255.1, u8, 255); - try expectFloatToInt(f16, 127.2, i8, 127); - try expectFloatToInt(f16, -128.2, i8, -128); -} - -fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void { - try expect(@floatToInt(I, f) == i); -} - -test "implicit cast from [*]T to ?*anyopaque" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - - var a = [_]u8{ 3, 2, 1 }; - var runtime_zero: usize = 0; - incrementVoidPtrArray(a[runtime_zero..].ptr, 3); - try expect(std.mem.eql(u8, &a, &[_]u8{ 4, 3, 2 })); -} - -fn incrementVoidPtrArray(array: ?*anyopaque, len: usize) void { - var n: usize = 0; - while (n < len) : (n += 1) { - @ptrCast([*]u8, array.?)[n] += 1; - } -} - -test "compile time int to ptr of function" { - if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - - try foobar(FUNCTION_CONSTANT); -} - -pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize)); -pub const PFN_void = *const fn (*anyopaque) callconv(.C) void; - -fn foobar(func: PFN_void) !void { - try std.testing.expect(@ptrToInt(func) == maxInt(usize)); -} - -test "implicit ptr to *anyopaque" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - - var a: u32 = 1; - var ptr: *align(@alignOf(u32)) anyopaque = &a; - var b: *u32 = @ptrCast(*u32, ptr); - try expect(b.* == 1); - var ptr2: ?*align(@alignOf(u32)) anyopaque = &a; - var c: *u32 = @ptrCast(*u32, ptr2.?); - try expect(c.* == 1); -} - -const A = struct { - a: i32, -}; -test "return null from fn() anyerror!?&T" { - const a = returnNullFromOptionalTypeErrorRef(); - const b = returnNullLitFromOptionalTypeErrorRef(); - try expect((try a) == null and (try b) == null); -} -fn returnNullFromOptionalTypeErrorRef() anyerror!?*A { - const a: ?*A = null; - return a; -} -fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A { - return null; -} - -test "peer type resolution: [0]u8 and []const u8" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); - try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); - comptime { - try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); - try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); - } -} -fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { - if (a) { - return &[_]u8{}; - } - - return slice[0..1]; -} - -test "implicitly cast from [N]T to ?[]const T" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); - comptime try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); -} - -fn castToOptionalSlice() ?[]const u8 { - return "hi"; -} - -test "cast u128 to f128 and back" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - comptime try testCast128(); - try testCast128(); -} - -fn testCast128() !void { - try expect(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000); -} - -fn cast128Int(x: f128) u128 { - return @bitCast(u128, x); -} - -fn cast128Float(x: u128) f128 { - return @bitCast(f128, x); -} - -test "implicit cast from *[N]T to ?[*]T" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - var x: ?[*]u16 = null; - var y: [4]u16 = [4]u16{ 0, 1, 2, 3 }; - - x = &y; - try expect(std.mem.eql(u16, x.?[0..4], y[0..4])); - x.?[0] = 8; - y[3] = 6; - try expect(std.mem.eql(u16, x.?[0..4], y[0..4])); -} - -test "implicit cast from *T to ?*anyopaque" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - var a: u8 = 1; - incrementVoidPtrValue(&a); - try std.testing.expect(a == 2); -} - -fn incrementVoidPtrValue(value: ?*anyopaque) void { - @ptrCast(*u8, value.?).* += 1; -} - -test "implicit cast *[0]T to E![]const u8" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - var x = @as(anyerror![]const u8, &[0]u8{}); - try expect((x catch unreachable).len == 0); -} - -var global_array: [4]u8 = undefined; -test "cast from array reference to fn: comptime fn ptr" { - const f = @ptrCast(*const fn () callconv(.C) void, &global_array); - try expect(@ptrToInt(f) == @ptrToInt(&global_array)); -} -test "cast from array reference to fn: runtime fn ptr" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - var f = @ptrCast(*const fn () callconv(.C) void, &global_array); - try expect(@ptrToInt(f) == @ptrToInt(&global_array)); -} - -test "*const [N]null u8 to ?[]const u8" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const S = struct { - fn doTheTest() !void { - var a = "Hello"; - var b: ?[]const u8 = a; - try expect(mem.eql(u8, b.?, "Hello")); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "cast between [*c]T and ?[*:0]T on fn parameter" { - const S = struct { - const Handler = ?fn ([*c]const u8) callconv(.C) void; - fn addCallback(handler: Handler) void { - _ = handler; - } - - fn myCallback(cstr: ?[*:0]const u8) callconv(.C) void { - _ = cstr; - } - - fn doTheTest() void { - addCallback(myCallback); - } - }; - S.doTheTest(); -} - -var global_struct: struct { f0: usize } = undefined; -test "assignment to optional pointer result loc" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - var foo: struct { ptr: ?*anyopaque } = .{ .ptr = &global_struct }; - try expect(foo.ptr.? == @ptrCast(*anyopaque, &global_struct)); -} - -test "cast between *[N]void and []void" { - var a: [4]void = undefined; - var b: []void = &a; - try expect(b.len == 4); -} - -test "peer resolve arrays of different size to const slice" { - try expect(mem.eql(u8, boolToStr(true), "true")); - try expect(mem.eql(u8, boolToStr(false), "false")); - comptime try expect(mem.eql(u8, boolToStr(true), "true")); - comptime try expect(mem.eql(u8, boolToStr(false), "false")); -} -fn boolToStr(b: bool) []const u8 { - return if (b) "true" else "false"; -} - -test "cast f16 to wider types" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - const S = struct { - fn doTheTest() !void { - var x: f16 = 1234.0; - try expect(@as(f32, 1234.0) == x); - try expect(@as(f64, 1234.0) == x); - try expect(@as(f128, 1234.0) == x); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "cast f128 to narrower types" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - - const S = struct { - fn doTheTest() !void { - var x: f128 = 1234.0; - try expect(@as(f16, 1234.0) == @floatCast(f16, x)); - try expect(@as(f32, 1234.0) == @floatCast(f32, x)); - try expect(@as(f64, 1234.0) == @floatCast(f64, x)); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "peer type resolution: unreachable, null, slice" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const S = struct { - fn doTheTest(num: usize, word: []const u8) !void { - const result = switch (num) { - 0 => null, - 1 => word, - else => unreachable, - }; - try expect(mem.eql(u8, result.?, "hi")); - } - }; - try S.doTheTest(1, "hi"); -} - -test "cast i8 fn call peers to i32 result" { - const S = struct { - fn doTheTest() !void { - var cond = true; - const value: i32 = if (cond) smallBoi() else bigBoi(); - try expect(value == 123); - } - fn smallBoi() i8 { - return 123; - } - fn bigBoi() i16 { - return 1234; - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} diff --git a/test/behavior/error.zig b/test/behavior/error.zig index d58ad6ccb5..2e243d1d23 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -478,3 +478,29 @@ test "error union comptime caching" { S.quux(@as(anyerror!void, {})); S.quux(@as(anyerror!void, {})); } + +test "@errorName" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + try expect(mem.eql(u8, @errorName(error.AnError), "AnError")); + try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); + try expect(mem.eql(u8, @errorName(gimmeItBroke()), "ItBroke")); +} +fn gimmeItBroke() anyerror { + return error.ItBroke; +} + +test "@errorName sentinel length matches slice length" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + const name = testBuiltinErrorName(error.FooBar); + const length: usize = 6; + try expect(length == std.mem.indexOfSentinel(u8, 0, name.ptr)); + try expect(length == name.len); +} + +pub fn testBuiltinErrorName(err: anyerror) [:0]const u8 { + return @errorName(err); +} diff --git a/test/behavior/error_llvm.zig b/test/behavior/error_llvm.zig deleted file mode 100644 index edebd5f629..0000000000 --- a/test/behavior/error_llvm.zig +++ /dev/null @@ -1,24 +0,0 @@ -const std = @import("std"); -const expect = std.testing.expect; -const mem = std.mem; - -fn gimmeItBroke() anyerror { - return error.ItBroke; -} - -test "@errorName" { - try expect(mem.eql(u8, @errorName(error.AnError), "AnError")); - try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); - try expect(mem.eql(u8, @errorName(gimmeItBroke()), "ItBroke")); -} - -test "@errorName sentinel length matches slice length" { - const name = testBuiltinErrorName(error.FooBar); - const length: usize = 6; - try expect(length == std.mem.indexOfSentinel(u8, 0, name.ptr)); - try expect(length == name.len); -} - -pub fn testBuiltinErrorName(err: anyerror) [:0]const u8 { - return @errorName(err); -} diff --git a/test/behavior/sizeof_and_typeof.zig b/test/behavior/sizeof_and_typeof.zig index 2dbd4b3495..f359fe458e 100644 --- a/test/behavior/sizeof_and_typeof.zig +++ b/test/behavior/sizeof_and_typeof.zig @@ -1,3 +1,4 @@ +const builtin = @import("builtin"); const std = @import("std"); const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; @@ -160,3 +161,115 @@ test "@bitOffsetOf" { try expect(@offsetOf(A, "f") * 8 == @bitOffsetOf(A, "f")); try expect(@offsetOf(A, "g") * 8 == @bitOffsetOf(A, "g")); } + +test "@sizeOf(T) == 0 doesn't force resolving struct size" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + const Foo = struct { + y: if (@sizeOf(Foo) == 0) u64 else u32, + }; + const Bar = struct { + x: i32, + y: if (0 == @sizeOf(Bar)) u64 else u32, + }; + }; + + try expect(@sizeOf(S.Foo) == 4); + try expect(@sizeOf(S.Bar) == 8); +} + +test "@TypeOf() has no runtime side effects" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + fn foo(comptime T: type, ptr: *T) T { + ptr.* += 1; + return ptr.*; + } + }; + var data: i32 = 0; + const T = @TypeOf(S.foo(i32, &data)); + comptime try expect(T == i32); + try expect(data == 0); +} + +test "branching logic inside @TypeOf" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + var data: i32 = 0; + fn foo() anyerror!i32 { + data += 1; + return undefined; + } + }; + const T = @TypeOf(S.foo() catch undefined); + comptime try expect(T == i32); + try expect(S.data == 0); +} + +test "@bitSizeOf" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + try expect(@bitSizeOf(u2) == 2); + try expect(@bitSizeOf(u8) == @sizeOf(u8) * 8); + try expect(@bitSizeOf(struct { + a: u2, + }) == 8); + try expect(@bitSizeOf(packed struct { + a: u2, + }) == 2); +} + +test "@sizeOf comparison against zero" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S0 = struct { + f: *@This(), + }; + const U0 = union { + f: *@This(), + }; + const S1 = struct { + fn H(comptime T: type) type { + return struct { + x: T, + }; + } + f0: H(*@This()), + f1: H(**@This()), + f2: H(***@This()), + }; + const U1 = union { + fn H(comptime T: type) type { + return struct { + x: T, + }; + } + f0: H(*@This()), + f1: H(**@This()), + f2: H(***@This()), + }; + const S = struct { + fn doTheTest(comptime T: type, comptime result: bool) !void { + try expectEqual(result, @sizeOf(T) > 0); + } + }; + // Zero-sized type + try S.doTheTest(u0, false); + try S.doTheTest(*u0, false); + // Non byte-sized type + try S.doTheTest(u1, true); + try S.doTheTest(*u1, true); + // Regular type + try S.doTheTest(u8, true); + try S.doTheTest(*u8, true); + try S.doTheTest(f32, true); + try S.doTheTest(*f32, true); + // Container with ptr pointing to themselves + try S.doTheTest(S0, true); + try S.doTheTest(U0, true); + try S.doTheTest(S1, true); + try S.doTheTest(U1, true); +} diff --git a/test/behavior/sizeof_and_typeof_stage1.zig b/test/behavior/sizeof_and_typeof_stage1.zig deleted file mode 100644 index 20cefef0e7..0000000000 --- a/test/behavior/sizeof_and_typeof_stage1.zig +++ /dev/null @@ -1,105 +0,0 @@ -const std = @import("std"); -const expect = std.testing.expect; -const expectEqual = std.testing.expectEqual; - -test "@sizeOf(T) == 0 doesn't force resolving struct size" { - const S = struct { - const Foo = struct { - y: if (@sizeOf(Foo) == 0) u64 else u32, - }; - const Bar = struct { - x: i32, - y: if (0 == @sizeOf(Bar)) u64 else u32, - }; - }; - - try expect(@sizeOf(S.Foo) == 4); - try expect(@sizeOf(S.Bar) == 8); -} - -test "@TypeOf() has no runtime side effects" { - const S = struct { - fn foo(comptime T: type, ptr: *T) T { - ptr.* += 1; - return ptr.*; - } - }; - var data: i32 = 0; - const T = @TypeOf(S.foo(i32, &data)); - comptime try expect(T == i32); - try expect(data == 0); -} - -test "branching logic inside @TypeOf" { - const S = struct { - var data: i32 = 0; - fn foo() anyerror!i32 { - data += 1; - return undefined; - } - }; - const T = @TypeOf(S.foo() catch undefined); - comptime try expect(T == i32); - try expect(S.data == 0); -} - -test "@bitSizeOf" { - try expect(@bitSizeOf(u2) == 2); - try expect(@bitSizeOf(u8) == @sizeOf(u8) * 8); - try expect(@bitSizeOf(struct { - a: u2, - }) == 8); - try expect(@bitSizeOf(packed struct { - a: u2, - }) == 2); -} - -test "@sizeOf comparison against zero" { - const S0 = struct { - f: *@This(), - }; - const U0 = union { - f: *@This(), - }; - const S1 = struct { - fn H(comptime T: type) type { - return struct { - x: T, - }; - } - f0: H(*@This()), - f1: H(**@This()), - f2: H(***@This()), - }; - const U1 = union { - fn H(comptime T: type) type { - return struct { - x: T, - }; - } - f0: H(*@This()), - f1: H(**@This()), - f2: H(***@This()), - }; - const S = struct { - fn doTheTest(comptime T: type, comptime result: bool) !void { - try expectEqual(result, @sizeOf(T) > 0); - } - }; - // Zero-sized type - try S.doTheTest(u0, false); - try S.doTheTest(*u0, false); - // Non byte-sized type - try S.doTheTest(u1, true); - try S.doTheTest(*u1, true); - // Regular type - try S.doTheTest(u8, true); - try S.doTheTest(*u8, true); - try S.doTheTest(f32, true); - try S.doTheTest(*f32, true); - // Container with ptr pointing to themselves - try S.doTheTest(S0, true); - try S.doTheTest(U0, true); - try S.doTheTest(S1, true); - try S.doTheTest(U1, true); -} diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 03be28b9d1..ecdd6a1846 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -133,15 +133,6 @@ fn returnEmptyStructInstance() StructWithNoFields { return empty_global_instance; } -const Node = struct { - val: Val, - next: *Node, -}; - -const Val = struct { - x: i32, -}; - test "fn call of struct field" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @@ -294,3 +285,888 @@ const blah: packed struct { test "bit field alignment" { try expect(@TypeOf(&blah.b) == *align(1:3:1) const u3); } + +const Node = struct { + val: Val, + next: *Node, +}; + +const Val = struct { + x: i32, +}; + +test "struct point to self" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + var root: Node = undefined; + root.val.x = 1; + + var node: Node = undefined; + node.next = &root; + node.val.x = 2; + + root.next = &node; + + try expect(node.next.next.next.val.x == 1); +} + +test "void struct fields" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const foo = VoidStructFieldsFoo{ + .a = void{}, + .b = 1, + .c = void{}, + }; + try expect(foo.b == 1); + try expect(@sizeOf(VoidStructFieldsFoo) == 4); +} +const VoidStructFieldsFoo = struct { + a: void, + b: i32, + c: void, +}; + +test "return empty struct from fn" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + _ = testReturnEmptyStructFromFn(); +} +const EmptyStruct2 = struct {}; +fn testReturnEmptyStructFromFn() EmptyStruct2 { + return EmptyStruct2{}; +} + +test "pass slice of empty struct to fn" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + try expect(testPassSliceOfEmptyStructToFn(&[_]EmptyStruct2{EmptyStruct2{}}) == 1); +} +fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { + return slice.len; +} + +test "self-referencing struct via array member" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const T = struct { + children: [1]*@This(), + }; + var x: T = undefined; + x = T{ .children = .{&x} }; + try expect(x.children[0] == &x); +} + +test "empty struct method call" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const es = EmptyStruct{}; + try expect(es.method() == 1234); +} +const EmptyStruct = struct { + fn method(es: *const EmptyStruct) i32 { + _ = es; + return 1234; + } +}; + +test "align 1 field before self referential align 8 field as slice return type" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const result = alloc(Expr); + try expect(result.len == 0); +} + +const Expr = union(enum) { + Literal: u8, + Question: *Expr, +}; + +fn alloc(comptime T: type) []T { + return &[_]T{}; +} + +const APackedStruct = packed struct { + x: u8, + y: u8, +}; + +test "packed struct" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + var foo = APackedStruct{ + .x = 1, + .y = 2, + }; + foo.y += 1; + const four = foo.x + foo.y; + try expect(four == 4); +} + +const Foo24Bits = packed struct { + field: u24, +}; +const Foo96Bits = packed struct { + a: u24, + b: u24, + c: u24, + d: u24, +}; + +test "packed struct 24bits" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + comptime { + try expect(@sizeOf(Foo24Bits) == 4); + if (@sizeOf(usize) == 4) { + try expect(@sizeOf(Foo96Bits) == 12); + } else { + try expect(@sizeOf(Foo96Bits) == 16); + } + } + + var value = Foo96Bits{ + .a = 0, + .b = 0, + .c = 0, + .d = 0, + }; + value.a += 1; + try expect(value.a == 1); + try expect(value.b == 0); + try expect(value.c == 0); + try expect(value.d == 0); + + value.b += 1; + try expect(value.a == 1); + try expect(value.b == 1); + try expect(value.c == 0); + try expect(value.d == 0); + + value.c += 1; + try expect(value.a == 1); + try expect(value.b == 1); + try expect(value.c == 1); + try expect(value.d == 0); + + value.d += 1; + try expect(value.a == 1); + try expect(value.b == 1); + try expect(value.c == 1); + try expect(value.d == 1); +} + +test "runtime struct initialization of bitfield" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const s1 = Nibbles{ + .x = x1, + .y = x1, + }; + const s2 = Nibbles{ + .x = @intCast(u4, x2), + .y = @intCast(u4, x2), + }; + + try expect(s1.x == x1); + try expect(s1.y == x1); + try expect(s2.x == @intCast(u4, x2)); + try expect(s2.y == @intCast(u4, x2)); +} + +var x1 = @as(u4, 1); +var x2 = @as(u8, 2); + +const Nibbles = packed struct { + x: u4, + y: u4, +}; + +const Bitfields = packed struct { + f1: u16, + f2: u16, + f3: u8, + f4: u8, + f5: u4, + f6: u4, + f7: u8, +}; + +test "native bit field understands endianness" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + var all: u64 = if (native_endian != .Little) + 0x1111222233445677 + else + 0x7765443322221111; + var bytes: [8]u8 = undefined; + @memcpy(&bytes, @ptrCast([*]u8, &all), 8); + var bitfields = @ptrCast(*Bitfields, &bytes).*; + + try expect(bitfields.f1 == 0x1111); + try expect(bitfields.f2 == 0x2222); + try expect(bitfields.f3 == 0x33); + try expect(bitfields.f4 == 0x44); + try expect(bitfields.f5 == 0x5); + try expect(bitfields.f6 == 0x6); + try expect(bitfields.f7 == 0x77); +} + +test "implicit cast packed struct field to const ptr" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const LevelUpMove = packed struct { + move_id: u9, + level: u7, + + fn toInt(value: u7) u7 { + return value; + } + }; + + var lup: LevelUpMove = undefined; + lup.level = 12; + const res = LevelUpMove.toInt(lup.level); + try expect(res == 12); +} + +test "zero-bit field in packed struct" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const S = packed struct { + x: u10, + y: void, + }; + var x: S = undefined; + _ = x; +} + +test "packed struct with non-ABI-aligned field" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const S = packed struct { + x: u9, + y: u183, + }; + var s: S = undefined; + s.x = 1; + s.y = 42; + try expect(s.x == 1); + try expect(s.y == 42); +} + +const BitField1 = packed struct { + a: u3, + b: u3, + c: u2, +}; + +const bit_field_1 = BitField1{ + .a = 1, + .b = 2, + .c = 3, +}; + +test "bit field access" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + var data = bit_field_1; + try expect(getA(&data) == 1); + try expect(getB(&data) == 2); + try expect(getC(&data) == 3); + comptime try expect(@sizeOf(BitField1) == 1); + + data.b += 1; + try expect(data.b == 3); + + data.a += 1; + try expect(data.a == 2); + try expect(data.b == 3); +} + +fn getA(data: *const BitField1) u3 { + return data.a; +} + +fn getB(data: *const BitField1) u3 { + return data.b; +} + +fn getC(data: *const BitField1) u2 { + return data.c; +} + +test "default struct initialization fields" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const S = struct { + a: i32 = 1234, + b: i32, + }; + const x = S{ + .b = 5, + }; + var five: i32 = 5; + const y = S{ + .b = five, + }; + if (x.a + x.b != 1239) { + @compileError("it should be comptime known"); + } + try expect(y.a == x.a); + try expect(y.b == x.b); + try expect(1239 == x.a + x.b); +} + +// TODO revisit this test when doing https://github.com/ziglang/zig/issues/1512 +test "packed array 24bits" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + comptime { + try expect(@sizeOf([9]Foo32Bits) == 9 * 4); + try expect(@sizeOf(FooArray24Bits) == 2 + 2 * 4 + 2); + } + + var bytes = [_]u8{0} ** (@sizeOf(FooArray24Bits) + 1); + bytes[bytes.len - 1] = 0xaa; + const ptr = &std.mem.bytesAsSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0]; + try expect(ptr.a == 0); + try expect(ptr.b[0].field == 0); + try expect(ptr.b[1].field == 0); + try expect(ptr.c == 0); + + ptr.a = maxInt(u16); + try expect(ptr.a == maxInt(u16)); + try expect(ptr.b[0].field == 0); + try expect(ptr.b[1].field == 0); + try expect(ptr.c == 0); + + ptr.b[0].field = maxInt(u24); + try expect(ptr.a == maxInt(u16)); + try expect(ptr.b[0].field == maxInt(u24)); + try expect(ptr.b[1].field == 0); + try expect(ptr.c == 0); + + ptr.b[1].field = maxInt(u24); + try expect(ptr.a == maxInt(u16)); + try expect(ptr.b[0].field == maxInt(u24)); + try expect(ptr.b[1].field == maxInt(u24)); + try expect(ptr.c == 0); + + ptr.c = maxInt(u16); + try expect(ptr.a == maxInt(u16)); + try expect(ptr.b[0].field == maxInt(u24)); + try expect(ptr.b[1].field == maxInt(u24)); + try expect(ptr.c == maxInt(u16)); + + try expect(bytes[bytes.len - 1] == 0xaa); +} + +const Foo32Bits = packed struct { + field: u24, + pad: u8, +}; + +const FooArray24Bits = packed struct { + a: u16, + b: [2]Foo32Bits, + c: u16, +}; + +test "aligned array of packed struct" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + comptime { + try expect(@sizeOf(FooStructAligned) == 2); + try expect(@sizeOf(FooArrayOfAligned) == 2 * 2); + } + + var bytes = [_]u8{0xbb} ** @sizeOf(FooArrayOfAligned); + const ptr = &std.mem.bytesAsSlice(FooArrayOfAligned, bytes[0..])[0]; + + try expect(ptr.a[0].a == 0xbb); + try expect(ptr.a[0].b == 0xbb); + try expect(ptr.a[1].a == 0xbb); + try expect(ptr.a[1].b == 0xbb); +} + +const FooStructAligned = packed struct { + a: u8, + b: u8, +}; + +const FooArrayOfAligned = packed struct { + a: [2]FooStructAligned, +}; + +test "pointer to packed struct member in a stack variable" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = packed struct { + a: u2, + b: u2, + }; + + var s = S{ .a = 2, .b = 0 }; + var b_ptr = &s.b; + try expect(s.b == 0); + b_ptr.* = 2; + try expect(s.b == 2); +} + +test "non-byte-aligned array inside packed struct" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const Foo = packed struct { + a: bool, + b: [0x16]u8, + }; + const S = struct { + fn bar(slice: []const u8) !void { + try expectEqualSlices(u8, slice, "abcdefghijklmnopqurstu"); + } + fn doTheTest() !void { + var foo = Foo{ + .a = true, + .b = "abcdefghijklmnopqurstu".*, + }; + const value = foo.b; + try bar(&value); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "packed struct with u0 field access" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = packed struct { + f0: u0, + }; + var s = S{ .f0 = 0 }; + comptime try expect(s.f0 == 0); +} + +test "access to global struct fields" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + g_foo.bar.value = 42; + try expect(g_foo.bar.value == 42); +} + +const S0 = struct { + bar: S1, + + pub const S1 = struct { + value: u8, + }; + + fn init() @This() { + return S0{ .bar = S1{ .value = 123 } }; + } +}; + +var g_foo: S0 = S0.init(); + +test "packed struct with fp fields" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = packed struct { + data: [3]f32, + + pub fn frob(self: *@This()) void { + self.data[0] += self.data[1] + self.data[2]; + self.data[1] += self.data[0] + self.data[2]; + self.data[2] += self.data[0] + self.data[1]; + } + }; + + var s: S = undefined; + s.data[0] = 1.0; + s.data[1] = 2.0; + s.data[2] = 3.0; + s.frob(); + try expectEqual(@as(f32, 6.0), s.data[0]); + try expectEqual(@as(f32, 11.0), s.data[1]); + try expectEqual(@as(f32, 20.0), s.data[2]); +} + +test "fn with C calling convention returns struct by value" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + fn entry() !void { + var x = makeBar(10); + try expectEqual(@as(i32, 10), x.handle); + } + + const ExternBar = extern struct { + handle: i32, + }; + + fn makeBar(t: i32) callconv(.C) ExternBar { + return ExternBar{ + .handle = t, + }; + } + }; + try S.entry(); + comptime try S.entry(); +} + +test "non-packed struct with u128 entry in union" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const U = union(enum) { + Num: u128, + Void, + }; + + const S = struct { + f1: U, + f2: U, + }; + + var sx: S = undefined; + var s = &sx; + try std.testing.expect(@ptrToInt(&s.f2) - @ptrToInt(&s.f1) == @offsetOf(S, "f2")); + var v2 = U{ .Num = 123 }; + s.f2 = v2; + try std.testing.expect(s.f2.Num == 123); +} + +test "packed struct field passed to generic function" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + const P = packed struct { + b: u5, + g: u5, + r: u5, + a: u1, + }; + + fn genericReadPackedField(ptr: anytype) u5 { + return ptr.*; + } + }; + + var p: S.P = undefined; + p.b = 29; + var loaded = S.genericReadPackedField(&p.b); + try expect(loaded == 29); +} + +test "anonymous struct literal syntax" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const S = struct { + const Point = struct { + x: i32, + y: i32, + }; + + fn doTheTest() !void { + var p: Point = .{ + .x = 1, + .y = 2, + }; + try expect(p.x == 1); + try expect(p.y == 2); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "fully anonymous struct" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + try dump(.{ + .int = @as(u32, 1234), + .float = @as(f64, 12.34), + .b = true, + .s = "hi", + }); + } + fn dump(args: anytype) !void { + try expect(args.int == 1234); + try expect(args.float == 12.34); + try expect(args.b); + try expect(args.s[0] == 'h'); + try expect(args.s[1] == 'i'); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "fully anonymous list literal" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + try dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi" }); + } + fn dump(args: anytype) !void { + try expect(args.@"0" == 1234); + try expect(args.@"1" == 12.34); + try expect(args.@"2"); + try expect(args.@"3"[0] == 'h'); + try expect(args.@"3"[1] == 'i'); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "anonymous struct literal assigned to variable" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + var vec = .{ @as(i32, 22), @as(i32, 55), @as(i32, 99) }; + try expect(vec.@"0" == 22); + try expect(vec.@"1" == 55); + try expect(vec.@"2" == 99); +} + +test "comptime struct field" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const T = struct { + a: i32, + comptime b: i32 = 1234, + }; + + var foo: T = undefined; + comptime try expect(foo.b == 1234); +} + +test "anon struct literal field value initialized with fn call" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var x = .{foo()}; + try expectEqualSlices(u8, x[0], "hi"); + } + fn foo() []const u8 { + return "hi"; + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "struct with union field" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const Value = struct { + ref: u32 = 2, + kind: union(enum) { + None: usize, + Bool: bool, + }, + }; + + var True = Value{ + .kind = .{ .Bool = true }, + }; + try expectEqual(@as(u32, 2), True.ref); + try expectEqual(true, True.kind.Bool); +} + +test "type coercion of anon struct literal to struct" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + const S2 = struct { + A: u32, + B: []const u8, + C: void, + D: Foo = .{}, + }; + + const Foo = struct { + field: i32 = 1234, + }; + + fn doTheTest() !void { + var y: u32 = 42; + const t0 = .{ .A = 123, .B = "foo", .C = {} }; + const t1 = .{ .A = y, .B = "foo", .C = {} }; + const y0: S2 = t0; + var y1: S2 = t1; + try expect(y0.A == 123); + try expect(std.mem.eql(u8, y0.B, "foo")); + try expect(y0.C == {}); + try expect(y0.D.field == 1234); + try expect(y1.A == y); + try expect(std.mem.eql(u8, y1.B, "foo")); + try expect(y1.C == {}); + try expect(y1.D.field == 1234); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "type coercion of pointer to anon struct literal to pointer to struct" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + const S2 = struct { + A: u32, + B: []const u8, + C: void, + D: Foo = .{}, + }; + + const Foo = struct { + field: i32 = 1234, + }; + + fn doTheTest() !void { + var y: u32 = 42; + const t0 = &.{ .A = 123, .B = "foo", .C = {} }; + const t1 = &.{ .A = y, .B = "foo", .C = {} }; + const y0: *const S2 = t0; + var y1: *const S2 = t1; + try expect(y0.A == 123); + try expect(std.mem.eql(u8, y0.B, "foo")); + try expect(y0.C == {}); + try expect(y0.D.field == 1234); + try expect(y1.A == y); + try expect(std.mem.eql(u8, y1.B, "foo")); + try expect(y1.C == {}); + try expect(y1.D.field == 1234); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "packed struct with undefined initializers" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const S = struct { + const P = packed struct { + a: u3, + _a: u3 = undefined, + b: u3, + _b: u3 = undefined, + c: u3, + _c: u3 = undefined, + }; + + fn doTheTest() !void { + var p: P = undefined; + p = P{ .a = 2, .b = 4, .c = 6 }; + // Make sure the compiler doesn't touch the unprefixed fields. + // Use expect since i386-linux doesn't like expectEqual + try expect(p.a == 2); + try expect(p.b == 4); + try expect(p.c == 6); + } + }; + + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "for loop over pointers to struct, getting field from struct pointer" { + // When enabling this test, be careful. I have observed it to pass when compiling + // stage2 alone, but when using stage1 with -fno-stage1 -fLLVM it fails. + // Maybe eyeball the LLVM that it generates and run in valgrind, both the compiler + // and the generated test at runtime. + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + const Foo = struct { + name: []const u8, + }; + + var ok = true; + + fn eql(a: []const u8) bool { + _ = a; + return true; + } + + const ArrayList = struct { + fn toSlice(self: *ArrayList) []*Foo { + _ = self; + return @as([*]*Foo, undefined)[0..0]; + } + }; + + fn doTheTest() !void { + var objects: ArrayList = undefined; + + for (objects.toSlice()) |obj| { + if (eql(obj.name)) { + ok = false; + } + } + + try expect(ok); + } + }; + try S.doTheTest(); +} diff --git a/test/behavior/struct_llvm.zig b/test/behavior/struct_llvm.zig deleted file mode 100644 index eef7b16487..0000000000 --- a/test/behavior/struct_llvm.zig +++ /dev/null @@ -1,802 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const native_endian = builtin.target.cpu.arch.endian(); -const expect = std.testing.expect; -const expectEqual = std.testing.expectEqual; -const expectEqualSlices = std.testing.expectEqualSlices; -const maxInt = std.math.maxInt; - -const Node = struct { - val: Val, - next: *Node, -}; - -const Val = struct { - x: i32, -}; - -test "struct point to self" { - var root: Node = undefined; - root.val.x = 1; - - var node: Node = undefined; - node.next = &root; - node.val.x = 2; - - root.next = &node; - - try expect(node.next.next.next.val.x == 1); -} - -test "void struct fields" { - const foo = VoidStructFieldsFoo{ - .a = void{}, - .b = 1, - .c = void{}, - }; - try expect(foo.b == 1); - try expect(@sizeOf(VoidStructFieldsFoo) == 4); -} -const VoidStructFieldsFoo = struct { - a: void, - b: i32, - c: void, -}; - -test "return empty struct from fn" { - _ = testReturnEmptyStructFromFn(); -} -const EmptyStruct2 = struct {}; -fn testReturnEmptyStructFromFn() EmptyStruct2 { - return EmptyStruct2{}; -} - -test "pass slice of empty struct to fn" { - try expect(testPassSliceOfEmptyStructToFn(&[_]EmptyStruct2{EmptyStruct2{}}) == 1); -} -fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { - return slice.len; -} - -test "self-referencing struct via array member" { - const T = struct { - children: [1]*@This(), - }; - var x: T = undefined; - x = T{ .children = .{&x} }; - try expect(x.children[0] == &x); -} - -test "empty struct method call" { - const es = EmptyStruct{}; - try expect(es.method() == 1234); -} -const EmptyStruct = struct { - fn method(es: *const EmptyStruct) i32 { - _ = es; - return 1234; - } -}; - -test "align 1 field before self referential align 8 field as slice return type" { - const result = alloc(Expr); - try expect(result.len == 0); -} - -const Expr = union(enum) { - Literal: u8, - Question: *Expr, -}; - -fn alloc(comptime T: type) []T { - return &[_]T{}; -} - -const APackedStruct = packed struct { - x: u8, - y: u8, -}; - -test "packed struct" { - var foo = APackedStruct{ - .x = 1, - .y = 2, - }; - foo.y += 1; - const four = foo.x + foo.y; - try expect(four == 4); -} - -const Foo24Bits = packed struct { - field: u24, -}; -const Foo96Bits = packed struct { - a: u24, - b: u24, - c: u24, - d: u24, -}; - -test "packed struct 24bits" { - comptime { - try expect(@sizeOf(Foo24Bits) == 4); - if (@sizeOf(usize) == 4) { - try expect(@sizeOf(Foo96Bits) == 12); - } else { - try expect(@sizeOf(Foo96Bits) == 16); - } - } - - var value = Foo96Bits{ - .a = 0, - .b = 0, - .c = 0, - .d = 0, - }; - value.a += 1; - try expect(value.a == 1); - try expect(value.b == 0); - try expect(value.c == 0); - try expect(value.d == 0); - - value.b += 1; - try expect(value.a == 1); - try expect(value.b == 1); - try expect(value.c == 0); - try expect(value.d == 0); - - value.c += 1; - try expect(value.a == 1); - try expect(value.b == 1); - try expect(value.c == 1); - try expect(value.d == 0); - - value.d += 1; - try expect(value.a == 1); - try expect(value.b == 1); - try expect(value.c == 1); - try expect(value.d == 1); -} - -test "runtime struct initialization of bitfield" { - const s1 = Nibbles{ - .x = x1, - .y = x1, - }; - const s2 = Nibbles{ - .x = @intCast(u4, x2), - .y = @intCast(u4, x2), - }; - - try expect(s1.x == x1); - try expect(s1.y == x1); - try expect(s2.x == @intCast(u4, x2)); - try expect(s2.y == @intCast(u4, x2)); -} - -var x1 = @as(u4, 1); -var x2 = @as(u8, 2); - -const Nibbles = packed struct { - x: u4, - y: u4, -}; - -const Bitfields = packed struct { - f1: u16, - f2: u16, - f3: u8, - f4: u8, - f5: u4, - f6: u4, - f7: u8, -}; - -test "native bit field understands endianness" { - var all: u64 = if (native_endian != .Little) - 0x1111222233445677 - else - 0x7765443322221111; - var bytes: [8]u8 = undefined; - @memcpy(&bytes, @ptrCast([*]u8, &all), 8); - var bitfields = @ptrCast(*Bitfields, &bytes).*; - - try expect(bitfields.f1 == 0x1111); - try expect(bitfields.f2 == 0x2222); - try expect(bitfields.f3 == 0x33); - try expect(bitfields.f4 == 0x44); - try expect(bitfields.f5 == 0x5); - try expect(bitfields.f6 == 0x6); - try expect(bitfields.f7 == 0x77); -} - -test "implicit cast packed struct field to const ptr" { - const LevelUpMove = packed struct { - move_id: u9, - level: u7, - - fn toInt(value: u7) u7 { - return value; - } - }; - - var lup: LevelUpMove = undefined; - lup.level = 12; - const res = LevelUpMove.toInt(lup.level); - try expect(res == 12); -} - -test "zero-bit field in packed struct" { - const S = packed struct { - x: u10, - y: void, - }; - var x: S = undefined; - _ = x; -} - -test "packed struct with non-ABI-aligned field" { - const S = packed struct { - x: u9, - y: u183, - }; - var s: S = undefined; - s.x = 1; - s.y = 42; - try expect(s.x == 1); - try expect(s.y == 42); -} - -const BitField1 = packed struct { - a: u3, - b: u3, - c: u2, -}; - -const bit_field_1 = BitField1{ - .a = 1, - .b = 2, - .c = 3, -}; - -test "bit field access" { - var data = bit_field_1; - try expect(getA(&data) == 1); - try expect(getB(&data) == 2); - try expect(getC(&data) == 3); - comptime try expect(@sizeOf(BitField1) == 1); - - data.b += 1; - try expect(data.b == 3); - - data.a += 1; - try expect(data.a == 2); - try expect(data.b == 3); -} - -fn getA(data: *const BitField1) u3 { - return data.a; -} - -fn getB(data: *const BitField1) u3 { - return data.b; -} - -fn getC(data: *const BitField1) u2 { - return data.c; -} - -test "default struct initialization fields" { - const S = struct { - a: i32 = 1234, - b: i32, - }; - const x = S{ - .b = 5, - }; - var five: i32 = 5; - const y = S{ - .b = five, - }; - if (x.a + x.b != 1239) { - @compileError("it should be comptime known"); - } - try expect(y.a == x.a); - try expect(y.b == x.b); - try expect(1239 == x.a + x.b); -} - -// TODO revisit this test when doing https://github.com/ziglang/zig/issues/1512 -test "packed array 24bits" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - comptime { - try expect(@sizeOf([9]Foo32Bits) == 9 * 4); - try expect(@sizeOf(FooArray24Bits) == 2 + 2 * 4 + 2); - } - - var bytes = [_]u8{0} ** (@sizeOf(FooArray24Bits) + 1); - bytes[bytes.len - 1] = 0xaa; - const ptr = &std.mem.bytesAsSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0]; - try expect(ptr.a == 0); - try expect(ptr.b[0].field == 0); - try expect(ptr.b[1].field == 0); - try expect(ptr.c == 0); - - ptr.a = maxInt(u16); - try expect(ptr.a == maxInt(u16)); - try expect(ptr.b[0].field == 0); - try expect(ptr.b[1].field == 0); - try expect(ptr.c == 0); - - ptr.b[0].field = maxInt(u24); - try expect(ptr.a == maxInt(u16)); - try expect(ptr.b[0].field == maxInt(u24)); - try expect(ptr.b[1].field == 0); - try expect(ptr.c == 0); - - ptr.b[1].field = maxInt(u24); - try expect(ptr.a == maxInt(u16)); - try expect(ptr.b[0].field == maxInt(u24)); - try expect(ptr.b[1].field == maxInt(u24)); - try expect(ptr.c == 0); - - ptr.c = maxInt(u16); - try expect(ptr.a == maxInt(u16)); - try expect(ptr.b[0].field == maxInt(u24)); - try expect(ptr.b[1].field == maxInt(u24)); - try expect(ptr.c == maxInt(u16)); - - try expect(bytes[bytes.len - 1] == 0xaa); -} - -const Foo32Bits = packed struct { - field: u24, - pad: u8, -}; - -const FooArray24Bits = packed struct { - a: u16, - b: [2]Foo32Bits, - c: u16, -}; - -test "aligned array of packed struct" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - comptime { - try expect(@sizeOf(FooStructAligned) == 2); - try expect(@sizeOf(FooArrayOfAligned) == 2 * 2); - } - - var bytes = [_]u8{0xbb} ** @sizeOf(FooArrayOfAligned); - const ptr = &std.mem.bytesAsSlice(FooArrayOfAligned, bytes[0..])[0]; - - try expect(ptr.a[0].a == 0xbb); - try expect(ptr.a[0].b == 0xbb); - try expect(ptr.a[1].a == 0xbb); - try expect(ptr.a[1].b == 0xbb); -} - -const FooStructAligned = packed struct { - a: u8, - b: u8, -}; - -const FooArrayOfAligned = packed struct { - a: [2]FooStructAligned, -}; - -test "pointer to packed struct member in a stack variable" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const S = packed struct { - a: u2, - b: u2, - }; - - var s = S{ .a = 2, .b = 0 }; - var b_ptr = &s.b; - try expect(s.b == 0); - b_ptr.* = 2; - try expect(s.b == 2); -} - -test "non-byte-aligned array inside packed struct" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const Foo = packed struct { - a: bool, - b: [0x16]u8, - }; - const S = struct { - fn bar(slice: []const u8) !void { - try expectEqualSlices(u8, slice, "abcdefghijklmnopqurstu"); - } - fn doTheTest() !void { - var foo = Foo{ - .a = true, - .b = "abcdefghijklmnopqurstu".*, - }; - const value = foo.b; - try bar(&value); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "packed struct with u0 field access" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const S = packed struct { - f0: u0, - }; - var s = S{ .f0 = 0 }; - comptime try expect(s.f0 == 0); -} - -test "access to global struct fields" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - g_foo.bar.value = 42; - try expect(g_foo.bar.value == 42); -} - -const S0 = struct { - bar: S1, - - pub const S1 = struct { - value: u8, - }; - - fn init() @This() { - return S0{ .bar = S1{ .value = 123 } }; - } -}; - -var g_foo: S0 = S0.init(); - -test "packed struct with fp fields" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const S = packed struct { - data: [3]f32, - - pub fn frob(self: *@This()) void { - self.data[0] += self.data[1] + self.data[2]; - self.data[1] += self.data[0] + self.data[2]; - self.data[2] += self.data[0] + self.data[1]; - } - }; - - var s: S = undefined; - s.data[0] = 1.0; - s.data[1] = 2.0; - s.data[2] = 3.0; - s.frob(); - try expectEqual(@as(f32, 6.0), s.data[0]); - try expectEqual(@as(f32, 11.0), s.data[1]); - try expectEqual(@as(f32, 20.0), s.data[2]); -} - -test "fn with C calling convention returns struct by value" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const S = struct { - fn entry() !void { - var x = makeBar(10); - try expectEqual(@as(i32, 10), x.handle); - } - - const ExternBar = extern struct { - handle: i32, - }; - - fn makeBar(t: i32) callconv(.C) ExternBar { - return ExternBar{ - .handle = t, - }; - } - }; - try S.entry(); - comptime try S.entry(); -} - -test "non-packed struct with u128 entry in union" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const U = union(enum) { - Num: u128, - Void, - }; - - const S = struct { - f1: U, - f2: U, - }; - - var sx: S = undefined; - var s = &sx; - try std.testing.expect(@ptrToInt(&s.f2) - @ptrToInt(&s.f1) == @offsetOf(S, "f2")); - var v2 = U{ .Num = 123 }; - s.f2 = v2; - try std.testing.expect(s.f2.Num == 123); -} - -test "packed struct field passed to generic function" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const S = struct { - const P = packed struct { - b: u5, - g: u5, - r: u5, - a: u1, - }; - - fn genericReadPackedField(ptr: anytype) u5 { - return ptr.*; - } - }; - - var p: S.P = undefined; - p.b = 29; - var loaded = S.genericReadPackedField(&p.b); - try expect(loaded == 29); -} - -test "anonymous struct literal syntax" { - const S = struct { - const Point = struct { - x: i32, - y: i32, - }; - - fn doTheTest() !void { - var p: Point = .{ - .x = 1, - .y = 2, - }; - try expect(p.x == 1); - try expect(p.y == 2); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "fully anonymous struct" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const S = struct { - fn doTheTest() !void { - try dump(.{ - .int = @as(u32, 1234), - .float = @as(f64, 12.34), - .b = true, - .s = "hi", - }); - } - fn dump(args: anytype) !void { - try expect(args.int == 1234); - try expect(args.float == 12.34); - try expect(args.b); - try expect(args.s[0] == 'h'); - try expect(args.s[1] == 'i'); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "fully anonymous list literal" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const S = struct { - fn doTheTest() !void { - try dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi" }); - } - fn dump(args: anytype) !void { - try expect(args.@"0" == 1234); - try expect(args.@"1" == 12.34); - try expect(args.@"2"); - try expect(args.@"3"[0] == 'h'); - try expect(args.@"3"[1] == 'i'); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "anonymous struct literal assigned to variable" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - var vec = .{ @as(i32, 22), @as(i32, 55), @as(i32, 99) }; - try expect(vec.@"0" == 22); - try expect(vec.@"1" == 55); - try expect(vec.@"2" == 99); -} - -test "comptime struct field" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const T = struct { - a: i32, - comptime b: i32 = 1234, - }; - - var foo: T = undefined; - comptime try expect(foo.b == 1234); -} - -test "anon struct literal field value initialized with fn call" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const S = struct { - fn doTheTest() !void { - var x = .{foo()}; - try expectEqualSlices(u8, x[0], "hi"); - } - fn foo() []const u8 { - return "hi"; - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "struct with union field" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const Value = struct { - ref: u32 = 2, - kind: union(enum) { - None: usize, - Bool: bool, - }, - }; - - var True = Value{ - .kind = .{ .Bool = true }, - }; - try expectEqual(@as(u32, 2), True.ref); - try expectEqual(true, True.kind.Bool); -} - -test "type coercion of anon struct literal to struct" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const S = struct { - const S2 = struct { - A: u32, - B: []const u8, - C: void, - D: Foo = .{}, - }; - - const Foo = struct { - field: i32 = 1234, - }; - - fn doTheTest() !void { - var y: u32 = 42; - const t0 = .{ .A = 123, .B = "foo", .C = {} }; - const t1 = .{ .A = y, .B = "foo", .C = {} }; - const y0: S2 = t0; - var y1: S2 = t1; - try expect(y0.A == 123); - try expect(std.mem.eql(u8, y0.B, "foo")); - try expect(y0.C == {}); - try expect(y0.D.field == 1234); - try expect(y1.A == y); - try expect(std.mem.eql(u8, y1.B, "foo")); - try expect(y1.C == {}); - try expect(y1.D.field == 1234); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "type coercion of pointer to anon struct literal to pointer to struct" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const S = struct { - const S2 = struct { - A: u32, - B: []const u8, - C: void, - D: Foo = .{}, - }; - - const Foo = struct { - field: i32 = 1234, - }; - - fn doTheTest() !void { - var y: u32 = 42; - const t0 = &.{ .A = 123, .B = "foo", .C = {} }; - const t1 = &.{ .A = y, .B = "foo", .C = {} }; - const y0: *const S2 = t0; - var y1: *const S2 = t1; - try expect(y0.A == 123); - try expect(std.mem.eql(u8, y0.B, "foo")); - try expect(y0.C == {}); - try expect(y0.D.field == 1234); - try expect(y1.A == y); - try expect(std.mem.eql(u8, y1.B, "foo")); - try expect(y1.C == {}); - try expect(y1.D.field == 1234); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "packed struct with undefined initializers" { - const S = struct { - const P = packed struct { - a: u3, - _a: u3 = undefined, - b: u3, - _b: u3 = undefined, - c: u3, - _c: u3 = undefined, - }; - - fn doTheTest() !void { - var p: P = undefined; - p = P{ .a = 2, .b = 4, .c = 6 }; - // Make sure the compiler doesn't touch the unprefixed fields. - // Use expect since i386-linux doesn't like expectEqual - try expect(p.a == 2); - try expect(p.b == 4); - try expect(p.c == 6); - } - }; - - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "for loop over pointers to struct, getting field from struct pointer" { - // When enabling this test, be careful. I have observed it to pass when compiling - // stage2 alone, but when using stage1 with -fno-stage1 -fLLVM it fails. - // Maybe eyeball the LLVM that it generates and run in valgrind, both the compiler - // and the generated test at runtime. - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const S = struct { - const Foo = struct { - name: []const u8, - }; - - var ok = true; - - fn eql(a: []const u8) bool { - _ = a; - return true; - } - - const ArrayList = struct { - fn toSlice(self: *ArrayList) []*Foo { - _ = self; - return @as([*]*Foo, undefined)[0..0]; - } - }; - - fn doTheTest() !void { - var objects: ArrayList = undefined; - - for (objects.toSlice()) |obj| { - if (eql(obj.name)) { - ok = false; - } - } - - try expect(ok); - } - }; - try S.doTheTest(); -} diff --git a/test/behavior/truncate.zig b/test/behavior/truncate.zig index ae3e11c929..001ba538b2 100644 --- a/test/behavior/truncate.zig +++ b/test/behavior/truncate.zig @@ -76,3 +76,16 @@ test "truncate on comptime integer" { var w = @truncate(u1, 1 << 100); try expect(w == 0); } + +test "truncate on vectors" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; + + const S = struct { + fn doTheTest() !void { + var v1: @Vector(4, u16) = .{ 0xaabb, 0xccdd, 0xeeff, 0x1122 }; + var v2 = @truncate(u8, v1); + try expect(std.mem.eql(u8, &@as([4]u8, v2), &[4]u8{ 0xbb, 0xdd, 0xff, 0x22 })); + } + }; + try S.doTheTest(); +} diff --git a/test/behavior/truncate_stage1.zig b/test/behavior/truncate_stage1.zig deleted file mode 100644 index 5c66085cbb..0000000000 --- a/test/behavior/truncate_stage1.zig +++ /dev/null @@ -1,13 +0,0 @@ -const std = @import("std"); -const expect = std.testing.expect; - -test "truncate on vectors" { - const S = struct { - fn doTheTest() !void { - var v1: @Vector(4, u16) = .{ 0xaabb, 0xccdd, 0xeeff, 0x1122 }; - var v2 = @truncate(u8, v1); - try expect(std.mem.eql(u8, &@as([4]u8, v2), &[4]u8{ 0xbb, 0xdd, 0xff, 0x22 })); - } - }; - try S.doTheTest(); -} diff --git a/test/behavior/type.zig b/test/behavior/type.zig index cb72f86b8b..60d4463873 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -102,3 +102,389 @@ test "Type.Pointer" { [*c]align(8) volatile u8, [*c]align(8) const volatile u8, }); } + +test "Type.Float" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + try testing.expect(f16 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 16 } })); + try testing.expect(f32 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 32 } })); + try testing.expect(f64 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 64 } })); + try testing.expect(f80 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 80 } })); + try testing.expect(f128 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 128 } })); + try testTypes(&[_]type{ f16, f32, f64, f80, f128 }); +} + +test "Type.Array" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + try testing.expect([123]u8 == @Type(TypeInfo{ + .Array = TypeInfo.Array{ + .len = 123, + .child = u8, + .sentinel = null, + }, + })); + try testing.expect([2]u32 == @Type(TypeInfo{ + .Array = TypeInfo.Array{ + .len = 2, + .child = u32, + .sentinel = null, + }, + })); + try testing.expect([2:0]u32 == @Type(TypeInfo{ + .Array = TypeInfo.Array{ + .len = 2, + .child = u32, + .sentinel = &@as(u32, 0), + }, + })); + try testTypes(&[_]type{ [1]u8, [30]usize, [7]bool }); +} + +test "@Type create slice with null sentinel" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const Slice = @Type(TypeInfo{ + .Pointer = .{ + .size = .Slice, + .is_const = true, + .is_volatile = false, + .is_allowzero = false, + .alignment = 8, + .address_space = .generic, + .child = *i32, + .sentinel = null, + }, + }); + try testing.expect(Slice == []align(8) const *i32); +} + +test "@Type picks up the sentinel value from TypeInfo" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + try testTypes(&[_]type{ + [11:0]u8, [4:10]u8, + [*:0]u8, [*:0]const u8, + [*:0]volatile u8, [*:0]const volatile u8, + [*:0]align(4) u8, [*:0]align(4) const u8, + [*:0]align(4) volatile u8, [*:0]align(4) const volatile u8, + [*:0]align(8) u8, [*:0]align(8) const u8, + [*:0]align(8) volatile u8, [*:0]align(8) const volatile u8, + [*:0]allowzero u8, [*:0]allowzero const u8, + [*:0]allowzero volatile u8, [*:0]allowzero const volatile u8, + [*:0]allowzero align(4) u8, [*:0]allowzero align(4) const u8, + [*:0]allowzero align(4) volatile u8, [*:0]allowzero align(4) const volatile u8, + [*:5]allowzero align(4) volatile u8, [*:5]allowzero align(4) const volatile u8, + [:0]u8, [:0]const u8, + [:0]volatile u8, [:0]const volatile u8, + [:0]align(4) u8, [:0]align(4) const u8, + [:0]align(4) volatile u8, [:0]align(4) const volatile u8, + [:0]align(8) u8, [:0]align(8) const u8, + [:0]align(8) volatile u8, [:0]align(8) const volatile u8, + [:0]allowzero u8, [:0]allowzero const u8, + [:0]allowzero volatile u8, [:0]allowzero const volatile u8, + [:0]allowzero align(4) u8, [:0]allowzero align(4) const u8, + [:0]allowzero align(4) volatile u8, [:0]allowzero align(4) const volatile u8, + [:4]allowzero align(4) volatile u8, [:4]allowzero align(4) const volatile u8, + }); +} + +test "Type.Optional" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + try testTypes(&[_]type{ + ?u8, + ?*u8, + ?[]u8, + ?[*]u8, + ?[*c]u8, + }); +} + +test "Type.ErrorUnion" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + try testTypes(&[_]type{ + error{}!void, + error{Error}!void, + }); +} + +test "Type.Opaque" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const Opaque = @Type(.{ + .Opaque = .{ + .decls = &[_]TypeInfo.Declaration{}, + }, + }); + try testing.expect(Opaque != opaque {}); + try testing.expectEqualSlices( + TypeInfo.Declaration, + &[_]TypeInfo.Declaration{}, + @typeInfo(Opaque).Opaque.decls, + ); +} + +test "Type.Vector" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + try testTypes(&[_]type{ + @Vector(0, u8), + @Vector(4, u8), + @Vector(8, *u8), + std.meta.Vector(0, u8), + std.meta.Vector(4, u8), + std.meta.Vector(8, *u8), + }); +} + +test "Type.AnyFrame" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + try testTypes(&[_]type{ + anyframe, + anyframe->u8, + anyframe->anyframe->u8, + }); +} + +fn add(a: i32, b: i32) i32 { + return a + b; +} + +test "Type.ErrorSet" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + // error sets don't compare equal so just check if they compile + _ = @Type(@typeInfo(error{})); + _ = @Type(@typeInfo(error{A})); + _ = @Type(@typeInfo(error{ A, B, C })); +} + +test "Type.Struct" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const A = @Type(@typeInfo(struct { x: u8, y: u32 })); + const infoA = @typeInfo(A).Struct; + try testing.expectEqual(TypeInfo.ContainerLayout.Auto, infoA.layout); + try testing.expectEqualSlices(u8, "x", infoA.fields[0].name); + try testing.expectEqual(u8, infoA.fields[0].field_type); + try testing.expectEqual(@as(?*const anyopaque, null), infoA.fields[0].default_value); + try testing.expectEqualSlices(u8, "y", infoA.fields[1].name); + try testing.expectEqual(u32, infoA.fields[1].field_type); + try testing.expectEqual(@as(?*const anyopaque, null), infoA.fields[1].default_value); + try testing.expectEqualSlices(TypeInfo.Declaration, &[_]TypeInfo.Declaration{}, infoA.decls); + try testing.expectEqual(@as(bool, false), infoA.is_tuple); + + var a = A{ .x = 0, .y = 1 }; + try testing.expectEqual(@as(u8, 0), a.x); + try testing.expectEqual(@as(u32, 1), a.y); + a.y += 1; + try testing.expectEqual(@as(u32, 2), a.y); + + const B = @Type(@typeInfo(extern struct { x: u8, y: u32 = 5 })); + const infoB = @typeInfo(B).Struct; + try testing.expectEqual(TypeInfo.ContainerLayout.Extern, infoB.layout); + try testing.expectEqualSlices(u8, "x", infoB.fields[0].name); + try testing.expectEqual(u8, infoB.fields[0].field_type); + try testing.expectEqual(@as(?*const anyopaque, null), infoB.fields[0].default_value); + try testing.expectEqualSlices(u8, "y", infoB.fields[1].name); + try testing.expectEqual(u32, infoB.fields[1].field_type); + try testing.expectEqual(@as(u32, 5), @ptrCast(*const u32, infoB.fields[1].default_value.?).*); + try testing.expectEqual(@as(usize, 0), infoB.decls.len); + try testing.expectEqual(@as(bool, false), infoB.is_tuple); + + const C = @Type(@typeInfo(packed struct { x: u8 = 3, y: u32 = 5 })); + const infoC = @typeInfo(C).Struct; + try testing.expectEqual(TypeInfo.ContainerLayout.Packed, infoC.layout); + try testing.expectEqualSlices(u8, "x", infoC.fields[0].name); + try testing.expectEqual(u8, infoC.fields[0].field_type); + try testing.expectEqual(@as(u8, 3), @ptrCast(*const u8, infoC.fields[0].default_value.?).*); + try testing.expectEqualSlices(u8, "y", infoC.fields[1].name); + try testing.expectEqual(u32, infoC.fields[1].field_type); + try testing.expectEqual(@as(u32, 5), @ptrCast(*const u32, infoC.fields[1].default_value.?).*); + try testing.expectEqual(@as(usize, 0), infoC.decls.len); + try testing.expectEqual(@as(bool, false), infoC.is_tuple); +} + +test "Type.Enum" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const Foo = @Type(.{ + .Enum = .{ + .layout = .Auto, + .tag_type = u8, + .fields = &[_]TypeInfo.EnumField{ + .{ .name = "a", .value = 1 }, + .{ .name = "b", .value = 5 }, + }, + .decls = &[_]TypeInfo.Declaration{}, + .is_exhaustive = true, + }, + }); + try testing.expectEqual(true, @typeInfo(Foo).Enum.is_exhaustive); + try testing.expectEqual(@as(u8, 1), @enumToInt(Foo.a)); + try testing.expectEqual(@as(u8, 5), @enumToInt(Foo.b)); + const Bar = @Type(.{ + .Enum = .{ + .layout = .Extern, + .tag_type = u32, + .fields = &[_]TypeInfo.EnumField{ + .{ .name = "a", .value = 1 }, + .{ .name = "b", .value = 5 }, + }, + .decls = &[_]TypeInfo.Declaration{}, + .is_exhaustive = false, + }, + }); + try testing.expectEqual(false, @typeInfo(Bar).Enum.is_exhaustive); + try testing.expectEqual(@as(u32, 1), @enumToInt(Bar.a)); + try testing.expectEqual(@as(u32, 5), @enumToInt(Bar.b)); + try testing.expectEqual(@as(u32, 6), @enumToInt(@intToEnum(Bar, 6))); +} + +test "Type.Union" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const Untagged = @Type(.{ + .Union = .{ + .layout = .Auto, + .tag_type = null, + .fields = &[_]TypeInfo.UnionField{ + .{ .name = "int", .field_type = i32, .alignment = @alignOf(f32) }, + .{ .name = "float", .field_type = f32, .alignment = @alignOf(f32) }, + }, + .decls = &[_]TypeInfo.Declaration{}, + }, + }); + var untagged = Untagged{ .int = 1 }; + untagged.float = 2.0; + untagged.int = 3; + try testing.expectEqual(@as(i32, 3), untagged.int); + + const PackedUntagged = @Type(.{ + .Union = .{ + .layout = .Packed, + .tag_type = null, + .fields = &[_]TypeInfo.UnionField{ + .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) }, + .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) }, + }, + .decls = &[_]TypeInfo.Declaration{}, + }, + }); + var packed_untagged = PackedUntagged{ .signed = -1 }; + try testing.expectEqual(@as(i32, -1), packed_untagged.signed); + try testing.expectEqual(~@as(u32, 0), packed_untagged.unsigned); + + const Tag = @Type(.{ + .Enum = .{ + .layout = .Auto, + .tag_type = u1, + .fields = &[_]TypeInfo.EnumField{ + .{ .name = "signed", .value = 0 }, + .{ .name = "unsigned", .value = 1 }, + }, + .decls = &[_]TypeInfo.Declaration{}, + .is_exhaustive = true, + }, + }); + const Tagged = @Type(.{ + .Union = .{ + .layout = .Auto, + .tag_type = Tag, + .fields = &[_]TypeInfo.UnionField{ + .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) }, + .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) }, + }, + .decls = &[_]TypeInfo.Declaration{}, + }, + }); + var tagged = Tagged{ .signed = -1 }; + try testing.expectEqual(Tag.signed, tagged); + tagged = .{ .unsigned = 1 }; + try testing.expectEqual(Tag.unsigned, tagged); +} + +test "Type.Union from Type.Enum" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const Tag = @Type(.{ + .Enum = .{ + .layout = .Auto, + .tag_type = u0, + .fields = &[_]TypeInfo.EnumField{ + .{ .name = "working_as_expected", .value = 0 }, + }, + .decls = &[_]TypeInfo.Declaration{}, + .is_exhaustive = true, + }, + }); + const T = @Type(.{ + .Union = .{ + .layout = .Auto, + .tag_type = Tag, + .fields = &[_]TypeInfo.UnionField{ + .{ .name = "working_as_expected", .field_type = u32, .alignment = @alignOf(u32) }, + }, + .decls = &[_]TypeInfo.Declaration{}, + }, + }); + _ = T; + _ = @typeInfo(T).Union; +} + +test "Type.Union from regular enum" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const E = enum { working_as_expected }; + const T = @Type(.{ + .Union = .{ + .layout = .Auto, + .tag_type = E, + .fields = &[_]TypeInfo.UnionField{ + .{ .name = "working_as_expected", .field_type = u32, .alignment = @alignOf(u32) }, + }, + .decls = &[_]TypeInfo.Declaration{}, + }, + }); + _ = T; + _ = @typeInfo(T).Union; +} + +test "Type.Fn" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + // wasm doesn't support align attributes on functions + if (builtin.target.cpu.arch == .wasm32 or builtin.target.cpu.arch == .wasm64) return error.SkipZigTest; + + const foo = struct { + fn func(a: usize, b: bool) align(4) callconv(.C) usize { + _ = a; + _ = b; + return 0; + } + }.func; + const Foo = @Type(@typeInfo(@TypeOf(foo))); + const foo_2: Foo = foo; + _ = foo_2; +} + +test "Type.BoundFn" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + // wasm doesn't support align attributes on functions + if (builtin.target.cpu.arch == .wasm32 or builtin.target.cpu.arch == .wasm64) return error.SkipZigTest; + + const TestStruct = packed struct { + pub fn foo(self: *const @This()) align(4) callconv(.Unspecified) void { + _ = self; + } + }; + const test_instance: TestStruct = undefined; + try testing.expect(std.meta.eql( + @typeName(@TypeOf(test_instance.foo)), + @typeName(@Type(@typeInfo(@TypeOf(test_instance.foo)))), + )); +} diff --git a/test/behavior/type_stage1.zig b/test/behavior/type_stage1.zig deleted file mode 100644 index 852c9fc82d..0000000000 --- a/test/behavior/type_stage1.zig +++ /dev/null @@ -1,362 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const TypeInfo = std.builtin.TypeInfo; -const testing = std.testing; - -fn testTypes(comptime types: []const type) !void { - inline for (types) |testType| { - try testing.expect(testType == @Type(@typeInfo(testType))); - } -} - -test "Type.Float" { - try testing.expect(f16 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 16 } })); - try testing.expect(f32 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 32 } })); - try testing.expect(f64 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 64 } })); - try testing.expect(f80 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 80 } })); - try testing.expect(f128 == @Type(TypeInfo{ .Float = TypeInfo.Float{ .bits = 128 } })); - try testTypes(&[_]type{ f16, f32, f64, f80, f128 }); -} - -test "Type.Array" { - try testing.expect([123]u8 == @Type(TypeInfo{ - .Array = TypeInfo.Array{ - .len = 123, - .child = u8, - .sentinel = null, - }, - })); - try testing.expect([2]u32 == @Type(TypeInfo{ - .Array = TypeInfo.Array{ - .len = 2, - .child = u32, - .sentinel = null, - }, - })); - try testing.expect([2:0]u32 == @Type(TypeInfo{ - .Array = TypeInfo.Array{ - .len = 2, - .child = u32, - .sentinel = &@as(u32, 0), - }, - })); - try testTypes(&[_]type{ [1]u8, [30]usize, [7]bool }); -} - -test "@Type create slice with null sentinel" { - const Slice = @Type(TypeInfo{ - .Pointer = .{ - .size = .Slice, - .is_const = true, - .is_volatile = false, - .is_allowzero = false, - .alignment = 8, - .address_space = .generic, - .child = *i32, - .sentinel = null, - }, - }); - try testing.expect(Slice == []align(8) const *i32); -} - -test "@Type picks up the sentinel value from TypeInfo" { - try testTypes(&[_]type{ - [11:0]u8, [4:10]u8, - [*:0]u8, [*:0]const u8, - [*:0]volatile u8, [*:0]const volatile u8, - [*:0]align(4) u8, [*:0]align(4) const u8, - [*:0]align(4) volatile u8, [*:0]align(4) const volatile u8, - [*:0]align(8) u8, [*:0]align(8) const u8, - [*:0]align(8) volatile u8, [*:0]align(8) const volatile u8, - [*:0]allowzero u8, [*:0]allowzero const u8, - [*:0]allowzero volatile u8, [*:0]allowzero const volatile u8, - [*:0]allowzero align(4) u8, [*:0]allowzero align(4) const u8, - [*:0]allowzero align(4) volatile u8, [*:0]allowzero align(4) const volatile u8, - [*:5]allowzero align(4) volatile u8, [*:5]allowzero align(4) const volatile u8, - [:0]u8, [:0]const u8, - [:0]volatile u8, [:0]const volatile u8, - [:0]align(4) u8, [:0]align(4) const u8, - [:0]align(4) volatile u8, [:0]align(4) const volatile u8, - [:0]align(8) u8, [:0]align(8) const u8, - [:0]align(8) volatile u8, [:0]align(8) const volatile u8, - [:0]allowzero u8, [:0]allowzero const u8, - [:0]allowzero volatile u8, [:0]allowzero const volatile u8, - [:0]allowzero align(4) u8, [:0]allowzero align(4) const u8, - [:0]allowzero align(4) volatile u8, [:0]allowzero align(4) const volatile u8, - [:4]allowzero align(4) volatile u8, [:4]allowzero align(4) const volatile u8, - }); -} - -test "Type.Optional" { - try testTypes(&[_]type{ - ?u8, - ?*u8, - ?[]u8, - ?[*]u8, - ?[*c]u8, - }); -} - -test "Type.ErrorUnion" { - try testTypes(&[_]type{ - error{}!void, - error{Error}!void, - }); -} - -test "Type.Opaque" { - const Opaque = @Type(.{ - .Opaque = .{ - .decls = &[_]TypeInfo.Declaration{}, - }, - }); - try testing.expect(Opaque != opaque {}); - try testing.expectEqualSlices( - TypeInfo.Declaration, - &[_]TypeInfo.Declaration{}, - @typeInfo(Opaque).Opaque.decls, - ); -} - -test "Type.Vector" { - try testTypes(&[_]type{ - @Vector(0, u8), - @Vector(4, u8), - @Vector(8, *u8), - std.meta.Vector(0, u8), - std.meta.Vector(4, u8), - std.meta.Vector(8, *u8), - }); -} - -test "Type.AnyFrame" { - try testTypes(&[_]type{ - anyframe, - anyframe->u8, - anyframe->anyframe->u8, - }); -} - -fn add(a: i32, b: i32) i32 { - return a + b; -} - -test "Type.ErrorSet" { - // error sets don't compare equal so just check if they compile - _ = @Type(@typeInfo(error{})); - _ = @Type(@typeInfo(error{A})); - _ = @Type(@typeInfo(error{ A, B, C })); -} - -test "Type.Struct" { - const A = @Type(@typeInfo(struct { x: u8, y: u32 })); - const infoA = @typeInfo(A).Struct; - try testing.expectEqual(TypeInfo.ContainerLayout.Auto, infoA.layout); - try testing.expectEqualSlices(u8, "x", infoA.fields[0].name); - try testing.expectEqual(u8, infoA.fields[0].field_type); - try testing.expectEqual(@as(?*const anyopaque, null), infoA.fields[0].default_value); - try testing.expectEqualSlices(u8, "y", infoA.fields[1].name); - try testing.expectEqual(u32, infoA.fields[1].field_type); - try testing.expectEqual(@as(?*const anyopaque, null), infoA.fields[1].default_value); - try testing.expectEqualSlices(TypeInfo.Declaration, &[_]TypeInfo.Declaration{}, infoA.decls); - try testing.expectEqual(@as(bool, false), infoA.is_tuple); - - var a = A{ .x = 0, .y = 1 }; - try testing.expectEqual(@as(u8, 0), a.x); - try testing.expectEqual(@as(u32, 1), a.y); - a.y += 1; - try testing.expectEqual(@as(u32, 2), a.y); - - const B = @Type(@typeInfo(extern struct { x: u8, y: u32 = 5 })); - const infoB = @typeInfo(B).Struct; - try testing.expectEqual(TypeInfo.ContainerLayout.Extern, infoB.layout); - try testing.expectEqualSlices(u8, "x", infoB.fields[0].name); - try testing.expectEqual(u8, infoB.fields[0].field_type); - try testing.expectEqual(@as(?*const anyopaque, null), infoB.fields[0].default_value); - try testing.expectEqualSlices(u8, "y", infoB.fields[1].name); - try testing.expectEqual(u32, infoB.fields[1].field_type); - try testing.expectEqual(@as(u32, 5), @ptrCast(*const u32, infoB.fields[1].default_value.?).*); - try testing.expectEqual(@as(usize, 0), infoB.decls.len); - try testing.expectEqual(@as(bool, false), infoB.is_tuple); - - const C = @Type(@typeInfo(packed struct { x: u8 = 3, y: u32 = 5 })); - const infoC = @typeInfo(C).Struct; - try testing.expectEqual(TypeInfo.ContainerLayout.Packed, infoC.layout); - try testing.expectEqualSlices(u8, "x", infoC.fields[0].name); - try testing.expectEqual(u8, infoC.fields[0].field_type); - try testing.expectEqual(@as(u8, 3), @ptrCast(*const u8, infoC.fields[0].default_value.?).*); - try testing.expectEqualSlices(u8, "y", infoC.fields[1].name); - try testing.expectEqual(u32, infoC.fields[1].field_type); - try testing.expectEqual(@as(u32, 5), @ptrCast(*const u32, infoC.fields[1].default_value.?).*); - try testing.expectEqual(@as(usize, 0), infoC.decls.len); - try testing.expectEqual(@as(bool, false), infoC.is_tuple); -} - -test "Type.Enum" { - const Foo = @Type(.{ - .Enum = .{ - .layout = .Auto, - .tag_type = u8, - .fields = &[_]TypeInfo.EnumField{ - .{ .name = "a", .value = 1 }, - .{ .name = "b", .value = 5 }, - }, - .decls = &[_]TypeInfo.Declaration{}, - .is_exhaustive = true, - }, - }); - try testing.expectEqual(true, @typeInfo(Foo).Enum.is_exhaustive); - try testing.expectEqual(@as(u8, 1), @enumToInt(Foo.a)); - try testing.expectEqual(@as(u8, 5), @enumToInt(Foo.b)); - const Bar = @Type(.{ - .Enum = .{ - .layout = .Extern, - .tag_type = u32, - .fields = &[_]TypeInfo.EnumField{ - .{ .name = "a", .value = 1 }, - .{ .name = "b", .value = 5 }, - }, - .decls = &[_]TypeInfo.Declaration{}, - .is_exhaustive = false, - }, - }); - try testing.expectEqual(false, @typeInfo(Bar).Enum.is_exhaustive); - try testing.expectEqual(@as(u32, 1), @enumToInt(Bar.a)); - try testing.expectEqual(@as(u32, 5), @enumToInt(Bar.b)); - try testing.expectEqual(@as(u32, 6), @enumToInt(@intToEnum(Bar, 6))); -} - -test "Type.Union" { - const Untagged = @Type(.{ - .Union = .{ - .layout = .Auto, - .tag_type = null, - .fields = &[_]TypeInfo.UnionField{ - .{ .name = "int", .field_type = i32, .alignment = @alignOf(f32) }, - .{ .name = "float", .field_type = f32, .alignment = @alignOf(f32) }, - }, - .decls = &[_]TypeInfo.Declaration{}, - }, - }); - var untagged = Untagged{ .int = 1 }; - untagged.float = 2.0; - untagged.int = 3; - try testing.expectEqual(@as(i32, 3), untagged.int); - - const PackedUntagged = @Type(.{ - .Union = .{ - .layout = .Packed, - .tag_type = null, - .fields = &[_]TypeInfo.UnionField{ - .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) }, - .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) }, - }, - .decls = &[_]TypeInfo.Declaration{}, - }, - }); - var packed_untagged = PackedUntagged{ .signed = -1 }; - try testing.expectEqual(@as(i32, -1), packed_untagged.signed); - try testing.expectEqual(~@as(u32, 0), packed_untagged.unsigned); - - const Tag = @Type(.{ - .Enum = .{ - .layout = .Auto, - .tag_type = u1, - .fields = &[_]TypeInfo.EnumField{ - .{ .name = "signed", .value = 0 }, - .{ .name = "unsigned", .value = 1 }, - }, - .decls = &[_]TypeInfo.Declaration{}, - .is_exhaustive = true, - }, - }); - const Tagged = @Type(.{ - .Union = .{ - .layout = .Auto, - .tag_type = Tag, - .fields = &[_]TypeInfo.UnionField{ - .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) }, - .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) }, - }, - .decls = &[_]TypeInfo.Declaration{}, - }, - }); - var tagged = Tagged{ .signed = -1 }; - try testing.expectEqual(Tag.signed, tagged); - tagged = .{ .unsigned = 1 }; - try testing.expectEqual(Tag.unsigned, tagged); -} - -test "Type.Union from Type.Enum" { - const Tag = @Type(.{ - .Enum = .{ - .layout = .Auto, - .tag_type = u0, - .fields = &[_]TypeInfo.EnumField{ - .{ .name = "working_as_expected", .value = 0 }, - }, - .decls = &[_]TypeInfo.Declaration{}, - .is_exhaustive = true, - }, - }); - const T = @Type(.{ - .Union = .{ - .layout = .Auto, - .tag_type = Tag, - .fields = &[_]TypeInfo.UnionField{ - .{ .name = "working_as_expected", .field_type = u32, .alignment = @alignOf(u32) }, - }, - .decls = &[_]TypeInfo.Declaration{}, - }, - }); - _ = T; - _ = @typeInfo(T).Union; -} - -test "Type.Union from regular enum" { - const E = enum { working_as_expected }; - const T = @Type(.{ - .Union = .{ - .layout = .Auto, - .tag_type = E, - .fields = &[_]TypeInfo.UnionField{ - .{ .name = "working_as_expected", .field_type = u32, .alignment = @alignOf(u32) }, - }, - .decls = &[_]TypeInfo.Declaration{}, - }, - }); - _ = T; - _ = @typeInfo(T).Union; -} - -test "Type.Fn" { - // wasm doesn't support align attributes on functions - if (builtin.target.cpu.arch == .wasm32 or builtin.target.cpu.arch == .wasm64) return error.SkipZigTest; - - const foo = struct { - fn func(a: usize, b: bool) align(4) callconv(.C) usize { - _ = a; - _ = b; - return 0; - } - }.func; - const Foo = @Type(@typeInfo(@TypeOf(foo))); - const foo_2: Foo = foo; - _ = foo_2; -} - -test "Type.BoundFn" { - // wasm doesn't support align attributes on functions - if (builtin.target.cpu.arch == .wasm32 or builtin.target.cpu.arch == .wasm64) return error.SkipZigTest; - - const TestStruct = packed struct { - pub fn foo(self: *const @This()) align(4) callconv(.Unspecified) void { - _ = self; - } - }; - const test_instance: TestStruct = undefined; - try testing.expect(std.meta.eql( - @typeName(@TypeOf(test_instance.foo)), - @typeName(@Type(@typeInfo(@TypeOf(test_instance.foo)))), - )); -} From 0b7347fd18eee7dd829cd9aaed3683123d84859b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 12 Feb 2022 21:35:29 -0700 Subject: [PATCH 0154/2031] move more behavior tests to the "passing" section --- test/behavior.zig | 14 +++++++------- test/behavior/fn_delegation.zig | 3 +++ test/behavior/ir_block_deps.zig | 4 ++++ test/behavior/reflection.zig | 6 ++++++ test/behavior/tuple.zig | 11 +++++++++++ test/behavior/union.zig | 2 +- test/behavior/var_args.zig | 19 +++++++++++++++++++ 7 files changed, 51 insertions(+), 8 deletions(-) diff --git a/test/behavior.zig b/test/behavior.zig index 404ce376a2..6b08465429 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -31,18 +31,23 @@ test { _ = @import("behavior/bugs/7250.zig"); _ = @import("behavior/cast.zig"); _ = @import("behavior/comptime_memory.zig"); + _ = @import("behavior/fn_delegation.zig"); _ = @import("behavior/fn_in_struct_in_comptime.zig"); _ = @import("behavior/hasdecl.zig"); _ = @import("behavior/hasfield.zig"); + _ = @import("behavior/ir_block_deps.zig"); _ = @import("behavior/namespace_depends_on_compile_var.zig"); _ = @import("behavior/optional.zig"); _ = @import("behavior/prefetch.zig"); _ = @import("behavior/pub_enum.zig"); + _ = @import("behavior/reflection.zig"); _ = @import("behavior/slice.zig"); _ = @import("behavior/slice_sentinel_comptime.zig"); - _ = @import("behavior/type.zig"); - _ = @import("behavior/truncate.zig"); _ = @import("behavior/struct.zig"); + _ = @import("behavior/truncate.zig"); + _ = @import("behavior/tuple.zig"); + _ = @import("behavior/type.zig"); + _ = @import("behavior/var_args.zig"); if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_x86_64) { // Tests that pass (partly) for stage1, llvm backend, C backend, wasm backend. @@ -145,21 +150,16 @@ test { _ = @import("behavior/const_slice_child.zig"); _ = @import("behavior/export_self_referential_type_info.zig"); _ = @import("behavior/field_parent_ptr.zig"); - _ = @import("behavior/fn_delegation.zig"); - _ = @import("behavior/ir_block_deps.zig"); _ = @import("behavior/misc.zig"); _ = @import("behavior/muladd.zig"); - _ = @import("behavior/reflection.zig"); _ = @import("behavior/select.zig"); _ = @import("behavior/shuffle.zig"); _ = @import("behavior/struct_contains_null_ptr_itself.zig"); _ = @import("behavior/struct_contains_slice_of_itself.zig"); _ = @import("behavior/switch_prong_err_enum.zig"); _ = @import("behavior/switch_prong_implicit_cast.zig"); - _ = @import("behavior/tuple.zig"); _ = @import("behavior/typename.zig"); _ = @import("behavior/union_with_members.zig"); - _ = @import("behavior/var_args.zig"); _ = @import("behavior/vector.zig"); if (builtin.target.cpu.arch == .wasm32) { _ = @import("behavior/wasm.zig"); diff --git a/test/behavior/fn_delegation.zig b/test/behavior/fn_delegation.zig index 72a72c0bdd..25ec3dea1b 100644 --- a/test/behavior/fn_delegation.zig +++ b/test/behavior/fn_delegation.zig @@ -1,3 +1,4 @@ +const builtin = @import("builtin"); const expect = @import("std").testing.expect; const Foo = struct { @@ -31,6 +32,8 @@ fn custom(comptime T: type, comptime num: u64) fn (T) u64 { } test "fn delegation" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const foo = Foo{}; try expect(foo.one() == 11); try expect(foo.two() == 12); diff --git a/test/behavior/ir_block_deps.zig b/test/behavior/ir_block_deps.zig index 09c1532bff..cbc5cc2419 100644 --- a/test/behavior/ir_block_deps.zig +++ b/test/behavior/ir_block_deps.zig @@ -1,3 +1,4 @@ +const builtin = @import("builtin"); const expect = @import("std").testing.expect; fn foo(id: u64) !i32 { @@ -17,6 +18,9 @@ fn getErrInt() anyerror!i32 { } test "ir block deps" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + try expect((foo(1) catch unreachable) == 0); try expect((foo(2) catch unreachable) == 0); } diff --git a/test/behavior/reflection.zig b/test/behavior/reflection.zig index 18ee9d5c8b..96c81fe0d0 100644 --- a/test/behavior/reflection.zig +++ b/test/behavior/reflection.zig @@ -1,9 +1,12 @@ +const builtin = @import("builtin"); const std = @import("std"); const expect = std.testing.expect; const mem = std.mem; const reflection = @This(); test "reflection: function return type, var args, and param types" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + comptime { const info = @typeInfo(@TypeOf(dummy)).Fn; try expect(info.return_type.? == i32); @@ -25,6 +28,9 @@ fn dummy(a: bool, b: i32, c: f32) i32 { } test "reflection: @field" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + var f = Foo{ .one = 42, .two = true, diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index 632e5be013..680de28b76 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -1,9 +1,12 @@ +const builtin = @import("builtin"); const std = @import("std"); const testing = std.testing; const expect = testing.expect; const expectEqual = testing.expectEqual; test "tuple concatenation" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { var a: i32 = 1; @@ -20,6 +23,8 @@ test "tuple concatenation" { } test "tuple multiplication" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { { @@ -81,6 +86,8 @@ test "tuple multiplication" { } test "pass tuple to comptime var parameter" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + const S = struct { fn Foo(comptime args: anytype) !void { try expect(args[0] == 1); @@ -95,6 +102,8 @@ test "pass tuple to comptime var parameter" { } test "tuple initializer for var" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() void { const Bytes = struct { @@ -114,6 +123,8 @@ test "tuple initializer for var" { } test "array-like initializer for tuple types" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + const T = @Type(std.builtin.TypeInfo{ .Struct = std.builtin.TypeInfo.Struct{ .is_tuple = true, diff --git a/test/behavior/union.zig b/test/behavior/union.zig index cdd63df44e..1cd5b05eb1 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -362,7 +362,7 @@ pub const FooUnion = union(enum) { var glbl_array: [2]FooUnion = undefined; test "initialize global array of union" { - if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; glbl_array[1] = FooUnion{ .U1 = 2 }; glbl_array[0] = FooUnion{ .U0 = 1 }; diff --git a/test/behavior/var_args.zig b/test/behavior/var_args.zig index 40770e1334..63b8c35e1b 100644 --- a/test/behavior/var_args.zig +++ b/test/behavior/var_args.zig @@ -1,3 +1,4 @@ +const builtin = @import("builtin"); const expect = @import("std").testing.expect; fn add(args: anytype) i32 { @@ -12,6 +13,8 @@ fn add(args: anytype) i32 { } test "add arbitrary args" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + try expect(add(.{ @as(i32, 1), @as(i32, 2), @as(i32, 3), @as(i32, 4) }) == 10); try expect(add(.{@as(i32, 1234)}) == 1234); try expect(add(.{}) == 0); @@ -22,10 +25,16 @@ fn readFirstVarArg(args: anytype) void { } test "send void arg to var args" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + readFirstVarArg(.{{}}); } test "pass args directly" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + try expect(addSomeStuff(.{ @as(i32, 1), @as(i32, 2), @as(i32, 3), @as(i32, 4) }) == 10); try expect(addSomeStuff(.{@as(i32, 1234)}) == 1234); try expect(addSomeStuff(.{}) == 0); @@ -36,6 +45,8 @@ fn addSomeStuff(args: anytype) i32 { } test "runtime parameter before var args" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + try expect((try extraFn(10, .{})) == 0); try expect((try extraFn(10, .{false})) == 1); try expect((try extraFn(10, .{ false, true })) == 2); @@ -73,11 +84,19 @@ fn foo2(args: anytype) bool { } test "array of var args functions" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + try expect(foos[0](.{})); try expect(!foos[1](.{})); } test "pass zero length array to var args param" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + doNothingWithFirstArg(.{""}); } From ddd6de86f7eb71814d3605d3e0ea9ed01d075613 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 13 Feb 2022 12:34:41 +0200 Subject: [PATCH 0155/2031] parser: make missing semicolon error point to the end of the previous token --- lib/std/zig/Ast.zig | 22 ++++++++++++++++++++++ lib/std/zig/parse.zig | 19 ++++++++++++++----- src/Module.zig | 3 ++- src/main.zig | 5 +++-- 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig index 17da4f5315..16430fe9d4 100644 --- a/lib/std/zig/Ast.zig +++ b/lib/std/zig/Ast.zig @@ -64,6 +64,17 @@ pub fn renderToArrayList(tree: Ast, buffer: *std.ArrayList(u8)) RenderError!void return @import("./render.zig").renderTree(buffer, tree); } +/// Returns an extra offset for column and byte offset of errors that +/// should point after the token in the error message. +pub fn errorOffset(tree:Ast, error_tag: Error.Tag, token: TokenIndex) u32 { + return switch (error_tag) { + .expected_semi_after_decl, + .expected_semi_after_stmt, + => @intCast(u32, tree.tokenSlice(token).len), + else => 0, + }; +} + pub fn tokenLocation(self: Ast, start_offset: ByteOffset, token_index: TokenIndex) Location { var loc = Location{ .line = 0, @@ -306,6 +317,13 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { return stream.writeAll("function prototype has parameter after varargs"); }, + .expected_semi_after_decl => { + return stream.writeAll("expected ';' after declaration"); + }, + .expected_semi_after_stmt => { + return stream.writeAll("expected ';' after statement"); + }, + .expected_token => { const found_tag = token_tags[parse_error.token]; const expected_symbol = parse_error.extra.expected_tag.symbol(); @@ -2495,6 +2513,10 @@ pub const Error = struct { unattached_doc_comment, varargs_nonfinal, + // these have `token` set to token after which a semicolon was expected + expected_semi_after_decl, + expected_semi_after_stmt, + /// `expected_tag` is populated. expected_token, }; diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index a70d0309e3..e818046ac5 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -586,7 +586,7 @@ const Parser = struct { const thread_local_token = p.eatToken(.keyword_threadlocal); const var_decl = try p.parseVarDecl(); if (var_decl != 0) { - _ = try p.expectToken(.semicolon); + try p.expectSemicolon(.expected_semi_after_decl, false); return var_decl; } if (thread_local_token != null) { @@ -614,7 +614,7 @@ const Parser = struct { fn expectUsingNamespace(p: *Parser) !Node.Index { const usingnamespace_token = p.assertToken(.keyword_usingnamespace); const expr = try p.expectExpr(); - _ = try p.expectToken(.semicolon); + try p.expectSemicolon(.expected_semi_after_decl, false); return p.addNode(.{ .tag = .@"usingnamespace", .main_token = usingnamespace_token, @@ -851,7 +851,7 @@ const Parser = struct { const var_decl = try p.parseVarDecl(); if (var_decl != 0) { - _ = try p.expectTokenRecoverable(.semicolon); + try p.expectSemicolon(.expected_semi_after_decl, true); return var_decl; } @@ -915,7 +915,7 @@ const Parser = struct { const assign_expr = try p.parseAssignExpr(); if (assign_expr != 0) { - _ = try p.expectTokenRecoverable(.semicolon); + try p.expectSemicolon(.expected_semi_after_stmt, true); return assign_expr; } @@ -1205,7 +1205,7 @@ const Parser = struct { } const assign_expr = try p.parseAssignExpr(); if (assign_expr != 0) { - _ = try p.expectTokenRecoverable(.semicolon); + try p.expectSemicolon(.expected_semi_after_stmt, true); return assign_expr; } return null_node; @@ -3664,6 +3664,15 @@ const Parser = struct { } } + fn expectSemicolon(p: *Parser, tag: AstError.Tag, recoverable: bool) Error!void { + if (p.token_tags[p.tok_i] == .semicolon) { + _ = p.nextToken(); + return; + } + try p.warnMsg(.{ .tag = tag, .token = p.tok_i - 1 }); + if (!recoverable) return error.ParseError; + } + fn nextToken(p: *Parser) TokenIndex { const result = p.tok_i; p.tok_i += 1; diff --git a/src/Module.zig b/src/Module.zig index 3631e41f25..2cd01acd59 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2995,13 +2995,14 @@ pub fn astGenFile(mod: *Module, file: *File) !void { const token_starts = file.tree.tokens.items(.start); const token_tags = file.tree.tokens.items(.tag); + const extra_offset = file.tree.errorOffset(parse_err.tag, parse_err.token); try file.tree.renderError(parse_err, msg.writer()); const err_msg = try gpa.create(ErrorMsg); err_msg.* = .{ .src_loc = .{ .file_scope = file, .parent_decl_node = 0, - .lazy = .{ .byte_abs = token_starts[parse_err.token] }, + .lazy = .{ .byte_abs = token_starts[parse_err.token] + extra_offset }, }, .msg = msg.toOwnedSlice(), }; diff --git a/src/main.zig b/src/main.zig index 75655d6a2a..12e9f88088 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4040,13 +4040,14 @@ fn printErrMsgToStdErr( notes_len += 1; } + const extra_offset = tree.errorOffset(parse_error.tag, parse_error.token); const message: Compilation.AllErrors.Message = .{ .src = .{ .src_path = path, .msg = text, - .byte_offset = @intCast(u32, start_loc.line_start), + .byte_offset = @intCast(u32, start_loc.line_start) + extra_offset, .line = @intCast(u32, start_loc.line), - .column = @intCast(u32, start_loc.column), + .column = @intCast(u32, start_loc.column) + extra_offset, .source_line = source_line, .notes = notes_buffer[0..notes_len], }, From 6456af5a45cc12c0cd28d957dfedc72c369a157e Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 13 Feb 2022 13:18:30 +0200 Subject: [PATCH 0156/2031] parser: make missing comma errors point to the end of the previous token --- lib/std/zig/Ast.zig | 35 ++++++++++++++--- lib/std/zig/parse.zig | 88 +++++++++++++++++++++---------------------- 2 files changed, 71 insertions(+), 52 deletions(-) diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig index 16430fe9d4..c12c230c69 100644 --- a/lib/std/zig/Ast.zig +++ b/lib/std/zig/Ast.zig @@ -70,6 +70,13 @@ pub fn errorOffset(tree:Ast, error_tag: Error.Tag, token: TokenIndex) u32 { return switch (error_tag) { .expected_semi_after_decl, .expected_semi_after_stmt, + .expected_comma_after_field, + .expected_comma_after_arg, + .expected_comma_after_param, + .expected_comma_after_initializer, + .expected_comma_after_switch_prong, + .expected_semi_or_else, + .expected_semi_or_lbrace, => @intCast(u32, tree.tokenSlice(token).len), else => 0, }; @@ -227,14 +234,10 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { }); }, .expected_semi_or_else => { - return stream.print("expected ';' or 'else', found '{s}'", .{ - token_tags[parse_error.token].symbol(), - }); + return stream.writeAll("expected ';' or 'else' after statement"); }, .expected_semi_or_lbrace => { - return stream.print("expected ';' or '{{', found '{s}'", .{ - token_tags[parse_error.token].symbol(), - }); + return stream.writeAll("expected ';' or block after function prototype"); }, .expected_statement => { return stream.print("expected statement, found '{s}'", .{ @@ -323,6 +326,21 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { .expected_semi_after_stmt => { return stream.writeAll("expected ';' after statement"); }, + .expected_comma_after_field => { + return stream.writeAll("expected ',' after field"); + }, + .expected_comma_after_arg => { + return stream.writeAll("expected ',' after argument"); + }, + .expected_comma_after_param => { + return stream.writeAll("expected ',' after parameter"); + }, + .expected_comma_after_initializer => { + return stream.writeAll("expected ',' after initializer"); + }, + .expected_comma_after_switch_prong => { + return stream.writeAll("expected ',' after switch prong"); + }, .expected_token => { const found_tag = token_tags[parse_error.token]; @@ -2516,6 +2534,11 @@ pub const Error = struct { // these have `token` set to token after which a semicolon was expected expected_semi_after_decl, expected_semi_after_stmt, + expected_comma_after_field, + expected_comma_after_arg, + expected_comma_after_param, + expected_comma_after_initializer, + expected_comma_after_switch_prong, /// `expected_tag` is populated. expected_token, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index e818046ac5..3b9679f62d 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -160,6 +160,12 @@ const Parser = struct { .extra = .{ .expected_tag = expected_token }, }); } + + fn warnExpectedAfter(p: *Parser, error_tag: AstError.Tag) error{OutOfMemory}!void { + @setCold(true); + try p.warnMsg(.{ .tag = error_tag, .token = p.tok_i - 1 }); + } + fn warnMsg(p: *Parser, msg: Ast.Error) error{OutOfMemory}!void { @setCold(true); try p.errors.append(p.gpa, msg); @@ -258,7 +264,7 @@ const Parser = struct { } // There is not allowed to be a decl after a field with no comma. // Report error but recover parser. - try p.warnExpected(.comma); + try p.warnExpectedAfter(.expected_comma_after_field); p.findNextContainerMember(); } }, @@ -361,7 +367,7 @@ const Parser = struct { } // There is not allowed to be a decl after a field with no comma. // Report error but recover parser. - try p.warnExpected(.comma); + try p.warnExpectedAfter(.expected_comma_after_field); p.findNextContainerMember(); } }, @@ -573,7 +579,7 @@ const Parser = struct { // Since parseBlock only return error.ParseError on // a missing '}' we can assume this function was // supposed to end here. - try p.warn(.expected_semi_or_lbrace); + try p.warnExpectedAfter(.expected_semi_or_lbrace); return null_node; }, } @@ -984,7 +990,7 @@ const Parser = struct { }; _ = p.eatToken(.keyword_else) orelse { if (else_required) { - try p.warn(.expected_semi_or_else); + try p.warnExpectedAfter(.expected_semi_or_else); } return p.addNode(.{ .tag = .if_simple, @@ -1079,7 +1085,7 @@ const Parser = struct { }; _ = p.eatToken(.keyword_else) orelse { if (else_required) { - try p.warn(.expected_semi_or_else); + try p.warnExpectedAfter(.expected_semi_or_else); } return p.addNode(.{ .tag = .for_simple, @@ -1154,7 +1160,7 @@ const Parser = struct { }; _ = p.eatToken(.keyword_else) orelse { if (else_required) { - try p.warn(.expected_semi_or_else); + try p.warnExpectedAfter(.expected_semi_or_else); } if (cont_expr == 0) { return p.addNode(.{ @@ -2038,7 +2044,7 @@ const Parser = struct { }, .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpected(.comma), + else => try p.warnExpectedAfter(.expected_comma_after_initializer), } if (p.eatToken(.r_brace)) |_| break; const next = try p.expectFieldInit(); @@ -2079,7 +2085,7 @@ const Parser = struct { }, .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpected(.comma), + else => try p.warnExpectedAfter(.expected_comma_after_initializer), } } const comma = (p.token_tags[p.tok_i - 2] == .comma); @@ -2158,7 +2164,7 @@ const Parser = struct { }, .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren), // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpected(.comma), + else => try p.warnExpectedAfter(.expected_comma_after_arg), } } const comma = (p.token_tags[p.tok_i - 2] == .comma); @@ -2214,7 +2220,7 @@ const Parser = struct { }, .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren), // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpected(.comma), + else => try p.warnExpectedAfter(.expected_comma_after_arg), } } const comma = (p.token_tags[p.tok_i - 2] == .comma); @@ -2455,7 +2461,7 @@ const Parser = struct { }, .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpected(.comma), + else => try p.warnExpectedAfter(.expected_comma_after_initializer), } if (p.eatToken(.r_brace)) |_| break; const next = try p.expectFieldInit(); @@ -2507,7 +2513,7 @@ const Parser = struct { }, .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpected(.comma), + else => try p.warnExpectedAfter(.expected_comma_after_initializer), } } const comma = (p.token_tags[p.tok_i - 2] == .comma); @@ -2568,7 +2574,7 @@ const Parser = struct { }, .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpected(.comma), + else => try p.warnExpectedAfter(.expected_comma_after_field), } } return p.addNode(.{ @@ -3383,7 +3389,24 @@ const Parser = struct { /// SwitchProngList <- (SwitchProng COMMA)* SwitchProng? fn parseSwitchProngList(p: *Parser) !Node.SubRange { - return ListParseFn(parseSwitchProng)(p); + const scratch_top = p.scratch.items.len; + defer p.scratch.shrinkRetainingCapacity(scratch_top); + + while (true) { + const item = try parseSwitchProng(p); + if (item == 0) break; + + try p.scratch.append(p.gpa, item); + + switch (p.token_tags[p.tok_i]) { + .comma => p.tok_i += 1, + // All possible delimiters. + .colon, .r_paren, .r_brace, .r_bracket => break, + // Likely just a missing comma; give error but continue parsing. + else => try p.warnExpectedAfter(.expected_comma_after_switch_prong), + } + } + return p.listToSpan(p.scratch.items[scratch_top..]); } /// ParamDeclList <- (ParamDecl COMMA)* ParamDecl? @@ -3409,7 +3432,7 @@ const Parser = struct { }, .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren), // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpected(.comma), + else => try p.warnExpectedAfter(.expected_comma_after_param), } } if (varargs == .nonfinal) { @@ -3423,33 +3446,6 @@ const Parser = struct { }; } - const NodeParseFn = fn (p: *Parser) Error!Node.Index; - - fn ListParseFn(comptime nodeParseFn: anytype) (fn (p: *Parser) Error!Node.SubRange) { - return struct { - pub fn parse(p: *Parser) Error!Node.SubRange { - const scratch_top = p.scratch.items.len; - defer p.scratch.shrinkRetainingCapacity(scratch_top); - - while (true) { - const item = try nodeParseFn(p); - if (item == 0) break; - - try p.scratch.append(p.gpa, item); - - switch (p.token_tags[p.tok_i]) { - .comma => p.tok_i += 1, - // All possible delimiters. - .colon, .r_paren, .r_brace, .r_bracket => break, - // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpected(.comma), - } - } - return p.listToSpan(p.scratch.items[scratch_top..]); - } - }.parse; - } - /// FnCallArguments <- LPAREN ExprList RPAREN /// ExprList <- (Expr COMMA)* Expr? fn parseBuiltinCall(p: *Parser) !Node.Index { @@ -3480,7 +3476,7 @@ const Parser = struct { break; }, // Likely just a missing comma; give error but continue parsing. - else => try p.warnExpected(.comma), + else => try p.warnExpectedAfter(.expected_comma_after_arg), } } const comma = (p.token_tags[p.tok_i - 2] == .comma); @@ -3576,7 +3572,7 @@ const Parser = struct { } /// KEYWORD_if LPAREN Expr RPAREN PtrPayload? Body (KEYWORD_else Payload? Body)? - fn parseIf(p: *Parser, bodyParseFn: NodeParseFn) !Node.Index { + fn parseIf(p: *Parser, bodyParseFn: fn (p: *Parser) Error!Node.Index) !Node.Index { const if_token = p.eatToken(.keyword_if) orelse return null_node; _ = try p.expectToken(.l_paren); const condition = try p.expectExpr(); @@ -3664,12 +3660,12 @@ const Parser = struct { } } - fn expectSemicolon(p: *Parser, tag: AstError.Tag, recoverable: bool) Error!void { + fn expectSemicolon(p: *Parser, error_tag: AstError.Tag, recoverable: bool) Error!void { if (p.token_tags[p.tok_i] == .semicolon) { _ = p.nextToken(); return; } - try p.warnMsg(.{ .tag = tag, .token = p.tok_i - 1 }); + try p.warnExpectedAfter(error_tag); if (!recoverable) return error.ParseError; } From 8a432436aedd8b10bb837965efe630ce58f89a0b Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 13 Feb 2022 13:39:03 +0200 Subject: [PATCH 0157/2031] update compile error tests --- test/compile_errors.zig | 44 ++++++++++++++++++++--------------------- test/stage2/cbe.zig | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 3c224013c9..79c17b4336 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2171,7 +2171,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ _ = x; \\} , &[_][]const u8{ - "tmp.zig:3:7: error: expected ',', found 'align'", + "tmp.zig:3:6: error: expected ',' after field", }); ctx.objErrStage1("bad alignment type", @@ -4704,7 +4704,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';', found 'var'", + "tmp.zig:4:9: error: expected ';' after statement", }); ctx.objErrStage1("implicit semicolon - block expr", @@ -4715,7 +4715,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';', found 'var'", + "tmp.zig:4:11: error: expected ';' after statement", }); ctx.objErrStage1("implicit semicolon - comptime statement", @@ -4726,7 +4726,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';', found 'var'", + "tmp.zig:4:18: error: expected ';' after statement", }); ctx.objErrStage1("implicit semicolon - comptime expression", @@ -4737,7 +4737,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';', found 'var'", + "tmp.zig:4:20: error: expected ';' after statement", }); ctx.objErrStage1("implicit semicolon - defer", @@ -4748,7 +4748,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';', found 'var'", + "tmp.zig:4:15: error: expected ';' after statement", }); ctx.objErrStage1("implicit semicolon - if statement", @@ -4759,7 +4759,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';' or 'else', found 'var'", + "tmp.zig:4:18: error: expected ';' or 'else' after statement", }); ctx.objErrStage1("implicit semicolon - if expression", @@ -4770,7 +4770,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';', found 'var'", + "tmp.zig:4:20: error: expected ';' after statement", }); ctx.objErrStage1("implicit semicolon - if-else statement", @@ -4781,7 +4781,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';', found 'var'", + "tmp.zig:4:28: error: expected ';' after statement", }); ctx.objErrStage1("implicit semicolon - if-else expression", @@ -4792,7 +4792,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';', found 'var'", + "tmp.zig:4:28: error: expected ';' after statement", }); ctx.objErrStage1("implicit semicolon - if-else-if statement", @@ -4803,7 +4803,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';' or 'else', found 'var'", + "tmp.zig:4:37: error: expected ';' or 'else' after statement", }); ctx.objErrStage1("implicit semicolon - if-else-if expression", @@ -4814,7 +4814,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';', found 'var'", + "tmp.zig:4:37: error: expected ';' after statement", }); ctx.objErrStage1("implicit semicolon - if-else-if-else statement", @@ -4825,7 +4825,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';', found 'var'", + "tmp.zig:4:47: error: expected ';' after statement", }); ctx.objErrStage1("implicit semicolon - if-else-if-else expression", @@ -4836,7 +4836,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';', found 'var'", + "tmp.zig:4:45: error: expected ';' after statement", }); ctx.objErrStage1("implicit semicolon - test statement", @@ -4847,7 +4847,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';' or 'else', found 'var'", + "tmp.zig:4:24: error: expected ';' or 'else' after statement", }); ctx.objErrStage1("implicit semicolon - test expression", @@ -4858,7 +4858,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';', found 'var'", + "tmp.zig:4:26: error: expected ';' after statement", }); ctx.objErrStage1("implicit semicolon - while statement", @@ -4869,7 +4869,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';' or 'else', found 'var'", + "tmp.zig:4:21: error: expected ';' or 'else' after statement", }); ctx.objErrStage1("implicit semicolon - while expression", @@ -4880,7 +4880,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';', found 'var'", + "tmp.zig:4:23: error: expected ';' after statement", }); ctx.objErrStage1("implicit semicolon - while-continue statement", @@ -4891,7 +4891,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';' or 'else', found 'var'", + "tmp.zig:4:26: error: expected ';' or 'else' after statement", }); ctx.objErrStage1("implicit semicolon - while-continue expression", @@ -4902,7 +4902,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';', found 'var'", + "tmp.zig:4:28: error: expected ';' after statement", }); ctx.objErrStage1("implicit semicolon - for statement", @@ -4913,7 +4913,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';' or 'else', found 'var'", + "tmp.zig:4:24: error: expected ';' or 'else' after statement", }); ctx.objErrStage1("implicit semicolon - for expression", @@ -4924,7 +4924,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ var bad = {}; \\} , &[_][]const u8{ - "tmp.zig:5:5: error: expected ';', found 'var'", + "tmp.zig:4:26: error: expected ';' after statement", }); ctx.objErrStage1("multiple function definitions", diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index cfb9831e40..949a7eb6b7 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -693,7 +693,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ _ = E1.a; \\} , &.{ - ":3:7: error: expected ',', found 'align'", + ":3:6: error: expected ',' after field", }); // Redundant non-exhaustive enum mark. From 0699b29ce0c5d264bf39c5f88fa6025b61ca6303 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 13 Feb 2022 14:21:53 +0200 Subject: [PATCH 0158/2031] parser: give better errors for misplaced `.{` --- lib/std/zig/Ast.zig | 2 +- lib/std/zig/parse.zig | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig index c12c230c69..65fea1ae2e 100644 --- a/lib/std/zig/Ast.zig +++ b/lib/std/zig/Ast.zig @@ -66,7 +66,7 @@ pub fn renderToArrayList(tree: Ast, buffer: *std.ArrayList(u8)) RenderError!void /// Returns an extra offset for column and byte offset of errors that /// should point after the token in the error message. -pub fn errorOffset(tree:Ast, error_tag: Error.Tag, token: TokenIndex) u32 { +pub fn errorOffset(tree: Ast, error_tag: Error.Tag, token: TokenIndex) u32 { return switch (error_tag) { .expected_semi_after_decl, .expected_semi_after_stmt, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 3b9679f62d..9c6f873748 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -3229,6 +3229,10 @@ const Parser = struct { .rhs = p.nextToken(), }, }), + .l_brace => { + // this a misplaced `.{`, handle the error somewhere else + return null_node; + }, else => { p.tok_i += 1; try p.warn(.expected_suffix_op); From 17822e4a050dcbcf7fe324fcdff68887fee73ff1 Mon Sep 17 00:00:00 2001 From: sharpobject Date: Thu, 10 Feb 2022 05:57:10 +0900 Subject: [PATCH 0159/2031] std.json: fix compile error for comptime fields This is covered by an existing test which was already failing. --- lib/std/json.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/json.zig b/lib/std/json.zig index ec3544364f..e9fde26bec 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -1766,7 +1766,7 @@ fn parseInternal( } } if (field.is_comptime) { - if (!try parsesTo(field.field_type, field.default_value.?, tokens, child_options)) { + if (!try parsesTo(field.field_type, @ptrCast(*const field.field_type, field.default_value.?).*, tokens, child_options)) { return error.UnexpectedValue; } } else { From 8937f18a6f8496e011b13cb086b7948b5f1d540e Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 13 Feb 2022 14:35:49 +0200 Subject: [PATCH 0160/2031] std: force refAllDecls to actually resolve all decls Only about half of the tests in std were actually being run (918 vs 2144). --- lib/std/testing.zig | 2 ++ lib/std/zig/c_translation.zig | 2 +- lib/std/zig/parser_test.zig | 20 +++++++++++--------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/std/testing.zig b/lib/std/testing.zig index a9874d4df1..1134717c02 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -466,6 +466,8 @@ test { pub fn refAllDecls(comptime T: type) void { if (!builtin.is_test) return; inline for (comptime std.meta.declarations(T)) |decl| { + if (decl.is_pub and @typeInfo(@TypeOf(@field(T, decl.name))) == .Struct) + _ = @hasDecl(@field(T, decl.name), "foo"); _ = decl; } } diff --git a/lib/std/zig/c_translation.zig b/lib/std/zig/c_translation.zig index 0062b071c2..67eceda937 100644 --- a/lib/std/zig/c_translation.zig +++ b/lib/std/zig/c_translation.zig @@ -166,7 +166,7 @@ pub fn sizeof(target: anytype) usize { const array_info = @typeInfo(ptr.child).Array; if ((array_info.child == u8 or array_info.child == u16) and array_info.sentinel != null and - array_info.sentinel.? == 0) + @ptrCast(*const array_info.child, array_info.sentinel.?).* == 0) { // length of the string plus one for the null terminator. return (array_info.len + 1) * @sizeOf(array_info.child); diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 14b0e8b501..a0cc11ce4b 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -585,15 +585,6 @@ test "zig fmt: asm expression with comptime content" { ); } -test "zig fmt: anytype struct field" { - try testCanonical( - \\pub const Pointer = struct { - \\ sentinel: anytype, - \\}; - \\ - ); -} - test "zig fmt: array types last token" { try testCanonical( \\test { @@ -4148,6 +4139,17 @@ test "zig fmt: container doc comments" { ); } +test "zig fmt: anytype struct field" { + try testError( + \\pub const Pointer = struct { + \\ sentinel: anytype, + \\}; + \\ + , &[_]Error{ + .expected_type_expr, + }); +} + test "zig fmt: extern without container keyword returns error" { try testError( \\const container = extern {}; From 3bbe6a28e069b03c8a9185dd14129517453e26d2 Mon Sep 17 00:00:00 2001 From: Jacob G-W Date: Thu, 27 Jan 2022 15:23:28 -0500 Subject: [PATCH 0161/2031] stage2: add decltests --- lib/std/zig/Ast.zig | 2 +- lib/std/zig/parse.zig | 10 ++++- lib/std/zig/render.zig | 3 +- src/AstGen.zig | 91 +++++++++++++++++++++++++++++++++----- src/Module.zig | 6 +++ src/Zir.zig | 3 +- src/print_zir.zig | 10 +++-- test/behavior.zig | 5 +++ test/behavior/decltest.zig | 7 +++ 9 files changed, 119 insertions(+), 18 deletions(-) create mode 100644 test/behavior/decltest.zig diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig index 17da4f5315..a756834c58 100644 --- a/lib/std/zig/Ast.zig +++ b/lib/std/zig/Ast.zig @@ -2519,7 +2519,7 @@ pub const Node = struct { root, /// `usingnamespace lhs;`. rhs unused. main_token is `usingnamespace`. @"usingnamespace", - /// lhs is test name token (must be string literal), if any. + /// lhs is test name token (must be string literal or identifier), if any. /// rhs is the body node. test_decl, /// lhs is the index into extra_data. diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index a70d0309e3..cf27e6b1c7 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -500,10 +500,16 @@ const Parser = struct { } } - /// TestDecl <- KEYWORD_test STRINGLITERALSINGLE? Block + /// TestDecl <- KEYWORD_test (STRINGLITERALSINGLE / IDENTIFIER)? Block fn expectTestDecl(p: *Parser) !Node.Index { const test_token = p.assertToken(.keyword_test); - const name_token = p.eatToken(.string_literal); + const name_token = switch (p.token_tags[p.nextToken()]) { + .string_literal, .identifier => p.tok_i - 1, + else => blk: { + p.tok_i -= 1; + break :blk null; + }, + }; const block_node = try p.parseBlock(); if (block_node == 0) return p.fail(.expected_block); return p.addNode(.{ diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index f17ee1e097..0f6fcac8b7 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -151,7 +151,8 @@ fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, spac .test_decl => { const test_token = main_tokens[decl]; try renderToken(ais, tree, test_token, .space); - if (token_tags[test_token + 1] == .string_literal) { + const test_name_tag = token_tags[test_token + 1]; + if (test_name_tag == .string_literal or test_name_tag == .identifier) { try renderToken(ais, tree, test_token + 1, .space); } try renderExpression(gpa, ais, tree, datas[decl].rhs, space); diff --git a/src/AstGen.zig b/src/AstGen.zig index 9bc10f25e8..0652a6232f 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -105,8 +105,8 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { }; defer astgen.deinit(gpa); - // String table indexes 0 and 1 are reserved for special meaning. - try astgen.string_bytes.appendSlice(gpa, &[_]u8{ 0, 0 }); + // String table indexes 0, 1, 2 are reserved for special meaning. + try astgen.string_bytes.appendSlice(gpa, &[_]u8{ 0, 0, 0 }); // We expect at least as many ZIR instructions and extra data items // as AST nodes. @@ -3736,13 +3736,78 @@ fn testDecl( }; defer decl_block.unstack(); + const main_tokens = tree.nodes.items(.main_token); + const token_tags = tree.tokens.items(.tag); + const test_token = main_tokens[node]; + const test_name_token = test_token + 1; + const test_name_token_tag = token_tags[test_name_token]; + const is_decltest = test_name_token_tag == .identifier; const test_name: u32 = blk: { - const main_tokens = tree.nodes.items(.main_token); - const token_tags = tree.tokens.items(.tag); - const test_token = main_tokens[node]; - const str_lit_token = test_token + 1; - if (token_tags[str_lit_token] == .string_literal) { - break :blk try astgen.testNameString(str_lit_token); + if (test_name_token_tag == .string_literal) { + break :blk try astgen.testNameString(test_name_token); + } else if (test_name_token_tag == .identifier) { + const ident_name_raw = tree.tokenSlice(test_name_token); + + if (mem.eql(u8, ident_name_raw, "_")) return astgen.failTok(test_name_token, "'_' used as an identifier without @\"_\" syntax", .{}); + + // if not @"" syntax, just use raw token slice + if (ident_name_raw[0] != '@') { + if (primitives.get(ident_name_raw)) |_| return astgen.failTok(test_name_token, "cannot test a primitive", .{}); + + if (ident_name_raw.len >= 2) integer: { + const first_c = ident_name_raw[0]; + if (first_c == 'i' or first_c == 'u') { + _ = switch (first_c == 'i') { + true => .signed, + false => .unsigned, + }; + _ = parseBitCount(ident_name_raw[1..]) catch |err| switch (err) { + error.Overflow => return astgen.failTok( + test_name_token, + "primitive integer type '{s}' exceeds maximum bit width of 65535", + .{ident_name_raw}, + ), + error.InvalidCharacter => break :integer, + }; + return astgen.failTok(test_name_token, "cannot test a primitive", .{}); + } + } + } + + // Local variables, including function parameters. + const name_str_index = try astgen.identAsString(test_name_token); + var s = scope; + var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already + var num_namespaces_out: u32 = 0; + var capturing_namespace: ?*Scope.Namespace = null; + while (true) switch (s.tag) { + .local_val, .local_ptr => unreachable, // a test cannot be in a local scope + .gen_zir => s = s.cast(GenZir).?.parent, + .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, + .namespace => { + const ns = s.cast(Scope.Namespace).?; + if (ns.decls.get(name_str_index)) |i| { + if (found_already) |f| { + return astgen.failTokNotes(test_name_token, "ambiguous reference", .{}, &.{ + try astgen.errNoteNode(f, "declared here", .{}), + try astgen.errNoteNode(i, "also declared here", .{}), + }); + } + // We found a match but must continue looking for ambiguous references to decls. + found_already = i; + } + num_namespaces_out += 1; + capturing_namespace = ns; + s = ns.parent; + }, + .top => break, + }; + if (found_already == null) { + const ident_name = try astgen.identifierTokenString(test_name_token); + return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name}); + } + + break :blk name_str_index; } // String table index 1 has a special meaning here of test decl with no name. break :blk 1; @@ -3804,9 +3869,15 @@ fn testDecl( const line_delta = decl_block.decl_line - gz.decl_line; wip_members.appendToDecl(line_delta); } - wip_members.appendToDecl(test_name); + if (is_decltest) + wip_members.appendToDecl(2) // 2 here means that it is a decltest, look at doc comment for name + else + wip_members.appendToDecl(test_name); wip_members.appendToDecl(block_inst); - wip_members.appendToDecl(0); // no doc comments on test decls + if (is_decltest) + wip_members.appendToDecl(test_name) // the doc comment on a decltest represents it's name + else + wip_members.appendToDecl(0); // no doc comments on test decls } fn structDeclInner( diff --git a/src/Module.zig b/src/Module.zig index 3631e41f25..b9e50355fd 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4170,6 +4170,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi const line_off = zir.extra[decl_sub_index + 4]; const line = iter.parent_decl.relativeToLine(line_off); const decl_name_index = zir.extra[decl_sub_index + 5]; + const decl_doccomment_index = zir.extra[decl_sub_index + 7]; const decl_index = zir.extra[decl_sub_index + 6]; const decl_block_inst_data = zir.instructions.items(.data)[decl_index].pl_node; const decl_node = iter.parent_decl.relativeToNodeIndex(decl_block_inst_data.src_node); @@ -4193,6 +4194,11 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi iter.unnamed_test_index += 1; break :name try std.fmt.allocPrintZ(gpa, "test_{d}", .{i}); }, + 2 => name: { + is_named_test = true; + const test_name = zir.nullTerminatedString(decl_doccomment_index); + break :name try std.fmt.allocPrintZ(gpa, "decltest.{s}", .{test_name}); + }, else => name: { const raw_name = zir.nullTerminatedString(decl_name_index); if (raw_name.len == 0) { diff --git a/src/Zir.zig b/src/Zir.zig index b7e3e60916..b8ff7ae50f 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -2579,10 +2579,11 @@ pub const Inst = struct { /// - 0 means comptime or usingnamespace decl. /// - if name == 0 `is_exported` determines which one: 0=comptime,1=usingnamespace /// - 1 means test decl with no name. + /// - 2 means that the test is a decltest, doc_comment gives the name of the identifier /// - if there is a 0 byte at the position `name` indexes, it indicates /// this is a test decl, and the name starts at `name+1`. /// value: Index, - /// doc_comment: u32, // 0 if no doc comment + /// doc_comment: u32, 0 if no doc comment, if this is a decltest, doc_comment references the decl name in the string table /// align: Ref, // if corresponding bit is set /// link_section_or_address_space: { // if corresponding bit is set. /// link_section: Ref, diff --git a/src/print_zir.zig b/src/print_zir.zig index 9c79ad1a37..6396f11467 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -1443,20 +1443,24 @@ const Writer = struct { } else if (decl_name_index == 1) { try stream.writeByteNTimes(' ', self.indent); try stream.writeAll("test"); + } else if (decl_name_index == 2) { + try stream.writeByteNTimes(' ', self.indent); + try stream.print("[{d}] decltest {s}", .{ sub_index, self.code.nullTerminatedString(doc_comment_index) }); } else { const raw_decl_name = self.code.nullTerminatedString(decl_name_index); const decl_name = if (raw_decl_name.len == 0) self.code.nullTerminatedString(decl_name_index + 1) else raw_decl_name; - const test_str = if (raw_decl_name.len == 0) "test " else ""; + const test_str = if (raw_decl_name.len == 0) "test \"" else ""; const export_str = if (is_exported) "export " else ""; try self.writeDocComment(stream, doc_comment_index); try stream.writeByteNTimes(' ', self.indent); - try stream.print("[{d}] {s}{s}{s}{}", .{ - sub_index, pub_str, test_str, export_str, std.zig.fmtId(decl_name), + const endquote_if_test: []const u8 = if (raw_decl_name.len == 0) "\"" else ""; + try stream.print("[{d}] {s}{s}{s}{}{s}", .{ + sub_index, pub_str, test_str, export_str, std.zig.fmtId(decl_name), endquote_if_test, }); if (align_inst != .none) { try stream.writeAll(" align("); diff --git a/test/behavior.zig b/test/behavior.zig index 6b08465429..db6863a8b0 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -49,6 +49,11 @@ test { _ = @import("behavior/type.zig"); _ = @import("behavior/var_args.zig"); + // tests that don't pass for stage1 + if (builtin.zig_backend != .stage1) { + _ = @import("behavior/decltest.zig"); + } + if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_x86_64) { // Tests that pass (partly) for stage1, llvm backend, C backend, wasm backend. _ = @import("behavior/bitcast.zig"); diff --git a/test/behavior/decltest.zig b/test/behavior/decltest.zig new file mode 100644 index 0000000000..f731f80fb2 --- /dev/null +++ b/test/behavior/decltest.zig @@ -0,0 +1,7 @@ +pub fn the_add_function(a: u32, b: u32) u32 { + return a + b; +} + +test the_add_function { + if (the_add_function(1, 2) != 3) unreachable; +} From f516e2c5b1d0e073d9ab4cd417aeb21f6cdf4a99 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Fri, 11 Feb 2022 09:24:08 -0700 Subject: [PATCH 0162/2031] Simplify implementation of floorPowerOfTwo in std.math --- lib/std/math.zig | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/std/math.zig b/lib/std/math.zig index 4b8bcf2287..71cb5a184c 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -1045,14 +1045,9 @@ pub fn isPowerOfTwo(v: anytype) bool { /// Returns the nearest power of two less than or equal to value, or /// zero if value is less than or equal to zero. pub fn floorPowerOfTwo(comptime T: type, value: T) T { - var x = value; - - comptime var i = 1; - inline while (@typeInfo(T).Int.bits > i) : (i *= 2) { - x |= (x >> i); - } - - return x - (x >> 1); + const uT = std.meta.Int(.unsigned, @typeInfo(T).Int.bits); + if (value <= 0) return 0; + return @as(T, 1) << log2_int(uT, @intCast(uT, value)); } test "math.floorPowerOfTwo" { @@ -1064,9 +1059,15 @@ fn testFloorPowerOfTwo() !void { try testing.expect(floorPowerOfTwo(u32, 63) == 32); try testing.expect(floorPowerOfTwo(u32, 64) == 64); try testing.expect(floorPowerOfTwo(u32, 65) == 64); + try testing.expect(floorPowerOfTwo(u32, 0) == 0); try testing.expect(floorPowerOfTwo(u4, 7) == 4); try testing.expect(floorPowerOfTwo(u4, 8) == 8); try testing.expect(floorPowerOfTwo(u4, 9) == 8); + try testing.expect(floorPowerOfTwo(u4, 0) == 0); + try testing.expect(floorPowerOfTwo(i4, 7) == 4); + try testing.expect(floorPowerOfTwo(i4, -8) == 0); + try testing.expect(floorPowerOfTwo(i4, -1) == 0); + try testing.expect(floorPowerOfTwo(i4, 0) == 0); } /// Returns the next power of two (if the value is not already a power of two). From f22443bb05a6be6c3ade08254f52fdd05eeb2910 Mon Sep 17 00:00:00 2001 From: Sebsatian Keller Date: Sun, 13 Feb 2022 14:19:33 +0100 Subject: [PATCH 0163/2031] Fixed progress indicator for `zig test` (#10859) Previously the progress displayed the first item as [0/x]. This was misleading when x is the number of items. The first item should be displayed as [1/x] --- lib/std/Progress.zig | 5 +++-- lib/std/special/test_runner.zig | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 24b66c1162..ecef04c600 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -261,6 +261,7 @@ fn refreshWithHeldLock(self: *Progress) void { need_ellipse = false; const eti = @atomicLoad(usize, &node.unprotected_estimated_total_items, .Monotonic); const completed_items = @atomicLoad(usize, &node.unprotected_completed_items, .Monotonic); + const current_item = completed_items + 1; if (node.name.len != 0 or eti > 0) { if (node.name.len != 0) { self.bufWrite(&end, "{s}", .{node.name}); @@ -268,11 +269,11 @@ fn refreshWithHeldLock(self: *Progress) void { } if (eti > 0) { if (need_ellipse) self.bufWrite(&end, " ", .{}); - self.bufWrite(&end, "[{d}/{d}] ", .{ completed_items, eti }); + self.bufWrite(&end, "[{d}/{d}] ", .{ current_item, eti }); need_ellipse = false; } else if (completed_items != 0) { if (need_ellipse) self.bufWrite(&end, " ", .{}); - self.bufWrite(&end, "[{d}] ", .{completed_items}); + self.bufWrite(&end, "[{d}] ", .{current_item}); need_ellipse = false; } } diff --git a/lib/std/special/test_runner.zig b/lib/std/special/test_runner.zig index fb00a9dc30..201a5ccd90 100644 --- a/lib/std/special/test_runner.zig +++ b/lib/std/special/test_runner.zig @@ -82,18 +82,18 @@ pub fn main() void { } else |err| switch (err) { error.SkipZigTest => { skip_count += 1; - test_node.end(); progress.log("{s}... SKIP\n", .{test_fn.name}); if (!have_tty) std.debug.print("SKIP\n", .{}); + test_node.end(); }, else => { fail_count += 1; - test_node.end(); progress.log("{s}... FAIL ({s})\n", .{ test_fn.name, @errorName(err) }); if (!have_tty) std.debug.print("FAIL ({s})\n", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } + test_node.end(); }, } } From b5f8fb85e64022ed1ee59ff70753577839ad41b6 Mon Sep 17 00:00:00 2001 From: Mateusz Radomski <33978857+m-radomski@users.noreply.github.com> Date: Sun, 13 Feb 2022 14:37:38 +0100 Subject: [PATCH 0164/2031] Implement f128 `@rem` --- lib/std/special/compiler_rt.zig | 3 + lib/std/special/compiler_rt/floatfmodl.zig | 126 ++++++++++++++++++ .../special/compiler_rt/floatfmodl_test.zig | 46 +++++++ src/stage1/ir.cpp | 32 ++++- src/value.zig | 9 +- test/behavior/math.zig | 60 ++++++++- 6 files changed, 263 insertions(+), 13 deletions(-) create mode 100644 lib/std/special/compiler_rt/floatfmodl.zig create mode 100644 lib/std/special/compiler_rt/floatfmodl_test.zig diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 36f703464a..3ef2bf4747 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -759,6 +759,9 @@ comptime { @export(__unordtf2, .{ .name = "__unordkf2", .linkage = linkage }); } + const fmodl = @import("compiler_rt/floatfmodl.zig").fmodl; + @export(fmodl, .{ .name = "fmodl", .linkage = linkage }); + @export(floorf, .{ .name = "floorf", .linkage = linkage }); @export(floor, .{ .name = "floor", .linkage = linkage }); @export(floorl, .{ .name = "floorl", .linkage = linkage }); diff --git a/lib/std/special/compiler_rt/floatfmodl.zig b/lib/std/special/compiler_rt/floatfmodl.zig new file mode 100644 index 0000000000..942a7c1125 --- /dev/null +++ b/lib/std/special/compiler_rt/floatfmodl.zig @@ -0,0 +1,126 @@ +const builtin = @import("builtin"); +const std = @import("std"); + +// fmodl - floating modulo large, returns the remainder of division for f128 types +// Logic and flow heavily inspired by MUSL fmodl for 113 mantissa digits +pub fn fmodl(a: f128, b: f128) callconv(.C) f128 { + @setRuntimeSafety(builtin.is_test); + var amod = a; + var bmod = b; + const aPtr_u64 = @ptrCast([*]u64, &amod); + const bPtr_u64 = @ptrCast([*]u64, &bmod); + const aPtr_u16 = @ptrCast([*]u16, &amod); + const bPtr_u16 = @ptrCast([*]u16, &bmod); + + const exp_and_sign_index = comptime switch (builtin.target.cpu.arch.endian()) { + .Little => 7, + .Big => 0, + }; + const low_index = comptime switch (builtin.target.cpu.arch.endian()) { + .Little => 0, + .Big => 1, + }; + const high_index = comptime switch (builtin.target.cpu.arch.endian()) { + .Little => 1, + .Big => 0, + }; + + const signA = aPtr_u16[exp_and_sign_index] & 0x8000; + var expA = @intCast(i32, (aPtr_u16[exp_and_sign_index] & 0x7fff)); + var expB = bPtr_u16[exp_and_sign_index] & 0x7fff; + + // There are 3 cases where the answer is undefined, check for: + // - fmodl(val, 0) + // - fmodl(val, NaN) + // - fmodl(inf, val) + // The sign on checked values does not matter. + // Doing (a * b) / (a * b) procudes undefined results + // because the three cases always produce undefined calculations: + // - 0 / 0 + // - val * NaN + // - inf / inf + if (b == 0 or std.math.isNan(b) or expA == 0x7fff) { + return (a * b) / (a * b); + } + + // Remove the sign from both + aPtr_u16[exp_and_sign_index] = @bitCast(u16, @intCast(i16, expA)); + bPtr_u16[exp_and_sign_index] = @bitCast(u16, @intCast(i16, expB)); + if (amod <= bmod) { + if (amod == bmod) { + return 0 * a; + } + return a; + } + + if (expA == 0) { + amod *= 0x1p120; + expA = aPtr_u16[exp_and_sign_index] -% 120; + } + + if (expB == 0) { + bmod *= 0x1p120; + expB = bPtr_u16[exp_and_sign_index] -% 120; + } + + // OR in extra non-stored mantissa digit + var highA: u64 = (aPtr_u64[high_index] & (std.math.maxInt(u64) >> 16)) | 1 << 48; + var highB: u64 = (bPtr_u64[high_index] & (std.math.maxInt(u64) >> 16)) | 1 << 48; + var lowA: u64 = aPtr_u64[low_index]; + var lowB: u64 = bPtr_u64[low_index]; + + while (expA > expB) : (expA -= 1) { + var high = highA -% highB; + var low = lowA -% lowB; + if (lowA < lowB) { + high = highA -% 1; + } + if (high >> 63 == 0) { + if ((high | low) == 0) { + return 0 * a; + } + highA = 2 *% high + (low >> 63); + lowA = 2 *% low; + } else { + highA = 2 *% highA + (lowA >> 63); + lowA = 2 *% lowA; + } + } + + var high = highA -% highB; + var low = lowA -% lowB; + if (lowA < lowB) { + high -= 1; + } + if (high >> 63 == 0) { + if ((high | low) == 0) { + return 0 * a; + } + highA = high; + lowA = low; + } + + while (highA >> 48 == 0) { + highA = 2 *% highA + (lowA >> 63); + lowA = 2 *% lowA; + expA = expA - 1; + } + + // Overwrite the current amod with the values in highA and lowA + aPtr_u64[high_index] = highA; + aPtr_u64[low_index] = lowA; + + // Combine the exponent with the sign, normalize if happend to be denormalized + if (expA <= 0) { + aPtr_u16[exp_and_sign_index] = @truncate(u16, @bitCast(u32, (expA +% 120))) | signA; + amod *= 0x1p-120; + } else { + aPtr_u16[exp_and_sign_index] = @truncate(u16, @bitCast(u32, expA)) | signA; + } + + return amod; +} + +test { + _ = @import("floatfmodl_test.zig"); +} diff --git a/lib/std/special/compiler_rt/floatfmodl_test.zig b/lib/std/special/compiler_rt/floatfmodl_test.zig new file mode 100644 index 0000000000..58636ef6f7 --- /dev/null +++ b/lib/std/special/compiler_rt/floatfmodl_test.zig @@ -0,0 +1,46 @@ +const std = @import("std"); +const fmodl = @import("floatfmodl.zig"); +const testing = std.testing; + +fn test_fmodl(a: f128, b: f128, exp: f128) !void { + const res = fmodl.fmodl(a, b); + try testing.expect(exp == res); +} + +fn test_fmodl_nans() !void { + try testing.expect(std.math.isNan(fmodl.fmodl(1.0, std.math.nan_f128))); + try testing.expect(std.math.isNan(fmodl.fmodl(1.0, -std.math.nan_f128))); + try testing.expect(std.math.isNan(fmodl.fmodl(std.math.nan_f128, 1.0))); + try testing.expect(std.math.isNan(fmodl.fmodl(-std.math.nan_f128, 1.0))); +} + +fn test_fmodl_infs() !void { + try testing.expect(fmodl.fmodl(1.0, std.math.inf_f128) == 1.0); + try testing.expect(fmodl.fmodl(1.0, -std.math.inf_f128) == 1.0); + try testing.expect(std.math.isNan(fmodl.fmodl(std.math.inf_f128, 1.0))); + try testing.expect(std.math.isNan(fmodl.fmodl(-std.math.inf_f128, 1.0))); +} + +test "fmodl" { + try test_fmodl(6.8, 4.0, 2.8); + try test_fmodl(6.8, -4.0, 2.8); + try test_fmodl(-6.8, 4.0, -2.8); + try test_fmodl(-6.8, -4.0, -2.8); + try test_fmodl(3.0, 2.0, 1.0); + try test_fmodl(-5.0, 3.0, -2.0); + try test_fmodl(3.0, 2.0, 1.0); + try test_fmodl(1.0, 2.0, 1.0); + try test_fmodl(0.0, 1.0, 0.0); + try test_fmodl(-0.0, 1.0, -0.0); + try test_fmodl(7046119.0, 5558362.0, 1487757.0); + try test_fmodl(9010357.0, 1957236.0, 1181413.0); + + // Denormals + const a: f128 = 0xedcb34a235253948765432134674p-16494; + const b: f128 = 0x5d2e38791cfbc0737402da5a9518p-16494; + const exp: f128 = 0x336ec3affb2db8618e4e7d5e1c44p-16494; + try test_fmodl(a, b, exp); + + try test_fmodl_nans(); + try test_fmodl_infs(); +} diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 0b6332f480..63466849a4 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -3338,6 +3338,32 @@ static void float_div_floor(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { } } +// c = a - b * trunc(a / b) +static float16_t zig_f16_rem(float16_t a, float16_t b) { + float16_t c; + c = f16_div(a, b); + c = f16_roundToInt(c, softfloat_round_minMag, false); + c = f16_mul(b, c); + c = f16_sub(a, c); + return c; +} + +// c = a - b * trunc(a / b) +static void zig_f128M_rem(const float128_t* a, const float128_t* b, float128_t* c) { + f128M_div(a, b, c); + f128M_roundToInt(c, softfloat_round_minMag, false, c); + f128M_mul(b, c, c); + f128M_sub(a, c, c); +} + +// c = a - b * trunc(a / b) +static void zig_extF80M_rem(const extFloat80_t* a, const extFloat80_t* b, extFloat80_t* c) { + extF80M_div(a, b, c); + extF80M_roundToInt(c, softfloat_round_minMag, false, c); + extF80M_mul(b, c, c); + extF80M_sub(a, c, c); +} + static void float_rem(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; @@ -3346,7 +3372,7 @@ static void float_rem(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { } else if (op1->type->id == ZigTypeIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: - out_val->data.x_f16 = f16_rem(op1->data.x_f16, op2->data.x_f16); + out_val->data.x_f16 = zig_f16_rem(op1->data.x_f16, op2->data.x_f16); return; case 32: out_val->data.x_f32 = fmodf(op1->data.x_f32, op2->data.x_f32); @@ -3355,10 +3381,10 @@ static void float_rem(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { out_val->data.x_f64 = fmod(op1->data.x_f64, op2->data.x_f64); return; case 80: - extF80M_rem(&op1->data.x_f80, &op2->data.x_f80, &out_val->data.x_f80); + zig_extF80M_rem(&op1->data.x_f80, &op2->data.x_f80, &out_val->data.x_f80); return; case 128: - f128M_rem(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); + zig_f128M_rem(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); diff --git a/src/value.zig b/src/value.zig index 1f93a828aa..89c57ad53d 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1482,8 +1482,7 @@ pub const Value = extern union { .float_64 => @rem(self.castTag(.float_64).?.data, 1) != 0, //.float_80 => @rem(self.castTag(.float_80).?.data, 1) != 0, .float_80 => @panic("TODO implement __remx in compiler-rt"), - //.float_128 => @rem(self.castTag(.float_128).?.data, 1) != 0, - .float_128 => @panic("TODO implement fmodl in compiler-rt"), + .float_128 => @rem(self.castTag(.float_128).?.data, 1) != 0, else => unreachable, }; @@ -2888,9 +2887,6 @@ pub const Value = extern union { return Value.Tag.float_80.create(arena, @rem(lhs_val, rhs_val)); }, 128 => { - if (true) { - @panic("TODO implement compiler_rt fmodl"); - } const lhs_val = lhs.toFloat(f128); const rhs_val = rhs.toFloat(f128); return Value.Tag.float_128.create(arena, @rem(lhs_val, rhs_val)); @@ -2925,9 +2921,6 @@ pub const Value = extern union { return Value.Tag.float_80.create(arena, @mod(lhs_val, rhs_val)); }, 128 => { - if (true) { - @panic("TODO implement compiler_rt fmodl"); - } const lhs_val = lhs.toFloat(f128); const rhs_val = rhs.toFloat(f128); return Value.Tag.float_128.create(arena, @mod(lhs_val, rhs_val)); diff --git a/test/behavior/math.zig b/test/behavior/math.zig index a9000353b8..fe5329ec81 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -782,8 +782,6 @@ test "comptime float rem int" { } test "remainder division" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - comptime try remdiv(f16); comptime try remdiv(f32); comptime try remdiv(f64); @@ -798,6 +796,64 @@ fn remdiv(comptime T: type) !void { try expect(@as(T, 1) == @as(T, 7) % @as(T, 3)); } +test "float remainder division using @rem" { + comptime try frem(f16); + comptime try frem(f32); + comptime try frem(f64); + comptime try frem(f128); + try frem(f16); + try frem(f32); + try frem(f64); + try frem(f128); +} + +fn frem(comptime T: type) !void { + const epsilon = switch (T) { + f16 => 1.0, + f32 => 0.001, + f64 => 0.00001, + f128 => 0.0000001, + else => unreachable, + }; + + try expect(std.math.fabs(@rem(@as(T, 6.9), @as(T, 4.0)) - @as(T, 2.9)) < epsilon); + try expect(std.math.fabs(@rem(@as(T, -6.9), @as(T, 4.0)) - @as(T, -2.9)) < epsilon); + try expect(std.math.fabs(@rem(@as(T, -5.0), @as(T, 3.0)) - @as(T, -2.0)) < epsilon); + try expect(std.math.fabs(@rem(@as(T, 3.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon); + try expect(std.math.fabs(@rem(@as(T, 1.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon); + try expect(std.math.fabs(@rem(@as(T, 0.0), @as(T, 1.0)) - @as(T, 0.0)) < epsilon); + try expect(std.math.fabs(@rem(@as(T, -0.0), @as(T, 1.0)) - @as(T, -0.0)) < epsilon); +} + +test "float modulo division using @mod" { + comptime try fmod(f16); + comptime try fmod(f32); + comptime try fmod(f64); + comptime try fmod(f128); + try fmod(f16); + try fmod(f32); + try fmod(f64); + try fmod(f128); +} + +fn fmod(comptime T: type) !void { + const epsilon = switch (T) { + f16 => 1.0, + f32 => 0.001, + f64 => 0.00001, + f128 => 0.0000001, + else => unreachable, + }; + + try expect(std.math.fabs(@mod(@as(T, 6.9), @as(T, 4.0)) - @as(T, 2.9)) < epsilon); + try expect(std.math.fabs(@mod(@as(T, -6.9), @as(T, 4.0)) - @as(T, 1.1)) < epsilon); + try expect(std.math.fabs(@mod(@as(T, -5.0), @as(T, 3.0)) - @as(T, 1.0)) < epsilon); + try expect(std.math.fabs(@mod(@as(T, 3.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon); + try expect(std.math.fabs(@mod(@as(T, 1.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon); + try expect(std.math.fabs(@mod(@as(T, 0.0), @as(T, 1.0)) - @as(T, 0.0)) < epsilon); + try expect(std.math.fabs(@mod(@as(T, -0.0), @as(T, 1.0)) - @as(T, -0.0)) < epsilon); +} + test "@sqrt" { try testSqrt(f64, 12.0); comptime try testSqrt(f64, 12.0); From 55fa349ad946aaa4e2ff5a5dc2a3f0d6bce64282 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 13 Feb 2022 22:16:22 +0700 Subject: [PATCH 0165/2031] Import SPARCv9 libunwind Import LLVM's D32450/D116857 patch to enable unwinding support on SPARCv9 systems. --- lib/libunwind/include/__libunwind_config.h | 7 + lib/libunwind/src/DwarfInstructions.hpp | 22 ++- lib/libunwind/src/DwarfParser.hpp | 25 ++- lib/libunwind/src/Registers.hpp | 187 +++++++++++++++++++++ lib/libunwind/src/UnwindCursor.hpp | 16 ++ lib/libunwind/src/UnwindRegistersRestore.S | 47 ++++++ lib/libunwind/src/UnwindRegistersSave.S | 60 ++++++- lib/libunwind/src/config.h | 11 +- lib/libunwind/src/libunwind.cpp | 2 + 9 files changed, 367 insertions(+), 10 deletions(-) diff --git a/lib/libunwind/include/__libunwind_config.h b/lib/libunwind/include/__libunwind_config.h index a50ba05388..438d6a705d 100644 --- a/lib/libunwind/include/__libunwind_config.h +++ b/lib/libunwind/include/__libunwind_config.h @@ -23,6 +23,7 @@ #define _LIBUNWIND_HIGHEST_DWARF_REGISTER_OR1K 32 #define _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS 65 #define _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC 31 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC64 31 #define _LIBUNWIND_HIGHEST_DWARF_REGISTER_HEXAGON 34 #define _LIBUNWIND_HIGHEST_DWARF_REGISTER_RISCV 64 #define _LIBUNWIND_HIGHEST_DWARF_REGISTER_VE 143 @@ -125,6 +126,11 @@ # error "Unsupported MIPS ABI and/or environment" # endif # define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS +# elif defined(__sparc__) && defined(__arch64__) +# define _LIBUNWIND_TARGET_SPARC64 1 +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC64 +# define _LIBUNWIND_CONTEXT_SIZE 33 +# define _LIBUNWIND_CURSOR_SIZE 45 # elif defined(__sparc__) #define _LIBUNWIND_TARGET_SPARC 1 #define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC @@ -165,6 +171,7 @@ # define _LIBUNWIND_TARGET_MIPS_O32 1 # define _LIBUNWIND_TARGET_MIPS_NEWABI 1 # define _LIBUNWIND_TARGET_SPARC 1 +# define _LIBUNWIND_TARGET_SPARC64 1 # define _LIBUNWIND_TARGET_HEXAGON 1 # define _LIBUNWIND_TARGET_RISCV 1 # define _LIBUNWIND_TARGET_VE 1 diff --git a/lib/libunwind/src/DwarfInstructions.hpp b/lib/libunwind/src/DwarfInstructions.hpp index 686c6be0d8..60182e4fef 100644 --- a/lib/libunwind/src/DwarfInstructions.hpp +++ b/lib/libunwind/src/DwarfInstructions.hpp @@ -67,7 +67,7 @@ private: return (pint_t)((sint_t)registers.getRegister((int)prolog.cfaRegister) + prolog.cfaRegisterOffset); if (prolog.cfaExpression != 0) - return evaluateExpression((pint_t)prolog.cfaExpression, addressSpace, + return evaluateExpression((pint_t)prolog.cfaExpression, addressSpace, registers, 0); assert(0 && "getCFA(): unknown location"); __builtin_unreachable(); @@ -75,6 +75,14 @@ private: }; +template +auto getSparcWCookie(const R &r, int) -> decltype(r.getWCookie()) { + return r.getWCookie(); +} +template uint64_t getSparcWCookie(const R &, long) { + return 0; +} + template typename A::pint_t DwarfInstructions::getSavedRegister( A &addressSpace, const R ®isters, pint_t cfa, @@ -83,6 +91,10 @@ typename A::pint_t DwarfInstructions::getSavedRegister( case CFI_Parser::kRegisterInCFA: return (pint_t)addressSpace.getRegister(cfa + (pint_t)savedReg.value); + case CFI_Parser::kRegisterInCFADecrypt: // sparc64 specific + return addressSpace.getP(cfa + (pint_t)savedReg.value) ^ + getSparcWCookie(registers, 0); + case CFI_Parser::kRegisterAtExpression: return (pint_t)addressSpace.getRegister(evaluateExpression( (pint_t)savedReg.value, addressSpace, registers, cfa)); @@ -121,6 +133,7 @@ double DwarfInstructions::getSavedFloatRegister( case CFI_Parser::kRegisterUndefined: case CFI_Parser::kRegisterOffsetFromCFA: case CFI_Parser::kRegisterInRegister: + case CFI_Parser::kRegisterInCFADecrypt: // FIX ME break; } @@ -145,6 +158,7 @@ v128 DwarfInstructions::getSavedVectorRegister( case CFI_Parser::kRegisterUndefined: case CFI_Parser::kRegisterOffsetFromCFA: case CFI_Parser::kRegisterInRegister: + case CFI_Parser::kRegisterInCFADecrypt: // FIX ME break; } @@ -249,6 +263,12 @@ int DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, } #endif +#if defined(_LIBUNWIND_TARGET_SPARC64) + // Skip call site instruction and delay slot + if (R::getArch() == REGISTERS_SPARC64) + returnAddress += 8; +#endif + #if defined(_LIBUNWIND_TARGET_PPC64) #define PPC64_ELFV1_R2_LOAD_INST_ENCODING 0xe8410028u // ld r2,40(r1) #define PPC64_ELFV1_R2_OFFSET 40 diff --git a/lib/libunwind/src/DwarfParser.hpp b/lib/libunwind/src/DwarfParser.hpp index de0eb6de9d..f0aa4085d3 100644 --- a/lib/libunwind/src/DwarfParser.hpp +++ b/lib/libunwind/src/DwarfParser.hpp @@ -71,6 +71,7 @@ public: kRegisterUnused, kRegisterUndefined, kRegisterInCFA, + kRegisterInCFADecrypt, // sparc64 specific kRegisterOffsetFromCFA, kRegisterInRegister, kRegisterAtExpression, @@ -723,7 +724,8 @@ bool CFI_Parser::parseFDEInstructions(A &addressSpace, "DW_CFA_GNU_negative_offset_extended(%" PRId64 ")\n", offset); break; -#if defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_SPARC) +#if defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_SPARC) || \ + defined(_LIBUNWIND_TARGET_SPARC64) // The same constant is used to represent different instructions on // AArch64 (negate_ra_state) and SPARC (window_save). static_assert(DW_CFA_AARCH64_negate_ra_state == DW_CFA_GNU_window_save, @@ -757,8 +759,29 @@ bool CFI_Parser::parseFDEInstructions(A &addressSpace, } break; #endif + +#if defined(_LIBUNWIND_TARGET_SPARC64) + // case DW_CFA_GNU_window_save: + case REGISTERS_SPARC64: + // Don't save %o0-%o7 on sparc64. + // https://reviews.llvm.org/D32450#736405 + + for (reg = UNW_SPARC_L0; reg <= UNW_SPARC_I7; reg++) { + if (reg == UNW_SPARC_I7) + results->setRegister( + reg, kRegisterInCFADecrypt, + ((int64_t)reg - UNW_SPARC_L0) * sizeof(pint_t), initialState); + else + results->setRegister( + reg, kRegisterInCFA, + ((int64_t)reg - UNW_SPARC_L0) * sizeof(pint_t), initialState); + } + _LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_window_save\n"); + break; +#endif } break; + #else (void)arch; #endif diff --git a/lib/libunwind/src/Registers.hpp b/lib/libunwind/src/Registers.hpp index aea84cc227..e37021a7c7 100644 --- a/lib/libunwind/src/Registers.hpp +++ b/lib/libunwind/src/Registers.hpp @@ -34,6 +34,7 @@ enum { REGISTERS_MIPS_O32, REGISTERS_MIPS_NEWABI, REGISTERS_SPARC, + REGISTERS_SPARC64, REGISTERS_HEXAGON, REGISTERS_RISCV, REGISTERS_VE, @@ -3546,6 +3547,192 @@ inline const char *Registers_sparc::getRegisterName(int regNum) { } #endif // _LIBUNWIND_TARGET_SPARC + +#if defined(_LIBUNWIND_TARGET_SPARC64) +/// Registers_sparc64 holds the register state of a thread in a 64-bit +/// sparc process. +class _LIBUNWIND_HIDDEN Registers_sparc64 { +public: + Registers_sparc64() = default; + Registers_sparc64(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC64; + } + static int getArch() { return REGISTERS_SPARC64; } + + uint64_t getSP() const { return _registers.__regs[UNW_SPARC_O6] + 2047; } + void setSP(uint64_t value) { _registers.__regs[UNW_SPARC_O6] = value - 2047; } + uint64_t getIP() const { return _registers.__regs[UNW_SPARC_O7]; } + void setIP(uint64_t value) { _registers.__regs[UNW_SPARC_O7] = value; } + uint64_t getWCookie() const { return _wcookie; } + +private: + struct sparc64_thread_state_t { + uint64_t __regs[32]; + }; + + sparc64_thread_state_t _registers{}; + uint64_t _wcookie = 0; +}; + +inline Registers_sparc64::Registers_sparc64(const void *registers) { + static_assert((check_fit::does_fit), + "sparc64 registers do not fit into unw_context_t"); + memcpy(&_registers, registers, sizeof(_registers)); + memcpy(&_wcookie, + static_cast(registers) + sizeof(_registers), + sizeof(_wcookie)); +} + +inline bool Registers_sparc64::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum <= UNW_SPARC_I7) + return true; + return false; +} + +inline uint64_t Registers_sparc64::getRegister(int regNum) const { + if (regNum >= UNW_SPARC_G0 && regNum <= UNW_SPARC_I7) + return _registers.__regs[regNum]; + + switch (regNum) { + case UNW_REG_IP: + return _registers.__regs[UNW_SPARC_O7]; + case UNW_REG_SP: + return _registers.__regs[UNW_SPARC_O6] + 2047; + } + _LIBUNWIND_ABORT("unsupported sparc64 register"); +} + +inline void Registers_sparc64::setRegister(int regNum, uint64_t value) { + if (regNum >= UNW_SPARC_G0 && regNum <= UNW_SPARC_I7) { + _registers.__regs[regNum] = value; + return; + } + + switch (regNum) { + case UNW_REG_IP: + _registers.__regs[UNW_SPARC_O7] = value; + return; + case UNW_REG_SP: + _registers.__regs[UNW_SPARC_O6] = value - 2047; + return; + } + _LIBUNWIND_ABORT("unsupported sparc64 register"); +} + +inline bool Registers_sparc64::validFloatRegister(int) const { return false; } + +inline double Registers_sparc64::getFloatRegister(int) const { + _LIBUNWIND_ABORT("no sparc64 float registers"); +} + +inline void Registers_sparc64::setFloatRegister(int, double) { + _LIBUNWIND_ABORT("no sparc64 float registers"); +} + +inline bool Registers_sparc64::validVectorRegister(int) const { return false; } + +inline v128 Registers_sparc64::getVectorRegister(int) const { + _LIBUNWIND_ABORT("no sparc64 vector registers"); +} + +inline void Registers_sparc64::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("no sparc64 vector registers"); +} + +inline const char *Registers_sparc64::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "pc"; + case UNW_SPARC_G0: + return "g0"; + case UNW_SPARC_G1: + return "g1"; + case UNW_SPARC_G2: + return "g2"; + case UNW_SPARC_G3: + return "g3"; + case UNW_SPARC_G4: + return "g4"; + case UNW_SPARC_G5: + return "g5"; + case UNW_SPARC_G6: + return "g6"; + case UNW_SPARC_G7: + return "g7"; + case UNW_SPARC_O0: + return "o0"; + case UNW_SPARC_O1: + return "o1"; + case UNW_SPARC_O2: + return "o2"; + case UNW_SPARC_O3: + return "o3"; + case UNW_SPARC_O4: + return "o4"; + case UNW_SPARC_O5: + return "o5"; + case UNW_REG_SP: + case UNW_SPARC_O6: + return "o6"; + case UNW_SPARC_O7: + return "o7"; + case UNW_SPARC_L0: + return "l0"; + case UNW_SPARC_L1: + return "l1"; + case UNW_SPARC_L2: + return "l2"; + case UNW_SPARC_L3: + return "l3"; + case UNW_SPARC_L4: + return "l4"; + case UNW_SPARC_L5: + return "l5"; + case UNW_SPARC_L6: + return "l6"; + case UNW_SPARC_L7: + return "l7"; + case UNW_SPARC_I0: + return "i0"; + case UNW_SPARC_I1: + return "i1"; + case UNW_SPARC_I2: + return "i2"; + case UNW_SPARC_I3: + return "i3"; + case UNW_SPARC_I4: + return "i4"; + case UNW_SPARC_I5: + return "i5"; + case UNW_SPARC_I6: + return "i6"; + case UNW_SPARC_I7: + return "i7"; + default: + return "unknown register"; + } +} +#endif // _LIBUNWIND_TARGET_SPARC64 + #if defined(_LIBUNWIND_TARGET_HEXAGON) /// Registers_hexagon holds the register state of a thread in a Hexagon QDSP6 /// process. diff --git a/lib/libunwind/src/UnwindCursor.hpp b/lib/libunwind/src/UnwindCursor.hpp index 757d9808a9..d63dff5e61 100644 --- a/lib/libunwind/src/UnwindCursor.hpp +++ b/lib/libunwind/src/UnwindCursor.hpp @@ -1020,6 +1020,10 @@ private: int stepWithCompactEncoding(Registers_sparc &) { return UNW_EINVAL; } #endif +#if defined(_LIBUNWIND_TARGET_SPARC64) + int stepWithCompactEncoding(Registers_sparc64 &) { return UNW_EINVAL; } +#endif + #if defined (_LIBUNWIND_TARGET_RISCV) int stepWithCompactEncoding(Registers_riscv &) { return UNW_EINVAL; @@ -1092,6 +1096,12 @@ private: bool compactSaysUseDwarf(Registers_sparc &, uint32_t *) const { return true; } #endif +#if defined(_LIBUNWIND_TARGET_SPARC64) + bool compactSaysUseDwarf(Registers_sparc64 &, uint32_t *) const { + return true; + } +#endif + #if defined (_LIBUNWIND_TARGET_RISCV) bool compactSaysUseDwarf(Registers_riscv &, uint32_t *) const { return true; @@ -1170,6 +1180,12 @@ private: compact_unwind_encoding_t dwarfEncoding(Registers_sparc &) const { return 0; } #endif +#if defined(_LIBUNWIND_TARGET_SPARC64) + compact_unwind_encoding_t dwarfEncoding(Registers_sparc64 &) const { + return 0; + } +#endif + #if defined (_LIBUNWIND_TARGET_RISCV) compact_unwind_encoding_t dwarfEncoding(Registers_riscv &) const { return 0; diff --git a/lib/libunwind/src/UnwindRegistersRestore.S b/lib/libunwind/src/UnwindRegistersRestore.S index d8bf1adee4..bbdabcc355 100644 --- a/lib/libunwind/src/UnwindRegistersRestore.S +++ b/lib/libunwind/src/UnwindRegistersRestore.S @@ -1050,6 +1050,53 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind21Registers_mips_newabi6jumptoEv) ld $4, (8 * 4)($4) .set pop +#elif defined(__sparc__) && defined(__arch64__) + +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind17Registers_sparc646jumptoEv) +// +// void libunwind::Registers_sparc64::jumpto() +// +// On entry: +// thread_state pointer is in %o0 +// + .register %g2, #scratch + .register %g3, #scratch + .register %g6, #scratch + .register %g7, #scratch + flushw + ldx [%o0 + 0x08], %g1 + ldx [%o0 + 0x10], %g2 + ldx [%o0 + 0x18], %g3 + ldx [%o0 + 0x20], %g4 + ldx [%o0 + 0x28], %g5 + ldx [%o0 + 0x30], %g6 + ldx [%o0 + 0x38], %g7 + ldx [%o0 + 0x48], %o1 + ldx [%o0 + 0x50], %o2 + ldx [%o0 + 0x58], %o3 + ldx [%o0 + 0x60], %o4 + ldx [%o0 + 0x68], %o5 + ldx [%o0 + 0x70], %o6 + ldx [%o0 + 0x78], %o7 + ldx [%o0 + 0x80], %l0 + ldx [%o0 + 0x88], %l1 + ldx [%o0 + 0x90], %l2 + ldx [%o0 + 0x98], %l3 + ldx [%o0 + 0xa0], %l4 + ldx [%o0 + 0xa8], %l5 + ldx [%o0 + 0xb0], %l6 + ldx [%o0 + 0xb8], %l7 + ldx [%o0 + 0xc0], %i0 + ldx [%o0 + 0xc8], %i1 + ldx [%o0 + 0xd0], %i2 + ldx [%o0 + 0xd8], %i3 + ldx [%o0 + 0xe0], %i4 + ldx [%o0 + 0xe8], %i5 + ldx [%o0 + 0xf0], %i6 + ldx [%o0 + 0xf8], %i7 + jmp %o7 + ldx [%o0 + 0x40], %o0 + #elif defined(__sparc__) // diff --git a/lib/libunwind/src/UnwindRegistersSave.S b/lib/libunwind/src/UnwindRegistersSave.S index f66dc532c2..b6fca2be27 100644 --- a/lib/libunwind/src/UnwindRegistersSave.S +++ b/lib/libunwind/src/UnwindRegistersSave.S @@ -766,7 +766,7 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) @ @ On entry: @ thread_state pointer is in r0 -@ +@ @ Per EHABI #4.7 this only saves the core integer registers. @ EHABI #7.4.5 notes that in general all VRS registers should be restored @ however this is very hard to do for VFP registers because it is unknown @@ -996,6 +996,64 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) jumpr r31 +#elif defined(__sparc__) && defined(__arch64__) + +# +# extern int __unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# thread_state pointer is in %o0 +# +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + .register %g2, #scratch + .register %g3, #scratch + .register %g6, #scratch + .register %g7, #scratch + stx %g1, [%o0 + 0x08] + stx %g2, [%o0 + 0x10] + stx %g3, [%o0 + 0x18] + stx %g4, [%o0 + 0x20] + stx %g5, [%o0 + 0x28] + stx %g6, [%o0 + 0x30] + stx %g7, [%o0 + 0x38] + stx %o0, [%o0 + 0x40] + stx %o1, [%o0 + 0x48] + stx %o2, [%o0 + 0x50] + stx %o3, [%o0 + 0x58] + stx %o4, [%o0 + 0x60] + stx %o5, [%o0 + 0x68] + stx %o6, [%o0 + 0x70] + stx %o7, [%o0 + 0x78] + stx %l0, [%o0 + 0x80] + stx %l1, [%o0 + 0x88] + stx %l2, [%o0 + 0x90] + stx %l3, [%o0 + 0x98] + stx %l4, [%o0 + 0xa0] + stx %l5, [%o0 + 0xa8] + stx %l6, [%o0 + 0xb0] + stx %l7, [%o0 + 0xb8] + stx %i0, [%o0 + 0xc0] + stx %i1, [%o0 + 0xc8] + stx %i2, [%o0 + 0xd0] + stx %i3, [%o0 + 0xd8] + stx %i4, [%o0 + 0xe0] + stx %i5, [%o0 + 0xe8] + stx %i6, [%o0 + 0xf0] + stx %i7, [%o0 + 0xf8] + + # save StackGhost cookie + mov %i7, %g4 + save %sp, -176, %sp + # register window flush necessary even without StackGhost + flushw + restore + ldx [%sp + 2047 + 0x78], %g5 + xor %g4, %g5, %g4 + stx %g4, [%o0 + 0x100] + retl + # return UNW_ESUCCESS + clr %o0 + #elif defined(__sparc__) # diff --git a/lib/libunwind/src/config.h b/lib/libunwind/src/config.h index 2ab9d2f5e0..f25b390c84 100644 --- a/lib/libunwind/src/config.h +++ b/lib/libunwind/src/config.h @@ -109,13 +109,10 @@ #define _LIBUNWIND_SUPPORT_FRAME_APIS #endif -#if defined(__i386__) || defined(__x86_64__) || \ - defined(__ppc__) || defined(__ppc64__) || defined(__powerpc64__) || \ - (!defined(__APPLE__) && defined(__arm__)) || \ - defined(__aarch64__) || \ - defined(__mips__) || \ - defined(__riscv) || \ - defined(__hexagon__) +#if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) || \ + (!defined(__APPLE__) && defined(__arm__)) || defined(__aarch64__) || \ + defined(__mips__) || defined(__riscv) || defined(__hexagon__) || \ + defined(__sparc__) #if !defined(_LIBUNWIND_BUILD_SJLJ_APIS) #define _LIBUNWIND_BUILD_ZERO_COST_APIS #endif diff --git a/lib/libunwind/src/libunwind.cpp b/lib/libunwind/src/libunwind.cpp index dca403c863..ac01246e86 100644 --- a/lib/libunwind/src/libunwind.cpp +++ b/lib/libunwind/src/libunwind.cpp @@ -69,6 +69,8 @@ _LIBUNWIND_HIDDEN int __unw_init_local(unw_cursor_t *cursor, # define REGISTER_KIND Registers_mips_newabi #elif defined(__mips__) # warning The MIPS architecture is not supported with this ABI and environment! +#elif defined(__sparc__) && defined(__arch64__) +# define REGISTER_KIND Registers_sparc64 #elif defined(__sparc__) # define REGISTER_KIND Registers_sparc #elif defined(__riscv) From 2a73700c0fc177fb8781b277a6ba5017dce875b9 Mon Sep 17 00:00:00 2001 From: Tw Date: Sat, 29 Jan 2022 17:44:13 +0800 Subject: [PATCH 0166/2031] Fix preadv/pwritev bug on 64bit platform Signed-off-by: Tw --- lib/std/os/linux.zig | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index c1591f7ea1..cc9a8bc85c 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -440,26 +440,30 @@ pub fn read(fd: i32, buf: [*]u8, count: usize) usize { } pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: i64) usize { - const offset_halves = splitValueLE64(offset); + const offset_u = @bitCast(u64, offset); return syscall5( .preadv, @bitCast(usize, @as(isize, fd)), @ptrToInt(iov), count, - offset_halves[0], - offset_halves[1], + // Kernel expects the offset is splitted into largest natural word-size. + // See following link for detail: + // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=601cc11d054ae4b5e9b5babec3d8e4667a2cb9b5 + @truncate(usize, offset_u), + if (usize_bits < 64) @truncate(usize, offset_u >> 32) else 0, ); } pub fn preadv2(fd: i32, iov: [*]const iovec, count: usize, offset: i64, flags: kernel_rwf) usize { - const offset_halves = splitValue64(offset); + const offset_u = @bitCast(u64, offset); return syscall6( .preadv2, @bitCast(usize, @as(isize, fd)), @ptrToInt(iov), count, - offset_halves[0], - offset_halves[1], + // See comments in preadv + @truncate(usize, offset_u), + if (usize_bits < 64) @truncate(usize, offset_u >> 32) else 0, flags, ); } @@ -473,26 +477,28 @@ pub fn writev(fd: i32, iov: [*]const iovec_const, count: usize) usize { } pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: i64) usize { - const offset_halves = splitValueLE64(offset); + const offset_u = @bitCast(u64, offset); return syscall5( .pwritev, @bitCast(usize, @as(isize, fd)), @ptrToInt(iov), count, - offset_halves[0], - offset_halves[1], + // See comments in preadv + @truncate(usize, offset_u), + if (usize_bits < 64) @truncate(usize, offset_u >> 32) else 0, ); } pub fn pwritev2(fd: i32, iov: [*]const iovec_const, count: usize, offset: i64, flags: kernel_rwf) usize { - const offset_halves = splitValue64(offset); + const offset_u = @bitCast(u64, offset); return syscall6( .pwritev2, @bitCast(usize, @as(isize, fd)), @ptrToInt(iov), count, - offset_halves[0], - offset_halves[1], + // See comments in preadv + @truncate(usize, offset_u), + if (usize_bits < 64) @truncate(usize, offset_u >> 32) else 0, flags, ); } From 7b72fc6bbc5554643bc27933310899e32783b81b Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Sun, 13 Feb 2022 11:54:37 -0700 Subject: [PATCH 0167/2031] Add `abi_size` parameter to read/writeTwosComplement Big-int functions were updated to respect the provided abi_size, rather than inferring a potentially incorrect abi_size implicitly. In combination with the convention that any required padding bits are added on the MSB end, this means that exotic integers can potentially have a well-defined memory layout. --- lib/std/math/big/int.zig | 141 +++++++++++++++++----------------- lib/std/math/big/int_test.zig | 95 ++++++++++++++++++++++- src/value.zig | 9 ++- 3 files changed, 166 insertions(+), 79 deletions(-) diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 1c6404fb3c..731f4b5456 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -1624,18 +1624,16 @@ pub const Mutable = struct { } /// Read the value of `x` from `buffer` - /// Asserts that `buffer` and `bit_count` are large enough to store the value. + /// Asserts that `buffer`, `abi_size`, and `bit_count` are large enough to store the value. /// - /// For integers with a well-defined layout (e.g. all power-of-two integers), this function - /// reads from `buffer` as if it were the contents of @ptrCast([]const u8, &x), where the - /// slice length is taken to be @sizeOf(std.meta.Int(signedness, )) - /// - /// For integers with a non-well-defined layout, `buffer` must have been created by - /// writeTwosComplement. + /// The contents of `buffer` are interpreted as if they were the contents of + /// @ptrCast(*[abi_size]const u8, &x). Byte ordering is determined by `endian` + /// and any required padding bits are expected on the MSB end. pub fn readTwosComplement( x: *Mutable, buffer: []const u8, bit_count: usize, + abi_size: usize, endian: Endian, signedness: Signedness, ) void { @@ -1646,20 +1644,18 @@ pub const Mutable = struct { return; } - // byte_count is the total amount of bytes to read from buffer - var byte_count = @sizeOf(Limb) * (bit_count / @bitSizeOf(Limb)); - if (bit_count % @bitSizeOf(Limb) != 0) { // Round up to a power-of-two integer <= Limb - byte_count += (std.math.ceilPowerOfTwoAssert(usize, bit_count % @bitSizeOf(Limb)) + 7) / 8; - } - - const limb_count = calcTwosCompLimbCount(8 * byte_count); + // byte_count is our total read size: it cannot exceed abi_size, + // but may be less as long as it includes the required bits + const limb_count = calcTwosCompLimbCount(bit_count); + const byte_count = std.math.min(abi_size, @sizeOf(Limb) * limb_count); + assert(8 * byte_count >= bit_count); // Check whether the input is negative var positive = true; if (signedness == .signed) { var last_byte = switch (endian) { .Little => ((bit_count + 7) / 8) - 1, - .Big => byte_count - ((bit_count + 7) / 8), + .Big => abi_size - ((bit_count + 7) / 8), }; const sign_bit = @as(u8, 1) << @intCast(u3, (bit_count - 1) % 8); @@ -1672,7 +1668,7 @@ pub const Mutable = struct { while (limb_index < bit_count / @bitSizeOf(Limb)) : (limb_index += 1) { var buf_index = switch (endian) { .Little => @sizeOf(Limb) * limb_index, - .Big => byte_count - (limb_index + 1) * @sizeOf(Limb), + .Big => abi_size - (limb_index + 1) * @sizeOf(Limb), }; const limb_buf = @ptrCast(*const [@sizeOf(Limb)]u8, buffer[buf_index..]); @@ -1683,32 +1679,34 @@ pub const Mutable = struct { x.limbs[limb_index] = limb; } - // Copy any remaining bytes, using the nearest power-of-two integer that is large enough - const bits_left = @intCast(Log2Limb, bit_count % @bitSizeOf(Limb)); - if (bits_left != 0) { - const bytes_read = limb_index * @sizeOf(Limb); - const bytes_left = byte_count - bytes_read; - var buffer_left = switch (endian) { - .Little => buffer[bytes_read..], - .Big => buffer[0..], - }; + // Copy the remaining N bytes (N <= @sizeOf(Limb)) + var bytes_read = limb_index * @sizeOf(Limb); + if (bytes_read != byte_count) { + var limb: Limb = 0; - var limb = @intCast(Limb, blk: { - // zig fmt: off - if (bytes_left == 1) break :blk mem.readInt( u8, buffer_left[0.. 1], endian); - if (bytes_left == 2) break :blk mem.readInt( u16, buffer_left[0.. 2], endian); - if (bytes_left == 4) break :blk mem.readInt( u32, buffer_left[0.. 4], endian); - if (bytes_left == 8) break :blk mem.readInt( u64, buffer_left[0.. 8], endian); - if (bytes_left == 16) break :blk mem.readInt(u128, buffer_left[0..16], endian); - // zig fmt: on - unreachable; - }); + while (bytes_read != byte_count) { + const read_size = std.math.floorPowerOfTwo(usize, byte_count - bytes_read); + var int_buffer = switch (endian) { + .Little => buffer[bytes_read..], + .Big => buffer[(abi_size - bytes_read - read_size)..], + }; + limb |= @intCast(Limb, switch (read_size) { + 1 => mem.readInt(u8, int_buffer[0..1], endian), + 2 => mem.readInt(u16, int_buffer[0..2], endian), + 4 => mem.readInt(u32, int_buffer[0..4], endian), + 8 => mem.readInt(u64, int_buffer[0..8], endian), + 16 => mem.readInt(u128, int_buffer[0..16], endian), + else => unreachable, + }) << @intCast(Log2Limb, 8 * (bytes_read % @sizeOf(Limb))); + bytes_read += read_size; + } // 2's complement (bitwise not, then add carry bit) if (!positive) _ = @addWithOverflow(Limb, ~limb, carry, &limb); // Mask off any unused bits - const mask = (@as(Limb, 1) << bits_left) -% 1; // 0b0..01..1 with (bits_left) trailing ones + const valid_bits = @intCast(Log2Limb, bit_count % @bitSizeOf(Limb)); + const mask = (@as(Limb, 1) << valid_bits) -% 1; // 0b0..01..1 with (valid_bits_in_limb) trailing ones limb &= mask; x.limbs[limb_count - 1] = limb; @@ -2076,21 +2074,16 @@ pub const Const = struct { } /// Write the value of `x` into `buffer` - /// Asserts that `buffer` and `bit_count` are large enough to store the value. + /// Asserts that `buffer`, `abi_size`, and `bit_count` are large enough to store the value. /// - /// For integers with a well-defined layout (e.g. all power-of-two integers), this function - /// can be thought of as writing to `buffer` the contents of @ptrCast([]const u8, &x), - /// where the slice length is taken to be @sizeOf(std.meta.Int(_,)) - /// - /// For integers with a non-well-defined layout, the only requirement is that readTwosComplement - /// on the same buffer creates an equivalent big integer. - pub fn writeTwosComplement(x: Const, buffer: []u8, bit_count: usize, endian: Endian) void { - if (bit_count == 0) return; + /// `buffer` is filled so that its contents match what would be observed via + /// @ptrCast(*[abi_size]const u8, &x). Byte ordering is determined by `endian`, + /// and any required padding bits are added on the MSB end. + pub fn writeTwosComplement(x: Const, buffer: []u8, bit_count: usize, abi_size: usize, endian: Endian) void { - var byte_count = @sizeOf(Limb) * (bit_count / @bitSizeOf(Limb)); - if (bit_count % @bitSizeOf(Limb) != 0) { - byte_count += (std.math.ceilPowerOfTwoAssert(usize, bit_count % @bitSizeOf(Limb)) + 7) / 8; - } + // byte_count is our total write size + const byte_count = abi_size; + assert(8 * byte_count >= bit_count); assert(buffer.len >= byte_count); assert(x.fitsInTwosComp(if (x.positive) .unsigned else .signed, bit_count)); @@ -2100,7 +2093,7 @@ pub const Const = struct { while (limb_index < byte_count / @sizeOf(Limb)) : (limb_index += 1) { var buf_index = switch (endian) { .Little => @sizeOf(Limb) * limb_index, - .Big => byte_count - (limb_index + 1) * @sizeOf(Limb), + .Big => abi_size - (limb_index + 1) * @sizeOf(Limb), }; var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0; @@ -2111,32 +2104,36 @@ pub const Const = struct { mem.writeInt(Limb, limb_buf, limb, endian); } - // Copy any remaining bytes - if (byte_count % @sizeOf(Limb) != 0) { - const bytes_read = limb_index * @sizeOf(Limb); - const bytes_left = byte_count - bytes_read; - var buffer_left = switch (endian) { - .Little => buffer[bytes_read..], - .Big => buffer[0..], - }; - + // Copy the remaining N bytes (N < @sizeOf(Limb)) + var bytes_written = limb_index * @sizeOf(Limb); + if (bytes_written != byte_count) { var limb: Limb = if (limb_index < x.limbs.len) x.limbs[limb_index] else 0; // 2's complement (bitwise not, then add carry bit) if (!x.positive) _ = @addWithOverflow(Limb, ~limb, carry, &limb); - if (bytes_left == 1) { - mem.writeInt(u8, buffer_left[0..1], @truncate(u8, limb), endian); - } else if (@sizeOf(Limb) > 1 and bytes_left == 2) { - mem.writeInt(u16, buffer_left[0..2], @truncate(u16, limb), endian); - } else if (@sizeOf(Limb) > 2 and bytes_left == 4) { - mem.writeInt(u32, buffer_left[0..4], @truncate(u32, limb), endian); - } else if (@sizeOf(Limb) > 4 and bytes_left == 8) { - mem.writeInt(u64, buffer_left[0..8], @truncate(u64, limb), endian); - } else if (@sizeOf(Limb) > 8 and bytes_left == 16) { - mem.writeInt(u128, buffer_left[0..16], @truncate(u128, limb), endian); - } else if (@sizeOf(Limb) > 16) { - @compileError("@sizeOf(Limb) exceeded supported range"); - } else unreachable; + while (bytes_written != byte_count) { + const write_size = std.math.floorPowerOfTwo(usize, byte_count - bytes_written); + var int_buffer = switch (endian) { + .Little => buffer[bytes_written..], + .Big => buffer[(abi_size - bytes_written - write_size)..], + }; + + if (write_size == 1) { + mem.writeInt(u8, int_buffer[0..1], @truncate(u8, limb), endian); + } else if (@sizeOf(Limb) >= 2 and write_size == 2) { + mem.writeInt(u16, int_buffer[0..2], @truncate(u16, limb), endian); + } else if (@sizeOf(Limb) >= 4 and write_size == 4) { + mem.writeInt(u32, int_buffer[0..4], @truncate(u32, limb), endian); + } else if (@sizeOf(Limb) >= 8 and write_size == 8) { + mem.writeInt(u64, int_buffer[0..8], @truncate(u64, limb), endian); + } else if (@sizeOf(Limb) >= 16 and write_size == 16) { + mem.writeInt(u128, int_buffer[0..16], @truncate(u128, limb), endian); + } else if (@sizeOf(Limb) >= 32) { + @compileError("@sizeOf(Limb) exceeded supported range"); + } else unreachable; + limb >>= @intCast(Log2Limb, 8 * write_size); + bytes_written += write_size; + } } } diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index f6f210f56c..69600425a4 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -2498,16 +2498,103 @@ test "big int conversion read/write twos complement" { defer testing.allocator.free(buffer1); const endians = [_]std.builtin.Endian{ .Little, .Big }; + const abi_size = 64; for (endians) |endian| { // Writing to buffer and back should not change anything - a.toConst().writeTwosComplement(buffer1, 493, endian); - m.readTwosComplement(buffer1, 493, endian, .unsigned); + a.toConst().writeTwosComplement(buffer1, 493, abi_size, endian); + m.readTwosComplement(buffer1, 493, abi_size, endian, .unsigned); try testing.expect(m.toConst().order(a.toConst()) == .eq); // Equivalent to @bitCast(i493, @as(u493, intMax(u493)) - a.toConst().writeTwosComplement(buffer1, 493, endian); - m.readTwosComplement(buffer1, 493, endian, .signed); + a.toConst().writeTwosComplement(buffer1, 493, abi_size, endian); + m.readTwosComplement(buffer1, 493, abi_size, endian, .signed); try testing.expect(m.toConst().orderAgainstScalar(-1) == .eq); } } + +test "big int conversion read twos complement with padding" { + var a = try Managed.initSet(testing.allocator, 0x01_02030405_06070809_0a0b0c0d); + defer a.deinit(); + + var buffer1 = try testing.allocator.alloc(u8, 16); + defer testing.allocator.free(buffer1); + @memset(buffer1.ptr, 0xaa, buffer1.len); + + // writeTwosComplement: + // (1) should not write beyond buffer[0..abi_size] + // (2) should correctly order bytes based on the provided endianness + // (3) should sign-extend any bits from bit_count to 8 * abi_size + + var bit_count: usize = 12 * 8 + 1; + a.toConst().writeTwosComplement(buffer1, bit_count, 13, .Little); + try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0xaa, 0xaa, 0xaa })); + a.toConst().writeTwosComplement(buffer1, bit_count, 13, .Big); + try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xaa, 0xaa, 0xaa })); + a.toConst().writeTwosComplement(buffer1, bit_count, 16, .Little); + try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0, 0x0, 0x0 })); + a.toConst().writeTwosComplement(buffer1, bit_count, 16, .Big); + try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0x0, 0x0, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd })); + + @memset(buffer1.ptr, 0xaa, buffer1.len); + try a.set(-0x01_02030405_06070809_0a0b0c0d); + bit_count = 12 * 8 + 2; + + a.toConst().writeTwosComplement(buffer1, bit_count, 13, .Little); + try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xaa, 0xaa, 0xaa })); + a.toConst().writeTwosComplement(buffer1, bit_count, 13, .Big); + try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3, 0xaa, 0xaa, 0xaa })); + a.toConst().writeTwosComplement(buffer1, bit_count, 16, .Little); + try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xff, 0xff })); + a.toConst().writeTwosComplement(buffer1, bit_count, 16, .Big); + try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3 })); +} + +test "big int conversion write twos complement with padding" { + var a = try Managed.initSet(testing.allocator, 0x01_ffffffff_ffffffff_ffffffff); + defer a.deinit(); + + var m = a.toMutable(); + + // readTwosComplement: + // (1) should not read beyond buffer[0..abi_size] + // (2) should correctly interpret bytes based on the provided endianness + // (3) should ignore any bits from bit_count to 8 * abi_size + + var bit_count: usize = 12 * 8 + 1; + var buffer: []const u8 = undefined; + + buffer = &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0xb }; + m.readTwosComplement(buffer, bit_count, 13, .Little, .unsigned); + try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq); + + buffer = &[_]u8{ 0xb, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd }; + m.readTwosComplement(buffer, bit_count, 13, .Big, .unsigned); + try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq); + + buffer = &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0xab, 0xaa, 0xaa, 0xaa }; + m.readTwosComplement(buffer, bit_count, 16, .Little, .unsigned); + try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq); + + buffer = &[_]u8{ 0xaa, 0xaa, 0xaa, 0xab, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd }; + m.readTwosComplement(buffer, bit_count, 16, .Big, .unsigned); + try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq); + + bit_count = 12 * 8 + 2; + + buffer = &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0x02 }; + m.readTwosComplement(buffer, bit_count, 13, .Little, .signed); + try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq); + + buffer = &[_]u8{ 0x02, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3 }; + m.readTwosComplement(buffer, bit_count, 13, .Big, .signed); + try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq); + + buffer = &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0x02, 0xaa, 0xaa, 0xaa }; + m.readTwosComplement(buffer, bit_count, 16, .Little, .signed); + try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq); + + buffer = &[_]u8{ 0xaa, 0xaa, 0xaa, 0x02, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3 }; + m.readTwosComplement(buffer, bit_count, 16, .Big, .signed); + try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq); +} diff --git a/src/value.zig b/src/value.zig index 2018eb3df3..a3dd6501e4 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1046,7 +1046,8 @@ pub const Value = extern union { var bigint_buffer: BigIntSpace = undefined; const bigint = val.toBigInt(&bigint_buffer); const bits = ty.intInfo(target).bits; - bigint.writeTwosComplement(buffer, bits, target.cpu.arch.endian()); + const abi_size = ty.abiSize(target); + bigint.writeTwosComplement(buffer, bits, abi_size, target.cpu.arch.endian()); }, .Enum => { var enum_buffer: Payload.U64 = undefined; @@ -1054,7 +1055,8 @@ pub const Value = extern union { var bigint_buffer: BigIntSpace = undefined; const bigint = int_val.toBigInt(&bigint_buffer); const bits = ty.intInfo(target).bits; - bigint.writeTwosComplement(buffer, bits, target.cpu.arch.endian()); + const abi_size = ty.abiSize(target); + bigint.writeTwosComplement(buffer, bits, abi_size, target.cpu.arch.endian()); }, .Float => switch (ty.floatBits(target)) { 16 => return floatWriteToMemory(f16, val.toFloat(f16), target, buffer), @@ -1096,8 +1098,9 @@ pub const Value = extern union { const Limb = std.math.big.Limb; const limb_count = (buffer.len + @sizeOf(Limb) - 1) / @sizeOf(Limb); const limbs_buffer = try arena.alloc(Limb, limb_count); + const abi_size = ty.abiSize(target); var bigint = BigIntMutable.init(limbs_buffer, 0); - bigint.readTwosComplement(buffer, int_info.bits, endian, int_info.signedness); + bigint.readTwosComplement(buffer, int_info.bits, abi_size, endian, int_info.signedness); return fromBigInt(arena, bigint.toConst()); }, .Float => switch (ty.floatBits(target)) { From c586e3ba1bbc7784aee22bf96f69b8003611112a Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Sun, 13 Feb 2022 11:59:14 -0700 Subject: [PATCH 0168/2031] Add additional tests for `@bitCast` --- lib/std/math/big/int_test.zig | 91 ++++++++++++++++++++++ test/behavior/bitcast.zig | 140 +++++++++------------------------- 2 files changed, 126 insertions(+), 105 deletions(-) diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index 69600425a4..e7469326e4 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -2550,6 +2550,43 @@ test "big int conversion read twos complement with padding" { try testing.expect(std.mem.eql(u8, buffer1, &[_]u8{ 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3 })); } +test "big int write twos complement +/- zero" { + var a = try Managed.initSet(testing.allocator, 0x0); + defer a.deinit(); + var m = a.toMutable(); + + var buffer1 = try testing.allocator.alloc(u8, 16); + defer testing.allocator.free(buffer1); + @memset(buffer1.ptr, 0xaa, buffer1.len); + + var bit_count: usize = 0; + + // Test zero + + m.toConst().writeTwosComplement(buffer1, bit_count, 13, .Little); + try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3)))); + m.toConst().writeTwosComplement(buffer1, bit_count, 13, .Big); + try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3)))); + m.toConst().writeTwosComplement(buffer1, bit_count, 16, .Little); + try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16)))); + m.toConst().writeTwosComplement(buffer1, bit_count, 16, .Big); + try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16)))); + + @memset(buffer1.ptr, 0xaa, buffer1.len); + m.positive = false; + + // Test negative zero + + m.toConst().writeTwosComplement(buffer1, bit_count, 13, .Little); + try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3)))); + m.toConst().writeTwosComplement(buffer1, bit_count, 13, .Big); + try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 13) ++ ([_]u8{0xaa} ** 3)))); + m.toConst().writeTwosComplement(buffer1, bit_count, 16, .Little); + try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16)))); + m.toConst().writeTwosComplement(buffer1, bit_count, 16, .Big); + try testing.expect(std.mem.eql(u8, buffer1, &(([_]u8{0} ** 16)))); +} + test "big int conversion write twos complement with padding" { var a = try Managed.initSet(testing.allocator, 0x01_ffffffff_ffffffff_ffffffff); defer a.deinit(); @@ -2564,6 +2601,8 @@ test "big int conversion write twos complement with padding" { var bit_count: usize = 12 * 8 + 1; var buffer: []const u8 = undefined; + // Test 0x01_02030405_06070809_0a0b0c0d + buffer = &[_]u8{ 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0xb }; m.readTwosComplement(buffer, bit_count, 13, .Little, .unsigned); try testing.expect(m.toConst().orderAgainstScalar(0x01_02030405_06070809_0a0b0c0d) == .eq); @@ -2582,6 +2621,8 @@ test "big int conversion write twos complement with padding" { bit_count = 12 * 8 + 2; + // Test -0x01_02030405_06070809_0a0b0c0d + buffer = &[_]u8{ 0xf3, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0x02 }; m.readTwosComplement(buffer, bit_count, 13, .Little, .signed); try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq); @@ -2597,4 +2638,54 @@ test "big int conversion write twos complement with padding" { buffer = &[_]u8{ 0xaa, 0xaa, 0xaa, 0x02, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf3 }; m.readTwosComplement(buffer, bit_count, 16, .Big, .signed); try testing.expect(m.toConst().orderAgainstScalar(-0x01_02030405_06070809_0a0b0c0d) == .eq); + + // Test 0 + + buffer = &([_]u8{0} ** 16); + m.readTwosComplement(buffer, bit_count, 13, .Little, .unsigned); + try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq); + m.readTwosComplement(buffer, bit_count, 13, .Big, .unsigned); + try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq); + m.readTwosComplement(buffer, bit_count, 16, .Little, .unsigned); + try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq); + m.readTwosComplement(buffer, bit_count, 16, .Big, .unsigned); + try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq); + + bit_count = 0; + buffer = &([_]u8{0xaa} ** 16); + m.readTwosComplement(buffer, bit_count, 13, .Little, .unsigned); + try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq); + m.readTwosComplement(buffer, bit_count, 13, .Big, .unsigned); + try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq); + m.readTwosComplement(buffer, bit_count, 16, .Little, .unsigned); + try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq); + m.readTwosComplement(buffer, bit_count, 16, .Big, .unsigned); + try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq); +} + +test "big int conversion write twos complement zero" { + var a = try Managed.initSet(testing.allocator, 0x01_ffffffff_ffffffff_ffffffff); + defer a.deinit(); + + var m = a.toMutable(); + + // readTwosComplement: + // (1) should not read beyond buffer[0..abi_size] + // (2) should correctly interpret bytes based on the provided endianness + // (3) should ignore any bits from bit_count to 8 * abi_size + + var bit_count: usize = 12 * 8 + 1; + var buffer: []const u8 = undefined; + + buffer = &([_]u8{0} ** 13); + m.readTwosComplement(buffer, bit_count, 13, .Little, .unsigned); + try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq); + m.readTwosComplement(buffer, bit_count, 13, .Big, .unsigned); + try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq); + + buffer = &([_]u8{0} ** 16); + m.readTwosComplement(buffer, bit_count, 16, .Little, .unsigned); + try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq); + m.readTwosComplement(buffer, bit_count, 16, .Big, .unsigned); + try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq); } diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index 43d6524a4e..59a16c5fc9 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -6,123 +6,53 @@ const maxInt = std.math.maxInt; const minInt = std.math.minInt; const native_endian = builtin.target.cpu.arch.endian(); -test "@bitCast i32 -> u32" { - try testBitCast_i32_u32(); - comptime try testBitCast_i32_u32(); +test "@bitCast iX -> uX" { + const bit_values = [_]usize{ 8, 16, 32, 64 }; + + inline for (bit_values) |bits| { + try testBitCast(bits); + comptime try testBitCast(bits); + } } -fn testBitCast_i32_u32() !void { - try expect(conv_i32(-1) == maxInt(u32)); - try expect(conv_u32(maxInt(u32)) == -1); - try expect(conv_u32(0x8000_0000) == minInt(i32)); - try expect(conv_i32(minInt(i32)) == 0x8000_0000); -} - -fn conv_i32(x: i32) u32 { - return @bitCast(u32, x); -} -fn conv_u32(x: u32) i32 { - return @bitCast(i32, x); -} - -test "@bitCast i48 -> u48" { - try testBitCast_i48_u48(); - comptime try testBitCast_i48_u48(); -} - -fn testBitCast_i48_u48() !void { +test "@bitCast iX -> uX exotic integers" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try expect(conv_i48(-1) == maxInt(u48)); - try expect(conv_u48(maxInt(u48)) == -1); - try expect(conv_u48(0x8000_0000_0000) == minInt(i48)); - try expect(conv_i48(minInt(i48)) == 0x8000_0000_0000); + const bit_values = [_]usize{ 1, 48, 27, 512, 493, 293, 125, 204, 112 }; + + inline for (bit_values) |bits| { + try testBitCast(bits); + comptime try testBitCast(bits); + } } -fn conv_i48(x: i48) u48 { - return @bitCast(u48, x); +fn testBitCast(comptime N: usize) !void { + const iN = std.meta.Int(.signed, N); + const uN = std.meta.Int(.unsigned, N); + + try expect(conv_iN(N, -1) == maxInt(uN)); + try expect(conv_uN(N, maxInt(uN)) == -1); + + try expect(conv_iN(N, maxInt(iN)) == maxInt(iN)); + try expect(conv_uN(N, maxInt(iN)) == maxInt(iN)); + + try expect(conv_uN(N, 1 << (N - 1)) == minInt(iN)); + try expect(conv_iN(N, minInt(iN)) == (1 << (N - 1))); + + try expect(conv_uN(N, 0) == 0); + try expect(conv_iN(N, 0) == 0); + + try expect(conv_iN(N, -0) == 0); } -fn conv_u48(x: u48) i48 { - return @bitCast(i48, x); +fn conv_iN(comptime N: usize, x: std.meta.Int(.signed, N)) std.meta.Int(.unsigned, N) { + return @bitCast(std.meta.Int(.unsigned, N), x); } -test "@bitCast i27 -> u27" { - try testBitCast_i27_u27(); - comptime try testBitCast_i27_u27(); -} - -fn testBitCast_i27_u27() !void { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - - try expect(conv_i27(-1) == maxInt(u27)); - try expect(conv_u27(maxInt(u27)) == -1); - try expect(conv_u27(0x400_0000) == minInt(i27)); - try expect(conv_i27(minInt(i27)) == 0x400_0000); -} - -fn conv_i27(x: i27) u27 { - return @bitCast(u27, x); -} - -fn conv_u27(x: u27) i27 { - return @bitCast(i27, x); -} - -test "@bitCast i512 -> u512" { - try testBitCast_i512_u512(); - comptime try testBitCast_i512_u512(); -} - -fn testBitCast_i512_u512() !void { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - - try expect(conv_i512(-1) == maxInt(u512)); - try expect(conv_u512(maxInt(u512)) == -1); - try expect(conv_u512(@as(u512, 1) << 511) == minInt(i512)); - try expect(conv_i512(minInt(i512)) == (@as(u512, 1) << 511)); -} - -fn conv_i512(x: i512) u512 { - return @bitCast(u512, x); -} - -fn conv_u512(x: u512) i512 { - return @bitCast(i512, x); -} - -test "bitcast result to _" { - _ = @bitCast(u8, @as(i8, 1)); -} - -test "@bitCast i493 -> u493" { - try testBitCast_i493_u493(); - comptime try testBitCast_i493_u493(); -} - -fn testBitCast_i493_u493() !void { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - - try expect(conv_i493(-1) == maxInt(u493)); - try expect(conv_u493(maxInt(u493)) == -1); - try expect(conv_u493(@as(u493, 1) << 492) == minInt(i493)); - try expect(conv_i493(minInt(i493)) == (@as(u493, 1) << 492)); -} - -fn conv_i493(x: i493) u493 { - return @bitCast(u493, x); -} - -fn conv_u493(x: u493) i493 { - return @bitCast(i493, x); +fn conv_uN(comptime N: usize, x: std.meta.Int(.unsigned, N)) std.meta.Int(.signed, N) { + return @bitCast(std.meta.Int(.signed, N), x); } test "nested bitcast" { From 45aed7171c5cafb06671714ac1b15fdcf056d040 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Sun, 13 Feb 2022 13:01:55 -0700 Subject: [PATCH 0169/2031] Skip 8/16-bit `@bitCast` test for wasm --- test/behavior/bitcast.zig | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index 59a16c5fc9..1bf33af57b 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -6,8 +6,18 @@ const maxInt = std.math.maxInt; const minInt = std.math.minInt; const native_endian = builtin.target.cpu.arch.endian(); -test "@bitCast iX -> uX" { - const bit_values = [_]usize{ 8, 16, 32, 64 }; +test "@bitCast iX -> uX (32, 64)" { + const bit_values = [_]usize{ 32, 64 }; + + inline for (bit_values) |bits| { + try testBitCast(bits); + comptime try testBitCast(bits); + } +} + +test "@bitCast iX -> uX (8, 16, 128)" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + const bit_values = [_]usize{ 8, 16, 128 }; inline for (bit_values) |bits| { try testBitCast(bits); From f23005eba7d323d3f26e90972fc042f4d2002df7 Mon Sep 17 00:00:00 2001 From: ominitay <37453713+ominitay@users.noreply.github.com> Date: Sat, 8 Jan 2022 21:14:22 +0000 Subject: [PATCH 0170/2031] std.c.darwin.Stat: use timespec Uses timespec for times in `Stat` instead of two `isize` fields per time. This matches the header file. --- lib/std/c/darwin.zig | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index f4ca9cd6dd..3f5b8b340a 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -372,14 +372,10 @@ pub const Stat = extern struct { uid: uid_t, gid: gid_t, rdev: i32, - atimesec: isize, - atimensec: isize, - mtimesec: isize, - mtimensec: isize, - ctimesec: isize, - ctimensec: isize, - birthtimesec: isize, - birthtimensec: isize, + atimespec: timespec, + mtimespec: timespec, + ctimespec: timespec, + birthtimespec: timespec, size: off_t, blocks: i64, blksize: i32, @@ -389,24 +385,15 @@ pub const Stat = extern struct { qspare: [2]i64, pub fn atime(self: @This()) timespec { - return timespec{ - .tv_sec = self.atimesec, - .tv_nsec = self.atimensec, - }; + return self.atimespec; } pub fn mtime(self: @This()) timespec { - return timespec{ - .tv_sec = self.mtimesec, - .tv_nsec = self.mtimensec, - }; + return self.mtimespec; } pub fn ctime(self: @This()) timespec { - return timespec{ - .tv_sec = self.ctimesec, - .tv_nsec = self.ctimensec, - }; + return self.ctimespec; } }; From 3dd3c5063b1ab939e41461d0c3a3318708d9f76f Mon Sep 17 00:00:00 2001 From: ominitay <37453713+ominitay@users.noreply.github.com> Date: Sat, 8 Jan 2022 21:16:26 +0000 Subject: [PATCH 0171/2031] std.c.haiku: move Stat.crtime to Stat.birthtime --- lib/std/c/haiku.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/c/haiku.zig b/lib/std/c/haiku.zig index 5997855ea3..176d53e6ae 100644 --- a/lib/std/c/haiku.zig +++ b/lib/std/c/haiku.zig @@ -226,7 +226,7 @@ pub const Stat = extern struct { pub fn ctime(self: @This()) timespec { return self.ctim; } - pub fn crtime(self: @This()) timespec { + pub fn birthtime(self: @This()) timespec { return self.crtim; } }; From d978fdaa67f087cd17a122fec9ce65eabf9053ea Mon Sep 17 00:00:00 2001 From: ominitay <37453713+ominitay@users.noreply.github.com> Date: Sat, 8 Jan 2022 21:18:42 +0000 Subject: [PATCH 0172/2031] std.c.Wasi.Stat: use timespec --- lib/std/c/wasi.zig | 24 ++++++------------------ lib/std/os/wasi.zig | 2 +- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/lib/std/c/wasi.zig b/lib/std/c/wasi.zig index 9c9148a783..c3635784dd 100644 --- a/lib/std/c/wasi.zig +++ b/lib/std/c/wasi.zig @@ -41,32 +41,20 @@ pub const Stat = extern struct { blksize: i32, blocks: i64, - atimesec: time_t, - atimensec: isize, - mtimesec: time_t, - mtimensec: isize, - ctimesec: time_t, - ctimensec: isize, + atim: timespec, + mtim: timespec, + ctim: timespec, pub fn atime(self: @This()) timespec { - return timespec{ - .tv_sec = self.atimesec, - .tv_nsec = self.atimensec, - }; + return self.atim; } pub fn mtime(self: @This()) timespec { - return timespec{ - .tv_sec = self.mtimesec, - .tv_nsec = self.mtimensec, - }; + return self.mtim; } pub fn ctime(self: @This()) timespec { - return timespec{ - .tv_sec = self.ctimesec, - .tv_nsec = self.ctimensec, - }; + return self.ctim; } }; diff --git a/lib/std/os/wasi.zig b/lib/std/os/wasi.zig index 029deca3fd..0b2538cb88 100644 --- a/lib/std/os/wasi.zig +++ b/lib/std/os/wasi.zig @@ -88,7 +88,7 @@ pub const mode_t = u32; pub const time_t = i64; // match https://github.com/CraneStation/wasi-libc -pub const timespec = struct { +pub const timespec = extern struct { tv_sec: time_t, tv_nsec: isize, From b2610649fcf43c678fd81f3bef71f3ba42ee3606 Mon Sep 17 00:00:00 2001 From: ominitay <37453713+ominitay@users.noreply.github.com> Date: Sat, 8 Jan 2022 21:19:35 +0000 Subject: [PATCH 0173/2031] std.c.*: add birthtime function to Stat Adds a birthtime function to the `Stat` structs of Unices which support this. This is done to match the `atime`, `mtime`, and `ctime` functions. --- lib/std/c/darwin.zig | 4 ++++ lib/std/c/freebsd.zig | 4 ++++ lib/std/c/netbsd.zig | 4 ++++ lib/std/c/openbsd.zig | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index 3f5b8b340a..2d72f69024 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -395,6 +395,10 @@ pub const Stat = extern struct { pub fn ctime(self: @This()) timespec { return self.ctimespec; } + + pub fn birthtime(self: @This()) timespec { + return self.birthtimespec; + } }; pub const timespec = extern struct { diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index a19ecd3bac..10ab830ab0 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -283,6 +283,10 @@ pub const Stat = extern struct { pub fn ctime(self: @This()) timespec { return self.ctim; } + + pub fn birthtime(self: @This()) timespec { + return self.birthtim; + } }; pub const timespec = extern struct { diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig index 4fbc7594b0..17c3bfd83c 100644 --- a/lib/std/c/netbsd.zig +++ b/lib/std/c/netbsd.zig @@ -312,6 +312,10 @@ pub const Stat = extern struct { pub fn ctime(self: @This()) timespec { return self.ctim; } + + pub fn birthtime(self: @This()) timespec { + return self.birthtim; + } }; pub const timespec = extern struct { diff --git a/lib/std/c/openbsd.zig b/lib/std/c/openbsd.zig index 6ba11e8e5a..4ee3351023 100644 --- a/lib/std/c/openbsd.zig +++ b/lib/std/c/openbsd.zig @@ -235,6 +235,10 @@ pub const Stat = extern struct { pub fn ctime(self: @This()) timespec { return self.ctim; } + + pub fn birthtime(self: @This()) timespec { + return self.birthtim; + } }; pub const timespec = extern struct { From 11b4cc589c85b6330824ac9a64bfb0829cc1adb0 Mon Sep 17 00:00:00 2001 From: ominitay <37453713+ominitay@users.noreply.github.com> Date: Sat, 8 Jan 2022 21:22:23 +0000 Subject: [PATCH 0174/2031] std.fs: Implement cross-platform metadata API Implements a cross-platform metadata API, aiming to reduce unnecessary Unix-dependence of the `std.fs` api. Presently, all OSes beside Windows are treated as Unix; this is likely the best way to treat things by default, instead of explicitly listing each Unix-like OS. Platform-specific operations are not provided by `File.Metadata`, and instead are to be accessed from `File.Metadata.inner`. Adds: - File.setPermissions() : Sets permission of a file according to a `Permissions` struct (not available on WASI) - File.Permissions : A cross-platform representation of file permissions - Permissions.readOnly() : Returns whether the file is read-only - Permissions.setReadOnly() : Sets whether the file is read-only - Permissions.unixSet() : Sets permissions for a class (UNIX-only) - Permissions.unixGet() : Checks a permission for a class (UNIX-only) - Permissions.unixNew() : Returns a new Permissions struct to represent the passed mode (UNIX-only) - File.Metadata : A cross-platform representation of file metadata - Metadata.size() : Returns the size of a file - Metadata.permissions() : Returns a `Permissions` struct, representing permissions on the file - Metadata.kind() : Returns the `Kind` of the file - Metadata.accessed() : Returns the time the file was last accessed - Metadata.modified() : Returns the time the file was last modified - Metadata.created() : Returns the time the file was created (this is an optional, as the underlying filesystem, or OS may not support this) Methods of `File.Metadata` are also available for the below, so I won't repeat myself The below may be used for platform-specific functionality - File.MetadataUnix : The internal implementation of `File.Metadata` on Unices - File.MetadataLinux : The internal implementation of `File.Metadata` on Linux - File.MetadataWindows : The implementation of `File.Metadata` on Windows --- lib/std/fs.zig | 25 +++ lib/std/fs/file.zig | 479 ++++++++++++++++++++++++++++++++++++++++++++ lib/std/fs/test.zig | 76 +++++++ 3 files changed, 580 insertions(+) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index f7818ea68b..7b577b1b62 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -2231,6 +2231,31 @@ pub const Dir = struct { } pub const ChownError = File.ChownError; + + const Permissions = File.Permissions; + pub const SetPermissionsError = File.SetPermissionsError; + + /// Sets permissions according to the provided `Permissions` struct. + /// This method is *NOT* available on WASI + pub fn setPermissions(self: Dir, permissions: Permissions) SetPermissionsError!void { + const file: File = .{ + .handle = self.fd, + .capable_io_mode = .blocking, + }; + try file.setPermissions(permissions); + } + + const Metadata = File.Metadata; + pub const MetadataError = File.MetadataError; + + /// Returns a `Metadata` struct, representing the permissions on the directory + pub fn metadata(self: Dir) MetadataError!Metadata { + const file: File = .{ + .handle = self.fd, + .capable_io_mode = .blocking, + }; + return try file.metadata(); + } }; /// Returns a handle to the current working directory. It is not opened with iteration capability. diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index ff71c82d2b..079d5905a1 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -398,6 +398,485 @@ pub const File = struct { try os.fchown(self.handle, owner, group); } + /// Cross-platform representation of permissions on a file. + /// The `readonly` and `setReadonly` are the only methods available across all platforms. + /// Platform-specific functionality is available through the `inner` field. + pub const Permissions = struct { + /// You may use the `inner` field to use platform-specific functionality + inner: switch (builtin.os.tag) { + .windows => PermissionsWindows, + else => PermissionsUnix, + }, + + const Self = @This(); + + /// Returns `true` if permissions represent an unwritable file. + /// On Unix, `true` is returned only if no class has write permissions. + pub fn readOnly(self: Self) bool { + return self.inner.readOnly(); + } + + /// Sets whether write permissions are provided. + /// On Unix, this affects *all* classes. If this is undesired, use `unixSet` + /// This method *DOES NOT* set permissions on the filesystem: use `File.setPermissions(permissions)` + pub fn setReadOnly(self: *Self, read_only: bool) void { + self.inner.setReadOnly(read_only); + } + }; + + pub const PermissionsWindows = struct { + attributes: os.windows.DWORD, + + const Self = @This(); + + /// Returns `true` if permissions represent an unwritable file. + pub fn readOnly(self: Self) bool { + return self.attributes & os.windows.FILE_ATTRIBUTE_READONLY != 0; + } + + /// Sets whether write permissions are provided. + /// This method *DOES NOT* set permissions on the filesystem: use `File.setPermissions(permissions)` + pub fn setReadOnly(self: *Self, read_only: bool) void { + if (read_only) { + self.attributes |= os.windows.FILE_ATTRIBUTE_READONLY; + } else { + self.attributes &= ~@as(os.windows.DWORD, os.windows.FILE_ATTRIBUTE_READONLY); + } + } + }; + + pub const PermissionsUnix = struct { + mode: Mode, + + const Self = @This(); + + /// Returns `true` if permissions represent an unwritable file. + /// `true` is returned only if no class has write permissions. + pub fn readOnly(self: Self) bool { + return self.mode & 0o222 == 0; + } + + /// Sets whether write permissions are provided. + /// This affects *all* classes. If this is undesired, use `unixSet` + /// This method *DOES NOT* set permissions on the filesystem: use `File.setPermissions(permissions)` + pub fn setReadOnly(self: *Self, read_only: bool) void { + if (read_only) { + self.mode &= ~@as(Mode, 0o222); + } else { + self.mode |= @as(Mode, 0o222); + } + } + + pub const Class = enum(u2) { + user = 2, + group = 1, + other = 0, + }; + + pub const Permission = enum(u3) { + read = 0o4, + write = 0o2, + execute = 0o1, + }; + + /// Returns `true` if the chosen class has the selected permission. + /// This method is only available on Unix platforms. + pub fn unixHas(self: Self, class: Class, permission: Permission) bool { + const mask = @as(Mode, @enumToInt(permission)) << @as(u3, @enumToInt(class)) * 3; + return self.mode & mask != 0; + } + + /// Sets the permissions for the chosen class. Any permissions set to `null` are left unchanged. + /// This method *DOES NOT* set permissions on the filesystem: use `File.setPermissions(permissions)` + pub fn unixSet(self: *Self, class: Class, permissions: struct { + read: ?bool = null, + write: ?bool = null, + execute: ?bool = null, + }) void { + const shift = @as(u3, @enumToInt(class)) * 3; + if (permissions.read) |r| { + if (r) { + self.mode |= @as(Mode, 0o4) << shift; + } else { + self.mode &= ~(@as(Mode, 0o4) << shift); + } + } + if (permissions.write) |w| { + if (w) { + self.mode |= @as(Mode, 0o2) << shift; + } else { + self.mode &= ~(@as(Mode, 0o2) << shift); + } + } + if (permissions.execute) |x| { + if (x) { + self.mode |= @as(Mode, 0o1) << shift; + } else { + self.mode &= ~(@as(Mode, 0o1) << shift); + } + } + } + + /// Returns a `Permissions` struct representing the permissions from the passed mode. + pub fn unixNew(new_mode: Mode) Self { + return Self{ + .mode = new_mode, + }; + } + }; + + pub const SetPermissionsError = ChmodError; + + /// Sets permissions according to the provided `Permissions` struct. + /// This method is *NOT* available on WASI + pub fn setPermissions(self: File, permissions: Permissions) SetPermissionsError!void { + switch (builtin.os.tag) { + .windows => { + var io_status_block: windows.IO_STATUS_BLOCK = undefined; + var info = windows.FILE_BASIC_INFORMATION{ + .CreationTime = 0, + .LastAccessTime = 0, + .LastWriteTime = 0, + .ChangeTime = 0, + .FileAttributes = permissions.inner.attributes, + }; + const rc = windows.ntdll.NtSetInformationFile( + self.handle, + &io_status_block, + &info, + @sizeOf(windows.FILE_BASIC_INFORMATION), + .FileBasicInformation, + ); + switch (rc) { + .SUCCESS => return, + .INVALID_HANDLE => unreachable, + .ACCESS_DENIED => return error.AccessDenied, + else => return windows.unexpectedStatus(rc), + } + }, + .wasi => @compileError("Unsupported OS"), // Wasi filesystem does not *yet* support chmod + else => { + try self.chmod(permissions.inner.mode); + }, + } + } + + /// Cross-platform representation of file metadata. + /// Platform-specific functionality is available through the `inner` field. + pub const Metadata = struct { + /// You may use the `inner` field to use platform-specific functionality + inner: switch (builtin.os.tag) { + .windows => MetadataWindows, + .linux => MetadataLinux, + else => MetadataUnix, + }, + + const Self = @This(); + + /// Returns the size of the file + pub fn size(self: Self) u64 { + return self.inner.size(); + } + + /// Returns a `Permissions` struct, representing the permissions on the file + pub fn permissions(self: Self) Permissions { + return self.inner.permissions(); + } + + /// Returns the `Kind` of file. + /// On Windows, can only return: `.File`, `.Directory`, `.SymLink` or `.Unknown` + pub fn kind(self: Self) Kind { + return self.inner.kind(); + } + + /// Returns the last time the file was accessed in nanoseconds since UTC 1970-01-01 + pub fn accessed(self: Self) i128 { + return self.inner.accessed(); + } + + /// Returns the time the file was modified in nanoseconds since UTC 1970-01-01 + pub fn modified(self: Self) i128 { + return self.inner.modified(); + } + + /// Returns the time the file was created in nanoseconds since UTC 1970-01-01 + /// On Windows, this cannot return null + /// On Linux, this returns null if the filesystem does not support creation times, or if the kernel is older than 4.11 + /// On Unices, this returns null if the filesystem or OS does not support creation times + /// On MacOS, this returns the ctime if the filesystem does not support creation times; this is insanity, and yet another reason to hate on Apple + pub fn created(self: Self) ?i128 { + return self.inner.created(); + } + }; + + pub const MetadataUnix = struct { + stat: os.Stat, + + const Self = @This(); + + /// Returns the size of the file + pub fn size(self: Self) u64 { + return @intCast(u64, self.stat.size); + } + + /// Returns a `Permissions` struct, representing the permissions on the file + pub fn permissions(self: Self) Permissions { + return Permissions{ .inner = PermissionsUnix{ .mode = self.stat.mode } }; + } + + /// Returns the `Kind` of the file + pub fn kind(self: Self) Kind { + if (builtin.os.tag == .wasi and !builtin.link_libc) return switch (self.stat.filetype) { + .BLOCK_DEVICE => Kind.BlockDevice, + .CHARACTER_DEVICE => Kind.CharacterDevice, + .DIRECTORY => Kind.Directory, + .SYMBOLIC_LINK => Kind.SymLink, + .REGULAR_FILE => Kind.File, + .SOCKET_STREAM, .SOCKET_DGRAM => Kind.UnixDomainSocket, + else => Kind.Unknown, + }; + + const m = self.stat.mode & os.S.IFMT; + + switch (m) { + os.S.IFBLK => return Kind.BlockDevice, + os.S.IFCHR => return Kind.CharacterDevice, + os.S.IFDIR => return Kind.Directory, + os.S.IFIFO => return Kind.NamedPipe, + os.S.IFLNK => return Kind.SymLink, + os.S.IFREG => return Kind.File, + os.S.IFSOCK => return Kind.UnixDomainSocket, + else => {}, + } + + if (builtin.os.tag == .solaris) switch (m) { + os.S.IFDOOR => return Kind.Door, + os.S.IFPORT => return Kind.EventPort, + else => {}, + }; + + return .Unknown; + } + + /// Returns the last time the file was accessed in nanoseconds since UTC 1970-01-01 + pub fn accessed(self: Self) i128 { + const atime = self.stat.atime(); + return @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec; + } + + /// Returns the last time the file was modified in nanoseconds since UTC 1970-01-01 + pub fn modified(self: Self) i128 { + const mtime = self.stat.mtime(); + return @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec; + } + + /// Returns the time the file was created in nanoseconds since UTC 1970-01-01 + /// Returns null if this is not supported by the OS or filesystem + pub fn created(self: Self) ?i128 { + if (!@hasDecl(@TypeOf(self.stat), "birthtime")) return null; + const birthtime = self.stat.birthtime(); + + // If the filesystem doesn't support this the value *should* be: + // On FreeBSD: tv_nsec = 0, tv_sec = -1 + // On NetBSD and OpenBSD: tv_nsec = 0, tv_sec = 0 + // On MacOS, it is set to ctime -- we cannot detect this!! + switch (builtin.os.tag) { + .freebsd => if (birthtime.tv_sec == -1 and birthtime.tv_nsec == 0) return null, + .netbsd, .openbsd => if (birthtime.tv_sec == 0 and birthtime.tv_nsec == 0) return null, + .macos => {}, + else => @compileError("Creation time detection not implemented for OS"), + } + + return @as(i128, birthtime.tv_sec) * std.time.ns_per_s + birthtime.tv_nsec; + } + }; + + /// `MetadataUnix`, but using Linux's `statx` syscall. + /// On Linux versions below 4.11, `statx` will be filled with data from stat. + pub const MetadataLinux = struct { + statx: os.linux.Statx, + + const Self = @This(); + + /// Returns the size of the file + pub fn size(self: Self) u64 { + return self.statx.size; + } + + /// Returns a `Permissions` struct, representing the permissions on the file + pub fn permissions(self: Self) Permissions { + return Permissions{ .inner = PermissionsUnix{ .mode = self.statx.mode } }; + } + + /// Returns the `Kind` of the file + pub fn kind(self: Self) Kind { + const m = self.statx.mode & os.S.IFMT; + + switch (m) { + os.S.IFBLK => return Kind.BlockDevice, + os.S.IFCHR => return Kind.CharacterDevice, + os.S.IFDIR => return Kind.Directory, + os.S.IFIFO => return Kind.NamedPipe, + os.S.IFLNK => return Kind.SymLink, + os.S.IFREG => return Kind.File, + os.S.IFSOCK => return Kind.UnixDomainSocket, + else => {}, + } + + return .Unknown; + } + + /// Returns the last time the file was accessed in nanoseconds since UTC 1970-01-01 + pub fn accessed(self: Self) i128 { + return @as(i128, self.statx.atime.tv_sec) * std.time.ns_per_s + self.statx.atime.tv_nsec; + } + + /// Returns the last time the file was modified in nanoseconds since UTC 1970-01-01 + pub fn modified(self: Self) i128 { + return @as(i128, self.statx.mtime.tv_sec) * std.time.ns_per_s + self.statx.mtime.tv_nsec; + } + + /// Returns the time the file was created in nanoseconds since UTC 1970-01-01 + /// Returns null if this is not supported by the filesystem, or on kernels before than version 4.11 + pub fn created(self: Self) ?i128 { + if (self.statx.mask & os.linux.STATX_BTIME == 0) return null; + return @as(i128, self.statx.btime.tv_sec) * std.time.ns_per_s + self.statx.btime.tv_nsec; + } + }; + + pub const MetadataWindows = struct { + attributes: windows.DWORD, + reparse_tag: windows.DWORD, + _size: u64, + access_time: i128, + modified_time: i128, + creation_time: i128, + + const Self = @This(); + + /// Returns the size of the file + pub fn size(self: Self) u64 { + return self._size; + } + + /// Returns a `Permissions` struct, representing the permissions on the file + pub fn permissions(self: Self) Permissions { + return Permissions{ .inner = PermissionsWindows{ .attributes = self.attributes } }; + } + + /// Returns the `Kind` of the file. + /// Can only return: `.File`, `.Directory`, `.SymLink` or `.Unknown` + pub fn kind(self: Self) Kind { + if (self.attributes & windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) { + if (self.reparse_tag & 0x20000000 != 0) { + return .SymLink; + } + } else if (self.attributes & windows.FILE_ATTRIBUTE_DIRECTORY != 0) { + return .Directory; + } else { + return .File; + } + return .Unknown; + } + + /// Returns the last time the file was accessed in nanoseconds since UTC 1970-01-01 + pub fn accessed(self: Self) i128 { + return self.access_time; + } + + /// Returns the time the file was modified in nanoseconds since UTC 1970-01-01 + pub fn modified(self: Self) i128 { + return self.modified_time; + } + + /// Returns the time the file was created in nanoseconds since UTC 1970-01-01 + /// This never returns null, only returning an optional for compatibility with other OSes + pub fn created(self: Self) ?i128 { + return self.creation_time; + } + }; + + pub const MetadataError = os.FStatError; + + pub fn metadata(self: File) MetadataError!Metadata { + return Metadata{ + .inner = switch (builtin.os.tag) { + .windows => blk: { + var io_status_block: windows.IO_STATUS_BLOCK = undefined; + var info: windows.FILE_ALL_INFORMATION = undefined; + + const rc = windows.ntdll.NtQueryInformationFile(self.handle, &io_status_block, &info, @sizeOf(windows.FILE_ALL_INFORMATION), .FileAllInformation); + switch (rc) { + .SUCCESS => {}, + .BUFFER_OVERFLOW => {}, + .INVALID_PARAMETER => unreachable, + .ACCESS_DENIED => return error.AccessDenied, + else => return windows.unexpectedStatus(rc), + } + + const reparse_tag: windows.DWORD = reparse_blk: { + if (info.BasicInformation.FileAttributes & windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) { + var reparse_buf: [windows.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined; + try windows.DeviceIoControl(self.handle, windows.FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..]); + const reparse_struct = @ptrCast(*const windows.REPARSE_DATA_BUFFER, @alignCast(@alignOf(windows.REPARSE_DATA_BUFFER), &reparse_buf[0])); + break :reparse_blk reparse_struct.ReparseTag; + } + break :reparse_blk 0; + }; + + break :blk MetadataWindows{ + .attributes = info.BasicInformation.FileAttributes, + .reparse_tag = reparse_tag, + ._size = @bitCast(u64, info.StandardInformation.EndOfFile), + .access_time = windows.fromSysTime(info.BasicInformation.LastAccessTime), + .modified_time = windows.fromSysTime(info.BasicInformation.LastWriteTime), + .creation_time = windows.fromSysTime(info.BasicInformation.CreationTime), + }; + }, + .linux => blk: { + var stx = mem.zeroes(os.linux.Statx); + const rcx = os.linux.statx(self.handle, "\x00", os.linux.AT.EMPTY_PATH, os.linux.STATX_TYPE | os.linux.STATX_MODE | os.linux.STATX_ATIME | os.linux.STATX_MTIME | os.linux.STATX_BTIME, &stx); + + switch (os.errno(rcx)) { + .SUCCESS => {}, + // NOSYS happens when `statx` is unsupported, which is the case on kernel versions before 4.11 + // Here, we call `fstat` and fill `stx` with the data we need + .NOSYS => { + const st = try os.fstat(self.handle); + + stx.mode = @intCast(u16, st.mode); + + // Hacky conversion from timespec to statx_timestamp + stx.atime = std.mem.zeroes(os.linux.statx_timestamp); + stx.atime.tv_sec = st.atim.tv_sec; + stx.atime.tv_nsec = @intCast(u32, st.atim.tv_nsec); // Guaranteed to succeed (tv_nsec is always below 10^9) + + stx.mtime = std.mem.zeroes(os.linux.statx_timestamp); + stx.mtime.tv_sec = st.mtim.tv_sec; + stx.mtime.tv_nsec = @intCast(u32, st.mtim.tv_nsec); + + stx.mask = os.linux.STATX_BASIC_STATS | os.linux.STATX_MTIME; + }, + .BADF => unreachable, + .FAULT => unreachable, + .NOMEM => return error.SystemResources, + else => |err| return os.unexpectedErrno(err), + } + + break :blk MetadataLinux{ + .statx = stx, + }; + }, + else => blk: { + const st = try os.fstat(self.handle); + break :blk MetadataUnix{ + .stat = st, + }; + }, + }, + }; + } + pub const UpdateTimesError = os.FutimensError || windows.SetFileTimeError; /// The underlying file system may have a different granularity than nanoseconds, diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 309d9c3b07..b00b0609b2 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -1101,3 +1101,79 @@ test "chown" { defer dir.close(); try dir.chown(null, null); } + +test "File.Metadata" { + var tmp = tmpDir(.{}); + defer tmp.cleanup(); + + const file = try tmp.dir.createFile("test_file", .{ .read = true }); + defer file.close(); + + const metadata = try file.metadata(); + try testing.expect(metadata.kind() == .File); + try testing.expect(metadata.size() == 0); + _ = metadata.accessed(); + _ = metadata.modified(); + _ = metadata.created(); +} + +test "File.Permissions" { + if (builtin.os.tag == .wasi) + return error.SkipZigTest; + + var tmp = tmpDir(.{}); + defer tmp.cleanup(); + + const file = try tmp.dir.createFile("test_file", .{ .read = true }); + defer file.close(); + + const metadata = try file.metadata(); + var permissions = metadata.permissions(); + + try testing.expect(!permissions.readOnly()); + permissions.setReadOnly(true); + try testing.expect(permissions.readOnly()); + + try file.setPermissions(permissions); + const new_permissions = (try file.metadata()).permissions(); + try testing.expect(new_permissions.readOnly()); + + // Must be set to non-read-only to delete + permissions.setReadOnly(false); + try file.setPermissions(permissions); +} + +test "File.PermissionsUnix" { + if (builtin.os.tag == .windows or builtin.os.tag == .wasi) + return error.SkipZigTest; + + var tmp = tmpDir(.{}); + defer tmp.cleanup(); + + const file = try tmp.dir.createFile("test_file", .{ .mode = 0o666, .read = true }); + defer file.close(); + + const metadata = try file.metadata(); + var permissions = metadata.permissions(); + + permissions.setReadOnly(true); + try testing.expect(permissions.readOnly()); + try testing.expect(!permissions.inner.unixHas(.user, .write)); + permissions.inner.unixSet(.user, .{ .write = true }); + try testing.expect(!permissions.readOnly()); + try testing.expect(permissions.inner.unixHas(.user, .write)); + try testing.expect(permissions.inner.mode & 0o400 != 0); + + permissions.setReadOnly(true); + try file.setPermissions(permissions); + permissions = (try file.metadata()).permissions(); + try testing.expect(permissions.readOnly()); + + // Must be set to non-read-only to delete + permissions.setReadOnly(false); + try file.setPermissions(permissions); + + const permissions_unix = File.PermissionsUnix.unixNew(0o754); + try testing.expect(permissions_unix.unixHas(.user, .execute)); + try testing.expect(!permissions_unix.unixHas(.other, .execute)); +} From 7edf3d9f2d53f5b1c5e31ee1294ff9b52337760b Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Sun, 13 Feb 2022 14:16:40 -0700 Subject: [PATCH 0175/2031] Cast abi_size to usize --- src/value.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/value.zig b/src/value.zig index a3dd6501e4..d07179359f 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1046,7 +1046,7 @@ pub const Value = extern union { var bigint_buffer: BigIntSpace = undefined; const bigint = val.toBigInt(&bigint_buffer); const bits = ty.intInfo(target).bits; - const abi_size = ty.abiSize(target); + const abi_size = @intCast(usize, ty.abiSize(target)); bigint.writeTwosComplement(buffer, bits, abi_size, target.cpu.arch.endian()); }, .Enum => { @@ -1055,7 +1055,7 @@ pub const Value = extern union { var bigint_buffer: BigIntSpace = undefined; const bigint = int_val.toBigInt(&bigint_buffer); const bits = ty.intInfo(target).bits; - const abi_size = ty.abiSize(target); + const abi_size = @intCast(usize, ty.abiSize(target)); bigint.writeTwosComplement(buffer, bits, abi_size, target.cpu.arch.endian()); }, .Float => switch (ty.floatBits(target)) { @@ -1098,7 +1098,7 @@ pub const Value = extern union { const Limb = std.math.big.Limb; const limb_count = (buffer.len + @sizeOf(Limb) - 1) / @sizeOf(Limb); const limbs_buffer = try arena.alloc(Limb, limb_count); - const abi_size = ty.abiSize(target); + const abi_size = @intCast(usize, ty.abiSize(target)); var bigint = BigIntMutable.init(limbs_buffer, 0); bigint.readTwosComplement(buffer, int_info.bits, abi_size, endian, int_info.signedness); return fromBigInt(arena, bigint.toConst()); From f5068107cdf54f143a150feb5d500962ba98e963 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sun, 13 Feb 2022 20:40:51 +0100 Subject: [PATCH 0176/2031] stage2 regalloc: track Inst instead of ?Inst in register mapping The information whether a register is allocated to an instruction is already encoded in the free_registers "bitmap". Duplicating that information in the registers map is unnecessary and may lead to performance degradations. --- src/register_manager.zig | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/register_manager.zig b/src/register_manager.zig index 81c9fa5734..3d76e94ba0 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -33,11 +33,12 @@ pub fn RegisterManager( assert(callee_preserved_regs.len > 0); // see note above return struct { - /// Tracks the AIR instruction allocated to every register or - /// `null` if no instruction is allocated to a register + /// Tracks the AIR instruction allocated to every register. If + /// no instruction is allocated to a register (i.e. the + /// register is free), the value in that slot is undefined. /// /// The key must be canonical register. - registers: [callee_preserved_regs.len]?Air.Inst.Index = [_]?Air.Inst.Index{null} ** callee_preserved_regs.len, + registers: [callee_preserved_regs.len]Air.Inst.Index = undefined, /// Tracks which registers are free (in which case the /// corresponding bit is set to 1) free_registers: FreeRegInt = math.maxInt(FreeRegInt), @@ -201,14 +202,14 @@ pub fn RegisterManager( if (self.isRegFree(reg)) { self.markRegUsed(reg); } else { - const spilled_inst = self.registers[index].?; + const spilled_inst = self.registers[index]; try self.getFunction().spillInstruction(reg, spilled_inst); } self.registers[index] = inst; } else { // Don't track the register if (!self.isRegFree(reg)) { - const spilled_inst = self.registers[index].?; + const spilled_inst = self.registers[index]; try self.getFunction().spillInstruction(reg, spilled_inst); self.freeReg(reg); } @@ -241,7 +242,7 @@ pub fn RegisterManager( if (!self.isRegFree(reg)) { // Move the instruction that was previously there to a // stack allocation. - const spilled_inst = self.registers[index].?; + const spilled_inst = self.registers[index]; self.registers[index] = tracked_inst; try self.getFunction().spillInstruction(reg, spilled_inst); } else { @@ -251,7 +252,7 @@ pub fn RegisterManager( if (!self.isRegFree(reg)) { // Move the instruction that was previously there to a // stack allocation. - const spilled_inst = self.registers[index].?; + const spilled_inst = self.registers[index]; try self.getFunction().spillInstruction(reg, spilled_inst); self.freeReg(reg); } @@ -265,7 +266,7 @@ pub fn RegisterManager( const index = reg.allocIndex() orelse return; self.markRegAllocated(reg); - assert(self.registers[index] == null); + assert(self.isRegFree(reg)); self.registers[index] = inst; self.markRegUsed(reg); } @@ -275,7 +276,7 @@ pub fn RegisterManager( const index = reg.allocIndex() orelse return; log.debug("freeing register {}", .{reg}); - self.registers[index] = null; + self.registers[index] = undefined; self.markRegFree(reg); } }; From 9ca3c897ec546bc08eefe53471c995deefc9f36d Mon Sep 17 00:00:00 2001 From: Sebastian Keller Date: Sun, 13 Feb 2022 21:58:41 +0100 Subject: [PATCH 0177/2031] test_runner.zig: Do not log test name twice In #10859 I moved the `test_node.end()` call after everything else has been logged. Now the `test_fn.name` is printed by `Progress` itself, making the additional log obsolete. --- lib/std/special/test_runner.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/std/special/test_runner.zig b/lib/std/special/test_runner.zig index 201a5ccd90..3a5849b1a2 100644 --- a/lib/std/special/test_runner.zig +++ b/lib/std/special/test_runner.zig @@ -70,7 +70,7 @@ pub fn main() void { .blocking => { skip_count += 1; test_node.end(); - progress.log("{s}... SKIP (async test)\n", .{test_fn.name}); + progress.log("SKIP (async test)\n", .{}); if (!have_tty) std.debug.print("SKIP (async test)\n", .{}); continue; }, @@ -82,13 +82,13 @@ pub fn main() void { } else |err| switch (err) { error.SkipZigTest => { skip_count += 1; - progress.log("{s}... SKIP\n", .{test_fn.name}); + progress.log("SKIP\n", .{}); if (!have_tty) std.debug.print("SKIP\n", .{}); test_node.end(); }, else => { fail_count += 1; - progress.log("{s}... FAIL ({s})\n", .{ test_fn.name, @errorName(err) }); + progress.log("FAIL ({s})\n", .{@errorName(err)}); if (!have_tty) std.debug.print("FAIL ({s})\n", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); From 8ed792b640ef3601dfb773d670915d74fbbbad13 Mon Sep 17 00:00:00 2001 From: erikarvstedt <36110478+erikarvstedt@users.noreply.github.com> Date: Mon, 14 Feb 2022 11:14:50 +0100 Subject: [PATCH 0178/2031] std.Progress: fix suffix printing Previously, `suffix` was copied to `output_buffer` at position `max_end`, thereby writing into reserved space after `max_end`. This only worked because `suffix` was not larger than `bytes_needed_for_esc_codes_at_end` (otherwise there'd be a potential buffer overrun) and no escape codes at end are actually written. Since 2d5b2bf1c986d037ef965bf8c9b4d8dfd5967478, escape codes are no longer written to the end of the buffer. They are now written exclusively to the front of the buffer. This allows removing `bytes_needed_for_esc_codes_at_end` and simplifying the suffix printing logic. This also fixes the bug that the ellipse suffix was not printed in Windows terminals because `end.* > max_end` was never true. --- lib/std/Progress.zig | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index ecef04c600..07f9077844 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -312,16 +312,10 @@ fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: any error.NoSpaceLeft => { self.columns_written += self.output_buffer.len - end.*; end.* = self.output_buffer.len; + const suffix = "... "; + std.mem.copy(u8, self.output_buffer[self.output_buffer.len - suffix.len ..], suffix); }, } - const bytes_needed_for_esc_codes_at_end: u8 = if (self.is_windows_terminal) 0 else 11; - const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end; - if (end.* > max_end) { - const suffix = "... "; - self.columns_written = self.columns_written - (end.* - max_end) + suffix.len; - std.mem.copy(u8, self.output_buffer[max_end..], suffix); - end.* = max_end + suffix.len; - } } test "basic functionality" { @@ -335,6 +329,8 @@ test "basic functionality" { const root_node = progress.start("", 100); defer root_node.end(); + const speed_factor = std.time.ns_per_ms; + const sub_task_names = [_][]const u8{ "reticulating splines", "adjusting shoes", @@ -350,24 +346,24 @@ test "basic functionality" { next_sub_task = (next_sub_task + 1) % sub_task_names.len; node.completeOne(); - std.time.sleep(5 * std.time.ns_per_ms); + std.time.sleep(5 * speed_factor); node.completeOne(); node.completeOne(); - std.time.sleep(5 * std.time.ns_per_ms); + std.time.sleep(5 * speed_factor); node.completeOne(); node.completeOne(); - std.time.sleep(5 * std.time.ns_per_ms); + std.time.sleep(5 * speed_factor); node.end(); - std.time.sleep(5 * std.time.ns_per_ms); + std.time.sleep(5 * speed_factor); } { var node = root_node.start("this is a really long name designed to activate the truncation code. let's find out if it works", 0); node.activate(); - std.time.sleep(10 * std.time.ns_per_ms); + std.time.sleep(10 * speed_factor); progress.refresh(); - std.time.sleep(10 * std.time.ns_per_ms); + std.time.sleep(10 * speed_factor); node.end(); } } From ee69a4b45f67e929fe5780ab6bf44360f6511d26 Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sat, 12 Feb 2022 20:03:16 +0100 Subject: [PATCH 0179/2031] stage2: improve compiler error message for bad union init --- src/Sema.zig | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 7dbc36af37..480a6a1ca2 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2631,9 +2631,24 @@ fn validateUnionInit( union_ptr: Air.Inst.Ref, ) CompileError!void { if (instrs.len != 1) { - // TODO add note for other field - // TODO add note for union declared here - return sema.fail(block, init_src, "only one union field can be active at once", .{}); + const msg = msg: { + const msg = try sema.errMsg( + block, + init_src, + "cannot initialize multiple union fields at once, unions can only have one active field", + .{}, + ); + errdefer msg.destroy(sema.gpa); + + for (instrs[1..]) |inst| { + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const inst_src: LazySrcLoc = .{ .node_offset_back2tok = inst_data.src_node }; + try sema.errNote(block, inst_src, msg, "additional initializer here", .{}); + } + try sema.mod.errNoteNonLazy(union_obj.srcLoc(), msg, "union declared here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); } const field_ptr = instrs[0]; From b85c0d6a472255362f692ddbc8afd14c2da4a996 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 14 Feb 2022 15:23:45 +0200 Subject: [PATCH 0180/2031] std: fix tests that were not run due to refAllDecls regression --- lib/std/zig/parser_test.zig | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index a0cc11ce4b..5eca272b62 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -233,7 +233,7 @@ test "zig fmt: eof after missing comma" { try testError( \\foo() , &[_]Error{ - .expected_token, + .expected_comma_after_field, }); } @@ -5074,8 +5074,8 @@ test "recovery: missing comma" { \\ } \\} , &[_]Error{ - .expected_token, - .expected_token, + .expected_comma_after_switch_prong, + .expected_comma_after_switch_prong, .invalid_token, }); } @@ -5156,10 +5156,10 @@ test "recovery: missing semicolon" { \\ @foo \\} , &[_]Error{ - .expected_token, - .expected_token, + .expected_semi_after_stmt, + .expected_semi_after_stmt, .expected_param_list, - .expected_token, + .expected_semi_after_stmt, }); } @@ -5174,9 +5174,9 @@ test "recovery: invalid container members" { \\} , &[_]Error{ .expected_expr, - .expected_token, + .expected_comma_after_field, .expected_container_members, - .expected_token, + .expected_semi_after_stmt, }); } @@ -5201,7 +5201,7 @@ test "recovery: mismatched bracket at top level" { \\ arr: 128]?G \\}; , &[_]Error{ - .expected_token, + .expected_comma_after_field, }); } @@ -5301,9 +5301,9 @@ test "recovery: missing comma in params" { \\fn bar(a: i32, b: i32 c) void { } \\ , &[_]Error{ - .expected_token, - .expected_token, - .expected_token, + .expected_comma_after_param, + .expected_comma_after_param, + .expected_comma_after_param, }); } From 04f3d9301798e41ac8272822328914073e01a6ab Mon Sep 17 00:00:00 2001 From: Al Hoang <3811822-hoanga@users.noreply.gitlab.com> Date: Sat, 18 Dec 2021 23:30:51 -0600 Subject: [PATCH 0181/2031] haiku add missing cimport include for compilation --- src/Compilation.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index bd7581863b..0e1714ba38 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -4467,7 +4467,7 @@ fn detectLibCIncludeDirs( } fn detectLibCFromLibCInstallation(arena: Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs { - var list = try std.ArrayList([]const u8).initCapacity(arena, 4); + var list = try std.ArrayList([]const u8).initCapacity(arena, 5); list.appendAssumeCapacity(lci.include_dir.?); @@ -4487,6 +4487,9 @@ fn detectLibCFromLibCInstallation(arena: Allocator, target: Target, lci: *const const include_dir_path = lci.include_dir orelse return error.LibCInstallationNotAvailable; const os_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "os" }); list.appendAssumeCapacity(os_dir); + // Errors.h + const os_support_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "os/support" }); + list.appendAssumeCapacity(os_support_dir); const config_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "config" }); list.appendAssumeCapacity(config_dir); From 27cfbf949a25375a6ae7671a00ed3f7bae67d3f9 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 12 Feb 2022 18:41:16 +0100 Subject: [PATCH 0182/2031] macho: re-enable creating dSYM bundle * update number of type abbrevs to match Elf linker * update `DebugSymbols` to write symbol and string tables at the end to match the `MachO` linker * TODO: update segment vm addresses when growing segments in the binary * TODO: store DWARF relocations in linker's interned arena --- src/link/MachO.zig | 106 ++++--- src/link/MachO/DebugSymbols.zig | 499 ++++++++++++++++++++------------ 2 files changed, 370 insertions(+), 235 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index b8e2ae0840..5b4ce9b99c 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -354,32 +354,31 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { return self; } - // TODO Migrate DebugSymbols to the merged linker codepaths - // if (!options.strip and options.module != null) { - // // Create dSYM bundle. - // const dir = options.module.?.zig_cache_artifact_directory; - // log.debug("creating {s}.dSYM bundle in {s}", .{ sub_path, dir.path }); + if (!options.strip and options.module != null) { + // Create dSYM bundle. + const dir = options.module.?.zig_cache_artifact_directory; + log.debug("creating {s}.dSYM bundle in {s}", .{ emit.sub_path, dir.path }); - // const d_sym_path = try fmt.allocPrint( - // allocator, - // "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF", - // .{sub_path}, - // ); - // defer allocator.free(d_sym_path); + const d_sym_path = try fmt.allocPrint( + allocator, + "{s}.dSYM" ++ fs.path.sep_str ++ "Contents" ++ fs.path.sep_str ++ "Resources" ++ fs.path.sep_str ++ "DWARF", + .{emit.sub_path}, + ); + defer allocator.free(d_sym_path); - // var d_sym_bundle = try dir.handle.makeOpenPath(d_sym_path, .{}); - // defer d_sym_bundle.close(); + var d_sym_bundle = try dir.handle.makeOpenPath(d_sym_path, .{}); + defer d_sym_bundle.close(); - // const d_sym_file = try d_sym_bundle.createFile(sub_path, .{ - // .truncate = false, - // .read = true, - // }); + const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{ + .truncate = false, + .read = true, + }); - // self.d_sym = .{ - // .base = self, - // .file = d_sym_file, - // }; - // } + self.d_sym = .{ + .base = self, + .file = d_sym_file, + }; + } // Index 0 is always a null symbol. try self.locals.append(allocator, .{ @@ -393,8 +392,8 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { try self.populateMissingMetadata(); - if (self.d_sym) |*ds| { - try ds.populateMissingMetadata(allocator); + if (self.d_sym) |*d_sym| { + try d_sym.populateMissingMetadata(allocator); } return self; @@ -1048,9 +1047,9 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { try self.updateSectionOrdinals(); try self.writeLinkeditSegment(); - if (self.d_sym) |*ds| { + if (self.d_sym) |*d_sym| { // Flush debug symbols bundle. - try ds.flushModule(self.base.allocator, self.base.options); + try d_sym.flushModule(self.base.allocator, self.base.options); } if (self.requires_adhoc_codesig) { @@ -3374,8 +3373,8 @@ pub fn deinit(self: *MachO) void { if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator); } - if (self.d_sym) |*ds| { - ds.deinit(self.base.allocator); + if (self.d_sym) |*d_sym| { + d_sym.deinit(self.base.allocator); } self.section_ordinals.deinit(self.base.allocator); @@ -3497,13 +3496,13 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) } } - if (self.d_sym) |*ds| { - if (ds.dbg_info_decl_first == atom) { - ds.dbg_info_decl_first = atom.dbg_info_next; + if (self.d_sym) |*d_sym| { + if (d_sym.dbg_info_decl_first == atom) { + d_sym.dbg_info_decl_first = atom.dbg_info_next; } - if (ds.dbg_info_decl_last == atom) { + if (d_sym.dbg_info_decl_last == atom) { // TODO shrink the .debug_info section size here - ds.dbg_info_decl_last = atom.dbg_info_prev; + d_sym.dbg_info_decl_last = atom.dbg_info_prev; } } @@ -3675,6 +3674,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv const decl = func.owner_decl; self.freeUnnamedConsts(decl); + // TODO clearing the code and relocs buffer should probably be orchestrated // in a different, smarter, more automatic way somewhere else, in a more centralised // way than this. @@ -3686,8 +3686,8 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv defer code_buffer.deinit(); var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined; - const debug_buffers = if (self.d_sym) |*ds| blk: { - debug_buffers_buf = try ds.initDeclDebugBuffers(self.base.allocator, module, decl); + const debug_buffers = if (self.d_sym) |*d_sym| blk: { + debug_buffers_buf = try d_sym.initDeclDebugBuffers(self.base.allocator, module, decl); break :blk &debug_buffers_buf; } else null; defer { @@ -3725,13 +3725,9 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv _ = try self.placeDecl(decl, decl.link.macho.code.items.len); if (debug_buffers) |db| { - try self.d_sym.?.commitDeclDebugInfo( - self.base.allocator, - module, - decl, - db, - self.base.options.target, - ); + if (self.d_sym) |*d_sym| { + try d_sym.commitDeclDebugInfo(self.base.allocator, module, decl, db); + } } // Since we updated the vaddr and the size, each corresponding export symbol also @@ -3827,8 +3823,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { defer code_buffer.deinit(); var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined; - const debug_buffers = if (self.d_sym) |*ds| blk: { - debug_buffers_buf = try ds.initDeclDebugBuffers(self.base.allocator, module, decl); + const debug_buffers = if (self.d_sym) |*d_sym| blk: { + debug_buffers_buf = try d_sym.initDeclDebugBuffers(self.base.allocator, module, decl); break :blk &debug_buffers_buf; } else null; defer { @@ -4125,8 +4121,8 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 } pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void { - if (self.d_sym) |*ds| { - try ds.updateDeclLineNumber(module, decl); + if (self.d_sym) |*d_sym| { + try d_sym.updateDeclLineNumber(module, decl); } } @@ -4322,27 +4318,27 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { _ = self.atom_by_index_table.remove(decl.link.macho.local_sym_index); decl.link.macho.local_sym_index = 0; } - if (self.d_sym) |*ds| { + if (self.d_sym) |*d_sym| { // TODO make this logic match freeAtom. Maybe abstract the logic // out since the same thing is desired for both. - _ = ds.dbg_line_fn_free_list.remove(&decl.fn_link.macho); + _ = d_sym.dbg_line_fn_free_list.remove(&decl.fn_link.macho); if (decl.fn_link.macho.prev) |prev| { - ds.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {}; + d_sym.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {}; prev.next = decl.fn_link.macho.next; if (decl.fn_link.macho.next) |next| { next.prev = prev; } else { - ds.dbg_line_fn_last = prev; + d_sym.dbg_line_fn_last = prev; } } else if (decl.fn_link.macho.next) |next| { - ds.dbg_line_fn_first = next; + d_sym.dbg_line_fn_first = next; next.prev = null; } - if (ds.dbg_line_fn_first == &decl.fn_link.macho) { - ds.dbg_line_fn_first = decl.fn_link.macho.next; + if (d_sym.dbg_line_fn_first == &decl.fn_link.macho) { + d_sym.dbg_line_fn_first = decl.fn_link.macho.next; } - if (ds.dbg_line_fn_last == &decl.fn_link.macho) { - ds.dbg_line_fn_last = decl.fn_link.macho.prev; + if (d_sym.dbg_line_fn_last == &decl.fn_link.macho) { + d_sym.dbg_line_fn_last = decl.fn_link.macho.prev; } } } diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index beef0b6b2c..36d93b8255 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -3,7 +3,8 @@ const DebugSymbols = @This(); const std = @import("std"); const assert = std.debug.assert; const fs = std.fs; -const log = std.log.scoped(.dsym); +const log = std.log.scoped(.link); +const leb128 = std.leb; const macho = std.macho; const math = std.math; const mem = std.mem; @@ -22,8 +23,6 @@ const SrcFn = MachO.SrcFn; const makeStaticString = MachO.makeStaticString; const padToIdeal = MachO.padToIdeal; -const page_size: u16 = 0x1000; - base: *MachO, file: fs.File, @@ -49,9 +48,6 @@ uuid_cmd_index: ?u16 = null, /// Index into __TEXT,__text section. text_section_index: ?u16 = null, -linkedit_off: u16 = page_size, -linkedit_size: u16 = page_size, - debug_info_section_index: ?u16 = null, debug_abbrev_section_index: ?u16 = null, debug_str_section_index: ?u16 = null, @@ -76,7 +72,6 @@ dbg_info_decl_last: ?*TextBlock = null, debug_string_table: std.ArrayListUnmanaged(u8) = .{}, load_commands_dirty: bool = false, -strtab_dirty: bool = false, debug_string_table_dirty: bool = false, debug_abbrev_section_dirty: bool = false, debug_aranges_section_dirty: bool = false, @@ -87,8 +82,12 @@ const abbrev_compile_unit = 1; const abbrev_subprogram = 2; const abbrev_subprogram_retvoid = 3; const abbrev_base_type = 4; -const abbrev_pad1 = 5; -const abbrev_parameter = 6; +const abbrev_ptr_type = 5; +const abbrev_struct_type = 6; +const abbrev_anon_struct_type = 7; +const abbrev_struct_member = 8; +const abbrev_pad1 = 9; +const abbrev_parameter = 10; /// The reloc offset for the virtual address of a function in its Line Number Program. /// Size is a virtual address integer. @@ -108,30 +107,21 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void try self.load_commands.append(allocator, base_cmd); self.load_commands_dirty = true; } + if (self.symtab_cmd_index == null) { self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); - const base_cmd = self.base.load_commands.items[self.base.symtab_cmd_index.?].symtab; - const symtab_size = base_cmd.nsyms * @sizeOf(macho.nlist_64); - const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64)); - - log.debug("found symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size }); - - const strtab_off = self.findFreeSpaceLinkedit(base_cmd.strsize, 1); - - log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + base_cmd.strsize }); - - try self.load_commands.append(allocator, .{ + try self.load_commands.append(self.base.base.allocator, .{ .symtab = .{ .cmdsize = @sizeOf(macho.symtab_command), - .symoff = @intCast(u32, symtab_off), - .nsyms = base_cmd.nsyms, - .stroff = @intCast(u32, strtab_off), - .strsize = base_cmd.strsize, + .symoff = 0, + .nsyms = 0, + .stroff = 0, + .strsize = 0, }, }); self.load_commands_dirty = true; - self.strtab_dirty = true; } + if (self.pagezero_segment_cmd_index == null) { self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); const base_cmd = self.base.load_commands.items[self.base.pagezero_segment_cmd_index.?].segment; @@ -139,6 +129,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void try self.load_commands.append(allocator, .{ .segment = cmd }); self.load_commands_dirty = true; } + if (self.text_segment_cmd_index == null) { self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len); const base_cmd = self.base.load_commands.items[self.base.text_segment_cmd_index.?].segment; @@ -146,6 +137,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void try self.load_commands.append(allocator, .{ .segment = cmd }); self.load_commands_dirty = true; } + if (self.data_const_segment_cmd_index == null) outer: { if (self.base.data_const_segment_cmd_index == null) break :outer; // __DATA_CONST is optional self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len); @@ -154,6 +146,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void try self.load_commands.append(allocator, .{ .segment = cmd }); self.load_commands_dirty = true; } + if (self.data_segment_cmd_index == null) outer: { if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len); @@ -162,26 +155,29 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void try self.load_commands.append(allocator, .{ .segment = cmd }); self.load_commands_dirty = true; } + if (self.linkedit_segment_cmd_index == null) { self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len); const base_cmd = self.base.load_commands.items[self.base.linkedit_segment_cmd_index.?].segment; var cmd = try self.copySegmentCommand(allocator, base_cmd); - cmd.inner.vmsize = self.linkedit_size; - cmd.inner.fileoff = self.linkedit_off; - cmd.inner.filesize = self.linkedit_size; + // TODO this needs reworking + cmd.inner.vmsize = self.base.page_size; + cmd.inner.fileoff = self.base.page_size; + cmd.inner.filesize = self.base.page_size; try self.load_commands.append(allocator, .{ .segment = cmd }); self.load_commands_dirty = true; } + if (self.dwarf_segment_cmd_index == null) { self.dwarf_segment_cmd_index = @intCast(u16, self.load_commands.items.len); const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; const ideal_size: u16 = 200 + 128 + 160 + 250; - const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), page_size); - const off = linkedit.inner.fileoff + linkedit.inner.filesize; + const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.base.page_size); + const fileoff = linkedit.inner.fileoff + linkedit.inner.filesize; const vmaddr = linkedit.inner.vmaddr + linkedit.inner.vmsize; - log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size }); + log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size }); try self.load_commands.append(allocator, .{ .segment = .{ @@ -189,13 +185,14 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void .segname = makeStaticString("__DWARF"), .vmaddr = vmaddr, .vmsize = needed_size, - .fileoff = off, + .fileoff = fileoff, .filesize = needed_size, }, }, }); self.load_commands_dirty = true; } + if (self.debug_str_section_index == null) { assert(self.debug_string_table.items.len == 0); self.debug_str_section_index = try self.allocateSection( @@ -205,18 +202,22 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void ); self.debug_string_table_dirty = true; } + if (self.debug_info_section_index == null) { self.debug_info_section_index = try self.allocateSection("__debug_info", 200, 0); self.debug_info_header_dirty = true; } + if (self.debug_abbrev_section_index == null) { self.debug_abbrev_section_index = try self.allocateSection("__debug_abbrev", 128, 0); self.debug_abbrev_section_dirty = true; } + if (self.debug_aranges_section_index == null) { self.debug_aranges_section_index = try self.allocateSection("__debug_aranges", 160, 4); self.debug_aranges_section_dirty = true; } + if (self.debug_line_section_index == null) { self.debug_line_section_index = try self.allocateSection("__debug_line", 250, 0); self.debug_line_header_dirty = true; @@ -300,41 +301,91 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti // we can simply append these bytes. const abbrev_buf = [_]u8{ abbrev_compile_unit, DW.TAG.compile_unit, DW.CHILDREN.yes, // header - DW.AT.stmt_list, DW.FORM.sec_offset, // offset - DW.AT.low_pc, DW.FORM.addr, - DW.AT.high_pc, DW.FORM.addr, - DW.AT.name, DW.FORM.strp, - DW.AT.comp_dir, DW.FORM.strp, - DW.AT.producer, DW.FORM.strp, - DW.AT.language, DW.FORM.data2, - 0, 0, // table sentinel - abbrev_subprogram, DW.TAG.subprogram, DW.CHILDREN.yes, // header - DW.AT.low_pc, DW.FORM.addr, // start VM address - DW.AT.high_pc, DW.FORM.data4, - DW.AT.type, DW.FORM.ref4, - DW.AT.name, DW.FORM.string, - DW.AT.decl_line, DW.FORM.data4, - DW.AT.decl_file, DW.FORM.data1, + DW.AT.stmt_list, DW.FORM.sec_offset, DW.AT.low_pc, + DW.FORM.addr, DW.AT.high_pc, DW.FORM.addr, + DW.AT.name, DW.FORM.strp, DW.AT.comp_dir, + DW.FORM.strp, DW.AT.producer, DW.FORM.strp, + DW.AT.language, DW.FORM.data2, 0, + 0, // table sentinel + abbrev_subprogram, + DW.TAG.subprogram, + DW.CHILDREN.yes, // header + DW.AT.low_pc, + DW.FORM.addr, + DW.AT.high_pc, + DW.FORM.data4, + DW.AT.type, + DW.FORM.ref4, + DW.AT.name, + DW.FORM.string, 0, 0, // table sentinel abbrev_subprogram_retvoid, DW.TAG.subprogram, DW.CHILDREN.yes, // header DW.AT.low_pc, DW.FORM.addr, DW.AT.high_pc, DW.FORM.data4, DW.AT.name, DW.FORM.string, - DW.AT.decl_line, DW.FORM.data4, - DW.AT.decl_file, DW.FORM.data1, - 0, 0, // table sentinel - abbrev_base_type, DW.TAG.base_type, DW.CHILDREN.no, // header - DW.AT.encoding, DW.FORM.data1, DW.AT.byte_size, - DW.FORM.data1, DW.AT.name, DW.FORM.string, - 0, 0, // table sentinel - abbrev_pad1, DW.TAG.unspecified_type, DW.CHILDREN.no, // header - 0, 0, // table sentinel - abbrev_parameter, DW.TAG.formal_parameter, DW.CHILDREN.no, // header - DW.AT.location, DW.FORM.exprloc, DW.AT.type, - DW.FORM.ref4, DW.AT.name, DW.FORM.string, - 0, 0, // table sentinel - 0, 0, 0, // section sentinel + 0, + 0, // table sentinel + abbrev_base_type, + DW.TAG.base_type, + DW.CHILDREN.no, // header + DW.AT.encoding, + DW.FORM.data1, + DW.AT.byte_size, + DW.FORM.data1, + DW.AT.name, + DW.FORM.string, + 0, + 0, // table sentinel + abbrev_ptr_type, + DW.TAG.pointer_type, + DW.CHILDREN.no, // header + DW.AT.type, + DW.FORM.ref4, + 0, + 0, // table sentinel + abbrev_struct_type, + DW.TAG.structure_type, + DW.CHILDREN.yes, // header + DW.AT.byte_size, + DW.FORM.sdata, + DW.AT.name, + DW.FORM.string, + 0, + 0, // table sentinel + abbrev_anon_struct_type, + DW.TAG.structure_type, + DW.CHILDREN.yes, // header + DW.AT.byte_size, + DW.FORM.sdata, + 0, + 0, // table sentinel + abbrev_struct_member, + DW.TAG.member, + DW.CHILDREN.no, // header + DW.AT.name, + DW.FORM.string, + DW.AT.type, + DW.FORM.ref4, + DW.AT.data_member_location, + DW.FORM.sdata, + 0, + 0, // table sentinel + abbrev_pad1, + DW.TAG.unspecified_type, + DW.CHILDREN.no, // header + 0, + 0, // table sentinel + abbrev_parameter, + DW.TAG.formal_parameter, DW.CHILDREN.no, // header + DW.AT.location, DW.FORM.exprloc, + DW.AT.type, DW.FORM.ref4, + DW.AT.name, DW.FORM.string, + 0, + 0, // table sentinel + 0, + 0, + 0, // section sentinel }; const needed_size = abbrev_buf.len; @@ -583,13 +634,12 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti } } - try self.writeStringTable(); + try self.writeLinkeditSegment(); self.updateDwarfSegment(); try self.writeLoadCommands(allocator); try self.writeHeader(); assert(!self.load_commands_dirty); - assert(!self.strtab_dirty); assert(!self.debug_abbrev_section_dirty); assert(!self.debug_aranges_section_dirty); assert(!self.debug_string_table_dirty); @@ -663,7 +713,7 @@ fn updateDwarfSegment(self: *DebugSymbols) void { if (file_size != dwarf_segment.inner.filesize) { dwarf_segment.inner.filesize = file_size; if (dwarf_segment.inner.vmsize < dwarf_segment.inner.filesize) { - dwarf_segment.inner.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.inner.filesize, page_size); + dwarf_segment.inner.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.inner.filesize, self.base.page_size); } self.load_commands_dirty = true; } @@ -719,23 +769,10 @@ fn writeHeader(self: *DebugSymbols) !void { try self.file.pwriteAll(mem.asBytes(&header), 0); } -fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 { - assert(start > 0); - var min_pos: u64 = std.math.maxInt(u64); - - if (self.symtab_cmd_index) |idx| { - const symtab = self.load_commands.items[idx].symtab; - if (symtab.symoff >= start and symtab.symoff < min_pos) min_pos = symtab.symoff; - if (symtab.stroff >= start and symtab.stroff < min_pos) min_pos = symtab.stroff; - } - - return min_pos - start; -} - fn allocatedSize(self: *DebugSymbols, start: u64) u64 { const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; assert(start >= seg.inner.fileoff); - var min_pos: u64 = seg.inner.fileoff + seg.inner.filesize; + var min_pos: u64 = std.math.maxInt(u64); for (seg.sections.items) |section| { if (section.offset <= start) continue; if (section.offset < min_pos) min_pos = section.offset; @@ -743,102 +780,72 @@ fn allocatedSize(self: *DebugSymbols, start: u64) u64 { return min_pos - start; } -fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64 { - const end = start + padToIdeal(size); - - if (self.symtab_cmd_index) |idx| outer: { - if (self.load_commands.items.len == idx) break :outer; - const symtab = self.load_commands.items[idx].symtab; - { - // Symbol table - const symsize = symtab.nsyms * @sizeOf(macho.nlist_64); - const increased_size = padToIdeal(symsize); - const test_end = symtab.symoff + increased_size; - if (end > symtab.symoff and start < test_end) { - return test_end; - } - } - { - // String table - const increased_size = padToIdeal(symtab.strsize); - const test_end = symtab.stroff + increased_size; - if (end > symtab.stroff and start < test_end) { - return test_end; - } - } - } - - return null; -} - -fn findFreeSpaceLinkedit(self: *DebugSymbols, object_size: u64, min_alignment: u16) u64 { - var start: u64 = self.linkedit_off; - while (self.detectAllocCollisionLinkedit(start, object_size)) |item_end| { - start = mem.alignForwardGeneric(u64, item_end, min_alignment); - } - return start; -} - -fn relocateSymbolTable(self: *DebugSymbols) !void { - const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab; - const nlocals = self.base.locals.items.len; - const nglobals = self.base.globals.items.len; - const nsyms = nlocals + nglobals; - - if (symtab.nsyms < nsyms) { - const needed_size = nsyms * @sizeOf(macho.nlist_64); - if (needed_size > self.allocatedSizeLinkedit(symtab.symoff)) { - // Move the entire symbol table to a new location - const new_symoff = self.findFreeSpaceLinkedit(needed_size, @alignOf(macho.nlist_64)); - const existing_size = symtab.nsyms * @sizeOf(macho.nlist_64); - - assert(new_symoff + existing_size <= self.linkedit_off + self.linkedit_size); // TODO expand LINKEDIT segment. - log.debug("relocating symbol table from 0x{x}-0x{x} to 0x{x}-0x{x}", .{ - symtab.symoff, - symtab.symoff + existing_size, - new_symoff, - new_symoff + existing_size, - }); - - const amt = try self.file.copyRangeAll(symtab.symoff, self.file, new_symoff, existing_size); - if (amt != existing_size) return error.InputOutput; - symtab.symoff = @intCast(u32, new_symoff); - } - symtab.nsyms = @intCast(u32, nsyms); - self.load_commands_dirty = true; - } -} - -pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void { +fn writeLinkeditSegment(self: *DebugSymbols) !void { const tracy = trace(@src()); defer tracy.end(); - try self.relocateSymbolTable(); + + const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; + seg.inner.filesize = 0; + + try self.writeSymbolTable(); + try self.writeStringTable(); +} + +fn writeSymbolTable(self: *DebugSymbols) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab; - const off = symtab.symoff + @sizeOf(macho.nlist_64) * index; - log.debug("writing local symbol {} at 0x{x}", .{ index, off }); - try self.file.pwriteAll(mem.asBytes(&self.base.locals.items[index]), off); + symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); + + var locals = std.ArrayList(macho.nlist_64).init(self.base.base.allocator); + defer locals.deinit(); + + for (self.base.locals.items) |sym| { + if (sym.n_strx == 0) continue; + if (self.base.symbol_resolver.get(sym.n_strx)) |_| continue; + try locals.append(sym); + } + + const nlocals = locals.items.len; + const nexports = self.base.globals.items.len; + + const locals_off = symtab.symoff; + const locals_size = nlocals * @sizeOf(macho.nlist_64); + log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off }); + try self.file.pwriteAll(mem.sliceAsBytes(locals.items), locals_off); + + const exports_off = locals_off + locals_size; + const exports_size = nexports * @sizeOf(macho.nlist_64); + log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off }); + try self.file.pwriteAll(mem.sliceAsBytes(self.base.globals.items), exports_off); + + symtab.nsyms = @intCast(u32, nlocals + nexports); + seg.inner.filesize += locals_size + exports_size; + + self.load_commands_dirty = true; } fn writeStringTable(self: *DebugSymbols) !void { - if (!self.strtab_dirty) return; - const tracy = trace(@src()); defer tracy.end(); + const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab; - const allocated_size = self.allocatedSizeLinkedit(symtab.stroff); - const needed_size = mem.alignForwardGeneric(u64, self.base.strtab.items.len, @alignOf(u64)); + symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); + symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.base.strtab.items.len, @alignOf(u64))); + seg.inner.filesize += symtab.strsize; - if (needed_size > allocated_size) { - symtab.strsize = 0; - symtab.stroff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1)); - } - symtab.strsize = @intCast(u32, needed_size); log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize }); try self.file.pwriteAll(self.base.strtab.items, symtab.stroff); + + if (symtab.strsize > self.base.strtab.items.len) { + // This is potentially the last section, so we need to pad it out. + try self.file.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1); + } self.load_commands_dirty = true; - self.strtab_dirty = false; } pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const Module.Decl) !void { @@ -846,14 +853,21 @@ pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const M const tracy = trace(@src()); defer tracy.end(); + log.debug("updateDeclLineNumber {s}{*}", .{ decl.name, decl }); + const func = decl.val.castTag(.function).?.data; - const line_off = @intCast(u28, decl.src_line + func.lbrace_line); + log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{ + decl.src_line, + func.lbrace_line, + func.rbrace_line, + }); + const line = @intCast(u28, decl.src_line + func.lbrace_line); const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; const shdr = &dwarf_segment.sections.items[self.debug_line_section_index.?]; const file_pos = shdr.offset + decl.fn_link.macho.off + getRelocDbgLineOff(); var data: [4]u8 = undefined; - leb.writeUnsignedFixed(4, &data, line_off); + leb.writeUnsignedFixed(4, &data, line); try self.file.pwriteAll(&data, file_pos); } @@ -886,7 +900,13 @@ pub fn initDeclDebugBuffers( try dbg_line_buffer.ensureTotalCapacity(26); const func = decl.val.castTag(.function).?.data; - const line_off = @intCast(u28, decl.src_line + func.lbrace_line); + log.debug("updateFunc {s}{*}", .{ decl.name, func.owner_decl }); + log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{ + decl.src_line, + func.lbrace_line, + func.rbrace_line, + }); + const line = @intCast(u28, decl.src_line + func.lbrace_line); dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{ DW.LNS.extended_op, @@ -902,7 +922,7 @@ pub fn initDeclDebugBuffers( // to this function's begin curly. assert(getRelocDbgLineOff() == dbg_line_buffer.items.len); // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later. - leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off); + leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line); dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_file); assert(getRelocDbgFileIndex() == dbg_line_buffer.items.len); @@ -917,7 +937,7 @@ pub fn initDeclDebugBuffers( // .debug_info subprogram const decl_name_with_null = decl.name[0 .. mem.sliceTo(decl.name, 0).len + 1]; - try dbg_info_buffer.ensureUnusedCapacity(27 + decl_name_with_null.len); + try dbg_info_buffer.ensureUnusedCapacity(25 + decl_name_with_null.len); const fn_ret_type = decl.ty.fnReturnType(); const fn_ret_has_bits = fn_ret_type.hasRuntimeBits(); @@ -945,8 +965,6 @@ pub fn initDeclDebugBuffers( dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4 } dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT.name, DW.FORM.string - mem.writeIntLittle(u32, dbg_info_buffer.addManyAsArrayAssumeCapacity(4), line_off + 1); // DW.AT.decl_line, DW.FORM.data4 - dbg_info_buffer.appendAssumeCapacity(file_index); // DW.AT.decl_file, DW.FORM.data1 }, else => { // TODO implement .debug_info for global variables @@ -966,7 +984,6 @@ pub fn commitDeclDebugInfo( module: *Module, decl: *Module.Decl, debug_buffers: *DeclDebugBuffers, - target: std.Target, ) !void { const tracy = trace(@src()); defer tracy.end(); @@ -1097,14 +1114,26 @@ pub fn commitDeclDebugInfo( if (dbg_info_buffer.items.len == 0) return; + // We need this for the duration of this function only so that for composite + // types such as []const u32, if the type *u32 is non-existent, we create + // it synthetically and store the backing bytes in this arena. After we are + // done with the relocations, we can safely deinit the entire memory slab. + // TODO currently, we do not store the relocations for future use, however, + // if that is the case, we should move memory management to a higher scope, + // such as linker scope, or whatnot. + var dbg_type_arena = std.heap.ArenaAllocator.init(allocator); + defer dbg_type_arena.deinit(); + { // Now we emit the .debug_info types of the Decl. These will count towards the size of // the buffer, so we have to do it before computing the offset, and we can't perform the actual // relocations yet. - var it = dbg_info_type_relocs.iterator(); - while (it.next()) |entry| { - entry.value_ptr.off = @intCast(u32, dbg_info_buffer.items.len); - try self.addDbgInfoType(entry.key_ptr.*, dbg_info_buffer, target); + var it: usize = 0; + while (it < dbg_info_type_relocs.count()) : (it += 1) { + const ty = dbg_info_type_relocs.keys()[it]; + const value_ptr = dbg_info_type_relocs.getPtr(ty).?; + value_ptr.off = @intCast(u32, dbg_info_buffer.items.len); + try self.addDbgInfoType(dbg_type_arena.allocator(), ty, dbg_info_buffer, dbg_info_type_relocs); } } @@ -1129,24 +1158,25 @@ pub fn commitDeclDebugInfo( /// Asserts the type has codegen bits. fn addDbgInfoType( self: *DebugSymbols, + arena: Allocator, ty: Type, dbg_info_buffer: *std.ArrayList(u8), - target: std.Target, + dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable, ) !void { - _ = self; + const target = self.base.base.options.target; + var relocs = std.ArrayList(struct { ty: Type, reloc: u32 }).init(arena); + switch (ty.zigTypeTag()) { - .Void => unreachable, .NoReturn => unreachable, + .Void => { + try dbg_info_buffer.append(abbrev_pad1); + }, .Bool => { try dbg_info_buffer.appendSlice(&[_]u8{ abbrev_base_type, DW.ATE.boolean, // DW.AT.encoding , DW.FORM.data1 1, // DW.AT.byte_size, DW.FORM.data1 - 'b', - 'o', - 'o', - 'l', - 0, // DW.AT.name, DW.FORM.string + 'b', 'o', 'o', 'l', 0, // DW.AT.name, DW.FORM.string }); }, .Int => { @@ -1163,11 +1193,120 @@ fn addDbgInfoType( // DW.AT.name, DW.FORM.string try dbg_info_buffer.writer().print("{}\x00", .{ty}); }, + .Optional => { + if (ty.isPtrLikeOptional()) { + try dbg_info_buffer.ensureUnusedCapacity(12); + dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); + // DW.AT.encoding, DW.FORM.data1 + dbg_info_buffer.appendAssumeCapacity(DW.ATE.address); + // DW.AT.byte_size, DW.FORM.data1 + dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); + // DW.AT.name, DW.FORM.string + try dbg_info_buffer.writer().print("{}\x00", .{ty}); + } else { + log.debug("TODO implement .debug_info for type '{}'", .{ty}); + try dbg_info_buffer.append(abbrev_pad1); + } + }, + .Pointer => { + if (ty.isSlice()) { + // Slices are anonymous structs: struct { .ptr = *, .len = N } + try dbg_info_buffer.ensureUnusedCapacity(23); + // DW.AT.structure_type + dbg_info_buffer.appendAssumeCapacity(abbrev_anon_struct_type); + // DW.AT.byte_size, DW.FORM.sdata + dbg_info_buffer.appendAssumeCapacity(16); + // DW.AT.member + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity("ptr"); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + var index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + var buf = try arena.create(Type.SlicePtrFieldTypeBuffer); + const ptr_ty = ty.slicePtrFieldType(buf); + try relocs.append(.{ .ty = ptr_ty, .reloc = @intCast(u32, index) }); + // DW.AT.data_member_location, DW.FORM.sdata + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.member + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity("len"); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try relocs.append(.{ .ty = Type.initTag(.usize), .reloc = @intCast(u32, index) }); + // DW.AT.data_member_location, DW.FORM.sdata + dbg_info_buffer.appendAssumeCapacity(8); + // DW.AT.structure_type delimit children + dbg_info_buffer.appendAssumeCapacity(0); + } else { + try dbg_info_buffer.ensureUnusedCapacity(5); + dbg_info_buffer.appendAssumeCapacity(abbrev_ptr_type); + // DW.AT.type, DW.FORM.ref4 + const index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try relocs.append(.{ .ty = ty.childType(), .reloc = @intCast(u32, index) }); + } + }, + .Struct => blk: { + // try dbg_info_buffer.ensureUnusedCapacity(23); + // DW.AT.structure_type + try dbg_info_buffer.append(abbrev_struct_type); + // DW.AT.byte_size, DW.FORM.sdata + const abi_size = ty.abiSize(target); + try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); + // DW.AT.name, DW.FORM.string + const struct_name = try ty.nameAlloc(arena); + try dbg_info_buffer.ensureUnusedCapacity(struct_name.len + 1); + dbg_info_buffer.appendSliceAssumeCapacity(struct_name); + dbg_info_buffer.appendAssumeCapacity(0); + + const struct_obj = ty.castTag(.@"struct").?.data; + if (struct_obj.layout == .Packed) { + log.debug("TODO implement .debug_info for packed structs", .{}); + break :blk; + } + + const fields = ty.structFields(); + for (fields.keys()) |field_name, field_index| { + const field = fields.get(field_name).?; + // DW.AT.member + try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2); + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity(field_name); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + var index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try relocs.append(.{ .ty = field.ty, .reloc = @intCast(u32, index) }); + // DW.AT.data_member_location, DW.FORM.sdata + const field_off = ty.structFieldOffset(field_index, target); + try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); + } + + // DW.AT.structure_type delimit children + try dbg_info_buffer.append(0); + }, else => { - std.log.scoped(.compiler).err("TODO implement .debug_info for type '{}'", .{ty}); + log.debug("TODO implement .debug_info for type '{}'", .{ty}); try dbg_info_buffer.append(abbrev_pad1); }, } + + for (relocs.items) |rel| { + const gop = try dbg_info_type_relocs.getOrPut(self.base.base.allocator, rel.ty); + if (!gop.found_existing) { + gop.value_ptr.* = .{ + .off = undefined, + .relocs = .{}, + }; + } + try gop.value_ptr.relocs.append(self.base.base.allocator, rel.reloc); + } } fn updateDeclDebugInfoAllocation( From d164865308fcc0870c21da30431caf5d15a4f978 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 14 Feb 2022 12:26:15 -0700 Subject: [PATCH 0183/2031] add missing source file to CMakeLists.txt --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index a6edfa04ac..505de972b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -655,6 +655,7 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/print_env.zig" "${CMAKE_SOURCE_DIR}/src/print_targets.zig" "${CMAKE_SOURCE_DIR}/src/print_zir.zig" + "${CMAKE_SOURCE_DIR}/src/register_manager.zig" "${CMAKE_SOURCE_DIR}/src/stage1.zig" "${CMAKE_SOURCE_DIR}/src/target.zig" "${CMAKE_SOURCE_DIR}/src/tracy.zig" From 1e49d1fca89a0fce1604a8188257fa6ea2749338 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 14 Feb 2022 12:26:55 -0700 Subject: [PATCH 0184/2031] langref: correct info about type info of declarations --- doc/langref.html.in | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index fd104db6da..c0b8c9cb48 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -9513,9 +9513,14 @@ test "integer truncation" { Provides type reflection.

      - For {#link|structs|struct#}, {#link|unions|union#}, {#link|enums|enum#}, and - {#link|error sets|Error Set Type#}, the fields are guaranteed to be in the same - order as declared. For declarations, the order is unspecified. + Type information of {#link|structs|struct#}, {#link|unions|union#}, {#link|enums|enum#}, and + {#link|error sets|Error Set Type#} has fields which are are guaranteed to be in the same + order as appearance in the source file. +

      +

      + Type information of {#link|structs|struct#}, {#link|unions|union#}, {#link|enums|enum#}, and + {#link|opaques|opaque#} has declarations, which are also guaranteed to be in the same + order as appearance in the source file.

      {#header_close#} From 7b938767bb18535a870d0460c9f4d9e3d93ab053 Mon Sep 17 00:00:00 2001 From: ominitay <37453713+ominitay@users.noreply.github.com> Date: Sun, 30 Jan 2022 13:22:49 +0000 Subject: [PATCH 0185/2031] std.os: throw compile error for `argv` on Windows On Windows, `argv` is not populated by start code, and instead left as undefined. This is problematic, and can lead to incorrect programs compiling, but panicking when trying to access `argv`. This change causes these programs to produce a compile error on Windows instead, which is far preferable to a runtime panic. --- lib/std/os.zig | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 16a32766dc..d7f60c1e1a 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -218,9 +218,13 @@ pub const socket_t = if (builtin.os.tag == .windows) windows.ws2_32.SOCKET else pub var environ: [][*:0]u8 = undefined; /// Populated by startup code before main(). -/// Not available on Windows. See `std.process.args` -/// for obtaining the process arguments. -pub var argv: [][*:0]u8 = undefined; +/// Not available on WASI or Windows without libc. See `std.process.argsAlloc` +/// or `std.process.argsWithAllocator` for a cross-platform alternative. +pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (builtin.os.tag) { + .windows => @compileError("argv isn't supported on Windows: use std.process.argsAlloc instead"), + .wasi => @compileError("argv isn't supported on WASI: use std.process.argsAlloc instead"), + else => undefined, +}; /// To obtain errno, call this function with the return value of the /// system function call. For some systems this will obtain the value directly From 0d16e908fbb93cdaee80bc2514f76a09736bfa04 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Tue, 1 Feb 2022 18:57:51 +0100 Subject: [PATCH 0186/2031] stage2 AArch64: implement is_err/is_non_err for simple error unions --- src/arch/aarch64/CodeGen.zig | 93 ++++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 20 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 79fa38e275..fd9211d919 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -1104,7 +1104,13 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { fn airUnwrapErrPayload(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 return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch}); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const error_union_ty = self.air.typeOf(ty_op.operand); + const payload_ty = error_union_ty.errorUnionPayload(); + if (!payload_ty.hasRuntimeBits()) break :result MCValue.none; + + return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{}); + }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -2008,18 +2014,52 @@ fn isNonNull(self: *Self, operand: MCValue) !MCValue { return self.fail("TODO call isNull and invert the result", .{}); } -fn isErr(self: *Self, operand: MCValue) !MCValue { +fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { _ = operand; - // Here you can specialize this instruction if it makes sense to, otherwise the default - // will call isNonNull and invert the result. - return self.fail("TODO call isNonErr and invert the result", .{}); + + const error_type = ty.errorUnionSet(); + const payload_type = ty.errorUnionPayload(); + + if (!error_type.hasRuntimeBits()) { + return MCValue{ .immediate = 0 }; // always false + } else if (!payload_type.hasRuntimeBits()) { + if (error_type.abiSize(self.target.*) <= 8) { + const reg_mcv: MCValue = switch (operand) { + .register => operand, + else => .{ .register = try self.copyToTmpRegister(error_type, operand) }, + }; + + _ = try self.addInst(.{ + .tag = .cmp_immediate, + .data = .{ .rr_imm12_sh = .{ + .rd = .xzr, + .rn = reg_mcv.register, + .imm12 = 0, + } }, + }); + + return MCValue{ .compare_flags_unsigned = .gt }; + } else { + return self.fail("TODO isErr for errors with size > 8", .{}); + } + } else { + return self.fail("TODO isErr for non-empty payloads", .{}); + } } -fn isNonErr(self: *Self, operand: MCValue) !MCValue { - _ = operand; - // Here you can specialize this instruction if it makes sense to, otherwise the default - // will call isNull and invert the result. - return self.fail("TODO call isErr and invert the result", .{}); +fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { + const is_err_result = try self.isErr(ty, operand); + switch (is_err_result) { + .compare_flags_unsigned => |op| { + assert(op == .gt); + return MCValue{ .compare_flags_unsigned = .lte }; + }, + .immediate => |imm| { + assert(imm == 0); + return MCValue{ .immediate = 1 }; + }, + else => unreachable, + } } fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { @@ -2080,7 +2120,8 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand = try self.resolveInst(un_op); - break :result try self.isErr(operand); + const ty = self.air.typeOf(un_op); + break :result try self.isErr(ty, operand); }; return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -2089,6 +2130,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand_ptr = try self.resolveInst(un_op); + const ptr_ty = self.air.typeOf(un_op); const operand: MCValue = blk: { if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { // The MCValue that holds the pointer can be re-used as the value. @@ -2098,7 +2140,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { } }; try self.load(operand, operand_ptr, self.air.typeOf(un_op)); - break :result try self.isErr(operand); + break :result try self.isErr(ptr_ty.elemType(), operand); }; return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -2107,7 +2149,8 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand = try self.resolveInst(un_op); - break :result try self.isNonErr(operand); + const ty = self.air.typeOf(un_op); + break :result try self.isNonErr(ty, operand); }; return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -2116,6 +2159,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand_ptr = try self.resolveInst(un_op); + const ptr_ty = self.air.typeOf(un_op); const operand: MCValue = blk: { if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { // The MCValue that holds the pointer can be re-used as the value. @@ -2125,7 +2169,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { } }; try self.load(operand, operand_ptr, self.air.typeOf(un_op)); - break :result try self.isNonErr(operand); + break :result try self.isNonErr(ptr_ty.elemType(), operand); }; return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -2864,14 +2908,23 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { .ErrorUnion => { const error_type = typed_value.ty.errorUnionSet(); const payload_type = typed_value.ty.errorUnionPayload(); - const sub_val = typed_value.val.castTag(.eu_payload).?.data; - if (!payload_type.hasRuntimeBits()) { - // We use the error type directly as the type. - return self.genTypedValue(.{ .ty = error_type, .val = sub_val }); + if (typed_value.val.castTag(.eu_payload)) |pl| { + if (!payload_type.hasRuntimeBits()) { + // We use the error type directly as the type. + return MCValue{ .immediate = 0 }; + } + + _ = pl; + return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty}); + } else { + if (!payload_type.hasRuntimeBits()) { + // We use the error type directly as the type. + return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); + } + + return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty}); } - - return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty}); }, else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}), } From 77cf000438c8f65f089e9c64fbb881a9136f2931 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Tue, 1 Feb 2022 20:26:23 +0100 Subject: [PATCH 0187/2031] stage2 AArch64: implement loading from register --- src/arch/aarch64/CodeGen.zig | 138 ++++++++++++++++++++++++++++++++++- src/arch/aarch64/Mir.zig | 2 +- 2 files changed, 137 insertions(+), 3 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index fd9211d919..39eaa77ad5 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -1301,8 +1301,64 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .embedded_in_code => { return self.fail("TODO implement loading from MCValue.embedded_in_code", .{}); }, - .register => { - return self.fail("TODO implement loading from MCValue.register for {}", .{self.target.cpu.arch}); + .register => |addr_reg| { + self.register_manager.freezeRegs(&.{addr_reg}); + defer self.register_manager.unfreezeRegs(&.{addr_reg}); + + switch (dst_mcv) { + .dead => unreachable, + .undef => unreachable, + .compare_flags_signed, .compare_flags_unsigned => unreachable, + .embedded_in_code => unreachable, + .stack_offset => |off| { + if (elem_ty.abiSize(self.target.*) <= 8) { + const tmp_reg = try self.register_manager.allocReg(null); + self.register_manager.freezeRegs(&.{tmp_reg}); + defer self.register_manager.unfreezeRegs(&.{tmp_reg}); + + try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); + try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); + } else { + // TODO optimize the register allocation + const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }); + self.register_manager.freezeRegs(®s); + defer self.register_manager.unfreezeRegs(®s); + + const src_reg = addr_reg; + const dst_reg = regs[0]; + const len_reg = regs[1]; + const count_reg = regs[2]; + const tmp_reg = regs[3]; + + // sub dst_reg, fp, #off + const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*)); + const adj_off = off + elem_size; + const offset = math.cast(u12, adj_off) catch return self.fail("TODO load: larger stack offsets", .{}); + _ = try self.addInst(.{ + .tag = .sub_immediate, + .data = .{ .rr_imm12_sh = .{ + .rd = dst_reg, + .rn = .x29, + .imm12 = offset, + } }, + }); + + // mov len, #elem_size + const len_imm = math.cast(u16, elem_size) catch return self.fail("TODO load: larger stack offsets", .{}); + _ = try self.addInst(.{ + .tag = .movk, + .data = .{ .r_imm16_sh = .{ + .rd = len_reg, + .imm16 = len_imm, + } }, + }); + + // memcpy(src, dst, len) + try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); + } + }, + else => return self.fail("TODO load from register into {}", .{dst_mcv}), + } }, .memory, .stack_offset, @@ -1317,6 +1373,84 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo } } +fn genInlineMemcpy( + self: *Self, + src: Register, + dst: Register, + len: Register, + count: Register, + tmp: Register, +) !void { + // movk count, #0 + _ = try self.addInst(.{ + .tag = .movk, + .data = .{ .r_imm16_sh = .{ + .rd = count, + .imm16 = 0, + } }, + }); + + // loop: + // cmp count, len + _ = try self.addInst(.{ + .tag = .cmp_shifted_register, + .data = .{ .rrr_imm6_shift = .{ + .rd = .xzr, + .rn = count, + .rm = len, + .imm6 = 0, + .shift = .lsl, + } }, + }); + + // bge end + _ = try self.addInst(.{ + .tag = .b_cond, + .data = .{ .inst_cond = .{ + .inst = @intCast(u32, self.mir_instructions.len + 5), + .cond = .ge, + } }, + }); + + // ldrb tmp, [src, count] + _ = try self.addInst(.{ + .tag = .ldrb_register, + .data = .{ .load_store_register_register = .{ + .rt = tmp, + .rn = src, + .offset = Instruction.LoadStoreOffset.reg(count).register, + } }, + }); + + // strb tmp, [dest, count] + _ = try self.addInst(.{ + .tag = .strb_register, + .data = .{ .load_store_register_register = .{ + .rt = tmp, + .rn = dst, + .offset = Instruction.LoadStoreOffset.reg(count).register, + } }, + }); + + // add count, count, #1 + _ = try self.addInst(.{ + .tag = .add_immediate, + .data = .{ .rr_imm12_sh = .{ + .rd = count, + .rn = count, + .imm12 = 1, + } }, + }); + + // b loop + _ = try self.addInst(.{ + .tag = .b, + .data = .{ .inst = @intCast(u32, self.mir_instructions.len - 5) }, + }); + + // end: +} + fn airLoad(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const elem_ty = self.air.typeOfIndex(inst); diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index 5546b32652..cd370c66ed 100644 --- a/src/arch/aarch64/Mir.zig +++ b/src/arch/aarch64/Mir.zig @@ -224,7 +224,7 @@ pub const Inst = struct { }, /// A registers and a stack offset /// - /// Used by e.g. str_register + /// Used by e.g. str_stack load_store_stack: struct { rt: Register, offset: u32, From 82f91adbb4ef77e8a2b05da923bb1d0c9c3a8262 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 5 Feb 2022 18:45:35 +0100 Subject: [PATCH 0188/2031] stage2 AArch64: Add madd, msub, mul, mneg instructions --- src/arch/aarch64/bits.zig | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/arch/aarch64/bits.zig b/src/arch/aarch64/bits.zig index 10eb919cb9..540a055c8e 100644 --- a/src/arch/aarch64/bits.zig +++ b/src/arch/aarch64/bits.zig @@ -332,6 +332,17 @@ pub const Instruction = union(enum) { op: u1, sf: u1, }, + data_processing_3_source: packed struct { + rd: u5, + rn: u5, + ra: u5, + o0: u1, + rm: u5, + op31: u3, + fixed: u5 = 0b11011, + op54: u2, + sf: u1, + }, pub const Shift = struct { shift: Type = .lsl, @@ -470,6 +481,7 @@ pub const Instruction = union(enum) { .conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25), .compare_and_branch => |v| @as(u32, v.rt) | (@as(u32, v.imm19) << 5) | (@as(u32, v.op) << 24) | (@as(u32, v.fixed) << 25) | (@as(u32, v.sf) << 31), .conditional_select => |v| @as(u32, v.rd) | @as(u32, v.rn) << 5 | @as(u32, v.op2) << 10 | @as(u32, v.cond) << 12 | @as(u32, v.rm) << 16 | @as(u32, v.fixed) << 21 | @as(u32, v.s) << 29 | @as(u32, v.op) << 30 | @as(u32, v.sf) << 31, + .data_processing_3_source => |v| @bitCast(u32, v), }; } @@ -967,6 +979,33 @@ pub const Instruction = union(enum) { }; } + fn dataProcessing3Source( + op54: u2, + op31: u3, + o0: u1, + rd: Register, + rn: Register, + rm: Register, + ra: Register, + ) Instruction { + return Instruction{ + .data_processing_3_source = .{ + .rd = rd.id(), + .rn = rn.id(), + .ra = ra.id(), + .o0 = o0, + .rm = rm.id(), + .op31 = op31, + .op54 = op54, + .sf = switch (rd.size()) { + 32 => 0b0, + 64 => 0b1, + else => unreachable, // unexpected register size + }, + }, + }; + } + // Helper functions for assembly syntax functions // Move wide (immediate) @@ -1245,6 +1284,24 @@ pub const Instruction = union(enum) { pub fn csneg(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction { return conditionalSelect(0b01, 0b1, 0b0, rd, rn, rm, cond); } + + // Data processing (3 source) + + pub fn madd(rd: Register, rn: Register, rm: Register, ra: Register) Instruction { + return dataProcessing3Source(0b00, 0b000, 0b0, rd, rn, rm, ra); + } + + pub fn msub(rd: Register, rn: Register, rm: Register, ra: Register) Instruction { + return dataProcessing3Source(0b00, 0b000, 0b1, rd, rn, rm, ra); + } + + pub fn mul(rd: Register, rn: Register, rm: Register) Instruction { + return madd(rd, rn, rm, .xzr); + } + + pub fn mneg(rd: Register, rn: Register, rm: Register) Instruction { + return msub(rd, rn, rm, .xzr); + } }; test { @@ -1414,6 +1471,10 @@ test "serialize instructions" { .inst = Instruction.csinc(.x1, .x2, .x4, .eq), .expected = 0b1_0_0_11010100_00100_0000_0_1_00010_00001, }, + .{ // mul x1, x4, x9 + .inst = Instruction.mul(.x1, .x4, .x9), + .expected = 0b1_00_11011_000_01001_0_11111_00100_00001, + }, }; for (testcases) |case| { From 8204ad193788f55b7fb5c5a2dd9a1902d964ed70 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 5 Feb 2022 20:51:11 +0100 Subject: [PATCH 0189/2031] stage2 AArch64: implement slice_len and slice_elem_val --- src/arch/aarch64/CodeGen.zig | 191 ++++++++++++++++++++++++++++++++++- src/arch/aarch64/Emit.zig | 33 ++++-- src/arch/aarch64/Mir.zig | 14 ++- 3 files changed, 225 insertions(+), 13 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 39eaa77ad5..2c6f2b33b7 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -1164,7 +1164,20 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { fn airSliceLen(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 return self.fail("TODO implement slice_len for {}", .{self.target.cpu.arch}); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const mcv = try self.resolveInst(ty_op.operand); + switch (mcv) { + .dead, .unreach => unreachable, + .register => unreachable, // a slice doesn't fit in one register + .stack_offset => |off| { + break :result MCValue{ .stack_offset = off + 8 }; + }, + .memory => |addr| { + break :result MCValue{ .memory = addr + 8 }; + }, + else => return self.fail("TODO implement slice_len for {}", .{mcv}), + } + }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -1183,10 +1196,114 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { fn airSliceElemVal(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 slice_elem_val for {}", .{self.target.cpu.arch}); + + if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + const result: MCValue = result: { + const slice_mcv = try self.resolveInst(bin_op.lhs); + + // TODO optimize for the case where the index is a constant, + // i.e. index_mcv == .immediate + const index_mcv = try self.resolveInst(bin_op.rhs); + const index_is_register = index_mcv == .register; + + const slice_ty = self.air.typeOf(bin_op.lhs); + const elem_ty = slice_ty.childType(); + const elem_size = elem_ty.abiSize(self.target.*); + + var buf: Type.SlicePtrFieldTypeBuffer = undefined; + const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); + + if (index_is_register) self.register_manager.freezeRegs(&.{index_mcv.register}); + defer if (index_is_register) self.register_manager.unfreezeRegs(&.{index_mcv.register}); + + const base_mcv: MCValue = switch (slice_mcv) { + .stack_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_offset = off + 8 }) }, + else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}), + }; + self.register_manager.freezeRegs(&.{base_mcv.register}); + + // TODO implement optimized ldr for airSliceElemVal + const dst_mcv = try self.allocRegOrMem(inst, true); + + const offset_mcv = try self.genMulConstant(bin_op.rhs, @intCast(u32, elem_size)); + assert(offset_mcv == .register); // result of multiplication should always be register + self.register_manager.freezeRegs(&.{offset_mcv.register}); + + const addr_reg = try self.register_manager.allocReg(null); + self.register_manager.freezeRegs(&.{addr_reg}); + defer self.register_manager.unfreezeRegs(&.{addr_reg}); + + _ = try self.addInst(.{ + .tag = .add_shifted_register, + .data = .{ .rrr_imm6_shift = .{ + .rd = addr_reg, + .rn = base_mcv.register, + .rm = offset_mcv.register, + .imm6 = 0, + .shift = .lsl, + } }, + }); + + // At this point in time, neither the base register + // nor the offset register contains any valuable data + // anymore. + self.register_manager.unfreezeRegs(&.{ base_mcv.register, offset_mcv.register }); + + try self.load(dst_mcv, .{ .register = addr_reg }, slice_ptr_field_type); + + break :result dst_mcv; + }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn genMulConstant(self: *Self, op: Air.Inst.Ref, imm: u32) !MCValue { + const lhs = try self.resolveInst(op); + const rhs = MCValue{ .immediate = imm }; + + const lhs_is_register = lhs == .register; + + if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); + defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register}); + + // Destination must be a register + // LHS must be a register + // RHS must be a register + var dst_mcv: MCValue = undefined; + var lhs_mcv: MCValue = lhs; + var rhs_mcv: MCValue = rhs; + + // Allocate registers for operands and/or destination + // Allocate 1 or 2 registers + if (lhs_is_register) { + // Move RHS to register + dst_mcv = MCValue{ .register = try self.register_manager.allocReg(null) }; + rhs_mcv = dst_mcv; + } else { + // Move LHS and RHS to register + const regs = try self.register_manager.allocRegs(2, .{ null, null }); + lhs_mcv = MCValue{ .register = regs[0] }; + rhs_mcv = MCValue{ .register = regs[1] }; + dst_mcv = lhs_mcv; + } + + // Move the operands to the newly allocated registers + if (!lhs_is_register) { + try self.genSetReg(self.air.typeOf(op), lhs_mcv.register, lhs); + } + try self.genSetReg(Type.initTag(.usize), rhs_mcv.register, rhs); + + _ = try self.addInst(.{ + .tag = .mul, + .data = .{ .rrr = .{ + .rd = dst_mcv.register, + .rn = lhs_mcv.register, + .rm = rhs_mcv.register, + } }, + }); + + return dst_mcv; +} + 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; @@ -1310,6 +1427,16 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .undef => unreachable, .compare_flags_signed, .compare_flags_unsigned => unreachable, .embedded_in_code => unreachable, + .register => |dst_reg| { + _ = try self.addInst(.{ + .tag = .ldr_immediate, + .data = .{ .load_store_register_immediate = .{ + .rt = dst_reg, + .rn = addr_reg, + .offset = Instruction.LoadStoreOffset.none.immediate, + } }, + }); + }, .stack_offset => |off| { if (elem_ty.abiSize(self.target.*) <= 8) { const tmp_reg = try self.register_manager.allocReg(null); @@ -2590,8 +2717,61 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro if (stack_offset == off) return; // Copy stack variable to itself; nothing to do. - const reg = try self.copyToTmpRegister(ty, mcv); - return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); + const ptr_bits = self.target.cpu.arch.ptrBitWidth(); + const ptr_bytes: u64 = @divExact(ptr_bits, 8); + if (ty.abiSize(self.target.*) <= ptr_bytes) { + const reg = try self.copyToTmpRegister(ty, mcv); + return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); + } else { + // TODO optimize the register allocation + const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }); + self.register_manager.freezeRegs(®s); + defer self.register_manager.unfreezeRegs(®s); + + const src_reg = regs[0]; + const dst_reg = regs[1]; + const len_reg = regs[2]; + const count_reg = regs[3]; + const tmp_reg = regs[4]; + + // sub src_reg, fp, #off + const adj_src_offset = off + @intCast(u32, ty.abiSize(self.target.*)); + const src_offset = math.cast(u12, adj_src_offset) catch return self.fail("TODO load: larger stack offsets", .{}); + _ = try self.addInst(.{ + .tag = .sub_immediate, + .data = .{ .rr_imm12_sh = .{ + .rd = src_reg, + .rn = .x29, + .imm12 = src_offset, + } }, + }); + + // sub dst_reg, fp, #stack_offset + const adj_dst_off = stack_offset + @intCast(u32, ty.abiSize(self.target.*)); + const dst_offset = math.cast(u12, adj_dst_off) catch return self.fail("TODO load: larger stack offsets", .{}); + _ = try self.addInst(.{ + .tag = .sub_immediate, + .data = .{ .rr_imm12_sh = .{ + .rd = dst_reg, + .rn = .x29, + .imm12 = dst_offset, + } }, + }); + + // mov len, #elem_size + const elem_size = @intCast(u32, ty.abiSize(self.target.*)); + const len_imm = math.cast(u16, elem_size) catch return self.fail("TODO load: larger stack offsets", .{}); + _ = try self.addInst(.{ + .tag = .movk, + .data = .{ .r_imm16_sh = .{ + .rd = len_reg, + .imm16 = len_imm, + } }, + }); + + // memcpy(src, dst, len) + try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); + } }, } } @@ -2711,7 +2891,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void } }, }); }, - else => return self.fail("TODO implement genSetReg other types abi_size={}", .{abi_size}), + 3 => return self.fail("TODO implement genSetReg types size 3", .{}), + else => unreachable, } }, else => return self.fail("TODO implement genSetReg for aarch64 {}", .{mcv}), diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 5b2610f508..140d0664b5 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -91,6 +91,7 @@ pub fn emitMir( .call_extern => try emit.mirCallExtern(inst), + .add_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), .cmp_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), .cset => try emit.mirConditionalSelect(inst), @@ -132,6 +133,8 @@ pub fn emitMir( .movk => try emit.mirMoveWideImmediate(inst), .movz => try emit.mirMoveWideImmediate(inst), + .mul => try emit.mirDataProcessing3Source(inst), + .nop => try emit.mirNop(), .push_regs => try emit.mirPushPopRegs(inst), @@ -201,6 +204,12 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize { return 5 * 4; } }, + .pop_regs, .push_regs => { + const reg_list = emit.mir.instructions.items(.data)[inst].reg_list; + const number_of_regs = @popCount(u32, reg_list); + const number_of_insts = std.math.divCeil(u6, number_of_regs, 2) catch unreachable; + return number_of_insts * 4; + }, .call_extern => return 4, .dbg_line, .dbg_epilogue_begin, @@ -565,15 +574,15 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void { fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; const rrr_imm6_shift = emit.mir.instructions.items(.data)[inst].rrr_imm6_shift; + const rd = rrr_imm6_shift.rd; + const rn = rrr_imm6_shift.rn; + const rm = rrr_imm6_shift.rm; + const shift = rrr_imm6_shift.shift; + const imm6 = rrr_imm6_shift.imm6; switch (tag) { - .cmp_shifted_register => try emit.writeInstruction(Instruction.subsShiftedRegister( - rrr_imm6_shift.rd, - rrr_imm6_shift.rn, - rrr_imm6_shift.rm, - rrr_imm6_shift.shift, - rrr_imm6_shift.imm6, - )), + .cmp_shifted_register => try emit.writeInstruction(Instruction.subsShiftedRegister(rd, rn, rm, shift, imm6)), + .add_shifted_register => try emit.writeInstruction(Instruction.addShiftedRegister(rd, rn, rm, shift, imm6)), else => unreachable, } } @@ -802,6 +811,16 @@ fn mirMoveWideImmediate(emit: *Emit, inst: Mir.Inst.Index) !void { } } +fn mirDataProcessing3Source(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const rrr = emit.mir.instructions.items(.data)[inst].rrr; + + switch (tag) { + .mul => try emit.writeInstruction(Instruction.mul(rrr.rd, rrr.rn, rrr.rm)), + else => unreachable, + } +} + fn mirNop(emit: *Emit) !void { try emit.writeInstruction(Instruction.nop()); } diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index cd370c66ed..4f653ff072 100644 --- a/src/arch/aarch64/Mir.zig +++ b/src/arch/aarch64/Mir.zig @@ -26,6 +26,8 @@ pub const Inst = struct { pub const Tag = enum(u16) { /// Add (immediate) add_immediate, + /// Add (shifted register) + add_shifted_register, /// Branch conditionally b_cond, /// Branch @@ -82,6 +84,8 @@ pub const Inst = struct { movk, /// Move wide with zero movz, + /// Multiply + mul, /// No Operation nop, /// Pseudo-instruction: Pop multiple registers @@ -187,6 +191,14 @@ pub const Inst = struct { imm12: u12, sh: u1 = 0, }, + /// Two registers + /// + /// Used by e.g. mul + rrr: struct { + rd: Register, + rn: Register, + rm: Register, + }, /// Three registers and a shift (shift type and 6-bit amount) /// /// Used by e.g. cmp_shifted_register @@ -208,7 +220,7 @@ pub const Inst = struct { }, /// Two registers and a LoadStoreOffsetImmediate /// - /// Used by e.g. str_register + /// Used by e.g. str_immediate load_store_register_immediate: struct { rt: Register, rn: Register, From f598d2ae056e72bda1efb3bc7d77e8183e95e191 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 5 Feb 2022 21:22:09 +0100 Subject: [PATCH 0190/2031] stage2 AArch64: implement unwrap_errunion_err and struct_field_ptr --- src/arch/aarch64/CodeGen.zig | 78 +++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 18 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 2c6f2b33b7..bd6b875550 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -1098,7 +1098,14 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { fn airUnwrapErrErr(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 return self.fail("TODO implement unwrap error union error for {}", .{self.target.cpu.arch}); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const error_union_ty = self.air.typeOf(ty_op.operand); + const payload_ty = error_union_ty.errorUnionPayload(); + const mcv = try self.resolveInst(ty_op.operand); + if (!payload_ty.hasRuntimeBits()) break :result mcv; + + return self.fail("TODO implement unwrap error union error for non-empty payloads", .{}); + }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -1644,20 +1651,31 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void { fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; - return self.structFieldPtr(extra.struct_operand, ty_pl.ty, extra.field_index); + const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index); + return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); } fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - return self.structFieldPtr(ty_op.operand, ty_op.ty, index); + const result = try self.structFieldPtr(inst, ty_op.operand, index); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } -fn structFieldPtr(self: *Self, operand: Air.Inst.Ref, ty: Air.Inst.Ref, index: u32) !void { - _ = self; - _ = operand; - _ = ty; - _ = index; - return self.fail("TODO implement codegen struct_field_ptr", .{}); - //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); + +fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue { + return if (self.liveness.isUnused(inst)) .dead else result: { + const mcv = try self.resolveInst(operand); + const struct_ty = self.air.typeOf(operand).childType(); + const struct_size = @intCast(u32, struct_ty.abiSize(self.target.*)); + const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); + const struct_field_ty = struct_ty.structFieldType(index); + const struct_field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*)); + switch (mcv) { + .ptr_stack_offset => |off| { + break :result MCValue{ .ptr_stack_offset = off + struct_size - struct_field_offset - struct_field_size }; + }, + else => return self.fail("TODO implement codegen struct_field_ptr for {}", .{mcv}), + } + }; } fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { @@ -1754,10 +1772,16 @@ fn airFence(self: *Self) !void { fn airCall(self: *Self, inst: Air.Inst.Index) !void { const pl_op = self.air.instructions.items(.data)[inst].pl_op; - const fn_ty = self.air.typeOf(pl_op.operand); const callee = pl_op.operand; const extra = self.air.extraData(Air.Call, pl_op.payload); const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); + const ty = self.air.typeOf(callee); + + const fn_ty = switch (ty.zigTypeTag()) { + .Fn => ty, + .Pointer => ty.childType(), + else => unreachable, + }; var info = try self.resolveCallingConventionValues(fn_ty); defer info.deinit(self); @@ -1821,7 +1845,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement calling bitcasted functions", .{}); } } else { - return self.fail("TODO implement calling runtime known function pointer", .{}); + assert(ty.zigTypeTag() == .Pointer); + const mcv = try self.resolveInst(callee); + try self.genSetReg(Type.initTag(.usize), .x30, mcv); + + _ = try self.addInst(.{ + .tag = .blr, + .data = .{ .reg = .x30 }, + }); } } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { for (info.args) |mc_arg, arg_i| { @@ -2008,12 +2039,23 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { 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)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + const ty = self.air.typeOf(bin_op.lhs); - assert(ty.eql(self.air.typeOf(bin_op.rhs))); - if (ty.zigTypeTag() == .ErrorSet) - return self.fail("TODO implement cmp for errors", .{}); + + if (ty.abiSize(self.target.*) > 8) { + return self.fail("TODO cmp for types with size > 8", .{}); + } + + const signedness: std.builtin.Signedness = blk: { + // by default we tell the operand type is unsigned (i.e. bools and enum values) + if (ty.zigTypeTag() != .Int) break :blk .unsigned; + + // incase of an actual integer, we emit the correct signedness + break :blk ty.intInfo(self.target.*).signedness; + }; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); @@ -2089,9 +2131,9 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { else => unreachable, } - break :result switch (ty.isSignedInt()) { - true => MCValue{ .compare_flags_signed = op }, - false => MCValue{ .compare_flags_unsigned = op }, + break :result switch (signedness) { + .signed => MCValue{ .compare_flags_signed = op }, + .unsigned => MCValue{ .compare_flags_unsigned = op }, }; }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); From f47245865eea35fa0b08cb2a87e3620fa904dd88 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Tue, 8 Feb 2022 19:57:01 +0100 Subject: [PATCH 0191/2031] stage2 AArch64: minor refactors in Mir + Emit --- src/arch/aarch64/CodeGen.zig | 79 ++++++++++++++---------------------- src/arch/aarch64/Emit.zig | 73 +++++++++++++++------------------ src/arch/aarch64/Mir.zig | 24 +++++++---- 3 files changed, 78 insertions(+), 98 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index bd6b875550..4e14760286 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -2121,8 +2121,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { .immediate => |imm| { _ = try self.addInst(.{ .tag = .cmp_immediate, - .data = .{ .rr_imm12_sh = .{ - .rd = .xzr, + .data = .{ .r_imm12_sh = .{ .rn = lhs_mcv.register, .imm12 = @intCast(u12, imm), } }, @@ -2334,8 +2333,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { _ = try self.addInst(.{ .tag = .cmp_immediate, - .data = .{ .rr_imm12_sh = .{ - .rd = .xzr, + .data = .{ .r_imm12_sh = .{ .rn = reg_mcv.register, .imm12 = 0, } }, @@ -2559,7 +2557,16 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { const operand_mcv = try self.resolveInst(operand); const block_mcv = block_data.mcv; if (block_mcv == .none) { - block_data.mcv = operand_mcv; + block_data.mcv = switch (operand_mcv) { + .none, .dead, .unreach => unreachable, + .register, .stack_offset, .memory => operand_mcv, + .immediate => blk: { + const new_mcv = try self.allocRegOrMem(block, true); + try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); + break :blk new_mcv; + }, + else => return self.fail("TODO implement block_data.mcv = operand_mcv for {}", .{operand_mcv}), + }; } else { try self.setRegOrMem(self.air.typeOfIndex(block), block_mcv, operand_mcv); } @@ -2845,10 +2852,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void _ = try self.addInst(.{ .tag = .cset, - .data = .{ .rrr_cond = .{ + .data = .{ .r_cond = .{ .rd = reg, - .rn = .xzr, - .rm = .xzr, .cond = condition, } }, }); @@ -2933,7 +2938,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void } }, }); }, - 3 => return self.fail("TODO implement genSetReg types size 3", .{}), + 3, 5, 6, 7 => return self.fail("TODO implement genSetReg types size {}", .{abi_size}), else => unreachable, } }, @@ -3114,27 +3119,6 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { } } -/// If the MCValue is an immediate, and it does not fit within this type, -/// we put it in a register. -/// A potential opportunity for future optimization here would be keeping track -/// of the fact that the instruction is available both as an immediate -/// and as a register. -fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCValue { - const mcv = try self.resolveInst(operand); - const ti = @typeInfo(T).Int; - switch (mcv) { - .immediate => |imm| { - // This immediate is unsigned. - const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed)); - if (imm >= math.maxInt(U)) { - return MCValue{ .register = try self.copyToTmpRegister(Type.initTag(.usize), mcv) }; - } - }, - else => {}, - } - return mcv; -} - fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue { const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); @@ -3248,19 +3232,11 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { } }, .ErrorSet => { - switch (typed_value.val.tag()) { - .@"error" => { - const err_name = typed_value.val.castTag(.@"error").?.data.name; - const module = self.bin_file.options.module.?; - const global_error_set = module.global_error_set; - const error_index = global_error_set.get(err_name).?; - return MCValue{ .immediate = error_index }; - }, - else => { - // In this case we are rendering an error union which has a 0 bits payload. - return MCValue{ .immediate = 0 }; - }, - } + const err_name = typed_value.val.castTag(.@"error").?.data.name; + const module = self.bin_file.options.module.?; + const global_error_set = module.global_error_set; + const error_index = global_error_set.get(err_name).?; + return MCValue{ .immediate = error_index }; }, .ErrorUnion => { const error_type = typed_value.ty.errorUnionSet(); @@ -3425,13 +3401,18 @@ fn parseRegName(name: []const u8) ?Register { } fn registerAlias(reg: Register, size_bytes: u32) Register { - _ = size_bytes; - - return reg; + if (size_bytes == 0) { + unreachable; // should be comptime known + } else if (size_bytes <= 4) { + return reg.to32(); + } else if (size_bytes <= 8) { + return reg.to64(); + } else { + unreachable; // TODO handle floating-point registers + } } -/// For most architectures this does nothing. For x86_64 it resolves any aliased registers -/// to the 64-bit wide ones. +/// Resolves any aliased registers to the 64-bit wide ones. fn toCanonicalReg(reg: Register) Register { - return reg; + return reg.to64(); } diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 140d0664b5..786fa4b3f3 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -423,27 +423,30 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { fn mirAddSubtractImmediate(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; - const rr_imm12_sh = emit.mir.instructions.items(.data)[inst].rr_imm12_sh; - switch (tag) { - .add_immediate => try emit.writeInstruction(Instruction.add( - rr_imm12_sh.rd, - rr_imm12_sh.rn, - rr_imm12_sh.imm12, - rr_imm12_sh.sh == 1, - )), - .cmp_immediate => try emit.writeInstruction(Instruction.subs( - rr_imm12_sh.rd, - rr_imm12_sh.rn, - rr_imm12_sh.imm12, - rr_imm12_sh.sh == 1, - )), - .sub_immediate => try emit.writeInstruction(Instruction.sub( - rr_imm12_sh.rd, - rr_imm12_sh.rn, - rr_imm12_sh.imm12, - rr_imm12_sh.sh == 1, - )), + .add_immediate, + .sub_immediate, + => { + const rr_imm12_sh = emit.mir.instructions.items(.data)[inst].rr_imm12_sh; + const rd = rr_imm12_sh.rd; + const rn = rr_imm12_sh.rn; + const imm12 = rr_imm12_sh.imm12; + const sh = rr_imm12_sh.sh == 1; + + switch (tag) { + .add_immediate => try emit.writeInstruction(Instruction.add(rd, rn, imm12, sh)), + .sub_immediate => try emit.writeInstruction(Instruction.sub(rd, rn, imm12, sh)), + else => unreachable, + } + }, + .cmp_immediate => { + const r_imm12_sh = emit.mir.instructions.items(.data)[inst].r_imm12_sh; + const rn = r_imm12_sh.rn; + const imm12 = r_imm12_sh.imm12; + const sh = r_imm12_sh.sh == 1; + + try emit.writeInstruction(Instruction.subs(.xzr, rn, imm12, sh)); + }, else => unreachable, } } @@ -589,15 +592,11 @@ fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void { fn mirConditionalSelect(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; - const rrr_cond = emit.mir.instructions.items(.data)[inst].rrr_cond; - switch (tag) { - .cset => try emit.writeInstruction(Instruction.csinc( - rrr_cond.rd, - rrr_cond.rn, - rrr_cond.rm, - rrr_cond.cond, - )), + .cset => { + const r_cond = emit.mir.instructions.items(.data)[inst].r_cond; + try emit.writeInstruction(Instruction.csinc(r_cond.rd, .xzr, .xzr, r_cond.cond)); + }, else => unreachable, } } @@ -662,20 +661,14 @@ fn mirLoadMemory(emit: *Emit, inst: Mir.Inst.Index) !void { fn mirLoadStoreRegisterPair(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; const load_store_register_pair = emit.mir.instructions.items(.data)[inst].load_store_register_pair; + const rt = load_store_register_pair.rt; + const rt2 = load_store_register_pair.rt2; + const rn = load_store_register_pair.rn; + const offset = load_store_register_pair.offset; switch (tag) { - .stp => try emit.writeInstruction(Instruction.stp( - load_store_register_pair.rt, - load_store_register_pair.rt2, - load_store_register_pair.rn, - load_store_register_pair.offset, - )), - .ldp => try emit.writeInstruction(Instruction.ldp( - load_store_register_pair.rt, - load_store_register_pair.rt2, - load_store_register_pair.rn, - load_store_register_pair.offset, - )), + .stp => try emit.writeInstruction(Instruction.stp(rt, rt2, rn, offset)), + .ldp => try emit.writeInstruction(Instruction.ldp(rt, rt2, rn, offset)), else => unreachable, } } diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index 4f653ff072..6bb681acb8 100644 --- a/src/arch/aarch64/Mir.zig +++ b/src/arch/aarch64/Mir.zig @@ -175,6 +175,13 @@ pub const Inst = struct { imm16: u16, hw: u2 = 0, }, + /// A register and a condition + /// + /// Used by e.g. cset + r_cond: struct { + rd: Register, + cond: bits.Instruction.Condition, + }, /// Two registers /// /// Used by e.g. mov_register @@ -182,6 +189,14 @@ pub const Inst = struct { rd: Register, rn: Register, }, + /// A register, an unsigned 12-bit immediate, and an optional shift + /// + /// Used by e.g. cmp_immediate + r_imm12_sh: struct { + rn: Register, + imm12: u12, + sh: u1 = 0, + }, /// Two registers, an unsigned 12-bit immediate, and an optional shift /// /// Used by e.g. sub_immediate @@ -209,15 +224,6 @@ pub const Inst = struct { imm6: u6, shift: bits.Instruction.AddSubtractShiftedRegisterShift, }, - /// Three registers and a condition - /// - /// Used by e.g. cset - rrr_cond: struct { - rd: Register, - rn: Register, - rm: Register, - cond: bits.Instruction.Condition, - }, /// Two registers and a LoadStoreOffsetImmediate /// /// Used by e.g. str_immediate From 8bfc4b2f9cf4798fe7731288c652145803ef1998 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Tue, 8 Feb 2022 20:15:09 +0100 Subject: [PATCH 0192/2031] stage2 AArch64: extract store out of airStore for recursive calls --- src/arch/aarch64/CodeGen.zig | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 4e14760286..8e5e620658 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -1611,11 +1611,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } -fn airStore(self: *Self, inst: Air.Inst.Index) !void { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - const ptr = try self.resolveInst(bin_op.lhs); - const value = try self.resolveInst(bin_op.rhs); - const elem_ty = self.air.typeOf(bin_op.rhs); +fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { switch (ptr) { .none => unreachable, .undef => unreachable, @@ -1624,13 +1620,13 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void { .compare_flags_unsigned => unreachable, .compare_flags_signed => unreachable, .immediate => |imm| { - try self.setRegOrMem(elem_ty, .{ .memory = imm }, value); + try self.setRegOrMem(value_ty, .{ .memory = imm }, value); }, .ptr_stack_offset => |off| { - try self.genSetStack(elem_ty, off, value); + try self.genSetStack(value_ty, off, value); }, .ptr_embedded_in_code => |off| { - try self.setRegOrMem(elem_ty, .{ .embedded_in_code = off }, value); + try self.setRegOrMem(value_ty, .{ .embedded_in_code = off }, value); }, .embedded_in_code => { return self.fail("TODO implement storing to MCValue.embedded_in_code", .{}); @@ -1638,13 +1634,24 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void { .register => { return self.fail("TODO implement storing to MCValue.register", .{}); }, - .memory => { - return self.fail("TODO implement storing to MCValue.memory", .{}); - }, - .stack_offset => { - return self.fail("TODO implement storing to MCValue.stack_offset", .{}); + .memory, + .stack_offset, + => { + const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr); + try self.store(.{ .register = addr_reg }, value, ptr_ty, value_ty); }, } +} + +fn airStore(self: *Self, inst: Air.Inst.Index) !void { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const ptr = try self.resolveInst(bin_op.lhs); + const value = try self.resolveInst(bin_op.rhs); + const ptr_ty = self.air.typeOf(bin_op.lhs); + const value_ty = self.air.typeOf(bin_op.rhs); + + try self.store(ptr, value, ptr_ty, value_ty); + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); } From edb2a75982a011dc883678ca57efa8c3f6be5466 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Wed, 9 Feb 2022 23:19:28 +0100 Subject: [PATCH 0193/2031] stage2 AArch64: Implement binOp for add, sub --- src/arch/aarch64/CodeGen.zig | 254 +++++++++++++++++++++++++++++++++-- src/arch/aarch64/Emit.zig | 4 +- src/arch/aarch64/Mir.zig | 2 + 3 files changed, 249 insertions(+), 11 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 8e5e620658..7c207039b9 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -511,10 +511,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off - .add, .ptr_add => try self.airAdd(inst), + .add, .ptr_add => try self.airBinOp(inst), .addwrap => try self.airAddWrap(inst), .add_sat => try self.airAddSat(inst), - .sub, .ptr_sub => try self.airSub(inst), + .sub, .ptr_sub => try self.airBinOp(inst), .subwrap => try self.airSubWrap(inst), .sub_sat => try self.airSubSat(inst), .mul => try self.airMul(inst), @@ -950,9 +950,249 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airAdd(self: *Self, inst: Air.Inst.Index) !void { +/// Don't call this function directly. Use binOp instead. +/// +/// Calling this function signals an intention to generate a Mir +/// instruction of the form +/// +/// op dest, lhs, rhs +/// +/// Asserts that generating an instruction of that form is possible. +fn binOpRegister( + self: *Self, + tag: Air.Inst.Tag, + maybe_inst: ?Air.Inst.Index, + lhs: MCValue, + rhs: MCValue, + lhs_ty: Type, + rhs_ty: Type, +) !MCValue { + const lhs_is_register = lhs == .register; + const rhs_is_register = rhs == .register; + + if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); + if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); + + const lhs_reg = if (lhs_is_register) lhs.register else blk: { + const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + break :inst Air.refToIndex(bin_op.lhs).?; + } else null; + const reg = try self.register_manager.allocReg(track_inst); + self.register_manager.freezeRegs(&.{reg}); + break :blk reg; + }; + defer self.register_manager.unfreezeRegs(&.{lhs_reg}); + + const rhs_reg = if (rhs_is_register) rhs.register else blk: { + const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + break :inst Air.refToIndex(bin_op.rhs).?; + } else null; + const reg = try self.register_manager.allocReg(track_inst); + self.register_manager.freezeRegs(&.{reg}); + break :blk reg; + }; + defer self.register_manager.unfreezeRegs(&.{rhs_reg}); + + const dest_reg = if (maybe_inst) |inst| blk: { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + + if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { + break :blk lhs_reg; + } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { + break :blk rhs_reg; + } else { + break :blk try self.register_manager.allocReg(inst); + } + } else try self.register_manager.allocReg(null); + + if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); + if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); + + const mir_tag: Mir.Inst.Tag = switch (tag) { + .add => .add_shifted_register, + .sub => .sub_shifted_register, + else => unreachable, + }; + const mir_data: Mir.Inst.Data = switch (tag) { + .add, + .sub, + => .{ .rrr_imm6_shift = .{ + .rd = dest_reg, + .rn = lhs_reg, + .rm = rhs_reg, + .imm6 = 0, + .shift = .lsl, + } }, + else => unreachable, + }; + + _ = try self.addInst(.{ + .tag = mir_tag, + .data = mir_data, + }); + + return MCValue{ .register = dest_reg }; +} + +/// Don't call this function directly. Use binOp instead. +/// +/// Calling this function signals an intention to generate a Mir +/// instruction of the form +/// +/// op dest, lhs, #rhs_imm +/// +/// Set lhs_and_rhs_swapped to true iff inst.bin_op.lhs corresponds to +/// rhs and vice versa. This parameter is only used when maybe_inst != +/// null. +/// +/// Asserts that generating an instruction of that form is possible. +fn binOpImmediate( + self: *Self, + tag: Air.Inst.Tag, + maybe_inst: ?Air.Inst.Index, + lhs: MCValue, + rhs: MCValue, + lhs_ty: Type, + lhs_and_rhs_swapped: bool, +) !MCValue { + const lhs_is_register = lhs == .register; + + if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); + + const lhs_reg = if (lhs_is_register) lhs.register else blk: { + const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + break :inst Air.refToIndex( + if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, + ).?; + } else null; + const reg = try self.register_manager.allocReg(track_inst); + self.register_manager.freezeRegs(&.{reg}); + break :blk reg; + }; + defer self.register_manager.unfreezeRegs(&.{lhs_reg}); + + const dest_reg = if (maybe_inst) |inst| blk: { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + + if (lhs_is_register and self.reuseOperand( + inst, + if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, + if (lhs_and_rhs_swapped) 1 else 0, + lhs, + )) { + break :blk lhs_reg; + } else { + break :blk try self.register_manager.allocReg(inst); + } + } else try self.register_manager.allocReg(null); + + if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); + + const mir_tag: Mir.Inst.Tag = switch (tag) { + .add => .add_immediate, + .sub => .sub_immediate, + else => unreachable, + }; + const mir_data: Mir.Inst.Data = switch (tag) { + .add, + .sub, + => .{ .rr_imm12_sh = .{ + .rd = dest_reg, + .rn = lhs_reg, + .imm12 = @intCast(u12, rhs.immediate), + } }, + else => unreachable, + }; + + _ = try self.addInst(.{ + .tag = mir_tag, + .data = mir_data, + }); + + return MCValue{ .register = dest_reg }; +} + +/// For all your binary operation needs, this function will generate +/// the corresponding Mir instruction(s). Returns the location of the +/// result. +/// +/// If the binary operation itself happens to be an Air instruction, +/// pass the corresponding index in the inst parameter. That helps +/// this function do stuff like reusing operands. +/// +/// This function does not do any lowering to Mir itself, but instead +/// looks at the lhs and rhs and determines which kind of lowering +/// would be best suitable and then delegates the lowering to other +/// functions. +fn binOp( + self: *Self, + tag: Air.Inst.Tag, + maybe_inst: ?Air.Inst.Index, + lhs: MCValue, + rhs: MCValue, + lhs_ty: Type, + rhs_ty: Type, +) !MCValue { + switch (tag) { + .add, + .sub, + => { + switch (lhs_ty.zigTypeTag()) { + .Float => return self.fail("TODO binary operations on floats", .{}), + .Vector => return self.fail("TODO binary operations on vectors", .{}), + .Int => { + assert(lhs_ty.eql(rhs_ty)); + const int_info = lhs_ty.intInfo(self.target.*); + if (int_info.bits <= 64) { + // Only say yes if the operation is + // commutative, i.e. we can swap both of the + // operands + const lhs_immediate_ok = switch (tag) { + .add => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), + .sub => false, + else => unreachable, + }; + const rhs_immediate_ok = switch (tag) { + .add, + .sub, + => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), + else => unreachable, + }; + + if (rhs_immediate_ok) { + return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false); + } else if (lhs_immediate_ok) { + // swap lhs and rhs + return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true); + } else { + return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + } + } else { + return self.fail("TODO binary operations on int with bits > 64", .{}); + } + }, + else => unreachable, + } + }, + .ptr_add, + .ptr_sub, + => return self.fail("TODO ptr_add, ptr_sub", .{}), + else => unreachable, + } +} + +fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { + const tag = self.air.instructions.items(.tag)[inst]; 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 add for {}", .{self.target.cpu.arch}); + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -968,12 +1208,6 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airSub(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 sub for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airSubWrap(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 subwrap for {}", .{self.target.cpu.arch}); diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 786fa4b3f3..1b30c78562 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -93,6 +93,7 @@ pub fn emitMir( .add_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), .cmp_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), + .sub_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), .cset => try emit.mirConditionalSelect(inst), @@ -584,8 +585,9 @@ fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void { const imm6 = rrr_imm6_shift.imm6; switch (tag) { - .cmp_shifted_register => try emit.writeInstruction(Instruction.subsShiftedRegister(rd, rn, rm, shift, imm6)), .add_shifted_register => try emit.writeInstruction(Instruction.addShiftedRegister(rd, rn, rm, shift, imm6)), + .cmp_shifted_register => try emit.writeInstruction(Instruction.subsShiftedRegister(rd, rn, rm, shift, imm6)), + .sub_shifted_register => try emit.writeInstruction(Instruction.subShiftedRegister(rd, rn, rm, shift, imm6)), else => unreachable, } } diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index 6bb681acb8..d2263c6e32 100644 --- a/src/arch/aarch64/Mir.zig +++ b/src/arch/aarch64/Mir.zig @@ -116,6 +116,8 @@ pub const Inst = struct { strh_register, /// Subtract (immediate) sub_immediate, + /// Subtract (shifted register) + sub_shifted_register, /// Supervisor Call svc, }; From 3a33f313347f3fa151ba3a90c1c3b14eee3d1d1e Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 12 Feb 2022 14:31:25 +0100 Subject: [PATCH 0194/2031] stage2 AArch64: implement cond_br for other MCValues --- src/arch/aarch64/CodeGen.zig | 32 ++++++++++++++++++------ src/arch/aarch64/Emit.zig | 47 +++++++++++++++++++++++++++++++----- src/arch/aarch64/Mir.zig | 9 +++++++ test/stage2/aarch64.zig | 25 +++++++++++++++++++ 4 files changed, 99 insertions(+), 14 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 7c207039b9..fade038eb0 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -923,12 +923,12 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { }; break :result r; }, - else => {}, + else => { + return self.fail("TODO implement NOT for {}", .{self.target.cpu.arch}); + }, } - - return self.fail("TODO implement NOT for {}", .{self.target.cpu.arch}); }; - _ = result; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } fn airMin(self: *Self, inst: Air.Inst.Index) !void { @@ -1411,7 +1411,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { .dead, .unreach => unreachable, .register => unreachable, // a slice doesn't fit in one register .stack_offset => |off| { - break :result MCValue{ .stack_offset = off + 8 }; + break :result MCValue{ .stack_offset = off }; }, .memory => |addr| { break :result MCValue{ .memory = addr + 8 }; @@ -2425,7 +2425,22 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { }, }, }), - else => return self.fail("TODO implement condbr when condition is {s}", .{@tagName(cond)}), + else => blk: { + const reg = switch (cond) { + .register => |r| r, + else => try self.copyToTmpRegister(Type.bool, cond), + }; + + break :blk try self.addInst(.{ + .tag = .cbz, + .data = .{ + .r_inst = .{ + .rt = reg, + .inst = undefined, // populated later through performReloc + }, + }, + }); + }, }; // Capture the state of register and stack allocation state so that we can revert to it. @@ -2770,8 +2785,9 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { const tag = self.mir_instructions.items(.tag)[inst]; switch (tag) { - .b_cond => self.mir_instructions.items(.data)[inst].inst_cond.inst = @intCast(Air.Inst.Index, self.mir_instructions.len), - .b => self.mir_instructions.items(.data)[inst].inst = @intCast(Air.Inst.Index, self.mir_instructions.len), + .cbz => self.mir_instructions.items(.data)[inst].r_inst.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len), + .b_cond => self.mir_instructions.items(.data)[inst].inst_cond.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len), + .b => self.mir_instructions.items(.data)[inst].inst = @intCast(Mir.Inst.Index, self.mir_instructions.len), else => unreachable, } } diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 1b30c78562..1772f08aa8 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -50,11 +50,13 @@ const InnerError = error{ }; const BranchType = enum { + cbz, b_cond, unconditional_branch_immediate, fn default(tag: Mir.Inst.Tag) BranchType { return switch (tag) { + .cbz => .cbz, .b, .bl => .unconditional_branch_immediate, .b_cond => .b_cond, else => unreachable, @@ -83,6 +85,8 @@ pub fn emitMir( .b => try emit.mirBranch(inst), .bl => try emit.mirBranch(inst), + .cbz => try emit.mirCompareAndBranch(inst), + .blr => try emit.mirUnconditionalBranchRegister(inst), .ret => try emit.mirUnconditionalBranchRegister(inst), @@ -160,15 +164,22 @@ fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType { assert(offset & 0b11 == 0); switch (tag) { + .cbz => { + if (std.math.cast(i19, @shrExact(offset, 2))) |_| { + return BranchType.cbz; + } else |_| { + return emit.fail("TODO support cbz branches larger than +-1 MiB", .{}); + } + }, .b, .bl => { - if (std.math.cast(i26, offset >> 2)) |_| { + if (std.math.cast(i26, @shrExact(offset, 2))) |_| { return BranchType.unconditional_branch_immediate; } else |_| { - return emit.fail("TODO support branches larger than +-128 MiB", .{}); + return emit.fail("TODO support unconditional branches larger than +-128 MiB", .{}); } }, .b_cond => { - if (std.math.cast(i19, offset >> 2)) |_| { + if (std.math.cast(i19, @shrExact(offset, 2))) |_| { return BranchType.b_cond; } else |_| { return emit.fail("TODO support conditional branches larger than +-1 MiB", .{}); @@ -183,8 +194,10 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize { if (isBranch(tag)) { switch (emit.branch_types.get(inst).?) { - .unconditional_branch_immediate => return 4, - .b_cond => return 4, + .cbz, + .unconditional_branch_immediate, + .b_cond, + => return 4, } } @@ -222,7 +235,11 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize { fn isBranch(tag: Mir.Inst.Tag) bool { return switch (tag) { - .b, .bl, .b_cond => true, + .cbz, + .b, + .bl, + .b_cond, + => true, else => false, }; } @@ -231,6 +248,7 @@ fn branchTarget(emit: *Emit, inst: Mir.Inst.Index) Mir.Inst.Index { const tag = emit.mir.instructions.items(.tag)[inst]; switch (tag) { + .cbz => return emit.mir.instructions.items(.data)[inst].r_inst.inst, .b, .bl => return emit.mir.instructions.items(.data)[inst].inst, .b_cond => return emit.mir.instructions.items(.data)[inst].inst_cond.inst, else => unreachable, @@ -494,6 +512,23 @@ fn mirBranch(emit: *Emit, inst: Mir.Inst.Index) !void { } } +fn mirCompareAndBranch(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const r_inst = emit.mir.instructions.items(.data)[inst].r_inst; + + const offset = @intCast(i64, emit.code_offset_mapping.get(r_inst.inst).?) - @intCast(i64, emit.code.items.len); + const branch_type = emit.branch_types.get(inst).?; + log.debug("mirCompareAndBranch: {} offset={}", .{ inst, offset }); + + switch (branch_type) { + .cbz => switch (tag) { + .cbz => try emit.writeInstruction(Instruction.cbz(r_inst.rt, @intCast(i21, offset))), + else => unreachable, + }, + else => unreachable, + } +} + fn mirUnconditionalBranchRegister(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; const reg = emit.mir.instructions.items(.data)[inst].reg; diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index d2263c6e32..65b80549a9 100644 --- a/src/arch/aarch64/Mir.zig +++ b/src/arch/aarch64/Mir.zig @@ -40,6 +40,8 @@ pub const Inst = struct { brk, /// Pseudo-instruction: Call extern call_extern, + /// Compare and Branch on Zero + cbz, /// Compare (immediate) cmp_immediate, /// Compare (shifted register) @@ -184,6 +186,13 @@ pub const Inst = struct { rd: Register, cond: bits.Instruction.Condition, }, + /// A register and another instruction + /// + /// Used by e.g. cbz + r_inst: struct { + rt: Register, + inst: Index, + }, /// Two registers /// /// Used by e.g. mov_register diff --git a/test/stage2/aarch64.zig b/test/stage2/aarch64.zig index 64e6f95a84..580a375aef 100644 --- a/test/stage2/aarch64.zig +++ b/test/stage2/aarch64.zig @@ -102,6 +102,31 @@ pub fn addCases(ctx: *TestContext) !void { , "Hello, World!\n", ); + + case.addCompareOutput( + \\pub fn main() void { + \\ foo(true); + \\} + \\ + \\fn foo(x: bool) void { + \\ if (x) { + \\ print(); + \\ } + \\} + \\ + \\fn print() void { + \\ asm volatile ("svc #0" + \\ : + \\ : [number] "{x8}" (64), + \\ [arg1] "{x0}" (1), + \\ [arg2] "{x1}" (@ptrToInt("Hello, World!\n")), + \\ [arg3] "{x2}" ("Hello, World!\n".len), + \\ : "memory", "cc" + \\ ); + \\} + , + "Hello, World!\n", + ); } // macOS tests From 1c37622659f70115b698b5924472c2268bca63a8 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 12 Feb 2022 18:37:11 +0100 Subject: [PATCH 0195/2031] stage2 AArch64: Implement not for booleans --- src/arch/aarch64/CodeGen.zig | 32 +++++++++- src/arch/aarch64/Emit.zig | 34 ++++++++-- src/arch/aarch64/Mir.zig | 24 +++++++ src/arch/aarch64/bits.zig | 120 ++++++++++++++++++++++------------- 4 files changed, 162 insertions(+), 48 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index fade038eb0..8bd5324ed1 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -894,6 +894,7 @@ fn airNot(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 result: { const operand = try self.resolveInst(ty_op.operand); + const operand_ty = self.air.typeOf(ty_op.operand); switch (operand) { .dead => unreachable, .unreach => unreachable, @@ -924,7 +925,14 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { break :result r; }, else => { - return self.fail("TODO implement NOT for {}", .{self.target.cpu.arch}); + switch (operand_ty.zigTypeTag()) { + .Bool => { + // TODO convert this to mvn + and + const dest = try self.binOp(.xor, null, operand, .{ .immediate = 1 }, operand_ty, Type.bool); + break :result dest; + }, + else => return self.fail("TODO bitwise not", .{}), + } }, } }; @@ -1013,6 +1021,7 @@ fn binOpRegister( const mir_tag: Mir.Inst.Tag = switch (tag) { .add => .add_shifted_register, .sub => .sub_shifted_register, + .xor => .eor_shifted_register, else => unreachable, }; const mir_data: Mir.Inst.Data = switch (tag) { @@ -1025,6 +1034,13 @@ fn binOpRegister( .imm6 = 0, .shift = .lsl, } }, + .xor => .{ .rrr_imm6_logical_shift = .{ + .rd = dest_reg, + .rn = lhs_reg, + .rm = rhs_reg, + .imm6 = 0, + .shift = .lsl, + } }, else => unreachable, }; @@ -1137,6 +1153,7 @@ fn binOp( rhs_ty: Type, ) !MCValue { switch (tag) { + // Arithmetic operations on integers and floats .add, .sub, => { @@ -1177,6 +1194,19 @@ fn binOp( else => unreachable, } }, + // Bitwise operations on integers + .xor => { + switch (lhs_ty.zigTypeTag()) { + .Vector => return self.fail("TODO binary operations on vectors", .{}), + .Int => return self.fail("TODO binary operations on vectors", .{}), + .Bool => { + assert(lhs_ty.eql(rhs_ty)); + // TODO boolean operations with immediates + return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + }, + else => unreachable, + } + }, .ptr_add, .ptr_sub, => return self.fail("TODO ptr_add, ptr_sub", .{}), diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 1772f08aa8..bc37cb56c6 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -106,6 +106,8 @@ pub fn emitMir( .dbg_prologue_end => try emit.mirDebugPrologueEnd(), .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(), + .eor_shifted_register => try emit.mirLogicalShiftedRegister(inst), + .load_memory => try emit.mirLoadMemory(inst), .ldp => try emit.mirLoadStoreRegisterPair(inst), @@ -134,6 +136,7 @@ pub fn emitMir( .mov_register => try emit.mirMoveRegister(inst), .mov_to_from_sp => try emit.mirMoveRegister(inst), + .mvn => try emit.mirMoveRegister(inst), .movk => try emit.mirMoveWideImmediate(inst), .movz => try emit.mirMoveWideImmediate(inst), @@ -638,6 +641,21 @@ fn mirConditionalSelect(emit: *Emit, inst: Mir.Inst.Index) !void { } } +fn mirLogicalShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const rrr_imm6_logical_shift = emit.mir.instructions.items(.data)[inst].rrr_imm6_logical_shift; + const rd = rrr_imm6_logical_shift.rd; + const rn = rrr_imm6_logical_shift.rn; + const rm = rrr_imm6_logical_shift.rm; + const shift = rrr_imm6_logical_shift.shift; + const imm6 = rrr_imm6_logical_shift.imm6; + + switch (tag) { + .eor_shifted_register => try emit.writeInstruction(Instruction.eor(rd, rn, rm, shift, imm6)), + else => unreachable, + } +} + fn mirLoadMemory(emit: *Emit, inst: Mir.Inst.Index) !void { assert(emit.mir.instructions.items(.tag)[inst] == .load_memory); const payload = emit.mir.instructions.items(.data)[inst].payload; @@ -821,11 +839,19 @@ fn mirLoadStoreRegisterRegister(emit: *Emit, inst: Mir.Inst.Index) !void { fn mirMoveRegister(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; - const rr = emit.mir.instructions.items(.data)[inst].rr; - switch (tag) { - .mov_register => try emit.writeInstruction(Instruction.orr(rr.rd, .xzr, rr.rn, Instruction.Shift.none)), - .mov_to_from_sp => try emit.writeInstruction(Instruction.add(rr.rd, rr.rn, 0, false)), + .mov_register => { + const rr = emit.mir.instructions.items(.data)[inst].rr; + try emit.writeInstruction(Instruction.orr(rr.rd, .xzr, rr.rn, .lsl, 0)); + }, + .mov_to_from_sp => { + const rr = emit.mir.instructions.items(.data)[inst].rr; + try emit.writeInstruction(Instruction.add(rr.rd, rr.rn, 0, false)); + }, + .mvn => { + const rr_imm6_shift = emit.mir.instructions.items(.data)[inst].rr_imm6_shift; + try emit.writeInstruction(Instruction.orn(rr_imm6_shift.rd, .xzr, rr_imm6_shift.rm, .lsl, 0)); + }, else => unreachable, } } diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index 65b80549a9..92b0604347 100644 --- a/src/arch/aarch64/Mir.zig +++ b/src/arch/aarch64/Mir.zig @@ -54,6 +54,8 @@ pub const Inst = struct { dbg_epilogue_begin, /// Pseudo-instruction: Update debug line dbg_line, + /// Bitwise Exclusive OR (shifted register) + eor_shifted_register, /// Pseudo-instruction: Load memory /// /// Payload is `LoadMemory` @@ -88,6 +90,8 @@ pub const Inst = struct { movz, /// Multiply mul, + /// Bitwise NOT + mvn, /// No Operation nop, /// Pseudo-instruction: Pop multiple registers @@ -217,6 +221,15 @@ pub const Inst = struct { imm12: u12, sh: u1 = 0, }, + /// Two registers and a shift (shift type and 6-bit amount) + /// + /// Used by e.g. mvn + rr_imm6_shift: struct { + rd: Register, + rm: Register, + imm6: u6, + shift: bits.Instruction.AddSubtractShiftedRegisterShift, + }, /// Two registers /// /// Used by e.g. mul @@ -235,6 +248,17 @@ pub const Inst = struct { imm6: u6, shift: bits.Instruction.AddSubtractShiftedRegisterShift, }, + /// Three registers and a shift (logical instruction version) + /// (shift type and 6-bit amount) + /// + /// Used by e.g. eor_shifted_register + rrr_imm6_logical_shift: struct { + rd: Register, + rn: Register, + rm: Register, + imm6: u6, + shift: bits.Instruction.LogicalShiftedRegisterShift, + }, /// Two registers and a LoadStoreOffsetImmediate /// /// Used by e.g. str_immediate diff --git a/src/arch/aarch64/bits.zig b/src/arch/aarch64/bits.zig index 540a055c8e..a5d56cfcc7 100644 --- a/src/arch/aarch64/bits.zig +++ b/src/arch/aarch64/bits.zig @@ -344,23 +344,6 @@ pub const Instruction = union(enum) { sf: u1, }, - pub const Shift = struct { - shift: Type = .lsl, - amount: u6 = 0, - - pub const Type = enum(u2) { - lsl, - lsr, - asr, - ror, - }; - - pub const none = Shift{ - .shift = .lsl, - .amount = 0, - }; - }; - pub const Condition = enum(u4) { /// Integer: Equal /// Floating point: Equal @@ -819,25 +802,28 @@ pub const Instruction = union(enum) { }; } + pub const LogicalShiftedRegisterShift = enum(u2) { lsl, lsr, asr, ror }; + fn logicalShiftedRegister( opc: u2, n: u1, - shift: Shift, rd: Register, rn: Register, rm: Register, + shift: LogicalShiftedRegisterShift, + amount: u6, ) Instruction { switch (rd.size()) { 32 => { - assert(shift.amount < 32); + assert(amount < 32); return Instruction{ .logical_shifted_register = .{ .rd = rd.id(), .rn = rn.id(), - .imm6 = shift.amount, + .imm6 = amount, .rm = rm.id(), .n = n, - .shift = @enumToInt(shift.shift), + .shift = @enumToInt(shift), .opc = opc, .sf = 0b0, }, @@ -848,10 +834,10 @@ pub const Instruction = union(enum) { .logical_shifted_register = .{ .rd = rd.id(), .rn = rn.id(), - .imm6 = shift.amount, + .imm6 = amount, .rm = rm.id(), .n = n, - .shift = @enumToInt(shift.shift), + .shift = @enumToInt(shift), .opc = opc, .sf = 0b1, }, @@ -1159,36 +1145,84 @@ pub const Instruction = union(enum) { // Logical (shifted register) - pub fn @"and"(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { - return logicalShiftedRegister(0b00, 0b0, shift, rd, rn, rm); + pub fn @"and"( + rd: Register, + rn: Register, + rm: Register, + shift: LogicalShiftedRegisterShift, + amount: u6, + ) Instruction { + return logicalShiftedRegister(0b00, 0b0, rd, rn, rm, shift, amount); } - pub fn bic(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { - return logicalShiftedRegister(0b00, 0b1, shift, rd, rn, rm); + pub fn bic( + rd: Register, + rn: Register, + rm: Register, + shift: LogicalShiftedRegisterShift, + amount: u6, + ) Instruction { + return logicalShiftedRegister(0b00, 0b1, rd, rn, rm, shift, amount); } - pub fn orr(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { - return logicalShiftedRegister(0b01, 0b0, shift, rd, rn, rm); + pub fn orr( + rd: Register, + rn: Register, + rm: Register, + shift: LogicalShiftedRegisterShift, + amount: u6, + ) Instruction { + return logicalShiftedRegister(0b01, 0b0, rd, rn, rm, shift, amount); } - pub fn orn(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { - return logicalShiftedRegister(0b01, 0b1, shift, rd, rn, rm); + pub fn orn( + rd: Register, + rn: Register, + rm: Register, + shift: LogicalShiftedRegisterShift, + amount: u6, + ) Instruction { + return logicalShiftedRegister(0b01, 0b1, rd, rn, rm, shift, amount); } - pub fn eor(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { - return logicalShiftedRegister(0b10, 0b0, shift, rd, rn, rm); + pub fn eor( + rd: Register, + rn: Register, + rm: Register, + shift: LogicalShiftedRegisterShift, + amount: u6, + ) Instruction { + return logicalShiftedRegister(0b10, 0b0, rd, rn, rm, shift, amount); } - pub fn eon(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { - return logicalShiftedRegister(0b10, 0b1, shift, rd, rn, rm); + pub fn eon( + rd: Register, + rn: Register, + rm: Register, + shift: LogicalShiftedRegisterShift, + amount: u6, + ) Instruction { + return logicalShiftedRegister(0b10, 0b1, rd, rn, rm, shift, amount); } - pub fn ands(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { - return logicalShiftedRegister(0b11, 0b0, shift, rd, rn, rm); + pub fn ands( + rd: Register, + rn: Register, + rm: Register, + shift: LogicalShiftedRegisterShift, + amount: u6, + ) Instruction { + return logicalShiftedRegister(0b11, 0b0, rd, rn, rm, shift, amount); } - pub fn bics(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { - return logicalShiftedRegister(0b11, 0b1, shift, rd, rn, rm); + pub fn bics( + rd: Register, + rn: Register, + rm: Register, + shift: LogicalShiftedRegisterShift, + amount: u6, + ) Instruction { + return logicalShiftedRegister(0b11, 0b1, rd, rn, rm, shift, amount); } // Add/subtract (immediate) @@ -1316,11 +1350,11 @@ test "serialize instructions" { const testcases = [_]Testcase{ .{ // orr x0, xzr, x1 - .inst = Instruction.orr(.x0, .xzr, .x1, Instruction.Shift.none), + .inst = Instruction.orr(.x0, .xzr, .x1, .lsl, 0), .expected = 0b1_01_01010_00_0_00001_000000_11111_00000, }, .{ // orn x0, xzr, x1 - .inst = Instruction.orn(.x0, .xzr, .x1, Instruction.Shift.none), + .inst = Instruction.orn(.x0, .xzr, .x1, .lsl, 0), .expected = 0b1_01_01010_00_1_00001_000000_11111_00000, }, .{ // movz x1, #4 @@ -1440,11 +1474,11 @@ test "serialize instructions" { .expected = 0b10_101_0_001_1_0000010_00010_11111_00001, }, .{ // and x0, x4, x2 - .inst = Instruction.@"and"(.x0, .x4, .x2, .{}), + .inst = Instruction.@"and"(.x0, .x4, .x2, .lsl, 0), .expected = 0b1_00_01010_00_0_00010_000000_00100_00000, }, .{ // and x0, x4, x2, lsl #0x8 - .inst = Instruction.@"and"(.x0, .x4, .x2, .{ .shift = .lsl, .amount = 0x8 }), + .inst = Instruction.@"and"(.x0, .x4, .x2, .lsl, 0x8), .expected = 0b1_00_01010_00_0_00010_001000_00100_00000, }, .{ // add x0, x10, #10 From 783e216e7d49ce30032cd768ca266f5f08773bf4 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 12 Feb 2022 21:12:18 +0100 Subject: [PATCH 0196/2031] stage2 AArch64: Fix issue in binOp and add regression test --- src/arch/aarch64/CodeGen.zig | 18 ++++++++++- test/stage2/aarch64.zig | 62 +++++++++++++++++++++++++----------- 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 8bd5324ed1..8b5503f293 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -981,13 +981,19 @@ fn binOpRegister( if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; + const lhs_reg = if (lhs_is_register) lhs.register else blk: { const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { const bin_op = self.air.instructions.items(.data)[inst].bin_op; break :inst Air.refToIndex(bin_op.lhs).?; } else null; + const reg = try self.register_manager.allocReg(track_inst); self.register_manager.freezeRegs(&.{reg}); + + if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); + break :blk reg; }; defer self.register_manager.unfreezeRegs(&.{lhs_reg}); @@ -997,8 +1003,12 @@ fn binOpRegister( const bin_op = self.air.instructions.items(.data)[inst].bin_op; break :inst Air.refToIndex(bin_op.rhs).?; } else null; + const reg = try self.register_manager.allocReg(track_inst); self.register_manager.freezeRegs(&.{reg}); + + if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); + break :blk reg; }; defer self.register_manager.unfreezeRegs(&.{rhs_reg}); @@ -1077,6 +1087,8 @@ fn binOpImmediate( if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; + const lhs_reg = if (lhs_is_register) lhs.register else blk: { const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { const bin_op = self.air.instructions.items(.data)[inst].bin_op; @@ -1084,8 +1096,12 @@ fn binOpImmediate( if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, ).?; } else null; + const reg = try self.register_manager.allocReg(track_inst); self.register_manager.freezeRegs(&.{reg}); + + if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); + break :blk reg; }; defer self.register_manager.unfreezeRegs(&.{lhs_reg}); @@ -3141,7 +3157,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .tag = .cset, .data = .{ .r_cond = .{ .rd = reg, - .cond = condition, + .cond = condition.negate(), } }, }); }, diff --git a/test/stage2/aarch64.zig b/test/stage2/aarch64.zig index 580a375aef..b16a29f56f 100644 --- a/test/stage2/aarch64.zig +++ b/test/stage2/aarch64.zig @@ -17,15 +17,8 @@ pub fn addCases(ctx: *TestContext) !void { var case = ctx.exe("linux_aarch64 hello world", linux_aarch64); // Regular old hello world case.addCompareOutput( - \\pub export fn _start() noreturn { + \\pub fn main() void { \\ print(); - \\ exit(); - \\} - \\ - \\fn doNothing() void {} - \\ - \\fn answer() u64 { - \\ return 0x1234abcd1234abcd; \\} \\ \\fn print() void { @@ -38,16 +31,6 @@ pub fn addCases(ctx: *TestContext) !void { \\ : "memory", "cc" \\ ); \\} - \\ - \\fn exit() noreturn { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{x8}" (93), - \\ [arg1] "{x0}" (0) - \\ : "memory", "cc" - \\ ); - \\ unreachable; - \\} , "Hello, World!\n", ); @@ -129,6 +112,49 @@ pub fn addCases(ctx: *TestContext) !void { ); } + { + var case = ctx.exe("large add function", linux_aarch64); + + case.addCompareOutput( + \\pub fn main() void { + \\ assert(add(3, 4) == 791); + \\} + \\ + \\fn add(a: u32, b: u32) u32 { + \\ const x: u32 = blk: { + \\ const c = a + b; // 7 + \\ const d = a + c; // 10 + \\ const e = d + b; // 14 + \\ const f = d + e; // 24 + \\ const g = e + f; // 38 + \\ const h = f + g; // 62 + \\ const i = g + h; // 100 + \\ const j = i + d; // 110 + \\ const k = i + j; // 210 + \\ const l = k + c; // 217 + \\ const m = l + d; // 227 + \\ const n = m + e; // 241 + \\ const o = n + f; // 265 + \\ const p = o + g; // 303 + \\ const q = p + h; // 365 + \\ const r = q + i; // 465 + \\ const s = r + j; // 575 + \\ const t = s + k; // 785 + \\ break :blk t; + \\ }; + \\ const y = x + a; // 788 + \\ const z = y + a; // 791 + \\ return z; + \\} + \\ + \\fn assert(ok: bool) void { + \\ if (!ok) unreachable; + \\} + , + "", + ); + } + // macOS tests { var case = ctx.exe("hello world with updates", macos_aarch64); From 98c71cc88a2d472dded057a4a6f1d610dad9e491 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sun, 13 Feb 2022 11:01:15 +0100 Subject: [PATCH 0197/2031] stage2 AArch64: Implement calling function pointers --- src/arch/aarch64/CodeGen.zig | 164 ++++++++++------------------------- 1 file changed, 46 insertions(+), 118 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 8b5503f293..55c8c64794 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -2073,41 +2073,41 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { var info = try self.resolveCallingConventionValues(fn_ty); defer info.deinit(self); + for (info.args) |mc_arg, arg_i| { + const arg = args[arg_i]; + const arg_ty = self.air.typeOf(arg); + const arg_mcv = try self.resolveInst(args[arg_i]); + + switch (mc_arg) { + .none => continue, + .undef => unreachable, + .immediate => unreachable, + .unreach => unreachable, + .dead => unreachable, + .embedded_in_code => unreachable, + .memory => unreachable, + .compare_flags_signed => unreachable, + .compare_flags_unsigned => unreachable, + .register => |reg| { + try self.register_manager.getReg(reg, null); + try self.genSetReg(arg_ty, reg, arg_mcv); + }, + .stack_offset => { + return self.fail("TODO implement calling with parameters in memory", .{}); + }, + .ptr_stack_offset => { + return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); + }, + .ptr_embedded_in_code => { + return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); + }, + } + } + // Due to incremental compilation, how function calls are generated depends // on linking. - if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) { - for (info.args) |mc_arg, arg_i| { - const arg = args[arg_i]; - const arg_ty = self.air.typeOf(arg); - const arg_mcv = try self.resolveInst(args[arg_i]); - - switch (mc_arg) { - .none => continue, - .undef => unreachable, - .immediate => unreachable, - .unreach => unreachable, - .dead => unreachable, - .embedded_in_code => unreachable, - .memory => unreachable, - .compare_flags_signed => unreachable, - .compare_flags_unsigned => unreachable, - .register => |reg| { - try self.register_manager.getReg(reg, null); - try self.genSetReg(arg_ty, reg, arg_mcv); - }, - .stack_offset => { - return self.fail("TODO implement calling with parameters in memory", .{}); - }, - .ptr_stack_offset => { - return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); - }, - .ptr_embedded_in_code => { - return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); - }, - } - } - - if (self.air.value(callee)) |func_value| { + if (self.air.value(callee)) |func_value| { + if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) { if (func_value.castTag(.function)) |func_payload| { const func = func_payload.data; const ptr_bits = self.target.cpu.arch.ptrBitWidth(); @@ -2131,52 +2131,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { } else { return self.fail("TODO implement calling bitcasted functions", .{}); } - } else { - assert(ty.zigTypeTag() == .Pointer); - const mcv = try self.resolveInst(callee); - try self.genSetReg(Type.initTag(.usize), .x30, mcv); - - _ = try self.addInst(.{ - .tag = .blr, - .data = .{ .reg = .x30 }, - }); - } - } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - for (info.args) |mc_arg, arg_i| { - const arg = args[arg_i]; - const arg_ty = self.air.typeOf(arg); - const arg_mcv = try self.resolveInst(args[arg_i]); - // Here we do not use setRegOrMem even though the logic is similar, because - // the function call will move the stack pointer, so the offsets are different. - switch (mc_arg) { - .none => continue, - .register => |reg| { - try self.register_manager.getReg(reg, null); - try self.genSetReg(arg_ty, reg, arg_mcv); - }, - .stack_offset => { - // Here we need to emit instructions like this: - // mov qword ptr [rsp + stack_offset], x - return self.fail("TODO implement calling with parameters in memory", .{}); - }, - .ptr_stack_offset => { - return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); - }, - .ptr_embedded_in_code => { - return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); - }, - .undef => unreachable, - .immediate => unreachable, - .unreach => unreachable, - .dead => unreachable, - .embedded_in_code => unreachable, - .memory => unreachable, - .compare_flags_signed => unreachable, - .compare_flags_unsigned => unreachable, - } - } - - if (self.air.value(callee)) |func_value| { + } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { if (func_value.castTag(.function)) |func_payload| { const func = func_payload.data; // TODO I'm hacking my way through here by repurposing .memory for storing @@ -2212,41 +2167,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { } else { return self.fail("TODO implement calling bitcasted functions", .{}); } - } else { - return self.fail("TODO implement calling runtime known function pointer", .{}); - } - } else if (self.bin_file.cast(link.File.Plan9)) |p9| { - for (info.args) |mc_arg, arg_i| { - const arg = args[arg_i]; - const arg_ty = self.air.typeOf(arg); - const arg_mcv = try self.resolveInst(args[arg_i]); - - switch (mc_arg) { - .none => continue, - .undef => unreachable, - .immediate => unreachable, - .unreach => unreachable, - .dead => unreachable, - .embedded_in_code => unreachable, - .memory => unreachable, - .compare_flags_signed => unreachable, - .compare_flags_unsigned => unreachable, - .register => |reg| { - try self.register_manager.getReg(reg, null); - try self.genSetReg(arg_ty, reg, arg_mcv); - }, - .stack_offset => { - return self.fail("TODO implement calling with parameters in memory", .{}); - }, - .ptr_stack_offset => { - return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); - }, - .ptr_embedded_in_code => { - return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); - }, - } - } - if (self.air.value(callee)) |func_value| { + } else if (self.bin_file.cast(link.File.Plan9)) |p9| { if (func_value.castTag(.function)) |func_payload| { try p9.seeDecl(func_payload.data.owner_decl); const ptr_bits = self.target.cpu.arch.ptrBitWidth(); @@ -2266,10 +2187,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { } else { return self.fail("TODO implement calling bitcasted functions", .{}); } - } else { - return self.fail("TODO implement calling runtime known function pointer", .{}); - } - } else unreachable; + } else unreachable; + } else { + assert(ty.zigTypeTag() == .Pointer); + const mcv = try self.resolveInst(callee); + try self.genSetReg(ty, .x30, mcv); + + _ = try self.addInst(.{ + .tag = .blr, + .data = .{ .reg = .x30 }, + }); + } const result: MCValue = result: { switch (info.return_value) { From 22895f5616c663bb7b8ad9866c29d00bc4bc315c Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Mon, 14 Feb 2022 22:33:01 +0100 Subject: [PATCH 0198/2031] stage2 AArch64: Enable behavior testing --- src/arch/aarch64/CodeGen.zig | 34 +++++++++++++++++++++++++++---- test/behavior.zig | 2 +- test/behavior/align.zig | 18 +++++++++++++++++ test/behavior/alignof.zig | 1 + test/behavior/array.zig | 27 +++++++++++++++++++++++++ test/behavior/basic.zig | 32 +++++++++++++++++++++++++++++ test/behavior/bit_shifting.zig | 1 + test/behavior/bugs/1381.zig | 1 + test/behavior/bugs/1486.zig | 2 ++ test/behavior/bugs/1735.zig | 1 + test/behavior/bugs/1741.zig | 1 + test/behavior/bugs/2006.zig | 1 + test/behavior/bugs/2578.zig | 1 + test/behavior/bugs/3007.zig | 1 + test/behavior/bugs/3112.zig | 1 + test/behavior/bugs/3367.zig | 1 + test/behavior/bugs/394.zig | 1 + test/behavior/bugs/656.zig | 1 + test/behavior/bugs/7250.zig | 1 + test/behavior/cast.zig | 36 +++++++++++++++++++++++++++++++++ test/behavior/fn_delegation.zig | 1 + test/behavior/ir_block_deps.zig | 1 + test/behavior/optional.zig | 10 +++++++++ test/behavior/reflection.zig | 1 + test/behavior/slice.zig | 14 +++++++++++++ test/behavior/struct.zig | 28 +++++++++++++++++++++++++ test/behavior/truncate.zig | 9 +++++++++ test/behavior/var_args.zig | 3 +++ 28 files changed, 226 insertions(+), 5 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 55c8c64794..b9d5a29f18 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -1029,14 +1029,16 @@ fn binOpRegister( if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); const mir_tag: Mir.Inst.Tag = switch (tag) { - .add => .add_shifted_register, - .sub => .sub_shifted_register, + .add, .ptr_add => .add_shifted_register, + .sub, .ptr_sub => .sub_shifted_register, .xor => .eor_shifted_register, else => unreachable, }; const mir_data: Mir.Inst.Data = switch (tag) { .add, .sub, + .ptr_add, + .ptr_sub, => .{ .rrr_imm6_shift = .{ .rd = dest_reg, .rn = lhs_reg, @@ -1225,7 +1227,24 @@ fn binOp( }, .ptr_add, .ptr_sub, - => return self.fail("TODO ptr_add, ptr_sub", .{}), + => { + switch (lhs_ty.zigTypeTag()) { + .Pointer => { + const ptr_ty = lhs_ty; + const pointee_ty = switch (ptr_ty.ptrSize()) { + .One => ptr_ty.childType().childType(), // ptr to array, so get array element type + else => ptr_ty.childType(), + }; + + if (pointee_ty.abiSize(self.target.*) > 1) { + return self.fail("TODO ptr_add, ptr_sub with more element sizes", .{}); + } + + return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + }, + else => unreachable, + } + }, else => unreachable, } } @@ -1439,7 +1458,14 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { /// E to E!T fn airWrapErrUnionErr(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 return self.fail("TODO implement wrap errunion error for {}", .{self.target.cpu.arch}); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const error_union_ty = self.air.getRefType(ty_op.ty); + const payload_ty = error_union_ty.errorUnionPayload(); + const mcv = try self.resolveInst(ty_op.operand); + if (!payload_ty.hasRuntimeBits()) break :result mcv; + + return self.fail("TODO implement wrap errunion error for non-empty payloads", .{}); + }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } diff --git a/test/behavior.zig b/test/behavior.zig index db6863a8b0..abfd8fb0bf 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -54,7 +54,7 @@ test { _ = @import("behavior/decltest.zig"); } - if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_x86_64) { + if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_x86_64 and builtin.zig_backend != .stage2_aarch64) { // Tests that pass (partly) for stage1, llvm backend, C backend, wasm backend. _ = @import("behavior/bitcast.zig"); _ = @import("behavior/bugs/624.zig"); diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 96278524c0..a8d8fcd206 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -27,6 +27,7 @@ test "default alignment allows unspecified in type syntax" { } test "implicitly decreasing pointer alignment" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const a: u32 align(4) = 3; const b: u32 align(8) = 4; try expect(addUnaligned(&a, &b) == 7); @@ -37,6 +38,7 @@ fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 { } test "@alignCast pointers" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; var x: u32 align(4) = 1; expectsOnly1(&x); try expect(x == 2); @@ -102,6 +104,7 @@ fn fnWithAlignedStack() i32 { } test "implicitly decreasing slice alignment" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const a: u32 align(4) = 3; @@ -113,6 +116,7 @@ fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 { } test "specifying alignment allows pointer cast" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; try testBytesAlign(0x33); @@ -124,6 +128,7 @@ fn testBytesAlign(b: u8) !void { } test "@alignCast slices" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var array align(4) = [_]u32{ 1, 1 }; @@ -139,6 +144,7 @@ fn sliceExpects4(slice: []align(4) u32) void { } test "return error union with 128-bit integer" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; try expect(3 == try give()); @@ -148,6 +154,7 @@ fn give() anyerror!u128 { } test "page aligned array on stack" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @@ -173,6 +180,7 @@ fn noop1() align(1) void {} fn noop4() align(4) void {} test "function alignment" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -189,6 +197,7 @@ test "function alignment" { } test "implicitly decreasing fn alignment" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; @@ -216,6 +225,7 @@ fn alignedBig() align(16) i32 { } test "@alignCast functions" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; @@ -239,6 +249,7 @@ fn simple4() align(4) i32 { } test "generic function with align param" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; @@ -260,6 +271,7 @@ fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { } test "runtime known array index has best alignment possible" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @@ -302,6 +314,7 @@ fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) !void { } test "alignment of function with c calling convention" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage1) return error.SkipZigTest; var runtime_nothing = ¬hing; @@ -318,6 +331,7 @@ const DefaultAligned = struct { }; test "read 128-bit field from default aligned struct in stack memory" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -337,6 +351,7 @@ var default_aligned_global = DefaultAligned{ }; test "read 128-bit field from default aligned struct in global memory" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; @@ -348,6 +363,7 @@ test "read 128-bit field from default aligned struct in global memory" { } test "struct field explicit alignment" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; @@ -369,6 +385,7 @@ test "struct field explicit alignment" { } test "align(@alignOf(T)) T does not force resolution of T" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; @@ -397,6 +414,7 @@ test "align(@alignOf(T)) T does not force resolution of T" { } test "align(N) on functions" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; diff --git a/test/behavior/alignof.zig b/test/behavior/alignof.zig index 749855db52..5a49146694 100644 --- a/test/behavior/alignof.zig +++ b/test/behavior/alignof.zig @@ -11,6 +11,7 @@ const Foo = struct { }; test "@alignOf(T) before referencing T" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 23820e71b5..e93f0f3e90 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -6,6 +6,7 @@ const expect = testing.expect; const expectEqual = testing.expectEqual; test "array to slice" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const a: u32 align(4) = 3; @@ -20,6 +21,7 @@ test "array to slice" { } test "arrays" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var array: [5]u32 = undefined; @@ -46,6 +48,7 @@ fn getArrayLen(a: []const u32) usize { } test "array init with mult" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const a = 'a'; @@ -57,6 +60,7 @@ test "array init with mult" { } test "array literal with explicit type" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const hex_mult: [4]u16 = .{ 4096, 256, 16, 1 }; @@ -86,6 +90,7 @@ const ArrayDotLenConstExpr = struct { const some_array = [_]u8{ 0, 1, 2, 3 }; test "array literal with specified size" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var array = [2]u8{ 1, 2 }; @@ -94,6 +99,7 @@ test "array literal with specified size" { } test "array len field" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var arr = [4]u8{ 0, 0, 0, 0 }; @@ -105,6 +111,7 @@ test "array len field" { } test "array with sentinels" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { @@ -134,6 +141,7 @@ test "array with sentinels" { } test "void arrays" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var array: [4]void = undefined; @@ -144,6 +152,7 @@ test "void arrays" { } test "nested arrays" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" }; @@ -157,6 +166,7 @@ test "nested arrays" { } test "implicit comptime in array type size" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var arr: [plusOne(10)]bool = undefined; @@ -168,6 +178,7 @@ fn plusOne(x: u32) u32 { } test "single-item pointer to array indexing and slicing" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; try testSingleItemPtrArrayIndexSlice(); @@ -193,6 +204,7 @@ fn doSomeMangling(array: *[4]u8) void { } test "implicit cast zero sized array ptr to slice" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; { @@ -208,6 +220,7 @@ test "implicit cast zero sized array ptr to slice" { } test "anonymous list literal syntax" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { @@ -227,6 +240,7 @@ var s_array: [8]Sub = undefined; const Sub = struct { b: u8 }; const Str = struct { a: []Sub }; test "set global var array via slice embedded in struct" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -243,6 +257,7 @@ test "set global var array via slice embedded in struct" { } test "read/write through global variable array of struct fields initialized via array mult" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -264,6 +279,7 @@ test "read/write through global variable array of struct fields initialized via } test "implicit cast single-item pointer" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -284,6 +300,7 @@ fn testArrayByValAtComptime(b: [2]u8) u8 { } test "comptime evaluating function that takes array by value" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -296,6 +313,7 @@ test "comptime evaluating function that takes array by value" { } test "runtime initialize array elem and then implicit cast to slice" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -306,6 +324,7 @@ test "runtime initialize array elem and then implicit cast to slice" { } test "array literal as argument to function" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -334,6 +353,7 @@ test "array literal as argument to function" { } test "double nested array to const slice cast in array literal" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -395,6 +415,7 @@ test "double nested array to const slice cast in array literal" { } test "anonymous literal in array" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -420,6 +441,7 @@ test "anonymous literal in array" { } test "access the null element of a null terminated array" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -437,6 +459,7 @@ test "access the null element of a null terminated array" { } test "type deduction for array subscript expression" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -455,6 +478,7 @@ test "type deduction for array subscript expression" { } test "sentinel element count towards the ABI size calculation" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO @@ -481,6 +505,7 @@ test "sentinel element count towards the ABI size calculation" { } test "zero-sized array with recursive type definition" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO @@ -505,6 +530,7 @@ test "zero-sized array with recursive type definition" { } test "type coercion of anon struct literal to array" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -540,6 +566,7 @@ test "type coercion of anon struct literal to array" { } test "type coercion of pointer to anon struct literal to pointer to array" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 18a24f9b3a..0c2c293d23 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -15,6 +15,7 @@ test "empty function with comments" { } test "truncate" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; try expect(testTruncate(0x10fd) == 0xfd); @@ -25,6 +26,7 @@ fn testTruncate(x: u32) u8 { } test "truncate to non-power-of-two integers" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; try testTrunc(u32, u1, 0b10101, 0b1); @@ -46,6 +48,7 @@ const g1: i32 = 1233 + 1; var g2: i32 = 0; test "global variables" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; try expect(g2 == 0); g2 = g1; try expect(g2 == 1234); @@ -112,6 +115,7 @@ fn first4KeysOfHomeRow() []const u8 { } test "return string from function" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -119,12 +123,14 @@ test "return string from function" { } test "hex escape" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; try expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); } test "multiline string" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const s1 = @@ -137,6 +143,7 @@ test "multiline string" { } test "multiline string comments at start" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const s1 = @@ -149,6 +156,7 @@ test "multiline string comments at start" { } test "multiline string comments at end" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const s1 = @@ -161,6 +169,7 @@ test "multiline string comments at end" { } test "multiline string comments in middle" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const s1 = @@ -173,6 +182,7 @@ test "multiline string comments in middle" { } test "multiline string comments at multiple places" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const s1 = @@ -191,6 +201,7 @@ test "string concatenation" { } test "array mult operator" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; try expect(mem.eql(u8, "ab" ** 5, "ababababab")); @@ -216,6 +227,7 @@ test "compile time global reinterpret" { } test "cast undefined" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const array: [100]u8 = undefined; @@ -227,6 +239,7 @@ fn testCastUndefined(x: []const u8) void { } test "implicit cast after unreachable" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; try expect(outer() == 1234); @@ -284,6 +297,7 @@ fn fB() []const u8 { } test "call function pointer in struct" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -310,6 +324,7 @@ const FnPtrWrapper = struct { }; test "const ptr from var variable" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var x: u64 = undefined; @@ -326,6 +341,7 @@ fn copy(src: *const u64, dst: *u64) void { } test "call result of if else expression" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO @@ -339,6 +355,7 @@ fn f2(x: bool) []const u8 { } test "memcpy and memset intrinsics" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO @@ -361,6 +378,7 @@ fn testMemcpyMemset() !void { } test "variable is allowed to be a pointer to an opaque type" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO @@ -374,6 +392,7 @@ fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA { } test "take address of parameter" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -400,6 +419,7 @@ fn testPointerToVoidReturnType2() *const void { } test "array 2D const double ptr" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -419,6 +439,7 @@ fn testArray2DConstDoublePtr(ptr: *const f32) !void { } test "double implicit cast in same expression" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -430,6 +451,7 @@ fn nine() u8 { } test "struct inside function" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; try testStructInFn(); @@ -451,6 +473,7 @@ fn testStructInFn() !void { } test "fn call returning scalar optional in equality expression" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; try expect(getNull() == null); } @@ -459,6 +482,7 @@ fn getNull() ?*i32 { } test "global variable assignment with optional unwrapping with var initialized to undefined" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { @@ -476,6 +500,7 @@ test "global variable assignment with optional unwrapping with var initialized t var global_foo: *i32 = undefined; test "peer result location with typed parent, runtime condition, comptime prongs" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -550,6 +575,7 @@ test "comptime cast fn to ptr" { } test "equality compare fn ptrs" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage1) return error.SkipZigTest; var a = &emptyFn; @@ -557,6 +583,7 @@ test "equality compare fn ptrs" { } test "self reference through fn ptr field" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; @@ -576,6 +603,7 @@ test "self reference through fn ptr field" { } test "global variable initialized to global variable array element" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -593,6 +621,7 @@ var gdt = [_]GDTEntry{ var global_ptr = &gdt[0]; test "global constant is loaded with a runtime-known index" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { @@ -610,6 +639,7 @@ test "global constant is loaded with a runtime-known index" { } test "multiline string literal is null terminated" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -643,6 +673,7 @@ test "explicit cast optional pointers" { } test "pointer comparison" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -655,6 +686,7 @@ fn ptrEql(a: *const []const u8, b: *const []const u8) bool { } test "string concatenation" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; diff --git a/test/behavior/bit_shifting.zig b/test/behavior/bit_shifting.zig index c0b2729bdf..1a01cbd732 100644 --- a/test/behavior/bit_shifting.zig +++ b/test/behavior/bit_shifting.zig @@ -61,6 +61,7 @@ fn ShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, compt } test "sharded table" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; diff --git a/test/behavior/bugs/1381.zig b/test/behavior/bugs/1381.zig index 91a253af24..2f05d2fa96 100644 --- a/test/behavior/bugs/1381.zig +++ b/test/behavior/bugs/1381.zig @@ -12,6 +12,7 @@ const A = union(enum) { }; test "union that needs padding bytes inside an array" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; var as = [_]A{ diff --git a/test/behavior/bugs/1486.zig b/test/behavior/bugs/1486.zig index 8f954a3600..91d5b621d2 100644 --- a/test/behavior/bugs/1486.zig +++ b/test/behavior/bugs/1486.zig @@ -1,10 +1,12 @@ const std = @import("std"); const expect = std.testing.expect; +const builtin = @import("builtin"); const ptr = &global; var global: usize = 123; test "constant pointer to global variable causes runtime load" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; global = 1234; try expect(&global == ptr); try expect(ptr.* == 1234); diff --git a/test/behavior/bugs/1735.zig b/test/behavior/bugs/1735.zig index c07bd9472b..556b899de1 100644 --- a/test/behavior/bugs/1735.zig +++ b/test/behavior/bugs/1735.zig @@ -42,6 +42,7 @@ const a = struct { }; test "initialization" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; diff --git a/test/behavior/bugs/1741.zig b/test/behavior/bugs/1741.zig index 280aafc52e..f4cc2101c4 100644 --- a/test/behavior/bugs/1741.zig +++ b/test/behavior/bugs/1741.zig @@ -2,6 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); test "fixed" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const x: f32 align(128) = 12.34; diff --git a/test/behavior/bugs/2006.zig b/test/behavior/bugs/2006.zig index 4d76230c88..fcacb9a2c6 100644 --- a/test/behavior/bugs/2006.zig +++ b/test/behavior/bugs/2006.zig @@ -6,6 +6,7 @@ const S = struct { p: *S, }; test "bug 2006" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; var a: S = undefined; a = S{ .p = undefined }; diff --git a/test/behavior/bugs/2578.zig b/test/behavior/bugs/2578.zig index 15f5bf0e53..90db296158 100644 --- a/test/behavior/bugs/2578.zig +++ b/test/behavior/bugs/2578.zig @@ -12,6 +12,7 @@ fn bar(pointer: ?*anyopaque) void { } test "fixed" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/3007.zig b/test/behavior/bugs/3007.zig index 0b3cbdc56d..c93bbf8d20 100644 --- a/test/behavior/bugs/3007.zig +++ b/test/behavior/bugs/3007.zig @@ -19,6 +19,7 @@ fn get_foo() Foo.FooError!*Foo { } test "fixed" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/3112.zig b/test/behavior/bugs/3112.zig index 089f3e59f6..ebd8fd1ef3 100644 --- a/test/behavior/bugs/3112.zig +++ b/test/behavior/bugs/3112.zig @@ -12,6 +12,7 @@ fn prev(p: ?State) void { } test "zig test crash" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; diff --git a/test/behavior/bugs/3367.zig b/test/behavior/bugs/3367.zig index f540fdf6df..6468498ab6 100644 --- a/test/behavior/bugs/3367.zig +++ b/test/behavior/bugs/3367.zig @@ -10,6 +10,7 @@ const Mixin = struct { }; test "container member access usingnamespace decls" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var foo = Foo{}; diff --git a/test/behavior/bugs/394.zig b/test/behavior/bugs/394.zig index ec1bd5cc9f..28934c8dd0 100644 --- a/test/behavior/bugs/394.zig +++ b/test/behavior/bugs/394.zig @@ -11,6 +11,7 @@ const expect = @import("std").testing.expect; const builtin = @import("builtin"); test "bug 394 fixed" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const x = S{ diff --git a/test/behavior/bugs/656.zig b/test/behavior/bugs/656.zig index bd93c2b88c..d71dc426f9 100644 --- a/test/behavior/bugs/656.zig +++ b/test/behavior/bugs/656.zig @@ -11,6 +11,7 @@ const Value = struct { }; test "optional if after an if in a switch prong of a switch with 2 prongs in an else" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; try foo(false, true); diff --git a/test/behavior/bugs/7250.zig b/test/behavior/bugs/7250.zig index 27810acea4..ee04847e51 100644 --- a/test/behavior/bugs/7250.zig +++ b/test/behavior/bugs/7250.zig @@ -14,6 +14,7 @@ threadlocal var g_uart0 = nrfx_uart_t{ }; test "reference a global threadlocal variable" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 4028d8c5f1..85e3368441 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -18,6 +18,7 @@ test "integer literal to pointer cast" { } test "peer type resolution: ?T and T" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; try expect(peerTypeTAndOptionalT(true, false).? == 0); @@ -94,6 +95,7 @@ test "comptime_int @intToFloat" { } test "@floatToInt" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -116,6 +118,7 @@ fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void { } test "implicitly cast indirect pointer to maybe-indirect pointer" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { @@ -174,6 +177,7 @@ test "@floatCast comptime_int and comptime_float" { } test "coerce undefined to optional" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; try expect(MakeType(void).getNull() == null); @@ -193,6 +197,7 @@ fn MakeType(comptime T: type) type { } test "implicit cast from *[N]T to [*c]T" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var x: [4]u16 = [4]u16{ 0, 1, 2, 3 }; @@ -205,6 +210,7 @@ test "implicit cast from *[N]T to [*c]T" { } test "*usize to *void" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; var i = @as(usize, 0); var v = @ptrCast(*void, &i); v.* = {}; @@ -230,6 +236,7 @@ test "@intCast to u0 and use the result" { } test "peer result null and comptime_int" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { @@ -253,6 +260,7 @@ test "peer result null and comptime_int" { } test "*const ?[*]const T to [*c]const [*c]const T" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var array = [_]u8{ 'o', 'k' }; @@ -264,6 +272,7 @@ test "*const ?[*]const T to [*c]const [*c]const T" { } test "array coersion to undefined at runtime" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @setRuntimeSafety(true); @@ -293,6 +302,7 @@ fn implicitIntLitToOptional() void { } test "return u8 coercing into ?u32 return type" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { @@ -313,6 +323,7 @@ test "cast from ?[*]T to ??[*]T" { } test "peer type unsigned int to signed" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -325,6 +336,7 @@ test "peer type unsigned int to signed" { } test "expected [*c]const u8, found [*:0]const u8" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -384,6 +396,7 @@ fn castToOptionalTypeError(z: i32) !void { } test "implicitly cast from [0]T to anyerror![]T" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -455,6 +468,7 @@ fn testCastConstArrayRefToConstSlice() !void { } test "peer type resolution: error and [N]T" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -689,6 +703,7 @@ test "type coercion related to sentinel-termination" { } test "peer type resolution implicit cast to return type" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -710,6 +725,7 @@ test "peer type resolution implicit cast to return type" { } test "peer type resolution implicit cast to variable type" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -806,6 +822,7 @@ test "comptime float casts" { } test "pointer reinterpret const float to int" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -822,6 +839,7 @@ test "pointer reinterpret const float to int" { } test "implicit cast from [*]T to ?*anyopaque" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -840,6 +858,7 @@ fn incrementVoidPtrArray(array: ?*anyopaque, len: usize) void { } test "compile time int to ptr of function" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO @@ -857,6 +876,7 @@ fn foobar(func: PFN_void) !void { } test "implicit ptr to *anyopaque" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -871,6 +891,7 @@ test "implicit ptr to *anyopaque" { } test "return null from fn() anyerror!?&T" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -887,6 +908,7 @@ fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A { } test "peer type resolution: [0]u8 and []const u8" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -907,6 +929,7 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { } test "implicitly cast from [N]T to ?[]const T" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -920,6 +943,7 @@ fn castToOptionalSlice() ?[]const u8 { } test "cast u128 to f128 and back" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -941,6 +965,7 @@ fn cast128Float(x: u128) f128 { } test "implicit cast from *[N]T to ?[*]T" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -956,6 +981,7 @@ test "implicit cast from *[N]T to ?[*]T" { } test "implicit cast from *T to ?*anyopaque" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -970,6 +996,7 @@ fn incrementVoidPtrValue(value: ?*anyopaque) void { } test "implicit cast *[0]T to E![]const u8" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -987,6 +1014,7 @@ test "cast from array reference to fn: comptime fn ptr" { try expect(@ptrToInt(f) == @ptrToInt(&global_array)); } test "cast from array reference to fn: runtime fn ptr" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -996,6 +1024,7 @@ test "cast from array reference to fn: runtime fn ptr" { } test "*const [N]null u8 to ?[]const u8" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -1034,6 +1063,7 @@ test "cast between [*c]T and ?[*:0]T on fn parameter" { var global_struct: struct { f0: usize } = undefined; test "assignment to optional pointer result loc" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -1043,6 +1073,7 @@ test "assignment to optional pointer result loc" { } test "cast between *[N]void and []void" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -1052,6 +1083,7 @@ test "cast between *[N]void and []void" { } test "peer resolve arrays of different size to const slice" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -1065,6 +1097,7 @@ fn boolToStr(b: bool) []const u8 { } test "cast f16 to wider types" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -1083,6 +1116,7 @@ test "cast f16 to wider types" { } test "cast f128 to narrower types" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -1101,6 +1135,7 @@ test "cast f128 to narrower types" { } test "peer type resolution: unreachable, null, slice" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -1119,6 +1154,7 @@ test "peer type resolution: unreachable, null, slice" { } test "cast i8 fn call peers to i32 result" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO diff --git a/test/behavior/fn_delegation.zig b/test/behavior/fn_delegation.zig index 25ec3dea1b..eee8f52490 100644 --- a/test/behavior/fn_delegation.zig +++ b/test/behavior/fn_delegation.zig @@ -32,6 +32,7 @@ fn custom(comptime T: type, comptime num: u64) fn (T) u64 { } test "fn delegation" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const foo = Foo{}; diff --git a/test/behavior/ir_block_deps.zig b/test/behavior/ir_block_deps.zig index cbc5cc2419..d7d50b4be1 100644 --- a/test/behavior/ir_block_deps.zig +++ b/test/behavior/ir_block_deps.zig @@ -18,6 +18,7 @@ fn getErrInt() anyerror!i32 { } test "ir block deps" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index 3caf777195..78788d6556 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -5,6 +5,7 @@ const expect = testing.expect; const expectEqual = testing.expectEqual; test "passing an optional integer as a parameter" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -25,6 +26,7 @@ test "passing an optional integer as a parameter" { pub const EmptyStruct = struct {}; test "optional pointer to size zero struct" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -34,6 +36,7 @@ test "optional pointer to size zero struct" { } test "equality compare optional pointers" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -58,6 +61,7 @@ fn testNullPtrsEql() !void { } test "optional with void type" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -69,6 +73,7 @@ test "optional with void type" { } test "address of unwrap optional" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -89,6 +94,7 @@ test "address of unwrap optional" { } test "nested optional field in struct" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -105,6 +111,7 @@ test "nested optional field in struct" { } test "equality compare optional with non-optional" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -142,6 +149,7 @@ fn test_cmp_optional_non_optional() !void { } test "unwrap function call with optional pointer return value" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -163,6 +171,7 @@ test "unwrap function call with optional pointer return value" { } test "nested orelse" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -189,6 +198,7 @@ test "nested orelse" { } test "self-referential struct through a slice of optional" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO diff --git a/test/behavior/reflection.zig b/test/behavior/reflection.zig index 96c81fe0d0..a181e95b86 100644 --- a/test/behavior/reflection.zig +++ b/test/behavior/reflection.zig @@ -28,6 +28,7 @@ fn dummy(a: bool, b: i32, c: f32) i32 { } test "reflection: @field" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index badaf7ef03..4b73a3a140 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -27,6 +27,7 @@ comptime { } test "slicing" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO @@ -68,6 +69,7 @@ test "comptime slice of undefined pointer of length 0" { } test "implicitly cast array of size 0 to slice" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @@ -80,6 +82,7 @@ fn assertLenIsZero(msg: []const u8) !void { } test "access len index of sentinel-terminated slice" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { @@ -129,6 +132,7 @@ test "slice of type" { } test "generic malloc free" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -187,6 +191,7 @@ test "comptime pointer cast array and then slice" { } test "slicing zero length array" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -202,6 +207,7 @@ test "slicing zero length array" { const x = @intToPtr([*]i32, 0x1000)[0..0x500]; const y = x[0x100..]; test "compile time slice of pointer to hard coded address" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @@ -215,6 +221,7 @@ test "compile time slice of pointer to hard coded address" { } test "slice string literal has correct type" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -230,6 +237,7 @@ test "slice string literal has correct type" { } test "result location zero sized array inside struct field implicit cast to slice" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const E = struct { @@ -240,6 +248,7 @@ test "result location zero sized array inside struct field implicit cast to slic } test "runtime safety lets us slice from len..len" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -252,6 +261,7 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { } test "C pointer" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -262,6 +272,7 @@ test "C pointer" { } test "C pointer slice access" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -291,6 +302,7 @@ fn sliceSum(comptime q: []const u8) i32 { } test "slice type with custom alignment" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; @@ -305,6 +317,7 @@ test "slice type with custom alignment" { } test "obtaining a null terminated slice" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO @@ -350,6 +363,7 @@ test "empty array to slice" { } test "@ptrCast slice to pointer" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index ecdd6a1846..8428ea886f 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -9,6 +9,7 @@ const maxInt = std.math.maxInt; top_level_field: i32, test "top level fields" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var instance = @This(){ @@ -42,6 +43,7 @@ const StructWithFields = struct { }; test "non-packed struct has fields padded out to the required alignment" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const foo = StructWithFields{ .a = 5, .b = 1, .c = 10, .d = 2 }; @@ -65,6 +67,7 @@ const SmallStruct = struct { }; test "lower unnamed constants" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; var foo = SmallStruct{ .a = 1, .b = 255 }; try expect(foo.first() == 1); try expect(foo.second() == 255); @@ -83,6 +86,7 @@ const StructFoo = struct { }; test "structs" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var foo: StructFoo = undefined; @@ -101,6 +105,7 @@ fn testMutation(foo: *StructFoo) void { } test "struct byval assign" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var foo1: StructFoo = undefined; @@ -134,6 +139,7 @@ fn returnEmptyStructInstance() StructWithNoFields { } test "fn call of struct field" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const Foo = struct { @@ -165,12 +171,14 @@ const MemberFnTestFoo = struct { }; test "call member function directly" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const instance = MemberFnTestFoo{ .x = 1234 }; const result = MemberFnTestFoo.member(instance); try expect(result == 1234); } test "store member function in variable" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const instance = MemberFnTestFoo{ .x = 1234 }; const memberFn = MemberFnTestFoo.member; const result = memberFn(instance); @@ -178,6 +186,7 @@ test "store member function in variable" { } test "member functions" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const r = MemberFnRand{ .seed = 1234 }; try expect(r.getSeed() == 1234); } @@ -189,6 +198,7 @@ const MemberFnRand = struct { }; test "return struct byval from function" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const bar = makeBar2(1234, 5678); @@ -206,6 +216,7 @@ fn makeBar2(x: i32, y: i32) Bar { } test "call method with mutable reference to struct with no fields" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { @@ -238,6 +249,7 @@ test "usingnamespace within struct scope" { } test "struct field init with catch" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { @@ -296,6 +308,7 @@ const Val = struct { }; test "struct point to self" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -314,6 +327,7 @@ test "struct point to self" { } test "void struct fields" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -334,6 +348,7 @@ const VoidStructFieldsFoo = struct { }; test "return empty struct from fn" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -347,6 +362,7 @@ fn testReturnEmptyStructFromFn() EmptyStruct2 { } test "pass slice of empty struct to fn" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -359,6 +375,7 @@ fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { } test "self-referencing struct via array member" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -389,6 +406,7 @@ const EmptyStruct = struct { }; test "align 1 field before self referential align 8 field as slice return type" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -413,6 +431,7 @@ const APackedStruct = packed struct { }; test "packed struct" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -438,6 +457,7 @@ const Foo96Bits = packed struct { }; test "packed struct 24bits" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -484,6 +504,7 @@ test "packed struct 24bits" { } test "runtime struct initialization of bitfield" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -523,6 +544,7 @@ const Bitfields = packed struct { }; test "native bit field understands endianness" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -546,6 +568,7 @@ test "native bit field understands endianness" { } test "implicit cast packed struct field to const ptr" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -581,6 +604,7 @@ test "zero-bit field in packed struct" { } test "packed struct with non-ABI-aligned field" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -610,6 +634,7 @@ const bit_field_1 = BitField1{ }; test "bit field access" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -642,6 +667,7 @@ fn getC(data: *const BitField1) u2 { } test "default struct initialization fields" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -907,6 +933,7 @@ test "packed struct field passed to generic function" { } test "anonymous struct literal syntax" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -1100,6 +1127,7 @@ test "type coercion of pointer to anon struct literal to pointer to struct" { } test "packed struct with undefined initializers" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/truncate.zig b/test/behavior/truncate.zig index 001ba538b2..7fe5b8ecb6 100644 --- a/test/behavior/truncate.zig +++ b/test/behavior/truncate.zig @@ -3,6 +3,7 @@ const builtin = @import("builtin"); const expect = std.testing.expect; test "truncate u0 to larger integer allowed and has comptime known result" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var x: u0 = 0; @@ -11,6 +12,7 @@ test "truncate u0 to larger integer allowed and has comptime known result" { } test "truncate.u0.literal" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var z = @truncate(u0, 0); @@ -18,6 +20,7 @@ test "truncate.u0.literal" { } test "truncate.u0.const" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const c0: usize = 0; @@ -26,6 +29,7 @@ test "truncate.u0.const" { } test "truncate.u0.var" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var d: u8 = 2; @@ -34,6 +38,7 @@ test "truncate.u0.var" { } test "truncate i0 to larger integer allowed and has comptime known result" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var x: i0 = 0; @@ -42,6 +47,7 @@ test "truncate i0 to larger integer allowed and has comptime known result" { } test "truncate.i0.literal" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var z = @truncate(i0, 0); @@ -49,6 +55,7 @@ test "truncate.i0.literal" { } test "truncate.i0.const" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const c0: isize = 0; @@ -57,6 +64,7 @@ test "truncate.i0.const" { } test "truncate.i0.var" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var d: i8 = 2; @@ -65,6 +73,7 @@ test "truncate.i0.var" { } test "truncate on comptime integer" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var x = @truncate(u16, 9999); diff --git a/test/behavior/var_args.zig b/test/behavior/var_args.zig index 63b8c35e1b..0e37c845b6 100644 --- a/test/behavior/var_args.zig +++ b/test/behavior/var_args.zig @@ -25,6 +25,7 @@ fn readFirstVarArg(args: anytype) void { } test "send void arg to var args" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -84,6 +85,7 @@ fn foo2(args: anytype) bool { } test "array of var args functions" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -93,6 +95,7 @@ test "array of var args functions" { } test "pass zero length array to var args param" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From 6c195db03a502074952181261807cf9a8e140d8d Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Mon, 14 Feb 2022 22:36:13 +0100 Subject: [PATCH 0199/2031] ci: add aarch64-linux behavior tests --- ci/zinc/linux_test.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ci/zinc/linux_test.sh b/ci/zinc/linux_test.sh index 8f3eaacc7e..453269029d 100755 --- a/ci/zinc/linux_test.sh +++ b/ci/zinc/linux_test.sh @@ -7,8 +7,9 @@ ZIG=$DEBUG_STAGING/bin/zig $ZIG test test/behavior.zig -fno-stage1 -I test -fLLVM $ZIG test test/behavior.zig -fno-stage1 -I test -fLLVM -target aarch64-linux --test-cmd qemu-aarch64 --test-cmd-bin $ZIG test test/behavior.zig -fno-stage1 -I test -ofmt=c -$ZIG test test/behavior.zig -fno-stage1 -I test -target wasm32-wasi --test-cmd wasmtime --test-cmd-bin -$ZIG test test/behavior.zig -fno-stage1 -I test -target arm-linux --test-cmd qemu-arm --test-cmd-bin +$ZIG test test/behavior.zig -fno-stage1 -I test -target wasm32-wasi --test-cmd wasmtime --test-cmd-bin +$ZIG test test/behavior.zig -fno-stage1 -I test -target arm-linux --test-cmd qemu-arm --test-cmd-bin +$ZIG test test/behavior.zig -fno-stage1 -I test -target aarch64-linux --test-cmd qemu-aarch64 --test-cmd-bin $ZIG test test/behavior.zig -fno-stage1 -I test $ZIG build test-behavior -fqemu -fwasmtime From 807edd2234b016cd5470c51ac2bac451554c614d Mon Sep 17 00:00:00 2001 From: John Schmidt <3405586+schmee@users.noreply.github.com> Date: Tue, 15 Feb 2022 03:52:12 +0100 Subject: [PATCH 0200/2031] LLVM backend: refactor LLVM bitcount ops (#10882) Use `llvm.getIntrinsic` instead of `llvm.getNamedFunction` --- src/codegen/llvm.zig | 56 +++++++++++--------------------------------- 1 file changed, 14 insertions(+), 42 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 493b895d5d..3d65829c1e 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2203,8 +2203,8 @@ pub const FuncGen = struct { .memcpy => try self.airMemcpy(inst), .set_union_tag => try self.airSetUnionTag(inst), .get_union_tag => try self.airGetUnionTag(inst), - .clz => try self.airClzCtz(inst, "ctlz"), - .ctz => try self.airClzCtz(inst, "cttz"), + .clz => try self.airClzCtz(inst, "llvm.ctlz"), + .ctz => try self.airClzCtz(inst, "llvm.cttz"), .popcount => try self.airPopCount(inst), .tag_name => try self.airTagName(inst), .error_name => try self.airErrorName(inst), @@ -4320,40 +4320,24 @@ pub const FuncGen = struct { return self.builder.buildCall(fn_val, ¶ms, params.len, .C, .Auto, ""); } - fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, prefix: [*:0]const u8) !?*const llvm.Value { + fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand_ty = self.air.typeOf(ty_op.operand); const operand = try self.resolveInst(ty_op.operand); - const target = self.dg.module.getTarget(); - const bits = operand_ty.intInfo(target).bits; - const vec_len: ?u32 = switch (operand_ty.zigTypeTag()) { - .Vector => operand_ty.vectorLen(), - else => null, - }; - var fn_name_buf: [100]u8 = undefined; - const llvm_fn_name = if (vec_len) |len| - std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.v{d}i{d}", .{ - prefix, len, bits, - }) catch unreachable - else - std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.i{d}", .{ - prefix, bits, - }) catch unreachable; const llvm_i1 = self.context.intType(1); - const fn_val = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: { - const operand_llvm_ty = try self.dg.llvmType(operand_ty); - const param_types = [_]*const llvm.Type{ operand_llvm_ty, llvm_i1 }; - const fn_type = llvm.functionType(operand_llvm_ty, ¶m_types, param_types.len, .False); - break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type); - }; + const operand_llvm_ty = try self.dg.llvmType(operand_ty); + const fn_val = self.getIntrinsic(llvm_fn_name, &.{operand_llvm_ty}); const params = [_]*const llvm.Value{ operand, llvm_i1.constNull() }; const wrong_size_result = self.builder.buildCall(fn_val, ¶ms, params.len, .C, .Auto, ""); const result_ty = self.air.typeOfIndex(inst); const result_llvm_ty = try self.dg.llvmType(result_ty); + + const target = self.dg.module.getTarget(); + const bits = operand_ty.intInfo(target).bits; const result_bits = result_ty.intInfo(target).bits; if (bits > result_bits) { return self.builder.buildTrunc(wrong_size_result, result_llvm_ty, ""); @@ -4370,29 +4354,17 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand_ty = self.air.typeOf(ty_op.operand); const operand = try self.resolveInst(ty_op.operand); - const target = self.dg.module.getTarget(); - const bits = operand_ty.intInfo(target).bits; - const vec_len: ?u32 = switch (operand_ty.zigTypeTag()) { - .Vector => operand_ty.vectorLen(), - else => null, - }; - - var fn_name_buf: [100]u8 = undefined; - const llvm_fn_name = if (vec_len) |len| - std.fmt.bufPrintZ(&fn_name_buf, "llvm.ctpop.v{d}i{d}", .{ len, bits }) catch unreachable - else - std.fmt.bufPrintZ(&fn_name_buf, "llvm.ctpop.i{d}", .{bits}) catch unreachable; - const fn_val = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: { - const operand_llvm_ty = try self.dg.llvmType(operand_ty); - const param_types = [_]*const llvm.Type{operand_llvm_ty}; - const fn_type = llvm.functionType(operand_llvm_ty, ¶m_types, param_types.len, .False); - break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type); - }; const params = [_]*const llvm.Value{operand}; + const operand_llvm_ty = try self.dg.llvmType(operand_ty); + const fn_val = self.getIntrinsic("llvm.ctpop", &.{operand_llvm_ty}); + const wrong_size_result = self.builder.buildCall(fn_val, ¶ms, params.len, .C, .Auto, ""); const result_ty = self.air.typeOfIndex(inst); const result_llvm_ty = try self.dg.llvmType(result_ty); + + const target = self.dg.module.getTarget(); + const bits = operand_ty.intInfo(target).bits; const result_bits = result_ty.intInfo(target).bits; if (bits > result_bits) { return self.builder.buildTrunc(wrong_size_result, result_llvm_ty, ""); From dc6553d93eb856a1593d43f0d1ea387c74fa7b44 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 14 Feb 2022 20:08:04 -0700 Subject: [PATCH 0201/2031] CI: update download page and langref for 0.9.1 --- ci/srht/index.json | 71 +++++++++++++++++++++++++++++++++++++++++++++ doc/langref.html.in | 2 +- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/ci/srht/index.json b/ci/srht/index.json index 98e8f4035b..aa0533af39 100644 --- a/ci/srht/index.json +++ b/ci/srht/index.json @@ -40,6 +40,77 @@ "size": "{{AARCH64_LINUX_BYTESIZE}}" } }, + "0.9.1": { + "date": "2022-02-14", + "docs": "https://ziglang.org/documentation/0.9.1/", + "stdDocs": "https://ziglang.org/documentation/0.9.1/std/", + "notes": "https://ziglang.org/download/0.9.1/release-notes.html", + "src": { + "tarball": "https://ziglang.org/download/0.9.1/zig-0.9.1.tar.xz", + "shasum": "38cf4e84481f5facc766ba72783e7462e08d6d29a5d47e3b75c8ee3142485210", + "size": "13940828" + }, + "bootstrap": { + "tarball": "https://ziglang.org/download/0.9.1/zig-bootstrap-0.9.1.tar.xz", + "shasum": "0a8e221c71860d8975c15662b3ed3bd863e81c4fe383455a596e5e0e490d6109", + "size": "42488812" + }, + "x86_64-freebsd": { + "tarball": "https://ziglang.org/download/0.9.1/zig-freebsd-x86_64-0.9.1.tar.xz", + "shasum": "4e06009bd3ede34b72757eec1b5b291b30aa0d5046dadd16ecb6b34a02411254", + "size": "39028848" + }, + "aarch64-linux": { + "tarball": "https://ziglang.org/download/0.9.1/zig-linux-aarch64-0.9.1.tar.xz", + "shasum": "5d99a39cded1870a3fa95d4de4ce68ac2610cca440336cfd252ffdddc2b90e66", + "size": "37034860" + }, + "armv7a-linux": { + "tarball": "https://ziglang.org/download/0.9.1/zig-linux-armv7a-0.9.1.tar.xz", + "shasum": "6de64456cb4757a555816611ea697f86fba7681d8da3e1863fa726a417de49be", + "size": "37974652" + }, + "i386-linux": { + "tarball": "https://ziglang.org/download/0.9.1/zig-linux-i386-0.9.1.tar.xz", + "shasum": "e776844fecd2e62fc40d94718891057a1dbca1816ff6013369e9a38c874374ca", + "size": "44969172" + }, + "riscv64-linux": { + "tarball": "https://ziglang.org/download/0.9.1/zig-linux-riscv64-0.9.1.tar.xz", + "shasum": "208dea53662c2c52777bd9e3076115d2126a4f71aed7f2ff3b8fe224dc3881aa", + "size": "39390868" + }, + "x86_64-linux": { + "tarball": "https://ziglang.org/download/0.9.1/zig-linux-x86_64-0.9.1.tar.xz", + "shasum": "be8da632c1d3273f766b69244d80669fe4f5e27798654681d77c992f17c237d7", + "size": "41011464" + }, + "aarch64-macos": { + "tarball": "https://ziglang.org/download/0.9.1/zig-macos-aarch64-0.9.1.tar.xz", + "shasum": "8c473082b4f0f819f1da05de2dbd0c1e891dff7d85d2c12b6ee876887d438287", + "size": "38995640" + }, + "x86_64-macos": { + "tarball": "https://ziglang.org/download/0.9.1/zig-macos-x86_64-0.9.1.tar.xz", + "shasum": "2d94984972d67292b55c1eb1c00de46580e9916575d083003546e9a01166754c", + "size": "43713044" + }, + "i386-windows": { + "tarball": "https://ziglang.org/download/0.9.1/zig-windows-i386-0.9.1.zip", + "shasum": "74a640ed459914b96bcc572183a8db687bed0af08c30d2ea2f8eba03ae930f69", + "size": "67929868" + }, + "x86_64-windows": { + "tarball": "https://ziglang.org/download/0.9.1/zig-windows-x86_64-0.9.1.zip", + "shasum": "443da53387d6ae8ba6bac4b3b90e9fef4ecbe545e1c5fa3a89485c36f5c0e3a2", + "size": "65047697" + }, + "aarch64-windows": { + "tarball": "https://ziglang.org/download/0.9.1/zig-windows-aarch64-0.9.1.zip", + "shasum": "621bf95f54dc3ff71466c5faae67479419951d7489e40e87fd26d195825fb842", + "size": "61478151" + } + }, "0.9.0": { "date": "2021-12-20", "docs": "https://ziglang.org/documentation/0.9.0/", diff --git a/doc/langref.html.in b/doc/langref.html.in index c0b8c9cb48..24b976ee42 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -316,7 +316,7 @@
      0.6.0 | 0.7.1 | 0.8.1 | - 0.9.0 | + 0.9.1 | master
    {#code_begin|exe|struct_name#} const std = @import("std"); diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index b6594f193c..1f76d90452 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -2188,7 +2188,7 @@ test "enum" { try expectFmt("enum: Enum.Two\n", "enum: {X}\n", .{Enum.Two}); // test very large enum to verify ct branch quota is large enough - try expectFmt("enum: Win32Error.INVALID_FUNCTION\n", "enum: {}\n", .{std.os.windows.Win32Error.INVALID_FUNCTION}); + try expectFmt("enum: os.windows.win32error.Win32Error.INVALID_FUNCTION\n", "enum: {}\n", .{std.os.windows.Win32Error.INVALID_FUNCTION}); } test "non-exhaustive enum" { diff --git a/src/stage1/astgen.cpp b/src/stage1/astgen.cpp index ee59aef04a..35566e2143 100644 --- a/src/stage1/astgen.cpp +++ b/src/stage1/astgen.cpp @@ -7680,11 +7680,14 @@ Buf *get_anon_type_name(CodeGen *codegen, Stage1Zir *exec, const char *kind_name if (!force_generic) { if (exec != nullptr && exec->name != nullptr) { - ZigType *import = get_scope_import(scope); + buf_resize(out_bare_name, 0); + if (scope->id == ScopeIdDecls) { + ScopeDecls *decls_scope = reinterpret_cast(scope); + append_namespace_qualification(codegen, out_bare_name, decls_scope->container_type); + } + buf_append_buf(out_bare_name, exec->name); Buf *namespace_name = buf_alloc(); - append_namespace_qualification(codegen, namespace_name, import); - buf_append_buf(namespace_name, exec->name); - buf_init_from_buf(out_bare_name, exec->name); + buf_append_buf(namespace_name, out_bare_name); return namespace_name; } if (exec != nullptr && exec->name_fn != nullptr) { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 22e1e527b9..3a84617222 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -267,7 +267,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ }); \\} , &[_][]const u8{ - "tmp.zig:3:31: error: expected type 'std.builtin.Type', found 'std.builtin.Int'", + "tmp.zig:3:31: error: expected type 'std.builtin.Type', found 'std.builtin.Type.Int'", }); ctx.objErrStage1("indexing a undefined slice at comptime", @@ -3461,7 +3461,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ _ = field; \\} , &[_][]const u8{ - "tmp.zig:9:51: error: values of type 'std.builtin.StructField' must be comptime known, but index value is runtime known", + "tmp.zig:9:51: error: values of type 'std.builtin.Type.StructField' must be comptime known, but index value is runtime known", }); ctx.objErrStage1("compile log statement inside function which must be comptime evaluated", From 19331b323d15b0fa0bae7add37a03528256a119f Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Sat, 19 Mar 2022 11:59:41 -0700 Subject: [PATCH 0823/2031] stage2: Correctly align decls for comptime allocs This updates WipAnonDecl to require an alignment provided by the caller, which is needed for explicitly aligned comptime allocs. --- src/Sema.zig | 46 ++++++++++++++++++++++++++++++++++------- test/behavior/align.zig | 15 ++++++++++++++ 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 05adc7cf79..28110efc83 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -486,15 +486,19 @@ pub const Block = struct { wad.* = undefined; } - pub fn finish(wad: *WipAnonDecl, ty: Type, val: Value) !*Decl { + pub fn finish(wad: *WipAnonDecl, ty: Type, val: Value, alignment: u32) !*Decl { const sema = wad.block.sema; // Do this ahead of time because `createAnonymousDecl` depends on calling // `type.hasRuntimeBits()`. _ = try sema.typeHasRuntimeBits(wad.block, wad.src, ty); + const align_val = if (alignment != 0) blk: { + break :blk try Value.Tag.int_u64.create(wad.arena(), alignment); + } else Value.@"null"; const new_decl = try sema.mod.createAnonymousDecl(wad.block, .{ .ty = ty, .val = val, }); + new_decl.align_val = align_val; errdefer sema.mod.abortAnonDecl(new_decl); try new_decl.finalizeNewArena(&wad.new_decl_arena); wad.finished = true; @@ -1644,6 +1648,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE iac.data.decl = try anon_decl.finish( try pointee_ty.copy(anon_decl.arena()), Value.undef, + iac.data.alignment, ); if (iac.data.alignment != 0) { try sema.resolveTypeLayout(block, src, pointee_ty); @@ -2711,6 +2716,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com const new_decl = try anon_decl.finish( try final_elem_ty.copy(anon_decl.arena()), try store_val.copy(anon_decl.arena()), + inferred_alloc.data.alignment, ); break :d new_decl; }; @@ -3503,8 +3509,8 @@ fn storeToInferredAllocComptime( iac.data.decl = try anon_decl.finish( try operand_ty.copy(anon_decl.arena()), try operand_val.copy(anon_decl.arena()), + iac.data.alignment, ); - // TODO set the alignment on the decl return; } else { return sema.failWithNeededComptime(block, src); @@ -3583,6 +3589,7 @@ fn addStrLit(sema: *Sema, block: *Block, zir_bytes: []const u8) CompileError!Air const new_decl = try anon_decl.finish( try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), + 0, // default alignment ); return sema.analyzeDeclRef(new_decl); @@ -8147,6 +8154,7 @@ fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A embed_file.owner_decl = try anon_decl.finish( try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), embed_file.bytes.len), try Value.Tag.bytes.create(anon_decl.arena(), bytes_including_null), + 0, // default alignment ); return sema.analyzeDeclRef(embed_file.owner_decl); @@ -8560,7 +8568,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .elem_type = try lhs_info.elem_type.copy(anon_decl.arena()), }); const val = try Value.Tag.aggregate.create(anon_decl.arena(), buf); - const decl = try anon_decl.finish(ty, val); + const decl = try anon_decl.finish(ty, val, 0); if (lhs_single_ptr or rhs_single_ptr) { return sema.analyzeDeclRef(decl); } else { @@ -8733,7 +8741,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } break :blk try Value.Tag.aggregate.create(anon_decl.arena(), buf); }; - const decl = try anon_decl.finish(final_ty, val); + const decl = try anon_decl.finish(final_ty, val, 0); if (is_single_ptr) { return sema.analyzeDeclRef(decl); } else { @@ -10384,6 +10392,7 @@ fn zirBuiltinSrc( const new_decl = try anon_decl.finish( try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len - 1), try Value.Tag.bytes.create(anon_decl.arena(), bytes), + 0, // default alignment ); break :blk try Value.Tag.decl_ref.create(sema.arena, new_decl); }; @@ -10395,6 +10404,7 @@ fn zirBuiltinSrc( const new_decl = try anon_decl.finish( try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), name.len), try Value.Tag.bytes.create(anon_decl.arena(), name[0 .. name.len + 1]), + 0, // default alignment ); break :blk try Value.Tag.decl_ref.create(sema.arena, new_decl); }; @@ -10547,6 +10557,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai params_anon_decl.arena(), param_vals, ), + 0, // default alignment ); break :v try Value.Tag.slice.create(sema.arena, .{ .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl), @@ -10736,6 +10747,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const new_decl = try anon_decl.finish( try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), + 0, // default alignment ); break :v try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl); }; @@ -10766,6 +10778,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai fields_anon_decl.arena(), vals, ), + 0, // default alignment ); const new_decl_val = try Value.Tag.decl_ref.create(sema.arena, new_decl); @@ -10844,6 +10857,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const new_decl = try anon_decl.finish( try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), + 0, // default alignment ); break :v try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl); }; @@ -10868,6 +10882,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai fields_anon_decl.arena(), enum_field_vals, ), + 0, // default alignment ); break :v try Value.Tag.decl_ref.create(sema.arena, new_decl); }; @@ -10936,6 +10951,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const new_decl = try anon_decl.finish( try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), + 0, // default alignment ); break :v try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl); }; @@ -10967,6 +10983,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai fields_anon_decl.arena(), try fields_anon_decl.arena().dupe(Value, union_field_vals), ), + 0, // default alignment ); break :v try Value.Tag.slice.create(sema.arena, .{ .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl), @@ -11044,6 +11061,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const new_decl = try anon_decl.finish( try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), + 0, // default alignment ); break :v try Value.Tag.slice.create(fields_anon_decl.arena(), .{ .ptr = try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl), @@ -11087,6 +11105,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const new_decl = try anon_decl.finish( try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), + 0, // default alignment ); break :v try Value.Tag.slice.create(fields_anon_decl.arena(), .{ .ptr = try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl), @@ -11132,6 +11151,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai fields_anon_decl.arena(), try fields_anon_decl.arena().dupe(Value, struct_field_vals), ), + 0, // default alignment ); break :v try Value.Tag.slice.create(sema.arena, .{ .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl), @@ -11225,6 +11245,7 @@ fn typeInfoDecls( const new_decl = try anon_decl.finish( try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), + 0, // default alignment ); break :v try Value.Tag.slice.create(decls_anon_decl.arena(), .{ .ptr = try Value.Tag.decl_ref.create(decls_anon_decl.arena(), new_decl), @@ -11251,6 +11272,7 @@ fn typeInfoDecls( decls_anon_decl.arena(), try decls_anon_decl.arena().dupe(Value, decls_vals), ), + 0, // default alignment ); return try Value.Tag.slice.create(sema.arena, .{ .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl), @@ -12350,6 +12372,7 @@ fn addConstantMaybeRef( const decl = try anon_decl.finish( try ty.copy(anon_decl.arena()), try val.copy(anon_decl.arena()), + 0, // default alignment ); return sema.analyzeDeclRef(decl); } @@ -13227,6 +13250,7 @@ fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const new_decl = try anon_decl.finish( try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), + 0, // default alignment ); return sema.analyzeDeclRef(new_decl); @@ -15851,6 +15875,7 @@ fn safetyPanic( break :msg_inst try sema.analyzeDeclRef(try anon_decl.finish( try Type.Tag.array_u8.create(anon_decl.arena(), msg.len), try Value.Tag.bytes.create(anon_decl.arena(), msg), + 0, // default alignment )); }; @@ -16080,6 +16105,7 @@ fn fieldPtr( return sema.analyzeDeclRef(try anon_decl.finish( Type.usize, try Value.Tag.int_u64.create(anon_decl.arena(), inner_ty.arrayLen()), + 0, // default alignment )); } else { return sema.fail( @@ -16107,6 +16133,7 @@ fn fieldPtr( return sema.analyzeDeclRef(try anon_decl.finish( try slice_ptr_ty.copy(anon_decl.arena()), try val.slicePtr().copy(anon_decl.arena()), + 0, // default alignment )); } try sema.requireRuntimeBlock(block, src); @@ -16126,6 +16153,7 @@ fn fieldPtr( return sema.analyzeDeclRef(try anon_decl.finish( Type.usize, try Value.Tag.int_u64.create(anon_decl.arena(), val.sliceLen()), + 0, // default alignment )); } try sema.requireRuntimeBlock(block, src); @@ -16175,6 +16203,7 @@ fn fieldPtr( return sema.analyzeDeclRef(try anon_decl.finish( try child_type.copy(anon_decl.arena()), try Value.Tag.@"error".create(anon_decl.arena(), .{ .name = name }), + 0, // default alignment )); }, .Union => { @@ -16191,6 +16220,7 @@ fn fieldPtr( return sema.analyzeDeclRef(try anon_decl.finish( try enum_ty.copy(anon_decl.arena()), try Value.Tag.enum_field_index.create(anon_decl.arena(), field_index_u32), + 0, // default alignment )); } } @@ -16211,6 +16241,7 @@ fn fieldPtr( return sema.analyzeDeclRef(try anon_decl.finish( try child_type.copy(anon_decl.arena()), try Value.Tag.enum_field_index.create(anon_decl.arena(), field_index_u32), + 0, // default alignment )); }, .Struct, .Opaque => { @@ -16522,10 +16553,8 @@ fn structFieldPtrByIndex( const decl = try anon_decl.finish( try field.ty.copy(anon_decl.arena()), try field.default_val.copy(anon_decl.arena()), + ptr_ty_data.@"align", ); - if (ptr_ty_data.@"align" != 0) { - decl.align_val = field.abi_align; - } return sema.analyzeDeclRef(decl); } @@ -19114,6 +19143,7 @@ fn refValue(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, val: Value) ! const decl = try anon_decl.finish( try ty.copy(anon_decl.arena()), try val.copy(anon_decl.arena()), + 0, // default alignment ); try sema.mod.declareDeclDependency(sema.owner_decl, decl); return try Value.Tag.decl_ref.create(sema.arena, decl); @@ -19170,6 +19200,7 @@ fn analyzeRef( return sema.analyzeDeclRef(try anon_decl.finish( try operand_ty.copy(anon_decl.arena()), try val.copy(anon_decl.arena()), + 0, // default alignment )); } @@ -21614,6 +21645,7 @@ fn analyzeComptimeAlloc( // sub-fields. So we need to initialize with undef to allow the mechanism to expand // into fields/elements and have those overridden with stored values. Value.undef, + alignment, ); decl.align_val = align_val; diff --git a/test/behavior/align.zig b/test/behavior/align.zig index ef5a790ead..3a35d1fcca 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -416,3 +416,18 @@ test "align(N) on functions" { fn overaligned_fn() align(0x1000) i32 { return 42; } + +test "comptime alloc alignment" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + comptime var bytes1 = [_]u8{0}; + _ = bytes1; + + comptime var bytes2 align(256) = [_]u8{0}; + var bytes2_addr = @ptrToInt(&bytes2); + try std.testing.expect(bytes2_addr & 0xff == 0); +} From b6203b89d6b41f17e44f7806f2244309fa3bd86c Mon Sep 17 00:00:00 2001 From: Daniele Cocca Date: Sun, 13 Mar 2022 21:37:36 +0000 Subject: [PATCH 0824/2031] CBE: implement mod, divFloor, divTrunc --- src/codegen/c.zig | 51 +++++++++++++++++++++++-- src/link/C/zig.h | 78 +++++++++++++++++++++++++++++++++++++++ test/behavior/int_div.zig | 1 - test/behavior/math.zig | 37 ++++++++++++++----- 4 files changed, 153 insertions(+), 14 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 88d26789cf..5c023c3dff 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1663,10 +1663,20 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .mul => try airBinOp (f, inst, " * "), // TODO use a different strategy for div that communicates to the optimizer // that wrapping is UB. - .div_float, .div_exact, .div_trunc => try airBinOp( f, inst, " / "), - .div_floor => try airBinOp( f, inst, " divfloor "), + .div_float, .div_exact => try airBinOp( f, inst, " / "), + .div_trunc => blk: { + const bin_op = f.air.instructions.items(.data)[inst].bin_op; + const lhs_ty = f.air.typeOf(bin_op.lhs); + // For binary operations @TypeOf(lhs)==@TypeOf(rhs), + // so we only check one. + break :blk if (lhs_ty.isInt()) + try airBinOp(f, inst, " / ") + else + try airBinOpBuiltinCall(f, inst, "div_trunc"); + }, + .div_floor => try airBinOpBuiltinCall(f, inst, "div_floor"), .rem => try airBinOp( f, inst, " % "), - .mod => try airBinOp( f, inst, " mod "), // TODO implement modulus division + .mod => try airBinOpBuiltinCall(f, inst, "mod"), .addwrap => try airWrapOp(f, inst, " + ", "addw_"), .subwrap => try airWrapOp(f, inst, " - ", "subw_"), @@ -3467,6 +3477,41 @@ fn airBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !C return local; } +fn airBinOpBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !CValue { + if (f.liveness.isUnused(inst)) return CValue.none; + + const inst_ty = f.air.typeOfIndex(inst); + const local = try f.allocLocal(inst_ty, .Const); + const bin_op = f.air.instructions.items(.data)[inst].bin_op; + const lhs_ty = f.air.typeOf(bin_op.lhs); + const target = f.object.dg.module.getTarget(); + const writer = f.object.writer(); + + // For binary operations @TypeOf(lhs)==@TypeOf(rhs), so we only check one. + if (lhs_ty.isInt()) { + const int_info = lhs_ty.intInfo(target); + const c_bits = toCIntBits(int_info.bits) orelse + return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); + const prefix_byte: u8 = switch (int_info.signedness) { + .signed => 'i', + .unsigned => 'u', + }; + try writer.print(" = zig_{s}_{c}{d}", .{ fn_name, prefix_byte, c_bits }); + } else if (lhs_ty.isRuntimeFloat()) { + const c_bits = lhs_ty.floatBits(target); + try writer.print(" = zig_{s}_f{d}", .{ fn_name, c_bits }); + } else { + return f.fail("TODO: C backend: implement airBinOpBuiltinCall for type {s}", .{@tagName(lhs_ty.tag())}); + } + + try writer.writeByte('('); + try f.writeCValue(writer, try f.resolveInst(bin_op.lhs)); + try writer.writeAll(", "); + try f.writeCValue(writer, try f.resolveInst(bin_op.rhs)); + try writer.writeAll(");\n"); + return local; +} + fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data; diff --git a/src/link/C/zig.h b/src/link/C/zig.h index 7259ba19ce..85c7856d2b 100644 --- a/src/link/C/zig.h +++ b/src/link/C/zig.h @@ -750,3 +750,81 @@ static inline uint128_t zig_bit_reverse_u128(uint128_t value, uint8_t zig_type_b } #define zig_bit_reverse_i128 zig_bit_reverse_u128 + +static inline float zig_div_truncf(float numerator, float denominator) { + return __builtin_truncf(numerator / denominator); +} + +static inline double zig_div_trunc(double numerator, double denominator) { + return __builtin_trunc(numerator / denominator); +} + +static inline long double zig_div_truncl(long double numerator, long double denominator) { + return __builtin_truncf(numerator / denominator); +} + +#define zig_div_trunc_f16 zig_div_truncf +#define zig_div_trunc_f32 zig_div_truncf +#define zig_div_trunc_f64 zig_div_trunc +#define zig_div_trunc_f80 zig_div_truncl +#define zig_div_trunc_f128 zig_div_truncl + +#define zig_div_floorf(numerator, denominator) \ + __builtin_floorf((float)(numerator) / (float)(denominator)) + +#define zig_div_floor(numerator, denominator) \ + __builtin_floor((double)(numerator) / (double)(denominator)) + +#define zig_div_floorl(numerator, denominator) \ + __builtin_floorl((long double)(numerator) / (long double)(denominator)) + +#define zig_div_floor_f16 zig_div_floorf +#define zig_div_floor_f32 zig_div_floorf +#define zig_div_floor_f64 zig_div_floor +#define zig_div_floor_f80 zig_div_floorl +#define zig_div_floor_f128 zig_div_floorl + +#define zig_div_floor_u8 zig_div_floorf +#define zig_div_floor_i8 zig_div_floorf +#define zig_div_floor_u16 zig_div_floorf +#define zig_div_floor_i16 zig_div_floorf +#define zig_div_floor_u32 zig_div_floor +#define zig_div_floor_i32 zig_div_floor +#define zig_div_floor_u64 zig_div_floor +#define zig_div_floor_i64 zig_div_floor +#define zig_div_floor_u128 zig_div_floorl +#define zig_div_floor_i128 zig_div_floorl + +static inline float zig_modf(float numerator, float denominator) { + return (numerator - (zig_div_floorf(numerator, denominator) * denominator)); +} + +static inline double zig_mod(double numerator, double denominator) { + return (numerator - (zig_div_floor(numerator, denominator) * denominator)); +} + +static inline long double zig_modl(long double numerator, long double denominator) { + return (numerator - (zig_div_floorl(numerator, denominator) * denominator)); +} + +#define zig_mod_f16 zig_modf +#define zig_mod_f32 zig_modf +#define zig_mod_f64 zig_mod +#define zig_mod_f80 zig_modl +#define zig_mod_f128 zig_modl + +#define zig_mod_int(ZigType, CType) \ + static inline CType zig_mod_##ZigType(CType numerator, CType denominator) { \ + return (numerator - (zig_div_floor_##ZigType(numerator, denominator) * denominator)); \ + } + +zig_mod_int( u8, uint8_t) +zig_mod_int( i8, int8_t) +zig_mod_int( u16, uint16_t) +zig_mod_int( i16, int16_t) +zig_mod_int( u32, uint32_t) +zig_mod_int( i32, int32_t) +zig_mod_int( u64, uint64_t) +zig_mod_int( i64, int64_t) +zig_mod_int(u128, uint128_t) +zig_mod_int(i128, int128_t) diff --git a/test/behavior/int_div.zig b/test/behavior/int_div.zig index 8ab8ad9e42..0c50ce8909 100644 --- a/test/behavior/int_div.zig +++ b/test/behavior/int_div.zig @@ -4,7 +4,6 @@ const expect = std.testing.expect; test "integer division" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 0d10113fb0..67f7fd101d 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -385,7 +385,6 @@ fn testBinaryNot(x: u16) !void { } test "division" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -394,22 +393,18 @@ test "division" { try testDivision(); comptime try testDivision(); } + fn testDivision() !void { try expect(div(u32, 13, 3) == 4); - try expect(div(f16, 1.0, 2.0) == 0.5); try expect(div(f32, 1.0, 2.0) == 0.5); try expect(divExact(u32, 55, 11) == 5); try expect(divExact(i32, -55, 11) == -5); - try expect(divExact(f16, 55.0, 11.0) == 5.0); - try expect(divExact(f16, -55.0, 11.0) == -5.0); try expect(divExact(f32, 55.0, 11.0) == 5.0); try expect(divExact(f32, -55.0, 11.0) == -5.0); try expect(divFloor(i32, 5, 3) == 1); try expect(divFloor(i32, -5, 3) == -2); - try expect(divFloor(f16, 5.0, 3.0) == 1.0); - try expect(divFloor(f16, -5.0, 3.0) == -2.0); try expect(divFloor(f32, 5.0, 3.0) == 1.0); try expect(divFloor(f32, -5.0, 3.0) == -2.0); try expect(divFloor(i32, -0x80000000, -2) == 0x40000000); @@ -424,10 +419,6 @@ fn testDivision() !void { try expect(divTrunc(i32, -5, 3) == -1); try expect(divTrunc(i32, 9, -10) == 0); try expect(divTrunc(i32, -9, 10) == 0); - try expect(divTrunc(f16, 5.0, 3.0) == 1.0); - try expect(divTrunc(f16, -5.0, 3.0) == -1.0); - try expect(divTrunc(f16, 9.0, -10.0) == 0.0); - try expect(divTrunc(f16, -9.0, 10.0) == 0.0); try expect(divTrunc(f32, 5.0, 3.0) == 1.0); try expect(divTrunc(f32, -5.0, 3.0) == -1.0); try expect(divTrunc(f32, 9.0, -10.0) == 0.0); @@ -468,6 +459,32 @@ fn testDivision() !void { ); } } + +test "division half-precision floats" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + try testDivisionFP16(); + comptime try testDivisionFP16(); +} + +fn testDivisionFP16() !void { + try expect(div(f16, 1.0, 2.0) == 0.5); + + try expect(divExact(f16, 55.0, 11.0) == 5.0); + try expect(divExact(f16, -55.0, 11.0) == -5.0); + + try expect(divFloor(f16, 5.0, 3.0) == 1.0); + try expect(divFloor(f16, -5.0, 3.0) == -2.0); + try expect(divTrunc(f16, 5.0, 3.0) == 1.0); + try expect(divTrunc(f16, -5.0, 3.0) == -1.0); + try expect(divTrunc(f16, 9.0, -10.0) == 0.0); + try expect(divTrunc(f16, -9.0, 10.0) == 0.0); +} + fn div(comptime T: type, a: T, b: T) T { return a / b; } From 6d73f89bf17e96349255b351ad0603644441947a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Mar 2022 00:33:56 -0700 Subject: [PATCH 0825/2031] stage2: disable default panic handler when linking -lc It's failing to compile std.os.dl_iterate_phdr correctly. --- lib/std/builtin.zig | 11 ++++++++++- lib/std/c/linux.zig | 6 +++++- src/Sema.zig | 4 ++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 4d69a624f9..7b66998dc1 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -751,9 +751,18 @@ else /// therefore must be kept in sync with the compiler implementation. pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn { @setCold(true); + // Until self-hosted catches up with stage1 language features, we have a simpler // default panic function: - if (builtin.zig_backend != .stage1 and builtin.zig_backend != .stage2_llvm) { + if ((builtin.zig_backend == .stage2_llvm and builtin.link_libc) or + builtin.zig_backend == .stage2_c or + builtin.zig_backend == .stage2_wasm or + builtin.zig_backend == .stage2_arm or + builtin.zig_backend == .stage2_aarch64 or + builtin.zig_backend == .stage2_x86_64 or + builtin.zig_backend == .stage2_x86 or + builtin.zig_backend == .stage2_riscv64) + { while (true) { @breakpoint(); } diff --git a/lib/std/c/linux.zig b/lib/std/c/linux.zig index a0f90f563b..5f96fe3fe0 100644 --- a/lib/std/c/linux.zig +++ b/lib/std/c/linux.zig @@ -263,7 +263,11 @@ pub extern "c" fn inotify_rm_watch(fd: fd_t, wd: c_int) c_int; /// See std.elf for constants for this pub extern "c" fn getauxval(__type: c_ulong) c_ulong; -pub const dl_iterate_phdr_callback = fn (info: *dl_phdr_info, size: usize, data: ?*anyopaque) callconv(.C) c_int; +pub const dl_iterate_phdr_callback = switch (builtin.zig_backend) { + .stage1 => fn (info: *dl_phdr_info, size: usize, data: ?*anyopaque) callconv(.C) c_int, + else => *const fn (info: *dl_phdr_info, size: usize, data: ?*anyopaque) callconv(.C) c_int, +}; + pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*anyopaque) c_int; pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; diff --git a/src/Sema.zig b/src/Sema.zig index 28110efc83..8ac994a543 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6231,6 +6231,10 @@ fn funcCommon( comptime_params[i] = param.is_comptime or try sema.typeRequiresComptime(block, param_src, param.ty); is_generic = is_generic or comptime_params[i] or param.ty.tag() == .generic_poison; + if (is_extern and is_generic) { + // TODO add note: function is generic because of this parameter + return sema.fail(block, param_src, "extern function cannot be generic", .{}); + } } is_generic = is_generic or From 3ef34feaeb3a18926bead1981e5ce577382da38e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Mar 2022 00:34:40 -0700 Subject: [PATCH 0826/2031] tools: fix gdb pretty printers needed after merging 5c3325588ef4e85e85cb201ad9328fe26bbb8dca. --- tools/stage2_gdb_pretty_printers.py | 140 ++++++++++++++-------------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/tools/stage2_gdb_pretty_printers.py b/tools/stage2_gdb_pretty_printers.py index db91fc172d..bd64916536 100644 --- a/tools/stage2_gdb_pretty_printers.py +++ b/tools/stage2_gdb_pretty_printers.py @@ -9,47 +9,47 @@ class TypePrinter: # Keep in sync with src/type.zig # Types which have no payload do not need to be entered here. payload_type_names = { - 'array_u8': 'type.Len', - 'array_u8_sentinel_0': 'Len', + 'array_u8': 'Type.Payload.Len', + 'array_u8_sentinel_0': 'Type.Payload.Len', - 'single_const_pointer': 'ElemType', - 'single_mut_pointer': 'ElemType', - 'many_const_pointer': 'ElemType', - 'many_mut_pointer': 'ElemType', - 'c_const_pointer': 'ElemType', - 'c_mut_pointer': 'ElemType', - 'const_slice': 'ElemType', - 'mut_slice': 'ElemType', - 'optional': 'ElemType', - 'optional_single_mut_pointer': 'ElemType', - 'optional_single_const_pointer': 'ElemType', - 'anyframe_T': 'ElemType', + 'single_const_pointer': 'Type.Payload.ElemType', + 'single_mut_pointer': 'Type.Payload.ElemType', + 'many_const_pointer': 'Type.Payload.ElemType', + 'many_mut_pointer': 'Type.Payload.ElemType', + 'c_const_pointer': 'Type.Payload.ElemType', + 'c_mut_pointer': 'Type.Payload.ElemType', + 'const_slice': 'Type.Payload.ElemType', + 'mut_slice': 'Type.Payload.ElemType', + 'optional': 'Type.Payload.ElemType', + 'optional_single_mut_pointer': 'Type.Payload.ElemType', + 'optional_single_const_pointer': 'Type.Payload.ElemType', + 'anyframe_T': 'Type.Payload.ElemType', - 'int_signed': 'Bits', - 'int_unsigned': 'Bits', + 'int_signed': 'Type.Payload.Bits', + 'int_unsigned': 'Type.Payload.Bits', - 'error_set': 'ErrorSet', - 'error_set_inferred': 'ErrorSetInferred', - 'error_set_merged': 'ErrorSetMerged', + 'error_set': 'Type.Payload.ErrorSet', + 'error_set_inferred': 'Type.Payload.ErrorSetInferred', + 'error_set_merged': 'Type.Payload.ErrorSetMerged', - 'array': 'Array', - 'vector': 'Array', + 'array': 'Type.Payload.Array', + 'vector': 'Type.Payload.Array', - 'array_sentinel': 'ArraySentinel', - 'pointer': 'Pointer', - 'function': 'Function', - 'error_union': 'ErrorUnion', - 'error_set_single': 'Name', - 'opaque': 'Opaque', - 'struct': 'Struct', - 'union': 'Union', - 'union_tagged': 'Union', - 'enum_full, .enum_nonexhaustive': 'EnumFull', - 'enum_simple': 'EnumSimple', - 'enum_numbered': 'EnumNumbered', - 'empty_struct': 'ContainerScope', - 'tuple': 'Tuple', - 'anon_struct': 'AnonStruct', + 'array_sentinel': 'Type.Payload.ArraySentinel', + 'pointer': 'Type.Payload.Pointer', + 'function': 'Type.Payload.Function', + 'error_union': 'Type.Payload.ErrorUnion', + 'error_set_single': 'Type.Payload.Name', + 'opaque': 'Type.Payload.Opaque', + 'struct': 'Type.Payload.Struct', + 'union': 'Type.Payload.Union', + 'union_tagged': 'Type.Payload.Union', + 'enum_full, .enum_nonexhaustive': 'Type.Payload.EnumFull', + 'enum_simple': 'Type.Payload.EnumSimple', + 'enum_numbered': 'Type.Payload.EnumNumbered', + 'empty_struct': 'Type.Payload.ContainerScope', + 'tuple': 'Type.Payload.Tuple', + 'anon_struct': 'Type.Payload.AnonStruct', } def __init__(self, val): @@ -98,48 +98,48 @@ class ValuePrinter: # Keep in sync with src/value.zig # Values which have no payload do not need to be entered here. payload_type_names = { - 'big_int_positive': 'BigInt', - 'big_int_negative': 'BigInt', + 'big_int_positive': 'Value.Payload.BigInt', + 'big_int_negative': 'Value.Payload.BigInt', - 'extern_fn': 'ExternFn', + 'extern_fn': 'Value.Payload.ExternFn', - 'decl_ref': 'Decl', + 'decl_ref': 'Value.Payload.Decl', - 'repeated': 'SubValue', - 'eu_payload': 'SubValue', - 'opt_payload': 'SubValue', - 'empty_array_sentinel': 'SubValue', + 'repeated': 'Value.Payload.SubValue', + 'eu_payload': 'Value.Payload.SubValue', + 'opt_payload': 'Value.Payload.SubValue', + 'empty_array_sentinel': 'Value.Payload.SubValue', - 'eu_payload_ptr': 'PayloadPtr', - 'opt_payload_ptr': 'PayloadPtr', + 'eu_payload_ptr': 'Value.Payload.PayloadPtr', + 'opt_payload_ptr': 'Value.Payload.PayloadPtr', - 'bytes': 'Bytes', - 'enum_literal': 'Bytes', + 'bytes': 'Value.Payload.Bytes', + 'enum_literal': 'Value.Payload.Bytes', - 'slice': 'Slice', + 'slice': 'Value.Payload.Slice', - 'enum_field_index': 'U32', + 'enum_field_index': 'Value.Payload.U32', - 'ty': 'Ty', - 'int_type': 'IntType', - 'int_u64': 'U64', - 'int_i64': 'I64', - 'function': 'Function', - 'variable': 'Variable', - 'decl_ref_mut': 'DeclRefMut', - 'elem_ptr': 'ElemPtr', - 'field_ptr': 'FieldPtr', - 'float_16': 'Float_16', - 'float_32': 'Float_32', - 'float_64': 'Float_64', - 'float_80': 'Float_80', - 'float_128': 'Float_128', - 'error': 'Error', - 'inferred_alloc': 'InferredAlloc', - 'inferred_alloc_comptime': 'InferredAllocComptime', - 'aggregate': 'Aggregate', - 'union': 'Union', - 'bound_fn': 'BoundFn', + 'ty': 'Value.Payload.Ty', + 'int_type': 'Value.Payload.IntType', + 'int_u64': 'Value.Payload.U64', + 'int_i64': 'Value.Payload.I64', + 'function': 'Value.Payload.Function', + 'variable': 'Value.Payload.Variable', + 'decl_ref_mut': 'Value.Payload.DeclRefMut', + 'elem_ptr': 'Value.Payload.ElemPtr', + 'field_ptr': 'Value.Payload.FieldPtr', + 'float_16': 'Value.Payload.Float_16', + 'float_32': 'Value.Payload.Float_32', + 'float_64': 'Value.Payload.Float_64', + 'float_80': 'Value.Payload.Float_80', + 'float_128': 'Value.Payload.Float_128', + 'error': 'Value.Payload.Error', + 'inferred_alloc': 'Value.Payload.InferredAlloc', + 'inferred_alloc_comptime': 'Value.Payload.InferredAllocComptime', + 'aggregate': 'Value.Payload.Aggregate', + 'union': 'Value.Payload.Union', + 'bound_fn': 'Value.Payload.BoundFn', } def __init__(self, val): From 0576086395774389a9f38d960f9ed5102a813bdb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Mar 2022 00:35:09 -0700 Subject: [PATCH 0827/2031] stage2: remove Value.Tag.abi_align_default and make Decl alignment & linksection, and struct & union field alignment be scalar values, not Value values. YAGNI --- src/Module.zig | 70 ++++++++++++++++++++++------------------- src/Sema.zig | 57 +++++++++++++-------------------- src/arch/x86_64/abi.zig | 10 +++--- src/codegen/c.zig | 48 ++++++++++------------------ src/value.zig | 4 --- 5 files changed, 79 insertions(+), 110 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 6dfc5f3528..d537a4cf5a 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -349,12 +349,13 @@ pub const Decl = struct { /// Populated when `has_tv`. val: Value, /// Populated when `has_tv`. - align_val: Value, + /// Points to memory inside value_arena. + @"linksection": ?[*:0]const u8, /// Populated when `has_tv`. - linksection_val: Value, + @"align": u32, /// Populated when `has_tv`. @"addrspace": std.builtin.AddressSpace, - /// The memory for ty, val, align_val, linksection_val, and captures. + /// The memory for ty, val, align, linksection, and captures. /// If this is `null` then there is no memory management needed. value_arena: ?*std.heap.ArenaAllocator.State = null, /// The direct parent namespace of the Decl. @@ -423,7 +424,7 @@ pub const Decl = struct { /// to require re-analysis. outdated, }, - /// Whether `typed_value`, `align_val`, `linksection_val` and `addrspace` are populated. + /// Whether `typed_value`, `align`, `linksection` and `addrspace` are populated. has_tv: bool, /// If `true` it means the `Decl` is the resource owner of the type/value associated /// with it. That means when `Decl` is destroyed, the cleanup code should additionally @@ -794,9 +795,9 @@ pub const Decl = struct { pub fn getAlignment(decl: Decl, target: Target) u32 { assert(decl.has_tv); - if (decl.align_val.tag() != .null_value) { + if (decl.@"align" != 0) { // Explicit alignment. - return @intCast(u32, decl.align_val.toUnsignedInt()); + return decl.@"align"; } else { // Natural alignment. return decl.ty.abiAlignment(target); @@ -893,9 +894,10 @@ pub const Struct = struct { /// Uses `noreturn` to indicate `anytype`. /// undefined until `status` is `have_field_types` or `have_layout`. ty: Type, - abi_align: Value, /// Uses `unreachable_value` to indicate no default. default_val: Value, + /// Zero means to use the ABI alignment of the type. + abi_align: u32, /// undefined until `status` is `have_layout`. offset: u32, /// If true then `default_val` is the comptime field value. @@ -903,10 +905,10 @@ pub const Struct = struct { /// Returns the field alignment, assuming the struct is not packed. pub fn normalAlignment(field: Field, target: Target) u32 { - if (field.abi_align.tag() == .abi_align_default) { + if (field.abi_align == 0) { return field.ty.abiAlignment(target); } else { - return @intCast(u32, field.abi_align.toUnsignedInt()); + return field.abi_align; } } }; @@ -1139,16 +1141,17 @@ pub const Union = struct { pub const Field = struct { /// undefined until `status` is `have_field_types` or `have_layout`. ty: Type, - abi_align: Value, + /// 0 means the ABI alignment of the type. + abi_align: u32, /// Returns the field alignment, assuming the union is not packed. /// Keep implementation in sync with `Sema.unionFieldAlignment`. /// Prefer to call that function instead of this one during Sema. pub fn normalAlignment(field: Field, target: Target) u32 { - if (field.abi_align.tag() == .abi_align_default) { + if (field.abi_align == 0) { return field.ty.abiAlignment(target); } else { - return @intCast(u32, field.abi_align.toUnsignedInt()); + return field.abi_align; } } }; @@ -1224,7 +1227,7 @@ pub const Union = struct { if (!field.ty.hasRuntimeBits()) continue; const field_align = a: { - if (field.abi_align.tag() == .abi_align_default) { + if (field.abi_align == 0) { break :a field.ty.abiAlignment(target); } else { break :a @intCast(u32, field.abi_align.toUnsignedInt()); @@ -1246,10 +1249,10 @@ pub const Union = struct { if (!field.ty.hasRuntimeBits()) continue; const field_align = a: { - if (field.abi_align.tag() == .abi_align_default) { + if (field.abi_align == 0) { break :a field.ty.abiAlignment(target); } else { - break :a @intCast(u32, field.abi_align.toUnsignedInt()); + break :a field.abi_align; } }; max_align = @maximum(max_align, field_align); @@ -1300,10 +1303,10 @@ pub const Union = struct { if (!field.ty.hasRuntimeBitsIgnoreComptime()) continue; const field_align = a: { - if (field.abi_align.tag() == .abi_align_default) { + if (field.abi_align == 0) { break :a field.ty.abiAlignment(target); } else { - break :a @intCast(u32, field.abi_align.toUnsignedInt()); + break :a field.abi_align; } }; const field_size = field.ty.abiSize(target); @@ -3863,15 +3866,16 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { try wip_captures.finalize(); const src: LazySrcLoc = .{ .node_offset = 0 }; const decl_tv = try sema.resolveInstValue(&block_scope, src, result_ref); - const align_val = blk: { + const decl_align: u16 = blk: { const align_ref = decl.zirAlignRef(); - if (align_ref == .none) break :blk Value.initTag(.null_value); - break :blk (try sema.resolveInstConst(&block_scope, src, align_ref)).val; + if (align_ref == .none) break :blk 0; + break :blk try sema.resolveAlign(&block_scope, src, align_ref); }; - const linksection_val = blk: { + const decl_linksection: ?[*:0]const u8 = blk: { const linksection_ref = decl.zirLinksectionRef(); - if (linksection_ref == .none) break :blk Value.initTag(.null_value); - break :blk (try sema.resolveInstConst(&block_scope, src, linksection_ref)).val; + if (linksection_ref == .none) break :blk null; + const bytes = try sema.resolveConstString(&block_scope, src, linksection_ref); + break :blk (try decl_arena_allocator.dupeZ(u8, bytes)).ptr; }; const address_space = blk: { const addrspace_ctx: Sema.AddressSpaceContext = switch (decl_tv.val.tag()) { @@ -3911,8 +3915,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { decl.ty = ty_ty; decl.val = try Value.Tag.ty.create(decl_arena_allocator, ty); - decl.align_val = Value.initTag(.null_value); - decl.linksection_val = Value.initTag(.null_value); + decl.@"align" = 0; + decl.@"linksection" = null; decl.has_tv = true; decl.owns_tv = false; decl_arena_state.* = decl_arena.state; @@ -3942,8 +3946,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { decl.ty = try decl_tv.ty.copy(decl_arena_allocator); decl.val = try decl_tv.val.copy(decl_arena_allocator); - decl.align_val = try align_val.copy(decl_arena_allocator); - decl.linksection_val = try linksection_val.copy(decl_arena_allocator); + decl.@"align" = decl_align; + decl.@"linksection" = decl_linksection; decl.@"addrspace" = address_space; decl.has_tv = true; decl.owns_tv = owns_tv; @@ -4022,8 +4026,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { decl.ty = try decl_tv.ty.copy(decl_arena_allocator); decl.val = try decl_tv.val.copy(decl_arena_allocator); - decl.align_val = try align_val.copy(decl_arena_allocator); - decl.linksection_val = try linksection_val.copy(decl_arena_allocator); + decl.@"align" = decl_align; + decl.@"linksection" = decl_linksection; decl.@"addrspace" = address_space; decl.has_tv = true; decl_arena_state.* = decl_arena.state; @@ -4889,8 +4893,8 @@ pub fn allocateNewDecl( .owns_tv = false, .ty = undefined, .val = undefined, - .align_val = undefined, - .linksection_val = undefined, + .@"align" = undefined, + .@"linksection" = undefined, .@"addrspace" = .generic, .analysis = .unreferenced, .deletion_flag = false, @@ -4995,8 +4999,8 @@ pub fn createAnonymousDeclFromDeclNamed( new_decl.src_line = src_decl.src_line; new_decl.ty = typed_value.ty; new_decl.val = typed_value.val; - new_decl.align_val = Value.@"null"; - new_decl.linksection_val = Value.@"null"; + new_decl.@"align" = 0; + new_decl.@"linksection" = null; new_decl.has_tv = true; new_decl.analysis = .complete; new_decl.generation = mod.generation; diff --git a/src/Sema.zig b/src/Sema.zig index 8ac994a543..eb14542a97 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -486,19 +486,17 @@ pub const Block = struct { wad.* = undefined; } + /// `alignment` value of 0 means to use ABI alignment. pub fn finish(wad: *WipAnonDecl, ty: Type, val: Value, alignment: u32) !*Decl { const sema = wad.block.sema; // Do this ahead of time because `createAnonymousDecl` depends on calling // `type.hasRuntimeBits()`. _ = try sema.typeHasRuntimeBits(wad.block, wad.src, ty); - const align_val = if (alignment != 0) blk: { - break :blk try Value.Tag.int_u64.create(wad.arena(), alignment); - } else Value.@"null"; const new_decl = try sema.mod.createAnonymousDecl(wad.block, .{ .ty = ty, .val = val, }); - new_decl.align_val = align_val; + new_decl.@"align" = alignment; errdefer sema.mod.abortAnonDecl(new_decl); try new_decl.finalizeNewArena(&wad.new_decl_arena); wad.finished = true; @@ -1281,7 +1279,7 @@ fn resolveConstBool( return val.toBool(); } -fn resolveConstString( +pub fn resolveConstString( sema: *Sema, block: *Block, src: LazySrcLoc, @@ -1539,7 +1537,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, block: *Block, err_msg: *Module.ErrorMsg) return error.AnalysisFail; } -fn resolveAlign( +pub fn resolveAlign( sema: *Sema, block: *Block, src: LazySrcLoc, @@ -13061,7 +13059,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I var buffer: Value.ToTypeBuffer = undefined; gop.value_ptr.* = .{ .ty = try field_type_val.toType(&buffer).copy(new_decl_arena_allocator), - .abi_align = try alignment_val.copy(new_decl_arena_allocator), + .abi_align = @intCast(u32, alignment_val.toUnsignedInt()), }; } } @@ -13230,7 +13228,7 @@ fn reifyStruct( var buffer: Value.ToTypeBuffer = undefined; gop.value_ptr.* = .{ .ty = try field_type_val.toType(&buffer).copy(new_decl_arena_allocator), - .abi_align = try alignment_val.copy(new_decl_arena_allocator), + .abi_align = @intCast(u32, alignment_val.toUnsignedInt()), .default_val = default_val, .is_comptime = is_comptime_val.toBool(), .offset = undefined, @@ -14949,9 +14947,9 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr }; if (struct_obj.layout == .Packed) { - // TODO handle packed structs - } else if (field.abi_align.tag() != .abi_align_default) { - ptr_ty_data.@"align" = @intCast(u32, field.abi_align.toUnsignedInt()); + return sema.fail(block, src, "TODO handle packed structs with @fieldParentPtr", .{}); + } else { + ptr_ty_data.@"align" = field.abi_align; } const target = sema.mod.getTarget(); @@ -15552,8 +15550,8 @@ fn zirBuiltinExtern( new_decl.src_line = sema.owner_decl.src_line; new_decl.ty = try ty.copy(new_decl_arena_allocator); new_decl.val = try Value.Tag.variable.create(new_decl_arena_allocator, new_var); - new_decl.align_val = Value.@"null"; - new_decl.linksection_val = Value.@"null"; + new_decl.@"align" = 0; + new_decl.@"linksection" = null; new_decl.has_tv = true; new_decl.analysis = .complete; new_decl.generation = sema.mod.generation; @@ -16543,9 +16541,7 @@ fn structFieldPtrByIndex( ptr_ty_data.bit_offset += struct_ptr_ty_info.bit_offset; } } else { - if (field.abi_align.tag() != .abi_align_default) { - ptr_ty_data.@"align" = @intCast(u32, field.abi_align.toUnsignedInt()); - } + ptr_ty_data.@"align" = field.abi_align; } const target = sema.mod.getTarget(); @@ -19168,15 +19164,11 @@ fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref { const decl_tv = try decl.typedValue(); if (decl_tv.val.castTag(.variable)) |payload| { const variable = payload.data; - const alignment: u32 = if (decl.align_val.tag() == .null_value) - 0 - else - @intCast(u32, decl.align_val.toUnsignedInt()); const ty = try Type.ptr(sema.arena, target, .{ .pointee_type = decl_tv.ty, .mutable = variable.is_mutable, .@"addrspace" = decl.@"addrspace", - .@"align" = alignment, + .@"align" = decl.@"align", }); return sema.addConstant(ty, try Value.Tag.decl_ref.create(sema.arena, decl)); } @@ -20848,7 +20840,7 @@ fn semaStructFields( assert(!gop.found_existing); gop.value_ptr.* = .{ .ty = try field_ty.copy(decl_arena_allocator), - .abi_align = Value.initTag(.abi_align_default), + .abi_align = 0, .default_val = Value.initTag(.unreachable_value), .is_comptime = is_comptime, .offset = undefined, @@ -20860,8 +20852,7 @@ fn semaStructFields( // TODO: if we need to report an error here, use a source location // that points to this alignment expression rather than the struct. // But only resolve the source location if we need to emit a compile error. - const abi_align_val = (try sema.resolveInstConst(&block_scope, src, align_ref)).val; - gop.value_ptr.abi_align = try abi_align_val.copy(decl_arena_allocator); + gop.value_ptr.abi_align = try sema.resolveAlign(&block_scope, src, align_ref); } if (has_default) { const default_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); @@ -21088,17 +21079,16 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { assert(!gop.found_existing); gop.value_ptr.* = .{ .ty = try field_ty.copy(decl_arena_allocator), - .abi_align = Value.initTag(.abi_align_default), + .abi_align = 0, }; if (align_ref != .none) { // TODO: if we need to report an error here, use a source location // that points to this alignment expression rather than the struct. // But only resolve the source location if we need to emit a compile error. - const abi_align_val = (try sema.resolveInstConst(&block_scope, src, align_ref)).val; - gop.value_ptr.abi_align = try abi_align_val.copy(decl_arena_allocator); + gop.value_ptr.abi_align = try sema.resolveAlign(&block_scope, src, align_ref); } else { - gop.value_ptr.abi_align = Value.initTag(.abi_align_default); + gop.value_ptr.abi_align = 0; } } } @@ -21638,11 +21628,6 @@ fn analyzeComptimeAlloc( var anon_decl = try block.startAnonDecl(src); defer anon_decl.deinit(); - const align_val = if (alignment == 0) - Value.@"null" - else - try Value.Tag.int_u64.create(anon_decl.arena(), alignment); - const decl = try anon_decl.finish( try var_type.copy(anon_decl.arena()), // There will be stores before the first load, but they may be to sub-elements or @@ -21651,7 +21636,7 @@ fn analyzeComptimeAlloc( Value.undef, alignment, ); - decl.align_val = align_val; + decl.@"align" = alignment; try sema.mod.declareDeclDependency(sema.owner_decl, decl); return sema.addConstant(ptr_type, try Value.Tag.decl_ref_mut.create(sema.arena, .{ @@ -22066,10 +22051,10 @@ fn unionFieldAlignment( src: LazySrcLoc, field: Module.Union.Field, ) !u32 { - if (field.abi_align.tag() == .abi_align_default) { + if (field.abi_align == 0) { return sema.typeAbiAlignment(block, src, field.ty); } else { - return @intCast(u32, field.abi_align.toUnsignedInt()); + return field.abi_align; } } diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index 5e9bd6e071..9fd09ab60a 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -179,9 +179,8 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class { var byte_i: usize = 0; // out of 8 const fields = ty.structFields(); for (fields.values()) |field| { - if (field.abi_align.tag() != .abi_align_default) { - const field_alignment = field.abi_align.toUnsignedInt(); - if (field_alignment < field.ty.abiAlignment(target)) { + if (field.abi_align != 0) { + if (field.abi_align < field.ty.abiAlignment(target)) { return memory_class; } } @@ -288,9 +287,8 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class { const fields = ty.unionFields(); for (fields.values()) |field| { - if (field.abi_align.tag() != .abi_align_default) { - const field_alignment = field.abi_align.toUnsignedInt(); - if (field_alignment < field.ty.abiAlignment(target)) { + if (field.abi_align != 0) { + if (field.abi_align < field.ty.abiAlignment(target)) { return memory_class; } } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 5c023c3dff..23ccdc007b 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -245,7 +245,7 @@ pub const Function = struct { ty, decl_c_value, .Const, - Value.initTag(.abi_align_default), + 0, ); try writer.writeAll(" = "); try f.object.dg.renderValue(writer, ty, val); @@ -267,10 +267,10 @@ pub const Function = struct { } fn allocLocal(f: *Function, ty: Type, mutability: Mutability) !CValue { - return f.allocAlignedLocal(ty, mutability, Value.initTag(.abi_align_default)); + return f.allocAlignedLocal(ty, mutability, 0); } - fn allocAlignedLocal(f: *Function, ty: Type, mutability: Mutability, alignment: Value) !CValue { + fn allocAlignedLocal(f: *Function, ty: Type, mutability: Mutability, alignment: u32) !CValue { const local_value = f.allocLocalValue(); try f.object.dg.renderTypeAndName( f.object.writer(), @@ -854,8 +854,7 @@ pub const DeclGen = struct { try w.writeAll(", "); } const name = CValue{ .arg = index }; - const alignment = Value.initTag(.abi_align_default); - try dg.renderTypeAndName(w, dg.decl.ty.fnParamType(index), name, .Mut, alignment); + try dg.renderTypeAndName(w, dg.decl.ty.fnParamType(index), name, .Mut, 0); params_written += 1; } @@ -928,8 +927,7 @@ pub const DeclGen = struct { var ptr_type_buf: Type.SlicePtrFieldTypeBuffer = undefined; const ptr_type = t.slicePtrFieldType(&ptr_type_buf); const ptr_name = CValue{ .bytes = "ptr" }; - const ptr_alignment = Value.initTag(.abi_align_default); - try dg.renderTypeAndName(bw, ptr_type, ptr_name, .Mut, ptr_alignment); + try dg.renderTypeAndName(bw, ptr_type, ptr_name, .Mut, 0); const ptr_sentinel = ptr_type.ptrInfo().data.sentinel; const child_type = t.childType(); @@ -1018,7 +1016,7 @@ pub const DeclGen = struct { try name.writer().print("field_{d}", .{i}); try buffer.append(' '); - try dg.renderTypeAndName(writer, field_ty, .{ .bytes = name.items }, .Mut, Value.initTag(.abi_align_default)); + try dg.renderTypeAndName(writer, field_ty, .{ .bytes = name.items }, .Mut, 0); try buffer.appendSlice(";\n"); } } @@ -1056,7 +1054,7 @@ pub const DeclGen = struct { const name: CValue = .{ .bytes = "tag" }; try buffer.appendSlice("struct {\n "); if (layout.tag_size != 0) { - try dg.renderTypeAndName(buffer.writer(), tag_ty, name, .Mut, Value.initTag(.abi_align_default)); + try dg.renderTypeAndName(buffer.writer(), tag_ty, name, .Mut, 0); try buffer.appendSlice(";\n"); } } @@ -1106,8 +1104,7 @@ pub const DeclGen = struct { try bw.writeAll("typedef struct { "); const payload_name = CValue{ .bytes = "payload" }; - const alignment = Value.initTag(.abi_align_default); - try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, alignment); + try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, 0); try bw.writeAll("; uint16_t error; } "); const name_index = buffer.items.len; if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| { @@ -1172,8 +1169,7 @@ pub const DeclGen = struct { try bw.writeAll("typedef struct { "); const payload_name = CValue{ .bytes = "payload" }; - const alignment = Value.initTag(.abi_align_default); - try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, alignment); + try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, 0); try bw.writeAll("; bool is_null; } "); const name_index = buffer.items.len; try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type)}); @@ -1375,12 +1371,9 @@ pub const DeclGen = struct { dg: *DeclGen, w: anytype, ty: Type, - //mutability: Mutability, - //alignment: Value, ) error{ OutOfMemory, AnalysisFail }!void { const name = CValue{ .bytes = "" }; - const alignment = Value.initTag(.abi_align_default); - return renderTypeAndName(dg, w, ty, name, .Mut, alignment); + return renderTypeAndName(dg, w, ty, name, .Mut, 0); } /// Renders a type and name in field declaration/definition format. @@ -1398,7 +1391,7 @@ pub const DeclGen = struct { ty: Type, name: CValue, mutability: Mutability, - alignment: Value, + alignment: u32, ) error{ OutOfMemory, AnalysisFail }!void { var suffix = std.ArrayList(u8).init(dg.gpa); defer suffix.deinit(); @@ -1413,8 +1406,8 @@ pub const DeclGen = struct { render_ty = render_ty.elemType(); } - if (alignment.tag() != .abi_align_default and alignment.tag() != .null_value) - try w.print("ZIG_ALIGN({}) ", .{alignment.toUnsignedInt()}); + if (alignment != 0) + try w.print("ZIG_ALIGN({}) ", .{alignment}); try dg.renderType(w, render_ty); const const_prefix = switch (mutability) { @@ -1570,7 +1563,7 @@ pub fn genDecl(o: *Object) !void { const decl_c_value: CValue = if (is_global) .{ .bytes = mem.span(o.dg.decl.name) } else .{ .decl = o.dg.decl }; - try o.dg.renderTypeAndName(fwd_decl_writer, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.align_val); + try o.dg.renderTypeAndName(fwd_decl_writer, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align"); try fwd_decl_writer.writeAll(";\n"); if (variable.init.isUndefDeep()) { @@ -1579,7 +1572,7 @@ pub fn genDecl(o: *Object) !void { try o.indent_writer.insertNewline(); const w = o.writer(); - try o.dg.renderTypeAndName(w, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.align_val); + try o.dg.renderTypeAndName(w, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align"); try w.writeAll(" = "); if (variable.init.tag() != .unreachable_value) { try o.dg.renderValue(w, tv.ty, variable.init); @@ -1594,7 +1587,7 @@ pub fn genDecl(o: *Object) !void { // https://github.com/ziglang/zig/issues/7582 const decl_c_value: CValue = .{ .decl = o.dg.decl }; - try o.dg.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut, o.dg.decl.align_val); + try o.dg.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut, o.dg.decl.@"align"); try writer.writeAll(" = "); try o.dg.renderValue(writer, tv.ty, tv.val); @@ -1993,15 +1986,8 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { } const target = f.object.dg.module.getTarget(); - const alignment = inst_ty.ptrAlignment(target); - var payload = Value.Payload.U64{ - .base = .{ .tag = .int_u64 }, - .data = alignment, - }; - const alignment_value = Value.initPayload(&payload.base); - // First line: the variable used as data storage. - const local = try f.allocAlignedLocal(elem_type, mutability, alignment_value); + const local = try f.allocAlignedLocal(elem_type, mutability, inst_ty.ptrAlignment(target)); try writer.writeAll(";\n"); return CValue{ .local_ref = local.local }; diff --git a/src/value.zig b/src/value.zig index 8e6afccb60..269b13b099 100644 --- a/src/value.zig +++ b/src/value.zig @@ -98,7 +98,6 @@ pub const Value = extern union { bool_false, generic_poison, - abi_align_default, empty_struct_value, empty_array, // See last_no_payload_tag below. // After this, the tag requires a payload. @@ -241,7 +240,6 @@ pub const Value = extern union { .null_value, .bool_true, .bool_false, - .abi_align_default, .manyptr_u8_type, .manyptr_const_u8_type, .manyptr_const_u8_sentinel_0_type, @@ -437,7 +435,6 @@ pub const Value = extern union { .bool_true, .bool_false, .empty_struct_value, - .abi_align_default, .manyptr_u8_type, .manyptr_const_u8_type, .manyptr_const_u8_sentinel_0_type, @@ -677,7 +674,6 @@ pub const Value = extern union { .export_options_type => return out_stream.writeAll("std.builtin.ExportOptions"), .extern_options_type => return out_stream.writeAll("std.builtin.ExternOptions"), .type_info_type => return out_stream.writeAll("std.builtin.Type"), - .abi_align_default => return out_stream.writeAll("(default ABI alignment)"), .empty_struct_value => return out_stream.writeAll("struct {}{}"), .aggregate => { From 911c839e97194eb270389b03d4d364659c46a5ac Mon Sep 17 00:00:00 2001 From: Daniel Hooper Date: Sun, 20 Mar 2022 06:55:04 -0400 Subject: [PATCH 0828/2031] add error when binary ops don't have matching whitespace on both sides This change also moves the warning about "&&" from the AstGen into the parser so that the "&&" warning can supersede the whitespace warning. --- lib/std/zig/Ast.zig | 9 +++- lib/std/zig/parse.zig | 23 +++++++--- lib/std/zig/parser_test.zig | 83 ++++++++++++++++++++++++++++++------- src/AstGen.zig | 19 +-------- test/compile_errors.zig | 2 +- test/stage2/x86_64.zig | 2 +- 6 files changed, 96 insertions(+), 42 deletions(-) diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig index 39a1d68847..b485f915f0 100644 --- a/lib/std/zig/Ast.zig +++ b/lib/std/zig/Ast.zig @@ -328,7 +328,12 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { .expected_initializer => { return stream.writeAll("expected field initializer"); }, - + .mismatched_binary_op_whitespace => { + return stream.print("binary operator `{s}` has whitespace on one side, but not the other.", .{token_tags[parse_error.token].lexeme().?}); + }, + .invalid_ampersand_ampersand => { + return stream.writeAll("ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND"); + }, .previous_field => { return stream.writeAll("field before declarations here"); }, @@ -2534,6 +2539,8 @@ pub const Error = struct { expected_comma_after_initializer, expected_comma_after_switch_prong, expected_initializer, + mismatched_binary_op_whitespace, + invalid_ampersand_ampersand, previous_field, next_field, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index d15e58eb1c..984b137bff 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -1451,13 +1451,11 @@ const Parser = struct { if (info.prec == banned_prec) { return p.fail(.chained_comparison_operators); } + const oper_token = p.nextToken(); - // Special-case handling for "catch" and "&&". - switch (tok_tag) { - .keyword_catch => { - _ = try p.parsePayload(); - }, - else => {}, + // Special-case handling for "catch" + if (tok_tag == .keyword_catch) { + _ = try p.parsePayload(); } const rhs = try p.parseExprPrecedence(info.prec + 1); if (rhs == 0) { @@ -1465,6 +1463,19 @@ const Parser = struct { return node; } + { + const tok_len = tok_tag.lexeme().?.len; + const char_before = p.source[p.token_starts[oper_token] - 1]; + const char_after = p.source[p.token_starts[oper_token] + tok_len]; + if (tok_tag == .ampersand and char_after == '&') { + // without types we don't know if '&&' was intended as 'bitwise_and address_of', or a c-style logical_and + // The best the parser can do is recommend changing it to 'and' or ' & &' + try p.warnMsg(.{ .tag = .invalid_ampersand_ampersand, .token = oper_token }); + } else if (std.ascii.isSpace(char_before) != std.ascii.isSpace(char_after)) { + try p.warnMsg(.{ .tag = .mismatched_binary_op_whitespace, .token = oper_token }); + } + } + node = try p.addNode(.{ .tag = info.tag, .main_token = oper_token, diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index c9c598b1d6..af40d8352c 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -4203,7 +4203,7 @@ test "zig fmt: integer literals with underscore separators" { \\const \\ x = \\ 1_234_567 - \\ +(0b0_1-0o7_0+0xff_FF ) + 0_0; + \\ + (0b0_1-0o7_0+0xff_FF ) + 0_0; , \\const x = \\ 1_234_567 + (0b0_1 - 0o7_0 + 0xff_FF) + 0_0; @@ -5105,7 +5105,7 @@ test "recovery: missing comma" { \\ 2 => {} \\ 3 => {} \\ else => { - \\ foo && bar +; + \\ foo & bar +; \\ } \\ } \\} @@ -5139,7 +5139,7 @@ test "recovery: extra qualifier" { test "recovery: missing return type" { try testError( \\fn foo() { - \\ a && b; + \\ a & b; \\} \\test "" , &[_]Error{ @@ -5154,7 +5154,7 @@ test "recovery: continue after invalid decl" { \\ inline; \\} \\pub test "" { - \\ async a && b; + \\ async a & b; \\} , &[_]Error{ .expected_token, @@ -5163,7 +5163,7 @@ test "recovery: continue after invalid decl" { }); try testError( \\threadlocal test "" { - \\ @a && b; + \\ @a & b; \\} , &[_]Error{ .expected_var_decl, @@ -5173,12 +5173,12 @@ test "recovery: continue after invalid decl" { test "recovery: invalid extern/inline" { try testError( - \\inline test "" { a && b; } + \\inline test "" { a & b; } , &[_]Error{ .expected_fn, }); try testError( - \\extern "" test "" { a && b; } + \\extern "" test "" { a & b; } , &[_]Error{ .expected_var_decl_or_fn, }); @@ -5187,8 +5187,8 @@ test "recovery: invalid extern/inline" { test "recovery: missing semicolon" { try testError( \\test "" { - \\ comptime a && b - \\ c && d + \\ comptime a & b + \\ c & d \\ @foo \\} , &[_]Error{ @@ -5206,7 +5206,7 @@ test "recovery: invalid container members" { \\bar@, \\while (a == 2) { test "" {}} \\test "" { - \\ a && b + \\ a & b \\} , &[_]Error{ .expected_expr, @@ -5224,7 +5224,7 @@ test "recovery: extra '}' at top level" { try testError( \\}}} \\test "" { - \\ a && b; + \\ a & b; \\} , &[_]Error{ .expected_token, @@ -5244,7 +5244,7 @@ test "recovery: mismatched bracket at top level" { test "recovery: invalid global error set access" { try testError( \\test "" { - \\ error && foo; + \\ error & foo; \\} , &[_]Error{ .expected_token, @@ -5259,13 +5259,15 @@ test "recovery: invalid asterisk after pointer dereference" { \\} , &[_]Error{ .asterisk_after_ptr_deref, + .mismatched_binary_op_whitespace, }); try testError( \\test "" { - \\ var sequence = "repeat".** 10&&a; + \\ var sequence = "repeat".** 10&a; \\} , &[_]Error{ .asterisk_after_ptr_deref, + .mismatched_binary_op_whitespace, }); } @@ -5275,7 +5277,7 @@ test "recovery: missing semicolon after if, for, while stmt" { \\ if (foo) bar \\ for (foo) |a| bar \\ while (foo) bar - \\ a && b; + \\ a & b; \\} , &[_]Error{ .expected_semi_or_else, @@ -5373,6 +5375,54 @@ test "recovery: eof in c pointer" { }); } +test "matching whitespace on minus op" { + try testError( + \\ _ = 2 -1, + \\ _ = 2- 1, + \\ _ = 2- + \\ 2, + \\ _ = 2 + \\ -2, + , &[_]Error{ + .mismatched_binary_op_whitespace, + .mismatched_binary_op_whitespace, + .mismatched_binary_op_whitespace, + .mismatched_binary_op_whitespace, + }); + + try testError( + \\ _ = - 1, + \\ _ = -1, + \\ _ = 2 - -1, + \\ _ = 2 - 1, + \\ _ = 2-1, + \\ _ = 2 - + \\1, + \\ _ = 2 + \\ - 1, + , &[_]Error{}); +} + +test "ampersand" { + try testError( + \\ _ = bar && foo, + \\ _ = bar&&foo, + \\ _ = bar& & foo, + \\ _ = bar& &foo, + , &.{ + .invalid_ampersand_ampersand, + .invalid_ampersand_ampersand, + .mismatched_binary_op_whitespace, + .mismatched_binary_op_whitespace, + }); + + try testError( + \\ _ = bar & &foo, + \\ _ = bar & &&foo, + \\ _ = &&foo, + , &.{}); +} + const std = @import("std"); const mem = std.mem; const print = std.debug.print; @@ -5466,7 +5516,10 @@ fn testError(source: [:0]const u8, expected_errors: []const Error) !void { var tree = try std.zig.parse(std.testing.allocator, source); defer tree.deinit(std.testing.allocator); - try std.testing.expectEqual(expected_errors.len, tree.errors.len); + std.testing.expectEqual(expected_errors.len, tree.errors.len) catch |err| { + std.debug.print("errors found: {any}\n", .{tree.errors}); + return err; + }; for (expected_errors) |expected, i| { try std.testing.expectEqual(expected, tree.errors[i].tag); } diff --git a/src/AstGen.zig b/src/AstGen.zig index 1fe524572b..4221f0cd14 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -669,24 +669,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr .mod => return simpleBinOp(gz, scope, rl, node, .mod_rem), .shl_sat => return simpleBinOp(gz, scope, rl, node, .shl_sat), - .bit_and => { - const current_ampersand_token = main_tokens[node]; - if (token_tags[current_ampersand_token + 1] == .ampersand) { - const token_starts = tree.tokens.items(.start); - const current_token_offset = token_starts[current_ampersand_token]; - const next_token_offset = token_starts[current_ampersand_token + 1]; - if (current_token_offset + 1 == next_token_offset) { - return astgen.failTok( - current_ampersand_token, - "`&&` is invalid; note that `and` is boolean AND", - .{}, - ); - } - } - - return simpleBinOp(gz, scope, rl, node, .bit_and); - }, - + .bit_and => return simpleBinOp(gz, scope, rl, node, .bit_and), .bit_or => return simpleBinOp(gz, scope, rl, node, .bit_or), .bit_xor => return simpleBinOp(gz, scope, rl, node, .xor), .bang_equal => return simpleBinOp(gz, scope, rl, node, .cmp_neq), diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 3a84617222..81083cb049 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3259,7 +3259,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ return 5678; \\} , &[_][]const u8{ - "tmp.zig:2:11: error: `&&` is invalid; note that `and` is boolean AND", + "tmp.zig:2:11: error: ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND", }); ctx.objErrStage1("attempted `||` on boolean values", diff --git a/test/stage2/x86_64.zig b/test/stage2/x86_64.zig index 518c505332..38dafd8fc0 100644 --- a/test/stage2/x86_64.zig +++ b/test/stage2/x86_64.zig @@ -1555,7 +1555,7 @@ pub fn addCases(ctx: *TestContext) !void { case.addError( \\pub const a = if (true && false) 1 else 2; - , &[_][]const u8{":1:24: error: `&&` is invalid; note that `and` is boolean AND"}); + , &[_][]const u8{":1:24: error: ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND"}); case.addError( \\pub fn main() void { From 9f25c8140cb859fcea7023362afcb29b1e4df41f Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 19 Mar 2022 17:37:05 -0700 Subject: [PATCH 0829/2031] cmake: add missing DWARF files to ZIG_STAGE2_SOURCES --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3bbe8acdcd..4f7cc155df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -398,6 +398,9 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/debug.zig" "${CMAKE_SOURCE_DIR}/lib/std/dwarf.zig" "${CMAKE_SOURCE_DIR}/lib/std/dwarf/AT.zig" + "${CMAKE_SOURCE_DIR}/lib/std/dwarf/ATE.zig" + "${CMAKE_SOURCE_DIR}/lib/std/dwarf/FORM.zig" + "${CMAKE_SOURCE_DIR}/lib/std/dwarf/LANG.zig" "${CMAKE_SOURCE_DIR}/lib/std/dwarf/OP.zig" "${CMAKE_SOURCE_DIR}/lib/std/dwarf/TAG.zig" "${CMAKE_SOURCE_DIR}/lib/std/elf.zig" From b7f40451844caa81f8ecb233c68777f06232d94c Mon Sep 17 00:00:00 2001 From: jagt Date: Sun, 20 Mar 2022 20:52:43 +0800 Subject: [PATCH 0830/2031] add compiler_rt ceilf/ceil/ceill this should fix stage1 build error with msvc 2019 --- lib/std/special/compiler_rt.zig | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 3941918c47..a017266304 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -680,6 +680,10 @@ comptime { @export(floor, .{ .name = "floor", .linkage = linkage }); @export(floorl, .{ .name = "floorl", .linkage = linkage }); + @export(ceilf, .{ .name = "ceilf", .linkage = linkage }); + @export(ceil, .{ .name = "ceil", .linkage = linkage }); + @export(ceill, .{ .name = "ceill", .linkage = linkage }); + @export(fma, .{ .name = "fma", .linkage = linkage }); @export(fmaf, .{ .name = "fmaf", .linkage = linkage }); @export(fmal, .{ .name = "fmal", .linkage = linkage }); @@ -811,6 +815,19 @@ fn floorl(x: c_longdouble) callconv(.C) c_longdouble { return math.floor(x); } +fn ceilf(x: f32) callconv(.C) f32 { + return math.ceil(x); +} +fn ceil(x: f64) callconv(.C) f64 { + return math.ceil(x); +} +fn ceill(x: c_longdouble) callconv(.C) c_longdouble { + if (!long_double_is_f128) { + @panic("TODO implement this"); + } + return math.ceil(x); +} + // Avoid dragging in the runtime safety mechanisms into this .o file, // unless we're trying to test this file. pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn { From 65058ebd72d06ee5f920df3e2c3e68dd77f5fccf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 20 Mar 2022 13:29:48 -0700 Subject: [PATCH 0831/2031] freestanding libc: remove ceil functions Now that they are in compiler-rt, they can be removed from here. --- lib/std/special/c.zig | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index 326ca3a8a0..d0639463a0 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -83,10 +83,6 @@ comptime { @export(log10, .{ .name = "log10", .linkage = .Strong }); @export(log10f, .{ .name = "log10f", .linkage = .Strong }); - @export(ceil, .{ .name = "ceil", .linkage = .Strong }); - @export(ceilf, .{ .name = "ceilf", .linkage = .Strong }); - @export(ceill, .{ .name = "ceill", .linkage = .Strong }); - @export(fmod, .{ .name = "fmod", .linkage = .Strong }); @export(fmodf, .{ .name = "fmodf", .linkage = .Strong }); @@ -429,21 +425,6 @@ fn log10f(a: f32) callconv(.C) f32 { return math.log10(a); } -fn ceilf(x: f32) callconv(.C) f32 { - return math.ceil(x); -} - -fn ceil(x: f64) callconv(.C) f64 { - return math.ceil(x); -} - -fn ceill(x: c_longdouble) callconv(.C) c_longdouble { - if (!long_double_is_f128) { - @panic("TODO implement this"); - } - return math.ceil(x); -} - fn fmodf(x: f32, y: f32) callconv(.C) f32 { return generic_fmod(f32, x, y); } From a5dc3f03423befa5a2ec1efc07f9bf26eae1a3d9 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Thu, 17 Mar 2022 09:12:24 -0700 Subject: [PATCH 0832/2031] stage2: add safety checks for index out of bounds --- src/Sema.zig | 396 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 267 insertions(+), 129 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index eb14542a97..e7c6ae0719 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3235,7 +3235,7 @@ fn zirValidateArrayInit( // any ZIR instructions at comptime; we need to do that here. if (array_ty.sentinel()) |sentinel_val| { const array_len_ref = try sema.addIntUnsigned(Type.usize, array_len); - const sentinel_ptr = try sema.elemPtrArray(block, init_src, array_ptr, array_len_ref, init_src); + const sentinel_ptr = try sema.elemPtrArray(block, init_src, array_ptr, init_src, array_len_ref); const sentinel = try sema.addConstant(array_ty.childType(), sentinel_val); try sema.storePtr2(block, init_src, sentinel_ptr, init_src, sentinel, init_src, .store); } @@ -15746,6 +15746,7 @@ pub const PanicId = enum { cast_to_null, incorrect_alignment, invalid_error_code, + index_out_of_bounds, }; fn addSafetyCheck( @@ -15867,6 +15868,7 @@ fn safetyPanic( .cast_to_null => "cast causes pointer to be null", .incorrect_alignment => "incorrect alignment", .invalid_error_code => "invalid error code", + .index_out_of_bounds => "attempt to index out of bounds", }; const msg_inst = msg_inst: { @@ -16483,10 +16485,10 @@ fn structFieldPtr( return sema.analyzeRef(block, src, len_inst); } const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src); - return sema.tupleFieldPtr(block, struct_ptr, field_index, src, field_name_src); + return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index); } else if (struct_ty.isAnonStruct()) { const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src); - return sema.tupleFieldPtr(block, struct_ptr, field_index, src, field_name_src); + return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index); } const struct_obj = struct_ty.castTag(.@"struct").?.data; @@ -16806,67 +16808,55 @@ fn elemPtr( sema: *Sema, block: *Block, src: LazySrcLoc, - array_ptr: Air.Inst.Ref, + indexable_ptr: 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 array_ptr_ty = sema.typeOf(array_ptr); - const array_ty = switch (array_ptr_ty.zigTypeTag()) { - .Pointer => array_ptr_ty.elemType(), - else => return sema.fail(block, array_ptr_src, "expected pointer, found '{}'", .{array_ptr_ty}), + const indexable_ptr_src = src; // TODO better source location + const indexable_ptr_ty = sema.typeOf(indexable_ptr); + const indexable_ty = switch (indexable_ptr_ty.zigTypeTag()) { + .Pointer => indexable_ptr_ty.elemType(), + else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty}), }; - if (!array_ty.isIndexable()) { - return sema.fail(block, src, "array access of non-indexable type '{}'", .{array_ty}); + if (!indexable_ty.isIndexable()) { + return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty}); } - switch (array_ty.zigTypeTag()) { + switch (indexable_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, array_ptr_src, array_ptr, array_ptr_src); + // In all below cases, we have to deref the ptr operand to get the actual indexable pointer. + const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); const target = sema.mod.getTarget(); - const result_ty = try array_ty.elemPtrType(sema.arena, target); - switch (array_ty.ptrSize()) { - .Slice => { - 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(array_ty, 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); - }, + const result_ty = try indexable_ty.elemPtrType(sema.arena, target); + switch (indexable_ty.ptrSize()) { + .Slice => return sema.elemPtrSlice(block, indexable_ptr_src, indexable, elem_index_src, elem_index), .Many, .C => { - const maybe_ptr_val = try sema.resolveDefinedValue(block, array_ptr_src, array); + const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_ptr_src, indexable); 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 ptr_val = maybe_ptr_val orelse break :rs indexable_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(array_ty, sema.arena, index); + const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index); return sema.addConstant(result_ty, elem_ptr); }; try sema.requireRuntimeBlock(block, runtime_src); - return block.addPtrElemPtr(array, elem_index, result_ty); + return block.addPtrElemPtr(indexable, elem_index, result_ty); }, .One => { - assert(array_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable - return sema.elemPtrArray(block, array_ptr_src, array, elem_index, elem_index_src); + assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable + return sema.elemPtrArray(block, indexable_ptr_src, indexable, elem_index_src, elem_index); }, } }, - .Array, .Vector => return sema.elemPtrArray(block, array_ptr_src, array_ptr, elem_index, elem_index_src), + .Array, .Vector => return sema.elemPtrArray(block, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index), .Struct => { // Tuple field access. const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index); const index = @intCast(u32, index_val.toUnsignedInt()); - return sema.tupleFieldPtr(block, array_ptr, index, src, elem_index_src); + return sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index); }, else => unreachable, } @@ -16876,90 +16866,66 @@ fn elemVal( sema: *Sema, block: *Block, src: LazySrcLoc, - array: Air.Inst.Ref, + indexable: Air.Inst.Ref, elem_index_uncasted: Air.Inst.Ref, elem_index_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { - const array_src = src; // TODO better source location - const array_ty = sema.typeOf(array); + const indexable_src = src; // TODO better source location + const indexable_ty = sema.typeOf(indexable); - if (!array_ty.isIndexable()) { - return sema.fail(block, src, "array access of non-indexable type '{}'", .{array_ty}); + if (!indexable_ty.isIndexable()) { + return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty}); } // TODO in case of a vector of pointers, we need to detect whether the element // index is a scalar or vector instead of unconditionally casting to usize. const elem_index = try sema.coerce(block, Type.usize, elem_index_uncasted, elem_index_src); - switch (array_ty.zigTypeTag()) { - .Pointer => switch (array_ty.ptrSize()) { - .Slice => { - 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_ty = array_ty.elemType2(); - - var payload: Value.Payload.ElemPtr = .{ .data = .{ - .array_ptr = slice_val.slicePtr(), - .elem_ty = elem_ty, - .index = index, - } }; - const elem_ptr_val = Value.initPayload(&payload.base); - - if (try sema.pointerDeref(block, array_src, elem_ptr_val, array_ty)) |elem_val| { - return sema.addConstant(elem_ty, elem_val); - } - break :rs array_src; - } else array_src; - - try sema.requireRuntimeBlock(block, runtime_src); - return block.addBinOp(.slice_elem_val, array, elem_index); - }, + switch (indexable_ty.zigTypeTag()) { + .Pointer => switch (indexable_ty.ptrSize()) { + .Slice => return sema.elemValSlice(block, indexable_src, indexable, elem_index_src, elem_index), .Many, .C => { - const maybe_array_val = try sema.resolveDefinedValue(block, array_src, array); + const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable); const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); const runtime_src = rs: { - const array_val = maybe_array_val orelse break :rs array_src; + const indexable_val = maybe_indexable_val orelse break :rs indexable_src; const index_val = maybe_index_val orelse break :rs elem_index_src; const index = @intCast(usize, index_val.toUnsignedInt()); - const elem_ty = array_ty.elemType2(); + const elem_ty = indexable_ty.elemType2(); var payload: Value.Payload.ElemPtr = .{ .data = .{ - .array_ptr = array_val, + .array_ptr = indexable_val, .elem_ty = elem_ty, .index = index, } }; const elem_ptr_val = Value.initPayload(&payload.base); - if (try sema.pointerDeref(block, array_src, elem_ptr_val, array_ty)) |elem_val| { + if (try sema.pointerDeref(block, indexable_src, elem_ptr_val, indexable_ty)) |elem_val| { return sema.addConstant(elem_ty, elem_val); } - break :rs array_src; + break :rs indexable_src; }; try sema.requireRuntimeBlock(block, runtime_src); - return block.addBinOp(.ptr_elem_val, array, elem_index); + return block.addBinOp(.ptr_elem_val, indexable, elem_index); }, .One => { - assert(array_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable - 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); + assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable + const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src); + return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src); }, }, - .Array => return elemValArray(sema, block, array, elem_index, array_src, elem_index_src), + .Array => return elemValArray(sema, block, indexable_src, indexable, elem_index_src, elem_index), .Vector => { // TODO: If the index is a vector, the result should be a vector. - return elemValArray(sema, block, array, elem_index, array_src, elem_index_src); + return elemValArray(sema, block, indexable_src, indexable, elem_index_src, elem_index); }, .Struct => { // Tuple field access. const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index); const index = @intCast(u32, index_val.toUnsignedInt()); - return tupleField(sema, block, array, index, array_src, elem_index_src); + return tupleField(sema, block, indexable_src, indexable, elem_index_src, index); }, else => unreachable, } @@ -16968,22 +16934,26 @@ fn elemVal( fn tupleFieldPtr( sema: *Sema, block: *Block, + tuple_ptr_src: LazySrcLoc, tuple_ptr: Air.Inst.Ref, - field_index: u32, - tuple_src: LazySrcLoc, field_index_src: LazySrcLoc, + field_index: u32, ) CompileError!Air.Inst.Ref { const tuple_ptr_ty = sema.typeOf(tuple_ptr); const tuple_ty = tuple_ptr_ty.childType(); - const tuple = tuple_ty.tupleFields(); + const tuple_fields = tuple_ty.tupleFields(); - if (field_index > tuple.types.len) { + if (tuple_fields.types.len == 0) { + return sema.fail(block, field_index_src, "indexing into empty tuple", .{}); + } + + if (field_index >= tuple_fields.types.len) { return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{ - field_index, tuple.types.len, + field_index, tuple_fields.types.len, }); } - const field_ty = tuple.types[field_index]; + const field_ty = tuple_fields.types[field_index]; const target = sema.mod.getTarget(); const ptr_field_ty = try Type.ptr(sema.arena, target, .{ .pointee_type = field_ty, @@ -16991,7 +16961,7 @@ fn tupleFieldPtr( .@"addrspace" = tuple_ptr_ty.ptrAddressSpace(), }); - if (try sema.resolveMaybeUndefVal(block, tuple_src, tuple_ptr)) |tuple_ptr_val| { + if (try sema.resolveMaybeUndefVal(block, tuple_ptr_src, tuple_ptr)) |tuple_ptr_val| { return sema.addConstant( ptr_field_ty, try Value.Tag.field_ptr.create(sema.arena, .{ @@ -17002,29 +16972,33 @@ fn tupleFieldPtr( ); } - try sema.requireRuntimeBlock(block, tuple_src); + try sema.requireRuntimeBlock(block, tuple_ptr_src); return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty); } fn tupleField( sema: *Sema, block: *Block, - tuple: Air.Inst.Ref, - field_index: u32, tuple_src: LazySrcLoc, + tuple: Air.Inst.Ref, field_index_src: LazySrcLoc, + field_index: u32, ) CompileError!Air.Inst.Ref { const tuple_ty = sema.typeOf(tuple); - const tuple_info = tuple_ty.tupleFields(); + const tuple_fields = tuple_ty.tupleFields(); - if (field_index > tuple_info.types.len) { + if (tuple_fields.types.len == 0) { + return sema.fail(block, field_index_src, "indexing into empty tuple", .{}); + } + + if (field_index >= tuple_fields.types.len) { return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{ - field_index, tuple_info.types.len, + field_index, tuple_fields.types.len, }); } - const field_ty = tuple_info.types[field_index]; - const field_val = tuple_info.values[field_index]; + const field_ty = tuple_fields.types[field_index]; + const field_val = tuple_fields.values[field_index]; if (field_val.tag() != .unreachable_value) { return sema.addConstant(field_ty, field_val); // comptime field @@ -17043,57 +17017,221 @@ fn tupleField( fn elemValArray( sema: *Sema, block: *Block, - array: Air.Inst.Ref, - elem_index: Air.Inst.Ref, array_src: LazySrcLoc, + array: Air.Inst.Ref, elem_index_src: LazySrcLoc, + elem_index: Air.Inst.Ref, ) CompileError!Air.Inst.Ref { const array_ty = sema.typeOf(array); - 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); + const array_sent = array_ty.sentinel() != null; + const array_len = array_ty.arrayLen(); + const array_len_s = array_len + @boolToInt(array_sent); + const elem_ty = array_ty.childType(); + + if (array_len_s == 0) { + return sema.fail(block, elem_index_src, "indexing into empty array", .{}); + } + + const maybe_undef_array_val = try sema.resolveMaybeUndefVal(block, array_src, array); + // index must be defined since it can access out of bounds + 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()); + if (index >= array_len_s) { + const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else ""; + return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); + } + } + if (maybe_undef_array_val) |array_val| { + if (array_val.isUndef()) { + return sema.addConstUndef(elem_ty); + } if (maybe_index_val) |index_val| { const index = @intCast(usize, index_val.toUnsignedInt()); - const len = array_ty.arrayLenIncludingSentinel(); - if (index >= len) { - return sema.fail(block, elem_index_src, "index {d} outside array of length {d}", .{ - index, len, - }); - } const elem_val = try array_val.elemValue(sema.arena, index); return sema.addConstant(elem_ty, elem_val); } } - try sema.requireRuntimeBlock(block, array_src); + + const runtime_src = if (maybe_undef_array_val != null) elem_index_src else array_src; + try sema.requireRuntimeBlock(block, runtime_src); + if (block.wantSafety()) { + // Runtime check is only needed if unable to comptime check + if (maybe_index_val == null) { + const len_inst = try sema.addIntUnsigned(Type.usize, array_len); + const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt; + const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst); + try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds); + } + } return block.addBinOp(.array_elem_val, array, elem_index); } fn elemPtrArray( sema: *Sema, block: *Block, - src: LazySrcLoc, + array_ptr_src: LazySrcLoc, array_ptr: Air.Inst.Ref, - elem_index: Air.Inst.Ref, elem_index_src: LazySrcLoc, + elem_index: Air.Inst.Ref, ) CompileError!Air.Inst.Ref { - const array_ptr_ty = sema.typeOf(array_ptr); const target = sema.mod.getTarget(); - const result_ty = try array_ptr_ty.elemPtrType(sema.arena, target); + const array_ptr_ty = sema.typeOf(array_ptr); + const array_ty = array_ptr_ty.childType(); + const array_sent = array_ty.sentinel() != null; + const array_len = array_ty.arrayLen(); + const array_len_s = array_len + @boolToInt(array_sent); + const elem_ptr_ty = try array_ptr_ty.elemPtrType(sema.arena, target); - if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| { - if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| { - // Both array pointer and index are compile-time known. - const index_u64 = index_val.toUnsignedInt(); - // @intCast here because it would have been impossible to construct a value that - // required a larger index. - const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, @intCast(usize, index_u64)); - return sema.addConstant(result_ty, elem_ptr); + if (array_len_s == 0) { + return sema.fail(block, elem_index_src, "indexing into empty array", .{}); + } + + const maybe_undef_array_ptr_val = try sema.resolveMaybeUndefVal(block, array_ptr_src, array_ptr); + // index must be defined since it can index out of bounds + 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()); + if (index >= array_len_s) { + const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else ""; + return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); } } - // TODO safety check for array bounds - try sema.requireRuntimeBlock(block, src); - return block.addPtrElemPtr(array_ptr, elem_index, result_ty); + if (maybe_undef_array_ptr_val) |array_ptr_val| { + if (array_ptr_val.isUndef()) { + return sema.addConstUndef(elem_ptr_ty); + } + if (maybe_index_val) |index_val| { + const index = @intCast(usize, index_val.toUnsignedInt()); + const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, index); + return sema.addConstant(elem_ptr_ty, elem_ptr); + } + } + + const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src; + try sema.requireRuntimeBlock(block, runtime_src); + if (block.wantSafety()) { + // Runtime check is only needed if unable to comptime check + if (maybe_index_val == null) { + const len_inst = try sema.addIntUnsigned(Type.usize, array_len); + const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt; + const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst); + try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds); + } + } + return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty); +} + +fn elemValSlice( + sema: *Sema, + block: *Block, + slice_src: LazySrcLoc, + slice: Air.Inst.Ref, + elem_index_src: LazySrcLoc, + elem_index: Air.Inst.Ref, +) CompileError!Air.Inst.Ref { + const slice_ty = sema.typeOf(slice); + const slice_sent = slice_ty.sentinel() != null; + const elem_ty = slice_ty.elemType2(); + var runtime_src = slice_src; + + // slice must be defined since it can dereferenced as null + const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice); + // index must be defined since it can index out of bounds + const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); + + if (maybe_slice_val) |slice_val| { + runtime_src = elem_index_src; + const slice_len = slice_val.sliceLen(); + const slice_len_s = slice_len + @boolToInt(slice_sent); + if (slice_len_s == 0) { + return sema.fail(block, elem_index_src, "indexing into empty slice", .{}); + } + if (maybe_index_val) |index_val| { + const index = @intCast(usize, index_val.toUnsignedInt()); + if (index >= slice_len_s) { + const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; + return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); + } + var elem_ptr_pl: Value.Payload.ElemPtr = .{ .data = .{ + .array_ptr = slice_val.slicePtr(), + .elem_ty = elem_ty, + .index = index, + } }; + const elem_ptr_val = Value.initPayload(&elem_ptr_pl.base); + if (try sema.pointerDeref(block, slice_src, elem_ptr_val, slice_ty)) |elem_val| { + return sema.addConstant(elem_ty, elem_val); + } + runtime_src = slice_src; + } + } + + try sema.requireRuntimeBlock(block, runtime_src); + if (block.wantSafety()) { + const len_inst = if (maybe_slice_val) |slice_val| + try sema.addIntUnsigned(Type.usize, slice_val.sliceLen()) + else + try block.addTyOp(.slice_len, Type.usize, slice); + const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; + const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst); + try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds); + } + return block.addBinOp(.slice_elem_val, slice, elem_index); +} + +fn elemPtrSlice( + sema: *Sema, + block: *Block, + slice_src: LazySrcLoc, + slice: Air.Inst.Ref, + elem_index_src: LazySrcLoc, + elem_index: Air.Inst.Ref, +) CompileError!Air.Inst.Ref { + const target = sema.mod.getTarget(); + const slice_ty = sema.typeOf(slice); + const slice_sent = slice_ty.sentinel() != null; + const elem_ptr_ty = try slice_ty.elemPtrType(sema.arena, target); + + const maybe_undef_slice_val = try sema.resolveMaybeUndefVal(block, slice_src, slice); + // index must be defined since it can index out of bounds + const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); + + if (maybe_undef_slice_val) |slice_val| { + if (slice_val.isUndef()) { + return sema.addConstUndef(elem_ptr_ty); + } + const slice_len = slice_val.sliceLen(); + const slice_len_s = slice_len + @boolToInt(slice_sent); + if (slice_len_s == 0) { + return sema.fail(block, elem_index_src, "indexing into empty slice", .{}); + } + if (maybe_index_val) |index_val| { + const index = @intCast(usize, index_val.toUnsignedInt()); + if (index >= slice_len_s) { + const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; + return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); + } + const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index); + return sema.addConstant(elem_ptr_ty, elem_ptr_val); + } + } + + const runtime_src = if (maybe_undef_slice_val != null) elem_index_src else slice_src; + try sema.requireRuntimeBlock(block, runtime_src); + if (block.wantSafety()) { + const len_inst = len: { + if (maybe_undef_slice_val) |slice_val| + if (!slice_val.isUndef()) + break :len try sema.addIntUnsigned(Type.usize, slice_val.sliceLen()); + break :len try block.addTyOp(.slice_len, Type.usize, slice); + }; + const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; + const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst); + try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds); + } + return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty); } fn coerce( @@ -18000,7 +18138,7 @@ fn storePtr2( for (tuple.types) |_, i_usize| { const i = @intCast(u32, i_usize); const elem_src = operand_src; // TODO better source location - const elem = try tupleField(sema, block, uncasted_operand, i, operand_src, elem_src); + const elem = try tupleField(sema, block, operand_src, uncasted_operand, elem_src, i); const elem_index = try sema.addIntUnsigned(Type.usize, i); const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src); try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store); @@ -18885,7 +19023,7 @@ fn coerceArrayLike( try Value.Tag.int_u64.create(sema.arena, i), ); const elem_src = inst_src; // TODO better source location - const elem_ref = try elemValArray(sema, block, inst, index_ref, inst_src, elem_src); + const elem_ref = try elemValArray(sema, block, inst_src, inst, elem_src, index_ref); const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); element_refs[i] = coerced; if (runtime_src == null) { @@ -18942,7 +19080,7 @@ fn coerceTupleToArray( for (element_vals) |*elem, i_usize| { const i = @intCast(u32, i_usize); const elem_src = inst_src; // TODO better source location - const elem_ref = try tupleField(sema, block, inst, i, inst_src, elem_src); + const elem_ref = try tupleField(sema, block, inst_src, inst, elem_src, i); const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); element_refs[i] = coerced; if (runtime_src == null) { @@ -19042,7 +19180,7 @@ fn coerceTupleToStruct( if (field.is_comptime) { return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to structs when one of the destination struct fields is comptime", .{}); } - const elem_ref = try tupleField(sema, block, inst, i, inst_src, field_src); + const elem_ref = try tupleField(sema, block, inst_src, inst, field_src, i); const coerced = try sema.coerce(block, field.ty, elem_ref, field_src); field_refs[field_index] = coerced; if (runtime_src == null) { From 3d8d6c0a6d7a29396725467672023b5ec3adbce6 Mon Sep 17 00:00:00 2001 From: Lee Cannon Date: Sun, 20 Mar 2022 21:00:23 +0000 Subject: [PATCH 0833/2031] OptionsStep: Always use `fmtId` for type names. --- lib/std/build/OptionsStep.zig | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/std/build/OptionsStep.zig b/lib/std/build/OptionsStep.zig index eae5983845..ccbaae358c 100644 --- a/lib/std/build/OptionsStep.zig +++ b/lib/std/build/OptionsStep.zig @@ -119,12 +119,17 @@ pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable; } out.writeAll("};\n") catch unreachable; - out.print("pub const {}: {s} = {s}.{s};\n", .{ std.zig.fmtId(name), @typeName(T), @typeName(T), std.zig.fmtId(@tagName(value)) }) catch unreachable; + out.print("pub const {}: {s} = {s}.{s};\n", .{ + std.zig.fmtId(name), + std.zig.fmtId(@typeName(T)), + std.zig.fmtId(@typeName(T)), + std.zig.fmtId(@tagName(value)), + }) catch unreachable; return; }, else => {}, } - out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), @typeName(T) }) catch unreachable; + out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), std.zig.fmtId(@typeName(T)) }) catch unreachable; printLiteral(out, value, 0) catch unreachable; out.writeAll(";\n") catch unreachable; } From a31fe0ff12270ba2f957c2a957941a23f2143ad5 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 21 Mar 2022 14:48:47 +0200 Subject: [PATCH 0834/2031] stage2: add way to print values with types --- src/Sema.zig | 16 +- src/TypedValue.zig | 281 +++++++++++++++++++++++++++++++++++ src/arch/aarch64/CodeGen.zig | 2 +- src/arch/x86_64/CodeGen.zig | 2 +- src/codegen.zig | 2 +- src/codegen/llvm.zig | 2 +- src/print_air.zig | 2 +- src/type.zig | 12 +- src/value.zig | 20 ++- 9 files changed, 318 insertions(+), 21 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index e7c6ae0719..7674121ba6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1867,7 +1867,7 @@ fn createTypeName( const arg_val = sema.resolveConstMaybeUndefVal(block, .unneeded, arg) catch unreachable; if (arg_i != 0) try buf.appendSlice(","); - try buf.writer().print("{}", .{arg_val}); + try buf.writer().print("{}", .{arg_val.fmtValue(sema.typeOf(arg))}); arg_i += 1; continue; @@ -3673,7 +3673,7 @@ fn zirCompileLog( const arg = sema.resolveInst(arg_ref); const arg_ty = sema.typeOf(arg); if (try sema.resolveMaybeUndefVal(block, src, arg)) |val| { - try writer.print("@as({}, {})", .{ arg_ty, val }); + try writer.print("@as({}, {})", .{ arg_ty, val.fmtValue(arg_ty) }); } else { try writer.print("@as({}, [runtime value])", .{arg_ty}); } @@ -5670,7 +5670,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A block, src, "enum '{}' has no tag with value {}", - .{ dest_ty, int_val }, + .{ dest_ty, int_val.fmtValue(sema.typeOf(operand)) }, ); errdefer msg.destroy(sema.gpa); try sema.mod.errNoteNonLazy( @@ -7900,7 +7900,7 @@ fn validateSwitchItemEnum( block, src, "enum '{}' has no tag with value '{}'", - .{ item_tv.ty, item_tv.val }, + .{ item_tv.ty, item_tv.val.fmtValue(item_tv.ty) }, ); errdefer msg.destroy(sema.gpa); try sema.mod.errNoteNonLazy( @@ -17507,7 +17507,7 @@ fn coerce( const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :float; if (val.floatHasFraction()) { - return sema.fail(block, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val, dest_ty }); + return sema.fail(block, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val.fmtValue(inst_ty), dest_ty }); } const result_val = val.floatToInt(sema.arena, dest_ty, target) catch |err| switch (err) { error.FloatCannotFit => { @@ -17521,7 +17521,7 @@ fn coerce( if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| { // comptime known integer to other number if (!val.intFitsInType(dest_ty, target)) { - return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_ty, val }); + return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_ty, val.fmtValue(inst_ty) }); } return try sema.addConstant(dest_ty, val); } @@ -17556,7 +17556,7 @@ fn coerce( block, inst_src, "type {} cannot represent float value {}", - .{ dest_ty, val }, + .{ dest_ty, val.fmtValue(inst_ty) }, ); } return try sema.addConstant(dest_ty, result_val); @@ -18850,7 +18850,7 @@ fn coerceEnumToUnion( const field_index = union_obj.tag_ty.enumTagFieldIndex(val) orelse { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "union {} has no tag with value {}", .{ - union_ty, val, + union_ty, val.fmtValue(tag_ty), }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, union_ty); diff --git a/src/TypedValue.zig b/src/TypedValue.zig index 1fa4813a34..fc5ef45991 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -42,3 +42,284 @@ pub fn hash(tv: TypedValue, hasher: *std.hash.Wyhash) void { pub fn enumToInt(tv: TypedValue, buffer: *Value.Payload.U64) Value { return tv.val.enumToInt(tv.ty, buffer); } + +const max_aggregate_items = 100; + +pub fn format( + tv: TypedValue, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + comptime std.debug.assert(fmt.len == 0); + return tv.print(options, writer, 3); +} + +pub fn print( + tv: TypedValue, + options: std.fmt.FormatOptions, + writer: anytype, + level: u8, +) @TypeOf(writer).Error!void { + var val = tv.val; + var ty = tv.ty; + while (true) switch (val.tag()) { + .u1_type => return writer.writeAll("u1"), + .u8_type => return writer.writeAll("u8"), + .i8_type => return writer.writeAll("i8"), + .u16_type => return writer.writeAll("u16"), + .i16_type => return writer.writeAll("i16"), + .u32_type => return writer.writeAll("u32"), + .i32_type => return writer.writeAll("i32"), + .u64_type => return writer.writeAll("u64"), + .i64_type => return writer.writeAll("i64"), + .u128_type => return writer.writeAll("u128"), + .i128_type => return writer.writeAll("i128"), + .isize_type => return writer.writeAll("isize"), + .usize_type => return writer.writeAll("usize"), + .c_short_type => return writer.writeAll("c_short"), + .c_ushort_type => return writer.writeAll("c_ushort"), + .c_int_type => return writer.writeAll("c_int"), + .c_uint_type => return writer.writeAll("c_uint"), + .c_long_type => return writer.writeAll("c_long"), + .c_ulong_type => return writer.writeAll("c_ulong"), + .c_longlong_type => return writer.writeAll("c_longlong"), + .c_ulonglong_type => return writer.writeAll("c_ulonglong"), + .c_longdouble_type => return writer.writeAll("c_longdouble"), + .f16_type => return writer.writeAll("f16"), + .f32_type => return writer.writeAll("f32"), + .f64_type => return writer.writeAll("f64"), + .f80_type => return writer.writeAll("f80"), + .f128_type => return writer.writeAll("f128"), + .anyopaque_type => return writer.writeAll("anyopaque"), + .bool_type => return writer.writeAll("bool"), + .void_type => return writer.writeAll("void"), + .type_type => return writer.writeAll("type"), + .anyerror_type => return writer.writeAll("anyerror"), + .comptime_int_type => return writer.writeAll("comptime_int"), + .comptime_float_type => return writer.writeAll("comptime_float"), + .noreturn_type => return writer.writeAll("noreturn"), + .null_type => return writer.writeAll("@Type(.Null)"), + .undefined_type => return writer.writeAll("@Type(.Undefined)"), + .fn_noreturn_no_args_type => return writer.writeAll("fn() noreturn"), + .fn_void_no_args_type => return writer.writeAll("fn() void"), + .fn_naked_noreturn_no_args_type => return writer.writeAll("fn() callconv(.Naked) noreturn"), + .fn_ccc_void_no_args_type => return writer.writeAll("fn() callconv(.C) void"), + .single_const_pointer_to_comptime_int_type => return writer.writeAll("*const comptime_int"), + .anyframe_type => return writer.writeAll("anyframe"), + .const_slice_u8_type => return writer.writeAll("[]const u8"), + .const_slice_u8_sentinel_0_type => return writer.writeAll("[:0]const u8"), + .anyerror_void_error_union_type => return writer.writeAll("anyerror!void"), + + .enum_literal_type => return writer.writeAll("@Type(.EnumLiteral)"), + .manyptr_u8_type => return writer.writeAll("[*]u8"), + .manyptr_const_u8_type => return writer.writeAll("[*]const u8"), + .manyptr_const_u8_sentinel_0_type => return writer.writeAll("[*:0]const u8"), + .atomic_order_type => return writer.writeAll("std.builtin.AtomicOrder"), + .atomic_rmw_op_type => return writer.writeAll("std.builtin.AtomicRmwOp"), + .calling_convention_type => return writer.writeAll("std.builtin.CallingConvention"), + .address_space_type => return writer.writeAll("std.builtin.AddressSpace"), + .float_mode_type => return writer.writeAll("std.builtin.FloatMode"), + .reduce_op_type => return writer.writeAll("std.builtin.ReduceOp"), + .call_options_type => return writer.writeAll("std.builtin.CallOptions"), + .prefetch_options_type => return writer.writeAll("std.builtin.PrefetchOptions"), + .export_options_type => return writer.writeAll("std.builtin.ExportOptions"), + .extern_options_type => return writer.writeAll("std.builtin.ExternOptions"), + .type_info_type => return writer.writeAll("std.builtin.Type"), + + .empty_struct_value => return writer.writeAll(".{}"), + .aggregate => { + if (level == 0) { + return writer.writeAll(".{ ... }"); + } + const vals = val.castTag(.aggregate).?.data; + if (ty.zigTypeTag() == .Struct) { + try writer.writeAll(".{ "); + const struct_fields = ty.structFields(); + const max_len = std.math.min(struct_fields.count(), max_aggregate_items); + + const field_names = struct_fields.keys(); + const fields = struct_fields.values(); + + var i: u32 = 0; + while (i < max_len) : (i += 1) { + if (i != 0) try writer.writeAll(", "); + try writer.print(".{s} = ", .{field_names[i]}); + try print(.{ + .ty = fields[i].ty, + .val = vals[i], + }, options, writer, level - 1); + } + return writer.writeAll(" }"); + } else { + try writer.writeAll(".{ "); + const elem_ty = ty.elemType2(); + const max_len = std.math.min(ty.arrayLen(), max_aggregate_items); + + var i: u32 = 0; + while (i < max_len) : (i += 1) { + if (i != 0) try writer.writeAll(", "); + try print(.{ + .ty = elem_ty, + .val = vals[i], + }, options, writer, level - 1); + } + return writer.writeAll(" }"); + } + }, + .@"union" => { + if (level == 0) { + return writer.writeAll(".{ ... }"); + } + const union_val = val.castTag(.@"union").?.data; + try writer.writeAll(".{ "); + + try print(.{ + .ty = ty.unionTagType().?, + .val = union_val.tag, + }, options, writer, level - 1); + try writer.writeAll(" = "); + try print(.{ + .ty = ty.unionFieldType(union_val.tag), + .val = union_val.val, + }, options, writer, level - 1); + + return writer.writeAll(" }"); + }, + .null_value => return writer.writeAll("null"), + .undef => return writer.writeAll("undefined"), + .zero => return writer.writeAll("0"), + .one => return writer.writeAll("1"), + .void_value => return writer.writeAll("{}"), + .unreachable_value => return writer.writeAll("unreachable"), + .the_only_possible_value => { + val = ty.onePossibleValue().?; + }, + .bool_true => return writer.writeAll("true"), + .bool_false => return writer.writeAll("false"), + .ty => return val.castTag(.ty).?.data.format("", options, writer), + .int_type => { + const int_type = val.castTag(.int_type).?.data; + return writer.print("{s}{d}", .{ + if (int_type.signed) "s" else "u", + int_type.bits, + }); + }, + .int_u64 => return std.fmt.formatIntValue(val.castTag(.int_u64).?.data, "", options, writer), + .int_i64 => return std.fmt.formatIntValue(val.castTag(.int_i64).?.data, "", options, writer), + .int_big_positive => return writer.print("{}", .{val.castTag(.int_big_positive).?.asBigInt()}), + .int_big_negative => return writer.print("{}", .{val.castTag(.int_big_negative).?.asBigInt()}), + .function => return writer.print("(function '{s}')", .{val.castTag(.function).?.data.owner_decl.name}), + .extern_fn => return writer.writeAll("(extern function)"), + .variable => return writer.writeAll("(variable)"), + .decl_ref_mut => { + const decl = val.castTag(.decl_ref_mut).?.data.decl; + if (level == 0) { + return writer.print("(decl ref mut '{s}')", .{decl.name}); + } + return print(.{ + .ty = decl.ty, + .val = decl.val, + }, options, writer, level - 1); + }, + .decl_ref => { + const decl = val.castTag(.decl_ref).?.data; + if (level == 0) { + return writer.print("(decl ref '{s}')", .{decl.name}); + } + return print(.{ + .ty = decl.ty, + .val = decl.val, + }, options, writer, level - 1); + }, + .elem_ptr => { + const elem_ptr = val.castTag(.elem_ptr).?.data; + try writer.writeAll("&"); + try print(.{ + .ty = elem_ptr.elem_ty, + .val = elem_ptr.array_ptr, + }, options, writer, level - 1); + return writer.print("[{}]", .{elem_ptr.index}); + }, + .field_ptr => { + const field_ptr = val.castTag(.field_ptr).?.data; + try writer.writeAll("&"); + try print(.{ + .ty = field_ptr.container_ty, + .val = field_ptr.container_ptr, + }, options, writer, level - 1); + + if (field_ptr.container_ty.zigTypeTag() == .Struct) { + const field_name = field_ptr.container_ty.structFields().keys()[field_ptr.field_index]; + return writer.print(".{s}", .{field_name}); + } else if (field_ptr.container_ty.zigTypeTag() == .Union) { + const field_name = field_ptr.container_ty.unionFields().keys()[field_ptr.field_index]; + return writer.print(".{s}", .{field_name}); + } else unreachable; + }, + .empty_array => return writer.writeAll(".{}"), + .enum_literal => return writer.print(".{}", .{std.zig.fmtId(val.castTag(.enum_literal).?.data)}), + .enum_field_index => { + return writer.print(".{s}", .{ty.enumFieldName(val.castTag(.enum_field_index).?.data)}); + }, + .bytes => return writer.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}), + .repeated => { + if (level == 0) { + return writer.writeAll(".{ ... }"); + } + var i: u32 = 0; + try writer.writeAll(".{ "); + const elem_tv = TypedValue{ + .ty = ty.elemType2(), + .val = val.castTag(.repeated).?.data, + }; + while (i < max_aggregate_items) : (i += 1) { + if (i != 0) try writer.writeAll(", "); + try print(elem_tv, options, writer, level - 1); + } + return writer.writeAll(" }"); + }, + .empty_array_sentinel => { + if (level == 0) { + return writer.writeAll(".{ (sentinel) }"); + } + try writer.writeAll(".{ "); + try print(.{ + .ty = ty.elemType2(), + .val = ty.sentinel().?, + }, options, writer, level - 1); + return writer.writeAll(" }"); + }, + .slice => return writer.writeAll("(slice)"), + .float_16 => return writer.print("{}", .{val.castTag(.float_16).?.data}), + .float_32 => return writer.print("{}", .{val.castTag(.float_32).?.data}), + .float_64 => return writer.print("{}", .{val.castTag(.float_64).?.data}), + .float_80 => return writer.print("{}", .{val.castTag(.float_80).?.data}), + .float_128 => return writer.print("{}", .{val.castTag(.float_128).?.data}), + .@"error" => return writer.print("error.{s}", .{val.castTag(.@"error").?.data.name}), + .eu_payload => { + val = val.castTag(.eu_payload).?.data; + }, + .opt_payload => { + val = val.castTag(.opt_payload).?.data; + }, + .eu_payload_ptr => { + try writer.writeAll("&"); + val = val.castTag(.eu_payload_ptr).?.data.container_ptr; + }, + .opt_payload_ptr => { + try writer.writeAll("&"); + val = val.castTag(.opt_payload_ptr).?.data.container_ptr; + }, + + // TODO these should not appear in this function + .inferred_alloc => return writer.writeAll("(inferred allocation value)"), + .inferred_alloc_comptime => return writer.writeAll("(inferred comptime allocation value)"), + .bound_fn => { + const bound_func = val.castTag(.bound_fn).?.data; + return writer.print("(bound_fn %{}(%{})", .{ bound_func.func_inst, bound_func.arg0_inst }); + }, + .generic_poison_type => return writer.writeAll("(generic poison type)"), + .generic_poison => return writer.writeAll("(generic poison)"), + }; +} diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 9a732e9fd0..c9121c8859 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -3873,7 +3873,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa } fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { - log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty, tv.val }); + log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty, tv.val.fmtDebug() }); const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| { return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); }; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index d90ec90c28..37d93f870c 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -5735,7 +5735,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa } fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { - log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty, tv.val }); + log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty, tv.val.fmtDebug() }); const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| { return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); }; diff --git a/src/codegen.zig b/src/codegen.zig index 5d61095ff5..95b145e901 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -165,7 +165,7 @@ pub fn generateSymbol( const target = bin_file.options.target; const endian = target.cpu.arch.endian(); - log.debug("generateSymbol: ty = {}, val = {}", .{ typed_value.ty, typed_value.val }); + log.debug("generateSymbol: ty = {}, val = {}", .{ typed_value.ty, typed_value.val.fmtDebug() }); if (typed_value.val.isUndefDeep()) { const abi_size = try math.cast(usize, typed_value.ty.abiSize(target)); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 2d5b38eb17..8eb1eba3f6 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1676,7 +1676,7 @@ pub const DeclGen = struct { const decl = dg.decl; assert(decl.has_tv); - log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val }); + log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val.fmtDebug() }); if (decl.val.castTag(.function)) |func_payload| { _ = func_payload; diff --git a/src/print_air.zig b/src/print_air.zig index a9605d5dcf..fb970e3b08 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -491,7 +491,7 @@ const Writer = struct { fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; const val = w.air.values[ty_pl.payload]; - try s.print("{}, {}", .{ w.air.getRefType(ty_pl.ty), val }); + try s.print("{}, {}", .{ w.air.getRefType(ty_pl.ty), val.fmtDebug() }); } fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { diff --git a/src/type.zig b/src/type.zig index a29dc19f8d..6d27150412 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1633,7 +1633,7 @@ pub const Type = extern union { }, .array_sentinel => { const payload = ty.castTag(.array_sentinel).?.data; - try writer.print("[{d}:{}]", .{ payload.len, payload.sentinel }); + try writer.print("[{d}:{}]", .{ payload.len, payload.sentinel.fmtValue(payload.elem_type) }); ty = payload.elem_type; continue; }, @@ -1648,8 +1648,7 @@ pub const Type = extern union { } try field_ty.format("", .{}, writer); if (val.tag() != .unreachable_value) { - try writer.writeAll(" = "); - try val.format("", .{}, writer); + try writer.print(" = {}", .{val.fmtValue(field_ty)}); } } try writer.writeAll("}"); @@ -1668,8 +1667,7 @@ pub const Type = extern union { try writer.writeAll(": "); try field_ty.format("", .{}, writer); if (val.tag() != .unreachable_value) { - try writer.writeAll(" = "); - try val.format("", .{}, writer); + try writer.print(" = {}", .{val.fmtValue(field_ty)}); } } try writer.writeAll("}"); @@ -1754,8 +1752,8 @@ pub const Type = extern union { const payload = ty.castTag(.pointer).?.data; if (payload.sentinel) |some| switch (payload.size) { .One, .C => unreachable, - .Many => try writer.print("[*:{}]", .{some}), - .Slice => try writer.print("[:{}]", .{some}), + .Many => try writer.print("[*:{}]", .{some.fmtValue(payload.pointee_type)}), + .Slice => try writer.print("[:{}]", .{some.fmtValue(payload.pointee_type)}), } else switch (payload.size) { .One => try writer.writeAll("*"), .Many => try writer.writeAll("[*]"), diff --git a/src/value.zig b/src/value.zig index 269b13b099..76ab20c7ab 100644 --- a/src/value.zig +++ b/src/value.zig @@ -600,9 +600,17 @@ pub const Value = extern union { return Value{ .ptr_otherwise = &new_payload.base }; } + pub fn format(val: Value, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { + _ = val; + _ = fmt; + _ = options; + _ = writer; + @compileError("do not use format values directly; use either fmtDebug or fmtValue"); + } + /// TODO this should become a debug dump() function. In order to print values in a meaningful way /// we also need access to the type. - pub fn format( + pub fn dump( start_val: Value, comptime fmt: []const u8, options: std.fmt.FormatOptions, @@ -766,6 +774,16 @@ pub const Value = extern union { }; } + pub fn fmtDebug(val: Value) std.fmt.Formatter(dump) { + return .{ .data = val }; + } + + const TypedValue = @import("TypedValue.zig"); + + pub fn fmtValue(val: Value, ty: Type) std.fmt.Formatter(TypedValue.format) { + return .{ .data = .{ .ty = ty, .val = val } }; + } + /// Asserts that the value is representable as an array of bytes. /// Copies the value into a freshly allocated slice of memory, which is owned by the caller. pub fn toAllocatedBytes(val: Value, ty: Type, allocator: Allocator) ![]u8 { From 59668fbe802be744c200ea9fd38fda28f5f951c1 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 21 Mar 2022 15:05:21 +0200 Subject: [PATCH 0835/2031] stage2: add test for fixed issue Closes #11157 --- test/behavior/comptime_memory.zig | 50 ++++++++++++++++++------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/test/behavior/comptime_memory.zig b/test/behavior/comptime_memory.zig index 24a774aeb6..17cee27771 100644 --- a/test/behavior/comptime_memory.zig +++ b/test/behavior/comptime_memory.zig @@ -5,10 +5,8 @@ const ptr_size = @sizeOf(usize); test "type pun signed and unsigned as single pointer" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend != .stage1) { - // TODO https://github.com/ziglang/zig/issues/9646 - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO comptime { var x: u32 = 0; @@ -20,10 +18,7 @@ test "type pun signed and unsigned as single pointer" { test "type pun signed and unsigned as many pointer" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend != .stage1) { - // TODO https://github.com/ziglang/zig/issues/9646 - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO comptime { var x: u32 = 0; @@ -35,10 +30,7 @@ test "type pun signed and unsigned as many pointer" { test "type pun signed and unsigned as array pointer" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend != .stage1) { - // TODO https://github.com/ziglang/zig/issues/9646 - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO comptime { var x: u32 = 0; @@ -82,10 +74,7 @@ test "type pun signed and unsigned as array pointer" { test "type pun value and struct" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend != .stage1) { - // TODO https://github.com/ziglang/zig/issues/9646 - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO comptime { const StructOfU32 = extern struct { x: u32 }; @@ -102,10 +91,8 @@ fn bigToNativeEndian(comptime T: type, v: T) T { } test "type pun endianness" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend != .stage1) { - // TODO https://github.com/ziglang/zig/issues/9646 - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO comptime { const StructOfBytes = extern struct { x: [4]u8 }; @@ -412,3 +399,26 @@ test "offset field ptr by enclosing array element size" { } } } + +test "accessing reinterpreted memory of parent object" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = extern struct { + a: f32, + b: [4]u8, + c: f32, + }; + const expected = if (endian == .Little) 102 else 38; + + comptime { + const x = S{ + .a = 1.5, + .b = [_]u8{ 1, 2, 3, 4 }, + .c = 2.6, + }; + const ptr = &x.b[0]; + const b = @ptrCast([*c]const u8, ptr)[5]; + try testing.expect(b == expected); + } +} From 0577069af5f5deb859762725736537d60c324453 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 21 Mar 2022 13:25:57 +0200 Subject: [PATCH 0836/2031] stage2 llvm: fix lowerDeclRefValue for function aliases --- lib/std/fmt.zig | 1 - lib/std/os/linux.zig | 9 ++------- src/codegen/llvm.zig | 11 +++++++++++ test/behavior.zig | 1 + test/behavior/bugs/11227.zig | 13 +++++++++++++ test/behavior/bugs/1277.zig | 9 +++++++-- 6 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 test/behavior/bugs/11227.zig diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 1f76d90452..8ccb337cc4 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -762,7 +762,6 @@ fn formatFloatValue( if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) { formatFloatScientific(value, options, buf_stream.writer()) catch |err| switch (err) { error.NoSpaceLeft => unreachable, - else => |e| return e, }; } else if (comptime std.mem.eql(u8, fmt, "d")) { formatFloatDecimal(value, options, buf_stream.writer()) catch |err| switch (err) { diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 99c122075b..2d001414e7 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1080,13 +1080,8 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact const mask_size = @sizeOf(@TypeOf(ksa.mask)); if (act) |new| { - const restore_rt_ptr = if (builtin.zig_backend == .stage1) restore_rt else &syscall_bits.restore_rt; - // TODO https://github.com/ziglang/zig/issues/11227 - const restore_ptr = if (builtin.zig_backend == .stage1) restore else switch (native_arch) { - .arm, .thumb, .mips, .mipsel, .i386 => &syscall_bits.restore, - .x86_64, .aarch64, .riscv64, .sparcv9, .powerpc, .powerpc64, .powerpc64le => &syscall_bits.restore_rt, - else => unreachable, - }; + const restore_rt_ptr = if (builtin.zig_backend == .stage1) restore_rt else &restore_rt; + const restore_ptr = if (builtin.zig_backend == .stage1) restore else &restore; const restorer_fn = if ((new.flags & SA.SIGINFO) != 0) restore_rt_ptr else restore_ptr; ksa = k_sigaction{ .handler = new.handler.handler, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 2d5b38eb17..3837679b0a 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3073,6 +3073,17 @@ pub const DeclGen = struct { return self.context.constStruct(&fields, fields.len, .False); } + // In the case of something like: + // fn foo() void {} + // const bar = foo; + // ... &bar; + // `bar` is just an alias and we actually want to lower a reference to `foo`. + if (decl.val.castTag(.function)) |func| { + if (func.data.owner_decl != decl) { + return self.lowerDeclRefValue(tv, func.data.owner_decl); + } + } + const is_fn_body = decl.ty.zigTypeTag() == .Fn; if (!is_fn_body and !decl.ty.hasRuntimeBitsIgnoreComptime()) { return self.lowerPtrToVoid(tv.ty); diff --git a/test/behavior.zig b/test/behavior.zig index 03152b47ad..d413529a50 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -161,6 +161,7 @@ test { builtin.zig_backend != .stage2_wasm and builtin.zig_backend != .stage2_c) { + _ = @import("behavior/bugs/11227.zig"); _ = @import("behavior/export.zig"); _ = @import("behavior/export_self_referential_type_info.zig"); } diff --git a/test/behavior/bugs/11227.zig b/test/behavior/bugs/11227.zig new file mode 100644 index 0000000000..2699b54247 --- /dev/null +++ b/test/behavior/bugs/11227.zig @@ -0,0 +1,13 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +fn foo() u32 { + return 11227; +} +const bar = foo; +test "pointer to alias behaves same as pointer to function" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 has different function pointers + + var a = &bar; + try std.testing.expect(foo() == a()); +} diff --git a/test/behavior/bugs/1277.zig b/test/behavior/bugs/1277.zig index 46fa1d27d4..595a8273f8 100644 --- a/test/behavior/bugs/1277.zig +++ b/test/behavior/bugs/1277.zig @@ -2,15 +2,20 @@ const std = @import("std"); const builtin = @import("builtin"); const S = struct { - f: ?fn () i32, + f: ?*const fn () i32, }; -const s = S{ .f = f }; +const s = S{ .f = &f }; fn f() i32 { return 1234; } test "don't emit an LLVM global for a const function when it's in an optional in a struct" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 has different function pointers + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + try std.testing.expect(s.f.?() == 1234); } From 6d7808e647e6d244e53a18139c22bce8cd38a3ca Mon Sep 17 00:00:00 2001 From: mparadinha Date: Fri, 18 Mar 2022 03:33:18 +0000 Subject: [PATCH 0837/2031] stage2: x86_64: implement `ptr_elem_val` The codegen for this is almost identical to `ptr_elem_ptr` except there's an extra `mov` at the end to replace the pointer with the value it points to, "in-place" (which can be done in a single instruction without any extra registers). --- src/arch/x86_64/CodeGen.zig | 42 +++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 37d93f870c..65b7b73f39 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2174,10 +2174,44 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { 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; - const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) - .dead - else - return self.fail("TODO implement ptr_elem_val for {}", .{self.target.cpu.arch}); + const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else result: { + // this is identical to the `airPtrElemPtr` codegen expect here an + // additional `mov` is needed at the end to get the actual value + + const ptr_ty = self.air.typeOf(bin_op.lhs); + const ptr = try self.resolveInst(bin_op.lhs); + ptr.freezeIfRegister(&self.register_manager); + defer ptr.unfreezeIfRegister(&self.register_manager); + + const elem_ty = ptr_ty.elemType2(); + const elem_abi_size = elem_ty.abiSize(self.target.*); + const index_ty = self.air.typeOf(bin_op.rhs); + const index = try self.resolveInst(bin_op.rhs); + index.freezeIfRegister(&self.register_manager); + defer index.unfreezeIfRegister(&self.register_manager); + + const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); + self.register_manager.freezeRegs(&.{offset_reg}); + defer self.register_manager.unfreezeRegs(&.{offset_reg}); + + const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); + try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); + if (elem_abi_size > 8) { + return self.fail("TODO copy value with size {} from pointer", .{elem_abi_size}); + } else { + // mov dst_mcv, [dst_mcv] + _ = try self.addInst(.{ + .tag = .mov, + .ops = (Mir.Ops{ + .flags = 0b01, + .reg1 = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)), + .reg2 = dst_mcv.register, + }).encode(), + .data = .{ .imm = 0 }, + }); + break :result .{ .register = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)) }; + } + }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } From 35eaaed7c4fa5ca16c3a593e2bb479163e4c56d8 Mon Sep 17 00:00:00 2001 From: mparadinha Date: Fri, 18 Mar 2022 19:38:06 +0000 Subject: [PATCH 0838/2031] stage2: x86_64: use correct register size when loading things from memory --- src/arch/x86_64/CodeGen.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 65b7b73f39..70f644b9c7 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -953,7 +953,8 @@ pub fn spillCompareFlagsIfOccupied(self: *Self) !void { /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { const reg = try self.register_manager.allocReg(null); - try self.genSetReg(ty, reg, mcv); + const sized_reg = registerAlias(reg, @intCast(u32, ty.abiSize(self.target.*))); + try self.genSetReg(ty, sized_reg, mcv); return reg; } @@ -5337,7 +5338,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void _ = try self.addInst(.{ .tag = .mov, .ops = (Mir.Ops{ - .reg1 = reg.to64(), + .reg1 = reg, .reg2 = reg.to64(), .flags = 0b01, }).encode(), From 79e2d4b3f6d759c8d9689d05e880c387daabe3b1 Mon Sep 17 00:00:00 2001 From: mparadinha Date: Fri, 18 Mar 2022 21:23:07 +0000 Subject: [PATCH 0839/2031] stage2: x86_64: update passing tests after implementing ptr_elem_val the 3 tests that called `testArray2DConstDoublePtr` started passing after implementing `ptr_elem_val`. the rest of these I think were already passing before. --- test/behavior/basic.zig | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index bee68d675c..9931c10f9a 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -382,7 +382,6 @@ fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA { test "take address of parameter" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; try testTakeAddressOfParameter(12.34); } @@ -408,7 +407,6 @@ fn testPointerToVoidReturnType2() *const void { test "array 2D const double ptr" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const rect_2d_vertexes = [_][1]f32{ @@ -421,7 +419,6 @@ test "array 2D const double ptr" { test "array 2D const double ptr with offset" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; const rect_2d_vertexes = [_][2]f32{ @@ -434,7 +431,6 @@ test "array 2D const double ptr with offset" { test "array 3D const double ptr with offset" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const rect_3d_vertexes = [_][2][2]f32{ @@ -519,7 +515,6 @@ var global_foo: *i32 = undefined; test "peer result location with typed parent, runtime condition, comptime prongs" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const S = struct { fn doTheTest(arg: i32) i32 { @@ -620,7 +615,6 @@ test "self reference through fn ptr field" { test "global variable initialized to global variable array element" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; @@ -655,7 +649,6 @@ test "global constant is loaded with a runtime-known index" { test "multiline string literal is null terminated" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const s1 = \\one From 00e2113c8b07f9d1c67c7c70b69e7b9e6343b2d9 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 21 Mar 2022 23:38:01 +0100 Subject: [PATCH 0840/2031] x64: refactor fix reg aliasing in genSetReg --- src/arch/x86_64/CodeGen.zig | 13 ++++++------- test/behavior/eval.zig | 1 + 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 70f644b9c7..ce049a4541 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -953,8 +953,7 @@ pub fn spillCompareFlagsIfOccupied(self: *Self) !void { /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { const reg = try self.register_manager.allocReg(null); - const sized_reg = registerAlias(reg, @intCast(u32, ty.abiSize(self.target.*))); - try self.genSetReg(ty, sized_reg, mcv); + try self.genSetReg(ty, reg, mcv); return reg; } @@ -5201,7 +5200,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void if (!self.wantSafety()) return; // The already existing value will do just fine. // Write the debug undefined value. - switch (reg.size()) { + switch (registerAlias(reg, abi_size).size()) { 8 => return self.genSetReg(ty, reg, .{ .immediate = 0xaa }), 16 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaa }), 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }), @@ -5338,7 +5337,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void _ = try self.addInst(.{ .tag = .mov, .ops = (Mir.Ops{ - .reg1 = reg, + .reg1 = registerAlias(reg, abi_size), .reg2 = reg.to64(), .flags = 0b01, }).encode(), @@ -5351,7 +5350,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void _ = try self.addInst(.{ .tag = .mov, .ops = (Mir.Ops{ - .reg1 = reg, + .reg1 = registerAlias(reg, abi_size), .flags = 0b01, }).encode(), .data = .{ .imm = @truncate(u32, x) }, @@ -5378,8 +5377,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void _ = try self.addInst(.{ .tag = .mov, .ops = (Mir.Ops{ - .reg1 = reg, - .reg2 = reg, + .reg1 = registerAlias(reg, abi_size), + .reg2 = reg.to64(), .flags = 0b01, }).encode(), .data = .{ .imm = 0 }, diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index c02bb9daa9..cce78b1944 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -424,6 +424,7 @@ test "f64 at compile time is lossy" { } test { + if (builtin.zig_backend != .stage1 and builtin.os.tag == .macos) return error.SkipZigTest; comptime try expect(@as(f128, 1 << 113) == 10384593717069655257060992658440192); } From 2f4473b6536ee43e51a17b02d8fad7518ab32c3b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 22 Mar 2022 00:03:13 +0100 Subject: [PATCH 0841/2031] macho: add more codesig constants --- lib/std/macho.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/std/macho.zig b/lib/std/macho.zig index 81d9bd0c47..0abe14f4a4 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -1661,6 +1661,8 @@ pub const CSMAGIC_EMBEDDED_SIGNATURE: u32 = 0xfade0cc0; pub const CSMAGIC_EMBEDDED_SIGNATURE_OLD: u32 = 0xfade0b02; /// Embedded entitlements pub const CSMAGIC_EMBEDDED_ENTITLEMENTS: u32 = 0xfade7171; +/// Embedded DER encoded entitlements +pub const CSMAGIC_EMBEDDED_DER_ENTITLEMENTS: u32 = 0xfade7172; /// Multi-arch collection of embedded signatures pub const CSMAGIC_DETACHED_SIGNATURE: u32 = 0xfade0cc1; /// CMS Signature, among other things @@ -1678,6 +1680,7 @@ pub const CSSLOT_REQUIREMENTS: u32 = 2; pub const CSSLOT_RESOURCEDIR: u32 = 3; pub const CSSLOT_APPLICATION: u32 = 4; pub const CSSLOT_ENTITLEMENTS: u32 = 5; +pub const CSSLOT_DER_ENTITLEMENTS: u32 = 7; /// first alternate CodeDirectory, if any pub const CSSLOT_ALTERNATE_CODEDIRECTORIES: u32 = 0x1000; From cda8f65489af099a5a93a7475f344c615678aa26 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 19 Mar 2022 19:38:43 -0700 Subject: [PATCH 0842/2031] behavior tests: use `expect` instead of `expectEqual` in vector.zig --- test/behavior/vector.zig | 72 +++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 7efd018a65..697969f42e 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -3,8 +3,6 @@ const builtin = @import("builtin"); const mem = std.mem; const math = std.math; const expect = std.testing.expect; -const expectEqual = std.testing.expectEqual; -const expectApproxEqRel = std.testing.expectApproxEqRel; const Vector = std.meta.Vector; test "implicit cast vector to array - bool" { @@ -118,10 +116,16 @@ test "implicit cast vector to array" { test "array to vector" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - var foo: f32 = 3.14; - var arr = [4]f32{ foo, 1.5, 0.0, 0.0 }; - var vec: Vector(4, f32) = arr; - _ = vec; + const S = struct { + fn doTheTest() !void { + var foo: f32 = 3.14; + var arr = [4]f32{ foo, 1.5, 0.0, 0.0 }; + var vec: Vector(4, f32) = arr; + try expect(mem.eql(f32, &@as([4]f32, vec), &arr)); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); } test "vector casts of sizes not divisible by 8" { @@ -160,9 +164,9 @@ test "vector @splat" { fn testForT(comptime N: comptime_int, v: anytype) !void { const T = @TypeOf(v); var vec = @splat(N, v); - try expectEqual(Vector(N, T), @TypeOf(vec)); + try expect(Vector(N, T) == @TypeOf(vec)); var as_array = @as([N]T, vec); - for (as_array) |elem| try expectEqual(v, elem); + for (as_array) |elem| try expect(v == elem); } fn doTheTest() !void { // Splats with multiple-of-8 bit types that fill a 128bit vector. @@ -294,27 +298,27 @@ test "vector comparison operators" { { const v1: Vector(4, bool) = [_]bool{ true, false, true, false }; const v2: Vector(4, bool) = [_]bool{ false, true, false, true }; - try expectEqual(@splat(4, true), v1 == v1); - try expectEqual(@splat(4, false), v1 == v2); - try expectEqual(@splat(4, true), v1 != v2); - try expectEqual(@splat(4, false), v2 != v2); + try expect(mem.eql(bool, &@as([4]bool, @splat(4, true)), &@as([4]bool, v1 == v1))); + try expect(mem.eql(bool, &@as([4]bool, @splat(4, false)), &@as([4]bool, v1 == v2))); + try expect(mem.eql(bool, &@as([4]bool, @splat(4, true)), &@as([4]bool, v1 != v2))); + try expect(mem.eql(bool, &@as([4]bool, @splat(4, false)), &@as([4]bool, v2 != v2))); } { const v1 = @splat(4, @as(u32, 0xc0ffeeee)); const v2: Vector(4, c_uint) = v1; const v3 = @splat(4, @as(u32, 0xdeadbeef)); - try expectEqual(@splat(4, true), v1 == v2); - try expectEqual(@splat(4, false), v1 == v3); - try expectEqual(@splat(4, true), v1 != v3); - try expectEqual(@splat(4, false), v1 != v2); + try expect(mem.eql(bool, &@as([4]bool, @splat(4, true)), &@as([4]bool, v1 == v2))); + try expect(mem.eql(bool, &@as([4]bool, @splat(4, false)), &@as([4]bool, v1 == v3))); + try expect(mem.eql(bool, &@as([4]bool, @splat(4, true)), &@as([4]bool, v1 != v3))); + try expect(mem.eql(bool, &@as([4]bool, @splat(4, false)), &@as([4]bool, v1 != v2))); } { // Comptime-known LHS/RHS var v1: @Vector(4, u32) = [_]u32{ 2, 1, 2, 1 }; const v2 = @splat(4, @as(u32, 2)); const v3: @Vector(4, bool) = [_]bool{ true, false, true, false }; - try expectEqual(v3, v1 == v2); - try expectEqual(v3, v2 == v1); + try expect(mem.eql(bool, &@as([4]bool, v3), &@as([4]bool, v1 == v2))); + try expect(mem.eql(bool, &@as([4]bool, v3), &@as([4]bool, v2 == v1))); } } }; @@ -329,20 +333,20 @@ test "vector division operators" { if (!comptime std.meta.trait.isSignedInt(T)) { const d0 = x / y; for (@as([4]T, d0)) |v, i| { - try expectEqual(x[i] / y[i], v); + try expect(x[i] / y[i] == v); } } const d1 = @divExact(x, y); for (@as([4]T, d1)) |v, i| { - try expectEqual(@divExact(x[i], y[i]), v); + try expect(@divExact(x[i], y[i]) == v); } const d2 = @divFloor(x, y); for (@as([4]T, d2)) |v, i| { - try expectEqual(@divFloor(x[i], y[i]), v); + try expect(@divFloor(x[i], y[i]) == v); } const d3 = @divTrunc(x, y); for (@as([4]T, d3)) |v, i| { - try expectEqual(@divTrunc(x[i], y[i]), v); + try expect(@divTrunc(x[i], y[i]) == v); } } @@ -350,16 +354,16 @@ test "vector division operators" { if ((!comptime std.meta.trait.isSignedInt(T)) and @typeInfo(T) != .Float) { const r0 = x % y; for (@as([4]T, r0)) |v, i| { - try expectEqual(x[i] % y[i], v); + try expect(x[i] % y[i] == v); } } const r1 = @mod(x, y); for (@as([4]T, r1)) |v, i| { - try expectEqual(@mod(x[i], y[i]), v); + try expect(@mod(x[i], y[i]) == v); } const r2 = @rem(x, y); for (@as([4]T, r2)) |v, i| { - try expectEqual(@rem(x[i], y[i]), v); + try expect(@rem(x[i], y[i]) == v); } } @@ -411,7 +415,7 @@ test "vector bitwise not operator" { fn doTheTestNot(comptime T: type, x: Vector(4, T)) !void { var y = ~x; for (@as([4]T, y)) |v, i| { - try expectEqual(~x[i], v); + try expect(~x[i] == v); } } fn doTheTest() !void { @@ -444,11 +448,11 @@ test "vector shift operators" { var z0 = xv >> yv; for (@as([N]TX, z0)) |v, i| { - try expectEqual(x[i] >> y[i], v); + try expect(x[i] >> y[i] == v); } var z1 = xv << yv; for (@as([N]TX, z1)) |v, i| { - try expectEqual(x[i] << y[i], v); + try expect(x[i] << y[i] == v); } } fn doTheTestShiftExact(x: anytype, y: anytype, dir: enum { Left, Right }) !void { @@ -462,7 +466,7 @@ test "vector shift operators" { var z = if (dir == .Left) @shlExact(xv, yv) else @shrExact(xv, yv); for (@as([N]TX, z)) |v, i| { const check = if (dir == .Left) x[i] << y[i] else x[i] >> y[i]; - try expectEqual(check, v); + try expect(check == v); } } fn doTheTest() !void { @@ -681,9 +685,9 @@ test "saturating add" { const S = struct { fn doTheTest() !void { const u8x3 = std.meta.Vector(3, u8); - try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 254, 1 } +| u8x3{ 1, 2, 255 })); + try expect(mem.eql(u8, &@as([3]u8, u8x3{ 255, 255, 255 }), &@as([3]u8, u8x3{ 255, 254, 1 } +| u8x3{ 1, 2, 255 }))); const i8x3 = std.meta.Vector(3, i8); - try expectEqual(i8x3{ 127, 127, 127 }, (i8x3{ 127, 126, 1 } +| i8x3{ 1, 2, 127 })); + try expect(mem.eql(i8, &@as([3]i8, i8x3{ 127, 127, 127 }), &@as([3]i8, i8x3{ 127, 126, 1 } +| i8x3{ 1, 2, 127 }))); } }; try S.doTheTest(); @@ -695,7 +699,7 @@ test "saturating subtraction" { const S = struct { fn doTheTest() !void { const u8x3 = std.meta.Vector(3, u8); - try expectEqual(u8x3{ 0, 0, 0 }, (u8x3{ 0, 0, 0 } -| u8x3{ 255, 255, 255 })); + try expect(mem.eql(u8, &@as([3]u8, u8x3{ 0, 0, 0 }), &@as([3]u8, u8x3{ 0, 0, 0 } -| u8x3{ 255, 255, 255 }))); } }; try S.doTheTest(); @@ -710,7 +714,7 @@ test "saturating multiplication" { const S = struct { fn doTheTest() !void { const u8x3 = std.meta.Vector(3, u8); - try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 2, 2, 2 } *| u8x3{ 255, 255, 255 })); + try expect(mem.eql(u8, &@as([3]u8, u8x3{ 255, 255, 255 }), &@as([3]u8, u8x3{ 2, 2, 2 } *| u8x3{ 255, 255, 255 }))); } }; @@ -723,7 +727,7 @@ test "saturating shift-left" { const S = struct { fn doTheTest() !void { const u8x3 = std.meta.Vector(3, u8); - try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 255, 255 } <<| u8x3{ 1, 1, 1 })); + try expect(mem.eql(u8, &@as([3]u8, u8x3{ 255, 255, 255 }), &@as([3]u8, u8x3{ 255, 255, 255 } <<| u8x3{ 1, 1, 1 }))); } }; try S.doTheTest(); From 862e63f535ec8d65e33ed7ea67eb9cf03bfc7d6a Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 19 Mar 2022 23:33:43 -0700 Subject: [PATCH 0843/2031] stage2: fix typo in print_air.zig --- src/print_air.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/print_air.zig b/src/print_air.zig index fb970e3b08..f3ccc70a46 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -470,8 +470,8 @@ const Writer = struct { } fn writeFieldParentPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const pl_op = w.air.instructions.items(.data)[inst].ty_pl; - const extra = w.air.extraData(Air.FieldParentPtr, pl_op.payload).data; + const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; + const extra = w.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; try w.writeOperand(s, inst, 0, extra.field_ptr); try s.print(", {d}", .{extra.field_index}); From 0f4830704171b734bb4a0235fc14809282457dc3 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 19 Mar 2022 23:44:55 -0700 Subject: [PATCH 0844/2031] stage2: add AIR instruction `cmp_vector` The existing `cmp_*` instructions get their result type from `lhs`, but vector comparison will always return a vector of bools with only the length derived from its operands. This necessitates the creation of a new AIR instruction. --- src/Air.zig | 19 +++++++++++++++++++ src/Liveness.zig | 4 ++++ src/Sema.zig | 14 ++++++++++++++ src/arch/aarch64/CodeGen.zig | 6 ++++++ src/arch/arm/CodeGen.zig | 7 ++++++- src/arch/riscv64/CodeGen.zig | 6 ++++++ src/arch/wasm/CodeGen.zig | 6 ++++++ src/arch/x86_64/CodeGen.zig | 6 ++++++ src/codegen/c.zig | 2 ++ src/codegen/llvm.zig | 6 ++++++ src/print_air.zig | 11 +++++++++++ 11 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/Air.zig b/src/Air.zig index 2d717f442d..8c24108abd 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -308,6 +308,10 @@ pub const Inst = struct { /// `!=`. Result type is always bool. /// Uses the `bin_op` field. cmp_neq, + /// Conditional between two vectors. + /// Result type is always a vector of bools. + /// Uses the `ty_pl` field, payload is `VectorCmp`. + cmp_vector, /// Conditional branch. /// Result type is always noreturn; no instructions in a block follow this one. @@ -781,6 +785,20 @@ pub const Shuffle = struct { mask_len: u32, }; +pub const VectorCmp = struct { + lhs: Inst.Ref, + rhs: Inst.Ref, + op: u32, + + pub fn compareOperator(self: VectorCmp) std.math.CompareOperator { + return @intToEnum(std.math.CompareOperator, @truncate(u3, self.op)); + } + + pub fn encodeOp(compare_operator: std.math.CompareOperator) u32 { + return @enumToInt(compare_operator); + } +}; + /// Trailing: /// 0. `Inst.Ref` for every outputs_len /// 1. `Inst.Ref` for every inputs_len @@ -942,6 +960,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .aggregate_init, .union_init, .field_parent_ptr, + .cmp_vector, => return air.getRefType(datas[inst].ty_pl.ty), .not, diff --git a/src/Liveness.zig b/src/Liveness.zig index dd93d44f72..79521e7a94 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -441,6 +441,10 @@ fn analyzeInst( const reduce = inst_datas[inst].reduce; return trackOperands(a, new_set, inst, main_tomb, .{ reduce.operand, .none, .none }); }, + .cmp_vector => { + const extra = a.air.extraData(Air.VectorCmp, inst_datas[inst].ty_pl.payload).data; + return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none }); + }, .aggregate_init => { const ty_pl = inst_datas[inst].ty_pl; const aggregate_ty = a.air.getRefType(ty_pl.ty); diff --git a/src/Sema.zig b/src/Sema.zig index 7674121ba6..20622cb98a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -397,6 +397,20 @@ pub const Block = struct { }); } + fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator, vector_ty: Air.Inst.Ref) !Air.Inst.Ref { + return block.addInst(.{ + .tag = .cmp_vector, + .data = .{ .ty_pl = .{ + .ty = vector_ty, + .payload = try block.sema.addExtra(Air.VectorCmp{ + .lhs = lhs, + .rhs = rhs, + .op = Air.VectorCmp.encodeOp(cmp_op), + }), + } }, + }); + } + fn addAggregateInit( block: *Block, aggregate_ty: Type, diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index c9121c8859..9c2d5890a7 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -577,6 +577,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_gte => try self.airCmp(inst, .gte), .cmp_gt => try self.airCmp(inst, .gt), .cmp_neq => try self.airCmp(inst, .neq), + .cmp_vector => try self.airCmpVector(inst), .bool_and => try self.airBinOp(inst), .bool_or => try self.airBinOp(inst), @@ -2713,6 +2714,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { + _ = inst; + return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); +} + fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 921bc92c72..0b9f9eb2e2 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -567,6 +567,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_gte => try self.airCmp(inst, .gte), .cmp_gt => try self.airCmp(inst, .gt), .cmp_neq => try self.airCmp(inst, .neq), + .cmp_vector => try self.airCmpVector(inst), .bool_and => try self.airBinOp(inst), .bool_or => try self.airBinOp(inst), @@ -2894,7 +2895,6 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { const lhs_ty = self.air.typeOf(bin_op.lhs); switch (lhs_ty.zigTypeTag()) { - .Vector => return self.fail("TODO ARM cmp vectors", .{}), .Optional => return self.fail("TODO ARM cmp optionals", .{}), .Float => return self.fail("TODO ARM cmp floats", .{}), .Int, .Bool, .Pointer, .ErrorSet, .Enum => { @@ -2929,6 +2929,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { + _ = inst; + return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); +} + fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 14beedd2a9..3b73fef51f 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -537,6 +537,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_gte => try self.airCmp(inst, .gte), .cmp_gt => try self.airCmp(inst, .gt), .cmp_neq => try self.airCmp(inst, .neq), + .cmp_vector => try self.airCmpVector(inst), .bool_and => try self.airBoolOp(inst), .bool_or => try self.airBoolOp(inst), @@ -1791,6 +1792,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { // return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { + _ = inst; + return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); +} + fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index c60c321572..8d515e9365 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1309,6 +1309,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .cmp_lte => self.airCmp(inst, .lte), .cmp_lt => self.airCmp(inst, .lt), .cmp_neq => self.airCmp(inst, .neq), + .cmp_vector => self.airCmpVector(inst), .array_elem_val => self.airArrayElemVal(inst), .array_to_slice => self.airArrayToSlice(inst), @@ -2222,6 +2223,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner return cmp_tmp; } +fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + _ = inst; + return self.fail("TODO implement airCmpVector for wasm", .{}); +} + fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const br = self.air.instructions.items(.data)[inst].br; const block = self.blocks.get(br.block_inst).?; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 72f12cf4e9..598511a631 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -658,6 +658,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_gte => try self.airCmp(inst, .gte), .cmp_gt => try self.airCmp(inst, .gt), .cmp_neq => try self.airCmp(inst, .neq), + .cmp_vector => try self.airCmpVector(inst), .bool_and => try self.airBoolOp(inst), .bool_or => try self.airBoolOp(inst), @@ -3699,6 +3700,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { + _ = inst; + return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); +} + fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; const payload = try self.addExtra(Mir.DbgLineColumn{ diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 23ccdc007b..f5a1036479 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1715,6 +1715,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .cmp_eq => try airEquality(f, inst, "((", "=="), .cmp_neq => try airEquality(f, inst, "!((", "!="), + .cmp_vector => return f.fail("TODO: C backend: implement binary op for tag '{s}'", .{@tagName(Air.Inst.Tag.cmp_vector)}), + // bool_and and bool_or are non-short-circuit operations .bool_and => try airBinOp(f, inst, " & "), .bool_or => try airBinOp(f, inst, " | "), diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 139c1f25cb..edf8992c56 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3375,6 +3375,7 @@ pub const FuncGen = struct { .cmp_lt => try self.airCmp(inst, .lt), .cmp_lte => try self.airCmp(inst, .lte), .cmp_neq => try self.airCmp(inst, .neq), + .cmp_vector => try self.airCmpVector(inst), .is_non_null => try self.airIsNonNull(inst, false, false, .NE), .is_non_null_ptr => try self.airIsNonNull(inst, true , false, .NE), @@ -3640,6 +3641,11 @@ pub const FuncGen = struct { return self.cmp(lhs, rhs, operand_ty, op); } + fn airCmpVector(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + _ = inst; + return self.todo("implement airCmpVector"); + } + fn cmp( self: *FuncGen, lhs: *const llvm.Value, diff --git a/src/print_air.zig b/src/print_air.zig index f3ccc70a46..c6b27a6cd6 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -266,6 +266,7 @@ const Writer = struct { .mul_add => try w.writeMulAdd(s, inst), .shuffle => try w.writeShuffle(s, inst), .reduce => try w.writeReduce(s, inst), + .cmp_vector => try w.writeCmpVector(s, inst), .add_with_overflow, .sub_with_overflow, @@ -402,6 +403,16 @@ const Writer = struct { try s.print(", {s}", .{@tagName(reduce.operation)}); } + fn writeCmpVector(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.VectorCmp, ty_pl.payload).data; + + try s.print("{s}, ", .{@tagName(extra.compareOperator())}); + try w.writeOperand(s, inst, 0, extra.lhs); + try s.writeAll(", "); + try w.writeOperand(s, inst, 1, extra.rhs); + } + fn writeFence(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const atomic_order = w.air.instructions.items(.data)[inst].fence; From b96699059c6224b0a425f2fe5010469ef6049cad Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 19 Mar 2022 23:49:53 -0700 Subject: [PATCH 0845/2031] stage2: implement `cmp_vector` for LLVM backend --- src/codegen/llvm.zig | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index edf8992c56..a90a2eeda3 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3642,8 +3642,17 @@ pub const FuncGen = struct { } fn airCmpVector(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { - _ = inst; - return self.todo("implement airCmpVector"); + if (self.liveness.isUnused(inst)) return null; + + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.VectorCmp, ty_pl.payload).data; + + const lhs = try self.resolveInst(extra.lhs); + const rhs = try self.resolveInst(extra.rhs); + const vec_ty = self.air.typeOf(extra.lhs); + const cmp_op = extra.compareOperator(); + + return self.cmp(lhs, rhs, vec_ty, cmp_op); } fn cmp( @@ -3656,9 +3665,10 @@ pub const FuncGen = struct { var int_buffer: Type.Payload.Bits = undefined; var opt_buffer: Type.Payload.ElemType = undefined; - const int_ty = switch (operand_ty.zigTypeTag()) { - .Enum => operand_ty.intTagType(&int_buffer), - .Int, .Bool, .Pointer, .ErrorSet => operand_ty, + const scalar_ty = operand_ty.scalarType(); + const int_ty = switch (scalar_ty.zigTypeTag()) { + .Enum => scalar_ty.intTagType(&int_buffer), + .Int, .Bool, .Pointer, .ErrorSet => scalar_ty, .Optional => blk: { const payload_ty = operand_ty.optionalChild(&opt_buffer); if (!payload_ty.hasRuntimeBitsIgnoreComptime() or operand_ty.isPtrLikeOptional()) { From 961248cde32e0d572225c6c1d367b960540ae220 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 19 Mar 2022 23:57:29 -0700 Subject: [PATCH 0846/2031] stage2: make more instructions vector-compatible in LLVM backend --- src/codegen/llvm.zig | 119 +++++++++++++++++++++++++++++-------------- 1 file changed, 80 insertions(+), 39 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a90a2eeda3..13f4f7d01b 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3960,10 +3960,11 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); const operand_ty = self.air.typeOf(ty_op.operand); + const operand_scalar_ty = operand_ty.scalarType(); const dest_ty = self.air.typeOfIndex(inst); const dest_llvm_ty = try self.dg.llvmType(dest_ty); - if (operand_ty.isSignedInt()) { + if (operand_scalar_ty.isSignedInt()) { return self.builder.buildSIToFP(operand, dest_llvm_ty, ""); } else { return self.builder.buildUIToFP(operand, dest_llvm_ty, ""); @@ -3977,11 +3978,12 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); const dest_ty = self.air.typeOfIndex(inst); + const dest_scalar_ty = dest_ty.scalarType(); const dest_llvm_ty = try self.dg.llvmType(dest_ty); // TODO set fast math flag - if (dest_ty.isSignedInt()) { + if (dest_scalar_ty.isSignedInt()) { return self.builder.buildFPToSI(operand, dest_llvm_ty, ""); } else { return self.builder.buildFPToUI(operand, dest_llvm_ty, ""); @@ -4912,9 +4914,10 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isAnyFloat()) return self.builder.buildFAdd(lhs, rhs, ""); - if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, ""); + if (scalar_ty.isAnyFloat()) return self.builder.buildFAdd(lhs, rhs, ""); + if (scalar_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, ""); return self.builder.buildNUWAdd(lhs, rhs, ""); } @@ -4935,9 +4938,10 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isAnyFloat()) return self.todo("saturating float add", .{}); - if (inst_ty.isSignedInt()) return self.builder.buildSAddSat(lhs, rhs, ""); + if (scalar_ty.isAnyFloat()) return self.todo("saturating float add", .{}); + if (scalar_ty.isSignedInt()) return self.builder.buildSAddSat(lhs, rhs, ""); return self.builder.buildUAddSat(lhs, rhs, ""); } @@ -4949,9 +4953,10 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isAnyFloat()) return self.builder.buildFSub(lhs, rhs, ""); - if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, ""); + if (scalar_ty.isAnyFloat()) return self.builder.buildFSub(lhs, rhs, ""); + if (scalar_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, ""); return self.builder.buildNUWSub(lhs, rhs, ""); } @@ -4972,9 +4977,10 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isAnyFloat()) return self.todo("saturating float sub", .{}); - if (inst_ty.isSignedInt()) return self.builder.buildSSubSat(lhs, rhs, ""); + if (scalar_ty.isAnyFloat()) return self.todo("saturating float sub", .{}); + if (scalar_ty.isSignedInt()) return self.builder.buildSSubSat(lhs, rhs, ""); return self.builder.buildUSubSat(lhs, rhs, ""); } @@ -4985,9 +4991,10 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isAnyFloat()) return self.builder.buildFMul(lhs, rhs, ""); - if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, ""); + if (scalar_ty.isAnyFloat()) return self.builder.buildFMul(lhs, rhs, ""); + if (scalar_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, ""); return self.builder.buildNUWMul(lhs, rhs, ""); } @@ -5008,9 +5015,10 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isAnyFloat()) return self.todo("saturating float mul", .{}); - if (inst_ty.isSignedInt()) return self.builder.buildSMulFixSat(lhs, rhs, ""); + if (scalar_ty.isAnyFloat()) return self.todo("saturating float mul", .{}); + if (scalar_ty.isSignedInt()) return self.builder.buildSMulFixSat(lhs, rhs, ""); return self.builder.buildUMulFixSat(lhs, rhs, ""); } @@ -5031,12 +5039,13 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isRuntimeFloat()) { + if (scalar_ty.isRuntimeFloat()) { const result = self.builder.buildFDiv(lhs, rhs, ""); return self.callTrunc(result, inst_ty); } - if (inst_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, ""); + if (scalar_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, ""); return self.builder.buildUDiv(lhs, rhs, ""); } @@ -5047,12 +5056,13 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isRuntimeFloat()) { + if (scalar_ty.isRuntimeFloat()) { const result = self.builder.buildFDiv(lhs, rhs, ""); return try self.callFloor(result, inst_ty); } - if (inst_ty.isSignedInt()) { + if (scalar_ty.isSignedInt()) { // const d = @divTrunc(a, b); // const r = @rem(a, b); // return if (r == 0) d else d - ((a < 0) ^ (b < 0)); @@ -5078,9 +5088,10 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, ""); - if (inst_ty.isSignedInt()) return self.builder.buildExactSDiv(lhs, rhs, ""); + if (scalar_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, ""); + if (scalar_ty.isSignedInt()) return self.builder.buildExactSDiv(lhs, rhs, ""); return self.builder.buildExactUDiv(lhs, rhs, ""); } @@ -5091,9 +5102,10 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isRuntimeFloat()) return self.builder.buildFRem(lhs, rhs, ""); - if (inst_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, ""); + if (scalar_ty.isRuntimeFloat()) return self.builder.buildFRem(lhs, rhs, ""); + if (scalar_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, ""); return self.builder.buildURem(lhs, rhs, ""); } @@ -5105,8 +5117,9 @@ pub const FuncGen = struct { const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); const inst_llvm_ty = try self.dg.llvmType(inst_ty); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isRuntimeFloat()) { + if (scalar_ty.isRuntimeFloat()) { const a = self.builder.buildFRem(lhs, rhs, ""); const b = self.builder.buildFAdd(a, rhs, ""); const c = self.builder.buildFRem(b, rhs, ""); @@ -5114,7 +5127,7 @@ pub const FuncGen = struct { const ltz = self.builder.buildFCmp(.OLT, lhs, zero, ""); return self.builder.buildSelect(ltz, c, a, ""); } - if (inst_ty.isSignedInt()) { + if (scalar_ty.isSignedInt()) { const a = self.builder.buildSRem(lhs, rhs, ""); const b = self.builder.buildNSWAdd(a, rhs, ""); const c = self.builder.buildSRem(b, rhs, ""); @@ -5339,15 +5352,22 @@ pub const FuncGen = struct { if (self.liveness.isUnused(inst)) return null; const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const lhs_type = self.air.typeOf(bin_op.lhs); + + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + const lhs_scalar_ty = lhs_ty.scalarType(); + const rhs_scalar_ty = rhs_ty.scalarType(); + const tg = self.dg.module.getTarget(); - const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg)) - self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "") + + const casted_rhs = if (rhs_scalar_ty.bitSize(tg) < lhs_scalar_ty.bitSize(tg)) + self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_ty), "") else rhs; - if (lhs_type.isSignedInt()) return self.builder.buildNSWShl(lhs, casted_rhs, ""); + if (lhs_scalar_ty.isSignedInt()) return self.builder.buildNSWShl(lhs, casted_rhs, ""); return self.builder.buildNUWShl(lhs, casted_rhs, ""); } @@ -5355,11 +5375,18 @@ pub const FuncGen = struct { if (self.liveness.isUnused(inst)) return null; const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); + const lhs_type = self.air.typeOf(bin_op.lhs); + const rhs_type = self.air.typeOf(bin_op.rhs); + const lhs_scalar_ty = lhs_type.scalarType(); + const rhs_scalar_ty = rhs_type.scalarType(); + const tg = self.dg.module.getTarget(); - const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg)) + + const casted_rhs = if (rhs_scalar_ty.bitSize(tg) < lhs_scalar_ty.bitSize(tg)) self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "") else rhs; @@ -5370,31 +5397,45 @@ pub const FuncGen = struct { if (self.liveness.isUnused(inst)) return null; const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const lhs_type = self.air.typeOf(bin_op.lhs); + + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + const lhs_scalar_ty = lhs_ty.scalarType(); + const rhs_scalar_ty = rhs_ty.scalarType(); + const tg = self.dg.module.getTarget(); - const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg)) - self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "") + + const casted_rhs = if (rhs_scalar_ty.bitSize(tg) < lhs_scalar_ty.bitSize(tg)) + self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_ty), "") else rhs; - if (lhs_type.isSignedInt()) return self.builder.buildSShlSat(lhs, casted_rhs, ""); + if (lhs_scalar_ty.isSignedInt()) return self.builder.buildSShlSat(lhs, casted_rhs, ""); return self.builder.buildUShlSat(lhs, casted_rhs, ""); } fn airShr(self: *FuncGen, inst: Air.Inst.Index, is_exact: bool) !?*const llvm.Value { - if (self.liveness.isUnused(inst)) - return null; + if (self.liveness.isUnused(inst)) return null; + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const lhs_type = self.air.typeOf(bin_op.lhs); + + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + const lhs_scalar_ty = lhs_ty.scalarType(); + const rhs_scalar_ty = rhs_ty.scalarType(); + const tg = self.dg.module.getTarget(); - const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg)) - self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "") + + const casted_rhs = if (rhs_scalar_ty.bitSize(tg) < lhs_scalar_ty.bitSize(tg)) + self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_ty), "") else rhs; - const is_signed_int = self.air.typeOfIndex(inst).isSignedInt(); + const is_signed_int = lhs_scalar_ty.isSignedInt(); if (is_exact) { if (is_signed_int) { From 4e357151a54c9a2f373cef59e2718a61cfbe3658 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sun, 20 Mar 2022 00:00:13 -0700 Subject: [PATCH 0847/2031] stage2: align store for vector-to-array bitcast in LLVM backend This was causing a very rare segfault when LLVM would emit `vmovdqa` using an unaligned memory operand on the stack. --- src/codegen/llvm.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 13f4f7d01b..107b059765 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5563,7 +5563,8 @@ pub const FuncGen = struct { if (bitcast_ok) { const llvm_vector_ty = try self.dg.llvmType(operand_ty); const casted_ptr = self.builder.buildBitCast(array_ptr, llvm_vector_ty.pointerType(0), ""); - _ = self.builder.buildStore(operand, casted_ptr); + const llvm_store = self.builder.buildStore(operand, casted_ptr); + llvm_store.setAlignment(inst_ty.abiAlignment(target)); } else { // If the ABI size of the element type is not evenly divisible by size in bits; // a simple bitcast will not work, and we fall back to extractelement. From 2d8fef5680dc8218596e701f91d918c4aa7215bc Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sun, 20 Mar 2022 00:06:23 -0700 Subject: [PATCH 0848/2031] stage2: make bool binop AIR return types based on operand type This allows vector-of-bools operands to return a vector-of-bools. --- src/Air.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 8c24108abd..e0f765ddc0 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -904,6 +904,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .shl_sat, .min, .max, + .bool_and, + .bool_or, => return air.typeOf(datas[inst].bin_op.lhs), .sqrt, @@ -935,8 +937,6 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .is_non_err, .is_err_ptr, .is_non_err_ptr, - .bool_and, - .bool_or, => return Type.initTag(.bool), .const_ty => return Type.initTag(.type), From 3f4676901a8c02d9d7069b284aa848d685c3975c Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sun, 20 Mar 2022 00:29:44 -0700 Subject: [PATCH 0849/2031] stage2: return `Value.zero` when truncating int to 0 bits at comptime --- src/value.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/value.zig b/src/value.zig index 76ab20c7ab..c5e082485a 100644 --- a/src/value.zig +++ b/src/value.zig @@ -3359,6 +3359,8 @@ pub const Value = extern union { } pub fn intTrunc(val: Value, allocator: Allocator, signedness: std.builtin.Signedness, bits: u16) !Value { + if (bits == 0) return Value.zero; + var val_space: Value.BigIntSpace = undefined; const val_bigint = val.toBigInt(&val_space); From afdcfb005ea32849f30e2abd7361ce1f33d0ee74 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sun, 20 Mar 2022 00:38:12 -0700 Subject: [PATCH 0850/2031] Sema: make most instructions vector-agnostic Made most `Value` functions require a `Type`. If the provided type is a vector, then automatically vectorize the operation and return with another vector. The Sema side can then automatically become vectorized with minimal changes. There are already a few manually vectorized instructions, but we can simplify those later. --- src/Sema.zig | 513 ++++++++++++++++++--------------- src/value.zig | 771 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 1006 insertions(+), 278 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 20622cb98a..500dec5246 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2105,7 +2105,7 @@ fn zirEnumDecl( }); } else if (any_values) { const tag_val = if (last_tag_val) |val| - try val.intAdd(Value.one, sema.arena) + try val.intAdd(Value.one, enum_obj.tag_ty, sema.arena) else Value.zero; last_tag_val = tag_val; @@ -8192,14 +8192,22 @@ fn zirShl( defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const lhs = sema.resolveInst(extra.lhs); const rhs = sema.resolveInst(extra.rhs); + const lhs_ty = sema.typeOf(lhs); + const rhs_ty = sema.typeOf(rhs); + const target = sema.mod.getTarget(); + try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); + + const scalar_ty = lhs_ty.scalarType(); + const scalar_rhs_ty = rhs_ty.scalarType(); // TODO coerce rhs if air_tag is not shl_sat - const rhs_is_comptime_int = try sema.checkIntType(block, rhs_src, sema.typeOf(rhs)); + const rhs_is_comptime_int = try sema.checkIntType(block, rhs_src, scalar_rhs_ty); const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs); const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs); @@ -8213,35 +8221,31 @@ fn zirShl( } } - const lhs_ty = sema.typeOf(lhs); - const rhs_ty = sema.typeOf(rhs); - const target = sema.mod.getTarget(); - const runtime_src = if (maybe_lhs_val) |lhs_val| rs: { if (lhs_val.isUndef()) return sema.addConstUndef(lhs_ty); const rhs_val = maybe_rhs_val orelse break :rs rhs_src; const val = switch (air_tag) { .shl_exact => val: { - const shifted = try lhs_val.shl(rhs_val, sema.arena); - if (lhs_ty.zigTypeTag() == .ComptimeInt) { + const shifted = try lhs_val.shl(rhs_val, lhs_ty, sema.arena); + if (scalar_ty.zigTypeTag() == .ComptimeInt) { break :val shifted; } - const int_info = lhs_ty.intInfo(target); - const truncated = try shifted.intTrunc(sema.arena, int_info.signedness, int_info.bits); - if (truncated.compareHetero(.eq, shifted)) { + const int_info = scalar_ty.intInfo(target); + const truncated = try shifted.intTrunc(lhs_ty, sema.arena, int_info.signedness, int_info.bits); + if (truncated.compare(.eq, shifted, lhs_ty)) { break :val shifted; } return sema.addConstUndef(lhs_ty); }, - .shl_sat => if (lhs_ty.zigTypeTag() == .ComptimeInt) - try lhs_val.shl(rhs_val, sema.arena) + .shl_sat => if (scalar_ty.zigTypeTag() == .ComptimeInt) + try lhs_val.shl(rhs_val, lhs_ty, sema.arena) else try lhs_val.shlSat(rhs_val, lhs_ty, sema.arena, target), - .shl => if (lhs_ty.zigTypeTag() == .ComptimeInt) - try lhs_val.shl(rhs_val, sema.arena) + .shl => if (scalar_ty.zigTypeTag() == .ComptimeInt) + try lhs_val.shl(rhs_val, lhs_ty, sema.arena) else try lhs_val.shlTrunc(rhs_val, lhs_ty, sema.arena, target), @@ -8256,7 +8260,7 @@ fn zirShl( const new_rhs = if (air_tag == .shl_sat) rhs: { // Limit the RHS type for saturating shl to be an integer as small as the LHS. if (rhs_is_comptime_int or - rhs_ty.intInfo(target).bits > lhs_ty.intInfo(target).bits) + scalar_rhs_ty.intInfo(target).bits > scalar_ty.intInfo(target).bits) { const max_int = try sema.addConstant( lhs_ty, @@ -8283,15 +8287,18 @@ fn zirShr( defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const lhs = sema.resolveInst(extra.lhs); const rhs = sema.resolveInst(extra.rhs); + const lhs_ty = sema.typeOf(lhs); + const rhs_ty = sema.typeOf(rhs); + try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); const runtime_src = if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| rs: { if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { - const lhs_ty = sema.typeOf(lhs); if (lhs_val.isUndef() or rhs_val.isUndef()) { return sema.addConstUndef(lhs_ty); } @@ -8301,13 +8308,12 @@ fn zirShr( } if (air_tag == .shr_exact) { // Detect if any ones would be shifted out. - const bits = @intCast(u16, rhs_val.toUnsignedInt()); - const truncated = try lhs_val.intTrunc(sema.arena, .unsigned, bits); + const truncated = try lhs_val.intTruncBitsAsValue(lhs_ty, sema.arena, .unsigned, rhs_val); if (!truncated.compareWithZero(.eq)) { return sema.addConstUndef(lhs_ty); } } - const val = try lhs_val.shr(rhs_val, sema.arena); + const val = try lhs_val.shr(rhs_val, lhs_ty, sema.arena); return sema.addConstant(lhs_ty, val); } else { // Even if lhs is not comptime known, we can still deduce certain things based @@ -8342,32 +8348,15 @@ fn zirBitwise( const rhs = sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); + try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]LazySrcLoc{ lhs_src, rhs_src } }); - const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); - const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); - - const scalar_type = if (resolved_type.zigTypeTag() == .Vector) - resolved_type.elemType() - else - resolved_type; - + const scalar_type = resolved_type.scalarType(); const scalar_tag = scalar_type.zigTypeTag(); - if (lhs_ty.zigTypeTag() == .Vector and rhs_ty.zigTypeTag() == .Vector) { - if (lhs_ty.arrayLen() != rhs_ty.arrayLen()) { - return sema.fail(block, src, "vector length mismatch: {d} and {d}", .{ - lhs_ty.arrayLen(), - rhs_ty.arrayLen(), - }); - } - } else if (lhs_ty.zigTypeTag() == .Vector or rhs_ty.zigTypeTag() == .Vector) { - return sema.fail(block, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ - lhs_ty, - rhs_ty, - }); - } + const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); + const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; @@ -8377,16 +8366,13 @@ fn zirBitwise( if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { - if (resolved_type.zigTypeTag() == .Vector) { - return sema.fail(block, src, "TODO implement zirBitwise for vectors at comptime", .{}); - } const result_val = switch (air_tag) { - .bit_and => try lhs_val.bitwiseAnd(rhs_val, sema.arena), - .bit_or => try lhs_val.bitwiseOr(rhs_val, sema.arena), - .xor => try lhs_val.bitwiseXor(rhs_val, sema.arena), + .bit_and => try lhs_val.bitwiseAnd(rhs_val, resolved_type, sema.arena), + .bit_or => try lhs_val.bitwiseOr(rhs_val, resolved_type, sema.arena), + .xor => try lhs_val.bitwiseXor(rhs_val, resolved_type, sema.arena), else => unreachable, }; - return sema.addConstant(scalar_type, result_val); + return sema.addConstant(resolved_type, result_val); } } @@ -8413,9 +8399,9 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { const target = sema.mod.getTarget(); if (val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(operand_type); } else if (operand_type.zigTypeTag() == .Vector) { - const vec_len = try sema.usizeCast(block, operand_src, operand_type.arrayLen()); + const vec_len = try sema.usizeCast(block, operand_src, operand_type.vectorLen()); var elem_val_buf: Value.ElemValueBuffer = undefined; const elems = try sema.arena.alloc(Value, vec_len); for (elems) |*elem, i| { @@ -8427,8 +8413,8 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. try Value.Tag.aggregate.create(sema.arena, elems), ); } else { - const result_val = try val.bitwiseNot(scalar_type, sema.arena, target); - return sema.addConstant(scalar_type, result_val); + const result_val = try val.bitwiseNot(operand_type, sema.arena, target); + return sema.addConstant(operand_type, result_val); } } @@ -8780,8 +8766,19 @@ fn zirNegate( const src = inst_data.src(); const lhs_src = src; const rhs_src = src; // TODO better source location - const lhs = sema.resolveInst(.zero); + const rhs = sema.resolveInst(inst_data.operand); + const rhs_ty = sema.typeOf(rhs); + const rhs_scalar_ty = rhs_ty.scalarType(); + + if (tag_override == .sub and rhs_scalar_ty.isUnsignedInt()) { + return sema.fail(block, src, "negation of type '{}'", .{rhs_ty}); + } + + const lhs = if (rhs_ty.zigTypeTag() == .Vector) + try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, Value.zero)) + else + sema.resolveInst(.zero); return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src); } @@ -8999,18 +8996,8 @@ fn analyzeArithmetic( const rhs_ty = sema.typeOf(rhs); const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); - if (lhs_zig_ty_tag == .Vector and rhs_zig_ty_tag == .Vector) { - if (lhs_ty.arrayLen() != rhs_ty.arrayLen()) { - return sema.fail(block, src, "vector length mismatch: {d} and {d}", .{ - lhs_ty.arrayLen(), rhs_ty.arrayLen(), - }); - } - return sema.fail(block, src, "TODO implement support for vectors in Sema.analyzeArithmetic", .{}); - } else if (lhs_zig_ty_tag == .Vector or rhs_zig_ty_tag == .Vector) { - return sema.fail(block, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ - lhs_ty, rhs_ty, - }); - } + try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); + if (lhs_zig_ty_tag == .Pointer) switch (lhs_ty.ptrSize()) { .One, .Slice => {}, .Many, .C => { @@ -9033,15 +9020,13 @@ fn analyzeArithmetic( const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]LazySrcLoc{ lhs_src, rhs_src }, }); + const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); - const scalar_type = if (resolved_type.zigTypeTag() == .Vector) - resolved_type.elemType() - else - resolved_type; - - const scalar_tag = scalar_type.zigTypeTag(); + const lhs_scalar_ty = lhs_ty.scalarType(); + const rhs_scalar_ty = rhs_ty.scalarType(); + const scalar_tag = resolved_type.scalarType().zigTypeTag(); const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat; @@ -9075,7 +9060,7 @@ fn analyzeArithmetic( if (is_int) { return sema.failWithUseOfUndef(block, rhs_src); } else { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } } if (rhs_val.compareWithZero(.eq)) { @@ -9087,19 +9072,19 @@ fn analyzeArithmetic( if (is_int) { return sema.failWithUseOfUndef(block, lhs_src); } else { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } } if (maybe_rhs_val) |rhs_val| { if (is_int) { return sema.addConstant( - scalar_type, - try lhs_val.intAdd(rhs_val, sema.arena), + resolved_type, + try lhs_val.intAdd(rhs_val, resolved_type, sema.arena), ); } else { return sema.addConstant( - scalar_type, - try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatAdd(rhs_val, resolved_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .add }; @@ -9116,15 +9101,15 @@ fn analyzeArithmetic( } if (maybe_rhs_val) |rhs_val| { if (rhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (rhs_val.compareWithZero(.eq)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { return sema.addConstant( - scalar_type, - try lhs_val.numberAddWrap(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.numberAddWrap(rhs_val, resolved_type, sema.arena, target), ); } else break :rs .{ .src = lhs_src, .air_tag = .addwrap }; } else break :rs .{ .src = rhs_src, .air_tag = .addwrap }; @@ -9140,18 +9125,18 @@ fn analyzeArithmetic( } if (maybe_rhs_val) |rhs_val| { if (rhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (rhs_val.compareWithZero(.eq)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { const val = if (scalar_tag == .ComptimeInt) - try lhs_val.intAdd(rhs_val, sema.arena) + try lhs_val.intAdd(rhs_val, resolved_type, sema.arena) else - try lhs_val.intAddSat(rhs_val, scalar_type, sema.arena, target); + try lhs_val.intAddSat(rhs_val, resolved_type, sema.arena, target); - return sema.addConstant(scalar_type, val); + return sema.addConstant(resolved_type, val); } else break :rs .{ .src = lhs_src, .air_tag = .add_sat }; } else break :rs .{ .src = rhs_src, .air_tag = .add_sat }; }, @@ -9168,7 +9153,7 @@ fn analyzeArithmetic( if (is_int) { return sema.failWithUseOfUndef(block, rhs_src); } else { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } } if (rhs_val.compareWithZero(.eq)) { @@ -9180,19 +9165,19 @@ fn analyzeArithmetic( if (is_int) { return sema.failWithUseOfUndef(block, lhs_src); } else { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } } if (maybe_rhs_val) |rhs_val| { if (is_int) { return sema.addConstant( - scalar_type, - try lhs_val.intSub(rhs_val, sema.arena), + resolved_type, + try lhs_val.intSub(rhs_val, resolved_type, sema.arena), ); } else { return sema.addConstant( - scalar_type, - try lhs_val.floatSub(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatSub(rhs_val, resolved_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .sub }; @@ -9204,7 +9189,7 @@ fn analyzeArithmetic( // If either of the operands are undefined, the result is undefined. if (maybe_rhs_val) |rhs_val| { if (rhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (rhs_val.compareWithZero(.eq)) { return casted_lhs; @@ -9212,12 +9197,12 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (maybe_rhs_val) |rhs_val| { return sema.addConstant( - scalar_type, - try lhs_val.numberSubWrap(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.numberSubWrap(rhs_val, resolved_type, sema.arena, target), ); } else break :rs .{ .src = rhs_src, .air_tag = .subwrap }; } else break :rs .{ .src = lhs_src, .air_tag = .subwrap }; @@ -9228,7 +9213,7 @@ fn analyzeArithmetic( // If either of the operands are undefined, result is undefined. if (maybe_rhs_val) |rhs_val| { if (rhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (rhs_val.compareWithZero(.eq)) { return casted_lhs; @@ -9236,15 +9221,15 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (maybe_rhs_val) |rhs_val| { const val = if (scalar_tag == .ComptimeInt) - try lhs_val.intSub(rhs_val, sema.arena) + try lhs_val.intSub(rhs_val, resolved_type, sema.arena) else - try lhs_val.intSubSat(rhs_val, scalar_type, sema.arena, target); + try lhs_val.intSubSat(rhs_val, resolved_type, sema.arena, target); - return sema.addConstant(scalar_type, val); + return sema.addConstant(resolved_type, val); } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat }; } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat }; }, @@ -9274,7 +9259,7 @@ fn analyzeArithmetic( if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { if (lhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } } } @@ -9288,27 +9273,27 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) { + if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) { - return sema.addConstUndef(scalar_type); + if (rhs_val.compare(.neq, Value.negative_one, rhs_ty)) { + return sema.addConstUndef(resolved_type); } } return sema.failWithUseOfUndef(block, rhs_src); } - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (maybe_rhs_val) |rhs_val| { if (is_int) { return sema.addConstant( - scalar_type, - try lhs_val.intDiv(rhs_val, sema.arena), + resolved_type, + try lhs_val.intDiv(rhs_val, resolved_type, sema.arena), ); } else { return sema.addConstant( - scalar_type, - try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, target), ); } } else { @@ -9349,7 +9334,7 @@ fn analyzeArithmetic( if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { if (lhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } } } @@ -9363,27 +9348,27 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) { + if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) { - return sema.addConstUndef(scalar_type); + if (rhs_val.compare(.neq, Value.negative_one, rhs_ty)) { + return sema.addConstUndef(resolved_type); } } return sema.failWithUseOfUndef(block, rhs_src); } - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (maybe_rhs_val) |rhs_val| { if (is_int) { return sema.addConstant( - scalar_type, - try lhs_val.intDiv(rhs_val, sema.arena), + resolved_type, + try lhs_val.intDiv(rhs_val, resolved_type, sema.arena), ); } else { return sema.addConstant( - scalar_type, - try lhs_val.floatDivTrunc(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatDivTrunc(rhs_val, resolved_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .div_trunc }; @@ -9412,7 +9397,7 @@ fn analyzeArithmetic( if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { if (lhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } } } @@ -9426,27 +9411,27 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) { + if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) { - return sema.addConstUndef(scalar_type); + if (rhs_val.compare(.neq, Value.negative_one, rhs_ty)) { + return sema.addConstUndef(resolved_type); } } return sema.failWithUseOfUndef(block, rhs_src); } - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (maybe_rhs_val) |rhs_val| { if (is_int) { return sema.addConstant( - scalar_type, - try lhs_val.intDivFloor(rhs_val, sema.arena), + resolved_type, + try lhs_val.intDivFloor(rhs_val, resolved_type, sema.arena), ); } else { return sema.addConstant( - scalar_type, - try lhs_val.floatDivFloor(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatDivFloor(rhs_val, resolved_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .div_floor }; @@ -9474,7 +9459,7 @@ fn analyzeArithmetic( return sema.failWithUseOfUndef(block, rhs_src); } else { if (lhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } } } @@ -9491,14 +9476,14 @@ fn analyzeArithmetic( if (is_int) { // TODO: emit compile error if there is a remainder return sema.addConstant( - scalar_type, - try lhs_val.intDiv(rhs_val, sema.arena), + resolved_type, + try lhs_val.intDiv(rhs_val, resolved_type, sema.arena), ); } else { // TODO: emit compile error if there is a remainder return sema.addConstant( - scalar_type, - try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .div_exact }; @@ -9516,9 +9501,9 @@ fn analyzeArithmetic( if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { if (lhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } - if (lhs_val.compare(.eq, Value.one, scalar_type)) { + if (lhs_val.compare(.eq, Value.one, lhs_ty)) { return casted_rhs; } } @@ -9528,13 +9513,13 @@ fn analyzeArithmetic( if (is_int) { return sema.failWithUseOfUndef(block, rhs_src); } else { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } } if (rhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } - if (rhs_val.compare(.eq, Value.one, scalar_type)) { + if (rhs_val.compare(.eq, Value.one, rhs_ty)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { @@ -9542,18 +9527,18 @@ fn analyzeArithmetic( if (is_int) { return sema.failWithUseOfUndef(block, lhs_src); } else { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } } if (is_int) { return sema.addConstant( - scalar_type, - try lhs_val.intMul(rhs_val, sema.arena), + resolved_type, + try lhs_val.intMul(rhs_val, resolved_type, sema.arena), ); } else { return sema.addConstant( - scalar_type, - try lhs_val.floatMul(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatMul(rhs_val, resolved_type, sema.arena, target), ); } } else break :rs .{ .src = lhs_src, .air_tag = .mul }; @@ -9567,30 +9552,30 @@ fn analyzeArithmetic( if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { if (lhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } - if (lhs_val.compare(.eq, Value.one, scalar_type)) { + if (lhs_val.compare(.eq, Value.one, lhs_ty)) { return casted_rhs; } } } if (maybe_rhs_val) |rhs_val| { if (rhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (rhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } - if (rhs_val.compare(.eq, Value.one, scalar_type)) { + if (rhs_val.compare(.eq, Value.one, rhs_ty)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } return sema.addConstant( - scalar_type, - try lhs_val.numberMulWrap(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.numberMulWrap(rhs_val, resolved_type, sema.arena, target), ); } else break :rs .{ .src = lhs_src, .air_tag = .mulwrap }; } else break :rs .{ .src = rhs_src, .air_tag = .mulwrap }; @@ -9603,34 +9588,34 @@ fn analyzeArithmetic( if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { if (lhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } - if (lhs_val.compare(.eq, Value.one, scalar_type)) { + if (lhs_val.compare(.eq, Value.one, lhs_ty)) { return casted_rhs; } } } if (maybe_rhs_val) |rhs_val| { if (rhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (rhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } - if (rhs_val.compare(.eq, Value.one, scalar_type)) { + if (rhs_val.compare(.eq, Value.one, rhs_ty)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } const val = if (scalar_tag == .ComptimeInt) - try lhs_val.intMul(rhs_val, sema.arena) + try lhs_val.intMul(rhs_val, resolved_type, sema.arena) else - try lhs_val.intMulSat(rhs_val, scalar_type, sema.arena, target); + try lhs_val.intMulSat(rhs_val, resolved_type, sema.arena, target); - return sema.addConstant(scalar_type, val); + return sema.addConstant(resolved_type, val); } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat }; } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat }; }, @@ -9654,9 +9639,9 @@ fn analyzeArithmetic( return sema.failWithUseOfUndef(block, lhs_src); } if (lhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } - } else if (lhs_ty.isSignedInt()) { + } else if (lhs_scalar_ty.isSignedInt()) { return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); } if (maybe_rhs_val) |rhs_val| { @@ -9667,7 +9652,7 @@ fn analyzeArithmetic( return sema.failWithDivideByZero(block, rhs_src); } if (maybe_lhs_val) |lhs_val| { - const rem_result = try lhs_val.intRem(rhs_val, sema.arena); + const rem_result = try lhs_val.intRem(rhs_val, resolved_type, sema.arena); // If this answer could possibly be different by doing `intMod`, // we must emit a compile error. Otherwise, it's OK. if (rhs_val.compareWithZero(.lt) != lhs_val.compareWithZero(.lt) and @@ -9681,12 +9666,12 @@ fn analyzeArithmetic( } if (lhs_val.compareWithZero(.lt)) { // Negative - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } - return sema.addConstant(scalar_type, rem_result); + return sema.addConstant(resolved_type, rem_result); } break :rs .{ .src = lhs_src, .air_tag = .rem }; - } else if (rhs_ty.isSignedInt()) { + } else if (rhs_scalar_ty.isSignedInt()) { return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); } else { break :rs .{ .src = rhs_src, .air_tag = .rem }; @@ -9708,8 +9693,8 @@ fn analyzeArithmetic( return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); } return sema.addConstant( - scalar_type, - try lhs_val.floatRem(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatRem(rhs_val, resolved_type, sema.arena, target), ); } else { return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); @@ -9745,8 +9730,8 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { return sema.addConstant( - scalar_type, - try lhs_val.intRem(rhs_val, sema.arena), + resolved_type, + try lhs_val.intRem(rhs_val, resolved_type, sema.arena), ); } break :rs .{ .src = lhs_src, .air_tag = .rem }; @@ -9765,12 +9750,12 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (maybe_rhs_val) |rhs_val| { return sema.addConstant( - scalar_type, - try lhs_val.floatRem(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatRem(rhs_val, resolved_type, sema.arena, target), ); } else break :rs .{ .src = rhs_src, .air_tag = .rem }; } else break :rs .{ .src = lhs_src, .air_tag = .rem }; @@ -9802,8 +9787,8 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { return sema.addConstant( - scalar_type, - try lhs_val.intMod(rhs_val, sema.arena), + resolved_type, + try lhs_val.intMod(rhs_val, resolved_type, sema.arena), ); } break :rs .{ .src = lhs_src, .air_tag = .mod }; @@ -9822,12 +9807,12 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (maybe_rhs_val) |rhs_val| { return sema.addConstant( - scalar_type, - try lhs_val.floatMod(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatMod(rhs_val, resolved_type, sema.arena, target), ); } else break :rs .{ .src = rhs_src, .air_tag = .mod }; } else break :rs .{ .src = lhs_src, .air_tag = .mod }; @@ -10178,6 +10163,11 @@ fn analyzeCmp( ) CompileError!Air.Inst.Ref { const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); + try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); + + if (lhs_ty.zigTypeTag() == .Vector and rhs_ty.zigTypeTag() == .Vector) { + return sema.cmpVector(block, src, lhs, rhs, op, lhs_src, rhs_src); + } if (lhs_ty.isNumeric() and rhs_ty.isNumeric()) { // This operation allows any combination of integer and float types, regardless of the // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for @@ -10212,6 +10202,12 @@ fn cmpSelf( if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { if (rhs_val.isUndef()) return sema.addConstUndef(Type.bool); + if (resolved_type.zigTypeTag() == .Vector) { + const result_ty = try Type.vector(sema.arena, resolved_type.vectorLen(), Type.@"bool"); + const cmp_val = try lhs_val.compareVector(op, rhs_val, resolved_type, sema.arena); + return sema.addConstant(result_ty, cmp_val); + } + if (lhs_val.compare(op, rhs_val, resolved_type)) { return Air.Inst.Ref.bool_true; } else { @@ -10237,16 +10233,12 @@ fn cmpSelf( } }; try sema.requireRuntimeBlock(block, runtime_src); - - const tag: Air.Inst.Tag = switch (op) { - .lt => .cmp_lt, - .lte => .cmp_lte, - .eq => .cmp_eq, - .gte => .cmp_gte, - .gt => .cmp_gt, - .neq => .cmp_neq, - }; - // TODO handle vectors + if (resolved_type.zigTypeTag() == .Vector) { + const result_ty = try Type.vector(sema.arena, resolved_type.vectorLen(), Type.@"bool"); + const result_ty_ref = try sema.addType(result_ty); + return block.addCmpVector(casted_lhs, casted_rhs, op, result_ty_ref); + } + const tag = Air.Inst.Tag.fromCmpOp(op); return block.addBinOp(tag, casted_lhs, casted_rhs); } @@ -11367,7 +11359,7 @@ fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) Compi const elem_ty = operand.elemType2(); const log2_elem_ty = try sema.log2IntType(block, elem_ty, src); return Type.Tag.vector.create(sema.arena, .{ - .len = operand.arrayLen(), + .len = operand.vectorLen(), .elem_type = log2_elem_ty, }); }, @@ -13298,7 +13290,7 @@ fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { const target = sema.mod.getTarget(); - const result_val = val.floatToInt(sema.arena, dest_ty, target) catch |err| switch (err) { + const result_val = val.floatToInt(sema.arena, operand_ty, dest_ty, target) catch |err| switch (err) { error.FloatCannotFit => { return sema.fail(block, operand_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty }); }, @@ -13325,7 +13317,7 @@ fn zirIntToFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { const target = sema.mod.getTarget(); - const result_val = try val.intToFloat(sema.arena, dest_ty, target); + const result_val = try val.intToFloat(sema.arena, operand_ty, dest_ty, target); return sema.addConstant(dest_ty, result_val); } @@ -13535,14 +13527,14 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (!is_vector) { return sema.addConstant( dest_ty, - try val.intTrunc(sema.arena, dest_info.signedness, dest_info.bits), + try val.intTrunc(operand_ty, sema.arena, dest_info.signedness, dest_info.bits), ); } var elem_buf: Value.ElemValueBuffer = undefined; const elems = try sema.arena.alloc(Value, operand_ty.vectorLen()); for (elems) |*elem, i| { const elem_val = val.elemValueBuffer(i, &elem_buf); - elem.* = try elem_val.intTrunc(sema.arena, dest_info.signedness, dest_info.bits); + elem.* = try elem_val.intTrunc(operand_scalar_ty, sema.arena, dest_info.signedness, dest_info.bits); } return sema.addConstant( dest_ty, @@ -14097,13 +14089,40 @@ fn checkSimdBinOp( ) CompileError!SimdBinOp { const lhs_ty = sema.typeOf(uncasted_lhs); const rhs_ty = sema.typeOf(uncasted_rhs); - const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); - const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); - var vec_len: ?usize = null; + try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); + var vec_len: ?usize = if (lhs_ty.zigTypeTag() == .Vector) lhs_ty.vectorLen() else null; + const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{ + .override = &[_]LazySrcLoc{ lhs_src, rhs_src }, + }); + const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src); + const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src); + + return SimdBinOp{ + .len = vec_len, + .lhs = lhs, + .rhs = rhs, + .lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs), + .rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs), + .result_ty = result_ty, + .scalar_ty = result_ty.scalarType(), + }; +} + +fn checkVectorizableBinaryOperands( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + lhs_ty: Type, + rhs_ty: Type, + lhs_src: LazySrcLoc, + rhs_src: LazySrcLoc, +) CompileError!void { + const lhs_zig_ty_tag = lhs_ty.zigTypeTag(); + const rhs_zig_ty_tag = rhs_ty.zigTypeTag(); if (lhs_zig_ty_tag == .Vector and rhs_zig_ty_tag == .Vector) { - const lhs_len = lhs_ty.arrayLen(); - const rhs_len = rhs_ty.arrayLen(); + const lhs_len = lhs_ty.vectorLen(); + const rhs_len = rhs_ty.vectorLen(); if (lhs_len != rhs_len) { const msg = msg: { const msg = try sema.errMsg(block, src, "vector length mismatch", .{}); @@ -14114,7 +14133,6 @@ fn checkSimdBinOp( }; return sema.failWithOwnedErrorMsg(block, msg); } - vec_len = try sema.usizeCast(block, lhs_src, lhs_len); } else if (lhs_zig_ty_tag == .Vector or rhs_zig_ty_tag == .Vector) { const msg = msg: { const msg = try sema.errMsg(block, src, "mixed scalar and vector operands: {} and {}", .{ @@ -14132,21 +14150,6 @@ fn checkSimdBinOp( }; return sema.failWithOwnedErrorMsg(block, msg); } - const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{ - .override = &[_]LazySrcLoc{ lhs_src, rhs_src }, - }); - const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src); - const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src); - - return SimdBinOp{ - .len = vec_len, - .lhs = lhs, - .rhs = rhs, - .lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs), - .rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs), - .result_ty = result_ty, - .scalar_ty = result_ty.scalarType(), - }; } fn resolveExportOptions( @@ -14376,9 +14379,9 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. while (i < vec_len) : (i += 1) { const elem_val = operand_val.elemValueBuffer(i, &elem_buf); switch (operation) { - .And => accum = try accum.bitwiseAnd(elem_val, sema.arena), - .Or => accum = try accum.bitwiseOr(elem_val, sema.arena), - .Xor => accum = try accum.bitwiseXor(elem_val, sema.arena), + .And => accum = try accum.bitwiseAnd(elem_val, scalar_ty, sema.arena), + .Or => accum = try accum.bitwiseOr(elem_val, scalar_ty, sema.arena), + .Xor => accum = try accum.bitwiseXor(elem_val, scalar_ty, sema.arena), .Min => accum = accum.numberMin(elem_val), .Max => accum = accum.numberMax(elem_val), .Add => accum = try accum.numberAddWrap(elem_val, scalar_ty, sema.arena, target), @@ -14697,10 +14700,10 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A .Xchg => operand_val, .Add => try stored_val.numberAddWrap(operand_val, operand_ty, sema.arena, target), .Sub => try stored_val.numberSubWrap(operand_val, operand_ty, sema.arena, target), - .And => try stored_val.bitwiseAnd (operand_val, sema.arena), + .And => try stored_val.bitwiseAnd (operand_val, operand_ty, sema.arena), .Nand => try stored_val.bitwiseNand (operand_val, operand_ty, sema.arena, target), - .Or => try stored_val.bitwiseOr (operand_val, sema.arena), - .Xor => try stored_val.bitwiseXor (operand_val, sema.arena), + .Or => try stored_val.bitwiseOr (operand_val, operand_ty, sema.arena), + .Xor => try stored_val.bitwiseXor (operand_val, operand_ty, sema.arena), .Max => stored_val.numberMax (operand_val), .Min => stored_val.numberMin (operand_val), // zig fmt: on @@ -17523,7 +17526,7 @@ fn coerce( if (val.floatHasFraction()) { return sema.fail(block, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val.fmtValue(inst_ty), dest_ty }); } - const result_val = val.floatToInt(sema.arena, dest_ty, target) catch |err| switch (err) { + const result_val = val.floatToInt(sema.arena, inst_ty, dest_ty, target) catch |err| switch (err) { error.FloatCannotFit => { return sema.fail(block, inst_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty }); }, @@ -17586,7 +17589,7 @@ fn coerce( }, .Int, .ComptimeInt => int: { const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :int; - const result_val = try val.intToFloat(sema.arena, dest_ty, target); + const result_val = try val.intToFloat(sema.arena, inst_ty, dest_ty, target); // TODO implement this compile error //const int_again_val = try result_val.floatToInt(sema.arena, inst_ty); //if (!int_again_val.eql(val, inst_ty)) { @@ -17823,8 +17826,21 @@ fn coerceInMemoryAllowed( return .ok; } + // Vectors + if (dest_tag == .Vector and src_tag == .Vector) vectors: { + const dest_len = dest_ty.vectorLen(); + const src_len = src_ty.vectorLen(); + if (dest_len != src_len) break :vectors; + + const dest_elem_ty = dest_ty.scalarType(); + const src_elem_ty = src_ty.scalarType(); + const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src); + if (child == .no_match) break :vectors; + + return .ok; + } + // TODO: non-pointer-like optionals - // TODO: vectors return .no_match; } @@ -19697,19 +19713,6 @@ fn cmpNumeric( const lhs_ty_tag = lhs_ty.zigTypeTag(); const rhs_ty_tag = rhs_ty.zigTypeTag(); - if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) { - if (lhs_ty.vectorLen() != rhs_ty.vectorLen()) { - return sema.fail(block, src, "vector length mismatch: {d} and {d}", .{ - lhs_ty.vectorLen(), rhs_ty.vectorLen(), - }); - } - return sema.fail(block, src, "TODO implement support for vectors in cmpNumeric", .{}); - } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) { - return sema.fail(block, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ - lhs_ty, rhs_ty, - }); - } - const runtime_src: LazySrcLoc = src: { if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { @@ -19895,6 +19898,46 @@ fn cmpNumeric( return block.addBinOp(Air.Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs); } +/// Asserts that lhs and rhs types are both vectors. +fn cmpVector( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + lhs: Air.Inst.Ref, + rhs: Air.Inst.Ref, + op: std.math.CompareOperator, + lhs_src: LazySrcLoc, + rhs_src: LazySrcLoc, +) CompileError!Air.Inst.Ref { + const lhs_ty = sema.typeOf(lhs); + const rhs_ty = sema.typeOf(rhs); + assert(lhs_ty.zigTypeTag() == .Vector); + assert(rhs_ty.zigTypeTag() == .Vector); + try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); + + const result_ty = try Type.vector(sema.arena, lhs_ty.vectorLen(), Type.@"bool"); + + const runtime_src: LazySrcLoc = src: { + if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { + if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { + if (lhs_val.isUndef() or rhs_val.isUndef()) { + return sema.addConstUndef(result_ty); + } + const cmp_val = try lhs_val.compareVector(op, rhs_val, lhs_ty, sema.arena); + return sema.addConstant(result_ty, cmp_val); + } else { + break :src rhs_src; + } + } else { + break :src lhs_src; + } + }; + + try sema.requireRuntimeBlock(block, runtime_src); + const result_ty_inst = try sema.addType(result_ty); + return block.addCmpVector(lhs, rhs, op, result_ty_inst); +} + fn wrapOptional( sema: *Sema, block: *Block, @@ -21201,7 +21244,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { map.putAssumeCapacityContext(copied_val, {}, .{ .ty = int_tag_ty }); } else { const val = if (last_tag_val) |val| - try val.intAdd(Value.one, sema.arena) + try val.intAdd(Value.one, int_tag_ty, sema.arena) else Value.zero; last_tag_val = val; diff --git a/src/value.zig b/src/value.zig index c5e082485a..6f0f786072 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1846,8 +1846,23 @@ pub const Value = extern union { return order(lhs, rhs).compare(op); } - /// Asserts the value is comparable. Both operands have type `ty`. + /// Asserts the values are comparable. Both operands have type `ty`. + /// Vector results will be reduced with AND. pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type) bool { + if (ty.zigTypeTag() == .Vector) { + var i: usize = 0; + while (i < ty.vectorLen()) : (i += 1) { + if (!compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType())) { + return false; + } + } + return true; + } + return compareScalar(lhs, op, rhs, ty); + } + + /// Asserts the values are comparable. Both operands have type `ty`. + pub fn compareScalar(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type) bool { return switch (op) { .eq => lhs.eql(rhs, ty), .neq => !lhs.eql(rhs, ty), @@ -1855,18 +1870,25 @@ pub const Value = extern union { }; } + /// Asserts the values are comparable vectors of type `ty`. + pub fn compareVector(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, allocator: Allocator) !Value { + assert(ty.zigTypeTag() == .Vector); + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + const res_bool = compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType()); + scalar.* = if (res_bool) Value.@"true" else Value.@"false"; + } + return Value.Tag.aggregate.create(allocator, result_data); + } + /// Asserts the value is comparable. - /// For vectors this is only valid with op == .eq. + /// Vector results will be reduced with AND. pub fn compareWithZero(lhs: Value, op: std.math.CompareOperator) bool { switch (lhs.tag()) { - .repeated => { - assert(op == .eq); - return lhs.castTag(.repeated).?.data.compareWithZero(.eq); - }, + .repeated => return lhs.castTag(.repeated).?.data.compareWithZero(op), .aggregate => { - assert(op == .eq); for (lhs.castTag(.aggregate).?.data) |elem_val| { - if (!elem_val.compareWithZero(.eq)) return false; + if (!elem_val.compareWithZero(op)) return false; } return true; }, @@ -2404,6 +2426,27 @@ pub const Value = extern union { }; } + /// Index into a vector-like `Value`. Asserts `index` is a valid index for `val`. + /// Some scalar values are considered vector-like to avoid needing to allocate + /// a new `repeated` each time a constant is used. + pub fn indexVectorlike(val: Value, index: usize) Value { + return switch (val.tag()) { + .aggregate => val.castTag(.aggregate).?.data[index], + + .repeated => val.castTag(.repeated).?.data, + // These values will implicitly be treated as `repeated`. + .zero, + .one, + .bool_false, + .bool_true, + .int_i64, + .int_u64, + => val, + + else => unreachable, + }; + } + /// Asserts the value is a single-item pointer to an array, or an array, /// or an unknown-length pointer, and returns the element value at the index. pub fn elemValue(val: Value, arena: Allocator, index: usize) !Value { @@ -2646,25 +2689,38 @@ pub const Value = extern union { }; } - pub fn intToFloat(val: Value, arena: Allocator, dest_ty: Type, target: Target) !Value { + pub fn intToFloat(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, target: Target) !Value { + if (int_ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, int_ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intToFloatScalar(val.indexVectorlike(i), arena, int_ty.scalarType(), float_ty.scalarType(), target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return intToFloatScalar(val, arena, int_ty, float_ty, target); + } + + pub fn intToFloatScalar(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, target: Target) !Value { + assert(int_ty.isNumeric() and !int_ty.isAnyFloat()); + assert(float_ty.isAnyFloat()); switch (val.tag()) { .undef, .zero, .one => return val, .the_only_possible_value => return Value.initTag(.zero), // for i0, u0 .int_u64 => { - return intToFloatInner(val.castTag(.int_u64).?.data, arena, dest_ty, target); + return intToFloatInner(val.castTag(.int_u64).?.data, arena, float_ty, target); }, .int_i64 => { - return intToFloatInner(val.castTag(.int_i64).?.data, arena, dest_ty, target); + return intToFloatInner(val.castTag(.int_i64).?.data, arena, float_ty, target); }, .int_big_positive => { const limbs = val.castTag(.int_big_positive).?.data; const float = bigIntToFloat(limbs, true); - return floatToValue(float, arena, dest_ty, target); + return floatToValue(float, arena, float_ty, target); }, .int_big_negative => { const limbs = val.castTag(.int_big_negative).?.data; const float = bigIntToFloat(limbs, false); - return floatToValue(float, arena, dest_ty, target); + return floatToValue(float, arena, float_ty, target); }, else => unreachable, } @@ -2694,7 +2750,20 @@ pub const Value = extern union { } } - pub fn floatToInt(val: Value, arena: Allocator, dest_ty: Type, target: Target) error{ FloatCannotFit, OutOfMemory }!Value { + pub fn floatToInt(val: Value, arena: Allocator, float_ty: Type, int_ty: Type, target: Target) error{ FloatCannotFit, OutOfMemory }!Value { + if (float_ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatToIntScalar(val.indexVectorlike(i), arena, float_ty.scalarType(), int_ty.scalarType(), target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatToIntScalar(val, arena, float_ty, int_ty, target); + } + + pub fn floatToIntScalar(val: Value, arena: Allocator, float_ty: Type, int_ty: Type, target: Target) error{ FloatCannotFit, OutOfMemory }!Value { + assert(float_ty.isAnyFloat()); + assert(int_ty.isInt()); const Limb = std.math.big.Limb; var value = val.toFloat(f64); // TODO: f128 ? @@ -2724,7 +2793,7 @@ pub const Value = extern union { else try Value.Tag.int_big_positive.create(arena, result_limbs); - if (result.intFitsInType(dest_ty, target)) { + if (result.intFitsInType(int_ty, target)) { return result; } else { return error.FloatCannotFit; @@ -2771,18 +2840,36 @@ pub const Value = extern union { }; } - /// Supports both floats and ints; handles undefined. + /// Supports both (vectors of) floats and ints; handles undefined scalars. pub fn numberAddWrap( lhs: Value, rhs: Value, ty: Type, arena: Allocator, target: Target, + ) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try numberAddWrapScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return numberAddWrapScalar(lhs, rhs, ty, arena, target); + } + + /// Supports both floats and ints; handles undefined. + pub fn numberAddWrapScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, ) !Value { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); if (ty.zigTypeTag() == .ComptimeInt) { - return intAdd(lhs, rhs, arena); + return intAdd(lhs, rhs, ty, arena); } if (ty.isAnyFloat()) { @@ -2809,13 +2896,31 @@ pub const Value = extern union { } } - /// Supports integers only; asserts neither operand is undefined. + /// Supports (vectors of) integers only; asserts neither operand is undefined. pub fn intAddSat( lhs: Value, rhs: Value, ty: Type, arena: Allocator, target: Target, + ) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intAddSatScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return intAddSatScalar(lhs, rhs, ty, arena, target); + } + + /// Supports integers only; asserts neither operand is undefined. + pub fn intAddSatScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, ) !Value { assert(!lhs.isUndef()); assert(!rhs.isUndef()); @@ -2861,18 +2966,36 @@ pub const Value = extern union { }; } - /// Supports both floats and ints; handles undefined. + /// Supports both (vectors of) floats and ints; handles undefined scalars. pub fn numberSubWrap( lhs: Value, rhs: Value, ty: Type, arena: Allocator, target: Target, + ) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try numberSubWrapScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return numberSubWrapScalar(lhs, rhs, ty, arena, target); + } + + /// Supports both floats and ints; handles undefined. + pub fn numberSubWrapScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, ) !Value { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); if (ty.zigTypeTag() == .ComptimeInt) { - return intSub(lhs, rhs, arena); + return intSub(lhs, rhs, ty, arena); } if (ty.isAnyFloat()) { @@ -2883,13 +3006,31 @@ pub const Value = extern union { return overflow_result.wrapped_result; } - /// Supports integers only; asserts neither operand is undefined. + /// Supports (vectors of) integers only; asserts neither operand is undefined. pub fn intSubSat( lhs: Value, rhs: Value, ty: Type, arena: Allocator, target: Target, + ) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intSubSatScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return intSubSatScalar(lhs, rhs, ty, arena, target); + } + + /// Supports integers only; asserts neither operand is undefined. + pub fn intSubSatScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, ) !Value { assert(!lhs.isUndef()); assert(!rhs.isUndef()); @@ -2944,18 +3085,36 @@ pub const Value = extern union { }; } - /// Supports both floats and ints; handles undefined. + /// Supports both (vectors of) floats and ints; handles undefined scalars. pub fn numberMulWrap( lhs: Value, rhs: Value, ty: Type, arena: Allocator, target: Target, + ) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try numberMulWrapScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return numberMulWrapScalar(lhs, rhs, ty, arena, target); + } + + /// Supports both floats and ints; handles undefined. + pub fn numberMulWrapScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, ) !Value { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); if (ty.zigTypeTag() == .ComptimeInt) { - return intMul(lhs, rhs, arena); + return intMul(lhs, rhs, ty, arena); } if (ty.isAnyFloat()) { @@ -2966,13 +3125,31 @@ pub const Value = extern union { return overflow_result.wrapped_result; } - /// Supports integers only; asserts neither operand is undefined. + /// Supports (vectors of) integers only; asserts neither operand is undefined. pub fn intMulSat( lhs: Value, rhs: Value, ty: Type, arena: Allocator, target: Target, + ) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intMulSatScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return intMulSatScalar(lhs, rhs, ty, arena, target); + } + + /// Supports (vectors of) integers only; asserts neither operand is undefined. + pub fn intMulSatScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, ) !Value { assert(!lhs.isUndef()); assert(!rhs.isUndef()); @@ -3025,8 +3202,20 @@ pub const Value = extern union { }; } - /// operands must be integers; handles undefined. + /// operands must be (vectors of) integers; handles undefined scalars. pub fn bitwiseNot(val: Value, ty: Type, arena: Allocator, target: Target) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try bitwiseNotScalar(val.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return bitwiseNotScalar(val, ty, arena, target); + } + + /// operands must be integers; handles undefined. + pub fn bitwiseNotScalar(val: Value, ty: Type, arena: Allocator, target: Target) !Value { if (val.isUndef()) return Value.initTag(.undef); const info = ty.intInfo(target); @@ -3050,8 +3239,20 @@ pub const Value = extern union { return fromBigInt(arena, result_bigint.toConst()); } + /// operands must be (vectors of) integers; handles undefined scalars. + pub fn bitwiseAnd(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try bitwiseAndScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return bitwiseAndScalar(lhs, rhs, allocator); + } + /// operands must be integers; handles undefined. - pub fn bitwiseAnd(lhs: Value, rhs: Value, arena: Allocator) !Value { + pub fn bitwiseAndScalar(lhs: Value, rhs: Value, arena: Allocator) !Value { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); // TODO is this a performance issue? maybe we should try the operation without @@ -3070,22 +3271,46 @@ pub const Value = extern union { return fromBigInt(arena, result_bigint.toConst()); } - /// operands must be integers; handles undefined. + /// operands must be (vectors of) integers; handles undefined scalars. pub fn bitwiseNand(lhs: Value, rhs: Value, ty: Type, arena: Allocator, target: Target) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try bitwiseNandScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return bitwiseNandScalar(lhs, rhs, ty, arena, target); + } + + /// operands must be integers; handles undefined. + pub fn bitwiseNandScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, target: Target) !Value { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); - const anded = try bitwiseAnd(lhs, rhs, arena); + const anded = try bitwiseAnd(lhs, rhs, ty, arena); const all_ones = if (ty.isSignedInt()) try Value.Tag.int_i64.create(arena, -1) else try ty.maxInt(arena, target); - return bitwiseXor(anded, all_ones, arena); + return bitwiseXor(anded, all_ones, ty, arena); + } + + /// operands must be (vectors of) integers; handles undefined scalars. + pub fn bitwiseOr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try bitwiseOrScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return bitwiseOrScalar(lhs, rhs, allocator); } /// operands must be integers; handles undefined. - pub fn bitwiseOr(lhs: Value, rhs: Value, arena: Allocator) !Value { + pub fn bitwiseOrScalar(lhs: Value, rhs: Value, arena: Allocator) !Value { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); // TODO is this a performance issue? maybe we should try the operation without @@ -3103,8 +3328,20 @@ pub const Value = extern union { return fromBigInt(arena, result_bigint.toConst()); } + /// operands must be (vectors of) integers; handles undefined scalars. + pub fn bitwiseXor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try bitwiseXorScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return bitwiseXorScalar(lhs, rhs, allocator); + } + /// operands must be integers; handles undefined. - pub fn bitwiseXor(lhs: Value, rhs: Value, arena: Allocator) !Value { + pub fn bitwiseXorScalar(lhs: Value, rhs: Value, arena: Allocator) !Value { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); // TODO is this a performance issue? maybe we should try the operation without @@ -3123,7 +3360,18 @@ pub const Value = extern union { return fromBigInt(arena, result_bigint.toConst()); } - pub fn intAdd(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intAdd(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intAddScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return intAddScalar(lhs, rhs, allocator); + } + + pub fn intAddScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -3139,7 +3387,18 @@ pub const Value = extern union { return fromBigInt(allocator, result_bigint.toConst()); } - pub fn intSub(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intSub(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intSubScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return intSubScalar(lhs, rhs, allocator); + } + + pub fn intSubScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -3155,7 +3414,18 @@ pub const Value = extern union { return fromBigInt(allocator, result_bigint.toConst()); } - pub fn intDiv(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intDiv(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intDivScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return intDivScalar(lhs, rhs, allocator); + } + + pub fn intDivScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -3180,7 +3450,18 @@ pub const Value = extern union { return fromBigInt(allocator, result_q.toConst()); } - pub fn intDivFloor(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intDivFloor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intDivFloorScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return intDivFloorScalar(lhs, rhs, allocator); + } + + pub fn intDivFloorScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -3205,7 +3486,18 @@ pub const Value = extern union { return fromBigInt(allocator, result_q.toConst()); } - pub fn intRem(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intRem(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intRemScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return intRemScalar(lhs, rhs, allocator); + } + + pub fn intRemScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -3232,7 +3524,18 @@ pub const Value = extern union { return fromBigInt(allocator, result_r.toConst()); } - pub fn intMod(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intMod(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intModScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return intModScalar(lhs, rhs, allocator); + } + + pub fn intModScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -3270,6 +3573,17 @@ pub const Value = extern union { } pub fn floatRem(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, target: Target) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatRemScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatRemScalar(lhs, rhs, float_type, arena, target); + } + + pub fn floatRemScalar(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, target: Target) !Value { switch (float_type.floatBits(target)) { 16 => { const lhs_val = lhs.toFloat(f16); @@ -3304,6 +3618,17 @@ pub const Value = extern union { } pub fn floatMod(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, target: Target) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatModScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatModScalar(lhs, rhs, float_type, arena, target); + } + + pub fn floatModScalar(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, target: Target) !Value { switch (float_type.floatBits(target)) { 16 => { const lhs_val = lhs.toFloat(f16); @@ -3337,7 +3662,18 @@ pub const Value = extern union { } } - pub fn intMul(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intMul(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intMulScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return intMulScalar(lhs, rhs, allocator); + } + + pub fn intMulScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -3358,7 +3694,30 @@ pub const Value = extern union { return fromBigInt(allocator, result_bigint.toConst()); } - pub fn intTrunc(val: Value, allocator: Allocator, signedness: std.builtin.Signedness, bits: u16) !Value { + pub fn intTrunc(val: Value, ty: Type, allocator: Allocator, signedness: std.builtin.Signedness, bits: u16) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intTruncScalar(val.indexVectorlike(i), allocator, signedness, bits); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return intTruncScalar(val, allocator, signedness, bits); + } + + /// This variant may vectorize on `bits`. Asserts that `bits` is a (vector of) `u16`. + pub fn intTruncBitsAsValue(val: Value, ty: Type, allocator: Allocator, signedness: std.builtin.Signedness, bits: Value) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intTruncScalar(val.indexVectorlike(i), allocator, signedness, @intCast(u16, bits.indexVectorlike(i).toUnsignedInt())); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return intTruncScalar(val, allocator, signedness, @intCast(u16, bits.toUnsignedInt())); + } + + pub fn intTruncScalar(val: Value, allocator: Allocator, signedness: std.builtin.Signedness, bits: u16) !Value { if (bits == 0) return Value.zero; var val_space: Value.BigIntSpace = undefined; @@ -3374,7 +3733,18 @@ pub const Value = extern union { return fromBigInt(allocator, result_bigint.toConst()); } - pub fn shl(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn shl(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try shlScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return shlScalar(lhs, rhs, allocator); + } + + pub fn shlScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -3430,6 +3800,23 @@ pub const Value = extern union { ty: Type, arena: Allocator, target: Target, + ) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try shlSatScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return shlSatScalar(lhs, rhs, ty, arena, target); + } + + pub fn shlSatScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, ) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. @@ -3458,13 +3845,41 @@ pub const Value = extern union { arena: Allocator, target: Target, ) !Value { - const shifted = try lhs.shl(rhs, arena); + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try shlTruncScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return shlTruncScalar(lhs, rhs, ty, arena, target); + } + + pub fn shlTruncScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, + ) !Value { + const shifted = try lhs.shl(rhs, ty, arena); const int_info = ty.intInfo(target); - const truncated = try shifted.intTrunc(arena, int_info.signedness, int_info.bits); + const truncated = try shifted.intTrunc(ty, arena, int_info.signedness, int_info.bits); return truncated; } - pub fn shr(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn shr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try shrScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return shrScalar(lhs, rhs, allocator); + } + + pub fn shrScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -3497,6 +3912,23 @@ pub const Value = extern union { float_type: Type, arena: Allocator, target: Target, + ) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatAddScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatAddScalar(lhs, rhs, float_type, arena, target); + } + + pub fn floatAddScalar( + lhs: Value, + rhs: Value, + float_type: Type, + arena: Allocator, + target: Target, ) !Value { switch (float_type.floatBits(target)) { 16 => { @@ -3534,6 +3966,23 @@ pub const Value = extern union { float_type: Type, arena: Allocator, target: Target, + ) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatSubScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatSubScalar(lhs, rhs, float_type, arena, target); + } + + pub fn floatSubScalar( + lhs: Value, + rhs: Value, + float_type: Type, + arena: Allocator, + target: Target, ) !Value { switch (float_type.floatBits(target)) { 16 => { @@ -3571,6 +4020,23 @@ pub const Value = extern union { float_type: Type, arena: Allocator, target: Target, + ) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatDivScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatDivScalar(lhs, rhs, float_type, arena, target); + } + + pub fn floatDivScalar( + lhs: Value, + rhs: Value, + float_type: Type, + arena: Allocator, + target: Target, ) !Value { switch (float_type.floatBits(target)) { 16 => { @@ -3611,6 +4077,23 @@ pub const Value = extern union { float_type: Type, arena: Allocator, target: Target, + ) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatDivFloorScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatDivFloorScalar(lhs, rhs, float_type, arena, target); + } + + pub fn floatDivFloorScalar( + lhs: Value, + rhs: Value, + float_type: Type, + arena: Allocator, + target: Target, ) !Value { switch (float_type.floatBits(target)) { 16 => { @@ -3651,6 +4134,23 @@ pub const Value = extern union { float_type: Type, arena: Allocator, target: Target, + ) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatDivTruncScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatDivTruncScalar(lhs, rhs, float_type, arena, target); + } + + pub fn floatDivTruncScalar( + lhs: Value, + rhs: Value, + float_type: Type, + arena: Allocator, + target: Target, ) !Value { switch (float_type.floatBits(target)) { 16 => { @@ -3691,6 +4191,23 @@ pub const Value = extern union { float_type: Type, arena: Allocator, target: Target, + ) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatMulScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatMulScalar(lhs, rhs, float_type, arena, target); + } + + pub fn floatMulScalar( + lhs: Value, + rhs: Value, + float_type: Type, + arena: Allocator, + target: Target, ) !Value { switch (float_type.floatBits(target)) { 16 => { @@ -3726,6 +4243,17 @@ pub const Value = extern union { } pub fn sqrt(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try sqrtScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return sqrtScalar(val, float_type, arena, target); + } + + pub fn sqrtScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -3758,6 +4286,17 @@ pub const Value = extern union { } pub fn sin(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try sinScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return sinScalar(val, float_type, arena, target); + } + + pub fn sinScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -3790,6 +4329,17 @@ pub const Value = extern union { } pub fn cos(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try cosScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return cosScalar(val, float_type, arena, target); + } + + pub fn cosScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -3822,6 +4372,17 @@ pub const Value = extern union { } pub fn exp(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try expScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return expScalar(val, float_type, arena, target); + } + + pub fn expScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -3854,6 +4415,17 @@ pub const Value = extern union { } pub fn exp2(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try exp2Scalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return exp2Scalar(val, float_type, arena, target); + } + + pub fn exp2Scalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -3886,6 +4458,17 @@ pub const Value = extern union { } pub fn log(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try logScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return logScalar(val, float_type, arena, target); + } + + pub fn logScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -3918,6 +4501,17 @@ pub const Value = extern union { } pub fn log2(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try log2Scalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return log2Scalar(val, float_type, arena, target); + } + + pub fn log2Scalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -3950,6 +4544,17 @@ pub const Value = extern union { } pub fn log10(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try log10Scalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return log10Scalar(val, float_type, arena, target); + } + + pub fn log10Scalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -3982,6 +4587,17 @@ pub const Value = extern union { } pub fn fabs(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try fabsScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return fabsScalar(val, float_type, arena, target); + } + + pub fn fabsScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -4011,6 +4627,17 @@ pub const Value = extern union { } pub fn floor(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floorScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floorScalar(val, float_type, arena, target); + } + + pub fn floorScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -4040,6 +4667,17 @@ pub const Value = extern union { } pub fn ceil(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try ceilScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return ceilScalar(val, float_type, arena, target); + } + + pub fn ceilScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -4069,6 +4707,17 @@ pub const Value = extern union { } pub fn round(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try roundScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return roundScalar(val, float_type, arena, target); + } + + pub fn roundScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -4098,6 +4747,17 @@ pub const Value = extern union { } pub fn trunc(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try truncScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return truncScalar(val, float_type, arena, target); + } + + pub fn truncScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -4133,6 +4793,31 @@ pub const Value = extern union { addend: Value, arena: Allocator, target: Target, + ) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try mulAddScalar( + float_type.scalarType(), + mulend1.indexVectorlike(i), + mulend2.indexVectorlike(i), + addend.indexVectorlike(i), + arena, + target, + ); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return mulAddScalar(float_type, mulend1, mulend2, addend, arena, target); + } + + pub fn mulAddScalar( + float_type: Type, + mulend1: Value, + mulend2: Value, + addend: Value, + arena: Allocator, + target: Target, ) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { From c9598c4cd38f742e75a54d5a28d169351104b61d Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sun, 20 Mar 2022 01:11:21 -0700 Subject: [PATCH 0851/2031] behavior tests: enable all vector tests for the stage2 LLVM backend --- src/value.zig | 16 ++- test/behavior/vector.zig | 209 +++++++++++++++++++++++++++++++++------ 2 files changed, 184 insertions(+), 41 deletions(-) diff --git a/src/value.zig b/src/value.zig index 6f0f786072..454b1d76a2 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2693,16 +2693,14 @@ pub const Value = extern union { if (int_ty.zigTypeTag() == .Vector) { const result_data = try arena.alloc(Value, int_ty.vectorLen()); for (result_data) |*scalar, i| { - scalar.* = try intToFloatScalar(val.indexVectorlike(i), arena, int_ty.scalarType(), float_ty.scalarType(), target); + scalar.* = try intToFloatScalar(val.indexVectorlike(i), arena, float_ty.scalarType(), target); } return Value.Tag.aggregate.create(arena, result_data); } - return intToFloatScalar(val, arena, int_ty, float_ty, target); + return intToFloatScalar(val, arena, float_ty, target); } - pub fn intToFloatScalar(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, target: Target) !Value { - assert(int_ty.isNumeric() and !int_ty.isAnyFloat()); - assert(float_ty.isAnyFloat()); + pub fn intToFloatScalar(val: Value, arena: Allocator, float_ty: Type, target: Target) !Value { switch (val.tag()) { .undef, .zero, .one => return val, .the_only_possible_value => return Value.initTag(.zero), // for i0, u0 @@ -2754,16 +2752,14 @@ pub const Value = extern union { if (float_ty.zigTypeTag() == .Vector) { const result_data = try arena.alloc(Value, float_ty.vectorLen()); for (result_data) |*scalar, i| { - scalar.* = try floatToIntScalar(val.indexVectorlike(i), arena, float_ty.scalarType(), int_ty.scalarType(), target); + scalar.* = try floatToIntScalar(val.indexVectorlike(i), arena, int_ty.scalarType(), target); } return Value.Tag.aggregate.create(arena, result_data); } - return floatToIntScalar(val, arena, float_ty, int_ty, target); + return floatToIntScalar(val, arena, int_ty, target); } - pub fn floatToIntScalar(val: Value, arena: Allocator, float_ty: Type, int_ty: Type, target: Target) error{ FloatCannotFit, OutOfMemory }!Value { - assert(float_ty.isAnyFloat()); - assert(int_ty.isInt()); + pub fn floatToIntScalar(val: Value, arena: Allocator, int_ty: Type, target: Target) error{ FloatCannotFit, OutOfMemory }!Value { const Limb = std.math.big.Limb; var value = val.toFloat(f64); // TODO: f128 ? diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 697969f42e..1d8dd9f661 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -6,7 +6,12 @@ const expect = std.testing.expect; const Vector = std.meta.Vector; test "implicit cast vector to array - bool" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { const a: Vector(4, bool) = [_]bool{ true, false, true, false }; @@ -19,7 +24,12 @@ test "implicit cast vector to array - bool" { } test "vector wrap operators" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { var v: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 }; @@ -36,7 +46,12 @@ test "vector wrap operators" { } test "vector bin compares with mem.eql" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { var v: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 }; @@ -54,7 +69,12 @@ test "vector bin compares with mem.eql" { } test "vector int operators" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { var v: Vector(4, i32) = [4]i32{ 10, 20, 30, 40 }; @@ -70,7 +90,12 @@ test "vector int operators" { } test "vector float operators" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { var v: Vector(4, f32) = [4]f32{ 10, 20, 30, 40 }; @@ -86,7 +111,12 @@ test "vector float operators" { } test "vector bit operators" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { var v: Vector(4, u8) = [4]u8{ 0b10101010, 0b10101010, 0b10101010, 0b10101010 }; @@ -101,7 +131,12 @@ test "vector bit operators" { } test "implicit cast vector to array" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { var a: Vector(4, i32) = [_]i32{ 1, 2, 3, 4 }; @@ -115,7 +150,12 @@ test "implicit cast vector to array" { } test "array to vector" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { var foo: f32 = 3.14; @@ -129,7 +169,12 @@ test "array to vector" { } test "vector casts of sizes not divisible by 8" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { { @@ -159,7 +204,12 @@ test "vector casts of sizes not divisible by 8" { } test "vector @splat" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn testForT(comptime N: comptime_int, v: anytype) !void { const T = @TypeOf(v); @@ -195,7 +245,12 @@ test "vector @splat" { } test "load vector elements via comptime index" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { var v: Vector(4, i32) = [_]i32{ 1, 2, 3, undefined }; @@ -213,7 +268,12 @@ test "load vector elements via comptime index" { } test "store vector elements via comptime index" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { var v: Vector(4, i32) = [_]i32{ 1, 5, 3, undefined }; @@ -237,7 +297,12 @@ test "store vector elements via comptime index" { } test "load vector elements via runtime index" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { var v: Vector(4, i32) = [_]i32{ 1, 2, 3, undefined }; @@ -255,7 +320,12 @@ test "load vector elements via runtime index" { } test "store vector elements via runtime index" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { var v: Vector(4, i32) = [_]i32{ 1, 5, 3, undefined }; @@ -274,7 +344,12 @@ test "store vector elements via runtime index" { } test "initialize vector which is a struct field" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const Vec4Obj = struct { data: Vector(4, f32), }; @@ -292,7 +367,12 @@ test "initialize vector which is a struct field" { } test "vector comparison operators" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { { @@ -327,7 +407,12 @@ test "vector comparison operators" { } test "vector division operators" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTestDiv(comptime T: type, x: Vector(4, T), y: Vector(4, T)) !void { if (!comptime std.meta.trait.isSignedInt(T)) { @@ -410,7 +495,12 @@ test "vector division operators" { } test "vector bitwise not operator" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTestNot(comptime T: type, x: Vector(4, T)) !void { var y = ~x; @@ -436,7 +526,12 @@ test "vector bitwise not operator" { } test "vector shift operators" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTestShift(x: anytype, y: anytype) !void { const N = @typeInfo(@TypeOf(x)).Array.len; @@ -667,7 +762,12 @@ test "vector reduce operation" { } test "mask parameter of @shuffle is comptime scope" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const __v4hi = std.meta.Vector(4, i16); var v4_a = __v4hi{ 0, 0, 0, 0 }; var v4_b = __v4hi{ 0, 0, 0, 0 }; @@ -681,13 +781,30 @@ test "mask parameter of @shuffle is comptime scope" { } test "saturating add" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { - const u8x3 = std.meta.Vector(3, u8); - try expect(mem.eql(u8, &@as([3]u8, u8x3{ 255, 255, 255 }), &@as([3]u8, u8x3{ 255, 254, 1 } +| u8x3{ 1, 2, 255 }))); - const i8x3 = std.meta.Vector(3, i8); - try expect(mem.eql(i8, &@as([3]i8, i8x3{ 127, 127, 127 }), &@as([3]i8, i8x3{ 127, 126, 1 } +| i8x3{ 1, 2, 127 }))); + { // Broken out to avoid https://github.com/ziglang/zig/issues/11251 + const u8x3 = std.meta.Vector(3, u8); + const lhs = u8x3{ 255, 254, 1 }; + const rhs = u8x3{ 1, 2, 255 }; + const result = lhs +| rhs; + const expected = u8x3{ 255, 255, 255 }; + try expect(mem.eql(u8, &@as([3]u8, expected), &@as([3]u8, result))); + } + { // Broken out to avoid https://github.com/ziglang/zig/issues/11251 + const i8x3 = std.meta.Vector(3, i8); + const lhs = i8x3{ 127, 126, 1 }; + const rhs = i8x3{ 1, 2, 127 }; + const result = lhs +| rhs; + const expected = i8x3{ 127, 127, 127 }; + try expect(mem.eql(i8, &@as([3]i8, expected), &@as([3]i8, result))); + } } }; try S.doTheTest(); @@ -695,11 +812,21 @@ test "saturating add" { } test "saturating subtraction" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { + // Broken out to avoid https://github.com/ziglang/zig/issues/11251 const u8x3 = std.meta.Vector(3, u8); - try expect(mem.eql(u8, &@as([3]u8, u8x3{ 0, 0, 0 }), &@as([3]u8, u8x3{ 0, 0, 0 } -| u8x3{ 255, 255, 255 }))); + const lhs = u8x3{ 0, 0, 0 }; + const rhs = u8x3{ 255, 255, 255 }; + const result = lhs -| rhs; + const expected = u8x3{ 0, 0, 0 }; + try expect(mem.eql(u8, &@as([3]u8, expected), &@as([3]u8, result))); } }; try S.doTheTest(); @@ -707,14 +834,24 @@ test "saturating subtraction" { } test "saturating multiplication" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + // TODO: once #9660 has been solved, remove this line if (builtin.target.cpu.arch == .wasm32) return error.SkipZigTest; const S = struct { fn doTheTest() !void { + // Broken out to avoid https://github.com/ziglang/zig/issues/11251 const u8x3 = std.meta.Vector(3, u8); - try expect(mem.eql(u8, &@as([3]u8, u8x3{ 255, 255, 255 }), &@as([3]u8, u8x3{ 2, 2, 2 } *| u8x3{ 255, 255, 255 }))); + const lhs = u8x3{ 2, 2, 2 }; + const rhs = u8x3{ 255, 255, 255 }; + const result = lhs *| rhs; + const expected = u8x3{ 255, 255, 255 }; + try expect(mem.eql(u8, &@as([3]u8, expected), &@as([3]u8, result))); } }; @@ -723,11 +860,21 @@ test "saturating multiplication" { } test "saturating shift-left" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { + // Broken out to avoid https://github.com/ziglang/zig/issues/11251 const u8x3 = std.meta.Vector(3, u8); - try expect(mem.eql(u8, &@as([3]u8, u8x3{ 255, 255, 255 }), &@as([3]u8, u8x3{ 255, 255, 255 } <<| u8x3{ 1, 1, 1 }))); + const lhs = u8x3{ 1, 1, 1 }; + const rhs = u8x3{ 255, 255, 255 }; + const result = lhs <<| rhs; + const expected = u8x3{ 255, 255, 255 }; + try expect(mem.eql(u8, &@as([3]u8, expected), &@as([3]u8, result))); } }; try S.doTheTest(); From 513a53e9aaa6356faf385e257925a8949bd1b46a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 21 Mar 2022 17:03:05 -0700 Subject: [PATCH 0852/2031] Sema: restore propagation of error.GenericPoison for checking vectors --- src/Sema.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 500dec5246..208f33cf7c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14118,8 +14118,8 @@ fn checkVectorizableBinaryOperands( lhs_src: LazySrcLoc, rhs_src: LazySrcLoc, ) CompileError!void { - const lhs_zig_ty_tag = lhs_ty.zigTypeTag(); - const rhs_zig_ty_tag = rhs_ty.zigTypeTag(); + const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); + const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); if (lhs_zig_ty_tag == .Vector and rhs_zig_ty_tag == .Vector) { const lhs_len = lhs_ty.vectorLen(); const rhs_len = rhs_ty.vectorLen(); From 7eddef423d74318ef9190864232f2e224837461e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 21 Mar 2022 17:03:24 -0700 Subject: [PATCH 0853/2031] behavior tests: alter test coverage for vectors * Use `@Vector` syntax instead of `std.meta.Vector`. * Use `var` instead of `const` for tests so that we get runtime coverage instead of only comptime coverage. Comptime coverage is done with `comptime doTheTest()` calls. --- test/behavior/vector.zig | 117 +++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 59 deletions(-) diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 1d8dd9f661..1651e8107b 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -3,7 +3,6 @@ const builtin = @import("builtin"); const mem = std.mem; const math = std.math; const expect = std.testing.expect; -const Vector = std.meta.Vector; test "implicit cast vector to array - bool" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO @@ -14,7 +13,7 @@ test "implicit cast vector to array - bool" { const S = struct { fn doTheTest() !void { - const a: Vector(4, bool) = [_]bool{ true, false, true, false }; + const a: @Vector(4, bool) = [_]bool{ true, false, true, false }; const result_array: [4]bool = a; try expect(mem.eql(bool, &result_array, &[4]bool{ true, false, true, false })); } @@ -32,12 +31,12 @@ test "vector wrap operators" { const S = struct { fn doTheTest() !void { - var v: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 }; - var x: Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 }; + var v: @Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 }; + var x: @Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 }; try expect(mem.eql(i32, &@as([4]i32, v +% x), &[4]i32{ -2147483648, 2147483645, 33, 44 })); try expect(mem.eql(i32, &@as([4]i32, v -% x), &[4]i32{ 2147483646, 2147483647, 27, 36 })); try expect(mem.eql(i32, &@as([4]i32, v *% x), &[4]i32{ 2147483647, 2, 90, 160 })); - var z: Vector(4, i32) = [4]i32{ 1, 2, 3, -2147483648 }; + var z: @Vector(4, i32) = [4]i32{ 1, 2, 3, -2147483648 }; try expect(mem.eql(i32, &@as([4]i32, -%z), &[4]i32{ -1, -2, -3, -2147483648 })); } }; @@ -54,8 +53,8 @@ test "vector bin compares with mem.eql" { const S = struct { fn doTheTest() !void { - var v: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 }; - var x: Vector(4, i32) = [4]i32{ 1, 2147483647, 30, 4 }; + var v: @Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 }; + var x: @Vector(4, i32) = [4]i32{ 1, 2147483647, 30, 4 }; try expect(mem.eql(bool, &@as([4]bool, v == x), &[4]bool{ false, false, true, false })); try expect(mem.eql(bool, &@as([4]bool, v != x), &[4]bool{ true, true, false, true })); try expect(mem.eql(bool, &@as([4]bool, v < x), &[4]bool{ false, true, false, false })); @@ -77,8 +76,8 @@ test "vector int operators" { const S = struct { fn doTheTest() !void { - var v: Vector(4, i32) = [4]i32{ 10, 20, 30, 40 }; - var x: Vector(4, i32) = [4]i32{ 1, 2, 3, 4 }; + var v: @Vector(4, i32) = [4]i32{ 10, 20, 30, 40 }; + var x: @Vector(4, i32) = [4]i32{ 1, 2, 3, 4 }; try expect(mem.eql(i32, &@as([4]i32, v + x), &[4]i32{ 11, 22, 33, 44 })); try expect(mem.eql(i32, &@as([4]i32, v - x), &[4]i32{ 9, 18, 27, 36 })); try expect(mem.eql(i32, &@as([4]i32, v * x), &[4]i32{ 10, 40, 90, 160 })); @@ -98,8 +97,8 @@ test "vector float operators" { const S = struct { fn doTheTest() !void { - var v: Vector(4, f32) = [4]f32{ 10, 20, 30, 40 }; - var x: Vector(4, f32) = [4]f32{ 1, 2, 3, 4 }; + var v: @Vector(4, f32) = [4]f32{ 10, 20, 30, 40 }; + var x: @Vector(4, f32) = [4]f32{ 1, 2, 3, 4 }; try expect(mem.eql(f32, &@as([4]f32, v + x), &[4]f32{ 11, 22, 33, 44 })); try expect(mem.eql(f32, &@as([4]f32, v - x), &[4]f32{ 9, 18, 27, 36 })); try expect(mem.eql(f32, &@as([4]f32, v * x), &[4]f32{ 10, 40, 90, 160 })); @@ -119,8 +118,8 @@ test "vector bit operators" { const S = struct { fn doTheTest() !void { - var v: Vector(4, u8) = [4]u8{ 0b10101010, 0b10101010, 0b10101010, 0b10101010 }; - var x: Vector(4, u8) = [4]u8{ 0b11110000, 0b00001111, 0b10101010, 0b01010101 }; + var v: @Vector(4, u8) = [4]u8{ 0b10101010, 0b10101010, 0b10101010, 0b10101010 }; + var x: @Vector(4, u8) = [4]u8{ 0b11110000, 0b00001111, 0b10101010, 0b01010101 }; try expect(mem.eql(u8, &@as([4]u8, v ^ x), &[4]u8{ 0b01011010, 0b10100101, 0b00000000, 0b11111111 })); try expect(mem.eql(u8, &@as([4]u8, v | x), &[4]u8{ 0b11111010, 0b10101111, 0b10101010, 0b11111111 })); try expect(mem.eql(u8, &@as([4]u8, v & x), &[4]u8{ 0b10100000, 0b00001010, 0b10101010, 0b00000000 })); @@ -139,7 +138,7 @@ test "implicit cast vector to array" { const S = struct { fn doTheTest() !void { - var a: Vector(4, i32) = [_]i32{ 1, 2, 3, 4 }; + var a: @Vector(4, i32) = [_]i32{ 1, 2, 3, 4 }; var result_array: [4]i32 = a; result_array = a; try expect(mem.eql(i32, &result_array, &[4]i32{ 1, 2, 3, 4 })); @@ -160,7 +159,7 @@ test "array to vector" { fn doTheTest() !void { var foo: f32 = 3.14; var arr = [4]f32{ foo, 1.5, 0.0, 0.0 }; - var vec: Vector(4, f32) = arr; + var vec: @Vector(4, f32) = arr; try expect(mem.eql(f32, &@as([4]f32, vec), &arr)); } }; @@ -178,22 +177,22 @@ test "vector casts of sizes not divisible by 8" { const S = struct { fn doTheTest() !void { { - var v: Vector(4, u3) = [4]u3{ 5, 2, 3, 0 }; + var v: @Vector(4, u3) = [4]u3{ 5, 2, 3, 0 }; var x: [4]u3 = v; try expect(mem.eql(u3, &x, &@as([4]u3, v))); } { - var v: Vector(4, u2) = [4]u2{ 1, 2, 3, 0 }; + var v: @Vector(4, u2) = [4]u2{ 1, 2, 3, 0 }; var x: [4]u2 = v; try expect(mem.eql(u2, &x, &@as([4]u2, v))); } { - var v: Vector(4, u1) = [4]u1{ 1, 0, 1, 0 }; + var v: @Vector(4, u1) = [4]u1{ 1, 0, 1, 0 }; var x: [4]u1 = v; try expect(mem.eql(u1, &x, &@as([4]u1, v))); } { - var v: Vector(4, bool) = [4]bool{ false, false, true, false }; + var v: @Vector(4, bool) = [4]bool{ false, false, true, false }; var x: [4]bool = v; try expect(mem.eql(bool, &x, &@as([4]bool, v))); } @@ -214,7 +213,7 @@ test "vector @splat" { fn testForT(comptime N: comptime_int, v: anytype) !void { const T = @TypeOf(v); var vec = @splat(N, v); - try expect(Vector(N, T) == @TypeOf(vec)); + try expect(@Vector(N, T) == @TypeOf(vec)); var as_array = @as([N]T, vec); for (as_array) |elem| try expect(v == elem); } @@ -253,7 +252,7 @@ test "load vector elements via comptime index" { const S = struct { fn doTheTest() !void { - var v: Vector(4, i32) = [_]i32{ 1, 2, 3, undefined }; + var v: @Vector(4, i32) = [_]i32{ 1, 2, 3, undefined }; try expect(v[0] == 1); try expect(v[1] == 2); try expect(loadv(&v[2]) == 3); @@ -276,7 +275,7 @@ test "store vector elements via comptime index" { const S = struct { fn doTheTest() !void { - var v: Vector(4, i32) = [_]i32{ 1, 5, 3, undefined }; + var v: @Vector(4, i32) = [_]i32{ 1, 5, 3, undefined }; v[2] = 42; try expect(v[1] == 5); @@ -305,7 +304,7 @@ test "load vector elements via runtime index" { const S = struct { fn doTheTest() !void { - var v: Vector(4, i32) = [_]i32{ 1, 2, 3, undefined }; + var v: @Vector(4, i32) = [_]i32{ 1, 2, 3, undefined }; var i: u32 = 0; try expect(v[i] == 1); i += 1; @@ -328,7 +327,7 @@ test "store vector elements via runtime index" { const S = struct { fn doTheTest() !void { - var v: Vector(4, i32) = [_]i32{ 1, 5, 3, undefined }; + var v: @Vector(4, i32) = [_]i32{ 1, 5, 3, undefined }; var i: u32 = 2; v[i] = 1; try expect(v[1] == 5); @@ -351,7 +350,7 @@ test "initialize vector which is a struct field" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const Vec4Obj = struct { - data: Vector(4, f32), + data: @Vector(4, f32), }; const S = struct { @@ -376,17 +375,17 @@ test "vector comparison operators" { const S = struct { fn doTheTest() !void { { - const v1: Vector(4, bool) = [_]bool{ true, false, true, false }; - const v2: Vector(4, bool) = [_]bool{ false, true, false, true }; + var v1: @Vector(4, bool) = [_]bool{ true, false, true, false }; + var v2: @Vector(4, bool) = [_]bool{ false, true, false, true }; try expect(mem.eql(bool, &@as([4]bool, @splat(4, true)), &@as([4]bool, v1 == v1))); try expect(mem.eql(bool, &@as([4]bool, @splat(4, false)), &@as([4]bool, v1 == v2))); try expect(mem.eql(bool, &@as([4]bool, @splat(4, true)), &@as([4]bool, v1 != v2))); try expect(mem.eql(bool, &@as([4]bool, @splat(4, false)), &@as([4]bool, v2 != v2))); } { - const v1 = @splat(4, @as(u32, 0xc0ffeeee)); - const v2: Vector(4, c_uint) = v1; - const v3 = @splat(4, @as(u32, 0xdeadbeef)); + var v1 = @splat(4, @as(u32, 0xc0ffeeee)); + var v2: @Vector(4, c_uint) = v1; + var v3 = @splat(4, @as(u32, 0xdeadbeef)); try expect(mem.eql(bool, &@as([4]bool, @splat(4, true)), &@as([4]bool, v1 == v2))); try expect(mem.eql(bool, &@as([4]bool, @splat(4, false)), &@as([4]bool, v1 == v3))); try expect(mem.eql(bool, &@as([4]bool, @splat(4, true)), &@as([4]bool, v1 != v3))); @@ -414,7 +413,7 @@ test "vector division operators" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { - fn doTheTestDiv(comptime T: type, x: Vector(4, T), y: Vector(4, T)) !void { + fn doTheTestDiv(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) !void { if (!comptime std.meta.trait.isSignedInt(T)) { const d0 = x / y; for (@as([4]T, d0)) |v, i| { @@ -435,7 +434,7 @@ test "vector division operators" { } } - fn doTheTestMod(comptime T: type, x: Vector(4, T), y: Vector(4, T)) !void { + fn doTheTestMod(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) !void { if ((!comptime std.meta.trait.isSignedInt(T)) and @typeInfo(T) != .Float) { const r0 = x % y; for (@as([4]T, r0)) |v, i| { @@ -502,7 +501,7 @@ test "vector bitwise not operator" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { - fn doTheTestNot(comptime T: type, x: Vector(4, T)) !void { + fn doTheTestNot(comptime T: type, x: @Vector(4, T)) !void { var y = ~x; for (@as([4]T, y)) |v, i| { try expect(~x[i] == v); @@ -538,8 +537,8 @@ test "vector shift operators" { const TX = @typeInfo(@TypeOf(x)).Array.child; const TY = @typeInfo(@TypeOf(y)).Array.child; - var xv = @as(Vector(N, TX), x); - var yv = @as(Vector(N, TY), y); + var xv = @as(@Vector(N, TX), x); + var yv = @as(@Vector(N, TY), y); var z0 = xv >> yv; for (@as([N]TX, z0)) |v, i| { @@ -555,8 +554,8 @@ test "vector shift operators" { const TX = @typeInfo(@TypeOf(x)).Array.child; const TY = @typeInfo(@TypeOf(y)).Array.child; - var xv = @as(Vector(N, TX), x); - var yv = @as(Vector(N, TY), y); + var xv = @as(@Vector(N, TX), x); + var yv = @as(@Vector(N, TY), y); var z = if (dir == .Left) @shlExact(xv, yv) else @shrExact(xv, yv); for (@as([N]TX, z)) |v, i| { @@ -768,10 +767,10 @@ test "mask parameter of @shuffle is comptime scope" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - const __v4hi = std.meta.Vector(4, i16); + const __v4hi = @Vector(4, i16); var v4_a = __v4hi{ 0, 0, 0, 0 }; var v4_b = __v4hi{ 0, 0, 0, 0 }; - var shuffled: __v4hi = @shuffle(i16, v4_a, v4_b, std.meta.Vector(4, i32){ + var shuffled: __v4hi = @shuffle(i16, v4_a, v4_b, @Vector(4, i32){ std.zig.c_translation.shuffleVectorIndex(0, @typeInfo(@TypeOf(v4_a)).Vector.len), std.zig.c_translation.shuffleVectorIndex(0, @typeInfo(@TypeOf(v4_a)).Vector.len), std.zig.c_translation.shuffleVectorIndex(0, @typeInfo(@TypeOf(v4_a)).Vector.len), @@ -790,18 +789,18 @@ test "saturating add" { const S = struct { fn doTheTest() !void { { // Broken out to avoid https://github.com/ziglang/zig/issues/11251 - const u8x3 = std.meta.Vector(3, u8); - const lhs = u8x3{ 255, 254, 1 }; - const rhs = u8x3{ 1, 2, 255 }; - const result = lhs +| rhs; + const u8x3 = @Vector(3, u8); + var lhs = u8x3{ 255, 254, 1 }; + var rhs = u8x3{ 1, 2, 255 }; + var result = lhs +| rhs; const expected = u8x3{ 255, 255, 255 }; try expect(mem.eql(u8, &@as([3]u8, expected), &@as([3]u8, result))); } { // Broken out to avoid https://github.com/ziglang/zig/issues/11251 - const i8x3 = std.meta.Vector(3, i8); - const lhs = i8x3{ 127, 126, 1 }; - const rhs = i8x3{ 1, 2, 127 }; - const result = lhs +| rhs; + const i8x3 = @Vector(3, i8); + var lhs = i8x3{ 127, 126, 1 }; + var rhs = i8x3{ 1, 2, 127 }; + var result = lhs +| rhs; const expected = i8x3{ 127, 127, 127 }; try expect(mem.eql(i8, &@as([3]i8, expected), &@as([3]i8, result))); } @@ -821,10 +820,10 @@ test "saturating subtraction" { const S = struct { fn doTheTest() !void { // Broken out to avoid https://github.com/ziglang/zig/issues/11251 - const u8x3 = std.meta.Vector(3, u8); - const lhs = u8x3{ 0, 0, 0 }; - const rhs = u8x3{ 255, 255, 255 }; - const result = lhs -| rhs; + const u8x3 = @Vector(3, u8); + var lhs = u8x3{ 0, 0, 0 }; + var rhs = u8x3{ 255, 255, 255 }; + var result = lhs -| rhs; const expected = u8x3{ 0, 0, 0 }; try expect(mem.eql(u8, &@as([3]u8, expected), &@as([3]u8, result))); } @@ -846,10 +845,10 @@ test "saturating multiplication" { const S = struct { fn doTheTest() !void { // Broken out to avoid https://github.com/ziglang/zig/issues/11251 - const u8x3 = std.meta.Vector(3, u8); - const lhs = u8x3{ 2, 2, 2 }; - const rhs = u8x3{ 255, 255, 255 }; - const result = lhs *| rhs; + const u8x3 = @Vector(3, u8); + var lhs = u8x3{ 2, 2, 2 }; + var rhs = u8x3{ 255, 255, 255 }; + var result = lhs *| rhs; const expected = u8x3{ 255, 255, 255 }; try expect(mem.eql(u8, &@as([3]u8, expected), &@as([3]u8, result))); } @@ -869,10 +868,10 @@ test "saturating shift-left" { const S = struct { fn doTheTest() !void { // Broken out to avoid https://github.com/ziglang/zig/issues/11251 - const u8x3 = std.meta.Vector(3, u8); - const lhs = u8x3{ 1, 1, 1 }; - const rhs = u8x3{ 255, 255, 255 }; - const result = lhs <<| rhs; + const u8x3 = @Vector(3, u8); + var lhs = u8x3{ 1, 1, 1 }; + var rhs = u8x3{ 255, 255, 255 }; + var result = lhs <<| rhs; const expected = u8x3{ 255, 255, 255 }; try expect(mem.eql(u8, &@as([3]u8, expected), &@as([3]u8, result))); } From be579d479753153e78e55b17cb8fd2d55004145b Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Mon, 21 Mar 2022 22:03:25 +0100 Subject: [PATCH 0854/2031] wasm: Implement @popCount --- src/arch/wasm/CodeGen.zig | 48 +++++++++++++++++++++++++++++++++++++- src/arch/wasm/Emit.zig | 2 ++ src/arch/wasm/Mir.zig | 4 ++++ test/behavior/popcount.zig | 27 ++++++++++++++------- 4 files changed, 72 insertions(+), 9 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 8d515e9365..7d55e95f23 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1371,6 +1371,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .aggregate_init => self.airAggregateInit(inst), .union_init => self.airUnionInit(inst), .prefetch => self.airPrefetch(inst), + .popcount => self.airPopcount(inst), .slice => self.airSlice(inst), .slice_len => self.airSliceLen(inst), @@ -1419,7 +1420,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .frame_addr, .clz, .ctz, - .popcount, .byte_swap, .bit_reverse, .is_err_ptr, @@ -3565,3 +3565,49 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.memcpy(dst, src, len); return WValue{ .none = {} }; } + +fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const operand = try self.resolveInst(ty_op.operand); + const op_ty = self.air.typeOf(ty_op.operand); + + if (op_ty.zigTypeTag() == .Vector) { + return self.fail("TODO: Implement @popCount for vectors", .{}); + } + + const int_info = op_ty.intInfo(self.target); + const bits = int_info.bits; + const wasm_bits = toWasmBits(bits) orelse { + return self.fail("TODO: Implement @popCount for integers with bitsize '{d}'", .{bits}); + }; + + try self.emitWValue(operand); + + // for signed integers we first mask the signedness bit + if (int_info.signedness == .signed and wasm_bits != bits) { + switch (wasm_bits) { + 32 => { + const mask = (@as(u32, 1) << @intCast(u5, bits)) - 1; + try self.addImm32(@bitCast(i32, mask)); + try self.addTag(.i32_and); + }, + 64 => { + const mask = (@as(u64, 1) << @intCast(u6, bits)) - 1; + try self.addImm64(mask); + try self.addTag(.i64_and); + }, + else => unreachable, + } + } + + switch (wasm_bits) { + 32 => try self.addTag(.i32_popcnt), + 64 => try self.addTag(.i64_popcnt), + else => unreachable, + } + + const result = try self.allocLocal(op_ty); + try self.addLabel(.local_set, result.local); + return result; +} diff --git a/src/arch/wasm/Emit.zig b/src/arch/wasm/Emit.zig index e0b40794b8..f0f4cfae5d 100644 --- a/src/arch/wasm/Emit.zig +++ b/src/arch/wasm/Emit.zig @@ -207,6 +207,8 @@ pub fn emitMir(emit: *Emit) InnerError!void { .i32_rem_u => try emit.emitTag(tag), .i64_rem_s => try emit.emitTag(tag), .i64_rem_u => try emit.emitTag(tag), + .i32_popcnt => try emit.emitTag(tag), + .i64_popcnt => try emit.emitTag(tag), .extended => try emit.emitExtended(inst), } diff --git a/src/arch/wasm/Mir.zig b/src/arch/wasm/Mir.zig index b24af64ff5..09a0c32901 100644 --- a/src/arch/wasm/Mir.zig +++ b/src/arch/wasm/Mir.zig @@ -317,6 +317,8 @@ pub const Inst = struct { /// Uses `tag` f64_ge = 0x66, /// Uses `tag` + i32_popcnt = 0x69, + /// Uses `tag` i32_add = 0x6A, /// Uses `tag` i32_sub = 0x6B, @@ -343,6 +345,8 @@ pub const Inst = struct { /// Uses `tag` i32_shr_u = 0x76, /// Uses `tag` + i64_popcnt = 0x7B, + /// Uses `tag` i64_add = 0x7C, /// Uses `tag` i64_sub = 0x7D, diff --git a/test/behavior/popcount.zig b/test/behavior/popcount.zig index 16f33f5514..9658e694a8 100644 --- a/test/behavior/popcount.zig +++ b/test/behavior/popcount.zig @@ -4,7 +4,6 @@ const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; test "@popCount integers" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -13,6 +12,25 @@ test "@popCount integers" { try testPopCountIntegers(); } +test "@popCount 128bit integer" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + comptime { + try expect(@popCount(u128, @as(u128, 0b11111111000110001100010000100001000011000011100101010001)) == 24); + try expect(@popCount(i128, @as(i128, 0b11111111000110001100010000100001000011000011100101010001)) == 24); + } + + { + var x: u128 = 0b11111111000110001100010000100001000011000011100101010001; + try expect(@popCount(u128, x) == 24); + } + + try expect(@popCount(i128, @as(i128, 0b11111111000110001100010000100001000011000011100101010001)) == 24); +} + fn testPopCountIntegers() !void { { var x: u32 = 0xffffffff; @@ -42,16 +60,9 @@ fn testPopCountIntegers() !void { var x: i8 = -120; try expect(@popCount(i8, x) == 2); } - { - var x: u128 = 0b11111111000110001100010000100001000011000011100101010001; - try expect(@popCount(u128, x) == 24); - } comptime { try expect(@popCount(u8, @bitCast(u8, @as(i8, -120))) == 2); } - comptime { - try expect(@popCount(i128, @as(i128, 0b11111111000110001100010000100001000011000011100101010001)) == 24); - } } test "@popCount vectors" { From 0fb005d1d05acb680389ce8be51bab6020309922 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 21 Mar 2022 19:07:20 -0700 Subject: [PATCH 0855/2031] Sema: dummy implementation of `@errorReturnTrace` Also update std/build.zig to use stage2 function pointer semantics. This gets us a little bit closer to `zig build` working, although it is now hitting a new crash in the compiler. --- lib/std/build.zig | 9 +++++++-- lib/std/start.zig | 9 ++++----- src/Sema.zig | 6 +++++- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 4df9e7bf6e..29a5d80705 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -3256,11 +3256,16 @@ const ThisModule = @This(); pub const Step = struct { id: Id, name: []const u8, - makeFn: fn (self: *Step) anyerror!void, + makeFn: MakeFn, dependencies: ArrayList(*Step), loop_flag: bool, done_flag: bool, + const MakeFn = switch (builtin.zig_backend) { + .stage1 => fn (self: *Step) anyerror!void, + else => *const fn (self: *Step) anyerror!void, + }; + pub const Id = enum { top_level, lib_exe_obj, @@ -3279,7 +3284,7 @@ pub const Step = struct { custom, }; - pub fn init(id: Id, name: []const u8, allocator: Allocator, makeFn: fn (*Step) anyerror!void) Step { + pub fn init(id: Id, name: []const u8, allocator: Allocator, makeFn: MakeFn) Step { return Step{ .id = id, .name = allocator.dupe(u8, name) catch unreachable, diff --git a/lib/std/start.zig b/lib/std/start.zig index 3502645172..cd247c915e 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -408,11 +408,6 @@ fn posixCallMainAndExit() noreturn { // Initialize the TLS area. std.os.linux.tls.initStaticTLS(phdrs); - if (builtin.zig_backend == .stage2_llvm) { - root.main(); - exit2(0); - } - // The way Linux executables represent stack size is via the PT_GNU_STACK // program header. However the kernel does not recognize it; it always gives 8 MiB. // Here we look for the stack size in our program headers and use setrlimit @@ -454,6 +449,10 @@ fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { std.os.argv = argv[0..argc]; std.os.environ = envp; + if (builtin.zig_backend == .stage2_llvm) { + return @call(.{ .modifier = .always_inline }, callMain, .{}); + } + std.debug.maybeEnableSegfaultHandler(); return initEventLoopAndCallMain(); diff --git a/src/Sema.zig b/src/Sema.zig index 208f33cf7c..61c46bcce7 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12439,7 +12439,11 @@ fn zirErrorReturnTrace( extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; - return sema.fail(block, src, "TODO: Sema.zirErrorReturnTrace", .{}); + const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); + const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); + const opt_stack_trace_ty = try Type.optional(sema.arena, stack_trace_ty); + // https://github.com/ziglang/zig/issues/11259 + return sema.addConstant(opt_stack_trace_ty, Value.@"null"); } fn zirFrame( From 91fd0f42c88f4bea424b5a5c58435a2a98b57a58 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 20 Mar 2022 20:04:18 -0700 Subject: [PATCH 0856/2031] stage2: out of bounds error for slicing --- src/Sema.zig | 30 ++++++++++++++++++++++++++++++ test/behavior/cast.zig | 6 +++++- test/compile_errors.zig | 23 +++++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 61c46bcce7..2a3eff928b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -19587,6 +19587,14 @@ fn analyzeSlice( if (!end_is_len) { const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); if (try sema.resolveMaybeUndefVal(block, end_src, end)) |end_val| { + if (end_val.compare(.gt, len_val, Type.usize)) { + return sema.fail( + block, + end_src, + "end index {} out of bounds for array of length {}", + .{ end_val.fmtValue(Type.usize), len_val.fmtValue(Type.usize) }, + ); + } if (end_val.eql(len_val, Type.usize)) { end_is_len = true; } @@ -19605,6 +19613,14 @@ fn analyzeSlice( .data = slice_val.sliceLen(), }; const slice_len_val = Value.initPayload(&int_payload.base); + if (end_val.compare(.gt, slice_len_val, Type.usize)) { + return sema.fail( + block, + end_src, + "end index {} out of bounds for slice of length {}", + .{ end_val.fmtValue(Type.usize), slice_len_val.fmtValue(Type.usize) }, + ); + } if (end_val.eql(slice_len_val, Type.usize)) { end_is_len = true; } @@ -19635,6 +19651,20 @@ fn analyzeSlice( break :s null; }; + // requirement: start <= end + if (try sema.resolveDefinedValue(block, src, end)) |end_val| { + if (try sema.resolveDefinedValue(block, src, start)) |start_val| { + if (start_val.compare(.gt, end_val, Type.usize)) { + return sema.fail( + block, + start_src, + "start index {} is larger than end index {}", + .{ start_val.fmtValue(Type.usize), end_val.fmtValue(Type.usize) }, + ); + } + } + } + const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src); const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 4e2b5dad7b..061a8219b8 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -906,7 +906,11 @@ test "peer cast [*:x]T to [*]T" { } test "peer cast [:x]T to [*:x]T" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 81083cb049..c479dd9c88 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -21,6 +21,29 @@ pub fn addCases(ctx: *TestContext) !void { , &[_][]const u8{ ":2:20: error: import of file outside package path: '../../above.zig'", }); + + case.addError( + \\comptime { + \\ var array = [_:0]u8{ 1, 2, 3, 4 }; + \\ var src_slice: [:0]u8 = &array; + \\ var slice = src_slice[2..5]; + \\ _ = slice; + \\} + \\comptime { + \\ var array = [_:0]u8{ 1, 2, 3, 4 }; + \\ var slice = array[2..5]; + \\ _ = slice; + \\} + \\comptime { + \\ var array = [_:0]u8{ 1, 2, 3, 4 }; + \\ var slice = array[3..2]; + \\ _ = slice; + \\} + , &[_][]const u8{ + ":4:26: error: end index 5 out of bounds for slice of length 4", + ":9:22: error: end index 5 out of bounds for array of length 4", + ":14:22: error: start index 3 is larger than end index 2", + }); } ctx.objErrStage1("exported enum without explicit integer tag type", From 0376fd09bc9f29ceeb83760e32532923e4fe7f98 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 19 Mar 2022 16:54:11 +0100 Subject: [PATCH 0857/2031] macho: extend CodeSignature to accept entitlements With this change, we can now bake in entitlements into the binary. Additionally, I see this as the first step towards full code signature support which includes baking in Apple issued certificates for redistribution, etc. --- lib/std/build.zig | 7 + src/Compilation.zig | 4 + src/link.zig | 3 + src/link/MachO.zig | 84 +++--- src/link/MachO/CodeSignature.zig | 425 +++++++++++++++++++++++-------- src/main.zig | 7 + 6 files changed, 386 insertions(+), 144 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 29a5d80705..f1287c7be5 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1570,6 +1570,9 @@ pub const LibExeObjStep = struct { /// (Darwin) Install name for the dylib install_name: ?[]const u8 = null, + /// (Darwin) Path to entitlements file + entitlements: ?[]const u8 = null, + /// Position Independent Code force_pic: ?bool = null, @@ -2515,6 +2518,10 @@ pub const LibExeObjStep = struct { } } + if (self.entitlements) |entitlements| { + try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements }); + } + if (self.bundle_compiler_rt) |x| { if (x) { try zig_args.append("-fcompiler-rt"); diff --git a/src/Compilation.zig b/src/Compilation.zig index f6fe452951..64848659a7 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -815,6 +815,8 @@ pub const InitOptions = struct { native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null, /// (Darwin) Install name of the dylib install_name: ?[]const u8 = null, + /// (Darwin) Path to entitlements file + entitlements: ?[]const u8 = null, }; fn addPackageTableToCacheHash( @@ -1624,6 +1626,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .enable_link_snapshots = options.enable_link_snapshots, .native_darwin_sdk = options.native_darwin_sdk, .install_name = options.install_name, + .entitlements = options.entitlements, }); errdefer bin_file.destroy(); comp.* = .{ @@ -2351,6 +2354,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes // Mach-O specific stuff man.hash.addListOfBytes(comp.bin_file.options.framework_dirs); man.hash.addListOfBytes(comp.bin_file.options.frameworks); + try man.addOptionalFile(comp.bin_file.options.entitlements); // COFF specific stuff man.hash.addOptional(comp.bin_file.options.subsystem); diff --git a/src/link.zig b/src/link.zig index 3a18f204e8..d431e9d5f1 100644 --- a/src/link.zig +++ b/src/link.zig @@ -183,6 +183,9 @@ pub const Options = struct { /// (Darwin) Install name for the dylib install_name: ?[]const u8 = null, + /// (Darwin) Path to entitlements file + entitlements: ?[]const u8 = null, + pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { return if (options.use_lld) .Obj else options.output_mode; } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 1cab5fe067..8bb0101301 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -58,11 +58,6 @@ d_sym: ?DebugSymbols = null, /// For x86_64 that's 4KB, whereas for aarch64, that's 16KB. page_size: u16, -/// TODO Should we figure out embedding code signatures for other Apple platforms as part of the linker? -/// Or should this be a separate tool? -/// https://github.com/ziglang/zig/issues/9567 -requires_adhoc_codesig: bool, - /// If true, the linker will preallocate several sections and segments before starting the linking /// process. This is for example true for stage2 debug builds, however, this is false for stage1 /// and potentially stage2 release builds in the future. @@ -76,6 +71,9 @@ header_pad: u16 = 0x1000, /// The absolute address of the entry point. entry_addr: ?u64 = null, +/// Code signature (if any) +code_signature: ?CodeSignature = null, + objects: std.ArrayListUnmanaged(Object) = .{}, archives: std.ArrayListUnmanaged(Archive) = .{}, @@ -402,7 +400,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO { .file = null, }, .page_size = page_size, - .requires_adhoc_codesig = requires_adhoc_codesig, + .code_signature = if (requires_adhoc_codesig) CodeSignature.init(page_size) else null, .needs_prealloc = needs_prealloc, }; @@ -534,6 +532,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { } link.hashAddSystemLibs(&man.hash, self.base.options.system_libs); man.hash.addOptionalBytes(self.base.options.sysroot); + try man.addOptionalFile(self.base.options.entitlements); // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. _ = try man.hit(); @@ -859,6 +858,19 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { self.load_commands_dirty = true; } + // code signature and entitlements + if (self.base.options.entitlements) |path| { + if (self.code_signature) |*csig| { + try csig.addEntitlements(self.base.allocator, path); + csig.code_directory.ident = self.base.options.emit.?.sub_path; + } else { + var csig = CodeSignature.init(self.page_size); + try csig.addEntitlements(self.base.allocator, path); + csig.code_directory.ident = self.base.options.emit.?.sub_path; + self.code_signature = csig; + } + } + if (self.base.options.verbose_link) { var argv = std.ArrayList([]const u8).init(arena); @@ -1033,13 +1045,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { try d_sym.flushModule(self.base.allocator, self.base.options); } - if (self.requires_adhoc_codesig) { + if (self.code_signature) |*csig| { + csig.clear(self.base.allocator); + csig.code_directory.ident = self.base.options.emit.?.sub_path; // Preallocate space for the code signature. // We need to do this at this stage so that we have the load commands with proper values // written out to the file. // The most important here is to have the correct vm and filesize of the __LINKEDIT segment // where the code signature goes into. - try self.writeCodeSignaturePadding(); + try self.writeCodeSignaturePadding(csig); } try self.writeLoadCommands(); @@ -1055,8 +1069,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { assert(!self.load_commands_dirty); - if (self.requires_adhoc_codesig) { - try self.writeCodeSignature(); // code signing always comes last + if (self.code_signature) |*csig| { + try self.writeCodeSignature(csig); // code signing always comes last } if (build_options.enable_link_snapshots) { @@ -3315,7 +3329,7 @@ fn addLoadDylibLC(self: *MachO, id: u16) !void { } fn addCodeSignatureLC(self: *MachO) !void { - if (self.code_signature_cmd_index != null or !self.requires_adhoc_codesig) return; + if (self.code_signature_cmd_index != null or self.code_signature == null) return; self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); try self.load_commands.append(self.base.allocator, .{ .linkedit_data = .{ @@ -3429,6 +3443,10 @@ pub fn deinit(self: *MachO) void { } self.atom_by_index_table.deinit(self.base.allocator); + + if (self.code_signature) |*csig| { + csig.deinit(self.base.allocator); + } } pub fn closeFiles(self: MachO) void { @@ -6143,7 +6161,7 @@ fn writeLinkeditSegment(self: *MachO) !void { seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size); } -fn writeCodeSignaturePadding(self: *MachO) !void { +fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { const tracy = trace(@src()); defer tracy.end(); @@ -6153,11 +6171,7 @@ fn writeCodeSignaturePadding(self: *MachO) !void { // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271 const fileoff = mem.alignForwardGeneric(u64, linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize, 16); const padding = fileoff - (linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize); - const needed_size = CodeSignature.calcCodeSignaturePaddingSize( - self.base.options.emit.?.sub_path, - fileoff, - self.page_size, - ); + const needed_size = code_sig.estimateSize(fileoff); code_sig_cmd.dataoff = @intCast(u32, fileoff); code_sig_cmd.datasize = needed_size; @@ -6173,34 +6187,30 @@ fn writeCodeSignaturePadding(self: *MachO) !void { self.load_commands_dirty = true; } -fn writeCodeSignature(self: *MachO) !void { +fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void { const tracy = trace(@src()); defer tracy.end(); const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment; const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data; - var code_sig: CodeSignature = .{}; - defer code_sig.deinit(self.base.allocator); + var buffer = std.ArrayList(u8).init(self.base.allocator); + defer buffer.deinit(); + try buffer.ensureTotalCapacityPrecise(code_sig.size()); + try code_sig.writeAdhocSignature(self.base.allocator, .{ + .file = self.base.file.?, + .text_segment = text_segment.inner, + .code_sig_cmd = code_sig_cmd, + .output_mode = self.base.options.output_mode, + }, buffer.writer()); + assert(buffer.items.len == code_sig.size()); - try code_sig.calcAdhocSignature( - self.base.allocator, - self.base.file.?, - self.base.options.emit.?.sub_path, - text_segment.inner, - code_sig_cmd, - self.base.options.output_mode, - self.page_size, - ); + log.debug("writing code signature from 0x{x} to 0x{x}", .{ + code_sig_cmd.dataoff, + code_sig_cmd.dataoff + buffer.items.len, + }); - var buffer = try self.base.allocator.alloc(u8, code_sig.size()); - defer self.base.allocator.free(buffer); - var stream = std.io.fixedBufferStream(buffer); - try code_sig.write(stream.writer()); - - log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len }); - - try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff); + try self.base.file.?.pwriteAll(buffer.items, code_sig_cmd.dataoff); } /// Writes all load commands and section headers. diff --git a/src/link/MachO/CodeSignature.zig b/src/link/MachO/CodeSignature.zig index 41c7c07741..6f2d516bf5 100644 --- a/src/link/MachO/CodeSignature.zig +++ b/src/link/MachO/CodeSignature.zig @@ -12,12 +12,102 @@ const Sha256 = std.crypto.hash.sha2.Sha256; const hash_size: u8 = 32; +const Blob = union(enum) { + code_directory: *CodeDirectory, + requirements: *Requirements, + entitlements: *Entitlements, + signature: *Signature, + + fn slotType(self: Blob) u32 { + return switch (self) { + .code_directory => |x| x.slotType(), + .requirements => |x| x.slotType(), + .entitlements => |x| x.slotType(), + .signature => |x| x.slotType(), + }; + } + + fn size(self: Blob) u32 { + return switch (self) { + .code_directory => |x| x.size(), + .requirements => |x| x.size(), + .entitlements => |x| x.size(), + .signature => |x| x.size(), + }; + } + + fn write(self: Blob, writer: anytype) !void { + return switch (self) { + .code_directory => |x| x.write(writer), + .requirements => |x| x.write(writer), + .entitlements => |x| x.write(writer), + .signature => |x| x.write(writer), + }; + } +}; + const CodeDirectory = struct { inner: macho.CodeDirectory, - data: std.ArrayListUnmanaged(u8) = .{}, + ident: []const u8, + special_slots: [n_special_slots][hash_size]u8, + code_slots: std.ArrayListUnmanaged([hash_size]u8) = .{}, + + const n_special_slots: usize = 7; + + fn init(page_size: u16) CodeDirectory { + var cdir: CodeDirectory = .{ + .inner = .{ + .magic = macho.CSMAGIC_CODEDIRECTORY, + .length = @sizeOf(macho.CodeDirectory), + .version = macho.CS_SUPPORTSEXECSEG, + .flags = macho.CS_ADHOC, + .hashOffset = 0, + .identOffset = @sizeOf(macho.CodeDirectory), + .nSpecialSlots = 0, + .nCodeSlots = 0, + .codeLimit = 0, + .hashSize = hash_size, + .hashType = macho.CS_HASHTYPE_SHA256, + .platform = 0, + .pageSize = @truncate(u8, std.math.log2(page_size)), + .spare2 = 0, + .scatterOffset = 0, + .teamOffset = 0, + .spare3 = 0, + .codeLimit64 = 0, + .execSegBase = 0, + .execSegLimit = 0, + .execSegFlags = 0, + }, + .ident = undefined, + .special_slots = undefined, + }; + comptime var i = 0; + inline while (i < n_special_slots) : (i += 1) { + cdir.special_slots[i] = [_]u8{0} ** hash_size; + } + return cdir; + } + + fn deinit(self: *CodeDirectory, allocator: Allocator) void { + self.code_slots.deinit(allocator); + } + + fn addSpecialHash(self: *CodeDirectory, index: u32, hash: [hash_size]u8) void { + assert(index > 0); + self.inner.nSpecialSlots = std.math.max(self.inner.nSpecialSlots, index); + mem.copy(u8, &self.special_slots[index - 1], &hash); + } + + fn slotType(self: CodeDirectory) u32 { + _ = self; + return macho.CSSLOT_CODEDIRECTORY; + } fn size(self: CodeDirectory) u32 { - return self.inner.length; + const code_slots = self.inner.nCodeSlots * hash_size; + const special_slots = self.inner.nSpecialSlots * hash_size; + return @sizeOf(macho.CodeDirectory) + @intCast(u32, self.ident.len + 1) + special_slots + code_slots; } fn write(self: CodeDirectory, writer: anytype) !void { @@ -42,142 +132,263 @@ const CodeDirectory = struct { try writer.writeIntBig(u64, self.inner.execSegBase); try writer.writeIntBig(u64, self.inner.execSegLimit); try writer.writeIntBig(u64, self.inner.execSegFlags); - try writer.writeAll(self.data.items); + + try writer.writeAll(self.ident); + try writer.writeByte(0); + + var i: isize = @intCast(isize, self.inner.nSpecialSlots); + while (i > 0) : (i -= 1) { + try writer.writeAll(&self.special_slots[@intCast(usize, i - 1)]); + } + + for (self.code_slots.items) |slot| { + try writer.writeAll(&slot); + } } }; -/// Code signature blob header. -inner: macho.SuperBlob = .{ - .magic = macho.CSMAGIC_EMBEDDED_SIGNATURE, - .length = @sizeOf(macho.SuperBlob), - .count = 0, -}, +const Requirements = struct { + fn deinit(self: *Requirements, allocator: Allocator) void { + _ = self; + _ = allocator; + } -/// CodeDirectory header which holds the hash of the binary. -cdir: ?CodeDirectory = null, + fn slotType(self: Requirements) u32 { + _ = self; + return macho.CSSLOT_REQUIREMENTS; + } -pub fn calcAdhocSignature( - self: *CodeSignature, - allocator: Allocator, + fn size(self: Requirements) u32 { + _ = self; + return 3 * @sizeOf(u32); + } + + fn write(self: Requirements, writer: anytype) !void { + try writer.writeIntBig(u32, macho.CSMAGIC_REQUIREMENTS); + try writer.writeIntBig(u32, self.size()); + try writer.writeIntBig(u32, 0); + } +}; + +const Entitlements = struct { + inner: []const u8, + + fn deinit(self: *Entitlements, allocator: Allocator) void { + allocator.free(self.inner); + } + + fn slotType(self: Entitlements) u32 { + _ = self; + return macho.CSSLOT_ENTITLEMENTS; + } + + fn size(self: Entitlements) u32 { + return @intCast(u32, self.inner.len) + 2 * @sizeOf(u32); + } + + fn write(self: Entitlements, writer: anytype) !void { + try writer.writeIntBig(u32, macho.CSMAGIC_EMBEDDED_ENTITLEMENTS); + try writer.writeIntBig(u32, self.size()); + try writer.writeAll(self.inner); + } +}; + +const Signature = struct { + fn deinit(self: *Signature, allocator: Allocator) void { + _ = self; + _ = allocator; + } + + fn slotType(self: Signature) u32 { + _ = self; + return macho.CSSLOT_SIGNATURESLOT; + } + + fn size(self: Signature) u32 { + _ = self; + return 2 * @sizeOf(u32); + } + + fn write(self: Signature, writer: anytype) !void { + try writer.writeIntBig(u32, macho.CSMAGIC_BLOBWRAPPER); + try writer.writeIntBig(u32, self.size()); + } +}; + +page_size: u16, +code_directory: CodeDirectory, +requirements: ?Requirements = null, +entitlements: ?Entitlements = null, +signature: ?Signature = null, + +pub fn init(page_size: u16) CodeSignature { + return .{ + .page_size = page_size, + .code_directory = CodeDirectory.init(page_size), + }; +} + +pub fn deinit(self: *CodeSignature, allocator: Allocator) void { + self.code_directory.deinit(allocator); + if (self.requirements) |*req| { + req.deinit(allocator); + } + if (self.entitlements) |*ents| { + ents.deinit(allocator); + } + if (self.signature) |*sig| { + sig.deinit(allocator); + } +} + +pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const u8) !void { + const file = try fs.cwd().openFile(path, .{}); + defer file.close(); + const inner = try file.readToEndAlloc(allocator, std.math.maxInt(u32)); + self.entitlements = .{ .inner = inner }; +} + +pub const WriteOpts = struct { file: fs.File, - id: []const u8, text_segment: macho.segment_command_64, code_sig_cmd: macho.linkedit_data_command, output_mode: std.builtin.OutputMode, - page_size: u16, +}; + +pub fn writeAdhocSignature( + self: *CodeSignature, + allocator: Allocator, + opts: WriteOpts, + writer: anytype, ) !void { - const execSegBase: u64 = text_segment.fileoff; - const execSegLimit: u64 = text_segment.filesize; - const execSegFlags: u64 = if (output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0; - const file_size = code_sig_cmd.dataoff; - var cdir = CodeDirectory{ - .inner = .{ - .magic = macho.CSMAGIC_CODEDIRECTORY, - .length = @sizeOf(macho.CodeDirectory), - .version = macho.CS_SUPPORTSEXECSEG, - .flags = macho.CS_ADHOC, - .hashOffset = 0, - .identOffset = 0, - .nSpecialSlots = 0, - .nCodeSlots = 0, - .codeLimit = file_size, - .hashSize = hash_size, - .hashType = macho.CS_HASHTYPE_SHA256, - .platform = 0, - .pageSize = @truncate(u8, std.math.log2(page_size)), - .spare2 = 0, - .scatterOffset = 0, - .teamOffset = 0, - .spare3 = 0, - .codeLimit64 = 0, - .execSegBase = execSegBase, - .execSegLimit = execSegLimit, - .execSegFlags = execSegFlags, - }, + var header: macho.SuperBlob = .{ + .magic = macho.CSMAGIC_EMBEDDED_SIGNATURE, + .length = @sizeOf(macho.SuperBlob), + .count = 0, }; - const total_pages = mem.alignForward(file_size, page_size) / page_size; + var blobs = std.ArrayList(Blob).init(allocator); + defer blobs.deinit(); - var hash: [hash_size]u8 = undefined; - var buffer = try allocator.alloc(u8, page_size); + self.code_directory.inner.execSegBase = opts.text_segment.fileoff; + self.code_directory.inner.execSegLimit = opts.text_segment.filesize; + self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0; + const file_size = opts.code_sig_cmd.dataoff; + self.code_directory.inner.codeLimit = file_size; + + const total_pages = mem.alignForward(file_size, self.page_size) / self.page_size; + + var buffer = try allocator.alloc(u8, self.page_size); defer allocator.free(buffer); - try cdir.data.ensureTotalCapacityPrecise(allocator, total_pages * hash_size + id.len + 1); + try self.code_directory.code_slots.ensureTotalCapacityPrecise(allocator, total_pages); - // 1. Save the identifier and update offsets - cdir.inner.identOffset = cdir.inner.length; - cdir.data.appendSliceAssumeCapacity(id); - cdir.data.appendAssumeCapacity(0); - - // 2. Calculate hash for each page (in file) and write it to the buffer - // TODO figure out how we can cache several hashes since we won't update - // every page during incremental linking - cdir.inner.hashOffset = cdir.inner.identOffset + @intCast(u32, id.len) + 1; + // Calculate hash for each page (in file) and write it to the buffer + var hash: [hash_size]u8 = undefined; var i: usize = 0; while (i < total_pages) : (i += 1) { - const fstart = i * page_size; - const fsize = if (fstart + page_size > file_size) file_size - fstart else page_size; - const len = try file.preadAll(buffer, fstart); + const fstart = i * self.page_size; + const fsize = if (fstart + self.page_size > file_size) file_size - fstart else self.page_size; + const len = try opts.file.preadAll(buffer, fstart); assert(fsize <= len); Sha256.hash(buffer[0..fsize], &hash, .{}); - cdir.data.appendSliceAssumeCapacity(&hash); - cdir.inner.nCodeSlots += 1; + self.code_directory.code_slots.appendAssumeCapacity(hash); + self.code_directory.inner.nCodeSlots += 1; } - // 3. Update CodeDirectory length - cdir.inner.length += @intCast(u32, cdir.data.items.len); + try blobs.append(.{ .code_directory = &self.code_directory }); + header.length += @sizeOf(macho.BlobIndex); + header.count += 1; - self.inner.length += @sizeOf(macho.BlobIndex) + cdir.size(); - self.inner.count = 1; - self.cdir = cdir; + if (self.requirements) |*req| { + var buf = std.ArrayList(u8).init(allocator); + defer buf.deinit(); + try req.write(buf.writer()); + Sha256.hash(buf.items, &hash, .{}); + self.code_directory.addSpecialHash(req.slotType(), hash); + + try blobs.append(.{ .requirements = req }); + header.count += 1; + header.length += @sizeOf(macho.BlobIndex) + req.size(); + } + + if (self.entitlements) |*ents| { + var buf = std.ArrayList(u8).init(allocator); + defer buf.deinit(); + try ents.write(buf.writer()); + Sha256.hash(buf.items, &hash, .{}); + self.code_directory.addSpecialHash(ents.slotType(), hash); + + try blobs.append(.{ .entitlements = ents }); + header.count += 1; + header.length += @sizeOf(macho.BlobIndex) + ents.size(); + } + + if (self.signature) |*sig| { + try blobs.append(.{ .signature = sig }); + header.count += 1; + header.length += @sizeOf(macho.BlobIndex) + sig.size(); + } + + self.code_directory.inner.hashOffset = + @sizeOf(macho.CodeDirectory) + @intCast(u32, self.code_directory.ident.len + 1) + self.code_directory.inner.nSpecialSlots * hash_size; + self.code_directory.inner.length = self.code_directory.size(); + header.length += self.code_directory.size(); + + try writer.writeIntBig(u32, header.magic); + try writer.writeIntBig(u32, header.length); + try writer.writeIntBig(u32, header.count); + + var offset: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) * @intCast(u32, blobs.items.len); + for (blobs.items) |blob| { + try writer.writeIntBig(u32, blob.slotType()); + try writer.writeIntBig(u32, offset); + offset += blob.size(); + } + + for (blobs.items) |blob| { + try blob.write(writer); + } } pub fn size(self: CodeSignature) u32 { - return self.inner.length; -} - -pub fn write(self: CodeSignature, writer: anytype) !void { - try self.writeHeader(writer); - const offset: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex); - try writeBlobIndex(macho.CSSLOT_CODEDIRECTORY, offset, writer); - try self.cdir.?.write(writer); -} - -pub fn deinit(self: *CodeSignature, allocator: Allocator) void { - if (self.cdir) |*cdir| { - cdir.data.deinit(allocator); + var ssize: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size(); + if (self.requirements) |req| { + ssize += @sizeOf(macho.BlobIndex) + req.size(); } + if (self.entitlements) |ent| { + ssize += @sizeOf(macho.BlobIndex) + ent.size(); + } + if (self.signature) |sig| { + ssize += @sizeOf(macho.BlobIndex) + sig.size(); + } + return ssize; } -fn writeHeader(self: CodeSignature, writer: anytype) !void { - try writer.writeIntBig(u32, self.inner.magic); - try writer.writeIntBig(u32, self.inner.length); - try writer.writeIntBig(u32, self.inner.count); +pub fn estimateSize(self: CodeSignature, file_size: u64) u32 { + var ssize: u64 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size(); + // Approx code slots + const total_pages = mem.alignForwardGeneric(u64, file_size, self.page_size) / self.page_size; + ssize += total_pages * hash_size; + var n_special_slots: u32 = 0; + if (self.requirements) |req| { + ssize += @sizeOf(macho.BlobIndex) + req.size(); + n_special_slots = std.math.max(n_special_slots, req.slotType()); + } + if (self.entitlements) |ent| { + ssize += @sizeOf(macho.BlobIndex) + ent.size() + hash_size; + n_special_slots = std.math.max(n_special_slots, ent.slotType()); + } + if (self.signature) |sig| { + ssize += @sizeOf(macho.BlobIndex) + sig.size(); + } + ssize += n_special_slots * hash_size; + return @intCast(u32, mem.alignForwardGeneric(u64, ssize, @sizeOf(u64))); } -fn writeBlobIndex(tt: u32, offset: u32, writer: anytype) !void { - try writer.writeIntBig(u32, tt); - try writer.writeIntBig(u32, offset); -} - -test "CodeSignature header" { - var code_sig: CodeSignature = .{}; - defer code_sig.deinit(testing.allocator); - - var buffer: [@sizeOf(macho.SuperBlob)]u8 = undefined; - var stream = std.io.fixedBufferStream(&buffer); - try code_sig.writeHeader(stream.writer()); - - const expected = &[_]u8{ 0xfa, 0xde, 0x0c, 0xc0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x0 }; - try testing.expect(mem.eql(u8, expected, &buffer)); -} - -pub fn calcCodeSignaturePaddingSize(id: []const u8, file_size: u64, page_size: u16) u32 { - const ident_size = id.len + 1; - const total_pages = mem.alignForwardGeneric(u64, file_size, page_size) / page_size; - const hashed_size = total_pages * hash_size; - const codesig_header = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + @sizeOf(macho.CodeDirectory); - return @intCast(u32, mem.alignForwardGeneric(u64, codesig_header + ident_size + hashed_size, @sizeOf(u64))); +pub fn clear(self: *CodeSignature, allocator: Allocator) void { + self.code_directory.deinit(allocator); + self.code_directory = CodeDirectory.init(self.page_size); } diff --git a/src/main.zig b/src/main.zig index a071ca9a60..115f3748b6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -433,6 +433,7 @@ const usage_build_generic = \\ -framework [name] (Darwin) link against framework \\ -F[dir] (Darwin) add search path for frameworks \\ -install_name=[value] (Darwin) add dylib's install name + \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature \\ --import-memory (WebAssembly) import memory from the environment \\ --import-table (WebAssembly) import function table from the host environment \\ --export-table (WebAssembly) export function table to the host environment @@ -680,6 +681,7 @@ fn buildOutputType( var native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null; var install_name: ?[]const u8 = null; var hash_style: link.HashStyle = .both; + var entitlements: ?[]const u8 = null; // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names. // This array is populated by zig cc frontend and then has to be converted to zig-style @@ -1036,6 +1038,10 @@ fn buildOutputType( } else { enable_link_snapshots = true; } + } else if (mem.eql(u8, arg, "--entitlements")) { + entitlements = args_iter.next() orelse { + fatal("expected parameter after {s}", .{arg}); + }; } else if (mem.eql(u8, arg, "-fcompiler-rt")) { want_compiler_rt = true; } else if (mem.eql(u8, arg, "-fno-compiler-rt")) { @@ -2729,6 +2735,7 @@ fn buildOutputType( .enable_link_snapshots = enable_link_snapshots, .native_darwin_sdk = native_darwin_sdk, .install_name = install_name, + .entitlements = entitlements, }) catch |err| switch (err) { error.LibCUnavailable => { const target = target_info.target; From 3701697a0a586e630a2452dea29951f0051a47fd Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Thu, 17 Feb 2022 18:00:58 +0100 Subject: [PATCH 0858/2031] ignore target lib dirs when invoked with -feach-lib-rpath --- src/main.zig | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main.zig b/src/main.zig index 115f3748b6..d0baa1acdc 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2197,10 +2197,6 @@ fn buildOutputType( clang_argv.appendAssumeCapacity(framework_dir); framework_dirs.appendAssumeCapacity(framework_dir); } - - for (paths.lib_dirs.items) |lib_dir| { - try lib_dirs.append(lib_dir); - } for (paths.rpaths.items) |rpath| { try rpath_list.append(rpath); } From b74f2924102fe06addb688dc5fd039dc2756f619 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 22 Mar 2022 20:44:14 +0100 Subject: [PATCH 0859/2031] Revert "ignore target lib dirs when invoked with -feach-lib-rpath" This reverts commit 3701697a0a586e630a2452dea29951f0051a47fd. The commit introduced a regression when building stage2 on nixOS where the linker would fail to find relevant LLVM dynamic libraries as some search dirs were missing. --- src/main.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main.zig b/src/main.zig index d0baa1acdc..115f3748b6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2197,6 +2197,10 @@ fn buildOutputType( clang_argv.appendAssumeCapacity(framework_dir); framework_dirs.appendAssumeCapacity(framework_dir); } + + for (paths.lib_dirs.items) |lib_dir| { + try lib_dirs.append(lib_dir); + } for (paths.rpaths.items) |rpath| { try rpath_list.append(rpath); } From 593130ce0a4b06185fcb4806f8330857a1da9f92 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 22 Mar 2022 00:23:54 -0700 Subject: [PATCH 0860/2031] stage2: lazy `@alignOf` Add a `target` parameter to every function that deals with Type and Value. --- src/Compilation.zig | 4 +- src/Module.zig | 52 +-- src/RangeSet.zig | 31 +- src/Sema.zig | 848 +++++++++++++++++++++-------------- src/TypedValue.zig | 57 ++- src/arch/aarch64/CodeGen.zig | 33 +- src/arch/arm/CodeGen.zig | 24 +- src/arch/arm/Emit.zig | 5 +- src/arch/riscv64/CodeGen.zig | 25 +- src/arch/wasm/CodeGen.zig | 35 +- src/arch/x86_64/CodeGen.zig | 20 +- src/arch/x86_64/Emit.zig | 4 +- src/codegen.zig | 35 +- src/codegen/c.zig | 57 ++- src/codegen/llvm.zig | 106 ++--- src/codegen/spirv.zig | 18 +- src/link.zig | 4 +- src/link/C.zig | 8 +- src/link/Dwarf.zig | 24 +- src/link/MachO.zig | 19 +- src/print_air.zig | 12 +- src/type.zig | 746 +++++++++++++++++++----------- src/value.zig | 464 ++++++++++--------- test/behavior.zig | 2 +- 24 files changed, 1575 insertions(+), 1058 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 64848659a7..e8b1ebb145 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2781,7 +2781,9 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress .error_msg = null, .decl = decl, .fwd_decl = fwd_decl.toManaged(gpa), - .typedefs = c_codegen.TypedefMap.init(gpa), + .typedefs = c_codegen.TypedefMap.initContext(gpa, .{ + .target = comp.getTarget(), + }), .typedefs_arena = typedefs_arena.allocator(), }; defer dg.fwd_decl.deinit(); diff --git a/src/Module.zig b/src/Module.zig index d537a4cf5a..4164c2659c 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -146,6 +146,8 @@ const MonomorphedFuncsSet = std.HashMapUnmanaged( ); const MonomorphedFuncsContext = struct { + target: Target, + pub fn eql(ctx: @This(), a: *Fn, b: *Fn) bool { _ = ctx; return a == b; @@ -153,7 +155,6 @@ const MonomorphedFuncsContext = struct { /// Must match `Sema.GenericCallAdapter.hash`. pub fn hash(ctx: @This(), key: *Fn) u64 { - _ = ctx; var hasher = std.hash.Wyhash.init(0); // The generic function Decl is guaranteed to be the first dependency @@ -168,7 +169,7 @@ const MonomorphedFuncsContext = struct { const generic_ty_info = generic_owner_decl.ty.fnInfo(); for (generic_ty_info.param_types) |param_ty, i| { if (generic_ty_info.paramIsComptime(i) and param_ty.tag() != .generic_poison) { - comptime_args[i].val.hash(param_ty, &hasher); + comptime_args[i].val.hash(param_ty, &hasher, ctx.target); } } @@ -184,6 +185,8 @@ pub const MemoizedCallSet = std.HashMapUnmanaged( ); pub const MemoizedCall = struct { + target: std.Target, + pub const Key = struct { func: *Fn, args: []TypedValue, @@ -195,14 +198,12 @@ pub const MemoizedCall = struct { }; pub fn eql(ctx: @This(), a: Key, b: Key) bool { - _ = ctx; - if (a.func != b.func) return false; assert(a.args.len == b.args.len); for (a.args) |a_arg, arg_i| { const b_arg = b.args[arg_i]; - if (!a_arg.eql(b_arg)) { + if (!a_arg.eql(b_arg, ctx.target)) { return false; } } @@ -212,8 +213,6 @@ pub const MemoizedCall = struct { /// Must match `Sema.GenericCallAdapter.hash`. pub fn hash(ctx: @This(), key: Key) u64 { - _ = ctx; - var hasher = std.hash.Wyhash.init(0); // The generic function Decl is guaranteed to be the first dependency @@ -223,7 +222,7 @@ pub const MemoizedCall = struct { // This logic must be kept in sync with the logic in `analyzeCall` that // computes the hash. for (key.args) |arg| { - arg.hash(&hasher); + arg.hash(&hasher, ctx.target); } return hasher.final(); @@ -1230,7 +1229,7 @@ pub const Union = struct { if (field.abi_align == 0) { break :a field.ty.abiAlignment(target); } else { - break :a @intCast(u32, field.abi_align.toUnsignedInt()); + break :a field.abi_align; } }; if (field_align > most_alignment) { @@ -3877,6 +3876,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { const bytes = try sema.resolveConstString(&block_scope, src, linksection_ref); break :blk (try decl_arena_allocator.dupeZ(u8, bytes)).ptr; }; + const target = sema.mod.getTarget(); const address_space = blk: { const addrspace_ctx: Sema.AddressSpaceContext = switch (decl_tv.val.tag()) { .function, .extern_fn => .function, @@ -3886,9 +3886,9 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { break :blk switch (decl.zirAddrspaceRef()) { .none => switch (addrspace_ctx) { - .function => target_util.defaultAddressSpace(sema.mod.getTarget(), .function), - .variable => target_util.defaultAddressSpace(sema.mod.getTarget(), .global_mutable), - .constant => target_util.defaultAddressSpace(sema.mod.getTarget(), .global_constant), + .function => target_util.defaultAddressSpace(target, .function), + .variable => target_util.defaultAddressSpace(target, .global_mutable), + .constant => target_util.defaultAddressSpace(target, .global_constant), else => unreachable, }, else => |addrspace_ref| try sema.analyzeAddrspace(&block_scope, src, addrspace_ref, addrspace_ctx), @@ -3904,13 +3904,15 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { if (decl.is_usingnamespace) { const ty_ty = Type.initTag(.type); - if (!decl_tv.ty.eql(ty_ty)) { - return sema.fail(&block_scope, src, "expected type, found {}", .{decl_tv.ty}); + if (!decl_tv.ty.eql(ty_ty, target)) { + return sema.fail(&block_scope, src, "expected type, found {}", .{ + decl_tv.ty.fmt(target), + }); } var buffer: Value.ToTypeBuffer = undefined; const ty = decl_tv.val.toType(&buffer); if (ty.getNamespace() == null) { - return sema.fail(&block_scope, src, "type {} has no namespace", .{ty}); + return sema.fail(&block_scope, src, "type {} has no namespace", .{ty.fmt(target)}); } decl.ty = ty_ty; @@ -3937,7 +3939,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { if (decl.has_tv) { prev_type_has_bits = decl.ty.isFnOrHasRuntimeBits(); - type_changed = !decl.ty.eql(decl_tv.ty); + type_changed = !decl.ty.eql(decl_tv.ty, target); if (decl.getFunction()) |prev_func| { prev_is_inline = prev_func.state == .inline_only; } @@ -3986,7 +3988,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { } var type_changed = true; if (decl.has_tv) { - type_changed = !decl.ty.eql(decl_tv.ty); + type_changed = !decl.ty.eql(decl_tv.ty, target); decl.clearValues(gpa); } @@ -5054,22 +5056,6 @@ pub fn errNoteNonLazy( }; } -pub fn errorUnionType( - arena: Allocator, - error_set: Type, - payload: Type, -) Allocator.Error!Type { - assert(error_set.zigTypeTag() == .ErrorSet); - if (error_set.eql(Type.initTag(.anyerror)) and payload.eql(Type.initTag(.void))) { - return Type.initTag(.anyerror_void_error_union); - } - - return Type.Tag.error_union.create(arena, .{ - .error_set = error_set, - .payload = payload, - }); -} - pub fn getTarget(mod: Module) Target { return mod.comp.bin_file.options.target; } diff --git a/src/RangeSet.zig b/src/RangeSet.zig index e4d65353a9..79bd22fd7f 100644 --- a/src/RangeSet.zig +++ b/src/RangeSet.zig @@ -6,6 +6,7 @@ const RangeSet = @This(); const SwitchProngSrc = @import("Module.zig").SwitchProngSrc; ranges: std.ArrayList(Range), +target: std.Target, pub const Range = struct { first: Value, @@ -13,9 +14,10 @@ pub const Range = struct { src: SwitchProngSrc, }; -pub fn init(allocator: std.mem.Allocator) RangeSet { +pub fn init(allocator: std.mem.Allocator, target: std.Target) RangeSet { return .{ .ranges = std.ArrayList(Range).init(allocator), + .target = target, }; } @@ -30,8 +32,12 @@ pub fn add( ty: Type, src: SwitchProngSrc, ) !?SwitchProngSrc { + const target = self.target; + for (self.ranges.items) |range| { - if (last.compare(.gte, range.first, ty) and first.compare(.lte, range.last, ty)) { + if (last.compare(.gte, range.first, ty, target) and + first.compare(.lte, range.last, ty, target)) + { return range.src; // They overlap. } } @@ -43,19 +49,26 @@ pub fn add( return null; } +const LessThanContext = struct { ty: Type, target: std.Target }; + /// Assumes a and b do not overlap -fn lessThan(ty: Type, a: Range, b: Range) bool { - return a.first.compare(.lt, b.first, ty); +fn lessThan(ctx: LessThanContext, a: Range, b: Range) bool { + return a.first.compare(.lt, b.first, ctx.ty, ctx.target); } pub fn spans(self: *RangeSet, first: Value, last: Value, ty: Type) !bool { if (self.ranges.items.len == 0) return false; - std.sort.sort(Range, self.ranges.items, ty, lessThan); + const target = self.target; - if (!self.ranges.items[0].first.eql(first, ty) or - !self.ranges.items[self.ranges.items.len - 1].last.eql(last, ty)) + std.sort.sort(Range, self.ranges.items, LessThanContext{ + .ty = ty, + .target = target, + }, lessThan); + + if (!self.ranges.items[0].first.eql(first, ty, target) or + !self.ranges.items[self.ranges.items.len - 1].last.eql(last, ty, target)) { return false; } @@ -71,10 +84,10 @@ pub fn spans(self: *RangeSet, first: Value, last: Value, ty: Type) !bool { const prev = self.ranges.items[i]; // prev.last + 1 == cur.first - try counter.copy(prev.last.toBigInt(&space)); + try counter.copy(prev.last.toBigInt(&space, target)); try counter.addScalar(counter.toConst(), 1); - const cur_start_int = cur.first.toBigInt(&space); + const cur_start_int = cur.first.toBigInt(&space, target); if (!cur_start_int.eq(counter.toConst())) { return false; } diff --git a/src/Sema.zig b/src/Sema.zig index 2a3eff928b..5e344bb7d0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1303,7 +1303,8 @@ pub fn resolveConstString( const wanted_type = Type.initTag(.const_slice_u8); const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); const val = try sema.resolveConstValue(block, src, coerced_inst); - return val.toAllocatedBytes(wanted_type, sema.arena); + const target = sema.mod.getTarget(); + return val.toAllocatedBytes(wanted_type, sema.arena, target); } pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type { @@ -1457,19 +1458,29 @@ fn failWithDivideByZero(sema: *Sema, block: *Block, src: LazySrcLoc) CompileErro } fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: Type, rhs_ty: Type) CompileError { - return sema.fail(block, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ lhs_ty, rhs_ty }); + const target = sema.mod.getTarget(); + return sema.fail(block, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ + lhs_ty.fmt(target), rhs_ty.fmt(target), + }); } fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, optional_ty: Type) CompileError { - return sema.fail(block, src, "expected optional type, found {}", .{optional_ty}); + const target = sema.mod.getTarget(); + return sema.fail(block, src, "expected optional type, found {}", .{optional_ty.fmt(target)}); } fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError { - return sema.fail(block, src, "type '{}' does not support array initialization syntax", .{ty}); + const target = sema.mod.getTarget(); + return sema.fail(block, src, "type '{}' does not support array initialization syntax", .{ + ty.fmt(target), + }); } fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError { - return sema.fail(block, src, "type '{}' does not support struct initialization syntax", .{ty}); + const target = sema.mod.getTarget(); + return sema.fail(block, src, "type '{}' does not support struct initialization syntax", .{ + ty.fmt(target), + }); } fn failWithErrorSetCodeMissing( @@ -1479,8 +1490,9 @@ fn failWithErrorSetCodeMissing( dest_err_set_ty: Type, src_err_set_ty: Type, ) CompileError { + const target = sema.mod.getTarget(); return sema.fail(block, src, "expected type '{}', found type '{}'", .{ - dest_err_set_ty, src_err_set_ty, + dest_err_set_ty.fmt(target), src_err_set_ty.fmt(target), }); } @@ -1578,8 +1590,8 @@ fn resolveInt( const air_inst = sema.resolveInst(zir_ref); const coerced = try sema.coerce(block, dest_ty, air_inst, src); const val = try sema.resolveConstValue(block, src, coerced); - - return val.toUnsignedInt(); + const target = sema.mod.getTarget(); + return val.toUnsignedInt(target); } // Returns a compile error if the value has tag `variable`. See `resolveInstValue` for @@ -1864,6 +1876,7 @@ fn createTypeName( }, .parent => return sema.gpa.dupeZ(u8, mem.sliceTo(block.src_decl.name, 0)), .func => { + const target = sema.mod.getTarget(); const fn_info = sema.code.getFnInfo(sema.func.?.zir_body_inst); const zir_tags = sema.code.instructions.items(.tag); @@ -1881,7 +1894,7 @@ fn createTypeName( const arg_val = sema.resolveConstMaybeUndefVal(block, .unneeded, arg) catch unreachable; if (arg_i != 0) try buf.appendSlice(","); - try buf.writer().print("{}", .{arg_val.fmtValue(sema.typeOf(arg))}); + try buf.writer().print("{}", .{arg_val.fmtValue(sema.typeOf(arg), target)}); arg_i += 1; continue; @@ -2045,6 +2058,7 @@ fn zirEnumDecl( enum_obj.tag_ty_inferred = true; } } + const target = mod.getTarget(); try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { @@ -2053,6 +2067,7 @@ fn zirEnumDecl( if (any_values) { try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{ .ty = enum_obj.tag_ty, + .target = target, }); } @@ -2102,16 +2117,18 @@ fn zirEnumDecl( const copied_tag_val = try tag_val.copy(new_decl_arena_allocator); enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{ .ty = enum_obj.tag_ty, + .target = target, }); } else if (any_values) { const tag_val = if (last_tag_val) |val| - try val.intAdd(Value.one, enum_obj.tag_ty, sema.arena) + try val.intAdd(Value.one, enum_obj.tag_ty, sema.arena, target) else Value.zero; last_tag_val = tag_val; const copied_tag_val = try tag_val.copy(new_decl_arena_allocator); enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{ .ty = enum_obj.tag_ty, + .target = target, }); } } @@ -2417,13 +2434,14 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE else object_ty; + const target = sema.mod.getTarget(); if (!array_ty.isIndexable()) { const msg = msg: { const msg = try sema.errMsg( block, src, "type '{}' does not support indexing", - .{array_ty}, + .{array_ty.fmt(target)}, ); errdefer msg.destroy(sema.gpa); try sema.errNote( @@ -3346,8 +3364,9 @@ fn failWithBadMemberAccess( else => unreachable, }; const msg = msg: { + const target = sema.mod.getTarget(); const msg = try sema.errMsg(block, field_src, "{s} '{}' has no member named '{s}'", .{ - kw_name, agg_ty, field_name, + kw_name, agg_ty.fmt(target), field_name, }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, agg_ty); @@ -3680,6 +3699,7 @@ fn zirCompileLog( const src_node = extra.data.src_node; const src: LazySrcLoc = .{ .node_offset = src_node }; const args = sema.code.refSlice(extra.end, extended.small); + const target = sema.mod.getTarget(); for (args) |arg_ref, i| { if (i != 0) try writer.print(", ", .{}); @@ -3687,9 +3707,11 @@ fn zirCompileLog( const arg = sema.resolveInst(arg_ref); const arg_ty = sema.typeOf(arg); if (try sema.resolveMaybeUndefVal(block, src, arg)) |val| { - try writer.print("@as({}, {})", .{ arg_ty, val.fmtValue(arg_ty) }); + try writer.print("@as({}, {})", .{ + arg_ty.fmt(target), val.fmtValue(arg_ty, target), + }); } else { - try writer.print("@as({}, [runtime value])", .{arg_ty}); + try writer.print("@as({}, [runtime value])", .{arg_ty.fmt(target)}); } } try writer.print("\n", .{}); @@ -3982,9 +4004,10 @@ fn analyzeBlockBody( const type_src = src; // TODO: better source location const valid_rt = try sema.validateRunTimeType(child_block, type_src, resolved_ty, false); + const target = sema.mod.getTarget(); if (!valid_rt) { const msg = msg: { - const msg = try sema.errMsg(child_block, type_src, "value with comptime only type '{}' depends on runtime control flow", .{resolved_ty}); + const msg = try sema.errMsg(child_block, type_src, "value with comptime only type '{}' depends on runtime control flow", .{resolved_ty.fmt(target)}); errdefer msg.destroy(sema.gpa); const runtime_src = child_block.runtime_cond orelse child_block.runtime_loop.?; @@ -4012,7 +4035,7 @@ fn analyzeBlockBody( const br_operand = sema.air_instructions.items(.data)[br].br.operand; const br_operand_src = src; const br_operand_ty = sema.typeOf(br_operand); - if (br_operand_ty.eql(resolved_ty)) { + if (br_operand_ty.eql(resolved_ty, target)) { // No type coercion needed. continue; } @@ -4102,12 +4125,15 @@ pub fn analyzeExport( ) !void { const Export = Module.Export; const mod = sema.mod; + const target = mod.getTarget(); try mod.ensureDeclAnalyzed(exported_decl); // TODO run the same checks as we do for C ABI struct fields switch (exported_decl.ty.zigTypeTag()) { .Fn, .Int, .Enum, .Struct, .Union, .Array, .Float => {}, - else => return sema.fail(block, src, "unable to export type '{}'", .{exported_decl.ty}), + else => return sema.fail(block, src, "unable to export type '{}'", .{ + exported_decl.ty.fmt(target), + }), } const gpa = mod.gpa; @@ -4520,6 +4546,7 @@ const GenericCallAdapter = struct { precomputed_hash: u64, func_ty_info: Type.Payload.Function.Data, comptime_tvs: []const TypedValue, + target: std.Target, pub fn eql(ctx: @This(), adapted_key: void, other_key: *Module.Fn) bool { _ = adapted_key; @@ -4532,7 +4559,7 @@ const GenericCallAdapter = struct { for (other_comptime_args[0..ctx.func_ty_info.param_types.len]) |other_arg, i| { if (other_arg.ty.tag() != .generic_poison) { // anytype parameter - if (!other_arg.ty.eql(ctx.comptime_tvs[i].ty)) { + if (!other_arg.ty.eql(ctx.comptime_tvs[i].ty, ctx.target)) { return false; } } @@ -4543,7 +4570,7 @@ const GenericCallAdapter = struct { // but the callsite does not. return false; } - if (!other_arg.val.eql(ctx.comptime_tvs[i].val, other_arg.ty)) { + if (!other_arg.val.eql(ctx.comptime_tvs[i].val, other_arg.ty, ctx.target)) { return false; } } @@ -4588,6 +4615,7 @@ fn analyzeCall( const mod = sema.mod; const callee_ty = sema.typeOf(func); + const target = sema.mod.getTarget(); const func_ty = func_ty: { switch (callee_ty.zigTypeTag()) { .Fn => break :func_ty callee_ty, @@ -4599,7 +4627,7 @@ fn analyzeCall( }, else => {}, } - return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty}); + return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty.fmt(target)}); }; const func_ty_info = func_ty.fnInfo(); @@ -4873,7 +4901,7 @@ fn analyzeCall( // bug generating invalid LLVM IR. const res2: Air.Inst.Ref = res2: { if (should_memoize and is_comptime_call) { - if (mod.memoized_calls.get(memoized_call_key)) |result| { + if (mod.memoized_calls.getContext(memoized_call_key, .{ .target = target })) |result| { const ty_inst = try sema.addType(fn_ret_ty); try sema.air_values.append(gpa, result.val); sema.air_instructions.set(block_inst, .{ @@ -4945,10 +4973,10 @@ fn analyzeCall( arg.* = try arg.*.copy(arena); } - try mod.memoized_calls.put(gpa, memoized_call_key, .{ + try mod.memoized_calls.putContext(gpa, memoized_call_key, .{ .val = try result_val.copy(arena), .arena = arena_allocator.state, - }); + }, .{ .target = sema.mod.getTarget() }); delete_memoized_call_key = false; } } @@ -5037,6 +5065,7 @@ fn instantiateGenericCall( std.hash.autoHash(&hasher, @ptrToInt(module_fn)); const comptime_tvs = try sema.arena.alloc(TypedValue, func_ty_info.param_types.len); + const target = sema.mod.getTarget(); for (func_ty_info.param_types) |param_ty, i| { const is_comptime = func_ty_info.paramIsComptime(i); @@ -5045,7 +5074,7 @@ fn instantiateGenericCall( const casted_arg = try sema.coerce(block, param_ty, uncasted_args[i], arg_src); if (try sema.resolveMaybeUndefVal(block, arg_src, casted_arg)) |arg_val| { if (param_ty.tag() != .generic_poison) { - arg_val.hash(param_ty, &hasher); + arg_val.hash(param_ty, &hasher, target); } comptime_tvs[i] = .{ // This will be different than `param_ty` in the case of `generic_poison`. @@ -5070,8 +5099,9 @@ fn instantiateGenericCall( .precomputed_hash = precomputed_hash, .func_ty_info = func_ty_info, .comptime_tvs = comptime_tvs, + .target = target, }; - const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter); + const gop = try mod.monomorphed_funcs.getOrPutContextAdapted(gpa, {}, adapter, .{ .target = target }); if (!gop.found_existing) { const new_module_func = try gpa.create(Module.Fn); gop.key_ptr.* = new_module_func; @@ -5255,7 +5285,7 @@ fn instantiateGenericCall( new_decl.analysis = .complete; log.debug("generic function '{s}' instantiated with type {}", .{ - new_decl.name, new_decl.ty, + new_decl.name, new_decl.ty.fmtDebug(), }); // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field @@ -5410,7 +5440,8 @@ fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const bin_inst = sema.code.instructions.items(.data)[inst].bin; const len = try sema.resolveInt(block, .unneeded, bin_inst.lhs, Type.usize); const elem_type = try sema.resolveType(block, .unneeded, bin_inst.rhs); - const array_ty = try Type.array(sema.arena, len, null, elem_type); + const target = sema.mod.getTarget(); + const array_ty = try Type.array(sema.arena, len, null, elem_type, target); return sema.addType(array_ty); } @@ -5429,7 +5460,8 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil const uncasted_sentinel = sema.resolveInst(extra.sentinel); const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src); const sentinel_val = try sema.resolveConstValue(block, sentinel_src, sentinel); - const array_ty = try Type.array(sema.arena, len, sentinel_val, elem_type); + const target = sema.mod.getTarget(); + const array_ty = try Type.array(sema.arena, len, sentinel_val, elem_type, target); return sema.addType(array_ty); } @@ -5456,13 +5488,14 @@ fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; const error_set = try sema.resolveType(block, lhs_src, extra.lhs); const payload = try sema.resolveType(block, rhs_src, extra.rhs); + const target = sema.mod.getTarget(); if (error_set.zigTypeTag() != .ErrorSet) { return sema.fail(block, lhs_src, "expected error set type, found {}", .{ - error_set, + error_set.fmt(target), }); } - const err_union_ty = try Module.errorUnionType(sema.arena, error_set, payload); + const err_union_ty = try Type.errorUnion(sema.arena, error_set, payload, target); return sema.addType(err_union_ty); } @@ -5520,9 +5553,10 @@ fn zirIntToError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const op = sema.resolveInst(inst_data.operand); + const target = sema.mod.getTarget(); if (try sema.resolveDefinedValue(block, operand_src, op)) |value| { - const int = value.toUnsignedInt(); + const int = value.toUnsignedInt(target); if (int > sema.mod.global_error_set.count() or int == 0) return sema.fail(block, operand_src, "integer value {d} represents no error", .{int}); const payload = try sema.arena.create(Value.Payload.Error); @@ -5569,10 +5603,11 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr } const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs); const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs); + const target = sema.mod.getTarget(); if (lhs_ty.zigTypeTag() != .ErrorSet) - return sema.fail(block, lhs_src, "expected error set type, found {}", .{lhs_ty}); + return sema.fail(block, lhs_src, "expected error set type, found {}", .{lhs_ty.fmt(target)}); if (rhs_ty.zigTypeTag() != .ErrorSet) - return sema.fail(block, rhs_src, "expected error set type, found {}", .{rhs_ty}); + return sema.fail(block, rhs_src, "expected error set type, found {}", .{rhs_ty.fmt(target)}); // Anything merged with anyerror is anyerror. if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) { @@ -5618,6 +5653,7 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); + const target = sema.mod.getTarget(); const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag()) { .Enum => operand, @@ -5634,7 +5670,7 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A }, else => { return sema.fail(block, operand_src, "expected enum or tagged union, found {}", .{ - operand_ty, + operand_ty.fmt(target), }); }, }; @@ -5668,7 +5704,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const operand = sema.resolveInst(extra.rhs); if (dest_ty.zigTypeTag() != .Enum) { - return sema.fail(block, dest_ty_src, "expected enum, found {}", .{dest_ty}); + return sema.fail(block, dest_ty_src, "expected enum, found {}", .{dest_ty.fmt(target)}); } if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |int_val| { @@ -5684,7 +5720,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A block, src, "enum '{}' has no tag with value {}", - .{ dest_ty, int_val.fmtValue(sema.typeOf(operand)) }, + .{ dest_ty.fmt(target), int_val.fmtValue(sema.typeOf(operand), target) }, ); errdefer msg.destroy(sema.gpa); try sema.mod.errNoteNonLazy( @@ -5733,13 +5769,13 @@ fn analyzeOptionalPayloadPtr( const optional_ptr_ty = sema.typeOf(optional_ptr); assert(optional_ptr_ty.zigTypeTag() == .Pointer); + const target = sema.mod.getTarget(); const opt_type = optional_ptr_ty.elemType(); if (opt_type.zigTypeTag() != .Optional) { - return sema.fail(block, src, "expected optional type, found {}", .{opt_type}); + return sema.fail(block, src, "expected optional type, found {}", .{opt_type.fmt(target)}); } const child_type = try opt_type.optionalChildAlloc(sema.arena); - const target = sema.mod.getTarget(); const child_pointer = try Type.ptr(sema.arena, target, .{ .pointee_type = child_type, .mutable = !optional_ptr_ty.isConstPtr(), @@ -5858,8 +5894,12 @@ fn zirErrUnionPayload( const operand = sema.resolveInst(inst_data.operand); const operand_src = src; const operand_ty = sema.typeOf(operand); - if (operand_ty.zigTypeTag() != .ErrorUnion) - return sema.fail(block, operand_src, "expected error union type, found '{}'", .{operand_ty}); + if (operand_ty.zigTypeTag() != .ErrorUnion) { + const target = sema.mod.getTarget(); + return sema.fail(block, operand_src, "expected error union type, found '{}'", .{ + operand_ty.fmt(target), + }); + } if (try sema.resolveDefinedValue(block, src, operand)) |val| { if (val.getError()) |name| { @@ -5906,11 +5946,14 @@ fn analyzeErrUnionPayloadPtr( const operand_ty = sema.typeOf(operand); assert(operand_ty.zigTypeTag() == .Pointer); - if (operand_ty.elemType().zigTypeTag() != .ErrorUnion) - return sema.fail(block, src, "expected error union type, found {}", .{operand_ty.elemType()}); + const target = sema.mod.getTarget(); + if (operand_ty.elemType().zigTypeTag() != .ErrorUnion) { + return sema.fail(block, src, "expected error union type, found {}", .{ + operand_ty.elemType().fmt(target), + }); + } const payload_ty = operand_ty.elemType().errorUnionPayload(); - const target = sema.mod.getTarget(); const operand_pointer_ty = try Type.ptr(sema.arena, target, .{ .pointee_type = payload_ty, .mutable = !operand_ty.isConstPtr(), @@ -5970,8 +6013,12 @@ fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro const src = inst_data.src(); const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); - if (operand_ty.zigTypeTag() != .ErrorUnion) - return sema.fail(block, src, "expected error union type, found '{}'", .{operand_ty}); + const target = sema.mod.getTarget(); + if (operand_ty.zigTypeTag() != .ErrorUnion) { + return sema.fail(block, src, "expected error union type, found '{}'", .{ + operand_ty.fmt(target), + }); + } const result_ty = operand_ty.errorUnionSet(); @@ -5995,8 +6042,12 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const operand_ty = sema.typeOf(operand); assert(operand_ty.zigTypeTag() == .Pointer); - if (operand_ty.elemType().zigTypeTag() != .ErrorUnion) - return sema.fail(block, src, "expected error union type, found {}", .{operand_ty.elemType()}); + if (operand_ty.elemType().zigTypeTag() != .ErrorUnion) { + const target = sema.mod.getTarget(); + return sema.fail(block, src, "expected error union type, found {}", .{ + operand_ty.elemType().fmt(target), + }); + } const result_ty = operand_ty.elemType().errorUnionSet(); @@ -6019,8 +6070,12 @@ fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com const src = inst_data.src(); const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); - if (operand_ty.zigTypeTag() != .ErrorUnion) - return sema.fail(block, src, "expected error union type, found '{}'", .{operand_ty}); + const target = sema.mod.getTarget(); + if (operand_ty.zigTypeTag() != .ErrorUnion) { + return sema.fail(block, src, "expected error union type, found '{}'", .{ + operand_ty.fmt(target), + }); + } if (operand_ty.errorUnionPayload().zigTypeTag() != .Void) { return sema.fail(block, src, "expression value is ignored", .{}); } @@ -6205,7 +6260,7 @@ fn funcCommon( const fn_ty: Type = fn_ty: { const alignment: u32 = if (align_val.tag() == .null_value) 0 else a: { - const alignment = @intCast(u32, align_val.toUnsignedInt()); + const alignment = @intCast(u32, align_val.toUnsignedInt(target)); if (alignment == target_util.defaultFunctionAlignment(target)) { break :a 0; } else { @@ -6494,7 +6549,8 @@ fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const ptr = sema.resolveInst(inst_data.operand); const ptr_ty = sema.typeOf(ptr); if (!ptr_ty.isPtrAtRuntime()) { - return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty}); + const target = sema.mod.getTarget(); + return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(target)}); } if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |ptr_val| { return sema.addConstant(Type.usize, ptr_val); @@ -6652,6 +6708,7 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); const operand = sema.resolveInst(extra.rhs); + const target = sema.mod.getTarget(); const dest_is_comptime_float = switch (dest_ty.zigTypeTag()) { .ComptimeFloat => true, .Float => false, @@ -6659,7 +6716,7 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A block, dest_ty_src, "expected float type, found '{}'", - .{dest_ty}, + .{dest_ty.fmt(target)}, ), }; @@ -6670,7 +6727,7 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A block, operand_src, "expected float type, found '{}'", - .{operand_ty}, + .{operand_ty.fmt(target)}, ), } @@ -6680,7 +6737,6 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A if (dest_is_comptime_float) { return sema.fail(block, src, "unable to cast runtime value to 'comptime_float'", .{}); } - const target = sema.mod.getTarget(); const src_bits = operand_ty.floatBits(target); const dst_bits = dest_ty.floatBits(target); if (dst_bits >= src_bits) { @@ -6839,13 +6895,14 @@ fn zirSwitchCapture( const item = sema.resolveInst(scalar_prong.item); // Previous switch validation ensured this will succeed const item_val = sema.resolveConstValue(block, .unneeded, item) catch unreachable; + const target = sema.mod.getTarget(); switch (operand_ty.zigTypeTag()) { .Union => { const union_obj = operand_ty.cast(Type.Payload.Union).?.data; const enum_ty = union_obj.tag_ty; - const field_index_usize = enum_ty.enumTagFieldIndex(item_val).?; + const field_index_usize = enum_ty.enumTagFieldIndex(item_val, target).?; const field_index = @intCast(u32, field_index_usize); const field = union_obj.fields.values()[field_index]; @@ -6854,7 +6911,6 @@ fn zirSwitchCapture( if (is_ref) { assert(operand_is_ref); - const target = sema.mod.getTarget(); const field_ty_ptr = try Type.ptr(sema.arena, target, .{ .pointee_type = field.ty, .@"addrspace" = .generic, @@ -6894,7 +6950,7 @@ fn zirSwitchCapture( }, else => { return sema.fail(block, operand_src, "switch on type '{}' provides no capture value", .{ - operand_ty, + operand_ty.fmt(target), }); }, } @@ -6915,6 +6971,7 @@ fn zirSwitchCond( else operand_ptr; const operand_ty = sema.typeOf(operand); + const target = sema.mod.getTarget(); switch (operand_ty.zigTypeTag()) { .Type, @@ -6962,7 +7019,7 @@ fn zirSwitchCond( .Vector, .Frame, .AnyFrame, - => return sema.fail(block, src, "switch on type '{}'", .{operand_ty}), + => return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(target)}), } } @@ -7030,6 +7087,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError return sema.failWithOwnedErrorMsg(block, msg); } + const target = sema.mod.getTarget(); + // Validate for duplicate items, missing else prong, and invalid range. switch (operand_ty.zigTypeTag()) { .Enum => { @@ -7115,7 +7174,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError operand_ty.declSrcLoc(), msg, "enum '{}' declared here", - .{operand_ty}, + .{operand_ty.fmt(target)}, ); break :msg msg; }; @@ -7232,7 +7291,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError operand_ty.declSrcLoc(), msg, "error set '{}' declared here", - .{operand_ty}, + .{operand_ty.fmt(target)}, ); return sema.failWithOwnedErrorMsg(block, msg); } @@ -7260,7 +7319,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError }, .Union => return sema.fail(block, src, "TODO validate switch .Union", .{}), .Int, .ComptimeInt => { - var range_set = RangeSet.init(gpa); + var range_set = RangeSet.init(gpa, target); defer range_set.deinit(); var extra_index: usize = special.end; @@ -7333,7 +7392,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError var arena = std.heap.ArenaAllocator.init(gpa); defer arena.deinit(); - const target = sema.mod.getTarget(); const min_int = try operand_ty.minInt(arena.allocator(), target); const max_int = try operand_ty.maxInt(arena.allocator(), target); if (try range_set.spans(min_int, max_int, operand_ty)) { @@ -7437,11 +7495,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError block, src, "else prong required when switching on type '{}'", - .{operand_ty}, + .{operand_ty.fmt(target)}, ); } - var seen_values = ValueSrcMap.initContext(gpa, .{ .ty = operand_ty }); + var seen_values = ValueSrcMap.initContext(gpa, .{ + .ty = operand_ty, + .target = target, + }); defer seen_values.deinit(); var extra_index: usize = special.end; @@ -7505,7 +7566,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError .ComptimeFloat, .Float, => return sema.fail(block, operand_src, "invalid switch operand type '{}'", .{ - operand_ty, + operand_ty.fmt(target), }), } @@ -7555,7 +7616,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const item = sema.resolveInst(item_ref); // Validation above ensured these will succeed. const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable; - if (operand_val.eql(item_val, operand_ty)) { + if (operand_val.eql(item_val, operand_ty, target)) { return sema.resolveBlockBody(block, src, &child_block, body, inst, merges); } } @@ -7577,7 +7638,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const item = sema.resolveInst(item_ref); // Validation above ensured these will succeed. const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable; - if (operand_val.eql(item_val, operand_ty)) { + if (operand_val.eql(item_val, operand_ty, target)) { return sema.resolveBlockBody(block, src, &child_block, body, inst, merges); } } @@ -7592,8 +7653,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError // Validation above ensured these will succeed. const first_tv = sema.resolveInstConst(&child_block, .unneeded, item_first) catch unreachable; const last_tv = sema.resolveInstConst(&child_block, .unneeded, item_last) catch unreachable; - if (Value.compare(operand_val, .gte, first_tv.val, operand_ty) and - Value.compare(operand_val, .lte, last_tv.val, operand_ty)) + if (Value.compare(operand_val, .gte, first_tv.val, operand_ty, target) and + Value.compare(operand_val, .lte, last_tv.val, operand_ty, target)) { return sema.resolveBlockBody(block, src, &child_block, body, inst, merges); } @@ -7907,14 +7968,15 @@ fn validateSwitchItemEnum( switch_prong_src: Module.SwitchProngSrc, ) CompileError!void { const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none); - const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val) orelse { + const target = sema.mod.getTarget(); + const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val, target) orelse { const msg = msg: { const src = switch_prong_src.resolve(sema.gpa, block.src_decl, src_node_offset, .none); const msg = try sema.errMsg( block, src, "enum '{}' has no tag with value '{}'", - .{ item_tv.ty, item_tv.val.fmtValue(item_tv.ty) }, + .{ item_tv.ty.fmt(target), item_tv.val.fmtValue(item_tv.ty, target) }, ); errdefer msg.destroy(sema.gpa); try sema.mod.errNoteNonLazy( @@ -8030,12 +8092,13 @@ fn validateSwitchNoRange( const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset }; const range_src: LazySrcLoc = .{ .node_offset_switch_range = src_node_offset }; + const target = sema.mod.getTarget(); const msg = msg: { const msg = try sema.errMsg( block, operand_src, "ranges not allowed when switching on type '{}'", - .{operand_ty}, + .{operand_ty.fmt(target)}, ); errdefer msg.destroy(sema.gpa); try sema.errNote( @@ -8058,6 +8121,7 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const unresolved_ty = try sema.resolveType(block, ty_src, extra.lhs); const field_name = try sema.resolveConstString(block, name_src, extra.rhs); const ty = try sema.resolveTypeFields(block, ty_src, unresolved_ty); + const target = sema.mod.getTarget(); const has_field = hf: { if (ty.isSlice()) { @@ -8080,7 +8144,7 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .Enum => ty.enumFields().contains(field_name), .Array => mem.eql(u8, field_name, "len"), else => return sema.fail(block, ty_src, "type '{}' does not support '@hasField'", .{ - ty, + ty.fmt(target), }), }; }; @@ -8227,25 +8291,25 @@ fn zirShl( const val = switch (air_tag) { .shl_exact => val: { - const shifted = try lhs_val.shl(rhs_val, lhs_ty, sema.arena); + const shifted = try lhs_val.shl(rhs_val, lhs_ty, sema.arena, target); if (scalar_ty.zigTypeTag() == .ComptimeInt) { break :val shifted; } const int_info = scalar_ty.intInfo(target); - const truncated = try shifted.intTrunc(lhs_ty, sema.arena, int_info.signedness, int_info.bits); - if (truncated.compare(.eq, shifted, lhs_ty)) { + const truncated = try shifted.intTrunc(lhs_ty, sema.arena, int_info.signedness, int_info.bits, target); + if (truncated.compare(.eq, shifted, lhs_ty, target)) { break :val shifted; } return sema.addConstUndef(lhs_ty); }, .shl_sat => if (scalar_ty.zigTypeTag() == .ComptimeInt) - try lhs_val.shl(rhs_val, lhs_ty, sema.arena) + try lhs_val.shl(rhs_val, lhs_ty, sema.arena, target) else try lhs_val.shlSat(rhs_val, lhs_ty, sema.arena, target), .shl => if (scalar_ty.zigTypeTag() == .ComptimeInt) - try lhs_val.shl(rhs_val, lhs_ty, sema.arena) + try lhs_val.shl(rhs_val, lhs_ty, sema.arena, target) else try lhs_val.shlTrunc(rhs_val, lhs_ty, sema.arena, target), @@ -8296,6 +8360,7 @@ fn zirShr( const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); + const target = sema.mod.getTarget(); const runtime_src = if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| rs: { if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { @@ -8308,12 +8373,12 @@ fn zirShr( } if (air_tag == .shr_exact) { // Detect if any ones would be shifted out. - const truncated = try lhs_val.intTruncBitsAsValue(lhs_ty, sema.arena, .unsigned, rhs_val); + const truncated = try lhs_val.intTruncBitsAsValue(lhs_ty, sema.arena, .unsigned, rhs_val, target); if (!truncated.compareWithZero(.eq)) { return sema.addConstUndef(lhs_ty); } } - const val = try lhs_val.shr(rhs_val, lhs_ty, sema.arena); + const val = try lhs_val.shr(rhs_val, lhs_ty, sema.arena, target); return sema.addConstant(lhs_ty, val); } else { // Even if lhs is not comptime known, we can still deduce certain things based @@ -8359,6 +8424,7 @@ fn zirBitwise( const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; + const target = sema.mod.getTarget(); if (!is_int) { return sema.fail(block, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag()), @tagName(rhs_ty.zigTypeTag()) }); @@ -8367,9 +8433,9 @@ fn zirBitwise( if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { const result_val = switch (air_tag) { - .bit_and => try lhs_val.bitwiseAnd(rhs_val, resolved_type, sema.arena), - .bit_or => try lhs_val.bitwiseOr(rhs_val, resolved_type, sema.arena), - .xor => try lhs_val.bitwiseXor(rhs_val, resolved_type, sema.arena), + .bit_and => try lhs_val.bitwiseAnd(rhs_val, resolved_type, sema.arena, target), + .bit_or => try lhs_val.bitwiseOr(rhs_val, resolved_type, sema.arena, target), + .xor => try lhs_val.bitwiseXor(rhs_val, resolved_type, sema.arena, target), else => unreachable, }; return sema.addConstant(resolved_type, result_val); @@ -8391,13 +8457,15 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const operand = sema.resolveInst(inst_data.operand); const operand_type = sema.typeOf(operand); const scalar_type = operand_type.scalarType(); + const target = sema.mod.getTarget(); if (scalar_type.zigTypeTag() != .Int) { - return sema.fail(block, src, "unable to perform binary not operation on type '{}'", .{operand_type}); + return sema.fail(block, src, "unable to perform binary not operation on type '{}'", .{ + operand_type.fmt(target), + }); } if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { - const target = sema.mod.getTarget(); if (val.isUndef()) { return sema.addConstUndef(operand_type); } else if (operand_type.zigTypeTag() == .Vector) { @@ -8513,19 +8581,22 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; + const target = sema.mod.getTarget(); const lhs_info = (try sema.getArrayCatInfo(block, lhs_src, lhs)) orelse - return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty}); + return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty.fmt(target)}); const rhs_info = (try sema.getArrayCatInfo(block, rhs_src, rhs)) orelse - return sema.fail(block, rhs_src, "expected array, found '{}'", .{rhs_ty}); - if (!lhs_info.elem_type.eql(rhs_info.elem_type)) { - return sema.fail(block, rhs_src, "expected array of type '{}', found '{}'", .{ lhs_info.elem_type, rhs_ty }); + return sema.fail(block, rhs_src, "expected array, found '{}'", .{rhs_ty.fmt(target)}); + if (!lhs_info.elem_type.eql(rhs_info.elem_type, target)) { + return sema.fail(block, rhs_src, "expected array of type '{}', found '{}'", .{ + lhs_info.elem_type.fmt(target), rhs_ty.fmt(target), + }); } // When there is a sentinel mismatch, no sentinel on the result. The type system // will catch this if it is a problem. var res_sent: ?Value = null; if (rhs_info.sentinel != null and lhs_info.sentinel != null) { - if (rhs_info.sentinel.?.eql(lhs_info.sentinel.?, lhs_info.elem_type)) { + if (rhs_info.sentinel.?.eql(lhs_info.sentinel.?, lhs_info.elem_type, target)) { res_sent = lhs_info.sentinel.?; } } @@ -8586,6 +8657,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, inst: Air.Inst.Ref) !?Type.ArrayInfo { const t = sema.typeOf(inst); + const target = sema.mod.getTarget(); return switch (t.zigTypeTag()) { .Array => t.arrayInfo(), .Pointer => blk: { @@ -8595,7 +8667,7 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, inst: Air.Inst.R return Type.ArrayInfo{ .elem_type = t.childType(), .sentinel = t.sentinel(), - .len = val.sliceLen(), + .len = val.sliceLen(target), }; } if (ptrinfo.pointee_type.zigTypeTag() != .Array) return null; @@ -8691,9 +8763,10 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (lhs_ty.isTuple()) { return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor); } + const target = sema.mod.getTarget(); const mulinfo = (try sema.getArrayCatInfo(block, lhs_src, lhs)) orelse - return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty}); + return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty.fmt(target)}); const final_len_u64 = std.math.mul(u64, mulinfo.len, factor) catch return sema.fail(block, rhs_src, "operation results in overflow", .{}); @@ -8771,8 +8844,9 @@ fn zirNegate( const rhs_ty = sema.typeOf(rhs); const rhs_scalar_ty = rhs_ty.scalarType(); + const target = sema.mod.getTarget(); if (tag_override == .sub and rhs_scalar_ty.isUnsignedInt()) { - return sema.fail(block, src, "negation of type '{}'", .{rhs_ty}); + return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(target)}); } const lhs = if (rhs_ty.zigTypeTag() == .Vector) @@ -8824,15 +8898,14 @@ fn zirOverflowArithmetic( const ptr = sema.resolveInst(extra.ptr); const lhs_ty = sema.typeOf(lhs); + const target = sema.mod.getTarget(); // Note, the types of lhs/rhs (also for shifting)/ptr are already correct as ensured by astgen. const dest_ty = lhs_ty; if (dest_ty.zigTypeTag() != .Int) { - return sema.fail(block, src, "expected integer type, found '{}'", .{dest_ty}); + return sema.fail(block, src, "expected integer type, found '{}'", .{dest_ty.fmt(target)}); } - const target = sema.mod.getTarget(); - const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs); const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs); @@ -8894,7 +8967,7 @@ fn zirOverflowArithmetic( if (!lhs_val.isUndef()) { if (lhs_val.compareWithZero(.eq)) { break :result .{ .overflowed = .no, .wrapped = lhs }; - } else if (lhs_val.compare(.eq, Value.one, dest_ty)) { + } else if (lhs_val.compare(.eq, Value.one, dest_ty, target)) { break :result .{ .overflowed = .no, .wrapped = rhs }; } } @@ -8904,7 +8977,7 @@ fn zirOverflowArithmetic( if (!rhs_val.isUndef()) { if (rhs_val.compareWithZero(.eq)) { break :result .{ .overflowed = .no, .wrapped = rhs }; - } else if (rhs_val.compare(.eq, Value.one, dest_ty)) { + } else if (rhs_val.compare(.eq, Value.one, dest_ty, target)) { break :result .{ .overflowed = .no, .wrapped = lhs }; } } @@ -9079,7 +9152,7 @@ fn analyzeArithmetic( if (is_int) { return sema.addConstant( resolved_type, - try lhs_val.intAdd(rhs_val, resolved_type, sema.arena), + try lhs_val.intAdd(rhs_val, resolved_type, sema.arena, target), ); } else { return sema.addConstant( @@ -9132,7 +9205,7 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { const val = if (scalar_tag == .ComptimeInt) - try lhs_val.intAdd(rhs_val, resolved_type, sema.arena) + try lhs_val.intAdd(rhs_val, resolved_type, sema.arena, target) else try lhs_val.intAddSat(rhs_val, resolved_type, sema.arena, target); @@ -9172,7 +9245,7 @@ fn analyzeArithmetic( if (is_int) { return sema.addConstant( resolved_type, - try lhs_val.intSub(rhs_val, resolved_type, sema.arena), + try lhs_val.intSub(rhs_val, resolved_type, sema.arena, target), ); } else { return sema.addConstant( @@ -9225,7 +9298,7 @@ fn analyzeArithmetic( } if (maybe_rhs_val) |rhs_val| { const val = if (scalar_tag == .ComptimeInt) - try lhs_val.intSub(rhs_val, resolved_type, sema.arena) + try lhs_val.intSub(rhs_val, resolved_type, sema.arena, target) else try lhs_val.intSubSat(rhs_val, resolved_type, sema.arena, target); @@ -9275,7 +9348,7 @@ fn analyzeArithmetic( if (lhs_val.isUndef()) { if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compare(.neq, Value.negative_one, rhs_ty)) { + if (rhs_val.compare(.neq, Value.negative_one, rhs_ty, target)) { return sema.addConstUndef(resolved_type); } } @@ -9288,7 +9361,7 @@ fn analyzeArithmetic( if (is_int) { return sema.addConstant( resolved_type, - try lhs_val.intDiv(rhs_val, resolved_type, sema.arena), + try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target), ); } else { return sema.addConstant( @@ -9350,7 +9423,7 @@ fn analyzeArithmetic( if (lhs_val.isUndef()) { if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compare(.neq, Value.negative_one, rhs_ty)) { + if (rhs_val.compare(.neq, Value.negative_one, rhs_ty, target)) { return sema.addConstUndef(resolved_type); } } @@ -9363,7 +9436,7 @@ fn analyzeArithmetic( if (is_int) { return sema.addConstant( resolved_type, - try lhs_val.intDiv(rhs_val, resolved_type, sema.arena), + try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target), ); } else { return sema.addConstant( @@ -9413,7 +9486,7 @@ fn analyzeArithmetic( if (lhs_val.isUndef()) { if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compare(.neq, Value.negative_one, rhs_ty)) { + if (rhs_val.compare(.neq, Value.negative_one, rhs_ty, target)) { return sema.addConstUndef(resolved_type); } } @@ -9426,7 +9499,7 @@ fn analyzeArithmetic( if (is_int) { return sema.addConstant( resolved_type, - try lhs_val.intDivFloor(rhs_val, resolved_type, sema.arena), + try lhs_val.intDivFloor(rhs_val, resolved_type, sema.arena, target), ); } else { return sema.addConstant( @@ -9477,7 +9550,7 @@ fn analyzeArithmetic( // TODO: emit compile error if there is a remainder return sema.addConstant( resolved_type, - try lhs_val.intDiv(rhs_val, resolved_type, sema.arena), + try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target), ); } else { // TODO: emit compile error if there is a remainder @@ -9503,7 +9576,7 @@ fn analyzeArithmetic( if (lhs_val.compareWithZero(.eq)) { return sema.addConstant(resolved_type, Value.zero); } - if (lhs_val.compare(.eq, Value.one, lhs_ty)) { + if (lhs_val.compare(.eq, Value.one, lhs_ty, target)) { return casted_rhs; } } @@ -9519,7 +9592,7 @@ fn analyzeArithmetic( if (rhs_val.compareWithZero(.eq)) { return sema.addConstant(resolved_type, Value.zero); } - if (rhs_val.compare(.eq, Value.one, rhs_ty)) { + if (rhs_val.compare(.eq, Value.one, rhs_ty, target)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { @@ -9533,7 +9606,7 @@ fn analyzeArithmetic( if (is_int) { return sema.addConstant( resolved_type, - try lhs_val.intMul(rhs_val, resolved_type, sema.arena), + try lhs_val.intMul(rhs_val, resolved_type, sema.arena, target), ); } else { return sema.addConstant( @@ -9554,7 +9627,7 @@ fn analyzeArithmetic( if (lhs_val.compareWithZero(.eq)) { return sema.addConstant(resolved_type, Value.zero); } - if (lhs_val.compare(.eq, Value.one, lhs_ty)) { + if (lhs_val.compare(.eq, Value.one, lhs_ty, target)) { return casted_rhs; } } @@ -9566,7 +9639,7 @@ fn analyzeArithmetic( if (rhs_val.compareWithZero(.eq)) { return sema.addConstant(resolved_type, Value.zero); } - if (rhs_val.compare(.eq, Value.one, rhs_ty)) { + if (rhs_val.compare(.eq, Value.one, rhs_ty, target)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { @@ -9590,7 +9663,7 @@ fn analyzeArithmetic( if (lhs_val.compareWithZero(.eq)) { return sema.addConstant(resolved_type, Value.zero); } - if (lhs_val.compare(.eq, Value.one, lhs_ty)) { + if (lhs_val.compare(.eq, Value.one, lhs_ty, target)) { return casted_rhs; } } @@ -9602,7 +9675,7 @@ fn analyzeArithmetic( if (rhs_val.compareWithZero(.eq)) { return sema.addConstant(resolved_type, Value.zero); } - if (rhs_val.compare(.eq, Value.one, rhs_ty)) { + if (rhs_val.compare(.eq, Value.one, rhs_ty, target)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { @@ -9611,7 +9684,7 @@ fn analyzeArithmetic( } const val = if (scalar_tag == .ComptimeInt) - try lhs_val.intMul(rhs_val, resolved_type, sema.arena) + try lhs_val.intMul(rhs_val, resolved_type, sema.arena, target) else try lhs_val.intMulSat(rhs_val, resolved_type, sema.arena, target); @@ -9652,7 +9725,7 @@ fn analyzeArithmetic( return sema.failWithDivideByZero(block, rhs_src); } if (maybe_lhs_val) |lhs_val| { - const rem_result = try lhs_val.intRem(rhs_val, resolved_type, sema.arena); + const rem_result = try lhs_val.intRem(rhs_val, resolved_type, sema.arena, target); // If this answer could possibly be different by doing `intMod`, // we must emit a compile error. Otherwise, it's OK. if (rhs_val.compareWithZero(.lt) != lhs_val.compareWithZero(.lt) and @@ -9731,7 +9804,7 @@ fn analyzeArithmetic( if (maybe_lhs_val) |lhs_val| { return sema.addConstant( resolved_type, - try lhs_val.intRem(rhs_val, resolved_type, sema.arena), + try lhs_val.intRem(rhs_val, resolved_type, sema.arena, target), ); } break :rs .{ .src = lhs_src, .air_tag = .rem }; @@ -9788,7 +9861,7 @@ fn analyzeArithmetic( if (maybe_lhs_val) |lhs_val| { return sema.addConstant( resolved_type, - try lhs_val.intMod(rhs_val, resolved_type, sema.arena), + try lhs_val.intMod(rhs_val, resolved_type, sema.arena, target), ); } break :rs .{ .src = lhs_src, .air_tag = .mod }; @@ -9839,6 +9912,7 @@ fn analyzePtrArithmetic( // coerce to isize instead of usize. const offset = try sema.coerce(block, Type.usize, uncasted_offset, offset_src); // TODO adjust the return type according to alignment and other factors + const target = sema.mod.getTarget(); const runtime_src = rs: { if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |ptr_val| { if (try sema.resolveMaybeUndefVal(block, offset_src, offset)) |offset_val| { @@ -9849,11 +9923,10 @@ fn analyzePtrArithmetic( return sema.addConstUndef(new_ptr_ty); } - const offset_int = try sema.usizeCast(block, offset_src, offset_val.toUnsignedInt()); + const offset_int = try sema.usizeCast(block, offset_src, offset_val.toUnsignedInt(target)); // TODO I tried to put this check earlier but it the LLVM backend generate invalid instructinons if (offset_int == 0) return ptr; - if (ptr_val.getUnsignedInt()) |addr| { - const target = sema.mod.getTarget(); + if (ptr_val.getUnsignedInt(target)) |addr| { const ptr_child_ty = ptr_ty.childType(); const elem_ty = if (ptr_ty.isSinglePointer() and ptr_child_ty.zigTypeTag() == .Array) ptr_child_ty.childType() @@ -9872,7 +9945,7 @@ fn analyzePtrArithmetic( if (air_tag == .ptr_sub) { return sema.fail(block, op_src, "TODO implement Sema comptime pointer subtraction", .{}); } - const new_ptr_val = try ptr_val.elemPtr(ptr_ty, sema.arena, offset_int); + const new_ptr_val = try ptr_val.elemPtr(ptr_ty, sema.arena, offset_int, target); return sema.addConstant(new_ptr_ty, new_ptr_val); } else break :rs offset_src; } else break :rs ptr_src; @@ -10035,6 +10108,7 @@ fn zirCmpEq( const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; const lhs = sema.resolveInst(extra.lhs); const rhs = sema.resolveInst(extra.rhs); + const target = sema.mod.getTarget(); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); @@ -10059,7 +10133,7 @@ fn zirCmpEq( if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) { const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty; - return sema.fail(block, src, "comparison of '{}' with null", .{non_null_type}); + return sema.fail(block, src, "comparison of '{}' with null", .{non_null_type.fmt(target)}); } if (lhs_ty_tag == .Union and (rhs_ty_tag == .EnumLiteral or rhs_ty_tag == .Enum)) { @@ -10099,7 +10173,7 @@ fn zirCmpEq( if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) { const lhs_as_type = try sema.analyzeAsType(block, lhs_src, lhs); const rhs_as_type = try sema.analyzeAsType(block, rhs_src, rhs); - if (lhs_as_type.eql(rhs_as_type) == (op == .eq)) { + if (lhs_as_type.eql(rhs_as_type, target) == (op == .eq)) { return Air.Inst.Ref.bool_true; } else { return Air.Inst.Ref.bool_false; @@ -10176,9 +10250,10 @@ fn analyzeCmp( } const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]LazySrcLoc{ lhs_src, rhs_src } }); + const target = sema.mod.getTarget(); if (!resolved_type.isSelfComparable(is_equality_cmp)) { return sema.fail(block, src, "{s} operator not allowed for type '{}'", .{ - @tagName(op), resolved_type, + @tagName(op), resolved_type.fmt(target), }); } const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); @@ -10196,6 +10271,7 @@ fn cmpSelf( rhs_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { const resolved_type = sema.typeOf(casted_lhs); + const target = sema.mod.getTarget(); const runtime_src: LazySrcLoc = src: { if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { if (lhs_val.isUndef()) return sema.addConstUndef(Type.bool); @@ -10204,11 +10280,11 @@ fn cmpSelf( if (resolved_type.zigTypeTag() == .Vector) { const result_ty = try Type.vector(sema.arena, resolved_type.vectorLen(), Type.@"bool"); - const cmp_val = try lhs_val.compareVector(op, rhs_val, resolved_type, sema.arena); + const cmp_val = try lhs_val.compareVector(op, rhs_val, resolved_type, sema.arena, target); return sema.addConstant(result_ty, cmp_val); } - if (lhs_val.compare(op, rhs_val, resolved_type)) { + if (lhs_val.compare(op, rhs_val, resolved_type, target)) { return Air.Inst.Ref.bool_true; } else { return Air.Inst.Ref.bool_false; @@ -10276,7 +10352,7 @@ fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. .Null, .BoundFn, .Opaque, - => return sema.fail(block, src, "no size available for type '{}'", .{operand_ty}), + => return sema.fail(block, src, "no size available for type '{}'", .{operand_ty.fmt(target)}), .Type, .EnumLiteral, @@ -11365,11 +11441,12 @@ fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) Compi }, else => {}, } + const target = sema.mod.getTarget(); return sema.fail( block, src, "bit shifting operation expected integer type, found '{}'", - .{operand}, + .{operand.fmt(target)}, ); } @@ -12414,6 +12491,7 @@ fn fieldType( ty_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { const resolved_ty = try sema.resolveTypeFields(block, ty_src, aggregate_ty); + const target = sema.mod.getTarget(); switch (resolved_ty.zigTypeTag()) { .Struct => { const struct_obj = resolved_ty.castTag(.@"struct").?.data; @@ -12428,7 +12506,7 @@ fn fieldType( return sema.addType(field.ty); }, else => return sema.fail(block, ty_src, "expected struct or union; found '{}'", .{ - resolved_ty, + resolved_ty.fmt(target), }), } } @@ -12459,11 +12537,11 @@ fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const ty = try sema.resolveType(block, operand_src, inst_data.operand); - const resolved_ty = try sema.resolveTypeFields(block, operand_src, ty); - try sema.resolveTypeLayout(block, operand_src, resolved_ty); const target = sema.mod.getTarget(); - const abi_align = resolved_ty.abiAlignment(target); - return sema.addIntUnsigned(Type.comptime_int, abi_align); + return sema.addConstant( + Type.comptime_int, + try ty.lazyAbiAlignment(target, sema.arena), + ); } fn zirBoolToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -12509,6 +12587,7 @@ fn zirUnaryMath( const operand = sema.resolveInst(inst_data.operand); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_ty = sema.typeOf(operand); + const target = sema.mod.getTarget(); switch (operand_ty.zigTypeTag()) { .ComptimeFloat, .Float => {}, @@ -12516,13 +12595,12 @@ fn zirUnaryMath( const scalar_ty = operand_ty.scalarType(); switch (scalar_ty.zigTypeTag()) { .ComptimeFloat, .Float => {}, - else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{scalar_ty}), + else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{scalar_ty.fmt(target)}), } }, - else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{operand_ty}), + else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{operand_ty.fmt(target)}), } - const target = sema.mod.getTarget(); switch (operand_ty.zigTypeTag()) { .Vector => { const scalar_ty = operand_ty.scalarType(); @@ -12568,6 +12646,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const src = inst_data.src(); const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); + const target = sema.mod.getTarget(); try sema.resolveTypeLayout(block, operand_src, operand_ty); const enum_ty = switch (operand_ty.zigTypeTag()) { @@ -12590,13 +12669,13 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air return sema.failWithOwnedErrorMsg(block, msg); }, else => return sema.fail(block, operand_src, "expected enum or union; found {}", .{ - operand_ty, + operand_ty.fmt(target), }), }; const enum_decl = enum_ty.getOwnerDecl(); const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src); if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| { - const field_index = enum_ty.enumTagFieldIndex(val) orelse { + const field_index = enum_ty.enumTagFieldIndex(val, target) orelse { const msg = msg: { const msg = try sema.errMsg(block, src, "no field with value {} in enum '{s}'", .{ casted_operand, enum_decl.name, @@ -12626,8 +12705,8 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const val = try sema.resolveConstValue(block, operand_src, type_info); const union_val = val.cast(Value.Payload.Union).?.data; const tag_ty = type_info_ty.unionTagType().?; - const tag_index = tag_ty.enumTagFieldIndex(union_val.tag).?; const target = sema.mod.getTarget(); + const tag_index = tag_ty.enumTagFieldIndex(union_val.tag, target).?; switch (@intToEnum(std.builtin.TypeId, tag_index)) { .Type => return Air.Inst.Ref.type_type, .Void => return Air.Inst.Ref.void_type, @@ -12646,7 +12725,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const bits_val = struct_val[1]; const signedness = signedness_val.toEnum(std.builtin.Signedness); - const bits = @intCast(u16, bits_val.toUnsignedInt()); + const bits = @intCast(u16, bits_val.toUnsignedInt(target)); const ty = switch (signedness) { .signed => try Type.Tag.int_signed.create(sema.arena, bits), .unsigned => try Type.Tag.int_unsigned.create(sema.arena, bits), @@ -12659,7 +12738,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const len_val = struct_val[0]; const child_val = struct_val[1]; - const len = len_val.toUnsignedInt(); + const len = len_val.toUnsignedInt(target); var buffer: Value.ToTypeBuffer = undefined; const child_ty = child_val.toType(&buffer); @@ -12672,7 +12751,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I // bits: comptime_int, const bits_val = struct_val[0]; - const bits = @intCast(u16, bits_val.toUnsignedInt()); + const bits = @intCast(u16, bits_val.toUnsignedInt(target)); const ty = switch (bits) { 16 => Type.@"f16", 32 => Type.@"f32", @@ -12717,7 +12796,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I .size = ptr_size, .mutable = !is_const_val.toBool(), .@"volatile" = is_volatile_val.toBool(), - .@"align" = @intCast(u16, alignment_val.toUnsignedInt()), // TODO: Validate this value. + .@"align" = @intCast(u16, alignment_val.toUnsignedInt(target)), // TODO: Validate this value. .@"addrspace" = address_space_val.toEnum(std.builtin.AddressSpace), .pointee_type = try child_ty.copy(sema.arena), .@"allowzero" = is_allowzero_val.toBool(), @@ -12735,7 +12814,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I // sentinel: ?*const anyopaque, const sentinel_val = struct_val[2]; - const len = len_val.toUnsignedInt(); + const len = len_val.toUnsignedInt(target); var buffer: Value.ToTypeBuffer = undefined; const child_ty = try child_val.toType(&buffer).copy(sema.arena); const sentinel = if (sentinel_val.castTag(.opt_payload)) |p| blk: { @@ -12746,7 +12825,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I break :blk (try sema.pointerDeref(block, src, p.data, ptr_ty)).?; } else null; - const ty = try Type.array(sema.arena, len, sentinel, child_ty); + const ty = try Type.array(sema.arena, len, sentinel, child_ty, target); return sema.addType(ty); }, .Optional => { @@ -12796,7 +12875,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const name_val = struct_val[0]; names.putAssumeCapacityNoClobber( - try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena), + try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, target), {}, ); } @@ -12817,7 +12896,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const is_tuple_val = struct_val[3]; // Decls - if (decls_val.sliceLen() > 0) { + if (decls_val.sliceLen(target) > 0) { return sema.fail(block, src, "reified structs must have no decls", .{}); } @@ -12847,7 +12926,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I } // Decls - if (decls_val.sliceLen() > 0) { + if (decls_val.sliceLen(target) > 0) { return sema.fail(block, src, "reified enums must have no decls", .{}); } @@ -12898,11 +12977,12 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I enum_obj.tag_ty = try tag_type_val.toType(&buffer).copy(new_decl_arena_allocator); // Fields - const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen()); + const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target)); if (fields_len > 0) { try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{ .ty = enum_obj.tag_ty, + .target = target, }); var i: usize = 0; @@ -12918,6 +12998,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const field_name = try name_val.toAllocatedBytes( Type.initTag(.const_slice_u8), new_decl_arena_allocator, + target, ); const gop = enum_obj.fields.getOrPutAssumeCapacity(field_name); @@ -12929,6 +13010,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const copied_tag_val = try value_val.copy(new_decl_arena_allocator); enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{ .ty = enum_obj.tag_ty, + .target = target, }); } } @@ -12942,7 +13024,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const decls_val = struct_val[0]; // Decls - if (decls_val.sliceLen() > 0) { + if (decls_val.sliceLen(target) > 0) { return sema.fail(block, src, "reified opaque must have no decls", .{}); } @@ -12993,7 +13075,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const decls_val = struct_val[3]; // Decls - if (decls_val.sliceLen() > 0) { + if (decls_val.sliceLen(target) > 0) { return sema.fail(block, src, "reified unions must have no decls", .{}); } @@ -13033,7 +13115,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I }; // Tag type - const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen()); + const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target)); union_obj.tag_ty = if (tag_type_val.optionalValue()) |payload_val| blk: { var buffer: Value.ToTypeBuffer = undefined; break :blk try payload_val.toType(&buffer).copy(new_decl_arena_allocator); @@ -13058,6 +13140,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const field_name = try name_val.toAllocatedBytes( Type.initTag(.const_slice_u8), new_decl_arena_allocator, + target, ); const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); @@ -13069,7 +13152,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I var buffer: Value.ToTypeBuffer = undefined; gop.value_ptr.* = .{ .ty = try field_type_val.toType(&buffer).copy(new_decl_arena_allocator), - .abi_align = @intCast(u32, alignment_val.toUnsignedInt()), + .abi_align = @intCast(u32, alignment_val.toUnsignedInt(target)), }; } } @@ -13089,7 +13172,9 @@ fn reifyTuple( src: LazySrcLoc, fields_val: Value, ) CompileError!Air.Inst.Ref { - const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen()); + const target = sema.mod.getTarget(); + + const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target)); if (fields_len == 0) return sema.addType(Type.initTag(.empty_struct_literal)); const types = try sema.arena.alloc(Type, fields_len); @@ -13114,6 +13199,7 @@ fn reifyTuple( const field_name = try name_val.toAllocatedBytes( Type.initTag(.const_slice_u8), sema.arena, + target, ); const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch |err| { @@ -13197,8 +13283,10 @@ fn reifyStruct( }, }; + const target = sema.mod.getTarget(); + // Fields - const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen()); + const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target)); try struct_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); var i: usize = 0; while (i < fields_len) : (i += 1) { @@ -13219,6 +13307,7 @@ fn reifyStruct( const field_name = try name_val.toAllocatedBytes( Type.initTag(.const_slice_u8), new_decl_arena_allocator, + target, ); const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name); @@ -13238,7 +13327,7 @@ fn reifyStruct( var buffer: Value.ToTypeBuffer = undefined; gop.value_ptr.* = .{ .ty = try field_type_val.toType(&buffer).copy(new_decl_arena_allocator), - .abi_align = @intCast(u32, alignment_val.toUnsignedInt()), + .abi_align = @intCast(u32, alignment_val.toUnsignedInt(target)), .default_val = default_val, .is_comptime = is_comptime_val.toBool(), .offset = undefined, @@ -13257,7 +13346,8 @@ fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded); defer anon_decl.deinit(); - const bytes = try ty.nameAllocArena(anon_decl.arena()); + const target = sema.mod.getTarget(); + const bytes = try ty.nameAllocArena(anon_decl.arena(), target); const new_decl = try anon_decl.finish( try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), @@ -13296,7 +13386,10 @@ fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const target = sema.mod.getTarget(); const result_val = val.floatToInt(sema.arena, operand_ty, dest_ty, target) catch |err| switch (err) { error.FloatCannotFit => { - return sema.fail(block, operand_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty }); + return sema.fail(block, operand_src, "integer value {d} cannot be stored in type '{}'", .{ + std.math.floor(val.toFloat(f64)), + dest_ty.fmt(target), + }); }, else => |e| return e, }; @@ -13344,13 +13437,14 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai try sema.checkPtrType(block, type_src, type_res); try sema.resolveTypeLayout(block, src, type_res.elemType2()); const ptr_align = type_res.ptrAlignment(sema.mod.getTarget()); + const target = sema.mod.getTarget(); if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| { - const addr = val.toUnsignedInt(); + const addr = val.toUnsignedInt(target); if (!type_res.isAllowzeroPtr() and addr == 0) - return sema.fail(block, operand_src, "pointer type '{}' does not allow address zero", .{type_res}); + return sema.fail(block, operand_src, "pointer type '{}' does not allow address zero", .{type_res.fmt(target)}); if (addr != 0 and addr % ptr_align != 0) - return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{type_res}); + return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{type_res.fmt(target)}); const val_payload = try sema.arena.create(Value.Payload.U64); val_payload.* = .{ @@ -13394,6 +13488,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); const operand = sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); + const target = sema.mod.getTarget(); try sema.checkErrorSetType(block, dest_ty_src, dest_ty); try sema.checkErrorSetType(block, operand_src, operand_ty); @@ -13407,7 +13502,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! block, src, "error.{s} not a member of error set '{}'", - .{ error_name, dest_ty }, + .{ error_name, dest_ty.fmt(target) }, ); } } @@ -13502,7 +13597,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (operand_info.signedness != dest_info.signedness) { return sema.fail(block, operand_src, "expected {s} integer type, found '{}'", .{ - @tagName(dest_info.signedness), operand_ty, + @tagName(dest_info.signedness), operand_ty.fmt(target), }); } if (operand_info.bits < dest_info.bits) { @@ -13511,7 +13606,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, "destination type '{}' has more bits than source type '{}'", - .{ dest_ty, operand_ty }, + .{ dest_ty.fmt(target), operand_ty.fmt(target) }, ); errdefer msg.destroy(sema.gpa); try sema.errNote(block, dest_ty_src, msg, "destination type has {d} bits", .{ @@ -13531,14 +13626,14 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (!is_vector) { return sema.addConstant( dest_ty, - try val.intTrunc(operand_ty, sema.arena, dest_info.signedness, dest_info.bits), + try val.intTrunc(operand_ty, sema.arena, dest_info.signedness, dest_info.bits, target), ); } var elem_buf: Value.ElemValueBuffer = undefined; const elems = try sema.arena.alloc(Value, operand_ty.vectorLen()); for (elems) |*elem, i| { const elem_val = val.elemValueBuffer(i, &elem_buf); - elem.* = try elem_val.intTrunc(operand_scalar_ty, sema.arena, dest_info.signedness, dest_info.bits); + elem.* = try elem_val.intTrunc(operand_scalar_ty, sema.arena, dest_info.signedness, dest_info.bits, target); } return sema.addConstant( dest_ty, @@ -13653,7 +13748,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, ty_src, "@byteSwap requires the number of bits to be evenly divisible by 8, but {} has {} bits", - .{ scalar_ty, bits }, + .{ scalar_ty.fmt(target), bits }, ); } @@ -13765,6 +13860,7 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 const ty = try sema.resolveType(block, lhs_src, extra.lhs); const field_name = try sema.resolveConstString(block, rhs_src, extra.rhs); + const target = sema.mod.getTarget(); try sema.resolveTypeLayout(block, lhs_src, ty); if (ty.tag() != .@"struct") { @@ -13772,7 +13868,7 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 block, lhs_src, "expected struct type, found '{}'", - .{ty}, + .{ty.fmt(target)}, ); } @@ -13782,11 +13878,10 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 block, rhs_src, "struct '{}' has no field '{s}'", - .{ ty, field_name }, + .{ ty.fmt(target), field_name }, ); }; - const target = sema.mod.getTarget(); switch (ty.containerLayout()) { .Packed => { var bit_sum: u64 = 0; @@ -13809,18 +13904,20 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 } fn checkNamespaceType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { + const target = sema.mod.getTarget(); switch (ty.zigTypeTag()) { .Struct, .Enum, .Union, .Opaque => return, - else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{}'", .{ty}), + else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{}'", .{ty.fmt(target)}), } } /// Returns `true` if the type was a comptime_int. fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { + const target = sema.mod.getTarget(); switch (try ty.zigTypeTagOrPoison()) { .ComptimeInt => return true, .Int => return false, - else => return sema.fail(block, src, "expected integer type, found '{}'", .{ty}), + else => return sema.fail(block, src, "expected integer type, found '{}'", .{ty.fmt(target)}), } } @@ -13830,6 +13927,7 @@ fn checkPtrOperand( ty_src: LazySrcLoc, ty: Type, ) CompileError!void { + const target = sema.mod.getTarget(); switch (ty.zigTypeTag()) { .Pointer => return, .Fn => { @@ -13838,7 +13936,7 @@ fn checkPtrOperand( block, ty_src, "expected pointer, found {}", - .{ty}, + .{ty.fmt(target)}, ); errdefer msg.destroy(sema.gpa); @@ -13851,7 +13949,7 @@ fn checkPtrOperand( .Optional => if (ty.isPtrLikeOptional()) return, else => {}, } - return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty}); + return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(target)}); } fn checkPtrType( @@ -13860,6 +13958,7 @@ fn checkPtrType( ty_src: LazySrcLoc, ty: Type, ) CompileError!void { + const target = sema.mod.getTarget(); switch (ty.zigTypeTag()) { .Pointer => return, .Fn => { @@ -13868,7 +13967,7 @@ fn checkPtrType( block, ty_src, "expected pointer type, found '{}'", - .{ty}, + .{ty.fmt(target)}, ); errdefer msg.destroy(sema.gpa); @@ -13881,7 +13980,7 @@ fn checkPtrType( .Optional => if (ty.isPtrLikeOptional()) return, else => {}, } - return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty}); + return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(target)}); } fn checkVectorElemType( @@ -13894,7 +13993,8 @@ fn checkVectorElemType( .Int, .Float, .Bool => return, else => if (ty.isPtrAtRuntime()) return, } - return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty}); + const target = sema.mod.getTarget(); + return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty.fmt(target)}); } fn checkFloatType( @@ -13903,9 +14003,10 @@ fn checkFloatType( ty_src: LazySrcLoc, ty: Type, ) CompileError!void { + const target = sema.mod.getTarget(); switch (ty.zigTypeTag()) { .ComptimeInt, .ComptimeFloat, .Float => {}, - else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty}), + else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty.fmt(target)}), } } @@ -13915,13 +14016,14 @@ fn checkNumericType( ty_src: LazySrcLoc, ty: Type, ) CompileError!void { + const target = sema.mod.getTarget(); switch (ty.zigTypeTag()) { .ComptimeFloat, .Float, .ComptimeInt, .Int => {}, .Vector => switch (ty.childType().zigTypeTag()) { .ComptimeFloat, .Float, .ComptimeInt, .Int => {}, else => |t| return sema.fail(block, ty_src, "expected number, found '{}'", .{t}), }, - else => return sema.fail(block, ty_src, "expected number, found '{}'", .{ty}), + else => return sema.fail(block, ty_src, "expected number, found '{}'", .{ty.fmt(target)}), } } @@ -13957,7 +14059,7 @@ fn checkAtomicOperandType( block, ty_src, "expected bool, integer, float, enum, or pointer type; found {}", - .{ty}, + .{ty.fmt(target)}, ); }, }; @@ -14021,6 +14123,7 @@ fn checkIntOrVector( operand_src: LazySrcLoc, ) CompileError!Type { const operand_ty = sema.typeOf(operand); + const target = sema.mod.getTarget(); switch (try operand_ty.zigTypeTagOrPoison()) { .Int => return operand_ty, .Vector => { @@ -14028,12 +14131,12 @@ fn checkIntOrVector( switch (try elem_ty.zigTypeTagOrPoison()) { .Int => return elem_ty, else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{ - elem_ty, + elem_ty.fmt(target), }), } }, else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{ - operand_ty, + operand_ty.fmt(target), }), } } @@ -14045,6 +14148,7 @@ fn checkIntOrVectorAllowComptime( operand_src: LazySrcLoc, ) CompileError!Type { const operand_ty = sema.typeOf(operand); + const target = sema.mod.getTarget(); switch (try operand_ty.zigTypeTagOrPoison()) { .Int, .ComptimeInt => return operand_ty, .Vector => { @@ -14052,20 +14156,21 @@ fn checkIntOrVectorAllowComptime( switch (try elem_ty.zigTypeTagOrPoison()) { .Int, .ComptimeInt => return elem_ty, else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{ - elem_ty, + elem_ty.fmt(target), }), } }, else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{ - operand_ty, + operand_ty.fmt(target), }), } } fn checkErrorSetType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { + const target = sema.mod.getTarget(); switch (ty.zigTypeTag()) { .ErrorSet => return, - else => return sema.fail(block, src, "expected error set type, found '{}'", .{ty}), + else => return sema.fail(block, src, "expected error set type, found '{}'", .{ty.fmt(target)}), } } @@ -14138,9 +14243,10 @@ fn checkVectorizableBinaryOperands( return sema.failWithOwnedErrorMsg(block, msg); } } else if (lhs_zig_ty_tag == .Vector or rhs_zig_ty_tag == .Vector) { + const target = sema.mod.getTarget(); const msg = msg: { const msg = try sema.errMsg(block, src, "mixed scalar and vector operands: {} and {}", .{ - lhs_ty, rhs_ty, + lhs_ty.fmt(target), rhs_ty.fmt(target), }); errdefer msg.destroy(sema.gpa); if (lhs_zig_ty_tag == .Vector) { @@ -14179,8 +14285,9 @@ fn resolveExportOptions( return sema.fail(block, src, "TODO: implement exporting with linksection", .{}); } const name_ty = Type.initTag(.const_slice_u8); + const target = sema.mod.getTarget(); return std.builtin.ExportOptions{ - .name = try name_val.toAllocatedBytes(name_ty, sema.arena), + .name = try name_val.toAllocatedBytes(name_ty, sema.arena, target), .linkage = linkage_val.toEnum(std.builtin.GlobalLinkage), .section = null, // TODO }; @@ -14239,12 +14346,13 @@ fn zirCmpxchg( const ptr_ty = sema.typeOf(ptr); const elem_ty = ptr_ty.elemType(); try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty); + const target = sema.mod.getTarget(); if (elem_ty.zigTypeTag() == .Float) { return sema.fail( block, elem_ty_src, "expected bool, integer, enum, or pointer type; found '{}'", - .{elem_ty}, + .{elem_ty.fmt(target)}, ); } const expected_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.expected_value), expected_src); @@ -14281,7 +14389,7 @@ fn zirCmpxchg( return sema.addConstUndef(result_ty); } const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; - const result_val = if (stored_val.eql(expected_val, elem_ty)) blk: { + const result_val = if (stored_val.eql(expected_val, elem_ty, target)) blk: { try sema.storePtr(block, src, ptr, new_value); break :blk Value.@"null"; } else try Value.Tag.opt_payload.create(sema.arena, stored_val); @@ -14343,9 +14451,10 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, "ReduceOp"); const operand = sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); + const target = sema.mod.getTarget(); if (operand_ty.zigTypeTag() != .Vector) { - return sema.fail(block, operand_src, "expected vector, found {}", .{operand_ty}); + return sema.fail(block, operand_src, "expected vector, found {}", .{operand_ty.fmt(target)}); } const scalar_ty = operand_ty.childType(); @@ -14355,13 +14464,13 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. .And, .Or, .Xor => switch (scalar_ty.zigTypeTag()) { .Int, .Bool => {}, else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found {}", .{ - @tagName(operation), operand_ty, + @tagName(operation), operand_ty.fmt(target), }), }, .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag()) { .Int, .Float => {}, else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found {}", .{ - @tagName(operation), operand_ty, + @tagName(operation), operand_ty.fmt(target), }), }, } @@ -14376,18 +14485,17 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| { if (operand_val.isUndef()) return sema.addConstUndef(scalar_ty); - const target = sema.mod.getTarget(); var accum: Value = try operand_val.elemValue(sema.arena, 0); var elem_buf: Value.ElemValueBuffer = undefined; var i: u32 = 1; while (i < vec_len) : (i += 1) { const elem_val = operand_val.elemValueBuffer(i, &elem_buf); switch (operation) { - .And => accum = try accum.bitwiseAnd(elem_val, scalar_ty, sema.arena), - .Or => accum = try accum.bitwiseOr(elem_val, scalar_ty, sema.arena), - .Xor => accum = try accum.bitwiseXor(elem_val, scalar_ty, sema.arena), - .Min => accum = accum.numberMin(elem_val), - .Max => accum = accum.numberMax(elem_val), + .And => accum = try accum.bitwiseAnd(elem_val, scalar_ty, sema.arena, target), + .Or => accum = try accum.bitwiseOr(elem_val, scalar_ty, sema.arena, target), + .Xor => accum = try accum.bitwiseXor(elem_val, scalar_ty, sema.arena, target), + .Min => accum = accum.numberMin(elem_val, target), + .Max => accum = accum.numberMax(elem_val, target), .Add => accum = try accum.numberAddWrap(elem_val, scalar_ty, sema.arena, target), .Mul => accum = try accum.numberMulWrap(elem_val, scalar_ty, sema.arena, target), } @@ -14417,10 +14525,11 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air var b = sema.resolveInst(extra.b); var mask = sema.resolveInst(extra.mask); var mask_ty = sema.typeOf(mask); + const target = sema.mod.getTarget(); const mask_len = switch (sema.typeOf(mask).zigTypeTag()) { .Array, .Vector => sema.typeOf(mask).arrayLen(), - else => return sema.fail(block, mask_src, "expected vector or array, found {}", .{sema.typeOf(mask)}), + else => return sema.fail(block, mask_src, "expected vector or array, found {}", .{sema.typeOf(mask).fmt(target)}), }; mask_ty = try Type.Tag.vector.create(sema.arena, .{ .len = mask_len, @@ -14452,20 +14561,21 @@ fn analyzeShuffle( .elem_type = elem_ty, }); + const target = sema.mod.getTarget(); var maybe_a_len = switch (sema.typeOf(a).zigTypeTag()) { .Array, .Vector => sema.typeOf(a).arrayLen(), .Undefined => null, else => return sema.fail(block, a_src, "expected vector or array with element type {}, found {}", .{ - elem_ty, - sema.typeOf(a), + elem_ty.fmt(target), + sema.typeOf(a).fmt(target), }), }; var maybe_b_len = switch (sema.typeOf(b).zigTypeTag()) { .Array, .Vector => sema.typeOf(b).arrayLen(), .Undefined => null, else => return sema.fail(block, b_src, "expected vector or array with element type {}, found {}", .{ - elem_ty, - sema.typeOf(b), + elem_ty.fmt(target), + sema.typeOf(b).fmt(target), }), }; if (maybe_a_len == null and maybe_b_len == null) { @@ -14513,7 +14623,7 @@ fn analyzeShuffle( try sema.errNote(block, operand_info[chosen][1], msg, "selected index {d} out of bounds of {}", .{ unsigned, - operand_info[chosen][2], + operand_info[chosen][2].fmt(target), }); if (chosen == 1) { @@ -14704,12 +14814,12 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A .Xchg => operand_val, .Add => try stored_val.numberAddWrap(operand_val, operand_ty, sema.arena, target), .Sub => try stored_val.numberSubWrap(operand_val, operand_ty, sema.arena, target), - .And => try stored_val.bitwiseAnd (operand_val, operand_ty, sema.arena), + .And => try stored_val.bitwiseAnd (operand_val, operand_ty, sema.arena, target), .Nand => try stored_val.bitwiseNand (operand_val, operand_ty, sema.arena, target), - .Or => try stored_val.bitwiseOr (operand_val, operand_ty, sema.arena), - .Xor => try stored_val.bitwiseXor (operand_val, operand_ty, sema.arena), - .Max => stored_val.numberMax (operand_val), - .Min => stored_val.numberMin (operand_val), + .Or => try stored_val.bitwiseOr (operand_val, operand_ty, sema.arena, target), + .Xor => try stored_val.bitwiseXor (operand_val, operand_ty, sema.arena, target), + .Max => stored_val.numberMax (operand_val, target), + .Min => stored_val.numberMin (operand_val, target), // zig fmt: on }; try sema.storePtrVal(block, src, ptr_val, new_val, operand_ty); @@ -14788,7 +14898,7 @@ fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. switch (ty.zigTypeTag()) { .ComptimeFloat, .Float, .Vector => {}, - else => return sema.fail(block, src, "expected vector of floats or float type, found '{}'", .{ty}), + else => return sema.fail(block, src, "expected vector of floats or float type, found '{}'", .{ty.fmt(target)}), } const runtime_src = if (maybe_mulend1) |mulend1_val| rs: { @@ -14814,7 +14924,7 @@ fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const scalar_ty = ty.scalarType(); switch (scalar_ty.zigTypeTag()) { .ComptimeFloat, .Float => {}, - else => return sema.fail(block, src, "expected vector of floats, found vector of '{}'", .{scalar_ty}), + else => return sema.fail(block, src, "expected vector of floats, found vector of '{}'", .{scalar_ty.fmt(target)}), } const vec_len = ty.vectorLen(); @@ -14906,9 +15016,10 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError break :modifier modifier_val.toEnum(std.builtin.CallOptions.Modifier); }; + const target = sema.mod.getTarget(); const args_ty = sema.typeOf(args); if (!args_ty.isTuple() and args_ty.tag() != .empty_struct_literal) { - return sema.fail(block, args_src, "expected a tuple, found {}", .{args_ty}); + return sema.fail(block, args_src, "expected a tuple, found {}", .{args_ty.fmt(target)}); } var resolved_args: []Air.Inst.Ref = undefined; @@ -14945,9 +15056,10 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const field_name = try sema.resolveConstString(block, name_src, extra.field_name); const field_ptr = sema.resolveInst(extra.field_ptr); const field_ptr_ty = sema.typeOf(field_ptr); + const target = sema.mod.getTarget(); if (struct_ty.zigTypeTag() != .Struct) { - return sema.fail(block, ty_src, "expected struct type, found '{}'", .{struct_ty}); + return sema.fail(block, ty_src, "expected struct type, found '{}'", .{struct_ty.fmt(target)}); } try sema.resolveTypeLayout(block, ty_src, struct_ty); @@ -14956,7 +15068,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr return sema.failWithBadStructFieldAccess(block, struct_obj, name_src, field_name); if (field_ptr_ty.zigTypeTag() != .Pointer) { - return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{field_ptr_ty}); + return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{field_ptr_ty.fmt(target)}); } const field = struct_obj.fields.values()[field_index]; const field_ptr_ty_info = field_ptr_ty.ptrInfo().data; @@ -14973,7 +15085,6 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr ptr_ty_data.@"align" = field.abi_align; } - const target = sema.mod.getTarget(); const actual_field_ptr_ty = try Type.ptr(sema.arena, target, ptr_ty_data); const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, ptr_src); @@ -15042,8 +15153,9 @@ fn analyzeMinMax( .max => Value.numberMax, else => unreachable, }; + const target = sema.mod.getTarget(); const vec_len = simd_op.len orelse { - const result_val = opFunc(lhs_val, rhs_val); + const result_val = opFunc(lhs_val, rhs_val, target); return sema.addConstant(simd_op.result_ty, result_val); }; var lhs_buf: Value.ElemValueBuffer = undefined; @@ -15052,7 +15164,7 @@ fn analyzeMinMax( for (elems) |*elem, i| { const lhs_elem_val = lhs_val.elemValueBuffer(i, &lhs_buf); const rhs_elem_val = rhs_val.elemValueBuffer(i, &rhs_buf); - elem.* = opFunc(lhs_elem_val, rhs_elem_val); + elem.* = opFunc(lhs_elem_val, rhs_elem_val, target); } return sema.addConstant( simd_op.result_ty, @@ -15078,17 +15190,17 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; const dest_ptr = sema.resolveInst(extra.dest); const dest_ptr_ty = sema.typeOf(dest_ptr); + const target = sema.mod.getTarget(); try sema.checkPtrOperand(block, dest_src, dest_ptr_ty); if (dest_ptr_ty.isConstPtr()) { - return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty}); + return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(target)}); } const uncasted_src_ptr = sema.resolveInst(extra.source); const uncasted_src_ptr_ty = sema.typeOf(uncasted_src_ptr); try sema.checkPtrOperand(block, src_src, uncasted_src_ptr_ty); const src_ptr_info = uncasted_src_ptr_ty.ptrInfo().data; - const target = sema.mod.getTarget(); const wanted_src_ptr_ty = try Type.ptr(sema.arena, target, .{ .pointee_type = dest_ptr_ty.elemType2(), .@"align" = src_ptr_info.@"align", @@ -15136,9 +15248,10 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; const dest_ptr = sema.resolveInst(extra.dest); const dest_ptr_ty = sema.typeOf(dest_ptr); + const target = sema.mod.getTarget(); try sema.checkPtrOperand(block, dest_src, dest_ptr_ty); if (dest_ptr_ty.isConstPtr()) { - return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty}); + return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(target)}); } const elem_ty = dest_ptr_ty.elemType2(); const value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.byte), value_src); @@ -15452,6 +15565,7 @@ fn zirPrefetch( const ptr = sema.resolveInst(extra.lhs); try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr)); const options = try sema.coerce(block, options_ty, sema.resolveInst(extra.rhs), opts_src); + const target = sema.mod.getTarget(); const rw = try sema.fieldVal(block, opts_src, options, "rw", opts_src); const rw_val = try sema.resolveConstValue(block, opts_src, rw); @@ -15459,7 +15573,7 @@ fn zirPrefetch( const locality = try sema.fieldVal(block, opts_src, options, "locality", opts_src); const locality_val = try sema.resolveConstValue(block, opts_src, locality); - const locality_int = @intCast(u2, locality_val.toUnsignedInt()); + const locality_int = @intCast(u2, locality_val.toUnsignedInt(target)); const cache = try sema.fieldVal(block, opts_src, options, "cache", opts_src); const cache_val = try sema.resolveConstValue(block, opts_src, cache); @@ -15492,6 +15606,7 @@ fn zirBuiltinExtern( var ty = try sema.resolveType(block, ty_src, extra.lhs); const options_inst = sema.resolveInst(extra.rhs); + const target = sema.mod.getTarget(); const options = options: { const extern_options_ty = try sema.getBuiltinType(block, options_src, "ExternOptions"); @@ -15512,11 +15627,11 @@ fn zirBuiltinExtern( var library_name: ?[]const u8 = null; if (!library_name_val.isNull()) { const payload = library_name_val.castTag(.opt_payload).?.data; - library_name = try payload.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena); + library_name = try payload.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, target); } break :options std.builtin.ExternOptions{ - .name = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena), + .name = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, target), .library_name = library_name, .linkage = linkage_val.toEnum(std.builtin.GlobalLinkage), .is_thread_local = is_thread_local_val.toBool(), @@ -15609,8 +15724,9 @@ fn validateVarType( ) CompileError!void { if (try sema.validateRunTimeType(block, src, var_ty, is_extern)) return; + const target = sema.mod.getTarget(); const msg = msg: { - const msg = try sema.errMsg(block, src, "variable of type '{}' must be const or comptime", .{var_ty}); + const msg = try sema.errMsg(block, src, "variable of type '{}' must be const or comptime", .{var_ty.fmt(target)}); errdefer msg.destroy(sema.gpa); try sema.explainWhyTypeIsComptime(block, src, msg, src.toSrcLoc(block.src_decl), var_ty); @@ -15685,6 +15801,7 @@ fn explainWhyTypeIsComptime( ty: Type, ) CompileError!void { const mod = sema.mod; + const target = mod.getTarget(); switch (ty.zigTypeTag()) { .Bool, .Int, @@ -15698,7 +15815,7 @@ fn explainWhyTypeIsComptime( .Fn => { try mod.errNoteNonLazy(src_loc, msg, "use '*const {}' for a function pointer type", .{ - ty, + ty.fmt(target), }); }, @@ -15941,6 +16058,8 @@ fn fieldVal( else object_ty; + const target = sema.mod.getTarget(); + switch (inner_ty.zigTypeTag()) { .Array => { if (mem.eql(u8, field_name, "len")) { @@ -15953,7 +16072,7 @@ fn fieldVal( block, field_name_src, "no member named '{s}' in '{}'", - .{ field_name, object_ty }, + .{ field_name, object_ty.fmt(target) }, ); } }, @@ -15977,7 +16096,7 @@ fn fieldVal( block, field_name_src, "no member named '{s}' in '{}'", - .{ field_name, object_ty }, + .{ field_name, object_ty.fmt(target) }, ); } } else if (ptr_info.pointee_type.zigTypeTag() == .Array) { @@ -15991,7 +16110,7 @@ fn fieldVal( block, field_name_src, "no member named '{s}' in '{}'", - .{ field_name, ptr_info.pointee_type }, + .{ field_name, ptr_info.pointee_type.fmt(target) }, ); } } @@ -16013,7 +16132,7 @@ fn fieldVal( break :blk entry.key_ptr.*; } return sema.fail(block, src, "no error named '{s}' in '{}'", .{ - field_name, child_type, + field_name, child_type.fmt(target), }); } else (try sema.mod.getErrorValue(field_name)).key; @@ -16067,10 +16186,10 @@ fn fieldVal( else => unreachable, }; return sema.fail(block, src, "{s} '{}' has no member named '{s}'", .{ - kw_name, child_type, field_name, + kw_name, child_type.fmt(target), field_name, }); }, - else => return sema.fail(block, src, "type '{}' has no members", .{child_type}), + else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(target)}), } }, .Struct => if (is_pointer_to) { @@ -16089,7 +16208,7 @@ fn fieldVal( }, else => {}, } - return sema.fail(block, src, "type '{}' does not support field access", .{object_ty}); + return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(target)}); } fn fieldPtr( @@ -16103,11 +16222,12 @@ fn fieldPtr( // When editing this function, note that there is corresponding logic to be edited // in `fieldVal`. This function takes a pointer and returns a pointer. + const target = sema.mod.getTarget(); const object_ptr_src = src; // TODO better source location const object_ptr_ty = sema.typeOf(object_ptr); const object_ty = switch (object_ptr_ty.zigTypeTag()) { .Pointer => object_ptr_ty.elemType(), - else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty}), + else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty.fmt(target)}), }; // Zig allows dereferencing a single pointer during field lookup. Note that @@ -16120,8 +16240,6 @@ fn fieldPtr( else object_ty; - const target = sema.mod.getTarget(); - switch (inner_ty.zigTypeTag()) { .Array => { if (mem.eql(u8, field_name, "len")) { @@ -16137,7 +16255,7 @@ fn fieldPtr( block, field_name_src, "no member named '{s}' in '{}'", - .{ field_name, object_ty }, + .{ field_name, object_ty.fmt(target) }, ); } }, @@ -16177,7 +16295,7 @@ fn fieldPtr( return sema.analyzeDeclRef(try anon_decl.finish( Type.usize, - try Value.Tag.int_u64.create(anon_decl.arena(), val.sliceLen()), + try Value.Tag.int_u64.create(anon_decl.arena(), val.sliceLen(target)), 0, // default alignment )); } @@ -16195,7 +16313,7 @@ fn fieldPtr( block, field_name_src, "no member named '{s}' in '{}'", - .{ field_name, object_ty }, + .{ field_name, object_ty.fmt(target) }, ); } }, @@ -16219,7 +16337,7 @@ fn fieldPtr( break :blk entry.key_ptr.*; } return sema.fail(block, src, "no error named '{s}' in '{}'", .{ - field_name, child_type, + field_name, child_type.fmt(target), }); } else (try sema.mod.getErrorValue(field_name)).key; @@ -16277,7 +16395,7 @@ fn fieldPtr( } return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); }, - else => return sema.fail(block, src, "type '{}' has no members", .{child_type}), + else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(target)}), } }, .Struct => { @@ -16296,7 +16414,7 @@ fn fieldPtr( }, else => {}, } - return sema.fail(block, src, "type '{}' does not support field access (fieldPtr, {}.{s})", .{ object_ty, object_ptr_ty, field_name }); + return sema.fail(block, src, "type '{}' does not support field access (fieldPtr, {}.{s})", .{ object_ty.fmt(target), object_ptr_ty.fmt(target), field_name }); } fn fieldCallBind( @@ -16310,12 +16428,13 @@ fn fieldCallBind( // When editing this function, note that there is corresponding logic to be edited // in `fieldVal`. This function takes a pointer and returns a pointer. + const target = sema.mod.getTarget(); const raw_ptr_src = src; // TODO better source location const raw_ptr_ty = sema.typeOf(raw_ptr); const inner_ty = if (raw_ptr_ty.zigTypeTag() == .Pointer and raw_ptr_ty.ptrSize() == .One) raw_ptr_ty.childType() else - return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty}); + return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(target)}); // Optionally dereference a second pointer to get the concrete type. const is_double_ptr = inner_ty.zigTypeTag() == .Pointer and inner_ty.ptrSize() == .One; @@ -16375,7 +16494,7 @@ fn fieldCallBind( first_param_type.zigTypeTag() == .Pointer and (first_param_type.ptrSize() == .One or first_param_type.ptrSize() == .C) and - first_param_type.childType().eql(concrete_ty))) + first_param_type.childType().eql(concrete_ty, target))) { // zig fmt: on // TODO: bound fn calls on rvalues should probably @@ -16386,7 +16505,7 @@ fn fieldCallBind( .arg0_inst = object_ptr, }); return sema.addConstant(ty, value); - } else if (first_param_type.eql(concrete_ty)) { + } else if (first_param_type.eql(concrete_ty, target)) { var deref = try sema.analyzeLoad(block, src, object_ptr, src); const ty = Type.Tag.bound_fn.init(); const value = try Value.Tag.bound_fn.create(arena, .{ @@ -16402,7 +16521,7 @@ fn fieldCallBind( else => {}, } - return sema.fail(block, src, "type '{}' has no field or member function named '{s}'", .{ concrete_ty, field_name }); + return sema.fail(block, src, "type '{}' has no field or member function named '{s}'", .{ concrete_ty.fmt(target), field_name }); } fn finishFieldCallBind( @@ -16540,10 +16659,11 @@ fn structFieldPtrByIndex( .@"addrspace" = struct_ptr_ty_info.@"addrspace", }; + const target = sema.mod.getTarget(); + // TODO handle when the struct pointer is overaligned, we should return a potentially // over-aligned field pointer too. if (struct_obj.layout == .Packed) { - const target = sema.mod.getTarget(); comptime assert(Type.packed_struct_layout_version == 2); var running_bits: u16 = 0; @@ -16567,7 +16687,6 @@ fn structFieldPtrByIndex( ptr_ty_data.@"align" = field.abi_align; } - const target = sema.mod.getTarget(); const ptr_field_ty = try Type.ptr(sema.arena, target, ptr_ty_data); if (field.is_comptime) { @@ -16667,14 +16786,15 @@ fn tupleFieldIndex( field_name: []const u8, field_name_src: LazySrcLoc, ) CompileError!u32 { + const target = sema.mod.getTarget(); const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch |err| { return sema.fail(block, field_name_src, "tuple {} has no such field '{s}': {s}", .{ - tuple_ty, field_name, @errorName(err), + tuple_ty.fmt(target), field_name, @errorName(err), }); }; if (field_index >= tuple_ty.structFieldCount()) { return sema.fail(block, field_name_src, "tuple {} has no such field '{s}'", .{ - tuple_ty, field_name, + tuple_ty.fmt(target), field_name, }); } return field_index; @@ -16749,7 +16869,7 @@ fn unionFieldPtr( // .data = field_index, //}; //const field_tag = Value.initPayload(&field_tag_buf.base); - //const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty); + //const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, target); //if (!tag_matches) { // // TODO enhance this saying which one was active // // and which one was accessed, and showing where the union was declared. @@ -16798,7 +16918,8 @@ fn unionFieldVal( .data = field_index, }; const field_tag = Value.initPayload(&field_tag_buf.base); - const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty); + const target = sema.mod.getTarget(); + const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, target); switch (union_obj.layout) { .Auto => { if (tag_matches) { @@ -16813,7 +16934,7 @@ fn unionFieldVal( if (tag_matches) { return sema.addConstant(field.ty, tag_and_val.val); } else { - const old_ty = union_ty.unionFieldType(tag_and_val.tag); + const old_ty = union_ty.unionFieldType(tag_and_val.tag, target); const new_val = try sema.bitCastVal(block, src, tag_and_val.val, old_ty, field.ty, 0); return sema.addConstant(field.ty, new_val); } @@ -16835,19 +16956,19 @@ fn elemPtr( ) CompileError!Air.Inst.Ref { const indexable_ptr_src = src; // TODO better source location const indexable_ptr_ty = sema.typeOf(indexable_ptr); + const target = sema.mod.getTarget(); const indexable_ty = switch (indexable_ptr_ty.zigTypeTag()) { .Pointer => indexable_ptr_ty.elemType(), - else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty}), + else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(target)}), }; if (!indexable_ty.isIndexable()) { - return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty}); + return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty.fmt(target)}); } switch (indexable_ty.zigTypeTag()) { .Pointer => { // In all below cases, we have to deref the ptr operand to get the actual indexable pointer. const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); - const target = sema.mod.getTarget(); const result_ty = try indexable_ty.elemPtrType(sema.arena, target); switch (indexable_ty.ptrSize()) { .Slice => return sema.elemPtrSlice(block, indexable_ptr_src, indexable, elem_index_src, elem_index), @@ -16858,8 +16979,8 @@ fn elemPtr( const runtime_src = rs: { const ptr_val = maybe_ptr_val orelse break :rs indexable_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(indexable_ty, sema.arena, index); + const index = @intCast(usize, index_val.toUnsignedInt(target)); + const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, target); return sema.addConstant(result_ty, elem_ptr); }; @@ -16876,7 +16997,7 @@ fn elemPtr( .Struct => { // Tuple field access. const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index); - const index = @intCast(u32, index_val.toUnsignedInt()); + const index = @intCast(u32, index_val.toUnsignedInt(target)); return sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index); }, else => unreachable, @@ -16893,9 +17014,10 @@ fn elemVal( ) CompileError!Air.Inst.Ref { const indexable_src = src; // TODO better source location const indexable_ty = sema.typeOf(indexable); + const target = sema.mod.getTarget(); if (!indexable_ty.isIndexable()) { - return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty}); + return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty.fmt(target)}); } // TODO in case of a vector of pointers, we need to detect whether the element @@ -16912,7 +17034,7 @@ fn elemVal( const runtime_src = rs: { const indexable_val = maybe_indexable_val orelse break :rs indexable_src; const index_val = maybe_index_val orelse break :rs elem_index_src; - const index = @intCast(usize, index_val.toUnsignedInt()); + const index = @intCast(usize, index_val.toUnsignedInt(target)); const elem_ty = indexable_ty.elemType2(); var payload: Value.Payload.ElemPtr = .{ .data = .{ @@ -16945,7 +17067,7 @@ fn elemVal( .Struct => { // Tuple field access. const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index); - const index = @intCast(u32, index_val.toUnsignedInt()); + const index = @intCast(u32, index_val.toUnsignedInt(target)); return tupleField(sema, block, indexable_src, indexable, elem_index_src, index); }, else => unreachable, @@ -17056,9 +17178,10 @@ fn elemValArray( const maybe_undef_array_val = try sema.resolveMaybeUndefVal(block, array_src, array); // index must be defined since it can access out of bounds const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); + const target = sema.mod.getTarget(); if (maybe_index_val) |index_val| { - const index = @intCast(usize, index_val.toUnsignedInt()); + const index = @intCast(usize, index_val.toUnsignedInt(target)); if (index >= array_len_s) { const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else ""; return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); @@ -17069,7 +17192,7 @@ fn elemValArray( return sema.addConstUndef(elem_ty); } if (maybe_index_val) |index_val| { - const index = @intCast(usize, index_val.toUnsignedInt()); + const index = @intCast(usize, index_val.toUnsignedInt(target)); const elem_val = try array_val.elemValue(sema.arena, index); return sema.addConstant(elem_ty, elem_val); } @@ -17114,7 +17237,7 @@ fn elemPtrArray( 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 index = @intCast(usize, index_val.toUnsignedInt(target)); if (index >= array_len_s) { const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else ""; return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); @@ -17125,8 +17248,8 @@ fn elemPtrArray( return sema.addConstUndef(elem_ptr_ty); } if (maybe_index_val) |index_val| { - const index = @intCast(usize, index_val.toUnsignedInt()); - const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, index); + const index = @intCast(usize, index_val.toUnsignedInt(target)); + const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, index, target); return sema.addConstant(elem_ptr_ty, elem_ptr); } } @@ -17162,16 +17285,17 @@ fn elemValSlice( const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice); // index must be defined since it can index out of bounds const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); + const target = sema.mod.getTarget(); if (maybe_slice_val) |slice_val| { runtime_src = elem_index_src; - const slice_len = slice_val.sliceLen(); + const slice_len = slice_val.sliceLen(target); const slice_len_s = slice_len + @boolToInt(slice_sent); if (slice_len_s == 0) { return sema.fail(block, elem_index_src, "indexing into empty slice", .{}); } if (maybe_index_val) |index_val| { - const index = @intCast(usize, index_val.toUnsignedInt()); + const index = @intCast(usize, index_val.toUnsignedInt(target)); if (index >= slice_len_s) { const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); @@ -17192,7 +17316,7 @@ fn elemValSlice( try sema.requireRuntimeBlock(block, runtime_src); if (block.wantSafety()) { const len_inst = if (maybe_slice_val) |slice_val| - try sema.addIntUnsigned(Type.usize, slice_val.sliceLen()) + try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(target)) else try block.addTyOp(.slice_len, Type.usize, slice); const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; @@ -17223,18 +17347,18 @@ fn elemPtrSlice( if (slice_val.isUndef()) { return sema.addConstUndef(elem_ptr_ty); } - const slice_len = slice_val.sliceLen(); + const slice_len = slice_val.sliceLen(target); const slice_len_s = slice_len + @boolToInt(slice_sent); if (slice_len_s == 0) { return sema.fail(block, elem_index_src, "indexing into empty slice", .{}); } if (maybe_index_val) |index_val| { - const index = @intCast(usize, index_val.toUnsignedInt()); + const index = @intCast(usize, index_val.toUnsignedInt(target)); if (index >= slice_len_s) { const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); } - const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index); + const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index, target); return sema.addConstant(elem_ptr_ty, elem_ptr_val); } } @@ -17245,7 +17369,7 @@ fn elemPtrSlice( const len_inst = len: { if (maybe_undef_slice_val) |slice_val| if (!slice_val.isUndef()) - break :len try sema.addIntUnsigned(Type.usize, slice_val.sliceLen()); + break :len try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(target)); break :len try block.addTyOp(.slice_len, Type.usize, slice); }; const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; @@ -17270,12 +17394,12 @@ fn coerce( const dest_ty_src = inst_src; // TODO better source location const dest_ty = try sema.resolveTypeFields(block, dest_ty_src, dest_ty_unresolved); const inst_ty = try sema.resolveTypeFields(block, inst_src, sema.typeOf(inst)); + const target = sema.mod.getTarget(); // If the types are the same, we can return the operand. - if (dest_ty.eql(inst_ty)) + if (dest_ty.eql(inst_ty, target)) return inst; const arena = sema.arena; - const target = sema.mod.getTarget(); const maybe_inst_val = try sema.resolveMaybeUndefVal(block, inst_src, inst); const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src); @@ -17379,7 +17503,7 @@ fn coerce( // *[N:s]T to [*]T if (dest_info.sentinel) |dst_sentinel| { if (array_ty.sentinel()) |src_sentinel| { - if (src_sentinel.eql(dst_sentinel, dst_elem_type)) { + if (src_sentinel.eql(dst_sentinel, dst_elem_type, target)) { return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); } } @@ -17448,7 +17572,7 @@ fn coerce( } if (inst_info.size == .Slice) { if (dest_info.sentinel == null or inst_info.sentinel == null or - !dest_info.sentinel.?.eql(inst_info.sentinel.?, dest_info.pointee_type)) + !dest_info.sentinel.?.eql(inst_info.sentinel.?, dest_info.pointee_type, target)) break :p; const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); @@ -17515,7 +17639,7 @@ fn coerce( } if (dest_info.sentinel == null or inst_info.sentinel == null or - !dest_info.sentinel.?.eql(inst_info.sentinel.?, dest_info.pointee_type)) + !dest_info.sentinel.?.eql(inst_info.sentinel.?, dest_info.pointee_type, target)) break :p; const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); @@ -17528,11 +17652,11 @@ fn coerce( const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :float; if (val.floatHasFraction()) { - return sema.fail(block, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val.fmtValue(inst_ty), dest_ty }); + return sema.fail(block, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val.fmtValue(inst_ty, target), dest_ty.fmt(target) }); } const result_val = val.floatToInt(sema.arena, inst_ty, dest_ty, target) catch |err| switch (err) { error.FloatCannotFit => { - return sema.fail(block, inst_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty }); + return sema.fail(block, inst_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty.fmt(target) }); }, else => |e| return e, }; @@ -17542,7 +17666,7 @@ fn coerce( if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| { // comptime known integer to other number if (!val.intFitsInType(dest_ty, target)) { - return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_ty, val.fmtValue(inst_ty) }); + return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_ty.fmt(target), val.fmtValue(inst_ty, target) }); } return try sema.addConstant(dest_ty, val); } @@ -17572,12 +17696,12 @@ fn coerce( .Float => { if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| { const result_val = try val.floatCast(sema.arena, dest_ty, target); - if (!val.eql(result_val, dest_ty)) { + if (!val.eql(result_val, dest_ty, target)) { return sema.fail( block, inst_src, "type {} cannot represent float value {}", - .{ dest_ty, val.fmtValue(inst_ty) }, + .{ dest_ty.fmt(target), val.fmtValue(inst_ty, target) }, ); } return try sema.addConstant(dest_ty, result_val); @@ -17596,12 +17720,12 @@ fn coerce( const result_val = try val.intToFloat(sema.arena, inst_ty, dest_ty, target); // TODO implement this compile error //const int_again_val = try result_val.floatToInt(sema.arena, inst_ty); - //if (!int_again_val.eql(val, inst_ty)) { + //if (!int_again_val.eql(val, inst_ty, target)) { // return sema.fail( // block, // inst_src, // "type {} cannot represent integer value {}", - // .{ dest_ty, val }, + // .{ dest_ty.fmt(target), val }, // ); //} return try sema.addConstant(dest_ty, result_val); @@ -17622,7 +17746,7 @@ fn coerce( block, inst_src, "enum '{}' has no field named '{s}'", - .{ dest_ty, bytes }, + .{ dest_ty.fmt(target), bytes }, ); errdefer msg.destroy(sema.gpa); try sema.mod.errNoteNonLazy( @@ -17643,7 +17767,7 @@ fn coerce( .Union => blk: { // union to its own tag type const union_tag_ty = inst_ty.unionTagType() orelse break :blk; - if (union_tag_ty.eql(dest_ty)) { + if (union_tag_ty.eql(dest_ty, target)) { return sema.unionToTag(block, dest_ty, inst, inst_src); } }, @@ -17743,7 +17867,7 @@ fn coerce( return sema.addConstUndef(dest_ty); } - return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty, inst_ty }); + return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty.fmt(target), inst_ty.fmt(target) }); } const InMemoryCoercionResult = enum { @@ -17772,7 +17896,7 @@ fn coerceInMemoryAllowed( dest_src: LazySrcLoc, src_src: LazySrcLoc, ) CompileError!InMemoryCoercionResult { - if (dest_ty.eql(src_ty)) + if (dest_ty.eql(src_ty, target)) return .ok; // Pointers / Pointer-like Optionals @@ -17823,7 +17947,7 @@ fn coerceInMemoryAllowed( } const ok_sent = dest_info.sentinel == null or (src_info.sentinel != null and - dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.elem_type)); + dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.elem_type, target)); if (!ok_sent) { return .no_match; } @@ -18050,7 +18174,7 @@ fn coerceInMemoryAllowedPtrs( const ok_sent = dest_info.sentinel == null or src_info.size == .C or (src_info.sentinel != null and - dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type)); + dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type, target)); if (!ok_sent) { return .no_match; } @@ -18091,7 +18215,7 @@ fn coerceInMemoryAllowedPtrs( // resolved and we compare the alignment numerically. alignment: { if (src_info.@"align" == 0 and dest_info.@"align" == 0 and - dest_info.pointee_type.eql(src_info.pointee_type)) + dest_info.pointee_type.eql(src_info.pointee_type, target)) { break :alignment; } @@ -18246,7 +18370,8 @@ fn obtainBitCastedVectorPtr(sema: *Sema, ptr: Air.Inst.Ref) ?Air.Inst.Ref { // We have a pointer-to-array and a pointer-to-vector. If the elements and // lengths match, return the result. const vector_ty = sema.typeOf(prev_ptr).childType(); - if (array_ty.childType().eql(vector_ty.childType()) and + const target = sema.mod.getTarget(); + if (array_ty.childType().eql(vector_ty.childType(), target) and array_ty.arrayLen() == vector_ty.vectorLen()) { return prev_ptr; @@ -18668,10 +18793,10 @@ fn beginComptimePtrLoad( if (maybe_array_ty) |load_ty| { // It's possible that we're loading a [N]T, in which case we'd like to slice // the pointee array directly from our parent array. - if (load_ty.isArrayLike() and load_ty.childType().eql(elem_ty)) { + if (load_ty.isArrayLike() and load_ty.childType().eql(elem_ty, target)) { const N = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel()); deref.pointee = if (elem_ptr.index + N <= check_len) TypedValue{ - .ty = try Type.array(sema.arena, N, null, elem_ty), + .ty = try Type.array(sema.arena, N, null, elem_ty, target), .val = try array_tv.val.sliceArray(sema.arena, elem_ptr.index, elem_ptr.index + N), } else null; break :blk deref; @@ -18807,11 +18932,11 @@ pub fn bitCastVal( new_ty: Type, buffer_offset: usize, ) !Value { - if (old_ty.eql(new_ty)) return val; + const target = sema.mod.getTarget(); + if (old_ty.eql(new_ty, target)) return val; // For types with well-defined memory layouts, we serialize them a byte buffer, // then deserialize to the new type. - const target = sema.mod.getTarget(); const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(target)); const buffer = try sema.gpa.alloc(u8, abi_size); defer sema.gpa.free(buffer); @@ -18864,11 +18989,12 @@ fn coerceEnumToUnion( inst_src: LazySrcLoc, ) !Air.Inst.Ref { const inst_ty = sema.typeOf(inst); + const target = sema.mod.getTarget(); const tag_ty = union_ty.unionTagType() orelse { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{ - union_ty, inst_ty, + union_ty.fmt(target), inst_ty.fmt(target), }); errdefer msg.destroy(sema.gpa); try sema.errNote(block, union_ty_src, msg, "cannot coerce enum to untagged union", .{}); @@ -18881,10 +19007,10 @@ fn coerceEnumToUnion( const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src); if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| { const union_obj = union_ty.cast(Type.Payload.Union).?.data; - const field_index = union_obj.tag_ty.enumTagFieldIndex(val) orelse { + const field_index = union_obj.tag_ty.enumTagFieldIndex(val, target) orelse { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "union {} has no tag with value {}", .{ - union_ty, val.fmtValue(tag_ty), + union_ty.fmt(target), val.fmtValue(tag_ty, target), }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, union_ty); @@ -18899,7 +19025,7 @@ fn coerceEnumToUnion( // also instead of 'union declared here' make it 'field "foo" declared here'. const msg = msg: { const msg = try sema.errMsg(block, inst_src, "coercion to union {} must initialize {} field", .{ - union_ty, field_ty, + union_ty.fmt(target), field_ty.fmt(target), }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, union_ty); @@ -18919,7 +19045,7 @@ fn coerceEnumToUnion( if (tag_ty.isNonexhaustiveEnum()) { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "runtime coercion to union {} from non-exhaustive enum", .{ - union_ty, + union_ty.fmt(target), }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, tag_ty); @@ -18937,7 +19063,7 @@ fn coerceEnumToUnion( // instead of the "union declared here" hint const msg = msg: { const msg = try sema.errMsg(block, inst_src, "runtime coercion to union {} which has non-void fields", .{ - union_ty, + union_ty.fmt(target), }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, union_ty); @@ -19020,11 +19146,12 @@ fn coerceArrayLike( const inst_ty = sema.typeOf(inst); const inst_len = inst_ty.arrayLen(); const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen()); + const target = sema.mod.getTarget(); if (dest_len != inst_len) { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{ - dest_ty, inst_ty, + dest_ty.fmt(target), inst_ty.fmt(target), }); errdefer msg.destroy(sema.gpa); try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len}); @@ -19034,7 +19161,6 @@ fn coerceArrayLike( return sema.failWithOwnedErrorMsg(block, msg); } - const target = sema.mod.getTarget(); const dest_elem_ty = dest_ty.childType(); const inst_elem_ty = inst_ty.childType(); const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, false, target, dest_ty_src, inst_src); @@ -19092,11 +19218,12 @@ fn coerceTupleToArray( const inst_ty = sema.typeOf(inst); const inst_len = inst_ty.arrayLen(); const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen()); + const target = sema.mod.getTarget(); if (dest_len != inst_len) { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{ - dest_ty, inst_ty, + dest_ty.fmt(target), inst_ty.fmt(target), }); errdefer msg.destroy(sema.gpa); try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len}); @@ -19149,7 +19276,8 @@ fn coerceTupleToSlicePtrs( const tuple_ty = sema.typeOf(ptr_tuple).childType(); const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src); const slice_info = slice_ty.ptrInfo().data; - const array_ty = try Type.array(sema.arena, tuple_ty.structFieldCount(), slice_info.sentinel, slice_info.pointee_type); + const target = sema.mod.getTarget(); + const array_ty = try Type.array(sema.arena, tuple_ty.structFieldCount(), slice_info.sentinel, slice_info.pointee_type, target); const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src); if (slice_info.@"align" != 0) { return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{}); @@ -19398,10 +19526,11 @@ fn analyzeLoad( ptr: Air.Inst.Ref, ptr_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { + const target = sema.mod.getTarget(); const ptr_ty = sema.typeOf(ptr); const elem_ty = switch (ptr_ty.zigTypeTag()) { .Pointer => ptr_ty.childType(), - else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty}), + else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(target)}), }; if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { if (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) |elem_val| { @@ -19440,7 +19569,8 @@ fn analyzeSliceLen( if (slice_val.isUndef()) { return sema.addConstUndef(Type.usize); } - return sema.addIntUnsigned(Type.usize, slice_val.sliceLen()); + const target = sema.mod.getTarget(); + return sema.addIntUnsigned(Type.usize, slice_val.sliceLen(target)); } try sema.requireRuntimeBlock(block, src); return block.addTyOp(.slice_len, Type.usize, slice_inst); @@ -19522,9 +19652,10 @@ fn analyzeSlice( // Slice expressions can operate on a variable whose type is an array. This requires // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer. const ptr_ptr_ty = sema.typeOf(ptr_ptr); + const target = sema.mod.getTarget(); const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag()) { .Pointer => ptr_ptr_ty.elemType(), - else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ptr_ty}), + else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ptr_ty.fmt(target)}), }; var array_ty = ptr_ptr_child_ty; @@ -19564,7 +19695,7 @@ fn analyzeSlice( elem_ty = ptr_ptr_child_ty.childType(); }, }, - else => return sema.fail(block, ptr_src, "slice of non-array type '{}'", .{ptr_ptr_child_ty}), + else => return sema.fail(block, ptr_src, "slice of non-array type '{}'", .{ptr_ptr_child_ty.fmt(target)}), } const ptr = if (slice_ty.isSlice()) @@ -19587,7 +19718,7 @@ fn analyzeSlice( if (!end_is_len) { const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); if (try sema.resolveMaybeUndefVal(block, end_src, end)) |end_val| { - if (end_val.compare(.gt, len_val, Type.usize)) { + if (end_val.compare(.gt, len_val, Type.usize, target)) { return sema.fail( block, end_src, @@ -19595,7 +19726,7 @@ fn analyzeSlice( .{ end_val.fmtValue(Type.usize), len_val.fmtValue(Type.usize) }, ); } - if (end_val.eql(len_val, Type.usize)) { + if (end_val.eql(len_val, Type.usize, target)) { end_is_len = true; } } @@ -19610,10 +19741,10 @@ fn analyzeSlice( if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| { var int_payload: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, - .data = slice_val.sliceLen(), + .data = slice_val.sliceLen(target), }; const slice_len_val = Value.initPayload(&int_payload.base); - if (end_val.compare(.gt, slice_len_val, Type.usize)) { + if (end_val.compare(.gt, slice_len_val, Type.usize, target)) { return sema.fail( block, end_src, @@ -19621,7 +19752,7 @@ fn analyzeSlice( .{ end_val.fmtValue(Type.usize), slice_len_val.fmtValue(Type.usize) }, ); } - if (end_val.eql(slice_len_val, Type.usize)) { + if (end_val.eql(slice_len_val, Type.usize, target)) { end_is_len = true; } } @@ -19670,13 +19801,12 @@ fn analyzeSlice( const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data; const new_allowzero = new_ptr_ty_info.@"allowzero" and sema.typeOf(ptr).ptrSize() != .C; - const target = sema.mod.getTarget(); if (opt_new_len_val) |new_len_val| { - const new_len_int = new_len_val.toUnsignedInt(); + const new_len_int = new_len_val.toUnsignedInt(target); const return_ty = try Type.ptr(sema.arena, target, .{ - .pointee_type = try Type.array(sema.arena, new_len_int, sentinel, elem_ty), + .pointee_type = try Type.array(sema.arena, new_len_int, sentinel, elem_ty, target), .sentinel = null, .@"align" = new_ptr_ty_info.@"align", .@"addrspace" = new_ptr_ty_info.@"addrspace", @@ -19746,6 +19876,7 @@ fn cmpNumeric( const lhs_ty_tag = lhs_ty.zigTypeTag(); const rhs_ty_tag = rhs_ty.zigTypeTag(); + const target = sema.mod.getTarget(); const runtime_src: LazySrcLoc = src: { if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { @@ -19760,7 +19891,7 @@ fn cmpNumeric( return Air.Inst.Ref.bool_false; } } - if (Value.compareHetero(lhs_val, op, rhs_val)) { + if (Value.compareHetero(lhs_val, op, rhs_val, target)) { return Air.Inst.Ref.bool_true; } else { return Air.Inst.Ref.bool_false; @@ -19789,7 +19920,6 @@ fn cmpNumeric( .Float, .ComptimeFloat => true, else => false, }; - const target = sema.mod.getTarget(); if (lhs_is_float and rhs_is_float) { // Implicit cast the smaller one to the larger one. const dest_ty = x: { @@ -19846,7 +19976,7 @@ fn cmpNumeric( } if (lhs_is_float) { var bigint_space: Value.BigIntSpace = undefined; - var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(sema.gpa); + var bigint = try lhs_val.toBigInt(&bigint_space, target).toManaged(sema.gpa); defer bigint.deinit(); if (lhs_val.floatHasFraction()) { switch (op) { @@ -19892,7 +20022,7 @@ fn cmpNumeric( } if (rhs_is_float) { var bigint_space: Value.BigIntSpace = undefined; - var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(sema.gpa); + var bigint = try rhs_val.toBigInt(&bigint_space, target).toManaged(sema.gpa); defer bigint.deinit(); if (rhs_val.floatHasFraction()) { switch (op) { @@ -19950,6 +20080,7 @@ fn cmpVector( try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); const result_ty = try Type.vector(sema.arena, lhs_ty.vectorLen(), Type.@"bool"); + const target = sema.mod.getTarget(); const runtime_src: LazySrcLoc = src: { if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { @@ -19957,7 +20088,7 @@ fn cmpVector( if (lhs_val.isUndef() or rhs_val.isUndef()) { return sema.addConstUndef(result_ty); } - const cmp_val = try lhs_val.compareVector(op, rhs_val, lhs_ty, sema.arena); + const cmp_val = try lhs_val.compareVector(op, rhs_val, lhs_ty, sema.arena, target); return sema.addConstant(result_ty, cmp_val); } else { break :src rhs_src; @@ -20108,7 +20239,7 @@ fn resolvePeerTypes( const candidate_ty_tag = try candidate_ty.zigTypeTagOrPoison(); const chosen_ty_tag = try chosen_ty.zigTypeTagOrPoison(); - if (candidate_ty.eql(chosen_ty)) + if (candidate_ty.eql(chosen_ty, target)) continue; switch (candidate_ty_tag) { @@ -20522,14 +20653,17 @@ fn resolvePeerTypes( ); const msg = msg: { - const msg = try sema.errMsg(block, src, "incompatible types: '{}' and '{}'", .{ chosen_ty, candidate_ty }); + const msg = try sema.errMsg(block, src, "incompatible types: '{}' and '{}'", .{ + chosen_ty.fmt(target), + candidate_ty.fmt(target), + }); errdefer msg.destroy(sema.gpa); if (chosen_src) |src_loc| - try sema.errNote(block, src_loc, msg, "type '{}' here", .{chosen_ty}); + try sema.errNote(block, src_loc, msg, "type '{}' here", .{chosen_ty.fmt(target)}); if (candidate_src) |src_loc| - try sema.errNote(block, src_loc, msg, "type '{}' here", .{candidate_ty}); + try sema.errNote(block, src_loc, msg, "type '{}' here", .{candidate_ty.fmt(target)}); break :msg msg; }; @@ -20557,7 +20691,7 @@ fn resolvePeerTypes( else new_ptr_ty; const set_ty = err_set_ty orelse return opt_ptr_ty; - return try Module.errorUnionType(sema.arena, set_ty, opt_ptr_ty); + return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, target); } if (seen_const) { @@ -20573,7 +20707,7 @@ fn resolvePeerTypes( else new_ptr_ty; const set_ty = err_set_ty orelse chosen_ty.errorUnionSet(); - return try Module.errorUnionType(sema.arena, set_ty, opt_ptr_ty); + return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, target); }, .Pointer => { var info = chosen_ty.ptrInfo(); @@ -20584,7 +20718,7 @@ fn resolvePeerTypes( else new_ptr_ty; const set_ty = err_set_ty orelse return opt_ptr_ty; - return try Module.errorUnionType(sema.arena, set_ty, opt_ptr_ty); + return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, target); }, else => return chosen_ty, } @@ -20596,16 +20730,16 @@ fn resolvePeerTypes( else => try Type.optional(sema.arena, chosen_ty), }; const set_ty = err_set_ty orelse return opt_ty; - return try Module.errorUnionType(sema.arena, set_ty, opt_ty); + return try Type.errorUnion(sema.arena, set_ty, opt_ty, target); } if (err_set_ty) |ty| switch (chosen_ty.zigTypeTag()) { .ErrorSet => return ty, .ErrorUnion => { const payload_ty = chosen_ty.errorUnionPayload(); - return try Module.errorUnionType(sema.arena, ty, payload_ty); + return try Type.errorUnion(sema.arena, ty, payload_ty, target); }, - else => return try Module.errorUnionType(sema.arena, ty, chosen_ty), + else => return try Type.errorUnion(sema.arena, ty, chosen_ty, target), }; return chosen_ty; @@ -20662,11 +20796,12 @@ fn resolveStructLayout( ) CompileError!void { const resolved_ty = try sema.resolveTypeFields(block, src, ty); if (resolved_ty.castTag(.@"struct")) |payload| { + const target = sema.mod.getTarget(); const struct_obj = payload.data; switch (struct_obj.status) { .none, .have_field_types => {}, .field_types_wip, .layout_wip => { - return sema.fail(block, src, "struct {} depends on itself", .{ty}); + return sema.fail(block, src, "struct {} depends on itself", .{ty.fmt(target)}); }, .have_layout, .fully_resolved_wip, .fully_resolved => return, } @@ -20694,10 +20829,11 @@ fn resolveUnionLayout( ) CompileError!void { const resolved_ty = try sema.resolveTypeFields(block, src, ty); const union_obj = resolved_ty.cast(Type.Payload.Union).?.data; + const target = sema.mod.getTarget(); switch (union_obj.status) { .none, .have_field_types => {}, .field_types_wip, .layout_wip => { - return sema.fail(block, src, "union {} depends on itself", .{ty}); + return sema.fail(block, src, "union {} depends on itself", .{ty.fmt(target)}); }, .have_layout, .fully_resolved_wip, .fully_resolved => return, } @@ -20828,10 +20964,11 @@ fn resolveTypeFieldsStruct( ty: Type, struct_obj: *Module.Struct, ) CompileError!void { + const target = sema.mod.getTarget(); switch (struct_obj.status) { .none => {}, .field_types_wip => { - return sema.fail(block, src, "struct {} depends on itself", .{ty}); + return sema.fail(block, src, "struct {} depends on itself", .{ty.fmt(target)}); }, .have_field_types, .have_layout, @@ -20858,10 +20995,11 @@ fn resolveTypeFieldsUnion( ty: Type, union_obj: *Module.Union, ) CompileError!void { + const target = sema.mod.getTarget(); switch (union_obj.status) { .none => {}, .field_types_wip => { - return sema.fail(block, src, "union {} depends on itself", .{ty}); + return sema.fail(block, src, "union {} depends on itself", .{ty.fmt(target)}); }, .have_field_types, .have_layout, @@ -21218,6 +21356,8 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { enum_field_names = &union_obj.tag_ty.castTag(.enum_simple).?.data.fields; } + const target = sema.mod.getTarget(); + const bits_per_field = 4; const fields_per_u32 = 32 / bits_per_field; const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; @@ -21275,16 +21415,22 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { // This puts the memory into the union arena, not the enum arena, but // it is OK since they share the same lifetime. const copied_val = try val.copy(decl_arena_allocator); - map.putAssumeCapacityContext(copied_val, {}, .{ .ty = int_tag_ty }); + map.putAssumeCapacityContext(copied_val, {}, .{ + .ty = int_tag_ty, + .target = target, + }); } else { const val = if (last_tag_val) |val| - try val.intAdd(Value.one, int_tag_ty, sema.arena) + try val.intAdd(Value.one, int_tag_ty, sema.arena, target) else Value.zero; last_tag_val = val; const copied_val = try val.copy(decl_arena_allocator); - map.putAssumeCapacityContext(copied_val, {}, .{ .ty = int_tag_ty }); + map.putAssumeCapacityContext(copied_val, {}, .{ + .ty = int_tag_ty, + .target = target, + }); } } @@ -21359,7 +21505,10 @@ fn generateUnionTagTypeNumbered( }; // Here we pre-allocate the maps using the decl arena. try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); - try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{ .ty = int_ty }); + try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{ + .ty = int_ty, + .target = sema.mod.getTarget(), + }); try new_decl.finalizeNewArena(&new_decl_arena); return enum_ty; } @@ -21962,7 +22111,7 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr // The type is not in-memory coercible or the direct dereference failed, so it must // be bitcast according to the pointer type we are performing the load through. if (!load_ty.hasWellDefinedLayout()) - return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{load_ty}); + return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{load_ty.fmt(target)}); const load_sz = try sema.typeAbiSize(block, src, load_ty); @@ -21977,11 +22126,11 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr if (deref.ty_without_well_defined_layout) |bad_ty| { // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem // is that some type we encountered when de-referencing does not have a well-defined layout. - return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{bad_ty}); + return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{bad_ty.fmt(target)}); } else { // If all encountered types had well-defined layouts, the parent is the root decl and it just // wasn't big enough for the load. - return sema.fail(block, src, "dereference of {} exceeds bounds of containing decl of type {}", .{ ptr_ty, deref.parent.?.tv.ty }); + return sema.fail(block, src, "dereference of {} exceeds bounds of containing decl of type {}", .{ ptr_ty.fmt(target), deref.parent.?.tv.ty.fmt(target) }); } } @@ -22344,7 +22493,8 @@ fn anonStructFieldIndex( return @intCast(u32, i); } } + const target = sema.mod.getTarget(); return sema.fail(block, field_src, "anonymous struct {} has no such field '{s}'", .{ - struct_ty, field_name, + struct_ty.fmt(target), field_name, }); } diff --git a/src/TypedValue.zig b/src/TypedValue.zig index fc5ef45991..b4be670e19 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -3,6 +3,7 @@ const Type = @import("type.zig").Type; const Value = @import("value.zig").Value; const Allocator = std.mem.Allocator; const TypedValue = @This(); +const Target = std.Target; ty: Type, val: Value, @@ -30,13 +31,13 @@ pub fn copy(self: TypedValue, arena: Allocator) error{OutOfMemory}!TypedValue { }; } -pub fn eql(a: TypedValue, b: TypedValue) bool { - if (!a.ty.eql(b.ty)) return false; - return a.val.eql(b.val, a.ty); +pub fn eql(a: TypedValue, b: TypedValue, target: std.Target) bool { + if (!a.ty.eql(b.ty, target)) return false; + return a.val.eql(b.val, a.ty, target); } -pub fn hash(tv: TypedValue, hasher: *std.hash.Wyhash) void { - return tv.val.hash(tv.ty, hasher); +pub fn hash(tv: TypedValue, hasher: *std.hash.Wyhash, target: std.Target) void { + return tv.val.hash(tv.ty, hasher, target); } pub fn enumToInt(tv: TypedValue, buffer: *Value.Payload.U64) Value { @@ -45,21 +46,28 @@ pub fn enumToInt(tv: TypedValue, buffer: *Value.Payload.U64) Value { const max_aggregate_items = 100; -pub fn format( +const FormatContext = struct { tv: TypedValue, + target: Target, +}; + +pub fn format( + ctx: FormatContext, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype, ) !void { + _ = options; comptime std.debug.assert(fmt.len == 0); - return tv.print(options, writer, 3); + return ctx.tv.print(writer, 3, ctx.target); } +/// Prints the Value according to the Type, not according to the Value Tag. pub fn print( tv: TypedValue, - options: std.fmt.FormatOptions, writer: anytype, level: u8, + target: std.Target, ) @TypeOf(writer).Error!void { var val = tv.val; var ty = tv.ty; @@ -148,7 +156,7 @@ pub fn print( try print(.{ .ty = fields[i].ty, .val = vals[i], - }, options, writer, level - 1); + }, writer, level - 1, target); } return writer.writeAll(" }"); } else { @@ -162,7 +170,7 @@ pub fn print( try print(.{ .ty = elem_ty, .val = vals[i], - }, options, writer, level - 1); + }, writer, level - 1, target); } return writer.writeAll(" }"); } @@ -177,12 +185,12 @@ pub fn print( try print(.{ .ty = ty.unionTagType().?, .val = union_val.tag, - }, options, writer, level - 1); + }, writer, level - 1, target); try writer.writeAll(" = "); try print(.{ - .ty = ty.unionFieldType(union_val.tag), + .ty = ty.unionFieldType(union_val.tag, target), .val = union_val.val, - }, options, writer, level - 1); + }, writer, level - 1, target); return writer.writeAll(" }"); }, @@ -197,7 +205,7 @@ pub fn print( }, .bool_true => return writer.writeAll("true"), .bool_false => return writer.writeAll("false"), - .ty => return val.castTag(.ty).?.data.format("", options, writer), + .ty => return val.castTag(.ty).?.data.print(writer, target), .int_type => { const int_type = val.castTag(.int_type).?.data; return writer.print("{s}{d}", .{ @@ -205,10 +213,15 @@ pub fn print( int_type.bits, }); }, - .int_u64 => return std.fmt.formatIntValue(val.castTag(.int_u64).?.data, "", options, writer), - .int_i64 => return std.fmt.formatIntValue(val.castTag(.int_i64).?.data, "", options, writer), + .int_u64 => return std.fmt.formatIntValue(val.castTag(.int_u64).?.data, "", .{}, writer), + .int_i64 => return std.fmt.formatIntValue(val.castTag(.int_i64).?.data, "", .{}, writer), .int_big_positive => return writer.print("{}", .{val.castTag(.int_big_positive).?.asBigInt()}), .int_big_negative => return writer.print("{}", .{val.castTag(.int_big_negative).?.asBigInt()}), + .lazy_align => { + const sub_ty = val.castTag(.lazy_align).?.data; + const x = sub_ty.abiAlignment(target); + return writer.print("{d}", .{x}); + }, .function => return writer.print("(function '{s}')", .{val.castTag(.function).?.data.owner_decl.name}), .extern_fn => return writer.writeAll("(extern function)"), .variable => return writer.writeAll("(variable)"), @@ -220,7 +233,7 @@ pub fn print( return print(.{ .ty = decl.ty, .val = decl.val, - }, options, writer, level - 1); + }, writer, level - 1, target); }, .decl_ref => { const decl = val.castTag(.decl_ref).?.data; @@ -230,7 +243,7 @@ pub fn print( return print(.{ .ty = decl.ty, .val = decl.val, - }, options, writer, level - 1); + }, writer, level - 1, target); }, .elem_ptr => { const elem_ptr = val.castTag(.elem_ptr).?.data; @@ -238,7 +251,7 @@ pub fn print( try print(.{ .ty = elem_ptr.elem_ty, .val = elem_ptr.array_ptr, - }, options, writer, level - 1); + }, writer, level - 1, target); return writer.print("[{}]", .{elem_ptr.index}); }, .field_ptr => { @@ -247,7 +260,7 @@ pub fn print( try print(.{ .ty = field_ptr.container_ty, .val = field_ptr.container_ptr, - }, options, writer, level - 1); + }, writer, level - 1, target); if (field_ptr.container_ty.zigTypeTag() == .Struct) { const field_name = field_ptr.container_ty.structFields().keys()[field_ptr.field_index]; @@ -275,7 +288,7 @@ pub fn print( }; while (i < max_aggregate_items) : (i += 1) { if (i != 0) try writer.writeAll(", "); - try print(elem_tv, options, writer, level - 1); + try print(elem_tv, writer, level - 1, target); } return writer.writeAll(" }"); }, @@ -287,7 +300,7 @@ pub fn print( try print(.{ .ty = ty.elemType2(), .val = ty.sentinel().?, - }, options, writer, level - 1); + }, writer, level - 1, target); return writer.writeAll(" }"); }, .slice => return writer.writeAll("(slice)"), diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 9c2d5890a7..8e502e8943 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -796,7 +796,9 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { const index = dbg_out.dbg_info.items.len; try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 - const gop = try dbg_out.dbg_info_type_relocs.getOrPut(self.gpa, ty); + const gop = try dbg_out.dbg_info_type_relocs.getOrPutContext(self.gpa, ty, .{ + .target = self.target.*, + }); if (!gop.found_existing) { gop.value_ptr.* = .{ .off = undefined, @@ -835,8 +837,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { return self.next_stack_offset; } + const target = self.target.*; const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{elem_ty}); + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); }; // TODO swap this for inst.ty.ptrAlign const abi_align = elem_ty.abiAlignment(self.target.*); @@ -845,8 +848,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const elem_ty = self.air.typeOfIndex(inst); + const target = self.target.*; const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{elem_ty}); + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); }; const abi_align = elem_ty.abiAlignment(self.target.*); if (abi_align > self.stack_align) @@ -1372,6 +1376,7 @@ fn binOp( lhs_ty: Type, rhs_ty: Type, ) InnerError!MCValue { + const target = self.target.*; switch (tag) { // Arithmetic operations on integers and floats .add, @@ -1381,7 +1386,7 @@ fn binOp( .Float => return self.fail("TODO binary operations on floats", .{}), .Vector => return self.fail("TODO binary operations on vectors", .{}), .Int => { - assert(lhs_ty.eql(rhs_ty)); + assert(lhs_ty.eql(rhs_ty, target)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 64) { // Only say yes if the operation is @@ -1418,7 +1423,7 @@ fn binOp( switch (lhs_ty.zigTypeTag()) { .Vector => return self.fail("TODO binary operations on vectors", .{}), .Int => { - assert(lhs_ty.eql(rhs_ty)); + assert(lhs_ty.eql(rhs_ty, target)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 64) { // TODO add optimisations for multiplication @@ -1440,7 +1445,7 @@ fn binOp( switch (lhs_ty.zigTypeTag()) { .Vector => return self.fail("TODO binary operations on vectors", .{}), .Int => { - assert(lhs_ty.eql(rhs_ty)); + assert(lhs_ty.eql(rhs_ty, target)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 64) { // TODO implement bitwise operations with immediates @@ -2348,11 +2353,12 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { const ty = self.air.typeOfIndex(inst); const result = self.args[arg_index]; + const target = self.target.*; const mcv = switch (result) { // Copy registers to the stack .register => |reg| blk: { const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{ty}); + return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(target)}); }; const abi_align = ty.abiAlignment(self.target.*); const stack_offset = try self.allocMem(inst, abi_size, abi_align); @@ -3879,7 +3885,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa } fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { - log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty, tv.val.fmtDebug() }); + log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() }); const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| { return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); }; @@ -3907,6 +3913,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { if (typed_value.val.castTag(.decl_ref_mut)) |payload| { return self.lowerDeclRef(typed_value, payload.data.decl); } + const target = self.target.*; switch (typed_value.ty.zigTypeTag()) { .Pointer => switch (typed_value.ty.ptrSize()) { @@ -3916,7 +3923,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { else => { switch (typed_value.val.tag()) { .int_u64 => { - return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; + return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; }, .slice => { return self.lowerUnnamedConst(typed_value); @@ -3935,7 +3942,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { const signed = typed_value.val.toSignedInt(); break :blk @bitCast(u64, signed); }, - .unsigned => typed_value.val.toUnsignedInt(), + .unsigned => typed_value.val.toUnsignedInt(target), }; return MCValue{ .immediate = unsigned }; @@ -4004,20 +4011,20 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { } _ = pl; - return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty}); + return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty.fmtDebug()}); } else { if (!payload_type.hasRuntimeBits()) { // We use the error type directly as the type. return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); } - return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty}); + return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty.fmtDebug()}); } }, .Struct => { return self.lowerUnnamedConst(typed_value); }, - else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}), + else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}), } } diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 0b9f9eb2e2..1654be162a 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -801,8 +801,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { return self.next_stack_offset; } + const target = self.target.*; const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{elem_ty}); + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); }; // TODO swap this for inst.ty.ptrAlign const abi_align = elem_ty.abiAlignment(self.target.*); @@ -811,8 +812,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const elem_ty = self.air.typeOfIndex(inst); + const target = self.target.*; const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{elem_ty}); + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); }; const abi_align = elem_ty.abiAlignment(self.target.*); if (abi_align > self.stack_align) @@ -2195,6 +2197,7 @@ fn binOp( lhs_ty: Type, rhs_ty: Type, ) InnerError!MCValue { + const target = self.target.*; switch (tag) { .add, .sub, @@ -2204,7 +2207,7 @@ fn binOp( .Float => return self.fail("TODO ARM binary operations on floats", .{}), .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), .Int => { - assert(lhs_ty.eql(rhs_ty)); + assert(lhs_ty.eql(rhs_ty, target)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 32) { // Only say yes if the operation is @@ -2245,7 +2248,7 @@ fn binOp( .Float => return self.fail("TODO ARM binary operations on floats", .{}), .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), .Int => { - assert(lhs_ty.eql(rhs_ty)); + assert(lhs_ty.eql(rhs_ty, target)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 32) { // TODO add optimisations for multiplication @@ -2299,7 +2302,7 @@ fn binOp( switch (lhs_ty.zigTypeTag()) { .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), .Int => { - assert(lhs_ty.eql(rhs_ty)); + assert(lhs_ty.eql(rhs_ty, target)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 32) { const lhs_immediate_ok = lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null; @@ -4376,6 +4379,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { if (typed_value.val.castTag(.decl_ref_mut)) |payload| { return self.lowerDeclRef(typed_value, payload.data.decl); } + const target = self.target.*; switch (typed_value.ty.zigTypeTag()) { .Array => { @@ -4388,7 +4392,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { else => { switch (typed_value.val.tag()) { .int_u64 => { - return MCValue{ .immediate = @intCast(u32, typed_value.val.toUnsignedInt()) }; + return MCValue{ .immediate = @intCast(u32, typed_value.val.toUnsignedInt(target)) }; }, .slice => { return self.lowerUnnamedConst(typed_value); @@ -4407,7 +4411,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { const signed = @intCast(i32, typed_value.val.toSignedInt()); break :blk @bitCast(u32, signed); }, - .unsigned => @intCast(u32, typed_value.val.toUnsignedInt()), + .unsigned => @intCast(u32, typed_value.val.toUnsignedInt(target)), }; return MCValue{ .immediate = unsigned }; @@ -4476,20 +4480,20 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { } _ = pl; - return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty}); + return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty.fmtDebug()}); } else { if (!payload_type.hasRuntimeBits()) { // We use the error type directly as the type. return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); } - return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty}); + return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty.fmtDebug()}); } }, .Struct => { return self.lowerUnnamedConst(typed_value); }, - else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}), + else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}), } } diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index c5aaeb4612..1e03c4649b 100644 --- a/src/arch/arm/Emit.zig +++ b/src/arch/arm/Emit.zig @@ -384,7 +384,7 @@ fn addDbgInfoTypeReloc(self: *Emit, ty: Type) !void { const index = dbg_out.dbg_info.items.len; try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 - const gop = try dbg_out.dbg_info_type_relocs.getOrPut(self.bin_file.allocator, ty); + const gop = try dbg_out.dbg_info_type_relocs.getOrPutContext(self.bin_file.allocator, ty, .{ .target = self.target.* }); if (!gop.found_existing) { gop.value_ptr.* = .{ .off = undefined, @@ -404,6 +404,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { const ty = self.function.air.instructions.items(.data)[inst].ty; const name = self.function.mod_fn.getParamName(arg_index); const name_with_null = name.ptr[0 .. name.len + 1]; + const target = self.target.*; switch (mcv) { .register => |reg| { @@ -429,7 +430,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { switch (self.debug_output) { .dwarf => |dbg_out| { const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{ty}); + return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(target)}); }; const adjusted_stack_offset = switch (mcv) { .stack_offset => |offset| math.negateCast(offset + abi_size) catch { diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 3b73fef51f..8cbd26b7a6 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -749,7 +749,9 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { const index = dbg_out.dbg_info.items.len; try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 - const gop = try dbg_out.dbg_info_type_relocs.getOrPut(self.gpa, ty); + const gop = try dbg_out.dbg_info_type_relocs.getOrPutContext(self.gpa, ty, .{ + .target = self.target.*, + }); if (!gop.found_existing) { gop.value_ptr.* = .{ .off = undefined, @@ -781,8 +783,9 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u /// Use a pointer instruction as the basis for allocating stack memory. fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { const elem_ty = self.air.typeOfIndex(inst).elemType(); + const target = self.target.*; const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{elem_ty}); + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); }; // TODO swap this for inst.ty.ptrAlign const abi_align = elem_ty.abiAlignment(self.target.*); @@ -791,8 +794,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const elem_ty = self.air.typeOfIndex(inst); + const target = self.target.*; const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{elem_ty}); + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); }; const abi_align = elem_ty.abiAlignment(self.target.*); if (abi_align > self.stack_align) @@ -1048,7 +1052,7 @@ fn binOp( .Float => return self.fail("TODO binary operations on floats", .{}), .Vector => return self.fail("TODO binary operations on vectors", .{}), .Int => { - assert(lhs_ty.eql(rhs_ty)); + assert(lhs_ty.eql(rhs_ty, self.target.*)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 64) { // TODO immediate operands @@ -1778,7 +1782,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); const ty = self.air.typeOf(bin_op.lhs); - assert(ty.eql(self.air.typeOf(bin_op.rhs))); + assert(ty.eql(self.air.typeOf(bin_op.rhs), self.target.*)); if (ty.zigTypeTag() == .ErrorSet) return self.fail("TODO implement cmp for errors", .{}); @@ -2531,6 +2535,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { if (typed_value.val.castTag(.decl_ref_mut)) |payload| { return self.lowerDeclRef(typed_value, payload.data.decl); } + const target = self.target.*; const ptr_bits = self.target.cpu.arch.ptrBitWidth(); switch (typed_value.ty.zigTypeTag()) { .Pointer => switch (typed_value.ty.ptrSize()) { @@ -2538,7 +2543,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { var buf: Type.SlicePtrFieldTypeBuffer = undefined; const ptr_type = typed_value.ty.slicePtrFieldType(&buf); const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val }); - const slice_len = typed_value.val.sliceLen(); + const slice_len = typed_value.val.sliceLen(target); // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean // the Sema code needs to use anonymous Decls or alloca instructions to store data. const ptr_imm = ptr_mcv.memory; @@ -2549,7 +2554,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { }, else => { if (typed_value.val.tag() == .int_u64) { - return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; + return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; } return self.fail("TODO codegen more kinds of const pointers", .{}); }, @@ -2559,7 +2564,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { if (info.bits > ptr_bits or info.signedness == .signed) { return self.fail("TODO const int bigger than ptr and signed int", .{}); } - return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; + return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; }, .Bool => { return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; @@ -2629,9 +2634,9 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { return self.genTypedValue(.{ .ty = error_type, .val = sub_val }); } - return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty}); + return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty.fmtDebug()}); }, - else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}), + else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}), } } diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 7d55e95f23..eb4004fa2c 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1021,7 +1021,9 @@ fn allocStack(self: *Self, ty: Type) !WValue { } const abi_size = std.math.cast(u32, ty.abiSize(self.target)) catch { - return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ ty, ty.abiSize(self.target) }); + return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ + ty.fmt(self.target), ty.abiSize(self.target), + }); }; const abi_align = ty.abiAlignment(self.target); @@ -1053,7 +1055,9 @@ fn allocStackPtr(self: *Self, inst: Air.Inst.Index) !WValue { const abi_alignment = ptr_ty.ptrAlignment(self.target); const abi_size = std.math.cast(u32, pointee_ty.abiSize(self.target)) catch { - return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ pointee_ty, pointee_ty.abiSize(self.target) }); + return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ + pointee_ty.fmt(self.target), pointee_ty.abiSize(self.target), + }); }; if (abi_alignment > self.stack_alignment) { self.stack_alignment = abi_alignment; @@ -1750,7 +1754,7 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { const operand_ty = self.air.typeOfIndex(inst); if (isByRef(operand_ty, self.target)) { - return self.fail("TODO: Implement binary operation for type: {}", .{operand_ty}); + return self.fail("TODO: Implement binary operation for type: {}", .{operand_ty.fmtDebug()}); } try self.emitWValue(lhs); @@ -1918,6 +1922,8 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl); } + const target = self.target; + switch (ty.zigTypeTag()) { .Int => { const int_info = ty.intInfo(self.target); @@ -1929,13 +1935,13 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { else => unreachable, }, .unsigned => switch (int_info.bits) { - 0...32 => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) }, - 33...64 => return WValue{ .imm64 = val.toUnsignedInt() }, + 0...32 => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(target)) }, + 33...64 => return WValue{ .imm64 = val.toUnsignedInt(target) }, else => unreachable, }, } }, - .Bool => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) }, + .Bool => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(target)) }, .Float => switch (ty.floatBits(self.target)) { 0...32 => return WValue{ .float32 = val.toFloat(f32) }, 33...64 => return WValue{ .float64 = val.toFloat(f64) }, @@ -1945,7 +1951,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { .field_ptr, .elem_ptr => { return self.lowerParentPtr(val, ty.childType()); }, - .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) }, + .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(target)) }, .zero, .null_value => return WValue{ .imm32 = 0 }, else => return self.fail("Wasm TODO: lowerConstant for other const pointer tag {s}", .{val.tag()}), }, @@ -2044,6 +2050,7 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!WValue { /// It's illegal to provide a value with a type that cannot be represented /// as an integer value. fn valueAsI32(self: Self, val: Value, ty: Type) i32 { + const target = self.target; switch (ty.zigTypeTag()) { .Enum => { if (val.castTag(.enum_field_index)) |field_index| { @@ -2071,7 +2078,7 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 { }, .Int => switch (ty.intInfo(self.target).signedness) { .signed => return @truncate(i32, val.toSignedInt()), - .unsigned => return @bitCast(i32, @truncate(u32, val.toUnsignedInt())), + .unsigned => return @bitCast(i32, @truncate(u32, val.toUnsignedInt(target))), }, .ErrorSet => { const kv = self.bin_file.base.options.module.?.getErrorValue(val.getError().?) catch unreachable; // passed invalid `Value` to function @@ -2296,7 +2303,7 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const struct_ty = self.air.typeOf(extra.data.struct_operand).childType(); const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, self.target)) catch { return self.fail("Field type '{}' too big to fit into stack frame", .{ - struct_ty.structFieldType(extra.data.field_index), + struct_ty.structFieldType(extra.data.field_index).fmt(self.target), }); }; return self.structFieldPtr(struct_ptr, offset); @@ -2309,7 +2316,7 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr const field_ty = struct_ty.structFieldType(index); const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch { return self.fail("Field type '{}' too big to fit into stack frame", .{ - field_ty, + field_ty.fmt(self.target), }); }; return self.structFieldPtr(struct_ptr, offset); @@ -2335,7 +2342,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const field_ty = struct_ty.structFieldType(field_index); if (!field_ty.hasRuntimeBits()) return WValue{ .none = {} }; const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch { - return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty}); + return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(self.target)}); }; if (isByRef(field_ty, self.target)) { @@ -2716,7 +2723,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue var buf: Type.Payload.ElemType = undefined; const payload_ty = opt_ty.optionalChild(&buf); if (!payload_ty.hasRuntimeBits()) { - return self.fail("TODO: Implement OptionalPayloadPtrSet for optional with zero-sized type {}", .{payload_ty}); + return self.fail("TODO: Implement OptionalPayloadPtrSet for optional with zero-sized type {}", .{payload_ty.fmtDebug()}); } if (opt_ty.isPtrLikeOptional()) { @@ -2724,7 +2731,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue } const offset = std.math.cast(u32, opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) catch { - return self.fail("Optional type {} too big to fit into stack frame", .{opt_ty}); + return self.fail("Optional type {} too big to fit into stack frame", .{opt_ty.fmt(self.target)}); }; try self.emitWValue(operand); @@ -2753,7 +2760,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return operand; } const offset = std.math.cast(u32, op_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) catch { - return self.fail("Optional type {} too big to fit into stack frame", .{op_ty}); + return self.fail("Optional type {} too big to fit into stack frame", .{op_ty.fmt(self.target)}); }; // Create optional type, set the non-null bit, and store the operand inside the optional type diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 598511a631..f2977977c5 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -892,8 +892,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { return self.allocMem(inst, @sizeOf(usize), @alignOf(usize)); } + const target = self.target.*; const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{elem_ty}); + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); }; // TODO swap this for inst.ty.ptrAlign const abi_align = ptr_ty.ptrAlignment(self.target.*); @@ -902,8 +903,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const elem_ty = self.air.typeOfIndex(inst); + const target = self.target.*; const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{elem_ty}); + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); }; const abi_align = elem_ty.abiAlignment(self.target.*); if (abi_align > self.stack_align) @@ -1142,7 +1144,7 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void { const ty = self.air.typeOfIndex(inst); if (ty.zigTypeTag() != .Int) { - return self.fail("TODO implement min for type {}", .{ty}); + return self.fail("TODO implement min for type {}", .{ty.fmtDebug()}); } const signedness = ty.intInfo(self.target.*).signedness; const result: MCValue = result: { @@ -1676,13 +1678,13 @@ fn airShl(self: *Self, inst: Air.Inst.Index) !void { const ty = self.air.typeOfIndex(inst); const tag = self.air.instructions.items(.tag)[inst]; switch (tag) { - .shl_exact => return self.fail("TODO implement {} for type {}", .{ tag, ty }), + .shl_exact => return self.fail("TODO implement {} for type {}", .{ tag, ty.fmtDebug() }), .shl => {}, else => unreachable, } if (ty.zigTypeTag() != .Int) { - return self.fail("TODO implement .shl for type {}", .{ty}); + return self.fail("TODO implement .shl for type {}", .{ty.fmtDebug()}); } if (ty.abiSize(self.target.*) > 8) { return self.fail("TODO implement .shl for integers larger than 8 bytes", .{}); @@ -5820,7 +5822,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa } fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { - log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty, tv.val.fmtDebug() }); + log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() }); const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| { return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); }; @@ -5850,13 +5852,15 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { return self.lowerDeclRef(typed_value, payload.data.decl); } + const target = self.target.*; + switch (typed_value.ty.zigTypeTag()) { .Pointer => switch (typed_value.ty.ptrSize()) { .Slice => {}, else => { switch (typed_value.val.tag()) { .int_u64 => { - return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; + return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; }, else => {}, } @@ -5868,7 +5872,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { return MCValue{ .immediate = @bitCast(u64, typed_value.val.toSignedInt()) }; } if (!(info.bits > ptr_bits or info.signedness == .signed)) { - return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; + return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; } }, .Bool => { diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 0543496750..3ce77ffb73 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -1118,7 +1118,9 @@ fn addDbgInfoTypeReloc(emit: *Emit, ty: Type) !void { const index = dbg_out.dbg_info.items.len; try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 - const gop = try dbg_out.dbg_info_type_relocs.getOrPut(emit.bin_file.allocator, ty); + const gop = try dbg_out.dbg_info_type_relocs.getOrPutContext(emit.bin_file.allocator, ty, .{ + .target = emit.target.*, + }); if (!gop.found_existing) { gop.value_ptr.* = .{ .off = undefined, diff --git a/src/codegen.zig b/src/codegen.zig index 9861dfc06c..20599f3eb6 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -165,7 +165,10 @@ pub fn generateSymbol( const target = bin_file.options.target; const endian = target.cpu.arch.endian(); - log.debug("generateSymbol: ty = {}, val = {}", .{ typed_value.ty, typed_value.val.fmtDebug() }); + log.debug("generateSymbol: ty = {}, val = {}", .{ + typed_value.ty.fmtDebug(), + typed_value.val.fmtDebug(), + }); if (typed_value.val.isUndefDeep()) { const abi_size = try math.cast(usize, typed_value.ty.abiSize(target)); @@ -295,11 +298,11 @@ pub fn generateSymbol( .zero, .one, .int_u64, .int_big_positive => { switch (target.cpu.arch.ptrBitWidth()) { 32 => { - const x = typed_value.val.toUnsignedInt(); + const x = typed_value.val.toUnsignedInt(target); mem.writeInt(u32, try code.addManyAsArray(4), @intCast(u32, x), endian); }, 64 => { - const x = typed_value.val.toUnsignedInt(); + const x = typed_value.val.toUnsignedInt(target); mem.writeInt(u64, try code.addManyAsArray(8), x, endian); }, else => unreachable, @@ -433,7 +436,7 @@ pub fn generateSymbol( // TODO populate .debug_info for the integer const info = typed_value.ty.intInfo(bin_file.options.target); if (info.bits <= 8) { - const x = @intCast(u8, typed_value.val.toUnsignedInt()); + const x = @intCast(u8, typed_value.val.toUnsignedInt(target)); try code.append(x); return Result{ .appended = {} }; } @@ -443,20 +446,20 @@ pub fn generateSymbol( bin_file.allocator, src_loc, "TODO implement generateSymbol for big ints ('{}')", - .{typed_value.ty}, + .{typed_value.ty.fmtDebug()}, ), }; } switch (info.signedness) { .unsigned => { if (info.bits <= 16) { - const x = @intCast(u16, typed_value.val.toUnsignedInt()); + const x = @intCast(u16, typed_value.val.toUnsignedInt(target)); mem.writeInt(u16, try code.addManyAsArray(2), x, endian); } else if (info.bits <= 32) { - const x = @intCast(u32, typed_value.val.toUnsignedInt()); + const x = @intCast(u32, typed_value.val.toUnsignedInt(target)); mem.writeInt(u32, try code.addManyAsArray(4), x, endian); } else { - const x = typed_value.val.toUnsignedInt(); + const x = typed_value.val.toUnsignedInt(target); mem.writeInt(u64, try code.addManyAsArray(8), x, endian); } }, @@ -482,7 +485,7 @@ pub fn generateSymbol( const info = typed_value.ty.intInfo(target); if (info.bits <= 8) { - const x = @intCast(u8, int_val.toUnsignedInt()); + const x = @intCast(u8, int_val.toUnsignedInt(target)); try code.append(x); return Result{ .appended = {} }; } @@ -492,20 +495,20 @@ pub fn generateSymbol( bin_file.allocator, src_loc, "TODO implement generateSymbol for big int enums ('{}')", - .{typed_value.ty}, + .{typed_value.ty.fmtDebug()}, ), }; } switch (info.signedness) { .unsigned => { if (info.bits <= 16) { - const x = @intCast(u16, int_val.toUnsignedInt()); + const x = @intCast(u16, int_val.toUnsignedInt(target)); mem.writeInt(u16, try code.addManyAsArray(2), x, endian); } else if (info.bits <= 32) { - const x = @intCast(u32, int_val.toUnsignedInt()); + const x = @intCast(u32, int_val.toUnsignedInt(target)); mem.writeInt(u32, try code.addManyAsArray(4), x, endian); } else { - const x = int_val.toUnsignedInt(); + const x = int_val.toUnsignedInt(target); mem.writeInt(u64, try code.addManyAsArray(8), x, endian); } }, @@ -597,7 +600,7 @@ pub fn generateSymbol( } const union_ty = typed_value.ty.cast(Type.Payload.Union).?.data; - const field_index = union_ty.tag_ty.enumTagFieldIndex(union_obj.tag).?; + const field_index = union_ty.tag_ty.enumTagFieldIndex(union_obj.tag, target).?; assert(union_ty.haveFieldTypes()); const field_ty = union_ty.fields.values()[field_index].ty; if (!field_ty.hasRuntimeBits()) { @@ -787,6 +790,7 @@ fn lowerDeclRef( debug_output: DebugInfoOutput, reloc_info: RelocInfo, ) GenerateSymbolError!Result { + const target = bin_file.options.target; if (typed_value.ty.isSlice()) { // generate ptr var buf: Type.SlicePtrFieldTypeBuffer = undefined; @@ -805,7 +809,7 @@ fn lowerDeclRef( // generate length var slice_len: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, - .data = typed_value.val.sliceLen(), + .data = typed_value.val.sliceLen(target), }; switch (try generateSymbol(bin_file, src_loc, .{ .ty = Type.usize, @@ -821,7 +825,6 @@ fn lowerDeclRef( return Result{ .appended = {} }; } - const target = bin_file.options.target; const ptr_width = target.cpu.arch.ptrBitWidth(); const is_fn_body = decl.ty.zigTypeTag() == .Fn; if (!is_fn_body and !decl.ty.hasRuntimeBits()) { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index f5a1036479..c62abdb3f6 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -56,8 +56,14 @@ pub const TypedefMap = std.ArrayHashMap( true, ); +const FormatTypeAsCIdentContext = struct { + ty: Type, + target: std.Target, +}; + +/// TODO make this not cut off at 128 bytes fn formatTypeAsCIdentifier( - data: Type, + data: FormatTypeAsCIdentContext, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype, @@ -65,13 +71,15 @@ fn formatTypeAsCIdentifier( _ = fmt; _ = options; var buffer = [1]u8{0} ** 128; - // We don't care if it gets cut off, it's still more unique than a number - var buf = std.fmt.bufPrint(&buffer, "{}", .{data}) catch &buffer; + var buf = std.fmt.bufPrint(&buffer, "{}", .{data.ty.fmt(data.target)}) catch &buffer; return formatIdent(buf, "", .{}, writer); } -pub fn typeToCIdentifier(t: Type) std.fmt.Formatter(formatTypeAsCIdentifier) { - return .{ .data = t }; +pub fn typeToCIdentifier(ty: Type, target: std.Target) std.fmt.Formatter(formatTypeAsCIdentifier) { + return .{ .data = .{ + .ty = ty, + .target = target, + } }; } const reserved_idents = std.ComptimeStringMap(void, .{ @@ -369,6 +377,8 @@ pub const DeclGen = struct { ) error{ OutOfMemory, AnalysisFail }!void { decl.markAlive(); + const target = dg.module.getTarget(); + if (ty.isSlice()) { try writer.writeByte('('); try dg.renderTypecast(writer, ty); @@ -376,7 +386,7 @@ pub const DeclGen = struct { var buf: Type.SlicePtrFieldTypeBuffer = undefined; try dg.renderValue(writer, ty.slicePtrFieldType(&buf), val.slicePtr()); try writer.writeAll(", "); - try writer.print("{d}", .{val.sliceLen()}); + try writer.print("{d}", .{val.sliceLen(target)}); try writer.writeAll("}"); return; } @@ -388,7 +398,7 @@ pub const DeclGen = struct { // somewhere and we should let the C compiler tell us about it. if (ty.castPtrToFn() == null) { // Determine if we must pointer cast. - if (ty.eql(decl.ty)) { + if (ty.eql(decl.ty, target)) { try writer.writeByte('&'); try dg.renderDeclName(writer, decl); return; @@ -508,6 +518,7 @@ pub const DeclGen = struct { ty: Type, val: Value, ) error{ OutOfMemory, AnalysisFail }!void { + const target = dg.module.getTarget(); if (val.isUndefDeep()) { switch (ty.zigTypeTag()) { // Using '{}' for integer and floats seemed to error C compilers (both GCC and Clang) @@ -551,7 +562,7 @@ pub const DeclGen = struct { else => { if (ty.isSignedInt()) return writer.print("{d}", .{val.toSignedInt()}); - return writer.print("{d}u", .{val.toUnsignedInt()}); + return writer.print("{d}u", .{val.toUnsignedInt(target)}); }, }, .Float => { @@ -609,7 +620,7 @@ pub const DeclGen = struct { .int_u64, .one => { try writer.writeAll("(("); try dg.renderTypecast(writer, ty); - try writer.print(")0x{x}u)", .{val.toUnsignedInt()}); + try writer.print(")0x{x}u)", .{val.toUnsignedInt(target)}); }, else => unreachable, }, @@ -653,7 +664,6 @@ pub const DeclGen = struct { if (ty.isPtrLikeOptional()) { return dg.renderValue(writer, payload_type, val); } - const target = dg.module.getTarget(); if (payload_type.abiSize(target) == 0) { const is_null = val.castTag(.opt_payload) == null; return writer.print("{}", .{is_null}); @@ -773,7 +783,6 @@ pub const DeclGen = struct { .Union => { const union_obj = val.castTag(.@"union").?.data; const union_ty = ty.cast(Type.Payload.Union).?.data; - const target = dg.module.getTarget(); const layout = ty.unionGetLayout(target); try writer.writeAll("("); @@ -789,7 +798,7 @@ pub const DeclGen = struct { try writer.writeAll(".payload = {"); } - const index = union_ty.tag_ty.enumTagFieldIndex(union_obj.tag).?; + const index = union_ty.tag_ty.enumTagFieldIndex(union_obj.tag, target).?; const field_ty = ty.unionFields().values()[index].ty; const field_name = ty.unionFields().keys()[index]; if (field_ty.hasRuntimeBits()) { @@ -879,8 +888,8 @@ pub const DeclGen = struct { try bw.writeAll(" (*"); const name_start = buffer.items.len; - // TODO: typeToCIdentifier truncates to 128 bytes, we probably don't want to do this - try bw.print("zig_F_{s})(", .{typeToCIdentifier(t)}); + const target = dg.module.getTarget(); + try bw.print("zig_F_{s})(", .{typeToCIdentifier(t, target)}); const name_end = buffer.items.len - 2; const param_len = fn_info.param_types.len; @@ -934,10 +943,11 @@ pub const DeclGen = struct { try bw.writeAll("; size_t len; } "); const name_index = buffer.items.len; + const target = dg.module.getTarget(); if (t.isConstPtr()) { - try bw.print("zig_L_{s}", .{typeToCIdentifier(child_type)}); + try bw.print("zig_L_{s}", .{typeToCIdentifier(child_type, target)}); } else { - try bw.print("zig_M_{s}", .{typeToCIdentifier(child_type)}); + try bw.print("zig_M_{s}", .{typeToCIdentifier(child_type, target)}); } if (ptr_sentinel) |s| { try bw.writeAll("_s_"); @@ -1023,7 +1033,8 @@ pub const DeclGen = struct { try buffer.appendSlice("} "); const name_start = buffer.items.len; - try writer.print("zig_T_{};\n", .{typeToCIdentifier(t)}); + const target = dg.module.getTarget(); + try writer.print("zig_T_{};\n", .{typeToCIdentifier(t, target)}); const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); @@ -1107,6 +1118,7 @@ pub const DeclGen = struct { try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, 0); try bw.writeAll("; uint16_t error; } "); const name_index = buffer.items.len; + const target = dg.module.getTarget(); if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| { const func = inf_err_set_payload.data.func; try bw.writeAll("zig_E_"); @@ -1114,7 +1126,7 @@ pub const DeclGen = struct { try bw.writeAll(";\n"); } else { try bw.print("zig_E_{s}_{s};\n", .{ - typeToCIdentifier(err_set_type), typeToCIdentifier(child_type), + typeToCIdentifier(err_set_type, target), typeToCIdentifier(child_type, target), }); } @@ -1144,7 +1156,8 @@ pub const DeclGen = struct { try dg.renderType(bw, elem_type); const name_start = buffer.items.len + 1; - try bw.print(" zig_A_{s}_{d}", .{ typeToCIdentifier(elem_type), c_len }); + const target = dg.module.getTarget(); + try bw.print(" zig_A_{s}_{d}", .{ typeToCIdentifier(elem_type, target), c_len }); const name_end = buffer.items.len; try bw.print("[{d}];\n", .{c_len}); @@ -1172,7 +1185,8 @@ pub const DeclGen = struct { try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, 0); try bw.writeAll("; bool is_null; } "); const name_index = buffer.items.len; - try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type)}); + const target = dg.module.getTarget(); + try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type, target)}); const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); @@ -2177,12 +2191,13 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { if (src_val_is_undefined) return try airStoreUndefined(f, dest_ptr); + const target = f.object.dg.module.getTarget(); const writer = f.object.writer(); if (lhs_child_type.zigTypeTag() == .Array) { // For this memcpy to safely work we need the rhs to have the same // underlying type as the lhs (i.e. they must both be arrays of the same underlying type). const rhs_type = f.air.typeOf(bin_op.rhs); - assert(rhs_type.eql(lhs_child_type)); + assert(rhs_type.eql(lhs_child_type, target)); // If the source is a constant, writeCValue will emit a brace initialization // so work around this by initializing into new local. diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 107b059765..c8ca540f59 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -812,7 +812,7 @@ pub const Object = struct { const gpa = o.gpa; // Be careful not to reference this `gop` variable after any recursive calls // to `lowerDebugType`. - const gop = try o.di_type_map.getOrPut(gpa, ty); + const gop = try o.di_type_map.getOrPutContext(gpa, ty, .{ .target = o.target }); if (gop.found_existing) { const annotated = gop.value_ptr.*; const di_type = annotated.toDIType(); @@ -825,7 +825,7 @@ pub const Object = struct { }; return o.lowerDebugTypeImpl(entry, resolve, di_type); } - errdefer assert(o.di_type_map.orderedRemove(ty)); + errdefer assert(o.di_type_map.orderedRemoveContext(ty, .{ .target = o.target })); // The Type memory is ephemeral; since we want to store a longer-lived // reference, we need to copy it here. gop.key_ptr.* = try ty.copy(o.type_map_arena.allocator()); @@ -856,7 +856,7 @@ pub const Object = struct { .Int => { const info = ty.intInfo(target); assert(info.bits != 0); - const name = try ty.nameAlloc(gpa); + const name = try ty.nameAlloc(gpa, target); defer gpa.free(name); const dwarf_encoding: c_uint = switch (info.signedness) { .signed => DW.ATE.signed, @@ -873,7 +873,7 @@ pub const Object = struct { const enum_di_ty = try o.makeEmptyNamespaceDIType(owner_decl); // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` // means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty, AnnotatedDITypePtr.initFull(enum_di_ty)); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(enum_di_ty), .{ .target = o.target }); return enum_di_ty; } @@ -903,7 +903,7 @@ pub const Object = struct { const di_file = try o.getDIFile(gpa, owner_decl.src_namespace.file_scope); const di_scope = try o.namespaceToDebugScope(owner_decl.src_namespace); - const name = try ty.nameAlloc(gpa); + const name = try ty.nameAlloc(gpa, target); defer gpa.free(name); var buffer: Type.Payload.Bits = undefined; const int_ty = ty.intTagType(&buffer); @@ -921,12 +921,12 @@ pub const Object = struct { "", ); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty, AnnotatedDITypePtr.initFull(enum_di_ty)); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(enum_di_ty), .{ .target = o.target }); return enum_di_ty; }, .Float => { const bits = ty.floatBits(target); - const name = try ty.nameAlloc(gpa); + const name = try ty.nameAlloc(gpa, target); defer gpa.free(name); const di_type = dib.createBasicType(name, bits, DW.ATE.float); gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_type); @@ -974,7 +974,7 @@ pub const Object = struct { const bland_ptr_ty = Type.initPayload(&payload.base); const ptr_di_ty = try o.lowerDebugType(bland_ptr_ty, resolve); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty, AnnotatedDITypePtr.init(ptr_di_ty, resolve)); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.init(ptr_di_ty, resolve), .{ .target = o.target }); return ptr_di_ty; } @@ -983,7 +983,7 @@ pub const Object = struct { const ptr_ty = ty.slicePtrFieldType(&buf); const len_ty = Type.usize; - const name = try ty.nameAlloc(gpa); + const name = try ty.nameAlloc(gpa, target); defer gpa.free(name); const di_file: ?*llvm.DIFile = null; const line = 0; @@ -1054,12 +1054,12 @@ pub const Object = struct { ); dib.replaceTemporary(fwd_decl, full_di_ty); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty)); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target }); return full_di_ty; } const elem_di_ty = try o.lowerDebugType(ptr_info.pointee_type, .fwd); - const name = try ty.nameAlloc(gpa); + const name = try ty.nameAlloc(gpa, target); defer gpa.free(name); const ptr_di_ty = dib.createPointerType( elem_di_ty, @@ -1068,7 +1068,7 @@ pub const Object = struct { name, ); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty)); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .target = o.target }); return ptr_di_ty; }, .Opaque => { @@ -1077,7 +1077,7 @@ pub const Object = struct { gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_ty); return di_ty; } - const name = try ty.nameAlloc(gpa); + const name = try ty.nameAlloc(gpa, target); defer gpa.free(name); const owner_decl = ty.getOwnerDecl(); const opaque_di_ty = dib.createForwardDeclType( @@ -1089,7 +1089,7 @@ pub const Object = struct { ); // The recursive call to `lowerDebugType` va `namespaceToDebugScope` // means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty, AnnotatedDITypePtr.initFull(opaque_di_ty)); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(opaque_di_ty), .{ .target = o.target }); return opaque_di_ty; }, .Array => { @@ -1100,7 +1100,7 @@ pub const Object = struct { @intCast(c_int, ty.arrayLen()), ); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty, AnnotatedDITypePtr.initFull(array_di_ty)); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(array_di_ty), .{ .target = o.target }); return array_di_ty; }, .Vector => { @@ -1111,11 +1111,11 @@ pub const Object = struct { ty.vectorLen(), ); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty, AnnotatedDITypePtr.initFull(vector_di_ty)); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(vector_di_ty), .{ .target = o.target }); return vector_di_ty; }, .Optional => { - const name = try ty.nameAlloc(gpa); + const name = try ty.nameAlloc(gpa, target); defer gpa.free(name); var buf: Type.Payload.ElemType = undefined; const child_ty = ty.optionalChild(&buf); @@ -1127,7 +1127,7 @@ pub const Object = struct { if (ty.isPtrLikeOptional()) { const ptr_di_ty = try o.lowerDebugType(child_ty, resolve); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty)); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .target = o.target }); return ptr_di_ty; } @@ -1200,7 +1200,7 @@ pub const Object = struct { ); dib.replaceTemporary(fwd_decl, full_di_ty); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty)); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target }); return full_di_ty; }, .ErrorUnion => { @@ -1209,10 +1209,10 @@ pub const Object = struct { if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { const err_set_di_ty = try o.lowerDebugType(err_set_ty, .full); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty, AnnotatedDITypePtr.initFull(err_set_di_ty)); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(err_set_di_ty), .{ .target = o.target }); return err_set_di_ty; } - const name = try ty.nameAlloc(gpa); + const name = try ty.nameAlloc(gpa, target); defer gpa.free(name); const di_file: ?*llvm.DIFile = null; const line = 0; @@ -1282,7 +1282,7 @@ pub const Object = struct { ); dib.replaceTemporary(fwd_decl, full_di_ty); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty)); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target }); return full_di_ty; }, .ErrorSet => { @@ -1294,7 +1294,7 @@ pub const Object = struct { }, .Struct => { const compile_unit_scope = o.di_compile_unit.?.toScope(); - const name = try ty.nameAlloc(gpa); + const name = try ty.nameAlloc(gpa, target); defer gpa.free(name); if (ty.castTag(.@"struct")) |payload| { @@ -1381,7 +1381,7 @@ pub const Object = struct { ); dib.replaceTemporary(fwd_decl, full_di_ty); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty)); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target }); return full_di_ty; } @@ -1395,7 +1395,7 @@ pub const Object = struct { dib.replaceTemporary(fwd_decl, struct_di_ty); // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` // means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty, AnnotatedDITypePtr.initFull(struct_di_ty)); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(struct_di_ty), .{ .target = o.target }); return struct_di_ty; } } @@ -1406,7 +1406,7 @@ pub const Object = struct { dib.replaceTemporary(fwd_decl, struct_di_ty); // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` // means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty, AnnotatedDITypePtr.initFull(struct_di_ty)); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(struct_di_ty), .{ .target = o.target }); return struct_di_ty; } @@ -1461,13 +1461,13 @@ pub const Object = struct { ); dib.replaceTemporary(fwd_decl, full_di_ty); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty)); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target }); return full_di_ty; }, .Union => { const owner_decl = ty.getOwnerDecl(); - const name = try ty.nameAlloc(gpa); + const name = try ty.nameAlloc(gpa, target); defer gpa.free(name); const fwd_decl = opt_fwd_decl orelse blk: { @@ -1489,7 +1489,7 @@ pub const Object = struct { dib.replaceTemporary(fwd_decl, union_di_ty); // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` // means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty)); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .target = o.target }); return union_di_ty; } @@ -1603,7 +1603,7 @@ pub const Object = struct { 0, ); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty, AnnotatedDITypePtr.initFull(fn_di_ty)); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(fn_di_ty), .{ .target = o.target }); return fn_di_ty; }, .ComptimeInt => unreachable, @@ -1676,7 +1676,9 @@ pub const DeclGen = struct { const decl = dg.decl; assert(decl.has_tv); - log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty, decl.val.fmtDebug() }); + log.debug("gen: {s} type: {}, value: {}", .{ + decl.name, decl.ty.fmtDebug(), decl.val.fmtDebug(), + }); if (decl.val.castTag(.function)) |func_payload| { _ = func_payload; @@ -1990,7 +1992,7 @@ pub const DeclGen = struct { }, .Opaque => switch (t.tag()) { .@"opaque" => { - const gop = try dg.object.type_map.getOrPut(gpa, t); + const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .target = target }); if (gop.found_existing) return gop.value_ptr.*; // The Type memory is ephemeral; since we want to store a longer-lived @@ -2051,7 +2053,7 @@ pub const DeclGen = struct { return dg.context.intType(16); }, .Struct => { - const gop = try dg.object.type_map.getOrPut(gpa, t); + const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .target = target }); if (gop.found_existing) return gop.value_ptr.*; // The Type memory is ephemeral; since we want to store a longer-lived @@ -2174,7 +2176,7 @@ pub const DeclGen = struct { return llvm_struct_ty; }, .Union => { - const gop = try dg.object.type_map.getOrPut(gpa, t); + const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .target = target }); if (gop.found_existing) return gop.value_ptr.*; // The Type memory is ephemeral; since we want to store a longer-lived @@ -2289,6 +2291,7 @@ pub const DeclGen = struct { const llvm_type = try dg.llvmType(tv.ty); return llvm_type.getUndef(); } + const target = dg.module.getTarget(); switch (tv.ty.zigTypeTag()) { .Bool => { @@ -2302,8 +2305,7 @@ pub const DeclGen = struct { .decl_ref => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref).?.data), else => { var bigint_space: Value.BigIntSpace = undefined; - const bigint = tv.val.toBigInt(&bigint_space); - const target = dg.module.getTarget(); + const bigint = tv.val.toBigInt(&bigint_space, target); const int_info = tv.ty.intInfo(target); assert(int_info.bits != 0); const llvm_type = dg.context.intType(int_info.bits); @@ -2331,9 +2333,8 @@ pub const DeclGen = struct { const int_val = tv.enumToInt(&int_buffer); var bigint_space: Value.BigIntSpace = undefined; - const bigint = int_val.toBigInt(&bigint_space); + const bigint = int_val.toBigInt(&bigint_space, target); - const target = dg.module.getTarget(); const int_info = tv.ty.intInfo(target); const llvm_type = dg.context.intType(int_info.bits); @@ -2356,7 +2357,6 @@ pub const DeclGen = struct { }, .Float => { const llvm_ty = try dg.llvmType(tv.ty); - const target = dg.module.getTarget(); switch (tv.ty.floatBits(target)) { 16, 32, 64 => return llvm_ty.constReal(tv.val.toFloat(f64)), 80 => { @@ -2414,7 +2414,7 @@ pub const DeclGen = struct { }, .int_u64, .one, .int_big_positive => { const llvm_usize = try dg.llvmType(Type.usize); - const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(), .False); + const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(target), .False); return llvm_int.constIntToPtr(try dg.llvmType(tv.ty)); }, .field_ptr, .opt_payload_ptr, .eu_payload_ptr, .elem_ptr => { @@ -2424,7 +2424,9 @@ pub const DeclGen = struct { const llvm_type = try dg.llvmType(tv.ty); return llvm_type.constNull(); }, - else => |tag| return dg.todo("implement const of pointer type '{}' ({})", .{ tv.ty, tag }), + else => |tag| return dg.todo("implement const of pointer type '{}' ({})", .{ + tv.ty.fmtDebug(), tag, + }), }, .Array => switch (tv.val.tag()) { .bytes => { @@ -2592,7 +2594,6 @@ pub const DeclGen = struct { const llvm_struct_ty = try dg.llvmType(tv.ty); const field_vals = tv.val.castTag(.aggregate).?.data; const gpa = dg.gpa; - const target = dg.module.getTarget(); if (tv.ty.isTupleOrAnonStruct()) { const tuple = tv.ty.tupleFields(); @@ -2753,7 +2754,6 @@ pub const DeclGen = struct { const llvm_union_ty = try dg.llvmType(tv.ty); const tag_and_val = tv.val.castTag(.@"union").?.data; - const target = dg.module.getTarget(); const layout = tv.ty.unionGetLayout(target); if (layout.payload_size == 0) { @@ -2763,7 +2763,7 @@ pub const DeclGen = struct { }); } const union_obj = tv.ty.cast(Type.Payload.Union).?.data; - const field_index = union_obj.tag_ty.enumTagFieldIndex(tag_and_val.tag).?; + const field_index = union_obj.tag_ty.enumTagFieldIndex(tag_and_val.tag, target).?; assert(union_obj.haveFieldTypes()); const field_ty = union_obj.fields.values()[field_index].ty; const payload = p: { @@ -2892,7 +2892,7 @@ pub const DeclGen = struct { .Frame, .AnyFrame, - => return dg.todo("implement const of type '{}'", .{tv.ty}), + => return dg.todo("implement const of type '{}'", .{tv.ty.fmtDebug()}), } } @@ -2910,7 +2910,8 @@ pub const DeclGen = struct { const ptr_ty = Type.initPayload(&ptr_ty_payload.base); const llvm_ptr = try dg.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl); - if (ptr_child_ty.eql(decl.ty)) { + const target = dg.module.getTarget(); + if (ptr_child_ty.eql(decl.ty, target)) { return llvm_ptr; } else { return llvm_ptr.constBitCast((try dg.llvmType(ptr_child_ty)).pointerType(0)); @@ -2918,6 +2919,7 @@ pub const DeclGen = struct { } fn lowerParentPtr(dg: *DeclGen, ptr_val: Value, ptr_child_ty: Type) Error!*const llvm.Value { + const target = dg.module.getTarget(); var bitcast_needed: bool = undefined; const llvm_ptr = switch (ptr_val.tag()) { .decl_ref_mut => { @@ -2951,7 +2953,6 @@ pub const DeclGen = struct { const field_index = @intCast(u32, field_ptr.field_index); const llvm_u32 = dg.context.intType(32); - const target = dg.module.getTarget(); switch (parent_ty.zigTypeTag()) { .Union => { bitcast_needed = true; @@ -2974,7 +2975,7 @@ pub const DeclGen = struct { }, .Struct => { const field_ty = parent_ty.structFieldType(field_index); - bitcast_needed = !field_ty.eql(ptr_child_ty); + bitcast_needed = !field_ty.eql(ptr_child_ty, target); var ty_buf: Type.Payload.Pointer = undefined; const llvm_field_index = llvmFieldIndex(parent_ty, field_index, target, &ty_buf).?; @@ -2990,7 +2991,7 @@ pub const DeclGen = struct { .elem_ptr => blk: { const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; const parent_llvm_ptr = try dg.lowerParentPtr(elem_ptr.array_ptr, elem_ptr.elem_ty); - bitcast_needed = !elem_ptr.elem_ty.eql(ptr_child_ty); + bitcast_needed = !elem_ptr.elem_ty.eql(ptr_child_ty, target); const llvm_usize = try dg.llvmType(Type.usize); const indices: [1]*const llvm.Value = .{ @@ -3004,7 +3005,7 @@ pub const DeclGen = struct { var buf: Type.Payload.ElemType = undefined; const payload_ty = opt_payload_ptr.container_ty.optionalChild(&buf); - bitcast_needed = !payload_ty.eql(ptr_child_ty); + bitcast_needed = !payload_ty.eql(ptr_child_ty, target); if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.isPtrLikeOptional()) { // In this case, we represent pointer to optional the same as pointer @@ -3024,7 +3025,7 @@ pub const DeclGen = struct { const parent_llvm_ptr = try dg.lowerParentPtr(eu_payload_ptr.container_ptr, eu_payload_ptr.container_ty); const payload_ty = eu_payload_ptr.container_ty.errorUnionPayload(); - bitcast_needed = !payload_ty.eql(ptr_child_ty); + bitcast_needed = !payload_ty.eql(ptr_child_ty, target); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { // In this case, we represent pointer to error union the same as pointer @@ -3053,12 +3054,13 @@ pub const DeclGen = struct { tv: TypedValue, decl: *Module.Decl, ) Error!*const llvm.Value { + const target = self.module.getTarget(); if (tv.ty.isSlice()) { var buf: Type.SlicePtrFieldTypeBuffer = undefined; const ptr_ty = tv.ty.slicePtrFieldType(&buf); var slice_len: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, - .data = tv.val.sliceLen(), + .data = tv.val.sliceLen(target), }; const fields: [2]*const llvm.Value = .{ try self.genTypedValue(.{ diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 0d9d1ae223..6072c59845 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -313,7 +313,7 @@ pub const DeclGen = struct { // As of yet, there is no vector support in the self-hosted compiler. .Vector => self.todo("implement arithmeticTypeInfo for Vector", .{}), // TODO: For which types is this the case? - else => self.todo("implement arithmeticTypeInfo for {}", .{ty}), + else => self.todo("implement arithmeticTypeInfo for {}", .{ty.fmtDebug()}), }; } @@ -335,7 +335,7 @@ pub const DeclGen = struct { const int_info = ty.intInfo(target); const backing_bits = self.backingIntBits(int_info.bits) orelse { // Integers too big for any native type are represented as "composite integers": An array of largestSupportedIntBits. - return self.todo("implement composite int constants for {}", .{ty}); + return self.todo("implement composite int constants for {}", .{ty.fmtDebug()}); }; // We can just use toSignedInt/toUnsignedInt here as it returns u64 - a type large enough to hold any @@ -345,7 +345,7 @@ pub const DeclGen = struct { // Note, value is required to be sign-extended, so we don't need to mask off the upper bits. // See https://www.khronos.org/registry/SPIR-V/specs/unified1/SPIRV.html#Literal - var int_bits = if (ty.isSignedInt()) @bitCast(u64, val.toSignedInt()) else val.toUnsignedInt(); + var int_bits = if (ty.isSignedInt()) @bitCast(u64, val.toSignedInt()) else val.toUnsignedInt(target); const value: spec.LiteralContextDependentNumber = switch (backing_bits) { 1...32 => .{ .uint32 = @truncate(u32, int_bits) }, @@ -388,7 +388,7 @@ pub const DeclGen = struct { }); }, .Void => unreachable, - else => return self.todo("constant generation of type {}", .{ty}), + else => return self.todo("constant generation of type {}", .{ty.fmtDebug()}), } return result_id.toRef(); @@ -414,7 +414,7 @@ pub const DeclGen = struct { const backing_bits = self.backingIntBits(int_info.bits) orelse { // TODO: Integers too big for any native type are represented as "composite integers": // An array of largestSupportedIntBits. - return self.todo("Implement composite int type {}", .{ty}); + return self.todo("Implement composite int type {}", .{ty.fmtDebug()}); }; const payload = try self.spv.arena.create(SpvType.Payload.Int); @@ -644,8 +644,10 @@ pub const DeclGen = struct { const result_id = self.spv.allocId(); const result_type_id = try self.resolveTypeId(ty); - assert(self.air.typeOf(bin_op.lhs).eql(ty)); - assert(self.air.typeOf(bin_op.rhs).eql(ty)); + const target = self.getTarget(); + + assert(self.air.typeOf(bin_op.lhs).eql(ty, target)); + assert(self.air.typeOf(bin_op.rhs).eql(ty, target)); // Binary operations are generally applicable to both scalar and vector operations // in SPIR-V, but int and float versions of operations require different opcodes. @@ -692,7 +694,7 @@ pub const DeclGen = struct { const result_id = self.spv.allocId(); const result_type_id = try self.resolveTypeId(Type.initTag(.bool)); const op_ty = self.air.typeOf(bin_op.lhs); - assert(op_ty.eql(self.air.typeOf(bin_op.rhs))); + assert(op_ty.eql(self.air.typeOf(bin_op.rhs), self.getTarget())); // Comparisons are generally applicable to both scalar and vector operations in SPIR-V, // but int and float versions of operations require different opcodes. diff --git a/src/link.zig b/src/link.zig index d431e9d5f1..0fd6797153 100644 --- a/src/link.zig +++ b/src/link.zig @@ -457,7 +457,7 @@ pub const File = struct { /// May be called before or after updateDeclExports but must be called /// after allocateDeclIndexes for any given Decl. pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) UpdateDeclError!void { - log.debug("updateDecl {*} ({s}), type={}", .{ decl, decl.name, decl.ty }); + log.debug("updateDecl {*} ({s}), type={}", .{ decl, decl.name, decl.ty.fmtDebug() }); assert(decl.has_tv); switch (base.tag) { // zig fmt: off @@ -477,7 +477,7 @@ pub const File = struct { /// after allocateDeclIndexes for any given Decl. pub fn updateFunc(base: *File, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) UpdateDeclError!void { log.debug("updateFunc {*} ({s}), type={}", .{ - func.owner_decl, func.owner_decl.name, func.owner_decl.ty, + func.owner_decl, func.owner_decl.name, func.owner_decl.ty.fmtDebug(), }); switch (base.tag) { // zig fmt: off diff --git a/src/link/C.zig b/src/link/C.zig index 6599008c73..85b7c24487 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -127,7 +127,7 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes .error_msg = null, .decl = decl, .fwd_decl = fwd_decl.toManaged(module.gpa), - .typedefs = typedefs.promote(module.gpa), + .typedefs = typedefs.promoteContext(module.gpa, .{ .target = module.getTarget() }), .typedefs_arena = self.arena.allocator(), }, .code = code.toManaged(module.gpa), @@ -192,7 +192,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { .error_msg = null, .decl = decl, .fwd_decl = fwd_decl.toManaged(module.gpa), - .typedefs = typedefs.promote(module.gpa), + .typedefs = typedefs.promoteContext(module.gpa, .{ .target = module.getTarget() }), .typedefs_arena = self.arena.allocator(), }, .code = code.toManaged(module.gpa), @@ -366,7 +366,9 @@ fn flushDecl(self: *C, f: *Flush, decl: *const Module.Decl) FlushDeclError!void try f.typedefs.ensureUnusedCapacity(gpa, @intCast(u32, decl_block.typedefs.count())); var it = decl_block.typedefs.iterator(); while (it.next()) |new| { - const gop = f.typedefs.getOrPutAssumeCapacity(new.key_ptr.*); + const gop = f.typedefs.getOrPutAssumeCapacityContext(new.key_ptr.*, .{ + .target = self.base.options.target, + }); if (!gop.found_existing) { try f.err_typedef_buf.appendSlice(gpa, new.value_ptr.rendered); } diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index fc8f1fab55..8f0eb96132 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -200,7 +200,9 @@ pub fn initDeclDebugInfo(self: *Dwarf, decl: *Module.Decl) !DeclDebugBuffers { assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len); dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4 if (fn_ret_has_bits) { - const gop = try dbg_info_type_relocs.getOrPut(gpa, fn_ret_type); + const gop = try dbg_info_type_relocs.getOrPutContext(gpa, fn_ret_type, .{ + .target = self.target, + }); if (!gop.found_existing) { gop.value_ptr.* = .{ .off = undefined, @@ -455,7 +457,9 @@ pub fn commitDeclDebugInfo( var it: usize = 0; while (it < dbg_info_type_relocs.count()) : (it += 1) { const ty = dbg_info_type_relocs.keys()[it]; - const value_ptr = dbg_info_type_relocs.getPtr(ty).?; + const value_ptr = dbg_info_type_relocs.getPtrContext(ty, .{ + .target = self.target, + }).?; value_ptr.off = @intCast(u32, dbg_info_buffer.items.len); try self.addDbgInfoType(dbg_type_arena.allocator(), ty, dbg_info_buffer, dbg_info_type_relocs); } @@ -774,7 +778,7 @@ fn addDbgInfoType( // DW.AT.byte_size, DW.FORM.data1 dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{}\x00", .{ty}); + try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); }, .Optional => { if (ty.isPtrLikeOptional()) { @@ -785,7 +789,7 @@ fn addDbgInfoType( // DW.AT.byte_size, DW.FORM.data1 dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{}\x00", .{ty}); + try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); } else { // Non-pointer optionals are structs: struct { .maybe = *, .val = * } var buf = try arena.create(Type.Payload.ElemType); @@ -796,7 +800,7 @@ fn addDbgInfoType( const abi_size = ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{}\x00", .{ty}); + try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(7); dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); @@ -835,7 +839,7 @@ fn addDbgInfoType( // DW.AT.byte_size, DW.FORM.sdata dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize) * 2); // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{}\x00", .{ty}); + try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(5); dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); @@ -882,7 +886,7 @@ fn addDbgInfoType( const abi_size = ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); // DW.AT.name, DW.FORM.string - const struct_name = try ty.nameAllocArena(arena); + const struct_name = try ty.nameAllocArena(arena, target); try dbg_info_buffer.ensureUnusedCapacity(struct_name.len + 1); dbg_info_buffer.appendSliceAssumeCapacity(struct_name); dbg_info_buffer.appendAssumeCapacity(0); @@ -915,13 +919,15 @@ fn addDbgInfoType( try dbg_info_buffer.append(0); }, else => { - log.debug("TODO implement .debug_info for type '{}'", .{ty}); + log.debug("TODO implement .debug_info for type '{}'", .{ty.fmtDebug()}); try dbg_info_buffer.append(abbrev_pad1); }, } for (relocs.items) |rel| { - const gop = try dbg_info_type_relocs.getOrPut(self.allocator, rel.ty); + const gop = try dbg_info_type_relocs.getOrPutContext(self.allocator, rel.ty, .{ + .target = self.target, + }); if (!gop.found_existing) { gop.value_ptr.* = .{ .off = undefined, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 8bb0101301..82363c7e24 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -3874,7 +3874,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { /// Checks if the value, or any of its embedded values stores a pointer, and thus requires /// a rebase opcode for the dynamic linker. -fn needsPointerRebase(ty: Type, val: Value) bool { +fn needsPointerRebase(ty: Type, val: Value, target: std.Target) bool { if (ty.zigTypeTag() == .Fn) { return false; } @@ -3890,7 +3890,7 @@ fn needsPointerRebase(ty: Type, val: Value) bool { const elem_ty = ty.childType(); var elem_value_buf: Value.ElemValueBuffer = undefined; const elem_val = val.elemValueBuffer(0, &elem_value_buf); - return needsPointerRebase(elem_ty, elem_val); + return needsPointerRebase(elem_ty, elem_val, target); }, .Struct => { const fields = ty.structFields().values(); @@ -3898,7 +3898,7 @@ fn needsPointerRebase(ty: Type, val: Value) bool { if (val.castTag(.aggregate)) |payload| { const field_values = payload.data; for (field_values) |field_val, i| { - if (needsPointerRebase(fields[i].ty, field_val)) return true; + if (needsPointerRebase(fields[i].ty, field_val, target)) return true; } else return false; } else return false; }, @@ -3907,18 +3907,18 @@ fn needsPointerRebase(ty: Type, val: Value) bool { const sub_val = payload.data; var buffer: Type.Payload.ElemType = undefined; const sub_ty = ty.optionalChild(&buffer); - return needsPointerRebase(sub_ty, sub_val); + return needsPointerRebase(sub_ty, sub_val, target); } else return false; }, .Union => { const union_obj = val.cast(Value.Payload.Union).?.data; - const active_field_ty = ty.unionFieldType(union_obj.tag); - return needsPointerRebase(active_field_ty, union_obj.val); + const active_field_ty = ty.unionFieldType(union_obj.tag, target); + return needsPointerRebase(active_field_ty, union_obj.val, target); }, .ErrorUnion => { if (val.castTag(.eu_payload)) |payload| { const payload_ty = ty.errorUnionPayload(); - return needsPointerRebase(payload_ty, payload.data); + return needsPointerRebase(payload_ty, payload.data, target); } else return false; }, else => return false, @@ -3927,7 +3927,8 @@ fn needsPointerRebase(ty: Type, val: Value) bool { fn getMatchingSectionAtom(self: *MachO, atom: *Atom, name: []const u8, ty: Type, val: Value) !MatchingSection { const code = atom.code.items; - const alignment = ty.abiAlignment(self.base.options.target); + const target = self.base.options.target; + const alignment = ty.abiAlignment(target); const align_log_2 = math.log2(alignment); const zig_ty = ty.zigTypeTag(); const mode = self.base.options.optimize_mode; @@ -3954,7 +3955,7 @@ fn getMatchingSectionAtom(self: *MachO, atom: *Atom, name: []const u8, ty: Type, }; } - if (needsPointerRebase(ty, val)) { + if (needsPointerRebase(ty, val, target)) { break :blk (try self.getMatchingSection(.{ .segname = makeStaticString("__DATA_CONST"), .sectname = makeStaticString("__const"), diff --git a/src/print_air.zig b/src/print_air.zig index c6b27a6cd6..5e0179e3cc 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -299,12 +299,12 @@ const Writer = struct { fn writeTy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const ty = w.air.instructions.items(.data)[inst].ty; - try s.print("{}", .{ty}); + try s.print("{}", .{ty.fmtDebug()}); } fn writeTyOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const ty_op = w.air.instructions.items(.data)[inst].ty_op; - try s.print("{}, ", .{w.air.getRefType(ty_op.ty)}); + try s.print("{}, ", .{w.air.getRefType(ty_op.ty).fmtDebug()}); try w.writeOperand(s, inst, 0, ty_op.operand); } @@ -313,7 +313,7 @@ const Writer = struct { const extra = w.air.extraData(Air.Block, ty_pl.payload); const body = w.air.extra[extra.end..][0..extra.data.body_len]; - try s.print("{}, {{\n", .{w.air.getRefType(ty_pl.ty)}); + try s.print("{}, {{\n", .{w.air.getRefType(ty_pl.ty).fmtDebug()}); const old_indent = w.indent; w.indent += 2; try w.writeBody(s, body); @@ -328,7 +328,7 @@ const Writer = struct { const len = @intCast(usize, vector_ty.arrayLen()); const elements = @bitCast([]const Air.Inst.Ref, w.air.extra[ty_pl.payload..][0..len]); - try s.print("{}, [", .{vector_ty}); + try s.print("{}, [", .{vector_ty.fmtDebug()}); for (elements) |elem, i| { if (i != 0) try s.writeAll(", "); try w.writeOperand(s, inst, i, elem); @@ -502,7 +502,7 @@ const Writer = struct { fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; const val = w.air.values[ty_pl.payload]; - try s.print("{}, {}", .{ w.air.getRefType(ty_pl.ty), val.fmtDebug() }); + try s.print("{}, {}", .{ w.air.getRefType(ty_pl.ty).fmtDebug(), val.fmtDebug() }); } fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { @@ -514,7 +514,7 @@ const Writer = struct { var op_index: usize = 0; const ret_ty = w.air.typeOfIndex(inst); - try s.print("{}", .{ret_ty}); + try s.print("{}", .{ret_ty.fmtDebug()}); if (is_volatile) { try s.writeAll(", volatile"); diff --git a/src/type.zig b/src/type.zig index 6d27150412..0b49eac0a8 100644 --- a/src/type.zig +++ b/src/type.zig @@ -6,6 +6,7 @@ const Target = std.Target; const Module = @import("Module.zig"); const log = std.log.scoped(.Type); const target_util = @import("target.zig"); +const TypedValue = @import("TypedValue.zig"); const file_struct = @This(); @@ -520,7 +521,7 @@ pub const Type = extern union { } } - pub fn eql(a: Type, b: Type) bool { + pub fn eql(a: Type, b: Type, target: Target) bool { // As a shortcut, if the small tags / addresses match, we're done. if (a.tag_if_small_enough == b.tag_if_small_enough) return true; @@ -636,7 +637,7 @@ pub const Type = extern union { const a_info = a.fnInfo(); const b_info = b.fnInfo(); - if (!eql(a_info.return_type, b_info.return_type)) + if (!eql(a_info.return_type, b_info.return_type, target)) return false; if (a_info.cc != b_info.cc) @@ -662,7 +663,7 @@ pub const Type = extern union { if (a_param_ty.tag() == .generic_poison) continue; if (b_param_ty.tag() == .generic_poison) continue; - if (!eql(a_param_ty, b_param_ty)) + if (!eql(a_param_ty, b_param_ty, target)) return false; } @@ -680,13 +681,13 @@ pub const Type = extern union { if (a.arrayLen() != b.arrayLen()) return false; const elem_ty = a.elemType(); - if (!elem_ty.eql(b.elemType())) + if (!elem_ty.eql(b.elemType(), target)) return false; const sentinel_a = a.sentinel(); const sentinel_b = b.sentinel(); if (sentinel_a) |sa| { if (sentinel_b) |sb| { - return sa.eql(sb, elem_ty); + return sa.eql(sb, elem_ty, target); } else { return false; } @@ -717,7 +718,7 @@ pub const Type = extern union { const info_a = a.ptrInfo().data; const info_b = b.ptrInfo().data; - if (!info_a.pointee_type.eql(info_b.pointee_type)) + if (!info_a.pointee_type.eql(info_b.pointee_type, target)) return false; if (info_a.@"align" != info_b.@"align") return false; @@ -740,7 +741,7 @@ pub const Type = extern union { const sentinel_b = info_b.sentinel; if (sentinel_a) |sa| { if (sentinel_b) |sb| { - if (!sa.eql(sb, info_a.pointee_type)) + if (!sa.eql(sb, info_a.pointee_type, target)) return false; } else { return false; @@ -761,7 +762,7 @@ pub const Type = extern union { var buf_a: Payload.ElemType = undefined; var buf_b: Payload.ElemType = undefined; - return a.optionalChild(&buf_a).eql(b.optionalChild(&buf_b)); + return a.optionalChild(&buf_a).eql(b.optionalChild(&buf_b), target); }, .anyerror_void_error_union, .error_union => { @@ -769,18 +770,18 @@ pub const Type = extern union { const a_set = a.errorUnionSet(); const b_set = b.errorUnionSet(); - if (!a_set.eql(b_set)) return false; + if (!a_set.eql(b_set, target)) return false; const a_payload = a.errorUnionPayload(); const b_payload = b.errorUnionPayload(); - if (!a_payload.eql(b_payload)) return false; + if (!a_payload.eql(b_payload, target)) return false; return true; }, .anyframe_T => { if (b.zigTypeTag() != .AnyFrame) return false; - return a.childType().eql(b.childType()); + return a.childType().eql(b.childType(), target); }, .empty_struct => { @@ -803,7 +804,7 @@ pub const Type = extern union { for (a_tuple.types) |a_ty, i| { const b_ty = b_tuple.types[i]; - if (!eql(a_ty, b_ty)) return false; + if (!eql(a_ty, b_ty, target)) return false; } for (a_tuple.values) |a_val, i| { @@ -819,7 +820,7 @@ pub const Type = extern union { if (b_val.tag() == .unreachable_value) { return false; } else { - if (!Value.eql(a_val, b_val, ty)) return false; + if (!Value.eql(a_val, b_val, ty, target)) return false; } } } @@ -839,7 +840,7 @@ pub const Type = extern union { for (a_struct_obj.types) |a_ty, i| { const b_ty = b_struct_obj.types[i]; - if (!eql(a_ty, b_ty)) return false; + if (!eql(a_ty, b_ty, target)) return false; } for (a_struct_obj.values) |a_val, i| { @@ -855,7 +856,7 @@ pub const Type = extern union { if (b_val.tag() == .unreachable_value) { return false; } else { - if (!Value.eql(a_val, b_val, ty)) return false; + if (!Value.eql(a_val, b_val, ty, target)) return false; } } } @@ -910,13 +911,13 @@ pub const Type = extern union { } } - pub fn hash(self: Type) u64 { + pub fn hash(self: Type, target: Target) u64 { var hasher = std.hash.Wyhash.init(0); - self.hashWithHasher(&hasher); + self.hashWithHasher(&hasher, target); return hasher.final(); } - pub fn hashWithHasher(ty: Type, hasher: *std.hash.Wyhash) void { + pub fn hashWithHasher(ty: Type, hasher: *std.hash.Wyhash, target: Target) void { switch (ty.tag()) { .generic_poison => unreachable, @@ -1035,7 +1036,7 @@ pub const Type = extern union { std.hash.autoHash(hasher, std.builtin.TypeId.Fn); const fn_info = ty.fnInfo(); - hashWithHasher(fn_info.return_type, hasher); + hashWithHasher(fn_info.return_type, hasher, target); std.hash.autoHash(hasher, fn_info.alignment); std.hash.autoHash(hasher, fn_info.cc); std.hash.autoHash(hasher, fn_info.is_var_args); @@ -1045,7 +1046,7 @@ pub const Type = extern union { for (fn_info.param_types) |param_ty, i| { std.hash.autoHash(hasher, fn_info.paramIsComptime(i)); if (param_ty.tag() == .generic_poison) continue; - hashWithHasher(param_ty, hasher); + hashWithHasher(param_ty, hasher, target); } }, @@ -1058,8 +1059,8 @@ pub const Type = extern union { const elem_ty = ty.elemType(); std.hash.autoHash(hasher, ty.arrayLen()); - hashWithHasher(elem_ty, hasher); - hashSentinel(ty.sentinel(), elem_ty, hasher); + hashWithHasher(elem_ty, hasher, target); + hashSentinel(ty.sentinel(), elem_ty, hasher, target); }, .vector => { @@ -1067,7 +1068,7 @@ pub const Type = extern union { const elem_ty = ty.elemType(); std.hash.autoHash(hasher, ty.vectorLen()); - hashWithHasher(elem_ty, hasher); + hashWithHasher(elem_ty, hasher, target); }, .single_const_pointer_to_comptime_int, @@ -1091,8 +1092,8 @@ pub const Type = extern union { std.hash.autoHash(hasher, std.builtin.TypeId.Pointer); const info = ty.ptrInfo().data; - hashWithHasher(info.pointee_type, hasher); - hashSentinel(info.sentinel, info.pointee_type, hasher); + hashWithHasher(info.pointee_type, hasher, target); + hashSentinel(info.sentinel, info.pointee_type, hasher, target); std.hash.autoHash(hasher, info.@"align"); std.hash.autoHash(hasher, info.@"addrspace"); std.hash.autoHash(hasher, info.bit_offset); @@ -1110,22 +1111,22 @@ pub const Type = extern union { std.hash.autoHash(hasher, std.builtin.TypeId.Optional); var buf: Payload.ElemType = undefined; - hashWithHasher(ty.optionalChild(&buf), hasher); + hashWithHasher(ty.optionalChild(&buf), hasher, target); }, .anyerror_void_error_union, .error_union => { std.hash.autoHash(hasher, std.builtin.TypeId.ErrorUnion); const set_ty = ty.errorUnionSet(); - hashWithHasher(set_ty, hasher); + hashWithHasher(set_ty, hasher, target); const payload_ty = ty.errorUnionPayload(); - hashWithHasher(payload_ty, hasher); + hashWithHasher(payload_ty, hasher, target); }, .anyframe_T => { std.hash.autoHash(hasher, std.builtin.TypeId.AnyFrame); - hashWithHasher(ty.childType(), hasher); + hashWithHasher(ty.childType(), hasher, target); }, .empty_struct => { @@ -1144,10 +1145,10 @@ pub const Type = extern union { std.hash.autoHash(hasher, tuple.types.len); for (tuple.types) |field_ty, i| { - hashWithHasher(field_ty, hasher); + hashWithHasher(field_ty, hasher, target); const field_val = tuple.values[i]; if (field_val.tag() == .unreachable_value) continue; - field_val.hash(field_ty, hasher); + field_val.hash(field_ty, hasher, target); } }, .anon_struct => { @@ -1159,9 +1160,9 @@ pub const Type = extern union { const field_name = struct_obj.names[i]; const field_val = struct_obj.values[i]; hasher.update(field_name); - hashWithHasher(field_ty, hasher); + hashWithHasher(field_ty, hasher, target); if (field_val.tag() == .unreachable_value) continue; - field_val.hash(field_ty, hasher); + field_val.hash(field_ty, hasher, target); } }, @@ -1209,35 +1210,35 @@ pub const Type = extern union { } } - fn hashSentinel(opt_val: ?Value, ty: Type, hasher: *std.hash.Wyhash) void { + fn hashSentinel(opt_val: ?Value, ty: Type, hasher: *std.hash.Wyhash, target: Target) void { if (opt_val) |s| { std.hash.autoHash(hasher, true); - s.hash(ty, hasher); + s.hash(ty, hasher, target); } else { std.hash.autoHash(hasher, false); } } pub const HashContext64 = struct { + target: Target, + pub fn hash(self: @This(), t: Type) u64 { - _ = self; - return t.hash(); + return t.hash(self.target); } pub fn eql(self: @This(), a: Type, b: Type) bool { - _ = self; - return a.eql(b); + return a.eql(b, self.target); } }; pub const HashContext32 = struct { + target: Target, + pub fn hash(self: @This(), t: Type) u32 { - _ = self; - return @truncate(u32, t.hash()); + return @truncate(u32, t.hash(self.target)); } pub fn eql(self: @This(), a: Type, b: Type, b_index: usize) bool { - _ = self; _ = b_index; - return a.eql(b); + return a.eql(b, self.target); } }; @@ -1404,8 +1405,8 @@ pub const Type = extern union { .function => { const payload = self.castTag(.function).?.data; const param_types = try allocator.alloc(Type, payload.param_types.len); - for (payload.param_types) |param_type, i| { - param_types[i] = try param_type.copy(allocator); + for (payload.param_types) |param_ty, i| { + param_types[i] = try param_ty.copy(allocator); } const other_comptime_params = payload.comptime_params[0..payload.param_types.len]; const comptime_params = try allocator.dupe(bool, other_comptime_params); @@ -1474,14 +1475,42 @@ pub const Type = extern union { return Type{ .ptr_otherwise = &new_payload.base }; } - pub fn format( + pub fn format(ty: Type, comptime unused_fmt_string: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { + _ = ty; + _ = unused_fmt_string; + _ = options; + _ = writer; + @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()"); + } + + pub fn fmt(ty: Type, target: Target) std.fmt.Formatter(TypedValue.format) { + var ty_payload: Value.Payload.Ty = .{ + .base = .{ .tag = .ty }, + .data = ty, + }; + return .{ .data = .{ + .tv = .{ + .ty = Type.type, + .val = Value.initPayload(&ty_payload.base), + }, + .target = target, + } }; + } + + pub fn fmtDebug(ty: Type) std.fmt.Formatter(dump) { + return .{ .data = ty }; + } + + /// This is a debug function. In order to print types in a meaningful way + /// we also need access to the target. + pub fn dump( start_type: Type, - comptime fmt: []const u8, + comptime unused_format_string: []const u8, options: std.fmt.FormatOptions, writer: anytype, ) @TypeOf(writer).Error!void { _ = options; - comptime assert(fmt.len == 0); + comptime assert(unused_format_string.len == 0); var ty = start_type; while (true) { const t = ty.tag(); @@ -1584,7 +1613,7 @@ pub const Type = extern union { try writer.writeAll("fn("); for (payload.param_types) |param_type, i| { if (i != 0) try writer.writeAll(", "); - try param_type.format("", .{}, writer); + try param_type.dump("", .{}, writer); } if (payload.is_var_args) { if (payload.param_types.len != 0) { @@ -1622,7 +1651,7 @@ pub const Type = extern union { .vector => { const payload = ty.castTag(.vector).?.data; try writer.print("@Vector({d}, ", .{payload.len}); - try payload.elem_type.format("", .{}, writer); + try payload.elem_type.dump("", .{}, writer); return writer.writeAll(")"); }, .array => { @@ -1633,7 +1662,10 @@ pub const Type = extern union { }, .array_sentinel => { const payload = ty.castTag(.array_sentinel).?.data; - try writer.print("[{d}:{}]", .{ payload.len, payload.sentinel.fmtValue(payload.elem_type) }); + try writer.print("[{d}:{}]", .{ + payload.len, + payload.sentinel.fmtDebug(), + }); ty = payload.elem_type; continue; }, @@ -1646,9 +1678,9 @@ pub const Type = extern union { if (val.tag() != .unreachable_value) { try writer.writeAll("comptime "); } - try field_ty.format("", .{}, writer); + try field_ty.dump("", .{}, writer); if (val.tag() != .unreachable_value) { - try writer.print(" = {}", .{val.fmtValue(field_ty)}); + try writer.print(" = {}", .{val.fmtDebug()}); } } try writer.writeAll("}"); @@ -1665,9 +1697,9 @@ pub const Type = extern union { } try writer.writeAll(anon_struct.names[i]); try writer.writeAll(": "); - try field_ty.format("", .{}, writer); + try field_ty.dump("", .{}, writer); if (val.tag() != .unreachable_value) { - try writer.print(" = {}", .{val.fmtValue(field_ty)}); + try writer.print(" = {}", .{val.fmtDebug()}); } } try writer.writeAll("}"); @@ -1752,8 +1784,8 @@ pub const Type = extern union { const payload = ty.castTag(.pointer).?.data; if (payload.sentinel) |some| switch (payload.size) { .One, .C => unreachable, - .Many => try writer.print("[*:{}]", .{some.fmtValue(payload.pointee_type)}), - .Slice => try writer.print("[:{}]", .{some.fmtValue(payload.pointee_type)}), + .Many => try writer.print("[*:{}]", .{some.fmtDebug()}), + .Slice => try writer.print("[:{}]", .{some.fmtDebug()}), } else switch (payload.size) { .One => try writer.writeAll("*"), .Many => try writer.writeAll("[*]"), @@ -1780,7 +1812,7 @@ pub const Type = extern union { }, .error_union => { const payload = ty.castTag(.error_union).?.data; - try payload.error_set.format("", .{}, writer); + try payload.error_set.dump("", .{}, writer); try writer.writeAll("!"); ty = payload.payload; continue; @@ -1821,20 +1853,17 @@ pub const Type = extern union { } } - pub fn nameAllocArena(ty: Type, arena: Allocator) Allocator.Error![:0]const u8 { - return nameAllocAdvanced(ty, arena, true); + pub const nameAllocArena = nameAlloc; + + pub fn nameAlloc(ty: Type, ally: Allocator, target: Target) Allocator.Error![:0]const u8 { + var buffer = std.ArrayList(u8).init(ally); + defer buffer.deinit(); + try ty.print(buffer.writer(), target); + return buffer.toOwnedSliceSentinel(0); } - pub fn nameAlloc(ty: Type, gpa: Allocator) Allocator.Error![:0]const u8 { - return nameAllocAdvanced(ty, gpa, false); - } - - /// Returns a name suitable for `@typeName`. - pub fn nameAllocAdvanced( - ty: Type, - ally: Allocator, - is_arena: bool, - ) Allocator.Error![:0]const u8 { + /// Prints a name suitable for `@typeName`. + pub fn print(ty: Type, writer: anytype, target: Target) @TypeOf(writer).Error!void { const t = ty.tag(); switch (t) { .inferred_alloc_const => unreachable, @@ -1892,141 +1921,251 @@ pub const Type = extern union { .comptime_int, .comptime_float, .noreturn, - => return maybeDupe(@tagName(t), ally, is_arena), + => try writer.writeAll(@tagName(t)), - .enum_literal => return maybeDupe("@TypeOf(.enum_literal)", ally, is_arena), - .@"null" => return maybeDupe("@TypeOf(null)", ally, is_arena), - .@"undefined" => return maybeDupe("@TypeOf(undefined)", ally, is_arena), - .empty_struct_literal => return maybeDupe("@TypeOf(.{})", ally, is_arena), + .enum_literal => try writer.writeAll("@TypeOf(.enum_literal)"), + .@"null" => try writer.writeAll("@TypeOf(null)"), + .@"undefined" => try writer.writeAll("@TypeOf(undefined)"), + .empty_struct_literal => try writer.writeAll("@TypeOf(.{})"), .empty_struct => { const namespace = ty.castTag(.empty_struct).?.data; - var buffer = std.ArrayList(u8).init(ally); - defer buffer.deinit(); - try namespace.renderFullyQualifiedName("", buffer.writer()); - return buffer.toOwnedSliceSentinel(0); + try namespace.renderFullyQualifiedName("", writer); }, .@"struct" => { const struct_obj = ty.castTag(.@"struct").?.data; - return try struct_obj.owner_decl.getFullyQualifiedName(ally); + try struct_obj.owner_decl.renderFullyQualifiedName(writer); }, .@"union", .union_tagged => { const union_obj = ty.cast(Payload.Union).?.data; - return try union_obj.owner_decl.getFullyQualifiedName(ally); + try union_obj.owner_decl.renderFullyQualifiedName(writer); }, .enum_full, .enum_nonexhaustive => { const enum_full = ty.cast(Payload.EnumFull).?.data; - return try enum_full.owner_decl.getFullyQualifiedName(ally); + try enum_full.owner_decl.renderFullyQualifiedName(writer); }, .enum_simple => { const enum_simple = ty.castTag(.enum_simple).?.data; - return try enum_simple.owner_decl.getFullyQualifiedName(ally); + try enum_simple.owner_decl.renderFullyQualifiedName(writer); }, .enum_numbered => { const enum_numbered = ty.castTag(.enum_numbered).?.data; - return try enum_numbered.owner_decl.getFullyQualifiedName(ally); + try enum_numbered.owner_decl.renderFullyQualifiedName(writer); }, .@"opaque" => { const opaque_obj = ty.cast(Payload.Opaque).?.data; - return try opaque_obj.owner_decl.getFullyQualifiedName(ally); + try opaque_obj.owner_decl.renderFullyQualifiedName(writer); }, - .anyerror_void_error_union => return maybeDupe("anyerror!void", ally, is_arena), - .const_slice_u8 => return maybeDupe("[]const u8", ally, is_arena), - .const_slice_u8_sentinel_0 => return maybeDupe("[:0]const u8", ally, is_arena), - .fn_noreturn_no_args => return maybeDupe("fn() noreturn", ally, is_arena), - .fn_void_no_args => return maybeDupe("fn() void", ally, is_arena), - .fn_naked_noreturn_no_args => return maybeDupe("fn() callconv(.Naked) noreturn", ally, is_arena), - .fn_ccc_void_no_args => return maybeDupe("fn() callconv(.C) void", ally, is_arena), - .single_const_pointer_to_comptime_int => return maybeDupe("*const comptime_int", ally, is_arena), - .manyptr_u8 => return maybeDupe("[*]u8", ally, is_arena), - .manyptr_const_u8 => return maybeDupe("[*]const u8", ally, is_arena), - .manyptr_const_u8_sentinel_0 => return maybeDupe("[*:0]const u8", ally, is_arena), + .anyerror_void_error_union => try writer.writeAll("anyerror!void"), + .const_slice_u8 => try writer.writeAll("[]const u8"), + .const_slice_u8_sentinel_0 => try writer.writeAll("[:0]const u8"), + .fn_noreturn_no_args => try writer.writeAll("fn() noreturn"), + .fn_void_no_args => try writer.writeAll("fn() void"), + .fn_naked_noreturn_no_args => try writer.writeAll("fn() callconv(.Naked) noreturn"), + .fn_ccc_void_no_args => try writer.writeAll("fn() callconv(.C) void"), + .single_const_pointer_to_comptime_int => try writer.writeAll("*const comptime_int"), + .manyptr_u8 => try writer.writeAll("[*]u8"), + .manyptr_const_u8 => try writer.writeAll("[*]const u8"), + .manyptr_const_u8_sentinel_0 => try writer.writeAll("[*:0]const u8"), .error_set_inferred => { const func = ty.castTag(.error_set_inferred).?.data.func; - var buf = std.ArrayList(u8).init(ally); - defer buf.deinit(); - try buf.appendSlice("@typeInfo(@typeInfo(@TypeOf("); - try func.owner_decl.renderFullyQualifiedName(buf.writer()); - try buf.appendSlice(")).Fn.return_type.?).ErrorUnion.error_set"); - return try buf.toOwnedSliceSentinel(0); + try writer.writeAll("@typeInfo(@typeInfo(@TypeOf("); + try func.owner_decl.renderFullyQualifiedName(writer); + try writer.writeAll(")).Fn.return_type.?).ErrorUnion.error_set"); }, .function => { const fn_info = ty.fnInfo(); - var buf = std.ArrayList(u8).init(ally); - defer buf.deinit(); - try buf.appendSlice("fn("); - for (fn_info.param_types) |param_type, i| { - if (i != 0) try buf.appendSlice(", "); - const param_name = try param_type.nameAllocAdvanced(ally, is_arena); - defer if (!is_arena) ally.free(param_name); - try buf.appendSlice(param_name); + try writer.writeAll("fn("); + for (fn_info.param_types) |param_ty, i| { + if (i != 0) try writer.writeAll(", "); + try print(param_ty, writer, target); } if (fn_info.is_var_args) { if (fn_info.param_types.len != 0) { - try buf.appendSlice(", "); + try writer.writeAll(", "); } - try buf.appendSlice("..."); + try writer.writeAll("..."); } - try buf.appendSlice(") "); + try writer.writeAll(") "); if (fn_info.cc != .Unspecified) { - try buf.appendSlice("callconv(."); - try buf.appendSlice(@tagName(fn_info.cc)); - try buf.appendSlice(") "); + try writer.writeAll("callconv(."); + try writer.writeAll(@tagName(fn_info.cc)); + try writer.writeAll(") "); } if (fn_info.alignment != 0) { - try buf.writer().print("align({d}) ", .{fn_info.alignment}); + try writer.print("align({d}) ", .{fn_info.alignment}); } - { - const ret_ty_name = try fn_info.return_type.nameAllocAdvanced(ally, is_arena); - defer if (!is_arena) ally.free(ret_ty_name); - try buf.appendSlice(ret_ty_name); - } - return try buf.toOwnedSliceSentinel(0); + try print(fn_info.return_type, writer, target); }, .error_union => { const error_union = ty.castTag(.error_union).?.data; - - var buf = std.ArrayList(u8).init(ally); - defer buf.deinit(); - - { - const err_set_ty_name = try error_union.error_set.nameAllocAdvanced(ally, is_arena); - defer if (!is_arena) ally.free(err_set_ty_name); - try buf.appendSlice(err_set_ty_name); - } - - try buf.appendSlice("!"); - - { - const payload_ty_name = try error_union.payload.nameAllocAdvanced(ally, is_arena); - defer if (!is_arena) ally.free(payload_ty_name); - try buf.appendSlice(payload_ty_name); - } - - return try buf.toOwnedSliceSentinel(0); + try print(error_union.error_set, writer, target); + try writer.writeAll("!"); + try print(error_union.payload, writer, target); }, - else => { - // TODO this is wasteful and also an incorrect implementation of `@typeName` - var buf = std.ArrayList(u8).init(ally); - defer buf.deinit(); - try buf.writer().print("{}", .{ty}); - return try buf.toOwnedSliceSentinel(0); + .array_u8 => { + const len = ty.castTag(.array_u8).?.data; + try writer.print("[{d}]u8", .{len}); }, - } - } + .array_u8_sentinel_0 => { + const len = ty.castTag(.array_u8_sentinel_0).?.data; + try writer.print("[{d}:0]u8", .{len}); + }, + .vector => { + const payload = ty.castTag(.vector).?.data; + try writer.print("@Vector({d}, ", .{payload.len}); + try print(payload.elem_type, writer, target); + try writer.writeAll(")"); + }, + .array => { + const payload = ty.castTag(.array).?.data; + try writer.print("[{d}]", .{payload.len}); + try print(payload.elem_type, writer, target); + }, + .array_sentinel => { + const payload = ty.castTag(.array_sentinel).?.data; + try writer.print("[{d}:{}]", .{ + payload.len, + payload.sentinel.fmtValue(payload.elem_type, target), + }); + try print(payload.elem_type, writer, target); + }, + .tuple => { + const tuple = ty.castTag(.tuple).?.data; - fn maybeDupe(s: [:0]const u8, ally: Allocator, is_arena: bool) Allocator.Error![:0]const u8 { - if (is_arena) { - return s; - } else { - return try ally.dupeZ(u8, s); + try writer.writeAll("tuple{"); + for (tuple.types) |field_ty, i| { + if (i != 0) try writer.writeAll(", "); + const val = tuple.values[i]; + if (val.tag() != .unreachable_value) { + try writer.writeAll("comptime "); + } + try print(field_ty, writer, target); + if (val.tag() != .unreachable_value) { + try writer.print(" = {}", .{val.fmtValue(field_ty, target)}); + } + } + try writer.writeAll("}"); + }, + .anon_struct => { + const anon_struct = ty.castTag(.anon_struct).?.data; + + try writer.writeAll("struct{"); + for (anon_struct.types) |field_ty, i| { + if (i != 0) try writer.writeAll(", "); + const val = anon_struct.values[i]; + if (val.tag() != .unreachable_value) { + try writer.writeAll("comptime "); + } + try writer.writeAll(anon_struct.names[i]); + try writer.writeAll(": "); + + try print(field_ty, writer, target); + + if (val.tag() != .unreachable_value) { + try writer.print(" = {}", .{val.fmtValue(field_ty, target)}); + } + } + try writer.writeAll("}"); + }, + + .pointer, + .single_const_pointer, + .single_mut_pointer, + .many_const_pointer, + .many_mut_pointer, + .c_const_pointer, + .c_mut_pointer, + .const_slice, + .mut_slice, + => { + const info = ty.ptrInfo().data; + + if (info.sentinel) |s| switch (info.size) { + .One, .C => unreachable, + .Many => try writer.print("[*:{}]", .{s.fmtValue(info.pointee_type, target)}), + .Slice => try writer.print("[:{}]", .{s.fmtValue(info.pointee_type, target)}), + } else switch (info.size) { + .One => try writer.writeAll("*"), + .Many => try writer.writeAll("[*]"), + .C => try writer.writeAll("[*c]"), + .Slice => try writer.writeAll("[]"), + } + if (info.@"align" != 0 or info.host_size != 0) { + try writer.print("align({d}", .{info.@"align"}); + + if (info.bit_offset != 0) { + try writer.print(":{d}:{d}", .{ info.bit_offset, info.host_size }); + } + try writer.writeAll(") "); + } + if (info.@"addrspace" != .generic) { + try writer.print("addrspace(.{s}) ", .{@tagName(info.@"addrspace")}); + } + if (!info.mutable) try writer.writeAll("const "); + if (info.@"volatile") try writer.writeAll("volatile "); + if (info.@"allowzero" and info.size != .C) try writer.writeAll("allowzero "); + + try print(info.pointee_type, writer, target); + }, + + .int_signed => { + const bits = ty.castTag(.int_signed).?.data; + return writer.print("i{d}", .{bits}); + }, + .int_unsigned => { + const bits = ty.castTag(.int_unsigned).?.data; + return writer.print("u{d}", .{bits}); + }, + .optional => { + const child_type = ty.castTag(.optional).?.data; + try writer.writeByte('?'); + try print(child_type, writer, target); + }, + .optional_single_mut_pointer => { + const pointee_type = ty.castTag(.optional_single_mut_pointer).?.data; + try writer.writeAll("?*"); + try print(pointee_type, writer, target); + }, + .optional_single_const_pointer => { + const pointee_type = ty.castTag(.optional_single_const_pointer).?.data; + try writer.writeAll("?*const "); + try print(pointee_type, writer, target); + }, + .anyframe_T => { + const return_type = ty.castTag(.anyframe_T).?.data; + try writer.print("anyframe->", .{}); + try print(return_type, writer, target); + }, + .error_set => { + const names = ty.castTag(.error_set).?.data.names.keys(); + try writer.writeAll("error{"); + for (names) |name, i| { + if (i != 0) try writer.writeByte(','); + try writer.writeAll(name); + } + try writer.writeAll("}"); + }, + .error_set_single => { + const name = ty.castTag(.error_set_single).?.data; + return writer.print("error{{{s}}}", .{name}); + }, + .error_set_merged => { + const names = ty.castTag(.error_set_merged).?.data.keys(); + try writer.writeAll("error{"); + for (names) |name, i| { + if (i != 0) try writer.writeByte(','); + try writer.writeAll(name); + } + try writer.writeAll("}"); + }, } } @@ -2518,8 +2657,33 @@ pub const Type = extern union { } /// Returns 0 for 0-bit types. - pub fn abiAlignment(self: Type, target: Target) u32 { - return switch (self.tag()) { + pub fn abiAlignment(ty: Type, target: Target) u32 { + return ty.abiAlignmentAdvanced(target, .eager).scalar; + } + + /// May capture a reference to `ty`. + pub fn lazyAbiAlignment(ty: Type, target: Target, arena: Allocator) !Value { + switch (ty.abiAlignmentAdvanced(target, .{ .lazy = arena })) { + .val => |val| return try val, + .scalar => |x| return Value.Tag.int_u64.create(arena, x), + } + } + + /// If you pass `eager` you will get back `scalar` and assert the type is resolved. + /// If you pass `lazy` you may get back `scalar` or `val`. + /// If `val` is returned, a reference to `ty` has been captured. + fn abiAlignmentAdvanced( + ty: Type, + target: Target, + strat: union(enum) { + eager, + lazy: Allocator, + }, + ) union(enum) { + scalar: u32, + val: Allocator.Error!Value, + } { + return switch (ty.tag()) { .u1, .u8, .i8, @@ -2538,25 +2702,25 @@ pub const Type = extern union { .extern_options, .@"opaque", .anyopaque, - => return 1, + => return .{ .scalar = 1 }, .fn_noreturn_no_args, // represents machine code; not a pointer .fn_void_no_args, // represents machine code; not a pointer .fn_naked_noreturn_no_args, // represents machine code; not a pointer .fn_ccc_void_no_args, // represents machine code; not a pointer - => return target_util.defaultFunctionAlignment(target), + => return .{ .scalar = target_util.defaultFunctionAlignment(target) }, // represents machine code; not a pointer .function => { - const alignment = self.castTag(.function).?.data.alignment; - if (alignment != 0) return alignment; - return target_util.defaultFunctionAlignment(target); + const alignment = ty.castTag(.function).?.data.alignment; + if (alignment != 0) return .{ .scalar = alignment }; + return .{ .scalar = target_util.defaultFunctionAlignment(target) }; }, - .i16, .u16 => return 2, - .i32, .u32 => return 4, - .i64, .u64 => return 8, - .u128, .i128 => return 16, + .i16, .u16 => return .{ .scalar = 2 }, + .i32, .u32 => return .{ .scalar = 4 }, + .i64, .u64 => return .{ .scalar = 8 }, + .u128, .i128 => return .{ .scalar = 16 }, .isize, .usize, @@ -2579,40 +2743,40 @@ pub const Type = extern union { .manyptr_const_u8_sentinel_0, .@"anyframe", .anyframe_T, - => return @divExact(target.cpu.arch.ptrBitWidth(), 8), + => return .{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }, - .c_short => return @divExact(CType.short.sizeInBits(target), 8), - .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8), - .c_int => return @divExact(CType.int.sizeInBits(target), 8), - .c_uint => return @divExact(CType.uint.sizeInBits(target), 8), - .c_long => return @divExact(CType.long.sizeInBits(target), 8), - .c_ulong => return @divExact(CType.ulong.sizeInBits(target), 8), - .c_longlong => return @divExact(CType.longlong.sizeInBits(target), 8), - .c_ulonglong => return @divExact(CType.ulonglong.sizeInBits(target), 8), + .c_short => return .{ .scalar = @divExact(CType.short.sizeInBits(target), 8) }, + .c_ushort => return .{ .scalar = @divExact(CType.ushort.sizeInBits(target), 8) }, + .c_int => return .{ .scalar = @divExact(CType.int.sizeInBits(target), 8) }, + .c_uint => return .{ .scalar = @divExact(CType.uint.sizeInBits(target), 8) }, + .c_long => return .{ .scalar = @divExact(CType.long.sizeInBits(target), 8) }, + .c_ulong => return .{ .scalar = @divExact(CType.ulong.sizeInBits(target), 8) }, + .c_longlong => return .{ .scalar = @divExact(CType.longlong.sizeInBits(target), 8) }, + .c_ulonglong => return .{ .scalar = @divExact(CType.ulonglong.sizeInBits(target), 8) }, - .f16 => return 2, - .f32 => return 4, - .f64 => return 8, - .f128 => return 16, + .f16 => return .{ .scalar = 2 }, + .f32 => return .{ .scalar = 4 }, + .f64 => return .{ .scalar = 8 }, + .f128 => return .{ .scalar = 16 }, .f80 => switch (target.cpu.arch) { - .i386 => return 4, - .x86_64 => return 16, + .i386 => return .{ .scalar = 4 }, + .x86_64 => return .{ .scalar = 16 }, else => { var payload: Payload.Bits = .{ .base = .{ .tag = .int_unsigned }, .data = 80, }; const u80_ty = initPayload(&payload.base); - return abiAlignment(u80_ty, target); + return .{ .scalar = abiAlignment(u80_ty, target) }; }, }, .c_longdouble => switch (CType.longdouble.sizeInBits(target)) { - 16 => return abiAlignment(Type.f16, target), - 32 => return abiAlignment(Type.f32, target), - 64 => return abiAlignment(Type.f64, target), - 80 => return abiAlignment(Type.f80, target), - 128 => return abiAlignment(Type.f128, target), + 16 => return .{ .scalar = abiAlignment(Type.f16, target) }, + 32 => return .{ .scalar = abiAlignment(Type.f32, target) }, + 64 => return .{ .scalar = abiAlignment(Type.f64, target) }, + 80 => return .{ .scalar = abiAlignment(Type.f80, target) }, + 128 => return .{ .scalar = abiAlignment(Type.f128, target) }, else => unreachable, }, @@ -2622,60 +2786,93 @@ pub const Type = extern union { .anyerror, .error_set_inferred, .error_set_merged, - => return 2, // TODO revisit this when we have the concept of the error tag type + => return .{ .scalar = 2 }, // TODO revisit this when we have the concept of the error tag type - .array, .array_sentinel => return self.elemType().abiAlignment(target), + .array, .array_sentinel => return ty.elemType().abiAlignmentAdvanced(target, strat), // TODO audit this - is there any more complicated logic to determine // ABI alignment of vectors? - .vector => return 16, + .vector => return .{ .scalar = 16 }, .int_signed, .int_unsigned => { - const bits: u16 = self.cast(Payload.Bits).?.data; - if (bits == 0) return 0; - if (bits <= 8) return 1; - if (bits <= 16) return 2; - if (bits <= 32) return 4; - if (bits <= 64) return 8; - return 16; + const bits: u16 = ty.cast(Payload.Bits).?.data; + if (bits == 0) return .{ .scalar = 0 }; + if (bits <= 8) return .{ .scalar = 1 }; + if (bits <= 16) return .{ .scalar = 2 }; + if (bits <= 32) return .{ .scalar = 4 }; + if (bits <= 64) return .{ .scalar = 8 }; + return .{ .scalar = 16 }; }, .optional => { var buf: Payload.ElemType = undefined; - const child_type = self.optionalChild(&buf); - if (!child_type.hasRuntimeBits()) return 1; + const child_type = ty.optionalChild(&buf); - if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr()) - return @divExact(target.cpu.arch.ptrBitWidth(), 8); + if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr()) { + return .{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }; + } - return child_type.abiAlignment(target); + switch (strat) { + .eager => { + if (!child_type.hasRuntimeBits()) return .{ .scalar = 1 }; + return .{ .scalar = child_type.abiAlignment(target) }; + }, + .lazy => |arena| switch (child_type.abiAlignmentAdvanced(target, strat)) { + .scalar => |x| return .{ .scalar = @maximum(x, 1) }, + .val => return .{ .val = Value.Tag.lazy_align.create(arena, ty) }, + }, + } }, .error_union => { - const data = self.castTag(.error_union).?.data; - if (!data.error_set.hasRuntimeBits()) { - return data.payload.abiAlignment(target); - } else if (!data.payload.hasRuntimeBits()) { - return data.error_set.abiAlignment(target); + const data = ty.castTag(.error_union).?.data; + switch (strat) { + .eager => { + if (!data.error_set.hasRuntimeBits()) { + return .{ .scalar = data.payload.abiAlignment(target) }; + } else if (!data.payload.hasRuntimeBits()) { + return .{ .scalar = data.error_set.abiAlignment(target) }; + } + return .{ .scalar = @maximum( + data.payload.abiAlignment(target), + data.error_set.abiAlignment(target), + ) }; + }, + .lazy => |arena| { + switch (data.payload.abiAlignmentAdvanced(target, strat)) { + .scalar => |payload_align| { + if (payload_align == 0) { + return data.error_set.abiAlignmentAdvanced(target, strat); + } + switch (data.error_set.abiAlignmentAdvanced(target, strat)) { + .scalar => |err_set_align| { + return .{ .scalar = @maximum(payload_align, err_set_align) }; + }, + .val => {}, + } + }, + .val => {}, + } + return .{ .val = Value.Tag.lazy_align.create(arena, ty) }; + }, } - return @maximum( - data.payload.abiAlignment(target), - data.error_set.abiAlignment(target), - ); }, .@"struct" => { - const fields = self.structFields(); - if (self.castTag(.@"struct")) |payload| { + if (ty.castTag(.@"struct")) |payload| { const struct_obj = payload.data; - assert(struct_obj.haveLayout()); + if (!struct_obj.haveLayout()) switch (strat) { + .eager => unreachable, // struct layout not resolved + .lazy => |arena| return .{ .val = Value.Tag.lazy_align.create(arena, ty) }, + }; if (struct_obj.layout == .Packed) { var buf: Type.Payload.Bits = undefined; const int_ty = struct_obj.packedIntegerType(target, &buf); - return int_ty.abiAlignment(target); + return .{ .scalar = int_ty.abiAlignment(target) }; } } + const fields = ty.structFields(); var big_align: u32 = 0; for (fields.values()) |field| { if (!field.ty.hasRuntimeBits()) continue; @@ -2683,31 +2880,45 @@ pub const Type = extern union { const field_align = field.normalAlignment(target); big_align = @maximum(big_align, field_align); } - return big_align; + return .{ .scalar = big_align }; }, .tuple, .anon_struct => { - const tuple = self.tupleFields(); + const tuple = ty.tupleFields(); var big_align: u32 = 0; for (tuple.types) |field_ty, i| { const val = tuple.values[i]; if (val.tag() != .unreachable_value) continue; // comptime field - if (!field_ty.hasRuntimeBits()) continue; - const field_align = field_ty.abiAlignment(target); - big_align = @maximum(big_align, field_align); + switch (field_ty.abiAlignmentAdvanced(target, strat)) { + .scalar => |field_align| big_align = @maximum(big_align, field_align), + .val => switch (strat) { + .eager => unreachable, // field type alignment not resolved + .lazy => |arena| return .{ .val = Value.Tag.lazy_align.create(arena, ty) }, + }, + } } - return big_align; + return .{ .scalar = big_align }; }, .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => { var buffer: Payload.Bits = undefined; - const int_tag_ty = self.intTagType(&buffer); - return int_tag_ty.abiAlignment(target); + const int_tag_ty = ty.intTagType(&buffer); + return .{ .scalar = int_tag_ty.abiAlignment(target) }; + }, + .@"union" => switch (strat) { + .eager => { + // TODO pass `true` for have_tag when unions have a safety tag + return .{ .scalar = ty.castTag(.@"union").?.data.abiAlignment(target, false) }; + }, + .lazy => |arena| return .{ .val = Value.Tag.lazy_align.create(arena, ty) }, + }, + .union_tagged => switch (strat) { + .eager => { + return .{ .scalar = ty.castTag(.union_tagged).?.data.abiAlignment(target, true) }; + }, + .lazy => |arena| return .{ .val = Value.Tag.lazy_align.create(arena, ty) }, }, - // TODO pass `true` for have_tag when unions have a safety tag - .@"union" => return self.castTag(.@"union").?.data.abiAlignment(target, false), - .union_tagged => return self.castTag(.union_tagged).?.data.abiAlignment(target, true), .empty_struct, .void, @@ -2719,7 +2930,7 @@ pub const Type = extern union { .@"undefined", .enum_literal, .type_info, - => return 0, + => return .{ .scalar = 0 }, .noreturn, .inferred_alloc_const, @@ -3392,10 +3603,7 @@ pub const Type = extern union { .optional => { const child_ty = self.castTag(.optional).?.data; - // optionals of zero sized types behave like bools, not pointers - if (!child_ty.hasRuntimeBits()) return false; if (child_ty.zigTypeTag() != .Pointer) return false; - const info = child_ty.ptrInfo().data; switch (info.size) { .Slice, .C => return false, @@ -3663,9 +3871,9 @@ pub const Type = extern union { return union_obj.fields; } - pub fn unionFieldType(ty: Type, enum_tag: Value) Type { + pub fn unionFieldType(ty: Type, enum_tag: Value, target: Target) Type { const union_obj = ty.cast(Payload.Union).?.data; - const index = union_obj.tag_ty.enumTagFieldIndex(enum_tag).?; + const index = union_obj.tag_ty.enumTagFieldIndex(enum_tag, target).?; assert(union_obj.haveFieldTypes()); return union_obj.fields.values()[index].ty; } @@ -4679,20 +4887,20 @@ pub const Type = extern union { /// Asserts `ty` is an enum. `enum_tag` can either be `enum_field_index` or /// an integer which represents the enum value. Returns the field index in /// declaration order, or `null` if `enum_tag` does not match any field. - pub fn enumTagFieldIndex(ty: Type, enum_tag: Value) ?usize { + pub fn enumTagFieldIndex(ty: Type, enum_tag: Value, target: Target) ?usize { if (enum_tag.castTag(.enum_field_index)) |payload| { return @as(usize, payload.data); } const S = struct { - fn fieldWithRange(int_ty: Type, int_val: Value, end: usize) ?usize { + fn fieldWithRange(int_ty: Type, int_val: Value, end: usize, tg: Target) ?usize { if (int_val.compareWithZero(.lt)) return null; var end_payload: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = end, }; const end_val = Value.initPayload(&end_payload.base); - if (int_val.compare(.gte, end_val, int_ty)) return null; - return @intCast(usize, int_val.toUnsignedInt()); + if (int_val.compare(.gte, end_val, int_ty, tg)) return null; + return @intCast(usize, int_val.toUnsignedInt(tg)); } }; switch (ty.tag()) { @@ -4700,18 +4908,24 @@ pub const Type = extern union { const enum_full = ty.cast(Payload.EnumFull).?.data; const tag_ty = enum_full.tag_ty; if (enum_full.values.count() == 0) { - return S.fieldWithRange(tag_ty, enum_tag, enum_full.fields.count()); + return S.fieldWithRange(tag_ty, enum_tag, enum_full.fields.count(), target); } else { - return enum_full.values.getIndexContext(enum_tag, .{ .ty = tag_ty }); + return enum_full.values.getIndexContext(enum_tag, .{ + .ty = tag_ty, + .target = target, + }); } }, .enum_numbered => { const enum_obj = ty.castTag(.enum_numbered).?.data; const tag_ty = enum_obj.tag_ty; if (enum_obj.values.count() == 0) { - return S.fieldWithRange(tag_ty, enum_tag, enum_obj.fields.count()); + return S.fieldWithRange(tag_ty, enum_tag, enum_obj.fields.count(), target); } else { - return enum_obj.values.getIndexContext(enum_tag, .{ .ty = tag_ty }); + return enum_obj.values.getIndexContext(enum_tag, .{ + .ty = tag_ty, + .target = target, + }); } }, .enum_simple => { @@ -4723,7 +4937,7 @@ pub const Type = extern union { .data = bits, }; const tag_ty = Type.initPayload(&buffer.base); - return S.fieldWithRange(tag_ty, enum_tag, fields_len); + return S.fieldWithRange(tag_ty, enum_tag, fields_len, target); }, .atomic_order, .atomic_rmw_op, @@ -5018,14 +5232,14 @@ pub const Type = extern union { /// Asserts the type is an enum. pub fn enumHasInt(ty: Type, int: Value, target: Target) bool { const S = struct { - fn intInRange(tag_ty: Type, int_val: Value, end: usize) bool { + fn intInRange(tag_ty: Type, int_val: Value, end: usize, tg: Target) bool { if (int_val.compareWithZero(.lt)) return false; var end_payload: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = end, }; const end_val = Value.initPayload(&end_payload.base); - if (int_val.compare(.gte, end_val, tag_ty)) return false; + if (int_val.compare(.gte, end_val, tag_ty, tg)) return false; return true; } }; @@ -5035,18 +5249,24 @@ pub const Type = extern union { const enum_full = ty.castTag(.enum_full).?.data; const tag_ty = enum_full.tag_ty; if (enum_full.values.count() == 0) { - return S.intInRange(tag_ty, int, enum_full.fields.count()); + return S.intInRange(tag_ty, int, enum_full.fields.count(), target); } else { - return enum_full.values.containsContext(int, .{ .ty = tag_ty }); + return enum_full.values.containsContext(int, .{ + .ty = tag_ty, + .target = target, + }); } }, .enum_numbered => { const enum_obj = ty.castTag(.enum_numbered).?.data; const tag_ty = enum_obj.tag_ty; if (enum_obj.values.count() == 0) { - return S.intInRange(tag_ty, int, enum_obj.fields.count()); + return S.intInRange(tag_ty, int, enum_obj.fields.count(), target); } else { - return enum_obj.values.containsContext(int, .{ .ty = tag_ty }); + return enum_obj.values.containsContext(int, .{ + .ty = tag_ty, + .target = target, + }); } }, .enum_simple => { @@ -5058,7 +5278,7 @@ pub const Type = extern union { .data = bits, }; const tag_ty = Type.initPayload(&buffer.base); - return S.intInRange(tag_ty, int, fields_len); + return S.intInRange(tag_ty, int, fields_len, target); }, .atomic_order, .atomic_rmw_op, @@ -5070,7 +5290,7 @@ pub const Type = extern union { .prefetch_options, .export_options, .extern_options, - => @panic("TODO resolve std.builtin types"), + => unreachable, else => unreachable, } @@ -5620,7 +5840,7 @@ pub const Type = extern union { d.bit_offset == 0 and d.host_size == 0 and !d.@"allowzero" and !d.@"volatile") { if (d.sentinel) |sent| { - if (!d.mutable and d.pointee_type.eql(Type.u8)) { + if (!d.mutable and d.pointee_type.eql(Type.u8, target)) { switch (d.size) { .Slice => { if (sent.compareWithZero(.eq)) { @@ -5635,7 +5855,7 @@ pub const Type = extern union { else => {}, } } - } else if (!d.mutable and d.pointee_type.eql(Type.u8)) { + } else if (!d.mutable and d.pointee_type.eql(Type.u8, target)) { switch (d.size) { .Slice => return Type.initTag(.const_slice_u8), .Many => return Type.initTag(.manyptr_const_u8), @@ -5669,10 +5889,11 @@ pub const Type = extern union { len: u64, sent: ?Value, elem_type: Type, + target: Target, ) Allocator.Error!Type { - if (elem_type.eql(Type.u8)) { + if (elem_type.eql(Type.u8, target)) { if (sent) |some| { - if (some.eql(Value.zero, elem_type)) { + if (some.eql(Value.zero, elem_type, target)) { return Tag.array_u8_sentinel_0.create(arena, len); } } else { @@ -5715,6 +5936,25 @@ pub const Type = extern union { } } + pub fn errorUnion( + arena: Allocator, + error_set: Type, + payload: Type, + target: Target, + ) Allocator.Error!Type { + assert(error_set.zigTypeTag() == .ErrorSet); + if (error_set.eql(Type.@"anyerror", target) and + payload.eql(Type.void, target)) + { + return Type.initTag(.anyerror_void_error_union); + } + + return Type.Tag.error_union.create(arena, .{ + .error_set = error_set, + .payload = payload, + }); + } + pub fn smallestUnsignedBits(max: u64) u16 { if (max == 0) return 0; const base = std.math.log2(max); diff --git a/src/value.zig b/src/value.zig index 454b1d76a2..b54f296a24 100644 --- a/src/value.zig +++ b/src/value.zig @@ -8,6 +8,7 @@ const Target = std.Target; const Allocator = std.mem.Allocator; const Module = @import("Module.zig"); const Air = @import("Air.zig"); +const TypedValue = @import("TypedValue.zig"); /// This is the raw data, with no bookkeeping, no memory awareness, /// no de-duplication, and no type system awareness. @@ -175,6 +176,8 @@ pub const Value = extern union { /// and refers directly to the air. It will never be referenced by the air itself. /// TODO: This is probably a bad encoding, maybe put temp data in the sema instead. bound_fn, + /// The ABI alignment of the payload type. + lazy_align, pub const last_no_payload_tag = Tag.empty_array; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -283,7 +286,10 @@ pub const Value = extern union { .enum_field_index => Payload.U32, - .ty => Payload.Ty, + .ty, + .lazy_align, + => Payload.Ty, + .int_type => Payload.IntType, .int_u64 => Payload.U64, .int_i64 => Payload.I64, @@ -453,7 +459,7 @@ pub const Value = extern union { .bound_fn, => unreachable, - .ty => { + .ty, .lazy_align => { const payload = self.castTag(.ty).?; const new_payload = try arena.create(Payload.Ty); new_payload.* = .{ @@ -608,7 +614,7 @@ pub const Value = extern union { @compileError("do not use format values directly; use either fmtDebug or fmtValue"); } - /// TODO this should become a debug dump() function. In order to print values in a meaningful way + /// This is a debug function. In order to print values in a meaningful way /// we also need access to the type. pub fn dump( start_val: Value, @@ -699,7 +705,12 @@ pub const Value = extern union { .the_only_possible_value => return out_stream.writeAll("(the only possible value)"), .bool_true => return out_stream.writeAll("true"), .bool_false => return out_stream.writeAll("false"), - .ty => return val.castTag(.ty).?.data.format("", options, out_stream), + .ty => return val.castTag(.ty).?.data.dump("", options, out_stream), + .lazy_align => { + try out_stream.writeAll("@alignOf("); + try val.castTag(.lazy_align).?.data.dump("", options, out_stream); + try out_stream.writeAll(")"); + }, .int_type => { const int_type = val.castTag(.int_type).?.data; return out_stream.print("{s}{d}", .{ @@ -778,15 +789,16 @@ pub const Value = extern union { return .{ .data = val }; } - const TypedValue = @import("TypedValue.zig"); - - pub fn fmtValue(val: Value, ty: Type) std.fmt.Formatter(TypedValue.format) { - return .{ .data = .{ .ty = ty, .val = val } }; + pub fn fmtValue(val: Value, ty: Type, target: Target) std.fmt.Formatter(TypedValue.format) { + return .{ .data = .{ + .tv = .{ .ty = ty, .val = val }, + .target = target, + } }; } /// Asserts that the value is representable as an array of bytes. /// Copies the value into a freshly allocated slice of memory, which is owned by the caller. - pub fn toAllocatedBytes(val: Value, ty: Type, allocator: Allocator) ![]u8 { + pub fn toAllocatedBytes(val: Value, ty: Type, allocator: Allocator, target: Target) ![]u8 { switch (val.tag()) { .bytes => { const bytes = val.castTag(.bytes).?.data; @@ -796,7 +808,7 @@ pub const Value = extern union { }, .enum_literal => return allocator.dupe(u8, val.castTag(.enum_literal).?.data), .repeated => { - const byte = @intCast(u8, val.castTag(.repeated).?.data.toUnsignedInt()); + const byte = @intCast(u8, val.castTag(.repeated).?.data.toUnsignedInt(target)); const result = try allocator.alloc(u8, @intCast(usize, ty.arrayLen())); std.mem.set(u8, result, byte); return result; @@ -804,23 +816,23 @@ pub const Value = extern union { .decl_ref => { const decl = val.castTag(.decl_ref).?.data; const decl_val = try decl.value(); - return decl_val.toAllocatedBytes(decl.ty, allocator); + return decl_val.toAllocatedBytes(decl.ty, allocator, target); }, .the_only_possible_value => return &[_]u8{}, .slice => { const slice = val.castTag(.slice).?.data; - return arrayToAllocatedBytes(slice.ptr, slice.len.toUnsignedInt(), allocator); + return arrayToAllocatedBytes(slice.ptr, slice.len.toUnsignedInt(target), allocator, target); }, - else => return arrayToAllocatedBytes(val, ty.arrayLen(), allocator), + else => return arrayToAllocatedBytes(val, ty.arrayLen(), allocator, target), } } - fn arrayToAllocatedBytes(val: Value, len: u64, allocator: Allocator) ![]u8 { + fn arrayToAllocatedBytes(val: Value, len: u64, allocator: Allocator, target: Target) ![]u8 { const result = try allocator.alloc(u8, @intCast(usize, len)); var elem_value_buf: ElemValueBuffer = undefined; for (result) |*elem, i| { const elem_val = val.elemValueBuffer(i, &elem_value_buf); - elem.* = @intCast(u8, elem_val.toUnsignedInt()); + elem.* = @intCast(u8, elem_val.toUnsignedInt(target)); } return result; } @@ -977,8 +989,8 @@ pub const Value = extern union { } /// Asserts the value is an integer. - pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst { - switch (self.tag()) { + pub fn toBigInt(val: Value, space: *BigIntSpace, target: Target) BigIntConst { + switch (val.tag()) { .zero, .bool_false, .the_only_possible_value, // i0, u0 @@ -988,19 +1000,25 @@ pub const Value = extern union { .bool_true, => return BigIntMutable.init(&space.limbs, 1).toConst(), - .int_u64 => return BigIntMutable.init(&space.limbs, self.castTag(.int_u64).?.data).toConst(), - .int_i64 => return BigIntMutable.init(&space.limbs, self.castTag(.int_i64).?.data).toConst(), - .int_big_positive => return self.castTag(.int_big_positive).?.asBigInt(), - .int_big_negative => return self.castTag(.int_big_negative).?.asBigInt(), + .int_u64 => return BigIntMutable.init(&space.limbs, val.castTag(.int_u64).?.data).toConst(), + .int_i64 => return BigIntMutable.init(&space.limbs, val.castTag(.int_i64).?.data).toConst(), + .int_big_positive => return val.castTag(.int_big_positive).?.asBigInt(), + .int_big_negative => return val.castTag(.int_big_negative).?.asBigInt(), .undef => unreachable, + + .lazy_align => { + const x = val.castTag(.lazy_align).?.data.abiAlignment(target); + return BigIntMutable.init(&space.limbs, x).toConst(); + }, + else => unreachable, } } /// If the value fits in a u64, return it, otherwise null. /// Asserts not undefined. - pub fn getUnsignedInt(val: Value) ?u64 { + pub fn getUnsignedInt(val: Value, target: Target) ?u64 { switch (val.tag()) { .zero, .bool_false, @@ -1017,13 +1035,16 @@ pub const Value = extern union { .int_big_negative => return val.castTag(.int_big_negative).?.asBigInt().to(u64) catch null, .undef => unreachable, + + .lazy_align => return val.castTag(.lazy_align).?.data.abiAlignment(target), + else => return null, } } /// Asserts the value is an integer and it fits in a u64 - pub fn toUnsignedInt(val: Value) u64 { - return getUnsignedInt(val).?; + pub fn toUnsignedInt(val: Value, target: Target) u64 { + return getUnsignedInt(val, target).?; } /// Asserts the value is an integer and it fits in a i64 @@ -1066,7 +1087,7 @@ pub const Value = extern union { switch (ty.zigTypeTag()) { .Int => { var bigint_buffer: BigIntSpace = undefined; - const bigint = val.toBigInt(&bigint_buffer); + const bigint = val.toBigInt(&bigint_buffer, target); const bits = ty.intInfo(target).bits; const abi_size = @intCast(usize, ty.abiSize(target)); bigint.writeTwosComplement(buffer, bits, abi_size, target.cpu.arch.endian()); @@ -1075,7 +1096,7 @@ pub const Value = extern union { var enum_buffer: Payload.U64 = undefined; const int_val = val.enumToInt(ty, &enum_buffer); var bigint_buffer: BigIntSpace = undefined; - const bigint = int_val.toBigInt(&bigint_buffer); + const bigint = int_val.toBigInt(&bigint_buffer, target); const bits = ty.intInfo(target).bits; const abi_size = @intCast(usize, ty.abiSize(target)); bigint.writeTwosComplement(buffer, bits, abi_size, target.cpu.arch.endian()); @@ -1151,7 +1172,7 @@ pub const Value = extern union { 128 => bitcastFloatToBigInt(f128, val.toFloat(f128), &field_buf), else => unreachable, }, - .Int, .Bool => field_val.toBigInt(&field_space), + .Int, .Bool => field_val.toBigInt(&field_space, target), .Struct => packedStructToInt(field_val, field.ty, target, &field_buf), else => unreachable, }; @@ -1511,7 +1532,7 @@ pub const Value = extern union { const info = ty.intInfo(target); var buffer: Value.BigIntSpace = undefined; - const operand_bigint = val.toBigInt(&buffer); + const operand_bigint = val.toBigInt(&buffer, target); var limbs_buffer: [4]std.math.big.Limb = undefined; var result_bigint = BigIntMutable{ @@ -1532,7 +1553,7 @@ pub const Value = extern union { const info = ty.intInfo(target); var buffer: Value.BigIntSpace = undefined; - const operand_bigint = val.toBigInt(&buffer); + const operand_bigint = val.toBigInt(&buffer, target); const limbs = try arena.alloc( std.math.big.Limb, @@ -1553,7 +1574,7 @@ pub const Value = extern union { assert(info.bits % 8 == 0); var buffer: Value.BigIntSpace = undefined; - const operand_bigint = val.toBigInt(&buffer); + const operand_bigint = val.toBigInt(&buffer, target); const limbs = try arena.alloc( std.math.big.Limb, @@ -1597,7 +1618,7 @@ pub const Value = extern union { else => { var buffer: BigIntSpace = undefined; - return self.toBigInt(&buffer).bitCountTwosComp(); + return self.toBigInt(&buffer, target).bitCountTwosComp(); }, } } @@ -1624,6 +1645,17 @@ pub const Value = extern union { else => unreachable, }, + .lazy_align => { + const info = ty.intInfo(target); + const max_needed_bits = @as(u16, 16) + @boolToInt(info.signedness == .signed); + // If it is u16 or bigger we know the alignment fits without resolving it. + if (info.bits >= max_needed_bits) return true; + const x = self.castTag(.lazy_align).?.data.abiAlignment(target); + if (x == 0) return true; + const actual_needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed); + return info.bits >= actual_needed_bits; + }, + .int_u64 => switch (ty.zigTypeTag()) { .Int => { const x = self.castTag(.int_u64).?.data; @@ -1643,7 +1675,7 @@ pub const Value = extern union { if (info.signedness == .unsigned and x < 0) return false; var buffer: BigIntSpace = undefined; - return self.toBigInt(&buffer).fitsInTwosComp(info.signedness, info.bits); + return self.toBigInt(&buffer, target).fitsInTwosComp(info.signedness, info.bits); }, .ComptimeInt => return true, else => unreachable, @@ -1765,6 +1797,15 @@ pub const Value = extern union { .int_big_positive => lhs.castTag(.int_big_positive).?.asBigInt().orderAgainstScalar(0), .int_big_negative => lhs.castTag(.int_big_negative).?.asBigInt().orderAgainstScalar(0), + .lazy_align => { + const ty = lhs.castTag(.lazy_align).?.data; + if (ty.hasRuntimeBitsIgnoreComptime()) { + return .gt; + } else { + return .eq; + } + }, + .float_16 => std.math.order(lhs.castTag(.float_16).?.data, 0), .float_32 => std.math.order(lhs.castTag(.float_32).?.data, 0), .float_64 => std.math.order(lhs.castTag(.float_64).?.data, 0), @@ -1776,7 +1817,7 @@ pub const Value = extern union { } /// Asserts the value is comparable. - pub fn order(lhs: Value, rhs: Value) std.math.Order { + pub fn order(lhs: Value, rhs: Value, target: Target) std.math.Order { const lhs_tag = lhs.tag(); const rhs_tag = rhs.tag(); const lhs_against_zero = lhs.orderAgainstZero(); @@ -1814,14 +1855,14 @@ pub const Value = extern union { var lhs_bigint_space: BigIntSpace = undefined; var rhs_bigint_space: BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_bigint_space); - const rhs_bigint = rhs.toBigInt(&rhs_bigint_space); + const lhs_bigint = lhs.toBigInt(&lhs_bigint_space, target); + const rhs_bigint = rhs.toBigInt(&rhs_bigint_space, target); return lhs_bigint.order(rhs_bigint); } /// Asserts the value is comparable. Does not take a type parameter because it supports /// comparisons between heterogeneous types. - pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value) bool { + pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, target: Target) bool { if (lhs.pointerDecl()) |lhs_decl| { if (rhs.pointerDecl()) |rhs_decl| { switch (op) { @@ -1843,39 +1884,39 @@ pub const Value = extern union { else => {}, } } - return order(lhs, rhs).compare(op); + return order(lhs, rhs, target).compare(op); } /// Asserts the values are comparable. Both operands have type `ty`. /// Vector results will be reduced with AND. - pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type) bool { + pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, target: Target) bool { if (ty.zigTypeTag() == .Vector) { var i: usize = 0; while (i < ty.vectorLen()) : (i += 1) { - if (!compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType())) { + if (!compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType(), target)) { return false; } } return true; } - return compareScalar(lhs, op, rhs, ty); + return compareScalar(lhs, op, rhs, ty, target); } /// Asserts the values are comparable. Both operands have type `ty`. - pub fn compareScalar(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type) bool { + pub fn compareScalar(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, target: Target) bool { return switch (op) { - .eq => lhs.eql(rhs, ty), - .neq => !lhs.eql(rhs, ty), - else => compareHetero(lhs, op, rhs), + .eq => lhs.eql(rhs, ty, target), + .neq => !lhs.eql(rhs, ty, target), + else => compareHetero(lhs, op, rhs, target), }; } /// Asserts the values are comparable vectors of type `ty`. - pub fn compareVector(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, allocator: Allocator) !Value { + pub fn compareVector(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { assert(ty.zigTypeTag() == .Vector); const result_data = try allocator.alloc(Value, ty.vectorLen()); for (result_data) |*scalar, i| { - const res_bool = compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType()); + const res_bool = compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType(), target); scalar.* = if (res_bool) Value.@"true" else Value.@"false"; } return Value.Tag.aggregate.create(allocator, result_data); @@ -1899,12 +1940,12 @@ pub const Value = extern union { /// This function is used by hash maps and so treats floating-point NaNs as equal /// to each other, and not equal to other floating-point values. - pub fn eql(a: Value, b: Value, ty: Type) bool { + /// Similarly, it treats `undef` as a distinct value from all other values. + pub fn eql(a: Value, b: Value, ty: Type, target: Target) bool { const a_tag = a.tag(); const b_tag = b.tag(); - assert(a_tag != .undef); - assert(b_tag != .undef); if (a_tag == b_tag) switch (a_tag) { + .undef => return true, .void_value, .null_value, .the_only_possible_value, .empty_struct_value => return true, .enum_literal => { const a_name = a.castTag(.enum_literal).?.data; @@ -1920,31 +1961,31 @@ pub const Value = extern union { const a_payload = a.castTag(.opt_payload).?.data; const b_payload = b.castTag(.opt_payload).?.data; var buffer: Type.Payload.ElemType = undefined; - return eql(a_payload, b_payload, ty.optionalChild(&buffer)); + return eql(a_payload, b_payload, ty.optionalChild(&buffer), target); }, .slice => { const a_payload = a.castTag(.slice).?.data; const b_payload = b.castTag(.slice).?.data; - if (!eql(a_payload.len, b_payload.len, Type.usize)) return false; + if (!eql(a_payload.len, b_payload.len, Type.usize, target)) return false; var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; const ptr_ty = ty.slicePtrFieldType(&ptr_buf); - return eql(a_payload.ptr, b_payload.ptr, ptr_ty); + return eql(a_payload.ptr, b_payload.ptr, ptr_ty, target); }, .elem_ptr => { const a_payload = a.castTag(.elem_ptr).?.data; const b_payload = b.castTag(.elem_ptr).?.data; if (a_payload.index != b_payload.index) return false; - return eql(a_payload.array_ptr, b_payload.array_ptr, ty); + return eql(a_payload.array_ptr, b_payload.array_ptr, ty, target); }, .field_ptr => { const a_payload = a.castTag(.field_ptr).?.data; const b_payload = b.castTag(.field_ptr).?.data; if (a_payload.field_index != b_payload.field_index) return false; - return eql(a_payload.container_ptr, b_payload.container_ptr, ty); + return eql(a_payload.container_ptr, b_payload.container_ptr, ty, target); }, .@"error" => { const a_name = a.castTag(.@"error").?.data.name; @@ -1954,7 +1995,7 @@ pub const Value = extern union { .eu_payload => { const a_payload = a.castTag(.eu_payload).?.data; const b_payload = b.castTag(.eu_payload).?.data; - return eql(a_payload, b_payload, ty.errorUnionPayload()); + return eql(a_payload, b_payload, ty.errorUnionPayload(), target); }, .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"), .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"), @@ -1972,7 +2013,7 @@ pub const Value = extern union { const types = ty.tupleFields().types; assert(types.len == a_field_vals.len); for (types) |field_ty, i| { - if (!eql(a_field_vals[i], b_field_vals[i], field_ty)) return false; + if (!eql(a_field_vals[i], b_field_vals[i], field_ty, target)) return false; } return true; } @@ -1981,7 +2022,7 @@ pub const Value = extern union { const fields = ty.structFields().values(); assert(fields.len == a_field_vals.len); for (fields) |field, i| { - if (!eql(a_field_vals[i], b_field_vals[i], field.ty)) return false; + if (!eql(a_field_vals[i], b_field_vals[i], field.ty, target)) return false; } return true; } @@ -1990,7 +2031,7 @@ pub const Value = extern union { for (a_field_vals) |a_elem, i| { const b_elem = b_field_vals[i]; - if (!eql(a_elem, b_elem, elem_ty)) return false; + if (!eql(a_elem, b_elem, elem_ty, target)) return false; } return true; }, @@ -2005,17 +2046,19 @@ pub const Value = extern union { }, .Auto => { const tag_ty = ty.unionTagTypeHypothetical(); - if (!a_union.tag.eql(b_union.tag, tag_ty)) { + if (!a_union.tag.eql(b_union.tag, tag_ty, target)) { return false; } - const active_field_ty = ty.unionFieldType(a_union.tag); - return a_union.val.eql(b_union.val, active_field_ty); + const active_field_ty = ty.unionFieldType(a_union.tag, target); + return a_union.val.eql(b_union.val, active_field_ty, target); }, } }, else => {}, } else if (a_tag == .null_value or b_tag == .null_value) { return false; + } else if (a_tag == .undef or b_tag == .undef) { + return false; } if (a.pointerDecl()) |a_decl| { @@ -2034,7 +2077,7 @@ pub const Value = extern union { var buf_b: ToTypeBuffer = undefined; const a_type = a.toType(&buf_a); const b_type = b.toType(&buf_b); - return a_type.eql(b_type); + return a_type.eql(b_type, target); }, .Enum => { var buf_a: Payload.U64 = undefined; @@ -2043,7 +2086,7 @@ pub const Value = extern union { const b_val = b.enumToInt(ty, &buf_b); var buf_ty: Type.Payload.Bits = undefined; const int_ty = ty.intTagType(&buf_ty); - return eql(a_val, b_val, int_ty); + return eql(a_val, b_val, int_ty, target); }, .Array, .Vector => { const len = ty.arrayLen(); @@ -2054,7 +2097,7 @@ pub const Value = extern union { while (i < len) : (i += 1) { const a_elem = elemValueBuffer(a, i, &a_buf); const b_elem = elemValueBuffer(b, i, &b_buf); - if (!eql(a_elem, b_elem, elem_ty)) return false; + if (!eql(a_elem, b_elem, elem_ty, target)) return false; } return true; }, @@ -2070,15 +2113,15 @@ pub const Value = extern union { if (a_nan or b_nan) { return a_nan and b_nan; } - return order(a, b).compare(.eq); + return order(a, b, target).compare(.eq); }, - else => return order(a, b).compare(.eq), + else => return order(a, b, target).compare(.eq), } } /// This function is used by hash maps and so treats floating-point NaNs as equal /// to each other, and not equal to other floating-point values. - pub fn hash(val: Value, ty: Type, hasher: *std.hash.Wyhash) void { + pub fn hash(val: Value, ty: Type, hasher: *std.hash.Wyhash, target: Target) void { const zig_ty_tag = ty.zigTypeTag(); std.hash.autoHash(hasher, zig_ty_tag); if (val.isUndef()) return; @@ -2095,7 +2138,7 @@ pub const Value = extern union { .Type => { var buf: ToTypeBuffer = undefined; - return val.toType(&buf).hashWithHasher(hasher); + return val.toType(&buf).hashWithHasher(hasher, target); }, .Float, .ComptimeFloat => { // Normalize the float here because this hash must match eql semantics. @@ -2116,11 +2159,11 @@ pub const Value = extern union { const slice = val.castTag(.slice).?.data; var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; const ptr_ty = ty.slicePtrFieldType(&ptr_buf); - hash(slice.ptr, ptr_ty, hasher); - hash(slice.len, Type.usize, hasher); + hash(slice.ptr, ptr_ty, hasher, target); + hash(slice.len, Type.usize, hasher, target); }, - else => return hashPtr(val, hasher), + else => return hashPtr(val, hasher, target), }, .Array, .Vector => { const len = ty.arrayLen(); @@ -2129,14 +2172,14 @@ pub const Value = extern union { var elem_value_buf: ElemValueBuffer = undefined; while (index < len) : (index += 1) { const elem_val = val.elemValueBuffer(index, &elem_value_buf); - elem_val.hash(elem_ty, hasher); + elem_val.hash(elem_ty, hasher, target); } }, .Struct => { if (ty.isTupleOrAnonStruct()) { const fields = ty.tupleFields(); for (fields.values) |field_val, i| { - field_val.hash(fields.types[i], hasher); + field_val.hash(fields.types[i], hasher, target); } return; } @@ -2145,13 +2188,13 @@ pub const Value = extern union { switch (val.tag()) { .empty_struct_value => { for (fields) |field| { - field.default_val.hash(field.ty, hasher); + field.default_val.hash(field.ty, hasher, target); } }, .aggregate => { const field_values = val.castTag(.aggregate).?.data; for (field_values) |field_val, i| { - field_val.hash(fields[i].ty, hasher); + field_val.hash(fields[i].ty, hasher, target); } }, else => unreachable, @@ -2163,7 +2206,7 @@ pub const Value = extern union { const sub_val = payload.data; var buffer: Type.Payload.ElemType = undefined; const sub_ty = ty.optionalChild(&buffer); - sub_val.hash(sub_ty, hasher); + sub_val.hash(sub_ty, hasher, target); } else { std.hash.autoHash(hasher, false); // non-null } @@ -2172,14 +2215,14 @@ pub const Value = extern union { if (val.tag() == .@"error") { std.hash.autoHash(hasher, false); // error const sub_ty = ty.errorUnionSet(); - val.hash(sub_ty, hasher); + val.hash(sub_ty, hasher, target); return; } if (val.castTag(.eu_payload)) |payload| { std.hash.autoHash(hasher, true); // payload const sub_ty = ty.errorUnionPayload(); - payload.data.hash(sub_ty, hasher); + payload.data.hash(sub_ty, hasher, target); return; } else unreachable; }, @@ -2192,15 +2235,15 @@ pub const Value = extern union { .Enum => { var enum_space: Payload.U64 = undefined; const int_val = val.enumToInt(ty, &enum_space); - hashInt(int_val, hasher); + hashInt(int_val, hasher, target); }, .Union => { const union_obj = val.cast(Payload.Union).?.data; if (ty.unionTagType()) |tag_ty| { - union_obj.tag.hash(tag_ty, hasher); + union_obj.tag.hash(tag_ty, hasher, target); } - const active_field_ty = ty.unionFieldType(union_obj.tag); - union_obj.val.hash(active_field_ty, hasher); + const active_field_ty = ty.unionFieldType(union_obj.tag, target); + union_obj.val.hash(active_field_ty, hasher, target); }, .Fn => { const func: *Module.Fn = val.castTag(.function).?.data; @@ -2225,28 +2268,30 @@ pub const Value = extern union { pub const ArrayHashContext = struct { ty: Type, + target: Target, pub fn hash(self: @This(), val: Value) u32 { - const other_context: HashContext = .{ .ty = self.ty }; + const other_context: HashContext = .{ .ty = self.ty, .target = self.target }; return @truncate(u32, other_context.hash(val)); } pub fn eql(self: @This(), a: Value, b: Value, b_index: usize) bool { _ = b_index; - return a.eql(b, self.ty); + return a.eql(b, self.ty, self.target); } }; pub const HashContext = struct { ty: Type, + target: Target, pub fn hash(self: @This(), val: Value) u64 { var hasher = std.hash.Wyhash.init(0); - val.hash(self.ty, &hasher); + val.hash(self.ty, &hasher, self.target); return hasher.final(); } pub fn eql(self: @This(), a: Value, b: Value) bool { - return a.eql(b, self.ty); + return a.eql(b, self.ty, self.target); } }; @@ -2296,16 +2341,16 @@ pub const Value = extern union { }; } - fn hashInt(int_val: Value, hasher: *std.hash.Wyhash) void { + fn hashInt(int_val: Value, hasher: *std.hash.Wyhash, target: Target) void { var buffer: BigIntSpace = undefined; - const big = int_val.toBigInt(&buffer); + const big = int_val.toBigInt(&buffer, target); std.hash.autoHash(hasher, big.positive); for (big.limbs) |limb| { std.hash.autoHash(hasher, limb); } } - fn hashPtr(ptr_val: Value, hasher: *std.hash.Wyhash) void { + fn hashPtr(ptr_val: Value, hasher: *std.hash.Wyhash, target: Target) void { switch (ptr_val.tag()) { .decl_ref, .decl_ref_mut, @@ -2319,25 +2364,25 @@ pub const Value = extern union { .elem_ptr => { const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; - hashPtr(elem_ptr.array_ptr, hasher); + hashPtr(elem_ptr.array_ptr, hasher, target); std.hash.autoHash(hasher, Value.Tag.elem_ptr); std.hash.autoHash(hasher, elem_ptr.index); }, .field_ptr => { const field_ptr = ptr_val.castTag(.field_ptr).?.data; std.hash.autoHash(hasher, Value.Tag.field_ptr); - hashPtr(field_ptr.container_ptr, hasher); + hashPtr(field_ptr.container_ptr, hasher, target); std.hash.autoHash(hasher, field_ptr.field_index); }, .eu_payload_ptr => { const err_union_ptr = ptr_val.castTag(.eu_payload_ptr).?.data; std.hash.autoHash(hasher, Value.Tag.eu_payload_ptr); - hashPtr(err_union_ptr.container_ptr, hasher); + hashPtr(err_union_ptr.container_ptr, hasher, target); }, .opt_payload_ptr => { const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data; std.hash.autoHash(hasher, Value.Tag.opt_payload_ptr); - hashPtr(opt_ptr.container_ptr, hasher); + hashPtr(opt_ptr.container_ptr, hasher, target); }, .zero, @@ -2349,7 +2394,7 @@ pub const Value = extern union { .bool_false, .bool_true, .the_only_possible_value, - => return hashInt(ptr_val, hasher), + => return hashInt(ptr_val, hasher, target), else => unreachable, } @@ -2411,9 +2456,9 @@ pub const Value = extern union { }; } - pub fn sliceLen(val: Value) u64 { + pub fn sliceLen(val: Value, target: Target) u64 { return switch (val.tag()) { - .slice => val.castTag(.slice).?.data.len.toUnsignedInt(), + .slice => val.castTag(.slice).?.data.len.toUnsignedInt(target), .decl_ref => { const decl = val.castTag(.decl_ref).?.data; if (decl.ty.zigTypeTag() == .Array) { @@ -2561,7 +2606,7 @@ pub const Value = extern union { } /// Returns a pointer to the element value at the index. - pub fn elemPtr(val: Value, ty: Type, arena: Allocator, index: usize) Allocator.Error!Value { + pub fn elemPtr(val: Value, ty: Type, arena: Allocator, index: usize, target: Target) Allocator.Error!Value { const elem_ty = ty.elemType2(); const ptr_val = switch (val.tag()) { .slice => val.castTag(.slice).?.data.ptr, @@ -2570,7 +2615,7 @@ pub const Value = extern union { if (ptr_val.tag() == .elem_ptr) { const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; - if (elem_ptr.elem_ty.eql(elem_ty)) { + if (elem_ptr.elem_ty.eql(elem_ty, target)) { return Tag.elem_ptr.create(arena, .{ .array_ptr = elem_ptr.array_ptr, .elem_ty = elem_ptr.elem_ty, @@ -2821,8 +2866,8 @@ pub const Value = extern union { var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const rhs_bigint = rhs.toBigInt(&rhs_space); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const rhs_bigint = rhs.toBigInt(&rhs_space, target); const limbs = try arena.alloc( std.math.big.Limb, std.math.big.int.calcTwosCompLimbCount(info.bits), @@ -2865,7 +2910,7 @@ pub const Value = extern union { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); if (ty.zigTypeTag() == .ComptimeInt) { - return intAdd(lhs, rhs, ty, arena); + return intAdd(lhs, rhs, ty, arena, target); } if (ty.isAnyFloat()) { @@ -2925,8 +2970,8 @@ pub const Value = extern union { var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const rhs_bigint = rhs.toBigInt(&rhs_space); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const rhs_bigint = rhs.toBigInt(&rhs_space, target); const limbs = try arena.alloc( std.math.big.Limb, std.math.big.int.calcTwosCompLimbCount(info.bits), @@ -2947,8 +2992,8 @@ pub const Value = extern union { var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const rhs_bigint = rhs.toBigInt(&rhs_space); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const rhs_bigint = rhs.toBigInt(&rhs_space, target); const limbs = try arena.alloc( std.math.big.Limb, std.math.big.int.calcTwosCompLimbCount(info.bits), @@ -2991,7 +3036,7 @@ pub const Value = extern union { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); if (ty.zigTypeTag() == .ComptimeInt) { - return intSub(lhs, rhs, ty, arena); + return intSub(lhs, rhs, ty, arena, target); } if (ty.isAnyFloat()) { @@ -3035,8 +3080,8 @@ pub const Value = extern union { var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const rhs_bigint = rhs.toBigInt(&rhs_space); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const rhs_bigint = rhs.toBigInt(&rhs_space, target); const limbs = try arena.alloc( std.math.big.Limb, std.math.big.int.calcTwosCompLimbCount(info.bits), @@ -3057,8 +3102,8 @@ pub const Value = extern union { var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const rhs_bigint = rhs.toBigInt(&rhs_space); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const rhs_bigint = rhs.toBigInt(&rhs_space, target); const limbs = try arena.alloc( std.math.big.Limb, lhs_bigint.limbs.len + rhs_bigint.limbs.len, @@ -3110,7 +3155,7 @@ pub const Value = extern union { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); if (ty.zigTypeTag() == .ComptimeInt) { - return intMul(lhs, rhs, ty, arena); + return intMul(lhs, rhs, ty, arena, target); } if (ty.isAnyFloat()) { @@ -3154,8 +3199,8 @@ pub const Value = extern union { var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const rhs_bigint = rhs.toBigInt(&rhs_space); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const rhs_bigint = rhs.toBigInt(&rhs_space, target); const limbs = try arena.alloc( std.math.big.Limb, std.math.max( @@ -3175,24 +3220,24 @@ pub const Value = extern union { } /// Supports both floats and ints; handles undefined. - pub fn numberMax(lhs: Value, rhs: Value) Value { + pub fn numberMax(lhs: Value, rhs: Value, target: Target) Value { if (lhs.isUndef() or rhs.isUndef()) return undef; if (lhs.isNan()) return rhs; if (rhs.isNan()) return lhs; - return switch (order(lhs, rhs)) { + return switch (order(lhs, rhs, target)) { .lt => rhs, .gt, .eq => lhs, }; } /// Supports both floats and ints; handles undefined. - pub fn numberMin(lhs: Value, rhs: Value) Value { + pub fn numberMin(lhs: Value, rhs: Value, target: Target) Value { if (lhs.isUndef() or rhs.isUndef()) return undef; if (lhs.isNan()) return rhs; if (rhs.isNan()) return lhs; - return switch (order(lhs, rhs)) { + return switch (order(lhs, rhs, target)) { .lt => lhs, .gt, .eq => rhs, }; @@ -3224,7 +3269,7 @@ pub const Value = extern union { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var val_space: Value.BigIntSpace = undefined; - const val_bigint = val.toBigInt(&val_space); + const val_bigint = val.toBigInt(&val_space, target); const limbs = try arena.alloc( std.math.big.Limb, std.math.big.int.calcTwosCompLimbCount(info.bits), @@ -3236,27 +3281,27 @@ pub const Value = extern union { } /// operands must be (vectors of) integers; handles undefined scalars. - pub fn bitwiseAnd(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + pub fn bitwiseAnd(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { if (ty.zigTypeTag() == .Vector) { const result_data = try allocator.alloc(Value, ty.vectorLen()); for (result_data) |*scalar, i| { - scalar.* = try bitwiseAndScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + scalar.* = try bitwiseAndScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator, target); } return Value.Tag.aggregate.create(allocator, result_data); } - return bitwiseAndScalar(lhs, rhs, allocator); + return bitwiseAndScalar(lhs, rhs, allocator, target); } /// operands must be integers; handles undefined. - pub fn bitwiseAndScalar(lhs: Value, rhs: Value, arena: Allocator) !Value { + pub fn bitwiseAndScalar(lhs: Value, rhs: Value, arena: Allocator, target: Target) !Value { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const rhs_bigint = rhs.toBigInt(&rhs_space); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const rhs_bigint = rhs.toBigInt(&rhs_space, target); const limbs = try arena.alloc( std.math.big.Limb, // + 1 for negatives @@ -3283,38 +3328,38 @@ pub const Value = extern union { pub fn bitwiseNandScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, target: Target) !Value { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); - const anded = try bitwiseAnd(lhs, rhs, ty, arena); + const anded = try bitwiseAnd(lhs, rhs, ty, arena, target); const all_ones = if (ty.isSignedInt()) try Value.Tag.int_i64.create(arena, -1) else try ty.maxInt(arena, target); - return bitwiseXor(anded, all_ones, ty, arena); + return bitwiseXor(anded, all_ones, ty, arena, target); } /// operands must be (vectors of) integers; handles undefined scalars. - pub fn bitwiseOr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + pub fn bitwiseOr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { if (ty.zigTypeTag() == .Vector) { const result_data = try allocator.alloc(Value, ty.vectorLen()); for (result_data) |*scalar, i| { - scalar.* = try bitwiseOrScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + scalar.* = try bitwiseOrScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator, target); } return Value.Tag.aggregate.create(allocator, result_data); } - return bitwiseOrScalar(lhs, rhs, allocator); + return bitwiseOrScalar(lhs, rhs, allocator, target); } /// operands must be integers; handles undefined. - pub fn bitwiseOrScalar(lhs: Value, rhs: Value, arena: Allocator) !Value { + pub fn bitwiseOrScalar(lhs: Value, rhs: Value, arena: Allocator, target: Target) !Value { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const rhs_bigint = rhs.toBigInt(&rhs_space); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const rhs_bigint = rhs.toBigInt(&rhs_space, target); const limbs = try arena.alloc( std.math.big.Limb, std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len), @@ -3325,27 +3370,27 @@ pub const Value = extern union { } /// operands must be (vectors of) integers; handles undefined scalars. - pub fn bitwiseXor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + pub fn bitwiseXor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { if (ty.zigTypeTag() == .Vector) { const result_data = try allocator.alloc(Value, ty.vectorLen()); for (result_data) |*scalar, i| { - scalar.* = try bitwiseXorScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + scalar.* = try bitwiseXorScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator, target); } return Value.Tag.aggregate.create(allocator, result_data); } - return bitwiseXorScalar(lhs, rhs, allocator); + return bitwiseXorScalar(lhs, rhs, allocator, target); } /// operands must be integers; handles undefined. - pub fn bitwiseXorScalar(lhs: Value, rhs: Value, arena: Allocator) !Value { + pub fn bitwiseXorScalar(lhs: Value, rhs: Value, arena: Allocator, target: Target) !Value { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const rhs_bigint = rhs.toBigInt(&rhs_space); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const rhs_bigint = rhs.toBigInt(&rhs_space, target); const limbs = try arena.alloc( std.math.big.Limb, // + 1 for negatives @@ -3356,24 +3401,24 @@ pub const Value = extern union { return fromBigInt(arena, result_bigint.toConst()); } - pub fn intAdd(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + pub fn intAdd(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { if (ty.zigTypeTag() == .Vector) { const result_data = try allocator.alloc(Value, ty.vectorLen()); for (result_data) |*scalar, i| { - scalar.* = try intAddScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + scalar.* = try intAddScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator, target); } return Value.Tag.aggregate.create(allocator, result_data); } - return intAddScalar(lhs, rhs, allocator); + return intAddScalar(lhs, rhs, allocator, target); } - pub fn intAddScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intAddScalar(lhs: Value, rhs: Value, allocator: Allocator, target: Target) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const rhs_bigint = rhs.toBigInt(&rhs_space); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const rhs_bigint = rhs.toBigInt(&rhs_space, target); const limbs = try allocator.alloc( std.math.big.Limb, std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, @@ -3383,24 +3428,24 @@ pub const Value = extern union { return fromBigInt(allocator, result_bigint.toConst()); } - pub fn intSub(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + pub fn intSub(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { if (ty.zigTypeTag() == .Vector) { const result_data = try allocator.alloc(Value, ty.vectorLen()); for (result_data) |*scalar, i| { - scalar.* = try intSubScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + scalar.* = try intSubScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator, target); } return Value.Tag.aggregate.create(allocator, result_data); } - return intSubScalar(lhs, rhs, allocator); + return intSubScalar(lhs, rhs, allocator, target); } - pub fn intSubScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intSubScalar(lhs: Value, rhs: Value, allocator: Allocator, target: Target) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const rhs_bigint = rhs.toBigInt(&rhs_space); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const rhs_bigint = rhs.toBigInt(&rhs_space, target); const limbs = try allocator.alloc( std.math.big.Limb, std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, @@ -3410,24 +3455,24 @@ pub const Value = extern union { return fromBigInt(allocator, result_bigint.toConst()); } - pub fn intDiv(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + pub fn intDiv(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { if (ty.zigTypeTag() == .Vector) { const result_data = try allocator.alloc(Value, ty.vectorLen()); for (result_data) |*scalar, i| { - scalar.* = try intDivScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + scalar.* = try intDivScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator, target); } return Value.Tag.aggregate.create(allocator, result_data); } - return intDivScalar(lhs, rhs, allocator); + return intDivScalar(lhs, rhs, allocator, target); } - pub fn intDivScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intDivScalar(lhs: Value, rhs: Value, allocator: Allocator, target: Target) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const rhs_bigint = rhs.toBigInt(&rhs_space); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const rhs_bigint = rhs.toBigInt(&rhs_space, target); const limbs_q = try allocator.alloc( std.math.big.Limb, lhs_bigint.limbs.len, @@ -3446,24 +3491,24 @@ pub const Value = extern union { return fromBigInt(allocator, result_q.toConst()); } - pub fn intDivFloor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + pub fn intDivFloor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { if (ty.zigTypeTag() == .Vector) { const result_data = try allocator.alloc(Value, ty.vectorLen()); for (result_data) |*scalar, i| { - scalar.* = try intDivFloorScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + scalar.* = try intDivFloorScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator, target); } return Value.Tag.aggregate.create(allocator, result_data); } - return intDivFloorScalar(lhs, rhs, allocator); + return intDivFloorScalar(lhs, rhs, allocator, target); } - pub fn intDivFloorScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intDivFloorScalar(lhs: Value, rhs: Value, allocator: Allocator, target: Target) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const rhs_bigint = rhs.toBigInt(&rhs_space); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const rhs_bigint = rhs.toBigInt(&rhs_space, target); const limbs_q = try allocator.alloc( std.math.big.Limb, lhs_bigint.limbs.len, @@ -3482,24 +3527,24 @@ pub const Value = extern union { return fromBigInt(allocator, result_q.toConst()); } - pub fn intRem(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + pub fn intRem(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { if (ty.zigTypeTag() == .Vector) { const result_data = try allocator.alloc(Value, ty.vectorLen()); for (result_data) |*scalar, i| { - scalar.* = try intRemScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + scalar.* = try intRemScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator, target); } return Value.Tag.aggregate.create(allocator, result_data); } - return intRemScalar(lhs, rhs, allocator); + return intRemScalar(lhs, rhs, allocator, target); } - pub fn intRemScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intRemScalar(lhs: Value, rhs: Value, allocator: Allocator, target: Target) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const rhs_bigint = rhs.toBigInt(&rhs_space); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const rhs_bigint = rhs.toBigInt(&rhs_space, target); const limbs_q = try allocator.alloc( std.math.big.Limb, lhs_bigint.limbs.len, @@ -3520,24 +3565,24 @@ pub const Value = extern union { return fromBigInt(allocator, result_r.toConst()); } - pub fn intMod(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + pub fn intMod(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { if (ty.zigTypeTag() == .Vector) { const result_data = try allocator.alloc(Value, ty.vectorLen()); for (result_data) |*scalar, i| { - scalar.* = try intModScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + scalar.* = try intModScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator, target); } return Value.Tag.aggregate.create(allocator, result_data); } - return intModScalar(lhs, rhs, allocator); + return intModScalar(lhs, rhs, allocator, target); } - pub fn intModScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intModScalar(lhs: Value, rhs: Value, allocator: Allocator, target: Target) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const rhs_bigint = rhs.toBigInt(&rhs_space); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const rhs_bigint = rhs.toBigInt(&rhs_space, target); const limbs_q = try allocator.alloc( std.math.big.Limb, lhs_bigint.limbs.len, @@ -3658,24 +3703,24 @@ pub const Value = extern union { } } - pub fn intMul(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + pub fn intMul(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { if (ty.zigTypeTag() == .Vector) { const result_data = try allocator.alloc(Value, ty.vectorLen()); for (result_data) |*scalar, i| { - scalar.* = try intMulScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + scalar.* = try intMulScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator, target); } return Value.Tag.aggregate.create(allocator, result_data); } - return intMulScalar(lhs, rhs, allocator); + return intMulScalar(lhs, rhs, allocator, target); } - pub fn intMulScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intMulScalar(lhs: Value, rhs: Value, allocator: Allocator, target: Target) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const rhs_bigint = rhs.toBigInt(&rhs_space); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const rhs_bigint = rhs.toBigInt(&rhs_space, target); const limbs = try allocator.alloc( std.math.big.Limb, lhs_bigint.limbs.len + rhs_bigint.limbs.len, @@ -3690,34 +3735,41 @@ pub const Value = extern union { return fromBigInt(allocator, result_bigint.toConst()); } - pub fn intTrunc(val: Value, ty: Type, allocator: Allocator, signedness: std.builtin.Signedness, bits: u16) !Value { + pub fn intTrunc(val: Value, ty: Type, allocator: Allocator, signedness: std.builtin.Signedness, bits: u16, target: Target) !Value { if (ty.zigTypeTag() == .Vector) { const result_data = try allocator.alloc(Value, ty.vectorLen()); for (result_data) |*scalar, i| { - scalar.* = try intTruncScalar(val.indexVectorlike(i), allocator, signedness, bits); + scalar.* = try intTruncScalar(val.indexVectorlike(i), allocator, signedness, bits, target); } return Value.Tag.aggregate.create(allocator, result_data); } - return intTruncScalar(val, allocator, signedness, bits); + return intTruncScalar(val, allocator, signedness, bits, target); } /// This variant may vectorize on `bits`. Asserts that `bits` is a (vector of) `u16`. - pub fn intTruncBitsAsValue(val: Value, ty: Type, allocator: Allocator, signedness: std.builtin.Signedness, bits: Value) !Value { + pub fn intTruncBitsAsValue( + val: Value, + ty: Type, + allocator: Allocator, + signedness: std.builtin.Signedness, + bits: Value, + target: Target, + ) !Value { if (ty.zigTypeTag() == .Vector) { const result_data = try allocator.alloc(Value, ty.vectorLen()); for (result_data) |*scalar, i| { - scalar.* = try intTruncScalar(val.indexVectorlike(i), allocator, signedness, @intCast(u16, bits.indexVectorlike(i).toUnsignedInt())); + scalar.* = try intTruncScalar(val.indexVectorlike(i), allocator, signedness, @intCast(u16, bits.indexVectorlike(i).toUnsignedInt(target)), target); } return Value.Tag.aggregate.create(allocator, result_data); } - return intTruncScalar(val, allocator, signedness, @intCast(u16, bits.toUnsignedInt())); + return intTruncScalar(val, allocator, signedness, @intCast(u16, bits.toUnsignedInt(target)), target); } - pub fn intTruncScalar(val: Value, allocator: Allocator, signedness: std.builtin.Signedness, bits: u16) !Value { + pub fn intTruncScalar(val: Value, allocator: Allocator, signedness: std.builtin.Signedness, bits: u16, target: Target) !Value { if (bits == 0) return Value.zero; var val_space: Value.BigIntSpace = undefined; - const val_bigint = val.toBigInt(&val_space); + const val_bigint = val.toBigInt(&val_space, target); const limbs = try allocator.alloc( std.math.big.Limb, @@ -3729,23 +3781,23 @@ pub const Value = extern union { return fromBigInt(allocator, result_bigint.toConst()); } - pub fn shl(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + pub fn shl(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { if (ty.zigTypeTag() == .Vector) { const result_data = try allocator.alloc(Value, ty.vectorLen()); for (result_data) |*scalar, i| { - scalar.* = try shlScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + scalar.* = try shlScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator, target); } return Value.Tag.aggregate.create(allocator, result_data); } - return shlScalar(lhs, rhs, allocator); + return shlScalar(lhs, rhs, allocator, target); } - pub fn shlScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn shlScalar(lhs: Value, rhs: Value, allocator: Allocator, target: Target) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const shift = @intCast(usize, rhs.toUnsignedInt()); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const shift = @intCast(usize, rhs.toUnsignedInt(target)); const limbs = try allocator.alloc( std.math.big.Limb, lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1, @@ -3768,8 +3820,8 @@ pub const Value = extern union { ) !OverflowArithmeticResult { const info = ty.intInfo(target); var lhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const shift = @intCast(usize, rhs.toUnsignedInt()); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const shift = @intCast(usize, rhs.toUnsignedInt(target)); const limbs = try allocator.alloc( std.math.big.Limb, lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1, @@ -3819,8 +3871,8 @@ pub const Value = extern union { const info = ty.intInfo(target); var lhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const shift = @intCast(usize, rhs.toUnsignedInt()); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const shift = @intCast(usize, rhs.toUnsignedInt(target)); const limbs = try arena.alloc( std.math.big.Limb, std.math.big.int.calcTwosCompLimbCount(info.bits), @@ -3858,29 +3910,29 @@ pub const Value = extern union { arena: Allocator, target: Target, ) !Value { - const shifted = try lhs.shl(rhs, ty, arena); + const shifted = try lhs.shl(rhs, ty, arena, target); const int_info = ty.intInfo(target); - const truncated = try shifted.intTrunc(ty, arena, int_info.signedness, int_info.bits); + const truncated = try shifted.intTrunc(ty, arena, int_info.signedness, int_info.bits, target); return truncated; } - pub fn shr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + pub fn shr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { if (ty.zigTypeTag() == .Vector) { const result_data = try allocator.alloc(Value, ty.vectorLen()); for (result_data) |*scalar, i| { - scalar.* = try shrScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + scalar.* = try shrScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator, target); } return Value.Tag.aggregate.create(allocator, result_data); } - return shrScalar(lhs, rhs, allocator); + return shrScalar(lhs, rhs, allocator, target); } - pub fn shrScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn shrScalar(lhs: Value, rhs: Value, allocator: Allocator, target: Target) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space); - const shift = @intCast(usize, rhs.toUnsignedInt()); + const lhs_bigint = lhs.toBigInt(&lhs_space, target); + const shift = @intCast(usize, rhs.toUnsignedInt(target)); const result_limbs = lhs_bigint.limbs.len -| (shift / (@sizeOf(std.math.big.Limb) * 8)); if (result_limbs == 0) { diff --git a/test/behavior.zig b/test/behavior.zig index d413529a50..29eadbf14f 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -125,6 +125,7 @@ test { _ = @import("behavior/src.zig"); _ = @import("behavior/struct.zig"); _ = @import("behavior/struct_contains_null_ptr_itself.zig"); + _ = @import("behavior/struct_contains_slice_of_itself.zig"); _ = @import("behavior/switch.zig"); _ = @import("behavior/switch_prong_err_enum.zig"); _ = @import("behavior/switch_prong_implicit_cast.zig"); @@ -179,6 +180,5 @@ test { _ = @import("behavior/bugs/6781.zig"); _ = @import("behavior/bugs/7027.zig"); _ = @import("behavior/select.zig"); - _ = @import("behavior/struct_contains_slice_of_itself.zig"); } } From 60d8c4739de14823c407245f01c9b7483f3b6e7f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 22 Mar 2022 15:42:09 -0700 Subject: [PATCH 0861/2031] Sema: introduce a mechanism in Value to resolve types This commit adds a new optional argument to several Value methods which provides the ability to resolve types if it comes to it. This prevents having duplicated logic inside both Sema and Value. With this commit, the "struct contains slice of itself" test is passing by exploiting the new lazy_align Value Tag. --- src/Module.zig | 6 ++ src/Sema.zig | 48 ++++++---- src/type.zig | 95 +++++++++++++------ src/value.zig | 63 ++++++++++-- .../struct_contains_slice_of_itself.zig | 9 ++ 5 files changed, 167 insertions(+), 54 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 4164c2659c..7b27546f52 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -177,6 +177,12 @@ const MonomorphedFuncsContext = struct { } }; +pub const WipAnalysis = struct { + sema: *Sema, + block: *Sema.Block, + src: Module.LazySrcLoc, +}; + pub const MemoizedCallSet = std.HashMapUnmanaged( MemoizedCall.Key, MemoizedCall.Result, diff --git a/src/Sema.zig b/src/Sema.zig index 5e344bb7d0..2428db5b0f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1591,7 +1591,7 @@ fn resolveInt( const coerced = try sema.coerce(block, dest_ty, air_inst, src); const val = try sema.resolveConstValue(block, src, coerced); const target = sema.mod.getTarget(); - return val.toUnsignedInt(target); + return (try val.getUnsignedIntAdvanced(target, sema.kit(block, src))).?; } // Returns a compile error if the value has tag `variable`. See `resolveInstValue` for @@ -9926,7 +9926,7 @@ fn analyzePtrArithmetic( const offset_int = try sema.usizeCast(block, offset_src, offset_val.toUnsignedInt(target)); // TODO I tried to put this check earlier but it the LLVM backend generate invalid instructinons if (offset_int == 0) return ptr; - if (ptr_val.getUnsignedInt(target)) |addr| { + if (try ptr_val.getUnsignedIntAdvanced(target, sema.kit(block, ptr_src))) |addr| { const ptr_child_ty = ptr_ty.childType(); const elem_ty = if (ptr_ty.isSinglePointer() and ptr_child_ty.zigTypeTag() == .Array) ptr_child_ty.childType() @@ -11863,6 +11863,8 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const elem_ty_src: LazySrcLoc = .unneeded; const inst_data = sema.code.instructions.items(.data)[inst].ptr_type; const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index); + const unresolved_elem_ty = try sema.resolveType(block, elem_ty_src, extra.data.elem_type); + const target = sema.mod.getTarget(); var extra_i = extra.end; @@ -11872,10 +11874,19 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air break :blk (try sema.resolveInstConst(block, .unneeded, ref)).val; } else null; - const abi_align = if (inst_data.flags.has_align) blk: { + const abi_align: u32 = if (inst_data.flags.has_align) blk: { const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); extra_i += 1; - const abi_align = try sema.resolveInt(block, .unneeded, ref, Type.u32); + const coerced = try sema.coerce(block, Type.u32, sema.resolveInst(ref), src); + const val = try sema.resolveConstValue(block, src, coerced); + // Check if this happens to be the lazy alignment of our element type, in + // which case we can make this 0 without resolving it. + if (val.castTag(.lazy_align)) |payload| { + if (payload.data.eql(unresolved_elem_ty, target)) { + break :blk 0; + } + } + const abi_align = (try val.getUnsignedIntAdvanced(target, sema.kit(block, src))).?; break :blk @intCast(u32, abi_align); } else 0; @@ -11903,7 +11914,6 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air return sema.fail(block, src, "bit offset starts after end of host integer", .{}); } - const unresolved_elem_ty = try sema.resolveType(block, elem_ty_src, extra.data.elem_type); const elem_ty = if (abi_align == 0) unresolved_elem_ty else t: { @@ -11911,7 +11921,6 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air try sema.resolveTypeLayout(block, elem_ty_src, elem_ty); break :t elem_ty; }; - const target = sema.mod.getTarget(); const ty = try Type.ptr(sema.arena, target, .{ .pointee_type = elem_ty, .sentinel = sentinel, @@ -18390,15 +18399,15 @@ fn storePtrVal( operand_val: Value, operand_ty: Type, ) !void { - var kit = try beginComptimePtrMutation(sema, block, src, ptr_val); - try sema.checkComptimeVarStore(block, src, kit.decl_ref_mut); + var mut_kit = try beginComptimePtrMutation(sema, block, src, ptr_val); + try sema.checkComptimeVarStore(block, src, mut_kit.decl_ref_mut); - const bitcasted_val = try sema.bitCastVal(block, src, operand_val, operand_ty, kit.ty, 0); + const bitcasted_val = try sema.bitCastVal(block, src, operand_val, operand_ty, mut_kit.ty, 0); - const arena = kit.beginArena(sema.gpa); - defer kit.finishArena(); + const arena = mut_kit.beginArena(sema.gpa); + defer mut_kit.finishArena(); - kit.val.* = try bitcasted_val.copy(arena); + mut_kit.val.* = try bitcasted_val.copy(arena); } const ComptimePtrMutationKit = struct { @@ -19891,7 +19900,7 @@ fn cmpNumeric( return Air.Inst.Ref.bool_false; } } - if (Value.compareHetero(lhs_val, op, rhs_val, target)) { + if (try Value.compareHeteroAdvanced(lhs_val, op, rhs_val, target, sema.kit(block, src))) { return Air.Inst.Ref.bool_true; } else { return Air.Inst.Ref.bool_false; @@ -20758,7 +20767,7 @@ pub fn resolveFnTypes( } } -fn resolveTypeLayout( +pub fn resolveTypeLayout( sema: *Sema, block: *Block, src: LazySrcLoc, @@ -20929,7 +20938,7 @@ fn resolveUnionFully( union_obj.status = .fully_resolved; } -fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!Type { +pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!Type { switch (ty.tag()) { .@"struct" => { const struct_obj = ty.castTag(.@"struct").?.data; @@ -22209,7 +22218,9 @@ fn typePtrOrOptionalPtrTy( /// This function returns false negatives when structs and unions are having their /// field types resolved. /// TODO assert the return value matches `ty.comptimeOnly` -fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { +/// TODO merge these implementations together with the "advanced"/sema_kit pattern seen +/// elsewhere in value.zig +pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { return switch (ty.tag()) { .u1, .u8, @@ -22415,6 +22426,7 @@ fn typeAbiSize(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u64 { return ty.abiSize(target); } +/// TODO merge with Type.abiAlignmentAdvanced fn typeAbiAlignment(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u32 { try sema.resolveTypeLayout(block, src, ty); const target = sema.mod.getTarget(); @@ -22498,3 +22510,7 @@ fn anonStructFieldIndex( struct_ty.fmt(target), field_name, }); } + +fn kit(sema: *Sema, block: *Block, src: LazySrcLoc) Module.WipAnalysis { + return .{ .sema = sema, .block = block, .src = src }; +} diff --git a/src/type.zig b/src/type.zig index 0b49eac0a8..8668b36dca 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1483,20 +1483,29 @@ pub const Type = extern union { @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()"); } - pub fn fmt(ty: Type, target: Target) std.fmt.Formatter(TypedValue.format) { - var ty_payload: Value.Payload.Ty = .{ - .base = .{ .tag = .ty }, - .data = ty, - }; + pub fn fmt(ty: Type, target: Target) std.fmt.Formatter(format2) { return .{ .data = .{ - .tv = .{ - .ty = Type.type, - .val = Value.initPayload(&ty_payload.base), - }, + .ty = ty, .target = target, } }; } + const FormatContext = struct { + ty: Type, + target: Target, + }; + + fn format2( + ctx: FormatContext, + comptime unused_format_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + comptime assert(unused_format_string.len == 0); + _ = options; + return print(ctx.ty, writer, ctx.target); + } + pub fn fmtDebug(ty: Type) std.fmt.Formatter(dump) { return .{ .data = ty }; } @@ -2241,8 +2250,12 @@ pub const Type = extern union { /// * the type has only one possible value, making its ABI size 0. /// When `ignore_comptime_only` is true, then types that are comptime only /// may return false positives. - pub fn hasRuntimeBitsAdvanced(ty: Type, ignore_comptime_only: bool) bool { - return switch (ty.tag()) { + pub fn hasRuntimeBitsAdvanced( + ty: Type, + ignore_comptime_only: bool, + sema_kit: ?Module.WipAnalysis, + ) Module.CompileError!bool { + switch (ty.tag()) { .u1, .u8, .i8, @@ -2296,7 +2309,7 @@ pub const Type = extern union { .@"anyframe", .anyopaque, .@"opaque", - => true, + => return true, // These are false because they are comptime-only types. .single_const_pointer_to_comptime_int, @@ -2320,7 +2333,7 @@ pub const Type = extern union { .fn_void_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, - => false, + => return false, // These types have more than one possible value, so the result is the same as // asking whether they are comptime-only types. @@ -2337,20 +2350,34 @@ pub const Type = extern union { .const_slice, .mut_slice, .pointer, - => if (ignore_comptime_only) true else !comptimeOnly(ty), + => { + if (ignore_comptime_only) { + return true; + } else if (sema_kit) |sk| { + return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, ty)); + } else { + return !comptimeOnly(ty); + } + }, .@"struct" => { const struct_obj = ty.castTag(.@"struct").?.data; + if (sema_kit) |sk| { + _ = try sk.sema.typeRequiresComptime(sk.block, sk.src, ty); + } switch (struct_obj.requires_comptime) { .wip => unreachable, .yes => return false, .no => if (struct_obj.known_non_opv) return true, .unknown => {}, } + if (sema_kit) |sk| { + _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty); + } assert(struct_obj.haveFieldTypes()); for (struct_obj.fields.values()) |value| { if (value.is_comptime) continue; - if (value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only)) + if (try value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) return true; } else { return false; @@ -2368,14 +2395,17 @@ pub const Type = extern union { .enum_numbered, .enum_nonexhaustive => { var buffer: Payload.Bits = undefined; const int_tag_ty = ty.intTagType(&buffer); - return int_tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only); + return int_tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit); }, .@"union" => { const union_obj = ty.castTag(.@"union").?.data; + if (sema_kit) |sk| { + _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty); + } assert(union_obj.haveFieldTypes()); for (union_obj.fields.values()) |value| { - if (value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only)) + if (try value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) return true; } else { return false; @@ -2383,29 +2413,32 @@ pub const Type = extern union { }, .union_tagged => { const union_obj = ty.castTag(.union_tagged).?.data; - if (union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only)) { + if (try union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) { return true; } + if (sema_kit) |sk| { + _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty); + } assert(union_obj.haveFieldTypes()); for (union_obj.fields.values()) |value| { - if (value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only)) + if (try value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) return true; } else { return false; } }, - .array, .vector => ty.arrayLen() != 0 and - ty.elemType().hasRuntimeBitsAdvanced(ignore_comptime_only), - .array_u8 => ty.arrayLen() != 0, - .array_sentinel => ty.childType().hasRuntimeBitsAdvanced(ignore_comptime_only), + .array, .vector => return ty.arrayLen() != 0 and + try ty.elemType().hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit), + .array_u8 => return ty.arrayLen() != 0, + .array_sentinel => return ty.childType().hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit), - .int_signed, .int_unsigned => ty.cast(Payload.Bits).?.data != 0, + .int_signed, .int_unsigned => return ty.cast(Payload.Bits).?.data != 0, .error_union => { const payload = ty.castTag(.error_union).?.data; - return payload.error_set.hasRuntimeBitsAdvanced(ignore_comptime_only) or - payload.payload.hasRuntimeBitsAdvanced(ignore_comptime_only); + return (try payload.error_set.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) or + (try payload.payload.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)); }, .tuple, .anon_struct => { @@ -2413,7 +2446,7 @@ pub const Type = extern union { for (tuple.types) |field_ty, i| { const val = tuple.values[i]; if (val.tag() != .unreachable_value) continue; // comptime field - if (field_ty.hasRuntimeBitsAdvanced(ignore_comptime_only)) return true; + if (try field_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) return true; } return false; }, @@ -2422,7 +2455,7 @@ pub const Type = extern union { .inferred_alloc_mut => unreachable, .var_args_param => unreachable, .generic_poison => unreachable, - }; + } } /// true if and only if the type has a well-defined memory layout @@ -2548,11 +2581,11 @@ pub const Type = extern union { } pub fn hasRuntimeBits(ty: Type) bool { - return hasRuntimeBitsAdvanced(ty, false); + return hasRuntimeBitsAdvanced(ty, false, null) catch unreachable; } pub fn hasRuntimeBitsIgnoreComptime(ty: Type) bool { - return hasRuntimeBitsAdvanced(ty, true); + return hasRuntimeBitsAdvanced(ty, true, null) catch unreachable; } pub fn isFnOrHasRuntimeBits(ty: Type) bool { @@ -4538,6 +4571,8 @@ pub const Type = extern union { /// During semantic analysis, instead call `Sema.typeRequiresComptime` which /// resolves field types rather than asserting they are already resolved. + /// TODO merge these implementations together with the "advanced" pattern seen + /// elsewhere in this file. pub fn comptimeOnly(ty: Type) bool { return switch (ty.tag()) { .u1, diff --git a/src/value.zig b/src/value.zig index b54f296a24..fb9a2f826a 100644 --- a/src/value.zig +++ b/src/value.zig @@ -9,6 +9,7 @@ const Allocator = std.mem.Allocator; const Module = @import("Module.zig"); const Air = @import("Air.zig"); const TypedValue = @import("TypedValue.zig"); +const Sema = @import("Sema.zig"); /// This is the raw data, with no bookkeeping, no memory awareness, /// no de-duplication, and no type system awareness. @@ -990,6 +991,16 @@ pub const Value = extern union { /// Asserts the value is an integer. pub fn toBigInt(val: Value, space: *BigIntSpace, target: Target) BigIntConst { + return val.toBigIntAdvanced(space, target, null) catch unreachable; + } + + /// Asserts the value is an integer. + pub fn toBigIntAdvanced( + val: Value, + space: *BigIntSpace, + target: Target, + sema_kit: ?Module.WipAnalysis, + ) !BigIntConst { switch (val.tag()) { .zero, .bool_false, @@ -1008,7 +1019,11 @@ pub const Value = extern union { .undef => unreachable, .lazy_align => { - const x = val.castTag(.lazy_align).?.data.abiAlignment(target); + const ty = val.castTag(.lazy_align).?.data; + if (sema_kit) |sk| { + try sk.sema.resolveTypeLayout(sk.block, sk.src, ty); + } + const x = ty.abiAlignment(target); return BigIntMutable.init(&space.limbs, x).toConst(); }, @@ -1019,6 +1034,12 @@ pub const Value = extern union { /// If the value fits in a u64, return it, otherwise null. /// Asserts not undefined. pub fn getUnsignedInt(val: Value, target: Target) ?u64 { + return getUnsignedIntAdvanced(val, target, null) catch unreachable; + } + + /// If the value fits in a u64, return it, otherwise null. + /// Asserts not undefined. + pub fn getUnsignedIntAdvanced(val: Value, target: Target, sema_kit: ?Module.WipAnalysis) !?u64 { switch (val.tag()) { .zero, .bool_false, @@ -1036,7 +1057,13 @@ pub const Value = extern union { .undef => unreachable, - .lazy_align => return val.castTag(.lazy_align).?.data.abiAlignment(target), + .lazy_align => { + const ty = val.castTag(.lazy_align).?.data; + if (sema_kit) |sk| { + try sk.sema.resolveTypeLayout(sk.block, sk.src, ty); + } + return ty.abiAlignment(target); + }, else => return null, } @@ -1777,6 +1804,10 @@ pub const Value = extern union { } pub fn orderAgainstZero(lhs: Value) std.math.Order { + return orderAgainstZeroAdvanced(lhs, null) catch unreachable; + } + + pub fn orderAgainstZeroAdvanced(lhs: Value, sema_kit: ?Module.WipAnalysis) !std.math.Order { return switch (lhs.tag()) { .zero, .bool_false, @@ -1799,7 +1830,7 @@ pub const Value = extern union { .lazy_align => { const ty = lhs.castTag(.lazy_align).?.data; - if (ty.hasRuntimeBitsIgnoreComptime()) { + if (try ty.hasRuntimeBitsAdvanced(false, sema_kit)) { return .gt; } else { return .eq; @@ -1818,10 +1849,16 @@ pub const Value = extern union { /// Asserts the value is comparable. pub fn order(lhs: Value, rhs: Value, target: Target) std.math.Order { + return orderAdvanced(lhs, rhs, target, null) catch unreachable; + } + + /// Asserts the value is comparable. + /// If sema_kit is null then this function asserts things are resolved and cannot fail. + pub fn orderAdvanced(lhs: Value, rhs: Value, target: Target, sema_kit: ?Module.WipAnalysis) !std.math.Order { const lhs_tag = lhs.tag(); const rhs_tag = rhs.tag(); - const lhs_against_zero = lhs.orderAgainstZero(); - const rhs_against_zero = rhs.orderAgainstZero(); + const lhs_against_zero = try lhs.orderAgainstZeroAdvanced(sema_kit); + const rhs_against_zero = try rhs.orderAgainstZeroAdvanced(sema_kit); switch (lhs_against_zero) { .lt => if (rhs_against_zero != .lt) return .lt, .eq => return rhs_against_zero.invert(), @@ -1855,14 +1892,24 @@ pub const Value = extern union { var lhs_bigint_space: BigIntSpace = undefined; var rhs_bigint_space: BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_bigint_space, target); - const rhs_bigint = rhs.toBigInt(&rhs_bigint_space, target); + const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_bigint_space, target, sema_kit); + const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_bigint_space, target, sema_kit); return lhs_bigint.order(rhs_bigint); } /// Asserts the value is comparable. Does not take a type parameter because it supports /// comparisons between heterogeneous types. pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, target: Target) bool { + return compareHeteroAdvanced(lhs, op, rhs, target, null) catch unreachable; + } + + pub fn compareHeteroAdvanced( + lhs: Value, + op: std.math.CompareOperator, + rhs: Value, + target: Target, + sema_kit: ?Module.WipAnalysis, + ) !bool { if (lhs.pointerDecl()) |lhs_decl| { if (rhs.pointerDecl()) |rhs_decl| { switch (op) { @@ -1884,7 +1931,7 @@ pub const Value = extern union { else => {}, } } - return order(lhs, rhs, target).compare(op); + return (try orderAdvanced(lhs, rhs, target, sema_kit)).compare(op); } /// Asserts the values are comparable. Both operands have type `ty`. diff --git a/test/behavior/struct_contains_slice_of_itself.zig b/test/behavior/struct_contains_slice_of_itself.zig index 11b838a758..cbff2514ea 100644 --- a/test/behavior/struct_contains_slice_of_itself.zig +++ b/test/behavior/struct_contains_slice_of_itself.zig @@ -1,3 +1,4 @@ +const builtin = @import("builtin"); const expect = @import("std").testing.expect; const Node = struct { @@ -11,6 +12,10 @@ const NodeAligned = struct { }; test "struct contains slice of itself" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + var other_nodes = [_]Node{ Node{ .payload = 31, @@ -48,6 +53,10 @@ test "struct contains slice of itself" { } test "struct contains aligned slice of itself" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + var other_nodes = [_]NodeAligned{ NodeAligned{ .payload = 31, From 44f9061b718e9bdcd43d258291f89930b74aa56a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 22 Mar 2022 15:58:19 -0700 Subject: [PATCH 0862/2031] fix merge conflicts and test cases --- src/Sema.zig | 17 +++++++++++++---- test/stage2/x86_64.zig | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 2428db5b0f..e6447ea2ef 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -19732,7 +19732,10 @@ fn analyzeSlice( block, end_src, "end index {} out of bounds for array of length {}", - .{ end_val.fmtValue(Type.usize), len_val.fmtValue(Type.usize) }, + .{ + end_val.fmtValue(Type.usize, target), + len_val.fmtValue(Type.usize, target), + }, ); } if (end_val.eql(len_val, Type.usize, target)) { @@ -19758,7 +19761,10 @@ fn analyzeSlice( block, end_src, "end index {} out of bounds for slice of length {}", - .{ end_val.fmtValue(Type.usize), slice_len_val.fmtValue(Type.usize) }, + .{ + end_val.fmtValue(Type.usize, target), + slice_len_val.fmtValue(Type.usize, target), + }, ); } if (end_val.eql(slice_len_val, Type.usize, target)) { @@ -19794,12 +19800,15 @@ fn analyzeSlice( // requirement: start <= end if (try sema.resolveDefinedValue(block, src, end)) |end_val| { if (try sema.resolveDefinedValue(block, src, start)) |start_val| { - if (start_val.compare(.gt, end_val, Type.usize)) { + if (start_val.compare(.gt, end_val, Type.usize, target)) { return sema.fail( block, start_src, "start index {} is larger than end index {}", - .{ start_val.fmtValue(Type.usize), end_val.fmtValue(Type.usize) }, + .{ + start_val.fmtValue(Type.usize, target), + end_val.fmtValue(Type.usize, target), + }, ); } } diff --git a/test/stage2/x86_64.zig b/test/stage2/x86_64.zig index 38dafd8fc0..a7ebce36d3 100644 --- a/test/stage2/x86_64.zig +++ b/test/stage2/x86_64.zig @@ -1166,7 +1166,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ _ = x; \\} , &[_][]const u8{ - ":2:9: error: variable of type '@Type(.Null)' must be const or comptime", + ":2:9: error: variable of type '@TypeOf(null)' must be const or comptime", }); } From cb6364624fb28bf51adb2dc16c8e93a30c33c76b Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 22 Mar 2022 13:26:35 -0700 Subject: [PATCH 0863/2031] stage2: slice behavior test passes, just has diff behavior from stage1 This is from discussions from #11249. The stage2 behavior is correct and is strictly more accurate, so we'd prefer to keep it. In that case, I modified the behavior tests to have the conditional between stage1/stage2 and get this test passing. --- test/behavior/slice.zig | 89 ++++++++++++++++++++++++++++++++--------- 1 file changed, 71 insertions(+), 18 deletions(-) diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 9177ba96ad..6b72e169fa 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -343,7 +343,10 @@ test "@ptrCast slice to pointer" { } test "slice syntax resulting in pointer-to-array" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -356,8 +359,6 @@ test "slice syntax resulting in pointer-to-array" { try testPointer0(); try testPointerAlign(); try testSlice(); - try testSliceZ(); - try testSlice0(); try testSliceOpt(); try testSliceAlign(); } @@ -460,21 +461,6 @@ test "slice syntax resulting in pointer-to-array" { comptime try expect(@TypeOf(slice.?[0..2]) == *[2]u8); } - fn testSlice0() !void { - { - var array = [0]u8{}; - var src_slice: []u8 = &array; - var slice = src_slice[0..0]; - comptime try expect(@TypeOf(slice) == *[0]u8); - } - { - var array = [0:0]u8{}; - var src_slice: [:0]u8 = &array; - var slice = src_slice[0..0]; - comptime try expect(@TypeOf(slice) == *[0]u8); - } - } - fn testSliceAlign() !void { var array align(4) = [5]u8{ 1, 2, 3, 4, 5 }; var src_slice: []align(4) u8 = &array; @@ -494,6 +480,73 @@ test "slice syntax resulting in pointer-to-array" { comptime try S.doTheTest(); } +test "slice pointer-to-array null terminated" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + comptime { + var array = [5:0]u8{ 1, 2, 3, 4, 5 }; + var slice: [:0]u8 = &array; + try expect(@TypeOf(slice[1..3]) == *[2]u8); + try expect(@TypeOf(slice[1..3 :4]) == *[2:4]u8); + + if (builtin.zig_backend == .stage1) { + try expect(@TypeOf(slice[1..]) == [:0]u8); + } else { + // stage2 gives a more accurate, correct answer + try expect(@TypeOf(slice[1..]) == *[4:0]u8); + } + } + + var array = [5:0]u8{ 1, 2, 3, 4, 5 }; + var slice: [:0]u8 = &array; + comptime try expect(@TypeOf(slice[1..3]) == *[2]u8); + comptime try expect(@TypeOf(slice[1..3 :4]) == *[2:4]u8); + comptime try expect(@TypeOf(slice[1..]) == [:0]u8); +} + +test "slice pointer-to-array zero length" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + comptime { + { + var array = [0]u8{}; + var src_slice: []u8 = &array; + var slice = src_slice[0..0]; + try expect(@TypeOf(slice) == *[0]u8); + } + { + var array = [0:0]u8{}; + var src_slice: [:0]u8 = &array; + var slice = src_slice[0..0]; + if (builtin.zig_backend == .stage1) { + try expect(@TypeOf(slice) == *[0]u8); + } else { + // stage2 gives a more accurate, correct answer + try expect(@TypeOf(slice) == *[0:0]u8); + } + } + } + + { + var array = [0]u8{}; + var src_slice: []u8 = &array; + var slice = src_slice[0..0]; + comptime try expect(@TypeOf(slice) == *[0]u8); + } + { + var array = [0:0]u8{}; + var src_slice: [:0]u8 = &array; + var slice = src_slice[0..0]; + comptime try expect(@TypeOf(slice) == *[0]u8); + } +} + test "type coercion of pointer to anon struct literal to pointer to slice" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO From 6ac04d8fd7d63dfd4aa611e1564f1d4768baf408 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sun, 20 Mar 2022 13:48:14 +0100 Subject: [PATCH 0864/2031] stage2 ARM: change semantics of MCValue.stack_offset A stack_offset will now denote the exact offset applied to the start of the stack frame (=fp when frame pointer is emitted) --- src/arch/arm/CodeGen.zig | 112 +++++++++++++++------------------------ 1 file changed, 44 insertions(+), 68 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 1654be162a..1653e07c47 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -396,8 +396,8 @@ fn gen(self: *Self) !void { // The address of where to store the return value is in // r0. As this register might get overwritten along the // way, save the address to the stack. - const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset, 4); - self.next_stack_offset = stack_offset + 4; + const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset, 4) + 4; + self.next_stack_offset = stack_offset; self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset); try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = .r0 }); @@ -780,10 +780,9 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u if (abi_align > self.stack_align) self.stack_align = abi_align; // TODO find a free slot instead of always appending - const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align); - self.next_stack_offset = offset + abi_size; - if (self.next_stack_offset > self.max_end_stack) - self.max_end_stack = self.next_stack_offset; + const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size; + self.next_stack_offset = offset; + self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset); try self.stack.putNoClobber(self.gpa, offset, .{ .inst = inst, .size = abi_size, @@ -797,8 +796,10 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { if (!elem_ty.hasRuntimeBits()) { // As this stack item will never be dereferenced at runtime, - // return the current stack offset - return self.next_stack_offset; + // return the stack offset 0. Stack offset 0 will be where all + // zero-sized stack allocations live as non-zero-sized + // allocations will always have an offset > 0. + return @as(u32, 0); } const target = self.target.*; @@ -1161,8 +1162,8 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { const len_ty = self.air.typeOf(bin_op.rhs); const stack_offset = try self.allocMem(inst, 8, 8); - try self.genSetStack(ptr_ty, stack_offset + 4, ptr); - try self.genSetStack(len_ty, stack_offset, len); + try self.genSetStack(ptr_ty, stack_offset, ptr); + try self.genSetStack(len_ty, stack_offset - 4, len); break :result MCValue{ .stack_offset = stack_offset }; }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); @@ -1180,36 +1181,18 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airAddWrap(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 addwrap for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airAddSat(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 add_sat for {}", .{self.target.cpu.arch}); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airSubWrap(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 subwrap for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airSubSat(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 sub_sat for {}", .{self.target.cpu.arch}); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airMulWrap(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 mulwrap for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airMulSat(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 mul_sat for {}", .{self.target.cpu.arch}); @@ -1327,12 +1310,14 @@ fn airWrapOptional(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 result: { const optional_ty = self.air.typeOfIndex(inst); + const abi_size = @intCast(u32, optional_ty.abiSize(self.target.*)); // Optional with a zero-bit payload type is just a boolean true - if (optional_ty.abiSize(self.target.*) == 1) + if (abi_size == 1) { break :result MCValue{ .immediate = 1 }; - - return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}); + } else { + return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}); + } }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -1366,7 +1351,7 @@ fn slicePtr(self: *Self, mcv: MCValue) !MCValue { return MCValue{ .stack_argument_offset = off + 4 }; }, .stack_offset => |off| { - return MCValue{ .stack_offset = off + 4 }; + return MCValue{ .stack_offset = off }; }, .memory => |addr| { return MCValue{ .memory = addr }; @@ -1395,7 +1380,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { break :result MCValue{ .stack_argument_offset = off }; }, .stack_offset => |off| { - break :result MCValue{ .stack_offset = off }; + break :result MCValue{ .stack_offset = off - 4 }; }, .memory => |addr| { break :result MCValue{ .memory = addr + 4 }; @@ -1413,7 +1398,7 @@ fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void { switch (mcv) { .dead, .unreach => unreachable, .ptr_stack_offset => |off| { - break :result MCValue{ .ptr_stack_offset = off }; + break :result MCValue{ .ptr_stack_offset = off - 4 }; }, else => return self.fail("TODO implement ptr_slice_len_ptr for {}", .{mcv}), } @@ -1428,7 +1413,7 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { switch (mcv) { .dead, .unreach => unreachable, .ptr_stack_offset => |off| { - break :result MCValue{ .ptr_stack_offset = off + 4 }; + break :result MCValue{ .ptr_stack_offset = off }; }, else => return self.fail("TODO implement ptr_slice_ptr_ptr for {}", .{mcv}), } @@ -1860,13 +1845,10 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde const mcv = try self.resolveInst(operand); const ptr_ty = self.air.typeOf(operand); const struct_ty = ptr_ty.childType(); - const struct_size = @intCast(u32, struct_ty.abiSize(self.target.*)); const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); - const struct_field_ty = struct_ty.structFieldType(index); - const struct_field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*)); switch (mcv) { .ptr_stack_offset => |off| { - break :result MCValue{ .ptr_stack_offset = off + struct_size - struct_field_offset - struct_field_size }; + break :result MCValue{ .ptr_stack_offset = off - struct_field_offset }; }, else => { const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ @@ -1914,7 +1896,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { break :result MCValue{ .stack_argument_offset = off + adjusted_field_offset }; }, .stack_offset => |off| { - break :result MCValue{ .stack_offset = off + adjusted_field_offset }; + break :result MCValue{ .stack_offset = off - struct_field_offset }; }, .memory => |addr| { break :result MCValue{ .memory = addr + adjusted_field_offset }; @@ -2871,10 +2853,9 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { if (abi_align > self.stack_align) self.stack_align = abi_align; // TODO find a free slot instead of always appending - const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align); - self.next_stack_offset = offset + abi_size; - if (self.next_stack_offset > self.max_end_stack) - self.max_end_stack = self.next_stack_offset; + const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size; + self.next_stack_offset = offset; + self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset); const tmp_mcv = MCValue{ .stack_offset = offset }; try self.load(tmp_mcv, ptr, ptr_ty); @@ -3192,7 +3173,9 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { if (!error_type.hasRuntimeBits()) { return MCValue{ .immediate = 0 }; // always false - } else if (!payload_type.hasRuntimeBits()) { + } + + if (!payload_type.hasRuntimeBits()) { if (error_type.abiSize(self.target.*) <= 4) { const reg_mcv: MCValue = switch (operand) { .register => operand, @@ -3620,13 +3603,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); }, .register => |reg| { - const adj_off = stack_offset + abi_size; - switch (abi_size) { 1, 4 => { - const offset = if (math.cast(u12, adj_off)) |imm| blk: { + const offset = if (math.cast(u12, stack_offset)) |imm| blk: { break :blk Instruction.Offset.imm(imm); - } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), .none); + } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = stack_offset }), .none); const tag: Mir.Inst.Tag = switch (abi_size) { 1 => .strb, @@ -3647,9 +3628,9 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro }); }, 2 => { - const offset = if (adj_off <= math.maxInt(u8)) blk: { - break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off)); - } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off })); + const offset = if (stack_offset <= math.maxInt(u8)) blk: { + break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, stack_offset)); + } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = stack_offset })); _ = try self.addInst(.{ .tag = .strh, @@ -3739,13 +3720,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void // Write the debug undefined value. return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }); }, - .ptr_stack_offset => |unadjusted_off| { + .ptr_stack_offset => |off| { // TODO: maybe addressing from sp instead of fp - const elem_ty = ty.childType(); - const abi_size = @intCast(u32, elem_ty.abiSize(self.target.*)); - const adj_off = unadjusted_off + abi_size; - - const op = Instruction.Operand.fromU32(adj_off) orelse + const op = Instruction.Operand.fromU32(off) orelse return self.fail("TODO larger stack offsets", .{}); _ = try self.addInst(.{ @@ -3919,10 +3896,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void try self.genSetReg(ty, reg, .{ .immediate = @intCast(u32, addr) }); try self.genLdrRegister(reg, reg, ty); }, - .stack_offset => |unadjusted_off| { + .stack_offset => |off| { // TODO: maybe addressing from sp instead of fp const abi_size = @intCast(u32, ty.abiSize(self.target.*)); - const adj_off = unadjusted_off + abi_size; const tag: Mir.Inst.Tag = switch (abi_size) { 1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb else .ldrb, @@ -3939,9 +3915,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }; if (extra_offset) { - const offset = if (adj_off <= math.maxInt(u8)) blk: { - break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off)); - } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off })); + const offset = if (off <= math.maxInt(u8)) blk: { + break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, off)); + } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.usize), MCValue{ .immediate = off })); _ = try self.addInst(.{ .tag = tag, @@ -3955,9 +3931,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void } }, }); } else { - const offset = if (adj_off <= math.maxInt(u12)) blk: { - break :blk Instruction.Offset.imm(@intCast(u12, adj_off)); - } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), .none); + const offset = if (off <= math.maxInt(u12)) blk: { + break :blk Instruction.Offset.imm(@intCast(u12, off)); + } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.usize), MCValue{ .immediate = off }), .none); _ = try self.addInst(.{ .tag = tag, @@ -4136,8 +4112,8 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { const array_len = @intCast(u32, array_ty.arrayLen()); const stack_offset = try self.allocMem(inst, 8, 8); - try self.genSetStack(ptr_ty, stack_offset + 4, ptr); - try self.genSetStack(Type.initTag(.usize), stack_offset, .{ .immediate = array_len }); + try self.genSetStack(ptr_ty, stack_offset, ptr); + try self.genSetStack(Type.initTag(.usize), stack_offset - 4, .{ .immediate = array_len }); break :result MCValue{ .stack_offset = stack_offset }; }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); From a4e8294c91d21e6c574bbae94e064935d90e8e95 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Tue, 22 Mar 2022 13:24:42 +0100 Subject: [PATCH 0865/2031] stage2 ARM: change semantics of MCValue.stack_argument_offset MCValue.stack_argument_offset now has the same semantics as MCValue.stack_offset --- src/arch/arm/CodeGen.zig | 53 ++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 1653e07c47..e2744e7459 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1348,7 +1348,7 @@ fn slicePtr(self: *Self, mcv: MCValue) !MCValue { .dead, .unreach => unreachable, .register => unreachable, // a slice doesn't fit in one register .stack_argument_offset => |off| { - return MCValue{ .stack_argument_offset = off + 4 }; + return MCValue{ .stack_argument_offset = off }; }, .stack_offset => |off| { return MCValue{ .stack_offset = off }; @@ -1377,7 +1377,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { .dead, .unreach => unreachable, .register => unreachable, // a slice doesn't fit in one register .stack_argument_offset => |off| { - break :result MCValue{ .stack_argument_offset = off }; + break :result MCValue{ .stack_argument_offset = off - 4 }; }, .stack_offset => |off| { break :result MCValue{ .stack_offset = off - 4 }; @@ -1782,14 +1782,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); }, .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = @intCast(u32, addr) }), - .stack_argument_offset => |unadjusted_off| { - const adj_off = unadjusted_off + elem_size; - + .stack_argument_offset => |off| { _ = try self.addInst(.{ .tag = .ldr_ptr_stack_argument, .data = .{ .r_stack_offset = .{ .rt = src_reg, - .stack_offset = adj_off, + .stack_offset = off, } }, }); }, @@ -1884,22 +1882,18 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const mcv = try self.resolveInst(operand); const struct_ty = self.air.typeOf(operand); - const struct_size = @intCast(u32, struct_ty.abiSize(self.target.*)); const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); - const struct_field_ty = struct_ty.structFieldType(index); - const struct_field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*)); - const adjusted_field_offset = struct_size - struct_field_offset - struct_field_size; switch (mcv) { .dead, .unreach => unreachable, .stack_argument_offset => |off| { - break :result MCValue{ .stack_argument_offset = off + adjusted_field_offset }; + break :result MCValue{ .stack_argument_offset = off - struct_field_offset }; }, .stack_offset => |off| { break :result MCValue{ .stack_offset = off - struct_field_offset }; }, .memory => |addr| { - break :result MCValue{ .memory = addr + adjusted_field_offset }; + break :result MCValue{ .memory = addr + struct_field_offset }; }, else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), } @@ -3683,14 +3677,12 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); }, .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(u32, addr) }), - .stack_argument_offset => |unadjusted_off| { - const adj_off = unadjusted_off + abi_size; - + .stack_argument_offset => |off| { _ = try self.addInst(.{ .tag = .ldr_ptr_stack_argument, .data = .{ .r_stack_offset = .{ .rt = src_reg, - .stack_offset = adj_off, + .stack_offset = off, } }, }); }, @@ -3948,9 +3940,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }); } }, - .stack_argument_offset => |unadjusted_off| { + .stack_argument_offset => |off| { const abi_size = ty.abiSize(self.target.*); - const adj_off = unadjusted_off + abi_size; const tag: Mir.Inst.Tag = switch (abi_size) { 1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb_stack_argument else .ldrb_stack_argument, @@ -3963,7 +3954,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .tag = tag, .data = .{ .r_stack_offset = .{ .rt = reg, - .stack_offset = @intCast(u32, adj_off), + .stack_offset = off, } }, }); }, @@ -3979,7 +3970,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I if (!self.wantSafety()) return; // The already existing value will do just fine. // TODO Upgrade this to a memset call when we have that available. - switch (ty.abiSize(self.target.*)) { + switch (abi_size) { 1 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaa }), 2 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaa }), 4 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }), @@ -3987,13 +3978,11 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I } }, .register => |reg| { - const adj_off = stack_offset - abi_size; - switch (abi_size) { 1, 4 => { - const offset = if (math.cast(u12, adj_off)) |imm| blk: { + const offset = if (math.cast(u12, stack_offset)) |imm| blk: { break :blk Instruction.Offset.imm(imm); - } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), .none); + } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = stack_offset }), .none); const tag: Mir.Inst.Tag = switch (abi_size) { 1 => .strb, @@ -4011,9 +4000,9 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I }); }, 2 => { - const offset = if (adj_off <= math.maxInt(u8)) blk: { - break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off)); - } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off })); + const offset = if (stack_offset <= math.maxInt(u8)) blk: { + break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, stack_offset)); + } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = stack_offset })); _ = try self.addInst(.{ .tag = .strh, @@ -4060,8 +4049,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I } // add dst_reg, sp, #stack_offset - const adj_dst_offset = stack_offset - abi_size; - const dst_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_dst_offset)) |x| x else { + const dst_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(stack_offset)) |x| x else { return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); }; _ = try self.addInst(.{ @@ -4553,8 +4541,8 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { if (ty.abiAlignment(self.target.*) == 8) nsaa = std.mem.alignForwardGeneric(u32, nsaa, 8); - result.args[i] = .{ .stack_argument_offset = nsaa }; nsaa += param_size; + result.args[i] = .{ .stack_argument_offset = nsaa }; } } @@ -4583,9 +4571,10 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { for (param_types) |ty, i| { if (ty.abiSize(self.target.*) > 0) { - stack_offset = std.mem.alignForwardGeneric(u32, stack_offset, ty.abiAlignment(self.target.*)); + const param_size = @intCast(u32, ty.abiSize(self.target.*)); + + stack_offset = std.mem.alignForwardGeneric(u32, stack_offset, ty.abiAlignment(self.target.*)) + param_size; result.args[i] = .{ .stack_argument_offset = stack_offset }; - stack_offset += @intCast(u32, ty.abiSize(self.target.*)); } else { result.args[i] = .{ .none = {} }; } From 62529a291bd41eccbafeda602d7efcb4ec946939 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Tue, 22 Mar 2022 15:53:43 +0100 Subject: [PATCH 0866/2031] stage2 ARM: More support for error unions --- src/arch/arm/CodeGen.zig | 139 +++++++++++++++++++++++---------------- 1 file changed, 84 insertions(+), 55 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index e2744e7459..13093f1fbb 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1261,27 +1261,84 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airWrapOptional(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 result: { + const optional_ty = self.air.typeOfIndex(inst); + const abi_size = @intCast(u32, optional_ty.abiSize(self.target.*)); + + // Optional with a zero-bit payload type is just a boolean true + if (abi_size == 1) { + break :result MCValue{ .immediate = 1 }; + } else { + return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}); + } + }; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + +/// Given an error union, returns the error +fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { + const payload_ty = error_union_ty.errorUnionPayload(); + if (!payload_ty.hasRuntimeBits()) return error_union_mcv; + + switch (error_union_mcv) { + .register => return self.fail("TODO errUnionErr for registers", .{}), + .stack_argument_offset => |off| { + return MCValue{ .stack_argument_offset = off }; + }, + .stack_offset => |off| { + return MCValue{ .stack_offset = off }; + }, + .memory => |addr| { + return MCValue{ .memory = addr }; + }, + else => unreachable, // invalid MCValue for an error union + } +} + fn airUnwrapErrErr(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 result: { const error_union_ty = self.air.typeOf(ty_op.operand); - const payload_ty = error_union_ty.errorUnionPayload(); const mcv = try self.resolveInst(ty_op.operand); - if (!payload_ty.hasRuntimeBits()) break :result mcv; - - return self.fail("TODO implement unwrap error union error for non-empty payloads", .{}); + break :result try self.errUnionErr(mcv, error_union_ty); }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +/// Given an error union, returns the payload +fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { + const payload_ty = error_union_ty.errorUnionPayload(); + if (!payload_ty.hasRuntimeBits()) return MCValue.none; + + const error_ty = error_union_ty.errorUnionSet(); + const error_size = @intCast(u32, error_ty.abiSize(self.target.*)); + const eu_align = @intCast(u32, error_union_ty.abiAlignment(self.target.*)); + const offset = std.mem.alignForwardGeneric(u32, error_size, eu_align); + + // TODO optimization for small error unions: put into register + switch (error_union_mcv) { + .register => return self.fail("TODO errUnionPayload for registers", .{}), + .stack_argument_offset => |off| { + return MCValue{ .stack_argument_offset = off - offset }; + }, + .stack_offset => |off| { + return MCValue{ .stack_offset = off - offset }; + }, + .memory => |addr| { + return MCValue{ .memory = addr - offset }; + }, + else => unreachable, // invalid MCValue for an error union + } +} + fn airUnwrapErrPayload(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 result: { const error_union_ty = self.air.typeOf(ty_op.operand); - const payload_ty = error_union_ty.errorUnionPayload(); - if (!payload_ty.hasRuntimeBits()) break :result MCValue.none; - - return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{}); + const mcv = try self.resolveInst(ty_op.operand); + break :result try self.errUnionPayload(mcv, error_union_ty); }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -1306,22 +1363,6 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } -fn airWrapOptional(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 result: { - const optional_ty = self.air.typeOfIndex(inst); - const abi_size = @intCast(u32, optional_ty.abiSize(self.target.*)); - - // Optional with a zero-bit payload type is just a boolean true - if (abi_size == 1) { - break :result MCValue{ .immediate = 1 }; - } else { - return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}); - } - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - /// T to E!T fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; @@ -1343,9 +1384,9 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } -fn slicePtr(self: *Self, mcv: MCValue) !MCValue { +/// Given a slice, returns the length +fn slicePtr(mcv: MCValue) MCValue { switch (mcv) { - .dead, .unreach => unreachable, .register => unreachable, // a slice doesn't fit in one register .stack_argument_offset => |off| { return MCValue{ .stack_argument_offset = off }; @@ -1356,7 +1397,7 @@ fn slicePtr(self: *Self, mcv: MCValue) !MCValue { .memory => |addr| { return MCValue{ .memory = addr }; }, - else => return self.fail("TODO implement slice_ptr for {}", .{mcv}), + else => unreachable, // invalid MCValue for a slice } } @@ -1364,7 +1405,7 @@ fn airSlicePtr(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 result: { const mcv = try self.resolveInst(ty_op.operand); - break :result try self.slicePtr(mcv); + break :result slicePtr(mcv); }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -1444,7 +1485,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { if (index_is_register) self.register_manager.freezeRegs(&.{index_mcv.register}); defer if (index_is_register) self.register_manager.unfreezeRegs(&.{index_mcv.register}); - const base_mcv = try self.slicePtr(slice_mcv); + const base_mcv = slicePtr(slice_mcv); switch (elem_size) { 1, 4 => { @@ -1507,7 +1548,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const slice_mcv = try self.resolveInst(extra.lhs); const index_mcv = try self.resolveInst(extra.rhs); - const base_mcv = try self.slicePtr(slice_mcv); + const base_mcv = slicePtr(slice_mcv); const slice_ty = self.air.typeOf(extra.lhs); @@ -3163,35 +3204,15 @@ fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue { fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { const error_type = ty.errorUnionSet(); - const payload_type = ty.errorUnionPayload(); + const error_int_type = Type.initTag(.u16); if (!error_type.hasRuntimeBits()) { return MCValue{ .immediate = 0 }; // always false } - if (!payload_type.hasRuntimeBits()) { - if (error_type.abiSize(self.target.*) <= 4) { - const reg_mcv: MCValue = switch (operand) { - .register => operand, - else => .{ .register = try self.copyToTmpRegister(error_type, operand) }, - }; - - _ = try self.addInst(.{ - .tag = .cmp, - .data = .{ .rr_op = .{ - .rd = undefined, - .rn = reg_mcv.register, - .op = Instruction.Operand.fromU32(0).?, - } }, - }); - - return MCValue{ .compare_flags_unsigned = .gt }; - } else { - return self.fail("TODO isErr for errors with size > 4", .{}); - } - } else { - return self.fail("TODO isErr for non-empty payloads", .{}); - } + const error_mcv = try self.errUnionErr(operand, ty); + _ = try self.binOp(.cmp_eq, null, error_mcv, .{ .immediate = 0 }, error_int_type, error_int_type); + return MCValue{ .compare_flags_unsigned = .gt }; } fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { @@ -4044,7 +4065,15 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); }, .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(u32, addr) }), - .stack_argument_offset => return self.fail("TODO genSetStackArgument src={}", .{mcv}), + .stack_argument_offset => |off| { + _ = try self.addInst(.{ + .tag = .ldr_ptr_stack_argument, + .data = .{ .r_stack_offset = .{ + .rt = src_reg, + .stack_offset = off, + } }, + }); + }, else => unreachable, } From 95e166b2e1609e93b472324c91b2d360abd595c2 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Tue, 22 Mar 2022 19:54:53 +0100 Subject: [PATCH 0867/2031] stage2 ARM: implement min, max for integers <= 32 bits --- src/arch/arm/CodeGen.zig | 120 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 112 insertions(+), 8 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 13093f1fbb..87f6b9eb10 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -535,8 +535,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .mod => try self.airMod(inst), .shl, .shl_exact => try self.airBinOp(inst), .shl_sat => try self.airShlSat(inst), - .min => try self.airMin(inst), - .max => try self.airMax(inst), + .min => try self.airMinMax(inst), + .max => try self.airMinMax(inst), .slice => try self.airSlice(inst), .sqrt, @@ -1140,15 +1140,119 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } -fn airMin(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 min for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +fn minMax( + self: *Self, + tag: Air.Inst.Tag, + maybe_inst: ?Air.Inst.Index, + lhs: MCValue, + rhs: MCValue, + lhs_ty: Type, + rhs_ty: Type, +) !MCValue { + switch (lhs_ty.zigTypeTag()) { + .Float => return self.fail("TODO ARM min/max on floats", .{}), + .Vector => return self.fail("TODO ARM min/max on vectors", .{}), + .Int => { + assert(lhs_ty.eql(rhs_ty)); + const int_info = lhs_ty.intInfo(self.target.*); + if (int_info.bits <= 32) { + const lhs_is_register = lhs == .register; + const rhs_is_register = rhs == .register; + + const lhs_reg = switch (lhs) { + .register => |r| r, + else => try self.copyToTmpRegister(lhs_ty, lhs), + }; + self.register_manager.freezeRegs(&.{lhs_reg}); + defer self.register_manager.unfreezeRegs(&.{lhs_reg}); + + const rhs_reg = switch (rhs) { + .register => |r| r, + else => try self.copyToTmpRegister(rhs_ty, rhs), + }; + self.register_manager.freezeRegs(&.{rhs_reg}); + defer self.register_manager.unfreezeRegs(&.{rhs_reg}); + + const dest_reg = if (maybe_inst) |inst| blk: { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + + if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { + break :blk lhs_reg; + } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { + break :blk rhs_reg; + } else { + break :blk try self.register_manager.allocReg(inst); + } + } else try self.register_manager.allocReg(null); + + // lhs == reg should have been checked by airMinMax + // + // By guaranteeing lhs != rhs, we guarantee (dst != + // lhs) or (dst != rhs), which is a property we use to + // omit generating one instruction when we reuse a + // register. + assert(lhs_reg != rhs_reg); // see note above + + _ = try self.binOpRegister(.cmp_eq, null, .{ .register = lhs_reg }, .{ .register = rhs_reg }, lhs_ty, rhs_ty); + + const cond_choose_lhs: Condition = switch (tag) { + .max => switch (int_info.signedness) { + .signed => Condition.gt, + .unsigned => Condition.hi, + }, + .min => switch (int_info.signedness) { + .signed => Condition.lt, + .unsigned => Condition.cc, + }, + else => unreachable, + }; + const cond_choose_rhs = cond_choose_lhs.negate(); + + if (dest_reg != lhs_reg) { + _ = try self.addInst(.{ + .tag = .mov, + .cond = cond_choose_lhs, + .data = .{ .rr_op = .{ + .rd = dest_reg, + .rn = .r0, + .op = Instruction.Operand.reg(lhs_reg, Instruction.Operand.Shift.none), + } }, + }); + } + if (dest_reg != rhs_reg) { + _ = try self.addInst(.{ + .tag = .mov, + .cond = cond_choose_rhs, + .data = .{ .rr_op = .{ + .rd = dest_reg, + .rn = .r0, + .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none), + } }, + }); + } + + return MCValue{ .register = dest_reg }; + } else { + return self.fail("TODO ARM min/max on integers > u32/i32", .{}); + } + }, + else => unreachable, + } } -fn airMax(self: *Self, inst: Air.Inst.Index) !void { +fn airMinMax(self: *Self, inst: Air.Inst.Index) !void { + const tag = self.air.instructions.items(.tag)[inst]; 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 max for {}", .{self.target.cpu.arch}); + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + if (bin_op.lhs == bin_op.rhs) break :result lhs; + + break :result try self.minMax(tag, inst, lhs, rhs, lhs_ty, rhs_ty); + }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } From be1cca341629b0783be6754b2d7642afce936acb Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Tue, 22 Mar 2022 21:10:18 +0100 Subject: [PATCH 0868/2031] stage2 ARM: implement comparison of optional pointers --- src/arch/arm/CodeGen.zig | 57 +++++++++++++++++++++----------------- test/behavior/optional.zig | 2 -- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 87f6b9eb10..1e736a6e86 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -3017,36 +3017,41 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { const rhs = try self.resolveInst(bin_op.rhs); const lhs_ty = self.air.typeOf(bin_op.lhs); - switch (lhs_ty.zigTypeTag()) { - .Optional => return self.fail("TODO ARM cmp optionals", .{}), - .Float => return self.fail("TODO ARM cmp floats", .{}), - .Int, .Bool, .Pointer, .ErrorSet, .Enum => { - var int_buffer: Type.Payload.Bits = undefined; - const int_ty = switch (lhs_ty.zigTypeTag()) { - .Enum => lhs_ty.intTagType(&int_buffer), - .Int => lhs_ty, - .Bool => Type.initTag(.u1), - .Pointer => Type.usize, - .ErrorSet => Type.initTag(.u16), - else => unreachable, - }; - - const int_info = int_ty.intInfo(self.target.*); - if (int_info.bits <= 32) { - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; - - _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty); - - break :result switch (int_info.signedness) { - .signed => MCValue{ .compare_flags_signed = op }, - .unsigned => MCValue{ .compare_flags_unsigned = op }, - }; + var int_buffer: Type.Payload.Bits = undefined; + const int_ty = switch (lhs_ty.zigTypeTag()) { + .Optional => blk: { + var opt_buffer: Type.Payload.ElemType = undefined; + const payload_ty = lhs_ty.optionalChild(&opt_buffer); + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + break :blk Type.initTag(.u1); + } else if (lhs_ty.isPtrLikeOptional()) { + break :blk Type.usize; } else { - return self.fail("TODO ARM cmp for ints > 32 bits", .{}); + return self.fail("TODO ARM cmp non-pointer optionals", .{}); } }, + .Float => return self.fail("TODO ARM cmp floats", .{}), + .Enum => lhs_ty.intTagType(&int_buffer), + .Int => lhs_ty, + .Bool => Type.initTag(.u1), + .Pointer => Type.usize, + .ErrorSet => Type.initTag(.u16), else => unreachable, + }; + + const int_info = int_ty.intInfo(self.target.*); + if (int_info.bits <= 32) { + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = inst; + + _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty); + + break :result switch (int_info.signedness) { + .signed => MCValue{ .compare_flags_signed = op }, + .unsigned => MCValue{ .compare_flags_unsigned = op }, + }; + } else { + return self.fail("TODO ARM cmp for ints > 32 bits", .{}); } }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index d9c16da066..10d829a2c0 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -33,8 +33,6 @@ test "optional pointer to size zero struct" { } test "equality compare optional pointers" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - try testNullPtrsEql(); comptime try testNullPtrsEql(); } From 98b932cfabb6d33f184f1cb9c3d4a39c41c5c4f7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 22 Mar 2022 20:17:43 -0700 Subject: [PATCH 0869/2031] fix merge conflicts --- src/arch/arm/CodeGen.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 1e736a6e86..5bbacb8999 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1153,7 +1153,7 @@ fn minMax( .Float => return self.fail("TODO ARM min/max on floats", .{}), .Vector => return self.fail("TODO ARM min/max on vectors", .{}), .Int => { - assert(lhs_ty.eql(rhs_ty)); + assert(lhs_ty.eql(rhs_ty, self.target.*)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 32) { const lhs_is_register = lhs == .register; From eb5e0cba21fbd7d617104518aebb4efb72f24965 Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Tue, 22 Mar 2022 20:54:26 +0800 Subject: [PATCH 0870/2031] Fix ucontext_t --- lib/std/os/linux/arm-eabi.zig | 2 +- lib/std/os/linux/arm64.zig | 2 +- lib/std/os/linux/i386.zig | 2 +- lib/std/os/linux/powerpc.zig | 2 +- lib/std/os/linux/powerpc64.zig | 2 +- lib/std/os/linux/sparc64.zig | 2 +- lib/std/os/linux/x86_64.zig | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/std/os/linux/arm-eabi.zig b/lib/std/os/linux/arm-eabi.zig index 287c61c49d..613957fa25 100644 --- a/lib/std/os/linux/arm-eabi.zig +++ b/lib/std/os/linux/arm-eabi.zig @@ -744,7 +744,7 @@ pub const mcontext_t = extern struct { pub const ucontext_t = extern struct { flags: usize, - link: *ucontext_t, + link: ?*ucontext_t, stack: stack_t, mcontext: mcontext_t, sigmask: sigset_t, diff --git a/lib/std/os/linux/arm64.zig b/lib/std/os/linux/arm64.zig index 9cb66c3fcf..92edc8a5e0 100644 --- a/lib/std/os/linux/arm64.zig +++ b/lib/std/os/linux/arm64.zig @@ -586,7 +586,7 @@ pub const mcontext_t = extern struct { pub const ucontext_t = extern struct { flags: usize, - link: *ucontext_t, + link: ?*ucontext_t, stack: stack_t, sigmask: sigset_t, mcontext: mcontext_t, diff --git a/lib/std/os/linux/i386.zig b/lib/std/os/linux/i386.zig index f5768aed71..e683501ecb 100644 --- a/lib/std/os/linux/i386.zig +++ b/lib/std/os/linux/i386.zig @@ -757,7 +757,7 @@ pub const REG = struct { pub const ucontext_t = extern struct { flags: usize, - link: *ucontext_t, + link: ?*ucontext_t, stack: stack_t, mcontext: mcontext_t, sigmask: sigset_t, diff --git a/lib/std/os/linux/powerpc.zig b/lib/std/os/linux/powerpc.zig index d1ef2b4a21..e45be74f77 100644 --- a/lib/std/os/linux/powerpc.zig +++ b/lib/std/os/linux/powerpc.zig @@ -740,7 +740,7 @@ pub const mcontext_t = extern struct { pub const ucontext_t = extern struct { flags: u32, - link: *ucontext_t, + link: ?*ucontext_t, stack: stack_t, pad: [7]i32, regs: *mcontext_t, diff --git a/lib/std/os/linux/powerpc64.zig b/lib/std/os/linux/powerpc64.zig index f1758c2b02..19d5f04bd3 100644 --- a/lib/std/os/linux/powerpc64.zig +++ b/lib/std/os/linux/powerpc64.zig @@ -727,7 +727,7 @@ pub const mcontext_t = extern struct { pub const ucontext_t = extern struct { flags: u32, - link: *ucontext_t, + link: ?*ucontext_t, stack: stack_t, sigmask: sigset_t, mcontext: mcontext_t, diff --git a/lib/std/os/linux/sparc64.zig b/lib/std/os/linux/sparc64.zig index 231cd0a283..2f9efecae4 100644 --- a/lib/std/os/linux/sparc64.zig +++ b/lib/std/os/linux/sparc64.zig @@ -821,7 +821,7 @@ pub const mcontext_t = extern struct { }; pub const ucontext_t = extern struct { - link: *ucontext_t, + link: ?*ucontext_t, flags: u64, sigmask: u64, mcontext: mcontext_t, diff --git a/lib/std/os/linux/x86_64.zig b/lib/std/os/linux/x86_64.zig index 0aaa28262c..8913d10585 100644 --- a/lib/std/os/linux/x86_64.zig +++ b/lib/std/os/linux/x86_64.zig @@ -736,7 +736,7 @@ pub const mcontext_t = extern struct { pub const ucontext_t = extern struct { flags: usize, - link: *ucontext_t, + link: ?*ucontext_t, stack: stack_t, mcontext: mcontext_t, sigmask: sigset_t, From 8f9c3fd3dfb93a8621a55edef8ebfba87c9a6975 Mon Sep 17 00:00:00 2001 From: Daniele Cocca Date: Wed, 23 Mar 2022 03:24:36 +0000 Subject: [PATCH 0871/2031] CBE: enable more passing tests (#11258) --- test/behavior/error.zig | 4 ---- test/behavior/eval.zig | 4 ---- test/behavior/pointers.zig | 3 --- 3 files changed, 11 deletions(-) diff --git a/test/behavior/error.zig b/test/behavior/error.zig index b5251db39f..f24804c581 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -168,7 +168,6 @@ fn testErrorUnionType() !void { } test "error set type" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO try testErrorSetType(); @@ -194,7 +193,6 @@ fn testErrorSetType() !void { } test "explicit error set cast" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO try testExplicitErrorSetCast(Set1.A); @@ -213,7 +211,6 @@ fn testExplicitErrorSetCast(set1: Set1) !void { } test "comptime test error for empty error set" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -294,7 +291,6 @@ fn foo3(b: usize) Error!usize { test "error: Infer error set from literals" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO _ = nullLiteral("n") catch |err| handleErrors(err); diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index cce78b1944..03a7a511f0 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -440,7 +440,6 @@ fn copyWithPartialInline(s: []u32, b: []u8) void { } test "binary math operator in partially inlined function" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -462,7 +461,6 @@ test "comptime shl" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var a: u128 = 3; var b: u7 = 63; @@ -527,7 +525,6 @@ test "runtime 128 bit integer division" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var a: u128 = 152313999999999991610955792383; var b: u128 = 10000000000000000000; @@ -598,7 +595,6 @@ var simple_struct = SimpleStruct{ .field = 1234 }; const bound_fn = simple_struct.method; test "ptr to local array argument at comptime" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index bd39452273..8d98e6fc64 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -435,7 +435,6 @@ test "indexing array with sentinel returns correct type" { } test "element pointer to slice" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -458,7 +457,6 @@ test "element pointer to slice" { } test "element pointer arithmetic to slice" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -484,7 +482,6 @@ test "element pointer arithmetic to slice" { } test "array slicing to slice" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From 47f1a43bb73607b7bdfcb50de2c4ac8d2bc796af Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 22 Mar 2022 21:27:10 +0100 Subject: [PATCH 0872/2031] dwarf: lower enums --- src/codegen.zig | 1 - src/link/Dwarf.zig | 69 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index 20599f3eb6..cc7014a136 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -479,7 +479,6 @@ pub fn generateSymbol( return Result{ .appended = {} }; }, .Enum => { - // TODO populate .debug_info for the enum var int_buffer: Value.Payload.U64 = undefined; const int_val = typed_value.enumToInt(&int_buffer); diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 8f0eb96132..7643fe55c4 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -81,8 +81,10 @@ pub const abbrev_base_type = 4; pub const abbrev_ptr_type = 5; pub const abbrev_struct_type = 6; pub const abbrev_struct_member = 7; -pub const abbrev_pad1 = 8; -pub const abbrev_parameter = 9; +pub const abbrev_enum_type = 8; +pub const abbrev_enum_variant = 9; +pub const abbrev_pad1 = 10; +pub const abbrev_parameter = 11; /// The reloc offset for the virtual address of a function in its Line Number Program. /// Size is a virtual address integer. @@ -879,6 +881,11 @@ fn addDbgInfoType( } }, .Struct => blk: { + if (ty.tag() == .tuple) { + log.debug("TODO implement .debug_info for type '{}'", .{ty.fmtDebug()}); + try dbg_info_buffer.append(abbrev_pad1); + break :blk; + } // try dbg_info_buffer.ensureUnusedCapacity(23); // DW.AT.structure_type try dbg_info_buffer.append(abbrev_struct_type); @@ -918,6 +925,46 @@ fn addDbgInfoType( // DW.AT.structure_type delimit children try dbg_info_buffer.append(0); }, + .Enum => { + // DW.AT.enumeration_type + try dbg_info_buffer.append(abbrev_enum_type); + // DW.AT.byte_size, DW.FORM.sdata + const abi_size = ty.abiSize(target); + try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); + // DW.AT.name, DW.FORM.string + const enum_name = try ty.nameAllocArena(arena, target); + try dbg_info_buffer.ensureUnusedCapacity(enum_name.len + 1); + dbg_info_buffer.appendSliceAssumeCapacity(enum_name); + dbg_info_buffer.appendAssumeCapacity(0); + + const fields = ty.enumFields(); + const values: ?Module.EnumFull.ValueMap = switch (ty.tag()) { + .enum_full, .enum_nonexhaustive => ty.cast(Type.Payload.EnumFull).?.data.values, + .enum_simple => null, + .enum_numbered => ty.castTag(.enum_numbered).?.data.values, + else => unreachable, + }; + const target_endian = self.target.cpu.arch.endian(); + for (fields.keys()) |field_name, field_i| { + // DW.AT.enumerator + try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2 + @sizeOf(u64)); + dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity(field_name); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.const_value, DW.FORM.data8 + const value: u64 = if (values) |vals| value: { + if (vals.count() == 0) break :value @intCast(u64, field_i); // auto-numbered + const value = vals.keys()[field_i]; + var int_buffer: Value.Payload.U64 = undefined; + break :value value.enumToInt(ty, &int_buffer).toUnsignedInt(target); + } else @intCast(u64, field_i); + mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), value, target_endian); + } + + // DW.AT.enumeration_type delimit children + try dbg_info_buffer.append(0); + }, else => { log.debug("TODO implement .debug_info for type '{}'", .{ty.fmtDebug()}); try dbg_info_buffer.append(abbrev_pad1); @@ -1006,6 +1053,24 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.sdata, 0, 0, // table sentinel + abbrev_enum_type, + DW.TAG.enumeration_type, + DW.CHILDREN.yes, // header + DW.AT.byte_size, + DW.FORM.sdata, + DW.AT.name, + DW.FORM.string, + 0, + 0, // table sentinel + abbrev_enum_variant, + DW.TAG.enumerator, + DW.CHILDREN.no, // header + DW.AT.name, + DW.FORM.string, + DW.AT.const_value, + DW.FORM.data8, + 0, + 0, // table sentinel abbrev_pad1, DW.TAG.unspecified_type, DW.CHILDREN.no, // header From e81ebc24e9abf96a6ffbb42cd00587e32e80f23c Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 22 Mar 2022 14:08:11 -0700 Subject: [PATCH 0873/2031] stage2: runtime safety checks for slicing --- src/Sema.zig | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index e6447ea2ef..96fabc197f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -19863,6 +19863,32 @@ fn analyzeSlice( }); try sema.requireRuntimeBlock(block, src); + if (block.wantSafety()) { + // requirement: end <= len + const opt_len_inst = if (array_ty.zigTypeTag() == .Array) + try sema.addIntUnsigned(Type.usize, array_ty.arrayLenIncludingSentinel()) + else if (slice_ty.isSlice()) blk: { + if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| { + // we don't need to add one for sentinels because the + // underlying value data includes the sentinel + break :blk try sema.addIntUnsigned(Type.usize, slice_val.sliceLen()); + } + + const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice); + if (slice_ty.sentinel() == null) break :blk slice_len_inst; + + // we have to add one because slice lengths don't include the sentinel + break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src); + } else null; + if (opt_len_inst) |len_inst| { + const end_is_in_bounds = try block.addBinOp(.cmp_lte, end, len_inst); + try sema.addSafetyCheck(block, end_is_in_bounds, .index_out_of_bounds); + } + + // requirement: start <= end + const start_is_in_bounds = try block.addBinOp(.cmp_lte, start, end); + try sema.addSafetyCheck(block, start_is_in_bounds, .index_out_of_bounds); + } return block.addInst(.{ .tag = .slice, .data = .{ .ty_pl = .{ From 8326ab7adb92aff11d19b21a6d42869a361b462b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 22 Mar 2022 20:44:32 -0700 Subject: [PATCH 0874/2031] Sema: fix merge conflicts with previous commit The previous commit and e8813b296bc55a13b534bd9b2a03e1f6af366915 had merge conflicts which didn't get caught by git. --- src/Sema.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 96fabc197f..dd9f2d74d8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -19871,7 +19871,7 @@ fn analyzeSlice( if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| { // we don't need to add one for sentinels because the // underlying value data includes the sentinel - break :blk try sema.addIntUnsigned(Type.usize, slice_val.sliceLen()); + break :blk try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(target)); } const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice); From 1cf1346323e05b1d481fd2fe0b20a79ed94d9360 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 22 Mar 2022 22:00:03 -0700 Subject: [PATCH 0875/2031] Sema: rename isArrayLike to isArrayOrVector --- src/Sema.zig | 4 ++-- src/type.zig | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index dd9f2d74d8..ab73c55369 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -18780,7 +18780,7 @@ fn beginComptimePtrLoad( // If we're loading an elem_ptr that was derived from a different type // than the true type of the underlying decl, we cannot deref directly - const ty_matches = if (deref.pointee != null and deref.pointee.?.ty.isArrayLike()) x: { + const ty_matches = if (deref.pointee != null and deref.pointee.?.ty.isArrayOrVector()) x: { const deref_elem_ty = deref.pointee.?.ty.childType(); break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok; @@ -18802,7 +18802,7 @@ fn beginComptimePtrLoad( if (maybe_array_ty) |load_ty| { // It's possible that we're loading a [N]T, in which case we'd like to slice // the pointee array directly from our parent array. - if (load_ty.isArrayLike() and load_ty.childType().eql(elem_ty, target)) { + if (load_ty.isArrayOrVector() and load_ty.childType().eql(elem_ty, target)) { const N = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel()); deref.pointee = if (elem_ptr.index + N <= check_len) TypedValue{ .ty = try Type.array(sema.arena, N, null, elem_ty, target), diff --git a/src/type.zig b/src/type.zig index 8668b36dca..d5b8e6f5b3 100644 --- a/src/type.zig +++ b/src/type.zig @@ -4734,7 +4734,7 @@ pub const Type = extern union { }; } - pub fn isArrayLike(ty: Type) bool { + pub fn isArrayOrVector(ty: Type) bool { return switch (ty.zigTypeTag()) { .Array, .Vector => true, else => false, From 2e0de0b4e20794c07fad76c657ad2c33f1928562 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 24 Mar 2022 07:24:25 +1300 Subject: [PATCH 0876/2031] math/big: correct fix for divmod (#11271) Fixes #11166. --- lib/std/math/big/int.zig | 8 +------- lib/std/math/big/int_test.zig | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 181615ec0c..0303aa12f7 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -1576,10 +1576,6 @@ pub const Mutable = struct { // for i from n down to t + 1, do var i = n; while (i >= t + 1) : (i -= 1) { - if (x.eqZero()) { - break; - } - const k = i - t - 1; // 3.1. // if x_i == y_t: @@ -1630,7 +1626,6 @@ pub const Mutable = struct { // Note, we multiply by a single limb here. // The shift doesn't need to be performed if we add the result of the first multiplication // to x[i - t - 1]. - // mem.set(Limb, x.limbs, 0); const underflow = llmulLimb(.sub, x.limbs[k..x.len], y.limbs[0..y.len], q.limbs[k]); // 3.4. @@ -1643,10 +1638,9 @@ pub const Mutable = struct { llaccum(.add, x.limbs[k..x.len], y.limbs[0..y.len]); q.limbs[k] -= 1; } - - x.normalize(x.len); } + x.normalize(x.len); q.normalize(q.len); // De-normalize r and y. diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index 73b549d204..790cc10775 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -1369,6 +1369,33 @@ test "big.int divFloor #10932" { try testing.expect((try mod.to(i32)) == 0); } +test "big.int divFloor #11166" { + var a = try Managed.init(testing.allocator); + defer a.deinit(); + + var b = try Managed.init(testing.allocator); + defer b.deinit(); + + var res = try Managed.init(testing.allocator); + defer res.deinit(); + + try a.setString(10, "10000007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000870000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + try b.setString(10, "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + + var mod = try Managed.init(testing.allocator); + defer mod.deinit(); + + try res.divFloor(&mod, a.toConst(), b.toConst()); + + const ress = try res.toString(testing.allocator, 10, .lower); + defer testing.allocator.free(ress); + try testing.expect(std.mem.eql(u8, ress, "1000000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")); + + const mods = try mod.toString(testing.allocator, 10, .lower); + defer testing.allocator.free(mods); + try testing.expect(std.mem.eql(u8, mods, "870000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")); +} + test "big.int gcd #10932" { var a = try Managed.init(testing.allocator); defer a.deinit(); From 2f9264d8dcedba3a75dcf9d6a31b82447dfc57e8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 23 Mar 2022 13:02:42 -0700 Subject: [PATCH 0877/2031] stage2: fix -Domit-stage2 regression This flag is used when building stage1 to omit the stage2 backends from the compiler to save memory on the CI server. It regressed with the merging of e8813b296bc55a13b534bd9b2a03e1f6af366915 because Value functions started calling into Sema functions. The end goal for this build option is to eliminate it. --- src/Compilation.zig | 6 ++++-- src/Sema.zig | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index e8b1ebb145..6487273e1f 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2042,7 +2042,8 @@ pub fn update(comp: *Compilation) !void { comp.c_object_work_queue.writeItemAssumeCapacity(key); } - const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1; + const use_stage1 = build_options.omit_stage2 or + (build_options.is_stage1 and comp.bin_file.options.use_stage1); if (comp.bin_file.options.module) |module| { module.compile_log_text.shrinkAndFree(module.gpa, 0); module.generation += 1; @@ -2198,7 +2199,8 @@ fn flush(comp: *Compilation) !void { try comp.bin_file.flush(comp); // This is needed before reading the error flags. comp.link_error_flags = comp.bin_file.errorFlags(); - const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1; + const use_stage1 = build_options.omit_stage2 or + (build_options.is_stage1 and comp.bin_file.options.use_stage1); if (!use_stage1) { if (comp.bin_file.options.module) |module| { try link.File.C.flushEmitH(module); diff --git a/src/Sema.zig b/src/Sema.zig index ab73c55369..7fdf384904 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -89,6 +89,7 @@ const RangeSet = @import("RangeSet.zig"); const target_util = @import("target.zig"); const Package = @import("Package.zig"); const crash_report = @import("crash_report.zig"); +const build_options = @import("build_options"); pub const InstMap = std.AutoHashMapUnmanaged(Zir.Inst.Index, Air.Inst.Ref); @@ -20808,6 +20809,9 @@ pub fn resolveTypeLayout( src: LazySrcLoc, ty: Type, ) CompileError!void { + if (build_options.omit_stage2) + @panic("sadly stage2 is omitted from this build to save memory on the CI server"); + switch (ty.zigTypeTag()) { .Struct => return sema.resolveStructLayout(block, src, ty), .Union => return sema.resolveUnionLayout(block, src, ty), @@ -20974,6 +20978,8 @@ fn resolveUnionFully( } pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!Type { + if (build_options.omit_stage2) + @panic("sadly stage2 is omitted from this build to save memory on the CI server"); switch (ty.tag()) { .@"struct" => { const struct_obj = ty.castTag(.@"struct").?.data; @@ -22256,6 +22262,8 @@ fn typePtrOrOptionalPtrTy( /// TODO merge these implementations together with the "advanced"/sema_kit pattern seen /// elsewhere in value.zig pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { + if (build_options.omit_stage2) + @panic("sadly stage2 is omitted from this build to save memory on the CI server"); return switch (ty.tag()) { .u1, .u8, From a9a91a5d49070acaa783d9d65f34a652659160c9 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Mon, 21 Mar 2022 12:18:58 -0700 Subject: [PATCH 0878/2031] stage2 CBE: Improve support for unions and error sets This includes various fixes/improvements to the C backend to improve error/union support. It also fixes up our handling of decls, where some decls were not correctly marked alive. --- src/codegen/c.zig | 140 ++++++++++++++++++++++++++++---------- test/behavior/error.zig | 19 ++++-- test/behavior/ptrcast.zig | 62 +++++++++++++++-- test/behavior/union.zig | 6 -- 4 files changed, 172 insertions(+), 55 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index c62abdb3f6..16558c6e04 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -375,8 +375,6 @@ pub const DeclGen = struct { val: Value, decl: *Decl, ) error{ OutOfMemory, AnalysisFail }!void { - decl.markAlive(); - const target = dg.module.getTarget(); if (ty.isSlice()) { @@ -461,12 +459,14 @@ pub const DeclGen = struct { } } - // Renders a "child" pointer (e.g. ElemPtr, FieldPtr) by recursing - // to the root decl/variable that acts as its parent + // Renders a "parent" pointer by recursing to the root decl/variable + // that its contents are defined with respect to. // - // Used for .elem_ptr, .field_ptr, .opt_payload_ptr, .eu_payload_ptr, since - // the Type of their container cannot be retrieved from their own Type - fn renderChildPtr(dg: *DeclGen, writer: anytype, ptr_val: Value) error{ OutOfMemory, AnalysisFail }!Type { + // Used for .elem_ptr, .field_ptr, .opt_payload_ptr, .eu_payload_ptr + fn renderParentPtr(dg: *DeclGen, writer: anytype, ptr_val: Value, ptr_ty: Type) error{ OutOfMemory, AnalysisFail }!void { + try writer.writeByte('('); + try dg.renderTypecast(writer, ptr_ty); + try writer.writeByte(')'); switch (ptr_val.tag()) { .decl_ref_mut, .decl_ref, .variable => { const decl = switch (ptr_val.tag()) { @@ -475,16 +475,12 @@ pub const DeclGen = struct { .variable => ptr_val.castTag(.variable).?.data.owner_decl, else => unreachable, }; - try dg.renderDeclName(writer, decl); - return decl.ty; + try dg.renderDeclValue(writer, ptr_ty, ptr_val, decl); }, .field_ptr => { const field_ptr = ptr_val.castTag(.field_ptr).?.data; + const container_ty = field_ptr.container_ty; const index = field_ptr.field_index; - - try writer.writeAll("&("); - const container_ty = try dg.renderChildPtr(writer, field_ptr.container_ptr); - const field_name = switch (container_ty.zigTypeTag()) { .Struct => container_ty.structFields().keys()[index], .Union => container_ty.unionFields().keys()[index], @@ -495,19 +491,48 @@ pub const DeclGen = struct { .Union => container_ty.unionFields().values()[index].ty, else => unreachable, }; - try writer.print(").{ }", .{fmtIdent(field_name)}); + var container_ptr_ty_pl: Type.Payload.ElemType = .{ + .base = .{ .tag = .c_mut_pointer }, + .data = field_ptr.container_ty, + }; + const container_ptr_ty = Type.initPayload(&container_ptr_ty_pl.base); - return field_ty; + if (field_ty.hasRuntimeBitsIgnoreComptime()) { + try writer.writeAll("&("); + try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty); + if (field_ptr.container_ty.tag() == .union_tagged) { + try writer.print(")->payload.{ }", .{fmtIdent(field_name)}); + } else { + try writer.print(")->{ }", .{fmtIdent(field_name)}); + } + } else { + try dg.renderParentPtr(writer, field_ptr.container_ptr, field_ty); + } }, .elem_ptr => { const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; + var elem_ptr_ty_pl: Type.Payload.ElemType = .{ + .base = .{ .tag = .c_mut_pointer }, + .data = elem_ptr.elem_ty, + }; + const elem_ptr_ty = Type.initPayload(&elem_ptr_ty_pl.base); + try writer.writeAll("&("); - const container_ty = try dg.renderChildPtr(writer, elem_ptr.array_ptr); + try dg.renderParentPtr(writer, elem_ptr.array_ptr, elem_ptr_ty); try writer.print(")[{d}]", .{elem_ptr.index}); - return container_ty.childType(); }, - .opt_payload_ptr => return dg.fail("implement renderChildPtr for optional payload", .{}), - .eu_payload_ptr => return dg.fail("implement renderChildPtr for error union payload", .{}), + .opt_payload_ptr, .eu_payload_ptr => { + const payload_ptr = ptr_val.cast(Value.Payload.PayloadPtr).?.data; + var container_ptr_ty_pl: Type.Payload.ElemType = .{ + .base = .{ .tag = .c_mut_pointer }, + .data = payload_ptr.container_ty, + }; + const container_ptr_ty = Type.initPayload(&container_ptr_ty_pl.base); + + try writer.writeAll("&("); + try dg.renderParentPtr(writer, payload_ptr.container_ptr, container_ptr_ty); + try writer.writeAll(")->payload"); + }, else => unreachable, } } @@ -559,6 +584,13 @@ pub const DeclGen = struct { .Int => switch (val.tag()) { .int_big_positive => try dg.renderBigIntConst(writer, val.castTag(.int_big_positive).?.asBigInt(), ty.isSignedInt()), .int_big_negative => try dg.renderBigIntConst(writer, val.castTag(.int_big_negative).?.asBigInt(), true), + .field_ptr, + .elem_ptr, + .opt_payload_ptr, + .eu_payload_ptr, + .decl_ref_mut, + .decl_ref, + => try dg.renderParentPtr(writer, val, ty), else => { if (ty.isSignedInt()) return writer.print("{d}", .{val.toSignedInt()}); @@ -586,10 +618,6 @@ pub const DeclGen = struct { // to the assigned pointer type. Note this is just a hack to fix warnings from ordered comparisons (<, >, etc) // between pointers and 0, which is an extension to begin with. .zero => try writer.writeByte('0'), - .decl_ref => { - const decl = val.castTag(.decl_ref).?.data; - return dg.renderDeclValue(writer, ty, val, decl); - }, .variable => { const decl = val.castTag(.variable).?.data.owner_decl; return dg.renderDeclValue(writer, ty, val, decl); @@ -606,9 +634,6 @@ pub const DeclGen = struct { try dg.renderValue(writer, Type.usize, slice.len); try writer.writeAll("}"); }, - .field_ptr, .elem_ptr, .opt_payload_ptr, .eu_payload_ptr => { - _ = try dg.renderChildPtr(writer, val); - }, .function => { const func = val.castTag(.function).?.data; try dg.renderDeclName(writer, func.owner_decl); @@ -622,6 +647,13 @@ pub const DeclGen = struct { try dg.renderTypecast(writer, ty); try writer.print(")0x{x}u)", .{val.toUnsignedInt(target)}); }, + .field_ptr, + .elem_ptr, + .opt_payload_ptr, + .eu_payload_ptr, + .decl_ref_mut, + .decl_ref, + => try dg.renderParentPtr(writer, val, ty), else => unreachable, }, .Array => { @@ -1326,7 +1358,7 @@ pub const DeclGen = struct { return w.writeAll(name); }, .Struct => { - const name = dg.getTypedefName(t) orelse if (t.isTuple()) + const name = dg.getTypedefName(t) orelse if (t.isTuple() or t.tag() == .anon_struct) try dg.renderTupleTypedef(t) else try dg.renderStructTypedef(t); @@ -1346,11 +1378,11 @@ pub const DeclGen = struct { try dg.renderType(w, int_tag_ty); }, + .Opaque => return w.writeAll("void"), .Frame, .AnyFrame, .Vector, - .Opaque, => |tag| return dg.fail("TODO: C backend: implement value of type {s}", .{ @tagName(tag), }), @@ -1497,6 +1529,8 @@ pub const DeclGen = struct { } fn renderDeclName(dg: DeclGen, writer: anytype, decl: *Decl) !void { + decl.markAlive(); + if (dg.module.decl_exports.get(decl)) |exports| { return writer.writeAll(exports[0].options.name); } else if (decl.val.tag() == .extern_fn) { @@ -3188,7 +3222,7 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc field_name = fields.keys()[index]; field_val_ty = fields.values()[index].ty; }, - .tuple => { + .tuple, .anon_struct => { const tuple = struct_ty.tupleFields(); if (tuple.values[index].tag() != .unreachable_value) return CValue.none; @@ -3203,9 +3237,17 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc const inst_ty = f.air.typeOfIndex(inst); const local = try f.allocLocal(inst_ty, .Const); - try writer.print(" = &", .{}); - try f.writeCValueDeref(writer, struct_ptr); - try writer.print(".{s}{ };\n", .{ payload, fmtIdent(field_name) }); + if (field_val_ty.hasRuntimeBitsIgnoreComptime()) { + try writer.writeAll(" = &"); + try f.writeCValueDeref(writer, struct_ptr); + try writer.print(".{s}{ };\n", .{ payload, fmtIdent(field_name) }); + } else { + try writer.writeAll(" = ("); + try f.renderTypecast(writer, inst_ty); + try writer.writeByte(')'); + try f.writeCValue(writer, struct_ptr); + try writer.writeAll(";\n"); + } return local; } @@ -3223,7 +3265,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { const field_name = switch (struct_ty.tag()) { .@"struct" => struct_ty.structFields().keys()[extra.field_index], .@"union", .union_tagged => struct_ty.unionFields().keys()[extra.field_index], - .tuple => blk: { + .tuple, .anon_struct => blk: { const tuple = struct_ty.tupleFields(); if (tuple.values[extra.field_index].tag() != .unreachable_value) return CValue.none; @@ -3348,8 +3390,36 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { } fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { - _ = inst; - return f.fail("TODO: C backend: implement airErrUnionPayloadPtrSet", .{}); + const writer = f.object.writer(); + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + const operand = try f.resolveInst(ty_op.operand); + const error_union_ty = f.air.typeOf(ty_op.operand).childType(); + + const error_ty = error_union_ty.errorUnionSet(); + const payload_ty = error_union_ty.errorUnionPayload(); + + // First, set the non-error value. + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + try f.writeCValueDeref(writer, operand); + try writer.writeAll(" = "); + try f.object.dg.renderValue(writer, error_ty, Value.zero); + try writer.writeAll(";\n "); + + return operand; + } + try f.writeCValueDeref(writer, operand); + try writer.writeAll(".error = "); + try f.object.dg.renderValue(writer, error_ty, Value.zero); + try writer.writeAll(";\n"); + + // Then return the payload pointer (only if it is used) + if (f.liveness.isUnused(inst)) return CValue.none; + + const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const); + try writer.writeAll(" = &("); + try f.writeCValueDeref(writer, operand); + try writer.writeAll(").payload;\n"); + return local; } fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { diff --git a/test/behavior/error.zig b/test/behavior/error.zig index f24804c581..fab9ce6b40 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -1,10 +1,20 @@ const builtin = @import("builtin"); const std = @import("std"); const expect = std.testing.expect; -const expectError = std.testing.expectError; const expectEqual = std.testing.expectEqual; const mem = std.mem; +/// A more basic implementation of std.testing.expectError which +/// does not require formatter/printing support +fn expectError(expected_err: anyerror, observed_err_union: anytype) !void { + if (observed_err_union) { + return error.TestExpectedError; + } else |err| if (err == expected_err) { + return; // Success + } + return error.TestExpectedError; +} + test "error values" { const a = @errorToInt(error.err1); const b = @errorToInt(error.err2); @@ -329,7 +339,6 @@ fn intLiteral(str: []const u8) !?i64 { test "nested error union function call in optional unwrap" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -377,7 +386,6 @@ test "nested error union function call in optional unwrap" { } test "return function call to error set from error union function" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -410,7 +418,6 @@ test "optional error set is the same size as error set" { } test "nested catch" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -471,7 +478,6 @@ test "function pointer with return type that is error union with payload which i test "return result loc as peer result loc in inferred error set function" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -503,7 +509,6 @@ test "return result loc as peer result loc in inferred error set function" { } test "error payload type is correctly resolved" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -519,7 +524,7 @@ test "error payload type is correctly resolved" { } }; - try expectEqual(MyIntWrapper{ .x = 42 }, try MyIntWrapper.create()); + try expect(std.meta.eql(MyIntWrapper{ .x = 42 }, try MyIntWrapper.create())); } test "error union comptime caching" { diff --git a/test/behavior/ptrcast.zig b/test/behavior/ptrcast.zig index 1bceb89412..71b2281564 100644 --- a/test/behavior/ptrcast.zig +++ b/test/behavior/ptrcast.zig @@ -39,7 +39,6 @@ fn testReinterpretWithOffsetAndNoWellDefinedLayout() !void { } test "reinterpret bytes inside auto-layout struct as integer with nonzero offset" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -80,9 +79,9 @@ fn testReinterpretBytesAsExternStruct() !void { try expect(val == 5); } -test "reinterpret bytes of an extern struct into another" { +test "reinterpret bytes of an extern struct (with under-aligned fields) into another" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO: Under-aligned fields are not yet supported in the CBE if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO try testReinterpretExternStructAsExternStruct(); @@ -106,12 +105,39 @@ fn testReinterpretExternStructAsExternStruct() !void { try expect(val == 5); } -test "lower reinterpreted comptime field ptr" { +test "reinterpret bytes of an extern struct into another" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + try testReinterpretOverAlignedExternStructAsExternStruct(); + comptime try testReinterpretOverAlignedExternStructAsExternStruct(); +} + +fn testReinterpretOverAlignedExternStructAsExternStruct() !void { + const S1 = extern struct { + a: u32, + b: u32, + c: u8, + }; + comptime var bytes: S1 = .{ .a = 0, .b = 0, .c = 5 }; + + const S2 = extern struct { + a0: u32, + a1: u16, + a2: u16, + c: u8, + }; + var ptr = @ptrCast(*const S2, &bytes); + var val = ptr.c; + try expect(val == 5); +} + +test "lower reinterpreted comptime field ptr (with under-aligned fields)" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO: CBE does not yet support under-aligned fields if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO // Test lowering a field ptr @@ -131,8 +157,31 @@ test "lower reinterpreted comptime field ptr" { try expect(val2.* == 5); } +test "lower reinterpreted comptime field ptr" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + // Test lowering a field ptr + comptime var bytes align(4) = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; + const S = extern struct { + a: u32, + c: u8, + }; + comptime var ptr = @ptrCast(*const S, &bytes); + var val = &ptr.c; + try expect(val.* == 5); + + // Test lowering an elem ptr + comptime var src_value = S{ .a = 15, .c = 5 }; + comptime var ptr2 = @ptrCast(*[@sizeOf(S)]u8, &src_value); + var val2 = &ptr2[4]; + try expect(val2.* == 5); +} + test "reinterpret struct field at comptime" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -164,7 +213,6 @@ test "comptime ptrcast keeps larger alignment" { } test "implicit optional pointer to optional anyopaque pointer" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 642a1f6279..0541817145 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -436,8 +436,6 @@ test "global union with single field is correctly initialized" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - glbl = Foo1{ .f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 }, }; @@ -456,8 +454,6 @@ test "initialize global array of union" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - glbl_array[1] = FooUnion{ .U1 = 2 }; glbl_array[0] = FooUnion{ .U0 = 1 }; try expect(glbl_array[0].U0 == 1); @@ -1033,7 +1029,6 @@ test "switching on non exhaustive union" { } test "containers with single-field enums" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -1113,7 +1108,6 @@ test "union enum type gets a separate scope" { } test "global variable struct contains union initialized to non-most-aligned field" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO From 23bae382c983c7685396e1525216060db06f2e00 Mon Sep 17 00:00:00 2001 From: Locria Cyber <74560659+locriacyber@users.noreply.github.com> Date: Mon, 21 Mar 2022 03:41:42 +0800 Subject: [PATCH 0879/2031] Add sem_open & sem_close --- lib/std/c.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/std/c.zig b/lib/std/c.zig index 396a6f6702..400feda63c 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -239,6 +239,8 @@ pub extern "c" fn pthread_getspecific(key: c.pthread_key_t) ?*anyopaque; pub extern "c" fn pthread_setspecific(key: c.pthread_key_t, value: ?*anyopaque) c_int; pub extern "c" fn sem_init(sem: *c.sem_t, pshared: c_int, value: c_uint) c_int; pub extern "c" fn sem_destroy(sem: *c.sem_t) c_int; +pub extern "c" fn sem_open(name: [*:0]const u8, flag: c_int, mode: c.mode_t, value: c_uint) *c.sem_t; +pub extern "c" fn sem_close(sem: *c.sem_t) c_int; pub extern "c" fn sem_post(sem: *c.sem_t) c_int; pub extern "c" fn sem_wait(sem: *c.sem_t) c_int; pub extern "c" fn sem_trywait(sem: *c.sem_t) c_int; From 6fc07f49a9d723fba6422d83d23384a484b0b0be Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Tue, 22 Mar 2022 19:26:25 -0700 Subject: [PATCH 0880/2031] stage2: concat/mult of slices yields ptr to array --- src/Sema.zig | 4 ++-- test/behavior/slice.zig | 19 ++++++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 7fdf384904..1734185073 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -8643,7 +8643,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }); const val = try Value.Tag.aggregate.create(anon_decl.arena(), buf); const decl = try anon_decl.finish(ty, val, 0); - if (lhs_single_ptr or rhs_single_ptr) { + if (lhs_ty.zigTypeTag() == .Pointer or rhs_ty.zigTypeTag() == .Pointer) { return sema.analyzeDeclRef(decl); } else { return sema.analyzeDeclVal(block, .unneeded, decl); @@ -8818,7 +8818,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai break :blk try Value.Tag.aggregate.create(anon_decl.arena(), buf); }; const decl = try anon_decl.finish(final_ty, val, 0); - if (is_single_ptr) { + if (lhs_ty.zigTypeTag() == .Pointer) { return sema.analyzeDeclRef(decl); } else { return sema.analyzeDeclVal(block, .unneeded, decl); diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 6b72e169fa..432524ebf7 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -583,14 +583,27 @@ test "type coercion of pointer to anon struct literal to pointer to slice" { comptime try S.doTheTest(); } -test "array concat of slices gives slice" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - +test "array concat of slices gives ptr to array" { comptime { var a: []const u8 = "aoeu"; var b: []const u8 = "asdf"; const c = a ++ b; try expect(std.mem.eql(u8, c, "aoeuasdf")); + if (builtin.zig_backend != .stage1) { + // spec change: array concat now returns pointer-to-array for slices + try expect(@TypeOf(c) == *const [8]u8); + } + } +} + +test "array mult of slice gives ptr to array" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; // Stage 1 does not support multiplying slices + + comptime { + var a: []const u8 = "aoeu"; + const c = a ** 2; + try expect(std.mem.eql(u8, c, "aoeuaoeu")); + try expect(@TypeOf(c) == *const [8]u8); } } From d7530c8f7bb14e860898c74aa6a20d86379c8822 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Wed, 23 Mar 2022 08:44:11 -0700 Subject: [PATCH 0881/2031] stage2: make zero-sized array not cause recursive type definition --- src/Sema.zig | 1 + test/behavior/array.zig | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 1734185073..03a8a89054 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -20816,6 +20816,7 @@ pub fn resolveTypeLayout( .Struct => return sema.resolveStructLayout(block, src, ty), .Union => return sema.resolveUnionLayout(block, src, ty), .Array => { + if (ty.arrayLenIncludingSentinel() == 0) return; const elem_ty = ty.childType(); return sema.resolveTypeLayout(block, src, elem_ty); }, diff --git a/test/behavior/array.zig b/test/behavior/array.zig index bbaf3b3c34..7e55bb13c3 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -479,10 +479,7 @@ test "sentinel element count towards the ABI size calculation" { test "zero-sized array with recursive type definition" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const U = struct { From b872539a13ac46abe57a59bafdf5392812468482 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Wed, 23 Mar 2022 08:49:16 -0700 Subject: [PATCH 0882/2031] stage2: enable some passing array & vector tests --- test/behavior/array.zig | 2 -- test/behavior/math.zig | 12 ++++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 7e55bb13c3..d7a87bed9c 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -453,9 +453,7 @@ test "type deduction for array subscript expression" { } test "sentinel element count towards the ABI size calculation" { - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 67f7fd101d..6690fbd193 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1282,7 +1282,11 @@ fn testRound(comptime T: type, x: T) !void { } test "vector integer addition" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -1336,7 +1340,11 @@ fn testNanEqNan(comptime F: type) !void { } test "vector comparison" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { From 49051c065120781b6ff78172c447b6307bd01e39 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 22 Mar 2022 21:20:36 +0100 Subject: [PATCH 0883/2031] wasm: Implement `@errorName` This implements the `error_name` instruction, which is emit for runtime `@errorName` callsites. The implementation works by creating 2 symbols and corresponding atoms. The initial symbol contains a table which each element consisting of a slice where the ptr field points towards the error name, and the len field contains the error name length without the sentinel. The secondary symbol contains a list of all error names from the global error set. During the error_name instruction, we first get a pointer to the first symbol. Then based on the operand we perform pointer arithmetic, to get the correct index into this table. e.g. error index 2 = ptr + (2 * ptr size). The result of this will be stored in a local and then returned as instruction result. During `flush()` we populate the error names table by looping over the global error set and creating a relocation for each error name. This relocation is appended to the table symbol. Then finally, this name is written to the names list itself. Finally, both symbols' atom are allocated within the rest of the binary. When no error name is referenced, the `error_name_symbol` is never set, and therefore no error name table will be emit into the final binary. --- src/arch/wasm/CodeGen.zig | 45 +++++++++++++- src/link/Wasm.zig | 127 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+), 1 deletion(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index eb4004fa2c..136ef5cc45 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1403,6 +1403,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .wrap_errunion_payload => self.airWrapErrUnionPayload(inst), .wrap_errunion_err => self.airWrapErrUnionErr(inst), .errunion_payload_ptr_set => self.airErrUnionPayloadPtrSet(inst), + .error_name => self.airErrorName(inst), .wasm_memory_size => self.airWasmMemorySize(inst), .wasm_memory_grow => self.airWasmMemoryGrow(inst), @@ -1458,7 +1459,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .atomic_store_seq_cst, .atomic_rmw, .tag_name, - .error_name, .mul_add, // For these 4, probably best to wait until https://github.com/ziglang/zig/issues/10248 @@ -3618,3 +3618,46 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addLabel(.local_set, result.local); return result; } + +fn airErrorName(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + + // First retrieve the symbol index to the error name table + // that will be used to emit a relocation for the pointer + // to the error name table. + // + // Each entry to this table is a slice (ptr+len). + // The operand in this instruction represents the index within this table. + // This means to get the final name, we emit the base pointer and then perform + // pointer arithmetic to find the pointer to this slice and return that. + // + // As the names are global and the slice elements are constant, we do not have + // to make a copy of the ptr+value but can point towards them directly. + const error_table_symbol = try self.bin_file.getErrorTableSymbol(); + const name_ty = Type.initTag(.const_slice_u8_sentinel_0); + const abi_size = name_ty.abiSize(self.target); + + const error_name_value: WValue = .{ .memory = error_table_symbol }; // emitting this will create a relocation + try self.emitWValue(error_name_value); + try self.emitWValue(operand); + switch (self.arch()) { + .wasm32 => { + try self.addImm32(@bitCast(i32, @intCast(u32, abi_size))); + try self.addTag(.i32_mul); + try self.addTag(.i32_add); + }, + .wasm64 => { + try self.addImm64(abi_size); + try self.addTag(.i64_mul); + try self.addTag(.i64_add); + }, + else => unreachable, + } + + const result_ptr = try self.allocLocal(Type.usize); + try self.addLabel(.local_set, result_ptr.local); + return result_ptr; +} diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 1288b27a81..2eb102c752 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -123,6 +123,13 @@ symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, *Atom) = .{}, /// Note: The value represents the offset into the string table, rather than the actual string. export_names: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{}, +/// Represents the symbol index of the error name table +/// When this is `null`, no code references an error using runtime `@errorName`. +/// During initializion, a symbol with corresponding atom will be created that is +/// used to perform relocations to the pointer of this table. +/// The actual table is populated during `flush`. +error_table_symbol: ?u32 = null, + pub const Segment = struct { alignment: u32, size: u32, @@ -1322,6 +1329,123 @@ pub fn getMatchingSegment(self: *Wasm, object_index: u16, relocatable_index: u32 } } +/// Returns the symbol index of the error name table. +/// +/// When the symbol does not yet exist, it will create a new one instead. +pub fn getErrorTableSymbol(self: *Wasm) !u32 { + if (self.error_table_symbol) |symbol| { + return symbol; + } + + // no error was referenced yet, so create a new symbol and atom for it + // and then return said symbol's index. The final table will be populated + // during `flush` when we know all possible error names. + + // As sym_index '0' is reserved, we use it for our stack pointer symbol + const symbol_index = self.symbols_free_list.popOrNull() orelse blk: { + const index = @intCast(u32, self.symbols.items.len); + _ = try self.symbols.addOne(self.base.allocator); + break :blk index; + }; + + const sym_name = try self.string_table.put(self.base.allocator, "__zig_err_name_table"); + const symbol = &self.symbols.items[symbol_index]; + symbol.* = .{ + .name = sym_name, + .tag = .data, + .flags = 0, + .index = 0, + }; + symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); + + const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); + + const atom = try self.base.allocator.create(Atom); + atom.* = Atom.empty; + atom.sym_index = symbol_index; + atom.alignment = slice_ty.abiAlignment(self.base.options.target); + try self.managed_atoms.append(self.base.allocator, atom); + const loc = atom.symbolLoc(); + try self.resolved_symbols.put(self.base.allocator, loc, {}); + try self.symbol_atom.put(self.base.allocator, loc, atom); + + log.debug("Error name table was created with symbol index: ({d})", .{symbol_index}); + self.error_table_symbol = symbol_index; + return symbol_index; +} + +/// Populates the error name table, when `error_table_symbol` is not null. +/// +/// This creates a table that consists of pointers and length to each error name. +/// The table is what is being pointed to within the runtime bodies that are generated. +fn populateErrorNameTable(self: *Wasm) !void { + const symbol_index = self.error_table_symbol orelse return; + const atom: *Atom = self.symbol_atom.get(.{ .file = null, .index = symbol_index }).?; + // Rather than creating a symbol for each individual error name, + // we create a symbol for the entire region of error names. We then calculate + // the pointers into the list using addends which are appended to the relocation. + const names_atom = try self.base.allocator.create(Atom); + names_atom.* = Atom.empty; + try self.managed_atoms.append(self.base.allocator, names_atom); + const names_symbol_index = self.symbols_free_list.popOrNull() orelse blk: { + const index = @intCast(u32, self.symbols.items.len); + _ = try self.symbols.addOne(self.base.allocator); + break :blk index; + }; + names_atom.sym_index = names_symbol_index; + names_atom.alignment = 1; + const sym_name = try self.string_table.put(self.base.allocator, "__zig_err_names"); + const names_symbol = &self.symbols.items[names_symbol_index]; + names_symbol.* = .{ + .name = sym_name, + .tag = .data, + .flags = 0, + .index = 0, + }; + names_symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); + + log.debug("Populating error names", .{}); + + // Addend for each relocation to the table + var addend: u32 = 0; + const module = self.base.options.module.?; + for (module.error_name_list.items) |error_name| { + const len = @intCast(u32, error_name.len + 1); // names are 0-termianted + + const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); + const offset = @intCast(u32, atom.code.items.len); + // first we create the data for the slice of the name + try atom.code.appendNTimes(self.base.allocator, 0, 4); // ptr to name, will be relocated + try atom.code.writer(self.base.allocator).writeIntLittle(u32, len - 1); + // create relocation to the error name + try atom.relocs.append(self.base.allocator, .{ + .index = names_symbol_index, + .relocation_type = .R_WASM_MEMORY_ADDR_I32, + .offset = offset, + .addend = addend, + }); + atom.size += @intCast(u32, slice_ty.abiSize(self.base.options.target)); + addend += len; + + // as we updated the error name table, we now store the actual name within the names atom + try names_atom.code.ensureUnusedCapacity(self.base.allocator, len); + names_atom.code.appendSliceAssumeCapacity(error_name); + names_atom.code.appendAssumeCapacity(0); + + log.debug("Populated error name: '{s}'", .{error_name}); + } + names_atom.size = addend; + + const name_loc = names_atom.symbolLoc(); + try self.resolved_symbols.put(self.base.allocator, name_loc, {}); + try self.symbol_atom.put(self.base.allocator, name_loc, names_atom); + + // link the atoms with the rest of the binary so they can be allocated + // and relocations will be performed. + try self.parseAtom(atom, .data); + try self.parseAtom(names_atom, .data); +} + fn resetState(self: *Wasm) void { for (self.segment_info.items) |*segment_info| { self.base.allocator.free(segment_info.name); @@ -1373,6 +1497,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { } } + // ensure the error names table is populated when an error name is referenced + try self.populateErrorNameTable(); + // The amount of sections that will be written var section_count: u32 = 0; // Index of the code section. Used to tell relocation table where the section lives. From a9a629f89a1d72130103bd59800e1257af84c97c Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 22 Mar 2022 21:56:38 +0100 Subject: [PATCH 0884/2031] wasm: Fix switching on errors Error sets contain the entire global error set. Users are often switching on specific errors only present within that operand. This means that cases are almost always sparse and not contiguous. For this reason, we will instead emit the default case for error values not present in that specific operand error set. This is fine as those cases will never be hit, as prevented by the type system. By still allowing jump tables for those cases, rather than if-else chains, we save runtime cost as well as binary size. --- src/arch/wasm/CodeGen.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 136ef5cc45..0300c0b612 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2452,7 +2452,11 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (case_value.integer == value) break :blk @intCast(u32, idx); } } - break :blk if (has_else_body) case_i else unreachable; + // error sets are almost always sparse so we use the default case + // for errors that are not present in any branch. This is fine as this default + // case will never be hit for those cases but we do save runtime cost and size + // by using a jump table for this instead of if-else chains. + break :blk if (has_else_body or target_ty.zigTypeTag() == .ErrorSet) case_i else unreachable; }; self.mir_extra.appendAssumeCapacity(idx); } else if (has_else_body) { From 685f05b562b1e5d001ae2da23485c03b704d19f6 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 23 Mar 2022 18:33:48 +0100 Subject: [PATCH 0885/2031] wasm: Implement opt_payload_ptr Implements lowering constants for pointers of value 'opt_payload_ptr'. The offset is calculated by determining the abi size of the full type and then substracting the payload size. --- src/arch/wasm/CodeGen.zig | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 0300c0b612..0415d8c90a 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1886,6 +1886,23 @@ fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WV .offset = @intCast(u32, offset), } }; }, + .opt_payload_ptr => { + const payload_ptr = ptr_val.castTag(.opt_payload_ptr).?.data; + const parent_ptr = try self.lowerParentPtr(payload_ptr.container_ptr, payload_ptr.container_ty); + var buf: Type.Payload.ElemType = undefined; + const payload_ty = payload_ptr.container_ty.optionalChild(&buf); + if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.isPtrLikeOptional()) { + return parent_ptr; + } + + const abi_size = payload_ptr.container_ty.abiSize(self.target); + const offset = abi_size - payload_ty.abiSize(self.target); + + return WValue{ .memory_offset = .{ + .pointer = parent_ptr.memory, + .offset = @intCast(u32, offset), + } }; + }, else => |tag| return self.fail("TODO: Implement lowerParentPtr for tag: {}", .{tag}), } } @@ -1948,7 +1965,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { else => unreachable, }, .Pointer => switch (val.tag()) { - .field_ptr, .elem_ptr => { + .field_ptr, .elem_ptr, .opt_payload_ptr => { return self.lowerParentPtr(val, ty.childType()); }, .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(target)) }, From 60676a0ba50ece8363883bdbaa803f31d8284c8c Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 23 Mar 2022 20:42:11 +0100 Subject: [PATCH 0886/2031] wasm: Implement more instructions Implements the following instructions: - int_to_float - ptr_slice_len_ptr - ptr_slice_ptr_ptr - unwrap_errunion_payload_ptr - unwrap_errunion_err_ptr --- src/arch/wasm/CodeGen.zig | 58 ++++++++++++++++++++++++++++++--------- src/arch/wasm/Emit.zig | 8 ++++++ src/arch/wasm/Mir.zig | 16 +++++++++++ 3 files changed, 69 insertions(+), 13 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 0415d8c90a..eb8d72a994 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1329,6 +1329,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .fptrunc => self.airFptrunc(inst), .fpext => self.airFpext(inst), .float_to_int => self.airFloatToInt(inst), + .int_to_float => self.airIntToFloat(inst), .get_union_tag => self.airGetUnionTag(inst), // TODO @@ -1382,6 +1383,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .slice_elem_val => self.airSliceElemVal(inst), .slice_elem_ptr => self.airSliceElemPtr(inst), .slice_ptr => self.airSlicePtr(inst), + .ptr_slice_len_ptr => self.airPtrSliceFieldPtr(inst, self.ptrSize()), + .ptr_slice_ptr_ptr => self.airPtrSliceFieldPtr(inst, 0), .store => self.airStore(inst), .set_union_tag => self.airSetUnionTag(inst), @@ -1398,8 +1401,10 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .unreach => self.airUnreachable(inst), .wrap_optional => self.airWrapOptional(inst), - .unwrap_errunion_payload => self.airUnwrapErrUnionPayload(inst), - .unwrap_errunion_err => self.airUnwrapErrUnionError(inst), + .unwrap_errunion_payload => self.airUnwrapErrUnionPayload(inst, false), + .unwrap_errunion_payload_ptr => self.airUnwrapErrUnionPayload(inst, true), + .unwrap_errunion_err => self.airUnwrapErrUnionError(inst, false), + .unwrap_errunion_err_ptr => self.airUnwrapErrUnionError(inst, true), .wrap_errunion_payload => self.airWrapErrUnionPayload(inst), .wrap_errunion_err => self.airWrapErrUnionErr(inst), .errunion_payload_ptr_set => self.airErrUnionPayloadPtrSet(inst), @@ -1429,8 +1434,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .bit_reverse, .is_err_ptr, .is_non_err_ptr, - .unwrap_errunion_payload_ptr, - .unwrap_errunion_err_ptr, .sqrt, .sin, @@ -1446,9 +1449,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .round, .trunc_float, - .ptr_slice_len_ptr, - .ptr_slice_ptr_ptr, - .int_to_float, .cmpxchg_weak, .cmpxchg_strong, .fence, @@ -2560,30 +2560,32 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W return is_err_tmp; } -fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { +fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!WValue { if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); - const err_ty = self.air.typeOf(ty_op.operand); + const op_ty = self.air.typeOf(ty_op.operand); + const err_ty = if (op_is_ptr) op_ty.childType() else op_ty; const payload_ty = err_ty.errorUnionPayload(); if (!payload_ty.hasRuntimeBits()) return WValue{ .none = {} }; const err_align = err_ty.abiAlignment(self.target); const set_size = err_ty.errorUnionSet().abiSize(self.target); const offset = mem.alignForwardGeneric(u64, set_size, err_align); - if (isByRef(payload_ty, self.target)) { + if (op_is_ptr or isByRef(payload_ty, self.target)) { return self.buildPointerOffset(operand, offset, .new); } return self.load(operand, payload_ty, @intCast(u32, offset)); } -fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue { +fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!WValue { if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); - const err_ty = self.air.typeOf(ty_op.operand); + const op_ty = self.air.typeOf(ty_op.operand); + const err_ty = if (op_is_ptr) op_ty.childType() else op_ty; const payload_ty = err_ty.errorUnionPayload(); - if (!payload_ty.hasRuntimeBits()) { + if (op_is_ptr or !payload_ty.hasRuntimeBits()) { return operand; } @@ -3231,6 +3233,28 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return result; } +fn airIntToFloat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const operand = try self.resolveInst(ty_op.operand); + const dest_ty = self.air.typeOfIndex(inst); + const op_ty = self.air.typeOf(ty_op.operand); + + try self.emitWValue(operand); + const op = buildOpcode(.{ + .op = .convert, + .valtype1 = typeToValtype(dest_ty, self.target), + .valtype2 = typeToValtype(op_ty, self.target), + .signedness = if (op_ty.isSignedInt()) .signed else .unsigned, + }); + try self.addTag(Mir.Inst.Tag.fromOpcode(op)); + + const result = try self.allocLocal(dest_ty); + try self.addLabel(.local_set, result.local); + return result; +} + fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; @@ -3682,3 +3706,11 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addLabel(.local_set, result_ptr.local); return result_ptr; } + +fn airPtrSliceFieldPtr(self: *Self, inst: Air.Inst.Index, offset: u32) InnerError!WValue { + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const slice_ptr = try self.resolveInst(ty_op.operand); + return self.buildPointerOffset(slice_ptr, offset, .new); +} diff --git a/src/arch/wasm/Emit.zig b/src/arch/wasm/Emit.zig index f0f4cfae5d..7487b014be 100644 --- a/src/arch/wasm/Emit.zig +++ b/src/arch/wasm/Emit.zig @@ -203,6 +203,14 @@ pub fn emitMir(emit: *Emit) InnerError!void { .i64_trunc_f32_u => try emit.emitTag(tag), .i64_trunc_f64_s => try emit.emitTag(tag), .i64_trunc_f64_u => try emit.emitTag(tag), + .f32_convert_i32_s => try emit.emitTag(tag), + .f32_convert_i32_u => try emit.emitTag(tag), + .f32_convert_i64_s => try emit.emitTag(tag), + .f32_convert_i64_u => try emit.emitTag(tag), + .f64_convert_i32_s => try emit.emitTag(tag), + .f64_convert_i32_u => try emit.emitTag(tag), + .f64_convert_i64_s => try emit.emitTag(tag), + .f64_convert_i64_u => try emit.emitTag(tag), .i32_rem_s => try emit.emitTag(tag), .i32_rem_u => try emit.emitTag(tag), .i64_rem_s => try emit.emitTag(tag), diff --git a/src/arch/wasm/Mir.zig b/src/arch/wasm/Mir.zig index 09a0c32901..395e9bb17c 100644 --- a/src/arch/wasm/Mir.zig +++ b/src/arch/wasm/Mir.zig @@ -451,8 +451,24 @@ pub const Inst = struct { /// Uses `tag` i64_trunc_f64_u = 0xB1, /// Uses `tag` + f32_convert_i32_s = 0xB2, + /// Uses `tag` + f32_convert_i32_u = 0xB3, + /// Uses `tag` + f32_convert_i64_s = 0xB4, + /// Uses `tag` + f32_convert_i64_u = 0xB5, + /// Uses `tag` f32_demote_f64 = 0xB6, /// Uses `tag` + f64_convert_i32_s = 0xB7, + /// Uses `tag` + f64_convert_i32_u = 0xB8, + /// Uses `tag` + f64_convert_i64_s = 0xB9, + /// Uses `tag` + f64_convert_i64_u = 0xBA, + /// Uses `tag` f64_promote_f32 = 0xBB, /// Uses `tag` i32_reinterpret_f32 = 0xBC, From 5cb16dfa59c6e1de1fbcbfcfdecdca1335690df0 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 23 Mar 2022 20:44:52 +0100 Subject: [PATCH 0887/2031] wasm: Enable all passing tests All tests have been manually verified which are now passing. This means that any remaining TODO is an actual to-be-fixed or to-be-implemented test case. --- test/behavior/bugs/11159.zig | 1 - test/behavior/bugs/5398.zig | 1 - test/behavior/bugs/6456.zig | 1 - test/behavior/cast.zig | 10 ---------- test/behavior/error.zig | 11 ----------- test/behavior/optional.zig | 1 - test/behavior/pointers.zig | 6 ------ test/behavior/struct.zig | 17 ----------------- test/behavior/switch.zig | 3 --- test/behavior/type.zig | 3 --- test/behavior/typename.zig | 6 ------ 11 files changed, 60 deletions(-) diff --git a/test/behavior/bugs/11159.zig b/test/behavior/bugs/11159.zig index fdd3807059..02cccf3e0a 100644 --- a/test/behavior/bugs/11159.zig +++ b/test/behavior/bugs/11159.zig @@ -10,7 +10,6 @@ test { test { if (builtin.zig_backend == .stage1) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { comptime x: i32 = 0, diff --git a/test/behavior/bugs/5398.zig b/test/behavior/bugs/5398.zig index 9e66020adc..caf36390ec 100644 --- a/test/behavior/bugs/5398.zig +++ b/test/behavior/bugs/5398.zig @@ -19,7 +19,6 @@ pub const Renderable = struct { var renderable: Renderable = undefined; test "assignment of field with padding" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; diff --git a/test/behavior/bugs/6456.zig b/test/behavior/bugs/6456.zig index f42d4f8804..a3b4720dbd 100644 --- a/test/behavior/bugs/6456.zig +++ b/test/behavior/bugs/6456.zig @@ -12,7 +12,6 @@ const text = test "issue 6456" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 061a8219b8..003b1b7bec 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -96,7 +96,6 @@ test "comptime_int @intToFloat" { } test "@intToFloat" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -545,7 +544,6 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const window_name = [1][*]const u8{"window name"}; const x: [*]const ?[*]const u8 = &window_name; @@ -607,7 +605,6 @@ test "@floatCast cast down" { } test "peer type resolution: unreachable, error set, unreachable" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -818,7 +815,6 @@ test "peer resolution of string literals" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { const E = enum { a, b, c, d }; @@ -910,7 +906,6 @@ test "peer cast [:x]T to [*:x]T" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -930,7 +925,6 @@ test "peer cast [:x]T to [*:x]T" { test "peer type resolution implicit cast to return type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { @@ -951,7 +945,6 @@ test "peer type resolution implicit cast to return type" { test "peer type resolution implicit cast to variable type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -991,7 +984,6 @@ test "cast between C pointer with different but compatible types" { } test "peer type resolve string lit with sentinel-terminated mutable slice" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO @@ -1011,7 +1003,6 @@ test "peer type resolve array pointers, one of them const" { } test "peer type resolve array pointer and unknown pointer" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const const_array: [4]u8 = undefined; @@ -1084,7 +1075,6 @@ test "compile time int to ptr of function" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO try foobar(FUNCTION_CONSTANT); } diff --git a/test/behavior/error.zig b/test/behavior/error.zig index fab9ce6b40..11146ac9ca 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -178,8 +178,6 @@ fn testErrorUnionType() !void { } test "error set type" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - try testErrorSetType(); comptime try testErrorSetType(); } @@ -221,7 +219,6 @@ fn testExplicitErrorSetCast(set1: Set1) !void { } test "comptime test error for empty error set" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -338,7 +335,6 @@ fn intLiteral(str: []const u8) !?i64 { } test "nested error union function call in optional unwrap" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -386,7 +382,6 @@ test "nested error union function call in optional unwrap" { } test "return function call to error set from error union function" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -418,7 +413,6 @@ test "optional error set is the same size as error set" { } test "nested catch" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -451,7 +445,6 @@ test "function pointer with return type that is error union with payload which i } if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -477,7 +470,6 @@ test "function pointer with return type that is error union with payload which i } test "return result loc as peer result loc in inferred error set function" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -509,7 +501,6 @@ test "return result loc as peer result loc in inferred error set function" { } test "error payload type is correctly resolved" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -540,7 +531,6 @@ test "error union comptime caching" { test "@errorName" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -555,7 +545,6 @@ fn gimmeItBroke() anyerror { test "@errorName sentinel length matches slice length" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index 10d829a2c0..6f5623d827 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -65,7 +65,6 @@ test "optional with void type" { } test "address of unwrap optional" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 8d98e6fc64..297c9be7b8 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -166,7 +166,6 @@ test "implicit casting between C pointer and optional non-C pointer" { } test "implicit cast error unions with non-optional to optional pointer" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -282,7 +281,6 @@ test "null terminated pointer" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -301,7 +299,6 @@ test "allow any sentinel" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -318,7 +315,6 @@ test "pointer sentinel with enums" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { const Number = enum { @@ -340,7 +336,6 @@ test "pointer sentinel with optional element" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -424,7 +419,6 @@ test "@ptrToInt on null optional at comptime" { } test "indexing array with sentinel returns correct type" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 8e0b66d07e..f9a039208f 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -282,7 +282,6 @@ const Val = struct { test "struct point to self" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -300,7 +299,6 @@ test "struct point to self" { test "void struct fields" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -321,7 +319,6 @@ const VoidStructFieldsFoo = struct { test "return empty struct from fn" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO _ = testReturnEmptyStructFromFn(); @@ -333,7 +330,6 @@ fn testReturnEmptyStructFromFn() EmptyStruct2 { test "pass slice of empty struct to fn" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try expect(testPassSliceOfEmptyStructToFn(&[_]EmptyStruct2{EmptyStruct2{}}) == 1); @@ -344,7 +340,6 @@ fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { test "self-referencing struct via array member" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -358,7 +353,6 @@ test "self-referencing struct via array member" { } test "empty struct method call" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const es = EmptyStruct{}; @@ -373,7 +367,6 @@ const EmptyStruct = struct { test "align 1 field before self referential align 8 field as slice return type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const result = alloc(Expr); @@ -633,7 +626,6 @@ fn getC(data: *const BitField1) u2 { test "default struct initialization fields" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { @@ -775,7 +767,6 @@ test "packed struct with u0 field access" { } test "access to global struct fields" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO @@ -903,7 +894,6 @@ test "packed struct field passed to generic function" { test "anonymous struct literal syntax" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { @@ -992,7 +982,6 @@ test "comptime struct field" { } test "tuple element initialized with fn call" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -1030,7 +1019,6 @@ test "struct with union field" { } test "type coercion of anon struct literal to struct" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -1069,7 +1057,6 @@ test "type coercion of anon struct literal to struct" { } test "type coercion of pointer to anon struct literal to pointer to struct" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -1181,7 +1168,6 @@ test "for loop over pointers to struct, getting field from struct pointer" { test "anon init through error unions and optionals" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -1210,7 +1196,6 @@ test "anon init through error unions and optionals" { test "anon init through optional" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -1232,7 +1217,6 @@ test "anon init through optional" { test "anon init through error union" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -1254,7 +1238,6 @@ test "anon init through error union" { test "typed init through error unions and optionals" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index ef9709b19a..db670e34a7 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -434,7 +434,6 @@ test "else prong of switch on error set excludes other cases" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -581,7 +580,6 @@ test "switch prongs with cases with identical payload types" { } test "switch on pointer type" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -630,7 +628,6 @@ test "switch on error set with single else" { } test "switch capture copies its payload" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO diff --git a/test/behavior/type.zig b/test/behavior/type.zig index 04409852c1..e3c896d0f7 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -353,7 +353,6 @@ test "Type.Struct" { } test "Type.Enum" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -465,7 +464,6 @@ test "Type.Union" { } test "Type.Union from Type.Enum" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -496,7 +494,6 @@ test "Type.Union from Type.Enum" { } test "Type.Union from regular enum" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO diff --git a/test/behavior/typename.zig b/test/behavior/typename.zig index 97a6605fc2..b237779886 100644 --- a/test/behavior/typename.zig +++ b/test/behavior/typename.zig @@ -18,7 +18,6 @@ test "anon fn param" { return error.SkipZigTest; } - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -51,7 +50,6 @@ test "anon field init" { return error.SkipZigTest; } - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -78,7 +76,6 @@ test "anon field init" { } test "basic" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -95,7 +92,6 @@ test "top level decl" { return error.SkipZigTest; } - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -147,7 +143,6 @@ test "fn body decl" { return error.SkipZigTest; } - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -182,7 +177,6 @@ const B = struct { }; test "fn param" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO From 1c223819098e5621e2e5ad526879cd33852144e1 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Tue, 22 Mar 2022 23:07:46 -0700 Subject: [PATCH 0888/2031] stage2: Properly "flatten" elem_ptrs before deref Sema.pointerDeref() assumes that elem_ptrs have been "flattened" when they were created, so that you an elem_ptr will never be the array_ptr of another elem_ptr when they share the same type. Value.elemPtr does this already, but a couple of places in Sema were bypassing this logic. --- src/Sema.zig | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 03a8a89054..7b5296dd78 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -17045,17 +17045,9 @@ fn elemVal( const indexable_val = maybe_indexable_val orelse break :rs indexable_src; const index_val = maybe_index_val orelse break :rs elem_index_src; const index = @intCast(usize, index_val.toUnsignedInt(target)); - const elem_ty = indexable_ty.elemType2(); - - var payload: Value.Payload.ElemPtr = .{ .data = .{ - .array_ptr = indexable_val, - .elem_ty = elem_ty, - .index = index, - } }; - const elem_ptr_val = Value.initPayload(&payload.base); - + const elem_ptr_val = try indexable_val.elemPtr(indexable_ty, sema.arena, index, target); if (try sema.pointerDeref(block, indexable_src, elem_ptr_val, indexable_ty)) |elem_val| { - return sema.addConstant(elem_ty, elem_val); + return sema.addConstant(indexable_ty.elemType2(), elem_val); } break :rs indexable_src; }; @@ -17310,12 +17302,7 @@ fn elemValSlice( const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); } - var elem_ptr_pl: Value.Payload.ElemPtr = .{ .data = .{ - .array_ptr = slice_val.slicePtr(), - .elem_ty = elem_ty, - .index = index, - } }; - const elem_ptr_val = Value.initPayload(&elem_ptr_pl.base); + const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index, target); if (try sema.pointerDeref(block, slice_src, elem_ptr_val, slice_ty)) |elem_val| { return sema.addConstant(elem_ty, elem_val); } From 5374e245c50fde8bbb133bbff2db15efa3604721 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Tue, 22 Mar 2022 23:32:57 -0700 Subject: [PATCH 0889/2031] stage2: Remove premature elem_val index check We were enforcing bounds on the index of an elem_ptr in pointerDeref, but we want to support out-of-bounds accesses by reinterpreting memory. This removes that check, so that the deref falls back to bitcasting, as usual. This was masked by another bug that was forcing bitcasts incorrectly, which is why this wasn't noticed earlier. --- src/Sema.zig | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 7b5296dd78..fe10810d57 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -18752,6 +18752,11 @@ fn beginComptimePtrLoad( const elem_ty = elem_ptr.elem_ty; var deref = try beginComptimePtrLoad(sema, block, src, elem_ptr.array_ptr, null); + // This code assumes that elem_ptrs have been "flattened" in order for direct dereference + // to succeed, meaning that elem ptrs of the same elem_ty are coalesced. Here we check that + // our parent is not an elem_ptr with the same elem_ty, since that would be "unflattened" + if (elem_ptr.array_ptr.castTag(.elem_ptr)) |parent_elem_ptr| assert(!(parent_elem_ptr.data.elem_ty.eql(elem_ty, target))); + if (elem_ptr.index != 0) { if (elem_ty.hasWellDefinedLayout()) { if (deref.parent) |*parent| { @@ -18780,13 +18785,6 @@ fn beginComptimePtrLoad( var array_tv = deref.pointee.?; const check_len = array_tv.ty.arrayLenIncludingSentinel(); - if (elem_ptr.index >= check_len) { - // TODO have the deref include the decl so we can say "declared here" - return sema.fail(block, src, "comptime load of index {d} out of bounds of array length {d}", .{ - elem_ptr.index, check_len, - }); - } - if (maybe_array_ty) |load_ty| { // It's possible that we're loading a [N]T, in which case we'd like to slice // the pointee array directly from our parent array. @@ -18800,10 +18798,10 @@ fn beginComptimePtrLoad( } } - deref.pointee = .{ + deref.pointee = if (elem_ptr.index < check_len) TypedValue{ .ty = elem_ty, .val = try array_tv.val.elemValue(sema.arena, elem_ptr.index), - }; + } else null; break :blk deref; }, From 41e300adf17bc4056c574b32de4e07f129f2bd24 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 23 Mar 2022 13:46:06 -0700 Subject: [PATCH 0890/2031] add behavior test to cover bug fix in previous commit --- test/behavior/eval.zig | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 03a7a511f0..c4c6f47981 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -1,5 +1,6 @@ const builtin = @import("builtin"); const std = @import("std"); +const assert = std.debug.assert; const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; @@ -830,3 +831,23 @@ test "const type-annotated local initialized with function call has correct type try expect(@TypeOf(x) == u64); try expect(x == 1234); } + +test "comptime pointer load through elem_ptr" { + const S = struct { + x: usize, + }; + + comptime { + var array: [10]S = undefined; + for (array) |*elem, i| { + elem.* = .{ + .x = i, + }; + } + var ptr = @ptrCast([*]S, &array); + var x = ptr[0].x; + assert(x == 0); + ptr += 1; + assert(ptr[1].x == 2); + } +} From f27d3409bdbe4a2b1b59f0b7336e582488e35986 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 23 Mar 2022 14:06:07 -0700 Subject: [PATCH 0891/2031] behavior tests: disable failing stage1 test My previous commit added a new behavior test that passes for stage2 but I forgot to check whether it passes for stage1. Since it does not, it has to be disabled. Additionally, this commit organizes behavior tests; there is no longer a section of tests only passing for stage1. Instead, tests are disabled on an individual basis. There is an except for the file which has global assembly in it. --- test/behavior.zig | 36 +++--- test/behavior/async_fn.zig | 196 +++++++++++++++++++++++++++++++++ test/behavior/await_struct.zig | 2 + test/behavior/bugs/1120.zig | 2 + test/behavior/bugs/529.zig | 2 + test/behavior/bugs/6781.zig | 2 + test/behavior/bugs/7027.zig | 4 +- test/behavior/bugs/920.zig | 2 + test/behavior/eval.zig | 2 + test/behavior/select.zig | 15 +-- 10 files changed, 237 insertions(+), 26 deletions(-) diff --git a/test/behavior.zig b/test/behavior.zig index 29eadbf14f..60b8d2bae7 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -4,16 +4,17 @@ test { _ = @import("behavior/align.zig"); _ = @import("behavior/alignof.zig"); _ = @import("behavior/array.zig"); + _ = @import("behavior/async_fn.zig"); _ = @import("behavior/atomics.zig"); + _ = @import("behavior/await_struct.zig"); _ = @import("behavior/basic.zig"); _ = @import("behavior/bit_shifting.zig"); _ = @import("behavior/bitcast.zig"); _ = @import("behavior/bitreverse.zig"); - _ = @import("behavior/byteswap.zig"); - _ = @import("behavior/byval_arg_var.zig"); _ = @import("behavior/bool.zig"); _ = @import("behavior/bugs/394.zig"); _ = @import("behavior/bugs/421.zig"); + _ = @import("behavior/bugs/529.zig"); _ = @import("behavior/bugs/624.zig"); _ = @import("behavior/bugs/655.zig"); _ = @import("behavior/bugs/656.zig"); @@ -22,9 +23,11 @@ test { _ = @import("behavior/bugs/718.zig"); _ = @import("behavior/bugs/726.zig"); _ = @import("behavior/bugs/828.zig"); + _ = @import("behavior/bugs/920.zig"); _ = @import("behavior/bugs/1025.zig"); _ = @import("behavior/bugs/1076.zig"); _ = @import("behavior/bugs/1111.zig"); + _ = @import("behavior/bugs/1120.zig"); _ = @import("behavior/bugs/1277.zig"); _ = @import("behavior/bugs/1310.zig"); _ = @import("behavior/bugs/1381.zig"); @@ -61,8 +64,10 @@ test { _ = @import("behavior/bugs/5474.zig"); _ = @import("behavior/bugs/5487.zig"); _ = @import("behavior/bugs/6456.zig"); + _ = @import("behavior/bugs/6781.zig"); _ = @import("behavior/bugs/6850.zig"); _ = @import("behavior/bugs/7003.zig"); + _ = @import("behavior/bugs/7027.zig"); _ = @import("behavior/bugs/7047.zig"); _ = @import("behavior/bugs/7250.zig"); _ = @import("behavior/bugs/9584.zig"); @@ -77,6 +82,8 @@ test { _ = @import("behavior/bugs/11181.zig"); _ = @import("behavior/bugs/11182.zig"); _ = @import("behavior/bugs/11213.zig"); + _ = @import("behavior/byteswap.zig"); + _ = @import("behavior/byval_arg_var.zig"); _ = @import("behavior/call.zig"); _ = @import("behavior/cast.zig"); _ = @import("behavior/cast_int.zig"); @@ -88,9 +95,9 @@ test { _ = @import("behavior/eval.zig"); _ = @import("behavior/field_parent_ptr.zig"); _ = @import("behavior/floatop.zig"); + _ = @import("behavior/fn.zig"); _ = @import("behavior/fn_delegation.zig"); _ = @import("behavior/fn_in_struct_in_comptime.zig"); - _ = @import("behavior/fn.zig"); _ = @import("behavior/for.zig"); _ = @import("behavior/generics.zig"); _ = @import("behavior/hasdecl.zig"); @@ -118,6 +125,7 @@ test { _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); _ = @import("behavior/reflection.zig"); _ = @import("behavior/saturating_arithmetic.zig"); + _ = @import("behavior/select.zig"); _ = @import("behavior/shuffle.zig"); _ = @import("behavior/sizeof_and_typeof.zig"); _ = @import("behavior/slice.zig"); @@ -156,6 +164,13 @@ test { _ = @import("behavior/decltest.zig"); } + if (builtin.os.tag != .wasi) { + if (builtin.zig_backend == .stage1) { + // TODO get these tests passing with stage2 + _ = @import("behavior/asm.zig"); + } + } + if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_x86_64 and builtin.zig_backend != .stage2_aarch64 and @@ -166,19 +181,4 @@ test { _ = @import("behavior/export.zig"); _ = @import("behavior/export_self_referential_type_info.zig"); } - - if (builtin.zig_backend == .stage1) { - // TODO get these tests passing with stage2 - if (builtin.os.tag != .wasi) { - _ = @import("behavior/asm.zig"); - _ = @import("behavior/async_fn.zig"); - } - _ = @import("behavior/await_struct.zig"); - _ = @import("behavior/bugs/529.zig"); - _ = @import("behavior/bugs/920.zig"); - _ = @import("behavior/bugs/1120.zig"); - _ = @import("behavior/bugs/6781.zig"); - _ = @import("behavior/bugs/7027.zig"); - _ = @import("behavior/select.zig"); - } } diff --git a/test/behavior/async_fn.zig b/test/behavior/async_fn.zig index 434ef5b42c..88398cca87 100644 --- a/test/behavior/async_fn.zig +++ b/test/behavior/async_fn.zig @@ -8,6 +8,9 @@ const expectError = std.testing.expectError; var global_x: i32 = 1; test "simple coroutine suspend and resume" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + var frame = async simpleAsyncFn(); try expect(global_x == 2); resume frame; @@ -28,6 +31,9 @@ fn simpleAsyncFn() void { var global_y: i32 = 1; test "pass parameter to coroutine" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + var p = async simpleAsyncFnWithArg(2); try expect(global_y == 3); resume p; @@ -40,6 +46,9 @@ fn simpleAsyncFnWithArg(delta: i32) void { } test "suspend at end of function" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var x: i32 = 1; @@ -59,6 +68,9 @@ test "suspend at end of function" { } test "local variable in async function" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var x: i32 = 0; @@ -88,6 +100,9 @@ test "local variable in async function" { } test "calling an inferred async function" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var x: i32 = 1; var other_frame: *@Frame(other) = undefined; @@ -112,6 +127,9 @@ test "calling an inferred async function" { } test "@frameSize" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + if (builtin.target.cpu.arch == .thumb or builtin.target.cpu.arch == .thumbeb) return error.SkipZigTest; @@ -143,6 +161,9 @@ test "@frameSize" { } test "coroutine suspend, resume" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var frame: anyframe = undefined; @@ -184,6 +205,9 @@ test "coroutine suspend, resume" { } test "coroutine suspend with block" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const p = async testSuspendBlock(); _ = p; try expect(!global_result); @@ -210,6 +234,9 @@ var await_a_promise: anyframe = undefined; var await_final_result: i32 = 0; test "coroutine await" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + await_seq('a'); var p = async await_amain(); _ = p; @@ -247,6 +274,9 @@ fn await_seq(c: u8) void { var early_final_result: i32 = 0; test "coroutine await early return" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + early_seq('a'); var p = async early_amain(); _ = p; @@ -275,6 +305,9 @@ fn early_seq(c: u8) void { } test "async function with dot syntax" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var y: i32 = 1; fn foo() callconv(.Async) void { @@ -288,6 +321,9 @@ test "async function with dot syntax" { } test "async fn pointer in a struct field" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + var data: i32 = 1; const Foo = struct { bar: fn (*i32) callconv(.Async) void, @@ -313,6 +349,9 @@ fn simpleAsyncFn2(y: *i32) callconv(.Async) void { } test "@asyncCall with return type" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const Foo = struct { bar: fn () callconv(.Async) i32, @@ -337,6 +376,9 @@ test "@asyncCall with return type" { } test "async fn with inferred error set" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var global_frame: anyframe = undefined; @@ -367,6 +409,9 @@ test "async fn with inferred error set" { } test "error return trace across suspend points - early return" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const p = nonFailing(); resume p; const p2 = async printTrace(p); @@ -374,6 +419,9 @@ test "error return trace across suspend points - early return" { } test "error return trace across suspend points - async return" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const p = nonFailing(); const p2 = async printTrace(p); _ = p2; @@ -404,6 +452,9 @@ fn printTrace(p: anyframe->(anyerror!void)) callconv(.Async) void { } test "break from suspend" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + var my_result: i32 = 1; const p = async testBreakFromSuspend(&my_result); _ = p; @@ -419,6 +470,9 @@ fn testBreakFromSuspend(my_result: *i32) callconv(.Async) void { } test "heap allocated async function frame" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var x: i32 = 42; @@ -443,6 +497,9 @@ test "heap allocated async function frame" { } test "async function call return value" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var frame: anyframe = undefined; var pt = Point{ .x = 10, .y = 11 }; @@ -484,6 +541,9 @@ test "async function call return value" { } test "suspension points inside branching control flow" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var result: i32 = 10; @@ -510,6 +570,9 @@ test "suspension points inside branching control flow" { } test "call async function which has struct return type" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var frame: anyframe = undefined; @@ -543,6 +606,9 @@ test "call async function which has struct return type" { } test "pass string literal to async function" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var frame: anyframe = undefined; var ok: bool = false; @@ -564,6 +630,9 @@ test "pass string literal to async function" { } test "await inside an errdefer" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var frame: anyframe = undefined; @@ -587,6 +656,9 @@ test "await inside an errdefer" { } test "try in an async function with error union and non-zero-bit payload" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var frame: anyframe = undefined; var ok = false; @@ -617,6 +689,9 @@ test "try in an async function with error union and non-zero-bit payload" { } test "returning a const error from async function" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var frame: anyframe = undefined; var ok = false; @@ -648,6 +723,9 @@ test "returning a const error from async function" { } test "async/await typical usage" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + inline for ([_]bool{ false, true }) |b1| { inline for ([_]bool{ false, true }) |b2| { inline for ([_]bool{ false, true }) |b3| { @@ -743,6 +821,9 @@ fn testAsyncAwaitTypicalUsage( } test "alignment of local variables in async functions" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { var y: u8 = 123; @@ -755,6 +836,9 @@ test "alignment of local variables in async functions" { } test "no reason to resolve frame still works" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + _ = async simpleNothing(); } fn simpleNothing() void { @@ -763,6 +847,9 @@ fn simpleNothing() void { } test "async call a generic function" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { var f = async func(i32, 2); @@ -783,6 +870,9 @@ test "async call a generic function" { } test "return from suspend block" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { expect(func() == 1234) catch @panic("test failure"); @@ -797,6 +887,9 @@ test "return from suspend block" { } test "struct parameter to async function is copied to the frame" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { const Point = struct { x: i32, @@ -841,6 +934,9 @@ test "struct parameter to async function is copied to the frame" { } test "cast fn to async fn when it is inferred to be async" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var frame: anyframe = undefined; var ok = false; @@ -869,6 +965,9 @@ test "cast fn to async fn when it is inferred to be async" { } test "cast fn to async fn when it is inferred to be async, awaited directly" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var frame: anyframe = undefined; var ok = false; @@ -896,6 +995,9 @@ test "cast fn to async fn when it is inferred to be async, awaited directly" { } test "await does not force async if callee is blocking" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { fn simple() i32 { return 1234; @@ -906,6 +1008,9 @@ test "await does not force async if callee is blocking" { } test "recursive async function" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + try expect(recursiveAsyncFunctionTest(false).doTheTest() == 55); try expect(recursiveAsyncFunctionTest(true).doTheTest() == 55); } @@ -968,6 +1073,9 @@ fn recursiveAsyncFunctionTest(comptime suspending_implementation: bool) type { } test "@asyncCall with comptime-known function, but not awaited directly" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var global_frame: anyframe = undefined; @@ -997,6 +1105,9 @@ test "@asyncCall with comptime-known function, but not awaited directly" { } test "@asyncCall with actual frame instead of byte buffer" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { fn func() i32 { suspend {} @@ -1011,6 +1122,9 @@ test "@asyncCall with actual frame instead of byte buffer" { } test "@asyncCall using the result location inside the frame" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { fn simple2(y: *i32) callconv(.Async) i32 { defer y.* += 2; @@ -1038,6 +1152,9 @@ test "@asyncCall using the result location inside the frame" { } test "@TypeOf an async function call of generic fn with error union type" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { fn func(comptime x: anytype) anyerror!i32 { const T = @TypeOf(async func(x)); @@ -1049,6 +1166,9 @@ test "@TypeOf an async function call of generic fn with error union type" { } test "using @TypeOf on a generic function call" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var global_frame: anyframe = undefined; var global_ok = false; @@ -1074,6 +1194,9 @@ test "using @TypeOf on a generic function call" { } test "recursive call of await @asyncCall with struct return type" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var global_frame: anyframe = undefined; var global_ok = false; @@ -1110,6 +1233,9 @@ test "recursive call of await @asyncCall with struct return type" { } test "nosuspend function call" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { const result = nosuspend add(50, 100); @@ -1126,6 +1252,9 @@ test "nosuspend function call" { } test "await used in expression and awaiting fn with no suspend but async calling convention" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { fn atest() void { var f1 = async add(1, 2); @@ -1142,6 +1271,9 @@ test "await used in expression and awaiting fn with no suspend but async calling } test "await used in expression after a fn call" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { fn atest() void { var f1 = async add(3, 4); @@ -1160,6 +1292,9 @@ test "await used in expression after a fn call" { } test "async fn call used in expression after a fn call" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { fn atest() void { var sum: i32 = 0; @@ -1177,6 +1312,9 @@ test "async fn call used in expression after a fn call" { } test "suspend in for loop" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var global_frame: ?anyframe = null; @@ -1203,6 +1341,9 @@ test "suspend in for loop" { } test "suspend in while loop" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var global_frame: ?anyframe = null; @@ -1240,6 +1381,9 @@ test "suspend in while loop" { } test "correctly spill when returning the error union result of another async fn" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var global_frame: anyframe = undefined; @@ -1263,6 +1407,9 @@ test "correctly spill when returning the error union result of another async fn" } test "spill target expr in a for loop" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var global_frame: anyframe = undefined; @@ -1294,6 +1441,9 @@ test "spill target expr in a for loop" { } test "spill target expr in a for loop, with a var decl in the loop body" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var global_frame: anyframe = undefined; @@ -1330,6 +1480,9 @@ test "spill target expr in a for loop, with a var decl in the loop body" { } test "async call with @call" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var global_frame: anyframe = undefined; fn doTheTest() void { @@ -1352,6 +1505,9 @@ test "async call with @call" { } test "async function passed 0-bit arg after non-0-bit arg" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var global_frame: anyframe = undefined; var global_int: i32 = 0; @@ -1373,6 +1529,9 @@ test "async function passed 0-bit arg after non-0-bit arg" { } test "async function passed align(16) arg after align(8) arg" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var global_frame: anyframe = undefined; var global_int: u128 = 0; @@ -1395,6 +1554,9 @@ test "async function passed align(16) arg after align(8) arg" { } test "async function call resolves target fn frame, comptime func" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var global_frame: anyframe = undefined; var global_int: i32 = 9; @@ -1417,6 +1579,9 @@ test "async function call resolves target fn frame, comptime func" { } test "async function call resolves target fn frame, runtime func" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var global_frame: anyframe = undefined; var global_int: i32 = 9; @@ -1440,6 +1605,9 @@ test "async function call resolves target fn frame, runtime func" { } test "properly spill optional payload capture value" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var global_frame: anyframe = undefined; var global_int: usize = 2; @@ -1464,6 +1632,9 @@ test "properly spill optional payload capture value" { } test "handle defer interfering with return value spill" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var global_frame1: anyframe = undefined; var global_frame2: anyframe = undefined; @@ -1504,6 +1675,9 @@ test "handle defer interfering with return value spill" { } test "take address of temporary async frame" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var global_frame: anyframe = undefined; var finished = false; @@ -1533,6 +1707,9 @@ test "take address of temporary async frame" { } test "nosuspend await" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var finished = false; @@ -1554,6 +1731,9 @@ test "nosuspend await" { } test "nosuspend on function calls" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S0 = struct { b: i32 = 42, }; @@ -1570,6 +1750,9 @@ test "nosuspend on function calls" { } test "nosuspend on async function calls" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S0 = struct { b: i32 = 42, }; @@ -1588,6 +1771,7 @@ test "nosuspend on async function calls" { } // test "resume nosuspend async function calls" { +// if (builtin.zig_backend != .stage1) return error.SkipZigTest; // if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO // const S0 = struct { // b: i32 = 42, // }; @@ -1610,6 +1794,9 @@ test "nosuspend on async function calls" { // } test "nosuspend resume async function calls" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S0 = struct { b: i32 = 42, }; @@ -1632,6 +1819,9 @@ test "nosuspend resume async function calls" { } test "avoid forcing frame alignment resolution implicit cast to *anyopaque" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const S = struct { var x: ?*anyopaque = null; @@ -1648,6 +1838,9 @@ test "avoid forcing frame alignment resolution implicit cast to *anyopaque" { } test "@asyncCall with pass-by-value arguments" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const F0: u64 = 0xbeefbeefbeefbeef; const F1: u64 = 0xf00df00df00df00d; const F2: u64 = 0xcafecafecafecafe; @@ -1681,6 +1874,9 @@ test "@asyncCall with pass-by-value arguments" { } test "@asyncCall with arguments having non-standard alignment" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO + const F0: u64 = 0xbeefbeef; const F1: u64 = 0xf00df00df00df00d; diff --git a/test/behavior/await_struct.zig b/test/behavior/await_struct.zig index 229a72b9ff..8e1cae1135 100644 --- a/test/behavior/await_struct.zig +++ b/test/behavior/await_struct.zig @@ -10,6 +10,8 @@ var await_a_promise: anyframe = undefined; var await_final_result = Foo{ .x = 0 }; test "coroutine await struct" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + await_seq('a'); var p = async await_amain(); _ = p; diff --git a/test/behavior/bugs/1120.zig b/test/behavior/bugs/1120.zig index cb68f87e8d..84c51feeac 100644 --- a/test/behavior/bugs/1120.zig +++ b/test/behavior/bugs/1120.zig @@ -11,6 +11,8 @@ const B = packed struct { b: u6, }; test "bug 1120" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO + var a = A{ .a = 2, .b = 2 }; var b = B{ .q = 22, .a = 3, .b = 2 }; var t: usize = 0; diff --git a/test/behavior/bugs/529.zig b/test/behavior/bugs/529.zig index 6aeb73c331..e23321cd5c 100644 --- a/test/behavior/bugs/529.zig +++ b/test/behavior/bugs/529.zig @@ -9,6 +9,8 @@ comptime { } test "issue 529 fixed" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO + @import("529_other_file.zig").issue529(null); issue529(null); } diff --git a/test/behavior/bugs/6781.zig b/test/behavior/bugs/6781.zig index 0c9126510d..8315e81c67 100644 --- a/test/behavior/bugs/6781.zig +++ b/test/behavior/bugs/6781.zig @@ -61,6 +61,8 @@ pub const JournalHeader = packed struct { }; test "fixed" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO + var buffer = [_]u8{0} ** 65536; var entry = std.mem.bytesAsValue(JournalHeader, buffer[0..@sizeOf(JournalHeader)]); entry.* = .{ diff --git a/test/behavior/bugs/7027.zig b/test/behavior/bugs/7027.zig index 064c511ca8..a49a1ca1a4 100644 --- a/test/behavior/bugs/7027.zig +++ b/test/behavior/bugs/7027.zig @@ -13,7 +13,9 @@ fn foo(arg: anytype) void { _ = arg; } -test "" { +test { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO + comptime var foobar = Foobar.foo(); foo(foobar.str[0..10]); } diff --git a/test/behavior/bugs/920.zig b/test/behavior/bugs/920.zig index 8d30f01369..380d42e5de 100644 --- a/test/behavior/bugs/920.zig +++ b/test/behavior/bugs/920.zig @@ -57,6 +57,8 @@ const NormalDist = blk: { }; test "bug 920 fixed" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO + const NormalDist1 = blk: { break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case); }; diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index c4c6f47981..cbe6cfc296 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -833,6 +833,8 @@ test "const type-annotated local initialized with function call has correct type } test "comptime pointer load through elem_ptr" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 fails this test + const S = struct { x: usize, }; diff --git a/test/behavior/select.zig b/test/behavior/select.zig index 5c69094413..8b4cba49bd 100644 --- a/test/behavior/select.zig +++ b/test/behavior/select.zig @@ -2,20 +2,21 @@ const std = @import("std"); const builtin = @import("builtin"); const mem = std.mem; const expect = std.testing.expect; -const Vector = std.meta.Vector; test "@select" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO + const S = struct { fn doTheTest() !void { - var a: Vector(4, bool) = [4]bool{ true, false, true, false }; - var b: Vector(4, i32) = [4]i32{ -1, 4, 999, -31 }; - var c: Vector(4, i32) = [4]i32{ -5, 1, 0, 1234 }; + var a: @Vector(4, bool) = [4]bool{ true, false, true, false }; + var b: @Vector(4, i32) = [4]i32{ -1, 4, 999, -31 }; + var c: @Vector(4, i32) = [4]i32{ -5, 1, 0, 1234 }; var abc = @select(i32, a, b, c); try expect(mem.eql(i32, &@as([4]i32, abc), &[4]i32{ -1, 1, 999, 1234 })); - var x: Vector(4, bool) = [4]bool{ false, false, false, true }; - var y: Vector(4, f32) = [4]f32{ 0.001, 33.4, 836, -3381.233 }; - var z: Vector(4, f32) = [4]f32{ 0.0, 312.1, -145.9, 9993.55 }; + var x: @Vector(4, bool) = [4]bool{ false, false, false, true }; + var y: @Vector(4, f32) = [4]f32{ 0.001, 33.4, 836, -3381.233 }; + var z: @Vector(4, f32) = [4]f32{ 0.0, 312.1, -145.9, 9993.55 }; var xyz = @select(f32, x, y, z); try expect(mem.eql(f32, &@as([4]f32, xyz), &[4]f32{ 0.0, 312.1, -145.9, -3381.233 })); } From a36f4ee290fa9f3f1515e8aa9bd2bb0f0117c505 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 23 Mar 2022 09:40:29 -0700 Subject: [PATCH 0892/2031] stage2: able to slice to sentinel index at comptime The runtime behavior allowed this in both stage1 and stage2, but stage1 fails with index out of bounds during comptime. This behavior makes sense to support, and comptime behavior should match runtime behavior. I implement this fix only in stage2. --- src/Sema.zig | 39 ++++++++++++++++++++++++++++++++----- test/behavior/slice.zig | 43 +++++++++++++++++++++++++++++++++++++++++ test/compile_errors.zig | 14 ++++++++++---- 3 files changed, 87 insertions(+), 9 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index fe10810d57..7e87ebbf33 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -19713,17 +19713,31 @@ fn analyzeSlice( if (!end_is_len) { const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); if (try sema.resolveMaybeUndefVal(block, end_src, end)) |end_val| { - if (end_val.compare(.gt, len_val, Type.usize, target)) { + const len_s_val = try Value.Tag.int_u64.create( + sema.arena, + array_ty.arrayLenIncludingSentinel(), + ); + if (end_val.compare(.gt, len_s_val, Type.usize, target)) { + const sentinel_label: []const u8 = if (array_ty.sentinel() != null) + " +1 (sentinel)" + else + ""; + return sema.fail( block, end_src, - "end index {} out of bounds for array of length {}", + "end index {} out of bounds for array of length {}{s}", .{ end_val.fmtValue(Type.usize, target), len_val.fmtValue(Type.usize, target), + sentinel_label, }, ); } + + // end_is_len is only true if we are NOT using the sentinel + // length. For sentinel-length, we don't want the type to + // contain the sentinel. if (end_val.eql(len_val, Type.usize, target)) { end_is_len = true; } @@ -19737,22 +19751,37 @@ fn analyzeSlice( const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| { + const has_sentinel = slice_ty.sentinel() != null; var int_payload: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, - .data = slice_val.sliceLen(target), + .data = slice_val.sliceLen(target) + @boolToInt(has_sentinel), }; const slice_len_val = Value.initPayload(&int_payload.base); if (end_val.compare(.gt, slice_len_val, Type.usize, target)) { + const sentinel_label: []const u8 = if (has_sentinel) + " +1 (sentinel)" + else + ""; + return sema.fail( block, end_src, - "end index {} out of bounds for slice of length {}", + "end index {} out of bounds for slice of length {d}{s}", .{ end_val.fmtValue(Type.usize, target), - slice_len_val.fmtValue(Type.usize, target), + slice_val.sliceLen(target), + sentinel_label, }, ); } + + // If the slice has a sentinel, we subtract one so that + // end_is_len is only true if it equals the length WITHOUT + // the sentinel, so we don't add a sentinel type. + if (has_sentinel) { + int_payload.data -= 1; + } + if (end_val.eql(slice_len_val, Type.usize, target)) { end_is_len = true; } diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 432524ebf7..9f3ba001cf 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -640,3 +640,46 @@ test "slice sentinel access at comptime" { try expect(slice0[slice0.len] == 0); } } + +test "slicing array with sentinel as end index" { + // Doesn't work in stage1 + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + + const S = struct { + fn do() !void { + var array = [_:0]u8{ 1, 2, 3, 4 }; + var slice = array[4..5]; + try expect(slice.len == 1); + try expect(slice[0] == 0); + try expect(@TypeOf(slice) == *[1]u8); + } + }; + + try S.do(); + comptime try S.do(); +} + +test "slicing slice with sentinel as end index" { + // Doesn't work in stage1 + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + + const S = struct { + fn do() !void { + var array = [_:0]u8{ 1, 2, 3, 4 }; + var src_slice: [:0]u8 = &array; + var slice = src_slice[4..5]; + try expect(slice.len == 1); + try expect(slice[0] == 0); + try expect(@TypeOf(slice) == *[1]u8); + } + }; + + try S.do(); + comptime try S.do(); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index c479dd9c88..5008bdd7b8 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -26,11 +26,16 @@ pub fn addCases(ctx: *TestContext) !void { \\comptime { \\ var array = [_:0]u8{ 1, 2, 3, 4 }; \\ var src_slice: [:0]u8 = &array; - \\ var slice = src_slice[2..5]; + \\ var slice = src_slice[2..6]; \\ _ = slice; \\} \\comptime { \\ var array = [_:0]u8{ 1, 2, 3, 4 }; + \\ var slice = array[2..6]; + \\ _ = slice; + \\} + \\comptime { + \\ var array = [_]u8{ 1, 2, 3, 4 }; \\ var slice = array[2..5]; \\ _ = slice; \\} @@ -40,9 +45,10 @@ pub fn addCases(ctx: *TestContext) !void { \\ _ = slice; \\} , &[_][]const u8{ - ":4:26: error: end index 5 out of bounds for slice of length 4", - ":9:22: error: end index 5 out of bounds for array of length 4", - ":14:22: error: start index 3 is larger than end index 2", + ":4:26: error: end index 6 out of bounds for slice of length 4 +1 (sentinel)", + ":9:22: error: end index 6 out of bounds for array of length 4 +1 (sentinel)", + ":14:22: error: end index 5 out of bounds for array of length 4", + ":19:22: error: start index 3 is larger than end index 2", }); } From 57539a26b4b1a118c9947116f2873ea3c0ced3da Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 23 Mar 2022 17:29:39 -0700 Subject: [PATCH 0893/2031] std.os: disable failing fnctl file locking test See #11074 --- lib/std/os/test.zig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 7f301826b1..1ca83dada6 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -832,9 +832,12 @@ test "writev longer than IOV_MAX" { } test "POSIX file locking with fcntl" { - if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; + if (native_os == .windows or native_os == .wasi) { + // Not POSIX. + return error.SkipZigTest; + } - if (native_os == .linux) { + if (true) { // https://github.com/ziglang/zig/issues/11074 return error.SkipZigTest; } From 7378ce67dabf996f2d0927138f826dfb3d6fa05f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 23 Mar 2022 18:45:51 -0700 Subject: [PATCH 0894/2031] Sema: introduce a type resolution queue That happens after a function body is analyzed. This prevents circular dependency compile errors and yet a way to mark types that need to be fully resolved before a given function is sent to the codegen backend. --- src/Air.zig | 2 +- src/Module.zig | 17 ++++++++++ src/Sema.zig | 35 +++++++++++++++------ src/arch/wasm/CodeGen.zig | 66 +++++++++++++++++++-------------------- test/behavior/eval.zig | 6 ++++ 5 files changed, 82 insertions(+), 44 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index e0f765ddc0..404ee8f9b7 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -1072,7 +1072,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .sub_with_overflow, .mul_with_overflow, .shl_with_overflow, - => return Type.initTag(.bool), + => return Type.bool, } } diff --git a/src/Module.zig b/src/Module.zig index 7b27546f52..79d6343949 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4837,6 +4837,9 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem // Finally we must resolve the return type and parameter types so that backends // have full access to type information. + // Crucially, this happens *after* we set the function state to success above, + // so that dependencies on the function body will now be satisfied rather than + // result in circular dependency errors. const src: LazySrcLoc = .{ .node_offset = 0 }; sema.resolveFnTypes(&inner_block, src, fn_ty_info) catch |err| switch (err) { error.NeededSourceLocation => unreachable, @@ -4847,6 +4850,20 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem else => |e| return e, }; + // Similarly, resolve any queued up types that were requested to be resolved for + // the backends. + for (sema.types_to_resolve.items) |inst_ref| { + const ty = sema.getTmpAir().getRefType(inst_ref); + sema.resolveTypeFully(&inner_block, src, ty) catch |err| switch (err) { + error.NeededSourceLocation => unreachable, + error.GenericPoison => unreachable, + error.ComptimeReturn => unreachable, + error.ComptimeBreak => unreachable, + error.AnalysisFail => {}, + else => |e| return e, + }; + } + return Air{ .instructions = sema.air_instructions.toOwnedSlice(), .extra = sema.air_extra.toOwnedSlice(gpa), diff --git a/src/Sema.zig b/src/Sema.zig index 7e87ebbf33..d8cc908ae8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -63,6 +63,11 @@ comptime_args_fn_inst: Zir.Inst.Index = 0, /// extra hash table lookup in the `monomorphed_funcs` set. /// Sema will set this to null when it takes ownership. preallocated_new_func: ?*Module.Fn = null, +/// The key is `constant` AIR instructions to types that must be fully resolved +/// after the current function body analysis is done. +/// TODO: after upgrading to use InternPool change the key here to be an +/// InternPool value index. +types_to_resolve: std.ArrayListUnmanaged(Air.Inst.Ref) = .{}, const std = @import("std"); const mem = std.mem; @@ -527,6 +532,7 @@ pub fn deinit(sema: *Sema) void { sema.air_values.deinit(gpa); sema.inst_map.deinit(gpa); sema.decl_val_table.deinit(gpa); + sema.types_to_resolve.deinit(gpa); sema.* = undefined; } @@ -1747,7 +1753,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE return sema.bitCast(block, ptr_ty, new_ptr, src); } const ty_op = air_datas[trash_inst].ty_op; - const operand_ty = sema.getTmpAir().typeOf(ty_op.operand); + const operand_ty = sema.typeOf(ty_op.operand); const ptr_operand_ty = try Type.ptr(sema.arena, target, .{ .pointee_type = operand_ty, .@"addrspace" = addr_space, @@ -2592,7 +2598,7 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); try sema.requireRuntimeBlock(block, var_decl_src); - try sema.resolveTypeFully(block, ty_src, var_ty); + try sema.queueFullTypeResolution(var_ty); return block.addTy(.alloc, ptr_type); } @@ -2614,7 +2620,7 @@ fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); try sema.requireRuntimeBlock(block, var_decl_src); - try sema.resolveTypeFully(block, ty_src, var_ty); + try sema.queueFullTypeResolution(var_ty); return block.addTy(.alloc, ptr_type); } @@ -2770,7 +2776,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com } try sema.requireRuntimeBlock(block, src); - try sema.resolveTypeFully(block, ty_src, final_elem_ty); + try sema.queueFullTypeResolution(final_elem_ty); // Change it to a normal alloc. sema.air_instructions.set(ptr_inst, .{ @@ -4363,6 +4369,8 @@ fn addDbgVar( else => unreachable, } + try sema.queueFullTypeResolution(operand_ty); + // Add the name to the AIR. const name_extra_index = @intCast(u32, sema.air_extra.items.len); const elements_used = name.len / 4 + 1; @@ -5004,7 +5012,7 @@ fn analyzeCall( } } - try sema.resolveTypeFully(block, call_src, func_ty_info.return_type); + try sema.queueFullTypeResolution(func_ty_info.return_type); try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len + args.len); @@ -5344,7 +5352,7 @@ fn instantiateGenericCall( total_i += 1; } - try sema.resolveTypeFully(block, call_src, new_fn_info.return_type); + try sema.queueFullTypeResolution(new_fn_info.return_type); } try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len + runtime_args_len); @@ -12030,7 +12038,8 @@ fn unionInit( } try sema.requireRuntimeBlock(block, init_src); - try sema.resolveTypeLayout(block, union_ty_src, union_ty); + _ = union_ty_src; + try sema.queueFullTypeResolution(union_ty); return block.addUnionInit(union_ty, field_index, init); } @@ -12205,6 +12214,7 @@ fn finishStructInit( } try sema.requireRuntimeBlock(block, src); + try sema.queueFullTypeResolution(struct_ty); return block.addAggregateInit(struct_ty, field_inits); } @@ -12351,7 +12361,7 @@ fn zirArrayInit( }; try sema.requireRuntimeBlock(block, runtime_src); - try sema.resolveTypeLayout(block, src, elem_ty); + try sema.queueFullTypeResolution(elem_ty); if (is_ref) { const target = sema.mod.getTarget(); @@ -18339,7 +18349,7 @@ fn storePtr2( // TODO handle if the element type requires comptime try sema.requireRuntimeBlock(block, runtime_src); - try sema.resolveTypeLayout(block, src, elem_ty); + try sema.queueFullTypeResolution(elem_ty); _ = try block.addBinOp(air_tag, ptr, operand); } @@ -21907,7 +21917,7 @@ fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type { return sema.getTmpAir().typeOf(inst); } -fn getTmpAir(sema: Sema) Air { +pub fn getTmpAir(sema: Sema) Air { return .{ .instructions = sema.air_instructions.slice(), .extra = sema.air_extra.items, @@ -22572,3 +22582,8 @@ fn anonStructFieldIndex( fn kit(sema: *Sema, block: *Block, src: LazySrcLoc) Module.WipAnalysis { return .{ .sema = sema, .block = block, .src = src }; } + +fn queueFullTypeResolution(sema: *Sema, ty: Type) !void { + const inst_ref = try sema.addType(ty); + try sema.types_to_resolve.append(sema.gpa, inst_ref); +} diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index eb8d72a994..f2979d96b1 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -632,7 +632,7 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue { // means we must generate it from a constant. const val = self.air.value(ref).?; const ty = self.air.typeOf(ref); - if (!ty.hasRuntimeBits() and !ty.isInt()) { + if (!ty.hasRuntimeBitsIgnoreComptime() and !ty.isInt()) { gop.value_ptr.* = WValue{ .none = {} }; return gop.value_ptr.*; } @@ -805,13 +805,13 @@ fn genFunctype(gpa: Allocator, fn_ty: Type, target: std.Target) !wasm.Type { defer gpa.free(fn_params); fn_ty.fnParamTypes(fn_params); for (fn_params) |param_type| { - if (!param_type.hasRuntimeBits()) continue; + if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; try params.append(typeToValtype(param_type, target)); } } // return type - if (!want_sret and return_type.hasRuntimeBits()) { + if (!want_sret and return_type.hasRuntimeBitsIgnoreComptime()) { try returns.append(typeToValtype(return_type, target)); } @@ -970,7 +970,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu .Naked => return result, .Unspecified, .C => { for (param_types) |ty| { - if (!ty.hasRuntimeBits()) { + if (!ty.hasRuntimeBitsIgnoreComptime()) { continue; } @@ -1015,7 +1015,7 @@ fn restoreStackPointer(self: *Self) !void { /// /// Asserts Type has codegenbits fn allocStack(self: *Self, ty: Type) !WValue { - assert(ty.hasRuntimeBits()); + assert(ty.hasRuntimeBitsIgnoreComptime()); if (self.initial_stack_value == .none) { try self.initializeStack(); } @@ -1049,7 +1049,7 @@ fn allocStackPtr(self: *Self, inst: Air.Inst.Index) !WValue { try self.initializeStack(); } - if (!pointee_ty.hasRuntimeBits()) { + if (!pointee_ty.hasRuntimeBitsIgnoreComptime()) { return self.allocStack(Type.usize); // create a value containing just the stack pointer. } @@ -1235,18 +1235,18 @@ fn isByRef(ty: Type, target: std.Target) bool { .Struct, .Frame, .Union, - => return ty.hasRuntimeBits(), + => return ty.hasRuntimeBitsIgnoreComptime(), .Int => return if (ty.intInfo(target).bits > 64) true else false, .ErrorUnion => { - const has_tag = ty.errorUnionSet().hasRuntimeBits(); - const has_pl = ty.errorUnionPayload().hasRuntimeBits(); + const has_tag = ty.errorUnionSet().hasRuntimeBitsIgnoreComptime(); + const has_pl = ty.errorUnionPayload().hasRuntimeBitsIgnoreComptime(); if (!has_tag or !has_pl) return false; - return ty.hasRuntimeBits(); + return ty.hasRuntimeBitsIgnoreComptime(); }, .Optional => { if (ty.isPtrLikeOptional()) return false; var buf: Type.Payload.ElemType = undefined; - return ty.optionalChild(&buf).hasRuntimeBits(); + return ty.optionalChild(&buf).hasRuntimeBitsIgnoreComptime(); }, .Pointer => { // Slices act like struct and will be passed by reference @@ -1511,7 +1511,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); const ret_ty = self.air.typeOf(un_op).childType(); - if (!ret_ty.hasRuntimeBits()) return WValue.none; + if (!ret_ty.hasRuntimeBitsIgnoreComptime()) return WValue.none; if (!isByRef(ret_ty, self.target)) { const result = try self.load(operand, ret_ty, 0); @@ -1567,7 +1567,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const arg_val = try self.resolveInst(arg_ref); const arg_ty = self.air.typeOf(arg_ref); - if (!arg_ty.hasRuntimeBits()) continue; + if (!arg_ty.hasRuntimeBitsIgnoreComptime()) continue; switch (arg_val) { .stack_offset => try self.emitWValue(try self.buildPointerOffset(arg_val, 0, .new)), @@ -1591,7 +1591,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. try self.addLabel(.call_indirect, fn_type_index); } - if (self.liveness.isUnused(inst) or !ret_ty.hasRuntimeBits()) { + if (self.liveness.isUnused(inst) or !ret_ty.hasRuntimeBitsIgnoreComptime()) { return WValue.none; } else if (ret_ty.isNoReturn()) { try self.addTag(.@"unreachable"); @@ -1625,7 +1625,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro .ErrorUnion => { const err_ty = ty.errorUnionSet(); const pl_ty = ty.errorUnionPayload(); - if (!pl_ty.hasRuntimeBits()) { + if (!pl_ty.hasRuntimeBitsIgnoreComptime()) { return self.store(lhs, rhs, err_ty, 0); } @@ -1638,7 +1638,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro } var buf: Type.Payload.ElemType = undefined; const pl_ty = ty.optionalChild(&buf); - if (!pl_ty.hasRuntimeBits()) { + if (!pl_ty.hasRuntimeBitsIgnoreComptime()) { return self.store(lhs, rhs, Type.u8, 0); } @@ -1696,7 +1696,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const operand = try self.resolveInst(ty_op.operand); const ty = self.air.getRefType(ty_op.ty); - if (!ty.hasRuntimeBits()) return WValue{ .none = {} }; + if (!ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} }; if (isByRef(ty, self.target)) { const new_local = try self.allocStack(ty); @@ -2200,7 +2200,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner if (operand_ty.zigTypeTag() == .Optional and !operand_ty.isPtrLikeOptional()) { var buf: Type.Payload.ElemType = undefined; const payload_ty = operand_ty.optionalChild(&buf); - if (payload_ty.hasRuntimeBits()) { + if (payload_ty.hasRuntimeBitsIgnoreComptime()) { // When we hit this case, we must check the value of optionals // that are not pointers. This means first checking against non-null for // both lhs and rhs, as well as checking the payload are matching of lhs and rhs @@ -2257,7 +2257,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const block = self.blocks.get(br.block_inst).?; // if operand has codegen bits we should break with a value - if (self.air.typeOf(br.operand).hasRuntimeBits()) { + if (self.air.typeOf(br.operand).hasRuntimeBitsIgnoreComptime()) { const operand = try self.resolveInst(br.operand); const op = switch (operand) { .stack_offset => try self.buildPointerOffset(operand, 0, .new), @@ -2357,7 +2357,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const operand = try self.resolveInst(struct_field.struct_operand); const field_index = struct_field.field_index; const field_ty = struct_ty.structFieldType(field_index); - if (!field_ty.hasRuntimeBits()) return WValue{ .none = {} }; + if (!field_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} }; const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch { return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(self.target)}); }; @@ -2544,7 +2544,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W // load the error tag value try self.emitWValue(operand); - if (pl_ty.hasRuntimeBits()) { + if (pl_ty.hasRuntimeBitsIgnoreComptime()) { try self.addMemArg(.i32_load16_u, .{ .offset = operand.offset(), .alignment = err_ty.errorUnionSet().abiAlignment(self.target), @@ -2567,7 +2567,7 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) const op_ty = self.air.typeOf(ty_op.operand); const err_ty = if (op_is_ptr) op_ty.childType() else op_ty; const payload_ty = err_ty.errorUnionPayload(); - if (!payload_ty.hasRuntimeBits()) return WValue{ .none = {} }; + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} }; const err_align = err_ty.abiAlignment(self.target); const set_size = err_ty.errorUnionSet().abiSize(self.target); const offset = mem.alignForwardGeneric(u64, set_size, err_align); @@ -2585,7 +2585,7 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) In const op_ty = self.air.typeOf(ty_op.operand); const err_ty = if (op_is_ptr) op_ty.childType() else op_ty; const payload_ty = err_ty.errorUnionPayload(); - if (op_is_ptr or !payload_ty.hasRuntimeBits()) { + if (op_is_ptr or !payload_ty.hasRuntimeBitsIgnoreComptime()) { return operand; } @@ -2599,7 +2599,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const operand = try self.resolveInst(ty_op.operand); const op_ty = self.air.typeOf(ty_op.operand); - if (!op_ty.hasRuntimeBits()) return operand; + if (!op_ty.hasRuntimeBitsIgnoreComptime()) return operand; const err_ty = self.air.getRefType(ty_op.ty); const err_align = err_ty.abiAlignment(self.target); const set_size = err_ty.errorUnionSet().abiSize(self.target); @@ -2624,7 +2624,7 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const operand = try self.resolveInst(ty_op.operand); const err_ty = self.air.getRefType(ty_op.ty); - if (!err_ty.errorUnionPayload().hasRuntimeBits()) return operand; + if (!err_ty.errorUnionPayload().hasRuntimeBitsIgnoreComptime()) return operand; const err_union = try self.allocStack(err_ty); try self.store(err_union, operand, err_ty.errorUnionSet(), 0); @@ -2690,7 +2690,7 @@ fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) const payload_ty = optional_ty.optionalChild(&buf); // When payload is zero-bits, we can treat operand as a value, rather than // a pointer to the stack value - if (payload_ty.hasRuntimeBits()) { + if (payload_ty.hasRuntimeBitsIgnoreComptime()) { try self.addMemArg(.i32_load8_u, .{ .offset = operand.offset(), .alignment = 1 }); } } @@ -2710,7 +2710,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const operand = try self.resolveInst(ty_op.operand); const opt_ty = self.air.typeOf(ty_op.operand); const payload_ty = self.air.typeOfIndex(inst); - if (!payload_ty.hasRuntimeBits()) return WValue{ .none = {} }; + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} }; if (opt_ty.isPtrLikeOptional()) return operand; const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target); @@ -2731,7 +2731,7 @@ fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { var buf: Type.Payload.ElemType = undefined; const payload_ty = opt_ty.optionalChild(&buf); - if (!payload_ty.hasRuntimeBits() or opt_ty.isPtrLikeOptional()) { + if (!payload_ty.hasRuntimeBitsIgnoreComptime() or opt_ty.isPtrLikeOptional()) { return operand; } @@ -2745,7 +2745,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue const opt_ty = self.air.typeOf(ty_op.operand).childType(); var buf: Type.Payload.ElemType = undefined; const payload_ty = opt_ty.optionalChild(&buf); - if (!payload_ty.hasRuntimeBits()) { + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { return self.fail("TODO: Implement OptionalPayloadPtrSet for optional with zero-sized type {}", .{payload_ty.fmtDebug()}); } @@ -2769,7 +2769,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const payload_ty = self.air.typeOf(ty_op.operand); - if (!payload_ty.hasRuntimeBits()) { + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { const non_null_bit = try self.allocStack(Type.initTag(.u1)); try self.emitWValue(non_null_bit); try self.addImm32(1); @@ -2958,7 +2958,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const slice_local = try self.allocStack(slice_ty); // store the array ptr in the slice - if (array_ty.hasRuntimeBits()) { + if (array_ty.hasRuntimeBitsIgnoreComptime()) { try self.store(slice_local, operand, Type.usize, 0); } @@ -3408,7 +3408,7 @@ fn airWasmMemoryGrow(self: *Self, inst: Air.Inst.Index) !WValue { } fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { - assert(operand_ty.hasRuntimeBits()); + assert(operand_ty.hasRuntimeBitsIgnoreComptime()); assert(op == .eq or op == .neq); var buf: Type.Payload.ElemType = undefined; const payload_ty = operand_ty.optionalChild(&buf); @@ -3575,7 +3575,7 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; - if (!payload_ty.hasRuntimeBits()) { + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { return operand; } diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index cbe6cfc296..2129512f96 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -853,3 +853,9 @@ test "comptime pointer load through elem_ptr" { assert(ptr[1].x == 2); } } + +test "debug variable type resolved through indirect zero-bit types" { + const T = struct { key: []void }; + const slice: []const T = &[_]T{}; + _ = slice; +} From 74ccd0c40b6871093f52769d342c1316e9ded0c0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 23 Mar 2022 19:20:38 -0700 Subject: [PATCH 0895/2031] Sema: Value.copy: we gotta copy the bytes For Value.Tag.bytes, the value copy implementation did not copy the bytes array. No good. This operation must do a deep copy. If we want some other mechanism for not copying very large byte buffers then it has to work differently than this one. --- src/value.zig | 10 +++++++++- test/behavior/cast.zig | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/value.zig b/src/value.zig index fb9a2f826a..24f6b1df75 100644 --- a/src/value.zig +++ b/src/value.zig @@ -526,7 +526,15 @@ pub const Value = extern union { }; return Value{ .ptr_otherwise = &new_payload.base }; }, - .bytes => return self.copyPayloadShallow(arena, Payload.Bytes), + .bytes => { + const bytes = self.castTag(.bytes).?.data; + const new_payload = try arena.create(Payload.Bytes); + new_payload.* = .{ + .base = .{ .tag = .bytes }, + .data = try arena.dupe(u8, bytes), + }; + return Value{ .ptr_otherwise = &new_payload.base }; + }, .repeated, .eu_payload, .opt_payload, diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 003b1b7bec..4bb8f147ec 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -462,7 +462,7 @@ fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 { } test "implicit cast from *const [N]T to []const T" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testCastConstArrayRefToConstSlice(); comptime try testCastConstArrayRefToConstSlice(); From aca42c62598523b92de7a51d3d84f2f2e5146536 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 23 Mar 2022 19:58:13 -0700 Subject: [PATCH 0896/2031] Sema: fix comptime elem_ptr compare fixed address --- src/value.zig | 30 ++++++++++++++++++++++++++++-- test/behavior/pointers.zig | 5 ++--- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/value.zig b/src/value.zig index 24f6b1df75..be5794d6ae 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1008,7 +1008,7 @@ pub const Value = extern union { space: *BigIntSpace, target: Target, sema_kit: ?Module.WipAnalysis, - ) !BigIntConst { + ) Module.CompileError!BigIntConst { switch (val.tag()) { .zero, .bool_false, @@ -1035,6 +1035,14 @@ pub const Value = extern union { return BigIntMutable.init(&space.limbs, x).toConst(); }, + .elem_ptr => { + const elem_ptr = val.castTag(.elem_ptr).?.data; + const array_addr = (try elem_ptr.array_ptr.getUnsignedIntAdvanced(target, sema_kit)).?; + const elem_size = elem_ptr.elem_ty.abiSize(target); + const new_addr = array_addr + elem_size * elem_ptr.index; + return BigIntMutable.init(&space.limbs, new_addr).toConst(); + }, + else => unreachable, } } @@ -1815,7 +1823,10 @@ pub const Value = extern union { return orderAgainstZeroAdvanced(lhs, null) catch unreachable; } - pub fn orderAgainstZeroAdvanced(lhs: Value, sema_kit: ?Module.WipAnalysis) !std.math.Order { + pub fn orderAgainstZeroAdvanced( + lhs: Value, + sema_kit: ?Module.WipAnalysis, + ) Module.CompileError!std.math.Order { return switch (lhs.tag()) { .zero, .bool_false, @@ -1851,6 +1862,21 @@ pub const Value = extern union { .float_80 => std.math.order(lhs.castTag(.float_80).?.data, 0), .float_128 => std.math.order(lhs.castTag(.float_128).?.data, 0), + .elem_ptr => { + const elem_ptr = lhs.castTag(.elem_ptr).?.data; + switch (try elem_ptr.array_ptr.orderAgainstZeroAdvanced(sema_kit)) { + .lt => unreachable, + .gt => return .gt, + .eq => { + if (elem_ptr.index == 0) { + return .eq; + } else { + return .gt; + } + }, + } + }, + else => unreachable, }; } diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 297c9be7b8..5b9c3c8cf0 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -366,11 +366,10 @@ test "pointer sentinel with +inf" { } test "pointer to array at fixed address" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const array = @intToPtr(*volatile [1]u32, 0x10); + const array = @intToPtr(*volatile [2]u32, 0x10); // Silly check just to reference `array` try expect(@ptrToInt(&array[0]) == 0x10); + try expect(@ptrToInt(&array[1]) == 0x14); } test "pointer arithmetic affects the alignment" { From 2af69710a7b1513f3540ae0e1178cb5f3948204d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 23 Mar 2022 23:28:05 -0700 Subject: [PATCH 0897/2031] stage2: fix some generics issues * std.meta: correct use of `default_value` in reification. stage1 accepted a wrong type for `null`. * Sema: after instantiating a generic function, if the return type ends up being a comptime-known type, then we return an error, undoing the generic function instantiation, and making a comptime function call instead. - We also needed to clean up the dependency graph in this case. * Sema: reified enums set tag_ty_inferred to false since an integer tag type is provided. This is a limitation of the `@Type` builtin which will be addressed with #10710. * Sema: fix resolveInferredErrorSet incorrectly calling ensureFuncBodyAnalyzed on generic functions. --- lib/std/meta.zig | 12 ++++---- src/Module.zig | 4 +-- src/Sema.zig | 57 +++++++++++++++++++++++++++++--------- test/behavior/generics.zig | 36 ++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 21 deletions(-) diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 7be06e0fd1..4e03eb602b 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -569,10 +569,10 @@ test "std.meta.fieldNames" { } pub fn FieldEnum(comptime T: type) type { - const fieldInfos = fields(T); - var enumFields: [fieldInfos.len]std.builtin.Type.EnumField = undefined; + const field_infos = fields(T); + var enumFields: [field_infos.len]std.builtin.Type.EnumField = undefined; var decls = [_]std.builtin.Type.Declaration{}; - inline for (fieldInfos) |field, i| { + inline for (field_infos) |field, i| { enumFields[i] = .{ .name = field.name, .value = i, @@ -581,7 +581,7 @@ pub fn FieldEnum(comptime T: type) type { return @Type(.{ .Enum = .{ .layout = .Auto, - .tag_type = std.math.IntFittingRange(0, fieldInfos.len - 1), + .tag_type = std.math.IntFittingRange(0, field_infos.len - 1), .fields = &enumFields, .decls = &decls, .is_exhaustive = true, @@ -966,7 +966,7 @@ pub fn ArgsTuple(comptime Function: type) type { argument_field_list[i] = .{ .name = std.fmt.bufPrint(&num_buf, "{d}", .{i}) catch unreachable, .field_type = T, - .default_value = @as(?T, null), + .default_value = null, .is_comptime = false, .alignment = if (@sizeOf(T) > 0) @alignOf(T) else 0, }; @@ -997,7 +997,7 @@ pub fn Tuple(comptime types: []const type) type { tuple_fields[i] = .{ .name = std.fmt.bufPrint(&num_buf, "{d}", .{i}) catch unreachable, .field_type = T, - .default_value = @as(?T, null), + .default_value = null, .is_comptime = false, .alignment = if (@sizeOf(T) > 0) @alignOf(T) else 0, }; diff --git a/src/Module.zig b/src/Module.zig index 79d6343949..0666936f1f 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -781,11 +781,11 @@ pub const Decl = struct { return &decl_plus_emit_h.emit_h; } - fn removeDependant(decl: *Decl, other: *Decl) void { + pub fn removeDependant(decl: *Decl, other: *Decl) void { assert(decl.dependants.swapRemove(other)); } - fn removeDependency(decl: *Decl, other: *Decl) void { + pub fn removeDependency(decl: *Decl, other: *Decl) void { assert(decl.dependencies.swapRemove(other)); } diff --git a/src/Sema.zig b/src/Sema.zig index d8cc908ae8..277e4a6ba6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4688,7 +4688,7 @@ fn analyzeCall( const gpa = sema.gpa; - const is_comptime_call = block.is_comptime or modifier == .compile_time or + var is_comptime_call = block.is_comptime or modifier == .compile_time or try sema.typeRequiresComptime(block, func_src, func_ty_info.return_type); var is_inline_call = is_comptime_call or modifier == .always_inline or func_ty_info.cc == .Inline; @@ -4706,7 +4706,13 @@ fn analyzeCall( )) |some| { return some; } else |err| switch (err) { - error.GenericPoison => is_inline_call = true, + error.GenericPoison => { + is_inline_call = true; + }, + error.ComptimeReturn => { + is_inline_call = true; + is_comptime_call = true; + }, else => |e| return e, } } @@ -5149,7 +5155,13 @@ fn instantiateGenericCall( // of each of its instantiations. assert(new_decl.dependencies.keys().len == 0); try mod.declareDeclDependency(new_decl, module_fn.owner_decl); - errdefer assert(module_fn.owner_decl.dependants.orderedRemove(new_decl)); + // Resolving the new function type below will possibly declare more decl dependencies + // and so we remove them all here in case of error. + errdefer { + for (new_decl.dependencies.keys()) |dep| { + dep.removeDependant(new_decl); + } + } var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); errdefer new_decl_arena.deinit(); @@ -5285,8 +5297,17 @@ fn instantiateGenericCall( // Populate the Decl ty/val with the function and its type. new_decl.ty = try child_sema.typeOf(new_func_inst).copy(new_decl_arena_allocator); - // If the call evaluated to a generic type return errror and call inline. - if (new_decl.ty.fnInfo().is_generic) return error.GenericPoison; + // If the call evaluated to a return type that requires comptime, never mind + // our generic instantiation. Instead we need to perform a comptime call. + const new_fn_info = new_decl.ty.fnInfo(); + if (try sema.typeRequiresComptime(block, call_src, new_fn_info.return_type)) { + return error.ComptimeReturn; + } + // Similarly, if the call evaluated to a generic type we need to instead + // call it inline. + if (new_fn_info.is_generic or new_fn_info.cc == .Inline) { + return error.GenericPoison; + } new_decl.val = try Value.Tag.function.create(new_decl_arena_allocator, new_func); new_decl.has_tv = true; @@ -12978,10 +12999,14 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl); + // Enum tag type + var buffer: Value.ToTypeBuffer = undefined; + const int_tag_ty = try tag_type_val.toType(&buffer).copy(new_decl_arena_allocator); + enum_obj.* = .{ .owner_decl = new_decl, - .tag_ty = Type.@"null", - .tag_ty_inferred = true, + .tag_ty = int_tag_ty, + .tag_ty_inferred = false, .fields = .{}, .values = .{}, .node_offset = src.node_offset, @@ -12992,10 +13017,6 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I }, }; - // Enum tag type - var buffer: Value.ToTypeBuffer = undefined; - enum_obj.tag_ty = try tag_type_val.toType(&buffer).copy(new_decl_arena_allocator); - // Fields const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target)); if (fields_len > 0) { @@ -21111,8 +21132,18 @@ fn resolveInferredErrorSet( return sema.fail(block, src, "unable to resolve inferred error set", .{}); } - // To ensure that all dependencies are properly added to the set. - try sema.ensureFuncBodyAnalyzed(ies.func); + // In order to ensure that all dependencies are properly added to the set, we + // need to ensure the function body is analyzed of the inferred error set. + // However, in the case of comptime/inline function calls with inferred error sets, + // each call gets a new InferredErrorSet object, which points to the same + // `*Module.Fn`. Not only is the function not relevant to the inferred error set + // in this case, it may be a generic function which would cause an assertion failure + // if we called `ensureFuncBodyAnalyzed` on it here. + if (ies.func.owner_decl.ty.fnInfo().return_type.errorUnionSet().castTag(.error_set_inferred).?.data == ies) { + // In this case we are dealing with the actual InferredErrorSet object that + // corresponds to the function, not one created to track an inline/comptime call. + try sema.ensureFuncBodyAnalyzed(ies.func); + } ies.is_resolved = true; diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index e9a46cbf5c..767c2f3ee3 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -241,3 +241,39 @@ test "function parameter is generic" { var rng: u32 = 2; S.init(rng, S.fill); } + +test "generic function instantiation turns into comptime call" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + const E1 = enum { A }; + const e1f = fieldInfo(E1, .A); + try expect(std.mem.eql(u8, e1f.name, "A")); + } + + pub fn fieldInfo(comptime T: type, comptime field: FieldEnum(T)) switch (@typeInfo(T)) { + .Enum => std.builtin.Type.EnumField, + else => void, + } { + return @typeInfo(T).Enum.fields[@enumToInt(field)]; + } + + pub fn FieldEnum(comptime T: type) type { + _ = T; + var enumFields: [1]std.builtin.Type.EnumField = .{.{ .name = "A", .value = 0 }}; + return @Type(.{ + .Enum = .{ + .layout = .Auto, + .tag_type = u0, + .fields = &enumFields, + .decls = &.{}, + .is_exhaustive = true, + }, + }); + } + }; + try S.doTheTest(); +} From 2286f9bd1f1c622d182e8b815864ad90d3ff0e92 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 24 Mar 2022 14:40:58 +0100 Subject: [PATCH 0898/2031] dwarf: add debug info for tuples and anon structs --- src/link/Dwarf.zig | 82 ++++++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 32 deletions(-) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 7643fe55c4..5e1af101de 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -881,45 +881,63 @@ fn addDbgInfoType( } }, .Struct => blk: { - if (ty.tag() == .tuple) { - log.debug("TODO implement .debug_info for type '{}'", .{ty.fmtDebug()}); - try dbg_info_buffer.append(abbrev_pad1); - break :blk; - } - // try dbg_info_buffer.ensureUnusedCapacity(23); // DW.AT.structure_type try dbg_info_buffer.append(abbrev_struct_type); // DW.AT.byte_size, DW.FORM.sdata const abi_size = ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); - // DW.AT.name, DW.FORM.string - const struct_name = try ty.nameAllocArena(arena, target); - try dbg_info_buffer.ensureUnusedCapacity(struct_name.len + 1); - dbg_info_buffer.appendSliceAssumeCapacity(struct_name); - dbg_info_buffer.appendAssumeCapacity(0); - const struct_obj = ty.castTag(.@"struct").?.data; - if (struct_obj.layout == .Packed) { - log.debug("TODO implement .debug_info for packed structs", .{}); - break :blk; - } + switch (ty.tag()) { + .tuple, .anon_struct => { + // DW.AT.name, DW.FORM.string + try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); - const fields = ty.structFields(); - for (fields.keys()) |field_name, field_index| { - const field = fields.get(field_name).?; - // DW.AT.member - try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity(field_name); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.type, DW.FORM.ref4 - var index = dbg_info_buffer.items.len; - try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = field.ty, .reloc = @intCast(u32, index) }); - // DW.AT.data_member_location, DW.FORM.sdata - const field_off = ty.structFieldOffset(field_index, target); - try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); + const fields = ty.tupleFields(); + for (fields.types) |field, field_index| { + // DW.AT.member + try dbg_info_buffer.append(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + try dbg_info_buffer.writer().print("{d}\x00", .{field_index}); + // DW.AT.type, DW.FORM.ref4 + var index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try relocs.append(.{ .ty = field, .reloc = @intCast(u32, index) }); + // DW.AT.data_member_location, DW.FORM.sdata + const field_off = ty.structFieldOffset(field_index, target); + try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); + } + }, + else => { + // DW.AT.name, DW.FORM.string + const struct_name = try ty.nameAllocArena(arena, target); + try dbg_info_buffer.ensureUnusedCapacity(struct_name.len + 1); + dbg_info_buffer.appendSliceAssumeCapacity(struct_name); + dbg_info_buffer.appendAssumeCapacity(0); + + const struct_obj = ty.castTag(.@"struct").?.data; + if (struct_obj.layout == .Packed) { + log.debug("TODO implement .debug_info for packed structs", .{}); + break :blk; + } + + const fields = ty.structFields(); + for (fields.keys()) |field_name, field_index| { + const field = fields.get(field_name).?; + // DW.AT.member + try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2); + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity(field_name); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + var index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try relocs.append(.{ .ty = field.ty, .reloc = @intCast(u32, index) }); + // DW.AT.data_member_location, DW.FORM.sdata + const field_off = ty.structFieldOffset(field_index, target); + try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); + } + }, } // DW.AT.structure_type delimit children From 4e801c27839be3f7ff209aa0f22c158b6340c69b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 24 Mar 2022 14:41:13 +0100 Subject: [PATCH 0899/2031] x64: implement aggregate_init for structs --- src/arch/x86_64/CodeGen.zig | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index f2977977c5..31c825c084 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -5671,13 +5671,31 @@ fn airReduce(self: *Self, inst: Air.Inst.Index) !void { } fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { - const vector_ty = self.air.typeOfIndex(inst); - const len = vector_ty.vectorLen(); + const result_ty = self.air.typeOfIndex(inst); + const len = result_ty.vectorLen(); const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); + const abi_size = @intCast(u32, result_ty.abiSize(self.target.*)); + const abi_align = result_ty.abiAlignment(self.target.*); const result: MCValue = res: { if (self.liveness.isUnused(inst)) break :res MCValue.dead; - return self.fail("TODO implement airAggregateInit for x86_64", .{}); + switch (result_ty.zigTypeTag()) { + .Struct => { + const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align)); + for (elements) |elem, elem_i| { + if (result_ty.structFieldValueComptime(elem_i) != null) continue; // comptime elem + + const elem_ty = result_ty.structFieldType(elem_i); + const elem_off = result_ty.structFieldOffset(elem_i, self.target.*); + const elem_mcv = try self.resolveInst(elem); + try self.genSetStack(elem_ty, stack_offset - @intCast(i32, elem_off), elem_mcv, .{}); + } + break :res MCValue{ .stack_offset = stack_offset }; + }, + .Array => return self.fail("TODO implement aggregate_init for arrays", .{}), + .Vector => return self.fail("TODO implement aggregate_init for vectors", .{}), + else => unreachable, + } }; if (elements.len <= Liveness.bpi - 1) { From 77352c5bea7e328006394b93bc239c3e34b464c9 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 24 Mar 2022 15:24:03 +0100 Subject: [PATCH 0900/2031] x64: implement aggregate_init for arrays --- src/arch/x86_64/CodeGen.zig | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 31c825c084..f72086b23b 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -5672,7 +5672,7 @@ fn airReduce(self: *Self, inst: Air.Inst.Index) !void { fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { const result_ty = self.air.typeOfIndex(inst); - const len = result_ty.vectorLen(); + const len = result_ty.arrayLen(); const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); const abi_size = @intCast(u32, result_ty.abiSize(self.target.*)); @@ -5692,7 +5692,18 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { } break :res MCValue{ .stack_offset = stack_offset }; }, - .Array => return self.fail("TODO implement aggregate_init for arrays", .{}), + .Array => { + const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align)); + const elem_ty = result_ty.childType(); + const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*)); + + for (elements) |elem, elem_i| { + const elem_mcv = try self.resolveInst(elem); + const elem_off = @intCast(i32, elem_size * elem_i); + try self.genSetStack(elem_ty, stack_offset - elem_off, elem_mcv, .{}); + } + break :res MCValue{ .stack_offset = stack_offset }; + }, .Vector => return self.fail("TODO implement aggregate_init for vectors", .{}), else => unreachable, } From 9d2ff7ed161ff0880818002fb75391be336b44ac Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 24 Mar 2022 16:38:04 +0100 Subject: [PATCH 0901/2031] x64: fix struct_field_val for structs fitting in register --- src/arch/x86_64/CodeGen.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index f72086b23b..fe59cd6c63 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2884,6 +2884,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { break :blk dst_mcv; } }; + dst_mcv.freezeIfRegister(&self.register_manager); + defer dst_mcv.unfreezeIfRegister(&self.register_manager); // Shift by struct_field_offset. const shift = @intCast(u8, struct_field_offset * @sizeOf(usize)); @@ -2893,7 +2895,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const max_reg_bit_width = Register.rax.size(); const mask_shift = @intCast(u6, (max_reg_bit_width - struct_field_ty.bitSize(self.target.*))); const mask = (~@as(u64, 0)) >> mask_shift; - try self.genBinMathOpMir(.@"and", Type.usize, dst_mcv, .{ .immediate = mask }); + + const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask }); + try self.genBinMathOpMir(.@"and", Type.usize, dst_mcv, .{ .register = tmp_reg }); break :result dst_mcv; }, From 608025456b164aae69c7e794b1419e6b03a5cb79 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 24 Mar 2022 17:01:00 +0100 Subject: [PATCH 0902/2031] x64: account for signed ints in struct_field_val when struct fits in a register --- src/arch/x86_64/CodeGen.zig | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index fe59cd6c63..fa83884329 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2899,6 +2899,22 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask }); try self.genBinMathOpMir(.@"and", Type.usize, dst_mcv, .{ .register = tmp_reg }); + const signedness: std.builtin.Signedness = blk: { + if (struct_field_ty.zigTypeTag() != .Int) break :blk .unsigned; + break :blk struct_field_ty.intInfo(self.target.*).signedness; + }; + const field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*)); + if (signedness == .signed and field_size < 8) { + _ = try self.addInst(.{ + .tag = .mov_sign_extend, + .ops = (Mir.Ops{ + .reg1 = dst_mcv.register, + .reg2 = registerAlias(dst_mcv.register, field_size), + }).encode(), + .data = undefined, + }); + } + break :result dst_mcv; }, else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), From 4ef26fc35562222263f564ca8d382ecd4b44c0a3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 24 Mar 2022 17:01:17 +0100 Subject: [PATCH 0903/2031] pass more behaviour tests --- test/behavior/array.zig | 1 - test/behavior/struct.zig | 2 -- test/behavior/tuple.zig | 2 -- 3 files changed, 5 deletions(-) diff --git a/test/behavior/array.zig b/test/behavior/array.zig index d7a87bed9c..13a4763a91 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -501,7 +501,6 @@ test "type coercion of anon struct literal to array" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const S = struct { const U = union { diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index f9a039208f..4bed1f56cc 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -301,7 +301,6 @@ test "void struct fields" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const foo = VoidStructFieldsFoo{ .a = void{}, @@ -1019,7 +1018,6 @@ test "struct with union field" { } test "type coercion of anon struct literal to struct" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index a51aa466d9..80eaea5072 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -4,7 +4,6 @@ const testing = std.testing; const expect = testing.expect; test "tuple concatenation" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -47,7 +46,6 @@ test "tuple multiplication" { } test "more tuple concatenation" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From 04523db6ae214e92f9c842436e66d5618d73d48f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 24 Mar 2022 18:54:17 +0100 Subject: [PATCH 0904/2031] x64: fix for 32bit builds --- src/arch/x86_64/CodeGen.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index fa83884329..1e79119997 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -5692,7 +5692,7 @@ fn airReduce(self: *Self, inst: Air.Inst.Index) !void { fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { const result_ty = self.air.typeOfIndex(inst); - const len = result_ty.arrayLen(); + const len = @intCast(usize, result_ty.arrayLen()); const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); const abi_size = @intCast(u32, result_ty.abiSize(self.target.*)); From b09280b48452be8b57b87bbf4eaacd430e6e3537 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 24 Mar 2022 12:20:53 -0700 Subject: [PATCH 0905/2031] move some files to the .github directory --- CODE_OF_CONDUCT.md => .github/CODE_OF_CONDUCT.md | 0 CONTRIBUTING.md => .github/CONTRIBUTING.md | 0 README.md | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) rename CODE_OF_CONDUCT.md => .github/CODE_OF_CONDUCT.md (100%) rename CONTRIBUTING.md => .github/CONTRIBUTING.md (100%) diff --git a/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md similarity index 100% rename from CODE_OF_CONDUCT.md rename to .github/CODE_OF_CONDUCT.md diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md diff --git a/README.md b/README.md index cd8b4b4e96..3ffd544e98 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,8 @@ A general-purpose programming language and toolchain for maintaining * [Download & Documentation](https://ziglang.org/download) * [Chapter 0 - Getting Started | ZigLearn.org](https://ziglearn.org/) * [Community](https://github.com/ziglang/zig/wiki/Community) - * [Contributing](https://github.com/ziglang/zig/blob/master/CONTRIBUTING.md) - * [Code of Conduct](https://github.com/ziglang/zig/blob/master/CODE_OF_CONDUCT.md) + * [Contributing](https://github.com/ziglang/zig/blob/master/.github/CONTRIBUTING.md) + * [Code of Conduct](https://github.com/ziglang/zig/blob/master/.github/CODE_OF_CONDUCT.md) * [Frequently Asked Questions](https://github.com/ziglang/zig/wiki/FAQ) * [Community Projects](https://github.com/ziglang/zig/wiki/Community-Projects) From f5f5b9373deae53a544497147cfd1380df34c000 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 24 Mar 2022 17:42:55 -0700 Subject: [PATCH 0906/2031] std.meta: fix unit tests depending on unstable behavior The unit tests of std.meta depended on `@typeInfo` for the same type returning a slice of declarations and fields with the same pointer address. This is not something guaranteed by the language specification. --- lib/std/meta.zig | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 4e03eb602b..33f690f7aa 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -592,11 +592,41 @@ pub fn FieldEnum(comptime T: type) type { fn expectEqualEnum(expected: anytype, actual: @TypeOf(expected)) !void { // TODO: https://github.com/ziglang/zig/issues/7419 // testing.expectEqual(@typeInfo(expected).Enum, @typeInfo(actual).Enum); - try testing.expectEqual(@typeInfo(expected).Enum.layout, @typeInfo(actual).Enum.layout); - try testing.expectEqual(@typeInfo(expected).Enum.tag_type, @typeInfo(actual).Enum.tag_type); - comptime try testing.expectEqualSlices(std.builtin.Type.EnumField, @typeInfo(expected).Enum.fields, @typeInfo(actual).Enum.fields); - comptime try testing.expectEqualSlices(std.builtin.Type.Declaration, @typeInfo(expected).Enum.decls, @typeInfo(actual).Enum.decls); - try testing.expectEqual(@typeInfo(expected).Enum.is_exhaustive, @typeInfo(actual).Enum.is_exhaustive); + try testing.expectEqual( + @typeInfo(expected).Enum.layout, + @typeInfo(actual).Enum.layout, + ); + try testing.expectEqual( + @typeInfo(expected).Enum.tag_type, + @typeInfo(actual).Enum.tag_type, + ); + // For comparing decls and fields, we cannot use the meta eql function here + // because the language does not guarantee that the slice pointers for field names + // and decl names will be the same. + comptime { + const expected_fields = @typeInfo(expected).Enum.fields; + const actual_fields = @typeInfo(actual).Enum.fields; + if (expected_fields.len != actual_fields.len) return error.FailedTest; + for (expected_fields) |expected_field, i| { + const actual_field = actual_fields[i]; + try testing.expectEqual(expected_field.value, actual_field.value); + try testing.expectEqualStrings(expected_field.name, actual_field.name); + } + } + comptime { + const expected_decls = @typeInfo(expected).Enum.decls; + const actual_decls = @typeInfo(actual).Enum.decls; + if (expected_decls.len != actual_decls.len) return error.FailedTest; + for (expected_decls) |expected_decl, i| { + const actual_decl = actual_decls[i]; + try testing.expectEqual(expected_decl.is_pub, actual_decl.is_pub); + try testing.expectEqualStrings(expected_decl.name, actual_decl.name); + } + } + try testing.expectEqual( + @typeInfo(expected).Enum.is_exhaustive, + @typeInfo(actual).Enum.is_exhaustive, + ); } test "std.meta.FieldEnum" { From 9a1d5001d4bf1f28bd0f23e8b936d677e0e5aac8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 24 Mar 2022 17:44:37 -0700 Subject: [PATCH 0907/2031] Sema: fix false negative detecting comptime const The code for detecting when a local const initialization expression ended up being comptime-known gave up when it encountered dbg_stmt instructions, but such instructions are not supposed to matter. --- src/Sema.zig | 50 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 277e4a6ba6..e8ef948abd 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2731,17 +2731,49 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com // instructions from the block, replacing the inst_map entry // corresponding to the ZIR alloc instruction with a constant // decl_ref pointing at our new Decl. + // dbg_stmt instructions may be interspersed into this pattern + // which must be ignored. if (block.instructions.items.len < 3) break :ct; - // zig fmt: off - const const_inst = block.instructions.items[block.instructions.items.len - 3]; - const bitcast_inst = block.instructions.items[block.instructions.items.len - 2]; - const store_inst = block.instructions.items[block.instructions.items.len - 1]; - const air_tags = sema.air_instructions.items(.tag); + var search_index: usize = block.instructions.items.len; + const air_tags = sema.air_instructions.items(.tag); const air_datas = sema.air_instructions.items(.data); - if (air_tags[const_inst] != .constant) break :ct; - if (air_tags[bitcast_inst] != .bitcast ) break :ct; - if (air_tags[store_inst] != .store ) break :ct; - // zig fmt: on + + const store_inst = while (true) { + if (search_index == 0) break :ct; + search_index -= 1; + + const candidate = block.instructions.items[search_index]; + switch (air_tags[candidate]) { + .dbg_stmt => continue, + .store => break candidate, + else => break :ct, + } + } else unreachable; // TODO shouldn't need this + + const bitcast_inst = while (true) { + if (search_index == 0) break :ct; + search_index -= 1; + + const candidate = block.instructions.items[search_index]; + switch (air_tags[candidate]) { + .dbg_stmt => continue, + .bitcast => break candidate, + else => break :ct, + } + } else unreachable; // TODO shouldn't need this + + const const_inst = while (true) { + if (search_index == 0) break :ct; + search_index -= 1; + + const candidate = block.instructions.items[search_index]; + switch (air_tags[candidate]) { + .dbg_stmt => continue, + .constant => break candidate, + else => break :ct, + } + } else unreachable; // TODO shouldn't need this + const store_op = air_datas[store_inst].bin_op; const store_val = (try sema.resolveMaybeUndefVal(block, src, store_op.rhs)) orelse break :ct; if (store_op.lhs != Air.indexToRef(bitcast_inst)) break :ct; From 5c68afef94b0b80823e033bd6965fcda74e19ebe Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 24 Mar 2022 17:45:34 -0700 Subject: [PATCH 0908/2031] AstGen: fix const locals with comptime initializations `const foo = comptime ...` generated invalid ZIR when the initialization expression contained an array literal because the validate_array_init_comptime instruction assumed that the corresponding alloc instruction was comptime. The solution is to look slightly ahead and notice that the initialization expression would be comptime-known and affect the alloc instruction tag accordingly. --- src/AstGen.zig | 9 ++++++--- test/behavior/eval.zig | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 4221f0cd14..aba4610c5d 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2671,6 +2671,9 @@ fn varDecl( return &sub_scope.base; } + const is_comptime = gz.force_comptime or + tree.nodes.items(.tag)[var_decl.ast.init_node] == .@"comptime"; + // Detect whether the initialization expression actually uses the // result location pointer. var init_scope = gz.makeSubBlock(scope); @@ -2692,7 +2695,7 @@ fn varDecl( .type_inst = type_inst, .align_inst = align_inst, .is_const = true, - .is_comptime = gz.force_comptime, + .is_comptime = is_comptime, }); init_scope.instructions_top = gz.instructions.items.len; } @@ -2700,7 +2703,7 @@ fn varDecl( } else { const alloc = if (align_inst == .none) alloc: { init_scope.instructions_top = gz.instructions.items.len; - const tag: Zir.Inst.Tag = if (gz.force_comptime) + const tag: Zir.Inst.Tag = if (is_comptime) .alloc_inferred_comptime else .alloc_inferred; @@ -2711,7 +2714,7 @@ fn varDecl( .type_inst = .none, .align_inst = align_inst, .is_const = true, - .is_comptime = gz.force_comptime, + .is_comptime = is_comptime, }); init_scope.instructions_top = gz.instructions.items.len; break :alloc ref; diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 2129512f96..0328111daa 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -859,3 +859,22 @@ test "debug variable type resolved through indirect zero-bit types" { const slice: []const T = &[_]T{}; _ = slice; } + +test "const local with comptime init through array init" { + const E1 = enum { + A, + fn a() void {} + }; + + const S = struct { + fn declarations(comptime T: type) []const std.builtin.Type.Declaration { + return @typeInfo(T).Enum.decls; + } + }; + + const decls = comptime [_][]const std.builtin.Type.Declaration{ + S.declarations(E1), + }; + + try comptime expect(decls[0][0].name[0] == 'a'); +} From 7cfa97aa4ee9eb49d97d7d3b88488387bf6d175f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 24 Mar 2022 18:08:10 -0700 Subject: [PATCH 0909/2031] std.meta.trait: remove assumption about vector ABI size The unit test for hasUniqueRepresentation asserted that a vector of length 3 would not have a unique representation. This would be true if it were lowered to ABI size 8 instead of 6. However lowering it to ABI size 6 is perfectly valid depending on the target. This commit also simplifies the logic for hasUniqueRepresentation of integers. --- lib/std/meta/trait.zig | 50 ++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/lib/std/meta/trait.zig b/lib/std/meta/trait.zig index faa87f189b..51cc3638fb 100644 --- a/lib/std/meta/trait.zig +++ b/lib/std/meta/trait.zig @@ -18,7 +18,7 @@ pub fn multiTrait(comptime traits: anytype) TraitFn { return Closure.trait; } -test "std.meta.trait.multiTrait" { +test "multiTrait" { const Vector2 = struct { const MyType = @This(); @@ -54,7 +54,7 @@ pub fn hasFn(comptime name: []const u8) TraitFn { return Closure.trait; } -test "std.meta.trait.hasFn" { +test "hasFn" { const TestStruct = struct { pub fn useless() void {} }; @@ -84,7 +84,7 @@ pub fn hasField(comptime name: []const u8) TraitFn { return Closure.trait; } -test "std.meta.trait.hasField" { +test "hasField" { const TestStruct = struct { value: u32, }; @@ -105,7 +105,7 @@ pub fn is(comptime id: std.builtin.TypeId) TraitFn { return Closure.trait; } -test "std.meta.trait.is" { +test "is" { try testing.expect(is(.Int)(u8)); try testing.expect(!is(.Int)(f32)); try testing.expect(is(.Pointer)(*u8)); @@ -123,7 +123,7 @@ pub fn isPtrTo(comptime id: std.builtin.TypeId) TraitFn { return Closure.trait; } -test "std.meta.trait.isPtrTo" { +test "isPtrTo" { try testing.expect(!isPtrTo(.Struct)(struct {})); try testing.expect(isPtrTo(.Struct)(*struct {})); try testing.expect(!isPtrTo(.Struct)(**struct {})); @@ -139,7 +139,7 @@ pub fn isSliceOf(comptime id: std.builtin.TypeId) TraitFn { return Closure.trait; } -test "std.meta.trait.isSliceOf" { +test "isSliceOf" { try testing.expect(!isSliceOf(.Struct)(struct {})); try testing.expect(isSliceOf(.Struct)([]struct {})); try testing.expect(!isSliceOf(.Struct)([][]struct {})); @@ -159,7 +159,7 @@ pub fn isExtern(comptime T: type) bool { }; } -test "std.meta.trait.isExtern" { +test "isExtern" { const TestExStruct = extern struct {}; const TestStruct = struct {}; @@ -177,7 +177,7 @@ pub fn isPacked(comptime T: type) bool { }; } -test "std.meta.trait.isPacked" { +test "isPacked" { const TestPStruct = packed struct {}; const TestStruct = struct {}; @@ -222,7 +222,7 @@ pub fn isSingleItemPtr(comptime T: type) bool { return false; } -test "std.meta.trait.isSingleItemPtr" { +test "isSingleItemPtr" { const array = [_]u8{0} ** 10; comptime try testing.expect(isSingleItemPtr(@TypeOf(&array[0]))); comptime try testing.expect(!isSingleItemPtr(@TypeOf(array))); @@ -237,7 +237,7 @@ pub fn isManyItemPtr(comptime T: type) bool { return false; } -test "std.meta.trait.isManyItemPtr" { +test "isManyItemPtr" { const array = [_]u8{0} ** 10; const mip = @ptrCast([*]const u8, &array[0]); try testing.expect(isManyItemPtr(@TypeOf(mip))); @@ -252,7 +252,7 @@ pub fn isSlice(comptime T: type) bool { return false; } -test "std.meta.trait.isSlice" { +test "isSlice" { const array = [_]u8{0} ** 10; var runtime_zero: usize = 0; try testing.expect(isSlice(@TypeOf(array[runtime_zero..]))); @@ -270,7 +270,7 @@ pub fn isIndexable(comptime T: type) bool { return comptime is(.Array)(T) or is(.Vector)(T) or isTuple(T); } -test "std.meta.trait.isIndexable" { +test "isIndexable" { const array = [_]u8{0} ** 10; const slice = @as([]const u8, &array); const vector: meta.Vector(2, u32) = [_]u32{0} ** 2; @@ -291,7 +291,7 @@ pub fn isNumber(comptime T: type) bool { }; } -test "std.meta.trait.isNumber" { +test "isNumber" { const NotANumber = struct { number: u8, }; @@ -342,7 +342,7 @@ pub fn isConstPtr(comptime T: type) bool { return @typeInfo(T).Pointer.is_const; } -test "std.meta.trait.isConstPtr" { +test "isConstPtr" { var t = @as(u8, 0); const c = @as(u8, 0); try testing.expect(isConstPtr(*const @TypeOf(t))); @@ -358,7 +358,7 @@ pub fn isContainer(comptime T: type) bool { }; } -test "std.meta.trait.isContainer" { +test "isContainer" { const TestStruct = struct {}; const TestUnion = union { a: void, @@ -380,7 +380,7 @@ pub fn isTuple(comptime T: type) bool { return is(.Struct)(T) and @typeInfo(T).Struct.is_tuple; } -test "std.meta.trait.isTuple" { +test "isTuple" { const t1 = struct {}; const t2 = .{ .a = 0 }; const t3 = .{ 1, 2, 3 }; @@ -429,7 +429,7 @@ pub fn isZigString(comptime T: type) bool { } } -test "std.meta.trait.isZigString" { +test "isZigString" { try testing.expect(isZigString([]const u8)); try testing.expect(isZigString([]u8)); try testing.expect(isZigString([:0]const u8)); @@ -475,7 +475,7 @@ pub fn hasDecls(comptime T: type, comptime names: anytype) bool { return true; } -test "std.meta.trait.hasDecls" { +test "hasDecls" { const TestStruct1 = struct {}; const TestStruct2 = struct { pub var a: u32 = undefined; @@ -501,7 +501,7 @@ pub fn hasFields(comptime T: type, comptime names: anytype) bool { return true; } -test "std.meta.trait.hasFields" { +test "hasFields" { const TestStruct1 = struct {}; const TestStruct2 = struct { a: u32, @@ -527,7 +527,7 @@ pub fn hasFunctions(comptime T: type, comptime names: anytype) bool { return true; } -test "std.meta.trait.hasFunctions" { +test "hasFunctions" { const TestStruct1 = struct {}; const TestStruct2 = struct { pub fn a() void {} @@ -557,9 +557,7 @@ pub fn hasUniqueRepresentation(comptime T: type) bool { .Bool => return false, - // The padding bits are undefined. - .Int => |info| return (info.bits % 8) == 0 and - (info.bits == 0 or std.math.isPowerOfTwo(info.bits)), + .Int => |info| return @sizeOf(T) * 8 == info.bits, .Pointer => |info| return info.size != .Slice, @@ -577,11 +575,12 @@ pub fn hasUniqueRepresentation(comptime T: type) bool { return @sizeOf(T) == sum_size; }, - .Vector => |info| return comptime hasUniqueRepresentation(info.child) and @sizeOf(T) == @sizeOf(info.child) * info.len, + .Vector => |info| return comptime hasUniqueRepresentation(info.child) and + @sizeOf(T) == @sizeOf(info.child) * info.len, } } -test "std.meta.trait.hasUniqueRepresentation" { +test "hasUniqueRepresentation" { const TestStruct1 = struct { a: u32, b: u32, @@ -650,5 +649,4 @@ test "std.meta.trait.hasUniqueRepresentation" { try testing.expect(!hasUniqueRepresentation([]const u8)); try testing.expect(hasUniqueRepresentation(@Vector(4, u16))); - try testing.expect(!hasUniqueRepresentation(@Vector(3, u16))); } From bb0e28a54ff60581c30156707eeeba1a6bd2b28f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 24 Mar 2022 19:36:36 -0700 Subject: [PATCH 0910/2031] Sema: fix generic function with void parameters This also fixes a bug that I didn't see causing any problems yet in generic function instantiation where it would read from a GetOrPutResult too late. Also it delays full resolution of generic function type parameters until after the function body is finished being analyzed. closes #11291 --- src/Sema.zig | 22 ++++++++++++++++++---- test/behavior/generics.zig | 13 +++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index e8ef948abd..03a51dc20d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5149,7 +5149,7 @@ fn instantiateGenericCall( .target = target, }; const gop = try mod.monomorphed_funcs.getOrPutContextAdapted(gpa, {}, adapter, .{ .target = target }); - if (!gop.found_existing) { + const callee = if (!gop.found_existing) callee: { const new_module_func = try gpa.create(Module.Fn); gop.key_ptr.* = new_module_func; errdefer gpa.destroy(new_module_func); @@ -5357,9 +5357,9 @@ fn instantiateGenericCall( try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func }); try new_decl.finalizeNewArena(&new_decl_arena); - } + break :callee new_func; + } else gop.key_ptr.*; - const callee = gop.key_ptr.*; const callee_inst = try sema.analyzeDeclVal(block, func_src, callee.owner_decl); // Make a runtime call to the new function, making sure to omit the comptime args. @@ -5397,8 +5397,8 @@ fn instantiateGenericCall( const param_ty = new_fn_info.param_types[runtime_i]; const arg_src = call_src; // TODO: better source location const uncasted_arg = uncasted_args[total_i]; - try sema.resolveTypeFully(block, arg_src, param_ty); const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src); + try sema.queueFullTypeResolution(param_ty); runtime_args[runtime_i] = casted_arg; runtime_i += 1; } @@ -6474,10 +6474,13 @@ fn zirParam( const err = err: { // Make sure any nested param instructions don't clobber our work. const prev_params = block.params; + const prev_preallocated_new_func = sema.preallocated_new_func; block.params = .{}; + sema.preallocated_new_func = null; defer { block.params.deinit(sema.gpa); block.params = prev_params; + sema.preallocated_new_func = prev_preallocated_new_func; } if (sema.resolveBody(block, body, inst)) |param_ty_inst| { @@ -6524,6 +6527,17 @@ fn zirParam( assert(sema.inst_map.remove(inst)); } + if (sema.preallocated_new_func != null) { + if (try sema.typeHasOnePossibleValue(block, src, param_ty)) |opv| { + // In this case we are instantiating a generic function call with a non-comptime + // non-anytype parameter that ended up being a one-possible-type. + // We don't want the parameter to be part of the instantiated function type. + const result = try sema.addConstant(param_ty, opv); + try sema.inst_map.put(sema.gpa, inst, result); + return; + } + } + try block.params.append(sema.gpa, .{ .ty = param_ty, .is_comptime = is_comptime, diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index 767c2f3ee3..867cf940b8 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -277,3 +277,16 @@ test "generic function instantiation turns into comptime call" { }; try S.doTheTest(); } + +test "generic function with void and comptime parameter" { + const S = struct { x: i32 }; + const namespace = struct { + fn foo(v: void, s: *S, comptime T: type) !void { + _ = @as(void, v); + try expect(s.x == 1234); + try expect(T == u8); + } + }; + var s: S = .{ .x = 1234 }; + try namespace.foo({}, &s, u8); +} From b802a67562cc912213ebfc6ef8a380c775c999fe Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 24 Mar 2022 19:55:12 -0700 Subject: [PATCH 0911/2031] std.MultiArrayList: check size of element, not pointer Ever since a semi-recent language specification update, pointers to zero-sized types still have runtime bits. --- lib/std/multi_array_list.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/std/multi_array_list.zig b/lib/std/multi_array_list.zig index 2af723d3a5..ae81b6c5e5 100644 --- a/lib/std/multi_array_list.zig +++ b/lib/std/multi_array_list.zig @@ -42,7 +42,10 @@ pub fn MultiArrayList(comptime S: type) type { return &[_]F{}; } const byte_ptr = self.ptrs[@enumToInt(field)]; - const casted_ptr: [*]F = if (@sizeOf([*]F) == 0) undefined else @ptrCast([*]F, @alignCast(@alignOf(F), byte_ptr)); + const casted_ptr: [*]F = if (@sizeOf(F) == 0) + undefined + else + @ptrCast([*]F, @alignCast(@alignOf(F), byte_ptr)); return casted_ptr[0..self.len]; } From bcf2eb1a003d076c166d4ce9cba20f6ed9b53887 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 24 Mar 2022 21:45:22 -0700 Subject: [PATCH 0912/2031] Sema: fix closure capture typeof runtime-known parameter Closures are not necessarily constant values. For example, Zig code might do something like this: fn foo(x: anytype) void { const S = struct {field: @TypeOf(x)}; } ...in which case the closure_capture instruction has access to a runtime value only. In such case we preserve the type and use a dummy runtime value. closes #11292 --- src/Sema.zig | 15 ++++++++++++--- test/behavior/eval.zig | 15 +++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 03a51dc20d..a42a4caf38 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -10483,10 +10483,19 @@ fn zirClosureCapture( ) CompileError!void { // TODO: Compile error when closed over values are modified const inst_data = sema.code.instructions.items(.data)[inst].un_tok; - const tv = try sema.resolveInstConst(block, inst_data.src(), inst_data.operand); + const src = inst_data.src(); + // Closures are not necessarily constant values. For example, the + // code might do something like this: + // fn foo(x: anytype) void { const S = struct {field: @TypeOf(x)}; } + // ...in which case the closure_capture instruction has access to a runtime + // value only. In such case we preserve the type and use a dummy runtime value. + const operand = sema.resolveInst(inst_data.operand); + const val = (try sema.resolveMaybeUndefValAllowVariables(block, src, operand)) orelse + Value.initTag(.generic_poison); + try block.wip_capture_scope.captures.putNoClobber(sema.gpa, inst, .{ - .ty = try tv.ty.copy(sema.perm_arena), - .val = try tv.val.copy(sema.perm_arena), + .ty = try sema.typeOf(operand).copy(sema.perm_arena), + .val = try val.copy(sema.perm_arena), }); } diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 0328111daa..e3024a3895 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -878,3 +878,18 @@ test "const local with comptime init through array init" { try comptime expect(decls[0][0].name[0] == 'a'); } + +test "closure capture type of runtime-known parameter" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn b(c: anytype) !void { + const D = struct { c: @TypeOf(c) }; + var d = D{ .c = c }; + try expect(d.c == 1234); + } + }; + var c: i32 = 1234; + try S.b(c); +} From 7f91be9c80f8d79e4a2c6cf0b15197cdd454cef6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 24 Mar 2022 22:45:10 -0700 Subject: [PATCH 0913/2031] AstGen: emit break_inline from inline while loop --- src/AstGen.zig | 4 ++-- test/behavior/while.zig | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index aba4610c5d..e0f4028cba 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1878,8 +1878,8 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) continue; } - // TODO emit a break_inline if the loop being continued is inline - _ = try parent_gz.addBreak(.@"break", continue_block, .void_value); + const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline) .break_inline else .@"break"; + _ = try parent_gz.addBreak(break_tag, continue_block, .void_value); return Zir.Inst.Ref.unreachable_value; }, .local_val => scope = scope.cast(Scope.LocalVal).?.parent, diff --git a/test/behavior/while.zig b/test/behavior/while.zig index 5bbcb153c5..71f1d253e9 100644 --- a/test/behavior/while.zig +++ b/test/behavior/while.zig @@ -1,6 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); const expect = std.testing.expect; +const assert = std.debug.assert; test "while loop" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -326,3 +327,12 @@ test "while error 2 break statements and an else" { try S.entry(true, false); comptime try S.entry(true, false); } + +test "continue inline while loop" { + comptime var i = 0; + inline while (i < 10) : (i += 1) { + if (i < 5) continue; + break; + } + comptime assert(i == 5); +} From 9a127501f68d6034367094915ad53e8d57495786 Mon Sep 17 00:00:00 2001 From: dxps Date: Mon, 21 Mar 2022 22:31:41 +0200 Subject: [PATCH 0914/2031] chore: doc fixes, closes 11091 --- doc/langref.html.in | 426 ++++++++++++++++++++++---------------------- 1 file changed, 213 insertions(+), 213 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index a7e8058a25..578f0297ae 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -52,255 +52,255 @@ #contents-wrapper { margin-left: calc(var(--nav-width) + var(--nav-margin-l)); } - } - - a:hover,a:focus { - background: #fff2a8; - } - dt { - font-weight: bold; - } - table, th, td { - border-collapse: collapse; - border: 1px solid grey; - } - th, td { - padding: 0.1em; - } - th[scope=row] { - text-align: left; - font-weight: normal; - } - .t0_1, .t37, .t37_1 { - font-weight: bold; - } - .t2_0 { - color: #575757; - } - .t31_1 { - color: #b40000; - } - .t32_1 { - color: green; - } - .t36_1 { - color: #005C7A; - } - .file { - font-weight: bold; - border: unset; - } - code { - background: #f8f8f8; - border: 1px dotted silver; - padding-left: 0.3em; - padding-right: 0.3em; - } - pre > code { - display: block; - overflow: auto; - padding: 0.5em; - border: 1px solid #eee; - line-height: normal; - } - samp { - background: #fafafa; - } - pre > samp { - display: block; - overflow: auto; - padding: 0.5em; - border: 1px solid #eee; - line-height: normal; - } - kbd { - font-weight: bold; - } - .table-wrapper { - width: 100%; - overflow-x: auto; - } - - .tok-kw { - color: #333; - font-weight: bold; - } - .tok-str { - color: #d14; - } - .tok-builtin { - color: #005C7A; - } - .tok-comment { - color: #545454; - font-style: italic; - } - .tok-fn { - color: #900; - font-weight: bold; - } - .tok-null { - color: #005C5C; - } - .tok-number { - color: #005C5C; - } - .tok-type { - color: #458; - font-weight: bold; - } - - figure { - margin: auto 0; - } - figure pre { - margin-top: 0; - } - - figcaption { - padding-left: 0.5em; - font-size: small; - border-top-left-radius: 5px; - border-top-right-radius: 5px; - } - figcaption.zig-cap { - background: #fcdba5; - } - figcaption.c-cap { - background: #a8b9cc; - color: #000; - } - figcaption.peg-cap { - background: #fcdba5; - } - figcaption.javascript-cap { - background: #365d95; - color: #fff; - } - figcaption.shell-cap { - background: #ccc; - color: #000; - } - - aside { - border-left: 0.25em solid #f7a41d; - padding: 0 1em 0 1em; - } - - h1 a, h2 a, h3 a, h4 a, h5 a { - text-decoration: none; - color: #333; - } - - a.hdr { - visibility: hidden; - } - h1:hover > a.hdr, h2:hover > a.hdr, h3:hover > a.hdr, h4:hover > a.hdr, h5:hover > a.hdr { - visibility: visible; - } - - pre { - counter-reset: line; - } - pre .line:before { - counter-increment: line; - content: counter(line); - display: inline-block; - padding-right: 1em; - width: 2em; - text-align: right; - color: #999; - } - th pre code { - background: none; - } - th .line:before { - display: none; - } - - @media (prefers-color-scheme: dark) { - body{ - background:#121212; - color: #ccc; - } - a { - color: #88f; } + a:hover,a:focus { - color: #000; + background: #fff2a8; + } + dt { + font-weight: bold; } table, th, td { - border-color: grey; + border-collapse: collapse; + border: 1px solid grey; + } + th, td { + padding: 0.1em; + } + th[scope=row] { + text-align: left; + font-weight: normal; + } + .t0_1, .t37, .t37_1 { + font-weight: bold; } .t2_0 { - color: grey; + color: #575757; } .t31_1 { - color: red; + color: #b40000; } .t32_1 { - color: #00B800; + color: green; } .t36_1 { - color: #0086b3; + color: #005C7A; } - code { - background: #222; - border-color: #444; - } - pre > code { - color: #ccc; - background: #222; - border: unset; - } - samp { - background: #000; - color: #ccc; - } - pre > samp { + .file { + font-weight: bold; border: unset; } + code { + background: #f8f8f8; + border: 1px dotted silver; + padding-left: 0.3em; + padding-right: 0.3em; + } + pre > code { + display: block; + overflow: auto; + padding: 0.5em; + border: 1px solid #eee; + line-height: normal; + } + samp { + background: #fafafa; + } + pre > samp { + display: block; + overflow: auto; + padding: 0.5em; + border: 1px solid #eee; + line-height: normal; + } + kbd { + font-weight: bold; + } + .table-wrapper { + width: 100%; + overflow-x: auto; + } + .tok-kw { - color: #eee; + color: #333; + font-weight: bold; } .tok-str { - color: #2e5; + color: #d14; } .tok-builtin { - color: #ff894c; + color: #005C7A; } .tok-comment { - color: #aa7; + color: #545454; + font-style: italic; } .tok-fn { - color: #B1A0F8; + color: #900; + font-weight: bold; } .tok-null { - color: #ff8080; + color: #005C5C; } .tok-number { - color: #ff8080; + color: #005C5C; } .tok-type { - color: #68f; + color: #458; + font-weight: bold; } - h1 a, h2 a, h3 a, h4 a, h5 a { - color: #aaa; + + figure { + margin: auto 0; + } + figure pre { + margin-top: 0; + } + + figcaption { + padding-left: 0.5em; + font-size: small; + border-top-left-radius: 5px; + border-top-right-radius: 5px; } figcaption.zig-cap { - background-color: #b27306; - color: #000; + background: #fcdba5; + } + figcaption.c-cap { + background: #a8b9cc; + color: #000; } figcaption.peg-cap { - background-color: #b27306; - color: #000; + background: #fcdba5; } - figcaption.shell-cap { - background: #2a2a2a; + figcaption.javascript-cap { + background: #365d95; color: #fff; } - } - + figcaption.shell-cap { + background: #ccc; + color: #000; + } + + aside { + border-left: 0.25em solid #f7a41d; + padding: 0 1em 0 1em; + } + + h1 a, h2 a, h3 a, h4 a, h5 a { + text-decoration: none; + color: #333; + } + + a.hdr { + visibility: hidden; + } + h1:hover > a.hdr, h2:hover > a.hdr, h3:hover > a.hdr, h4:hover > a.hdr, h5:hover > a.hdr { + visibility: visible; + } + + pre { + counter-reset: line; + } + pre .line:before { + counter-increment: line; + content: counter(line); + display: inline-block; + padding-right: 1em; + width: 2em; + text-align: right; + color: #999; + } + th pre code { + background: none; + } + th .line:before { + display: none; + } + + @media (prefers-color-scheme: dark) { + body{ + background:#121212; + color: #ccc; + } + a { + color: #88f; + } + a:hover,a:focus { + color: #000; + } + table, th, td { + border-color: grey; + } + .t2_0 { + color: grey; + } + .t31_1 { + color: red; + } + .t32_1 { + color: #00B800; + } + .t36_1 { + color: #0086b3; + } + code { + background: #222; + border-color: #444; + } + pre > code { + color: #ccc; + background: #222; + border: unset; + } + samp { + background: #000; + color: #ccc; + } + pre > samp { + border: unset; + } + .tok-kw { + color: #eee; + } + .tok-str { + color: #2e5; + } + .tok-builtin { + color: #ff894c; + } + .tok-comment { + color: #aa7; + } + .tok-fn { + color: #B1A0F8; + } + .tok-null { + color: #ff8080; + } + .tok-number { + color: #ff8080; + } + .tok-type { + color: #68f; + } + h1 a, h2 a, h3 a, h4 a, h5 a { + color: #aaa; + } + figcaption.zig-cap { + background-color: #b27306; + color: #000; + } + figcaption.peg-cap { + background-color: #b27306; + color: #000; + } + figcaption.shell-cap { + background: #2a2a2a; + color: #fff; + } + } +

    Zig Language Reference

    @@ -535,8 +535,8 @@ const Timestamp = struct { {#header_close#} {#header_open|Top-Level Doc Comments#}

    User documentation that doesn't belong to whatever - immediately follows it, like package-level documentation, goes - in top-level doc comments. A top-level doc comment is one that + immediately follows it, like container level documentation, goes + in top level doc comments. A top level doc comment is one that begins with two slashes and an exclamation point: {#syntax#}//!{#endsyntax#}.

    {#code_begin|syntax|tldoc_comments#} From 0d5d353197114c614f8efde4a8c11c61a5f82519 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Thu, 24 Mar 2022 23:41:11 -0700 Subject: [PATCH 0915/2031] translate-c: Use @Vector for vector expressions Removes translate-c usage of std.meta.Vector, which was deprecated in d42d31f72f --- src/translate_c.zig | 4 ++-- src/translate_c/ast.zig | 15 +++++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 8b79dd53ab..39733dd246 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1441,7 +1441,7 @@ fn makeShuffleMask(c: *Context, scope: *Scope, expr: *const clang.ShuffleVectorE assert(num_subexprs >= 3); // two source vectors + at least 1 index expression const mask_len = num_subexprs - 2; - const mask_type = try Tag.std_meta_vector.create(c.arena, .{ + const mask_type = try Tag.vector.create(c.arena, .{ .lhs = try transCreateNodeNumber(c, mask_len, .int), .rhs = try Tag.type.create(c.arena, "i32"), }); @@ -4800,7 +4800,7 @@ fn transType(c: *Context, scope: *Scope, ty: *const clang.Type, source_loc: clan const vector_ty = @ptrCast(*const clang.VectorType, ty); const num_elements = vector_ty.getNumElements(); const element_qt = vector_ty.getElementType(); - return Tag.std_meta_vector.create(c.arena, .{ + return Tag.vector.create(c.arena, .{ .lhs = try transCreateNodeNumber(c, num_elements, .int), .rhs = try transQualType(c, scope, element_qt, source_loc), }); diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 6351f67214..66c2406187 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -192,8 +192,8 @@ pub const Node = extern union { helpers_shuffle_vector_index, /// @import("std").zig.c_translation.Macro. helpers_macro, - /// @import("std").meta.Vector(lhs, rhs) - std_meta_vector, + /// @Vector(lhs, rhs) + vector, /// @import("std").mem.zeroes(operand) std_mem_zeroes, /// @import("std").mem.zeroInit(lhs, rhs) @@ -322,7 +322,7 @@ pub const Node = extern union { .std_mem_zeroinit, .helpers_flexible_array_type, .helpers_shuffle_vector_index, - .std_meta_vector, + .vector, .ptr_cast, .div_exact, .offset_of, @@ -899,10 +899,9 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { const import_node = try renderStdImport(c, &.{ "zig", "c_translation", "shuffleVectorIndex" }); return renderCall(c, import_node, &.{ payload.lhs, payload.rhs }); }, - .std_meta_vector => { - const payload = node.castTag(.std_meta_vector).?.data; - const import_node = try renderStdImport(c, &.{ "meta", "Vector" }); - return renderCall(c, import_node, &.{ payload.lhs, payload.rhs }); + .vector => { + const payload = node.castTag(.vector).?.data; + return renderBuiltinCall(c, "@Vector", &.{ payload.lhs, payload.rhs }); }, .call => { const payload = node.castTag(.call).?.data; @@ -2221,7 +2220,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .typeof, .typeinfo, .std_meta_alignment, - .std_meta_vector, + .vector, .helpers_sizeof, .helpers_cast, .helpers_promoteIntLiteral, From 1c33ea2c35e9260babedb116ad527256e0a4ef5e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 24 Mar 2022 23:01:46 +0100 Subject: [PATCH 0916/2031] dwarf: add debug info for unions --- src/link/Dwarf.zig | 131 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 126 insertions(+), 5 deletions(-) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 5e1af101de..370aac2734 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -83,8 +83,9 @@ pub const abbrev_struct_type = 6; pub const abbrev_struct_member = 7; pub const abbrev_enum_type = 8; pub const abbrev_enum_variant = 9; -pub const abbrev_pad1 = 10; -pub const abbrev_parameter = 11; +pub const abbrev_union_type = 10; +pub const abbrev_pad1 = 11; +pub const abbrev_parameter = 12; /// The reloc offset for the virtual address of a function in its Line Number Program. /// Size is a virtual address integer. @@ -452,6 +453,8 @@ pub fn commitDeclDebugInfo( var dbg_type_arena = std.heap.ArenaAllocator.init(gpa); defer dbg_type_arena.deinit(); + var nested_ref4_relocs = std.ArrayList(u32).init(gpa); + defer nested_ref4_relocs.deinit(); { // Now we emit the .debug_info types of the Decl. These will count towards the size of // the buffer, so we have to do it before computing the offset, and we can't perform the actual @@ -463,7 +466,13 @@ pub fn commitDeclDebugInfo( .target = self.target, }).?; value_ptr.off = @intCast(u32, dbg_info_buffer.items.len); - try self.addDbgInfoType(dbg_type_arena.allocator(), ty, dbg_info_buffer, dbg_info_type_relocs); + try self.addDbgInfoType( + dbg_type_arena.allocator(), + ty, + dbg_info_buffer, + dbg_info_type_relocs, + &nested_ref4_relocs, + ); } } @@ -478,13 +487,27 @@ pub fn commitDeclDebugInfo( // Now that we have the offset assigned we can finally perform type relocations. for (dbg_info_type_relocs.values()) |value| { for (value.relocs.items) |off| { - mem.writeIntLittle( + mem.writeInt( u32, dbg_info_buffer.items[off..][0..4], atom.off + value.off, + target_endian, ); } } + // Offsets to positions with known a priori relative displacement values. + // Here, we just need to add the offset of the atom to the read value in the + // relocated cell. + // TODO Should probably generalise this with type relocs. + for (nested_ref4_relocs.items) |off| { + const addend = mem.readInt(u32, dbg_info_buffer.items[off..][0..4], target_endian); + mem.writeInt( + u32, + dbg_info_buffer.items[off..][0..4], + atom.off + addend, + target_endian, + ); + } } try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items); @@ -751,8 +774,10 @@ fn addDbgInfoType( ty: Type, dbg_info_buffer: *std.ArrayList(u8), dbg_info_type_relocs: *File.DbgInfoTypeRelocsTable, + nested_ref4_relocs: *std.ArrayList(u32), ) error{OutOfMemory}!void { const target = self.target; + const target_endian = self.target.cpu.arch.endian(); var relocs = std.ArrayList(struct { ty: Type, reloc: u32 }).init(arena); switch (ty.zigTypeTag()) { @@ -962,7 +987,6 @@ fn addDbgInfoType( .enum_numbered => ty.castTag(.enum_numbered).?.data.values, else => unreachable, }; - const target_endian = self.target.cpu.arch.endian(); for (fields.keys()) |field_name, field_i| { // DW.AT.enumerator try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2 + @sizeOf(u64)); @@ -983,6 +1007,94 @@ fn addDbgInfoType( // DW.AT.enumeration_type delimit children try dbg_info_buffer.append(0); }, + .Union => { + const layout = ty.unionGetLayout(target); + const union_obj = ty.cast(Type.Payload.Union).?.data; + const payload_offset = if (layout.tag_align >= layout.payload_align) layout.tag_size else 0; + const tag_offset = if (layout.tag_align >= layout.payload_align) 0 else layout.payload_size; + const is_tagged = layout.tag_size > 0; + const union_name = try ty.nameAllocArena(arena, target); + + // TODO this is temporary to match current state of unions in Zig - we don't yet have + // safety checks implemented meaning the implicit tag is not yet stored and generated + // for untagged unions. + if (is_tagged) { + // DW.AT.structure_type + try dbg_info_buffer.append(abbrev_struct_type); + // DW.AT.byte_size, DW.FORM.sdata + try leb128.writeULEB128(dbg_info_buffer.writer(), layout.abi_size); + // DW.AT.name, DW.FORM.string + try dbg_info_buffer.ensureUnusedCapacity(union_name.len + 1); + dbg_info_buffer.appendSliceAssumeCapacity(union_name); + dbg_info_buffer.appendAssumeCapacity(0); + + // DW.AT.member + try dbg_info_buffer.ensureUnusedCapacity(9); + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity("payload"); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + const inner_union_index = dbg_info_buffer.items.len; + try dbg_info_buffer.ensureUnusedCapacity(4); + mem.writeInt( + u32, + dbg_info_buffer.addManyAsArrayAssumeCapacity(4), + @intCast(u32, inner_union_index + 5), + target_endian, + ); + try nested_ref4_relocs.append(@intCast(u32, inner_union_index)); + // DW.AT.data_member_location, DW.FORM.sdata + try leb128.writeULEB128(dbg_info_buffer.writer(), payload_offset); + } + + // DW.AT.union_type + try dbg_info_buffer.append(abbrev_union_type); + // DW.AT.byte_size, DW.FORM.sdata, + try leb128.writeULEB128(dbg_info_buffer.writer(), layout.payload_size); + // DW.AT.name, DW.FORM.string + if (is_tagged) { + try dbg_info_buffer.writer().print("AnonUnion\x00", .{}); + } else { + try dbg_info_buffer.writer().print("{s}\x00", .{union_name}); + } + + const fields = ty.unionFields(); + for (fields.keys()) |field_name| { + const field = fields.get(field_name).?; + if (!field.ty.hasRuntimeBits()) continue; + // DW.AT.member + try dbg_info_buffer.append(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + try dbg_info_buffer.writer().print("{s}\x00", .{field_name}); + // DW.AT.type, DW.FORM.ref4 + const index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try relocs.append(.{ .ty = field.ty, .reloc = @intCast(u32, index) }); + // DW.AT.data_member_location, DW.FORM.sdata + try dbg_info_buffer.append(0); + } + // DW.AT.union_type delimit children + try dbg_info_buffer.append(0); + + if (is_tagged) { + // DW.AT.member + try dbg_info_buffer.ensureUnusedCapacity(5); + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity("tag"); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + const index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try relocs.append(.{ .ty = union_obj.tag_ty, .reloc = @intCast(u32, index) }); + // DW.AT.data_member_location, DW.FORM.sdata + try leb128.writeULEB128(dbg_info_buffer.writer(), tag_offset); + + // DW.AT.structure_type delimit children + try dbg_info_buffer.append(0); + } + }, else => { log.debug("TODO implement .debug_info for type '{}'", .{ty.fmtDebug()}); try dbg_info_buffer.append(abbrev_pad1); @@ -1089,6 +1201,15 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.data8, 0, 0, // table sentinel + abbrev_union_type, + DW.TAG.union_type, + DW.CHILDREN.yes, // header + DW.AT.byte_size, + DW.FORM.sdata, + DW.AT.name, + DW.FORM.string, + 0, + 0, // table sentinel abbrev_pad1, DW.TAG.unspecified_type, DW.CHILDREN.no, // header From 12d5efcbe621b98b5a99c5f84a9d5b605e4acc40 Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Wed, 23 Mar 2022 21:41:35 +0100 Subject: [PATCH 0917/2031] stage2: implement `@select` --- src/Air.zig | 12 ++++- src/Liveness.zig | 4 ++ src/Sema.zig | 101 ++++++++++++++++++++++++++++++++++- src/arch/aarch64/CodeGen.zig | 8 +++ src/arch/arm/CodeGen.zig | 8 +++ src/arch/riscv64/CodeGen.zig | 8 +++ src/arch/wasm/CodeGen.zig | 11 ++++ src/arch/x86_64/CodeGen.zig | 8 +++ src/codegen/c.zig | 16 ++++++ src/codegen/llvm.zig | 13 +++++ src/print_air.zig | 13 +++++ test/behavior/select.zig | 42 ++++++++------- 12 files changed, 223 insertions(+), 21 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 404ee8f9b7..4938cb8135 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -344,7 +344,7 @@ pub const Inst = struct { /// to the storage for the variable. The local may be a const or a var. /// Result type is always void. /// Uses `pl_op`. The payload index is the variable name. It points to the extra - /// array, reinterpreting the bytes there as a null-terminated string. + /// array, reinterpreting the bytes there as a null-terminated string. dbg_var_ptr, /// Same as `dbg_var_ptr` except the local is a const, not a var, and the /// operand is the local's value. @@ -553,6 +553,9 @@ pub const Inst = struct { /// Constructs a vector by selecting elements from `a` and `b` based on `mask`. /// Uses the `ty_pl` field with payload `Shuffle`. shuffle, + /// Constructs a vector element-wise from `a` or `b` based on `pred`. + /// Uses the `ty_pl` field with payload `Select`. + select, /// Given dest ptr, value, and len, set all elements at dest to value. /// Result type is always void. @@ -785,6 +788,12 @@ pub const Shuffle = struct { mask_len: u32, }; +pub const Select = struct { + pred: Inst.Ref, + a: Inst.Ref, + b: Inst.Ref, +}; + pub const VectorCmp = struct { lhs: Inst.Ref, rhs: Inst.Ref, @@ -956,6 +965,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .cmpxchg_weak, .cmpxchg_strong, .slice, + .select, .shuffle, .aggregate_init, .union_init, diff --git a/src/Liveness.zig b/src/Liveness.zig index 79521e7a94..9e15567d73 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -433,6 +433,10 @@ fn analyzeInst( } return extra_tombs.finish(); }, + .select => { + const extra = a.air.extraData(Air.Select, inst_datas[inst].ty_pl.payload).data; + return trackOperands(a, new_set, inst, main_tomb, .{ extra.pred, extra.a, extra.b }); + }, .shuffle => { const extra = a.air.extraData(Air.Shuffle, inst_datas[inst].ty_pl.payload).data; return trackOperands(a, new_set, inst, main_tomb, .{ extra.a, extra.b, .none }); diff --git a/src/Sema.zig b/src/Sema.zig index a42a4caf38..e4d719f346 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14804,8 +14804,105 @@ fn analyzeShuffle( fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - return sema.fail(block, src, "TODO: Sema.zirSelect", .{}); + const extra = sema.code.extraData(Zir.Inst.Select, inst_data.payload_index).data; + + const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; + const pred_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; + const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; + const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; + + const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); + try sema.checkVectorElemType(block, elem_ty_src, elem_ty); + const pred = sema.resolveInst(extra.pred); + const a = sema.resolveInst(extra.a); + const b = sema.resolveInst(extra.b); + const target = sema.mod.getTarget(); + + const pred_ty = sema.typeOf(pred); + switch (try pred_ty.zigTypeTagOrPoison()) { + .Vector => { + const scalar_ty = pred_ty.childType(); + if (!scalar_ty.eql(Type.bool, target)) { + const bool_vec_ty = try Type.vector(sema.arena, pred_ty.vectorLen(), Type.bool); + return sema.fail(block, pred_src, "Expected '{}', found '{}'", .{ bool_vec_ty.fmt(target), pred_ty.fmt(target) }); + } + }, + else => return sema.fail(block, pred_src, "Expected vector type, found '{}'", .{pred_ty.fmt(target)}), + } + + const vec_len = pred_ty.vectorLen(); + const vec_ty = try Type.vector(sema.arena, vec_len, elem_ty); + + const a_ty = sema.typeOf(a); + if (!a_ty.eql(vec_ty, target)) { + return sema.fail(block, a_src, "Expected '{}', found '{}'", .{ vec_ty.fmt(target), a_ty.fmt(target) }); + } + + const b_ty = sema.typeOf(b); + if (!b_ty.eql(vec_ty, target)) { + return sema.fail(block, b_src, "Expected '{}', found '{}'", .{ vec_ty.fmt(target), b_ty.fmt(target) }); + } + + const maybe_pred = try sema.resolveMaybeUndefVal(block, pred_src, pred); + const maybe_a = try sema.resolveMaybeUndefVal(block, a_src, a); + const maybe_b = try sema.resolveMaybeUndefVal(block, b_src, b); + + const runtime_src = if (maybe_pred) |pred_val| rs: { + if (pred_val.isUndef()) return sema.addConstUndef(vec_ty); + + if (maybe_a) |a_val| { + if (a_val.isUndef()) return sema.addConstUndef(vec_ty); + + if (maybe_b) |b_val| { + if (b_val.isUndef()) return sema.addConstUndef(vec_ty); + + var buf: Value.ElemValueBuffer = undefined; + const elems = try sema.gpa.alloc(Value, vec_len); + for (elems) |*elem, i| { + const pred_elem_val = pred_val.elemValueBuffer(i, &buf); + const should_choose_a = pred_elem_val.toBool(); + if (should_choose_a) { + elem.* = a_val.elemValueBuffer(i, &buf); + } else { + elem.* = b_val.elemValueBuffer(i, &buf); + } + } + + return sema.addConstant( + vec_ty, + try Value.Tag.aggregate.create(sema.arena, elems), + ); + } else { + break :rs b_src; + } + } else { + if (maybe_b) |b_val| { + if (b_val.isUndef()) return sema.addConstUndef(vec_ty); + } + break :rs a_src; + } + } else rs: { + if (maybe_a) |a_val| { + if (a_val.isUndef()) return sema.addConstUndef(vec_ty); + } + if (maybe_b) |b_val| { + if (b_val.isUndef()) return sema.addConstUndef(vec_ty); + } + break :rs pred_src; + }; + + try sema.requireRuntimeBlock(block, runtime_src); + return block.addInst(.{ + .tag = .select, + .data = .{ .ty_pl = .{ + .ty = try block.sema.addType(vec_ty), + .payload = try block.sema.addExtra(Air.Select{ + .pred = pred, + .a = a, + .b = b, + }), + } }, + }); } fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 8e502e8943..178dfe2c29 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -640,6 +640,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .tag_name => try self.airTagName(inst), .error_name => try self.airErrorName(inst), .splat => try self.airSplat(inst), + .select => try self.airSelect(inst), .shuffle => try self.airShuffle(inst), .reduce => try self.airReduce(inst), .aggregate_init => try self.airAggregateInit(inst), @@ -3746,6 +3747,13 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airSelect(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Select, ty_pl.payload).data; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ extra.pred, extra.a, extra.b }); +} + fn airShuffle(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 return self.fail("TODO implement airShuffle for {}", .{self.target.cpu.arch}); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 5bbacb8999..7fa116edd1 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -630,6 +630,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .tag_name => try self.airTagName(inst), .error_name => try self.airErrorName(inst), .splat => try self.airSplat(inst), + .select => try self.airSelect(inst), .shuffle => try self.airShuffle(inst), .reduce => try self.airReduce(inst), .aggregate_init => try self.airAggregateInit(inst), @@ -4323,6 +4324,13 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airSelect(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Select, ty_pl.payload).data; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for arm", .{}); + return self.finishAir(inst, result, .{ extra.pred, extra.a, extra.b }); +} + fn airShuffle(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 return self.fail("TODO implement airShuffle for arm", .{}); diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 8cbd26b7a6..65b3b4904d 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -600,6 +600,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .tag_name => try self.airTagName(inst), .error_name => try self.airErrorName(inst), .splat => try self.airSplat(inst), + .select => try self.airSelect(inst), .shuffle => try self.airShuffle(inst), .reduce => try self.airReduce(inst), .aggregate_init => try self.airAggregateInit(inst), @@ -2396,6 +2397,13 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airSelect(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Select, ty_pl.payload).data; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for riscv64", .{}); + return self.finishAir(inst, result, .{ extra.pred, extra.a, extra.b }); +} + fn airShuffle(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 return self.fail("TODO implement airShuffle for riscv64", .{}); diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index f2979d96b1..de5e698eca 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1371,6 +1371,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .ret_ptr => self.airRetPtr(inst), .ret_load => self.airRetLoad(inst), .splat => self.airSplat(inst), + .select => self.airSelect(inst), .shuffle => self.airShuffle(inst), .reduce => self.airReduce(inst), .aggregate_init => self.airAggregateInit(inst), @@ -3265,6 +3266,16 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return self.fail("TODO: Implement wasm airSplat", .{}); } +fn airSelect(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const ty = try self.resolveInst(ty_pl.ty); + + _ = ty; + return self.fail("TODO: Implement wasm airSelect", .{}); +} + fn airShuffle(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 1e79119997..8a9aaebb22 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -721,6 +721,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .tag_name => try self.airTagName(inst), .error_name => try self.airErrorName(inst), .splat => try self.airSplat(inst), + .select => try self.airSelect(inst), .shuffle => try self.airShuffle(inst), .reduce => try self.airReduce(inst), .aggregate_init => try self.airAggregateInit(inst), @@ -5678,6 +5679,13 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airSelect(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Select, ty_pl.payload).data; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for x86_64", .{}); + return self.finishAir(inst, result, .{ extra.pred, extra.a, extra.b }); +} + fn airShuffle(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 return self.fail("TODO implement airShuffle for x86_64", .{}); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 16558c6e04..84bb3a211b 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1825,6 +1825,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .tag_name => try airTagName(f, inst), .error_name => try airErrorName(f, inst), .splat => try airSplat(f, inst), + .select => try airSelect(f, inst), .shuffle => try airShuffle(f, inst), .reduce => try airReduce(f, inst), .aggregate_init => try airAggregateInit(f, inst), @@ -3794,6 +3795,21 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { return f.fail("TODO: C backend: implement airSplat", .{}); } +fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue { + if (f.liveness.isUnused(inst)) return CValue.none; + + const inst_ty = f.air.typeOfIndex(inst); + const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; + + const writer = f.object.writer(); + const local = try f.allocLocal(inst_ty, .Const); + try writer.writeAll(" = "); + + _ = local; + _ = ty_pl; + return f.fail("TODO: C backend: implement airSelect", .{}); +} + fn airShuffle(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 c8ca540f59..0f481e0a6d 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3444,6 +3444,7 @@ pub const FuncGen = struct { .tag_name => try self.airTagName(inst), .error_name => try self.airErrorName(inst), .splat => try self.airSplat(inst), + .select => try self.airSelect(inst), .shuffle => try self.airShuffle(inst), .reduce => try self.airReduce(inst), .aggregate_init => try self.airAggregateInit(inst), @@ -6355,6 +6356,18 @@ pub const FuncGen = struct { return self.builder.buildShuffleVector(op_vector, undef_vector, mask_llvm_ty.constNull(), ""); } + fn airSelect(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 extra = self.air.extraData(Air.Select, ty_pl.payload).data; + const pred = try self.resolveInst(extra.pred); + const a = try self.resolveInst(extra.a); + const b = try self.resolveInst(extra.b); + + return self.builder.buildSelect(pred, a, b, ""); + } + fn airShuffle(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 5e0179e3cc..a2fe461887 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -264,6 +264,7 @@ const Writer = struct { .wasm_memory_size => try w.writeWasmMemorySize(s, inst), .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst), .mul_add => try w.writeMulAdd(s, inst), + .select => try w.writeSelect(s, inst), .shuffle => try w.writeShuffle(s, inst), .reduce => try w.writeReduce(s, inst), .cmp_vector => try w.writeCmpVector(s, inst), @@ -396,6 +397,18 @@ const Writer = struct { try s.print(", mask {d}, len {d}", .{ extra.mask, extra.mask_len }); } + fn writeSelect(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.Select, ty_pl.payload).data; + + try s.print("{}, ", .{w.air.getRefType(ty_pl.ty).fmtDebug()}); + try w.writeOperand(s, inst, 0, extra.pred); + try s.writeAll(", "); + try w.writeOperand(s, inst, 1, extra.a); + try s.writeAll(", "); + try w.writeOperand(s, inst, 2, extra.b); + } + fn writeReduce(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const reduce = w.air.instructions.items(.data)[inst].reduce; diff --git a/test/behavior/select.zig b/test/behavior/select.zig index 8b4cba49bd..a1fcfb761a 100644 --- a/test/behavior/select.zig +++ b/test/behavior/select.zig @@ -4,23 +4,29 @@ const mem = std.mem; const expect = std.testing.expect; test "@select" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - const S = struct { - fn doTheTest() !void { - var a: @Vector(4, bool) = [4]bool{ true, false, true, false }; - var b: @Vector(4, i32) = [4]i32{ -1, 4, 999, -31 }; - var c: @Vector(4, i32) = [4]i32{ -5, 1, 0, 1234 }; - var abc = @select(i32, a, b, c); - try expect(mem.eql(i32, &@as([4]i32, abc), &[4]i32{ -1, 1, 999, 1234 })); - - var x: @Vector(4, bool) = [4]bool{ false, false, false, true }; - var y: @Vector(4, f32) = [4]f32{ 0.001, 33.4, 836, -3381.233 }; - var z: @Vector(4, f32) = [4]f32{ 0.0, 312.1, -145.9, 9993.55 }; - var xyz = @select(f32, x, y, z); - try expect(mem.eql(f32, &@as([4]f32, xyz), &[4]f32{ 0.0, 312.1, -145.9, -3381.233 })); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); + try doTheTest(); + comptime try doTheTest(); +} + +fn doTheTest() !void { + var a = @Vector(4, bool){ true, false, true, false }; + var b = @Vector(4, i32){ -1, 4, 999, -31 }; + var c = @Vector(4, i32){ -5, 1, 0, 1234 }; + var abc = @select(i32, a, b, c); + try expect(abc[0] == -1); + try expect(abc[1] == 1); + try expect(abc[2] == 999); + try expect(abc[3] == 1234); + + var x = @Vector(4, bool){ false, false, false, true }; + var y = @Vector(4, f32){ 0.001, 33.4, 836, -3381.233 }; + var z = @Vector(4, f32){ 0.0, 312.1, -145.9, 9993.55 }; + var xyz = @select(f32, x, y, z); + try expect(mem.eql(f32, &@as([4]f32, xyz), &[4]f32{ 0.0, 312.1, -145.9, -3381.233 })); } From f47db0a0dbf64f1fef0ba86bd9f684a5df29bc3d Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Thu, 24 Mar 2022 23:02:06 +0100 Subject: [PATCH 0918/2031] sema: use `pl_op` for `@select` --- src/Air.zig | 13 +++++-------- src/Liveness.zig | 5 +++-- src/Sema.zig | 11 +++++------ src/arch/aarch64/CodeGen.zig | 6 +++--- src/arch/arm/CodeGen.zig | 6 +++--- src/arch/riscv64/CodeGen.zig | 6 +++--- src/arch/wasm/CodeGen.zig | 6 +++--- src/arch/x86_64/CodeGen.zig | 6 +++--- src/codegen/llvm.zig | 10 +++++----- src/print_air.zig | 13 +++++++------ 10 files changed, 40 insertions(+), 42 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 4938cb8135..5120e0fd67 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -554,7 +554,7 @@ pub const Inst = struct { /// Uses the `ty_pl` field with payload `Shuffle`. shuffle, /// Constructs a vector element-wise from `a` or `b` based on `pred`. - /// Uses the `ty_pl` field with payload `Select`. + /// Uses the `pl_op` field with `pred` as operand, and payload `Bin`. select, /// Given dest ptr, value, and len, set all elements at dest to value. @@ -788,12 +788,6 @@ pub const Shuffle = struct { mask_len: u32, }; -pub const Select = struct { - pred: Inst.Ref, - a: Inst.Ref, - b: Inst.Ref, -}; - pub const VectorCmp = struct { lhs: Inst.Ref, rhs: Inst.Ref, @@ -965,7 +959,6 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .cmpxchg_weak, .cmpxchg_strong, .slice, - .select, .shuffle, .aggregate_init, .union_init, @@ -1077,6 +1070,10 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .reduce => return air.typeOf(datas[inst].reduce.operand).childType(), .mul_add => return air.typeOf(datas[inst].pl_op.operand), + .select => { + const extra = air.extraData(Air.Bin, datas[inst].pl_op.payload).data; + return air.typeOf(extra.lhs); + }, .add_with_overflow, .sub_with_overflow, diff --git a/src/Liveness.zig b/src/Liveness.zig index 9e15567d73..b9f4e6b33a 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -434,8 +434,9 @@ fn analyzeInst( return extra_tombs.finish(); }, .select => { - const extra = a.air.extraData(Air.Select, inst_datas[inst].ty_pl.payload).data; - return trackOperands(a, new_set, inst, main_tomb, .{ extra.pred, extra.a, extra.b }); + const pl_op = inst_datas[inst].pl_op; + const extra = a.air.extraData(Air.Bin, pl_op.payload).data; + return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs }); }, .shuffle => { const extra = a.air.extraData(Air.Shuffle, inst_datas[inst].ty_pl.payload).data; diff --git a/src/Sema.zig b/src/Sema.zig index e4d719f346..6971be64bf 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14894,12 +14894,11 @@ fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. try sema.requireRuntimeBlock(block, runtime_src); return block.addInst(.{ .tag = .select, - .data = .{ .ty_pl = .{ - .ty = try block.sema.addType(vec_ty), - .payload = try block.sema.addExtra(Air.Select{ - .pred = pred, - .a = a, - .b = b, + .data = .{ .pl_op = .{ + .operand = pred, + .payload = try block.sema.addExtra(Air.Bin{ + .lhs = a, + .rhs = b, }), } }, }); diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 178dfe2c29..4d931da0b2 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -3748,10 +3748,10 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void { } fn airSelect(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const extra = self.air.extraData(Air.Select, ty_pl.payload).data; + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const extra = self.air.extraData(Air.Bin, pl_op.payload).data; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ extra.pred, extra.a, extra.b }); + return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs }); } fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 7fa116edd1..15d0e47d4a 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -4325,10 +4325,10 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void { } fn airSelect(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const extra = self.air.extraData(Air.Select, ty_pl.payload).data; + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const extra = self.air.extraData(Air.Bin, pl_op.payload).data; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for arm", .{}); - return self.finishAir(inst, result, .{ extra.pred, extra.a, extra.b }); + return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs }); } fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 65b3b4904d..8b697677c1 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -2398,10 +2398,10 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void { } fn airSelect(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const extra = self.air.extraData(Air.Select, ty_pl.payload).data; + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const extra = self.air.extraData(Air.Bin, pl_op.payload).data; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for riscv64", .{}); - return self.finishAir(inst, result, .{ extra.pred, extra.a, extra.b }); + return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs }); } fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index de5e698eca..7ab4a33be9 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3269,10 +3269,10 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { fn airSelect(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; - const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const ty = try self.resolveInst(ty_pl.ty); + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const operand = try self.resolveInst(pl_op.operand); - _ = ty; + _ = operand; return self.fail("TODO: Implement wasm airSelect", .{}); } diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 8a9aaebb22..4efb836c3a 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -5680,10 +5680,10 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void { } fn airSelect(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const extra = self.air.extraData(Air.Select, ty_pl.payload).data; + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const extra = self.air.extraData(Air.Bin, pl_op.payload).data; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for x86_64", .{}); - return self.finishAir(inst, result, .{ extra.pred, extra.a, extra.b }); + return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs }); } fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 0f481e0a6d..00074d69d1 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -6359,11 +6359,11 @@ pub const FuncGen = struct { fn airSelect(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 extra = self.air.extraData(Air.Select, ty_pl.payload).data; - const pred = try self.resolveInst(extra.pred); - const a = try self.resolveInst(extra.a); - const b = try self.resolveInst(extra.b); + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const extra = self.air.extraData(Air.Bin, pl_op.payload).data; + const pred = try self.resolveInst(pl_op.operand); + const a = try self.resolveInst(extra.lhs); + const b = try self.resolveInst(extra.rhs); return self.builder.buildSelect(pred, a, b, ""); } diff --git a/src/print_air.zig b/src/print_air.zig index a2fe461887..f1e51150a6 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -398,15 +398,16 @@ const Writer = struct { } fn writeSelect(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.Select, ty_pl.payload).data; + const pl_op = w.air.instructions.items(.data)[inst].pl_op; + const extra = w.air.extraData(Air.Bin, pl_op.payload).data; - try s.print("{}, ", .{w.air.getRefType(ty_pl.ty).fmtDebug()}); - try w.writeOperand(s, inst, 0, extra.pred); + const elem_ty = w.air.typeOfIndex(inst).childType(); + try s.print("{}, ", .{elem_ty.fmtDebug()}); + try w.writeOperand(s, inst, 0, pl_op.operand); try s.writeAll(", "); - try w.writeOperand(s, inst, 1, extra.a); + try w.writeOperand(s, inst, 1, extra.lhs); try s.writeAll(", "); - try w.writeOperand(s, inst, 2, extra.b); + try w.writeOperand(s, inst, 2, extra.rhs); } fn writeReduce(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { From cd46daf7d047eeceb7690e2739af5952d60c3884 Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Thu, 24 Mar 2022 23:27:23 +0100 Subject: [PATCH 0919/2031] sema: coerce inputs to vectors in zirSelect --- src/Sema.zig | 39 +++++++++++++-------------------------- test/behavior/select.zig | 37 +++++++++++++++++++++++++++++++++---- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 6971be64bf..27f12485a4 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14805,6 +14805,7 @@ fn analyzeShuffle( fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Select, inst_data.payload_index).data; + const target = sema.mod.getTarget(); const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const pred_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; @@ -14813,35 +14814,21 @@ fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); try sema.checkVectorElemType(block, elem_ty_src, elem_ty); - const pred = sema.resolveInst(extra.pred); - const a = sema.resolveInst(extra.a); - const b = sema.resolveInst(extra.b); - const target = sema.mod.getTarget(); + const pred_uncoerced = sema.resolveInst(extra.pred); + const pred_ty = sema.typeOf(pred_uncoerced); - const pred_ty = sema.typeOf(pred); - switch (try pred_ty.zigTypeTagOrPoison()) { - .Vector => { - const scalar_ty = pred_ty.childType(); - if (!scalar_ty.eql(Type.bool, target)) { - const bool_vec_ty = try Type.vector(sema.arena, pred_ty.vectorLen(), Type.bool); - return sema.fail(block, pred_src, "Expected '{}', found '{}'", .{ bool_vec_ty.fmt(target), pred_ty.fmt(target) }); - } - }, - else => return sema.fail(block, pred_src, "Expected vector type, found '{}'", .{pred_ty.fmt(target)}), - } + const vec_len_u64 = switch (try pred_ty.zigTypeTagOrPoison()) { + .Vector, .Array => pred_ty.arrayLen(), + else => return sema.fail(block, pred_src, "expected vector or array, found '{}'", .{pred_ty.fmt(target)}), + }; + const vec_len = try sema.usizeCast(block, pred_src, vec_len_u64); + + const bool_vec_ty = try Type.vector(sema.arena, vec_len, Type.bool); + const pred = try sema.coerce(block, bool_vec_ty, pred_uncoerced, pred_src); - const vec_len = pred_ty.vectorLen(); const vec_ty = try Type.vector(sema.arena, vec_len, elem_ty); - - const a_ty = sema.typeOf(a); - if (!a_ty.eql(vec_ty, target)) { - return sema.fail(block, a_src, "Expected '{}', found '{}'", .{ vec_ty.fmt(target), a_ty.fmt(target) }); - } - - const b_ty = sema.typeOf(b); - if (!b_ty.eql(vec_ty, target)) { - return sema.fail(block, b_src, "Expected '{}', found '{}'", .{ vec_ty.fmt(target), b_ty.fmt(target) }); - } + const a = try sema.coerce(block, vec_ty, sema.resolveInst(extra.a), a_src); + const b = try sema.coerce(block, vec_ty, sema.resolveInst(extra.b), b_src); const maybe_pred = try sema.resolveMaybeUndefVal(block, pred_src, pred); const maybe_a = try sema.resolveMaybeUndefVal(block, a_src, a); diff --git a/test/behavior/select.zig b/test/behavior/select.zig index a1fcfb761a..f731ded09e 100644 --- a/test/behavior/select.zig +++ b/test/behavior/select.zig @@ -3,18 +3,18 @@ const builtin = @import("builtin"); const mem = std.mem; const expect = std.testing.expect; -test "@select" { +test "@select vectors" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - try doTheTest(); - comptime try doTheTest(); + comptime try selectVectors(); + try selectVectors(); } -fn doTheTest() !void { +fn selectVectors() !void { var a = @Vector(4, bool){ true, false, true, false }; var b = @Vector(4, i32){ -1, 4, 999, -31 }; var c = @Vector(4, i32){ -5, 1, 0, 1234 }; @@ -30,3 +30,32 @@ fn doTheTest() !void { var xyz = @select(f32, x, y, z); try expect(mem.eql(f32, &@as([4]f32, xyz), &[4]f32{ 0.0, 312.1, -145.9, -3381.233 })); } + +test "@select arrays" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + comptime try selectArrays(); + try selectArrays(); +} + +fn selectArrays() !void { + var a = [4]bool{ false, true, false, true }; + var b = [4]usize{ 0, 1, 2, 3 }; + var c = [4]usize{ 4, 5, 6, 7 }; + var abc = @select(usize, a, b, c); + try expect(abc[0] == 4); + try expect(abc[1] == 1); + try expect(abc[2] == 6); + try expect(abc[3] == 3); + + var x = [4]bool{ false, false, false, true }; + var y = [4]f32{ 0.001, 33.4, 836, -3381.233 }; + var z = [4]f32{ 0.0, 312.1, -145.9, 9993.55 }; + var xyz = @select(f32, x, y, z); + try expect(mem.eql(f32, &@as([4]f32, xyz), &[4]f32{ 0.0, 312.1, -145.9, -3381.233 })); +} From 061d6699c0231cb33f3b47de727ab54be6d20389 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Thu, 17 Mar 2022 22:58:33 +0100 Subject: [PATCH 0920/2031] stage2 AArch64: lower cmp to binOp --- src/arch/aarch64/CodeGen.zig | 179 +++++++++++++---------------------- src/arch/aarch64/Emit.zig | 44 ++++++--- src/arch/aarch64/Mir.zig | 13 ++- test/behavior/optional.zig | 2 + 4 files changed, 111 insertions(+), 127 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 8e502e8943..d58092f7da 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -1058,7 +1058,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { _ = try self.addInst(.{ .tag = .mvn, - .data = .{ .rr_imm6_shift = .{ + .data = .{ .rr_imm6_logical_shift = .{ .rd = dest_reg, .rm = op_reg, .imm6 = 0, @@ -1188,6 +1188,7 @@ fn binOpRegister( .sub, .ptr_sub, => .sub_shifted_register, + .cmp_eq => .cmp_shifted_register, .mul => .mul, .bit_and, .bool_and, @@ -1219,6 +1220,12 @@ fn binOpRegister( .imm6 = 0, .shift = .lsl, } }, + .cmp_eq => .{ .rr_imm6_shift = .{ + .rn = lhs_reg, + .rm = rhs_reg, + .imm6 = 0, + .shift = .lsl, + } }, .mul, .shl, .shl_exact, @@ -1296,20 +1303,23 @@ fn binOpImmediate( }; defer self.register_manager.unfreezeRegs(&.{lhs_reg}); - const dest_reg = if (maybe_inst) |inst| blk: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const dest_reg = switch (tag) { + .cmp_eq => undefined, // cmp has no destination register + else => if (maybe_inst) |inst| blk: { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; - if (lhs_is_register and self.reuseOperand( - inst, - if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, - if (lhs_and_rhs_swapped) 1 else 0, - lhs, - )) { - break :blk lhs_reg; - } else { - break :blk try self.register_manager.allocReg(inst); - } - } else try self.register_manager.allocReg(null); + if (lhs_is_register and self.reuseOperand( + inst, + if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, + if (lhs_and_rhs_swapped) 1 else 0, + lhs, + )) { + break :blk lhs_reg; + } else { + break :blk try self.register_manager.allocReg(inst); + } + } else try self.register_manager.allocReg(null), + }; if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); @@ -1325,6 +1335,7 @@ fn binOpImmediate( .signed => Mir.Inst.Tag.asr_immediate, .unsigned => Mir.Inst.Tag.lsr_immediate, }, + .cmp_eq => .cmp_immediate, else => unreachable, }; const mir_data: Mir.Inst.Data = switch (tag) { @@ -1344,6 +1355,10 @@ fn binOpImmediate( .rn = lhs_reg, .shift = @intCast(u6, rhs.immediate), } }, + .cmp_eq => .{ .r_imm12_sh = .{ + .rn = lhs_reg, + .imm12 = @intCast(u12, rhs.immediate), + } }, else => unreachable, }; @@ -1381,6 +1396,7 @@ fn binOp( // Arithmetic operations on integers and floats .add, .sub, + .cmp_eq, => { switch (lhs_ty.zigTypeTag()) { .Float => return self.fail("TODO binary operations on floats", .{}), @@ -1394,12 +1410,13 @@ fn binOp( // operands const lhs_immediate_ok = switch (tag) { .add => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), - .sub => false, + .sub, .cmp_eq => false, else => unreachable, }; const rhs_immediate_ok = switch (tag) { .add, .sub, + .cmp_eq, => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), else => unreachable, }; @@ -2036,8 +2053,7 @@ fn genInlineMemcpy( // cmp count, len _ = try self.addInst(.{ .tag = .cmp_shifted_register, - .data = .{ .rrr_imm6_shift = .{ - .rd = .xzr, + .data = .{ .rr_imm6_shift = .{ .rn = count, .rm = len, .imm6 = 0, @@ -2615,107 +2631,44 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); - if (self.liveness.isUnused(inst)) - return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + var int_buffer: Type.Payload.Bits = undefined; + const int_ty = switch (lhs_ty.zigTypeTag()) { + .Vector => return self.fail("TODO AArch64 cmp vectors", .{}), + .Enum => lhs_ty.intTagType(&int_buffer), + .Int => lhs_ty, + .Bool => Type.initTag(.u1), + .Pointer => Type.usize, + .ErrorSet => Type.initTag(.u16), + .Optional => blk: { + if (lhs_ty.isPtrLikeOptional()) { + break :blk Type.usize; + } - const ty = self.air.typeOf(bin_op.lhs); - - if (ty.abiSize(self.target.*) > 8) { - return self.fail("TODO cmp for types with size > 8", .{}); - } - - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; - - const signedness: std.builtin.Signedness = blk: { - // by default we tell the operand type is unsigned (i.e. bools and enum values) - if (ty.zigTypeTag() != .Int) break :blk .unsigned; - - // incase of an actual integer, we emit the correct signedness - break :blk ty.intInfo(self.target.*).signedness; - }; - - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - const result: MCValue = result: { - const lhs_is_register = lhs == .register; - const rhs_is_register = rhs == .register; - // lhs should always be a register - const rhs_should_be_register = switch (rhs) { - .immediate => |imm| imm < 0 or imm > std.math.maxInt(u12), - else => true, - }; - - if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); - defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register}); - if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); - defer if (rhs_is_register) self.register_manager.unfreezeRegs(&.{rhs.register}); - - var lhs_mcv = lhs; - var rhs_mcv = rhs; - - // Allocate registers - if (rhs_should_be_register) { - if (!lhs_is_register and !rhs_is_register) { - const regs = try self.register_manager.allocRegs(2, .{ - Air.refToIndex(bin_op.lhs).?, Air.refToIndex(bin_op.rhs).?, - }); - lhs_mcv = MCValue{ .register = regs[0] }; - rhs_mcv = MCValue{ .register = regs[1] }; - } else if (!rhs_is_register) { - rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(bin_op.rhs).?) }; - } else if (!lhs_is_register) { - lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(bin_op.lhs).?) }; - } - } else { - if (!lhs_is_register) { - lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(bin_op.lhs).?) }; - } - } - - // Move the operands to the newly allocated registers - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; - if (lhs_mcv == .register and !lhs_is_register) { - try self.genSetReg(ty, lhs_mcv.register, lhs); - branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.lhs).?, lhs); - } - if (rhs_mcv == .register and !rhs_is_register) { - try self.genSetReg(ty, rhs_mcv.register, rhs); - branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.rhs).?, rhs); - } - - // The destination register is not present in the cmp instruction - // The signedness of the integer does not matter for the cmp instruction - switch (rhs_mcv) { - .register => |reg| { - _ = try self.addInst(.{ - .tag = .cmp_shifted_register, - .data = .{ .rrr_imm6_shift = .{ - .rd = .xzr, - .rn = lhs_mcv.register, - .rm = reg, - .imm6 = 0, - .shift = .lsl, - } }, - }); - }, - .immediate => |imm| { - _ = try self.addInst(.{ - .tag = .cmp_immediate, - .data = .{ .r_imm12_sh = .{ - .rn = lhs_mcv.register, - .imm12 = @intCast(u12, imm), - } }, - }); + return self.fail("TODO AArch64 cmp optionals", .{}); }, + .Float => return self.fail("TODO AArch64 cmp floats", .{}), else => unreachable, - } - - break :result switch (signedness) { - .signed => MCValue{ .compare_flags_signed = op }, - .unsigned => MCValue{ .compare_flags_unsigned = op }, }; + + const int_info = int_ty.intInfo(self.target.*); + if (int_info.bits <= 64) { + _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty); + + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = inst; + + break :result switch (int_info.signedness) { + .signed => MCValue{ .compare_flags_signed = op }, + .unsigned => MCValue{ .compare_flags_unsigned = op }, + }; + } else { + return self.fail("TODO AArch64 cmp for ints > 64 bits", .{}); + } }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 2957389b32..3eb25dda7b 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -650,17 +650,32 @@ fn mirLogicalImmediate(emit: *Emit, inst: Mir.Inst.Index) !void { fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; - const rrr_imm6_shift = emit.mir.instructions.items(.data)[inst].rrr_imm6_shift; - const rd = rrr_imm6_shift.rd; - const rn = rrr_imm6_shift.rn; - const rm = rrr_imm6_shift.rm; - const shift = rrr_imm6_shift.shift; - const imm6 = rrr_imm6_shift.imm6; - switch (tag) { - .add_shifted_register => try emit.writeInstruction(Instruction.addShiftedRegister(rd, rn, rm, shift, imm6)), - .cmp_shifted_register => try emit.writeInstruction(Instruction.subsShiftedRegister(rd, rn, rm, shift, imm6)), - .sub_shifted_register => try emit.writeInstruction(Instruction.subShiftedRegister(rd, rn, rm, shift, imm6)), + .add_shifted_register, + .sub_shifted_register, + => { + const rrr_imm6_shift = emit.mir.instructions.items(.data)[inst].rrr_imm6_shift; + const rd = rrr_imm6_shift.rd; + const rn = rrr_imm6_shift.rn; + const rm = rrr_imm6_shift.rm; + const shift = rrr_imm6_shift.shift; + const imm6 = rrr_imm6_shift.imm6; + + switch (tag) { + .add_shifted_register => try emit.writeInstruction(Instruction.addShiftedRegister(rd, rn, rm, shift, imm6)), + .sub_shifted_register => try emit.writeInstruction(Instruction.subShiftedRegister(rd, rn, rm, shift, imm6)), + else => unreachable, + } + }, + .cmp_shifted_register => { + const rr_imm6_shift = emit.mir.instructions.items(.data)[inst].rr_imm6_shift; + const rn = rr_imm6_shift.rn; + const rm = rr_imm6_shift.rm; + const shift = rr_imm6_shift.shift; + const imm6 = rr_imm6_shift.imm6; + + try emit.writeInstruction(Instruction.subsShiftedRegister(.xzr, rn, rm, shift, imm6)); + }, else => unreachable, } } @@ -896,8 +911,13 @@ fn mirMoveRegister(emit: *Emit, inst: Mir.Inst.Index) !void { try emit.writeInstruction(Instruction.add(rr.rd, rr.rn, 0, false)); }, .mvn => { - const rr_imm6_shift = emit.mir.instructions.items(.data)[inst].rr_imm6_shift; - try emit.writeInstruction(Instruction.ornShiftedRegister(rr_imm6_shift.rd, .xzr, rr_imm6_shift.rm, rr_imm6_shift.shift, rr_imm6_shift.imm6)); + const rr_imm6_logical_shift = emit.mir.instructions.items(.data)[inst].rr_imm6_logical_shift; + const rd = rr_imm6_logical_shift.rd; + const rm = rr_imm6_logical_shift.rm; + const shift = rr_imm6_logical_shift.shift; + const imm6 = rr_imm6_logical_shift.imm6; + + try emit.writeInstruction(Instruction.ornShiftedRegister(rd, .xzr, rm, shift, imm6)); }, else => unreachable, } diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index 679daf8ae2..6515a8da2e 100644 --- a/src/arch/aarch64/Mir.zig +++ b/src/arch/aarch64/Mir.zig @@ -249,11 +249,20 @@ pub const Inst = struct { imm12: u12, sh: u1 = 0, }, + /// Two registers and a shift (shift type and 6-bit amount) + /// + /// Used by e.g. cmp_shifted_register + rr_imm6_shift: struct { + rn: Register, + rm: Register, + imm6: u6, + shift: bits.Instruction.AddSubtractShiftedRegisterShift, + }, /// Two registers and a shift (logical instruction version) /// (shift type and 6-bit amount) /// /// Used by e.g. mvn - rr_imm6_shift: struct { + rr_imm6_logical_shift: struct { rd: Register, rm: Register, imm6: u6, @@ -287,7 +296,7 @@ pub const Inst = struct { }, /// Three registers and a shift (shift type and 6-bit amount) /// - /// Used by e.g. cmp_shifted_register + /// Used by e.g. add_shifted_register rrr_imm6_shift: struct { rd: Register, rn: Register, diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index 6f5623d827..9956c3f3ec 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -33,6 +33,8 @@ test "optional pointer to size zero struct" { } test "equality compare optional pointers" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + try testNullPtrsEql(); comptime try testNullPtrsEql(); } From 16e49663329007c7f9c33086f4938c1450b1ebbd Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Thu, 17 Mar 2022 23:03:41 +0100 Subject: [PATCH 0921/2031] stage2 AArch64: remove MCValue.embedded_in_code --- src/arch/aarch64/CodeGen.zig | 33 +-------------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index d58092f7da..74a323c797 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -115,11 +115,6 @@ const MCValue = union(enum) { /// A pointer-sized integer that fits in a register. /// If the type is a pointer, this is the pointer address in virtual address space. immediate: u64, - /// The constant was emitted into the code, at this offset. - /// If the type is a pointer, it means the pointer address is embedded in the code. - embedded_in_code: usize, - /// The value is a pointer to a constant which was emitted into the code, at this offset. - ptr_embedded_in_code: usize, /// The value is in a target-specific register. register: Register, /// The value is in memory at a hard-coded address. @@ -147,7 +142,7 @@ const MCValue = union(enum) { fn isMemory(mcv: MCValue) bool { return switch (mcv) { - .embedded_in_code, .memory, .stack_offset => true, + .memory, .stack_offset => true, else => false, }; } @@ -166,12 +161,10 @@ const MCValue = union(enum) { .dead => unreachable, .immediate, - .embedded_in_code, .memory, .compare_flags_unsigned, .compare_flags_signed, .ptr_stack_offset, - .ptr_embedded_in_code, .undef, => false, @@ -1966,12 +1959,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .compare_flags_signed => unreachable, .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), - .ptr_embedded_in_code => |off| { - try self.setRegOrMem(elem_ty, dst_mcv, .{ .embedded_in_code = off }); - }, - .embedded_in_code => { - return self.fail("TODO implement loading from MCValue.embedded_in_code", .{}); - }, .register => |addr_reg| { self.register_manager.freezeRegs(&.{addr_reg}); defer self.register_manager.unfreezeRegs(&.{addr_reg}); @@ -1980,7 +1967,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .dead => unreachable, .undef => unreachable, .compare_flags_signed, .compare_flags_unsigned => unreachable, - .embedded_in_code => unreachable, .register => |dst_reg| { try self.genLdrRegister(dst_reg, addr_reg, elem_size); }, @@ -2243,12 +2229,6 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .ptr_stack_offset => |off| { try self.genSetStack(value_ty, off, value); }, - .ptr_embedded_in_code => |off| { - try self.setRegOrMem(value_ty, .{ .embedded_in_code = off }, value); - }, - .embedded_in_code => { - return self.fail("TODO implement storing to MCValue.embedded_in_code", .{}); - }, .register => |addr_reg| { self.register_manager.freezeRegs(&.{addr_reg}); defer self.register_manager.unfreezeRegs(&.{addr_reg}); @@ -2461,7 +2441,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .immediate => unreachable, .unreach => unreachable, .dead => unreachable, - .embedded_in_code => unreachable, .memory => unreachable, .compare_flags_signed => unreachable, .compare_flags_unsigned => unreachable, @@ -2477,9 +2456,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .ptr_stack_offset => { return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); }, - .ptr_embedded_in_code => { - return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); - }, } } @@ -3337,15 +3313,10 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro .compare_flags_signed, .immediate, .ptr_stack_offset, - .ptr_embedded_in_code, => { const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); }, - .embedded_in_code => |code_offset| { - _ = code_offset; - return self.fail("TODO implement set stack variable from embedded_in_code", .{}); - }, .register => |reg| { const adj_off = stack_offset + abi_size; @@ -3448,7 +3419,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { switch (mcv) { .dead => unreachable, - .ptr_embedded_in_code => unreachable, .unreach, .none => return, // Nothing to do. .undef => { if (!self.wantSafety()) @@ -3586,7 +3556,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void else => unreachable, } }, - else => return self.fail("TODO implement genSetReg for aarch64 {}", .{mcv}), } } From f7da4b9bc802b66d409413596204df355604fc67 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Fri, 25 Mar 2022 19:16:58 +0100 Subject: [PATCH 0922/2031] stage2 AArch64: change semantics of MCValue.stack_offset Mirrors the changes performed to stack_offset in the ARM backend --- src/arch/aarch64/CodeGen.zig | 72 ++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 74a323c797..09219f74ba 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -809,10 +809,9 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u if (abi_align > self.stack_align) self.stack_align = abi_align; // TODO find a free slot instead of always appending - const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align); - self.next_stack_offset = offset + abi_size; - if (self.next_stack_offset > self.max_end_stack) - self.max_end_stack = self.next_stack_offset; + const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size; + self.next_stack_offset = offset; + self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset); try self.stack.putNoClobber(self.gpa, offset, .{ .inst = inst, .size = abi_size, @@ -825,9 +824,10 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { const elem_ty = self.air.typeOfIndex(inst).elemType(); if (!elem_ty.hasRuntimeBits()) { - // As this stack item will never be dereferenced at runtime, - // return the current stack offset - return self.next_stack_offset; + // return the stack offset 0. Stack offset 0 will be where all + // zero-sized stack allocations live as non-zero-sized + // allocations will always have an offset > 0. + return @as(u32, 0); } const target = self.target.*; @@ -1097,8 +1097,8 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { const ptr_bytes = @divExact(ptr_bits, 8); const stack_offset = try self.allocMem(inst, ptr_bytes * 2, ptr_bytes * 2); - try self.genSetStack(ptr_ty, stack_offset + ptr_bytes, ptr); - try self.genSetStack(len_ty, stack_offset, len); + try self.genSetStack(ptr_ty, stack_offset, ptr); + try self.genSetStack(len_ty, stack_offset - ptr_bytes, len); break :result MCValue{ .stack_offset = stack_offset }; }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); @@ -1515,7 +1515,13 @@ fn binOp( const elem_size = elem_ty.abiSize(self.target.*); if (elem_size == 1) { - return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + const base_tag: Air.Inst.Tag = switch (tag) { + .ptr_add => .add, + .ptr_sub => .sub, + else => unreachable, + }; + + return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); } else { // convert the offset into a byte offset by // multiplying it with elem_size @@ -1724,14 +1730,12 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { fn airSlicePtr(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 result: { - const ptr_bits = self.target.cpu.arch.ptrBitWidth(); - const ptr_bytes = @divExact(ptr_bits, 8); const mcv = try self.resolveInst(ty_op.operand); switch (mcv) { .dead, .unreach, .none => unreachable, .register => unreachable, // a slice doesn't fit in one register .stack_offset => |off| { - break :result MCValue{ .stack_offset = off + ptr_bytes }; + break :result MCValue{ .stack_offset = off }; }, .memory => |addr| { break :result MCValue{ .memory = addr }; @@ -1752,7 +1756,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { .dead, .unreach, .none => unreachable, .register => unreachable, // a slice doesn't fit in one register .stack_offset => |off| { - break :result MCValue{ .stack_offset = off }; + break :result MCValue{ .stack_offset = off - ptr_bytes }; }, .memory => |addr| { break :result MCValue{ .memory = addr + ptr_bytes }; @@ -1772,7 +1776,7 @@ fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void { switch (mcv) { .dead, .unreach, .none => unreachable, .ptr_stack_offset => |off| { - break :result MCValue{ .ptr_stack_offset = off + ptr_bytes }; + break :result MCValue{ .ptr_stack_offset = off - ptr_bytes }; }, else => return self.fail("TODO implement ptr_slice_len_ptr for {}", .{mcv}), } @@ -1819,7 +1823,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { defer if (index_is_register) self.register_manager.unfreezeRegs(&.{index_mcv.register}); const base_mcv: MCValue = switch (slice_mcv) { - .stack_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_offset = off + 8 }) }, + .stack_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_offset = off }) }, else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}), }; self.register_manager.freezeRegs(&.{base_mcv.register}); @@ -2293,13 +2297,10 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde const mcv = try self.resolveInst(operand); const ptr_ty = self.air.typeOf(operand); const struct_ty = ptr_ty.childType(); - const struct_size = @intCast(u32, struct_ty.abiSize(self.target.*)); const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); - const struct_field_ty = struct_ty.structFieldType(index); - const struct_field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*)); switch (mcv) { .ptr_stack_offset => |off| { - break :result MCValue{ .ptr_stack_offset = off + struct_size - struct_field_offset - struct_field_size }; + break :result MCValue{ .ptr_stack_offset = off - struct_field_offset }; }, else => { const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ @@ -2621,11 +2622,15 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { .Pointer => Type.usize, .ErrorSet => Type.initTag(.u16), .Optional => blk: { - if (lhs_ty.isPtrLikeOptional()) { + var opt_buffer: Type.Payload.ElemType = undefined; + const payload_ty = lhs_ty.optionalChild(&opt_buffer); + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + break :blk Type.initTag(.u1); + } else if (lhs_ty.isPtrLikeOptional()) { break :blk Type.usize; + } else { + return self.fail("TODO AArch64 cmp non-pointer optionals", .{}); } - - return self.fail("TODO AArch64 cmp optionals", .{}); }, .Float => return self.fail("TODO AArch64 cmp floats", .{}), else => unreachable, @@ -3318,8 +3323,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); }, .register => |reg| { - const adj_off = stack_offset + abi_size; - switch (abi_size) { 1, 2, 4, 8 => { const tag: Mir.Inst.Tag = switch (abi_size) { @@ -3334,7 +3337,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro .tag = tag, .data = .{ .load_store_stack = .{ .rt = rt, - .offset = @intCast(u32, adj_off), + .offset = @intCast(u32, stack_offset), } }, }); }, @@ -3430,13 +3433,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void else => unreachable, // unexpected register size } }, - .ptr_stack_offset => |unadjusted_off| { + .ptr_stack_offset => |off| { // TODO: maybe addressing from sp instead of fp - const elem_ty = ty.childType(); - const abi_size = elem_ty.abiSize(self.target.*); - const adj_off = unadjusted_off + abi_size; - - const imm12 = math.cast(u12, adj_off) catch + const imm12 = math.cast(u12, off) catch return self.fail("TODO larger stack offsets", .{}); _ = try self.addInst(.{ @@ -3526,9 +3525,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void try self.genSetReg(ty, reg, .{ .immediate = addr }); try self.genLdrRegister(reg, reg, ty.abiSize(self.target.*)); }, - .stack_offset => |unadjusted_off| { + .stack_offset => |off| { const abi_size = ty.abiSize(self.target.*); - const adj_off = unadjusted_off + abi_size; switch (abi_size) { 1, 2, 4, 8 => { @@ -3548,7 +3546,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .tag = tag, .data = .{ .load_store_stack = .{ .rt = rt, - .offset = @intCast(u32, adj_off), + .offset = @intCast(u32, off), } }, }); }, @@ -3583,8 +3581,8 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { const ptr_bytes = @divExact(ptr_bits, 8); const stack_offset = try self.allocMem(inst, ptr_bytes * 2, ptr_bytes * 2); - try self.genSetStack(ptr_ty, stack_offset + ptr_bytes, ptr); - try self.genSetStack(Type.initTag(.usize), stack_offset, .{ .immediate = array_len }); + try self.genSetStack(ptr_ty, stack_offset, ptr); + try self.genSetStack(Type.initTag(.usize), stack_offset - ptr_bytes, .{ .immediate = array_len }); break :result MCValue{ .stack_offset = stack_offset }; }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); From 7f64f7c9259ba5f67adb386ba55473d4b2b74607 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Wed, 23 Mar 2022 22:03:17 -0700 Subject: [PATCH 0923/2031] Add rudimentary compile error test file support This brings two quality-of-life improvements for folks working on compile error test cases: - test cases can be added/changed without re-building Zig - wrapping the source in a multi-line string literal is not necessary I decided to keep things as simple as possible for this initial implementation. The test "manifest" is a contiguous comment block at the end of the test file: 1. The first line is the test case name 2. The second line is a blank comment 2. The following lines are expected errors Here's an example: ```zig const U = union(enum(u2)) { A: u8, B: u8, C: u8, D: u8, E: u8, }; export fn entry() void { _ = U{ .E = 1 }; } // union with too small explicit unsigned tag type // // tmp.zig:1:22: error: specified integer tag type cannot represent every field // tmp.zig:1:22: note: type u2 cannot fit values in range 0...4 ``` The mode of the test (obj/exe/test), as well as the target (stage1/stage2) is determined based on the directory containing the test. We'll probably eventually want to support embedding this information in the test files themselves, similar to the arocc test runner, but that enhancement can be tackled later. --- src/test.zig | 99 +++++++++++++++++++++++++++++++++++++++++ test/compile_errors.zig | 36 +++++++++++++++ 2 files changed, 135 insertions(+) diff --git a/src/test.zig b/src/test.zig index e2be5e2172..6bdff16a58 100644 --- a/src/test.zig +++ b/src/test.zig @@ -20,6 +20,7 @@ const assert = std.debug.assert; const zig_h = link.File.C.zig_h; +var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; const hr = "=" ** 80; test { @@ -594,6 +595,104 @@ pub const TestContext = struct { case.compiles(fixed_src); } + /// Adds a compile-error test for each file in the provided directory, using the + /// selected backend and output mode. If `one_test_case_per_file` is true, a new + /// test case is created for each file. Otherwise, a single test case is used for + /// all tests. + /// + /// Each file should include a test manifest as a contiguous block of comments at + /// the end of the file. The first line should be the test case name, followed by + /// a blank line, then one expected errors on each line in the form + /// `:line:column: error: message` + pub fn addErrorCasesFromDir( + ctx: *TestContext, + name: []const u8, + dir: std.fs.Dir, + backend: Backend, + output_mode: std.builtin.OutputMode, + is_test: bool, + one_test_case_per_file: bool, + ) !void { + if (skip_compile_errors) return; + + const gpa = general_purpose_allocator.allocator(); + var case: ?*Case = null; + + var it = dir.iterate(); + while (try it.next()) |entry| { + if (entry.kind != .File) continue; + + var contents = try dir.readFileAlloc(gpa, entry.name, std.math.maxInt(u32)); + defer gpa.free(contents); + + // The manifest is the last contiguous block of comments in the file + // We scan for the beginning by searching backward for the first non-empty line that does not start with "//" + var manifest_start: ?usize = null; + var manifest_end: usize = contents.len; + if (contents.len > 0) { + var cursor: usize = contents.len - 1; + while (true) { + // Move to beginning of line + while (cursor > 0 and contents[cursor - 1] != '\n') cursor -= 1; + + // Check if line is non-empty and does not start with "//" + if (cursor + 1 < contents.len and contents[cursor + 1] != '\n' and contents[cursor + 1] != '\r') { + if (std.mem.startsWith(u8, contents[cursor..], "//")) { + manifest_start = cursor; + } else { + break; + } + } else manifest_end = cursor; + + // Move to previous line + if (cursor != 0) cursor -= 1 else break; + } + } + + var errors = std.ArrayList([]const u8).init(gpa); + defer errors.deinit(); + + if (manifest_start) |start| { + // Due to the above processing, we know that this is a contiguous block of comments + var manifest_it = std.mem.tokenize(u8, contents[start..manifest_end], "\r\n"); + + // First line is the test case name + const first_line = manifest_it.next() orelse return error.InvalidFile; + const case_name = try std.mem.concat(gpa, u8, &.{ name, ": ", std.mem.trim(u8, first_line[2..], " \t") }); + + // If the second line is present, it should be blank + if (manifest_it.next()) |second_line| { + if (std.mem.trim(u8, second_line[2..], " \t").len != 0) return error.InvalidFile; + } + + // All following lines are expected error messages + while (manifest_it.next()) |line| try errors.append(try gpa.dupe(u8, std.mem.trim(u8, line[2..], " \t"))); + + // The entire file contents is the source, including the manifest + const src = try gpa.dupeZ(u8, contents); + + // Create a new test case, if necessary + case = if (one_test_case_per_file or case == null) blk: { + ctx.cases.append(TestContext.Case{ + .name = if (one_test_case_per_file) case_name else name, + .target = .{}, + .backend = backend, + .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), + .is_test = is_test, + .output_mode = output_mode, + .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), + }) catch @panic("out of memory"); + break :blk &ctx.cases.items[ctx.cases.items.len - 1]; + } else case.?; + + // Add our update + expected errors + case.?.addError(src, errors.items); + } else { + return error.InvalidFile; // Manifests are currently mandatory + } + } + } + fn init() TestContext { const allocator = std.heap.page_allocator; return .{ .cases = std.ArrayList(Case).init(allocator) }; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 5008bdd7b8..e0d90e3ee2 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3,6 +3,42 @@ const builtin = @import("builtin"); const TestContext = @import("../src/test.zig").TestContext; pub fn addCases(ctx: *TestContext) !void { + var parent_dir = try std.fs.cwd().openDir(std.fs.path.dirname(@src().file).?, .{ .no_follow = true }); + defer parent_dir.close(); + + var compile_errors_dir = try parent_dir.openDir("compile_errors", .{ .no_follow = true }); + defer compile_errors_dir.close(); + + { + var stage2_dir = try compile_errors_dir.openDir("stage2", .{ .iterate = true, .no_follow = true }); + defer stage2_dir.close(); + + const one_test_case_per_file = false; + try ctx.addErrorCasesFromDir("stage2 compile errors", stage2_dir, .stage2, .Obj, false, one_test_case_per_file); + } + + { + var stage1_dir = try compile_errors_dir.openDir("stage1", .{ .no_follow = true }); + defer stage1_dir.close(); + { + const one_test_case_per_file = true; + + var obj_dir = try stage1_dir.openDir("obj", .{ .iterate = true, .no_follow = true }); + defer obj_dir.close(); + + try ctx.addErrorCasesFromDir("stage1", obj_dir, .stage1, .Obj, false, one_test_case_per_file); + + var exe_dir = try stage1_dir.openDir("exe", .{ .iterate = true, .no_follow = true }); + defer exe_dir.close(); + + try ctx.addErrorCasesFromDir("stage1", exe_dir, .stage1, .Exe, false, one_test_case_per_file); + + var test_dir = try stage1_dir.openDir("test", .{ .iterate = true, .no_follow = true }); + defer test_dir.close(); + + try ctx.addErrorCasesFromDir("stage1", test_dir, .stage1, .Exe, true, one_test_case_per_file); + } + } { var case = ctx.obj("stage2 compile errors", .{}); From 1de63ad79331aec9f53a7a9d95069fd0ca5f66f4 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Wed, 23 Mar 2022 23:05:33 -0700 Subject: [PATCH 0924/2031] zig fmt: Add `--exclude` argument to skip dir/file This change adds a "--exclude" parameter to zig format, which can be used to make sure that it does not process certain files or folders when recursively walking a directory. To do this, we simply piggy-back on the existing "seen" logic in zig fmt and mark these files/folders as seen before processing begins. --- ci/zinc/linux_test.sh | 2 +- src/main.zig | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/ci/zinc/linux_test.sh b/ci/zinc/linux_test.sh index 9b0734b170..739d85cff0 100755 --- a/ci/zinc/linux_test.sh +++ b/ci/zinc/linux_test.sh @@ -45,7 +45,7 @@ cd $WORKSPACE # Look for non-conforming code formatting. # Formatting errors can be fixed by running `zig fmt` on the files printed here. -$ZIG fmt --check . +$ZIG fmt --check . --exclude test/compile_errors/ # Build stage2 standalone so that we can test stage2 against stage2 compiler-rt. $ZIG build -p stage2 -Denable-llvm -Duse-zig-libcxx diff --git a/src/main.zig b/src/main.zig index 115f3748b6..a647216b05 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3792,6 +3792,7 @@ pub const usage_fmt = \\ --check List non-conforming files and exit with an error \\ if the list is non-empty \\ --ast-check Run zig ast-check on every file + \\ --exclude [file] Exclude file or directory from formatting \\ \\ ; @@ -3815,6 +3816,8 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void var check_ast_flag: bool = false; var input_files = ArrayList([]const u8).init(gpa); defer input_files.deinit(); + var excluded_files = ArrayList([]const u8).init(gpa); + defer excluded_files.deinit(); { var i: usize = 0; @@ -3840,6 +3843,13 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void check_flag = true; } else if (mem.eql(u8, arg, "--ast-check")) { check_ast_flag = true; + } else if (mem.eql(u8, arg, "--exclude")) { + if (i + 1 >= args.len) { + fatal("expected parameter after --exclude", .{}); + } + i += 1; + const next_arg = args[i]; + try excluded_files.append(next_arg); } else { fatal("unrecognized parameter: '{s}'", .{arg}); } @@ -3940,6 +3950,16 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void defer fmt.seen.deinit(); defer fmt.out_buffer.deinit(); + // Mark any excluded files/directories as already seen, + // so that they are skipped later during actual processing + for (excluded_files.items) |file_path| { + var dir = try fs.cwd().openDir(file_path, .{}); + defer dir.close(); + + const stat = try dir.stat(); + try fmt.seen.put(stat.inode, {}); + } + for (input_files.items) |file_path| { try fmtPath(&fmt, file_path, check_flag, fs.cwd(), file_path); } From 0568b4577947f7af2e04cb9e9c95011b0fca88c8 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Wed, 23 Mar 2022 22:28:08 -0700 Subject: [PATCH 0925/2031] Move existing compile errors to independent files Some cases had to stay behind, either because they required complex case configuration that we don't support in independent files yet, or because they have associated comments which we don't want to lose track of. To make sure I didn't drop any tests in the process, I logged all obj/test/exe test cases from a run of "zig build test" and compared before/after this change. All of the test cases match, with two exceptions: - "use of comptime-known undefined function value" was deleted, since it was a duplicate - "slice sentinel mismatch" was renamed to "vector index out of bounds", since it was incorrectly named --- test/compile_errors.zig | 8573 +---------------- .../stage1/exe/main_missing_name.zig | 5 + .../exe/missing_main_fn_in_executable.zig | 5 + .../stage1/exe/private_main_fn.zig | 6 + .../std.fmt_error_for_unused_arguments.zig | 7 + ..._ABI_compatible_type_or_has_align_attr.zig | 10 + .../stage1/obj/C_pointer_to_anyopaque.zig | 9 + .../stage1/obj/Frame_of_generic_function.zig | 12 + ...nus_for_unsigned_types_a_compile_error.zig | 14 + ...e_6823_dont_allow_._to_be_followed_by_.zig | 8 + ...5_windows_tcp_server_compilation_error.zig | 14 + ...ccess_non-existent_member_of_error_set.zig | 9 + ..._runtime_parameter_from_outer_function.zig | 19 + .../obj/add_assign_on_undefined_value.zig | 8 + .../stage1/obj/add_on_undefined_value.zig | 8 + .../add_overflow_in_function_evaluation.zig | 10 + .../add_wrap_assign_on_undefined_value.zig | 8 + .../obj/add_wrap_on_undefined_value.zig | 8 + .../stage1/obj/addition_with_non_numbers.zig | 10 + .../stage1/obj/address_of_number_literal.zig | 8 + .../alignCast_expects_pointer_or_slice.zig | 7 + .../obj/aligned_variable_of_zero-bit_type.zig | 8 + .../obj/alignment_of_enum_field_specified.zig | 12 + .../stage1/obj/ambiguous_decl_reference.zig | 19 + .../stage1/obj/and_on_undefined_value.zig | 8 + .../stage1/obj/array_access_of_non_array.zig | 13 + .../stage1/obj/array_access_of_type.zig | 8 + .../array_access_of_undeclared_identifier.zig | 7 + .../array_access_with_non_integer_index.zig | 15 + .../array_concatenation_with_wrong_type.zig | 9 + .../obj/array_in_c_exported_function.zig | 12 + .../stage1/obj/asm_at_compile_time.zig | 15 + .../assign_inline_fn_to_non-comptime_var.zig | 10 + .../assign_null_to_non-optional_pointer.zig | 7 + .../obj/assign_through_constant_pointer.zig | 8 + .../obj/assign_through_constant_slice.zig | 8 + .../stage1/obj/assign_to_constant_field.zig | 11 + .../obj/assign_to_constant_variable.zig | 8 + .../obj/assign_to_invalid_dereference.zig | 7 + .../obj/assign_too_big_number_to_u16.zig | 8 + .../stage1/obj/assign_unreachable.zig | 8 + ...th_a_function_that_returns_an_optional.zig | 19 + ...sync_function_depends_on_its_own_frame.zig | 11 + ...on_indirectly_depends_on_its_own_frame.zig | 15 + ...rings_of_atomicStore_Acquire_or_AcqRel.zig | 8 + ..._cmpxchg-failure_stricter_than_success.zig | 9 + ..._cmpxchg-success_Monotonic_or_stricter.zig | 9 + ...orderings_of_fence_Acquire_or_stricter.zig | 7 + .../obj/atomicrmw_with_bool_op_not_.Xchg.zig | 8 + .../obj/atomicrmw_with_enum_op_not_.Xchg.zig | 14 + ...w_with_float_op_not_.Xchg_.Add_or_.Sub.zig | 8 + .../attempt_to_cast_enum_literal_to_error.zig | 9 + ...ver_comptime_variable_from_outer_scope.zig | 12 + .../attempt_to_create_17_bit_float_type.zig | 8 + ...n-integer_non-float_or_non-vector_type.zig | 12 + ...attempt_to_use_0_bit_type_in_extern_fn.zig | 15 + .../stage1/obj/attempted_double_ampersand.zig | 10 + ...ttempted_double_pipe_on_boolean_values.zig | 11 + ..._implicit_cast_from_T_to_slice_const_T.zig | 8 + ...cit_cast_from_const_T_to_array_len_1_T.zig | 12 + ...d_implicit_cast_from_const_T_to_sliceT.zig | 9 + .../stage1/obj/bad_alignCast_at_comptime.zig | 9 + ...licit_cast_from_array_pointer_to_slice.zig | 9 + .../stage1/obj/bad_alignment_type.zig | 13 + ..._function_which_references_local_const.zig | 15 + test/compile_errors/stage1/obj/bad_import.zig | 5 + .../stage1/obj/bad_usage_of_call.zig | 28 + .../obj/bin_and_assign_on_undefined_value.zig | 8 + .../stage1/obj/bin_and_on_undefined_value.zig | 8 + .../stage1/obj/bin_not_on_undefined_value.zig | 8 + .../obj/bin_or_assign_on_undefined_value.zig | 8 + .../stage1/obj/bin_or_on_undefined_value.zig | 8 + .../obj/bin_xor_assign_on_undefined_value.zig | 8 + .../stage1/obj/bin_xor_on_undefined_value.zig | 8 + .../obj/binary_not_on_number_literal.zig | 9 + ...tCast_same_size_but_bit_count_mismatch.zig | 8 + .../stage1/obj/bitCast_to_enum_type.zig | 8 + ...h_different_sizes_inside_an_expression.zig | 8 + ...t_shifting_only_works_on_integer_types.zig | 8 + .../stage1/obj/bogus_compile_var.zig | 6 + .../stage1/obj/bogus_method_call_on_slice.zig | 9 + .../obj/bool_not_on_undefined_value.zig | 8 + .../stage1/obj/branch_on_undefined_value.zig | 7 + .../stage1/obj/cImport_with_bogus_include.zig | 7 + .../stage1/obj/call_assigned_to_constant.zig | 22 + ...generic_function_only_known_at_runtime.zig | 12 + ...function_with_naked_calling_convention.zig | 9 + ...ction_passing_array_instead_of_pointer.zig | 8 + .../cannot_break_out_of_defer_expression.zig | 11 + ...annot_continue_out_of_defer_expression.zig | 11 + ..._prong_with_incompatible_payload_types.zig | 19 + ...um_literal_to_enum_but_it_doesnt_match.zig | 13 + ...et_to_error_union_of_smaller_error_set.zig | 14 + .../cast_global_error_set_to_error_set.zig | 13 + ...cast_negative_integer_literal_to_usize.zig | 8 + ...ast_negative_value_to_unsigned_integer.zig | 15 + .../stage1/obj/cast_unreachable.zig | 9 + ..._bit_offset_pointer_to_regular_pointer.zig | 19 + .../stage1/obj/catch_on_undefined_value.zig | 8 + .../obj/chained_comparison_operators.zig | 7 + .../stage1/obj/cmpxchg_with_float.zig | 8 + .../colliding_invalid_top_level_functions.zig | 8 + ...nal_to_non-optional_with_invalid_types.zig | 33 + ...ng_a_non-optional_pointer_against_null.zig | 8 + ...nst_undefined_produces_undefined_value.zig | 7 + ...parison_operators_with_undefined_value.zig | 45 + ...rison_with_error_union_and_error_value.zig | 8 + .../obj/compile-time_division_by_zero.zig | 10 + ...ompile-time_remainder_division_by_zero.zig | 10 + ...traceback_of_references_that_caused_it.zig | 12 + ..._tagged_enum_doesnt_crash_the_compiler.zig | 15 + ...ompile_error_in_struct_init_expression.zig | 14 + ...ting_return_type_of_inferred_error_set.zig | 12 + .../compile_errors/stage1/obj/compile_log.zig | 14 + ...mpile_log_a_pointer_to_an_opaque_value.zig | 7 + ...ction_which_must_be_comptime_evaluated.zig | 12 + ...nt_warning_deduplication_in_generic_fn.zig | 12 + .../obj/compile_time_division_by_zero.zig | 10 + ...st_enum_to_union_but_field_has_payload.zig | 15 + ...comptime_continue_inside_runtime_catch.zig | 14 + ...mptime_continue_inside_runtime_if_bool.zig | 13 + ...ptime_continue_inside_runtime_if_error.zig | 13 + ...me_continue_inside_runtime_if_optional.zig | 13 + ...omptime_continue_inside_runtime_switch.zig | 16 + ...ime_continue_inside_runtime_while_bool.zig | 13 + ...me_continue_inside_runtime_while_error.zig | 15 + ...continue_inside_runtime_while_optional.zig | 13 + .../obj/comptime_float_in_asm_input.zig | 7 + .../obj/comptime_implicit_cast_f64_to_f32.zig | 9 + .../stage1/obj/comptime_int_in_asm_input.zig | 7 + .../comptime_ptrcast_of_zero-sized_type.zig | 10 + ...atch_memory_at_target_index_terminated.zig | 65 + ...ch_memory_at_target_index_unterminated.zig | 65 + ...entinel_does_not_match_target-sentinel.zig | 65 + ...e-sentinel_is_out_of_bounds_terminated.zig | 65 + ...sentinel_is_out_of_bounds_unterminated.zig | 65 + .../comptime_slice_of_an_undefined_slice.zig | 9 + ...lice_of_undefined_pointer_non-zero_len.zig | 8 + .../comptime_struct_field_no_init_value.zig | 11 + .../obj/const_frame_cast_to_anyframe.zig | 17 + ...const_is_a_statement_not_an_expression.zig | 7 + ...de_comptime_function_has_compile_error.zig | 19 + .../obj/container_init_with_non-type.zig | 8 + ...trol_flow_uses_comptime_var_at_runtime.zig | 13 + ...ntrol_reaches_end_of_non-void_function.zig | 6 + .../stage1/obj/declaration_between_fields.zig | 22 + ...e_as_primitive_must_use_special_syntax.zig | 10 + .../obj/deduplicate_undeclared_identifier.zig | 10 + .../stage1/obj/deref_on_undefined_value.zig | 8 + .../obj/deref_slice_and_get_len_field.zig | 8 + .../stage1/obj/dereference_an_array.zig | 12 + .../dereference_unknown_length_pointer.zig | 7 + .../stage1/obj/direct_struct_loop.zig | 6 + ...edding_opaque_type_in_struct_and_union.zig | 27 + ...ted_pointer_to_null-terminated_pointer.zig | 10 + .../stage1/obj/discarding_error_value.zig | 10 + .../obj/div_assign_on_undefined_value.zig | 8 + .../stage1/obj/div_on_undefined_value.zig | 8 + .../stage1/obj/division_by_zero.zig | 16 + ...licit_cast_double_pointer_to_anyopaque.zig | 11 + .../double_optional_on_main_return_value.zig | 6 + .../obj/duplicate_boolean_switch_value.zig | 21 + .../stage1/obj/duplicate_enum_field.zig | 14 + .../stage1/obj/duplicate_error_in_switch.zig | 20 + .../duplicate_error_value_in_error_set.zig | 13 + ...icate_field_in_struct_value_expression.zig | 18 + .../stage1/obj/duplicate_struct_field.zig | 13 + .../stage1/obj/duplicate_union_field.zig | 13 + .../stage1/obj/embedFile_with_bogus_file.zig | 7 + .../stage1/obj/empty_for_loop_body.zig | 7 + .../stage1/obj/empty_if_body.zig | 7 + .../stage1/obj/empty_switch_on_an_integer.zig | 8 + .../stage1/obj/empty_while_loop_body.zig | 7 + .../endless_loop_in_function_evaluation.zig | 10 + .../obj/enum_field_value_references_enum.zig | 13 + ...field_count_range_but_not_matching_tag.zig | 13 + .../stage1/obj/enum_value_already_taken.zig | 16 + .../stage1/obj/enum_with_0_fields.zig | 5 + ...eclarations_unavailable_for_reify_type.zig | 7 + ...uality_but_sets_have_no_common_members.zig | 14 + .../obj/error_not_handled_in_switch.zig | 18 + ...for_function_parameter_incompatibility.zig | 10 + ..._union_operator_with_non_error_set_LHS.zig | 9 + .../obj/error_when_evaluating_return_type.zig | 15 + .../exceeded_maximum_bit_width_of_integer.zig | 13 + ...ger_when_there_is_a_fraction_component.zig | 7 + ..._known_at_comptime_violates_error_sets.zig | 11 + ...xplicitly_casting_non_tag_type_to_enum.zig | 16 + ...xport_function_with_comptime_parameter.zig | 7 + .../stage1/obj/export_generic_function.zig | 8 + .../stage1/obj/exported_async_function.zig | 5 + ...enum_without_explicit_integer_tag_type.zig | 13 + .../obj/extern_function_pointer_mismatch.zig | 10 + ...xtern_function_with_comptime_parameter.zig | 9 + ...mpatible_but_inferred_integer_tag_type.zig | 41 + ...non-extern-compatible_integer_tag_type.zig | 12 + .../obj/extern_union_field_missing_type.zig | 11 + .../obj/extern_union_given_enum_tag_type.zig | 18 + .../obj/extern_variable_has_no_type.zig | 8 + .../obj/fieldParentPtr-bad_field_name.zig | 10 + ...comptime_field_ptr_not_based_on_struct.zig | 15 + ...ldParentPtr-comptime_wrong_field_index.zig | 14 + ...ParentPtr-field_pointer_is_not_pointer.zig | 10 + .../stage1/obj/fieldParentPtr-non_struct.zig | 8 + .../obj/field_access_of_opaque_type.zig | 14 + .../stage1/obj/field_access_of_slices.zig | 9 + ...field_access_of_unknown_length_pointer.zig | 11 + .../obj/field_type_supplied_in_an_enum.zig | 10 + .../stage1/obj/floatToInt_comptime_safety.zig | 15 + .../obj/float_literal_too_large_error.zig | 8 + ...float_literal_too_small_error_denormal.zig | 8 + .../obj/for_loop_body_expression_ignored.zig | 16 + ..._called_outside_of_function_definition.zig | 9 + .../obj/frame_causes_function_to_be_async.zig | 11 + .../obj/function_alignment_non_power_of_2.zig | 6 + ...nction_call_assigned_to_incorrect_type.zig | 11 + .../obj/function_parameter_is_opaque.zig | 27 + .../obj/function_prototype_with_no_body.zig | 8 + .../obj/function_returning_opaque_type.zig | 17 + ..._ccc_indirectly_calling_async_function.zig | 16 + .../obj/function_with_invalid_return_type.zig | 5 + ...h_non-extern_non-packed_enum_parameter.zig | 6 + ...non-extern_non-packed_struct_parameter.zig | 10 + ..._non-extern_non-packed_union_parameter.zig | 10 + ..._as_parameter_without_comptime_keyword.zig | 9 + ...nction_call_assigned_to_incorrect_type.zig | 11 + ..._instance_with_non-constant_expression.zig | 10 + ...generic_function_returning_opaque_type.zig | 23 + ...n_where_return_type_is_self-referenced.zig | 13 + ...obal_variable_alignment_non_power_of_2.zig | 6 + ...nitializer_must_be_constant_expression.zig | 7 + .../stage1/obj/hasDecl_with_non-container.zig | 7 + .../obj/if_condition_is_bool_not_int.zig | 7 + .../ignored_assert-err-ok_return_value.zig | 8 + .../obj/ignored_comptime_statement_value.zig | 7 + .../stage1/obj/ignored_comptime_value.zig | 7 + .../obj/ignored_deferred_function_call.zig | 8 + .../obj/ignored_deferred_statement_value.zig | 7 + ...nored_expression_in_while_continuation.zig | 20 + .../stage1/obj/ignored_return_value.zig | 8 + .../stage1/obj/ignored_statement_value.zig | 7 + .../obj/illegal_comparison_of_types.zig | 18 + ..._and_Zig_pointer-bad_const-align-child.zig | 40 + ...icit_cast_const_array_to_mutable_slice.zig | 10 + ...licit_cast_from_array_to_mutable_slice.zig | 9 + .../obj/implicit_cast_from_f64_to_f32.zig | 8 + ...mplicit_cast_of_error_set_not_a_subset.zig | 14 + ...ers_which_would_mess_up_null_semantics.zig | 24 + ..._casting_null_c_pointer_to_zig_pointer.zig | 9 + ...casting_too_big_integers_to_C_pointers.zig | 14 + ...ing_undefined_c_pointer_to_zig_pointer.zig | 9 + .../obj/implicit_semicolon-block_expr.zig | 10 + .../implicit_semicolon-block_statement.zig | 10 + ...implicit_semicolon-comptime_expression.zig | 10 + .../implicit_semicolon-comptime_statement.zig | 10 + .../stage1/obj/implicit_semicolon-defer.zig | 10 + .../obj/implicit_semicolon-for_expression.zig | 10 + .../obj/implicit_semicolon-for_statement.zig | 10 + ...t_semicolon-if-else-if-else_expression.zig | 10 + ...it_semicolon-if-else-if-else_statement.zig | 10 + ...plicit_semicolon-if-else-if_expression.zig | 10 + ...mplicit_semicolon-if-else-if_statement.zig | 10 + .../implicit_semicolon-if-else_expression.zig | 10 + .../implicit_semicolon-if-else_statement.zig | 10 + .../obj/implicit_semicolon-if_expression.zig | 10 + .../obj/implicit_semicolon-if_statement.zig | 10 + .../implicit_semicolon-test_expression.zig | 10 + .../obj/implicit_semicolon-test_statement.zig | 10 + ...it_semicolon-while-continue_expression.zig | 10 + ...cit_semicolon-while-continue_statement.zig | 10 + .../implicit_semicolon-while_expression.zig | 10 + .../implicit_semicolon-while_statement.zig | 10 + .../implicitly_casting_enum_to_tag_type.zig | 15 + ...mplicitly_increasing_pointer_alignment.zig | 17 + .../implicitly_increasing_slice_alignment.zig | 20 + .../obj/import_outside_package_path.zig | 7 + .../stage1/obj/incorrect_return_type.zig | 19 + .../increase_pointer_alignment_in_ptrCast.zig | 11 + ...indexing_a_undefined_slice_at_comptime.zig | 8 + .../obj/indexing_an_array_of_size_zero.zig | 9 + ..._array_of_size_zero_with_runtime_index.zig | 10 + .../obj/indexing_single-item_pointer.zig | 7 + ..._recursion_of_async_functions_detected.zig | 34 + .../stage1/obj/indirect_struct_loop.zig | 8 + .../obj/inferred_array_size_invalid_here.zig | 14 + ...nferring_error_set_of_function_pointer.zig | 7 + .../initializing_array_with_struct_syntax.zig | 8 + ...an_invalid_struct_that_contains_itself.zig | 13 + .../obj/intToPtr_with_misaligned_address.zig | 8 + .../obj/int_to_err_global_invalid_number.zig | 13 + .../int_to_err_non_global_invalid_number.zig | 17 + .../stage1/obj/int_to_ptr_of_0_bits.zig | 9 + .../obj/integer_cast_truncates_bits.zig | 29 + .../stage1/obj/integer_overflow_error.zig | 6 + .../stage1/obj/integer_underflow_error.zig | 7 + .../stage1/obj/invalid_break_expression.zig | 7 + .../stage1/obj/invalid_builtin_fn.zig | 7 + ...nvalid_cast_from_integral_type_to_enum.zig | 15 + ...valid_comparison_for_function_pointers.zig | 8 + .../obj/invalid_continue_expression.zig | 7 + .../obj/invalid_deref_on_switch_target.zig | 15 + .../obj/invalid_empty_unicode_escape.zig | 7 + .../invalid_exponent_in_float_literal-1.zig | 9 + .../invalid_exponent_in_float_literal-2.zig | 9 + .../obj/invalid_field_access_in_comptime.zig | 5 + ...valid_field_in_struct_value_expression.zig | 17 + .../stage1/obj/invalid_float_literal.zig | 11 + .../obj/invalid_legacy_unicode_escape.zig | 8 + .../stage1/obj/invalid_maybe_type.zig | 7 + .../obj/invalid_member_of_builtin_enum.zig | 9 + .../obj/invalid_multiple_dereferences.zig | 17 + ...invalid_optional_type_in_extern_struct.zig | 8 + .../obj/invalid_pointer_for_var_type.zig | 11 + .../stage1/obj/invalid_pointer_syntax.zig | 7 + .../stage1/obj/invalid_shift_amount_error.zig | 9 + .../stage1/obj/invalid_struct_field.zig | 17 + .../invalid_suspend_in_exported_function.zig | 13 + .../stage1/obj/invalid_type.zig | 6 + .../obj/invalid_type_used_in_array_type.zig | 12 + ...nderscore_placement_in_float_literal-1.zig | 9 + ...derscore_placement_in_float_literal-10.zig | 9 + ...derscore_placement_in_float_literal-11.zig | 9 + ...derscore_placement_in_float_literal-12.zig | 9 + ...derscore_placement_in_float_literal-13.zig | 9 + ...derscore_placement_in_float_literal-14.zig | 9 + ...nderscore_placement_in_float_literal-2.zig | 9 + ...nderscore_placement_in_float_literal-3.zig | 9 + ...nderscore_placement_in_float_literal-4.zig | 9 + ...nderscore_placement_in_float_literal-5.zig | 9 + ...nderscore_placement_in_float_literal-6.zig | 9 + ...nderscore_placement_in_float_literal-7.zig | 9 + ...nderscore_placement_in_float_literal-9.zig | 9 + ..._underscore_placement_in_int_literal-1.zig | 9 + ..._underscore_placement_in_int_literal-2.zig | 9 + ..._underscore_placement_in_int_literal-3.zig | 9 + ..._underscore_placement_in_int_literal-4.zig | 9 + ...invalid_union_field_access_in_comptime.zig | 13 + ...gnostic_string_for_top_level_decl_type.zig | 8 + ..._from_undefined_array_pointer_to_slice.zig | 25 + ..._3818_bitcast_from_parray-slice_to_u16.zig | 15 + ...terminated-slice_to_terminated-pointer.zig | 9 + ...d_by_typeInfo_and_passed_into_function.zig | 14 + ...ional_anyopaque_to_anyopaque_must_fail.zig | 9 + ...time_slice-len_increment_beyond_bounds.zig | 12 + ..._9346_return_outside_of_function_scope.zig | 5 + .../stage1/obj/labeled_break_not_found.zig | 11 + .../stage1/obj/labeled_continue_not_found.zig | 12 + ...zy_pointer_with_undefined_element_type.zig | 10 + ...es_from_comptime_reinterpreted_pointer.zig | 11 + ...tor_pointer_with_unknown_runtime_index.zig | 15 + ...local_shadows_global_that_occurs_later.zig | 10 + .../obj/local_variable_redeclaration.zig | 9 + .../local_variable_redeclares_parameter.zig | 9 + .../obj/local_variable_shadowing_global.zig | 12 + .../locally_shadowing_a_primitive_type.zig | 10 + .../main_function_with_bogus_args_type.zig | 5 + ...hod_call_with_first_arg_type_primitive.zig | 19 + ...ll_with_first_arg_type_wrong_container.zig | 28 + .../obj/missing_boolean_switch_value.zig | 17 + ..._const_in_slice_with_nested_array_type.zig | 16 + .../stage1/obj/missing_else_clause.zig | 14 + ...ssing_field_in_struct_value_expression.zig | 18 + .../obj/missing_function_call_param.zig | 29 + .../stage1/obj/missing_function_name.zig | 6 + .../stage1/obj/missing_param_name.zig | 6 + ...ing_parameter_name_of_generic_function.zig | 9 + .../obj/missing_result_type_for_phi_node.zig | 10 + ...elled_type_with_pointer_only_reference.zig | 35 + .../obj/mod_assign_on_undefined_value.zig | 8 + .../stage1/obj/mod_on_undefined_value.zig | 8 + .../mul_overflow_in_function_evaluation.zig | 10 + .../obj/mult_assign_on_undefined_value.zig | 8 + .../stage1/obj/mult_on_undefined_value.zig | 8 + .../mult_wrap_assign_on_undefined_value.zig | 8 + .../obj/mult_wrap_on_undefined_value.zig | 8 + .../obj/multiple_function_definitions.zig | 8 + .../stage1/obj/negate_on_undefined_value.zig | 8 + .../obj/negate_wrap_on_undefined_value.zig | 8 + ...gation_overflow_in_function_evaluation.zig | 10 + .../stage1/obj/nested_error_set_mismatch.zig | 18 + ...se_prong_on_switch_on_global_error_set.zig | 12 + .../obj/noalias_on_non_pointer_param.zig | 6 + ...eventually_is_inferred_to_become_async.zig | 13 + ...h_struct_return_value_outside_function.zig | 15 + ...ion_in_struct_literal_outside_function.zig | 11 + .../obj/non-const_switch_number_literal.zig | 15 + ...of_things_that_require_const_variables.zig | 49 + .../obj/non-enum_tag_type_passed_to_union.zig | 11 + .../obj/non-extern_function_with_var_args.zig | 8 + ..._loop_on_a_type_that_requires_comptime.zig | 12 + ...teger_tag_type_to_automatic_union_enum.zig | 11 + .../obj/non-pure_function_returns_type.zig | 22 + ...c_function_pointer_passed_to_asyncCall.zig | 10 + .../non_compile_time_array_concatenation.zig | 9 + .../non_constant_expression_in_array_size.zig | 12 + ...sets_used_in_merge_error_sets_operator.zig | 15 + .../obj/non_float_passed_to_floatToInt.zig | 8 + .../obj/non_int_passed_to_intToFloat.zig | 8 + .../obj/non_pointer_given_to_ptrToInt.zig | 7 + .../stage1/obj/normal_string_with_newline.zig | 7 + .../stage1/obj/offsetOf-bad_field_name.zig | 10 + .../stage1/obj/offsetOf-non_struct.zig | 8 + ...binary_operator_allowed_for_error_sets.zig | 8 + .../stage1/obj/opaque_type_with_field.zig | 9 + ...ional_pointer_to_void_in_extern_struct.zig | 12 + .../stage1/obj/or_on_undefined_value.zig | 8 + .../stage1/obj/orelse_on_undefined_value.zig | 8 + ...ange_comptime_int_passed_to_floatToInt.zig | 8 + .../obj/overflow_in_enum_value_allocation.zig | 12 + .../obj/packed_union_given_enum_tag_type.zig | 18 + ...cked_union_with_automatic_layout_field.zig | 16 + .../obj/panic_called_at_compile_time.zig | 9 + .../stage1/obj/parameter_redeclaration.zig | 8 + .../stage1/obj/parameter_shadowing_global.zig | 10 + .../obj/pass_const_ptr_to_mutable_ptr_fn.zig | 15 + ..._not-aligned-enough_pointer_to_cmpxchg.zig | 10 + ...sing_an_under-aligned_function_pointer.zig | 11 + ...ast_const_pointer_to_mutable_C_pointer.zig | 9 + ...pointer_arithmetic_on_pointer-to-array.zig | 10 + ..._when_coercing_pointer_to_anon_literal.zig | 22 + .../stage1/obj/pointer_to_noreturn.zig | 6 + .../stage1/obj/popCount-non-integer.zig | 7 + ...bad_implicit_casting_of_anyframe_types.zig | 22 + ...ives_take_precedence_over_declarations.zig | 9 + ...Cast_a_0_bit_type_to_a_non-_0_bit_type.zig | 11 + .../obj/ptrCast_discards_const_qualifier.zig | 9 + .../ptrToInt_0_to_non_optional_pointer.zig | 8 + .../stage1/obj/ptrToInt_on_void.zig | 7 + .../stage1/obj/ptrcast_to_non-pointer.zig | 7 + ...e_operator_in_switch_used_on_error_set.zig | 17 + ...ading_past_end_of_pointer_casted_array.zig | 11 + .../obj/recursive_inferred_error_set.zig | 10 + .../stage1/obj/redefinition_of_enums.zig | 7 + .../obj/redefinition_of_global_variables.zig | 7 + .../stage1/obj/redefinition_of_struct.zig | 7 + ...efer_to_the_type_of_a_generic_function.zig | 9 + .../referring_to_a_struct_that_is_invalid.zig | 15 + ...when_assigning_a_value_within_a_struct.zig | 19 + .../reify_type.Fn_with_is_generic_true.zig | 15 + ...th_is_var_args_true_and_non-C_callconv.zig | 15 + .../reify_type.Fn_with_return_type_null.zig | 15 + ...ype.Pointer_with_invalid_address_space.zig | 16 + ...austive_enum_with_non-integer_tag_type.zig | 16 + ...xhaustive_enum_with_undefined_tag_type.zig | 16 + ...e_for_exhaustive_enum_with_zero_fields.zig | 16 + ...for_tagged_union_with_extra_enum_field.zig | 32 + ...or_tagged_union_with_extra_union_field.zig | 33 + ...reify_type_for_union_with_opaque_field.zig | 17 + .../reify_type_for_union_with_zero_fields.zig | 15 + .../reify_type_union_payload_is_undefined.zig | 8 + .../stage1/obj/reify_type_with_Type.Int.zig | 11 + ...eify_type_with_non-constant_expression.zig | 9 + .../stage1/obj/reify_type_with_undefined.zig | 18 + ...ompatibility_mismatching_handle_is_ptr.zig | 16 + ...mismatching_handle_is_ptr_generic_call.zig | 16 + .../obj/return_from_defer_expression.zig | 19 + ...turning_error_from_void_async_function.zig | 10 + .../runtime-known_async_function_called.zig | 12 + ...own_function_called_with_async_keyword.zig | 10 + ...ime_assignment_to_comptime_struct_type.zig | 13 + ...time_assignment_to_comptime_union_type.zig | 13 + ...ast_to_union_which_has_non-void_fields.zig | 18 + ...runtime_index_into_comptime_type_slice.zig | 15 + ...ating_arithmetic_does_not_allow_floats.zig | 7 + ...oes_not_allow_negative_rhs_at_comptime.zig | 10 + ...oes_not_allow_negative_rhs_at_comptime.zig | 7 + .../obj/setAlignStack_in_inline_function.zig | 10 + .../obj/setAlignStack_in_naked_function.zig | 7 + .../obj/setAlignStack_outside_function.zig | 7 + .../stage1/obj/setAlignStack_set_twice.zig | 9 + .../stage1/obj/setAlignStack_too_big.zig | 7 + .../obj/setFloatMode_twice_for_same_scope.zig | 9 + .../setRuntimeSafety_twice_for_same_scope.zig | 9 + .../setting_a_section_on_a_local_variable.zig | 8 + ...shift_amount_has_to_be_an_integer_type.zig | 8 + .../shift_by_negative_comptime_integer.zig | 8 + .../shift_left_assign_on_undefined_value.zig | 8 + .../obj/shift_left_on_undefined_value.zig | 8 + .../shift_right_assign_on_undefined_value.zig | 8 + .../obj/shift_right_on_undefined_value.zig | 8 + ...fting_RHS_is_log2_of_LHS_int_bit_width.zig | 7 + ...ing_without_int_type_or_comptime_known.zig | 7 + .../stage1/obj/shlExact_shifts_out_1_bits.zig | 8 + .../stage1/obj/shrExact_shifts_out_1_bits.zig | 8 + .../stage1/obj/signed_integer_division.zig | 7 + .../obj/signed_integer_remainder_division.zig | 7 + .../stage1/obj/sizeOf_bad_type.zig | 7 + ...ce_cannot_have_its_bytes_reinterpreted.zig | 9 + .../obj/slice_passed_as_array_init_type.zig | 8 + ...e_passed_as_array_init_type_with_elems.zig | 8 + .../stage1/obj/slice_sentinel_mismatch-1.zig | 8 + .../stage1/obj/slice_sentinel_mismatch-2.zig | 10 + .../slicing_of_global_undefined_pointer.zig | 8 + .../obj/slicing_single-item_pointer.zig | 8 + ...pecify_enum_tag_type_that_is_too_small.zig | 16 + .../obj/specify_non-integer_enum_tag_type.zig | 14 + .../stage1/obj/src_outside_function.zig | 7 + ...tor_pointer_with_unknown_runtime_index.zig | 14 + ...in_compile_time_variable_then_using_it.zig | 47 + ...t_depends_on_itself_via_optional_field.zig | 17 + .../stage1/obj/struct_field_missing_type.zig | 11 + .../obj/struct_init_syntax_for_array.zig | 8 + ...eclarations_unavailable_for_reify_type.zig | 7 + .../stage1/obj/struct_with_invalid_field.zig | 28 + .../obj/sub_assign_on_undefined_value.zig | 8 + .../stage1/obj/sub_on_undefined_value.zig | 8 + .../sub_overflow_in_function_evaluation.zig | 10 + .../sub_wrap_assign_on_undefined_value.zig | 8 + .../obj/sub_wrap_on_undefined_value.zig | 8 + .../obj/suspend_inside_suspend_block.zig | 14 + ...expression-duplicate_enumeration_prong.zig | 22 + ...te_enumeration_prong_when_else_present.zig | 23 + ...duplicate_or_overlapping_integer_value.zig | 14 + .../obj/switch_expression-duplicate_type.zig | 15 + ...expression-duplicate_type_struct_alias.zig | 19 + ...h_expression-missing_enumeration_prong.zig | 19 + ...switch_expression-multiple_else_prongs.zig | 14 + ...pression-non_exhaustive_integer_prongs.zig | 10 + ...on-switch_on_pointer_type_with_no_else.zig | 11 + ...expression-unreachable_else_prong_bool.zig | 12 + ...expression-unreachable_else_prong_enum.zig | 22 + ...ession-unreachable_else_prong_range_i8.zig | 15 + ...ession-unreachable_else_prong_range_u8.zig | 15 + ...h_expression-unreachable_else_prong_u1.zig | 12 + ...h_expression-unreachable_else_prong_u2.zig | 14 + ...ch_on_enum_with_1_field_with_no_prongs.zig | 10 + .../switch_on_union_with_no_attached_enum.zig | 20 + ...itch_with_invalid_expression_parameter.zig | 15 + .../switch_with_overlapping_case_ranges.zig | 11 + ...d_on_union_with_no_associated_enum_tag.zig | 14 + .../obj/take_slice_of_invalid_dereference.zig | 8 + ...ing_bit_offset_of_void_field_in_struct.zig | 11 + ...ng_byte_offset_of_void_field_in_struct.zig | 11 + .../obj/threadlocal_qualifier_on_const.zig | 8 + .../obj/top_level_decl_dependency_loop.zig | 10 + .../stage1/obj/truncate_sign_mismatch.zig | 23 + .../stage1/obj/truncate_undefined_value.zig | 8 + ...in_function_with_non_error_return_type.zig | 8 + .../obj/type_checking_function_pointers.zig | 11 + .../obj/type_variables_must_be_constant.zig | 8 + .../stage1/obj/undeclared_identifier.zig | 9 + ...ntifier_error_should_mark_fn_as_impure.zig | 10 + ...clared_identifier_in_unanalyzed_branch.zig | 9 + .../undefined_as_field_type_is_rejected.zig | 11 + .../stage1/obj/undefined_function_call.zig | 7 + .../underscore_is_not_a_declarable_symbol.zig | 8 + ...rscore_should_not_be_usable_inside_for.zig | 11 + ...core_should_not_be_usable_inside_while.zig | 14 + ...should_not_be_usable_inside_while_else.zig | 16 + .../union_auto-enum_value_already_taken.zig | 16 + .../union_enum_field_does_not_match_enum.zig | 20 + .../union_fields_with_value_assignments.zig | 12 + .../stage1/obj/union_with_0_fields.zig | 5 + .../union_with_specified_enum_omits_field.zig | 17 + ...ith_too_small_explicit_signed_tag_type.zig | 14 + ...h_too_small_explicit_unsigned_tag_type.zig | 15 + .../obj/unknown_length_pointer_to_opaque.zig | 5 + .../obj/unreachable_code-double_break.zig | 10 + .../obj/unreachable_code-nested_returns.zig | 8 + .../stage1/obj/unreachable_code.zig | 11 + .../obj/unreachable_executed_at_comptime.zig | 14 + .../stage1/obj/unreachable_parameter.zig | 6 + .../stage1/obj/unreachable_variable.zig | 8 + .../stage1/obj/unreachable_with_return.zig | 6 + ...fier_at_start_of_asm_output_constraint.zig | 8 + ...use_anyopaque_as_return_type_of_fn_ptr.zig | 8 + ...to_assign_null_to_non-nullable_pointer.zig | 12 + ..._invalid_number_literal_as_array_index.zig | 9 + ...omptime-known_undefined_function_value.zig | 11 + .../obj/use_of_undeclared_identifier.zig | 7 + ..._unknown_len_ptr_type_instead_of_array.zig | 11 + ...types_in_function_call_raises_an_error.zig | 9 + .../obj/usingnamespace_with_wrong_type.zig | 5 + .../stage1/obj/variable_has_wrong_type.zig | 8 + ...lization_compile_error_then_referenced.zig | 17 + .../obj/variable_with_type_noreturn.zig | 8 + .../stage1/obj/vector_index_out_of_bounds.zig | 8 + .../obj/volatile_on_global_assembly.zig | 7 + ...is_a_compile_error_in_non-Wasm_targets.zig | 8 + ...is_a_compile_error_in_non-Wasm_targets.zig | 8 + .../while_expected_bool_got_error_union.zig | 8 + .../obj/while_expected_bool_got_optional.zig | 8 + .../while_expected_error_union_got_bool.zig | 8 + ...hile_expected_error_union_got_optional.zig | 8 + .../obj/while_expected_optional_got_bool.zig | 8 + ...hile_expected_optional_got_error_union.zig | 8 + .../while_loop_body_expression_ignored.zig | 20 + .../obj/write_to_const_global_variable.zig | 9 + .../wrong_frame_type_used_for_async_call.zig | 14 + .../stage1/obj/wrong_function_type.zig | 9 + ...ializer_for_union_payload_of_type_type.zig | 14 + .../stage1/obj/wrong_number_of_arguments.zig | 8 + ...number_of_arguments_for_method_fn_call.zig | 12 + ...wrong_panic_signature_generic_function.zig | 10 + ...wrong_panic_signature_runtime_function.zig | 8 + ...ointer_coerced_to_pointer_to_opaque_{}.zig | 10 + .../stage1/obj/wrong_return_type_for_main.zig | 5 + .../obj/wrong_size_to_an_array_literal.zig | 8 + ...g_type_for_argument_tuple_to_asyncCall.zig | 12 + .../stage1/obj/wrong_type_for_reify_type.zig | 7 + ...wrong_type_for_result_ptr_to_asyncCall.zig | 14 + .../stage1/obj/wrong_type_passed_to_panic.zig | 8 + .../stage1/obj/wrong_type_to_hasField.zig | 7 + ..._given_to_atomic_order_args_in_cmpxchg.zig | 8 + .../obj/wrong_types_given_to_export.zig | 8 + .../test/access_invalid_typeInfo_decl.zig | 8 + .../test/alignCast_of_zero_sized_types.zig | 25 + .../stage1/test/bad_splat_type.zig | 9 + .../test/binary_OR_operator_on_error_sets.zig | 10 + ...ts_non_comptime-known_fn-always_inline.zig | 8 + ...cts_non_comptime-known_fn-compile_time.zig | 8 + ...en_optional_T_where_T_is_not_a_pointer.zig | 12 + .../combination_of_nosuspend_and_async.zig | 13 + ...n_of_non-tagged_union_and_enum_literal.zig | 11 + ...mptime_vector_overflow_shows_the_index.zig | 11 + .../stage1/test/duplicate-unused_labels.zig | 30 + ...cate_field_in_anonymous_struct_literal.zig | 16 + ..._initializer_doesnt_crash_the_compiler.zig | 12 + ...rors_in_for_loop_bodies_are_propagated.zig | 8 + .../test/export_with_empty_name_string.zig | 8 + .../helpful_return_type_error_message.zig | 27 + ...float_conversion_to_comptime_int-float.zig | 13 + .../stage1/test/invalid_assignments.zig | 17 + .../stage1/test/invalid_float_casts.zig | 23 + .../stage1/test/invalid_int_casts.zig | 23 + .../invalid_non-exhaustive_enum_to_union.zig | 24 + .../test/invalid_pointer_with_reify_type.zig | 16 + .../stage1/test/nested_vectors.zig | 10 + ...xhaustive_enum_marker_assigned_a_value.zig | 17 + .../stage1/test/non-exhaustive_enums.zig | 19 + .../stage1/test/not_an_enum_type.zig | 17 + ...truct_with_fields_of_not_allowed_types.zig | 71 + ...rToInt_with_pointer_to_zero-sized_type.zig | 9 + .../test/reassign_to_array_parameter.zig | 10 + .../test/reassign_to_slice_parameter.zig | 10 + .../test/reassign_to_struct_parameter.zig | 13 + .../stage1/test/reference_to_const_data.zig | 27 + ...ify_typeOf_with_incompatible_arguments.zig | 9 + .../test/reify_typeOf_with_no_arguments.zig | 7 + ..._extern_function_definitions_with_body.zig | 7 + ...ect_extern_variables_with_initializers.zig | 5 + ...n_returning_type_crashes_compiler_2655.zig | 11 + .../test/return_invalid_type_from_test.zig | 5 + ...ift_on_type_with_non-power-of-two_size.zig | 31 + ...elected_index_past_first_vector_length.zig | 12 + .../switch_ranges_endpoints_are_validated.zig | 13 + ...hing_with_exhaustive_enum_has___prong_.zig | 16 + .../switching_with_non-exhaustive_enums.zig | 32 + ...n_invalid_value_of_non-exhaustive_enum.zig | 8 + ...e_mismatch_in_C_prototype_with_varargs.zig | 11 + ...type_mismatch_with_tuple_concatenation.zig | 8 + .../unused_variable_error_on_errdefer.zig | 11 + .../stage2/embed_outside_package.zig | 7 + .../stage2/import_outside_package.zig | 7 + .../stage2/out_of_bounds_index.zig | 28 + 655 files changed, 7896 insertions(+), 8563 deletions(-) create mode 100644 test/compile_errors/stage1/exe/main_missing_name.zig create mode 100644 test/compile_errors/stage1/exe/missing_main_fn_in_executable.zig create mode 100644 test/compile_errors/stage1/exe/private_main_fn.zig create mode 100644 test/compile_errors/stage1/exe/std.fmt_error_for_unused_arguments.zig create mode 100644 test/compile_errors/stage1/obj/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig create mode 100644 test/compile_errors/stage1/obj/C_pointer_to_anyopaque.zig create mode 100644 test/compile_errors/stage1/obj/Frame_of_generic_function.zig create mode 100644 test/compile_errors/stage1/obj/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig create mode 100644 test/compile_errors/stage1/obj/Issue_6823_dont_allow_._to_be_followed_by_.zig create mode 100644 test/compile_errors/stage1/obj/Issue_9165_windows_tcp_server_compilation_error.zig create mode 100644 test/compile_errors/stage1/obj/access_non-existent_member_of_error_set.zig create mode 100644 test/compile_errors/stage1/obj/accessing_runtime_parameter_from_outer_function.zig create mode 100644 test/compile_errors/stage1/obj/add_assign_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/add_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/add_overflow_in_function_evaluation.zig create mode 100644 test/compile_errors/stage1/obj/add_wrap_assign_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/add_wrap_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/addition_with_non_numbers.zig create mode 100644 test/compile_errors/stage1/obj/address_of_number_literal.zig create mode 100644 test/compile_errors/stage1/obj/alignCast_expects_pointer_or_slice.zig create mode 100644 test/compile_errors/stage1/obj/aligned_variable_of_zero-bit_type.zig create mode 100644 test/compile_errors/stage1/obj/alignment_of_enum_field_specified.zig create mode 100644 test/compile_errors/stage1/obj/ambiguous_decl_reference.zig create mode 100644 test/compile_errors/stage1/obj/and_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/array_access_of_non_array.zig create mode 100644 test/compile_errors/stage1/obj/array_access_of_type.zig create mode 100644 test/compile_errors/stage1/obj/array_access_of_undeclared_identifier.zig create mode 100644 test/compile_errors/stage1/obj/array_access_with_non_integer_index.zig create mode 100644 test/compile_errors/stage1/obj/array_concatenation_with_wrong_type.zig create mode 100644 test/compile_errors/stage1/obj/array_in_c_exported_function.zig create mode 100644 test/compile_errors/stage1/obj/asm_at_compile_time.zig create mode 100644 test/compile_errors/stage1/obj/assign_inline_fn_to_non-comptime_var.zig create mode 100644 test/compile_errors/stage1/obj/assign_null_to_non-optional_pointer.zig create mode 100644 test/compile_errors/stage1/obj/assign_through_constant_pointer.zig create mode 100644 test/compile_errors/stage1/obj/assign_through_constant_slice.zig create mode 100644 test/compile_errors/stage1/obj/assign_to_constant_field.zig create mode 100644 test/compile_errors/stage1/obj/assign_to_constant_variable.zig create mode 100644 test/compile_errors/stage1/obj/assign_to_invalid_dereference.zig create mode 100644 test/compile_errors/stage1/obj/assign_too_big_number_to_u16.zig create mode 100644 test/compile_errors/stage1/obj/assign_unreachable.zig create mode 100644 test/compile_errors/stage1/obj/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig create mode 100644 test/compile_errors/stage1/obj/async_function_depends_on_its_own_frame.zig create mode 100644 test/compile_errors/stage1/obj/async_function_indirectly_depends_on_its_own_frame.zig create mode 100644 test/compile_errors/stage1/obj/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig create mode 100644 test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig create mode 100644 test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig create mode 100644 test/compile_errors/stage1/obj/atomic_orderings_of_fence_Acquire_or_stricter.zig create mode 100644 test/compile_errors/stage1/obj/atomicrmw_with_bool_op_not_.Xchg.zig create mode 100644 test/compile_errors/stage1/obj/atomicrmw_with_enum_op_not_.Xchg.zig create mode 100644 test/compile_errors/stage1/obj/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig create mode 100644 test/compile_errors/stage1/obj/attempt_to_cast_enum_literal_to_error.zig create mode 100644 test/compile_errors/stage1/obj/attempt_to_close_over_comptime_variable_from_outer_scope.zig create mode 100644 test/compile_errors/stage1/obj/attempt_to_create_17_bit_float_type.zig create mode 100644 test/compile_errors/stage1/obj/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig create mode 100644 test/compile_errors/stage1/obj/attempt_to_use_0_bit_type_in_extern_fn.zig create mode 100644 test/compile_errors/stage1/obj/attempted_double_ampersand.zig create mode 100644 test/compile_errors/stage1/obj/attempted_double_pipe_on_boolean_values.zig create mode 100644 test/compile_errors/stage1/obj/attempted_implicit_cast_from_T_to_slice_const_T.zig create mode 100644 test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_array_len_1_T.zig create mode 100644 test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_sliceT.zig create mode 100644 test/compile_errors/stage1/obj/bad_alignCast_at_comptime.zig create mode 100644 test/compile_errors/stage1/obj/bad_alignment_in_implicit_cast_from_array_pointer_to_slice.zig create mode 100644 test/compile_errors/stage1/obj/bad_alignment_type.zig create mode 100644 test/compile_errors/stage1/obj/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig create mode 100644 test/compile_errors/stage1/obj/bad_import.zig create mode 100644 test/compile_errors/stage1/obj/bad_usage_of_call.zig create mode 100644 test/compile_errors/stage1/obj/bin_and_assign_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/bin_and_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/bin_not_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/bin_or_assign_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/bin_or_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/bin_xor_assign_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/bin_xor_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/binary_not_on_number_literal.zig create mode 100644 test/compile_errors/stage1/obj/bitCast_same_size_but_bit_count_mismatch.zig create mode 100644 test/compile_errors/stage1/obj/bitCast_to_enum_type.zig create mode 100644 test/compile_errors/stage1/obj/bitCast_with_different_sizes_inside_an_expression.zig create mode 100644 test/compile_errors/stage1/obj/bit_shifting_only_works_on_integer_types.zig create mode 100644 test/compile_errors/stage1/obj/bogus_compile_var.zig create mode 100644 test/compile_errors/stage1/obj/bogus_method_call_on_slice.zig create mode 100644 test/compile_errors/stage1/obj/bool_not_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/branch_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/cImport_with_bogus_include.zig create mode 100644 test/compile_errors/stage1/obj/call_assigned_to_constant.zig create mode 100644 test/compile_errors/stage1/obj/calling_a_generic_function_only_known_at_runtime.zig create mode 100644 test/compile_errors/stage1/obj/calling_function_with_naked_calling_convention.zig create mode 100644 test/compile_errors/stage1/obj/calling_var_args_extern_function_passing_array_instead_of_pointer.zig create mode 100644 test/compile_errors/stage1/obj/cannot_break_out_of_defer_expression.zig create mode 100644 test/compile_errors/stage1/obj/cannot_continue_out_of_defer_expression.zig create mode 100644 test/compile_errors/stage1/obj/capture_group_on_switch_prong_with_incompatible_payload_types.zig create mode 100644 test/compile_errors/stage1/obj/cast_enum_literal_to_enum_but_it_doesnt_match.zig create mode 100644 test/compile_errors/stage1/obj/cast_error_union_of_global_error_set_to_error_union_of_smaller_error_set.zig create mode 100644 test/compile_errors/stage1/obj/cast_global_error_set_to_error_set.zig create mode 100644 test/compile_errors/stage1/obj/cast_negative_integer_literal_to_usize.zig create mode 100644 test/compile_errors/stage1/obj/cast_negative_value_to_unsigned_integer.zig create mode 100644 test/compile_errors/stage1/obj/cast_unreachable.zig create mode 100644 test/compile_errors/stage1/obj/casting_bit_offset_pointer_to_regular_pointer.zig create mode 100644 test/compile_errors/stage1/obj/catch_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/chained_comparison_operators.zig create mode 100644 test/compile_errors/stage1/obj/cmpxchg_with_float.zig create mode 100644 test/compile_errors/stage1/obj/colliding_invalid_top_level_functions.zig create mode 100644 test/compile_errors/stage1/obj/compare_optional_to_non-optional_with_invalid_types.zig create mode 100644 test/compile_errors/stage1/obj/comparing_a_non-optional_pointer_against_null.zig create mode 100644 test/compile_errors/stage1/obj/comparing_against_undefined_produces_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/comparison_operators_with_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/comparison_with_error_union_and_error_value.zig create mode 100644 test/compile_errors/stage1/obj/compile-time_division_by_zero.zig create mode 100644 test/compile_errors/stage1/obj/compile-time_remainder_division_by_zero.zig create mode 100644 test/compile_errors/stage1/obj/compileError_shows_traceback_of_references_that_caused_it.zig create mode 100644 test/compile_errors/stage1/obj/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig create mode 100644 test/compile_errors/stage1/obj/compile_error_in_struct_init_expression.zig create mode 100644 test/compile_errors/stage1/obj/compile_error_when_evaluating_return_type_of_inferred_error_set.zig create mode 100644 test/compile_errors/stage1/obj/compile_log.zig create mode 100644 test/compile_errors/stage1/obj/compile_log_a_pointer_to_an_opaque_value.zig create mode 100644 test/compile_errors/stage1/obj/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig create mode 100644 test/compile_errors/stage1/obj/compile_log_statement_warning_deduplication_in_generic_fn.zig create mode 100644 test/compile_errors/stage1/obj/compile_time_division_by_zero.zig create mode 100644 test/compile_errors/stage1/obj/comptime_cast_enum_to_union_but_field_has_payload.zig create mode 100644 test/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig create mode 100644 test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig create mode 100644 test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig create mode 100644 test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig create mode 100644 test/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig create mode 100644 test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig create mode 100644 test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig create mode 100644 test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig create mode 100644 test/compile_errors/stage1/obj/comptime_float_in_asm_input.zig create mode 100644 test/compile_errors/stage1/obj/comptime_implicit_cast_f64_to_f32.zig create mode 100644 test/compile_errors/stage1/obj/comptime_int_in_asm_input.zig create mode 100644 test/compile_errors/stage1/obj/comptime_ptrcast_of_zero-sized_type.zig create mode 100644 test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig create mode 100644 test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig create mode 100644 test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_target-sentinel.zig create mode 100644 test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_terminated.zig create mode 100644 test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig create mode 100644 test/compile_errors/stage1/obj/comptime_slice_of_an_undefined_slice.zig create mode 100644 test/compile_errors/stage1/obj/comptime_slice_of_undefined_pointer_non-zero_len.zig create mode 100644 test/compile_errors/stage1/obj/comptime_struct_field_no_init_value.zig create mode 100644 test/compile_errors/stage1/obj/const_frame_cast_to_anyframe.zig create mode 100644 test/compile_errors/stage1/obj/const_is_a_statement_not_an_expression.zig create mode 100644 test/compile_errors/stage1/obj/constant_inside_comptime_function_has_compile_error.zig create mode 100644 test/compile_errors/stage1/obj/container_init_with_non-type.zig create mode 100644 test/compile_errors/stage1/obj/control_flow_uses_comptime_var_at_runtime.zig create mode 100644 test/compile_errors/stage1/obj/control_reaches_end_of_non-void_function.zig create mode 100644 test/compile_errors/stage1/obj/declaration_between_fields.zig create mode 100644 test/compile_errors/stage1/obj/declaration_with_same_name_as_primitive_must_use_special_syntax.zig create mode 100644 test/compile_errors/stage1/obj/deduplicate_undeclared_identifier.zig create mode 100644 test/compile_errors/stage1/obj/deref_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig create mode 100644 test/compile_errors/stage1/obj/dereference_an_array.zig create mode 100644 test/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig create mode 100644 test/compile_errors/stage1/obj/direct_struct_loop.zig create mode 100644 test/compile_errors/stage1/obj/directly_embedding_opaque_type_in_struct_and_union.zig create mode 100644 test/compile_errors/stage1/obj/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig create mode 100644 test/compile_errors/stage1/obj/discarding_error_value.zig create mode 100644 test/compile_errors/stage1/obj/div_assign_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/div_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/division_by_zero.zig create mode 100644 test/compile_errors/stage1/obj/dont_implicit_cast_double_pointer_to_anyopaque.zig create mode 100644 test/compile_errors/stage1/obj/double_optional_on_main_return_value.zig create mode 100644 test/compile_errors/stage1/obj/duplicate_boolean_switch_value.zig create mode 100644 test/compile_errors/stage1/obj/duplicate_enum_field.zig create mode 100644 test/compile_errors/stage1/obj/duplicate_error_in_switch.zig create mode 100644 test/compile_errors/stage1/obj/duplicate_error_value_in_error_set.zig create mode 100644 test/compile_errors/stage1/obj/duplicate_field_in_struct_value_expression.zig create mode 100644 test/compile_errors/stage1/obj/duplicate_struct_field.zig create mode 100644 test/compile_errors/stage1/obj/duplicate_union_field.zig create mode 100644 test/compile_errors/stage1/obj/embedFile_with_bogus_file.zig create mode 100644 test/compile_errors/stage1/obj/empty_for_loop_body.zig create mode 100644 test/compile_errors/stage1/obj/empty_if_body.zig create mode 100644 test/compile_errors/stage1/obj/empty_switch_on_an_integer.zig create mode 100644 test/compile_errors/stage1/obj/empty_while_loop_body.zig create mode 100644 test/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig create mode 100644 test/compile_errors/stage1/obj/enum_field_value_references_enum.zig create mode 100644 test/compile_errors/stage1/obj/enum_in_field_count_range_but_not_matching_tag.zig create mode 100644 test/compile_errors/stage1/obj/enum_value_already_taken.zig create mode 100644 test/compile_errors/stage1/obj/enum_with_0_fields.zig create mode 100644 test/compile_errors/stage1/obj/enum_with_declarations_unavailable_for_reify_type.zig create mode 100644 test/compile_errors/stage1/obj/error_equality_but_sets_have_no_common_members.zig create mode 100644 test/compile_errors/stage1/obj/error_not_handled_in_switch.zig create mode 100644 test/compile_errors/stage1/obj/error_note_for_function_parameter_incompatibility.zig create mode 100644 test/compile_errors/stage1/obj/error_union_operator_with_non_error_set_LHS.zig create mode 100644 test/compile_errors/stage1/obj/error_when_evaluating_return_type.zig create mode 100644 test/compile_errors/stage1/obj/exceeded_maximum_bit_width_of_integer.zig create mode 100644 test/compile_errors/stage1/obj/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig create mode 100644 test/compile_errors/stage1/obj/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig create mode 100644 test/compile_errors/stage1/obj/explicitly_casting_non_tag_type_to_enum.zig create mode 100644 test/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig create mode 100644 test/compile_errors/stage1/obj/export_generic_function.zig create mode 100644 test/compile_errors/stage1/obj/exported_async_function.zig create mode 100644 test/compile_errors/stage1/obj/exported_enum_without_explicit_integer_tag_type.zig create mode 100644 test/compile_errors/stage1/obj/extern_function_pointer_mismatch.zig create mode 100644 test/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig create mode 100644 test/compile_errors/stage1/obj/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig create mode 100644 test/compile_errors/stage1/obj/extern_struct_with_non-extern-compatible_integer_tag_type.zig create mode 100644 test/compile_errors/stage1/obj/extern_union_field_missing_type.zig create mode 100644 test/compile_errors/stage1/obj/extern_union_given_enum_tag_type.zig create mode 100644 test/compile_errors/stage1/obj/extern_variable_has_no_type.zig create mode 100644 test/compile_errors/stage1/obj/fieldParentPtr-bad_field_name.zig create mode 100644 test/compile_errors/stage1/obj/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig create mode 100644 test/compile_errors/stage1/obj/fieldParentPtr-comptime_wrong_field_index.zig create mode 100644 test/compile_errors/stage1/obj/fieldParentPtr-field_pointer_is_not_pointer.zig create mode 100644 test/compile_errors/stage1/obj/fieldParentPtr-non_struct.zig create mode 100644 test/compile_errors/stage1/obj/field_access_of_opaque_type.zig create mode 100644 test/compile_errors/stage1/obj/field_access_of_slices.zig create mode 100644 test/compile_errors/stage1/obj/field_access_of_unknown_length_pointer.zig create mode 100644 test/compile_errors/stage1/obj/field_type_supplied_in_an_enum.zig create mode 100644 test/compile_errors/stage1/obj/floatToInt_comptime_safety.zig create mode 100644 test/compile_errors/stage1/obj/float_literal_too_large_error.zig create mode 100644 test/compile_errors/stage1/obj/float_literal_too_small_error_denormal.zig create mode 100644 test/compile_errors/stage1/obj/for_loop_body_expression_ignored.zig create mode 100644 test/compile_errors/stage1/obj/frame_called_outside_of_function_definition.zig create mode 100644 test/compile_errors/stage1/obj/frame_causes_function_to_be_async.zig create mode 100644 test/compile_errors/stage1/obj/function_alignment_non_power_of_2.zig create mode 100644 test/compile_errors/stage1/obj/function_call_assigned_to_incorrect_type.zig create mode 100644 test/compile_errors/stage1/obj/function_parameter_is_opaque.zig create mode 100644 test/compile_errors/stage1/obj/function_prototype_with_no_body.zig create mode 100644 test/compile_errors/stage1/obj/function_returning_opaque_type.zig create mode 100644 test/compile_errors/stage1/obj/function_with_ccc_indirectly_calling_async_function.zig create mode 100644 test/compile_errors/stage1/obj/function_with_invalid_return_type.zig create mode 100644 test/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig create mode 100644 test/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig create mode 100644 test/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig create mode 100644 test/compile_errors/stage1/obj/generic_fn_as_parameter_without_comptime_keyword.zig create mode 100644 test/compile_errors/stage1/obj/generic_function_call_assigned_to_incorrect_type.zig create mode 100644 test/compile_errors/stage1/obj/generic_function_instance_with_non-constant_expression.zig create mode 100644 test/compile_errors/stage1/obj/generic_function_returning_opaque_type.zig create mode 100644 test/compile_errors/stage1/obj/generic_function_where_return_type_is_self-referenced.zig create mode 100644 test/compile_errors/stage1/obj/global_variable_alignment_non_power_of_2.zig create mode 100644 test/compile_errors/stage1/obj/global_variable_initializer_must_be_constant_expression.zig create mode 100644 test/compile_errors/stage1/obj/hasDecl_with_non-container.zig create mode 100644 test/compile_errors/stage1/obj/if_condition_is_bool_not_int.zig create mode 100644 test/compile_errors/stage1/obj/ignored_assert-err-ok_return_value.zig create mode 100644 test/compile_errors/stage1/obj/ignored_comptime_statement_value.zig create mode 100644 test/compile_errors/stage1/obj/ignored_comptime_value.zig create mode 100644 test/compile_errors/stage1/obj/ignored_deferred_function_call.zig create mode 100644 test/compile_errors/stage1/obj/ignored_deferred_statement_value.zig create mode 100644 test/compile_errors/stage1/obj/ignored_expression_in_while_continuation.zig create mode 100644 test/compile_errors/stage1/obj/ignored_return_value.zig create mode 100644 test/compile_errors/stage1/obj/ignored_statement_value.zig create mode 100644 test/compile_errors/stage1/obj/illegal_comparison_of_types.zig create mode 100644 test/compile_errors/stage1/obj/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig create mode 100644 test/compile_errors/stage1/obj/implicit_cast_const_array_to_mutable_slice.zig create mode 100644 test/compile_errors/stage1/obj/implicit_cast_from_array_to_mutable_slice.zig create mode 100644 test/compile_errors/stage1/obj/implicit_cast_from_f64_to_f32.zig create mode 100644 test/compile_errors/stage1/obj/implicit_cast_of_error_set_not_a_subset.zig create mode 100644 test/compile_errors/stage1/obj/implicit_casting_C_pointers_which_would_mess_up_null_semantics.zig create mode 100644 test/compile_errors/stage1/obj/implicit_casting_null_c_pointer_to_zig_pointer.zig create mode 100644 test/compile_errors/stage1/obj/implicit_casting_too_big_integers_to_C_pointers.zig create mode 100644 test/compile_errors/stage1/obj/implicit_casting_undefined_c_pointer_to_zig_pointer.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-block_expr.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-block_statement.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-comptime_expression.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-comptime_statement.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-defer.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-for_expression.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-for_statement.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_expression.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_statement.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_expression.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_statement.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-if-else_expression.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-if-else_statement.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-if_expression.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-if_statement.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-test_expression.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-test_statement.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-while-continue_expression.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-while-continue_statement.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-while_expression.zig create mode 100644 test/compile_errors/stage1/obj/implicit_semicolon-while_statement.zig create mode 100644 test/compile_errors/stage1/obj/implicitly_casting_enum_to_tag_type.zig create mode 100644 test/compile_errors/stage1/obj/implicitly_increasing_pointer_alignment.zig create mode 100644 test/compile_errors/stage1/obj/implicitly_increasing_slice_alignment.zig create mode 100644 test/compile_errors/stage1/obj/import_outside_package_path.zig create mode 100644 test/compile_errors/stage1/obj/incorrect_return_type.zig create mode 100644 test/compile_errors/stage1/obj/increase_pointer_alignment_in_ptrCast.zig create mode 100644 test/compile_errors/stage1/obj/indexing_a_undefined_slice_at_comptime.zig create mode 100644 test/compile_errors/stage1/obj/indexing_an_array_of_size_zero.zig create mode 100644 test/compile_errors/stage1/obj/indexing_an_array_of_size_zero_with_runtime_index.zig create mode 100644 test/compile_errors/stage1/obj/indexing_single-item_pointer.zig create mode 100644 test/compile_errors/stage1/obj/indirect_recursion_of_async_functions_detected.zig create mode 100644 test/compile_errors/stage1/obj/indirect_struct_loop.zig create mode 100644 test/compile_errors/stage1/obj/inferred_array_size_invalid_here.zig create mode 100644 test/compile_errors/stage1/obj/inferring_error_set_of_function_pointer.zig create mode 100644 test/compile_errors/stage1/obj/initializing_array_with_struct_syntax.zig create mode 100644 test/compile_errors/stage1/obj/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig create mode 100644 test/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig create mode 100644 test/compile_errors/stage1/obj/int_to_err_global_invalid_number.zig create mode 100644 test/compile_errors/stage1/obj/int_to_err_non_global_invalid_number.zig create mode 100644 test/compile_errors/stage1/obj/int_to_ptr_of_0_bits.zig create mode 100644 test/compile_errors/stage1/obj/integer_cast_truncates_bits.zig create mode 100644 test/compile_errors/stage1/obj/integer_overflow_error.zig create mode 100644 test/compile_errors/stage1/obj/integer_underflow_error.zig create mode 100644 test/compile_errors/stage1/obj/invalid_break_expression.zig create mode 100644 test/compile_errors/stage1/obj/invalid_builtin_fn.zig create mode 100644 test/compile_errors/stage1/obj/invalid_cast_from_integral_type_to_enum.zig create mode 100644 test/compile_errors/stage1/obj/invalid_comparison_for_function_pointers.zig create mode 100644 test/compile_errors/stage1/obj/invalid_continue_expression.zig create mode 100644 test/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig create mode 100644 test/compile_errors/stage1/obj/invalid_empty_unicode_escape.zig create mode 100644 test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-1.zig create mode 100644 test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-2.zig create mode 100644 test/compile_errors/stage1/obj/invalid_field_access_in_comptime.zig create mode 100644 test/compile_errors/stage1/obj/invalid_field_in_struct_value_expression.zig create mode 100644 test/compile_errors/stage1/obj/invalid_float_literal.zig create mode 100644 test/compile_errors/stage1/obj/invalid_legacy_unicode_escape.zig create mode 100644 test/compile_errors/stage1/obj/invalid_maybe_type.zig create mode 100644 test/compile_errors/stage1/obj/invalid_member_of_builtin_enum.zig create mode 100644 test/compile_errors/stage1/obj/invalid_multiple_dereferences.zig create mode 100644 test/compile_errors/stage1/obj/invalid_optional_type_in_extern_struct.zig create mode 100644 test/compile_errors/stage1/obj/invalid_pointer_for_var_type.zig create mode 100644 test/compile_errors/stage1/obj/invalid_pointer_syntax.zig create mode 100644 test/compile_errors/stage1/obj/invalid_shift_amount_error.zig create mode 100644 test/compile_errors/stage1/obj/invalid_struct_field.zig create mode 100644 test/compile_errors/stage1/obj/invalid_suspend_in_exported_function.zig create mode 100644 test/compile_errors/stage1/obj/invalid_type.zig create mode 100644 test/compile_errors/stage1/obj/invalid_type_used_in_array_type.zig create mode 100644 test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-1.zig create mode 100644 test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-10.zig create mode 100644 test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-11.zig create mode 100644 test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-12.zig create mode 100644 test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-13.zig create mode 100644 test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-14.zig create mode 100644 test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-2.zig create mode 100644 test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-3.zig create mode 100644 test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-4.zig create mode 100644 test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-5.zig create mode 100644 test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-6.zig create mode 100644 test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-7.zig create mode 100644 test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-9.zig create mode 100644 test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-1.zig create mode 100644 test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-2.zig create mode 100644 test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-3.zig create mode 100644 test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-4.zig create mode 100644 test/compile_errors/stage1/obj/invalid_union_field_access_in_comptime.zig create mode 100644 test/compile_errors/stage1/obj/issue_2032_compile_diagnostic_string_for_top_level_decl_type.zig create mode 100644 test/compile_errors/stage1/obj/issue_2687_coerce_from_undefined_array_pointer_to_slice.zig create mode 100644 test/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig create mode 100644 test/compile_errors/stage1/obj/issue_4207_coerce_from_non-terminated-slice_to_terminated-pointer.zig create mode 100644 test/compile_errors/stage1/obj/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig create mode 100644 test/compile_errors/stage1/obj/issue_5618_coercion_of_optional_anyopaque_to_anyopaque_must_fail.zig create mode 100644 test/compile_errors/stage1/obj/issue_7810-comptime_slice-len_increment_beyond_bounds.zig create mode 100644 test/compile_errors/stage1/obj/issue_9346_return_outside_of_function_scope.zig create mode 100644 test/compile_errors/stage1/obj/labeled_break_not_found.zig create mode 100644 test/compile_errors/stage1/obj/labeled_continue_not_found.zig create mode 100644 test/compile_errors/stage1/obj/lazy_pointer_with_undefined_element_type.zig create mode 100644 test/compile_errors/stage1/obj/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig create mode 100644 test/compile_errors/stage1/obj/load_vector_pointer_with_unknown_runtime_index.zig create mode 100644 test/compile_errors/stage1/obj/local_shadows_global_that_occurs_later.zig create mode 100644 test/compile_errors/stage1/obj/local_variable_redeclaration.zig create mode 100644 test/compile_errors/stage1/obj/local_variable_redeclares_parameter.zig create mode 100644 test/compile_errors/stage1/obj/local_variable_shadowing_global.zig create mode 100644 test/compile_errors/stage1/obj/locally_shadowing_a_primitive_type.zig create mode 100644 test/compile_errors/stage1/obj/main_function_with_bogus_args_type.zig create mode 100644 test/compile_errors/stage1/obj/method_call_with_first_arg_type_primitive.zig create mode 100644 test/compile_errors/stage1/obj/method_call_with_first_arg_type_wrong_container.zig create mode 100644 test/compile_errors/stage1/obj/missing_boolean_switch_value.zig create mode 100644 test/compile_errors/stage1/obj/missing_const_in_slice_with_nested_array_type.zig create mode 100644 test/compile_errors/stage1/obj/missing_else_clause.zig create mode 100644 test/compile_errors/stage1/obj/missing_field_in_struct_value_expression.zig create mode 100644 test/compile_errors/stage1/obj/missing_function_call_param.zig create mode 100644 test/compile_errors/stage1/obj/missing_function_name.zig create mode 100644 test/compile_errors/stage1/obj/missing_param_name.zig create mode 100644 test/compile_errors/stage1/obj/missing_parameter_name_of_generic_function.zig create mode 100644 test/compile_errors/stage1/obj/missing_result_type_for_phi_node.zig create mode 100644 test/compile_errors/stage1/obj/misspelled_type_with_pointer_only_reference.zig create mode 100644 test/compile_errors/stage1/obj/mod_assign_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/mod_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/mul_overflow_in_function_evaluation.zig create mode 100644 test/compile_errors/stage1/obj/mult_assign_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/mult_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/mult_wrap_assign_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/mult_wrap_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/multiple_function_definitions.zig create mode 100644 test/compile_errors/stage1/obj/negate_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/negate_wrap_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/negation_overflow_in_function_evaluation.zig create mode 100644 test/compile_errors/stage1/obj/nested_error_set_mismatch.zig create mode 100644 test/compile_errors/stage1/obj/no_else_prong_on_switch_on_global_error_set.zig create mode 100644 test/compile_errors/stage1/obj/noalias_on_non_pointer_param.zig create mode 100644 test/compile_errors/stage1/obj/non-async_function_pointer_eventually_is_inferred_to_become_async.zig create mode 100644 test/compile_errors/stage1/obj/non-const_expression_function_call_with_struct_return_value_outside_function.zig create mode 100644 test/compile_errors/stage1/obj/non-const_expression_in_struct_literal_outside_function.zig create mode 100644 test/compile_errors/stage1/obj/non-const_switch_number_literal.zig create mode 100644 test/compile_errors/stage1/obj/non-const_variables_of_things_that_require_const_variables.zig create mode 100644 test/compile_errors/stage1/obj/non-enum_tag_type_passed_to_union.zig create mode 100644 test/compile_errors/stage1/obj/non-extern_function_with_var_args.zig create mode 100644 test/compile_errors/stage1/obj/non-inline_for_loop_on_a_type_that_requires_comptime.zig create mode 100644 test/compile_errors/stage1/obj/non-integer_tag_type_to_automatic_union_enum.zig create mode 100644 test/compile_errors/stage1/obj/non-pure_function_returns_type.zig create mode 100644 test/compile_errors/stage1/obj/non_async_function_pointer_passed_to_asyncCall.zig create mode 100644 test/compile_errors/stage1/obj/non_compile_time_array_concatenation.zig create mode 100644 test/compile_errors/stage1/obj/non_constant_expression_in_array_size.zig create mode 100644 test/compile_errors/stage1/obj/non_error_sets_used_in_merge_error_sets_operator.zig create mode 100644 test/compile_errors/stage1/obj/non_float_passed_to_floatToInt.zig create mode 100644 test/compile_errors/stage1/obj/non_int_passed_to_intToFloat.zig create mode 100644 test/compile_errors/stage1/obj/non_pointer_given_to_ptrToInt.zig create mode 100644 test/compile_errors/stage1/obj/normal_string_with_newline.zig create mode 100644 test/compile_errors/stage1/obj/offsetOf-bad_field_name.zig create mode 100644 test/compile_errors/stage1/obj/offsetOf-non_struct.zig create mode 100644 test/compile_errors/stage1/obj/only_equality_binary_operator_allowed_for_error_sets.zig create mode 100644 test/compile_errors/stage1/obj/opaque_type_with_field.zig create mode 100644 test/compile_errors/stage1/obj/optional_pointer_to_void_in_extern_struct.zig create mode 100644 test/compile_errors/stage1/obj/or_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/orelse_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/out_of_range_comptime_int_passed_to_floatToInt.zig create mode 100644 test/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig create mode 100644 test/compile_errors/stage1/obj/packed_union_given_enum_tag_type.zig create mode 100644 test/compile_errors/stage1/obj/packed_union_with_automatic_layout_field.zig create mode 100644 test/compile_errors/stage1/obj/panic_called_at_compile_time.zig create mode 100644 test/compile_errors/stage1/obj/parameter_redeclaration.zig create mode 100644 test/compile_errors/stage1/obj/parameter_shadowing_global.zig create mode 100644 test/compile_errors/stage1/obj/pass_const_ptr_to_mutable_ptr_fn.zig create mode 100644 test/compile_errors/stage1/obj/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig create mode 100644 test/compile_errors/stage1/obj/passing_an_under-aligned_function_pointer.zig create mode 100644 test/compile_errors/stage1/obj/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig create mode 100644 test/compile_errors/stage1/obj/pointer_arithmetic_on_pointer-to-array.zig create mode 100644 test/compile_errors/stage1/obj/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig create mode 100644 test/compile_errors/stage1/obj/pointer_to_noreturn.zig create mode 100644 test/compile_errors/stage1/obj/popCount-non-integer.zig create mode 100644 test/compile_errors/stage1/obj/prevent_bad_implicit_casting_of_anyframe_types.zig create mode 100644 test/compile_errors/stage1/obj/primitives_take_precedence_over_declarations.zig create mode 100644 test/compile_errors/stage1/obj/ptrCast_a_0_bit_type_to_a_non-_0_bit_type.zig create mode 100644 test/compile_errors/stage1/obj/ptrCast_discards_const_qualifier.zig create mode 100644 test/compile_errors/stage1/obj/ptrToInt_0_to_non_optional_pointer.zig create mode 100644 test/compile_errors/stage1/obj/ptrToInt_on_void.zig create mode 100644 test/compile_errors/stage1/obj/ptrcast_to_non-pointer.zig create mode 100644 test/compile_errors/stage1/obj/range_operator_in_switch_used_on_error_set.zig create mode 100644 test/compile_errors/stage1/obj/reading_past_end_of_pointer_casted_array.zig create mode 100644 test/compile_errors/stage1/obj/recursive_inferred_error_set.zig create mode 100644 test/compile_errors/stage1/obj/redefinition_of_enums.zig create mode 100644 test/compile_errors/stage1/obj/redefinition_of_global_variables.zig create mode 100644 test/compile_errors/stage1/obj/redefinition_of_struct.zig create mode 100644 test/compile_errors/stage1/obj/refer_to_the_type_of_a_generic_function.zig create mode 100644 test/compile_errors/stage1/obj/referring_to_a_struct_that_is_invalid.zig create mode 100644 test/compile_errors/stage1/obj/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig create mode 100644 test/compile_errors/stage1/obj/reify_type.Fn_with_is_generic_true.zig create mode 100644 test/compile_errors/stage1/obj/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig create mode 100644 test/compile_errors/stage1/obj/reify_type.Fn_with_return_type_null.zig create mode 100644 test/compile_errors/stage1/obj/reify_type.Pointer_with_invalid_address_space.zig create mode 100644 test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig create mode 100644 test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig create mode 100644 test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_zero_fields.zig create mode 100644 test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_enum_field.zig create mode 100644 test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_union_field.zig create mode 100644 test/compile_errors/stage1/obj/reify_type_for_union_with_opaque_field.zig create mode 100644 test/compile_errors/stage1/obj/reify_type_for_union_with_zero_fields.zig create mode 100644 test/compile_errors/stage1/obj/reify_type_union_payload_is_undefined.zig create mode 100644 test/compile_errors/stage1/obj/reify_type_with_Type.Int.zig create mode 100644 test/compile_errors/stage1/obj/reify_type_with_non-constant_expression.zig create mode 100644 test/compile_errors/stage1/obj/reify_type_with_undefined.zig create mode 100644 test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr.zig create mode 100644 test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig create mode 100644 test/compile_errors/stage1/obj/return_from_defer_expression.zig create mode 100644 test/compile_errors/stage1/obj/returning_error_from_void_async_function.zig create mode 100644 test/compile_errors/stage1/obj/runtime-known_async_function_called.zig create mode 100644 test/compile_errors/stage1/obj/runtime-known_function_called_with_async_keyword.zig create mode 100644 test/compile_errors/stage1/obj/runtime_assignment_to_comptime_struct_type.zig create mode 100644 test/compile_errors/stage1/obj/runtime_assignment_to_comptime_union_type.zig create mode 100644 test/compile_errors/stage1/obj/runtime_cast_to_union_which_has_non-void_fields.zig create mode 100644 test/compile_errors/stage1/obj/runtime_index_into_comptime_type_slice.zig create mode 100644 test/compile_errors/stage1/obj/saturating_arithmetic_does_not_allow_floats.zig create mode 100644 test/compile_errors/stage1/obj/saturating_shl_assign_does_not_allow_negative_rhs_at_comptime.zig create mode 100644 test/compile_errors/stage1/obj/saturating_shl_does_not_allow_negative_rhs_at_comptime.zig create mode 100644 test/compile_errors/stage1/obj/setAlignStack_in_inline_function.zig create mode 100644 test/compile_errors/stage1/obj/setAlignStack_in_naked_function.zig create mode 100644 test/compile_errors/stage1/obj/setAlignStack_outside_function.zig create mode 100644 test/compile_errors/stage1/obj/setAlignStack_set_twice.zig create mode 100644 test/compile_errors/stage1/obj/setAlignStack_too_big.zig create mode 100644 test/compile_errors/stage1/obj/setFloatMode_twice_for_same_scope.zig create mode 100644 test/compile_errors/stage1/obj/setRuntimeSafety_twice_for_same_scope.zig create mode 100644 test/compile_errors/stage1/obj/setting_a_section_on_a_local_variable.zig create mode 100644 test/compile_errors/stage1/obj/shift_amount_has_to_be_an_integer_type.zig create mode 100644 test/compile_errors/stage1/obj/shift_by_negative_comptime_integer.zig create mode 100644 test/compile_errors/stage1/obj/shift_left_assign_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/shift_left_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/shift_right_assign_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/shift_right_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/shifting_RHS_is_log2_of_LHS_int_bit_width.zig create mode 100644 test/compile_errors/stage1/obj/shifting_without_int_type_or_comptime_known.zig create mode 100644 test/compile_errors/stage1/obj/shlExact_shifts_out_1_bits.zig create mode 100644 test/compile_errors/stage1/obj/shrExact_shifts_out_1_bits.zig create mode 100644 test/compile_errors/stage1/obj/signed_integer_division.zig create mode 100644 test/compile_errors/stage1/obj/signed_integer_remainder_division.zig create mode 100644 test/compile_errors/stage1/obj/sizeOf_bad_type.zig create mode 100644 test/compile_errors/stage1/obj/slice_cannot_have_its_bytes_reinterpreted.zig create mode 100644 test/compile_errors/stage1/obj/slice_passed_as_array_init_type.zig create mode 100644 test/compile_errors/stage1/obj/slice_passed_as_array_init_type_with_elems.zig create mode 100644 test/compile_errors/stage1/obj/slice_sentinel_mismatch-1.zig create mode 100644 test/compile_errors/stage1/obj/slice_sentinel_mismatch-2.zig create mode 100644 test/compile_errors/stage1/obj/slicing_of_global_undefined_pointer.zig create mode 100644 test/compile_errors/stage1/obj/slicing_single-item_pointer.zig create mode 100644 test/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig create mode 100644 test/compile_errors/stage1/obj/specify_non-integer_enum_tag_type.zig create mode 100644 test/compile_errors/stage1/obj/src_outside_function.zig create mode 100644 test/compile_errors/stage1/obj/store_vector_pointer_with_unknown_runtime_index.zig create mode 100644 test/compile_errors/stage1/obj/storing_runtime_value_in_compile_time_variable_then_using_it.zig create mode 100644 test/compile_errors/stage1/obj/struct_depends_on_itself_via_optional_field.zig create mode 100644 test/compile_errors/stage1/obj/struct_field_missing_type.zig create mode 100644 test/compile_errors/stage1/obj/struct_init_syntax_for_array.zig create mode 100644 test/compile_errors/stage1/obj/struct_with_declarations_unavailable_for_reify_type.zig create mode 100644 test/compile_errors/stage1/obj/struct_with_invalid_field.zig create mode 100644 test/compile_errors/stage1/obj/sub_assign_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/sub_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/sub_overflow_in_function_evaluation.zig create mode 100644 test/compile_errors/stage1/obj/sub_wrap_assign_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/sub_wrap_on_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/suspend_inside_suspend_block.zig create mode 100644 test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong.zig create mode 100644 test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong_when_else_present.zig create mode 100644 test/compile_errors/stage1/obj/switch_expression-duplicate_or_overlapping_integer_value.zig create mode 100644 test/compile_errors/stage1/obj/switch_expression-duplicate_type.zig create mode 100644 test/compile_errors/stage1/obj/switch_expression-duplicate_type_struct_alias.zig create mode 100644 test/compile_errors/stage1/obj/switch_expression-missing_enumeration_prong.zig create mode 100644 test/compile_errors/stage1/obj/switch_expression-multiple_else_prongs.zig create mode 100644 test/compile_errors/stage1/obj/switch_expression-non_exhaustive_integer_prongs.zig create mode 100644 test/compile_errors/stage1/obj/switch_expression-switch_on_pointer_type_with_no_else.zig create mode 100644 test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_bool.zig create mode 100644 test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_enum.zig create mode 100644 test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_i8.zig create mode 100644 test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_u8.zig create mode 100644 test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u1.zig create mode 100644 test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u2.zig create mode 100644 test/compile_errors/stage1/obj/switch_on_enum_with_1_field_with_no_prongs.zig create mode 100644 test/compile_errors/stage1/obj/switch_on_union_with_no_attached_enum.zig create mode 100644 test/compile_errors/stage1/obj/switch_with_invalid_expression_parameter.zig create mode 100644 test/compile_errors/stage1/obj/switch_with_overlapping_case_ranges.zig create mode 100644 test/compile_errors/stage1/obj/tagName_used_on_union_with_no_associated_enum_tag.zig create mode 100644 test/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig create mode 100644 test/compile_errors/stage1/obj/taking_bit_offset_of_void_field_in_struct.zig create mode 100644 test/compile_errors/stage1/obj/taking_byte_offset_of_void_field_in_struct.zig create mode 100644 test/compile_errors/stage1/obj/threadlocal_qualifier_on_const.zig create mode 100644 test/compile_errors/stage1/obj/top_level_decl_dependency_loop.zig create mode 100644 test/compile_errors/stage1/obj/truncate_sign_mismatch.zig create mode 100644 test/compile_errors/stage1/obj/truncate_undefined_value.zig create mode 100644 test/compile_errors/stage1/obj/try_in_function_with_non_error_return_type.zig create mode 100644 test/compile_errors/stage1/obj/type_checking_function_pointers.zig create mode 100644 test/compile_errors/stage1/obj/type_variables_must_be_constant.zig create mode 100644 test/compile_errors/stage1/obj/undeclared_identifier.zig create mode 100644 test/compile_errors/stage1/obj/undeclared_identifier_error_should_mark_fn_as_impure.zig create mode 100644 test/compile_errors/stage1/obj/undeclared_identifier_in_unanalyzed_branch.zig create mode 100644 test/compile_errors/stage1/obj/undefined_as_field_type_is_rejected.zig create mode 100644 test/compile_errors/stage1/obj/undefined_function_call.zig create mode 100644 test/compile_errors/stage1/obj/underscore_is_not_a_declarable_symbol.zig create mode 100644 test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_for.zig create mode 100644 test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while.zig create mode 100644 test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while_else.zig create mode 100644 test/compile_errors/stage1/obj/union_auto-enum_value_already_taken.zig create mode 100644 test/compile_errors/stage1/obj/union_enum_field_does_not_match_enum.zig create mode 100644 test/compile_errors/stage1/obj/union_fields_with_value_assignments.zig create mode 100644 test/compile_errors/stage1/obj/union_with_0_fields.zig create mode 100644 test/compile_errors/stage1/obj/union_with_specified_enum_omits_field.zig create mode 100644 test/compile_errors/stage1/obj/union_with_too_small_explicit_signed_tag_type.zig create mode 100644 test/compile_errors/stage1/obj/union_with_too_small_explicit_unsigned_tag_type.zig create mode 100644 test/compile_errors/stage1/obj/unknown_length_pointer_to_opaque.zig create mode 100644 test/compile_errors/stage1/obj/unreachable_code-double_break.zig create mode 100644 test/compile_errors/stage1/obj/unreachable_code-nested_returns.zig create mode 100644 test/compile_errors/stage1/obj/unreachable_code.zig create mode 100644 test/compile_errors/stage1/obj/unreachable_executed_at_comptime.zig create mode 100644 test/compile_errors/stage1/obj/unreachable_parameter.zig create mode 100644 test/compile_errors/stage1/obj/unreachable_variable.zig create mode 100644 test/compile_errors/stage1/obj/unreachable_with_return.zig create mode 100644 test/compile_errors/stage1/obj/unsupported_modifier_at_start_of_asm_output_constraint.zig create mode 100644 test/compile_errors/stage1/obj/use_anyopaque_as_return_type_of_fn_ptr.zig create mode 100644 test/compile_errors/stage1/obj/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig create mode 100644 test/compile_errors/stage1/obj/use_invalid_number_literal_as_array_index.zig create mode 100644 test/compile_errors/stage1/obj/use_of_comptime-known_undefined_function_value.zig create mode 100644 test/compile_errors/stage1/obj/use_of_undeclared_identifier.zig create mode 100644 test/compile_errors/stage1/obj/using_an_unknown_len_ptr_type_instead_of_array.zig create mode 100644 test/compile_errors/stage1/obj/using_invalid_types_in_function_call_raises_an_error.zig create mode 100644 test/compile_errors/stage1/obj/usingnamespace_with_wrong_type.zig create mode 100644 test/compile_errors/stage1/obj/variable_has_wrong_type.zig create mode 100644 test/compile_errors/stage1/obj/variable_initialization_compile_error_then_referenced.zig create mode 100644 test/compile_errors/stage1/obj/variable_with_type_noreturn.zig create mode 100644 test/compile_errors/stage1/obj/vector_index_out_of_bounds.zig create mode 100644 test/compile_errors/stage1/obj/volatile_on_global_assembly.zig create mode 100644 test/compile_errors/stage1/obj/wasmMemoryGrow_is_a_compile_error_in_non-Wasm_targets.zig create mode 100644 test/compile_errors/stage1/obj/wasmMemorySize_is_a_compile_error_in_non-Wasm_targets.zig create mode 100644 test/compile_errors/stage1/obj/while_expected_bool_got_error_union.zig create mode 100644 test/compile_errors/stage1/obj/while_expected_bool_got_optional.zig create mode 100644 test/compile_errors/stage1/obj/while_expected_error_union_got_bool.zig create mode 100644 test/compile_errors/stage1/obj/while_expected_error_union_got_optional.zig create mode 100644 test/compile_errors/stage1/obj/while_expected_optional_got_bool.zig create mode 100644 test/compile_errors/stage1/obj/while_expected_optional_got_error_union.zig create mode 100644 test/compile_errors/stage1/obj/while_loop_body_expression_ignored.zig create mode 100644 test/compile_errors/stage1/obj/write_to_const_global_variable.zig create mode 100644 test/compile_errors/stage1/obj/wrong_frame_type_used_for_async_call.zig create mode 100644 test/compile_errors/stage1/obj/wrong_function_type.zig create mode 100644 test/compile_errors/stage1/obj/wrong_initializer_for_union_payload_of_type_type.zig create mode 100644 test/compile_errors/stage1/obj/wrong_number_of_arguments.zig create mode 100644 test/compile_errors/stage1/obj/wrong_number_of_arguments_for_method_fn_call.zig create mode 100644 test/compile_errors/stage1/obj/wrong_panic_signature_generic_function.zig create mode 100644 test/compile_errors/stage1/obj/wrong_panic_signature_runtime_function.zig create mode 100644 test/compile_errors/stage1/obj/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig create mode 100644 test/compile_errors/stage1/obj/wrong_return_type_for_main.zig create mode 100644 test/compile_errors/stage1/obj/wrong_size_to_an_array_literal.zig create mode 100644 test/compile_errors/stage1/obj/wrong_type_for_argument_tuple_to_asyncCall.zig create mode 100644 test/compile_errors/stage1/obj/wrong_type_for_reify_type.zig create mode 100644 test/compile_errors/stage1/obj/wrong_type_for_result_ptr_to_asyncCall.zig create mode 100644 test/compile_errors/stage1/obj/wrong_type_passed_to_panic.zig create mode 100644 test/compile_errors/stage1/obj/wrong_type_to_hasField.zig create mode 100644 test/compile_errors/stage1/obj/wrong_types_given_to_atomic_order_args_in_cmpxchg.zig create mode 100644 test/compile_errors/stage1/obj/wrong_types_given_to_export.zig create mode 100644 test/compile_errors/stage1/test/access_invalid_typeInfo_decl.zig create mode 100644 test/compile_errors/stage1/test/alignCast_of_zero_sized_types.zig create mode 100644 test/compile_errors/stage1/test/bad_splat_type.zig create mode 100644 test/compile_errors/stage1/test/binary_OR_operator_on_error_sets.zig create mode 100644 test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-always_inline.zig create mode 100644 test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-compile_time.zig create mode 100644 test/compile_errors/stage1/test/cast_between_optional_T_where_T_is_not_a_pointer.zig create mode 100644 test/compile_errors/stage1/test/combination_of_nosuspend_and_async.zig create mode 100644 test/compile_errors/stage1/test/comparison_of_non-tagged_union_and_enum_literal.zig create mode 100644 test/compile_errors/stage1/test/comptime_vector_overflow_shows_the_index.zig create mode 100644 test/compile_errors/stage1/test/duplicate-unused_labels.zig create mode 100644 test/compile_errors/stage1/test/duplicate_field_in_anonymous_struct_literal.zig create mode 100644 test/compile_errors/stage1/test/error_in_struct_initializer_doesnt_crash_the_compiler.zig create mode 100644 test/compile_errors/stage1/test/errors_in_for_loop_bodies_are_propagated.zig create mode 100644 test/compile_errors/stage1/test/export_with_empty_name_string.zig create mode 100644 test/compile_errors/stage1/test/helpful_return_type_error_message.zig create mode 100644 test/compile_errors/stage1/test/int-float_conversion_to_comptime_int-float.zig create mode 100644 test/compile_errors/stage1/test/invalid_assignments.zig create mode 100644 test/compile_errors/stage1/test/invalid_float_casts.zig create mode 100644 test/compile_errors/stage1/test/invalid_int_casts.zig create mode 100644 test/compile_errors/stage1/test/invalid_non-exhaustive_enum_to_union.zig create mode 100644 test/compile_errors/stage1/test/invalid_pointer_with_reify_type.zig create mode 100644 test/compile_errors/stage1/test/nested_vectors.zig create mode 100644 test/compile_errors/stage1/test/non-exhaustive_enum_marker_assigned_a_value.zig create mode 100644 test/compile_errors/stage1/test/non-exhaustive_enums.zig create mode 100644 test/compile_errors/stage1/test/not_an_enum_type.zig create mode 100644 test/compile_errors/stage1/test/packed_struct_with_fields_of_not_allowed_types.zig create mode 100644 test/compile_errors/stage1/test/ptrToInt_with_pointer_to_zero-sized_type.zig create mode 100644 test/compile_errors/stage1/test/reassign_to_array_parameter.zig create mode 100644 test/compile_errors/stage1/test/reassign_to_slice_parameter.zig create mode 100644 test/compile_errors/stage1/test/reassign_to_struct_parameter.zig create mode 100644 test/compile_errors/stage1/test/reference_to_const_data.zig create mode 100644 test/compile_errors/stage1/test/reify_typeOf_with_incompatible_arguments.zig create mode 100644 test/compile_errors/stage1/test/reify_typeOf_with_no_arguments.zig create mode 100644 test/compile_errors/stage1/test/reject_extern_function_definitions_with_body.zig create mode 100644 test/compile_errors/stage1/test/reject_extern_variables_with_initializers.zig create mode 100644 test/compile_errors/stage1/test/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig create mode 100644 test/compile_errors/stage1/test/return_invalid_type_from_test.zig create mode 100644 test/compile_errors/stage1/test/shift_on_type_with_non-power-of-two_size.zig create mode 100644 test/compile_errors/stage1/test/shuffle_with_selected_index_past_first_vector_length.zig create mode 100644 test/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig create mode 100644 test/compile_errors/stage1/test/switching_with_exhaustive_enum_has___prong_.zig create mode 100644 test/compile_errors/stage1/test/switching_with_non-exhaustive_enums.zig create mode 100644 test/compile_errors/stage1/test/tagName_on_invalid_value_of_non-exhaustive_enum.zig create mode 100644 test/compile_errors/stage1/test/type_mismatch_in_C_prototype_with_varargs.zig create mode 100644 test/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig create mode 100644 test/compile_errors/stage1/test/unused_variable_error_on_errdefer.zig create mode 100644 test/compile_errors/stage2/embed_outside_package.zig create mode 100644 test/compile_errors/stage2/import_outside_package.zig create mode 100644 test/compile_errors/stage2/out_of_bounds_index.zig diff --git a/test/compile_errors.zig b/test/compile_errors.zig index e0d90e3ee2..14f32c150e 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -39,180 +39,6 @@ pub fn addCases(ctx: *TestContext) !void { try ctx.addErrorCasesFromDir("stage1", test_dir, .stage1, .Exe, true, one_test_case_per_file); } } - { - var case = ctx.obj("stage2 compile errors", .{}); - - case.addError( - \\export fn a() usize { - \\ return @embedFile("/root/foo").len; - \\} - , &[_][]const u8{ - ":2:23: error: embed of file outside package path: '/root/foo'", - }); - - case.addError( - \\export fn a() usize { - \\ return @import("../../above.zig").len; - \\} - , &[_][]const u8{ - ":2:20: error: import of file outside package path: '../../above.zig'", - }); - - case.addError( - \\comptime { - \\ var array = [_:0]u8{ 1, 2, 3, 4 }; - \\ var src_slice: [:0]u8 = &array; - \\ var slice = src_slice[2..6]; - \\ _ = slice; - \\} - \\comptime { - \\ var array = [_:0]u8{ 1, 2, 3, 4 }; - \\ var slice = array[2..6]; - \\ _ = slice; - \\} - \\comptime { - \\ var array = [_]u8{ 1, 2, 3, 4 }; - \\ var slice = array[2..5]; - \\ _ = slice; - \\} - \\comptime { - \\ var array = [_:0]u8{ 1, 2, 3, 4 }; - \\ var slice = array[3..2]; - \\ _ = slice; - \\} - , &[_][]const u8{ - ":4:26: error: end index 6 out of bounds for slice of length 4 +1 (sentinel)", - ":9:22: error: end index 6 out of bounds for array of length 4 +1 (sentinel)", - ":14:22: error: end index 5 out of bounds for array of length 4", - ":19:22: error: start index 3 is larger than end index 2", - }); - } - - ctx.objErrStage1("exported enum without explicit integer tag type", - \\const E = enum { one, two }; - \\comptime { - \\ @export(E, .{ .name = "E" }); - \\} - \\const e: E = .two; - \\comptime { - \\ @export(e, .{ .name = "e" }); - \\} - , &.{ - "tmp.zig:3:13: error: exported enum without explicit integer tag type", - "tmp.zig:7:13: error: exported enum value without explicit integer tag type", - }); - - ctx.objErrStage1("issue #9346: return outside of function scope", - \\pub const empty = return 1; - , &.{"tmp.zig:1:19: error: 'return' outside function scope"}); - - ctx.exeErrStage1("std.fmt error for unused arguments", - \\pub fn main() !void { - \\ @import("std").debug.print("{d} {d} {d} {d} {d}", .{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}); - \\} - , &.{ - "?:?:?: error: 10 unused arguments in '{d} {d} {d} {d} {d}'", - }); - - ctx.objErrStage1("lazy pointer with undefined element type", - \\export fn foo() void { - \\ comptime var T: type = undefined; - \\ const S = struct { x: *T }; - \\ const I = @typeInfo(S); - \\ _ = I; - \\} - , &[_][]const u8{ - ":3:28: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("pointer arithmetic on pointer-to-array", - \\export fn foo() void { - \\ var x: [10]u8 = undefined; - \\ var y = &x; - \\ var z = y + 1; - \\ _ = z; - \\} - , &[_][]const u8{ - "tmp.zig:4:17: error: integer value 1 cannot be coerced to type '*[10]u8'", - }); - - ctx.objErrStage1("pointer attributes checked when coercing pointer to anon literal", - \\comptime { - \\ const c: [][]const u8 = &.{"hello", "world" }; - \\ _ = c; - \\} - \\comptime { - \\ const c: *[2][]const u8 = &.{"hello", "world" }; - \\ _ = c; - \\} - \\const S = struct {a: u8 = 1, b: u32 = 2}; - \\comptime { - \\ const c: *S = &.{}; - \\ _ = c; - \\} - , &[_][]const u8{ - "tmp.zig:2:31: error: cannot cast pointer to array literal to slice type '[][]const u8'", - "tmp.zig:2:31: note: cast discards const qualifier", - "tmp.zig:6:33: error: cannot cast pointer to array literal to '*[2][]const u8'", - "tmp.zig:6:33: note: cast discards const qualifier", - "tmp.zig:11:21: error: expected type '*S', found '*const struct:11:21'", - "tmp.zig:11:21: note: cast discards const qualifier", - }); - - ctx.objErrStage1("@Type() union payload is undefined", - \\const Foo = @Type(.{ - \\ .Struct = undefined, - \\}); - \\comptime { _ = Foo; } - , &[_][]const u8{ - "tmp.zig:1:20: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("wrong initializer for union payload of type 'type'", - \\const U = union(enum) { - \\ A: type, - \\}; - \\const S = struct { - \\ u: U, - \\}; - \\export fn entry() void { - \\ comptime var v: S = undefined; - \\ v.u.A = U{ .A = i32 }; - \\} - , &[_][]const u8{ - "tmp.zig:9:8: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("union with too small explicit signed tag type", - \\const U = union(enum(i2)) { - \\ A: u8, - \\ B: u8, - \\ C: u8, - \\ D: u8, - \\}; - \\export fn entry() void { - \\ _ = U{ .D = 1 }; - \\} - , &[_][]const u8{ - "tmp.zig:1:22: error: specified integer tag type cannot represent every field", - "tmp.zig:1:22: note: type i2 cannot fit values in range 0...3", - }); - - ctx.objErrStage1("union with too small explicit unsigned tag type", - \\const U = union(enum(u2)) { - \\ A: u8, - \\ B: u8, - \\ C: u8, - \\ D: u8, - \\ E: u8, - \\}; - \\export fn entry() void { - \\ _ = U{ .E = 1 }; - \\} - , &[_][]const u8{ - "tmp.zig:1:22: error: specified integer tag type cannot represent every field", - "tmp.zig:1:22: note: type u2 cannot fit values in range 0...4", - }); { const case = ctx.obj("callconv(.Interrupt) on unsupported platform", .{ @@ -308,1550 +134,6 @@ pub fn addCases(ctx: *TestContext) !void { }); } - ctx.objErrStage1("unreachable executed at comptime", - \\fn foo(comptime x: i32) i32 { - \\ comptime { - \\ if (x >= 0) return -x; - \\ unreachable; - \\ } - \\} - \\export fn entry() void { - \\ _ = foo(-42); - \\} - , &[_][]const u8{ - "tmp.zig:4:9: error: reached unreachable code", - "tmp.zig:8:12: note: called from here", - }); - - ctx.objErrStage1("@Type with Type.Int", - \\const builtin = @import("std").builtin; - \\export fn entry() void { - \\ _ = @Type(builtin.Type.Int{ - \\ .signedness = .signed, - \\ .bits = 8, - \\ }); - \\} - , &[_][]const u8{ - "tmp.zig:3:31: error: expected type 'std.builtin.Type', found 'std.builtin.Type.Int'", - }); - - ctx.objErrStage1("indexing a undefined slice at comptime", - \\comptime { - \\ var slice: []u8 = undefined; - \\ slice[0] = 2; - \\} - , &[_][]const u8{ - "tmp.zig:3:10: error: index 0 outside slice of size 0", - }); - - ctx.objErrStage1("array in c exported function", - \\export fn zig_array(x: [10]u8) void { - \\ try std.testing.expect(std.mem.eql(u8, &x, "1234567890")); - \\} - \\const std = @import("std"); - \\export fn zig_return_array() [10]u8 { - \\ return "1234567890".*; - \\} - , &[_][]const u8{ - "tmp.zig:1:24: error: parameter of type '[10]u8' not allowed in function with calling convention 'C'", - "tmp.zig:5:30: error: return type '[10]u8' not allowed in function with calling convention 'C'", - }); - - ctx.objErrStage1("@Type for exhaustive enum with undefined tag type", - \\const Tag = @Type(.{ - \\ .Enum = .{ - \\ .layout = .Auto, - \\ .tag_type = undefined, - \\ .fields = &.{}, - \\ .decls = &.{}, - \\ .is_exhaustive = false, - \\ }, - \\}); - \\export fn entry() void { - \\ _ = @intToEnum(Tag, 0); - \\} - , &[_][]const u8{ - "tmp.zig:1:20: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("extern struct with non-extern-compatible integer tag type", - \\pub const E = enum(u31) { A, B, C }; - \\pub const S = extern struct { - \\ e: E, - \\}; - \\export fn entry() void { - \\ const s: S = undefined; - \\ _ = s; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: extern structs cannot contain fields of type 'E'", - }); - - ctx.objErrStage1("@Type for exhaustive enum with non-integer tag type", - \\const Tag = @Type(.{ - \\ .Enum = .{ - \\ .layout = .Auto, - \\ .tag_type = bool, - \\ .fields = &.{}, - \\ .decls = &.{}, - \\ .is_exhaustive = false, - \\ }, - \\}); - \\export fn entry() void { - \\ _ = @intToEnum(Tag, 0); - \\} - , &[_][]const u8{ - "tmp.zig:1:20: error: Type.Enum.tag_type must be an integer type, not 'bool'", - }); - - ctx.objErrStage1("extern struct with extern-compatible but inferred integer tag type", - \\pub const E = enum { - \\@"0",@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"10",@"11",@"12", - \\@"13",@"14",@"15",@"16",@"17",@"18",@"19",@"20",@"21",@"22",@"23", - \\@"24",@"25",@"26",@"27",@"28",@"29",@"30",@"31",@"32",@"33",@"34", - \\@"35",@"36",@"37",@"38",@"39",@"40",@"41",@"42",@"43",@"44",@"45", - \\@"46",@"47",@"48",@"49",@"50",@"51",@"52",@"53",@"54",@"55",@"56", - \\@"57",@"58",@"59",@"60",@"61",@"62",@"63",@"64",@"65",@"66",@"67", - \\@"68",@"69",@"70",@"71",@"72",@"73",@"74",@"75",@"76",@"77",@"78", - \\@"79",@"80",@"81",@"82",@"83",@"84",@"85",@"86",@"87",@"88",@"89", - \\@"90",@"91",@"92",@"93",@"94",@"95",@"96",@"97",@"98",@"99",@"100", - \\@"101",@"102",@"103",@"104",@"105",@"106",@"107",@"108",@"109", - \\@"110",@"111",@"112",@"113",@"114",@"115",@"116",@"117",@"118", - \\@"119",@"120",@"121",@"122",@"123",@"124",@"125",@"126",@"127", - \\@"128",@"129",@"130",@"131",@"132",@"133",@"134",@"135",@"136", - \\@"137",@"138",@"139",@"140",@"141",@"142",@"143",@"144",@"145", - \\@"146",@"147",@"148",@"149",@"150",@"151",@"152",@"153",@"154", - \\@"155",@"156",@"157",@"158",@"159",@"160",@"161",@"162",@"163", - \\@"164",@"165",@"166",@"167",@"168",@"169",@"170",@"171",@"172", - \\@"173",@"174",@"175",@"176",@"177",@"178",@"179",@"180",@"181", - \\@"182",@"183",@"184",@"185",@"186",@"187",@"188",@"189",@"190", - \\@"191",@"192",@"193",@"194",@"195",@"196",@"197",@"198",@"199", - \\@"200",@"201",@"202",@"203",@"204",@"205",@"206",@"207",@"208", - \\@"209",@"210",@"211",@"212",@"213",@"214",@"215",@"216",@"217", - \\@"218",@"219",@"220",@"221",@"222",@"223",@"224",@"225",@"226", - \\@"227",@"228",@"229",@"230",@"231",@"232",@"233",@"234",@"235", - \\@"236",@"237",@"238",@"239",@"240",@"241",@"242",@"243",@"244", - \\@"245",@"246",@"247",@"248",@"249",@"250",@"251",@"252",@"253", - \\@"254",@"255" - \\}; - \\pub const S = extern struct { - \\ e: E, - \\}; - \\export fn entry() void { - \\ if (@typeInfo(E).Enum.tag_type != u8) @compileError("did not infer u8 tag type"); - \\ const s: S = undefined; - \\ _ = s; - \\} - , &[_][]const u8{ - "tmp.zig:31:5: error: extern structs cannot contain fields of type 'E'", - }); - - ctx.objErrStage1("@Type for tagged union with extra enum field", - \\const Tag = @Type(.{ - \\ .Enum = .{ - \\ .layout = .Auto, - \\ .tag_type = u2, - \\ .fields = &.{ - \\ .{ .name = "signed", .value = 0 }, - \\ .{ .name = "unsigned", .value = 1 }, - \\ .{ .name = "arst", .value = 2 }, - \\ }, - \\ .decls = &.{}, - \\ .is_exhaustive = true, - \\ }, - \\}); - \\const Tagged = @Type(.{ - \\ .Union = .{ - \\ .layout = .Auto, - \\ .tag_type = Tag, - \\ .fields = &.{ - \\ .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) }, - \\ .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) }, - \\ }, - \\ .decls = &.{}, - \\ }, - \\}); - \\export fn entry() void { - \\ var tagged = Tagged{ .signed = -1 }; - \\ tagged = .{ .unsigned = 1 }; - \\} - , &[_][]const u8{ - "tmp.zig:14:23: error: enum field missing: 'arst'", - }); - - ctx.objErrStage1("field access of opaque type", - \\const MyType = opaque {}; - \\ - \\export fn entry() bool { - \\ var x: i32 = 1; - \\ return bar(@ptrCast(*MyType, &x)); - \\} - \\ - \\fn bar(x: *MyType) bool { - \\ return x.blah; - \\} - , &[_][]const u8{ - "tmp.zig:9:13: error: no member named 'blah' in opaque type 'MyType'", - }); - - ctx.objErrStage1("opaque type with field", - \\const Opaque = opaque { foo: i32 }; - \\export fn entry() void { - \\ const foo: ?*Opaque = null; - \\ _ = foo; - \\} - , &[_][]const u8{ - "tmp.zig:1:25: error: opaque types cannot have fields", - }); - - ctx.objErrStage1("@Type(.Fn) with is_generic = true", - \\const Foo = @Type(.{ - \\ .Fn = .{ - \\ .calling_convention = .Unspecified, - \\ .alignment = 0, - \\ .is_generic = true, - \\ .is_var_args = false, - \\ .return_type = u0, - \\ .args = &.{}, - \\ }, - \\}); - \\comptime { _ = Foo; } - , &[_][]const u8{ - "tmp.zig:1:20: error: Type.Fn.is_generic must be false for @Type", - }); - - ctx.objErrStage1("@Type(.Fn) with is_var_args = true and non-C callconv", - \\const Foo = @Type(.{ - \\ .Fn = .{ - \\ .calling_convention = .Unspecified, - \\ .alignment = 0, - \\ .is_generic = false, - \\ .is_var_args = true, - \\ .return_type = u0, - \\ .args = &.{}, - \\ }, - \\}); - \\comptime { _ = Foo; } - , &[_][]const u8{ - "tmp.zig:1:20: error: varargs functions must have C calling convention", - }); - - ctx.objErrStage1("@Type(.Fn) with return_type = null", - \\const Foo = @Type(.{ - \\ .Fn = .{ - \\ .calling_convention = .Unspecified, - \\ .alignment = 0, - \\ .is_generic = false, - \\ .is_var_args = false, - \\ .return_type = null, - \\ .args = &.{}, - \\ }, - \\}); - \\comptime { _ = Foo; } - , &[_][]const u8{ - "tmp.zig:1:20: error: Type.Fn.return_type must be non-null for @Type", - }); - - ctx.objErrStage1("@Type for union with opaque field", - \\const Untagged = @Type(.{ - \\ .Union = .{ - \\ .layout = .Auto, - \\ .tag_type = null, - \\ .fields = &.{ - \\ .{ .name = "foo", .field_type = opaque {}, .alignment = 1 }, - \\ }, - \\ .decls = &.{}, - \\ }, - \\}); - \\export fn entry() void { - \\ _ = Untagged{}; - \\} - , &[_][]const u8{ - "tmp.zig:1:25: error: opaque types have unknown size and therefore cannot be directly embedded in unions", - }); - - ctx.objErrStage1("slice sentinel mismatch", - \\export fn entry() void { - \\ const x = @import("std").meta.Vector(3, f32){ 25, 75, 5, 0 }; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:62: error: index 3 outside vector of size 3", - }); - - ctx.objErrStage1("slice sentinel mismatch", - \\export fn entry() void { - \\ const y: [:1]const u8 = &[_:2]u8{ 1, 2 }; - \\ _ = y; - \\} - , &[_][]const u8{ - "tmp.zig:2:37: error: expected type '[:1]const u8', found '*const [2:2]u8'", - }); - - ctx.objErrStage1("@Type for union with zero fields", - \\const Untagged = @Type(.{ - \\ .Union = .{ - \\ .layout = .Auto, - \\ .tag_type = null, - \\ .fields = &.{}, - \\ .decls = &.{}, - \\ }, - \\}); - \\export fn entry() void { - \\ _ = Untagged{}; - \\} - , &[_][]const u8{ - "tmp.zig:1:25: error: unions must have 1 or more fields", - }); - - ctx.objErrStage1("@Type for exhaustive enum with zero fields", - \\const Tag = @Type(.{ - \\ .Enum = .{ - \\ .layout = .Auto, - \\ .tag_type = u1, - \\ .fields = &.{}, - \\ .decls = &.{}, - \\ .is_exhaustive = true, - \\ }, - \\}); - \\export fn entry() void { - \\ _ = @intToEnum(Tag, 0); - \\} - , &[_][]const u8{ - "tmp.zig:1:20: error: enums must have 1 or more fields", - }); - - ctx.objErrStage1("@Type for tagged union with extra union field", - \\const Tag = @Type(.{ - \\ .Enum = .{ - \\ .layout = .Auto, - \\ .tag_type = u1, - \\ .fields = &.{ - \\ .{ .name = "signed", .value = 0 }, - \\ .{ .name = "unsigned", .value = 1 }, - \\ }, - \\ .decls = &.{}, - \\ .is_exhaustive = true, - \\ }, - \\}); - \\const Tagged = @Type(.{ - \\ .Union = .{ - \\ .layout = .Auto, - \\ .tag_type = Tag, - \\ .fields = &.{ - \\ .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) }, - \\ .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) }, - \\ .{ .name = "arst", .field_type = f32, .alignment = @alignOf(f32) }, - \\ }, - \\ .decls = &.{}, - \\ }, - \\}); - \\export fn entry() void { - \\ var tagged = Tagged{ .signed = -1 }; - \\ tagged = .{ .unsigned = 1 }; - \\} - , &[_][]const u8{ - "tmp.zig:13:23: error: enum field not found: 'arst'", - "tmp.zig:1:20: note: enum declared here", - }); - - ctx.objErrStage1("@Type with undefined", - \\comptime { - \\ _ = @Type(.{ .Array = .{ .len = 0, .child = u8, .sentinel = undefined } }); - \\} - \\comptime { - \\ _ = @Type(.{ - \\ .Struct = .{ - \\ .fields = undefined, - \\ .decls = undefined, - \\ .is_tuple = false, - \\ .layout = .Auto, - \\ }, - \\ }); - \\} - , &[_][]const u8{ - "tmp.zig:2:16: error: use of undefined value here causes undefined behavior", - "tmp.zig:5:16: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("struct with declarations unavailable for @Type", - \\export fn entry() void { - \\ _ = @Type(@typeInfo(struct { const foo = 1; })); - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: Type.Struct.decls must be empty for @Type", - }); - - ctx.objErrStage1("enum with declarations unavailable for @Type", - \\export fn entry() void { - \\ _ = @Type(@typeInfo(enum { foo, const bar = 1; })); - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: Type.Enum.decls must be empty for @Type", - }); - - ctx.testErrStage1("reject extern variables with initializers", - \\extern var foo: int = 2; - , &[_][]const u8{ - "tmp.zig:1:23: error: extern variables have no initializers", - }); - - ctx.testErrStage1("duplicate/unused labels", - \\comptime { - \\ blk: { blk: while (false) {} } - \\} - \\comptime { - \\ blk: while (false) { blk: for (@as([0]void, undefined)) |_| {} } - \\} - \\comptime { - \\ blk: for (@as([0]void, undefined)) |_| { blk: {} } - \\} - \\comptime { - \\ blk: {} - \\} - \\comptime { - \\ blk: while(false) {} - \\} - \\comptime { - \\ blk: for(@as([0]void, undefined)) |_| {} - \\} - , &[_][]const u8{ - "tmp.zig:2:12: error: redefinition of label 'blk'", - "tmp.zig:2:5: note: previous definition here", - "tmp.zig:5:26: error: redefinition of label 'blk'", - "tmp.zig:5:5: note: previous definition here", - "tmp.zig:8:46: error: redefinition of label 'blk'", - "tmp.zig:8:5: note: previous definition here", - "tmp.zig:11:5: error: unused block label", - "tmp.zig:14:5: error: unused while loop label", - "tmp.zig:17:5: error: unused for loop label", - }); - - ctx.testErrStage1("@alignCast of zero sized types", - \\export fn foo() void { - \\ const a: *void = undefined; - \\ _ = @alignCast(2, a); - \\} - \\export fn bar() void { - \\ const a: ?*void = undefined; - \\ _ = @alignCast(2, a); - \\} - \\export fn baz() void { - \\ const a: []void = undefined; - \\ _ = @alignCast(2, a); - \\} - \\export fn qux() void { - \\ const a = struct { - \\ fn a(comptime b: u32) void { _ = b; } - \\ }.a; - \\ _ = @alignCast(2, a); - \\} - , &[_][]const u8{ - "tmp.zig:3:23: error: cannot adjust alignment of zero sized type '*void'", - "tmp.zig:7:23: error: cannot adjust alignment of zero sized type '?*void'", - "tmp.zig:11:23: error: cannot adjust alignment of zero sized type '[]void'", - "tmp.zig:17:23: error: cannot adjust alignment of zero sized type 'fn(u32) anytype'", - }); - - ctx.testErrStage1("invalid non-exhaustive enum to union", - \\const E = enum(u8) { - \\ a, - \\ b, - \\ _, - \\}; - \\const U = union(E) { - \\ a, - \\ b, - \\}; - \\export fn foo() void { - \\ var e = @intToEnum(E, 15); - \\ var u: U = e; - \\ _ = u; - \\} - \\export fn bar() void { - \\ const e = @intToEnum(E, 15); - \\ var u: U = e; - \\ _ = u; - \\} - , &[_][]const u8{ - "tmp.zig:12:16: error: runtime cast to union 'U' from non-exhaustive enum", - "tmp.zig:17:16: error: no tag by value 15", - }); - - ctx.testErrStage1("switching with exhaustive enum has '_' prong ", - \\const E = enum{ - \\ a, - \\ b, - \\}; - \\pub export fn entry() void { - \\ var e: E = .b; - \\ switch (e) { - \\ .a => {}, - \\ .b => {}, - \\ _ => {}, - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:7:5: error: switch on exhaustive enum has `_` prong", - }); - - ctx.testErrStage1("invalid pointer with @Type", - \\export fn entry() void { - \\ _ = @Type(.{ .Pointer = .{ - \\ .size = .One, - \\ .is_const = false, - \\ .is_volatile = false, - \\ .alignment = 1, - \\ .address_space = .generic, - \\ .child = u8, - \\ .is_allowzero = false, - \\ .sentinel = &@as(u8, 0), - \\ }}); - \\} - , &[_][]const u8{ - "tmp.zig:2:16: error: sentinels are only allowed on slices and unknown-length pointers", - }); - - ctx.objErrStage1("@Type(.Pointer) with invalid address space ", - \\export fn entry() void { - \\ _ = @Type(.{ .Pointer = .{ - \\ .size = .One, - \\ .is_const = false, - \\ .is_volatile = false, - \\ .alignment = 1, - \\ .address_space = .gs, - \\ .child = u8, - \\ .is_allowzero = false, - \\ .sentinel = null, - \\ }}); - \\} - , &[_][]const u8{ - "tmp.zig:2:16: error: address space 'gs' not available in stage 1 compiler, must be .generic", - }); - - ctx.testErrStage1("helpful return type error message", - \\export fn foo() u32 { - \\ return error.Ohno; - \\} - \\fn bar() !u32 { - \\ return error.Ohno; - \\} - \\export fn baz() void { - \\ try bar(); - \\} - \\export fn qux() u32 { - \\ return bar(); - \\} - \\export fn quux() u32 { - \\ var buf: u32 = 0; - \\ buf = bar(); - \\} - , &[_][]const u8{ - "tmp.zig:2:17: error: expected type 'u32', found 'error{Ohno}'", - "tmp.zig:1:17: note: function cannot return an error", - "tmp.zig:8:5: error: expected type 'void', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set'", - "tmp.zig:7:17: note: function cannot return an error", - "tmp.zig:11:15: error: cannot convert error union to payload type. consider using `try`, `catch`, or `if`. expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set!u32'", - "tmp.zig:10:17: note: function cannot return an error", - "tmp.zig:15:14: error: cannot convert error union to payload type. consider using `try`, `catch`, or `if`. expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set!u32'", - "tmp.zig:14:5: note: cannot store an error in type 'u32'", - }); - - ctx.testErrStage1("int/float conversion to comptime_int/float", - \\export fn foo() void { - \\ var a: f32 = 2; - \\ _ = @floatToInt(comptime_int, a); - \\} - \\export fn bar() void { - \\ var a: u32 = 2; - \\ _ = @intToFloat(comptime_float, a); - \\} - , &[_][]const u8{ - "tmp.zig:3:35: error: unable to evaluate constant expression", - "tmp.zig:7:37: error: unable to evaluate constant expression", - }); - - ctx.objErrStage1("extern variable has no type", - \\extern var foo; - \\pub export fn entry() void { - \\ foo; - \\} - , &[_][]const u8{ - "tmp.zig:1:8: error: unable to infer variable type", - }); - - ctx.objErrStage1("@src outside function", - \\comptime { - \\ @src(); - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: @src outside function", - }); - - ctx.objErrStage1("call assigned to constant", - \\const Foo = struct { - \\ x: i32, - \\}; - \\fn foo() Foo { - \\ return .{ .x = 42 }; - \\} - \\fn bar(val: anytype) Foo { - \\ return .{ .x = val }; - \\} - \\export fn entry() void { - \\ const baz: Foo = undefined; - \\ baz = foo(); - \\} - \\export fn entry1() void { - \\ const baz: Foo = undefined; - \\ baz = bar(42); - \\} - , &[_][]const u8{ - "tmp.zig:12:14: error: cannot assign to constant", - "tmp.zig:16:14: error: cannot assign to constant", - }); - - ctx.objErrStage1("invalid pointer syntax", - \\export fn foo() void { - \\ var guid: *:0 const u8 = undefined; - \\} - , &[_][]const u8{ - "tmp.zig:2:16: error: expected type expression, found ':'", - }); - - ctx.objErrStage1("declaration between fields", - \\const S = struct { - \\ const foo = 2; - \\ const bar = 2; - \\ const baz = 2; - \\ a: struct { - \\ a: u32, - \\ b: u32, - \\ }, - \\ const foo1 = 2; - \\ const bar1 = 2; - \\ const baz1 = 2; - \\ b: usize, - \\}; - \\comptime { - \\ _ = S; - \\} - , &[_][]const u8{ - "tmp.zig:9:5: error: declarations are not allowed between container fields", - "tmp.zig:5:5: note: field before declarations here", - "tmp.zig:12:5: note: field after declarations here", - }); - - ctx.objErrStage1("non-extern function with var args", - \\fn foo(args: ...) void {} - \\export fn entry() void { - \\ foo(); - \\} - , &[_][]const u8{ - "tmp.zig:1:14: error: expected type expression, found '...'", - }); - - ctx.testErrStage1("invalid int casts", - \\export fn foo() void { - \\ var a: u32 = 2; - \\ _ = @intCast(comptime_int, a); - \\} - \\export fn bar() void { - \\ var a: u32 = 2; - \\ _ = @intToFloat(u32, a); - \\} - \\export fn baz() void { - \\ var a: u32 = 2; - \\ _ = @floatToInt(u32, a); - \\} - \\export fn qux() void { - \\ var a: f32 = 2; - \\ _ = @intCast(u32, a); - \\} - , &[_][]const u8{ - "tmp.zig:3:32: error: unable to evaluate constant expression", - "tmp.zig:7:21: error: expected float type, found 'u32'", - "tmp.zig:11:26: error: expected float type, found 'u32'", - "tmp.zig:15:23: error: expected integer type, found 'f32'", - }); - - ctx.testErrStage1("invalid float casts", - \\export fn foo() void { - \\ var a: f32 = 2; - \\ _ = @floatCast(comptime_float, a); - \\} - \\export fn bar() void { - \\ var a: f32 = 2; - \\ _ = @floatToInt(f32, a); - \\} - \\export fn baz() void { - \\ var a: f32 = 2; - \\ _ = @intToFloat(f32, a); - \\} - \\export fn qux() void { - \\ var a: u32 = 2; - \\ _ = @floatCast(f32, a); - \\} - , &[_][]const u8{ - "tmp.zig:3:36: error: unable to evaluate constant expression", - "tmp.zig:7:21: error: expected integer type, found 'f32'", - "tmp.zig:11:26: error: expected int type, found 'f32'", - "tmp.zig:15:25: error: expected float type, found 'u32'", - }); - - ctx.testErrStage1("invalid assignments", - \\export fn entry1() void { - \\ var a: []const u8 = "foo"; - \\ a[0..2] = "bar"; - \\} - \\export fn entry2() void { - \\ var a: u8 = 2; - \\ a + 2 = 3; - \\} - \\export fn entry4() void { - \\ 2 + 2 = 3; - \\} - , &[_][]const u8{ - "tmp.zig:3:6: error: invalid left-hand side to assignment", - "tmp.zig:7:7: error: invalid left-hand side to assignment", - "tmp.zig:10:7: error: invalid left-hand side to assignment", - }); - - ctx.testErrStage1("reassign to array parameter", - \\fn reassign(a: [3]f32) void { - \\ a = [3]f32{4, 5, 6}; - \\} - \\export fn entry() void { - \\ reassign(.{1, 2, 3}); - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: cannot assign to constant", - }); - - ctx.testErrStage1("reassign to slice parameter", - \\pub fn reassign(s: []const u8) void { - \\ s = s[0..]; - \\} - \\export fn entry() void { - \\ reassign("foo"); - \\} - , &[_][]const u8{ - "tmp.zig:2:10: error: cannot assign to constant", - }); - - ctx.testErrStage1("reassign to struct parameter", - \\const S = struct { - \\ x: u32, - \\}; - \\fn reassign(s: S) void { - \\ s = S{.x = 2}; - \\} - \\export fn entry() void { - \\ reassign(S{.x = 3}); - \\} - , &[_][]const u8{ - "tmp.zig:5:10: error: cannot assign to constant", - }); - - ctx.testErrStage1("reference to const data", - \\export fn foo() void { - \\ var ptr = &[_]u8{0,0,0,0}; - \\ ptr[1] = 2; - \\} - \\export fn bar() void { - \\ var ptr = &@as(u32, 2); - \\ ptr.* = 2; - \\} - \\export fn baz() void { - \\ var ptr = &true; - \\ ptr.* = false; - \\} - \\export fn qux() void { - \\ const S = struct{ - \\ x: usize, - \\ y: usize, - \\ }; - \\ var ptr = &S{.x=1,.y=2}; - \\ ptr.x = 2; - \\} - , &[_][]const u8{ - "tmp.zig:3:14: error: cannot assign to constant", - "tmp.zig:7:13: error: cannot assign to constant", - "tmp.zig:11:13: error: cannot assign to constant", - "tmp.zig:19:13: error: cannot assign to constant", - }); - - ctx.testErrStage1("cast between ?T where T is not a pointer", - \\pub const fnty1 = ?fn (i8) void; - \\pub const fnty2 = ?fn (u64) void; - \\export fn entry() void { - \\ var a: fnty1 = undefined; - \\ var b: fnty2 = undefined; - \\ a = b; - \\} - , &[_][]const u8{ - "tmp.zig:6:9: error: expected type '?fn(i8) void', found '?fn(u64) void'", - "tmp.zig:6:9: note: optional type child 'fn(u64) void' cannot cast into optional type child 'fn(i8) void'", - }); - - ctx.testErrStage1("unused variable error on errdefer", - \\fn foo() !void { - \\ errdefer |a| unreachable; - \\ return error.A; - \\} - \\export fn entry() void { - \\ foo() catch unreachable; - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: unused variable: 'a'", - }); - - ctx.testErrStage1("comparison of non-tagged union and enum literal", - \\export fn entry() void { - \\ const U = union { A: u32, B: u64 }; - \\ var u = U{ .A = 42 }; - \\ var ok = u == .A; - \\ _ = ok; - \\} - , &[_][]const u8{ - "tmp.zig:4:16: error: comparison of union and enum literal is only valid for tagged union types", - "tmp.zig:2:15: note: type U is not a tagged union", - }); - - ctx.testErrStage1("shift on type with non-power-of-two size", - \\export fn entry() void { - \\ const S = struct { - \\ fn a() void { - \\ var x: u24 = 42; - \\ _ = x >> 24; - \\ } - \\ fn b() void { - \\ var x: u24 = 42; - \\ _ = x << 24; - \\ } - \\ fn c() void { - \\ var x: u24 = 42; - \\ _ = @shlExact(x, 24); - \\ } - \\ fn d() void { - \\ var x: u24 = 42; - \\ _ = @shrExact(x, 24); - \\ } - \\ }; - \\ S.a(); - \\ S.b(); - \\ S.c(); - \\ S.d(); - \\} - , &[_][]const u8{ - "tmp.zig:5:19: error: RHS of shift is too large for LHS type", - "tmp.zig:9:19: error: RHS of shift is too large for LHS type", - "tmp.zig:13:17: error: RHS of shift is too large for LHS type", - "tmp.zig:17:17: error: RHS of shift is too large for LHS type", - }); - - ctx.testErrStage1("combination of nosuspend and async", - \\export fn entry() void { - \\ nosuspend { - \\ const bar = async foo(); - \\ suspend {} - \\ resume bar; - \\ } - \\} - \\fn foo() void {} - , &[_][]const u8{ - "tmp.zig:4:9: error: suspend inside nosuspend block", - "tmp.zig:2:5: note: nosuspend block here", - }); - - ctx.objErrStage1("atomicrmw with bool op not .Xchg", - \\export fn entry() void { - \\ var x = false; - \\ _ = @atomicRmw(bool, &x, .Add, true, .SeqCst); - \\} - , &[_][]const u8{ - "tmp.zig:3:30: error: @atomicRmw with bool only allowed with .Xchg", - }); - - ctx.testErrStage1("@TypeOf with no arguments", - \\export fn entry() void { - \\ _ = @TypeOf(); - \\} - , &[_][]const u8{ - "tmp.zig:2:9: error: expected at least 1 argument, found 0", - }); - - ctx.testErrStage1("@TypeOf with incompatible arguments", - \\export fn entry() void { - \\ var var_1: f32 = undefined; - \\ var var_2: u32 = undefined; - \\ _ = @TypeOf(var_1, var_2); - \\} - , &[_][]const u8{ - "tmp.zig:4:9: error: incompatible types: 'f32' and 'u32'", - }); - - ctx.testErrStage1("type mismatch with tuple concatenation", - \\export fn entry() void { - \\ var x = .{}; - \\ x = x ++ .{ 1, 2, 3 }; - \\} - , &[_][]const u8{ - "tmp.zig:3:11: error: expected type 'struct:2:14', found 'struct:3:11'", - }); - - ctx.testErrStage1("@tagName on invalid value of non-exhaustive enum", - \\test "enum" { - \\ const E = enum(u8) {A, B, _}; - \\ _ = @tagName(@intToEnum(E, 5)); - \\} - , &[_][]const u8{ - "tmp.zig:3:18: error: no tag by value 5", - }); - - ctx.testErrStage1("@ptrToInt with pointer to zero-sized type", - \\export fn entry() void { - \\ var pointer: ?*u0 = null; - \\ var x = @ptrToInt(pointer); - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:3:23: error: pointer to size 0 type has no address", - }); - - ctx.testErrStage1("access invalid @typeInfo decl", - \\const A = B; - \\test "Crash" { - \\ _ = @typeInfo(@This()).Struct.decls[0]; - \\} - , &[_][]const u8{ - "tmp.zig:1:11: error: use of undeclared identifier 'B'", - }); - - ctx.testErrStage1("reject extern function definitions with body", - \\extern "c" fn definitelyNotInLibC(a: i32, b: i32) i32 { - \\ return a + b; - \\} - , &[_][]const u8{ - "tmp.zig:1:1: error: extern functions have no body", - }); - - ctx.testErrStage1("duplicate field in anonymous struct literal", - \\export fn entry() void { - \\ const anon = .{ - \\ .inner = .{ - \\ .a = .{ - \\ .something = "text", - \\ }, - \\ .a = .{}, - \\ }, - \\ }; - \\ _ = anon; - \\} - , &[_][]const u8{ - "tmp.zig:7:13: error: duplicate field", - "tmp.zig:4:13: note: other field here", - }); - - ctx.testErrStage1("type mismatch in C prototype with varargs", - \\const fn_ty = ?fn ([*c]u8, ...) callconv(.C) void; - \\extern fn fn_decl(fmt: [*:0]u8, ...) void; - \\ - \\export fn main() void { - \\ const x: fn_ty = fn_decl; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:5:22: error: expected type 'fn([*c]u8, ...) callconv(.C) void', found 'fn([*:0]u8, ...) callconv(.C) void'", - }); - - ctx.objErrStage1("function call assigned to incorrect type", - \\export fn entry() void { - \\ var arr: [4]f32 = undefined; - \\ arr = concat(); - \\} - \\fn concat() [16]f32 { - \\ return [1]f32{0}**16; - \\} - , &[_][]const u8{ - "tmp.zig:3:17: error: expected type '[4]f32', found '[16]f32'", - }); - - ctx.objErrStage1("generic function call assigned to incorrect type", - \\pub export fn entry() void { - \\ var res: []i32 = undefined; - \\ res = myAlloc(i32); - \\} - \\fn myAlloc(comptime arg: type) anyerror!arg{ - \\ unreachable; - \\} - , &[_][]const u8{ - "tmp.zig:3:18: error: expected type '[]i32', found 'anyerror!i32", - }); - - ctx.testErrStage1("non-exhaustive enum marker assigned a value", - \\const A = enum { - \\ a, - \\ b, - \\ _ = 1, - \\}; - \\const B = enum { - \\ a, - \\ b, - \\ _, - \\}; - \\comptime { _ = A; _ = B; } - , &[_][]const u8{ - "tmp.zig:4:9: error: '_' is used to mark an enum as non-exhaustive and cannot be assigned a value", - "tmp.zig:6:11: error: non-exhaustive enum missing integer tag type", - "tmp.zig:9:5: note: marked non-exhaustive here", - }); - - ctx.testErrStage1("non-exhaustive enums", - \\const B = enum(u1) { - \\ a, - \\ _, - \\ b, - \\}; - \\const C = enum(u1) { - \\ a, - \\ b, - \\ _, - \\}; - \\pub export fn entry() void { - \\ _ = B; - \\ _ = C; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: '_' field of non-exhaustive enum must be last", - "tmp.zig:6:11: error: non-exhaustive enum specifies every value", - }); - - ctx.testErrStage1("switching with non-exhaustive enums", - \\const E = enum(u8) { - \\ a, - \\ b, - \\ _, - \\}; - \\const U = union(E) { - \\ a: i32, - \\ b: u32, - \\}; - \\pub export fn entry() void { - \\ var e: E = .b; - \\ switch (e) { // error: switch not handling the tag `b` - \\ .a => {}, - \\ _ => {}, - \\ } - \\ switch (e) { // error: switch on non-exhaustive enum must include `else` or `_` prong - \\ .a => {}, - \\ .b => {}, - \\ } - \\ var u = U{.a = 2}; - \\ switch (u) { // error: `_` prong not allowed when switching on tagged union - \\ .a => {}, - \\ .b => {}, - \\ _ => {}, - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:12:5: error: enumeration value 'E.b' not handled in switch", - "tmp.zig:16:5: error: switch on non-exhaustive enum must include `else` or `_` prong", - "tmp.zig:21:5: error: `_` prong not allowed when switching on tagged union", - }); - - ctx.objErrStage1("switch expression - unreachable else prong (bool)", - \\fn foo(x: bool) void { - \\ switch (x) { - \\ true => {}, - \\ false => {}, - \\ else => {}, - \\ } - \\} - \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } - , &[_][]const u8{ - "tmp.zig:5:9: error: unreachable else prong, all cases already handled", - }); - - ctx.objErrStage1("switch expression - unreachable else prong (u1)", - \\fn foo(x: u1) void { - \\ switch (x) { - \\ 0 => {}, - \\ 1 => {}, - \\ else => {}, - \\ } - \\} - \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } - , &[_][]const u8{ - "tmp.zig:5:9: error: unreachable else prong, all cases already handled", - }); - - ctx.objErrStage1("switch expression - unreachable else prong (u2)", - \\fn foo(x: u2) void { - \\ switch (x) { - \\ 0 => {}, - \\ 1 => {}, - \\ 2 => {}, - \\ 3 => {}, - \\ else => {}, - \\ } - \\} - \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } - , &[_][]const u8{ - "tmp.zig:7:9: error: unreachable else prong, all cases already handled", - }); - - ctx.objErrStage1("switch expression - unreachable else prong (range u8)", - \\fn foo(x: u8) void { - \\ switch (x) { - \\ 0 => {}, - \\ 1 => {}, - \\ 2 => {}, - \\ 3 => {}, - \\ 4...255 => {}, - \\ else => {}, - \\ } - \\} - \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } - , &[_][]const u8{ - "tmp.zig:8:9: error: unreachable else prong, all cases already handled", - }); - - ctx.objErrStage1("switch expression - unreachable else prong (range i8)", - \\fn foo(x: i8) void { - \\ switch (x) { - \\ -128...0 => {}, - \\ 1 => {}, - \\ 2 => {}, - \\ 3 => {}, - \\ 4...127 => {}, - \\ else => {}, - \\ } - \\} - \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } - , &[_][]const u8{ - "tmp.zig:8:9: error: unreachable else prong, all cases already handled", - }); - - ctx.objErrStage1("switch expression - unreachable else prong (enum)", - \\const TestEnum = enum{ T1, T2 }; - \\ - \\fn err(x: u8) TestEnum { - \\ switch (x) { - \\ 0 => return TestEnum.T1, - \\ else => return TestEnum.T2, - \\ } - \\} - \\ - \\fn foo(x: u8) void { - \\ switch (err(x)) { - \\ TestEnum.T1 => {}, - \\ TestEnum.T2 => {}, - \\ else => {}, - \\ } - \\} - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } - , &[_][]const u8{ - "tmp.zig:14:9: error: unreachable else prong, all cases already handled", - }); - - ctx.testErrStage1("@export with empty name string", - \\pub export fn entry() void { } - \\comptime { - \\ @export(entry, .{ .name = "" }); - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: exported symbol name cannot be empty", - }); - - ctx.testErrStage1("switch ranges endpoints are validated", - \\pub export fn entry() void { - \\ var x: i32 = 0; - \\ switch (x) { - \\ 6...1 => {}, - \\ -1...-5 => {}, - \\ else => unreachable, - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:4:9: error: range start value is greater than the end value", - "tmp.zig:5:9: error: range start value is greater than the end value", - }); - - ctx.testErrStage1("errors in for loop bodies are propagated", - \\pub export fn entry() void { - \\ var arr: [100]u8 = undefined; - \\ for (arr) |bits| _ = @popCount(bits); - \\} - , &[_][]const u8{ - "tmp.zig:3:26: error: expected 2 arguments, found 1", - }); - - ctx.testErrStage1("@call rejects non comptime-known fn - always_inline", - \\pub export fn entry() void { - \\ var call_me: fn () void = undefined; - \\ @call(.{ .modifier = .always_inline }, call_me, .{}); - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: the specified modifier requires a comptime-known function", - }); - - ctx.testErrStage1("@call rejects non comptime-known fn - compile_time", - \\pub export fn entry() void { - \\ var call_me: fn () void = undefined; - \\ @call(.{ .modifier = .compile_time }, call_me, .{}); - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: the specified modifier requires a comptime-known function", - }); - - ctx.testErrStage1("error in struct initializer doesn't crash the compiler", - \\pub export fn entry() void { - \\ const bitfield = struct { - \\ e: u8, - \\ e: u8, - \\ }; - \\ var a = .{@sizeOf(bitfield)}; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:4:9: error: duplicate struct field: 'e'", - }); - - ctx.testErrStage1("repeated invalid field access to generic function returning type crashes compiler. #2655", - \\pub fn A() type { - \\ return Q; - \\} - \\test "1" { - \\ _ = A().a; - \\ _ = A().a; - \\} - , &[_][]const u8{ - "tmp.zig:2:12: error: use of undeclared identifier 'Q'", - }); - - ctx.objErrStage1("bitCast to enum type", - \\export fn entry() void { - \\ const y = @bitCast(enum(u32) { a, b }, @as(u32, 3)); - \\ _ = y; - \\} - , &[_][]const u8{ - "tmp.zig:2:24: error: cannot cast a value of type 'y'", - }); - - ctx.objErrStage1("comparing against undefined produces undefined value", - \\export fn entry() void { - \\ if (2 == undefined) {} - \\} - , &[_][]const u8{ - "tmp.zig:2:11: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("comptime ptrcast of zero-sized type", - \\fn foo() void { - \\ const node: struct {} = undefined; - \\ const vla_ptr = @ptrCast([*]const u8, &node); - \\ _ = vla_ptr; - \\} - \\comptime { foo(); } - , &[_][]const u8{ - "tmp.zig:3:21: error: '*const struct:2:17' and '[*]const u8' do not have the same in-memory representation", - }); - - ctx.objErrStage1("slice sentinel mismatch", - \\fn foo() [:0]u8 { - \\ var x: []u8 = undefined; - \\ return x; - \\} - \\comptime { _ = foo; } - , &[_][]const u8{ - "tmp.zig:3:12: error: expected type '[:0]u8', found '[]u8'", - "tmp.zig:3:12: note: destination pointer requires a terminating '0' sentinel", - }); - - ctx.objErrStage1("cmpxchg with float", - \\export fn entry() void { - \\ var x: f32 = 0; - \\ _ = @cmpxchgWeak(f32, &x, 1, 2, .SeqCst, .SeqCst); - \\} - , &[_][]const u8{ - "tmp.zig:3:22: error: expected bool, integer, enum or pointer type, found 'f32'", - }); - - ctx.objErrStage1("atomicrmw with float op not .Xchg, .Add or .Sub", - \\export fn entry() void { - \\ var x: f32 = 0; - \\ _ = @atomicRmw(f32, &x, .And, 2, .SeqCst); - \\} - , &[_][]const u8{ - "tmp.zig:3:29: error: @atomicRmw with float only allowed with .Xchg, .Add and .Sub", - }); - - ctx.objErrStage1("intToPtr with misaligned address", - \\pub fn main() void { - \\ var y = @intToPtr([*]align(4) u8, 5); - \\ _ = y; - \\} - , &[_][]const u8{ - "tmp.zig:2:13: error: pointer type '[*]align(4) u8' requires aligned address", - }); - - ctx.objErrStage1("invalid float literal", - \\const std = @import("std"); - \\ - \\pub fn main() void { - \\ var bad_float :f32 = 0.0; - \\ bad_float = bad_float + .20; - \\ std.debug.assert(bad_float < 1.0); - \\} - , &[_][]const u8{ - "tmp.zig:5:29: error: expected expression, found '.'", - }); - - ctx.objErrStage1("invalid exponent in float literal - 1", - \\fn main() void { - \\ var bad: f128 = 0x1.0p1ab1; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:28: note: invalid byte: 'a'", - }); - - ctx.objErrStage1("invalid exponent in float literal - 2", - \\fn main() void { - \\ var bad: f128 = 0x1.0p50F; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:29: note: invalid byte: 'F'", - }); - - ctx.objErrStage1("invalid underscore placement in float literal - 1", - \\fn main() void { - \\ var bad: f128 = 0._0; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:23: note: invalid byte: '_'", - }); - - ctx.objErrStage1("invalid underscore placement in float literal - 2", - \\fn main() void { - \\ var bad: f128 = 0_.0; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:23: note: invalid byte: '.'", - }); - - ctx.objErrStage1("invalid underscore placement in float literal - 3", - \\fn main() void { - \\ var bad: f128 = 0.0_; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:25: note: invalid byte: ';'", - }); - - ctx.objErrStage1("invalid underscore placement in float literal - 4", - \\fn main() void { - \\ var bad: f128 = 1.0e_1; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:25: note: invalid byte: '_'", - }); - - ctx.objErrStage1("invalid underscore placement in float literal - 5", - \\fn main() void { - \\ var bad: f128 = 1.0e+_1; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:26: note: invalid byte: '_'", - }); - - ctx.objErrStage1("invalid underscore placement in float literal - 6", - \\fn main() void { - \\ var bad: f128 = 1.0e-_1; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:26: note: invalid byte: '_'", - }); - - ctx.objErrStage1("invalid underscore placement in float literal - 7", - \\fn main() void { - \\ var bad: f128 = 1.0e-1_; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:28: note: invalid byte: ';'", - }); - - ctx.objErrStage1("invalid underscore placement in float literal - 9", - \\fn main() void { - \\ var bad: f128 = 1__0.0e-1; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:23: note: invalid byte: '_'", - }); - - ctx.objErrStage1("invalid underscore placement in float literal - 10", - \\fn main() void { - \\ var bad: f128 = 1.0__0e-1; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:25: note: invalid byte: '_'", - }); - - ctx.objErrStage1("invalid underscore placement in float literal - 11", - \\fn main() void { - \\ var bad: f128 = 1.0e-1__0; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:28: note: invalid byte: '_'", - }); - - ctx.objErrStage1("invalid underscore placement in float literal - 12", - \\fn main() void { - \\ var bad: f128 = 0_x0.0; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:23: note: invalid byte: 'x'", - }); - - ctx.objErrStage1("invalid underscore placement in float literal - 13", - \\fn main() void { - \\ var bad: f128 = 0x_0.0; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:23: note: invalid byte: '_'", - }); - - ctx.objErrStage1("invalid underscore placement in float literal - 14", - \\fn main() void { - \\ var bad: f128 = 0x0.0_p1; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:27: note: invalid byte: 'p'", - }); - - ctx.objErrStage1("invalid underscore placement in int literal - 1", - \\fn main() void { - \\ var bad: u128 = 0010_; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:26: note: invalid byte: ';'", - }); - - ctx.objErrStage1("invalid underscore placement in int literal - 2", - \\fn main() void { - \\ var bad: u128 = 0b0010_; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:28: note: invalid byte: ';'", - }); - - ctx.objErrStage1("invalid underscore placement in int literal - 3", - \\fn main() void { - \\ var bad: u128 = 0o0010_; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:28: note: invalid byte: ';'", - }); - - ctx.objErrStage1("invalid underscore placement in int literal - 4", - \\fn main() void { - \\ var bad: u128 = 0x0010_; - \\ _ = bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:28: note: invalid byte: ';'", - }); - - ctx.objErrStage1("comptime struct field, no init value", - \\const Foo = struct { - \\ comptime b: i32, - \\}; - \\export fn entry() void { - \\ var f: Foo = undefined; - \\ _ = f; - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: comptime field without default initialization value", - }); - - ctx.objErrStage1("bad usage of @call", - \\export fn entry1() void { - \\ @call(.{}, foo, {}); - \\} - \\export fn entry2() void { - \\ comptime @call(.{ .modifier = .never_inline }, foo, .{}); - \\} - \\export fn entry3() void { - \\ comptime @call(.{ .modifier = .never_tail }, foo, .{}); - \\} - \\export fn entry4() void { - \\ @call(.{ .modifier = .never_inline }, bar, .{}); - \\} - \\export fn entry5(c: bool) void { - \\ var baz = if (c) baz1 else baz2; - \\ @call(.{ .modifier = .compile_time }, baz, .{}); - \\} - \\fn foo() void {} - \\fn bar() callconv(.Inline) void {} - \\fn baz1() void {} - \\fn baz2() void {} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected tuple or struct, found 'void'", - "tmp.zig:5:14: error: unable to perform 'never_inline' call at compile-time", - "tmp.zig:8:14: error: unable to perform 'never_tail' call at compile-time", - "tmp.zig:11:5: error: no-inline call of inline function", - "tmp.zig:15:5: error: the specified modifier requires a comptime-known function", - }); - - ctx.objErrStage1("exported async function", - \\export fn foo() callconv(.Async) void {} - , &[_][]const u8{ - "tmp.zig:1:1: error: exported function cannot be async", - }); - - ctx.exeErrStage1("main missing name", - \\pub fn (main) void {} - , &[_][]const u8{ - "tmp.zig:1:5: error: missing function name", - }); - { const case = ctx.obj("call with new stack on unsupported target", .{ .cpu_arch = .wasm32, @@ -1899,361 +181,6 @@ pub fn addCases(ctx: *TestContext) !void { "tmp.zig:12:31: note: destination array requires a terminating '0' sentinel", }); - ctx.objErrStage1("empty switch on an integer", - \\export fn entry() void { - \\ var x: u32 = 0; - \\ switch(x) {} - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: switch must handle all possibilities", - }); - - ctx.objErrStage1("incorrect return type", - \\ pub export fn entry() void{ - \\ _ = foo(); - \\ } - \\ const A = struct { - \\ a: u32, - \\ }; - \\ fn foo() A { - \\ return bar(); - \\ } - \\ const B = struct { - \\ a: u32, - \\ }; - \\ fn bar() B { - \\ unreachable; - \\ } - , &[_][]const u8{ - "tmp.zig:8:16: error: expected type 'A', found 'B'", - }); - - ctx.objErrStage1("regression test #2980: base type u32 is not type checked properly when assigning a value within a struct", - \\const Foo = struct { - \\ ptr: ?*usize, - \\ uval: u32, - \\}; - \\fn get_uval(x: u32) !u32 { - \\ _ = x; - \\ return error.NotFound; - \\} - \\export fn entry() void { - \\ const afoo = Foo{ - \\ .ptr = null, - \\ .uval = get_uval(42), - \\ }; - \\ _ = afoo; - \\} - , &[_][]const u8{ - "tmp.zig:12:25: error: cannot convert error union to payload type. consider using `try`, `catch`, or `if`. expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(get_uval)).Fn.return_type.?).ErrorUnion.error_set!u32'", - }); - - ctx.objErrStage1("assigning to struct or union fields that are not optionals with a function that returns an optional", - \\fn maybe(is: bool) ?u8 { - \\ if (is) return @as(u8, 10) else return null; - \\} - \\const U = union { - \\ Ye: u8, - \\}; - \\const S = struct { - \\ num: u8, - \\}; - \\export fn entry() void { - \\ var u = U{ .Ye = maybe(false) }; - \\ var s = S{ .num = maybe(false) }; - \\ _ = u; - \\ _ = s; - \\} - , &[_][]const u8{ - "tmp.zig:11:27: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type 'u8', found '?u8'", - }); - - ctx.objErrStage1("missing result type for phi node", - \\fn foo() !void { - \\ return anyerror.Foo; - \\} - \\export fn entry() void { - \\ foo() catch 0; - \\} - , &[_][]const u8{ - "tmp.zig:5:17: error: integer value 0 cannot be coerced to type 'void'", - }); - - ctx.objErrStage1("atomicrmw with enum op not .Xchg", - \\export fn entry() void { - \\ const E = enum(u8) { - \\ a, - \\ b, - \\ c, - \\ d, - \\ }; - \\ var x: E = .a; - \\ _ = @atomicRmw(E, &x, .Add, .b, .SeqCst); - \\} - , &[_][]const u8{ - "tmp.zig:9:27: error: @atomicRmw with enum only allowed with .Xchg", - }); - - ctx.objErrStage1("disallow coercion from non-null-terminated pointer to null-terminated pointer", - \\extern fn puts(s: [*:0]const u8) c_int; - \\pub fn main() void { - \\ const no_zero_array = [_]u8{'h', 'e', 'l', 'l', 'o'}; - \\ const no_zero_ptr: [*]const u8 = &no_zero_array; - \\ _ = puts(no_zero_ptr); - \\} - , &[_][]const u8{ - "tmp.zig:5:14: error: expected type '[*:0]const u8', found '[*]const u8'", - }); - - ctx.objErrStage1("atomic orderings of atomicStore Acquire or AcqRel", - \\export fn entry() void { - \\ var x: u32 = 0; - \\ @atomicStore(u32, &x, 1, .Acquire); - \\} - , &[_][]const u8{ - "tmp.zig:3:30: error: @atomicStore atomic ordering must not be Acquire or AcqRel", - }); - - ctx.objErrStage1("missing const in slice with nested array type", - \\const Geo3DTex2D = struct { vertices: [][2]f32 }; - \\pub fn getGeo3DTex2D() Geo3DTex2D { - \\ return Geo3DTex2D{ - \\ .vertices = [_][2]f32{ - \\ [_]f32{ -0.5, -0.5}, - \\ }, - \\ }; - \\} - \\export fn entry() void { - \\ var geo_data = getGeo3DTex2D(); - \\ _ = geo_data; - \\} - , &[_][]const u8{ - "tmp.zig:4:30: error: array literal requires address-of operator (&) to coerce to slice type '[][2]f32'", - }); - - ctx.objErrStage1("slicing of global undefined pointer", - \\var buf: *[1]u8 = undefined; - \\export fn entry() void { - \\ _ = buf[0..1]; - \\} - , &[_][]const u8{ - "tmp.zig:3:12: error: non-zero length slice of undefined pointer", - }); - - ctx.objErrStage1("using invalid types in function call raises an error", - \\const MenuEffect = enum {}; - \\fn func(effect: MenuEffect) void { _ = effect; } - \\export fn entry() void { - \\ func(MenuEffect.ThisDoesNotExist); - \\} - , &[_][]const u8{ - "tmp.zig:1:20: error: enum declarations must have at least one tag", - }); - - ctx.objErrStage1("store vector pointer with unknown runtime index", - \\export fn entry() void { - \\ var v: @import("std").meta.Vector(4, i32) = [_]i32{ 1, 5, 3, undefined }; - \\ - \\ var i: u32 = 0; - \\ storev(&v[i], 42); - \\} - \\ - \\fn storev(ptr: anytype, val: i32) void { - \\ ptr.* = val; - \\} - , &[_][]const u8{ - "tmp.zig:9:8: error: unable to determine vector element index of type '*align(16:0:4:?) i32", - }); - - ctx.objErrStage1("load vector pointer with unknown runtime index", - \\export fn entry() void { - \\ var v: @import("std").meta.Vector(4, i32) = [_]i32{ 1, 5, 3, undefined }; - \\ - \\ var i: u32 = 0; - \\ var x = loadv(&v[i]); - \\ _ = x; - \\} - \\ - \\fn loadv(ptr: anytype) i32 { - \\ return ptr.*; - \\} - , &[_][]const u8{ - "tmp.zig:10:12: error: unable to determine vector element index of type '*align(16:0:4:?) i32", - }); - - ctx.objErrStage1("using an unknown len ptr type instead of array", - \\const resolutions = [*][*]const u8{ - \\ "[320 240 ]", - \\ null, - \\}; - \\comptime { - \\ _ = resolutions; - \\} - , &[_][]const u8{ - "tmp.zig:1:21: error: expected array type or [_], found '[*][*]const u8'", - }); - - ctx.objErrStage1("comparison with error union and error value", - \\export fn entry() void { - \\ var number_or_error: anyerror!i32 = error.SomethingAwful; - \\ _ = number_or_error == error.SomethingAwful; - \\} - , &[_][]const u8{ - "tmp.zig:3:25: error: operator not allowed for type 'anyerror!i32'", - }); - - ctx.objErrStage1("switch with overlapping case ranges", - \\export fn entry() void { - \\ var q: u8 = 0; - \\ switch (q) { - \\ 1...2 => {}, - \\ 0...255 => {}, - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:5:9: error: duplicate switch value", - }); - - ctx.objErrStage1("invalid optional type in extern struct", - \\const stroo = extern struct { - \\ moo: ?[*c]u8, - \\}; - \\export fn testf(fluff: *stroo) void { _ = fluff; } - , &[_][]const u8{ - "tmp.zig:2:5: error: extern structs cannot contain fields of type '?[*c]u8'", - }); - - ctx.objErrStage1("attempt to negate a non-integer, non-float or non-vector type", - \\fn foo() anyerror!u32 { - \\ return 1; - \\} - \\ - \\export fn entry() void { - \\ const x = -foo(); - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:6:15: error: negation of type 'anyerror!u32'", - }); - - ctx.objErrStage1("attempt to create 17 bit float type", - \\const builtin = @import("std").builtin; - \\comptime { - \\ _ = @Type(.{ .Float = .{ .bits = 17 } }); - \\} - , &[_][]const u8{ - "tmp.zig:3:16: error: 17-bit float unsupported", - }); - - ctx.objErrStage1("wrong type for @Type", - \\export fn entry() void { - \\ _ = @Type(0); - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: expected type 'std.builtin.Type', found 'comptime_int'", - }); - - ctx.objErrStage1("@Type with non-constant expression", - \\const builtin = @import("std").builtin; - \\var globalTypeInfo : builtin.Type = undefined; - \\export fn entry() void { - \\ _ = @Type(globalTypeInfo); - \\} - , &[_][]const u8{ - "tmp.zig:4:15: error: unable to evaluate constant expression", - }); - - ctx.objErrStage1("wrong type for argument tuple to @asyncCall", - \\export fn entry1() void { - \\ var frame: @Frame(foo) = undefined; - \\ @asyncCall(&frame, {}, foo, {}); - \\} - \\ - \\fn foo() i32 { - \\ return 0; - \\} - , &[_][]const u8{ - "tmp.zig:3:33: error: expected tuple or struct, found 'void'", - }); - - ctx.objErrStage1("wrong type for result ptr to @asyncCall", - \\export fn entry() void { - \\ _ = async amain(); - \\} - \\fn amain() i32 { - \\ var frame: @Frame(foo) = undefined; - \\ return await @asyncCall(&frame, false, foo, .{}); - \\} - \\fn foo() i32 { - \\ return 1234; - \\} - , &[_][]const u8{ - "tmp.zig:6:37: error: expected type '*i32', found 'bool'", - }); - - ctx.objErrStage1("shift amount has to be an integer type", - \\export fn entry() void { - \\ const x = 1 << &@as(u8, 10); - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: shift amount has to be an integer type, but found '*const u8'", - }); - - ctx.objErrStage1("bit shifting only works on integer types", - \\export fn entry() void { - \\ const x = &@as(u8, 1) << 10; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:16: error: bit shifting operation expected integer type, found '*const u8'", - }); - - ctx.objErrStage1("struct depends on itself via optional field", - \\const LhsExpr = struct { - \\ rhsExpr: ?AstObject, - \\}; - \\const AstObject = union { - \\ lhsExpr: LhsExpr, - \\}; - \\export fn entry() void { - \\ const lhsExpr = LhsExpr{ .rhsExpr = null }; - \\ const obj = AstObject{ .lhsExpr = lhsExpr }; - \\ _ = obj; - \\} - , &[_][]const u8{ - "tmp.zig:1:17: error: struct 'LhsExpr' depends on itself", - "tmp.zig:5:5: note: while checking this field", - "tmp.zig:2:5: note: while checking this field", - }); - - ctx.objErrStage1("alignment of enum field specified", - \\const Number = enum { - \\ a, - \\ b align(i32), - \\}; - \\export fn entry1() void { - \\ var x: Number = undefined; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:3:7: error: expected ',' after field", - }); - - ctx.objErrStage1("bad alignment type", - \\export fn entry1() void { - \\ var x: []align(true) i32 = undefined; - \\ _ = x; - \\} - \\export fn entry2() void { - \\ var x: *align(@as(f64, 12.34)) i32 = undefined; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:20: error: expected type 'u29', found 'bool'", - "tmp.zig:6:19: error: fractional component prevents float value 12.340000 from being casted to type 'u29'", - }); - { const case = ctx.obj("variable in inline assembly template cannot be found", .{ .cpu_arch = .x86_64, @@ -2274,55 +201,6 @@ pub fn addCases(ctx: *TestContext) !void { }); } - ctx.objErrStage1("indirect recursion of async functions detected", - \\var frame: ?anyframe = null; - \\ - \\export fn a() void { - \\ _ = async rangeSum(10); - \\ while (frame) |f| resume f; - \\} - \\ - \\fn rangeSum(x: i32) i32 { - \\ suspend { - \\ frame = @frame(); - \\ } - \\ frame = null; - \\ - \\ if (x == 0) return 0; - \\ var child = rangeSumIndirect(x - 1); - \\ return child + 1; - \\} - \\ - \\fn rangeSumIndirect(x: i32) i32 { - \\ suspend { - \\ frame = @frame(); - \\ } - \\ frame = null; - \\ - \\ if (x == 0) return 0; - \\ var child = rangeSum(x - 1); - \\ return child + 1; - \\} - , &[_][]const u8{ - "tmp.zig:8:1: error: '@Frame(rangeSum)' depends on itself", - "tmp.zig:15:33: note: when analyzing type '@Frame(rangeSum)' here", - "tmp.zig:26:25: note: when analyzing type '@Frame(rangeSumIndirect)' here", - }); - - ctx.objErrStage1("non-async function pointer eventually is inferred to become async", - \\export fn a() void { - \\ var non_async_fn: fn () void = undefined; - \\ non_async_fn = func; - \\} - \\fn func() void { - \\ suspend {} - \\} - , &[_][]const u8{ - "tmp.zig:5:1: error: 'func' cannot be async", - "tmp.zig:3:20: note: required to be non-async here", - "tmp.zig:6:5: note: suspends here", - }); - { const case = ctx.obj("bad alignment in @asyncCall", .{ .cpu_arch = .aarch64, @@ -2342,633 +220,6 @@ pub fn addCases(ctx: *TestContext) !void { }); } - ctx.objErrStage1("atomic orderings of fence Acquire or stricter", - \\export fn entry() void { - \\ @fence(.Monotonic); - \\} - , &[_][]const u8{ - "tmp.zig:2:12: error: atomic ordering must be Acquire or stricter", - }); - - ctx.objErrStage1("bad alignment in implicit cast from array pointer to slice", - \\export fn a() void { - \\ var x: [10]u8 = undefined; - \\ var y: []align(16) u8 = &x; - \\ _ = y; - \\} - , &[_][]const u8{ - "tmp.zig:3:30: error: expected type '[]align(16) u8', found '*[10]u8'", - }); - - ctx.objErrStage1("result location incompatibility mismatching handle_is_ptr (generic call)", - \\export fn entry() void { - \\ var damn = Container{ - \\ .not_optional = getOptional(i32), - \\ }; - \\ _ = damn; - \\} - \\pub fn getOptional(comptime T: type) ?T { - \\ return 0; - \\} - \\pub const Container = struct { - \\ not_optional: i32, - \\}; - , &[_][]const u8{ - "tmp.zig:3:36: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type 'i32', found '?i32'", - }); - - ctx.objErrStage1("result location incompatibility mismatching handle_is_ptr", - \\export fn entry() void { - \\ var damn = Container{ - \\ .not_optional = getOptional(), - \\ }; - \\ _ = damn; - \\} - \\pub fn getOptional() ?i32 { - \\ return 0; - \\} - \\pub const Container = struct { - \\ not_optional: i32, - \\}; - , &[_][]const u8{ - "tmp.zig:3:36: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type 'i32', found '?i32'", - }); - - ctx.objErrStage1("const frame cast to anyframe", - \\export fn a() void { - \\ const f = async func(); - \\ resume f; - \\} - \\export fn b() void { - \\ const f = async func(); - \\ var x: anyframe = &f; - \\ _ = x; - \\} - \\fn func() void { - \\ suspend {} - \\} - , &[_][]const u8{ - "tmp.zig:3:12: error: expected type 'anyframe', found '*const @Frame(func)'", - "tmp.zig:7:24: error: expected type 'anyframe', found '*const @Frame(func)'", - }); - - ctx.objErrStage1("prevent bad implicit casting of anyframe types", - \\export fn a() void { - \\ var x: anyframe = undefined; - \\ var y: anyframe->i32 = x; - \\ _ = y; - \\} - \\export fn b() void { - \\ var x: i32 = undefined; - \\ var y: anyframe->i32 = x; - \\ _ = y; - \\} - \\export fn c() void { - \\ var x: @Frame(func) = undefined; - \\ var y: anyframe->i32 = &x; - \\ _ = y; - \\} - \\fn func() void {} - , &[_][]const u8{ - "tmp.zig:3:28: error: expected type 'anyframe->i32', found 'anyframe'", - "tmp.zig:8:28: error: expected type 'anyframe->i32', found 'i32'", - "tmp.zig:13:29: error: expected type 'anyframe->i32', found '*@Frame(func)'", - }); - - ctx.objErrStage1("wrong frame type used for async call", - \\export fn entry() void { - \\ var frame: @Frame(foo) = undefined; - \\ frame = async bar(); - \\} - \\fn foo() void { - \\ suspend {} - \\} - \\fn bar() void { - \\ suspend {} - \\} - , &[_][]const u8{ - "tmp.zig:3:13: error: expected type '*@Frame(bar)', found '*@Frame(foo)'", - }); - - ctx.objErrStage1("@Frame() of generic function", - \\export fn entry() void { - \\ var frame: @Frame(func) = undefined; - \\ _ = frame; - \\} - \\fn func(comptime T: type) void { - \\ var x: T = undefined; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:16: error: @Frame() of generic function", - }); - - ctx.objErrStage1("@frame() causes function to be async", - \\export fn entry() void { - \\ func(); - \\} - \\fn func() void { - \\ _ = @frame(); - \\} - , &[_][]const u8{ - "tmp.zig:1:1: error: function with calling convention 'C' cannot be async", - "tmp.zig:5:9: note: @frame() causes function to be async", - }); - - ctx.objErrStage1("invalid suspend in exported function", - \\export fn entry() void { - \\ var frame = async func(); - \\ var result = await frame; - \\ _ = result; - \\} - \\fn func() void { - \\ suspend {} - \\} - , &[_][]const u8{ - "tmp.zig:1:1: error: function with calling convention 'C' cannot be async", - "tmp.zig:3:18: note: await here is a suspend point", - }); - - ctx.objErrStage1("async function indirectly depends on its own frame", - \\export fn entry() void { - \\ _ = async amain(); - \\} - \\fn amain() callconv(.Async) void { - \\ other(); - \\} - \\fn other() void { - \\ var x: [@sizeOf(@Frame(amain))]u8 = undefined; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:4:1: error: unable to determine async function frame of 'amain'", - "tmp.zig:5:10: note: analysis of function 'other' depends on the frame", - }); - - ctx.objErrStage1("async function depends on its own frame", - \\export fn entry() void { - \\ _ = async amain(); - \\} - \\fn amain() callconv(.Async) void { - \\ var x: [@sizeOf(@Frame(amain))]u8 = undefined; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:4:1: error: cannot resolve '@Frame(amain)': function not fully analyzed yet", - }); - - ctx.objErrStage1("non async function pointer passed to @asyncCall", - \\export fn entry() void { - \\ var ptr = afunc; - \\ var bytes: [100]u8 align(16) = undefined; - \\ _ = @asyncCall(&bytes, {}, ptr, .{}); - \\} - \\fn afunc() void { } - , &[_][]const u8{ - "tmp.zig:4:32: error: expected async function, found 'fn() void'", - }); - - ctx.objErrStage1("runtime-known async function called", - \\export fn entry() void { - \\ _ = async amain(); - \\} - \\fn amain() void { - \\ var ptr = afunc; - \\ _ = ptr(); - \\} - \\fn afunc() callconv(.Async) void {} - , &[_][]const u8{ - "tmp.zig:6:12: error: function is not comptime-known; @asyncCall required", - }); - - ctx.objErrStage1("runtime-known function called with async keyword", - \\export fn entry() void { - \\ var ptr = afunc; - \\ _ = async ptr(); - \\} - \\ - \\fn afunc() callconv(.Async) void { } - , &[_][]const u8{ - "tmp.zig:3:15: error: function is not comptime-known; @asyncCall required", - }); - - ctx.objErrStage1("function with ccc indirectly calling async function", - \\export fn entry() void { - \\ foo(); - \\} - \\fn foo() void { - \\ bar(); - \\} - \\fn bar() void { - \\ suspend {} - \\} - , &[_][]const u8{ - "tmp.zig:1:1: error: function with calling convention 'C' cannot be async", - "tmp.zig:2:8: note: async function call here", - "tmp.zig:5:8: note: async function call here", - "tmp.zig:8:5: note: suspends here", - }); - - ctx.objErrStage1("capture group on switch prong with incompatible payload types", - \\const Union = union(enum) { - \\ A: usize, - \\ B: isize, - \\}; - \\comptime { - \\ var u = Union{ .A = 8 }; - \\ switch (u) { - \\ .A, .B => |e| { - \\ _ = e; - \\ unreachable; - \\ }, - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:8:20: error: capture group with incompatible types", - "tmp.zig:8:9: note: type 'usize' here", - "tmp.zig:8:13: note: type 'isize' here", - }); - - ctx.objErrStage1("wrong type to @hasField", - \\export fn entry() bool { - \\ return @hasField(i32, "hi"); - \\} - , &[_][]const u8{ - "tmp.zig:2:22: error: type 'i32' does not support @hasField", - }); - - ctx.objErrStage1("slice passed as array init type with elems", - \\export fn entry() void { - \\ const x = []u8{1, 2}; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: array literal requires address-of operator (&) to coerce to slice type '[]u8'", - }); - - ctx.objErrStage1("slice passed as array init type", - \\export fn entry() void { - \\ const x = []u8{}; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: array literal requires address-of operator (&) to coerce to slice type '[]u8'", - }); - - ctx.objErrStage1("inferred array size invalid here", - \\export fn entry() void { - \\ const x = [_]u8; - \\ _ = x; - \\} - \\export fn entry2() void { - \\ const S = struct { a: *const [_]u8 }; - \\ var a = .{ S{} }; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:2:16: error: unable to infer array size", - "tmp.zig:6:35: error: unable to infer array size", - }); - - ctx.objErrStage1("initializing array with struct syntax", - \\export fn entry() void { - \\ const x = [_]u8{ .y = 2 }; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: initializing array with struct syntax", - }); - - ctx.objErrStage1("compile error in struct init expression", - \\const Foo = struct { - \\ a: i32 = crap, - \\ b: i32, - \\}; - \\export fn entry() void { - \\ var x = Foo{ - \\ .b = 5, - \\ }; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:14: error: use of undeclared identifier 'crap'", - }); - - ctx.objErrStage1("undefined as field type is rejected", - \\const Foo = struct { - \\ a: undefined, - \\}; - \\export fn entry1() void { - \\ const foo: Foo = undefined; - \\ _ = foo; - \\} - , &[_][]const u8{ - "tmp.zig:2:8: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("@hasDecl with non-container", - \\export fn entry() void { - \\ _ = @hasDecl(i32, "hi"); - \\} - , &[_][]const u8{ - "tmp.zig:2:18: error: expected struct, enum, or union; found 'i32'", - }); - - ctx.objErrStage1("field access of slices", - \\export fn entry() void { - \\ var slice: []i32 = undefined; - \\ const info = @TypeOf(slice).unknown; - \\ _ = info; - \\} - , &[_][]const u8{ - "tmp.zig:3:32: error: type 'type' does not support field access", - }); - - ctx.objErrStage1("peer cast then implicit cast const pointer to mutable C pointer", - \\export fn func() void { - \\ var strValue: [*c]u8 = undefined; - \\ strValue = strValue orelse ""; - \\} - , &[_][]const u8{ - "tmp.zig:3:32: error: expected type '[*c]u8', found '*const [0:0]u8'", - "tmp.zig:3:32: note: cast discards const qualifier", - }); - - ctx.objErrStage1("overflow in enum value allocation", - \\const Moo = enum(u8) { - \\ Last = 255, - \\ Over, - \\}; - \\pub fn main() void { - \\ var y = Moo.Last; - \\ _ = y; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: enumeration value 256 too large for type 'u8'", - }); - - ctx.objErrStage1("attempt to cast enum literal to error", - \\export fn entry() void { - \\ switch (error.Hi) { - \\ .Hi => {}, - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: expected type 'error{Hi}', found '@Type(.EnumLiteral)'", - }); - - ctx.objErrStage1("@sizeOf bad type", - \\export fn entry() usize { - \\ return @sizeOf(@TypeOf(null)); - \\} - , &[_][]const u8{ - "tmp.zig:2:20: error: no size available for type '@Type(.Null)'", - }); - - ctx.objErrStage1("generic function where return type is self-referenced", - \\fn Foo(comptime T: type) Foo(T) { - \\ return struct{ x: T }; - \\} - \\export fn entry() void { - \\ const t = Foo(u32) { - \\ .x = 1 - \\ }; - \\ _ = t; - \\} - , &[_][]const u8{ - "tmp.zig:1:29: error: evaluation exceeded 1000 backwards branches", - }); - - ctx.objErrStage1("@ptrToInt 0 to non optional pointer", - \\export fn entry() void { - \\ var b = @intToPtr(*i32, 0); - \\ _ = b; - \\} - , &[_][]const u8{ - "tmp.zig:2:13: error: pointer type '*i32' does not allow address zero", - }); - - ctx.objErrStage1("cast enum literal to enum but it doesn't match", - \\const Foo = enum { - \\ a, - \\ b, - \\}; - \\export fn entry() void { - \\ const x: Foo = .c; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:6:20: error: enum 'Foo' has no field named 'c'", - "tmp.zig:1:13: note: 'Foo' declared here", - }); - - ctx.objErrStage1("discarding error value", - \\export fn entry() void { - \\ _ = foo(); - \\} - \\fn foo() !void { - \\ return error.OutOfMemory; - \\} - , &[_][]const u8{ - "tmp.zig:2:12: error: error is discarded. consider using `try`, `catch`, or `if`", - }); - - ctx.objErrStage1("volatile on global assembly", - \\comptime { - \\ asm volatile (""); - \\} - , &[_][]const u8{ - "tmp.zig:2:9: error: volatile is meaningless on global assembly", - }); - - ctx.objErrStage1("invalid multiple dereferences", - \\export fn a() void { - \\ var box = Box{ .field = 0 }; - \\ box.*.field = 1; - \\} - \\export fn b() void { - \\ var box = Box{ .field = 0 }; - \\ var boxPtr = &box; - \\ boxPtr.*.*.field = 1; - \\} - \\pub const Box = struct { - \\ field: i32, - \\}; - , &[_][]const u8{ - "tmp.zig:3:8: error: attempt to dereference non-pointer type 'Box'", - "tmp.zig:8:13: error: attempt to dereference non-pointer type 'Box'", - }); - - ctx.objErrStage1("usingnamespace with wrong type", - \\usingnamespace void; - , &[_][]const u8{ - "tmp.zig:1:1: error: expected struct, enum, or union; found 'void'", - }); - - ctx.objErrStage1("ignored expression in while continuation", - \\export fn a() void { - \\ while (true) : (bad()) {} - \\} - \\export fn b() void { - \\ var x: anyerror!i32 = 1234; - \\ while (x) |_| : (bad()) {} else |_| {} - \\} - \\export fn c() void { - \\ var x: ?i32 = 1234; - \\ while (x) |_| : (bad()) {} - \\} - \\fn bad() anyerror!void { - \\ return error.Bad; - \\} - , &[_][]const u8{ - "tmp.zig:2:24: error: error is ignored. consider using `try`, `catch`, or `if`", - "tmp.zig:6:25: error: error is ignored. consider using `try`, `catch`, or `if`", - "tmp.zig:10:25: error: error is ignored. consider using `try`, `catch`, or `if`", - }); - - ctx.objErrStage1("empty while loop body", - \\export fn a() void { - \\ while(true); - \\} - , &[_][]const u8{ - "tmp.zig:2:16: error: expected block or assignment, found ';'", - }); - - ctx.objErrStage1("empty for loop body", - \\export fn a() void { - \\ for(undefined) |x|; - \\} - , &[_][]const u8{ - "tmp.zig:2:23: error: expected block or assignment, found ';'", - }); - - ctx.objErrStage1("empty if body", - \\export fn a() void { - \\ if(true); - \\} - , &[_][]const u8{ - "tmp.zig:2:13: error: expected block or assignment, found ';'", - }); - - ctx.objErrStage1("import outside package path", - \\comptime{ - \\ _ = @import("../a.zig"); - \\} - , &[_][]const u8{ - "tmp.zig:2:9: error: import of file outside package path: '../a.zig'", - }); - - ctx.objErrStage1("bogus compile var", - \\const x = @import("builtin").bogus; - \\export fn entry() usize { return @sizeOf(@TypeOf(x)); } - , &[_][]const u8{ - "tmp.zig:1:29: error: container 'builtin' has no member called 'bogus'", - }); - - ctx.objErrStage1("wrong panic signature, runtime function", - \\test "" {} - \\ - \\pub fn panic() void {} - \\ - , &[_][]const u8{ - "error: expected type 'fn([]const u8, ?*std.builtin.StackTrace) noreturn', found 'fn() void'", - }); - - ctx.objErrStage1("wrong panic signature, generic function", - \\pub fn panic(comptime msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { - \\ _ = msg; _ = error_return_trace; - \\ while (true) {} - \\} - \\const builtin = @import("std").builtin; - , &[_][]const u8{ - "error: expected type 'fn([]const u8, ?*std.builtin.StackTrace) noreturn', found 'fn([]const u8,anytype) anytype'", - "note: only one of the functions is generic", - }); - - ctx.objErrStage1("direct struct loop", - \\const A = struct { a : A, }; - \\export fn entry() usize { return @sizeOf(A); } - , &[_][]const u8{ - "tmp.zig:1:11: error: struct 'A' depends on itself", - }); - - ctx.objErrStage1("indirect struct loop", - \\const A = struct { b : B, }; - \\const B = struct { c : C, }; - \\const C = struct { a : A, }; - \\export fn entry() usize { return @sizeOf(A); } - , &[_][]const u8{ - "tmp.zig:1:11: error: struct 'A' depends on itself", - }); - - ctx.objErrStage1("instantiating an undefined value for an invalid struct that contains itself", - \\const Foo = struct { - \\ x: Foo, - \\}; - \\ - \\var foo: Foo = undefined; - \\ - \\export fn entry() usize { - \\ return @sizeOf(@TypeOf(foo.x)); - \\} - , &[_][]const u8{ - "tmp.zig:1:13: error: struct 'Foo' depends on itself", - }); - - ctx.objErrStage1("enum field value references enum", - \\pub const Foo = enum(c_int) { - \\ A = Foo.B, - \\ C = D, - \\}; - \\export fn entry() void { - \\ var s: Foo = Foo.E; - \\ _ = s; - \\} - \\const D = 1; - , &[_][]const u8{ - "tmp.zig:1:17: error: enum 'Foo' depends on itself", - }); - - ctx.objErrStage1("top level decl dependency loop", - \\const a : @TypeOf(b) = 0; - \\const b : @TypeOf(a) = 0; - \\export fn entry() void { - \\ const c = a + b; - \\ _ = c; - \\} - , &[_][]const u8{ - "tmp.zig:2:19: error: dependency loop detected", - }); - - ctx.testErrStage1("not an enum type", - \\export fn entry() void { - \\ var self: Error = undefined; - \\ switch (self) { - \\ InvalidToken => |x| return x.token, - \\ ExpectedVarDeclOrFn => |x| return x.token, - \\ } - \\} - \\const Error = union(enum) { - \\ A: InvalidToken, - \\ B: ExpectedVarDeclOrFn, - \\}; - \\const InvalidToken = struct {}; - \\const ExpectedVarDeclOrFn = struct {}; - , &[_][]const u8{ - "tmp.zig:4:9: error: expected type '@typeInfo(Error).Union.tag_type.?', found 'type'", - }); - - ctx.testErrStage1("binary OR operator on error sets", - \\pub const A = error.A; - \\pub const AB = A | error.B; - \\export fn entry() void { - \\ var x: AB = undefined; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:18: error: invalid operands to binary expression: 'error{A}' and 'error{B}'", - }); - if (builtin.os.tag == .linux) { ctx.testErrStage1("implicit dependency on libc", \\extern "c" fn exit(u8) void; @@ -2990,1048 +241,6 @@ pub fn addCases(ctx: *TestContext) !void { }); } - ctx.testErrStage1("comptime vector overflow shows the index", - \\comptime { - \\ var a: @import("std").meta.Vector(4, u8) = [_]u8{ 1, 2, 255, 4 }; - \\ var b: @import("std").meta.Vector(4, u8) = [_]u8{ 5, 6, 1, 8 }; - \\ var x = a + b; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:4:15: error: operation caused overflow", - "tmp.zig:4:15: note: when computing vector element at index 2", - }); - - ctx.testErrStage1("packed struct with fields of not allowed types", - \\const A = packed struct { - \\ x: anyerror, - \\}; - \\const B = packed struct { - \\ x: [2]u24, - \\}; - \\const C = packed struct { - \\ x: [1]anyerror, - \\}; - \\const D = packed struct { - \\ x: [1]S, - \\}; - \\const E = packed struct { - \\ x: [1]U, - \\}; - \\const F = packed struct { - \\ x: ?anyerror, - \\}; - \\const G = packed struct { - \\ x: Enum, - \\}; - \\export fn entry1() void { - \\ var a: A = undefined; - \\ _ = a; - \\} - \\export fn entry2() void { - \\ var b: B = undefined; - \\ _ = b; - \\} - \\export fn entry3() void { - \\ var r: C = undefined; - \\ _ = r; - \\} - \\export fn entry4() void { - \\ var d: D = undefined; - \\ _ = d; - \\} - \\export fn entry5() void { - \\ var e: E = undefined; - \\ _ = e; - \\} - \\export fn entry6() void { - \\ var f: F = undefined; - \\ _ = f; - \\} - \\export fn entry7() void { - \\ var g: G = undefined; - \\ _ = g; - \\} - \\const S = struct { - \\ x: i32, - \\}; - \\const U = struct { - \\ A: i32, - \\ B: u32, - \\}; - \\const Enum = enum { - \\ A, - \\ B, - \\}; - , &[_][]const u8{ - "tmp.zig:2:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation", - "tmp.zig:5:5: error: array of 'u24' not allowed in packed struct due to padding bits (must be padded from 48 to 64 bits)", - "tmp.zig:8:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation", - "tmp.zig:11:5: error: non-packed, non-extern struct 'S' not allowed in packed struct; no guaranteed in-memory representation", - "tmp.zig:14:5: error: non-packed, non-extern struct 'U' not allowed in packed struct; no guaranteed in-memory representation", - "tmp.zig:17:5: error: type '?anyerror' not allowed in packed struct; no guaranteed in-memory representation", - "tmp.zig:20:5: error: type 'Enum' not allowed in packed struct; no guaranteed in-memory representation", - "tmp.zig:57:14: note: enum declaration does not specify an integer tag type", - }); - - ctx.objErrStage1("deduplicate undeclared identifier", - \\export fn a() void { - \\ x += 1; - \\} - \\export fn b() void { - \\ x += 1; - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: use of undeclared identifier 'x'", - }); - - ctx.objErrStage1("export generic function", - \\export fn foo(num: anytype) i32 { - \\ _ = num; - \\ return 0; - \\} - , &[_][]const u8{ - "tmp.zig:1:15: error: parameter of type 'anytype' not allowed in function with calling convention 'C'", - }); - - ctx.objErrStage1("C pointer to anyopaque", - \\export fn a() void { - \\ var x: *anyopaque = undefined; - \\ var y: [*c]anyopaque = x; - \\ _ = y; - \\} - , &[_][]const u8{ - "tmp.zig:3:16: error: C pointers cannot point to opaque types", - }); - - ctx.objErrStage1("directly embedding opaque type in struct and union", - \\const O = opaque {}; - \\const Foo = struct { - \\ o: O, - \\}; - \\const Bar = union { - \\ One: i32, - \\ Two: O, - \\}; - \\export fn a() void { - \\ var foo: Foo = undefined; - \\ _ = foo; - \\} - \\export fn b() void { - \\ var bar: Bar = undefined; - \\ _ = bar; - \\} - \\export fn c() void { - \\ var baz: *opaque {} = undefined; - \\ const qux = .{baz.*}; - \\ _ = qux; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: opaque types have unknown size and therefore cannot be directly embedded in structs", - "tmp.zig:7:5: error: opaque types have unknown size and therefore cannot be directly embedded in unions", - "tmp.zig:19:22: error: opaque types have unknown size and therefore cannot be directly embedded in structs", - }); - - ctx.objErrStage1("implicit cast between C pointer and Zig pointer - bad const/align/child", - \\export fn a() void { - \\ var x: [*c]u8 = undefined; - \\ var y: *align(4) u8 = x; - \\ _ = y; - \\} - \\export fn b() void { - \\ var x: [*c]const u8 = undefined; - \\ var y: *u8 = x; - \\ _ = y; - \\} - \\export fn c() void { - \\ var x: [*c]u8 = undefined; - \\ var y: *u32 = x; - \\ _ = y; - \\} - \\export fn d() void { - \\ var y: *align(1) u32 = undefined; - \\ var x: [*c]u32 = y; - \\ _ = x; - \\} - \\export fn e() void { - \\ var y: *const u8 = undefined; - \\ var x: [*c]u8 = y; - \\ _ = x; - \\} - \\export fn f() void { - \\ var y: *u8 = undefined; - \\ var x: [*c]u32 = y; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:3:27: error: cast increases pointer alignment", - "tmp.zig:8:18: error: cast discards const qualifier", - "tmp.zig:13:19: error: expected type '*u32', found '[*c]u8'", - "tmp.zig:13:19: note: pointer type child 'u8' cannot cast into pointer type child 'u32'", - "tmp.zig:18:22: error: cast increases pointer alignment", - "tmp.zig:23:21: error: cast discards const qualifier", - "tmp.zig:28:22: error: expected type '[*c]u32', found '*u8'", - }); - - ctx.objErrStage1("implicit casting null c pointer to zig pointer", - \\comptime { - \\ var c_ptr: [*c]u8 = 0; - \\ var zig_ptr: *u8 = c_ptr; - \\ _ = zig_ptr; - \\} - , &[_][]const u8{ - "tmp.zig:3:24: error: null pointer casted to type '*u8'", - }); - - ctx.objErrStage1("implicit casting undefined c pointer to zig pointer", - \\comptime { - \\ var c_ptr: [*c]u8 = undefined; - \\ var zig_ptr: *u8 = c_ptr; - \\ _ = zig_ptr; - \\} - , &[_][]const u8{ - "tmp.zig:3:24: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("implicit casting C pointers which would mess up null semantics", - \\export fn entry() void { - \\ var slice: []const u8 = "aoeu"; - \\ const opt_many_ptr: [*]const u8 = slice.ptr; - \\ var ptr_opt_many_ptr = &opt_many_ptr; - \\ var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr; - \\ ptr_opt_many_ptr = c_ptr; - \\} - \\export fn entry2() void { - \\ var buf: [4]u8 = "aoeu".*; - \\ var slice: []u8 = &buf; - \\ var opt_many_ptr: [*]u8 = slice.ptr; - \\ var ptr_opt_many_ptr = &opt_many_ptr; - \\ var c_ptr: [*c][*c]const u8 = ptr_opt_many_ptr; - \\ _ = c_ptr; - \\} - , &[_][]const u8{ - "tmp.zig:6:24: error: expected type '*const [*]const u8', found '[*c]const [*c]const u8'", - "tmp.zig:6:24: note: pointer type child '[*c]const u8' cannot cast into pointer type child '[*]const u8'", - "tmp.zig:6:24: note: '[*c]const u8' could have null values which are illegal in type '[*]const u8'", - "tmp.zig:13:35: error: expected type '[*c][*c]const u8', found '*[*]u8'", - "tmp.zig:13:35: note: pointer type child '[*]u8' cannot cast into pointer type child '[*c]const u8'", - "tmp.zig:13:35: note: mutable '[*c]const u8' allows illegal null values stored to type '[*]u8'", - }); - - ctx.objErrStage1("implicit casting too big integers to C pointers", - \\export fn a() void { - \\ var ptr: [*c]u8 = (1 << 64) + 1; - \\ _ = ptr; - \\} - \\export fn b() void { - \\ var x: u65 = 0x1234; - \\ var ptr: [*c]u8 = x; - \\ _ = ptr; - \\} - , &[_][]const u8{ - "tmp.zig:2:33: error: integer value 18446744073709551617 cannot be coerced to type 'usize'", - "tmp.zig:7:23: error: integer type 'u65' too big for implicit @intToPtr to type '[*c]u8'", - }); - - ctx.objErrStage1("C pointer pointing to non C ABI compatible type or has align attr", - \\const Foo = struct {}; - \\export fn a() void { - \\ const T = [*c]Foo; - \\ var t: T = undefined; - \\ _ = t; - \\} - , &[_][]const u8{ - "tmp.zig:3:19: error: C pointers cannot point to non-C-ABI-compatible type 'Foo'", - }); - - ctx.objErrStage1("compile log statement warning deduplication in generic fn", - \\export fn entry() void { - \\ inner(1); - \\ inner(2); - \\} - \\fn inner(comptime n: usize) void { - \\ comptime var i = 0; - \\ inline while (i < n) : (i += 1) { @compileLog("!@#$"); } - \\} - , &[_][]const u8{ - "tmp.zig:7:39: error: found compile log statement", - }); - - ctx.objErrStage1("assign to invalid dereference", - \\export fn entry() void { - \\ 'a'.* = 1; - \\} - , &[_][]const u8{ - "tmp.zig:2:8: error: attempt to dereference non-pointer type 'comptime_int'", - }); - - ctx.objErrStage1("take slice of invalid dereference", - \\export fn entry() void { - \\ const x = 'a'.*[0..]; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:18: error: attempt to dereference non-pointer type 'comptime_int'", - }); - - ctx.objErrStage1("@truncate undefined value", - \\export fn entry() void { - \\ var z = @truncate(u8, @as(u16, undefined)); - \\ _ = z; - \\} - , &[_][]const u8{ - "tmp.zig:2:27: error: use of undefined value here causes undefined behavior", - }); - - ctx.testErrStage1("return invalid type from test", - \\test "example" { return 1; } - , &[_][]const u8{ - "tmp.zig:1:25: error: expected type 'void', found 'comptime_int'", - }); - - ctx.objErrStage1("threadlocal qualifier on const", - \\threadlocal const x: i32 = 1234; - \\export fn entry() i32 { - \\ return x; - \\} - , &[_][]const u8{ - "tmp.zig:1:1: error: threadlocal variable cannot be constant", - }); - - ctx.objErrStage1("@bitCast same size but bit count mismatch", - \\export fn entry(byte: u8) void { - \\ var oops = @bitCast(u7, byte); - \\ _ = oops; - \\} - , &[_][]const u8{ - "tmp.zig:2:25: error: destination type 'u7' has 7 bits but source type 'u8' has 8 bits", - }); - - ctx.objErrStage1("@bitCast with different sizes inside an expression", - \\export fn entry() void { - \\ var foo = (@bitCast(u8, @as(f32, 1.0)) == 0xf); - \\ _ = foo; - \\} - , &[_][]const u8{ - "tmp.zig:2:25: error: destination type 'u8' has size 1 but source type 'f32' has size 4", - }); - - ctx.objErrStage1("attempted `&&`", - \\export fn entry(a: bool, b: bool) i32 { - \\ if (a && b) { - \\ return 1234; - \\ } - \\ return 5678; - \\} - , &[_][]const u8{ - "tmp.zig:2:11: error: ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND", - }); - - ctx.objErrStage1("attempted `||` on boolean values", - \\export fn entry(a: bool, b: bool) i32 { - \\ if (a || b) { - \\ return 1234; - \\ } - \\ return 5678; - \\} - , &[_][]const u8{ - "tmp.zig:2:9: error: expected error set type, found 'bool'", - "tmp.zig:2:11: note: `||` merges error sets; `or` performs boolean OR", - }); - - ctx.objErrStage1("compile log a pointer to an opaque value", - \\export fn entry() void { - \\ @compileLog(@ptrCast(*const anyopaque, &entry)); - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: found compile log statement", - }); - - ctx.objErrStage1("duplicate boolean switch value", - \\comptime { - \\ const x = switch (true) { - \\ true => false, - \\ false => true, - \\ true => false, - \\ }; - \\ _ = x; - \\} - \\comptime { - \\ const x = switch (true) { - \\ false => true, - \\ true => false, - \\ false => true, - \\ }; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:5:9: error: duplicate switch value", - "tmp.zig:13:9: error: duplicate switch value", - }); - - ctx.objErrStage1("missing boolean switch value", - \\comptime { - \\ const x = switch (true) { - \\ true => false, - \\ }; - \\ _ = x; - \\} - \\comptime { - \\ const x = switch (true) { - \\ false => true, - \\ }; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: switch must handle all possibilities", - "tmp.zig:8:15: error: switch must handle all possibilities", - }); - - ctx.objErrStage1("reading past end of pointer casted array", - \\comptime { - \\ const array: [4]u8 = "aoeu".*; - \\ const sub_array = array[1..]; - \\ const int_ptr = @ptrCast(*const u24, sub_array); - \\ const deref = int_ptr.*; - \\ _ = deref; - \\} - , &[_][]const u8{ - "tmp.zig:5:26: error: attempt to read 4 bytes from [4]u8 at index 1 which is 3 bytes", - }); - - ctx.objErrStage1("error note for function parameter incompatibility", - \\fn do_the_thing(func: fn (arg: i32) void) void { _ = func; } - \\fn bar(arg: bool) void { _ = arg; } - \\export fn entry() void { - \\ do_the_thing(bar); - \\} - , &[_][]const u8{ - "tmp.zig:4:18: error: expected type 'fn(i32) void', found 'fn(bool) void", - "tmp.zig:4:18: note: parameter 0: 'bool' cannot cast into 'i32'", - }); - ctx.objErrStage1("cast negative value to unsigned integer", - \\comptime { - \\ const value: i32 = -1; - \\ const unsigned = @intCast(u32, value); - \\ _ = unsigned; - \\} - \\export fn entry1() void { - \\ const value: i32 = -1; - \\ const unsigned: u32 = value; - \\ _ = unsigned; - \\} - , &[_][]const u8{ - "tmp.zig:3:22: error: attempt to cast negative value to unsigned integer", - "tmp.zig:8:27: error: cannot cast negative value -1 to unsigned integer type 'u32'", - }); - - ctx.objErrStage1("integer cast truncates bits", - \\export fn entry1() void { - \\ const spartan_count: u16 = 300; - \\ const byte = @intCast(u8, spartan_count); - \\ _ = byte; - \\} - \\export fn entry2() void { - \\ const spartan_count: u16 = 300; - \\ const byte: u8 = spartan_count; - \\ _ = byte; - \\} - \\export fn entry3() void { - \\ var spartan_count: u16 = 300; - \\ var byte: u8 = spartan_count; - \\ _ = byte; - \\} - \\export fn entry4() void { - \\ var signed: i8 = -1; - \\ var unsigned: u64 = signed; - \\ _ = unsigned; - \\} - , &[_][]const u8{ - "tmp.zig:3:18: error: cast from 'u16' to 'u8' truncates bits", - "tmp.zig:8:22: error: integer value 300 cannot be coerced to type 'u8'", - "tmp.zig:13:20: error: expected type 'u8', found 'u16'", - "tmp.zig:13:20: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values", - "tmp.zig:18:25: error: expected type 'u64', found 'i8'", - "tmp.zig:18:25: note: unsigned 64-bit int cannot represent all possible signed 8-bit values", - }); - - ctx.objErrStage1("comptime implicit cast f64 to f32", - \\export fn entry() void { - \\ const x: f64 = 16777217; - \\ const y: f32 = x; - \\ _ = y; - \\} - , &[_][]const u8{ - "tmp.zig:3:20: error: cast of value 16777217.000000 to type 'f32' loses information", - }); - - ctx.objErrStage1("implicit cast from f64 to f32", - \\var x: f64 = 1.0; - \\var y: f32 = x; - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(y)); } - , &[_][]const u8{ - "tmp.zig:2:14: error: expected type 'f32', found 'f64'", - }); - - ctx.objErrStage1("exceeded maximum bit width of integer", - \\export fn entry1() void { - \\ const T = u65536; - \\ _ = T; - \\} - \\export fn entry2() void { - \\ var x: i65536 = 1; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: primitive integer type 'u65536' exceeds maximum bit width of 65535", - "tmp.zig:6:12: error: primitive integer type 'i65536' exceeds maximum bit width of 65535", - }); - - ctx.objErrStage1("compile error when evaluating return type of inferred error set", - \\const Car = struct { - \\ foo: *SymbolThatDoesNotExist, - \\ pub fn init() !Car {} - \\}; - \\export fn entry() void { - \\ const car = Car.init(); - \\ _ = car; - \\} - , &[_][]const u8{ - "tmp.zig:2:11: error: use of undeclared identifier 'SymbolThatDoesNotExist'", - }); - - ctx.objErrStage1("don't implicit cast double pointer to *anyopaque", - \\export fn entry() void { - \\ var a: u32 = 1; - \\ var ptr: *align(@alignOf(u32)) anyopaque = &a; - \\ var b: *u32 = @ptrCast(*u32, ptr); - \\ var ptr2: *anyopaque = &b; - \\ _ = ptr2; - \\} - , &[_][]const u8{ - "tmp.zig:5:29: error: expected type '*anyopaque', found '**u32'", - }); - - ctx.objErrStage1("runtime index into comptime type slice", - \\const Struct = struct { - \\ a: u32, - \\}; - \\fn getIndex() usize { - \\ return 2; - \\} - \\export fn entry() void { - \\ const index = getIndex(); - \\ const field = @typeInfo(Struct).Struct.fields[index]; - \\ _ = field; - \\} - , &[_][]const u8{ - "tmp.zig:9:51: error: values of type 'std.builtin.Type.StructField' must be comptime known, but index value is runtime known", - }); - - ctx.objErrStage1("compile log statement inside function which must be comptime evaluated", - \\fn Foo(comptime T: type) type { - \\ @compileLog(@typeName(T)); - \\ return T; - \\} - \\export fn entry() void { - \\ _ = Foo(i32); - \\ _ = @typeName(Foo(i32)); - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: found compile log statement", - }); - - ctx.objErrStage1("comptime slice of an undefined slice", - \\comptime { - \\ var a: []u8 = undefined; - \\ var b = a[0..10]; - \\ _ = b; - \\} - , &[_][]const u8{ - "tmp.zig:3:14: error: slice of undefined", - }); - - ctx.objErrStage1("implicit cast const array to mutable slice", - \\export fn entry() void { - \\ const buffer: [1]u8 = [_]u8{8}; - \\ const sliceA: []u8 = &buffer; - \\ _ = sliceA; - \\} - , &[_][]const u8{ - "tmp.zig:3:27: error: cannot cast pointer to array literal to slice type '[]u8'", - "tmp.zig:3:27: note: cast discards const qualifier", - }); - - ctx.objErrStage1("deref slice and get len field", - \\export fn entry() void { - \\ var a: []u8 = undefined; - \\ _ = a.*.len; - \\} - , &[_][]const u8{ - "tmp.zig:3:10: error: attempt to dereference non-pointer type '[]u8'", - }); - - ctx.objErrStage1("@ptrCast a 0 bit type to a non- 0 bit type", - \\export fn entry() bool { - \\ var x: u0 = 0; - \\ const p = @ptrCast(?*u0, &x); - \\ return p == null; - \\} - , &[_][]const u8{ - "tmp.zig:3:15: error: '*u0' and '?*u0' do not have the same in-memory representation", - "tmp.zig:3:31: note: '*u0' has no in-memory bits", - "tmp.zig:3:24: note: '?*u0' has in-memory bits", - }); - - ctx.objErrStage1("comparing a non-optional pointer against null", - \\export fn entry() void { - \\ var x: i32 = 1; - \\ _ = &x == null; - \\} - , &[_][]const u8{ - "tmp.zig:3:12: error: comparison of '*i32' with null", - }); - - ctx.objErrStage1("non error sets used in merge error sets operator", - \\export fn foo() void { - \\ const Errors = u8 || u16; - \\ _ = Errors; - \\} - \\export fn bar() void { - \\ const Errors = error{} || u16; - \\ _ = Errors; - \\} - , &[_][]const u8{ - "tmp.zig:2:20: error: expected error set type, found type 'u8'", - "tmp.zig:2:23: note: `||` merges error sets; `or` performs boolean OR", - "tmp.zig:6:31: error: expected error set type, found type 'u16'", - "tmp.zig:6:28: note: `||` merges error sets; `or` performs boolean OR", - }); - - ctx.objErrStage1("variable initialization compile error then referenced", - \\fn Undeclared() type { - \\ return T; - \\} - \\fn Gen() type { - \\ const X = Undeclared(); - \\ return struct { - \\ x: X, - \\ }; - \\} - \\export fn entry() void { - \\ const S = Gen(); - \\ _ = S; - \\} - , &[_][]const u8{ - "tmp.zig:2:12: error: use of undeclared identifier 'T'", - }); - - ctx.objErrStage1("refer to the type of a generic function", - \\export fn entry() void { - \\ const Func = fn (type) void; - \\ const f: Func = undefined; - \\ f(i32); - \\} - , &[_][]const u8{ - "tmp.zig:4:5: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("accessing runtime parameter from outer function", - \\fn outer(y: u32) fn (u32) u32 { - \\ const st = struct { - \\ fn get(z: u32) u32 { - \\ return z + y; - \\ } - \\ }; - \\ return st.get; - \\} - \\export fn entry() void { - \\ var func = outer(10); - \\ var x = func(3); - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:4:24: error: 'y' not accessible from inner function", - "tmp.zig:3:28: note: crossed function definition here", - "tmp.zig:1:10: note: declared here", - }); - - ctx.objErrStage1("non int passed to @intToFloat", - \\export fn entry() void { - \\ const x = @intToFloat(f32, 1.1); - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:32: error: expected int type, found 'comptime_float'", - }); - - ctx.objErrStage1("non float passed to @floatToInt", - \\export fn entry() void { - \\ const x = @floatToInt(i32, @as(i32, 54)); - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:32: error: expected float type, found 'i32'", - }); - - ctx.objErrStage1("out of range comptime_int passed to @floatToInt", - \\export fn entry() void { - \\ const x = @floatToInt(i8, 200); - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:31: error: integer value 200 cannot be coerced to type 'i8'", - }); - - ctx.objErrStage1("load too many bytes from comptime reinterpreted pointer", - \\export fn entry() void { - \\ const float: f32 = 5.99999999999994648725e-01; - \\ const float_ptr = &float; - \\ const int_ptr = @ptrCast(*const i64, float_ptr); - \\ const int_val = int_ptr.*; - \\ _ = int_val; - \\} - , &[_][]const u8{ - "tmp.zig:5:28: error: attempt to read 8 bytes from pointer to f32 which is 4 bytes", - }); - - ctx.objErrStage1("invalid type used in array type", - \\const Item = struct { - \\ field: SomeNonexistentType, - \\}; - \\var items: [100]Item = undefined; - \\export fn entry() void { - \\ const a = items[0]; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:2:12: error: use of undeclared identifier 'SomeNonexistentType'", - }); - - ctx.objErrStage1("comptime continue inside runtime catch", - \\export fn entry() void { - \\ const ints = [_]u8{ 1, 2 }; - \\ inline for (ints) |_| { - \\ bad() catch continue; - \\ } - \\} - \\fn bad() !void { - \\ return error.Bad; - \\} - , &[_][]const u8{ - "tmp.zig:4:21: error: comptime control flow inside runtime block", - "tmp.zig:4:15: note: runtime block created here", - }); - - ctx.objErrStage1("comptime continue inside runtime switch", - \\export fn entry() void { - \\ var p: i32 = undefined; - \\ comptime var q = true; - \\ inline while (q) { - \\ switch (p) { - \\ 11 => continue, - \\ else => {}, - \\ } - \\ q = false; - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:6:19: error: comptime control flow inside runtime block", - "tmp.zig:5:9: note: runtime block created here", - }); - - ctx.objErrStage1("comptime continue inside runtime while error", - \\export fn entry() void { - \\ var p: anyerror!usize = undefined; - \\ comptime var q = true; - \\ outer: inline while (q) { - \\ while (p) |_| { - \\ continue :outer; - \\ } else |_| {} - \\ q = false; - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:6:13: error: comptime control flow inside runtime block", - "tmp.zig:5:9: note: runtime block created here", - }); - - ctx.objErrStage1("comptime continue inside runtime while optional", - \\export fn entry() void { - \\ var p: ?usize = undefined; - \\ comptime var q = true; - \\ outer: inline while (q) { - \\ while (p) |_| continue :outer; - \\ q = false; - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:5:23: error: comptime control flow inside runtime block", - "tmp.zig:5:9: note: runtime block created here", - }); - - ctx.objErrStage1("comptime continue inside runtime while bool", - \\export fn entry() void { - \\ var p: usize = undefined; - \\ comptime var q = true; - \\ outer: inline while (q) { - \\ while (p == 11) continue :outer; - \\ q = false; - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:5:25: error: comptime control flow inside runtime block", - "tmp.zig:5:9: note: runtime block created here", - }); - - ctx.objErrStage1("comptime continue inside runtime if error", - \\export fn entry() void { - \\ var p: anyerror!i32 = undefined; - \\ comptime var q = true; - \\ inline while (q) { - \\ if (p) |_| continue else |_| {} - \\ q = false; - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:5:20: error: comptime control flow inside runtime block", - "tmp.zig:5:9: note: runtime block created here", - }); - - ctx.objErrStage1("comptime continue inside runtime if optional", - \\export fn entry() void { - \\ var p: ?i32 = undefined; - \\ comptime var q = true; - \\ inline while (q) { - \\ if (p) |_| continue; - \\ q = false; - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:5:20: error: comptime control flow inside runtime block", - "tmp.zig:5:9: note: runtime block created here", - }); - - ctx.objErrStage1("comptime continue inside runtime if bool", - \\export fn entry() void { - \\ var p: usize = undefined; - \\ comptime var q = true; - \\ inline while (q) { - \\ if (p == 11) continue; - \\ q = false; - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:5:22: error: comptime control flow inside runtime block", - "tmp.zig:5:9: note: runtime block created here", - }); - - ctx.objErrStage1("switch with invalid expression parameter", - \\export fn entry() void { - \\ Test(i32); - \\} - \\fn Test(comptime T: type) void { - \\ const x = switch (T) { - \\ []u8 => |x| x, - \\ i32 => |x| x, - \\ else => unreachable, - \\ }; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:7:17: error: switch on type 'type' provides no expression parameter", - }); - - ctx.objErrStage1("function prototype with no body", - \\fn foo() void; - \\export fn entry() void { - \\ foo(); - \\} - , &[_][]const u8{ - "tmp.zig:1:1: error: non-extern function has no body", - }); - - ctx.objErrStage1("@frame() called outside of function definition", - \\var handle_undef: anyframe = undefined; - \\var handle_dummy: anyframe = @frame(); - \\export fn entry() bool { - \\ return handle_undef == handle_dummy; - \\} - , &[_][]const u8{ - "tmp.zig:2:30: error: @frame() called outside of function definition", - }); - - ctx.objErrStage1("`_` is not a declarable symbol", - \\export fn f1() usize { - \\ var _: usize = 2; - \\ return _; - \\} - , &[_][]const u8{ - "tmp.zig:2:9: error: '_' used as an identifier without @\"_\" syntax", - }); - - ctx.objErrStage1("`_` should not be usable inside for", - \\export fn returns() void { - \\ for ([_]void{}) |_, i| { - \\ for ([_]void{}) |_, j| { - \\ return _; - \\ } - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:4:20: error: '_' used as an identifier without @\"_\" syntax", - }); - - ctx.objErrStage1("`_` should not be usable inside while", - \\export fn returns() void { - \\ while (optionalReturn()) |_| { - \\ while (optionalReturn()) |_| { - \\ return _; - \\ } - \\ } - \\} - \\fn optionalReturn() ?u32 { - \\ return 1; - \\} - , &[_][]const u8{ - "tmp.zig:4:20: error: '_' used as an identifier without @\"_\" syntax", - }); - - ctx.objErrStage1("`_` should not be usable inside while else", - \\export fn returns() void { - \\ while (optionalReturnError()) |_| { - \\ while (optionalReturnError()) |_| { - \\ return; - \\ } else |_| { - \\ if (_ == error.optionalReturnError) return; - \\ } - \\ } - \\} - \\fn optionalReturnError() !?u32 { - \\ return error.optionalReturnError; - \\} - , &[_][]const u8{ - "tmp.zig:6:17: error: '_' used as an identifier without @\"_\" syntax", - }); - - ctx.objErrStage1("while loop body expression ignored", - \\fn returns() usize { - \\ return 2; - \\} - \\export fn f1() void { - \\ while (true) returns(); - \\} - \\export fn f2() void { - \\ var x: ?i32 = null; - \\ while (x) |_| returns(); - \\} - \\export fn f3() void { - \\ var x: anyerror!i32 = error.Bad; - \\ while (x) |_| returns() else |_| unreachable; - \\} - , &[_][]const u8{ - "tmp.zig:5:25: error: expression value is ignored", - "tmp.zig:9:26: error: expression value is ignored", - "tmp.zig:13:26: error: expression value is ignored", - }); - - ctx.objErrStage1("missing parameter name of generic function", - \\fn dump(anytype) void {} - \\export fn entry() void { - \\ var a: u8 = 9; - \\ dump(a); - \\} - , &[_][]const u8{ - "tmp.zig:1:9: error: missing parameter name", - }); - - ctx.objErrStage1("non-inline for loop on a type that requires comptime", - \\const Foo = struct { - \\ name: []const u8, - \\ T: type, - \\}; - \\export fn entry() void { - \\ const xx: [2]Foo = undefined; - \\ for (xx) |f| { _ = f;} - \\} - , &[_][]const u8{ - "tmp.zig:7:5: error: values of type 'Foo' must be comptime known, but index value is runtime known", - }); - - ctx.objErrStage1("generic fn as parameter without comptime keyword", - \\fn f(_: fn (anytype) void) void {} - \\fn g(_: anytype) void {} - \\export fn entry() void { - \\ f(g); - \\} - , &[_][]const u8{ - "tmp.zig:1:9: error: parameter of type 'fn(anytype) anytype' must be declared comptime", - }); - - ctx.objErrStage1("optional pointer to void in extern struct", - \\const Foo = extern struct { - \\ x: ?*const void, - \\}; - \\const Bar = extern struct { - \\ foo: Foo, - \\ y: i32, - \\}; - \\export fn entry(bar: *Bar) void {_ = bar;} - , &[_][]const u8{ - "tmp.zig:2:5: error: extern structs cannot contain fields of type '?*const void'", - }); - - ctx.objErrStage1("use of comptime-known undefined function value", - \\const Cmd = struct { - \\ exec: fn () void, - \\}; - \\export fn entry() void { - \\ const command = Cmd{ .exec = undefined }; - \\ command.exec(); - \\} - , &[_][]const u8{ - "tmp.zig:6:12: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("use of comptime-known undefined function value", - \\const Cmd = struct { - \\ exec: fn () void, - \\}; - \\export fn entry() void { - \\ const command = Cmd{ .exec = undefined }; - \\ command.exec(); - \\} - , &[_][]const u8{ - "tmp.zig:6:12: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("bad @alignCast at comptime", - \\comptime { - \\ const ptr = @intToPtr(*align(1) i32, 0x1); - \\ const aligned = @alignCast(4, ptr); - \\ _ = aligned; - \\} - , &[_][]const u8{ - "tmp.zig:3:35: error: pointer address 0x1 is not aligned to 4 bytes", - }); - - ctx.objErrStage1("@ptrToInt on *void", - \\export fn entry() bool { - \\ return @ptrToInt(&{}) == @ptrToInt(&{}); - \\} - , &[_][]const u8{ - "tmp.zig:2:23: error: pointer to size 0 type has no address", - }); - - ctx.objErrStage1("@popCount - non-integer", - \\export fn entry(x: f32) u32 { - \\ return @popCount(f32, x); - \\} - , &[_][]const u8{ - "tmp.zig:2:22: error: expected integer type, found 'f32'", - }); - { const case = ctx.obj("wrong same named struct", .{}); case.backend = .stage1; @@ -4066,2558 +275,6 @@ pub fn addCases(ctx: *TestContext) !void { }); } - ctx.objErrStage1("@floatToInt comptime safety", - \\comptime { - \\ _ = @floatToInt(i8, @as(f32, -129.1)); - \\} - \\comptime { - \\ _ = @floatToInt(u8, @as(f32, -1.1)); - \\} - \\comptime { - \\ _ = @floatToInt(u8, @as(f32, 256.1)); - \\} - , &[_][]const u8{ - "tmp.zig:2:9: error: integer value '-129' cannot be stored in type 'i8'", - "tmp.zig:5:9: error: integer value '-1' cannot be stored in type 'u8'", - "tmp.zig:8:9: error: integer value '256' cannot be stored in type 'u8'", - }); - - ctx.objErrStage1("use anyopaque as return type of fn ptr", - \\export fn entry() void { - \\ const a: fn () anyopaque = undefined; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:2:20: error: return type cannot be opaque", - }); - - ctx.objErrStage1("use implicit casts to assign null to non-nullable pointer", - \\export fn entry() void { - \\ var x: i32 = 1234; - \\ var p: *i32 = &x; - \\ var pp: *?*i32 = &p; - \\ pp.* = null; - \\ var y = p.*; - \\ _ = y; - \\} - , &[_][]const u8{ - "tmp.zig:4:23: error: expected type '*?*i32', found '**i32'", - }); - - ctx.objErrStage1("attempted implicit cast from T to [*]const T", - \\export fn entry() void { - \\ const x: [*]const bool = true; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:30: error: expected type '[*]const bool', found 'bool'", - }); - - ctx.objErrStage1("dereference unknown length pointer", - \\export fn entry(x: [*]i32) i32 { - \\ return x.*; - \\} - , &[_][]const u8{ - "tmp.zig:2:13: error: index syntax required for unknown-length pointer type '[*]i32'", - }); - - ctx.objErrStage1("field access of unknown length pointer", - \\const Foo = extern struct { - \\ a: i32, - \\}; - \\ - \\export fn entry(foo: [*]Foo) void { - \\ foo.a += 1; - \\} - , &[_][]const u8{ - "tmp.zig:6:8: error: type '[*]Foo' does not support field access", - }); - - ctx.objErrStage1("unknown length pointer to opaque", - \\export const T = [*]opaque {}; - , &[_][]const u8{ - "tmp.zig:1:21: error: unknown-length pointer to opaque", - }); - - ctx.objErrStage1("error when evaluating return type", - \\const Foo = struct { - \\ map: @as(i32, i32), - \\ - \\ fn init() Foo { - \\ return undefined; - \\ } - \\}; - \\export fn entry() void { - \\ var rule_set = try Foo.init(); - \\ _ = rule_set; - \\} - , &[_][]const u8{ - "tmp.zig:2:19: error: expected type 'i32', found 'type'", - }); - - ctx.objErrStage1("slicing single-item pointer", - \\export fn entry(ptr: *i32) void { - \\ const slice = ptr[0..2]; - \\ _ = slice; - \\} - , &[_][]const u8{ - "tmp.zig:2:22: error: slice of single-item pointer", - }); - - ctx.objErrStage1("indexing single-item pointer", - \\export fn entry(ptr: *i32) i32 { - \\ return ptr[1]; - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: index of single-item pointer", - }); - - ctx.objErrStage1("nested error set mismatch", - \\const NextError = error{NextError}; - \\const OtherError = error{OutOfMemory}; - \\ - \\export fn entry() void { - \\ const a: ?NextError!i32 = foo(); - \\ _ = a; - \\} - \\ - \\fn foo() ?OtherError!i32 { - \\ return null; - \\} - , &[_][]const u8{ - "tmp.zig:5:34: error: expected type '?NextError!i32', found '?OtherError!i32'", - "tmp.zig:5:34: note: optional type child 'OtherError!i32' cannot cast into optional type child 'NextError!i32'", - "tmp.zig:5:34: note: error set 'OtherError' cannot cast into error set 'NextError'", - "tmp.zig:2:26: note: 'error.OutOfMemory' not a member of destination error set", - }); - - ctx.objErrStage1("invalid deref on switch target", - \\comptime { - \\ var tile = Tile.Empty; - \\ switch (tile.*) { - \\ Tile.Empty => {}, - \\ Tile.Filled => {}, - \\ } - \\} - \\const Tile = enum { - \\ Empty, - \\ Filled, - \\}; - , &[_][]const u8{ - "tmp.zig:3:17: error: attempt to dereference non-pointer type 'Tile'", - }); - - ctx.objErrStage1("invalid field access in comptime", - \\comptime { var x = doesnt_exist.whatever; _ = x; } - , &[_][]const u8{ - "tmp.zig:1:20: error: use of undeclared identifier 'doesnt_exist'", - }); - - ctx.objErrStage1("suspend inside suspend block", - \\export fn entry() void { - \\ _ = async foo(); - \\} - \\fn foo() void { - \\ suspend { - \\ suspend { - \\ } - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:6:9: error: cannot suspend inside suspend block", - "tmp.zig:5:5: note: other suspend block here", - }); - - ctx.objErrStage1("assign inline fn to non-comptime var", - \\export fn entry() void { - \\ var a = b; - \\ _ = a; - \\} - \\fn b() callconv(.Inline) void { } - , &[_][]const u8{ - "tmp.zig:2:5: error: functions marked inline must be stored in const or comptime var", - "tmp.zig:5:1: note: declared here", - }); - - ctx.objErrStage1("wrong type passed to @panic", - \\export fn entry() void { - \\ var e = error.Foo; - \\ @panic(e); - \\} - , &[_][]const u8{ - "tmp.zig:3:12: error: expected type '[]const u8', found 'error{Foo}'", - }); - - ctx.objErrStage1("@tagName used on union with no associated enum tag", - \\const FloatInt = extern union { - \\ Float: f32, - \\ Int: i32, - \\}; - \\export fn entry() void { - \\ var fi = FloatInt{.Float = 123.45}; - \\ var tagName = @tagName(fi); - \\ _ = tagName; - \\} - , &[_][]const u8{ - "tmp.zig:7:19: error: union has no associated enum", - "tmp.zig:1:18: note: declared here", - }); - - ctx.objErrStage1("returning error from void async function", - \\export fn entry() void { - \\ _ = async amain(); - \\} - \\fn amain() callconv(.Async) void { - \\ return error.ShouldBeCompileError; - \\} - , &[_][]const u8{ - "tmp.zig:5:17: error: expected type 'void', found 'error{ShouldBeCompileError}'", - }); - - ctx.objErrStage1("@ptrCast discards const qualifier", - \\export fn entry() void { - \\ const x: i32 = 1234; - \\ const y = @ptrCast(*i32, &x); - \\ _ = y; - \\} - , &[_][]const u8{ - "tmp.zig:3:15: error: cast discards const qualifier", - }); - - ctx.objErrStage1("comptime slice of undefined pointer non-zero len", - \\export fn entry() void { - \\ const slice = @as([*]i32, undefined)[0..1]; - \\ _ = slice; - \\} - , &[_][]const u8{ - "tmp.zig:2:41: error: non-zero length slice of undefined pointer", - }); - - ctx.objErrStage1("type checking function pointers", - \\fn a(b: fn (*const u8) void) void { - \\ b('a'); - \\} - \\fn c(d: u8) void {_ = d;} - \\export fn entry() void { - \\ a(c); - \\} - , &[_][]const u8{ - "tmp.zig:6:7: error: expected type 'fn(*const u8) void', found 'fn(u8) void'", - }); - - ctx.objErrStage1("no else prong on switch on global error set", - \\export fn entry() void { - \\ foo(error.A); - \\} - \\fn foo(a: anyerror) void { - \\ switch (a) { - \\ error.A => {}, - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:5:5: error: else prong required when switching on type 'anyerror'", - }); - - ctx.objErrStage1("error not handled in switch", - \\export fn entry() void { - \\ foo(452) catch |err| switch (err) { - \\ error.Foo => {}, - \\ }; - \\} - \\fn foo(x: i32) !void { - \\ switch (x) { - \\ 0 ... 10 => return error.Foo, - \\ 11 ... 20 => return error.Bar, - \\ 21 ... 30 => return error.Baz, - \\ else => {}, - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:2:26: error: error.Baz not handled in switch", - "tmp.zig:2:26: error: error.Bar not handled in switch", - }); - - ctx.objErrStage1("duplicate error in switch", - \\export fn entry() void { - \\ foo(452) catch |err| switch (err) { - \\ error.Foo => {}, - \\ error.Bar => {}, - \\ error.Foo => {}, - \\ else => {}, - \\ }; - \\} - \\fn foo(x: i32) !void { - \\ switch (x) { - \\ 0 ... 10 => return error.Foo, - \\ 11 ... 20 => return error.Bar, - \\ else => {}, - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:5:14: error: duplicate switch value: '@typeInfo(@typeInfo(@TypeOf(foo)).Fn.return_type.?).ErrorUnion.error_set.Foo'", - "tmp.zig:3:14: note: other value here", - }); - - ctx.objErrStage1("invalid cast from integral type to enum", - \\const E = enum(usize) { One, Two }; - \\ - \\export fn entry() void { - \\ foo(1); - \\} - \\ - \\fn foo(x: usize) void { - \\ switch (x) { - \\ E.One => {}, - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:9:10: error: expected type 'usize', found 'E'", - }); - - ctx.objErrStage1("range operator in switch used on error set", - \\export fn entry() void { - \\ try foo(452) catch |err| switch (err) { - \\ error.A ... error.B => {}, - \\ else => {}, - \\ }; - \\} - \\fn foo(x: i32) !void { - \\ switch (x) { - \\ 0 ... 10 => return error.Foo, - \\ 11 ... 20 => return error.Bar, - \\ else => {}, - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:3:17: error: operator not allowed for errors", - }); - - ctx.objErrStage1("inferring error set of function pointer", - \\comptime { - \\ const z: ?fn()!void = null; - \\} - , &[_][]const u8{ - "tmp.zig:2:19: error: function prototype may not have inferred error set", - }); - - ctx.objErrStage1("access non-existent member of error set", - \\const Foo = error{A}; - \\comptime { - \\ const z = Foo.Bar; - \\ _ = z; - \\} - , &[_][]const u8{ - "tmp.zig:3:18: error: no error named 'Bar' in 'Foo'", - }); - - ctx.objErrStage1("error union operator with non error set LHS", - \\comptime { - \\ const z = i32!i32; - \\ var x: z = undefined; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: expected error set type, found type 'i32'", - }); - - ctx.objErrStage1("error equality but sets have no common members", - \\const Set1 = error{A, C}; - \\const Set2 = error{B, D}; - \\export fn entry() void { - \\ foo(Set1.A); - \\} - \\fn foo(x: Set1) void { - \\ if (x == Set2.B) { - \\ - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:7:11: error: error sets 'Set1' and 'Set2' have no common errors", - }); - - ctx.objErrStage1("only equality binary operator allowed for error sets", - \\comptime { - \\ const z = error.A > error.B; - \\ _ = z; - \\} - , &[_][]const u8{ - "tmp.zig:2:23: error: operator not allowed for errors", - }); - - ctx.objErrStage1("explicit error set cast known at comptime violates error sets", - \\const Set1 = error {A, B}; - \\const Set2 = error {A, C}; - \\comptime { - \\ var x = Set1.B; - \\ var y = @errSetCast(Set2, x); - \\ _ = y; - \\} - , &[_][]const u8{ - "tmp.zig:5:13: error: error.B not a member of error set 'Set2'", - }); - - ctx.objErrStage1("cast error union of global error set to error union of smaller error set", - \\const SmallErrorSet = error{A}; - \\export fn entry() void { - \\ var x: SmallErrorSet!i32 = foo(); - \\ _ = x; - \\} - \\fn foo() anyerror!i32 { - \\ return error.B; - \\} - , &[_][]const u8{ - "tmp.zig:3:35: error: expected type 'SmallErrorSet!i32', found 'anyerror!i32'", - "tmp.zig:3:35: note: error set 'anyerror' cannot cast into error set 'SmallErrorSet'", - "tmp.zig:3:35: note: cannot cast global error set into smaller set", - }); - - ctx.objErrStage1("cast global error set to error set", - \\const SmallErrorSet = error{A}; - \\export fn entry() void { - \\ var x: SmallErrorSet = foo(); - \\ _ = x; - \\} - \\fn foo() anyerror { - \\ return error.B; - \\} - , &[_][]const u8{ - "tmp.zig:3:31: error: expected type 'SmallErrorSet', found 'anyerror'", - "tmp.zig:3:31: note: cannot cast global error set into smaller set", - }); - ctx.objErrStage1("recursive inferred error set", - \\export fn entry() void { - \\ foo() catch unreachable; - \\} - \\fn foo() !void { - \\ try foo(); - \\} - , &[_][]const u8{ - "tmp.zig:5:5: error: cannot resolve inferred error set '@typeInfo(@typeInfo(@TypeOf(foo)).Fn.return_type.?).ErrorUnion.error_set': function 'foo' not fully analyzed yet", - }); - - ctx.objErrStage1("implicit cast of error set not a subset", - \\const Set1 = error{A, B}; - \\const Set2 = error{A, C}; - \\export fn entry() void { - \\ foo(Set1.B); - \\} - \\fn foo(set1: Set1) void { - \\ var x: Set2 = set1; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:7:19: error: expected type 'Set2', found 'Set1'", - "tmp.zig:1:23: note: 'error.B' not a member of destination error set", - }); - - ctx.objErrStage1("int to err global invalid number", - \\const Set1 = error{ - \\ A, - \\ B, - \\}; - \\comptime { - \\ var x: u16 = 3; - \\ var y = @intToError(x); - \\ _ = y; - \\} - , &[_][]const u8{ - "tmp.zig:7:13: error: integer value 3 represents no error", - }); - - ctx.objErrStage1("int to err non global invalid number", - \\const Set1 = error{ - \\ A, - \\ B, - \\}; - \\const Set2 = error{ - \\ A, - \\ C, - \\}; - \\comptime { - \\ var x = @errorToInt(Set1.B); - \\ var y = @errSetCast(Set2, @intToError(x)); - \\ _ = y; - \\} - , &[_][]const u8{ - "tmp.zig:11:13: error: error.B not a member of error set 'Set2'", - }); - - ctx.objErrStage1("duplicate error value in error set", - \\const Foo = error { - \\ Bar, - \\ Bar, - \\}; - \\export fn entry() void { - \\ const a: Foo = undefined; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: duplicate error set field 'Bar'", - "tmp.zig:2:5: note: previous declaration here", - }); - - ctx.objErrStage1("cast negative integer literal to usize", - \\export fn entry() void { - \\ const x = @as(usize, -10); - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:26: error: cannot cast negative value -10 to unsigned integer type 'usize'", - }); - - ctx.objErrStage1("use invalid number literal as array index", - \\var v = 25; - \\export fn entry() void { - \\ var arr: [v]u8 = undefined; - \\ _ = arr; - \\} - , &[_][]const u8{ - "tmp.zig:1:1: error: unable to infer variable type", - }); - - ctx.objErrStage1("duplicate struct field", - \\const Foo = struct { - \\ Bar: i32, - \\ Bar: usize, - \\}; - \\export fn entry() void { - \\ const a: Foo = undefined; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: duplicate struct field: 'Bar'", - "tmp.zig:2:5: note: other field here", - }); - - ctx.objErrStage1("duplicate union field", - \\const Foo = union { - \\ Bar: i32, - \\ Bar: usize, - \\}; - \\export fn entry() void { - \\ const a: Foo = undefined; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: duplicate union field: 'Bar'", - "tmp.zig:2:5: note: other field here", - }); - - ctx.objErrStage1("duplicate enum field", - \\const Foo = enum { - \\ Bar, - \\ Bar, - \\}; - \\ - \\export fn entry() void { - \\ const a: Foo = undefined; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: duplicate enum field: 'Bar'", - "tmp.zig:2:5: note: other field here", - }); - - ctx.objErrStage1("calling function with naked calling convention", - \\export fn entry() void { - \\ foo(); - \\} - \\fn foo() callconv(.Naked) void { } - , &[_][]const u8{ - "tmp.zig:2:5: error: unable to call function with naked calling convention", - "tmp.zig:4:1: note: declared here", - }); - - ctx.objErrStage1("function with invalid return type", - \\export fn foo() boid {} - , &[_][]const u8{ - "tmp.zig:1:17: error: use of undeclared identifier 'boid'", - }); - - ctx.objErrStage1("function with non-extern non-packed enum parameter", - \\const Foo = enum { A, B, C }; - \\export fn entry(foo: Foo) void { _ = foo; } - , &[_][]const u8{ - "tmp.zig:2:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C'", - }); - - ctx.objErrStage1("function with non-extern non-packed struct parameter", - \\const Foo = struct { - \\ A: i32, - \\ B: f32, - \\ C: bool, - \\}; - \\export fn entry(foo: Foo) void { _ = foo; } - , &[_][]const u8{ - "tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C'", - }); - - ctx.objErrStage1("function with non-extern non-packed union parameter", - \\const Foo = union { - \\ A: i32, - \\ B: f32, - \\ C: bool, - \\}; - \\export fn entry(foo: Foo) void { _ = foo; } - , &[_][]const u8{ - "tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C'", - }); - - ctx.objErrStage1("switch on enum with 1 field with no prongs", - \\const Foo = enum { M }; - \\ - \\export fn entry() void { - \\ var f = Foo.M; - \\ switch (f) {} - \\} - , &[_][]const u8{ - "tmp.zig:5:5: error: enumeration value 'Foo.M' not handled in switch", - }); - - ctx.objErrStage1("shift by negative comptime integer", - \\comptime { - \\ var a = 1 >> -1; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:2:18: error: shift by negative value -1", - }); - - ctx.objErrStage1("@panic called at compile time", - \\export fn entry() void { - \\ comptime { - \\ @panic("aoeu",); - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: encountered @panic at compile-time", - }); - - ctx.objErrStage1("wrong return type for main", - \\pub fn main() f32 { } - , &[_][]const u8{ - "error: expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'", - }); - - ctx.objErrStage1("double ?? on main return value", - \\pub fn main() ??void { - \\} - , &[_][]const u8{ - "error: expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8'", - }); - - ctx.objErrStage1("bad identifier in function with struct defined inside function which references local const", - \\export fn entry() void { - \\ const BlockKind = u32; - \\ - \\ const Block = struct { - \\ kind: BlockKind, - \\ }; - \\ - \\ bogus; - \\ - \\ _ = Block; - \\} - , &[_][]const u8{ - "tmp.zig:8:5: error: use of undeclared identifier 'bogus'", - }); - - ctx.objErrStage1("labeled break not found", - \\export fn entry() void { - \\ blah: while (true) { - \\ while (true) { - \\ break :outer; - \\ } - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:4:20: error: label not found: 'outer'", - }); - - ctx.objErrStage1("labeled continue not found", - \\export fn entry() void { - \\ var i: usize = 0; - \\ blah: while (i < 10) : (i += 1) { - \\ while (true) { - \\ continue :outer; - \\ } - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:5:23: error: label not found: 'outer'", - }); - - ctx.objErrStage1("attempt to use 0 bit type in extern fn", - \\extern fn foo(ptr: fn(*void) callconv(.C) void) void; - \\ - \\export fn entry() void { - \\ foo(bar); - \\} - \\ - \\fn bar(x: *void) callconv(.C) void { _ = x; } - \\export fn entry2() void { - \\ bar(&{}); - \\} - , &[_][]const u8{ - "tmp.zig:1:23: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'C'", - "tmp.zig:7:11: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'C'", - }); - - ctx.objErrStage1("implicit semicolon - block statement", - \\export fn entry() void { - \\ {} - \\ var good = {}; - \\ ({}) - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:9: error: expected ';' after statement", - }); - - ctx.objErrStage1("implicit semicolon - block expr", - \\export fn entry() void { - \\ _ = {}; - \\ var good = {}; - \\ _ = {} - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:11: error: expected ';' after statement", - }); - - ctx.objErrStage1("implicit semicolon - comptime statement", - \\export fn entry() void { - \\ comptime {} - \\ var good = {}; - \\ comptime ({}) - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:18: error: expected ';' after statement", - }); - - ctx.objErrStage1("implicit semicolon - comptime expression", - \\export fn entry() void { - \\ _ = comptime {}; - \\ var good = {}; - \\ _ = comptime {} - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:20: error: expected ';' after statement", - }); - - ctx.objErrStage1("implicit semicolon - defer", - \\export fn entry() void { - \\ defer {} - \\ var good = {}; - \\ defer ({}) - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:15: error: expected ';' after statement", - }); - - ctx.objErrStage1("implicit semicolon - if statement", - \\export fn entry() void { - \\ if(true) {} - \\ var good = {}; - \\ if(true) ({}) - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:18: error: expected ';' or 'else' after statement", - }); - - ctx.objErrStage1("implicit semicolon - if expression", - \\export fn entry() void { - \\ _ = if(true) {}; - \\ var good = {}; - \\ _ = if(true) {} - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:20: error: expected ';' after statement", - }); - - ctx.objErrStage1("implicit semicolon - if-else statement", - \\export fn entry() void { - \\ if(true) {} else {} - \\ var good = {}; - \\ if(true) ({}) else ({}) - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:28: error: expected ';' after statement", - }); - - ctx.objErrStage1("implicit semicolon - if-else expression", - \\export fn entry() void { - \\ _ = if(true) {} else {}; - \\ var good = {}; - \\ _ = if(true) {} else {} - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:28: error: expected ';' after statement", - }); - - ctx.objErrStage1("implicit semicolon - if-else-if statement", - \\export fn entry() void { - \\ if(true) {} else if(true) {} - \\ var good = {}; - \\ if(true) ({}) else if(true) ({}) - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:37: error: expected ';' or 'else' after statement", - }); - - ctx.objErrStage1("implicit semicolon - if-else-if expression", - \\export fn entry() void { - \\ _ = if(true) {} else if(true) {}; - \\ var good = {}; - \\ _ = if(true) {} else if(true) {} - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:37: error: expected ';' after statement", - }); - - ctx.objErrStage1("implicit semicolon - if-else-if-else statement", - \\export fn entry() void { - \\ if(true) {} else if(true) {} else {} - \\ var good = {}; - \\ if(true) ({}) else if(true) ({}) else ({}) - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:47: error: expected ';' after statement", - }); - - ctx.objErrStage1("implicit semicolon - if-else-if-else expression", - \\export fn entry() void { - \\ _ = if(true) {} else if(true) {} else {}; - \\ var good = {}; - \\ _ = if(true) {} else if(true) {} else {} - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:45: error: expected ';' after statement", - }); - - ctx.objErrStage1("implicit semicolon - test statement", - \\export fn entry() void { - \\ if (foo()) |_| {} - \\ var good = {}; - \\ if (foo()) |_| ({}) - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:24: error: expected ';' or 'else' after statement", - }); - - ctx.objErrStage1("implicit semicolon - test expression", - \\export fn entry() void { - \\ _ = if (foo()) |_| {}; - \\ var good = {}; - \\ _ = if (foo()) |_| {} - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:26: error: expected ';' after statement", - }); - - ctx.objErrStage1("implicit semicolon - while statement", - \\export fn entry() void { - \\ while(true) {} - \\ var good = {}; - \\ while(true) 1 - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:18: error: expected ';' or 'else' after statement", - }); - - ctx.objErrStage1("implicit semicolon - while expression", - \\export fn entry() void { - \\ _ = while(true) {}; - \\ var good = {}; - \\ _ = while(true) {} - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:23: error: expected ';' after statement", - }); - - ctx.objErrStage1("implicit semicolon - while-continue statement", - \\export fn entry() void { - \\ while(true):({}) {} - \\ var good = {}; - \\ while(true):({}) ({}) - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:26: error: expected ';' or 'else' after statement", - }); - - ctx.objErrStage1("implicit semicolon - while-continue expression", - \\export fn entry() void { - \\ _ = while(true):({}) {}; - \\ var good = {}; - \\ _ = while(true):({}) {} - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:28: error: expected ';' after statement", - }); - - ctx.objErrStage1("implicit semicolon - for statement", - \\export fn entry() void { - \\ for(foo()) |_| {} - \\ var good = {}; - \\ for(foo()) |_| ({}) - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:24: error: expected ';' or 'else' after statement", - }); - - ctx.objErrStage1("implicit semicolon - for expression", - \\export fn entry() void { - \\ _ = for(foo()) |_| {}; - \\ var good = {}; - \\ _ = for(foo()) |_| {} - \\ var bad = {}; - \\} - , &[_][]const u8{ - "tmp.zig:4:26: error: expected ';' after statement", - }); - - ctx.objErrStage1("multiple function definitions", - \\fn a() void {} - \\fn a() void {} - \\export fn entry() void { a(); } - , &[_][]const u8{ - "tmp.zig:2:1: error: redeclaration of 'a'", - "tmp.zig:1:1: note: other declaration here", - }); - - ctx.objErrStage1("unreachable with return", - \\fn a() noreturn {return;} - \\export fn entry() void { a(); } - , &[_][]const u8{ - "tmp.zig:1:18: error: expected type 'noreturn', found 'void'", - }); - - ctx.objErrStage1("control reaches end of non-void function", - \\fn a() i32 {} - \\export fn entry() void { _ = a(); } - , &[_][]const u8{ - "tmp.zig:1:12: error: expected type 'i32', found 'void'", - }); - - ctx.objErrStage1("undefined function call", - \\export fn a() void { - \\ b(); - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: use of undeclared identifier 'b'", - }); - - ctx.objErrStage1("wrong number of arguments", - \\export fn a() void { - \\ c(1); - \\} - \\fn c(d: i32, e: i32, f: i32) void { _ = d; _ = e; _ = f; } - , &[_][]const u8{ - "tmp.zig:2:6: error: expected 3 argument(s), found 1", - }); - - ctx.objErrStage1("invalid type", - \\fn a() bogus {} - \\export fn entry() void { _ = a(); } - , &[_][]const u8{ - "tmp.zig:1:8: error: use of undeclared identifier 'bogus'", - }); - - ctx.objErrStage1("pointer to noreturn", - \\fn a() *noreturn {} - \\export fn entry() void { _ = a(); } - , &[_][]const u8{ - "tmp.zig:1:9: error: pointer to noreturn not allowed", - }); - - ctx.objErrStage1("unreachable code", - \\export fn a() void { - \\ return; - \\ b(); - \\} - \\ - \\fn b() void {} - , &[_][]const u8{ - "tmp.zig:3:6: error: unreachable code", - "tmp.zig:2:5: note: control flow is diverted here", - }); - - ctx.objErrStage1("unreachable code - nested returns", - \\export fn a() i32 { - \\ return return 1; - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: unreachable code", - "tmp.zig:2:12: note: control flow is diverted here", - }); - - ctx.objErrStage1("unreachable code - double break", - \\export fn a() void { - \\ const b = blk: { - \\ break :blk break :blk @as(u32, 1); - \\ }; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: unreachable code", - "tmp.zig:3:20: note: control flow is diverted here", - }); - - ctx.objErrStage1("chained comparison operators", - \\export fn a(value: u32) bool { - \\ return 1 < value < 1000; - \\} - , &[_][]const u8{ - "tmp.zig:2:22: error: comparison operators cannot be chained", - }); - - ctx.objErrStage1("bad import", - \\const bogus = @import("bogus-does-not-exist.zig",); - , &[_][]const u8{ - "tmp.zig:1:23: error: unable to load '${DIR}bogus-does-not-exist.zig': FileNotFound", - }); - - ctx.objErrStage1("undeclared identifier", - \\export fn a() void { - \\ return - \\ b + - \\ c; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: use of undeclared identifier 'b'", - }); - - ctx.objErrStage1("parameter redeclaration", - \\fn f(a : i32, a : i32) void { - \\} - \\export fn entry() void { f(1, 2); } - , &[_][]const u8{ - "tmp.zig:1:15: error: redeclaration of function parameter 'a'", - "tmp.zig:1:6: note: previous declaration here", - }); - - ctx.objErrStage1("local variable redeclaration", - \\export fn f() void { - \\ const a : i32 = 0; - \\ var a = 0; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: redeclaration of local constant 'a'", - "tmp.zig:2:11: note: previous declaration here", - }); - - ctx.objErrStage1("local variable redeclares parameter", - \\fn f(a : i32) void { - \\ const a = 0; - \\} - \\export fn entry() void { f(1); } - , &[_][]const u8{ - "tmp.zig:2:11: error: redeclaration of function parameter 'a'", - "tmp.zig:1:6: note: previous declaration here", - }); - - ctx.objErrStage1("variable has wrong type", - \\export fn f() i32 { - \\ const a = "a"; - \\ return a; - \\} - , &[_][]const u8{ - "tmp.zig:3:12: error: expected type 'i32', found '*const [1:0]u8'", - }); - - ctx.objErrStage1("if condition is bool, not int", - \\export fn f() void { - \\ if (0) {} - \\} - , &[_][]const u8{ - "tmp.zig:2:9: error: expected type 'bool', found 'comptime_int'", - }); - - ctx.objErrStage1("assign unreachable", - \\export fn f() void { - \\ const a = return; - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: unreachable code", - "tmp.zig:2:15: note: control flow is diverted here", - }); - - ctx.objErrStage1("unreachable variable", - \\export fn f() void { - \\ const a: noreturn = {}; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:2:25: error: expected type 'noreturn', found 'void'", - }); - - ctx.objErrStage1("unreachable parameter", - \\fn f(a: noreturn) void { _ = a; } - \\export fn entry() void { f(); } - , &[_][]const u8{ - "tmp.zig:1:9: error: parameter of type 'noreturn' not allowed", - }); - - ctx.objErrStage1("assign to constant variable", - \\export fn f() void { - \\ const a = 3; - \\ a = 4; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: cannot assign to constant", - }); - - ctx.objErrStage1("use of undeclared identifier", - \\export fn f() void { - \\ b = 3; - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: use of undeclared identifier 'b'", - }); - - ctx.objErrStage1("const is a statement, not an expression", - \\export fn f() void { - \\ (const a = 0); - \\} - , &[_][]const u8{ - "tmp.zig:2:6: error: expected expression, found 'const'", - }); - - ctx.objErrStage1("array access of undeclared identifier", - \\export fn f() void { - \\ i[i] = i[i]; - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: use of undeclared identifier 'i'", - }); - - ctx.objErrStage1("array access of non array", - \\export fn f() void { - \\ var bad : bool = undefined; - \\ bad[0] = bad[0]; - \\} - \\export fn g() void { - \\ var bad : bool = undefined; - \\ _ = bad[0]; - \\} - , &[_][]const u8{ - "tmp.zig:3:8: error: array access of non-array type 'bool'", - "tmp.zig:7:12: error: array access of non-array type 'bool'", - }); - - ctx.objErrStage1("array access with non integer index", - \\export fn f() void { - \\ var array = "aoeu"; - \\ var bad = false; - \\ array[bad] = array[bad]; - \\} - \\export fn g() void { - \\ var array = "aoeu"; - \\ var bad = false; - \\ _ = array[bad]; - \\} - , &[_][]const u8{ - "tmp.zig:4:11: error: expected type 'usize', found 'bool'", - "tmp.zig:9:15: error: expected type 'usize', found 'bool'", - }); - - ctx.objErrStage1("write to const global variable", - \\const x : i32 = 99; - \\fn f() void { - \\ x = 1; - \\} - \\export fn entry() void { f(); } - , &[_][]const u8{ - "tmp.zig:3:9: error: cannot assign to constant", - }); - - ctx.objErrStage1("missing else clause", - \\fn f(b: bool) void { - \\ const x : i32 = if (b) h: { break :h 1; }; - \\ _ = x; - \\} - \\fn g(b: bool) void { - \\ const y = if (b) h: { break :h @as(i32, 1); }; - \\ _ = y; - \\} - \\export fn entry() void { f(true); g(true); } - , &[_][]const u8{ - "tmp.zig:2:21: error: expected type 'i32', found 'void'", - "tmp.zig:6:15: error: incompatible types: 'i32' and 'void'", - }); - - ctx.objErrStage1("invalid struct field", - \\const A = struct { x : i32, }; - \\export fn f() void { - \\ var a : A = undefined; - \\ a.foo = 1; - \\ const y = a.bar; - \\ _ = y; - \\} - \\export fn g() void { - \\ var a : A = undefined; - \\ const y = a.bar; - \\ _ = y; - \\} - , &[_][]const u8{ - "tmp.zig:4:6: error: no member named 'foo' in struct 'A'", - "tmp.zig:10:16: error: no member named 'bar' in struct 'A'", - }); - - ctx.objErrStage1("redefinition of struct", - \\const A = struct { x : i32, }; - \\const A = struct { y : i32, }; - , &[_][]const u8{ - "tmp.zig:2:1: error: redeclaration of 'A'", - "tmp.zig:1:1: note: other declaration here", - }); - - ctx.objErrStage1("redefinition of enums", - \\const A = enum {x}; - \\const A = enum {x}; - , &[_][]const u8{ - "tmp.zig:2:1: error: redeclaration of 'A'", - "tmp.zig:1:1: note: other declaration here", - }); - - ctx.objErrStage1("redefinition of global variables", - \\var a : i32 = 1; - \\var a : i32 = 2; - , &[_][]const u8{ - "tmp.zig:2:1: error: redeclaration of 'a'", - "tmp.zig:1:1: note: other declaration here", - }); - - ctx.objErrStage1("duplicate field in struct value expression", - \\const A = struct { - \\ x : i32, - \\ y : i32, - \\ z : i32, - \\}; - \\export fn f() void { - \\ const a = A { - \\ .z = 1, - \\ .y = 2, - \\ .x = 3, - \\ .z = 4, - \\ }; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:11:9: error: duplicate field", - }); - - ctx.objErrStage1("missing field in struct value expression", - \\const A = struct { - \\ x : i32, - \\ y : i32, - \\ z : i32, - \\}; - \\export fn f() void { - \\ // we want the error on the '{' not the 'A' because - \\ // the A could be a complicated expression - \\ const a = A { - \\ .z = 4, - \\ .y = 2, - \\ }; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:9:17: error: missing field: 'x'", - }); - - ctx.objErrStage1("invalid field in struct value expression", - \\const A = struct { - \\ x : i32, - \\ y : i32, - \\ z : i32, - \\}; - \\export fn f() void { - \\ const a = A { - \\ .z = 4, - \\ .y = 2, - \\ .foo = 42, - \\ }; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:10:9: error: no member named 'foo' in struct 'A'", - }); - - ctx.objErrStage1("invalid break expression", - \\export fn f() void { - \\ break; - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: break expression outside loop", - }); - - ctx.objErrStage1("invalid continue expression", - \\export fn f() void { - \\ continue; - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: continue expression outside loop", - }); - - ctx.objErrStage1("invalid maybe type", - \\export fn f() void { - \\ if (true) |x| { _ = x; } - \\} - , &[_][]const u8{ - "tmp.zig:2:9: error: expected optional type, found 'bool'", - }); - - ctx.objErrStage1("cast unreachable", - \\fn f() i32 { - \\ return @as(i32, return 1); - \\} - \\export fn entry() void { _ = f(); } - , &[_][]const u8{ - "tmp.zig:2:12: error: unreachable code", - "tmp.zig:2:21: note: control flow is diverted here", - }); - - ctx.objErrStage1("invalid builtin fn", - \\fn f() @bogus(foo) { - \\} - \\export fn entry() void { _ = f(); } - , &[_][]const u8{ - "tmp.zig:1:8: error: invalid builtin function: '@bogus'", - }); - - ctx.objErrStage1("noalias on non pointer param", - \\fn f(noalias x: i32) void { _ = x; } - \\export fn entry() void { f(1234); } - , &[_][]const u8{ - "tmp.zig:1:6: error: noalias on non-pointer parameter", - }); - - ctx.objErrStage1("struct init syntax for array", - \\const foo = [3]u16{ .x = 1024 }; - \\comptime { - \\ _ = foo; - \\} - , &[_][]const u8{ - "tmp.zig:1:13: error: initializing array with struct syntax", - }); - - ctx.objErrStage1("type variables must be constant", - \\var foo = u8; - \\export fn entry() foo { - \\ return 1; - \\} - , &[_][]const u8{ - "tmp.zig:1:1: error: variable of type 'type' must be constant", - }); - - ctx.objErrStage1("parameter shadowing global", - \\const Foo = struct {}; - \\fn f(Foo: i32) void {} - \\export fn entry() void { - \\ f(1234); - \\} - , &[_][]const u8{ - "tmp.zig:2:6: error: local shadows declaration of 'Foo'", - "tmp.zig:1:1: note: declared here", - }); - - ctx.objErrStage1("local variable shadowing global", - \\const Foo = struct {}; - \\const Bar = struct {}; - \\ - \\export fn entry() void { - \\ var Bar : i32 = undefined; - \\ _ = Bar; - \\} - , &[_][]const u8{ - "tmp.zig:5:9: error: local shadows declaration of 'Bar'", - "tmp.zig:2:1: note: declared here", - }); - - ctx.objErrStage1("local shadows global that occurs later", - \\pub fn main() void { - \\ var foo = true; - \\ _ = foo; - \\} - \\fn foo() void {} - , &[_][]const u8{ - "tmp.zig:2:9: error: local shadows declaration of 'foo'", - "tmp.zig:5:1: note: declared here", - }); - - ctx.objErrStage1("switch expression - missing enumeration prong", - \\const Number = enum { - \\ One, - \\ Two, - \\ Three, - \\ Four, - \\}; - \\fn f(n: Number) i32 { - \\ switch (n) { - \\ Number.One => 1, - \\ Number.Two => 2, - \\ Number.Three => @as(i32, 3), - \\ } - \\} - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } - , &[_][]const u8{ - "tmp.zig:8:5: error: enumeration value 'Number.Four' not handled in switch", - }); - - ctx.objErrStage1("switch expression - duplicate enumeration prong", - \\const Number = enum { - \\ One, - \\ Two, - \\ Three, - \\ Four, - \\}; - \\fn f(n: Number) i32 { - \\ switch (n) { - \\ Number.One => 1, - \\ Number.Two => 2, - \\ Number.Three => @as(i32, 3), - \\ Number.Four => 4, - \\ Number.Two => 2, - \\ } - \\} - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } - , &[_][]const u8{ - "tmp.zig:13:15: error: duplicate switch value", - "tmp.zig:10:15: note: other value here", - }); - - ctx.objErrStage1("switch expression - duplicate enumeration prong when else present", - \\const Number = enum { - \\ One, - \\ Two, - \\ Three, - \\ Four, - \\}; - \\fn f(n: Number) i32 { - \\ switch (n) { - \\ Number.One => 1, - \\ Number.Two => 2, - \\ Number.Three => @as(i32, 3), - \\ Number.Four => 4, - \\ Number.Two => 2, - \\ else => 10, - \\ } - \\} - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } - , &[_][]const u8{ - "tmp.zig:13:15: error: duplicate switch value", - "tmp.zig:10:15: note: other value here", - }); - - ctx.objErrStage1("switch expression - multiple else prongs", - \\fn f(x: u32) void { - \\ const value: bool = switch (x) { - \\ 1234 => false, - \\ else => true, - \\ else => true, - \\ }; - \\} - \\export fn entry() void { - \\ f(1234); - \\} - , &[_][]const u8{ - "tmp.zig:5:9: error: multiple else prongs in switch expression", - }); - - ctx.objErrStage1("switch expression - non exhaustive integer prongs", - \\fn foo(x: u8) void { - \\ switch (x) { - \\ 0 => {}, - \\ } - \\} - \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } - , &[_][]const u8{ - "tmp.zig:2:5: error: switch must handle all possibilities", - }); - - ctx.objErrStage1("switch expression - duplicate or overlapping integer value", - \\fn foo(x: u8) u8 { - \\ return switch (x) { - \\ 0 ... 100 => @as(u8, 0), - \\ 101 ... 200 => 1, - \\ 201, 203 ... 207 => 2, - \\ 206 ... 255 => 3, - \\ }; - \\} - \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } - , &[_][]const u8{ - "tmp.zig:6:9: error: duplicate switch value", - "tmp.zig:5:14: note: previous value here", - }); - - ctx.objErrStage1("switch expression - duplicate type", - \\fn foo(comptime T: type, x: T) u8 { - \\ _ = x; - \\ return switch (T) { - \\ u32 => 0, - \\ u64 => 1, - \\ u32 => 2, - \\ else => 3, - \\ }; - \\} - \\export fn entry() usize { return @sizeOf(@TypeOf(foo(u32, 0))); } - , &[_][]const u8{ - "tmp.zig:6:9: error: duplicate switch value", - "tmp.zig:4:9: note: previous value here", - }); - - ctx.objErrStage1("switch expression - duplicate type (struct alias)", - \\const Test = struct { - \\ bar: i32, - \\}; - \\const Test2 = Test; - \\fn foo(comptime T: type, x: T) u8 { - \\ _ = x; - \\ return switch (T) { - \\ Test => 0, - \\ u64 => 1, - \\ Test2 => 2, - \\ else => 3, - \\ }; - \\} - \\export fn entry() usize { return @sizeOf(@TypeOf(foo(u32, 0))); } - , &[_][]const u8{ - "tmp.zig:10:9: error: duplicate switch value", - "tmp.zig:8:9: note: previous value here", - }); - - ctx.objErrStage1("switch expression - switch on pointer type with no else", - \\fn foo(x: *u8) void { - \\ switch (x) { - \\ &y => {}, - \\ } - \\} - \\const y: u8 = 100; - \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } - , &[_][]const u8{ - "tmp.zig:2:5: error: else prong required when switching on type '*u8'", - }); - - ctx.objErrStage1("global variable initializer must be constant expression", - \\extern fn foo() i32; - \\const x = foo(); - \\export fn entry() i32 { return x; } - , &[_][]const u8{ - "tmp.zig:2:11: error: unable to evaluate constant expression", - }); - - ctx.objErrStage1("array concatenation with wrong type", - \\const src = "aoeu"; - \\const derp: usize = 1234; - \\const a = derp ++ "foo"; - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(a)); } - , &[_][]const u8{ - "tmp.zig:3:11: error: expected array, found 'usize'", - }); - - ctx.objErrStage1("non compile time array concatenation", - \\fn f() []u8 { - \\ return s ++ "foo"; - \\} - \\var s: [10]u8 = undefined; - \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } - , &[_][]const u8{ - "tmp.zig:2:12: error: unable to evaluate constant expression", - }); - - ctx.objErrStage1("@cImport with bogus include", - \\const c = @cImport(@cInclude("bogus.h")); - \\export fn entry() usize { return @sizeOf(@TypeOf(c.bogo)); } - , &[_][]const u8{ - "tmp.zig:1:11: error: C import failed", - ".h:1:10: note: 'bogus.h' file not found", - }); - - ctx.objErrStage1("address of number literal", - \\const x = 3; - \\const y = &x; - \\fn foo() *const i32 { return y; } - \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } - , &[_][]const u8{ - "tmp.zig:3:30: error: expected type '*const i32', found '*const comptime_int'", - }); - - ctx.objErrStage1("integer overflow error", - \\const x : u8 = 300; - \\export fn entry() usize { return @sizeOf(@TypeOf(x)); } - , &[_][]const u8{ - "tmp.zig:1:16: error: integer value 300 cannot be coerced to type 'u8'", - }); - - ctx.objErrStage1("invalid shift amount error", - \\const x : u8 = 2; - \\fn f() u16 { - \\ return x << 8; - \\} - \\export fn entry() u16 { return f(); } - , &[_][]const u8{ - "tmp.zig:3:17: error: integer value 8 cannot be coerced to type 'u3'", - }); - - ctx.objErrStage1("missing function call param", - \\const Foo = struct { - \\ a: i32, - \\ b: i32, - \\ - \\ fn member_a(foo: *const Foo) i32 { - \\ return foo.a; - \\ } - \\ fn member_b(foo: *const Foo) i32 { - \\ return foo.b; - \\ } - \\}; - \\ - \\const member_fn_type = @TypeOf(Foo.member_a); - \\const members = [_]member_fn_type { - \\ Foo.member_a, - \\ Foo.member_b, - \\}; - \\ - \\fn f(foo: *const Foo, index: usize) void { - \\ const result = members[index](); - \\ _ = foo; - \\ _ = result; - \\} - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } - , &[_][]const u8{ - "tmp.zig:20:34: error: expected 1 argument(s), found 0", - }); - - ctx.objErrStage1("missing function name", - \\fn () void {} - \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } - , &[_][]const u8{ - "tmp.zig:1:1: error: missing function name", - }); - - ctx.objErrStage1("missing param name", - \\fn f(i32) void {} - \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } - , &[_][]const u8{ - "tmp.zig:1:6: error: missing parameter name", - }); - - ctx.objErrStage1("wrong function type", - \\const fns = [_]fn() void { a, b, c }; - \\fn a() i32 {return 0;} - \\fn b() i32 {return 1;} - \\fn c() i32 {return 2;} - \\export fn entry() usize { return @sizeOf(@TypeOf(fns)); } - , &[_][]const u8{ - "tmp.zig:1:28: error: expected type 'fn() void', found 'fn() i32'", - }); - - ctx.objErrStage1("extern function pointer mismatch", - \\const fns = [_](fn(i32)i32) { a, b, c }; - \\pub fn a(x: i32) i32 {return x + 0;} - \\pub fn b(x: i32) i32 {return x + 1;} - \\export fn c(x: i32) i32 {return x + 2;} - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(fns)); } - , &[_][]const u8{ - "tmp.zig:1:37: error: expected type 'fn(i32) i32', found 'fn(i32) callconv(.C) i32'", - }); - - ctx.objErrStage1("colliding invalid top level functions", - \\fn func() bogus {} - \\fn func() bogus {} - \\export fn entry() usize { return @sizeOf(@TypeOf(func)); } - , &[_][]const u8{ - "tmp.zig:2:1: error: redeclaration of 'func'", - "tmp.zig:1:1: note: other declaration here", - }); - - ctx.objErrStage1("non constant expression in array size", - \\const Foo = struct { - \\ y: [get()]u8, - \\}; - \\var global_var: usize = 1; - \\fn get() usize { return global_var; } - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(Foo)); } - , &[_][]const u8{ - "tmp.zig:5:25: error: cannot store runtime value in compile time variable", - "tmp.zig:2:12: note: called from here", - }); - - ctx.objErrStage1("addition with non numbers", - \\const Foo = struct { - \\ field: i32, - \\}; - \\const x = Foo {.field = 1} + Foo {.field = 2}; - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(x)); } - , &[_][]const u8{ - "tmp.zig:4:28: error: invalid operands to binary expression: 'Foo' and 'Foo'", - }); - - ctx.objErrStage1("division by zero", - \\const lit_int_x = 1 / 0; - \\const lit_float_x = 1.0 / 0.0; - \\const int_x = @as(u32, 1) / @as(u32, 0); - \\const float_x = @as(f32, 1.0) / @as(f32, 0.0); - \\ - \\export fn entry1() usize { return @sizeOf(@TypeOf(lit_int_x)); } - \\export fn entry2() usize { return @sizeOf(@TypeOf(lit_float_x)); } - \\export fn entry3() usize { return @sizeOf(@TypeOf(int_x)); } - \\export fn entry4() usize { return @sizeOf(@TypeOf(float_x)); } - , &[_][]const u8{ - "tmp.zig:1:21: error: division by zero", - "tmp.zig:2:25: error: division by zero", - "tmp.zig:3:27: error: division by zero", - "tmp.zig:4:31: error: division by zero", - }); - - ctx.objErrStage1("normal string with newline", - \\const foo = "a - \\b"; - , &[_][]const u8{ - "tmp.zig:1:13: error: expected expression, found 'invalid bytes'", - "tmp.zig:1:15: note: invalid byte: '\\n'", - }); - - ctx.objErrStage1("invalid comparison for function pointers", - \\fn foo() void {} - \\const invalid = foo > foo; - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(invalid)); } - , &[_][]const u8{ - "tmp.zig:2:21: error: operator not allowed for type 'fn() void'", - }); - - ctx.objErrStage1("generic function instance with non-constant expression", - \\fn foo(comptime x: i32, y: i32) i32 { return x + y; } - \\fn test1(a: i32, b: i32) i32 { - \\ return foo(a, b); - \\} - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(test1)); } - , &[_][]const u8{ - "tmp.zig:3:16: error: runtime value cannot be passed to comptime arg", - }); - - ctx.objErrStage1("assign null to non-optional pointer", - \\const a: *u8 = null; - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(a)); } - , &[_][]const u8{ - "tmp.zig:1:16: error: expected type '*u8', found '@Type(.Null)'", - }); - - ctx.objErrStage1("indexing an array of size zero", - \\const array = [_]u8{}; - \\export fn foo() void { - \\ const pointer = &array[0]; - \\ _ = pointer; - \\} - , &[_][]const u8{ - "tmp.zig:3:27: error: accessing a zero length array is not allowed", - }); - - ctx.objErrStage1("indexing an array of size zero with runtime index", - \\const array = [_]u8{}; - \\export fn foo() void { - \\ var index: usize = 0; - \\ const pointer = &array[index]; - \\ _ = pointer; - \\} - , &[_][]const u8{ - "tmp.zig:4:27: error: accessing a zero length array is not allowed", - }); - - ctx.objErrStage1("compile time division by zero", - \\const y = foo(0); - \\fn foo(x: u32) u32 { - \\ return 1 / x; - \\} - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(y)); } - , &[_][]const u8{ - "tmp.zig:3:14: error: division by zero", - }); - - ctx.objErrStage1("branch on undefined value", - \\const x = if (undefined) true else false; - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(x)); } - , &[_][]const u8{ - "tmp.zig:1:15: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("div on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ _ = a / a; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("div assign on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ a /= a; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("mod on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ _ = a % a; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("mod assign on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ a %= a; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("add on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ _ = a + a; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("add assign on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ a += a; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("add wrap on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ _ = a +% a; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("add wrap assign on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ a +%= a; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("sub on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ _ = a - a; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("sub assign on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ a -= a; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("sub wrap on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ _ = a -% a; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("sub wrap assign on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ a -%= a; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("mult on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ _ = a * a; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("mult assign on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ a *= a; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("mult wrap on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ _ = a *% a; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("mult wrap assign on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ a *%= a; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("shift left on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ _ = a << 2; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("shift left assign on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ a <<= 2; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("shift right on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ _ = a >> 2; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("shift left assign on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ a >>= 2; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("bin and on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ _ = a & a; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("bin and assign on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ a &= a; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("bin or on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ _ = a | a; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("bin or assign on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ a |= a; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("bin xor on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ _ = a ^ a; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("bin xor assign on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ a ^= a; - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("comparison operators with undefined value", - \\// operator == - \\comptime { - \\ var a: i64 = undefined; - \\ var x: i32 = 0; - \\ if (a == a) x += 1; - \\} - \\// operator != - \\comptime { - \\ var a: i64 = undefined; - \\ var x: i32 = 0; - \\ if (a != a) x += 1; - \\} - \\// operator > - \\comptime { - \\ var a: i64 = undefined; - \\ var x: i32 = 0; - \\ if (a > a) x += 1; - \\} - \\// operator < - \\comptime { - \\ var a: i64 = undefined; - \\ var x: i32 = 0; - \\ if (a < a) x += 1; - \\} - \\// operator >= - \\comptime { - \\ var a: i64 = undefined; - \\ var x: i32 = 0; - \\ if (a >= a) x += 1; - \\} - \\// operator <= - \\comptime { - \\ var a: i64 = undefined; - \\ var x: i32 = 0; - \\ if (a <= a) x += 1; - \\} - , &[_][]const u8{ - "tmp.zig:5:11: error: use of undefined value here causes undefined behavior", - "tmp.zig:11:11: error: use of undefined value here causes undefined behavior", - "tmp.zig:17:11: error: use of undefined value here causes undefined behavior", - "tmp.zig:23:11: error: use of undefined value here causes undefined behavior", - "tmp.zig:29:11: error: use of undefined value here causes undefined behavior", - "tmp.zig:35:11: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("and on undefined value", - \\comptime { - \\ var a: bool = undefined; - \\ _ = a and a; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("or on undefined value", - \\comptime { - \\ var a: bool = undefined; - \\ _ = a or a; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("negate on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ _ = -a; - \\} - , &[_][]const u8{ - "tmp.zig:3:10: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("negate wrap on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ _ = -%a; - \\} - , &[_][]const u8{ - "tmp.zig:3:11: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("bin not on undefined value", - \\comptime { - \\ var a: i64 = undefined; - \\ _ = ~a; - \\} - , &[_][]const u8{ - "tmp.zig:3:10: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("bool not on undefined value", - \\comptime { - \\ var a: bool = undefined; - \\ _ = !a; - \\} - , &[_][]const u8{ - "tmp.zig:3:10: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("orelse on undefined value", - \\comptime { - \\ var a: ?bool = undefined; - \\ _ = a orelse false; - \\} - , &[_][]const u8{ - "tmp.zig:3:11: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("catch on undefined value", - \\comptime { - \\ var a: anyerror!bool = undefined; - \\ _ = a catch false; - \\} - , &[_][]const u8{ - "tmp.zig:3:11: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("deref on undefined value", - \\comptime { - \\ var a: *u8 = undefined; - \\ _ = a.*; - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: attempt to dereference undefined value", - }); - - ctx.objErrStage1("endless loop in function evaluation", - \\const seventh_fib_number = fibonacci(7); - \\fn fibonacci(x: i32) i32 { - \\ return fibonacci(x - 1) + fibonacci(x - 2); - \\} - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(seventh_fib_number)); } - , &[_][]const u8{ - "tmp.zig:3:21: error: evaluation exceeded 1000 backwards branches", - }); - - ctx.objErrStage1("@embedFile with bogus file", - \\const resource = @embedFile("bogus.txt",); - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(resource)); } - , &[_][]const u8{ - "tmp.zig:1:29: error: unable to find '", - }); - - ctx.objErrStage1("non-const expression in struct literal outside function", - \\const Foo = struct { - \\ x: i32, - \\}; - \\const a = Foo {.x = get_it()}; - \\extern fn get_it() i32; - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(a)); } - , &[_][]const u8{ - "tmp.zig:4:21: error: unable to evaluate constant expression", - }); - - ctx.objErrStage1("non-const expression function call with struct return value outside function", - \\const Foo = struct { - \\ x: i32, - \\}; - \\const a = get_it(); - \\fn get_it() Foo { - \\ global_side_effect = true; - \\ return Foo {.x = 13}; - \\} - \\var global_side_effect = false; - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(a)); } - , &[_][]const u8{ - "tmp.zig:6:26: error: unable to evaluate constant expression", - }); - - ctx.objErrStage1("undeclared identifier error should mark fn as impure", - \\export fn foo() void { - \\ test_a_thing(); - \\} - \\fn test_a_thing() void { - \\ bad_fn_call(); - \\} - , &[_][]const u8{ - "tmp.zig:5:5: error: use of undeclared identifier 'bad_fn_call'", - }); - - ctx.objErrStage1("illegal comparison of types", - \\fn bad_eql_1(a: []u8, b: []u8) bool { - \\ return a == b; - \\} - \\const EnumWithData = union(enum) { - \\ One: void, - \\ Two: i32, - \\}; - \\fn bad_eql_2(a: *const EnumWithData, b: *const EnumWithData) bool { - \\ return a.* == b.*; - \\} - \\ - \\export fn entry1() usize { return @sizeOf(@TypeOf(bad_eql_1)); } - \\export fn entry2() usize { return @sizeOf(@TypeOf(bad_eql_2)); } - , &[_][]const u8{ - "tmp.zig:2:14: error: operator not allowed for type '[]u8'", - "tmp.zig:9:16: error: operator not allowed for type 'EnumWithData'", - }); - - ctx.objErrStage1("non-const switch number literal", - \\export fn foo() void { - \\ const x = switch (bar()) { - \\ 1, 2 => 1, - \\ 3, 4 => 2, - \\ else => 3, - \\ }; - \\ _ = x; - \\} - \\fn bar() i32 { - \\ return 2; - \\} - , &[_][]const u8{ - "tmp.zig:5:17: error: cannot store runtime value in type 'comptime_int'", - }); - - ctx.objErrStage1("atomic orderings of cmpxchg - failure stricter than success", - \\const AtomicOrder = @import("std").builtin.AtomicOrder; - \\export fn f() void { - \\ var x: i32 = 1234; - \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {} - \\} - , &[_][]const u8{ - "tmp.zig:4:81: error: failure atomic ordering must be no stricter than success", - }); - - ctx.objErrStage1("atomic orderings of cmpxchg - success Monotonic or stricter", - \\const AtomicOrder = @import("std").builtin.AtomicOrder; - \\export fn f() void { - \\ var x: i32 = 1234; - \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {} - \\} - , &[_][]const u8{ - "tmp.zig:4:58: error: success atomic ordering must be Monotonic or stricter", - }); - - ctx.objErrStage1("negation overflow in function evaluation", - \\const y = neg(-128); - \\fn neg(x: i8) i8 { - \\ return -x; - \\} - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(y)); } - , &[_][]const u8{ - "tmp.zig:3:12: error: negation caused overflow", - }); - - ctx.objErrStage1("add overflow in function evaluation", - \\const y = add(65530, 10); - \\fn add(a: u16, b: u16) u16 { - \\ return a + b; - \\} - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(y)); } - , &[_][]const u8{ - "tmp.zig:3:14: error: operation caused overflow", - }); - - ctx.objErrStage1("sub overflow in function evaluation", - \\const y = sub(10, 20); - \\fn sub(a: u16, b: u16) u16 { - \\ return a - b; - \\} - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(y)); } - , &[_][]const u8{ - "tmp.zig:3:14: error: operation caused overflow", - }); - - ctx.objErrStage1("mul overflow in function evaluation", - \\const y = mul(300, 6000); - \\fn mul(a: u16, b: u16) u16 { - \\ return a * b; - \\} - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(y)); } - , &[_][]const u8{ - "tmp.zig:3:14: error: operation caused overflow", - }); - - ctx.objErrStage1("truncate sign mismatch", - \\export fn entry1() i8 { - \\ var x: u32 = 10; - \\ return @truncate(i8, x); - \\} - \\export fn entry2() u8 { - \\ var x: i32 = -10; - \\ return @truncate(u8, x); - \\} - \\export fn entry3() i8 { - \\ comptime var x: u32 = 10; - \\ return @truncate(i8, x); - \\} - \\export fn entry4() u8 { - \\ comptime var x: i32 = -10; - \\ return @truncate(u8, x); - \\} - , &[_][]const u8{ - "tmp.zig:3:26: error: expected signed integer type, found 'u32'", - "tmp.zig:7:26: error: expected unsigned integer type, found 'i32'", - "tmp.zig:11:26: error: expected signed integer type, found 'u32'", - "tmp.zig:15:26: error: expected unsigned integer type, found 'i32'", - }); - - ctx.objErrStage1("try in function with non error return type", - \\export fn f() void { - \\ try something(); - \\} - \\fn something() anyerror!void { } - , &[_][]const u8{ - "tmp.zig:2:5: error: expected type 'void', found 'anyerror'", - }); - - ctx.objErrStage1("invalid pointer for var type", - \\extern fn ext() usize; - \\var bytes: [ext()]u8 = undefined; - \\export fn f() void { - \\ for (bytes) |*b, i| { - \\ b.* = @as(u8, i); - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:2:13: error: unable to evaluate constant expression", - }); - - ctx.objErrStage1("export function with comptime parameter", - \\export fn foo(comptime x: i32, y: i32) i32{ - \\ return x + y; - \\} - , &[_][]const u8{ - "tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'C'", - }); - - ctx.objErrStage1("extern function with comptime parameter", - \\extern fn foo(comptime x: i32, y: i32) i32; - \\fn f() i32 { - \\ return foo(1, 2); - \\} - \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } - , &[_][]const u8{ - "tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'C'", - }); - - ctx.objErrStage1("non-pure function returns type", - \\var a: u32 = 0; - \\pub fn List(comptime T: type) type { - \\ a += 1; - \\ return SmallList(T, 8); - \\} - \\ - \\pub fn SmallList(comptime T: type, comptime STATIC_SIZE: usize) type { - \\ return struct { - \\ items: []T, - \\ length: usize, - \\ prealloc_items: [STATIC_SIZE]T, - \\ }; - \\} - \\ - \\export fn function_with_return_type_type() void { - \\ var list: List(i32) = undefined; - \\ list.length = 10; - \\} - , &[_][]const u8{ - "tmp.zig:3:7: error: unable to evaluate constant expression", - }); - - ctx.objErrStage1("bogus method call on slice", - \\var self = "aoeu"; - \\fn f(m: []const u8) void { - \\ m.copy(u8, self[0..], m); - \\} - \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } - , &[_][]const u8{ - "tmp.zig:3:6: error: no member named 'copy' in '[]const u8'", - }); - - ctx.objErrStage1("wrong number of arguments for method fn call", - \\const Foo = struct { - \\ fn method(self: *const Foo, a: i32) void {_ = self; _ = a;} - \\}; - \\fn f(foo: *const Foo) void { - \\ - \\ foo.method(1, 2); - \\} - \\export fn entry() usize { return @sizeOf(@TypeOf(f)); } - , &[_][]const u8{ - "tmp.zig:6:15: error: expected 2 argument(s), found 3", - }); - - ctx.objErrStage1("assign through constant pointer", - \\export fn f() void { - \\ var cstr = "Hat"; - \\ cstr[0] = 'W'; - \\} - , &[_][]const u8{ - "tmp.zig:3:13: error: cannot assign to constant", - }); - - ctx.objErrStage1("assign through constant slice", - \\export fn f() void { - \\ var cstr: []const u8 = "Hat"; - \\ cstr[0] = 'W'; - \\} - , &[_][]const u8{ - "tmp.zig:3:13: error: cannot assign to constant", - }); - - ctx.objErrStage1("main function with bogus args type", - \\pub fn main(args: [][]bogus) !void {_ = args;} - , &[_][]const u8{ - "tmp.zig:1:23: error: use of undeclared identifier 'bogus'", - }); - - ctx.objErrStage1("misspelled type with pointer only reference", - \\const JasonHM = u8; - \\const JasonList = *JsonNode; - \\ - \\const JsonOA = union(enum) { - \\ JSONArray: JsonList, - \\ JSONObject: JasonHM, - \\}; - \\ - \\const JsonType = union(enum) { - \\ JSONNull: void, - \\ JSONInteger: isize, - \\ JSONDouble: f64, - \\ JSONBool: bool, - \\ JSONString: []u8, - \\ JSONArray: void, - \\ JSONObject: void, - \\}; - \\ - \\pub const JsonNode = struct { - \\ kind: JsonType, - \\ jobject: ?JsonOA, - \\}; - \\ - \\fn foo() void { - \\ var jll: JasonList = undefined; - \\ jll.init(1234); - \\ var jd = JsonNode {.kind = JsonType.JSONArray , .jobject = JsonOA.JSONArray {jll} }; - \\ _ = jd; - \\} - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } - , &[_][]const u8{ - "tmp.zig:5:16: error: use of undeclared identifier 'JsonList'", - }); - - ctx.objErrStage1("method call with first arg type primitive", - \\const Foo = struct { - \\ x: i32, - \\ - \\ fn init(x: i32) Foo { - \\ return Foo { - \\ .x = x, - \\ }; - \\ } - \\}; - \\ - \\export fn f() void { - \\ const derp = Foo.init(3); - \\ - \\ derp.init(); - \\} - , &[_][]const u8{ - "tmp.zig:14:5: error: expected type 'i32', found 'Foo'", - }); - - ctx.objErrStage1("method call with first arg type wrong container", - \\pub const List = struct { - \\ len: usize, - \\ allocator: *Allocator, - \\ - \\ pub fn init(allocator: *Allocator) List { - \\ return List { - \\ .len = 0, - \\ .allocator = allocator, - \\ }; - \\ } - \\}; - \\ - \\pub var global_allocator = Allocator { - \\ .field = 1234, - \\}; - \\ - \\pub const Allocator = struct { - \\ field: i32, - \\}; - \\ - \\export fn foo() void { - \\ var x = List.init(&global_allocator); - \\ x.init(); - \\} - , &[_][]const u8{ - "tmp.zig:23:5: error: expected type '*Allocator', found '*List'", - }); - - ctx.objErrStage1("binary not on number literal", - \\const TINY_QUANTUM_SHIFT = 4; - \\const TINY_QUANTUM_SIZE = 1 << TINY_QUANTUM_SHIFT; - \\var block_aligned_stuff: usize = (4 + TINY_QUANTUM_SIZE) & ~(TINY_QUANTUM_SIZE - 1); - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(block_aligned_stuff)); } - , &[_][]const u8{ - "tmp.zig:3:60: error: unable to perform binary not operation on type 'comptime_int'", - }); - { const case = ctx.obj("multiple files with private function error", .{}); case.backend = .stage1; @@ -6684,223 +341,6 @@ pub fn addCases(ctx: *TestContext) !void { }); } - ctx.objErrStage1("container init with non-type", - \\const zero: i32 = 0; - \\const a = zero{1}; - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(a)); } - , &[_][]const u8{ - "tmp.zig:2:11: error: expected type 'type', found 'i32'", - }); - - ctx.objErrStage1("assign to constant field", - \\const Foo = struct { - \\ field: i32, - \\}; - \\export fn derp() void { - \\ const f = Foo {.field = 1234,}; - \\ f.field = 0; - \\} - , &[_][]const u8{ - "tmp.zig:6:15: error: cannot assign to constant", - }); - - ctx.objErrStage1("return from defer expression", - \\pub fn testTrickyDefer() !void { - \\ defer canFail() catch {}; - \\ - \\ defer try canFail(); - \\ - \\ const a = maybeInt() orelse return; - \\} - \\ - \\fn canFail() anyerror!void { } - \\ - \\pub fn maybeInt() ?i32 { - \\ return 0; - \\} - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(testTrickyDefer)); } - , &[_][]const u8{ - "tmp.zig:4:11: error: 'try' not allowed inside defer expression", - }); - - ctx.objErrStage1("assign too big number to u16", - \\export fn foo() void { - \\ var vga_mem: u16 = 0xB8000; - \\ _ = vga_mem; - \\} - , &[_][]const u8{ - "tmp.zig:2:24: error: integer value 753664 cannot be coerced to type 'u16'", - }); - - ctx.objErrStage1("global variable alignment non power of 2", - \\const some_data: [100]u8 align(3) = undefined; - \\export fn entry() usize { return @sizeOf(@TypeOf(some_data)); } - , &[_][]const u8{ - "tmp.zig:1:32: error: alignment value 3 is not a power of 2", - }); - - ctx.objErrStage1("function alignment non power of 2", - \\extern fn foo() align(3) void; - \\export fn entry() void { return foo(); } - , &[_][]const u8{ - "tmp.zig:1:23: error: alignment value 3 is not a power of 2", - }); - - ctx.objErrStage1("compile log", - \\export fn foo() void { - \\ comptime bar(12, "hi",); - \\} - \\fn bar(a: i32, b: []const u8) void { - \\ @compileLog("begin",); - \\ @compileLog("a", a, "b", b); - \\ @compileLog("end",); - \\} - , &[_][]const u8{ - "tmp.zig:5:5: error: found compile log statement", - "tmp.zig:6:5: error: found compile log statement", - "tmp.zig:7:5: error: found compile log statement", - }); - - ctx.objErrStage1("casting bit offset pointer to regular pointer", - \\const BitField = packed struct { - \\ a: u3, - \\ b: u3, - \\ c: u2, - \\}; - \\ - \\fn foo(bit_field: *const BitField) u3 { - \\ return bar(&bit_field.b); - \\} - \\ - \\fn bar(x: *const u3) u3 { - \\ return x.*; - \\} - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } - , &[_][]const u8{ - "tmp.zig:8:26: error: expected type '*const u3', found '*align(:3:1) const u3'", - }); - - ctx.objErrStage1("referring to a struct that is invalid", - \\const UsbDeviceRequest = struct { - \\ Type: u8, - \\}; - \\ - \\export fn foo() void { - \\ comptime assert(@sizeOf(UsbDeviceRequest) == 0x8); - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , &[_][]const u8{ - "tmp.zig:10:14: error: reached unreachable code", - }); - - ctx.objErrStage1("control flow uses comptime var at runtime", - \\export fn foo() void { - \\ comptime var i = 0; - \\ while (i < 5) : (i += 1) { - \\ bar(); - \\ } - \\} - \\ - \\fn bar() void { } - , &[_][]const u8{ - "tmp.zig:3:5: error: control flow attempts to use compile-time variable at runtime", - "tmp.zig:3:24: note: compile-time variable assigned here", - }); - - ctx.objErrStage1("ignored return value", - \\export fn foo() void { - \\ bar(); - \\} - \\fn bar() i32 { return 0; } - , &[_][]const u8{ - "tmp.zig:2:8: error: expression value is ignored", - }); - - ctx.objErrStage1("ignored assert-err-ok return value", - \\export fn foo() void { - \\ bar() catch unreachable; - \\} - \\fn bar() anyerror!i32 { return 0; } - , &[_][]const u8{ - "tmp.zig:2:11: error: expression value is ignored", - }); - - ctx.objErrStage1("ignored statement value", - \\export fn foo() void { - \\ 1; - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: expression value is ignored", - }); - - ctx.objErrStage1("ignored comptime statement value", - \\export fn foo() void { - \\ comptime {1;} - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: expression value is ignored", - }); - - ctx.objErrStage1("ignored comptime value", - \\export fn foo() void { - \\ comptime 1; - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: expression value is ignored", - }); - - ctx.objErrStage1("ignored deferred statement value", - \\export fn foo() void { - \\ defer {1;} - \\} - , &[_][]const u8{ - "tmp.zig:2:12: error: expression value is ignored", - }); - - ctx.objErrStage1("ignored deferred function call", - \\export fn foo() void { - \\ defer bar(); - \\} - \\fn bar() anyerror!i32 { return 0; } - , &[_][]const u8{ - "tmp.zig:2:14: error: error is ignored. consider using `try`, `catch`, or `if`", - }); - - ctx.objErrStage1("dereference an array", - \\var s_buffer: [10]u8 = undefined; - \\pub fn pass(in: []u8) []u8 { - \\ var out = &s_buffer; - \\ out.*.* = in[0]; - \\ return out.*[0..1]; - \\} - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(pass)); } - , &[_][]const u8{ - "tmp.zig:4:10: error: attempt to dereference non-pointer type '[10]u8'", - }); - - ctx.objErrStage1("pass const ptr to mutable ptr fn", - \\fn foo() bool { - \\ const a = @as([]const u8, "a",); - \\ const b = &a; - \\ return ptrEql(b, b); - \\} - \\fn ptrEql(a: *[]const u8, b: *[]const u8) bool { - \\ _ = a; _ = b; - \\ return true; - \\} - \\ - \\export fn entry() usize { return @sizeOf(@TypeOf(foo)); } - , &[_][]const u8{ - "tmp.zig:4:19: error: expected type '*[]const u8', found '*const []const u8'", - }); - { const case = ctx.obj("export collision", .{}); case.backend = .stage1; @@ -6922,236 +362,18 @@ pub fn addCases(ctx: *TestContext) !void { }); } - ctx.objErrStage1("implicit cast from array to mutable slice", - \\var global_array: [10]i32 = undefined; - \\fn foo(param: []i32) void {_ = param;} - \\export fn entry() void { - \\ foo(global_array); - \\} - , &[_][]const u8{ - "tmp.zig:4:9: error: expected type '[]i32', found '[10]i32'", + ctx.objErrStage1("non-printable invalid character", "\xff\xfe" ++ + "fn foo() bool {\r\n" ++ + " return true;\r\n" ++ + "}\r\n", &[_][]const u8{ + "tmp.zig:1:1: error: expected test, comptime, var decl, or container field, found 'invalid bytes'", + "tmp.zig:1:1: note: invalid byte: '\\xff'", }); - ctx.objErrStage1("ptrcast to non-pointer", - \\export fn entry(a: *i32) usize { - \\ return @ptrCast(usize, a); - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: expected pointer, found 'usize'", - }); - - ctx.objErrStage1("asm at compile time", - \\comptime { - \\ doSomeAsm(); - \\} - \\ - \\fn doSomeAsm() void { - \\ asm volatile ( - \\ \\.globl aoeu; - \\ \\.type aoeu, @function; - \\ \\.set aoeu, derp; - \\ ); - \\} - , &[_][]const u8{ - "tmp.zig:6:5: error: unable to evaluate constant expression", - }); - - ctx.objErrStage1("invalid member of builtin enum", - \\const builtin = @import("std").builtin; - \\export fn entry() void { - \\ const foo = builtin.Mode.x86; - \\ _ = foo; - \\} - , &[_][]const u8{ - "tmp.zig:3:29: error: container 'std.builtin.Mode' has no member called 'x86'", - }); - - ctx.objErrStage1("int to ptr of 0 bits", - \\export fn foo() void { - \\ var x: usize = 0x1000; - \\ var y: *void = @intToPtr(*void, x); - \\ _ = y; - \\} - , &[_][]const u8{ - "tmp.zig:3:30: error: type '*void' has 0 bits and cannot store information", - }); - - ctx.objErrStage1("@fieldParentPtr - non struct", - \\const Foo = i32; - \\export fn foo(a: *i32) *Foo { - \\ return @fieldParentPtr(Foo, "a", a); - \\} - , &[_][]const u8{ - "tmp.zig:3:28: error: expected struct type, found 'i32'", - }); - - ctx.objErrStage1("@fieldParentPtr - bad field name", - \\const Foo = extern struct { - \\ derp: i32, - \\}; - \\export fn foo(a: *i32) *Foo { - \\ return @fieldParentPtr(Foo, "a", a); - \\} - , &[_][]const u8{ - "tmp.zig:5:33: error: struct 'Foo' has no field 'a'", - }); - - ctx.objErrStage1("@fieldParentPtr - field pointer is not pointer", - \\const Foo = extern struct { - \\ a: i32, - \\}; - \\export fn foo(a: i32) *Foo { - \\ return @fieldParentPtr(Foo, "a", a); - \\} - , &[_][]const u8{ - "tmp.zig:5:38: error: expected pointer, found 'i32'", - }); - - ctx.objErrStage1("@fieldParentPtr - comptime field ptr not based on struct", - \\const Foo = struct { - \\ a: i32, - \\ b: i32, - \\}; - \\const foo = Foo { .a = 1, .b = 2, }; - \\ - \\comptime { - \\ const field_ptr = @intToPtr(*i32, 0x1234); - \\ const another_foo_ptr = @fieldParentPtr(Foo, "b", field_ptr); - \\ _ = another_foo_ptr; - \\} - , &[_][]const u8{ - "tmp.zig:9:55: error: pointer value not based on parent struct", - }); - - ctx.objErrStage1("@fieldParentPtr - comptime wrong field index", - \\const Foo = struct { - \\ a: i32, - \\ b: i32, - \\}; - \\const foo = Foo { .a = 1, .b = 2, }; - \\ - \\comptime { - \\ const another_foo_ptr = @fieldParentPtr(Foo, "b", &foo.a); - \\ _ = another_foo_ptr; - \\} - , &[_][]const u8{ - "tmp.zig:8:29: error: field 'b' has index 1 but pointer value is index 0 of struct 'Foo'", - }); - - ctx.objErrStage1("@offsetOf - non struct", - \\const Foo = i32; - \\export fn foo() usize { - \\ return @offsetOf(Foo, "a",); - \\} - , &[_][]const u8{ - "tmp.zig:3:22: error: expected struct type, found 'i32'", - }); - - ctx.objErrStage1("@offsetOf - bad field name", - \\const Foo = struct { - \\ derp: i32, - \\}; - \\export fn foo() usize { - \\ return @offsetOf(Foo, "a",); - \\} - , &[_][]const u8{ - "tmp.zig:5:27: error: struct 'Foo' has no field 'a'", - }); - - ctx.exeErrStage1("missing main fn in executable", - \\ - , &[_][]const u8{ - "error: root source file has no member called 'main'", - }); - - ctx.exeErrStage1("private main fn", - \\fn main() void {} - , &[_][]const u8{ - "error: 'main' is private", - "tmp.zig:1:1: note: declared here", - }); - - ctx.objErrStage1("setting a section on a local variable", - \\export fn entry() i32 { - \\ var foo: i32 linksection(".text2") = 1234; - \\ return foo; - \\} - , &[_][]const u8{ - "tmp.zig:2:30: error: cannot set section of local variable 'foo'", - }); - - ctx.objErrStage1("ambiguous decl reference", - \\fn foo() void {} - \\fn bar() void { - \\ const S = struct { - \\ fn baz() void { - \\ foo(); - \\ } - \\ fn foo() void {} - \\ }; - \\ S.baz(); - \\} - \\export fn entry() void { - \\ bar(); - \\} - , &[_][]const u8{ - "tmp.zig:5:13: error: ambiguous reference", - "tmp.zig:7:9: note: declared here", - "tmp.zig:1:1: note: also declared here", - }); - - ctx.objErrStage1("while expected bool, got optional", - \\export fn foo() void { - \\ while (bar()) {} - \\} - \\fn bar() ?i32 { return 1; } - , &[_][]const u8{ - "tmp.zig:2:15: error: expected type 'bool', found '?i32'", - }); - - ctx.objErrStage1("while expected bool, got error union", - \\export fn foo() void { - \\ while (bar()) {} - \\} - \\fn bar() anyerror!i32 { return 1; } - , &[_][]const u8{ - "tmp.zig:2:15: error: expected type 'bool', found 'anyerror!i32'", - }); - - ctx.objErrStage1("while expected optional, got bool", - \\export fn foo() void { - \\ while (bar()) |x| {_ = x;} - \\} - \\fn bar() bool { return true; } - , &[_][]const u8{ - "tmp.zig:2:15: error: expected optional type, found 'bool'", - }); - - ctx.objErrStage1("while expected optional, got error union", - \\export fn foo() void { - \\ while (bar()) |x| {_ = x;} - \\} - \\fn bar() anyerror!i32 { return 1; } - , &[_][]const u8{ - "tmp.zig:2:15: error: expected optional type, found 'anyerror!i32'", - }); - - ctx.objErrStage1("while expected error union, got bool", - \\export fn foo() void { - \\ while (bar()) |x| {_ = x;} else |err| {_ = err;} - \\} - \\fn bar() bool { return true; } - , &[_][]const u8{ - "tmp.zig:2:15: error: expected error union type, found 'bool'", - }); - - ctx.objErrStage1("while expected error union, got optional", - \\export fn foo() void { - \\ while (bar()) |x| {_ = x;} else |err| {_ = err;} - \\} - \\fn bar() ?i32 { return 1; } - , &[_][]const u8{ - "tmp.zig:2:15: error: expected error union type, found '?i32'", + ctx.objErrStage1("non-printable invalid character with escape alternative", "fn foo() bool {\n" ++ + "\treturn true;\n" ++ + "}\n", &[_][]const u8{ + "tmp.zig:2:1: error: invalid character: '\\t'", }); // TODO test this in stage2, but we won't even try in stage1 @@ -7182,1620 +404,6 @@ pub fn addCases(ctx: *TestContext) !void { // "tmp.zig:4:1: error: unable to inline function", //}); - ctx.objErrStage1("signed integer division", - \\export fn foo(a: i32, b: i32) i32 { - \\ return a / b; - \\} - , &[_][]const u8{ - "tmp.zig:2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact", - }); - - ctx.objErrStage1("signed integer remainder division", - \\export fn foo(a: i32, b: i32) i32 { - \\ return a % b; - \\} - , &[_][]const u8{ - "tmp.zig:2:14: error: remainder division with 'i32' and 'i32': signed integers and floats must use @rem or @mod", - }); - - ctx.objErrStage1("compile-time division by zero", - \\comptime { - \\ const a: i32 = 1; - \\ const b: i32 = 0; - \\ const c = a / b; - \\ _ = c; - \\} - , &[_][]const u8{ - "tmp.zig:4:17: error: division by zero", - }); - - ctx.objErrStage1("compile-time remainder division by zero", - \\comptime { - \\ const a: i32 = 1; - \\ const b: i32 = 0; - \\ const c = a % b; - \\ _ = c; - \\} - , &[_][]const u8{ - "tmp.zig:4:17: error: division by zero", - }); - - ctx.objErrStage1("@setRuntimeSafety twice for same scope", - \\export fn foo() void { - \\ @setRuntimeSafety(false); - \\ @setRuntimeSafety(false); - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: runtime safety set twice for same scope", - "tmp.zig:2:5: note: first set here", - }); - - ctx.objErrStage1("@setFloatMode twice for same scope", - \\export fn foo() void { - \\ @setFloatMode(@import("std").builtin.FloatMode.Optimized); - \\ @setFloatMode(@import("std").builtin.FloatMode.Optimized); - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: float mode set twice for same scope", - "tmp.zig:2:5: note: first set here", - }); - - ctx.objErrStage1("array access of type", - \\export fn foo() void { - \\ var b: u8[40] = undefined; - \\ _ = b; - \\} - , &[_][]const u8{ - "tmp.zig:2:14: error: array access of non-array type 'type'", - }); - - ctx.objErrStage1("cannot break out of defer expression", - \\export fn foo() void { - \\ while (true) { - \\ defer { - \\ break; - \\ } - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:4:13: error: cannot break out of defer expression", - }); - - ctx.objErrStage1("cannot continue out of defer expression", - \\export fn foo() void { - \\ while (true) { - \\ defer { - \\ continue; - \\ } - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:4:13: error: cannot continue out of defer expression", - }); - - ctx.objErrStage1("calling a generic function only known at runtime", - \\var foos = [_]fn(anytype) void { foo1, foo2 }; - \\ - \\fn foo1(arg: anytype) void {_ = arg;} - \\fn foo2(arg: anytype) void {_ = arg;} - \\ - \\pub fn main() !void { - \\ foos[0](true); - \\} - , &[_][]const u8{ - "tmp.zig:7:9: error: calling a generic function requires compile-time known function value", - }); - - ctx.objErrStage1("@compileError shows traceback of references that caused it", - \\const foo = @compileError("aoeu",); - \\ - \\const bar = baz + foo; - \\const baz = 1; - \\ - \\export fn entry() i32 { - \\ return bar; - \\} - , &[_][]const u8{ - "tmp.zig:1:13: error: aoeu", - }); - - ctx.objErrStage1("float literal too large error", - \\comptime { - \\ const a = 0x1.0p18495; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: float literal out of range of any type", - }); - - ctx.objErrStage1("float literal too small error (denormal)", - \\comptime { - \\ const a = 0x1.0p-19000; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: float literal out of range of any type", - }); - - ctx.objErrStage1("explicit cast float literal to integer when there is a fraction component", - \\export fn entry() i32 { - \\ return @as(i32, 12.34); - \\} - , &[_][]const u8{ - "tmp.zig:2:21: error: fractional component prevents float value 12.340000 from being casted to type 'i32'", - }); - - ctx.objErrStage1("non pointer given to @ptrToInt", - \\export fn entry(x: i32) usize { - \\ return @ptrToInt(x); - \\} - , &[_][]const u8{ - "tmp.zig:2:22: error: expected pointer, found 'i32'", - }); - - ctx.objErrStage1("@shlExact shifts out 1 bits", - \\comptime { - \\ const x = @shlExact(@as(u8, 0b01010101), 2); - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: operation caused overflow", - }); - - ctx.objErrStage1("@shrExact shifts out 1 bits", - \\comptime { - \\ const x = @shrExact(@as(u8, 0b10101010), 2); - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: exact shift shifted out 1 bits", - }); - - ctx.objErrStage1("shifting without int type or comptime known", - \\export fn entry(x: u8) u8 { - \\ return 0x11 << x; - \\} - , &[_][]const u8{ - "tmp.zig:2:17: error: LHS of shift must be a fixed-width integer type, or RHS must be compile-time known", - }); - - ctx.objErrStage1("shifting RHS is log2 of LHS int bit width", - \\export fn entry(x: u8, y: u8) u8 { - \\ return x << y; - \\} - , &[_][]const u8{ - "tmp.zig:2:17: error: expected type 'u3', found 'u8'", - }); - - ctx.objErrStage1("locally shadowing a primitive type", - \\export fn foo() void { - \\ const u8 = u16; - \\ const a: u8 = 300; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:2:11: error: name shadows primitive 'u8'", - "tmp.zig:2:11: note: consider using @\"u8\" to disambiguate", - }); - - ctx.objErrStage1("primitives take precedence over declarations", - \\const @"u8" = u16; - \\export fn entry() void { - \\ const a: u8 = 300; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:3:19: error: integer value 300 cannot be coerced to type 'u8'", - }); - - ctx.objErrStage1("declaration with same name as primitive must use special syntax", - \\const u8 = u16; - \\export fn entry() void { - \\ const a: u8 = 300; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:1:7: error: name shadows primitive 'u8'", - "tmp.zig:1:7: note: consider using @\"u8\" to disambiguate", - }); - - ctx.objErrStage1("implicitly increasing pointer alignment", - \\const Foo = packed struct { - \\ a: u8, - \\ b: u32, - \\}; - \\ - \\export fn entry() void { - \\ var foo = Foo { .a = 1, .b = 10 }; - \\ bar(&foo.b); - \\} - \\ - \\fn bar(x: *u32) void { - \\ x.* += 1; - \\} - , &[_][]const u8{ - "tmp.zig:8:13: error: expected type '*u32', found '*align(1) u32'", - }); - - ctx.objErrStage1("implicitly increasing slice alignment", - \\const Foo = packed struct { - \\ a: u8, - \\ b: u32, - \\}; - \\ - \\export fn entry() void { - \\ var foo = Foo { .a = 1, .b = 10 }; - \\ foo.b += 1; - \\ bar(@as(*[1]u32, &foo.b)[0..]); - \\} - \\ - \\fn bar(x: []u32) void { - \\ x[0] += 1; - \\} - , &[_][]const u8{ - "tmp.zig:9:26: error: cast increases pointer alignment", - "tmp.zig:9:26: note: '*align(1) u32' has alignment 1", - "tmp.zig:9:26: note: '*[1]u32' has alignment 4", - }); - - ctx.objErrStage1("increase pointer alignment in @ptrCast", - \\export fn entry() u32 { - \\ var bytes: [4]u8 = [_]u8{0x01, 0x02, 0x03, 0x04}; - \\ const ptr = @ptrCast(*u32, &bytes[0]); - \\ return ptr.*; - \\} - , &[_][]const u8{ - "tmp.zig:3:17: error: cast increases pointer alignment", - "tmp.zig:3:38: note: '*u8' has alignment 1", - "tmp.zig:3:26: note: '*u32' has alignment 4", - }); - - ctx.objErrStage1("@alignCast expects pointer or slice", - \\export fn entry() void { - \\ @alignCast(4, @as(u32, 3)); - \\} - , &[_][]const u8{ - "tmp.zig:2:19: error: expected pointer or slice, found 'u32'", - }); - - ctx.objErrStage1("passing an under-aligned function pointer", - \\export fn entry() void { - \\ testImplicitlyDecreaseFnAlign(alignedSmall, 1234); - \\} - \\fn testImplicitlyDecreaseFnAlign(ptr: fn () align(8) i32, answer: i32) void { - \\ if (ptr() != answer) unreachable; - \\} - \\fn alignedSmall() align(4) i32 { return 1234; } - , &[_][]const u8{ - "tmp.zig:2:35: error: expected type 'fn() align(8) i32', found 'fn() align(4) i32'", - }); - - ctx.objErrStage1("passing a not-aligned-enough pointer to cmpxchg", - \\const AtomicOrder = @import("std").builtin.AtomicOrder; - \\export fn entry() bool { - \\ var x: i32 align(1) = 1234; - \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {} - \\ return x == 5678; - \\} - , &[_][]const u8{ - "tmp.zig:4:32: error: expected type '*i32', found '*align(1) i32'", - }); - - ctx.objErrStage1("wrong size to an array literal", - \\comptime { - \\ const array = [2]u8{1, 2, 3}; - \\ _ = array; - \\} - , &[_][]const u8{ - "tmp.zig:2:31: error: index 2 outside array of size 2", - }); - - ctx.objErrStage1("wrong pointer coerced to pointer to opaque {}", - \\const Derp = opaque {}; - \\extern fn bar(d: *Derp) void; - \\export fn foo() void { - \\ var x = @as(u8, 1); - \\ bar(@ptrCast(*anyopaque, &x)); - \\} - , &[_][]const u8{ - "tmp.zig:5:9: error: expected type '*Derp', found '*anyopaque'", - }); - - ctx.objErrStage1("non-const variables of things that require const variables", - \\export fn entry1() void { - \\ var m2 = &2; - \\ _ = m2; - \\} - \\export fn entry2() void { - \\ var a = undefined; - \\ _ = a; - \\} - \\export fn entry3() void { - \\ var b = 1; - \\ _ = b; - \\} - \\export fn entry4() void { - \\ var c = 1.0; - \\ _ = c; - \\} - \\export fn entry5() void { - \\ var d = null; - \\ _ = d; - \\} - \\export fn entry6(opaque_: *Opaque) void { - \\ var e = opaque_.*; - \\ _ = e; - \\} - \\export fn entry7() void { - \\ var f = i32; - \\ _ = f; - \\} - \\export fn entry8() void { - \\ var h = (Foo {}).bar; - \\ _ = h; - \\} - \\const Opaque = opaque {}; - \\const Foo = struct { - \\ fn bar(self: *const Foo) void {_ = self;} - \\}; - , &[_][]const u8{ - "tmp.zig:2:4: error: variable of type '*const comptime_int' must be const or comptime", - "tmp.zig:6:4: error: variable of type '@Type(.Undefined)' must be const or comptime", - "tmp.zig:10:4: error: variable of type 'comptime_int' must be const or comptime", - "tmp.zig:10:4: note: to modify this variable at runtime, it must be given an explicit fixed-size number type", - "tmp.zig:14:4: error: variable of type 'comptime_float' must be const or comptime", - "tmp.zig:14:4: note: to modify this variable at runtime, it must be given an explicit fixed-size number type", - "tmp.zig:18:4: error: variable of type '@Type(.Null)' must be const or comptime", - "tmp.zig:22:4: error: variable of type 'Opaque' not allowed", - "tmp.zig:26:4: error: variable of type 'type' must be const or comptime", - "tmp.zig:30:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime", - }); - - ctx.objErrStage1("variable with type 'noreturn'", - \\export fn entry9() void { - \\ var z: noreturn = return; - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: unreachable code", - "tmp.zig:2:23: note: control flow is diverted here", - }); - - ctx.objErrStage1("wrong types given to atomic order args in cmpxchg", - \\export fn entry() void { - \\ var x: i32 = 1234; - \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, @as(u32, 1234), @as(u32, 1234))) {} - \\} - , &[_][]const u8{ - "tmp.zig:3:47: error: expected type 'std.builtin.AtomicOrder', found 'u32'", - }); - - ctx.objErrStage1("wrong types given to @export", - \\fn entry() callconv(.C) void { } - \\comptime { - \\ @export(entry, .{.name = "entry", .linkage = @as(u32, 1234) }); - \\} - , &[_][]const u8{ - "tmp.zig:3:59: error: expected type 'std.builtin.GlobalLinkage', found 'comptime_int'", - }); - - ctx.objErrStage1("struct with invalid field", - \\const std = @import("std",); - \\const Allocator = std.mem.Allocator; - \\const ArrayList = std.ArrayList; - \\ - \\const HeaderWeight = enum { - \\ H1, H2, H3, H4, H5, H6, - \\}; - \\ - \\const MdText = ArrayList(u8); - \\ - \\const MdNode = union(enum) { - \\ Header: struct { - \\ text: MdText, - \\ weight: HeaderValue, - \\ }, - \\}; - \\ - \\export fn entry() void { - \\ const a = MdNode.Header { - \\ .text = MdText.init(std.testing.allocator), - \\ .weight = HeaderWeight.H1, - \\ }; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:14:17: error: use of undeclared identifier 'HeaderValue'", - }); - - ctx.objErrStage1("@setAlignStack outside function", - \\comptime { - \\ @setAlignStack(16); - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: @setAlignStack outside function", - }); - - ctx.objErrStage1("@setAlignStack in naked function", - \\export fn entry() callconv(.Naked) void { - \\ @setAlignStack(16); - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: @setAlignStack in naked function", - }); - - ctx.objErrStage1("@setAlignStack in inline function", - \\export fn entry() void { - \\ foo(); - \\} - \\fn foo() callconv(.Inline) void { - \\ @setAlignStack(16); - \\} - , &[_][]const u8{ - "tmp.zig:5:5: error: @setAlignStack in inline function", - }); - - ctx.objErrStage1("@setAlignStack set twice", - \\export fn entry() void { - \\ @setAlignStack(16); - \\ @setAlignStack(16); - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: alignstack set twice", - "tmp.zig:2:5: note: first set here", - }); - - ctx.objErrStage1("@setAlignStack too big", - \\export fn entry() void { - \\ @setAlignStack(511 + 1); - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: attempt to @setAlignStack(512); maximum is 256", - }); - - ctx.objErrStage1("storing runtime value in compile time variable then using it", - \\const Mode = @import("std").builtin.Mode; - \\ - \\fn Free(comptime filename: []const u8) TestCase { - \\ return TestCase { - \\ .filename = filename, - \\ .problem_type = ProblemType.Free, - \\ }; - \\} - \\ - \\fn LibC(comptime filename: []const u8) TestCase { - \\ return TestCase { - \\ .filename = filename, - \\ .problem_type = ProblemType.LinkLibC, - \\ }; - \\} - \\ - \\const TestCase = struct { - \\ filename: []const u8, - \\ problem_type: ProblemType, - \\}; - \\ - \\const ProblemType = enum { - \\ Free, - \\ LinkLibC, - \\}; - \\ - \\export fn entry() void { - \\ const tests = [_]TestCase { - \\ Free("001"), - \\ Free("002"), - \\ LibC("078"), - \\ Free("116"), - \\ Free("117"), - \\ }; - \\ - \\ for ([_]Mode { Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast }) |mode| { - \\ _ = mode; - \\ inline for (tests) |test_case| { - \\ const foo = test_case.filename ++ ".zig"; - \\ _ = foo; - \\ } - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:38:29: error: cannot store runtime value in compile time variable", - }); - - ctx.objErrStage1("invalid legacy unicode escape", - \\export fn entry() void { - \\ const a = '\U1234'; - \\} - , &[_][]const u8{ - "tmp.zig:2:15: error: expected expression, found 'invalid bytes'", - "tmp.zig:2:18: note: invalid byte: '1'", - }); - - ctx.objErrStage1("invalid empty unicode escape", - \\export fn entry() void { - \\ const a = '\u{}'; - \\} - , &[_][]const u8{ - "tmp.zig:2:19: error: empty unicode escape sequence", - }); - - ctx.objErrStage1("non-printable invalid character", "\xff\xfe" ++ - "fn foo() bool {\r\n" ++ - " return true;\r\n" ++ - "}\r\n", &[_][]const u8{ - "tmp.zig:1:1: error: expected test, comptime, var decl, or container field, found 'invalid bytes'", - "tmp.zig:1:1: note: invalid byte: '\\xff'", - }); - - ctx.objErrStage1("non-printable invalid character with escape alternative", "fn foo() bool {\n" ++ - "\treturn true;\n" ++ - "}\n", &[_][]const u8{ - "tmp.zig:2:1: error: invalid character: '\\t'", - }); - - ctx.objErrStage1("calling var args extern function, passing array instead of pointer", - \\export fn entry() void { - \\ foo("hello".*,); - \\} - \\pub extern fn foo(format: *const u8, ...) void; - , &[_][]const u8{ - "tmp.zig:2:16: error: expected type '*const u8', found '[5:0]u8'", - }); - - ctx.objErrStage1("constant inside comptime function has compile error", - \\const ContextAllocator = MemoryPool(usize); - \\ - \\pub fn MemoryPool(comptime T: type) type { - \\ const free_list_t = @compileError("aoeu",); - \\ - \\ return struct { - \\ free_list: free_list_t, - \\ }; - \\} - \\ - \\export fn entry() void { - \\ var allocator: ContextAllocator = undefined; - \\} - , &[_][]const u8{ - "tmp.zig:4:5: error: unreachable code", - "tmp.zig:4:25: note: control flow is diverted here", - "tmp.zig:12:9: error: unused local variable", - }); - - ctx.objErrStage1("specify enum tag type that is too small", - \\const Small = enum (u2) { - \\ One, - \\ Two, - \\ Three, - \\ Four, - \\ Five, - \\}; - \\ - \\export fn entry() void { - \\ var x = Small.One; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:6:5: error: enumeration value 4 too large for type 'u2'", - }); - - ctx.objErrStage1("specify non-integer enum tag type", - \\const Small = enum (f32) { - \\ One, - \\ Two, - \\ Three, - \\}; - \\ - \\export fn entry() void { - \\ var x = Small.One; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:1:21: error: expected integer, found 'f32'", - }); - - ctx.objErrStage1("implicitly casting enum to tag type", - \\const Small = enum(u2) { - \\ One, - \\ Two, - \\ Three, - \\ Four, - \\}; - \\ - \\export fn entry() void { - \\ var x: u2 = Small.Two; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:9:22: error: expected type 'u2', found 'Small'", - }); - - ctx.objErrStage1("explicitly casting non tag type to enum", - \\const Small = enum(u2) { - \\ One, - \\ Two, - \\ Three, - \\ Four, - \\}; - \\ - \\export fn entry() void { - \\ var y = @as(f32, 3); - \\ var x = @intToEnum(Small, y); - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:10:31: error: expected integer type, found 'f32'", - }); - - ctx.objErrStage1("union fields with value assignments", - \\const MultipleChoice = union { - \\ A: i32 = 20, - \\}; - \\export fn entry() void { - \\ var x: MultipleChoice = undefined; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:1:24: error: explicitly valued tagged union missing integer tag type", - "tmp.zig:2:14: note: tag value specified here", - }); - - ctx.objErrStage1("enum with 0 fields", - \\const Foo = enum {}; - , &[_][]const u8{ - "tmp.zig:1:13: error: enum declarations must have at least one tag", - }); - - ctx.objErrStage1("union with 0 fields", - \\const Foo = union {}; - , &[_][]const u8{ - "tmp.zig:1:13: error: union declarations must have at least one tag", - }); - - ctx.objErrStage1("enum value already taken", - \\const MultipleChoice = enum(u32) { - \\ A = 20, - \\ B = 40, - \\ C = 60, - \\ D = 1000, - \\ E = 60, - \\}; - \\export fn entry() void { - \\ var x = MultipleChoice.C; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:6:5: error: enum tag value 60 already taken", - "tmp.zig:4:5: note: other occurrence here", - }); - - ctx.objErrStage1("union with specified enum omits field", - \\const Letter = enum { - \\ A, - \\ B, - \\ C, - \\}; - \\const Payload = union(Letter) { - \\ A: i32, - \\ B: f64, - \\}; - \\export fn entry() usize { - \\ return @sizeOf(Payload); - \\} - , &[_][]const u8{ - "tmp.zig:6:17: error: enum field missing: 'C'", - "tmp.zig:4:5: note: declared here", - }); - - ctx.objErrStage1("non-integer tag type to automatic union enum", - \\const Foo = union(enum(f32)) { - \\ A: i32, - \\}; - \\export fn entry() void { - \\ const x = @typeInfo(Foo).Union.tag_type.?; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:1:24: error: expected integer tag type, found 'f32'", - }); - - ctx.objErrStage1("non-enum tag type passed to union", - \\const Foo = union(u32) { - \\ A: i32, - \\}; - \\export fn entry() void { - \\ const x = @typeInfo(Foo).Union.tag_type.?; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:1:19: error: expected enum tag type, found 'u32'", - }); - - ctx.objErrStage1("union auto-enum value already taken", - \\const MultipleChoice = union(enum(u32)) { - \\ A = 20, - \\ B = 40, - \\ C = 60, - \\ D = 1000, - \\ E = 60, - \\}; - \\export fn entry() void { - \\ var x = MultipleChoice { .C = {} }; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:6:9: error: enum tag value 60 already taken", - "tmp.zig:4:9: note: other occurrence here", - }); - - ctx.objErrStage1("union enum field does not match enum", - \\const Letter = enum { - \\ A, - \\ B, - \\ C, - \\}; - \\const Payload = union(Letter) { - \\ A: i32, - \\ B: f64, - \\ C: bool, - \\ D: bool, - \\}; - \\export fn entry() void { - \\ var a = Payload {.A = 1234}; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:10:5: error: enum field not found: 'D'", - "tmp.zig:1:16: note: enum declared here", - }); - - ctx.objErrStage1("field type supplied in an enum", - \\const Letter = enum { - \\ A: void, - \\ B, - \\ C, - \\}; - , &[_][]const u8{ - "tmp.zig:2:8: error: enum fields do not have types", - "tmp.zig:1:16: note: consider 'union(enum)' here to make it a tagged union", - }); - - ctx.objErrStage1("struct field missing type", - \\const Letter = struct { - \\ A, - \\}; - \\export fn entry() void { - \\ var a = Letter { .A = {} }; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: struct field missing type", - }); - - ctx.objErrStage1("extern union field missing type", - \\const Letter = extern union { - \\ A, - \\}; - \\export fn entry() void { - \\ var a = Letter { .A = {} }; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: union field missing type", - }); - - ctx.objErrStage1("extern union given enum tag type", - \\const Letter = enum { - \\ A, - \\ B, - \\ C, - \\}; - \\const Payload = extern union(Letter) { - \\ A: i32, - \\ B: f64, - \\ C: bool, - \\}; - \\export fn entry() void { - \\ var a = Payload { .A = 1234 }; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:6:30: error: extern union does not support enum tag type", - }); - - ctx.objErrStage1("packed union given enum tag type", - \\const Letter = enum { - \\ A, - \\ B, - \\ C, - \\}; - \\const Payload = packed union(Letter) { - \\ A: i32, - \\ B: f64, - \\ C: bool, - \\}; - \\export fn entry() void { - \\ var a = Payload { .A = 1234 }; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:6:30: error: packed union does not support enum tag type", - }); - - ctx.objErrStage1("packed union with automatic layout field", - \\const Foo = struct { - \\ a: u32, - \\ b: f32, - \\}; - \\const Payload = packed union { - \\ A: Foo, - \\ B: bool, - \\}; - \\export fn entry() void { - \\ var a = Payload { .B = true }; - \\ _ = a; - \\} - , &[_][]const u8{ - "tmp.zig:6:5: error: non-packed, non-extern struct 'Foo' not allowed in packed union; no guaranteed in-memory representation", - }); - - ctx.objErrStage1("switch on union with no attached enum", - \\const Payload = union { - \\ A: i32, - \\ B: f64, - \\ C: bool, - \\}; - \\export fn entry() void { - \\ const a = Payload { .A = 1234 }; - \\ foo(a); - \\} - \\fn foo(a: *const Payload) void { - \\ switch (a.*) { - \\ Payload.A => {}, - \\ else => unreachable, - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:11:14: error: switch on union which has no attached enum", - "tmp.zig:1:17: note: consider 'union(enum)' here", - }); - - ctx.objErrStage1("enum in field count range but not matching tag", - \\const Foo = enum(u32) { - \\ A = 10, - \\ B = 11, - \\}; - \\export fn entry() void { - \\ var x = @intToEnum(Foo, 0); - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:6:13: error: enum 'Foo' has no tag matching integer value 0", - "tmp.zig:1:13: note: 'Foo' declared here", - }); - - ctx.objErrStage1("comptime cast enum to union but field has payload", - \\const Letter = enum { A, B, C }; - \\const Value = union(Letter) { - \\ A: i32, - \\ B, - \\ C, - \\}; - \\export fn entry() void { - \\ var x: Value = Letter.A; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:8:26: error: cast to union 'Value' must initialize 'i32' field 'A'", - "tmp.zig:3:5: note: field 'A' declared here", - }); - - ctx.objErrStage1("runtime cast to union which has non-void fields", - \\const Letter = enum { A, B, C }; - \\const Value = union(Letter) { - \\ A: i32, - \\ B, - \\ C, - \\}; - \\export fn entry() void { - \\ foo(Letter.A); - \\} - \\fn foo(l: Letter) void { - \\ var x: Value = l; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:11:20: error: runtime cast to union 'Value' which has non-void fields", - "tmp.zig:3:5: note: field 'A' has type 'i32'", - }); - - ctx.objErrStage1("taking byte offset of void field in struct", - \\const Empty = struct { - \\ val: void, - \\}; - \\export fn foo() void { - \\ const fieldOffset = @offsetOf(Empty, "val",); - \\ _ = fieldOffset; - \\} - , &[_][]const u8{ - "tmp.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset", - }); - - ctx.objErrStage1("taking bit offset of void field in struct", - \\const Empty = struct { - \\ val: void, - \\}; - \\export fn foo() void { - \\ const fieldOffset = @bitOffsetOf(Empty, "val",); - \\ _ = fieldOffset; - \\} - , &[_][]const u8{ - "tmp.zig:5:45: error: zero-bit field 'val' in struct 'Empty' has no offset", - }); - - ctx.objErrStage1("invalid union field access in comptime", - \\const Foo = union { - \\ Bar: u8, - \\ Baz: void, - \\}; - \\comptime { - \\ var foo = Foo {.Baz = {}}; - \\ const bar_val = foo.Bar; - \\ _ = bar_val; - \\} - , &[_][]const u8{ - "tmp.zig:7:24: error: accessing union field 'Bar' while field 'Baz' is set", - }); - - ctx.objErrStage1("unsupported modifier at start of asm output constraint", - \\export fn foo() void { - \\ var bar: u32 = 3; - \\ asm volatile ("" : [baz]"+r"(bar) : : ""); - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: invalid modifier starting output constraint for 'baz': '+', only '=' is supported. Compiler TODO: see https://github.com/ziglang/zig/issues/215", - }); - - ctx.objErrStage1("comptime_int in asm input", - \\export fn foo() void { - \\ asm volatile ("" : : [bar]"r"(3) : ""); - \\} - , &[_][]const u8{ - "tmp.zig:2:35: error: expected sized integer or sized float, found comptime_int", - }); - - ctx.objErrStage1("comptime_float in asm input", - \\export fn foo() void { - \\ asm volatile ("" : : [bar]"r"(3.17) : ""); - \\} - , &[_][]const u8{ - "tmp.zig:2:35: error: expected sized integer or sized float, found comptime_float", - }); - - ctx.objErrStage1("runtime assignment to comptime struct type", - \\const Foo = struct { - \\ Bar: u8, - \\ Baz: type, - \\}; - \\export fn f() void { - \\ var x: u8 = 0; - \\ const foo = Foo { .Bar = x, .Baz = u8 }; - \\ _ = foo; - \\} - , &[_][]const u8{ - "tmp.zig:7:23: error: unable to evaluate constant expression", - }); - - ctx.objErrStage1("runtime assignment to comptime union type", - \\const Foo = union { - \\ Bar: u8, - \\ Baz: type, - \\}; - \\export fn f() void { - \\ var x: u8 = 0; - \\ const foo = Foo { .Bar = x }; - \\ _ = foo; - \\} - , &[_][]const u8{ - "tmp.zig:7:23: error: unable to evaluate constant expression", - }); - - ctx.testErrStage1("@shuffle with selected index past first vector length", - \\export fn entry() void { - \\ const v: @import("std").meta.Vector(4, u32) = [4]u32{ 10, 11, 12, 13 }; - \\ const x: @import("std").meta.Vector(4, u32) = [4]u32{ 14, 15, 16, 17 }; - \\ var z = @shuffle(u32, v, x, [8]i32{ 0, 1, 2, 3, 7, 6, 5, 4 }); - \\ _ = z; - \\} - , &[_][]const u8{ - "tmp.zig:4:39: error: mask index '4' has out-of-bounds selection", - "tmp.zig:4:27: note: selected index '7' out of bounds of @Vector(4, u32)", - "tmp.zig:4:30: note: selections from the second vector are specified with negative numbers", - }); - - ctx.testErrStage1("nested vectors", - \\export fn entry() void { - \\ const V1 = @import("std").meta.Vector(4, u8); - \\ const V2 = @Type(.{ .Vector = .{ .len = 4, .child = V1 } }); - \\ var v: V2 = undefined; - \\ _ = v; - \\} - , &[_][]const u8{ - "tmp.zig:3:23: error: vector element type must be integer, float, bool, or pointer; '@Vector(4, u8)' is invalid", - }); - - ctx.testErrStage1("bad @splat type", - \\export fn entry() void { - \\ const c = 4; - \\ var v = @splat(4, c); - \\ _ = v; - \\} - , &[_][]const u8{ - "tmp.zig:3:23: error: vector element type must be integer, float, bool, or pointer; 'comptime_int' is invalid", - }); - - ctx.objErrStage1("compileLog of tagged enum doesn't crash the compiler", - \\const Bar = union(enum(u32)) { - \\ X: i32 = 1 - \\}; - \\ - \\fn testCompileLog(x: Bar) void { - \\ @compileLog(x); - \\} - \\ - \\pub fn main () void { - \\ comptime testCompileLog(Bar{.X = 123}); - \\} - , &[_][]const u8{ - "tmp.zig:6:5: error: found compile log statement", - }); - - ctx.objErrStage1("attempted implicit cast from *const T to *[1]T", - \\export fn entry(byte: u8) void { - \\ const w: i32 = 1234; - \\ var x: *const i32 = &w; - \\ var y: *[1]i32 = x; - \\ y[0] += 1; - \\ _ = byte; - \\} - , &[_][]const u8{ - "tmp.zig:4:22: error: expected type '*[1]i32', found '*const i32'", - "tmp.zig:4:22: note: cast discards const qualifier", - }); - - ctx.objErrStage1("attempted implicit cast from *const T to []T", - \\export fn entry() void { - \\ const u: u32 = 42; - \\ const x: []u32 = &u; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:3:23: error: expected type '[]u32', found '*const u32'", - }); - - ctx.objErrStage1("for loop body expression ignored", - \\fn returns() usize { - \\ return 2; - \\} - \\export fn f1() void { - \\ for ("hello") |_| returns(); - \\} - \\export fn f2() void { - \\ var x: anyerror!i32 = error.Bad; - \\ for ("hello") |_| returns() else unreachable; - \\ _ = x; - \\} - , &[_][]const u8{ - "tmp.zig:5:30: error: expression value is ignored", - "tmp.zig:9:30: error: expression value is ignored", - }); - - ctx.objErrStage1("aligned variable of zero-bit type", - \\export fn f() void { - \\ var s: struct {} align(4) = undefined; - \\ _ = s; - \\} - , &[_][]const u8{ - "tmp.zig:2:5: error: variable 's' of zero-bit type 'struct:2:12' has no in-memory representation, it cannot be aligned", - }); - - ctx.objErrStage1("function returning opaque type", - \\const FooType = opaque {}; - \\export fn bar() !FooType { - \\ return error.InvalidValue; - \\} - \\export fn bav() !@TypeOf(null) { - \\ return error.InvalidValue; - \\} - \\export fn baz() !@TypeOf(undefined) { - \\ return error.InvalidValue; - \\} - , &[_][]const u8{ - "tmp.zig:2:18: error: Opaque return type 'FooType' not allowed", - "tmp.zig:1:1: note: type declared here", - "tmp.zig:5:18: error: Null return type '@Type(.Null)' not allowed", - "tmp.zig:8:18: error: Undefined return type '@Type(.Undefined)' not allowed", - }); - - ctx.objErrStage1("generic function returning opaque type", - \\const FooType = opaque {}; - \\fn generic(comptime T: type) !T { - \\ return undefined; - \\} - \\export fn bar() void { - \\ _ = generic(FooType); - \\} - \\export fn bav() void { - \\ _ = generic(@TypeOf(null)); - \\} - \\export fn baz() void { - \\ _ = generic(@TypeOf(undefined)); - \\} - , &[_][]const u8{ - "tmp.zig:6:16: error: call to generic function with Opaque return type 'FooType' not allowed", - "tmp.zig:2:1: note: function declared here", - "tmp.zig:1:1: note: type declared here", - "tmp.zig:9:16: error: call to generic function with Null return type '@Type(.Null)' not allowed", - "tmp.zig:2:1: note: function declared here", - "tmp.zig:12:16: error: call to generic function with Undefined return type '@Type(.Undefined)' not allowed", - "tmp.zig:2:1: note: function declared here", - }); - - ctx.objErrStage1("function parameter is opaque", - \\const FooType = opaque {}; - \\export fn entry1() void { - \\ const someFuncPtr: fn (FooType) void = undefined; - \\ _ = someFuncPtr; - \\} - \\ - \\export fn entry2() void { - \\ const someFuncPtr: fn (@TypeOf(null)) void = undefined; - \\ _ = someFuncPtr; - \\} - \\ - \\fn foo(p: FooType) void {_ = p;} - \\export fn entry3() void { - \\ _ = foo; - \\} - \\ - \\fn bar(p: @TypeOf(null)) void {_ = p;} - \\export fn entry4() void { - \\ _ = bar; - \\} - , &[_][]const u8{ - "tmp.zig:3:28: error: parameter of opaque type 'FooType' not allowed", - "tmp.zig:8:28: error: parameter of type '@Type(.Null)' not allowed", - "tmp.zig:12:11: error: parameter of opaque type 'FooType' not allowed", - "tmp.zig:17:11: error: parameter of type '@Type(.Null)' not allowed", - }); - - ctx.objErrStage1( // fixed bug #2032 - "compile diagnostic string for top level decl type", - \\export fn entry() void { - \\ var foo: u32 = @This(){}; - \\ _ = foo; - \\} - , &[_][]const u8{ - "tmp.zig:2:27: error: type 'u32' does not support array initialization", - }); - - ctx.objErrStage1("issue #2687: coerce from undefined array pointer to slice", - \\export fn foo1() void { - \\ const a: *[1]u8 = undefined; - \\ var b: []u8 = a; - \\ _ = b; - \\} - \\export fn foo2() void { - \\ comptime { - \\ var a: *[1]u8 = undefined; - \\ var b: []u8 = a; - \\ _ = b; - \\ } - \\} - \\export fn foo3() void { - \\ comptime { - \\ const a: *[1]u8 = undefined; - \\ var b: []u8 = a; - \\ _ = b; - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:3:19: error: use of undefined value here causes undefined behavior", - "tmp.zig:9:23: error: use of undefined value here causes undefined behavior", - "tmp.zig:16:23: error: use of undefined value here causes undefined behavior", - }); - - ctx.objErrStage1("issue #3818: bitcast from parray/slice to u16", - \\export fn foo1() void { - \\ var bytes = [_]u8{1, 2}; - \\ const word: u16 = @bitCast(u16, bytes[0..]); - \\ _ = word; - \\} - \\export fn foo2() void { - \\ var bytes: []const u8 = &[_]u8{1, 2}; - \\ const word: u16 = @bitCast(u16, bytes); - \\ _ = word; - \\} - , &[_][]const u8{ - "tmp.zig:3:42: error: unable to @bitCast from pointer type '*[2]u8'", - "tmp.zig:8:32: error: destination type 'u16' has size 2 but source type '[]const u8' has size 16", - }); - - // issue #7810 - ctx.objErrStage1("comptime slice-len increment beyond bounds", - \\export fn foo_slice_len_increment_beyond_bounds() void { - \\ comptime { - \\ var buf_storage: [8]u8 = undefined; - \\ var buf: []const u8 = buf_storage[0..]; - \\ buf.len += 1; - \\ buf[8] = 42; - \\ } - \\} - , &[_][]const u8{ - ":6:12: error: out of bounds slice", - }); - - ctx.objErrStage1("comptime slice-sentinel is out of bounds (unterminated)", - \\export fn foo_array() void { - \\ comptime { - \\ var target = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ const slice = target[0..14 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_ptr_array() void { - \\ comptime { - \\ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target = &buf; - \\ const slice = target[0..14 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_vector_ConstPtrSpecialBaseArray() void { - \\ comptime { - \\ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*]u8 = &buf; - \\ const slice = target[0..14 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_vector_ConstPtrSpecialRef() void { - \\ comptime { - \\ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*]u8 = @ptrCast([*]u8, &buf); - \\ const slice = target[0..14 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_cvector_ConstPtrSpecialBaseArray() void { - \\ comptime { - \\ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*c]u8 = &buf; - \\ const slice = target[0..14 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_cvector_ConstPtrSpecialRef() void { - \\ comptime { - \\ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*c]u8 = @ptrCast([*c]u8, &buf); - \\ const slice = target[0..14 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_slice() void { - \\ comptime { - \\ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: []u8 = &buf; - \\ const slice = target[0..14 :0]; - \\ _ = slice; - \\ } - \\} - , &[_][]const u8{ - ":4:29: error: slice-sentinel is out of bounds", - ":12:29: error: slice-sentinel is out of bounds", - ":20:29: error: slice-sentinel is out of bounds", - ":28:29: error: slice-sentinel is out of bounds", - ":36:29: error: slice-sentinel is out of bounds", - ":44:29: error: slice-sentinel is out of bounds", - ":52:29: error: slice-sentinel is out of bounds", - }); - - ctx.objErrStage1("comptime slice-sentinel is out of bounds (terminated)", - \\export fn foo_array() void { - \\ comptime { - \\ var target = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ const slice = target[0..15 :1]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_ptr_array() void { - \\ comptime { - \\ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target = &buf; - \\ const slice = target[0..15 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_vector_ConstPtrSpecialBaseArray() void { - \\ comptime { - \\ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*]u8 = &buf; - \\ const slice = target[0..15 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_vector_ConstPtrSpecialRef() void { - \\ comptime { - \\ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*]u8 = @ptrCast([*]u8, &buf); - \\ const slice = target[0..15 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_cvector_ConstPtrSpecialBaseArray() void { - \\ comptime { - \\ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*c]u8 = &buf; - \\ const slice = target[0..15 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_cvector_ConstPtrSpecialRef() void { - \\ comptime { - \\ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*c]u8 = @ptrCast([*c]u8, &buf); - \\ const slice = target[0..15 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_slice() void { - \\ comptime { - \\ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: []u8 = &buf; - \\ const slice = target[0..15 :0]; - \\ _ = slice; - \\ } - \\} - , &[_][]const u8{ - ":4:29: error: out of bounds slice", - ":12:29: error: out of bounds slice", - ":20:29: error: out of bounds slice", - ":28:29: error: out of bounds slice", - ":36:29: error: out of bounds slice", - ":44:29: error: out of bounds slice", - ":52:29: error: out of bounds slice", - }); - - ctx.objErrStage1("comptime slice-sentinel does not match memory at target index (unterminated)", - \\export fn foo_array() void { - \\ comptime { - \\ var target = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ const slice = target[0..3 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_ptr_array() void { - \\ comptime { - \\ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target = &buf; - \\ const slice = target[0..3 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_vector_ConstPtrSpecialBaseArray() void { - \\ comptime { - \\ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*]u8 = &buf; - \\ const slice = target[0..3 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_vector_ConstPtrSpecialRef() void { - \\ comptime { - \\ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*]u8 = @ptrCast([*]u8, &buf); - \\ const slice = target[0..3 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_cvector_ConstPtrSpecialBaseArray() void { - \\ comptime { - \\ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*c]u8 = &buf; - \\ const slice = target[0..3 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_cvector_ConstPtrSpecialRef() void { - \\ comptime { - \\ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*c]u8 = @ptrCast([*c]u8, &buf); - \\ const slice = target[0..3 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_slice() void { - \\ comptime { - \\ var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: []u8 = &buf; - \\ const slice = target[0..3 :0]; - \\ _ = slice; - \\ } - \\} - , &[_][]const u8{ - ":4:29: error: slice-sentinel does not match memory at target index", - ":12:29: error: slice-sentinel does not match memory at target index", - ":20:29: error: slice-sentinel does not match memory at target index", - ":28:29: error: slice-sentinel does not match memory at target index", - ":36:29: error: slice-sentinel does not match memory at target index", - ":44:29: error: slice-sentinel does not match memory at target index", - ":52:29: error: slice-sentinel does not match memory at target index", - }); - - ctx.objErrStage1("comptime slice-sentinel does not match memory at target index (terminated)", - \\export fn foo_array() void { - \\ comptime { - \\ var target = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ const slice = target[0..3 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_ptr_array() void { - \\ comptime { - \\ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target = &buf; - \\ const slice = target[0..3 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_vector_ConstPtrSpecialBaseArray() void { - \\ comptime { - \\ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*]u8 = &buf; - \\ const slice = target[0..3 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_vector_ConstPtrSpecialRef() void { - \\ comptime { - \\ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*]u8 = @ptrCast([*]u8, &buf); - \\ const slice = target[0..3 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_cvector_ConstPtrSpecialBaseArray() void { - \\ comptime { - \\ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*c]u8 = &buf; - \\ const slice = target[0..3 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_cvector_ConstPtrSpecialRef() void { - \\ comptime { - \\ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*c]u8 = @ptrCast([*c]u8, &buf); - \\ const slice = target[0..3 :0]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_slice() void { - \\ comptime { - \\ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: []u8 = &buf; - \\ const slice = target[0..3 :0]; - \\ _ = slice; - \\ } - \\} - , &[_][]const u8{ - ":4:29: error: slice-sentinel does not match memory at target index", - ":12:29: error: slice-sentinel does not match memory at target index", - ":20:29: error: slice-sentinel does not match memory at target index", - ":28:29: error: slice-sentinel does not match memory at target index", - ":36:29: error: slice-sentinel does not match memory at target index", - ":44:29: error: slice-sentinel does not match memory at target index", - ":52:29: error: slice-sentinel does not match memory at target index", - }); - - ctx.objErrStage1("comptime slice-sentinel does not match target-sentinel", - \\export fn foo_array() void { - \\ comptime { - \\ var target = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ const slice = target[0..14 :255]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_ptr_array() void { - \\ comptime { - \\ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target = &buf; - \\ const slice = target[0..14 :255]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_vector_ConstPtrSpecialBaseArray() void { - \\ comptime { - \\ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*]u8 = &buf; - \\ const slice = target[0..14 :255]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_vector_ConstPtrSpecialRef() void { - \\ comptime { - \\ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*]u8 = @ptrCast([*]u8, &buf); - \\ const slice = target[0..14 :255]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_cvector_ConstPtrSpecialBaseArray() void { - \\ comptime { - \\ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*c]u8 = &buf; - \\ const slice = target[0..14 :255]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_cvector_ConstPtrSpecialRef() void { - \\ comptime { - \\ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: [*c]u8 = @ptrCast([*c]u8, &buf); - \\ const slice = target[0..14 :255]; - \\ _ = slice; - \\ } - \\} - \\export fn foo_slice() void { - \\ comptime { - \\ var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; - \\ var target: []u8 = &buf; - \\ const slice = target[0..14 :255]; - \\ _ = slice; - \\ } - \\} - , &[_][]const u8{ - ":4:29: error: slice-sentinel does not match target-sentinel", - ":12:29: error: slice-sentinel does not match target-sentinel", - ":20:29: error: slice-sentinel does not match target-sentinel", - ":28:29: error: slice-sentinel does not match target-sentinel", - ":36:29: error: slice-sentinel does not match target-sentinel", - ":44:29: error: slice-sentinel does not match target-sentinel", - ":52:29: error: slice-sentinel does not match target-sentinel", - }); - - ctx.objErrStage1("issue #4207: coerce from non-terminated-slice to terminated-pointer", - \\export fn foo() [*:0]const u8 { - \\ var buffer: [64]u8 = undefined; - \\ return buffer[0..]; - \\} - , &[_][]const u8{ - ":3:18: error: expected type '[*:0]const u8', found '*[64]u8'", - ":3:18: note: destination pointer requires a terminating '0' sentinel", - }); - - ctx.objErrStage1("issue #5221: invalid struct init type referenced by @typeInfo and passed into function", - \\fn ignore(comptime param: anytype) void {_ = param;} - \\ - \\export fn foo() void { - \\ const MyStruct = struct { - \\ wrong_type: []u8 = "foo", - \\ }; - \\ - \\ comptime ignore(@typeInfo(MyStruct).Struct.fields[0]); - \\} - , &[_][]const u8{ - ":5:28: error: cannot cast pointer to array literal to slice type '[]u8'", - ":5:28: note: cast discards const qualifier", - }); - - ctx.objErrStage1("integer underflow error", - \\export fn entry() void { - \\ _ = @intToPtr(*anyopaque, ~@as(usize, @import("std").math.maxInt(usize)) - 1); - \\} - , &[_][]const u8{ - ":2:78: error: operation caused overflow", - }); - { const case = ctx.obj("align(N) expr function pointers is a compile error", .{ .cpu_arch = .wasm32, @@ -8812,165 +420,4 @@ pub fn addCases(ctx: *TestContext) !void { "tmp.zig:1:23: error: align(N) expr is not allowed on function prototypes in wasm32/wasm64", }); } - - ctx.objErrStage1("compare optional to non-optional with invalid types", - \\export fn inconsistentChildType() void { - \\ var x: ?i32 = undefined; - \\ const y: comptime_int = 10; - \\ _ = (x == y); - \\} - \\ - \\export fn optionalToOptional() void { - \\ var x: ?i32 = undefined; - \\ var y: ?i32 = undefined; - \\ _ = (x == y); - \\} - \\ - \\export fn optionalVector() void { - \\ var x: ?@Vector(10, i32) = undefined; - \\ var y: @Vector(10, i32) = undefined; - \\ _ = (x == y); - \\} - \\ - \\export fn invalidChildType() void { - \\ var x: ?[3]i32 = undefined; - \\ var y: [3]i32 = undefined; - \\ _ = (x == y); - \\} - , &[_][]const u8{ - ":4:12: error: cannot compare types '?i32' and 'comptime_int'", - ":4:12: note: optional child type 'i32' must be the same as non-optional type 'comptime_int'", - ":10:12: error: cannot compare types '?i32' and '?i32'", - ":10:12: note: optional to optional comparison is only supported for optional pointer types", - ":16:12: error: TODO add comparison of optional vector", - ":22:12: error: cannot compare types '?[3]i32' and '[3]i32'", - ":22:12: note: operator not supported for type '[3]i32'", - }); - - ctx.objErrStage1("slice cannot have its bytes reinterpreted", - \\export fn foo() void { - \\ const bytes = [1]u8{ 0xfa } ** 16; - \\ var value = @ptrCast(*const []const u8, &bytes).*; - \\ _ = value; - \\} - , &[_][]const u8{ - ":3:52: error: slice '[]const u8' cannot have its bytes reinterpreted", - }); - - ctx.objErrStage1("wasmMemorySize is a compile error in non-Wasm targets", - \\export fn foo() void { - \\ _ = @wasmMemorySize(0); - \\ return; - \\} - , &[_][]const u8{ - "tmp.zig:2:9: error: @wasmMemorySize is a wasm32 feature only", - }); - - ctx.objErrStage1("wasmMemoryGrow is a compile error in non-Wasm targets", - \\export fn foo() void { - \\ _ = @wasmMemoryGrow(0, 1); - \\ return; - \\} - , &[_][]const u8{ - "tmp.zig:2:9: error: @wasmMemoryGrow is a wasm32 feature only", - }); - ctx.objErrStage1("Issue #5586: Make unary minus for unsigned types a compile error", - \\export fn f1(x: u32) u32 { - \\ const y = -%x; - \\ return -y; - \\} - \\const V = @import("std").meta.Vector; - \\export fn f2(x: V(4, u32)) V(4, u32) { - \\ const y = -%x; - \\ return -y; - \\} - , &[_][]const u8{ - "tmp.zig:3:12: error: negation of type 'u32'", - "tmp.zig:8:12: error: negation of type 'u32'", - }); - - ctx.objErrStage1("Issue #5618: coercion of ?*anyopaque to *anyopaque must fail.", - \\export fn foo() void { - \\ var u: ?*anyopaque = null; - \\ var v: *anyopaque = undefined; - \\ v = u; - \\} - , &[_][]const u8{ - "tmp.zig:4:9: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type '*anyopaque', found '?*anyopaque'", - }); - - ctx.objErrStage1("Issue #6823: don't allow .* to be followed by **", - \\fn foo() void { - \\ var sequence = "repeat".*** 10; - \\ _ = sequence; - \\} - , &[_][]const u8{ - // Ideally this would be column 30 but it's not very important. - "tmp.zig:2:28: error: '.*' cannot be followed by '*'. Are you missing a space?", - }); - - ctx.objErrStage1("Issue #9165: windows tcp server compilation error", - \\const std = @import("std"); - \\const builtin = @import("builtin"); - \\pub const io_mode = .evented; - \\pub fn main() !void { - \\ if (builtin.os.tag == .windows) { - \\ _ = try (std.net.StreamServer.init(.{})).accept(); - \\ } else { - \\ @compileError("Unsupported OS"); - \\ } - \\} - , &[_][]const u8{ - "error: Unsupported OS", - }); - - ctx.objErrStage1("attempt to close over comptime variable from outer scope", - \\fn SimpleList(comptime L: usize) type { - \\ var T = u8; - \\ return struct { - \\ array: [L]T, - \\ }; - \\} - , &[_][]const u8{ - "tmp.zig:4:19: error: mutable 'T' not accessible from here", - "tmp.zig:2:9: note: declared mutable here", - "tmp.zig:3:12: note: crosses namespace boundary here", - }); - - ctx.objErrStage1("saturating arithmetic does not allow floats", - \\export fn a() void { - \\ _ = @as(f32, 1.0) +| @as(f32, 1.0); - \\} - , &[_][]const u8{ - "error: invalid operands to binary expression: 'f32' and 'f32'", - }); - - ctx.objErrStage1("saturating shl does not allow negative rhs at comptime", - \\export fn a() void { - \\ _ = @as(i32, 1) <<| @as(i32, -2); - \\} - , &[_][]const u8{ - "error: shift by negative value -2", - }); - - ctx.objErrStage1("saturating shl assign does not allow negative rhs at comptime", - \\export fn a() void { - \\ comptime { - \\ var x = @as(i32, 1); - \\ x <<|= @as(i32, -2); - \\ } - \\} - , &[_][]const u8{ - "error: shift by negative value -2", - }); - - ctx.objErrStage1("undeclared identifier in unanalyzed branch", - \\export fn a() void { - \\ if (false) { - \\ lol_this_doesnt_exist = nonsense; - \\ } - \\} - , &[_][]const u8{ - "tmp.zig:3:9: error: use of undeclared identifier 'lol_this_doesnt_exist'", - }); } diff --git a/test/compile_errors/stage1/exe/main_missing_name.zig b/test/compile_errors/stage1/exe/main_missing_name.zig new file mode 100644 index 0000000000..c029665367 --- /dev/null +++ b/test/compile_errors/stage1/exe/main_missing_name.zig @@ -0,0 +1,5 @@ +pub fn (main) void {} + +// main missing name +// +// tmp.zig:1:5: error: missing function name diff --git a/test/compile_errors/stage1/exe/missing_main_fn_in_executable.zig b/test/compile_errors/stage1/exe/missing_main_fn_in_executable.zig new file mode 100644 index 0000000000..eb9ab469fd --- /dev/null +++ b/test/compile_errors/stage1/exe/missing_main_fn_in_executable.zig @@ -0,0 +1,5 @@ + + +// missing main fn in executable +// +// error: root source file has no member called 'main' diff --git a/test/compile_errors/stage1/exe/private_main_fn.zig b/test/compile_errors/stage1/exe/private_main_fn.zig new file mode 100644 index 0000000000..da617899a5 --- /dev/null +++ b/test/compile_errors/stage1/exe/private_main_fn.zig @@ -0,0 +1,6 @@ +fn main() void {} + +// private main fn +// +// error: 'main' is private +// tmp.zig:1:1: note: declared here diff --git a/test/compile_errors/stage1/exe/std.fmt_error_for_unused_arguments.zig b/test/compile_errors/stage1/exe/std.fmt_error_for_unused_arguments.zig new file mode 100644 index 0000000000..7da78d5b03 --- /dev/null +++ b/test/compile_errors/stage1/exe/std.fmt_error_for_unused_arguments.zig @@ -0,0 +1,7 @@ +pub fn main() !void { + @import("std").debug.print("{d} {d} {d} {d} {d}", .{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}); +} + +// std.fmt error for unused arguments +// +// ?:?:?: error: 10 unused arguments in '{d} {d} {d} {d} {d}' diff --git a/test/compile_errors/stage1/obj/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig b/test/compile_errors/stage1/obj/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig new file mode 100644 index 0000000000..24fd0d1b9f --- /dev/null +++ b/test/compile_errors/stage1/obj/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig @@ -0,0 +1,10 @@ +const Foo = struct {}; +export fn a() void { + const T = [*c]Foo; + var t: T = undefined; + _ = t; +} + +// C pointer pointing to non C ABI compatible type or has align attr +// +// tmp.zig:3:19: error: C pointers cannot point to non-C-ABI-compatible type 'Foo' diff --git a/test/compile_errors/stage1/obj/C_pointer_to_anyopaque.zig b/test/compile_errors/stage1/obj/C_pointer_to_anyopaque.zig new file mode 100644 index 0000000000..3012996145 --- /dev/null +++ b/test/compile_errors/stage1/obj/C_pointer_to_anyopaque.zig @@ -0,0 +1,9 @@ +export fn a() void { + var x: *anyopaque = undefined; + var y: [*c]anyopaque = x; + _ = y; +} + +// C pointer to anyopaque +// +// tmp.zig:3:16: error: C pointers cannot point to opaque types diff --git a/test/compile_errors/stage1/obj/Frame_of_generic_function.zig b/test/compile_errors/stage1/obj/Frame_of_generic_function.zig new file mode 100644 index 0000000000..b9b2280dd7 --- /dev/null +++ b/test/compile_errors/stage1/obj/Frame_of_generic_function.zig @@ -0,0 +1,12 @@ +export fn entry() void { + var frame: @Frame(func) = undefined; + _ = frame; +} +fn func(comptime T: type) void { + var x: T = undefined; + _ = x; +} + +// @Frame() of generic function +// +// tmp.zig:2:16: error: @Frame() of generic function diff --git a/test/compile_errors/stage1/obj/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig b/test/compile_errors/stage1/obj/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig new file mode 100644 index 0000000000..d01e68e4eb --- /dev/null +++ b/test/compile_errors/stage1/obj/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig @@ -0,0 +1,14 @@ +export fn f1(x: u32) u32 { + const y = -%x; + return -y; +} +const V = @import("std").meta.Vector; +export fn f2(x: V(4, u32)) V(4, u32) { + const y = -%x; + return -y; +} + +// Issue #5586: Make unary minus for unsigned types a compile error +// +// tmp.zig:3:12: error: negation of type 'u32' +// tmp.zig:8:12: error: negation of type 'u32' diff --git a/test/compile_errors/stage1/obj/Issue_6823_dont_allow_._to_be_followed_by_.zig b/test/compile_errors/stage1/obj/Issue_6823_dont_allow_._to_be_followed_by_.zig new file mode 100644 index 0000000000..c21df43362 --- /dev/null +++ b/test/compile_errors/stage1/obj/Issue_6823_dont_allow_._to_be_followed_by_.zig @@ -0,0 +1,8 @@ +fn foo() void { + var sequence = "repeat".*** 10; + _ = sequence; +} + +// Issue #6823: don't allow .* to be followed by ** +// +// tmp.zig:2:28: error: '.*' cannot be followed by '*'. Are you missing a space? diff --git a/test/compile_errors/stage1/obj/Issue_9165_windows_tcp_server_compilation_error.zig b/test/compile_errors/stage1/obj/Issue_9165_windows_tcp_server_compilation_error.zig new file mode 100644 index 0000000000..e3b9d4b009 --- /dev/null +++ b/test/compile_errors/stage1/obj/Issue_9165_windows_tcp_server_compilation_error.zig @@ -0,0 +1,14 @@ +const std = @import("std"); +const builtin = @import("builtin"); +pub const io_mode = .evented; +pub fn main() !void { + if (builtin.os.tag == .windows) { + _ = try (std.net.StreamServer.init(.{})).accept(); + } else { + @compileError("Unsupported OS"); + } +} + +// Issue #9165: windows tcp server compilation error +// +// error: Unsupported OS diff --git a/test/compile_errors/stage1/obj/access_non-existent_member_of_error_set.zig b/test/compile_errors/stage1/obj/access_non-existent_member_of_error_set.zig new file mode 100644 index 0000000000..42a54b08cd --- /dev/null +++ b/test/compile_errors/stage1/obj/access_non-existent_member_of_error_set.zig @@ -0,0 +1,9 @@ +const Foo = error{A}; +comptime { + const z = Foo.Bar; + _ = z; +} + +// access non-existent member of error set +// +// tmp.zig:3:18: error: no error named 'Bar' in 'Foo' diff --git a/test/compile_errors/stage1/obj/accessing_runtime_parameter_from_outer_function.zig b/test/compile_errors/stage1/obj/accessing_runtime_parameter_from_outer_function.zig new file mode 100644 index 0000000000..68b23c6a43 --- /dev/null +++ b/test/compile_errors/stage1/obj/accessing_runtime_parameter_from_outer_function.zig @@ -0,0 +1,19 @@ +fn outer(y: u32) fn (u32) u32 { + const st = struct { + fn get(z: u32) u32 { + return z + y; + } + }; + return st.get; +} +export fn entry() void { + var func = outer(10); + var x = func(3); + _ = x; +} + +// accessing runtime parameter from outer function +// +// tmp.zig:4:24: error: 'y' not accessible from inner function +// tmp.zig:3:28: note: crossed function definition here +// tmp.zig:1:10: note: declared here diff --git a/test/compile_errors/stage1/obj/add_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/add_assign_on_undefined_value.zig new file mode 100644 index 0000000000..0076cf1bab --- /dev/null +++ b/test/compile_errors/stage1/obj/add_assign_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + a += a; +} + +// add assign on undefined value +// +// tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/add_on_undefined_value.zig b/test/compile_errors/stage1/obj/add_on_undefined_value.zig new file mode 100644 index 0000000000..5f64c6999b --- /dev/null +++ b/test/compile_errors/stage1/obj/add_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + _ = a + a; +} + +// add on undefined value +// +// tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/add_overflow_in_function_evaluation.zig b/test/compile_errors/stage1/obj/add_overflow_in_function_evaluation.zig new file mode 100644 index 0000000000..b01e005a04 --- /dev/null +++ b/test/compile_errors/stage1/obj/add_overflow_in_function_evaluation.zig @@ -0,0 +1,10 @@ +const y = add(65530, 10); +fn add(a: u16, b: u16) u16 { + return a + b; +} + +export fn entry() usize { return @sizeOf(@TypeOf(y)); } + +// add overflow in function evaluation +// +// tmp.zig:3:14: error: operation caused overflow diff --git a/test/compile_errors/stage1/obj/add_wrap_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/add_wrap_assign_on_undefined_value.zig new file mode 100644 index 0000000000..37dbf9106f --- /dev/null +++ b/test/compile_errors/stage1/obj/add_wrap_assign_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + a +%= a; +} + +// add wrap assign on undefined value +// +// tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/add_wrap_on_undefined_value.zig b/test/compile_errors/stage1/obj/add_wrap_on_undefined_value.zig new file mode 100644 index 0000000000..3ba9bd11c6 --- /dev/null +++ b/test/compile_errors/stage1/obj/add_wrap_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + _ = a +% a; +} + +// add wrap on undefined value +// +// tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/addition_with_non_numbers.zig b/test/compile_errors/stage1/obj/addition_with_non_numbers.zig new file mode 100644 index 0000000000..296b9f4103 --- /dev/null +++ b/test/compile_errors/stage1/obj/addition_with_non_numbers.zig @@ -0,0 +1,10 @@ +const Foo = struct { + field: i32, +}; +const x = Foo {.field = 1} + Foo {.field = 2}; + +export fn entry() usize { return @sizeOf(@TypeOf(x)); } + +// addition with non numbers +// +// tmp.zig:4:28: error: invalid operands to binary expression: 'Foo' and 'Foo' diff --git a/test/compile_errors/stage1/obj/address_of_number_literal.zig b/test/compile_errors/stage1/obj/address_of_number_literal.zig new file mode 100644 index 0000000000..ee61f2180f --- /dev/null +++ b/test/compile_errors/stage1/obj/address_of_number_literal.zig @@ -0,0 +1,8 @@ +const x = 3; +const y = &x; +fn foo() *const i32 { return y; } +export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + +// address of number literal +// +// tmp.zig:3:30: error: expected type '*const i32', found '*const comptime_int' diff --git a/test/compile_errors/stage1/obj/alignCast_expects_pointer_or_slice.zig b/test/compile_errors/stage1/obj/alignCast_expects_pointer_or_slice.zig new file mode 100644 index 0000000000..91b269cef4 --- /dev/null +++ b/test/compile_errors/stage1/obj/alignCast_expects_pointer_or_slice.zig @@ -0,0 +1,7 @@ +export fn entry() void { + @alignCast(4, @as(u32, 3)); +} + +// @alignCast expects pointer or slice +// +// tmp.zig:2:19: error: expected pointer or slice, found 'u32' diff --git a/test/compile_errors/stage1/obj/aligned_variable_of_zero-bit_type.zig b/test/compile_errors/stage1/obj/aligned_variable_of_zero-bit_type.zig new file mode 100644 index 0000000000..90e690056d --- /dev/null +++ b/test/compile_errors/stage1/obj/aligned_variable_of_zero-bit_type.zig @@ -0,0 +1,8 @@ +export fn f() void { + var s: struct {} align(4) = undefined; + _ = s; +} + +// aligned variable of zero-bit type +// +// tmp.zig:2:5: error: variable 's' of zero-bit type 'struct:2:12' has no in-memory representation, it cannot be aligned diff --git a/test/compile_errors/stage1/obj/alignment_of_enum_field_specified.zig b/test/compile_errors/stage1/obj/alignment_of_enum_field_specified.zig new file mode 100644 index 0000000000..cf5cdfd213 --- /dev/null +++ b/test/compile_errors/stage1/obj/alignment_of_enum_field_specified.zig @@ -0,0 +1,12 @@ +const Number = enum { + a, + b align(i32), +}; +export fn entry1() void { + var x: Number = undefined; + _ = x; +} + +// alignment of enum field specified +// +// tmp.zig:3:7: error: expected ',' after field diff --git a/test/compile_errors/stage1/obj/ambiguous_decl_reference.zig b/test/compile_errors/stage1/obj/ambiguous_decl_reference.zig new file mode 100644 index 0000000000..421c41d2c2 --- /dev/null +++ b/test/compile_errors/stage1/obj/ambiguous_decl_reference.zig @@ -0,0 +1,19 @@ +fn foo() void {} +fn bar() void { + const S = struct { + fn baz() void { + foo(); + } + fn foo() void {} + }; + S.baz(); +} +export fn entry() void { + bar(); +} + +// ambiguous decl reference +// +// tmp.zig:5:13: error: ambiguous reference +// tmp.zig:7:9: note: declared here +// tmp.zig:1:1: note: also declared here diff --git a/test/compile_errors/stage1/obj/and_on_undefined_value.zig b/test/compile_errors/stage1/obj/and_on_undefined_value.zig new file mode 100644 index 0000000000..82363848f3 --- /dev/null +++ b/test/compile_errors/stage1/obj/and_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: bool = undefined; + _ = a and a; +} + +// and on undefined value +// +// tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/array_access_of_non_array.zig b/test/compile_errors/stage1/obj/array_access_of_non_array.zig new file mode 100644 index 0000000000..e4420debcd --- /dev/null +++ b/test/compile_errors/stage1/obj/array_access_of_non_array.zig @@ -0,0 +1,13 @@ +export fn f() void { + var bad : bool = undefined; + bad[0] = bad[0]; +} +export fn g() void { + var bad : bool = undefined; + _ = bad[0]; +} + +// array access of non array +// +// tmp.zig:3:8: error: array access of non-array type 'bool' +// tmp.zig:7:12: error: array access of non-array type 'bool' diff --git a/test/compile_errors/stage1/obj/array_access_of_type.zig b/test/compile_errors/stage1/obj/array_access_of_type.zig new file mode 100644 index 0000000000..82708eed6c --- /dev/null +++ b/test/compile_errors/stage1/obj/array_access_of_type.zig @@ -0,0 +1,8 @@ +export fn foo() void { + var b: u8[40] = undefined; + _ = b; +} + +// array access of type +// +// tmp.zig:2:14: error: array access of non-array type 'type' diff --git a/test/compile_errors/stage1/obj/array_access_of_undeclared_identifier.zig b/test/compile_errors/stage1/obj/array_access_of_undeclared_identifier.zig new file mode 100644 index 0000000000..193a7bf5de --- /dev/null +++ b/test/compile_errors/stage1/obj/array_access_of_undeclared_identifier.zig @@ -0,0 +1,7 @@ +export fn f() void { + i[i] = i[i]; +} + +// array access of undeclared identifier +// +// tmp.zig:2:5: error: use of undeclared identifier 'i' diff --git a/test/compile_errors/stage1/obj/array_access_with_non_integer_index.zig b/test/compile_errors/stage1/obj/array_access_with_non_integer_index.zig new file mode 100644 index 0000000000..1a3e17d896 --- /dev/null +++ b/test/compile_errors/stage1/obj/array_access_with_non_integer_index.zig @@ -0,0 +1,15 @@ +export fn f() void { + var array = "aoeu"; + var bad = false; + array[bad] = array[bad]; +} +export fn g() void { + var array = "aoeu"; + var bad = false; + _ = array[bad]; +} + +// array access with non integer index +// +// tmp.zig:4:11: error: expected type 'usize', found 'bool' +// tmp.zig:9:15: error: expected type 'usize', found 'bool' diff --git a/test/compile_errors/stage1/obj/array_concatenation_with_wrong_type.zig b/test/compile_errors/stage1/obj/array_concatenation_with_wrong_type.zig new file mode 100644 index 0000000000..91036e5f25 --- /dev/null +++ b/test/compile_errors/stage1/obj/array_concatenation_with_wrong_type.zig @@ -0,0 +1,9 @@ +const src = "aoeu"; +const derp: usize = 1234; +const a = derp ++ "foo"; + +export fn entry() usize { return @sizeOf(@TypeOf(a)); } + +// array concatenation with wrong type +// +// tmp.zig:3:11: error: expected array, found 'usize' diff --git a/test/compile_errors/stage1/obj/array_in_c_exported_function.zig b/test/compile_errors/stage1/obj/array_in_c_exported_function.zig new file mode 100644 index 0000000000..fe03d220b1 --- /dev/null +++ b/test/compile_errors/stage1/obj/array_in_c_exported_function.zig @@ -0,0 +1,12 @@ +export fn zig_array(x: [10]u8) void { + try std.testing.expect(std.mem.eql(u8, &x, "1234567890")); +} +const std = @import("std"); +export fn zig_return_array() [10]u8 { + return "1234567890".*; +} + +// array in c exported function +// +// tmp.zig:1:24: error: parameter of type '[10]u8' not allowed in function with calling convention 'C' +// tmp.zig:5:30: error: return type '[10]u8' not allowed in function with calling convention 'C' diff --git a/test/compile_errors/stage1/obj/asm_at_compile_time.zig b/test/compile_errors/stage1/obj/asm_at_compile_time.zig new file mode 100644 index 0000000000..e08b500c6e --- /dev/null +++ b/test/compile_errors/stage1/obj/asm_at_compile_time.zig @@ -0,0 +1,15 @@ +comptime { + doSomeAsm(); +} + +fn doSomeAsm() void { + asm volatile ( + \\.globl aoeu; + \\.type aoeu, @function; + \\.set aoeu, derp; + ); +} + +// asm at compile time +// +// tmp.zig:6:5: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/assign_inline_fn_to_non-comptime_var.zig b/test/compile_errors/stage1/obj/assign_inline_fn_to_non-comptime_var.zig new file mode 100644 index 0000000000..aa7bb91d1b --- /dev/null +++ b/test/compile_errors/stage1/obj/assign_inline_fn_to_non-comptime_var.zig @@ -0,0 +1,10 @@ +export fn entry() void { + var a = b; + _ = a; +} +fn b() callconv(.Inline) void { } + +// assign inline fn to non-comptime var +// +// tmp.zig:2:5: error: functions marked inline must be stored in const or comptime var +// tmp.zig:5:1: note: declared here diff --git a/test/compile_errors/stage1/obj/assign_null_to_non-optional_pointer.zig b/test/compile_errors/stage1/obj/assign_null_to_non-optional_pointer.zig new file mode 100644 index 0000000000..9c1aaf5031 --- /dev/null +++ b/test/compile_errors/stage1/obj/assign_null_to_non-optional_pointer.zig @@ -0,0 +1,7 @@ +const a: *u8 = null; + +export fn entry() usize { return @sizeOf(@TypeOf(a)); } + +// assign null to non-optional pointer +// +// tmp.zig:1:16: error: expected type '*u8', found '@Type(.Null)' diff --git a/test/compile_errors/stage1/obj/assign_through_constant_pointer.zig b/test/compile_errors/stage1/obj/assign_through_constant_pointer.zig new file mode 100644 index 0000000000..452e3ea617 --- /dev/null +++ b/test/compile_errors/stage1/obj/assign_through_constant_pointer.zig @@ -0,0 +1,8 @@ +export fn f() void { + var cstr = "Hat"; + cstr[0] = 'W'; +} + +// assign through constant pointer +// +// tmp.zig:3:13: error: cannot assign to constant diff --git a/test/compile_errors/stage1/obj/assign_through_constant_slice.zig b/test/compile_errors/stage1/obj/assign_through_constant_slice.zig new file mode 100644 index 0000000000..e36991897b --- /dev/null +++ b/test/compile_errors/stage1/obj/assign_through_constant_slice.zig @@ -0,0 +1,8 @@ +export fn f() void { + var cstr: []const u8 = "Hat"; + cstr[0] = 'W'; +} + +// assign through constant slice +// +// tmp.zig:3:13: error: cannot assign to constant diff --git a/test/compile_errors/stage1/obj/assign_to_constant_field.zig b/test/compile_errors/stage1/obj/assign_to_constant_field.zig new file mode 100644 index 0000000000..f371655fb5 --- /dev/null +++ b/test/compile_errors/stage1/obj/assign_to_constant_field.zig @@ -0,0 +1,11 @@ +const Foo = struct { + field: i32, +}; +export fn derp() void { + const f = Foo {.field = 1234,}; + f.field = 0; +} + +// assign to constant field +// +// tmp.zig:6:15: error: cannot assign to constant diff --git a/test/compile_errors/stage1/obj/assign_to_constant_variable.zig b/test/compile_errors/stage1/obj/assign_to_constant_variable.zig new file mode 100644 index 0000000000..1e8dd6c06f --- /dev/null +++ b/test/compile_errors/stage1/obj/assign_to_constant_variable.zig @@ -0,0 +1,8 @@ +export fn f() void { + const a = 3; + a = 4; +} + +// assign to constant variable +// +// tmp.zig:3:9: error: cannot assign to constant diff --git a/test/compile_errors/stage1/obj/assign_to_invalid_dereference.zig b/test/compile_errors/stage1/obj/assign_to_invalid_dereference.zig new file mode 100644 index 0000000000..6466f7afc8 --- /dev/null +++ b/test/compile_errors/stage1/obj/assign_to_invalid_dereference.zig @@ -0,0 +1,7 @@ +export fn entry() void { + 'a'.* = 1; +} + +// assign to invalid dereference +// +// tmp.zig:2:8: error: attempt to dereference non-pointer type 'comptime_int' diff --git a/test/compile_errors/stage1/obj/assign_too_big_number_to_u16.zig b/test/compile_errors/stage1/obj/assign_too_big_number_to_u16.zig new file mode 100644 index 0000000000..13b5280588 --- /dev/null +++ b/test/compile_errors/stage1/obj/assign_too_big_number_to_u16.zig @@ -0,0 +1,8 @@ +export fn foo() void { + var vga_mem: u16 = 0xB8000; + _ = vga_mem; +} + +// assign too big number to u16 +// +// tmp.zig:2:24: error: integer value 753664 cannot be coerced to type 'u16' diff --git a/test/compile_errors/stage1/obj/assign_unreachable.zig b/test/compile_errors/stage1/obj/assign_unreachable.zig new file mode 100644 index 0000000000..78e2305abc --- /dev/null +++ b/test/compile_errors/stage1/obj/assign_unreachable.zig @@ -0,0 +1,8 @@ +export fn f() void { + const a = return; +} + +// assign unreachable +// +// tmp.zig:2:5: error: unreachable code +// tmp.zig:2:15: note: control flow is diverted here diff --git a/test/compile_errors/stage1/obj/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig b/test/compile_errors/stage1/obj/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig new file mode 100644 index 0000000000..f93650821b --- /dev/null +++ b/test/compile_errors/stage1/obj/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig @@ -0,0 +1,19 @@ +fn maybe(is: bool) ?u8 { + if (is) return @as(u8, 10) else return null; +} +const U = union { + Ye: u8, +}; +const S = struct { + num: u8, +}; +export fn entry() void { + var u = U{ .Ye = maybe(false) }; + var s = S{ .num = maybe(false) }; + _ = u; + _ = s; +} + +// assigning to struct or union fields that are not optionals with a function that returns an optional +// +// tmp.zig:11:27: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type 'u8', found '?u8' diff --git a/test/compile_errors/stage1/obj/async_function_depends_on_its_own_frame.zig b/test/compile_errors/stage1/obj/async_function_depends_on_its_own_frame.zig new file mode 100644 index 0000000000..0d1cd7f77e --- /dev/null +++ b/test/compile_errors/stage1/obj/async_function_depends_on_its_own_frame.zig @@ -0,0 +1,11 @@ +export fn entry() void { + _ = async amain(); +} +fn amain() callconv(.Async) void { + var x: [@sizeOf(@Frame(amain))]u8 = undefined; + _ = x; +} + +// async function depends on its own frame +// +// tmp.zig:4:1: error: cannot resolve '@Frame(amain)': function not fully analyzed yet diff --git a/test/compile_errors/stage1/obj/async_function_indirectly_depends_on_its_own_frame.zig b/test/compile_errors/stage1/obj/async_function_indirectly_depends_on_its_own_frame.zig new file mode 100644 index 0000000000..809409f524 --- /dev/null +++ b/test/compile_errors/stage1/obj/async_function_indirectly_depends_on_its_own_frame.zig @@ -0,0 +1,15 @@ +export fn entry() void { + _ = async amain(); +} +fn amain() callconv(.Async) void { + other(); +} +fn other() void { + var x: [@sizeOf(@Frame(amain))]u8 = undefined; + _ = x; +} + +// async function indirectly depends on its own frame +// +// tmp.zig:4:1: error: unable to determine async function frame of 'amain' +// tmp.zig:5:10: note: analysis of function 'other' depends on the frame diff --git a/test/compile_errors/stage1/obj/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig b/test/compile_errors/stage1/obj/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig new file mode 100644 index 0000000000..7d97c2813e --- /dev/null +++ b/test/compile_errors/stage1/obj/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig @@ -0,0 +1,8 @@ +export fn entry() void { + var x: u32 = 0; + @atomicStore(u32, &x, 1, .Acquire); +} + +// atomic orderings of atomicStore Acquire or AcqRel +// +// tmp.zig:3:30: error: @atomicStore atomic ordering must not be Acquire or AcqRel diff --git a/test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig b/test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig new file mode 100644 index 0000000000..d6d9ab478b --- /dev/null +++ b/test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig @@ -0,0 +1,9 @@ +const AtomicOrder = @import("std").builtin.AtomicOrder; +export fn f() void { + var x: i32 = 1234; + while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {} +} + +// atomic orderings of cmpxchg - failure stricter than success +// +// tmp.zig:4:81: error: failure atomic ordering must be no stricter than success diff --git a/test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig b/test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig new file mode 100644 index 0000000000..770c08f3fe --- /dev/null +++ b/test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig @@ -0,0 +1,9 @@ +const AtomicOrder = @import("std").builtin.AtomicOrder; +export fn f() void { + var x: i32 = 1234; + while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {} +} + +// atomic orderings of cmpxchg - success Monotonic or stricter +// +// tmp.zig:4:58: error: success atomic ordering must be Monotonic or stricter diff --git a/test/compile_errors/stage1/obj/atomic_orderings_of_fence_Acquire_or_stricter.zig b/test/compile_errors/stage1/obj/atomic_orderings_of_fence_Acquire_or_stricter.zig new file mode 100644 index 0000000000..34ee200eeb --- /dev/null +++ b/test/compile_errors/stage1/obj/atomic_orderings_of_fence_Acquire_or_stricter.zig @@ -0,0 +1,7 @@ +export fn entry() void { + @fence(.Monotonic); +} + +// atomic orderings of fence Acquire or stricter +// +// tmp.zig:2:12: error: atomic ordering must be Acquire or stricter diff --git a/test/compile_errors/stage1/obj/atomicrmw_with_bool_op_not_.Xchg.zig b/test/compile_errors/stage1/obj/atomicrmw_with_bool_op_not_.Xchg.zig new file mode 100644 index 0000000000..03309737cf --- /dev/null +++ b/test/compile_errors/stage1/obj/atomicrmw_with_bool_op_not_.Xchg.zig @@ -0,0 +1,8 @@ +export fn entry() void { + var x = false; + _ = @atomicRmw(bool, &x, .Add, true, .SeqCst); +} + +// atomicrmw with bool op not .Xchg +// +// tmp.zig:3:30: error: @atomicRmw with bool only allowed with .Xchg diff --git a/test/compile_errors/stage1/obj/atomicrmw_with_enum_op_not_.Xchg.zig b/test/compile_errors/stage1/obj/atomicrmw_with_enum_op_not_.Xchg.zig new file mode 100644 index 0000000000..95e6a7d95d --- /dev/null +++ b/test/compile_errors/stage1/obj/atomicrmw_with_enum_op_not_.Xchg.zig @@ -0,0 +1,14 @@ +export fn entry() void { + const E = enum(u8) { + a, + b, + c, + d, + }; + var x: E = .a; + _ = @atomicRmw(E, &x, .Add, .b, .SeqCst); +} + +// atomicrmw with enum op not .Xchg +// +// tmp.zig:9:27: error: @atomicRmw with enum only allowed with .Xchg diff --git a/test/compile_errors/stage1/obj/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig b/test/compile_errors/stage1/obj/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig new file mode 100644 index 0000000000..a7359dbf32 --- /dev/null +++ b/test/compile_errors/stage1/obj/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig @@ -0,0 +1,8 @@ +export fn entry() void { + var x: f32 = 0; + _ = @atomicRmw(f32, &x, .And, 2, .SeqCst); +} + +// atomicrmw with float op not .Xchg, .Add or .Sub +// +// tmp.zig:3:29: error: @atomicRmw with float only allowed with .Xchg, .Add and .Sub diff --git a/test/compile_errors/stage1/obj/attempt_to_cast_enum_literal_to_error.zig b/test/compile_errors/stage1/obj/attempt_to_cast_enum_literal_to_error.zig new file mode 100644 index 0000000000..fd120af3eb --- /dev/null +++ b/test/compile_errors/stage1/obj/attempt_to_cast_enum_literal_to_error.zig @@ -0,0 +1,9 @@ +export fn entry() void { + switch (error.Hi) { + .Hi => {}, + } +} + +// attempt to cast enum literal to error +// +// tmp.zig:3:9: error: expected type 'error{Hi}', found '@Type(.EnumLiteral)' diff --git a/test/compile_errors/stage1/obj/attempt_to_close_over_comptime_variable_from_outer_scope.zig b/test/compile_errors/stage1/obj/attempt_to_close_over_comptime_variable_from_outer_scope.zig new file mode 100644 index 0000000000..5eb5db3dc8 --- /dev/null +++ b/test/compile_errors/stage1/obj/attempt_to_close_over_comptime_variable_from_outer_scope.zig @@ -0,0 +1,12 @@ +fn SimpleList(comptime L: usize) type { + var T = u8; + return struct { + array: [L]T, + }; +} + +// attempt to close over comptime variable from outer scope +// +// tmp.zig:4:19: error: mutable 'T' not accessible from here +// tmp.zig:2:9: note: declared mutable here +// tmp.zig:3:12: note: crosses namespace boundary here diff --git a/test/compile_errors/stage1/obj/attempt_to_create_17_bit_float_type.zig b/test/compile_errors/stage1/obj/attempt_to_create_17_bit_float_type.zig new file mode 100644 index 0000000000..bdea578696 --- /dev/null +++ b/test/compile_errors/stage1/obj/attempt_to_create_17_bit_float_type.zig @@ -0,0 +1,8 @@ +const builtin = @import("std").builtin; +comptime { + _ = @Type(.{ .Float = .{ .bits = 17 } }); +} + +// attempt to create 17 bit float type +// +// tmp.zig:3:16: error: 17-bit float unsupported diff --git a/test/compile_errors/stage1/obj/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig b/test/compile_errors/stage1/obj/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig new file mode 100644 index 0000000000..00ee26033d --- /dev/null +++ b/test/compile_errors/stage1/obj/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig @@ -0,0 +1,12 @@ +fn foo() anyerror!u32 { + return 1; +} + +export fn entry() void { + const x = -foo(); + _ = x; +} + +// attempt to negate a non-integer, non-float or non-vector type +// +// tmp.zig:6:15: error: negation of type 'anyerror!u32' diff --git a/test/compile_errors/stage1/obj/attempt_to_use_0_bit_type_in_extern_fn.zig b/test/compile_errors/stage1/obj/attempt_to_use_0_bit_type_in_extern_fn.zig new file mode 100644 index 0000000000..d7767e25e9 --- /dev/null +++ b/test/compile_errors/stage1/obj/attempt_to_use_0_bit_type_in_extern_fn.zig @@ -0,0 +1,15 @@ +extern fn foo(ptr: fn(*void) callconv(.C) void) void; + +export fn entry() void { + foo(bar); +} + +fn bar(x: *void) callconv(.C) void { _ = x; } +export fn entry2() void { + bar(&{}); +} + +// attempt to use 0 bit type in extern fn +// +// tmp.zig:1:23: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'C' +// tmp.zig:7:11: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'C' diff --git a/test/compile_errors/stage1/obj/attempted_double_ampersand.zig b/test/compile_errors/stage1/obj/attempted_double_ampersand.zig new file mode 100644 index 0000000000..cc92bc2fae --- /dev/null +++ b/test/compile_errors/stage1/obj/attempted_double_ampersand.zig @@ -0,0 +1,10 @@ +export fn entry(a: bool, b: bool) i32 { + if (a && b) { + return 1234; + } + return 5678; +} + +// attempted `&&` +// +// tmp.zig:2:11: error: ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND diff --git a/test/compile_errors/stage1/obj/attempted_double_pipe_on_boolean_values.zig b/test/compile_errors/stage1/obj/attempted_double_pipe_on_boolean_values.zig new file mode 100644 index 0000000000..8e67b4950d --- /dev/null +++ b/test/compile_errors/stage1/obj/attempted_double_pipe_on_boolean_values.zig @@ -0,0 +1,11 @@ +export fn entry(a: bool, b: bool) i32 { + if (a || b) { + return 1234; + } + return 5678; +} + +// attempted `||` on boolean values +// +// tmp.zig:2:9: error: expected error set type, found 'bool' +// tmp.zig:2:11: note: `||` merges error sets; `or` performs boolean OR diff --git a/test/compile_errors/stage1/obj/attempted_implicit_cast_from_T_to_slice_const_T.zig b/test/compile_errors/stage1/obj/attempted_implicit_cast_from_T_to_slice_const_T.zig new file mode 100644 index 0000000000..4776104928 --- /dev/null +++ b/test/compile_errors/stage1/obj/attempted_implicit_cast_from_T_to_slice_const_T.zig @@ -0,0 +1,8 @@ +export fn entry() void { + const x: [*]const bool = true; + _ = x; +} + +// attempted implicit cast from T to [*]const T +// +// tmp.zig:2:30: error: expected type '[*]const bool', found 'bool' diff --git a/test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_array_len_1_T.zig b/test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_array_len_1_T.zig new file mode 100644 index 0000000000..d389c572ca --- /dev/null +++ b/test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_array_len_1_T.zig @@ -0,0 +1,12 @@ +export fn entry(byte: u8) void { + const w: i32 = 1234; + var x: *const i32 = &w; + var y: *[1]i32 = x; + y[0] += 1; + _ = byte; +} + +// attempted implicit cast from *const T to *[1]T +// +// tmp.zig:4:22: error: expected type '*[1]i32', found '*const i32' +// tmp.zig:4:22: note: cast discards const qualifier diff --git a/test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_sliceT.zig b/test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_sliceT.zig new file mode 100644 index 0000000000..70d1cbece2 --- /dev/null +++ b/test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_sliceT.zig @@ -0,0 +1,9 @@ +export fn entry() void { + const u: u32 = 42; + const x: []u32 = &u; + _ = x; +} + +// attempted implicit cast from *const T to []T +// +// tmp.zig:3:23: error: expected type '[]u32', found '*const u32' diff --git a/test/compile_errors/stage1/obj/bad_alignCast_at_comptime.zig b/test/compile_errors/stage1/obj/bad_alignCast_at_comptime.zig new file mode 100644 index 0000000000..7396dfc4dd --- /dev/null +++ b/test/compile_errors/stage1/obj/bad_alignCast_at_comptime.zig @@ -0,0 +1,9 @@ +comptime { + const ptr = @intToPtr(*align(1) i32, 0x1); + const aligned = @alignCast(4, ptr); + _ = aligned; +} + +// bad @alignCast at comptime +// +// tmp.zig:3:35: error: pointer address 0x1 is not aligned to 4 bytes diff --git a/test/compile_errors/stage1/obj/bad_alignment_in_implicit_cast_from_array_pointer_to_slice.zig b/test/compile_errors/stage1/obj/bad_alignment_in_implicit_cast_from_array_pointer_to_slice.zig new file mode 100644 index 0000000000..ecfc5523d0 --- /dev/null +++ b/test/compile_errors/stage1/obj/bad_alignment_in_implicit_cast_from_array_pointer_to_slice.zig @@ -0,0 +1,9 @@ +export fn a() void { + var x: [10]u8 = undefined; + var y: []align(16) u8 = &x; + _ = y; +} + +// bad alignment in implicit cast from array pointer to slice +// +// tmp.zig:3:30: error: expected type '[]align(16) u8', found '*[10]u8' diff --git a/test/compile_errors/stage1/obj/bad_alignment_type.zig b/test/compile_errors/stage1/obj/bad_alignment_type.zig new file mode 100644 index 0000000000..902ffc79d6 --- /dev/null +++ b/test/compile_errors/stage1/obj/bad_alignment_type.zig @@ -0,0 +1,13 @@ +export fn entry1() void { + var x: []align(true) i32 = undefined; + _ = x; +} +export fn entry2() void { + var x: *align(@as(f64, 12.34)) i32 = undefined; + _ = x; +} + +// bad alignment type +// +// tmp.zig:2:20: error: expected type 'u29', found 'bool' +// tmp.zig:6:19: error: fractional component prevents float value 12.340000 from being casted to type 'u29' diff --git a/test/compile_errors/stage1/obj/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig b/test/compile_errors/stage1/obj/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig new file mode 100644 index 0000000000..f32301f9ff --- /dev/null +++ b/test/compile_errors/stage1/obj/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig @@ -0,0 +1,15 @@ +export fn entry() void { + const BlockKind = u32; + + const Block = struct { + kind: BlockKind, + }; + + bogus; + + _ = Block; +} + +// bad identifier in function with struct defined inside function which references local const +// +// tmp.zig:8:5: error: use of undeclared identifier 'bogus' diff --git a/test/compile_errors/stage1/obj/bad_import.zig b/test/compile_errors/stage1/obj/bad_import.zig new file mode 100644 index 0000000000..f6e93559b2 --- /dev/null +++ b/test/compile_errors/stage1/obj/bad_import.zig @@ -0,0 +1,5 @@ +const bogus = @import("bogus-does-not-exist.zig",); + +// bad import +// +// tmp.zig:1:23: error: unable to load '${DIR}bogus-does-not-exist.zig': FileNotFound diff --git a/test/compile_errors/stage1/obj/bad_usage_of_call.zig b/test/compile_errors/stage1/obj/bad_usage_of_call.zig new file mode 100644 index 0000000000..928c39b5a8 --- /dev/null +++ b/test/compile_errors/stage1/obj/bad_usage_of_call.zig @@ -0,0 +1,28 @@ +export fn entry1() void { + @call(.{}, foo, {}); +} +export fn entry2() void { + comptime @call(.{ .modifier = .never_inline }, foo, .{}); +} +export fn entry3() void { + comptime @call(.{ .modifier = .never_tail }, foo, .{}); +} +export fn entry4() void { + @call(.{ .modifier = .never_inline }, bar, .{}); +} +export fn entry5(c: bool) void { + var baz = if (c) baz1 else baz2; + @call(.{ .modifier = .compile_time }, baz, .{}); +} +fn foo() void {} +fn bar() callconv(.Inline) void {} +fn baz1() void {} +fn baz2() void {} + +// bad usage of @call +// +// tmp.zig:2:21: error: expected tuple or struct, found 'void' +// tmp.zig:5:14: error: unable to perform 'never_inline' call at compile-time +// tmp.zig:8:14: error: unable to perform 'never_tail' call at compile-time +// tmp.zig:11:5: error: no-inline call of inline function +// tmp.zig:15:5: error: the specified modifier requires a comptime-known function diff --git a/test/compile_errors/stage1/obj/bin_and_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/bin_and_assign_on_undefined_value.zig new file mode 100644 index 0000000000..d9dbdea741 --- /dev/null +++ b/test/compile_errors/stage1/obj/bin_and_assign_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + a &= a; +} + +// bin and assign on undefined value +// +// tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/bin_and_on_undefined_value.zig b/test/compile_errors/stage1/obj/bin_and_on_undefined_value.zig new file mode 100644 index 0000000000..6775cd56ea --- /dev/null +++ b/test/compile_errors/stage1/obj/bin_and_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + _ = a & a; +} + +// bin and on undefined value +// +// tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/bin_not_on_undefined_value.zig b/test/compile_errors/stage1/obj/bin_not_on_undefined_value.zig new file mode 100644 index 0000000000..0e543c4f00 --- /dev/null +++ b/test/compile_errors/stage1/obj/bin_not_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + _ = ~a; +} + +// bin not on undefined value +// +// tmp.zig:3:10: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/bin_or_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/bin_or_assign_on_undefined_value.zig new file mode 100644 index 0000000000..bfbeb7ce3e --- /dev/null +++ b/test/compile_errors/stage1/obj/bin_or_assign_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + a |= a; +} + +// bin or assign on undefined value +// +// tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/bin_or_on_undefined_value.zig b/test/compile_errors/stage1/obj/bin_or_on_undefined_value.zig new file mode 100644 index 0000000000..7a62ace9dd --- /dev/null +++ b/test/compile_errors/stage1/obj/bin_or_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + _ = a | a; +} + +// bin or on undefined value +// +// tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/bin_xor_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/bin_xor_assign_on_undefined_value.zig new file mode 100644 index 0000000000..bea5e1e089 --- /dev/null +++ b/test/compile_errors/stage1/obj/bin_xor_assign_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + a ^= a; +} + +// bin xor assign on undefined value +// +// tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/bin_xor_on_undefined_value.zig b/test/compile_errors/stage1/obj/bin_xor_on_undefined_value.zig new file mode 100644 index 0000000000..f6ddb24521 --- /dev/null +++ b/test/compile_errors/stage1/obj/bin_xor_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + _ = a ^ a; +} + +// bin xor on undefined value +// +// tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/binary_not_on_number_literal.zig b/test/compile_errors/stage1/obj/binary_not_on_number_literal.zig new file mode 100644 index 0000000000..12f5953f16 --- /dev/null +++ b/test/compile_errors/stage1/obj/binary_not_on_number_literal.zig @@ -0,0 +1,9 @@ +const TINY_QUANTUM_SHIFT = 4; +const TINY_QUANTUM_SIZE = 1 << TINY_QUANTUM_SHIFT; +var block_aligned_stuff: usize = (4 + TINY_QUANTUM_SIZE) & ~(TINY_QUANTUM_SIZE - 1); + +export fn entry() usize { return @sizeOf(@TypeOf(block_aligned_stuff)); } + +// binary not on number literal +// +// tmp.zig:3:60: error: unable to perform binary not operation on type 'comptime_int' diff --git a/test/compile_errors/stage1/obj/bitCast_same_size_but_bit_count_mismatch.zig b/test/compile_errors/stage1/obj/bitCast_same_size_but_bit_count_mismatch.zig new file mode 100644 index 0000000000..e4945a1194 --- /dev/null +++ b/test/compile_errors/stage1/obj/bitCast_same_size_but_bit_count_mismatch.zig @@ -0,0 +1,8 @@ +export fn entry(byte: u8) void { + var oops = @bitCast(u7, byte); + _ = oops; +} + +// @bitCast same size but bit count mismatch +// +// tmp.zig:2:25: error: destination type 'u7' has 7 bits but source type 'u8' has 8 bits diff --git a/test/compile_errors/stage1/obj/bitCast_to_enum_type.zig b/test/compile_errors/stage1/obj/bitCast_to_enum_type.zig new file mode 100644 index 0000000000..dbb73eba18 --- /dev/null +++ b/test/compile_errors/stage1/obj/bitCast_to_enum_type.zig @@ -0,0 +1,8 @@ +export fn entry() void { + const y = @bitCast(enum(u32) { a, b }, @as(u32, 3)); + _ = y; +} + +// bitCast to enum type +// +// tmp.zig:2:24: error: cannot cast a value of type 'y' diff --git a/test/compile_errors/stage1/obj/bitCast_with_different_sizes_inside_an_expression.zig b/test/compile_errors/stage1/obj/bitCast_with_different_sizes_inside_an_expression.zig new file mode 100644 index 0000000000..bb5c0c5936 --- /dev/null +++ b/test/compile_errors/stage1/obj/bitCast_with_different_sizes_inside_an_expression.zig @@ -0,0 +1,8 @@ +export fn entry() void { + var foo = (@bitCast(u8, @as(f32, 1.0)) == 0xf); + _ = foo; +} + +// @bitCast with different sizes inside an expression +// +// tmp.zig:2:25: error: destination type 'u8' has size 1 but source type 'f32' has size 4 diff --git a/test/compile_errors/stage1/obj/bit_shifting_only_works_on_integer_types.zig b/test/compile_errors/stage1/obj/bit_shifting_only_works_on_integer_types.zig new file mode 100644 index 0000000000..98fa3128aa --- /dev/null +++ b/test/compile_errors/stage1/obj/bit_shifting_only_works_on_integer_types.zig @@ -0,0 +1,8 @@ +export fn entry() void { + const x = &@as(u8, 1) << 10; + _ = x; +} + +// bit shifting only works on integer types +// +// tmp.zig:2:16: error: bit shifting operation expected integer type, found '*const u8' diff --git a/test/compile_errors/stage1/obj/bogus_compile_var.zig b/test/compile_errors/stage1/obj/bogus_compile_var.zig new file mode 100644 index 0000000000..05777b9ecd --- /dev/null +++ b/test/compile_errors/stage1/obj/bogus_compile_var.zig @@ -0,0 +1,6 @@ +const x = @import("builtin").bogus; +export fn entry() usize { return @sizeOf(@TypeOf(x)); } + +// bogus compile var +// +// tmp.zig:1:29: error: container 'builtin' has no member called 'bogus' diff --git a/test/compile_errors/stage1/obj/bogus_method_call_on_slice.zig b/test/compile_errors/stage1/obj/bogus_method_call_on_slice.zig new file mode 100644 index 0000000000..8d34a09817 --- /dev/null +++ b/test/compile_errors/stage1/obj/bogus_method_call_on_slice.zig @@ -0,0 +1,9 @@ +var self = "aoeu"; +fn f(m: []const u8) void { + m.copy(u8, self[0..], m); +} +export fn entry() usize { return @sizeOf(@TypeOf(f)); } + +// bogus method call on slice +// +// tmp.zig:3:6: error: no member named 'copy' in '[]const u8' diff --git a/test/compile_errors/stage1/obj/bool_not_on_undefined_value.zig b/test/compile_errors/stage1/obj/bool_not_on_undefined_value.zig new file mode 100644 index 0000000000..71ea7d5e96 --- /dev/null +++ b/test/compile_errors/stage1/obj/bool_not_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: bool = undefined; + _ = !a; +} + +// bool not on undefined value +// +// tmp.zig:3:10: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/branch_on_undefined_value.zig b/test/compile_errors/stage1/obj/branch_on_undefined_value.zig new file mode 100644 index 0000000000..d02ac22a2a --- /dev/null +++ b/test/compile_errors/stage1/obj/branch_on_undefined_value.zig @@ -0,0 +1,7 @@ +const x = if (undefined) true else false; + +export fn entry() usize { return @sizeOf(@TypeOf(x)); } + +// branch on undefined value +// +// tmp.zig:1:15: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/cImport_with_bogus_include.zig b/test/compile_errors/stage1/obj/cImport_with_bogus_include.zig new file mode 100644 index 0000000000..89627b354f --- /dev/null +++ b/test/compile_errors/stage1/obj/cImport_with_bogus_include.zig @@ -0,0 +1,7 @@ +const c = @cImport(@cInclude("bogus.h")); +export fn entry() usize { return @sizeOf(@TypeOf(c.bogo)); } + +// @cImport with bogus include +// +// tmp.zig:1:11: error: C import failed +// .h:1:10: note: 'bogus.h' file not found diff --git a/test/compile_errors/stage1/obj/call_assigned_to_constant.zig b/test/compile_errors/stage1/obj/call_assigned_to_constant.zig new file mode 100644 index 0000000000..fb2febadb2 --- /dev/null +++ b/test/compile_errors/stage1/obj/call_assigned_to_constant.zig @@ -0,0 +1,22 @@ +const Foo = struct { + x: i32, +}; +fn foo() Foo { + return .{ .x = 42 }; +} +fn bar(val: anytype) Foo { + return .{ .x = val }; +} +export fn entry() void { + const baz: Foo = undefined; + baz = foo(); +} +export fn entry1() void { + const baz: Foo = undefined; + baz = bar(42); +} + +// call assigned to constant +// +// tmp.zig:12:14: error: cannot assign to constant +// tmp.zig:16:14: error: cannot assign to constant diff --git a/test/compile_errors/stage1/obj/calling_a_generic_function_only_known_at_runtime.zig b/test/compile_errors/stage1/obj/calling_a_generic_function_only_known_at_runtime.zig new file mode 100644 index 0000000000..c31b18bb6f --- /dev/null +++ b/test/compile_errors/stage1/obj/calling_a_generic_function_only_known_at_runtime.zig @@ -0,0 +1,12 @@ +var foos = [_]fn(anytype) void { foo1, foo2 }; + +fn foo1(arg: anytype) void {_ = arg;} +fn foo2(arg: anytype) void {_ = arg;} + +pub fn main() !void { + foos[0](true); +} + +// calling a generic function only known at runtime +// +// tmp.zig:7:9: error: calling a generic function requires compile-time known function value diff --git a/test/compile_errors/stage1/obj/calling_function_with_naked_calling_convention.zig b/test/compile_errors/stage1/obj/calling_function_with_naked_calling_convention.zig new file mode 100644 index 0000000000..c99e5b7831 --- /dev/null +++ b/test/compile_errors/stage1/obj/calling_function_with_naked_calling_convention.zig @@ -0,0 +1,9 @@ +export fn entry() void { + foo(); +} +fn foo() callconv(.Naked) void { } + +// calling function with naked calling convention +// +// tmp.zig:2:5: error: unable to call function with naked calling convention +// tmp.zig:4:1: note: declared here diff --git a/test/compile_errors/stage1/obj/calling_var_args_extern_function_passing_array_instead_of_pointer.zig b/test/compile_errors/stage1/obj/calling_var_args_extern_function_passing_array_instead_of_pointer.zig new file mode 100644 index 0000000000..e3cc4425db --- /dev/null +++ b/test/compile_errors/stage1/obj/calling_var_args_extern_function_passing_array_instead_of_pointer.zig @@ -0,0 +1,8 @@ +export fn entry() void { + foo("hello".*,); +} +pub extern fn foo(format: *const u8, ...) void; + +// calling var args extern function, passing array instead of pointer +// +// tmp.zig:2:16: error: expected type '*const u8', found '[5:0]u8' diff --git a/test/compile_errors/stage1/obj/cannot_break_out_of_defer_expression.zig b/test/compile_errors/stage1/obj/cannot_break_out_of_defer_expression.zig new file mode 100644 index 0000000000..002a7717d3 --- /dev/null +++ b/test/compile_errors/stage1/obj/cannot_break_out_of_defer_expression.zig @@ -0,0 +1,11 @@ +export fn foo() void { + while (true) { + defer { + break; + } + } +} + +// cannot break out of defer expression +// +// tmp.zig:4:13: error: cannot break out of defer expression diff --git a/test/compile_errors/stage1/obj/cannot_continue_out_of_defer_expression.zig b/test/compile_errors/stage1/obj/cannot_continue_out_of_defer_expression.zig new file mode 100644 index 0000000000..8adb7eafd6 --- /dev/null +++ b/test/compile_errors/stage1/obj/cannot_continue_out_of_defer_expression.zig @@ -0,0 +1,11 @@ +export fn foo() void { + while (true) { + defer { + continue; + } + } +} + +// cannot continue out of defer expression +// +// tmp.zig:4:13: error: cannot continue out of defer expression diff --git a/test/compile_errors/stage1/obj/capture_group_on_switch_prong_with_incompatible_payload_types.zig b/test/compile_errors/stage1/obj/capture_group_on_switch_prong_with_incompatible_payload_types.zig new file mode 100644 index 0000000000..8a4c94f503 --- /dev/null +++ b/test/compile_errors/stage1/obj/capture_group_on_switch_prong_with_incompatible_payload_types.zig @@ -0,0 +1,19 @@ +const Union = union(enum) { + A: usize, + B: isize, +}; +comptime { + var u = Union{ .A = 8 }; + switch (u) { + .A, .B => |e| { + _ = e; + unreachable; + }, + } +} + +// capture group on switch prong with incompatible payload types +// +// tmp.zig:8:20: error: capture group with incompatible types +// tmp.zig:8:9: note: type 'usize' here +// tmp.zig:8:13: note: type 'isize' here diff --git a/test/compile_errors/stage1/obj/cast_enum_literal_to_enum_but_it_doesnt_match.zig b/test/compile_errors/stage1/obj/cast_enum_literal_to_enum_but_it_doesnt_match.zig new file mode 100644 index 0000000000..10717ff392 --- /dev/null +++ b/test/compile_errors/stage1/obj/cast_enum_literal_to_enum_but_it_doesnt_match.zig @@ -0,0 +1,13 @@ +const Foo = enum { + a, + b, +}; +export fn entry() void { + const x: Foo = .c; + _ = x; +} + +// cast enum literal to enum but it doesn't match +// +// tmp.zig:6:20: error: enum 'Foo' has no field named 'c' +// tmp.zig:1:13: note: 'Foo' declared here diff --git a/test/compile_errors/stage1/obj/cast_error_union_of_global_error_set_to_error_union_of_smaller_error_set.zig b/test/compile_errors/stage1/obj/cast_error_union_of_global_error_set_to_error_union_of_smaller_error_set.zig new file mode 100644 index 0000000000..fbb68c30c8 --- /dev/null +++ b/test/compile_errors/stage1/obj/cast_error_union_of_global_error_set_to_error_union_of_smaller_error_set.zig @@ -0,0 +1,14 @@ +const SmallErrorSet = error{A}; +export fn entry() void { + var x: SmallErrorSet!i32 = foo(); + _ = x; +} +fn foo() anyerror!i32 { + return error.B; +} + +// cast error union of global error set to error union of smaller error set +// +// tmp.zig:3:35: error: expected type 'SmallErrorSet!i32', found 'anyerror!i32' +// tmp.zig:3:35: note: error set 'anyerror' cannot cast into error set 'SmallErrorSet' +// tmp.zig:3:35: note: cannot cast global error set into smaller set diff --git a/test/compile_errors/stage1/obj/cast_global_error_set_to_error_set.zig b/test/compile_errors/stage1/obj/cast_global_error_set_to_error_set.zig new file mode 100644 index 0000000000..f37ffce229 --- /dev/null +++ b/test/compile_errors/stage1/obj/cast_global_error_set_to_error_set.zig @@ -0,0 +1,13 @@ +const SmallErrorSet = error{A}; +export fn entry() void { + var x: SmallErrorSet = foo(); + _ = x; +} +fn foo() anyerror { + return error.B; +} + +// cast global error set to error set +// +// tmp.zig:3:31: error: expected type 'SmallErrorSet', found 'anyerror' +// tmp.zig:3:31: note: cannot cast global error set into smaller set diff --git a/test/compile_errors/stage1/obj/cast_negative_integer_literal_to_usize.zig b/test/compile_errors/stage1/obj/cast_negative_integer_literal_to_usize.zig new file mode 100644 index 0000000000..37e42c062c --- /dev/null +++ b/test/compile_errors/stage1/obj/cast_negative_integer_literal_to_usize.zig @@ -0,0 +1,8 @@ +export fn entry() void { + const x = @as(usize, -10); + _ = x; +} + +// cast negative integer literal to usize +// +// tmp.zig:2:26: error: cannot cast negative value -10 to unsigned integer type 'usize' diff --git a/test/compile_errors/stage1/obj/cast_negative_value_to_unsigned_integer.zig b/test/compile_errors/stage1/obj/cast_negative_value_to_unsigned_integer.zig new file mode 100644 index 0000000000..e740d406f6 --- /dev/null +++ b/test/compile_errors/stage1/obj/cast_negative_value_to_unsigned_integer.zig @@ -0,0 +1,15 @@ +comptime { + const value: i32 = -1; + const unsigned = @intCast(u32, value); + _ = unsigned; +} +export fn entry1() void { + const value: i32 = -1; + const unsigned: u32 = value; + _ = unsigned; +} + +// cast negative value to unsigned integer +// +// tmp.zig:3:22: error: attempt to cast negative value to unsigned integer +// tmp.zig:8:27: error: cannot cast negative value -1 to unsigned integer type 'u32' diff --git a/test/compile_errors/stage1/obj/cast_unreachable.zig b/test/compile_errors/stage1/obj/cast_unreachable.zig new file mode 100644 index 0000000000..94fa121770 --- /dev/null +++ b/test/compile_errors/stage1/obj/cast_unreachable.zig @@ -0,0 +1,9 @@ +fn f() i32 { + return @as(i32, return 1); +} +export fn entry() void { _ = f(); } + +// cast unreachable +// +// tmp.zig:2:12: error: unreachable code +// tmp.zig:2:21: note: control flow is diverted here diff --git a/test/compile_errors/stage1/obj/casting_bit_offset_pointer_to_regular_pointer.zig b/test/compile_errors/stage1/obj/casting_bit_offset_pointer_to_regular_pointer.zig new file mode 100644 index 0000000000..9b6c5ea0e6 --- /dev/null +++ b/test/compile_errors/stage1/obj/casting_bit_offset_pointer_to_regular_pointer.zig @@ -0,0 +1,19 @@ +const BitField = packed struct { + a: u3, + b: u3, + c: u2, +}; + +fn foo(bit_field: *const BitField) u3 { + return bar(&bit_field.b); +} + +fn bar(x: *const u3) u3 { + return x.*; +} + +export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + +// casting bit offset pointer to regular pointer +// +// tmp.zig:8:26: error: expected type '*const u3', found '*align(:3:1) const u3' diff --git a/test/compile_errors/stage1/obj/catch_on_undefined_value.zig b/test/compile_errors/stage1/obj/catch_on_undefined_value.zig new file mode 100644 index 0000000000..9d22d07219 --- /dev/null +++ b/test/compile_errors/stage1/obj/catch_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: anyerror!bool = undefined; + _ = a catch false; +} + +// catch on undefined value +// +// tmp.zig:3:11: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/chained_comparison_operators.zig b/test/compile_errors/stage1/obj/chained_comparison_operators.zig new file mode 100644 index 0000000000..89e1ea1e75 --- /dev/null +++ b/test/compile_errors/stage1/obj/chained_comparison_operators.zig @@ -0,0 +1,7 @@ +export fn a(value: u32) bool { + return 1 < value < 1000; +} + +// chained comparison operators +// +// tmp.zig:2:22: error: comparison operators cannot be chained diff --git a/test/compile_errors/stage1/obj/cmpxchg_with_float.zig b/test/compile_errors/stage1/obj/cmpxchg_with_float.zig new file mode 100644 index 0000000000..8926ffc36b --- /dev/null +++ b/test/compile_errors/stage1/obj/cmpxchg_with_float.zig @@ -0,0 +1,8 @@ +export fn entry() void { + var x: f32 = 0; + _ = @cmpxchgWeak(f32, &x, 1, 2, .SeqCst, .SeqCst); +} + +// cmpxchg with float +// +// tmp.zig:3:22: error: expected bool, integer, enum or pointer type, found 'f32' diff --git a/test/compile_errors/stage1/obj/colliding_invalid_top_level_functions.zig b/test/compile_errors/stage1/obj/colliding_invalid_top_level_functions.zig new file mode 100644 index 0000000000..f4e5d24c46 --- /dev/null +++ b/test/compile_errors/stage1/obj/colliding_invalid_top_level_functions.zig @@ -0,0 +1,8 @@ +fn func() bogus {} +fn func() bogus {} +export fn entry() usize { return @sizeOf(@TypeOf(func)); } + +// colliding invalid top level functions +// +// tmp.zig:2:1: error: redeclaration of 'func' +// tmp.zig:1:1: note: other declaration here diff --git a/test/compile_errors/stage1/obj/compare_optional_to_non-optional_with_invalid_types.zig b/test/compile_errors/stage1/obj/compare_optional_to_non-optional_with_invalid_types.zig new file mode 100644 index 0000000000..8713cf4501 --- /dev/null +++ b/test/compile_errors/stage1/obj/compare_optional_to_non-optional_with_invalid_types.zig @@ -0,0 +1,33 @@ +export fn inconsistentChildType() void { + var x: ?i32 = undefined; + const y: comptime_int = 10; + _ = (x == y); +} + +export fn optionalToOptional() void { + var x: ?i32 = undefined; + var y: ?i32 = undefined; + _ = (x == y); +} + +export fn optionalVector() void { + var x: ?@Vector(10, i32) = undefined; + var y: @Vector(10, i32) = undefined; + _ = (x == y); +} + +export fn invalidChildType() void { + var x: ?[3]i32 = undefined; + var y: [3]i32 = undefined; + _ = (x == y); +} + +// compare optional to non-optional with invalid types +// +// :4:12: error: cannot compare types '?i32' and 'comptime_int' +// :4:12: note: optional child type 'i32' must be the same as non-optional type 'comptime_int' +// :10:12: error: cannot compare types '?i32' and '?i32' +// :10:12: note: optional to optional comparison is only supported for optional pointer types +// :16:12: error: TODO add comparison of optional vector +// :22:12: error: cannot compare types '?[3]i32' and '[3]i32' +// :22:12: note: operator not supported for type '[3]i32' diff --git a/test/compile_errors/stage1/obj/comparing_a_non-optional_pointer_against_null.zig b/test/compile_errors/stage1/obj/comparing_a_non-optional_pointer_against_null.zig new file mode 100644 index 0000000000..69a5120135 --- /dev/null +++ b/test/compile_errors/stage1/obj/comparing_a_non-optional_pointer_against_null.zig @@ -0,0 +1,8 @@ +export fn entry() void { + var x: i32 = 1; + _ = &x == null; +} + +// comparing a non-optional pointer against null +// +// tmp.zig:3:12: error: comparison of '*i32' with null diff --git a/test/compile_errors/stage1/obj/comparing_against_undefined_produces_undefined_value.zig b/test/compile_errors/stage1/obj/comparing_against_undefined_produces_undefined_value.zig new file mode 100644 index 0000000000..c329f51ef7 --- /dev/null +++ b/test/compile_errors/stage1/obj/comparing_against_undefined_produces_undefined_value.zig @@ -0,0 +1,7 @@ +export fn entry() void { + if (2 == undefined) {} +} + +// comparing against undefined produces undefined value +// +// tmp.zig:2:11: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/comparison_operators_with_undefined_value.zig b/test/compile_errors/stage1/obj/comparison_operators_with_undefined_value.zig new file mode 100644 index 0000000000..6021382fd4 --- /dev/null +++ b/test/compile_errors/stage1/obj/comparison_operators_with_undefined_value.zig @@ -0,0 +1,45 @@ +// operator == +comptime { + var a: i64 = undefined; + var x: i32 = 0; + if (a == a) x += 1; +} +// operator != +comptime { + var a: i64 = undefined; + var x: i32 = 0; + if (a != a) x += 1; +} +// operator > +comptime { + var a: i64 = undefined; + var x: i32 = 0; + if (a > a) x += 1; +} +// operator < +comptime { + var a: i64 = undefined; + var x: i32 = 0; + if (a < a) x += 1; +} +// operator >= +comptime { + var a: i64 = undefined; + var x: i32 = 0; + if (a >= a) x += 1; +} +// operator <= +comptime { + var a: i64 = undefined; + var x: i32 = 0; + if (a <= a) x += 1; +} + +// comparison operators with undefined value +// +// tmp.zig:5:11: error: use of undefined value here causes undefined behavior +// tmp.zig:11:11: error: use of undefined value here causes undefined behavior +// tmp.zig:17:11: error: use of undefined value here causes undefined behavior +// tmp.zig:23:11: error: use of undefined value here causes undefined behavior +// tmp.zig:29:11: error: use of undefined value here causes undefined behavior +// tmp.zig:35:11: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/comparison_with_error_union_and_error_value.zig b/test/compile_errors/stage1/obj/comparison_with_error_union_and_error_value.zig new file mode 100644 index 0000000000..260ccc6f01 --- /dev/null +++ b/test/compile_errors/stage1/obj/comparison_with_error_union_and_error_value.zig @@ -0,0 +1,8 @@ +export fn entry() void { + var number_or_error: anyerror!i32 = error.SomethingAwful; + _ = number_or_error == error.SomethingAwful; +} + +// comparison with error union and error value +// +// tmp.zig:3:25: error: operator not allowed for type 'anyerror!i32' diff --git a/test/compile_errors/stage1/obj/compile-time_division_by_zero.zig b/test/compile_errors/stage1/obj/compile-time_division_by_zero.zig new file mode 100644 index 0000000000..578dbc7b4a --- /dev/null +++ b/test/compile_errors/stage1/obj/compile-time_division_by_zero.zig @@ -0,0 +1,10 @@ +comptime { + const a: i32 = 1; + const b: i32 = 0; + const c = a / b; + _ = c; +} + +// compile-time division by zero +// +// tmp.zig:4:17: error: division by zero diff --git a/test/compile_errors/stage1/obj/compile-time_remainder_division_by_zero.zig b/test/compile_errors/stage1/obj/compile-time_remainder_division_by_zero.zig new file mode 100644 index 0000000000..015d229110 --- /dev/null +++ b/test/compile_errors/stage1/obj/compile-time_remainder_division_by_zero.zig @@ -0,0 +1,10 @@ +comptime { + const a: i32 = 1; + const b: i32 = 0; + const c = a % b; + _ = c; +} + +// compile-time remainder division by zero +// +// tmp.zig:4:17: error: division by zero diff --git a/test/compile_errors/stage1/obj/compileError_shows_traceback_of_references_that_caused_it.zig b/test/compile_errors/stage1/obj/compileError_shows_traceback_of_references_that_caused_it.zig new file mode 100644 index 0000000000..4273741ad2 --- /dev/null +++ b/test/compile_errors/stage1/obj/compileError_shows_traceback_of_references_that_caused_it.zig @@ -0,0 +1,12 @@ +const foo = @compileError("aoeu",); + +const bar = baz + foo; +const baz = 1; + +export fn entry() i32 { + return bar; +} + +// @compileError shows traceback of references that caused it +// +// tmp.zig:1:13: error: aoeu diff --git a/test/compile_errors/stage1/obj/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig b/test/compile_errors/stage1/obj/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig new file mode 100644 index 0000000000..0770b3b6a4 --- /dev/null +++ b/test/compile_errors/stage1/obj/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig @@ -0,0 +1,15 @@ +const Bar = union(enum(u32)) { + X: i32 = 1 +}; + +fn testCompileLog(x: Bar) void { + @compileLog(x); +} + +pub fn main () void { + comptime testCompileLog(Bar{.X = 123}); +} + +// compileLog of tagged enum doesn't crash the compiler +// +// tmp.zig:6:5: error: found compile log statement diff --git a/test/compile_errors/stage1/obj/compile_error_in_struct_init_expression.zig b/test/compile_errors/stage1/obj/compile_error_in_struct_init_expression.zig new file mode 100644 index 0000000000..6215c9ad76 --- /dev/null +++ b/test/compile_errors/stage1/obj/compile_error_in_struct_init_expression.zig @@ -0,0 +1,14 @@ +const Foo = struct { + a: i32 = crap, + b: i32, +}; +export fn entry() void { + var x = Foo{ + .b = 5, + }; + _ = x; +} + +// compile error in struct init expression +// +// tmp.zig:2:14: error: use of undeclared identifier 'crap' diff --git a/test/compile_errors/stage1/obj/compile_error_when_evaluating_return_type_of_inferred_error_set.zig b/test/compile_errors/stage1/obj/compile_error_when_evaluating_return_type_of_inferred_error_set.zig new file mode 100644 index 0000000000..44d5b6a389 --- /dev/null +++ b/test/compile_errors/stage1/obj/compile_error_when_evaluating_return_type_of_inferred_error_set.zig @@ -0,0 +1,12 @@ +const Car = struct { + foo: *SymbolThatDoesNotExist, + pub fn init() !Car {} +}; +export fn entry() void { + const car = Car.init(); + _ = car; +} + +// compile error when evaluating return type of inferred error set +// +// tmp.zig:2:11: error: use of undeclared identifier 'SymbolThatDoesNotExist' diff --git a/test/compile_errors/stage1/obj/compile_log.zig b/test/compile_errors/stage1/obj/compile_log.zig new file mode 100644 index 0000000000..ef1cba4502 --- /dev/null +++ b/test/compile_errors/stage1/obj/compile_log.zig @@ -0,0 +1,14 @@ +export fn foo() void { + comptime bar(12, "hi",); +} +fn bar(a: i32, b: []const u8) void { + @compileLog("begin",); + @compileLog("a", a, "b", b); + @compileLog("end",); +} + +// compile log +// +// tmp.zig:5:5: error: found compile log statement +// tmp.zig:6:5: error: found compile log statement +// tmp.zig:7:5: error: found compile log statement diff --git a/test/compile_errors/stage1/obj/compile_log_a_pointer_to_an_opaque_value.zig b/test/compile_errors/stage1/obj/compile_log_a_pointer_to_an_opaque_value.zig new file mode 100644 index 0000000000..15b7825a91 --- /dev/null +++ b/test/compile_errors/stage1/obj/compile_log_a_pointer_to_an_opaque_value.zig @@ -0,0 +1,7 @@ +export fn entry() void { + @compileLog(@ptrCast(*const anyopaque, &entry)); +} + +// compile log a pointer to an opaque value +// +// tmp.zig:2:5: error: found compile log statement diff --git a/test/compile_errors/stage1/obj/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig b/test/compile_errors/stage1/obj/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig new file mode 100644 index 0000000000..f848ed7c7c --- /dev/null +++ b/test/compile_errors/stage1/obj/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig @@ -0,0 +1,12 @@ +fn Foo(comptime T: type) type { + @compileLog(@typeName(T)); + return T; +} +export fn entry() void { + _ = Foo(i32); + _ = @typeName(Foo(i32)); +} + +// compile log statement inside function which must be comptime evaluated +// +// tmp.zig:2:5: error: found compile log statement diff --git a/test/compile_errors/stage1/obj/compile_log_statement_warning_deduplication_in_generic_fn.zig b/test/compile_errors/stage1/obj/compile_log_statement_warning_deduplication_in_generic_fn.zig new file mode 100644 index 0000000000..05ac76724c --- /dev/null +++ b/test/compile_errors/stage1/obj/compile_log_statement_warning_deduplication_in_generic_fn.zig @@ -0,0 +1,12 @@ +export fn entry() void { + inner(1); + inner(2); +} +fn inner(comptime n: usize) void { + comptime var i = 0; + inline while (i < n) : (i += 1) { @compileLog("!@#$"); } +} + +// compile log statement warning deduplication in generic fn +// +// tmp.zig:7:39: error: found compile log statement diff --git a/test/compile_errors/stage1/obj/compile_time_division_by_zero.zig b/test/compile_errors/stage1/obj/compile_time_division_by_zero.zig new file mode 100644 index 0000000000..17187c47bf --- /dev/null +++ b/test/compile_errors/stage1/obj/compile_time_division_by_zero.zig @@ -0,0 +1,10 @@ +const y = foo(0); +fn foo(x: u32) u32 { + return 1 / x; +} + +export fn entry() usize { return @sizeOf(@TypeOf(y)); } + +// compile time division by zero +// +// tmp.zig:3:14: error: division by zero diff --git a/test/compile_errors/stage1/obj/comptime_cast_enum_to_union_but_field_has_payload.zig b/test/compile_errors/stage1/obj/comptime_cast_enum_to_union_but_field_has_payload.zig new file mode 100644 index 0000000000..36a2079a01 --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_cast_enum_to_union_but_field_has_payload.zig @@ -0,0 +1,15 @@ +const Letter = enum { A, B, C }; +const Value = union(Letter) { + A: i32, + B, + C, +}; +export fn entry() void { + var x: Value = Letter.A; + _ = x; +} + +// comptime cast enum to union but field has payload +// +// tmp.zig:8:26: error: cast to union 'Value' must initialize 'i32' field 'A' +// tmp.zig:3:5: note: field 'A' declared here diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig new file mode 100644 index 0000000000..18c78c858c --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig @@ -0,0 +1,14 @@ +export fn entry() void { + const ints = [_]u8{ 1, 2 }; + inline for (ints) |_| { + bad() catch continue; + } +} +fn bad() !void { + return error.Bad; +} + +// comptime continue inside runtime catch +// +// tmp.zig:4:21: error: comptime control flow inside runtime block +// tmp.zig:4:15: note: runtime block created here diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig new file mode 100644 index 0000000000..f6ec1b98b1 --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig @@ -0,0 +1,13 @@ +export fn entry() void { + var p: usize = undefined; + comptime var q = true; + inline while (q) { + if (p == 11) continue; + q = false; + } +} + +// comptime continue inside runtime if bool +// +// tmp.zig:5:22: error: comptime control flow inside runtime block +// tmp.zig:5:9: note: runtime block created here diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig new file mode 100644 index 0000000000..556a8769a5 --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig @@ -0,0 +1,13 @@ +export fn entry() void { + var p: anyerror!i32 = undefined; + comptime var q = true; + inline while (q) { + if (p) |_| continue else |_| {} + q = false; + } +} + +// comptime continue inside runtime if error +// +// tmp.zig:5:20: error: comptime control flow inside runtime block +// tmp.zig:5:9: note: runtime block created here diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig new file mode 100644 index 0000000000..4f35398a2b --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig @@ -0,0 +1,13 @@ +export fn entry() void { + var p: ?i32 = undefined; + comptime var q = true; + inline while (q) { + if (p) |_| continue; + q = false; + } +} + +// comptime continue inside runtime if optional +// +// tmp.zig:5:20: error: comptime control flow inside runtime block +// tmp.zig:5:9: note: runtime block created here diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig new file mode 100644 index 0000000000..259cbd602e --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig @@ -0,0 +1,16 @@ +export fn entry() void { + var p: i32 = undefined; + comptime var q = true; + inline while (q) { + switch (p) { + 11 => continue, + else => {}, + } + q = false; + } +} + +// comptime continue inside runtime switch +// +// tmp.zig:6:19: error: comptime control flow inside runtime block +// tmp.zig:5:9: note: runtime block created here diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig new file mode 100644 index 0000000000..5d86394815 --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig @@ -0,0 +1,13 @@ +export fn entry() void { + var p: usize = undefined; + comptime var q = true; + outer: inline while (q) { + while (p == 11) continue :outer; + q = false; + } +} + +// comptime continue inside runtime while bool +// +// tmp.zig:5:25: error: comptime control flow inside runtime block +// tmp.zig:5:9: note: runtime block created here diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig new file mode 100644 index 0000000000..0ba49cb3ca --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig @@ -0,0 +1,15 @@ +export fn entry() void { + var p: anyerror!usize = undefined; + comptime var q = true; + outer: inline while (q) { + while (p) |_| { + continue :outer; + } else |_| {} + q = false; + } +} + +// comptime continue inside runtime while error +// +// tmp.zig:6:13: error: comptime control flow inside runtime block +// tmp.zig:5:9: note: runtime block created here diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig new file mode 100644 index 0000000000..b2cfd2f69d --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig @@ -0,0 +1,13 @@ +export fn entry() void { + var p: ?usize = undefined; + comptime var q = true; + outer: inline while (q) { + while (p) |_| continue :outer; + q = false; + } +} + +// comptime continue inside runtime while optional +// +// tmp.zig:5:23: error: comptime control flow inside runtime block +// tmp.zig:5:9: note: runtime block created here diff --git a/test/compile_errors/stage1/obj/comptime_float_in_asm_input.zig b/test/compile_errors/stage1/obj/comptime_float_in_asm_input.zig new file mode 100644 index 0000000000..c2333586b4 --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_float_in_asm_input.zig @@ -0,0 +1,7 @@ +export fn foo() void { + asm volatile ("" : : [bar]"r"(3.17) : ""); +} + +// comptime_float in asm input +// +// tmp.zig:2:35: error: expected sized integer or sized float, found comptime_float diff --git a/test/compile_errors/stage1/obj/comptime_implicit_cast_f64_to_f32.zig b/test/compile_errors/stage1/obj/comptime_implicit_cast_f64_to_f32.zig new file mode 100644 index 0000000000..f98c45a348 --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_implicit_cast_f64_to_f32.zig @@ -0,0 +1,9 @@ +export fn entry() void { + const x: f64 = 16777217; + const y: f32 = x; + _ = y; +} + +// comptime implicit cast f64 to f32 +// +// tmp.zig:3:20: error: cast of value 16777217.000000 to type 'f32' loses information diff --git a/test/compile_errors/stage1/obj/comptime_int_in_asm_input.zig b/test/compile_errors/stage1/obj/comptime_int_in_asm_input.zig new file mode 100644 index 0000000000..5a587e5a77 --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_int_in_asm_input.zig @@ -0,0 +1,7 @@ +export fn foo() void { + asm volatile ("" : : [bar]"r"(3) : ""); +} + +// comptime_int in asm input +// +// tmp.zig:2:35: error: expected sized integer or sized float, found comptime_int diff --git a/test/compile_errors/stage1/obj/comptime_ptrcast_of_zero-sized_type.zig b/test/compile_errors/stage1/obj/comptime_ptrcast_of_zero-sized_type.zig new file mode 100644 index 0000000000..1d3f91fe91 --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_ptrcast_of_zero-sized_type.zig @@ -0,0 +1,10 @@ +fn foo() void { + const node: struct {} = undefined; + const vla_ptr = @ptrCast([*]const u8, &node); + _ = vla_ptr; +} +comptime { foo(); } + +// comptime ptrcast of zero-sized type +// +// tmp.zig:3:21: error: '*const struct:2:17' and '[*]const u8' do not have the same in-memory representation diff --git a/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig b/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig new file mode 100644 index 0000000000..622e0cf76b --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig @@ -0,0 +1,65 @@ +export fn foo_array() void { + comptime { + var target = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + const slice = target[0..3 :0]; + _ = slice; + } +} +export fn foo_ptr_array() void { + comptime { + var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target = &buf; + const slice = target[0..3 :0]; + _ = slice; + } +} +export fn foo_vector_ConstPtrSpecialBaseArray() void { + comptime { + var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*]u8 = &buf; + const slice = target[0..3 :0]; + _ = slice; + } +} +export fn foo_vector_ConstPtrSpecialRef() void { + comptime { + var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*]u8 = @ptrCast([*]u8, &buf); + const slice = target[0..3 :0]; + _ = slice; + } +} +export fn foo_cvector_ConstPtrSpecialBaseArray() void { + comptime { + var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*c]u8 = &buf; + const slice = target[0..3 :0]; + _ = slice; + } +} +export fn foo_cvector_ConstPtrSpecialRef() void { + comptime { + var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*c]u8 = @ptrCast([*c]u8, &buf); + const slice = target[0..3 :0]; + _ = slice; + } +} +export fn foo_slice() void { + comptime { + var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: []u8 = &buf; + const slice = target[0..3 :0]; + _ = slice; + } +} + +// comptime slice-sentinel does not match memory at target index (terminated) +// +// :4:29: error: slice-sentinel does not match memory at target index +// :12:29: error: slice-sentinel does not match memory at target index +// :20:29: error: slice-sentinel does not match memory at target index +// :28:29: error: slice-sentinel does not match memory at target index +// :36:29: error: slice-sentinel does not match memory at target index +// :44:29: error: slice-sentinel does not match memory at target index +// :52:29: error: slice-sentinel does not match memory at target index diff --git a/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig b/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig new file mode 100644 index 0000000000..e9451cf9aa --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig @@ -0,0 +1,65 @@ +export fn foo_array() void { + comptime { + var target = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + const slice = target[0..3 :0]; + _ = slice; + } +} +export fn foo_ptr_array() void { + comptime { + var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target = &buf; + const slice = target[0..3 :0]; + _ = slice; + } +} +export fn foo_vector_ConstPtrSpecialBaseArray() void { + comptime { + var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*]u8 = &buf; + const slice = target[0..3 :0]; + _ = slice; + } +} +export fn foo_vector_ConstPtrSpecialRef() void { + comptime { + var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*]u8 = @ptrCast([*]u8, &buf); + const slice = target[0..3 :0]; + _ = slice; + } +} +export fn foo_cvector_ConstPtrSpecialBaseArray() void { + comptime { + var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*c]u8 = &buf; + const slice = target[0..3 :0]; + _ = slice; + } +} +export fn foo_cvector_ConstPtrSpecialRef() void { + comptime { + var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*c]u8 = @ptrCast([*c]u8, &buf); + const slice = target[0..3 :0]; + _ = slice; + } +} +export fn foo_slice() void { + comptime { + var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: []u8 = &buf; + const slice = target[0..3 :0]; + _ = slice; + } +} + +// comptime slice-sentinel does not match memory at target index (unterminated) +// +// :4:29: error: slice-sentinel does not match memory at target index +// :12:29: error: slice-sentinel does not match memory at target index +// :20:29: error: slice-sentinel does not match memory at target index +// :28:29: error: slice-sentinel does not match memory at target index +// :36:29: error: slice-sentinel does not match memory at target index +// :44:29: error: slice-sentinel does not match memory at target index +// :52:29: error: slice-sentinel does not match memory at target index diff --git a/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_target-sentinel.zig b/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_target-sentinel.zig new file mode 100644 index 0000000000..597a493705 --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_target-sentinel.zig @@ -0,0 +1,65 @@ +export fn foo_array() void { + comptime { + var target = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + const slice = target[0..14 :255]; + _ = slice; + } +} +export fn foo_ptr_array() void { + comptime { + var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target = &buf; + const slice = target[0..14 :255]; + _ = slice; + } +} +export fn foo_vector_ConstPtrSpecialBaseArray() void { + comptime { + var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*]u8 = &buf; + const slice = target[0..14 :255]; + _ = slice; + } +} +export fn foo_vector_ConstPtrSpecialRef() void { + comptime { + var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*]u8 = @ptrCast([*]u8, &buf); + const slice = target[0..14 :255]; + _ = slice; + } +} +export fn foo_cvector_ConstPtrSpecialBaseArray() void { + comptime { + var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*c]u8 = &buf; + const slice = target[0..14 :255]; + _ = slice; + } +} +export fn foo_cvector_ConstPtrSpecialRef() void { + comptime { + var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*c]u8 = @ptrCast([*c]u8, &buf); + const slice = target[0..14 :255]; + _ = slice; + } +} +export fn foo_slice() void { + comptime { + var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: []u8 = &buf; + const slice = target[0..14 :255]; + _ = slice; + } +} + +// comptime slice-sentinel does not match target-sentinel +// +// :4:29: error: slice-sentinel does not match target-sentinel +// :12:29: error: slice-sentinel does not match target-sentinel +// :20:29: error: slice-sentinel does not match target-sentinel +// :28:29: error: slice-sentinel does not match target-sentinel +// :36:29: error: slice-sentinel does not match target-sentinel +// :44:29: error: slice-sentinel does not match target-sentinel +// :52:29: error: slice-sentinel does not match target-sentinel diff --git a/test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_terminated.zig b/test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_terminated.zig new file mode 100644 index 0000000000..d5fc64ea84 --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_terminated.zig @@ -0,0 +1,65 @@ +export fn foo_array() void { + comptime { + var target = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + const slice = target[0..15 :1]; + _ = slice; + } +} +export fn foo_ptr_array() void { + comptime { + var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target = &buf; + const slice = target[0..15 :0]; + _ = slice; + } +} +export fn foo_vector_ConstPtrSpecialBaseArray() void { + comptime { + var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*]u8 = &buf; + const slice = target[0..15 :0]; + _ = slice; + } +} +export fn foo_vector_ConstPtrSpecialRef() void { + comptime { + var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*]u8 = @ptrCast([*]u8, &buf); + const slice = target[0..15 :0]; + _ = slice; + } +} +export fn foo_cvector_ConstPtrSpecialBaseArray() void { + comptime { + var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*c]u8 = &buf; + const slice = target[0..15 :0]; + _ = slice; + } +} +export fn foo_cvector_ConstPtrSpecialRef() void { + comptime { + var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*c]u8 = @ptrCast([*c]u8, &buf); + const slice = target[0..15 :0]; + _ = slice; + } +} +export fn foo_slice() void { + comptime { + var buf = [_:0]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: []u8 = &buf; + const slice = target[0..15 :0]; + _ = slice; + } +} + +// comptime slice-sentinel is out of bounds (terminated) +// +// :4:29: error: out of bounds slice +// :12:29: error: out of bounds slice +// :20:29: error: out of bounds slice +// :28:29: error: out of bounds slice +// :36:29: error: out of bounds slice +// :44:29: error: out of bounds slice +// :52:29: error: out of bounds slice diff --git a/test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig b/test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig new file mode 100644 index 0000000000..05a499ca8a --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig @@ -0,0 +1,65 @@ +export fn foo_array() void { + comptime { + var target = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + const slice = target[0..14 :0]; + _ = slice; + } +} +export fn foo_ptr_array() void { + comptime { + var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target = &buf; + const slice = target[0..14 :0]; + _ = slice; + } +} +export fn foo_vector_ConstPtrSpecialBaseArray() void { + comptime { + var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*]u8 = &buf; + const slice = target[0..14 :0]; + _ = slice; + } +} +export fn foo_vector_ConstPtrSpecialRef() void { + comptime { + var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*]u8 = @ptrCast([*]u8, &buf); + const slice = target[0..14 :0]; + _ = slice; + } +} +export fn foo_cvector_ConstPtrSpecialBaseArray() void { + comptime { + var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*c]u8 = &buf; + const slice = target[0..14 :0]; + _ = slice; + } +} +export fn foo_cvector_ConstPtrSpecialRef() void { + comptime { + var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: [*c]u8 = @ptrCast([*c]u8, &buf); + const slice = target[0..14 :0]; + _ = slice; + } +} +export fn foo_slice() void { + comptime { + var buf = [_]u8{ 'a', 'b', 'c', 'd' } ++ [_]u8{undefined} ** 10; + var target: []u8 = &buf; + const slice = target[0..14 :0]; + _ = slice; + } +} + +// comptime slice-sentinel is out of bounds (unterminated) +// +// :4:29: error: slice-sentinel is out of bounds +// :12:29: error: slice-sentinel is out of bounds +// :20:29: error: slice-sentinel is out of bounds +// :28:29: error: slice-sentinel is out of bounds +// :36:29: error: slice-sentinel is out of bounds +// :44:29: error: slice-sentinel is out of bounds +// :52:29: error: slice-sentinel is out of bounds diff --git a/test/compile_errors/stage1/obj/comptime_slice_of_an_undefined_slice.zig b/test/compile_errors/stage1/obj/comptime_slice_of_an_undefined_slice.zig new file mode 100644 index 0000000000..b56feddc02 --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_slice_of_an_undefined_slice.zig @@ -0,0 +1,9 @@ +comptime { + var a: []u8 = undefined; + var b = a[0..10]; + _ = b; +} + +// comptime slice of an undefined slice +// +// tmp.zig:3:14: error: slice of undefined diff --git a/test/compile_errors/stage1/obj/comptime_slice_of_undefined_pointer_non-zero_len.zig b/test/compile_errors/stage1/obj/comptime_slice_of_undefined_pointer_non-zero_len.zig new file mode 100644 index 0000000000..bcddd1d866 --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_slice_of_undefined_pointer_non-zero_len.zig @@ -0,0 +1,8 @@ +export fn entry() void { + const slice = @as([*]i32, undefined)[0..1]; + _ = slice; +} + +// comptime slice of undefined pointer non-zero len +// +// tmp.zig:2:41: error: non-zero length slice of undefined pointer diff --git a/test/compile_errors/stage1/obj/comptime_struct_field_no_init_value.zig b/test/compile_errors/stage1/obj/comptime_struct_field_no_init_value.zig new file mode 100644 index 0000000000..c3de76a794 --- /dev/null +++ b/test/compile_errors/stage1/obj/comptime_struct_field_no_init_value.zig @@ -0,0 +1,11 @@ +const Foo = struct { + comptime b: i32, +}; +export fn entry() void { + var f: Foo = undefined; + _ = f; +} + +// comptime struct field, no init value +// +// tmp.zig:2:5: error: comptime field without default initialization value diff --git a/test/compile_errors/stage1/obj/const_frame_cast_to_anyframe.zig b/test/compile_errors/stage1/obj/const_frame_cast_to_anyframe.zig new file mode 100644 index 0000000000..1e6cdba507 --- /dev/null +++ b/test/compile_errors/stage1/obj/const_frame_cast_to_anyframe.zig @@ -0,0 +1,17 @@ +export fn a() void { + const f = async func(); + resume f; +} +export fn b() void { + const f = async func(); + var x: anyframe = &f; + _ = x; +} +fn func() void { + suspend {} +} + +// const frame cast to anyframe +// +// tmp.zig:3:12: error: expected type 'anyframe', found '*const @Frame(func)' +// tmp.zig:7:24: error: expected type 'anyframe', found '*const @Frame(func)' diff --git a/test/compile_errors/stage1/obj/const_is_a_statement_not_an_expression.zig b/test/compile_errors/stage1/obj/const_is_a_statement_not_an_expression.zig new file mode 100644 index 0000000000..4396407dd1 --- /dev/null +++ b/test/compile_errors/stage1/obj/const_is_a_statement_not_an_expression.zig @@ -0,0 +1,7 @@ +export fn f() void { + (const a = 0); +} + +// const is a statement, not an expression +// +// tmp.zig:2:6: error: expected expression, found 'const' diff --git a/test/compile_errors/stage1/obj/constant_inside_comptime_function_has_compile_error.zig b/test/compile_errors/stage1/obj/constant_inside_comptime_function_has_compile_error.zig new file mode 100644 index 0000000000..8eabcfdeaa --- /dev/null +++ b/test/compile_errors/stage1/obj/constant_inside_comptime_function_has_compile_error.zig @@ -0,0 +1,19 @@ +const ContextAllocator = MemoryPool(usize); + +pub fn MemoryPool(comptime T: type) type { + const free_list_t = @compileError("aoeu",); + + return struct { + free_list: free_list_t, + }; +} + +export fn entry() void { + var allocator: ContextAllocator = undefined; +} + +// constant inside comptime function has compile error +// +// tmp.zig:4:5: error: unreachable code +// tmp.zig:4:25: note: control flow is diverted here +// tmp.zig:12:9: error: unused local variable diff --git a/test/compile_errors/stage1/obj/container_init_with_non-type.zig b/test/compile_errors/stage1/obj/container_init_with_non-type.zig new file mode 100644 index 0000000000..6464555df5 --- /dev/null +++ b/test/compile_errors/stage1/obj/container_init_with_non-type.zig @@ -0,0 +1,8 @@ +const zero: i32 = 0; +const a = zero{1}; + +export fn entry() usize { return @sizeOf(@TypeOf(a)); } + +// container init with non-type +// +// tmp.zig:2:11: error: expected type 'type', found 'i32' diff --git a/test/compile_errors/stage1/obj/control_flow_uses_comptime_var_at_runtime.zig b/test/compile_errors/stage1/obj/control_flow_uses_comptime_var_at_runtime.zig new file mode 100644 index 0000000000..ee2c0234c3 --- /dev/null +++ b/test/compile_errors/stage1/obj/control_flow_uses_comptime_var_at_runtime.zig @@ -0,0 +1,13 @@ +export fn foo() void { + comptime var i = 0; + while (i < 5) : (i += 1) { + bar(); + } +} + +fn bar() void { } + +// control flow uses comptime var at runtime +// +// tmp.zig:3:5: error: control flow attempts to use compile-time variable at runtime +// tmp.zig:3:24: note: compile-time variable assigned here diff --git a/test/compile_errors/stage1/obj/control_reaches_end_of_non-void_function.zig b/test/compile_errors/stage1/obj/control_reaches_end_of_non-void_function.zig new file mode 100644 index 0000000000..13d2876823 --- /dev/null +++ b/test/compile_errors/stage1/obj/control_reaches_end_of_non-void_function.zig @@ -0,0 +1,6 @@ +fn a() i32 {} +export fn entry() void { _ = a(); } + +// control reaches end of non-void function +// +// tmp.zig:1:12: error: expected type 'i32', found 'void' diff --git a/test/compile_errors/stage1/obj/declaration_between_fields.zig b/test/compile_errors/stage1/obj/declaration_between_fields.zig new file mode 100644 index 0000000000..6e6b60f2e4 --- /dev/null +++ b/test/compile_errors/stage1/obj/declaration_between_fields.zig @@ -0,0 +1,22 @@ +const S = struct { + const foo = 2; + const bar = 2; + const baz = 2; + a: struct { + a: u32, + b: u32, + }, + const foo1 = 2; + const bar1 = 2; + const baz1 = 2; + b: usize, +}; +comptime { + _ = S; +} + +// declaration between fields +// +// tmp.zig:9:5: error: declarations are not allowed between container fields +// tmp.zig:5:5: note: field before declarations here +// tmp.zig:12:5: note: field after declarations here diff --git a/test/compile_errors/stage1/obj/declaration_with_same_name_as_primitive_must_use_special_syntax.zig b/test/compile_errors/stage1/obj/declaration_with_same_name_as_primitive_must_use_special_syntax.zig new file mode 100644 index 0000000000..c10f397da4 --- /dev/null +++ b/test/compile_errors/stage1/obj/declaration_with_same_name_as_primitive_must_use_special_syntax.zig @@ -0,0 +1,10 @@ +const u8 = u16; +export fn entry() void { + const a: u8 = 300; + _ = a; +} + +// declaration with same name as primitive must use special syntax +// +// tmp.zig:1:7: error: name shadows primitive 'u8' +// tmp.zig:1:7: note: consider using @"u8" to disambiguate diff --git a/test/compile_errors/stage1/obj/deduplicate_undeclared_identifier.zig b/test/compile_errors/stage1/obj/deduplicate_undeclared_identifier.zig new file mode 100644 index 0000000000..1c9d6c3d31 --- /dev/null +++ b/test/compile_errors/stage1/obj/deduplicate_undeclared_identifier.zig @@ -0,0 +1,10 @@ +export fn a() void { + x += 1; +} +export fn b() void { + x += 1; +} + +// deduplicate undeclared identifier +// +// tmp.zig:2:5: error: use of undeclared identifier 'x' diff --git a/test/compile_errors/stage1/obj/deref_on_undefined_value.zig b/test/compile_errors/stage1/obj/deref_on_undefined_value.zig new file mode 100644 index 0000000000..607f6ea5e0 --- /dev/null +++ b/test/compile_errors/stage1/obj/deref_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: *u8 = undefined; + _ = a.*; +} + +// deref on undefined value +// +// tmp.zig:3:9: error: attempt to dereference undefined value diff --git a/test/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig b/test/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig new file mode 100644 index 0000000000..2fa5ff6133 --- /dev/null +++ b/test/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig @@ -0,0 +1,8 @@ +export fn entry() void { + var a: []u8 = undefined; + _ = a.*.len; +} + +// deref slice and get len field +// +// tmp.zig:3:10: error: attempt to dereference non-pointer type '[]u8' diff --git a/test/compile_errors/stage1/obj/dereference_an_array.zig b/test/compile_errors/stage1/obj/dereference_an_array.zig new file mode 100644 index 0000000000..713c655784 --- /dev/null +++ b/test/compile_errors/stage1/obj/dereference_an_array.zig @@ -0,0 +1,12 @@ +var s_buffer: [10]u8 = undefined; +pub fn pass(in: []u8) []u8 { + var out = &s_buffer; + out.*.* = in[0]; + return out.*[0..1]; +} + +export fn entry() usize { return @sizeOf(@TypeOf(pass)); } + +// dereference an array +// +// tmp.zig:4:10: error: attempt to dereference non-pointer type '[10]u8' diff --git a/test/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig b/test/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig new file mode 100644 index 0000000000..22ad181fb2 --- /dev/null +++ b/test/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig @@ -0,0 +1,7 @@ +export fn entry(x: [*]i32) i32 { + return x.*; +} + +// dereference unknown length pointer +// +// tmp.zig:2:13: error: index syntax required for unknown-length pointer type '[*]i32' diff --git a/test/compile_errors/stage1/obj/direct_struct_loop.zig b/test/compile_errors/stage1/obj/direct_struct_loop.zig new file mode 100644 index 0000000000..25b3c724c3 --- /dev/null +++ b/test/compile_errors/stage1/obj/direct_struct_loop.zig @@ -0,0 +1,6 @@ +const A = struct { a : A, }; +export fn entry() usize { return @sizeOf(A); } + +// direct struct loop +// +// tmp.zig:1:11: error: struct 'A' depends on itself diff --git a/test/compile_errors/stage1/obj/directly_embedding_opaque_type_in_struct_and_union.zig b/test/compile_errors/stage1/obj/directly_embedding_opaque_type_in_struct_and_union.zig new file mode 100644 index 0000000000..811fc00f50 --- /dev/null +++ b/test/compile_errors/stage1/obj/directly_embedding_opaque_type_in_struct_and_union.zig @@ -0,0 +1,27 @@ +const O = opaque {}; +const Foo = struct { + o: O, +}; +const Bar = union { + One: i32, + Two: O, +}; +export fn a() void { + var foo: Foo = undefined; + _ = foo; +} +export fn b() void { + var bar: Bar = undefined; + _ = bar; +} +export fn c() void { + var baz: *opaque {} = undefined; + const qux = .{baz.*}; + _ = qux; +} + +// directly embedding opaque type in struct and union +// +// tmp.zig:3:5: error: opaque types have unknown size and therefore cannot be directly embedded in structs +// tmp.zig:7:5: error: opaque types have unknown size and therefore cannot be directly embedded in unions +// tmp.zig:19:22: error: opaque types have unknown size and therefore cannot be directly embedded in structs diff --git a/test/compile_errors/stage1/obj/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig b/test/compile_errors/stage1/obj/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig new file mode 100644 index 0000000000..a8467ddc87 --- /dev/null +++ b/test/compile_errors/stage1/obj/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig @@ -0,0 +1,10 @@ +extern fn puts(s: [*:0]const u8) c_int; +pub fn main() void { + const no_zero_array = [_]u8{'h', 'e', 'l', 'l', 'o'}; + const no_zero_ptr: [*]const u8 = &no_zero_array; + _ = puts(no_zero_ptr); +} + +// disallow coercion from non-null-terminated pointer to null-terminated pointer +// +// tmp.zig:5:14: error: expected type '[*:0]const u8', found '[*]const u8' diff --git a/test/compile_errors/stage1/obj/discarding_error_value.zig b/test/compile_errors/stage1/obj/discarding_error_value.zig new file mode 100644 index 0000000000..966a43a47c --- /dev/null +++ b/test/compile_errors/stage1/obj/discarding_error_value.zig @@ -0,0 +1,10 @@ +export fn entry() void { + _ = foo(); +} +fn foo() !void { + return error.OutOfMemory; +} + +// discarding error value +// +// tmp.zig:2:12: error: error is discarded. consider using `try`, `catch`, or `if` diff --git a/test/compile_errors/stage1/obj/div_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/div_assign_on_undefined_value.zig new file mode 100644 index 0000000000..40c31649fb --- /dev/null +++ b/test/compile_errors/stage1/obj/div_assign_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + a /= a; +} + +// div assign on undefined value +// +// tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/div_on_undefined_value.zig b/test/compile_errors/stage1/obj/div_on_undefined_value.zig new file mode 100644 index 0000000000..17af3fa221 --- /dev/null +++ b/test/compile_errors/stage1/obj/div_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + _ = a / a; +} + +// div on undefined value +// +// tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/division_by_zero.zig b/test/compile_errors/stage1/obj/division_by_zero.zig new file mode 100644 index 0000000000..178d1d935b --- /dev/null +++ b/test/compile_errors/stage1/obj/division_by_zero.zig @@ -0,0 +1,16 @@ +const lit_int_x = 1 / 0; +const lit_float_x = 1.0 / 0.0; +const int_x = @as(u32, 1) / @as(u32, 0); +const float_x = @as(f32, 1.0) / @as(f32, 0.0); + +export fn entry1() usize { return @sizeOf(@TypeOf(lit_int_x)); } +export fn entry2() usize { return @sizeOf(@TypeOf(lit_float_x)); } +export fn entry3() usize { return @sizeOf(@TypeOf(int_x)); } +export fn entry4() usize { return @sizeOf(@TypeOf(float_x)); } + +// division by zero +// +// tmp.zig:1:21: error: division by zero +// tmp.zig:2:25: error: division by zero +// tmp.zig:3:27: error: division by zero +// tmp.zig:4:31: error: division by zero diff --git a/test/compile_errors/stage1/obj/dont_implicit_cast_double_pointer_to_anyopaque.zig b/test/compile_errors/stage1/obj/dont_implicit_cast_double_pointer_to_anyopaque.zig new file mode 100644 index 0000000000..5894103f1c --- /dev/null +++ b/test/compile_errors/stage1/obj/dont_implicit_cast_double_pointer_to_anyopaque.zig @@ -0,0 +1,11 @@ +export fn entry() void { + var a: u32 = 1; + var ptr: *align(@alignOf(u32)) anyopaque = &a; + var b: *u32 = @ptrCast(*u32, ptr); + var ptr2: *anyopaque = &b; + _ = ptr2; +} + +// don't implicit cast double pointer to *anyopaque +// +// tmp.zig:5:29: error: expected type '*anyopaque', found '**u32' diff --git a/test/compile_errors/stage1/obj/double_optional_on_main_return_value.zig b/test/compile_errors/stage1/obj/double_optional_on_main_return_value.zig new file mode 100644 index 0000000000..c85706bc87 --- /dev/null +++ b/test/compile_errors/stage1/obj/double_optional_on_main_return_value.zig @@ -0,0 +1,6 @@ +pub fn main() ??void { +} + +// double ?? on main return value +// +// error: expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8' diff --git a/test/compile_errors/stage1/obj/duplicate_boolean_switch_value.zig b/test/compile_errors/stage1/obj/duplicate_boolean_switch_value.zig new file mode 100644 index 0000000000..8c496d5e19 --- /dev/null +++ b/test/compile_errors/stage1/obj/duplicate_boolean_switch_value.zig @@ -0,0 +1,21 @@ +comptime { + const x = switch (true) { + true => false, + false => true, + true => false, + }; + _ = x; +} +comptime { + const x = switch (true) { + false => true, + true => false, + false => true, + }; + _ = x; +} + +// duplicate boolean switch value +// +// tmp.zig:5:9: error: duplicate switch value +// tmp.zig:13:9: error: duplicate switch value diff --git a/test/compile_errors/stage1/obj/duplicate_enum_field.zig b/test/compile_errors/stage1/obj/duplicate_enum_field.zig new file mode 100644 index 0000000000..2aee3cc2dc --- /dev/null +++ b/test/compile_errors/stage1/obj/duplicate_enum_field.zig @@ -0,0 +1,14 @@ +const Foo = enum { + Bar, + Bar, +}; + +export fn entry() void { + const a: Foo = undefined; + _ = a; +} + +// duplicate enum field +// +// tmp.zig:3:5: error: duplicate enum field: 'Bar' +// tmp.zig:2:5: note: other field here diff --git a/test/compile_errors/stage1/obj/duplicate_error_in_switch.zig b/test/compile_errors/stage1/obj/duplicate_error_in_switch.zig new file mode 100644 index 0000000000..bca59056c3 --- /dev/null +++ b/test/compile_errors/stage1/obj/duplicate_error_in_switch.zig @@ -0,0 +1,20 @@ +export fn entry() void { + foo(452) catch |err| switch (err) { + error.Foo => {}, + error.Bar => {}, + error.Foo => {}, + else => {}, + }; +} +fn foo(x: i32) !void { + switch (x) { + 0 ... 10 => return error.Foo, + 11 ... 20 => return error.Bar, + else => {}, + } +} + +// duplicate error in switch +// +// tmp.zig:5:14: error: duplicate switch value: '@typeInfo(@typeInfo(@TypeOf(foo)).Fn.return_type.?).ErrorUnion.error_set.Foo' +// tmp.zig:3:14: note: other value here diff --git a/test/compile_errors/stage1/obj/duplicate_error_value_in_error_set.zig b/test/compile_errors/stage1/obj/duplicate_error_value_in_error_set.zig new file mode 100644 index 0000000000..90457562b2 --- /dev/null +++ b/test/compile_errors/stage1/obj/duplicate_error_value_in_error_set.zig @@ -0,0 +1,13 @@ +const Foo = error { + Bar, + Bar, +}; +export fn entry() void { + const a: Foo = undefined; + _ = a; +} + +// duplicate error value in error set +// +// tmp.zig:3:5: error: duplicate error set field 'Bar' +// tmp.zig:2:5: note: previous declaration here diff --git a/test/compile_errors/stage1/obj/duplicate_field_in_struct_value_expression.zig b/test/compile_errors/stage1/obj/duplicate_field_in_struct_value_expression.zig new file mode 100644 index 0000000000..6df71430d9 --- /dev/null +++ b/test/compile_errors/stage1/obj/duplicate_field_in_struct_value_expression.zig @@ -0,0 +1,18 @@ +const A = struct { + x : i32, + y : i32, + z : i32, +}; +export fn f() void { + const a = A { + .z = 1, + .y = 2, + .x = 3, + .z = 4, + }; + _ = a; +} + +// duplicate field in struct value expression +// +// tmp.zig:11:9: error: duplicate field diff --git a/test/compile_errors/stage1/obj/duplicate_struct_field.zig b/test/compile_errors/stage1/obj/duplicate_struct_field.zig new file mode 100644 index 0000000000..d1fd05aacc --- /dev/null +++ b/test/compile_errors/stage1/obj/duplicate_struct_field.zig @@ -0,0 +1,13 @@ +const Foo = struct { + Bar: i32, + Bar: usize, +}; +export fn entry() void { + const a: Foo = undefined; + _ = a; +} + +// duplicate struct field +// +// tmp.zig:3:5: error: duplicate struct field: 'Bar' +// tmp.zig:2:5: note: other field here diff --git a/test/compile_errors/stage1/obj/duplicate_union_field.zig b/test/compile_errors/stage1/obj/duplicate_union_field.zig new file mode 100644 index 0000000000..a8bff106c6 --- /dev/null +++ b/test/compile_errors/stage1/obj/duplicate_union_field.zig @@ -0,0 +1,13 @@ +const Foo = union { + Bar: i32, + Bar: usize, +}; +export fn entry() void { + const a: Foo = undefined; + _ = a; +} + +// duplicate union field +// +// tmp.zig:3:5: error: duplicate union field: 'Bar' +// tmp.zig:2:5: note: other field here diff --git a/test/compile_errors/stage1/obj/embedFile_with_bogus_file.zig b/test/compile_errors/stage1/obj/embedFile_with_bogus_file.zig new file mode 100644 index 0000000000..e50e091909 --- /dev/null +++ b/test/compile_errors/stage1/obj/embedFile_with_bogus_file.zig @@ -0,0 +1,7 @@ +const resource = @embedFile("bogus.txt",); + +export fn entry() usize { return @sizeOf(@TypeOf(resource)); } + +// @embedFile with bogus file +// +// tmp.zig:1:29: error: unable to find ' diff --git a/test/compile_errors/stage1/obj/empty_for_loop_body.zig b/test/compile_errors/stage1/obj/empty_for_loop_body.zig new file mode 100644 index 0000000000..6e042f71e4 --- /dev/null +++ b/test/compile_errors/stage1/obj/empty_for_loop_body.zig @@ -0,0 +1,7 @@ +export fn a() void { + for(undefined) |x|; +} + +// empty for loop body +// +// tmp.zig:2:23: error: expected block or assignment, found ';' diff --git a/test/compile_errors/stage1/obj/empty_if_body.zig b/test/compile_errors/stage1/obj/empty_if_body.zig new file mode 100644 index 0000000000..a81973396f --- /dev/null +++ b/test/compile_errors/stage1/obj/empty_if_body.zig @@ -0,0 +1,7 @@ +export fn a() void { + if(true); +} + +// empty if body +// +// tmp.zig:2:13: error: expected block or assignment, found ';' diff --git a/test/compile_errors/stage1/obj/empty_switch_on_an_integer.zig b/test/compile_errors/stage1/obj/empty_switch_on_an_integer.zig new file mode 100644 index 0000000000..eb0bdbab19 --- /dev/null +++ b/test/compile_errors/stage1/obj/empty_switch_on_an_integer.zig @@ -0,0 +1,8 @@ +export fn entry() void { + var x: u32 = 0; + switch(x) {} +} + +// empty switch on an integer +// +// tmp.zig:3:5: error: switch must handle all possibilities diff --git a/test/compile_errors/stage1/obj/empty_while_loop_body.zig b/test/compile_errors/stage1/obj/empty_while_loop_body.zig new file mode 100644 index 0000000000..3a186f1a2b --- /dev/null +++ b/test/compile_errors/stage1/obj/empty_while_loop_body.zig @@ -0,0 +1,7 @@ +export fn a() void { + while(true); +} + +// empty while loop body +// +// tmp.zig:2:16: error: expected block or assignment, found ';' diff --git a/test/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig b/test/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig new file mode 100644 index 0000000000..fa422ba588 --- /dev/null +++ b/test/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig @@ -0,0 +1,10 @@ +const seventh_fib_number = fibonacci(7); +fn fibonacci(x: i32) i32 { + return fibonacci(x - 1) + fibonacci(x - 2); +} + +export fn entry() usize { return @sizeOf(@TypeOf(seventh_fib_number)); } + +// endless loop in function evaluation +// +// tmp.zig:3:21: error: evaluation exceeded 1000 backwards branches diff --git a/test/compile_errors/stage1/obj/enum_field_value_references_enum.zig b/test/compile_errors/stage1/obj/enum_field_value_references_enum.zig new file mode 100644 index 0000000000..02dcfc1f9d --- /dev/null +++ b/test/compile_errors/stage1/obj/enum_field_value_references_enum.zig @@ -0,0 +1,13 @@ +pub const Foo = enum(c_int) { + A = Foo.B, + C = D, +}; +export fn entry() void { + var s: Foo = Foo.E; + _ = s; +} +const D = 1; + +// enum field value references enum +// +// tmp.zig:1:17: error: enum 'Foo' depends on itself diff --git a/test/compile_errors/stage1/obj/enum_in_field_count_range_but_not_matching_tag.zig b/test/compile_errors/stage1/obj/enum_in_field_count_range_but_not_matching_tag.zig new file mode 100644 index 0000000000..dc90c467df --- /dev/null +++ b/test/compile_errors/stage1/obj/enum_in_field_count_range_but_not_matching_tag.zig @@ -0,0 +1,13 @@ +const Foo = enum(u32) { + A = 10, + B = 11, +}; +export fn entry() void { + var x = @intToEnum(Foo, 0); + _ = x; +} + +// enum in field count range but not matching tag +// +// tmp.zig:6:13: error: enum 'Foo' has no tag matching integer value 0 +// tmp.zig:1:13: note: 'Foo' declared here diff --git a/test/compile_errors/stage1/obj/enum_value_already_taken.zig b/test/compile_errors/stage1/obj/enum_value_already_taken.zig new file mode 100644 index 0000000000..9939553c41 --- /dev/null +++ b/test/compile_errors/stage1/obj/enum_value_already_taken.zig @@ -0,0 +1,16 @@ +const MultipleChoice = enum(u32) { + A = 20, + B = 40, + C = 60, + D = 1000, + E = 60, +}; +export fn entry() void { + var x = MultipleChoice.C; + _ = x; +} + +// enum value already taken +// +// tmp.zig:6:5: error: enum tag value 60 already taken +// tmp.zig:4:5: note: other occurrence here diff --git a/test/compile_errors/stage1/obj/enum_with_0_fields.zig b/test/compile_errors/stage1/obj/enum_with_0_fields.zig new file mode 100644 index 0000000000..10559d15b3 --- /dev/null +++ b/test/compile_errors/stage1/obj/enum_with_0_fields.zig @@ -0,0 +1,5 @@ +const Foo = enum {}; + +// enum with 0 fields +// +// tmp.zig:1:13: error: enum declarations must have at least one tag diff --git a/test/compile_errors/stage1/obj/enum_with_declarations_unavailable_for_reify_type.zig b/test/compile_errors/stage1/obj/enum_with_declarations_unavailable_for_reify_type.zig new file mode 100644 index 0000000000..4a27caab3d --- /dev/null +++ b/test/compile_errors/stage1/obj/enum_with_declarations_unavailable_for_reify_type.zig @@ -0,0 +1,7 @@ +export fn entry() void { + _ = @Type(@typeInfo(enum { foo, const bar = 1; })); +} + +// enum with declarations unavailable for @Type +// +// tmp.zig:2:15: error: Type.Enum.decls must be empty for @Type diff --git a/test/compile_errors/stage1/obj/error_equality_but_sets_have_no_common_members.zig b/test/compile_errors/stage1/obj/error_equality_but_sets_have_no_common_members.zig new file mode 100644 index 0000000000..433f6d50e3 --- /dev/null +++ b/test/compile_errors/stage1/obj/error_equality_but_sets_have_no_common_members.zig @@ -0,0 +1,14 @@ +const Set1 = error{A, C}; +const Set2 = error{B, D}; +export fn entry() void { + foo(Set1.A); +} +fn foo(x: Set1) void { + if (x == Set2.B) { + + } +} + +// error equality but sets have no common members +// +// tmp.zig:7:11: error: error sets 'Set1' and 'Set2' have no common errors diff --git a/test/compile_errors/stage1/obj/error_not_handled_in_switch.zig b/test/compile_errors/stage1/obj/error_not_handled_in_switch.zig new file mode 100644 index 0000000000..a69c538eac --- /dev/null +++ b/test/compile_errors/stage1/obj/error_not_handled_in_switch.zig @@ -0,0 +1,18 @@ +export fn entry() void { + foo(452) catch |err| switch (err) { + error.Foo => {}, + }; +} +fn foo(x: i32) !void { + switch (x) { + 0 ... 10 => return error.Foo, + 11 ... 20 => return error.Bar, + 21 ... 30 => return error.Baz, + else => {}, + } +} + +// error not handled in switch +// +// tmp.zig:2:26: error: error.Baz not handled in switch +// tmp.zig:2:26: error: error.Bar not handled in switch diff --git a/test/compile_errors/stage1/obj/error_note_for_function_parameter_incompatibility.zig b/test/compile_errors/stage1/obj/error_note_for_function_parameter_incompatibility.zig new file mode 100644 index 0000000000..5014d01a3d --- /dev/null +++ b/test/compile_errors/stage1/obj/error_note_for_function_parameter_incompatibility.zig @@ -0,0 +1,10 @@ +fn do_the_thing(func: fn (arg: i32) void) void { _ = func; } +fn bar(arg: bool) void { _ = arg; } +export fn entry() void { + do_the_thing(bar); +} + +// error note for function parameter incompatibility +// +// tmp.zig:4:18: error: expected type 'fn(i32) void', found 'fn(bool) void +// tmp.zig:4:18: note: parameter 0: 'bool' cannot cast into 'i32' diff --git a/test/compile_errors/stage1/obj/error_union_operator_with_non_error_set_LHS.zig b/test/compile_errors/stage1/obj/error_union_operator_with_non_error_set_LHS.zig new file mode 100644 index 0000000000..40b5872fc8 --- /dev/null +++ b/test/compile_errors/stage1/obj/error_union_operator_with_non_error_set_LHS.zig @@ -0,0 +1,9 @@ +comptime { + const z = i32!i32; + var x: z = undefined; + _ = x; +} + +// error union operator with non error set LHS +// +// tmp.zig:2:15: error: expected error set type, found type 'i32' diff --git a/test/compile_errors/stage1/obj/error_when_evaluating_return_type.zig b/test/compile_errors/stage1/obj/error_when_evaluating_return_type.zig new file mode 100644 index 0000000000..60baa1c5b4 --- /dev/null +++ b/test/compile_errors/stage1/obj/error_when_evaluating_return_type.zig @@ -0,0 +1,15 @@ +const Foo = struct { + map: @as(i32, i32), + + fn init() Foo { + return undefined; + } +}; +export fn entry() void { + var rule_set = try Foo.init(); + _ = rule_set; +} + +// error when evaluating return type +// +// tmp.zig:2:19: error: expected type 'i32', found 'type' diff --git a/test/compile_errors/stage1/obj/exceeded_maximum_bit_width_of_integer.zig b/test/compile_errors/stage1/obj/exceeded_maximum_bit_width_of_integer.zig new file mode 100644 index 0000000000..e1784de63e --- /dev/null +++ b/test/compile_errors/stage1/obj/exceeded_maximum_bit_width_of_integer.zig @@ -0,0 +1,13 @@ +export fn entry1() void { + const T = u65536; + _ = T; +} +export fn entry2() void { + var x: i65536 = 1; + _ = x; +} + +// exceeded maximum bit width of integer +// +// tmp.zig:2:15: error: primitive integer type 'u65536' exceeds maximum bit width of 65535 +// tmp.zig:6:12: error: primitive integer type 'i65536' exceeds maximum bit width of 65535 diff --git a/test/compile_errors/stage1/obj/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig b/test/compile_errors/stage1/obj/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig new file mode 100644 index 0000000000..eaa2be84c7 --- /dev/null +++ b/test/compile_errors/stage1/obj/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig @@ -0,0 +1,7 @@ +export fn entry() i32 { + return @as(i32, 12.34); +} + +// explicit cast float literal to integer when there is a fraction component +// +// tmp.zig:2:21: error: fractional component prevents float value 12.340000 from being casted to type 'i32' diff --git a/test/compile_errors/stage1/obj/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig b/test/compile_errors/stage1/obj/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig new file mode 100644 index 0000000000..68d78e0f92 --- /dev/null +++ b/test/compile_errors/stage1/obj/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig @@ -0,0 +1,11 @@ +const Set1 = error {A, B}; +const Set2 = error {A, C}; +comptime { + var x = Set1.B; + var y = @errSetCast(Set2, x); + _ = y; +} + +// explicit error set cast known at comptime violates error sets +// +// tmp.zig:5:13: error: error.B not a member of error set 'Set2' diff --git a/test/compile_errors/stage1/obj/explicitly_casting_non_tag_type_to_enum.zig b/test/compile_errors/stage1/obj/explicitly_casting_non_tag_type_to_enum.zig new file mode 100644 index 0000000000..e7c4d5f5d9 --- /dev/null +++ b/test/compile_errors/stage1/obj/explicitly_casting_non_tag_type_to_enum.zig @@ -0,0 +1,16 @@ +const Small = enum(u2) { + One, + Two, + Three, + Four, +}; + +export fn entry() void { + var y = @as(f32, 3); + var x = @intToEnum(Small, y); + _ = x; +} + +// explicitly casting non tag type to enum +// +// tmp.zig:10:31: error: expected integer type, found 'f32' diff --git a/test/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig b/test/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig new file mode 100644 index 0000000000..0f300f324b --- /dev/null +++ b/test/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig @@ -0,0 +1,7 @@ +export fn foo(comptime x: i32, y: i32) i32{ + return x + y; +} + +// export function with comptime parameter +// +// tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'C' diff --git a/test/compile_errors/stage1/obj/export_generic_function.zig b/test/compile_errors/stage1/obj/export_generic_function.zig new file mode 100644 index 0000000000..3fb4375ed4 --- /dev/null +++ b/test/compile_errors/stage1/obj/export_generic_function.zig @@ -0,0 +1,8 @@ +export fn foo(num: anytype) i32 { + _ = num; + return 0; +} + +// export generic function +// +// tmp.zig:1:15: error: parameter of type 'anytype' not allowed in function with calling convention 'C' diff --git a/test/compile_errors/stage1/obj/exported_async_function.zig b/test/compile_errors/stage1/obj/exported_async_function.zig new file mode 100644 index 0000000000..296a5950e9 --- /dev/null +++ b/test/compile_errors/stage1/obj/exported_async_function.zig @@ -0,0 +1,5 @@ +export fn foo() callconv(.Async) void {} + +// exported async function +// +// tmp.zig:1:1: error: exported function cannot be async diff --git a/test/compile_errors/stage1/obj/exported_enum_without_explicit_integer_tag_type.zig b/test/compile_errors/stage1/obj/exported_enum_without_explicit_integer_tag_type.zig new file mode 100644 index 0000000000..3d6e63db43 --- /dev/null +++ b/test/compile_errors/stage1/obj/exported_enum_without_explicit_integer_tag_type.zig @@ -0,0 +1,13 @@ +const E = enum { one, two }; +comptime { + @export(E, .{ .name = "E" }); +} +const e: E = .two; +comptime { + @export(e, .{ .name = "e" }); +} + +// exported enum without explicit integer tag type +// +// tmp.zig:3:13: error: exported enum without explicit integer tag type +// tmp.zig:7:13: error: exported enum value without explicit integer tag type diff --git a/test/compile_errors/stage1/obj/extern_function_pointer_mismatch.zig b/test/compile_errors/stage1/obj/extern_function_pointer_mismatch.zig new file mode 100644 index 0000000000..f93a24db34 --- /dev/null +++ b/test/compile_errors/stage1/obj/extern_function_pointer_mismatch.zig @@ -0,0 +1,10 @@ +const fns = [_](fn(i32)i32) { a, b, c }; +pub fn a(x: i32) i32 {return x + 0;} +pub fn b(x: i32) i32 {return x + 1;} +export fn c(x: i32) i32 {return x + 2;} + +export fn entry() usize { return @sizeOf(@TypeOf(fns)); } + +// extern function pointer mismatch +// +// tmp.zig:1:37: error: expected type 'fn(i32) i32', found 'fn(i32) callconv(.C) i32' diff --git a/test/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig b/test/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig new file mode 100644 index 0000000000..3dfdfe663e --- /dev/null +++ b/test/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig @@ -0,0 +1,9 @@ +extern fn foo(comptime x: i32, y: i32) i32; +fn f() i32 { + return foo(1, 2); +} +export fn entry() usize { return @sizeOf(@TypeOf(f)); } + +// extern function with comptime parameter +// +// tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'C' diff --git a/test/compile_errors/stage1/obj/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig b/test/compile_errors/stage1/obj/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig new file mode 100644 index 0000000000..e65c438da3 --- /dev/null +++ b/test/compile_errors/stage1/obj/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig @@ -0,0 +1,41 @@ +pub const E = enum { +@"0",@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"10",@"11",@"12", +@"13",@"14",@"15",@"16",@"17",@"18",@"19",@"20",@"21",@"22",@"23", +@"24",@"25",@"26",@"27",@"28",@"29",@"30",@"31",@"32",@"33",@"34", +@"35",@"36",@"37",@"38",@"39",@"40",@"41",@"42",@"43",@"44",@"45", +@"46",@"47",@"48",@"49",@"50",@"51",@"52",@"53",@"54",@"55",@"56", +@"57",@"58",@"59",@"60",@"61",@"62",@"63",@"64",@"65",@"66",@"67", +@"68",@"69",@"70",@"71",@"72",@"73",@"74",@"75",@"76",@"77",@"78", +@"79",@"80",@"81",@"82",@"83",@"84",@"85",@"86",@"87",@"88",@"89", +@"90",@"91",@"92",@"93",@"94",@"95",@"96",@"97",@"98",@"99",@"100", +@"101",@"102",@"103",@"104",@"105",@"106",@"107",@"108",@"109", +@"110",@"111",@"112",@"113",@"114",@"115",@"116",@"117",@"118", +@"119",@"120",@"121",@"122",@"123",@"124",@"125",@"126",@"127", +@"128",@"129",@"130",@"131",@"132",@"133",@"134",@"135",@"136", +@"137",@"138",@"139",@"140",@"141",@"142",@"143",@"144",@"145", +@"146",@"147",@"148",@"149",@"150",@"151",@"152",@"153",@"154", +@"155",@"156",@"157",@"158",@"159",@"160",@"161",@"162",@"163", +@"164",@"165",@"166",@"167",@"168",@"169",@"170",@"171",@"172", +@"173",@"174",@"175",@"176",@"177",@"178",@"179",@"180",@"181", +@"182",@"183",@"184",@"185",@"186",@"187",@"188",@"189",@"190", +@"191",@"192",@"193",@"194",@"195",@"196",@"197",@"198",@"199", +@"200",@"201",@"202",@"203",@"204",@"205",@"206",@"207",@"208", +@"209",@"210",@"211",@"212",@"213",@"214",@"215",@"216",@"217", +@"218",@"219",@"220",@"221",@"222",@"223",@"224",@"225",@"226", +@"227",@"228",@"229",@"230",@"231",@"232",@"233",@"234",@"235", +@"236",@"237",@"238",@"239",@"240",@"241",@"242",@"243",@"244", +@"245",@"246",@"247",@"248",@"249",@"250",@"251",@"252",@"253", +@"254",@"255" +}; +pub const S = extern struct { + e: E, +}; +export fn entry() void { + if (@typeInfo(E).Enum.tag_type != u8) @compileError("did not infer u8 tag type"); + const s: S = undefined; + _ = s; +} + +// extern struct with extern-compatible but inferred integer tag type +// +// tmp.zig:31:5: error: extern structs cannot contain fields of type 'E' diff --git a/test/compile_errors/stage1/obj/extern_struct_with_non-extern-compatible_integer_tag_type.zig b/test/compile_errors/stage1/obj/extern_struct_with_non-extern-compatible_integer_tag_type.zig new file mode 100644 index 0000000000..6d698ce8e3 --- /dev/null +++ b/test/compile_errors/stage1/obj/extern_struct_with_non-extern-compatible_integer_tag_type.zig @@ -0,0 +1,12 @@ +pub const E = enum(u31) { A, B, C }; +pub const S = extern struct { + e: E, +}; +export fn entry() void { + const s: S = undefined; + _ = s; +} + +// extern struct with non-extern-compatible integer tag type +// +// tmp.zig:3:5: error: extern structs cannot contain fields of type 'E' diff --git a/test/compile_errors/stage1/obj/extern_union_field_missing_type.zig b/test/compile_errors/stage1/obj/extern_union_field_missing_type.zig new file mode 100644 index 0000000000..1287ce0159 --- /dev/null +++ b/test/compile_errors/stage1/obj/extern_union_field_missing_type.zig @@ -0,0 +1,11 @@ +const Letter = extern union { + A, +}; +export fn entry() void { + var a = Letter { .A = {} }; + _ = a; +} + +// extern union field missing type +// +// tmp.zig:2:5: error: union field missing type diff --git a/test/compile_errors/stage1/obj/extern_union_given_enum_tag_type.zig b/test/compile_errors/stage1/obj/extern_union_given_enum_tag_type.zig new file mode 100644 index 0000000000..5b38d77eb6 --- /dev/null +++ b/test/compile_errors/stage1/obj/extern_union_given_enum_tag_type.zig @@ -0,0 +1,18 @@ +const Letter = enum { + A, + B, + C, +}; +const Payload = extern union(Letter) { + A: i32, + B: f64, + C: bool, +}; +export fn entry() void { + var a = Payload { .A = 1234 }; + _ = a; +} + +// extern union given enum tag type +// +// tmp.zig:6:30: error: extern union does not support enum tag type diff --git a/test/compile_errors/stage1/obj/extern_variable_has_no_type.zig b/test/compile_errors/stage1/obj/extern_variable_has_no_type.zig new file mode 100644 index 0000000000..f9d3952275 --- /dev/null +++ b/test/compile_errors/stage1/obj/extern_variable_has_no_type.zig @@ -0,0 +1,8 @@ +extern var foo; +pub export fn entry() void { + foo; +} + +// extern variable has no type +// +// tmp.zig:1:8: error: unable to infer variable type diff --git a/test/compile_errors/stage1/obj/fieldParentPtr-bad_field_name.zig b/test/compile_errors/stage1/obj/fieldParentPtr-bad_field_name.zig new file mode 100644 index 0000000000..8c40641113 --- /dev/null +++ b/test/compile_errors/stage1/obj/fieldParentPtr-bad_field_name.zig @@ -0,0 +1,10 @@ +const Foo = extern struct { + derp: i32, +}; +export fn foo(a: *i32) *Foo { + return @fieldParentPtr(Foo, "a", a); +} + +// @fieldParentPtr - bad field name +// +// tmp.zig:5:33: error: struct 'Foo' has no field 'a' diff --git a/test/compile_errors/stage1/obj/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig b/test/compile_errors/stage1/obj/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig new file mode 100644 index 0000000000..a8a5431352 --- /dev/null +++ b/test/compile_errors/stage1/obj/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig @@ -0,0 +1,15 @@ +const Foo = struct { + a: i32, + b: i32, +}; +const foo = Foo { .a = 1, .b = 2, }; + +comptime { + const field_ptr = @intToPtr(*i32, 0x1234); + const another_foo_ptr = @fieldParentPtr(Foo, "b", field_ptr); + _ = another_foo_ptr; +} + +// @fieldParentPtr - comptime field ptr not based on struct +// +// tmp.zig:9:55: error: pointer value not based on parent struct diff --git a/test/compile_errors/stage1/obj/fieldParentPtr-comptime_wrong_field_index.zig b/test/compile_errors/stage1/obj/fieldParentPtr-comptime_wrong_field_index.zig new file mode 100644 index 0000000000..11bb7282fc --- /dev/null +++ b/test/compile_errors/stage1/obj/fieldParentPtr-comptime_wrong_field_index.zig @@ -0,0 +1,14 @@ +const Foo = struct { + a: i32, + b: i32, +}; +const foo = Foo { .a = 1, .b = 2, }; + +comptime { + const another_foo_ptr = @fieldParentPtr(Foo, "b", &foo.a); + _ = another_foo_ptr; +} + +// @fieldParentPtr - comptime wrong field index +// +// tmp.zig:8:29: error: field 'b' has index 1 but pointer value is index 0 of struct 'Foo' diff --git a/test/compile_errors/stage1/obj/fieldParentPtr-field_pointer_is_not_pointer.zig b/test/compile_errors/stage1/obj/fieldParentPtr-field_pointer_is_not_pointer.zig new file mode 100644 index 0000000000..8db9075b7b --- /dev/null +++ b/test/compile_errors/stage1/obj/fieldParentPtr-field_pointer_is_not_pointer.zig @@ -0,0 +1,10 @@ +const Foo = extern struct { + a: i32, +}; +export fn foo(a: i32) *Foo { + return @fieldParentPtr(Foo, "a", a); +} + +// @fieldParentPtr - field pointer is not pointer +// +// tmp.zig:5:38: error: expected pointer, found 'i32' diff --git a/test/compile_errors/stage1/obj/fieldParentPtr-non_struct.zig b/test/compile_errors/stage1/obj/fieldParentPtr-non_struct.zig new file mode 100644 index 0000000000..325fbe5b3b --- /dev/null +++ b/test/compile_errors/stage1/obj/fieldParentPtr-non_struct.zig @@ -0,0 +1,8 @@ +const Foo = i32; +export fn foo(a: *i32) *Foo { + return @fieldParentPtr(Foo, "a", a); +} + +// @fieldParentPtr - non struct +// +// tmp.zig:3:28: error: expected struct type, found 'i32' diff --git a/test/compile_errors/stage1/obj/field_access_of_opaque_type.zig b/test/compile_errors/stage1/obj/field_access_of_opaque_type.zig new file mode 100644 index 0000000000..3354c0f471 --- /dev/null +++ b/test/compile_errors/stage1/obj/field_access_of_opaque_type.zig @@ -0,0 +1,14 @@ +const MyType = opaque {}; + +export fn entry() bool { + var x: i32 = 1; + return bar(@ptrCast(*MyType, &x)); +} + +fn bar(x: *MyType) bool { + return x.blah; +} + +// field access of opaque type +// +// tmp.zig:9:13: error: no member named 'blah' in opaque type 'MyType' diff --git a/test/compile_errors/stage1/obj/field_access_of_slices.zig b/test/compile_errors/stage1/obj/field_access_of_slices.zig new file mode 100644 index 0000000000..3e79e1f0e0 --- /dev/null +++ b/test/compile_errors/stage1/obj/field_access_of_slices.zig @@ -0,0 +1,9 @@ +export fn entry() void { + var slice: []i32 = undefined; + const info = @TypeOf(slice).unknown; + _ = info; +} + +// field access of slices +// +// tmp.zig:3:32: error: type 'type' does not support field access diff --git a/test/compile_errors/stage1/obj/field_access_of_unknown_length_pointer.zig b/test/compile_errors/stage1/obj/field_access_of_unknown_length_pointer.zig new file mode 100644 index 0000000000..7097c13605 --- /dev/null +++ b/test/compile_errors/stage1/obj/field_access_of_unknown_length_pointer.zig @@ -0,0 +1,11 @@ +const Foo = extern struct { + a: i32, +}; + +export fn entry(foo: [*]Foo) void { + foo.a += 1; +} + +// field access of unknown length pointer +// +// tmp.zig:6:8: error: type '[*]Foo' does not support field access diff --git a/test/compile_errors/stage1/obj/field_type_supplied_in_an_enum.zig b/test/compile_errors/stage1/obj/field_type_supplied_in_an_enum.zig new file mode 100644 index 0000000000..cb308307bb --- /dev/null +++ b/test/compile_errors/stage1/obj/field_type_supplied_in_an_enum.zig @@ -0,0 +1,10 @@ +const Letter = enum { + A: void, + B, + C, +}; + +// field type supplied in an enum +// +// tmp.zig:2:8: error: enum fields do not have types +// tmp.zig:1:16: note: consider 'union(enum)' here to make it a tagged union diff --git a/test/compile_errors/stage1/obj/floatToInt_comptime_safety.zig b/test/compile_errors/stage1/obj/floatToInt_comptime_safety.zig new file mode 100644 index 0000000000..e54047fb2f --- /dev/null +++ b/test/compile_errors/stage1/obj/floatToInt_comptime_safety.zig @@ -0,0 +1,15 @@ +comptime { + _ = @floatToInt(i8, @as(f32, -129.1)); +} +comptime { + _ = @floatToInt(u8, @as(f32, -1.1)); +} +comptime { + _ = @floatToInt(u8, @as(f32, 256.1)); +} + +// @floatToInt comptime safety +// +// tmp.zig:2:9: error: integer value '-129' cannot be stored in type 'i8' +// tmp.zig:5:9: error: integer value '-1' cannot be stored in type 'u8' +// tmp.zig:8:9: error: integer value '256' cannot be stored in type 'u8' diff --git a/test/compile_errors/stage1/obj/float_literal_too_large_error.zig b/test/compile_errors/stage1/obj/float_literal_too_large_error.zig new file mode 100644 index 0000000000..d7be9874ac --- /dev/null +++ b/test/compile_errors/stage1/obj/float_literal_too_large_error.zig @@ -0,0 +1,8 @@ +comptime { + const a = 0x1.0p18495; + _ = a; +} + +// float literal too large error +// +// tmp.zig:2:15: error: float literal out of range of any type diff --git a/test/compile_errors/stage1/obj/float_literal_too_small_error_denormal.zig b/test/compile_errors/stage1/obj/float_literal_too_small_error_denormal.zig new file mode 100644 index 0000000000..2657775be0 --- /dev/null +++ b/test/compile_errors/stage1/obj/float_literal_too_small_error_denormal.zig @@ -0,0 +1,8 @@ +comptime { + const a = 0x1.0p-19000; + _ = a; +} + +// float literal too small error (denormal) +// +// tmp.zig:2:15: error: float literal out of range of any type diff --git a/test/compile_errors/stage1/obj/for_loop_body_expression_ignored.zig b/test/compile_errors/stage1/obj/for_loop_body_expression_ignored.zig new file mode 100644 index 0000000000..fffbb13604 --- /dev/null +++ b/test/compile_errors/stage1/obj/for_loop_body_expression_ignored.zig @@ -0,0 +1,16 @@ +fn returns() usize { + return 2; +} +export fn f1() void { + for ("hello") |_| returns(); +} +export fn f2() void { + var x: anyerror!i32 = error.Bad; + for ("hello") |_| returns() else unreachable; + _ = x; +} + +// for loop body expression ignored +// +// tmp.zig:5:30: error: expression value is ignored +// tmp.zig:9:30: error: expression value is ignored diff --git a/test/compile_errors/stage1/obj/frame_called_outside_of_function_definition.zig b/test/compile_errors/stage1/obj/frame_called_outside_of_function_definition.zig new file mode 100644 index 0000000000..a76fff93a8 --- /dev/null +++ b/test/compile_errors/stage1/obj/frame_called_outside_of_function_definition.zig @@ -0,0 +1,9 @@ +var handle_undef: anyframe = undefined; +var handle_dummy: anyframe = @frame(); +export fn entry() bool { + return handle_undef == handle_dummy; +} + +// @frame() called outside of function definition +// +// tmp.zig:2:30: error: @frame() called outside of function definition diff --git a/test/compile_errors/stage1/obj/frame_causes_function_to_be_async.zig b/test/compile_errors/stage1/obj/frame_causes_function_to_be_async.zig new file mode 100644 index 0000000000..b45ff6965e --- /dev/null +++ b/test/compile_errors/stage1/obj/frame_causes_function_to_be_async.zig @@ -0,0 +1,11 @@ +export fn entry() void { + func(); +} +fn func() void { + _ = @frame(); +} + +// @frame() causes function to be async +// +// tmp.zig:1:1: error: function with calling convention 'C' cannot be async +// tmp.zig:5:9: note: @frame() causes function to be async diff --git a/test/compile_errors/stage1/obj/function_alignment_non_power_of_2.zig b/test/compile_errors/stage1/obj/function_alignment_non_power_of_2.zig new file mode 100644 index 0000000000..949e11c115 --- /dev/null +++ b/test/compile_errors/stage1/obj/function_alignment_non_power_of_2.zig @@ -0,0 +1,6 @@ +extern fn foo() align(3) void; +export fn entry() void { return foo(); } + +// function alignment non power of 2 +// +// tmp.zig:1:23: error: alignment value 3 is not a power of 2 diff --git a/test/compile_errors/stage1/obj/function_call_assigned_to_incorrect_type.zig b/test/compile_errors/stage1/obj/function_call_assigned_to_incorrect_type.zig new file mode 100644 index 0000000000..4257b802c1 --- /dev/null +++ b/test/compile_errors/stage1/obj/function_call_assigned_to_incorrect_type.zig @@ -0,0 +1,11 @@ +export fn entry() void { + var arr: [4]f32 = undefined; + arr = concat(); +} +fn concat() [16]f32 { + return [1]f32{0}**16; +} + +// function call assigned to incorrect type +// +// tmp.zig:3:17: error: expected type '[4]f32', found '[16]f32' diff --git a/test/compile_errors/stage1/obj/function_parameter_is_opaque.zig b/test/compile_errors/stage1/obj/function_parameter_is_opaque.zig new file mode 100644 index 0000000000..12e50ca59b --- /dev/null +++ b/test/compile_errors/stage1/obj/function_parameter_is_opaque.zig @@ -0,0 +1,27 @@ +const FooType = opaque {}; +export fn entry1() void { + const someFuncPtr: fn (FooType) void = undefined; + _ = someFuncPtr; +} + +export fn entry2() void { + const someFuncPtr: fn (@TypeOf(null)) void = undefined; + _ = someFuncPtr; +} + +fn foo(p: FooType) void {_ = p;} +export fn entry3() void { + _ = foo; +} + +fn bar(p: @TypeOf(null)) void {_ = p;} +export fn entry4() void { + _ = bar; +} + +// function parameter is opaque +// +// tmp.zig:3:28: error: parameter of opaque type 'FooType' not allowed +// tmp.zig:8:28: error: parameter of type '@Type(.Null)' not allowed +// tmp.zig:12:11: error: parameter of opaque type 'FooType' not allowed +// tmp.zig:17:11: error: parameter of type '@Type(.Null)' not allowed diff --git a/test/compile_errors/stage1/obj/function_prototype_with_no_body.zig b/test/compile_errors/stage1/obj/function_prototype_with_no_body.zig new file mode 100644 index 0000000000..07597af015 --- /dev/null +++ b/test/compile_errors/stage1/obj/function_prototype_with_no_body.zig @@ -0,0 +1,8 @@ +fn foo() void; +export fn entry() void { + foo(); +} + +// function prototype with no body +// +// tmp.zig:1:1: error: non-extern function has no body diff --git a/test/compile_errors/stage1/obj/function_returning_opaque_type.zig b/test/compile_errors/stage1/obj/function_returning_opaque_type.zig new file mode 100644 index 0000000000..956a4ed224 --- /dev/null +++ b/test/compile_errors/stage1/obj/function_returning_opaque_type.zig @@ -0,0 +1,17 @@ +const FooType = opaque {}; +export fn bar() !FooType { + return error.InvalidValue; +} +export fn bav() !@TypeOf(null) { + return error.InvalidValue; +} +export fn baz() !@TypeOf(undefined) { + return error.InvalidValue; +} + +// function returning opaque type +// +// tmp.zig:2:18: error: Opaque return type 'FooType' not allowed +// tmp.zig:1:1: note: type declared here +// tmp.zig:5:18: error: Null return type '@Type(.Null)' not allowed +// tmp.zig:8:18: error: Undefined return type '@Type(.Undefined)' not allowed diff --git a/test/compile_errors/stage1/obj/function_with_ccc_indirectly_calling_async_function.zig b/test/compile_errors/stage1/obj/function_with_ccc_indirectly_calling_async_function.zig new file mode 100644 index 0000000000..1ccf486be6 --- /dev/null +++ b/test/compile_errors/stage1/obj/function_with_ccc_indirectly_calling_async_function.zig @@ -0,0 +1,16 @@ +export fn entry() void { + foo(); +} +fn foo() void { + bar(); +} +fn bar() void { + suspend {} +} + +// function with ccc indirectly calling async function +// +// tmp.zig:1:1: error: function with calling convention 'C' cannot be async +// tmp.zig:2:8: note: async function call here +// tmp.zig:5:8: note: async function call here +// tmp.zig:8:5: note: suspends here diff --git a/test/compile_errors/stage1/obj/function_with_invalid_return_type.zig b/test/compile_errors/stage1/obj/function_with_invalid_return_type.zig new file mode 100644 index 0000000000..acedbac7d1 --- /dev/null +++ b/test/compile_errors/stage1/obj/function_with_invalid_return_type.zig @@ -0,0 +1,5 @@ +export fn foo() boid {} + +// function with invalid return type +// +// tmp.zig:1:17: error: use of undeclared identifier 'boid' diff --git a/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig b/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig new file mode 100644 index 0000000000..9c1913305d --- /dev/null +++ b/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig @@ -0,0 +1,6 @@ +const Foo = enum { A, B, C }; +export fn entry(foo: Foo) void { _ = foo; } + +// function with non-extern non-packed enum parameter +// +// tmp.zig:2:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C' diff --git a/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig b/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig new file mode 100644 index 0000000000..eb2617f279 --- /dev/null +++ b/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig @@ -0,0 +1,10 @@ +const Foo = struct { + A: i32, + B: f32, + C: bool, +}; +export fn entry(foo: Foo) void { _ = foo; } + +// function with non-extern non-packed struct parameter +// +// tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C' diff --git a/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig b/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig new file mode 100644 index 0000000000..662ffd349b --- /dev/null +++ b/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig @@ -0,0 +1,10 @@ +const Foo = union { + A: i32, + B: f32, + C: bool, +}; +export fn entry(foo: Foo) void { _ = foo; } + +// function with non-extern non-packed union parameter +// +// tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C' diff --git a/test/compile_errors/stage1/obj/generic_fn_as_parameter_without_comptime_keyword.zig b/test/compile_errors/stage1/obj/generic_fn_as_parameter_without_comptime_keyword.zig new file mode 100644 index 0000000000..fceea0961b --- /dev/null +++ b/test/compile_errors/stage1/obj/generic_fn_as_parameter_without_comptime_keyword.zig @@ -0,0 +1,9 @@ +fn f(_: fn (anytype) void) void {} +fn g(_: anytype) void {} +export fn entry() void { + f(g); +} + +// generic fn as parameter without comptime keyword +// +// tmp.zig:1:9: error: parameter of type 'fn(anytype) anytype' must be declared comptime diff --git a/test/compile_errors/stage1/obj/generic_function_call_assigned_to_incorrect_type.zig b/test/compile_errors/stage1/obj/generic_function_call_assigned_to_incorrect_type.zig new file mode 100644 index 0000000000..b48d5def57 --- /dev/null +++ b/test/compile_errors/stage1/obj/generic_function_call_assigned_to_incorrect_type.zig @@ -0,0 +1,11 @@ +pub export fn entry() void { + var res: []i32 = undefined; + res = myAlloc(i32); +} +fn myAlloc(comptime arg: type) anyerror!arg{ + unreachable; +} + +// generic function call assigned to incorrect type +// +// tmp.zig:3:18: error: expected type '[]i32', found 'anyerror!i32 diff --git a/test/compile_errors/stage1/obj/generic_function_instance_with_non-constant_expression.zig b/test/compile_errors/stage1/obj/generic_function_instance_with_non-constant_expression.zig new file mode 100644 index 0000000000..3698370b57 --- /dev/null +++ b/test/compile_errors/stage1/obj/generic_function_instance_with_non-constant_expression.zig @@ -0,0 +1,10 @@ +fn foo(comptime x: i32, y: i32) i32 { return x + y; } +fn test1(a: i32, b: i32) i32 { + return foo(a, b); +} + +export fn entry() usize { return @sizeOf(@TypeOf(test1)); } + +// generic function instance with non-constant expression +// +// tmp.zig:3:16: error: runtime value cannot be passed to comptime arg diff --git a/test/compile_errors/stage1/obj/generic_function_returning_opaque_type.zig b/test/compile_errors/stage1/obj/generic_function_returning_opaque_type.zig new file mode 100644 index 0000000000..c109a5ce7c --- /dev/null +++ b/test/compile_errors/stage1/obj/generic_function_returning_opaque_type.zig @@ -0,0 +1,23 @@ +const FooType = opaque {}; +fn generic(comptime T: type) !T { + return undefined; +} +export fn bar() void { + _ = generic(FooType); +} +export fn bav() void { + _ = generic(@TypeOf(null)); +} +export fn baz() void { + _ = generic(@TypeOf(undefined)); +} + +// generic function returning opaque type +// +// tmp.zig:6:16: error: call to generic function with Opaque return type 'FooType' not allowed +// tmp.zig:2:1: note: function declared here +// tmp.zig:1:1: note: type declared here +// tmp.zig:9:16: error: call to generic function with Null return type '@Type(.Null)' not allowed +// tmp.zig:2:1: note: function declared here +// tmp.zig:12:16: error: call to generic function with Undefined return type '@Type(.Undefined)' not allowed +// tmp.zig:2:1: note: function declared here diff --git a/test/compile_errors/stage1/obj/generic_function_where_return_type_is_self-referenced.zig b/test/compile_errors/stage1/obj/generic_function_where_return_type_is_self-referenced.zig new file mode 100644 index 0000000000..18237a5023 --- /dev/null +++ b/test/compile_errors/stage1/obj/generic_function_where_return_type_is_self-referenced.zig @@ -0,0 +1,13 @@ +fn Foo(comptime T: type) Foo(T) { + return struct{ x: T }; +} +export fn entry() void { + const t = Foo(u32) { + .x = 1 + }; + _ = t; +} + +// generic function where return type is self-referenced +// +// tmp.zig:1:29: error: evaluation exceeded 1000 backwards branches diff --git a/test/compile_errors/stage1/obj/global_variable_alignment_non_power_of_2.zig b/test/compile_errors/stage1/obj/global_variable_alignment_non_power_of_2.zig new file mode 100644 index 0000000000..a6c4ccaa98 --- /dev/null +++ b/test/compile_errors/stage1/obj/global_variable_alignment_non_power_of_2.zig @@ -0,0 +1,6 @@ +const some_data: [100]u8 align(3) = undefined; +export fn entry() usize { return @sizeOf(@TypeOf(some_data)); } + +// global variable alignment non power of 2 +// +// tmp.zig:1:32: error: alignment value 3 is not a power of 2 diff --git a/test/compile_errors/stage1/obj/global_variable_initializer_must_be_constant_expression.zig b/test/compile_errors/stage1/obj/global_variable_initializer_must_be_constant_expression.zig new file mode 100644 index 0000000000..1d6c20f3af --- /dev/null +++ b/test/compile_errors/stage1/obj/global_variable_initializer_must_be_constant_expression.zig @@ -0,0 +1,7 @@ +extern fn foo() i32; +const x = foo(); +export fn entry() i32 { return x; } + +// global variable initializer must be constant expression +// +// tmp.zig:2:11: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/hasDecl_with_non-container.zig b/test/compile_errors/stage1/obj/hasDecl_with_non-container.zig new file mode 100644 index 0000000000..50f1231997 --- /dev/null +++ b/test/compile_errors/stage1/obj/hasDecl_with_non-container.zig @@ -0,0 +1,7 @@ +export fn entry() void { + _ = @hasDecl(i32, "hi"); +} + +// @hasDecl with non-container +// +// tmp.zig:2:18: error: expected struct, enum, or union; found 'i32' diff --git a/test/compile_errors/stage1/obj/if_condition_is_bool_not_int.zig b/test/compile_errors/stage1/obj/if_condition_is_bool_not_int.zig new file mode 100644 index 0000000000..e8b7c61417 --- /dev/null +++ b/test/compile_errors/stage1/obj/if_condition_is_bool_not_int.zig @@ -0,0 +1,7 @@ +export fn f() void { + if (0) {} +} + +// if condition is bool, not int +// +// tmp.zig:2:9: error: expected type 'bool', found 'comptime_int' diff --git a/test/compile_errors/stage1/obj/ignored_assert-err-ok_return_value.zig b/test/compile_errors/stage1/obj/ignored_assert-err-ok_return_value.zig new file mode 100644 index 0000000000..53fbcaa8b5 --- /dev/null +++ b/test/compile_errors/stage1/obj/ignored_assert-err-ok_return_value.zig @@ -0,0 +1,8 @@ +export fn foo() void { + bar() catch unreachable; +} +fn bar() anyerror!i32 { return 0; } + +// ignored assert-err-ok return value +// +// tmp.zig:2:11: error: expression value is ignored diff --git a/test/compile_errors/stage1/obj/ignored_comptime_statement_value.zig b/test/compile_errors/stage1/obj/ignored_comptime_statement_value.zig new file mode 100644 index 0000000000..80925d6c84 --- /dev/null +++ b/test/compile_errors/stage1/obj/ignored_comptime_statement_value.zig @@ -0,0 +1,7 @@ +export fn foo() void { + comptime {1;} +} + +// ignored comptime statement value +// +// tmp.zig:2:15: error: expression value is ignored diff --git a/test/compile_errors/stage1/obj/ignored_comptime_value.zig b/test/compile_errors/stage1/obj/ignored_comptime_value.zig new file mode 100644 index 0000000000..375a5d242c --- /dev/null +++ b/test/compile_errors/stage1/obj/ignored_comptime_value.zig @@ -0,0 +1,7 @@ +export fn foo() void { + comptime 1; +} + +// ignored comptime value +// +// tmp.zig:2:5: error: expression value is ignored diff --git a/test/compile_errors/stage1/obj/ignored_deferred_function_call.zig b/test/compile_errors/stage1/obj/ignored_deferred_function_call.zig new file mode 100644 index 0000000000..58b85da985 --- /dev/null +++ b/test/compile_errors/stage1/obj/ignored_deferred_function_call.zig @@ -0,0 +1,8 @@ +export fn foo() void { + defer bar(); +} +fn bar() anyerror!i32 { return 0; } + +// ignored deferred function call +// +// tmp.zig:2:14: error: error is ignored. consider using `try`, `catch`, or `if` diff --git a/test/compile_errors/stage1/obj/ignored_deferred_statement_value.zig b/test/compile_errors/stage1/obj/ignored_deferred_statement_value.zig new file mode 100644 index 0000000000..effc79b039 --- /dev/null +++ b/test/compile_errors/stage1/obj/ignored_deferred_statement_value.zig @@ -0,0 +1,7 @@ +export fn foo() void { + defer {1;} +} + +// ignored deferred statement value +// +// tmp.zig:2:12: error: expression value is ignored diff --git a/test/compile_errors/stage1/obj/ignored_expression_in_while_continuation.zig b/test/compile_errors/stage1/obj/ignored_expression_in_while_continuation.zig new file mode 100644 index 0000000000..fb205924e3 --- /dev/null +++ b/test/compile_errors/stage1/obj/ignored_expression_in_while_continuation.zig @@ -0,0 +1,20 @@ +export fn a() void { + while (true) : (bad()) {} +} +export fn b() void { + var x: anyerror!i32 = 1234; + while (x) |_| : (bad()) {} else |_| {} +} +export fn c() void { + var x: ?i32 = 1234; + while (x) |_| : (bad()) {} +} +fn bad() anyerror!void { + return error.Bad; +} + +// ignored expression in while continuation +// +// tmp.zig:2:24: error: error is ignored. consider using `try`, `catch`, or `if` +// tmp.zig:6:25: error: error is ignored. consider using `try`, `catch`, or `if` +// tmp.zig:10:25: error: error is ignored. consider using `try`, `catch`, or `if` diff --git a/test/compile_errors/stage1/obj/ignored_return_value.zig b/test/compile_errors/stage1/obj/ignored_return_value.zig new file mode 100644 index 0000000000..9c8cfa0aa4 --- /dev/null +++ b/test/compile_errors/stage1/obj/ignored_return_value.zig @@ -0,0 +1,8 @@ +export fn foo() void { + bar(); +} +fn bar() i32 { return 0; } + +// ignored return value +// +// tmp.zig:2:8: error: expression value is ignored diff --git a/test/compile_errors/stage1/obj/ignored_statement_value.zig b/test/compile_errors/stage1/obj/ignored_statement_value.zig new file mode 100644 index 0000000000..7855cf584b --- /dev/null +++ b/test/compile_errors/stage1/obj/ignored_statement_value.zig @@ -0,0 +1,7 @@ +export fn foo() void { + 1; +} + +// ignored statement value +// +// tmp.zig:2:5: error: expression value is ignored diff --git a/test/compile_errors/stage1/obj/illegal_comparison_of_types.zig b/test/compile_errors/stage1/obj/illegal_comparison_of_types.zig new file mode 100644 index 0000000000..462664a400 --- /dev/null +++ b/test/compile_errors/stage1/obj/illegal_comparison_of_types.zig @@ -0,0 +1,18 @@ +fn bad_eql_1(a: []u8, b: []u8) bool { + return a == b; +} +const EnumWithData = union(enum) { + One: void, + Two: i32, +}; +fn bad_eql_2(a: *const EnumWithData, b: *const EnumWithData) bool { + return a.* == b.*; +} + +export fn entry1() usize { return @sizeOf(@TypeOf(bad_eql_1)); } +export fn entry2() usize { return @sizeOf(@TypeOf(bad_eql_2)); } + +// illegal comparison of types +// +// tmp.zig:2:14: error: operator not allowed for type '[]u8' +// tmp.zig:9:16: error: operator not allowed for type 'EnumWithData' diff --git a/test/compile_errors/stage1/obj/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig b/test/compile_errors/stage1/obj/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig new file mode 100644 index 0000000000..1dd751f989 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig @@ -0,0 +1,40 @@ +export fn a() void { + var x: [*c]u8 = undefined; + var y: *align(4) u8 = x; + _ = y; +} +export fn b() void { + var x: [*c]const u8 = undefined; + var y: *u8 = x; + _ = y; +} +export fn c() void { + var x: [*c]u8 = undefined; + var y: *u32 = x; + _ = y; +} +export fn d() void { + var y: *align(1) u32 = undefined; + var x: [*c]u32 = y; + _ = x; +} +export fn e() void { + var y: *const u8 = undefined; + var x: [*c]u8 = y; + _ = x; +} +export fn f() void { + var y: *u8 = undefined; + var x: [*c]u32 = y; + _ = x; +} + +// implicit cast between C pointer and Zig pointer - bad const/align/child +// +// tmp.zig:3:27: error: cast increases pointer alignment +// tmp.zig:8:18: error: cast discards const qualifier +// tmp.zig:13:19: error: expected type '*u32', found '[*c]u8' +// tmp.zig:13:19: note: pointer type child 'u8' cannot cast into pointer type child 'u32' +// tmp.zig:18:22: error: cast increases pointer alignment +// tmp.zig:23:21: error: cast discards const qualifier +// tmp.zig:28:22: error: expected type '[*c]u32', found '*u8' diff --git a/test/compile_errors/stage1/obj/implicit_cast_const_array_to_mutable_slice.zig b/test/compile_errors/stage1/obj/implicit_cast_const_array_to_mutable_slice.zig new file mode 100644 index 0000000000..f38c2c9fe1 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_cast_const_array_to_mutable_slice.zig @@ -0,0 +1,10 @@ +export fn entry() void { + const buffer: [1]u8 = [_]u8{8}; + const sliceA: []u8 = &buffer; + _ = sliceA; +} + +// implicit cast const array to mutable slice +// +// tmp.zig:3:27: error: cannot cast pointer to array literal to slice type '[]u8' +// tmp.zig:3:27: note: cast discards const qualifier diff --git a/test/compile_errors/stage1/obj/implicit_cast_from_array_to_mutable_slice.zig b/test/compile_errors/stage1/obj/implicit_cast_from_array_to_mutable_slice.zig new file mode 100644 index 0000000000..60fc5baf1f --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_cast_from_array_to_mutable_slice.zig @@ -0,0 +1,9 @@ +var global_array: [10]i32 = undefined; +fn foo(param: []i32) void {_ = param;} +export fn entry() void { + foo(global_array); +} + +// implicit cast from array to mutable slice +// +// tmp.zig:4:9: error: expected type '[]i32', found '[10]i32' diff --git a/test/compile_errors/stage1/obj/implicit_cast_from_f64_to_f32.zig b/test/compile_errors/stage1/obj/implicit_cast_from_f64_to_f32.zig new file mode 100644 index 0000000000..d1d4417f34 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_cast_from_f64_to_f32.zig @@ -0,0 +1,8 @@ +var x: f64 = 1.0; +var y: f32 = x; + +export fn entry() usize { return @sizeOf(@TypeOf(y)); } + +// implicit cast from f64 to f32 +// +// tmp.zig:2:14: error: expected type 'f32', found 'f64' diff --git a/test/compile_errors/stage1/obj/implicit_cast_of_error_set_not_a_subset.zig b/test/compile_errors/stage1/obj/implicit_cast_of_error_set_not_a_subset.zig new file mode 100644 index 0000000000..9cfce6524f --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_cast_of_error_set_not_a_subset.zig @@ -0,0 +1,14 @@ +const Set1 = error{A, B}; +const Set2 = error{A, C}; +export fn entry() void { + foo(Set1.B); +} +fn foo(set1: Set1) void { + var x: Set2 = set1; + _ = x; +} + +// implicit cast of error set not a subset +// +// tmp.zig:7:19: error: expected type 'Set2', found 'Set1' +// tmp.zig:1:23: note: 'error.B' not a member of destination error set diff --git a/test/compile_errors/stage1/obj/implicit_casting_C_pointers_which_would_mess_up_null_semantics.zig b/test/compile_errors/stage1/obj/implicit_casting_C_pointers_which_would_mess_up_null_semantics.zig new file mode 100644 index 0000000000..cdc35e3963 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_casting_C_pointers_which_would_mess_up_null_semantics.zig @@ -0,0 +1,24 @@ +export fn entry() void { + var slice: []const u8 = "aoeu"; + const opt_many_ptr: [*]const u8 = slice.ptr; + var ptr_opt_many_ptr = &opt_many_ptr; + var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr; + ptr_opt_many_ptr = c_ptr; +} +export fn entry2() void { + var buf: [4]u8 = "aoeu".*; + var slice: []u8 = &buf; + var opt_many_ptr: [*]u8 = slice.ptr; + var ptr_opt_many_ptr = &opt_many_ptr; + var c_ptr: [*c][*c]const u8 = ptr_opt_many_ptr; + _ = c_ptr; +} + +// implicit casting C pointers which would mess up null semantics +// +// tmp.zig:6:24: error: expected type '*const [*]const u8', found '[*c]const [*c]const u8' +// tmp.zig:6:24: note: pointer type child '[*c]const u8' cannot cast into pointer type child '[*]const u8' +// tmp.zig:6:24: note: '[*c]const u8' could have null values which are illegal in type '[*]const u8' +// tmp.zig:13:35: error: expected type '[*c][*c]const u8', found '*[*]u8' +// tmp.zig:13:35: note: pointer type child '[*]u8' cannot cast into pointer type child '[*c]const u8' +// tmp.zig:13:35: note: mutable '[*c]const u8' allows illegal null values stored to type '[*]u8' diff --git a/test/compile_errors/stage1/obj/implicit_casting_null_c_pointer_to_zig_pointer.zig b/test/compile_errors/stage1/obj/implicit_casting_null_c_pointer_to_zig_pointer.zig new file mode 100644 index 0000000000..29dd6d06f9 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_casting_null_c_pointer_to_zig_pointer.zig @@ -0,0 +1,9 @@ +comptime { + var c_ptr: [*c]u8 = 0; + var zig_ptr: *u8 = c_ptr; + _ = zig_ptr; +} + +// implicit casting null c pointer to zig pointer +// +// tmp.zig:3:24: error: null pointer casted to type '*u8' diff --git a/test/compile_errors/stage1/obj/implicit_casting_too_big_integers_to_C_pointers.zig b/test/compile_errors/stage1/obj/implicit_casting_too_big_integers_to_C_pointers.zig new file mode 100644 index 0000000000..3deb817b76 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_casting_too_big_integers_to_C_pointers.zig @@ -0,0 +1,14 @@ +export fn a() void { + var ptr: [*c]u8 = (1 << 64) + 1; + _ = ptr; +} +export fn b() void { + var x: u65 = 0x1234; + var ptr: [*c]u8 = x; + _ = ptr; +} + +// implicit casting too big integers to C pointers +// +// tmp.zig:2:33: error: integer value 18446744073709551617 cannot be coerced to type 'usize' +// tmp.zig:7:23: error: integer type 'u65' too big for implicit @intToPtr to type '[*c]u8' diff --git a/test/compile_errors/stage1/obj/implicit_casting_undefined_c_pointer_to_zig_pointer.zig b/test/compile_errors/stage1/obj/implicit_casting_undefined_c_pointer_to_zig_pointer.zig new file mode 100644 index 0000000000..4c19f57442 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_casting_undefined_c_pointer_to_zig_pointer.zig @@ -0,0 +1,9 @@ +comptime { + var c_ptr: [*c]u8 = undefined; + var zig_ptr: *u8 = c_ptr; + _ = zig_ptr; +} + +// implicit casting undefined c pointer to zig pointer +// +// tmp.zig:3:24: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-block_expr.zig b/test/compile_errors/stage1/obj/implicit_semicolon-block_expr.zig new file mode 100644 index 0000000000..bacd484c0c --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-block_expr.zig @@ -0,0 +1,10 @@ +export fn entry() void { + _ = {}; + var good = {}; + _ = {} + var bad = {}; +} + +// implicit semicolon - block expr +// +// tmp.zig:4:11: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-block_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-block_statement.zig new file mode 100644 index 0000000000..dca8998ee6 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-block_statement.zig @@ -0,0 +1,10 @@ +export fn entry() void { + {} + var good = {}; + ({}) + var bad = {}; +} + +// implicit semicolon - block statement +// +// tmp.zig:4:9: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-comptime_expression.zig b/test/compile_errors/stage1/obj/implicit_semicolon-comptime_expression.zig new file mode 100644 index 0000000000..a3f679a59a --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-comptime_expression.zig @@ -0,0 +1,10 @@ +export fn entry() void { + _ = comptime {}; + var good = {}; + _ = comptime {} + var bad = {}; +} + +// implicit semicolon - comptime expression +// +// tmp.zig:4:20: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-comptime_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-comptime_statement.zig new file mode 100644 index 0000000000..299b081e4b --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-comptime_statement.zig @@ -0,0 +1,10 @@ +export fn entry() void { + comptime {} + var good = {}; + comptime ({}) + var bad = {}; +} + +// implicit semicolon - comptime statement +// +// tmp.zig:4:18: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-defer.zig b/test/compile_errors/stage1/obj/implicit_semicolon-defer.zig new file mode 100644 index 0000000000..7e58d930d6 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-defer.zig @@ -0,0 +1,10 @@ +export fn entry() void { + defer {} + var good = {}; + defer ({}) + var bad = {}; +} + +// implicit semicolon - defer +// +// tmp.zig:4:15: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-for_expression.zig b/test/compile_errors/stage1/obj/implicit_semicolon-for_expression.zig new file mode 100644 index 0000000000..3e332ef189 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-for_expression.zig @@ -0,0 +1,10 @@ +export fn entry() void { + _ = for(foo()) |_| {}; + var good = {}; + _ = for(foo()) |_| {} + var bad = {}; +} + +// implicit semicolon - for expression +// +// tmp.zig:4:26: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-for_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-for_statement.zig new file mode 100644 index 0000000000..092c28889f --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-for_statement.zig @@ -0,0 +1,10 @@ +export fn entry() void { + for(foo()) |_| {} + var good = {}; + for(foo()) |_| ({}) + var bad = {}; +} + +// implicit semicolon - for statement +// +// tmp.zig:4:24: error: expected ';' or 'else' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_expression.zig b/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_expression.zig new file mode 100644 index 0000000000..fd55529a8f --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_expression.zig @@ -0,0 +1,10 @@ +export fn entry() void { + _ = if(true) {} else if(true) {} else {}; + var good = {}; + _ = if(true) {} else if(true) {} else {} + var bad = {}; +} + +// implicit semicolon - if-else-if-else expression +// +// tmp.zig:4:45: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_statement.zig new file mode 100644 index 0000000000..3d59e38aa9 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_statement.zig @@ -0,0 +1,10 @@ +export fn entry() void { + if(true) {} else if(true) {} else {} + var good = {}; + if(true) ({}) else if(true) ({}) else ({}) + var bad = {}; +} + +// implicit semicolon - if-else-if-else statement +// +// tmp.zig:4:47: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_expression.zig b/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_expression.zig new file mode 100644 index 0000000000..2caaad52b8 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_expression.zig @@ -0,0 +1,10 @@ +export fn entry() void { + _ = if(true) {} else if(true) {}; + var good = {}; + _ = if(true) {} else if(true) {} + var bad = {}; +} + +// implicit semicolon - if-else-if expression +// +// tmp.zig:4:37: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_statement.zig new file mode 100644 index 0000000000..263aa36da3 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_statement.zig @@ -0,0 +1,10 @@ +export fn entry() void { + if(true) {} else if(true) {} + var good = {}; + if(true) ({}) else if(true) ({}) + var bad = {}; +} + +// implicit semicolon - if-else-if statement +// +// tmp.zig:4:37: error: expected ';' or 'else' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if-else_expression.zig b/test/compile_errors/stage1/obj/implicit_semicolon-if-else_expression.zig new file mode 100644 index 0000000000..12d993f6a7 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-if-else_expression.zig @@ -0,0 +1,10 @@ +export fn entry() void { + _ = if(true) {} else {}; + var good = {}; + _ = if(true) {} else {} + var bad = {}; +} + +// implicit semicolon - if-else expression +// +// tmp.zig:4:28: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if-else_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-if-else_statement.zig new file mode 100644 index 0000000000..401fc46a51 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-if-else_statement.zig @@ -0,0 +1,10 @@ +export fn entry() void { + if(true) {} else {} + var good = {}; + if(true) ({}) else ({}) + var bad = {}; +} + +// implicit semicolon - if-else statement +// +// tmp.zig:4:28: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if_expression.zig b/test/compile_errors/stage1/obj/implicit_semicolon-if_expression.zig new file mode 100644 index 0000000000..b3a81e9d36 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-if_expression.zig @@ -0,0 +1,10 @@ +export fn entry() void { + _ = if(true) {}; + var good = {}; + _ = if(true) {} + var bad = {}; +} + +// implicit semicolon - if expression +// +// tmp.zig:4:20: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-if_statement.zig new file mode 100644 index 0000000000..e543925d78 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-if_statement.zig @@ -0,0 +1,10 @@ +export fn entry() void { + if(true) {} + var good = {}; + if(true) ({}) + var bad = {}; +} + +// implicit semicolon - if statement +// +// tmp.zig:4:18: error: expected ';' or 'else' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-test_expression.zig b/test/compile_errors/stage1/obj/implicit_semicolon-test_expression.zig new file mode 100644 index 0000000000..1702bec048 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-test_expression.zig @@ -0,0 +1,10 @@ +export fn entry() void { + _ = if (foo()) |_| {}; + var good = {}; + _ = if (foo()) |_| {} + var bad = {}; +} + +// implicit semicolon - test expression +// +// tmp.zig:4:26: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-test_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-test_statement.zig new file mode 100644 index 0000000000..8715ca9ac0 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-test_statement.zig @@ -0,0 +1,10 @@ +export fn entry() void { + if (foo()) |_| {} + var good = {}; + if (foo()) |_| ({}) + var bad = {}; +} + +// implicit semicolon - test statement +// +// tmp.zig:4:24: error: expected ';' or 'else' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-while-continue_expression.zig b/test/compile_errors/stage1/obj/implicit_semicolon-while-continue_expression.zig new file mode 100644 index 0000000000..b1a7bdbab8 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-while-continue_expression.zig @@ -0,0 +1,10 @@ +export fn entry() void { + _ = while(true):({}) {}; + var good = {}; + _ = while(true):({}) {} + var bad = {}; +} + +// implicit semicolon - while-continue expression +// +// tmp.zig:4:28: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-while-continue_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-while-continue_statement.zig new file mode 100644 index 0000000000..601f1f0318 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-while-continue_statement.zig @@ -0,0 +1,10 @@ +export fn entry() void { + while(true):({}) {} + var good = {}; + while(true):({}) ({}) + var bad = {}; +} + +// implicit semicolon - while-continue statement +// +// tmp.zig:4:26: error: expected ';' or 'else' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-while_expression.zig b/test/compile_errors/stage1/obj/implicit_semicolon-while_expression.zig new file mode 100644 index 0000000000..9580889bd7 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-while_expression.zig @@ -0,0 +1,10 @@ +export fn entry() void { + _ = while(true) {}; + var good = {}; + _ = while(true) {} + var bad = {}; +} + +// implicit semicolon - while expression +// +// tmp.zig:4:23: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-while_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-while_statement.zig new file mode 100644 index 0000000000..ab64dd991d --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_semicolon-while_statement.zig @@ -0,0 +1,10 @@ +export fn entry() void { + while(true) {} + var good = {}; + while(true) 1 + var bad = {}; +} + +// implicit semicolon - while statement +// +// tmp.zig:4:18: error: expected ';' or 'else' after statement diff --git a/test/compile_errors/stage1/obj/implicitly_casting_enum_to_tag_type.zig b/test/compile_errors/stage1/obj/implicitly_casting_enum_to_tag_type.zig new file mode 100644 index 0000000000..26806a3914 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicitly_casting_enum_to_tag_type.zig @@ -0,0 +1,15 @@ +const Small = enum(u2) { + One, + Two, + Three, + Four, +}; + +export fn entry() void { + var x: u2 = Small.Two; + _ = x; +} + +// implicitly casting enum to tag type +// +// tmp.zig:9:22: error: expected type 'u2', found 'Small' diff --git a/test/compile_errors/stage1/obj/implicitly_increasing_pointer_alignment.zig b/test/compile_errors/stage1/obj/implicitly_increasing_pointer_alignment.zig new file mode 100644 index 0000000000..8f743a3b59 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicitly_increasing_pointer_alignment.zig @@ -0,0 +1,17 @@ +const Foo = packed struct { + a: u8, + b: u32, +}; + +export fn entry() void { + var foo = Foo { .a = 1, .b = 10 }; + bar(&foo.b); +} + +fn bar(x: *u32) void { + x.* += 1; +} + +// implicitly increasing pointer alignment +// +// tmp.zig:8:13: error: expected type '*u32', found '*align(1) u32' diff --git a/test/compile_errors/stage1/obj/implicitly_increasing_slice_alignment.zig b/test/compile_errors/stage1/obj/implicitly_increasing_slice_alignment.zig new file mode 100644 index 0000000000..c1d4ec2a0f --- /dev/null +++ b/test/compile_errors/stage1/obj/implicitly_increasing_slice_alignment.zig @@ -0,0 +1,20 @@ +const Foo = packed struct { + a: u8, + b: u32, +}; + +export fn entry() void { + var foo = Foo { .a = 1, .b = 10 }; + foo.b += 1; + bar(@as(*[1]u32, &foo.b)[0..]); +} + +fn bar(x: []u32) void { + x[0] += 1; +} + +// implicitly increasing slice alignment +// +// tmp.zig:9:26: error: cast increases pointer alignment +// tmp.zig:9:26: note: '*align(1) u32' has alignment 1 +// tmp.zig:9:26: note: '*[1]u32' has alignment 4 diff --git a/test/compile_errors/stage1/obj/import_outside_package_path.zig b/test/compile_errors/stage1/obj/import_outside_package_path.zig new file mode 100644 index 0000000000..843de67b63 --- /dev/null +++ b/test/compile_errors/stage1/obj/import_outside_package_path.zig @@ -0,0 +1,7 @@ +comptime{ + _ = @import("../a.zig"); +} + +// import outside package path +// +// tmp.zig:2:9: error: import of file outside package path: '../a.zig' diff --git a/test/compile_errors/stage1/obj/incorrect_return_type.zig b/test/compile_errors/stage1/obj/incorrect_return_type.zig new file mode 100644 index 0000000000..86c5f23dc3 --- /dev/null +++ b/test/compile_errors/stage1/obj/incorrect_return_type.zig @@ -0,0 +1,19 @@ + pub export fn entry() void{ + _ = foo(); + } + const A = struct { + a: u32, + }; + fn foo() A { + return bar(); + } + const B = struct { + a: u32, + }; + fn bar() B { + unreachable; + } + +// incorrect return type +// +// tmp.zig:8:16: error: expected type 'A', found 'B' diff --git a/test/compile_errors/stage1/obj/increase_pointer_alignment_in_ptrCast.zig b/test/compile_errors/stage1/obj/increase_pointer_alignment_in_ptrCast.zig new file mode 100644 index 0000000000..01d8ac821e --- /dev/null +++ b/test/compile_errors/stage1/obj/increase_pointer_alignment_in_ptrCast.zig @@ -0,0 +1,11 @@ +export fn entry() u32 { + var bytes: [4]u8 = [_]u8{0x01, 0x02, 0x03, 0x04}; + const ptr = @ptrCast(*u32, &bytes[0]); + return ptr.*; +} + +// increase pointer alignment in @ptrCast +// +// tmp.zig:3:17: error: cast increases pointer alignment +// tmp.zig:3:38: note: '*u8' has alignment 1 +// tmp.zig:3:26: note: '*u32' has alignment 4 diff --git a/test/compile_errors/stage1/obj/indexing_a_undefined_slice_at_comptime.zig b/test/compile_errors/stage1/obj/indexing_a_undefined_slice_at_comptime.zig new file mode 100644 index 0000000000..4653d8668e --- /dev/null +++ b/test/compile_errors/stage1/obj/indexing_a_undefined_slice_at_comptime.zig @@ -0,0 +1,8 @@ +comptime { + var slice: []u8 = undefined; + slice[0] = 2; +} + +// indexing a undefined slice at comptime +// +// tmp.zig:3:10: error: index 0 outside slice of size 0 diff --git a/test/compile_errors/stage1/obj/indexing_an_array_of_size_zero.zig b/test/compile_errors/stage1/obj/indexing_an_array_of_size_zero.zig new file mode 100644 index 0000000000..9066985de5 --- /dev/null +++ b/test/compile_errors/stage1/obj/indexing_an_array_of_size_zero.zig @@ -0,0 +1,9 @@ +const array = [_]u8{}; +export fn foo() void { + const pointer = &array[0]; + _ = pointer; +} + +// indexing an array of size zero +// +// tmp.zig:3:27: error: accessing a zero length array is not allowed diff --git a/test/compile_errors/stage1/obj/indexing_an_array_of_size_zero_with_runtime_index.zig b/test/compile_errors/stage1/obj/indexing_an_array_of_size_zero_with_runtime_index.zig new file mode 100644 index 0000000000..c5f3acb3cc --- /dev/null +++ b/test/compile_errors/stage1/obj/indexing_an_array_of_size_zero_with_runtime_index.zig @@ -0,0 +1,10 @@ +const array = [_]u8{}; +export fn foo() void { + var index: usize = 0; + const pointer = &array[index]; + _ = pointer; +} + +// indexing an array of size zero with runtime index +// +// tmp.zig:4:27: error: accessing a zero length array is not allowed diff --git a/test/compile_errors/stage1/obj/indexing_single-item_pointer.zig b/test/compile_errors/stage1/obj/indexing_single-item_pointer.zig new file mode 100644 index 0000000000..e228083964 --- /dev/null +++ b/test/compile_errors/stage1/obj/indexing_single-item_pointer.zig @@ -0,0 +1,7 @@ +export fn entry(ptr: *i32) i32 { + return ptr[1]; +} + +// indexing single-item pointer +// +// tmp.zig:2:15: error: index of single-item pointer diff --git a/test/compile_errors/stage1/obj/indirect_recursion_of_async_functions_detected.zig b/test/compile_errors/stage1/obj/indirect_recursion_of_async_functions_detected.zig new file mode 100644 index 0000000000..5765f7aae1 --- /dev/null +++ b/test/compile_errors/stage1/obj/indirect_recursion_of_async_functions_detected.zig @@ -0,0 +1,34 @@ +var frame: ?anyframe = null; + +export fn a() void { + _ = async rangeSum(10); + while (frame) |f| resume f; +} + +fn rangeSum(x: i32) i32 { + suspend { + frame = @frame(); + } + frame = null; + + if (x == 0) return 0; + var child = rangeSumIndirect(x - 1); + return child + 1; +} + +fn rangeSumIndirect(x: i32) i32 { + suspend { + frame = @frame(); + } + frame = null; + + if (x == 0) return 0; + var child = rangeSum(x - 1); + return child + 1; +} + +// indirect recursion of async functions detected +// +// tmp.zig:8:1: error: '@Frame(rangeSum)' depends on itself +// tmp.zig:15:33: note: when analyzing type '@Frame(rangeSum)' here +// tmp.zig:26:25: note: when analyzing type '@Frame(rangeSumIndirect)' here diff --git a/test/compile_errors/stage1/obj/indirect_struct_loop.zig b/test/compile_errors/stage1/obj/indirect_struct_loop.zig new file mode 100644 index 0000000000..903a1bda39 --- /dev/null +++ b/test/compile_errors/stage1/obj/indirect_struct_loop.zig @@ -0,0 +1,8 @@ +const A = struct { b : B, }; +const B = struct { c : C, }; +const C = struct { a : A, }; +export fn entry() usize { return @sizeOf(A); } + +// indirect struct loop +// +// tmp.zig:1:11: error: struct 'A' depends on itself diff --git a/test/compile_errors/stage1/obj/inferred_array_size_invalid_here.zig b/test/compile_errors/stage1/obj/inferred_array_size_invalid_here.zig new file mode 100644 index 0000000000..a45b6aa027 --- /dev/null +++ b/test/compile_errors/stage1/obj/inferred_array_size_invalid_here.zig @@ -0,0 +1,14 @@ +export fn entry() void { + const x = [_]u8; + _ = x; +} +export fn entry2() void { + const S = struct { a: *const [_]u8 }; + var a = .{ S{} }; + _ = a; +} + +// inferred array size invalid here +// +// tmp.zig:2:16: error: unable to infer array size +// tmp.zig:6:35: error: unable to infer array size diff --git a/test/compile_errors/stage1/obj/inferring_error_set_of_function_pointer.zig b/test/compile_errors/stage1/obj/inferring_error_set_of_function_pointer.zig new file mode 100644 index 0000000000..ead5afd248 --- /dev/null +++ b/test/compile_errors/stage1/obj/inferring_error_set_of_function_pointer.zig @@ -0,0 +1,7 @@ +comptime { + const z: ?fn()!void = null; +} + +// inferring error set of function pointer +// +// tmp.zig:2:19: error: function prototype may not have inferred error set diff --git a/test/compile_errors/stage1/obj/initializing_array_with_struct_syntax.zig b/test/compile_errors/stage1/obj/initializing_array_with_struct_syntax.zig new file mode 100644 index 0000000000..15412ac5ab --- /dev/null +++ b/test/compile_errors/stage1/obj/initializing_array_with_struct_syntax.zig @@ -0,0 +1,8 @@ +export fn entry() void { + const x = [_]u8{ .y = 2 }; + _ = x; +} + +// initializing array with struct syntax +// +// tmp.zig:2:15: error: initializing array with struct syntax diff --git a/test/compile_errors/stage1/obj/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig b/test/compile_errors/stage1/obj/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig new file mode 100644 index 0000000000..c39908c9b5 --- /dev/null +++ b/test/compile_errors/stage1/obj/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig @@ -0,0 +1,13 @@ +const Foo = struct { + x: Foo, +}; + +var foo: Foo = undefined; + +export fn entry() usize { + return @sizeOf(@TypeOf(foo.x)); +} + +// instantiating an undefined value for an invalid struct that contains itself +// +// tmp.zig:1:13: error: struct 'Foo' depends on itself diff --git a/test/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig b/test/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig new file mode 100644 index 0000000000..a04f318f0c --- /dev/null +++ b/test/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig @@ -0,0 +1,8 @@ +pub fn main() void { + var y = @intToPtr([*]align(4) u8, 5); + _ = y; +} + +// intToPtr with misaligned address +// +// tmp.zig:2:13: error: pointer type '[*]align(4) u8' requires aligned address diff --git a/test/compile_errors/stage1/obj/int_to_err_global_invalid_number.zig b/test/compile_errors/stage1/obj/int_to_err_global_invalid_number.zig new file mode 100644 index 0000000000..180da60ab2 --- /dev/null +++ b/test/compile_errors/stage1/obj/int_to_err_global_invalid_number.zig @@ -0,0 +1,13 @@ +const Set1 = error{ + A, + B, +}; +comptime { + var x: u16 = 3; + var y = @intToError(x); + _ = y; +} + +// int to err global invalid number +// +// tmp.zig:7:13: error: integer value 3 represents no error diff --git a/test/compile_errors/stage1/obj/int_to_err_non_global_invalid_number.zig b/test/compile_errors/stage1/obj/int_to_err_non_global_invalid_number.zig new file mode 100644 index 0000000000..a6627891d0 --- /dev/null +++ b/test/compile_errors/stage1/obj/int_to_err_non_global_invalid_number.zig @@ -0,0 +1,17 @@ +const Set1 = error{ + A, + B, +}; +const Set2 = error{ + A, + C, +}; +comptime { + var x = @errorToInt(Set1.B); + var y = @errSetCast(Set2, @intToError(x)); + _ = y; +} + +// int to err non global invalid number +// +// tmp.zig:11:13: error: error.B not a member of error set 'Set2' diff --git a/test/compile_errors/stage1/obj/int_to_ptr_of_0_bits.zig b/test/compile_errors/stage1/obj/int_to_ptr_of_0_bits.zig new file mode 100644 index 0000000000..7047600c1b --- /dev/null +++ b/test/compile_errors/stage1/obj/int_to_ptr_of_0_bits.zig @@ -0,0 +1,9 @@ +export fn foo() void { + var x: usize = 0x1000; + var y: *void = @intToPtr(*void, x); + _ = y; +} + +// int to ptr of 0 bits +// +// tmp.zig:3:30: error: type '*void' has 0 bits and cannot store information diff --git a/test/compile_errors/stage1/obj/integer_cast_truncates_bits.zig b/test/compile_errors/stage1/obj/integer_cast_truncates_bits.zig new file mode 100644 index 0000000000..a5c8036152 --- /dev/null +++ b/test/compile_errors/stage1/obj/integer_cast_truncates_bits.zig @@ -0,0 +1,29 @@ +export fn entry1() void { + const spartan_count: u16 = 300; + const byte = @intCast(u8, spartan_count); + _ = byte; +} +export fn entry2() void { + const spartan_count: u16 = 300; + const byte: u8 = spartan_count; + _ = byte; +} +export fn entry3() void { + var spartan_count: u16 = 300; + var byte: u8 = spartan_count; + _ = byte; +} +export fn entry4() void { + var signed: i8 = -1; + var unsigned: u64 = signed; + _ = unsigned; +} + +// integer cast truncates bits +// +// tmp.zig:3:18: error: cast from 'u16' to 'u8' truncates bits +// tmp.zig:8:22: error: integer value 300 cannot be coerced to type 'u8' +// tmp.zig:13:20: error: expected type 'u8', found 'u16' +// tmp.zig:13:20: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values +// tmp.zig:18:25: error: expected type 'u64', found 'i8' +// tmp.zig:18:25: note: unsigned 64-bit int cannot represent all possible signed 8-bit values diff --git a/test/compile_errors/stage1/obj/integer_overflow_error.zig b/test/compile_errors/stage1/obj/integer_overflow_error.zig new file mode 100644 index 0000000000..568cc5034b --- /dev/null +++ b/test/compile_errors/stage1/obj/integer_overflow_error.zig @@ -0,0 +1,6 @@ +const x : u8 = 300; +export fn entry() usize { return @sizeOf(@TypeOf(x)); } + +// integer overflow error +// +// tmp.zig:1:16: error: integer value 300 cannot be coerced to type 'u8' diff --git a/test/compile_errors/stage1/obj/integer_underflow_error.zig b/test/compile_errors/stage1/obj/integer_underflow_error.zig new file mode 100644 index 0000000000..01171589d9 --- /dev/null +++ b/test/compile_errors/stage1/obj/integer_underflow_error.zig @@ -0,0 +1,7 @@ +export fn entry() void { + _ = @intToPtr(*anyopaque, ~@as(usize, @import("std").math.maxInt(usize)) - 1); +} + +// integer underflow error +// +// :2:78: error: operation caused overflow diff --git a/test/compile_errors/stage1/obj/invalid_break_expression.zig b/test/compile_errors/stage1/obj/invalid_break_expression.zig new file mode 100644 index 0000000000..b6d27d6ea5 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_break_expression.zig @@ -0,0 +1,7 @@ +export fn f() void { + break; +} + +// invalid break expression +// +// tmp.zig:2:5: error: break expression outside loop diff --git a/test/compile_errors/stage1/obj/invalid_builtin_fn.zig b/test/compile_errors/stage1/obj/invalid_builtin_fn.zig new file mode 100644 index 0000000000..e10baf5965 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_builtin_fn.zig @@ -0,0 +1,7 @@ +fn f() @bogus(foo) { +} +export fn entry() void { _ = f(); } + +// invalid builtin fn +// +// tmp.zig:1:8: error: invalid builtin function: '@bogus' diff --git a/test/compile_errors/stage1/obj/invalid_cast_from_integral_type_to_enum.zig b/test/compile_errors/stage1/obj/invalid_cast_from_integral_type_to_enum.zig new file mode 100644 index 0000000000..476a4929ef --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_cast_from_integral_type_to_enum.zig @@ -0,0 +1,15 @@ +const E = enum(usize) { One, Two }; + +export fn entry() void { + foo(1); +} + +fn foo(x: usize) void { + switch (x) { + E.One => {}, + } +} + +// invalid cast from integral type to enum +// +// tmp.zig:9:10: error: expected type 'usize', found 'E' diff --git a/test/compile_errors/stage1/obj/invalid_comparison_for_function_pointers.zig b/test/compile_errors/stage1/obj/invalid_comparison_for_function_pointers.zig new file mode 100644 index 0000000000..f7827ed0e9 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_comparison_for_function_pointers.zig @@ -0,0 +1,8 @@ +fn foo() void {} +const invalid = foo > foo; + +export fn entry() usize { return @sizeOf(@TypeOf(invalid)); } + +// invalid comparison for function pointers +// +// tmp.zig:2:21: error: operator not allowed for type 'fn() void' diff --git a/test/compile_errors/stage1/obj/invalid_continue_expression.zig b/test/compile_errors/stage1/obj/invalid_continue_expression.zig new file mode 100644 index 0000000000..9bd40c516a --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_continue_expression.zig @@ -0,0 +1,7 @@ +export fn f() void { + continue; +} + +// invalid continue expression +// +// tmp.zig:2:5: error: continue expression outside loop diff --git a/test/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig b/test/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig new file mode 100644 index 0000000000..47dc0a52c1 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig @@ -0,0 +1,15 @@ +comptime { + var tile = Tile.Empty; + switch (tile.*) { + Tile.Empty => {}, + Tile.Filled => {}, + } +} +const Tile = enum { + Empty, + Filled, +}; + +// invalid deref on switch target +// +// tmp.zig:3:17: error: attempt to dereference non-pointer type 'Tile' diff --git a/test/compile_errors/stage1/obj/invalid_empty_unicode_escape.zig b/test/compile_errors/stage1/obj/invalid_empty_unicode_escape.zig new file mode 100644 index 0000000000..d8853110a4 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_empty_unicode_escape.zig @@ -0,0 +1,7 @@ +export fn entry() void { + const a = '\u{}'; +} + +// invalid empty unicode escape +// +// tmp.zig:2:19: error: empty unicode escape sequence diff --git a/test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-1.zig b/test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-1.zig new file mode 100644 index 0000000000..4f13de2a62 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-1.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: f128 = 0x1.0p1ab1; + _ = bad; +} + +// invalid exponent in float literal - 1 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:28: note: invalid byte: 'a' diff --git a/test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-2.zig b/test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-2.zig new file mode 100644 index 0000000000..f612d8c510 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-2.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: f128 = 0x1.0p50F; + _ = bad; +} + +// invalid exponent in float literal - 2 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:29: note: invalid byte: 'F' diff --git a/test/compile_errors/stage1/obj/invalid_field_access_in_comptime.zig b/test/compile_errors/stage1/obj/invalid_field_access_in_comptime.zig new file mode 100644 index 0000000000..e8395b5e7a --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_field_access_in_comptime.zig @@ -0,0 +1,5 @@ +comptime { var x = doesnt_exist.whatever; _ = x; } + +// invalid field access in comptime +// +// tmp.zig:1:20: error: use of undeclared identifier 'doesnt_exist' diff --git a/test/compile_errors/stage1/obj/invalid_field_in_struct_value_expression.zig b/test/compile_errors/stage1/obj/invalid_field_in_struct_value_expression.zig new file mode 100644 index 0000000000..41511f0d0a --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_field_in_struct_value_expression.zig @@ -0,0 +1,17 @@ +const A = struct { + x : i32, + y : i32, + z : i32, +}; +export fn f() void { + const a = A { + .z = 4, + .y = 2, + .foo = 42, + }; + _ = a; +} + +// invalid field in struct value expression +// +// tmp.zig:10:9: error: no member named 'foo' in struct 'A' diff --git a/test/compile_errors/stage1/obj/invalid_float_literal.zig b/test/compile_errors/stage1/obj/invalid_float_literal.zig new file mode 100644 index 0000000000..5d9a26032f --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_float_literal.zig @@ -0,0 +1,11 @@ +const std = @import("std"); + +pub fn main() void { + var bad_float :f32 = 0.0; + bad_float = bad_float + .20; + std.debug.assert(bad_float < 1.0); +} + +// invalid float literal +// +// tmp.zig:5:29: error: expected expression, found '.' diff --git a/test/compile_errors/stage1/obj/invalid_legacy_unicode_escape.zig b/test/compile_errors/stage1/obj/invalid_legacy_unicode_escape.zig new file mode 100644 index 0000000000..58e1390345 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_legacy_unicode_escape.zig @@ -0,0 +1,8 @@ +export fn entry() void { + const a = '\U1234'; +} + +// invalid legacy unicode escape +// +// tmp.zig:2:15: error: expected expression, found 'invalid bytes' +// tmp.zig:2:18: note: invalid byte: '1' diff --git a/test/compile_errors/stage1/obj/invalid_maybe_type.zig b/test/compile_errors/stage1/obj/invalid_maybe_type.zig new file mode 100644 index 0000000000..9edc313526 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_maybe_type.zig @@ -0,0 +1,7 @@ +export fn f() void { + if (true) |x| { _ = x; } +} + +// invalid maybe type +// +// tmp.zig:2:9: error: expected optional type, found 'bool' diff --git a/test/compile_errors/stage1/obj/invalid_member_of_builtin_enum.zig b/test/compile_errors/stage1/obj/invalid_member_of_builtin_enum.zig new file mode 100644 index 0000000000..32888ff84b --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_member_of_builtin_enum.zig @@ -0,0 +1,9 @@ +const builtin = @import("std").builtin; +export fn entry() void { + const foo = builtin.Mode.x86; + _ = foo; +} + +// invalid member of builtin enum +// +// tmp.zig:3:29: error: container 'std.builtin.Mode' has no member called 'x86' diff --git a/test/compile_errors/stage1/obj/invalid_multiple_dereferences.zig b/test/compile_errors/stage1/obj/invalid_multiple_dereferences.zig new file mode 100644 index 0000000000..bdf5ccb228 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_multiple_dereferences.zig @@ -0,0 +1,17 @@ +export fn a() void { + var box = Box{ .field = 0 }; + box.*.field = 1; +} +export fn b() void { + var box = Box{ .field = 0 }; + var boxPtr = &box; + boxPtr.*.*.field = 1; +} +pub const Box = struct { + field: i32, +}; + +// invalid multiple dereferences +// +// tmp.zig:3:8: error: attempt to dereference non-pointer type 'Box' +// tmp.zig:8:13: error: attempt to dereference non-pointer type 'Box' diff --git a/test/compile_errors/stage1/obj/invalid_optional_type_in_extern_struct.zig b/test/compile_errors/stage1/obj/invalid_optional_type_in_extern_struct.zig new file mode 100644 index 0000000000..17d9477abf --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_optional_type_in_extern_struct.zig @@ -0,0 +1,8 @@ +const stroo = extern struct { + moo: ?[*c]u8, +}; +export fn testf(fluff: *stroo) void { _ = fluff; } + +// invalid optional type in extern struct +// +// tmp.zig:2:5: error: extern structs cannot contain fields of type '?[*c]u8' diff --git a/test/compile_errors/stage1/obj/invalid_pointer_for_var_type.zig b/test/compile_errors/stage1/obj/invalid_pointer_for_var_type.zig new file mode 100644 index 0000000000..599aef54f2 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_pointer_for_var_type.zig @@ -0,0 +1,11 @@ +extern fn ext() usize; +var bytes: [ext()]u8 = undefined; +export fn f() void { + for (bytes) |*b, i| { + b.* = @as(u8, i); + } +} + +// invalid pointer for var type +// +// tmp.zig:2:13: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/invalid_pointer_syntax.zig b/test/compile_errors/stage1/obj/invalid_pointer_syntax.zig new file mode 100644 index 0000000000..d4b68200b6 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_pointer_syntax.zig @@ -0,0 +1,7 @@ +export fn foo() void { + var guid: *:0 const u8 = undefined; +} + +// invalid pointer syntax +// +// tmp.zig:2:16: error: expected type expression, found ':' diff --git a/test/compile_errors/stage1/obj/invalid_shift_amount_error.zig b/test/compile_errors/stage1/obj/invalid_shift_amount_error.zig new file mode 100644 index 0000000000..f4fe1cb2ff --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_shift_amount_error.zig @@ -0,0 +1,9 @@ +const x : u8 = 2; +fn f() u16 { + return x << 8; +} +export fn entry() u16 { return f(); } + +// invalid shift amount error +// +// tmp.zig:3:17: error: integer value 8 cannot be coerced to type 'u3' diff --git a/test/compile_errors/stage1/obj/invalid_struct_field.zig b/test/compile_errors/stage1/obj/invalid_struct_field.zig new file mode 100644 index 0000000000..2d85914907 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_struct_field.zig @@ -0,0 +1,17 @@ +const A = struct { x : i32, }; +export fn f() void { + var a : A = undefined; + a.foo = 1; + const y = a.bar; + _ = y; +} +export fn g() void { + var a : A = undefined; + const y = a.bar; + _ = y; +} + +// invalid struct field +// +// tmp.zig:4:6: error: no member named 'foo' in struct 'A' +// tmp.zig:10:16: error: no member named 'bar' in struct 'A' diff --git a/test/compile_errors/stage1/obj/invalid_suspend_in_exported_function.zig b/test/compile_errors/stage1/obj/invalid_suspend_in_exported_function.zig new file mode 100644 index 0000000000..da808563a5 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_suspend_in_exported_function.zig @@ -0,0 +1,13 @@ +export fn entry() void { + var frame = async func(); + var result = await frame; + _ = result; +} +fn func() void { + suspend {} +} + +// invalid suspend in exported function +// +// tmp.zig:1:1: error: function with calling convention 'C' cannot be async +// tmp.zig:3:18: note: await here is a suspend point diff --git a/test/compile_errors/stage1/obj/invalid_type.zig b/test/compile_errors/stage1/obj/invalid_type.zig new file mode 100644 index 0000000000..0307300517 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_type.zig @@ -0,0 +1,6 @@ +fn a() bogus {} +export fn entry() void { _ = a(); } + +// invalid type +// +// tmp.zig:1:8: error: use of undeclared identifier 'bogus' diff --git a/test/compile_errors/stage1/obj/invalid_type_used_in_array_type.zig b/test/compile_errors/stage1/obj/invalid_type_used_in_array_type.zig new file mode 100644 index 0000000000..26f0e6a7db --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_type_used_in_array_type.zig @@ -0,0 +1,12 @@ +const Item = struct { + field: SomeNonexistentType, +}; +var items: [100]Item = undefined; +export fn entry() void { + const a = items[0]; + _ = a; +} + +// invalid type used in array type +// +// tmp.zig:2:12: error: use of undeclared identifier 'SomeNonexistentType' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-1.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-1.zig new file mode 100644 index 0000000000..5cea540104 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-1.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: f128 = 0._0; + _ = bad; +} + +// invalid underscore placement in float literal - 1 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:23: note: invalid byte: '_' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-10.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-10.zig new file mode 100644 index 0000000000..d789579a2b --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-10.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: f128 = 1.0__0e-1; + _ = bad; +} + +// invalid underscore placement in float literal - 10 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:25: note: invalid byte: '_' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-11.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-11.zig new file mode 100644 index 0000000000..197b114090 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-11.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: f128 = 1.0e-1__0; + _ = bad; +} + +// invalid underscore placement in float literal - 11 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:28: note: invalid byte: '_' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-12.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-12.zig new file mode 100644 index 0000000000..1d25c9f4b4 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-12.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: f128 = 0_x0.0; + _ = bad; +} + +// invalid underscore placement in float literal - 12 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:23: note: invalid byte: 'x' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-13.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-13.zig new file mode 100644 index 0000000000..c3de75ed32 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-13.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: f128 = 0x_0.0; + _ = bad; +} + +// invalid underscore placement in float literal - 13 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:23: note: invalid byte: '_' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-14.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-14.zig new file mode 100644 index 0000000000..cbb967e926 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-14.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: f128 = 0x0.0_p1; + _ = bad; +} + +// invalid underscore placement in float literal - 14 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:27: note: invalid byte: 'p' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-2.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-2.zig new file mode 100644 index 0000000000..e83c1b420b --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-2.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: f128 = 0_.0; + _ = bad; +} + +// invalid underscore placement in float literal - 2 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:23: note: invalid byte: '.' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-3.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-3.zig new file mode 100644 index 0000000000..f9ca8e0d7c --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-3.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: f128 = 0.0_; + _ = bad; +} + +// invalid underscore placement in float literal - 3 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:25: note: invalid byte: ';' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-4.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-4.zig new file mode 100644 index 0000000000..20e8d3692e --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-4.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: f128 = 1.0e_1; + _ = bad; +} + +// invalid underscore placement in float literal - 4 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:25: note: invalid byte: '_' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-5.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-5.zig new file mode 100644 index 0000000000..3719f54e06 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-5.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: f128 = 1.0e+_1; + _ = bad; +} + +// invalid underscore placement in float literal - 5 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:26: note: invalid byte: '_' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-6.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-6.zig new file mode 100644 index 0000000000..64a439538a --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-6.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: f128 = 1.0e-_1; + _ = bad; +} + +// invalid underscore placement in float literal - 6 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:26: note: invalid byte: '_' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-7.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-7.zig new file mode 100644 index 0000000000..311b27339a --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-7.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: f128 = 1.0e-1_; + _ = bad; +} + +// invalid underscore placement in float literal - 7 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:28: note: invalid byte: ';' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-9.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-9.zig new file mode 100644 index 0000000000..ecd1149f22 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-9.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: f128 = 1__0.0e-1; + _ = bad; +} + +// invalid underscore placement in float literal - 9 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:23: note: invalid byte: '_' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-1.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-1.zig new file mode 100644 index 0000000000..47da090745 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-1.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: u128 = 0010_; + _ = bad; +} + +// invalid underscore placement in int literal - 1 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:26: note: invalid byte: ';' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-2.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-2.zig new file mode 100644 index 0000000000..bb230daa9e --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-2.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: u128 = 0b0010_; + _ = bad; +} + +// invalid underscore placement in int literal - 2 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:28: note: invalid byte: ';' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-3.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-3.zig new file mode 100644 index 0000000000..7808b6ee4d --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-3.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: u128 = 0o0010_; + _ = bad; +} + +// invalid underscore placement in int literal - 3 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:28: note: invalid byte: ';' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-4.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-4.zig new file mode 100644 index 0000000000..51ddb3d061 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-4.zig @@ -0,0 +1,9 @@ +fn main() void { + var bad: u128 = 0x0010_; + _ = bad; +} + +// invalid underscore placement in int literal - 4 +// +// tmp.zig:2:21: error: expected expression, found 'invalid bytes' +// tmp.zig:2:28: note: invalid byte: ';' diff --git a/test/compile_errors/stage1/obj/invalid_union_field_access_in_comptime.zig b/test/compile_errors/stage1/obj/invalid_union_field_access_in_comptime.zig new file mode 100644 index 0000000000..d570dca0b9 --- /dev/null +++ b/test/compile_errors/stage1/obj/invalid_union_field_access_in_comptime.zig @@ -0,0 +1,13 @@ +const Foo = union { + Bar: u8, + Baz: void, +}; +comptime { + var foo = Foo {.Baz = {}}; + const bar_val = foo.Bar; + _ = bar_val; +} + +// invalid union field access in comptime +// +// tmp.zig:7:24: error: accessing union field 'Bar' while field 'Baz' is set diff --git a/test/compile_errors/stage1/obj/issue_2032_compile_diagnostic_string_for_top_level_decl_type.zig b/test/compile_errors/stage1/obj/issue_2032_compile_diagnostic_string_for_top_level_decl_type.zig new file mode 100644 index 0000000000..9e353a4d38 --- /dev/null +++ b/test/compile_errors/stage1/obj/issue_2032_compile_diagnostic_string_for_top_level_decl_type.zig @@ -0,0 +1,8 @@ +export fn entry() void { + var foo: u32 = @This(){}; + _ = foo; +} + +// compile diagnostic string for top level decl type (issue 2032) +// +// tmp.zig:2:27: error: type 'u32' does not support array initialization diff --git a/test/compile_errors/stage1/obj/issue_2687_coerce_from_undefined_array_pointer_to_slice.zig b/test/compile_errors/stage1/obj/issue_2687_coerce_from_undefined_array_pointer_to_slice.zig new file mode 100644 index 0000000000..7c125b85ce --- /dev/null +++ b/test/compile_errors/stage1/obj/issue_2687_coerce_from_undefined_array_pointer_to_slice.zig @@ -0,0 +1,25 @@ +export fn foo1() void { + const a: *[1]u8 = undefined; + var b: []u8 = a; + _ = b; +} +export fn foo2() void { + comptime { + var a: *[1]u8 = undefined; + var b: []u8 = a; + _ = b; + } +} +export fn foo3() void { + comptime { + const a: *[1]u8 = undefined; + var b: []u8 = a; + _ = b; + } +} + +// issue #2687: coerce from undefined array pointer to slice +// +// tmp.zig:3:19: error: use of undefined value here causes undefined behavior +// tmp.zig:9:23: error: use of undefined value here causes undefined behavior +// tmp.zig:16:23: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig b/test/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig new file mode 100644 index 0000000000..1c9d9cd1b1 --- /dev/null +++ b/test/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig @@ -0,0 +1,15 @@ +export fn foo1() void { + var bytes = [_]u8{1, 2}; + const word: u16 = @bitCast(u16, bytes[0..]); + _ = word; +} +export fn foo2() void { + var bytes: []const u8 = &[_]u8{1, 2}; + const word: u16 = @bitCast(u16, bytes); + _ = word; +} + +// issue #3818: bitcast from parray/slice to u16 +// +// tmp.zig:3:42: error: unable to @bitCast from pointer type '*[2]u8' +// tmp.zig:8:32: error: destination type 'u16' has size 2 but source type '[]const u8' has size 16 diff --git a/test/compile_errors/stage1/obj/issue_4207_coerce_from_non-terminated-slice_to_terminated-pointer.zig b/test/compile_errors/stage1/obj/issue_4207_coerce_from_non-terminated-slice_to_terminated-pointer.zig new file mode 100644 index 0000000000..bfb0595cce --- /dev/null +++ b/test/compile_errors/stage1/obj/issue_4207_coerce_from_non-terminated-slice_to_terminated-pointer.zig @@ -0,0 +1,9 @@ +export fn foo() [*:0]const u8 { + var buffer: [64]u8 = undefined; + return buffer[0..]; +} + +// issue #4207: coerce from non-terminated-slice to terminated-pointer +// +// :3:18: error: expected type '[*:0]const u8', found '*[64]u8' +// :3:18: note: destination pointer requires a terminating '0' sentinel diff --git a/test/compile_errors/stage1/obj/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig b/test/compile_errors/stage1/obj/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig new file mode 100644 index 0000000000..aa839bc72c --- /dev/null +++ b/test/compile_errors/stage1/obj/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig @@ -0,0 +1,14 @@ +fn ignore(comptime param: anytype) void {_ = param;} + +export fn foo() void { + const MyStruct = struct { + wrong_type: []u8 = "foo", + }; + + comptime ignore(@typeInfo(MyStruct).Struct.fields[0]); +} + +// issue #5221: invalid struct init type referenced by @typeInfo and passed into function +// +// :5:28: error: cannot cast pointer to array literal to slice type '[]u8' +// :5:28: note: cast discards const qualifier diff --git a/test/compile_errors/stage1/obj/issue_5618_coercion_of_optional_anyopaque_to_anyopaque_must_fail.zig b/test/compile_errors/stage1/obj/issue_5618_coercion_of_optional_anyopaque_to_anyopaque_must_fail.zig new file mode 100644 index 0000000000..daa8a91f40 --- /dev/null +++ b/test/compile_errors/stage1/obj/issue_5618_coercion_of_optional_anyopaque_to_anyopaque_must_fail.zig @@ -0,0 +1,9 @@ +export fn foo() void { + var u: ?*anyopaque = null; + var v: *anyopaque = undefined; + v = u; +} + +// Issue #5618: coercion of ?*anyopaque to *anyopaque must fail. +// +// tmp.zig:4:9: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type '*anyopaque', found '?*anyopaque' diff --git a/test/compile_errors/stage1/obj/issue_7810-comptime_slice-len_increment_beyond_bounds.zig b/test/compile_errors/stage1/obj/issue_7810-comptime_slice-len_increment_beyond_bounds.zig new file mode 100644 index 0000000000..533ac928b4 --- /dev/null +++ b/test/compile_errors/stage1/obj/issue_7810-comptime_slice-len_increment_beyond_bounds.zig @@ -0,0 +1,12 @@ +export fn foo_slice_len_increment_beyond_bounds() void { + comptime { + var buf_storage: [8]u8 = undefined; + var buf: []const u8 = buf_storage[0..]; + buf.len += 1; + buf[8] = 42; + } +} + +// comptime slice-len increment beyond bounds +// +// :6:12: error: out of bounds slice diff --git a/test/compile_errors/stage1/obj/issue_9346_return_outside_of_function_scope.zig b/test/compile_errors/stage1/obj/issue_9346_return_outside_of_function_scope.zig new file mode 100644 index 0000000000..6be92e2ee8 --- /dev/null +++ b/test/compile_errors/stage1/obj/issue_9346_return_outside_of_function_scope.zig @@ -0,0 +1,5 @@ +pub const empty = return 1; + +// issue #9346: return outside of function scope +// +// tmp.zig:1:19: error: 'return' outside function scope diff --git a/test/compile_errors/stage1/obj/labeled_break_not_found.zig b/test/compile_errors/stage1/obj/labeled_break_not_found.zig new file mode 100644 index 0000000000..0f4632d74d --- /dev/null +++ b/test/compile_errors/stage1/obj/labeled_break_not_found.zig @@ -0,0 +1,11 @@ +export fn entry() void { + blah: while (true) { + while (true) { + break :outer; + } + } +} + +// labeled break not found +// +// tmp.zig:4:20: error: label not found: 'outer' diff --git a/test/compile_errors/stage1/obj/labeled_continue_not_found.zig b/test/compile_errors/stage1/obj/labeled_continue_not_found.zig new file mode 100644 index 0000000000..8f95a0fce1 --- /dev/null +++ b/test/compile_errors/stage1/obj/labeled_continue_not_found.zig @@ -0,0 +1,12 @@ +export fn entry() void { + var i: usize = 0; + blah: while (i < 10) : (i += 1) { + while (true) { + continue :outer; + } + } +} + +// labeled continue not found +// +// tmp.zig:5:23: error: label not found: 'outer' diff --git a/test/compile_errors/stage1/obj/lazy_pointer_with_undefined_element_type.zig b/test/compile_errors/stage1/obj/lazy_pointer_with_undefined_element_type.zig new file mode 100644 index 0000000000..47b271a05e --- /dev/null +++ b/test/compile_errors/stage1/obj/lazy_pointer_with_undefined_element_type.zig @@ -0,0 +1,10 @@ +export fn foo() void { + comptime var T: type = undefined; + const S = struct { x: *T }; + const I = @typeInfo(S); + _ = I; +} + +// lazy pointer with undefined element type +// +// :3:28: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig b/test/compile_errors/stage1/obj/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig new file mode 100644 index 0000000000..2c5e1c7327 --- /dev/null +++ b/test/compile_errors/stage1/obj/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig @@ -0,0 +1,11 @@ +export fn entry() void { + const float: f32 = 5.99999999999994648725e-01; + const float_ptr = &float; + const int_ptr = @ptrCast(*const i64, float_ptr); + const int_val = int_ptr.*; + _ = int_val; +} + +// load too many bytes from comptime reinterpreted pointer +// +// tmp.zig:5:28: error: attempt to read 8 bytes from pointer to f32 which is 4 bytes diff --git a/test/compile_errors/stage1/obj/load_vector_pointer_with_unknown_runtime_index.zig b/test/compile_errors/stage1/obj/load_vector_pointer_with_unknown_runtime_index.zig new file mode 100644 index 0000000000..fe28fa9102 --- /dev/null +++ b/test/compile_errors/stage1/obj/load_vector_pointer_with_unknown_runtime_index.zig @@ -0,0 +1,15 @@ +export fn entry() void { + var v: @import("std").meta.Vector(4, i32) = [_]i32{ 1, 5, 3, undefined }; + + var i: u32 = 0; + var x = loadv(&v[i]); + _ = x; +} + +fn loadv(ptr: anytype) i32 { + return ptr.*; +} + +// load vector pointer with unknown runtime index +// +// tmp.zig:10:12: error: unable to determine vector element index of type '*align(16:0:4:?) i32 diff --git a/test/compile_errors/stage1/obj/local_shadows_global_that_occurs_later.zig b/test/compile_errors/stage1/obj/local_shadows_global_that_occurs_later.zig new file mode 100644 index 0000000000..2265a34f18 --- /dev/null +++ b/test/compile_errors/stage1/obj/local_shadows_global_that_occurs_later.zig @@ -0,0 +1,10 @@ +pub fn main() void { + var foo = true; + _ = foo; +} +fn foo() void {} + +// local shadows global that occurs later +// +// tmp.zig:2:9: error: local shadows declaration of 'foo' +// tmp.zig:5:1: note: declared here diff --git a/test/compile_errors/stage1/obj/local_variable_redeclaration.zig b/test/compile_errors/stage1/obj/local_variable_redeclaration.zig new file mode 100644 index 0000000000..2367963edb --- /dev/null +++ b/test/compile_errors/stage1/obj/local_variable_redeclaration.zig @@ -0,0 +1,9 @@ +export fn f() void { + const a : i32 = 0; + var a = 0; +} + +// local variable redeclaration +// +// tmp.zig:3:9: error: redeclaration of local constant 'a' +// tmp.zig:2:11: note: previous declaration here diff --git a/test/compile_errors/stage1/obj/local_variable_redeclares_parameter.zig b/test/compile_errors/stage1/obj/local_variable_redeclares_parameter.zig new file mode 100644 index 0000000000..e624e14e31 --- /dev/null +++ b/test/compile_errors/stage1/obj/local_variable_redeclares_parameter.zig @@ -0,0 +1,9 @@ +fn f(a : i32) void { + const a = 0; +} +export fn entry() void { f(1); } + +// local variable redeclares parameter +// +// tmp.zig:2:11: error: redeclaration of function parameter 'a' +// tmp.zig:1:6: note: previous declaration here diff --git a/test/compile_errors/stage1/obj/local_variable_shadowing_global.zig b/test/compile_errors/stage1/obj/local_variable_shadowing_global.zig new file mode 100644 index 0000000000..c824cb3df7 --- /dev/null +++ b/test/compile_errors/stage1/obj/local_variable_shadowing_global.zig @@ -0,0 +1,12 @@ +const Foo = struct {}; +const Bar = struct {}; + +export fn entry() void { + var Bar : i32 = undefined; + _ = Bar; +} + +// local variable shadowing global +// +// tmp.zig:5:9: error: local shadows declaration of 'Bar' +// tmp.zig:2:1: note: declared here diff --git a/test/compile_errors/stage1/obj/locally_shadowing_a_primitive_type.zig b/test/compile_errors/stage1/obj/locally_shadowing_a_primitive_type.zig new file mode 100644 index 0000000000..457cea418c --- /dev/null +++ b/test/compile_errors/stage1/obj/locally_shadowing_a_primitive_type.zig @@ -0,0 +1,10 @@ +export fn foo() void { + const u8 = u16; + const a: u8 = 300; + _ = a; +} + +// locally shadowing a primitive type +// +// tmp.zig:2:11: error: name shadows primitive 'u8' +// tmp.zig:2:11: note: consider using @"u8" to disambiguate diff --git a/test/compile_errors/stage1/obj/main_function_with_bogus_args_type.zig b/test/compile_errors/stage1/obj/main_function_with_bogus_args_type.zig new file mode 100644 index 0000000000..3954c4b846 --- /dev/null +++ b/test/compile_errors/stage1/obj/main_function_with_bogus_args_type.zig @@ -0,0 +1,5 @@ +pub fn main(args: [][]bogus) !void {_ = args;} + +// main function with bogus args type +// +// tmp.zig:1:23: error: use of undeclared identifier 'bogus' diff --git a/test/compile_errors/stage1/obj/method_call_with_first_arg_type_primitive.zig b/test/compile_errors/stage1/obj/method_call_with_first_arg_type_primitive.zig new file mode 100644 index 0000000000..9763e84fba --- /dev/null +++ b/test/compile_errors/stage1/obj/method_call_with_first_arg_type_primitive.zig @@ -0,0 +1,19 @@ +const Foo = struct { + x: i32, + + fn init(x: i32) Foo { + return Foo { + .x = x, + }; + } +}; + +export fn f() void { + const derp = Foo.init(3); + + derp.init(); +} + +// method call with first arg type primitive +// +// tmp.zig:14:5: error: expected type 'i32', found 'Foo' diff --git a/test/compile_errors/stage1/obj/method_call_with_first_arg_type_wrong_container.zig b/test/compile_errors/stage1/obj/method_call_with_first_arg_type_wrong_container.zig new file mode 100644 index 0000000000..ccbc7e32e3 --- /dev/null +++ b/test/compile_errors/stage1/obj/method_call_with_first_arg_type_wrong_container.zig @@ -0,0 +1,28 @@ +pub const List = struct { + len: usize, + allocator: *Allocator, + + pub fn init(allocator: *Allocator) List { + return List { + .len = 0, + .allocator = allocator, + }; + } +}; + +pub var global_allocator = Allocator { + .field = 1234, +}; + +pub const Allocator = struct { + field: i32, +}; + +export fn foo() void { + var x = List.init(&global_allocator); + x.init(); +} + +// method call with first arg type wrong container +// +// tmp.zig:23:5: error: expected type '*Allocator', found '*List' diff --git a/test/compile_errors/stage1/obj/missing_boolean_switch_value.zig b/test/compile_errors/stage1/obj/missing_boolean_switch_value.zig new file mode 100644 index 0000000000..63a12c4e6f --- /dev/null +++ b/test/compile_errors/stage1/obj/missing_boolean_switch_value.zig @@ -0,0 +1,17 @@ +comptime { + const x = switch (true) { + true => false, + }; + _ = x; +} +comptime { + const x = switch (true) { + false => true, + }; + _ = x; +} + +// missing boolean switch value +// +// tmp.zig:2:15: error: switch must handle all possibilities +// tmp.zig:8:15: error: switch must handle all possibilities diff --git a/test/compile_errors/stage1/obj/missing_const_in_slice_with_nested_array_type.zig b/test/compile_errors/stage1/obj/missing_const_in_slice_with_nested_array_type.zig new file mode 100644 index 0000000000..4f552284d7 --- /dev/null +++ b/test/compile_errors/stage1/obj/missing_const_in_slice_with_nested_array_type.zig @@ -0,0 +1,16 @@ +const Geo3DTex2D = struct { vertices: [][2]f32 }; +pub fn getGeo3DTex2D() Geo3DTex2D { + return Geo3DTex2D{ + .vertices = [_][2]f32{ + [_]f32{ -0.5, -0.5}, + }, + }; +} +export fn entry() void { + var geo_data = getGeo3DTex2D(); + _ = geo_data; +} + +// missing const in slice with nested array type +// +// tmp.zig:4:30: error: array literal requires address-of operator (&) to coerce to slice type '[][2]f32' diff --git a/test/compile_errors/stage1/obj/missing_else_clause.zig b/test/compile_errors/stage1/obj/missing_else_clause.zig new file mode 100644 index 0000000000..28adfd21e8 --- /dev/null +++ b/test/compile_errors/stage1/obj/missing_else_clause.zig @@ -0,0 +1,14 @@ +fn f(b: bool) void { + const x : i32 = if (b) h: { break :h 1; }; + _ = x; +} +fn g(b: bool) void { + const y = if (b) h: { break :h @as(i32, 1); }; + _ = y; +} +export fn entry() void { f(true); g(true); } + +// missing else clause +// +// tmp.zig:2:21: error: expected type 'i32', found 'void' +// tmp.zig:6:15: error: incompatible types: 'i32' and 'void' diff --git a/test/compile_errors/stage1/obj/missing_field_in_struct_value_expression.zig b/test/compile_errors/stage1/obj/missing_field_in_struct_value_expression.zig new file mode 100644 index 0000000000..bea4021a5a --- /dev/null +++ b/test/compile_errors/stage1/obj/missing_field_in_struct_value_expression.zig @@ -0,0 +1,18 @@ +const A = struct { + x : i32, + y : i32, + z : i32, +}; +export fn f() void { + // we want the error on the '{' not the 'A' because + // the A could be a complicated expression + const a = A { + .z = 4, + .y = 2, + }; + _ = a; +} + +// missing field in struct value expression +// +// tmp.zig:9:17: error: missing field: 'x' diff --git a/test/compile_errors/stage1/obj/missing_function_call_param.zig b/test/compile_errors/stage1/obj/missing_function_call_param.zig new file mode 100644 index 0000000000..45a7259814 --- /dev/null +++ b/test/compile_errors/stage1/obj/missing_function_call_param.zig @@ -0,0 +1,29 @@ +const Foo = struct { + a: i32, + b: i32, + + fn member_a(foo: *const Foo) i32 { + return foo.a; + } + fn member_b(foo: *const Foo) i32 { + return foo.b; + } +}; + +const member_fn_type = @TypeOf(Foo.member_a); +const members = [_]member_fn_type { + Foo.member_a, + Foo.member_b, +}; + +fn f(foo: *const Foo, index: usize) void { + const result = members[index](); + _ = foo; + _ = result; +} + +export fn entry() usize { return @sizeOf(@TypeOf(f)); } + +// missing function call param +// +// tmp.zig:20:34: error: expected 1 argument(s), found 0 diff --git a/test/compile_errors/stage1/obj/missing_function_name.zig b/test/compile_errors/stage1/obj/missing_function_name.zig new file mode 100644 index 0000000000..194f5eb6a8 --- /dev/null +++ b/test/compile_errors/stage1/obj/missing_function_name.zig @@ -0,0 +1,6 @@ +fn () void {} +export fn entry() usize { return @sizeOf(@TypeOf(f)); } + +// missing function name +// +// tmp.zig:1:1: error: missing function name diff --git a/test/compile_errors/stage1/obj/missing_param_name.zig b/test/compile_errors/stage1/obj/missing_param_name.zig new file mode 100644 index 0000000000..1c379dd245 --- /dev/null +++ b/test/compile_errors/stage1/obj/missing_param_name.zig @@ -0,0 +1,6 @@ +fn f(i32) void {} +export fn entry() usize { return @sizeOf(@TypeOf(f)); } + +// missing param name +// +// tmp.zig:1:6: error: missing parameter name diff --git a/test/compile_errors/stage1/obj/missing_parameter_name_of_generic_function.zig b/test/compile_errors/stage1/obj/missing_parameter_name_of_generic_function.zig new file mode 100644 index 0000000000..3a3ccd28a8 --- /dev/null +++ b/test/compile_errors/stage1/obj/missing_parameter_name_of_generic_function.zig @@ -0,0 +1,9 @@ +fn dump(anytype) void {} +export fn entry() void { + var a: u8 = 9; + dump(a); +} + +// missing parameter name of generic function +// +// tmp.zig:1:9: error: missing parameter name diff --git a/test/compile_errors/stage1/obj/missing_result_type_for_phi_node.zig b/test/compile_errors/stage1/obj/missing_result_type_for_phi_node.zig new file mode 100644 index 0000000000..3ef99e0859 --- /dev/null +++ b/test/compile_errors/stage1/obj/missing_result_type_for_phi_node.zig @@ -0,0 +1,10 @@ +fn foo() !void { + return anyerror.Foo; +} +export fn entry() void { + foo() catch 0; +} + +// missing result type for phi node +// +// tmp.zig:5:17: error: integer value 0 cannot be coerced to type 'void' diff --git a/test/compile_errors/stage1/obj/misspelled_type_with_pointer_only_reference.zig b/test/compile_errors/stage1/obj/misspelled_type_with_pointer_only_reference.zig new file mode 100644 index 0000000000..6b3f647c02 --- /dev/null +++ b/test/compile_errors/stage1/obj/misspelled_type_with_pointer_only_reference.zig @@ -0,0 +1,35 @@ +const JasonHM = u8; +const JasonList = *JsonNode; + +const JsonOA = union(enum) { + JSONArray: JsonList, + JSONObject: JasonHM, +}; + +const JsonType = union(enum) { + JSONNull: void, + JSONInteger: isize, + JSONDouble: f64, + JSONBool: bool, + JSONString: []u8, + JSONArray: void, + JSONObject: void, +}; + +pub const JsonNode = struct { + kind: JsonType, + jobject: ?JsonOA, +}; + +fn foo() void { + var jll: JasonList = undefined; + jll.init(1234); + var jd = JsonNode {.kind = JsonType.JSONArray , .jobject = JsonOA.JSONArray {jll} }; + _ = jd; +} + +export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + +// misspelled type with pointer only reference +// +// tmp.zig:5:16: error: use of undeclared identifier 'JsonList' diff --git a/test/compile_errors/stage1/obj/mod_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/mod_assign_on_undefined_value.zig new file mode 100644 index 0000000000..3165970d44 --- /dev/null +++ b/test/compile_errors/stage1/obj/mod_assign_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + a %= a; +} + +// mod assign on undefined value +// +// tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/mod_on_undefined_value.zig b/test/compile_errors/stage1/obj/mod_on_undefined_value.zig new file mode 100644 index 0000000000..3b33f6ed82 --- /dev/null +++ b/test/compile_errors/stage1/obj/mod_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + _ = a % a; +} + +// mod on undefined value +// +// tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/mul_overflow_in_function_evaluation.zig b/test/compile_errors/stage1/obj/mul_overflow_in_function_evaluation.zig new file mode 100644 index 0000000000..ea8eea5ab5 --- /dev/null +++ b/test/compile_errors/stage1/obj/mul_overflow_in_function_evaluation.zig @@ -0,0 +1,10 @@ +const y = mul(300, 6000); +fn mul(a: u16, b: u16) u16 { + return a * b; +} + +export fn entry() usize { return @sizeOf(@TypeOf(y)); } + +// mul overflow in function evaluation +// +// tmp.zig:3:14: error: operation caused overflow diff --git a/test/compile_errors/stage1/obj/mult_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/mult_assign_on_undefined_value.zig new file mode 100644 index 0000000000..4617c5f62b --- /dev/null +++ b/test/compile_errors/stage1/obj/mult_assign_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + a *= a; +} + +// mult assign on undefined value +// +// tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/mult_on_undefined_value.zig b/test/compile_errors/stage1/obj/mult_on_undefined_value.zig new file mode 100644 index 0000000000..836b59e19d --- /dev/null +++ b/test/compile_errors/stage1/obj/mult_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + _ = a * a; +} + +// mult on undefined value +// +// tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/mult_wrap_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/mult_wrap_assign_on_undefined_value.zig new file mode 100644 index 0000000000..38bd38bf79 --- /dev/null +++ b/test/compile_errors/stage1/obj/mult_wrap_assign_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + a *%= a; +} + +// mult wrap assign on undefined value +// +// tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/mult_wrap_on_undefined_value.zig b/test/compile_errors/stage1/obj/mult_wrap_on_undefined_value.zig new file mode 100644 index 0000000000..0c0ca46540 --- /dev/null +++ b/test/compile_errors/stage1/obj/mult_wrap_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + _ = a *% a; +} + +// mult wrap on undefined value +// +// tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/multiple_function_definitions.zig b/test/compile_errors/stage1/obj/multiple_function_definitions.zig new file mode 100644 index 0000000000..c4efefce8d --- /dev/null +++ b/test/compile_errors/stage1/obj/multiple_function_definitions.zig @@ -0,0 +1,8 @@ +fn a() void {} +fn a() void {} +export fn entry() void { a(); } + +// multiple function definitions +// +// tmp.zig:2:1: error: redeclaration of 'a' +// tmp.zig:1:1: note: other declaration here diff --git a/test/compile_errors/stage1/obj/negate_on_undefined_value.zig b/test/compile_errors/stage1/obj/negate_on_undefined_value.zig new file mode 100644 index 0000000000..7e7e392104 --- /dev/null +++ b/test/compile_errors/stage1/obj/negate_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + _ = -a; +} + +// negate on undefined value +// +// tmp.zig:3:10: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/negate_wrap_on_undefined_value.zig b/test/compile_errors/stage1/obj/negate_wrap_on_undefined_value.zig new file mode 100644 index 0000000000..f49c6eac34 --- /dev/null +++ b/test/compile_errors/stage1/obj/negate_wrap_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + _ = -%a; +} + +// negate wrap on undefined value +// +// tmp.zig:3:11: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/negation_overflow_in_function_evaluation.zig b/test/compile_errors/stage1/obj/negation_overflow_in_function_evaluation.zig new file mode 100644 index 0000000000..ca69b6fb5d --- /dev/null +++ b/test/compile_errors/stage1/obj/negation_overflow_in_function_evaluation.zig @@ -0,0 +1,10 @@ +const y = neg(-128); +fn neg(x: i8) i8 { + return -x; +} + +export fn entry() usize { return @sizeOf(@TypeOf(y)); } + +// negation overflow in function evaluation +// +// tmp.zig:3:12: error: negation caused overflow diff --git a/test/compile_errors/stage1/obj/nested_error_set_mismatch.zig b/test/compile_errors/stage1/obj/nested_error_set_mismatch.zig new file mode 100644 index 0000000000..6c677049df --- /dev/null +++ b/test/compile_errors/stage1/obj/nested_error_set_mismatch.zig @@ -0,0 +1,18 @@ +const NextError = error{NextError}; +const OtherError = error{OutOfMemory}; + +export fn entry() void { + const a: ?NextError!i32 = foo(); + _ = a; +} + +fn foo() ?OtherError!i32 { + return null; +} + +// nested error set mismatch +// +// tmp.zig:5:34: error: expected type '?NextError!i32', found '?OtherError!i32' +// tmp.zig:5:34: note: optional type child 'OtherError!i32' cannot cast into optional type child 'NextError!i32' +// tmp.zig:5:34: note: error set 'OtherError' cannot cast into error set 'NextError' +// tmp.zig:2:26: note: 'error.OutOfMemory' not a member of destination error set diff --git a/test/compile_errors/stage1/obj/no_else_prong_on_switch_on_global_error_set.zig b/test/compile_errors/stage1/obj/no_else_prong_on_switch_on_global_error_set.zig new file mode 100644 index 0000000000..7de3f189f0 --- /dev/null +++ b/test/compile_errors/stage1/obj/no_else_prong_on_switch_on_global_error_set.zig @@ -0,0 +1,12 @@ +export fn entry() void { + foo(error.A); +} +fn foo(a: anyerror) void { + switch (a) { + error.A => {}, + } +} + +// no else prong on switch on global error set +// +// tmp.zig:5:5: error: else prong required when switching on type 'anyerror' diff --git a/test/compile_errors/stage1/obj/noalias_on_non_pointer_param.zig b/test/compile_errors/stage1/obj/noalias_on_non_pointer_param.zig new file mode 100644 index 0000000000..3419dc8c99 --- /dev/null +++ b/test/compile_errors/stage1/obj/noalias_on_non_pointer_param.zig @@ -0,0 +1,6 @@ +fn f(noalias x: i32) void { _ = x; } +export fn entry() void { f(1234); } + +// noalias on non pointer param +// +// tmp.zig:1:6: error: noalias on non-pointer parameter diff --git a/test/compile_errors/stage1/obj/non-async_function_pointer_eventually_is_inferred_to_become_async.zig b/test/compile_errors/stage1/obj/non-async_function_pointer_eventually_is_inferred_to_become_async.zig new file mode 100644 index 0000000000..93391daaef --- /dev/null +++ b/test/compile_errors/stage1/obj/non-async_function_pointer_eventually_is_inferred_to_become_async.zig @@ -0,0 +1,13 @@ +export fn a() void { + var non_async_fn: fn () void = undefined; + non_async_fn = func; +} +fn func() void { + suspend {} +} + +// non-async function pointer eventually is inferred to become async +// +// tmp.zig:5:1: error: 'func' cannot be async +// tmp.zig:3:20: note: required to be non-async here +// tmp.zig:6:5: note: suspends here diff --git a/test/compile_errors/stage1/obj/non-const_expression_function_call_with_struct_return_value_outside_function.zig b/test/compile_errors/stage1/obj/non-const_expression_function_call_with_struct_return_value_outside_function.zig new file mode 100644 index 0000000000..d6a463e71f --- /dev/null +++ b/test/compile_errors/stage1/obj/non-const_expression_function_call_with_struct_return_value_outside_function.zig @@ -0,0 +1,15 @@ +const Foo = struct { + x: i32, +}; +const a = get_it(); +fn get_it() Foo { + global_side_effect = true; + return Foo {.x = 13}; +} +var global_side_effect = false; + +export fn entry() usize { return @sizeOf(@TypeOf(a)); } + +// non-const expression function call with struct return value outside function +// +// tmp.zig:6:26: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/non-const_expression_in_struct_literal_outside_function.zig b/test/compile_errors/stage1/obj/non-const_expression_in_struct_literal_outside_function.zig new file mode 100644 index 0000000000..ef7b1309c3 --- /dev/null +++ b/test/compile_errors/stage1/obj/non-const_expression_in_struct_literal_outside_function.zig @@ -0,0 +1,11 @@ +const Foo = struct { + x: i32, +}; +const a = Foo {.x = get_it()}; +extern fn get_it() i32; + +export fn entry() usize { return @sizeOf(@TypeOf(a)); } + +// non-const expression in struct literal outside function +// +// tmp.zig:4:21: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/non-const_switch_number_literal.zig b/test/compile_errors/stage1/obj/non-const_switch_number_literal.zig new file mode 100644 index 0000000000..aecaa7dcb6 --- /dev/null +++ b/test/compile_errors/stage1/obj/non-const_switch_number_literal.zig @@ -0,0 +1,15 @@ +export fn foo() void { + const x = switch (bar()) { + 1, 2 => 1, + 3, 4 => 2, + else => 3, + }; + _ = x; +} +fn bar() i32 { + return 2; +} + +// non-const switch number literal +// +// tmp.zig:5:17: error: cannot store runtime value in type 'comptime_int' diff --git a/test/compile_errors/stage1/obj/non-const_variables_of_things_that_require_const_variables.zig b/test/compile_errors/stage1/obj/non-const_variables_of_things_that_require_const_variables.zig new file mode 100644 index 0000000000..0598e04e4b --- /dev/null +++ b/test/compile_errors/stage1/obj/non-const_variables_of_things_that_require_const_variables.zig @@ -0,0 +1,49 @@ +export fn entry1() void { + var m2 = &2; + _ = m2; +} +export fn entry2() void { + var a = undefined; + _ = a; +} +export fn entry3() void { + var b = 1; + _ = b; +} +export fn entry4() void { + var c = 1.0; + _ = c; +} +export fn entry5() void { + var d = null; + _ = d; +} +export fn entry6(opaque_: *Opaque) void { + var e = opaque_.*; + _ = e; +} +export fn entry7() void { + var f = i32; + _ = f; +} +export fn entry8() void { + var h = (Foo {}).bar; + _ = h; +} +const Opaque = opaque {}; +const Foo = struct { + fn bar(self: *const Foo) void {_ = self;} +}; + +// non-const variables of things that require const variables +// +// tmp.zig:2:4: error: variable of type '*const comptime_int' must be const or comptime +// tmp.zig:6:4: error: variable of type '@Type(.Undefined)' must be const or comptime +// tmp.zig:10:4: error: variable of type 'comptime_int' must be const or comptime +// tmp.zig:10:4: note: to modify this variable at runtime, it must be given an explicit fixed-size number type +// tmp.zig:14:4: error: variable of type 'comptime_float' must be const or comptime +// tmp.zig:14:4: note: to modify this variable at runtime, it must be given an explicit fixed-size number type +// tmp.zig:18:4: error: variable of type '@Type(.Null)' must be const or comptime +// tmp.zig:22:4: error: variable of type 'Opaque' not allowed +// tmp.zig:26:4: error: variable of type 'type' must be const or comptime +// tmp.zig:30:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime diff --git a/test/compile_errors/stage1/obj/non-enum_tag_type_passed_to_union.zig b/test/compile_errors/stage1/obj/non-enum_tag_type_passed_to_union.zig new file mode 100644 index 0000000000..3161f16b28 --- /dev/null +++ b/test/compile_errors/stage1/obj/non-enum_tag_type_passed_to_union.zig @@ -0,0 +1,11 @@ +const Foo = union(u32) { + A: i32, +}; +export fn entry() void { + const x = @typeInfo(Foo).Union.tag_type.?; + _ = x; +} + +// non-enum tag type passed to union +// +// tmp.zig:1:19: error: expected enum tag type, found 'u32' diff --git a/test/compile_errors/stage1/obj/non-extern_function_with_var_args.zig b/test/compile_errors/stage1/obj/non-extern_function_with_var_args.zig new file mode 100644 index 0000000000..02b99383c1 --- /dev/null +++ b/test/compile_errors/stage1/obj/non-extern_function_with_var_args.zig @@ -0,0 +1,8 @@ +fn foo(args: ...) void {} +export fn entry() void { + foo(); +} + +// non-extern function with var args +// +// tmp.zig:1:14: error: expected type expression, found '...' diff --git a/test/compile_errors/stage1/obj/non-inline_for_loop_on_a_type_that_requires_comptime.zig b/test/compile_errors/stage1/obj/non-inline_for_loop_on_a_type_that_requires_comptime.zig new file mode 100644 index 0000000000..9190e7cea8 --- /dev/null +++ b/test/compile_errors/stage1/obj/non-inline_for_loop_on_a_type_that_requires_comptime.zig @@ -0,0 +1,12 @@ +const Foo = struct { + name: []const u8, + T: type, +}; +export fn entry() void { + const xx: [2]Foo = undefined; + for (xx) |f| { _ = f;} +} + +// non-inline for loop on a type that requires comptime +// +// tmp.zig:7:5: error: values of type 'Foo' must be comptime known, but index value is runtime known diff --git a/test/compile_errors/stage1/obj/non-integer_tag_type_to_automatic_union_enum.zig b/test/compile_errors/stage1/obj/non-integer_tag_type_to_automatic_union_enum.zig new file mode 100644 index 0000000000..a84ca36f6c --- /dev/null +++ b/test/compile_errors/stage1/obj/non-integer_tag_type_to_automatic_union_enum.zig @@ -0,0 +1,11 @@ +const Foo = union(enum(f32)) { + A: i32, +}; +export fn entry() void { + const x = @typeInfo(Foo).Union.tag_type.?; + _ = x; +} + +// non-integer tag type to automatic union enum +// +// tmp.zig:1:24: error: expected integer tag type, found 'f32' diff --git a/test/compile_errors/stage1/obj/non-pure_function_returns_type.zig b/test/compile_errors/stage1/obj/non-pure_function_returns_type.zig new file mode 100644 index 0000000000..0925207b9f --- /dev/null +++ b/test/compile_errors/stage1/obj/non-pure_function_returns_type.zig @@ -0,0 +1,22 @@ +var a: u32 = 0; +pub fn List(comptime T: type) type { + a += 1; + return SmallList(T, 8); +} + +pub fn SmallList(comptime T: type, comptime STATIC_SIZE: usize) type { + return struct { + items: []T, + length: usize, + prealloc_items: [STATIC_SIZE]T, + }; +} + +export fn function_with_return_type_type() void { + var list: List(i32) = undefined; + list.length = 10; +} + +// non-pure function returns type +// +// tmp.zig:3:7: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/non_async_function_pointer_passed_to_asyncCall.zig b/test/compile_errors/stage1/obj/non_async_function_pointer_passed_to_asyncCall.zig new file mode 100644 index 0000000000..73c4fd96ca --- /dev/null +++ b/test/compile_errors/stage1/obj/non_async_function_pointer_passed_to_asyncCall.zig @@ -0,0 +1,10 @@ +export fn entry() void { + var ptr = afunc; + var bytes: [100]u8 align(16) = undefined; + _ = @asyncCall(&bytes, {}, ptr, .{}); +} +fn afunc() void { } + +// non async function pointer passed to @asyncCall +// +// tmp.zig:4:32: error: expected async function, found 'fn() void' diff --git a/test/compile_errors/stage1/obj/non_compile_time_array_concatenation.zig b/test/compile_errors/stage1/obj/non_compile_time_array_concatenation.zig new file mode 100644 index 0000000000..c119cc03f7 --- /dev/null +++ b/test/compile_errors/stage1/obj/non_compile_time_array_concatenation.zig @@ -0,0 +1,9 @@ +fn f() []u8 { + return s ++ "foo"; +} +var s: [10]u8 = undefined; +export fn entry() usize { return @sizeOf(@TypeOf(f)); } + +// non compile time array concatenation +// +// tmp.zig:2:12: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/non_constant_expression_in_array_size.zig b/test/compile_errors/stage1/obj/non_constant_expression_in_array_size.zig new file mode 100644 index 0000000000..fe28921265 --- /dev/null +++ b/test/compile_errors/stage1/obj/non_constant_expression_in_array_size.zig @@ -0,0 +1,12 @@ +const Foo = struct { + y: [get()]u8, +}; +var global_var: usize = 1; +fn get() usize { return global_var; } + +export fn entry() usize { return @sizeOf(@TypeOf(Foo)); } + +// non constant expression in array size +// +// tmp.zig:5:25: error: cannot store runtime value in compile time variable +// tmp.zig:2:12: note: called from here diff --git a/test/compile_errors/stage1/obj/non_error_sets_used_in_merge_error_sets_operator.zig b/test/compile_errors/stage1/obj/non_error_sets_used_in_merge_error_sets_operator.zig new file mode 100644 index 0000000000..c600bea8b0 --- /dev/null +++ b/test/compile_errors/stage1/obj/non_error_sets_used_in_merge_error_sets_operator.zig @@ -0,0 +1,15 @@ +export fn foo() void { + const Errors = u8 || u16; + _ = Errors; +} +export fn bar() void { + const Errors = error{} || u16; + _ = Errors; +} + +// non error sets used in merge error sets operator +// +// tmp.zig:2:20: error: expected error set type, found type 'u8' +// tmp.zig:2:23: note: `||` merges error sets; `or` performs boolean OR +// tmp.zig:6:31: error: expected error set type, found type 'u16' +// tmp.zig:6:28: note: `||` merges error sets; `or` performs boolean OR diff --git a/test/compile_errors/stage1/obj/non_float_passed_to_floatToInt.zig b/test/compile_errors/stage1/obj/non_float_passed_to_floatToInt.zig new file mode 100644 index 0000000000..cad658ba5a --- /dev/null +++ b/test/compile_errors/stage1/obj/non_float_passed_to_floatToInt.zig @@ -0,0 +1,8 @@ +export fn entry() void { + const x = @floatToInt(i32, @as(i32, 54)); + _ = x; +} + +// non float passed to @floatToInt +// +// tmp.zig:2:32: error: expected float type, found 'i32' diff --git a/test/compile_errors/stage1/obj/non_int_passed_to_intToFloat.zig b/test/compile_errors/stage1/obj/non_int_passed_to_intToFloat.zig new file mode 100644 index 0000000000..8b985c2b0b --- /dev/null +++ b/test/compile_errors/stage1/obj/non_int_passed_to_intToFloat.zig @@ -0,0 +1,8 @@ +export fn entry() void { + const x = @intToFloat(f32, 1.1); + _ = x; +} + +// non int passed to @intToFloat +// +// tmp.zig:2:32: error: expected int type, found 'comptime_float' diff --git a/test/compile_errors/stage1/obj/non_pointer_given_to_ptrToInt.zig b/test/compile_errors/stage1/obj/non_pointer_given_to_ptrToInt.zig new file mode 100644 index 0000000000..1d4d3087da --- /dev/null +++ b/test/compile_errors/stage1/obj/non_pointer_given_to_ptrToInt.zig @@ -0,0 +1,7 @@ +export fn entry(x: i32) usize { + return @ptrToInt(x); +} + +// non pointer given to @ptrToInt +// +// tmp.zig:2:22: error: expected pointer, found 'i32' diff --git a/test/compile_errors/stage1/obj/normal_string_with_newline.zig b/test/compile_errors/stage1/obj/normal_string_with_newline.zig new file mode 100644 index 0000000000..dafc041744 --- /dev/null +++ b/test/compile_errors/stage1/obj/normal_string_with_newline.zig @@ -0,0 +1,7 @@ +const foo = "a +b"; + +// normal string with newline +// +// tmp.zig:1:13: error: expected expression, found 'invalid bytes' +// tmp.zig:1:15: note: invalid byte: '\n' diff --git a/test/compile_errors/stage1/obj/offsetOf-bad_field_name.zig b/test/compile_errors/stage1/obj/offsetOf-bad_field_name.zig new file mode 100644 index 0000000000..3b301e2677 --- /dev/null +++ b/test/compile_errors/stage1/obj/offsetOf-bad_field_name.zig @@ -0,0 +1,10 @@ +const Foo = struct { + derp: i32, +}; +export fn foo() usize { + return @offsetOf(Foo, "a",); +} + +// @offsetOf - bad field name +// +// tmp.zig:5:27: error: struct 'Foo' has no field 'a' diff --git a/test/compile_errors/stage1/obj/offsetOf-non_struct.zig b/test/compile_errors/stage1/obj/offsetOf-non_struct.zig new file mode 100644 index 0000000000..173119407a --- /dev/null +++ b/test/compile_errors/stage1/obj/offsetOf-non_struct.zig @@ -0,0 +1,8 @@ +const Foo = i32; +export fn foo() usize { + return @offsetOf(Foo, "a",); +} + +// @offsetOf - non struct +// +// tmp.zig:3:22: error: expected struct type, found 'i32' diff --git a/test/compile_errors/stage1/obj/only_equality_binary_operator_allowed_for_error_sets.zig b/test/compile_errors/stage1/obj/only_equality_binary_operator_allowed_for_error_sets.zig new file mode 100644 index 0000000000..58512ef2a6 --- /dev/null +++ b/test/compile_errors/stage1/obj/only_equality_binary_operator_allowed_for_error_sets.zig @@ -0,0 +1,8 @@ +comptime { + const z = error.A > error.B; + _ = z; +} + +// only equality binary operator allowed for error sets +// +// tmp.zig:2:23: error: operator not allowed for errors diff --git a/test/compile_errors/stage1/obj/opaque_type_with_field.zig b/test/compile_errors/stage1/obj/opaque_type_with_field.zig new file mode 100644 index 0000000000..748d5c0736 --- /dev/null +++ b/test/compile_errors/stage1/obj/opaque_type_with_field.zig @@ -0,0 +1,9 @@ +const Opaque = opaque { foo: i32 }; +export fn entry() void { + const foo: ?*Opaque = null; + _ = foo; +} + +// opaque type with field +// +// tmp.zig:1:25: error: opaque types cannot have fields diff --git a/test/compile_errors/stage1/obj/optional_pointer_to_void_in_extern_struct.zig b/test/compile_errors/stage1/obj/optional_pointer_to_void_in_extern_struct.zig new file mode 100644 index 0000000000..f4ebb577d9 --- /dev/null +++ b/test/compile_errors/stage1/obj/optional_pointer_to_void_in_extern_struct.zig @@ -0,0 +1,12 @@ +const Foo = extern struct { + x: ?*const void, +}; +const Bar = extern struct { + foo: Foo, + y: i32, +}; +export fn entry(bar: *Bar) void {_ = bar;} + +// optional pointer to void in extern struct +// +// tmp.zig:2:5: error: extern structs cannot contain fields of type '?*const void' diff --git a/test/compile_errors/stage1/obj/or_on_undefined_value.zig b/test/compile_errors/stage1/obj/or_on_undefined_value.zig new file mode 100644 index 0000000000..ac636bff9e --- /dev/null +++ b/test/compile_errors/stage1/obj/or_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: bool = undefined; + _ = a or a; +} + +// or on undefined value +// +// tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/orelse_on_undefined_value.zig b/test/compile_errors/stage1/obj/orelse_on_undefined_value.zig new file mode 100644 index 0000000000..28e7c4b425 --- /dev/null +++ b/test/compile_errors/stage1/obj/orelse_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: ?bool = undefined; + _ = a orelse false; +} + +// orelse on undefined value +// +// tmp.zig:3:11: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/out_of_range_comptime_int_passed_to_floatToInt.zig b/test/compile_errors/stage1/obj/out_of_range_comptime_int_passed_to_floatToInt.zig new file mode 100644 index 0000000000..581bb58ffd --- /dev/null +++ b/test/compile_errors/stage1/obj/out_of_range_comptime_int_passed_to_floatToInt.zig @@ -0,0 +1,8 @@ +export fn entry() void { + const x = @floatToInt(i8, 200); + _ = x; +} + +// out of range comptime_int passed to @floatToInt +// +// tmp.zig:2:31: error: integer value 200 cannot be coerced to type 'i8' diff --git a/test/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig b/test/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig new file mode 100644 index 0000000000..5c737e06a2 --- /dev/null +++ b/test/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig @@ -0,0 +1,12 @@ +const Moo = enum(u8) { + Last = 255, + Over, +}; +pub fn main() void { + var y = Moo.Last; + _ = y; +} + +// overflow in enum value allocation +// +// tmp.zig:3:5: error: enumeration value 256 too large for type 'u8' diff --git a/test/compile_errors/stage1/obj/packed_union_given_enum_tag_type.zig b/test/compile_errors/stage1/obj/packed_union_given_enum_tag_type.zig new file mode 100644 index 0000000000..f0bc445295 --- /dev/null +++ b/test/compile_errors/stage1/obj/packed_union_given_enum_tag_type.zig @@ -0,0 +1,18 @@ +const Letter = enum { + A, + B, + C, +}; +const Payload = packed union(Letter) { + A: i32, + B: f64, + C: bool, +}; +export fn entry() void { + var a = Payload { .A = 1234 }; + _ = a; +} + +// packed union given enum tag type +// +// tmp.zig:6:30: error: packed union does not support enum tag type diff --git a/test/compile_errors/stage1/obj/packed_union_with_automatic_layout_field.zig b/test/compile_errors/stage1/obj/packed_union_with_automatic_layout_field.zig new file mode 100644 index 0000000000..4450531a5b --- /dev/null +++ b/test/compile_errors/stage1/obj/packed_union_with_automatic_layout_field.zig @@ -0,0 +1,16 @@ +const Foo = struct { + a: u32, + b: f32, +}; +const Payload = packed union { + A: Foo, + B: bool, +}; +export fn entry() void { + var a = Payload { .B = true }; + _ = a; +} + +// packed union with automatic layout field +// +// tmp.zig:6:5: error: non-packed, non-extern struct 'Foo' not allowed in packed union; no guaranteed in-memory representation diff --git a/test/compile_errors/stage1/obj/panic_called_at_compile_time.zig b/test/compile_errors/stage1/obj/panic_called_at_compile_time.zig new file mode 100644 index 0000000000..6dd32cdf59 --- /dev/null +++ b/test/compile_errors/stage1/obj/panic_called_at_compile_time.zig @@ -0,0 +1,9 @@ +export fn entry() void { + comptime { + @panic("aoeu",); + } +} + +// @panic called at compile time +// +// tmp.zig:3:9: error: encountered @panic at compile-time diff --git a/test/compile_errors/stage1/obj/parameter_redeclaration.zig b/test/compile_errors/stage1/obj/parameter_redeclaration.zig new file mode 100644 index 0000000000..01fc507afc --- /dev/null +++ b/test/compile_errors/stage1/obj/parameter_redeclaration.zig @@ -0,0 +1,8 @@ +fn f(a : i32, a : i32) void { +} +export fn entry() void { f(1, 2); } + +// parameter redeclaration +// +// tmp.zig:1:15: error: redeclaration of function parameter 'a' +// tmp.zig:1:6: note: previous declaration here diff --git a/test/compile_errors/stage1/obj/parameter_shadowing_global.zig b/test/compile_errors/stage1/obj/parameter_shadowing_global.zig new file mode 100644 index 0000000000..a58a0b93ce --- /dev/null +++ b/test/compile_errors/stage1/obj/parameter_shadowing_global.zig @@ -0,0 +1,10 @@ +const Foo = struct {}; +fn f(Foo: i32) void {} +export fn entry() void { + f(1234); +} + +// parameter shadowing global +// +// tmp.zig:2:6: error: local shadows declaration of 'Foo' +// tmp.zig:1:1: note: declared here diff --git a/test/compile_errors/stage1/obj/pass_const_ptr_to_mutable_ptr_fn.zig b/test/compile_errors/stage1/obj/pass_const_ptr_to_mutable_ptr_fn.zig new file mode 100644 index 0000000000..ff0057bb48 --- /dev/null +++ b/test/compile_errors/stage1/obj/pass_const_ptr_to_mutable_ptr_fn.zig @@ -0,0 +1,15 @@ +fn foo() bool { + const a = @as([]const u8, "a",); + const b = &a; + return ptrEql(b, b); +} +fn ptrEql(a: *[]const u8, b: *[]const u8) bool { + _ = a; _ = b; + return true; +} + +export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + +// pass const ptr to mutable ptr fn +// +// tmp.zig:4:19: error: expected type '*[]const u8', found '*const []const u8' diff --git a/test/compile_errors/stage1/obj/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig b/test/compile_errors/stage1/obj/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig new file mode 100644 index 0000000000..40564998b7 --- /dev/null +++ b/test/compile_errors/stage1/obj/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig @@ -0,0 +1,10 @@ +const AtomicOrder = @import("std").builtin.AtomicOrder; +export fn entry() bool { + var x: i32 align(1) = 1234; + while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {} + return x == 5678; +} + +// passing a not-aligned-enough pointer to cmpxchg +// +// tmp.zig:4:32: error: expected type '*i32', found '*align(1) i32' diff --git a/test/compile_errors/stage1/obj/passing_an_under-aligned_function_pointer.zig b/test/compile_errors/stage1/obj/passing_an_under-aligned_function_pointer.zig new file mode 100644 index 0000000000..f6c0e52cc0 --- /dev/null +++ b/test/compile_errors/stage1/obj/passing_an_under-aligned_function_pointer.zig @@ -0,0 +1,11 @@ +export fn entry() void { + testImplicitlyDecreaseFnAlign(alignedSmall, 1234); +} +fn testImplicitlyDecreaseFnAlign(ptr: fn () align(8) i32, answer: i32) void { + if (ptr() != answer) unreachable; +} +fn alignedSmall() align(4) i32 { return 1234; } + +// passing an under-aligned function pointer +// +// tmp.zig:2:35: error: expected type 'fn() align(8) i32', found 'fn() align(4) i32' diff --git a/test/compile_errors/stage1/obj/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig b/test/compile_errors/stage1/obj/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig new file mode 100644 index 0000000000..78f072c0d0 --- /dev/null +++ b/test/compile_errors/stage1/obj/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig @@ -0,0 +1,9 @@ +export fn func() void { + var strValue: [*c]u8 = undefined; + strValue = strValue orelse ""; +} + +// peer cast then implicit cast const pointer to mutable C pointer +// +// tmp.zig:3:32: error: expected type '[*c]u8', found '*const [0:0]u8' +// tmp.zig:3:32: note: cast discards const qualifier diff --git a/test/compile_errors/stage1/obj/pointer_arithmetic_on_pointer-to-array.zig b/test/compile_errors/stage1/obj/pointer_arithmetic_on_pointer-to-array.zig new file mode 100644 index 0000000000..b293ae2b32 --- /dev/null +++ b/test/compile_errors/stage1/obj/pointer_arithmetic_on_pointer-to-array.zig @@ -0,0 +1,10 @@ +export fn foo() void { + var x: [10]u8 = undefined; + var y = &x; + var z = y + 1; + _ = z; +} + +// pointer arithmetic on pointer-to-array +// +// tmp.zig:4:17: error: integer value 1 cannot be coerced to type '*[10]u8' diff --git a/test/compile_errors/stage1/obj/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig b/test/compile_errors/stage1/obj/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig new file mode 100644 index 0000000000..079844ac7f --- /dev/null +++ b/test/compile_errors/stage1/obj/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig @@ -0,0 +1,22 @@ +comptime { + const c: [][]const u8 = &.{"hello", "world" }; + _ = c; +} +comptime { + const c: *[2][]const u8 = &.{"hello", "world" }; + _ = c; +} +const S = struct {a: u8 = 1, b: u32 = 2}; +comptime { + const c: *S = &.{}; + _ = c; +} + +// pointer attributes checked when coercing pointer to anon literal +// +// tmp.zig:2:31: error: cannot cast pointer to array literal to slice type '[][]const u8' +// tmp.zig:2:31: note: cast discards const qualifier +// tmp.zig:6:33: error: cannot cast pointer to array literal to '*[2][]const u8' +// tmp.zig:6:33: note: cast discards const qualifier +// tmp.zig:11:21: error: expected type '*S', found '*const struct:11:21' +// tmp.zig:11:21: note: cast discards const qualifier diff --git a/test/compile_errors/stage1/obj/pointer_to_noreturn.zig b/test/compile_errors/stage1/obj/pointer_to_noreturn.zig new file mode 100644 index 0000000000..3b945919fc --- /dev/null +++ b/test/compile_errors/stage1/obj/pointer_to_noreturn.zig @@ -0,0 +1,6 @@ +fn a() *noreturn {} +export fn entry() void { _ = a(); } + +// pointer to noreturn +// +// tmp.zig:1:9: error: pointer to noreturn not allowed diff --git a/test/compile_errors/stage1/obj/popCount-non-integer.zig b/test/compile_errors/stage1/obj/popCount-non-integer.zig new file mode 100644 index 0000000000..694b794927 --- /dev/null +++ b/test/compile_errors/stage1/obj/popCount-non-integer.zig @@ -0,0 +1,7 @@ +export fn entry(x: f32) u32 { + return @popCount(f32, x); +} + +// @popCount - non-integer +// +// tmp.zig:2:22: error: expected integer type, found 'f32' diff --git a/test/compile_errors/stage1/obj/prevent_bad_implicit_casting_of_anyframe_types.zig b/test/compile_errors/stage1/obj/prevent_bad_implicit_casting_of_anyframe_types.zig new file mode 100644 index 0000000000..14bdb9cb11 --- /dev/null +++ b/test/compile_errors/stage1/obj/prevent_bad_implicit_casting_of_anyframe_types.zig @@ -0,0 +1,22 @@ +export fn a() void { + var x: anyframe = undefined; + var y: anyframe->i32 = x; + _ = y; +} +export fn b() void { + var x: i32 = undefined; + var y: anyframe->i32 = x; + _ = y; +} +export fn c() void { + var x: @Frame(func) = undefined; + var y: anyframe->i32 = &x; + _ = y; +} +fn func() void {} + +// prevent bad implicit casting of anyframe types +// +// tmp.zig:3:28: error: expected type 'anyframe->i32', found 'anyframe' +// tmp.zig:8:28: error: expected type 'anyframe->i32', found 'i32' +// tmp.zig:13:29: error: expected type 'anyframe->i32', found '*@Frame(func)' diff --git a/test/compile_errors/stage1/obj/primitives_take_precedence_over_declarations.zig b/test/compile_errors/stage1/obj/primitives_take_precedence_over_declarations.zig new file mode 100644 index 0000000000..3517c07917 --- /dev/null +++ b/test/compile_errors/stage1/obj/primitives_take_precedence_over_declarations.zig @@ -0,0 +1,9 @@ +const @"u8" = u16; +export fn entry() void { + const a: u8 = 300; + _ = a; +} + +// primitives take precedence over declarations +// +// tmp.zig:3:19: error: integer value 300 cannot be coerced to type 'u8' diff --git a/test/compile_errors/stage1/obj/ptrCast_a_0_bit_type_to_a_non-_0_bit_type.zig b/test/compile_errors/stage1/obj/ptrCast_a_0_bit_type_to_a_non-_0_bit_type.zig new file mode 100644 index 0000000000..63d0656f21 --- /dev/null +++ b/test/compile_errors/stage1/obj/ptrCast_a_0_bit_type_to_a_non-_0_bit_type.zig @@ -0,0 +1,11 @@ +export fn entry() bool { + var x: u0 = 0; + const p = @ptrCast(?*u0, &x); + return p == null; +} + +// @ptrCast a 0 bit type to a non- 0 bit type +// +// tmp.zig:3:15: error: '*u0' and '?*u0' do not have the same in-memory representation +// tmp.zig:3:31: note: '*u0' has no in-memory bits +// tmp.zig:3:24: note: '?*u0' has in-memory bits diff --git a/test/compile_errors/stage1/obj/ptrCast_discards_const_qualifier.zig b/test/compile_errors/stage1/obj/ptrCast_discards_const_qualifier.zig new file mode 100644 index 0000000000..1517fd4bd8 --- /dev/null +++ b/test/compile_errors/stage1/obj/ptrCast_discards_const_qualifier.zig @@ -0,0 +1,9 @@ +export fn entry() void { + const x: i32 = 1234; + const y = @ptrCast(*i32, &x); + _ = y; +} + +// @ptrCast discards const qualifier +// +// tmp.zig:3:15: error: cast discards const qualifier diff --git a/test/compile_errors/stage1/obj/ptrToInt_0_to_non_optional_pointer.zig b/test/compile_errors/stage1/obj/ptrToInt_0_to_non_optional_pointer.zig new file mode 100644 index 0000000000..fde1e53c1a --- /dev/null +++ b/test/compile_errors/stage1/obj/ptrToInt_0_to_non_optional_pointer.zig @@ -0,0 +1,8 @@ +export fn entry() void { + var b = @intToPtr(*i32, 0); + _ = b; +} + +// @ptrToInt 0 to non optional pointer +// +// tmp.zig:2:13: error: pointer type '*i32' does not allow address zero diff --git a/test/compile_errors/stage1/obj/ptrToInt_on_void.zig b/test/compile_errors/stage1/obj/ptrToInt_on_void.zig new file mode 100644 index 0000000000..fd43053103 --- /dev/null +++ b/test/compile_errors/stage1/obj/ptrToInt_on_void.zig @@ -0,0 +1,7 @@ +export fn entry() bool { + return @ptrToInt(&{}) == @ptrToInt(&{}); +} + +// @ptrToInt on *void +// +// tmp.zig:2:23: error: pointer to size 0 type has no address diff --git a/test/compile_errors/stage1/obj/ptrcast_to_non-pointer.zig b/test/compile_errors/stage1/obj/ptrcast_to_non-pointer.zig new file mode 100644 index 0000000000..74894d5e8a --- /dev/null +++ b/test/compile_errors/stage1/obj/ptrcast_to_non-pointer.zig @@ -0,0 +1,7 @@ +export fn entry(a: *i32) usize { + return @ptrCast(usize, a); +} + +// ptrcast to non-pointer +// +// tmp.zig:2:21: error: expected pointer, found 'usize' diff --git a/test/compile_errors/stage1/obj/range_operator_in_switch_used_on_error_set.zig b/test/compile_errors/stage1/obj/range_operator_in_switch_used_on_error_set.zig new file mode 100644 index 0000000000..4f2949c50c --- /dev/null +++ b/test/compile_errors/stage1/obj/range_operator_in_switch_used_on_error_set.zig @@ -0,0 +1,17 @@ +export fn entry() void { + try foo(452) catch |err| switch (err) { + error.A ... error.B => {}, + else => {}, + }; +} +fn foo(x: i32) !void { + switch (x) { + 0 ... 10 => return error.Foo, + 11 ... 20 => return error.Bar, + else => {}, + } +} + +// range operator in switch used on error set +// +// tmp.zig:3:17: error: operator not allowed for errors diff --git a/test/compile_errors/stage1/obj/reading_past_end_of_pointer_casted_array.zig b/test/compile_errors/stage1/obj/reading_past_end_of_pointer_casted_array.zig new file mode 100644 index 0000000000..558797d69b --- /dev/null +++ b/test/compile_errors/stage1/obj/reading_past_end_of_pointer_casted_array.zig @@ -0,0 +1,11 @@ +comptime { + const array: [4]u8 = "aoeu".*; + const sub_array = array[1..]; + const int_ptr = @ptrCast(*const u24, sub_array); + const deref = int_ptr.*; + _ = deref; +} + +// reading past end of pointer casted array +// +// tmp.zig:5:26: error: attempt to read 4 bytes from [4]u8 at index 1 which is 3 bytes diff --git a/test/compile_errors/stage1/obj/recursive_inferred_error_set.zig b/test/compile_errors/stage1/obj/recursive_inferred_error_set.zig new file mode 100644 index 0000000000..8059833923 --- /dev/null +++ b/test/compile_errors/stage1/obj/recursive_inferred_error_set.zig @@ -0,0 +1,10 @@ +export fn entry() void { + foo() catch unreachable; +} +fn foo() !void { + try foo(); +} + +// recursive inferred error set +// +// tmp.zig:5:5: error: cannot resolve inferred error set '@typeInfo(@typeInfo(@TypeOf(foo)).Fn.return_type.?).ErrorUnion.error_set': function 'foo' not fully analyzed yet diff --git a/test/compile_errors/stage1/obj/redefinition_of_enums.zig b/test/compile_errors/stage1/obj/redefinition_of_enums.zig new file mode 100644 index 0000000000..b4033536c3 --- /dev/null +++ b/test/compile_errors/stage1/obj/redefinition_of_enums.zig @@ -0,0 +1,7 @@ +const A = enum {x}; +const A = enum {x}; + +// redefinition of enums +// +// tmp.zig:2:1: error: redeclaration of 'A' +// tmp.zig:1:1: note: other declaration here diff --git a/test/compile_errors/stage1/obj/redefinition_of_global_variables.zig b/test/compile_errors/stage1/obj/redefinition_of_global_variables.zig new file mode 100644 index 0000000000..b2267423b0 --- /dev/null +++ b/test/compile_errors/stage1/obj/redefinition_of_global_variables.zig @@ -0,0 +1,7 @@ +var a : i32 = 1; +var a : i32 = 2; + +// redefinition of global variables +// +// tmp.zig:2:1: error: redeclaration of 'a' +// tmp.zig:1:1: note: other declaration here diff --git a/test/compile_errors/stage1/obj/redefinition_of_struct.zig b/test/compile_errors/stage1/obj/redefinition_of_struct.zig new file mode 100644 index 0000000000..8c169356c9 --- /dev/null +++ b/test/compile_errors/stage1/obj/redefinition_of_struct.zig @@ -0,0 +1,7 @@ +const A = struct { x : i32, }; +const A = struct { y : i32, }; + +// redefinition of struct +// +// tmp.zig:2:1: error: redeclaration of 'A' +// tmp.zig:1:1: note: other declaration here diff --git a/test/compile_errors/stage1/obj/refer_to_the_type_of_a_generic_function.zig b/test/compile_errors/stage1/obj/refer_to_the_type_of_a_generic_function.zig new file mode 100644 index 0000000000..6e23854cd6 --- /dev/null +++ b/test/compile_errors/stage1/obj/refer_to_the_type_of_a_generic_function.zig @@ -0,0 +1,9 @@ +export fn entry() void { + const Func = fn (type) void; + const f: Func = undefined; + f(i32); +} + +// refer to the type of a generic function +// +// tmp.zig:4:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/referring_to_a_struct_that_is_invalid.zig b/test/compile_errors/stage1/obj/referring_to_a_struct_that_is_invalid.zig new file mode 100644 index 0000000000..5f646461d8 --- /dev/null +++ b/test/compile_errors/stage1/obj/referring_to_a_struct_that_is_invalid.zig @@ -0,0 +1,15 @@ +const UsbDeviceRequest = struct { + Type: u8, +}; + +export fn foo() void { + comptime assert(@sizeOf(UsbDeviceRequest) == 0x8); +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// referring to a struct that is invalid +// +// tmp.zig:10:14: error: reached unreachable code diff --git a/test/compile_errors/stage1/obj/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig b/test/compile_errors/stage1/obj/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig new file mode 100644 index 0000000000..a714b10ef6 --- /dev/null +++ b/test/compile_errors/stage1/obj/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig @@ -0,0 +1,19 @@ +const Foo = struct { + ptr: ?*usize, + uval: u32, +}; +fn get_uval(x: u32) !u32 { + _ = x; + return error.NotFound; +} +export fn entry() void { + const afoo = Foo{ + .ptr = null, + .uval = get_uval(42), + }; + _ = afoo; +} + +// regression test #2980: base type u32 is not type checked properly when assigning a value within a struct +// +// tmp.zig:12:25: error: cannot convert error union to payload type. consider using `try`, `catch`, or `if`. expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(get_uval)).Fn.return_type.?).ErrorUnion.error_set!u32' diff --git a/test/compile_errors/stage1/obj/reify_type.Fn_with_is_generic_true.zig b/test/compile_errors/stage1/obj/reify_type.Fn_with_is_generic_true.zig new file mode 100644 index 0000000000..ed33158649 --- /dev/null +++ b/test/compile_errors/stage1/obj/reify_type.Fn_with_is_generic_true.zig @@ -0,0 +1,15 @@ +const Foo = @Type(.{ + .Fn = .{ + .calling_convention = .Unspecified, + .alignment = 0, + .is_generic = true, + .is_var_args = false, + .return_type = u0, + .args = &.{}, + }, +}); +comptime { _ = Foo; } + +// @Type(.Fn) with is_generic = true +// +// tmp.zig:1:20: error: Type.Fn.is_generic must be false for @Type diff --git a/test/compile_errors/stage1/obj/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig b/test/compile_errors/stage1/obj/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig new file mode 100644 index 0000000000..a57484075f --- /dev/null +++ b/test/compile_errors/stage1/obj/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig @@ -0,0 +1,15 @@ +const Foo = @Type(.{ + .Fn = .{ + .calling_convention = .Unspecified, + .alignment = 0, + .is_generic = false, + .is_var_args = true, + .return_type = u0, + .args = &.{}, + }, +}); +comptime { _ = Foo; } + +// @Type(.Fn) with is_var_args = true and non-C callconv +// +// tmp.zig:1:20: error: varargs functions must have C calling convention diff --git a/test/compile_errors/stage1/obj/reify_type.Fn_with_return_type_null.zig b/test/compile_errors/stage1/obj/reify_type.Fn_with_return_type_null.zig new file mode 100644 index 0000000000..f8ddea52cf --- /dev/null +++ b/test/compile_errors/stage1/obj/reify_type.Fn_with_return_type_null.zig @@ -0,0 +1,15 @@ +const Foo = @Type(.{ + .Fn = .{ + .calling_convention = .Unspecified, + .alignment = 0, + .is_generic = false, + .is_var_args = false, + .return_type = null, + .args = &.{}, + }, +}); +comptime { _ = Foo; } + +// @Type(.Fn) with return_type = null +// +// tmp.zig:1:20: error: Type.Fn.return_type must be non-null for @Type diff --git a/test/compile_errors/stage1/obj/reify_type.Pointer_with_invalid_address_space.zig b/test/compile_errors/stage1/obj/reify_type.Pointer_with_invalid_address_space.zig new file mode 100644 index 0000000000..78160fae58 --- /dev/null +++ b/test/compile_errors/stage1/obj/reify_type.Pointer_with_invalid_address_space.zig @@ -0,0 +1,16 @@ +export fn entry() void { + _ = @Type(.{ .Pointer = .{ + .size = .One, + .is_const = false, + .is_volatile = false, + .alignment = 1, + .address_space = .gs, + .child = u8, + .is_allowzero = false, + .sentinel = null, + }}); +} + +// @Type(.Pointer) with invalid address space +// +// tmp.zig:2:16: error: address space 'gs' not available in stage 1 compiler, must be .generic diff --git a/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig b/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig new file mode 100644 index 0000000000..e77c2eb627 --- /dev/null +++ b/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig @@ -0,0 +1,16 @@ +const Tag = @Type(.{ + .Enum = .{ + .layout = .Auto, + .tag_type = bool, + .fields = &.{}, + .decls = &.{}, + .is_exhaustive = false, + }, +}); +export fn entry() void { + _ = @intToEnum(Tag, 0); +} + +// @Type for exhaustive enum with non-integer tag type +// +// tmp.zig:1:20: error: Type.Enum.tag_type must be an integer type, not 'bool' diff --git a/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig b/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig new file mode 100644 index 0000000000..6a00516387 --- /dev/null +++ b/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig @@ -0,0 +1,16 @@ +const Tag = @Type(.{ + .Enum = .{ + .layout = .Auto, + .tag_type = undefined, + .fields = &.{}, + .decls = &.{}, + .is_exhaustive = false, + }, +}); +export fn entry() void { + _ = @intToEnum(Tag, 0); +} + +// @Type for exhaustive enum with undefined tag type +// +// tmp.zig:1:20: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_zero_fields.zig b/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_zero_fields.zig new file mode 100644 index 0000000000..87689247aa --- /dev/null +++ b/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_zero_fields.zig @@ -0,0 +1,16 @@ +const Tag = @Type(.{ + .Enum = .{ + .layout = .Auto, + .tag_type = u1, + .fields = &.{}, + .decls = &.{}, + .is_exhaustive = true, + }, +}); +export fn entry() void { + _ = @intToEnum(Tag, 0); +} + +// @Type for exhaustive enum with zero fields +// +// tmp.zig:1:20: error: enums must have 1 or more fields diff --git a/test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_enum_field.zig b/test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_enum_field.zig new file mode 100644 index 0000000000..f9155d7161 --- /dev/null +++ b/test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_enum_field.zig @@ -0,0 +1,32 @@ +const Tag = @Type(.{ + .Enum = .{ + .layout = .Auto, + .tag_type = u2, + .fields = &.{ + .{ .name = "signed", .value = 0 }, + .{ .name = "unsigned", .value = 1 }, + .{ .name = "arst", .value = 2 }, + }, + .decls = &.{}, + .is_exhaustive = true, + }, +}); +const Tagged = @Type(.{ + .Union = .{ + .layout = .Auto, + .tag_type = Tag, + .fields = &.{ + .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) }, + .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) }, + }, + .decls = &.{}, + }, +}); +export fn entry() void { + var tagged = Tagged{ .signed = -1 }; + tagged = .{ .unsigned = 1 }; +} + +// @Type for tagged union with extra enum field +// +// tmp.zig:14:23: error: enum field missing: 'arst' diff --git a/test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_union_field.zig b/test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_union_field.zig new file mode 100644 index 0000000000..9cd82213df --- /dev/null +++ b/test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_union_field.zig @@ -0,0 +1,33 @@ +const Tag = @Type(.{ + .Enum = .{ + .layout = .Auto, + .tag_type = u1, + .fields = &.{ + .{ .name = "signed", .value = 0 }, + .{ .name = "unsigned", .value = 1 }, + }, + .decls = &.{}, + .is_exhaustive = true, + }, +}); +const Tagged = @Type(.{ + .Union = .{ + .layout = .Auto, + .tag_type = Tag, + .fields = &.{ + .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) }, + .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) }, + .{ .name = "arst", .field_type = f32, .alignment = @alignOf(f32) }, + }, + .decls = &.{}, + }, +}); +export fn entry() void { + var tagged = Tagged{ .signed = -1 }; + tagged = .{ .unsigned = 1 }; +} + +// @Type for tagged union with extra union field +// +// tmp.zig:13:23: error: enum field not found: 'arst' +// tmp.zig:1:20: note: enum declared here diff --git a/test/compile_errors/stage1/obj/reify_type_for_union_with_opaque_field.zig b/test/compile_errors/stage1/obj/reify_type_for_union_with_opaque_field.zig new file mode 100644 index 0000000000..374ccb544b --- /dev/null +++ b/test/compile_errors/stage1/obj/reify_type_for_union_with_opaque_field.zig @@ -0,0 +1,17 @@ +const Untagged = @Type(.{ + .Union = .{ + .layout = .Auto, + .tag_type = null, + .fields = &.{ + .{ .name = "foo", .field_type = opaque {}, .alignment = 1 }, + }, + .decls = &.{}, + }, +}); +export fn entry() void { + _ = Untagged{}; +} + +// @Type for union with opaque field +// +// tmp.zig:1:25: error: opaque types have unknown size and therefore cannot be directly embedded in unions diff --git a/test/compile_errors/stage1/obj/reify_type_for_union_with_zero_fields.zig b/test/compile_errors/stage1/obj/reify_type_for_union_with_zero_fields.zig new file mode 100644 index 0000000000..8243ff3292 --- /dev/null +++ b/test/compile_errors/stage1/obj/reify_type_for_union_with_zero_fields.zig @@ -0,0 +1,15 @@ +const Untagged = @Type(.{ + .Union = .{ + .layout = .Auto, + .tag_type = null, + .fields = &.{}, + .decls = &.{}, + }, +}); +export fn entry() void { + _ = Untagged{}; +} + +// @Type for union with zero fields +// +// tmp.zig:1:25: error: unions must have 1 or more fields diff --git a/test/compile_errors/stage1/obj/reify_type_union_payload_is_undefined.zig b/test/compile_errors/stage1/obj/reify_type_union_payload_is_undefined.zig new file mode 100644 index 0000000000..43261dc32f --- /dev/null +++ b/test/compile_errors/stage1/obj/reify_type_union_payload_is_undefined.zig @@ -0,0 +1,8 @@ +const Foo = @Type(.{ + .Struct = undefined, +}); +comptime { _ = Foo; } + +// @Type() union payload is undefined +// +// tmp.zig:1:20: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/reify_type_with_Type.Int.zig b/test/compile_errors/stage1/obj/reify_type_with_Type.Int.zig new file mode 100644 index 0000000000..cea09d3f19 --- /dev/null +++ b/test/compile_errors/stage1/obj/reify_type_with_Type.Int.zig @@ -0,0 +1,11 @@ +const builtin = @import("std").builtin; +export fn entry() void { + _ = @Type(builtin.Type.Int{ + .signedness = .signed, + .bits = 8, + }); +} + +// @Type with Type.Int +// +// tmp.zig:3:31: error: expected type 'std.builtin.Type', found 'std.builtin.Type.Int' diff --git a/test/compile_errors/stage1/obj/reify_type_with_non-constant_expression.zig b/test/compile_errors/stage1/obj/reify_type_with_non-constant_expression.zig new file mode 100644 index 0000000000..a58b2a6b33 --- /dev/null +++ b/test/compile_errors/stage1/obj/reify_type_with_non-constant_expression.zig @@ -0,0 +1,9 @@ +const builtin = @import("std").builtin; +var globalTypeInfo : builtin.Type = undefined; +export fn entry() void { + _ = @Type(globalTypeInfo); +} + +// @Type with non-constant expression +// +// tmp.zig:4:15: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/reify_type_with_undefined.zig b/test/compile_errors/stage1/obj/reify_type_with_undefined.zig new file mode 100644 index 0000000000..984c9ac555 --- /dev/null +++ b/test/compile_errors/stage1/obj/reify_type_with_undefined.zig @@ -0,0 +1,18 @@ +comptime { + _ = @Type(.{ .Array = .{ .len = 0, .child = u8, .sentinel = undefined } }); +} +comptime { + _ = @Type(.{ + .Struct = .{ + .fields = undefined, + .decls = undefined, + .is_tuple = false, + .layout = .Auto, + }, + }); +} + +// @Type with undefined +// +// tmp.zig:2:16: error: use of undefined value here causes undefined behavior +// tmp.zig:5:16: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr.zig b/test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr.zig new file mode 100644 index 0000000000..a45fd9f41a --- /dev/null +++ b/test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr.zig @@ -0,0 +1,16 @@ +export fn entry() void { + var damn = Container{ + .not_optional = getOptional(), + }; + _ = damn; +} +pub fn getOptional() ?i32 { + return 0; +} +pub const Container = struct { + not_optional: i32, +}; + +// result location incompatibility mismatching handle_is_ptr +// +// tmp.zig:3:36: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type 'i32', found '?i32' diff --git a/test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig b/test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig new file mode 100644 index 0000000000..8b913bd15c --- /dev/null +++ b/test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig @@ -0,0 +1,16 @@ +export fn entry() void { + var damn = Container{ + .not_optional = getOptional(i32), + }; + _ = damn; +} +pub fn getOptional(comptime T: type) ?T { + return 0; +} +pub const Container = struct { + not_optional: i32, +}; + +// result location incompatibility mismatching handle_is_ptr (generic call) +// +// tmp.zig:3:36: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type 'i32', found '?i32' diff --git a/test/compile_errors/stage1/obj/return_from_defer_expression.zig b/test/compile_errors/stage1/obj/return_from_defer_expression.zig new file mode 100644 index 0000000000..0f4ac8e071 --- /dev/null +++ b/test/compile_errors/stage1/obj/return_from_defer_expression.zig @@ -0,0 +1,19 @@ +pub fn testTrickyDefer() !void { + defer canFail() catch {}; + + defer try canFail(); + + const a = maybeInt() orelse return; +} + +fn canFail() anyerror!void { } + +pub fn maybeInt() ?i32 { + return 0; +} + +export fn entry() usize { return @sizeOf(@TypeOf(testTrickyDefer)); } + +// return from defer expression +// +// tmp.zig:4:11: error: 'try' not allowed inside defer expression diff --git a/test/compile_errors/stage1/obj/returning_error_from_void_async_function.zig b/test/compile_errors/stage1/obj/returning_error_from_void_async_function.zig new file mode 100644 index 0000000000..a00422dd12 --- /dev/null +++ b/test/compile_errors/stage1/obj/returning_error_from_void_async_function.zig @@ -0,0 +1,10 @@ +export fn entry() void { + _ = async amain(); +} +fn amain() callconv(.Async) void { + return error.ShouldBeCompileError; +} + +// returning error from void async function +// +// tmp.zig:5:17: error: expected type 'void', found 'error{ShouldBeCompileError}' diff --git a/test/compile_errors/stage1/obj/runtime-known_async_function_called.zig b/test/compile_errors/stage1/obj/runtime-known_async_function_called.zig new file mode 100644 index 0000000000..52afd83a11 --- /dev/null +++ b/test/compile_errors/stage1/obj/runtime-known_async_function_called.zig @@ -0,0 +1,12 @@ +export fn entry() void { + _ = async amain(); +} +fn amain() void { + var ptr = afunc; + _ = ptr(); +} +fn afunc() callconv(.Async) void {} + +// runtime-known async function called +// +// tmp.zig:6:12: error: function is not comptime-known; @asyncCall required diff --git a/test/compile_errors/stage1/obj/runtime-known_function_called_with_async_keyword.zig b/test/compile_errors/stage1/obj/runtime-known_function_called_with_async_keyword.zig new file mode 100644 index 0000000000..79f0420a83 --- /dev/null +++ b/test/compile_errors/stage1/obj/runtime-known_function_called_with_async_keyword.zig @@ -0,0 +1,10 @@ +export fn entry() void { + var ptr = afunc; + _ = async ptr(); +} + +fn afunc() callconv(.Async) void { } + +// runtime-known function called with async keyword +// +// tmp.zig:3:15: error: function is not comptime-known; @asyncCall required diff --git a/test/compile_errors/stage1/obj/runtime_assignment_to_comptime_struct_type.zig b/test/compile_errors/stage1/obj/runtime_assignment_to_comptime_struct_type.zig new file mode 100644 index 0000000000..f8a3fd3ba1 --- /dev/null +++ b/test/compile_errors/stage1/obj/runtime_assignment_to_comptime_struct_type.zig @@ -0,0 +1,13 @@ +const Foo = struct { + Bar: u8, + Baz: type, +}; +export fn f() void { + var x: u8 = 0; + const foo = Foo { .Bar = x, .Baz = u8 }; + _ = foo; +} + +// runtime assignment to comptime struct type +// +// tmp.zig:7:23: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/runtime_assignment_to_comptime_union_type.zig b/test/compile_errors/stage1/obj/runtime_assignment_to_comptime_union_type.zig new file mode 100644 index 0000000000..bc4f093b0d --- /dev/null +++ b/test/compile_errors/stage1/obj/runtime_assignment_to_comptime_union_type.zig @@ -0,0 +1,13 @@ +const Foo = union { + Bar: u8, + Baz: type, +}; +export fn f() void { + var x: u8 = 0; + const foo = Foo { .Bar = x }; + _ = foo; +} + +// runtime assignment to comptime union type +// +// tmp.zig:7:23: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/runtime_cast_to_union_which_has_non-void_fields.zig b/test/compile_errors/stage1/obj/runtime_cast_to_union_which_has_non-void_fields.zig new file mode 100644 index 0000000000..8be361f2fe --- /dev/null +++ b/test/compile_errors/stage1/obj/runtime_cast_to_union_which_has_non-void_fields.zig @@ -0,0 +1,18 @@ +const Letter = enum { A, B, C }; +const Value = union(Letter) { + A: i32, + B, + C, +}; +export fn entry() void { + foo(Letter.A); +} +fn foo(l: Letter) void { + var x: Value = l; + _ = x; +} + +// runtime cast to union which has non-void fields +// +// tmp.zig:11:20: error: runtime cast to union 'Value' which has non-void fields +// tmp.zig:3:5: note: field 'A' has type 'i32' diff --git a/test/compile_errors/stage1/obj/runtime_index_into_comptime_type_slice.zig b/test/compile_errors/stage1/obj/runtime_index_into_comptime_type_slice.zig new file mode 100644 index 0000000000..4b9538c1a1 --- /dev/null +++ b/test/compile_errors/stage1/obj/runtime_index_into_comptime_type_slice.zig @@ -0,0 +1,15 @@ +const Struct = struct { + a: u32, +}; +fn getIndex() usize { + return 2; +} +export fn entry() void { + const index = getIndex(); + const field = @typeInfo(Struct).Struct.fields[index]; + _ = field; +} + +// runtime index into comptime type slice +// +// tmp.zig:9:51: error: values of type 'std.builtin.Type.StructField' must be comptime known, but index value is runtime known diff --git a/test/compile_errors/stage1/obj/saturating_arithmetic_does_not_allow_floats.zig b/test/compile_errors/stage1/obj/saturating_arithmetic_does_not_allow_floats.zig new file mode 100644 index 0000000000..57ee724e74 --- /dev/null +++ b/test/compile_errors/stage1/obj/saturating_arithmetic_does_not_allow_floats.zig @@ -0,0 +1,7 @@ +export fn a() void { + _ = @as(f32, 1.0) +| @as(f32, 1.0); +} + +// saturating arithmetic does not allow floats +// +// error: invalid operands to binary expression: 'f32' and 'f32' diff --git a/test/compile_errors/stage1/obj/saturating_shl_assign_does_not_allow_negative_rhs_at_comptime.zig b/test/compile_errors/stage1/obj/saturating_shl_assign_does_not_allow_negative_rhs_at_comptime.zig new file mode 100644 index 0000000000..9a46367ae5 --- /dev/null +++ b/test/compile_errors/stage1/obj/saturating_shl_assign_does_not_allow_negative_rhs_at_comptime.zig @@ -0,0 +1,10 @@ +export fn a() void { + comptime { + var x = @as(i32, 1); + x <<|= @as(i32, -2); + } +} + +// saturating shl assign does not allow negative rhs at comptime +// +// error: shift by negative value -2 diff --git a/test/compile_errors/stage1/obj/saturating_shl_does_not_allow_negative_rhs_at_comptime.zig b/test/compile_errors/stage1/obj/saturating_shl_does_not_allow_negative_rhs_at_comptime.zig new file mode 100644 index 0000000000..ffe715a8e3 --- /dev/null +++ b/test/compile_errors/stage1/obj/saturating_shl_does_not_allow_negative_rhs_at_comptime.zig @@ -0,0 +1,7 @@ +export fn a() void { + _ = @as(i32, 1) <<| @as(i32, -2); +} + +// saturating shl does not allow negative rhs at comptime +// +// error: shift by negative value -2 diff --git a/test/compile_errors/stage1/obj/setAlignStack_in_inline_function.zig b/test/compile_errors/stage1/obj/setAlignStack_in_inline_function.zig new file mode 100644 index 0000000000..9261ecdb18 --- /dev/null +++ b/test/compile_errors/stage1/obj/setAlignStack_in_inline_function.zig @@ -0,0 +1,10 @@ +export fn entry() void { + foo(); +} +fn foo() callconv(.Inline) void { + @setAlignStack(16); +} + +// @setAlignStack in inline function +// +// tmp.zig:5:5: error: @setAlignStack in inline function diff --git a/test/compile_errors/stage1/obj/setAlignStack_in_naked_function.zig b/test/compile_errors/stage1/obj/setAlignStack_in_naked_function.zig new file mode 100644 index 0000000000..d7146625c5 --- /dev/null +++ b/test/compile_errors/stage1/obj/setAlignStack_in_naked_function.zig @@ -0,0 +1,7 @@ +export fn entry() callconv(.Naked) void { + @setAlignStack(16); +} + +// @setAlignStack in naked function +// +// tmp.zig:2:5: error: @setAlignStack in naked function diff --git a/test/compile_errors/stage1/obj/setAlignStack_outside_function.zig b/test/compile_errors/stage1/obj/setAlignStack_outside_function.zig new file mode 100644 index 0000000000..c9ac12e6e0 --- /dev/null +++ b/test/compile_errors/stage1/obj/setAlignStack_outside_function.zig @@ -0,0 +1,7 @@ +comptime { + @setAlignStack(16); +} + +// @setAlignStack outside function +// +// tmp.zig:2:5: error: @setAlignStack outside function diff --git a/test/compile_errors/stage1/obj/setAlignStack_set_twice.zig b/test/compile_errors/stage1/obj/setAlignStack_set_twice.zig new file mode 100644 index 0000000000..de8cde7c01 --- /dev/null +++ b/test/compile_errors/stage1/obj/setAlignStack_set_twice.zig @@ -0,0 +1,9 @@ +export fn entry() void { + @setAlignStack(16); + @setAlignStack(16); +} + +// @setAlignStack set twice +// +// tmp.zig:3:5: error: alignstack set twice +// tmp.zig:2:5: note: first set here diff --git a/test/compile_errors/stage1/obj/setAlignStack_too_big.zig b/test/compile_errors/stage1/obj/setAlignStack_too_big.zig new file mode 100644 index 0000000000..669ec45204 --- /dev/null +++ b/test/compile_errors/stage1/obj/setAlignStack_too_big.zig @@ -0,0 +1,7 @@ +export fn entry() void { + @setAlignStack(511 + 1); +} + +// @setAlignStack too big +// +// tmp.zig:2:5: error: attempt to @setAlignStack(512); maximum is 256 diff --git a/test/compile_errors/stage1/obj/setFloatMode_twice_for_same_scope.zig b/test/compile_errors/stage1/obj/setFloatMode_twice_for_same_scope.zig new file mode 100644 index 0000000000..3e55604030 --- /dev/null +++ b/test/compile_errors/stage1/obj/setFloatMode_twice_for_same_scope.zig @@ -0,0 +1,9 @@ +export fn foo() void { + @setFloatMode(@import("std").builtin.FloatMode.Optimized); + @setFloatMode(@import("std").builtin.FloatMode.Optimized); +} + +// @setFloatMode twice for same scope +// +// tmp.zig:3:5: error: float mode set twice for same scope +// tmp.zig:2:5: note: first set here diff --git a/test/compile_errors/stage1/obj/setRuntimeSafety_twice_for_same_scope.zig b/test/compile_errors/stage1/obj/setRuntimeSafety_twice_for_same_scope.zig new file mode 100644 index 0000000000..741a214cde --- /dev/null +++ b/test/compile_errors/stage1/obj/setRuntimeSafety_twice_for_same_scope.zig @@ -0,0 +1,9 @@ +export fn foo() void { + @setRuntimeSafety(false); + @setRuntimeSafety(false); +} + +// @setRuntimeSafety twice for same scope +// +// tmp.zig:3:5: error: runtime safety set twice for same scope +// tmp.zig:2:5: note: first set here diff --git a/test/compile_errors/stage1/obj/setting_a_section_on_a_local_variable.zig b/test/compile_errors/stage1/obj/setting_a_section_on_a_local_variable.zig new file mode 100644 index 0000000000..eb3df0e214 --- /dev/null +++ b/test/compile_errors/stage1/obj/setting_a_section_on_a_local_variable.zig @@ -0,0 +1,8 @@ +export fn entry() i32 { + var foo: i32 linksection(".text2") = 1234; + return foo; +} + +// setting a section on a local variable +// +// tmp.zig:2:30: error: cannot set section of local variable 'foo' diff --git a/test/compile_errors/stage1/obj/shift_amount_has_to_be_an_integer_type.zig b/test/compile_errors/stage1/obj/shift_amount_has_to_be_an_integer_type.zig new file mode 100644 index 0000000000..6e54405619 --- /dev/null +++ b/test/compile_errors/stage1/obj/shift_amount_has_to_be_an_integer_type.zig @@ -0,0 +1,8 @@ +export fn entry() void { + const x = 1 << &@as(u8, 10); + _ = x; +} + +// shift amount has to be an integer type +// +// tmp.zig:2:21: error: shift amount has to be an integer type, but found '*const u8' diff --git a/test/compile_errors/stage1/obj/shift_by_negative_comptime_integer.zig b/test/compile_errors/stage1/obj/shift_by_negative_comptime_integer.zig new file mode 100644 index 0000000000..41f0ff80aa --- /dev/null +++ b/test/compile_errors/stage1/obj/shift_by_negative_comptime_integer.zig @@ -0,0 +1,8 @@ +comptime { + var a = 1 >> -1; + _ = a; +} + +// shift by negative comptime integer +// +// tmp.zig:2:18: error: shift by negative value -1 diff --git a/test/compile_errors/stage1/obj/shift_left_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/shift_left_assign_on_undefined_value.zig new file mode 100644 index 0000000000..a23d0e190c --- /dev/null +++ b/test/compile_errors/stage1/obj/shift_left_assign_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + a >>= 2; +} + +// shift left assign on undefined value +// +// tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/shift_left_on_undefined_value.zig b/test/compile_errors/stage1/obj/shift_left_on_undefined_value.zig new file mode 100644 index 0000000000..aa0891133b --- /dev/null +++ b/test/compile_errors/stage1/obj/shift_left_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + _ = a << 2; +} + +// shift left on undefined value +// +// tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/shift_right_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/shift_right_assign_on_undefined_value.zig new file mode 100644 index 0000000000..65f4fe50b8 --- /dev/null +++ b/test/compile_errors/stage1/obj/shift_right_assign_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + a >>= 2; +} + +// shift right assign on undefined value +// +// tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/shift_right_on_undefined_value.zig b/test/compile_errors/stage1/obj/shift_right_on_undefined_value.zig new file mode 100644 index 0000000000..ccd2563c84 --- /dev/null +++ b/test/compile_errors/stage1/obj/shift_right_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + _ = a >> 2; +} + +// shift right on undefined value +// +// tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/shifting_RHS_is_log2_of_LHS_int_bit_width.zig b/test/compile_errors/stage1/obj/shifting_RHS_is_log2_of_LHS_int_bit_width.zig new file mode 100644 index 0000000000..8a5d6cfee4 --- /dev/null +++ b/test/compile_errors/stage1/obj/shifting_RHS_is_log2_of_LHS_int_bit_width.zig @@ -0,0 +1,7 @@ +export fn entry(x: u8, y: u8) u8 { + return x << y; +} + +// shifting RHS is log2 of LHS int bit width +// +// tmp.zig:2:17: error: expected type 'u3', found 'u8' diff --git a/test/compile_errors/stage1/obj/shifting_without_int_type_or_comptime_known.zig b/test/compile_errors/stage1/obj/shifting_without_int_type_or_comptime_known.zig new file mode 100644 index 0000000000..380a08a95c --- /dev/null +++ b/test/compile_errors/stage1/obj/shifting_without_int_type_or_comptime_known.zig @@ -0,0 +1,7 @@ +export fn entry(x: u8) u8 { + return 0x11 << x; +} + +// shifting without int type or comptime known +// +// tmp.zig:2:17: error: LHS of shift must be a fixed-width integer type, or RHS must be compile-time known diff --git a/test/compile_errors/stage1/obj/shlExact_shifts_out_1_bits.zig b/test/compile_errors/stage1/obj/shlExact_shifts_out_1_bits.zig new file mode 100644 index 0000000000..bd6e48f25c --- /dev/null +++ b/test/compile_errors/stage1/obj/shlExact_shifts_out_1_bits.zig @@ -0,0 +1,8 @@ +comptime { + const x = @shlExact(@as(u8, 0b01010101), 2); + _ = x; +} + +// @shlExact shifts out 1 bits +// +// tmp.zig:2:15: error: operation caused overflow diff --git a/test/compile_errors/stage1/obj/shrExact_shifts_out_1_bits.zig b/test/compile_errors/stage1/obj/shrExact_shifts_out_1_bits.zig new file mode 100644 index 0000000000..004bf0c1c3 --- /dev/null +++ b/test/compile_errors/stage1/obj/shrExact_shifts_out_1_bits.zig @@ -0,0 +1,8 @@ +comptime { + const x = @shrExact(@as(u8, 0b10101010), 2); + _ = x; +} + +// @shrExact shifts out 1 bits +// +// tmp.zig:2:15: error: exact shift shifted out 1 bits diff --git a/test/compile_errors/stage1/obj/signed_integer_division.zig b/test/compile_errors/stage1/obj/signed_integer_division.zig new file mode 100644 index 0000000000..524f9fe1db --- /dev/null +++ b/test/compile_errors/stage1/obj/signed_integer_division.zig @@ -0,0 +1,7 @@ +export fn foo(a: i32, b: i32) i32 { + return a / b; +} + +// signed integer division +// +// tmp.zig:2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact diff --git a/test/compile_errors/stage1/obj/signed_integer_remainder_division.zig b/test/compile_errors/stage1/obj/signed_integer_remainder_division.zig new file mode 100644 index 0000000000..e060b780d0 --- /dev/null +++ b/test/compile_errors/stage1/obj/signed_integer_remainder_division.zig @@ -0,0 +1,7 @@ +export fn foo(a: i32, b: i32) i32 { + return a % b; +} + +// signed integer remainder division +// +// tmp.zig:2:14: error: remainder division with 'i32' and 'i32': signed integers and floats must use @rem or @mod diff --git a/test/compile_errors/stage1/obj/sizeOf_bad_type.zig b/test/compile_errors/stage1/obj/sizeOf_bad_type.zig new file mode 100644 index 0000000000..1b336cf657 --- /dev/null +++ b/test/compile_errors/stage1/obj/sizeOf_bad_type.zig @@ -0,0 +1,7 @@ +export fn entry() usize { + return @sizeOf(@TypeOf(null)); +} + +// @sizeOf bad type +// +// tmp.zig:2:20: error: no size available for type '@Type(.Null)' diff --git a/test/compile_errors/stage1/obj/slice_cannot_have_its_bytes_reinterpreted.zig b/test/compile_errors/stage1/obj/slice_cannot_have_its_bytes_reinterpreted.zig new file mode 100644 index 0000000000..4f7670ba74 --- /dev/null +++ b/test/compile_errors/stage1/obj/slice_cannot_have_its_bytes_reinterpreted.zig @@ -0,0 +1,9 @@ +export fn foo() void { + const bytes = [1]u8{ 0xfa } ** 16; + var value = @ptrCast(*const []const u8, &bytes).*; + _ = value; +} + +// slice cannot have its bytes reinterpreted +// +// :3:52: error: slice '[]const u8' cannot have its bytes reinterpreted diff --git a/test/compile_errors/stage1/obj/slice_passed_as_array_init_type.zig b/test/compile_errors/stage1/obj/slice_passed_as_array_init_type.zig new file mode 100644 index 0000000000..0d29120ca4 --- /dev/null +++ b/test/compile_errors/stage1/obj/slice_passed_as_array_init_type.zig @@ -0,0 +1,8 @@ +export fn entry() void { + const x = []u8{}; + _ = x; +} + +// slice passed as array init type +// +// tmp.zig:2:15: error: array literal requires address-of operator (&) to coerce to slice type '[]u8' diff --git a/test/compile_errors/stage1/obj/slice_passed_as_array_init_type_with_elems.zig b/test/compile_errors/stage1/obj/slice_passed_as_array_init_type_with_elems.zig new file mode 100644 index 0000000000..375ed55a72 --- /dev/null +++ b/test/compile_errors/stage1/obj/slice_passed_as_array_init_type_with_elems.zig @@ -0,0 +1,8 @@ +export fn entry() void { + const x = []u8{1, 2}; + _ = x; +} + +// slice passed as array init type with elems +// +// tmp.zig:2:15: error: array literal requires address-of operator (&) to coerce to slice type '[]u8' diff --git a/test/compile_errors/stage1/obj/slice_sentinel_mismatch-1.zig b/test/compile_errors/stage1/obj/slice_sentinel_mismatch-1.zig new file mode 100644 index 0000000000..1e3c2450cb --- /dev/null +++ b/test/compile_errors/stage1/obj/slice_sentinel_mismatch-1.zig @@ -0,0 +1,8 @@ +export fn entry() void { + const y: [:1]const u8 = &[_:2]u8{ 1, 2 }; + _ = y; +} + +// slice sentinel mismatch - 1 +// +// tmp.zig:2:37: error: expected type '[:1]const u8', found '*const [2:2]u8' diff --git a/test/compile_errors/stage1/obj/slice_sentinel_mismatch-2.zig b/test/compile_errors/stage1/obj/slice_sentinel_mismatch-2.zig new file mode 100644 index 0000000000..f1d73eb9df --- /dev/null +++ b/test/compile_errors/stage1/obj/slice_sentinel_mismatch-2.zig @@ -0,0 +1,10 @@ +fn foo() [:0]u8 { + var x: []u8 = undefined; + return x; +} +comptime { _ = foo; } + +// slice sentinel mismatch - 2 +// +// tmp.zig:3:12: error: expected type '[:0]u8', found '[]u8' +// tmp.zig:3:12: note: destination pointer requires a terminating '0' sentinel diff --git a/test/compile_errors/stage1/obj/slicing_of_global_undefined_pointer.zig b/test/compile_errors/stage1/obj/slicing_of_global_undefined_pointer.zig new file mode 100644 index 0000000000..d52bfdf6a5 --- /dev/null +++ b/test/compile_errors/stage1/obj/slicing_of_global_undefined_pointer.zig @@ -0,0 +1,8 @@ +var buf: *[1]u8 = undefined; +export fn entry() void { + _ = buf[0..1]; +} + +// slicing of global undefined pointer +// +// tmp.zig:3:12: error: non-zero length slice of undefined pointer diff --git a/test/compile_errors/stage1/obj/slicing_single-item_pointer.zig b/test/compile_errors/stage1/obj/slicing_single-item_pointer.zig new file mode 100644 index 0000000000..f6bbe752b1 --- /dev/null +++ b/test/compile_errors/stage1/obj/slicing_single-item_pointer.zig @@ -0,0 +1,8 @@ +export fn entry(ptr: *i32) void { + const slice = ptr[0..2]; + _ = slice; +} + +// slicing single-item pointer +// +// tmp.zig:2:22: error: slice of single-item pointer diff --git a/test/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig b/test/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig new file mode 100644 index 0000000000..ca7c305d38 --- /dev/null +++ b/test/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig @@ -0,0 +1,16 @@ +const Small = enum (u2) { + One, + Two, + Three, + Four, + Five, +}; + +export fn entry() void { + var x = Small.One; + _ = x; +} + +// specify enum tag type that is too small +// +// tmp.zig:6:5: error: enumeration value 4 too large for type 'u2' diff --git a/test/compile_errors/stage1/obj/specify_non-integer_enum_tag_type.zig b/test/compile_errors/stage1/obj/specify_non-integer_enum_tag_type.zig new file mode 100644 index 0000000000..095a0812c1 --- /dev/null +++ b/test/compile_errors/stage1/obj/specify_non-integer_enum_tag_type.zig @@ -0,0 +1,14 @@ +const Small = enum (f32) { + One, + Two, + Three, +}; + +export fn entry() void { + var x = Small.One; + _ = x; +} + +// specify non-integer enum tag type +// +// tmp.zig:1:21: error: expected integer, found 'f32' diff --git a/test/compile_errors/stage1/obj/src_outside_function.zig b/test/compile_errors/stage1/obj/src_outside_function.zig new file mode 100644 index 0000000000..5359135066 --- /dev/null +++ b/test/compile_errors/stage1/obj/src_outside_function.zig @@ -0,0 +1,7 @@ +comptime { + @src(); +} + +// @src outside function +// +// tmp.zig:2:5: error: @src outside function diff --git a/test/compile_errors/stage1/obj/store_vector_pointer_with_unknown_runtime_index.zig b/test/compile_errors/stage1/obj/store_vector_pointer_with_unknown_runtime_index.zig new file mode 100644 index 0000000000..6ef71c863a --- /dev/null +++ b/test/compile_errors/stage1/obj/store_vector_pointer_with_unknown_runtime_index.zig @@ -0,0 +1,14 @@ +export fn entry() void { + var v: @import("std").meta.Vector(4, i32) = [_]i32{ 1, 5, 3, undefined }; + + var i: u32 = 0; + storev(&v[i], 42); +} + +fn storev(ptr: anytype, val: i32) void { + ptr.* = val; +} + +// store vector pointer with unknown runtime index +// +// tmp.zig:9:8: error: unable to determine vector element index of type '*align(16:0:4:?) i32 diff --git a/test/compile_errors/stage1/obj/storing_runtime_value_in_compile_time_variable_then_using_it.zig b/test/compile_errors/stage1/obj/storing_runtime_value_in_compile_time_variable_then_using_it.zig new file mode 100644 index 0000000000..318db771d5 --- /dev/null +++ b/test/compile_errors/stage1/obj/storing_runtime_value_in_compile_time_variable_then_using_it.zig @@ -0,0 +1,47 @@ +const Mode = @import("std").builtin.Mode; + +fn Free(comptime filename: []const u8) TestCase { + return TestCase { + .filename = filename, + .problem_type = ProblemType.Free, + }; +} + +fn LibC(comptime filename: []const u8) TestCase { + return TestCase { + .filename = filename, + .problem_type = ProblemType.LinkLibC, + }; +} + +const TestCase = struct { + filename: []const u8, + problem_type: ProblemType, +}; + +const ProblemType = enum { + Free, + LinkLibC, +}; + +export fn entry() void { + const tests = [_]TestCase { + Free("001"), + Free("002"), + LibC("078"), + Free("116"), + Free("117"), + }; + + for ([_]Mode { Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast }) |mode| { + _ = mode; + inline for (tests) |test_case| { + const foo = test_case.filename ++ ".zig"; + _ = foo; + } + } +} + +// storing runtime value in compile time variable then using it +// +// tmp.zig:38:29: error: cannot store runtime value in compile time variable diff --git a/test/compile_errors/stage1/obj/struct_depends_on_itself_via_optional_field.zig b/test/compile_errors/stage1/obj/struct_depends_on_itself_via_optional_field.zig new file mode 100644 index 0000000000..13ee95155d --- /dev/null +++ b/test/compile_errors/stage1/obj/struct_depends_on_itself_via_optional_field.zig @@ -0,0 +1,17 @@ +const LhsExpr = struct { + rhsExpr: ?AstObject, +}; +const AstObject = union { + lhsExpr: LhsExpr, +}; +export fn entry() void { + const lhsExpr = LhsExpr{ .rhsExpr = null }; + const obj = AstObject{ .lhsExpr = lhsExpr }; + _ = obj; +} + +// struct depends on itself via optional field +// +// tmp.zig:1:17: error: struct 'LhsExpr' depends on itself +// tmp.zig:5:5: note: while checking this field +// tmp.zig:2:5: note: while checking this field diff --git a/test/compile_errors/stage1/obj/struct_field_missing_type.zig b/test/compile_errors/stage1/obj/struct_field_missing_type.zig new file mode 100644 index 0000000000..669718a3d4 --- /dev/null +++ b/test/compile_errors/stage1/obj/struct_field_missing_type.zig @@ -0,0 +1,11 @@ +const Letter = struct { + A, +}; +export fn entry() void { + var a = Letter { .A = {} }; + _ = a; +} + +// struct field missing type +// +// tmp.zig:2:5: error: struct field missing type diff --git a/test/compile_errors/stage1/obj/struct_init_syntax_for_array.zig b/test/compile_errors/stage1/obj/struct_init_syntax_for_array.zig new file mode 100644 index 0000000000..4eda9622ab --- /dev/null +++ b/test/compile_errors/stage1/obj/struct_init_syntax_for_array.zig @@ -0,0 +1,8 @@ +const foo = [3]u16{ .x = 1024 }; +comptime { + _ = foo; +} + +// struct init syntax for array +// +// tmp.zig:1:13: error: initializing array with struct syntax diff --git a/test/compile_errors/stage1/obj/struct_with_declarations_unavailable_for_reify_type.zig b/test/compile_errors/stage1/obj/struct_with_declarations_unavailable_for_reify_type.zig new file mode 100644 index 0000000000..656a9b9f62 --- /dev/null +++ b/test/compile_errors/stage1/obj/struct_with_declarations_unavailable_for_reify_type.zig @@ -0,0 +1,7 @@ +export fn entry() void { + _ = @Type(@typeInfo(struct { const foo = 1; })); +} + +// struct with declarations unavailable for @Type +// +// tmp.zig:2:15: error: Type.Struct.decls must be empty for @Type diff --git a/test/compile_errors/stage1/obj/struct_with_invalid_field.zig b/test/compile_errors/stage1/obj/struct_with_invalid_field.zig new file mode 100644 index 0000000000..a2a632fbd9 --- /dev/null +++ b/test/compile_errors/stage1/obj/struct_with_invalid_field.zig @@ -0,0 +1,28 @@ +const std = @import("std",); +const Allocator = std.mem.Allocator; +const ArrayList = std.ArrayList; + +const HeaderWeight = enum { + H1, H2, H3, H4, H5, H6, +}; + +const MdText = ArrayList(u8); + +const MdNode = union(enum) { + Header: struct { + text: MdText, + weight: HeaderValue, + }, +}; + +export fn entry() void { + const a = MdNode.Header { + .text = MdText.init(std.testing.allocator), + .weight = HeaderWeight.H1, + }; + _ = a; +} + +// struct with invalid field +// +// tmp.zig:14:17: error: use of undeclared identifier 'HeaderValue' diff --git a/test/compile_errors/stage1/obj/sub_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/sub_assign_on_undefined_value.zig new file mode 100644 index 0000000000..01ef9313e5 --- /dev/null +++ b/test/compile_errors/stage1/obj/sub_assign_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + a -= a; +} + +// sub assign on undefined value +// +// tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/sub_on_undefined_value.zig b/test/compile_errors/stage1/obj/sub_on_undefined_value.zig new file mode 100644 index 0000000000..80746abf0e --- /dev/null +++ b/test/compile_errors/stage1/obj/sub_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + _ = a - a; +} + +// sub on undefined value +// +// tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/sub_overflow_in_function_evaluation.zig b/test/compile_errors/stage1/obj/sub_overflow_in_function_evaluation.zig new file mode 100644 index 0000000000..1084f1a111 --- /dev/null +++ b/test/compile_errors/stage1/obj/sub_overflow_in_function_evaluation.zig @@ -0,0 +1,10 @@ +const y = sub(10, 20); +fn sub(a: u16, b: u16) u16 { + return a - b; +} + +export fn entry() usize { return @sizeOf(@TypeOf(y)); } + +// sub overflow in function evaluation +// +// tmp.zig:3:14: error: operation caused overflow diff --git a/test/compile_errors/stage1/obj/sub_wrap_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/sub_wrap_assign_on_undefined_value.zig new file mode 100644 index 0000000000..3c7fd4faa5 --- /dev/null +++ b/test/compile_errors/stage1/obj/sub_wrap_assign_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + a -%= a; +} + +// sub wrap assign on undefined value +// +// tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/sub_wrap_on_undefined_value.zig b/test/compile_errors/stage1/obj/sub_wrap_on_undefined_value.zig new file mode 100644 index 0000000000..367b4c9c0e --- /dev/null +++ b/test/compile_errors/stage1/obj/sub_wrap_on_undefined_value.zig @@ -0,0 +1,8 @@ +comptime { + var a: i64 = undefined; + _ = a -% a; +} + +// sub wrap on undefined value +// +// tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/suspend_inside_suspend_block.zig b/test/compile_errors/stage1/obj/suspend_inside_suspend_block.zig new file mode 100644 index 0000000000..2e807268ce --- /dev/null +++ b/test/compile_errors/stage1/obj/suspend_inside_suspend_block.zig @@ -0,0 +1,14 @@ +export fn entry() void { + _ = async foo(); +} +fn foo() void { + suspend { + suspend { + } + } +} + +// suspend inside suspend block +// +// tmp.zig:6:9: error: cannot suspend inside suspend block +// tmp.zig:5:5: note: other suspend block here diff --git a/test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong.zig b/test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong.zig new file mode 100644 index 0000000000..c5db39f5b2 --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong.zig @@ -0,0 +1,22 @@ +const Number = enum { + One, + Two, + Three, + Four, +}; +fn f(n: Number) i32 { + switch (n) { + Number.One => 1, + Number.Two => 2, + Number.Three => @as(i32, 3), + Number.Four => 4, + Number.Two => 2, + } +} + +export fn entry() usize { return @sizeOf(@TypeOf(f)); } + +// switch expression - duplicate enumeration prong +// +// tmp.zig:13:15: error: duplicate switch value +// tmp.zig:10:15: note: other value here diff --git a/test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong_when_else_present.zig b/test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong_when_else_present.zig new file mode 100644 index 0000000000..22a0c189c1 --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong_when_else_present.zig @@ -0,0 +1,23 @@ +const Number = enum { + One, + Two, + Three, + Four, +}; +fn f(n: Number) i32 { + switch (n) { + Number.One => 1, + Number.Two => 2, + Number.Three => @as(i32, 3), + Number.Four => 4, + Number.Two => 2, + else => 10, + } +} + +export fn entry() usize { return @sizeOf(@TypeOf(f)); } + +// switch expression - duplicate enumeration prong when else present +// +// tmp.zig:13:15: error: duplicate switch value +// tmp.zig:10:15: note: other value here diff --git a/test/compile_errors/stage1/obj/switch_expression-duplicate_or_overlapping_integer_value.zig b/test/compile_errors/stage1/obj/switch_expression-duplicate_or_overlapping_integer_value.zig new file mode 100644 index 0000000000..06ef7de62e --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_expression-duplicate_or_overlapping_integer_value.zig @@ -0,0 +1,14 @@ +fn foo(x: u8) u8 { + return switch (x) { + 0 ... 100 => @as(u8, 0), + 101 ... 200 => 1, + 201, 203 ... 207 => 2, + 206 ... 255 => 3, + }; +} +export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + +// switch expression - duplicate or overlapping integer value +// +// tmp.zig:6:9: error: duplicate switch value +// tmp.zig:5:14: note: previous value here diff --git a/test/compile_errors/stage1/obj/switch_expression-duplicate_type.zig b/test/compile_errors/stage1/obj/switch_expression-duplicate_type.zig new file mode 100644 index 0000000000..9ac9feaeaf --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_expression-duplicate_type.zig @@ -0,0 +1,15 @@ +fn foo(comptime T: type, x: T) u8 { + _ = x; + return switch (T) { + u32 => 0, + u64 => 1, + u32 => 2, + else => 3, + }; +} +export fn entry() usize { return @sizeOf(@TypeOf(foo(u32, 0))); } + +// switch expression - duplicate type +// +// tmp.zig:6:9: error: duplicate switch value +// tmp.zig:4:9: note: previous value here diff --git a/test/compile_errors/stage1/obj/switch_expression-duplicate_type_struct_alias.zig b/test/compile_errors/stage1/obj/switch_expression-duplicate_type_struct_alias.zig new file mode 100644 index 0000000000..b92336cac5 --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_expression-duplicate_type_struct_alias.zig @@ -0,0 +1,19 @@ +const Test = struct { + bar: i32, +}; +const Test2 = Test; +fn foo(comptime T: type, x: T) u8 { + _ = x; + return switch (T) { + Test => 0, + u64 => 1, + Test2 => 2, + else => 3, + }; +} +export fn entry() usize { return @sizeOf(@TypeOf(foo(u32, 0))); } + +// switch expression - duplicate type (struct alias) +// +// tmp.zig:10:9: error: duplicate switch value +// tmp.zig:8:9: note: previous value here diff --git a/test/compile_errors/stage1/obj/switch_expression-missing_enumeration_prong.zig b/test/compile_errors/stage1/obj/switch_expression-missing_enumeration_prong.zig new file mode 100644 index 0000000000..b6cd60c22b --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_expression-missing_enumeration_prong.zig @@ -0,0 +1,19 @@ +const Number = enum { + One, + Two, + Three, + Four, +}; +fn f(n: Number) i32 { + switch (n) { + Number.One => 1, + Number.Two => 2, + Number.Three => @as(i32, 3), + } +} + +export fn entry() usize { return @sizeOf(@TypeOf(f)); } + +// switch expression - missing enumeration prong +// +// tmp.zig:8:5: error: enumeration value 'Number.Four' not handled in switch diff --git a/test/compile_errors/stage1/obj/switch_expression-multiple_else_prongs.zig b/test/compile_errors/stage1/obj/switch_expression-multiple_else_prongs.zig new file mode 100644 index 0000000000..6a7e274b56 --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_expression-multiple_else_prongs.zig @@ -0,0 +1,14 @@ +fn f(x: u32) void { + const value: bool = switch (x) { + 1234 => false, + else => true, + else => true, + }; +} +export fn entry() void { + f(1234); +} + +// switch expression - multiple else prongs +// +// tmp.zig:5:9: error: multiple else prongs in switch expression diff --git a/test/compile_errors/stage1/obj/switch_expression-non_exhaustive_integer_prongs.zig b/test/compile_errors/stage1/obj/switch_expression-non_exhaustive_integer_prongs.zig new file mode 100644 index 0000000000..88fb8548de --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_expression-non_exhaustive_integer_prongs.zig @@ -0,0 +1,10 @@ +fn foo(x: u8) void { + switch (x) { + 0 => {}, + } +} +export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + +// switch expression - non exhaustive integer prongs +// +// tmp.zig:2:5: error: switch must handle all possibilities diff --git a/test/compile_errors/stage1/obj/switch_expression-switch_on_pointer_type_with_no_else.zig b/test/compile_errors/stage1/obj/switch_expression-switch_on_pointer_type_with_no_else.zig new file mode 100644 index 0000000000..ec7565b882 --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_expression-switch_on_pointer_type_with_no_else.zig @@ -0,0 +1,11 @@ +fn foo(x: *u8) void { + switch (x) { + &y => {}, + } +} +const y: u8 = 100; +export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + +// switch expression - switch on pointer type with no else +// +// tmp.zig:2:5: error: else prong required when switching on type '*u8' diff --git a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_bool.zig b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_bool.zig new file mode 100644 index 0000000000..8c36a3c289 --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_bool.zig @@ -0,0 +1,12 @@ +fn foo(x: bool) void { + switch (x) { + true => {}, + false => {}, + else => {}, + } +} +export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + +// switch expression - unreachable else prong (bool) +// +// tmp.zig:5:9: error: unreachable else prong, all cases already handled diff --git a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_enum.zig b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_enum.zig new file mode 100644 index 0000000000..8207d05234 --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_enum.zig @@ -0,0 +1,22 @@ +const TestEnum = enum{ T1, T2 }; + +fn err(x: u8) TestEnum { + switch (x) { + 0 => return TestEnum.T1, + else => return TestEnum.T2, + } +} + +fn foo(x: u8) void { + switch (err(x)) { + TestEnum.T1 => {}, + TestEnum.T2 => {}, + else => {}, + } +} + +export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + +// switch expression - unreachable else prong (enum) +// +// tmp.zig:14:9: error: unreachable else prong, all cases already handled diff --git a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_i8.zig b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_i8.zig new file mode 100644 index 0000000000..8750e57fae --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_i8.zig @@ -0,0 +1,15 @@ +fn foo(x: i8) void { + switch (x) { + -128...0 => {}, + 1 => {}, + 2 => {}, + 3 => {}, + 4...127 => {}, + else => {}, + } +} +export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + +// switch expression - unreachable else prong (range i8) +// +// tmp.zig:8:9: error: unreachable else prong, all cases already handled diff --git a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_u8.zig b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_u8.zig new file mode 100644 index 0000000000..280663ae51 --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_u8.zig @@ -0,0 +1,15 @@ +fn foo(x: u8) void { + switch (x) { + 0 => {}, + 1 => {}, + 2 => {}, + 3 => {}, + 4...255 => {}, + else => {}, + } +} +export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + +// switch expression - unreachable else prong (range u8) +// +// tmp.zig:8:9: error: unreachable else prong, all cases already handled diff --git a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u1.zig b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u1.zig new file mode 100644 index 0000000000..58c4f378bf --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u1.zig @@ -0,0 +1,12 @@ +fn foo(x: u1) void { + switch (x) { + 0 => {}, + 1 => {}, + else => {}, + } +} +export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + +// switch expression - unreachable else prong (u1) +// +// tmp.zig:5:9: error: unreachable else prong, all cases already handled diff --git a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u2.zig b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u2.zig new file mode 100644 index 0000000000..ec1804316e --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u2.zig @@ -0,0 +1,14 @@ +fn foo(x: u2) void { + switch (x) { + 0 => {}, + 1 => {}, + 2 => {}, + 3 => {}, + else => {}, + } +} +export fn entry() usize { return @sizeOf(@TypeOf(foo)); } + +// switch expression - unreachable else prong (u2) +// +// tmp.zig:7:9: error: unreachable else prong, all cases already handled diff --git a/test/compile_errors/stage1/obj/switch_on_enum_with_1_field_with_no_prongs.zig b/test/compile_errors/stage1/obj/switch_on_enum_with_1_field_with_no_prongs.zig new file mode 100644 index 0000000000..fbfc039f59 --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_on_enum_with_1_field_with_no_prongs.zig @@ -0,0 +1,10 @@ +const Foo = enum { M }; + +export fn entry() void { + var f = Foo.M; + switch (f) {} +} + +// switch on enum with 1 field with no prongs +// +// tmp.zig:5:5: error: enumeration value 'Foo.M' not handled in switch diff --git a/test/compile_errors/stage1/obj/switch_on_union_with_no_attached_enum.zig b/test/compile_errors/stage1/obj/switch_on_union_with_no_attached_enum.zig new file mode 100644 index 0000000000..f1ae750b4b --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_on_union_with_no_attached_enum.zig @@ -0,0 +1,20 @@ +const Payload = union { + A: i32, + B: f64, + C: bool, +}; +export fn entry() void { + const a = Payload { .A = 1234 }; + foo(a); +} +fn foo(a: *const Payload) void { + switch (a.*) { + Payload.A => {}, + else => unreachable, + } +} + +// switch on union with no attached enum +// +// tmp.zig:11:14: error: switch on union which has no attached enum +// tmp.zig:1:17: note: consider 'union(enum)' here diff --git a/test/compile_errors/stage1/obj/switch_with_invalid_expression_parameter.zig b/test/compile_errors/stage1/obj/switch_with_invalid_expression_parameter.zig new file mode 100644 index 0000000000..f65879bc7a --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_with_invalid_expression_parameter.zig @@ -0,0 +1,15 @@ +export fn entry() void { + Test(i32); +} +fn Test(comptime T: type) void { + const x = switch (T) { + []u8 => |x| x, + i32 => |x| x, + else => unreachable, + }; + _ = x; +} + +// switch with invalid expression parameter +// +// tmp.zig:7:17: error: switch on type 'type' provides no expression parameter diff --git a/test/compile_errors/stage1/obj/switch_with_overlapping_case_ranges.zig b/test/compile_errors/stage1/obj/switch_with_overlapping_case_ranges.zig new file mode 100644 index 0000000000..2818d766a7 --- /dev/null +++ b/test/compile_errors/stage1/obj/switch_with_overlapping_case_ranges.zig @@ -0,0 +1,11 @@ +export fn entry() void { + var q: u8 = 0; + switch (q) { + 1...2 => {}, + 0...255 => {}, + } +} + +// switch with overlapping case ranges +// +// tmp.zig:5:9: error: duplicate switch value diff --git a/test/compile_errors/stage1/obj/tagName_used_on_union_with_no_associated_enum_tag.zig b/test/compile_errors/stage1/obj/tagName_used_on_union_with_no_associated_enum_tag.zig new file mode 100644 index 0000000000..bd03a97cfd --- /dev/null +++ b/test/compile_errors/stage1/obj/tagName_used_on_union_with_no_associated_enum_tag.zig @@ -0,0 +1,14 @@ +const FloatInt = extern union { + Float: f32, + Int: i32, +}; +export fn entry() void { + var fi = FloatInt{.Float = 123.45}; + var tagName = @tagName(fi); + _ = tagName; +} + +// @tagName used on union with no associated enum tag +// +// tmp.zig:7:19: error: union has no associated enum +// tmp.zig:1:18: note: declared here diff --git a/test/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig b/test/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig new file mode 100644 index 0000000000..9beda30e2f --- /dev/null +++ b/test/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig @@ -0,0 +1,8 @@ +export fn entry() void { + const x = 'a'.*[0..]; + _ = x; +} + +// take slice of invalid dereference +// +// tmp.zig:2:18: error: attempt to dereference non-pointer type 'comptime_int' diff --git a/test/compile_errors/stage1/obj/taking_bit_offset_of_void_field_in_struct.zig b/test/compile_errors/stage1/obj/taking_bit_offset_of_void_field_in_struct.zig new file mode 100644 index 0000000000..936a110a61 --- /dev/null +++ b/test/compile_errors/stage1/obj/taking_bit_offset_of_void_field_in_struct.zig @@ -0,0 +1,11 @@ +const Empty = struct { + val: void, +}; +export fn foo() void { + const fieldOffset = @bitOffsetOf(Empty, "val",); + _ = fieldOffset; +} + +// taking bit offset of void field in struct +// +// tmp.zig:5:45: error: zero-bit field 'val' in struct 'Empty' has no offset diff --git a/test/compile_errors/stage1/obj/taking_byte_offset_of_void_field_in_struct.zig b/test/compile_errors/stage1/obj/taking_byte_offset_of_void_field_in_struct.zig new file mode 100644 index 0000000000..d71dbc1502 --- /dev/null +++ b/test/compile_errors/stage1/obj/taking_byte_offset_of_void_field_in_struct.zig @@ -0,0 +1,11 @@ +const Empty = struct { + val: void, +}; +export fn foo() void { + const fieldOffset = @offsetOf(Empty, "val",); + _ = fieldOffset; +} + +// taking byte offset of void field in struct +// +// tmp.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset diff --git a/test/compile_errors/stage1/obj/threadlocal_qualifier_on_const.zig b/test/compile_errors/stage1/obj/threadlocal_qualifier_on_const.zig new file mode 100644 index 0000000000..fa99e592ae --- /dev/null +++ b/test/compile_errors/stage1/obj/threadlocal_qualifier_on_const.zig @@ -0,0 +1,8 @@ +threadlocal const x: i32 = 1234; +export fn entry() i32 { + return x; +} + +// threadlocal qualifier on const +// +// tmp.zig:1:1: error: threadlocal variable cannot be constant diff --git a/test/compile_errors/stage1/obj/top_level_decl_dependency_loop.zig b/test/compile_errors/stage1/obj/top_level_decl_dependency_loop.zig new file mode 100644 index 0000000000..4ecdc6f67d --- /dev/null +++ b/test/compile_errors/stage1/obj/top_level_decl_dependency_loop.zig @@ -0,0 +1,10 @@ +const a : @TypeOf(b) = 0; +const b : @TypeOf(a) = 0; +export fn entry() void { + const c = a + b; + _ = c; +} + +// top level decl dependency loop +// +// tmp.zig:2:19: error: dependency loop detected diff --git a/test/compile_errors/stage1/obj/truncate_sign_mismatch.zig b/test/compile_errors/stage1/obj/truncate_sign_mismatch.zig new file mode 100644 index 0000000000..a60ca4bc7a --- /dev/null +++ b/test/compile_errors/stage1/obj/truncate_sign_mismatch.zig @@ -0,0 +1,23 @@ +export fn entry1() i8 { + var x: u32 = 10; + return @truncate(i8, x); +} +export fn entry2() u8 { + var x: i32 = -10; + return @truncate(u8, x); +} +export fn entry3() i8 { + comptime var x: u32 = 10; + return @truncate(i8, x); +} +export fn entry4() u8 { + comptime var x: i32 = -10; + return @truncate(u8, x); +} + +// truncate sign mismatch +// +// tmp.zig:3:26: error: expected signed integer type, found 'u32' +// tmp.zig:7:26: error: expected unsigned integer type, found 'i32' +// tmp.zig:11:26: error: expected signed integer type, found 'u32' +// tmp.zig:15:26: error: expected unsigned integer type, found 'i32' diff --git a/test/compile_errors/stage1/obj/truncate_undefined_value.zig b/test/compile_errors/stage1/obj/truncate_undefined_value.zig new file mode 100644 index 0000000000..9d3913f9c3 --- /dev/null +++ b/test/compile_errors/stage1/obj/truncate_undefined_value.zig @@ -0,0 +1,8 @@ +export fn entry() void { + var z = @truncate(u8, @as(u16, undefined)); + _ = z; +} + +// @truncate undefined value +// +// tmp.zig:2:27: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/try_in_function_with_non_error_return_type.zig b/test/compile_errors/stage1/obj/try_in_function_with_non_error_return_type.zig new file mode 100644 index 0000000000..d799f769b6 --- /dev/null +++ b/test/compile_errors/stage1/obj/try_in_function_with_non_error_return_type.zig @@ -0,0 +1,8 @@ +export fn f() void { + try something(); +} +fn something() anyerror!void { } + +// try in function with non error return type +// +// tmp.zig:2:5: error: expected type 'void', found 'anyerror' diff --git a/test/compile_errors/stage1/obj/type_checking_function_pointers.zig b/test/compile_errors/stage1/obj/type_checking_function_pointers.zig new file mode 100644 index 0000000000..d9bcde6bab --- /dev/null +++ b/test/compile_errors/stage1/obj/type_checking_function_pointers.zig @@ -0,0 +1,11 @@ +fn a(b: fn (*const u8) void) void { + b('a'); +} +fn c(d: u8) void {_ = d;} +export fn entry() void { + a(c); +} + +// type checking function pointers +// +// tmp.zig:6:7: error: expected type 'fn(*const u8) void', found 'fn(u8) void' diff --git a/test/compile_errors/stage1/obj/type_variables_must_be_constant.zig b/test/compile_errors/stage1/obj/type_variables_must_be_constant.zig new file mode 100644 index 0000000000..33846d6da8 --- /dev/null +++ b/test/compile_errors/stage1/obj/type_variables_must_be_constant.zig @@ -0,0 +1,8 @@ +var foo = u8; +export fn entry() foo { + return 1; +} + +// type variables must be constant +// +// tmp.zig:1:1: error: variable of type 'type' must be constant diff --git a/test/compile_errors/stage1/obj/undeclared_identifier.zig b/test/compile_errors/stage1/obj/undeclared_identifier.zig new file mode 100644 index 0000000000..36f46a22f4 --- /dev/null +++ b/test/compile_errors/stage1/obj/undeclared_identifier.zig @@ -0,0 +1,9 @@ +export fn a() void { + return + b + + c; +} + +// undeclared identifier +// +// tmp.zig:3:5: error: use of undeclared identifier 'b' diff --git a/test/compile_errors/stage1/obj/undeclared_identifier_error_should_mark_fn_as_impure.zig b/test/compile_errors/stage1/obj/undeclared_identifier_error_should_mark_fn_as_impure.zig new file mode 100644 index 0000000000..fa629bdcb8 --- /dev/null +++ b/test/compile_errors/stage1/obj/undeclared_identifier_error_should_mark_fn_as_impure.zig @@ -0,0 +1,10 @@ +export fn foo() void { + test_a_thing(); +} +fn test_a_thing() void { + bad_fn_call(); +} + +// undeclared identifier error should mark fn as impure +// +// tmp.zig:5:5: error: use of undeclared identifier 'bad_fn_call' diff --git a/test/compile_errors/stage1/obj/undeclared_identifier_in_unanalyzed_branch.zig b/test/compile_errors/stage1/obj/undeclared_identifier_in_unanalyzed_branch.zig new file mode 100644 index 0000000000..55d952e0fd --- /dev/null +++ b/test/compile_errors/stage1/obj/undeclared_identifier_in_unanalyzed_branch.zig @@ -0,0 +1,9 @@ +export fn a() void { + if (false) { + lol_this_doesnt_exist = nonsense; + } +} + +// undeclared identifier in unanalyzed branch +// +// tmp.zig:3:9: error: use of undeclared identifier 'lol_this_doesnt_exist' diff --git a/test/compile_errors/stage1/obj/undefined_as_field_type_is_rejected.zig b/test/compile_errors/stage1/obj/undefined_as_field_type_is_rejected.zig new file mode 100644 index 0000000000..4035955387 --- /dev/null +++ b/test/compile_errors/stage1/obj/undefined_as_field_type_is_rejected.zig @@ -0,0 +1,11 @@ +const Foo = struct { + a: undefined, +}; +export fn entry1() void { + const foo: Foo = undefined; + _ = foo; +} + +// undefined as field type is rejected +// +// tmp.zig:2:8: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/undefined_function_call.zig b/test/compile_errors/stage1/obj/undefined_function_call.zig new file mode 100644 index 0000000000..ebf76aafbd --- /dev/null +++ b/test/compile_errors/stage1/obj/undefined_function_call.zig @@ -0,0 +1,7 @@ +export fn a() void { + b(); +} + +// undefined function call +// +// tmp.zig:2:5: error: use of undeclared identifier 'b' diff --git a/test/compile_errors/stage1/obj/underscore_is_not_a_declarable_symbol.zig b/test/compile_errors/stage1/obj/underscore_is_not_a_declarable_symbol.zig new file mode 100644 index 0000000000..bfcfbc3bcc --- /dev/null +++ b/test/compile_errors/stage1/obj/underscore_is_not_a_declarable_symbol.zig @@ -0,0 +1,8 @@ +export fn f1() usize { + var _: usize = 2; + return _; +} + +// `_` is not a declarable symbol +// +// tmp.zig:2:9: error: '_' used as an identifier without @"_" syntax diff --git a/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_for.zig b/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_for.zig new file mode 100644 index 0000000000..be2ae559f3 --- /dev/null +++ b/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_for.zig @@ -0,0 +1,11 @@ +export fn returns() void { + for ([_]void{}) |_, i| { + for ([_]void{}) |_, j| { + return _; + } + } +} + +// `_` should not be usable inside for +// +// tmp.zig:4:20: error: '_' used as an identifier without @"_" syntax diff --git a/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while.zig b/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while.zig new file mode 100644 index 0000000000..d93ed28c9e --- /dev/null +++ b/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while.zig @@ -0,0 +1,14 @@ +export fn returns() void { + while (optionalReturn()) |_| { + while (optionalReturn()) |_| { + return _; + } + } +} +fn optionalReturn() ?u32 { + return 1; +} + +// `_` should not be usable inside while +// +// tmp.zig:4:20: error: '_' used as an identifier without @"_" syntax diff --git a/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while_else.zig b/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while_else.zig new file mode 100644 index 0000000000..e1e4c7b4c1 --- /dev/null +++ b/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while_else.zig @@ -0,0 +1,16 @@ +export fn returns() void { + while (optionalReturnError()) |_| { + while (optionalReturnError()) |_| { + return; + } else |_| { + if (_ == error.optionalReturnError) return; + } + } +} +fn optionalReturnError() !?u32 { + return error.optionalReturnError; +} + +// `_` should not be usable inside while else +// +// tmp.zig:6:17: error: '_' used as an identifier without @"_" syntax diff --git a/test/compile_errors/stage1/obj/union_auto-enum_value_already_taken.zig b/test/compile_errors/stage1/obj/union_auto-enum_value_already_taken.zig new file mode 100644 index 0000000000..377290bd7e --- /dev/null +++ b/test/compile_errors/stage1/obj/union_auto-enum_value_already_taken.zig @@ -0,0 +1,16 @@ +const MultipleChoice = union(enum(u32)) { + A = 20, + B = 40, + C = 60, + D = 1000, + E = 60, +}; +export fn entry() void { + var x = MultipleChoice { .C = {} }; + _ = x; +} + +// union auto-enum value already taken +// +// tmp.zig:6:9: error: enum tag value 60 already taken +// tmp.zig:4:9: note: other occurrence here diff --git a/test/compile_errors/stage1/obj/union_enum_field_does_not_match_enum.zig b/test/compile_errors/stage1/obj/union_enum_field_does_not_match_enum.zig new file mode 100644 index 0000000000..a924932c9f --- /dev/null +++ b/test/compile_errors/stage1/obj/union_enum_field_does_not_match_enum.zig @@ -0,0 +1,20 @@ +const Letter = enum { + A, + B, + C, +}; +const Payload = union(Letter) { + A: i32, + B: f64, + C: bool, + D: bool, +}; +export fn entry() void { + var a = Payload {.A = 1234}; + _ = a; +} + +// union enum field does not match enum +// +// tmp.zig:10:5: error: enum field not found: 'D' +// tmp.zig:1:16: note: enum declared here diff --git a/test/compile_errors/stage1/obj/union_fields_with_value_assignments.zig b/test/compile_errors/stage1/obj/union_fields_with_value_assignments.zig new file mode 100644 index 0000000000..0cf67a0eca --- /dev/null +++ b/test/compile_errors/stage1/obj/union_fields_with_value_assignments.zig @@ -0,0 +1,12 @@ +const MultipleChoice = union { + A: i32 = 20, +}; +export fn entry() void { + var x: MultipleChoice = undefined; + _ = x; +} + +// union fields with value assignments +// +// tmp.zig:1:24: error: explicitly valued tagged union missing integer tag type +// tmp.zig:2:14: note: tag value specified here diff --git a/test/compile_errors/stage1/obj/union_with_0_fields.zig b/test/compile_errors/stage1/obj/union_with_0_fields.zig new file mode 100644 index 0000000000..36086e8cce --- /dev/null +++ b/test/compile_errors/stage1/obj/union_with_0_fields.zig @@ -0,0 +1,5 @@ +const Foo = union {}; + +// union with 0 fields +// +// tmp.zig:1:13: error: union declarations must have at least one tag diff --git a/test/compile_errors/stage1/obj/union_with_specified_enum_omits_field.zig b/test/compile_errors/stage1/obj/union_with_specified_enum_omits_field.zig new file mode 100644 index 0000000000..c1c1d38f02 --- /dev/null +++ b/test/compile_errors/stage1/obj/union_with_specified_enum_omits_field.zig @@ -0,0 +1,17 @@ +const Letter = enum { + A, + B, + C, +}; +const Payload = union(Letter) { + A: i32, + B: f64, +}; +export fn entry() usize { + return @sizeOf(Payload); +} + +// union with specified enum omits field +// +// tmp.zig:6:17: error: enum field missing: 'C' +// tmp.zig:4:5: note: declared here diff --git a/test/compile_errors/stage1/obj/union_with_too_small_explicit_signed_tag_type.zig b/test/compile_errors/stage1/obj/union_with_too_small_explicit_signed_tag_type.zig new file mode 100644 index 0000000000..a3e75c25f6 --- /dev/null +++ b/test/compile_errors/stage1/obj/union_with_too_small_explicit_signed_tag_type.zig @@ -0,0 +1,14 @@ +const U = union(enum(i2)) { + A: u8, + B: u8, + C: u8, + D: u8, +}; +export fn entry() void { + _ = U{ .D = 1 }; +} + +// union with too small explicit signed tag type +// +// tmp.zig:1:22: error: specified integer tag type cannot represent every field +// tmp.zig:1:22: note: type i2 cannot fit values in range 0...3 diff --git a/test/compile_errors/stage1/obj/union_with_too_small_explicit_unsigned_tag_type.zig b/test/compile_errors/stage1/obj/union_with_too_small_explicit_unsigned_tag_type.zig new file mode 100644 index 0000000000..6f13dcda8a --- /dev/null +++ b/test/compile_errors/stage1/obj/union_with_too_small_explicit_unsigned_tag_type.zig @@ -0,0 +1,15 @@ +const U = union(enum(u2)) { + A: u8, + B: u8, + C: u8, + D: u8, + E: u8, +}; +export fn entry() void { + _ = U{ .E = 1 }; +} + +// union with too small explicit unsigned tag type +// +// tmp.zig:1:22: error: specified integer tag type cannot represent every field +// tmp.zig:1:22: note: type u2 cannot fit values in range 0...4 diff --git a/test/compile_errors/stage1/obj/unknown_length_pointer_to_opaque.zig b/test/compile_errors/stage1/obj/unknown_length_pointer_to_opaque.zig new file mode 100644 index 0000000000..a847bae599 --- /dev/null +++ b/test/compile_errors/stage1/obj/unknown_length_pointer_to_opaque.zig @@ -0,0 +1,5 @@ +export const T = [*]opaque {}; + +// unknown length pointer to opaque +// +// tmp.zig:1:21: error: unknown-length pointer to opaque diff --git a/test/compile_errors/stage1/obj/unreachable_code-double_break.zig b/test/compile_errors/stage1/obj/unreachable_code-double_break.zig new file mode 100644 index 0000000000..b030c1d24d --- /dev/null +++ b/test/compile_errors/stage1/obj/unreachable_code-double_break.zig @@ -0,0 +1,10 @@ +export fn a() void { + const b = blk: { + break :blk break :blk @as(u32, 1); + }; +} + +// unreachable code - double break +// +// tmp.zig:3:9: error: unreachable code +// tmp.zig:3:20: note: control flow is diverted here diff --git a/test/compile_errors/stage1/obj/unreachable_code-nested_returns.zig b/test/compile_errors/stage1/obj/unreachable_code-nested_returns.zig new file mode 100644 index 0000000000..7bfa8eef64 --- /dev/null +++ b/test/compile_errors/stage1/obj/unreachable_code-nested_returns.zig @@ -0,0 +1,8 @@ +export fn a() i32 { + return return 1; +} + +// unreachable code - nested returns +// +// tmp.zig:2:5: error: unreachable code +// tmp.zig:2:12: note: control flow is diverted here diff --git a/test/compile_errors/stage1/obj/unreachable_code.zig b/test/compile_errors/stage1/obj/unreachable_code.zig new file mode 100644 index 0000000000..779cd90379 --- /dev/null +++ b/test/compile_errors/stage1/obj/unreachable_code.zig @@ -0,0 +1,11 @@ +export fn a() void { + return; + b(); +} + +fn b() void {} + +// unreachable code +// +// tmp.zig:3:6: error: unreachable code +// tmp.zig:2:5: note: control flow is diverted here diff --git a/test/compile_errors/stage1/obj/unreachable_executed_at_comptime.zig b/test/compile_errors/stage1/obj/unreachable_executed_at_comptime.zig new file mode 100644 index 0000000000..857ab0417f --- /dev/null +++ b/test/compile_errors/stage1/obj/unreachable_executed_at_comptime.zig @@ -0,0 +1,14 @@ +fn foo(comptime x: i32) i32 { + comptime { + if (x >= 0) return -x; + unreachable; + } +} +export fn entry() void { + _ = foo(-42); +} + +// unreachable executed at comptime +// +// tmp.zig:4:9: error: reached unreachable code +// tmp.zig:8:12: note: called from here diff --git a/test/compile_errors/stage1/obj/unreachable_parameter.zig b/test/compile_errors/stage1/obj/unreachable_parameter.zig new file mode 100644 index 0000000000..3feed62a1e --- /dev/null +++ b/test/compile_errors/stage1/obj/unreachable_parameter.zig @@ -0,0 +1,6 @@ +fn f(a: noreturn) void { _ = a; } +export fn entry() void { f(); } + +// unreachable parameter +// +// tmp.zig:1:9: error: parameter of type 'noreturn' not allowed diff --git a/test/compile_errors/stage1/obj/unreachable_variable.zig b/test/compile_errors/stage1/obj/unreachable_variable.zig new file mode 100644 index 0000000000..8e053acd06 --- /dev/null +++ b/test/compile_errors/stage1/obj/unreachable_variable.zig @@ -0,0 +1,8 @@ +export fn f() void { + const a: noreturn = {}; + _ = a; +} + +// unreachable variable +// +// tmp.zig:2:25: error: expected type 'noreturn', found 'void' diff --git a/test/compile_errors/stage1/obj/unreachable_with_return.zig b/test/compile_errors/stage1/obj/unreachable_with_return.zig new file mode 100644 index 0000000000..ac9ecc93e3 --- /dev/null +++ b/test/compile_errors/stage1/obj/unreachable_with_return.zig @@ -0,0 +1,6 @@ +fn a() noreturn {return;} +export fn entry() void { a(); } + +// unreachable with return +// +// tmp.zig:1:18: error: expected type 'noreturn', found 'void' diff --git a/test/compile_errors/stage1/obj/unsupported_modifier_at_start_of_asm_output_constraint.zig b/test/compile_errors/stage1/obj/unsupported_modifier_at_start_of_asm_output_constraint.zig new file mode 100644 index 0000000000..c85850a61b --- /dev/null +++ b/test/compile_errors/stage1/obj/unsupported_modifier_at_start_of_asm_output_constraint.zig @@ -0,0 +1,8 @@ +export fn foo() void { + var bar: u32 = 3; + asm volatile ("" : [baz]"+r"(bar) : : ""); +} + +// unsupported modifier at start of asm output constraint +// +// tmp.zig:3:5: error: invalid modifier starting output constraint for 'baz': '+', only '=' is supported. Compiler TODO: see https://github.com/ziglang/zig/issues/215 diff --git a/test/compile_errors/stage1/obj/use_anyopaque_as_return_type_of_fn_ptr.zig b/test/compile_errors/stage1/obj/use_anyopaque_as_return_type_of_fn_ptr.zig new file mode 100644 index 0000000000..f54bc14914 --- /dev/null +++ b/test/compile_errors/stage1/obj/use_anyopaque_as_return_type_of_fn_ptr.zig @@ -0,0 +1,8 @@ +export fn entry() void { + const a: fn () anyopaque = undefined; + _ = a; +} + +// use anyopaque as return type of fn ptr +// +// tmp.zig:2:20: error: return type cannot be opaque diff --git a/test/compile_errors/stage1/obj/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig b/test/compile_errors/stage1/obj/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig new file mode 100644 index 0000000000..a8e01dbde0 --- /dev/null +++ b/test/compile_errors/stage1/obj/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig @@ -0,0 +1,12 @@ +export fn entry() void { + var x: i32 = 1234; + var p: *i32 = &x; + var pp: *?*i32 = &p; + pp.* = null; + var y = p.*; + _ = y; +} + +// use implicit casts to assign null to non-nullable pointer +// +// tmp.zig:4:23: error: expected type '*?*i32', found '**i32' diff --git a/test/compile_errors/stage1/obj/use_invalid_number_literal_as_array_index.zig b/test/compile_errors/stage1/obj/use_invalid_number_literal_as_array_index.zig new file mode 100644 index 0000000000..5ec655737e --- /dev/null +++ b/test/compile_errors/stage1/obj/use_invalid_number_literal_as_array_index.zig @@ -0,0 +1,9 @@ +var v = 25; +export fn entry() void { + var arr: [v]u8 = undefined; + _ = arr; +} + +// use invalid number literal as array index +// +// tmp.zig:1:1: error: unable to infer variable type diff --git a/test/compile_errors/stage1/obj/use_of_comptime-known_undefined_function_value.zig b/test/compile_errors/stage1/obj/use_of_comptime-known_undefined_function_value.zig new file mode 100644 index 0000000000..5facc7e753 --- /dev/null +++ b/test/compile_errors/stage1/obj/use_of_comptime-known_undefined_function_value.zig @@ -0,0 +1,11 @@ +const Cmd = struct { + exec: fn () void, +}; +export fn entry() void { + const command = Cmd{ .exec = undefined }; + command.exec(); +} + +// use of comptime-known undefined function value +// +// tmp.zig:6:12: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/use_of_undeclared_identifier.zig b/test/compile_errors/stage1/obj/use_of_undeclared_identifier.zig new file mode 100644 index 0000000000..e36453acc7 --- /dev/null +++ b/test/compile_errors/stage1/obj/use_of_undeclared_identifier.zig @@ -0,0 +1,7 @@ +export fn f() void { + b = 3; +} + +// use of undeclared identifier +// +// tmp.zig:2:5: error: use of undeclared identifier 'b' diff --git a/test/compile_errors/stage1/obj/using_an_unknown_len_ptr_type_instead_of_array.zig b/test/compile_errors/stage1/obj/using_an_unknown_len_ptr_type_instead_of_array.zig new file mode 100644 index 0000000000..983995b098 --- /dev/null +++ b/test/compile_errors/stage1/obj/using_an_unknown_len_ptr_type_instead_of_array.zig @@ -0,0 +1,11 @@ +const resolutions = [*][*]const u8{ + "[320 240 ]", + null, +}; +comptime { + _ = resolutions; +} + +// using an unknown len ptr type instead of array +// +// tmp.zig:1:21: error: expected array type or [_], found '[*][*]const u8' diff --git a/test/compile_errors/stage1/obj/using_invalid_types_in_function_call_raises_an_error.zig b/test/compile_errors/stage1/obj/using_invalid_types_in_function_call_raises_an_error.zig new file mode 100644 index 0000000000..182b5c59b0 --- /dev/null +++ b/test/compile_errors/stage1/obj/using_invalid_types_in_function_call_raises_an_error.zig @@ -0,0 +1,9 @@ +const MenuEffect = enum {}; +fn func(effect: MenuEffect) void { _ = effect; } +export fn entry() void { + func(MenuEffect.ThisDoesNotExist); +} + +// using invalid types in function call raises an error +// +// tmp.zig:1:20: error: enum declarations must have at least one tag diff --git a/test/compile_errors/stage1/obj/usingnamespace_with_wrong_type.zig b/test/compile_errors/stage1/obj/usingnamespace_with_wrong_type.zig new file mode 100644 index 0000000000..a74a8e6124 --- /dev/null +++ b/test/compile_errors/stage1/obj/usingnamespace_with_wrong_type.zig @@ -0,0 +1,5 @@ +usingnamespace void; + +// usingnamespace with wrong type +// +// tmp.zig:1:1: error: expected struct, enum, or union; found 'void' diff --git a/test/compile_errors/stage1/obj/variable_has_wrong_type.zig b/test/compile_errors/stage1/obj/variable_has_wrong_type.zig new file mode 100644 index 0000000000..1e20dde269 --- /dev/null +++ b/test/compile_errors/stage1/obj/variable_has_wrong_type.zig @@ -0,0 +1,8 @@ +export fn f() i32 { + const a = "a"; + return a; +} + +// variable has wrong type +// +// tmp.zig:3:12: error: expected type 'i32', found '*const [1:0]u8' diff --git a/test/compile_errors/stage1/obj/variable_initialization_compile_error_then_referenced.zig b/test/compile_errors/stage1/obj/variable_initialization_compile_error_then_referenced.zig new file mode 100644 index 0000000000..d96b67bc3f --- /dev/null +++ b/test/compile_errors/stage1/obj/variable_initialization_compile_error_then_referenced.zig @@ -0,0 +1,17 @@ +fn Undeclared() type { + return T; +} +fn Gen() type { + const X = Undeclared(); + return struct { + x: X, + }; +} +export fn entry() void { + const S = Gen(); + _ = S; +} + +// variable initialization compile error then referenced +// +// tmp.zig:2:12: error: use of undeclared identifier 'T' diff --git a/test/compile_errors/stage1/obj/variable_with_type_noreturn.zig b/test/compile_errors/stage1/obj/variable_with_type_noreturn.zig new file mode 100644 index 0000000000..b6cc65593b --- /dev/null +++ b/test/compile_errors/stage1/obj/variable_with_type_noreturn.zig @@ -0,0 +1,8 @@ +export fn entry9() void { + var z: noreturn = return; +} + +// variable with type 'noreturn' +// +// tmp.zig:2:5: error: unreachable code +// tmp.zig:2:23: note: control flow is diverted here diff --git a/test/compile_errors/stage1/obj/vector_index_out_of_bounds.zig b/test/compile_errors/stage1/obj/vector_index_out_of_bounds.zig new file mode 100644 index 0000000000..efdd8b5fe9 --- /dev/null +++ b/test/compile_errors/stage1/obj/vector_index_out_of_bounds.zig @@ -0,0 +1,8 @@ +export fn entry() void { + const x = @import("std").meta.Vector(3, f32){ 25, 75, 5, 0 }; + _ = x; +} + +// vector index out of bounds +// +// tmp.zig:2:62: error: index 3 outside vector of size 3 diff --git a/test/compile_errors/stage1/obj/volatile_on_global_assembly.zig b/test/compile_errors/stage1/obj/volatile_on_global_assembly.zig new file mode 100644 index 0000000000..8fc7ddfcc2 --- /dev/null +++ b/test/compile_errors/stage1/obj/volatile_on_global_assembly.zig @@ -0,0 +1,7 @@ +comptime { + asm volatile (""); +} + +// volatile on global assembly +// +// tmp.zig:2:9: error: volatile is meaningless on global assembly diff --git a/test/compile_errors/stage1/obj/wasmMemoryGrow_is_a_compile_error_in_non-Wasm_targets.zig b/test/compile_errors/stage1/obj/wasmMemoryGrow_is_a_compile_error_in_non-Wasm_targets.zig new file mode 100644 index 0000000000..363b36a0af --- /dev/null +++ b/test/compile_errors/stage1/obj/wasmMemoryGrow_is_a_compile_error_in_non-Wasm_targets.zig @@ -0,0 +1,8 @@ +export fn foo() void { + _ = @wasmMemoryGrow(0, 1); + return; +} + +// wasmMemoryGrow is a compile error in non-Wasm targets +// +// tmp.zig:2:9: error: @wasmMemoryGrow is a wasm32 feature only diff --git a/test/compile_errors/stage1/obj/wasmMemorySize_is_a_compile_error_in_non-Wasm_targets.zig b/test/compile_errors/stage1/obj/wasmMemorySize_is_a_compile_error_in_non-Wasm_targets.zig new file mode 100644 index 0000000000..419173bd01 --- /dev/null +++ b/test/compile_errors/stage1/obj/wasmMemorySize_is_a_compile_error_in_non-Wasm_targets.zig @@ -0,0 +1,8 @@ +export fn foo() void { + _ = @wasmMemorySize(0); + return; +} + +// wasmMemorySize is a compile error in non-Wasm targets +// +// tmp.zig:2:9: error: @wasmMemorySize is a wasm32 feature only diff --git a/test/compile_errors/stage1/obj/while_expected_bool_got_error_union.zig b/test/compile_errors/stage1/obj/while_expected_bool_got_error_union.zig new file mode 100644 index 0000000000..e79296694d --- /dev/null +++ b/test/compile_errors/stage1/obj/while_expected_bool_got_error_union.zig @@ -0,0 +1,8 @@ +export fn foo() void { + while (bar()) {} +} +fn bar() anyerror!i32 { return 1; } + +// while expected bool, got error union +// +// tmp.zig:2:15: error: expected type 'bool', found 'anyerror!i32' diff --git a/test/compile_errors/stage1/obj/while_expected_bool_got_optional.zig b/test/compile_errors/stage1/obj/while_expected_bool_got_optional.zig new file mode 100644 index 0000000000..7a3d184a1d --- /dev/null +++ b/test/compile_errors/stage1/obj/while_expected_bool_got_optional.zig @@ -0,0 +1,8 @@ +export fn foo() void { + while (bar()) {} +} +fn bar() ?i32 { return 1; } + +// while expected bool, got optional +// +// tmp.zig:2:15: error: expected type 'bool', found '?i32' diff --git a/test/compile_errors/stage1/obj/while_expected_error_union_got_bool.zig b/test/compile_errors/stage1/obj/while_expected_error_union_got_bool.zig new file mode 100644 index 0000000000..8f92276af6 --- /dev/null +++ b/test/compile_errors/stage1/obj/while_expected_error_union_got_bool.zig @@ -0,0 +1,8 @@ +export fn foo() void { + while (bar()) |x| {_ = x;} else |err| {_ = err;} +} +fn bar() bool { return true; } + +// while expected error union, got bool +// +// tmp.zig:2:15: error: expected error union type, found 'bool' diff --git a/test/compile_errors/stage1/obj/while_expected_error_union_got_optional.zig b/test/compile_errors/stage1/obj/while_expected_error_union_got_optional.zig new file mode 100644 index 0000000000..a67bf1ae16 --- /dev/null +++ b/test/compile_errors/stage1/obj/while_expected_error_union_got_optional.zig @@ -0,0 +1,8 @@ +export fn foo() void { + while (bar()) |x| {_ = x;} else |err| {_ = err;} +} +fn bar() ?i32 { return 1; } + +// while expected error union, got optional +// +// tmp.zig:2:15: error: expected error union type, found '?i32' diff --git a/test/compile_errors/stage1/obj/while_expected_optional_got_bool.zig b/test/compile_errors/stage1/obj/while_expected_optional_got_bool.zig new file mode 100644 index 0000000000..6fc0d880ce --- /dev/null +++ b/test/compile_errors/stage1/obj/while_expected_optional_got_bool.zig @@ -0,0 +1,8 @@ +export fn foo() void { + while (bar()) |x| {_ = x;} +} +fn bar() bool { return true; } + +// while expected optional, got bool +// +// tmp.zig:2:15: error: expected optional type, found 'bool' diff --git a/test/compile_errors/stage1/obj/while_expected_optional_got_error_union.zig b/test/compile_errors/stage1/obj/while_expected_optional_got_error_union.zig new file mode 100644 index 0000000000..472b3b81bc --- /dev/null +++ b/test/compile_errors/stage1/obj/while_expected_optional_got_error_union.zig @@ -0,0 +1,8 @@ +export fn foo() void { + while (bar()) |x| {_ = x;} +} +fn bar() anyerror!i32 { return 1; } + +// while expected optional, got error union +// +// tmp.zig:2:15: error: expected optional type, found 'anyerror!i32' diff --git a/test/compile_errors/stage1/obj/while_loop_body_expression_ignored.zig b/test/compile_errors/stage1/obj/while_loop_body_expression_ignored.zig new file mode 100644 index 0000000000..621c0d41d7 --- /dev/null +++ b/test/compile_errors/stage1/obj/while_loop_body_expression_ignored.zig @@ -0,0 +1,20 @@ +fn returns() usize { + return 2; +} +export fn f1() void { + while (true) returns(); +} +export fn f2() void { + var x: ?i32 = null; + while (x) |_| returns(); +} +export fn f3() void { + var x: anyerror!i32 = error.Bad; + while (x) |_| returns() else |_| unreachable; +} + +// while loop body expression ignored +// +// tmp.zig:5:25: error: expression value is ignored +// tmp.zig:9:26: error: expression value is ignored +// tmp.zig:13:26: error: expression value is ignored diff --git a/test/compile_errors/stage1/obj/write_to_const_global_variable.zig b/test/compile_errors/stage1/obj/write_to_const_global_variable.zig new file mode 100644 index 0000000000..327b3d02d0 --- /dev/null +++ b/test/compile_errors/stage1/obj/write_to_const_global_variable.zig @@ -0,0 +1,9 @@ +const x : i32 = 99; +fn f() void { + x = 1; +} +export fn entry() void { f(); } + +// write to const global variable +// +// tmp.zig:3:9: error: cannot assign to constant diff --git a/test/compile_errors/stage1/obj/wrong_frame_type_used_for_async_call.zig b/test/compile_errors/stage1/obj/wrong_frame_type_used_for_async_call.zig new file mode 100644 index 0000000000..ba150c45cc --- /dev/null +++ b/test/compile_errors/stage1/obj/wrong_frame_type_used_for_async_call.zig @@ -0,0 +1,14 @@ +export fn entry() void { + var frame: @Frame(foo) = undefined; + frame = async bar(); +} +fn foo() void { + suspend {} +} +fn bar() void { + suspend {} +} + +// wrong frame type used for async call +// +// tmp.zig:3:13: error: expected type '*@Frame(bar)', found '*@Frame(foo)' diff --git a/test/compile_errors/stage1/obj/wrong_function_type.zig b/test/compile_errors/stage1/obj/wrong_function_type.zig new file mode 100644 index 0000000000..c2238bb649 --- /dev/null +++ b/test/compile_errors/stage1/obj/wrong_function_type.zig @@ -0,0 +1,9 @@ +const fns = [_]fn() void { a, b, c }; +fn a() i32 {return 0;} +fn b() i32 {return 1;} +fn c() i32 {return 2;} +export fn entry() usize { return @sizeOf(@TypeOf(fns)); } + +// wrong function type +// +// tmp.zig:1:28: error: expected type 'fn() void', found 'fn() i32' diff --git a/test/compile_errors/stage1/obj/wrong_initializer_for_union_payload_of_type_type.zig b/test/compile_errors/stage1/obj/wrong_initializer_for_union_payload_of_type_type.zig new file mode 100644 index 0000000000..1d56094b6d --- /dev/null +++ b/test/compile_errors/stage1/obj/wrong_initializer_for_union_payload_of_type_type.zig @@ -0,0 +1,14 @@ +const U = union(enum) { + A: type, +}; +const S = struct { + u: U, +}; +export fn entry() void { + comptime var v: S = undefined; + v.u.A = U{ .A = i32 }; +} + +// wrong initializer for union payload of type 'type' +// +// tmp.zig:9:8: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/wrong_number_of_arguments.zig b/test/compile_errors/stage1/obj/wrong_number_of_arguments.zig new file mode 100644 index 0000000000..4cb13fdd12 --- /dev/null +++ b/test/compile_errors/stage1/obj/wrong_number_of_arguments.zig @@ -0,0 +1,8 @@ +export fn a() void { + c(1); +} +fn c(d: i32, e: i32, f: i32) void { _ = d; _ = e; _ = f; } + +// wrong number of arguments +// +// tmp.zig:2:6: error: expected 3 argument(s), found 1 diff --git a/test/compile_errors/stage1/obj/wrong_number_of_arguments_for_method_fn_call.zig b/test/compile_errors/stage1/obj/wrong_number_of_arguments_for_method_fn_call.zig new file mode 100644 index 0000000000..25449feb22 --- /dev/null +++ b/test/compile_errors/stage1/obj/wrong_number_of_arguments_for_method_fn_call.zig @@ -0,0 +1,12 @@ +const Foo = struct { + fn method(self: *const Foo, a: i32) void {_ = self; _ = a;} +}; +fn f(foo: *const Foo) void { + + foo.method(1, 2); +} +export fn entry() usize { return @sizeOf(@TypeOf(f)); } + +// wrong number of arguments for method fn call +// +// tmp.zig:6:15: error: expected 2 argument(s), found 3 diff --git a/test/compile_errors/stage1/obj/wrong_panic_signature_generic_function.zig b/test/compile_errors/stage1/obj/wrong_panic_signature_generic_function.zig new file mode 100644 index 0000000000..faaa24ba60 --- /dev/null +++ b/test/compile_errors/stage1/obj/wrong_panic_signature_generic_function.zig @@ -0,0 +1,10 @@ +pub fn panic(comptime msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { + _ = msg; _ = error_return_trace; + while (true) {} +} +const builtin = @import("std").builtin; + +// wrong panic signature, generic function +// +// error: expected type 'fn([]const u8, ?*std.builtin.StackTrace) noreturn', found 'fn([]const u8,anytype) anytype' +// note: only one of the functions is generic diff --git a/test/compile_errors/stage1/obj/wrong_panic_signature_runtime_function.zig b/test/compile_errors/stage1/obj/wrong_panic_signature_runtime_function.zig new file mode 100644 index 0000000000..92553c3104 --- /dev/null +++ b/test/compile_errors/stage1/obj/wrong_panic_signature_runtime_function.zig @@ -0,0 +1,8 @@ +test "" {} + +pub fn panic() void {} + + +// wrong panic signature, runtime function +// +// error: expected type 'fn([]const u8, ?*std.builtin.StackTrace) noreturn', found 'fn() void' diff --git a/test/compile_errors/stage1/obj/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig b/test/compile_errors/stage1/obj/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig new file mode 100644 index 0000000000..ed88e3be28 --- /dev/null +++ b/test/compile_errors/stage1/obj/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig @@ -0,0 +1,10 @@ +const Derp = opaque {}; +extern fn bar(d: *Derp) void; +export fn foo() void { + var x = @as(u8, 1); + bar(@ptrCast(*anyopaque, &x)); +} + +// wrong pointer coerced to pointer to opaque {} +// +// tmp.zig:5:9: error: expected type '*Derp', found '*anyopaque' diff --git a/test/compile_errors/stage1/obj/wrong_return_type_for_main.zig b/test/compile_errors/stage1/obj/wrong_return_type_for_main.zig new file mode 100644 index 0000000000..bf335874db --- /dev/null +++ b/test/compile_errors/stage1/obj/wrong_return_type_for_main.zig @@ -0,0 +1,5 @@ +pub fn main() f32 { } + +// wrong return type for main +// +// error: expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8' diff --git a/test/compile_errors/stage1/obj/wrong_size_to_an_array_literal.zig b/test/compile_errors/stage1/obj/wrong_size_to_an_array_literal.zig new file mode 100644 index 0000000000..68cb263212 --- /dev/null +++ b/test/compile_errors/stage1/obj/wrong_size_to_an_array_literal.zig @@ -0,0 +1,8 @@ +comptime { + const array = [2]u8{1, 2, 3}; + _ = array; +} + +// wrong size to an array literal +// +// tmp.zig:2:31: error: index 2 outside array of size 2 diff --git a/test/compile_errors/stage1/obj/wrong_type_for_argument_tuple_to_asyncCall.zig b/test/compile_errors/stage1/obj/wrong_type_for_argument_tuple_to_asyncCall.zig new file mode 100644 index 0000000000..d9e45a69b7 --- /dev/null +++ b/test/compile_errors/stage1/obj/wrong_type_for_argument_tuple_to_asyncCall.zig @@ -0,0 +1,12 @@ +export fn entry1() void { + var frame: @Frame(foo) = undefined; + @asyncCall(&frame, {}, foo, {}); +} + +fn foo() i32 { + return 0; +} + +// wrong type for argument tuple to @asyncCall +// +// tmp.zig:3:33: error: expected tuple or struct, found 'void' diff --git a/test/compile_errors/stage1/obj/wrong_type_for_reify_type.zig b/test/compile_errors/stage1/obj/wrong_type_for_reify_type.zig new file mode 100644 index 0000000000..2d8ea02326 --- /dev/null +++ b/test/compile_errors/stage1/obj/wrong_type_for_reify_type.zig @@ -0,0 +1,7 @@ +export fn entry() void { + _ = @Type(0); +} + +// wrong type for @Type +// +// tmp.zig:2:15: error: expected type 'std.builtin.Type', found 'comptime_int' diff --git a/test/compile_errors/stage1/obj/wrong_type_for_result_ptr_to_asyncCall.zig b/test/compile_errors/stage1/obj/wrong_type_for_result_ptr_to_asyncCall.zig new file mode 100644 index 0000000000..07c5ae8a0b --- /dev/null +++ b/test/compile_errors/stage1/obj/wrong_type_for_result_ptr_to_asyncCall.zig @@ -0,0 +1,14 @@ +export fn entry() void { + _ = async amain(); +} +fn amain() i32 { + var frame: @Frame(foo) = undefined; + return await @asyncCall(&frame, false, foo, .{}); +} +fn foo() i32 { + return 1234; +} + +// wrong type for result ptr to @asyncCall +// +// tmp.zig:6:37: error: expected type '*i32', found 'bool' diff --git a/test/compile_errors/stage1/obj/wrong_type_passed_to_panic.zig b/test/compile_errors/stage1/obj/wrong_type_passed_to_panic.zig new file mode 100644 index 0000000000..b16d563571 --- /dev/null +++ b/test/compile_errors/stage1/obj/wrong_type_passed_to_panic.zig @@ -0,0 +1,8 @@ +export fn entry() void { + var e = error.Foo; + @panic(e); +} + +// wrong type passed to @panic +// +// tmp.zig:3:12: error: expected type '[]const u8', found 'error{Foo}' diff --git a/test/compile_errors/stage1/obj/wrong_type_to_hasField.zig b/test/compile_errors/stage1/obj/wrong_type_to_hasField.zig new file mode 100644 index 0000000000..a79fec2a21 --- /dev/null +++ b/test/compile_errors/stage1/obj/wrong_type_to_hasField.zig @@ -0,0 +1,7 @@ +export fn entry() bool { + return @hasField(i32, "hi"); +} + +// wrong type to @hasField +// +// tmp.zig:2:22: error: type 'i32' does not support @hasField diff --git a/test/compile_errors/stage1/obj/wrong_types_given_to_atomic_order_args_in_cmpxchg.zig b/test/compile_errors/stage1/obj/wrong_types_given_to_atomic_order_args_in_cmpxchg.zig new file mode 100644 index 0000000000..47cdaa372f --- /dev/null +++ b/test/compile_errors/stage1/obj/wrong_types_given_to_atomic_order_args_in_cmpxchg.zig @@ -0,0 +1,8 @@ +export fn entry() void { + var x: i32 = 1234; + while (!@cmpxchgWeak(i32, &x, 1234, 5678, @as(u32, 1234), @as(u32, 1234))) {} +} + +// wrong types given to atomic order args in cmpxchg +// +// tmp.zig:3:47: error: expected type 'std.builtin.AtomicOrder', found 'u32' diff --git a/test/compile_errors/stage1/obj/wrong_types_given_to_export.zig b/test/compile_errors/stage1/obj/wrong_types_given_to_export.zig new file mode 100644 index 0000000000..cdd7ab4631 --- /dev/null +++ b/test/compile_errors/stage1/obj/wrong_types_given_to_export.zig @@ -0,0 +1,8 @@ +fn entry() callconv(.C) void { } +comptime { + @export(entry, .{.name = "entry", .linkage = @as(u32, 1234) }); +} + +// wrong types given to @export +// +// tmp.zig:3:59: error: expected type 'std.builtin.GlobalLinkage', found 'comptime_int' diff --git a/test/compile_errors/stage1/test/access_invalid_typeInfo_decl.zig b/test/compile_errors/stage1/test/access_invalid_typeInfo_decl.zig new file mode 100644 index 0000000000..2fc5730af0 --- /dev/null +++ b/test/compile_errors/stage1/test/access_invalid_typeInfo_decl.zig @@ -0,0 +1,8 @@ +const A = B; +test "Crash" { + _ = @typeInfo(@This()).Struct.decls[0]; +} + +// access invalid @typeInfo decl +// +// tmp.zig:1:11: error: use of undeclared identifier 'B' diff --git a/test/compile_errors/stage1/test/alignCast_of_zero_sized_types.zig b/test/compile_errors/stage1/test/alignCast_of_zero_sized_types.zig new file mode 100644 index 0000000000..4c05571202 --- /dev/null +++ b/test/compile_errors/stage1/test/alignCast_of_zero_sized_types.zig @@ -0,0 +1,25 @@ +export fn foo() void { + const a: *void = undefined; + _ = @alignCast(2, a); +} +export fn bar() void { + const a: ?*void = undefined; + _ = @alignCast(2, a); +} +export fn baz() void { + const a: []void = undefined; + _ = @alignCast(2, a); +} +export fn qux() void { + const a = struct { + fn a(comptime b: u32) void { _ = b; } + }.a; + _ = @alignCast(2, a); +} + +// @alignCast of zero sized types +// +// tmp.zig:3:23: error: cannot adjust alignment of zero sized type '*void' +// tmp.zig:7:23: error: cannot adjust alignment of zero sized type '?*void' +// tmp.zig:11:23: error: cannot adjust alignment of zero sized type '[]void' +// tmp.zig:17:23: error: cannot adjust alignment of zero sized type 'fn(u32) anytype' diff --git a/test/compile_errors/stage1/test/bad_splat_type.zig b/test/compile_errors/stage1/test/bad_splat_type.zig new file mode 100644 index 0000000000..9a16bbc73d --- /dev/null +++ b/test/compile_errors/stage1/test/bad_splat_type.zig @@ -0,0 +1,9 @@ +export fn entry() void { + const c = 4; + var v = @splat(4, c); + _ = v; +} + +// bad @splat type +// +// tmp.zig:3:23: error: vector element type must be integer, float, bool, or pointer; 'comptime_int' is invalid diff --git a/test/compile_errors/stage1/test/binary_OR_operator_on_error_sets.zig b/test/compile_errors/stage1/test/binary_OR_operator_on_error_sets.zig new file mode 100644 index 0000000000..d9894a32a0 --- /dev/null +++ b/test/compile_errors/stage1/test/binary_OR_operator_on_error_sets.zig @@ -0,0 +1,10 @@ +pub const A = error.A; +pub const AB = A | error.B; +export fn entry() void { + var x: AB = undefined; + _ = x; +} + +// binary OR operator on error sets +// +// tmp.zig:2:18: error: invalid operands to binary expression: 'error{A}' and 'error{B}' diff --git a/test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-always_inline.zig b/test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-always_inline.zig new file mode 100644 index 0000000000..ea822f8d91 --- /dev/null +++ b/test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-always_inline.zig @@ -0,0 +1,8 @@ +pub export fn entry() void { + var call_me: fn () void = undefined; + @call(.{ .modifier = .always_inline }, call_me, .{}); +} + +// @call rejects non comptime-known fn - always_inline +// +// tmp.zig:3:5: error: the specified modifier requires a comptime-known function diff --git a/test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-compile_time.zig b/test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-compile_time.zig new file mode 100644 index 0000000000..3c50830787 --- /dev/null +++ b/test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-compile_time.zig @@ -0,0 +1,8 @@ +pub export fn entry() void { + var call_me: fn () void = undefined; + @call(.{ .modifier = .compile_time }, call_me, .{}); +} + +// @call rejects non comptime-known fn - compile_time +// +// tmp.zig:3:5: error: the specified modifier requires a comptime-known function diff --git a/test/compile_errors/stage1/test/cast_between_optional_T_where_T_is_not_a_pointer.zig b/test/compile_errors/stage1/test/cast_between_optional_T_where_T_is_not_a_pointer.zig new file mode 100644 index 0000000000..8f5ca1c25d --- /dev/null +++ b/test/compile_errors/stage1/test/cast_between_optional_T_where_T_is_not_a_pointer.zig @@ -0,0 +1,12 @@ +pub const fnty1 = ?fn (i8) void; +pub const fnty2 = ?fn (u64) void; +export fn entry() void { + var a: fnty1 = undefined; + var b: fnty2 = undefined; + a = b; +} + +// cast between ?T where T is not a pointer +// +// tmp.zig:6:9: error: expected type '?fn(i8) void', found '?fn(u64) void' +// tmp.zig:6:9: note: optional type child 'fn(u64) void' cannot cast into optional type child 'fn(i8) void' diff --git a/test/compile_errors/stage1/test/combination_of_nosuspend_and_async.zig b/test/compile_errors/stage1/test/combination_of_nosuspend_and_async.zig new file mode 100644 index 0000000000..a164fb621b --- /dev/null +++ b/test/compile_errors/stage1/test/combination_of_nosuspend_and_async.zig @@ -0,0 +1,13 @@ +export fn entry() void { + nosuspend { + const bar = async foo(); + suspend {} + resume bar; + } +} +fn foo() void {} + +// combination of nosuspend and async +// +// tmp.zig:4:9: error: suspend inside nosuspend block +// tmp.zig:2:5: note: nosuspend block here diff --git a/test/compile_errors/stage1/test/comparison_of_non-tagged_union_and_enum_literal.zig b/test/compile_errors/stage1/test/comparison_of_non-tagged_union_and_enum_literal.zig new file mode 100644 index 0000000000..b3c1506705 --- /dev/null +++ b/test/compile_errors/stage1/test/comparison_of_non-tagged_union_and_enum_literal.zig @@ -0,0 +1,11 @@ +export fn entry() void { + const U = union { A: u32, B: u64 }; + var u = U{ .A = 42 }; + var ok = u == .A; + _ = ok; +} + +// comparison of non-tagged union and enum literal +// +// tmp.zig:4:16: error: comparison of union and enum literal is only valid for tagged union types +// tmp.zig:2:15: note: type U is not a tagged union diff --git a/test/compile_errors/stage1/test/comptime_vector_overflow_shows_the_index.zig b/test/compile_errors/stage1/test/comptime_vector_overflow_shows_the_index.zig new file mode 100644 index 0000000000..a638c05b3b --- /dev/null +++ b/test/compile_errors/stage1/test/comptime_vector_overflow_shows_the_index.zig @@ -0,0 +1,11 @@ +comptime { + var a: @import("std").meta.Vector(4, u8) = [_]u8{ 1, 2, 255, 4 }; + var b: @import("std").meta.Vector(4, u8) = [_]u8{ 5, 6, 1, 8 }; + var x = a + b; + _ = x; +} + +// comptime vector overflow shows the index +// +// tmp.zig:4:15: error: operation caused overflow +// tmp.zig:4:15: note: when computing vector element at index 2 diff --git a/test/compile_errors/stage1/test/duplicate-unused_labels.zig b/test/compile_errors/stage1/test/duplicate-unused_labels.zig new file mode 100644 index 0000000000..285741afc4 --- /dev/null +++ b/test/compile_errors/stage1/test/duplicate-unused_labels.zig @@ -0,0 +1,30 @@ +comptime { + blk: { blk: while (false) {} } +} +comptime { + blk: while (false) { blk: for (@as([0]void, undefined)) |_| {} } +} +comptime { + blk: for (@as([0]void, undefined)) |_| { blk: {} } +} +comptime { + blk: {} +} +comptime { + blk: while(false) {} +} +comptime { + blk: for(@as([0]void, undefined)) |_| {} +} + +// duplicate/unused labels +// +// tmp.zig:2:12: error: redefinition of label 'blk' +// tmp.zig:2:5: note: previous definition here +// tmp.zig:5:26: error: redefinition of label 'blk' +// tmp.zig:5:5: note: previous definition here +// tmp.zig:8:46: error: redefinition of label 'blk' +// tmp.zig:8:5: note: previous definition here +// tmp.zig:11:5: error: unused block label +// tmp.zig:14:5: error: unused while loop label +// tmp.zig:17:5: error: unused for loop label diff --git a/test/compile_errors/stage1/test/duplicate_field_in_anonymous_struct_literal.zig b/test/compile_errors/stage1/test/duplicate_field_in_anonymous_struct_literal.zig new file mode 100644 index 0000000000..1b671d2ad3 --- /dev/null +++ b/test/compile_errors/stage1/test/duplicate_field_in_anonymous_struct_literal.zig @@ -0,0 +1,16 @@ +export fn entry() void { + const anon = .{ + .inner = .{ + .a = .{ + .something = "text", + }, + .a = .{}, + }, + }; + _ = anon; +} + +// duplicate field in anonymous struct literal +// +// tmp.zig:7:13: error: duplicate field +// tmp.zig:4:13: note: other field here diff --git a/test/compile_errors/stage1/test/error_in_struct_initializer_doesnt_crash_the_compiler.zig b/test/compile_errors/stage1/test/error_in_struct_initializer_doesnt_crash_the_compiler.zig new file mode 100644 index 0000000000..e37b887121 --- /dev/null +++ b/test/compile_errors/stage1/test/error_in_struct_initializer_doesnt_crash_the_compiler.zig @@ -0,0 +1,12 @@ +pub export fn entry() void { + const bitfield = struct { + e: u8, + e: u8, + }; + var a = .{@sizeOf(bitfield)}; + _ = a; +} + +// error in struct initializer doesn't crash the compiler +// +// tmp.zig:4:9: error: duplicate struct field: 'e' diff --git a/test/compile_errors/stage1/test/errors_in_for_loop_bodies_are_propagated.zig b/test/compile_errors/stage1/test/errors_in_for_loop_bodies_are_propagated.zig new file mode 100644 index 0000000000..32e99cf410 --- /dev/null +++ b/test/compile_errors/stage1/test/errors_in_for_loop_bodies_are_propagated.zig @@ -0,0 +1,8 @@ +pub export fn entry() void { + var arr: [100]u8 = undefined; + for (arr) |bits| _ = @popCount(bits); +} + +// errors in for loop bodies are propagated +// +// tmp.zig:3:26: error: expected 2 arguments, found 1 diff --git a/test/compile_errors/stage1/test/export_with_empty_name_string.zig b/test/compile_errors/stage1/test/export_with_empty_name_string.zig new file mode 100644 index 0000000000..424ee6e4b5 --- /dev/null +++ b/test/compile_errors/stage1/test/export_with_empty_name_string.zig @@ -0,0 +1,8 @@ +pub export fn entry() void { } +comptime { + @export(entry, .{ .name = "" }); +} + +// @export with empty name string +// +// tmp.zig:3:5: error: exported symbol name cannot be empty diff --git a/test/compile_errors/stage1/test/helpful_return_type_error_message.zig b/test/compile_errors/stage1/test/helpful_return_type_error_message.zig new file mode 100644 index 0000000000..6f1f9639da --- /dev/null +++ b/test/compile_errors/stage1/test/helpful_return_type_error_message.zig @@ -0,0 +1,27 @@ +export fn foo() u32 { + return error.Ohno; +} +fn bar() !u32 { + return error.Ohno; +} +export fn baz() void { + try bar(); +} +export fn qux() u32 { + return bar(); +} +export fn quux() u32 { + var buf: u32 = 0; + buf = bar(); +} + +// helpful return type error message +// +// tmp.zig:2:17: error: expected type 'u32', found 'error{Ohno}' +// tmp.zig:1:17: note: function cannot return an error +// tmp.zig:8:5: error: expected type 'void', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set' +// tmp.zig:7:17: note: function cannot return an error +// tmp.zig:11:15: error: cannot convert error union to payload type. consider using `try`, `catch`, or `if`. expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set!u32' +// tmp.zig:10:17: note: function cannot return an error +// tmp.zig:15:14: error: cannot convert error union to payload type. consider using `try`, `catch`, or `if`. expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set!u32' +// tmp.zig:14:5: note: cannot store an error in type 'u32' diff --git a/test/compile_errors/stage1/test/int-float_conversion_to_comptime_int-float.zig b/test/compile_errors/stage1/test/int-float_conversion_to_comptime_int-float.zig new file mode 100644 index 0000000000..eeb2e4798e --- /dev/null +++ b/test/compile_errors/stage1/test/int-float_conversion_to_comptime_int-float.zig @@ -0,0 +1,13 @@ +export fn foo() void { + var a: f32 = 2; + _ = @floatToInt(comptime_int, a); +} +export fn bar() void { + var a: u32 = 2; + _ = @intToFloat(comptime_float, a); +} + +// int/float conversion to comptime_int/float +// +// tmp.zig:3:35: error: unable to evaluate constant expression +// tmp.zig:7:37: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/test/invalid_assignments.zig b/test/compile_errors/stage1/test/invalid_assignments.zig new file mode 100644 index 0000000000..203784c554 --- /dev/null +++ b/test/compile_errors/stage1/test/invalid_assignments.zig @@ -0,0 +1,17 @@ +export fn entry1() void { + var a: []const u8 = "foo"; + a[0..2] = "bar"; +} +export fn entry2() void { + var a: u8 = 2; + a + 2 = 3; +} +export fn entry4() void { + 2 + 2 = 3; +} + +// invalid assignments +// +// tmp.zig:3:6: error: invalid left-hand side to assignment +// tmp.zig:7:7: error: invalid left-hand side to assignment +// tmp.zig:10:7: error: invalid left-hand side to assignment diff --git a/test/compile_errors/stage1/test/invalid_float_casts.zig b/test/compile_errors/stage1/test/invalid_float_casts.zig new file mode 100644 index 0000000000..0e2509dcaf --- /dev/null +++ b/test/compile_errors/stage1/test/invalid_float_casts.zig @@ -0,0 +1,23 @@ +export fn foo() void { + var a: f32 = 2; + _ = @floatCast(comptime_float, a); +} +export fn bar() void { + var a: f32 = 2; + _ = @floatToInt(f32, a); +} +export fn baz() void { + var a: f32 = 2; + _ = @intToFloat(f32, a); +} +export fn qux() void { + var a: u32 = 2; + _ = @floatCast(f32, a); +} + +// invalid float casts +// +// tmp.zig:3:36: error: unable to evaluate constant expression +// tmp.zig:7:21: error: expected integer type, found 'f32' +// tmp.zig:11:26: error: expected int type, found 'f32' +// tmp.zig:15:25: error: expected float type, found 'u32' diff --git a/test/compile_errors/stage1/test/invalid_int_casts.zig b/test/compile_errors/stage1/test/invalid_int_casts.zig new file mode 100644 index 0000000000..6870ecc723 --- /dev/null +++ b/test/compile_errors/stage1/test/invalid_int_casts.zig @@ -0,0 +1,23 @@ +export fn foo() void { + var a: u32 = 2; + _ = @intCast(comptime_int, a); +} +export fn bar() void { + var a: u32 = 2; + _ = @intToFloat(u32, a); +} +export fn baz() void { + var a: u32 = 2; + _ = @floatToInt(u32, a); +} +export fn qux() void { + var a: f32 = 2; + _ = @intCast(u32, a); +} + +// invalid int casts +// +// tmp.zig:3:32: error: unable to evaluate constant expression +// tmp.zig:7:21: error: expected float type, found 'u32' +// tmp.zig:11:26: error: expected float type, found 'u32' +// tmp.zig:15:23: error: expected integer type, found 'f32' diff --git a/test/compile_errors/stage1/test/invalid_non-exhaustive_enum_to_union.zig b/test/compile_errors/stage1/test/invalid_non-exhaustive_enum_to_union.zig new file mode 100644 index 0000000000..f78a9e62ff --- /dev/null +++ b/test/compile_errors/stage1/test/invalid_non-exhaustive_enum_to_union.zig @@ -0,0 +1,24 @@ +const E = enum(u8) { + a, + b, + _, +}; +const U = union(E) { + a, + b, +}; +export fn foo() void { + var e = @intToEnum(E, 15); + var u: U = e; + _ = u; +} +export fn bar() void { + const e = @intToEnum(E, 15); + var u: U = e; + _ = u; +} + +// invalid non-exhaustive enum to union +// +// tmp.zig:12:16: error: runtime cast to union 'U' from non-exhaustive enum +// tmp.zig:17:16: error: no tag by value 15 diff --git a/test/compile_errors/stage1/test/invalid_pointer_with_reify_type.zig b/test/compile_errors/stage1/test/invalid_pointer_with_reify_type.zig new file mode 100644 index 0000000000..febf9685fd --- /dev/null +++ b/test/compile_errors/stage1/test/invalid_pointer_with_reify_type.zig @@ -0,0 +1,16 @@ +export fn entry() void { + _ = @Type(.{ .Pointer = .{ + .size = .One, + .is_const = false, + .is_volatile = false, + .alignment = 1, + .address_space = .generic, + .child = u8, + .is_allowzero = false, + .sentinel = &@as(u8, 0), + }}); +} + +// invalid pointer with @Type +// +// tmp.zig:2:16: error: sentinels are only allowed on slices and unknown-length pointers diff --git a/test/compile_errors/stage1/test/nested_vectors.zig b/test/compile_errors/stage1/test/nested_vectors.zig new file mode 100644 index 0000000000..a05e9b0c5b --- /dev/null +++ b/test/compile_errors/stage1/test/nested_vectors.zig @@ -0,0 +1,10 @@ +export fn entry() void { + const V1 = @import("std").meta.Vector(4, u8); + const V2 = @Type(.{ .Vector = .{ .len = 4, .child = V1 } }); + var v: V2 = undefined; + _ = v; +} + +// nested vectors +// +// tmp.zig:3:23: error: vector element type must be integer, float, bool, or pointer; '@Vector(4, u8)' is invalid diff --git a/test/compile_errors/stage1/test/non-exhaustive_enum_marker_assigned_a_value.zig b/test/compile_errors/stage1/test/non-exhaustive_enum_marker_assigned_a_value.zig new file mode 100644 index 0000000000..53037ff181 --- /dev/null +++ b/test/compile_errors/stage1/test/non-exhaustive_enum_marker_assigned_a_value.zig @@ -0,0 +1,17 @@ +const A = enum { + a, + b, + _ = 1, +}; +const B = enum { + a, + b, + _, +}; +comptime { _ = A; _ = B; } + +// non-exhaustive enum marker assigned a value +// +// tmp.zig:4:9: error: '_' is used to mark an enum as non-exhaustive and cannot be assigned a value +// tmp.zig:6:11: error: non-exhaustive enum missing integer tag type +// tmp.zig:9:5: note: marked non-exhaustive here diff --git a/test/compile_errors/stage1/test/non-exhaustive_enums.zig b/test/compile_errors/stage1/test/non-exhaustive_enums.zig new file mode 100644 index 0000000000..49ceff3178 --- /dev/null +++ b/test/compile_errors/stage1/test/non-exhaustive_enums.zig @@ -0,0 +1,19 @@ +const B = enum(u1) { + a, + _, + b, +}; +const C = enum(u1) { + a, + b, + _, +}; +pub export fn entry() void { + _ = B; + _ = C; +} + +// non-exhaustive enums +// +// tmp.zig:3:5: error: '_' field of non-exhaustive enum must be last +// tmp.zig:6:11: error: non-exhaustive enum specifies every value diff --git a/test/compile_errors/stage1/test/not_an_enum_type.zig b/test/compile_errors/stage1/test/not_an_enum_type.zig new file mode 100644 index 0000000000..f92c3d44e1 --- /dev/null +++ b/test/compile_errors/stage1/test/not_an_enum_type.zig @@ -0,0 +1,17 @@ +export fn entry() void { + var self: Error = undefined; + switch (self) { + InvalidToken => |x| return x.token, + ExpectedVarDeclOrFn => |x| return x.token, + } +} +const Error = union(enum) { + A: InvalidToken, + B: ExpectedVarDeclOrFn, +}; +const InvalidToken = struct {}; +const ExpectedVarDeclOrFn = struct {}; + +// not an enum type +// +// tmp.zig:4:9: error: expected type '@typeInfo(Error).Union.tag_type.?', found 'type' diff --git a/test/compile_errors/stage1/test/packed_struct_with_fields_of_not_allowed_types.zig b/test/compile_errors/stage1/test/packed_struct_with_fields_of_not_allowed_types.zig new file mode 100644 index 0000000000..dc156342f4 --- /dev/null +++ b/test/compile_errors/stage1/test/packed_struct_with_fields_of_not_allowed_types.zig @@ -0,0 +1,71 @@ +const A = packed struct { + x: anyerror, +}; +const B = packed struct { + x: [2]u24, +}; +const C = packed struct { + x: [1]anyerror, +}; +const D = packed struct { + x: [1]S, +}; +const E = packed struct { + x: [1]U, +}; +const F = packed struct { + x: ?anyerror, +}; +const G = packed struct { + x: Enum, +}; +export fn entry1() void { + var a: A = undefined; + _ = a; +} +export fn entry2() void { + var b: B = undefined; + _ = b; +} +export fn entry3() void { + var r: C = undefined; + _ = r; +} +export fn entry4() void { + var d: D = undefined; + _ = d; +} +export fn entry5() void { + var e: E = undefined; + _ = e; +} +export fn entry6() void { + var f: F = undefined; + _ = f; +} +export fn entry7() void { + var g: G = undefined; + _ = g; +} +const S = struct { + x: i32, +}; +const U = struct { + A: i32, + B: u32, +}; +const Enum = enum { + A, + B, +}; + +// packed struct with fields of not allowed types +// +// tmp.zig:2:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation +// tmp.zig:5:5: error: array of 'u24' not allowed in packed struct due to padding bits (must be padded from 48 to 64 bits) +// tmp.zig:8:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation +// tmp.zig:11:5: error: non-packed, non-extern struct 'S' not allowed in packed struct; no guaranteed in-memory representation +// tmp.zig:14:5: error: non-packed, non-extern struct 'U' not allowed in packed struct; no guaranteed in-memory representation +// tmp.zig:17:5: error: type '?anyerror' not allowed in packed struct; no guaranteed in-memory representation +// tmp.zig:20:5: error: type 'Enum' not allowed in packed struct; no guaranteed in-memory representation +// tmp.zig:57:14: note: enum declaration does not specify an integer tag type diff --git a/test/compile_errors/stage1/test/ptrToInt_with_pointer_to_zero-sized_type.zig b/test/compile_errors/stage1/test/ptrToInt_with_pointer_to_zero-sized_type.zig new file mode 100644 index 0000000000..63ba217247 --- /dev/null +++ b/test/compile_errors/stage1/test/ptrToInt_with_pointer_to_zero-sized_type.zig @@ -0,0 +1,9 @@ +export fn entry() void { + var pointer: ?*u0 = null; + var x = @ptrToInt(pointer); + _ = x; +} + +// @ptrToInt with pointer to zero-sized type +// +// tmp.zig:3:23: error: pointer to size 0 type has no address diff --git a/test/compile_errors/stage1/test/reassign_to_array_parameter.zig b/test/compile_errors/stage1/test/reassign_to_array_parameter.zig new file mode 100644 index 0000000000..a222150a2c --- /dev/null +++ b/test/compile_errors/stage1/test/reassign_to_array_parameter.zig @@ -0,0 +1,10 @@ +fn reassign(a: [3]f32) void { + a = [3]f32{4, 5, 6}; +} +export fn entry() void { + reassign(.{1, 2, 3}); +} + +// reassign to array parameter +// +// tmp.zig:2:15: error: cannot assign to constant diff --git a/test/compile_errors/stage1/test/reassign_to_slice_parameter.zig b/test/compile_errors/stage1/test/reassign_to_slice_parameter.zig new file mode 100644 index 0000000000..a8e555182a --- /dev/null +++ b/test/compile_errors/stage1/test/reassign_to_slice_parameter.zig @@ -0,0 +1,10 @@ +pub fn reassign(s: []const u8) void { + s = s[0..]; +} +export fn entry() void { + reassign("foo"); +} + +// reassign to slice parameter +// +// tmp.zig:2:10: error: cannot assign to constant diff --git a/test/compile_errors/stage1/test/reassign_to_struct_parameter.zig b/test/compile_errors/stage1/test/reassign_to_struct_parameter.zig new file mode 100644 index 0000000000..018548ab32 --- /dev/null +++ b/test/compile_errors/stage1/test/reassign_to_struct_parameter.zig @@ -0,0 +1,13 @@ +const S = struct { + x: u32, +}; +fn reassign(s: S) void { + s = S{.x = 2}; +} +export fn entry() void { + reassign(S{.x = 3}); +} + +// reassign to struct parameter +// +// tmp.zig:5:10: error: cannot assign to constant diff --git a/test/compile_errors/stage1/test/reference_to_const_data.zig b/test/compile_errors/stage1/test/reference_to_const_data.zig new file mode 100644 index 0000000000..d785eb648e --- /dev/null +++ b/test/compile_errors/stage1/test/reference_to_const_data.zig @@ -0,0 +1,27 @@ +export fn foo() void { + var ptr = &[_]u8{0,0,0,0}; + ptr[1] = 2; +} +export fn bar() void { + var ptr = &@as(u32, 2); + ptr.* = 2; +} +export fn baz() void { + var ptr = &true; + ptr.* = false; +} +export fn qux() void { + const S = struct{ + x: usize, + y: usize, + }; + var ptr = &S{.x=1,.y=2}; + ptr.x = 2; +} + +// reference to const data +// +// tmp.zig:3:14: error: cannot assign to constant +// tmp.zig:7:13: error: cannot assign to constant +// tmp.zig:11:13: error: cannot assign to constant +// tmp.zig:19:13: error: cannot assign to constant diff --git a/test/compile_errors/stage1/test/reify_typeOf_with_incompatible_arguments.zig b/test/compile_errors/stage1/test/reify_typeOf_with_incompatible_arguments.zig new file mode 100644 index 0000000000..97ee7aaf1b --- /dev/null +++ b/test/compile_errors/stage1/test/reify_typeOf_with_incompatible_arguments.zig @@ -0,0 +1,9 @@ +export fn entry() void { + var var_1: f32 = undefined; + var var_2: u32 = undefined; + _ = @TypeOf(var_1, var_2); +} + +// @TypeOf with incompatible arguments +// +// tmp.zig:4:9: error: incompatible types: 'f32' and 'u32' diff --git a/test/compile_errors/stage1/test/reify_typeOf_with_no_arguments.zig b/test/compile_errors/stage1/test/reify_typeOf_with_no_arguments.zig new file mode 100644 index 0000000000..e18b9e66b7 --- /dev/null +++ b/test/compile_errors/stage1/test/reify_typeOf_with_no_arguments.zig @@ -0,0 +1,7 @@ +export fn entry() void { + _ = @TypeOf(); +} + +// @TypeOf with no arguments +// +// tmp.zig:2:9: error: expected at least 1 argument, found 0 diff --git a/test/compile_errors/stage1/test/reject_extern_function_definitions_with_body.zig b/test/compile_errors/stage1/test/reject_extern_function_definitions_with_body.zig new file mode 100644 index 0000000000..024080a6bc --- /dev/null +++ b/test/compile_errors/stage1/test/reject_extern_function_definitions_with_body.zig @@ -0,0 +1,7 @@ +extern "c" fn definitelyNotInLibC(a: i32, b: i32) i32 { + return a + b; +} + +// reject extern function definitions with body +// +// tmp.zig:1:1: error: extern functions have no body diff --git a/test/compile_errors/stage1/test/reject_extern_variables_with_initializers.zig b/test/compile_errors/stage1/test/reject_extern_variables_with_initializers.zig new file mode 100644 index 0000000000..d3f65ff0cb --- /dev/null +++ b/test/compile_errors/stage1/test/reject_extern_variables_with_initializers.zig @@ -0,0 +1,5 @@ +extern var foo: int = 2; + +// reject extern variables with initializers +// +// tmp.zig:1:23: error: extern variables have no initializers diff --git a/test/compile_errors/stage1/test/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig b/test/compile_errors/stage1/test/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig new file mode 100644 index 0000000000..c419a6ae83 --- /dev/null +++ b/test/compile_errors/stage1/test/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig @@ -0,0 +1,11 @@ +pub fn A() type { + return Q; +} +test "1" { + _ = A().a; + _ = A().a; +} + +// repeated invalid field access to generic function returning type crashes compiler. #2655 +// +// tmp.zig:2:12: error: use of undeclared identifier 'Q' diff --git a/test/compile_errors/stage1/test/return_invalid_type_from_test.zig b/test/compile_errors/stage1/test/return_invalid_type_from_test.zig new file mode 100644 index 0000000000..e7aeab3be7 --- /dev/null +++ b/test/compile_errors/stage1/test/return_invalid_type_from_test.zig @@ -0,0 +1,5 @@ +test "example" { return 1; } + +// return invalid type from test +// +// tmp.zig:1:25: error: expected type 'void', found 'comptime_int' diff --git a/test/compile_errors/stage1/test/shift_on_type_with_non-power-of-two_size.zig b/test/compile_errors/stage1/test/shift_on_type_with_non-power-of-two_size.zig new file mode 100644 index 0000000000..d76406223d --- /dev/null +++ b/test/compile_errors/stage1/test/shift_on_type_with_non-power-of-two_size.zig @@ -0,0 +1,31 @@ +export fn entry() void { + const S = struct { + fn a() void { + var x: u24 = 42; + _ = x >> 24; + } + fn b() void { + var x: u24 = 42; + _ = x << 24; + } + fn c() void { + var x: u24 = 42; + _ = @shlExact(x, 24); + } + fn d() void { + var x: u24 = 42; + _ = @shrExact(x, 24); + } + }; + S.a(); + S.b(); + S.c(); + S.d(); +} + +// shift on type with non-power-of-two size +// +// tmp.zig:5:19: error: RHS of shift is too large for LHS type +// tmp.zig:9:19: error: RHS of shift is too large for LHS type +// tmp.zig:13:17: error: RHS of shift is too large for LHS type +// tmp.zig:17:17: error: RHS of shift is too large for LHS type diff --git a/test/compile_errors/stage1/test/shuffle_with_selected_index_past_first_vector_length.zig b/test/compile_errors/stage1/test/shuffle_with_selected_index_past_first_vector_length.zig new file mode 100644 index 0000000000..c45eb4a0e2 --- /dev/null +++ b/test/compile_errors/stage1/test/shuffle_with_selected_index_past_first_vector_length.zig @@ -0,0 +1,12 @@ +export fn entry() void { + const v: @import("std").meta.Vector(4, u32) = [4]u32{ 10, 11, 12, 13 }; + const x: @import("std").meta.Vector(4, u32) = [4]u32{ 14, 15, 16, 17 }; + var z = @shuffle(u32, v, x, [8]i32{ 0, 1, 2, 3, 7, 6, 5, 4 }); + _ = z; +} + +// @shuffle with selected index past first vector length +// +// tmp.zig:4:39: error: mask index '4' has out-of-bounds selection +// tmp.zig:4:27: note: selected index '7' out of bounds of @Vector(4, u32) +// tmp.zig:4:30: note: selections from the second vector are specified with negative numbers diff --git a/test/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig b/test/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig new file mode 100644 index 0000000000..4c0545ea92 --- /dev/null +++ b/test/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig @@ -0,0 +1,13 @@ +pub export fn entry() void { + var x: i32 = 0; + switch (x) { + 6...1 => {}, + -1...-5 => {}, + else => unreachable, + } +} + +// switch ranges endpoints are validated +// +// tmp.zig:4:9: error: range start value is greater than the end value +// tmp.zig:5:9: error: range start value is greater than the end value diff --git a/test/compile_errors/stage1/test/switching_with_exhaustive_enum_has___prong_.zig b/test/compile_errors/stage1/test/switching_with_exhaustive_enum_has___prong_.zig new file mode 100644 index 0000000000..b132b7834e --- /dev/null +++ b/test/compile_errors/stage1/test/switching_with_exhaustive_enum_has___prong_.zig @@ -0,0 +1,16 @@ +const E = enum{ + a, + b, +}; +pub export fn entry() void { + var e: E = .b; + switch (e) { + .a => {}, + .b => {}, + _ => {}, + } +} + +// switching with exhaustive enum has '_' prong +// +// tmp.zig:7:5: error: switch on exhaustive enum has `_` prong diff --git a/test/compile_errors/stage1/test/switching_with_non-exhaustive_enums.zig b/test/compile_errors/stage1/test/switching_with_non-exhaustive_enums.zig new file mode 100644 index 0000000000..53cd88e68c --- /dev/null +++ b/test/compile_errors/stage1/test/switching_with_non-exhaustive_enums.zig @@ -0,0 +1,32 @@ +const E = enum(u8) { + a, + b, + _, +}; +const U = union(E) { + a: i32, + b: u32, +}; +pub export fn entry() void { + var e: E = .b; + switch (e) { // error: switch not handling the tag `b` + .a => {}, + _ => {}, + } + switch (e) { // error: switch on non-exhaustive enum must include `else` or `_` prong + .a => {}, + .b => {}, + } + var u = U{.a = 2}; + switch (u) { // error: `_` prong not allowed when switching on tagged union + .a => {}, + .b => {}, + _ => {}, + } +} + +// switching with non-exhaustive enums +// +// tmp.zig:12:5: error: enumeration value 'E.b' not handled in switch +// tmp.zig:16:5: error: switch on non-exhaustive enum must include `else` or `_` prong +// tmp.zig:21:5: error: `_` prong not allowed when switching on tagged union diff --git a/test/compile_errors/stage1/test/tagName_on_invalid_value_of_non-exhaustive_enum.zig b/test/compile_errors/stage1/test/tagName_on_invalid_value_of_non-exhaustive_enum.zig new file mode 100644 index 0000000000..139c7c7a23 --- /dev/null +++ b/test/compile_errors/stage1/test/tagName_on_invalid_value_of_non-exhaustive_enum.zig @@ -0,0 +1,8 @@ +test "enum" { + const E = enum(u8) {A, B, _}; + _ = @tagName(@intToEnum(E, 5)); +} + +// @tagName on invalid value of non-exhaustive enum +// +// tmp.zig:3:18: error: no tag by value 5 diff --git a/test/compile_errors/stage1/test/type_mismatch_in_C_prototype_with_varargs.zig b/test/compile_errors/stage1/test/type_mismatch_in_C_prototype_with_varargs.zig new file mode 100644 index 0000000000..437cca8772 --- /dev/null +++ b/test/compile_errors/stage1/test/type_mismatch_in_C_prototype_with_varargs.zig @@ -0,0 +1,11 @@ +const fn_ty = ?fn ([*c]u8, ...) callconv(.C) void; +extern fn fn_decl(fmt: [*:0]u8, ...) void; + +export fn main() void { + const x: fn_ty = fn_decl; + _ = x; +} + +// type mismatch in C prototype with varargs +// +// tmp.zig:5:22: error: expected type 'fn([*c]u8, ...) callconv(.C) void', found 'fn([*:0]u8, ...) callconv(.C) void' diff --git a/test/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig b/test/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig new file mode 100644 index 0000000000..ce5e4d4629 --- /dev/null +++ b/test/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig @@ -0,0 +1,8 @@ +export fn entry() void { + var x = .{}; + x = x ++ .{ 1, 2, 3 }; +} + +// type mismatch with tuple concatenation +// +// tmp.zig:3:11: error: expected type 'struct:2:14', found 'struct:3:11' diff --git a/test/compile_errors/stage1/test/unused_variable_error_on_errdefer.zig b/test/compile_errors/stage1/test/unused_variable_error_on_errdefer.zig new file mode 100644 index 0000000000..eb92776938 --- /dev/null +++ b/test/compile_errors/stage1/test/unused_variable_error_on_errdefer.zig @@ -0,0 +1,11 @@ +fn foo() !void { + errdefer |a| unreachable; + return error.A; +} +export fn entry() void { + foo() catch unreachable; +} + +// unused variable error on errdefer +// +// tmp.zig:2:15: error: unused variable: 'a' diff --git a/test/compile_errors/stage2/embed_outside_package.zig b/test/compile_errors/stage2/embed_outside_package.zig new file mode 100644 index 0000000000..8df6b3d9af --- /dev/null +++ b/test/compile_errors/stage2/embed_outside_package.zig @@ -0,0 +1,7 @@ +export fn a() usize { + return @embedFile("/root/foo").len; +} + +// embed outside package +// +//:2:23: error: embed of file outside package path: '/root/foo' diff --git a/test/compile_errors/stage2/import_outside_package.zig b/test/compile_errors/stage2/import_outside_package.zig new file mode 100644 index 0000000000..f9de9202de --- /dev/null +++ b/test/compile_errors/stage2/import_outside_package.zig @@ -0,0 +1,7 @@ +export fn a() usize { + return @import("../../above.zig").len; +} + +// import outside package +// +// :2:20: error: import of file outside package path: '../../above.zig' diff --git a/test/compile_errors/stage2/out_of_bounds_index.zig b/test/compile_errors/stage2/out_of_bounds_index.zig new file mode 100644 index 0000000000..3c34bb5d0f --- /dev/null +++ b/test/compile_errors/stage2/out_of_bounds_index.zig @@ -0,0 +1,28 @@ +comptime { + var array = [_:0]u8{ 1, 2, 3, 4 }; + var src_slice: [:0]u8 = &array; + var slice = src_slice[2..6]; + _ = slice; +} +comptime { + var array = [_:0]u8{ 1, 2, 3, 4 }; + var slice = array[2..6]; + _ = slice; +} +comptime { + var array = [_]u8{ 1, 2, 3, 4 }; + var slice = array[2..5]; + _ = slice; +} +comptime { + var array = [_:0]u8{ 1, 2, 3, 4 }; + var slice = array[3..2]; + _ = slice; +} + +// out of bounds indexing +// +// :4:26: error: end index 6 out of bounds for slice of length 4 +1 (sentinel) +// :9:22: error: end index 6 out of bounds for array of length 4 +1 (sentinel) +// :14:22: error: end index 5 out of bounds for array of length 4 +// :19:22: error: start index 3 is larger than end index 2 From a4c5ec49f1781054ef9394fe114e4338ed9970d4 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 25 Mar 2022 11:51:40 +0200 Subject: [PATCH 0926/2031] Sema: add error for empty switch --- src/Sema.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index a42a4caf38..d9eea3e811 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7743,6 +7743,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError } if (scalar_cases_len + multi_cases_len == 0) { + if (special_prong == .none) { + return sema.fail(block, src, "switch must handle all possibilities", .{}); + } return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges); } From f6bd534fc9ccbb45d6dfbcaf51eccd2967856661 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 25 Mar 2022 11:57:19 +0200 Subject: [PATCH 0927/2031] Sema: ensure error_set_merged is sorted --- src/Sema.zig | 4 ++++ test/behavior/type.zig | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index d9eea3e811..2e7bd06ada 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7390,6 +7390,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError names.putAssumeCapacityNoClobber(error_name, {}); } + // names must be sorted + Module.ErrorSet.sortNames(&names); else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names); } }, @@ -12979,6 +12981,8 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I ); } + // names must be sorted + Module.ErrorSet.sortNames(&names); const ty = try Type.Tag.error_set_merged.create(sema.arena, names); return sema.addType(ty); }, diff --git a/test/behavior/type.zig b/test/behavior/type.zig index e3c896d0f7..65bebff946 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -260,6 +260,13 @@ test "Type.ErrorSet" { .{ .name = "C" }, }, }); + _ = @Type(.{ + .ErrorSet = &.{ + .{ .name = "C" }, + .{ .name = "B" }, + .{ .name = "A" }, + }, + }); } test "Type.Struct" { From 26dfbf8122618de865e847bed18554f6b023198a Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 25 Mar 2022 12:13:06 +0200 Subject: [PATCH 0928/2031] type: fix onePossibleValue for auto numbered enums --- src/type.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/type.zig b/src/type.zig index d5b8e6f5b3..17c8d4d111 100644 --- a/src/type.zig +++ b/src/type.zig @@ -4511,7 +4511,11 @@ pub const Type = extern union { .enum_full => { const enum_full = ty.castTag(.enum_full).?.data; if (enum_full.fields.count() == 1) { - return enum_full.values.keys()[0]; + if (enum_full.values.count() == 0) { + return Value.zero; + } else { + return enum_full.values.keys()[0]; + } } else { return null; } From 5ff518fbb9ea2fb5a745841731912acbe2f046d9 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 25 Mar 2022 13:21:22 +0200 Subject: [PATCH 0929/2031] Sema: implement zirSwitchCapture for error sets --- src/Sema.zig | 32 ++++++++++++++++++++++++--- src/Zir.zig | 47 ++++++++++++++++++++++++++++++++++++++++ test/behavior/switch.zig | 5 ++++- 3 files changed, 80 insertions(+), 4 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 2e7bd06ada..1896ee40df 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6938,7 +6938,6 @@ fn zirSwitchCapture( const switch_info = zir_datas[capture_info.switch_inst].pl_node; const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index); const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_info.src_node }; - const switch_src = switch_info.src(); const operand_is_ref = switch_extra.data.bits.is_ref; const cond_inst = Zir.refToIndex(switch_extra.data.operand).?; const cond_info = sema.code.instructions.items(.data)[cond_inst].un_node; @@ -6965,7 +6964,29 @@ fn zirSwitchCapture( } if (is_multi) { - return sema.fail(block, switch_src, "TODO implement Sema for switch capture multi", .{}); + const items = switch_extra.data.getMultiProng(sema.code, switch_extra.end, capture_info.prong_index).items; + + var names: Module.ErrorSet.NameMap = .{}; + try names.ensureUnusedCapacity(sema.arena, items.len); + for (items) |item| { + const item_ref = sema.resolveInst(item); + // Previous switch validation ensured this will succeed + const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable; + names.putAssumeCapacityNoClobber( + item_val.getError().?, + {}, + ); + } + + // names must be sorted + Module.ErrorSet.sortNames(&names); + const else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names); + + const operand = if (operand_is_ref) + try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src) + else + operand_ptr; + return sema.bitCast(block, else_error_ty, operand, operand_src); } const scalar_prong = switch_extra.data.getScalarProng(sema.code, switch_extra.end, capture_info.prong_index); const item = sema.resolveInst(scalar_prong.item); @@ -7022,7 +7043,12 @@ fn zirSwitchCapture( return block.addStructFieldVal(operand, field_index, field.ty); }, .ErrorSet => { - return sema.fail(block, operand_src, "TODO implement Sema for zirSwitchCapture for error sets", .{}); + const item_ty = try Type.Tag.error_set_single.create(sema.arena, item_val.getError().?); + const operand = if (operand_is_ref) + try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src) + else + operand_ptr; + return sema.bitCast(block, item_ty, operand, operand_src); }, else => { return sema.fail(block, operand_src, "switch on type '{}' provides no capture value", .{ diff --git a/src/Zir.zig b/src/Zir.zig index f9b80c88ef..d6e8486ea7 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -2620,6 +2620,53 @@ pub const Inst = struct { }; } } + + pub const MultiProng = struct { + items: []const Ref, + body: []const Index, + }; + + pub fn getMultiProng( + self: SwitchBlock, + zir: Zir, + extra_end: usize, + prong_index: usize, + ) MultiProng { + // +1 for self.bits.has_multi_cases == true + var extra_index: usize = extra_end + 1; + + if (self.bits.specialProng() != .none) { + const body_len = zir.extra[extra_index]; + extra_index += 1; + const body = zir.extra[extra_index..][0..body_len]; + extra_index += body.len; + } + + var scalar_i: usize = 0; + while (scalar_i < self.bits.scalar_cases_len) : (scalar_i += 1) { + extra_index += 1; + const body_len = zir.extra[extra_index]; + extra_index += 1; + extra_index += body_len; + } + var multi_i: u32 = 0; + while (true) : (multi_i += 1) { + const items_len = zir.extra[extra_index]; + extra_index += 2; + const body_len = zir.extra[extra_index]; + extra_index += 1; + const items = zir.refSlice(extra_index, items_len); + extra_index += items_len; + const body = zir.extra[extra_index..][0..body_len]; + extra_index += body_len; + + if (multi_i < prong_index) continue; + return .{ + .items = items, + .body = body, + }; + } + } }; pub const Field = struct { diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index db670e34a7..9634a4de37 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -465,7 +465,10 @@ test "else prong of switch on error set excludes other cases" { } test "switch prongs with error set cases make a new error set type for capture value" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { From 17d214a249e8d28aa01bc89325ba57918d5bb525 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 25 Mar 2022 13:27:03 +0200 Subject: [PATCH 0930/2031] Sema: implement zirStructInit for runtime-known union values --- src/Sema.zig | 4 +++- test/behavior/union.zig | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 1896ee40df..c8e12d21cc 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12264,7 +12264,9 @@ fn zirStructInit( return alloc; } - return sema.fail(block, src, "TODO: Sema.zirStructInit for runtime-known union values", .{}); + try sema.requireRuntimeBlock(block, src); + try sema.queueFullTypeResolution(resolved_ty); + return block.addUnionInit(resolved_ty, field_index, init_inst); } unreachable; } diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 0541817145..532d4f79eb 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1132,3 +1132,20 @@ test "global variable struct contains union initialized to non-most-aligned fiel T.s.u.a += 1; try expect(T.s.u.a == 4); } + +test "union with no result loc initiated with a runtime value" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const U = union { + a: u32, + b: u32, + fn foo(u: @This()) void { + _ = u; + } + }; + var a: u32 = 1; + U.foo(U{ .a = a }); +} From 2f326f24dd812281e321ec03aee3a8150201d389 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 25 Mar 2022 13:48:11 +0200 Subject: [PATCH 0931/2031] Sema: implement zirSwitchCapture multi for unions --- src/Sema.zig | 123 ++++++++++++++++++++++----------------- test/behavior/switch.zig | 3 +- 2 files changed, 70 insertions(+), 56 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index c8e12d21cc..e1a8d6f09a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6938,12 +6938,19 @@ fn zirSwitchCapture( const switch_info = zir_datas[capture_info.switch_inst].pl_node; const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index); const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_info.src_node }; + const switch_src = switch_info.src(); const operand_is_ref = switch_extra.data.bits.is_ref; const cond_inst = Zir.refToIndex(switch_extra.data.operand).?; const cond_info = sema.code.instructions.items(.data)[cond_inst].un_node; const operand_ptr = sema.resolveInst(cond_info.operand); const operand_ptr_ty = sema.typeOf(operand_ptr); const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty; + const target = sema.mod.getTarget(); + + const operand = if (operand_is_ref) + try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src) + else + operand_ptr; if (capture_info.prong_index == std.math.maxInt(@TypeOf(capture_info.prong_index))) { // It is the else/`_` prong. @@ -6952,64 +6959,57 @@ fn zirSwitchCapture( return operand_ptr; } - const operand = if (operand_is_ref) - try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src) - else - operand_ptr; - switch (operand_ty.zigTypeTag()) { .ErrorSet => return sema.bitCast(block, block.switch_else_err_ty.?, operand, operand_src), else => return operand, } } - if (is_multi) { - const items = switch_extra.data.getMultiProng(sema.code, switch_extra.end, capture_info.prong_index).items; - - var names: Module.ErrorSet.NameMap = .{}; - try names.ensureUnusedCapacity(sema.arena, items.len); - for (items) |item| { - const item_ref = sema.resolveInst(item); - // Previous switch validation ensured this will succeed - const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable; - names.putAssumeCapacityNoClobber( - item_val.getError().?, - {}, - ); - } - - // names must be sorted - Module.ErrorSet.sortNames(&names); - const else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names); - - const operand = if (operand_is_ref) - try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src) - else - operand_ptr; - return sema.bitCast(block, else_error_ty, operand, operand_src); - } - const scalar_prong = switch_extra.data.getScalarProng(sema.code, switch_extra.end, capture_info.prong_index); - const item = sema.resolveInst(scalar_prong.item); - // Previous switch validation ensured this will succeed - const item_val = sema.resolveConstValue(block, .unneeded, item) catch unreachable; - const target = sema.mod.getTarget(); + const items = if (is_multi) + switch_extra.data.getMultiProng(sema.code, switch_extra.end, capture_info.prong_index).items + else + &[_]Zir.Inst.Ref{ + switch_extra.data.getScalarProng(sema.code, switch_extra.end, capture_info.prong_index).item, + }; switch (operand_ty.zigTypeTag()) { .Union => { const union_obj = operand_ty.cast(Type.Payload.Union).?.data; const enum_ty = union_obj.tag_ty; - const field_index_usize = enum_ty.enumTagFieldIndex(item_val, target).?; - const field_index = @intCast(u32, field_index_usize); - const field = union_obj.fields.values()[field_index]; + const first_item = sema.resolveInst(items[0]); + // Previous switch validation ensured this will succeed + const first_item_val = sema.resolveConstValue(block, .unneeded, first_item) catch unreachable; - // TODO handle multiple union tags which have compatible types + const first_field_index = @intCast(u32, enum_ty.enumTagFieldIndex(first_item_val, target).?); + const first_field = union_obj.fields.values()[first_field_index]; + + for (items[1..]) |item| { + const item_ref = sema.resolveInst(item); + // Previous switch validation ensured this will succeed + const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable; + + const field_index = enum_ty.enumTagFieldIndex(item_val, target).?; + const field = union_obj.fields.values()[field_index]; + if (!field.ty.eql(first_field.ty, target)) { + const first_item_src = switch_src; // TODO better source location + const item_src = switch_src; + const msg = msg: { + const msg = try sema.errMsg(block, switch_src, "capture group with incompatible types", .{}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(block, first_item_src, msg, "type '{}' here", .{first_field.ty.fmt(target)}); + try sema.errNote(block, item_src, msg, "type '{}' here", .{field.ty.fmt(target)}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + } if (is_ref) { assert(operand_is_ref); const field_ty_ptr = try Type.ptr(sema.arena, target, .{ - .pointee_type = field.ty, + .pointee_type = first_field.ty, .@"addrspace" = .generic, .mutable = operand_ptr_ty.ptrIsMutable(), }); @@ -7020,35 +7020,48 @@ fn zirSwitchCapture( try Value.Tag.field_ptr.create(sema.arena, .{ .container_ptr = op_ptr_val, .container_ty = operand_ty, - .field_index = field_index, + .field_index = first_field_index, }), ); } try sema.requireRuntimeBlock(block, operand_src); - return block.addStructFieldPtr(operand_ptr, field_index, field_ty_ptr); + return block.addStructFieldPtr(operand_ptr, first_field_index, field_ty_ptr); } - const operand = if (operand_is_ref) - try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src) - else - operand_ptr; - if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| { return sema.addConstant( - field.ty, + first_field.ty, operand_val.castTag(.@"union").?.data.val, ); } try sema.requireRuntimeBlock(block, operand_src); - return block.addStructFieldVal(operand, field_index, field.ty); + return block.addStructFieldVal(operand, first_field_index, first_field.ty); }, .ErrorSet => { - const item_ty = try Type.Tag.error_set_single.create(sema.arena, item_val.getError().?); - const operand = if (operand_is_ref) - try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src) - else - operand_ptr; - return sema.bitCast(block, item_ty, operand, operand_src); + if (is_multi) { + var names: Module.ErrorSet.NameMap = .{}; + try names.ensureUnusedCapacity(sema.arena, items.len); + for (items) |item| { + const item_ref = sema.resolveInst(item); + // Previous switch validation ensured this will succeed + const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable; + names.putAssumeCapacityNoClobber( + item_val.getError().?, + {}, + ); + } + // names must be sorted + Module.ErrorSet.sortNames(&names); + const else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names); + + return sema.bitCast(block, else_error_ty, operand, operand_src); + } else { + // Previous switch validation ensured this will succeed + const item_val = sema.resolveConstValue(block, .unneeded, items[0]) catch unreachable; + + const item_ty = try Type.Tag.error_set_single.create(sema.arena, item_val.getError().?); + return sema.bitCast(block, item_ty, operand, operand_src); + } }, else => { return sema.fail(block, operand_src, "switch on type '{}' provides no capture value", .{ diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index 9634a4de37..b988f32a38 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -541,7 +541,8 @@ test "switch with null and T peer types and inferred result location type" { } test "switch prongs with cases with identical payload types" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const Union = union(enum) { A: usize, From a7b3082ba0d07c66d2ec19304ffe993d77f714b7 Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sat, 12 Feb 2022 16:57:52 +0100 Subject: [PATCH 0932/2031] Implement `type.bitSize` for unions --- src/type.zig | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/type.zig b/src/type.zig index d5b8e6f5b3..6b7214888d 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3251,8 +3251,20 @@ pub const Type = extern union { const int_tag_ty = ty.intTagType(&buffer); return int_tag_ty.bitSize(target); }, + .@"union", .union_tagged => { - @panic("TODO bitSize unions"); + const union_obj = ty.cast(Payload.Union).?.data; + + const fields = union_obj.fields; + if (fields.count() == 0) return 0; + + assert(union_obj.haveFieldTypes()); + + var size: u64 = 0; + for (fields.values()) |field| { + size = @maximum(size, field.ty.bitSize(target)); + } + return size; }, .vector => { From dc5dc3ac59bb0f013a867b278b994b76df655480 Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sat, 12 Feb 2022 16:57:58 +0100 Subject: [PATCH 0933/2031] stage2: add type checking for @bitCast --- src/Sema.zig | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index a42a4caf38..63d7eca23b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6765,8 +6765,71 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; + const target = sema.mod.getTarget(); const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); + switch (dest_ty.zigTypeTag()) { + .Type, + .Void, + .NoReturn, + .ComptimeFloat, + .ComptimeInt, + .Undefined, + .Null, + .Optional, + .ErrorUnion, + .ErrorSet, + .Opaque, + .Frame, + .AnyFrame, + .EnumLiteral, + .Union, + .Fn, + => return sema.fail(block, dest_ty_src, "invalid type '{}' for @bitCast", .{dest_ty.fmt(target)}), + + .Pointer => { + const msg = msg: { + const msg = try sema.errMsg(block, dest_ty_src, "cannot @bitCast to pointer type '{}'", .{dest_ty.fmt(target)}); + errdefer msg.destroy(sema.gpa); + + const pointee_ty = dest_ty.ptrInfo().data.pointee_type; + try sema.errNote(block, dest_ty_src, msg, "to cast to a pointer type, use @ptrCast({}, ...)", .{dest_ty.fmt(target)}); + try sema.errNote(block, dest_ty_src, msg, "to cast to a non-pointer type, use @bitCast({}, ...)", .{pointee_ty.fmt(target)}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + }, + .Struct => { + if (dest_ty.containerLayout() == .Auto) { + const msg = msg: { + const msg = try sema.errMsg( + block, + dest_ty_src, + "cannot @bitCast to '{}', struct does not have a specified layout", + .{dest_ty.fmt(target)}, + ); + errdefer msg.destroy(sema.gpa); + + const ty_decl_src = dest_ty.declSrcLoc(); + try sema.mod.errNoteNonLazy( + ty_decl_src, + msg, + "consider using 'packed struct' or 'extern struct' for a specified layout.", + .{}, + ); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + }, + .BoundFn => @panic("TODO remove this type from the language and compiler"), + else => {}, + } + + // When bitcasting we compare the bit size of the types, so we need to + // fully resolve all composite types to finalize the layout. + try sema.resolveTypeFully(block, dest_ty_src, dest_ty); + const operand = sema.resolveInst(extra.rhs); return sema.bitCast(block, dest_ty, operand, operand_src); } @@ -18995,7 +19058,19 @@ fn bitCast( const old_ty = try sema.resolveTypeFields(block, inst_src, sema.typeOf(inst)); try sema.resolveTypeLayout(block, inst_src, old_ty); - // TODO validate the type size and other compile errors + const target = sema.mod.getTarget(); + var dest_bits = dest_ty.bitSize(target); + var old_bits = old_ty.bitSize(target); + + if (old_bits != dest_bits) { + return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{}' has {d} bits but source type '{}' has {d} bits", .{ + dest_ty.fmt(target), + dest_bits, + old_ty.fmt(target), + old_bits, + }); + } + if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| { const result_val = try sema.bitCastVal(block, inst_src, val, old_ty, dest_ty, 0); return sema.addConstant(dest_ty, result_val); From 607300a59bfa24fae19b85029065c4a3cc136692 Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sat, 26 Mar 2022 00:57:17 +0100 Subject: [PATCH 0934/2031] sema: simplify @bitCast error messages --- src/Sema.zig | 80 ++++++++++++++++++++-------------------------------- 1 file changed, 30 insertions(+), 50 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 63d7eca23b..a566a2b56c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6769,66 +6769,46 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); switch (dest_ty.zigTypeTag()) { - .Type, - .Void, - .NoReturn, + .AnyFrame, .ComptimeFloat, .ComptimeInt, - .Undefined, - .Null, - .Optional, - .ErrorUnion, - .ErrorSet, - .Opaque, - .Frame, - .AnyFrame, + .Enum, .EnumLiteral, - .Union, + .ErrorSet, + .ErrorUnion, .Fn, + .Frame, + .NoReturn, + .Null, + .Opaque, + .Optional, + .Type, + .Undefined, + .Void, => return sema.fail(block, dest_ty_src, "invalid type '{}' for @bitCast", .{dest_ty.fmt(target)}), - .Pointer => { - const msg = msg: { - const msg = try sema.errMsg(block, dest_ty_src, "cannot @bitCast to pointer type '{}'", .{dest_ty.fmt(target)}); - errdefer msg.destroy(sema.gpa); - - const pointee_ty = dest_ty.ptrInfo().data.pointee_type; - try sema.errNote(block, dest_ty_src, msg, "to cast to a pointer type, use @ptrCast({}, ...)", .{dest_ty.fmt(target)}); - try sema.errNote(block, dest_ty_src, msg, "to cast to a non-pointer type, use @bitCast({}, ...)", .{pointee_ty.fmt(target)}); - break :msg msg; + .Pointer => return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}', use @ptrCast to cast to a pointer", .{ + dest_ty.fmt(target), + }), + .Struct, .Union => if (dest_ty.containerLayout() == .Auto) { + const container = switch (dest_ty.zigTypeTag()) { + .Struct => "struct", + .Union => "union", + else => unreachable, }; - return sema.failWithOwnedErrorMsg(block, msg); - }, - .Struct => { - if (dest_ty.containerLayout() == .Auto) { - const msg = msg: { - const msg = try sema.errMsg( - block, - dest_ty_src, - "cannot @bitCast to '{}', struct does not have a specified layout", - .{dest_ty.fmt(target)}, - ); - errdefer msg.destroy(sema.gpa); - - const ty_decl_src = dest_ty.declSrcLoc(); - try sema.mod.errNoteNonLazy( - ty_decl_src, - msg, - "consider using 'packed struct' or 'extern struct' for a specified layout.", - .{}, - ); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } + return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}', {s} does not have a guaranteed in-memory layout", .{ + dest_ty.fmt(target), container, + }); }, .BoundFn => @panic("TODO remove this type from the language and compiler"), - else => {}, - } - // When bitcasting we compare the bit size of the types, so we need to - // fully resolve all composite types to finalize the layout. - try sema.resolveTypeFully(block, dest_ty_src, dest_ty); + .Array, + .Bool, + .Float, + .Int, + .Vector, + => {}, + } const operand = sema.resolveInst(extra.rhs); return sema.bitCast(block, dest_ty, operand, operand_src); From c1098e90367c8965fbf0c65e4967d938986af8b0 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Fri, 25 Mar 2022 19:35:38 +0100 Subject: [PATCH 0935/2031] stage2 x86_64: remove MCValue.embedded_in_code --- src/arch/x86_64/CodeGen.zig | 66 ++++--------------------------------- 1 file changed, 6 insertions(+), 60 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 1e79119997..17a113bc81 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -117,11 +117,6 @@ pub const MCValue = union(enum) { /// A pointer-sized integer that fits in a register. /// If the type is a pointer, this is the pointer address in virtual address space. immediate: u64, - /// The constant was emitted into the code, at this offset. - /// If the type is a pointer, it means the pointer address is embedded in the code. - embedded_in_code: usize, - /// The value is a pointer to a constant which was emitted into the code, at this offset. - ptr_embedded_in_code: usize, /// The value is in a target-specific register. register: Register, /// The value is in memory at a hard-coded address. @@ -149,7 +144,7 @@ pub const MCValue = union(enum) { fn isMemory(mcv: MCValue) bool { return switch (mcv) { - .embedded_in_code, .memory, .stack_offset => true, + .memory, .stack_offset => true, else => false, }; } @@ -168,12 +163,10 @@ pub const MCValue = union(enum) { .dead => unreachable, .immediate, - .embedded_in_code, .memory, .compare_flags_unsigned, .compare_flags_signed, .ptr_stack_offset, - .ptr_embedded_in_code, .undef, => false, @@ -2437,12 +2430,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .ptr_stack_offset => |off| { try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }); }, - .ptr_embedded_in_code => |off| { - try self.setRegOrMem(elem_ty, dst_mcv, .{ .embedded_in_code = off }); - }, - .embedded_in_code => { - return self.fail("TODO implement loading from MCValue.embedded_in_code", .{}); - }, .register => |reg| { self.register_manager.freezeRegs(&.{reg}); defer self.register_manager.unfreezeRegs(&.{reg}); @@ -2452,7 +2439,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .undef => unreachable, .compare_flags_unsigned => unreachable, .compare_flags_signed => unreachable, - .embedded_in_code => unreachable, .register => |dst_reg| { // mov dst_reg, [reg] _ = try self.addInst(.{ @@ -2567,12 +2553,6 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .ptr_stack_offset => |off| { try self.genSetStack(value_ty, off, value, .{}); }, - .ptr_embedded_in_code => |off| { - try self.setRegOrMem(value_ty, .{ .embedded_in_code = off }, value); - }, - .embedded_in_code => { - return self.fail("TODO implement storing to MCValue.embedded_in_code", .{}); - }, .register => |reg| { self.register_manager.freezeRegs(&.{reg}); defer self.register_manager.unfreezeRegs(&.{reg}); @@ -3025,7 +3005,6 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .compare_flags_unsigned => unreachable, .compare_flags_signed => unreachable, .ptr_stack_offset => unreachable, - .ptr_embedded_in_code => unreachable, .register => |dst_reg| { switch (src_mcv) { .none => unreachable, @@ -3037,7 +3016,6 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC const reg = try self.copyToTmpRegister(dst_ty, src_mcv); return self.genBinMathOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); }, - .ptr_embedded_in_code => unreachable, .register => |src_reg| { _ = try self.addInst(.{ .tag = mir_tag, @@ -3057,7 +3035,6 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .data = .{ .imm = @truncate(u32, imm) }, }); }, - .embedded_in_code, .memory, .got_load, .direct_load, @@ -3099,7 +3076,6 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .undef => return self.genSetStack(dst_ty, off, .undef, .{}), .dead, .unreach => unreachable, .ptr_stack_offset => unreachable, - .ptr_embedded_in_code => unreachable, .register => |src_reg| { _ = try self.addInst(.{ .tag = mir_tag, @@ -3141,7 +3117,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .data = .{ .payload = payload }, }); }, - .embedded_in_code, .memory, .stack_offset => { + .memory, .stack_offset => { return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{}); }, .got_load, .direct_load => { @@ -3155,7 +3131,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC }, } }, - .embedded_in_code, .memory => { + .memory => { return self.fail("TODO implement x86 ADD/SUB/CMP destination memory", .{}); }, .got_load, .direct_load => { @@ -3173,14 +3149,12 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! .compare_flags_unsigned => unreachable, .compare_flags_signed => unreachable, .ptr_stack_offset => unreachable, - .ptr_embedded_in_code => unreachable, .register => |dst_reg| { switch (src_mcv) { .none => unreachable, .undef => try self.genSetReg(dst_ty, dst_reg, .undef), .dead, .unreach => unreachable, .ptr_stack_offset => unreachable, - .ptr_embedded_in_code => unreachable, .register => |src_reg| { // register, register _ = try self.addInst(.{ @@ -3222,7 +3196,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! .data = .{ .imm = @bitCast(u32, -off) }, }); }, - .embedded_in_code, .memory => { + .memory => { return self.fail("TODO implement x86 multiply source memory", .{}); }, .got_load, .direct_load => { @@ -3242,7 +3216,6 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! .undef => return self.genSetStack(dst_ty, off, .undef, .{}), .dead, .unreach => unreachable, .ptr_stack_offset => unreachable, - .ptr_embedded_in_code => unreachable, .register => |src_reg| { // copy dst to a register const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); @@ -3263,7 +3236,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! _ = imm; return self.fail("TODO implement x86 multiply source immediate", .{}); }, - .embedded_in_code, .memory, .stack_offset => { + .memory, .stack_offset => { return self.fail("TODO implement x86 multiply source memory", .{}); }, .got_load, .direct_load => { @@ -3277,7 +3250,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! }, } }, - .embedded_in_code, .memory => { + .memory => { return self.fail("TODO implement x86 multiply destination memory", .{}); }, .got_load, .direct_load => { @@ -3396,14 +3369,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .ptr_stack_offset => { return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); }, - .ptr_embedded_in_code => { - return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); - }, .undef => unreachable, .immediate => unreachable, .unreach => unreachable, .dead => unreachable, - .embedded_in_code => unreachable, .memory => unreachable, .got_load => unreachable, .direct_load => unreachable, @@ -4626,7 +4595,6 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE const abi_size = ty.abiSize(self.target.*); switch (mcv) { .dead => unreachable, - .ptr_embedded_in_code => unreachable, .unreach, .none => return, .undef => { if (abi_size <= 8) { @@ -4677,13 +4645,6 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE else => return self.fail("TODO implement inputs on stack for {} with abi size > 8", .{mcv}), } }, - .embedded_in_code => { - if (abi_size <= 8) { - const reg = try self.copyToTmpRegister(ty, mcv); - return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); - } - return self.fail("TODO implement inputs on stack for {} with abi size > 8", .{mcv}); - }, .memory, .direct_load, .got_load, @@ -4731,7 +4692,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl const abi_size = ty.abiSize(self.target.*); switch (mcv) { .dead => unreachable, - .ptr_embedded_in_code => unreachable, .unreach, .none => return, // Nothing to do. .undef => { if (!self.wantSafety()) @@ -4870,7 +4830,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl } }, .memory, - .embedded_in_code, .got_load, .direct_load, => { @@ -5222,7 +5181,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .data = .{ .imm = @bitCast(u32, -off) }, }); }, - .ptr_embedded_in_code => unreachable, .unreach, .none => return, // Nothing to do. .undef => { if (!self.wantSafety()) @@ -5304,18 +5262,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .data = .{ .payload = payload }, }); }, - .embedded_in_code => |code_offset| { - // We need the offset from RIP in a signed i32 twos complement. - const payload = try self.addExtra(Mir.Imm64.encode(code_offset)); - _ = try self.addInst(.{ - .tag = .lea, - .ops = (Mir.Ops{ - .reg1 = reg, - .flags = 0b01, - }).encode(), - .data = .{ .payload = payload }, - }); - }, .register => |src_reg| { // If the registers are the same, nothing to do. if (src_reg.id() == reg.id()) From bae35bdf2d8919b60dee9a0af3afbdd93dd72b59 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 25 Mar 2022 18:27:10 -0700 Subject: [PATCH 0936/2031] stage2: result location types for function call arguments * AstGen: restore the param_type ZIR instruction and pass it to the expression for function call arguments. This does not solve the problem for generic function parameters, but it catches stage2 up to stage1 which also does not solve the problem for generic function parameters. - Most of the enhancements in this commit will still be needed for a more sophisticated further improvement to handle generic function types. - In Sema, handling of `as` coercion recognizes the `var_args_param` Type Tag and passes the operand through doing no coercion. - That was the last ZIR tag and we are now using all 256 ZIR tags. * AstGen: array init and struct init expressions use the anon form even when the result location has a type. Prevents the type system incorrectly believing, for example, that a tuple is actually an array when the result location is a param_type of a function with `anytype` parameter. * Sema: add missing coercion in `unionInit` to coerce the init to the corresponding union field type. * `Value.fieldValue` now takes a type and does not take an allocator. closes #11293 After this commit, stage2 passes all the parser tests. --- src/AstGen.zig | 41 ++++++++++++++++++++++++++++------------- src/Sema.zig | 42 ++++++++++++++++++++++++++++++++++++++++-- src/Zir.zig | 15 +++++++++++++++ src/print_zir.zig | 11 +++++++++++ src/type.zig | 2 ++ src/value.zig | 15 +++++++++++---- test/behavior/call.zig | 20 ++++++++++++++++++++ 7 files changed, 127 insertions(+), 19 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index e0f4028cba..4141b73e9a 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1318,13 +1318,13 @@ fn arrayInitExpr( return arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon); } }, - .ty, .coerced_ty => |ty_inst| { + .ty, .coerced_ty => { if (types.array != .none) { const result = try arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, types.sentinel, false); return rvalue(gz, rl, result, node); } else { - const elem_type = try gz.addUnNode(.elem_type, ty_inst, node); - return arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, elem_type, types.sentinel, false); + const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon); + return rvalue(gz, rl, result, node); } }, .ptr => |ptr_inst| { @@ -1559,7 +1559,7 @@ fn structInitExpr( _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init_ref); } else { - return structInitExprRlNone(gz, scope, node, struct_init, .struct_init_anon_ref); + return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon_ref); } }, .none => { @@ -1568,12 +1568,13 @@ fn structInitExpr( _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init); } else { - return structInitExprRlNone(gz, scope, node, struct_init, .struct_init_anon); + return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon); } }, .ty, .coerced_ty => |ty_inst| { if (struct_init.ast.type_expr == 0) { - return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init); + const result = try structInitExprRlNone(gz, scope, node, struct_init, ty_inst, .struct_init_anon); + return rvalue(gz, rl, result, node); } const inner_ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); _ = try gz.addUnNode(.validate_struct_init_ty, inner_ty_inst, node); @@ -1586,7 +1587,7 @@ fn structInitExpr( // We treat this case differently so that we don't get a crash when // analyzing field_base_ptr against an alloc_inferred_mut. // See corresponding logic in arrayInitExpr. - const result = try structInitExprRlNone(gz, scope, node, struct_init, .struct_init_anon); + const result = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon); return rvalue(gz, rl, result, node); } else { return structInitExprRlPtr(gz, scope, rl, node, struct_init, ptr_inst); @@ -1596,7 +1597,7 @@ fn structInitExpr( // This condition is here for the same reason as the above condition in `inferred_ptr`. // See corresponding logic in arrayInitExpr. if (struct_init.ast.type_expr == 0 and astgen.isInferred(block_gz.rl_ptr)) { - const result = try structInitExprRlNone(gz, scope, node, struct_init, .struct_init_anon); + const result = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon); return rvalue(gz, rl, result, node); } @@ -1610,6 +1611,7 @@ fn structInitExprRlNone( scope: *Scope, node: Ast.Node.Index, struct_init: Ast.full.StructInit, + ty_inst: Zir.Inst.Ref, tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { const astgen = gz.astgen; @@ -1624,9 +1626,16 @@ fn structInitExprRlNone( for (struct_init.ast.fields) |field_init| { const name_token = tree.firstToken(field_init) - 2; const str_index = try astgen.identAsString(name_token); + const sub_rl: ResultLoc = if (ty_inst != .none) + ResultLoc{ .ty = try gz.addPlNode(.field_type, field_init, Zir.Inst.FieldType{ + .container_type = ty_inst, + .name_start = str_index, + }) } + else + .none; setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{ .field_name = str_index, - .init = try expr(gz, scope, .none, field_init), + .init = try expr(gz, scope, sub_rl, field_init), }); extra_index += field_size; } @@ -2350,6 +2359,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .closure_get, .array_base_ptr, .field_base_ptr, + .param_type, => break :b false, // ZIR instructions that are always `noreturn`. @@ -7846,10 +7856,15 @@ fn callExpr( }); var extra_index = try reserveExtra(astgen, call.ast.params.len); - for (call.ast.params) |param_node| { - // Parameters are always temporary values, they have no - // meaningful result location. Sema will coerce them. - const arg_ref = try expr(gz, scope, .none, param_node); + for (call.ast.params) |param_node, i| { + const param_type = try gz.add(.{ + .tag = .param_type, + .data = .{ .param_type = .{ + .callee = callee, + .param_index = @intCast(u32, i), + } }, + }); + const arg_ref = try expr(gz, scope, .{ .coerced_ty = param_type }, param_node); astgen.extra.items[extra_index] = @enumToInt(arg_ref); extra_index += 1; } diff --git a/src/Sema.zig b/src/Sema.zig index e1a8d6f09a..428ad71da1 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -738,6 +738,7 @@ fn analyzeBodyInner( .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false), .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false), .optional_type => try sema.zirOptionalType(block, inst), + .param_type => try sema.zirParamType(block, inst), .ptr_type => try sema.zirPtrType(block, inst), .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst), .ref => try sema.zirRef(block, inst), @@ -3638,6 +3639,39 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v return sema.storePtr(block, src, ptr, operand); } +fn zirParamType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const callee_src = sema.src; + + const inst_data = sema.code.instructions.items(.data)[inst].param_type; + const callee = sema.resolveInst(inst_data.callee); + const callee_ty = sema.typeOf(callee); + var param_index = inst_data.param_index; + + const fn_ty = if (callee_ty.tag() == .bound_fn) fn_ty: { + const bound_fn_val = try sema.resolveConstValue(block, callee_src, callee); + const bound_fn = bound_fn_val.castTag(.bound_fn).?.data; + const fn_ty = sema.typeOf(bound_fn.func_inst); + param_index += 1; + break :fn_ty fn_ty; + } else callee_ty; + + const fn_info = if (fn_ty.zigTypeTag() == .Pointer) + fn_ty.childType().fnInfo() + else + fn_ty.fnInfo(); + + if (param_index >= fn_info.param_types.len) { + assert(fn_info.is_var_args); + return sema.addType(Type.initTag(.var_args_param)); + } + + if (fn_info.param_types[param_index].tag() == .generic_poison) { + return sema.addType(Type.initTag(.var_args_param)); + } + + return sema.addType(fn_info.param_types[param_index]); +} + fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -6613,6 +6647,7 @@ fn analyzeAs( ) CompileError!Air.Inst.Ref { const dest_ty = try sema.resolveType(block, src, zir_dest_type); const operand = sema.resolveInst(zir_operand); + if (dest_ty.tag() == .var_args_param) return operand; return sema.coerce(block, dest_ty, operand, src); } @@ -12140,7 +12175,7 @@ fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A fn unionInit( sema: *Sema, block: *Block, - init: Air.Inst.Ref, + uncasted_init: Air.Inst.Ref, init_src: LazySrcLoc, union_ty: Type, union_ty_src: LazySrcLoc, @@ -12148,6 +12183,8 @@ fn unionInit( field_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src); + const field = union_ty.unionFields().values()[field_index]; + const init = try sema.coerce(block, field.ty, uncasted_init, init_src); if (try sema.resolveMaybeUndefVal(block, init_src, init)) |init_val| { const tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index); @@ -12620,6 +12657,7 @@ fn zirFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const ty_src = inst_data.src(); const field_src = inst_data.src(); const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type); + if (aggregate_ty.tag() == .var_args_param) return sema.addType(aggregate_ty); const field_name = sema.code.nullTerminatedString(extra.name_start); return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src); } @@ -18964,7 +19002,7 @@ fn beginComptimePtrLoad( if (coerce_in_mem_ok) { deref.pointee = TypedValue{ .ty = field_ty, - .val = try tv.val.fieldValue(sema.arena, field_index), + .val = tv.val.fieldValue(tv.ty, field_index), }; break :blk deref; } diff --git a/src/Zir.zig b/src/Zir.zig index d6e8486ea7..d895281141 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -464,6 +464,14 @@ pub const Inst = struct { /// Merge two error sets into one, `E1 || E2`. /// Uses the `pl_node` field with payload `Bin`. merge_error_sets, + /// Given a reference to a function and a parameter index, returns the + /// type of the parameter. The only usage of this instruction is for the + /// result location of parameters of function calls. In the case of a function's + /// parameter type being `anytype`, it is the type coercion's job to detect this + /// scenario and skip the coercion, so that semantic analysis of this instruction + /// is not in a position where it must create an invalid type. + /// Uses the `param_type` union field. + param_type, /// Turns an R-Value into a const L-Value. In other words, it takes a value, /// stores it in a memory location, and returns a const pointer to it. If the value /// is `comptime`, the memory location is global static constant data. Otherwise, @@ -1077,6 +1085,7 @@ pub const Inst = struct { .mul, .mulwrap, .mul_sat, + .param_type, .ref, .shl, .shl_sat, @@ -1266,6 +1275,7 @@ pub const Inst = struct { .mulwrap = .pl_node, .mul_sat = .pl_node, + .param_type = .param_type, .param = .pl_tok, .param_comptime = .pl_tok, .param_anytype = .str_tok, @@ -2213,6 +2223,10 @@ pub const Inst = struct { /// Points to a `Block`. payload_index: u32, }, + param_type: struct { + callee: Ref, + param_index: u32, + }, @"unreachable": struct { /// Offset from Decl AST node index. /// `Tag` determines which kind of AST node this points to. @@ -2288,6 +2302,7 @@ pub const Inst = struct { ptr_type, int_type, bool_br, + param_type, @"unreachable", @"break", switch_capture, diff --git a/src/print_zir.zig b/src/print_zir.zig index 12aad86984..81562dc7bc 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -252,6 +252,7 @@ const Writer = struct { => try self.writeBoolBr(stream, inst), .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst), + .param_type => try self.writeParamType(stream, inst), .ptr_type_simple => try self.writePtrTypeSimple(stream, inst), .ptr_type => try self.writePtrType(stream, inst), .int => try self.writeInt(stream, inst), @@ -558,6 +559,16 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } + fn writeParamType( + self: *Writer, + stream: anytype, + inst: Zir.Inst.Index, + ) (@TypeOf(stream).Error || error{OutOfMemory})!void { + const inst_data = self.code.instructions.items(.data)[inst].param_type; + try self.writeInstRef(stream, inst_data.callee); + try stream.print(", {d})", .{inst_data.param_index}); + } + fn writePtrTypeSimple( self: *Writer, stream: anytype, diff --git a/src/type.zig b/src/type.zig index 17c8d4d111..a706483003 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3724,6 +3724,8 @@ pub const Type = extern union { .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int), .pointer => ty.castTag(.pointer).?.data.pointee_type, + .var_args_param => ty, + else => unreachable, }; } diff --git a/src/value.zig b/src/value.zig index be5794d6ae..80d76f6bce 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2659,8 +2659,7 @@ pub const Value = extern union { }; } - pub fn fieldValue(val: Value, allocator: Allocator, index: usize) error{OutOfMemory}!Value { - _ = allocator; + pub fn fieldValue(val: Value, ty: Type, index: usize) Value { switch (val.tag()) { .aggregate => { const field_values = val.castTag(.aggregate).?.data; @@ -2671,8 +2670,16 @@ pub const Value = extern union { // TODO assert the tag is correct return payload.val; }, - // Structs which have only one possible value need to consist of members which have only one possible value. - .the_only_possible_value => return val, + + .the_only_possible_value => return ty.onePossibleValue().?, + + .empty_struct_value => { + if (ty.isTupleOrAnonStruct()) { + const tuple = ty.tupleFields(); + return tuple.values[index]; + } + unreachable; + }, else => unreachable, } diff --git a/test/behavior/call.zig b/test/behavior/call.zig index 57bc0fb3f7..119dc289b1 100644 --- a/test/behavior/call.zig +++ b/test/behavior/call.zig @@ -98,3 +98,23 @@ test "comptime call with bound function as parameter" { var inst: S = undefined; try expectEqual(?i32, S.ReturnType(inst.call_me_maybe)); } + +test "result location of function call argument through runtime condition and struct init" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const E = enum { a, b }; + const S = struct { + e: E, + }; + const namespace = struct { + fn foo(s: S) !void { + try expect(s.e == .b); + } + }; + var runtime = true; + try namespace.foo(.{ + .e = if (!runtime) .a else .b, + }); +} From af844931b2600e50e586436dee0d607d67ed9ff2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 26 Mar 2022 00:33:14 -0700 Subject: [PATCH 0937/2031] stage2: resolve types more lazily This avoids unwanted "foo depends on itself" compilation errors. --- src/Module.zig | 6 +- src/Sema.zig | 16 ++--- src/type.zig | 181 ++++++++++++++++++++++++++++--------------------- src/value.zig | 5 +- 4 files changed, 116 insertions(+), 92 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 0666936f1f..71e2bd8d7c 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3904,7 +3904,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { // Note this resolves the type of the Decl, not the value; if this Decl // is a struct, for example, this resolves `type` (which needs no resolution), // not the struct itself. - try sema.resolveTypeFully(&block_scope, src, decl_tv.ty); + try sema.resolveTypeLayout(&block_scope, src, decl_tv.ty); const decl_arena_state = try decl_arena_allocator.create(std.heap.ArenaAllocator.State); @@ -4049,6 +4049,10 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { if (has_runtime_bits) { log.debug("queue linker work for {*} ({s})", .{ decl, decl.name }); + // Needed for codegen_decl which will call updateDecl and then the + // codegen backend wants full access to the Decl Type. + try sema.resolveTypeFully(&block_scope, src, decl.ty); + try mod.comp.bin_file.allocateDeclIndexes(decl); try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl }); diff --git a/src/Sema.zig b/src/Sema.zig index 80227f81af..f41528eca4 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -10880,9 +10880,9 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .Pointer => { const info = ty.ptrInfo().data; const alignment = if (info.@"align" != 0) - info.@"align" + try Value.Tag.int_u64.create(sema.arena, info.@"align") else - try sema.typeAbiAlignment(block, src, info.pointee_type); + try info.pointee_type.lazyAbiAlignment(target, sema.arena); const field_values = try sema.arena.create([8]Value); field_values.* = .{ @@ -10893,7 +10893,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // is_volatile: bool, Value.makeBool(info.@"volatile"), // alignment: comptime_int, - try Value.Tag.int_u64.create(sema.arena, alignment), + alignment, // address_space: AddressSpace try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.@"addrspace")), // child: type, @@ -11322,8 +11322,6 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const is_comptime = field_val.tag() != .unreachable_value; const opt_default_val = if (is_comptime) field_val else null; const default_val_ptr = try sema.optRefValue(block, src, field_ty, opt_default_val); - const alignment = field_ty.abiAlignment(target); - struct_field_fields.* = .{ // name: []const u8, name_val, @@ -11334,7 +11332,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // is_comptime: bool, Value.makeBool(is_comptime), // alignment: comptime_int, - try Value.Tag.int_u64.create(fields_anon_decl.arena(), alignment), + try field_ty.lazyAbiAlignment(target, fields_anon_decl.arena()), }; struct_field_val.* = try Value.Tag.aggregate.create(fields_anon_decl.arena(), struct_field_fields); } @@ -22749,11 +22747,9 @@ fn typeAbiSize(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u64 { return ty.abiSize(target); } -/// TODO merge with Type.abiAlignmentAdvanced -fn typeAbiAlignment(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u32 { - try sema.resolveTypeLayout(block, src, ty); +fn typeAbiAlignment(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!u32 { const target = sema.mod.getTarget(); - return ty.abiAlignment(target); + return (try ty.abiAlignmentAdvanced(target, .{ .sema_kit = sema.kit(block, src) })).scalar; } /// Not valid to call for packed unions. diff --git a/src/type.zig b/src/type.zig index a706483003..421c1e07c0 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2691,31 +2691,41 @@ pub const Type = extern union { /// Returns 0 for 0-bit types. pub fn abiAlignment(ty: Type, target: Target) u32 { - return ty.abiAlignmentAdvanced(target, .eager).scalar; + return (ty.abiAlignmentAdvanced(target, .eager) catch unreachable).scalar; } /// May capture a reference to `ty`. pub fn lazyAbiAlignment(ty: Type, target: Target, arena: Allocator) !Value { - switch (ty.abiAlignmentAdvanced(target, .{ .lazy = arena })) { - .val => |val| return try val, + switch (try ty.abiAlignmentAdvanced(target, .{ .lazy = arena })) { + .val => |val| return val, .scalar => |x| return Value.Tag.int_u64.create(arena, x), } } + const AbiAlignmentAdvanced = union(enum) { + scalar: u32, + val: Value, + }; + /// If you pass `eager` you will get back `scalar` and assert the type is resolved. + /// In this case there will be no error, guaranteed. /// If you pass `lazy` you may get back `scalar` or `val`. /// If `val` is returned, a reference to `ty` has been captured. - fn abiAlignmentAdvanced( + /// If you pass `sema_kit` you will get back `scalar` and resolve the type if + /// necessary, possibly returning a CompileError. + pub fn abiAlignmentAdvanced( ty: Type, target: Target, strat: union(enum) { eager, lazy: Allocator, + sema_kit: Module.WipAnalysis, }, - ) union(enum) { - scalar: u32, - val: Allocator.Error!Value, - } { + ) Module.CompileError!AbiAlignmentAdvanced { + const sema_kit = switch (strat) { + .sema_kit => |sk| sk, + else => null, + }; return switch (ty.tag()) { .u1, .u8, @@ -2735,25 +2745,25 @@ pub const Type = extern union { .extern_options, .@"opaque", .anyopaque, - => return .{ .scalar = 1 }, + => return AbiAlignmentAdvanced{ .scalar = 1 }, .fn_noreturn_no_args, // represents machine code; not a pointer .fn_void_no_args, // represents machine code; not a pointer .fn_naked_noreturn_no_args, // represents machine code; not a pointer .fn_ccc_void_no_args, // represents machine code; not a pointer - => return .{ .scalar = target_util.defaultFunctionAlignment(target) }, + => return AbiAlignmentAdvanced{ .scalar = target_util.defaultFunctionAlignment(target) }, // represents machine code; not a pointer .function => { const alignment = ty.castTag(.function).?.data.alignment; - if (alignment != 0) return .{ .scalar = alignment }; - return .{ .scalar = target_util.defaultFunctionAlignment(target) }; + if (alignment != 0) return AbiAlignmentAdvanced{ .scalar = alignment }; + return AbiAlignmentAdvanced{ .scalar = target_util.defaultFunctionAlignment(target) }; }, - .i16, .u16 => return .{ .scalar = 2 }, - .i32, .u32 => return .{ .scalar = 4 }, - .i64, .u64 => return .{ .scalar = 8 }, - .u128, .i128 => return .{ .scalar = 16 }, + .i16, .u16 => return AbiAlignmentAdvanced{ .scalar = 2 }, + .i32, .u32 => return AbiAlignmentAdvanced{ .scalar = 4 }, + .i64, .u64 => return AbiAlignmentAdvanced{ .scalar = 8 }, + .u128, .i128 => return AbiAlignmentAdvanced{ .scalar = 16 }, .isize, .usize, @@ -2776,40 +2786,40 @@ pub const Type = extern union { .manyptr_const_u8_sentinel_0, .@"anyframe", .anyframe_T, - => return .{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }, + => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }, - .c_short => return .{ .scalar = @divExact(CType.short.sizeInBits(target), 8) }, - .c_ushort => return .{ .scalar = @divExact(CType.ushort.sizeInBits(target), 8) }, - .c_int => return .{ .scalar = @divExact(CType.int.sizeInBits(target), 8) }, - .c_uint => return .{ .scalar = @divExact(CType.uint.sizeInBits(target), 8) }, - .c_long => return .{ .scalar = @divExact(CType.long.sizeInBits(target), 8) }, - .c_ulong => return .{ .scalar = @divExact(CType.ulong.sizeInBits(target), 8) }, - .c_longlong => return .{ .scalar = @divExact(CType.longlong.sizeInBits(target), 8) }, - .c_ulonglong => return .{ .scalar = @divExact(CType.ulonglong.sizeInBits(target), 8) }, + .c_short => return AbiAlignmentAdvanced{ .scalar = @divExact(CType.short.sizeInBits(target), 8) }, + .c_ushort => return AbiAlignmentAdvanced{ .scalar = @divExact(CType.ushort.sizeInBits(target), 8) }, + .c_int => return AbiAlignmentAdvanced{ .scalar = @divExact(CType.int.sizeInBits(target), 8) }, + .c_uint => return AbiAlignmentAdvanced{ .scalar = @divExact(CType.uint.sizeInBits(target), 8) }, + .c_long => return AbiAlignmentAdvanced{ .scalar = @divExact(CType.long.sizeInBits(target), 8) }, + .c_ulong => return AbiAlignmentAdvanced{ .scalar = @divExact(CType.ulong.sizeInBits(target), 8) }, + .c_longlong => return AbiAlignmentAdvanced{ .scalar = @divExact(CType.longlong.sizeInBits(target), 8) }, + .c_ulonglong => return AbiAlignmentAdvanced{ .scalar = @divExact(CType.ulonglong.sizeInBits(target), 8) }, - .f16 => return .{ .scalar = 2 }, - .f32 => return .{ .scalar = 4 }, - .f64 => return .{ .scalar = 8 }, - .f128 => return .{ .scalar = 16 }, + .f16 => return AbiAlignmentAdvanced{ .scalar = 2 }, + .f32 => return AbiAlignmentAdvanced{ .scalar = 4 }, + .f64 => return AbiAlignmentAdvanced{ .scalar = 8 }, + .f128 => return AbiAlignmentAdvanced{ .scalar = 16 }, .f80 => switch (target.cpu.arch) { - .i386 => return .{ .scalar = 4 }, - .x86_64 => return .{ .scalar = 16 }, + .i386 => return AbiAlignmentAdvanced{ .scalar = 4 }, + .x86_64 => return AbiAlignmentAdvanced{ .scalar = 16 }, else => { var payload: Payload.Bits = .{ .base = .{ .tag = .int_unsigned }, .data = 80, }; const u80_ty = initPayload(&payload.base); - return .{ .scalar = abiAlignment(u80_ty, target) }; + return AbiAlignmentAdvanced{ .scalar = abiAlignment(u80_ty, target) }; }, }, .c_longdouble => switch (CType.longdouble.sizeInBits(target)) { - 16 => return .{ .scalar = abiAlignment(Type.f16, target) }, - 32 => return .{ .scalar = abiAlignment(Type.f32, target) }, - 64 => return .{ .scalar = abiAlignment(Type.f64, target) }, - 80 => return .{ .scalar = abiAlignment(Type.f80, target) }, - 128 => return .{ .scalar = abiAlignment(Type.f128, target) }, + 16 => return AbiAlignmentAdvanced{ .scalar = abiAlignment(Type.f16, target) }, + 32 => return AbiAlignmentAdvanced{ .scalar = abiAlignment(Type.f32, target) }, + 64 => return AbiAlignmentAdvanced{ .scalar = abiAlignment(Type.f64, target) }, + 80 => return AbiAlignmentAdvanced{ .scalar = abiAlignment(Type.f80, target) }, + 128 => return AbiAlignmentAdvanced{ .scalar = abiAlignment(Type.f128, target) }, else => unreachable, }, @@ -2819,22 +2829,22 @@ pub const Type = extern union { .anyerror, .error_set_inferred, .error_set_merged, - => return .{ .scalar = 2 }, // TODO revisit this when we have the concept of the error tag type + => return AbiAlignmentAdvanced{ .scalar = 2 }, // TODO revisit this when we have the concept of the error tag type .array, .array_sentinel => return ty.elemType().abiAlignmentAdvanced(target, strat), // TODO audit this - is there any more complicated logic to determine // ABI alignment of vectors? - .vector => return .{ .scalar = 16 }, + .vector => return AbiAlignmentAdvanced{ .scalar = 16 }, .int_signed, .int_unsigned => { const bits: u16 = ty.cast(Payload.Bits).?.data; - if (bits == 0) return .{ .scalar = 0 }; - if (bits <= 8) return .{ .scalar = 1 }; - if (bits <= 16) return .{ .scalar = 2 }; - if (bits <= 32) return .{ .scalar = 4 }; - if (bits <= 64) return .{ .scalar = 8 }; - return .{ .scalar = 16 }; + if (bits == 0) return AbiAlignmentAdvanced{ .scalar = 0 }; + if (bits <= 8) return AbiAlignmentAdvanced{ .scalar = 1 }; + if (bits <= 16) return AbiAlignmentAdvanced{ .scalar = 2 }; + if (bits <= 32) return AbiAlignmentAdvanced{ .scalar = 4 }; + if (bits <= 64) return AbiAlignmentAdvanced{ .scalar = 8 }; + return AbiAlignmentAdvanced{ .scalar = 16 }; }, .optional => { @@ -2842,17 +2852,19 @@ pub const Type = extern union { const child_type = ty.optionalChild(&buf); if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr()) { - return .{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }; + return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }; } switch (strat) { - .eager => { - if (!child_type.hasRuntimeBits()) return .{ .scalar = 1 }; - return .{ .scalar = child_type.abiAlignment(target) }; + .eager, .sema_kit => { + if (!(try child_type.hasRuntimeBitsAdvanced(false, sema_kit))) { + return AbiAlignmentAdvanced{ .scalar = 1 }; + } + return child_type.abiAlignmentAdvanced(target, strat); }, - .lazy => |arena| switch (child_type.abiAlignmentAdvanced(target, strat)) { - .scalar => |x| return .{ .scalar = @maximum(x, 1) }, - .val => return .{ .val = Value.Tag.lazy_align.create(arena, ty) }, + .lazy => |arena| switch (try child_type.abiAlignmentAdvanced(target, strat)) { + .scalar => |x| return AbiAlignmentAdvanced{ .scalar = @maximum(x, 1) }, + .val => return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }, }, } }, @@ -2860,60 +2872,64 @@ pub const Type = extern union { .error_union => { const data = ty.castTag(.error_union).?.data; switch (strat) { - .eager => { - if (!data.error_set.hasRuntimeBits()) { - return .{ .scalar = data.payload.abiAlignment(target) }; - } else if (!data.payload.hasRuntimeBits()) { - return .{ .scalar = data.error_set.abiAlignment(target) }; + .eager, .sema_kit => { + if (!(try data.error_set.hasRuntimeBitsAdvanced(false, sema_kit))) { + return data.payload.abiAlignmentAdvanced(target, strat); + } else if (!(try data.payload.hasRuntimeBitsAdvanced(false, sema_kit))) { + return data.error_set.abiAlignmentAdvanced(target, strat); } - return .{ .scalar = @maximum( - data.payload.abiAlignment(target), - data.error_set.abiAlignment(target), + return AbiAlignmentAdvanced{ .scalar = @maximum( + (try data.payload.abiAlignmentAdvanced(target, strat)).scalar, + (try data.error_set.abiAlignmentAdvanced(target, strat)).scalar, ) }; }, .lazy => |arena| { - switch (data.payload.abiAlignmentAdvanced(target, strat)) { + switch (try data.payload.abiAlignmentAdvanced(target, strat)) { .scalar => |payload_align| { if (payload_align == 0) { return data.error_set.abiAlignmentAdvanced(target, strat); } - switch (data.error_set.abiAlignmentAdvanced(target, strat)) { + switch (try data.error_set.abiAlignmentAdvanced(target, strat)) { .scalar => |err_set_align| { - return .{ .scalar = @maximum(payload_align, err_set_align) }; + return AbiAlignmentAdvanced{ .scalar = @maximum(payload_align, err_set_align) }; }, .val => {}, } }, .val => {}, } - return .{ .val = Value.Tag.lazy_align.create(arena, ty) }; + return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }; }, } }, .@"struct" => { + if (sema_kit) |sk| { + try sk.sema.resolveTypeLayout(sk.block, sk.src, ty); + } if (ty.castTag(.@"struct")) |payload| { const struct_obj = payload.data; if (!struct_obj.haveLayout()) switch (strat) { .eager => unreachable, // struct layout not resolved - .lazy => |arena| return .{ .val = Value.Tag.lazy_align.create(arena, ty) }, + .sema_kit => unreachable, // handled above + .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }, }; if (struct_obj.layout == .Packed) { var buf: Type.Payload.Bits = undefined; const int_ty = struct_obj.packedIntegerType(target, &buf); - return .{ .scalar = int_ty.abiAlignment(target) }; + return AbiAlignmentAdvanced{ .scalar = int_ty.abiAlignment(target) }; } } const fields = ty.structFields(); var big_align: u32 = 0; for (fields.values()) |field| { - if (!field.ty.hasRuntimeBits()) continue; + if (!(try field.ty.hasRuntimeBitsAdvanced(false, sema_kit))) continue; const field_align = field.normalAlignment(target); big_align = @maximum(big_align, field_align); } - return .{ .scalar = big_align }; + return AbiAlignmentAdvanced{ .scalar = big_align }; }, .tuple, .anon_struct => { @@ -2923,34 +2939,41 @@ pub const Type = extern union { const val = tuple.values[i]; if (val.tag() != .unreachable_value) continue; // comptime field - switch (field_ty.abiAlignmentAdvanced(target, strat)) { + switch (try field_ty.abiAlignmentAdvanced(target, strat)) { .scalar => |field_align| big_align = @maximum(big_align, field_align), .val => switch (strat) { .eager => unreachable, // field type alignment not resolved - .lazy => |arena| return .{ .val = Value.Tag.lazy_align.create(arena, ty) }, + .sema_kit => unreachable, // passed to abiAlignmentAdvanced above + .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }, }, } } - return .{ .scalar = big_align }; + return AbiAlignmentAdvanced{ .scalar = big_align }; }, .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => { var buffer: Payload.Bits = undefined; const int_tag_ty = ty.intTagType(&buffer); - return .{ .scalar = int_tag_ty.abiAlignment(target) }; + return AbiAlignmentAdvanced{ .scalar = int_tag_ty.abiAlignment(target) }; }, .@"union" => switch (strat) { - .eager => { + .eager, .sema_kit => { + if (sema_kit) |sk| { + try sk.sema.resolveTypeLayout(sk.block, sk.src, ty); + } // TODO pass `true` for have_tag when unions have a safety tag - return .{ .scalar = ty.castTag(.@"union").?.data.abiAlignment(target, false) }; + return AbiAlignmentAdvanced{ .scalar = ty.castTag(.@"union").?.data.abiAlignment(target, false) }; }, - .lazy => |arena| return .{ .val = Value.Tag.lazy_align.create(arena, ty) }, + .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }, }, .union_tagged => switch (strat) { - .eager => { - return .{ .scalar = ty.castTag(.union_tagged).?.data.abiAlignment(target, true) }; + .eager, .sema_kit => { + if (sema_kit) |sk| { + try sk.sema.resolveTypeLayout(sk.block, sk.src, ty); + } + return AbiAlignmentAdvanced{ .scalar = ty.castTag(.union_tagged).?.data.abiAlignment(target, true) }; }, - .lazy => |arena| return .{ .val = Value.Tag.lazy_align.create(arena, ty) }, + .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }, }, .empty_struct, @@ -2963,7 +2986,7 @@ pub const Type = extern union { .@"undefined", .enum_literal, .type_info, - => return .{ .scalar = 0 }, + => return AbiAlignmentAdvanced{ .scalar = 0 }, .noreturn, .inferred_alloc_const, diff --git a/src/value.zig b/src/value.zig index 80d76f6bce..0467f0362c 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1076,9 +1076,10 @@ pub const Value = extern union { .lazy_align => { const ty = val.castTag(.lazy_align).?.data; if (sema_kit) |sk| { - try sk.sema.resolveTypeLayout(sk.block, sk.src, ty); + return (try ty.abiAlignmentAdvanced(target, .{ .sema_kit = sk })).scalar; + } else { + return ty.abiAlignment(target); } - return ty.abiAlignment(target); }, else => return null, From 109e730c8ccdfe144f568f232578ab600ef4f33c Mon Sep 17 00:00:00 2001 From: Igor Stojkovic Date: Wed, 23 Mar 2022 20:29:53 +0100 Subject: [PATCH 0938/2031] stage1: Fix packed structs (#2627, #10104) Fixed formatting in packed-struct-zig Skipped packed_structs tests in stage2 simplified packed struct tests --- src/stage1/all_types.hpp | 1 + src/stage1/analyze.cpp | 24 ++- src/stage1/codegen.cpp | 2 +- src/stage1/ir.cpp | 7 + test/behavior.zig | 1 + test/behavior/packed-struct.zig | 324 ++++++++++++++++++++++++++++++++ test/behavior/struct.zig | 18 +- 7 files changed, 369 insertions(+), 8 deletions(-) create mode 100644 test/behavior/packed-struct.zig diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp index 36f136c77f..cbefcd1078 100644 --- a/src/stage1/all_types.hpp +++ b/src/stage1/all_types.hpp @@ -1398,6 +1398,7 @@ enum StructSpecial { struct ZigTypeStruct { AstNode *decl_node; TypeStructField **fields; + TypeStructField *misaligned_field; ScopeDecls *decls_scope; HashMap fields_by_name; RootStruct *root_struct; diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp index a2db15c622..15a8fdf81e 100644 --- a/src/stage1/analyze.cpp +++ b/src/stage1/analyze.cpp @@ -2404,6 +2404,8 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { size_t size_in_bits = 0; size_t abi_align = struct_type->abi_align; + TypeStructField *last_packed_field = nullptr; + // Calculate offsets for (size_t i = 0; i < field_count; i += 1) { TypeStructField *field = struct_type->data.structure.fields[i]; @@ -2428,6 +2430,7 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { return err; } + last_packed_field = field; size_t field_size_in_bits = type_size_bits(g, field_type); size_t next_packed_bits_offset = packed_bits_offset + field_size_in_bits; @@ -2487,8 +2490,13 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { } if (first_packed_bits_offset_misalign != SIZE_MAX) { size_t full_bit_count = packed_bits_offset - first_packed_bits_offset_misalign; - size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes); + size_t full_abi_size = get_abi_size_bytes(full_bit_count, 1); next_offset = next_field_offset(next_offset, abi_align, full_abi_size, abi_align); + ZigType* last_field_type = last_packed_field->type_entry; + // If only last field is misaligned and it is of int type save it so we can generate proper code for it later + if (last_field_type->size_in_bits == full_bit_count && (last_field_type->id == ZigTypeIdInt || last_field_type->id == ZigTypeIdEnum)) { + struct_type->data.structure.misaligned_field = last_packed_field; + } host_int_bytes[gen_field_index] = full_abi_size; gen_field_index += 1; } @@ -8839,6 +8847,8 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS llvm_struct_abi_align = max(llvm_struct_abi_align, llvm_field_abi_align); } + ZigType* last_packed_field_type = nullptr; + for (size_t i = 0; i < field_count; i += 1) { TypeStructField *field = struct_type->data.structure.fields[i]; ZigType *field_type = field->type_entry; @@ -8849,6 +8859,7 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS } if (packed) { + last_packed_field_type = field_type; size_t field_size_in_bits = type_size_bits(g, field_type); size_t next_packed_bits_offset = packed_bits_offset + field_size_in_bits; @@ -8933,8 +8944,15 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS if (first_packed_bits_offset_misalign != SIZE_MAX) { size_t full_bit_count = packed_bits_offset - first_packed_bits_offset_misalign; - size_t full_abi_size = get_abi_size_bytes(full_bit_count, g->pointer_size_bytes); - element_types[gen_field_index] = get_llvm_type_of_n_bytes(full_abi_size); + size_t full_abi_size = get_abi_size_bytes(full_bit_count, 1); + if (last_packed_field_type->size_in_bits == full_bit_count && last_packed_field_type->id != ZigTypeIdInt && last_packed_field_type->id != ZigTypeIdEnum) { + // If there is only one field that is misaligned and it is a custom type just use it + element_types[gen_field_index] = get_llvm_type(g, last_packed_field_type); + assert(full_abi_size == LLVMStoreSizeOfType(g->target_data_ref, element_types[gen_field_index])); + } else { + // Otherwise represent it as array of proper number of bytes in LLVM + element_types[gen_field_index] = get_llvm_type_of_n_bytes(full_abi_size); + } gen_field_index += 1; } diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index bfe58ae912..0223e54894 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -8334,7 +8334,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n break; } - if (src_field_index + 1 == src_field_index_end) { + if (src_field_index + 1 == src_field_index_end && !type_entry->data.structure.misaligned_field) { ZigValue *field_val = const_val->data.x_struct.fields[src_field_index]; LLVMValueRef val = gen_const_val(g, field_val, ""); fields[type_struct_field->gen_index] = val; diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 11f47c592b..22ed5befe1 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -15490,6 +15490,13 @@ static Stage1AirInst *ir_analyze_struct_field_ptr(IrAnalyze *ira, Scope *scope, is_const, is_volatile, PtrLenSingle, field->align, (uint32_t)(ptr_bit_offset + field->bit_offset_in_host), (uint32_t)host_int_bytes_for_result_type, false); + + if (field == struct_type->data.structure.misaligned_field) { + // If field is the last single misaligned field it will be represented as array + // of bytes in LLVM but get_pointer_to_type_extra will set its host_int_bytes to 0. + // We need it not to be 0 so later stage would generate proper bit casting code. + ptr_type->data.pointer.host_int_bytes = host_int_bytes_for_result_type; + } } if (instr_is_comptime(struct_ptr)) { ZigValue *ptr_val = ir_resolve_const(ira, struct_ptr, UndefBad); diff --git a/test/behavior.zig b/test/behavior.zig index 60b8d2bae7..8635d69836 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -132,6 +132,7 @@ test { _ = @import("behavior/slice_sentinel_comptime.zig"); _ = @import("behavior/src.zig"); _ = @import("behavior/struct.zig"); + _ = @import("behavior/packed-struct.zig"); _ = @import("behavior/struct_contains_null_ptr_itself.zig"); _ = @import("behavior/struct_contains_slice_of_itself.zig"); _ = @import("behavior/switch.zig"); diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig new file mode 100644 index 0000000000..86ef9e3c1e --- /dev/null +++ b/test/behavior/packed-struct.zig @@ -0,0 +1,324 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; +const native_endian = builtin.cpu.arch.endian(); + +test "correct size of packed structs" { + if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + + const T1 = packed struct { one: u8, three: [3]u8 }; + + try expectEqual(4, @sizeOf(T1)); + try expectEqual(4 * 8, @bitSizeOf(T1)); + + const T2 = packed struct { three: [3]u8, one: u8 }; + + try expectEqual(4, @sizeOf(T2)); + try expectEqual(4 * 8, @bitSizeOf(T2)); + + const T3 = packed struct { _1: u1, x: u7, _: u24 }; + + try expectEqual(4, @sizeOf(T3)); + try expectEqual(4 * 8, @bitSizeOf(T3)); + + const T4 = packed struct { _1: u1, x: u7, _2: u8, _3: u16 }; + + try expectEqual(4, @sizeOf(T4)); + try expectEqual(4 * 8, @bitSizeOf(T4)); + + const T5 = packed struct { _1: u1, x: u7, _2: u16, _3: u8 }; + + try expectEqual(4, @sizeOf(T5)); + try expectEqual(4 * 8, @bitSizeOf(T5)); +} + +test "flags in packed structs" { + if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + + const Flags1 = packed struct { + // byte 0 + b0_0: u1, + b0_1: u1, + b0_2: u1, + b0_3: u1, + b0_4: u1, + b0_5: u1, + b0_6: u1, + b0_7: u1, + + // partial byte 1 (but not 8 bits) + b1_0: u1, + b1_1: u1, + b1_2: u1, + b1_3: u1, + b1_4: u1, + b1_5: u1, + b1_6: u1, + + // some padding to fill to size 3 + _: u9, + }; + + try expectEqual(3, @sizeOf(Flags1)); + try expectEqual(3 * 8, @bitSizeOf(Flags1)); + + const Flags2 = packed struct { + // byte 0 + b0_0: u1, + b0_1: u1, + b0_2: u1, + b0_3: u1, + b0_4: u1, + b0_5: u1, + b0_6: u1, + b0_7: u1, + + // partial byte 1 (but not 8 bits) + b1_0: u1, + b1_1: u1, + b1_2: u1, + b1_3: u1, + b1_4: u1, + b1_5: u1, + b1_6: u1, + + // some padding that should yield @sizeOf(Flags2) == 4 + _: u10, + }; + + try expectEqual(4, @sizeOf(Flags2)); + try expectEqual(8 + 7 + 10, @bitSizeOf(Flags2)); + + const Flags3 = packed struct { + // byte 0 + b0_0: u1, + b0_1: u1, + b0_2: u1, + b0_3: u1, + b0_4: u1, + b0_5: u1, + b0_6: u1, + b0_7: u1, + + // byte 1 + b1_0: u1, + b1_1: u1, + b1_2: u1, + b1_3: u1, + b1_4: u1, + b1_5: u1, + b1_6: u1, + b1_7: u1, + + // some padding that should yield @sizeOf(Flags2) == 4 + _: u16, // it works, if the padding is 8-based + }; + + try expectEqual(4, @sizeOf(Flags3)); + try expectEqual(4 * 8, @bitSizeOf(Flags3)); +} + +test "arrays in packed structs" { + if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + + const T1 = packed struct { array: [3][3]u8 }; + const T2 = packed struct { array: [9]u8 }; + + try expectEqual(9, @sizeOf(T1)); + try expectEqual(9 * 8, @bitSizeOf(T1)); + try expectEqual(9, @sizeOf(T2)); + try expectEqual(9 * 8, @bitSizeOf(T2)); +} + +test "consistent size of packed structs" { + if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + + const TxData1 = packed struct { data: u8, _23: u23, full: bool = false }; + const TxData2 = packed struct { data: u9, _22: u22, full: bool = false }; + + const register_size_bits = 32; + const register_size_bytes = register_size_bits / 8; + + try expectEqual(register_size_bits, @bitSizeOf(TxData1)); + try expectEqual(register_size_bytes, @sizeOf(TxData1)); + + try expectEqual(register_size_bits, @bitSizeOf(TxData2)); + try expectEqual(register_size_bytes, @sizeOf(TxData2)); + + const TxData3 = packed struct { a: u32, b: [3]u8 }; + const TxData4 = packed struct { a: u32, b: u24 }; + const TxData5 = packed struct { a: [3]u8, b: u32 }; + const TxData6 = packed struct { a: u24, b: u32 }; + + const expectedBitSize = 56; + const expectedByteSize = expectedBitSize / 8; + + try expectEqual(expectedBitSize, @bitSizeOf(TxData3)); + try expectEqual(expectedByteSize, @sizeOf(TxData3)); + + try expectEqual(expectedBitSize, @bitSizeOf(TxData4)); + try expectEqual(expectedByteSize, @sizeOf(TxData4)); + + try expectEqual(expectedBitSize, @bitSizeOf(TxData5)); + try expectEqual(expectedByteSize, @sizeOf(TxData5)); + + try expectEqual(expectedBitSize, @bitSizeOf(TxData6)); + try expectEqual(expectedByteSize, @sizeOf(TxData6)); +} + +test "correct sizeOf and offsets in packed structs" { + if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + + const PStruct = packed struct { + bool_a: bool, + bool_b: bool, + bool_c: bool, + bool_d: bool, + bool_e: bool, + bool_f: bool, + u1_a: u1, + bool_g: bool, + u1_b: u1, + u3_a: u3, + u10_a: u10, + u10_b: u10, + }; + try expectEqual(0, @offsetOf(PStruct, "bool_a")); + try expectEqual(0, @bitOffsetOf(PStruct, "bool_a")); + try expectEqual(0, @offsetOf(PStruct, "bool_b")); + try expectEqual(1, @bitOffsetOf(PStruct, "bool_b")); + try expectEqual(0, @offsetOf(PStruct, "bool_c")); + try expectEqual(2, @bitOffsetOf(PStruct, "bool_c")); + try expectEqual(0, @offsetOf(PStruct, "bool_d")); + try expectEqual(3, @bitOffsetOf(PStruct, "bool_d")); + try expectEqual(0, @offsetOf(PStruct, "bool_e")); + try expectEqual(4, @bitOffsetOf(PStruct, "bool_e")); + try expectEqual(0, @offsetOf(PStruct, "bool_f")); + try expectEqual(5, @bitOffsetOf(PStruct, "bool_f")); + try expectEqual(0, @offsetOf(PStruct, "u1_a")); + try expectEqual(6, @bitOffsetOf(PStruct, "u1_a")); + try expectEqual(0, @offsetOf(PStruct, "bool_g")); + try expectEqual(7, @bitOffsetOf(PStruct, "bool_g")); + try expectEqual(1, @offsetOf(PStruct, "u1_b")); + try expectEqual(8, @bitOffsetOf(PStruct, "u1_b")); + try expectEqual(1, @offsetOf(PStruct, "u3_a")); + try expectEqual(9, @bitOffsetOf(PStruct, "u3_a")); + try expectEqual(1, @offsetOf(PStruct, "u10_a")); + try expectEqual(12, @bitOffsetOf(PStruct, "u10_a")); + try expectEqual(2, @offsetOf(PStruct, "u10_b")); + try expectEqual(22, @bitOffsetOf(PStruct, "u10_b")); + try expectEqual(4, @sizeOf(PStruct)); + + if (native_endian == .Little) { + const s1 = @bitCast(PStruct, @as(u32, 0x12345678)); + try expectEqual(false, s1.bool_a); + try expectEqual(false, s1.bool_b); + try expectEqual(false, s1.bool_c); + try expectEqual(true, s1.bool_d); + try expectEqual(true, s1.bool_e); + try expectEqual(true, s1.bool_f); + try expectEqual(@as(u1, 1), s1.u1_a); + try expectEqual(false, s1.bool_g); + try expectEqual(@as(u1, 0), s1.u1_b); + try expectEqual(@as(u3, 3), s1.u3_a); + try expectEqual(@as(u10, 0b1101000101), s1.u10_a); + try expectEqual(@as(u10, 0b0001001000), s1.u10_b); + + const s2 = @bitCast(packed struct { x: u1, y: u7, z: u24 }, @as(u32, 0xd5c71ff4)); + try expectEqual(@as(u1, 0), s2.x); + try expectEqual(@as(u7, 0b1111010), s2.y); + try expectEqual(@as(u24, 0xd5c71f), s2.z); + } + + const S = packed struct { a: u32, pad: [3]u32, b: u32 }; + + try expectEqual(16, @offsetOf(S, "b")); + try expectEqual(128, @bitOffsetOf(S, "b")); + try expectEqual(20, @sizeOf(S)); +} + +test "nested packed structs" { + if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + + const S1 = packed struct { a: u8, b: u8, c: u8 }; + + const S2 = packed struct { d: u8, e: u8, f: u8 }; + + const S3 = packed struct { x: S1, y: S2 }; + const S3Padded = packed struct { s3: S3, pad: u16 }; + + try expectEqual(48, @bitSizeOf(S3)); + try expectEqual(6, @sizeOf(S3)); + + try expectEqual(3, @offsetOf(S3, "y")); + try expectEqual(24, @bitOffsetOf(S3, "y")); + + if (native_endian == .Little) { + const s3 = @bitCast(S3Padded, @as(u64, 0xe952d5c71ff4)).s3; + try expectEqual(@as(u8, 0xf4), s3.x.a); + try expectEqual(@as(u8, 0x1f), s3.x.b); + try expectEqual(@as(u8, 0xc7), s3.x.c); + try expectEqual(@as(u8, 0xd5), s3.y.d); + try expectEqual(@as(u8, 0x52), s3.y.e); + try expectEqual(@as(u8, 0xe9), s3.y.f); + } + + const S4 = packed struct { a: i32, b: i8 }; + const S5 = packed struct { a: i32, b: i8, c: S4 }; + const S6 = packed struct { a: i32, b: S4, c: i8 }; + + const expectedBitSize = 80; + const expectedByteSize = expectedBitSize / 8; + try expectEqual(expectedBitSize, @bitSizeOf(S5)); + try expectEqual(expectedByteSize, @sizeOf(S5)); + try expectEqual(expectedBitSize, @bitSizeOf(S6)); + try expectEqual(expectedByteSize, @sizeOf(S6)); + + try expectEqual(5, @offsetOf(S5, "c")); + try expectEqual(40, @bitOffsetOf(S5, "c")); + try expectEqual(9, @offsetOf(S6, "c")); + try expectEqual(72, @bitOffsetOf(S6, "c")); +} diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 4bed1f56cc..ccfcd7d74f 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -422,11 +422,21 @@ test "packed struct 24bits" { if (builtin.cpu.arch == .arm) return error.SkipZigTest; // TODO comptime { - try expect(@sizeOf(Foo24Bits) == 4); - if (@sizeOf(usize) == 4) { - try expect(@sizeOf(Foo96Bits) == 12); + // TODO Remove if and leave only the else branch when it is also fixed in stage2 + if (builtin.zig_backend == .stage2_llvm or builtin.zig_backend == .stage2_x86 or + builtin.zig_backend == .stage2_riscv64) + { + // Stage 2 still expects the wrong values + try expect(@sizeOf(Foo24Bits) == 4); + if (@sizeOf(usize) == 4) { + try expect(@sizeOf(Foo96Bits) == 12); + } else { + try expect(@sizeOf(Foo96Bits) == 16); + } } else { - try expect(@sizeOf(Foo96Bits) == 16); + // Stage1 is now fixed and is expected to return right values + try expectEqual(@sizeOf(Foo24Bits), 3); + try expectEqual(@sizeOf(Foo96Bits), 12); } } From 1803167db2d5045e71015498b8e5b184c53edf07 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Fri, 25 Mar 2022 09:30:53 +0100 Subject: [PATCH 0939/2031] AIR: change signature of overflow arithmetic instructions add_with_overflow and similar functions now have the ty_pl data attached. The Payload will now be a binary operation and the inst is expected to return a tuple consisting of the destination integer type and an overflow bit (u1). Co-authored-by: Jan Philipp Hafer --- src/Air.zig | 38 ++++++++++++++++---------------------- src/print_air.zig | 10 ++++------ 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 5120e0fd67..a0fb512934 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -134,28 +134,24 @@ pub const Inst = struct { /// Uses the `bin_op` field. min, /// Integer addition with overflow. Both operands are guaranteed to be the same type, - /// and the result is bool. The wrapped value is written to the pointer given by the in - /// operand of the `pl_op` field. Payload is `Bin` with `lhs` and `rhs` the relevant types - /// of the operation. - /// Uses the `pl_op` field with payload `Bin`. + /// and the result is a tuple with .{res, ov}. The wrapped value is written to res + /// and if an overflow happens, ov is 1. Otherwise ov is 0. + /// Uses the `ty_pl` field. Payload is `Bin`. add_with_overflow, /// Integer subtraction with overflow. Both operands are guaranteed to be the same type, - /// and the result is bool. The wrapped value is written to the pointer given by the in - /// operand of the `pl_op` field. Payload is `Bin` with `lhs` and `rhs` the relevant types - /// of the operation. - /// Uses the `pl_op` field with payload `Bin`. + /// and the result is a tuple with .{res, ov}. The wrapped value is written to res + /// and if an overflow happens, ov is 1. Otherwise ov is 0. + /// Uses the `ty_pl` field. Payload is `Bin`. sub_with_overflow, /// Integer multiplication with overflow. Both operands are guaranteed to be the same type, - /// and the result is bool. The wrapped value is written to the pointer given by the in - /// operand of the `pl_op` field. Payload is `Bin` with `lhs` and `rhs` the relevant types - /// of the operation. - /// Uses the `pl_op` field with payload `Bin`. + /// and the result is a tuple with .{res, ov}. The wrapped value is written to res + /// and if an overflow happens, ov is 1. Otherwise ov is 0. + /// Uses the `ty_pl` field. Payload is `Bin`. mul_with_overflow, /// Integer left-shift with overflow. Both operands are guaranteed to be the same type, - /// and the result is bool. The wrapped value is written to the pointer given by the in - /// operand of the `pl_op` field. Payload is `Bin` with `lhs` and `rhs` the relevant types - /// of the operation. - /// Uses the `pl_op` field with payload `Bin`. + /// and the result is a tuple with .{res, ov}. The wrapped value is written to res + /// and if an overflow happens, ov is 1. Otherwise ov is 0. + /// Uses the `ty_pl` field. Payload is `Bin`. shl_with_overflow, /// Allocates stack local memory. /// Uses the `ty` field. @@ -964,6 +960,10 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .union_init, .field_parent_ptr, .cmp_vector, + .add_with_overflow, + .sub_with_overflow, + .mul_with_overflow, + .shl_with_overflow, => return air.getRefType(datas[inst].ty_pl.ty), .not, @@ -1074,12 +1074,6 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { const extra = air.extraData(Air.Bin, datas[inst].pl_op.payload).data; return air.typeOf(extra.lhs); }, - - .add_with_overflow, - .sub_with_overflow, - .mul_with_overflow, - .shl_with_overflow, - => return Type.bool, } } diff --git a/src/print_air.zig b/src/print_air.zig index f1e51150a6..6552b54faf 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -473,14 +473,12 @@ const Writer = struct { } fn writeOverflow(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const pl_op = w.air.instructions.items(.data)[inst].pl_op; - const extra = w.air.extraData(Air.Bin, pl_op.payload).data; + 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, pl_op.operand); + try w.writeOperand(s, inst, 0, extra.lhs); try s.writeAll(", "); - try w.writeOperand(s, inst, 1, extra.lhs); - try s.writeAll(", "); - try w.writeOperand(s, inst, 2, extra.rhs); + try w.writeOperand(s, inst, 1, extra.rhs); } fn writeMemset(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { From e2468e3f2732a7a301f4174eb4d99bca5315a643 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Fri, 25 Mar 2022 21:35:28 +0100 Subject: [PATCH 0940/2031] Sema: change zirOverflowArithmetic to use new version of AIR insts Also applies the change to Liveness --- src/Liveness.zig | 11 ++++++++--- src/Sema.zig | 28 ++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/Liveness.zig b/src/Liveness.zig index b9f4e6b33a..4b099baf6d 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -508,14 +508,19 @@ fn analyzeInst( }, .memset, .memcpy, + => { + const pl_op = inst_datas[inst].pl_op; + const extra = a.air.extraData(Air.Bin, pl_op.payload).data; + return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs }); + }, .add_with_overflow, .sub_with_overflow, .mul_with_overflow, .shl_with_overflow, => { - const pl_op = inst_datas[inst].pl_op; - const extra = a.air.extraData(Air.Bin, pl_op.payload).data; - return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs }); + const ty_pl = inst_datas[inst].ty_pl; + const extra = a.air.extraData(Air.Bin, ty_pl.payload).data; + return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none }); }, .br => { const br = inst_datas[inst].br; diff --git a/src/Sema.zig b/src/Sema.zig index f41528eca4..d2295a01a8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9064,6 +9064,18 @@ fn zirOverflowArithmetic( const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs); const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs); + const types = try sema.arena.alloc(Type, 2); + const values = try sema.arena.alloc(Value, 2); + const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{ + .types = types, + .values = values, + }); + + types[0] = dest_ty; + types[1] = Type.initTag(.u1); + values[0] = Value.initTag(.unreachable_value); + values[1] = Value.initTag(.unreachable_value); + const result: struct { overflowed: enum { yes, no, undef }, wrapped: Air.Inst.Ref, @@ -9188,16 +9200,24 @@ fn zirOverflowArithmetic( }; try sema.requireRuntimeBlock(block, src); - return block.addInst(.{ + + const tuple = try block.addInst(.{ .tag = air_tag, - .data = .{ .pl_op = .{ - .operand = ptr, - .payload = try sema.addExtra(Air.Bin{ + .data = .{ .ty_pl = .{ + .ty = try block.sema.addType(tuple_ty), + .payload = try block.sema.addExtra(Air.Bin{ .lhs = lhs, .rhs = rhs, }), } }, }); + + const wrapped = try block.addStructFieldVal(tuple, 0, dest_ty); + try sema.storePtr2(block, src, ptr, ptr_src, wrapped, src, .store); + + const overflow_bit = try block.addStructFieldVal(tuple, 1, Type.initTag(.u1)); + const zero_u1 = try sema.addConstant(Type.initTag(.u1), Value.zero); + return try block.addBinOp(.cmp_neq, overflow_bit, zero_u1); }; try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store); From 9070ad77740477f7f3806bad884b4070d476a68c Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 26 Mar 2022 12:04:52 +0100 Subject: [PATCH 0941/2031] stage2 LLVM: Adjust to new AIR arithmetic overflow instructions --- src/codegen/llvm.zig | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 00074d69d1..df7f05948d 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5189,14 +5189,12 @@ pub const FuncGen = struct { if (self.liveness.isUnused(inst)) return null; - const pl_op = self.air.instructions.items(.data)[inst].pl_op; - const extra = self.air.extraData(Air.Bin, pl_op.payload).data; + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - const ptr = try self.resolveInst(pl_op.operand); const lhs = try self.resolveInst(extra.lhs); const rhs = try self.resolveInst(extra.rhs); - const ptr_ty = self.air.typeOf(pl_op.operand); const lhs_ty = self.air.typeOf(extra.lhs); const intrinsic_name = if (lhs_ty.isSignedInt()) signed_intrinsic else unsigned_intrinsic; @@ -5205,13 +5203,7 @@ pub const FuncGen = struct { const llvm_fn = self.getIntrinsic(intrinsic_name, &.{llvm_lhs_ty}); const result_struct = self.builder.buildCall(llvm_fn, &[_]*const llvm.Value{ lhs, rhs }, 2, .Fast, .Auto, ""); - - const result = self.builder.buildExtractValue(result_struct, 0, ""); - const overflow_bit = self.builder.buildExtractValue(result_struct, 1, ""); - - self.store(ptr, ptr_ty, result, .NotAtomic); - - return overflow_bit; + return result_struct; } fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { @@ -5293,16 +5285,16 @@ pub const FuncGen = struct { if (self.liveness.isUnused(inst)) return null; - const pl_op = self.air.instructions.items(.data)[inst].pl_op; - const extra = self.air.extraData(Air.Bin, pl_op.payload).data; + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - const ptr = try self.resolveInst(pl_op.operand); const lhs = try self.resolveInst(extra.lhs); const rhs = try self.resolveInst(extra.rhs); - const ptr_ty = self.air.typeOf(pl_op.operand); const lhs_ty = self.air.typeOf(extra.lhs); const rhs_ty = self.air.typeOf(extra.rhs); + const dest_ty = self.air.typeOfIndex(inst); + const llvm_dest_ty = try self.dg.llvmType(dest_ty); const tg = self.dg.module.getTarget(); @@ -5319,9 +5311,8 @@ pub const FuncGen = struct { const overflow_bit = self.builder.buildICmp(.NE, lhs, reconstructed, ""); - self.store(ptr, ptr_ty, result, .NotAtomic); - - return overflow_bit; + const partial = self.builder.buildInsertValue(llvm_dest_ty.getUndef(), result, 0, ""); + return self.builder.buildInsertValue(partial, overflow_bit, 1, ""); } fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { From 97448e4d5f6a4c1f9eb7ed11d8b147c0883168c2 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 26 Mar 2022 17:14:56 +0100 Subject: [PATCH 0942/2031] wasm: Only generate import when referenced Rather than creating an import for externs on updateDecl, we now generate them when they're referenced. This is required so using @TypeOf(extern_fn()) will not emit the import into the binary (causing an incorrect function type index as it won't be fully analyzed). --- src/arch/wasm/CodeGen.zig | 7 ++++++- src/link/Wasm.zig | 30 +++++++++++++----------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 7ab4a33be9..db09e0bfcf 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -64,7 +64,7 @@ const WValue = union(enum) { /// loads and stores without requiring checks everywhere. fn offset(self: WValue) u32 { switch (self) { - .stack_offset => |offset| return offset, + .stack_offset => |stack_offset| return stack_offset, else => return 0, } } @@ -1549,6 +1549,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. var func_type = try genFunctype(self.gpa, ext_decl.ty, self.target); defer func_type.deinit(self.gpa); ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); + try self.bin_file.addOrUpdateImport(ext_decl); break :blk ext_decl; } else if (func_val.castTag(.decl_ref)) |decl_ref| { break :blk decl_ref.data; @@ -1939,6 +1940,10 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { const decl = decl_ref.data; return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl); } + if (val.castTag(.decl_ref_mut)) |decl_ref| { + const decl = decl_ref.data.decl; + return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl); + } const target = self.target; diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 2eb102c752..c717b42bb6 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -496,8 +496,6 @@ pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void { atom.sym_index = @intCast(u32, self.symbols.items.len); self.symbols.appendAssumeCapacity(symbol); } - - try self.resolved_symbols.putNoClobber(self.base.allocator, atom.symbolLoc(), {}); try self.symbol_atom.putNoClobber(self.base.allocator, atom.symbolLoc(), atom); } @@ -552,7 +550,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { decl.link.wasm.clear(); if (decl.isExtern()) { - return self.addOrUpdateImport(decl); + return; } if (decl.val.castTag(.function)) |_| { @@ -588,10 +586,6 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { } fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void { - if (decl.isExtern()) { - return self.addOrUpdateImport(decl); - } - if (code.len == 0) return; const atom: *Atom = &decl.link.wasm; atom.size = @intCast(u32, code.len); @@ -602,6 +596,8 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void { defer self.base.allocator.free(full_name); symbol.name = try self.string_table.put(self.base.allocator, full_name); try atom.code.appendSlice(self.base.allocator, code); + + try self.resolved_symbols.put(self.base.allocator, atom.symbolLoc(), {}); } /// Lowers a constant typed value to a local symbol and atom. @@ -831,10 +827,10 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { } if (decl.isExtern()) { - assert(self.imports.remove(atom.symbolLoc())); + _ = self.imports.remove(atom.symbolLoc()); } - assert(self.resolved_symbols.swapRemove(atom.symbolLoc())); - _ = self.symbol_atom.remove(atom.symbolLoc()); // not all decl's exist in symbol_atom + _ = self.resolved_symbols.swapRemove(atom.symbolLoc()); + _ = self.symbol_atom.remove(atom.symbolLoc()); atom.deinit(self.base.allocator); } @@ -855,19 +851,19 @@ fn mapFunctionTable(self: *Wasm) void { } } -fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void { +pub fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void { // For the import name itself, we use the decl's name, rather than the fully qualified name const decl_name_index = try self.string_table.put(self.base.allocator, mem.sliceTo(decl.name, 0)); const symbol_index = decl.link.wasm.sym_index; const symbol: *Symbol = &self.symbols.items[symbol_index]; symbol.setUndefined(true); symbol.setGlobal(true); - try self.globals.putNoClobber( - self.base.allocator, - decl_name_index, - .{ .file = null, .index = symbol_index }, - ); - try self.resolved_symbols.put(self.base.allocator, .{ .file = null, .index = symbol_index }, {}); + const global_gop = try self.globals.getOrPut(self.base.allocator, decl_name_index); + if (!global_gop.found_existing) { + const loc: SymbolLoc = .{ .file = null, .index = symbol_index }; + global_gop.value_ptr.* = loc; + try self.resolved_symbols.put(self.base.allocator, loc, {}); + } switch (decl.ty.zigTypeTag()) { .Fn => { From 16e88b75ba3e2eb13dbbf0de9194c3bf1a01026e Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 26 Mar 2022 17:18:47 +0100 Subject: [PATCH 0943/2031] wasm: Enable passing tests --- test/behavior/basic.zig | 5 ----- test/behavior/bitcast.zig | 1 - test/behavior/bugs/4328.zig | 5 ----- test/behavior/ptrcast.zig | 5 ----- test/behavior/type_info.zig | 2 -- 5 files changed, 18 deletions(-) diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 9931c10f9a..e0e0f25569 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -336,7 +336,6 @@ test "call result of if else expression" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO try expect(mem.eql(u8, f2(true), "a")); try expect(mem.eql(u8, f2(false), "b")); @@ -616,8 +615,6 @@ test "global variable initialized to global variable array element" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - try expect(global_ptr == &gdt[0]); } const GDTEntry = struct { @@ -660,7 +657,6 @@ test "multiline string literal is null terminated" { } test "string escapes" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @@ -733,7 +729,6 @@ test "thread local variable" { } test "result location is optional inside error union" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index 99451e8442..6be3cc15c4 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -241,7 +241,6 @@ test "bitcast packed struct literal to byte" { } test "comptime bitcast used in expression has the correct type" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; const Foo = packed struct { diff --git a/test/behavior/bugs/4328.zig b/test/behavior/bugs/4328.zig index c24017683e..3158cb2c0d 100644 --- a/test/behavior/bugs/4328.zig +++ b/test/behavior/bugs/4328.zig @@ -17,8 +17,6 @@ const S = extern struct { }; test "Extern function calls in @TypeOf" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - const Test = struct { fn test_fn_1(a: anytype, b: anytype) @TypeOf(printf("%d %s\n", a, b)) { return 0; @@ -39,8 +37,6 @@ test "Extern function calls in @TypeOf" { } test "Peer resolution of extern function calls in @TypeOf" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - const Test = struct { fn test_fn() @TypeOf(ftell(null), fputs(null, null)) { return 0; @@ -57,7 +53,6 @@ test "Peer resolution of extern function calls in @TypeOf" { test "Extern function calls, dereferences and field access in @TypeOf" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; const Test = struct { fn test_fn_1(a: c_long) @TypeOf(fopen("test", "r").*) { diff --git a/test/behavior/ptrcast.zig b/test/behavior/ptrcast.zig index 71b2281564..d9a3892664 100644 --- a/test/behavior/ptrcast.zig +++ b/test/behavior/ptrcast.zig @@ -59,7 +59,6 @@ fn testReinterpretStructWrappedBytesAsInteger() !void { test "reinterpret bytes of an array into an extern struct" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO try testReinterpretBytesAsExternStruct(); comptime try testReinterpretBytesAsExternStruct(); @@ -82,7 +81,6 @@ fn testReinterpretBytesAsExternStruct() !void { test "reinterpret bytes of an extern struct (with under-aligned fields) into another" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO: Under-aligned fields are not yet supported in the CBE - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO try testReinterpretExternStructAsExternStruct(); comptime try testReinterpretExternStructAsExternStruct(); @@ -107,7 +105,6 @@ fn testReinterpretExternStructAsExternStruct() !void { test "reinterpret bytes of an extern struct into another" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO try testReinterpretOverAlignedExternStructAsExternStruct(); comptime try testReinterpretOverAlignedExternStructAsExternStruct(); @@ -138,7 +135,6 @@ test "lower reinterpreted comptime field ptr (with under-aligned fields)" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO: CBE does not yet support under-aligned fields - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO // Test lowering a field ptr comptime var bytes align(2) = [_]u8{ 1, 2, 3, 4, 5, 6 }; @@ -162,7 +158,6 @@ test "lower reinterpreted comptime field ptr" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO // Test lowering a field ptr comptime var bytes align(4) = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index b2a9d88be8..55c6e666ee 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -160,7 +160,6 @@ test "type info: error set, error union info, anyerror" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO try testErrorSet(); comptime try testErrorSet(); @@ -192,7 +191,6 @@ test "type info: error set single value" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const TestSet = error.One; From 6a48345649de9d0ad5c948ca1ddd3277de046567 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 26 Mar 2022 15:25:31 -0700 Subject: [PATCH 0944/2031] Sema: remove redundant `mul_add` comptime vectorization Moving comptime vectorization into `Value` has proven to simplify and clean up a lot of code. --- src/Sema.zig | 48 ++---------------------------------------------- 1 file changed, 2 insertions(+), 46 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index d2295a01a8..b7580511cc 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -15190,52 +15190,8 @@ fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. if (maybe_addend) |addend_val| { if (addend_val.isUndef()) return sema.addConstUndef(ty); - - switch (ty.zigTypeTag()) { - .ComptimeFloat, .Float => { - const result_val = try Value.mulAdd( - ty, - mulend1_val, - mulend2_val, - addend_val, - sema.arena, - target, - ); - return sema.addConstant(ty, result_val); - }, - .Vector => { - const scalar_ty = ty.scalarType(); - switch (scalar_ty.zigTypeTag()) { - .ComptimeFloat, .Float => {}, - else => return sema.fail(block, src, "expected vector of floats, found vector of '{}'", .{scalar_ty.fmt(target)}), - } - - const vec_len = ty.vectorLen(); - const result_ty = try Type.vector(sema.arena, vec_len, scalar_ty); - var mulend1_buf: Value.ElemValueBuffer = undefined; - var mulend2_buf: Value.ElemValueBuffer = undefined; - var addend_buf: Value.ElemValueBuffer = undefined; - const elems = try sema.arena.alloc(Value, vec_len); - for (elems) |*elem, i| { - const mulend1_elem_val = mulend1_val.elemValueBuffer(i, &mulend1_buf); - const mulend2_elem_val = mulend2_val.elemValueBuffer(i, &mulend2_buf); - const addend_elem_val = addend_val.elemValueBuffer(i, &addend_buf); - elem.* = try Value.mulAdd( - scalar_ty, - mulend1_elem_val, - mulend2_elem_val, - addend_elem_val, - sema.arena, - target, - ); - } - return sema.addConstant( - result_ty, - try Value.Tag.aggregate.create(sema.arena, elems), - ); - }, - else => unreachable, - } + const result_val = try Value.mulAdd(ty, mulend1_val, mulend2_val, addend_val, sema.arena, target); + return sema.addConstant(ty, result_val); } else { break :rs addend_src; } From adc2824fa405beeea25edacd9362d1f66f8f9520 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 26 Mar 2022 15:30:13 -0700 Subject: [PATCH 0945/2031] stage2: simplify `scalar_ty` for `mul_add` in LLVM backend --- src/codegen/llvm.zig | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index df7f05948d..049f7bfad6 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5218,6 +5218,7 @@ pub const FuncGen = struct { const ty = self.air.typeOfIndex(inst); const llvm_ty = try self.dg.llvmType(ty); + const scalar_ty = ty.scalarType(); const target = self.dg.module.getTarget(); const Strat = union(enum) { @@ -5225,11 +5226,6 @@ pub const FuncGen = struct { libc: [*:0]const u8, }; - const scalar_ty = if (ty.zigTypeTag() == .Vector) - ty.elemType() - else - ty; - const strat: Strat = switch (scalar_ty.floatBits(target)) { 16, 32, 64 => Strat.intrinsic, 80 => if (CType.longdouble.sizeInBits(target) == 80) Strat{ .intrinsic = {} } else Strat{ .libc = "__fmax" }, From 29c32b3dc50218d15f779201e154d425fffcefeb Mon Sep 17 00:00:00 2001 From: jagt Date: Fri, 25 Mar 2022 22:53:58 +0800 Subject: [PATCH 0946/2031] `Namespace.decls` use context to save memory change `Module.Namespace.decls` from `AutoArrayHashMapUnmanaged` to `ArrayHashMapUnmanaged(*Decl, void)` with custom context to eliminate duplicated decl name strings. Also see: https://zig.news/andrewrk/how-to-use-hash-map-contexts-to-save-memory-when-doing-a-string-table-3l33 --- src/Module.zig | 47 ++++++++++++++++++++++++++++++++++++----------- src/Sema.zig | 9 ++++----- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 71e2bd8d7c..e74d5f1dd8 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1591,6 +1591,19 @@ pub const Var = struct { } }; +pub const DeclAdapter = struct { + pub fn hash(self: @This(), s: []const u8) u32 { + _ = self; + return @truncate(u32, std.hash.Wyhash.hash(0, s)); + } + + pub fn eql(self: @This(), a: []const u8, b_decl: *Decl, b_index: usize) bool { + _ = self; + _ = b_index; + return mem.eql(u8, a, mem.sliceTo(b_decl.name, 0)); + } +}; + /// The container that structs, enums, unions, and opaques have. pub const Namespace = struct { parent: ?*Namespace, @@ -1601,9 +1614,8 @@ pub const Namespace = struct { /// which decls have been added/removed from source. /// Declaration order is preserved via entry order. /// Key memory is owned by `decl.name`. - /// TODO save memory with https://github.com/ziglang/zig/issues/8619. /// Anonymous decls are not stored here; they are kept in `anon_decls` instead. - decls: std.StringArrayHashMapUnmanaged(*Decl) = .{}, + decls: std.ArrayHashMapUnmanaged(*Decl, void, DeclContext, true) = .{}, anon_decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, @@ -1612,6 +1624,19 @@ pub const Namespace = struct { /// Value is whether the usingnamespace decl is marked `pub`. usingnamespace_set: std.AutoHashMapUnmanaged(*Decl, bool) = .{}, + const DeclContext = struct { + pub fn hash(self: @This(), decl: *Decl) u32 { + _ = self; + return @truncate(u32, std.hash.Wyhash.hash(0, mem.sliceTo(decl.name, 0))); + } + + pub fn eql(self: @This(), a: *Decl, b: *Decl, b_index: usize) bool { + _ = self; + _ = b_index; + return mem.eql(u8, mem.sliceTo(a.name, 0), mem.sliceTo(b.name, 0)); + } + }; + pub fn deinit(ns: *Namespace, mod: *Module) void { ns.destroyDecls(mod); ns.* = undefined; @@ -1628,8 +1653,8 @@ pub const Namespace = struct { var anon_decls = ns.anon_decls; ns.anon_decls = .{}; - for (decls.values()) |value| { - value.destroy(mod); + for (decls.keys()) |decl| { + decl.destroy(mod); } decls.deinit(gpa); @@ -1658,7 +1683,7 @@ pub const Namespace = struct { // TODO rework this code to not panic on OOM. // (might want to coordinate with the clearDecl function) - for (decls.values()) |child_decl| { + for (decls.keys()) |child_decl| { mod.clearDecl(child_decl, outdated_decls) catch @panic("out of memory"); child_decl.destroy(mod); } @@ -3325,7 +3350,7 @@ fn updateZirRefs(gpa: Allocator, file: *File, old_zir: Zir) !void { } if (decl.getInnerNamespace()) |namespace| { - for (namespace.decls.values()) |sub_decl| { + for (namespace.decls.keys()) |sub_decl| { try decl_stack.append(gpa, sub_decl); } for (namespace.anon_decls.keys()) |sub_decl| { @@ -4420,7 +4445,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi if (is_usingnamespace) try namespace.usingnamespace_set.ensureUnusedCapacity(gpa, 1); // We create a Decl for it regardless of analysis status. - const gop = try namespace.decls.getOrPut(gpa, decl_name); + const gop = try namespace.decls.getOrPutAdapted(gpa, @as([]const u8, mem.sliceTo(decl_name, 0)), DeclAdapter{}); if (!gop.found_existing) { const new_decl = try mod.allocateNewDecl(decl_name, namespace, decl_node, iter.parent_decl.src_scope); if (is_usingnamespace) { @@ -4428,7 +4453,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi } log.debug("scan new {*} ({s}) into {*}", .{ new_decl, decl_name, namespace }); new_decl.src_line = line; - gop.value_ptr.* = new_decl; + gop.key_ptr.* = new_decl; // Exported decls, comptime decls, usingnamespace decls, and // test decls if in test mode, get analyzed. const decl_pkg = namespace.file_scope.pkg; @@ -4464,7 +4489,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi return; } gpa.free(decl_name); - const decl = gop.value_ptr.*; + const decl = gop.key_ptr.*; log.debug("scan existing {*} ({s}) of {*}", .{ decl, decl.name, namespace }); // Update the AST node of the decl; even if its contents are unchanged, it may // have been re-ordered. @@ -5333,7 +5358,7 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { // Remove from the namespace it resides in, preserving declaration order. assert(decl.zir_decl_index != 0); - _ = decl.src_namespace.decls.orderedRemove(mem.sliceTo(decl.name, 0)); + _ = decl.src_namespace.decls.orderedRemoveAdapted(@as([]const u8, mem.sliceTo(decl.name, 0)), DeclAdapter{}); try mod.clearDecl(decl, &outdated_decls); decl.destroy(mod); @@ -5400,7 +5425,7 @@ pub fn populateTestFunctions(mod: *Module) !void { const builtin_pkg = mod.main_pkg.table.get("builtin").?; const builtin_file = (mod.importPkg(builtin_pkg) catch unreachable).file; const builtin_namespace = builtin_file.root_decl.?.src_namespace; - const decl = builtin_namespace.decls.get("test_functions").?; + const decl = builtin_namespace.decls.getKeyAdapted(@as([]const u8, "test_functions"), DeclAdapter{}).?; var buf: Type.SlicePtrFieldTypeBuffer = undefined; const tmp_test_fn_ty = decl.ty.slicePtrFieldType(&buf).elemType(); diff --git a/src/Sema.zig b/src/Sema.zig index b7580511cc..121a5a20ac 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4516,7 +4516,7 @@ fn lookupInNamespace( while (check_i < checked_namespaces.count()) : (check_i += 1) { const check_ns = checked_namespaces.keys()[check_i]; - if (check_ns.decls.get(ident_name)) |decl| { + if (check_ns.decls.getKeyAdapted(ident_name, Module.DeclAdapter{})) |decl| { // Skip decls which are not marked pub, which are in a different // file than the `a.b`/`@hasDecl` syntax. if (decl.is_pub or src_file == decl.getFileScope()) { @@ -4559,7 +4559,7 @@ fn lookupInNamespace( return sema.failWithOwnedErrorMsg(block, msg); }, } - } else if (namespace.decls.get(ident_name)) |decl| { + } else if (namespace.decls.getKeyAdapted(ident_name, Module.DeclAdapter{})) |decl| { try mod.declareDeclDependency(sema.owner_decl, decl); return decl; } @@ -11502,12 +11502,11 @@ fn typeInfoDecls( const decls_len = if (opt_namespace) |ns| ns.decls.count() else 0; const decls_vals = try decls_anon_decl.arena().alloc(Value, decls_len); for (decls_vals) |*decls_val, i| { - const decl = opt_namespace.?.decls.values()[i]; - const name = opt_namespace.?.decls.keys()[i]; + const decl = opt_namespace.?.decls.keys()[i]; const name_val = v: { var anon_decl = try block.startAnonDecl(src); defer anon_decl.deinit(); - const bytes = try anon_decl.arena().dupeZ(u8, name); + const bytes = try anon_decl.arena().dupeZ(u8, mem.sliceTo(decl.name, 0)); const new_decl = try anon_decl.finish( try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), From a3030221c3bb3f3d59cedc6db83c679470fca417 Mon Sep 17 00:00:00 2001 From: Yorhel Date: Fri, 25 Mar 2022 07:47:15 +0100 Subject: [PATCH 0947/2031] std.fs: Handle EINVAL from linux.getdents64 Fixes #11178 --- lib/std/fs.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 7a41bdf6a1..73efccbbfc 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -605,7 +605,7 @@ pub const Dir = struct { .BADF => unreachable, // Dir is invalid or was opened without iteration ability .FAULT => unreachable, .NOTDIR => unreachable, - .INVAL => unreachable, + .INVAL => return error.Unexpected, // Linux may in some cases return EINVAL when reading /proc/$PID/net. else => |err| return os.unexpectedErrno(err), } if (rc == 0) return null; From 3beef3945c87c589feebf50ed985bb6e65887110 Mon Sep 17 00:00:00 2001 From: tecanec <39187961+tecanec@users.noreply.github.com> Date: Sun, 27 Mar 2022 10:28:44 +0200 Subject: [PATCH 0948/2031] std: SIMD utility functions This file contains a collections of functions that may be useful for SIMD, such as generating a vector with a linear range of numbers starting at zero, joining two vectors together, getting the index of the first true in a vector of bools, etc. --- lib/std/simd.zig | 409 +++++++++++++++++++++++++++++++++++++++++++++++ lib/std/std.zig | 1 + 2 files changed, 410 insertions(+) create mode 100644 lib/std/simd.zig diff --git a/lib/std/simd.zig b/lib/std/simd.zig new file mode 100644 index 0000000000..50e0e99d37 --- /dev/null +++ b/lib/std/simd.zig @@ -0,0 +1,409 @@ +//! This module provides functions for working conveniently with SIMD (Single Instruction; Multiple Data), +//! which may offer a potential boost in performance on some targets by performing the same operations on +//! multiple elements at once. +//! Please be aware that some functions are known to not work on MIPS. + +const std = @import("std"); +const builtin = @import("builtin"); + +pub const Vector = std.meta.Vector; + +pub fn suggestVectorSizeForCpu(comptime T: type, cpu: std.Target.Cpu) ?usize { + switch (cpu.arch) { + .x86_64 => { + // Note: This is mostly just guesswork. It'd be great if someone more qualified were to take a + // proper look at this. + + if (T == bool and std.Target.x86.featureSetHas(.prefer_mask_registers)) return 64; + + const vector_bit_size = blk: { + if (std.Target.x86.featureSetHas(.avx512f)) break :blk 512; + if (std.Target.x86.featureSetHas(.prefer_256_bit)) break :blk 256; + if (std.Target.x86.featureSetHas(.prefer_128_bit)) break :blk 128; + return null; + }; + const element_bit_size = std.math.max(8, std.math.ceilPowerOfTwo(T, @bitSizeOf(T))); + return @divExact(vector_bit_size, element_bit_size); + }, + else => @compileError("No vector sizes for this CPU architecture have yet been recommended"), + } +} + +/// Suggests a target-dependant vector size for a given type, or null if scalars are recommended. +/// Not yet implemented for every CPU architecture. +pub fn suggestVectorSize(comptime T: type) ?usize { + return suggestVectorSizeForCpu(T, builtin.cpu); +} + +fn vectorLength(comptime VectorType: type) comptime_int { + return switch (@typeInfo(VectorType)) { + .Vector => |info| info.len, + .Array => |info| info.len, + else => @compileError("Invalid type " ++ @typeName(VectorType)), + }; +} + +/// Returns the smallest type of unsigned ints capable of indexing any element within the given vector type. +pub fn VectorIndex(comptime VectorType: type) type { + return std.math.IntFittingRange(0, vectorLength(VectorType) - 1); +} + +/// Returns the smallest type of unsigned ints capable of holding the length of the given vector type. +pub fn VectorCount(comptime VectorType: type) type { + return std.math.IntFittingRange(0, vectorLength(VectorType)); +} + +/// Returns a vector containing the first `len` integers in order from 0 to `len`-1. +/// For example, `iota(i32, 8)` will return a vector containing `.{0, 1, 2, 3, 4, 5, 6, 7}`. +pub fn iota(comptime T: type, comptime len: usize) Vector(len, T) { + var out: [len]T = undefined; + for (out) |*element, i| { + element.* = switch (@typeInfo(T)) { + .Int => @intCast(T, i), + .Float => @intToFloat(T, i), + else => @compileError("Can't use type " ++ @typeName(T) ++ " in iota."), + }; + } + return @as(Vector(len, T), out); +} + +/// Returns a vector containing the same elements as the input, but repeated until the desired length is reached. +/// For example, `repeat(8, [_]u32{1, 2, 3})` will return a vector containing `.{1, 2, 3, 1, 2, 3, 1, 2}`. +pub fn repeat(comptime len: usize, vec: anytype) Vector(len, std.meta.Child(@TypeOf(vec))) { + const Child = std.meta.Child(@TypeOf(vec)); + + return @shuffle(Child, vec, undefined, iota(i32, len) % @splat(len, @intCast(i32, vectorLength(@TypeOf(vec))))); +} + +/// Returns a vector containing all elements of the first vector at the lower indices followed by all elements of the second vector +/// at the higher indices. +pub fn join(a: anytype, b: anytype) Vector(vectorLength(@TypeOf(a)) + vectorLength(@TypeOf(b)), std.meta.Child(@TypeOf(a))) { + const Child = std.meta.Child(@TypeOf(a)); + const a_len = vectorLength(@TypeOf(a)); + const b_len = vectorLength(@TypeOf(b)); + + return @shuffle(Child, a, b, @as([a_len]i32, iota(i32, a_len)) ++ @as([b_len]i32, ~iota(i32, b_len))); +} + +/// Returns a vector whose elements alternates between those of each input vector. +/// For example, `interlace(.{[4]u32{11, 12, 13, 14}, [4]u32{21, 22, 23, 24}})` returns a vector containing `.{11, 21, 12, 22, 13, 23, 14, 24}`. +pub fn interlace(vecs: anytype) Vector(vectorLength(@TypeOf(vecs[0])) * vecs.len, std.meta.Child(@TypeOf(vecs[0]))) { + // interlace doesn't work on MIPS, for some reason. + // Notes from earlier debug attempt: + // The indices are correct. The problem seems to be with the @shuffle builtin. + // On MIPS, the test that interlaces small_base gives { 0, 2, 0, 0, 64, 255, 248, 200, 0, 0 }. + // Calling this with two inputs seems to work fine, but I'll let the compile error trigger for all inputs, just to be safe. + comptime if (builtin.cpu.arch.isMIPS()) @compileError("TODO: Find out why interlace() doesn't work on MIPS"); + + const VecType = @TypeOf(vecs[0]); + const vecs_arr = @as([vecs.len]VecType, vecs); + const Child = std.meta.Child(@TypeOf(vecs_arr[0])); + + if (vecs_arr.len == 1) return vecs_arr[0]; + + const a_vec_count = (1 + vecs_arr.len) >> 1; + const b_vec_count = vecs_arr.len >> 1; + + const a = interlace(@ptrCast(*const [a_vec_count]VecType, vecs_arr[0..a_vec_count]).*); + const b = interlace(@ptrCast(*const [b_vec_count]VecType, vecs_arr[a_vec_count..]).*); + + const a_len = vectorLength(@TypeOf(a)); + const b_len = vectorLength(@TypeOf(b)); + const len = a_len + b_len; + + const indices = comptime blk: { + const count_up = iota(i32, len); + const cycle = @divFloor(count_up, @splat(len, @intCast(i32, vecs_arr.len))); + const select_mask = repeat(len, join(@splat(a_vec_count, true), @splat(b_vec_count, false))); + const a_indices = count_up - cycle * @splat(len, @intCast(i32, b_vec_count)); + const b_indices = shiftElementsRight(count_up - cycle * @splat(len, @intCast(i32, a_vec_count)), a_vec_count, 0); + break :blk @select(i32, select_mask, a_indices, ~b_indices); + }; + + return @shuffle(Child, a, b, indices); +} + +/// The contents of `interlaced` is evenly split between vec_count vectors that are returned as an array. They "take turns", +/// recieving one element from `interlaced` at a time. +pub fn deinterlace( + comptime vec_count: usize, + interlaced: anytype, +) [vec_count]Vector( + vectorLength(@TypeOf(interlaced)) / vec_count, + std.meta.Child(@TypeOf(interlaced)), +) { + const vec_len = vectorLength(@TypeOf(interlaced)) / vec_count; + const Child = std.meta.Child(@TypeOf(interlaced)); + + var out: [vec_count]Vector(vec_len, Child) = undefined; + + comptime var i: usize = 0; // for-loops don't work for this, apparently. + inline while (i < out.len) : (i += 1) { + const indices = comptime iota(i32, vec_len) * @splat(vec_len, @intCast(i32, vec_count)) + @splat(vec_len, @intCast(i32, i)); + out[i] = @shuffle(Child, interlaced, undefined, indices); + } + + return out; +} + +pub fn extract( + vec: anytype, + comptime first: VectorIndex(@TypeOf(vec)), + comptime count: VectorCount(@TypeOf(vec)), +) Vector(count, std.meta.Child(@TypeOf(vec))) { + const Child = std.meta.Child(@TypeOf(vec)); + const len = vectorLength(@TypeOf(vec)); + + std.debug.assert(@intCast(comptime_int, first) + @intCast(comptime_int, count) <= len); + + return @shuffle(Child, vec, undefined, iota(i32, count) + @splat(count, @intCast(i32, first))); +} + +test "vector patterns" { + const base = Vector(4, u32){ 10, 20, 30, 40 }; + const other_base = Vector(4, u32){ 55, 66, 77, 88 }; + + const small_bases = [5]Vector(2, u8){ + Vector(2, u8){ 0, 1 }, + Vector(2, u8){ 2, 3 }, + Vector(2, u8){ 4, 5 }, + Vector(2, u8){ 6, 7 }, + Vector(2, u8){ 8, 9 }, + }; + + try std.testing.expectEqual([6]u32{ 10, 20, 30, 40, 10, 20 }, repeat(6, base)); + try std.testing.expectEqual([8]u32{ 10, 20, 30, 40, 55, 66, 77, 88 }, join(base, other_base)); + try std.testing.expectEqual([2]u32{ 20, 30 }, extract(base, 1, 2)); + + if (comptime !builtin.cpu.arch.isMIPS()) { + try std.testing.expectEqual([8]u32{ 10, 55, 20, 66, 30, 77, 40, 88 }, interlace(.{ base, other_base })); + + const small_braid = interlace(small_bases); + try std.testing.expectEqual([10]u8{ 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 }, small_braid); + try std.testing.expectEqual(small_bases, deinterlace(small_bases.len, small_braid)); + } +} + +/// Joins two vectors, shifts them leftwards (towards lower indices) and extracts the leftmost elements into a vector the size of a and b. +pub fn mergeShift(a: anytype, b: anytype, comptime shift: VectorCount(@TypeOf(a, b))) @TypeOf(a, b) { + const len = vectorLength(@TypeOf(a, b)); + + return extract(join(a, b), shift, len); +} + +/// Elements are shifted rightwards (towards higher indices). New elements are added to the left, and the rightmost elements are cut off +/// so that the size of the vector stays the same. +pub fn shiftElementsRight(vec: anytype, comptime amount: VectorCount(@TypeOf(vec)), shift_in: std.meta.Child(@TypeOf(vec))) @TypeOf(vec) { + // It may be possible to implement shifts and rotates with a runtime-friendly slice of two joined vectors, as the length of the + // slice would be comptime-known. This would permit vector shifts and rotates by a non-comptime-known amount. + // However, I am unsure whether compiler optimizations would handle that well enough on all platforms. + const len = vectorLength(@TypeOf(vec)); + + return mergeShift(@splat(len, shift_in), vec, len - amount); +} + +/// Elements are shifted leftwards (towards lower indices). New elements are added to the right, and the leftmost elements are cut off +/// so that no elements with indices below 0 remain. +pub fn shiftElementsLeft(vec: anytype, comptime amount: VectorCount(@TypeOf(vec)), shift_in: std.meta.Child(@TypeOf(vec))) @TypeOf(vec) { + const len = vectorLength(@TypeOf(vec)); + + return mergeShift(vec, @splat(len, shift_in), amount); +} + +/// Elements are shifted leftwards (towards lower indices). Elements that leave to the left will reappear to the right in the same order. +pub fn rotateElementsLeft(vec: anytype, comptime amount: VectorCount(@TypeOf(vec))) @TypeOf(vec) { + return mergeShift(vec, vec, amount); +} + +/// Elements are shifted rightwards (towards higher indices). Elements that leave to the right will reappear to the left in the same order. +pub fn rotateElementsRight(vec: anytype, comptime amount: VectorCount(@TypeOf(vec))) @TypeOf(vec) { + return rotateElementsLeft(vec, vectorLength(@TypeOf(vec)) - amount); +} + +pub fn reverseOrder(vec: anytype) @TypeOf(vec) { + const Child = std.meta.Child(@TypeOf(vec)); + const len = vectorLength(@TypeOf(vec)); + + return @shuffle(Child, vec, undefined, @splat(len, @intCast(i32, len) - 1) - iota(i32, len)); +} + +test "vector shifting" { + const base = Vector(4, u32){ 10, 20, 30, 40 }; + + try std.testing.expectEqual([4]u32{ 30, 40, 999, 999 }, shiftElementsLeft(base, 2, 999)); + try std.testing.expectEqual([4]u32{ 999, 999, 10, 20 }, shiftElementsRight(base, 2, 999)); + try std.testing.expectEqual([4]u32{ 20, 30, 40, 10 }, rotateElementsLeft(base, 1)); + try std.testing.expectEqual([4]u32{ 40, 10, 20, 30 }, rotateElementsRight(base, 1)); + try std.testing.expectEqual([4]u32{ 40, 30, 20, 10 }, reverseOrder(base)); +} + +pub fn firstTrue(vec: anytype) ?VectorIndex(@TypeOf(vec)) { + const len = vectorLength(@TypeOf(vec)); + const IndexInt = VectorIndex(@TypeOf(vec)); + + if (!@reduce(.Or, vec)) { + return null; + } + const indices = @select(IndexInt, vec, iota(IndexInt, len), @splat(len, ~@as(IndexInt, 0))); + return @reduce(.Min, indices); +} + +pub fn lastTrue(vec: anytype) ?VectorIndex(@TypeOf(vec)) { + const len = vectorLength(@TypeOf(vec)); + const IndexInt = VectorIndex(@TypeOf(vec)); + + if (!@reduce(.Or, vec)) { + return null; + } + const indices = @select(IndexInt, vec, iota(IndexInt, len), @splat(len, @as(IndexInt, 0))); + return @reduce(.Max, indices); +} + +pub fn countTrues(vec: anytype) VectorCount(@TypeOf(vec)) { + const len = vectorLength(@TypeOf(vec)); + const CountIntType = VectorCount(@TypeOf(vec)); + + const one_if_true = @select(CountIntType, vec, @splat(len, @as(CountIntType, 1)), @splat(len, @as(CountIntType, 0))); + return @reduce(.Add, one_if_true); +} + +pub fn firstIndexOfValue(vec: anytype, value: std.meta.Child(@TypeOf(vec))) ?VectorIndex(@TypeOf(vec)) { + const len = vectorLength(@TypeOf(vec)); + + return firstTrue(vec == @splat(len, value)); +} + +pub fn lastIndexOfValue(vec: anytype, value: std.meta.Child(@TypeOf(vec))) ?VectorIndex(@TypeOf(vec)) { + const len = vectorLength(@TypeOf(vec)); + + return lastTrue(vec == @splat(len, value)); +} + +pub fn countElementsWithValue(vec: anytype, value: std.meta.Child(@TypeOf(vec))) VectorCount(@TypeOf(vec)) { + const len = vectorLength(@TypeOf(vec)); + + return countTrues(vec == @splat(len, value)); +} + +test "vector searching" { + const base = Vector(8, u32){ 6, 4, 7, 4, 4, 2, 3, 7 }; + + try std.testing.expectEqual(@as(?u3, 1), firstIndexOfValue(base, 4)); + try std.testing.expectEqual(@as(?u3, 4), lastIndexOfValue(base, 4)); + try std.testing.expectEqual(@as(?u3, null), lastIndexOfValue(base, 99)); + try std.testing.expectEqual(@as(u4, 3), countElementsWithValue(base, 4)); +} + +/// Same as prefixScan, but with a user-provided, mathematically associative function. +pub fn prefixScanWithFunc( + comptime hop: isize, + vec: anytype, + /// The error type that `func` might return. Set this to `void` if `func` doesn't return an error union. + comptime ErrorType: type, + comptime func: fn (@TypeOf(vec), @TypeOf(vec)) if (ErrorType == void) @TypeOf(vec) else ErrorType!@TypeOf(vec), + /// When one operand of the operation performed by `func` is this value, the result must equal the other operand. + /// For example, this should be 0 for addition or 1 for multiplication. + comptime identity: std.meta.Child(@TypeOf(vec)), +) if (ErrorType == void) @TypeOf(vec) else ErrorType!@TypeOf(vec) { + // I haven't debugged this, but it might be a cousin of sorts to what's going on with interlace. + comptime if (builtin.cpu.arch.isMIPS()) @compileError("TODO: Find out why prefixScan doesn't work on MIPS"); + + const len = vectorLength(@TypeOf(vec)); + + if (hop == 0) @compileError("hop can not be 0; you'd be going nowhere forever!"); + const abs_hop = if (hop < 0) -hop else hop; + + var acc = vec; + comptime var i = 0; + inline while ((abs_hop << i) < len) : (i += 1) { + const shifted = if (hop < 0) shiftElementsLeft(acc, abs_hop << i, identity) else shiftElementsRight(acc, abs_hop << i, identity); + + acc = if (ErrorType == void) func(acc, shifted) else try func(acc, shifted); + } + return acc; +} + +/// Returns a vector whose elements are the result of performing the specified operation on the corresponding +/// element of the input vector and every hop'th element that came before it (or after, if hop is negative). +/// Supports the same operations as the @reduce() builtin. Takes O(logN) to compute. +/// The scan is not linear, which may affect floating point errors. This may affect the determinism of +/// algorithms that use this function. +pub fn prefixScan(comptime op: std.builtin.ReduceOp, comptime hop: isize, vec: anytype) @TypeOf(vec) { + const VecType = @TypeOf(vec); + const Child = std.meta.Child(VecType); + const len = vectorLength(VecType); + + const identity = comptime switch (@typeInfo(Child)) { + .Bool => switch (op) { + .Or, .Xor => false, + .And => true, + else => @compileError("Invalid prefixScan operation " ++ @tagName(op) ++ " for vector of booleans."), + }, + .Int => switch (op) { + .Max => std.math.minInt(Child), + .Add, .Or, .Xor => 0, + .Mul => 1, + .And, .Min => std.math.maxInt(Child), + }, + .Float => switch (op) { + .Max => -std.math.inf(Child), + .Add => 0, + .Mul => 1, + .Min => std.math.inf(Child), + else => @compileError("Invalid prefixScan operation " ++ @tagName(op) ++ " for vector of floats."), + }, + else => @compileError("Invalid type " ++ @typeName(VecType) ++ " for prefixScan."), + }; + + const fn_container = struct { + fn opFn(a: VecType, b: VecType) VecType { + return if (Child == bool) switch (op) { + .And => @select(bool, a, b, @splat(len, false)), + .Or => @select(bool, a, @splat(len, true), b), + .Xor => a != b, + else => unreachable, + } else switch (op) { + .And => a & b, + .Or => a | b, + .Xor => a ^ b, + .Add => a + b, + .Mul => a * b, + .Min => @minimum(a, b), + .Max => @maximum(a, b), + }; + } + }; + + return prefixScanWithFunc(hop, vec, void, fn_container.opFn, identity); +} + +test "vector prefix scan" { + if (comptime builtin.cpu.arch.isMIPS()) { + return error.SkipZigTest; + } + + const int_base = Vector(4, i32){ 11, 23, 9, -21 }; + const float_base = Vector(4, f32){ 2, 0.5, -10, 6.54321 }; + const bool_base = Vector(4, bool){ true, false, true, false }; + + try std.testing.expectEqual(iota(u8, 32) + @splat(32, @as(u8, 1)), prefixScan(.Add, 1, @splat(32, @as(u8, 1)))); + try std.testing.expectEqual(Vector(4, i32){ 11, 3, 1, 1 }, prefixScan(.And, 1, int_base)); + try std.testing.expectEqual(Vector(4, i32){ 11, 31, 31, -1 }, prefixScan(.Or, 1, int_base)); + try std.testing.expectEqual(Vector(4, i32){ 11, 28, 21, -2 }, prefixScan(.Xor, 1, int_base)); + try std.testing.expectEqual(Vector(4, i32){ 11, 34, 43, 22 }, prefixScan(.Add, 1, int_base)); + try std.testing.expectEqual(Vector(4, i32){ 11, 253, 2277, -47817 }, prefixScan(.Mul, 1, int_base)); + try std.testing.expectEqual(Vector(4, i32){ 11, 11, 9, -21 }, prefixScan(.Min, 1, int_base)); + try std.testing.expectEqual(Vector(4, i32){ 11, 23, 23, 23 }, prefixScan(.Max, 1, int_base)); + + // Trying to predict all inaccuracies when adding and multiplying floats with prefixScans would be a mess, so we don't test those. + try std.testing.expectEqual(Vector(4, f32){ 2, 0.5, -10, -10 }, prefixScan(.Min, 1, float_base)); + try std.testing.expectEqual(Vector(4, f32){ 2, 2, 2, 6.54321 }, prefixScan(.Max, 1, float_base)); + + try std.testing.expectEqual(Vector(4, bool){ true, true, false, false }, prefixScan(.Xor, 1, bool_base)); + try std.testing.expectEqual(Vector(4, bool){ true, true, true, true }, prefixScan(.Or, 1, bool_base)); + try std.testing.expectEqual(Vector(4, bool){ true, false, false, false }, prefixScan(.And, 1, bool_base)); + + try std.testing.expectEqual(Vector(4, i32){ 11, 23, 20, 2 }, prefixScan(.Add, 2, int_base)); + try std.testing.expectEqual(Vector(4, i32){ 22, 11, -12, -21 }, prefixScan(.Add, -1, int_base)); + try std.testing.expectEqual(Vector(4, i32){ 11, 23, 9, -10 }, prefixScan(.Add, 3, int_base)); +} diff --git a/lib/std/std.zig b/lib/std/std.zig index 5ae09a7f5b..f34d108271 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -79,6 +79,7 @@ pub const pdb = @import("pdb.zig"); pub const process = @import("process.zig"); pub const rand = @import("rand.zig"); pub const sort = @import("sort.zig"); +pub const simd = @import("simd.zig"); pub const ascii = @import("ascii.zig"); pub const testing = @import("testing.zig"); pub const time = @import("time.zig"); From dbbda0f41a7c5e214801925f8447a15193c3c731 Mon Sep 17 00:00:00 2001 From: matu3ba Date: Sun, 27 Mar 2022 10:43:40 +0200 Subject: [PATCH 0949/2031] std.testing: add methods tmpDirPath, getTestArgs, buildExe continuation of #11093 to simplify testing IPC * use cases - get path to temporary directory - get the test arguments inside test block for reusage - build executables from text within test blocks, ie to test IPC * missing conventions - how to name and debug test cases - where do simple+repititve build commands for testing belong --- lib/std/child_process.zig | 56 ++++++++++++++------------------------- lib/std/testing.zig | 50 ++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 36 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index cc211a69a1..8171ff7eea 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -1348,46 +1348,30 @@ const childstr = test "build and call child_process" { if (builtin.os.tag == .wasi) return error.SkipZigTest; const testing = std.testing; - var it = try std.process.argsWithAllocator(std.testing.allocator); - defer it.deinit(); // no-op unless WASI or Windows + const allocator = testing.allocator; + + var it = try std.process.argsWithAllocator(allocator); + defer it.deinit(); // no-op unless WASI or Windows + const testargs = try testing.getTestArgs(&it); - _ = it.next() orelse unreachable; - const zigexec = it.next() orelse unreachable; - try testing.expect(it.next() == null); - try testing.expect(!it.skip()); - const cwd_str = try process.getCwdAlloc(testing.allocator); - defer testing.allocator.free(cwd_str); var tmp = testing.tmpDir(.{ .no_follow = true }); // ie zig-cache/tmp/8DLgoSEqz593PAEE defer tmp.cleanup(); - const cache = "zig-cache"; - const tmpdir = "tmp"; + const tmpdirpath = try tmp.getFullPath(allocator); + defer allocator.free(tmpdirpath); const child_name = "child"; // no need for suffixes (.exe, .wasm) due to '-femit-bin' const suffix_zig = ".zig"; - const child_path = try fs.path.join(testing.allocator, &[_][]const u8{ cwd_str, std.fs.path.sep_str, cache, tmpdir, &tmp.sub_path, child_name }); - defer testing.allocator.free(child_path); + const child_path = try fs.path.join(allocator, &[_][]const u8{ tmpdirpath, child_name }); + defer allocator.free(child_path); + const child_zig = try mem.concat(allocator, u8, &[_][]const u8{ child_path, suffix_zig }); + defer allocator.free(child_zig); - const child_zig = try mem.concat(testing.allocator, u8, &[_][]const u8{ child_path, suffix_zig }); - defer testing.allocator.free(child_zig); - const emit_flag = "-femit-bin="; - const emit_bin = try mem.concat(testing.allocator, u8, &[_][]const u8{ emit_flag, child_path }); - defer testing.allocator.free(emit_bin); - { - // 'zigexec build-exe path/to/child.zig -femit-bin=path/to/child' expect success - try tmp.dir.writeFile("child.zig", childstr); - const args = [_][]const u8{ zigexec, "build-exe", child_zig, emit_bin }; - var procCompileChild = try ChildProcess.init(&args, testing.allocator); - defer procCompileChild.deinit(); - try procCompileChild.spawn(); - const ret_val = try procCompileChild.wait(); - try testing.expectEqual(ret_val, .{ .Exited = 0 }); - } - { - // spawn compiled file as child_process with argument 'hello world' + expect success - const args = [_][]const u8{ child_path, "hello world" }; - var child_proc = try ChildProcess.init(&args, testing.allocator); - defer child_proc.deinit(); - try child_proc.spawn(); - const ret_val = try child_proc.wait(); - try testing.expectEqual(ret_val, .{ .Exited = 0 }); - } + try tmp.dir.writeFile("child.zig", childstr); + try testing.buildExe(testargs.zigexec, child_zig, child_path); + + // spawn compiled file as child_process with argument 'hello world' + expect success + const args = [_][]const u8{ child_path, "hello world" }; + var child_proc = try ChildProcess.init(&args, allocator); + defer child_proc.deinit(); + const ret_val = try child_proc.spawnAndWait(); + try testing.expectEqual(ret_val, .{ .Exited = 0 }); } diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 5049218c90..4146e033b4 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -355,6 +355,19 @@ pub const TmpDir = struct { const random_bytes_count = 12; const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count); + /// caller owns memory + pub fn getFullPath(self: *TmpDir, alloc: std.mem.Allocator) ![]u8 { + const cwd_str = try std.process.getCwdAlloc(alloc); + defer alloc.free(cwd_str); + const path = try std.fs.path.join(alloc, &[_][]const u8{ + cwd_str, + "zig-cache", + "tmp", + &self.sub_path, + }); + return path; + } + pub fn cleanup(self: *TmpDir) void { self.dir.close(); self.parent_dir.deleteTree(&self.sub_path) catch {}; @@ -400,6 +413,43 @@ pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir { }; } +const TestArgs = struct { + testexec: [:0]const u8 = undefined, + zigexec: [:0]const u8 = undefined, +}; + +/// Get test arguments inside test block by regular test runner ('zig test file.zig') +/// Caller must provide backing ArgIterator +pub fn getTestArgs(it: *std.process.ArgIterator) !TestArgs { + var testargs = TestArgs{}; + testargs.testexec = it.next() orelse unreachable; + testargs.zigexec = it.next() orelse unreachable; + try expect(!it.skip()); + return testargs; +} + +test "getTestArgs" { + var it = try std.process.argsWithAllocator(allocator); + const testargs = try getTestArgs(&it); + defer it.deinit(); // no-op unless WASI or Windows + try expect(testargs.testexec.len > 0); // zig compiler executable path + try expect(testargs.zigexec.len > 0); // test runner executable path +} + +/// Spawns child process with 'zigexec build-exe zigfile -femit-bin=binfile' +/// and expects success +pub fn buildExe(zigexec: []const u8, zigfile: []const u8, binfile: []const u8) !void { + const flag_emit = "-femit-bin="; + const cmd_emit = try std.mem.concat(allocator, u8, &[_][]const u8{ flag_emit, binfile }); + defer allocator.free(cmd_emit); + const args = [_][]const u8{ zigexec, "build-exe", zigfile, cmd_emit }; + var procCompileChild = try std.ChildProcess.init(&args, allocator); + defer procCompileChild.deinit(); + try procCompileChild.spawn(); + const ret_val = try procCompileChild.wait(); + try expectEqual(ret_val, .{ .Exited = 0 }); +} + test "expectEqual nested array" { const a = [2][2]f32{ [_]f32{ 1.0, 0.0 }, From 7ae22813eedbe3461f555b6f4c6e708b8c952b15 Mon Sep 17 00:00:00 2001 From: leesongun <12179851+leesongun@users.noreply.github.com> Date: Sun, 27 Mar 2022 17:49:54 +0900 Subject: [PATCH 0950/2031] stage1: implement casting from u0 --- src/stage1/codegen.cpp | 14 +++++++++++++- test/behavior/widening.zig | 8 ++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index bfe58ae912..40e4440102 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -1707,7 +1707,6 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z ZigType *wanted_type, LLVMValueRef expr_val) { assert(actual_type->id == wanted_type->id); - assert(expr_val != nullptr); ZigType *scalar_actual_type = (actual_type->id == ZigTypeIdVector) ? actual_type->data.vector.elem_type : actual_type; @@ -1733,6 +1732,19 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z zig_unreachable(); } + if (expr_val == nullptr) { + if (scalar_actual_type->id == ZigTypeIdInt && actual_bits == 0) { + if (wanted_bits == 0) { + return expr_val; + } else { + LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, wanted_type)); + return zero; + } + } else { + zig_unreachable(); + } + } + if (scalar_actual_type->id == ZigTypeIdInt && want_runtime_safety && ( // negative to unsigned (!scalar_wanted_type->data.integral.is_signed && scalar_actual_type->data.integral.is_signed) || diff --git a/test/behavior/widening.zig b/test/behavior/widening.zig index 0d5722db2b..5c25f7f627 100644 --- a/test/behavior/widening.zig +++ b/test/behavior/widening.zig @@ -20,6 +20,14 @@ test "integer widening" { try expect(f == a); } +fn zero() u0 { + return 0; +} +test "integer widening u0 to u8" { + const a: u8 = zero(); + try expect(a == 0); +} + test "implicit unsigned integer to signed integer" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO From 406507c6dcdfff13633f9047549fe1d1f93b6819 Mon Sep 17 00:00:00 2001 From: BlueAlmost <100024520+BlueAlmost@users.noreply.github.com> Date: Sun, 27 Mar 2022 10:54:43 +0200 Subject: [PATCH 0951/2031] std.math.Complex: add 'negation' and 'mulitply by i' --- lib/std/math/complex.zig | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/std/math/complex.zig b/lib/std/math/complex.zig index 447b5f9fc2..42342faa3e 100644 --- a/lib/std/math/complex.zig +++ b/lib/std/math/complex.zig @@ -88,6 +88,22 @@ pub fn Complex(comptime T: type) type { }; } + /// Returns the negation of a complex number. + pub fn neg(self: Self) Self { + return Self{ + .re = -self.re, + .im = -self.im, + }; + } + + /// Returns the product of complex number and i=sqrt(-1) + pub fn mulbyi(self: Self) Self { + return Self{ + .re = -self.im, + .im = self.re, + }; + } + /// Returns the reciprocal of a complex number. pub fn reciprocal(self: Self) Self { const m = self.re * self.re + self.im * self.im; @@ -146,6 +162,20 @@ test "complex.conjugate" { try testing.expect(c.re == 5 and c.im == -3); } +test "complex.neg" { + const a = Complex(f32).init(5, 3); + const c = a.neg(); + + try testing.expect(c.re == -5 and c.im == -3); +} + +test "complex.mulbyi" { + const a = Complex(f32).init(5, 3); + const c = a.mulbyi(); + + try testing.expect(c.re == -3 and c.im == 5); +} + test "complex.reciprocal" { const a = Complex(f32).init(5, 3); const c = a.reciprocal(); From d15bbebe2e6b8fcbfcd730a6c0d1be621b27045d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 27 Mar 2022 13:20:06 +0200 Subject: [PATCH 0952/2031] macho: do not create dSYM bundle for stage2 LLVM backend Instead, we fallback to the old-fashioned stabs-based mechanism until I add the missing mechanism for extracting and relocating DWARF from relocatable object files and writing it into a dSYM bundle. --- src/link/MachO.zig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 82363c7e24..e613772f22 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -332,7 +332,13 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { return self; } - if (!options.strip and options.module != null) { + if (!options.strip and options.module != null) blk: { + // TODO once I add support for converting (and relocating) DWARF info from relocatable + // object files, this check becomes unnecessary. + // For now, for LLVM backend we fallback to the old-fashioned stabs approach used by + // stage1. + if (build_options.have_llvm and options.use_llvm) break :blk; + // Create dSYM bundle. const dir = options.module.?.zig_cache_artifact_directory; log.debug("creating {s}.dSYM bundle in {s}", .{ emit.sub_path, dir.path }); From df544cace97b57b318dba439d4e67a3953388477 Mon Sep 17 00:00:00 2001 From: iddev5 Date: Fri, 18 Mar 2022 13:10:41 +0530 Subject: [PATCH 0953/2031] std: explicitly handle error.UnexpectedExitCode in build_runner RunStep on unexpected exit code used to return error.UncleanExit, which was confusing and unclear. When it was changed, the error handling code in build_runner was not modified, which produced an error trace. This commit explicitly handles error.UnexpectedExitCode in build_runner so that the behavior now matches that of zig 0.8.1 after which it was regressed. --- lib/std/special/build_runner.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/special/build_runner.zig b/lib/std/special/build_runner.zig index eb83ef8fcd..bdc2d0457e 100644 --- a/lib/std/special/build_runner.zig +++ b/lib/std/special/build_runner.zig @@ -205,7 +205,7 @@ pub fn main() !void { error.InvalidStepName => { return usageAndErr(builder, true, stderr_stream); }, - error.UncleanExit => process.exit(1), + error.UnexpectedExitCode, error.UncleanExit => process.exit(1), else => return err, } }; From 01698528d1dff627b7e057651b137c20df7c7231 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Mar 2022 09:09:32 -0700 Subject: [PATCH 0954/2031] stage2: safety checks for slicing a null C pointer --- src/Sema.zig | 14 ++++++++++++++ test/behavior/slice.zig | 1 + .../stage2/slice_of_null_pointer.zig | 10 ++++++++++ 3 files changed, 25 insertions(+) create mode 100644 test/compile_errors/stage2/slice_of_null_pointer.zig diff --git a/src/Sema.zig b/src/Sema.zig index 4e0b57fbc6..86e330a759 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -19964,6 +19964,14 @@ fn analyzeSlice( slice_ty = ptr_ptr_child_ty; array_ty = ptr_ptr_child_ty; elem_ty = ptr_ptr_child_ty.childType(); + + if (ptr_ptr_child_ty.ptrSize() == .C) { + if (try sema.resolveDefinedValue(block, ptr_src, ptr_or_slice)) |ptr_val| { + if (ptr_val.isNull()) { + return sema.fail(block, ptr_src, "slice of null pointer", .{}); + } + } + } }, .Slice => { ptr_sentinel = ptr_ptr_child_ty.sentinel(); @@ -20162,6 +20170,12 @@ fn analyzeSlice( try sema.requireRuntimeBlock(block, src); if (block.wantSafety()) { + // requirement: slicing C ptr is non-null + if (ptr_ptr_child_ty.isCPtr()) { + const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true); + try sema.addSafetyCheck(block, is_non_null, .unwrap_null); + } + // requirement: end <= len const opt_len_inst = if (array_ty.zigTypeTag() == .Array) try sema.addIntUnsigned(Type.usize, array_ty.arrayLenIncludingSentinel()) diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 9f3ba001cf..a9f89130a1 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -233,6 +233,7 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { test "C pointer" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf"; var len: u32 = 10; diff --git a/test/compile_errors/stage2/slice_of_null_pointer.zig b/test/compile_errors/stage2/slice_of_null_pointer.zig new file mode 100644 index 0000000000..1e3f0d6aee --- /dev/null +++ b/test/compile_errors/stage2/slice_of_null_pointer.zig @@ -0,0 +1,10 @@ +comptime { + var x: [*c]u8 = null; + var runtime_len: usize = 0; + var y = x[0..runtime_len]; + _ = y; +} + +// slice of null C pointer +// +// :4:14: error: slice of null pointer From 8fbac2e86d35bf363b67aba0f1915b7c9d32dcd0 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Mar 2022 09:40:11 -0700 Subject: [PATCH 0955/2031] stage2: runtime safety check integer cast truncating bits --- src/Sema.zig | 21 ++++++++++++++++++--- test/behavior/eval.zig | 1 + test/behavior/fn.zig | 1 + test/behavior/for.zig | 1 + test/behavior/int128.zig | 1 + 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 86e330a759..473c9eab66 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6781,14 +6781,27 @@ fn intCast( return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{}); } - // TODO insert safety check to make sure the value fits in the dest type - _ = runtime_safety; - if ((try sema.typeHasOnePossibleValue(block, dest_ty_src, dest_ty))) |opv| { return sema.addConstant(dest_ty, opv); } try sema.requireRuntimeBlock(block, operand_src); + if (runtime_safety) { + const target = sema.mod.getTarget(); + const operand_ty = sema.typeOf(operand); + const actual_info = operand_ty.intInfo(target); + const wanted_info = dest_ty.intInfo(target); + const actual_bits = actual_info.bits; + const wanted_bits = wanted_info.bits; + + // requirement: operand can fit into bit size of destination type + if (actual_bits > wanted_bits) { + const max_int = try dest_ty.maxInt(sema.arena, target); + const max_int_inst = try sema.addConstant(operand_ty, max_int); + const is_in_range = try block.addBinOp(.cmp_lte, operand, max_int_inst); + try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data); + } + } return block.addTyOp(.intcast, dest_ty, operand); } @@ -16166,6 +16179,7 @@ pub const PanicId = enum { incorrect_alignment, invalid_error_code, index_out_of_bounds, + cast_truncated_data, }; fn addSafetyCheck( @@ -16288,6 +16302,7 @@ fn safetyPanic( .incorrect_alignment => "incorrect alignment", .invalid_error_code => "invalid error code", .index_out_of_bounds => "attempt to index out of bounds", + .cast_truncated_data => "integer cast truncated bits", }; const msg_inst = msg_inst: { diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index e3024a3895..9cad1c6106 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -443,6 +443,7 @@ fn copyWithPartialInline(s: []u32, b: []u8) void { test "binary math operator in partially inlined function" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var s: [4]u32 = undefined; var b: [16]u8 = undefined; diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index ed71bf3d59..68eb730b57 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -315,6 +315,7 @@ test "function pointers" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const fns = [_]*const @TypeOf(fn1){ &fn1, diff --git a/test/behavior/for.zig b/test/behavior/for.zig index 5188f02381..db3288a4d1 100644 --- a/test/behavior/for.zig +++ b/test/behavior/for.zig @@ -69,6 +69,7 @@ test "basic for loop" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; const expected_result = [_]u8{ 9, 8, 7, 6, 0, 1, 2, 3 } ** 3; diff --git a/test/behavior/int128.zig b/test/behavior/int128.zig index f57999511c..08c6dd0e4d 100644 --- a/test/behavior/int128.zig +++ b/test/behavior/int128.zig @@ -46,6 +46,7 @@ test "int128" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var buff: i128 = -1; try expect(buff < 0 and (buff + 1) == 0); From 4ad98d07141cfba9ec9eb94c5daa13cc70c9d9cd Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Mar 2022 09:53:07 -0700 Subject: [PATCH 0956/2031] stage2: runtime safety check intCast to u0 must fit --- src/Sema.zig | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 473c9eab66..4a87145700 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6782,6 +6782,20 @@ fn intCast( } if ((try sema.typeHasOnePossibleValue(block, dest_ty_src, dest_ty))) |opv| { + // requirement: intCast(u0, input) iff input == 0 + if (runtime_safety and block.wantSafety()) { + try sema.requireRuntimeBlock(block, operand_src); + const target = sema.mod.getTarget(); + const wanted_info = dest_ty.intInfo(target); + const wanted_bits = wanted_info.bits; + + if (wanted_bits == 0) { + const zero_inst = try sema.addConstant(sema.typeOf(operand), Value.zero); + const is_in_range = try block.addBinOp(.cmp_eq, operand, zero_inst); + try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data); + } + } + return sema.addConstant(dest_ty, opv); } @@ -6794,7 +6808,7 @@ fn intCast( const actual_bits = actual_info.bits; const wanted_bits = wanted_info.bits; - // requirement: operand can fit into bit size of destination type + // requirement: bitSizeOf(operand) <= bitSizeOf(destination type) if (actual_bits > wanted_bits) { const max_int = try dest_ty.maxInt(sema.arena, target); const max_int_inst = try sema.addConstant(operand_ty, max_int); From 3c918184385f9ffc80693fb2683b4a9b574f9b66 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Mar 2022 16:45:29 -0700 Subject: [PATCH 0957/2031] stage2: runtime safety check intCast signedness --- src/Sema.zig | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 4a87145700..beab957529 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6800,7 +6800,7 @@ fn intCast( } try sema.requireRuntimeBlock(block, operand_src); - if (runtime_safety) { + if (runtime_safety and block.wantSafety()) { const target = sema.mod.getTarget(); const operand_ty = sema.typeOf(operand); const actual_info = operand_ty.intInfo(target); @@ -6808,8 +6808,21 @@ fn intCast( const actual_bits = actual_info.bits; const wanted_bits = wanted_info.bits; - // requirement: bitSizeOf(operand) <= bitSizeOf(destination type) - if (actual_bits > wanted_bits) { + // requirement: signed to unsigned >= 0 + if (actual_info.signedness == .signed and + wanted_info.signedness == .unsigned) + { + const zero_inst = try sema.addConstant(sema.typeOf(operand), Value.zero); + const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst); + try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data); + } + + // requirement: unsigned int value fits into target type + if (actual_bits > wanted_bits or + (actual_bits == wanted_bits and + actual_info.signedness == .unsigned and + wanted_info.signedness == .signed)) + { const max_int = try dest_ty.maxInt(sema.arena, target); const max_int_inst = try sema.addConstant(operand_ty, max_int); const is_in_range = try block.addBinOp(.cmp_lte, operand, max_int_inst); From 3114faddd84d67b68384d3321f192e3c09bf13bf Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 27 Mar 2022 19:00:49 +0200 Subject: [PATCH 0958/2031] wasm: Implement overflow arithmetic This implements the overflow arithmetic for unsigned and signed integers. Meaning the following instructions: - @addWithOverflow - @subWithOverflow - @shlWithOverflow - @mulWithOverflow --- src/arch/wasm/CodeGen.zig | 181 ++++++++++++++++++++++++++++++++------ 1 file changed, 156 insertions(+), 25 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index db09e0bfcf..d43e8758a1 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1303,10 +1303,16 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .bool_and => self.airBinOp(inst, .@"and"), .bool_or => self.airBinOp(inst, .@"or"), .rem => self.airBinOp(inst, .rem), - .shl, .shl_exact => self.airBinOp(inst, .shl), + .shl => self.airWrapBinOp(inst, .shl), + .shl_exact => self.airBinOp(inst, .shl), .shr, .shr_exact => self.airBinOp(inst, .shr), .xor => self.airBinOp(inst, .xor), + .add_with_overflow => self.airBinOpOverflow(inst, .add), + .sub_with_overflow => self.airBinOpOverflow(inst, .sub), + .shl_with_overflow => self.airBinOpOverflow(inst, .shl), + .mul_with_overflow => self.airBinOpOverflow(inst, .mul), + .cmp_eq => self.airCmp(inst, .eq), .cmp_gte => self.airCmp(inst, .gte), .cmp_gt => self.airCmp(inst, .gt), @@ -1461,13 +1467,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .atomic_rmw, .tag_name, .mul_add, - - // For these 4, probably best to wait until https://github.com/ziglang/zig/issues/10248 - // is implemented in the frontend before implementing them here in the wasm backend. - .add_with_overflow, - .sub_with_overflow, - .mul_with_overflow, - .shl_with_overflow, => |tag| return self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), }; } @@ -1754,24 +1753,28 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const operand_ty = self.air.typeOfIndex(inst); + const ty = self.air.typeOf(bin_op.lhs); if (isByRef(operand_ty, self.target)) { return self.fail("TODO: Implement binary operation for type: {}", .{operand_ty.fmtDebug()}); } + return self.binOp(lhs, rhs, ty, op); +} + +fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { try self.emitWValue(lhs); try self.emitWValue(rhs); - const bin_ty = self.air.typeOf(bin_op.lhs); const opcode: wasm.Opcode = buildOpcode(.{ .op = op, - .valtype1 = typeToValtype(bin_ty, self.target), - .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned, + .valtype1 = typeToValtype(ty, self.target), + .signedness = if (ty.isSignedInt()) .signed else .unsigned, }); try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); // save the result in a temporary - const bin_local = try self.allocLocal(bin_ty); + const bin_local = try self.allocLocal(ty); try self.addLabel(.local_set, bin_local.local); return bin_local; } @@ -1781,18 +1784,21 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); + return self.wrapBinOp(lhs, rhs, self.air.typeOf(bin_op.lhs), op); +} + +fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { try self.emitWValue(lhs); try self.emitWValue(rhs); - const bin_ty = self.air.typeOf(bin_op.lhs); const opcode: wasm.Opcode = buildOpcode(.{ .op = op, - .valtype1 = typeToValtype(bin_ty, self.target), - .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned, + .valtype1 = typeToValtype(ty, self.target), + .signedness = if (ty.isSignedInt()) .signed else .unsigned, }); try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); - const int_info = bin_ty.intInfo(self.target); + const int_info = ty.intInfo(self.target); const bitsize = int_info.bits; const is_signed = int_info.signedness == .signed; // if target type bitsize is x < 32 and 32 > x < 64, we perform @@ -1820,7 +1826,7 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { } // save the result in a temporary - const bin_local = try self.allocLocal(bin_ty); + const bin_local = try self.allocLocal(ty); try self.addLabel(.local_set, bin_local.local); return bin_local; } @@ -2202,18 +2208,21 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const operand_ty = self.air.typeOf(bin_op.lhs); + return self.cmp(lhs, rhs, operand_ty, op); +} - if (operand_ty.zigTypeTag() == .Optional and !operand_ty.isPtrLikeOptional()) { +fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOperator) InnerError!WValue { + if (ty.zigTypeTag() == .Optional and !ty.isPtrLikeOptional()) { var buf: Type.Payload.ElemType = undefined; - const payload_ty = operand_ty.optionalChild(&buf); + const payload_ty = ty.optionalChild(&buf); if (payload_ty.hasRuntimeBitsIgnoreComptime()) { // When we hit this case, we must check the value of optionals // that are not pointers. This means first checking against non-null for // both lhs and rhs, as well as checking the payload are matching of lhs and rhs - return self.cmpOptionals(lhs, rhs, operand_ty, op); + return self.cmpOptionals(lhs, rhs, ty, op); } - } else if (isByRef(operand_ty, self.target)) { - return self.cmpBigInt(lhs, rhs, operand_ty, op); + } else if (isByRef(ty, self.target)) { + return self.cmpBigInt(lhs, rhs, ty, op); } // ensure that when we compare pointers, we emit @@ -2229,13 +2238,13 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner const signedness: std.builtin.Signedness = blk: { // by default we tell the operand type is unsigned (i.e. bools and enum values) - if (operand_ty.zigTypeTag() != .Int) break :blk .unsigned; + if (ty.zigTypeTag() != .Int) break :blk .unsigned; // incase of an actual integer, we emit the correct signedness - break :blk operand_ty.intInfo(self.target).signedness; + break :blk ty.intInfo(self.target).signedness; }; const opcode: wasm.Opcode = buildOpcode(.{ - .valtype1 = typeToValtype(operand_ty, self.target), + .valtype1 = typeToValtype(ty, self.target), .op = switch (op) { .lt => .lt, .lte => .le, @@ -3730,3 +3739,125 @@ fn airPtrSliceFieldPtr(self: *Self, inst: Air.Inst.Index, offset: u32) InnerErro const slice_ptr = try self.resolveInst(ty_op.operand); return self.buildPointerOffset(slice_ptr, offset, .new); } + +fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; + const lhs = try self.resolveInst(extra.lhs); + const rhs = try self.resolveInst(extra.rhs); + const lhs_ty = self.air.typeOf(extra.lhs); + + // We store the bit if it's overflowed or not in this. As it's zero-initialized + // we only need to update it if an overflow (or underflow) occured. + const overflow_bit = try self.allocLocal(Type.initTag(.u1)); + const int_info = lhs_ty.intInfo(self.target); + const wasm_bits = toWasmBits(int_info.bits) orelse { + return self.fail("TODO: Implement overflow arithmetic for integer bitsize: {d}", .{int_info.bits}); + }; + + const zero = switch (wasm_bits) { + 32 => WValue{ .imm32 = 0 }, + 64 => WValue{ .imm64 = 0 }, + else => unreachable, + }; + const int_max = (@as(u65, 1) << @intCast(u7, int_info.bits - @boolToInt(int_info.signedness == .signed))) - 1; + const int_max_wvalue = switch (wasm_bits) { + 32 => WValue{ .imm32 = @intCast(u32, int_max) }, + 64 => WValue{ .imm64 = @intCast(u64, int_max) }, + else => unreachable, + }; + const int_min = if (int_info.signedness == .unsigned) + @as(i64, 0) + else + -@as(i64, 1) << @intCast(u6, int_info.bits - 1); + const int_min_wvalue = switch (wasm_bits) { + 32 => WValue{ .imm32 = @bitCast(u32, @intCast(i32, int_min)) }, + 64 => WValue{ .imm64 = @bitCast(u64, int_min) }, + else => unreachable, + }; + + if (int_info.signedness == .unsigned and op == .add) { + const diff = try self.binOp(int_max_wvalue, lhs, lhs_ty, .sub); + const cmp_res = try self.cmp(rhs, diff, lhs_ty, .gt); + try self.emitWValue(cmp_res); + try self.addLabel(.local_set, overflow_bit.local); + } else if (int_info.signedness == .unsigned and op == .sub) { + const cmp_res = try self.cmp(lhs, rhs, lhs_ty, .lt); + try self.emitWValue(cmp_res); + try self.addLabel(.local_set, overflow_bit.local); + } else if (int_info.signedness == .signed and op != .shl) { + // for overflow, we first check if lhs is > 0 (or lhs < 0 in case of subtraction). If not, we will not overflow. + // We first create an outer block, where we handle overflow. + // Then we create an inner block, where underflow is handled. + try self.startBlock(.block, wasm.block_empty); + try self.startBlock(.block, wasm.block_empty); + { + try self.emitWValue(lhs); + const cmp_result = try self.cmp(lhs, zero, lhs_ty, .lt); + try self.emitWValue(cmp_result); + } + try self.addLabel(.br_if, 0); // break to outer block, and handle underflow + + // handle overflow + { + const diff = try self.binOp(int_max_wvalue, lhs, lhs_ty, .sub); + const cmp_res = try self.cmp(rhs, diff, lhs_ty, if (op == .add) .gt else .lt); + try self.emitWValue(cmp_res); + try self.addLabel(.local_set, overflow_bit.local); + } + try self.addLabel(.br, 1); // break from blocks, and continue regular flow. + try self.endBlock(); + + // handle underflow + { + const diff = try self.binOp(int_min_wvalue, lhs, lhs_ty, .sub); + const cmp_res = try self.cmp(rhs, diff, lhs_ty, if (op == .add) .lt else .gt); + try self.emitWValue(cmp_res); + try self.addLabel(.local_set, overflow_bit.local); + } + try self.endBlock(); + } + + const bin_op = if (op == .shl) blk: { + const tmp_val = try self.binOp(lhs, rhs, lhs_ty, op); + const cmp_res = try self.cmp(tmp_val, int_max_wvalue, lhs_ty, .gt); + try self.emitWValue(cmp_res); + try self.addLabel(.local_set, overflow_bit.local); + + try self.emitWValue(tmp_val); + try self.emitWValue(int_max_wvalue); + switch (wasm_bits) { + 32 => try self.addTag(.i32_and), + 64 => try self.addTag(.i64_and), + else => unreachable, + } + try self.addLabel(.local_set, tmp_val.local); + break :blk tmp_val; + } else if (op == .mul) blk: { + const bin_op = try self.wrapBinOp(lhs, rhs, lhs_ty, op); + try self.startBlock(.block, wasm.block_empty); + // check if 0. true => Break out of block as cannot over -or underflow. + try self.emitWValue(lhs); + switch (wasm_bits) { + 32 => try self.addTag(.i32_eqz), + 64 => try self.addTag(.i64_eqz), + else => unreachable, + } + try self.addLabel(.br_if, 0); + const div = try self.binOp(bin_op, lhs, lhs_ty, .div); + const cmp_res = try self.cmp(div, rhs, lhs_ty, .neq); + try self.emitWValue(cmp_res); + try self.addLabel(.local_set, overflow_bit.local); + try self.endBlock(); + break :blk bin_op; + } else try self.wrapBinOp(lhs, rhs, lhs_ty, op); + + const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); + try self.store(result_ptr, bin_op, lhs_ty, 0); + const offset = @intCast(u32, lhs_ty.abiSize(self.target)); + try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset); + + return result_ptr; +} From e1bb09648fbd827975b9f589fa849aabefe73f67 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 27 Mar 2022 19:02:45 +0200 Subject: [PATCH 0959/2031] wasm: Enable overflow behavior tests --- test/behavior/math.zig | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 6690fbd193..3abbece353 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -639,7 +639,6 @@ test "128-bit multiplication" { test "@addWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -661,7 +660,6 @@ test "@addWithOverflow" { test "small int addition" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -686,7 +684,6 @@ test "small int addition" { test "@mulWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -708,7 +705,6 @@ test "@mulWithOverflow" { test "@subWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -730,7 +726,6 @@ test "@subWithOverflow" { test "@shlWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -752,7 +747,6 @@ test "@shlWithOverflow" { test "overflow arithmetic with u0 values" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO var result: u0 = undefined; @@ -879,7 +873,6 @@ test "quad hex float literal parsing accurate" { } test "truncating shift left" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testShlTrunc(maxInt(u16)); From bda7993bebf6c13d1c4f98269c80a2463f99e659 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 27 Mar 2022 20:31:54 +0300 Subject: [PATCH 0960/2031] Sema: fix error set memory unsafety All error names are supposed to be owned by Module. --- src/Sema.zig | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 4e0b57fbc6..da98cb488d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2317,8 +2317,8 @@ fn zirErrorSetDecl( const extra_index_end = extra_index + (extra.data.fields_len * 2); while (extra_index < extra_index_end) : (extra_index += 2) { // +2 to skip over doc_string const str_index = sema.code.extra[extra_index]; - const name = try new_decl_arena_allocator.dupe(u8, sema.code.nullTerminatedString(str_index)); - const result = names.getOrPutAssumeCapacity(name); + const kv = try sema.mod.getErrorValue(sema.code.nullTerminatedString(str_index)); + const result = names.getOrPutAssumeCapacity(kv.key); assert(!result.found_existing); // verified in AstGen } @@ -13113,11 +13113,10 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I // TODO use reflection instead of magic numbers here // error_set: type, const name_val = struct_val[0]; + const name_str = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, target); - names.putAssumeCapacityNoClobber( - try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, target), - {}, - ); + const kv = try sema.mod.getErrorValue(name_str); + names.putAssumeCapacityNoClobber(kv.key, {}); } // names must be sorted From a1040a105ab4dcab9e23d218d2cfa4e58f56be37 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 25 Mar 2022 13:55:12 +0100 Subject: [PATCH 0961/2031] dwarf: add debug info for error sets --- src/codegen.zig | 2 -- src/link/Dwarf.zig | 28 ++++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index cc7014a136..ca24f998e8 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -573,7 +573,6 @@ pub fn generateSymbol( return Result{ .appended = {} }; }, .Union => { - // TODO generate debug info for unions const union_obj = typed_value.val.castTag(.@"union").?.data; const layout = typed_value.ty.unionGetLayout(target); @@ -749,7 +748,6 @@ pub fn generateSymbol( return Result{ .appended = {} }; }, .ErrorSet => { - // TODO generate debug info for error sets switch (typed_value.val.tag()) { .@"error" => { const name = typed_value.val.getError().?; diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 370aac2734..353b40e201 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -468,6 +468,7 @@ pub fn commitDeclDebugInfo( value_ptr.off = @intCast(u32, dbg_info_buffer.items.len); try self.addDbgInfoType( dbg_type_arena.allocator(), + module, ty, dbg_info_buffer, dbg_info_type_relocs, @@ -771,6 +772,7 @@ pub fn freeDecl(self: *Dwarf, decl: *Module.Decl) void { fn addDbgInfoType( self: *Dwarf, arena: Allocator, + module: *Module, ty: Type, dbg_info_buffer: *std.ArrayList(u8), dbg_info_type_relocs: *File.DbgInfoTypeRelocsTable, @@ -1095,6 +1097,32 @@ fn addDbgInfoType( try dbg_info_buffer.append(0); } }, + .ErrorSet => { + // DW.AT.enumeration_type + try dbg_info_buffer.append(abbrev_enum_type); + // DW.AT.byte_size, DW.FORM.sdata + const abi_size = ty.abiSize(target); + try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); + // DW.AT.name, DW.FORM.string + const name = try ty.nameAllocArena(arena, target); + try dbg_info_buffer.writer().print("{s}\x00", .{name}); + + const error_names = if (ty.isAnyError()) module.error_name_list.items else ty.errorSetNames(); + for (error_names) |error_name| { + const kv = module.getErrorValue(error_name) catch unreachable; + // DW.AT.enumerator + try dbg_info_buffer.ensureUnusedCapacity(error_name.len + 2 + @sizeOf(u64)); + dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity(error_name); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.const_value, DW.FORM.data8 + mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), kv.value, target_endian); + } + + // DW.AT.enumeration_type delimit children + try dbg_info_buffer.append(0); + }, else => { log.debug("TODO implement .debug_info for type '{}'", .{ty.fmtDebug()}); try dbg_info_buffer.append(abbrev_pad1); From 4985abcc494cc545e34b164fe12de592cf1b50f4 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 25 Mar 2022 15:50:42 +0100 Subject: [PATCH 0962/2031] dwarf: add debug info for error unions --- src/codegen.zig | 1 - src/link/Dwarf.zig | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/codegen.zig b/src/codegen.zig index ca24f998e8..8924f13e17 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -694,7 +694,6 @@ pub fn generateSymbol( return Result{ .appended = {} }; }, .ErrorUnion => { - // TODO generate debug info for error unions const error_ty = typed_value.ty.errorUnionSet(); const payload_ty = typed_value.ty.errorUnionPayload(); const is_payload = typed_value.val.errorUnionIsPayload(); diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 353b40e201..639ef31be4 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -1107,6 +1107,16 @@ fn addDbgInfoType( const name = try ty.nameAllocArena(arena, target); try dbg_info_buffer.writer().print("{s}\x00", .{name}); + // DW.AT.enumerator + const no_error = "(no error)"; + try dbg_info_buffer.ensureUnusedCapacity(no_error.len + 2 + @sizeOf(u64)); + dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity(no_error); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.const_value, DW.FORM.data8 + mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian); + const error_names = if (ty.isAnyError()) module.error_name_list.items else ty.errorSetNames(); for (error_names) |error_name| { const kv = module.getErrorValue(error_name) catch unreachable; @@ -1123,6 +1133,50 @@ fn addDbgInfoType( // DW.AT.enumeration_type delimit children try dbg_info_buffer.append(0); }, + .ErrorUnion => { + const error_ty = ty.errorUnionSet(); + const payload_ty = ty.errorUnionPayload(); + const abi_size = ty.abiSize(target); + const abi_align = ty.abiAlignment(target); + const payload_off = mem.alignForwardGeneric(u64, error_ty.abiSize(target), abi_align); + + // DW.AT.structure_type + try dbg_info_buffer.append(abbrev_struct_type); + // DW.AT.byte_size, DW.FORM.sdata + try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); + // DW.AT.name, DW.FORM.string + const name = try ty.nameAllocArena(arena, target); + try dbg_info_buffer.writer().print("{s}\x00", .{name}); + + // DW.AT.member + try dbg_info_buffer.ensureUnusedCapacity(7); + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity("value"); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + var index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try relocs.append(.{ .ty = payload_ty, .reloc = @intCast(u32, index) }); + // DW.AT.data_member_location, DW.FORM.sdata + try leb128.writeULEB128(dbg_info_buffer.writer(), payload_off); + + // DW.AT.member + try dbg_info_buffer.ensureUnusedCapacity(5); + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity("err"); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try relocs.append(.{ .ty = error_ty, .reloc = @intCast(u32, index) }); + // DW.AT.data_member_location, DW.FORM.sdata + try dbg_info_buffer.append(0); + + // DW.AT.structure_type delimit children + try dbg_info_buffer.append(0); + }, else => { log.debug("TODO implement .debug_info for type '{}'", .{ty.fmtDebug()}); try dbg_info_buffer.append(abbrev_pad1); From e444e69dc4f9e6a1f25f6081bd0cd4d45f4c9c93 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 25 Mar 2022 17:31:22 +0100 Subject: [PATCH 0963/2031] dwarf: do not use `Type.errorSetNames()` for inferred error sets Otherwise, we risk tripping an assertion in `Type.errorSetNames()` in case the inferred error set was not yet fully resolved. This so far only surfaced when Dwarf triggers recursive analysis of an error set as part of emitting debug info for an error union. --- src/link/Dwarf.zig | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 639ef31be4..f00b1f6479 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -1117,7 +1117,18 @@ fn addDbgInfoType( // DW.AT.const_value, DW.FORM.data8 mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian); - const error_names = if (ty.isAnyError()) module.error_name_list.items else ty.errorSetNames(); + const error_names = blk: { + if (ty.isAnyError()) + break :blk module.error_name_list.items; + // TODO not quite sure about the next one, but if I don't do this, I risk + // tripping an assert in `Type.errorSetNames` in case the inferred error set + // was not yet fully resolved. This so far only surfaced when this code would + // schedule analysis of an error set part of some error union. + if (ty.tag() == .error_set_inferred) + break :blk ty.castTag(.error_set_inferred).?.data.errors.keys(); + break :blk ty.errorSetNames(); + }; + for (error_names) |error_name| { const kv = module.getErrorValue(error_name) catch unreachable; // DW.AT.enumerator From b4815b31310a36e3c1fabd83d010b44b693c9782 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 26 Mar 2022 12:04:18 +0100 Subject: [PATCH 0964/2031] dwarf: draft poc of deferred resolution of error sets debug info --- src/link/Dwarf.zig | 212 ++++++++++++++++++++++++++++++++++----------- src/link/Elf.zig | 13 ++- 2 files changed, 172 insertions(+), 53 deletions(-) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index f00b1f6479..5e46811900 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -41,6 +41,8 @@ abbrev_table_offset: ?u64 = null, /// Table of debug symbol names. strtab: std.ArrayListUnmanaged(u8) = .{}, +deferred_error_sets_relocs: std.ArrayListUnmanaged(u32) = .{}, + pub const DebugInfoAtom = struct { /// Previous/next linked list pointers. /// This is the linked list node for this Decl's corresponding .debug_info tag. @@ -119,6 +121,7 @@ pub fn deinit(self: *Dwarf) void { self.dbg_line_fn_free_list.deinit(gpa); self.dbg_info_decl_free_list.deinit(gpa); self.strtab.deinit(gpa); + self.deferred_error_sets_relocs.deinit(gpa); } pub const DeclDebugBuffers = struct { @@ -462,6 +465,18 @@ pub fn commitDeclDebugInfo( var it: usize = 0; while (it < dbg_info_type_relocs.count()) : (it += 1) { const ty = dbg_info_type_relocs.keys()[it]; + const deferred: bool = blk: { + if (ty.isAnyError()) break :blk true; + switch (ty.tag()) { + .error_set_inferred => { + if (!ty.castTag(.error_set_inferred).?.data.is_resolved) break :blk true; + }, + else => {}, + } + break :blk false; + }; + if (deferred) continue; + const value_ptr = dbg_info_type_relocs.getPtrContext(ty, .{ .target = self.target, }).?; @@ -486,14 +501,32 @@ pub fn commitDeclDebugInfo( { // Now that we have the offset assigned we can finally perform type relocations. - for (dbg_info_type_relocs.values()) |value| { + for (dbg_info_type_relocs.keys()) |ty| { + const value = dbg_info_type_relocs.getContext(ty, .{ + .target = self.target, + }).?; for (value.relocs.items) |off| { - mem.writeInt( - u32, - dbg_info_buffer.items[off..][0..4], - atom.off + value.off, - target_endian, - ); + const deferred: bool = blk: { + if (ty.isAnyError()) break :blk true; + switch (ty.tag()) { + .error_set_inferred => { + if (!ty.castTag(.error_set_inferred).?.data.is_resolved) break :blk true; + }, + else => {}, + } + break :blk false; + }; + if (deferred) { + // Defer until later + try self.deferred_error_sets_relocs.append(self.allocator, atom.off + off); + } else { + mem.writeInt( + u32, + dbg_info_buffer.items[off..][0..4], + atom.off + value.off, + target_endian, + ); + } } } // Offsets to positions with known a priori relative displacement values. @@ -514,6 +547,79 @@ pub fn commitDeclDebugInfo( try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items); } +pub fn commitErrorSetDebugInfo(self: *Dwarf, file: *File, module: *Module) !void { + if (self.deferred_error_sets_relocs.items.len == 0) return; // Nothing to do + + const gpa = self.allocator; + var arena_alloc = std.heap.ArenaAllocator.init(gpa); + defer arena_alloc.deinit(); + const arena = arena_alloc.allocator(); + + const error_set = try arena.create(Module.ErrorSet); + const ty = try Type.Tag.error_set.create(arena, error_set); + var names = Module.ErrorSet.NameMap{}; + try names.ensureUnusedCapacity(arena, module.global_error_set.count()); + var it = module.global_error_set.keyIterator(); + while (it.next()) |key| { + names.putAssumeCapacityNoClobber(key.*, {}); + } + error_set.names = names; + + var dbg_info_buffer = std.ArrayList(u8).init(arena); + try self.addDbgInfoErrorSet(arena, module, ty, &dbg_info_buffer); + + // TODO seems like we need to store DebugInfoAtoms in Dwarf object + // In other words, I have turned Dwarf into a linker... + // FIXME memory leak!!! + const atom = try gpa.create(DebugInfoAtom); + errdefer gpa.destroy(atom); + atom.* = .{ + .prev = null, + .next = null, + .off = 0, + .len = 0, + }; + try self.updateDeclDebugInfoAllocation(file, atom, @intCast(u32, dbg_info_buffer.items.len)); + try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items); + + const file_pos = blk: { + switch (self.tag) { + .elf => { + const elf_file = file.cast(File.Elf).?; + const debug_info_sect = &elf_file.sections.items[elf_file.debug_info_section_index.?]; + break :blk debug_info_sect.sh_offset; + }, + .macho => { + const macho_file = file.cast(File.MachO).?; + const d_sym = &macho_file.d_sym.?; + const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment; + const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?]; + break :blk debug_info_sect.offset; + }, + else => unreachable, + } + }; + + const target_endian = self.target.cpu.arch.endian(); + var buf: [@sizeOf(u32)]u8 = undefined; + while (self.deferred_error_sets_relocs.popOrNull()) |reloc| { + mem.writeInt(u32, &buf, atom.off, target_endian); + + switch (self.tag) { + .elf => { + const elf_file = file.cast(File.Elf).?; + try elf_file.base.file.?.pwriteAll(&buf, file_pos + reloc); + }, + .macho => { + const macho_file = file.cast(File.MachO).?; + const d_sym = &macho_file.d_sym.?; + try d_sym.file.pwriteAll(&buf, file_pos + reloc); + }, + else => unreachable, + } + } +} + fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *DebugInfoAtom, len: u32) !void { const tracy = trace(@src()); defer tracy.end(); @@ -1098,51 +1204,7 @@ fn addDbgInfoType( } }, .ErrorSet => { - // DW.AT.enumeration_type - try dbg_info_buffer.append(abbrev_enum_type); - // DW.AT.byte_size, DW.FORM.sdata - const abi_size = ty.abiSize(target); - try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); - // DW.AT.name, DW.FORM.string - const name = try ty.nameAllocArena(arena, target); - try dbg_info_buffer.writer().print("{s}\x00", .{name}); - - // DW.AT.enumerator - const no_error = "(no error)"; - try dbg_info_buffer.ensureUnusedCapacity(no_error.len + 2 + @sizeOf(u64)); - dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity(no_error); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.const_value, DW.FORM.data8 - mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian); - - const error_names = blk: { - if (ty.isAnyError()) - break :blk module.error_name_list.items; - // TODO not quite sure about the next one, but if I don't do this, I risk - // tripping an assert in `Type.errorSetNames` in case the inferred error set - // was not yet fully resolved. This so far only surfaced when this code would - // schedule analysis of an error set part of some error union. - if (ty.tag() == .error_set_inferred) - break :blk ty.castTag(.error_set_inferred).?.data.errors.keys(); - break :blk ty.errorSetNames(); - }; - - for (error_names) |error_name| { - const kv = module.getErrorValue(error_name) catch unreachable; - // DW.AT.enumerator - try dbg_info_buffer.ensureUnusedCapacity(error_name.len + 2 + @sizeOf(u64)); - dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity(error_name); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.const_value, DW.FORM.data8 - mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), kv.value, target_endian); - } - - // DW.AT.enumeration_type delimit children - try dbg_info_buffer.append(0); + try self.addDbgInfoErrorSet(arena, module, ty, dbg_info_buffer); }, .ErrorUnion => { const error_ty = ty.errorUnionSet(); @@ -1208,6 +1270,52 @@ fn addDbgInfoType( } } +fn addDbgInfoErrorSet( + self: *Dwarf, + arena: Allocator, + module: *Module, + ty: Type, + dbg_info_buffer: *std.ArrayList(u8), +) error{OutOfMemory}!void { + const target = self.target; + const target_endian = self.target.cpu.arch.endian(); + + // DW.AT.enumeration_type + try dbg_info_buffer.append(abbrev_enum_type); + // DW.AT.byte_size, DW.FORM.sdata + const abi_size = ty.abiSize(target); + try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); + // DW.AT.name, DW.FORM.string + const name = try ty.nameAllocArena(arena, target); + try dbg_info_buffer.writer().print("{s}\x00", .{name}); + + // DW.AT.enumerator + const no_error = "(no error)"; + try dbg_info_buffer.ensureUnusedCapacity(no_error.len + 2 + @sizeOf(u64)); + dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity(no_error); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.const_value, DW.FORM.data8 + mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian); + + const error_names = ty.errorSetNames(); + for (error_names) |error_name| { + const kv = module.getErrorValue(error_name) catch unreachable; + // DW.AT.enumerator + try dbg_info_buffer.ensureUnusedCapacity(error_name.len + 2 + @sizeOf(u64)); + dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity(error_name); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.const_value, DW.FORM.data8 + mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), kv.value, target_endian); + } + + // DW.AT.enumeration_type delimit children + try dbg_info_buffer.append(0); +} + pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { // These are LEB encoded but since the values are all less than 127 // we can simply append these bytes. diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 64d6df6756..c81c15f597 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -958,6 +958,10 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { const target_endian = self.base.options.target.cpu.arch.endian(); const foreign_endian = target_endian != builtin.cpu.arch.endian(); + if (self.dwarf) |*dwarf| { + try dwarf.commitErrorSetDebugInfo(&self.base, module); + } + { var it = self.relocs.iterator(); while (it.next()) |entry| { @@ -2376,7 +2380,14 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven }; const local_sym = try self.updateDeclCode(decl, code, elf.STT_FUNC); if (debug_buffers) |dbg| { - try self.dwarf.?.commitDeclDebugInfo(&self.base, module, decl, local_sym.st_value, local_sym.st_size, dbg); + try self.dwarf.?.commitDeclDebugInfo( + &self.base, + module, + decl, + local_sym.st_value, + local_sym.st_size, + dbg, + ); } // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. From 1a80315836f77e38eee3e4c0a646b82febbc3604 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 26 Mar 2022 14:20:08 +0100 Subject: [PATCH 0965/2031] dwarf: rename DebugInfoAtom into Atom; free all allocated memory --- src/link/Dwarf.zig | 65 +++++++++++++++++++++++------------------ src/link/Elf.zig | 2 +- src/link/MachO/Atom.zig | 2 +- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 5e46811900..b2848c8e1e 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -31,10 +31,11 @@ dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{}, dbg_line_fn_first: ?*SrcFn = null, dbg_line_fn_last: ?*SrcFn = null, -/// A list of `TextBlock` whose corresponding .debug_info tags have surplus capacity. /// This is the same concept as `text_block_free_list`; see those doc comments. -dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*DebugInfoAtom, void) = .{}, -dbg_info_decl_first: ?*DebugInfoAtom = null, -dbg_info_decl_last: ?*DebugInfoAtom = null, +/// A list of `Atom`s whose corresponding .debug_info tags have surplus capacity. +/// This is the same concept as `text_block_free_list`; see those doc comments. +atom_free_list: std.AutoHashMapUnmanaged(*Atom, void) = .{}, +atom_first: ?*Atom = null, +atom_last: ?*Atom = null, abbrev_table_offset: ?u64 = null, @@ -43,11 +44,16 @@ strtab: std.ArrayListUnmanaged(u8) = .{}, deferred_error_sets_relocs: std.ArrayListUnmanaged(u32) = .{}, -pub const DebugInfoAtom = struct { +/// List of atoms that are owned directly by the DWARF module. +/// TODO convert links in DebugInfoAtom into indices and make +/// sure every atom is owned by this module. +managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, + +pub const Atom = struct { /// Previous/next linked list pointers. /// This is the linked list node for this Decl's corresponding .debug_info tag. - prev: ?*DebugInfoAtom, - next: ?*DebugInfoAtom, + prev: ?*Atom, + next: ?*Atom, /// Offset into .debug_info pointing to the tag for this Decl. off: u32, /// Size of the .debug_info tag for this Decl, not including padding. @@ -119,9 +125,14 @@ pub fn init(allocator: Allocator, tag: File.Tag, target: std.Target) Dwarf { pub fn deinit(self: *Dwarf) void { const gpa = self.allocator; self.dbg_line_fn_free_list.deinit(gpa); - self.dbg_info_decl_free_list.deinit(gpa); + self.atom_free_list.deinit(gpa); self.strtab.deinit(gpa); self.deferred_error_sets_relocs.deinit(gpa); + + for (self.managed_atoms.items) |atom| { + gpa.destroy(atom); + } + self.managed_atoms.deinit(gpa); } pub const DeclDebugBuffers = struct { @@ -568,10 +579,7 @@ pub fn commitErrorSetDebugInfo(self: *Dwarf, file: *File, module: *Module) !void var dbg_info_buffer = std.ArrayList(u8).init(arena); try self.addDbgInfoErrorSet(arena, module, ty, &dbg_info_buffer); - // TODO seems like we need to store DebugInfoAtoms in Dwarf object - // In other words, I have turned Dwarf into a linker... - // FIXME memory leak!!! - const atom = try gpa.create(DebugInfoAtom); + const atom = try gpa.create(Atom); errdefer gpa.destroy(atom); atom.* = .{ .prev = null, @@ -579,6 +587,7 @@ pub fn commitErrorSetDebugInfo(self: *Dwarf, file: *File, module: *Module) !void .off = 0, .len = 0, }; + try self.managed_atoms.append(gpa, atom); try self.updateDeclDebugInfoAllocation(file, atom, @intCast(u32, dbg_info_buffer.items.len)); try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items); @@ -620,7 +629,7 @@ pub fn commitErrorSetDebugInfo(self: *Dwarf, file: *File, module: *Module) !void } } -fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *DebugInfoAtom, len: u32) !void { +fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u32) !void { const tracy = trace(@src()); defer tracy.end(); @@ -630,14 +639,14 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *DebugInfoAtom const gpa = self.allocator; atom.len = len; - if (self.dbg_info_decl_last) |last| blk: { + if (self.atom_last) |last| blk: { if (atom == last) break :blk; if (atom.next) |next| { // Update existing Decl - non-last item. if (atom.off + atom.len + min_nop_size > next.off) { // It grew too big, so we move it to a new location. if (atom.prev) |prev| { - self.dbg_info_decl_free_list.put(gpa, prev, {}) catch {}; + self.atom_free_list.put(gpa, prev, {}) catch {}; prev.next = atom.next; } next.prev = atom.prev; @@ -663,7 +672,7 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *DebugInfoAtom // TODO Look at the free list before appending at the end. atom.prev = last; last.next = atom; - self.dbg_info_decl_last = atom; + self.atom_last = atom; atom.off = last.off + padToIdeal(last.len); } @@ -672,20 +681,20 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *DebugInfoAtom // TODO Look at the free list before appending at the end. atom.prev = last; last.next = atom; - self.dbg_info_decl_last = atom; + self.atom_last = atom; atom.off = last.off + padToIdeal(last.len); } } else { // This is the first Decl of the .debug_info - self.dbg_info_decl_first = atom; - self.dbg_info_decl_last = atom; + self.atom_first = atom; + self.atom_last = atom; atom.off = @intCast(u32, padToIdeal(self.dbgInfoHeaderBytes())); } } -fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *DebugInfoAtom, dbg_info_buf: []const u8) !void { +fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []const u8) !void { const tracy = trace(@src()); defer tracy.end(); @@ -694,7 +703,7 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *DebugInfoAtom, dbg_info_ // probably need to edit that logic too. const gpa = self.allocator; - const last_decl = self.dbg_info_decl_last.?; + const last_decl = self.atom_last.?; // +1 for a trailing zero to end the children of the decl tag. const needed_size = last_decl.off + last_decl.len + 1; const prev_padding_size: u32 = if (atom.prev) |prev| atom.off - (prev.off + prev.len) else 0; @@ -819,13 +828,13 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl) } } -pub fn freeAtom(self: *Dwarf, atom: *DebugInfoAtom) void { - if (self.dbg_info_decl_first == atom) { - self.dbg_info_decl_first = atom.next; +pub fn freeAtom(self: *Dwarf, atom: *Atom) void { + if (self.atom_first == atom) { + self.atom_first = atom.next; } - if (self.dbg_info_decl_last == atom) { + if (self.atom_last == atom) { // TODO shrink the .debug_info section size here - self.dbg_info_decl_last = atom.prev; + self.atom_last = atom.prev; } if (atom.prev) |prev| { @@ -1964,12 +1973,12 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void { } fn getDebugInfoOff(self: Dwarf) ?u32 { - const first = self.dbg_info_decl_first orelse return null; + const first = self.atom_first orelse return null; return first.off; } fn getDebugInfoEnd(self: Dwarf) ?u32 { - const last = self.dbg_info_decl_last orelse return null; + const last = self.atom_last orelse return null; return last.off + last.len; } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index c81c15f597..676952ea24 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -207,7 +207,7 @@ pub const TextBlock = struct { prev: ?*TextBlock, next: ?*TextBlock, - dbg_info_atom: Dwarf.DebugInfoAtom, + dbg_info_atom: Dwarf.Atom, pub const empty = TextBlock{ .local_sym_index = 0, diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index 4e58946735..dd2dc3c1f1 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -72,7 +72,7 @@ stab: ?Stab = null, next: ?*Atom, prev: ?*Atom, -dbg_info_atom: Dwarf.DebugInfoAtom, +dbg_info_atom: Dwarf.Atom, dirty: bool = true, From 4ca9b4c44a4fbe2b64b11d2c8a951c2c3e961619 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 26 Mar 2022 14:33:31 +0100 Subject: [PATCH 0966/2031] dwarf: move DbgInfoTypeRelocsTable into Dwarf module --- src/codegen.zig | 31 +++++++++++++++---------------- src/link.zig | 44 +++++++++++++------------------------------- src/link/Dwarf.zig | 22 +++++++++++++++++++--- src/link/Elf.zig | 2 +- 4 files changed, 48 insertions(+), 51 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index 8924f13e17..f4803c77ea 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1,26 +1,25 @@ const std = @import("std"); +const build_options = @import("build_options"); const builtin = @import("builtin"); +const assert = std.debug.assert; +const leb128 = std.leb; +const link = @import("link.zig"); +const log = std.log.scoped(.codegen); const mem = std.mem; const math = std.math; -const assert = std.debug.assert; +const trace = @import("tracy.zig").trace; + const Air = @import("Air.zig"); -const Zir = @import("Zir.zig"); -const Liveness = @import("Liveness.zig"); -const Type = @import("type.zig").Type; -const Value = @import("value.zig").Value; -const TypedValue = @import("TypedValue.zig"); -const link = @import("link.zig"); -const Module = @import("Module.zig"); +const Allocator = mem.Allocator; const Compilation = @import("Compilation.zig"); const ErrorMsg = Module.ErrorMsg; +const Liveness = @import("Liveness.zig"); +const Module = @import("Module.zig"); const Target = std.Target; -const Allocator = mem.Allocator; -const trace = @import("tracy.zig").trace; -const DW = std.dwarf; -const leb128 = std.leb; -const log = std.log.scoped(.codegen); -const build_options = @import("build_options"); -const RegisterManager = @import("register_manager.zig").RegisterManager; +const Type = @import("type.zig").Type; +const TypedValue = @import("TypedValue.zig"); +const Value = @import("value.zig").Value; +const Zir = @import("Zir.zig"); pub const FnResult = union(enum) { /// The `code` parameter passed to `generateSymbol` has the value appended. @@ -46,7 +45,7 @@ pub const DebugInfoOutput = union(enum) { dwarf: struct { dbg_line: *std.ArrayList(u8), dbg_info: *std.ArrayList(u8), - dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable, + dbg_info_type_relocs: *link.File.Dwarf.DbgInfoTypeRelocsTable, }, /// the plan9 debuginfo output is a bytecode with 4 opcodes /// assume all numbers/variables are bytes diff --git a/src/link.zig b/src/link.zig index 0fd6797153..7c135a7405 100644 --- a/src/link.zig +++ b/src/link.zig @@ -1,22 +1,22 @@ const std = @import("std"); +const build_options = @import("build_options"); const builtin = @import("builtin"); -const mem = std.mem; -const Allocator = std.mem.Allocator; -const fs = std.fs; -const log = std.log.scoped(.link); const assert = std.debug.assert; - -const Compilation = @import("Compilation.zig"); -const Module = @import("Module.zig"); +const fs = std.fs; +const mem = std.mem; +const log = std.log.scoped(.link); const trace = @import("tracy.zig").trace; +const wasi_libc = @import("wasi_libc.zig"); + +const Air = @import("Air.zig"); +const Allocator = std.mem.Allocator; +const Cache = @import("Cache.zig"); +const Compilation = @import("Compilation.zig"); +const LibCInstallation = @import("libc_installation.zig").LibCInstallation; +const Liveness = @import("Liveness.zig"); +const Module = @import("Module.zig"); const Package = @import("Package.zig"); const Type = @import("type.zig").Type; -const Cache = @import("Cache.zig"); -const build_options = @import("build_options"); -const LibCInstallation = @import("libc_installation.zig").LibCInstallation; -const wasi_libc = @import("wasi_libc.zig"); -const Air = @import("Air.zig"); -const Liveness = @import("Liveness.zig"); const TypedValue = @import("TypedValue.zig"); pub const SystemLib = struct { @@ -245,24 +245,6 @@ pub const File = struct { nvptx: void, }; - /// For DWARF .debug_info. - pub const DbgInfoTypeRelocsTable = std.ArrayHashMapUnmanaged( - Type, - DbgInfoTypeReloc, - Type.HashContext32, - true, - ); - - /// For DWARF .debug_info. - pub const DbgInfoTypeReloc = struct { - /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). - /// This is where the .debug_info tag for the type is. - off: u32, - /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). - /// List of DW.AT.type / DW.FORM.ref4 that points to the type. - relocs: std.ArrayListUnmanaged(u32), - }; - /// Attempts incremental linking, if the file already exists. If /// incremental linking fails, falls back to truncating the file and /// rewriting it. A malicious file is detected as incremental link failure diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index b2848c8e1e..199bf9df61 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -82,6 +82,22 @@ pub const SrcFn = struct { pub const PtrWidth = enum { p32, p64 }; +pub const DbgInfoTypeRelocsTable = std.ArrayHashMapUnmanaged( + Type, + DbgInfoTypeReloc, + Type.HashContext32, + true, +); + +pub const DbgInfoTypeReloc = struct { + /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). + /// This is where the .debug_info tag for the type is. + off: u32, + /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). + /// List of DW.AT.type / DW.FORM.ref4 that points to the type. + relocs: std.ArrayListUnmanaged(u32), +}; + pub const abbrev_compile_unit = 1; pub const abbrev_subprogram = 2; pub const abbrev_subprogram_retvoid = 3; @@ -138,7 +154,7 @@ pub fn deinit(self: *Dwarf) void { pub const DeclDebugBuffers = struct { dbg_line_buffer: std.ArrayList(u8), dbg_info_buffer: std.ArrayList(u8), - dbg_info_type_relocs: File.DbgInfoTypeRelocsTable, + dbg_info_type_relocs: DbgInfoTypeRelocsTable, }; pub fn initDeclDebugInfo(self: *Dwarf, decl: *Module.Decl) !DeclDebugBuffers { @@ -153,7 +169,7 @@ pub fn initDeclDebugInfo(self: *Dwarf, decl: *Module.Decl) !DeclDebugBuffers { const gpa = self.allocator; var dbg_line_buffer = std.ArrayList(u8).init(gpa); var dbg_info_buffer = std.ArrayList(u8).init(gpa); - var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{}; + var dbg_info_type_relocs: DbgInfoTypeRelocsTable = .{}; assert(decl.has_tv); @@ -890,7 +906,7 @@ fn addDbgInfoType( module: *Module, ty: Type, dbg_info_buffer: *std.ArrayList(u8), - dbg_info_type_relocs: *File.DbgInfoTypeRelocsTable, + dbg_info_type_relocs: *DbgInfoTypeRelocsTable, nested_ref4_relocs: *std.ArrayList(u32), ) error{OutOfMemory}!void { const target = self.target; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 676952ea24..dda2841db8 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2232,7 +2232,7 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { } } -fn deinitRelocs(gpa: Allocator, table: *File.DbgInfoTypeRelocsTable) void { +fn deinitRelocs(gpa: Allocator, table: *link.File.Dwarf.DbgInfoTypeRelocsTable) void { for (table.values()) |*value| { value.relocs.deinit(gpa); } From 366ec2105249050fe88e5fa9f241e14f708db891 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 27 Mar 2022 17:04:20 +0200 Subject: [PATCH 0967/2031] dwarf: track type relocation state in Dwarf module --- src/arch/aarch64/Emit.zig | 23 +- src/arch/arm/Emit.zig | 73 ++--- src/arch/riscv64/CodeGen.zig | 37 +-- src/arch/riscv64/Emit.zig | 23 +- src/arch/x86_64/CodeGen.zig | 1 + src/arch/x86_64/Emit.zig | 81 ++--- src/codegen.zig | 6 +- src/link/Dwarf.zig | 546 ++++++++++++++++---------------- src/link/Elf.zig | 68 +--- src/link/MachO.zig | 72 ++--- src/link/MachO/DebugSymbols.zig | 22 -- 11 files changed, 435 insertions(+), 517 deletions(-) diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 3eb25dda7b..2a4e6cb985 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -386,18 +386,19 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line); const delta_pc: usize = self.code.items.len - self.prev_di_pc; switch (self.debug_output) { - .dwarf => |dbg_out| { + .dwarf => |dw| { // TODO Look into using the DWARF special opcodes to compress this data. // It lets you emit single-byte opcodes that add different numbers to // both the PC and the line number at the same time. - try dbg_out.dbg_line.ensureUnusedCapacity(11); - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); - leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable; + const dbg_line = dw.getDeclDebugLineBuffer(); + try dbg_line.ensureUnusedCapacity(11); + dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); + leb128.writeULEB128(dbg_line.writer(), delta_pc) catch unreachable; if (delta_line != 0) { - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line); - leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; + dbg_line.appendAssumeCapacity(DW.LNS.advance_line); + leb128.writeILEB128(dbg_line.writer(), delta_line) catch unreachable; } - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); + dbg_line.appendAssumeCapacity(DW.LNS.copy); self.prev_di_pc = self.code.items.len; self.prev_di_line = line; self.prev_di_column = column; @@ -586,8 +587,8 @@ fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { fn mirDebugPrologueEnd(self: *Emit) !void { switch (self.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); + .dwarf => |dw| { + try dw.getDeclDebugLineBuffer().append(DW.LNS.set_prologue_end); try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); }, .plan9 => {}, @@ -597,8 +598,8 @@ fn mirDebugPrologueEnd(self: *Emit) !void { fn mirDebugEpilogueBegin(self: *Emit) !void { switch (self.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); + .dwarf => |dw| { + try dw.getDeclDebugLineBuffer().append(DW.LNS.set_epilogue_begin); try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); }, .plan9 => {}, diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index 1e03c4649b..62705651a6 100644 --- a/src/arch/arm/Emit.zig +++ b/src/arch/arm/Emit.zig @@ -328,18 +328,19 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line); const delta_pc: usize = self.code.items.len - self.prev_di_pc; switch (self.debug_output) { - .dwarf => |dbg_out| { + .dwarf => |dw| { // TODO Look into using the DWARF special opcodes to compress this data. // It lets you emit single-byte opcodes that add different numbers to // both the PC and the line number at the same time. - try dbg_out.dbg_line.ensureUnusedCapacity(11); - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); - leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable; + const dbg_line = dw.getDeclDebugLineBuffer(); + try dbg_line.ensureUnusedCapacity(11); + dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); + leb128.writeULEB128(dbg_line.writer(), delta_pc) catch unreachable; if (delta_line != 0) { - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line); - leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; + dbg_line.appendAssumeCapacity(DW.LNS.advance_line); + leb128.writeILEB128(dbg_line.writer(), delta_line) catch unreachable; } - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); + dbg_line.appendAssumeCapacity(DW.LNS.copy); self.prev_di_pc = self.code.items.len; self.prev_di_line = line; self.prev_di_column = column; @@ -379,19 +380,17 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { /// after codegen for this symbol is done. fn addDbgInfoTypeReloc(self: *Emit, ty: Type) !void { switch (self.debug_output) { - .dwarf => |dbg_out| { + .dwarf => |dw| { assert(ty.hasRuntimeBits()); - const index = dbg_out.dbg_info.items.len; - try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 - - const gop = try dbg_out.dbg_info_type_relocs.getOrPutContext(self.bin_file.allocator, ty, .{ .target = self.target.* }); - if (!gop.found_existing) { - gop.value_ptr.* = .{ - .off = undefined, - .relocs = .{}, - }; - } - try gop.value_ptr.relocs.append(self.bin_file.allocator, @intCast(u32, index)); + const dbg_info = dw.getDeclDebugInfoBuffer(); + const index = dbg_info.items.len; + try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 + const atom = switch (self.bin_file.tag) { + .elf => &self.function.mod_fn.owner_decl.link.elf.dbg_info_atom, + .macho => unreachable, + else => unreachable, + }; + try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); }, .plan9 => {}, .none => {}, @@ -409,16 +408,17 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { switch (mcv) { .register => |reg| { switch (self.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_info.ensureUnusedCapacity(3); - dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); - dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + .dwarf => |dw| { + const dbg_info = dw.getDeclDebugInfoBuffer(); + try dbg_info.ensureUnusedCapacity(3); + dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1, // ULEB128 dwarf expression length reg.dwarfLocOp(), }); - try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string }, .plan9 => {}, .none => {}, @@ -428,7 +428,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { .stack_argument_offset, => { switch (self.debug_output) { - .dwarf => |dbg_out| { + .dwarf => |dw| { const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch { return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(target)}); }; @@ -442,7 +442,8 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { else => unreachable, }; - try dbg_out.dbg_info.append(link.File.Dwarf.abbrev_parameter); + const dbg_info = dw.getDeclDebugInfoBuffer(); + try dbg_info.append(link.File.Dwarf.abbrev_parameter); // Get length of the LEB128 stack offset var counting_writer = std.io.countingWriter(std.io.null_writer); @@ -450,13 +451,13 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { // DW.AT.location, DW.FORM.exprloc // ULEB128 dwarf expression length - try leb128.writeULEB128(dbg_out.dbg_info.writer(), counting_writer.bytes_written + 1); - try dbg_out.dbg_info.append(DW.OP.breg11); - try leb128.writeILEB128(dbg_out.dbg_info.writer(), adjusted_stack_offset); + try leb128.writeULEB128(dbg_info.writer(), counting_writer.bytes_written + 1); + try dbg_info.append(DW.OP.breg11); + try leb128.writeILEB128(dbg_info.writer(), adjusted_stack_offset); - try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string }, .plan9 => {}, .none => {}, @@ -558,8 +559,8 @@ fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { fn mirDebugPrologueEnd(emit: *Emit) !void { switch (emit.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); + .dwarf => |dw| { + try dw.getDeclDebugLineBuffer().append(DW.LNS.set_prologue_end); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, .plan9 => {}, @@ -569,8 +570,8 @@ fn mirDebugPrologueEnd(emit: *Emit) !void { fn mirDebugEpilogueBegin(emit: *Emit) !void { switch (emit.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); + .dwarf => |dw| { + try dw.getDeclDebugLineBuffer().append(DW.LNS.set_epilogue_begin); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, .plan9 => {}, diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 8b697677c1..fa243819cf 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -745,21 +745,17 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { /// after codegen for this symbol is done. fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { switch (self.debug_output) { - .dwarf => |dbg_out| { + .dwarf => |dw| { assert(ty.hasRuntimeBits()); - const index = dbg_out.dbg_info.items.len; - try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 - - const gop = try dbg_out.dbg_info_type_relocs.getOrPutContext(self.gpa, ty, .{ - .target = self.target.*, - }); - if (!gop.found_existing) { - gop.value_ptr.* = .{ - .off = undefined, - .relocs = .{}, - }; - } - try gop.value_ptr.relocs.append(self.gpa, @intCast(u32, index)); + const dbg_info = dw.getDeclDebugInfoBuffer(); + const index = dbg_info.items.len; + try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 + const atom = switch (self.bin_file.tag) { + .elf => &self.mod_fn.owner_decl.link.elf.dbg_info_atom, + .macho => unreachable, + else => unreachable, + }; + try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); }, .plan9 => {}, .none => {}, @@ -1573,16 +1569,17 @@ fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32 switch (mcv) { .register => |reg| { switch (self.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_info.ensureUnusedCapacity(3); - dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); - dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + .dwarf => |dw| { + const dbg_info = dw.getDeclDebugInfoBuffer(); + try dbg_info.ensureUnusedCapacity(3); + dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1, // ULEB128 dwarf expression length reg.dwarfLocOp(), }); - try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string }, .plan9 => {}, .none => {}, diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index c34d2f4ff4..bfa4e00d00 100644 --- a/src/arch/riscv64/Emit.zig +++ b/src/arch/riscv64/Emit.zig @@ -89,18 +89,19 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line); const delta_pc: usize = self.code.items.len - self.prev_di_pc; switch (self.debug_output) { - .dwarf => |dbg_out| { + .dwarf => |dw| { // TODO Look into using the DWARF special opcodes to compress this data. // It lets you emit single-byte opcodes that add different numbers to // both the PC and the line number at the same time. - try dbg_out.dbg_line.ensureUnusedCapacity(11); - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); - leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable; + const dbg_line = dw.getDeclDebugLineBuffer(); + try dbg_line.ensureUnusedCapacity(11); + dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); + leb128.writeULEB128(dbg_line.writer(), delta_pc) catch unreachable; if (delta_line != 0) { - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line); - leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; + dbg_line.appendAssumeCapacity(DW.LNS.advance_line); + leb128.writeILEB128(dbg_line.writer(), delta_line) catch unreachable; } - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); + dbg_line.appendAssumeCapacity(DW.LNS.copy); self.prev_di_pc = self.code.items.len; self.prev_di_line = line; self.prev_di_column = column; @@ -182,8 +183,8 @@ fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { fn mirDebugPrologueEnd(self: *Emit) !void { switch (self.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); + .dwarf => |dw| { + try dw.getDeclDebugLineBuffer().append(DW.LNS.set_prologue_end); try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); }, .plan9 => {}, @@ -193,8 +194,8 @@ fn mirDebugPrologueEnd(self: *Emit) !void { fn mirDebugEpilogueBegin(self: *Emit) !void { switch (self.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); + .dwarf => |dw| { + try dw.getDeclDebugLineBuffer().append(DW.LNS.set_epilogue_begin); try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); }, .plan9 => {}, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5b252b6c0a..c95e86a424 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -350,6 +350,7 @@ pub fn generate( var emit = Emit{ .mir = mir, .bin_file = bin_file, + .function = &function, .debug_output = debug_output, .target = &bin_file.options.target, .src_loc = src_loc, diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 3ce77ffb73..4616d3ff3a 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -16,6 +16,7 @@ const testing = std.testing; const Air = @import("../../Air.zig"); const Allocator = mem.Allocator; +const CodeGen = @import("CodeGen.zig"); const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; const DW = std.dwarf; const Encoder = bits.Encoder; @@ -29,6 +30,7 @@ const Type = @import("../../type.zig").Type; mir: Mir, bin_file: *link.File, +function: *const CodeGen, debug_output: DebugInfoOutput, target: *const std.Target, err_msg: ?*ErrorMsg = null, @@ -963,18 +965,19 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void { const delta_pc: usize = emit.code.items.len - emit.prev_di_pc; log.debug(" (advance pc={d} and line={d})", .{ delta_line, delta_pc }); switch (emit.debug_output) { - .dwarf => |dbg_out| { + .dwarf => |dw| { // TODO Look into using the DWARF special opcodes to compress this data. // It lets you emit single-byte opcodes that add different numbers to // both the PC and the line number at the same time. - try dbg_out.dbg_line.ensureUnusedCapacity(11); - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); - leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable; + const dbg_line = dw.getDeclDebugLineBuffer(); + try dbg_line.ensureUnusedCapacity(11); + dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); + leb128.writeULEB128(dbg_line.writer(), delta_pc) catch unreachable; if (delta_line != 0) { - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line); - leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; + dbg_line.appendAssumeCapacity(DW.LNS.advance_line); + leb128.writeILEB128(dbg_line.writer(), delta_line) catch unreachable; } - dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); + dbg_line.appendAssumeCapacity(DW.LNS.copy); emit.prev_di_line = line; emit.prev_di_column = column; emit.prev_di_pc = emit.code.items.len; @@ -1022,8 +1025,8 @@ fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .dbg_prologue_end); switch (emit.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); + .dwarf => |dw| { + try dw.getDeclDebugLineBuffer().append(DW.LNS.set_prologue_end); log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{ emit.prev_di_line, emit.prev_di_column }); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, @@ -1036,8 +1039,8 @@ fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .dbg_epilogue_begin); switch (emit.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); + .dwarf => |dw| { + try dw.getDeclDebugLineBuffer().append(DW.LNS.set_epilogue_begin); log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{ emit.prev_di_line, emit.prev_di_column }); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, @@ -1063,16 +1066,17 @@ fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32 switch (mcv) { .register => |reg| { switch (emit.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_info.ensureUnusedCapacity(3); - dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); - dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + .dwarf => |dw| { + const dbg_info = dw.getDeclDebugInfoBuffer(); + try dbg_info.ensureUnusedCapacity(3); + dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1, // ULEB128 dwarf expression length reg.dwarfLocOp(), }); - try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); try emit.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string }, .plan9 => {}, .none => {}, @@ -1080,25 +1084,26 @@ fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32 }, .stack_offset => |off| { switch (emit.debug_output) { - .dwarf => |dbg_out| { + .dwarf => |dw| { // we add here +16 like we do in airArg in CodeGen since we refer directly to // rbp as the start of function frame minus 8 bytes for caller's rbp preserved in the // prologue, and 8 bytes for return address. // TODO we need to make this more generic if we don't use rbp as the frame pointer // for example when -fomit-frame-pointer is set. const disp = @intCast(i32, max_stack) - off + 16; - try dbg_out.dbg_info.ensureUnusedCapacity(8); - dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); - const fixup = dbg_out.dbg_info.items.len; - dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + const dbg_info = dw.getDeclDebugInfoBuffer(); + try dbg_info.ensureUnusedCapacity(8); + dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); + const fixup = dbg_info.items.len; + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1, // we will backpatch it after we encode the displacement in LEB128 DW.OP.breg6, // .rbp TODO handle -fomit-frame-pointer }); - leb128.writeILEB128(dbg_out.dbg_info.writer(), disp) catch unreachable; - dbg_out.dbg_info.items[fixup] += @intCast(u8, dbg_out.dbg_info.items.len - fixup - 2); - try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + leb128.writeILEB128(dbg_info.writer(), disp) catch unreachable; + dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); try emit.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string }, .plan9 => {}, @@ -1113,21 +1118,17 @@ fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32 /// after codegen for this symbol is done. fn addDbgInfoTypeReloc(emit: *Emit, ty: Type) !void { switch (emit.debug_output) { - .dwarf => |dbg_out| { + .dwarf => |dw| { assert(ty.hasRuntimeBits()); - const index = dbg_out.dbg_info.items.len; - try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 - - const gop = try dbg_out.dbg_info_type_relocs.getOrPutContext(emit.bin_file.allocator, ty, .{ - .target = emit.target.*, - }); - if (!gop.found_existing) { - gop.value_ptr.* = .{ - .off = undefined, - .relocs = .{}, - }; - } - try gop.value_ptr.relocs.append(emit.bin_file.allocator, @intCast(u32, index)); + const dbg_info = dw.getDeclDebugInfoBuffer(); + const index = dbg_info.items.len; + try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 + const atom = switch (emit.bin_file.tag) { + .elf => &emit.function.mod_fn.owner_decl.link.elf.dbg_info_atom, + .macho => &emit.function.mod_fn.owner_decl.link.macho.dbg_info_atom, + else => unreachable, + }; + try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); }, .plan9 => {}, .none => {}, diff --git a/src/codegen.zig b/src/codegen.zig index f4803c77ea..fa84ad0d52 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -42,11 +42,7 @@ pub const GenerateSymbolError = error{ }; pub const DebugInfoOutput = union(enum) { - dwarf: struct { - dbg_line: *std.ArrayList(u8), - dbg_info: *std.ArrayList(u8), - dbg_info_type_relocs: *link.File.Dwarf.DbgInfoTypeRelocsTable, - }, + dwarf: *link.File.Dwarf, /// the plan9 debuginfo output is a bytecode with 4 opcodes /// assume all numbers/variables are bytes /// 0 w x y z -> interpret w x y z as a big-endian i32, and add it to the line offset diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 199bf9df61..83222090c0 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -39,16 +39,22 @@ atom_last: ?*Atom = null, abbrev_table_offset: ?u64 = null, +/// TODO replace with InternArena /// Table of debug symbol names. strtab: std.ArrayListUnmanaged(u8) = .{}, -deferred_error_sets_relocs: std.ArrayListUnmanaged(u32) = .{}, +/// Lives only as long as the analysed Decl. +/// Allocated with `initDeclState`. +/// Freed with `commitDeclState`. +decl_state: ?DeclState = null, /// List of atoms that are owned directly by the DWARF module. /// TODO convert links in DebugInfoAtom into indices and make /// sure every atom is owned by this module. managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, +global_abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{}, + pub const Atom = struct { /// Previous/next linked list pointers. /// This is the linked list node for this Decl's corresponding .debug_info tag. @@ -60,6 +66,54 @@ pub const Atom = struct { len: u32, }; +/// Represents state of the analysed Decl. +/// Includes Decl's abbrev table of type Types, matching arena +/// and a set of relocations that will be resolved once this +/// Decl's inner Atom is assigned an offset within the DWARF section. +pub const DeclState = struct { + dbg_line: std.ArrayList(u8), + dbg_info: std.ArrayList(u8), + abbrev_type_arena: std.heap.ArenaAllocator, + abbrev_table: std.ArrayListUnmanaged(AbbrevEntry) = .{}, + abbrev_resolver: std.HashMapUnmanaged( + Type, + u32, + Type.HashContext64, + std.hash_map.default_max_load_percentage, + ) = .{}, + abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{}, + + fn init(gpa: Allocator) DeclState { + return .{ + .dbg_line = std.ArrayList(u8).init(gpa), + .dbg_info = std.ArrayList(u8).init(gpa), + .abbrev_type_arena = std.heap.ArenaAllocator.init(gpa), + }; + } + + fn deinit(self: *DeclState, gpa: Allocator) void { + self.dbg_line.deinit(); + self.dbg_info.deinit(); + self.abbrev_type_arena.deinit(); + self.abbrev_table.deinit(gpa); + self.abbrev_resolver.deinit(gpa); + self.abbrev_relocs.deinit(gpa); + } +}; + +pub const AbbrevEntry = struct { + atom: *const Atom, + @"type": Type, + offset: u32, +}; + +pub const AbbrevRelocation = struct { + target: u32, + atom: *const Atom, + offset: u32, + addend: u32, +}; + pub const SrcFn = struct { /// Offset from the beginning of the Debug Line Program header that contains this function. off: u32, @@ -82,22 +136,6 @@ pub const SrcFn = struct { pub const PtrWidth = enum { p32, p64 }; -pub const DbgInfoTypeRelocsTable = std.ArrayHashMapUnmanaged( - Type, - DbgInfoTypeReloc, - Type.HashContext32, - true, -); - -pub const DbgInfoTypeReloc = struct { - /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). - /// This is where the .debug_info tag for the type is. - off: u32, - /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). - /// List of DW.AT.type / DW.FORM.ref4 that points to the type. - relocs: std.ArrayListUnmanaged(u32), -}; - pub const abbrev_compile_unit = 1; pub const abbrev_subprogram = 2; pub const abbrev_subprogram_retvoid = 3; @@ -143,7 +181,7 @@ pub fn deinit(self: *Dwarf) void { self.dbg_line_fn_free_list.deinit(gpa); self.atom_free_list.deinit(gpa); self.strtab.deinit(gpa); - self.deferred_error_sets_relocs.deinit(gpa); + self.global_abbrev_relocs.deinit(gpa); for (self.managed_atoms.items) |atom| { gpa.destroy(atom); @@ -151,25 +189,22 @@ pub fn deinit(self: *Dwarf) void { self.managed_atoms.deinit(gpa); } -pub const DeclDebugBuffers = struct { - dbg_line_buffer: std.ArrayList(u8), - dbg_info_buffer: std.ArrayList(u8), - dbg_info_type_relocs: DbgInfoTypeRelocsTable, -}; - -pub fn initDeclDebugInfo(self: *Dwarf, decl: *Module.Decl) !DeclDebugBuffers { +/// Initializes Decl's state and its matching output buffers. +/// Call this before `commitDeclState`. +pub fn initDeclState(self: *Dwarf, decl: *Module.Decl) !void { const tracy = trace(@src()); defer tracy.end(); const decl_name = try decl.getFullyQualifiedName(self.allocator); defer self.allocator.free(decl_name); - log.debug("initDeclDebugInfo {s}{*}", .{ decl_name, decl }); + log.debug("initDeclState {s}{*}", .{ decl_name, decl }); const gpa = self.allocator; - var dbg_line_buffer = std.ArrayList(u8).init(gpa); - var dbg_info_buffer = std.ArrayList(u8).init(gpa); - var dbg_info_type_relocs: DbgInfoTypeRelocsTable = .{}; + assert(self.decl_state == null); + self.decl_state = DeclState.init(gpa); + const dbg_line_buffer = &self.decl_state.?.dbg_line; + const dbg_info_buffer = &self.decl_state.?.dbg_info; assert(decl.has_tv); @@ -232,19 +267,17 @@ pub fn initDeclDebugInfo(self: *Dwarf, decl: *Module.Decl) !DeclDebugBuffers { dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT.low_pc, DW.FORM.addr assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len); dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4 + // if (fn_ret_has_bits) { - const gop = try dbg_info_type_relocs.getOrPutContext(gpa, fn_ret_type, .{ - .target = self.target, - }); - if (!gop.found_existing) { - gop.value_ptr.* = .{ - .off = undefined, - .relocs = .{}, - }; - } - try gop.value_ptr.relocs.append(gpa, @intCast(u32, dbg_info_buffer.items.len)); + const atom = switch (self.tag) { + .elf => &decl.link.elf.dbg_info_atom, + .macho => &decl.link.macho.dbg_info_atom, + else => unreachable, + }; + try self.addTypeReloc(atom, fn_ret_type, @intCast(u32, dbg_info_buffer.items.len), null); dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4 } + dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT.name, DW.FORM.string }, @@ -252,30 +285,28 @@ pub fn initDeclDebugInfo(self: *Dwarf, decl: *Module.Decl) !DeclDebugBuffers { // TODO implement .debug_info for global variables }, } - - return DeclDebugBuffers{ - .dbg_info_buffer = dbg_info_buffer, - .dbg_line_buffer = dbg_line_buffer, - .dbg_info_type_relocs = dbg_info_type_relocs, - }; } -pub fn commitDeclDebugInfo( +pub fn commitDeclState( self: *Dwarf, file: *File, module: *Module, decl: *Module.Decl, sym_addr: u64, sym_size: u64, - debug_buffers: *DeclDebugBuffers, ) !void { const tracy = trace(@src()); defer tracy.end(); + assert(self.decl_state != null); // Caller forgot to call `initDeclState` + defer { + self.decl_state.?.deinit(self.allocator); + self.decl_state = null; + } + const gpa = self.allocator; - var dbg_line_buffer = &debug_buffers.dbg_line_buffer; - var dbg_info_buffer = &debug_buffers.dbg_info_buffer; - var dbg_info_type_relocs = &debug_buffers.dbg_info_type_relocs; + var dbg_line_buffer = &self.decl_state.?.dbg_line; + var dbg_info_buffer = &self.decl_state.?.dbg_info; const target_endian = self.target.cpu.arch.endian(); @@ -473,25 +504,21 @@ pub fn commitDeclDebugInfo( if (dbg_info_buffer.items.len == 0) return; - // We need this for the duration of this function only so that for composite - // types such as []const u32, if the type *u32 is non-existent, we create - // it synthetically and store the backing bytes in this arena. After we are - // done with the relocations, we can safely deinit the entire memory slab. - // TODO currently, we do not store the relocations for future use, however, - // if that is the case, we should move memory management to a higher scope, - // such as linker scope, or whatnot. - var dbg_type_arena = std.heap.ArenaAllocator.init(gpa); - defer dbg_type_arena.deinit(); + const atom = switch (self.tag) { + .elf => &decl.link.elf.dbg_info_atom, + .macho => &decl.link.macho.dbg_info_atom, + else => unreachable, + }; + const decl_state = &self.decl_state.?; - var nested_ref4_relocs = std.ArrayList(u32).init(gpa); - defer nested_ref4_relocs.deinit(); { // Now we emit the .debug_info types of the Decl. These will count towards the size of // the buffer, so we have to do it before computing the offset, and we can't perform the actual // relocations yet. - var it: usize = 0; - while (it < dbg_info_type_relocs.count()) : (it += 1) { - const ty = dbg_info_type_relocs.keys()[it]; + var sym_index: usize = 0; + while (sym_index < decl_state.abbrev_table.items.len) : (sym_index += 1) { + const symbol = &decl_state.abbrev_table.items[sym_index]; + const ty = symbol.@"type"; const deferred: bool = blk: { if (ty.isAnyError()) break :blk true; switch (ty.tag()) { @@ -504,68 +531,38 @@ pub fn commitDeclDebugInfo( }; if (deferred) continue; - const value_ptr = dbg_info_type_relocs.getPtrContext(ty, .{ - .target = self.target, - }).?; - value_ptr.off = @intCast(u32, dbg_info_buffer.items.len); - try self.addDbgInfoType( - dbg_type_arena.allocator(), - module, - ty, - dbg_info_buffer, - dbg_info_type_relocs, - &nested_ref4_relocs, - ); + symbol.offset = @intCast(u32, dbg_info_buffer.items.len); + try self.addDbgInfoType(decl_state.abbrev_type_arena.allocator(), module, atom, ty, dbg_info_buffer); } } - const atom = switch (self.tag) { - .elf => &decl.link.elf.dbg_info_atom, - .macho => &decl.link.macho.dbg_info_atom, - else => unreachable, - }; try self.updateDeclDebugInfoAllocation(file, atom, @intCast(u32, dbg_info_buffer.items.len)); - { - // Now that we have the offset assigned we can finally perform type relocations. - for (dbg_info_type_relocs.keys()) |ty| { - const value = dbg_info_type_relocs.getContext(ty, .{ - .target = self.target, - }).?; - for (value.relocs.items) |off| { - const deferred: bool = blk: { - if (ty.isAnyError()) break :blk true; - switch (ty.tag()) { - .error_set_inferred => { - if (!ty.castTag(.error_set_inferred).?.data.is_resolved) break :blk true; - }, - else => {}, - } - break :blk false; - }; - if (deferred) { - // Defer until later - try self.deferred_error_sets_relocs.append(self.allocator, atom.off + off); - } else { - mem.writeInt( - u32, - dbg_info_buffer.items[off..][0..4], - atom.off + value.off, - target_endian, - ); - } + while (decl_state.abbrev_relocs.popOrNull()) |reloc| { + const symbol = decl_state.abbrev_table.items[reloc.target]; + const ty = symbol.@"type"; + const deferred: bool = blk: { + if (ty.isAnyError()) break :blk true; + switch (ty.tag()) { + .error_set_inferred => { + if (!ty.castTag(.error_set_inferred).?.data.is_resolved) break :blk true; + }, + else => {}, } - } - // Offsets to positions with known a priori relative displacement values. - // Here, we just need to add the offset of the atom to the read value in the - // relocated cell. - // TODO Should probably generalise this with type relocs. - for (nested_ref4_relocs.items) |off| { - const addend = mem.readInt(u32, dbg_info_buffer.items[off..][0..4], target_endian); + break :blk false; + }; + if (deferred) { + try self.global_abbrev_relocs.append(gpa, .{ + .target = undefined, + .offset = reloc.offset, + .atom = reloc.atom, + .addend = reloc.addend, + }); + } else { mem.writeInt( u32, - dbg_info_buffer.items[off..][0..4], - atom.off + addend, + dbg_info_buffer.items[reloc.offset..][0..@sizeOf(u32)], + symbol.atom.off + symbol.offset + reloc.addend, target_endian, ); } @@ -574,77 +571,6 @@ pub fn commitDeclDebugInfo( try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items); } -pub fn commitErrorSetDebugInfo(self: *Dwarf, file: *File, module: *Module) !void { - if (self.deferred_error_sets_relocs.items.len == 0) return; // Nothing to do - - const gpa = self.allocator; - var arena_alloc = std.heap.ArenaAllocator.init(gpa); - defer arena_alloc.deinit(); - const arena = arena_alloc.allocator(); - - const error_set = try arena.create(Module.ErrorSet); - const ty = try Type.Tag.error_set.create(arena, error_set); - var names = Module.ErrorSet.NameMap{}; - try names.ensureUnusedCapacity(arena, module.global_error_set.count()); - var it = module.global_error_set.keyIterator(); - while (it.next()) |key| { - names.putAssumeCapacityNoClobber(key.*, {}); - } - error_set.names = names; - - var dbg_info_buffer = std.ArrayList(u8).init(arena); - try self.addDbgInfoErrorSet(arena, module, ty, &dbg_info_buffer); - - const atom = try gpa.create(Atom); - errdefer gpa.destroy(atom); - atom.* = .{ - .prev = null, - .next = null, - .off = 0, - .len = 0, - }; - try self.managed_atoms.append(gpa, atom); - try self.updateDeclDebugInfoAllocation(file, atom, @intCast(u32, dbg_info_buffer.items.len)); - try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items); - - const file_pos = blk: { - switch (self.tag) { - .elf => { - const elf_file = file.cast(File.Elf).?; - const debug_info_sect = &elf_file.sections.items[elf_file.debug_info_section_index.?]; - break :blk debug_info_sect.sh_offset; - }, - .macho => { - const macho_file = file.cast(File.MachO).?; - const d_sym = &macho_file.d_sym.?; - const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment; - const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?]; - break :blk debug_info_sect.offset; - }, - else => unreachable, - } - }; - - const target_endian = self.target.cpu.arch.endian(); - var buf: [@sizeOf(u32)]u8 = undefined; - while (self.deferred_error_sets_relocs.popOrNull()) |reloc| { - mem.writeInt(u32, &buf, atom.off, target_endian); - - switch (self.tag) { - .elf => { - const elf_file = file.cast(File.Elf).?; - try elf_file.base.file.?.pwriteAll(&buf, file_pos + reloc); - }, - .macho => { - const macho_file = file.cast(File.MachO).?; - const d_sym = &macho_file.d_sym.?; - try d_sym.file.pwriteAll(&buf, file_pos + reloc); - }, - else => unreachable, - } - } -} - fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u32) !void { const tracy = trace(@src()); defer tracy.end(); @@ -904,14 +830,12 @@ fn addDbgInfoType( self: *Dwarf, arena: Allocator, module: *Module, + atom: *Atom, ty: Type, dbg_info_buffer: *std.ArrayList(u8), - dbg_info_type_relocs: *DbgInfoTypeRelocsTable, - nested_ref4_relocs: *std.ArrayList(u32), ) error{OutOfMemory}!void { const target = self.target; const target_endian = self.target.cpu.arch.endian(); - var relocs = std.ArrayList(struct { ty: Type, reloc: u32 }).init(arena); switch (ty.zigTypeTag()) { .NoReturn => unreachable, @@ -970,7 +894,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 var index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = Type.bool, .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, Type.bool, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata try dbg_info_buffer.ensureUnusedCapacity(6); dbg_info_buffer.appendAssumeCapacity(0); @@ -982,7 +906,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = payload_ty, .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, payload_ty, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata const offset = abi_size - payload_ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), offset); @@ -1011,7 +935,7 @@ fn addDbgInfoType( try dbg_info_buffer.resize(index + 4); var buf = try arena.create(Type.SlicePtrFieldTypeBuffer); const ptr_ty = ty.slicePtrFieldType(buf); - try relocs.append(.{ .ty = ptr_ty, .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, ptr_ty, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata try dbg_info_buffer.ensureUnusedCapacity(6); dbg_info_buffer.appendAssumeCapacity(0); @@ -1023,7 +947,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = Type.initTag(.usize), .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, Type.usize, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata try dbg_info_buffer.ensureUnusedCapacity(2); dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize)); @@ -1035,7 +959,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 const index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = ty.childType(), .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, ty.childType(), @intCast(u32, index), null); } }, .Struct => blk: { @@ -1059,7 +983,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 var index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = field, .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, field, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata const field_off = ty.structFieldOffset(field_index, target); try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); @@ -1090,7 +1014,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 var index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = field.ty, .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, field.ty, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata const field_off = ty.structFieldOffset(field_index, target); try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); @@ -1169,14 +1093,8 @@ fn addDbgInfoType( dbg_info_buffer.appendAssumeCapacity(0); // DW.AT.type, DW.FORM.ref4 const inner_union_index = dbg_info_buffer.items.len; - try dbg_info_buffer.ensureUnusedCapacity(4); - mem.writeInt( - u32, - dbg_info_buffer.addManyAsArrayAssumeCapacity(4), - @intCast(u32, inner_union_index + 5), - target_endian, - ); - try nested_ref4_relocs.append(@intCast(u32, inner_union_index)); + try dbg_info_buffer.resize(inner_union_index + 4); + try self.addTypeReloc(atom, ty, @intCast(u32, inner_union_index), 5); // DW.AT.data_member_location, DW.FORM.sdata try leb128.writeULEB128(dbg_info_buffer.writer(), payload_offset); } @@ -1203,7 +1121,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 const index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = field.ty, .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, field.ty, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata try dbg_info_buffer.append(0); } @@ -1220,7 +1138,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 const index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = union_obj.tag_ty, .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, union_obj.tag_ty, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata try leb128.writeULEB128(dbg_info_buffer.writer(), tag_offset); @@ -1229,7 +1147,40 @@ fn addDbgInfoType( } }, .ErrorSet => { - try self.addDbgInfoErrorSet(arena, module, ty, dbg_info_buffer); + // DW.AT.enumeration_type + try dbg_info_buffer.append(abbrev_enum_type); + // DW.AT.byte_size, DW.FORM.sdata + const abi_size = ty.abiSize(target); + try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); + // DW.AT.name, DW.FORM.string + const name = try ty.nameAllocArena(arena, target); + try dbg_info_buffer.writer().print("{s}\x00", .{name}); + + // DW.AT.enumerator + const no_error = "(no error)"; + try dbg_info_buffer.ensureUnusedCapacity(no_error.len + 2 + @sizeOf(u64)); + dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity(no_error); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.const_value, DW.FORM.data8 + mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian); + + const error_names = ty.errorSetNames(); + for (error_names) |error_name| { + const kv = module.getErrorValue(error_name) catch unreachable; + // DW.AT.enumerator + try dbg_info_buffer.ensureUnusedCapacity(error_name.len + 2 + @sizeOf(u64)); + dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity(error_name); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.const_value, DW.FORM.data8 + mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), kv.value, target_endian); + } + + // DW.AT.enumeration_type delimit children + try dbg_info_buffer.append(0); }, .ErrorUnion => { const error_ty = ty.errorUnionSet(); @@ -1255,7 +1206,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 var index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = payload_ty, .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, payload_ty, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata try leb128.writeULEB128(dbg_info_buffer.writer(), payload_off); @@ -1268,7 +1219,7 @@ fn addDbgInfoType( // DW.AT.type, DW.FORM.ref4 index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try relocs.append(.{ .ty = error_ty, .reloc = @intCast(u32, index) }); + try self.addTypeReloc(atom, error_ty, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata try dbg_info_buffer.append(0); @@ -1280,65 +1231,6 @@ fn addDbgInfoType( try dbg_info_buffer.append(abbrev_pad1); }, } - - for (relocs.items) |rel| { - const gop = try dbg_info_type_relocs.getOrPutContext(self.allocator, rel.ty, .{ - .target = self.target, - }); - if (!gop.found_existing) { - gop.value_ptr.* = .{ - .off = undefined, - .relocs = .{}, - }; - } - try gop.value_ptr.relocs.append(self.allocator, rel.reloc); - } -} - -fn addDbgInfoErrorSet( - self: *Dwarf, - arena: Allocator, - module: *Module, - ty: Type, - dbg_info_buffer: *std.ArrayList(u8), -) error{OutOfMemory}!void { - const target = self.target; - const target_endian = self.target.cpu.arch.endian(); - - // DW.AT.enumeration_type - try dbg_info_buffer.append(abbrev_enum_type); - // DW.AT.byte_size, DW.FORM.sdata - const abi_size = ty.abiSize(target); - try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); - // DW.AT.name, DW.FORM.string - const name = try ty.nameAllocArena(arena, target); - try dbg_info_buffer.writer().print("{s}\x00", .{name}); - - // DW.AT.enumerator - const no_error = "(no error)"; - try dbg_info_buffer.ensureUnusedCapacity(no_error.len + 2 + @sizeOf(u64)); - dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity(no_error); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.const_value, DW.FORM.data8 - mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian); - - const error_names = ty.errorSetNames(); - for (error_names) |error_name| { - const kv = module.getErrorValue(error_name) catch unreachable; - // DW.AT.enumerator - try dbg_info_buffer.ensureUnusedCapacity(error_name.len + 2 + @sizeOf(u64)); - dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity(error_name); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.const_value, DW.FORM.data8 - mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), kv.value, target_endian); - } - - // DW.AT.enumeration_type delimit children - try dbg_info_buffer.append(0); } pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { @@ -2059,3 +1951,115 @@ fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch std.math.maxInt(@TypeOf(actual_size)); } + +pub fn addTypeReloc(self: *Dwarf, atom: *const Atom, ty: Type, offset: u32, addend: ?u32) !void { + const decl_state = &self.decl_state.?; + const gpa = self.allocator; + const resolv = decl_state.abbrev_resolver.getContext(ty, .{ + .target = self.target, + }) orelse blk: { + const sym_index = @intCast(u32, decl_state.abbrev_table.items.len); + try decl_state.abbrev_table.append(gpa, .{ + .atom = atom, + .@"type" = ty, + .offset = undefined, + }); + log.debug("@{d}: {}", .{ sym_index, ty.fmtDebug() }); + try decl_state.abbrev_resolver.putNoClobberContext(gpa, ty, sym_index, .{ + .target = self.target, + }); + break :blk decl_state.abbrev_resolver.getContext(ty, .{ + .target = self.target, + }).?; + }; + const add: u32 = addend orelse 0; + + log.debug("{x}: @{d} + {x}", .{ offset, resolv, add }); + try decl_state.abbrev_relocs.append(gpa, .{ + .target = resolv, + .atom = atom, + .offset = offset, + .addend = add, + }); +} + +pub fn getDeclDebugLineBuffer(self: *Dwarf) *std.ArrayList(u8) { + return &self.decl_state.?.dbg_line; +} + +pub fn getDeclDebugInfoBuffer(self: *Dwarf) *std.ArrayList(u8) { + return &self.decl_state.?.dbg_info; +} + +pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void { + if (self.global_abbrev_relocs.items.len > 0) { + const gpa = self.allocator; + var arena_alloc = std.heap.ArenaAllocator.init(gpa); + defer arena_alloc.deinit(); + const arena = arena_alloc.allocator(); + + const error_set = try arena.create(Module.ErrorSet); + const error_ty = try Type.Tag.error_set.create(arena, error_set); + var names = Module.ErrorSet.NameMap{}; + try names.ensureUnusedCapacity(arena, module.global_error_set.count()); + var it = module.global_error_set.keyIterator(); + while (it.next()) |key| { + names.putAssumeCapacityNoClobber(key.*, {}); + } + error_set.names = names; + + const atom = try gpa.create(Atom); + errdefer gpa.destroy(atom); + atom.* = .{ + .prev = null, + .next = null, + .off = 0, + .len = 0, + }; + + var dbg_info_buffer = std.ArrayList(u8).init(arena); + try self.addDbgInfoType(arena, module, atom, error_ty, &dbg_info_buffer); + + try self.managed_atoms.append(gpa, atom); + try self.updateDeclDebugInfoAllocation(file, atom, @intCast(u32, dbg_info_buffer.items.len)); + try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items); + + const file_pos = blk: { + switch (self.tag) { + .elf => { + const elf_file = file.cast(File.Elf).?; + const debug_info_sect = &elf_file.sections.items[elf_file.debug_info_section_index.?]; + break :blk debug_info_sect.sh_offset; + }, + .macho => { + const macho_file = file.cast(File.MachO).?; + const d_sym = &macho_file.d_sym.?; + const dwarf_segment = &d_sym.load_commands.items[d_sym.dwarf_segment_cmd_index.?].segment; + const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?]; + break :blk debug_info_sect.offset; + }, + else => unreachable, + } + }; + + var buf: [@sizeOf(u32)]u8 = undefined; + mem.writeInt(u32, &buf, atom.off, self.target.cpu.arch.endian()); + + while (self.global_abbrev_relocs.popOrNull()) |reloc| { + switch (self.tag) { + .elf => { + const elf_file = file.cast(File.Elf).?; + try elf_file.base.file.?.pwriteAll(&buf, file_pos + reloc.atom.off + reloc.offset); + }, + .macho => { + const macho_file = file.cast(File.MachO).?; + const d_sym = &macho_file.d_sym.?; + try d_sym.file.pwriteAll(&buf, file_pos + reloc.atom.off + reloc.offset); + }, + else => unreachable, + } + } + } + + assert(self.decl_state == null); +} diff --git a/src/link/Elf.zig b/src/link/Elf.zig index dda2841db8..771a57b26e 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -958,8 +958,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { const target_endian = self.base.options.target.cpu.arch.endian(); const foreign_endian = target_endian != builtin.cpu.arch.endian(); - if (self.dwarf) |*dwarf| { - try dwarf.commitErrorSetDebugInfo(&self.base, module); + if (self.dwarf) |*dw| { + try dw.flushModule(&self.base, module); } { @@ -2232,13 +2232,6 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { } } -fn deinitRelocs(gpa: Allocator, table: *link.File.Dwarf.DbgInfoTypeRelocsTable) void { - for (table.values()) |*value| { - value.relocs.deinit(gpa); - } - table.deinit(gpa); -} - fn getDeclPhdrIndex(self: *Elf, decl: *Module.Decl) !u16 { const ty = decl.ty; const zig_ty = ty.zigTypeTag(); @@ -2346,26 +2339,13 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven const decl = func.owner_decl; self.freeUnnamedConsts(decl); - var debug_buffers_buf: Dwarf.DeclDebugBuffers = undefined; - const debug_buffers = if (self.dwarf) |*dw| blk: { - debug_buffers_buf = try dw.initDeclDebugInfo(decl); - break :blk &debug_buffers_buf; - } else null; - defer { - if (debug_buffers) |dbg| { - dbg.dbg_line_buffer.deinit(); - dbg.dbg_info_buffer.deinit(); - deinitRelocs(self.base.allocator, &dbg.dbg_info_type_relocs); - } + if (self.dwarf) |*dw| { + try dw.initDeclState(decl); } - const res = if (debug_buffers) |dbg| + const res = if (self.dwarf) |*dw| try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ - .dwarf = .{ - .dbg_line = &dbg.dbg_line_buffer, - .dbg_info = &dbg.dbg_info_buffer, - .dbg_info_type_relocs = &dbg.dbg_info_type_relocs, - }, + .dwarf = dw, }) else try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); @@ -2379,15 +2359,8 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven }, }; const local_sym = try self.updateDeclCode(decl, code, elf.STT_FUNC); - if (debug_buffers) |dbg| { - try self.dwarf.?.commitDeclDebugInfo( - &self.base, - module, - decl, - local_sym.st_value, - local_sym.st_size, - dbg, - ); + if (self.dwarf) |*dw| { + try dw.commitDeclState(&self.base, module, decl, local_sym.st_value, local_sym.st_size); } // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. @@ -2421,31 +2394,18 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); - var debug_buffers_buf: Dwarf.DeclDebugBuffers = undefined; - const debug_buffers = if (self.dwarf) |*dw| blk: { - debug_buffers_buf = try dw.initDeclDebugInfo(decl); - break :blk &debug_buffers_buf; - } else null; - defer { - if (debug_buffers) |dbg| { - dbg.dbg_line_buffer.deinit(); - dbg.dbg_info_buffer.deinit(); - deinitRelocs(self.base.allocator, &dbg.dbg_info_type_relocs); - } + if (self.dwarf) |*dw| { + try dw.initDeclState(decl); } // TODO implement .debug_info for global variables const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; - const res = if (debug_buffers) |dbg| + const res = if (self.dwarf) |*dw| try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ .ty = decl.ty, .val = decl_val, }, &code_buffer, .{ - .dwarf = .{ - .dbg_line = &dbg.dbg_line_buffer, - .dbg_info = &dbg.dbg_info_buffer, - .dbg_info_type_relocs = &dbg.dbg_info_type_relocs, - }, + .dwarf = dw, }, .{ .parent_atom_index = decl.link.elf.local_sym_index, }) @@ -2468,8 +2428,8 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { }; const local_sym = try self.updateDeclCode(decl, code, elf.STT_OBJECT); - if (debug_buffers) |dbg| { - try self.dwarf.?.commitDeclDebugInfo(&self.base, module, decl, local_sym.st_value, local_sym.st_size, dbg); + if (self.dwarf) |*dw| { + try dw.commitDeclState(&self.base, module, decl, local_sym.st_value, local_sym.st_size); } // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. diff --git a/src/link/MachO.zig b/src/link/MachO.zig index e613772f22..31ada3b4ee 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -453,6 +453,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); + if (self.d_sym) |*d_sym| { + if (self.base.options.module) |module| { + try d_sym.dwarf.flushModule(&self.base, module); + } + } + // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { @@ -3670,32 +3676,17 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); - var debug_buffers_buf: link.File.Dwarf.DeclDebugBuffers = undefined; - const debug_buffers = if (self.d_sym) |*d_sym| blk: { - debug_buffers_buf = try d_sym.initDeclDebugInfo(module, decl); - break :blk &debug_buffers_buf; - } else null; - defer { - if (debug_buffers) |dbg| { - dbg.dbg_line_buffer.deinit(); - dbg.dbg_info_buffer.deinit(); - for (dbg.dbg_info_type_relocs.values()) |*value| { - value.relocs.deinit(self.base.allocator); - } - dbg.dbg_info_type_relocs.deinit(self.base.allocator); - } + if (self.d_sym) |*d_sym| { + try d_sym.dwarf.initDeclState(decl); } - const res = if (debug_buffers) |dbg| + const res = if (self.d_sym) |*d_sym| try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ - .dwarf = .{ - .dbg_line = &dbg.dbg_line_buffer, - .dbg_info = &dbg.dbg_info_buffer, - .dbg_info_type_relocs = &dbg.dbg_info_type_relocs, - }, + .dwarf = &d_sym.dwarf, }) else try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); + switch (res) { .appended => { try decl.link.macho.code.appendSlice(self.base.allocator, code_buffer.items); @@ -3707,12 +3698,10 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv }, } - _ = try self.placeDecl(decl, decl.link.macho.code.items.len); + const symbol = try self.placeDecl(decl, decl.link.macho.code.items.len); - if (debug_buffers) |db| { - if (self.d_sym) |*d_sym| { - try d_sym.commitDeclDebugInfo(module, decl, db); - } + if (self.d_sym) |*d_sym| { + try d_sym.dwarf.commitDeclState(&self.base, module, decl, symbol.n_value, decl.link.macho.size); } // Since we updated the vaddr and the size, each corresponding export symbol also @@ -3812,33 +3801,17 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); - var debug_buffers_buf: link.File.Dwarf.DeclDebugBuffers = undefined; - const debug_buffers = if (self.d_sym) |*d_sym| blk: { - debug_buffers_buf = try d_sym.initDeclDebugInfo(module, decl); - break :blk &debug_buffers_buf; - } else null; - defer { - if (debug_buffers) |dbg| { - dbg.dbg_line_buffer.deinit(); - dbg.dbg_info_buffer.deinit(); - for (dbg.dbg_info_type_relocs.values()) |*value| { - value.relocs.deinit(self.base.allocator); - } - dbg.dbg_info_type_relocs.deinit(self.base.allocator); - } + if (self.d_sym) |*d_sym| { + try d_sym.dwarf.initDeclState(decl); } const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; - const res = if (debug_buffers) |dbg| + const res = if (self.d_sym) |*d_sym| try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ .ty = decl.ty, .val = decl_val, }, &code_buffer, .{ - .dwarf = .{ - .dbg_line = &dbg.dbg_line_buffer, - .dbg_info = &dbg.dbg_info_buffer, - .dbg_info_type_relocs = &dbg.dbg_info_type_relocs, - }, + .dwarf = &d_sym.dwarf, }, .{ .parent_atom_index = decl.link.macho.local_sym_index, }) @@ -3870,7 +3843,11 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { }, } }; - _ = try self.placeDecl(decl, code.len); + const symbol = try self.placeDecl(decl, code.len); + + if (self.d_sym) |*d_sym| { + try d_sym.dwarf.commitDeclState(&self.base, module, decl, symbol.n_value, decl.link.macho.size); + } // Since we updated the vaddr and the size, each corresponding export symbol also // needs to be updated. @@ -4084,8 +4061,9 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 } pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void { + _ = module; if (self.d_sym) |*d_sym| { - try d_sym.updateDeclLineNumber(module, decl); + try d_sym.dwarf.updateDeclLineNumber(&self.base, decl); } } diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 9fc2828ea1..885f0ca6a8 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -645,25 +645,3 @@ fn writeStringTable(self: *DebugSymbols) !void { self.load_commands_dirty = true; } - -pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const Module.Decl) !void { - _ = module; - return self.dwarf.updateDeclLineNumber(&self.base.base, decl); -} - -/// Caller owns the returned memory. -pub fn initDeclDebugInfo(self: *DebugSymbols, module: *Module, decl: *Module.Decl) !Dwarf.DeclDebugBuffers { - _ = module; - return self.dwarf.initDeclDebugInfo(decl); -} - -pub fn commitDeclDebugInfo( - self: *DebugSymbols, - module: *Module, - decl: *Module.Decl, - debug_buffers: *Dwarf.DeclDebugBuffers, -) !void { - const symbol = self.base.locals.items[decl.link.macho.local_sym_index]; - const atom = &decl.link.macho; - return self.dwarf.commitDeclDebugInfo(&self.base.base, module, decl, symbol.n_value, atom.size, debug_buffers); -} From 7217148edf28a0d3ab69b8c36b13d29c023e12fd Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 27 Mar 2022 21:05:42 +0300 Subject: [PATCH 0968/2031] Value: hash lazy_align --- build.zig | 3 ++- src/value.zig | 11 +++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/build.zig b/build.zig index 5896ab1a8c..7849245462 100644 --- a/build.zig +++ b/build.zig @@ -613,7 +613,8 @@ fn addCxxKnownPath( ctx.cxx_compiler, b.fmt("-print-file-name={s}", .{objname}), }); - const path_unpadded = mem.tokenize(u8, path_padded, "\r\n").next().?; + var tokenizer = mem.tokenize(u8, path_padded, "\r\n"); + const path_unpadded = tokenizer.next().?; if (mem.eql(u8, path_unpadded, objname)) { if (errtxt) |msg| { std.debug.print("{s}", .{msg}); diff --git a/src/value.zig b/src/value.zig index 0467f0362c..926a52c2b7 100644 --- a/src/value.zig +++ b/src/value.zig @@ -461,7 +461,7 @@ pub const Value = extern union { => unreachable, .ty, .lazy_align => { - const payload = self.castTag(.ty).?; + const payload = self.cast(Payload.Ty).?; const new_payload = try arena.create(Payload.Ty); new_payload.* = .{ .base = payload.base, @@ -718,7 +718,7 @@ pub const Value = extern union { .lazy_align => { try out_stream.writeAll("@alignOf("); try val.castTag(.lazy_align).?.data.dump("", options, out_stream); - try out_stream.writeAll(")"); + return try out_stream.writeAll(")"); }, .int_type => { const int_type = val.castTag(.int_type).?.data; @@ -2478,6 +2478,13 @@ pub const Value = extern union { .the_only_possible_value, => return hashInt(ptr_val, hasher, target), + .lazy_align => { + // Bit weird to have this here but this function is also called + // on integers. + const ty = ptr_val.castTag(.lazy_align).?.data; + ty.hashWithHasher(hasher, target); + }, + else => unreachable, } } From d5e89dd70b2b5e26e2457d276ce25125f111b29a Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 27 Mar 2022 22:52:28 +0300 Subject: [PATCH 0969/2031] stage2: add temporary workaround for lack of argument count check --- src/Sema.zig | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index da98cb488d..a5b0804d9c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3661,8 +3661,12 @@ fn zirParamType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A fn_ty.fnInfo(); if (param_index >= fn_info.param_types.len) { - assert(fn_info.is_var_args); - return sema.addType(Type.initTag(.var_args_param)); + if (fn_info.is_var_args) { + return sema.addType(Type.initTag(.var_args_param)); + } + // TODO implement begin_call/end_call Zir instructions and check + // argument count before casting arguments to parameter types. + return sema.fail(block, callee_src, "wrong number of arguments", .{}); } if (fn_info.param_types[param_index].tag() == .generic_poison) { From 6d2ec7a4e39e3464bb5f14747937df9544e89ee7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 27 Mar 2022 14:21:32 -0700 Subject: [PATCH 0970/2031] LLVM: handle aggregate_init for packed structs --- src/codegen/llvm.zig | 28 ++++++++++++++++++++++++++++ src/codegen/llvm/bindings.zig | 8 ++++++++ test/behavior/struct.zig | 21 +++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 049f7bfad6..431d158293 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -6459,6 +6459,34 @@ pub const FuncGen = struct { return vector; }, .Struct => { + if (result_ty.containerLayout() == .Packed) { + const struct_obj = result_ty.castTag(.@"struct").?.data; + const big_bits = struct_obj.packedIntegerBits(target); + const int_llvm_ty = self.dg.context.intType(big_bits); + const fields = struct_obj.fields.values(); + comptime assert(Type.packed_struct_layout_version == 2); + var running_int: *const llvm.Value = int_llvm_ty.constNull(); + var running_bits: u16 = 0; + for (elements) |elem, i| { + const field = fields[i]; + if (!field.ty.hasRuntimeBitsIgnoreComptime()) continue; + + const non_int_val = try self.resolveInst(elem); + const ty_bit_size = @intCast(u16, field.ty.bitSize(target)); + const small_int_ty = self.dg.context.intType(ty_bit_size); + const small_int_val = self.builder.buildBitCast(non_int_val, small_int_ty, ""); + const shift_rhs = int_llvm_ty.constInt(running_bits, .False); + // If the field is as large as the entire packed struct, this + // zext would go from, e.g. i16 to i16. This is legal with + // constZExtOrBitCast but not legal with constZExt. + const extended_int_val = self.builder.buildZExtOrBitCast(small_int_val, int_llvm_ty, ""); + const shifted = self.builder.buildShl(extended_int_val, shift_rhs, ""); + running_int = self.builder.buildOr(running_int, shifted, ""); + running_bits += ty_bit_size; + } + return running_int; + } + var ptr_ty_buf: Type.Payload.Pointer = undefined; if (isByRef(result_ty)) { diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index b7a3ff7230..33b862499c 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -448,6 +448,14 @@ pub const Builder = opaque { Name: [*:0]const u8, ) *const Value; + pub const buildZExtOrBitCast = LLVMBuildZExtOrBitCast; + extern fn LLVMBuildZExtOrBitCast( + *const Builder, + Val: *const Value, + DestTy: *const Type, + Name: [*:0]const u8, + ) *const Value; + pub const buildSExt = LLVMBuildSExt; extern fn LLVMBuildSExt( *const Builder, diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 4bed1f56cc..a46605c424 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1294,3 +1294,24 @@ test "loading a struct pointer perfoms a copy" { try expect(s2.b == 2); try expect(s2.c == 3); } + +test "packed struct aggregate init" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn foo(a: i2, b: i6) u8 { + return @bitCast(u8, P{ .a = a, .b = b }); + } + + const P = packed struct { + a: i2, + b: i6, + }; + }; + const result = @bitCast(u8, S.foo(1, 2)); + try expect(result == 9); +} From c8f844027185b808e2176a73fd41c955bc6e153e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 27 Mar 2022 14:40:24 -0700 Subject: [PATCH 0971/2031] stage1: disable failing test The new behavior test introduced in the previous commit is not passing for stage1 on mips. --- test/behavior/struct.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index a46605c424..aea67005f4 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1296,6 +1296,10 @@ test "loading a struct pointer perfoms a copy" { } test "packed struct aggregate init" { + if (builtin.zig_backend == .stage1) { + // stage1 fails this test on mips + return error.SkipZigTest; + } if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO From 3fc0e0c57bcc8eb4a14712abc20c16677722fce0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 27 Mar 2022 14:52:12 -0700 Subject: [PATCH 0972/2031] Sema: implement `@setFloatMode` We are putting off actual optimization of floats because we have a couple proposals being considered which would change how it works. In the meantime, lowering optimized float mode to be the same as strict is a perfectly legal way to implement the Zig language specification. --- src/Sema.zig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index beab957529..3afc6e2bd3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4315,7 +4315,13 @@ fn zirSetCold(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!voi fn zirSetFloatMode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src: LazySrcLoc = inst_data.src(); - return sema.fail(block, src, "TODO: implement Sema.zirSetFloatMode", .{}); + const float_mode = try sema.resolveBuiltinEnum(block, src, inst_data.operand, "FloatMode"); + switch (float_mode) { + .Strict => return, + .Optimized => { + // TODO implement optimized float mode + }, + } } fn zirSetRuntimeSafety(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { From 7be340e3cc62285d77cbd13f19f4ff0a2a982e82 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 27 Mar 2022 14:53:40 -0700 Subject: [PATCH 0973/2031] std.crypto.blake3: use `@Vector` instead of `std.meta.Vector` --- lib/std/crypto/blake3.zig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/std/crypto/blake3.zig b/lib/std/crypto/blake3.zig index 7f2c21be33..a31dcc814a 100644 --- a/lib/std/crypto/blake3.zig +++ b/lib/std/crypto/blake3.zig @@ -7,7 +7,6 @@ const fmt = std.fmt; const math = std.math; const mem = std.mem; const testing = std.testing; -const Vector = std.meta.Vector; const ChunkIterator = struct { slice: []u8, @@ -59,7 +58,7 @@ const DERIVE_KEY_CONTEXT: u8 = 1 << 5; const DERIVE_KEY_MATERIAL: u8 = 1 << 6; const CompressVectorized = struct { - const Lane = Vector(4, u32); + const Lane = @Vector(4, u32); const Rows = [4]Lane; inline fn g(comptime even: bool, rows: *Rows, m: Lane) void { @@ -132,8 +131,8 @@ const CompressVectorized = struct { rows[0] ^= rows[2]; rows[1] ^= rows[3]; - rows[2] ^= Vector(4, u32){ chaining_value[0], chaining_value[1], chaining_value[2], chaining_value[3] }; - rows[3] ^= Vector(4, u32){ chaining_value[4], chaining_value[5], chaining_value[6], chaining_value[7] }; + rows[2] ^= @Vector(4, u32){ chaining_value[0], chaining_value[1], chaining_value[2], chaining_value[3] }; + rows[3] ^= @Vector(4, u32){ chaining_value[4], chaining_value[5], chaining_value[6], chaining_value[7] }; return @bitCast([16]u32, rows); } From 25d4c5df706bb8ca6e7882f80d6f8209b9ad31f8 Mon Sep 17 00:00:00 2001 From: ominitay <37453713+ominitay@users.noreply.github.com> Date: Mon, 28 Mar 2022 12:10:36 +0100 Subject: [PATCH 0974/2031] std.mem.zeroInit: Fix behaviour with empty initialiser --- lib/std/mem.zig | 50 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 01680791dc..88d0e877b8 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -432,6 +432,13 @@ pub fn zeroInit(comptime T: type, init: anytype) T { .Struct => |init_info| { var value = std.mem.zeroes(T); + inline for (struct_info.fields) |field| { + if (field.default_value) |default_value_ptr| { + const default_value = @ptrCast(*const field.field_type, default_value_ptr).*; + @field(value, field.name) = default_value; + } + } + if (init_info.is_tuple) { inline for (init_info.fields) |field, i| { @field(value, struct_info.fields[i].name) = @field(init, field.name); @@ -443,21 +450,14 @@ pub fn zeroInit(comptime T: type, init: anytype) T { if (!@hasField(T, field.name)) { @compileError("Encountered an initializer for `" ++ field.name ++ "`, but it is not a field of " ++ @typeName(T)); } - } - inline for (struct_info.fields) |field| { - if (@hasField(Init, field.name)) { - switch (@typeInfo(field.field_type)) { - .Struct => { - @field(value, field.name) = zeroInit(field.field_type, @field(init, field.name)); - }, - else => { - @field(value, field.name) = @field(init, field.name); - }, - } - } else if (field.default_value) |default_value_ptr| { - const default_value = @ptrCast(*const field.field_type, default_value_ptr).*; - @field(value, field.name) = default_value; + switch (@typeInfo(field.field_type)) { + .Struct => { + @field(value, field.name) = zeroInit(field.field_type, @field(init, field.name)); + }, + else => { + @field(value, field.name) = @field(init, field.name); + }, } } @@ -515,6 +515,28 @@ test "zeroInit" { .b = 0, .a = 0, }, c); + + const Foo = struct { + foo: u8 = 69, + bar: u8, + }; + + const f = zeroInit(Foo, .{}); + try testing.expectEqual(Foo{ + .foo = 69, + .bar = 0, + }, f); + + const Bar = struct { + foo: u32 = 666, + bar: u32 = 420, + }; + + const b = zeroInit(Bar, .{69}); + try testing.expectEqual(Bar{ + .foo = 69, + .bar = 420, + }, b); } /// Compares two slices of numbers lexicographically. O(n). From 107052aded5dc1666c687838e8e2995654f17a45 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 28 Mar 2022 13:20:09 +0200 Subject: [PATCH 0975/2031] x64: implement add, sub and mul with overflow --- src/arch/x86_64/CodeGen.zig | 354 ++++++++++++++++++++++++++---------- src/arch/x86_64/Emit.zig | 7 + src/arch/x86_64/Mir.zig | 7 + 3 files changed, 270 insertions(+), 98 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index c95e86a424..45d73a4180 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -119,6 +119,12 @@ pub const MCValue = union(enum) { immediate: u64, /// The value is in a target-specific register. register: Register, + /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the register, + /// and the operation is an unsigned operation. + register_overflow_unsigned: Register, + /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the register, + /// and the operation is a signed operation. + register_overflow_signed: Register, /// The value is in memory at a hard-coded address. /// If the type is a pointer, it means the pointer address is at this memory location. memory: u64, @@ -144,7 +150,10 @@ pub const MCValue = union(enum) { fn isMemory(mcv: MCValue) bool { return switch (mcv) { - .memory, .stack_offset => true, + .memory, + .stack_offset, + .ptr_stack_offset, + => true, else => false, }; } @@ -168,6 +177,8 @@ pub const MCValue = union(enum) { .compare_flags_signed, .ptr_stack_offset, .undef, + .register_overflow_unsigned, + .register_overflow_signed, => false, .register, @@ -176,16 +187,33 @@ pub const MCValue = union(enum) { }; } + fn usesCompareFlags(mcv: MCValue) bool { + return switch (mcv) { + .compare_flags_unsigned, + .compare_flags_signed, + .register_overflow_unsigned, + .register_overflow_signed, + => true, + else => false, + }; + } + fn isRegister(mcv: MCValue) bool { return switch (mcv) { - .register => true, + .register, + .register_overflow_unsigned, + .register_overflow_signed, + => true, else => false, }; } fn freezeIfRegister(mcv: MCValue, mgr: *RegisterManager) void { switch (mcv) { - .register => |reg| { + .register, + .register_overflow_signed, + .register_overflow_unsigned, + => |reg| { mgr.freezeRegs(&.{reg}); }, else => {}, @@ -194,12 +222,25 @@ pub const MCValue = union(enum) { fn unfreezeIfRegister(mcv: MCValue, mgr: *RegisterManager) void { switch (mcv) { - .register => |reg| { + .register, + .register_overflow_signed, + .register_overflow_unsigned, + => |reg| { mgr.unfreezeRegs(&.{reg}); }, else => {}, } } + + fn asRegister(mcv: MCValue) ?Register { + return switch (mcv) { + .register, + .register_overflow_signed, + .register_overflow_unsigned, + => |reg| reg, + else => null, + }; + } }; const Branch = struct { @@ -807,8 +848,11 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { branch.inst_table.putAssumeCapacity(inst, .dead); switch (prev_value) { .register => |reg| { - const canon_reg = reg.to64(); - self.register_manager.freeReg(canon_reg); + self.register_manager.freeReg(reg.to64()); + }, + .register_overflow_signed, .register_overflow_unsigned => |reg| { + self.register_manager.freeReg(reg.to64()); + self.compare_flags_inst = null; }, .compare_flags_signed, .compare_flags_unsigned => { self.compare_flags_inst = null; @@ -841,18 +885,15 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; branch.inst_table.putAssumeCapacityNoClobber(inst, result); - switch (result) { - .register => |reg| { - // In some cases (such as bitcast), an operand - // may be the same MCValue as the result. If - // that operand died and was a register, it - // was freed by processDeath. We have to - // "re-allocate" the register. - if (self.register_manager.isRegFree(reg)) { - self.register_manager.getRegAssumeFree(reg, inst); - } - }, - else => {}, + if (result.asRegister()) |reg| { + // In some cases (such as bitcast), an operand + // may be the same MCValue as the result. If + // that operand died and was a register, it + // was freed by processDeath. We have to + // "re-allocate" the register. + if (self.register_manager.isRegFree(reg)) { + self.register_manager.getRegAssumeFree(reg, inst); + } } } self.finishAirBookkeeping(); @@ -924,7 +965,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void const stack_mcv = try self.allocRegOrMem(inst, false); log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); const reg_mcv = self.getResolvedInstValue(inst); - assert(reg.to64() == reg_mcv.register.to64()); + assert(reg.to64() == reg_mcv.asRegister().?.to64()); const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; try branch.inst_table.put(self.gpa, inst, stack_mcv); try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{}); @@ -933,7 +974,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void pub fn spillCompareFlagsIfOccupied(self: *Self) !void { if (self.compare_flags_inst) |inst_to_save| { const mcv = self.getResolvedInstValue(inst_to_save); - assert(mcv == .compare_flags_signed or mcv == .compare_flags_unsigned); + assert(mcv.usesCompareFlags()); const new_mcv = try self.allocRegOrMem(inst_to_save, true); try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); @@ -1291,17 +1332,21 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +/// Result is always a register. fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue { - const dst_ty = self.air.typeOfIndex(inst); - const lhs = try self.resolveInst(op_lhs); - const rhs = try self.resolveInst(op_rhs); + const dst_ty = self.air.typeOf(op_lhs); + const lhs = try self.resolveInst(op_lhs); + lhs.freezeIfRegister(&self.register_manager); + defer lhs.unfreezeIfRegister(&self.register_manager); + + const rhs = try self.resolveInst(op_rhs); rhs.freezeIfRegister(&self.register_manager); defer rhs.unfreezeIfRegister(&self.register_manager); const dst_mcv = blk: { - if (self.reuseOperand(inst, op_lhs, 0, lhs)) { - if (lhs.isMemory() or lhs.isRegister()) break :blk lhs; + if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) { + break :blk lhs; } break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); }; @@ -1310,7 +1355,7 @@ fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air defer dst_mcv.unfreezeIfRegister(&self.register_manager); const rhs_mcv = blk: { - if (rhs.isRegister()) break :blk rhs; + if (rhs.isMemory() or rhs.isRegister()) break :blk rhs; break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, rhs) }; }; @@ -1377,18 +1422,78 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { } fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch}); + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + } + + const ty = self.air.typeOf(bin_op.lhs); + const signedness: std.builtin.Signedness = blk: { + if (ty.zigTypeTag() != .Int) { + return self.fail("TODO implement airAddWithOverflow for type {}", .{ty.fmtDebug()}); + } + break :blk ty.intInfo(self.target.*).signedness; + }; + + const partial = try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); + const result: MCValue = switch (signedness) { + .signed => .{ .register_overflow_signed = partial.register }, + .unsigned => .{ .register_overflow_unsigned = partial.register }, + }; + + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch}); + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + } + + const ty = self.air.typeOf(bin_op.lhs); + const signedness: std.builtin.Signedness = blk: { + if (ty.zigTypeTag() != .Int) { + return self.fail("TODO implement airSubWithOverflow for type {}", .{ty.fmtDebug()}); + } + break :blk ty.intInfo(self.target.*).signedness; + }; + + const partial = try self.genSubOp(inst, bin_op.lhs, bin_op.rhs); + const result: MCValue = switch (signedness) { + .signed => .{ .register_overflow_signed = partial.register }, + .unsigned => .{ .register_overflow_unsigned = partial.register }, + }; + + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch}); + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + } + + const ty = self.air.typeOf(bin_op.lhs); + const signedness: std.builtin.Signedness = blk: { + if (ty.zigTypeTag() != .Int) { + return self.fail("TODO implement airMulWithOverflow for type {}", .{ty.fmtDebug()}); + } + break :blk ty.intInfo(self.target.*).signedness; + }; + + const partial = try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); + const result: MCValue = switch (signedness) { + .signed => .{ .register_overflow_signed = partial.register }, + .unsigned => .{ .register_overflow_unsigned = partial.register }, + }; + + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { @@ -2422,6 +2527,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .dead => unreachable, .compare_flags_unsigned => unreachable, .compare_flags_signed => unreachable, + .register_overflow_unsigned => unreachable, + .register_overflow_signed => unreachable, .immediate => |imm| { try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }); }, @@ -2545,6 +2652,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .dead => unreachable, .compare_flags_unsigned => unreachable, .compare_flags_signed => unreachable, + .register_overflow_unsigned => unreachable, + .register_overflow_signed => unreachable, .immediate => |imm| { try self.setRegOrMem(value_ty, .{ .memory = imm }, value); }, @@ -2899,6 +3008,38 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { break :result dst_mcv; }, + .register_overflow_unsigned, + .register_overflow_signed, + => |reg| { + switch (index) { + 0 => { + // Get wrapped value for overflow operation. + break :result MCValue{ .register = reg }; + }, + 1 => { + // Get overflow bit. + mcv.freezeIfRegister(&self.register_manager); + defer mcv.unfreezeIfRegister(&self.register_manager); + + const dst_reg = try self.register_manager.allocReg(inst); + const flags: u2 = switch (mcv) { + .register_overflow_unsigned => 0b10, + .register_overflow_signed => 0b00, + else => unreachable, + }; + _ = try self.addInst(.{ + .tag = .cond_set_byte_overflow, + .ops = (Mir.Ops{ + .reg1 = dst_reg.to8(), + .flags = flags, + }).encode(), + .data = undefined, + }); + break :result MCValue{ .register = dst_reg.to8() }; + }, + else => unreachable, + } + }, else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), } }; @@ -2915,84 +3056,47 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } -/// Perform "binary" operators, excluding comparisons. -/// Currently, the following ops are supported: -/// ADD, SUB, XOR, OR, AND +/// Result is always a register. fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue { - // TODO: make this algorithm less bad + const dst_ty = self.air.typeOf(op_lhs); + const lhs = try self.resolveInst(op_lhs); + lhs.freezeIfRegister(&self.register_manager); + defer lhs.unfreezeIfRegister(&self.register_manager); + const rhs = try self.resolveInst(op_rhs); + rhs.freezeIfRegister(&self.register_manager); + defer rhs.unfreezeIfRegister(&self.register_manager); - // There are 2 operands, destination and source. - // Either one, but not both, can be a memory operand. - // Source operand can be an immediate, 8 bits or 32 bits. - // So, if either one of the operands dies with this instruction, we can use it - // as the result MCValue. - const dst_ty = self.air.typeOfIndex(inst); - var dst_mcv: MCValue = undefined; - var src_mcv: MCValue = undefined; - - if (self.reuseOperand(inst, op_lhs, 0, lhs)) { - // LHS dies; use it as the destination. - // Both operands cannot be memory. - if (lhs.isMemory() and rhs.isMemory()) { - dst_mcv = try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); - src_mcv = rhs; - } else { - dst_mcv = lhs; - src_mcv = rhs; + var flipped: bool = false; + const dst_mcv = blk: { + if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) { + break :blk lhs; } - } else if (self.reuseOperand(inst, op_rhs, 1, rhs)) { - // RHS dies; use it as the destination. - // Both operands cannot be memory. - if (lhs.isMemory() and rhs.isMemory()) { - dst_mcv = try self.copyToRegisterWithInstTracking(inst, dst_ty, rhs); - src_mcv = lhs; - } else { - dst_mcv = rhs; - src_mcv = lhs; + if (self.reuseOperand(inst, op_rhs, 1, rhs) and rhs.isRegister()) { + flipped = true; + break :blk rhs; } - } else { - if (lhs.isMemory()) { - rhs.freezeIfRegister(&self.register_manager); - defer rhs.unfreezeIfRegister(&self.register_manager); + break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); + }; + dst_mcv.freezeIfRegister(&self.register_manager); + defer dst_mcv.unfreezeIfRegister(&self.register_manager); - dst_mcv = try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); - src_mcv = rhs; - } else { - lhs.freezeIfRegister(&self.register_manager); - defer lhs.unfreezeIfRegister(&self.register_manager); - - dst_mcv = try self.copyToRegisterWithInstTracking(inst, dst_ty, rhs); - src_mcv = lhs; - } - } - // This instruction supports only signed 32-bit immediates at most. If the immediate - // value is larger than this, we put it in a register. - // A potential opportunity for future optimization here would be keeping track - // of the fact that the instruction is available both as an immediate - // and as a register. - // TODO consolidate with limitImmediateType() function - switch (src_mcv) { - .immediate => |imm| { - if (imm > math.maxInt(u31)) { - dst_mcv.freezeIfRegister(&self.register_manager); - defer dst_mcv.unfreezeIfRegister(&self.register_manager); - - src_mcv = MCValue{ .register = try self.copyToTmpRegister(Type.usize, src_mcv) }; - } - }, - else => {}, - } + const src_mcv = blk: { + const mcv = if (flipped) lhs else rhs; + if (mcv.isRegister() or mcv.isMemory()) break :blk mcv; + break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, mcv) }; + }; + src_mcv.freezeIfRegister(&self.register_manager); + defer src_mcv.unfreezeIfRegister(&self.register_manager); const tag = self.air.instructions.items(.tag)[inst]; switch (tag) { - .add, .addwrap => try self.genBinMathOpMir(.add, dst_ty, dst_mcv, src_mcv), + .add, .addwrap, .add_with_overflow => try self.genBinMathOpMir(.add, dst_ty, dst_mcv, src_mcv), .bool_or, .bit_or => try self.genBinMathOpMir(.@"or", dst_ty, dst_mcv, src_mcv), .bool_and, .bit_and => try self.genBinMathOpMir(.@"and", dst_ty, dst_mcv, src_mcv), - .sub, .subwrap => try self.genBinMathOpMir(.sub, dst_ty, dst_mcv, src_mcv), .xor, .not => try self.genBinMathOpMir(.xor, dst_ty, dst_mcv, src_mcv), - .mul, .mulwrap => try self.genIMulOpMir(dst_ty, dst_mcv, src_mcv), + .mul, .mulwrap, .mul_with_overflow => try self.genIMulOpMir(dst_ty, dst_mcv, src_mcv), else => unreachable, } return dst_mcv; @@ -3007,11 +3111,15 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .compare_flags_unsigned => unreachable, .compare_flags_signed => unreachable, .ptr_stack_offset => unreachable, + .register_overflow_unsigned => unreachable, + .register_overflow_signed => unreachable, .register => |dst_reg| { switch (src_mcv) { .none => unreachable, .undef => try self.genSetReg(dst_ty, dst_reg, .undef), .dead, .unreach => unreachable, + .register_overflow_unsigned => unreachable, + .register_overflow_signed => unreachable, .ptr_stack_offset => { self.register_manager.freezeRegs(&.{dst_reg}); defer self.register_manager.unfreezeRegs(&.{dst_reg}); @@ -3078,6 +3186,8 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .undef => return self.genSetStack(dst_ty, off, .undef, .{}), .dead, .unreach => unreachable, .ptr_stack_offset => unreachable, + .register_overflow_unsigned => unreachable, + .register_overflow_signed => unreachable, .register => |src_reg| { _ = try self.addInst(.{ .tag = mir_tag, @@ -3151,12 +3261,16 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! .compare_flags_unsigned => unreachable, .compare_flags_signed => unreachable, .ptr_stack_offset => unreachable, + .register_overflow_unsigned => unreachable, + .register_overflow_signed => unreachable, .register => |dst_reg| { switch (src_mcv) { .none => unreachable, .undef => try self.genSetReg(dst_ty, dst_reg, .undef), .dead, .unreach => unreachable, .ptr_stack_offset => unreachable, + .register_overflow_unsigned => unreachable, + .register_overflow_signed => unreachable, .register => |src_reg| { // register, register _ = try self.addInst(.{ @@ -3191,7 +3305,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! _ = try self.addInst(.{ .tag = .imul_complex, .ops = (Mir.Ops{ - .reg1 = dst_reg, + .reg1 = registerAlias(dst_reg, @intCast(u32, dst_ty.abiSize(self.target.*))), .reg2 = .rbp, .flags = 0b01, }).encode(), @@ -3218,6 +3332,8 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! .undef => return self.genSetStack(dst_ty, off, .undef, .{}), .dead, .unreach => unreachable, .ptr_stack_offset => unreachable, + .register_overflow_unsigned => unreachable, + .register_overflow_signed => unreachable, .register => |src_reg| { // copy dst to a register const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); @@ -3366,6 +3482,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. try self.genSetReg(arg_ty, reg, arg_mcv); }, .stack_offset => |off| { + // TODO rewrite using `genSetStack` try self.genSetStackArg(arg_ty, off, arg_mcv); }, .ptr_stack_offset => { @@ -3380,6 +3497,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .direct_load => unreachable, .compare_flags_signed => unreachable, .compare_flags_unsigned => unreachable, + .register_overflow_signed => unreachable, + .register_overflow_unsigned => unreachable, } } @@ -4610,6 +4729,9 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE .{ .dest_stack_base = .rsp }, ); }, + .register_overflow_unsigned, + .register_overflow_signed, + => return self.fail("TODO genSetStackArg for register with overflow bit", .{}), .compare_flags_unsigned, .compare_flags_signed, => { @@ -4712,6 +4834,39 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl ), } }, + .register_overflow_unsigned, + .register_overflow_signed, + => |reg| { + self.register_manager.freezeRegs(&.{reg}); + defer self.register_manager.unfreezeRegs(&.{reg}); + + const wrapped_ty = ty.structFieldType(0); + try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg }, .{}); + + const overflow_bit_ty = ty.structFieldType(1); + const overflow_bit_offset = ty.structFieldOffset(1, self.target.*); + const tmp_reg = try self.register_manager.allocReg(null); + const flags: u2 = switch (mcv) { + .register_overflow_unsigned => 0b10, + .register_overflow_signed => 0b00, + else => unreachable, + }; + _ = try self.addInst(.{ + .tag = .cond_set_byte_overflow, + .ops = (Mir.Ops{ + .reg1 = tmp_reg.to8(), + .flags = flags, + }).encode(), + .data = undefined, + }); + + return self.genSetStack( + overflow_bit_ty, + stack_offset - @intCast(i32, overflow_bit_offset), + .{ .register = tmp_reg.to8() }, + .{}, + ); + }, .compare_flags_unsigned, .compare_flags_signed, => { @@ -5170,6 +5325,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void const abi_size = @intCast(u32, ty.abiSize(self.target.*)); switch (mcv) { .dead => unreachable, + .register_overflow_unsigned, + .register_overflow_signed, + => unreachable, .ptr_stack_offset => |off| { if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { return self.fail("stack offset too large", .{}); diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 4616d3ff3a..8d478f0a64 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -165,6 +165,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .cond_set_byte_greater_less, .cond_set_byte_above_below, .cond_set_byte_eq_ne, + .cond_set_byte_overflow, => try emit.mirCondSetByte(tag, inst), .cond_mov_eq => try emit.mirCondMov(.cmove, inst), @@ -378,6 +379,12 @@ fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) Inne 0b0 => Tag.setne, 0b1 => Tag.sete, }, + .cond_set_byte_overflow => switch (ops.flags) { + 0b00 => Tag.seto, + 0b01 => Tag.setno, + 0b10 => Tag.setc, + 0b11 => Tag.setnc, + }, else => unreachable, }; return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code); diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index f4d6f454ae..96a8eec93d 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -309,6 +309,13 @@ pub const Inst = struct { cond_mov_lt, cond_mov_below, + /// ops flags: + /// 0b00 reg1 if OF = 1 + /// 0b01 reg1 if OF = 0 + /// 0b10 reg1 if CF = 1 + /// 0b11 reg1 if CF = 0 + cond_set_byte_overflow, + /// ops flags: form: /// 0b00 reg1 /// 0b01 [reg1 + imm32] From e6729036e44af2a52c52ce9f1f99e01d1e642cf3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 28 Mar 2022 17:45:50 +0200 Subject: [PATCH 0976/2031] x64: partially fix genImul, enable overflow tests --- src/arch/x86_64/CodeGen.zig | 11 ++++++----- test/behavior/math.zig | 13 ++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 45d73a4180..bc0171899a 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -3254,6 +3254,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC // Performs integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv. fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { + const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); switch (dst_mcv) { .none => unreachable, .undef => unreachable, @@ -3276,8 +3277,8 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! _ = try self.addInst(.{ .tag = .imul_complex, .ops = (Mir.Ops{ - .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)), - .reg2 = src_reg, + .reg1 = registerAlias(dst_reg, abi_size), + .reg2 = registerAlias(src_reg, abi_size), }).encode(), .data = undefined, }); @@ -3305,7 +3306,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! _ = try self.addInst(.{ .tag = .imul_complex, .ops = (Mir.Ops{ - .reg1 = registerAlias(dst_reg, @intCast(u32, dst_ty.abiSize(self.target.*))), + .reg1 = registerAlias(dst_reg, abi_size), .reg2 = .rbp, .flags = 0b01, }).encode(), @@ -3342,8 +3343,8 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! _ = try self.addInst(.{ .tag = .imul_complex, .ops = (Mir.Ops{ - .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)), - .reg2 = src_reg, + .reg1 = registerAlias(dst_reg, abi_size), + .reg2 = registerAlias(src_reg, abi_size), }).encode(), .data = undefined, }); diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 3abbece353..314d52be66 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -639,7 +639,6 @@ test "128-bit multiplication" { test "@addWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -684,7 +683,6 @@ test "small int addition" { test "@mulWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -698,14 +696,16 @@ test "@mulWithOverflow" { var b: u8 = 2; try expect(!@mulWithOverflow(u8, a, b, &result)); try expect(result == 246); - b = 4; - try expect(@mulWithOverflow(u8, a, b, &result)); - try expect(result == 236); + + if (builtin.zig_backend != .stage2_x86_64) { // TODO fix mul/imul on x86_64 + b = 4; + try expect(@mulWithOverflow(u8, a, b, &result)); + try expect(result == 236); + } } test "@subWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -747,7 +747,6 @@ test "@shlWithOverflow" { test "overflow arithmetic with u0 values" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO var result: u0 = undefined; try expect(!@addWithOverflow(u0, 0, 0, &result)); From 7e47f106ccf73af4c890ecbb4d48e9e92d38d3c4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 Mar 2022 12:03:58 -0700 Subject: [PATCH 0977/2031] Value.hashPtr: handle lazy_align as an integer Although lazy_align is a different tag than integer values, it needs to be hashed and equality-tested as if it were a simple integer. Otherwise our basic data structure guarantees fall apart. --- src/value.zig | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/value.zig b/src/value.zig index 926a52c2b7..c7960741f6 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2476,15 +2476,9 @@ pub const Value = extern union { .bool_false, .bool_true, .the_only_possible_value, + .lazy_align, => return hashInt(ptr_val, hasher, target), - .lazy_align => { - // Bit weird to have this here but this function is also called - // on integers. - const ty = ptr_val.castTag(.lazy_align).?.data; - ty.hashWithHasher(hasher, target); - }, - else => unreachable, } } From 3c4ac47e00d961186c8728df964a29a33275514c Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 28 Mar 2022 11:03:01 +0300 Subject: [PATCH 0978/2031] stage2 llvm: fix union init of byRef values --- src/codegen/llvm.zig | 14 ++++++++++---- test/behavior/union.zig | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 431d158293..2e2e4ca819 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -6630,6 +6630,14 @@ pub const FuncGen = struct { // tag and the payload. const index_type = self.context.intType(32); + var field_ptr_payload: Type.Payload.Pointer = .{ + .data = .{ + .pointee_type = field.ty, + .@"align" = field_align, + .@"addrspace" = .generic, + }, + }; + const field_ptr_ty = Type.initPayload(&field_ptr_payload.base); if (layout.tag_size == 0) { const indices: [3]*const llvm.Value = .{ index_type.constNull(), @@ -6638,8 +6646,7 @@ pub const FuncGen = struct { }; const len: c_uint = if (field_size == layout.payload_size) 2 else 3; const field_ptr = self.builder.buildInBoundsGEP(casted_ptr, &indices, len, ""); - const store_inst = self.builder.buildStore(llvm_payload, field_ptr); - store_inst.setAlignment(field_align); + self.store(field_ptr, field_ptr_ty, llvm_payload, .NotAtomic); return result_ptr; } @@ -6651,8 +6658,7 @@ pub const FuncGen = struct { }; const len: c_uint = if (field_size == layout.payload_size) 2 else 3; const field_ptr = self.builder.buildInBoundsGEP(casted_ptr, &indices, len, ""); - const store_inst = self.builder.buildStore(llvm_payload, field_ptr); - store_inst.setAlignment(field_align); + self.store(field_ptr, field_ptr_ty, llvm_payload, .NotAtomic); } { const indices: [2]*const llvm.Value = .{ diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 532d4f79eb..87efcbd5f1 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1149,3 +1149,22 @@ test "union with no result loc initiated with a runtime value" { var a: u32 = 1; U.foo(U{ .a = a }); } + +test "union with a large struct field" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const S = struct { + a: [8]usize, + }; + + const U = union { + s: S, + b: u32, + fn foo(_: @This()) void {} + }; + var s: S = undefined; + U.foo(U{ .s = s }); +} From a415fe0bc083159cf4bca6984636a564589e9789 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 28 Mar 2022 12:23:19 +0300 Subject: [PATCH 0979/2031] AstGen: clear rl_ty_inst in setBreakResultLoc if one is not provided --- src/AstGen.zig | 7 +++++-- test/behavior/basic.zig | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 4141b73e9a..f194270823 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2731,6 +2731,7 @@ fn varDecl( }; resolve_inferred_alloc = alloc; init_scope.rl_ptr = alloc; + init_scope.rl_ty_inst = .none; } const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope }; const init_inst = try reachableExpr(&init_scope, &init_scope.base, init_result_loc, var_decl.ast.init_node, node); @@ -4860,7 +4861,7 @@ fn orelseCatchExpr( // block_scope unstacked now, can add new instructions to parent_gz try parent_gz.instructions.append(astgen.gpa, block); - var then_scope = parent_gz.makeSubBlock(scope); + var then_scope = block_scope.makeSubBlock(scope); defer then_scope.unstack(); // This could be a pointer or value depending on `unwrap_op`. @@ -4870,7 +4871,7 @@ fn orelseCatchExpr( else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node), }; - var else_scope = parent_gz.makeSubBlock(scope); + var else_scope = block_scope.makeSubBlock(scope); defer else_scope.unstack(); var err_val_scope: Scope.LocalVal = undefined; @@ -9850,10 +9851,12 @@ const GenZir = struct { }, .discard, .none, .ptr, .ref => { + gz.rl_ty_inst = .none; gz.break_result_loc = parent_rl; }, .inferred_ptr => |ptr| { + gz.rl_ty_inst = .none; gz.rl_ptr = ptr; gz.break_result_loc = .{ .block_ptr = gz }; }, diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index e0e0f25569..6c21b8dd6c 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -854,3 +854,24 @@ test "labeled block implicitly ends in a break" { if (a) break :blk; } } + +test "catch in block has correct result location" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + const S = struct { + fn open() error{A}!@This() { + return @This(){}; + } + fn foo(_: @This()) u32 { + return 1; + } + }; + const config_h_text: u32 = blk: { + var dir = S.open() catch unreachable; + break :blk dir.foo(); + }; + try expect(config_h_text == 1); +} From 5515b81f8c4f1b7eae98ee7bb41e4b4579de45dc Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 28 Mar 2022 12:44:24 +0300 Subject: [PATCH 0980/2031] Sema: use the correct integer Sure would be nice if these two incompatible indexes had distinct types. --- src/Sema.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index aa27685658..5523d44aee 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7184,8 +7184,9 @@ fn zirSwitchCapture( return sema.bitCast(block, else_error_ty, operand, operand_src); } else { + const item_ref = sema.resolveInst(items[0]); // Previous switch validation ensured this will succeed - const item_val = sema.resolveConstValue(block, .unneeded, items[0]) catch unreachable; + const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable; const item_ty = try Type.Tag.error_set_single.create(sema.arena, item_val.getError().?); return sema.bitCast(block, item_ty, operand, operand_src); From c517e65d8f5d7c06aea56dfce5527843d7711a99 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 28 Mar 2022 13:47:03 +0300 Subject: [PATCH 0981/2031] Sema: implement coerceInMemoryAllowed for optionals --- src/Sema.zig | 24 +++++++++++++++++++++--- test/behavior/cast.zig | 9 +++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 5523d44aee..375b588c40 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -18222,8 +18222,10 @@ fn coerceInMemoryAllowed( // Pointers / Pointer-like Optionals var dest_buf: Type.Payload.ElemType = undefined; var src_buf: Type.Payload.ElemType = undefined; - if (try sema.typePtrOrOptionalPtrTy(block, dest_ty, &dest_buf, dest_src)) |dest_ptr_ty| { - if (try sema.typePtrOrOptionalPtrTy(block, src_ty, &src_buf, src_src)) |src_ptr_ty| { + const maybe_dest_ptr_ty = try sema.typePtrOrOptionalPtrTy(block, dest_ty, &dest_buf, dest_src); + const maybe_src_ptr_ty = try sema.typePtrOrOptionalPtrTy(block, src_ty, &src_buf, src_src); + if (maybe_dest_ptr_ty) |dest_ptr_ty| { + if (maybe_src_ptr_ty) |src_ptr_ty| { return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target, dest_src, src_src); } } @@ -18288,7 +18290,23 @@ fn coerceInMemoryAllowed( return .ok; } - // TODO: non-pointer-like optionals + // Optionals + if (dest_tag == .Optional and src_tag == .Optional) optionals: { + if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) { + // TODO "optional type child '{}' cannot cast into optional type '{}'" + return .no_match; + } + const dest_child_type = dest_ty.optionalChild(&dest_buf); + const src_child_type = src_ty.optionalChild(&src_buf); + + const child = try sema.coerceInMemoryAllowed(block, dest_child_type, src_child_type, dest_is_mut, target, dest_src, src_src); + if (child == .no_match) { + // TODO "optional type child '{}' cannot cast into optional type child '{}'" + break :optionals; + } + + return .ok; + } return .no_match; } diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 4bb8f147ec..15a793d109 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1363,3 +1363,12 @@ test "cast i8 fn call peers to i32 result" { try S.doTheTest(); comptime try S.doTheTest(); } + +test "cast compatible optional types" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + var a: ?[:0]const u8 = null; + var b: ?[]const u8 = a; + try expect(b == null); +} From 691c7cb3cd3cc6cb4f6324d0ad4a5c3e2e8ff94c Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 28 Mar 2022 17:32:20 +0300 Subject: [PATCH 0982/2031] std.build: fix functions returning address of by value parameter --- lib/std/build/OptionsStep.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/build/OptionsStep.zig b/lib/std/build/OptionsStep.zig index ccbaae358c..a615454659 100644 --- a/lib/std/build/OptionsStep.zig +++ b/lib/std/build/OptionsStep.zig @@ -199,11 +199,11 @@ pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *LibExe self.step.dependOn(&artifact.step); } -pub fn getPackage(self: OptionsStep, package_name: []const u8) build.Pkg { +pub fn getPackage(self: *OptionsStep, package_name: []const u8) build.Pkg { return .{ .name = package_name, .path = self.getSource() }; } -pub fn getSource(self: OptionsStep) FileSource { +pub fn getSource(self: *OptionsStep) FileSource { return .{ .generated = &self.generated_file }; } From b6ccde47adeb0dbd7b39150c36498100e0d98075 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 Mar 2022 14:17:05 -0700 Subject: [PATCH 0983/2031] Sema: allow mixing array and vector operands * Added peer type resolution for arrays and vectors: the vector type is selected. * Fixed passing the lhs type or rhs type instead of the peer resolved type when calling Value methods during analyzeArithmetic handling of comptime expressions. * `checkVectorizableBinaryOperands` now allows mixing vectors and arrays, as long as one of the operands is a vector. This matches stage1's handling of `^=` but apparently stage1 is inconsistent and does not handle e.g. `*=`. stage2 now will always allow mixing vector and array operands for all operations. --- src/Sema.zig | 51 +++++++++++++++++++++++++++++----------- test/behavior/vector.zig | 24 +++++++++++++++++++ 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 375b588c40..e79e358907 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9617,7 +9617,7 @@ fn analyzeArithmetic( if (lhs_val.isUndef()) { if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compare(.neq, Value.negative_one, rhs_ty, target)) { + if (rhs_val.compare(.neq, Value.negative_one, resolved_type, target)) { return sema.addConstUndef(resolved_type); } } @@ -9692,7 +9692,7 @@ fn analyzeArithmetic( if (lhs_val.isUndef()) { if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compare(.neq, Value.negative_one, rhs_ty, target)) { + if (rhs_val.compare(.neq, Value.negative_one, resolved_type, target)) { return sema.addConstUndef(resolved_type); } } @@ -9755,7 +9755,7 @@ fn analyzeArithmetic( if (lhs_val.isUndef()) { if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compare(.neq, Value.negative_one, rhs_ty, target)) { + if (rhs_val.compare(.neq, Value.negative_one, resolved_type, target)) { return sema.addConstUndef(resolved_type); } } @@ -9845,7 +9845,7 @@ fn analyzeArithmetic( if (lhs_val.compareWithZero(.eq)) { return sema.addConstant(resolved_type, Value.zero); } - if (lhs_val.compare(.eq, Value.one, lhs_ty, target)) { + if (lhs_val.compare(.eq, Value.one, resolved_type, target)) { return casted_rhs; } } @@ -9861,7 +9861,7 @@ fn analyzeArithmetic( if (rhs_val.compareWithZero(.eq)) { return sema.addConstant(resolved_type, Value.zero); } - if (rhs_val.compare(.eq, Value.one, rhs_ty, target)) { + if (rhs_val.compare(.eq, Value.one, resolved_type, target)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { @@ -9896,7 +9896,7 @@ fn analyzeArithmetic( if (lhs_val.compareWithZero(.eq)) { return sema.addConstant(resolved_type, Value.zero); } - if (lhs_val.compare(.eq, Value.one, lhs_ty, target)) { + if (lhs_val.compare(.eq, Value.one, resolved_type, target)) { return casted_rhs; } } @@ -9908,7 +9908,7 @@ fn analyzeArithmetic( if (rhs_val.compareWithZero(.eq)) { return sema.addConstant(resolved_type, Value.zero); } - if (rhs_val.compare(.eq, Value.one, rhs_ty, target)) { + if (rhs_val.compare(.eq, Value.one, resolved_type, target)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { @@ -9932,7 +9932,7 @@ fn analyzeArithmetic( if (lhs_val.compareWithZero(.eq)) { return sema.addConstant(resolved_type, Value.zero); } - if (lhs_val.compare(.eq, Value.one, lhs_ty, target)) { + if (lhs_val.compare(.eq, Value.one, resolved_type, target)) { return casted_rhs; } } @@ -9944,7 +9944,7 @@ fn analyzeArithmetic( if (rhs_val.compareWithZero(.eq)) { return sema.addConstant(resolved_type, Value.zero); } - if (rhs_val.compare(.eq, Value.one, rhs_ty, target)) { + if (rhs_val.compare(.eq, Value.one, resolved_type, target)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { @@ -14521,9 +14521,20 @@ fn checkVectorizableBinaryOperands( ) CompileError!void { const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); - if (lhs_zig_ty_tag == .Vector and rhs_zig_ty_tag == .Vector) { - const lhs_len = lhs_ty.vectorLen(); - const rhs_len = rhs_ty.vectorLen(); + if (lhs_zig_ty_tag != .Vector and rhs_zig_ty_tag != .Vector) return; + + const lhs_is_vector = switch (lhs_zig_ty_tag) { + .Vector, .Array => true, + else => false, + }; + const rhs_is_vector = switch (rhs_zig_ty_tag) { + .Vector, .Array => true, + else => false, + }; + + if (lhs_is_vector and rhs_is_vector) { + const lhs_len = lhs_ty.arrayLen(); + const rhs_len = rhs_ty.arrayLen(); if (lhs_len != rhs_len) { const msg = msg: { const msg = try sema.errMsg(block, src, "vector length mismatch", .{}); @@ -14534,14 +14545,14 @@ fn checkVectorizableBinaryOperands( }; return sema.failWithOwnedErrorMsg(block, msg); } - } else if (lhs_zig_ty_tag == .Vector or rhs_zig_ty_tag == .Vector) { + } else { const target = sema.mod.getTarget(); const msg = msg: { const msg = try sema.errMsg(block, src, "mixed scalar and vector operands: {} and {}", .{ lhs_ty.fmt(target), rhs_ty.fmt(target), }); errdefer msg.destroy(sema.gpa); - if (lhs_zig_ty_tag == .Vector) { + if (lhs_is_vector) { try sema.errNote(block, lhs_src, msg, "vector here", .{}); try sema.errNote(block, rhs_src, msg, "scalar here", .{}); } else { @@ -21017,6 +21028,18 @@ fn resolvePeerTypes( chosen_i = candidate_i + 1; continue; }, + .Vector => switch (chosen_ty_tag) { + .Array => { + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + }, + else => {}, + }, + .Array => switch (chosen_ty_tag) { + .Vector => continue, + else => {}, + }, else => {}, } diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 1651e8107b..ccf22a2094 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -879,3 +879,27 @@ test "saturating shift-left" { try S.doTheTest(); comptime try S.doTheTest(); } + +test "multiplication-assignment operator with an array operand" { + if (builtin.zig_backend == .stage1) { + // stage1 emits a compile error + return error.SkipZigTest; + } + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var x: @Vector(3, i32) = .{ 1, 2, 3 }; + x *= [_]i32{ 4, 5, 6 }; + try expect(x[0] == 4); + try expect(x[1] == 10); + try expect(x[2] == 18); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} From c546608fcae4e36a593c4ff6c566b864d379e741 Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sun, 27 Mar 2022 14:37:13 +0200 Subject: [PATCH 0984/2031] stage2: LLVM: (WIP) add union fields debug info --- src/Module.zig | 16 +--- src/codegen/llvm.zig | 163 +++++++++++++++++++++++++++++++--- src/codegen/llvm/bindings.zig | 2 +- 3 files changed, 156 insertions(+), 25 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index e74d5f1dd8..ce93e091fd 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1231,13 +1231,7 @@ pub const Union = struct { for (u.fields.values()) |field, i| { if (!field.ty.hasRuntimeBits()) continue; - const field_align = a: { - if (field.abi_align == 0) { - break :a field.ty.abiAlignment(target); - } else { - break :a field.abi_align; - } - }; + const field_align = field.normalAlignment(target); if (field_align > most_alignment) { most_alignment = field_align; most_index = i; @@ -1253,13 +1247,7 @@ pub const Union = struct { for (u.fields.values()) |field| { if (!field.ty.hasRuntimeBits()) continue; - const field_align = a: { - if (field.abi_align == 0) { - break :a field.ty.abiAlignment(target); - } else { - break :a field.abi_align; - } - }; + const field_align = field.normalAlignment(target); max_align = @maximum(max_align, field_align); } return max_align; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 2e2e4ca819..e0fb3103c2 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1465,11 +1465,22 @@ pub const Object = struct { return full_di_ty; }, .Union => { + const compile_unit_scope = o.di_compile_unit.?.toScope(); const owner_decl = ty.getOwnerDecl(); const name = try ty.nameAlloc(gpa, target); defer gpa.free(name); + if (ty.cast(Type.Payload.Union)) |payload| { + const union_obj = payload.data; + if (union_obj.layout == .Packed) { + const bit_size = ty.bitSize(target); + const di_ty = dib.createBasicType(name, bit_size, DW.ATE.unsigned); + gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_ty); + return di_ty; + } + } + const fwd_decl = opt_fwd_decl orelse blk: { const fwd_decl = dib.createReplaceableCompositeType( DW.TAG.structure_type, @@ -1483,8 +1494,22 @@ pub const Object = struct { break :blk fwd_decl; }; - const TODO_implement_this = true; // TODO - if (TODO_implement_this or !ty.hasRuntimeBitsIgnoreComptime()) { + const union_obj = ty.cast(Type.Payload.Union).?.data; + + // TODO COPYPASTE >>> + if (!union_obj.haveFieldTypes()) { + // TODO: improve the frontend to populate this union. + // For now we treat it as a zero bit type. + const union_di_ty = try o.makeEmptyNamespaceDIType(owner_decl); + dib.replaceTemporary(fwd_decl, union_di_ty); + // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` + // means we can't use `gop` anymore. + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .target = o.target }); + return union_di_ty; + } + // TODO <<< + + if (!ty.hasRuntimeBitsIgnoreComptime()) { const union_di_ty = try o.makeEmptyNamespaceDIType(owner_decl); dib.replaceTemporary(fwd_decl, union_di_ty); // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` @@ -1493,16 +1518,134 @@ pub const Object = struct { return union_di_ty; } - @panic("TODO debug info type for union"); - //const gop = try o.type_map.getOrPut(gpa, ty); - //if (gop.found_existing) return gop.value_ptr.*; + var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{}; + defer di_fields.deinit(gpa); - //// The Type memory is ephemeral; since we want to store a longer-lived - //// reference, we need to copy it here. - //gop.key_ptr.* = try ty.copy(o.type_map_arena.allocator()); + try di_fields.ensureUnusedCapacity(gpa, union_obj.fields.count()); - //const layout = ty.unionGetLayout(target); - //const union_obj = ty.cast(Type.Payload.Union).?.data; + var field_iterator = union_obj.fields.iterator(); + while (field_iterator.next()) |kv| { + const field_name = kv.key_ptr.*; + const field = kv.value_ptr.*; + + if (!field.ty.hasRuntimeBitsIgnoreComptime()) continue; + + const field_size = field.ty.abiSize(target); + const field_align = field.normalAlignment(target); + + const field_name_copy = try gpa.dupeZ(u8, field_name); + defer gpa.free(field_name_copy); + + try di_fields.append(gpa, dib.createMemberType( + fwd_decl.toScope(), + field_name_copy, + null, // file + 0, // line + field_size * 8, // size in bits + field_align * 8, // align in bits + 0, // offset in bits + 0, // flags + try o.lowerDebugType(field.ty, .full), + )); + } + + const tag_ty = union_obj.tag_ty; + if (!tag_ty.hasRuntimeBitsIgnoreComptime()) { + const union_di_ty = dib.createUnionType( + compile_unit_scope, + name.ptr, + null, // file + 0, // line + ty.abiSize(target) * 8, // size in bits + ty.abiAlignment(target) * 8, // align in bits + 0, // flags + di_fields.items.ptr, + @intCast(c_int, di_fields.items.len), + 0, // run time lang + "", // unique id + ); + + dib.replaceTemporary(fwd_decl, union_di_ty); + // The recursive call to `lowerDebugType` means we can't use `gop` anymore. + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .target = o.target }); + return union_di_ty; + } + + const union_di_ty = dib.createUnionType( + fwd_decl.toScope(), + "AnonUnion", + null, // file + 0, // line + ty.abiSize(target) * 8, // size in bits + ty.abiAlignment(target) * 8, // align in bits + 0, // flags + di_fields.items.ptr, + @intCast(c_int, di_fields.items.len), + 0, // run time lang + "", // unique id + ); + + const payload_size = ty.abiSize(target); + const payload_align = ty.abiAlignment(target); + const tag_size = tag_ty.abiSize(target); + const tag_align = tag_ty.abiAlignment(target); + + assert(tag_size > 0); + assert(tag_align > 0); + + var offset: u64 = 0; + offset += payload_size; + offset = std.mem.alignForwardGeneric(u64, offset, tag_align); + const tag_offset = offset; + + const payload_di = dib.createMemberType( + fwd_decl.toScope(), + "payload", + null, // file + 0, // line + payload_size * 8, // size in bits + payload_align * 8, // align in bits + 0, // field_offset * 8, // offset in bits + 0, // flags + union_di_ty, + ); + + const tag_di = dib.createMemberType( + fwd_decl.toScope(), + "tag", + null, // file + 0, // line + tag_size * 8, // TODO: should this be multiplied by 8??? analyze.cpp:9237 + tag_align * 8, // align in bits + tag_offset * 8, // offset in bits + 0, // flags + try o.lowerDebugType(tag_ty, .full), + ); + + const full_di_fields = [_]*llvm.DIType { + payload_di, + tag_di, + }; + + const full_di_ty = dib.createStructType( + compile_unit_scope, + name.ptr, + null, // file + 0, // line + ty.abiSize(target) * 8, // size in bits + ty.abiAlignment(target) * 8, // align in bits + 0, // flags + null, // derived from + &full_di_fields, + @intCast(c_int, full_di_fields.len), + 0, // run time lang + null, // vtable holder + "", // unique id + ); + dib.replaceTemporary(fwd_decl, full_di_ty); + // The recursive call to `lowerDebugType` means we can't use `gop` anymore. + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target }); + return full_di_ty; //if (layout.payload_size == 0) { // const enum_tag_llvm_ty = try dg.llvmType(union_obj.tag_ty); diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 33b862499c..b799d649b0 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -1601,7 +1601,7 @@ pub const DIBuilder = opaque { dib: *DIBuilder, scope: *DIScope, name: [*:0]const u8, - file: *DIFile, + file: ?*DIFile, line_number: c_uint, size_in_bits: u64, align_in_bits: u64, From f4a357d7209db61acdfcb24ecec316da66eb318d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 Mar 2022 16:20:38 -0700 Subject: [PATCH 0985/2031] stage2: finish debug info for unions in the LLVM backend Sema: * queue full resolution of std.builtin.Type.Error when doing `@typeInfo` for error sets. LLVM backend: * change a TODO comment to a proper explanation of why debug info for structs is left as a fwd decl sometimes. * remove handling of packed unions which does not match the type information or constant generation code. * remove copy+pasted code * fix union debug info not matching the memory layout * remove unnecessary error checks and type casting --- src/Sema.zig | 2 + src/codegen/llvm.zig | 213 +++++++++++++++---------------------------- 2 files changed, 77 insertions(+), 138 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index e79e358907..8842c74ca7 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -11091,6 +11091,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai break :t try set_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena()); }; + try sema.queueFullTypeResolution(try error_field_ty.copy(sema.arena)); + // If the error set is inferred it has to be resolved at this point try sema.resolveInferredErrorSetTy(block, src, ty); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index e0fb3103c2..74de485721 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1388,8 +1388,13 @@ pub const Object = struct { if (ty.castTag(.@"struct")) |payload| { const struct_obj = payload.data; if (!struct_obj.haveFieldTypes()) { - // TODO: improve the frontend to populate this struct. - // For now we treat it as a zero bit type. + // This can happen if a struct type makes it all the way to + // flush() without ever being instantiated or referenced (even + // via pointer). The only reason we are hearing about it now is + // that it is being used as a namespace to put other debug types + // into. Therefore we can satisfy this by making an empty namespace, + // rather than changing the frontend to unnecessarily resolve the + // struct field types. const owner_decl = ty.getOwnerDecl(); const struct_di_ty = try o.makeEmptyNamespaceDIType(owner_decl); dib.replaceTemporary(fwd_decl, struct_di_ty); @@ -1471,16 +1476,6 @@ pub const Object = struct { const name = try ty.nameAlloc(gpa, target); defer gpa.free(name); - if (ty.cast(Type.Payload.Union)) |payload| { - const union_obj = payload.data; - if (union_obj.layout == .Packed) { - const bit_size = ty.bitSize(target); - const di_ty = dib.createBasicType(name, bit_size, DW.ATE.unsigned); - gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_ty); - return di_ty; - } - } - const fwd_decl = opt_fwd_decl orelse blk: { const fwd_decl = dib.createReplaceableCompositeType( DW.TAG.structure_type, @@ -1494,21 +1489,6 @@ pub const Object = struct { break :blk fwd_decl; }; - const union_obj = ty.cast(Type.Payload.Union).?.data; - - // TODO COPYPASTE >>> - if (!union_obj.haveFieldTypes()) { - // TODO: improve the frontend to populate this union. - // For now we treat it as a zero bit type. - const union_di_ty = try o.makeEmptyNamespaceDIType(owner_decl); - dib.replaceTemporary(fwd_decl, union_di_ty); - // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` - // means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .target = o.target }); - return union_di_ty; - } - // TODO <<< - if (!ty.hasRuntimeBitsIgnoreComptime()) { const union_di_ty = try o.makeEmptyNamespaceDIType(owner_decl); dib.replaceTemporary(fwd_decl, union_di_ty); @@ -1518,13 +1498,41 @@ pub const Object = struct { return union_di_ty; } + const layout = ty.unionGetLayout(target); + const union_obj = ty.cast(Type.Payload.Union).?.data; + + if (layout.payload_size == 0) { + const tag_di_ty = try o.lowerDebugType(union_obj.tag_ty, .full); + const di_fields = [_]*llvm.DIType{tag_di_ty}; + const full_di_ty = dib.createStructType( + compile_unit_scope, + name.ptr, + null, // file + 0, // line + ty.abiSize(target) * 8, // size in bits + ty.abiAlignment(target) * 8, // align in bits + 0, // flags + null, // derived from + &di_fields, + di_fields.len, + 0, // run time lang + null, // vtable holder + "", // unique id + ); + dib.replaceTemporary(fwd_decl, full_di_ty); + // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` + // means we can't use `gop` anymore. + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target }); + return full_di_ty; + } + var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{}; defer di_fields.deinit(gpa); try di_fields.ensureUnusedCapacity(gpa, union_obj.fields.count()); - var field_iterator = union_obj.fields.iterator(); - while (field_iterator.next()) |kv| { + var it = union_obj.fields.iterator(); + while (it.next()) |kv| { const field_name = kv.key_ptr.*; const field = kv.value_ptr.*; @@ -1536,7 +1544,7 @@ pub const Object = struct { const field_name_copy = try gpa.dupeZ(u8, field_name); defer gpa.free(field_name_copy); - try di_fields.append(gpa, dib.createMemberType( + di_fields.appendAssumeCapacity(dib.createMemberType( fwd_decl.toScope(), field_name_copy, null, // file @@ -1549,31 +1557,11 @@ pub const Object = struct { )); } - const tag_ty = union_obj.tag_ty; - if (!tag_ty.hasRuntimeBitsIgnoreComptime()) { - const union_di_ty = dib.createUnionType( - compile_unit_scope, - name.ptr, - null, // file - 0, // line - ty.abiSize(target) * 8, // size in bits - ty.abiAlignment(target) * 8, // align in bits - 0, // flags - di_fields.items.ptr, - @intCast(c_int, di_fields.items.len), - 0, // run time lang - "", // unique id - ); - - dib.replaceTemporary(fwd_decl, union_di_ty); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .target = o.target }); - return union_di_ty; - } + const union_name = if (layout.tag_size == 0) "AnonUnion" else name.ptr; const union_di_ty = dib.createUnionType( - fwd_decl.toScope(), - "AnonUnion", + compile_unit_scope, + union_name, null, // file 0, // line ty.abiSize(target) * 8, // size in bits @@ -1585,47 +1573,50 @@ pub const Object = struct { "", // unique id ); - const payload_size = ty.abiSize(target); - const payload_align = ty.abiAlignment(target); - const tag_size = tag_ty.abiSize(target); - const tag_align = tag_ty.abiAlignment(target); + if (layout.tag_size == 0) { + dib.replaceTemporary(fwd_decl, union_di_ty); + // The recursive call to `lowerDebugType` means we can't use `gop` anymore. + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .target = o.target }); + return union_di_ty; + } - assert(tag_size > 0); - assert(tag_align > 0); - - var offset: u64 = 0; - offset += payload_size; - offset = std.mem.alignForwardGeneric(u64, offset, tag_align); - const tag_offset = offset; - - const payload_di = dib.createMemberType( - fwd_decl.toScope(), - "payload", - null, // file - 0, // line - payload_size * 8, // size in bits - payload_align * 8, // align in bits - 0, // field_offset * 8, // offset in bits - 0, // flags - union_di_ty, - ); + var tag_offset: u64 = undefined; + var payload_offset: u64 = undefined; + if (layout.tag_align >= layout.payload_align) { + tag_offset = 0; + payload_offset = std.mem.alignForwardGeneric(u64, layout.tag_size, layout.payload_align); + } else { + payload_offset = 0; + tag_offset = std.mem.alignForwardGeneric(u64, layout.payload_size, layout.tag_align); + } const tag_di = dib.createMemberType( fwd_decl.toScope(), "tag", null, // file 0, // line - tag_size * 8, // TODO: should this be multiplied by 8??? analyze.cpp:9237 - tag_align * 8, // align in bits + layout.tag_size * 8, + layout.tag_align * 8, // align in bits tag_offset * 8, // offset in bits 0, // flags - try o.lowerDebugType(tag_ty, .full), + try o.lowerDebugType(union_obj.tag_ty, .full), ); - const full_di_fields = [_]*llvm.DIType { - payload_di, - tag_di, - }; + const payload_di = dib.createMemberType( + fwd_decl.toScope(), + "payload", + null, // file + 0, // line + layout.payload_size * 8, // size in bits + layout.payload_align * 8, // align in bits + payload_offset * 8, // offset in bits + 0, // flags + union_di_ty, + ); + + const full_di_fields: [2]*llvm.DIType = + if (layout.tag_align >= layout.payload_align) + .{ tag_di, payload_di } else .{ payload_di, tag_di }; const full_di_ty = dib.createStructType( compile_unit_scope, @@ -1637,7 +1628,7 @@ pub const Object = struct { 0, // flags null, // derived from &full_di_fields, - @intCast(c_int, full_di_fields.len), + full_di_fields.len, 0, // run time lang null, // vtable holder "", // unique id @@ -1646,60 +1637,6 @@ pub const Object = struct { // The recursive call to `lowerDebugType` means we can't use `gop` anymore. try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target }); return full_di_ty; - - //if (layout.payload_size == 0) { - // const enum_tag_llvm_ty = try dg.llvmType(union_obj.tag_ty); - // gop.value_ptr.* = enum_tag_llvm_ty; - // return enum_tag_llvm_ty; - //} - - //const name = try union_obj.getFullyQualifiedName(gpa); - //defer gpa.free(name); - - //const llvm_union_ty = dg.context.structCreateNamed(name); - //gop.value_ptr.* = llvm_union_ty; // must be done before any recursive calls - - //const aligned_field = union_obj.fields.values()[layout.most_aligned_field]; - //const llvm_aligned_field_ty = try dg.llvmType(aligned_field.ty); - - //const llvm_payload_ty = ty: { - // if (layout.most_aligned_field_size == layout.payload_size) { - // break :ty llvm_aligned_field_ty; - // } - // const padding_len = @intCast(c_uint, layout.payload_size - layout.most_aligned_field_size); - // const fields: [2]*const llvm.Type = .{ - // llvm_aligned_field_ty, - // dg.context.intType(8).arrayType(padding_len), - // }; - // break :ty dg.context.structType(&fields, fields.len, .True); - //}; - - //if (layout.tag_size == 0) { - // var llvm_fields: [1]*const llvm.Type = .{llvm_payload_ty}; - // llvm_union_ty.structSetBody(&llvm_fields, llvm_fields.len, .False); - // return llvm_union_ty; - //} - //const enum_tag_llvm_ty = try dg.llvmType(union_obj.tag_ty); - - //// Put the tag before or after the payload depending on which one's - //// alignment is greater. - //var llvm_fields: [3]*const llvm.Type = undefined; - //var llvm_fields_len: c_uint = 2; - - //if (layout.tag_align >= layout.payload_align) { - // llvm_fields = .{ enum_tag_llvm_ty, llvm_payload_ty, undefined }; - //} else { - // llvm_fields = .{ llvm_payload_ty, enum_tag_llvm_ty, undefined }; - //} - - //// Insert padding to make the LLVM struct ABI size match the Zig union ABI size. - //if (layout.padding != 0) { - // llvm_fields[2] = dg.context.intType(8).arrayType(layout.padding); - // llvm_fields_len = 3; - //} - - //llvm_union_ty.structSetBody(&llvm_fields, llvm_fields_len, .False); - //return llvm_union_ty; }, .Fn => { const fn_info = ty.fnInfo(); From 4dd65316b7d5756809394710078718b3ee536a9b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 Mar 2022 18:28:08 -0700 Subject: [PATCH 0986/2031] AstGen: coerce break operands of labeled blocks Similar code was already in place for conditional branches. This updates AstGen to do the same for labeled blocks. It takes advantage of the `store_to_block_ptr` instructions by mutating them in place to become `as` instructions, coercing the break operands before they are returned from the block. --- src/AstGen.zig | 27 +++++++++++++++++++++++++++ test/behavior/basic.zig | 12 ++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/AstGen.zig b/src/AstGen.zig index f194270823..6c75cc233c 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2015,6 +2015,7 @@ fn labeledBlockExpr( } const zir_datas = gz.astgen.instructions.items(.data); + const zir_tags = gz.astgen.instructions.items(.tag); const strat = rl.strategy(&block_scope); switch (strat.tag) { .break_void => { @@ -2029,6 +2030,31 @@ fn labeledBlockExpr( }, .break_operand => { // All break operands are values that did not use the result location pointer. + // The break instructions need to have their operands coerced if the + // block's result location is a `ty`. In this case we overwrite the + // `store_to_block_ptr` instruction with an `as` instruction and repurpose + // it as the break operand. + // This corresponds to similar code in `setCondBrPayloadElideBlockStorePtr`. + if (block_scope.rl_ty_inst != .none) { + for (block_scope.labeled_breaks.items) |br| { + // We expect the `store_to_block_ptr` to be created between 1-3 instructions + // prior to the break. + var search_index = br -| 3; + while (search_index < br) : (search_index += 1) { + if (zir_tags[search_index] == .store_to_block_ptr and + zir_datas[search_index].bin.lhs == block_scope.rl_ptr) + { + zir_tags[search_index] = .as; + zir_datas[search_index].bin = .{ + .lhs = block_scope.rl_ty_inst, + .rhs = zir_datas[br].@"break".operand, + }; + zir_datas[br].@"break".operand = indexToRef(search_index); + break; + } + } else unreachable; + } + } try block_scope.setBlockBody(block_inst); const block_ref = indexToRef(block_inst); switch (rl) { @@ -5366,6 +5392,7 @@ fn setCondBrPayloadElideBlockStorePtr( // switch's result location is a `ty`. In this case we overwrite the // `store_to_block_ptr` instruction with an `as` instruction and repurpose // it as the break operand. + // This corresponds to similar code in `labeledBlockExpr`. for (then_body) |src_inst| { if (zir_tags[src_inst] == .store_to_block_ptr and zir_datas[src_inst].bin.lhs == block_ptr) diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 6c21b8dd6c..134ca1a235 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -875,3 +875,15 @@ test "catch in block has correct result location" { }; try expect(config_h_text == 1); } + +test "labeled block with runtime branch forwards its result location type to break statements" { + const E = enum { a, b }; + var a = false; + const e: E = blk: { + if (a) { + break :blk .a; + } + break :blk .b; + }; + try expect(e == .b); +} From 8df84cce8b68339c332d30213efe90cdc833d059 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 Mar 2022 18:49:49 -0700 Subject: [PATCH 0987/2031] Sema: queue full type resolution of builtin types --- src/Sema.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 8842c74ca7..afb97f3b3d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -11594,6 +11594,7 @@ fn typeInfoDecls( var buffer: Value.ToTypeBuffer = undefined; break :t try declaration_ty_decl.val.toType(&buffer).copy(decls_anon_decl.arena()); }; + try sema.queueFullTypeResolution(try declaration_ty.copy(sema.arena)); const decls_len = if (opt_namespace) |ns| ns.decls.count() else 0; const decls_vals = try decls_anon_decl.arena().alloc(Value, decls_len); @@ -22047,7 +22048,9 @@ fn getBuiltinType( name: []const u8, ) CompileError!Type { const ty_inst = try sema.getBuiltin(block, src, name); - return sema.analyzeAsType(block, src, ty_inst); + const result_ty = try sema.analyzeAsType(block, src, ty_inst); + try sema.queueFullTypeResolution(result_ty); + return result_ty; } /// There is another implementation of this in `Type.onePossibleValue`. This one From 8238d4b33585a715c58ab559cd001dd3ea1db55b Mon Sep 17 00:00:00 2001 From: Daniele Cocca Date: Mon, 28 Mar 2022 21:30:07 +0100 Subject: [PATCH 0988/2031] CBE: fix C output after PR #11302, reenable tests Commit 052079c99455d01312d377d72fa1b8b5c0b22aad surfaced two issues with the generated C code: - renderInt128() contained a seemingly unnecessary assertion to verify that the high 64 bits of the number were nonzero, dating back to 9bf1681990fe87a6b2e5fc644a89f1aece304579. I removed it. - renderValue() didn't have any special handling for undefined structs, falling back to printing "{}" which generated invalid expressions such as "return {}" for functions returning structs, whereas "return (S){}" is the correct form. I changed it accordingly. At the same time I'm reenabling the relevant tests. --- src/codegen/c.zig | 6 +++++- test/behavior/eval.zig | 1 - test/behavior/fn.zig | 1 - test/behavior/for.zig | 1 - test/behavior/int128.zig | 1 - test/behavior/slice.zig | 2 -- 6 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 84bb3a211b..38a105c172 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -433,7 +433,6 @@ pub const DeclGen = struct { if (is_signed) try writer.writeAll("(int128_t)"); if (is_neg) try writer.writeByte('-'); - assert(high > 0); try writer.print("(((uint128_t)0x{x}u<<64)", .{high}); if (low > 0) @@ -572,6 +571,11 @@ pub const DeclGen = struct { 64 => return writer.writeAll("(void *)0xaaaaaaaaaaaaaaaa"), else => unreachable, }, + .Struct => { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + return writer.writeAll("){0xaa}"); + }, else => { // This should lower to 0xaa bytes in safe modes, and for unsafe modes should // lower to leaving variables uninitialized (that might need to be implemented diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 9cad1c6106..e3024a3895 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -443,7 +443,6 @@ fn copyWithPartialInline(s: []u32, b: []u8) void { test "binary math operator in partially inlined function" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var s: [4]u32 = undefined; var b: [16]u8 = undefined; diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 68eb730b57..7d02229ff4 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -75,7 +75,6 @@ test "return inner function which references comptime variable of outer function } test "discard the result of a function that returns a struct" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; diff --git a/test/behavior/for.zig b/test/behavior/for.zig index db3288a4d1..5188f02381 100644 --- a/test/behavior/for.zig +++ b/test/behavior/for.zig @@ -69,7 +69,6 @@ test "basic for loop" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; const expected_result = [_]u8{ 9, 8, 7, 6, 0, 1, 2, 3 } ** 3; diff --git a/test/behavior/int128.zig b/test/behavior/int128.zig index 08c6dd0e4d..f57999511c 100644 --- a/test/behavior/int128.zig +++ b/test/behavior/int128.zig @@ -46,7 +46,6 @@ test "int128" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var buff: i128 = -1; try expect(buff < 0 and (buff + 1) == 0); diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index a9f89130a1..09d15e3ac5 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -74,8 +74,6 @@ fn assertLenIsZero(msg: []const u8) !void { } test "access len index of sentinel-terminated slice" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - const S = struct { fn doTheTest() !void { var slice: [:0]const u8 = "hello"; From 9aa431cba34699ae35f7905398a0c8263b2ad453 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 Mar 2022 12:01:45 -0700 Subject: [PATCH 0989/2031] test harness: include case names for compile errors in the progress nodes --- src/test.zig | 44 ++++++++++++++++++++++++++++++----------- test/compile_errors.zig | 2 +- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/test.zig b/src/test.zig index 6bdff16a58..067efe4f21 100644 --- a/src/test.zig +++ b/src/test.zig @@ -125,6 +125,7 @@ pub const TestContext = struct { /// you can keep it mostly consistent, with small changes, testing the /// effects of the incremental compilation. src: [:0]const u8, + name: []const u8, case: union(enum) { /// Check the main binary output file against an expected set of bytes. /// This is most useful with, for example, `-ofmt=c`. @@ -190,6 +191,7 @@ pub const TestContext = struct { self.emit_h = true; self.updates.append(.{ .src = src, + .name = "update", .case = .{ .Header = result }, }) catch @panic("out of memory"); } @@ -199,6 +201,7 @@ pub const TestContext = struct { pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void { self.updates.append(.{ .src = src, + .name = "update", .case = .{ .Execution = result }, }) catch @panic("out of memory"); } @@ -208,15 +211,25 @@ pub const TestContext = struct { pub fn addCompareObjectFile(self: *Case, src: [:0]const u8, result: []const u8) void { self.updates.append(.{ .src = src, + .name = "update", .case = .{ .CompareObjectFile = result }, }) catch @panic("out of memory"); } + pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void { + return self.addErrorNamed("update", src, errors); + } + /// Adds a subcase in which the module is updated with `src`, which /// should contain invalid input, and ensures that compilation fails /// for the expected reasons, given in sequential order in `errors` in /// the form `:line:column: error: message`. - pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void { + pub fn addErrorNamed( + self: *Case, + name: []const u8, + src: [:0]const u8, + errors: []const []const u8, + ) void { var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch @panic("out of memory"); for (errors) |err_msg_line, i| { if (std.mem.startsWith(u8, err_msg_line, "error: ")) { @@ -279,7 +292,11 @@ pub const TestContext = struct { }, }; } - self.updates.append(.{ .src = src, .case = .{ .Error = array } }) catch @panic("out of memory"); + self.updates.append(.{ + .src = src, + .name = name, + .case = .{ .Error = array }, + }) catch @panic("out of memory"); } /// Adds a subcase in which the module is updated with `src`, and @@ -616,7 +633,7 @@ pub const TestContext = struct { if (skip_compile_errors) return; const gpa = general_purpose_allocator.allocator(); - var case: ?*Case = null; + var opt_case: ?*Case = null; var it = dir.iterate(); while (try it.next()) |entry| { @@ -671,10 +688,9 @@ pub const TestContext = struct { // The entire file contents is the source, including the manifest const src = try gpa.dupeZ(u8, contents); - // Create a new test case, if necessary - case = if (one_test_case_per_file or case == null) blk: { + const case = opt_case orelse case: { ctx.cases.append(TestContext.Case{ - .name = if (one_test_case_per_file) case_name else name, + .name = name, .target = .{}, .backend = backend, .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), @@ -682,11 +698,15 @@ pub const TestContext = struct { .output_mode = output_mode, .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), }) catch @panic("out of memory"); - break :blk &ctx.cases.items[ctx.cases.items.len - 1]; - } else case.?; - - // Add our update + expected errors - case.?.addError(src, errors.items); + break :case &ctx.cases.items[ctx.cases.items.len - 1]; + }; + if (one_test_case_per_file) { + case.name = case_name; + case.addError(src, errors.items); + opt_case = null; + } else { + case.addErrorNamed(case_name, src, errors.items); + } } else { return error.InvalidFile; // Manifests are currently mandatory } @@ -1018,7 +1038,7 @@ pub const TestContext = struct { defer comp.destroy(); for (case.updates.items) |update, update_index| { - var update_node = root_node.start("update", 3); + var update_node = root_node.start(update.name, 3); update_node.activate(); defer update_node.end(); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 14f32c150e..7da410f9e3 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -14,7 +14,7 @@ pub fn addCases(ctx: *TestContext) !void { defer stage2_dir.close(); const one_test_case_per_file = false; - try ctx.addErrorCasesFromDir("stage2 compile errors", stage2_dir, .stage2, .Obj, false, one_test_case_per_file); + try ctx.addErrorCasesFromDir("stage2", stage2_dir, .stage2, .Obj, false, one_test_case_per_file); } { From 12e1304805cbe132d17b31bac2e8123869007e4d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 Mar 2022 12:20:10 -0700 Subject: [PATCH 0990/2031] test harness: fix not honoring one_test_case_per_file I regressed this in 9aa431cba34699ae35f7905398a0c8263b2ad453. thanks @topolarity for pointing out the issue --- src/test.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test.zig b/src/test.zig index 067efe4f21..870c0cab87 100644 --- a/src/test.zig +++ b/src/test.zig @@ -698,7 +698,9 @@ pub const TestContext = struct { .output_mode = output_mode, .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), }) catch @panic("out of memory"); - break :case &ctx.cases.items[ctx.cases.items.len - 1]; + const case = &ctx.cases.items[ctx.cases.items.len - 1]; + opt_case = case; + break :case case; }; if (one_test_case_per_file) { case.name = case_name; From f9773ab622d84527ddfdb08d07443ec05cf28737 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 29 Mar 2022 10:11:24 +0200 Subject: [PATCH 0991/2031] x64: clean up abstraction for generating integer division --- src/arch/x86_64/CodeGen.zig | 54 ++++++++++++++++++------------------- src/arch/x86_64/Emit.zig | 7 +++-- src/arch/x86_64/Mir.zig | 1 + 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index bc0171899a..ef4eeba3d8 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1501,11 +1501,12 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch}); } -/// Generates signed or unsigned integer division. +/// Generates signed or unsigned integer multiplication/division. /// Requires use of .rax and .rdx registers. Spills them if necessary. /// Quotient is saved in .rax and remainder in .rdx. -fn genIntDivOpMir( +fn genIntMulDivOpMir( self: *Self, + tag: Mir.Inst.Tag, ty: Type, signedness: std.builtin.Signedness, lhs: MCValue, @@ -1513,7 +1514,7 @@ fn genIntDivOpMir( ) !void { const abi_size = @intCast(u32, ty.abiSize(self.target.*)); if (abi_size > 8) { - return self.fail("TODO implement genIntDivOpMir for ABI size larger than 8", .{}); + return self.fail("TODO implement genIntMulDivOpMir for ABI size larger than 8", .{}); } try self.register_manager.getReg(.rax, null); @@ -1521,17 +1522,7 @@ fn genIntDivOpMir( self.register_manager.freezeRegs(&.{ .rax, .rdx }); defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); - const dividend = switch (lhs) { - .register => lhs, - else => blk: { - const reg = try self.copyToTmpRegister(ty, lhs); - break :blk MCValue{ .register = reg }; - }, - }; - try self.genSetReg(ty, .rax, dividend); - - self.register_manager.freezeRegs(&.{dividend.register}); - defer self.register_manager.unfreezeRegs(&.{dividend.register}); + try self.genSetReg(ty, .rax, lhs); switch (signedness) { .signed => { @@ -1555,22 +1546,19 @@ fn genIntDivOpMir( }, } - const divisor = switch (rhs) { + const factor = switch (rhs) { .register => rhs, + .stack_offset => rhs, else => blk: { const reg = try self.copyToTmpRegister(ty, rhs); break :blk MCValue{ .register = reg }; }, }; - const op_tag: Mir.Inst.Tag = switch (signedness) { - .signed => .idiv, - .unsigned => .div, - }; - switch (divisor) { + switch (factor) { .register => |reg| { _ = try self.addInst(.{ - .tag = op_tag, + .tag = tag, .ops = (Mir.Ops{ .reg1 = reg, }).encode(), @@ -1579,7 +1567,7 @@ fn genIntDivOpMir( }, .stack_offset => |off| { _ = try self.addInst(.{ - .tag = op_tag, + .tag = tag, .ops = (Mir.Ops{ .reg2 = .rbp, .flags = switch (abi_size) { @@ -1612,7 +1600,10 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa self.register_manager.freezeRegs(&.{divisor}); defer self.register_manager.unfreezeRegs(&.{ dividend, divisor }); - try self.genIntDivOpMir(Type.isize, signedness, .{ .register = dividend }, .{ .register = divisor }); + try self.genIntMulDivOpMir(switch (signedness) { + .signed => .idiv, + .unsigned => .div, + }, Type.isize, signedness, .{ .register = dividend }, .{ .register = divisor }); _ = try self.addInst(.{ .tag = .xor, @@ -1673,13 +1664,16 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { const signedness = ty.intInfo(self.target.*).signedness; if (signedness == .unsigned) { - try self.genIntDivOpMir(ty, signedness, lhs, rhs); + try self.genIntMulDivOpMir(.div, ty, signedness, lhs, rhs); break :result MCValue{ .register = .rax }; } switch (tag) { .div_exact, .div_trunc => { - try self.genIntDivOpMir(ty, signedness, lhs, rhs); + try self.genIntMulDivOpMir(switch (signedness) { + .signed => .idiv, + .unsigned => .div, + }, ty, signedness, lhs, rhs); break :result MCValue{ .register = .rax }; }, .div_floor => { @@ -1704,7 +1698,10 @@ fn airRem(self: *Self, inst: Air.Inst.Index) !void { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const signedness = ty.intInfo(self.target.*).signedness; - try self.genIntDivOpMir(ty, signedness, lhs, rhs); + try self.genIntMulDivOpMir(switch (signedness) { + .signed => .idiv, + .unsigned => .div, + }, ty, signedness, lhs, rhs); break :result MCValue{ .register = .rdx }; }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); @@ -1725,7 +1722,10 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void { const signedness = ty.intInfo(self.target.*).signedness; switch (signedness) { .unsigned => { - try self.genIntDivOpMir(ty, signedness, lhs, rhs); + try self.genIntMulDivOpMir(switch (signedness) { + .signed => .idiv, + .unsigned => .div, + }, ty, signedness, lhs, rhs); break :result MCValue{ .register = .rdx }; }, .signed => { diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 8d478f0a64..e8ea10bf29 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -145,6 +145,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .sar => try emit.mirShift(.sar, inst), .imul => try emit.mirMulDiv(.imul, inst), + .mul => try emit.mirMulDiv(.mul, inst), .idiv => try emit.mirMulDiv(.idiv, inst), .div => try emit.mirMulDiv(.div, inst), .imul_complex => try emit.mirIMulComplex(inst), @@ -1164,6 +1165,7 @@ const Tag = enum { brk, nop, imul, + mul, idiv, div, syscall, @@ -1411,7 +1413,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .setnl, .setge => OpCode.twoByte(0x0f, 0x9d), .setle, .setng => OpCode.twoByte(0x0f, 0x9e), .setnle, .setg => OpCode.twoByte(0x0f, 0x9f), - .idiv, .div, .imul => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7), + .idiv, .div, .imul, .mul => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7), .fisttp16 => OpCode.oneByte(0xdf), .fisttp32 => OpCode.oneByte(0xdb), .fisttp64 => OpCode.oneByte(0xdd), @@ -1554,9 +1556,10 @@ inline fn getModRmExt(tag: Tag) ?u3 { => 0x4, .shr => 0x5, .sar => 0x7, + .mul => 0x4, .imul => 0x5, - .idiv => 0x7, .div => 0x6, + .idiv => 0x7, .fisttp16 => 0x1, .fisttp32 => 0x1, .fisttp64 => 0x1, diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 96a8eec93d..183a76e4b7 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -227,6 +227,7 @@ pub const Inst = struct { /// 0b11 qword ptr [reg2 + imm32] imul, idiv, + mul, div, /// ops flags: form: From ee6e3aef5deb4aedd8f65e0ba7c44b4979ed6a96 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 29 Mar 2022 10:39:25 +0200 Subject: [PATCH 0992/2031] x64: redo @mulWithOverflow using rax/rdx based multiplication --- src/arch/x86_64/CodeGen.zig | 76 +++++++++++++++++++++++++------------ test/behavior/math.zig | 8 ++-- 2 files changed, 54 insertions(+), 30 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index ef4eeba3d8..a800da2a9d 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1254,7 +1254,7 @@ fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_r offset_mcv.freezeIfRegister(&self.register_manager); defer offset_mcv.unfreezeIfRegister(&self.register_manager); - try self.genIMulOpMir(offset_ty, offset_mcv, .{ .immediate = elem_size }); + try self.genIntMulComplexOpMir(offset_ty, offset_mcv, .{ .immediate = elem_size }); const tag = self.air.instructions.items(.tag)[inst]; switch (tag) { @@ -1396,10 +1396,27 @@ fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { fn airMul(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 - try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const ty = self.air.typeOfIndex(inst); + + if (ty.zigTypeTag() != .Int) { + return self.fail("TODO implement 'mul' for operands of dst type {}", .{ty.zigTypeTag()}); + } + + // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. + try self.register_manager.getReg(.rax, null); + try self.register_manager.getReg(.rdx, null); + + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + const signedness = ty.intInfo(self.target.*).signedness; + try self.genIntMulDivOpMir(switch (signedness) { + .signed => .imul, + .unsigned => .mul, + }, ty, signedness, lhs, rhs); + break :result MCValue{ .register = .rax }; + }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1474,23 +1491,31 @@ fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + const result = if (self.liveness.isUnused(inst)) .dead else result: { + const ty = self.air.typeOf(bin_op.lhs); + const signedness: std.builtin.Signedness = blk: { + if (ty.zigTypeTag() != .Int) { + return self.fail("TODO implement airMulWithOverflow for type {}", .{ty.fmtDebug()}); + } + break :blk ty.intInfo(self.target.*).signedness; + }; - if (self.liveness.isUnused(inst)) { - return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); - } + // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. + try self.register_manager.getReg(.rax, null); + try self.register_manager.getReg(.rdx, null); - const ty = self.air.typeOf(bin_op.lhs); - const signedness: std.builtin.Signedness = blk: { - if (ty.zigTypeTag() != .Int) { - return self.fail("TODO implement airMulWithOverflow for type {}", .{ty.fmtDebug()}); + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + try self.genIntMulDivOpMir(switch (signedness) { + .signed => .imul, + .unsigned => .mul, + }, ty, signedness, lhs, rhs); + + switch (signedness) { + .signed => break :result MCValue{ .register_overflow_signed = .rax }, + .unsigned => break :result MCValue{ .register_overflow_unsigned = .rax }, } - break :blk ty.intInfo(self.target.*).signedness; - }; - - const partial = try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); - const result: MCValue = switch (signedness) { - .signed => .{ .register_overflow_signed = partial.register }, - .unsigned => .{ .register_overflow_unsigned = partial.register }, }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); @@ -1730,7 +1755,7 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void { }, .signed => { const div_floor = try self.genInlineIntDivFloor(ty, lhs, rhs); - try self.genIMulOpMir(ty, div_floor, rhs); + try self.genIntMulComplexOpMir(ty, div_floor, rhs); const reg = try self.copyToTmpRegister(ty, lhs); try self.genBinMathOpMir(.sub, ty, .{ .register = reg }, div_floor); @@ -2132,7 +2157,7 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { fn elemOffset(self: *Self, index_ty: Type, index: MCValue, elem_size: u64) !Register { const reg = try self.copyToTmpRegister(index_ty, index); - try self.genIMulOpMir(index_ty, .{ .register = reg }, .{ .immediate = elem_size }); + try self.genIntMulComplexOpMir(index_ty, .{ .register = reg }, .{ .immediate = elem_size }); return reg; } @@ -3096,7 +3121,6 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: .bool_or, .bit_or => try self.genBinMathOpMir(.@"or", dst_ty, dst_mcv, src_mcv), .bool_and, .bit_and => try self.genBinMathOpMir(.@"and", dst_ty, dst_mcv, src_mcv), .xor, .not => try self.genBinMathOpMir(.xor, dst_ty, dst_mcv, src_mcv), - .mul, .mulwrap, .mul_with_overflow => try self.genIMulOpMir(dst_ty, dst_mcv, src_mcv), else => unreachable, } return dst_mcv; @@ -3252,8 +3276,10 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC } } -// Performs integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv. -fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { +/// Performs multi-operand integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv. +/// Does not use/spill .rax/.rdx. +/// Does not support byte-size operands. +fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); switch (dst_mcv) { .none => unreachable, @@ -3299,7 +3325,7 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! } else { // TODO verify we don't spill and assign to the same register as dst_mcv const src_reg = try self.copyToTmpRegister(dst_ty, src_mcv); - return self.genIMulOpMir(dst_ty, dst_mcv, MCValue{ .register = src_reg }); + return self.genIntMulComplexOpMir(dst_ty, dst_mcv, MCValue{ .register = src_reg }); } }, .stack_offset => |off| { diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 314d52be66..815c6e0efc 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -697,11 +697,9 @@ test "@mulWithOverflow" { try expect(!@mulWithOverflow(u8, a, b, &result)); try expect(result == 246); - if (builtin.zig_backend != .stage2_x86_64) { // TODO fix mul/imul on x86_64 - b = 4; - try expect(@mulWithOverflow(u8, a, b, &result)); - try expect(result == 236); - } + b = 4; + try expect(@mulWithOverflow(u8, a, b, &result)); + try expect(result == 236); } test "@subWithOverflow" { From 60879bc8ae216ddd33fab2e07d1d460e32636c95 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 29 Mar 2022 11:00:57 +0200 Subject: [PATCH 0993/2031] x64: clean up instruction tracking for div/mul ops --- src/arch/x86_64/CodeGen.zig | 71 ++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 17 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index a800da2a9d..744c7fbf75 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1404,8 +1404,10 @@ fn airMul(self: *Self, inst: Air.Inst.Index) !void { } // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. - try self.register_manager.getReg(.rax, null); + try self.register_manager.getReg(.rax, inst); try self.register_manager.getReg(.rdx, null); + self.register_manager.freezeRegs(&.{ .rax, .rdx }); + defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); @@ -1501,8 +1503,10 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { }; // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. - try self.register_manager.getReg(.rax, null); + try self.register_manager.getReg(.rax, inst); try self.register_manager.getReg(.rdx, null); + self.register_manager.freezeRegs(&.{ .rax, .rdx }); + defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); @@ -1527,7 +1531,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { } /// Generates signed or unsigned integer multiplication/division. -/// Requires use of .rax and .rdx registers. Spills them if necessary. +/// Clobbers .rax and .rdx registers. /// Quotient is saved in .rax and remainder in .rdx. fn genIntMulDivOpMir( self: *Self, @@ -1542,11 +1546,6 @@ fn genIntMulDivOpMir( return self.fail("TODO implement genIntMulDivOpMir for ABI size larger than 8", .{}); } - try self.register_manager.getReg(.rax, null); - try self.register_manager.getReg(.rdx, null); - self.register_manager.freezeRegs(&.{ .rax, .rdx }); - defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); - try self.genSetReg(ty, .rax, lhs); switch (signedness) { @@ -1610,6 +1609,7 @@ fn genIntMulDivOpMir( } } +/// Clobbers .rax and .rdx registers. fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCValue { const signedness = ty.intInfo(self.target.*).signedness; const dividend = switch (lhs) { @@ -1680,14 +1680,42 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement {}", .{tag}); } + const signedness = ty.intInfo(self.target.*).signedness; + // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. - try self.register_manager.getReg(.rax, null); + const track_rax: ?Air.Inst.Index = blk: { + if (signedness == .unsigned) break :blk inst; + switch (tag) { + .div_exact, .div_trunc => break :blk inst, + else => break :blk null, + } + }; + try self.register_manager.getReg(.rax, track_rax); try self.register_manager.getReg(.rdx, null); + self.register_manager.freezeRegs(&.{ .rax, .rdx }); + defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); + lhs.freezeIfRegister(&self.register_manager); + defer lhs.unfreezeIfRegister(&self.register_manager); + + const rhs = blk: { + const rhs = try self.resolveInst(bin_op.rhs); + if (signedness == .signed) { + switch (tag) { + .div_floor => { + rhs.freezeIfRegister(&self.register_manager); + defer rhs.unfreezeIfRegister(&self.register_manager); + break :blk try self.copyToRegisterWithInstTracking(inst, ty, rhs); + }, + else => {}, + } + } + break :blk rhs; + }; + rhs.freezeIfRegister(&self.register_manager); + defer rhs.unfreezeIfRegister(&self.register_manager); - const signedness = ty.intInfo(self.target.*).signedness; if (signedness == .unsigned) { try self.genIntMulDivOpMir(.div, ty, signedness, lhs, rhs); break :result MCValue{ .register = .rax }; @@ -1719,9 +1747,13 @@ fn airRem(self: *Self, inst: Air.Inst.Index) !void { } // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. try self.register_manager.getReg(.rax, null); - try self.register_manager.getReg(.rdx, null); + try self.register_manager.getReg(.rdx, inst); + self.register_manager.freezeRegs(&.{ .rax, .rdx }); + defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); + const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); + const signedness = ty.intInfo(self.target.*).signedness; try self.genIntMulDivOpMir(switch (signedness) { .signed => .idiv, @@ -1739,12 +1771,17 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void { if (ty.zigTypeTag() != .Int) { return self.fail("TODO implement .mod for operands of dst type {}", .{ty.zigTypeTag()}); } + const signedness = ty.intInfo(self.target.*).signedness; + // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. try self.register_manager.getReg(.rax, null); - try self.register_manager.getReg(.rdx, null); + try self.register_manager.getReg(.rdx, if (signedness == .unsigned) inst else null); + self.register_manager.freezeRegs(&.{ .rax, .rdx }); + defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); + const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const signedness = ty.intInfo(self.target.*).signedness; + switch (signedness) { .unsigned => { try self.genIntMulDivOpMir(switch (signedness) { @@ -1757,10 +1794,10 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void { const div_floor = try self.genInlineIntDivFloor(ty, lhs, rhs); try self.genIntMulComplexOpMir(ty, div_floor, rhs); - const reg = try self.copyToTmpRegister(ty, lhs); - try self.genBinMathOpMir(.sub, ty, .{ .register = reg }, div_floor); + const result = try self.copyToRegisterWithInstTracking(inst, ty, lhs); + try self.genBinMathOpMir(.sub, ty, result, div_floor); - break :result MCValue{ .register = reg }; + break :result result; }, } }; From d447cd940d7da884f0d699d9da679d8bbabb237a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 29 Mar 2022 11:50:25 +0200 Subject: [PATCH 0994/2031] x64: track callee and caller saved registers This is now required to correctly track and spill registers required for some ops such `mul` or `div` (both required use of `.rax` and `.rdx` registers). --- src/arch/x86_64/CodeGen.zig | 10 ++++++++-- src/arch/x86_64/abi.zig | 11 ++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 744c7fbf75..854017054b 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -31,6 +31,8 @@ const bits = @import("bits.zig"); const abi = @import("abi.zig"); const Register = bits.Register; const callee_preserved_regs = abi.callee_preserved_regs; +const caller_preserved_regs = abi.caller_preserved_regs; +const allocatable_registers = abi.allocatable_registers; const c_abi_int_param_regs = abi.c_abi_int_param_regs; const c_abi_int_return_regs = abi.c_abi_int_return_regs; @@ -40,7 +42,7 @@ const InnerError = error{ OutOfRegisters, }; -const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs); +const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers); gpa: Allocator, air: Air, @@ -3519,6 +3521,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. try self.spillCompareFlagsIfOccupied(); + for (caller_preserved_regs) |reg| { + try self.register_manager.getReg(reg, null); + } + if (info.return_value == .stack_offset) { const ret_ty = fn_ty.fnReturnType(); const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); @@ -3715,7 +3721,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const result: MCValue = result: { switch (info.return_value) { .register => |reg| { - if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) { + if (RegisterManager.indexOfRegIntoTracked(reg) == null) { // Save function return value in a callee saved register break :result try self.copyToRegisterWithInstTracking( inst, diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index 9fd09ab60a..4afa3ad792 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -370,8 +370,13 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class { } } -/// These registers need to be preserved (saved on the stack) and restored by the callee before getting clobbered -/// and when the callee returns. -pub const callee_preserved_regs = [_]Register{ .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; +/// Note that .rsp and .rbp also belong to this set, however, we never expect to use them +/// for anything else but stack offset tracking therefore we exclude them from this set. +pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 }; +/// These registers need to be preserved (saved on the stack) and restored by the caller before +/// the caller relinquishes control to a subroutine via call instruction (or similar). +/// In other words, these registers are free to use by the callee. +pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; +pub const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs; pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; From 376d0878ec2e366633e2dbc7c91ab7ae2a6ae5b7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 29 Mar 2022 18:08:03 +0200 Subject: [PATCH 0995/2031] x64: spill .rdi to stack if expecting return value saved on stack Since .rdi is not part of the callee saved registers, it needs to be proactively spilled to the stack so that we don't clobber the return address where to save the return value. --- src/arch/x86_64/CodeGen.zig | 113 ++++++++++++++---------------------- 1 file changed, 45 insertions(+), 68 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 854017054b..da25541cd1 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -58,7 +58,6 @@ arg_index: u32, src_loc: Module.SrcLoc, stack_align: u32, -ret_backpatches: std.ArrayListUnmanaged(Mir.Inst.Index) = .{}, compare_flags_inst: ?Air.Inst.Index = null, /// MIR Instructions @@ -353,7 +352,6 @@ pub fn generate( std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index).init(bin_file.allocator) else {}, }; - defer function.ret_backpatches.deinit(bin_file.allocator); defer function.stack.deinit(bin_file.allocator); defer function.blocks.deinit(bin_file.allocator); defer function.exitlude_jump_relocs.deinit(bin_file.allocator); @@ -482,6 +480,21 @@ fn gen(self: *Self) InnerError!void { .data = undefined, }); + if (self.ret_mcv == .stack_offset) { + // The address where to store the return value for the caller is in `.rdi` + // register which the callee is free to clobber. Therefore, we purposely + // spill it to stack immediately. + const ptr_ty = Type.usize; + const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*)); + const abi_align = ptr_ty.abiAlignment(self.target.*); + const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset + abi_size, abi_align); + self.next_stack_offset = stack_offset; + self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset); + try self.genSetStack(ptr_ty, @intCast(i32, stack_offset), MCValue{ .register = .rdi }, .{}); + self.ret_mcv = MCValue{ .stack_offset = @intCast(i32, stack_offset) }; + log.debug("gen: spilling .rdi to stack at offset {}", .{stack_offset}); + } + _ = try self.addInst(.{ .tag = .dbg_prologue_end, .ops = undefined, @@ -519,23 +532,6 @@ fn gen(self: *Self) InnerError!void { var disp = data.disp + 8; inline for (callee_preserved_regs) |reg, i| { if (self.register_manager.isRegAllocated(reg)) { - if (reg.to64() == .rdi) { - for (self.ret_backpatches.items) |inst| { - log.debug(".rdi was spilled, backpatching with mov from stack at offset {}", .{ - -@intCast(i32, disp), - }); - const ops = Mir.Ops.decode(self.mir_instructions.items(.ops)[inst]); - self.mir_instructions.set(inst, Mir.Inst{ - .tag = .mov, - .ops = (Mir.Ops{ - .reg1 = ops.reg1, - .reg2 = .rbp, - .flags = 0b01, - }).encode(), - .data = .{ .imm = @bitCast(u32, -@intCast(i32, disp)) }, - }); - } - } data.regs |= 1 << @intCast(u5, i); self.max_end_stack += 8; disp += 8; @@ -912,8 +908,7 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u // TODO find a free slot instead of always appending const offset = mem.alignForwardGeneric(u32, self.next_stack_offset + abi_size, abi_align); self.next_stack_offset = offset; - if (self.next_stack_offset > self.max_end_stack) - self.max_end_stack = self.next_stack_offset; + self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset); try self.stack.putNoClobber(self.gpa, offset, .{ .inst = inst, .size = abi_size, @@ -3316,7 +3311,6 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC } /// Performs multi-operand integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv. -/// Does not use/spill .rax/.rdx. /// Does not support byte-size operands. fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); @@ -3530,10 +3524,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*)); const stack_offset = @intCast(i32, try self.allocMem(inst, ret_abi_size, ret_abi_align)); + log.debug("airCall: return value on stack at offset {}", .{stack_offset}); try self.register_manager.getReg(.rdi, null); - self.register_manager.freezeRegs(&.{.rdi}); try self.genSetReg(Type.usize, .rdi, .{ .ptr_stack_offset = stack_offset }); + self.register_manager.freezeRegs(&.{.rdi}); info.return_value.stack_offset = stack_offset; } @@ -3720,15 +3715,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const result: MCValue = result: { switch (info.return_value) { - .register => |reg| { - if (RegisterManager.indexOfRegIntoTracked(reg) == null) { - // Save function return value in a callee saved register - break :result try self.copyToRegisterWithInstTracking( - inst, - self.air.typeOfIndex(inst), - info.return_value, - ); - } + .register => { + // Save function return value in a new register + break :result try self.copyToRegisterWithInstTracking( + inst, + self.air.typeOfIndex(inst), + info.return_value, + ); }, else => {}, } @@ -3755,19 +3748,11 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { const ret_ty = self.fn_type.fnReturnType(); switch (self.ret_mcv) { .stack_offset => { - // TODO audit register allocation! - self.register_manager.freezeRegs(&.{ .rax, .rcx, .rdi }); - defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx, .rdi }); - const reg = try self.register_manager.allocReg(null); - const backpatch = try self.addInst(.{ - .tag = .mov, - .ops = (Mir.Ops{ - .reg1 = reg, - .reg2 = .rdi, - }).encode(), - .data = undefined, - }); - try self.ret_backpatches.append(self.gpa, backpatch); + self.register_manager.freezeRegs(&.{ .rax, .rcx }); + defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx }); + const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv); + self.register_manager.freezeRegs(&.{reg}); + defer self.register_manager.unfreezeRegs(&.{reg}); try self.genSetStack(ret_ty, 0, operand, .{ .source_stack_base = .rbp, .dest_stack_base = reg, @@ -3798,19 +3783,11 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { const elem_ty = ptr_ty.elemType(); switch (self.ret_mcv) { .stack_offset => { - // TODO audit register allocation! - self.register_manager.freezeRegs(&.{ .rax, .rcx, .rdi }); - defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx, .rdi }); - const reg = try self.register_manager.allocReg(null); - const backpatch = try self.addInst(.{ - .tag = .mov, - .ops = (Mir.Ops{ - .reg1 = reg, - .reg2 = .rdi, - }).encode(), - .data = undefined, - }); - try self.ret_backpatches.append(self.gpa, backpatch); + self.register_manager.freezeRegs(&.{ .rax, .rcx }); + defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx }); + const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv); + self.register_manager.freezeRegs(&.{reg}); + defer self.register_manager.unfreezeRegs(&.{reg}); try self.genInlineMemcpy(.{ .stack_offset = 0 }, ptr, .{ .immediate = elem_ty.abiSize(self.target.*) }, .{ .source_stack_base = .rbp, .dest_stack_base = reg, @@ -5092,6 +5069,7 @@ const InlineMemcpyOpts = struct { dest_stack_base: ?Register = null, }; +/// Spills .rax and .rcx. fn genInlineMemcpy( self: *Self, dst_ptr: MCValue, @@ -5099,13 +5077,7 @@ fn genInlineMemcpy( len: MCValue, opts: InlineMemcpyOpts, ) InnerError!void { - // TODO this is wrong. We should check first if any of the operands is in `.rax` or `.rcx` before - // spilling. Consolidate with other TODOs regarding register allocation mechanics. - try self.register_manager.getReg(.rax, null); - try self.register_manager.getReg(.rcx, null); - self.register_manager.freezeRegs(&.{ .rax, .rcx }); - defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx }); if (opts.source_stack_base) |reg| self.register_manager.freezeRegs(&.{reg}); defer if (opts.source_stack_base) |reg| self.register_manager.unfreezeRegs(&.{reg}); @@ -5145,7 +5117,6 @@ fn genInlineMemcpy( return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); }, } - self.register_manager.freezeRegs(&.{dst_addr_reg}); defer self.register_manager.unfreezeRegs(&.{dst_addr_reg}); @@ -5181,7 +5152,6 @@ fn genInlineMemcpy( return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr}); }, } - self.register_manager.freezeRegs(&.{src_addr_reg}); defer self.register_manager.unfreezeRegs(&.{src_addr_reg}); @@ -5189,6 +5159,11 @@ fn genInlineMemcpy( const count_reg = regs[0].to64(); const tmp_reg = regs[1].to8(); + self.register_manager.unfreezeRegs(&.{ .rax, .rcx }); + + try self.register_manager.getReg(.rax, null); + try self.register_manager.getReg(.rcx, null); + try self.genSetReg(Type.usize, count_reg, len); // mov rcx, 0 @@ -5284,6 +5259,7 @@ fn genInlineMemcpy( try self.performReloc(loop_reloc); } +/// Spills .rax register. fn genInlineMemset( self: *Self, dst_ptr: MCValue, @@ -5291,9 +5267,7 @@ fn genInlineMemset( len: MCValue, opts: InlineMemcpyOpts, ) InnerError!void { - try self.register_manager.getReg(.rax, null); self.register_manager.freezeRegs(&.{.rax}); - defer self.register_manager.unfreezeRegs(&.{.rax}); const addr_reg = try self.register_manager.allocReg(null); switch (dst_ptr) { @@ -5330,6 +5304,9 @@ fn genInlineMemset( self.register_manager.freezeRegs(&.{addr_reg}); defer self.register_manager.unfreezeRegs(&.{addr_reg}); + self.register_manager.unfreezeRegs(&.{.rax}); + try self.register_manager.getReg(.rax, null); + try self.genSetReg(Type.usize, .rax, len); try self.genBinMathOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 }); From b59428e9f78b1f9f265c0ffdba79b128d77644d2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 Mar 2022 16:56:12 -0700 Subject: [PATCH 0996/2031] Sema: adjust coercion of undefined error union payload To no longer set the error code to undefined. This fixes the problem where an undefined single-item pointer coerced to an error union of a slice set the whole thing to undefined even though the sub-coercion to the slice would have produced a defined value. --- src/Sema.zig | 5 ----- test/behavior/cast.zig | 10 ++++++++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index afb97f3b3d..09f99c5751 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -18140,11 +18140,6 @@ fn coerce( return sema.addConstUndef(dest_ty); }, else => { - // undefined sets the error code also to undefined. - if (is_undef) { - return sema.addConstUndef(dest_ty); - } - // T to E!T return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src); }, diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 15a793d109..8a4bec935e 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1372,3 +1372,13 @@ test "cast compatible optional types" { var b: ?[]const u8 = a; try expect(b == null); } + +test "coerce undefined single-item pointer of array to error union of slice" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const a = @as([*]u8, undefined)[0..0]; + var b: error{a}![]const u8 = a; + const s = try b; + try expect(s.len == 0); +} From e39c86399da565d3855d54b242bf7b5940e3924d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 Mar 2022 20:08:28 -0700 Subject: [PATCH 0997/2031] link: half-hearted bug fix for decl_state field The init()/commit() API of this field leads to the type of bug that this commit fixes by defering an uncomfortably complex expression. I didn't bother doing the equivalent fix in link/MachO.zig because instead I think the `decl_state` field should be entirely removed from Dwarf. --- src/link/Dwarf.zig | 2 +- src/link/Elf.zig | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 83222090c0..7840b8d6a7 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -91,7 +91,7 @@ pub const DeclState = struct { }; } - fn deinit(self: *DeclState, gpa: Allocator) void { + pub fn deinit(self: *DeclState, gpa: Allocator) void { self.dbg_line.deinit(); self.dbg_info.deinit(); self.abbrev_type_arena.deinit(); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 771a57b26e..ece30c4347 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2342,6 +2342,12 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven if (self.dwarf) |*dw| { try dw.initDeclState(decl); } + defer if (self.dwarf) |*dw| { + if (dw.decl_state) |*ds| { + ds.deinit(dw.allocator); + dw.decl_state = null; + } + }; const res = if (self.dwarf) |*dw| try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ From 9821a0c6f0caf3df4cc6000c000d9cc38baf27d8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 Mar 2022 20:11:48 -0700 Subject: [PATCH 0998/2031] Sema: fix generic instantiations of return types with nested captures * In semaStructFields and semaUnionFields we return error.GenericPoison if one of the field types ends up being generic poison. - This requires handling function calls and function types taking this into account when calling `typeRequiresComptime` on the return type. * Unrelated: I noticed using Valgrind that struct reification did not populate the `known_opv` field. After fixing it, the behavior tests run Valgrind-clean. * ZIR: use `@ptrCast` to cast between slices instead of exploiting the fact that stage1 incorrectly allows `@bitCast` between slices. - A future enhancement will make Zig support `@ptrCast` to directly cast between slices. --- src/Sema.zig | 40 +++++++++++++++++++++++++++++++------- src/Zir.zig | 5 +++-- test/behavior/generics.zig | 16 +++++++++++++++ 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 09f99c5751..e1e77f4c11 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4764,12 +4764,20 @@ fn analyzeCall( const gpa = sema.gpa; - var is_comptime_call = block.is_comptime or modifier == .compile_time or - try sema.typeRequiresComptime(block, func_src, func_ty_info.return_type); + var is_generic_call = func_ty_info.is_generic; + var is_comptime_call = block.is_comptime or modifier == .compile_time; + if (!is_comptime_call) { + if (sema.typeRequiresComptime(block, func_src, func_ty_info.return_type)) |ct| { + is_comptime_call = ct; + } else |err| switch (err) { + error.GenericPoison => is_generic_call = true, + else => |e| return e, + } + } var is_inline_call = is_comptime_call or modifier == .always_inline or func_ty_info.cc == .Inline; - if (!is_inline_call and func_ty_info.is_generic) { + if (!is_inline_call and is_generic_call) { if (sema.instantiateGenericCall( block, func, @@ -6410,10 +6418,20 @@ fn funcCommon( } } - is_generic = is_generic or - try sema.typeRequiresComptime(block, ret_ty_src, bare_return_type); + const ret_poison = if (!is_generic) rp: { + if (sema.typeRequiresComptime(block, ret_ty_src, bare_return_type)) |ret_comptime| { + is_generic = ret_comptime; + break :rp bare_return_type.tag() == .generic_poison; + } else |err| switch (err) { + error.GenericPoison => { + is_generic = true; + break :rp true; + }, + else => |e| return e, + } + } else bare_return_type.tag() == .generic_poison; - const return_type = if (!inferred_error_set or bare_return_type.tag() == .generic_poison) + const return_type = if (!inferred_error_set or ret_poison) bare_return_type else blk: { const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode); @@ -13570,7 +13588,7 @@ fn reifyStruct( .zir_index = inst, .layout = layout_val.toEnum(std.builtin.Type.ContainerLayout), .status = .have_field_types, - .known_non_opv = undefined, + .known_non_opv = false, .namespace = .{ .parent = block.namespace, .ty = struct_ty, @@ -21666,6 +21684,10 @@ fn semaStructFields( // TODO emit compile errors for invalid field types // such as arrays and pointers inside packed structs. + if (field_ty.tag() == .generic_poison) { + return error.GenericPoison; + } + const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name); assert(!gop.found_existing); gop.value_ptr.* = .{ @@ -21913,6 +21935,10 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { // But only resolve the source location if we need to emit a compile error. try sema.resolveType(&block_scope, src, field_type_ref); + if (field_ty.tag() == .generic_poison) { + return error.GenericPoison; + } + const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); assert(!gop.found_existing); gop.value_ptr.* = .{ diff --git a/src/Zir.zig b/src/Zir.zig index d895281141..9474627675 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -63,7 +63,7 @@ pub const ExtraIndex = enum(u32) { /// Returns the requested data, as well as the new index which is at the start of the /// trailers for the object. pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, end: usize } { - const fields = std.meta.fields(T); + const fields = @typeInfo(T).Struct.fields; var i: usize = index; var result: T = undefined; inline for (fields) |field| { @@ -94,7 +94,8 @@ pub fn nullTerminatedString(code: Zir, index: usize) [:0]const u8 { pub fn refSlice(code: Zir, start: usize, len: usize) []Inst.Ref { const raw_slice = code.extra[start..][0..len]; - return @bitCast([]Inst.Ref, raw_slice); + // TODO we should be able to directly `@ptrCast` the slice to the other slice type. + return @ptrCast([*]Inst.Ref, raw_slice.ptr)[0..len]; } pub fn hasCompileErrors(code: Zir) bool { diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index 867cf940b8..08f1e5bad9 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -290,3 +290,19 @@ test "generic function with void and comptime parameter" { var s: S = .{ .x = 1234 }; try namespace.foo({}, &s, u8); } + +test "anonymous struct return type referencing comptime parameter" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const S = struct { + pub fn extraData(comptime T: type, index: usize) struct { data: T, end: usize } { + return .{ + .data = 1234, + .end = index, + }; + } + }; + const s = S.extraData(i32, 5678); + try expect(s.data == 1234); + try expect(s.end == 5678); +} From 83617eac5902a9e66449e8c409dfa9e560bf9f12 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 Mar 2022 22:16:43 -0700 Subject: [PATCH 0999/2031] std: avoid referencing event loop when io_mode is blocking This prevents unwanted symbols from ending up in the output binary. --- lib/std/event/loop.zig | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/std/event/loop.zig b/lib/std/event/loop.zig index 23c89aabc5..1eaa95d249 100644 --- a/lib/std/event/loop.zig +++ b/lib/std/event/loop.zig @@ -103,12 +103,17 @@ pub const Loop = struct { }; }; - var global_instance_state: Loop = undefined; - const default_instance: ?*Loop = switch (std.io.mode) { + const LoopOrVoid = switch (std.io.mode) { + .blocking => void, + .evented => Loop, + }; + + var global_instance_state: LoopOrVoid = undefined; + const default_instance: ?*LoopOrVoid = switch (std.io.mode) { .blocking => null, .evented => &global_instance_state, }; - pub const instance: ?*Loop = if (@hasDecl(root, "event_loop")) root.event_loop else default_instance; + pub const instance: ?*LoopOrVoid = if (@hasDecl(root, "event_loop")) root.event_loop else default_instance; /// TODO copy elision / named return values so that the threads referencing *Loop /// have the correct pointer value. From 05947ea870f95ab90a75e174079492f546baaf72 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 Mar 2022 22:19:06 -0700 Subject: [PATCH 1000/2031] stage2: implement `@intToError` with safety This commit introduces a new AIR instruction `cmp_lt_errors_len`. It's specific to this use case for two reasons: * The total number of errors is not stable during semantic analysis; it can only be reliably checked when flush() is called. So the backend that is lowering the instruction must emit a relocation of some kind and then populate it during flush(). * The fewer AIR instructions in memory, the better for compiler performance, so we squish complex meanings into AIR tags without hesitation. The instruction is implemented only in the LLVM backend so far. It does this by creating a simple function which is gutted and re-populated with each flush(). AstGen now uses ResultLoc.coerced_ty for `@intToError` and Sema does the coercion. --- src/Air.zig | 14 ++++++-- src/AstGen.zig | 2 +- src/Liveness.zig | 1 + src/Sema.zig | 19 ++++++----- src/arch/aarch64/CodeGen.zig | 10 ++++++ src/arch/arm/CodeGen.zig | 10 ++++++ src/arch/riscv64/CodeGen.zig | 10 ++++++ src/arch/wasm/CodeGen.zig | 12 +++++++ src/arch/x86_64/CodeGen.zig | 10 ++++++ src/codegen/c.zig | 3 +- src/codegen/llvm.zig | 63 ++++++++++++++++++++++++++++++++++++ src/print_air.zig | 1 + test/behavior/cast.zig | 6 +++- 13 files changed, 146 insertions(+), 15 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index a0fb512934..39215495c3 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -637,6 +637,15 @@ pub const Inst = struct { /// Uses the `pl_op` field, payload represents the index of the target memory. wasm_memory_grow, + /// Returns `true` if and only if the operand, an integer with + /// the same size as the error integer type, is less than the + /// total number of errors in the Module. + /// Result type is always `bool`. + /// Uses the `un_op` field. + /// Note that the number of errors in the Module cannot be considered stable until + /// flush(). + cmp_lt_errors_len, + pub fn fromCmpOp(op: std.math.CompareOperator) Tag { return switch (op) { .lt => .cmp_lt, @@ -928,6 +937,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .cmp_gte, .cmp_gt, .cmp_neq, + .cmp_lt_errors_len, .is_null, .is_non_null, .is_null_ptr, @@ -936,9 +946,9 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .is_non_err, .is_err_ptr, .is_non_err_ptr, - => return Type.initTag(.bool), + => return Type.bool, - .const_ty => return Type.initTag(.type), + .const_ty => return Type.type, .alloc, .ret_ptr, diff --git a/src/AstGen.zig b/src/AstGen.zig index 6c75cc233c..ed6c9f86ce 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -7250,7 +7250,7 @@ fn builtinCall( .ptr_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .ptr_to_int), .error_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .error_to_int), - .int_to_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .u16_type }, params[0], .int_to_error), + .int_to_error => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .u16_type }, params[0], .int_to_error), .compile_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .compile_error), .set_eval_branch_quota => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .u32_type }, params[0], .set_eval_branch_quota), .enum_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .enum_to_int), diff --git a/src/Liveness.zig b/src/Liveness.zig index 4b099baf6d..59f7f5be91 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -393,6 +393,7 @@ fn analyzeInst( .ceil, .round, .trunc_float, + .cmp_lt_errors_len, => { const operand = inst_datas[inst].un_op; return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none }); diff --git a/src/Sema.zig b/src/Sema.zig index e1e77f4c11..7ad90cdf09 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5640,7 +5640,7 @@ fn zirErrorToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const op = sema.resolveInst(inst_data.operand); const op_coerced = try sema.coerce(block, Type.anyerror, op, operand_src); - const result_ty = Type.initTag(.u16); + const result_ty = Type.u16; if (try sema.resolveMaybeUndefVal(block, src, op_coerced)) |val| { if (val.isUndef()) { @@ -5665,32 +5665,31 @@ fn zirIntToError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - - const op = sema.resolveInst(inst_data.operand); + const uncasted_operand = sema.resolveInst(inst_data.operand); + const operand = try sema.coerce(block, Type.u16, uncasted_operand, operand_src); const target = sema.mod.getTarget(); - if (try sema.resolveDefinedValue(block, operand_src, op)) |value| { - const int = value.toUnsignedInt(target); + if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| { + const int = try sema.usizeCast(block, operand_src, value.toUnsignedInt(target)); if (int > sema.mod.global_error_set.count() or int == 0) return sema.fail(block, operand_src, "integer value {d} represents no error", .{int}); const payload = try sema.arena.create(Value.Payload.Error); payload.* = .{ .base = .{ .tag = .@"error" }, - .data = .{ .name = sema.mod.error_name_list.items[@intCast(usize, int)] }, + .data = .{ .name = sema.mod.error_name_list.items[int] }, }; return sema.addConstant(Type.anyerror, Value.initPayload(&payload.base)); } try sema.requireRuntimeBlock(block, src); if (block.wantSafety()) { - return sema.fail(block, src, "TODO: get max errors in compilation", .{}); - // const is_gt_max = @panic("TODO get max errors in compilation"); - // try sema.addSafetyCheck(block, is_gt_max, .invalid_error_code); + const is_lt_len = try block.addUnOp(.cmp_lt_errors_len, operand); + try sema.addSafetyCheck(block, is_lt_len, .invalid_error_code); } return block.addInst(.{ .tag = .bitcast, .data = .{ .ty_op = .{ .ty = Air.Inst.Ref.anyerror_type, - .operand = op, + .operand = operand, } }, }); } diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index da6da9c877..142611d083 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -570,7 +570,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_gte => try self.airCmp(inst, .gte), .cmp_gt => try self.airCmp(inst, .gt), .cmp_neq => try self.airCmp(inst, .neq), + .cmp_vector => try self.airCmpVector(inst), + .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), .bool_and => try self.airBinOp(inst), .bool_or => try self.airBinOp(inst), @@ -2660,6 +2662,14 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); } +fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + _ = operand; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 15d0e47d4a..6e03dbf4fa 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -567,7 +567,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_gte => try self.airCmp(inst, .gte), .cmp_gt => try self.airCmp(inst, .gt), .cmp_neq => try self.airCmp(inst, .neq), + .cmp_vector => try self.airCmpVector(inst), + .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), .bool_and => try self.airBinOp(inst), .bool_or => try self.airBinOp(inst), @@ -3063,6 +3065,14 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); } +fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + _ = operand; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index fa243819cf..2c8374fca1 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -537,7 +537,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_gte => try self.airCmp(inst, .gte), .cmp_gt => try self.airCmp(inst, .gt), .cmp_neq => try self.airCmp(inst, .neq), + .cmp_vector => try self.airCmpVector(inst), + .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), .bool_and => try self.airBoolOp(inst), .bool_or => try self.airBoolOp(inst), @@ -1799,6 +1801,14 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); } +fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + _ = operand; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index d43e8758a1..024a94aaf4 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1319,7 +1319,9 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .cmp_lte => self.airCmp(inst, .lte), .cmp_lt => self.airCmp(inst, .lt), .cmp_neq => self.airCmp(inst, .neq), + .cmp_vector => self.airCmpVector(inst), + .cmp_lt_errors_len => self.airCmpLtErrorsLen(inst), .array_elem_val => self.airArrayElemVal(inst), .array_to_slice => self.airArrayToSlice(inst), @@ -2267,6 +2269,16 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return self.fail("TODO implement airCmpVector for wasm", .{}); } +fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + + _ = operand; + return self.fail("TODO implement airCmpLtErrorsLen for wasm", .{}); +} + fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const br = self.air.instructions.items(.data)[inst].br; const block = self.blocks.get(br.block_inst).?; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index bc0171899a..5d8864f1a2 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -693,7 +693,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_gte => try self.airCmp(inst, .gte), .cmp_gt => try self.airCmp(inst, .gt), .cmp_neq => try self.airCmp(inst, .neq), + .cmp_vector => try self.airCmpVector(inst), + .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), .bool_and => try self.airBoolOp(inst), .bool_or => try self.airBoolOp(inst), @@ -3818,6 +3820,14 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); } +fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + _ = operand; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; const payload = try self.addExtra(Mir.DbgLineColumn{ diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 38a105c172..19556fe8c4 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1767,7 +1767,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .cmp_eq => try airEquality(f, inst, "((", "=="), .cmp_neq => try airEquality(f, inst, "!((", "!="), - .cmp_vector => return f.fail("TODO: C backend: implement binary op for tag '{s}'", .{@tagName(Air.Inst.Tag.cmp_vector)}), + .cmp_vector => return f.fail("TODO: C backend: implement cmp_vector", .{}), + .cmp_lt_errors_len => return f.fail("TODO: C backend: implement cmp_lt_errors_len", .{}), // bool_and and bool_or are non-short-circuit operations .bool_and => try airBinOp(f, inst, " & "), diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 74de485721..af68255fe8 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -440,8 +440,38 @@ pub const Object = struct { error_name_table_ptr_global.setInitializer(error_name_table_ptr); } + fn genCmpLtErrorsLenFunction(object: *Object, comp: *Compilation) !void { + // If there is no such function in the module, it means the source code does not need it. + const llvm_fn = object.llvm_module.getNamedFunction(lt_errors_fn_name) orelse return; + const mod = comp.bin_file.options.module.?; + const errors_len = mod.global_error_set.count(); + + // Delete previous implementation. We replace it with every flush() because the + // total number of errors may have changed. + while (llvm_fn.getFirstBasicBlock()) |bb| { + bb.deleteBasicBlock(); + } + + const builder = object.context.createBuilder(); + + const entry_block = object.context.appendBasicBlock(llvm_fn, "Entry"); + builder.positionBuilderAtEnd(entry_block); + builder.clearCurrentDebugLocation(); + + // Example source of the following LLVM IR: + // fn __zig_lt_errors_len(index: u16) bool { + // return index < total_errors_len; + // } + + const lhs = llvm_fn.getParam(0); + const rhs = lhs.typeOf().constInt(errors_len, .False); + const is_lt = builder.buildICmp(.ULT, lhs, rhs, ""); + _ = builder.buildRet(is_lt); + } + pub fn flushModule(self: *Object, comp: *Compilation) !void { try self.genErrorNameTable(comp); + try self.genCmpLtErrorsLenFunction(comp); if (self.di_builder) |dib| { // When lowering debug info for pointers, we emitted the element types as @@ -3457,7 +3487,9 @@ pub const FuncGen = struct { .cmp_lt => try self.airCmp(inst, .lt), .cmp_lte => try self.airCmp(inst, .lte), .cmp_neq => try self.airCmp(inst, .neq), + .cmp_vector => try self.airCmpVector(inst), + .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), .is_non_null => try self.airIsNonNull(inst, false, false, .NE), .is_non_null_ptr => try self.airIsNonNull(inst, true , false, .NE), @@ -3738,6 +3770,16 @@ pub const FuncGen = struct { return self.cmp(lhs, rhs, vec_ty, cmp_op); } + fn airCmpLtErrorsLen(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) return null; + + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + const llvm_fn = try self.getCmpLtErrorsLenFunction(); + const args: [1]*const llvm.Value = .{operand}; + return self.builder.buildCall(llvm_fn, &args, args.len, .Fast, .Auto, ""); + } + fn cmp( self: *FuncGen, lhs: *const llvm.Value, @@ -6392,6 +6434,25 @@ pub const FuncGen = struct { return fn_val; } + fn getCmpLtErrorsLenFunction(self: *FuncGen) !*const llvm.Value { + if (self.dg.object.llvm_module.getNamedFunction(lt_errors_fn_name)) |llvm_fn| { + return llvm_fn; + } + + // Function signature: fn (anyerror) bool + + const ret_llvm_ty = try self.dg.llvmType(Type.bool); + const anyerror_llvm_ty = try self.dg.llvmType(Type.anyerror); + const param_types = [_]*const llvm.Type{anyerror_llvm_ty}; + + const fn_type = llvm.functionType(ret_llvm_ty, ¶m_types, param_types.len, .False); + const llvm_fn = self.dg.object.llvm_module.addFunction(lt_errors_fn_name, fn_type); + llvm_fn.setLinkage(.Internal); + llvm_fn.setFunctionCallConv(.Fast); + self.dg.addCommonFnAttributes(llvm_fn); + return llvm_fn; + } + fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; @@ -7663,3 +7724,5 @@ const AnnotatedDITypePtr = enum(usize) { return @truncate(u1, @enumToInt(self)) != 0; } }; + +const lt_errors_fn_name = "__zig_lt_errors_len"; diff --git a/src/print_air.zig b/src/print_air.zig index 6552b54faf..69d7b63b2a 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -166,6 +166,7 @@ const Writer = struct { .ceil, .round, .trunc_float, + .cmp_lt_errors_len, => try w.writeUnOp(s, inst), .breakpoint, diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 8a4bec935e..4e952828c5 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -360,7 +360,11 @@ test "expected [*c]const u8, found [*:0]const u8" { } test "explicit cast from integer to error type" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO try testCastIntToErr(error.ItBroke); comptime try testCastIntToErr(error.ItBroke); From c21f046a8b019c42aa0dbb0fb9c592b590edf977 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 Mar 2022 00:47:55 -0700 Subject: [PATCH 1001/2031] Sema: enhance is_non_err to be comptime more often * Sema: store the precomputed monomorphed_funcs hash inside Module.Fn. This is important because it may be accessed when resizing monomorphed_funcs while this Fn has already been added to the set, but does not have the owner_decl, comptime_args, or other fields populated yet. * Sema: in `analyzeIsNonErr`, take advantage of the AIR tag being `wrap_errunion_payload` to infer that `is_non_err` is comptime true without performing any error set resolution. - Also add some code to check for empty inferred error sets in this function. If necessary we do resolve the inferred error set. * Sema: queue full type resolution of payload type when `wrap_errunion_payload` AIR instruction is emitted. This ensures the backend may check the alignment of it. * Sema: resolveTypeFully now additionally resolves comptime-only status. closes #11306 --- src/Module.zig | 29 +++++------------- src/Sema.zig | 64 ++++++++++++++++++++++++--------------- src/arch/wasm/CodeGen.zig | 8 ++--- test/behavior/error.zig | 7 +++++ 4 files changed, 58 insertions(+), 50 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index ce93e091fd..3bc763103b 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -146,8 +146,6 @@ const MonomorphedFuncsSet = std.HashMapUnmanaged( ); const MonomorphedFuncsContext = struct { - target: Target, - pub fn eql(ctx: @This(), a: *Fn, b: *Fn) bool { _ = ctx; return a == b; @@ -155,25 +153,8 @@ const MonomorphedFuncsContext = struct { /// Must match `Sema.GenericCallAdapter.hash`. pub fn hash(ctx: @This(), key: *Fn) u64 { - var hasher = std.hash.Wyhash.init(0); - - // The generic function Decl is guaranteed to be the first dependency - // of each of its instantiations. - const generic_owner_decl = key.owner_decl.dependencies.keys()[0]; - const generic_func: *const Fn = generic_owner_decl.val.castTag(.function).?.data; - std.hash.autoHash(&hasher, generic_func); - - // This logic must be kept in sync with the logic in `analyzeCall` that - // computes the hash. - const comptime_args = key.comptime_args.?; - const generic_ty_info = generic_owner_decl.ty.fnInfo(); - for (generic_ty_info.param_types) |param_ty, i| { - if (generic_ty_info.paramIsComptime(i) and param_ty.tag() != .generic_poison) { - comptime_args[i].val.hash(param_ty, &hasher, ctx.target); - } - } - - return hasher.final(); + _ = ctx; + return key.hash; } }; @@ -1427,6 +1408,12 @@ pub const Fn = struct { /// determine param names rather than redundantly storing them here. param_names: []const [:0]const u8, + /// Precomputed hash for monomorphed_funcs. + /// This is important because it may be accessed when resizing monomorphed_funcs + /// while this Fn has already been added to the set, but does not have the + /// owner_decl, comptime_args, or other fields populated yet. + hash: u64, + /// Relative to owner Decl. lbrace_line: u32, /// Relative to owner Decl. diff --git a/src/Sema.zig b/src/Sema.zig index 7ad90cdf09..9fc54f805b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4671,22 +4671,6 @@ const GenericCallAdapter = struct { } }; -const GenericRemoveAdapter = struct { - precomputed_hash: u64, - - pub fn eql(ctx: @This(), adapted_key: *Module.Fn, other_key: *Module.Fn) bool { - _ = ctx; - return adapted_key == other_key; - } - - /// The implementation of the hash is in semantic analysis of function calls, so - /// that any errors when computing the hash can be properly reported. - pub fn hash(ctx: @This(), adapted_key: *Module.Fn) u64 { - _ = adapted_key; - return ctx.precomputed_hash; - } -}; - fn analyzeCall( sema: *Sema, block: *Block, @@ -5200,15 +5184,15 @@ fn instantiateGenericCall( .comptime_tvs = comptime_tvs, .target = target, }; - const gop = try mod.monomorphed_funcs.getOrPutContextAdapted(gpa, {}, adapter, .{ .target = target }); + const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter); const callee = if (!gop.found_existing) callee: { const new_module_func = try gpa.create(Module.Fn); + // This ensures that we can operate on the hash map before the Module.Fn + // struct is fully initialized. + new_module_func.hash = precomputed_hash; gop.key_ptr.* = new_module_func; errdefer gpa.destroy(new_module_func); - const remove_adapter: GenericRemoveAdapter = .{ - .precomputed_hash = precomputed_hash, - }; - errdefer assert(mod.monomorphed_funcs.removeAdapted(new_module_func, remove_adapter)); + errdefer assert(mod.monomorphed_funcs.remove(new_module_func)); try namespace.anon_decls.ensureUnusedCapacity(gpa, 1); @@ -6494,12 +6478,14 @@ fn funcCommon( param_name.* = try sema.gpa.dupeZ(u8, block.params.items[i].name); } + const hash = new_func.hash; const fn_payload = try sema.arena.create(Value.Payload.Function); new_func.* = .{ .state = anal_state, .zir_body_inst = func_inst, .owner_decl = sema.owner_decl, .comptime_args = comptime_args, + .hash = hash, .lbrace_line = src_locs.lbrace_line, .rbrace_line = src_locs.rbrace_line, .lbrace_column = @truncate(u16, src_locs.columns), @@ -19987,18 +19973,39 @@ fn analyzeIsNonErr( if (ot == .ErrorSet) return Air.Inst.Ref.bool_false; assert(ot == .ErrorUnion); + if (Air.refToIndex(operand)) |operand_inst| { + const air_tags = sema.air_instructions.items(.tag); + if (air_tags[operand_inst] == .wrap_errunion_payload) { + return Air.Inst.Ref.bool_true; + } + } + + const maybe_operand_val = try sema.resolveMaybeUndefVal(block, src, operand); + // exception if the error union error set is known to be empty, // we allow the comparison but always make it comptime known. const set_ty = operand_ty.errorUnionSet(); switch (set_ty.tag()) { - .anyerror, .error_set_inferred => {}, + .anyerror => {}, + .error_set_inferred => blk: { + // If the error set is empty, we must return a comptime true or false. + // However we want to avoid unnecessarily resolving an inferred error set + // in case it is already non-empty. + const ies = set_ty.castTag(.error_set_inferred).?.data; + if (ies.is_anyerror) break :blk; + if (ies.errors.count() != 0) break :blk; + if (maybe_operand_val == null) { + try sema.resolveInferredErrorSet(block, src, ies); + if (ies.is_anyerror) break :blk; + if (ies.errors.count() == 0) return Air.Inst.Ref.bool_true; + } + }, else => if (set_ty.errorSetNames().len == 0) return Air.Inst.Ref.bool_true, } - const result_ty = Type.bool; - if (try sema.resolveMaybeUndefVal(block, src, operand)) |err_union| { + if (maybe_operand_val) |err_union| { if (err_union.isUndef()) { - return sema.addConstUndef(result_ty); + return sema.addConstUndef(Type.bool); } if (err_union.getError() == null) { return Air.Inst.Ref.bool_true; @@ -20583,6 +20590,7 @@ fn wrapErrorUnionPayload( return sema.addConstant(dest_ty, try Value.Tag.eu_payload.create(sema.arena, val)); } try sema.requireRuntimeBlock(block, inst_src); + try sema.queueFullTypeResolution(dest_payload_ty); return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced); } @@ -21372,6 +21380,9 @@ fn resolveStructFully( try sema.resolveTypeFully(block, src, field.ty); } struct_obj.status = .fully_resolved; + + // And let's not forget comptime-only status. + _ = try sema.typeRequiresComptime(block, src, ty); } fn resolveUnionFully( @@ -21395,6 +21406,9 @@ fn resolveUnionFully( try sema.resolveTypeFully(block, src, field.ty); } union_obj.status = .fully_resolved; + + // And let's not forget comptime-only status. + _ = try sema.typeRequiresComptime(block, src, ty); } pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!Type { diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 024a94aaf4..5a191c5276 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2627,12 +2627,12 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const op_ty = self.air.typeOf(ty_op.operand); if (!op_ty.hasRuntimeBitsIgnoreComptime()) return operand; - const err_ty = self.air.getRefType(ty_op.ty); - const err_align = err_ty.abiAlignment(self.target); - const set_size = err_ty.errorUnionSet().abiSize(self.target); + const err_union_ty = self.air.getRefType(ty_op.ty); + const err_align = err_union_ty.abiAlignment(self.target); + const set_size = err_union_ty.errorUnionSet().abiSize(self.target); const offset = mem.alignForwardGeneric(u64, set_size, err_align); - const err_union = try self.allocStack(err_ty); + const err_union = try self.allocStack(err_union_ty); const payload_ptr = try self.buildPointerOffset(err_union, offset, .new); try self.store(payload_ptr, operand, op_ty, 0); diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 11146ac9ca..3030ea67ed 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -251,6 +251,13 @@ fn testErrToIntWithOnePossibleValue( } } +test "inferred empty error set comptime catch" { + const S = struct { + fn foo() !void {} + }; + S.foo() catch @compileError("fail"); +} + test "error union peer type resolution" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO From 5d5282b5f18b33489187a9a9e23a1aa2d301b4e8 Mon Sep 17 00:00:00 2001 From: Daniele Cocca Date: Sat, 19 Mar 2022 12:00:07 +0000 Subject: [PATCH 1002/2031] AstGen: support local var references for outputs --- src/AstGen.zig | 50 ++++++++++++++++---------------------------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index ed6c9f86ce..296eb80e7c 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -6423,7 +6423,6 @@ fn identifier( const astgen = gz.astgen; const tree = astgen.tree; - const gpa = astgen.gpa; const main_tokens = tree.nodes.items(.main_token); const ident_token = main_tokens[ident]; @@ -6467,6 +6466,19 @@ fn identifier( } // Local variables, including function parameters. + return localVarRef(gz, scope, rl, ident, ident_token); +} + +fn localVarRef( + gz: *GenZir, + scope: *Scope, + rl: ResultLoc, + ident: Ast.Node.Index, + ident_token: Ast.Node.Index, +) InnerError!Zir.Inst.Ref { + const astgen = gz.astgen; + const gpa = astgen.gpa; + const name_str_index = try astgen.identAsString(ident_token); var s = scope; var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already @@ -6808,43 +6820,13 @@ fn asmExpr( }; } else { const ident_token = symbolic_name + 4; - const str_index = try astgen.identAsString(ident_token); - // TODO this needs extra code for local variables. Have a look at #215 and related - // issues and decide how to handle outputs. Do we want this to be identifiers? + // TODO have a look at #215 and related issues and decide how to + // handle outputs. Do we want this to be identifiers? // Or maybe we want to force this to be expressions with a pointer type. - // Until that is figured out this is only hooked up for referencing Decls. - // TODO we have put this as an identifier lookup just so that we don't get - // unused vars for outputs. We need to check if this is correct in the future ^^ - // so we just put in this simple lookup. This is a workaround. - { - var s = scope; - while (true) switch (s.tag) { - .local_val => { - const local_val = s.cast(Scope.LocalVal).?; - if (local_val.name == str_index) { - local_val.used = true; - break; - } - s = local_val.parent; - }, - .local_ptr => { - const local_ptr = s.cast(Scope.LocalPtr).?; - if (local_ptr.name == str_index) { - local_ptr.used = true; - break; - } - s = local_ptr.parent; - }, - .gen_zir => s = s.cast(GenZir).?.parent, - .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, - .namespace, .top => break, - }; - } - const operand = try gz.addStrTok(.decl_ref, str_index, ident_token); outputs[i] = .{ .name = name, .constraint = constraint, - .operand = operand, + .operand = try localVarRef(gz, scope, rl, node, ident_token), }; } } From 633fe41a2c2310d8cfab53ee9d87bb50ac4efc41 Mon Sep 17 00:00:00 2001 From: Daniele Cocca Date: Sun, 20 Mar 2022 20:50:59 +0000 Subject: [PATCH 1003/2031] Sema: allow comptime blocks for global assembly An assembly expression in a comptime block is legal Zig in the case of global assembly [^1]. Instead of unconditionally asserting that the expression lives in a runtime block, here we assert that if the expression lives in a comptime block it must be outside of function scope. [^1]: https://ziglang.org/documentation/0.9.1/#Global-Assembly --- src/Sema.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 9fc54f805b..f13f97bbbb 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -10253,6 +10253,11 @@ fn zirAsm( const inputs_len = @truncate(u5, extended.small >> 5); const clobbers_len = @truncate(u5, extended.small >> 10); const is_volatile = @truncate(u1, extended.small >> 15) != 0; + const is_global_assembly = sema.func == null; + + if (block.is_comptime and !is_global_assembly) { + try sema.requireRuntimeBlock(block, src); + } if (extra.data.asm_source == 0) { // This can move to become an AstGen error after inline assembly improvements land @@ -10317,7 +10322,6 @@ fn zirAsm( needed_capacity += (asm_source.len + 3) / 4; const gpa = sema.gpa; - try sema.requireRuntimeBlock(block, src); try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity); const asm_air = try block.addInst(.{ .tag = .assembly, From ebafdb958c1aa6b41c28fc7d45f44e38a69a3bd5 Mon Sep 17 00:00:00 2001 From: Daniele Cocca Date: Sun, 20 Mar 2022 20:57:56 +0000 Subject: [PATCH 1004/2031] AstGen: don't coerce inputs to usize in asmExpr Instead, use ResultLoc.none to allow for the expression type to be inferred [^1]. This effectively moves the type coercion to Sema, in order to turn comptime values into usable values for the backends to consume. Right now the coercion is applies as comptime_int -> usize and comptime_float -> f64, as an arbitrary choice. [^1]: https://github.com/ziglang/zig/blob/9f25c8140cb859fcea7023362afcb29b1e4df41f/src/AstGen.zig#L207-L208 --- src/AstGen.zig | 2 +- src/Sema.zig | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 296eb80e7c..b66f7b868b 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -6842,7 +6842,7 @@ fn asmExpr( const name = try astgen.identAsString(symbolic_name); const constraint_token = symbolic_name + 2; const constraint = (try astgen.strLitAsString(constraint_token)).index; - const operand = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[input_node].lhs); + const operand = try expr(gz, scope, .none, node_datas[input_node].lhs); inputs[i] = .{ .name = name, .constraint = constraint, diff --git a/src/Sema.zig b/src/Sema.zig index f13f97bbbb..112939c995 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -10304,7 +10304,14 @@ fn zirAsm( const name = sema.code.nullTerminatedString(input.data.name); _ = name; // TODO: use the name - arg.* = sema.resolveInst(input.data.operand); + const uncasted_arg = sema.resolveInst(input.data.operand); + const uncasted_arg_ty = sema.typeOf(uncasted_arg); + switch (uncasted_arg_ty.zigTypeTag()) { + .ComptimeInt => arg.* = try sema.coerce(block, Type.initTag(.usize), uncasted_arg, src), + .ComptimeFloat => arg.* = try sema.coerce(block, Type.initTag(.f64), uncasted_arg, src), + else => arg.* = uncasted_arg, + } + const constraint = sema.code.nullTerminatedString(input.data.constraint); needed_capacity += constraint.len / 4 + 1; inputs[arg_i] = constraint; From 907dc1e13f657f349dbdecf739b2f1a13ad7011a Mon Sep 17 00:00:00 2001 From: Daniele Cocca Date: Sun, 20 Mar 2022 21:04:28 +0000 Subject: [PATCH 1005/2031] CBE: improve support for asm inputs This is not complete support for asm expressions, but allows a few more test cases from test/behavior/asm.zig to pass. Since the non-register inputs are named `input_${n}` they can cause name collisions: I'm wrapping the asm expressions in their own block to prevent that. Contextually, this change also makes test/behavior/asm.zig run for stage2, but skips individual tests for most backends (I only verified the C and LLVM backends successfully run one new test case) and the entire test file for aarch64, where it's running into preexisting shortcomings. --- src/codegen/c.zig | 18 +++++++++++++----- test/behavior.zig | 5 +---- test/behavior/asm.zig | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 19556fe8c4..4085305941 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -3014,9 +3014,10 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { } else null; const writer = f.object.writer(); - const inputs_extra_begin = extra_i; + try writer.writeAll("{\n"); - for (inputs) |input| { + const inputs_extra_begin = extra_i; + for (inputs) |input, i| { const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. @@ -3032,7 +3033,11 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, arg_c_value); try writer.writeAll(";\n"); } else { - return f.fail("TODO non-explicit inline asm regs", .{}); + try writer.writeAll("register "); + try f.renderType(writer, f.air.typeOf(input)); + try writer.print(" input_{d} = ", .{i}); + try f.writeCValue(writer, try f.resolveInst(input)); + try writer.writeAll(";\n"); } } @@ -3074,12 +3079,15 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { } try writer.print("\"r\"({s}_constant)", .{reg}); } else { - // This is blocked by the earlier test - unreachable; + if (index > 0) { + try writer.writeAll(", "); + } + try writer.print("\"r\"(input_{d})", .{index}); } } } try writer.writeAll(");\n"); + try writer.writeAll("}\n"); if (f.liveness.isUnused(inst)) return CValue.none; diff --git a/test/behavior.zig b/test/behavior.zig index 60b8d2bae7..de61efa482 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -165,10 +165,7 @@ test { } if (builtin.os.tag != .wasi) { - if (builtin.zig_backend == .stage1) { - // TODO get these tests passing with stage2 - _ = @import("behavior/asm.zig"); - } + _ = @import("behavior/asm.zig"); } if (builtin.zig_backend != .stage2_arm and diff --git a/test/behavior/asm.zig b/test/behavior/asm.zig index f856f0128e..dab6f12127 100644 --- a/test/behavior/asm.zig +++ b/test/behavior/asm.zig @@ -5,7 +5,10 @@ const expect = std.testing.expect; const is_x86_64_linux = builtin.cpu.arch == .x86_64 and builtin.os.tag == .linux; comptime { - if (is_x86_64_linux) { + if (builtin.zig_backend != .stage2_arm and + builtin.zig_backend != .stage2_aarch64 and + is_x86_64_linux) + { asm ( \\.globl this_is_my_alias; \\.type this_is_my_alias, @function; @@ -15,12 +18,26 @@ comptime { } test "module level assembly" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO + if (is_x86_64_linux) { try expect(this_is_my_alias() == 1234); } } test "output constraint modifiers" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO + // This is only testing compilation. var a: u32 = 3; asm volatile ("" @@ -36,6 +53,13 @@ test "output constraint modifiers" { } test "alternative constraints" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO + // Make sure we allow commas as a separator for alternative constraints. var a: u32 = 3; asm volatile ("" @@ -46,6 +70,11 @@ test "alternative constraints" { } test "sized integer/float in asm input" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + asm volatile ("" : : [_] "m" (@as(usize, 3)), @@ -89,6 +118,13 @@ test "sized integer/float in asm input" { } test "struct/array/union types as input values" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO + asm volatile ("" : : [_] "m" (@as([1]u32, undefined)), From b73cf97c93bb23150475ac0c23aadf86dbd71bc4 Mon Sep 17 00:00:00 2001 From: Meghan Date: Wed, 30 Mar 2022 11:12:14 -0700 Subject: [PATCH 1006/2031] replace other uses of `std.meta.Vector` with `@Vector` (#11346) --- lib/std/crypto/aes/aesni.zig | 4 +- lib/std/crypto/aes/armcrypto.zig | 8 ++- lib/std/crypto/chacha20.zig | 3 +- lib/std/crypto/ghash.zig | 14 +++-- lib/std/crypto/gimli.zig | 3 +- lib/std/crypto/salsa20.zig | 5 +- lib/std/crypto/utils.zig | 10 ++-- lib/std/fmt.zig | 6 +-- lib/std/math.zig | 20 +++---- lib/std/simd.zig | 74 +++++++++++++------------- lib/std/special/compiler_rt/multi3.zig | 2 +- lib/std/target.zig | 8 ++- test/behavior/floatop.zig | 25 +++++---- test/behavior/math.zig | 8 +-- test/behavior/type.zig | 6 +-- test/behavior/type_info.zig | 2 +- test/runtime_safety.zig | 40 +++++++------- 17 files changed, 112 insertions(+), 126 deletions(-) diff --git a/lib/std/crypto/aes/aesni.zig b/lib/std/crypto/aes/aesni.zig index b76e01e70b..27bda52c7b 100644 --- a/lib/std/crypto/aes/aesni.zig +++ b/lib/std/crypto/aes/aesni.zig @@ -2,9 +2,7 @@ const std = @import("../../std.zig"); const builtin = @import("builtin"); const mem = std.mem; const debug = std.debug; -const Vector = std.meta.Vector; - -const BlockVec = Vector(2, u64); +const BlockVec = @Vector(2, u64); /// A single AES block. pub const Block = struct { diff --git a/lib/std/crypto/aes/armcrypto.zig b/lib/std/crypto/aes/armcrypto.zig index 6d3c1e8ddf..b3ff7ff217 100644 --- a/lib/std/crypto/aes/armcrypto.zig +++ b/lib/std/crypto/aes/armcrypto.zig @@ -1,9 +1,7 @@ const std = @import("../../std.zig"); const mem = std.mem; const debug = std.debug; -const Vector = std.meta.Vector; - -const BlockVec = Vector(2, u64); +const BlockVec = @Vector(2, u64); /// A single AES block. pub const Block = struct { @@ -29,7 +27,7 @@ pub const Block = struct { return mem.toBytes(x); } - const zero = Vector(2, u64){ 0, 0 }; + const zero = @Vector(2, u64){ 0, 0 }; /// Encrypt a block with a round key. pub inline fn encrypt(block: Block, round_key: Block) Block { @@ -182,7 +180,7 @@ fn KeySchedule(comptime Aes: type) type { return struct { const Self = @This(); - const zero = Vector(2, u64){ 0, 0 }; + const zero = @Vector(2, u64){ 0, 0 }; const mask1 = @Vector(16, u8){ 13, 14, 15, 12, 13, 14, 15, 12, 13, 14, 15, 12, 13, 14, 15, 12 }; const mask2 = @Vector(16, u8){ 12, 13, 14, 15, 12, 13, 14, 15, 12, 13, 14, 15, 12, 13, 14, 15 }; diff --git a/lib/std/crypto/chacha20.zig b/lib/std/crypto/chacha20.zig index 8655121ce0..2a43f4b94c 100644 --- a/lib/std/crypto/chacha20.zig +++ b/lib/std/crypto/chacha20.zig @@ -7,7 +7,6 @@ const mem = std.mem; const assert = std.debug.assert; const testing = std.testing; const maxInt = math.maxInt; -const Vector = std.meta.Vector; const Poly1305 = std.crypto.onetimeauth.Poly1305; const AuthenticationError = std.crypto.errors.AuthenticationError; @@ -79,7 +78,7 @@ pub const XChaCha8Poly1305 = XChaChaPoly1305(8); // Vectorized implementation of the core function fn ChaChaVecImpl(comptime rounds_nb: usize) type { return struct { - const Lane = Vector(4, u32); + const Lane = @Vector(4, u32); const BlockVec = [4]Lane; fn initContext(key: [8]u32, d: [4]u32) BlockVec { diff --git a/lib/std/crypto/ghash.zig b/lib/std/crypto/ghash.zig index c24c16ce77..f3fac0038e 100644 --- a/lib/std/crypto/ghash.zig +++ b/lib/std/crypto/ghash.zig @@ -92,23 +92,21 @@ pub const Ghash = struct { } inline fn clmul_pclmul(x: u64, y: u64) u64 { - const Vector = std.meta.Vector; const product = asm ( \\ vpclmulqdq $0x00, %[x], %[y], %[out] - : [out] "=x" (-> Vector(2, u64)), - : [x] "x" (@bitCast(Vector(2, u64), @as(u128, x))), - [y] "x" (@bitCast(Vector(2, u64), @as(u128, y))), + : [out] "=x" (-> @Vector(2, u64)), + : [x] "x" (@bitCast(@Vector(2, u64), @as(u128, x))), + [y] "x" (@bitCast(@Vector(2, u64), @as(u128, y))), ); return product[0]; } inline fn clmul_pmull(x: u64, y: u64) u64 { - const Vector = std.meta.Vector; const product = asm ( \\ pmull %[out].1q, %[x].1d, %[y].1d - : [out] "=w" (-> Vector(2, u64)), - : [x] "w" (@bitCast(Vector(2, u64), @as(u128, x))), - [y] "w" (@bitCast(Vector(2, u64), @as(u128, y))), + : [out] "=w" (-> @Vector(2, u64)), + : [x] "w" (@bitCast(@Vector(2, u64), @as(u128, x))), + [y] "w" (@bitCast(@Vector(2, u64), @as(u128, y))), ); return product[0]; } diff --git a/lib/std/crypto/gimli.zig b/lib/std/crypto/gimli.zig index 3e2fad4229..96a7d69e6f 100644 --- a/lib/std/crypto/gimli.zig +++ b/lib/std/crypto/gimli.zig @@ -15,7 +15,6 @@ const debug = std.debug; const assert = std.debug.assert; const testing = std.testing; const htest = @import("test.zig"); -const Vector = std.meta.Vector; const AuthenticationError = std.crypto.errors.AuthenticationError; pub const State = struct { @@ -111,7 +110,7 @@ pub const State = struct { self.endianSwap(); } - const Lane = Vector(4, u32); + const Lane = @Vector(4, u32); inline fn shift(x: Lane, comptime n: comptime_int) Lane { return x << @splat(4, @as(u5, n)); diff --git a/lib/std/crypto/salsa20.zig b/lib/std/crypto/salsa20.zig index 0e7d67b78f..7477b7ad69 100644 --- a/lib/std/crypto/salsa20.zig +++ b/lib/std/crypto/salsa20.zig @@ -5,7 +5,6 @@ const debug = std.debug; const math = std.math; const mem = std.mem; const utils = std.crypto.utils; -const Vector = std.meta.Vector; const Poly1305 = crypto.onetimeauth.Poly1305; const Blake2b = crypto.hash.blake2.Blake2b; @@ -16,8 +15,8 @@ const IdentityElementError = crypto.errors.IdentityElementError; const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; const Salsa20VecImpl = struct { - const Lane = Vector(4, u32); - const Half = Vector(2, u32); + const Lane = @Vector(4, u32); + const Half = @Vector(2, u32); const BlockVec = [4]Lane; fn initContext(key: [8]u32, d: [4]u32) BlockVec { diff --git a/lib/std/crypto/utils.zig b/lib/std/crypto/utils.zig index 7cdb79d00b..0a3540d895 100644 --- a/lib/std/crypto/utils.zig +++ b/lib/std/crypto/utils.zig @@ -149,11 +149,11 @@ test "crypto.utils.timingSafeEql (vectors)" { var b: [100]u8 = undefined; random.bytes(a[0..]); random.bytes(b[0..]); - const v1: std.meta.Vector(100, u8) = a; - const v2: std.meta.Vector(100, u8) = b; - try testing.expect(!timingSafeEql(std.meta.Vector(100, u8), v1, v2)); - const v3: std.meta.Vector(100, u8) = a; - try testing.expect(timingSafeEql(std.meta.Vector(100, u8), v1, v3)); + const v1: @Vector(100, u8) = a; + const v2: @Vector(100, u8) = b; + try testing.expect(!timingSafeEql(@Vector(100, u8), v1, v2)); + const v3: @Vector(100, u8) = a; + try testing.expect(timingSafeEql(@Vector(100, u8), v1, v3)); } test "crypto.utils.timingSafeCompare" { diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 8ccb337cc4..395c502d61 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -2594,9 +2594,9 @@ test "vector" { return error.SkipZigTest; } - const vbool: std.meta.Vector(4, bool) = [_]bool{ true, false, true, false }; - const vi64: std.meta.Vector(4, i64) = [_]i64{ -2, -1, 0, 1 }; - const vu64: std.meta.Vector(4, u64) = [_]u64{ 1000, 2000, 3000, 4000 }; + const vbool: @Vector(4, bool) = [_]bool{ true, false, true, false }; + const vi64: @Vector(4, i64) = [_]i64{ -2, -1, 0, 1 }; + const vu64: @Vector(4, u64) = [_]u64{ 1000, 2000, 3000, 4000 }; try expectFmt("{ true, false, true, false }", "{}", .{vbool}); try expectFmt("{ -2, -1, 0, 1 }", "{}", .{vi64}); diff --git a/lib/std/math.zig b/lib/std/math.zig index 08019835f7..318d70c726 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -524,9 +524,9 @@ test "shl" { try testing.expect(shl(u8, 0b11111111, 8) == 0); try testing.expect(shl(u8, 0b11111111, 9) == 0); try testing.expect(shl(u8, 0b11111111, -2) == 0b00111111); - try testing.expect(shl(std.meta.Vector(1, u32), std.meta.Vector(1, u32){42}, @as(usize, 1))[0] == @as(u32, 42) << 1); - try testing.expect(shl(std.meta.Vector(1, u32), std.meta.Vector(1, u32){42}, @as(isize, -1))[0] == @as(u32, 42) >> 1); - try testing.expect(shl(std.meta.Vector(1, u32), std.meta.Vector(1, u32){42}, 33)[0] == 0); + try testing.expect(shl(@Vector(1, u32), @Vector(1, u32){42}, @as(usize, 1))[0] == @as(u32, 42) << 1); + try testing.expect(shl(@Vector(1, u32), @Vector(1, u32){42}, @as(isize, -1))[0] == @as(u32, 42) >> 1); + try testing.expect(shl(@Vector(1, u32), @Vector(1, u32){42}, 33)[0] == 0); } /// Shifts right. Overflowed bits are truncated. @@ -564,9 +564,9 @@ test "shr" { try testing.expect(shr(u8, 0b11111111, 8) == 0); try testing.expect(shr(u8, 0b11111111, 9) == 0); try testing.expect(shr(u8, 0b11111111, -2) == 0b11111100); - try testing.expect(shr(std.meta.Vector(1, u32), std.meta.Vector(1, u32){42}, @as(usize, 1))[0] == @as(u32, 42) >> 1); - try testing.expect(shr(std.meta.Vector(1, u32), std.meta.Vector(1, u32){42}, @as(isize, -1))[0] == @as(u32, 42) << 1); - try testing.expect(shr(std.meta.Vector(1, u32), std.meta.Vector(1, u32){42}, 33)[0] == 0); + try testing.expect(shr(@Vector(1, u32), @Vector(1, u32){42}, @as(usize, 1))[0] == @as(u32, 42) >> 1); + try testing.expect(shr(@Vector(1, u32), @Vector(1, u32){42}, @as(isize, -1))[0] == @as(u32, 42) << 1); + try testing.expect(shr(@Vector(1, u32), @Vector(1, u32){42}, 33)[0] == 0); } /// Rotates right. Only unsigned values can be rotated. Negative shift @@ -593,8 +593,8 @@ test "rotr" { try testing.expect(rotr(u8, 0b00000001, @as(usize, 8)) == 0b00000001); try testing.expect(rotr(u8, 0b00000001, @as(usize, 4)) == 0b00010000); try testing.expect(rotr(u8, 0b00000001, @as(isize, -1)) == 0b00000010); - try testing.expect(rotr(std.meta.Vector(1, u32), std.meta.Vector(1, u32){1}, @as(usize, 1))[0] == @as(u32, 1) << 31); - try testing.expect(rotr(std.meta.Vector(1, u32), std.meta.Vector(1, u32){1}, @as(isize, -1))[0] == @as(u32, 1) << 1); + try testing.expect(rotr(@Vector(1, u32), @Vector(1, u32){1}, @as(usize, 1))[0] == @as(u32, 1) << 31); + try testing.expect(rotr(@Vector(1, u32), @Vector(1, u32){1}, @as(isize, -1))[0] == @as(u32, 1) << 1); } /// Rotates left. Only unsigned values can be rotated. Negative shift @@ -621,8 +621,8 @@ test "rotl" { try testing.expect(rotl(u8, 0b00000001, @as(usize, 8)) == 0b00000001); try testing.expect(rotl(u8, 0b00000001, @as(usize, 4)) == 0b00010000); try testing.expect(rotl(u8, 0b00000001, @as(isize, -1)) == 0b10000000); - try testing.expect(rotl(std.meta.Vector(1, u32), std.meta.Vector(1, u32){1 << 31}, @as(usize, 1))[0] == 1); - try testing.expect(rotl(std.meta.Vector(1, u32), std.meta.Vector(1, u32){1 << 31}, @as(isize, -1))[0] == @as(u32, 1) << 30); + try testing.expect(rotl(@Vector(1, u32), @Vector(1, u32){1 << 31}, @as(usize, 1))[0] == 1); + try testing.expect(rotl(@Vector(1, u32), @Vector(1, u32){1 << 31}, @as(isize, -1))[0] == @as(u32, 1) << 30); } /// Returns an unsigned int type that can hold the number of bits in T diff --git a/lib/std/simd.zig b/lib/std/simd.zig index 50e0e99d37..02e08b0dad 100644 --- a/lib/std/simd.zig +++ b/lib/std/simd.zig @@ -6,8 +6,6 @@ const std = @import("std"); const builtin = @import("builtin"); -pub const Vector = std.meta.Vector; - pub fn suggestVectorSizeForCpu(comptime T: type, cpu: std.Target.Cpu) ?usize { switch (cpu.arch) { .x86_64 => { @@ -55,7 +53,7 @@ pub fn VectorCount(comptime VectorType: type) type { /// Returns a vector containing the first `len` integers in order from 0 to `len`-1. /// For example, `iota(i32, 8)` will return a vector containing `.{0, 1, 2, 3, 4, 5, 6, 7}`. -pub fn iota(comptime T: type, comptime len: usize) Vector(len, T) { +pub fn iota(comptime T: type, comptime len: usize) @Vector(len, T) { var out: [len]T = undefined; for (out) |*element, i| { element.* = switch (@typeInfo(T)) { @@ -64,12 +62,12 @@ pub fn iota(comptime T: type, comptime len: usize) Vector(len, T) { else => @compileError("Can't use type " ++ @typeName(T) ++ " in iota."), }; } - return @as(Vector(len, T), out); + return @as(@Vector(len, T), out); } /// Returns a vector containing the same elements as the input, but repeated until the desired length is reached. /// For example, `repeat(8, [_]u32{1, 2, 3})` will return a vector containing `.{1, 2, 3, 1, 2, 3, 1, 2}`. -pub fn repeat(comptime len: usize, vec: anytype) Vector(len, std.meta.Child(@TypeOf(vec))) { +pub fn repeat(comptime len: usize, vec: anytype) @Vector(len, std.meta.Child(@TypeOf(vec))) { const Child = std.meta.Child(@TypeOf(vec)); return @shuffle(Child, vec, undefined, iota(i32, len) % @splat(len, @intCast(i32, vectorLength(@TypeOf(vec))))); @@ -77,7 +75,7 @@ pub fn repeat(comptime len: usize, vec: anytype) Vector(len, std.meta.Child(@Typ /// Returns a vector containing all elements of the first vector at the lower indices followed by all elements of the second vector /// at the higher indices. -pub fn join(a: anytype, b: anytype) Vector(vectorLength(@TypeOf(a)) + vectorLength(@TypeOf(b)), std.meta.Child(@TypeOf(a))) { +pub fn join(a: anytype, b: anytype) @Vector(vectorLength(@TypeOf(a)) + vectorLength(@TypeOf(b)), std.meta.Child(@TypeOf(a))) { const Child = std.meta.Child(@TypeOf(a)); const a_len = vectorLength(@TypeOf(a)); const b_len = vectorLength(@TypeOf(b)); @@ -87,7 +85,7 @@ pub fn join(a: anytype, b: anytype) Vector(vectorLength(@TypeOf(a)) + vectorLeng /// Returns a vector whose elements alternates between those of each input vector. /// For example, `interlace(.{[4]u32{11, 12, 13, 14}, [4]u32{21, 22, 23, 24}})` returns a vector containing `.{11, 21, 12, 22, 13, 23, 14, 24}`. -pub fn interlace(vecs: anytype) Vector(vectorLength(@TypeOf(vecs[0])) * vecs.len, std.meta.Child(@TypeOf(vecs[0]))) { +pub fn interlace(vecs: anytype) @Vector(vectorLength(@TypeOf(vecs[0])) * vecs.len, std.meta.Child(@TypeOf(vecs[0]))) { // interlace doesn't work on MIPS, for some reason. // Notes from earlier debug attempt: // The indices are correct. The problem seems to be with the @shuffle builtin. @@ -128,14 +126,14 @@ pub fn interlace(vecs: anytype) Vector(vectorLength(@TypeOf(vecs[0])) * vecs.len pub fn deinterlace( comptime vec_count: usize, interlaced: anytype, -) [vec_count]Vector( +) [vec_count]@Vector( vectorLength(@TypeOf(interlaced)) / vec_count, std.meta.Child(@TypeOf(interlaced)), ) { const vec_len = vectorLength(@TypeOf(interlaced)) / vec_count; const Child = std.meta.Child(@TypeOf(interlaced)); - var out: [vec_count]Vector(vec_len, Child) = undefined; + var out: [vec_count]@Vector(vec_len, Child) = undefined; comptime var i: usize = 0; // for-loops don't work for this, apparently. inline while (i < out.len) : (i += 1) { @@ -150,7 +148,7 @@ pub fn extract( vec: anytype, comptime first: VectorIndex(@TypeOf(vec)), comptime count: VectorCount(@TypeOf(vec)), -) Vector(count, std.meta.Child(@TypeOf(vec))) { +) @Vector(count, std.meta.Child(@TypeOf(vec))) { const Child = std.meta.Child(@TypeOf(vec)); const len = vectorLength(@TypeOf(vec)); @@ -160,15 +158,15 @@ pub fn extract( } test "vector patterns" { - const base = Vector(4, u32){ 10, 20, 30, 40 }; - const other_base = Vector(4, u32){ 55, 66, 77, 88 }; + const base = @Vector(4, u32){ 10, 20, 30, 40 }; + const other_base = @Vector(4, u32){ 55, 66, 77, 88 }; - const small_bases = [5]Vector(2, u8){ - Vector(2, u8){ 0, 1 }, - Vector(2, u8){ 2, 3 }, - Vector(2, u8){ 4, 5 }, - Vector(2, u8){ 6, 7 }, - Vector(2, u8){ 8, 9 }, + const small_bases = [5]@Vector(2, u8){ + @Vector(2, u8){ 0, 1 }, + @Vector(2, u8){ 2, 3 }, + @Vector(2, u8){ 4, 5 }, + @Vector(2, u8){ 6, 7 }, + @Vector(2, u8){ 8, 9 }, }; try std.testing.expectEqual([6]u32{ 10, 20, 30, 40, 10, 20 }, repeat(6, base)); @@ -228,7 +226,7 @@ pub fn reverseOrder(vec: anytype) @TypeOf(vec) { } test "vector shifting" { - const base = Vector(4, u32){ 10, 20, 30, 40 }; + const base = @Vector(4, u32){ 10, 20, 30, 40 }; try std.testing.expectEqual([4]u32{ 30, 40, 999, 999 }, shiftElementsLeft(base, 2, 999)); try std.testing.expectEqual([4]u32{ 999, 999, 10, 20 }, shiftElementsRight(base, 2, 999)); @@ -286,7 +284,7 @@ pub fn countElementsWithValue(vec: anytype, value: std.meta.Child(@TypeOf(vec))) } test "vector searching" { - const base = Vector(8, u32){ 6, 4, 7, 4, 4, 2, 3, 7 }; + const base = @Vector(8, u32){ 6, 4, 7, 4, 4, 2, 3, 7 }; try std.testing.expectEqual(@as(?u3, 1), firstIndexOfValue(base, 4)); try std.testing.expectEqual(@as(?u3, 4), lastIndexOfValue(base, 4)); @@ -382,28 +380,28 @@ test "vector prefix scan" { return error.SkipZigTest; } - const int_base = Vector(4, i32){ 11, 23, 9, -21 }; - const float_base = Vector(4, f32){ 2, 0.5, -10, 6.54321 }; - const bool_base = Vector(4, bool){ true, false, true, false }; + const int_base = @Vector(4, i32){ 11, 23, 9, -21 }; + const float_base = @Vector(4, f32){ 2, 0.5, -10, 6.54321 }; + const bool_base = @Vector(4, bool){ true, false, true, false }; try std.testing.expectEqual(iota(u8, 32) + @splat(32, @as(u8, 1)), prefixScan(.Add, 1, @splat(32, @as(u8, 1)))); - try std.testing.expectEqual(Vector(4, i32){ 11, 3, 1, 1 }, prefixScan(.And, 1, int_base)); - try std.testing.expectEqual(Vector(4, i32){ 11, 31, 31, -1 }, prefixScan(.Or, 1, int_base)); - try std.testing.expectEqual(Vector(4, i32){ 11, 28, 21, -2 }, prefixScan(.Xor, 1, int_base)); - try std.testing.expectEqual(Vector(4, i32){ 11, 34, 43, 22 }, prefixScan(.Add, 1, int_base)); - try std.testing.expectEqual(Vector(4, i32){ 11, 253, 2277, -47817 }, prefixScan(.Mul, 1, int_base)); - try std.testing.expectEqual(Vector(4, i32){ 11, 11, 9, -21 }, prefixScan(.Min, 1, int_base)); - try std.testing.expectEqual(Vector(4, i32){ 11, 23, 23, 23 }, prefixScan(.Max, 1, int_base)); + try std.testing.expectEqual(@Vector(4, i32){ 11, 3, 1, 1 }, prefixScan(.And, 1, int_base)); + try std.testing.expectEqual(@Vector(4, i32){ 11, 31, 31, -1 }, prefixScan(.Or, 1, int_base)); + try std.testing.expectEqual(@Vector(4, i32){ 11, 28, 21, -2 }, prefixScan(.Xor, 1, int_base)); + try std.testing.expectEqual(@Vector(4, i32){ 11, 34, 43, 22 }, prefixScan(.Add, 1, int_base)); + try std.testing.expectEqual(@Vector(4, i32){ 11, 253, 2277, -47817 }, prefixScan(.Mul, 1, int_base)); + try std.testing.expectEqual(@Vector(4, i32){ 11, 11, 9, -21 }, prefixScan(.Min, 1, int_base)); + try std.testing.expectEqual(@Vector(4, i32){ 11, 23, 23, 23 }, prefixScan(.Max, 1, int_base)); // Trying to predict all inaccuracies when adding and multiplying floats with prefixScans would be a mess, so we don't test those. - try std.testing.expectEqual(Vector(4, f32){ 2, 0.5, -10, -10 }, prefixScan(.Min, 1, float_base)); - try std.testing.expectEqual(Vector(4, f32){ 2, 2, 2, 6.54321 }, prefixScan(.Max, 1, float_base)); + try std.testing.expectEqual(@Vector(4, f32){ 2, 0.5, -10, -10 }, prefixScan(.Min, 1, float_base)); + try std.testing.expectEqual(@Vector(4, f32){ 2, 2, 2, 6.54321 }, prefixScan(.Max, 1, float_base)); - try std.testing.expectEqual(Vector(4, bool){ true, true, false, false }, prefixScan(.Xor, 1, bool_base)); - try std.testing.expectEqual(Vector(4, bool){ true, true, true, true }, prefixScan(.Or, 1, bool_base)); - try std.testing.expectEqual(Vector(4, bool){ true, false, false, false }, prefixScan(.And, 1, bool_base)); + try std.testing.expectEqual(@Vector(4, bool){ true, true, false, false }, prefixScan(.Xor, 1, bool_base)); + try std.testing.expectEqual(@Vector(4, bool){ true, true, true, true }, prefixScan(.Or, 1, bool_base)); + try std.testing.expectEqual(@Vector(4, bool){ true, false, false, false }, prefixScan(.And, 1, bool_base)); - try std.testing.expectEqual(Vector(4, i32){ 11, 23, 20, 2 }, prefixScan(.Add, 2, int_base)); - try std.testing.expectEqual(Vector(4, i32){ 22, 11, -12, -21 }, prefixScan(.Add, -1, int_base)); - try std.testing.expectEqual(Vector(4, i32){ 11, 23, 9, -10 }, prefixScan(.Add, 3, int_base)); + try std.testing.expectEqual(@Vector(4, i32){ 11, 23, 20, 2 }, prefixScan(.Add, 2, int_base)); + try std.testing.expectEqual(@Vector(4, i32){ 22, 11, -12, -21 }, prefixScan(.Add, -1, int_base)); + try std.testing.expectEqual(@Vector(4, i32){ 11, 23, 9, -10 }, prefixScan(.Add, 3, int_base)); } diff --git a/lib/std/special/compiler_rt/multi3.zig b/lib/std/special/compiler_rt/multi3.zig index 4e5c49730a..a088dbcf9e 100644 --- a/lib/std/special/compiler_rt/multi3.zig +++ b/lib/std/special/compiler_rt/multi3.zig @@ -17,7 +17,7 @@ pub fn __multi3(a: i128, b: i128) callconv(.C) i128 { return r.all; } -const v128 = std.meta.Vector(2, u64); +const v128 = @Vector(2, u64); pub fn __multi3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { return @bitCast(v128, @call(.{ .modifier = .always_inline }, __multi3, .{ @bitCast(i128, a), diff --git a/lib/std/target.zig b/lib/std/target.zig index 0b2a4a4df6..45a9c985b5 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -673,8 +673,7 @@ pub const Target = struct { /// Adds the specified feature set but not its dependencies. pub fn addFeatureSet(set: *Set, other_set: Set) void { - set.ints = @as(std.meta.Vector(usize_count, usize), set.ints) | - @as(std.meta.Vector(usize_count, usize), other_set.ints); + set.ints = @as(@Vector(usize_count, usize), set.ints) | @as(@Vector(usize_count, usize), other_set.ints); } /// Removes the specified feature but not its dependents. @@ -686,8 +685,7 @@ pub const Target = struct { /// Removes the specified feature but not its dependents. pub fn removeFeatureSet(set: *Set, other_set: Set) void { - set.ints = @as(std.meta.Vector(usize_count, usize), set.ints) & - ~@as(std.meta.Vector(usize_count, usize), other_set.ints); + set.ints = @as(@Vector(usize_count, usize), set.ints) & ~@as(@Vector(usize_count, usize), other_set.ints); } pub fn populateDependencies(set: *Set, all_features_list: []const Cpu.Feature) void { @@ -716,7 +714,7 @@ pub const Target = struct { } pub fn isSuperSetOf(set: Set, other_set: Set) bool { - const V = std.meta.Vector(usize_count, usize); + const V = @Vector(usize_count, usize); const set_v: V = set.ints; const other_v: V = other_set.ints; return @reduce(.And, (set_v & other_v) == other_v); diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index f8445b6e26..6d8c33efa2 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -4,7 +4,6 @@ const expect = std.testing.expect; const math = std.math; const pi = std.math.pi; const e = std.math.e; -const Vector = std.meta.Vector; const has_f80_rt = switch (builtin.cpu.arch) { .x86_64, .i386 => true, else => false, @@ -120,7 +119,7 @@ fn testSqrt() !void { try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 2.0)), 1.4142135623730950, epsilon)); { - var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 }; + var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 }; var result = @sqrt(v); try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 1.1)), result[0], epsilon)); try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 2.2)), result[1], epsilon)); @@ -200,7 +199,7 @@ fn testSin() !void { } { - var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 }; + var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 }; var result = @sin(v); try expect(math.approxEqAbs(f32, @sin(@as(f32, 1.1)), result[0], epsilon)); try expect(math.approxEqAbs(f32, @sin(@as(f32, 2.2)), result[1], epsilon)); @@ -234,7 +233,7 @@ fn testCos() !void { } { - var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 }; + var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 }; var result = @cos(v); try expect(math.approxEqAbs(f32, @cos(@as(f32, 1.1)), result[0], epsilon)); try expect(math.approxEqAbs(f32, @cos(@as(f32, 2.2)), result[1], epsilon)); @@ -263,7 +262,7 @@ fn testExp() !void { } { - var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; + var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; var result = @exp(v); try expect(math.approxEqAbs(f32, @exp(@as(f32, 1.1)), result[0], epsilon)); try expect(math.approxEqAbs(f32, @exp(@as(f32, 2.2)), result[1], epsilon)); @@ -292,7 +291,7 @@ fn testExp2() !void { } { - var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; + var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; var result = @exp2(v); try expect(math.approxEqAbs(f32, @exp2(@as(f32, 1.1)), result[0], epsilon)); try expect(math.approxEqAbs(f32, @exp2(@as(f32, 2.2)), result[1], epsilon)); @@ -332,7 +331,7 @@ fn testLog() !void { } } -test "@log with vectors" { +test "@log with @vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -369,7 +368,7 @@ fn testLog2() !void { } { - var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; + var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; var result = @log2(v); try expect(@log2(@as(f32, 1.1)) == result[0]); try expect(@log2(@as(f32, 2.2)) == result[1]); @@ -398,7 +397,7 @@ fn testLog10() !void { } { - var v: Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; + var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; var result = @log10(v); try expect(@log10(@as(f32, 1.1)) == result[0]); try expect(@log10(@as(f32, 2.2)) == result[1]); @@ -436,7 +435,7 @@ fn testFabs() !void { // } { - var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 }; + var v: @Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 }; var result = @fabs(v); try expect(math.approxEqAbs(f32, @fabs(@as(f32, 1.1)), result[0], epsilon)); try expect(math.approxEqAbs(f32, @fabs(@as(f32, -2.2)), result[1], epsilon)); @@ -469,7 +468,7 @@ fn testFloor() !void { // } { - var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 }; + var v: @Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 }; var result = @floor(v); try expect(math.approxEqAbs(f32, @floor(@as(f32, 1.1)), result[0], epsilon)); try expect(math.approxEqAbs(f32, @floor(@as(f32, -2.2)), result[1], epsilon)); @@ -502,7 +501,7 @@ fn testCeil() !void { // } { - var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 }; + var v: @Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 }; var result = @ceil(v); try expect(math.approxEqAbs(f32, @ceil(@as(f32, 1.1)), result[0], epsilon)); try expect(math.approxEqAbs(f32, @ceil(@as(f32, -2.2)), result[1], epsilon)); @@ -535,7 +534,7 @@ fn testTrunc() !void { // } { - var v: Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 }; + var v: @Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 }; var result = @trunc(v); try expect(math.approxEqAbs(f32, @trunc(@as(f32, 1.1)), result[0], epsilon)); try expect(math.approxEqAbs(f32, @trunc(@as(f32, -2.2)), result[1], epsilon)); diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 815c6e0efc..a79d20dcdb 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1280,8 +1280,8 @@ test "vector integer addition" { const S = struct { fn doTheTest() !void { - var a: std.meta.Vector(4, i32) = [_]i32{ 1, 2, 3, 4 }; - var b: std.meta.Vector(4, i32) = [_]i32{ 5, 6, 7, 8 }; + var a: @Vector(4, i32) = [_]i32{ 1, 2, 3, 4 }; + var b: @Vector(4, i32) = [_]i32{ 5, 6, 7, 8 }; var result = a + b; var result_array: [4]i32 = result; const expected = [_]i32{ 6, 8, 10, 12 }; @@ -1338,8 +1338,8 @@ test "vector comparison" { const S = struct { fn doTheTest() !void { - var a: std.meta.Vector(6, i32) = [_]i32{ 1, 3, -1, 5, 7, 9 }; - var b: std.meta.Vector(6, i32) = [_]i32{ -1, 3, 0, 6, 10, -10 }; + var a: @Vector(6, i32) = [_]i32{ 1, 3, -1, 5, 7, 9 }; + var b: @Vector(6, i32) = [_]i32{ -1, 3, 0, 6, 10, -10 }; try expect(mem.eql(bool, &@as([6]bool, a < b), &[_]bool{ false, false, true, true, true, false })); try expect(mem.eql(bool, &@as([6]bool, a <= b), &[_]bool{ false, true, true, true, true, false })); try expect(mem.eql(bool, &@as([6]bool, a == b), &[_]bool{ false, true, false, false, false, false })); diff --git a/test/behavior/type.zig b/test/behavior/type.zig index 65bebff946..c543d4f969 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -223,9 +223,9 @@ test "Type.Vector" { @Vector(0, u8), @Vector(4, u8), @Vector(8, *u8), - std.meta.Vector(0, u8), - std.meta.Vector(4, u8), - std.meta.Vector(8, *u8), + @Vector(0, u8), + @Vector(4, u8), + @Vector(8, *u8), }); } diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index 55c6e666ee..32b9671f96 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -447,7 +447,7 @@ test "type info: vectors" { } fn testVector() !void { - const vec_info = @typeInfo(std.meta.Vector(4, i32)); + const vec_info = @typeInfo(@Vector(4, i32)); try expect(vec_info == .Vector); try expect(vec_info.Vector.len == 4); try expect(vec_info.Vector.child == i32); diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 6d68a2e1dd..0a1e7e00f3 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -711,12 +711,12 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ std.os.exit(126); \\} \\pub fn main() void { - \\ var a: std.meta.Vector(4, i32) = [_]i32{ 1, 2, 2147483643, 4 }; - \\ var b: std.meta.Vector(4, i32) = [_]i32{ 5, 6, 7, 8 }; + \\ var a: @Vector(4, i32) = [_]i32{ 1, 2, 2147483643, 4 }; + \\ var b: @Vector(4, i32) = [_]i32{ 5, 6, 7, 8 }; \\ const x = add(a, b); \\ _ = x; \\} - \\fn add(a: std.meta.Vector(4, i32), b: std.meta.Vector(4, i32)) std.meta.Vector(4, i32) { + \\fn add(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) { \\ return a + b; \\} ); @@ -729,12 +729,12 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ std.os.exit(126); \\} \\pub fn main() void { - \\ var a: std.meta.Vector(4, u32) = [_]u32{ 1, 2, 8, 4 }; - \\ var b: std.meta.Vector(4, u32) = [_]u32{ 5, 6, 7, 8 }; + \\ var a: @Vector(4, u32) = [_]u32{ 1, 2, 8, 4 }; + \\ var b: @Vector(4, u32) = [_]u32{ 5, 6, 7, 8 }; \\ const x = sub(b, a); \\ _ = x; \\} - \\fn sub(a: std.meta.Vector(4, u32), b: std.meta.Vector(4, u32)) std.meta.Vector(4, u32) { + \\fn sub(a: @Vector(4, u32), b: @Vector(4, u32)) @Vector(4, u32) { \\ return a - b; \\} ); @@ -747,12 +747,12 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ std.os.exit(126); \\} \\pub fn main() void { - \\ var a: std.meta.Vector(4, u8) = [_]u8{ 1, 2, 200, 4 }; - \\ var b: std.meta.Vector(4, u8) = [_]u8{ 5, 6, 2, 8 }; + \\ var a: @Vector(4, u8) = [_]u8{ 1, 2, 200, 4 }; + \\ var b: @Vector(4, u8) = [_]u8{ 5, 6, 2, 8 }; \\ const x = mul(b, a); \\ _ = x; \\} - \\fn mul(a: std.meta.Vector(4, u8), b: std.meta.Vector(4, u8)) std.meta.Vector(4, u8) { + \\fn mul(a: @Vector(4, u8), b: @Vector(4, u8)) @Vector(4, u8) { \\ return a * b; \\} ); @@ -765,11 +765,11 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ std.os.exit(126); \\} \\pub fn main() void { - \\ var a: std.meta.Vector(4, i16) = [_]i16{ 1, -32768, 200, 4 }; + \\ var a: @Vector(4, i16) = [_]i16{ 1, -32768, 200, 4 }; \\ const x = neg(a); \\ _ = x; \\} - \\fn neg(a: std.meta.Vector(4, i16)) std.meta.Vector(4, i16) { + \\fn neg(a: @Vector(4, i16)) @Vector(4, i16) { \\ return -a; \\} ); @@ -846,12 +846,12 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ std.os.exit(126); \\} \\pub fn main() !void { - \\ var a: std.meta.Vector(4, i16) = [_]i16{ 1, 2, -32768, 4 }; - \\ var b: std.meta.Vector(4, i16) = [_]i16{ 1, 2, -1, 4 }; + \\ var a: @Vector(4, i16) = [_]i16{ 1, 2, -32768, 4 }; + \\ var b: @Vector(4, i16) = [_]i16{ 1, 2, -1, 4 }; \\ const x = div(a, b); \\ if (x[2] == 32767) return error.Whatever; \\} - \\fn div(a: std.meta.Vector(4, i16), b: std.meta.Vector(4, i16)) std.meta.Vector(4, i16) { + \\fn div(a: @Vector(4, i16), b: @Vector(4, i16)) @Vector(4, i16) { \\ return @divTrunc(a, b); \\} ); @@ -944,12 +944,12 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ std.os.exit(126); \\} \\pub fn main() void { - \\ var a: std.meta.Vector(4, i32) = [4]i32{111, 222, 333, 444}; - \\ var b: std.meta.Vector(4, i32) = [4]i32{111, 0, 333, 444}; + \\ var a: @Vector(4, i32) = [4]i32{111, 222, 333, 444}; + \\ var b: @Vector(4, i32) = [4]i32{111, 0, 333, 444}; \\ const x = div0(a, b); \\ _ = x; \\} - \\fn div0(a: std.meta.Vector(4, i32), b: std.meta.Vector(4, i32)) std.meta.Vector(4, i32) { + \\fn div0(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) { \\ return @divTrunc(a, b); \\} ); @@ -978,12 +978,12 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ std.os.exit(126); \\} \\pub fn main() !void { - \\ var a: std.meta.Vector(4, i32) = [4]i32{111, 222, 333, 444}; - \\ var b: std.meta.Vector(4, i32) = [4]i32{111, 222, 333, 441}; + \\ var a: @Vector(4, i32) = [4]i32{111, 222, 333, 444}; + \\ var b: @Vector(4, i32) = [4]i32{111, 222, 333, 441}; \\ const x = divExact(a, b); \\ _ = x; \\} - \\fn divExact(a: std.meta.Vector(4, i32), b: std.meta.Vector(4, i32)) std.meta.Vector(4, i32) { + \\fn divExact(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) { \\ return @divExact(a, b); \\} ); From f5d9160f1b9fcf7b459c6746e8256d349f0c6873 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 30 Mar 2022 11:03:50 +0200 Subject: [PATCH 1007/2031] dwarf: pass DeclState around instead of storing a temp global in Dwarf Avoids many pitfalls connected with premature/early return in case there are errors with Decl, etc. This is effectively bringing back the old design however in a much nicer packaging, where every mechanism related to tracking Decl's debug info is now nicely wrapped in a single struct (aka the `DeclState`). This includes relocation table, type arena, etc. It is now the caller's responsibility to deinit the state (so that no memory is leaked) after `Decl` has been analysed (or errored out). The caller here is typically a linker such as `Elf` or `MachO`. --- src/arch/aarch64/Emit.zig | 6 +- src/arch/arm/Emit.zig | 12 +- src/arch/riscv64/CodeGen.zig | 4 +- src/arch/riscv64/Emit.zig | 6 +- src/arch/x86_64/Emit.zig | 12 +- src/codegen.zig | 2 +- src/link/Dwarf.zig | 963 ++++++++++++++++++----------------- src/link/Elf.zig | 46 +- src/link/MachO.zig | 47 +- 9 files changed, 567 insertions(+), 531 deletions(-) diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 2a4e6cb985..b62f9d8691 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -390,7 +390,7 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { // TODO Look into using the DWARF special opcodes to compress this data. // It lets you emit single-byte opcodes that add different numbers to // both the PC and the line number at the same time. - const dbg_line = dw.getDeclDebugLineBuffer(); + const dbg_line = &dw.dbg_line; try dbg_line.ensureUnusedCapacity(11); dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); leb128.writeULEB128(dbg_line.writer(), delta_pc) catch unreachable; @@ -588,7 +588,7 @@ fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { fn mirDebugPrologueEnd(self: *Emit) !void { switch (self.debug_output) { .dwarf => |dw| { - try dw.getDeclDebugLineBuffer().append(DW.LNS.set_prologue_end); + try dw.dbg_line.append(DW.LNS.set_prologue_end); try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); }, .plan9 => {}, @@ -599,7 +599,7 @@ fn mirDebugPrologueEnd(self: *Emit) !void { fn mirDebugEpilogueBegin(self: *Emit) !void { switch (self.debug_output) { .dwarf => |dw| { - try dw.getDeclDebugLineBuffer().append(DW.LNS.set_epilogue_begin); + try dw.dbg_line.append(DW.LNS.set_epilogue_begin); try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); }, .plan9 => {}, diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index 62705651a6..45bcad485e 100644 --- a/src/arch/arm/Emit.zig +++ b/src/arch/arm/Emit.zig @@ -332,7 +332,7 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { // TODO Look into using the DWARF special opcodes to compress this data. // It lets you emit single-byte opcodes that add different numbers to // both the PC and the line number at the same time. - const dbg_line = dw.getDeclDebugLineBuffer(); + const dbg_line = &dw.dbg_line; try dbg_line.ensureUnusedCapacity(11); dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); leb128.writeULEB128(dbg_line.writer(), delta_pc) catch unreachable; @@ -382,7 +382,7 @@ fn addDbgInfoTypeReloc(self: *Emit, ty: Type) !void { switch (self.debug_output) { .dwarf => |dw| { assert(ty.hasRuntimeBits()); - const dbg_info = dw.getDeclDebugInfoBuffer(); + const dbg_info = &dw.dbg_info; const index = dbg_info.items.len; try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 const atom = switch (self.bin_file.tag) { @@ -409,7 +409,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { .register => |reg| { switch (self.debug_output) { .dwarf => |dw| { - const dbg_info = dw.getDeclDebugInfoBuffer(); + const dbg_info = &dw.dbg_info; try dbg_info.ensureUnusedCapacity(3); dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc @@ -442,7 +442,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { else => unreachable, }; - const dbg_info = dw.getDeclDebugInfoBuffer(); + const dbg_info = &dw.dbg_info; try dbg_info.append(link.File.Dwarf.abbrev_parameter); // Get length of the LEB128 stack offset @@ -560,7 +560,7 @@ fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { fn mirDebugPrologueEnd(emit: *Emit) !void { switch (emit.debug_output) { .dwarf => |dw| { - try dw.getDeclDebugLineBuffer().append(DW.LNS.set_prologue_end); + try dw.dbg_line.append(DW.LNS.set_prologue_end); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, .plan9 => {}, @@ -571,7 +571,7 @@ fn mirDebugPrologueEnd(emit: *Emit) !void { fn mirDebugEpilogueBegin(emit: *Emit) !void { switch (emit.debug_output) { .dwarf => |dw| { - try dw.getDeclDebugLineBuffer().append(DW.LNS.set_epilogue_begin); + try dw.dbg_line.append(DW.LNS.set_epilogue_begin); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, .plan9 => {}, diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 2c8374fca1..bbc880f0bd 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -749,7 +749,7 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { switch (self.debug_output) { .dwarf => |dw| { assert(ty.hasRuntimeBits()); - const dbg_info = dw.getDeclDebugInfoBuffer(); + const dbg_info = &dw.dbg_info; const index = dbg_info.items.len; try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 const atom = switch (self.bin_file.tag) { @@ -1572,7 +1572,7 @@ fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32 .register => |reg| { switch (self.debug_output) { .dwarf => |dw| { - const dbg_info = dw.getDeclDebugInfoBuffer(); + const dbg_info = &dw.dbg_info; try dbg_info.ensureUnusedCapacity(3); dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index bfa4e00d00..8190566c23 100644 --- a/src/arch/riscv64/Emit.zig +++ b/src/arch/riscv64/Emit.zig @@ -93,7 +93,7 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { // TODO Look into using the DWARF special opcodes to compress this data. // It lets you emit single-byte opcodes that add different numbers to // both the PC and the line number at the same time. - const dbg_line = dw.getDeclDebugLineBuffer(); + const dbg_line = &dw.dbg_line; try dbg_line.ensureUnusedCapacity(11); dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); leb128.writeULEB128(dbg_line.writer(), delta_pc) catch unreachable; @@ -184,7 +184,7 @@ fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { fn mirDebugPrologueEnd(self: *Emit) !void { switch (self.debug_output) { .dwarf => |dw| { - try dw.getDeclDebugLineBuffer().append(DW.LNS.set_prologue_end); + try dw.dbg_line.append(DW.LNS.set_prologue_end); try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); }, .plan9 => {}, @@ -195,7 +195,7 @@ fn mirDebugPrologueEnd(self: *Emit) !void { fn mirDebugEpilogueBegin(self: *Emit) !void { switch (self.debug_output) { .dwarf => |dw| { - try dw.getDeclDebugLineBuffer().append(DW.LNS.set_epilogue_begin); + try dw.dbg_line.append(DW.LNS.set_epilogue_begin); try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); }, .plan9 => {}, diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index e8ea10bf29..3296339419 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -977,7 +977,7 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void { // TODO Look into using the DWARF special opcodes to compress this data. // It lets you emit single-byte opcodes that add different numbers to // both the PC and the line number at the same time. - const dbg_line = dw.getDeclDebugLineBuffer(); + const dbg_line = &dw.dbg_line; try dbg_line.ensureUnusedCapacity(11); dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); leb128.writeULEB128(dbg_line.writer(), delta_pc) catch unreachable; @@ -1034,7 +1034,7 @@ fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { assert(tag == .dbg_prologue_end); switch (emit.debug_output) { .dwarf => |dw| { - try dw.getDeclDebugLineBuffer().append(DW.LNS.set_prologue_end); + try dw.dbg_line.append(DW.LNS.set_prologue_end); log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{ emit.prev_di_line, emit.prev_di_column }); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, @@ -1048,7 +1048,7 @@ fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { assert(tag == .dbg_epilogue_begin); switch (emit.debug_output) { .dwarf => |dw| { - try dw.getDeclDebugLineBuffer().append(DW.LNS.set_epilogue_begin); + try dw.dbg_line.append(DW.LNS.set_epilogue_begin); log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{ emit.prev_di_line, emit.prev_di_column }); try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, @@ -1075,7 +1075,7 @@ fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32 .register => |reg| { switch (emit.debug_output) { .dwarf => |dw| { - const dbg_info = dw.getDeclDebugInfoBuffer(); + const dbg_info = &dw.dbg_info; try dbg_info.ensureUnusedCapacity(3); dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc @@ -1099,7 +1099,7 @@ fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32 // TODO we need to make this more generic if we don't use rbp as the frame pointer // for example when -fomit-frame-pointer is set. const disp = @intCast(i32, max_stack) - off + 16; - const dbg_info = dw.getDeclDebugInfoBuffer(); + const dbg_info = &dw.dbg_info; try dbg_info.ensureUnusedCapacity(8); dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); const fixup = dbg_info.items.len; @@ -1128,7 +1128,7 @@ fn addDbgInfoTypeReloc(emit: *Emit, ty: Type) !void { switch (emit.debug_output) { .dwarf => |dw| { assert(ty.hasRuntimeBits()); - const dbg_info = dw.getDeclDebugInfoBuffer(); + const dbg_info = &dw.dbg_info; const index = dbg_info.items.len; try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 const atom = switch (emit.bin_file.tag) { diff --git a/src/codegen.zig b/src/codegen.zig index fa84ad0d52..2ace45c8cb 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -42,7 +42,7 @@ pub const GenerateSymbolError = error{ }; pub const DebugInfoOutput = union(enum) { - dwarf: *link.File.Dwarf, + dwarf: *link.File.Dwarf.DeclState, /// the plan9 debuginfo output is a bytecode with 4 opcodes /// assume all numbers/variables are bytes /// 0 w x y z -> interpret w x y z as a big-endian i32, and add it to the line offset diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 7840b8d6a7..907a21b774 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -43,11 +43,6 @@ abbrev_table_offset: ?u64 = null, /// Table of debug symbol names. strtab: std.ArrayListUnmanaged(u8) = .{}, -/// Lives only as long as the analysed Decl. -/// Allocated with `initDeclState`. -/// Freed with `commitDeclState`. -decl_state: ?DeclState = null, - /// List of atoms that are owned directly by the DWARF module. /// TODO convert links in DebugInfoAtom into indices and make /// sure every atom is owned by this module. @@ -71,6 +66,8 @@ pub const Atom = struct { /// and a set of relocations that will be resolved once this /// Decl's inner Atom is assigned an offset within the DWARF section. pub const DeclState = struct { + gpa: Allocator, + target: std.Target, dbg_line: std.ArrayList(u8), dbg_info: std.ArrayList(u8), abbrev_type_arena: std.heap.ArenaAllocator, @@ -83,21 +80,438 @@ pub const DeclState = struct { ) = .{}, abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{}, - fn init(gpa: Allocator) DeclState { + fn init(gpa: Allocator, target: std.Target) DeclState { return .{ + .gpa = gpa, + .target = target, .dbg_line = std.ArrayList(u8).init(gpa), .dbg_info = std.ArrayList(u8).init(gpa), .abbrev_type_arena = std.heap.ArenaAllocator.init(gpa), }; } - pub fn deinit(self: *DeclState, gpa: Allocator) void { + pub fn deinit(self: *DeclState) void { self.dbg_line.deinit(); self.dbg_info.deinit(); self.abbrev_type_arena.deinit(); - self.abbrev_table.deinit(gpa); - self.abbrev_resolver.deinit(gpa); - self.abbrev_relocs.deinit(gpa); + self.abbrev_table.deinit(self.gpa); + self.abbrev_resolver.deinit(self.gpa); + self.abbrev_relocs.deinit(self.gpa); + } + + pub fn addTypeReloc( + self: *DeclState, + atom: *const Atom, + ty: Type, + offset: u32, + addend: ?u32, + ) !void { + const resolv = self.abbrev_resolver.getContext(ty, .{ + .target = self.target, + }) orelse blk: { + const sym_index = @intCast(u32, self.abbrev_table.items.len); + try self.abbrev_table.append(self.gpa, .{ + .atom = atom, + .@"type" = ty, + .offset = undefined, + }); + log.debug("@{d}: {}", .{ sym_index, ty.fmtDebug() }); + try self.abbrev_resolver.putNoClobberContext(self.gpa, ty, sym_index, .{ + .target = self.target, + }); + break :blk self.abbrev_resolver.getContext(ty, .{ + .target = self.target, + }).?; + }; + const add: u32 = addend orelse 0; + + log.debug("{x}: @{d} + {x}", .{ offset, resolv, add }); + try self.abbrev_relocs.append(self.gpa, .{ + .target = resolv, + .atom = atom, + .offset = offset, + .addend = add, + }); + } + + fn addDbgInfoType( + self: *DeclState, + module: *Module, + atom: *Atom, + ty: Type, + ) error{OutOfMemory}!void { + const arena = self.abbrev_type_arena.allocator(); + const dbg_info_buffer = &self.dbg_info; + const target = self.target; + const target_endian = self.target.cpu.arch.endian(); + + switch (ty.zigTypeTag()) { + .NoReturn => unreachable, + .Void => { + try dbg_info_buffer.append(abbrev_pad1); + }, + .Bool => { + try dbg_info_buffer.appendSlice(&[_]u8{ + abbrev_base_type, + DW.ATE.boolean, // DW.AT.encoding , DW.FORM.data1 + 1, // DW.AT.byte_size, DW.FORM.data1 + 'b', 'o', 'o', 'l', 0, // DW.AT.name, DW.FORM.string + }); + }, + .Int => { + const info = ty.intInfo(target); + try dbg_info_buffer.ensureUnusedCapacity(12); + dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); + // DW.AT.encoding, DW.FORM.data1 + dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) { + .signed => DW.ATE.signed, + .unsigned => DW.ATE.unsigned, + }); + // DW.AT.byte_size, DW.FORM.data1 + dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); + // DW.AT.name, DW.FORM.string + try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); + }, + .Optional => { + if (ty.isPtrLikeOptional()) { + try dbg_info_buffer.ensureUnusedCapacity(12); + dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); + // DW.AT.encoding, DW.FORM.data1 + dbg_info_buffer.appendAssumeCapacity(DW.ATE.address); + // DW.AT.byte_size, DW.FORM.data1 + dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); + // DW.AT.name, DW.FORM.string + try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); + } else { + // Non-pointer optionals are structs: struct { .maybe = *, .val = * } + var buf = try arena.create(Type.Payload.ElemType); + const payload_ty = ty.optionalChild(buf); + // DW.AT.structure_type + try dbg_info_buffer.append(abbrev_struct_type); + // DW.AT.byte_size, DW.FORM.sdata + const abi_size = ty.abiSize(target); + try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); + // DW.AT.name, DW.FORM.string + try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); + // DW.AT.member + try dbg_info_buffer.ensureUnusedCapacity(7); + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity("maybe"); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + var index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try self.addTypeReloc(atom, Type.bool, @intCast(u32, index), null); + // DW.AT.data_member_location, DW.FORM.sdata + try dbg_info_buffer.ensureUnusedCapacity(6); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.member + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity("val"); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try self.addTypeReloc(atom, payload_ty, @intCast(u32, index), null); + // DW.AT.data_member_location, DW.FORM.sdata + const offset = abi_size - payload_ty.abiSize(target); + try leb128.writeULEB128(dbg_info_buffer.writer(), offset); + // DW.AT.structure_type delimit children + try dbg_info_buffer.append(0); + } + }, + .Pointer => { + if (ty.isSlice()) { + // Slices are structs: struct { .ptr = *, .len = N } + // DW.AT.structure_type + try dbg_info_buffer.ensureUnusedCapacity(2); + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_type); + // DW.AT.byte_size, DW.FORM.sdata + dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize) * 2); + // DW.AT.name, DW.FORM.string + try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); + // DW.AT.member + try dbg_info_buffer.ensureUnusedCapacity(5); + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity("ptr"); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + var index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + var buf = try arena.create(Type.SlicePtrFieldTypeBuffer); + const ptr_ty = ty.slicePtrFieldType(buf); + try self.addTypeReloc(atom, ptr_ty, @intCast(u32, index), null); + // DW.AT.data_member_location, DW.FORM.sdata + try dbg_info_buffer.ensureUnusedCapacity(6); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.member + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity("len"); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try self.addTypeReloc(atom, Type.usize, @intCast(u32, index), null); + // DW.AT.data_member_location, DW.FORM.sdata + try dbg_info_buffer.ensureUnusedCapacity(2); + dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize)); + // DW.AT.structure_type delimit children + dbg_info_buffer.appendAssumeCapacity(0); + } else { + try dbg_info_buffer.ensureUnusedCapacity(5); + dbg_info_buffer.appendAssumeCapacity(abbrev_ptr_type); + // DW.AT.type, DW.FORM.ref4 + const index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try self.addTypeReloc(atom, ty.childType(), @intCast(u32, index), null); + } + }, + .Struct => blk: { + // DW.AT.structure_type + try dbg_info_buffer.append(abbrev_struct_type); + // DW.AT.byte_size, DW.FORM.sdata + const abi_size = ty.abiSize(target); + try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); + + switch (ty.tag()) { + .tuple, .anon_struct => { + // DW.AT.name, DW.FORM.string + try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); + + const fields = ty.tupleFields(); + for (fields.types) |field, field_index| { + // DW.AT.member + try dbg_info_buffer.append(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + try dbg_info_buffer.writer().print("{d}\x00", .{field_index}); + // DW.AT.type, DW.FORM.ref4 + var index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try self.addTypeReloc(atom, field, @intCast(u32, index), null); + // DW.AT.data_member_location, DW.FORM.sdata + const field_off = ty.structFieldOffset(field_index, target); + try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); + } + }, + else => { + // DW.AT.name, DW.FORM.string + const struct_name = try ty.nameAllocArena(arena, target); + try dbg_info_buffer.ensureUnusedCapacity(struct_name.len + 1); + dbg_info_buffer.appendSliceAssumeCapacity(struct_name); + dbg_info_buffer.appendAssumeCapacity(0); + + const struct_obj = ty.castTag(.@"struct").?.data; + if (struct_obj.layout == .Packed) { + log.debug("TODO implement .debug_info for packed structs", .{}); + break :blk; + } + + const fields = ty.structFields(); + for (fields.keys()) |field_name, field_index| { + const field = fields.get(field_name).?; + // DW.AT.member + try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2); + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity(field_name); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + var index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try self.addTypeReloc(atom, field.ty, @intCast(u32, index), null); + // DW.AT.data_member_location, DW.FORM.sdata + const field_off = ty.structFieldOffset(field_index, target); + try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); + } + }, + } + + // DW.AT.structure_type delimit children + try dbg_info_buffer.append(0); + }, + .Enum => { + // DW.AT.enumeration_type + try dbg_info_buffer.append(abbrev_enum_type); + // DW.AT.byte_size, DW.FORM.sdata + const abi_size = ty.abiSize(target); + try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); + // DW.AT.name, DW.FORM.string + const enum_name = try ty.nameAllocArena(arena, target); + try dbg_info_buffer.ensureUnusedCapacity(enum_name.len + 1); + dbg_info_buffer.appendSliceAssumeCapacity(enum_name); + dbg_info_buffer.appendAssumeCapacity(0); + + const fields = ty.enumFields(); + const values: ?Module.EnumFull.ValueMap = switch (ty.tag()) { + .enum_full, .enum_nonexhaustive => ty.cast(Type.Payload.EnumFull).?.data.values, + .enum_simple => null, + .enum_numbered => ty.castTag(.enum_numbered).?.data.values, + else => unreachable, + }; + for (fields.keys()) |field_name, field_i| { + // DW.AT.enumerator + try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2 + @sizeOf(u64)); + dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity(field_name); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.const_value, DW.FORM.data8 + const value: u64 = if (values) |vals| value: { + if (vals.count() == 0) break :value @intCast(u64, field_i); // auto-numbered + const value = vals.keys()[field_i]; + var int_buffer: Value.Payload.U64 = undefined; + break :value value.enumToInt(ty, &int_buffer).toUnsignedInt(target); + } else @intCast(u64, field_i); + mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), value, target_endian); + } + + // DW.AT.enumeration_type delimit children + try dbg_info_buffer.append(0); + }, + .Union => { + const layout = ty.unionGetLayout(target); + const union_obj = ty.cast(Type.Payload.Union).?.data; + const payload_offset = if (layout.tag_align >= layout.payload_align) layout.tag_size else 0; + const tag_offset = if (layout.tag_align >= layout.payload_align) 0 else layout.payload_size; + const is_tagged = layout.tag_size > 0; + const union_name = try ty.nameAllocArena(arena, target); + + // TODO this is temporary to match current state of unions in Zig - we don't yet have + // safety checks implemented meaning the implicit tag is not yet stored and generated + // for untagged unions. + if (is_tagged) { + // DW.AT.structure_type + try dbg_info_buffer.append(abbrev_struct_type); + // DW.AT.byte_size, DW.FORM.sdata + try leb128.writeULEB128(dbg_info_buffer.writer(), layout.abi_size); + // DW.AT.name, DW.FORM.string + try dbg_info_buffer.ensureUnusedCapacity(union_name.len + 1); + dbg_info_buffer.appendSliceAssumeCapacity(union_name); + dbg_info_buffer.appendAssumeCapacity(0); + + // DW.AT.member + try dbg_info_buffer.ensureUnusedCapacity(9); + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity("payload"); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + const inner_union_index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(inner_union_index + 4); + try self.addTypeReloc(atom, ty, @intCast(u32, inner_union_index), 5); + // DW.AT.data_member_location, DW.FORM.sdata + try leb128.writeULEB128(dbg_info_buffer.writer(), payload_offset); + } + + // DW.AT.union_type + try dbg_info_buffer.append(abbrev_union_type); + // DW.AT.byte_size, DW.FORM.sdata, + try leb128.writeULEB128(dbg_info_buffer.writer(), layout.payload_size); + // DW.AT.name, DW.FORM.string + if (is_tagged) { + try dbg_info_buffer.writer().print("AnonUnion\x00", .{}); + } else { + try dbg_info_buffer.writer().print("{s}\x00", .{union_name}); + } + + const fields = ty.unionFields(); + for (fields.keys()) |field_name| { + const field = fields.get(field_name).?; + if (!field.ty.hasRuntimeBits()) continue; + // DW.AT.member + try dbg_info_buffer.append(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + try dbg_info_buffer.writer().print("{s}\x00", .{field_name}); + // DW.AT.type, DW.FORM.ref4 + const index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try self.addTypeReloc(atom, field.ty, @intCast(u32, index), null); + // DW.AT.data_member_location, DW.FORM.sdata + try dbg_info_buffer.append(0); + } + // DW.AT.union_type delimit children + try dbg_info_buffer.append(0); + + if (is_tagged) { + // DW.AT.member + try dbg_info_buffer.ensureUnusedCapacity(5); + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity("tag"); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + const index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try self.addTypeReloc(atom, union_obj.tag_ty, @intCast(u32, index), null); + // DW.AT.data_member_location, DW.FORM.sdata + try leb128.writeULEB128(dbg_info_buffer.writer(), tag_offset); + + // DW.AT.structure_type delimit children + try dbg_info_buffer.append(0); + } + }, + .ErrorSet => { + try addDbgInfoErrorSet( + self.abbrev_type_arena.allocator(), + module, + ty, + self.target, + &self.dbg_info, + ); + }, + .ErrorUnion => { + const error_ty = ty.errorUnionSet(); + const payload_ty = ty.errorUnionPayload(); + const abi_size = ty.abiSize(target); + const abi_align = ty.abiAlignment(target); + const payload_off = mem.alignForwardGeneric(u64, error_ty.abiSize(target), abi_align); + + // DW.AT.structure_type + try dbg_info_buffer.append(abbrev_struct_type); + // DW.AT.byte_size, DW.FORM.sdata + try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); + // DW.AT.name, DW.FORM.string + const name = try ty.nameAllocArena(arena, target); + try dbg_info_buffer.writer().print("{s}\x00", .{name}); + + // DW.AT.member + try dbg_info_buffer.ensureUnusedCapacity(7); + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity("value"); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + var index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try self.addTypeReloc(atom, payload_ty, @intCast(u32, index), null); + // DW.AT.data_member_location, DW.FORM.sdata + try leb128.writeULEB128(dbg_info_buffer.writer(), payload_off); + + // DW.AT.member + try dbg_info_buffer.ensureUnusedCapacity(5); + dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity("err"); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.type, DW.FORM.ref4 + index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try self.addTypeReloc(atom, error_ty, @intCast(u32, index), null); + // DW.AT.data_member_location, DW.FORM.sdata + try dbg_info_buffer.append(0); + + // DW.AT.structure_type delimit children + try dbg_info_buffer.append(0); + }, + else => { + log.debug("TODO implement .debug_info for type '{}'", .{ty.fmtDebug()}); + try dbg_info_buffer.append(abbrev_pad1); + }, + } } }; @@ -191,7 +605,7 @@ pub fn deinit(self: *Dwarf) void { /// Initializes Decl's state and its matching output buffers. /// Call this before `commitDeclState`. -pub fn initDeclState(self: *Dwarf, decl: *Module.Decl) !void { +pub fn initDeclState(self: *Dwarf, decl: *Module.Decl) !DeclState { const tracy = trace(@src()); defer tracy.end(); @@ -201,10 +615,10 @@ pub fn initDeclState(self: *Dwarf, decl: *Module.Decl) !void { log.debug("initDeclState {s}{*}", .{ decl_name, decl }); const gpa = self.allocator; - assert(self.decl_state == null); - self.decl_state = DeclState.init(gpa); - const dbg_line_buffer = &self.decl_state.?.dbg_line; - const dbg_info_buffer = &self.decl_state.?.dbg_info; + var decl_state = DeclState.init(gpa, self.target); + errdefer decl_state.deinit(); + const dbg_line_buffer = &decl_state.dbg_line; + const dbg_info_buffer = &decl_state.dbg_info; assert(decl.has_tv); @@ -274,7 +688,12 @@ pub fn initDeclState(self: *Dwarf, decl: *Module.Decl) !void { .macho => &decl.link.macho.dbg_info_atom, else => unreachable, }; - try self.addTypeReloc(atom, fn_ret_type, @intCast(u32, dbg_info_buffer.items.len), null); + try decl_state.addTypeReloc( + atom, + fn_ret_type, + @intCast(u32, dbg_info_buffer.items.len), + null, + ); dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4 } @@ -285,6 +704,8 @@ pub fn initDeclState(self: *Dwarf, decl: *Module.Decl) !void { // TODO implement .debug_info for global variables }, } + + return decl_state; } pub fn commitDeclState( @@ -294,19 +715,14 @@ pub fn commitDeclState( decl: *Module.Decl, sym_addr: u64, sym_size: u64, + decl_state: *DeclState, ) !void { const tracy = trace(@src()); defer tracy.end(); - assert(self.decl_state != null); // Caller forgot to call `initDeclState` - defer { - self.decl_state.?.deinit(self.allocator); - self.decl_state = null; - } - const gpa = self.allocator; - var dbg_line_buffer = &self.decl_state.?.dbg_line; - var dbg_info_buffer = &self.decl_state.?.dbg_info; + var dbg_line_buffer = &decl_state.dbg_line; + var dbg_info_buffer = &decl_state.dbg_info; const target_endian = self.target.cpu.arch.endian(); @@ -509,7 +925,6 @@ pub fn commitDeclState( .macho => &decl.link.macho.dbg_info_atom, else => unreachable, }; - const decl_state = &self.decl_state.?; { // Now we emit the .debug_info types of the Decl. These will count towards the size of @@ -532,7 +947,7 @@ pub fn commitDeclState( if (deferred) continue; symbol.offset = @intCast(u32, dbg_info_buffer.items.len); - try self.addDbgInfoType(decl_state.abbrev_type_arena.allocator(), module, atom, ty, dbg_info_buffer); + try decl_state.addDbgInfoType(module, atom, ty); } } @@ -825,414 +1240,6 @@ pub fn freeDecl(self: *Dwarf, decl: *Module.Decl) void { } } -/// Asserts the type has codegen bits. -fn addDbgInfoType( - self: *Dwarf, - arena: Allocator, - module: *Module, - atom: *Atom, - ty: Type, - dbg_info_buffer: *std.ArrayList(u8), -) error{OutOfMemory}!void { - const target = self.target; - const target_endian = self.target.cpu.arch.endian(); - - switch (ty.zigTypeTag()) { - .NoReturn => unreachable, - .Void => { - try dbg_info_buffer.append(abbrev_pad1); - }, - .Bool => { - try dbg_info_buffer.appendSlice(&[_]u8{ - abbrev_base_type, - DW.ATE.boolean, // DW.AT.encoding , DW.FORM.data1 - 1, // DW.AT.byte_size, DW.FORM.data1 - 'b', 'o', 'o', 'l', 0, // DW.AT.name, DW.FORM.string - }); - }, - .Int => { - const info = ty.intInfo(target); - try dbg_info_buffer.ensureUnusedCapacity(12); - dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); - // DW.AT.encoding, DW.FORM.data1 - dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) { - .signed => DW.ATE.signed, - .unsigned => DW.ATE.unsigned, - }); - // DW.AT.byte_size, DW.FORM.data1 - dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); - // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); - }, - .Optional => { - if (ty.isPtrLikeOptional()) { - try dbg_info_buffer.ensureUnusedCapacity(12); - dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); - // DW.AT.encoding, DW.FORM.data1 - dbg_info_buffer.appendAssumeCapacity(DW.ATE.address); - // DW.AT.byte_size, DW.FORM.data1 - dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); - // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); - } else { - // Non-pointer optionals are structs: struct { .maybe = *, .val = * } - var buf = try arena.create(Type.Payload.ElemType); - const payload_ty = ty.optionalChild(buf); - // DW.AT.structure_type - try dbg_info_buffer.append(abbrev_struct_type); - // DW.AT.byte_size, DW.FORM.sdata - const abi_size = ty.abiSize(target); - try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); - // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); - // DW.AT.member - try dbg_info_buffer.ensureUnusedCapacity(7); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity("maybe"); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.type, DW.FORM.ref4 - var index = dbg_info_buffer.items.len; - try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, Type.bool, @intCast(u32, index), null); - // DW.AT.data_member_location, DW.FORM.sdata - try dbg_info_buffer.ensureUnusedCapacity(6); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.member - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity("val"); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.type, DW.FORM.ref4 - index = dbg_info_buffer.items.len; - try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, payload_ty, @intCast(u32, index), null); - // DW.AT.data_member_location, DW.FORM.sdata - const offset = abi_size - payload_ty.abiSize(target); - try leb128.writeULEB128(dbg_info_buffer.writer(), offset); - // DW.AT.structure_type delimit children - try dbg_info_buffer.append(0); - } - }, - .Pointer => { - if (ty.isSlice()) { - // Slices are structs: struct { .ptr = *, .len = N } - // DW.AT.structure_type - try dbg_info_buffer.ensureUnusedCapacity(2); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_type); - // DW.AT.byte_size, DW.FORM.sdata - dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize) * 2); - // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); - // DW.AT.member - try dbg_info_buffer.ensureUnusedCapacity(5); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity("ptr"); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.type, DW.FORM.ref4 - var index = dbg_info_buffer.items.len; - try dbg_info_buffer.resize(index + 4); - var buf = try arena.create(Type.SlicePtrFieldTypeBuffer); - const ptr_ty = ty.slicePtrFieldType(buf); - try self.addTypeReloc(atom, ptr_ty, @intCast(u32, index), null); - // DW.AT.data_member_location, DW.FORM.sdata - try dbg_info_buffer.ensureUnusedCapacity(6); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.member - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity("len"); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.type, DW.FORM.ref4 - index = dbg_info_buffer.items.len; - try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, Type.usize, @intCast(u32, index), null); - // DW.AT.data_member_location, DW.FORM.sdata - try dbg_info_buffer.ensureUnusedCapacity(2); - dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize)); - // DW.AT.structure_type delimit children - dbg_info_buffer.appendAssumeCapacity(0); - } else { - try dbg_info_buffer.ensureUnusedCapacity(5); - dbg_info_buffer.appendAssumeCapacity(abbrev_ptr_type); - // DW.AT.type, DW.FORM.ref4 - const index = dbg_info_buffer.items.len; - try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, ty.childType(), @intCast(u32, index), null); - } - }, - .Struct => blk: { - // DW.AT.structure_type - try dbg_info_buffer.append(abbrev_struct_type); - // DW.AT.byte_size, DW.FORM.sdata - const abi_size = ty.abiSize(target); - try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); - - switch (ty.tag()) { - .tuple, .anon_struct => { - // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); - - const fields = ty.tupleFields(); - for (fields.types) |field, field_index| { - // DW.AT.member - try dbg_info_buffer.append(abbrev_struct_member); - // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{d}\x00", .{field_index}); - // DW.AT.type, DW.FORM.ref4 - var index = dbg_info_buffer.items.len; - try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, field, @intCast(u32, index), null); - // DW.AT.data_member_location, DW.FORM.sdata - const field_off = ty.structFieldOffset(field_index, target); - try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); - } - }, - else => { - // DW.AT.name, DW.FORM.string - const struct_name = try ty.nameAllocArena(arena, target); - try dbg_info_buffer.ensureUnusedCapacity(struct_name.len + 1); - dbg_info_buffer.appendSliceAssumeCapacity(struct_name); - dbg_info_buffer.appendAssumeCapacity(0); - - const struct_obj = ty.castTag(.@"struct").?.data; - if (struct_obj.layout == .Packed) { - log.debug("TODO implement .debug_info for packed structs", .{}); - break :blk; - } - - const fields = ty.structFields(); - for (fields.keys()) |field_name, field_index| { - const field = fields.get(field_name).?; - // DW.AT.member - try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity(field_name); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.type, DW.FORM.ref4 - var index = dbg_info_buffer.items.len; - try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, field.ty, @intCast(u32, index), null); - // DW.AT.data_member_location, DW.FORM.sdata - const field_off = ty.structFieldOffset(field_index, target); - try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); - } - }, - } - - // DW.AT.structure_type delimit children - try dbg_info_buffer.append(0); - }, - .Enum => { - // DW.AT.enumeration_type - try dbg_info_buffer.append(abbrev_enum_type); - // DW.AT.byte_size, DW.FORM.sdata - const abi_size = ty.abiSize(target); - try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); - // DW.AT.name, DW.FORM.string - const enum_name = try ty.nameAllocArena(arena, target); - try dbg_info_buffer.ensureUnusedCapacity(enum_name.len + 1); - dbg_info_buffer.appendSliceAssumeCapacity(enum_name); - dbg_info_buffer.appendAssumeCapacity(0); - - const fields = ty.enumFields(); - const values: ?Module.EnumFull.ValueMap = switch (ty.tag()) { - .enum_full, .enum_nonexhaustive => ty.cast(Type.Payload.EnumFull).?.data.values, - .enum_simple => null, - .enum_numbered => ty.castTag(.enum_numbered).?.data.values, - else => unreachable, - }; - for (fields.keys()) |field_name, field_i| { - // DW.AT.enumerator - try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2 + @sizeOf(u64)); - dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity(field_name); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.const_value, DW.FORM.data8 - const value: u64 = if (values) |vals| value: { - if (vals.count() == 0) break :value @intCast(u64, field_i); // auto-numbered - const value = vals.keys()[field_i]; - var int_buffer: Value.Payload.U64 = undefined; - break :value value.enumToInt(ty, &int_buffer).toUnsignedInt(target); - } else @intCast(u64, field_i); - mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), value, target_endian); - } - - // DW.AT.enumeration_type delimit children - try dbg_info_buffer.append(0); - }, - .Union => { - const layout = ty.unionGetLayout(target); - const union_obj = ty.cast(Type.Payload.Union).?.data; - const payload_offset = if (layout.tag_align >= layout.payload_align) layout.tag_size else 0; - const tag_offset = if (layout.tag_align >= layout.payload_align) 0 else layout.payload_size; - const is_tagged = layout.tag_size > 0; - const union_name = try ty.nameAllocArena(arena, target); - - // TODO this is temporary to match current state of unions in Zig - we don't yet have - // safety checks implemented meaning the implicit tag is not yet stored and generated - // for untagged unions. - if (is_tagged) { - // DW.AT.structure_type - try dbg_info_buffer.append(abbrev_struct_type); - // DW.AT.byte_size, DW.FORM.sdata - try leb128.writeULEB128(dbg_info_buffer.writer(), layout.abi_size); - // DW.AT.name, DW.FORM.string - try dbg_info_buffer.ensureUnusedCapacity(union_name.len + 1); - dbg_info_buffer.appendSliceAssumeCapacity(union_name); - dbg_info_buffer.appendAssumeCapacity(0); - - // DW.AT.member - try dbg_info_buffer.ensureUnusedCapacity(9); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity("payload"); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.type, DW.FORM.ref4 - const inner_union_index = dbg_info_buffer.items.len; - try dbg_info_buffer.resize(inner_union_index + 4); - try self.addTypeReloc(atom, ty, @intCast(u32, inner_union_index), 5); - // DW.AT.data_member_location, DW.FORM.sdata - try leb128.writeULEB128(dbg_info_buffer.writer(), payload_offset); - } - - // DW.AT.union_type - try dbg_info_buffer.append(abbrev_union_type); - // DW.AT.byte_size, DW.FORM.sdata, - try leb128.writeULEB128(dbg_info_buffer.writer(), layout.payload_size); - // DW.AT.name, DW.FORM.string - if (is_tagged) { - try dbg_info_buffer.writer().print("AnonUnion\x00", .{}); - } else { - try dbg_info_buffer.writer().print("{s}\x00", .{union_name}); - } - - const fields = ty.unionFields(); - for (fields.keys()) |field_name| { - const field = fields.get(field_name).?; - if (!field.ty.hasRuntimeBits()) continue; - // DW.AT.member - try dbg_info_buffer.append(abbrev_struct_member); - // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{s}\x00", .{field_name}); - // DW.AT.type, DW.FORM.ref4 - const index = dbg_info_buffer.items.len; - try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, field.ty, @intCast(u32, index), null); - // DW.AT.data_member_location, DW.FORM.sdata - try dbg_info_buffer.append(0); - } - // DW.AT.union_type delimit children - try dbg_info_buffer.append(0); - - if (is_tagged) { - // DW.AT.member - try dbg_info_buffer.ensureUnusedCapacity(5); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity("tag"); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.type, DW.FORM.ref4 - const index = dbg_info_buffer.items.len; - try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, union_obj.tag_ty, @intCast(u32, index), null); - // DW.AT.data_member_location, DW.FORM.sdata - try leb128.writeULEB128(dbg_info_buffer.writer(), tag_offset); - - // DW.AT.structure_type delimit children - try dbg_info_buffer.append(0); - } - }, - .ErrorSet => { - // DW.AT.enumeration_type - try dbg_info_buffer.append(abbrev_enum_type); - // DW.AT.byte_size, DW.FORM.sdata - const abi_size = ty.abiSize(target); - try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); - // DW.AT.name, DW.FORM.string - const name = try ty.nameAllocArena(arena, target); - try dbg_info_buffer.writer().print("{s}\x00", .{name}); - - // DW.AT.enumerator - const no_error = "(no error)"; - try dbg_info_buffer.ensureUnusedCapacity(no_error.len + 2 + @sizeOf(u64)); - dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity(no_error); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.const_value, DW.FORM.data8 - mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian); - - const error_names = ty.errorSetNames(); - for (error_names) |error_name| { - const kv = module.getErrorValue(error_name) catch unreachable; - // DW.AT.enumerator - try dbg_info_buffer.ensureUnusedCapacity(error_name.len + 2 + @sizeOf(u64)); - dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity(error_name); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.const_value, DW.FORM.data8 - mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), kv.value, target_endian); - } - - // DW.AT.enumeration_type delimit children - try dbg_info_buffer.append(0); - }, - .ErrorUnion => { - const error_ty = ty.errorUnionSet(); - const payload_ty = ty.errorUnionPayload(); - const abi_size = ty.abiSize(target); - const abi_align = ty.abiAlignment(target); - const payload_off = mem.alignForwardGeneric(u64, error_ty.abiSize(target), abi_align); - - // DW.AT.structure_type - try dbg_info_buffer.append(abbrev_struct_type); - // DW.AT.byte_size, DW.FORM.sdata - try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); - // DW.AT.name, DW.FORM.string - const name = try ty.nameAllocArena(arena, target); - try dbg_info_buffer.writer().print("{s}\x00", .{name}); - - // DW.AT.member - try dbg_info_buffer.ensureUnusedCapacity(7); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity("value"); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.type, DW.FORM.ref4 - var index = dbg_info_buffer.items.len; - try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, payload_ty, @intCast(u32, index), null); - // DW.AT.data_member_location, DW.FORM.sdata - try leb128.writeULEB128(dbg_info_buffer.writer(), payload_off); - - // DW.AT.member - try dbg_info_buffer.ensureUnusedCapacity(5); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); - // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity("err"); - dbg_info_buffer.appendAssumeCapacity(0); - // DW.AT.type, DW.FORM.ref4 - index = dbg_info_buffer.items.len; - try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, error_ty, @intCast(u32, index), null); - // DW.AT.data_member_location, DW.FORM.sdata - try dbg_info_buffer.append(0); - - // DW.AT.structure_type delimit children - try dbg_info_buffer.append(0); - }, - else => { - log.debug("TODO implement .debug_info for type '{}'", .{ty.fmtDebug()}); - try dbg_info_buffer.append(abbrev_pad1); - }, - } -} - pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { // These are LEB encoded but since the values are all less than 127 // we can simply append these bytes. @@ -1952,45 +1959,6 @@ fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { std.math.maxInt(@TypeOf(actual_size)); } -pub fn addTypeReloc(self: *Dwarf, atom: *const Atom, ty: Type, offset: u32, addend: ?u32) !void { - const decl_state = &self.decl_state.?; - const gpa = self.allocator; - const resolv = decl_state.abbrev_resolver.getContext(ty, .{ - .target = self.target, - }) orelse blk: { - const sym_index = @intCast(u32, decl_state.abbrev_table.items.len); - try decl_state.abbrev_table.append(gpa, .{ - .atom = atom, - .@"type" = ty, - .offset = undefined, - }); - log.debug("@{d}: {}", .{ sym_index, ty.fmtDebug() }); - try decl_state.abbrev_resolver.putNoClobberContext(gpa, ty, sym_index, .{ - .target = self.target, - }); - break :blk decl_state.abbrev_resolver.getContext(ty, .{ - .target = self.target, - }).?; - }; - const add: u32 = addend orelse 0; - - log.debug("{x}: @{d} + {x}", .{ offset, resolv, add }); - try decl_state.abbrev_relocs.append(gpa, .{ - .target = resolv, - .atom = atom, - .offset = offset, - .addend = add, - }); -} - -pub fn getDeclDebugLineBuffer(self: *Dwarf) *std.ArrayList(u8) { - return &self.decl_state.?.dbg_line; -} - -pub fn getDeclDebugInfoBuffer(self: *Dwarf) *std.ArrayList(u8) { - return &self.decl_state.?.dbg_info; -} - pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void { if (self.global_abbrev_relocs.items.len > 0) { const gpa = self.allocator; @@ -2018,7 +1986,7 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void { }; var dbg_info_buffer = std.ArrayList(u8).init(arena); - try self.addDbgInfoType(arena, module, atom, error_ty, &dbg_info_buffer); + try addDbgInfoErrorSet(arena, module, error_ty, self.target, &dbg_info_buffer); try self.managed_atoms.append(gpa, atom); try self.updateDeclDebugInfoAllocation(file, atom, @intCast(u32, dbg_info_buffer.items.len)); @@ -2060,6 +2028,49 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void { } } } - - assert(self.decl_state == null); +} + +fn addDbgInfoErrorSet( + arena: Allocator, + module: *Module, + ty: Type, + target: std.Target, + dbg_info_buffer: *std.ArrayList(u8), +) !void { + const target_endian = target.cpu.arch.endian(); + + // DW.AT.enumeration_type + try dbg_info_buffer.append(abbrev_enum_type); + // DW.AT.byte_size, DW.FORM.sdata + const abi_size = ty.abiSize(target); + try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); + // DW.AT.name, DW.FORM.string + const name = try ty.nameAllocArena(arena, target); + try dbg_info_buffer.writer().print("{s}\x00", .{name}); + + // DW.AT.enumerator + const no_error = "(no error)"; + try dbg_info_buffer.ensureUnusedCapacity(no_error.len + 2 + @sizeOf(u64)); + dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity(no_error); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.const_value, DW.FORM.data8 + mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian); + + const error_names = ty.errorSetNames(); + for (error_names) |error_name| { + const kv = module.getErrorValue(error_name) catch unreachable; + // DW.AT.enumerator + try dbg_info_buffer.ensureUnusedCapacity(error_name.len + 2 + @sizeOf(u64)); + dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + // DW.AT.name, DW.FORM.string + dbg_info_buffer.appendSliceAssumeCapacity(error_name); + dbg_info_buffer.appendAssumeCapacity(0); + // DW.AT.const_value, DW.FORM.data8 + mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), kv.value, target_endian); + } + + // DW.AT.enumeration_type delimit children + try dbg_info_buffer.append(0); } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index ece30c4347..636b2ba7df 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2339,19 +2339,12 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven const decl = func.owner_decl; self.freeUnnamedConsts(decl); - if (self.dwarf) |*dw| { - try dw.initDeclState(decl); - } - defer if (self.dwarf) |*dw| { - if (dw.decl_state) |*ds| { - ds.deinit(dw.allocator); - dw.decl_state = null; - } - }; + var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(decl) else null; + defer if (decl_state) |*ds| ds.deinit(); - const res = if (self.dwarf) |*dw| + const res = if (decl_state) |*ds| try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ - .dwarf = dw, + .dwarf = ds, }) else try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); @@ -2365,8 +2358,15 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven }, }; const local_sym = try self.updateDeclCode(decl, code, elf.STT_FUNC); - if (self.dwarf) |*dw| { - try dw.commitDeclState(&self.base, module, decl, local_sym.st_value, local_sym.st_size); + if (decl_state) |*ds| { + try self.dwarf.?.commitDeclState( + &self.base, + module, + decl, + local_sym.st_value, + local_sym.st_size, + ds, + ); } // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. @@ -2400,18 +2400,17 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); - if (self.dwarf) |*dw| { - try dw.initDeclState(decl); - } + var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(decl) else null; + defer if (decl_state) |*ds| ds.deinit(); // TODO implement .debug_info for global variables const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; - const res = if (self.dwarf) |*dw| + const res = if (decl_state) |*ds| try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ .ty = decl.ty, .val = decl_val, }, &code_buffer, .{ - .dwarf = dw, + .dwarf = ds, }, .{ .parent_atom_index = decl.link.elf.local_sym_index, }) @@ -2434,8 +2433,15 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { }; const local_sym = try self.updateDeclCode(decl, code, elf.STT_OBJECT); - if (self.dwarf) |*dw| { - try dw.commitDeclState(&self.base, module, decl, local_sym.st_value, local_sym.st_size); + if (decl_state) |*ds| { + try self.dwarf.?.commitDeclState( + &self.base, + module, + decl, + local_sym.st_value, + local_sym.st_size, + ds, + ); } // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 31ada3b4ee..1d75eb442a 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -27,6 +27,7 @@ const Atom = @import("MachO/Atom.zig"); const Cache = @import("../Cache.zig"); const CodeSignature = @import("MachO/CodeSignature.zig"); const Compilation = @import("../Compilation.zig"); +const Dwarf = File.Dwarf; const Dylib = @import("MachO/Dylib.zig"); const File = link.File; const Object = @import("MachO/Object.zig"); @@ -3676,13 +3677,15 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); - if (self.d_sym) |*d_sym| { - try d_sym.dwarf.initDeclState(decl); - } + var decl_state = if (self.d_sym) |*d_sym| + try d_sym.dwarf.initDeclState(decl) + else + null; + defer if (decl_state) |*ds| ds.deinit(); - const res = if (self.d_sym) |*d_sym| + const res = if (decl_state) |*ds| try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ - .dwarf = &d_sym.dwarf, + .dwarf = ds, }) else try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); @@ -3700,8 +3703,15 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv const symbol = try self.placeDecl(decl, decl.link.macho.code.items.len); - if (self.d_sym) |*d_sym| { - try d_sym.dwarf.commitDeclState(&self.base, module, decl, symbol.n_value, decl.link.macho.size); + if (decl_state) |*ds| { + try self.d_sym.?.dwarf.commitDeclState( + &self.base, + module, + decl, + symbol.n_value, + decl.link.macho.size, + ds, + ); } // Since we updated the vaddr and the size, each corresponding export symbol also @@ -3801,17 +3811,19 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); - if (self.d_sym) |*d_sym| { - try d_sym.dwarf.initDeclState(decl); - } + var decl_state: ?Dwarf.DeclState = if (self.d_sym) |*d_sym| + try d_sym.dwarf.initDeclState(decl) + else + null; + defer if (decl_state) |*ds| ds.deinit(); const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; - const res = if (self.d_sym) |*d_sym| + const res = if (decl_state) |*ds| try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ .ty = decl.ty, .val = decl_val, }, &code_buffer, .{ - .dwarf = &d_sym.dwarf, + .dwarf = ds, }, .{ .parent_atom_index = decl.link.macho.local_sym_index, }) @@ -3845,8 +3857,15 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { }; const symbol = try self.placeDecl(decl, code.len); - if (self.d_sym) |*d_sym| { - try d_sym.dwarf.commitDeclState(&self.base, module, decl, symbol.n_value, decl.link.macho.size); + if (decl_state) |*ds| { + try self.d_sym.?.dwarf.commitDeclState( + &self.base, + module, + decl, + symbol.n_value, + decl.link.macho.size, + ds, + ); } // Since we updated the vaddr and the size, each corresponding export symbol also From 47dfaf47b89d4d87a9a15aed512e910fdec5cbb0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 Mar 2022 11:21:33 -0700 Subject: [PATCH 1008/2031] stage2: test compile errors independently Until we land some incremental compilation bug fixes, this prevents CI failures when running the compile errors test cases. --- test/compile_errors.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 7da410f9e3..4d18d77ce4 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -13,7 +13,8 @@ pub fn addCases(ctx: *TestContext) !void { var stage2_dir = try compile_errors_dir.openDir("stage2", .{ .iterate = true, .no_follow = true }); defer stage2_dir.close(); - const one_test_case_per_file = false; + // TODO make this false once the bug is solved that it triggers + const one_test_case_per_file = true; try ctx.addErrorCasesFromDir("stage2", stage2_dir, .stage2, .Obj, false, one_test_case_per_file); } From d227f76afbacb619152ffd943cf3018e62e00adc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 Mar 2022 11:52:10 -0700 Subject: [PATCH 1009/2031] std.zig.Ast: fix escaped capture of by-value parameters --- lib/std/zig/Ast.zig | 6 +++--- src/AstGen.zig | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig index b485f915f0..230eff84c0 100644 --- a/lib/std/zig/Ast.zig +++ b/lib/std/zig/Ast.zig @@ -2354,10 +2354,10 @@ pub const full = struct { } }; - pub fn iterate(fn_proto: FnProto, tree: Ast) Iterator { + pub fn iterate(fn_proto: *const FnProto, tree: *const Ast) Iterator { return .{ - .tree = &tree, - .fn_proto = &fn_proto, + .tree = tree, + .fn_proto = fn_proto, .param_i = 0, .tok_i = fn_proto.lparen + 1, .tok_flag = true, diff --git a/src/AstGen.zig b/src/AstGen.zig index ed6c9f86ce..1e4865bd97 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1122,7 +1122,7 @@ fn fnProtoExpr( const is_var_args = is_var_args: { var param_type_i: usize = 0; - var it = fn_proto.iterate(tree.*); + var it = fn_proto.iterate(tree); while (it.next()) |param| : (param_type_i += 1) { const is_comptime = if (param.comptime_noalias) |token| token_tags[token] == .keyword_comptime @@ -3377,7 +3377,7 @@ fn fnDecl( var params_scope = &fn_gz.base; const is_var_args = is_var_args: { var param_type_i: usize = 0; - var it = fn_proto.iterate(tree.*); + var it = fn_proto.iterate(tree); while (it.next()) |param| : (param_type_i += 1) { const is_comptime = if (param.comptime_noalias) |token| token_tags[token] == .keyword_comptime From 5d5b1b68fc1b3e1f5a8c7871c9f31716c89eb062 Mon Sep 17 00:00:00 2001 From: James Mintram Date: Wed, 30 Mar 2022 23:18:22 +0100 Subject: [PATCH 1010/2031] Remove a std.debug.print from the dwarf.zig file This was causing freestanding builds to fail due to the use of `std.debug.print` --- lib/std/dwarf.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index a13dba2516..506f9eeef8 100644 --- a/lib/std/dwarf.zig +++ b/lib/std/dwarf.zig @@ -514,7 +514,6 @@ fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, en FORM.implicit_const => FormValue{ .Const = Constant{ .signed = true, .payload = undefined } }, else => { - std.debug.print("dwarf: unhandled form_id: 0x{x}\n", .{form_id}); return error.InvalidDebugInfo; }, }; From 02d69f50092ee9ecfa552ff94d20fde62edd7adc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 Mar 2022 17:20:12 -0700 Subject: [PATCH 1011/2031] Sema: fix usingnamespace decl Value in wrong arena closes #11297 --- src/Module.zig | 7 +++---- test/behavior/usingnamespace.zig | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 3bc763103b..1fd83108ff 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3909,19 +3909,18 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { const decl_arena_state = try decl_arena_allocator.create(std.heap.ArenaAllocator.State); if (decl.is_usingnamespace) { - const ty_ty = Type.initTag(.type); - if (!decl_tv.ty.eql(ty_ty, target)) { + if (!decl_tv.ty.eql(Type.type, target)) { return sema.fail(&block_scope, src, "expected type, found {}", .{ decl_tv.ty.fmt(target), }); } var buffer: Value.ToTypeBuffer = undefined; - const ty = decl_tv.val.toType(&buffer); + const ty = try decl_tv.val.toType(&buffer).copy(decl_arena_allocator); if (ty.getNamespace() == null) { return sema.fail(&block_scope, src, "type {} has no namespace", .{ty.fmt(target)}); } - decl.ty = ty_ty; + decl.ty = Type.type; decl.val = try Value.Tag.ty.create(decl_arena_allocator, ty); decl.@"align" = 0; decl.@"linksection" = null; diff --git a/test/behavior/usingnamespace.zig b/test/behavior/usingnamespace.zig index ed84c36071..426f0aa6b9 100644 --- a/test/behavior/usingnamespace.zig +++ b/test/behavior/usingnamespace.zig @@ -56,3 +56,23 @@ test "two files usingnamespace import each other" { try expect(@This().ok()); } + +test { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const AA = struct { + x: i32, + fn b(x: i32) @This() { + return .{ .x = x }; + } + fn c() type { + return if (true) struct { + const expected: i32 = 42; + } else struct {}; + } + usingnamespace c(); + }; + const a = AA.b(42); + try expect(a.x == AA.c().expected); +} From 6655c6092efbdc8b1aeb89ff9f7a8b05f736916a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 Mar 2022 20:38:01 -0700 Subject: [PATCH 1012/2031] std.base64: upgrade to new function pointer semantics --- lib/std/base64.zig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/std/base64.zig b/lib/std/base64.zig index 9e5fa0c25c..866002ca0f 100644 --- a/lib/std/base64.zig +++ b/lib/std/base64.zig @@ -9,11 +9,16 @@ pub const Error = error{ NoSpaceLeft, }; +const decoderWithIgnoreProto = switch (@import("builtin").zig_backend) { + .stage1 => fn (ignore: []const u8) Base64DecoderWithIgnore, + else => *const fn (ignore: []const u8) Base64DecoderWithIgnore, +}; + /// Base64 codecs pub const Codecs = struct { alphabet_chars: [64]u8, pad_char: ?u8, - decoderWithIgnore: fn (ignore: []const u8) Base64DecoderWithIgnore, + decoderWithIgnore: decoderWithIgnoreProto, Encoder: Base64Encoder, Decoder: Base64Decoder, }; From 75c2cff40ef5c758ec18455324a50378684aff85 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 31 Mar 2022 00:38:19 +0300 Subject: [PATCH 1013/2031] stage2: handle assembly input names --- src/Air.zig | 2 ++ src/Sema.zig | 20 +++++++++--------- src/arch/aarch64/CodeGen.zig | 6 ++++-- src/arch/arm/CodeGen.zig | 6 ++++-- src/arch/riscv64/CodeGen.zig | 6 ++++-- src/arch/x86_64/CodeGen.zig | 6 ++++-- src/codegen/llvm.zig | 39 ++++++++++++++++++++++++++++++++---- 7 files changed, 63 insertions(+), 22 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 39215495c3..d02491ff89 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -815,6 +815,8 @@ pub const VectorCmp = struct { /// terminated string. pad to the next u32 after the null byte. /// 3. for every inputs_len /// - constraint: memory at this position is reinterpreted as a null +/// terminated string. +/// - name: memory at this position is reinterpreted as a null /// terminated string. pad to the next u32 after the null byte. /// 4. for every clobbers_len /// - clobber_name: memory at this position is reinterpreted as a null diff --git a/src/Sema.zig b/src/Sema.zig index 112939c995..9f5d6d5f9e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -10295,15 +10295,12 @@ fn zirAsm( }; const args = try sema.arena.alloc(Air.Inst.Ref, inputs_len); - const inputs = try sema.arena.alloc([]const u8, inputs_len); + const inputs = try sema.arena.alloc(struct { c: []const u8, n: []const u8 }, inputs_len); for (args) |*arg, arg_i| { const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i); extra_i = input.end; - const name = sema.code.nullTerminatedString(input.data.name); - _ = name; // TODO: use the name - const uncasted_arg = sema.resolveInst(input.data.operand); const uncasted_arg_ty = sema.typeOf(uncasted_arg); switch (uncasted_arg_ty.zigTypeTag()) { @@ -10313,8 +10310,9 @@ fn zirAsm( } const constraint = sema.code.nullTerminatedString(input.data.constraint); - needed_capacity += constraint.len / 4 + 1; - inputs[arg_i] = constraint; + const name = sema.code.nullTerminatedString(input.data.name); + needed_capacity += (constraint.len + name.len + 1) / 4 + 1; + inputs[arg_i] = .{ .c = constraint, .n = name }; } const clobbers = try sema.arena.alloc([]const u8, clobbers_len); @@ -10353,11 +10351,13 @@ fn zirAsm( buffer[o.constraint.len] = 0; sema.air_extra.items.len += o.constraint.len / 4 + 1; } - for (inputs) |constraint| { + for (inputs) |input| { const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); - mem.copy(u8, buffer, constraint); - buffer[constraint.len] = 0; - sema.air_extra.items.len += constraint.len / 4 + 1; + mem.copy(u8, buffer, input.c); + buffer[input.c.len] = 0; + mem.copy(u8, buffer[input.c.len + 1 ..], input.n); + buffer[input.c.len + 1 + input.n.len] = 0; + sema.air_extra.items.len += (input.c.len + input.n.len + 1) / 4 + 1; } for (clobbers) |clobber| { const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 142611d083..ea946d6ba6 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -3200,10 +3200,12 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { } else null; for (inputs) |input| { - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(input_bytes, 0); + const input_name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += constraint.len / 4 + 1; + extra_i += (constraint.len + input_name.len + 1) / 4 + 1; if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 6e03dbf4fa..85545e33a5 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -3609,10 +3609,12 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { } else null; for (inputs) |input| { - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(input_bytes, 0); + const input_name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += constraint.len / 4 + 1; + extra_i += (constraint.len + input_name.len + 1) / 4 + 1; if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index bbc880f0bd..ac12bbceaf 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -2113,10 +2113,12 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { } else null; for (inputs) |input| { - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(input_bytes, 0); + const input_name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += constraint.len / 4 + 1; + extra_i += (constraint.len + input_name.len + 1) / 4 + 1; if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 9ecd2d4551..b093209a2e 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -4618,10 +4618,12 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { } else null; for (inputs) |input| { - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(input_bytes, 0); + const input_name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += constraint.len / 4 + 1; + extra_i += (constraint.len + input_name.len + 1) / 4 + 1; if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index af68255fe8..1391d8fe66 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4545,11 +4545,14 @@ pub const FuncGen = struct { total_i += 1; } + const input_start_extra_i = extra_i; for (inputs) |input| { - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(input_bytes, 0); + const input_name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += constraint.len / 4 + 1; + extra_i += (constraint.len + input_name.len + 1) / 4 + 1; const arg_llvm_value = try self.resolveInst(input); @@ -4591,11 +4594,12 @@ pub const FuncGen = struct { var rendered_template = std.ArrayList(u8).init(self.gpa); defer rendered_template.deinit(); - const State = enum { start, percent }; + const State = enum { start, percent, input }; var state: State = .start; - for (asm_source) |byte| { + var name_start: usize = undefined; + for (asm_source) |byte, i| { switch (state) { .start => switch (byte) { '%' => state = .percent, @@ -4606,12 +4610,39 @@ pub const FuncGen = struct { try rendered_template.append('%'); state = .start; }, + '[' => { + try rendered_template.append('$'); + name_start = i + 1; + state = .input; + }, else => { try rendered_template.append('%'); try rendered_template.append(byte); state = .start; }, }, + .input => switch (byte) { + ']' => { + const name = asm_source[name_start..i]; + state = .start; + + extra_i = input_start_extra_i; + for (inputs) |_, input_i| { + const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(input_bytes, 0); + const input_name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); + extra_i += (constraint.len + input_name.len + 1) / 4 + 1; + + if (std.mem.eql(u8, name, input_name)) { + try rendered_template.writer().print("{d}", .{input_i}); + break; + } + } else { + return self.todo("TODO validate asm in Sema", .{}); + } + }, + else => {}, + }, } } From 08565b23f94655e3b2610187f71ba774ce89eeb8 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Wed, 30 Mar 2022 20:47:45 -0700 Subject: [PATCH 1014/2031] stage2: fix print_zir for .builtin_src --- src/AstGen.zig | 7 ++----- src/Zir.zig | 2 +- src/print_zir.zig | 7 ++++++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index b6ca1fe848..0fe64ec36b 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -7205,14 +7205,11 @@ fn builtinCall( .src => { const token_starts = tree.tokens.items(.start); const node_start = token_starts[tree.firstToken(node)]; - astgen.advanceSourceCursor(node_start); - const result = try gz.addExtendedPayload(.builtin_src, Zir.Inst.LineColumn{ - .line = @intCast(u32, astgen.source_line), - .column = @intCast(u32, astgen.source_column), + .line = astgen.source_line, + .column = astgen.source_column, }); - return rvalue(gz, rl, result, node); }, diff --git a/src/Zir.zig b/src/Zir.zig index 9474627675..6c9cd9d778 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -1587,7 +1587,7 @@ pub const Inst = struct { /// `operand` is `src_node: i32`. ret_addr, /// Implements the `@src` builtin. - /// `operand` is payload index to `ColumnLine`. + /// `operand` is payload index to `LineColumn`. builtin_src, /// Implements the `@errorReturnTrace` builtin. /// `operand` is `src_node: i32`. diff --git a/src/print_zir.zig b/src/print_zir.zig index 81562dc7bc..e85e69fe7f 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -461,9 +461,14 @@ const Writer = struct { .error_return_trace, .frame, .frame_address, - .builtin_src, => try self.writeExtNode(stream, extended), + .builtin_src => { + try stream.writeAll("))"); + const inst_data = self.code.extraData(Zir.Inst.LineColumn, extended.operand).data; + try stream.print(":{d}:{d}", .{ inst_data.line + 1, inst_data.column + 1 }); + }, + .dbg_block_begin, .dbg_block_end, => try stream.writeAll("))"), From cf4aad4858ac61b4814d8f021c8eae22ee7f63e6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 Mar 2022 23:16:32 -0700 Subject: [PATCH 1015/2031] AstGen: fix referencing unreferencable instructions Sema avoids adding map entries for certain instructions such as `set_eval_branch_quota` and `atomic_store`. This means that result location semantics in AstGen must not emit any instructions that attempt to use the result of any of these instructions. This commit makes AstGen replace such instructions with `Zir.Inst.Ref.void_value` if their result value ends up being referenced. This fixes a compiler crash when running std lib atomic tests. --- src/AstGen.zig | 11 +- src/Zir.zig | 267 ++++++++++++++++++++++++++++++++++++++ test/behavior/atomics.zig | 34 +++++ 3 files changed, 311 insertions(+), 1 deletion(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 0fe64ec36b..0d52a1a2bc 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -8924,9 +8924,18 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool { fn rvalue( gz: *GenZir, rl: ResultLoc, - result: Zir.Inst.Ref, + raw_result: Zir.Inst.Ref, src_node: Ast.Node.Index, ) InnerError!Zir.Inst.Ref { + const result = r: { + if (refToIndex(raw_result)) |result_index| { + const zir_tags = gz.astgen.instructions.items(.tag); + if (zir_tags[result_index].isAlwaysVoid()) { + break :r Zir.Inst.Ref.void_value; + } + } + break :r raw_result; + }; if (gz.endsWithNoReturn()) return result; switch (rl) { .none, .coerced_ty => return result, diff --git a/src/Zir.zig b/src/Zir.zig index 6c9cd9d778..d8e53af4f9 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -1262,6 +1262,273 @@ pub const Inst = struct { }; } + /// AstGen uses this to find out if `Ref.void_value` should be used in place + /// of the result of a given instruction. This allows Sema to forego adding + /// the instruction to the map after analysis. + pub fn isAlwaysVoid(tag: Tag) bool { + return switch (tag) { + .breakpoint, + .fence, + .@"break", + .break_inline, + .condbr, + .condbr_inline, + .compile_error, + .ret_node, + .ret_load, + .ret_tok, + .ret_err_value, + .@"unreachable", + .repeat, + .repeat_inline, + .panic, + .dbg_stmt, + .dbg_var_ptr, + .dbg_var_val, + .ensure_result_used, + .ensure_result_non_error, + .ensure_err_payload_void, + .set_eval_branch_quota, + .atomic_store, + .store, + .store_node, + .store_to_block_ptr, + .store_to_inferred_ptr, + .resolve_inferred_alloc, + .validate_array_init_ty, + .validate_struct_init_ty, + .validate_struct_init, + .validate_struct_init_comptime, + .validate_array_init, + .validate_array_init_comptime, + .@"export", + .export_value, + .set_align_stack, + .set_cold, + .set_float_mode, + .set_runtime_safety, + .memcpy, + .memset, + => true, + + .param, + .param_comptime, + .param_anytype, + .param_anytype_comptime, + .add, + .addwrap, + .add_sat, + .alloc, + .alloc_mut, + .alloc_comptime_mut, + .alloc_inferred, + .alloc_inferred_mut, + .alloc_inferred_comptime, + .alloc_inferred_comptime_mut, + .make_ptr_const, + .array_cat, + .array_mul, + .array_type, + .array_type_sentinel, + .vector_type, + .elem_type, + .indexable_ptr_len, + .anyframe_type, + .as, + .as_node, + .bit_and, + .bitcast, + .bit_or, + .block, + .block_inline, + .suspend_block, + .loop, + .bool_br_and, + .bool_br_or, + .bool_not, + .call, + .cmp_lt, + .cmp_lte, + .cmp_eq, + .cmp_gte, + .cmp_gt, + .cmp_neq, + .coerce_result_ptr, + .error_set_decl, + .error_set_decl_anon, + .error_set_decl_func, + .decl_ref, + .decl_val, + .load, + .div, + .elem_ptr, + .elem_val, + .elem_ptr_node, + .elem_ptr_imm, + .elem_val_node, + .field_ptr, + .field_val, + .field_call_bind, + .field_ptr_named, + .field_val_named, + .field_call_bind_named, + .func, + .func_inferred, + .has_decl, + .int, + .int_big, + .float, + .float128, + .int_type, + .is_non_null, + .is_non_null_ptr, + .is_non_err, + .is_non_err_ptr, + .mod_rem, + .mul, + .mulwrap, + .mul_sat, + .param_type, + .ref, + .shl, + .shl_sat, + .shr, + .str, + .sub, + .subwrap, + .sub_sat, + .negate, + .negate_wrap, + .typeof, + .typeof_builtin, + .xor, + .optional_type, + .optional_payload_safe, + .optional_payload_unsafe, + .optional_payload_safe_ptr, + .optional_payload_unsafe_ptr, + .err_union_payload_safe, + .err_union_payload_unsafe, + .err_union_payload_safe_ptr, + .err_union_payload_unsafe_ptr, + .err_union_code, + .err_union_code_ptr, + .error_to_int, + .int_to_error, + .ptr_type, + .ptr_type_simple, + .enum_literal, + .merge_error_sets, + .error_union_type, + .bit_not, + .error_value, + .slice_start, + .slice_end, + .slice_sentinel, + .import, + .typeof_log2_int_type, + .log2_int_type, + .switch_capture, + .switch_capture_ref, + .switch_capture_multi, + .switch_capture_multi_ref, + .switch_block, + .switch_cond, + .switch_cond_ref, + .array_base_ptr, + .field_base_ptr, + .struct_init_empty, + .struct_init, + .struct_init_ref, + .struct_init_anon, + .struct_init_anon_ref, + .array_init, + .array_init_sent, + .array_init_anon, + .array_init_ref, + .array_init_sent_ref, + .array_init_anon_ref, + .union_init, + .field_type, + .field_type_ref, + .int_to_enum, + .enum_to_int, + .type_info, + .size_of, + .bit_size_of, + .ptr_to_int, + .align_of, + .bool_to_int, + .embed_file, + .error_name, + .sqrt, + .sin, + .cos, + .exp, + .exp2, + .log, + .log2, + .log10, + .fabs, + .floor, + .ceil, + .trunc, + .round, + .tag_name, + .reify, + .type_name, + .frame_type, + .frame_size, + .float_to_int, + .int_to_float, + .int_to_ptr, + .float_cast, + .int_cast, + .err_set_cast, + .ptr_cast, + .truncate, + .align_cast, + .has_field, + .clz, + .ctz, + .pop_count, + .byte_swap, + .bit_reverse, + .div_exact, + .div_floor, + .div_trunc, + .mod, + .rem, + .shl_exact, + .shr_exact, + .bit_offset_of, + .offset_of, + .cmpxchg_strong, + .cmpxchg_weak, + .splat, + .reduce, + .shuffle, + .select, + .atomic_load, + .atomic_rmw, + .mul_add, + .builtin_call, + .field_parent_ptr, + .maximum, + .minimum, + .builtin_async_call, + .c_import, + .@"resume", + .@"await", + .await_nosuspend, + .ret_err_value_code, + .extended, + .closure_get, + .closure_capture, + => false, + }; + } + /// Used by debug safety-checking code. pub const data_tags = list: { @setEvalBranchQuota(2000); diff --git a/test/behavior/atomics.zig b/test/behavior/atomics.zig index 123b4b4886..71e17d9b4c 100644 --- a/test/behavior/atomics.zig +++ b/test/behavior/atomics.zig @@ -305,3 +305,37 @@ fn testAtomicsWithType(comptime T: type, a: T, b: T) !void { if (@sizeOf(T) != 0) try expect(@cmpxchgStrong(T, &x, b, a, .SeqCst, .SeqCst).? == a); } + +test "return @atomicStore, using it as a void value" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + const A = struct { + value: usize, + + pub fn store(self: *A, value: usize) void { + return @atomicStore(usize, &self.value, value, .Unordered); + } + + pub fn store2(self: *A, value: usize) void { + return switch (value) { + else => @atomicStore(usize, &self.value, value, .Unordered), + }; + } + }; + + fn doTheTest() !void { + var x: A = .{ .value = 5 }; + x.store(10); + try expect(x.value == 10); + x.store(100); + try expect(x.value == 100); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} From b6133931d04afc6980dfff2e87dadc85922d5335 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 31 Mar 2022 12:25:48 -0700 Subject: [PATCH 1016/2031] Sema: fix segfault during resolveInferredErrorSet There was a simple missing check of adding an inferred error set to itself, in which case we should not try to mutate the hash map while iterating over it. --- src/Sema.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Sema.zig b/src/Sema.zig index 9f5d6d5f9e..6ab89af3f9 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -21549,6 +21549,7 @@ fn resolveInferredErrorSet( var it = ies.inferred_error_sets.keyIterator(); while (it.next()) |other_error_set_ptr| { const other_ies: *Module.Fn.InferredErrorSet = other_error_set_ptr.*; + if (ies == other_ies) continue; try sema.resolveInferredErrorSet(block, src, other_ies); for (other_ies.errors.keys()) |key| { From df1ba38a88a255286af4d939d427f8a4ee667485 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 31 Mar 2022 15:06:12 -0700 Subject: [PATCH 1017/2031] AstGen: fix treating noreturn instructions as void Fixes regression introduced in cf4aad4858ac61b4814d8f021c8eae22ee7f63e6. --- src/Zir.zig | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Zir.zig b/src/Zir.zig index d8e53af4f9..8fe5276792 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -1269,19 +1269,6 @@ pub const Inst = struct { return switch (tag) { .breakpoint, .fence, - .@"break", - .break_inline, - .condbr, - .condbr_inline, - .compile_error, - .ret_node, - .ret_load, - .ret_tok, - .ret_err_value, - .@"unreachable", - .repeat, - .repeat_inline, - .panic, .dbg_stmt, .dbg_var_ptr, .dbg_var_val, @@ -1525,6 +1512,19 @@ pub const Inst = struct { .extended, .closure_get, .closure_capture, + .@"break", + .break_inline, + .condbr, + .condbr_inline, + .compile_error, + .ret_node, + .ret_load, + .ret_tok, + .ret_err_value, + .@"unreachable", + .repeat, + .repeat_inline, + .panic, => false, }; } From 243afdcdf57d74a184784551aebe58062e5afc03 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 31 Mar 2022 15:06:44 -0700 Subject: [PATCH 1018/2031] test harness improvements * `-Dskip-compile-errors` is removed; `-Dskip-stage1` is added. * Use `std.testing.allocator` instead of a new instance of GPA. - Fix the memory leaks this revealed. * Show the file name when it is not parsed correctly such as when the manifest is missing. - Better error messages when test files are not parsed correctly. * Ignore unknown files such as swap files. * Move logic from declarative file to the test harness implementation. * Move stage1 tests to stage2 tests where appropriate. --- build.zig | 4 +- src/test.zig | 159 +++++++++++++----- test/compile_errors.zig | 38 ----- .../std.fmt_error_for_unused_arguments.zig | 2 +- .../unused_variable_error_on_errdefer.zig | 0 .../stage1/test/duplicate-unused_labels.zig | 30 ---- ...de_comptime_function_has_compile_error.zig | 7 +- .../stage2/duplicate-unused_labels.zig | 30 ++++ 8 files changed, 151 insertions(+), 119 deletions(-) rename test/compile_errors/stage1/{exe => obj}/std.fmt_error_for_unused_arguments.zig (88%) rename test/compile_errors/stage1/{test => obj}/unused_variable_error_on_errdefer.zig (100%) delete mode 100644 test/compile_errors/stage1/test/duplicate-unused_labels.zig rename test/compile_errors/{stage1/obj => stage2}/constant_inside_comptime_function_has_compile_error.zig (70%) create mode 100644 test/compile_errors/stage2/duplicate-unused_labels.zig diff --git a/build.zig b/build.zig index 7849245462..4d3cf492bd 100644 --- a/build.zig +++ b/build.zig @@ -54,7 +54,7 @@ pub fn build(b: *Builder) !void { const skip_release_safe = b.option(bool, "skip-release-safe", "Main test suite skips release-safe builds") orelse skip_release; const skip_non_native = b.option(bool, "skip-non-native", "Main test suite skips non-native builds") orelse false; const skip_libc = b.option(bool, "skip-libc", "Main test suite skips tests that link libc") orelse false; - const skip_compile_errors = b.option(bool, "skip-compile-errors", "Main test suite skips compile error tests") orelse false; + const skip_stage1 = b.option(bool, "skip-stage1", "Main test suite skips stage1 compile error tests") orelse false; const skip_run_translated_c = b.option(bool, "skip-run-translated-c", "Main test suite skips run-translated-c tests") orelse false; const skip_stage2_tests = b.option(bool, "skip-stage2-tests", "Main test suite skips self-hosted compiler tests") orelse false; const skip_install_lib_files = b.option(bool, "skip-install-lib-files", "Do not copy lib/ files to installation prefix") orelse false; @@ -386,7 +386,7 @@ pub fn build(b: *Builder) !void { test_stage2_options.addOption(bool, "enable_logging", enable_logging); test_stage2_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots); test_stage2_options.addOption(bool, "skip_non_native", skip_non_native); - test_stage2_options.addOption(bool, "skip_compile_errors", skip_compile_errors); + test_stage2_options.addOption(bool, "skip_stage1", skip_stage1); test_stage2_options.addOption(bool, "is_stage1", is_stage1); test_stage2_options.addOption(bool, "omit_stage2", omit_stage2); test_stage2_options.addOption(bool, "have_llvm", enable_llvm); diff --git a/src/test.zig b/src/test.zig index 870c0cab87..a0d0d202d1 100644 --- a/src/test.zig +++ b/src/test.zig @@ -12,7 +12,7 @@ const enable_wasmtime: bool = build_options.enable_wasmtime; const enable_darling: bool = build_options.enable_darling; const enable_rosetta: bool = build_options.enable_rosetta; const glibc_runtimes_dir: ?[]const u8 = build_options.glibc_runtimes_dir; -const skip_compile_errors = build_options.skip_compile_errors; +const skip_stage1 = build_options.skip_stage1; const ThreadPool = @import("ThreadPool.zig"); const CrossTarget = std.zig.CrossTarget; const print = std.debug.print; @@ -20,7 +20,6 @@ const assert = std.debug.assert; const zig_h = link.File.C.zig_h; -var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; const hr = "=" ** 80; test { @@ -28,9 +27,50 @@ test { @import("stage1.zig").os_init(); } - var ctx = TestContext.init(); + var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + var ctx = TestContext.init(std.testing.allocator, arena); defer ctx.deinit(); + const compile_errors_dir_path = try std.fs.path.join(arena, &.{ + std.fs.path.dirname(@src().file).?, "..", "test", "compile_errors", + }); + + var compile_errors_dir = try std.fs.cwd().openDir(compile_errors_dir_path, .{}); + defer compile_errors_dir.close(); + + { + var stage2_dir = try compile_errors_dir.openDir("stage2", .{ .iterate = true }); + defer stage2_dir.close(); + + // TODO make this incremental once the bug is solved that it triggers + ctx.addErrorCasesFromDir("stage2", stage2_dir, .stage2, .Obj, false, .independent); + } + + if (!skip_stage1) { + var stage1_dir = try compile_errors_dir.openDir("stage1", .{}); + defer stage1_dir.close(); + + const Config = struct { + name: []const u8, + is_test: bool, + output_mode: std.builtin.OutputMode, + }; + + for ([_]Config{ + .{ .name = "obj", .is_test = false, .output_mode = .Obj }, + .{ .name = "exe", .is_test = false, .output_mode = .Exe }, + .{ .name = "test", .is_test = true, .output_mode = .Exe }, + }) |config| { + var dir = try stage1_dir.openDir(config.name, .{ .iterate = true }); + defer dir.close(); + + ctx.addErrorCasesFromDir("stage1", dir, .stage1, config.output_mode, config.is_test, .independent); + } + } + try @import("test_cases").addCases(&ctx); try ctx.run(); @@ -114,6 +154,7 @@ const ErrorMsg = union(enum) { }; pub const TestContext = struct { + arena: Allocator, cases: std.ArrayList(Case), pub const Update = struct { @@ -316,7 +357,7 @@ pub const TestContext = struct { .target = target, .updates = std.ArrayList(Update).init(ctx.cases.allocator), .output_mode = .Exe, - .files = std.ArrayList(File).init(ctx.cases.allocator), + .files = std.ArrayList(File).init(ctx.arena), }) catch @panic("out of memory"); return &ctx.cases.items[ctx.cases.items.len - 1]; } @@ -327,7 +368,7 @@ pub const TestContext = struct { } pub fn exeFromCompiledC(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { - const prefixed_name = std.fmt.allocPrint(ctx.cases.allocator, "CBE: {s}", .{name}) catch + const prefixed_name = std.fmt.allocPrint(ctx.arena, "CBE: {s}", .{name}) catch @panic("out of memory"); ctx.cases.append(Case{ .name = prefixed_name, @@ -335,7 +376,7 @@ pub const TestContext = struct { .updates = std.ArrayList(Update).init(ctx.cases.allocator), .output_mode = .Exe, .object_format = .c, - .files = std.ArrayList(File).init(ctx.cases.allocator), + .files = std.ArrayList(File).init(ctx.arena), }) catch @panic("out of memory"); return &ctx.cases.items[ctx.cases.items.len - 1]; } @@ -348,7 +389,7 @@ pub const TestContext = struct { .target = target, .updates = std.ArrayList(Update).init(ctx.cases.allocator), .output_mode = .Exe, - .files = std.ArrayList(File).init(ctx.cases.allocator), + .files = std.ArrayList(File).init(ctx.arena), .backend = .llvm, .link_libc = true, }) catch @panic("out of memory"); @@ -365,7 +406,7 @@ pub const TestContext = struct { .target = target, .updates = std.ArrayList(Update).init(ctx.cases.allocator), .output_mode = .Obj, - .files = std.ArrayList(File).init(ctx.cases.allocator), + .files = std.ArrayList(File).init(ctx.arena), }) catch @panic("out of memory"); return &ctx.cases.items[ctx.cases.items.len - 1]; } @@ -381,7 +422,7 @@ pub const TestContext = struct { .updates = std.ArrayList(Update).init(ctx.cases.allocator), .output_mode = .Exe, .is_test = true, - .files = std.ArrayList(File).init(ctx.cases.allocator), + .files = std.ArrayList(File).init(ctx.arena), }) catch @panic("out of memory"); return &ctx.cases.items[ctx.cases.items.len - 1]; } @@ -404,7 +445,7 @@ pub const TestContext = struct { .updates = std.ArrayList(Update).init(ctx.cases.allocator), .output_mode = .Obj, .object_format = .c, - .files = std.ArrayList(File).init(ctx.cases.allocator), + .files = std.ArrayList(File).init(ctx.arena), }) catch @panic("out of memory"); return &ctx.cases.items[ctx.cases.items.len - 1]; } @@ -423,7 +464,7 @@ pub const TestContext = struct { src: [:0]const u8, expected_errors: []const []const u8, ) void { - if (skip_compile_errors) return; + if (skip_stage1) return; const case = ctx.addObj(name, .{}); case.backend = .stage1; @@ -436,7 +477,7 @@ pub const TestContext = struct { src: [:0]const u8, expected_errors: []const []const u8, ) void { - if (skip_compile_errors) return; + if (skip_stage1) return; const case = ctx.addTest(name, .{}); case.backend = .stage1; @@ -449,7 +490,7 @@ pub const TestContext = struct { src: [:0]const u8, expected_errors: []const []const u8, ) void { - if (skip_compile_errors) return; + if (skip_stage1) return; const case = ctx.addExe(name, .{}); case.backend = .stage1; @@ -612,6 +653,8 @@ pub const TestContext = struct { case.compiles(fixed_src); } + const Strategy = enum { incremental, independent }; + /// Adds a compile-error test for each file in the provided directory, using the /// selected backend and output mode. If `one_test_case_per_file` is true, a new /// test case is created for each file. Otherwise, a single test case is used for @@ -628,33 +671,58 @@ pub const TestContext = struct { backend: Backend, output_mode: std.builtin.OutputMode, is_test: bool, - one_test_case_per_file: bool, - ) !void { - if (skip_compile_errors) return; + strategy: Strategy, + ) void { + var current_file: []const u8 = "none"; + addErrorCasesFromDirInner(ctx, name, dir, backend, output_mode, is_test, strategy, ¤t_file) catch |err| { + std.debug.panic("test harness failed to process file '{s}': {s}\n", .{ + current_file, @errorName(err), + }); + }; + } - const gpa = general_purpose_allocator.allocator(); + fn addErrorCasesFromDirInner( + ctx: *TestContext, + name: []const u8, + dir: std.fs.Dir, + backend: Backend, + output_mode: std.builtin.OutputMode, + is_test: bool, + strategy: Strategy, + /// This is kept up to date with the currently being processed file so + /// that if any errors occur the caller knows it happened during this file. + current_file: *[]const u8, + ) !void { var opt_case: ?*Case = null; var it = dir.iterate(); while (try it.next()) |entry| { if (entry.kind != .File) continue; - var contents = try dir.readFileAlloc(gpa, entry.name, std.math.maxInt(u32)); - defer gpa.free(contents); + // Ignore stuff such as .swp files + switch (Compilation.classifyFileExt(entry.name)) { + .unknown => continue, + else => {}, + } + + current_file.* = try ctx.arena.dupe(u8, entry.name); + + const max_file_size = 10 * 1024 * 1024; + const src = try dir.readFileAllocOptions(ctx.arena, entry.name, max_file_size, null, 1, 0); // The manifest is the last contiguous block of comments in the file // We scan for the beginning by searching backward for the first non-empty line that does not start with "//" var manifest_start: ?usize = null; - var manifest_end: usize = contents.len; - if (contents.len > 0) { - var cursor: usize = contents.len - 1; + var manifest_end: usize = src.len; + if (src.len > 0) { + var cursor: usize = src.len - 1; while (true) { // Move to beginning of line - while (cursor > 0 and contents[cursor - 1] != '\n') cursor -= 1; + while (cursor > 0 and src[cursor - 1] != '\n') cursor -= 1; // Check if line is non-empty and does not start with "//" - if (cursor + 1 < contents.len and contents[cursor + 1] != '\n' and contents[cursor + 1] != '\r') { - if (std.mem.startsWith(u8, contents[cursor..], "//")) { + if (cursor + 1 < src.len and src[cursor + 1] != '\n' and src[cursor + 1] != '\r') { + if (std.mem.startsWith(u8, src[cursor..], "//")) { manifest_start = cursor; } else { break; @@ -666,27 +734,23 @@ pub const TestContext = struct { } } - var errors = std.ArrayList([]const u8).init(gpa); - defer errors.deinit(); + var errors = std.ArrayList([]const u8).init(ctx.arena); if (manifest_start) |start| { // Due to the above processing, we know that this is a contiguous block of comments - var manifest_it = std.mem.tokenize(u8, contents[start..manifest_end], "\r\n"); + var manifest_it = std.mem.tokenize(u8, src[start..manifest_end], "\r\n"); // First line is the test case name - const first_line = manifest_it.next() orelse return error.InvalidFile; - const case_name = try std.mem.concat(gpa, u8, &.{ name, ": ", std.mem.trim(u8, first_line[2..], " \t") }); + const first_line = manifest_it.next() orelse return error.MissingTestCaseName; + const case_name = try std.mem.concat(ctx.arena, u8, &.{ name, ": ", std.mem.trim(u8, first_line[2..], " \t") }); // If the second line is present, it should be blank if (manifest_it.next()) |second_line| { - if (std.mem.trim(u8, second_line[2..], " \t").len != 0) return error.InvalidFile; + if (std.mem.trim(u8, second_line[2..], " \t").len != 0) return error.SecondLineNotBlank; } // All following lines are expected error messages - while (manifest_it.next()) |line| try errors.append(try gpa.dupe(u8, std.mem.trim(u8, line[2..], " \t"))); - - // The entire file contents is the source, including the manifest - const src = try gpa.dupeZ(u8, contents); + while (manifest_it.next()) |line| try errors.append(try ctx.arena.dupe(u8, std.mem.trim(u8, line[2..], " \t"))); const case = opt_case orelse case: { ctx.cases.append(TestContext.Case{ @@ -702,22 +766,27 @@ pub const TestContext = struct { opt_case = case; break :case case; }; - if (one_test_case_per_file) { - case.name = case_name; - case.addError(src, errors.items); - opt_case = null; - } else { - case.addErrorNamed(case_name, src, errors.items); + switch (strategy) { + .independent => { + case.name = case_name; + case.addError(src, errors.items); + opt_case = null; + }, + .incremental => { + case.addErrorNamed(case_name, src, errors.items); + }, } } else { - return error.InvalidFile; // Manifests are currently mandatory + return error.MissingManifest; } } } - fn init() TestContext { - const allocator = std.heap.page_allocator; - return .{ .cases = std.ArrayList(Case).init(allocator) }; + fn init(gpa: Allocator, arena: Allocator) TestContext { + return .{ + .cases = std.ArrayList(Case).init(gpa), + .arena = arena, + }; } fn deinit(self: *TestContext) void { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 4d18d77ce4..d0e4156f18 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3,44 +3,6 @@ const builtin = @import("builtin"); const TestContext = @import("../src/test.zig").TestContext; pub fn addCases(ctx: *TestContext) !void { - var parent_dir = try std.fs.cwd().openDir(std.fs.path.dirname(@src().file).?, .{ .no_follow = true }); - defer parent_dir.close(); - - var compile_errors_dir = try parent_dir.openDir("compile_errors", .{ .no_follow = true }); - defer compile_errors_dir.close(); - - { - var stage2_dir = try compile_errors_dir.openDir("stage2", .{ .iterate = true, .no_follow = true }); - defer stage2_dir.close(); - - // TODO make this false once the bug is solved that it triggers - const one_test_case_per_file = true; - try ctx.addErrorCasesFromDir("stage2", stage2_dir, .stage2, .Obj, false, one_test_case_per_file); - } - - { - var stage1_dir = try compile_errors_dir.openDir("stage1", .{ .no_follow = true }); - defer stage1_dir.close(); - { - const one_test_case_per_file = true; - - var obj_dir = try stage1_dir.openDir("obj", .{ .iterate = true, .no_follow = true }); - defer obj_dir.close(); - - try ctx.addErrorCasesFromDir("stage1", obj_dir, .stage1, .Obj, false, one_test_case_per_file); - - var exe_dir = try stage1_dir.openDir("exe", .{ .iterate = true, .no_follow = true }); - defer exe_dir.close(); - - try ctx.addErrorCasesFromDir("stage1", exe_dir, .stage1, .Exe, false, one_test_case_per_file); - - var test_dir = try stage1_dir.openDir("test", .{ .iterate = true, .no_follow = true }); - defer test_dir.close(); - - try ctx.addErrorCasesFromDir("stage1", test_dir, .stage1, .Exe, true, one_test_case_per_file); - } - } - { const case = ctx.obj("callconv(.Interrupt) on unsupported platform", .{ .cpu_arch = .aarch64, diff --git a/test/compile_errors/stage1/exe/std.fmt_error_for_unused_arguments.zig b/test/compile_errors/stage1/obj/std.fmt_error_for_unused_arguments.zig similarity index 88% rename from test/compile_errors/stage1/exe/std.fmt_error_for_unused_arguments.zig rename to test/compile_errors/stage1/obj/std.fmt_error_for_unused_arguments.zig index 7da78d5b03..68299c2177 100644 --- a/test/compile_errors/stage1/exe/std.fmt_error_for_unused_arguments.zig +++ b/test/compile_errors/stage1/obj/std.fmt_error_for_unused_arguments.zig @@ -1,4 +1,4 @@ -pub fn main() !void { +export fn entry() void { @import("std").debug.print("{d} {d} {d} {d} {d}", .{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}); } diff --git a/test/compile_errors/stage1/test/unused_variable_error_on_errdefer.zig b/test/compile_errors/stage1/obj/unused_variable_error_on_errdefer.zig similarity index 100% rename from test/compile_errors/stage1/test/unused_variable_error_on_errdefer.zig rename to test/compile_errors/stage1/obj/unused_variable_error_on_errdefer.zig diff --git a/test/compile_errors/stage1/test/duplicate-unused_labels.zig b/test/compile_errors/stage1/test/duplicate-unused_labels.zig deleted file mode 100644 index 285741afc4..0000000000 --- a/test/compile_errors/stage1/test/duplicate-unused_labels.zig +++ /dev/null @@ -1,30 +0,0 @@ -comptime { - blk: { blk: while (false) {} } -} -comptime { - blk: while (false) { blk: for (@as([0]void, undefined)) |_| {} } -} -comptime { - blk: for (@as([0]void, undefined)) |_| { blk: {} } -} -comptime { - blk: {} -} -comptime { - blk: while(false) {} -} -comptime { - blk: for(@as([0]void, undefined)) |_| {} -} - -// duplicate/unused labels -// -// tmp.zig:2:12: error: redefinition of label 'blk' -// tmp.zig:2:5: note: previous definition here -// tmp.zig:5:26: error: redefinition of label 'blk' -// tmp.zig:5:5: note: previous definition here -// tmp.zig:8:46: error: redefinition of label 'blk' -// tmp.zig:8:5: note: previous definition here -// tmp.zig:11:5: error: unused block label -// tmp.zig:14:5: error: unused while loop label -// tmp.zig:17:5: error: unused for loop label diff --git a/test/compile_errors/stage1/obj/constant_inside_comptime_function_has_compile_error.zig b/test/compile_errors/stage2/constant_inside_comptime_function_has_compile_error.zig similarity index 70% rename from test/compile_errors/stage1/obj/constant_inside_comptime_function_has_compile_error.zig rename to test/compile_errors/stage2/constant_inside_comptime_function_has_compile_error.zig index 8eabcfdeaa..969b73713f 100644 --- a/test/compile_errors/stage1/obj/constant_inside_comptime_function_has_compile_error.zig +++ b/test/compile_errors/stage2/constant_inside_comptime_function_has_compile_error.zig @@ -2,6 +2,7 @@ const ContextAllocator = MemoryPool(usize); pub fn MemoryPool(comptime T: type) type { const free_list_t = @compileError("aoeu",); + _ = T; return struct { free_list: free_list_t, @@ -10,10 +11,10 @@ pub fn MemoryPool(comptime T: type) type { export fn entry() void { var allocator: ContextAllocator = undefined; + _ = allocator; } // constant inside comptime function has compile error // -// tmp.zig:4:5: error: unreachable code -// tmp.zig:4:25: note: control flow is diverted here -// tmp.zig:12:9: error: unused local variable +// :4:5: error: unreachable code +// :4:25: note: control flow is diverted here diff --git a/test/compile_errors/stage2/duplicate-unused_labels.zig b/test/compile_errors/stage2/duplicate-unused_labels.zig new file mode 100644 index 0000000000..82afe4e854 --- /dev/null +++ b/test/compile_errors/stage2/duplicate-unused_labels.zig @@ -0,0 +1,30 @@ +comptime { + blk: { blk: while (false) {} } +} +comptime { + blk: while (false) { blk: for (@as([0]void, undefined)) |_| {} } +} +comptime { + blk: for (@as([0]void, undefined)) |_| { blk: {} } +} +comptime { + blk: {} +} +comptime { + blk: while(false) {} +} +comptime { + blk: for(@as([0]void, undefined)) |_| {} +} + +// duplicate/unused labels +// +// :2:12: error: redefinition of label 'blk' +// :2:5: note: previous definition here +// :5:26: error: redefinition of label 'blk' +// :5:5: note: previous definition here +// :8:46: error: redefinition of label 'blk' +// :8:5: note: previous definition here +// :11:5: error: unused block label +// :14:5: error: unused while loop label +// :17:5: error: unused for loop label From ecd756834bb79b198bd560cfc11c8b63c66437ec Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 31 Mar 2022 16:06:50 -0700 Subject: [PATCH 1019/2031] CI: update CLI invokation 243afdcdf57d74a184784551aebe58062e5afc03 removed `-Dskip-compile-errors` and added `-Dskip-stage`. --- ci/srht/freebsd_script | 2 +- ci/srht/netbsd_script | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/srht/freebsd_script b/ci/srht/freebsd_script index 513707c71f..8a48097b9d 100755 --- a/ci/srht/freebsd_script +++ b/ci/srht/freebsd_script @@ -44,7 +44,7 @@ samu install #release/bin/zig test ../test/behavior.zig -fno-stage1 -fLLVM -I ../test # Here we skip some tests to save time. -release/bin/zig build test -Dskip-compile-errors -Dskip-non-native +release/bin/zig build test -Dskip-stage1 -Dskip-non-native if [ -f ~/.s3cfg ]; then mv ../LICENSE release/ diff --git a/ci/srht/netbsd_script b/ci/srht/netbsd_script index f2d2f09467..5f5b28e92f 100755 --- a/ci/srht/netbsd_script +++ b/ci/srht/netbsd_script @@ -57,7 +57,7 @@ unset CXX #release/bin/zig test ../test/behavior.zig -fno-stage1 -fLLVM -I ../test # Here we skip some tests to save time. -release/bin/zig build test -Dskip-compile-errors -Dskip-non-native +release/bin/zig build test -Dskip-stage1 -Dskip-non-native if [ -f ~/.s3cfg ]; then mv ../LICENSE release/ From 26253acf1ddcc2007804a3fbf538f3120e048547 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 31 Mar 2022 22:08:59 -0700 Subject: [PATCH 1020/2031] AstGen: use `block_inline` and `break_inline` consistently These are more efficiently semantically analyzed. More importantly, if they don't match, we get a crash in Sema. Missing places prior to this commit: * labeled blocks * `break` and `continue` on comptime (not inline) loops * `if`, `try`, `orelse`, and `catch` inside comptime scopes --- src/AstGen.zig | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 0d52a1a2bc..75882c761b 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1801,7 +1801,10 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn continue; }; - const break_tag: Zir.Inst.Tag = if (block_gz.is_inline) .break_inline else .@"break"; + const break_tag: Zir.Inst.Tag = if (block_gz.is_inline or block_gz.force_comptime) + .break_inline + else + .@"break"; if (rhs == 0) { _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); @@ -1887,7 +1890,10 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) continue; } - const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline) .break_inline else .@"break"; + const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline or gen_zir.force_comptime) + .break_inline + else + .@"break"; _ = try parent_gz.addBreak(break_tag, continue_block, .void_value); return Zir.Inst.Ref.unreachable_value; }, @@ -1993,7 +1999,8 @@ fn labeledBlockExpr( // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct // so that break statements can reference it. - const block_inst = try gz.makeBlockInst(.block, block_node); + const block_tag: Zir.Inst.Tag = if (gz.force_comptime) .block_inline else .block; + const block_inst = try gz.makeBlockInst(block_tag, block_node); try gz.instructions.append(astgen.gpa, block_inst); var block_scope = gz.makeSubBlock(parent_scope); @@ -2007,7 +2014,8 @@ fn labeledBlockExpr( try blockExprStmts(&block_scope, &block_scope.base, statements); if (!block_scope.endsWithNoReturn()) { - _ = try block_scope.addBreak(.@"break", block_inst, .void_value); + const break_tag: Zir.Inst.Tag = if (block_scope.force_comptime) .break_inline else .@"break"; + _ = try block_scope.addBreak(break_tag, block_inst, .void_value); } if (!block_scope.label.?.used) { @@ -4833,6 +4841,7 @@ fn tryExpr( try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code }); const else_result = try else_scope.addUnNode(.ret_node, err_code, node); + const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break"; return finishThenElseBlock( parent_gz, rl, @@ -4846,7 +4855,7 @@ fn tryExpr( else_result, block, block, - .@"break", + break_tag, ); } @@ -4928,6 +4937,7 @@ fn orelseCatchExpr( // instructions into place until we know whether to keep store_to_block_ptr // instructions or not. + const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break"; return finishThenElseBlock( parent_gz, rl, @@ -4941,7 +4951,7 @@ fn orelseCatchExpr( else_result, block, block, - .@"break", + break_tag, ); } @@ -5306,6 +5316,7 @@ fn ifExpr( .result = .none, }; + const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break"; return finishThenElseBlock( parent_gz, rl, @@ -5319,7 +5330,7 @@ fn ifExpr( else_info.result, block, block, - .@"break", + break_tag, ); } From b45c6c757cb4a16f5021c8bf057d14183036f14c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 31 Mar 2022 19:05:24 -0700 Subject: [PATCH 1021/2031] std.hash_map: workaround for circular dependency See #11367 It's debatable whether this ends up being a legitimate compile error or whether the lang spec allows this test case. For now this workaround seems very reasonable; delaying comptime execution of `verifyContext` until the struct is instantiated. --- lib/std/hash_map.zig | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig index 8c4e6b2581..96df243f6e 100644 --- a/lib/std/hash_map.zig +++ b/lib/std/hash_map.zig @@ -370,12 +370,15 @@ pub fn HashMap( comptime Context: type, comptime max_load_percentage: u64, ) type { - comptime verifyContext(Context, K, K, u64, false); return struct { unmanaged: Unmanaged, allocator: Allocator, ctx: Context, + comptime { + verifyContext(Context, K, K, u64, false); + } + /// The type of the unmanaged hash map underlying this wrapper pub const Unmanaged = HashMapUnmanaged(K, V, Context, max_load_percentage); /// An entry, containing pointers to a key and value stored in the map @@ -694,11 +697,13 @@ pub fn HashMapUnmanaged( ) type { if (max_load_percentage <= 0 or max_load_percentage >= 100) @compileError("max_load_percentage must be between 0 and 100."); - comptime verifyContext(Context, K, K, u64, false); - return struct { const Self = @This(); + comptime { + verifyContext(Context, K, K, u64, false); + } + // This is actually a midway pointer to the single buffer containing // a `Header` field, the `Metadata`s and `Entry`s. // At `-@sizeOf(Header)` is the Header field. From 87179d91a76470eb5e09d4e070063a2021c7f176 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 31 Mar 2022 19:07:05 -0700 Subject: [PATCH 1022/2031] stage2: hook up Sema to the progress bar --- src/Compilation.zig | 97 ++++++++++++++++++++------------------------- src/Module.zig | 5 +++ 2 files changed, 49 insertions(+), 53 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 6487273e1f..338be582d8 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -232,9 +232,6 @@ const Job = union(enum) { /// one of WASI libc static objects wasi_libc_crt_file: wasi_libc.CRTFile, - /// Use stage1 C++ code to compile zig code into an object file. - stage1_module: void, - /// The value is the index into `link.File.Options.system_libs`. windows_import_lib: usize, }; @@ -1831,10 +1828,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { } } - if (comp.bin_file.options.use_stage1 and comp.bin_file.options.module != null) { - try comp.work_queue.writeItem(.{ .stage1_module = {} }); - } - return comp; } @@ -2597,13 +2590,13 @@ pub fn getCompileLogOutput(self: *Compilation) []const u8 { return module.compile_log_text.items; } -pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemory }!void { +pub fn performAllTheWork(comp: *Compilation) error{ TimerUnsupported, OutOfMemory }!void { // If the terminal is dumb, we dont want to show the user all the // output. var progress: std.Progress = .{ .dont_print_on_dumb = true }; var main_progress_node = progress.start("", 0); defer main_progress_node.end(); - if (self.color == .off) progress.terminal = null; + if (comp.color == .off) progress.terminal = null; // Here we queue up all the AstGen tasks first, followed by C object compilation. // We wait until the AstGen tasks are all completed before proceeding to the @@ -2613,93 +2606,105 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor var zir_prog_node = main_progress_node.start("AST Lowering", 0); defer zir_prog_node.end(); - var c_obj_prog_node = main_progress_node.start("Compile C Objects", self.c_source_files.len); + var c_obj_prog_node = main_progress_node.start("Compile C Objects", comp.c_source_files.len); defer c_obj_prog_node.end(); - var embed_file_prog_node = main_progress_node.start("Detect @embedFile updates", self.embed_file_work_queue.count); + var embed_file_prog_node = main_progress_node.start("Detect @embedFile updates", comp.embed_file_work_queue.count); defer embed_file_prog_node.end(); - self.work_queue_wait_group.reset(); - defer self.work_queue_wait_group.wait(); + comp.work_queue_wait_group.reset(); + defer comp.work_queue_wait_group.wait(); { const astgen_frame = tracy.namedFrame("astgen"); defer astgen_frame.end(); - self.astgen_wait_group.reset(); - defer self.astgen_wait_group.wait(); + comp.astgen_wait_group.reset(); + defer comp.astgen_wait_group.wait(); // builtin.zig is handled specially for two reasons: // 1. to avoid race condition of zig processes truncating each other's builtin.zig files // 2. optimization; in the hot path it only incurs a stat() syscall, which happens // in the `astgen_wait_group`. - if (self.bin_file.options.module) |mod| { + if (comp.bin_file.options.module) |mod| { if (mod.job_queued_update_builtin_zig) { mod.job_queued_update_builtin_zig = false; - self.astgen_wait_group.start(); - try self.thread_pool.spawn(workerUpdateBuiltinZigFile, .{ - self, mod, &self.astgen_wait_group, + comp.astgen_wait_group.start(); + try comp.thread_pool.spawn(workerUpdateBuiltinZigFile, .{ + comp, mod, &comp.astgen_wait_group, }); } } - while (self.astgen_work_queue.readItem()) |file| { - self.astgen_wait_group.start(); - try self.thread_pool.spawn(workerAstGenFile, .{ - self, file, &zir_prog_node, &self.astgen_wait_group, .root, + while (comp.astgen_work_queue.readItem()) |file| { + comp.astgen_wait_group.start(); + try comp.thread_pool.spawn(workerAstGenFile, .{ + comp, file, &zir_prog_node, &comp.astgen_wait_group, .root, }); } - while (self.embed_file_work_queue.readItem()) |embed_file| { - self.astgen_wait_group.start(); - try self.thread_pool.spawn(workerCheckEmbedFile, .{ - self, embed_file, &embed_file_prog_node, &self.astgen_wait_group, + while (comp.embed_file_work_queue.readItem()) |embed_file| { + comp.astgen_wait_group.start(); + try comp.thread_pool.spawn(workerCheckEmbedFile, .{ + comp, embed_file, &embed_file_prog_node, &comp.astgen_wait_group, }); } - while (self.c_object_work_queue.readItem()) |c_object| { - self.work_queue_wait_group.start(); - try self.thread_pool.spawn(workerUpdateCObject, .{ - self, c_object, &c_obj_prog_node, &self.work_queue_wait_group, + while (comp.c_object_work_queue.readItem()) |c_object| { + comp.work_queue_wait_group.start(); + try comp.thread_pool.spawn(workerUpdateCObject, .{ + comp, c_object, &c_obj_prog_node, &comp.work_queue_wait_group, }); } } - const use_stage1 = build_options.is_stage1 and self.bin_file.options.use_stage1; + const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1; if (!use_stage1) { const outdated_and_deleted_decls_frame = tracy.namedFrame("outdated_and_deleted_decls"); defer outdated_and_deleted_decls_frame.end(); // Iterate over all the files and look for outdated and deleted declarations. - if (self.bin_file.options.module) |mod| { + if (comp.bin_file.options.module) |mod| { try mod.processOutdatedAndDeletedDecls(); } - } else if (self.bin_file.options.module) |mod| { + } else if (comp.bin_file.options.module) |mod| { // If there are any AstGen compile errors, report them now to avoid // hitting stage1 bugs. if (mod.failed_files.count() != 0) { return; } + comp.updateStage1Module(main_progress_node) catch |err| { + fatal("unable to build stage1 zig object: {s}", .{@errorName(err)}); + }; } + if (comp.bin_file.options.module) |mod| { + mod.sema_prog_node = main_progress_node.start("Semantic Analysis", 0); + mod.sema_prog_node.activate(); + } + defer if (comp.bin_file.options.module) |mod| { + mod.sema_prog_node.end(); + mod.sema_prog_node = undefined; + }; + // In this main loop we give priority to non-anonymous Decls in the work queue, so // that they can establish references to anonymous Decls, setting alive=true in the // backend, preventing anonymous Decls from being prematurely destroyed. while (true) { - if (self.work_queue.readItem()) |work_item| { - try processOneJob(self, work_item, main_progress_node); + if (comp.work_queue.readItem()) |work_item| { + try processOneJob(comp, work_item); continue; } - if (self.anon_work_queue.readItem()) |work_item| { - try processOneJob(self, work_item, main_progress_node); + if (comp.anon_work_queue.readItem()) |work_item| { + try processOneJob(comp, work_item); continue; } break; } } -fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress.Node) !void { +fn processOneJob(comp: *Compilation, job: Job) !void { switch (job) { .codegen_decl => |decl| switch (decl.analysis) { .unreferenced => unreachable, @@ -2807,9 +2812,6 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress if (build_options.omit_stage2) @panic("sadly stage2 is omitted from this build to save memory on the CI server"); - const named_frame = tracy.namedFrame("analyze_decl"); - defer named_frame.end(); - const module = comp.bin_file.options.module.?; module.ensureDeclAnalyzed(decl) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, @@ -3074,17 +3076,6 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress ), }; }, - .stage1_module => { - const named_frame = tracy.namedFrame("stage1_module"); - defer named_frame.end(); - - if (!build_options.is_stage1) - unreachable; - - comp.updateStage1Module(main_progress_node) catch |err| { - fatal("unable to build stage1 zig object: {s}", .{@errorName(err)}); - }; - }, } } diff --git a/src/Module.zig b/src/Module.zig index 1fd83108ff..154c7426d2 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -42,6 +42,7 @@ root_pkg: *Package, /// Normally, `main_pkg` and `root_pkg` are the same. The exception is `zig test`, in which /// `root_pkg` is the test runner, and `main_pkg` is the user's source file which has the tests. main_pkg: *Package, +sema_prog_node: std.Progress.Node = undefined, /// Used by AstGen worker to load and store ZIR cache. global_zir_cache: Compilation.Directory, @@ -3517,6 +3518,10 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void { .unreferenced => false, }; + var decl_prog_node = mod.sema_prog_node.start(mem.sliceTo(decl.name, 0), 0); + decl_prog_node.activate(); + defer decl_prog_node.end(); + const type_changed = mod.semaDecl(decl) catch |err| switch (err) { error.AnalysisFail => { if (decl.analysis == .in_progress) { From fd29ddc06c914a994ecb41714a7c4470ceda5af3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 31 Mar 2022 19:41:50 +0200 Subject: [PATCH 1023/2031] x64: implement add/sub with wrapping and xor op --- src/arch/x86_64/CodeGen.zig | 28 +++++----------------------- test/behavior/math.zig | 5 ----- 2 files changed, 5 insertions(+), 28 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index b093209a2e..0a11c1480f 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -645,13 +645,13 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off .add => try self.airAdd(inst), - .addwrap => try self.airAddWrap(inst), + .addwrap => try self.airAdd(inst), .add_sat => try self.airAddSat(inst), .sub => try self.airSub(inst), - .subwrap => try self.airSubWrap(inst), + .subwrap => try self.airSub(inst), .sub_sat => try self.airSubSat(inst), .mul => try self.airMul(inst), - .mulwrap => try self.airMulWrap(inst), + .mulwrap => try self.airMul(inst), .mul_sat => try self.airMulSat(inst), .rem => try self.airRem(inst), .mod => try self.airMod(inst), @@ -1318,7 +1318,7 @@ fn airAddWrap(self: *Self, inst: Air.Inst.Index) !void { const result: MCValue = if (self.liveness.isUnused(inst)) .dead else - return self.fail("TODO implement addwrap for {}", .{self.target.cpu.arch}); + try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1375,15 +1375,6 @@ fn airSub(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airSubWrap(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 subwrap for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airSubSat(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)) @@ -1421,15 +1412,6 @@ fn airMul(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airMulWrap(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 mulwrap for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airMulSat(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)) @@ -1826,7 +1808,7 @@ fn airXor(self: *Self, inst: Air.Inst.Index) !void { const result: MCValue = if (self.liveness.isUnused(inst)) .dead else - return self.fail("TODO implement xor for {}", .{self.target.cpu.arch}); + try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } diff --git a/test/behavior/math.zig b/test/behavior/math.zig index a79d20dcdb..7a388a0983 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -320,7 +320,6 @@ test "comptime_int multi-limb partial shift right" { test "xor" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO try test_xor(); comptime try test_xor(); @@ -502,7 +501,6 @@ fn mod(comptime T: type, a: T, b: T) T { } test "unsigned wrapping" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testUnsignedWrappingEval(maxInt(u32)); @@ -516,7 +514,6 @@ fn testUnsignedWrappingEval(x: u32) !void { } test "signed wrapping" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testSignedWrappingEval(maxInt(i32)); @@ -530,7 +527,6 @@ fn testSignedWrappingEval(x: i32) !void { } test "signed negation wrapping" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testSignedNegationWrappingEval(minInt(i16)); @@ -543,7 +539,6 @@ fn testSignedNegationWrappingEval(x: i16) !void { } test "unsigned negation wrapping" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testUnsignedNegationWrappingEval(1); From 8b5d5f44e23c86082fba480f01092f2f7ffb3dfe Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 1 Apr 2022 14:33:37 +0200 Subject: [PATCH 1024/2031] macho: set CS_LINKER_SIGNED flag in code signature generated by zld This way, if the user wants to use `codesign` (or other tool) they will not be forced to `-f` force signature update. This matches the behavior promoted by Apple's `ld64` linker. --- lib/std/macho.zig | 1 + src/link/MachO/CodeSignature.zig | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/macho.zig b/lib/std/macho.zig index 0abe14f4a4..1f8b7e8f33 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -1718,6 +1718,7 @@ pub const CS_SIGNER_TYPE_LEGACYVPN: u32 = 5; pub const CS_SIGNER_TYPE_MAC_APP_STORE: u32 = 6; pub const CS_ADHOC: u32 = 0x2; +pub const CS_LINKER_SIGNED: u32 = 0x20000; pub const CS_EXECSEG_MAIN_BINARY: u32 = 0x1; diff --git a/src/link/MachO/CodeSignature.zig b/src/link/MachO/CodeSignature.zig index 6f2d516bf5..6c9656c3bf 100644 --- a/src/link/MachO/CodeSignature.zig +++ b/src/link/MachO/CodeSignature.zig @@ -60,7 +60,7 @@ const CodeDirectory = struct { .magic = macho.CSMAGIC_CODEDIRECTORY, .length = @sizeOf(macho.CodeDirectory), .version = macho.CS_SUPPORTSEXECSEG, - .flags = macho.CS_ADHOC, + .flags = macho.CS_ADHOC | macho.CS_LINKER_SIGNED, .hashOffset = 0, .identOffset = @sizeOf(macho.CodeDirectory), .nSpecialSlots = 0, From e2e69803dc16efe11a6d42c6c49853e16a41fd0c Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sun, 27 Mar 2022 15:06:37 +0200 Subject: [PATCH 1025/2031] stage2 ARM: change binOp lowering mechanism to use Mir tags The Air -> Mir correspondence is not 1:1, so this better represents what Mir insruction we actually want to generate. --- src/arch/arm/CodeGen.zig | 140 ++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 75 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 85545e33a5..d671fc5004 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1196,7 +1196,7 @@ fn minMax( // register. assert(lhs_reg != rhs_reg); // see note above - _ = try self.binOpRegister(.cmp_eq, null, .{ .register = lhs_reg }, .{ .register = rhs_reg }, lhs_ty, rhs_ty); + _ = try self.binOpRegister(.cmp, null, .{ .register = lhs_reg }, .{ .register = rhs_reg }, lhs_ty, rhs_ty); const cond_choose_lhs: Condition = switch (tag) { .max => switch (int_info.signedness) { @@ -2067,7 +2067,7 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { /// Asserts that generating an instruction of that form is possible. fn binOpRegister( self: *Self, - tag: Air.Inst.Tag, + mir_tag: Mir.Inst.Tag, maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, @@ -2112,8 +2112,8 @@ fn binOpRegister( }; defer self.register_manager.unfreezeRegs(&.{rhs_reg}); - const dest_reg = switch (tag) { - .cmp_eq => .r0, // cmp has no destination regardless + const dest_reg = switch (mir_tag) { + .cmp => .r0, // cmp has no destination regardless else => if (maybe_inst) |inst| blk: { const bin_op = self.air.instructions.items(.data)[inst].bin_op; @@ -2130,41 +2130,21 @@ fn binOpRegister( if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); - const mir_tag: Mir.Inst.Tag = switch (tag) { - .add => .add, - .sub => .sub, - .cmp_eq => .cmp, - .mul => .mul, - .bit_and, - .bool_and, - => .@"and", - .bit_or, - .bool_or, - => .orr, - .shl_exact => .lsl, - .shr_exact => switch (lhs_ty.intInfo(self.target.*).signedness) { - .signed => Mir.Inst.Tag.asr, - .unsigned => Mir.Inst.Tag.lsr, - }, - .xor => .eor, - else => unreachable, - }; - const mir_data: Mir.Inst.Data = switch (tag) { + const mir_data: Mir.Inst.Data = switch (mir_tag) { .add, .sub, - .cmp_eq, - .bit_and, - .bool_and, - .bit_or, - .bool_or, - .xor, + .cmp, + .@"and", + .orr, + .eor, => .{ .rr_op = .{ .rd = dest_reg, .rn = lhs_reg, .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none), } }, - .shl_exact, - .shr_exact, + .lsl, + .asr, + .lsr, => .{ .rr_shift = .{ .rd = dest_reg, .rm = lhs_reg, @@ -2200,7 +2180,7 @@ fn binOpRegister( /// Asserts that generating an instruction of that form is possible. fn binOpImmediate( self: *Self, - tag: Air.Inst.Tag, + mir_tag: Mir.Inst.Tag, maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, @@ -2230,8 +2210,8 @@ fn binOpImmediate( }; defer self.register_manager.unfreezeRegs(&.{lhs_reg}); - const dest_reg = switch (tag) { - .cmp_eq => .r0, // cmp has no destination reg + const dest_reg = switch (mir_tag) { + .cmp => .r0, // cmp has no destination reg else => if (maybe_inst) |inst| blk: { const bin_op = self.air.instructions.items(.data)[inst].bin_op; @@ -2250,40 +2230,21 @@ fn binOpImmediate( if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); - const mir_tag: Mir.Inst.Tag = switch (tag) { - .add => .add, - .sub => .sub, - .cmp_eq => .cmp, - .bit_and, - .bool_and, - => .@"and", - .bit_or, - .bool_or, - => .orr, - .shl_exact => .lsl, - .shr_exact => switch (lhs_ty.intInfo(self.target.*).signedness) { - .signed => Mir.Inst.Tag.asr, - .unsigned => Mir.Inst.Tag.lsr, - }, - .xor => .eor, - else => unreachable, - }; - const mir_data: Mir.Inst.Data = switch (tag) { + const mir_data: Mir.Inst.Data = switch (mir_tag) { .add, .sub, - .cmp_eq, - .bit_and, - .bool_and, - .bit_or, - .bool_or, - .xor, + .cmp, + .@"and", + .orr, + .eor, => .{ .rr_op = .{ .rd = dest_reg, .rn = lhs_reg, .op = Instruction.Operand.fromU32(rhs.immediate).?, } }, - .shl_exact, - .shr_exact, + .lsl, + .asr, + .lsr, => .{ .rr_shift = .{ .rd = dest_reg, .rm = lhs_reg, @@ -2352,13 +2313,20 @@ fn binOp( else => unreachable, }; + const mir_tag: Mir.Inst.Tag = switch (tag) { + .add => .add, + .sub => .sub, + .cmp_eq => .cmp, + else => unreachable, + }; + if (rhs_immediate_ok) { - return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); } else if (lhs_immediate_ok) { // swap lhs and rhs - return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true); + return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true); } else { - return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); } } else { return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); @@ -2378,7 +2346,7 @@ fn binOp( // TODO add optimisations for multiplication // with immediates, for example a * 2 can be // lowered to a << 1 - return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(.mul, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); } else { return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); } @@ -2432,13 +2400,20 @@ fn binOp( const lhs_immediate_ok = lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null; const rhs_immediate_ok = rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null; + const mir_tag: Mir.Inst.Tag = switch (tag) { + .bit_and => .@"and", + .bit_or => .orr, + .xor => .eor, + else => unreachable, + }; + if (rhs_immediate_ok) { - return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); } else if (lhs_immediate_ok) { // swap lhs and rhs - return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true); + return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true); } else { - return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); } } else { return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); @@ -2457,10 +2432,19 @@ fn binOp( if (int_info.bits <= 32) { const rhs_immediate_ok = rhs == .immediate; + const mir_tag: Mir.Inst.Tag = switch (tag) { + .shl_exact => .lsl, + .shr_exact => switch (lhs_ty.intInfo(self.target.*).signedness) { + .signed => Mir.Inst.Tag.asr, + .unsigned => Mir.Inst.Tag.lsr, + }, + else => unreachable, + }; + if (rhs_immediate_ok) { - return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); } else { - return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); } } else { return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); @@ -2512,13 +2496,19 @@ fn binOp( const lhs_immediate_ok = lhs == .immediate; const rhs_immediate_ok = rhs == .immediate; + const mir_tag: Mir.Inst.Tag = switch (tag) { + .bool_and => .@"and", + .bool_or => .orr, + else => unreachable, + }; + if (rhs_immediate_ok) { - return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); } else if (lhs_immediate_ok) { // swap lhs and rhs - return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true); + return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true); } else { - return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); } }, else => unreachable, @@ -2537,7 +2527,7 @@ fn binOp( const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*)); if (elem_size == 1) { - const base_tag: Air.Inst.Tag = switch (tag) { + const base_tag: Mir.Inst.Tag = switch (tag) { .ptr_add => .add, .ptr_sub => .sub, else => unreachable, From 7285f0557cbc26893a7a64f345432ad01981180c Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sun, 27 Mar 2022 21:30:16 +0200 Subject: [PATCH 1026/2031] stage2 ARM: implement add/sub_with_overflow for u32/i32 --- src/arch/arm/CodeGen.zig | 228 +++++++++++++++++++++++++++++++++------ src/arch/arm/Emit.zig | 4 + src/arch/arm/Mir.zig | 4 + 3 files changed, 202 insertions(+), 34 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index d671fc5004..3484103ced 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -105,9 +105,12 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; const MCValue = union(enum) { - /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. - /// TODO Look into deleting this tag and using `dead` instead, since every use - /// of MCValue.none should be instead looking at the type and noticing it is 0 bits. + /// No runtime bits. `void` types, empty structs, u0, enums with 1 + /// tag, etc. + /// + /// TODO Look into deleting this tag and using `dead` instead, + /// since every use of MCValue.none should be instead looking at + /// the type and noticing it is 0 bits. none, /// Control flow will not allow this value to be observed. unreach, @@ -116,20 +119,41 @@ const MCValue = union(enum) { /// The value is undefined. undef, /// A pointer-sized integer that fits in a register. - /// If the type is a pointer, this is the pointer address in virtual address space. + /// + /// If the type is a pointer, this is the pointer address in + /// virtual address space. immediate: u32, /// The value is in a target-specific register. register: Register, + /// The value is a tuple { wrapped: u32, overflow: u1 } where + /// wrapped is stored in the register and the overflow bit is + /// stored in the C flag of the CPSR. + /// + /// This MCValue is only generated by a add_with_overflow or + /// sub_with_overflow instruction operating on u32. + register_c_flag: Register, + /// The value is a tuple { wrapped: i32, overflow: u1 } where + /// wrapped is stored in the register and the overflow bit is + /// stored in the V flag of the CPSR. + /// + /// This MCValue is only generated by a add_with_overflow or + /// sub_with_overflow instruction operating on i32. + register_v_flag: Register, /// The value is in memory at a hard-coded address. - /// If the type is a pointer, it means the pointer address is at this memory location. + /// + /// If the type is a pointer, it means the pointer address is at + /// this memory location. memory: u64, /// The value is one of the stack variables. - /// If the type is a pointer, it means the pointer address is in the stack at this offset. + /// + /// If the type is a pointer, it means the pointer address is in + /// the stack at this offset. stack_offset: u32, - /// The value is a pointer to one of the stack variables (payload is stack offset). + /// The value is a pointer to one of the stack variables (payload + /// is stack offset). ptr_stack_offset: u32, - /// The value is in the compare flags assuming an unsigned operation, - /// with this operator applied on top of it. + /// The value is in the compare flags assuming an unsigned + /// operation, with this operator applied on top of it. compare_flags_unsigned: math.CompareOperator, /// The value is in the compare flags assuming a signed operation, /// with this operator applied on top of it. @@ -554,8 +578,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .trunc_float, => try self.airUnaryMath(inst), - .add_with_overflow => try self.airAddWithOverflow(inst), - .sub_with_overflow => try self.airSubWithOverflow(inst), + .add_with_overflow => try self.airOverflow(inst), + .sub_with_overflow => try self.airOverflow(inst), .mul_with_overflow => try self.airMulWithOverflow(inst), .shl_with_overflow => try self.airShlWithOverflow(inst), @@ -726,6 +750,12 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { .register => |reg| { self.register_manager.freeReg(reg); }, + .register_c_flag, + .register_v_flag, + => |reg| { + self.register_manager.freeReg(reg); + self.compare_flags_inst = null; + }, .compare_flags_signed, .compare_flags_unsigned => { self.compare_flags_inst = null; }, @@ -841,8 +871,16 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { const stack_mcv = try self.allocRegOrMem(inst, false); log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv }); + const reg_mcv = self.getResolvedInstValue(inst); - assert(reg == reg_mcv.register); + switch (reg_mcv) { + .register, + .register_c_flag, + .register_v_flag, + => |r| assert(r == reg), + else => unreachable, // not a register + } + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; try branch.inst_table.put(self.gpa, inst, stack_mcv); try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); @@ -853,7 +891,14 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void fn spillCompareFlagsIfOccupied(self: *Self) !void { if (self.compare_flags_inst) |inst_to_save| { const mcv = self.getResolvedInstValue(inst_to_save); - assert(mcv == .compare_flags_signed or mcv == .compare_flags_unsigned); + switch (mcv) { + .compare_flags_signed, + .compare_flags_unsigned, + .register_c_flag, + .register_v_flag, + => {}, + else => unreachable, // mcv doesn't occupy the compare flags + } const new_mcv = try self.allocRegOrMem(inst_to_save, true); try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); @@ -1268,7 +1313,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { const len = try self.resolveInst(bin_op.rhs); const len_ty = self.air.typeOf(bin_op.rhs); - const stack_offset = try self.allocMem(inst, 8, 8); + const stack_offset = try self.allocMem(inst, 8, 4); try self.genSetStack(ptr_ty, stack_offset, ptr); try self.genSetStack(len_ty, stack_offset - 4, len); break :result MCValue{ .stack_offset = stack_offset }; @@ -1306,14 +1351,71 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch}); -} +fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { + const tag = self.air.instructions.items(.tag)[inst]; + 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 result: { + const lhs = try self.resolveInst(extra.lhs); + const rhs = try self.resolveInst(extra.rhs); + const lhs_ty = self.air.typeOf(extra.lhs); + const rhs_ty = self.air.typeOf(extra.rhs); -fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch}); + switch (lhs_ty.zigTypeTag()) { + .Vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}), + .Int => { + assert(lhs_ty.eql(rhs_ty, self.target.*)); + const int_info = lhs_ty.intInfo(self.target.*); + if (int_info.bits < 32) { + return self.fail("TODO ARM overflow operations on integers < u32/i32", .{}); + } else if (int_info.bits == 32) { + // Only say yes if the operation is + // commutative, i.e. we can swap both of the + // operands + const lhs_immediate_ok = switch (tag) { + .add_with_overflow => lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null, + .sub_with_overflow => false, + else => unreachable, + }; + const rhs_immediate_ok = switch (tag) { + .add_with_overflow, + .sub_with_overflow, + => rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null, + else => unreachable, + }; + + const mir_tag: Mir.Inst.Tag = switch (tag) { + .add_with_overflow => .adds, + .sub_with_overflow => .subs, + else => unreachable, + }; + + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = inst; + + const dest = blk: { + if (rhs_immediate_ok) { + break :blk try self.binOpImmediate(mir_tag, null, lhs, rhs, lhs_ty, false); + } else if (lhs_immediate_ok) { + // swap lhs and rhs + break :blk try self.binOpImmediate(mir_tag, null, rhs, lhs, rhs_ty, true); + } else { + break :blk try self.binOpRegister(mir_tag, null, lhs, rhs, lhs_ty, rhs_ty); + } + }; + + switch (int_info.signedness) { + .unsigned => break :result MCValue{ .register_c_flag = dest.register }, + .signed => break :result MCValue{ .register_v_flag = dest.register }, + } + } else { + return self.fail("TODO ARM overflow operations on integers > u32/i32", .{}); + } + }, + else => unreachable, + } + }; + return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); } fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { @@ -1424,7 +1526,6 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) const eu_align = @intCast(u32, error_union_ty.abiAlignment(self.target.*)); const offset = std.mem.alignForwardGeneric(u32, error_size, eu_align); - // TODO optimization for small error unions: put into register switch (error_union_mcv) { .register => return self.fail("TODO errUnionPayload for registers", .{}), .stack_argument_offset => |off| { @@ -1791,8 +1892,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .undef => unreachable, .unreach => unreachable, .dead => unreachable, - .compare_flags_unsigned => unreachable, - .compare_flags_signed => unreachable, + .compare_flags_unsigned, + .compare_flags_signed, + .register_c_flag, + .register_v_flag, + => unreachable, // cannot hold an address .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), .register => |reg| { @@ -1887,8 +1991,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .undef => unreachable, .unreach => unreachable, .dead => unreachable, - .compare_flags_unsigned => unreachable, - .compare_flags_signed => unreachable, + .compare_flags_unsigned, + .compare_flags_signed, + .register_c_flag, + .register_v_flag, + => unreachable, // cannot hold an address .immediate => |imm| { try self.setRegOrMem(value_ty, .{ .memory = imm }, value); }, @@ -2043,6 +2150,50 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { .memory => |addr| { break :result MCValue{ .memory = addr + struct_field_offset }; }, + .register_c_flag, + .register_v_flag, + => |reg| { + switch (index) { + 0 => { + // get wrapped value: return register + break :result MCValue{ .register = reg }; + }, + 1 => { + // get overflow bit: set register to C flag + // resp. V flag + const dest_reg = try self.register_manager.allocReg(null); + + // mov reg, #0 + _ = try self.addInst(.{ + .tag = .mov, + .data = .{ .rr_op = .{ + .rd = dest_reg, + .rn = .r0, + .op = Instruction.Operand.fromU32(0).?, + } }, + }); + + // C flag: movcs reg, #1 + // V flag: movvs reg, #1 + _ = try self.addInst(.{ + .tag = .mov, + .cond = switch (mcv) { + .register_c_flag => .cs, + .register_v_flag => .vs, + else => unreachable, + }, + .data = .{ .rr_op = .{ + .rd = dest_reg, + .rn = .r0, + .op = Instruction.Operand.fromU32(1).?, + } }, + }); + + break :result MCValue{ .register = dest_reg }; + }, + else => unreachable, + } + }, else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), } }; @@ -2132,7 +2283,9 @@ fn binOpRegister( const mir_data: Mir.Inst.Data = switch (mir_tag) { .add, + .adds, .sub, + .subs, .cmp, .@"and", .orr, @@ -2232,7 +2385,9 @@ fn binOpImmediate( const mir_data: Mir.Inst.Data = switch (mir_tag) { .add, + .adds, .sub, + .subs, .cmp, .@"and", .orr, @@ -2814,14 +2969,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. switch (mc_arg) { .none => continue, - .undef => unreachable, - .immediate => unreachable, - .unreach => unreachable, - .dead => unreachable, - .memory => unreachable, - .compare_flags_signed => unreachable, - .compare_flags_unsigned => unreachable, - .ptr_stack_offset => unreachable, .register => |reg| { try self.register_manager.getReg(reg, null); try self.genSetReg(arg_ty, reg, arg_mcv); @@ -2832,6 +2979,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. info.stack_byte_count - offset, arg_mcv, ), + else => unreachable, } } @@ -3774,6 +3922,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), } }, + .register_c_flag, + .register_v_flag, + => { + return self.fail("TODO implement genSetStack {}", .{mcv}); + }, .memory, .stack_argument_offset, .stack_offset, @@ -4015,6 +4168,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void } }, }); }, + .register_c_flag => unreachable, // doesn't fit into a register + .register_v_flag => unreachable, // doesn't fit into a register .memory => |addr| { // The value is in memory at a hard-coded address. // If the type is a pointer, it means the pointer address is at this memory location. @@ -4149,6 +4304,11 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), } }, + .register_c_flag, + .register_v_flag, + => { + return self.fail("TODO implement genSetStack {}", .{mcv}); + }, .stack_offset, .memory, .stack_argument_offset, diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index 45bcad485e..dfa0fabb2e 100644 --- a/src/arch/arm/Emit.zig +++ b/src/arch/arm/Emit.zig @@ -79,6 +79,7 @@ pub fn emitMir( const inst = @intCast(u32, index); switch (tag) { .add => try emit.mirDataProcessing(inst), + .adds => try emit.mirDataProcessing(inst), .@"and" => try emit.mirDataProcessing(inst), .cmp => try emit.mirDataProcessing(inst), .eor => try emit.mirDataProcessing(inst), @@ -87,6 +88,7 @@ pub fn emitMir( .orr => try emit.mirDataProcessing(inst), .rsb => try emit.mirDataProcessing(inst), .sub => try emit.mirDataProcessing(inst), + .subs => try emit.mirDataProcessing(inst), .asr => try emit.mirShift(inst), .lsl => try emit.mirShift(inst), @@ -474,6 +476,7 @@ fn mirDataProcessing(emit: *Emit, inst: Mir.Inst.Index) !void { switch (tag) { .add => try emit.writeInstruction(Instruction.add(cond, rr_op.rd, rr_op.rn, rr_op.op)), + .adds => try emit.writeInstruction(Instruction.adds(cond, rr_op.rd, rr_op.rn, rr_op.op)), .@"and" => try emit.writeInstruction(Instruction.@"and"(cond, rr_op.rd, rr_op.rn, rr_op.op)), .cmp => try emit.writeInstruction(Instruction.cmp(cond, rr_op.rn, rr_op.op)), .eor => try emit.writeInstruction(Instruction.eor(cond, rr_op.rd, rr_op.rn, rr_op.op)), @@ -482,6 +485,7 @@ fn mirDataProcessing(emit: *Emit, inst: Mir.Inst.Index) !void { .orr => try emit.writeInstruction(Instruction.orr(cond, rr_op.rd, rr_op.rn, rr_op.op)), .rsb => try emit.writeInstruction(Instruction.rsb(cond, rr_op.rd, rr_op.rn, rr_op.op)), .sub => try emit.writeInstruction(Instruction.sub(cond, rr_op.rd, rr_op.rn, rr_op.op)), + .subs => try emit.writeInstruction(Instruction.sub(cond, rr_op.rd, rr_op.rn, rr_op.op)), else => unreachable, } } diff --git a/src/arch/arm/Mir.zig b/src/arch/arm/Mir.zig index c6fdfd3051..496042d674 100644 --- a/src/arch/arm/Mir.zig +++ b/src/arch/arm/Mir.zig @@ -28,6 +28,8 @@ pub const Inst = struct { pub const Tag = enum(u16) { /// Add add, + /// Add, update condition flags + adds, /// Bitwise AND @"and", /// Arithmetic Shift Right @@ -108,6 +110,8 @@ pub const Inst = struct { strh, /// Subtract sub, + /// Subtract, update condition flags + subs, /// Supervisor Call svc, /// Unsigned Bit Field Extract From 37a8c28802b418718487995a1fa6000b0aab8a84 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Tue, 29 Mar 2022 20:19:25 +0200 Subject: [PATCH 1027/2031] stage2 ARM: implement add/sub_with_overflow for ints < 32 bits --- src/arch/arm/CodeGen.zig | 35 ++++++++++++++++++++++++++++++++++- test/behavior/math.zig | 2 -- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 3484103ced..360b3ad97b 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1361,13 +1361,46 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { const lhs_ty = self.air.typeOf(extra.lhs); const rhs_ty = self.air.typeOf(extra.rhs); + const tuple_ty = self.air.typeOfIndex(inst); + const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); + const tuple_align = tuple_ty.abiAlignment(self.target.*); + const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*)); + switch (lhs_ty.zigTypeTag()) { .Vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}), .Int => { assert(lhs_ty.eql(rhs_ty, self.target.*)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits < 32) { - return self.fail("TODO ARM overflow operations on integers < u32/i32", .{}); + const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); + + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = null; + + const base_tag: Air.Inst.Tag = switch (tag) { + .add_with_overflow => .add, + .sub_with_overflow => .sub, + else => unreachable, + }; + const dest = try self.binOp(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); + const dest_reg = dest.register; + self.register_manager.freezeRegs(&.{dest_reg}); + defer self.register_manager.unfreezeRegs(&.{dest_reg}); + + const truncated_reg = try self.register_manager.allocReg(null); + self.register_manager.freezeRegs(&.{truncated_reg}); + defer self.register_manager.unfreezeRegs(&.{truncated_reg}); + + // sbfx/ubfx truncated, dest, #0, #bits + try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); + + // cmp dest, truncated + _ = try self.binOp(.cmp_eq, null, dest, .{ .register = truncated_reg }, Type.usize, Type.usize); + + try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); + try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); + + break :result MCValue{ .stack_offset = stack_offset }; } else if (int_info.bits == 32) { // Only say yes if the operation is // commutative, i.e. we can swap both of the diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 7a388a0983..3ede976ef8 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -635,7 +635,6 @@ test "128-bit multiplication" { test "@addWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO var result: u8 = undefined; try expect(@addWithOverflow(u8, 250, 100, &result)); @@ -700,7 +699,6 @@ test "@mulWithOverflow" { test "@subWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO var result: u8 = undefined; try expect(@subWithOverflow(u8, 1, 2, &result)); From 77e70189f438316a8d4e48b2457be0b5eb5974f3 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Wed, 30 Mar 2022 11:09:05 +0200 Subject: [PATCH 1028/2031] stage2 ARM: implement shl_with_overflow for ints <= 32 bits --- src/arch/arm/CodeGen.zig | 50 ++++++++++++++++++++++++++++++++++++++-- test/behavior/math.zig | 1 - 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 360b3ad97b..00d82349eb 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1457,8 +1457,54 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { } fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch}); + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; + if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); + const result: MCValue = result: { + const lhs = try self.resolveInst(extra.lhs); + const rhs = try self.resolveInst(extra.rhs); + const lhs_ty = self.air.typeOf(extra.lhs); + const rhs_ty = self.air.typeOf(extra.rhs); + + const tuple_ty = self.air.typeOfIndex(inst); + const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); + const tuple_align = tuple_ty.abiAlignment(self.target.*); + const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*)); + + switch (lhs_ty.zigTypeTag()) { + .Vector => return self.fail("TODO implement shl_with_overflow for vectors", .{}), + .Int => { + const int_info = lhs_ty.intInfo(self.target.*); + if (int_info.bits <= 32) { + const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); + + if (lhs == .register) self.register_manager.freezeRegs(&.{lhs.register}); + defer if (lhs == .register) self.register_manager.unfreezeRegs(&.{lhs.register}); + + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = null; + + // lsl dest, lhs, rhs + const dest = try self.binOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty); + + // asr/lsr reconstructed, dest, rhs + const reconstructed = try self.binOp(.shr, null, dest, rhs, lhs_ty, rhs_ty); + + // cmp lhs, reconstructed + _ = try self.binOp(.cmp_eq, null, lhs, reconstructed, lhs_ty, lhs_ty); + + try self.genSetStack(lhs_ty, stack_offset, dest); + try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); + + break :result MCValue{ .stack_offset = stack_offset }; + } else { + return self.fail("TODO ARM overflow operations on integers > u32/i32", .{}); + } + }, + else => unreachable, + } + }; + return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); } fn airDiv(self: *Self, inst: Air.Inst.Index) !void { diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 3ede976ef8..e1886955aa 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -719,7 +719,6 @@ test "@shlWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO var result: u16 = undefined; try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); From c4778fc0292b7024bf815e20e31029955a7a7241 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Thu, 31 Mar 2022 18:25:53 +0200 Subject: [PATCH 1029/2031] stage2 ARM: implement mul_with_overflow for ints <= 16 bits --- src/arch/arm/CodeGen.zig | 63 ++++++++++++++++++++++++++++++++++++++-- src/arch/arm/Emit.zig | 4 ++- src/arch/arm/Mir.zig | 2 ++ src/arch/arm/bits.zig | 55 +++++++++++++++++++++++++++++++++++ test/behavior/math.zig | 1 - 5 files changed, 120 insertions(+), 5 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 00d82349eb..07403e9f93 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1452,8 +1452,63 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { } fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch}); + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; + if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); + const result: MCValue = result: { + const lhs = try self.resolveInst(extra.lhs); + const rhs = try self.resolveInst(extra.rhs); + const lhs_ty = self.air.typeOf(extra.lhs); + const rhs_ty = self.air.typeOf(extra.rhs); + + const tuple_ty = self.air.typeOfIndex(inst); + const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); + const tuple_align = tuple_ty.abiAlignment(self.target.*); + const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*)); + + switch (lhs_ty.zigTypeTag()) { + .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}), + .Int => { + assert(lhs_ty.eql(rhs_ty, self.target.*)); + const int_info = lhs_ty.intInfo(self.target.*); + if (int_info.bits <= 16) { + const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); + + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = null; + + const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { + .signed => .smulbb, + .unsigned => .mul, + }; + + const dest = try self.binOpRegister(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); + const dest_reg = dest.register; + self.register_manager.freezeRegs(&.{dest_reg}); + defer self.register_manager.unfreezeRegs(&.{dest_reg}); + + const truncated_reg = try self.register_manager.allocReg(null); + self.register_manager.freezeRegs(&.{truncated_reg}); + defer self.register_manager.unfreezeRegs(&.{truncated_reg}); + + // sbfx/ubfx truncated, dest, #0, #bits + try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); + + // cmp dest, truncated + _ = try self.binOp(.cmp_eq, null, dest, .{ .register = truncated_reg }, Type.usize, Type.usize); + + try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); + try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); + + break :result MCValue{ .stack_offset = stack_offset }; + } else { + return self.fail("TODO ARM overflow operations on integers > u16/i16", .{}); + } + }, + else => unreachable, + } + }; + return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); } fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { @@ -2382,7 +2437,9 @@ fn binOpRegister( .rm = lhs_reg, .shift_amount = Instruction.ShiftAmount.reg(rhs_reg), } }, - .mul => .{ .rrr = .{ + .mul, + .smulbb, + => .{ .rrr = .{ .rd = dest_reg, .rn = lhs_reg, .rm = rhs_reg, diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index dfa0fabb2e..10da79e1cb 100644 --- a/src/arch/arm/Emit.zig +++ b/src/arch/arm/Emit.zig @@ -122,7 +122,7 @@ pub fn emitMir( .ldrsh_stack_argument => try emit.mirLoadStackArgument(inst), .ldrh => try emit.mirLoadStoreExtra(inst), - .ldrsb => try emit.mirLoadStore(inst), + .ldrsb => try emit.mirLoadStoreExtra(inst), .ldrsh => try emit.mirLoadStoreExtra(inst), .strh => try emit.mirLoadStoreExtra(inst), @@ -130,6 +130,7 @@ pub fn emitMir( .movt => try emit.mirSpecialMove(inst), .mul => try emit.mirMultiply(inst), + .smulbb => try emit.mirMultiply(inst), .nop => try emit.mirNop(), @@ -689,6 +690,7 @@ fn mirMultiply(emit: *Emit, inst: Mir.Inst.Index) !void { switch (tag) { .mul => try emit.writeInstruction(Instruction.mul(cond, rrr.rd, rrr.rn, rrr.rm)), + .smulbb => try emit.writeInstruction(Instruction.smulbb(cond, rrr.rd, rrr.rn, rrr.rm)), else => unreachable, } } diff --git a/src/arch/arm/Mir.zig b/src/arch/arm/Mir.zig index 496042d674..fe96d4209f 100644 --- a/src/arch/arm/Mir.zig +++ b/src/arch/arm/Mir.zig @@ -102,6 +102,8 @@ pub const Inst = struct { rsb, /// Signed Bit Field Extract sbfx, + /// Signed Multiply (halfwords), bottom half, bottom half + smulbb, /// Store Register str, /// Store Register Byte diff --git a/src/arch/arm/bits.zig b/src/arch/arm/bits.zig index c6dafb6924..af7fb301b9 100644 --- a/src/arch/arm/bits.zig +++ b/src/arch/arm/bits.zig @@ -216,6 +216,18 @@ pub const Instruction = union(enum) { fixed_2: u5 = 0b00001, cond: u4, }, + signed_multiply_halfwords: packed struct { + rn: u4, + fixed_1: u1 = 0b0, + n: u1, + m: u1, + fixed_2: u1 = 0b1, + rm: u4, + fixed_3: u4 = 0b0000, + rd: u4, + fixed_4: u8 = 0b00010110, + cond: u4, + }, integer_saturating_arithmetic: packed struct { rm: u4, fixed_1: u8 = 0b0000_0101, @@ -592,6 +604,7 @@ pub const Instruction = union(enum) { .data_processing => |v| @bitCast(u32, v), .multiply => |v| @bitCast(u32, v), .multiply_long => |v| @bitCast(u32, v), + .signed_multiply_halfwords => |v| @bitCast(u32, v), .integer_saturating_arithmetic => |v| @bitCast(u32, v), .bit_field_extract => |v| @bitCast(u32, v), .single_data_transfer => |v| @bitCast(u32, v), @@ -691,6 +704,26 @@ pub const Instruction = union(enum) { }; } + fn signedMultiplyHalfwords( + n: u1, + m: u1, + cond: Condition, + rd: Register, + rn: Register, + rm: Register, + ) Instruction { + return Instruction{ + .signed_multiply_halfwords = .{ + .rn = rn.id(), + .n = n, + .m = m, + .rm = rm.id(), + .rd = rd.id(), + .cond = @enumToInt(cond), + }, + }; + } + fn integerSaturationArithmetic( cond: Condition, rd: Register, @@ -1093,6 +1126,24 @@ pub const Instruction = union(enum) { return multiplyLong(cond, 1, 1, 1, rdhi, rdlo, rm, rn); } + // Signed Multiply (halfwords) + + pub fn smulbb(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { + return signedMultiplyHalfwords(0, 0, cond, rd, rn, rm); + } + + pub fn smulbt(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { + return signedMultiplyHalfwords(0, 1, cond, rd, rn, rm); + } + + pub fn smultb(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { + return signedMultiplyHalfwords(1, 0, cond, rd, rn, rm); + } + + pub fn smultt(cond: Condition, rd: Register, rn: Register, rm: Register) Instruction { + return signedMultiplyHalfwords(1, 1, cond, rd, rn, rm); + } + // Bit field extract pub fn ubfx(cond: Condition, rd: Register, rn: Register, lsb: u5, width: u6) Instruction { @@ -1440,6 +1491,10 @@ test "serialize instructions" { .inst = Instruction.qadd(.al, .r0, .r7, .r8), .expected = 0b1110_00010_00_0_1000_0000_0000_0101_0111, }, + .{ // smulbt r0, r0, r0 + .inst = Instruction.smulbt(.al, .r0, .r0, .r0), + .expected = 0b1110_00010110_0000_0000_0000_1_1_0_0_0000, + }, }; for (testcases) |case| { diff --git a/test/behavior/math.zig b/test/behavior/math.zig index e1886955aa..00728b13a4 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -678,7 +678,6 @@ test "small int addition" { test "@mulWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO var result: u8 = undefined; try expect(@mulWithOverflow(u8, 86, 3, &result)); From 8c12ad98b857cee3f6a8bc557f08b8dfcba2db7e Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Fri, 1 Apr 2022 22:51:18 +0200 Subject: [PATCH 1030/2031] stage2 ARM: implement mul_with_overflow for ints <= 32 bits --- src/arch/arm/CodeGen.zig | 108 ++++++++++++++++++++++++++++++++++++++- src/arch/arm/Emit.zig | 15 ++++++ src/arch/arm/Mir.zig | 13 +++++ 3 files changed, 135 insertions(+), 1 deletion(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 07403e9f93..9a660ceff6 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1500,9 +1500,115 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); + break :result MCValue{ .stack_offset = stack_offset }; + } else if (int_info.bits <= 32) { + const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); + + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = null; + + const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { + .signed => .smull, + .unsigned => .umull, + }; + + // TODO extract umull etc. to binOpTwoRegister + // once MCValue.rr is implemented + const lhs_is_register = lhs == .register; + const rhs_is_register = rhs == .register; + + if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); + if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); + + const lhs_reg = if (lhs_is_register) lhs.register else blk: { + const reg = try self.register_manager.allocReg(null); + self.register_manager.freezeRegs(&.{reg}); + + break :blk reg; + }; + defer self.register_manager.unfreezeRegs(&.{lhs_reg}); + + const rhs_reg = if (rhs_is_register) rhs.register else blk: { + const reg = try self.register_manager.allocReg(null); + self.register_manager.freezeRegs(&.{reg}); + + break :blk reg; + }; + defer self.register_manager.unfreezeRegs(&.{rhs_reg}); + + const dest_regs = try self.register_manager.allocRegs(2, .{ null, null }); + self.register_manager.freezeRegs(&dest_regs); + defer self.register_manager.unfreezeRegs(&dest_regs); + const rdlo = dest_regs[0]; + const rdhi = dest_regs[1]; + + if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); + if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); + + const truncated_reg = try self.register_manager.allocReg(null); + self.register_manager.freezeRegs(&.{truncated_reg}); + defer self.register_manager.unfreezeRegs(&.{truncated_reg}); + + _ = try self.addInst(.{ + .tag = base_tag, + .data = .{ .rrrr = .{ + .rdlo = rdlo, + .rdhi = rdhi, + .rn = lhs_reg, + .rm = rhs_reg, + } }, + }); + + // sbfx/ubfx truncated, rdlo, #0, #bits + try self.truncRegister(rdlo, truncated_reg, int_info.signedness, int_info.bits); + + // str truncated, [...] + try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); + + // cmp truncated, rdlo + _ = try self.binOp(.cmp_eq, null, .{ .register = truncated_reg }, .{ .register = rdlo }, Type.usize, Type.usize); + + // mov rdlo, #0 + _ = try self.addInst(.{ + .tag = .mov, + .data = .{ .rr_op = .{ + .rd = rdlo, + .rn = .r0, + .op = Instruction.Operand.fromU32(0).?, + } }, + }); + + // movne rdlo, #1 + _ = try self.addInst(.{ + .tag = .mov, + .cond = .ne, + .data = .{ .rr_op = .{ + .rd = rdlo, + .rn = .r0, + .op = Instruction.Operand.fromU32(1).?, + } }, + }); + + // cmp rdhi, #0 + _ = try self.binOp(.cmp_eq, null, .{ .register = rdhi }, .{ .immediate = 0 }, Type.usize, Type.usize); + + // movne rdlo, #1 + _ = try self.addInst(.{ + .tag = .mov, + .cond = .ne, + .data = .{ .rr_op = .{ + .rd = rdlo, + .rn = .r0, + .op = Instruction.Operand.fromU32(1).?, + } }, + }); + + // strb rdlo, [...] + try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .register = rdlo }); + break :result MCValue{ .stack_offset = stack_offset }; } else { - return self.fail("TODO ARM overflow operations on integers > u16/i16", .{}); + return self.fail("TODO ARM overflow operations on integers > u32/i32", .{}); } }, else => unreachable, diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index 10da79e1cb..77fa82d1d2 100644 --- a/src/arch/arm/Emit.zig +++ b/src/arch/arm/Emit.zig @@ -132,6 +132,9 @@ pub fn emitMir( .mul => try emit.mirMultiply(inst), .smulbb => try emit.mirMultiply(inst), + .smull => try emit.mirMultiplyLong(inst), + .umull => try emit.mirMultiplyLong(inst), + .nop => try emit.mirNop(), .pop => try emit.mirBlockDataTransfer(inst), @@ -695,6 +698,18 @@ fn mirMultiply(emit: *Emit, inst: Mir.Inst.Index) !void { } } +fn mirMultiplyLong(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const cond = emit.mir.instructions.items(.cond)[inst]; + const rrrr = emit.mir.instructions.items(.data)[inst].rrrr; + + switch (tag) { + .smull => try emit.writeInstruction(Instruction.smull(cond, rrrr.rdlo, rrrr.rdhi, rrrr.rn, rrrr.rm)), + .umull => try emit.writeInstruction(Instruction.umull(cond, rrrr.rdlo, rrrr.rdhi, rrrr.rn, rrrr.rm)), + else => unreachable, + } +} + fn mirNop(emit: *Emit) !void { try emit.writeInstruction(Instruction.nop()); } diff --git a/src/arch/arm/Mir.zig b/src/arch/arm/Mir.zig index fe96d4209f..209b3e508e 100644 --- a/src/arch/arm/Mir.zig +++ b/src/arch/arm/Mir.zig @@ -104,6 +104,8 @@ pub const Inst = struct { sbfx, /// Signed Multiply (halfwords), bottom half, bottom half smulbb, + /// Signed Multiply Long + smull, /// Store Register str, /// Store Register Byte @@ -118,6 +120,8 @@ pub const Inst = struct { svc, /// Unsigned Bit Field Extract ubfx, + /// Unsigned Multiply Long + umull, }; /// The position of an MIR instruction within the `Mir` instructions array. @@ -215,6 +219,15 @@ pub const Inst = struct { rn: Register, rm: Register, }, + /// Four registers + /// + /// Used by e.g. smull + rrrr: struct { + rdlo: Register, + rdhi: Register, + rn: Register, + rm: Register, + }, /// An unordered list of registers /// /// Used by e.g. push From 219fa192c6311c627d4b507c928ebcf2920af9e8 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 2 Apr 2022 15:48:26 +0200 Subject: [PATCH 1031/2031] wasm: Implement `@maximum` & `@minimum` This implements the `max` and `min` AIR instructions by checking whether LHS is great/lesser than RHS. If that's the case, we assign LHS to the result, otherwise assign RHS to it instead. --- src/arch/wasm/CodeGen.zig | 43 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 5a191c5276..d3bae94102 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1307,6 +1307,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .shl_exact => self.airBinOp(inst, .shl), .shr, .shr_exact => self.airBinOp(inst, .shr), .xor => self.airBinOp(inst, .xor), + .max => self.airMaxMin(inst, .max), + .min => self.airMaxMin(inst, .min), .add_with_overflow => self.airBinOpOverflow(inst, .add), .sub_with_overflow => self.airBinOpOverflow(inst, .sub), @@ -1431,8 +1433,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .div_floor, .div_exact, .mod, - .max, - .min, .assembly, .shl_sat, .ret_addr, @@ -3873,3 +3873,42 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue return result_ptr; } + +fn airMaxMin(self: *Self, inst: Air.Inst.Index, op: enum { max, min }) InnerError!WValue { + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const ty = self.air.typeOfIndex(inst); + if (ty.zigTypeTag() == .Vector) { + return self.fail("TODO: `@maximum` and `@minimum` for vectors", .{}); + } + + if (ty.abiSize(self.target) > 8) { + return self.fail("TODO: `@maximum` and `@minimum` for types larger than 8 bytes", .{}); + } + + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + const result = try self.allocLocal(ty); + + try self.startBlock(.block, wasm.block_empty); + try self.startBlock(.block, wasm.block_empty); + + // check if LHS is greater/lesser than RHS + const cmp_result = try self.cmp(lhs, rhs, ty, if (op == .max) .gt else .lt); + try self.addLabel(.local_get, cmp_result.local); + try self.addLabel(.br_if, 0); // break to outer loop if LHS is greater/lesser than RHS + + // set RHS as max/min + try self.emitWValue(rhs); + try self.addLabel(.local_set, result.local); + try self.addLabel(.br, 1); // break out of all blocks + try self.endBlock(); + + // set LHS as max/min + try self.emitWValue(lhs); + try self.addLabel(.local_set, result.local); + try self.endBlock(); + + return result; +} From 5ba03369ee11b6b57dcad99ab7ed8ce3b08c7456 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 2 Apr 2022 16:50:39 +0200 Subject: [PATCH 1032/2031] wasm: Implement `@mulAdd` for f32, f64 This implements the `mul_add` AIR instruction for floats of bitsize 32 and 64. f16's will require us being able to extend and truncate f16's to correctly store and load them without losing the accuracy. --- src/arch/wasm/CodeGen.zig | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index d3bae94102..9cb61ca4ee 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1309,6 +1309,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .xor => self.airBinOp(inst, .xor), .max => self.airMaxMin(inst, .max), .min => self.airMaxMin(inst, .min), + .mul_add => self.airMulAdd(inst), .add_with_overflow => self.airBinOpOverflow(inst, .add), .sub_with_overflow => self.airBinOpOverflow(inst, .sub), @@ -1468,7 +1469,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .atomic_store_seq_cst, .atomic_rmw, .tag_name, - .mul_add, => |tag| return self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), }; } @@ -1721,8 +1721,7 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { else .signed; - // TODO: Revisit below to determine if optional zero-sized pointers should still have abi-size 4. - const abi_size = if (ty.isPtrLikeOptional()) @as(u8, 4) else @intCast(u8, ty.abiSize(self.target)); + const abi_size = @intCast(u8, ty.abiSize(self.target)); const opcode = buildOpcode(.{ .valtype1 = typeToValtype(ty, self.target), @@ -3912,3 +3911,24 @@ fn airMaxMin(self: *Self, inst: Air.Inst.Index, op: enum { max, min }) InnerErro return result; } + +fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const bin_op = self.air.extraData(Air.Bin, pl_op.payload).data; + const ty = self.air.typeOfIndex(inst); + if (ty.zigTypeTag() == .Vector) { + return self.fail("TODO: `@mulAdd` for vectors", .{}); + } + + if (ty.floatBits(self.target) == 16) { + return self.fail("TODO: `@mulAdd` for f16", .{}); + } + + const addend = try self.resolveInst(pl_op.operand); + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + const mul_result = try self.binOp(lhs, rhs, ty, .mul); + return self.binOp(mul_result, addend, ty, .add); +} From bd27fe2bf58d72dd0cdef73dd4040f2747215b78 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 2 Apr 2022 17:34:24 +0200 Subject: [PATCH 1033/2031] wasm: Implement `@clz` Implements the `clz` AIR instruction for integers with bitsize <= 64. When the bitsize of the integer is not the same as wasm's bitsize, we substract the difference in bits as those will always be 0 for the integer, but should not be counted towards the end result. We also wrap the result to ensure it fits in the result type as documented in the language reference. --- src/arch/wasm/CodeGen.zig | 48 ++++++++++++++++++++++++++++++++++++++- src/arch/wasm/Emit.zig | 4 ++++ src/arch/wasm/Mir.zig | 8 +++++++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 9cb61ca4ee..cd1aa286e1 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1316,6 +1316,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .shl_with_overflow => self.airBinOpOverflow(inst, .shl), .mul_with_overflow => self.airBinOpOverflow(inst, .mul), + .clz => self.airClz(inst), + .cmp_eq => self.airCmp(inst, .eq), .cmp_gte => self.airCmp(inst, .gte), .cmp_gt => self.airCmp(inst, .gt), @@ -1438,7 +1440,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .shl_sat, .ret_addr, .frame_addr, - .clz, .ctz, .byte_swap, .bit_reverse, @@ -3932,3 +3933,48 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const mul_result = try self.binOp(lhs, rhs, ty, .mul); return self.binOp(mul_result, addend, ty, .add); } + +fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const ty = self.air.typeOf(ty_op.operand); + const result_ty = self.air.typeOfIndex(inst); + if (ty.zigTypeTag() == .Vector) { + return self.fail("TODO: `@clz` for vectors", .{}); + } + + const operand = try self.resolveInst(ty_op.operand); + const int_info = ty.intInfo(self.target); + const wasm_bits = toWasmBits(int_info.bits) orelse { + return self.fail("TODO: `@clz` for integers with bitsize '{d}'", .{int_info.bits}); + }; + + try self.emitWValue(operand); + switch (wasm_bits) { + 32 => { + try self.addTag(.i32_clz); + + if (wasm_bits != int_info.bits) { + const tmp = try self.allocLocal(ty); + try self.addLabel(.local_set, tmp.local); + const val: i32 = -@intCast(i32, wasm_bits - int_info.bits); + return self.wrapBinOp(tmp, .{ .imm32 = @bitCast(u32, val) }, ty, .add); + } + }, + 64 => { + try self.addTag(.i64_clz); + + if (wasm_bits != int_info.bits) { + const tmp = try self.allocLocal(ty); + try self.addLabel(.local_set, tmp.local); + const val: i64 = -@intCast(i64, wasm_bits - int_info.bits); + return self.wrapBinOp(tmp, .{ .imm64 = @bitCast(u64, val) }, ty, .add); + } + }, + else => unreachable, + } + + const result = try self.allocLocal(result_ty); + try self.addLabel(.local_set, result.local); + return result; +} diff --git a/src/arch/wasm/Emit.zig b/src/arch/wasm/Emit.zig index 7487b014be..6fc2dfa3b3 100644 --- a/src/arch/wasm/Emit.zig +++ b/src/arch/wasm/Emit.zig @@ -217,6 +217,10 @@ pub fn emitMir(emit: *Emit) InnerError!void { .i64_rem_u => try emit.emitTag(tag), .i32_popcnt => try emit.emitTag(tag), .i64_popcnt => try emit.emitTag(tag), + .i32_clz => try emit.emitTag(tag), + .i32_ctz => try emit.emitTag(tag), + .i64_clz => try emit.emitTag(tag), + .i64_ctz => try emit.emitTag(tag), .extended => try emit.emitExtended(inst), } diff --git a/src/arch/wasm/Mir.zig b/src/arch/wasm/Mir.zig index 395e9bb17c..27f683cf1e 100644 --- a/src/arch/wasm/Mir.zig +++ b/src/arch/wasm/Mir.zig @@ -317,6 +317,10 @@ pub const Inst = struct { /// Uses `tag` f64_ge = 0x66, /// Uses `tag` + i32_clz = 0x67, + /// Uses `tag` + i32_ctz = 0x68, + /// Uses `tag` i32_popcnt = 0x69, /// Uses `tag` i32_add = 0x6A, @@ -345,6 +349,10 @@ pub const Inst = struct { /// Uses `tag` i32_shr_u = 0x76, /// Uses `tag` + i64_clz = 0x79, + /// Uses `tag` + i64_ctz = 0x7A, + /// Uses `tag` i64_popcnt = 0x7B, /// Uses `tag` i64_add = 0x7C, From 2c40b37f79b1b40b5f22e13131064bcab2191f64 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 2 Apr 2022 17:56:11 +0200 Subject: [PATCH 1034/2031] wasm: Implement `@ctz` for bitsize <= 64 Implements the `ctz` AIR instruction for integers with bitsize <= 64. When the bitsize of the integer does not match the bitsize of a wasm type, we first XOR the value with the value of (1< self.airBinOpOverflow(inst, .mul), .clz => self.airClz(inst), + .ctz => self.airCtz(inst), .cmp_eq => self.airCmp(inst, .eq), .cmp_gte => self.airCmp(inst, .gte), @@ -1440,7 +1441,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .shl_sat, .ret_addr, .frame_addr, - .ctz, .byte_swap, .bit_reverse, .is_err_ptr, @@ -3978,3 +3978,44 @@ fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addLabel(.local_set, result.local); return result; } + +fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const ty = self.air.typeOf(ty_op.operand); + const result_ty = self.air.typeOfIndex(inst); + + if (ty.zigTypeTag() == .Vector) { + return self.fail("TODO: `@ctz` for vectors", .{}); + } + + const operand = try self.resolveInst(ty_op.operand); + const int_info = ty.intInfo(self.target); + const wasm_bits = toWasmBits(int_info.bits) orelse { + return self.fail("TODO: `@clz` for integers with bitsize '{d}'", .{int_info.bits}); + }; + + switch (wasm_bits) { + 32 => { + if (wasm_bits != int_info.bits) { + const val: u32 = @as(u32, 1) << @intCast(u5, int_info.bits); + const bin_op = try self.binOp(operand, .{ .imm32 = val }, ty, .@"or"); + try self.emitWValue(bin_op); + } else try self.emitWValue(operand); + try self.addTag(.i32_ctz); + }, + 64 => { + if (wasm_bits != int_info.bits) { + const val: u64 = @as(u64, 1) << @intCast(u6, int_info.bits); + const bin_op = try self.binOp(operand, .{ .imm64 = val }, ty, .@"or"); + try self.emitWValue(bin_op); + } else try self.emitWValue(operand); + try self.addTag(.i64_ctz); + }, + else => unreachable, + } + + const result = try self.allocLocal(result_ty); + try self.addLabel(.local_set, result.local); + return result; +} From a0a587ff85d3785f99c475d9e0d5f1eb9e27bd26 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 2 Apr 2022 19:09:51 +0200 Subject: [PATCH 1035/2031] wasm: Enable passing behavior tests This shuffles some tests do ensure the new instructions are tested for the wasm backend, by moving vectors into their own tests as well as move the f16 test cases as those require special operating also. --- test/behavior/floatop.zig | 1 + test/behavior/math.zig | 2 -- test/behavior/maximum_minimum.zig | 42 ++++++++++++++++++++++++++----- test/behavior/muladd.zig | 24 ++++++++++++------ 4 files changed, 54 insertions(+), 15 deletions(-) diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 6d8c33efa2..0700b47c61 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -544,6 +544,7 @@ fn testTrunc() !void { } test "negation f16" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 00728b13a4..32945e452d 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -60,7 +60,6 @@ fn assertFalse(b: bool) !void { } test "@clz" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -137,7 +136,6 @@ fn expectVectorsEqual(a: anytype, b: anytype) !void { } test "@ctz" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/maximum_minimum.zig b/test/behavior/maximum_minimum.zig index 3d1b689db2..b3b8ee25c5 100644 --- a/test/behavior/maximum_minimum.zig +++ b/test/behavior/maximum_minimum.zig @@ -5,6 +5,24 @@ const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; test "@maximum" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var x: i32 = 10; + var y: f32 = 0.68; + try expect(@as(i32, 10) == @maximum(@as(i32, -3), x)); + try expect(@as(f32, 3.2) == @maximum(@as(f32, 3.2), y)); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "@maximum on vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO @@ -13,9 +31,6 @@ test "@maximum" { const S = struct { fn doTheTest() !void { - try expect(@as(i32, 10) == @maximum(@as(i32, -3), @as(i32, 10))); - try expect(@as(f32, 3.2) == @maximum(@as(f32, 3.2), @as(f32, 0.68))); - var a: @Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 }; var b: @Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 }; var x = @maximum(a, b); @@ -37,6 +52,24 @@ test "@maximum" { } test "@minimum" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var x: i32 = 10; + var y: f32 = 0.68; + try expect(@as(i32, -3) == @minimum(@as(i32, -3), x)); + try expect(@as(f32, 0.68) == @minimum(@as(f32, 3.2), y)); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "@minimum for vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -45,9 +78,6 @@ test "@minimum" { const S = struct { fn doTheTest() !void { - try expect(@as(i32, -3) == @minimum(@as(i32, -3), @as(i32, 10))); - try expect(@as(f32, 0.68) == @minimum(@as(f32, 3.2), @as(f32, 0.68))); - var a: @Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 }; var b: @Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 }; var x = @minimum(a, b); diff --git a/test/behavior/muladd.zig b/test/behavior/muladd.zig index 88e1769866..a7e7c3b816 100644 --- a/test/behavior/muladd.zig +++ b/test/behavior/muladd.zig @@ -3,7 +3,6 @@ const expect = @import("std").testing.expect; test "@mulAdd" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -13,12 +12,6 @@ test "@mulAdd" { } fn testMulAdd() !void { - { - var a: f16 = 5.5; - var b: f16 = 2.5; - var c: f16 = 6.25; - try expect(@mulAdd(f16, a, b, c) == 20); - } { var a: f32 = 5.5; var b: f32 = 2.5; @@ -33,6 +26,23 @@ fn testMulAdd() !void { } } +test "@mulAdd f16" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + comptime try testMulAdd16(); + try testMulAdd16(); +} + +fn testMulAdd16() !void { + var a: f16 = 5.5; + var b: f16 = 2.5; + var c: f16 = 6.25; + try expect(@mulAdd(f16, a, b, c) == 20); +} + test "@mulAdd f80" { if (true) { // https://github.com/ziglang/zig/issues/11030 From 83bb98e13b19d417c9a8de819b98a1566718b993 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 2 Apr 2022 00:36:57 +0300 Subject: [PATCH 1036/2031] stage2 llvm: properly align error union payload --- src/codegen/llvm.zig | 74 +++++++++++++++++++++++++++++++++-------- test/behavior/error.zig | 18 ++++++++++ 2 files changed, 78 insertions(+), 14 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 1391d8fe66..db4c08f86f 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1270,7 +1270,8 @@ pub const Object = struct { offset = std.mem.alignForwardGeneric(u64, offset, payload_align); const payload_offset = offset; - const fields: [2]*llvm.DIType = .{ + var len: u8 = 2; + var fields: [3]*llvm.DIType = .{ dib.createMemberType( fwd_decl.toScope(), "tag", @@ -1293,8 +1294,22 @@ pub const Object = struct { 0, // flags try o.lowerDebugType(payload_ty, .full), ), + undefined, }; + const error_size = Type.anyerror.abiSize(target); + if (payload_align > error_size) { + fields[2] = fields[1]; + const pad_len = @intCast(u32, payload_align - error_size); + fields[1] = dib.createArrayType( + pad_len * 8, + 8, + try o.lowerDebugType(Type.u8, .full), + @intCast(c_int, pad_len), + ); + len += 1; + } + const full_di_ty = dib.createStructType( compile_unit_scope, name.ptr, @@ -1305,7 +1320,7 @@ pub const Object = struct { 0, // flags null, // derived from &fields, - fields.len, + len, 0, // run time lang null, // vtable holder "", // unique id @@ -2156,8 +2171,16 @@ pub const DeclGen = struct { } const llvm_payload_type = try dg.llvmType(payload_type); - const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type }; - return dg.context.structType(&fields, fields.len, .False); + const payload_align = payload_type.abiAlignment(target); + const error_size = error_type.abiSize(target); + if (payload_align > error_size) { + const pad_type = dg.context.intType(8).arrayType(@intCast(u32, payload_align - error_size)); + const fields: [3]*const llvm.Type = .{ llvm_error_type, pad_type, llvm_payload_type }; + return dg.context.structType(&fields, fields.len, .False); + } else { + const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type }; + return dg.context.structType(&fields, fields.len, .False); + } }, .ErrorSet => { return dg.context.intType(16); @@ -2687,8 +2710,8 @@ pub const DeclGen = struct { const err_val = if (!is_pl) tv.val else Value.initTag(.zero); return dg.genTypedValue(.{ .ty = error_type, .val = err_val }); } - - const fields: [2]*const llvm.Value = .{ + var len: u8 = 2; + var fields: [3]*const llvm.Value = .{ try dg.genTypedValue(.{ .ty = error_type, .val = if (is_pl) Value.initTag(.zero) else tv.val, @@ -2697,8 +2720,18 @@ pub const DeclGen = struct { .ty = payload_type, .val = if (tv.val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef), }), + undefined, }; - return dg.context.constStruct(&fields, fields.len, .False); + + const payload_align = payload_type.abiAlignment(target); + const error_size = error_type.abiSize(target); + if (payload_align > error_size) { + fields[2] = fields[1]; + const pad_type = dg.context.intType(8).arrayType(@intCast(u32, payload_align - error_size)); + fields[1] = pad_type.getUndef(); + len += 1; + } + return dg.context.constStruct(&fields, len, .False); }, .Struct => { const llvm_struct_ty = try dg.llvmType(tv.ty); @@ -3143,10 +3176,11 @@ pub const DeclGen = struct { break :blk parent_llvm_ptr; } + const payload_offset: u8 = if (payload_ty.abiAlignment(target) > Type.anyerror.abiSize(target)) 2 else 1; const llvm_u32 = dg.context.intType(32); const indices: [2]*const llvm.Value = .{ llvm_u32.constInt(0, .False), - llvm_u32.constInt(1, .False), + llvm_u32.constInt(payload_offset, .False), }; break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len); }, @@ -4834,11 +4868,14 @@ pub const FuncGen = struct { const result_ty = self.air.getRefType(ty_op.ty); const payload_ty = if (operand_is_ptr) result_ty.childType() else result_ty; + const target = self.dg.module.getTarget(); + const offset: u8 = if (payload_ty.abiAlignment(target) > Type.anyerror.abiSize(target)) 2 else 1; + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return null; if (operand_is_ptr or isByRef(payload_ty)) { - return self.builder.buildStructGEP(operand, 1, ""); + return self.builder.buildStructGEP(operand, offset, ""); } - return self.builder.buildExtractValue(operand, 1, ""); + return self.builder.buildExtractValue(operand, offset, ""); } fn airErrUnionErr( @@ -4894,9 +4931,12 @@ pub const FuncGen = struct { // Then return the payload pointer (only if it is used). if (self.liveness.isUnused(inst)) return null; + + const target = self.dg.module.getTarget(); + const payload_offset: u8 = if (payload_ty.abiAlignment(target) > Type.anyerror.abiSize(target)) 2 else 1; const indices: [2]*const llvm.Value = .{ index_type.constNull(), // dereference the pointer - index_type.constInt(1, .False), // second field is the payload + index_type.constInt(payload_offset, .False), // second field is the payload }; return self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""); } @@ -4941,11 +4981,14 @@ pub const FuncGen = struct { const inst_ty = self.air.typeOfIndex(inst); const ok_err_code = self.context.intType(16).constNull(); const err_un_llvm_ty = try self.dg.llvmType(inst_ty); + + const target = self.dg.module.getTarget(); + const payload_offset: u8 = if (payload_ty.abiAlignment(target) > Type.anyerror.abiSize(target)) 2 else 1; if (isByRef(inst_ty)) { const result_ptr = self.buildAlloca(err_un_llvm_ty); const err_ptr = self.builder.buildStructGEP(result_ptr, 0, ""); _ = self.builder.buildStore(ok_err_code, err_ptr); - const payload_ptr = self.builder.buildStructGEP(result_ptr, 1, ""); + const payload_ptr = self.builder.buildStructGEP(result_ptr, payload_offset, ""); var ptr_ty_payload: Type.Payload.ElemType = .{ .base = .{ .tag = .single_mut_pointer }, .data = payload_ty, @@ -4956,7 +4999,7 @@ pub const FuncGen = struct { } const partial = self.builder.buildInsertValue(err_un_llvm_ty.getUndef(), ok_err_code, 0, ""); - return self.builder.buildInsertValue(partial, operand, 1, ""); + return self.builder.buildInsertValue(partial, operand, payload_offset, ""); } fn airWrapErrUnionErr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { @@ -4970,11 +5013,14 @@ pub const FuncGen = struct { return operand; } const err_un_llvm_ty = try self.dg.llvmType(err_un_ty); + + const target = self.dg.module.getTarget(); + const payload_offset: u8 = if (payload_ty.abiAlignment(target) > Type.anyerror.abiSize(target)) 2 else 1; if (isByRef(err_un_ty)) { const result_ptr = self.buildAlloca(err_un_llvm_ty); const err_ptr = self.builder.buildStructGEP(result_ptr, 0, ""); _ = self.builder.buildStore(operand, err_ptr); - const payload_ptr = self.builder.buildStructGEP(result_ptr, 1, ""); + const payload_ptr = self.builder.buildStructGEP(result_ptr, payload_offset, ""); var ptr_ty_payload: Type.Payload.ElemType = .{ .base = .{ .tag = .single_mut_pointer }, .data = payload_ty, diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 3030ea67ed..3c19471705 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -644,3 +644,21 @@ test "coerce error set to the current inferred error set" { }; S.foo() catch {}; } + +test "error union payload is properly aligned" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + const S = struct { + a: u128, + b: u128, + c: u128, + fn foo() error{}!@This() { + return @This(){ .a = 1, .b = 2, .c = 3 }; + } + }; + const blk = S.foo() catch unreachable; + if (blk.a != 1) unreachable; +} From 4618c41fa6ca70f06c7e65762d2f38d57b00818c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 2 Apr 2022 17:59:07 -0700 Subject: [PATCH 1037/2031] Sema: mechanism for converting comptime breaks to runtime closes #11369 --- src/Sema.zig | 222 ++++++++++++++++++++++++++++++++++++++--- test/behavior/eval.zig | 105 +++++++++++++++++++ 2 files changed, 312 insertions(+), 15 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 6ab89af3f9..d333e03ad2 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -68,6 +68,11 @@ preallocated_new_func: ?*Module.Fn = null, /// TODO: after upgrading to use InternPool change the key here to be an /// InternPool value index. types_to_resolve: std.ArrayListUnmanaged(Air.Inst.Ref) = .{}, +/// These are lazily created runtime blocks from inline_block instructions. +/// They are created when an inline_break passes through a runtime condition, because +/// Sema must convert comptime control flow to runtime control flow, which means +/// breaking from a block. +post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .{}, const std = @import("std"); const mem = std.mem; @@ -525,6 +530,18 @@ pub const Block = struct { }; }; +const LabeledBlock = struct { + block: Block, + label: Block.Label, + + fn destroy(lb: *LabeledBlock, gpa: Allocator) void { + lb.block.instructions.deinit(gpa); + lb.label.merges.results.deinit(gpa); + lb.label.merges.br_list.deinit(gpa); + gpa.destroy(lb); + } +}; + pub fn deinit(sema: *Sema) void { const gpa = sema.gpa; sema.air_instructions.deinit(gpa); @@ -533,6 +550,14 @@ pub fn deinit(sema: *Sema) void { sema.inst_map.deinit(gpa); sema.decl_val_table.deinit(gpa); sema.types_to_resolve.deinit(gpa); + { + var it = sema.post_hoc_blocks.iterator(); + while (it.next()) |entry| { + const labeled_block = entry.value_ptr.*; + labeled_block.destroy(gpa); + } + sema.post_hoc_blocks.deinit(gpa); + } sema.* = undefined; } @@ -573,8 +598,8 @@ pub fn analyzeBody( const BreakData = struct { block_inst: Zir.Inst.Index, - operand: Air.Inst.Ref, - inst: Air.Inst.Index, + operand: Zir.Inst.Ref, + inst: Zir.Inst.Index, }; pub fn analyzeBodyBreak( @@ -1192,20 +1217,67 @@ fn analyzeBodyInner( }, .block_inline => blk: { // Directly analyze the block body without introducing a new block. + // However, in the case of a corresponding break_inline which reaches + // through a runtime conditional branch, we must retroactively emit + // a block, so we remember the block index here just in case. + const block_index = block.instructions.items.len; const inst_data = datas[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; + const gpa = sema.gpa; // If this block contains a function prototype, we need to reset the // current list of parameters and restore it later. // Note: this probably needs to be resolved in a more general manner. const prev_params = block.params; block.params = .{}; defer { - block.params.deinit(sema.gpa); + block.params.deinit(gpa); block.params = prev_params; } - const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse - break always_noreturn; + const opt_break_data = try sema.analyzeBodyBreak(block, inline_body); + // A runtime conditional branch that needs a post-hoc block to be + // emitted communicates this by mapping the block index into the inst map. + if (map.get(inst)) |new_block_ref| ph: { + // Comptime control flow populates the map, so we don't actually know + // if this is a post-hoc runtime block until we check the + // post_hoc_block map. + const new_block_inst = Air.refToIndex(new_block_ref) orelse break :ph; + const labeled_block = sema.post_hoc_blocks.get(new_block_inst) orelse + break :ph; + + // In this case we need to move all the instructions starting at + // block_index from the current block into this new one. + + if (opt_break_data) |break_data| { + // This is a comptime break which we now change to a runtime break + // since it crosses a runtime branch. + // It may pass through our currently being analyzed block_inline or it + // may point directly to it. In the latter case, this modifies the + // block that we are about to look up in the post_hoc_blocks map below. + try sema.addRuntimeBreak(block, break_data); + } else { + // Here the comptime control flow ends with noreturn; however + // we have runtime control flow continuing after this block. + // This branch is therefore handled by the `i += 1; continue;` + // logic below. + } + + try labeled_block.block.instructions.appendSlice(gpa, block.instructions.items[block_index..]); + block.instructions.items.len = block_index; + + const block_result = try sema.analyzeBlockBody(block, inst_data.src(), &labeled_block.block, &labeled_block.label.merges); + { + // Destroy the ad-hoc block entry so that it does not interfere with + // the next iteration of comptime control flow, if any. + labeled_block.destroy(gpa); + assert(sema.post_hoc_blocks.remove(new_block_inst)); + } + try map.put(gpa, inst, block_result); + i += 1; + continue; + } + + const break_data = opt_break_data orelse break always_noreturn; if (inst == break_data.block_inst) { break :blk sema.resolveInst(break_data.operand); } else { @@ -3996,13 +4068,12 @@ fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErro .inlining = parent_block.inlining, .is_comptime = parent_block.is_comptime, }; - const merges = &child_block.label.?.merges; defer child_block.instructions.deinit(gpa); - defer merges.results.deinit(gpa); - defer merges.br_list.deinit(gpa); + defer label.merges.results.deinit(gpa); + defer label.merges.br_list.deinit(gpa); - return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, merges); + return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges); } fn resolveBlockBody( @@ -7955,7 +8026,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const item = sema.resolveInst(item_ref); // `item` is already guaranteed to be constant known. - try sema.analyzeBody(&case_block, body); + _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) { + error.ComptimeBreak => { + const zir_datas = sema.code.instructions.items(.data); + const break_data = zir_datas[sema.comptime_break_inst].@"break"; + try sema.addRuntimeBreak(&case_block, .{ + .block_inst = break_data.block_inst, + .operand = break_data.operand, + .inst = sema.comptime_break_inst, + }); + }, + else => |e| return e, + }; try wip_captures.finalize(); @@ -7998,7 +8080,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const body = sema.code.extra[extra_index..][0..body_len]; extra_index += body_len; - try sema.analyzeBody(&case_block, body); + _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) { + error.ComptimeBreak => { + const zir_datas = sema.code.instructions.items(.data); + const break_data = zir_datas[sema.comptime_break_inst].@"break"; + try sema.addRuntimeBreak(&case_block, .{ + .block_inst = break_data.block_inst, + .operand = break_data.operand, + .inst = sema.comptime_break_inst, + }); + }, + else => |e| return e, + }; try cases_extra.ensureUnusedCapacity(gpa, 2 + items.len + case_block.instructions.items.len); @@ -8073,7 +8166,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const body = sema.code.extra[extra_index..][0..body_len]; extra_index += body_len; - try sema.analyzeBody(&case_block, body); + _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) { + error.ComptimeBreak => { + const zir_datas = sema.code.instructions.items(.data); + const break_data = zir_datas[sema.comptime_break_inst].@"break"; + try sema.addRuntimeBreak(&case_block, .{ + .block_inst = break_data.block_inst, + .operand = break_data.operand, + .inst = sema.comptime_break_inst, + }); + }, + else => |e| return e, + }; try wip_captures.finalize(); @@ -8110,7 +8214,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError case_block.wip_capture_scope = wip_captures.scope; if (special.body.len != 0) { - try sema.analyzeBody(&case_block, special.body); + _ = sema.analyzeBodyInner(&case_block, special.body) catch |err| switch (err) { + error.ComptimeBreak => { + const zir_datas = sema.code.instructions.items(.data); + const break_data = zir_datas[sema.comptime_break_inst].@"break"; + try sema.addRuntimeBreak(&case_block, .{ + .block_inst = break_data.block_inst, + .operand = break_data.operand, + .inst = sema.comptime_break_inst, + }); + }, + else => |e| return e, + }; } else { // We still need a terminator in this block, but we have proven // that it is unreachable. @@ -11979,11 +12094,33 @@ fn zirCondbr( sub_block.runtime_index += 1; defer sub_block.instructions.deinit(gpa); - try sema.analyzeBody(&sub_block, then_body); + _ = sema.analyzeBodyInner(&sub_block, then_body) catch |err| switch (err) { + error.ComptimeBreak => { + const zir_datas = sema.code.instructions.items(.data); + const break_data = zir_datas[sema.comptime_break_inst].@"break"; + try sema.addRuntimeBreak(&sub_block, .{ + .block_inst = break_data.block_inst, + .operand = break_data.operand, + .inst = sema.comptime_break_inst, + }); + }, + else => |e| return e, + }; const true_instructions = sub_block.instructions.toOwnedSlice(gpa); defer gpa.free(true_instructions); - try sema.analyzeBody(&sub_block, else_body); + _ = sema.analyzeBodyInner(&sub_block, else_body) catch |err| switch (err) { + error.ComptimeBreak => { + const zir_datas = sema.code.instructions.items(.data); + const break_data = zir_datas[sema.comptime_break_inst].@"break"; + try sema.addRuntimeBreak(&sub_block, .{ + .block_inst = break_data.block_inst, + .operand = break_data.operand, + .inst = sema.comptime_break_inst, + }); + }, + else => |e| return e, + }; try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + true_instructions.len + sub_block.instructions.items.len); _ = try parent_block.addInst(.{ @@ -12001,6 +12138,61 @@ fn zirCondbr( return always_noreturn; } +// A `break` statement is inside a runtime condition, but trying to +// break from an inline loop. In such case we must convert it to +// a runtime break. +fn addRuntimeBreak(sema: *Sema, child_block: *Block, break_data: BreakData) !void { + const gop = try sema.inst_map.getOrPut(sema.gpa, break_data.block_inst); + const labeled_block = if (!gop.found_existing) blk: { + try sema.post_hoc_blocks.ensureUnusedCapacity(sema.gpa, 1); + + const new_block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len); + gop.value_ptr.* = Air.indexToRef(new_block_inst); + try sema.air_instructions.append(sema.gpa, .{ + .tag = .block, + .data = undefined, + }); + const labeled_block = try sema.gpa.create(LabeledBlock); + labeled_block.* = .{ + .label = .{ + .zir_block = break_data.block_inst, + .merges = .{ + .results = .{}, + .br_list = .{}, + .block_inst = new_block_inst, + }, + }, + .block = .{ + .parent = child_block, + .sema = sema, + .src_decl = child_block.src_decl, + .namespace = child_block.namespace, + .wip_capture_scope = child_block.wip_capture_scope, + .instructions = .{}, + .label = &labeled_block.label, + .inlining = child_block.inlining, + .is_comptime = child_block.is_comptime, + }, + }; + sema.post_hoc_blocks.putAssumeCapacityNoClobber(new_block_inst, labeled_block); + break :blk labeled_block; + } else blk: { + const new_block_inst = Air.refToIndex(gop.value_ptr.*).?; + const labeled_block = sema.post_hoc_blocks.get(new_block_inst).?; + break :blk labeled_block; + }; + + const operand = sema.resolveInst(break_data.operand); + const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand); + try labeled_block.label.merges.results.append(sema.gpa, operand); + try labeled_block.label.merges.br_list.append(sema.gpa, Air.refToIndex(br_ref).?); + labeled_block.block.runtime_index += 1; + if (labeled_block.block.runtime_cond == null and labeled_block.block.runtime_loop == null) { + labeled_block.block.runtime_cond = child_block.runtime_cond orelse child_block.runtime_loop; + labeled_block.block.runtime_loop = child_block.runtime_loop; + } +} + fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { const tracy = trace(@src()); defer tracy.end(); diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index e3024a3895..c84f3b0e6a 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -893,3 +893,108 @@ test "closure capture type of runtime-known parameter" { var c: i32 = 1234; try S.b(c); } + +test "comptime break passing through runtime condition converted to runtime break" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var runtime: u8 = 'b'; + inline for ([3]u8{ 'a', 'b', 'c' }) |byte| { + bar(); + if (byte == runtime) { + foo(byte); + break; + } + } + try expect(ok); + try expect(count == 2); + } + var ok = false; + var count: usize = 0; + + fn foo(byte: u8) void { + ok = byte == 'b'; + } + + fn bar() void { + count += 1; + } + }; + + try S.doTheTest(); +} + +test "comptime break to outer loop passing through runtime condition converted to runtime break" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var runtime: u8 = 'b'; + outer: inline for ([3]u8{ 'A', 'B', 'C' }) |outer_byte| { + inline for ([3]u8{ 'a', 'b', 'c' }) |byte| { + bar(outer_byte); + if (byte == runtime) { + foo(byte); + break :outer; + } + } + } + try expect(ok); + try expect(count == 2); + } + var ok = false; + var count: usize = 0; + + fn foo(byte: u8) void { + ok = byte == 'b'; + } + + fn bar(byte: u8) void { + _ = byte; + count += 1; + } + }; + + try S.doTheTest(); +} + +test "comptime break operand passing through runtime condition converted to runtime break" { + const S = struct { + fn doTheTest(runtime: u8) !void { + const result = inline for ([3]u8{ 'a', 'b', 'c' }) |byte| { + if (byte == runtime) { + break runtime; + } + } else 'z'; + try expect(result == 'b'); + } + }; + + try S.doTheTest('b'); + comptime try S.doTheTest('b'); +} + +test "comptime break operand passing through runtime switch converted to runtime break" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest(runtime: u8) !void { + const result = inline for ([3]u8{ 'a', 'b', 'c' }) |byte| { + switch (runtime) { + byte => break runtime, + else => {}, + } + } else 'z'; + try expect(result == 'b'); + } + }; + + try S.doTheTest('b'); + comptime try S.doTheTest('b'); +} From 3b32e0be3187d8815ce20dd6b45665dc4e85b443 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 2 Apr 2022 19:01:56 -0700 Subject: [PATCH 1038/2031] behavior tests: disable failing stage1 test I forgot to check that the new behavior tests also pass in stage1. One of them does not. Fixes regression from 4618c41fa6ca70f06c7e65762d2f38d57b00818c. --- test/behavior/eval.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index c84f3b0e6a..3895b4b7b2 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -980,6 +980,7 @@ test "comptime break operand passing through runtime condition converted to runt } test "comptime break operand passing through runtime switch converted to runtime break" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From 843d5adcd6ec0b620fe93e0366aa0e8bb92bd171 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 2 Apr 2022 19:10:09 -0700 Subject: [PATCH 1039/2031] std.ArrayHashMap: lazier verifyContext calls Companion commit to b45c6c757cb4a16f5021c8bf057d14183036f14c. Related: #11367 --- lib/std/array_hash_map.zig | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/std/array_hash_map.zig b/lib/std/array_hash_map.zig index 3ddaac49eb..e582f55819 100644 --- a/lib/std/array_hash_map.zig +++ b/lib/std/array_hash_map.zig @@ -77,12 +77,15 @@ pub fn ArrayHashMap( comptime Context: type, comptime store_hash: bool, ) type { - comptime std.hash_map.verifyContext(Context, K, K, u32, true); return struct { unmanaged: Unmanaged, allocator: Allocator, ctx: Context, + comptime { + std.hash_map.verifyContext(Context, K, K, u32, true); + } + /// The ArrayHashMapUnmanaged type using the same settings as this managed map. pub const Unmanaged = ArrayHashMapUnmanaged(K, V, Context, store_hash); @@ -470,7 +473,6 @@ pub fn ArrayHashMapUnmanaged( comptime Context: type, comptime store_hash: bool, ) type { - comptime std.hash_map.verifyContext(Context, K, K, u32, true); return struct { /// It is permitted to access this field directly. entries: DataList = .{}, @@ -481,6 +483,10 @@ pub fn ArrayHashMapUnmanaged( /// by how many total indexes there are. index_header: ?*IndexHeader = null, + comptime { + std.hash_map.verifyContext(Context, K, K, u32, true); + } + /// Modifying the key is allowed only if it does not change the hash. /// Modifying the value is allowed. /// Entry pointers become invalid whenever this ArrayHashMap is modified, From 3432e66faf8940bbbfc7ee24d0e17e6127e66bf6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 2 Apr 2022 19:11:04 -0700 Subject: [PATCH 1040/2031] stage2: remove dependencies on async functions This commit removes the tiny amount of dependency on async/await that the self-hosted compiler has so that it can self-host before async/await language features are working. --- src/libc_installation.zig | 43 ++++++++++++++------------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/src/libc_installation.zig b/src/libc_installation.zig index fe1a2b2ca5..0b40580d7b 100644 --- a/src/libc_installation.zig +++ b/src/libc_installation.zig @@ -3,7 +3,6 @@ const builtin = @import("builtin"); const Target = std.Target; const fs = std.fs; const Allocator = std.mem.Allocator; -const Batch = std.event.Batch; const build_options = @import("build_options"); const is_darwin = builtin.target.isDarwin(); @@ -195,40 +194,28 @@ pub const LibCInstallation = struct { .None => { defer sdk.free(); - var batch = Batch(FindError!void, 5, .auto_async).init(); - batch.add(&async self.findNativeMsvcIncludeDir(args, sdk)); - batch.add(&async self.findNativeMsvcLibDir(args, sdk)); - batch.add(&async self.findNativeKernel32LibDir(args, sdk)); - batch.add(&async self.findNativeIncludeDirWindows(args, sdk)); - batch.add(&async self.findNativeCrtDirWindows(args, sdk)); - try batch.wait(); + try self.findNativeMsvcIncludeDir(args, sdk); + try self.findNativeMsvcLibDir(args, sdk); + try self.findNativeKernel32LibDir(args, sdk); + try self.findNativeIncludeDirWindows(args, sdk); + try self.findNativeCrtDirWindows(args, sdk); }, .OutOfMemory => return error.OutOfMemory, .NotFound => return error.WindowsSdkNotFound, .PathTooLong => return error.WindowsSdkNotFound, } } else if (is_haiku) { - try blk: { - var batch = Batch(FindError!void, 2, .auto_async).init(); - errdefer batch.wait() catch {}; - batch.add(&async self.findNativeIncludeDirPosix(args)); - batch.add(&async self.findNativeCrtBeginDirHaiku(args)); - self.crt_dir = try args.allocator.dupeZ(u8, "/system/develop/lib"); - break :blk batch.wait(); - }; + try self.findNativeIncludeDirPosix(args); + try self.findNativeCrtBeginDirHaiku(args); + self.crt_dir = try args.allocator.dupeZ(u8, "/system/develop/lib"); } else if (std.process.can_spawn) { - try blk: { - var batch = Batch(FindError!void, 2, .auto_async).init(); - errdefer batch.wait() catch {}; - batch.add(&async self.findNativeIncludeDirPosix(args)); - switch (builtin.target.os.tag) { - .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib"), - .solaris => self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib/64"), - .linux => batch.add(&async self.findNativeCrtDirPosix(args)), - else => {}, - } - break :blk batch.wait(); - }; + try self.findNativeIncludeDirPosix(args); + switch (builtin.target.os.tag) { + .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib"), + .solaris => self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib/64"), + .linux => try self.findNativeCrtDirPosix(args), + else => {}, + } } else { return error.LibCRuntimeNotFound; } From 91eb1af9177d774158d888484023e3bf0be65412 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 2 Apr 2022 19:12:08 -0700 Subject: [PATCH 1041/2031] stage2: more resilient error handling * If more than one error is reported for the same Decl, the first error message is kept and the second one discarded. * Prevent functions from being sent to codegen backends if there were any errors resolving any of their parameter types or return type. --- src/Module.zig | 14 ++++++++++++-- src/Sema.zig | 8 +++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 154c7426d2..fdf61c4bf6 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4854,7 +4854,12 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem error.GenericPoison => unreachable, error.ComptimeReturn => unreachable, error.ComptimeBreak => unreachable, - error.AnalysisFail => {}, + error.AnalysisFail => { + // In this case our function depends on a type that had a compile error. + // We should not try to lower this function. + decl.analysis = .dependency_failure; + return error.AnalysisFail; + }, else => |e| return e, }; @@ -4867,7 +4872,12 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem error.GenericPoison => unreachable, error.ComptimeReturn => unreachable, error.ComptimeBreak => unreachable, - error.AnalysisFail => {}, + error.AnalysisFail => { + // In this case our function depends on a type that had a compile error. + // We should not try to lower this function. + decl.analysis = .dependency_failure; + return error.AnalysisFail; + }, else => |e| return e, }; } diff --git a/src/Sema.zig b/src/Sema.zig index d333e03ad2..65550bec00 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1639,7 +1639,13 @@ fn failWithOwnedErrorMsg(sema: *Sema, block: *Block, err_msg: *Module.ErrorMsg) sema.owner_decl.analysis = .sema_failure; sema.owner_decl.generation = mod.generation; } - mod.failed_decls.putAssumeCapacityNoClobber(sema.owner_decl, err_msg); + const gop = mod.failed_decls.getOrPutAssumeCapacity(sema.owner_decl); + if (gop.found_existing) { + // If there are multiple errors for the same Decl, prefer the first one added. + err_msg.destroy(mod.gpa); + } else { + gop.value_ptr.* = err_msg; + } return error.AnalysisFail; } From 8f0dac01ef00574167ca99b716d9903c86a96a3d Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Fri, 18 Mar 2022 17:35:30 +0100 Subject: [PATCH 1042/2031] sema: add more info to error message for invalid comptime union field access --- src/Sema.zig | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 65550bec00..5f997f131e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -17484,9 +17484,15 @@ fn unionFieldVal( if (tag_matches) { return sema.addConstant(field.ty, tag_and_val.val); } else { - // TODO enhance this saying which one was active - // and which one was accessed, and showing where the union was declared. - return sema.fail(block, src, "access of inactive union field", .{}); + const msg = msg: { + const active_index = tag_and_val.tag.castTag(.enum_field_index).?.data; + const active_field_name = union_obj.fields.keys()[active_index]; + const msg = try sema.errMsg(block, src, "access of union field '{s}' while field '{s}' is active", .{ field_name, active_field_name }); + errdefer msg.destroy(sema.gpa); + try sema.addDeclaredHereNote(msg, union_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); } }, .Packed, .Extern => { From f7f4702795d76c606d119941e2cf5d5c3e2045b6 Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sat, 19 Mar 2022 12:55:58 +0100 Subject: [PATCH 1043/2031] sema: add more info to error messages for enum->union coercion --- src/Sema.zig | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 5f997f131e..e7bc442f84 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -19596,13 +19596,17 @@ fn coerceEnumToUnion( const field = union_obj.fields.values()[field_index]; const field_ty = try sema.resolveTypeFields(block, inst_src, field.ty); const opv = (try sema.typeHasOnePossibleValue(block, inst_src, field_ty)) orelse { - // TODO resolve the field names and include in the error message, - // also instead of 'union declared here' make it 'field "foo" declared here'. const msg = msg: { - const msg = try sema.errMsg(block, inst_src, "coercion to union {} must initialize {} field", .{ - union_ty.fmt(target), field_ty.fmt(target), + const field_name = union_obj.fields.keys()[field_index]; + const msg = try sema.errMsg(block, inst_src, "coercion from enum '{}' to union '{}' must initialize '{}' field '{s}'", .{ + inst_ty.fmt(target), union_ty.fmt(target), field_ty.fmt(target), field_name, }); errdefer msg.destroy(sema.gpa); + + const tree = try sema.getAstTree(block); + const union_decl = union_obj.owner_decl; + const field_src = enumFieldSrcLoc(union_decl, tree.*, union_obj.node_offset, field_index); + try sema.mod.errNoteNonLazy(field_src.toSrcLoc(union_decl), msg, "field '{s}' declared here", .{field_name}); try sema.addDeclaredHereNote(msg, union_ty); break :msg msg; }; @@ -19634,13 +19638,27 @@ fn coerceEnumToUnion( return block.addBitCast(union_ty, enum_tag); } - // TODO resolve the field names and add a hint that says "field 'foo' has type 'bar'" - // instead of the "union declared here" hint const msg = msg: { - const msg = try sema.errMsg(block, inst_src, "runtime coercion to union {} which has non-void fields", .{ - union_ty.fmt(target), - }); + const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const msg = try sema.errMsg( + block, + inst_src, + "runtime coercion from enum '{}' to union '{}' which has non-void fields", + .{ tag_ty.fmt(target), union_ty.fmt(target) }, + ); errdefer msg.destroy(sema.gpa); + + const tree = try sema.getAstTree(block); + const union_decl = union_obj.owner_decl; + var it = union_obj.fields.iterator(); + var field_index: usize = 0; + while (it.next()) |field| { + const field_name = field.key_ptr.*; + const field_ty = field.value_ptr.ty; + const field_src = enumFieldSrcLoc(union_decl, tree.*, union_obj.node_offset, field_index); + try sema.mod.errNoteNonLazy(field_src.toSrcLoc(union_decl), msg, "field '{s}' has type '{}'", .{ field_name, field_ty.fmt(target) }); + field_index += 1; + } try sema.addDeclaredHereNote(msg, union_ty); break :msg msg; }; From b922caf1691111e5c23f01afbe1f1c9b5104807b Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sat, 19 Mar 2022 19:32:31 +0100 Subject: [PATCH 1044/2031] sema: add compile error for missing/extra enum fields in union decl --- src/Sema.zig | 44 ++++++++++++++++++++++++++++++++++++++++++-- src/type.zig | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index e7bc442f84..4e8acb0030 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -21727,7 +21727,7 @@ fn resolveTypeFieldsUnion( } union_obj.status = .field_types_wip; - try semaUnionFields(sema.mod, union_obj); + try semaUnionFields(block, sema.mod, union_obj); union_obj.status = .have_field_types; } @@ -21967,7 +21967,7 @@ fn semaStructFields( } } -fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { +fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) CompileError!void { const tracy = trace(@src()); defer tracy.end(); @@ -22067,6 +22067,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { var int_tag_ty: Type = undefined; var enum_field_names: ?*Module.EnumNumbered.NameMap = null; var enum_value_map: ?*Module.EnumNumbered.ValueMap = null; + var tag_ty_field_names: ?Module.EnumFull.NameMap = null; if (tag_type_ref != .none) { const provided_ty = try sema.resolveType(&block_scope, src, tag_type_ref); if (small.auto_enum_tag) { @@ -22079,6 +22080,10 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { } else { // The provided type is the enum tag type. union_obj.tag_ty = try provided_ty.copy(decl_arena_allocator); + // The fields of the union must match the enum exactly. + // Store a copy of the enum field names so we can check for + // missing or extraneous fields later. + tag_ty_field_names = try union_obj.tag_ty.enumFields().clone(decl_arena_allocator); } } else { // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis @@ -22172,6 +22177,20 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { set.putAssumeCapacity(field_name, {}); } + if (tag_ty_field_names) |*names| { + const enum_has_field = names.contains(field_name); + if (!enum_has_field) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "enum '{}' has no field named '{s}'", .{ union_obj.tag_ty.fmt(target), field_name }); + errdefer msg.destroy(sema.gpa); + try sema.addDeclaredHereNote(msg, union_obj.tag_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + _ = names.orderedRemove(field_name); + } + const field_ty: Type = if (!has_type) Type.void else if (field_type_ref == .none) @@ -22202,6 +22221,27 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { gop.value_ptr.abi_align = 0; } } + + if (tag_ty_field_names) |names| { + if (names.count() > 0) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{}); + errdefer msg.destroy(sema.gpa); + + const enum_ty = union_obj.tag_ty; + const tree = try sema.getAstTree(block); + const enum_decl = enum_ty.getOwnerDecl(); + for (names.keys()) |field_name| { + const field_index = enum_ty.enumFieldIndex(field_name).?; + const field_src = enumFieldSrcLoc(enum_decl, tree.*, enum_ty.getNodeOffset(), field_index); + try sema.mod.errNoteNonLazy(field_src.toSrcLoc(enum_decl), msg, "field '{s}' missing, declared here", .{field_name}); + } + try sema.addDeclaredHereNote(msg, union_obj.tag_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + } } fn generateUnionTagTypeNumbered( diff --git a/src/type.zig b/src/type.zig index bcb6e63f6e..735227f9d2 100644 --- a/src/type.zig +++ b/src/type.zig @@ -5305,6 +5305,50 @@ pub const Type = extern union { } } + pub fn getNodeOffset(ty: Type) i32 { + switch (ty.tag()) { + .enum_full, .enum_nonexhaustive => { + const enum_full = ty.cast(Payload.EnumFull).?.data; + return enum_full.node_offset; + }, + .enum_numbered => return ty.castTag(.enum_numbered).?.data.node_offset, + .enum_simple => { + const enum_simple = ty.castTag(.enum_simple).?.data; + return enum_simple.node_offset; + }, + .@"struct" => { + const struct_obj = ty.castTag(.@"struct").?.data; + return struct_obj.node_offset; + }, + .error_set => { + const error_set = ty.castTag(.error_set).?.data; + return error_set.node_offset; + }, + .@"union", .union_tagged => { + const union_obj = ty.cast(Payload.Union).?.data; + return union_obj.node_offset; + }, + .@"opaque" => { + const opaque_obj = ty.cast(Payload.Opaque).?.data; + return opaque_obj.node_offset; + }, + .atomic_order, + .atomic_rmw_op, + .calling_convention, + .address_space, + .float_mode, + .reduce_op, + .call_options, + .prefetch_options, + .export_options, + .extern_options, + .type_info, + => unreachable, // These need to be resolved earlier. + + else => unreachable, + } + } + /// Asserts the type is an enum. pub fn enumHasInt(ty: Type, int: Value, target: Target) bool { const S = struct { From e4d427f12e2052e9bcd6af40e7ddbc4e544451e6 Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sun, 20 Mar 2022 13:47:03 +0100 Subject: [PATCH 1045/2031] refactor: add Sema.addFieldErrNote --- src/Sema.zig | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 4e8acb0030..fb37f3f1ba 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1589,6 +1589,21 @@ fn errNote( return sema.mod.errNoteNonLazy(src.toSrcLoc(block.src_decl), parent, format, args); } +fn addFieldErrNote( + sema: *Sema, + block: *Block, + container_ty: Type, + field_index: usize, + parent: *Module.ErrorMsg, + comptime format: []const u8, + args: anytype, +) !void { + const decl = container_ty.getOwnerDecl(); + const tree = try sema.getAstTree(block); + const field_src = enumFieldSrcLoc(decl, tree.*, container_ty.getNodeOffset(), field_index); + try sema.mod.errNoteNonLazy(field_src.toSrcLoc(decl), parent, format, args); +} + fn errMsg( sema: *Sema, block: *Block, @@ -19603,10 +19618,7 @@ fn coerceEnumToUnion( }); errdefer msg.destroy(sema.gpa); - const tree = try sema.getAstTree(block); - const union_decl = union_obj.owner_decl; - const field_src = enumFieldSrcLoc(union_decl, tree.*, union_obj.node_offset, field_index); - try sema.mod.errNoteNonLazy(field_src.toSrcLoc(union_decl), msg, "field '{s}' declared here", .{field_name}); + try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' declared here", .{field_name}); try sema.addDeclaredHereNote(msg, union_ty); break :msg msg; }; @@ -19648,15 +19660,12 @@ fn coerceEnumToUnion( ); errdefer msg.destroy(sema.gpa); - const tree = try sema.getAstTree(block); - const union_decl = union_obj.owner_decl; var it = union_obj.fields.iterator(); var field_index: usize = 0; while (it.next()) |field| { const field_name = field.key_ptr.*; const field_ty = field.value_ptr.ty; - const field_src = enumFieldSrcLoc(union_decl, tree.*, union_obj.node_offset, field_index); - try sema.mod.errNoteNonLazy(field_src.toSrcLoc(union_decl), msg, "field '{s}' has type '{}'", .{ field_name, field_ty.fmt(target) }); + try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' has type '{}'", .{ field_name, field_ty.fmt(target) }); field_index += 1; } try sema.addDeclaredHereNote(msg, union_ty); @@ -22229,12 +22238,9 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil errdefer msg.destroy(sema.gpa); const enum_ty = union_obj.tag_ty; - const tree = try sema.getAstTree(block); - const enum_decl = enum_ty.getOwnerDecl(); for (names.keys()) |field_name| { const field_index = enum_ty.enumFieldIndex(field_name).?; - const field_src = enumFieldSrcLoc(enum_decl, tree.*, enum_ty.getNodeOffset(), field_index); - try sema.mod.errNoteNonLazy(field_src.toSrcLoc(enum_decl), msg, "field '{s}' missing, declared here", .{field_name}); + try sema.addFieldErrNote(block, enum_ty, field_index, msg, "field '{s}' missing, declared here", .{field_name}); } try sema.addDeclaredHereNote(msg, union_obj.tag_ty); break :msg msg; From fd1ce329b3f978a4ac2ae272afee7f07b5841990 Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sat, 26 Mar 2022 19:34:55 +0100 Subject: [PATCH 1046/2031] stage2: add union compile error tests --- .../stage2/union_access_of_inactive_field.zig | 14 ++++++++++++ .../stage2/union_enum_field_missing.zig | 20 +++++++++++++++++ .../stage2/union_extra_field.zig | 19 ++++++++++++++++ .../union_runtime_coercion_from_enum.zig | 22 +++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 test/compile_errors/stage2/union_access_of_inactive_field.zig create mode 100644 test/compile_errors/stage2/union_enum_field_missing.zig create mode 100644 test/compile_errors/stage2/union_extra_field.zig create mode 100644 test/compile_errors/stage2/union_runtime_coercion_from_enum.zig diff --git a/test/compile_errors/stage2/union_access_of_inactive_field.zig b/test/compile_errors/stage2/union_access_of_inactive_field.zig new file mode 100644 index 0000000000..34fa661d79 --- /dev/null +++ b/test/compile_errors/stage2/union_access_of_inactive_field.zig @@ -0,0 +1,14 @@ +const U = union { + a: void, + b: u64, +}; +comptime { + var u: U = .{.a = {}}; + const v = u.b; + _ = v; +} + +// access of inactive union field +// +// :7:16: error: access of union field 'b' while field 'a' is active +// :1:11: note: union declared here diff --git a/test/compile_errors/stage2/union_enum_field_missing.zig b/test/compile_errors/stage2/union_enum_field_missing.zig new file mode 100644 index 0000000000..b29ca83d3a --- /dev/null +++ b/test/compile_errors/stage2/union_enum_field_missing.zig @@ -0,0 +1,20 @@ +const E = enum { + a, + b, + c, +}; + +const U = union(E) { + a: i32, + b: f64, +}; + +export fn entry() usize { + return @sizeOf(U); +} + +// enum field missing in union +// +// :7:1: error: enum field(s) missing in union +// :4:5: note: field 'c' missing, declared here +// :1:11: note: enum declared here diff --git a/test/compile_errors/stage2/union_extra_field.zig b/test/compile_errors/stage2/union_extra_field.zig new file mode 100644 index 0000000000..4a0ab41936 --- /dev/null +++ b/test/compile_errors/stage2/union_extra_field.zig @@ -0,0 +1,19 @@ +const E = enum { + a, + b, + c, +}; +const U = union(E) { + a: i32, + b: f64, + c: f64, + d: f64, +}; +export fn entry() usize { + return @sizeOf(U); +} + +// union extra field +// +// :6:1: error: enum 'tmp.E' hs no field named 'd' +// :1:11: note: enum declared here diff --git a/test/compile_errors/stage2/union_runtime_coercion_from_enum.zig b/test/compile_errors/stage2/union_runtime_coercion_from_enum.zig new file mode 100644 index 0000000000..f7e96834fd --- /dev/null +++ b/test/compile_errors/stage2/union_runtime_coercion_from_enum.zig @@ -0,0 +1,22 @@ +const E = enum { + a, + b, +}; +const U = union(E) { + a: u32, + b: u64, +}; +fn foo() E { + return E.b; +} +export fn doTheTest() u64 { + var u: U = foo(); + return u.b; +} + +// runtime coercion from enum to union +// +// :13:19: error: runtime coercion from enum 'tmp.E' to union 'tmp.U' which has non-void fields +// :6:5: note: field 'a' has type 'u32' +// :7:5: note: field 'b' has type 'u64' +// :5:11: note: union declared here From 174a889364d285e32534016f7425e8adaf9cab3c Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sun, 3 Apr 2022 14:01:06 +0200 Subject: [PATCH 1047/2031] sema: add compile error for duplicate union field --- src/Sema.zig | 17 ++++++++++++++++- .../stage2/union_duplicate_field_definition.zig | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 test/compile_errors/stage2/union_duplicate_field_definition.zig diff --git a/src/Sema.zig b/src/Sema.zig index fb37f3f1ba..8744548892 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -22215,7 +22215,22 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil } const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); - assert(!gop.found_existing); + if (gop.found_existing) { + const msg = msg: { + const tree = try sema.getAstTree(&block_scope); + const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const msg = try sema.errMsg(&block_scope, field_src, "duplicate union field: '{s}'", .{field_name}); + errdefer msg.destroy(gpa); + + const prev_field_index = union_obj.fields.getIndex(field_name).?; + const prev_field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, prev_field_index); + try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl), msg, "other field here", .{}); + try sema.errNote(&block_scope, src, msg, "union declared here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + } + gop.value_ptr.* = .{ .ty = try field_ty.copy(decl_arena_allocator), .abi_align = 0, diff --git a/test/compile_errors/stage2/union_duplicate_field_definition.zig b/test/compile_errors/stage2/union_duplicate_field_definition.zig new file mode 100644 index 0000000000..6ad2ae4f4e --- /dev/null +++ b/test/compile_errors/stage2/union_duplicate_field_definition.zig @@ -0,0 +1,15 @@ +const U = union { + foo: u32, + foo: u32, +}; + +export fn entry() void { + const u: U = .{ .foo = 100 }; + _ = u; +} + +// duplicate union field name +// +// :3:5: error: duplicate union field: 'foo' +// :2:5: note: other field here +// :1:11: note: union declared here From 6bbc2cd59af250ea164a4114e3d9fa15b27c2c8e Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sun, 3 Apr 2022 14:07:13 +0200 Subject: [PATCH 1048/2031] sema: add compile error for duplicate struct field --- src/Sema.zig | 16 +++++++++++++++- .../stage2/struct_duplicate_field_name.zig | 15 +++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 test/compile_errors/stage2/struct_duplicate_field_name.zig diff --git a/src/Sema.zig b/src/Sema.zig index 8744548892..17a7a8eb1c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -21945,7 +21945,21 @@ fn semaStructFields( } const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name); - assert(!gop.found_existing); + if (gop.found_existing) { + const msg = msg: { + const tree = try sema.getAstTree(&block_scope); + const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, field_i); + const msg = try sema.errMsg(&block_scope, field_src, "duplicate struct field: '{s}'", .{field_name}); + errdefer msg.destroy(gpa); + + const prev_field_index = struct_obj.fields.getIndex(field_name).?; + const prev_field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, prev_field_index); + try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl), msg, "other field here", .{}); + try sema.errNote(&block_scope, src, msg, "struct declared here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + } gop.value_ptr.* = .{ .ty = try field_ty.copy(decl_arena_allocator), .abi_align = 0, diff --git a/test/compile_errors/stage2/struct_duplicate_field_name.zig b/test/compile_errors/stage2/struct_duplicate_field_name.zig new file mode 100644 index 0000000000..274dce4e4a --- /dev/null +++ b/test/compile_errors/stage2/struct_duplicate_field_name.zig @@ -0,0 +1,15 @@ +const S = struct { + foo: u32, + foo: u32, +}; + +export fn entry() void { + const s: S = .{ .foo = 100 }; + _ = s; +} + +// duplicate struct field name +// +// :3:5: error: duplicate struct field: 'foo' +// :2:5: note: other field here +// :1:11: note: struct declared here From 17b804f56b0e75962d3e78a8bade1affa7984b4e Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sun, 3 Apr 2022 14:19:55 +0200 Subject: [PATCH 1049/2031] Fix typo in compile error test --- test/compile_errors/stage2/union_extra_field.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/compile_errors/stage2/union_extra_field.zig b/test/compile_errors/stage2/union_extra_field.zig index 4a0ab41936..e8ba581aad 100644 --- a/test/compile_errors/stage2/union_extra_field.zig +++ b/test/compile_errors/stage2/union_extra_field.zig @@ -15,5 +15,5 @@ export fn entry() usize { // union extra field // -// :6:1: error: enum 'tmp.E' hs no field named 'd' +// :6:1: error: enum 'tmp.E' has no field named 'd' // :1:11: note: enum declared here From 6d04ab6d5be1eaa47a920aaa3989bd3515fec5d6 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Thu, 13 Jan 2022 00:35:50 -0800 Subject: [PATCH 1050/2031] Add `std.testing.checkAllAllocationFailures` Adds a function that allows checking for memory leaks (and other problems) by taking advantage of the FailingAllocator and inducing failure at every allocation point within the provided `test_fn` (based on the strategy employed in the Zig parser tests, which can now use this function). --- lib/std/testing.zig | 144 ++++++++++++++++++++++++++++++++++++ lib/std/zig/parser_test.zig | 62 +++++----------- 2 files changed, 161 insertions(+), 45 deletions(-) diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 4146e033b4..56cc86d769 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -574,6 +574,150 @@ test { try expectEqualStrings("foo", "foo"); } +/// Exhaustively check that allocation failures within `test_fn` are handled without +/// introducing memory leaks. If used with the `testing.allocator` as the `backing_allocator`, +/// it will also be able to detect double frees, etc (when runtime safety is enabled). +/// +/// The provided `test_fn` must have a `std.mem.Allocator` as its first argument, +/// and must have a return type of `!void`. Any extra arguments of `test_fn` can +/// be provided via the `extra_args` tuple. +/// +/// Any relevant state shared between runs of `test_fn` *must* be reset within `test_fn`. +/// +/// Expects that the `test_fn` has a deterministic number of memory allocations +/// (an error will be returned if non-deterministic allocations are detected). +/// +/// The strategy employed is to: +/// - Run the test function once to get the total number of allocations. +/// - Then, iterate and run the function X more times, incrementing +/// the failing index each iteration (where X is the total number of +/// allocations determined previously) +/// +/// --- +/// +/// Here's an example of using a simple test case that will cause a leak when the +/// allocation of `bar` fails (but will pass normally): +/// +/// ```zig +/// test { +/// const length: usize = 10; +/// const allocator = std.testing.allocator; +/// var foo = try allocator.alloc(u8, length); +/// var bar = try allocator.alloc(u8, length); +/// +/// allocator.free(foo); +/// allocator.free(bar); +/// } +/// ``` +/// +/// The test case can be converted to something that this function can use by +/// doing: +/// +/// ```zig +/// fn testImpl(allocator: std.mem.Allocator, length: usize) !void { +/// var foo = try allocator.alloc(u8, length); +/// var bar = try allocator.alloc(u8, length); +/// +/// allocator.free(foo); +/// allocator.free(bar); +/// } +/// +/// test { +/// const length: usize = 10; +/// const allocator = std.testing.allocator; +/// try std.testing.checkAllAllocationFailures(allocator, testImpl, .{length}); +/// } +/// ``` +/// +/// Running this test will show that `foo` is leaked when the allocation of +/// `bar` fails. The simplest fix, in this case, would be to use defer like so: +/// +/// ```zig +/// fn testImpl(allocator: std.mem.Allocator, length: usize) !void { +/// var foo = try allocator.alloc(u8, length); +/// defer allocator.free(foo); +/// var bar = try allocator.alloc(u8, length); +/// defer allocator.free(bar); +/// } +/// ``` +pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime test_fn: anytype, extra_args: anytype) !void { + switch (@typeInfo(@typeInfo(@TypeOf(test_fn)).Fn.return_type.?)) { + .ErrorUnion => |info| { + if (info.payload != void) { + @compileError("Return type must be !void"); + } + }, + else => @compileError("Return type must be !void"), + } + if (@typeInfo(@TypeOf(extra_args)) != .Struct) { + @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(extra_args))); + } + + const ArgsTuple = std.meta.ArgsTuple(@TypeOf(test_fn)); + const fn_args_fields = @typeInfo(ArgsTuple).Struct.fields; + if (fn_args_fields.len == 0 or fn_args_fields[0].field_type != std.mem.Allocator) { + @compileError("The provided function must have an " ++ @typeName(std.mem.Allocator) ++ " as its first argument"); + } + const expected_args_tuple_len = fn_args_fields.len - 1; + if (extra_args.len != expected_args_tuple_len) { + @compileError("The provided function expects " ++ (comptime std.fmt.comptimePrint("{d}", .{expected_args_tuple_len})) ++ " extra arguments, but the provided tuple contains " ++ (comptime std.fmt.comptimePrint("{d}", .{extra_args.len}))); + } + + // Setup the tuple that will actually be used with @call (we'll need to insert + // the failing allocator in field @"0" before each @call) + var args: ArgsTuple = undefined; + inline for (@typeInfo(@TypeOf(extra_args)).Struct.fields) |field, i| { + const expected_type = fn_args_fields[i + 1].field_type; + if (expected_type != field.field_type) { + @compileError("Unexpected type for extra argument at index " ++ (comptime std.fmt.comptimePrint("{d}", .{i})) ++ ": expected " ++ @typeName(expected_type) ++ ", found " ++ @typeName(field.field_type)); + } + const arg_i_str = comptime str: { + var str_buf: [100]u8 = undefined; + const args_i = i + 1; + const str_len = std.fmt.formatIntBuf(&str_buf, args_i, 10, .lower, .{}); + break :str str_buf[0..str_len]; + }; + @field(args, arg_i_str) = @field(extra_args, field.name); + } + + // Try it once with unlimited memory, make sure it works + const needed_alloc_count = x: { + var failing_allocator_inst = std.testing.FailingAllocator.init(backing_allocator, std.math.maxInt(usize)); + args.@"0" = failing_allocator_inst.allocator(); + + try @call(.{}, test_fn, args); + break :x failing_allocator_inst.index; + }; + + var fail_index: usize = 0; + while (fail_index < needed_alloc_count) : (fail_index += 1) { + var failing_allocator_inst = std.testing.FailingAllocator.init(backing_allocator, fail_index); + args.@"0" = failing_allocator_inst.allocator(); + + if (@call(.{}, test_fn, args)) |_| { + return error.NondeterministicMemoryUsage; + } else |err| switch (err) { + error.OutOfMemory => { + if (failing_allocator_inst.allocated_bytes != failing_allocator_inst.freed_bytes) { + print( + "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\n", + .{ + fail_index, + needed_alloc_count, + failing_allocator_inst.allocated_bytes, + failing_allocator_inst.freed_bytes, + failing_allocator_inst.allocations, + failing_allocator_inst.deallocations, + }, + ); + return error.MemoryLeakDetected; + } + }, + else => return err, + } + } +} + /// Given a type, reference all the declarations inside, so that the semantic analyzer sees them. pub fn refAllDecls(comptime T: type) void { if (!builtin.is_test) return; diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index af40d8352c..9853dee684 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -5459,52 +5459,24 @@ fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: * anything_changed.* = !mem.eql(u8, formatted, source); return formatted; } -fn testTransform(source: [:0]const u8, expected_source: []const u8) !void { - const needed_alloc_count = x: { - // Try it once with unlimited memory, make sure it works - var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); - var failing_allocator = std.testing.FailingAllocator.init(fixed_allocator.allocator(), maxInt(usize)); - const allocator = failing_allocator.allocator(); - var anything_changed: bool = undefined; - const result_source = try testParse(source, allocator, &anything_changed); - try std.testing.expectEqualStrings(expected_source, result_source); - const changes_expected = source.ptr != expected_source.ptr; - if (anything_changed != changes_expected) { - print("std.zig.render returned {} instead of {}\n", .{ anything_changed, changes_expected }); - return error.TestFailed; - } - try std.testing.expect(anything_changed == changes_expected); - allocator.free(result_source); - break :x failing_allocator.index; - }; - - var fail_index: usize = 0; - while (fail_index < needed_alloc_count) : (fail_index += 1) { - var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); - var failing_allocator = std.testing.FailingAllocator.init(fixed_allocator.allocator(), fail_index); - var anything_changed: bool = undefined; - if (testParse(source, failing_allocator.allocator(), &anything_changed)) |_| { - return error.NondeterministicMemoryUsage; - } else |err| switch (err) { - error.OutOfMemory => { - if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) { - print( - "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\n", - .{ - fail_index, - needed_alloc_count, - failing_allocator.allocated_bytes, - failing_allocator.freed_bytes, - failing_allocator.allocations, - failing_allocator.deallocations, - }, - ); - return error.MemoryLeakDetected; - } - }, - else => return err, - } +fn testTransformImpl(allocator: mem.Allocator, fba: *std.heap.FixedBufferAllocator, source: [:0]const u8, expected_source: []const u8) !void { + // reset the fixed buffer allocator each run so that it can be re-used for each + // iteration of the failing index + fba.reset(); + var anything_changed: bool = undefined; + const result_source = try testParse(source, allocator, &anything_changed); + try std.testing.expectEqualStrings(expected_source, result_source); + const changes_expected = source.ptr != expected_source.ptr; + if (anything_changed != changes_expected) { + print("std.zig.render returned {} instead of {}\n", .{ anything_changed, changes_expected }); + return error.TestFailed; } + try std.testing.expect(anything_changed == changes_expected); + allocator.free(result_source); +} +fn testTransform(source: [:0]const u8, expected_source: []const u8) !void { + var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); + return std.testing.checkAllAllocationFailures(fixed_allocator.allocator(), testTransformImpl, .{ &fixed_allocator, source, expected_source }); } fn testCanonical(source: [:0]const u8) !void { return testTransform(source, source); From cdcb34cdf42fa3eb7a399106ccd57efc87643032 Mon Sep 17 00:00:00 2001 From: Tom Read Cutting Date: Mon, 4 Apr 2022 13:33:24 +0100 Subject: [PATCH 1051/2031] Pull elf magic string out to re-used constant --- lib/std/debug.zig | 2 +- lib/std/dynamic_library.zig | 2 +- lib/std/elf.zig | 4 +++- lib/std/os.zig | 2 +- lib/std/os/test.zig | 2 +- lib/std/zig/system/NativeTargetInfo.zig | 2 +- src/link/Elf.zig | 2 +- 7 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index d2173e114a..e00c0a21a2 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -842,7 +842,7 @@ pub fn readElfDebugInfo(allocator: mem.Allocator, elf_file: File) !ModuleDebugIn nosuspend { const mapped_mem = try mapWholeFile(elf_file); const hdr = @ptrCast(*const elf.Ehdr, &mapped_mem[0]); - if (!mem.eql(u8, hdr.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic; + if (!mem.eql(u8, hdr.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic; if (hdr.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion; const endian: std.builtin.Endian = switch (hdr.e_ident[elf.EI_DATA]) { diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index d9ebb0d1d5..40a84fc76f 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -133,7 +133,7 @@ pub const ElfDynLib = struct { defer os.munmap(file_bytes); const eh = @ptrCast(*elf.Ehdr, file_bytes.ptr); - if (!mem.eql(u8, eh.e_ident[0..4], "\x7fELF")) return error.NotElfFile; + if (!mem.eql(u8, eh.e_ident[0..4], elf.MAGIC)) return error.NotElfFile; if (eh.e_type != elf.ET.DYN) return error.NotDynamicLibrary; const elf_addr = @ptrToInt(file_bytes.ptr); diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 84001ec1c9..846bb8d11a 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -305,6 +305,8 @@ pub const STT_ARM_16BIT = STT_HIPROC; pub const VER_FLG_BASE = 0x1; pub const VER_FLG_WEAK = 0x2; +pub const MAGIC = "\x7fELF"; + /// File types pub const ET = enum(u16) { /// No file type @@ -367,7 +369,7 @@ pub const Header = struct { pub fn parse(hdr_buf: *align(@alignOf(Elf64_Ehdr)) const [@sizeOf(Elf64_Ehdr)]u8) !Header { const hdr32 = @ptrCast(*const Elf32_Ehdr, hdr_buf); const hdr64 = @ptrCast(*const Elf64_Ehdr, hdr_buf); - if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic; + if (!mem.eql(u8, hdr32.e_ident[0..4], MAGIC)) return error.InvalidElfMagic; if (hdr32.e_ident[EI_VERSION] != 1) return error.InvalidElfVersion; const endian: std.builtin.Endian = switch (hdr32.e_ident[EI_DATA]) { diff --git a/lib/std/os.zig b/lib/std/os.zig index 87fe6c7f7b..096be0b941 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -5325,7 +5325,7 @@ pub fn dl_iterate_phdr( const elf_base = std.process.getBaseAddress(); const ehdr = @intToPtr(*elf.Ehdr, elf_base); // Make sure the base address points to an ELF image. - assert(mem.eql(u8, ehdr.e_ident[0..4], "\x7fELF")); + assert(mem.eql(u8, ehdr.e_ident[0..4], elf.MAGIC)); const n_phdr = ehdr.e_phnum; const phdrs = (@intToPtr([*]elf.Phdr, elf_base + ehdr.e_phoff))[0..n_phdr]; diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 1ca83dada6..8598c7b3be 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -447,7 +447,7 @@ fn iter_fn(info: *dl_phdr_info, size: usize, counter: *usize) IterFnError!void { // Find the ELF header const elf_header = @intToPtr(*elf.Ehdr, reloc_addr - phdr.p_offset); // Validate the magic - if (!mem.eql(u8, elf_header.e_ident[0..4], "\x7fELF")) return error.BadElfMagic; + if (!mem.eql(u8, elf_header.e_ident[0..4], elf.MAGIC)) return error.BadElfMagic; // Consistency check if (elf_header.e_phnum != info.dlpi_phnum) return error.FailedConsistencyCheck; diff --git a/lib/std/zig/system/NativeTargetInfo.zig b/lib/std/zig/system/NativeTargetInfo.zig index 36f6207677..f917ee8e34 100644 --- a/lib/std/zig/system/NativeTargetInfo.zig +++ b/lib/std/zig/system/NativeTargetInfo.zig @@ -473,7 +473,7 @@ pub fn abiAndDynamicLinkerFromFile( _ = try preadMin(file, &hdr_buf, 0, hdr_buf.len); const hdr32 = @ptrCast(*elf.Elf32_Ehdr, &hdr_buf); const hdr64 = @ptrCast(*elf.Elf64_Ehdr, &hdr_buf); - if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic; + if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic; const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) { elf.ELFDATA2LSB => .Little, elf.ELFDATA2MSB => .Big, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 636b2ba7df..aa5c5e2a47 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1827,7 +1827,7 @@ fn writeElfHeader(self: *Elf) !void { var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined; var index: usize = 0; - hdr_buf[0..4].* = "\x7fELF".*; + hdr_buf[0..4].* = elf.MAGIC.*; index += 4; hdr_buf[index] = switch (self.ptr_width) { From f654e16d06460a5f44fd8089538a41fc1936ea0b Mon Sep 17 00:00:00 2001 From: ominitay <37453713+ominitay@users.noreply.github.com> Date: Thu, 31 Mar 2022 19:13:27 +0100 Subject: [PATCH 1052/2031] std.simd: Fix suggestVectorSizeForCpu --- lib/std/simd.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/std/simd.zig b/lib/std/simd.zig index 02e08b0dad..88c86cdd76 100644 --- a/lib/std/simd.zig +++ b/lib/std/simd.zig @@ -23,7 +23,9 @@ pub fn suggestVectorSizeForCpu(comptime T: type, cpu: std.Target.Cpu) ?usize { const element_bit_size = std.math.max(8, std.math.ceilPowerOfTwo(T, @bitSizeOf(T))); return @divExact(vector_bit_size, element_bit_size); }, - else => @compileError("No vector sizes for this CPU architecture have yet been recommended"), + else => { + return null; + }, } } From 795f075790d641499dee283507fc40a2e609e9ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20L=C3=BChmann?= <47984692+luehmann@users.noreply.github.com> Date: Thu, 31 Mar 2022 09:18:34 +0200 Subject: [PATCH 1053/2031] langref: rename incorrect expect to assert --- doc/langref.html.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 578f0297ae..7948c2defc 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -7833,7 +7833,7 @@ fn readFile(allocator: Allocator, filename: []const u8) ![]u8 { for the current target to match the C ABI. When the child type of a pointer has this alignment, the alignment can be omitted from the type.

    -
    {#syntax#}const expect = @import("std").debug.assert;
    +      
    {#syntax#}const assert = @import("std").debug.assert;
     comptime {
         assert(*u32 == *align(@alignOf(u32)) u32);
     }{#endsyntax#}
    From 8d3e7aa5e0a7b3956a5ba8cf2c85d756ab581465 Mon Sep 17 00:00:00 2001 From: iddev5 Date: Mon, 4 Apr 2022 18:33:47 +0530 Subject: [PATCH 1054/2031] std.testing: add function zigBuild for running zig build runner commands --- lib/std/testing.zig | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 4146e033b4..c979a555b8 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -450,6 +450,36 @@ pub fn buildExe(zigexec: []const u8, zigfile: []const u8, binfile: []const u8) ! try expectEqual(ret_val, .{ .Exited = 0 }); } +/// Spawns a zig build runner process 'zigexec build subcmd' and +/// expects success +/// If specified, runs zig build in the cwd path +/// If specified, uses the specified lib_dir for zig standard library +/// instead of compiler's default library directory +pub fn runZigBuild(zigexec: []const u8, options: struct { + subcmd: ?[]const u8 = null, + cwd: ?[]const u8 = null, + lib_dir: ?[]const u8 = null, +}) !std.ChildProcess.ExecResult { + var args = std.ArrayList([]const u8).init(allocator); + defer args.deinit(); + + try args.appendSlice(&.{ zigexec, "build" }); + if (options.subcmd) |subcmd| try args.append(subcmd); + if (options.lib_dir) |lib_dir| try args.append(lib_dir); + + var result = try std.ChildProcess.exec(.{ + .allocator = allocator, + .argv = args.items, + .cwd = if (options.cwd) |c| c else null, + }); + errdefer { + allocator.free(result.stdout); + allocator.free(result.stderr); + } + + return result; +} + test "expectEqual nested array" { const a = [2][2]f32{ [_]f32{ 1.0, 0.0 }, From 94fd914e584d466808f40b9eb5fac49c1cc3c66a Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Mon, 4 Apr 2022 16:48:01 +0200 Subject: [PATCH 1055/2031] Address review comments --- src/Sema.zig | 29 +++++++++---------- .../stage2/union_duplicate_enum_field.zig | 16 ++++++++++ 2 files changed, 30 insertions(+), 15 deletions(-) create mode 100644 test/compile_errors/stage2/union_duplicate_enum_field.zig diff --git a/src/Sema.zig b/src/Sema.zig index 17a7a8eb1c..ae13298337 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -22106,7 +22106,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil // The fields of the union must match the enum exactly. // Store a copy of the enum field names so we can check for // missing or extraneous fields later. - tag_ty_field_names = try union_obj.tag_ty.enumFields().clone(decl_arena_allocator); + tag_ty_field_names = try union_obj.tag_ty.enumFields().clone(sema.arena); } } else { // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis @@ -22200,20 +22200,6 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil set.putAssumeCapacity(field_name, {}); } - if (tag_ty_field_names) |*names| { - const enum_has_field = names.contains(field_name); - if (!enum_has_field) { - const msg = msg: { - const msg = try sema.errMsg(block, src, "enum '{}' has no field named '{s}'", .{ union_obj.tag_ty.fmt(target), field_name }); - errdefer msg.destroy(sema.gpa); - try sema.addDeclaredHereNote(msg, union_obj.tag_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - _ = names.orderedRemove(field_name); - } - const field_ty: Type = if (!has_type) Type.void else if (field_type_ref == .none) @@ -22245,6 +22231,19 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil return sema.failWithOwnedErrorMsg(&block_scope, msg); } + if (tag_ty_field_names) |*names| { + const enum_has_field = names.orderedRemove(field_name); + if (!enum_has_field) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "enum '{}' has no field named '{s}'", .{ union_obj.tag_ty.fmt(target), field_name }); + errdefer msg.destroy(sema.gpa); + try sema.addDeclaredHereNote(msg, union_obj.tag_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + } + gop.value_ptr.* = .{ .ty = try field_ty.copy(decl_arena_allocator), .abi_align = 0, diff --git a/test/compile_errors/stage2/union_duplicate_enum_field.zig b/test/compile_errors/stage2/union_duplicate_enum_field.zig new file mode 100644 index 0000000000..9044f9e97e --- /dev/null +++ b/test/compile_errors/stage2/union_duplicate_enum_field.zig @@ -0,0 +1,16 @@ +const E = enum {a, b}; +const U = union(E) { + a: u32, + a: u32, +}; + +export fn foo() void { + var u: U = .{ .a = 123 }; + _ = u; +} + +// union with enum and duplicate fields +// +// :4:5: error: duplicate union field: 'a' +// :3:5: note: other field here +// :2:11: note: union declared here From 364e53f3bf6b5aa4e5e7eba5d790c5b957007067 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 30 Mar 2022 18:29:44 +0200 Subject: [PATCH 1056/2031] dwarf: emit debug info for local variables on x86_64 Add support for emitting debug info for local variables within a subprogram. This required moving bits responsible for populating the debug info back to `CodeGen` from `Emit` as we require the operand to be resolved at callsite plus we need to know its type. Without enforcing this, we could end up with a `dead` mcv. --- src/arch/arm/Emit.zig | 4 +- src/arch/riscv64/CodeGen.zig | 2 +- src/arch/x86_64/CodeGen.zig | 148 +++++++++++++++++++++++++++++++---- src/arch/x86_64/Emit.zig | 88 --------------------- src/arch/x86_64/Mir.zig | 22 ++---- src/arch/x86_64/PrintMir.zig | 2 +- src/link/Dwarf.zig | 138 +++++++++++++++++--------------- 7 files changed, 216 insertions(+), 188 deletions(-) diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index 77fa82d1d2..7c9e508e5f 100644 --- a/src/arch/arm/Emit.zig +++ b/src/arch/arm/Emit.zig @@ -417,7 +417,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { .dwarf => |dw| { const dbg_info = &dw.dbg_info; try dbg_info.ensureUnusedCapacity(3); - dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1, // ULEB128 dwarf expression length reg.dwarfLocOp(), @@ -449,7 +449,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { }; const dbg_info = &dw.dbg_info; - try dbg_info.append(link.File.Dwarf.abbrev_parameter); + try dbg_info.append(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); // Get length of the LEB128 stack offset var counting_writer = std.io.countingWriter(std.io.null_writer); diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index ac12bbceaf..a7d4c872a7 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1574,7 +1574,7 @@ fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32 .dwarf => |dw| { const dbg_info = &dw.dbg_info; try dbg_info.ensureUnusedCapacity(3); - dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1, // ULEB128 dwarf expression length reg.dwarfLocOp(), diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 0a11c1480f..7f5144a6dc 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -48,6 +48,7 @@ gpa: Allocator, air: Air, liveness: Liveness, bin_file: *link.File, +debug_output: DebugInfoOutput, target: *const std.Target, mod_fn: *const Module.Fn, err_msg: ?*ErrorMsg, @@ -337,6 +338,7 @@ pub fn generate( .liveness = liveness, .target = &bin_file.options.target, .bin_file = bin_file, + .debug_output = debug_output, .mod_fn = module_fn, .err_msg = null, .args = undefined, // populated after `resolveCallingConventionValues` @@ -382,7 +384,6 @@ pub fn generate( }; var mir = Mir{ - .function = &function, .instructions = function.mir_instructions.toOwnedSlice(), .extra = function.mir_extra.toOwnedSlice(bin_file.allocator), }; @@ -391,7 +392,6 @@ pub fn generate( var emit = Emit{ .mir = mir, .bin_file = bin_file, - .function = &function, .debug_output = debug_output, .target = &bin_file.options.target, .src_loc = src_loc, @@ -3425,17 +3425,11 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { const arg_index = self.arg_index; self.arg_index += 1; + const ty = self.air.typeOfIndex(inst); const mcv = self.args[arg_index]; - const payload = try self.addExtra(Mir.ArgDbgInfo{ - .air_inst = inst, - .arg_index = arg_index, - .max_stack = self.max_end_stack, - }); - _ = try self.addInst(.{ - .tag = .arg_dbg_info, - .ops = undefined, - .data = .{ .payload = payload }, - }); + const name = self.mod_fn.getParamName(arg_index); + const name_with_null = name.ptr[0 .. name.len + 1]; + if (self.liveness.isUnused(inst)) return self.finishAirBookkeeping(); @@ -3443,10 +3437,46 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { switch (mcv) { .register => |reg| { self.register_manager.getRegAssumeFree(reg.to64(), inst); + switch (self.debug_output) { + .dwarf => |dw| { + const dbg_info = &dw.dbg_info; + try dbg_info.ensureUnusedCapacity(3); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1, // ULEB128 dwarf expression length + reg.dwarfLocOp(), + }); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + }, + .plan9 => {}, + .none => {}, + } break :blk mcv; }, .stack_offset => |off| { const offset = @intCast(i32, self.max_end_stack) - off + 16; + switch (self.debug_output) { + .dwarf => |dw| { + const dbg_info = &dw.dbg_info; + try dbg_info.ensureUnusedCapacity(8); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); + const fixup = dbg_info.items.len; + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1, // we will backpatch it after we encode the displacement in LEB128 + DW.OP.breg6, // .rbp TODO handle -fomit-frame-pointer + }); + leb128.writeILEB128(dbg_info.writer(), offset) catch unreachable; + dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + + }, + .plan9 => {}, + .none => {}, + } break :blk MCValue{ .stack_offset = -offset }; }, else => return self.fail("TODO implement arg for {}", .{mcv}), @@ -3885,13 +3915,99 @@ fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { const pl_op = self.air.instructions.items(.data)[inst].pl_op; - const name = self.air.nullTerminatedString(pl_op.payload); const operand = pl_op.operand; - // TODO emit debug info for this variable - _ = name; + const ty = self.air.typeOf(operand); + + if (!self.liveness.operandDies(inst, 0)) { + const mcv = try self.resolveInst(operand); + const name = self.air.nullTerminatedString(pl_op.payload); + + const tag = self.air.instructions.items(.tag)[inst]; + switch (tag) { + .dbg_var_ptr => try self.genVarDbgInfo(ty.childType(), mcv, name), + .dbg_var_val => try self.genVarDbgInfo(ty, mcv, name), + else => unreachable, + } + } + return self.finishAir(inst, .dead, .{ operand, .none, .none }); } +fn genVarDbgInfo( + self: *Self, + ty: Type, + mcv: MCValue, + name: [:0]const u8, +) !void { + const name_with_null = name.ptr[0 .. name.len + 1]; + switch (mcv) { + .register => |reg| { + switch (self.debug_output) { + .dwarf => |dw| { + const dbg_info = &dw.dbg_info; + try dbg_info.ensureUnusedCapacity(3); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.variable)); + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1, // ULEB128 dwarf expression length + reg.dwarfLocOp(), + }); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + }, + .plan9 => {}, + .none => {}, + } + }, + .ptr_stack_offset, .stack_offset => |off| { + switch (self.debug_output) { + .dwarf => |dw| { + const dbg_info = &dw.dbg_info; + try dbg_info.ensureUnusedCapacity(8); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.variable)); + const fixup = dbg_info.items.len; + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1, // we will backpatch it after we encode the displacement in LEB128 + DW.OP.breg6, // .rbp TODO handle -fomit-frame-pointer + }); + leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable; + dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + + }, + .plan9 => {}, + .none => {}, + } + }, + else => { + log.debug("TODO generate debug info for {}", .{mcv}); + }, + } +} + +/// Adds a Type to the .debug_info at the current position. The bytes will be populated later, +/// after codegen for this symbol is done. +fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { + switch (self.debug_output) { + .dwarf => |dw| { + assert(ty.hasRuntimeBits()); + const dbg_info = &dw.dbg_info; + const index = dbg_info.items.len; + try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 + const atom = switch (self.bin_file.tag) { + .elf => &self.mod_fn.owner_decl.link.elf.dbg_info_atom, + .macho => &self.mod_fn.owner_decl.link.macho.dbg_info_atom, + else => unreachable, + }; + try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); + }, + .plan9 => {}, + .none => {}, + } +} + fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { const abi_size = ty.abiSize(self.target.*); switch (mcv) { @@ -5919,7 +6035,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); } -fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { +pub 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); if (ref_int < Air.Inst.Ref.typed_value_map.len) { diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 3296339419..6af2c07974 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -30,7 +30,6 @@ const Type = @import("../../type.zig").Type; mir: Mir, bin_file: *link.File, -function: *const CodeGen, debug_output: DebugInfoOutput, target: *const std.Target, err_msg: ?*ErrorMsg = null, @@ -187,7 +186,6 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .dbg_line => try emit.mirDbgLine(inst), .dbg_prologue_end => try emit.mirDbgPrologueEnd(inst), .dbg_epilogue_begin => try emit.mirDbgEpilogueBegin(inst), - .arg_dbg_info => try emit.mirArgDbgInfo(inst), .push_regs_from_callee_preserved_regs => try emit.mirPushPopRegsFromCalleePreservedRegs(.push, inst), .pop_regs_from_callee_preserved_regs => try emit.mirPushPopRegsFromCalleePreservedRegs(.pop, inst), @@ -1057,92 +1055,6 @@ fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } } -fn mirArgDbgInfo(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .arg_dbg_info); - const payload = emit.mir.instructions.items(.data)[inst].payload; - const arg_dbg_info = emit.mir.extraData(Mir.ArgDbgInfo, payload).data; - const mcv = emit.mir.function.args[arg_dbg_info.arg_index]; - try emit.genArgDbgInfo(arg_dbg_info.air_inst, mcv, arg_dbg_info.max_stack, arg_dbg_info.arg_index); -} - -fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32, arg_index: u32) !void { - const ty = emit.mir.function.air.instructions.items(.data)[inst].ty; - const name = emit.mir.function.mod_fn.getParamName(arg_index); - const name_with_null = name.ptr[0 .. name.len + 1]; - - switch (mcv) { - .register => |reg| { - switch (emit.debug_output) { - .dwarf => |dw| { - const dbg_info = &dw.dbg_info; - try dbg_info.ensureUnusedCapacity(3); - dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); - dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc - 1, // ULEB128 dwarf expression length - reg.dwarfLocOp(), - }); - try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); - try emit.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string - }, - .plan9 => {}, - .none => {}, - } - }, - .stack_offset => |off| { - switch (emit.debug_output) { - .dwarf => |dw| { - // we add here +16 like we do in airArg in CodeGen since we refer directly to - // rbp as the start of function frame minus 8 bytes for caller's rbp preserved in the - // prologue, and 8 bytes for return address. - // TODO we need to make this more generic if we don't use rbp as the frame pointer - // for example when -fomit-frame-pointer is set. - const disp = @intCast(i32, max_stack) - off + 16; - const dbg_info = &dw.dbg_info; - try dbg_info.ensureUnusedCapacity(8); - dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); - const fixup = dbg_info.items.len; - dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc - 1, // we will backpatch it after we encode the displacement in LEB128 - DW.OP.breg6, // .rbp TODO handle -fomit-frame-pointer - }); - leb128.writeILEB128(dbg_info.writer(), disp) catch unreachable; - dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); - try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); - try emit.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string - - }, - .plan9 => {}, - .none => {}, - } - }, - else => {}, - } -} - -/// Adds a Type to the .debug_info at the current position. The bytes will be populated later, -/// after codegen for this symbol is done. -fn addDbgInfoTypeReloc(emit: *Emit, ty: Type) !void { - switch (emit.debug_output) { - .dwarf => |dw| { - assert(ty.hasRuntimeBits()); - const dbg_info = &dw.dbg_info; - const index = dbg_info.items.len; - try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 - const atom = switch (emit.bin_file.tag) { - .elf => &emit.function.mod_fn.owner_decl.link.elf.dbg_info_atom, - .macho => &emit.function.mod_fn.owner_decl.link.macho.dbg_info_atom, - else => unreachable, - }; - try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); - }, - .plan9 => {}, - .none => {}, - } -} - const Tag = enum { adc, add, diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 183a76e4b7..a99cd58f06 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -16,7 +16,6 @@ const Air = @import("../../Air.zig"); const CodeGen = @import("CodeGen.zig"); const Register = bits.Register; -function: *const CodeGen, instructions: std.MultiArrayList(Inst).Slice, /// The meaning of this data is determined by `Inst.Tag` value. extra: []const u32, @@ -364,9 +363,6 @@ pub const Inst = struct { /// update debug line dbg_line, - /// arg debug info - arg_dbg_info, - /// push registers from the callee_preserved_regs /// data is the bitfield of which regs to push /// for example on x86_64, the callee_preserved_regs are [_]Register{ .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; }; @@ -453,18 +449,6 @@ pub const DbgLineColumn = struct { column: u32, }; -pub const ArgDbgInfo = struct { - air_inst: Air.Inst.Index, - arg_index: u32, - max_stack: u32, -}; - -pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { - mir.instructions.deinit(gpa); - gpa.free(mir.extra); - mir.* = undefined; -} - pub const Ops = struct { reg1: Register = .none, reg2: Register = .none, @@ -490,6 +474,12 @@ pub const Ops = struct { } }; +pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { + mir.instructions.deinit(gpa); + gpa.free(mir.extra); + mir.* = undefined; +} + pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } { const fields = std.meta.fields(T); var i: usize = index; diff --git a/src/arch/x86_64/PrintMir.zig b/src/arch/x86_64/PrintMir.zig index 8c07350c2d..e457d859ea 100644 --- a/src/arch/x86_64/PrintMir.zig +++ b/src/arch/x86_64/PrintMir.zig @@ -147,7 +147,7 @@ pub fn printMir(print: *const Print, w: anytype, mir_to_air_map: std.AutoHashMap .call_extern => try print.mirCallExtern(inst, w), - .dbg_line, .dbg_prologue_end, .dbg_epilogue_begin, .arg_dbg_info => try w.print("{s}\n", .{@tagName(tag)}), + .dbg_line, .dbg_prologue_end, .dbg_epilogue_begin => try w.print("{s}\n", .{@tagName(tag)}), .push_regs_from_callee_preserved_regs => try print.mirPushPopRegsFromCalleePreservedRegs(.push, inst, w), .pop_regs_from_callee_preserved_regs => try print.mirPushPopRegsFromCalleePreservedRegs(.pop, inst, w), diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 907a21b774..4c6a80fe98 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -148,11 +148,11 @@ pub const DeclState = struct { switch (ty.zigTypeTag()) { .NoReturn => unreachable, .Void => { - try dbg_info_buffer.append(abbrev_pad1); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.pad1)); }, .Bool => { try dbg_info_buffer.appendSlice(&[_]u8{ - abbrev_base_type, + @enumToInt(AbbrevKind.base_type), DW.ATE.boolean, // DW.AT.encoding , DW.FORM.data1 1, // DW.AT.byte_size, DW.FORM.data1 'b', 'o', 'o', 'l', 0, // DW.AT.name, DW.FORM.string @@ -161,7 +161,7 @@ pub const DeclState = struct { .Int => { const info = ty.intInfo(target); try dbg_info_buffer.ensureUnusedCapacity(12); - dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.base_type)); // DW.AT.encoding, DW.FORM.data1 dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) { .signed => DW.ATE.signed, @@ -175,7 +175,7 @@ pub const DeclState = struct { .Optional => { if (ty.isPtrLikeOptional()) { try dbg_info_buffer.ensureUnusedCapacity(12); - dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.base_type)); // DW.AT.encoding, DW.FORM.data1 dbg_info_buffer.appendAssumeCapacity(DW.ATE.address); // DW.AT.byte_size, DW.FORM.data1 @@ -187,7 +187,7 @@ pub const DeclState = struct { var buf = try arena.create(Type.Payload.ElemType); const payload_ty = ty.optionalChild(buf); // DW.AT.structure_type - try dbg_info_buffer.append(abbrev_struct_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_type)); // DW.AT.byte_size, DW.FORM.sdata const abi_size = ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); @@ -195,7 +195,7 @@ pub const DeclState = struct { try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(7); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("maybe"); dbg_info_buffer.appendAssumeCapacity(0); @@ -207,7 +207,7 @@ pub const DeclState = struct { try dbg_info_buffer.ensureUnusedCapacity(6); dbg_info_buffer.appendAssumeCapacity(0); // DW.AT.member - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("val"); dbg_info_buffer.appendAssumeCapacity(0); @@ -227,14 +227,14 @@ pub const DeclState = struct { // Slices are structs: struct { .ptr = *, .len = N } // DW.AT.structure_type try dbg_info_buffer.ensureUnusedCapacity(2); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_type); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_type)); // DW.AT.byte_size, DW.FORM.sdata dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize) * 2); // DW.AT.name, DW.FORM.string try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(5); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("ptr"); dbg_info_buffer.appendAssumeCapacity(0); @@ -248,7 +248,7 @@ pub const DeclState = struct { try dbg_info_buffer.ensureUnusedCapacity(6); dbg_info_buffer.appendAssumeCapacity(0); // DW.AT.member - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("len"); dbg_info_buffer.appendAssumeCapacity(0); @@ -263,7 +263,7 @@ pub const DeclState = struct { dbg_info_buffer.appendAssumeCapacity(0); } else { try dbg_info_buffer.ensureUnusedCapacity(5); - dbg_info_buffer.appendAssumeCapacity(abbrev_ptr_type); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.ptr_type)); // DW.AT.type, DW.FORM.ref4 const index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); @@ -272,7 +272,7 @@ pub const DeclState = struct { }, .Struct => blk: { // DW.AT.structure_type - try dbg_info_buffer.append(abbrev_struct_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_type)); // DW.AT.byte_size, DW.FORM.sdata const abi_size = ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); @@ -285,7 +285,7 @@ pub const DeclState = struct { const fields = ty.tupleFields(); for (fields.types) |field, field_index| { // DW.AT.member - try dbg_info_buffer.append(abbrev_struct_member); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string try dbg_info_buffer.writer().print("{d}\x00", .{field_index}); // DW.AT.type, DW.FORM.ref4 @@ -315,7 +315,7 @@ pub const DeclState = struct { const field = fields.get(field_name).?; // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity(field_name); dbg_info_buffer.appendAssumeCapacity(0); @@ -335,7 +335,7 @@ pub const DeclState = struct { }, .Enum => { // DW.AT.enumeration_type - try dbg_info_buffer.append(abbrev_enum_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.enum_type)); // DW.AT.byte_size, DW.FORM.sdata const abi_size = ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); @@ -355,7 +355,7 @@ pub const DeclState = struct { for (fields.keys()) |field_name, field_i| { // DW.AT.enumerator try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2 + @sizeOf(u64)); - dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.enum_variant)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity(field_name); dbg_info_buffer.appendAssumeCapacity(0); @@ -385,7 +385,7 @@ pub const DeclState = struct { // for untagged unions. if (is_tagged) { // DW.AT.structure_type - try dbg_info_buffer.append(abbrev_struct_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_type)); // DW.AT.byte_size, DW.FORM.sdata try leb128.writeULEB128(dbg_info_buffer.writer(), layout.abi_size); // DW.AT.name, DW.FORM.string @@ -395,7 +395,7 @@ pub const DeclState = struct { // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(9); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("payload"); dbg_info_buffer.appendAssumeCapacity(0); @@ -408,7 +408,7 @@ pub const DeclState = struct { } // DW.AT.union_type - try dbg_info_buffer.append(abbrev_union_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.union_type)); // DW.AT.byte_size, DW.FORM.sdata, try leb128.writeULEB128(dbg_info_buffer.writer(), layout.payload_size); // DW.AT.name, DW.FORM.string @@ -423,7 +423,7 @@ pub const DeclState = struct { const field = fields.get(field_name).?; if (!field.ty.hasRuntimeBits()) continue; // DW.AT.member - try dbg_info_buffer.append(abbrev_struct_member); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string try dbg_info_buffer.writer().print("{s}\x00", .{field_name}); // DW.AT.type, DW.FORM.ref4 @@ -439,7 +439,7 @@ pub const DeclState = struct { if (is_tagged) { // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(5); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("tag"); dbg_info_buffer.appendAssumeCapacity(0); @@ -471,7 +471,7 @@ pub const DeclState = struct { const payload_off = mem.alignForwardGeneric(u64, error_ty.abiSize(target), abi_align); // DW.AT.structure_type - try dbg_info_buffer.append(abbrev_struct_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_type)); // DW.AT.byte_size, DW.FORM.sdata try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); // DW.AT.name, DW.FORM.string @@ -480,7 +480,7 @@ pub const DeclState = struct { // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(7); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("value"); dbg_info_buffer.appendAssumeCapacity(0); @@ -493,7 +493,7 @@ pub const DeclState = struct { // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(5); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("err"); dbg_info_buffer.appendAssumeCapacity(0); @@ -509,7 +509,7 @@ pub const DeclState = struct { }, else => { log.debug("TODO implement .debug_info for type '{}'", .{ty.fmtDebug()}); - try dbg_info_buffer.append(abbrev_pad1); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.pad1)); }, } } @@ -550,18 +550,21 @@ pub const SrcFn = struct { pub const PtrWidth = enum { p32, p64 }; -pub const abbrev_compile_unit = 1; -pub const abbrev_subprogram = 2; -pub const abbrev_subprogram_retvoid = 3; -pub const abbrev_base_type = 4; -pub const abbrev_ptr_type = 5; -pub const abbrev_struct_type = 6; -pub const abbrev_struct_member = 7; -pub const abbrev_enum_type = 8; -pub const abbrev_enum_variant = 9; -pub const abbrev_union_type = 10; -pub const abbrev_pad1 = 11; -pub const abbrev_parameter = 12; +pub const AbbrevKind = enum(u8) { + compile_unit = 1, + subprogram, + subprogram_retvoid, + base_type, + ptr_type, + struct_type, + struct_member, + enum_type, + enum_variant, + union_type, + pad1, + parameter, + variable, +}; /// The reloc offset for the virtual address of a function in its Line Number Program. /// Size is a virtual address integer. @@ -670,9 +673,9 @@ pub fn initDeclState(self: *Dwarf, decl: *Module.Decl) !DeclState { const fn_ret_type = decl.ty.fnReturnType(); const fn_ret_has_bits = fn_ret_type.hasRuntimeBits(); if (fn_ret_has_bits) { - dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.subprogram)); } else { - dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.subprogram_retvoid)); } // These get overwritten after generating the machine code. These values are // "relocations" and have to be in this fixed place so that functions can be @@ -926,7 +929,7 @@ pub fn commitDeclState( else => unreachable, }; - { + if (decl_state.abbrev_table.items.len > 0) { // Now we emit the .debug_info types of the Decl. These will count towards the size of // the buffer, so we have to do it before computing the offset, and we can't perform the actual // relocations yet. @@ -1244,14 +1247,14 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { // These are LEB encoded but since the values are all less than 127 // we can simply append these bytes. const abbrev_buf = [_]u8{ - abbrev_compile_unit, DW.TAG.compile_unit, DW.CHILDREN.yes, // header - DW.AT.stmt_list, DW.FORM.sec_offset, DW.AT.low_pc, - DW.FORM.addr, DW.AT.high_pc, DW.FORM.addr, - DW.AT.name, DW.FORM.strp, DW.AT.comp_dir, - DW.FORM.strp, DW.AT.producer, DW.FORM.strp, - DW.AT.language, DW.FORM.data2, 0, + @enumToInt(AbbrevKind.compile_unit), DW.TAG.compile_unit, DW.CHILDREN.yes, // header + DW.AT.stmt_list, DW.FORM.sec_offset, DW.AT.low_pc, + DW.FORM.addr, DW.AT.high_pc, DW.FORM.addr, + DW.AT.name, DW.FORM.strp, DW.AT.comp_dir, + DW.FORM.strp, DW.AT.producer, DW.FORM.strp, + DW.AT.language, DW.FORM.data2, 0, 0, // table sentinel - abbrev_subprogram, + @enumToInt(AbbrevKind.subprogram), DW.TAG.subprogram, DW.CHILDREN.yes, // header DW.AT.low_pc, @@ -1262,15 +1265,15 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.ref4, DW.AT.name, DW.FORM.string, - 0, 0, // table sentinel - abbrev_subprogram_retvoid, + 0, 0, // table sentinel + @enumToInt(AbbrevKind.subprogram_retvoid), DW.TAG.subprogram, DW.CHILDREN.yes, // header DW.AT.low_pc, DW.FORM.addr, DW.AT.high_pc, DW.FORM.data4, DW.AT.name, DW.FORM.string, 0, 0, // table sentinel - abbrev_base_type, + @enumToInt(AbbrevKind.base_type), DW.TAG.base_type, DW.CHILDREN.no, // header DW.AT.encoding, @@ -1281,14 +1284,14 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.string, 0, 0, // table sentinel - abbrev_ptr_type, + @enumToInt(AbbrevKind.ptr_type), DW.TAG.pointer_type, DW.CHILDREN.no, // header DW.AT.type, DW.FORM.ref4, 0, 0, // table sentinel - abbrev_struct_type, + @enumToInt(AbbrevKind.struct_type), DW.TAG.structure_type, DW.CHILDREN.yes, // header DW.AT.byte_size, @@ -1297,7 +1300,7 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.string, 0, 0, // table sentinel - abbrev_struct_member, + @enumToInt(AbbrevKind.struct_member), DW.TAG.member, DW.CHILDREN.no, // header DW.AT.name, @@ -1308,7 +1311,7 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.sdata, 0, 0, // table sentinel - abbrev_enum_type, + @enumToInt(AbbrevKind.enum_type), DW.TAG.enumeration_type, DW.CHILDREN.yes, // header DW.AT.byte_size, @@ -1317,7 +1320,7 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.string, 0, 0, // table sentinel - abbrev_enum_variant, + @enumToInt(AbbrevKind.enum_variant), DW.TAG.enumerator, DW.CHILDREN.no, // header DW.AT.name, @@ -1326,7 +1329,7 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.data8, 0, 0, // table sentinel - abbrev_union_type, + @enumToInt(AbbrevKind.union_type), DW.TAG.union_type, DW.CHILDREN.yes, // header DW.AT.byte_size, @@ -1335,18 +1338,25 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.string, 0, 0, // table sentinel - abbrev_pad1, + @enumToInt(AbbrevKind.pad1), DW.TAG.unspecified_type, DW.CHILDREN.no, // header 0, 0, // table sentinel - abbrev_parameter, + @enumToInt(AbbrevKind.parameter), DW.TAG.formal_parameter, DW.CHILDREN.no, // header DW.AT.location, DW.FORM.exprloc, DW.AT.type, DW.FORM.ref4, DW.AT.name, DW.FORM.string, 0, 0, // table sentinel + @enumToInt(AbbrevKind.variable), + DW.TAG.variable, DW.CHILDREN.no, // header + DW.AT.location, DW.FORM.exprloc, + DW.AT.type, DW.FORM.ref4, + DW.AT.name, DW.FORM.string, + 0, + 0, // table sentinel 0, 0, 0, // section sentinel @@ -1459,7 +1469,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6 const comp_dir_strp = try self.makeString(module.root_pkg.root_src_directory.path orelse "."); const producer_strp = try self.makeString(link.producer_string); - di_buf.appendAssumeCapacity(abbrev_compile_unit); + di_buf.appendAssumeCapacity(@enumToInt(AbbrevKind.compile_unit)); if (self.tag == .macho) { mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0); // DW.AT.stmt_list, DW.FORM.sec_offset mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), low_pc); @@ -1606,7 +1616,7 @@ fn pwriteDbgInfoNops( const tracy = trace(@src()); defer tracy.end(); - const page_of_nops = [1]u8{abbrev_pad1} ** 4096; + const page_of_nops = [1]u8{@enumToInt(AbbrevKind.pad1)} ** 4096; var vecs: [32]std.os.iovec_const = undefined; var vec_index: usize = 0; { @@ -1673,7 +1683,7 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void { .p32 => @as(usize, 4), .p64 => 12, }; - const ptr_width_bytes: u8 = self.ptrWidthBytes(); + const ptr_width_bytes = self.ptrWidthBytes(); // Enough for all the data without resizing. When support for more compilation units // is added, the size of this section will become more variable. @@ -2040,7 +2050,7 @@ fn addDbgInfoErrorSet( const target_endian = target.cpu.arch.endian(); // DW.AT.enumeration_type - try dbg_info_buffer.append(abbrev_enum_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.enum_type)); // DW.AT.byte_size, DW.FORM.sdata const abi_size = ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); @@ -2051,7 +2061,7 @@ fn addDbgInfoErrorSet( // DW.AT.enumerator const no_error = "(no error)"; try dbg_info_buffer.ensureUnusedCapacity(no_error.len + 2 + @sizeOf(u64)); - dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.enum_variant)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity(no_error); dbg_info_buffer.appendAssumeCapacity(0); @@ -2063,7 +2073,7 @@ fn addDbgInfoErrorSet( const kv = module.getErrorValue(error_name) catch unreachable; // DW.AT.enumerator try dbg_info_buffer.ensureUnusedCapacity(error_name.len + 2 + @sizeOf(u64)); - dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.enum_variant)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity(error_name); dbg_info_buffer.appendAssumeCapacity(0); From b4bf3bdf7eac05c5e4ff887294385946f4dd5f3f Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Sun, 3 Apr 2022 08:47:17 -0700 Subject: [PATCH 1057/2031] std.fmt: Fix incorrect behavior with large floating point integers. I consider this an interim workaround/hack until #1299 is finished. There is a bug in the original C implementation of the errol3 (and errol4) algorithm that can result in undefined behavior or an obviously incorrect result (leading ':' in the output) This change checks for those two problems and uses a slower fallback path if they occur. I can't guarantee that this will always produce the correct result, but since the workaround is only used if the original algorithm is guaranteed to fail, it should never turn a previously-correct result into an incorrect one. Fixes #11283 --- lib/std/fmt.zig | 2 ++ lib/std/fmt/errol.zig | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 395c502d61..6d4f3a1daa 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -2299,6 +2299,8 @@ test "float.decimal" { try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)}); try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)}); try expectFmt("f64: 10000000000000.00", "f64: {d:.2}", .{@as(f64, 9999999999999.999)}); + try expectFmt("f64: 10000000000000000000000000000000000000", "f64: {d}", .{@as(f64, 1e37)}); + try expectFmt("f64: 100000000000000000000000000000000000000", "f64: {d}", .{@as(f64, 1e38)}); } test "float.libc.sanity" { diff --git a/lib/std/fmt/errol.zig b/lib/std/fmt/errol.zig index e98c23f6ec..29dd2b7a63 100644 --- a/lib/std/fmt/errol.zig +++ b/lib/std/fmt/errol.zig @@ -106,7 +106,10 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { } else if (val >= 16.0 and val < 9.007199254740992e15) { return errolFixed(val, buffer); } + return errolSlow(val, buffer); +} +fn errolSlow(val: f64, buffer: []u8) FloatDecimal { // normalize the midpoint const e = math.frexp(val).exponent; @@ -336,7 +339,9 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { var buf_index = u64toa(m64, buffer) - 1; if (mi != 0) { - buffer[buf_index - 1] += @boolToInt(buffer[buf_index] >= '5'); + const round_up = buffer[buf_index] >= '5'; + if (buf_index == 0 or (round_up and buffer[buf_index - 1] == '9')) return errolSlow(val, buffer); + buffer[buf_index - 1] += @boolToInt(round_up); } else { buf_index += 1; } From 51ef31a8331e92103253a37ddfdacbd263cee81d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 4 Apr 2022 14:28:30 -0700 Subject: [PATCH 1058/2031] Sema: add empty tuple to mutable slice coercion --- src/Sema.zig | 13 +++++++++++++ test/behavior/cast.zig | 5 +++++ 2 files changed, 18 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index 65550bec00..1430e80ac0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -18166,6 +18166,19 @@ fn coerce( { return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src); } + + // empty tuple to zero-length slice + // note that this allows coercing to a mutable slice. + if (inst_ty.isSinglePointer() and + inst_ty.childType().tag() == .empty_struct_literal and + dest_info.size == .Slice) + { + const slice_val = try Value.Tag.slice.create(sema.arena, .{ + .ptr = Value.undef, + .len = Value.zero, + }); + return sema.addConstant(dest_ty, slice_val); + } }, .Many => p: { if (!inst_ty.isSlice()) break :p; diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 4e952828c5..0473a36033 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1386,3 +1386,8 @@ test "coerce undefined single-item pointer of array to error union of slice" { const s = try b; try expect(s.len == 0); } + +test "pointer to empty struct literal to mutable slice" { + var x: []i32 = &.{}; + try expect(x.len == 0); +} From 95a87e88fac3fc563ac3baaf4eb8027341f4131e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 4 Apr 2022 22:46:05 -0700 Subject: [PATCH 1059/2031] Sema: forward switch condition to captures --- src/Sema.zig | 11 ++++++++--- test/behavior/switch.zig | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 1430e80ac0..b1267e195f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7273,9 +7273,14 @@ fn zirSwitchCapture( } }, else => { - return sema.fail(block, operand_src, "switch on type '{}' provides no capture value", .{ - operand_ty.fmt(target), - }); + // In this case the capture value is just the passed-through value of the + // switch condition. + if (is_ref) { + assert(operand_is_ref); + return operand_ptr; + } else { + return operand; + } }, } } diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index b988f32a38..2d10ad0a13 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -656,3 +656,25 @@ test "switch capture copies its payload" { try S.doTheTest(); comptime try S.doTheTest(); } + +test "capture of integer forwards the switch condition directly" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const S = struct { + fn foo(x: u8) !void { + switch (x) { + 40...45 => |capture| { + try expect(capture == 42); + }, + else => |capture| { + try expect(capture == 100); + }, + } + } + }; + try S.foo(42); + try S.foo(100); + comptime try S.foo(42); + comptime try S.foo(100); +} From 5fafcc2b629e2ff00755b2bf45e903590f04aa9f Mon Sep 17 00:00:00 2001 From: Damien Firmenich Date: Tue, 5 Apr 2022 17:08:33 +0200 Subject: [PATCH 1060/2031] zig fmt: remove trailing whitespace on doc comments Fixes #11353 The renderer treats comments and doc comments differently since doc comments are parsed into the Ast. This commit adds a check after getting the text for the doc comment and trims whitespace at the end before rendering. The `a = 0,` in the test is here to avoid a ParseError while parsing the test. --- lib/std/Thread/Futex.zig | 4 ++-- lib/std/compress/deflate/compressor.zig | 2 +- lib/std/crypto/argon2.zig | 28 ++++++++++++------------ lib/std/crypto/scrypt.zig | 4 ++-- lib/std/math.zig | 2 +- lib/std/math/big/int.zig | 4 ++-- lib/std/mem/Allocator.zig | 4 ++-- lib/std/os.zig | 4 ++-- lib/std/os/uefi/pool_allocator.zig | 2 +- lib/std/os/uefi/tables/boot_services.zig | 2 +- lib/std/packed_int_array.zig | 2 +- lib/std/simd.zig | 2 +- lib/std/time.zig | 6 ++--- lib/std/x/net/tcp.zig | 2 +- lib/std/x/os/socket_posix.zig | 6 ++--- lib/std/zig/parser_test.zig | 22 +++++++++++++++++++ lib/std/zig/render.zig | 12 +++++++--- lib/std/zig/string_literal.zig | 2 +- src/arch/wasm/Mir.zig | 4 ++-- src/arch/x86_64/Mir.zig | 2 +- src/link/Elf.zig | 2 +- src/link/MachO.zig | 2 +- 22 files changed, 74 insertions(+), 46 deletions(-) diff --git a/lib/std/Thread/Futex.zig b/lib/std/Thread/Futex.zig index c9a19b4cd9..d256fb5a43 100644 --- a/lib/std/Thread/Futex.zig +++ b/lib/std/Thread/Futex.zig @@ -1,7 +1,7 @@ //! Futex is a mechanism used to block (`wait`) and unblock (`wake`) threads using a 32bit memory address as hints. //! Blocking a thread is acknowledged only if the 32bit memory address is equal to a given value. //! This check helps avoid block/unblock deadlocks which occur if a `wake()` happens before a `wait()`. -//! Using Futex, other Thread synchronization primitives can be built which efficiently wait for cross-thread events or signals. +//! Using Futex, other Thread synchronization primitives can be built which efficiently wait for cross-thread events or signals. const std = @import("../std.zig"); const builtin = @import("builtin"); @@ -20,7 +20,7 @@ const spinLoopHint = std.atomic.spinLoopHint; /// - The value at `ptr` is no longer equal to `expect`. /// - The caller is unblocked by a matching `wake()`. /// - The caller is unblocked spuriously by an arbitrary internal signal. -/// +/// /// If `timeout` is provided, and the caller is blocked for longer than `timeout` nanoseconds`, `error.TimedOut` is returned. /// /// The checking of `ptr` and `expect`, along with blocking the caller, is done atomically diff --git a/lib/std/compress/deflate/compressor.zig b/lib/std/compress/deflate/compressor.zig index 8ced8b5faf..58b1955838 100644 --- a/lib/std/compress/deflate/compressor.zig +++ b/lib/std/compress/deflate/compressor.zig @@ -219,7 +219,7 @@ const CompressorOptions = struct { /// /// `dictionary` is optional and initializes the new `Compressor` with a preset dictionary. /// The returned Compressor behaves as if the dictionary had been written to it without producing -/// any compressed output. The compressed data written to hm_bw can only be decompressed by a +/// any compressed output. The compressed data written to hm_bw can only be decompressed by a /// Decompressor initialized with the same dictionary. /// /// The compressed data will be passed to the provided `writer`, see `writer()` and `write()`. diff --git a/lib/std/crypto/argon2.zig b/lib/std/crypto/argon2.zig index 493f36ca94..7269470d5f 100644 --- a/lib/std/crypto/argon2.zig +++ b/lib/std/crypto/argon2.zig @@ -34,18 +34,18 @@ const max_hash_len = 64; /// Argon2 type pub const Mode = enum { - /// Argon2d is faster and uses data-depending memory access, which makes it highly resistant - /// against GPU cracking attacks and suitable for applications with no threats from side-channel + /// Argon2d is faster and uses data-depending memory access, which makes it highly resistant + /// against GPU cracking attacks and suitable for applications with no threats from side-channel /// timing attacks (eg. cryptocurrencies). argon2d, - /// Argon2i instead uses data-independent memory access, which is preferred for password - /// hashing and password-based key derivation, but it is slower as it makes more passes over + /// Argon2i instead uses data-independent memory access, which is preferred for password + /// hashing and password-based key derivation, but it is slower as it makes more passes over /// the memory to protect from tradeoff attacks. argon2i, - /// Argon2id is a hybrid of Argon2i and Argon2d, using a combination of data-depending and - /// data-independent memory accesses, which gives some of Argon2i's resistance to side-channel + /// Argon2id is a hybrid of Argon2i and Argon2d, using a combination of data-depending and + /// data-independent memory accesses, which gives some of Argon2i's resistance to side-channel /// cache timing attacks and much of Argon2d's resistance to GPU cracking attacks. argon2id, }; @@ -54,7 +54,7 @@ pub const Mode = enum { pub const Params = struct { const Self = @This(); - /// A [t]ime cost, which defines the amount of computation realized and therefore the execution + /// A [t]ime cost, which defines the amount of computation realized and therefore the execution /// time, given in number of iterations. t: u32, @@ -64,16 +64,16 @@ pub const Params = struct { /// A [p]arallelism degree, which defines the number of parallel threads. p: u24, - /// The [secret] parameter, which is used for keyed hashing. This allows a secret key to be input - /// at hashing time (from some external location) and be folded into the value of the hash. This - /// means that even if your salts and hashes are compromised, an attacker cannot brute-force to + /// The [secret] parameter, which is used for keyed hashing. This allows a secret key to be input + /// at hashing time (from some external location) and be folded into the value of the hash. This + /// means that even if your salts and hashes are compromised, an attacker cannot brute-force to /// find the password without the key. secret: ?[]const u8 = null, - /// The [ad] parameter, which is used to fold any additional data into the hash value. Functionally, - /// this behaves almost exactly like the secret or salt parameters; the ad parameter is folding - /// into the value of the hash. However, this parameter is used for different data. The salt - /// should be a random string stored alongside your password. The secret should be a random key + /// The [ad] parameter, which is used to fold any additional data into the hash value. Functionally, + /// this behaves almost exactly like the secret or salt parameters; the ad parameter is folding + /// into the value of the hash. However, this parameter is used for different data. The salt + /// should be a random string stored alongside your password. The secret should be a random key /// only usable at hashing time. The ad is for any other data. ad: ?[]const u8 = null, diff --git a/lib/std/crypto/scrypt.zig b/lib/std/crypto/scrypt.zig index e464cca28e..a9506d3d23 100644 --- a/lib/std/crypto/scrypt.zig +++ b/lib/std/crypto/scrypt.zig @@ -131,7 +131,7 @@ pub const Params = struct { r: u30, /// The [p]arallelization parameter. - /// A large value of [p] can be used to increase the computational cost of scrypt without + /// A large value of [p] can be used to increase the computational cost of scrypt without /// increasing the memory usage. p: u30, @@ -326,7 +326,7 @@ const crypt_format = struct { try out.writeAll(hash_str); } - /// Custom codec that maps 6 bits into 8 like regular Base64, but uses its own alphabet, + /// Custom codec that maps 6 bits into 8 like regular Base64, but uses its own alphabet, /// encodes bits in little-endian, and can also encode integers. fn CustomB64Codec(comptime map: [64]u8) type { return struct { diff --git a/lib/std/math.zig b/lib/std/math.zig index 318d70c726..7111cd6bd5 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -437,7 +437,7 @@ test "max3" { try testing.expect(max3(@as(i32, 2), @as(i32, 1), @as(i32, 0)) == 2); } -/// Limit val to the inclusive range [lower, upper]. +/// Limit val to the inclusive range [lower, upper]. pub fn clamp(val: anytype, lower: anytype, upper: anytype) @TypeOf(val, lower, upper) { assert(lower <= upper); return max(lower, min(val, upper)); diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 0303aa12f7..7ca9b9ccb7 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -1750,8 +1750,8 @@ pub const Mutable = struct { /// Read the value of `x` from `buffer` /// Asserts that `buffer`, `abi_size`, and `bit_count` are large enough to store the value. /// - /// The contents of `buffer` are interpreted as if they were the contents of - /// @ptrCast(*[abi_size]const u8, &x). Byte ordering is determined by `endian` + /// The contents of `buffer` are interpreted as if they were the contents of + /// @ptrCast(*[abi_size]const u8, &x). Byte ordering is determined by `endian` /// and any required padding bits are expected on the MSB end. pub fn readTwosComplement( x: *Mutable, diff --git a/lib/std/mem/Allocator.zig b/lib/std/mem/Allocator.zig index 59e93480ba..2c6d53849d 100644 --- a/lib/std/mem/Allocator.zig +++ b/lib/std/mem/Allocator.zig @@ -50,7 +50,7 @@ pub const VTable = struct { else => *const resizeProto, }, - /// Free and invalidate a buffer. `buf.len` must equal the most recent length returned by `alloc` or `resize`. + /// Free and invalidate a buffer. `buf.len` must equal the most recent length returned by `alloc` or `resize`. /// `buf_align` must equal the same value that was passed as the `ptr_align` parameter to the original `alloc` call. /// /// `ret_addr` is optionally provided as the first return address of the allocation call stack. @@ -603,7 +603,7 @@ test "allocBytes non-zero len_align" { /// allocation could not be granted this function returns `error.OutOfMemory`. /// When the size/alignment is less than or equal to the previous allocation, /// this function returns `error.OutOfMemory` when the allocator decides the client -/// would be better off keeping the extra alignment/size. +/// would be better off keeping the extra alignment/size. /// Clients will call `resizeFn` when they require the allocator to track a new alignment/size, /// and so this function should only return success when the allocator considers /// the reallocation desirable from the allocator's perspective. diff --git a/lib/std/os.zig b/lib/std/os.zig index 096be0b941..df42d14c3d 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1441,7 +1441,7 @@ var wasi_cwd = if (builtin.os.tag == .wasi and !builtin.link_libc) struct { /// Note that `cwd_init` corresponds to a Preopen directory, not necessarily /// a POSIX path. For example, "." matches a Preopen provided with `--dir=.` /// -/// This must be called before using any relative or absolute paths with `std.os` +/// This must be called before using any relative or absolute paths with `std.os` /// functions, if you are on WASI without linking libc. /// /// `alloc` must not be a temporary or leak-detecting allocator, since `std.os` @@ -1475,7 +1475,7 @@ pub fn initPreopensWasi(alloc: Allocator, cwd_init: ?[]const u8) !void { /// Resolve a relative or absolute path to an handle (`fd_t`) and a relative subpath. /// -/// For absolute paths, this automatically searches among available Preopens to find +/// For absolute paths, this automatically searches among available Preopens to find /// a match. For relative paths, it uses the "emulated" CWD. pub fn resolvePathWasi(path: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) !RelativePathWasi { // Note: Due to WASI's "sandboxed" file handles, operations with this RelativePathWasi diff --git a/lib/std/os/uefi/pool_allocator.zig b/lib/std/os/uefi/pool_allocator.zig index 3294621a18..544179aad3 100644 --- a/lib/std/os/uefi/pool_allocator.zig +++ b/lib/std/os/uefi/pool_allocator.zig @@ -90,7 +90,7 @@ const pool_allocator_vtable = Allocator.VTable{ .free = UefiPoolAllocator.free, }; -/// Asserts allocations are 8 byte aligned and calls `boot_services.allocatePool`. +/// Asserts allocations are 8 byte aligned and calls `boot_services.allocatePool`. pub const raw_pool_allocator = Allocator{ .ptr = undefined, .vtable = &raw_pool_allocator_table, diff --git a/lib/std/os/uefi/tables/boot_services.zig b/lib/std/os/uefi/tables/boot_services.zig index ee1e72f094..4774375b3c 100644 --- a/lib/std/os/uefi/tables/boot_services.zig +++ b/lib/std/os/uefi/tables/boot_services.zig @@ -67,7 +67,7 @@ pub const BootServices = extern struct { /// Reinstalls a protocol interface on a device handle reinstallProtocolInterface: fn (handle: Handle, protocol: *align(8) const Guid, old_interface: *anyopaque, new_interface: *anyopaque) callconv(.C) Status, - /// Removes a protocol interface from a device handle. Usage of + /// Removes a protocol interface from a device handle. Usage of /// uninstallMultipleProtocolInterfaces is recommended over this. uninstallProtocolInterface: fn (handle: Handle, protocol: *align(8) const Guid, interface: *anyopaque) callconv(.C) Status, diff --git a/lib/std/packed_int_array.zig b/lib/std/packed_int_array.zig index 599f02ca89..a07fc13af1 100644 --- a/lib/std/packed_int_array.zig +++ b/lib/std/packed_int_array.zig @@ -182,7 +182,7 @@ pub fn PackedIntIo(comptime Int: type, comptime endian: Endian) type { /// Creates a bit-packed array of `Int`. Non-byte-multiple integers /// will take up less memory in PackedIntArray than in a normal array. -/// Elements are packed using native endianess and without storing any +/// Elements are packed using native endianess and without storing any /// meta data. PackedArray(i3, 8) will occupy exactly 3 bytes /// of memory. pub fn PackedIntArray(comptime Int: type, comptime int_count: usize) type { diff --git a/lib/std/simd.zig b/lib/std/simd.zig index 88c86cdd76..a30622aef6 100644 --- a/lib/std/simd.zig +++ b/lib/std/simd.zig @@ -298,7 +298,7 @@ test "vector searching" { pub fn prefixScanWithFunc( comptime hop: isize, vec: anytype, - /// The error type that `func` might return. Set this to `void` if `func` doesn't return an error union. + /// The error type that `func` might return. Set this to `void` if `func` doesn't return an error union. comptime ErrorType: type, comptime func: fn (@TypeOf(vec), @TypeOf(vec)) if (ErrorType == void) @TypeOf(vec) else ErrorType!@TypeOf(vec), /// When one operand of the operation performed by `func` is this value, the result must equal the other operand. diff --git a/lib/std/time.zig b/lib/std/time.zig index 687a5336de..668a4b2cf8 100644 --- a/lib/std/time.zig +++ b/lib/std/time.zig @@ -147,7 +147,7 @@ pub const s_per_day = s_per_hour * 24; pub const s_per_week = s_per_day * 7; /// An Instant represents a timestamp with respect to the currently -/// executing program that ticks during suspend and can be used to +/// executing program that ticks during suspend and can be used to /// record elapsed time unlike `nanoTimestamp`. /// /// It tries to sample the system's fastest and most precise timer available. @@ -256,7 +256,7 @@ pub const Instant = struct { /// /// Monotonicity is ensured by saturating on the most previous sample. /// This means that while timings reported are monotonic, -/// they're not guaranteed to tick at a steady rate as this is up to the underlying system. +/// they're not guaranteed to tick at a steady rate as this is up to the underlying system. pub const Timer = struct { started: Instant, previous: Instant, @@ -290,7 +290,7 @@ pub const Timer = struct { return current.since(self.started); } - /// Returns an Instant sampled at the callsite that is + /// Returns an Instant sampled at the callsite that is /// guaranteed to be monotonic with respect to the timer's starting point. fn sample(self: *Timer) Instant { const current = Instant.now() catch unreachable; diff --git a/lib/std/x/net/tcp.zig b/lib/std/x/net/tcp.zig index cb99940180..a750e27fc9 100644 --- a/lib/std/x/net/tcp.zig +++ b/lib/std/x/net/tcp.zig @@ -186,7 +186,7 @@ pub const Client = struct { /// Have keep-alive messages be sent periodically. The timing in which keep-alive messages are sent are /// dependant on operating system settings. It returns `error.UnsupportedSocketOption` if the host does - /// not support periodically sending keep-alive messages on connection-oriented sockets. + /// not support periodically sending keep-alive messages on connection-oriented sockets. pub fn setKeepAlive(self: Client, enabled: bool) !void { return self.socket.setKeepAlive(enabled); } diff --git a/lib/std/x/os/socket_posix.zig b/lib/std/x/os/socket_posix.zig index 3d8346db17..859075aa20 100644 --- a/lib/std/x/os/socket_posix.zig +++ b/lib/std/x/os/socket_posix.zig @@ -205,7 +205,7 @@ pub fn Mixin(comptime Socket: type) type { /// On connection-oriented sockets, have keep-alive messages be sent periodically. The timing in which keep-alive /// messages are sent are dependant on operating system settings. It returns `error.UnsupportedSocketOption` if - /// the host does not support periodically sending keep-alive messages on connection-oriented sockets. + /// the host does not support periodically sending keep-alive messages on connection-oriented sockets. pub fn setKeepAlive(self: Socket, enabled: bool) !void { if (@hasDecl(os.SO, "KEEPALIVE")) { return self.setOption(os.SOL.SOCKET, os.SO.KEEPALIVE, mem.asBytes(&@as(u32, @boolToInt(enabled)))); @@ -243,7 +243,7 @@ pub fn Mixin(comptime Socket: type) type { /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is /// set on a non-blocking socket. - /// + /// /// Set a timeout on the socket that is to occur if no messages are successfully written /// to its bound destination after a specified number of milliseconds. A subsequent write /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded. @@ -258,7 +258,7 @@ pub fn Mixin(comptime Socket: type) type { /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is /// set on a non-blocking socket. - /// + /// /// Set a timeout on the socket that is to occur if no messages are successfully read /// from its bound destination after a specified number of milliseconds. A subsequent /// read from the socket will thereafter return `error.WouldBlock` should the timeout be diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 9853dee684..d9555c1ff8 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -4712,6 +4712,28 @@ test "zig fmt: space after top level doc comment" { ); } +test "zig fmt: remove trailing whitespace after container doc comment" { + try testTransform( + \\//! top level doc comment + \\ + , + \\//! top level doc comment + \\ + ); +} + +test "zig fmt: remove trailing whitespace after doc comment" { + try testTransform( + \\/// doc comment + \\a = 0, + \\ + , + \\/// doc comment + \\a = 0, + \\ + ); +} + test "zig fmt: for loop with ptr payload and index" { try testCanonical( \\test { diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index eaae725e9a..cf7a161b8d 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -2506,9 +2506,15 @@ fn renderContainerDocComments(ais: *Ais, tree: Ast, start_token: Ast.TokenIndex) fn tokenSliceForRender(tree: Ast, token_index: Ast.TokenIndex) []const u8 { var ret = tree.tokenSlice(token_index); - if (tree.tokens.items(.tag)[token_index] == .multiline_string_literal_line) { - assert(ret[ret.len - 1] == '\n'); - ret.len -= 1; + switch (tree.tokens.items(.tag)[token_index]) { + .multiline_string_literal_line => { + assert(ret[ret.len - 1] == '\n'); + ret.len -= 1; + }, + .container_doc_comment, .doc_comment => { + ret = mem.trimRight(u8, ret, &std.ascii.spaces); + }, + else => {}, } return ret; } diff --git a/lib/std/zig/string_literal.zig b/lib/std/zig/string_literal.zig index 07ce08f491..ebee8a3c9f 100644 --- a/lib/std/zig/string_literal.zig +++ b/lib/std/zig/string_literal.zig @@ -61,7 +61,7 @@ pub fn parseCharLiteral(slice: []const u8) ParsedCharLiteral { } } -/// Parse an escape sequence from `slice[offset..]`. If parsing is successful, +/// Parse an escape sequence from `slice[offset..]`. If parsing is successful, /// offset is updated to reflect the characters consumed. fn parseEscapeSequence(slice: []const u8, offset: *usize) ParsedCharLiteral { assert(slice.len > offset.*); diff --git a/src/arch/wasm/Mir.zig b/src/arch/wasm/Mir.zig index 27f683cf1e..b15dbd9020 100644 --- a/src/arch/wasm/Mir.zig +++ b/src/arch/wasm/Mir.zig @@ -10,7 +10,7 @@ const Mir = @This(); const std = @import("std"); -/// A struct of array that represents each individual wasm +/// A struct of array that represents each individual wasm instructions: std.MultiArrayList(Inst).Slice, /// A slice of indexes where the meaning of the data is determined by the /// `Inst.Tag` value. @@ -538,7 +538,7 @@ pub const Inst = struct { /// Contains an u32 index into a wasm section entry, such as a local. /// Note: This is not an index to another instruction. /// - /// Used by e.g. `local_get`, `local_set`, etc. + /// Used by e.g. `local_get`, `local_set`, etc. label: u32, /// A 32-bit immediate value. /// diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index a99cd58f06..50f28d7f19 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -364,7 +364,7 @@ pub const Inst = struct { dbg_line, /// push registers from the callee_preserved_regs - /// data is the bitfield of which regs to push + /// data is the bitfield of which regs to push /// for example on x86_64, the callee_preserved_regs are [_]Register{ .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; }; /// so to push rcx and r8 one would make data 0b00000000_00000000_00000000_00001001 (the first and fourth bits are set) /// ops is unused diff --git a/src/link/Elf.zig b/src/link/Elf.zig index aa5c5e2a47..9e1ed0cf54 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -154,7 +154,7 @@ atom_by_index_table: std.AutoHashMapUnmanaged(u32, *TextBlock) = .{}, /// const Foo = struct{ /// a: u8, /// }; -/// +/// /// pub fn main() void { /// var foo = Foo{ .a = 1 }; /// _ = foo; diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 1d75eb442a..b193068361 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -232,7 +232,7 @@ atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{}, /// const Foo = struct{ /// a: u8, /// }; -/// +/// /// pub fn main() void { /// var foo = Foo{ .a = 1 }; /// _ = foo; From ac873367b9d68e1d7b4cf4e5efbe179960dc7557 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 5 Apr 2022 18:35:38 +0200 Subject: [PATCH 1061/2031] wasm: Use 'select' instruction for max/min Rather than using blocks and control flow to check which operand is the maximum or minimum, we use wasm's `select` instruction which returns us the operand based on a result from a comparison. This saves us the need of control flow, as well as reduce the instruction count from 13 to 7. --- src/arch/wasm/CodeGen.zig | 37 ++++++++++++++++++------------------- src/arch/wasm/Emit.zig | 2 ++ src/arch/wasm/Mir.zig | 4 ++++ 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index f52506c393..38ab19cb60 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3889,27 +3889,26 @@ fn airMaxMin(self: *Self, inst: Air.Inst.Index, op: enum { max, min }) InnerErro const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const result = try self.allocLocal(ty); - - try self.startBlock(.block, wasm.block_empty); - try self.startBlock(.block, wasm.block_empty); - - // check if LHS is greater/lesser than RHS - const cmp_result = try self.cmp(lhs, rhs, ty, if (op == .max) .gt else .lt); - try self.addLabel(.local_get, cmp_result.local); - try self.addLabel(.br_if, 0); // break to outer loop if LHS is greater/lesser than RHS - - // set RHS as max/min - try self.emitWValue(rhs); - try self.addLabel(.local_set, result.local); - try self.addLabel(.br, 1); // break out of all blocks - try self.endBlock(); - - // set LHS as max/min + // operands to select from try self.emitWValue(lhs); - try self.addLabel(.local_set, result.local); - try self.endBlock(); + try self.emitWValue(rhs); + // operands to compare + try self.emitWValue(lhs); + try self.emitWValue(rhs); + const opcode = buildOpcode(.{ + .op = if (op == .max) .gt else .lt, + .signedness = if (ty.isSignedInt()) .signed else .unsigned, + .valtype1 = typeToValtype(ty, self.target), + }); + try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); + + // based on the result from comparison, return operand 0 or 1. + try self.addTag(.select); + + // store result in local + const result = try self.allocLocal(ty); + try self.addLabel(.local_set, result.local); return result; } diff --git a/src/arch/wasm/Emit.zig b/src/arch/wasm/Emit.zig index 6fc2dfa3b3..bcbff8d195 100644 --- a/src/arch/wasm/Emit.zig +++ b/src/arch/wasm/Emit.zig @@ -96,6 +96,8 @@ pub fn emitMir(emit: *Emit) InnerError!void { .@"return" => try emit.emitTag(tag), .@"unreachable" => try emit.emitTag(tag), + .select => try emit.emitTag(tag), + // arithmetic .i32_eqz => try emit.emitTag(tag), .i32_eq => try emit.emitTag(tag), diff --git a/src/arch/wasm/Mir.zig b/src/arch/wasm/Mir.zig index b15dbd9020..87e64ce9e0 100644 --- a/src/arch/wasm/Mir.zig +++ b/src/arch/wasm/Mir.zig @@ -77,6 +77,10 @@ pub const Inst = struct { /// /// Uses `label` call_indirect = 0x11, + /// Pops three values from the stack and pushes + /// the first or second value dependent on the third value. + /// Uses `tag` + select = 0x1B, /// Loads a local at given index onto the stack. /// /// Uses `label` From dd9782a8bc6f4af458a04cea2619f55e749c39ee Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Apr 2022 23:12:11 -0700 Subject: [PATCH 1062/2031] Sema: fix handling compile errors during circular dependency error Previously, Zig would try to generate a function whose type contained structs or unions which had not been fully resolved due to circular dependency errors. With this commit, `resolveTypeFully` will be sure to return `error.AnalysisFail` even in this scenario, leading to proper display of compilation errors instead of a crash. --- src/Sema.zig | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index b1267e195f..90fe93ed88 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -21547,6 +21547,8 @@ fn resolveUnionLayout( union_obj.status = .have_layout; } +/// Returns `error.AnalysisFail` if any of the types (recursively) failed to +/// be resolved. pub fn resolveTypeFully( sema: *Sema, block: *Block, @@ -21595,18 +21597,29 @@ fn resolveStructFully( const resolved_ty = try sema.resolveTypeFields(block, src, ty); const payload = resolved_ty.castTag(.@"struct") orelse return; const struct_obj = payload.data; + switch (struct_obj.status) { .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {}, .fully_resolved_wip, .fully_resolved => return, } - // After we have resolve struct layout we have to go over the fields again to - // make sure pointer fields get their child types resolved as well - struct_obj.status = .fully_resolved_wip; - for (struct_obj.fields.values()) |field| { - try sema.resolveTypeFully(block, src, field.ty); + log.debug("resolveStructFully {*} ('{s}')", .{ + struct_obj.owner_decl, struct_obj.owner_decl.name, + }); + + { + // After we have resolve struct layout we have to go over the fields again to + // make sure pointer fields get their child types resolved as well. + // See also similar code for unions. + const prev_status = struct_obj.status; + errdefer struct_obj.status = prev_status; + + struct_obj.status = .fully_resolved_wip; + for (struct_obj.fields.values()) |field| { + try sema.resolveTypeFully(block, src, field.ty); + } + struct_obj.status = .fully_resolved; } - struct_obj.status = .fully_resolved; // And let's not forget comptime-only status. _ = try sema.typeRequiresComptime(block, src, ty); @@ -21627,12 +21640,19 @@ fn resolveUnionFully( .fully_resolved_wip, .fully_resolved => return, } - // Same goes for unions (see comment about structs) - union_obj.status = .fully_resolved_wip; - for (union_obj.fields.values()) |field| { - try sema.resolveTypeFully(block, src, field.ty); + { + // After we have resolve union layout we have to go over the fields again to + // make sure pointer fields get their child types resolved as well. + // See also similar code for structs. + const prev_status = union_obj.status; + errdefer union_obj.status = prev_status; + + union_obj.status = .fully_resolved_wip; + for (union_obj.fields.values()) |field| { + try sema.resolveTypeFully(block, src, field.ty); + } + union_obj.status = .fully_resolved; } - union_obj.status = .fully_resolved; // And let's not forget comptime-only status. _ = try sema.typeRequiresComptime(block, src, ty); From 9213aa789b4b44711f2747e5ab053a2b1f57a15b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Apr 2022 23:13:39 -0700 Subject: [PATCH 1063/2031] stage2: ThreadPool: update to new function pointer semantics --- src/ThreadPool.zig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ThreadPool.zig b/src/ThreadPool.zig index ac95def319..36d004cfc6 100644 --- a/src/ThreadPool.zig +++ b/src/ThreadPool.zig @@ -12,7 +12,12 @@ idle_queue: IdleQueue = .{}, const IdleQueue = std.SinglyLinkedList(std.Thread.ResetEvent); const RunQueue = std.SinglyLinkedList(Runnable); const Runnable = struct { - runFn: fn (*Runnable) void, + runFn: RunProto, +}; + +const RunProto = switch (builtin.zig_backend) { + .stage1 => fn (*Runnable) void, + else => *const fn (*Runnable) void, }; const Worker = struct { From 62f54aa39cb3dca3055033a57bc4626e94061aec Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 6 Apr 2022 02:39:55 -0700 Subject: [PATCH 1064/2031] Sema: in-memory coercion of differently named int types which have the same number of bits and the same signedness. --- src/Sema.zig | 11 +++++++++++ test/behavior/cast.zig | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 90fe93ed88..ae6d95ef96 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -18458,6 +18458,17 @@ fn coerceInMemoryAllowed( if (dest_ty.eql(src_ty, target)) return .ok; + // Differently-named integers with the same number of bits. + if (dest_ty.zigTypeTag() == .Int and src_ty.zigTypeTag() == .Int) { + const dest_info = dest_ty.intInfo(target); + const src_info = src_ty.intInfo(target); + if (dest_info.signedness == src_info.signedness and + dest_info.bits == src_info.bits) + { + return .ok; + } + } + // Pointers / Pointer-like Optionals var dest_buf: Type.Payload.ElemType = undefined; var src_buf: Type.Payload.ElemType = undefined; diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 0473a36033..8793e7cb19 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -973,7 +973,8 @@ test "variable initialization uses result locations properly with regards to the } test "cast between C pointer with different but compatible types" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { fn foo(arg: [*]c_ushort) u16 { @@ -985,6 +986,7 @@ test "cast between C pointer with different but compatible types" { } }; try S.doTheTest(); + comptime try S.doTheTest(); } test "peer type resolve string lit with sentinel-terminated mutable slice" { From 5b8ac9821dd25c3e5282130b4d93d6c5b7debb08 Mon Sep 17 00:00:00 2001 From: viri Date: Fri, 1 Apr 2022 15:17:24 -0600 Subject: [PATCH 1065/2031] derive float constants in a generic way (#10133) --- lib/std/math.zig | 98 ++++++++++++++---------------------- lib/std/math/epsilon.zig | 15 ------ lib/std/math/float.zig | 105 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+), 76 deletions(-) delete mode 100644 lib/std/math/epsilon.zig create mode 100644 lib/std/math/float.zig diff --git a/lib/std/math.zig b/lib/std/math.zig index 7111cd6bd5..353b3539b9 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -36,38 +36,44 @@ pub const sqrt2 = 1.414213562373095048801688724209698079; /// 1/sqrt(2) pub const sqrt1_2 = 0.707106781186547524400844362104849039; -pub const f128_true_min = @bitCast(f128, @as(u128, 0x00000000000000000000000000000001)); -pub const f128_min = @bitCast(f128, @as(u128, 0x00010000000000000000000000000000)); -pub const f128_max = @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF)); -pub const f128_epsilon = @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000)); -pub const f128_toint = 1.0 / f128_epsilon; +pub const floatExponentBits = @import("math/float.zig").floatExponentBits; +pub const floatMantissaBits = @import("math/float.zig").floatMantissaBits; +pub const floatMantissaDigits = @import("math/float.zig").floatMantissaDigits; +pub const floatExponentMin = @import("math/float.zig").floatExponentMin; +pub const floatExponentMax = @import("math/float.zig").floatExponentMax; +pub const floatTrueMin = @import("math/float.zig").floatTrueMin; +pub const floatMin = @import("math/float.zig").floatMin; +pub const floatMax = @import("math/float.zig").floatMax; +pub const floatEps = @import("math/float.zig").floatEps; -// float.h details -pub const f80_true_min = make_f80(.{ .fraction = 1, .exp = 0 }); -pub const f80_min = make_f80(.{ .fraction = 0x8000000000000000, .exp = 1 }); -pub const f80_max = make_f80(.{ .fraction = 0xFFFFFFFFFFFFFFFF, .exp = 0x7FFE }); -pub const f80_epsilon = make_f80(.{ .fraction = 0x8000000000000000, .exp = 0x3FC0 }); -pub const f80_toint = 1.0 / f80_epsilon; - -pub const f64_true_min = 4.94065645841246544177e-324; -pub const f64_min = 2.2250738585072014e-308; -pub const f64_max = 1.79769313486231570815e+308; -pub const f64_epsilon = 2.22044604925031308085e-16; -pub const f64_toint = 1.0 / f64_epsilon; - -pub const f32_true_min = 1.40129846432481707092e-45; -pub const f32_min = 1.17549435082228750797e-38; -pub const f32_max = 3.40282346638528859812e+38; -pub const f32_epsilon = 1.1920928955078125e-07; -pub const f32_toint = 1.0 / f32_epsilon; - -pub const f16_true_min = 0.000000059604644775390625; // 2**-24 -pub const f16_min = 0.00006103515625; // 2**-14 -pub const f16_max = 65504; -pub const f16_epsilon = 0.0009765625; // 2**-10 -pub const f16_toint = 1.0 / f16_epsilon; - -pub const epsilon = @import("math/epsilon.zig").epsilon; +// TODO Replace with @compileError("deprecated for foobar") after 0.10.0 is released. +pub const f16_true_min: comptime_float = floatTrueMin(f16); // prev: 0.000000059604644775390625 +pub const f32_true_min: comptime_float = floatTrueMin(f32); // prev: 1.40129846432481707092e-45 +pub const f64_true_min: comptime_float = floatTrueMin(f64); // prev: 4.94065645841246544177e-324 +pub const f80_true_min = floatTrueMin(f80); // prev: make_f80(.{ .fraction = 1, .exp = 0 }) +pub const f128_true_min = floatTrueMin(f128); // prev: @bitCast(f128, @as(u128, 0x00000000000000000000000000000001)) +pub const f16_min: comptime_float = floatMin(f16); // prev: 0.00006103515625 +pub const f32_min: comptime_float = floatMin(f32); // prev: 1.17549435082228750797e-38 +pub const f64_min: comptime_float = floatMin(f64); // prev: 2.2250738585072014e-308 +pub const f80_min = floatMin(f80); // prev: make_f80(.{ .fraction = 0x8000000000000000, .exp = 1 }) +pub const f128_min = floatMin(f128); // prev: @bitCast(f128, @as(u128, 0x00010000000000000000000000000000)) +pub const f16_max: comptime_float = floatMax(f16); // prev: 65504 +pub const f32_max: comptime_float = floatMax(f32); // prev: 3.40282346638528859812e+38 +pub const f64_max: comptime_float = floatMax(f64); // prev: 1.79769313486231570815e+308 +pub const f80_max = floatMax(f80); // prev: make_f80(.{ .fraction = 0xFFFFFFFFFFFFFFFF, .exp = 0x7FFE }) +pub const f128_max = floatMax(f128); // prev: @bitCast(f128, @as(u128, 0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) +pub const f16_epsilon: comptime_float = floatEps(f16); // prev: 0.0009765625 +pub const f32_epsilon: comptime_float = floatEps(f32); // prev: 1.1920928955078125e-07 +pub const f64_epsilon: comptime_float = floatEps(f64); // prev: 2.22044604925031308085e-16 +pub const f80_epsilon = floatEps(f80); // prev: make_f80(.{ .fraction = 0x8000000000000000, .exp = 0x3FC0 }) +pub const f128_epsilon = floatEps(f128); // prev: @bitCast(f128, @as(u128, 0x3F8F0000000000000000000000000000)) +pub const f16_toint: comptime_float = 1.0 / f16_epsilon; // same as before +pub const f32_toint: comptime_float = 1.0 / f32_epsilon; // same as before +pub const f64_toint: comptime_float = 1.0 / f64_epsilon; // same as before +pub const f80_toint = 1.0 / f80_epsilon; // same as before +pub const f128_toint = 1.0 / f128_epsilon; // same as before +pub const epsilon = floatEps; +// End of "soft deprecated" section pub const nan_u16 = @as(u16, 0x7C01); pub const nan_f16 = @bitCast(f16, nan_u16); @@ -294,36 +300,6 @@ test { std.testing.refAllDecls(@This()); } -/// Returns the number of bits in the mantissa of floating point type -/// T. -pub fn floatMantissaBits(comptime T: type) comptime_int { - assert(@typeInfo(T) == .Float); - - return switch (@typeInfo(T).Float.bits) { - 16 => 10, - 32 => 23, - 64 => 52, - 80 => 64, - 128 => 112, - else => @compileError("unknown floating point type " ++ @typeName(T)), - }; -} - -/// Returns the number of bits in the exponent of floating point type -/// T. -pub fn floatExponentBits(comptime T: type) comptime_int { - assert(@typeInfo(T) == .Float); - - return switch (@typeInfo(T).Float.bits) { - 16 => 5, - 32 => 8, - 64 => 11, - 80 => 15, - 128 => 15, - else => @compileError("unknown floating point type " ++ @typeName(T)), - }; -} - /// Given two types, returns the smallest one which is capable of holding the /// full range of the minimum value. pub fn Min(comptime A: type, comptime B: type) type { diff --git a/lib/std/math/epsilon.zig b/lib/std/math/epsilon.zig deleted file mode 100644 index 7f78be1aab..0000000000 --- a/lib/std/math/epsilon.zig +++ /dev/null @@ -1,15 +0,0 @@ -const math = @import("../math.zig"); - -/// Returns the machine epsilon for type T. -/// This is the smallest value of type T that satisfies the inequality 1.0 + -/// epsilon != 1.0. -pub fn epsilon(comptime T: type) T { - return switch (T) { - f16 => math.f16_epsilon, - f32 => math.f32_epsilon, - f64 => math.f64_epsilon, - f80 => math.f80_epsilon, - f128 => math.f128_epsilon, - else => @compileError("epsilon not implemented for " ++ @typeName(T)), - }; -} diff --git a/lib/std/math/float.zig b/lib/std/math/float.zig new file mode 100644 index 0000000000..9858aeb161 --- /dev/null +++ b/lib/std/math/float.zig @@ -0,0 +1,105 @@ +const std = @import("../std.zig"); +const assert = std.debug.assert; +const expect = std.testing.expect; + +/// Creates a raw "1.0" mantissa for floating point type T. Used to dedupe f80 logic. +fn mantissaOne(comptime T: type) comptime_int { + return if (floatMantissaDigits(T) == 64) 1 << 63 else 0; +} + +/// Creates floating point type T from an unbiased exponent and raw mantissa. +fn reconstructFloat(comptime T: type, exponent: comptime_int, mantissa: comptime_int) T { + const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); + const biased_exponent = @as(TBits, exponent + floatExponentMax(T)); + return @bitCast(T, (biased_exponent << floatMantissaBits(T)) | @as(TBits, mantissa)); +} + +/// Returns the number of bits in the exponent of floating point type T. +pub fn floatExponentBits(comptime T: type) comptime_int { + assert(@typeInfo(T) == .Float); + + return switch (@typeInfo(T).Float.bits) { + 16 => 5, + 32 => 8, + 64 => 11, + 80 => 15, + 128 => 15, + else => @compileError("unknown floating point type " ++ @typeName(T)), + }; +} + +/// Returns the number of bits in the mantissa of floating point type T. +pub fn floatMantissaBits(comptime T: type) comptime_int { + assert(@typeInfo(T) == .Float); + + return switch (@typeInfo(T).Float.bits) { + 16 => 10, + 32 => 23, + 64 => 52, + 80 => 64, + 128 => 112, + else => @compileError("unknown floating point type " ++ @typeName(T)), + }; +} + +/// Returns the number of binary digits in the mantissa of floating point type T. +pub fn floatMantissaDigits(comptime T: type) comptime_int { + assert(@typeInfo(T) == .Float); + + // standard IEEE floats have an implicit 0.m or 1.m integer part + // f80 is special and has an explicitly stored bit in the MSB + // this function corresponds to `MANT_DIG' constants from C + return switch (@typeInfo(T).Float.bits) { + 16 => 11, + 32 => 24, + 64 => 53, + 80 => 64, + 128 => 113, + else => @compileError("unknown floating point type " ++ @typeName(T)), + }; +} + +/// Returns the minimum exponent that can represent +/// a normalised value in floating point type T. +pub fn floatExponentMin(comptime T: type) comptime_int { + return -floatExponentMax(T) + 1; +} + +/// Returns the maximum exponent that can represent +/// a normalised value in floating point type T. +pub fn floatExponentMax(comptime T: type) comptime_int { + return (1 << (floatExponentBits(T) - 1)) - 1; +} + +/// Returns the smallest subnormal number representable in floating point type T. +pub fn floatTrueMin(comptime T: type) T { + return reconstructFloat(T, floatExponentMin(T) - 1, 1); +} + +/// Returns the smallest normal number representable in floating point type T. +pub fn floatMin(comptime T: type) T { + return reconstructFloat(T, floatExponentMin(T), mantissaOne(T)); +} + +/// Returns the largest normal number representable in floating point type T. +pub fn floatMax(comptime T: type) T { + const all1s_mantissa = (1 << floatMantissaBits(T)) - 1; + return reconstructFloat(T, floatExponentMax(T), all1s_mantissa); +} + +/// Returns the machine epsilon of floating point type T. +pub fn floatEps(comptime T: type) T { + return reconstructFloat(T, -(floatMantissaDigits(T) - 1), mantissaOne(T)); +} + +test "std.math.float" { + inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| { + // (1 +) for the sign bit, since it is separate from the other bits + const size = 1 + floatExponentBits(T) + floatMantissaBits(T); + try expect(@bitSizeOf(T) == size); + + // for machine epsilon, assert expmin <= -prec <= expmax + try expect(floatExponentMin(T) <= -(floatMantissaDigits(T) - 1)); + try expect(-(floatMantissaDigits(T) - 1) <= floatExponentMax(T)); + } +} From 2f6ee4a97d95fe3e0ff7418e57d05242aa5aae7d Mon Sep 17 00:00:00 2001 From: viri Date: Fri, 1 Apr 2022 15:18:02 -0600 Subject: [PATCH 1066/2031] math.isNormal: simplify implementation, add tests --- lib/std/math/isnormal.zig | 89 +++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 46 deletions(-) diff --git a/lib/std/math/isnormal.zig b/lib/std/math/isnormal.zig index 88e186a3c9..7376b86eb9 100644 --- a/lib/std/math/isnormal.zig +++ b/lib/std/math/isnormal.zig @@ -1,57 +1,54 @@ const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; -const maxInt = std.math.maxInt; -// Returns whether x has a normalized representation (i.e. integer part of mantissa is 1). +/// Returns whether x is neither zero, subnormal, infinity, or NaN. pub fn isNormal(x: anytype) bool { const T = @TypeOf(x); - switch (T) { - f16 => { - const bits = @bitCast(u16, x); - return (bits +% (1 << 10)) & (maxInt(u16) >> 1) >= (1 << 11); - }, - f32 => { - const bits = @bitCast(u32, x); - return (bits +% (1 << 23)) & (maxInt(u32) >> 1) >= (1 << 24); - }, - f64 => { - const bits = @bitCast(u64, x); - return (bits +% (1 << 52)) & (maxInt(u64) >> 1) >= (1 << 53); - }, - f128 => { - const bits = @bitCast(u128, x); - return (bits +% (1 << 112)) & (maxInt(u128) >> 1) >= (1 << 113); - }, - else => { - @compileError("isNormal not implemented for " ++ @typeName(T)); - }, + const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); + if (@typeInfo(T) != .Float) { + @compileError("isNormal not implemented for " ++ @typeName(T)); } + + const increment_exp = 1 << math.floatMantissaBits(T); + const remove_sign = ~@as(TBits, 0) >> 1; + + // We add 1 to the exponent, and if it overflows to 0 or becomes 1, + // then it was all zeroes (subnormal) or all ones (special, inf/nan). + // The sign bit is removed because all ones would overflow into it. + // For f80, even though it has an explicit integer part stored, + // the exponent effectively takes priority if mismatching. + const value = @bitCast(TBits, x) +% increment_exp; + return value & remove_sign >= (increment_exp << 1); } test "math.isNormal" { - try expect(!isNormal(math.nan(f16))); - try expect(!isNormal(math.nan(f32))); - try expect(!isNormal(math.nan(f64))); - try expect(!isNormal(math.nan(f128))); - try expect(!isNormal(-math.nan(f16))); - try expect(!isNormal(-math.nan(f32))); - try expect(!isNormal(-math.nan(f64))); - try expect(!isNormal(-math.nan(f128))); - try expect(!isNormal(math.inf(f16))); - try expect(!isNormal(math.inf(f32))); - try expect(!isNormal(math.inf(f64))); - try expect(!isNormal(math.inf(f128))); - try expect(!isNormal(-math.inf(f16))); - try expect(!isNormal(-math.inf(f32))); - try expect(!isNormal(-math.inf(f64))); - try expect(!isNormal(-math.inf(f128))); - try expect(!isNormal(@as(f16, 0))); - try expect(!isNormal(@as(f32, 0))); - try expect(!isNormal(@as(f64, 0))); - try expect(!isNormal(@as(f128, 0))); - try expect(isNormal(@as(f16, 1.0))); - try expect(isNormal(@as(f32, 1.0))); - try expect(isNormal(@as(f64, 1.0))); - try expect(isNormal(@as(f128, 1.0))); + // TODO remove when #11391 is resolved + if (@import("builtin").os.tag == .freebsd) return error.SkipZigTest; + + // TODO add `c_longdouble' when math.inf(T) supports it + inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { + const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); + + // normals + try expect(isNormal(@as(T, 1.0))); + try expect(isNormal(math.floatMin(T))); + try expect(isNormal(math.floatMax(T))); + + // subnormals + try expect(!isNormal(@as(T, -0.0))); + try expect(!isNormal(@as(T, 0.0))); + try expect(!isNormal(@as(T, math.floatTrueMin(T)))); + + // largest subnormal + try expect(!isNormal(@bitCast(T, ~(~@as(TBits, 0) << math.floatMantissaDigits(T) - 1)))); + + // non-finite numbers + try expect(!isNormal(-math.inf(T))); + try expect(!isNormal(math.inf(T))); + try expect(!isNormal(math.nan(T))); + + // overflow edge-case (described in implementation, also see #10133) + try expect(!isNormal(@bitCast(T, ~@as(TBits, 0)))); + } } From cb019b80ac8ae8ffd7f7dd619c4da29a83668cde Mon Sep 17 00:00:00 2001 From: viri Date: Fri, 1 Apr 2022 15:18:25 -0600 Subject: [PATCH 1067/2031] math.fabs: simplify implementation, add tests --- lib/std/math/fabs.zig | 108 ++++++++++-------------------------------- 1 file changed, 26 insertions(+), 82 deletions(-) diff --git a/lib/std/math/fabs.zig b/lib/std/math/fabs.zig index f098d531cf..f1bb4be7e7 100644 --- a/lib/std/math/fabs.zig +++ b/lib/std/math/fabs.zig @@ -1,13 +1,6 @@ -// Ported from musl, which is licensed under the MIT license: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/fabsf.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/fabs.c - const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; -const maxInt = std.math.maxInt; /// Returns the absolute value of x. /// @@ -16,86 +9,37 @@ const maxInt = std.math.maxInt; /// - fabs(nan) = nan pub fn fabs(x: anytype) @TypeOf(x) { const T = @TypeOf(x); - return switch (T) { - f16 => fabs16(x), - f32 => fabs32(x), - f64 => fabs64(x), - f128 => fabs128(x), - else => @compileError("fabs not implemented for " ++ @typeName(T)), - }; -} + const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); + if (@typeInfo(T) != .Float) { + @compileError("fabs not implemented for " ++ @typeName(T)); + } -fn fabs16(x: f16) f16 { - var u = @bitCast(u16, x); - u &= maxInt(u16) >> 1; - return @bitCast(f16, u); -} + const float_bits = @bitCast(TBits, x); + const remove_sign = ~@as(TBits, 0) >> 1; -fn fabs32(x: f32) f32 { - var u = @bitCast(u32, x); - u &= maxInt(u32) >> 1; - return @bitCast(f32, u); -} - -fn fabs64(x: f64) f64 { - var u = @bitCast(u64, x); - u &= maxInt(u64) >> 1; - return @bitCast(f64, u); -} - -fn fabs128(x: f128) f128 { - var u = @bitCast(u128, x); - u &= maxInt(u128) >> 1; - return @bitCast(f128, u); + return @bitCast(T, float_bits & remove_sign); } test "math.fabs" { - try expect(fabs(@as(f16, 1.0)) == fabs16(1.0)); - try expect(fabs(@as(f32, 1.0)) == fabs32(1.0)); - try expect(fabs(@as(f64, 1.0)) == fabs64(1.0)); - try expect(fabs(@as(f128, 1.0)) == fabs128(1.0)); -} + // TODO add support for f80 & c_longdouble here + inline for ([_]type{ f16, f32, f64, f128 }) |T| { + // normals + try expect(fabs(@as(T, 1.0)) == 1.0); + try expect(fabs(@as(T, -1.0)) == 1.0); + try expect(fabs(math.floatMin(T)) == math.floatMin(T)); + try expect(fabs(-math.floatMin(T)) == math.floatMin(T)); + try expect(fabs(math.floatMax(T)) == math.floatMax(T)); + try expect(fabs(-math.floatMax(T)) == math.floatMax(T)); -test "math.fabs16" { - try expect(fabs16(1.0) == 1.0); - try expect(fabs16(-1.0) == 1.0); -} + // subnormals + try expect(fabs(@as(T, 0.0)) == 0.0); + try expect(fabs(@as(T, -0.0)) == 0.0); + try expect(fabs(math.floatTrueMin(T)) == math.floatTrueMin(T)); + try expect(fabs(-math.floatTrueMin(T)) == math.floatTrueMin(T)); -test "math.fabs32" { - try expect(fabs32(1.0) == 1.0); - try expect(fabs32(-1.0) == 1.0); -} - -test "math.fabs64" { - try expect(fabs64(1.0) == 1.0); - try expect(fabs64(-1.0) == 1.0); -} - -test "math.fabs128" { - try expect(fabs128(1.0) == 1.0); - try expect(fabs128(-1.0) == 1.0); -} - -test "math.fabs16.special" { - try expect(math.isPositiveInf(fabs(math.inf(f16)))); - try expect(math.isPositiveInf(fabs(-math.inf(f16)))); - try expect(math.isNan(fabs(math.nan(f16)))); -} - -test "math.fabs32.special" { - try expect(math.isPositiveInf(fabs(math.inf(f32)))); - try expect(math.isPositiveInf(fabs(-math.inf(f32)))); - try expect(math.isNan(fabs(math.nan(f32)))); -} - -test "math.fabs64.special" { - try expect(math.isPositiveInf(fabs(math.inf(f64)))); - try expect(math.isPositiveInf(fabs(-math.inf(f64)))); - try expect(math.isNan(fabs(math.nan(f64)))); -} - -test "math.fabs128.special" { - try expect(math.isPositiveInf(fabs(math.inf(f128)))); - try expect(math.isPositiveInf(fabs(-math.inf(f128)))); - try expect(math.isNan(fabs(math.nan(f128)))); + // non-finite numbers + try expect(math.isPositiveInf(fabs(math.inf(T)))); + try expect(math.isPositiveInf(fabs(-math.inf(T)))); + try expect(math.isNan(fabs(math.nan(T)))); + } } From 7b7f45dc2a54aa9dfd6263b2654a5ccc8c5d2c82 Mon Sep 17 00:00:00 2001 From: viri Date: Fri, 1 Apr 2022 15:21:07 -0600 Subject: [PATCH 1068/2031] std.{fmt, math}: derive float constants from std This also addresses a nit from #10133 where IntT might be a confusing name because it might imply signed integer (iX, not uX). We settled on TBits for math/float.zig so I've applied that change here too. When I originally wrote ldexp() I copied the name from parse_hex_float. --- lib/std/fmt/parse_hex_float.zig | 17 ++++++++--------- lib/std/math/ldexp.zig | 16 ++++++++-------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/lib/std/fmt/parse_hex_float.zig b/lib/std/fmt/parse_hex_float.zig index 83c798ab96..3885e7e1a8 100644 --- a/lib/std/fmt/parse_hex_float.zig +++ b/lib/std/fmt/parse_hex_float.zig @@ -12,17 +12,16 @@ const assert = std.debug.assert; pub fn parseHexFloat(comptime T: type, s: []const u8) !T { assert(@typeInfo(T) == .Float); - const IntT = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); + const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); const mantissa_bits = math.floatMantissaBits(T); const exponent_bits = math.floatExponentBits(T); + const exponent_min = math.floatExponentMin(T); + const exponent_max = math.floatExponentMax(T); + const exponent_bias = exponent_max; const sign_shift = mantissa_bits + exponent_bits; - const exponent_bias = (1 << (exponent_bits - 1)) - 1; - const exponent_min = 1 - exponent_bias; - const exponent_max = exponent_bias; - if (s.len == 0) return error.InvalidCharacter; @@ -233,10 +232,10 @@ pub fn parseHexFloat(comptime T: type, s: []const u8) !T { // Remove the implicit bit. mantissa &= @as(u128, (1 << mantissa_bits) - 1); - const raw: IntT = - (if (negative) @as(IntT, 1) << sign_shift else 0) | - @as(IntT, @bitCast(u16, exponent + exponent_bias)) << mantissa_bits | - @truncate(IntT, mantissa); + const raw: TBits = + (if (negative) @as(TBits, 1) << sign_shift else 0) | + @as(TBits, @bitCast(u16, exponent + exponent_bias)) << mantissa_bits | + @truncate(TBits, mantissa); return @bitCast(T, raw); } diff --git a/lib/std/math/ldexp.zig b/lib/std/math/ldexp.zig index 228bd7dd39..f8ab237fad 100644 --- a/lib/std/math/ldexp.zig +++ b/lib/std/math/ldexp.zig @@ -15,22 +15,22 @@ pub fn ldexp(x: anytype, n: i32) @TypeOf(x) { var shift = n; const T = @TypeOf(base); - const IntT = std.meta.Int(.unsigned, @bitSizeOf(T)); + const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); if (@typeInfo(T) != .Float) { @compileError("ldexp not implemented for " ++ @typeName(T)); } const mantissa_bits = math.floatMantissaBits(T); - const exponent_bits = math.floatExponentBits(T); - const exponent_bias = (1 << (exponent_bits - 1)) - 1; - const exponent_min = 1 - exponent_bias; - const exponent_max = exponent_bias; + const exponent_min = math.floatExponentMin(T); + const exponent_max = math.floatExponentMax(T); + + const exponent_bias = exponent_max; // fix double rounding errors in subnormal ranges // https://git.musl-libc.org/cgit/musl/commit/src/math/ldexp.c?id=8c44a060243f04283ca68dad199aab90336141db const scale_min_expo = exponent_min + mantissa_bits + 1; - const scale_min = @bitCast(T, @as(IntT, scale_min_expo + exponent_bias) << mantissa_bits); - const scale_max = @bitCast(T, @intCast(IntT, exponent_max + exponent_bias) << mantissa_bits); + const scale_min = @bitCast(T, @as(TBits, scale_min_expo + exponent_bias) << mantissa_bits); + const scale_max = @bitCast(T, @intCast(TBits, exponent_max + exponent_bias) << mantissa_bits); // scale `shift` within floating point limits, if possible // second pass is possible due to subnormal range @@ -53,7 +53,7 @@ pub fn ldexp(x: anytype, n: i32) @TypeOf(x) { } } - return base * @bitCast(T, @intCast(IntT, shift + exponent_bias) << mantissa_bits); + return base * @bitCast(T, @intCast(TBits, shift + exponent_bias) << mantissa_bits); } test "math.ldexp" { From 289ba5dfc2dbd5f6f179c49e974b0332f16604a6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 6 Apr 2022 11:50:23 -0700 Subject: [PATCH 1069/2031] stage2: rename InternArena to InternPool --- src/{InternArena.zig => InternPool.zig} | 70 ++++++++++++------------- src/link/Dwarf.zig | 2 +- 2 files changed, 36 insertions(+), 36 deletions(-) rename src/{InternArena.zig => InternPool.zig} (79%) diff --git a/src/InternArena.zig b/src/InternPool.zig similarity index 79% rename from src/InternArena.zig rename to src/InternPool.zig index c2397b8b42..95947df61b 100644 --- a/src/InternArena.zig +++ b/src/InternPool.zig @@ -2,17 +2,17 @@ map: std.AutoArrayHashMapUnmanaged(void, void) = .{}, items: std.MultiArrayList(Item) = .{}, extra: std.ArrayListUnmanaged(u32) = .{}, -const InternArena = @This(); +const InternPool = @This(); const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; const KeyAdapter = struct { - intern_arena: *const InternArena, + intern_pool: *const InternPool, pub fn eql(ctx: @This(), a: Key, b_void: void, b_map_index: usize) bool { _ = b_void; - return ctx.intern_arena.indexToKey(@intToEnum(Index, b_map_index)).eql(a); + return ctx.intern_pool.indexToKey(@intToEnum(Index, b_map_index)).eql(a); } pub fn hash(ctx: @This(), a: Key) u32 { @@ -94,10 +94,10 @@ pub const Item = struct { }; /// Represents an index into `map`. It represents the canonical index -/// of a `Value` within this `InternArena`. The values are typed. +/// of a `Value` within this `InternPool`. The values are typed. /// Two values which have the same type can be equality compared simply /// by checking if their indexes are equal, provided they are both in -/// the same `InternArena`. +/// the same `InternPool`. pub const Index = enum(u32) { none = std.math.maxInt(u32), _, @@ -180,14 +180,14 @@ pub const Array = struct { child: Index, }; -pub fn deinit(ia: *InternArena, gpa: Allocator) void { - ia.map.deinit(gpa); - ia.items.deinit(gpa); - ia.extra.deinit(gpa); +pub fn deinit(ip: *InternPool, gpa: Allocator) void { + ip.map.deinit(gpa); + ip.items.deinit(gpa); + ip.extra.deinit(gpa); } -pub fn indexToKey(ia: InternArena, index: Index) Key { - const item = ia.items.get(@enumToInt(index)); +pub fn indexToKey(ip: InternPool, index: Index) Key { + const item = ip.items.get(@enumToInt(index)); const data = item.data; return switch (item.tag) { .type_int_signed => .{ @@ -203,7 +203,7 @@ pub fn indexToKey(ia: InternArena, index: Index) Key { }, }, .type_array => { - const array_info = ia.extraData(Array, data); + const array_info = ip.extraData(Array, data); return .{ .array_type = .{ .len = array_info.len, .child = array_info.child, @@ -216,9 +216,9 @@ pub fn indexToKey(ia: InternArena, index: Index) Key { }; } -pub fn get(ia: *InternArena, gpa: Allocator, key: Key) Allocator.Error!Index { - const adapter: KeyAdapter = .{ .intern_arena = ia }; - const gop = try ia.map.getOrPutAdapted(gpa, key, adapter); +pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { + const adapter: KeyAdapter = .{ .intern_pool = ip }; + const gop = try ip.map.getOrPutAdapted(gpa, key, adapter); if (gop.found_existing) { return @intToEnum(Index, gop.index); } @@ -228,7 +228,7 @@ pub fn get(ia: *InternArena, gpa: Allocator, key: Key) Allocator.Error!Index { .signed => .type_int_signed, .unsigned => .type_int_unsigned, }; - try ia.items.append(gpa, .{ + try ip.items.append(gpa, .{ .tag = tag, .data = int_type.bits, }); @@ -236,9 +236,9 @@ pub fn get(ia: *InternArena, gpa: Allocator, key: Key) Allocator.Error!Index { .array_type => |array_type| { const len = @intCast(u32, array_type.len); // TODO have a big_array encoding assert(array_type.sentinel == .none); // TODO have a sentinel_array encoding - try ia.items.append(gpa, .{ + try ip.items.append(gpa, .{ .tag = .type_array, - .data = try ia.addExtra(gpa, Array{ + .data = try ip.addExtra(gpa, Array{ .len = len, .child = array_type.child, }), @@ -246,20 +246,20 @@ pub fn get(ia: *InternArena, gpa: Allocator, key: Key) Allocator.Error!Index { }, else => @panic("TODO"), } - return @intToEnum(Index, ia.items.len - 1); + return @intToEnum(Index, ip.items.len - 1); } -fn addExtra(ia: *InternArena, gpa: Allocator, extra: anytype) Allocator.Error!u32 { +fn addExtra(ip: *InternPool, gpa: Allocator, extra: anytype) Allocator.Error!u32 { const fields = std.meta.fields(@TypeOf(extra)); - try ia.extra.ensureUnusedCapacity(gpa, fields.len); - return ia.addExtraAssumeCapacity(extra); + try ip.extra.ensureUnusedCapacity(gpa, fields.len); + return ip.addExtraAssumeCapacity(extra); } -fn addExtraAssumeCapacity(ia: *InternArena, extra: anytype) u32 { +fn addExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 { const fields = std.meta.fields(@TypeOf(extra)); - const result = @intCast(u32, ia.extra.items.len); + const result = @intCast(u32, ip.extra.items.len); inline for (fields) |field| { - ia.extra.appendAssumeCapacity(switch (field.field_type) { + ip.extra.appendAssumeCapacity(switch (field.field_type) { u32 => @field(extra, field.name), Index => @enumToInt(@field(extra, field.name)), i32 => @bitCast(u32, @field(extra, field.name)), @@ -269,15 +269,15 @@ fn addExtraAssumeCapacity(ia: *InternArena, extra: anytype) u32 { return result; } -fn extraData(ia: InternArena, comptime T: type, index: usize) T { +fn extraData(ip: InternPool, comptime T: type, index: usize) T { const fields = std.meta.fields(T); var i: usize = index; var result: T = undefined; inline for (fields) |field| { @field(result, field.name) = switch (field.field_type) { - u32 => ia.extra.items[i], - Index => @intToEnum(Index, ia.extra.items[i]), - i32 => @bitCast(i32, ia.extra.items[i]), + u32 => ip.extra.items[i], + Index => @intToEnum(Index, ip.extra.items[i]), + i32 => @bitCast(i32, ip.extra.items[i]), else => @compileError("bad field type"), }; i += 1; @@ -288,26 +288,26 @@ fn extraData(ia: InternArena, comptime T: type, index: usize) T { test "basic usage" { const gpa = std.testing.allocator; - var ia: InternArena = .{}; - defer ia.deinit(gpa); + var ip: InternPool = .{}; + defer ip.deinit(gpa); - const i32_type = try ia.get(gpa, .{ .int_type = .{ + const i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32, } }); - const array_i32 = try ia.get(gpa, .{ .array_type = .{ + const array_i32 = try ip.get(gpa, .{ .array_type = .{ .len = 10, .child = i32_type, .sentinel = .none, } }); - const another_i32_type = try ia.get(gpa, .{ .int_type = .{ + const another_i32_type = try ip.get(gpa, .{ .int_type = .{ .signedness = .signed, .bits = 32, } }); try std.testing.expect(another_i32_type == i32_type); - const another_array_i32 = try ia.get(gpa, .{ .array_type = .{ + const another_array_i32 = try ip.get(gpa, .{ .array_type = .{ .len = 10, .child = i32_type, .sentinel = .none, diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 4c6a80fe98..fc392bfe3e 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -39,7 +39,7 @@ atom_last: ?*Atom = null, abbrev_table_offset: ?u64 = null, -/// TODO replace with InternArena +/// TODO replace with InternPool /// Table of debug symbol names. strtab: std.ArrayListUnmanaged(u8) = .{}, From 5eee8f70d1bff5504760f7080e1d50b91be4dc1c Mon Sep 17 00:00:00 2001 From: Wojtek Mach Date: Wed, 6 Apr 2022 22:48:07 +0200 Subject: [PATCH 1070/2031] zig cc: support --subsystem linker flag Example: $ zig cc -o main.exe main.cpp -target x86_64-windows -Wl,--subsystem,windows --- src/main.zig | 69 +++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/src/main.zig b/src/main.zig index a647216b05..aec88c1175 100644 --- a/src/main.zig +++ b/src/main.zig @@ -849,36 +849,7 @@ fn buildOutputType( const next_arg = args_iter.next() orelse { fatal("expected parameter after {s}", .{arg}); }; - if (mem.eql(u8, next_arg, "console")) { - subsystem = .Console; - } else if (mem.eql(u8, next_arg, "windows")) { - subsystem = .Windows; - } else if (mem.eql(u8, next_arg, "posix")) { - subsystem = .Posix; - } else if (mem.eql(u8, next_arg, "native")) { - subsystem = .Native; - } else if (mem.eql(u8, next_arg, "efi_application")) { - subsystem = .EfiApplication; - } else if (mem.eql(u8, next_arg, "efi_boot_service_driver")) { - subsystem = .EfiBootServiceDriver; - } else if (mem.eql(u8, next_arg, "efi_rom")) { - subsystem = .EfiRom; - } else if (mem.eql(u8, next_arg, "efi_runtime_driver")) { - subsystem = .EfiRuntimeDriver; - } else { - fatal("invalid: --subsystem: '{s}'. Options are:\n{s}", .{ - next_arg, - \\ console - \\ windows - \\ posix - \\ native - \\ efi_application - \\ efi_boot_service_driver - \\ efi_rom - \\ efi_runtime_driver - \\ - }); - } + subsystem = try parseSubSystem(next_arg); } else if (mem.eql(u8, arg, "-O")) { optimize_mode_string = args_iter.next() orelse { fatal("expected parameter after {s}", .{arg}); @@ -1467,6 +1438,11 @@ fn buildOutputType( mem.eql(u8, linker_arg, "-static")) { force_static_libs = true; + } else if (mem.eql(u8, linker_arg, "--subsystem")) { + const next_arg = split_it.next() orelse { + fatal("expected parameter after {s}", .{linker_arg}); + }; + subsystem = try parseSubSystem(next_arg); } else { try linker_args.append(linker_arg); } @@ -5132,3 +5108,36 @@ fn warnAboutForeignBinaries( }, } } + +fn parseSubSystem(next_arg: []const u8) !std.Target.SubSystem { + if (mem.eql(u8, next_arg, "console")) { + return .Console; + } else if (mem.eql(u8, next_arg, "windows")) { + return .Windows; + } else if (mem.eql(u8, next_arg, "posix")) { + return .Posix; + } else if (mem.eql(u8, next_arg, "native")) { + return .Native; + } else if (mem.eql(u8, next_arg, "efi_application")) { + return .EfiApplication; + } else if (mem.eql(u8, next_arg, "efi_boot_service_driver")) { + return .EfiBootServiceDriver; + } else if (mem.eql(u8, next_arg, "efi_rom")) { + return .EfiRom; + } else if (mem.eql(u8, next_arg, "efi_runtime_driver")) { + return .EfiRuntimeDriver; + } else { + fatal("invalid: --subsystem: '{s}'. Options are:\n{s}", .{ + next_arg, + \\ console + \\ windows + \\ posix + \\ native + \\ efi_application + \\ efi_boot_service_driver + \\ efi_rom + \\ efi_runtime_driver + \\ + }); + } +} From a2f5f0da5c21dd89834ec2b284ef2e7d2f5851a2 Mon Sep 17 00:00:00 2001 From: viri Date: Thu, 7 Apr 2022 01:24:46 -0600 Subject: [PATCH 1071/2031] std.math.isFinite: make generic, support f80 --- lib/std/math/isfinite.zig | 74 +++++++++++++++------------------------ 1 file changed, 29 insertions(+), 45 deletions(-) diff --git a/lib/std/math/isfinite.zig b/lib/std/math/isfinite.zig index 762fb39991..77aab8bf76 100644 --- a/lib/std/math/isfinite.zig +++ b/lib/std/math/isfinite.zig @@ -1,59 +1,43 @@ const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; -const maxInt = std.math.maxInt; /// Returns whether x is a finite value. pub fn isFinite(x: anytype) bool { const T = @TypeOf(x); - switch (T) { - f16 => { - const bits = @bitCast(u16, x); - return bits & 0x7FFF < 0x7C00; - }, - f32 => { - const bits = @bitCast(u32, x); - return bits & 0x7FFFFFFF < 0x7F800000; - }, - f64 => { - const bits = @bitCast(u64, x); - return bits & (maxInt(u64) >> 1) < (0x7FF << 52); - }, - f128 => { - const bits = @bitCast(u128, x); - return bits & (maxInt(u128) >> 1) < (0x7FFF << 112); - }, - else => { - @compileError("isFinite not implemented for " ++ @typeName(T)); - }, + const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); + if (@typeInfo(T) != .Float) { + @compileError("isFinite not implemented for " ++ @typeName(T)); } + const exponent_bits = math.floatExponentBits(T); + const mantissa_bits = math.floatMantissaBits(T); + const all1s_exponent = ((1 << exponent_bits) - 1) << mantissa_bits; + const remove_sign = ~@as(TBits, 0) >> 1; + return @bitCast(TBits, x) & remove_sign < all1s_exponent; } test "math.isFinite" { - try expect(isFinite(@as(f16, 0.0))); - try expect(isFinite(@as(f16, -0.0))); - try expect(isFinite(@as(f32, 0.0))); - try expect(isFinite(@as(f32, -0.0))); - try expect(isFinite(@as(f64, 0.0))); - try expect(isFinite(@as(f64, -0.0))); - try expect(isFinite(@as(f128, 0.0))); - try expect(isFinite(@as(f128, -0.0))); + // TODO remove when #11391 is resolved + if (@import("builtin").os.tag == .freebsd) return error.SkipZigTest; - try expect(!isFinite(math.inf(f16))); - try expect(!isFinite(-math.inf(f16))); - try expect(!isFinite(math.inf(f32))); - try expect(!isFinite(-math.inf(f32))); - try expect(!isFinite(math.inf(f64))); - try expect(!isFinite(-math.inf(f64))); - try expect(!isFinite(math.inf(f128))); - try expect(!isFinite(-math.inf(f128))); + inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { + // normals + try expect(isFinite(@as(T, 1.0))); + try expect(isFinite(-@as(T, 1.0))); - try expect(!isFinite(math.nan(f16))); - try expect(!isFinite(-math.nan(f16))); - try expect(!isFinite(math.nan(f32))); - try expect(!isFinite(-math.nan(f32))); - try expect(!isFinite(math.nan(f64))); - try expect(!isFinite(-math.nan(f64))); - try expect(!isFinite(math.nan(f128))); - try expect(!isFinite(-math.nan(f128))); + // zero & subnormals + try expect(isFinite(@as(T, 0.0))); + try expect(isFinite(@as(T, -0.0))); + try expect(isFinite(math.floatTrueMin(T))); + + // other float limits + try expect(isFinite(math.floatMin(T))); + try expect(isFinite(math.floatMax(T))); + + // inf & nan + try expect(!isFinite(math.inf(T))); + try expect(!isFinite(-math.inf(T))); + try expect(!isFinite(math.nan(T))); + try expect(!isFinite(-math.nan(T))); + } } From c5c62605341143b19accc00f28af1c46c0a416be Mon Sep 17 00:00:00 2001 From: viri Date: Thu, 7 Apr 2022 02:34:10 -0600 Subject: [PATCH 1072/2031] std.math: generalise `inf`, even simpler `isFinite` --- CMakeLists.txt | 2 +- lib/std/math.zig | 24 ++++++++++-------------- lib/std/math/float.zig | 5 +++++ lib/std/math/inf.zig | 14 -------------- lib/std/math/isfinite.zig | 5 +---- 5 files changed, 17 insertions(+), 33 deletions(-) delete mode 100644 lib/std/math/inf.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 39d12843be..22e17d84c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -444,9 +444,9 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/math.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/big.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/big/int.zig" + "${CMAKE_SOURCE_DIR}/lib/std/math/float.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/floor.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/frexp.zig" - "${CMAKE_SOURCE_DIR}/lib/std/math/inf.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/isinf.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/isnan.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/ln.zig" diff --git a/lib/std/math.zig b/lib/std/math.zig index 353b3539b9..1ef7ac9798 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -45,6 +45,7 @@ pub const floatTrueMin = @import("math/float.zig").floatTrueMin; pub const floatMin = @import("math/float.zig").floatMin; pub const floatMax = @import("math/float.zig").floatMax; pub const floatEps = @import("math/float.zig").floatEps; +pub const inf = @import("math/float.zig").inf; // TODO Replace with @compileError("deprecated for foobar") after 0.10.0 is released. pub const f16_true_min: comptime_float = floatTrueMin(f16); // prev: 0.000000059604644775390625 @@ -72,6 +73,15 @@ pub const f32_toint: comptime_float = 1.0 / f32_epsilon; // same as before pub const f64_toint: comptime_float = 1.0 / f64_epsilon; // same as before pub const f80_toint = 1.0 / f80_epsilon; // same as before pub const f128_toint = 1.0 / f128_epsilon; // same as before +pub const inf_u16 = @bitCast(u16, inf_f16); // prev: @as(u16, 0x7C00) +pub const inf_f16 = inf(f16); // prev: @bitCast(f16, inf_u16) +pub const inf_u32 = @bitCast(u32, inf_f32); // prev: @as(u32, 0x7F800000) +pub const inf_f32 = inf(f32); // prev: @bitCast(f32, inf_u32) +pub const inf_u64 = @bitCast(u64, inf_f64); // prev: @as(u64, 0x7FF << 52) +pub const inf_f64 = inf(f64); // prev: @bitCast(f64, inf_u64) +pub const inf_f80 = inf(f80); // prev: make_f80(F80{ .fraction = 0x8000000000000000, .exp = 0x7fff }) +pub const inf_u128 = @bitCast(u128, inf_f128); // prev: @as(u128, 0x7fff0000000000000000000000000000) +pub const inf_f128 = inf(f128); // prev: @bitCast(f128, inf_u128) pub const epsilon = floatEps; // End of "soft deprecated" section @@ -81,28 +91,18 @@ pub const nan_f16 = @bitCast(f16, nan_u16); pub const qnan_u16 = @as(u16, 0x7E00); pub const qnan_f16 = @bitCast(f16, qnan_u16); -pub const inf_u16 = @as(u16, 0x7C00); -pub const inf_f16 = @bitCast(f16, inf_u16); - pub const nan_u32 = @as(u32, 0x7F800001); pub const nan_f32 = @bitCast(f32, nan_u32); pub const qnan_u32 = @as(u32, 0x7FC00000); pub const qnan_f32 = @bitCast(f32, qnan_u32); -pub const inf_u32 = @as(u32, 0x7F800000); -pub const inf_f32 = @bitCast(f32, inf_u32); - pub const nan_u64 = @as(u64, 0x7FF << 52) | 1; pub const nan_f64 = @bitCast(f64, nan_u64); pub const qnan_u64 = @as(u64, 0x7ff8000000000000); pub const qnan_f64 = @bitCast(f64, qnan_u64); -pub const inf_u64 = @as(u64, 0x7FF << 52); -pub const inf_f64 = @bitCast(f64, inf_u64); - -pub const inf_f80 = make_f80(F80{ .fraction = 0x8000000000000000, .exp = 0x7fff }); pub const nan_f80 = make_f80(F80{ .fraction = 0xA000000000000000, .exp = 0x7fff }); pub const qnan_f80 = make_f80(F80{ .fraction = 0xC000000000000000, .exp = 0x7fff }); @@ -112,12 +112,8 @@ pub const nan_f128 = @bitCast(f128, nan_u128); pub const qnan_u128 = @as(u128, 0x7fff8000000000000000000000000000); pub const qnan_f128 = @bitCast(f128, qnan_u128); -pub const inf_u128 = @as(u128, 0x7fff0000000000000000000000000000); -pub const inf_f128 = @bitCast(f128, inf_u128); - pub const nan = @import("math/nan.zig").nan; pub const snan = @import("math/nan.zig").snan; -pub const inf = @import("math/inf.zig").inf; /// Performs an approximate comparison of two floating point values `x` and `y`. /// Returns true if the absolute difference between them is less or equal than diff --git a/lib/std/math/float.zig b/lib/std/math/float.zig index 9858aeb161..6d9c17d2a2 100644 --- a/lib/std/math/float.zig +++ b/lib/std/math/float.zig @@ -92,6 +92,11 @@ pub fn floatEps(comptime T: type) T { return reconstructFloat(T, -(floatMantissaDigits(T) - 1), mantissaOne(T)); } +/// Returns the value inf for floating point type T. +pub fn inf(comptime T: type) T { + return reconstructFloat(T, floatExponentMax(T) + 1, mantissaOne(T)); +} + test "std.math.float" { inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| { // (1 +) for the sign bit, since it is separate from the other bits diff --git a/lib/std/math/inf.zig b/lib/std/math/inf.zig deleted file mode 100644 index fd7d7c4380..0000000000 --- a/lib/std/math/inf.zig +++ /dev/null @@ -1,14 +0,0 @@ -const std = @import("../std.zig"); -const math = std.math; - -/// Returns value inf for the type T. -pub fn inf(comptime T: type) T { - return switch (T) { - f16 => math.inf_f16, - f32 => math.inf_f32, - f64 => math.inf_f64, - f80 => math.inf_f80, - f128 => math.inf_f128, - else => @compileError("inf not implemented for " ++ @typeName(T)), - }; -} diff --git a/lib/std/math/isfinite.zig b/lib/std/math/isfinite.zig index 77aab8bf76..e9314213ce 100644 --- a/lib/std/math/isfinite.zig +++ b/lib/std/math/isfinite.zig @@ -9,11 +9,8 @@ pub fn isFinite(x: anytype) bool { if (@typeInfo(T) != .Float) { @compileError("isFinite not implemented for " ++ @typeName(T)); } - const exponent_bits = math.floatExponentBits(T); - const mantissa_bits = math.floatMantissaBits(T); - const all1s_exponent = ((1 << exponent_bits) - 1) << mantissa_bits; const remove_sign = ~@as(TBits, 0) >> 1; - return @bitCast(TBits, x) & remove_sign < all1s_exponent; + return @bitCast(TBits, x) & remove_sign < @bitCast(TBits, math.inf(T)); } test "math.isFinite" { From 93e11b824a37a14fc392bfc64ed8f364f4fc7d46 Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Thu, 7 Apr 2022 10:46:23 +0200 Subject: [PATCH 1073/2031] crypto/x25519: implement clearCofactor() (#11355) This is the x25519 counterpart to `edwards25519.clearCofactor()`. It is useful to check for low-order points in protocols where it matters and where clamping cannot work, such as PAKEs. --- lib/std/crypto/25519/curve25519.zig | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/std/crypto/25519/curve25519.zig b/lib/std/crypto/25519/curve25519.zig index 20b0dccaa0..f5938dd218 100644 --- a/lib/std/crypto/25519/curve25519.zig +++ b/lib/std/crypto/25519/curve25519.zig @@ -39,8 +39,11 @@ pub const Curve25519 = struct { } } - /// Multiply a point by the cofactor - pub const clearCofactor = @compileError("TODO what was this function supposed to do? it didn't compile successfully"); + /// Multiply a point by the cofactor, returning WeakPublicKey if the element is in a small-order group. + pub fn clearCofactor(p: Curve25519) WeakPublicKeyError!Curve25519 { + const cofactor = [_]u8{8} ++ [_]u8{0} ** 31; + return ladder(p, cofactor, 4) catch return error.WeakPublicKey; + } fn ladder(p: Curve25519, s: [32]u8, comptime bits: usize) IdentityElementError!Curve25519 { var x1 = p.x; @@ -94,8 +97,7 @@ pub const Curve25519 = struct { /// the identity element or error.WeakPublicKey if the public /// key is a low-order point. pub fn mul(p: Curve25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Curve25519 { - const cofactor = [_]u8{8} ++ [_]u8{0} ** 31; - _ = ladder(p, cofactor, 4) catch return error.WeakPublicKey; + _ = try p.clearCofactor(); return try ladder(p, s, 256); } @@ -148,6 +150,7 @@ test "curve25519 small order check" { }, }; for (small_order_ss) |small_order_s| { + try std.testing.expectError(error.WeakPublicKey, Curve25519.fromBytes(small_order_s).clearCofactor()); try std.testing.expectError(error.WeakPublicKey, Curve25519.fromBytes(small_order_s).mul(s)); var extra = small_order_s; extra[31] ^= 0x80; From 5d6a5a123676ada3ccd229f31dc8855aabaf8057 Mon Sep 17 00:00:00 2001 From: viri Date: Thu, 7 Apr 2022 03:08:42 -0600 Subject: [PATCH 1074/2031] std.math.is*Inf: make generic, support f80 --- lib/std/math/isinf.zig | 139 ++++++++++------------------------------- 1 file changed, 34 insertions(+), 105 deletions(-) diff --git a/lib/std/math/isinf.zig b/lib/std/math/isinf.zig index cadaa8d937..2524354207 100644 --- a/lib/std/math/isinf.zig +++ b/lib/std/math/isinf.zig @@ -1,131 +1,60 @@ const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; -const maxInt = std.math.maxInt; /// Returns whether x is an infinity, ignoring sign. pub fn isInf(x: anytype) bool { const T = @TypeOf(x); - switch (T) { - f16 => { - const bits = @bitCast(u16, x); - return bits & 0x7FFF == 0x7C00; - }, - f32 => { - const bits = @bitCast(u32, x); - return bits & 0x7FFFFFFF == 0x7F800000; - }, - f64 => { - const bits = @bitCast(u64, x); - return bits & (maxInt(u64) >> 1) == (0x7FF << 52); - }, - f128 => { - const bits = @bitCast(u128, x); - return bits & (maxInt(u128) >> 1) == (0x7FFF << 112); - }, - else => { - @compileError("isInf not implemented for " ++ @typeName(T)); - }, + const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); + if (@typeInfo(T) != .Float) { + @compileError("isInf not implemented for " ++ @typeName(T)); } + const remove_sign = ~@as(TBits, 0) >> 1; + return @bitCast(TBits, x) & remove_sign == @bitCast(TBits, math.inf(T)); } /// Returns whether x is an infinity with a positive sign. pub fn isPositiveInf(x: anytype) bool { - const T = @TypeOf(x); - switch (T) { - f16 => { - return @bitCast(u16, x) == 0x7C00; - }, - f32 => { - return @bitCast(u32, x) == 0x7F800000; - }, - f64 => { - return @bitCast(u64, x) == 0x7FF << 52; - }, - f128 => { - return @bitCast(u128, x) == 0x7FFF << 112; - }, - else => { - @compileError("isPositiveInf not implemented for " ++ @typeName(T)); - }, - } + return x == math.inf(@TypeOf(x)); } /// Returns whether x is an infinity with a negative sign. pub fn isNegativeInf(x: anytype) bool { - const T = @TypeOf(x); - switch (T) { - f16 => { - return @bitCast(u16, x) == 0xFC00; - }, - f32 => { - return @bitCast(u32, x) == 0xFF800000; - }, - f64 => { - return @bitCast(u64, x) == 0xFFF << 52; - }, - f128 => { - return @bitCast(u128, x) == 0xFFFF << 112; - }, - else => { - @compileError("isNegativeInf not implemented for " ++ @typeName(T)); - }, - } + return x == -math.inf(@TypeOf(x)); } test "math.isInf" { - try expect(!isInf(@as(f16, 0.0))); - try expect(!isInf(@as(f16, -0.0))); - try expect(!isInf(@as(f32, 0.0))); - try expect(!isInf(@as(f32, -0.0))); - try expect(!isInf(@as(f64, 0.0))); - try expect(!isInf(@as(f64, -0.0))); - try expect(!isInf(@as(f128, 0.0))); - try expect(!isInf(@as(f128, -0.0))); - try expect(isInf(math.inf(f16))); - try expect(isInf(-math.inf(f16))); - try expect(isInf(math.inf(f32))); - try expect(isInf(-math.inf(f32))); - try expect(isInf(math.inf(f64))); - try expect(isInf(-math.inf(f64))); - try expect(isInf(math.inf(f128))); - try expect(isInf(-math.inf(f128))); + // TODO remove when #11391 is resolved + if (@import("builtin").os.tag == .freebsd) return error.SkipZigTest; + + inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { + try expect(!isInf(@as(T, 0.0))); + try expect(!isInf(@as(T, -0.0))); + try expect(isInf(math.inf(T))); + try expect(isInf(-math.inf(T))); + } } test "math.isPositiveInf" { - try expect(!isPositiveInf(@as(f16, 0.0))); - try expect(!isPositiveInf(@as(f16, -0.0))); - try expect(!isPositiveInf(@as(f32, 0.0))); - try expect(!isPositiveInf(@as(f32, -0.0))); - try expect(!isPositiveInf(@as(f64, 0.0))); - try expect(!isPositiveInf(@as(f64, -0.0))); - try expect(!isPositiveInf(@as(f128, 0.0))); - try expect(!isPositiveInf(@as(f128, -0.0))); - try expect(isPositiveInf(math.inf(f16))); - try expect(!isPositiveInf(-math.inf(f16))); - try expect(isPositiveInf(math.inf(f32))); - try expect(!isPositiveInf(-math.inf(f32))); - try expect(isPositiveInf(math.inf(f64))); - try expect(!isPositiveInf(-math.inf(f64))); - try expect(isPositiveInf(math.inf(f128))); - try expect(!isPositiveInf(-math.inf(f128))); + // TODO remove when #11391 is resolved + if (@import("builtin").os.tag == .freebsd) return error.SkipZigTest; + + inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { + try expect(!isPositiveInf(@as(T, 0.0))); + try expect(!isPositiveInf(@as(T, -0.0))); + try expect(isPositiveInf(math.inf(T))); + try expect(!isPositiveInf(-math.inf(T))); + } } test "math.isNegativeInf" { - try expect(!isNegativeInf(@as(f16, 0.0))); - try expect(!isNegativeInf(@as(f16, -0.0))); - try expect(!isNegativeInf(@as(f32, 0.0))); - try expect(!isNegativeInf(@as(f32, -0.0))); - try expect(!isNegativeInf(@as(f64, 0.0))); - try expect(!isNegativeInf(@as(f64, -0.0))); - try expect(!isNegativeInf(@as(f128, 0.0))); - try expect(!isNegativeInf(@as(f128, -0.0))); - try expect(!isNegativeInf(math.inf(f16))); - try expect(isNegativeInf(-math.inf(f16))); - try expect(!isNegativeInf(math.inf(f32))); - try expect(isNegativeInf(-math.inf(f32))); - try expect(!isNegativeInf(math.inf(f64))); - try expect(isNegativeInf(-math.inf(f64))); - try expect(!isNegativeInf(math.inf(f128))); - try expect(isNegativeInf(-math.inf(f128))); + // TODO remove when #11391 is resolved + if (@import("builtin").os.tag == .freebsd) return error.SkipZigTest; + + inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { + try expect(!isNegativeInf(@as(T, 0.0))); + try expect(!isNegativeInf(@as(T, -0.0))); + try expect(!isNegativeInf(math.inf(T))); + try expect(isNegativeInf(-math.inf(T))); + } } From e46c61250332e615afc5aed6e4ebe57b8e6713eb Mon Sep 17 00:00:00 2001 From: viri Date: Wed, 6 Apr 2022 20:49:05 -0600 Subject: [PATCH 1075/2031] use math/float.zig everywhere --- lib/std/fmt.zig | 32 ++++----- lib/std/fmt/parse_hex_float.zig | 48 ++++++------- lib/std/math.zig | 24 ++----- lib/std/math/__rem_pio2.zig | 2 +- lib/std/math/__rem_pio2f.zig | 2 +- lib/std/math/ceil.zig | 12 ++-- lib/std/math/complex/exp.zig | 4 +- lib/std/math/complex/sinh.zig | 4 +- lib/std/math/floor.zig | 12 ++-- lib/std/math/fma.zig | 4 +- lib/std/math/ldexp.zig | 30 ++++---- lib/std/math/round.zig | 18 +++-- lib/std/rand/ziggurat.zig | 2 +- lib/std/special/compiler_rt/divtf3_test.zig | 2 +- lib/std/special/compiler_rt/fixdfdi_test.zig | 8 +-- lib/std/special/compiler_rt/fixdfsi_test.zig | 8 +-- lib/std/special/compiler_rt/fixdfti_test.zig | 8 +-- lib/std/special/compiler_rt/fixint_test.zig | 72 +++++++++---------- lib/std/special/compiler_rt/fixsfdi_test.zig | 8 +-- lib/std/special/compiler_rt/fixsfsi_test.zig | 8 +-- lib/std/special/compiler_rt/fixsfti_test.zig | 8 +-- lib/std/special/compiler_rt/fixtfdi_test.zig | 8 +-- lib/std/special/compiler_rt/fixtfsi_test.zig | 8 +-- lib/std/special/compiler_rt/fixtfti_test.zig | 8 +-- .../special/compiler_rt/floatfmodl_test.zig | 8 +-- test/behavior/vector.zig | 2 +- 26 files changed, 177 insertions(+), 173 deletions(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 6d4f3a1daa..03395c024c 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -2228,8 +2228,8 @@ test "float.special" { if (builtin.target.cpu.arch != .arm) { try expectFmt("f64: -nan", "f64: {}", .{-math.nan_f64}); } - try expectFmt("f64: inf", "f64: {}", .{math.inf_f64}); - try expectFmt("f64: -inf", "f64: {}", .{-math.inf_f64}); + try expectFmt("f64: inf", "f64: {}", .{math.inf(f64)}); + try expectFmt("f64: -inf", "f64: {}", .{-math.inf(f64)}); } test "float.hexadecimal.special" { @@ -2239,8 +2239,8 @@ test "float.hexadecimal.special" { if (builtin.target.cpu.arch != .arm) { try expectFmt("f64: -nan", "f64: {x}", .{-math.nan_f64}); } - try expectFmt("f64: inf", "f64: {x}", .{math.inf_f64}); - try expectFmt("f64: -inf", "f64: {x}", .{-math.inf_f64}); + try expectFmt("f64: inf", "f64: {x}", .{math.inf(f64)}); + try expectFmt("f64: -inf", "f64: {x}", .{-math.inf(f64)}); try expectFmt("f64: 0x0.0p0", "f64: {x}", .{@as(f64, 0)}); try expectFmt("f64: -0x0.0p0", "f64: {x}", .{-@as(f64, 0)}); @@ -2252,20 +2252,20 @@ test "float.hexadecimal" { try expectFmt("f64: 0x1.5555555555555p-2", "f64: {x}", .{@as(f64, 1.0 / 3.0)}); try expectFmt("f128: 0x1.5555555555555555555555555555p-2", "f128: {x}", .{@as(f128, 1.0 / 3.0)}); - try expectFmt("f16: 0x1p-14", "f16: {x}", .{@as(f16, math.f16_min)}); - try expectFmt("f32: 0x1p-126", "f32: {x}", .{@as(f32, math.f32_min)}); - try expectFmt("f64: 0x1p-1022", "f64: {x}", .{@as(f64, math.f64_min)}); - try expectFmt("f128: 0x1p-16382", "f128: {x}", .{@as(f128, math.f128_min)}); + try expectFmt("f16: 0x1p-14", "f16: {x}", .{math.floatMin(f16)}); + try expectFmt("f32: 0x1p-126", "f32: {x}", .{math.floatMin(f32)}); + try expectFmt("f64: 0x1p-1022", "f64: {x}", .{math.floatMin(f64)}); + try expectFmt("f128: 0x1p-16382", "f128: {x}", .{math.floatMin(f128)}); - try expectFmt("f16: 0x0.004p-14", "f16: {x}", .{@as(f16, math.f16_true_min)}); - try expectFmt("f32: 0x0.000002p-126", "f32: {x}", .{@as(f32, math.f32_true_min)}); - try expectFmt("f64: 0x0.0000000000001p-1022", "f64: {x}", .{@as(f64, math.f64_true_min)}); - try expectFmt("f128: 0x0.0000000000000000000000000001p-16382", "f128: {x}", .{@as(f128, math.f128_true_min)}); + try expectFmt("f16: 0x0.004p-14", "f16: {x}", .{math.floatTrueMin(f16)}); + try expectFmt("f32: 0x0.000002p-126", "f32: {x}", .{math.floatTrueMin(f32)}); + try expectFmt("f64: 0x0.0000000000001p-1022", "f64: {x}", .{math.floatTrueMin(f64)}); + try expectFmt("f128: 0x0.0000000000000000000000000001p-16382", "f128: {x}", .{math.floatTrueMin(f128)}); - try expectFmt("f16: 0x1.ffcp15", "f16: {x}", .{@as(f16, math.f16_max)}); - try expectFmt("f32: 0x1.fffffep127", "f32: {x}", .{@as(f32, math.f32_max)}); - try expectFmt("f64: 0x1.fffffffffffffp1023", "f64: {x}", .{@as(f64, math.f64_max)}); - try expectFmt("f128: 0x1.ffffffffffffffffffffffffffffp16383", "f128: {x}", .{@as(f128, math.f128_max)}); + try expectFmt("f16: 0x1.ffcp15", "f16: {x}", .{math.floatMax(f16)}); + try expectFmt("f32: 0x1.fffffep127", "f32: {x}", .{math.floatMax(f32)}); + try expectFmt("f64: 0x1.fffffffffffffp1023", "f64: {x}", .{math.floatMax(f64)}); + try expectFmt("f128: 0x1.ffffffffffffffffffffffffffffp16383", "f128: {x}", .{math.floatMax(f128)}); } test "float.hexadecimal.precision" { diff --git a/lib/std/fmt/parse_hex_float.zig b/lib/std/fmt/parse_hex_float.zig index 3885e7e1a8..3e8bc5c5d9 100644 --- a/lib/std/fmt/parse_hex_float.zig +++ b/lib/std/fmt/parse_hex_float.zig @@ -262,14 +262,14 @@ test "f16" { .{ .s = "0x10p+10", .v = 16384.0 }, .{ .s = "0x10p-10", .v = 0.015625 }, // Max normalized value. - .{ .s = "0x1.ffcp+15", .v = math.f16_max }, - .{ .s = "-0x1.ffcp+15", .v = -math.f16_max }, + .{ .s = "0x1.ffcp+15", .v = math.floatMax(f16) }, + .{ .s = "-0x1.ffcp+15", .v = -math.floatMax(f16) }, // Min normalized value. - .{ .s = "0x1p-14", .v = math.f16_min }, - .{ .s = "-0x1p-14", .v = -math.f16_min }, + .{ .s = "0x1p-14", .v = math.floatMin(f16) }, + .{ .s = "-0x1p-14", .v = -math.floatMin(f16) }, // Min denormal value. - .{ .s = "0x1p-24", .v = math.f16_true_min }, - .{ .s = "-0x1p-24", .v = -math.f16_true_min }, + .{ .s = "0x1p-24", .v = math.floatTrueMin(f16) }, + .{ .s = "-0x1p-24", .v = -math.floatTrueMin(f16) }, }; for (cases) |case| { @@ -286,14 +286,14 @@ test "f32" { .{ .s = "0x0.ffffffp128", .v = 0x0.ffffffp128 }, .{ .s = "0x0.1234570p-125", .v = 0x0.1234570p-125 }, // Max normalized value. - .{ .s = "0x1.fffffeP+127", .v = math.f32_max }, - .{ .s = "-0x1.fffffeP+127", .v = -math.f32_max }, + .{ .s = "0x1.fffffeP+127", .v = math.floatMax(f32) }, + .{ .s = "-0x1.fffffeP+127", .v = -math.floatMax(f32) }, // Min normalized value. - .{ .s = "0x1p-126", .v = math.f32_min }, - .{ .s = "-0x1p-126", .v = -math.f32_min }, + .{ .s = "0x1p-126", .v = math.floatMin(f32) }, + .{ .s = "-0x1p-126", .v = -math.floatMin(f32) }, // Min denormal value. - .{ .s = "0x1P-149", .v = math.f32_true_min }, - .{ .s = "-0x1P-149", .v = -math.f32_true_min }, + .{ .s = "0x1P-149", .v = math.floatTrueMin(f32) }, + .{ .s = "-0x1P-149", .v = -math.floatTrueMin(f32) }, }; for (cases) |case| { @@ -308,14 +308,14 @@ test "f64" { .{ .s = "0x10p+10", .v = 16384.0 }, .{ .s = "0x10p-10", .v = 0.015625 }, // Max normalized value. - .{ .s = "0x1.fffffffffffffp+1023", .v = math.f64_max }, - .{ .s = "-0x1.fffffffffffffp1023", .v = -math.f64_max }, + .{ .s = "0x1.fffffffffffffp+1023", .v = math.floatMax(f64) }, + .{ .s = "-0x1.fffffffffffffp1023", .v = -math.floatMax(f64) }, // Min normalized value. - .{ .s = "0x1p-1022", .v = math.f64_min }, - .{ .s = "-0x1p-1022", .v = -math.f64_min }, + .{ .s = "0x1p-1022", .v = math.floatMin(f64) }, + .{ .s = "-0x1p-1022", .v = -math.floatMin(f64) }, // Min denormalized value. - .{ .s = "0x1p-1074", .v = math.f64_true_min }, - .{ .s = "-0x1p-1074", .v = -math.f64_true_min }, + .{ .s = "0x1p-1074", .v = math.floatTrueMin(f64) }, + .{ .s = "-0x1p-1074", .v = -math.floatTrueMin(f64) }, }; for (cases) |case| { @@ -330,14 +330,14 @@ test "f128" { .{ .s = "0x10p+10", .v = 16384.0 }, .{ .s = "0x10p-10", .v = 0.015625 }, // Max normalized value. - .{ .s = "0xf.fffffffffffffffffffffffffff8p+16380", .v = math.f128_max }, - .{ .s = "-0xf.fffffffffffffffffffffffffff8p+16380", .v = -math.f128_max }, + .{ .s = "0xf.fffffffffffffffffffffffffff8p+16380", .v = math.floatMax(f128) }, + .{ .s = "-0xf.fffffffffffffffffffffffffff8p+16380", .v = -math.floatMax(f128) }, // Min normalized value. - .{ .s = "0x1p-16382", .v = math.f128_min }, - .{ .s = "-0x1p-16382", .v = -math.f128_min }, + .{ .s = "0x1p-16382", .v = math.floatMin(f128) }, + .{ .s = "-0x1p-16382", .v = -math.floatMin(f128) }, // // Min denormalized value. - .{ .s = "0x1p-16494", .v = math.f128_true_min }, - .{ .s = "-0x1p-16494", .v = -math.f128_true_min }, + .{ .s = "0x1p-16494", .v = math.floatTrueMin(f128) }, + .{ .s = "-0x1p-16494", .v = -math.floatTrueMin(f128) }, .{ .s = "0x1.edcb34a235253948765432134674fp-1", .v = 0x1.edcb34a235253948765432134674fp-1 }, }; diff --git a/lib/std/math.zig b/lib/std/math.zig index 1ef7ac9798..94e3ab6d2a 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -121,7 +121,7 @@ pub const snan = @import("math/nan.zig").snan; /// /// The `tolerance` parameter is the absolute tolerance used when determining if /// the two numbers are close enough; a good value for this parameter is a small -/// multiple of `epsilon(T)`. +/// multiple of `floatEps(T)`. /// /// Note that this function is recommended for comparing small numbers /// around zero; using `approxEqRel` is suggested otherwise. @@ -148,7 +148,7 @@ pub fn approxEqAbs(comptime T: type, x: T, y: T, tolerance: T) bool { /// /// The `tolerance` parameter is the relative tolerance used when determining if /// the two numbers are close enough; a good value for this parameter is usually -/// `sqrt(epsilon(T))`, meaning that the two numbers are considered equal if at +/// `sqrt(floatEps(T))`, meaning that the two numbers are considered equal if at /// least half of the digits are equal. /// /// Note that for comparisons of small numbers around zero this function won't @@ -179,25 +179,19 @@ pub fn approxEq(comptime T: type, x: T, y: T, tolerance: T) bool { test "approxEqAbs and approxEqRel" { inline for ([_]type{ f16, f32, f64, f128 }) |T| { - const eps_value = comptime epsilon(T); + const eps_value = comptime floatEps(T); const sqrt_eps_value = comptime sqrt(eps_value); const nan_value = comptime nan(T); const inf_value = comptime inf(T); - const min_value: T = switch (T) { - f16 => f16_min, - f32 => f32_min, - f64 => f64_min, - f128 => f128_min, - else => unreachable, - }; + const min_value = comptime floatMin(T); try testing.expect(approxEqAbs(T, 0.0, 0.0, eps_value)); try testing.expect(approxEqAbs(T, -0.0, -0.0, eps_value)); try testing.expect(approxEqAbs(T, 0.0, -0.0, eps_value)); try testing.expect(approxEqRel(T, 1.0, 1.0, sqrt_eps_value)); try testing.expect(!approxEqRel(T, 1.0, 0.0, sqrt_eps_value)); - try testing.expect(!approxEqAbs(T, 1.0 + 2 * epsilon(T), 1.0, eps_value)); - try testing.expect(approxEqAbs(T, 1.0 + 1 * epsilon(T), 1.0, eps_value)); + try testing.expect(!approxEqAbs(T, 1.0 + 2 * eps_value, 1.0, eps_value)); + try testing.expect(approxEqAbs(T, 1.0 + 1 * eps_value, 1.0, eps_value)); try testing.expect(!approxEqRel(T, 1.0, nan_value, sqrt_eps_value)); try testing.expect(!approxEqRel(T, nan_value, nan_value, sqrt_eps_value)); try testing.expect(approxEqRel(T, inf_value, inf_value, sqrt_eps_value)); @@ -1193,12 +1187,6 @@ test "lossyCast" { try testing.expect(lossyCast(u32, @as(f32, maxInt(u32))) == maxInt(u32)); } -test "f64_min" { - const f64_min_u64 = 0x0010000000000000; - const fmin: f64 = f64_min; - try testing.expect(@bitCast(u64, fmin) == f64_min_u64); -} - /// Returns the maximum value of integer type T. pub fn maxInt(comptime T: type) comptime_int { const info = @typeInfo(T); diff --git a/lib/std/math/__rem_pio2.zig b/lib/std/math/__rem_pio2.zig index c8cb8fb644..f01d8fe94a 100644 --- a/lib/std/math/__rem_pio2.zig +++ b/lib/std/math/__rem_pio2.zig @@ -7,7 +7,7 @@ const std = @import("../std.zig"); const __rem_pio2_large = @import("__rem_pio2_large.zig").__rem_pio2_large; const math = std.math; -const toint = 1.5 / math.epsilon(f64); +const toint = 1.5 / math.floatEps(f64); // pi/4 const pio4 = 0x1.921fb54442d18p-1; // invpio2: 53 bits of 2/pi diff --git a/lib/std/math/__rem_pio2f.zig b/lib/std/math/__rem_pio2f.zig index 9f78e18d36..5867fb30d9 100644 --- a/lib/std/math/__rem_pio2f.zig +++ b/lib/std/math/__rem_pio2f.zig @@ -7,7 +7,7 @@ const std = @import("../std.zig"); const __rem_pio2_large = @import("__rem_pio2_large.zig").__rem_pio2_large; const math = std.math; -const toint = 1.5 / math.epsilon(f64); +const toint = 1.5 / math.floatEps(f64); // pi/4 const pio4 = 0x1.921fb6p-1; // invpio2: 53 bits of 2/pi diff --git a/lib/std/math/ceil.zig b/lib/std/math/ceil.zig index cf3adcf5b5..686be8e58d 100644 --- a/lib/std/math/ceil.zig +++ b/lib/std/math/ceil.zig @@ -62,6 +62,8 @@ fn ceil32(x: f32) f32 { } fn ceil64(x: f64) f64 { + const f64_toint = 1.0 / math.floatEps(f64); + const u = @bitCast(u64, x); const e = (u >> 52) & 0x7FF; var y: f64 = undefined; @@ -71,9 +73,9 @@ fn ceil64(x: f64) f64 { } if (u >> 63 != 0) { - y = x - math.f64_toint + math.f64_toint - x; + y = x - f64_toint + f64_toint - x; } else { - y = x + math.f64_toint - math.f64_toint - x; + y = x + f64_toint - f64_toint - x; } if (e <= 0x3FF - 1) { @@ -91,6 +93,8 @@ fn ceil64(x: f64) f64 { } fn ceil128(x: f128) f128 { + const f128_toint = 1.0 / math.floatEps(f128); + const u = @bitCast(u128, x); const e = (u >> 112) & 0x7FFF; var y: f128 = undefined; @@ -98,9 +102,9 @@ fn ceil128(x: f128) f128 { if (e >= 0x3FFF + 112 or x == 0) return x; if (u >> 127 != 0) { - y = x - math.f128_toint + math.f128_toint - x; + y = x - f128_toint + f128_toint - x; } else { - y = x + math.f128_toint - math.f128_toint - x; + y = x + f128_toint - f128_toint - x; } if (e <= 0x3FFF - 1) { diff --git a/lib/std/math/complex/exp.zig b/lib/std/math/complex/exp.zig index f2ae28d3fd..ce25025ded 100644 --- a/lib/std/math/complex/exp.zig +++ b/lib/std/math/complex/exp.zig @@ -120,7 +120,7 @@ fn exp64(z: Complex(f64)) Complex(f64) { } test "complex.cexp32" { - const tolerance_f32 = math.sqrt(math.epsilon(f32)); + const tolerance_f32 = math.sqrt(math.floatEps(f32)); { const a = Complex(f32).init(5, 3); @@ -140,7 +140,7 @@ test "complex.cexp32" { } test "complex.cexp64" { - const tolerance_f64 = math.sqrt(math.epsilon(f64)); + const tolerance_f64 = math.sqrt(math.floatEps(f64)); { const a = Complex(f64).init(5, 3); diff --git a/lib/std/math/complex/sinh.zig b/lib/std/math/complex/sinh.zig index ed344999ee..851af3e62e 100644 --- a/lib/std/math/complex/sinh.zig +++ b/lib/std/math/complex/sinh.zig @@ -79,7 +79,7 @@ fn sinh32(z: Complex(f32)) Complex(f32) { if (iy >= 0x7f800000) { return Complex(f32).init(x * x, x * (y - y)); } - return Complex(f32).init(x * math.cos(y), math.inf_f32 * math.sin(y)); + return Complex(f32).init(x * math.cos(y), math.inf(f32) * math.sin(y)); } return Complex(f32).init((x * x) * (y - y), (x + x) * (y - y)); @@ -146,7 +146,7 @@ fn sinh64(z: Complex(f64)) Complex(f64) { if (iy >= 0x7ff00000) { return Complex(f64).init(x * x, x * (y - y)); } - return Complex(f64).init(x * math.cos(y), math.inf_f64 * math.sin(y)); + return Complex(f64).init(x * math.cos(y), math.inf(f64) * math.sin(y)); } return Complex(f64).init((x * x) * (y - y), (x + x) * (y - y)); diff --git a/lib/std/math/floor.zig b/lib/std/math/floor.zig index d6761ba77e..ab5ca3583b 100644 --- a/lib/std/math/floor.zig +++ b/lib/std/math/floor.zig @@ -98,6 +98,8 @@ fn floor32(x: f32) f32 { } fn floor64(x: f64) f64 { + const f64_toint = 1.0 / math.floatEps(f64); + const u = @bitCast(u64, x); const e = (u >> 52) & 0x7FF; var y: f64 = undefined; @@ -107,9 +109,9 @@ fn floor64(x: f64) f64 { } if (u >> 63 != 0) { - y = x - math.f64_toint + math.f64_toint - x; + y = x - f64_toint + f64_toint - x; } else { - y = x + math.f64_toint - math.f64_toint - x; + y = x + f64_toint - f64_toint - x; } if (e <= 0x3FF - 1) { @@ -127,6 +129,8 @@ fn floor64(x: f64) f64 { } fn floor128(x: f128) f128 { + const f128_toint = 1.0 / math.floatEps(f128); + const u = @bitCast(u128, x); const e = (u >> 112) & 0x7FFF; var y: f128 = undefined; @@ -134,9 +138,9 @@ fn floor128(x: f128) f128 { if (e >= 0x3FFF + 112 or x == 0) return x; if (u >> 127 != 0) { - y = x - math.f128_toint + math.f128_toint - x; + y = x - f128_toint + f128_toint - x; } else { - y = x + math.f128_toint - math.f128_toint - x; + y = x + f128_toint - f128_toint - x; } if (e <= 0x3FFF - 1) { diff --git a/lib/std/math/fma.zig b/lib/std/math/fma.zig index 7ef734cf4e..7afc6e557e 100644 --- a/lib/std/math/fma.zig +++ b/lib/std/math/fma.zig @@ -68,7 +68,7 @@ fn fma64(x: f64, y: f64, z: f64) f64 { if (spread <= 53 * 2) { zs = math.scalbn(zs, -spread); } else { - zs = math.copysign(f64, math.f64_min, zs); + zs = math.copysign(f64, math.floatMin(f64), zs); } const xy = dd_mul(xs, ys); @@ -277,7 +277,7 @@ fn fma128(x: f128, y: f128, z: f128) f128 { if (spread <= 113 * 2) { zs = math.scalbn(zs, -spread); } else { - zs = math.copysign(f128, math.f128_min, zs); + zs = math.copysign(f128, math.floatMin(f128), zs); } const xy = dd_mul128(xs, ys); diff --git a/lib/std/math/ldexp.zig b/lib/std/math/ldexp.zig index f8ab237fad..0934244c65 100644 --- a/lib/std/math/ldexp.zig +++ b/lib/std/math/ldexp.zig @@ -57,6 +57,8 @@ pub fn ldexp(x: anytype, n: i32) @TypeOf(x) { } test "math.ldexp" { + // TODO derive the various constants here with new maths API + // basic usage try expect(ldexp(@as(f16, 1.5), 4) == 24.0); try expect(ldexp(@as(f32, 1.5), 4) == 24.0); @@ -73,20 +75,20 @@ test "math.ldexp" { try expect(math.isNormal(ldexp(@as(f128, 1.0), -16382))); try expect(!math.isNormal(ldexp(@as(f128, 1.0), -16383))); // unreliable due to lack of native f16 support, see talk on PR #8733 - // try expect(ldexp(@as(f16, 0x1.1FFp-1), -14 - 9) == math.f16_true_min); - try expect(ldexp(@as(f32, 0x1.3FFFFFp-1), -126 - 22) == math.f32_true_min); - try expect(ldexp(@as(f64, 0x1.7FFFFFFFFFFFFp-1), -1022 - 51) == math.f64_true_min); - try expect(ldexp(@as(f128, 0x1.7FFFFFFFFFFFFFFFFFFFFFFFFFFFp-1), -16382 - 111) == math.f128_true_min); + // try expect(ldexp(@as(f16, 0x1.1FFp-1), -14 - 9) == math.floatTrueMin(f16)); + try expect(ldexp(@as(f32, 0x1.3FFFFFp-1), -126 - 22) == math.floatTrueMin(f32)); + try expect(ldexp(@as(f64, 0x1.7FFFFFFFFFFFFp-1), -1022 - 51) == math.floatTrueMin(f64)); + try expect(ldexp(@as(f128, 0x1.7FFFFFFFFFFFFFFFFFFFFFFFFFFFp-1), -16382 - 111) == math.floatTrueMin(f128)); // float limits - try expect(ldexp(@as(f32, math.f32_max), -128 - 149) > 0.0); - try expect(ldexp(@as(f32, math.f32_max), -128 - 149 - 1) == 0.0); - try expect(!math.isPositiveInf(ldexp(@as(f16, math.f16_true_min), 15 + 24))); - try expect(math.isPositiveInf(ldexp(@as(f16, math.f16_true_min), 15 + 24 + 1))); - try expect(!math.isPositiveInf(ldexp(@as(f32, math.f32_true_min), 127 + 149))); - try expect(math.isPositiveInf(ldexp(@as(f32, math.f32_true_min), 127 + 149 + 1))); - try expect(!math.isPositiveInf(ldexp(@as(f64, math.f64_true_min), 1023 + 1074))); - try expect(math.isPositiveInf(ldexp(@as(f64, math.f64_true_min), 1023 + 1074 + 1))); - try expect(!math.isPositiveInf(ldexp(@as(f128, math.f128_true_min), 16383 + 16494))); - try expect(math.isPositiveInf(ldexp(@as(f128, math.f128_true_min), 16383 + 16494 + 1))); + try expect(ldexp(math.floatMax(f32), -128 - 149) > 0.0); + try expect(ldexp(math.floatMax(f32), -128 - 149 - 1) == 0.0); + try expect(!math.isPositiveInf(ldexp(math.floatTrueMin(f16), 15 + 24))); + try expect(math.isPositiveInf(ldexp(math.floatTrueMin(f16), 15 + 24 + 1))); + try expect(!math.isPositiveInf(ldexp(math.floatTrueMin(f32), 127 + 149))); + try expect(math.isPositiveInf(ldexp(math.floatTrueMin(f32), 127 + 149 + 1))); + try expect(!math.isPositiveInf(ldexp(math.floatTrueMin(f64), 1023 + 1074))); + try expect(math.isPositiveInf(ldexp(math.floatTrueMin(f64), 1023 + 1074 + 1))); + try expect(!math.isPositiveInf(ldexp(math.floatTrueMin(f128), 16383 + 16494))); + try expect(math.isPositiveInf(ldexp(math.floatTrueMin(f128), 16383 + 16494 + 1))); } diff --git a/lib/std/math/round.zig b/lib/std/math/round.zig index c948431a35..be33a9cfbd 100644 --- a/lib/std/math/round.zig +++ b/lib/std/math/round.zig @@ -29,6 +29,8 @@ pub fn round(x: anytype) @TypeOf(x) { } fn round32(x_: f32) f32 { + const f32_toint = 1.0 / math.floatEps(f32); + var x = x_; const u = @bitCast(u32, x); const e = (u >> 23) & 0xFF; @@ -41,11 +43,11 @@ fn round32(x_: f32) f32 { x = -x; } if (e < 0x7F - 1) { - math.doNotOptimizeAway(x + math.f32_toint); + math.doNotOptimizeAway(x + f32_toint); return 0 * @bitCast(f32, u); } - y = x + math.f32_toint - math.f32_toint - x; + y = x + f32_toint - f32_toint - x; if (y > 0.5) { y = y + x - 1; } else if (y <= -0.5) { @@ -62,6 +64,8 @@ fn round32(x_: f32) f32 { } fn round64(x_: f64) f64 { + const f64_toint = 1.0 / math.floatEps(f64); + var x = x_; const u = @bitCast(u64, x); const e = (u >> 52) & 0x7FF; @@ -74,11 +78,11 @@ fn round64(x_: f64) f64 { x = -x; } if (e < 0x3ff - 1) { - math.doNotOptimizeAway(x + math.f64_toint); + math.doNotOptimizeAway(x + f64_toint); return 0 * @bitCast(f64, u); } - y = x + math.f64_toint - math.f64_toint - x; + y = x + f64_toint - f64_toint - x; if (y > 0.5) { y = y + x - 1; } else if (y <= -0.5) { @@ -95,6 +99,8 @@ fn round64(x_: f64) f64 { } fn round128(x_: f128) f128 { + const f128_toint = 1.0 / math.floatEps(f128); + var x = x_; const u = @bitCast(u128, x); const e = (u >> 112) & 0x7FFF; @@ -107,11 +113,11 @@ fn round128(x_: f128) f128 { x = -x; } if (e < 0x3FFF - 1) { - math.doNotOptimizeAway(x + math.f64_toint); + math.doNotOptimizeAway(x + f128_toint); return 0 * @bitCast(f128, u); } - y = x + math.f128_toint - math.f128_toint - x; + y = x + f128_toint - f128_toint - x; if (y > 0.5) { y = y + x - 1; } else if (y <= -0.5) { diff --git a/lib/std/rand/ziggurat.zig b/lib/std/rand/ziggurat.zig index 59b7e53395..5c18d0023b 100644 --- a/lib/std/rand/ziggurat.zig +++ b/lib/std/rand/ziggurat.zig @@ -28,7 +28,7 @@ pub fn next_f64(random: Random, comptime tables: ZigTable) f64 { } else { // Generate a value in the range [1, 2) and scale into (0, 1) const repr = (0x3ff << 52) | (bits >> 12); - break :blk @bitCast(f64, repr) - (1.0 - math.f64_epsilon / 2.0); + break :blk @bitCast(f64, repr) - (1.0 - math.floatEps(f64) / 2.0); } }; diff --git a/lib/std/special/compiler_rt/divtf3_test.zig b/lib/std/special/compiler_rt/divtf3_test.zig index f426f827e8..925f8bbc91 100644 --- a/lib/std/special/compiler_rt/divtf3_test.zig +++ b/lib/std/special/compiler_rt/divtf3_test.zig @@ -35,7 +35,7 @@ test "divtf3" { // NaN / any = NaN try test__divtf3(math.nan_f128, 0x1.23456789abcdefp+5, 0x7fff800000000000, 0); // inf / any = inf - try test__divtf3(math.inf_f128, 0x1.23456789abcdefp+5, 0x7fff000000000000, 0); + try test__divtf3(math.inf(f128), 0x1.23456789abcdefp+5, 0x7fff000000000000, 0); try test__divtf3(0x1.a23b45362464523375893ab4cdefp+5, 0x1.eedcbaba3a94546558237654321fp-1, 0x4004b0b72924d407, 0x0717e84356c6eba2); try test__divtf3(0x1.a2b34c56d745382f9abf2c3dfeffp-50, 0x1.ed2c3ba15935332532287654321fp-9, 0x3fd5b2af3f828c9b, 0x40e51f64cde8b1f2); diff --git a/lib/std/special/compiler_rt/fixdfdi_test.zig b/lib/std/special/compiler_rt/fixdfdi_test.zig index ac2fdbe7ef..e80a875800 100644 --- a/lib/std/special/compiler_rt/fixdfdi_test.zig +++ b/lib/std/special/compiler_rt/fixdfdi_test.zig @@ -9,7 +9,7 @@ fn test__fixdfdi(a: f64, expected: i64) !void { } test "fixdfdi" { - try test__fixdfdi(-math.f64_max, math.minInt(i64)); + try test__fixdfdi(-math.floatMax(f64), math.minInt(i64)); try test__fixdfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); try test__fixdfdi(-0x1.FFFFFFFFFFFFFp+1023, -0x8000000000000000); @@ -32,9 +32,9 @@ test "fixdfdi" { try test__fixdfdi(-1.0, -1); try test__fixdfdi(-0.99, 0); try test__fixdfdi(-0.5, 0); - try test__fixdfdi(-math.f64_min, 0); + try test__fixdfdi(-math.floatMin(f64), 0); try test__fixdfdi(0.0, 0); - try test__fixdfdi(math.f64_min, 0); + try test__fixdfdi(math.floatMin(f64), 0); try test__fixdfdi(0.5, 0); try test__fixdfdi(0.99, 0); try test__fixdfdi(1.0, 1); @@ -58,5 +58,5 @@ test "fixdfdi" { try test__fixdfdi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFF); try test__fixdfdi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i64)); - try test__fixdfdi(math.f64_max, math.maxInt(i64)); + try test__fixdfdi(math.floatMax(f64), math.maxInt(i64)); } diff --git a/lib/std/special/compiler_rt/fixdfsi_test.zig b/lib/std/special/compiler_rt/fixdfsi_test.zig index 39d4f64369..a1e76a6ee4 100644 --- a/lib/std/special/compiler_rt/fixdfsi_test.zig +++ b/lib/std/special/compiler_rt/fixdfsi_test.zig @@ -9,7 +9,7 @@ fn test__fixdfsi(a: f64, expected: i32) !void { } test "fixdfsi" { - try test__fixdfsi(-math.f64_max, math.minInt(i32)); + try test__fixdfsi(-math.floatMax(f64), math.minInt(i32)); try test__fixdfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); try test__fixdfsi(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000); @@ -36,9 +36,9 @@ test "fixdfsi" { try test__fixdfsi(-1.0, -1); try test__fixdfsi(-0.99, 0); try test__fixdfsi(-0.5, 0); - try test__fixdfsi(-math.f64_min, 0); + try test__fixdfsi(-math.floatMin(f64), 0); try test__fixdfsi(0.0, 0); - try test__fixdfsi(math.f64_min, 0); + try test__fixdfsi(math.floatMin(f64), 0); try test__fixdfsi(0.5, 0); try test__fixdfsi(0.99, 0); try test__fixdfsi(1.0, 1); @@ -66,5 +66,5 @@ test "fixdfsi" { try test__fixdfsi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFF); try test__fixdfsi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i32)); - try test__fixdfsi(math.f64_max, math.maxInt(i32)); + try test__fixdfsi(math.floatMax(f64), math.maxInt(i32)); } diff --git a/lib/std/special/compiler_rt/fixdfti_test.zig b/lib/std/special/compiler_rt/fixdfti_test.zig index eb8269b0ea..cc6eec6b23 100644 --- a/lib/std/special/compiler_rt/fixdfti_test.zig +++ b/lib/std/special/compiler_rt/fixdfti_test.zig @@ -9,7 +9,7 @@ fn test__fixdfti(a: f64, expected: i128) !void { } test "fixdfti" { - try test__fixdfti(-math.f64_max, math.minInt(i128)); + try test__fixdfti(-math.floatMax(f64), math.minInt(i128)); try test__fixdfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); try test__fixdfti(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000000000000000000000000000); @@ -32,9 +32,9 @@ test "fixdfti" { try test__fixdfti(-1.0, -1); try test__fixdfti(-0.99, 0); try test__fixdfti(-0.5, 0); - try test__fixdfti(-math.f64_min, 0); + try test__fixdfti(-math.floatMin(f64), 0); try test__fixdfti(0.0, 0); - try test__fixdfti(math.f64_min, 0); + try test__fixdfti(math.floatMin(f64), 0); try test__fixdfti(0.5, 0); try test__fixdfti(0.99, 0); try test__fixdfti(1.0, 1); @@ -58,5 +58,5 @@ test "fixdfti" { try test__fixdfti(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); try test__fixdfti(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i128)); - try test__fixdfti(math.f64_max, math.maxInt(i128)); + try test__fixdfti(math.floatMax(f64), math.maxInt(i128)); } diff --git a/lib/std/special/compiler_rt/fixint_test.zig b/lib/std/special/compiler_rt/fixint_test.zig index 9c31444ac5..57b4093809 100644 --- a/lib/std/special/compiler_rt/fixint_test.zig +++ b/lib/std/special/compiler_rt/fixint_test.zig @@ -11,49 +11,49 @@ fn test__fixint(comptime fp_t: type, comptime fixint_t: type, a: fp_t, expected: } test "fixint.i1" { - try test__fixint(f32, i1, -math.inf_f32, -1); - try test__fixint(f32, i1, -math.f32_max, -1); + try test__fixint(f32, i1, -math.inf(f32), -1); + try test__fixint(f32, i1, -math.floatMax(f32), -1); try test__fixint(f32, i1, -2.0, -1); try test__fixint(f32, i1, -1.1, -1); try test__fixint(f32, i1, -1.0, -1); try test__fixint(f32, i1, -0.9, 0); try test__fixint(f32, i1, -0.1, 0); - try test__fixint(f32, i1, -math.f32_min, 0); + try test__fixint(f32, i1, -math.floatMin(f32), 0); try test__fixint(f32, i1, -0.0, 0); try test__fixint(f32, i1, 0.0, 0); - try test__fixint(f32, i1, math.f32_min, 0); + try test__fixint(f32, i1, math.floatMin(f32), 0); try test__fixint(f32, i1, 0.1, 0); try test__fixint(f32, i1, 0.9, 0); try test__fixint(f32, i1, 1.0, 0); try test__fixint(f32, i1, 2.0, 0); - try test__fixint(f32, i1, math.f32_max, 0); - try test__fixint(f32, i1, math.inf_f32, 0); + try test__fixint(f32, i1, math.floatMax(f32), 0); + try test__fixint(f32, i1, math.inf(f32), 0); } test "fixint.i2" { - try test__fixint(f32, i2, -math.inf_f32, -2); - try test__fixint(f32, i2, -math.f32_max, -2); + try test__fixint(f32, i2, -math.inf(f32), -2); + try test__fixint(f32, i2, -math.floatMax(f32), -2); try test__fixint(f32, i2, -2.0, -2); try test__fixint(f32, i2, -1.9, -1); try test__fixint(f32, i2, -1.1, -1); try test__fixint(f32, i2, -1.0, -1); try test__fixint(f32, i2, -0.9, 0); try test__fixint(f32, i2, -0.1, 0); - try test__fixint(f32, i2, -math.f32_min, 0); + try test__fixint(f32, i2, -math.floatMin(f32), 0); try test__fixint(f32, i2, -0.0, 0); try test__fixint(f32, i2, 0.0, 0); - try test__fixint(f32, i2, math.f32_min, 0); + try test__fixint(f32, i2, math.floatMin(f32), 0); try test__fixint(f32, i2, 0.1, 0); try test__fixint(f32, i2, 0.9, 0); try test__fixint(f32, i2, 1.0, 1); try test__fixint(f32, i2, 2.0, 1); - try test__fixint(f32, i2, math.f32_max, 1); - try test__fixint(f32, i2, math.inf_f32, 1); + try test__fixint(f32, i2, math.floatMax(f32), 1); + try test__fixint(f32, i2, math.inf(f32), 1); } test "fixint.i3" { - try test__fixint(f32, i3, -math.inf_f32, -4); - try test__fixint(f32, i3, -math.f32_max, -4); + try test__fixint(f32, i3, -math.inf(f32), -4); + try test__fixint(f32, i3, -math.floatMax(f32), -4); try test__fixint(f32, i3, -4.0, -4); try test__fixint(f32, i3, -3.0, -3); try test__fixint(f32, i3, -2.0, -2); @@ -62,23 +62,23 @@ test "fixint.i3" { try test__fixint(f32, i3, -1.0, -1); try test__fixint(f32, i3, -0.9, 0); try test__fixint(f32, i3, -0.1, 0); - try test__fixint(f32, i3, -math.f32_min, 0); + try test__fixint(f32, i3, -math.floatMin(f32), 0); try test__fixint(f32, i3, -0.0, 0); try test__fixint(f32, i3, 0.0, 0); - try test__fixint(f32, i3, math.f32_min, 0); + try test__fixint(f32, i3, math.floatMin(f32), 0); try test__fixint(f32, i3, 0.1, 0); try test__fixint(f32, i3, 0.9, 0); try test__fixint(f32, i3, 1.0, 1); try test__fixint(f32, i3, 2.0, 2); try test__fixint(f32, i3, 3.0, 3); try test__fixint(f32, i3, 4.0, 3); - try test__fixint(f32, i3, math.f32_max, 3); - try test__fixint(f32, i3, math.inf_f32, 3); + try test__fixint(f32, i3, math.floatMax(f32), 3); + try test__fixint(f32, i3, math.inf(f32), 3); } test "fixint.i32" { - try test__fixint(f64, i32, -math.inf_f64, math.minInt(i32)); - try test__fixint(f64, i32, -math.f64_max, math.minInt(i32)); + try test__fixint(f64, i32, -math.inf(f64), math.minInt(i32)); + try test__fixint(f64, i32, -math.floatMax(f64), math.minInt(i32)); try test__fixint(f64, i32, @as(f64, math.minInt(i32)), math.minInt(i32)); try test__fixint(f64, i32, @as(f64, math.minInt(i32)) + 1, math.minInt(i32) + 1); try test__fixint(f64, i32, -2.0, -2); @@ -87,22 +87,22 @@ test "fixint.i32" { try test__fixint(f64, i32, -1.0, -1); try test__fixint(f64, i32, -0.9, 0); try test__fixint(f64, i32, -0.1, 0); - try test__fixint(f64, i32, -math.f32_min, 0); + try test__fixint(f64, i32, -@as(f64, math.floatMin(f32)), 0); try test__fixint(f64, i32, -0.0, 0); try test__fixint(f64, i32, 0.0, 0); - try test__fixint(f64, i32, math.f32_min, 0); + try test__fixint(f64, i32, @as(f64, math.floatMin(f32)), 0); try test__fixint(f64, i32, 0.1, 0); try test__fixint(f64, i32, 0.9, 0); try test__fixint(f64, i32, 1.0, 1); try test__fixint(f64, i32, @as(f64, math.maxInt(i32)) - 1, math.maxInt(i32) - 1); try test__fixint(f64, i32, @as(f64, math.maxInt(i32)), math.maxInt(i32)); - try test__fixint(f64, i32, math.f64_max, math.maxInt(i32)); - try test__fixint(f64, i32, math.inf_f64, math.maxInt(i32)); + try test__fixint(f64, i32, math.floatMax(f64), math.maxInt(i32)); + try test__fixint(f64, i32, math.inf(f64), math.maxInt(i32)); } test "fixint.i64" { - try test__fixint(f64, i64, -math.inf_f64, math.minInt(i64)); - try test__fixint(f64, i64, -math.f64_max, math.minInt(i64)); + try test__fixint(f64, i64, -math.inf(f64), math.minInt(i64)); + try test__fixint(f64, i64, -math.floatMax(f64), math.minInt(i64)); try test__fixint(f64, i64, @as(f64, math.minInt(i64)), math.minInt(i64)); try test__fixint(f64, i64, @as(f64, math.minInt(i64)) + 1, math.minInt(i64)); try test__fixint(f64, i64, @as(f64, math.minInt(i64) / 2), math.minInt(i64) / 2); @@ -112,22 +112,22 @@ test "fixint.i64" { try test__fixint(f64, i64, -1.0, -1); try test__fixint(f64, i64, -0.9, 0); try test__fixint(f64, i64, -0.1, 0); - try test__fixint(f64, i64, -math.f32_min, 0); + try test__fixint(f64, i64, -@as(f64, math.floatMin(f32)), 0); try test__fixint(f64, i64, -0.0, 0); try test__fixint(f64, i64, 0.0, 0); - try test__fixint(f64, i64, math.f32_min, 0); + try test__fixint(f64, i64, @as(f64, math.floatMin(f32)), 0); try test__fixint(f64, i64, 0.1, 0); try test__fixint(f64, i64, 0.9, 0); try test__fixint(f64, i64, 1.0, 1); try test__fixint(f64, i64, @as(f64, math.maxInt(i64)) - 1, math.maxInt(i64)); try test__fixint(f64, i64, @as(f64, math.maxInt(i64)), math.maxInt(i64)); - try test__fixint(f64, i64, math.f64_max, math.maxInt(i64)); - try test__fixint(f64, i64, math.inf_f64, math.maxInt(i64)); + try test__fixint(f64, i64, math.floatMax(f64), math.maxInt(i64)); + try test__fixint(f64, i64, math.inf(f64), math.maxInt(i64)); } test "fixint.i128" { - try test__fixint(f64, i128, -math.inf_f64, math.minInt(i128)); - try test__fixint(f64, i128, -math.f64_max, math.minInt(i128)); + try test__fixint(f64, i128, -math.inf(f64), math.minInt(i128)); + try test__fixint(f64, i128, -math.floatMax(f64), math.minInt(i128)); try test__fixint(f64, i128, @as(f64, math.minInt(i128)), math.minInt(i128)); try test__fixint(f64, i128, @as(f64, math.minInt(i128)) + 1, math.minInt(i128)); try test__fixint(f64, i128, -2.0, -2); @@ -136,15 +136,15 @@ test "fixint.i128" { try test__fixint(f64, i128, -1.0, -1); try test__fixint(f64, i128, -0.9, 0); try test__fixint(f64, i128, -0.1, 0); - try test__fixint(f64, i128, -math.f32_min, 0); + try test__fixint(f64, i128, -@as(f64, math.floatMin(f32)), 0); try test__fixint(f64, i128, -0.0, 0); try test__fixint(f64, i128, 0.0, 0); - try test__fixint(f64, i128, math.f32_min, 0); + try test__fixint(f64, i128, @as(f64, math.floatMin(f32)), 0); try test__fixint(f64, i128, 0.1, 0); try test__fixint(f64, i128, 0.9, 0); try test__fixint(f64, i128, 1.0, 1); try test__fixint(f64, i128, @as(f64, math.maxInt(i128)) - 1, math.maxInt(i128)); try test__fixint(f64, i128, @as(f64, math.maxInt(i128)), math.maxInt(i128)); - try test__fixint(f64, i128, math.f64_max, math.maxInt(i128)); - try test__fixint(f64, i128, math.inf_f64, math.maxInt(i128)); + try test__fixint(f64, i128, math.floatMax(f64), math.maxInt(i128)); + try test__fixint(f64, i128, math.inf(f64), math.maxInt(i128)); } diff --git a/lib/std/special/compiler_rt/fixsfdi_test.zig b/lib/std/special/compiler_rt/fixsfdi_test.zig index 95f56bd29e..1ddd99bbe8 100644 --- a/lib/std/special/compiler_rt/fixsfdi_test.zig +++ b/lib/std/special/compiler_rt/fixsfdi_test.zig @@ -9,7 +9,7 @@ fn test__fixsfdi(a: f32, expected: i64) !void { } test "fixsfdi" { - try test__fixsfdi(-math.f32_max, math.minInt(i64)); + try test__fixsfdi(-math.floatMax(f32), math.minInt(i64)); try test__fixsfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); try test__fixsfdi(-0x1.FFFFFFFFFFFFFp+1023, -0x8000000000000000); @@ -33,9 +33,9 @@ test "fixsfdi" { try test__fixsfdi(-1.0, -1); try test__fixsfdi(-0.99, 0); try test__fixsfdi(-0.5, 0); - try test__fixsfdi(-math.f32_min, 0); + try test__fixsfdi(-math.floatMin(f32), 0); try test__fixsfdi(0.0, 0); - try test__fixsfdi(math.f32_min, 0); + try test__fixsfdi(math.floatMin(f32), 0); try test__fixsfdi(0.5, 0); try test__fixsfdi(0.99, 0); try test__fixsfdi(1.0, 1); @@ -60,5 +60,5 @@ test "fixsfdi" { try test__fixsfdi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFF); try test__fixsfdi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i64)); - try test__fixsfdi(math.f64_max, math.maxInt(i64)); + try test__fixsfdi(math.floatMax(f32), math.maxInt(i64)); } diff --git a/lib/std/special/compiler_rt/fixsfsi_test.zig b/lib/std/special/compiler_rt/fixsfsi_test.zig index 9ea1aafb3e..2393cc388e 100644 --- a/lib/std/special/compiler_rt/fixsfsi_test.zig +++ b/lib/std/special/compiler_rt/fixsfsi_test.zig @@ -9,7 +9,7 @@ fn test__fixsfsi(a: f32, expected: i32) !void { } test "fixsfsi" { - try test__fixsfsi(-math.f32_max, math.minInt(i32)); + try test__fixsfsi(-math.floatMax(f32), math.minInt(i32)); try test__fixsfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); try test__fixsfsi(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000); @@ -37,9 +37,9 @@ test "fixsfsi" { try test__fixsfsi(-1.0, -1); try test__fixsfsi(-0.99, 0); try test__fixsfsi(-0.5, 0); - try test__fixsfsi(-math.f32_min, 0); + try test__fixsfsi(-math.floatMin(f32), 0); try test__fixsfsi(0.0, 0); - try test__fixsfsi(math.f32_min, 0); + try test__fixsfsi(math.floatMin(f32), 0); try test__fixsfsi(0.5, 0); try test__fixsfsi(0.99, 0); try test__fixsfsi(1.0, 1); @@ -68,5 +68,5 @@ test "fixsfsi" { try test__fixsfsi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFF); try test__fixsfsi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i32)); - try test__fixsfsi(math.f32_max, math.maxInt(i32)); + try test__fixsfsi(math.floatMax(f32), math.maxInt(i32)); } diff --git a/lib/std/special/compiler_rt/fixsfti_test.zig b/lib/std/special/compiler_rt/fixsfti_test.zig index 8f29d9ea06..7149f300db 100644 --- a/lib/std/special/compiler_rt/fixsfti_test.zig +++ b/lib/std/special/compiler_rt/fixsfti_test.zig @@ -9,7 +9,7 @@ fn test__fixsfti(a: f32, expected: i128) !void { } test "fixsfti" { - try test__fixsfti(-math.f32_max, math.minInt(i128)); + try test__fixsfti(-math.floatMax(f32), math.minInt(i128)); try test__fixsfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); try test__fixsfti(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000000000000000000000000000); @@ -41,9 +41,9 @@ test "fixsfti" { try test__fixsfti(-1.0, -1); try test__fixsfti(-0.99, 0); try test__fixsfti(-0.5, 0); - try test__fixsfti(-math.f32_min, 0); + try test__fixsfti(-math.floatMin(f32), 0); try test__fixsfti(0.0, 0); - try test__fixsfti(math.f32_min, 0); + try test__fixsfti(math.floatMin(f32), 0); try test__fixsfti(0.5, 0); try test__fixsfti(0.99, 0); try test__fixsfti(1.0, 1); @@ -76,5 +76,5 @@ test "fixsfti" { try test__fixsfti(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); try test__fixsfti(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i128)); - try test__fixsfti(math.f32_max, math.maxInt(i128)); + try test__fixsfti(math.floatMax(f32), math.maxInt(i128)); } diff --git a/lib/std/special/compiler_rt/fixtfdi_test.zig b/lib/std/special/compiler_rt/fixtfdi_test.zig index 5e43a85408..79c320f622 100644 --- a/lib/std/special/compiler_rt/fixtfdi_test.zig +++ b/lib/std/special/compiler_rt/fixtfdi_test.zig @@ -9,7 +9,7 @@ fn test__fixtfdi(a: f128, expected: i64) !void { } test "fixtfdi" { - try test__fixtfdi(-math.f128_max, math.minInt(i64)); + try test__fixtfdi(-math.floatMax(f128), math.minInt(i64)); try test__fixtfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); try test__fixtfdi(-0x1.FFFFFFFFFFFFFp+1023, -0x8000000000000000); @@ -37,9 +37,9 @@ test "fixtfdi" { try test__fixtfdi(-1.0, -1); try test__fixtfdi(-0.99, 0); try test__fixtfdi(-0.5, 0); - try test__fixtfdi(-math.f64_min, 0); + try test__fixtfdi(-@as(f128, math.floatMin(f64)), 0); try test__fixtfdi(0.0, 0); - try test__fixtfdi(math.f64_min, 0); + try test__fixtfdi(@as(f128, math.floatMin(f64)), 0); try test__fixtfdi(0.5, 0); try test__fixtfdi(0.99, 0); try test__fixtfdi(1.0, 1); @@ -68,5 +68,5 @@ test "fixtfdi" { try test__fixtfdi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFF); try test__fixtfdi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i64)); - try test__fixtfdi(math.f128_max, math.maxInt(i64)); + try test__fixtfdi(math.floatMax(f128), math.maxInt(i64)); } diff --git a/lib/std/special/compiler_rt/fixtfsi_test.zig b/lib/std/special/compiler_rt/fixtfsi_test.zig index f00c4735d6..f05a4778f0 100644 --- a/lib/std/special/compiler_rt/fixtfsi_test.zig +++ b/lib/std/special/compiler_rt/fixtfsi_test.zig @@ -9,7 +9,7 @@ fn test__fixtfsi(a: f128, expected: i32) !void { } test "fixtfsi" { - try test__fixtfsi(-math.f128_max, math.minInt(i32)); + try test__fixtfsi(-math.floatMax(f128), math.minInt(i32)); try test__fixtfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); try test__fixtfsi(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000); @@ -37,9 +37,9 @@ test "fixtfsi" { try test__fixtfsi(-1.0, -1); try test__fixtfsi(-0.99, 0); try test__fixtfsi(-0.5, 0); - try test__fixtfsi(-math.f32_min, 0); + try test__fixtfsi(-@as(f128, math.floatMin(f32)), 0); try test__fixtfsi(0.0, 0); - try test__fixtfsi(math.f32_min, 0); + try test__fixtfsi(@as(f128, math.floatMin(f32)), 0); try test__fixtfsi(0.5, 0); try test__fixtfsi(0.99, 0); try test__fixtfsi(1.0, 1); @@ -68,5 +68,5 @@ test "fixtfsi" { try test__fixtfsi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFF); try test__fixtfsi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i32)); - try test__fixtfsi(math.f128_max, math.maxInt(i32)); + try test__fixtfsi(math.floatMax(f128), math.maxInt(i32)); } diff --git a/lib/std/special/compiler_rt/fixtfti_test.zig b/lib/std/special/compiler_rt/fixtfti_test.zig index 3bb113e46b..4c1c9bd640 100644 --- a/lib/std/special/compiler_rt/fixtfti_test.zig +++ b/lib/std/special/compiler_rt/fixtfti_test.zig @@ -9,7 +9,7 @@ fn test__fixtfti(a: f128, expected: i128) !void { } test "fixtfti" { - try test__fixtfti(-math.f128_max, math.minInt(i128)); + try test__fixtfti(-math.floatMax(f128), math.minInt(i128)); try test__fixtfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); try test__fixtfti(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000000000000000000000000000); @@ -32,9 +32,9 @@ test "fixtfti" { try test__fixtfti(-1.0, -1); try test__fixtfti(-0.99, 0); try test__fixtfti(-0.5, 0); - try test__fixtfti(-math.f128_min, 0); + try test__fixtfti(-math.floatMin(f128), 0); try test__fixtfti(0.0, 0); - try test__fixtfti(math.f128_min, 0); + try test__fixtfti(math.floatMin(f128), 0); try test__fixtfti(0.5, 0); try test__fixtfti(0.99, 0); try test__fixtfti(1.0, 1); @@ -58,5 +58,5 @@ test "fixtfti" { try test__fixtfti(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); try test__fixtfti(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i128)); - try test__fixtfti(math.f128_max, math.maxInt(i128)); + try test__fixtfti(math.floatMax(f128), math.maxInt(i128)); } diff --git a/lib/std/special/compiler_rt/floatfmodl_test.zig b/lib/std/special/compiler_rt/floatfmodl_test.zig index 58636ef6f7..22b981d5f2 100644 --- a/lib/std/special/compiler_rt/floatfmodl_test.zig +++ b/lib/std/special/compiler_rt/floatfmodl_test.zig @@ -15,10 +15,10 @@ fn test_fmodl_nans() !void { } fn test_fmodl_infs() !void { - try testing.expect(fmodl.fmodl(1.0, std.math.inf_f128) == 1.0); - try testing.expect(fmodl.fmodl(1.0, -std.math.inf_f128) == 1.0); - try testing.expect(std.math.isNan(fmodl.fmodl(std.math.inf_f128, 1.0))); - try testing.expect(std.math.isNan(fmodl.fmodl(-std.math.inf_f128, 1.0))); + try testing.expect(fmodl.fmodl(1.0, std.math.inf(f128)) == 1.0); + try testing.expect(fmodl.fmodl(1.0, -std.math.inf(f128)) == 1.0); + try testing.expect(std.math.isNan(fmodl.fmodl(std.math.inf(f128), 1.0))); + try testing.expect(std.math.isNan(fmodl.fmodl(-std.math.inf(f128), 1.0))); } test "fmodl" { diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index ccf22a2094..aaf61745b0 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -641,7 +641,7 @@ test "vector reduce operation" { // equal. } else { const F = @TypeOf(expected); - const tolerance = @sqrt(math.epsilon(TX)); + const tolerance = @sqrt(math.floatEps(TX)); try expect(std.math.approxEqRel(F, expected, r, tolerance)); } }, From 737ef3e81b4a417b3f81d54a419644fc8fb6b964 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 7 Apr 2022 22:58:50 -0700 Subject: [PATCH 1076/2031] Sema: remove unnecessary type resolution This causes false positive "foo depends on itself" errors. Prior to some recent enhancements, this type resolution was needed, however, we now have a more sophisticated type resolution mechanism that fully resolves types for the backend, but only after the Decl is fully analyzed, avoiding dependency loops. --- src/Sema.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index ae6d95ef96..503927f2a6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -18026,8 +18026,7 @@ fn coerce( const array_ty = inst_ty.childType(); if (array_ty.zigTypeTag() != .Array) break :src_array_ptr; const len0 = array_ty.arrayLen() == 0; - // We resolve here so that the backend has the layout of the elem type. - const array_elem_type = try sema.resolveTypeFields(block, inst_src, array_ty.childType()); + const array_elem_type = array_ty.childType(); const dest_is_mut = dest_info.mutable; if (inst_ty.isConstPtr() and dest_is_mut and !len0) break :src_array_ptr; if (inst_ty.isVolatilePtr() and !dest_info.@"volatile") break :src_array_ptr; From 6ae8fe193bd5397b9fceea98ba684d9d870286cf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 7 Apr 2022 23:11:11 -0700 Subject: [PATCH 1077/2031] Liveness: utilize Air.refToIndex --- src/Liveness.zig | 4 +--- src/arch/x86_64/CodeGen.zig | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Liveness.zig b/src/Liveness.zig index 59f7f5be91..2ba59ec1de 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -796,9 +796,7 @@ const ExtraTombs = struct { assert(this_bit_index < 32); // TODO mechanism for when there are greater than 32 operands et.bit_index += 1; const gpa = et.analysis.gpa; - const op_int = @enumToInt(op_ref); - if (op_int < Air.Inst.Ref.typed_value_map.len) return; - const op_index: Air.Inst.Index = op_int - @intCast(u32, Air.Inst.Ref.typed_value_map.len); + const op_index = Air.refToIndex(op_ref) orelse return; const prev = try et.analysis.table.fetchPut(gpa, op_index, {}); if (prev == null) { // Death. diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 7f5144a6dc..f52742ffa5 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -280,9 +280,7 @@ const BigTomb = struct { const this_bit_index = bt.bit_index; bt.bit_index += 1; - const op_int = @enumToInt(op_ref); - if (op_int < Air.Inst.Ref.typed_value_map.len) return; - const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); + const op_index = Air.refToIndex(op_ref) orelse return; if (this_bit_index < Liveness.bpi - 1) { const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0; From 531d5b213f5cc7450c56bea3fc2050dda5794067 Mon Sep 17 00:00:00 2001 From: cryptocode Date: Fri, 8 Apr 2022 20:26:56 +0200 Subject: [PATCH 1078/2031] std: add Thread.Condition.timedWait (#11352) * std: add Thread.Condition.timedWait I needed the equivalent of `std::condition_variable::wait_for`, but it's missing in std. This PR adds an implementation, following the status quo of using std.os.CLOCK.REALTIME in the pthread case (i.e. Futex) A follow-up patch moving futex/condition stuff to monotonic clocks where available seems like a good idea. This would involve conditionally exposing more functions and constants through std.c and std.os. For instance, Chromium picks `pthread_cond_timedwait_relative_np` on macOS and `clock_gettime(CLOCK_MONOTONIC...)` on BSD's. Tested on Windows 11, macOS 12.2.1 and Linux (with/without libc) * Sleep in the single threaded case, handle timeout overflow in the Windows case and address a race condition in the AtomicCondition case. --- lib/std/Thread/Condition.zig | 175 ++++++++++++++++++++++++++++++++++- 1 file changed, 173 insertions(+), 2 deletions(-) diff --git a/lib/std/Thread/Condition.zig b/lib/std/Thread/Condition.zig index 7a479e5540..fb48db8e53 100644 --- a/lib/std/Thread/Condition.zig +++ b/lib/std/Thread/Condition.zig @@ -17,6 +17,10 @@ pub fn wait(cond: *Condition, mutex: *Mutex) void { cond.impl.wait(mutex); } +pub fn timedWait(cond: *Condition, mutex: *Mutex, timeout_ns: u64) error{TimedOut}!void { + try cond.impl.timedWait(mutex, timeout_ns); +} + pub fn signal(cond: *Condition) void { cond.impl.signal(); } @@ -41,6 +45,14 @@ pub const SingleThreadedCondition = struct { unreachable; // deadlock detected } + pub fn timedWait(cond: *SingleThreadedCondition, mutex: *Mutex, timeout_ns: u64) error{TimedOut}!void { + _ = cond; + _ = mutex; + _ = timeout_ns; + std.time.sleep(timeout_ns); + return error.TimedOut; + } + pub fn signal(cond: *SingleThreadedCondition) void { _ = cond; } @@ -63,6 +75,25 @@ pub const WindowsCondition = struct { assert(rc != windows.FALSE); } + pub fn timedWait(cond: *WindowsCondition, mutex: *Mutex, timeout_ns: u64) error{TimedOut}!void { + var timeout_checked = std.math.cast(windows.DWORD, timeout_ns / std.time.ns_per_ms) catch overflow: { + break :overflow std.math.maxInt(windows.DWORD); + }; + + // Handle the case where timeout is INFINITE, otherwise SleepConditionVariableSRW's time-out never elapses + const timeout_overflowed = timeout_checked == windows.INFINITE; + timeout_checked -= @boolToInt(timeout_overflowed); + + const rc = windows.kernel32.SleepConditionVariableSRW( + &cond.cond, + &mutex.impl.srwlock, + timeout_checked, + @as(windows.ULONG, 0), + ); + if (rc == windows.FALSE and windows.kernel32.GetLastError() == windows.Win32Error.TIMEOUT) return error.TimedOut; + assert(rc != windows.FALSE); + } + pub fn signal(cond: *WindowsCondition) void { windows.kernel32.WakeConditionVariable(&cond.cond); } @@ -80,6 +111,24 @@ pub const PthreadCondition = struct { assert(rc == .SUCCESS); } + pub fn timedWait(cond: *PthreadCondition, mutex: *Mutex, timeout_ns: u64) error{TimedOut}!void { + var ts: std.os.timespec = undefined; + std.os.clock_gettime(std.os.CLOCK.REALTIME, &ts) catch unreachable; + ts.tv_sec += @intCast(@TypeOf(ts.tv_sec), timeout_ns / std.time.ns_per_s); + ts.tv_nsec += @intCast(@TypeOf(ts.tv_nsec), timeout_ns % std.time.ns_per_s); + if (ts.tv_nsec >= std.time.ns_per_s) { + ts.tv_sec += 1; + ts.tv_nsec -= std.time.ns_per_s; + } + + const rc = std.c.pthread_cond_timedwait(&cond.cond, &mutex.impl.pthread_mutex, &ts); + return switch (rc) { + .SUCCESS => {}, + .TIMEDOUT => error.TimedOut, + else => unreachable, + }; + } + pub fn signal(cond: *PthreadCondition) void { const rc = std.c.pthread_cond_signal(&cond.cond); assert(rc == .SUCCESS); @@ -100,6 +149,7 @@ pub const AtomicCondition = struct { pub const QueueItem = struct { futex: i32 = 0, + dequeued: bool = false, fn wait(cond: *@This()) void { while (@atomicLoad(i32, &cond.futex, .Acquire) == 0) { @@ -122,6 +172,39 @@ pub const AtomicCondition = struct { } } + pub fn timedWait(cond: *@This(), timeout_ns: u64) error{TimedOut}!void { + const start_time = std.time.nanoTimestamp(); + while (@atomicLoad(i32, &cond.futex, .Acquire) == 0) { + switch (builtin.os.tag) { + .linux => { + var ts: std.os.timespec = undefined; + ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), timeout_ns / std.time.ns_per_s); + ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), timeout_ns % std.time.ns_per_s); + switch (linux.getErrno(linux.futex_wait( + &cond.futex, + linux.FUTEX.PRIVATE_FLAG | linux.FUTEX.WAIT, + 0, + &ts, + ))) { + .SUCCESS => {}, + .INTR => {}, + .AGAIN => {}, + .TIMEDOUT => return error.TimedOut, + .INVAL => {}, // possibly timeout overflow + .FAULT => unreachable, + else => unreachable, + } + }, + else => { + if (std.time.nanoTimestamp() - start_time >= timeout_ns) { + return error.TimedOut; + } + std.atomic.spinLoopHint(); + }, + } + } + } + fn notify(cond: *@This()) void { @atomicStore(i32, &cond.futex, 1, .Release); @@ -158,6 +241,41 @@ pub const AtomicCondition = struct { mutex.lock(); } + pub fn timedWait(cond: *AtomicCondition, mutex: *Mutex, timeout_ns: u64) error{TimedOut}!void { + var waiter = QueueList.Node{ .data = .{} }; + + { + cond.queue_mutex.lock(); + defer cond.queue_mutex.unlock(); + + cond.queue_list.prepend(&waiter); + @atomicStore(bool, &cond.pending, true, .SeqCst); + } + + var timed_out = false; + mutex.unlock(); + defer mutex.lock(); + waiter.data.timedWait(timeout_ns) catch |err| switch (err) { + error.TimedOut => { + defer if (!timed_out) { + waiter.data.wait(); + }; + cond.queue_mutex.lock(); + defer cond.queue_mutex.unlock(); + + if (!waiter.data.dequeued) { + timed_out = true; + cond.queue_list.remove(&waiter); + } + }, + else => unreachable, + }; + + if (timed_out) { + return error.TimedOut; + } + } + pub fn signal(cond: *AtomicCondition) void { if (@atomicLoad(bool, &cond.pending, .SeqCst) == false) return; @@ -167,12 +285,16 @@ pub const AtomicCondition = struct { defer cond.queue_mutex.unlock(); const maybe_waiter = cond.queue_list.popFirst(); + if (maybe_waiter) |waiter| { + waiter.data.dequeued = true; + } @atomicStore(bool, &cond.pending, cond.queue_list.first != null, .SeqCst); break :blk maybe_waiter; }; - if (maybe_waiter) |waiter| + if (maybe_waiter) |waiter| { waiter.data.notify(); + } } pub fn broadcast(cond: *AtomicCondition) void { @@ -186,12 +308,19 @@ pub const AtomicCondition = struct { defer cond.queue_mutex.unlock(); const waiters = cond.queue_list; + + var it = waiters.first; + while (it) |node| : (it = node.next) { + node.data.dequeued = true; + } + cond.queue_list = .{}; break :blk waiters; }; - while (waiters.popFirst()) |waiter| + while (waiters.popFirst()) |waiter| { waiter.data.notify(); + } } }; @@ -238,3 +367,45 @@ test "Thread.Condition" { for (threads) |t| t.join(); } + +test "Thread.Condition.timedWait" { + if (builtin.single_threaded) { + return error.SkipZigTest; + } + + var cond = Condition{}; + var mut = Mutex{}; + + // Expect a timeout, as the condition variable is never signaled + { + mut.lock(); + defer mut.unlock(); + try testing.expectError(error.TimedOut, cond.timedWait(&mut, 10 * std.time.ns_per_ms)); + } + + // Expect a signal before timeout + { + const TestContext = struct { + cond: *Condition, + mutex: *Mutex, + n: *u32, + fn worker(ctx: *@This()) void { + ctx.mutex.lock(); + defer ctx.mutex.unlock(); + ctx.n.* = 1; + ctx.cond.signal(); + } + }; + + var n: u32 = 0; + + var ctx = TestContext{ .cond = &cond, .mutex = &mut, .n = &n }; + mut.lock(); + var thread = try std.Thread.spawn(.{}, TestContext.worker, .{&ctx}); + // Looped check to handle spurious wakeups + while (n != 1) try cond.timedWait(&mut, 500 * std.time.ns_per_ms); + mut.unlock(); + try testing.expect(n == 1); + thread.join(); + } +} From f9a9e8ba7375a6e4c7e9fa50902a897387212dbb Mon Sep 17 00:00:00 2001 From: viri Date: Fri, 8 Apr 2022 20:13:30 -0600 Subject: [PATCH 1079/2031] std.math.isInf: add tests for +-NaN --- lib/std/math/isinf.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/std/math/isinf.zig b/lib/std/math/isinf.zig index 2524354207..e88b9810b6 100644 --- a/lib/std/math/isinf.zig +++ b/lib/std/math/isinf.zig @@ -32,6 +32,8 @@ test "math.isInf" { try expect(!isInf(@as(T, -0.0))); try expect(isInf(math.inf(T))); try expect(isInf(-math.inf(T))); + try expect(!isInf(math.nan(T))); + try expect(!isInf(-math.nan(T))); } } @@ -44,6 +46,8 @@ test "math.isPositiveInf" { try expect(!isPositiveInf(@as(T, -0.0))); try expect(isPositiveInf(math.inf(T))); try expect(!isPositiveInf(-math.inf(T))); + try expect(!isInf(math.nan(T))); + try expect(!isInf(-math.nan(T))); } } @@ -56,5 +60,7 @@ test "math.isNegativeInf" { try expect(!isNegativeInf(@as(T, -0.0))); try expect(!isNegativeInf(math.inf(T))); try expect(isNegativeInf(-math.inf(T))); + try expect(!isInf(math.nan(T))); + try expect(!isInf(-math.nan(T))); } } From 9b5c02022f997d01bcfcfd79ba4c721af1bd9a6c Mon Sep 17 00:00:00 2001 From: viri Date: Fri, 8 Apr 2022 20:43:27 -0600 Subject: [PATCH 1080/2031] compiler-rt(divtf3): fix remark, add more tests --- lib/std/special/compiler_rt/divtf3_test.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/std/special/compiler_rt/divtf3_test.zig b/lib/std/special/compiler_rt/divtf3_test.zig index 925f8bbc91..62204057d4 100644 --- a/lib/std/special/compiler_rt/divtf3_test.zig +++ b/lib/std/special/compiler_rt/divtf3_test.zig @@ -34,8 +34,12 @@ test "divtf3" { try test__divtf3(math.qnan_f128, 0x1.23456789abcdefp+5, 0x7fff800000000000, 0); // NaN / any = NaN try test__divtf3(math.nan_f128, 0x1.23456789abcdefp+5, 0x7fff800000000000, 0); - // inf / any = inf + // inf / any(except inf and nan) = inf try test__divtf3(math.inf(f128), 0x1.23456789abcdefp+5, 0x7fff000000000000, 0); + // inf / inf = nan + try test__divtf3(math.inf(f128), math.inf(f128), 0x7fff800000000000, 0); + // inf / nan = nan + try test__divtf3(math.inf(f128), math.nan(f128), 0x7fff800000000000, 0); try test__divtf3(0x1.a23b45362464523375893ab4cdefp+5, 0x1.eedcbaba3a94546558237654321fp-1, 0x4004b0b72924d407, 0x0717e84356c6eba2); try test__divtf3(0x1.a2b34c56d745382f9abf2c3dfeffp-50, 0x1.ed2c3ba15935332532287654321fp-9, 0x3fd5b2af3f828c9b, 0x40e51f64cde8b1f2); From 19e343b8d46725265eb72796bec506050e182900 Mon Sep 17 00:00:00 2001 From: Igor Stojkovic Date: Sat, 9 Apr 2022 12:43:30 +0200 Subject: [PATCH 1081/2031] stage1: Additional fix for packed structs --- src/stage1/ir.cpp | 3 +++ test/behavior/packed-struct.zig | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 22ed5befe1..df3439d25c 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -15484,6 +15484,9 @@ static Stage1AirInst *ir_analyze_struct_field_ptr(IrAnalyze *ira, Scope *scope, assert(struct_ptr->value->type->id == ZigTypeIdPointer); uint32_t ptr_bit_offset = struct_ptr->value->type->data.pointer.bit_offset_in_host; uint32_t ptr_host_int_bytes = struct_ptr->value->type->data.pointer.host_int_bytes; + if (ptr_host_int_bytes > 0) { + ptr_bit_offset += field->offset * 8; + } uint32_t host_int_bytes_for_result_type = (ptr_host_int_bytes == 0) ? get_host_int_bytes(ira->codegen, struct_type, field) : ptr_host_int_bytes; ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index 86ef9e3c1e..44ac21aea9 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -322,3 +322,33 @@ test "nested packed structs" { try expectEqual(9, @offsetOf(S6, "c")); try expectEqual(72, @bitOffsetOf(S6, "c")); } + +test "regular in irregular packed struct" { + if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + + const Irregular = packed struct { + bar: Regular = Regular{}, + + // This field forces the regular packed struct to be a part of single u48 + // and thus it all gets represented as an array of 6 bytes in LLVM + _: u24 = 0, + + // This struct on its own can represent its fields directly in LLVM + // with no need to use array of bytes as underlaying representation. + pub const Regular = packed struct { a: u16 = 0, b: u8 = 0 }; + }; + + var foo = Irregular{}; + foo.bar.a = 235; + foo.bar.b = 42; + + try expectEqual(@as(u16, 235), foo.bar.a); + try expectEqual(@as(u8, 42), foo.bar.b); +} From 971ef7b9c2d67ee849e252e75b79ee1e1ef3da6f Mon Sep 17 00:00:00 2001 From: Kirk Scheibelhut Date: Fri, 8 Apr 2022 15:25:22 -0700 Subject: [PATCH 1082/2031] Remove primitive values from keyword reference 5a53ab28 removed these as keywords, and the Primitive Values section of the docs already exists to describe them. --- doc/langref.html.in | 44 -------------------------------------------- 1 file changed, 44 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 7948c2defc..0f90a88822 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -11955,17 +11955,6 @@ fn readU32Be() u32 {}
- - -
{#syntax#}false{#endsyntax#}
- - - The boolean value {#syntax#}false{#endsyntax#}. -
    -
  • See also {#link|Primitive Values#}
  • -
- -
{#syntax#}fn{#endsyntax#}
@@ -12041,17 +12030,6 @@ fn readU32Be() u32 {} - - -
{#syntax#}null{#endsyntax#}
- - - The optional value {#syntax#}null{#endsyntax#}. -
    -
  • See also {#link|null#}
  • -
- -
{#syntax#}or{#endsyntax#}
@@ -12190,17 +12168,6 @@ fn readU32Be() u32 {} - - -
{#syntax#}true{#endsyntax#}
- - - The boolean value {#syntax#}true{#endsyntax#}. -
    -
  • See also {#link|Primitive Values#}
  • -
- -
{#syntax#}try{#endsyntax#}
@@ -12214,17 +12181,6 @@ fn readU32Be() u32 {} - - -
{#syntax#}undefined{#endsyntax#}
- - - {#syntax#}undefined{#endsyntax#} can be used to leave a value uninitialized. -
    -
  • See also {#link|undefined#}
  • -
- -
{#syntax#}union{#endsyntax#}
From d2681d2537b630359657e117ed77cf12a15bb7df Mon Sep 17 00:00:00 2001 From: Stephen Gutekanst Date: Sun, 10 Apr 2022 20:53:56 -0700 Subject: [PATCH 1083/2031] std.event.WaitGroup: fix compilation (acquire->lock, release->unlock) In 008b0ec5e58fc7e31f3b989868a7d1ea4df3f41d the `std.Thread.Mutex` API was changed from `acquire` and `release` to `lock` and `unlock`. `std.event.Lock` still uses `acquire` and `release`. `std.event.WaitGroup` is using `std.Thread.Mutex` and was not updated to use `lock` and `unlock`, and so compilation failed prior to this commit. Signed-off-by: Stephen Gutekanst --- lib/std/event/wait_group.zig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/std/event/wait_group.zig b/lib/std/event/wait_group.zig index d26986f384..4cc82cf98e 100644 --- a/lib/std/event/wait_group.zig +++ b/lib/std/event/wait_group.zig @@ -35,8 +35,8 @@ pub fn WaitGroupGeneric(comptime counter_size: u16) type { const Self = @This(); pub fn begin(self: *Self, count: CounterType) error{Overflow}!void { - const held = self.mutex.acquire(); - defer held.release(); + self.mutex.lock(); + defer self.mutex.unlock(); const new_counter = try std.math.add(CounterType, self.counter, count); if (new_counter > self.max_counter) return error.Overflow; @@ -45,8 +45,8 @@ pub fn WaitGroupGeneric(comptime counter_size: u16) type { pub fn finish(self: *Self, count: CounterType) void { var waiters = blk: { - const held = self.mutex.acquire(); - defer held.release(); + self.mutex.lock(); + defer self.mutex.unlock(); self.counter = std.math.sub(CounterType, self.counter, count) catch unreachable; if (self.counter == 0) { const temp = self.waiters; @@ -65,10 +65,10 @@ pub fn WaitGroupGeneric(comptime counter_size: u16) type { } pub fn wait(self: *Self) void { - const held = self.mutex.acquire(); + self.mutex.lock(); if (self.counter == 0) { - held.release(); + self.mutex.unlock(); return; } @@ -83,7 +83,7 @@ pub fn WaitGroupGeneric(comptime counter_size: u16) type { self_waiter.next = null; } suspend { - held.release(); + self.mutex.unlock(); } } }; From 7851377e95f1a0e9467a7e69dd506b47a4f45b87 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Mon, 28 Mar 2022 14:16:48 -0700 Subject: [PATCH 1084/2031] Improve whitespace-handling in (compile-error) test manifest parsing --- src/test.zig | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/test.zig b/src/test.zig index a0d0d202d1..e664b0b893 100644 --- a/src/test.zig +++ b/src/test.zig @@ -720,14 +720,17 @@ pub const TestContext = struct { // Move to beginning of line while (cursor > 0 and src[cursor - 1] != '\n') cursor -= 1; - // Check if line is non-empty and does not start with "//" - if (cursor + 1 < src.len and src[cursor + 1] != '\n' and src[cursor + 1] != '\r') { - if (std.mem.startsWith(u8, src[cursor..], "//")) { - manifest_start = cursor; - } else { - break; - } - } else manifest_end = cursor; + if (std.mem.startsWith(u8, src[cursor..], "//")) { + manifest_start = cursor; // Contiguous comment line, include in manifest + } else { + if (manifest_start != null) break; // Encountered non-comment line, end of manifest + + // We ignore all-whitespace lines following the comment block, but anything else + // means that there is no manifest present. + if (std.mem.trim(u8, src[cursor..manifest_end], " \r\n\t").len == 0) { + manifest_end = cursor; + } else break; // If it's not whitespace, there is no manifest + } // Move to previous line if (cursor != 0) cursor -= 1 else break; From 879b5627799b82ba7bcb4f15a8f0f517e014b222 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Mon, 11 Apr 2022 14:52:09 -0700 Subject: [PATCH 1085/2031] Add file support for incremental error tests Compile error test cases can now be given as a sequence of files: - "foo.1.zig" - "foo.2.zig" - "foo.3.zig" - etc. This sequence of files is tested as incremental compilation updates to a single "foo.zig" source file. To help avoid mistakes, we enforce strict ordering for these files. "foo.zig" cannot co-exist with "foo.X.zig", the sequence must include "foo.1.zig", and no numbers may be skipped. --- src/test.zig | 120 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 113 insertions(+), 7 deletions(-) diff --git a/src/test.zig b/src/test.zig index e664b0b893..3bb17f7605 100644 --- a/src/test.zig +++ b/src/test.zig @@ -46,6 +46,7 @@ test { defer stage2_dir.close(); // TODO make this incremental once the bug is solved that it triggers + // See: https://github.com/ziglang/zig/issues/11344 ctx.addErrorCasesFromDir("stage2", stage2_dir, .stage2, .Obj, false, .independent); } @@ -653,7 +654,14 @@ pub const TestContext = struct { case.compiles(fixed_src); } - const Strategy = enum { incremental, independent }; + const Strategy = enum { + /// Execute tests as independent compilations, unless they are explicitly + /// incremental ("foo.1.zig", "foo.2.zig", etc.) + independent, + /// Execute all tests as incremental updates to a single compilation. Explicitly + /// incremental tests ("foo.1.zig", "foo.2.zig", etc.) still execute in order + incremental, + }; /// Adds a compile-error test for each file in the provided directory, using the /// selected backend and output mode. If `one_test_case_per_file` is true, a new @@ -681,6 +689,66 @@ pub const TestContext = struct { }; } + /// For a filename in the format ".X." or ".", returns + /// "", "" and X parsed as a decimal number. If X is not present, or + /// cannot be parsed as a decimal number, it is treated as part of + fn getTestFileNameParts(name: []const u8) struct { + base_name: []const u8, + file_ext: []const u8, + test_index: ?usize, + } { + const file_ext = std.fs.path.extension(name); + const trimmed = name[0 .. name.len - file_ext.len]; // Trim off "." + const maybe_index = std.fs.path.extension(trimmed); // Extract ".X" + + // Attempt to parse index + const index: ?usize = if (maybe_index.len > 0) + std.fmt.parseInt(usize, maybe_index[1..], 10) catch null + else + null; + + // Adjust "" extent based on parsing success + const base_name_end = trimmed.len - if (index != null) maybe_index.len else 0; + return .{ + .base_name = name[0..base_name_end], + .file_ext = if (file_ext.len > 0) file_ext[1..] else file_ext, + .test_index = index, + }; + } + + /// Sort test filenames in-place, so that incremental test cases ("foo.1.zig", + /// "foo.2.zig", etc.) are contiguous and appear in numerical order. + fn sortTestFilenames( + filenames: [][]const u8, + ) void { + const Context = struct { + pub fn lessThan(_: @This(), a: []const u8, b: []const u8) bool { + const a_parts = getTestFileNameParts(a); + const b_parts = getTestFileNameParts(b); + + // Sort ".X." based on "" and "" first + return switch (std.mem.order(u8, a_parts.base_name, b_parts.base_name)) { + .lt => true, + .gt => false, + .eq => switch (std.mem.order(u8, a_parts.file_ext, b_parts.file_ext)) { + .lt => true, + .gt => false, + .eq => b: { // a and b differ only in their ".X" part + + // Sort "." before any ".X." + if (a_parts.test_index == null) break :b true; + if (b_parts.test_index == null) break :b false; + + // Make sure that incremental tests appear in linear order + return a_parts.test_index.? < b_parts.test_index.?; + }, + }, + }; + } + }; + std.sort.sort([]const u8, filenames, Context{}, Context.lessThan); + } + fn addErrorCasesFromDirInner( ctx: *TestContext, name: []const u8, @@ -696,6 +764,9 @@ pub const TestContext = struct { var opt_case: ?*Case = null; var it = dir.iterate(); + var filenames = std.ArrayList([]const u8).init(ctx.arena); + defer filenames.deinit(); + while (try it.next()) |entry| { if (entry.kind != .File) continue; @@ -704,11 +775,46 @@ pub const TestContext = struct { .unknown => continue, else => {}, } + try filenames.append(try ctx.arena.dupe(u8, entry.name)); + } - current_file.* = try ctx.arena.dupe(u8, entry.name); + // Sort filenames, so that incremental tests are contiguous and in-order + sortTestFilenames(filenames.items); + + var prev_filename: []const u8 = ""; + for (filenames.items) |filename| { + current_file.* = filename; + + { // First, check if this file is part of an incremental update sequence + + // Split filename into ".." + const prev_parts = getTestFileNameParts(prev_filename); + const new_parts = getTestFileNameParts(filename); + + // If base_name and file_ext match, these files are in the same test sequence + // and the new one should be the incremented version of the previous test + if (std.mem.eql(u8, prev_parts.base_name, new_parts.base_name) and + std.mem.eql(u8, prev_parts.file_ext, new_parts.file_ext)) + { + + // This is "foo.X.zig" followed by "foo.Y.zig". Make sure that X = Y + 1 + if (prev_parts.test_index == null) return error.InvalidIncrementalTestIndex; + if (new_parts.test_index == null) return error.InvalidIncrementalTestIndex; + if (new_parts.test_index.? != prev_parts.test_index.? + 1) return error.InvalidIncrementalTestIndex; + } else { + + // This is not the same test sequence, so the new file must be the first file + // in a new sequence ("*.1.zig") or an independent test file ("*.zig") + if (new_parts.test_index != null and new_parts.test_index.? != 1) return error.InvalidIncrementalTestIndex; + + if (strategy == .independent) + opt_case = null; // Generate a new independent test case for this update + } + } + prev_filename = filename; const max_file_size = 10 * 1024 * 1024; - const src = try dir.readFileAllocOptions(ctx.arena, entry.name, max_file_size, null, 1, 0); + const src = try dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0); // The manifest is the last contiguous block of comments in the file // We scan for the beginning by searching backward for the first non-empty line that does not start with "//" @@ -741,6 +847,7 @@ pub const TestContext = struct { if (manifest_start) |start| { // Due to the above processing, we know that this is a contiguous block of comments + // and do not need to re-validate the leading "//" on each line var manifest_it = std.mem.tokenize(u8, src[start..manifest_end], "\r\n"); // First line is the test case name @@ -773,7 +880,6 @@ pub const TestContext = struct { .independent => { case.name = case_name; case.addError(src, errors.items); - opt_case = null; }, .incremental => { case.addErrorNamed(case_name, src, errors.items); @@ -1133,7 +1239,7 @@ pub const TestContext = struct { if (all_errors.list.len != 0) { print( "\nCase '{s}': unexpected errors at update_index={d}:\n{s}\n", - .{ case.name, update_index, hr }, + .{ case.name, update_index + 1, hr }, ); for (all_errors.list) |err_msg| { switch (err_msg) { @@ -1295,7 +1401,7 @@ pub const TestContext = struct { } if (any_failed) { - print("\nupdate_index={d} ", .{update_index}); + print("\nupdate_index={d}\n", .{update_index + 1}); return error.WrongCompileErrors; } }, @@ -1402,7 +1508,7 @@ pub const TestContext = struct { .cwd = tmp_dir_path, }) catch |err| { print("\nupdate_index={d} The following command failed with {s}:\n", .{ - update_index, @errorName(err), + update_index + 1, @errorName(err), }); dumpArgs(argv.items); return error.ChildProcessExecution; From 988afd51cd34649887f493b6ac9e05fcaa1f768d Mon Sep 17 00:00:00 2001 From: r00ster Date: Tue, 12 Apr 2022 11:32:45 +0200 Subject: [PATCH 1086/2031] Add `std.fs.File.sync` (#11410) --- lib/std/fs/file.zig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index 568c34e0ac..3ea8dd0285 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -200,6 +200,17 @@ pub const File = struct { } } + pub const SyncError = os.SyncError; + + /// Blocks until all pending file contents and metadata modifications + /// for the file have been synchronized with the underlying filesystem. + /// + /// Note that this does not ensure that metadata for the + /// directory containing the file has also reached disk. + pub fn sync(self: File) SyncError!void { + return os.fsync(self.handle); + } + /// Test whether the file refers to a terminal. /// See also `supportsAnsiEscapeCodes`. pub fn isTty(self: File) bool { From c4aac28a426351c7718c605e0c06d9e69b60772e Mon Sep 17 00:00:00 2001 From: r00ster Date: Fri, 8 Apr 2022 18:15:57 +0200 Subject: [PATCH 1087/2031] Reuse code in `Utf8Iterator.nextCodepoint` --- lib/std/unicode.zig | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/std/unicode.zig b/lib/std/unicode.zig index e0a000dfe5..487f3defdf 100644 --- a/lib/std/unicode.zig +++ b/lib/std/unicode.zig @@ -269,14 +269,7 @@ pub const Utf8Iterator = struct { pub fn nextCodepoint(it: *Utf8Iterator) ?u21 { const slice = it.nextCodepointSlice() orelse return null; - - switch (slice.len) { - 1 => return @as(u21, slice[0]), - 2 => return utf8Decode2(slice) catch unreachable, - 3 => return utf8Decode3(slice) catch unreachable, - 4 => return utf8Decode4(slice) catch unreachable, - else => unreachable, - } + return utf8Decode(slice) catch unreachable; } /// Look ahead at the next n codepoints without advancing the iterator. From b9d86c6bc8e5d475ed8613bb241d1520377e629c Mon Sep 17 00:00:00 2001 From: jagt Date: Tue, 12 Apr 2022 12:01:47 +0800 Subject: [PATCH 1088/2031] fix `link.renameTmpIntoCache` on windows when dest dir exists. Previously it would fail as `renameW` do not ever fail with `PathAlreadyExists`. As a workaround we check for dest dir existence before rename on Windows. --- src/link.zig | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/src/link.zig b/src/link.zig index 7c135a7405..139b12af99 100644 --- a/src/link.zig +++ b/src/link.zig @@ -717,19 +717,38 @@ pub const File = struct { // directly, and remove this function from link.zig. _ = base; while (true) { - std.fs.rename( - cache_directory.handle, - tmp_dir_sub_path, - cache_directory.handle, - o_sub_path, - ) catch |err| switch (err) { - error.PathAlreadyExists => { + if (builtin.os.tag == .windows) { + // workaround windows `renameW` can't fail with `PathAlreadyExists` + // See https://github.com/ziglang/zig/issues/8362 + if (cache_directory.handle.access(o_sub_path, .{})) |_| { try cache_directory.handle.deleteTree(o_sub_path); continue; - }, - else => |e| return e, - }; - break; + } else |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + } + try std.fs.rename( + cache_directory.handle, + tmp_dir_sub_path, + cache_directory.handle, + o_sub_path, + ); + break; + } else { + std.fs.rename( + cache_directory.handle, + tmp_dir_sub_path, + cache_directory.handle, + o_sub_path, + ) catch |err| switch (err) { + error.PathAlreadyExists => { + try cache_directory.handle.deleteTree(o_sub_path); + continue; + }, + else => |e| return e, + }; + break; + } } } From 38d6e1d8a85ff77bc98dd80f604525e0804dec11 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Tue, 12 Apr 2022 00:25:47 +0200 Subject: [PATCH 1089/2031] std.build: Fix transitive linkSystemLibraryName() dependencies Currently transitive system library dependencies are always linked using linkSystemLibrary() and therefore pkg-config even if they were originally specified with linkSystemLibraryName() instead. This causes problems in practice for projects needing total control over exactly what library is linked, such as the mach game engine. This is fixed by keeping track of whether libraries are to be linked with pkg-config or not and holding off on actually running pkg-config until after transitive dependency resolution in LibExeObjStep.make(). This also fixes a separate issue with the pkg-config handling that could cause partial application of pkg-config flags if the first part of the pkg-config output parses correctly but there is an error later on. This error isn't always fatal as we fall back to a plain -lfoo in the case of linkSystemLibrary(). --- lib/std/build.zig | 170 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 120 insertions(+), 50 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index f1287c7be5..ff0f36b4a8 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1600,12 +1600,26 @@ pub const LibExeObjStep = struct { pub const LinkObject = union(enum) { static_path: FileSource, other_step: *LibExeObjStep, - system_lib: []const u8, + system_lib: SystemLib, assembly_file: FileSource, c_source_file: *CSourceFile, c_source_files: *CSourceFiles, }; + pub const SystemLib = struct { + name: []const u8, + use_pkg_config: enum { + /// Don't use pkg-config, just pass -lfoo where foo is name. + no, + /// Try to get information on how to link the library from pkg-config. + /// If that fails, fall back to passing -lfoo where foo is name. + yes, + /// Try to get information on how to link the library from pkg-config. + /// If that fails, error out. + force, + }, + }; + pub const IncludeDir = union(enum) { raw_path: []const u8, raw_path_system: []const u8, @@ -1854,7 +1868,7 @@ pub const LibExeObjStep = struct { } for (self.link_objects.items) |link_object| { switch (link_object) { - .system_lib => |n| if (mem.eql(u8, n, name)) return true, + .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true, else => continue, } } @@ -1879,14 +1893,24 @@ pub const LibExeObjStep = struct { pub fn linkLibC(self: *LibExeObjStep) void { if (!self.is_linking_libc) { self.is_linking_libc = true; - self.link_objects.append(.{ .system_lib = "c" }) catch unreachable; + self.link_objects.append(.{ + .system_lib = .{ + .name = "c", + .use_pkg_config = .no, + }, + }) catch unreachable; } } pub fn linkLibCpp(self: *LibExeObjStep) void { if (!self.is_linking_libcpp) { self.is_linking_libcpp = true; - self.link_objects.append(.{ .system_lib = "c++" }) catch unreachable; + self.link_objects.append(.{ + .system_lib = .{ + .name = "c++", + .use_pkg_config = .no, + }, + }) catch unreachable; } } @@ -1905,12 +1929,28 @@ pub const LibExeObjStep = struct { /// This one has no integration with anything, it just puts -lname on the command line. /// Prefer to use `linkSystemLibrary` instead. pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void { - self.link_objects.append(.{ .system_lib = self.builder.dupe(name) }) catch unreachable; + self.link_objects.append(.{ + .system_lib = .{ + .name = self.builder.dupe(name), + .use_pkg_config = .no, + }, + }) catch unreachable; } /// This links against a system library, exclusively using pkg-config to find the library. /// Prefer to use `linkSystemLibrary` instead. - pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) !void { + pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void { + self.link_objects.append(.{ + .system_lib = .{ + .name = self.builder.dupe(lib_name), + .use_pkg_config = .force, + }, + }) catch unreachable; + } + + /// Run pkg-config for the given library name and parse the output, returning the arguments + /// that should be passed to zig to link the given library. + fn runPkgConfig(self: *LibExeObjStep, lib_name: []const u8) ![]const []const u8 { const pkg_name = match: { // First we have to map the library name to pkg config name. Unfortunately, // there are several examples where this is not straightforward: @@ -1970,34 +2010,38 @@ pub const LibExeObjStep = struct { error.ChildExecFailed => return error.PkgConfigFailed, else => return err, }; + + var zig_args = std.ArrayList([]const u8).init(self.builder.allocator); + defer zig_args.deinit(); + var it = mem.tokenize(u8, stdout, " \r\n\t"); while (it.next()) |tok| { if (mem.eql(u8, tok, "-I")) { const dir = it.next() orelse return error.PkgConfigInvalidOutput; - self.addIncludePath(dir); + try zig_args.appendSlice(&[_][]const u8{ "-I", dir }); } else if (mem.startsWith(u8, tok, "-I")) { - self.addIncludePath(tok["-I".len..]); + try zig_args.append(tok); } else if (mem.eql(u8, tok, "-L")) { const dir = it.next() orelse return error.PkgConfigInvalidOutput; - self.addLibraryPath(dir); + try zig_args.appendSlice(&[_][]const u8{ "-L", dir }); } else if (mem.startsWith(u8, tok, "-L")) { - self.addLibraryPath(tok["-L".len..]); + try zig_args.append(tok); } else if (mem.eql(u8, tok, "-l")) { const lib = it.next() orelse return error.PkgConfigInvalidOutput; - self.linkSystemLibraryName(lib); + try zig_args.appendSlice(&[_][]const u8{ "-l", lib }); } else if (mem.startsWith(u8, tok, "-l")) { - self.linkSystemLibraryName(tok["-l".len..]); + try zig_args.append(tok); } else if (mem.eql(u8, tok, "-D")) { const macro = it.next() orelse return error.PkgConfigInvalidOutput; - self.defineCMacroRaw(macro); + try zig_args.appendSlice(&[_][]const u8{ "-D", macro }); } else if (mem.startsWith(u8, tok, "-D")) { - self.defineCMacroRaw(tok["-D".len..]); - } else if (mem.eql(u8, tok, "-pthread")) { - self.linkLibC(); + try zig_args.append(tok); } else if (self.builder.verbose) { warn("Ignoring pkg-config flag '{s}'\n", .{tok}); } } + + return zig_args.toOwnedSlice(); } pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void { @@ -2009,21 +2053,13 @@ pub const LibExeObjStep = struct { self.linkLibCpp(); return; } - if (self.linkSystemLibraryPkgConfigOnly(name)) |_| { - // pkg-config worked, so nothing further needed to do. - return; - } else |err| switch (err) { - error.PkgConfigInvalidOutput, - error.PkgConfigCrashed, - error.PkgConfigFailed, - error.PkgConfigNotInstalled, - error.PackageNotFound, - => {}, - else => unreachable, - } - - self.linkSystemLibraryName(name); + self.link_objects.append(.{ + .system_lib = .{ + .name = self.builder.dupe(name), + .use_pkg_config = .yes, + }, + }) catch unreachable; } pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void { @@ -2317,27 +2353,34 @@ pub const LibExeObjStep = struct { var prev_has_extra_flags = false; // Resolve transitive dependencies - for (self.link_objects.items) |link_object| { - switch (link_object) { - .other_step => |other| { - // Inherit dependency on system libraries - for (other.link_objects.items) |other_link_object| { - switch (other_link_object) { - .system_lib => |name| self.linkSystemLibrary(name), - else => continue, - } - } + { + var transitive_dependencies = std.ArrayList(LinkObject).init(builder.allocator); + defer transitive_dependencies.deinit(); - // Inherit dependencies on darwin frameworks - if (!other.isDynamicLibrary()) { - var it = other.frameworks.iterator(); - while (it.next()) |framework| { - self.frameworks.insert(framework.*) catch unreachable; + for (self.link_objects.items) |link_object| { + switch (link_object) { + .other_step => |other| { + // Inherit dependency on system libraries + for (other.link_objects.items) |other_link_object| { + switch (other_link_object) { + .system_lib => try transitive_dependencies.append(other_link_object), + else => continue, + } } - } - }, - else => continue, + + // Inherit dependencies on darwin frameworks + if (!other.isDynamicLibrary()) { + var it = other.frameworks.iterator(); + while (it.next()) |framework| { + self.frameworks.insert(framework.*) catch unreachable; + } + } + }, + else => continue, + } } + + try self.link_objects.appendSlice(transitive_dependencies.items); } for (self.link_objects.items) |link_object| { @@ -2363,8 +2406,35 @@ pub const LibExeObjStep = struct { } }, }, - .system_lib => |name| { - try zig_args.append(builder.fmt("-l{s}", .{name})); + + .system_lib => |system_lib| { + switch (system_lib.use_pkg_config) { + .no => try zig_args.append(builder.fmt("-l{s}", .{system_lib.name})), + .yes, .force => { + if (self.runPkgConfig(system_lib.name)) |args| { + try zig_args.appendSlice(args); + } else |err| switch (err) { + error.PkgConfigInvalidOutput, + error.PkgConfigCrashed, + error.PkgConfigFailed, + error.PkgConfigNotInstalled, + error.PackageNotFound, + => switch (system_lib.use_pkg_config) { + .yes => { + // pkg-config failed, so fall back to linking the library + // by name directly. + try zig_args.append(builder.fmt("-l{s}", .{system_lib.name})); + }, + .force => { + panic("pkg-config failed for library {s}", .{system_lib.name}); + }, + .no => unreachable, + }, + + else => |e| return e, + } + }, + } }, .assembly_file => |asm_file| { From 17daba1806896a2e45a2c1b1969a540f44a64d86 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Mon, 11 Apr 2022 14:39:45 -0700 Subject: [PATCH 1090/2031] std/fs/test.zig: Add test for renaming a dir onto an empty dir Also split the Dir.rename on directories test into 3 tests: - General rename of a directory - Rename of a directory onto an existing empty directory - Rename of a directory onto an existing non-empty directory The only new case is the rename onto an existing empty directory, but splitting the tests this way made them much more understandable. --- lib/std/fs/test.zig | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 69fbe5449f..ca512636ed 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -461,18 +461,44 @@ test "Dir.rename directories" { file = try dir.openFile("test_file", .{}); file.close(); dir.close(); +} - // Try to rename to a non-empty directory now - var target_dir = try tmp_dir.dir.makeOpenPath("non_empty_target_dir", .{}); - file = try target_dir.createFile("filler", .{ .read = true }); +test "Dir.rename directory onto empty dir" { + // TODO: Fix on Windows, see https://github.com/ziglang/zig/issues/6364 + if (builtin.os.tag == .windows) return error.SkipZigTest; + + var tmp_dir = testing.tmpDir(.{}); + defer tmp_dir.cleanup(); + + try tmp_dir.dir.makeDir("test_dir"); + try tmp_dir.dir.makeDir("target_dir"); + try tmp_dir.dir.rename("test_dir", "target_dir"); + + // Ensure the directory was renamed + try testing.expectError(error.FileNotFound, tmp_dir.dir.openDir("test_dir", .{})); + var dir = try tmp_dir.dir.openDir("target_dir", .{}); + dir.close(); +} + +test "Dir.rename directory onto non-empty dir" { + // TODO: Fix on Windows, see https://github.com/ziglang/zig/issues/6364 + if (builtin.os.tag == .windows) return error.SkipZigTest; + + var tmp_dir = testing.tmpDir(.{}); + defer tmp_dir.cleanup(); + + try tmp_dir.dir.makeDir("test_dir"); + + var target_dir = try tmp_dir.dir.makeOpenPath("target_dir", .{}); + var file = try target_dir.createFile("test_file", .{ .read = true }); file.close(); + target_dir.close(); - try testing.expectError(error.PathAlreadyExists, tmp_dir.dir.rename("test_dir_renamed_again", "non_empty_target_dir")); + // Rename should fail with PathAlreadyExists if target_dir is non-empty + try testing.expectError(error.PathAlreadyExists, tmp_dir.dir.rename("test_dir", "target_dir")); // Ensure the directory was not renamed - dir = try tmp_dir.dir.openDir("test_dir_renamed_again", .{}); - file = try dir.openFile("test_file", .{}); - file.close(); + var dir = try tmp_dir.dir.openDir("test_dir", .{}); dir.close(); } From b5d5685a4e3a536999d3e69ce92cc786f052d4ec Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Fri, 8 Apr 2022 18:54:53 -0700 Subject: [PATCH 1091/2031] compiler_rt: Implement floatXiYf/fixXfYi, incl f80 This change: - Adds generic implementation of the float -> integer conversion functions floatXiYf, including support for f80 - Updates the existing implementation of integer -> float conversion fixXiYf to support f16 and f80 - Fixes the handling of the explicit integer bit in `__trunctfxf2` - Combines the test cases for fixXfYi/floatXiYf into a single file - Renames `fmodl` to `fmodq`, since it operates on 128-bit floats The new implementation for floatXiYf has been benchmarked, and generally provides equal or better performance versus the current implementations: Throughput (MiB/s) - Before | u32 | i32 | u64 | i64 | u128 | i128 | -----|----------|----------|----------|----------|----------|----------| f16 | none | none | none | none | none | none | f32 | 2231.67 | 2001.19 | 1745.66 | 1405.77 | 2173.99 | 1874.63 | f64 | 1407.17 | 1055.83 | 2911.68 | 2437.21 | 1676.05 | 1476.67 | f80 | none | none | none | none | none | none | f128 | 327.56 | 321.25 | 645.92 | 654.52 | 1153.56 | 1096.27 | Throughput (MiB/s) - After | u32 | i32 | u64 | i64 | u128 | i128 | -----|----------|----------|----------|----------|----------|----------| f16 | 1407.61 | 1637.25 | 3555.03 | 2594.56 | 3680.60 | 3063.34 | f32 | 2101.36 | 2122.62 | 3225.46 | 3123.86 | 2860.05 | 1985.21 | f64 | 1395.57 | 1314.87 | 2409.24 | 2196.30 | 2384.95 | 1908.15 | f80 | 475.53 | 457.92 | 884.50 | 812.12 | 1475.27 | 1382.16 | f128 | 359.60 | 350.91 | 723.08 | 706.80 | 1296.42 | 1198.87 | --- CMakeLists.txt | 37 +- lib/std/special/compiler_rt.zig | 242 +++-- lib/std/special/compiler_rt/fixXfYi.zig | 224 +++++ lib/std/special/compiler_rt/fixXfYi_test.zig | 948 ++++++++++++++++++ lib/std/special/compiler_rt/fixdfdi.zig | 16 - lib/std/special/compiler_rt/fixdfdi_test.zig | 62 -- lib/std/special/compiler_rt/fixdfsi.zig | 16 - lib/std/special/compiler_rt/fixdfsi_test.zig | 70 -- lib/std/special/compiler_rt/fixdfti.zig | 11 - lib/std/special/compiler_rt/fixdfti_test.zig | 62 -- lib/std/special/compiler_rt/fixint.zig | 75 -- lib/std/special/compiler_rt/fixsfdi.zig | 16 - lib/std/special/compiler_rt/fixsfdi_test.zig | 64 -- lib/std/special/compiler_rt/fixsfsi.zig | 16 - lib/std/special/compiler_rt/fixsfsi_test.zig | 72 -- lib/std/special/compiler_rt/fixsfti.zig | 11 - lib/std/special/compiler_rt/fixsfti_test.zig | 80 -- lib/std/special/compiler_rt/fixtfdi.zig | 11 - lib/std/special/compiler_rt/fixtfdi_test.zig | 72 -- lib/std/special/compiler_rt/fixtfsi.zig | 11 - lib/std/special/compiler_rt/fixtfsi_test.zig | 72 -- lib/std/special/compiler_rt/fixtfti.zig | 11 - lib/std/special/compiler_rt/fixtfti_test.zig | 62 -- lib/std/special/compiler_rt/fixuint.zig | 50 - lib/std/special/compiler_rt/fixunsdfdi.zig | 16 - .../special/compiler_rt/fixunsdfdi_test.zig | 39 - lib/std/special/compiler_rt/fixunsdfsi.zig | 16 - .../special/compiler_rt/fixunsdfsi_test.zig | 39 - lib/std/special/compiler_rt/fixunsdfti.zig | 11 - .../special/compiler_rt/fixunsdfti_test.zig | 46 - lib/std/special/compiler_rt/fixunssfdi.zig | 16 - .../special/compiler_rt/fixunssfdi_test.zig | 35 - lib/std/special/compiler_rt/fixunssfsi.zig | 16 - .../special/compiler_rt/fixunssfsi_test.zig | 36 - lib/std/special/compiler_rt/fixunssfti.zig | 11 - .../special/compiler_rt/fixunssfti_test.zig | 41 - lib/std/special/compiler_rt/fixunstfdi.zig | 11 - .../special/compiler_rt/fixunstfdi_test.zig | 49 - lib/std/special/compiler_rt/fixunstfsi.zig | 11 - .../special/compiler_rt/fixunstfsi_test.zig | 22 - lib/std/special/compiler_rt/fixunstfti.zig | 11 - .../special/compiler_rt/fixunstfti_test.zig | 32 - lib/std/special/compiler_rt/floatXiYf.zig | 222 ++++ .../special/compiler_rt/floatXiYf_test.zig | 831 +++++++++++++++ lib/std/special/compiler_rt/floatXisf.zig | 90 -- lib/std/special/compiler_rt/floatdidf.zig | 27 - .../special/compiler_rt/floatdidf_test.zig | 53 - .../special/compiler_rt/floatdisf_test.zig | 32 - lib/std/special/compiler_rt/floatditf.zig | 38 - .../special/compiler_rt/floatditf_test.zig | 26 - .../special/compiler_rt/floatfmodl_test.zig | 46 - .../{floatfmodl.zig => floatfmodq.zig} | 12 +- .../special/compiler_rt/floatfmodq_test.zig | 46 + lib/std/special/compiler_rt/floatsiXf.zig | 120 --- lib/std/special/compiler_rt/floattidf.zig | 71 -- .../special/compiler_rt/floattidf_test.zig | 84 -- .../special/compiler_rt/floattisf_test.zig | 60 -- lib/std/special/compiler_rt/floattitf.zig | 71 -- .../special/compiler_rt/floattitf_test.zig | 96 -- lib/std/special/compiler_rt/floatundidf.zig | 29 - .../special/compiler_rt/floatundidf_test.zig | 50 - lib/std/special/compiler_rt/floatundisf.zig | 94 -- lib/std/special/compiler_rt/floatunditf.zig | 28 - .../special/compiler_rt/floatunditf_test.zig | 32 - lib/std/special/compiler_rt/floatunsidf.zig | 41 - lib/std/special/compiler_rt/floatunsisf.zig | 61 -- lib/std/special/compiler_rt/floatunsitf.zig | 29 - .../special/compiler_rt/floatunsitf_test.zig | 28 - lib/std/special/compiler_rt/floatuntidf.zig | 62 -- .../special/compiler_rt/floatuntidf_test.zig | 81 -- lib/std/special/compiler_rt/floatuntisf.zig | 61 -- .../special/compiler_rt/floatuntisf_test.zig | 72 -- lib/std/special/compiler_rt/floatuntitf.zig | 62 -- .../special/compiler_rt/floatuntitf_test.zig | 99 -- lib/std/special/compiler_rt/sparc.zig | 16 +- lib/std/special/compiler_rt/trunc_f80.zig | 34 +- 76 files changed, 2458 insertions(+), 3115 deletions(-) create mode 100644 lib/std/special/compiler_rt/fixXfYi.zig create mode 100644 lib/std/special/compiler_rt/fixXfYi_test.zig delete mode 100644 lib/std/special/compiler_rt/fixdfdi.zig delete mode 100644 lib/std/special/compiler_rt/fixdfdi_test.zig delete mode 100644 lib/std/special/compiler_rt/fixdfsi.zig delete mode 100644 lib/std/special/compiler_rt/fixdfsi_test.zig delete mode 100644 lib/std/special/compiler_rt/fixdfti.zig delete mode 100644 lib/std/special/compiler_rt/fixdfti_test.zig delete mode 100644 lib/std/special/compiler_rt/fixint.zig delete mode 100644 lib/std/special/compiler_rt/fixsfdi.zig delete mode 100644 lib/std/special/compiler_rt/fixsfdi_test.zig delete mode 100644 lib/std/special/compiler_rt/fixsfsi.zig delete mode 100644 lib/std/special/compiler_rt/fixsfsi_test.zig delete mode 100644 lib/std/special/compiler_rt/fixsfti.zig delete mode 100644 lib/std/special/compiler_rt/fixsfti_test.zig delete mode 100644 lib/std/special/compiler_rt/fixtfdi.zig delete mode 100644 lib/std/special/compiler_rt/fixtfdi_test.zig delete mode 100644 lib/std/special/compiler_rt/fixtfsi.zig delete mode 100644 lib/std/special/compiler_rt/fixtfsi_test.zig delete mode 100644 lib/std/special/compiler_rt/fixtfti.zig delete mode 100644 lib/std/special/compiler_rt/fixtfti_test.zig delete mode 100644 lib/std/special/compiler_rt/fixuint.zig delete mode 100644 lib/std/special/compiler_rt/fixunsdfdi.zig delete mode 100644 lib/std/special/compiler_rt/fixunsdfdi_test.zig delete mode 100644 lib/std/special/compiler_rt/fixunsdfsi.zig delete mode 100644 lib/std/special/compiler_rt/fixunsdfsi_test.zig delete mode 100644 lib/std/special/compiler_rt/fixunsdfti.zig delete mode 100644 lib/std/special/compiler_rt/fixunsdfti_test.zig delete mode 100644 lib/std/special/compiler_rt/fixunssfdi.zig delete mode 100644 lib/std/special/compiler_rt/fixunssfdi_test.zig delete mode 100644 lib/std/special/compiler_rt/fixunssfsi.zig delete mode 100644 lib/std/special/compiler_rt/fixunssfsi_test.zig delete mode 100644 lib/std/special/compiler_rt/fixunssfti.zig delete mode 100644 lib/std/special/compiler_rt/fixunssfti_test.zig delete mode 100644 lib/std/special/compiler_rt/fixunstfdi.zig delete mode 100644 lib/std/special/compiler_rt/fixunstfdi_test.zig delete mode 100644 lib/std/special/compiler_rt/fixunstfsi.zig delete mode 100644 lib/std/special/compiler_rt/fixunstfsi_test.zig delete mode 100644 lib/std/special/compiler_rt/fixunstfti.zig delete mode 100644 lib/std/special/compiler_rt/fixunstfti_test.zig create mode 100644 lib/std/special/compiler_rt/floatXiYf.zig create mode 100644 lib/std/special/compiler_rt/floatXiYf_test.zig delete mode 100644 lib/std/special/compiler_rt/floatXisf.zig delete mode 100644 lib/std/special/compiler_rt/floatdidf.zig delete mode 100644 lib/std/special/compiler_rt/floatdidf_test.zig delete mode 100644 lib/std/special/compiler_rt/floatdisf_test.zig delete mode 100644 lib/std/special/compiler_rt/floatditf.zig delete mode 100644 lib/std/special/compiler_rt/floatditf_test.zig delete mode 100644 lib/std/special/compiler_rt/floatfmodl_test.zig rename lib/std/special/compiler_rt/{floatfmodl.zig => floatfmodq.zig} (93%) create mode 100644 lib/std/special/compiler_rt/floatfmodq_test.zig delete mode 100644 lib/std/special/compiler_rt/floatsiXf.zig delete mode 100644 lib/std/special/compiler_rt/floattidf.zig delete mode 100644 lib/std/special/compiler_rt/floattidf_test.zig delete mode 100644 lib/std/special/compiler_rt/floattisf_test.zig delete mode 100644 lib/std/special/compiler_rt/floattitf.zig delete mode 100644 lib/std/special/compiler_rt/floattitf_test.zig delete mode 100644 lib/std/special/compiler_rt/floatundidf.zig delete mode 100644 lib/std/special/compiler_rt/floatundidf_test.zig delete mode 100644 lib/std/special/compiler_rt/floatundisf.zig delete mode 100644 lib/std/special/compiler_rt/floatunditf.zig delete mode 100644 lib/std/special/compiler_rt/floatunditf_test.zig delete mode 100644 lib/std/special/compiler_rt/floatunsidf.zig delete mode 100644 lib/std/special/compiler_rt/floatunsisf.zig delete mode 100644 lib/std/special/compiler_rt/floatunsitf.zig delete mode 100644 lib/std/special/compiler_rt/floatunsitf_test.zig delete mode 100644 lib/std/special/compiler_rt/floatuntidf.zig delete mode 100644 lib/std/special/compiler_rt/floatuntidf_test.zig delete mode 100644 lib/std/special/compiler_rt/floatuntisf.zig delete mode 100644 lib/std/special/compiler_rt/floatuntisf_test.zig delete mode 100644 lib/std/special/compiler_rt/floatuntitf.zig delete mode 100644 lib/std/special/compiler_rt/floatuntitf_test.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 22e17d84c6..81b96d67ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -493,41 +493,8 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divtf3.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divti3.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/extendXfYf2.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixdfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixdfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixdfti.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixint.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixsfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixsfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixsfti.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixtfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixtfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixtfti.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixuint.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixunsdfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixunsdfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixunsdfti.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixunssfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixunssfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixunssfti.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixunstfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixunstfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixunstfti.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatXisf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatdidf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatditf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatsiXf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floattidf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floattitf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatundidf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatundisf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatunditf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatunsidf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatunsisf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatunsitf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatuntidf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatuntisf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatuntitf.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixXfYi.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatXiYf.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/int.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/modti3.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/mulXf3.zig" diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index a017266304..9ccf5619e1 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -291,47 +291,140 @@ comptime { const __bswapti2 = @import("compiler_rt/bswap.zig").__bswapti2; @export(__bswapti2, .{ .name = "__bswapti2", .linkage = linkage }); - // Integral / floating point conversion (part 1/2) - const __floatsidf = @import("compiler_rt/floatsiXf.zig").__floatsidf; - @export(__floatsidf, .{ .name = "__floatsidf", .linkage = linkage }); - const __floatsisf = @import("compiler_rt/floatsiXf.zig").__floatsisf; + // Integral -> Float Conversion + + // Conversion to f32 + const __floatsisf = @import("compiler_rt/floatXiYf.zig").__floatsisf; @export(__floatsisf, .{ .name = "__floatsisf", .linkage = linkage }); - const __floatdidf = @import("compiler_rt/floatdidf.zig").__floatdidf; - @export(__floatdidf, .{ .name = "__floatdidf", .linkage = linkage }); - const __floatsitf = @import("compiler_rt/floatsiXf.zig").__floatsitf; - @export(__floatsitf, .{ .name = "__floatsitf", .linkage = linkage }); - - const __floatunsisf = @import("compiler_rt/floatunsisf.zig").__floatunsisf; + const __floatunsisf = @import("compiler_rt/floatXiYf.zig").__floatunsisf; @export(__floatunsisf, .{ .name = "__floatunsisf", .linkage = linkage }); - const __floatundisf = @import("compiler_rt/floatundisf.zig").__floatundisf; - @export(__floatundisf, .{ .name = "__floatundisf", .linkage = linkage }); - const __floatunsidf = @import("compiler_rt/floatunsidf.zig").__floatunsidf; - @export(__floatunsidf, .{ .name = "__floatunsidf", .linkage = linkage }); - const __floatundidf = @import("compiler_rt/floatundidf.zig").__floatundidf; - @export(__floatundidf, .{ .name = "__floatundidf", .linkage = linkage }); - const __floatditf = @import("compiler_rt/floatditf.zig").__floatditf; - @export(__floatditf, .{ .name = "__floatditf", .linkage = linkage }); - const __floattitf = @import("compiler_rt/floattitf.zig").__floattitf; - @export(__floattitf, .{ .name = "__floattitf", .linkage = linkage }); - const __floattidf = @import("compiler_rt/floattidf.zig").__floattidf; - @export(__floattidf, .{ .name = "__floattidf", .linkage = linkage }); - const __floattisf = @import("compiler_rt/floatXisf.zig").__floattisf; - @export(__floattisf, .{ .name = "__floattisf", .linkage = linkage }); - const __floatdisf = @import("compiler_rt/floatXisf.zig").__floatdisf; + const __floatundisf = @import("compiler_rt/floatXiYf.zig").__floatundisf; + @export(__floatundisf, .{ .name = "__floatundisf", .linkage = linkage }); + const __floatdisf = @import("compiler_rt/floatXiYf.zig").__floatdisf; @export(__floatdisf, .{ .name = "__floatdisf", .linkage = linkage }); - const __floatunditf = @import("compiler_rt/floatunditf.zig").__floatunditf; - @export(__floatunditf, .{ .name = "__floatunditf", .linkage = linkage }); - const __floatunsitf = @import("compiler_rt/floatunsitf.zig").__floatunsitf; + const __floattisf = @import("compiler_rt/floatXiYf.zig").__floattisf; + @export(__floattisf, .{ .name = "__floattisf", .linkage = linkage }); + const __floatuntisf = @import("compiler_rt/floatXiYf.zig").__floatuntisf; + @export(__floatuntisf, .{ .name = "__floatuntisf", .linkage = linkage }); + + // Conversion to f64 + const __floatsidf = @import("compiler_rt/floatXiYf.zig").__floatsidf; + @export(__floatsidf, .{ .name = "__floatsidf", .linkage = linkage }); + const __floatunsidf = @import("compiler_rt/floatXiYf.zig").__floatunsidf; + @export(__floatunsidf, .{ .name = "__floatunsidf", .linkage = linkage }); + + const __floatdidf = @import("compiler_rt/floatXiYf.zig").__floatdidf; + @export(__floatdidf, .{ .name = "__floatdidf", .linkage = linkage }); + const __floatundidf = @import("compiler_rt/floatXiYf.zig").__floatundidf; + @export(__floatundidf, .{ .name = "__floatundidf", .linkage = linkage }); + + const __floattidf = @import("compiler_rt/floatXiYf.zig").__floattidf; + @export(__floattidf, .{ .name = "__floattidf", .linkage = linkage }); + const __floatuntidf = @import("compiler_rt/floatXiYf.zig").__floatuntidf; + @export(__floatuntidf, .{ .name = "__floatuntidf", .linkage = linkage }); + + // Conversion to f80 + const __floatsixf = @import("compiler_rt/floatXiYf.zig").__floatsixf; + @export(__floatsixf, .{ .name = "__floatsixf", .linkage = linkage }); + const __floatunsixf = @import("compiler_rt/floatXiYf.zig").__floatunsixf; + @export(__floatunsixf, .{ .name = "__floatunsixf", .linkage = linkage }); + + const __floatdixf = @import("compiler_rt/floatXiYf.zig").__floatdixf; + @export(__floatdixf, .{ .name = "__floatdixf", .linkage = linkage }); + const __floatundixf = @import("compiler_rt/floatXiYf.zig").__floatundixf; + @export(__floatundixf, .{ .name = "__floatundixf", .linkage = linkage }); + + const __floattixf = @import("compiler_rt/floatXiYf.zig").__floattixf; + @export(__floattixf, .{ .name = "__floattixf", .linkage = linkage }); + const __floatuntixf = @import("compiler_rt/floatXiYf.zig").__floatuntixf; + @export(__floatuntixf, .{ .name = "__floatuntixf", .linkage = linkage }); + + // Conversion to f128 + const __floatsitf = @import("compiler_rt/floatXiYf.zig").__floatsitf; + @export(__floatsitf, .{ .name = "__floatsitf", .linkage = linkage }); + const __floatunsitf = @import("compiler_rt/floatXiYf.zig").__floatunsitf; @export(__floatunsitf, .{ .name = "__floatunsitf", .linkage = linkage }); - const __floatuntitf = @import("compiler_rt/floatuntitf.zig").__floatuntitf; + const __floatditf = @import("compiler_rt/floatXiYf.zig").__floatditf; + @export(__floatditf, .{ .name = "__floatditf", .linkage = linkage }); + const __floatunditf = @import("compiler_rt/floatXiYf.zig").__floatunditf; + @export(__floatunditf, .{ .name = "__floatunditf", .linkage = linkage }); + + const __floattitf = @import("compiler_rt/floatXiYf.zig").__floattitf; + @export(__floattitf, .{ .name = "__floattitf", .linkage = linkage }); + const __floatuntitf = @import("compiler_rt/floatXiYf.zig").__floatuntitf; @export(__floatuntitf, .{ .name = "__floatuntitf", .linkage = linkage }); - const __floatuntidf = @import("compiler_rt/floatuntidf.zig").__floatuntidf; - @export(__floatuntidf, .{ .name = "__floatuntidf", .linkage = linkage }); - const __floatuntisf = @import("compiler_rt/floatuntisf.zig").__floatuntisf; - @export(__floatuntisf, .{ .name = "__floatuntisf", .linkage = linkage }); + + // Float -> Integral Conversion + + // Conversion from f32 + const __fixsfsi = @import("compiler_rt/fixXfYi.zig").__fixsfsi; + @export(__fixsfsi, .{ .name = "__fixsfsi", .linkage = linkage }); + const __fixunssfsi = @import("compiler_rt/fixXfYi.zig").__fixunssfsi; + @export(__fixunssfsi, .{ .name = "__fixunssfsi", .linkage = linkage }); + + const __fixsfdi = @import("compiler_rt/fixXfYi.zig").__fixsfdi; + @export(__fixsfdi, .{ .name = "__fixsfdi", .linkage = linkage }); + const __fixunssfdi = @import("compiler_rt/fixXfYi.zig").__fixunssfdi; + @export(__fixunssfdi, .{ .name = "__fixunssfdi", .linkage = linkage }); + + const __fixsfti = @import("compiler_rt/fixXfYi.zig").__fixsfti; + @export(__fixsfti, .{ .name = "__fixsfti", .linkage = linkage }); + const __fixunssfti = @import("compiler_rt/fixXfYi.zig").__fixunssfti; + @export(__fixunssfti, .{ .name = "__fixunssfti", .linkage = linkage }); + + // Conversion from f64 + const __fixdfsi = @import("compiler_rt/fixXfYi.zig").__fixdfsi; + @export(__fixdfsi, .{ .name = "__fixdfsi", .linkage = linkage }); + const __fixunsdfsi = @import("compiler_rt/fixXfYi.zig").__fixunsdfsi; + @export(__fixunsdfsi, .{ .name = "__fixunsdfsi", .linkage = linkage }); + + const __fixdfdi = @import("compiler_rt/fixXfYi.zig").__fixdfdi; + @export(__fixdfdi, .{ .name = "__fixdfdi", .linkage = linkage }); + const __fixunsdfdi = @import("compiler_rt/fixXfYi.zig").__fixunsdfdi; + @export(__fixunsdfdi, .{ .name = "__fixunsdfdi", .linkage = linkage }); + + const __fixdfti = @import("compiler_rt/fixXfYi.zig").__fixdfti; + @export(__fixdfti, .{ .name = "__fixdfti", .linkage = linkage }); + const __fixunsdfti = @import("compiler_rt/fixXfYi.zig").__fixunsdfti; + @export(__fixunsdfti, .{ .name = "__fixunsdfti", .linkage = linkage }); + + // Conversion from f80 + const __fixxfsi = @import("compiler_rt/fixXfYi.zig").__fixxfsi; + @export(__fixxfsi, .{ .name = "__fixxfsi", .linkage = linkage }); + const __fixunsxfsi = @import("compiler_rt/fixXfYi.zig").__fixunsxfsi; + @export(__fixunsxfsi, .{ .name = "__fixunsxfsi", .linkage = linkage }); + + const __fixxfdi = @import("compiler_rt/fixXfYi.zig").__fixxfdi; + @export(__fixxfdi, .{ .name = "__fixxfdi", .linkage = linkage }); + const __fixunsxfdi = @import("compiler_rt/fixXfYi.zig").__fixunsxfdi; + @export(__fixunsxfdi, .{ .name = "__fixunsxfdi", .linkage = linkage }); + + const __fixxfti = @import("compiler_rt/fixXfYi.zig").__fixxfti; + @export(__fixxfti, .{ .name = "__fixxfti", .linkage = linkage }); + const __fixunsxfti = @import("compiler_rt/fixXfYi.zig").__fixunsxfti; + @export(__fixunsxfti, .{ .name = "__fixunsxfti", .linkage = linkage }); + + // Conversion from f128 + const __fixtfsi = @import("compiler_rt/fixXfYi.zig").__fixtfsi; + @export(__fixtfsi, .{ .name = "__fixtfsi", .linkage = linkage }); + const __fixunstfsi = @import("compiler_rt/fixXfYi.zig").__fixunstfsi; + @export(__fixunstfsi, .{ .name = "__fixunstfsi", .linkage = linkage }); + + const __fixtfdi = @import("compiler_rt/fixXfYi.zig").__fixtfdi; + @export(__fixtfdi, .{ .name = "__fixtfdi", .linkage = linkage }); + const __fixunstfdi = @import("compiler_rt/fixXfYi.zig").__fixunstfdi; + @export(__fixunstfdi, .{ .name = "__fixunstfdi", .linkage = linkage }); + + const __fixtfti = @import("compiler_rt/fixXfYi.zig").__fixtfti; + @export(__fixtfti, .{ .name = "__fixtfti", .linkage = linkage }); + const __fixunstfti = @import("compiler_rt/fixXfYi.zig").__fixunstfti; + @export(__fixunstfti, .{ .name = "__fixunstfti", .linkage = linkage }); + + const __udivmoddi4 = @import("compiler_rt/int.zig").__udivmoddi4; + @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = linkage }); const __truncsfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncsfhf2; @export(__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage }); @@ -341,50 +434,6 @@ comptime { const __extendsfdf2 = @import("compiler_rt/extendXfYf2.zig").__extendsfdf2; @export(__extendsfdf2, .{ .name = "__extendsfdf2", .linkage = linkage }); - // Integral / floating point conversion (part 2/2) - const __fixunssfsi = @import("compiler_rt/fixunssfsi.zig").__fixunssfsi; - @export(__fixunssfsi, .{ .name = "__fixunssfsi", .linkage = linkage }); - const __fixunssfdi = @import("compiler_rt/fixunssfdi.zig").__fixunssfdi; - @export(__fixunssfdi, .{ .name = "__fixunssfdi", .linkage = linkage }); - const __fixunssfti = @import("compiler_rt/fixunssfti.zig").__fixunssfti; - @export(__fixunssfti, .{ .name = "__fixunssfti", .linkage = linkage }); - - const __fixunsdfsi = @import("compiler_rt/fixunsdfsi.zig").__fixunsdfsi; - @export(__fixunsdfsi, .{ .name = "__fixunsdfsi", .linkage = linkage }); - const __fixunsdfdi = @import("compiler_rt/fixunsdfdi.zig").__fixunsdfdi; - @export(__fixunsdfdi, .{ .name = "__fixunsdfdi", .linkage = linkage }); - const __fixunsdfti = @import("compiler_rt/fixunsdfti.zig").__fixunsdfti; - @export(__fixunsdfti, .{ .name = "__fixunsdfti", .linkage = linkage }); - - const __fixunstfsi = @import("compiler_rt/fixunstfsi.zig").__fixunstfsi; - @export(__fixunstfsi, .{ .name = "__fixunstfsi", .linkage = linkage }); - const __fixunstfdi = @import("compiler_rt/fixunstfdi.zig").__fixunstfdi; - @export(__fixunstfdi, .{ .name = "__fixunstfdi", .linkage = linkage }); - const __fixunstfti = @import("compiler_rt/fixunstfti.zig").__fixunstfti; - @export(__fixunstfti, .{ .name = "__fixunstfti", .linkage = linkage }); - - const __fixdfdi = @import("compiler_rt/fixdfdi.zig").__fixdfdi; - @export(__fixdfdi, .{ .name = "__fixdfdi", .linkage = linkage }); - const __fixdfsi = @import("compiler_rt/fixdfsi.zig").__fixdfsi; - @export(__fixdfsi, .{ .name = "__fixdfsi", .linkage = linkage }); - const __fixdfti = @import("compiler_rt/fixdfti.zig").__fixdfti; - @export(__fixdfti, .{ .name = "__fixdfti", .linkage = linkage }); - const __fixsfdi = @import("compiler_rt/fixsfdi.zig").__fixsfdi; - @export(__fixsfdi, .{ .name = "__fixsfdi", .linkage = linkage }); - const __fixsfsi = @import("compiler_rt/fixsfsi.zig").__fixsfsi; - @export(__fixsfsi, .{ .name = "__fixsfsi", .linkage = linkage }); - const __fixsfti = @import("compiler_rt/fixsfti.zig").__fixsfti; - @export(__fixsfti, .{ .name = "__fixsfti", .linkage = linkage }); - const __fixtfdi = @import("compiler_rt/fixtfdi.zig").__fixtfdi; - @export(__fixtfdi, .{ .name = "__fixtfdi", .linkage = linkage }); - const __fixtfsi = @import("compiler_rt/fixtfsi.zig").__fixtfsi; - @export(__fixtfsi, .{ .name = "__fixtfsi", .linkage = linkage }); - const __fixtfti = @import("compiler_rt/fixtfti.zig").__fixtfti; - @export(__fixtfti, .{ .name = "__fixtfti", .linkage = linkage }); - - const __udivmoddi4 = @import("compiler_rt/int.zig").__udivmoddi4; - @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = linkage }); - if (is_darwin) { const __isPlatformVersionAtLeast = @import("compiler_rt/os_version_check.zig").__isPlatformVersionAtLeast; @export(__isPlatformVersionAtLeast, .{ .name = "__isPlatformVersionAtLeast", .linkage = linkage }); @@ -553,19 +602,19 @@ comptime { const __aeabi_f2d = @import("compiler_rt/extendXfYf2.zig").__aeabi_f2d; @export(__aeabi_f2d, .{ .name = "__aeabi_f2d", .linkage = linkage }); - const __aeabi_i2d = @import("compiler_rt/floatsiXf.zig").__aeabi_i2d; + const __aeabi_i2d = @import("compiler_rt/floatXiYf.zig").__aeabi_i2d; @export(__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = linkage }); - const __aeabi_l2d = @import("compiler_rt/floatdidf.zig").__aeabi_l2d; + const __aeabi_l2d = @import("compiler_rt/floatXiYf.zig").__aeabi_l2d; @export(__aeabi_l2d, .{ .name = "__aeabi_l2d", .linkage = linkage }); - const __aeabi_l2f = @import("compiler_rt/floatXisf.zig").__aeabi_l2f; + const __aeabi_l2f = @import("compiler_rt/floatXiYf.zig").__aeabi_l2f; @export(__aeabi_l2f, .{ .name = "__aeabi_l2f", .linkage = linkage }); - const __aeabi_ui2d = @import("compiler_rt/floatunsidf.zig").__aeabi_ui2d; + const __aeabi_ui2d = @import("compiler_rt/floatXiYf.zig").__aeabi_ui2d; @export(__aeabi_ui2d, .{ .name = "__aeabi_ui2d", .linkage = linkage }); - const __aeabi_ul2d = @import("compiler_rt/floatundidf.zig").__aeabi_ul2d; + const __aeabi_ul2d = @import("compiler_rt/floatXiYf.zig").__aeabi_ul2d; @export(__aeabi_ul2d, .{ .name = "__aeabi_ul2d", .linkage = linkage }); - const __aeabi_ui2f = @import("compiler_rt/floatunsisf.zig").__aeabi_ui2f; + const __aeabi_ui2f = @import("compiler_rt/floatXiYf.zig").__aeabi_ui2f; @export(__aeabi_ui2f, .{ .name = "__aeabi_ui2f", .linkage = linkage }); - const __aeabi_ul2f = @import("compiler_rt/floatundisf.zig").__aeabi_ul2f; + const __aeabi_ul2f = @import("compiler_rt/floatXiYf.zig").__aeabi_ul2f; @export(__aeabi_ul2f, .{ .name = "__aeabi_ul2f", .linkage = linkage }); const __aeabi_fneg = @import("compiler_rt/negXf2.zig").__aeabi_fneg; @@ -581,17 +630,17 @@ comptime { const __aeabi_d2h = @import("compiler_rt/truncXfYf2.zig").__aeabi_d2h; @export(__aeabi_d2h, .{ .name = "__aeabi_d2h", .linkage = linkage }); - const __aeabi_f2ulz = @import("compiler_rt/fixunssfdi.zig").__aeabi_f2ulz; + const __aeabi_f2ulz = @import("compiler_rt/fixXfYi.zig").__aeabi_f2ulz; @export(__aeabi_f2ulz, .{ .name = "__aeabi_f2ulz", .linkage = linkage }); - const __aeabi_d2ulz = @import("compiler_rt/fixunsdfdi.zig").__aeabi_d2ulz; + const __aeabi_d2ulz = @import("compiler_rt/fixXfYi.zig").__aeabi_d2ulz; @export(__aeabi_d2ulz, .{ .name = "__aeabi_d2ulz", .linkage = linkage }); - const __aeabi_f2lz = @import("compiler_rt/fixsfdi.zig").__aeabi_f2lz; + const __aeabi_f2lz = @import("compiler_rt/fixXfYi.zig").__aeabi_f2lz; @export(__aeabi_f2lz, .{ .name = "__aeabi_f2lz", .linkage = linkage }); - const __aeabi_d2lz = @import("compiler_rt/fixdfdi.zig").__aeabi_d2lz; + const __aeabi_d2lz = @import("compiler_rt/fixXfYi.zig").__aeabi_d2lz; @export(__aeabi_d2lz, .{ .name = "__aeabi_d2lz", .linkage = linkage }); - const __aeabi_d2uiz = @import("compiler_rt/fixunsdfsi.zig").__aeabi_d2uiz; + const __aeabi_d2uiz = @import("compiler_rt/fixXfYi.zig").__aeabi_d2uiz; @export(__aeabi_d2uiz, .{ .name = "__aeabi_d2uiz", .linkage = linkage }); const __aeabi_h2f = @import("compiler_rt/extendXfYf2.zig").__aeabi_h2f; @@ -599,7 +648,7 @@ comptime { const __aeabi_f2h = @import("compiler_rt/truncXfYf2.zig").__aeabi_f2h; @export(__aeabi_f2h, .{ .name = "__aeabi_f2h", .linkage = linkage }); - const __aeabi_i2f = @import("compiler_rt/floatsiXf.zig").__aeabi_i2f; + const __aeabi_i2f = @import("compiler_rt/floatXiYf.zig").__aeabi_i2f; @export(__aeabi_i2f, .{ .name = "__aeabi_i2f", .linkage = linkage }); const __aeabi_d2f = @import("compiler_rt/truncXfYf2.zig").__aeabi_d2f; @export(__aeabi_d2f, .{ .name = "__aeabi_d2f", .linkage = linkage }); @@ -613,12 +662,12 @@ comptime { const __aeabi_dsub = @import("compiler_rt/addXf3.zig").__aeabi_dsub; @export(__aeabi_dsub, .{ .name = "__aeabi_dsub", .linkage = linkage }); - const __aeabi_f2uiz = @import("compiler_rt/fixunssfsi.zig").__aeabi_f2uiz; + const __aeabi_f2uiz = @import("compiler_rt/fixXfYi.zig").__aeabi_f2uiz; @export(__aeabi_f2uiz, .{ .name = "__aeabi_f2uiz", .linkage = linkage }); - const __aeabi_f2iz = @import("compiler_rt/fixsfsi.zig").__aeabi_f2iz; + const __aeabi_f2iz = @import("compiler_rt/fixXfYi.zig").__aeabi_f2iz; @export(__aeabi_f2iz, .{ .name = "__aeabi_f2iz", .linkage = linkage }); - const __aeabi_d2iz = @import("compiler_rt/fixdfsi.zig").__aeabi_d2iz; + const __aeabi_d2iz = @import("compiler_rt/fixXfYi.zig").__aeabi_d2iz; @export(__aeabi_d2iz, .{ .name = "__aeabi_d2iz", .linkage = linkage }); const __aeabi_fdiv = @import("compiler_rt/divsf3.zig").__aeabi_fdiv; @@ -672,9 +721,12 @@ comptime { @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage }); } - const fmodl = @import("compiler_rt/floatfmodl.zig").fmodl; + const fmodq = @import("compiler_rt/floatfmodq.zig").fmodq; if (!is_test) { - @export(fmodl, .{ .name = "fmodl", .linkage = linkage }); + @export(fmodq, .{ .name = "fmodq", .linkage = linkage }); + if (long_double_is_f128) { + @export(fmodq, .{ .name = "fmodl", .linkage = linkage }); + } @export(floorf, .{ .name = "floorf", .linkage = linkage }); @export(floor, .{ .name = "floor", .linkage = linkage }); diff --git a/lib/std/special/compiler_rt/fixXfYi.zig b/lib/std/special/compiler_rt/fixXfYi.zig new file mode 100644 index 0000000000..9e46f74a53 --- /dev/null +++ b/lib/std/special/compiler_rt/fixXfYi.zig @@ -0,0 +1,224 @@ +const std = @import("std"); +const math = std.math; +const Log2Int = math.Log2Int; +const is_test = @import("builtin").is_test; + +pub inline fn fixXfYi(comptime I: type, a: anytype) I { + @setRuntimeSafety(is_test); + + const F = @TypeOf(a); + const float_bits = @typeInfo(F).Float.bits; + const int_bits = @typeInfo(I).Int.bits; + const rep_t = std.meta.Int(.unsigned, float_bits); + const sig_bits = math.floatMantissaBits(F); + const exp_bits = math.floatExponentBits(F); + const fractional_sig_bits = math.floatMantissaDigits(F) - 1; + + const implicit_bit = if (F != f80) (@as(rep_t, 1) << sig_bits) else 0; + const max_exp = (1 << (exp_bits - 1)); + const exp_bias = max_exp - 1; + const sig_mask = (@as(rep_t, 1) << sig_bits) - 1; + + // Break a into sign, exponent, significand + const a_rep: rep_t = @bitCast(rep_t, a); + const negative = (a_rep >> (float_bits - 1)) != 0; + const exponent = @intCast(i32, (a_rep << 1) >> (sig_bits + 1)) - exp_bias; + const significand: rep_t = (a_rep & sig_mask) | implicit_bit; + + // If the exponent is negative, the result rounds to zero. + if (exponent < 0) return 0; + + // If the value is too large for the integer type, saturate. + switch (@typeInfo(I).Int.signedness) { + .unsigned => { + if (negative) return 0; + if (@intCast(c_uint, exponent) >= @minimum(int_bits, max_exp)) return math.maxInt(I); + }, + .signed => if (@intCast(c_uint, exponent) >= @minimum(int_bits - 1, max_exp)) { + return if (negative) math.minInt(I) else math.maxInt(I); + }, + } + + // If 0 <= exponent < sig_bits, right shift to get the result. + // Otherwise, shift left. + var result: I = undefined; + if (exponent < fractional_sig_bits) { + result = @intCast(I, significand >> @intCast(Log2Int(rep_t), fractional_sig_bits - exponent)); + } else { + result = @intCast(I, significand) << @intCast(Log2Int(I), exponent - fractional_sig_bits); + } + + if ((@typeInfo(I).Int.signedness == .signed) and negative) + return ~result +% 1; + return result; +} + +// Conversion from f16 + +pub fn __fixhfsi(a: f16) callconv(.C) i32 { + return fixXfYi(i32, a); +} + +pub fn __fixunshfsi(a: f16) callconv(.C) u32 { + return fixXfYi(u32, a); +} + +pub fn __fixhfdi(a: f16) callconv(.C) i64 { + return fixXfYi(i64, a); +} + +pub fn __fixunshfdi(a: f16) callconv(.C) u64 { + return fixXfYi(u64, a); +} + +pub fn __fixhfti(a: f16) callconv(.C) i128 { + return fixXfYi(i128, a); +} + +pub fn __fixunshfti(a: f16) callconv(.C) u128 { + return fixXfYi(u128, a); +} + +// Conversion from f32 + +pub fn __fixsfsi(a: f32) callconv(.C) i32 { + return fixXfYi(i32, a); +} + +pub fn __fixunssfsi(a: f32) callconv(.C) u32 { + return fixXfYi(u32, a); +} + +pub fn __fixsfdi(a: f32) callconv(.C) i64 { + return fixXfYi(i64, a); +} + +pub fn __fixunssfdi(a: f32) callconv(.C) u64 { + return fixXfYi(u64, a); +} + +pub fn __fixsfti(a: f32) callconv(.C) i128 { + return fixXfYi(i128, a); +} + +pub fn __fixunssfti(a: f32) callconv(.C) u128 { + return fixXfYi(u128, a); +} + +// Conversion from f64 + +pub fn __fixdfsi(a: f64) callconv(.C) i32 { + return fixXfYi(i32, a); +} + +pub fn __fixunsdfsi(a: f64) callconv(.C) u32 { + return fixXfYi(u32, a); +} + +pub fn __fixdfdi(a: f64) callconv(.C) i64 { + return fixXfYi(i64, a); +} + +pub fn __fixunsdfdi(a: f64) callconv(.C) u64 { + return fixXfYi(u64, a); +} + +pub fn __fixdfti(a: f64) callconv(.C) i128 { + return fixXfYi(i128, a); +} + +pub fn __fixunsdfti(a: f64) callconv(.C) u128 { + return fixXfYi(u128, a); +} + +// Conversion from f80 + +pub fn __fixxfsi(a: f80) callconv(.C) i32 { + return fixXfYi(i32, a); +} + +pub fn __fixunsxfsi(a: f80) callconv(.C) u32 { + return fixXfYi(u32, a); +} + +pub fn __fixxfdi(a: f80) callconv(.C) i64 { + return fixXfYi(i64, a); +} + +pub fn __fixunsxfdi(a: f80) callconv(.C) u64 { + return fixXfYi(u64, a); +} + +pub fn __fixxfti(a: f80) callconv(.C) i128 { + return fixXfYi(i128, a); +} + +pub fn __fixunsxfti(a: f80) callconv(.C) u128 { + return fixXfYi(u128, a); +} + +// Conversion from f128 + +pub fn __fixtfsi(a: f128) callconv(.C) i32 { + return fixXfYi(i32, a); +} + +pub fn __fixunstfsi(a: f128) callconv(.C) u32 { + return fixXfYi(u32, a); +} + +pub fn __fixtfdi(a: f128) callconv(.C) i64 { + return fixXfYi(i64, a); +} + +pub fn __fixunstfdi(a: f128) callconv(.C) u64 { + return fixXfYi(u64, a); +} + +pub fn __fixtfti(a: f128) callconv(.C) i128 { + return fixXfYi(i128, a); +} + +pub fn __fixunstfti(a: f128) callconv(.C) u128 { + return fixXfYi(u128, a); +} + +// Conversion from f32 + +pub fn __aeabi_f2iz(a: f32) callconv(.AAPCS) i32 { + return fixXfYi(i32, a); +} + +pub fn __aeabi_f2uiz(a: f32) callconv(.AAPCS) u32 { + return fixXfYi(u32, a); +} + +pub fn __aeabi_f2lz(a: f32) callconv(.AAPCS) i64 { + return fixXfYi(i64, a); +} + +pub fn __aeabi_f2ulz(a: f32) callconv(.AAPCS) u64 { + return fixXfYi(u64, a); +} + +// Conversion from f64 + +pub fn __aeabi_d2iz(a: f64) callconv(.AAPCS) i32 { + return fixXfYi(i32, a); +} + +pub fn __aeabi_d2uiz(a: f64) callconv(.AAPCS) u32 { + return fixXfYi(u32, a); +} + +pub fn __aeabi_d2lz(a: f64) callconv(.AAPCS) i64 { + return fixXfYi(i64, a); +} + +pub fn __aeabi_d2ulz(a: f64) callconv(.AAPCS) u64 { + return fixXfYi(u64, a); +} + +test { + _ = @import("fixXfYi_test.zig"); +} diff --git a/lib/std/special/compiler_rt/fixXfYi_test.zig b/lib/std/special/compiler_rt/fixXfYi_test.zig new file mode 100644 index 0000000000..00ed455609 --- /dev/null +++ b/lib/std/special/compiler_rt/fixXfYi_test.zig @@ -0,0 +1,948 @@ +const std = @import("std"); +const testing = std.testing; +const math = std.math; +const fixXfYi = @import("fixXfYi.zig").fixXfYi; + +// Conversion from f32 +const __fixsfsi = @import("fixXfYi.zig").__fixsfsi; +const __fixunssfsi = @import("fixXfYi.zig").__fixunssfsi; +const __fixsfdi = @import("fixXfYi.zig").__fixsfdi; +const __fixunssfdi = @import("fixXfYi.zig").__fixunssfdi; +const __fixsfti = @import("fixXfYi.zig").__fixsfti; +const __fixunssfti = @import("fixXfYi.zig").__fixunssfti; + +// Conversion from f64 +const __fixdfsi = @import("fixXfYi.zig").__fixdfsi; +const __fixunsdfsi = @import("fixXfYi.zig").__fixunsdfsi; +const __fixdfdi = @import("fixXfYi.zig").__fixdfdi; +const __fixunsdfdi = @import("fixXfYi.zig").__fixunsdfdi; +const __fixdfti = @import("fixXfYi.zig").__fixdfti; +const __fixunsdfti = @import("fixXfYi.zig").__fixunsdfti; + +// Conversion from f128 +const __fixtfsi = @import("fixXfYi.zig").__fixtfsi; +const __fixunstfsi = @import("fixXfYi.zig").__fixunstfsi; +const __fixtfdi = @import("fixXfYi.zig").__fixtfdi; +const __fixunstfdi = @import("fixXfYi.zig").__fixunstfdi; +const __fixtfti = @import("fixXfYi.zig").__fixtfti; +const __fixunstfti = @import("fixXfYi.zig").__fixunstfti; + +fn test__fixsfsi(a: f32, expected: i32) !void { + const x = __fixsfsi(a); + try testing.expect(x == expected); +} + +fn test__fixunssfsi(a: f32, expected: u32) !void { + const x = __fixunssfsi(a); + try testing.expect(x == expected); +} + +test "fixsfsi" { + try test__fixsfsi(-math.floatMax(f32), math.minInt(i32)); + + try test__fixsfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); + try test__fixsfsi(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000); + + try test__fixsfsi(-0x1.0000000000000p+127, -0x80000000); + try test__fixsfsi(-0x1.FFFFFFFFFFFFFp+126, -0x80000000); + try test__fixsfsi(-0x1.FFFFFFFFFFFFEp+126, -0x80000000); + + try test__fixsfsi(-0x1.0000000000001p+63, -0x80000000); + try test__fixsfsi(-0x1.0000000000000p+63, -0x80000000); + try test__fixsfsi(-0x1.FFFFFFFFFFFFFp+62, -0x80000000); + try test__fixsfsi(-0x1.FFFFFFFFFFFFEp+62, -0x80000000); + + try test__fixsfsi(-0x1.FFFFFEp+62, -0x80000000); + try test__fixsfsi(-0x1.FFFFFCp+62, -0x80000000); + + try test__fixsfsi(-0x1.000000p+31, -0x80000000); + try test__fixsfsi(-0x1.FFFFFFp+30, -0x80000000); + try test__fixsfsi(-0x1.FFFFFEp+30, -0x7FFFFF80); + try test__fixsfsi(-0x1.FFFFFCp+30, -0x7FFFFF00); + + try test__fixsfsi(-2.01, -2); + try test__fixsfsi(-2.0, -2); + try test__fixsfsi(-1.99, -1); + try test__fixsfsi(-1.0, -1); + try test__fixsfsi(-0.99, 0); + try test__fixsfsi(-0.5, 0); + try test__fixsfsi(-math.floatMin(f32), 0); + try test__fixsfsi(0.0, 0); + try test__fixsfsi(math.floatMin(f32), 0); + try test__fixsfsi(0.5, 0); + try test__fixsfsi(0.99, 0); + try test__fixsfsi(1.0, 1); + try test__fixsfsi(1.5, 1); + try test__fixsfsi(1.99, 1); + try test__fixsfsi(2.0, 2); + try test__fixsfsi(2.01, 2); + + try test__fixsfsi(0x1.FFFFFCp+30, 0x7FFFFF00); + try test__fixsfsi(0x1.FFFFFEp+30, 0x7FFFFF80); + try test__fixsfsi(0x1.FFFFFFp+30, 0x7FFFFFFF); + try test__fixsfsi(0x1.000000p+31, 0x7FFFFFFF); + + try test__fixsfsi(0x1.FFFFFCp+62, 0x7FFFFFFF); + try test__fixsfsi(0x1.FFFFFEp+62, 0x7FFFFFFF); + + try test__fixsfsi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFF); + try test__fixsfsi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFF); + try test__fixsfsi(0x1.0000000000000p+63, 0x7FFFFFFF); + try test__fixsfsi(0x1.0000000000001p+63, 0x7FFFFFFF); + + try test__fixsfsi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFF); + try test__fixsfsi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFF); + try test__fixsfsi(0x1.0000000000000p+127, 0x7FFFFFFF); + + try test__fixsfsi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFF); + try test__fixsfsi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i32)); + + try test__fixsfsi(math.floatMax(f32), math.maxInt(i32)); +} + +test "fixunssfsi" { + try test__fixunssfsi(0.0, 0); + + try test__fixunssfsi(0.5, 0); + try test__fixunssfsi(0.99, 0); + try test__fixunssfsi(1.0, 1); + try test__fixunssfsi(1.5, 1); + try test__fixunssfsi(1.99, 1); + try test__fixunssfsi(2.0, 2); + try test__fixunssfsi(2.01, 2); + try test__fixunssfsi(-0.5, 0); + try test__fixunssfsi(-0.99, 0); + + try test__fixunssfsi(-1.0, 0); + try test__fixunssfsi(-1.5, 0); + try test__fixunssfsi(-1.99, 0); + try test__fixunssfsi(-2.0, 0); + try test__fixunssfsi(-2.01, 0); + + try test__fixunssfsi(0x1.000000p+31, 0x80000000); + try test__fixunssfsi(0x1.000000p+32, 0xFFFFFFFF); + try test__fixunssfsi(0x1.FFFFFEp+31, 0xFFFFFF00); + try test__fixunssfsi(0x1.FFFFFEp+30, 0x7FFFFF80); + try test__fixunssfsi(0x1.FFFFFCp+30, 0x7FFFFF00); + + try test__fixunssfsi(-0x1.FFFFFEp+30, 0); + try test__fixunssfsi(-0x1.FFFFFCp+30, 0); +} + +fn test__fixsfdi(a: f32, expected: i64) !void { + const x = __fixsfdi(a); + try testing.expect(x == expected); +} + +fn test__fixunssfdi(a: f32, expected: u64) !void { + const x = __fixunssfdi(a); + try testing.expect(x == expected); +} + +test "fixsfdi" { + try test__fixsfdi(-math.floatMax(f32), math.minInt(i64)); + + try test__fixsfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); + try test__fixsfdi(-0x1.FFFFFFFFFFFFFp+1023, -0x8000000000000000); + + try test__fixsfdi(-0x1.0000000000000p+127, -0x8000000000000000); + try test__fixsfdi(-0x1.FFFFFFFFFFFFFp+126, -0x8000000000000000); + try test__fixsfdi(-0x1.FFFFFFFFFFFFEp+126, -0x8000000000000000); + + try test__fixsfdi(-0x1.0000000000001p+63, -0x8000000000000000); + try test__fixsfdi(-0x1.0000000000000p+63, -0x8000000000000000); + try test__fixsfdi(-0x1.FFFFFFFFFFFFFp+62, -0x8000000000000000); + try test__fixsfdi(-0x1.FFFFFFFFFFFFEp+62, -0x8000000000000000); + + try test__fixsfdi(-0x1.FFFFFFp+62, -0x8000000000000000); + try test__fixsfdi(-0x1.FFFFFEp+62, -0x7fffff8000000000); + try test__fixsfdi(-0x1.FFFFFCp+62, -0x7fffff0000000000); + + try test__fixsfdi(-2.01, -2); + try test__fixsfdi(-2.0, -2); + try test__fixsfdi(-1.99, -1); + try test__fixsfdi(-1.0, -1); + try test__fixsfdi(-0.99, 0); + try test__fixsfdi(-0.5, 0); + try test__fixsfdi(-math.floatMin(f32), 0); + try test__fixsfdi(0.0, 0); + try test__fixsfdi(math.floatMin(f32), 0); + try test__fixsfdi(0.5, 0); + try test__fixsfdi(0.99, 0); + try test__fixsfdi(1.0, 1); + try test__fixsfdi(1.5, 1); + try test__fixsfdi(1.99, 1); + try test__fixsfdi(2.0, 2); + try test__fixsfdi(2.01, 2); + + try test__fixsfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + try test__fixsfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + try test__fixsfdi(0x1.FFFFFFp+62, 0x7FFFFFFFFFFFFFFF); + + try test__fixsfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFFFFF); + try test__fixsfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFFFF); + try test__fixsfdi(0x1.0000000000000p+63, 0x7FFFFFFFFFFFFFFF); + try test__fixsfdi(0x1.0000000000001p+63, 0x7FFFFFFFFFFFFFFF); + + try test__fixsfdi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFF); + try test__fixsfdi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFF); + try test__fixsfdi(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFF); + + try test__fixsfdi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFF); + try test__fixsfdi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i64)); + + try test__fixsfdi(math.floatMax(f32), math.maxInt(i64)); +} + +test "fixunssfdi" { + try test__fixunssfdi(0.0, 0); + + try test__fixunssfdi(0.5, 0); + try test__fixunssfdi(0.99, 0); + try test__fixunssfdi(1.0, 1); + try test__fixunssfdi(1.5, 1); + try test__fixunssfdi(1.99, 1); + try test__fixunssfdi(2.0, 2); + try test__fixunssfdi(2.01, 2); + try test__fixunssfdi(-0.5, 0); + try test__fixunssfdi(-0.99, 0); + + try test__fixunssfdi(-1.0, 0); + try test__fixunssfdi(-1.5, 0); + try test__fixunssfdi(-1.99, 0); + try test__fixunssfdi(-2.0, 0); + try test__fixunssfdi(-2.01, 0); + + try test__fixunssfdi(0x1.FFFFFEp+63, 0xFFFFFF0000000000); + try test__fixunssfdi(0x1.000000p+63, 0x8000000000000000); + try test__fixunssfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + try test__fixunssfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + + try test__fixunssfdi(-0x1.FFFFFEp+62, 0x0000000000000000); + try test__fixunssfdi(-0x1.FFFFFCp+62, 0x0000000000000000); +} + +fn test__fixsfti(a: f32, expected: i128) !void { + const x = __fixsfti(a); + try testing.expect(x == expected); +} + +fn test__fixunssfti(a: f32, expected: u128) !void { + const x = __fixunssfti(a); + try testing.expect(x == expected); +} + +test "fixsfti" { + try test__fixsfti(-math.floatMax(f32), math.minInt(i128)); + + try test__fixsfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); + try test__fixsfti(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000000000000000000000000000); + + try test__fixsfti(-0x1.0000000000000p+127, -0x80000000000000000000000000000000); + try test__fixsfti(-0x1.FFFFFFFFFFFFFp+126, -0x80000000000000000000000000000000); + try test__fixsfti(-0x1.FFFFFFFFFFFFEp+126, -0x80000000000000000000000000000000); + try test__fixsfti(-0x1.FFFFFF0000000p+126, -0x80000000000000000000000000000000); + try test__fixsfti(-0x1.FFFFFE0000000p+126, -0x7FFFFF80000000000000000000000000); + try test__fixsfti(-0x1.FFFFFC0000000p+126, -0x7FFFFF00000000000000000000000000); + + try test__fixsfti(-0x1.0000000000001p+63, -0x8000000000000000); + try test__fixsfti(-0x1.0000000000000p+63, -0x8000000000000000); + try test__fixsfti(-0x1.FFFFFFFFFFFFFp+62, -0x8000000000000000); + try test__fixsfti(-0x1.FFFFFFFFFFFFEp+62, -0x8000000000000000); + + try test__fixsfti(-0x1.FFFFFFp+62, -0x8000000000000000); + try test__fixsfti(-0x1.FFFFFEp+62, -0x7fffff8000000000); + try test__fixsfti(-0x1.FFFFFCp+62, -0x7fffff0000000000); + + try test__fixsfti(-0x1.000000p+31, -0x80000000); + try test__fixsfti(-0x1.FFFFFFp+30, -0x80000000); + try test__fixsfti(-0x1.FFFFFEp+30, -0x7FFFFF80); + try test__fixsfti(-0x1.FFFFFCp+30, -0x7FFFFF00); + + try test__fixsfti(-2.01, -2); + try test__fixsfti(-2.0, -2); + try test__fixsfti(-1.99, -1); + try test__fixsfti(-1.0, -1); + try test__fixsfti(-0.99, 0); + try test__fixsfti(-0.5, 0); + try test__fixsfti(-math.floatMin(f32), 0); + try test__fixsfti(0.0, 0); + try test__fixsfti(math.floatMin(f32), 0); + try test__fixsfti(0.5, 0); + try test__fixsfti(0.99, 0); + try test__fixsfti(1.0, 1); + try test__fixsfti(1.5, 1); + try test__fixsfti(1.99, 1); + try test__fixsfti(2.0, 2); + try test__fixsfti(2.01, 2); + + try test__fixsfti(0x1.FFFFFCp+30, 0x7FFFFF00); + try test__fixsfti(0x1.FFFFFEp+30, 0x7FFFFF80); + try test__fixsfti(0x1.FFFFFFp+30, 0x80000000); + try test__fixsfti(0x1.000000p+31, 0x80000000); + + try test__fixsfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + try test__fixsfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + try test__fixsfti(0x1.FFFFFFp+62, 0x8000000000000000); + + try test__fixsfti(0x1.FFFFFFFFFFFFEp+62, 0x8000000000000000); + try test__fixsfti(0x1.FFFFFFFFFFFFFp+62, 0x8000000000000000); + try test__fixsfti(0x1.0000000000000p+63, 0x8000000000000000); + try test__fixsfti(0x1.0000000000001p+63, 0x8000000000000000); + + try test__fixsfti(0x1.FFFFFC0000000p+126, 0x7FFFFF00000000000000000000000000); + try test__fixsfti(0x1.FFFFFE0000000p+126, 0x7FFFFF80000000000000000000000000); + try test__fixsfti(0x1.FFFFFF0000000p+126, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + try test__fixsfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + try test__fixsfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + try test__fixsfti(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + + try test__fixsfti(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + try test__fixsfti(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i128)); + + try test__fixsfti(math.floatMax(f32), math.maxInt(i128)); +} + +test "fixunssfti" { + try test__fixunssfti(0.0, 0); + + try test__fixunssfti(0.5, 0); + try test__fixunssfti(0.99, 0); + try test__fixunssfti(1.0, 1); + try test__fixunssfti(1.5, 1); + try test__fixunssfti(1.99, 1); + try test__fixunssfti(2.0, 2); + try test__fixunssfti(2.01, 2); + try test__fixunssfti(-0.5, 0); + try test__fixunssfti(-0.99, 0); + + try test__fixunssfti(-1.0, 0); + try test__fixunssfti(-1.5, 0); + try test__fixunssfti(-1.99, 0); + try test__fixunssfti(-2.0, 0); + try test__fixunssfti(-2.01, 0); + + try test__fixunssfti(0x1.FFFFFEp+63, 0xFFFFFF0000000000); + try test__fixunssfti(0x1.000000p+63, 0x8000000000000000); + try test__fixunssfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + try test__fixunssfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + try test__fixunssfti(0x1.FFFFFEp+127, 0xFFFFFF00000000000000000000000000); + try test__fixunssfti(0x1.000000p+127, 0x80000000000000000000000000000000); + try test__fixunssfti(0x1.FFFFFEp+126, 0x7FFFFF80000000000000000000000000); + try test__fixunssfti(0x1.FFFFFCp+126, 0x7FFFFF00000000000000000000000000); + + try test__fixunssfti(-0x1.FFFFFEp+62, 0x0000000000000000); + try test__fixunssfti(-0x1.FFFFFCp+62, 0x0000000000000000); + try test__fixunssfti(-0x1.FFFFFEp+126, 0x0000000000000000); + try test__fixunssfti(-0x1.FFFFFCp+126, 0x0000000000000000); + try test__fixunssfti(math.floatMax(f32), 0xffffff00000000000000000000000000); + try test__fixunssfti(math.inf(f32), math.maxInt(u128)); +} + +fn test__fixdfsi(a: f64, expected: i32) !void { + const x = __fixdfsi(a); + try testing.expect(x == expected); +} + +fn test__fixunsdfsi(a: f64, expected: u32) !void { + const x = __fixunsdfsi(a); + try testing.expect(x == expected); +} + +test "fixdfsi" { + try test__fixdfsi(-math.floatMax(f64), math.minInt(i32)); + + try test__fixdfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); + try test__fixdfsi(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000); + + try test__fixdfsi(-0x1.0000000000000p+127, -0x80000000); + try test__fixdfsi(-0x1.FFFFFFFFFFFFFp+126, -0x80000000); + try test__fixdfsi(-0x1.FFFFFFFFFFFFEp+126, -0x80000000); + + try test__fixdfsi(-0x1.0000000000001p+63, -0x80000000); + try test__fixdfsi(-0x1.0000000000000p+63, -0x80000000); + try test__fixdfsi(-0x1.FFFFFFFFFFFFFp+62, -0x80000000); + try test__fixdfsi(-0x1.FFFFFFFFFFFFEp+62, -0x80000000); + + try test__fixdfsi(-0x1.FFFFFEp+62, -0x80000000); + try test__fixdfsi(-0x1.FFFFFCp+62, -0x80000000); + + try test__fixdfsi(-0x1.000000p+31, -0x80000000); + try test__fixdfsi(-0x1.FFFFFFp+30, -0x7FFFFFC0); + try test__fixdfsi(-0x1.FFFFFEp+30, -0x7FFFFF80); + + try test__fixdfsi(-2.01, -2); + try test__fixdfsi(-2.0, -2); + try test__fixdfsi(-1.99, -1); + try test__fixdfsi(-1.0, -1); + try test__fixdfsi(-0.99, 0); + try test__fixdfsi(-0.5, 0); + try test__fixdfsi(-math.floatMin(f64), 0); + try test__fixdfsi(0.0, 0); + try test__fixdfsi(math.floatMin(f64), 0); + try test__fixdfsi(0.5, 0); + try test__fixdfsi(0.99, 0); + try test__fixdfsi(1.0, 1); + try test__fixdfsi(1.5, 1); + try test__fixdfsi(1.99, 1); + try test__fixdfsi(2.0, 2); + try test__fixdfsi(2.01, 2); + + try test__fixdfsi(0x1.FFFFFEp+30, 0x7FFFFF80); + try test__fixdfsi(0x1.FFFFFFp+30, 0x7FFFFFC0); + try test__fixdfsi(0x1.000000p+31, 0x7FFFFFFF); + + try test__fixdfsi(0x1.FFFFFCp+62, 0x7FFFFFFF); + try test__fixdfsi(0x1.FFFFFEp+62, 0x7FFFFFFF); + + try test__fixdfsi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFF); + try test__fixdfsi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFF); + try test__fixdfsi(0x1.0000000000000p+63, 0x7FFFFFFF); + try test__fixdfsi(0x1.0000000000001p+63, 0x7FFFFFFF); + + try test__fixdfsi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFF); + try test__fixdfsi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFF); + try test__fixdfsi(0x1.0000000000000p+127, 0x7FFFFFFF); + + try test__fixdfsi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFF); + try test__fixdfsi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i32)); + + try test__fixdfsi(math.floatMax(f64), math.maxInt(i32)); +} + +test "fixunsdfsi" { + try test__fixunsdfsi(0.0, 0); + + try test__fixunsdfsi(0.5, 0); + try test__fixunsdfsi(0.99, 0); + try test__fixunsdfsi(1.0, 1); + try test__fixunsdfsi(1.5, 1); + try test__fixunsdfsi(1.99, 1); + try test__fixunsdfsi(2.0, 2); + try test__fixunsdfsi(2.01, 2); + try test__fixunsdfsi(-0.5, 0); + try test__fixunsdfsi(-0.99, 0); + try test__fixunsdfsi(-1.0, 0); + try test__fixunsdfsi(-1.5, 0); + try test__fixunsdfsi(-1.99, 0); + try test__fixunsdfsi(-2.0, 0); + try test__fixunsdfsi(-2.01, 0); + + try test__fixunsdfsi(0x1.000000p+31, 0x80000000); + try test__fixunsdfsi(0x1.000000p+32, 0xFFFFFFFF); + try test__fixunsdfsi(0x1.FFFFFEp+31, 0xFFFFFF00); + try test__fixunsdfsi(0x1.FFFFFEp+30, 0x7FFFFF80); + try test__fixunsdfsi(0x1.FFFFFCp+30, 0x7FFFFF00); + + try test__fixunsdfsi(-0x1.FFFFFEp+30, 0); + try test__fixunsdfsi(-0x1.FFFFFCp+30, 0); + + try test__fixunsdfsi(0x1.FFFFFFFEp+31, 0xFFFFFFFF); + try test__fixunsdfsi(0x1.FFFFFFFC00000p+30, 0x7FFFFFFF); + try test__fixunsdfsi(0x1.FFFFFFF800000p+30, 0x7FFFFFFE); +} + +fn test__fixdfdi(a: f64, expected: i64) !void { + const x = __fixdfdi(a); + try testing.expect(x == expected); +} + +fn test__fixunsdfdi(a: f64, expected: u64) !void { + const x = __fixunsdfdi(a); + try testing.expect(x == expected); +} + +test "fixdfdi" { + try test__fixdfdi(-math.floatMax(f64), math.minInt(i64)); + + try test__fixdfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); + try test__fixdfdi(-0x1.FFFFFFFFFFFFFp+1023, -0x8000000000000000); + + try test__fixdfdi(-0x1.0000000000000p+127, -0x8000000000000000); + try test__fixdfdi(-0x1.FFFFFFFFFFFFFp+126, -0x8000000000000000); + try test__fixdfdi(-0x1.FFFFFFFFFFFFEp+126, -0x8000000000000000); + + try test__fixdfdi(-0x1.0000000000001p+63, -0x8000000000000000); + try test__fixdfdi(-0x1.0000000000000p+63, -0x8000000000000000); + try test__fixdfdi(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); + try test__fixdfdi(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); + + try test__fixdfdi(-0x1.FFFFFEp+62, -0x7fffff8000000000); + try test__fixdfdi(-0x1.FFFFFCp+62, -0x7fffff0000000000); + + try test__fixdfdi(-2.01, -2); + try test__fixdfdi(-2.0, -2); + try test__fixdfdi(-1.99, -1); + try test__fixdfdi(-1.0, -1); + try test__fixdfdi(-0.99, 0); + try test__fixdfdi(-0.5, 0); + try test__fixdfdi(-math.floatMin(f64), 0); + try test__fixdfdi(0.0, 0); + try test__fixdfdi(math.floatMin(f64), 0); + try test__fixdfdi(0.5, 0); + try test__fixdfdi(0.99, 0); + try test__fixdfdi(1.0, 1); + try test__fixdfdi(1.5, 1); + try test__fixdfdi(1.99, 1); + try test__fixdfdi(2.0, 2); + try test__fixdfdi(2.01, 2); + + try test__fixdfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + try test__fixdfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + + try test__fixdfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); + try test__fixdfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); + try test__fixdfdi(0x1.0000000000000p+63, 0x7FFFFFFFFFFFFFFF); + try test__fixdfdi(0x1.0000000000001p+63, 0x7FFFFFFFFFFFFFFF); + + try test__fixdfdi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFF); + try test__fixdfdi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFF); + try test__fixdfdi(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFF); + + try test__fixdfdi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFF); + try test__fixdfdi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i64)); + + try test__fixdfdi(math.floatMax(f64), math.maxInt(i64)); +} + +test "fixunsdfdi" { + try test__fixunsdfdi(0.0, 0); + try test__fixunsdfdi(0.5, 0); + try test__fixunsdfdi(0.99, 0); + try test__fixunsdfdi(1.0, 1); + try test__fixunsdfdi(1.5, 1); + try test__fixunsdfdi(1.99, 1); + try test__fixunsdfdi(2.0, 2); + try test__fixunsdfdi(2.01, 2); + try test__fixunsdfdi(-0.5, 0); + try test__fixunsdfdi(-0.99, 0); + try test__fixunsdfdi(-1.0, 0); + try test__fixunsdfdi(-1.5, 0); + try test__fixunsdfdi(-1.99, 0); + try test__fixunsdfdi(-2.0, 0); + try test__fixunsdfdi(-2.01, 0); + + try test__fixunsdfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + try test__fixunsdfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + + try test__fixunsdfdi(-0x1.FFFFFEp+62, 0); + try test__fixunsdfdi(-0x1.FFFFFCp+62, 0); + + try test__fixunsdfdi(0x1.FFFFFFFFFFFFFp+63, 0xFFFFFFFFFFFFF800); + try test__fixunsdfdi(0x1.0000000000000p+63, 0x8000000000000000); + try test__fixunsdfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); + try test__fixunsdfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); + + try test__fixunsdfdi(-0x1.FFFFFFFFFFFFFp+62, 0); + try test__fixunsdfdi(-0x1.FFFFFFFFFFFFEp+62, 0); +} + +fn test__fixdfti(a: f64, expected: i128) !void { + const x = __fixdfti(a); + try testing.expect(x == expected); +} + +fn test__fixunsdfti(a: f64, expected: u128) !void { + const x = __fixunsdfti(a); + try testing.expect(x == expected); +} + +test "fixdfti" { + try test__fixdfti(-math.floatMax(f64), math.minInt(i128)); + + try test__fixdfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); + try test__fixdfti(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000000000000000000000000000); + + try test__fixdfti(-0x1.0000000000000p+127, -0x80000000000000000000000000000000); + try test__fixdfti(-0x1.FFFFFFFFFFFFFp+126, -0x7FFFFFFFFFFFFC000000000000000000); + try test__fixdfti(-0x1.FFFFFFFFFFFFEp+126, -0x7FFFFFFFFFFFF8000000000000000000); + + try test__fixdfti(-0x1.0000000000001p+63, -0x8000000000000800); + try test__fixdfti(-0x1.0000000000000p+63, -0x8000000000000000); + try test__fixdfti(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); + try test__fixdfti(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); + + try test__fixdfti(-0x1.FFFFFEp+62, -0x7fffff8000000000); + try test__fixdfti(-0x1.FFFFFCp+62, -0x7fffff0000000000); + + try test__fixdfti(-2.01, -2); + try test__fixdfti(-2.0, -2); + try test__fixdfti(-1.99, -1); + try test__fixdfti(-1.0, -1); + try test__fixdfti(-0.99, 0); + try test__fixdfti(-0.5, 0); + try test__fixdfti(-math.floatMin(f64), 0); + try test__fixdfti(0.0, 0); + try test__fixdfti(math.floatMin(f64), 0); + try test__fixdfti(0.5, 0); + try test__fixdfti(0.99, 0); + try test__fixdfti(1.0, 1); + try test__fixdfti(1.5, 1); + try test__fixdfti(1.99, 1); + try test__fixdfti(2.0, 2); + try test__fixdfti(2.01, 2); + + try test__fixdfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + try test__fixdfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + + try test__fixdfti(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); + try test__fixdfti(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); + try test__fixdfti(0x1.0000000000000p+63, 0x8000000000000000); + try test__fixdfti(0x1.0000000000001p+63, 0x8000000000000800); + + try test__fixdfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFF8000000000000000000); + try test__fixdfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFC000000000000000000); + try test__fixdfti(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + + try test__fixdfti(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + try test__fixdfti(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i128)); + + try test__fixdfti(math.floatMax(f64), math.maxInt(i128)); +} + +test "fixunsdfti" { + try test__fixunsdfti(0.0, 0); + + try test__fixunsdfti(0.5, 0); + try test__fixunsdfti(0.99, 0); + try test__fixunsdfti(1.0, 1); + try test__fixunsdfti(1.5, 1); + try test__fixunsdfti(1.99, 1); + try test__fixunsdfti(2.0, 2); + try test__fixunsdfti(2.01, 2); + try test__fixunsdfti(-0.5, 0); + try test__fixunsdfti(-0.99, 0); + try test__fixunsdfti(-1.0, 0); + try test__fixunsdfti(-1.5, 0); + try test__fixunsdfti(-1.99, 0); + try test__fixunsdfti(-2.0, 0); + try test__fixunsdfti(-2.01, 0); + + try test__fixunsdfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + try test__fixunsdfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + + try test__fixunsdfti(-0x1.FFFFFEp+62, 0); + try test__fixunsdfti(-0x1.FFFFFCp+62, 0); + + try test__fixunsdfti(0x1.FFFFFFFFFFFFFp+63, 0xFFFFFFFFFFFFF800); + try test__fixunsdfti(0x1.0000000000000p+63, 0x8000000000000000); + try test__fixunsdfti(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); + try test__fixunsdfti(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); + + try test__fixunsdfti(0x1.FFFFFFFFFFFFFp+127, 0xFFFFFFFFFFFFF8000000000000000000); + try test__fixunsdfti(0x1.0000000000000p+127, 0x80000000000000000000000000000000); + try test__fixunsdfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFC000000000000000000); + try test__fixunsdfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFF8000000000000000000); + try test__fixunsdfti(0x1.0000000000000p+128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + + try test__fixunsdfti(-0x1.FFFFFFFFFFFFFp+62, 0); + try test__fixunsdfti(-0x1.FFFFFFFFFFFFEp+62, 0); +} + +fn test__fixtfsi(a: f128, expected: i32) !void { + const x = __fixtfsi(a); + try testing.expect(x == expected); +} + +fn test__fixunstfsi(a: f128, expected: u32) !void { + const x = __fixunstfsi(a); + try testing.expect(x == expected); +} + +test "fixtfsi" { + try test__fixtfsi(-math.floatMax(f128), math.minInt(i32)); + + try test__fixtfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); + try test__fixtfsi(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000); + + try test__fixtfsi(-0x1.0000000000000p+127, -0x80000000); + try test__fixtfsi(-0x1.FFFFFFFFFFFFFp+126, -0x80000000); + try test__fixtfsi(-0x1.FFFFFFFFFFFFEp+126, -0x80000000); + + try test__fixtfsi(-0x1.0000000000001p+63, -0x80000000); + try test__fixtfsi(-0x1.0000000000000p+63, -0x80000000); + try test__fixtfsi(-0x1.FFFFFFFFFFFFFp+62, -0x80000000); + try test__fixtfsi(-0x1.FFFFFFFFFFFFEp+62, -0x80000000); + + try test__fixtfsi(-0x1.FFFFFEp+62, -0x80000000); + try test__fixtfsi(-0x1.FFFFFCp+62, -0x80000000); + + try test__fixtfsi(-0x1.000000p+31, -0x80000000); + try test__fixtfsi(-0x1.FFFFFFp+30, -0x7FFFFFC0); + try test__fixtfsi(-0x1.FFFFFEp+30, -0x7FFFFF80); + try test__fixtfsi(-0x1.FFFFFCp+30, -0x7FFFFF00); + + try test__fixtfsi(-2.01, -2); + try test__fixtfsi(-2.0, -2); + try test__fixtfsi(-1.99, -1); + try test__fixtfsi(-1.0, -1); + try test__fixtfsi(-0.99, 0); + try test__fixtfsi(-0.5, 0); + try test__fixtfsi(-math.floatMin(f32), 0); + try test__fixtfsi(0.0, 0); + try test__fixtfsi(math.floatMin(f32), 0); + try test__fixtfsi(0.5, 0); + try test__fixtfsi(0.99, 0); + try test__fixtfsi(1.0, 1); + try test__fixtfsi(1.5, 1); + try test__fixtfsi(1.99, 1); + try test__fixtfsi(2.0, 2); + try test__fixtfsi(2.01, 2); + + try test__fixtfsi(0x1.FFFFFCp+30, 0x7FFFFF00); + try test__fixtfsi(0x1.FFFFFEp+30, 0x7FFFFF80); + try test__fixtfsi(0x1.FFFFFFp+30, 0x7FFFFFC0); + try test__fixtfsi(0x1.000000p+31, 0x7FFFFFFF); + + try test__fixtfsi(0x1.FFFFFCp+62, 0x7FFFFFFF); + try test__fixtfsi(0x1.FFFFFEp+62, 0x7FFFFFFF); + + try test__fixtfsi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFF); + try test__fixtfsi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFF); + try test__fixtfsi(0x1.0000000000000p+63, 0x7FFFFFFF); + try test__fixtfsi(0x1.0000000000001p+63, 0x7FFFFFFF); + + try test__fixtfsi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFF); + try test__fixtfsi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFF); + try test__fixtfsi(0x1.0000000000000p+127, 0x7FFFFFFF); + + try test__fixtfsi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFF); + try test__fixtfsi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i32)); + + try test__fixtfsi(math.floatMax(f128), math.maxInt(i32)); +} + +test "fixunstfsi" { + try test__fixunstfsi(math.inf(f128), 0xffffffff); + try test__fixunstfsi(0, 0x0); + try test__fixunstfsi(0x1.23456789abcdefp+5, 0x24); + try test__fixunstfsi(0x1.23456789abcdefp-3, 0x0); + try test__fixunstfsi(0x1.23456789abcdefp+20, 0x123456); + try test__fixunstfsi(0x1.23456789abcdefp+40, 0xffffffff); + try test__fixunstfsi(0x1.23456789abcdefp+256, 0xffffffff); + try test__fixunstfsi(-0x1.23456789abcdefp+3, 0x0); + + try test__fixunstfsi(0x1p+32, 0xFFFFFFFF); +} + +fn test__fixtfdi(a: f128, expected: i64) !void { + const x = __fixtfdi(a); + try testing.expect(x == expected); +} + +fn test__fixunstfdi(a: f128, expected: u64) !void { + const x = __fixunstfdi(a); + try testing.expect(x == expected); +} + +test "fixtfdi" { + try test__fixtfdi(-math.floatMax(f128), math.minInt(i64)); + + try test__fixtfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); + try test__fixtfdi(-0x1.FFFFFFFFFFFFFp+1023, -0x8000000000000000); + + try test__fixtfdi(-0x1.0000000000000p+127, -0x8000000000000000); + try test__fixtfdi(-0x1.FFFFFFFFFFFFFp+126, -0x8000000000000000); + try test__fixtfdi(-0x1.FFFFFFFFFFFFEp+126, -0x8000000000000000); + + try test__fixtfdi(-0x1.0000000000001p+63, -0x8000000000000000); + try test__fixtfdi(-0x1.0000000000000p+63, -0x8000000000000000); + try test__fixtfdi(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); + try test__fixtfdi(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); + + try test__fixtfdi(-0x1.FFFFFEp+62, -0x7FFFFF8000000000); + try test__fixtfdi(-0x1.FFFFFCp+62, -0x7FFFFF0000000000); + + try test__fixtfdi(-0x1.000000p+31, -0x80000000); + try test__fixtfdi(-0x1.FFFFFFp+30, -0x7FFFFFC0); + try test__fixtfdi(-0x1.FFFFFEp+30, -0x7FFFFF80); + try test__fixtfdi(-0x1.FFFFFCp+30, -0x7FFFFF00); + + try test__fixtfdi(-2.01, -2); + try test__fixtfdi(-2.0, -2); + try test__fixtfdi(-1.99, -1); + try test__fixtfdi(-1.0, -1); + try test__fixtfdi(-0.99, 0); + try test__fixtfdi(-0.5, 0); + try test__fixtfdi(-math.floatMin(f64), 0); + try test__fixtfdi(0.0, 0); + try test__fixtfdi(math.floatMin(f64), 0); + try test__fixtfdi(0.5, 0); + try test__fixtfdi(0.99, 0); + try test__fixtfdi(1.0, 1); + try test__fixtfdi(1.5, 1); + try test__fixtfdi(1.99, 1); + try test__fixtfdi(2.0, 2); + try test__fixtfdi(2.01, 2); + + try test__fixtfdi(0x1.FFFFFCp+30, 0x7FFFFF00); + try test__fixtfdi(0x1.FFFFFEp+30, 0x7FFFFF80); + try test__fixtfdi(0x1.FFFFFFp+30, 0x7FFFFFC0); + try test__fixtfdi(0x1.000000p+31, 0x80000000); + + try test__fixtfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + try test__fixtfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + + try test__fixtfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); + try test__fixtfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); + try test__fixtfdi(0x1.0000000000000p+63, 0x7FFFFFFFFFFFFFFF); + try test__fixtfdi(0x1.0000000000001p+63, 0x7FFFFFFFFFFFFFFF); + + try test__fixtfdi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFF); + try test__fixtfdi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFF); + try test__fixtfdi(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFF); + + try test__fixtfdi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFF); + try test__fixtfdi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i64)); + + try test__fixtfdi(math.floatMax(f128), math.maxInt(i64)); +} + +test "fixunstfdi" { + try test__fixunstfdi(0.0, 0); + + try test__fixunstfdi(0.5, 0); + try test__fixunstfdi(0.99, 0); + try test__fixunstfdi(1.0, 1); + try test__fixunstfdi(1.5, 1); + try test__fixunstfdi(1.99, 1); + try test__fixunstfdi(2.0, 2); + try test__fixunstfdi(2.01, 2); + try test__fixunstfdi(-0.5, 0); + try test__fixunstfdi(-0.99, 0); + try test__fixunstfdi(-1.0, 0); + try test__fixunstfdi(-1.5, 0); + try test__fixunstfdi(-1.99, 0); + try test__fixunstfdi(-2.0, 0); + try test__fixunstfdi(-2.01, 0); + + try test__fixunstfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + try test__fixunstfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + + try test__fixunstfdi(-0x1.FFFFFEp+62, 0); + try test__fixunstfdi(-0x1.FFFFFCp+62, 0); + + try test__fixunstfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); + try test__fixunstfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); + + try test__fixunstfdi(-0x1.FFFFFFFFFFFFFp+62, 0); + try test__fixunstfdi(-0x1.FFFFFFFFFFFFEp+62, 0); + + try test__fixunstfdi(0x1.FFFFFFFFFFFFFFFEp+63, 0xFFFFFFFFFFFFFFFF); + try test__fixunstfdi(0x1.0000000000000002p+63, 0x8000000000000001); + try test__fixunstfdi(0x1.0000000000000000p+63, 0x8000000000000000); + try test__fixunstfdi(0x1.FFFFFFFFFFFFFFFCp+62, 0x7FFFFFFFFFFFFFFF); + try test__fixunstfdi(0x1.FFFFFFFFFFFFFFF8p+62, 0x7FFFFFFFFFFFFFFE); + try test__fixunstfdi(0x1p+64, 0xFFFFFFFFFFFFFFFF); + + try test__fixunstfdi(-0x1.0000000000000000p+63, 0); + try test__fixunstfdi(-0x1.FFFFFFFFFFFFFFFCp+62, 0); + try test__fixunstfdi(-0x1.FFFFFFFFFFFFFFF8p+62, 0); +} + +fn test__fixtfti(a: f128, expected: i128) !void { + const x = __fixtfti(a); + try testing.expect(x == expected); +} + +fn test__fixunstfti(a: f128, expected: u128) !void { + const x = __fixunstfti(a); + try testing.expect(x == expected); +} + +test "fixtfti" { + try test__fixtfti(-math.floatMax(f128), math.minInt(i128)); + + try test__fixtfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); + try test__fixtfti(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000000000000000000000000000); + + try test__fixtfti(-0x1.0000000000000p+127, -0x80000000000000000000000000000000); + try test__fixtfti(-0x1.FFFFFFFFFFFFFp+126, -0x7FFFFFFFFFFFFC000000000000000000); + try test__fixtfti(-0x1.FFFFFFFFFFFFEp+126, -0x7FFFFFFFFFFFF8000000000000000000); + + try test__fixtfti(-0x1.0000000000001p+63, -0x8000000000000800); + try test__fixtfti(-0x1.0000000000000p+63, -0x8000000000000000); + try test__fixtfti(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); + try test__fixtfti(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); + + try test__fixtfti(-0x1.FFFFFEp+62, -0x7fffff8000000000); + try test__fixtfti(-0x1.FFFFFCp+62, -0x7fffff0000000000); + + try test__fixtfti(-2.01, -2); + try test__fixtfti(-2.0, -2); + try test__fixtfti(-1.99, -1); + try test__fixtfti(-1.0, -1); + try test__fixtfti(-0.99, 0); + try test__fixtfti(-0.5, 0); + try test__fixtfti(-math.floatMin(f128), 0); + try test__fixtfti(0.0, 0); + try test__fixtfti(math.floatMin(f128), 0); + try test__fixtfti(0.5, 0); + try test__fixtfti(0.99, 0); + try test__fixtfti(1.0, 1); + try test__fixtfti(1.5, 1); + try test__fixtfti(1.99, 1); + try test__fixtfti(2.0, 2); + try test__fixtfti(2.01, 2); + + try test__fixtfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); + try test__fixtfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); + + try test__fixtfti(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); + try test__fixtfti(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); + try test__fixtfti(0x1.0000000000000p+63, 0x8000000000000000); + try test__fixtfti(0x1.0000000000001p+63, 0x8000000000000800); + + try test__fixtfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFF8000000000000000000); + try test__fixtfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFC000000000000000000); + try test__fixtfti(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + + try test__fixtfti(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + try test__fixtfti(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i128)); + + try test__fixtfti(math.floatMax(f128), math.maxInt(i128)); +} + +test "fixunstfti" { + try test__fixunstfti(math.inf(f128), 0xffffffffffffffffffffffffffffffff); + + try test__fixunstfti(0.0, 0); + + try test__fixunstfti(0.5, 0); + try test__fixunstfti(0.99, 0); + try test__fixunstfti(1.0, 1); + try test__fixunstfti(1.5, 1); + try test__fixunstfti(1.99, 1); + try test__fixunstfti(2.0, 2); + try test__fixunstfti(2.01, 2); + try test__fixunstfti(-0.01, 0); + try test__fixunstfti(-0.99, 0); + + try test__fixunstfti(0x1p+128, 0xffffffffffffffffffffffffffffffff); + + try test__fixunstfti(0x1.FFFFFEp+126, 0x7fffff80000000000000000000000000); + try test__fixunstfti(0x1.FFFFFEp+127, 0xffffff00000000000000000000000000); + try test__fixunstfti(0x1.FFFFFEp+128, 0xffffffffffffffffffffffffffffffff); + try test__fixunstfti(0x1.FFFFFEp+129, 0xffffffffffffffffffffffffffffffff); +} + +fn test__fixunshfti(a: f16, expected: u128) !void { + const x = fixXfYi(u128, a); + try testing.expect(x == expected); +} + +test "fixXfYi for f16" { + try test__fixunshfti(math.inf(f16), math.maxInt(u128)); + try test__fixunshfti(math.floatMax(f16), 65504); +} + +fn test__fixunsxfti(a: f80, expected: u128) !void { + const x = fixXfYi(u128, a); + try testing.expect(x == expected); +} + +test "fixXfYi for f80" { + try test__fixunsxfti(math.inf(f80), math.maxInt(u128)); + try test__fixunsxfti(math.floatMax(f80), math.maxInt(u128)); + try test__fixunsxfti(math.maxInt(u64), math.maxInt(u64)); +} diff --git a/lib/std/special/compiler_rt/fixdfdi.zig b/lib/std/special/compiler_rt/fixdfdi.zig deleted file mode 100644 index 6c69f10e09..0000000000 --- a/lib/std/special/compiler_rt/fixdfdi.zig +++ /dev/null @@ -1,16 +0,0 @@ -const fixint = @import("fixint.zig").fixint; -const builtin = @import("builtin"); - -pub fn __fixdfdi(a: f64) callconv(.C) i64 { - @setRuntimeSafety(builtin.is_test); - return fixint(f64, i64, a); -} - -pub fn __aeabi_d2lz(arg: f64) callconv(.AAPCS) i64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __fixdfdi, .{arg}); -} - -test { - _ = @import("fixdfdi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixdfdi_test.zig b/lib/std/special/compiler_rt/fixdfdi_test.zig deleted file mode 100644 index e80a875800..0000000000 --- a/lib/std/special/compiler_rt/fixdfdi_test.zig +++ /dev/null @@ -1,62 +0,0 @@ -const __fixdfdi = @import("fixdfdi.zig").__fixdfdi; -const std = @import("std"); -const math = std.math; -const testing = std.testing; - -fn test__fixdfdi(a: f64, expected: i64) !void { - const x = __fixdfdi(a); - try testing.expect(x == expected); -} - -test "fixdfdi" { - try test__fixdfdi(-math.floatMax(f64), math.minInt(i64)); - - try test__fixdfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); - try test__fixdfdi(-0x1.FFFFFFFFFFFFFp+1023, -0x8000000000000000); - - try test__fixdfdi(-0x1.0000000000000p+127, -0x8000000000000000); - try test__fixdfdi(-0x1.FFFFFFFFFFFFFp+126, -0x8000000000000000); - try test__fixdfdi(-0x1.FFFFFFFFFFFFEp+126, -0x8000000000000000); - - try test__fixdfdi(-0x1.0000000000001p+63, -0x8000000000000000); - try test__fixdfdi(-0x1.0000000000000p+63, -0x8000000000000000); - try test__fixdfdi(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); - try test__fixdfdi(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); - - try test__fixdfdi(-0x1.FFFFFEp+62, -0x7fffff8000000000); - try test__fixdfdi(-0x1.FFFFFCp+62, -0x7fffff0000000000); - - try test__fixdfdi(-2.01, -2); - try test__fixdfdi(-2.0, -2); - try test__fixdfdi(-1.99, -1); - try test__fixdfdi(-1.0, -1); - try test__fixdfdi(-0.99, 0); - try test__fixdfdi(-0.5, 0); - try test__fixdfdi(-math.floatMin(f64), 0); - try test__fixdfdi(0.0, 0); - try test__fixdfdi(math.floatMin(f64), 0); - try test__fixdfdi(0.5, 0); - try test__fixdfdi(0.99, 0); - try test__fixdfdi(1.0, 1); - try test__fixdfdi(1.5, 1); - try test__fixdfdi(1.99, 1); - try test__fixdfdi(2.0, 2); - try test__fixdfdi(2.01, 2); - - try test__fixdfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - try test__fixdfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - - try test__fixdfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); - try test__fixdfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); - try test__fixdfdi(0x1.0000000000000p+63, 0x7FFFFFFFFFFFFFFF); - try test__fixdfdi(0x1.0000000000001p+63, 0x7FFFFFFFFFFFFFFF); - - try test__fixdfdi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFF); - try test__fixdfdi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFF); - try test__fixdfdi(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFF); - - try test__fixdfdi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFF); - try test__fixdfdi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i64)); - - try test__fixdfdi(math.floatMax(f64), math.maxInt(i64)); -} diff --git a/lib/std/special/compiler_rt/fixdfsi.zig b/lib/std/special/compiler_rt/fixdfsi.zig deleted file mode 100644 index 5842d99cd3..0000000000 --- a/lib/std/special/compiler_rt/fixdfsi.zig +++ /dev/null @@ -1,16 +0,0 @@ -const fixint = @import("fixint.zig").fixint; -const builtin = @import("builtin"); - -pub fn __fixdfsi(a: f64) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - return fixint(f64, i32, a); -} - -pub fn __aeabi_d2iz(a: f64) callconv(.AAPCS) i32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __fixdfsi, .{a}); -} - -test { - _ = @import("fixdfsi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixdfsi_test.zig b/lib/std/special/compiler_rt/fixdfsi_test.zig deleted file mode 100644 index a1e76a6ee4..0000000000 --- a/lib/std/special/compiler_rt/fixdfsi_test.zig +++ /dev/null @@ -1,70 +0,0 @@ -const __fixdfsi = @import("fixdfsi.zig").__fixdfsi; -const std = @import("std"); -const math = std.math; -const testing = std.testing; - -fn test__fixdfsi(a: f64, expected: i32) !void { - const x = __fixdfsi(a); - try testing.expect(x == expected); -} - -test "fixdfsi" { - try test__fixdfsi(-math.floatMax(f64), math.minInt(i32)); - - try test__fixdfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); - try test__fixdfsi(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000); - - try test__fixdfsi(-0x1.0000000000000p+127, -0x80000000); - try test__fixdfsi(-0x1.FFFFFFFFFFFFFp+126, -0x80000000); - try test__fixdfsi(-0x1.FFFFFFFFFFFFEp+126, -0x80000000); - - try test__fixdfsi(-0x1.0000000000001p+63, -0x80000000); - try test__fixdfsi(-0x1.0000000000000p+63, -0x80000000); - try test__fixdfsi(-0x1.FFFFFFFFFFFFFp+62, -0x80000000); - try test__fixdfsi(-0x1.FFFFFFFFFFFFEp+62, -0x80000000); - - try test__fixdfsi(-0x1.FFFFFEp+62, -0x80000000); - try test__fixdfsi(-0x1.FFFFFCp+62, -0x80000000); - - try test__fixdfsi(-0x1.000000p+31, -0x80000000); - try test__fixdfsi(-0x1.FFFFFFp+30, -0x7FFFFFC0); - try test__fixdfsi(-0x1.FFFFFEp+30, -0x7FFFFF80); - - try test__fixdfsi(-2.01, -2); - try test__fixdfsi(-2.0, -2); - try test__fixdfsi(-1.99, -1); - try test__fixdfsi(-1.0, -1); - try test__fixdfsi(-0.99, 0); - try test__fixdfsi(-0.5, 0); - try test__fixdfsi(-math.floatMin(f64), 0); - try test__fixdfsi(0.0, 0); - try test__fixdfsi(math.floatMin(f64), 0); - try test__fixdfsi(0.5, 0); - try test__fixdfsi(0.99, 0); - try test__fixdfsi(1.0, 1); - try test__fixdfsi(1.5, 1); - try test__fixdfsi(1.99, 1); - try test__fixdfsi(2.0, 2); - try test__fixdfsi(2.01, 2); - - try test__fixdfsi(0x1.FFFFFEp+30, 0x7FFFFF80); - try test__fixdfsi(0x1.FFFFFFp+30, 0x7FFFFFC0); - try test__fixdfsi(0x1.000000p+31, 0x7FFFFFFF); - - try test__fixdfsi(0x1.FFFFFCp+62, 0x7FFFFFFF); - try test__fixdfsi(0x1.FFFFFEp+62, 0x7FFFFFFF); - - try test__fixdfsi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFF); - try test__fixdfsi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFF); - try test__fixdfsi(0x1.0000000000000p+63, 0x7FFFFFFF); - try test__fixdfsi(0x1.0000000000001p+63, 0x7FFFFFFF); - - try test__fixdfsi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFF); - try test__fixdfsi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFF); - try test__fixdfsi(0x1.0000000000000p+127, 0x7FFFFFFF); - - try test__fixdfsi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFF); - try test__fixdfsi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i32)); - - try test__fixdfsi(math.floatMax(f64), math.maxInt(i32)); -} diff --git a/lib/std/special/compiler_rt/fixdfti.zig b/lib/std/special/compiler_rt/fixdfti.zig deleted file mode 100644 index e8af6a2daf..0000000000 --- a/lib/std/special/compiler_rt/fixdfti.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixint = @import("fixint.zig").fixint; -const builtin = @import("builtin"); - -pub fn __fixdfti(a: f64) callconv(.C) i128 { - @setRuntimeSafety(builtin.is_test); - return fixint(f64, i128, a); -} - -test { - _ = @import("fixdfti_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixdfti_test.zig b/lib/std/special/compiler_rt/fixdfti_test.zig deleted file mode 100644 index cc6eec6b23..0000000000 --- a/lib/std/special/compiler_rt/fixdfti_test.zig +++ /dev/null @@ -1,62 +0,0 @@ -const __fixdfti = @import("fixdfti.zig").__fixdfti; -const std = @import("std"); -const math = std.math; -const testing = std.testing; - -fn test__fixdfti(a: f64, expected: i128) !void { - const x = __fixdfti(a); - try testing.expect(x == expected); -} - -test "fixdfti" { - try test__fixdfti(-math.floatMax(f64), math.minInt(i128)); - - try test__fixdfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); - try test__fixdfti(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000000000000000000000000000); - - try test__fixdfti(-0x1.0000000000000p+127, -0x80000000000000000000000000000000); - try test__fixdfti(-0x1.FFFFFFFFFFFFFp+126, -0x7FFFFFFFFFFFFC000000000000000000); - try test__fixdfti(-0x1.FFFFFFFFFFFFEp+126, -0x7FFFFFFFFFFFF8000000000000000000); - - try test__fixdfti(-0x1.0000000000001p+63, -0x8000000000000800); - try test__fixdfti(-0x1.0000000000000p+63, -0x8000000000000000); - try test__fixdfti(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); - try test__fixdfti(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); - - try test__fixdfti(-0x1.FFFFFEp+62, -0x7fffff8000000000); - try test__fixdfti(-0x1.FFFFFCp+62, -0x7fffff0000000000); - - try test__fixdfti(-2.01, -2); - try test__fixdfti(-2.0, -2); - try test__fixdfti(-1.99, -1); - try test__fixdfti(-1.0, -1); - try test__fixdfti(-0.99, 0); - try test__fixdfti(-0.5, 0); - try test__fixdfti(-math.floatMin(f64), 0); - try test__fixdfti(0.0, 0); - try test__fixdfti(math.floatMin(f64), 0); - try test__fixdfti(0.5, 0); - try test__fixdfti(0.99, 0); - try test__fixdfti(1.0, 1); - try test__fixdfti(1.5, 1); - try test__fixdfti(1.99, 1); - try test__fixdfti(2.0, 2); - try test__fixdfti(2.01, 2); - - try test__fixdfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - try test__fixdfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - - try test__fixdfti(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); - try test__fixdfti(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); - try test__fixdfti(0x1.0000000000000p+63, 0x8000000000000000); - try test__fixdfti(0x1.0000000000001p+63, 0x8000000000000800); - - try test__fixdfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFF8000000000000000000); - try test__fixdfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFC000000000000000000); - try test__fixdfti(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - - try test__fixdfti(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - try test__fixdfti(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i128)); - - try test__fixdfti(math.floatMax(f64), math.maxInt(i128)); -} diff --git a/lib/std/special/compiler_rt/fixint.zig b/lib/std/special/compiler_rt/fixint.zig deleted file mode 100644 index ff0577c115..0000000000 --- a/lib/std/special/compiler_rt/fixint.zig +++ /dev/null @@ -1,75 +0,0 @@ -const is_test = @import("builtin").is_test; -const std = @import("std"); -const math = std.math; -const Log2Int = std.math.Log2Int; -const maxInt = std.math.maxInt; -const minInt = std.math.minInt; - -const DBG = false; - -pub fn fixint(comptime fp_t: type, comptime fixint_t: type, a: fp_t) fixint_t { - @setRuntimeSafety(is_test); - - const rep_t = switch (fp_t) { - f32 => u32, - f64 => u64, - f128 => u128, - else => unreachable, - }; - const significandBits = switch (fp_t) { - f32 => 23, - f64 => 52, - f128 => 112, - else => unreachable, - }; - - const typeWidth = @typeInfo(rep_t).Int.bits; - const exponentBits = (typeWidth - significandBits - 1); - const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); - const maxExponent = ((1 << exponentBits) - 1); - const exponentBias = (maxExponent >> 1); - - const implicitBit = (@as(rep_t, 1) << significandBits); - const significandMask = (implicitBit - 1); - - // Break a into sign, exponent, significand - const aRep: rep_t = @bitCast(rep_t, a); - const absMask = signBit - 1; - const aAbs: rep_t = aRep & absMask; - - const negative = (aRep & signBit) != 0; - const exponent = @intCast(i32, aAbs >> significandBits) - exponentBias; - const significand: rep_t = (aAbs & significandMask) | implicitBit; - - // If exponent is negative, the uint_result is zero. - if (exponent < 0) return 0; - - // The unsigned result needs to be large enough to handle an fixint_t or rep_t - const fixint_bits = @typeInfo(fixint_t).Int.bits; - const fixuint_t = std.meta.Int(.unsigned, fixint_bits); - const UintResultType = if (fixint_bits > typeWidth) fixuint_t else rep_t; - var uint_result: UintResultType = undefined; - - // If the value is too large for the integer type, saturate. - if (@intCast(usize, exponent) >= fixint_bits) { - return if (negative) @as(fixint_t, minInt(fixint_t)) else @as(fixint_t, maxInt(fixint_t)); - } - - // If 0 <= exponent < significandBits, right shift else left shift - if (exponent < significandBits) { - uint_result = @intCast(UintResultType, significand) >> @intCast(Log2Int(UintResultType), significandBits - exponent); - } else { - uint_result = @intCast(UintResultType, significand) << @intCast(Log2Int(UintResultType), exponent - significandBits); - } - - // Cast to final signed result - if (negative) { - return if (uint_result >= -math.minInt(fixint_t)) math.minInt(fixint_t) else -@intCast(fixint_t, uint_result); - } else { - return if (uint_result >= math.maxInt(fixint_t)) math.maxInt(fixint_t) else @intCast(fixint_t, uint_result); - } -} - -test { - _ = @import("fixint_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixsfdi.zig b/lib/std/special/compiler_rt/fixsfdi.zig deleted file mode 100644 index 64961075d0..0000000000 --- a/lib/std/special/compiler_rt/fixsfdi.zig +++ /dev/null @@ -1,16 +0,0 @@ -const fixint = @import("fixint.zig").fixint; -const builtin = @import("builtin"); - -pub fn __fixsfdi(a: f32) callconv(.C) i64 { - @setRuntimeSafety(builtin.is_test); - return fixint(f32, i64, a); -} - -pub fn __aeabi_f2lz(arg: f32) callconv(.AAPCS) i64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __fixsfdi, .{arg}); -} - -test { - _ = @import("fixsfdi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixsfdi_test.zig b/lib/std/special/compiler_rt/fixsfdi_test.zig deleted file mode 100644 index 1ddd99bbe8..0000000000 --- a/lib/std/special/compiler_rt/fixsfdi_test.zig +++ /dev/null @@ -1,64 +0,0 @@ -const __fixsfdi = @import("fixsfdi.zig").__fixsfdi; -const std = @import("std"); -const math = std.math; -const testing = std.testing; - -fn test__fixsfdi(a: f32, expected: i64) !void { - const x = __fixsfdi(a); - try testing.expect(x == expected); -} - -test "fixsfdi" { - try test__fixsfdi(-math.floatMax(f32), math.minInt(i64)); - - try test__fixsfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); - try test__fixsfdi(-0x1.FFFFFFFFFFFFFp+1023, -0x8000000000000000); - - try test__fixsfdi(-0x1.0000000000000p+127, -0x8000000000000000); - try test__fixsfdi(-0x1.FFFFFFFFFFFFFp+126, -0x8000000000000000); - try test__fixsfdi(-0x1.FFFFFFFFFFFFEp+126, -0x8000000000000000); - - try test__fixsfdi(-0x1.0000000000001p+63, -0x8000000000000000); - try test__fixsfdi(-0x1.0000000000000p+63, -0x8000000000000000); - try test__fixsfdi(-0x1.FFFFFFFFFFFFFp+62, -0x8000000000000000); - try test__fixsfdi(-0x1.FFFFFFFFFFFFEp+62, -0x8000000000000000); - - try test__fixsfdi(-0x1.FFFFFFp+62, -0x8000000000000000); - try test__fixsfdi(-0x1.FFFFFEp+62, -0x7fffff8000000000); - try test__fixsfdi(-0x1.FFFFFCp+62, -0x7fffff0000000000); - - try test__fixsfdi(-2.01, -2); - try test__fixsfdi(-2.0, -2); - try test__fixsfdi(-1.99, -1); - try test__fixsfdi(-1.0, -1); - try test__fixsfdi(-0.99, 0); - try test__fixsfdi(-0.5, 0); - try test__fixsfdi(-math.floatMin(f32), 0); - try test__fixsfdi(0.0, 0); - try test__fixsfdi(math.floatMin(f32), 0); - try test__fixsfdi(0.5, 0); - try test__fixsfdi(0.99, 0); - try test__fixsfdi(1.0, 1); - try test__fixsfdi(1.5, 1); - try test__fixsfdi(1.99, 1); - try test__fixsfdi(2.0, 2); - try test__fixsfdi(2.01, 2); - - try test__fixsfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - try test__fixsfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - try test__fixsfdi(0x1.FFFFFFp+62, 0x7FFFFFFFFFFFFFFF); - - try test__fixsfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFFFFF); - try test__fixsfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFFFF); - try test__fixsfdi(0x1.0000000000000p+63, 0x7FFFFFFFFFFFFFFF); - try test__fixsfdi(0x1.0000000000001p+63, 0x7FFFFFFFFFFFFFFF); - - try test__fixsfdi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFF); - try test__fixsfdi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFF); - try test__fixsfdi(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFF); - - try test__fixsfdi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFF); - try test__fixsfdi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i64)); - - try test__fixsfdi(math.floatMax(f32), math.maxInt(i64)); -} diff --git a/lib/std/special/compiler_rt/fixsfsi.zig b/lib/std/special/compiler_rt/fixsfsi.zig deleted file mode 100644 index 932a6e1a4f..0000000000 --- a/lib/std/special/compiler_rt/fixsfsi.zig +++ /dev/null @@ -1,16 +0,0 @@ -const fixint = @import("fixint.zig").fixint; -const builtin = @import("builtin"); - -pub fn __fixsfsi(a: f32) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - return fixint(f32, i32, a); -} - -pub fn __aeabi_f2iz(a: f32) callconv(.AAPCS) i32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __fixsfsi, .{a}); -} - -test { - _ = @import("fixsfsi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixsfsi_test.zig b/lib/std/special/compiler_rt/fixsfsi_test.zig deleted file mode 100644 index 2393cc388e..0000000000 --- a/lib/std/special/compiler_rt/fixsfsi_test.zig +++ /dev/null @@ -1,72 +0,0 @@ -const __fixsfsi = @import("fixsfsi.zig").__fixsfsi; -const std = @import("std"); -const math = std.math; -const testing = std.testing; - -fn test__fixsfsi(a: f32, expected: i32) !void { - const x = __fixsfsi(a); - try testing.expect(x == expected); -} - -test "fixsfsi" { - try test__fixsfsi(-math.floatMax(f32), math.minInt(i32)); - - try test__fixsfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); - try test__fixsfsi(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000); - - try test__fixsfsi(-0x1.0000000000000p+127, -0x80000000); - try test__fixsfsi(-0x1.FFFFFFFFFFFFFp+126, -0x80000000); - try test__fixsfsi(-0x1.FFFFFFFFFFFFEp+126, -0x80000000); - - try test__fixsfsi(-0x1.0000000000001p+63, -0x80000000); - try test__fixsfsi(-0x1.0000000000000p+63, -0x80000000); - try test__fixsfsi(-0x1.FFFFFFFFFFFFFp+62, -0x80000000); - try test__fixsfsi(-0x1.FFFFFFFFFFFFEp+62, -0x80000000); - - try test__fixsfsi(-0x1.FFFFFEp+62, -0x80000000); - try test__fixsfsi(-0x1.FFFFFCp+62, -0x80000000); - - try test__fixsfsi(-0x1.000000p+31, -0x80000000); - try test__fixsfsi(-0x1.FFFFFFp+30, -0x80000000); - try test__fixsfsi(-0x1.FFFFFEp+30, -0x7FFFFF80); - try test__fixsfsi(-0x1.FFFFFCp+30, -0x7FFFFF00); - - try test__fixsfsi(-2.01, -2); - try test__fixsfsi(-2.0, -2); - try test__fixsfsi(-1.99, -1); - try test__fixsfsi(-1.0, -1); - try test__fixsfsi(-0.99, 0); - try test__fixsfsi(-0.5, 0); - try test__fixsfsi(-math.floatMin(f32), 0); - try test__fixsfsi(0.0, 0); - try test__fixsfsi(math.floatMin(f32), 0); - try test__fixsfsi(0.5, 0); - try test__fixsfsi(0.99, 0); - try test__fixsfsi(1.0, 1); - try test__fixsfsi(1.5, 1); - try test__fixsfsi(1.99, 1); - try test__fixsfsi(2.0, 2); - try test__fixsfsi(2.01, 2); - - try test__fixsfsi(0x1.FFFFFCp+30, 0x7FFFFF00); - try test__fixsfsi(0x1.FFFFFEp+30, 0x7FFFFF80); - try test__fixsfsi(0x1.FFFFFFp+30, 0x7FFFFFFF); - try test__fixsfsi(0x1.000000p+31, 0x7FFFFFFF); - - try test__fixsfsi(0x1.FFFFFCp+62, 0x7FFFFFFF); - try test__fixsfsi(0x1.FFFFFEp+62, 0x7FFFFFFF); - - try test__fixsfsi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFF); - try test__fixsfsi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFF); - try test__fixsfsi(0x1.0000000000000p+63, 0x7FFFFFFF); - try test__fixsfsi(0x1.0000000000001p+63, 0x7FFFFFFF); - - try test__fixsfsi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFF); - try test__fixsfsi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFF); - try test__fixsfsi(0x1.0000000000000p+127, 0x7FFFFFFF); - - try test__fixsfsi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFF); - try test__fixsfsi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i32)); - - try test__fixsfsi(math.floatMax(f32), math.maxInt(i32)); -} diff --git a/lib/std/special/compiler_rt/fixsfti.zig b/lib/std/special/compiler_rt/fixsfti.zig deleted file mode 100644 index e67bbabbd7..0000000000 --- a/lib/std/special/compiler_rt/fixsfti.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixint = @import("fixint.zig").fixint; -const builtin = @import("builtin"); - -pub fn __fixsfti(a: f32) callconv(.C) i128 { - @setRuntimeSafety(builtin.is_test); - return fixint(f32, i128, a); -} - -test { - _ = @import("fixsfti_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixsfti_test.zig b/lib/std/special/compiler_rt/fixsfti_test.zig deleted file mode 100644 index 7149f300db..0000000000 --- a/lib/std/special/compiler_rt/fixsfti_test.zig +++ /dev/null @@ -1,80 +0,0 @@ -const __fixsfti = @import("fixsfti.zig").__fixsfti; -const std = @import("std"); -const math = std.math; -const testing = std.testing; - -fn test__fixsfti(a: f32, expected: i128) !void { - const x = __fixsfti(a); - try testing.expect(x == expected); -} - -test "fixsfti" { - try test__fixsfti(-math.floatMax(f32), math.minInt(i128)); - - try test__fixsfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); - try test__fixsfti(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000000000000000000000000000); - - try test__fixsfti(-0x1.0000000000000p+127, -0x80000000000000000000000000000000); - try test__fixsfti(-0x1.FFFFFFFFFFFFFp+126, -0x80000000000000000000000000000000); - try test__fixsfti(-0x1.FFFFFFFFFFFFEp+126, -0x80000000000000000000000000000000); - try test__fixsfti(-0x1.FFFFFF0000000p+126, -0x80000000000000000000000000000000); - try test__fixsfti(-0x1.FFFFFE0000000p+126, -0x7FFFFF80000000000000000000000000); - try test__fixsfti(-0x1.FFFFFC0000000p+126, -0x7FFFFF00000000000000000000000000); - - try test__fixsfti(-0x1.0000000000001p+63, -0x8000000000000000); - try test__fixsfti(-0x1.0000000000000p+63, -0x8000000000000000); - try test__fixsfti(-0x1.FFFFFFFFFFFFFp+62, -0x8000000000000000); - try test__fixsfti(-0x1.FFFFFFFFFFFFEp+62, -0x8000000000000000); - - try test__fixsfti(-0x1.FFFFFFp+62, -0x8000000000000000); - try test__fixsfti(-0x1.FFFFFEp+62, -0x7fffff8000000000); - try test__fixsfti(-0x1.FFFFFCp+62, -0x7fffff0000000000); - - try test__fixsfti(-0x1.000000p+31, -0x80000000); - try test__fixsfti(-0x1.FFFFFFp+30, -0x80000000); - try test__fixsfti(-0x1.FFFFFEp+30, -0x7FFFFF80); - try test__fixsfti(-0x1.FFFFFCp+30, -0x7FFFFF00); - - try test__fixsfti(-2.01, -2); - try test__fixsfti(-2.0, -2); - try test__fixsfti(-1.99, -1); - try test__fixsfti(-1.0, -1); - try test__fixsfti(-0.99, 0); - try test__fixsfti(-0.5, 0); - try test__fixsfti(-math.floatMin(f32), 0); - try test__fixsfti(0.0, 0); - try test__fixsfti(math.floatMin(f32), 0); - try test__fixsfti(0.5, 0); - try test__fixsfti(0.99, 0); - try test__fixsfti(1.0, 1); - try test__fixsfti(1.5, 1); - try test__fixsfti(1.99, 1); - try test__fixsfti(2.0, 2); - try test__fixsfti(2.01, 2); - - try test__fixsfti(0x1.FFFFFCp+30, 0x7FFFFF00); - try test__fixsfti(0x1.FFFFFEp+30, 0x7FFFFF80); - try test__fixsfti(0x1.FFFFFFp+30, 0x80000000); - try test__fixsfti(0x1.000000p+31, 0x80000000); - - try test__fixsfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - try test__fixsfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - try test__fixsfti(0x1.FFFFFFp+62, 0x8000000000000000); - - try test__fixsfti(0x1.FFFFFFFFFFFFEp+62, 0x8000000000000000); - try test__fixsfti(0x1.FFFFFFFFFFFFFp+62, 0x8000000000000000); - try test__fixsfti(0x1.0000000000000p+63, 0x8000000000000000); - try test__fixsfti(0x1.0000000000001p+63, 0x8000000000000000); - - try test__fixsfti(0x1.FFFFFC0000000p+126, 0x7FFFFF00000000000000000000000000); - try test__fixsfti(0x1.FFFFFE0000000p+126, 0x7FFFFF80000000000000000000000000); - try test__fixsfti(0x1.FFFFFF0000000p+126, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - try test__fixsfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - try test__fixsfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - try test__fixsfti(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - - try test__fixsfti(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - try test__fixsfti(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i128)); - - try test__fixsfti(math.floatMax(f32), math.maxInt(i128)); -} diff --git a/lib/std/special/compiler_rt/fixtfdi.zig b/lib/std/special/compiler_rt/fixtfdi.zig deleted file mode 100644 index 6087d7e720..0000000000 --- a/lib/std/special/compiler_rt/fixtfdi.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixint = @import("fixint.zig").fixint; -const builtin = @import("builtin"); - -pub fn __fixtfdi(a: f128) callconv(.C) i64 { - @setRuntimeSafety(builtin.is_test); - return fixint(f128, i64, a); -} - -test { - _ = @import("fixtfdi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixtfdi_test.zig b/lib/std/special/compiler_rt/fixtfdi_test.zig deleted file mode 100644 index 79c320f622..0000000000 --- a/lib/std/special/compiler_rt/fixtfdi_test.zig +++ /dev/null @@ -1,72 +0,0 @@ -const __fixtfdi = @import("fixtfdi.zig").__fixtfdi; -const std = @import("std"); -const math = std.math; -const testing = std.testing; - -fn test__fixtfdi(a: f128, expected: i64) !void { - const x = __fixtfdi(a); - try testing.expect(x == expected); -} - -test "fixtfdi" { - try test__fixtfdi(-math.floatMax(f128), math.minInt(i64)); - - try test__fixtfdi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i64)); - try test__fixtfdi(-0x1.FFFFFFFFFFFFFp+1023, -0x8000000000000000); - - try test__fixtfdi(-0x1.0000000000000p+127, -0x8000000000000000); - try test__fixtfdi(-0x1.FFFFFFFFFFFFFp+126, -0x8000000000000000); - try test__fixtfdi(-0x1.FFFFFFFFFFFFEp+126, -0x8000000000000000); - - try test__fixtfdi(-0x1.0000000000001p+63, -0x8000000000000000); - try test__fixtfdi(-0x1.0000000000000p+63, -0x8000000000000000); - try test__fixtfdi(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); - try test__fixtfdi(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); - - try test__fixtfdi(-0x1.FFFFFEp+62, -0x7FFFFF8000000000); - try test__fixtfdi(-0x1.FFFFFCp+62, -0x7FFFFF0000000000); - - try test__fixtfdi(-0x1.000000p+31, -0x80000000); - try test__fixtfdi(-0x1.FFFFFFp+30, -0x7FFFFFC0); - try test__fixtfdi(-0x1.FFFFFEp+30, -0x7FFFFF80); - try test__fixtfdi(-0x1.FFFFFCp+30, -0x7FFFFF00); - - try test__fixtfdi(-2.01, -2); - try test__fixtfdi(-2.0, -2); - try test__fixtfdi(-1.99, -1); - try test__fixtfdi(-1.0, -1); - try test__fixtfdi(-0.99, 0); - try test__fixtfdi(-0.5, 0); - try test__fixtfdi(-@as(f128, math.floatMin(f64)), 0); - try test__fixtfdi(0.0, 0); - try test__fixtfdi(@as(f128, math.floatMin(f64)), 0); - try test__fixtfdi(0.5, 0); - try test__fixtfdi(0.99, 0); - try test__fixtfdi(1.0, 1); - try test__fixtfdi(1.5, 1); - try test__fixtfdi(1.99, 1); - try test__fixtfdi(2.0, 2); - try test__fixtfdi(2.01, 2); - - try test__fixtfdi(0x1.FFFFFCp+30, 0x7FFFFF00); - try test__fixtfdi(0x1.FFFFFEp+30, 0x7FFFFF80); - try test__fixtfdi(0x1.FFFFFFp+30, 0x7FFFFFC0); - try test__fixtfdi(0x1.000000p+31, 0x80000000); - - try test__fixtfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - try test__fixtfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - - try test__fixtfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); - try test__fixtfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); - try test__fixtfdi(0x1.0000000000000p+63, 0x7FFFFFFFFFFFFFFF); - try test__fixtfdi(0x1.0000000000001p+63, 0x7FFFFFFFFFFFFFFF); - - try test__fixtfdi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFFFFF); - try test__fixtfdi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFFFF); - try test__fixtfdi(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFF); - - try test__fixtfdi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFF); - try test__fixtfdi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i64)); - - try test__fixtfdi(math.floatMax(f128), math.maxInt(i64)); -} diff --git a/lib/std/special/compiler_rt/fixtfsi.zig b/lib/std/special/compiler_rt/fixtfsi.zig deleted file mode 100644 index b5fea97c5c..0000000000 --- a/lib/std/special/compiler_rt/fixtfsi.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixint = @import("fixint.zig").fixint; -const builtin = @import("builtin"); - -pub fn __fixtfsi(a: f128) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - return fixint(f128, i32, a); -} - -test { - _ = @import("fixtfsi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixtfsi_test.zig b/lib/std/special/compiler_rt/fixtfsi_test.zig deleted file mode 100644 index f05a4778f0..0000000000 --- a/lib/std/special/compiler_rt/fixtfsi_test.zig +++ /dev/null @@ -1,72 +0,0 @@ -const __fixtfsi = @import("fixtfsi.zig").__fixtfsi; -const std = @import("std"); -const math = std.math; -const testing = std.testing; - -fn test__fixtfsi(a: f128, expected: i32) !void { - const x = __fixtfsi(a); - try testing.expect(x == expected); -} - -test "fixtfsi" { - try test__fixtfsi(-math.floatMax(f128), math.minInt(i32)); - - try test__fixtfsi(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i32)); - try test__fixtfsi(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000); - - try test__fixtfsi(-0x1.0000000000000p+127, -0x80000000); - try test__fixtfsi(-0x1.FFFFFFFFFFFFFp+126, -0x80000000); - try test__fixtfsi(-0x1.FFFFFFFFFFFFEp+126, -0x80000000); - - try test__fixtfsi(-0x1.0000000000001p+63, -0x80000000); - try test__fixtfsi(-0x1.0000000000000p+63, -0x80000000); - try test__fixtfsi(-0x1.FFFFFFFFFFFFFp+62, -0x80000000); - try test__fixtfsi(-0x1.FFFFFFFFFFFFEp+62, -0x80000000); - - try test__fixtfsi(-0x1.FFFFFEp+62, -0x80000000); - try test__fixtfsi(-0x1.FFFFFCp+62, -0x80000000); - - try test__fixtfsi(-0x1.000000p+31, -0x80000000); - try test__fixtfsi(-0x1.FFFFFFp+30, -0x7FFFFFC0); - try test__fixtfsi(-0x1.FFFFFEp+30, -0x7FFFFF80); - try test__fixtfsi(-0x1.FFFFFCp+30, -0x7FFFFF00); - - try test__fixtfsi(-2.01, -2); - try test__fixtfsi(-2.0, -2); - try test__fixtfsi(-1.99, -1); - try test__fixtfsi(-1.0, -1); - try test__fixtfsi(-0.99, 0); - try test__fixtfsi(-0.5, 0); - try test__fixtfsi(-@as(f128, math.floatMin(f32)), 0); - try test__fixtfsi(0.0, 0); - try test__fixtfsi(@as(f128, math.floatMin(f32)), 0); - try test__fixtfsi(0.5, 0); - try test__fixtfsi(0.99, 0); - try test__fixtfsi(1.0, 1); - try test__fixtfsi(1.5, 1); - try test__fixtfsi(1.99, 1); - try test__fixtfsi(2.0, 2); - try test__fixtfsi(2.01, 2); - - try test__fixtfsi(0x1.FFFFFCp+30, 0x7FFFFF00); - try test__fixtfsi(0x1.FFFFFEp+30, 0x7FFFFF80); - try test__fixtfsi(0x1.FFFFFFp+30, 0x7FFFFFC0); - try test__fixtfsi(0x1.000000p+31, 0x7FFFFFFF); - - try test__fixtfsi(0x1.FFFFFCp+62, 0x7FFFFFFF); - try test__fixtfsi(0x1.FFFFFEp+62, 0x7FFFFFFF); - - try test__fixtfsi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFF); - try test__fixtfsi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFF); - try test__fixtfsi(0x1.0000000000000p+63, 0x7FFFFFFF); - try test__fixtfsi(0x1.0000000000001p+63, 0x7FFFFFFF); - - try test__fixtfsi(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFF); - try test__fixtfsi(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFF); - try test__fixtfsi(0x1.0000000000000p+127, 0x7FFFFFFF); - - try test__fixtfsi(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFF); - try test__fixtfsi(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i32)); - - try test__fixtfsi(math.floatMax(f128), math.maxInt(i32)); -} diff --git a/lib/std/special/compiler_rt/fixtfti.zig b/lib/std/special/compiler_rt/fixtfti.zig deleted file mode 100644 index 5a001d0b69..0000000000 --- a/lib/std/special/compiler_rt/fixtfti.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixint = @import("fixint.zig").fixint; -const builtin = @import("builtin"); - -pub fn __fixtfti(a: f128) callconv(.C) i128 { - @setRuntimeSafety(builtin.is_test); - return fixint(f128, i128, a); -} - -test { - _ = @import("fixtfti_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixtfti_test.zig b/lib/std/special/compiler_rt/fixtfti_test.zig deleted file mode 100644 index 4c1c9bd640..0000000000 --- a/lib/std/special/compiler_rt/fixtfti_test.zig +++ /dev/null @@ -1,62 +0,0 @@ -const __fixtfti = @import("fixtfti.zig").__fixtfti; -const std = @import("std"); -const math = std.math; -const testing = std.testing; - -fn test__fixtfti(a: f128, expected: i128) !void { - const x = __fixtfti(a); - try testing.expect(x == expected); -} - -test "fixtfti" { - try test__fixtfti(-math.floatMax(f128), math.minInt(i128)); - - try test__fixtfti(-0x1.FFFFFFFFFFFFFp+1023, math.minInt(i128)); - try test__fixtfti(-0x1.FFFFFFFFFFFFFp+1023, -0x80000000000000000000000000000000); - - try test__fixtfti(-0x1.0000000000000p+127, -0x80000000000000000000000000000000); - try test__fixtfti(-0x1.FFFFFFFFFFFFFp+126, -0x7FFFFFFFFFFFFC000000000000000000); - try test__fixtfti(-0x1.FFFFFFFFFFFFEp+126, -0x7FFFFFFFFFFFF8000000000000000000); - - try test__fixtfti(-0x1.0000000000001p+63, -0x8000000000000800); - try test__fixtfti(-0x1.0000000000000p+63, -0x8000000000000000); - try test__fixtfti(-0x1.FFFFFFFFFFFFFp+62, -0x7FFFFFFFFFFFFC00); - try test__fixtfti(-0x1.FFFFFFFFFFFFEp+62, -0x7FFFFFFFFFFFF800); - - try test__fixtfti(-0x1.FFFFFEp+62, -0x7fffff8000000000); - try test__fixtfti(-0x1.FFFFFCp+62, -0x7fffff0000000000); - - try test__fixtfti(-2.01, -2); - try test__fixtfti(-2.0, -2); - try test__fixtfti(-1.99, -1); - try test__fixtfti(-1.0, -1); - try test__fixtfti(-0.99, 0); - try test__fixtfti(-0.5, 0); - try test__fixtfti(-math.floatMin(f128), 0); - try test__fixtfti(0.0, 0); - try test__fixtfti(math.floatMin(f128), 0); - try test__fixtfti(0.5, 0); - try test__fixtfti(0.99, 0); - try test__fixtfti(1.0, 1); - try test__fixtfti(1.5, 1); - try test__fixtfti(1.99, 1); - try test__fixtfti(2.0, 2); - try test__fixtfti(2.01, 2); - - try test__fixtfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - try test__fixtfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - - try test__fixtfti(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); - try test__fixtfti(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); - try test__fixtfti(0x1.0000000000000p+63, 0x8000000000000000); - try test__fixtfti(0x1.0000000000001p+63, 0x8000000000000800); - - try test__fixtfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFF8000000000000000000); - try test__fixtfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFC000000000000000000); - try test__fixtfti(0x1.0000000000000p+127, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - - try test__fixtfti(0x1.FFFFFFFFFFFFFp+1023, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - try test__fixtfti(0x1.FFFFFFFFFFFFFp+1023, math.maxInt(i128)); - - try test__fixtfti(math.floatMax(f128), math.maxInt(i128)); -} diff --git a/lib/std/special/compiler_rt/fixuint.zig b/lib/std/special/compiler_rt/fixuint.zig deleted file mode 100644 index 6bfbcf6d65..0000000000 --- a/lib/std/special/compiler_rt/fixuint.zig +++ /dev/null @@ -1,50 +0,0 @@ -const is_test = @import("builtin").is_test; -const Log2Int = @import("std").math.Log2Int; - -pub inline fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t { - @setRuntimeSafety(is_test); - - const rep_t = switch (fp_t) { - f32 => u32, - f64 => u64, - f128 => u128, - else => unreachable, - }; - const typeWidth = @typeInfo(rep_t).Int.bits; - const significandBits = switch (fp_t) { - f32 => 23, - f64 => 52, - f128 => 112, - else => unreachable, - }; - const exponentBits = (typeWidth - significandBits - 1); - const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); - const maxExponent = ((1 << exponentBits) - 1); - const exponentBias = (maxExponent >> 1); - - const implicitBit = (@as(rep_t, 1) << significandBits); - const significandMask = (implicitBit - 1); - - // Break a into sign, exponent, significand - const aRep: rep_t = @bitCast(rep_t, a); - const absMask = signBit - 1; - const aAbs: rep_t = aRep & absMask; - - const sign = if ((aRep & signBit) != 0) @as(i32, -1) else @as(i32, 1); - const exponent = @intCast(i32, aAbs >> significandBits) - exponentBias; - const significand: rep_t = (aAbs & significandMask) | implicitBit; - - // If either the value or the exponent is negative, the result is zero. - if (sign == -1 or exponent < 0) return 0; - - // If the value is too large for the integer type, saturate. - if (@intCast(c_uint, exponent) >= @typeInfo(fixuint_t).Int.bits) return ~@as(fixuint_t, 0); - - // If 0 <= exponent < significandBits, right shift to get the result. - // Otherwise, shift left. - if (exponent < significandBits) { - return @intCast(fixuint_t, significand >> @intCast(Log2Int(rep_t), significandBits - exponent)); - } else { - return @intCast(fixuint_t, significand) << @intCast(Log2Int(fixuint_t), exponent - significandBits); - } -} diff --git a/lib/std/special/compiler_rt/fixunsdfdi.zig b/lib/std/special/compiler_rt/fixunsdfdi.zig deleted file mode 100644 index c864f0f6c2..0000000000 --- a/lib/std/special/compiler_rt/fixunsdfdi.zig +++ /dev/null @@ -1,16 +0,0 @@ -const fixuint = @import("fixuint.zig").fixuint; -const builtin = @import("builtin"); - -pub fn __fixunsdfdi(a: f64) callconv(.C) u64 { - @setRuntimeSafety(builtin.is_test); - return fixuint(f64, u64, a); -} - -pub fn __aeabi_d2ulz(a: f64) callconv(.AAPCS) u64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __fixunsdfdi, .{a}); -} - -test { - _ = @import("fixunsdfdi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixunsdfdi_test.zig b/lib/std/special/compiler_rt/fixunsdfdi_test.zig deleted file mode 100644 index 59591cf181..0000000000 --- a/lib/std/special/compiler_rt/fixunsdfdi_test.zig +++ /dev/null @@ -1,39 +0,0 @@ -const __fixunsdfdi = @import("fixunsdfdi.zig").__fixunsdfdi; -const testing = @import("std").testing; - -fn test__fixunsdfdi(a: f64, expected: u64) !void { - const x = __fixunsdfdi(a); - try testing.expect(x == expected); -} - -test "fixunsdfdi" { - //test__fixunsdfdi(0.0, 0); - //test__fixunsdfdi(0.5, 0); - //test__fixunsdfdi(0.99, 0); - try test__fixunsdfdi(1.0, 1); - try test__fixunsdfdi(1.5, 1); - try test__fixunsdfdi(1.99, 1); - try test__fixunsdfdi(2.0, 2); - try test__fixunsdfdi(2.01, 2); - try test__fixunsdfdi(-0.5, 0); - try test__fixunsdfdi(-0.99, 0); - try test__fixunsdfdi(-1.0, 0); - try test__fixunsdfdi(-1.5, 0); - try test__fixunsdfdi(-1.99, 0); - try test__fixunsdfdi(-2.0, 0); - try test__fixunsdfdi(-2.01, 0); - - try test__fixunsdfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - try test__fixunsdfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - - try test__fixunsdfdi(-0x1.FFFFFEp+62, 0); - try test__fixunsdfdi(-0x1.FFFFFCp+62, 0); - - try test__fixunsdfdi(0x1.FFFFFFFFFFFFFp+63, 0xFFFFFFFFFFFFF800); - try test__fixunsdfdi(0x1.0000000000000p+63, 0x8000000000000000); - try test__fixunsdfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); - try test__fixunsdfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); - - try test__fixunsdfdi(-0x1.FFFFFFFFFFFFFp+62, 0); - try test__fixunsdfdi(-0x1.FFFFFFFFFFFFEp+62, 0); -} diff --git a/lib/std/special/compiler_rt/fixunsdfsi.zig b/lib/std/special/compiler_rt/fixunsdfsi.zig deleted file mode 100644 index c1f5173661..0000000000 --- a/lib/std/special/compiler_rt/fixunsdfsi.zig +++ /dev/null @@ -1,16 +0,0 @@ -const fixuint = @import("fixuint.zig").fixuint; -const builtin = @import("builtin"); - -pub fn __fixunsdfsi(a: f64) callconv(.C) u32 { - @setRuntimeSafety(builtin.is_test); - return fixuint(f64, u32, a); -} - -pub fn __aeabi_d2uiz(arg: f64) callconv(.AAPCS) u32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __fixunsdfsi, .{arg}); -} - -test { - _ = @import("fixunsdfsi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixunsdfsi_test.zig b/lib/std/special/compiler_rt/fixunsdfsi_test.zig deleted file mode 100644 index b4b7ec8840..0000000000 --- a/lib/std/special/compiler_rt/fixunsdfsi_test.zig +++ /dev/null @@ -1,39 +0,0 @@ -const __fixunsdfsi = @import("fixunsdfsi.zig").__fixunsdfsi; -const testing = @import("std").testing; - -fn test__fixunsdfsi(a: f64, expected: u32) !void { - const x = __fixunsdfsi(a); - try testing.expect(x == expected); -} - -test "fixunsdfsi" { - try test__fixunsdfsi(0.0, 0); - - try test__fixunsdfsi(0.5, 0); - try test__fixunsdfsi(0.99, 0); - try test__fixunsdfsi(1.0, 1); - try test__fixunsdfsi(1.5, 1); - try test__fixunsdfsi(1.99, 1); - try test__fixunsdfsi(2.0, 2); - try test__fixunsdfsi(2.01, 2); - try test__fixunsdfsi(-0.5, 0); - try test__fixunsdfsi(-0.99, 0); - try test__fixunsdfsi(-1.0, 0); - try test__fixunsdfsi(-1.5, 0); - try test__fixunsdfsi(-1.99, 0); - try test__fixunsdfsi(-2.0, 0); - try test__fixunsdfsi(-2.01, 0); - - try test__fixunsdfsi(0x1.000000p+31, 0x80000000); - try test__fixunsdfsi(0x1.000000p+32, 0xFFFFFFFF); - try test__fixunsdfsi(0x1.FFFFFEp+31, 0xFFFFFF00); - try test__fixunsdfsi(0x1.FFFFFEp+30, 0x7FFFFF80); - try test__fixunsdfsi(0x1.FFFFFCp+30, 0x7FFFFF00); - - try test__fixunsdfsi(-0x1.FFFFFEp+30, 0); - try test__fixunsdfsi(-0x1.FFFFFCp+30, 0); - - try test__fixunsdfsi(0x1.FFFFFFFEp+31, 0xFFFFFFFF); - try test__fixunsdfsi(0x1.FFFFFFFC00000p+30, 0x7FFFFFFF); - try test__fixunsdfsi(0x1.FFFFFFF800000p+30, 0x7FFFFFFE); -} diff --git a/lib/std/special/compiler_rt/fixunsdfti.zig b/lib/std/special/compiler_rt/fixunsdfti.zig deleted file mode 100644 index 3c9871a4df..0000000000 --- a/lib/std/special/compiler_rt/fixunsdfti.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixuint = @import("fixuint.zig").fixuint; -const builtin = @import("builtin"); - -pub fn __fixunsdfti(a: f64) callconv(.C) u128 { - @setRuntimeSafety(builtin.is_test); - return fixuint(f64, u128, a); -} - -test { - _ = @import("fixunsdfti_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixunsdfti_test.zig b/lib/std/special/compiler_rt/fixunsdfti_test.zig deleted file mode 100644 index d9e1424836..0000000000 --- a/lib/std/special/compiler_rt/fixunsdfti_test.zig +++ /dev/null @@ -1,46 +0,0 @@ -const __fixunsdfti = @import("fixunsdfti.zig").__fixunsdfti; -const testing = @import("std").testing; - -fn test__fixunsdfti(a: f64, expected: u128) !void { - const x = __fixunsdfti(a); - try testing.expect(x == expected); -} - -test "fixunsdfti" { - try test__fixunsdfti(0.0, 0); - - try test__fixunsdfti(0.5, 0); - try test__fixunsdfti(0.99, 0); - try test__fixunsdfti(1.0, 1); - try test__fixunsdfti(1.5, 1); - try test__fixunsdfti(1.99, 1); - try test__fixunsdfti(2.0, 2); - try test__fixunsdfti(2.01, 2); - try test__fixunsdfti(-0.5, 0); - try test__fixunsdfti(-0.99, 0); - try test__fixunsdfti(-1.0, 0); - try test__fixunsdfti(-1.5, 0); - try test__fixunsdfti(-1.99, 0); - try test__fixunsdfti(-2.0, 0); - try test__fixunsdfti(-2.01, 0); - - try test__fixunsdfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - try test__fixunsdfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - - try test__fixunsdfti(-0x1.FFFFFEp+62, 0); - try test__fixunsdfti(-0x1.FFFFFCp+62, 0); - - try test__fixunsdfti(0x1.FFFFFFFFFFFFFp+63, 0xFFFFFFFFFFFFF800); - try test__fixunsdfti(0x1.0000000000000p+63, 0x8000000000000000); - try test__fixunsdfti(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); - try test__fixunsdfti(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); - - try test__fixunsdfti(0x1.FFFFFFFFFFFFFp+127, 0xFFFFFFFFFFFFF8000000000000000000); - try test__fixunsdfti(0x1.0000000000000p+127, 0x80000000000000000000000000000000); - try test__fixunsdfti(0x1.FFFFFFFFFFFFFp+126, 0x7FFFFFFFFFFFFC000000000000000000); - try test__fixunsdfti(0x1.FFFFFFFFFFFFEp+126, 0x7FFFFFFFFFFFF8000000000000000000); - try test__fixunsdfti(0x1.0000000000000p+128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - - try test__fixunsdfti(-0x1.FFFFFFFFFFFFFp+62, 0); - try test__fixunsdfti(-0x1.FFFFFFFFFFFFEp+62, 0); -} diff --git a/lib/std/special/compiler_rt/fixunssfdi.zig b/lib/std/special/compiler_rt/fixunssfdi.zig deleted file mode 100644 index edad20e8bf..0000000000 --- a/lib/std/special/compiler_rt/fixunssfdi.zig +++ /dev/null @@ -1,16 +0,0 @@ -const fixuint = @import("fixuint.zig").fixuint; -const builtin = @import("builtin"); - -pub fn __fixunssfdi(a: f32) callconv(.C) u64 { - @setRuntimeSafety(builtin.is_test); - return fixuint(f32, u64, a); -} - -pub fn __aeabi_f2ulz(a: f32) callconv(.AAPCS) u64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __fixunssfdi, .{a}); -} - -test { - _ = @import("fixunssfdi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixunssfdi_test.zig b/lib/std/special/compiler_rt/fixunssfdi_test.zig deleted file mode 100644 index 3c46511b6d..0000000000 --- a/lib/std/special/compiler_rt/fixunssfdi_test.zig +++ /dev/null @@ -1,35 +0,0 @@ -const __fixunssfdi = @import("fixunssfdi.zig").__fixunssfdi; -const testing = @import("std").testing; - -fn test__fixunssfdi(a: f32, expected: u64) !void { - const x = __fixunssfdi(a); - try testing.expect(x == expected); -} - -test "fixunssfdi" { - try test__fixunssfdi(0.0, 0); - - try test__fixunssfdi(0.5, 0); - try test__fixunssfdi(0.99, 0); - try test__fixunssfdi(1.0, 1); - try test__fixunssfdi(1.5, 1); - try test__fixunssfdi(1.99, 1); - try test__fixunssfdi(2.0, 2); - try test__fixunssfdi(2.01, 2); - try test__fixunssfdi(-0.5, 0); - try test__fixunssfdi(-0.99, 0); - - try test__fixunssfdi(-1.0, 0); - try test__fixunssfdi(-1.5, 0); - try test__fixunssfdi(-1.99, 0); - try test__fixunssfdi(-2.0, 0); - try test__fixunssfdi(-2.01, 0); - - try test__fixunssfdi(0x1.FFFFFEp+63, 0xFFFFFF0000000000); - try test__fixunssfdi(0x1.000000p+63, 0x8000000000000000); - try test__fixunssfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - try test__fixunssfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - - try test__fixunssfdi(-0x1.FFFFFEp+62, 0x0000000000000000); - try test__fixunssfdi(-0x1.FFFFFCp+62, 0x0000000000000000); -} diff --git a/lib/std/special/compiler_rt/fixunssfsi.zig b/lib/std/special/compiler_rt/fixunssfsi.zig deleted file mode 100644 index 96722a2a36..0000000000 --- a/lib/std/special/compiler_rt/fixunssfsi.zig +++ /dev/null @@ -1,16 +0,0 @@ -const fixuint = @import("fixuint.zig").fixuint; -const builtin = @import("builtin"); - -pub fn __fixunssfsi(a: f32) callconv(.C) u32 { - @setRuntimeSafety(builtin.is_test); - return fixuint(f32, u32, a); -} - -pub fn __aeabi_f2uiz(a: f32) callconv(.AAPCS) u32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __fixunssfsi, .{a}); -} - -test { - _ = @import("fixunssfsi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixunssfsi_test.zig b/lib/std/special/compiler_rt/fixunssfsi_test.zig deleted file mode 100644 index 4bf90e4bff..0000000000 --- a/lib/std/special/compiler_rt/fixunssfsi_test.zig +++ /dev/null @@ -1,36 +0,0 @@ -const __fixunssfsi = @import("fixunssfsi.zig").__fixunssfsi; -const testing = @import("std").testing; - -fn test__fixunssfsi(a: f32, expected: u32) !void { - const x = __fixunssfsi(a); - try testing.expect(x == expected); -} - -test "fixunssfsi" { - try test__fixunssfsi(0.0, 0); - - try test__fixunssfsi(0.5, 0); - try test__fixunssfsi(0.99, 0); - try test__fixunssfsi(1.0, 1); - try test__fixunssfsi(1.5, 1); - try test__fixunssfsi(1.99, 1); - try test__fixunssfsi(2.0, 2); - try test__fixunssfsi(2.01, 2); - try test__fixunssfsi(-0.5, 0); - try test__fixunssfsi(-0.99, 0); - - try test__fixunssfsi(-1.0, 0); - try test__fixunssfsi(-1.5, 0); - try test__fixunssfsi(-1.99, 0); - try test__fixunssfsi(-2.0, 0); - try test__fixunssfsi(-2.01, 0); - - try test__fixunssfsi(0x1.000000p+31, 0x80000000); - try test__fixunssfsi(0x1.000000p+32, 0xFFFFFFFF); - try test__fixunssfsi(0x1.FFFFFEp+31, 0xFFFFFF00); - try test__fixunssfsi(0x1.FFFFFEp+30, 0x7FFFFF80); - try test__fixunssfsi(0x1.FFFFFCp+30, 0x7FFFFF00); - - try test__fixunssfsi(-0x1.FFFFFEp+30, 0); - try test__fixunssfsi(-0x1.FFFFFCp+30, 0); -} diff --git a/lib/std/special/compiler_rt/fixunssfti.zig b/lib/std/special/compiler_rt/fixunssfti.zig deleted file mode 100644 index 4967202e69..0000000000 --- a/lib/std/special/compiler_rt/fixunssfti.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixuint = @import("fixuint.zig").fixuint; -const builtin = @import("builtin"); - -pub fn __fixunssfti(a: f32) callconv(.C) u128 { - @setRuntimeSafety(builtin.is_test); - return fixuint(f32, u128, a); -} - -test { - _ = @import("fixunssfti_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixunssfti_test.zig b/lib/std/special/compiler_rt/fixunssfti_test.zig deleted file mode 100644 index 3c1a314e91..0000000000 --- a/lib/std/special/compiler_rt/fixunssfti_test.zig +++ /dev/null @@ -1,41 +0,0 @@ -const __fixunssfti = @import("fixunssfti.zig").__fixunssfti; -const testing = @import("std").testing; - -fn test__fixunssfti(a: f32, expected: u128) !void { - const x = __fixunssfti(a); - try testing.expect(x == expected); -} - -test "fixunssfti" { - try test__fixunssfti(0.0, 0); - - try test__fixunssfti(0.5, 0); - try test__fixunssfti(0.99, 0); - try test__fixunssfti(1.0, 1); - try test__fixunssfti(1.5, 1); - try test__fixunssfti(1.99, 1); - try test__fixunssfti(2.0, 2); - try test__fixunssfti(2.01, 2); - try test__fixunssfti(-0.5, 0); - try test__fixunssfti(-0.99, 0); - - try test__fixunssfti(-1.0, 0); - try test__fixunssfti(-1.5, 0); - try test__fixunssfti(-1.99, 0); - try test__fixunssfti(-2.0, 0); - try test__fixunssfti(-2.01, 0); - - try test__fixunssfti(0x1.FFFFFEp+63, 0xFFFFFF0000000000); - try test__fixunssfti(0x1.000000p+63, 0x8000000000000000); - try test__fixunssfti(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - try test__fixunssfti(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - try test__fixunssfti(0x1.FFFFFEp+127, 0xFFFFFF00000000000000000000000000); - try test__fixunssfti(0x1.000000p+127, 0x80000000000000000000000000000000); - try test__fixunssfti(0x1.FFFFFEp+126, 0x7FFFFF80000000000000000000000000); - try test__fixunssfti(0x1.FFFFFCp+126, 0x7FFFFF00000000000000000000000000); - - try test__fixunssfti(-0x1.FFFFFEp+62, 0x0000000000000000); - try test__fixunssfti(-0x1.FFFFFCp+62, 0x0000000000000000); - try test__fixunssfti(-0x1.FFFFFEp+126, 0x0000000000000000); - try test__fixunssfti(-0x1.FFFFFCp+126, 0x0000000000000000); -} diff --git a/lib/std/special/compiler_rt/fixunstfdi.zig b/lib/std/special/compiler_rt/fixunstfdi.zig deleted file mode 100644 index 0db4987a08..0000000000 --- a/lib/std/special/compiler_rt/fixunstfdi.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixuint = @import("fixuint.zig").fixuint; -const builtin = @import("builtin"); - -pub fn __fixunstfdi(a: f128) callconv(.C) u64 { - @setRuntimeSafety(builtin.is_test); - return fixuint(f128, u64, a); -} - -test { - _ = @import("fixunstfdi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixunstfdi_test.zig b/lib/std/special/compiler_rt/fixunstfdi_test.zig deleted file mode 100644 index aa746799b7..0000000000 --- a/lib/std/special/compiler_rt/fixunstfdi_test.zig +++ /dev/null @@ -1,49 +0,0 @@ -const __fixunstfdi = @import("fixunstfdi.zig").__fixunstfdi; -const testing = @import("std").testing; - -fn test__fixunstfdi(a: f128, expected: u64) !void { - const x = __fixunstfdi(a); - try testing.expect(x == expected); -} - -test "fixunstfdi" { - try test__fixunstfdi(0.0, 0); - - try test__fixunstfdi(0.5, 0); - try test__fixunstfdi(0.99, 0); - try test__fixunstfdi(1.0, 1); - try test__fixunstfdi(1.5, 1); - try test__fixunstfdi(1.99, 1); - try test__fixunstfdi(2.0, 2); - try test__fixunstfdi(2.01, 2); - try test__fixunstfdi(-0.5, 0); - try test__fixunstfdi(-0.99, 0); - try test__fixunstfdi(-1.0, 0); - try test__fixunstfdi(-1.5, 0); - try test__fixunstfdi(-1.99, 0); - try test__fixunstfdi(-2.0, 0); - try test__fixunstfdi(-2.01, 0); - - try test__fixunstfdi(0x1.FFFFFEp+62, 0x7FFFFF8000000000); - try test__fixunstfdi(0x1.FFFFFCp+62, 0x7FFFFF0000000000); - - try test__fixunstfdi(-0x1.FFFFFEp+62, 0); - try test__fixunstfdi(-0x1.FFFFFCp+62, 0); - - try test__fixunstfdi(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00); - try test__fixunstfdi(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800); - - try test__fixunstfdi(-0x1.FFFFFFFFFFFFFp+62, 0); - try test__fixunstfdi(-0x1.FFFFFFFFFFFFEp+62, 0); - - try test__fixunstfdi(0x1.FFFFFFFFFFFFFFFEp+63, 0xFFFFFFFFFFFFFFFF); - try test__fixunstfdi(0x1.0000000000000002p+63, 0x8000000000000001); - try test__fixunstfdi(0x1.0000000000000000p+63, 0x8000000000000000); - try test__fixunstfdi(0x1.FFFFFFFFFFFFFFFCp+62, 0x7FFFFFFFFFFFFFFF); - try test__fixunstfdi(0x1.FFFFFFFFFFFFFFF8p+62, 0x7FFFFFFFFFFFFFFE); - try test__fixunstfdi(0x1p+64, 0xFFFFFFFFFFFFFFFF); - - try test__fixunstfdi(-0x1.0000000000000000p+63, 0); - try test__fixunstfdi(-0x1.FFFFFFFFFFFFFFFCp+62, 0); - try test__fixunstfdi(-0x1.FFFFFFFFFFFFFFF8p+62, 0); -} diff --git a/lib/std/special/compiler_rt/fixunstfsi.zig b/lib/std/special/compiler_rt/fixunstfsi.zig deleted file mode 100644 index 8cedc07a35..0000000000 --- a/lib/std/special/compiler_rt/fixunstfsi.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixuint = @import("fixuint.zig").fixuint; -const builtin = @import("builtin"); - -pub fn __fixunstfsi(a: f128) callconv(.C) u32 { - @setRuntimeSafety(builtin.is_test); - return fixuint(f128, u32, a); -} - -test { - _ = @import("fixunstfsi_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixunstfsi_test.zig b/lib/std/special/compiler_rt/fixunstfsi_test.zig deleted file mode 100644 index 206161bef5..0000000000 --- a/lib/std/special/compiler_rt/fixunstfsi_test.zig +++ /dev/null @@ -1,22 +0,0 @@ -const __fixunstfsi = @import("fixunstfsi.zig").__fixunstfsi; -const testing = @import("std").testing; - -fn test__fixunstfsi(a: f128, expected: u32) !void { - const x = __fixunstfsi(a); - try testing.expect(x == expected); -} - -const inf128 = @bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)); - -test "fixunstfsi" { - try test__fixunstfsi(inf128, 0xffffffff); - try test__fixunstfsi(0, 0x0); - try test__fixunstfsi(0x1.23456789abcdefp+5, 0x24); - try test__fixunstfsi(0x1.23456789abcdefp-3, 0x0); - try test__fixunstfsi(0x1.23456789abcdefp+20, 0x123456); - try test__fixunstfsi(0x1.23456789abcdefp+40, 0xffffffff); - try test__fixunstfsi(0x1.23456789abcdefp+256, 0xffffffff); - try test__fixunstfsi(-0x1.23456789abcdefp+3, 0x0); - - try test__fixunstfsi(0x1p+32, 0xFFFFFFFF); -} diff --git a/lib/std/special/compiler_rt/fixunstfti.zig b/lib/std/special/compiler_rt/fixunstfti.zig deleted file mode 100644 index 0359327027..0000000000 --- a/lib/std/special/compiler_rt/fixunstfti.zig +++ /dev/null @@ -1,11 +0,0 @@ -const fixuint = @import("fixuint.zig").fixuint; -const builtin = @import("builtin"); - -pub fn __fixunstfti(a: f128) callconv(.C) u128 { - @setRuntimeSafety(builtin.is_test); - return fixuint(f128, u128, a); -} - -test { - _ = @import("fixunstfti_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fixunstfti_test.zig b/lib/std/special/compiler_rt/fixunstfti_test.zig deleted file mode 100644 index e35e2a65be..0000000000 --- a/lib/std/special/compiler_rt/fixunstfti_test.zig +++ /dev/null @@ -1,32 +0,0 @@ -const __fixunstfti = @import("fixunstfti.zig").__fixunstfti; -const testing = @import("std").testing; - -fn test__fixunstfti(a: f128, expected: u128) !void { - const x = __fixunstfti(a); - try testing.expect(x == expected); -} - -const inf128 = @bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)); - -test "fixunstfti" { - try test__fixunstfti(inf128, 0xffffffffffffffffffffffffffffffff); - - try test__fixunstfti(0.0, 0); - - try test__fixunstfti(0.5, 0); - try test__fixunstfti(0.99, 0); - try test__fixunstfti(1.0, 1); - try test__fixunstfti(1.5, 1); - try test__fixunstfti(1.99, 1); - try test__fixunstfti(2.0, 2); - try test__fixunstfti(2.01, 2); - try test__fixunstfti(-0.01, 0); - try test__fixunstfti(-0.99, 0); - - try test__fixunstfti(0x1p+128, 0xffffffffffffffffffffffffffffffff); - - try test__fixunstfti(0x1.FFFFFEp+126, 0x7fffff80000000000000000000000000); - try test__fixunstfti(0x1.FFFFFEp+127, 0xffffff00000000000000000000000000); - try test__fixunstfti(0x1.FFFFFEp+128, 0xffffffffffffffffffffffffffffffff); - try test__fixunstfti(0x1.FFFFFEp+129, 0xffffffffffffffffffffffffffffffff); -} diff --git a/lib/std/special/compiler_rt/floatXiYf.zig b/lib/std/special/compiler_rt/floatXiYf.zig new file mode 100644 index 0000000000..c006a9df97 --- /dev/null +++ b/lib/std/special/compiler_rt/floatXiYf.zig @@ -0,0 +1,222 @@ +const builtin = @import("builtin"); +const is_test = builtin.is_test; +const std = @import("std"); +const math = std.math; +const expect = std.testing.expect; + +pub fn floatXiYf(comptime T: type, x: anytype) T { + @setRuntimeSafety(is_test); + + if (x == 0) return 0; + + // Various constants whose values follow from the type parameters. + // Any reasonable optimizer will fold and propagate all of these. + const Z = std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(x))); + const uT = std.meta.Int(.unsigned, @bitSizeOf(T)); + const inf = math.inf(T); + const float_bits = @bitSizeOf(T); + const int_bits = @bitSizeOf(@TypeOf(x)); + const exp_bits = math.floatExponentBits(T); + const sig_bits = math.floatMantissaDigits(T) - 1; // Only counts the fractional bits + const exp_bias = math.maxInt(std.meta.Int(.unsigned, exp_bits - 1)); + const implicit_bit = if (T != f80) @as(uT, 1) << sig_bits else 0; + const max_exp = exp_bias; + + // Sign + var abs_val = math.absCast(x); + const sign_bit = if (x < 0) @as(uT, 1) << (float_bits - 1) else 0; + var result: uT = sign_bit; + + // Compute significand + var exp = int_bits - @clz(Z, abs_val) - 1; + if (int_bits <= sig_bits or exp <= sig_bits) { + const shift_amt = sig_bits - @intCast(math.Log2Int(uT), exp); + + // Shift up result to line up with the significand - no rounding required + result = (@intCast(uT, abs_val) << shift_amt); + result ^= implicit_bit; // Remove implicit integer bit + } else { + var shift_amt = @intCast(math.Log2Int(Z), exp - sig_bits); + const exact_tie: bool = @ctz(Z, abs_val) == shift_amt - 1; + + // Shift down result and remove implicit integer bit + result = @intCast(uT, (abs_val >> (shift_amt - 1))) ^ (implicit_bit << 1); + + // Round result, including round-to-even for exact ties + result = ((result + 1) >> 1) & ~@as(uT, @boolToInt(exact_tie)); + } + + // Compute exponent + if ((int_bits > max_exp) and (exp > max_exp)) // If exponent too large, overflow to infinity + return @bitCast(T, sign_bit | @bitCast(uT, inf)); + + result += (@as(uT, exp) + exp_bias) << math.floatMantissaBits(T); + + // If the result included a carry, we need to restore the explicit integer bit + if (T == f80) result |= 1 << sig_bits; + + return @bitCast(T, sign_bit | result); +} + +// Conversion to f16 +pub fn __floatsihf(a: i32) callconv(.C) f16 { + return floatXiYf(f16, a); +} + +pub fn __floatunsihf(a: u32) callconv(.C) f16 { + return floatXiYf(f16, a); +} + +pub fn __floatdihf(a: i64) callconv(.C) f16 { + return floatXiYf(f16, a); +} + +pub fn __floatundihf(a: u64) callconv(.C) f16 { + return floatXiYf(f16, a); +} + +pub fn __floattihf(a: i128) callconv(.C) f16 { + return floatXiYf(f16, a); +} + +pub fn __floatuntihf(a: u128) callconv(.C) f16 { + return floatXiYf(f16, a); +} + +// Conversion to f32 +pub fn __floatsisf(a: i32) callconv(.C) f32 { + return floatXiYf(f32, a); +} + +pub fn __floatunsisf(a: u32) callconv(.C) f32 { + return floatXiYf(f32, a); +} + +pub fn __floatdisf(a: i64) callconv(.C) f32 { + return floatXiYf(f32, a); +} + +pub fn __floatundisf(a: u64) callconv(.C) f32 { + return floatXiYf(f32, a); +} + +pub fn __floattisf(a: i128) callconv(.C) f32 { + return floatXiYf(f32, a); +} + +pub fn __floatuntisf(a: u128) callconv(.C) f32 { + return floatXiYf(f32, a); +} + +// Conversion to f64 +pub fn __floatsidf(a: i32) callconv(.C) f64 { + return floatXiYf(f64, a); +} + +pub fn __floatunsidf(a: u32) callconv(.C) f64 { + return floatXiYf(f64, a); +} + +pub fn __floatdidf(a: i64) callconv(.C) f64 { + return floatXiYf(f64, a); +} + +pub fn __floatundidf(a: u64) callconv(.C) f64 { + return floatXiYf(f64, a); +} + +pub fn __floattidf(a: i128) callconv(.C) f64 { + return floatXiYf(f64, a); +} + +pub fn __floatuntidf(a: u128) callconv(.C) f64 { + return floatXiYf(f64, a); +} + +// Conversion to f80 +pub fn __floatsixf(a: i32) callconv(.C) f80 { + return floatXiYf(f80, a); +} + +pub fn __floatunsixf(a: u32) callconv(.C) f80 { + return floatXiYf(f80, a); +} + +pub fn __floatdixf(a: i64) callconv(.C) f80 { + return floatXiYf(f80, a); +} + +pub fn __floatundixf(a: u64) callconv(.C) f80 { + return floatXiYf(f80, a); +} + +pub fn __floattixf(a: i128) callconv(.C) f80 { + return floatXiYf(f80, a); +} + +pub fn __floatuntixf(a: u128) callconv(.C) f80 { + return floatXiYf(f80, a); +} + +// Conversion to f128 +pub fn __floatsitf(a: i32) callconv(.C) f128 { + return floatXiYf(f128, a); +} + +pub fn __floatunsitf(a: u32) callconv(.C) f128 { + return floatXiYf(f128, a); +} + +pub fn __floatditf(a: i64) callconv(.C) f128 { + return floatXiYf(f128, a); +} + +pub fn __floatunditf(a: u64) callconv(.C) f128 { + return floatXiYf(f128, a); +} + +pub fn __floattitf(a: i128) callconv(.C) f128 { + return floatXiYf(f128, a); +} + +pub fn __floatuntitf(a: u128) callconv(.C) f128 { + return floatXiYf(f128, a); +} + +// Conversion to f32 +pub fn __aeabi_ui2f(arg: u32) callconv(.AAPCS) f32 { + return floatXiYf(f32, arg); +} + +pub fn __aeabi_i2f(arg: i32) callconv(.AAPCS) f32 { + return floatXiYf(f32, arg); +} + +pub fn __aeabi_ul2f(arg: u64) callconv(.AAPCS) f32 { + return floatXiYf(f32, arg); +} + +pub fn __aeabi_l2f(arg: i64) callconv(.AAPCS) f32 { + return floatXiYf(f32, arg); +} + +// Conversion to f64 +pub fn __aeabi_ui2d(arg: u32) callconv(.AAPCS) f64 { + return floatXiYf(f64, arg); +} + +pub fn __aeabi_i2d(arg: i32) callconv(.AAPCS) f64 { + return floatXiYf(f64, arg); +} + +pub fn __aeabi_ul2d(arg: u64) callconv(.AAPCS) f64 { + return floatXiYf(f64, arg); +} + +pub fn __aeabi_l2d(arg: i64) callconv(.AAPCS) f64 { + return floatXiYf(f64, arg); +} + +test { + _ = @import("floatXiYf_test.zig"); +} diff --git a/lib/std/special/compiler_rt/floatXiYf_test.zig b/lib/std/special/compiler_rt/floatXiYf_test.zig new file mode 100644 index 0000000000..400ecaf57e --- /dev/null +++ b/lib/std/special/compiler_rt/floatXiYf_test.zig @@ -0,0 +1,831 @@ +const std = @import("std"); +const testing = std.testing; +const math = std.math; +const floatXiYf = @import("floatXiYf.zig").floatXiYf; + +// Conversion to f32 +const __floatsisf = @import("floatXiYf.zig").__floatsisf; +const __floatunsisf = @import("floatXiYf.zig").__floatunsisf; +const __floatdisf = @import("floatXiYf.zig").__floatdisf; +const __floatundisf = @import("floatXiYf.zig").__floatundisf; +const __floattisf = @import("floatXiYf.zig").__floattisf; +const __floatuntisf = @import("floatXiYf.zig").__floatuntisf; + +// Conversion to f64 +const __floatsidf = @import("floatXiYf.zig").__floatsidf; +const __floatunsidf = @import("floatXiYf.zig").__floatunsidf; +const __floatdidf = @import("floatXiYf.zig").__floatdidf; +const __floatundidf = @import("floatXiYf.zig").__floatundidf; +const __floattidf = @import("floatXiYf.zig").__floattidf; +const __floatuntidf = @import("floatXiYf.zig").__floatuntidf; + +// Conversion to f128 +const __floatsitf = @import("floatXiYf.zig").__floatsitf; +const __floatunsitf = @import("floatXiYf.zig").__floatunsitf; +const __floatditf = @import("floatXiYf.zig").__floatditf; +const __floatunditf = @import("floatXiYf.zig").__floatunditf; +const __floattitf = @import("floatXiYf.zig").__floattitf; +const __floatuntitf = @import("floatXiYf.zig").__floatuntitf; + +fn test__floatsisf(a: i32, expected: u32) !void { + const r = __floatsisf(a); + try std.testing.expect(@bitCast(u32, r) == expected); +} + +fn test_one_floatunsisf(a: u32, expected: u32) !void { + const r = __floatunsisf(a); + try std.testing.expect(@bitCast(u32, r) == expected); +} + +test "floatsisf" { + try test__floatsisf(0, 0x00000000); + try test__floatsisf(1, 0x3f800000); + try test__floatsisf(-1, 0xbf800000); + try test__floatsisf(0x7FFFFFFF, 0x4f000000); + try test__floatsisf(@bitCast(i32, @intCast(u32, 0x80000000)), 0xcf000000); +} + +test "floatunsisf" { + // Test the produced bit pattern + try test_one_floatunsisf(0, 0); + try test_one_floatunsisf(1, 0x3f800000); + try test_one_floatunsisf(0x7FFFFFFF, 0x4f000000); + try test_one_floatunsisf(0x80000000, 0x4f000000); + try test_one_floatunsisf(0xFFFFFFFF, 0x4f800000); +} + +fn test__floatdisf(a: i64, expected: f32) !void { + const x = __floatdisf(a); + try testing.expect(x == expected); +} + +fn test__floatundisf(a: u64, expected: f32) !void { + try std.testing.expectEqual(expected, __floatundisf(a)); +} + +test "floatdisf" { + try test__floatdisf(0, 0.0); + try test__floatdisf(1, 1.0); + try test__floatdisf(2, 2.0); + try test__floatdisf(-1, -1.0); + try test__floatdisf(-2, -2.0); + try test__floatdisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floatdisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + try test__floatdisf(@bitCast(i64, @as(u64, 0x8000008000000000)), -0x1.FFFFFEp+62); + try test__floatdisf(@bitCast(i64, @as(u64, 0x8000010000000000)), -0x1.FFFFFCp+62); + try test__floatdisf(@bitCast(i64, @as(u64, 0x8000000000000000)), -0x1.000000p+63); + try test__floatdisf(@bitCast(i64, @as(u64, 0x8000000000000001)), -0x1.000000p+63); + try test__floatdisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + try test__floatdisf(0x0007FB72EA000000, 0x1.FEDCBAp+50); + try test__floatdisf(0x0007FB72EB000000, 0x1.FEDCBAp+50); + try test__floatdisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50); + try test__floatdisf(0x0007FB72EC000000, 0x1.FEDCBCp+50); + try test__floatdisf(0x0007FB72E8000001, 0x1.FEDCBAp+50); + try test__floatdisf(0x0007FB72E6000000, 0x1.FEDCBAp+50); + try test__floatdisf(0x0007FB72E7000000, 0x1.FEDCBAp+50); + try test__floatdisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50); + try test__floatdisf(0x0007FB72E4000001, 0x1.FEDCBAp+50); + try test__floatdisf(0x0007FB72E4000000, 0x1.FEDCB8p+50); +} + +test "floatundisf" { + try test__floatundisf(0, 0.0); + try test__floatundisf(1, 1.0); + try test__floatundisf(2, 2.0); + try test__floatundisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floatundisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + try test__floatundisf(0x8000008000000000, 0x1p+63); + try test__floatundisf(0x8000010000000000, 0x1.000002p+63); + try test__floatundisf(0x8000000000000000, 0x1p+63); + try test__floatundisf(0x8000000000000001, 0x1p+63); + try test__floatundisf(0xFFFFFFFFFFFFFFFE, 0x1p+64); + try test__floatundisf(0xFFFFFFFFFFFFFFFF, 0x1p+64); + try test__floatundisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + try test__floatundisf(0x0007FB72EA000000, 0x1.FEDCBAp+50); + try test__floatundisf(0x0007FB72EB000000, 0x1.FEDCBAp+50); + try test__floatundisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50); + try test__floatundisf(0x0007FB72EC000000, 0x1.FEDCBCp+50); + try test__floatundisf(0x0007FB72E8000001, 0x1.FEDCBAp+50); + try test__floatundisf(0x0007FB72E6000000, 0x1.FEDCBAp+50); + try test__floatundisf(0x0007FB72E7000000, 0x1.FEDCBAp+50); + try test__floatundisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50); + try test__floatundisf(0x0007FB72E4000001, 0x1.FEDCBAp+50); + try test__floatundisf(0x0007FB72E4000000, 0x1.FEDCB8p+50); +} + +fn test__floattisf(a: i128, expected: f32) !void { + const x = __floattisf(a); + try testing.expect(x == expected); +} + +fn test__floatuntisf(a: u128, expected: f32) !void { + const x = __floatuntisf(a); + try testing.expect(x == expected); +} + +test "floattisf" { + try test__floattisf(0, 0.0); + + try test__floattisf(1, 1.0); + try test__floattisf(2, 2.0); + try test__floattisf(-1, -1.0); + try test__floattisf(-2, -2.0); + + try test__floattisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floattisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + + try test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000008000000000), -0x1.FFFFFEp+62); + try test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000010000000000), -0x1.FFFFFCp+62); + + try test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000000000000000), -0x1.000000p+63); + try test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000000000000001), -0x1.000000p+63); + + try test__floattisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + try test__floattisf(0x0007FB72EA000000, 0x1.FEDCBAp+50); + try test__floattisf(0x0007FB72EB000000, 0x1.FEDCBAp+50); + try test__floattisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50); + try test__floattisf(0x0007FB72EC000000, 0x1.FEDCBCp+50); + try test__floattisf(0x0007FB72E8000001, 0x1.FEDCBAp+50); + + try test__floattisf(0x0007FB72E6000000, 0x1.FEDCBAp+50); + try test__floattisf(0x0007FB72E7000000, 0x1.FEDCBAp+50); + try test__floattisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50); + try test__floattisf(0x0007FB72E4000001, 0x1.FEDCBAp+50); + try test__floattisf(0x0007FB72E4000000, 0x1.FEDCB8p+50); + + try test__floattisf(make_ti(0x0007FB72E8000000, 0), 0x1.FEDCBAp+114); + + try test__floattisf(make_ti(0x0007FB72EA000000, 0), 0x1.FEDCBAp+114); + try test__floattisf(make_ti(0x0007FB72EB000000, 0), 0x1.FEDCBAp+114); + try test__floattisf(make_ti(0x0007FB72EBFFFFFF, 0), 0x1.FEDCBAp+114); + try test__floattisf(make_ti(0x0007FB72EC000000, 0), 0x1.FEDCBCp+114); + try test__floattisf(make_ti(0x0007FB72E8000001, 0), 0x1.FEDCBAp+114); + + try test__floattisf(make_ti(0x0007FB72E6000000, 0), 0x1.FEDCBAp+114); + try test__floattisf(make_ti(0x0007FB72E7000000, 0), 0x1.FEDCBAp+114); + try test__floattisf(make_ti(0x0007FB72E7FFFFFF, 0), 0x1.FEDCBAp+114); + try test__floattisf(make_ti(0x0007FB72E4000001, 0), 0x1.FEDCBAp+114); + try test__floattisf(make_ti(0x0007FB72E4000000, 0), 0x1.FEDCB8p+114); +} + +test "floatuntisf" { + try test__floatuntisf(0, 0.0); + + try test__floatuntisf(1, 1.0); + try test__floatuntisf(2, 2.0); + try test__floatuntisf(20, 20.0); + + try test__floatuntisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floatuntisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + + try test__floatuntisf(make_uti(0x8000008000000000, 0), 0x1.000001p+127); + try test__floatuntisf(make_uti(0x8000000000000800, 0), 0x1.0p+127); + try test__floatuntisf(make_uti(0x8000010000000000, 0), 0x1.000002p+127); + + try test__floatuntisf(make_uti(0x8000000000000000, 0), 0x1.000000p+127); + + try test__floatuntisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + try test__floatuntisf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + try test__floatuntisf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + + try test__floatuntisf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + + try test__floatuntisf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + try test__floatuntisf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + try test__floatuntisf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + + try test__floatuntisf(0xFFFFFFFFFFFFFFFE, 0x1p+64); + try test__floatuntisf(0xFFFFFFFFFFFFFFFF, 0x1p+64); + + try test__floatuntisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + try test__floatuntisf(0x0007FB72EA000000, 0x1.FEDCBAp+50); + try test__floatuntisf(0x0007FB72EB000000, 0x1.FEDCBAp+50); + try test__floatuntisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50); + try test__floatuntisf(0x0007FB72EC000000, 0x1.FEDCBCp+50); + try test__floatuntisf(0x0007FB72E8000001, 0x1.FEDCBAp+50); + + try test__floatuntisf(0x0007FB72E6000000, 0x1.FEDCBAp+50); + try test__floatuntisf(0x0007FB72E7000000, 0x1.FEDCBAp+50); + try test__floatuntisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50); + try test__floatuntisf(0x0007FB72E4000001, 0x1.FEDCBAp+50); + try test__floatuntisf(0x0007FB72E4000000, 0x1.FEDCB8p+50); + + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCB90000000000001), 0x1.FEDCBAp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBA0000000000000), 0x1.FEDCBAp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBAFFFFFFFFFFFFF), 0x1.FEDCBAp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBB0000000000000), 0x1.FEDCBCp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBB0000000000001), 0x1.FEDCBCp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBBFFFFFFFFFFFFF), 0x1.FEDCBCp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBC0000000000000), 0x1.FEDCBCp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBC0000000000001), 0x1.FEDCBCp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBD0000000000000), 0x1.FEDCBCp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBD0000000000001), 0x1.FEDCBEp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBDFFFFFFFFFFFFF), 0x1.FEDCBEp+76); + try test__floatuntisf(make_uti(0x0000000000001FED, 0xCBE0000000000000), 0x1.FEDCBEp+76); + + // Test overflow to infinity + try test__floatuntisf(@as(u128, math.maxInt(u128)), @bitCast(f32, math.inf(f32))); +} + +fn test_one_floatsidf(a: i32, expected: u64) !void { + const r = __floatsidf(a); + try std.testing.expect(@bitCast(u64, r) == expected); +} + +fn test_one_floatunsidf(a: u32, expected: u64) !void { + const r = __floatunsidf(a); + try std.testing.expect(@bitCast(u64, r) == expected); +} + +test "floatsidf" { + try test_one_floatsidf(0, 0x0000000000000000); + try test_one_floatsidf(1, 0x3ff0000000000000); + try test_one_floatsidf(-1, 0xbff0000000000000); + try test_one_floatsidf(0x7FFFFFFF, 0x41dfffffffc00000); + try test_one_floatsidf(@bitCast(i32, @intCast(u32, 0x80000000)), 0xc1e0000000000000); +} + +test "floatunsidf" { + try test_one_floatunsidf(0, 0x0000000000000000); + try test_one_floatunsidf(1, 0x3ff0000000000000); + try test_one_floatunsidf(0x7FFFFFFF, 0x41dfffffffc00000); + try test_one_floatunsidf(@intCast(u32, 0x80000000), 0x41e0000000000000); + try test_one_floatunsidf(@intCast(u32, 0xFFFFFFFF), 0x41efffffffe00000); +} + +fn test__floatdidf(a: i64, expected: f64) !void { + const r = __floatdidf(a); + try testing.expect(r == expected); +} + +fn test__floatundidf(a: u64, expected: f64) !void { + const r = __floatundidf(a); + try testing.expect(r == expected); +} + +test "floatdidf" { + try test__floatdidf(0, 0.0); + try test__floatdidf(1, 1.0); + try test__floatdidf(2, 2.0); + try test__floatdidf(20, 20.0); + try test__floatdidf(-1, -1.0); + try test__floatdidf(-2, -2.0); + try test__floatdidf(-20, -20.0); + try test__floatdidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floatdidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); + try test__floatdidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + try test__floatdidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); + try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000008000000000)), -0x1.FFFFFEp+62); + try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000000800)), -0x1.FFFFFFFFFFFFEp+62); + try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000010000000000)), -0x1.FFFFFCp+62); + try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000001000)), -0x1.FFFFFFFFFFFFCp+62); + try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000000000)), -0x1.000000p+63); + try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000000001)), -0x1.000000p+63); // 0x8000000000000001 + try test__floatdidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + try test__floatdidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + try test__floatdidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + try test__floatdidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); + try test__floatdidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + try test__floatdidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); + try test__floatdidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + try test__floatdidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + try test__floatdidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); + try test__floatdidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); + try test__floatdidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + try test__floatdidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); + try test__floatdidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); + try test__floatdidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); + try test__floatdidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); + try test__floatdidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); + try test__floatdidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); +} + +test "floatundidf" { + try test__floatundidf(0, 0.0); + try test__floatundidf(1, 1.0); + try test__floatundidf(2, 2.0); + try test__floatundidf(20, 20.0); + try test__floatundidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floatundidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); + try test__floatundidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + try test__floatundidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); + try test__floatundidf(0x8000008000000000, 0x1.000001p+63); + try test__floatundidf(0x8000000000000800, 0x1.0000000000001p+63); + try test__floatundidf(0x8000010000000000, 0x1.000002p+63); + try test__floatundidf(0x8000000000001000, 0x1.0000000000002p+63); + try test__floatundidf(0x8000000000000000, 0x1p+63); + try test__floatundidf(0x8000000000000001, 0x1p+63); + try test__floatundidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + try test__floatundidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + try test__floatundidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + try test__floatundidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); + try test__floatundidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + try test__floatundidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); + try test__floatundidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + try test__floatundidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + try test__floatundidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); + try test__floatundidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); + try test__floatundidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + try test__floatundidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); + try test__floatundidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); + try test__floatundidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); + try test__floatundidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); + try test__floatundidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); + try test__floatundidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); +} + +fn test__floattidf(a: i128, expected: f64) !void { + const x = __floattidf(a); + try testing.expect(x == expected); +} + +fn test__floatuntidf(a: u128, expected: f64) !void { + const x = __floatuntidf(a); + try testing.expect(x == expected); +} + +test "floattidf" { + try test__floattidf(0, 0.0); + + try test__floattidf(1, 1.0); + try test__floattidf(2, 2.0); + try test__floattidf(20, 20.0); + try test__floattidf(-1, -1.0); + try test__floattidf(-2, -2.0); + try test__floattidf(-20, -20.0); + + try test__floattidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floattidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); + try test__floattidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + try test__floattidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); + + try test__floattidf(make_ti(0x8000008000000000, 0), -0x1.FFFFFEp+126); + try test__floattidf(make_ti(0x8000000000000800, 0), -0x1.FFFFFFFFFFFFEp+126); + try test__floattidf(make_ti(0x8000010000000000, 0), -0x1.FFFFFCp+126); + try test__floattidf(make_ti(0x8000000000001000, 0), -0x1.FFFFFFFFFFFFCp+126); + + try test__floattidf(make_ti(0x8000000000000000, 0), -0x1.000000p+127); + try test__floattidf(make_ti(0x8000000000000001, 0), -0x1.000000p+127); + + try test__floattidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + try test__floattidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + try test__floattidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + try test__floattidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); + try test__floattidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + try test__floattidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); + + try test__floattidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + try test__floattidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + try test__floattidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); + try test__floattidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); + try test__floattidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + + try test__floattidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); + try test__floattidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); + try test__floattidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); + try test__floattidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); + try test__floattidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); + try test__floattidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); + + try test__floattidf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496Dp+121); + try test__floattidf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496Ep+121); + try test__floattidf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496Fp+121); + try test__floattidf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496Fp+121); + try test__floattidf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496Fp+121); + try test__floattidf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); +} + +test "floatuntidf" { + try test__floatuntidf(0, 0.0); + + try test__floatuntidf(1, 1.0); + try test__floatuntidf(2, 2.0); + try test__floatuntidf(20, 20.0); + + try test__floatuntidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floatuntidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); + try test__floatuntidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + try test__floatuntidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); + + try test__floatuntidf(make_uti(0x8000008000000000, 0), 0x1.000001p+127); + try test__floatuntidf(make_uti(0x8000000000000800, 0), 0x1.0000000000001p+127); + try test__floatuntidf(make_uti(0x8000010000000000, 0), 0x1.000002p+127); + try test__floatuntidf(make_uti(0x8000000000001000, 0), 0x1.0000000000002p+127); + + try test__floatuntidf(make_uti(0x8000000000000000, 0), 0x1.000000p+127); + try test__floatuntidf(make_uti(0x8000000000000001, 0), 0x1.0000000000000002p+127); + + try test__floatuntidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + try test__floatuntidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + try test__floatuntidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + try test__floatuntidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); + try test__floatuntidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + try test__floatuntidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); + + try test__floatuntidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + try test__floatuntidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + try test__floatuntidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); + try test__floatuntidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); + try test__floatuntidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + + try test__floatuntidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); + try test__floatuntidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); + try test__floatuntidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); + try test__floatuntidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); + try test__floatuntidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); + try test__floatuntidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); + + try test__floatuntidf(make_uti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496Dp+121); + try test__floatuntidf(make_uti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496Ep+121); + try test__floatuntidf(make_uti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496Fp+121); + try test__floatuntidf(make_uti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496Fp+121); + try test__floatuntidf(make_uti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496Fp+121); + try test__floatuntidf(make_uti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); +} + +fn test__floatsitf(a: i32, expected: u128) !void { + const r = __floatsitf(a); + try std.testing.expect(@bitCast(u128, r) == expected); +} + +test "floatsitf" { + try test__floatsitf(0, 0); + try test__floatsitf(0x7FFFFFFF, 0x401dfffffffc00000000000000000000); + try test__floatsitf(0x12345678, 0x401b2345678000000000000000000000); + try test__floatsitf(-0x12345678, 0xc01b2345678000000000000000000000); + try test__floatsitf(@bitCast(i32, @intCast(u32, 0xffffffff)), 0xbfff0000000000000000000000000000); + try test__floatsitf(@bitCast(i32, @intCast(u32, 0x80000000)), 0xc01e0000000000000000000000000000); +} + +fn test__floatunsitf(a: u32, expected_hi: u64, expected_lo: u64) !void { + const x = __floatunsitf(a); + + const x_repr = @bitCast(u128, x); + const x_hi = @intCast(u64, x_repr >> 64); + const x_lo = @truncate(u64, x_repr); + + if (x_hi == expected_hi and x_lo == expected_lo) { + return; + } + // nan repr + else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { + if ((x_hi & 0x7fff000000000000) == 0x7fff000000000000 and ((x_hi & 0xffffffffffff) > 0 or x_lo > 0)) { + return; + } + } + + @panic("__floatunsitf test failure"); +} + +test "floatunsitf" { + try test__floatunsitf(0x7fffffff, 0x401dfffffffc0000, 0x0); + try test__floatunsitf(0, 0x0, 0x0); + try test__floatunsitf(0xffffffff, 0x401efffffffe0000, 0x0); + try test__floatunsitf(0x12345678, 0x401b234567800000, 0x0); +} + +fn test__floatditf(a: i64, expected: f128) !void { + const x = __floatditf(a); + try testing.expect(x == expected); +} + +fn test__floatunditf(a: u64, expected_hi: u64, expected_lo: u64) !void { + const x = __floatunditf(a); + + const x_repr = @bitCast(u128, x); + const x_hi = @intCast(u64, x_repr >> 64); + const x_lo = @truncate(u64, x_repr); + + if (x_hi == expected_hi and x_lo == expected_lo) { + return; + } + // nan repr + else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { + if ((x_hi & 0x7fff000000000000) == 0x7fff000000000000 and ((x_hi & 0xffffffffffff) > 0 or x_lo > 0)) { + return; + } + } + + @panic("__floatunditf test failure"); +} + +test "floatditf" { + try test__floatditf(0x7fffffffffffffff, make_tf(0x403dffffffffffff, 0xfffc000000000000)); + try test__floatditf(0x123456789abcdef1, make_tf(0x403b23456789abcd, 0xef10000000000000)); + try test__floatditf(0x2, make_tf(0x4000000000000000, 0x0)); + try test__floatditf(0x1, make_tf(0x3fff000000000000, 0x0)); + try test__floatditf(0x0, make_tf(0x0, 0x0)); + try test__floatditf(@bitCast(i64, @as(u64, 0xffffffffffffffff)), make_tf(0xbfff000000000000, 0x0)); + try test__floatditf(@bitCast(i64, @as(u64, 0xfffffffffffffffe)), make_tf(0xc000000000000000, 0x0)); + try test__floatditf(-0x123456789abcdef1, make_tf(0xc03b23456789abcd, 0xef10000000000000)); + try test__floatditf(@bitCast(i64, @as(u64, 0x8000000000000000)), make_tf(0xc03e000000000000, 0x0)); +} + +test "floatunditf" { + try test__floatunditf(0xffffffffffffffff, 0x403effffffffffff, 0xfffe000000000000); + try test__floatunditf(0xfffffffffffffffe, 0x403effffffffffff, 0xfffc000000000000); + try test__floatunditf(0x8000000000000000, 0x403e000000000000, 0x0); + try test__floatunditf(0x7fffffffffffffff, 0x403dffffffffffff, 0xfffc000000000000); + try test__floatunditf(0x123456789abcdef1, 0x403b23456789abcd, 0xef10000000000000); + try test__floatunditf(0x2, 0x4000000000000000, 0x0); + try test__floatunditf(0x1, 0x3fff000000000000, 0x0); + try test__floatunditf(0x0, 0x0, 0x0); +} + +fn test__floattitf(a: i128, expected: f128) !void { + const x = __floattitf(a); + try testing.expect(x == expected); +} + +fn test__floatuntitf(a: u128, expected: f128) !void { + const x = __floatuntitf(a); + try testing.expect(x == expected); +} + +test "floattitf" { + try test__floattitf(0, 0.0); + + try test__floattitf(1, 1.0); + try test__floattitf(2, 2.0); + try test__floattitf(20, 20.0); + try test__floattitf(-1, -1.0); + try test__floattitf(-2, -2.0); + try test__floattitf(-20, -20.0); + + try test__floattitf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floattitf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); + try test__floattitf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + try test__floattitf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); + + try test__floattitf(make_ti(0x8000008000000000, 0), -0x1.FFFFFEp+126); + try test__floattitf(make_ti(0x8000000000000800, 0), -0x1.FFFFFFFFFFFFEp+126); + try test__floattitf(make_ti(0x8000010000000000, 0), -0x1.FFFFFCp+126); + try test__floattitf(make_ti(0x8000000000001000, 0), -0x1.FFFFFFFFFFFFCp+126); + + try test__floattitf(make_ti(0x8000000000000000, 0), -0x1.000000p+127); + try test__floattitf(make_ti(0x8000000000000001, 0), -0x1.FFFFFFFFFFFFFFFCp+126); + + try test__floattitf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + try test__floattitf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + try test__floattitf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + try test__floattitf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); + try test__floattitf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + try test__floattitf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); + + try test__floattitf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + try test__floattitf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + try test__floattitf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); + try test__floattitf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); + try test__floattitf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + + try test__floattitf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); + try test__floattitf(0x023479FD0E092DA1, 0x1.1A3CFE870496D08p+57); + try test__floattitf(0x023479FD0E092DB0, 0x1.1A3CFE870496D8p+57); + try test__floattitf(0x023479FD0E092DB8, 0x1.1A3CFE870496DCp+57); + try test__floattitf(0x023479FD0E092DB6, 0x1.1A3CFE870496DBp+57); + try test__floattitf(0x023479FD0E092DBF, 0x1.1A3CFE870496DF8p+57); + try test__floattitf(0x023479FD0E092DC1, 0x1.1A3CFE870496E08p+57); + try test__floattitf(0x023479FD0E092DC7, 0x1.1A3CFE870496E38p+57); + try test__floattitf(0x023479FD0E092DC8, 0x1.1A3CFE870496E4p+57); + try test__floattitf(0x023479FD0E092DCF, 0x1.1A3CFE870496E78p+57); + try test__floattitf(0x023479FD0E092DD0, 0x1.1A3CFE870496E8p+57); + try test__floattitf(0x023479FD0E092DD1, 0x1.1A3CFE870496E88p+57); + try test__floattitf(0x023479FD0E092DD8, 0x1.1A3CFE870496ECp+57); + try test__floattitf(0x023479FD0E092DDF, 0x1.1A3CFE870496EF8p+57); + try test__floattitf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); + + try test__floattitf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); + try test__floattitf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496D08p+121); + try test__floattitf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496D8p+121); + try test__floattitf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496DCp+121); + try test__floattitf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496DBp+121); + try test__floattitf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496DF8p+121); + try test__floattitf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496E08p+121); + try test__floattitf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496E38p+121); + try test__floattitf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496E4p+121); + try test__floattitf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496E78p+121); + try test__floattitf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496E8p+121); + try test__floattitf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496E88p+121); + try test__floattitf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496ECp+121); + try test__floattitf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496EF8p+121); + try test__floattitf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); + + try test__floattitf(make_ti(0, 0xFFFFFFFFFFFFFFFF), 0x1.FFFFFFFFFFFFFFFEp+63); + + try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC2801), 0x1.23456789ABCDEF0123456789ABC3p+124); + try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3000), 0x1.23456789ABCDEF0123456789ABC3p+124); + try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC37FF), 0x1.23456789ABCDEF0123456789ABC3p+124); + try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3800), 0x1.23456789ABCDEF0123456789ABC4p+124); + try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4000), 0x1.23456789ABCDEF0123456789ABC4p+124); + try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC47FF), 0x1.23456789ABCDEF0123456789ABC4p+124); + try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4800), 0x1.23456789ABCDEF0123456789ABC4p+124); + try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4801), 0x1.23456789ABCDEF0123456789ABC5p+124); + try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC57FF), 0x1.23456789ABCDEF0123456789ABC5p+124); +} + +test "floatuntitf" { + try test__floatuntitf(0, 0.0); + + try test__floatuntitf(1, 1.0); + try test__floatuntitf(2, 2.0); + try test__floatuntitf(20, 20.0); + + try test__floatuntitf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + try test__floatuntitf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); + try test__floatuntitf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + try test__floatuntitf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); + try test__floatuntitf(0x7FFFFFFFFFFFFFFF, 0xF.FFFFFFFFFFFFFFEp+59); + try test__floatuntitf(0xFFFFFFFFFFFFFFFE, 0xF.FFFFFFFFFFFFFFEp+60); + try test__floatuntitf(0xFFFFFFFFFFFFFFFF, 0xF.FFFFFFFFFFFFFFFp+60); + + try test__floatuntitf(0x8000008000000000, 0x8.000008p+60); + try test__floatuntitf(0x8000000000000800, 0x8.0000000000008p+60); + try test__floatuntitf(0x8000010000000000, 0x8.00001p+60); + try test__floatuntitf(0x8000000000001000, 0x8.000000000001p+60); + + try test__floatuntitf(0x8000000000000000, 0x8p+60); + try test__floatuntitf(0x8000000000000001, 0x8.000000000000001p+60); + + try test__floatuntitf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + try test__floatuntitf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + try test__floatuntitf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + try test__floatuntitf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); + try test__floatuntitf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + try test__floatuntitf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); + + try test__floatuntitf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + try test__floatuntitf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + try test__floatuntitf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); + try test__floatuntitf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); + try test__floatuntitf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + + try test__floatuntitf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); + try test__floatuntitf(0x023479FD0E092DA1, 0x1.1A3CFE870496D08p+57); + try test__floatuntitf(0x023479FD0E092DB0, 0x1.1A3CFE870496D8p+57); + try test__floatuntitf(0x023479FD0E092DB8, 0x1.1A3CFE870496DCp+57); + try test__floatuntitf(0x023479FD0E092DB6, 0x1.1A3CFE870496DBp+57); + try test__floatuntitf(0x023479FD0E092DBF, 0x1.1A3CFE870496DF8p+57); + try test__floatuntitf(0x023479FD0E092DC1, 0x1.1A3CFE870496E08p+57); + try test__floatuntitf(0x023479FD0E092DC7, 0x1.1A3CFE870496E38p+57); + try test__floatuntitf(0x023479FD0E092DC8, 0x1.1A3CFE870496E4p+57); + try test__floatuntitf(0x023479FD0E092DCF, 0x1.1A3CFE870496E78p+57); + try test__floatuntitf(0x023479FD0E092DD0, 0x1.1A3CFE870496E8p+57); + try test__floatuntitf(0x023479FD0E092DD1, 0x1.1A3CFE870496E88p+57); + try test__floatuntitf(0x023479FD0E092DD8, 0x1.1A3CFE870496ECp+57); + try test__floatuntitf(0x023479FD0E092DDF, 0x1.1A3CFE870496EF8p+57); + try test__floatuntitf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); + + try test__floatuntitf(make_uti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); + try test__floatuntitf(make_uti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496D08p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496D8p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496DCp+121); + try test__floatuntitf(make_uti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496DBp+121); + try test__floatuntitf(make_uti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496DF8p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496E08p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496E38p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496E4p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496E78p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496E8p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496E88p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496ECp+121); + try test__floatuntitf(make_uti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496EF8p+121); + try test__floatuntitf(make_uti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); + + try test__floatuntitf(make_uti(0, 0xFFFFFFFFFFFFFFFF), 0x1.FFFFFFFFFFFFFFFEp+63); + + try test__floatuntitf(make_uti(0xFFFFFFFFFFFFFFFF, 0x0000000000000000), 0x1.FFFFFFFFFFFFFFFEp+127); + try test__floatuntitf(make_uti(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), 0x1.0000000000000000p+128); + + try test__floatuntitf(make_uti(0x123456789ABCDEF0, 0x123456789ABC2801), 0x1.23456789ABCDEF0123456789ABC3p+124); + try test__floatuntitf(make_uti(0x123456789ABCDEF0, 0x123456789ABC3000), 0x1.23456789ABCDEF0123456789ABC3p+124); + try test__floatuntitf(make_uti(0x123456789ABCDEF0, 0x123456789ABC37FF), 0x1.23456789ABCDEF0123456789ABC3p+124); + try test__floatuntitf(make_uti(0x123456789ABCDEF0, 0x123456789ABC3800), 0x1.23456789ABCDEF0123456789ABC4p+124); + try test__floatuntitf(make_uti(0x123456789ABCDEF0, 0x123456789ABC4000), 0x1.23456789ABCDEF0123456789ABC4p+124); + try test__floatuntitf(make_uti(0x123456789ABCDEF0, 0x123456789ABC47FF), 0x1.23456789ABCDEF0123456789ABC4p+124); + try test__floatuntitf(make_uti(0x123456789ABCDEF0, 0x123456789ABC4800), 0x1.23456789ABCDEF0123456789ABC4p+124); + try test__floatuntitf(make_uti(0x123456789ABCDEF0, 0x123456789ABC4801), 0x1.23456789ABCDEF0123456789ABC5p+124); + try test__floatuntitf(make_uti(0x123456789ABCDEF0, 0x123456789ABC57FF), 0x1.23456789ABCDEF0123456789ABC5p+124); +} + +fn make_ti(high: u64, low: u64) i128 { + var result: u128 = high; + result <<= 64; + result |= low; + return @bitCast(i128, result); +} + +fn make_uti(high: u64, low: u64) u128 { + var result: u128 = high; + result <<= 64; + result |= low; + return result; +} + +fn make_tf(high: u64, low: u64) f128 { + var result: u128 = high; + result <<= 64; + result |= low; + return @bitCast(f128, result); +} + +test "conversion to f16" { + try testing.expect(floatXiYf(f16, @as(u32, 0)) == 0.0); + try testing.expect(floatXiYf(f16, @as(u32, 1)) == 1.0); + try testing.expect(floatXiYf(f16, @as(u32, 65504)) == 65504); + try testing.expect(floatXiYf(f16, @as(u32, 65504 + (1 << 4))) == math.inf(f16)); +} + +test "conversion to f32" { + try testing.expect(floatXiYf(f32, @as(u32, 0)) == 0.0); + try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u32))) != 1.0); + try testing.expect(floatXiYf(f32, @as(i32, math.minInt(i32))) != 1.0); + try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u24))) == math.maxInt(u24)); + try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u24)) + 1) == math.maxInt(u24) + 1); // 0x100_0000 - Exact + try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u24)) + 2) == math.maxInt(u24) + 1); // 0x100_0001 - Tie: Rounds down to even + try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u24)) + 3) == math.maxInt(u24) + 3); // 0x100_0002 - Exact + try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u24)) + 4) == math.maxInt(u24) + 5); // 0x100_0003 - Tie: Rounds up to even + try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u24)) + 5) == math.maxInt(u24) + 5); // 0x100_0004 - Exact +} + +test "conversion to f80" { + try testing.expect(floatXiYf(f80, @as(i80, -12)) == -12); + try testing.expect(@floatToInt(u80, floatXiYf(f80, @as(u64, math.maxInt(u64)) + 0)) == math.maxInt(u64) + 0); + try testing.expect(@floatToInt(u80, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 1)) == math.maxInt(u64) + 1); + + try testing.expect(floatXiYf(f80, @as(u32, 0)) == 0.0); + try testing.expect(floatXiYf(f80, @as(u32, 1)) == 1.0); + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u32, math.maxInt(u24)) + 0)) == math.maxInt(u24)); + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 0)) == math.maxInt(u64)); + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 1)) == math.maxInt(u64) + 1); // Exact + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 2)) == math.maxInt(u64) + 1); // Rounds down + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 3)) == math.maxInt(u64) + 3); // Tie - Exact + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 4)) == math.maxInt(u64) + 5); // Rounds up + + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u65)) + 0)) == math.maxInt(u65) + 1); // Rounds up + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u65)) + 1)) == math.maxInt(u65) + 1); // Exact + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u65)) + 2)) == math.maxInt(u65) + 1); // Rounds down + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u65)) + 3)) == math.maxInt(u65) + 1); // Tie - Rounds down + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u65)) + 4)) == math.maxInt(u65) + 5); // Rounds up + try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u65)) + 5)) == math.maxInt(u65) + 5); // Exact +} diff --git a/lib/std/special/compiler_rt/floatXisf.zig b/lib/std/special/compiler_rt/floatXisf.zig deleted file mode 100644 index de3f4495cb..0000000000 --- a/lib/std/special/compiler_rt/floatXisf.zig +++ /dev/null @@ -1,90 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const maxInt = std.math.maxInt; - -const FLT_MANT_DIG = 24; - -inline fn floatXisf(comptime T: type, arg: T) f32 { - @setRuntimeSafety(builtin.is_test); - - const bits = @typeInfo(T).Int.bits; - const Z = std.meta.Int(.unsigned, bits); - const S = std.meta.Int(.unsigned, bits - @clz(Z, @as(Z, bits) - 1)); - - if (arg == 0) { - return @as(f32, 0.0); - } - - var ai = arg; - const N: u32 = bits; - const si = ai >> @intCast(S, (N - 1)); - ai = ((ai ^ si) -% si); - var a = @bitCast(Z, ai); - - const sd = @bitCast(i32, N - @clz(Z, a)); // number of significant digits - var e: i32 = sd - 1; // exponent - - if (sd > FLT_MANT_DIG) { - // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx - // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR - // 12345678901234567890123456 - // 1 = msb 1 bit - // P = bit FLT_MANT_DIG-1 bits to the right of 1 - // Q = bit FLT_MANT_DIG bits to the right of 1 - // R = "or" of all bits to the right of Q - switch (sd) { - FLT_MANT_DIG + 1 => { - a <<= 1; - }, - FLT_MANT_DIG + 2 => {}, - else => { - const shift1_amt = @intCast(i32, sd - (FLT_MANT_DIG + 2)); - const shift1_amt_u7 = @intCast(S, shift1_amt); - - const shift2_amt = @intCast(i32, N + (FLT_MANT_DIG + 2)) - sd; - const shift2_amt_u7 = @intCast(S, shift2_amt); - - a = (a >> shift1_amt_u7) | @boolToInt((a & (@intCast(Z, maxInt(Z)) >> shift2_amt_u7)) != 0); - }, - } - // finish - a |= @boolToInt((a & 4) != 0); // Or P into R - a += 1; // round - this step may add a significant bit - a >>= 2; // dump Q and R - // a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits - if ((a & (@as(Z, 1) << FLT_MANT_DIG)) != 0) { - a >>= 1; - e += 1; - } - // a is now rounded to FLT_MANT_DIG bits - } else { - a <<= @intCast(S, FLT_MANT_DIG - sd); - // a is now rounded to FLT_MANT_DIG bits - } - - const s = @bitCast(Z, arg) >> (@typeInfo(T).Int.bits - 32); - const r = (@intCast(u32, s) & 0x80000000) | // sign - (@intCast(u32, (e + 127)) << 23) | // exponent - (@truncate(u32, a) & 0x007fffff); // mantissa-high - - return @bitCast(f32, r); -} - -pub fn __floatdisf(arg: i64) callconv(.C) f32 { - return floatXisf(i64, arg); -} - -pub fn __floattisf(arg: i128) callconv(.C) f32 { - return floatXisf(i128, arg); -} - -pub fn __aeabi_l2f(arg: i64) callconv(.AAPCS) f32 { - return floatXisf(i64, arg); -} - -test { - _ = @import("floattisf_test.zig"); -} -test { - _ = @import("floattisf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floatdidf.zig b/lib/std/special/compiler_rt/floatdidf.zig deleted file mode 100644 index 16a514b615..0000000000 --- a/lib/std/special/compiler_rt/floatdidf.zig +++ /dev/null @@ -1,27 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); - -const twop52: f64 = 0x1.0p52; -const twop32: f64 = 0x1.0p32; - -pub fn __floatdidf(a: i64) callconv(.C) f64 { - @setRuntimeSafety(builtin.is_test); - - if (a == 0) return 0; - - var low = @bitCast(i64, twop52); - const high = @intToFloat(f64, @truncate(i32, a >> 32)) * twop32; - - low |= @bitCast(i64, a & 0xFFFFFFFF); - - return (high - twop52) + @bitCast(f64, low); -} - -pub fn __aeabi_l2d(arg: i64) callconv(.AAPCS) f64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __floatdidf, .{arg}); -} - -test { - _ = @import("floatdidf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floatdidf_test.zig b/lib/std/special/compiler_rt/floatdidf_test.zig deleted file mode 100644 index 6b01ac5f3f..0000000000 --- a/lib/std/special/compiler_rt/floatdidf_test.zig +++ /dev/null @@ -1,53 +0,0 @@ -const __floatdidf = @import("floatdidf.zig").__floatdidf; -const testing = @import("std").testing; - -fn test__floatdidf(a: i64, expected: f64) !void { - const r = __floatdidf(a); - try testing.expect(r == expected); -} - -test "floatdidf" { - try test__floatdidf(0, 0.0); - try test__floatdidf(1, 1.0); - try test__floatdidf(2, 2.0); - try test__floatdidf(20, 20.0); - try test__floatdidf(-1, -1.0); - try test__floatdidf(-2, -2.0); - try test__floatdidf(-20, -20.0); - try test__floatdidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floatdidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); - try test__floatdidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - try test__floatdidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); - try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000008000000000)), -0x1.FFFFFEp+62); - try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000000800)), -0x1.FFFFFFFFFFFFEp+62); - try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000010000000000)), -0x1.FFFFFCp+62); - try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000001000)), -0x1.FFFFFFFFFFFFCp+62); - try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000000000)), -0x1.000000p+63); - try test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000000001)), -0x1.000000p+63); - try test__floatdidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - try test__floatdidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); - try test__floatdidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); - try test__floatdidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); - try test__floatdidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); - try test__floatdidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); - try test__floatdidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); - try test__floatdidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); - try test__floatdidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); - try test__floatdidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); - try test__floatdidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); - try test__floatdidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); - try test__floatdidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); - try test__floatdidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); - try test__floatdidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); - try test__floatdidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); - try test__floatdidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); -} diff --git a/lib/std/special/compiler_rt/floatdisf_test.zig b/lib/std/special/compiler_rt/floatdisf_test.zig deleted file mode 100644 index 010c4faddd..0000000000 --- a/lib/std/special/compiler_rt/floatdisf_test.zig +++ /dev/null @@ -1,32 +0,0 @@ -const __floatdisf = @import("floatXisf.zig").__floatdisf; -const testing = @import("std").testing; - -fn test__floatdisf(a: i64, expected: f32) !void { - const x = __floatdisf(a); - try testing.expect(x == expected); -} - -test "floatdisf" { - try test__floatdisf(0, 0.0); - try test__floatdisf(1, 1.0); - try test__floatdisf(2, 2.0); - try test__floatdisf(-1, -1.0); - try test__floatdisf(-2, -2.0); - try test__floatdisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floatdisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - try test__floatdisf(0x8000008000000000, -0x1.FFFFFEp+62); - try test__floatdisf(0x8000010000000000, -0x1.FFFFFCp+62); - try test__floatdisf(0x8000000000000000, -0x1.000000p+63); - try test__floatdisf(0x8000000000000001, -0x1.000000p+63); - try test__floatdisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - try test__floatdisf(0x0007FB72EA000000, 0x1.FEDCBAp+50); - try test__floatdisf(0x0007FB72EB000000, 0x1.FEDCBAp+50); - try test__floatdisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50); - try test__floatdisf(0x0007FB72EC000000, 0x1.FEDCBCp+50); - try test__floatdisf(0x0007FB72E8000001, 0x1.FEDCBAp+50); - try test__floatdisf(0x0007FB72E6000000, 0x1.FEDCBAp+50); - try test__floatdisf(0x0007FB72E7000000, 0x1.FEDCBAp+50); - try test__floatdisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50); - try test__floatdisf(0x0007FB72E4000001, 0x1.FEDCBAp+50); - try test__floatdisf(0x0007FB72E4000000, 0x1.FEDCB8p+50); -} diff --git a/lib/std/special/compiler_rt/floatditf.zig b/lib/std/special/compiler_rt/floatditf.zig deleted file mode 100644 index fef696e6fb..0000000000 --- a/lib/std/special/compiler_rt/floatditf.zig +++ /dev/null @@ -1,38 +0,0 @@ -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const std = @import("std"); -const maxInt = std.math.maxInt; - -const significandBits = 112; -const exponentBias = 16383; -const implicitBit = (@as(u128, 1) << significandBits); - -pub fn __floatditf(arg: i64) callconv(.C) f128 { - @setRuntimeSafety(is_test); - - if (arg == 0) - return 0.0; - - // All other cases begin by extracting the sign and absolute value of a - var sign: u128 = 0; - var aAbs = @bitCast(u64, arg); - if (arg < 0) { - sign = 1 << 127; - aAbs = ~@bitCast(u64, arg) + 1; - } - - // Exponent of (fp_t)a is the width of abs(a). - const exponent = 63 - @clz(u64, aAbs); - var result: u128 = undefined; - - // Shift a into the significand field, rounding if it is a right-shift - const shift = significandBits - exponent; - result = @as(u128, aAbs) << shift ^ implicitBit; - - result += (@as(u128, exponent) + exponentBias) << significandBits; - return @bitCast(f128, result | sign); -} - -test { - _ = @import("floatditf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floatditf_test.zig b/lib/std/special/compiler_rt/floatditf_test.zig deleted file mode 100644 index cf8a81c68c..0000000000 --- a/lib/std/special/compiler_rt/floatditf_test.zig +++ /dev/null @@ -1,26 +0,0 @@ -const __floatditf = @import("floatditf.zig").__floatditf; -const testing = @import("std").testing; - -fn test__floatditf(a: i64, expected: f128) !void { - const x = __floatditf(a); - try testing.expect(x == expected); -} - -test "floatditf" { - try test__floatditf(0x7fffffffffffffff, make_ti(0x403dffffffffffff, 0xfffc000000000000)); - try test__floatditf(0x123456789abcdef1, make_ti(0x403b23456789abcd, 0xef10000000000000)); - try test__floatditf(0x2, make_ti(0x4000000000000000, 0x0)); - try test__floatditf(0x1, make_ti(0x3fff000000000000, 0x0)); - try test__floatditf(0x0, make_ti(0x0, 0x0)); - try test__floatditf(@bitCast(i64, @as(u64, 0xffffffffffffffff)), make_ti(0xbfff000000000000, 0x0)); - try test__floatditf(@bitCast(i64, @as(u64, 0xfffffffffffffffe)), make_ti(0xc000000000000000, 0x0)); - try test__floatditf(-0x123456789abcdef1, make_ti(0xc03b23456789abcd, 0xef10000000000000)); - try test__floatditf(@bitCast(i64, @as(u64, 0x8000000000000000)), make_ti(0xc03e000000000000, 0x0)); -} - -fn make_ti(high: u64, low: u64) f128 { - var result: u128 = high; - result <<= 64; - result |= low; - return @bitCast(f128, result); -} diff --git a/lib/std/special/compiler_rt/floatfmodl_test.zig b/lib/std/special/compiler_rt/floatfmodl_test.zig deleted file mode 100644 index 22b981d5f2..0000000000 --- a/lib/std/special/compiler_rt/floatfmodl_test.zig +++ /dev/null @@ -1,46 +0,0 @@ -const std = @import("std"); -const fmodl = @import("floatfmodl.zig"); -const testing = std.testing; - -fn test_fmodl(a: f128, b: f128, exp: f128) !void { - const res = fmodl.fmodl(a, b); - try testing.expect(exp == res); -} - -fn test_fmodl_nans() !void { - try testing.expect(std.math.isNan(fmodl.fmodl(1.0, std.math.nan_f128))); - try testing.expect(std.math.isNan(fmodl.fmodl(1.0, -std.math.nan_f128))); - try testing.expect(std.math.isNan(fmodl.fmodl(std.math.nan_f128, 1.0))); - try testing.expect(std.math.isNan(fmodl.fmodl(-std.math.nan_f128, 1.0))); -} - -fn test_fmodl_infs() !void { - try testing.expect(fmodl.fmodl(1.0, std.math.inf(f128)) == 1.0); - try testing.expect(fmodl.fmodl(1.0, -std.math.inf(f128)) == 1.0); - try testing.expect(std.math.isNan(fmodl.fmodl(std.math.inf(f128), 1.0))); - try testing.expect(std.math.isNan(fmodl.fmodl(-std.math.inf(f128), 1.0))); -} - -test "fmodl" { - try test_fmodl(6.8, 4.0, 2.8); - try test_fmodl(6.8, -4.0, 2.8); - try test_fmodl(-6.8, 4.0, -2.8); - try test_fmodl(-6.8, -4.0, -2.8); - try test_fmodl(3.0, 2.0, 1.0); - try test_fmodl(-5.0, 3.0, -2.0); - try test_fmodl(3.0, 2.0, 1.0); - try test_fmodl(1.0, 2.0, 1.0); - try test_fmodl(0.0, 1.0, 0.0); - try test_fmodl(-0.0, 1.0, -0.0); - try test_fmodl(7046119.0, 5558362.0, 1487757.0); - try test_fmodl(9010357.0, 1957236.0, 1181413.0); - - // Denormals - const a: f128 = 0xedcb34a235253948765432134674p-16494; - const b: f128 = 0x5d2e38791cfbc0737402da5a9518p-16494; - const exp: f128 = 0x336ec3affb2db8618e4e7d5e1c44p-16494; - try test_fmodl(a, b, exp); - - try test_fmodl_nans(); - try test_fmodl_infs(); -} diff --git a/lib/std/special/compiler_rt/floatfmodl.zig b/lib/std/special/compiler_rt/floatfmodq.zig similarity index 93% rename from lib/std/special/compiler_rt/floatfmodl.zig rename to lib/std/special/compiler_rt/floatfmodq.zig index 942a7c1125..b8da727c90 100644 --- a/lib/std/special/compiler_rt/floatfmodl.zig +++ b/lib/std/special/compiler_rt/floatfmodq.zig @@ -1,9 +1,9 @@ const builtin = @import("builtin"); const std = @import("std"); -// fmodl - floating modulo large, returns the remainder of division for f128 types +// fmodq - floating modulo large, returns the remainder of division for f128 types // Logic and flow heavily inspired by MUSL fmodl for 113 mantissa digits -pub fn fmodl(a: f128, b: f128) callconv(.C) f128 { +pub fn fmodq(a: f128, b: f128) callconv(.C) f128 { @setRuntimeSafety(builtin.is_test); var amod = a; var bmod = b; @@ -30,9 +30,9 @@ pub fn fmodl(a: f128, b: f128) callconv(.C) f128 { var expB = bPtr_u16[exp_and_sign_index] & 0x7fff; // There are 3 cases where the answer is undefined, check for: - // - fmodl(val, 0) - // - fmodl(val, NaN) - // - fmodl(inf, val) + // - fmodq(val, 0) + // - fmodq(val, NaN) + // - fmodq(inf, val) // The sign on checked values does not matter. // Doing (a * b) / (a * b) procudes undefined results // because the three cases always produce undefined calculations: @@ -122,5 +122,5 @@ pub fn fmodl(a: f128, b: f128) callconv(.C) f128 { } test { - _ = @import("floatfmodl_test.zig"); + _ = @import("floatfmodq_test.zig"); } diff --git a/lib/std/special/compiler_rt/floatfmodq_test.zig b/lib/std/special/compiler_rt/floatfmodq_test.zig new file mode 100644 index 0000000000..a272b797e3 --- /dev/null +++ b/lib/std/special/compiler_rt/floatfmodq_test.zig @@ -0,0 +1,46 @@ +const std = @import("std"); +const fmodq = @import("floatfmodq.zig"); +const testing = std.testing; + +fn test_fmodq(a: f128, b: f128, exp: f128) !void { + const res = fmodq.fmodq(a, b); + try testing.expect(exp == res); +} + +fn test_fmodq_nans() !void { + try testing.expect(std.math.isNan(fmodq.fmodq(1.0, std.math.nan(f128)))); + try testing.expect(std.math.isNan(fmodq.fmodq(1.0, -std.math.nan(f128)))); + try testing.expect(std.math.isNan(fmodq.fmodq(std.math.nan(f128), 1.0))); + try testing.expect(std.math.isNan(fmodq.fmodq(-std.math.nan(f128), 1.0))); +} + +fn test_fmodq_infs() !void { + try testing.expect(fmodq.fmodq(1.0, std.math.inf(f128)) == 1.0); + try testing.expect(fmodq.fmodq(1.0, -std.math.inf(f128)) == 1.0); + try testing.expect(std.math.isNan(fmodq.fmodq(std.math.inf(f128), 1.0))); + try testing.expect(std.math.isNan(fmodq.fmodq(-std.math.inf(f128), 1.0))); +} + +test "fmodq" { + try test_fmodq(6.8, 4.0, 2.8); + try test_fmodq(6.8, -4.0, 2.8); + try test_fmodq(-6.8, 4.0, -2.8); + try test_fmodq(-6.8, -4.0, -2.8); + try test_fmodq(3.0, 2.0, 1.0); + try test_fmodq(-5.0, 3.0, -2.0); + try test_fmodq(3.0, 2.0, 1.0); + try test_fmodq(1.0, 2.0, 1.0); + try test_fmodq(0.0, 1.0, 0.0); + try test_fmodq(-0.0, 1.0, -0.0); + try test_fmodq(7046119.0, 5558362.0, 1487757.0); + try test_fmodq(9010357.0, 1957236.0, 1181413.0); + + // Denormals + const a: f128 = 0xedcb34a235253948765432134674p-16494; + const b: f128 = 0x5d2e38791cfbc0737402da5a9518p-16494; + const exp: f128 = 0x336ec3affb2db8618e4e7d5e1c44p-16494; + try test_fmodq(a, b, exp); + + try test_fmodq_nans(); + try test_fmodq_infs(); +} diff --git a/lib/std/special/compiler_rt/floatsiXf.zig b/lib/std/special/compiler_rt/floatsiXf.zig deleted file mode 100644 index ef551d1911..0000000000 --- a/lib/std/special/compiler_rt/floatsiXf.zig +++ /dev/null @@ -1,120 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const maxInt = std.math.maxInt; - -inline fn floatsiXf(comptime T: type, a: i32) T { - @setRuntimeSafety(builtin.is_test); - - const bits = @typeInfo(T).Float.bits; - const Z = std.meta.Int(.unsigned, bits); - const S = std.meta.Int(.unsigned, bits - @clz(Z, @as(Z, bits) - 1)); - - if (a == 0) { - return @as(T, 0.0); - } - - const significandBits = std.math.floatMantissaBits(T); - const exponentBits = std.math.floatExponentBits(T); - const exponentBias = ((1 << exponentBits - 1) - 1); - - const implicitBit = @as(Z, 1) << significandBits; - const signBit = @as(Z, 1 << bits - 1); - - const sign = a >> 31; - // Take absolute value of a via abs(x) = (x^(x >> 31)) - (x >> 31). - const abs_a = (a ^ sign) -% sign; - // The exponent is the width of abs(a) - const exp = @as(Z, 31 - @clz(i32, abs_a)); - - const sign_bit = if (sign < 0) signBit else 0; - - var mantissa: Z = undefined; - // Shift a into the significand field and clear the implicit bit. - if (exp <= significandBits) { - // No rounding needed - const shift = @intCast(S, significandBits - exp); - mantissa = @intCast(Z, @bitCast(u32, abs_a)) << shift ^ implicitBit; - } else { - const shift = @intCast(S, exp - significandBits); - // Round to the nearest number after truncation - mantissa = @intCast(Z, @bitCast(u32, abs_a)) >> shift ^ implicitBit; - // Align to the left and check if the truncated part is halfway over - const round = @bitCast(u32, abs_a) << @intCast(u5, 31 - shift); - mantissa += @boolToInt(round > 0x80000000); - // Tie to even - mantissa += mantissa & 1; - } - - // Use the addition instead of a or since we may have a carry from the - // mantissa to the exponent - var result = mantissa; - result += (exp + exponentBias) << significandBits; - result += sign_bit; - - return @bitCast(T, result); -} - -pub fn __floatsisf(arg: i32) callconv(.C) f32 { - @setRuntimeSafety(builtin.is_test); - return floatsiXf(f32, arg); -} - -pub fn __floatsidf(arg: i32) callconv(.C) f64 { - @setRuntimeSafety(builtin.is_test); - return floatsiXf(f64, arg); -} - -pub fn __floatsitf(arg: i32) callconv(.C) f128 { - @setRuntimeSafety(builtin.is_test); - return floatsiXf(f128, arg); -} - -pub fn __aeabi_i2d(arg: i32) callconv(.AAPCS) f64 { - @setRuntimeSafety(false); - return floatsiXf(f64, arg); -} - -pub fn __aeabi_i2f(arg: i32) callconv(.AAPCS) f32 { - @setRuntimeSafety(false); - return floatsiXf(f32, arg); -} - -fn test_one_floatsitf(a: i32, expected: u128) !void { - const r = __floatsitf(a); - try std.testing.expect(@bitCast(u128, r) == expected); -} - -fn test_one_floatsidf(a: i32, expected: u64) !void { - const r = __floatsidf(a); - try std.testing.expect(@bitCast(u64, r) == expected); -} - -fn test_one_floatsisf(a: i32, expected: u32) !void { - const r = __floatsisf(a); - try std.testing.expect(@bitCast(u32, r) == expected); -} - -test "floatsidf" { - try test_one_floatsidf(0, 0x0000000000000000); - try test_one_floatsidf(1, 0x3ff0000000000000); - try test_one_floatsidf(-1, 0xbff0000000000000); - try test_one_floatsidf(0x7FFFFFFF, 0x41dfffffffc00000); - try test_one_floatsidf(@bitCast(i32, @intCast(u32, 0x80000000)), 0xc1e0000000000000); -} - -test "floatsisf" { - try test_one_floatsisf(0, 0x00000000); - try test_one_floatsisf(1, 0x3f800000); - try test_one_floatsisf(-1, 0xbf800000); - try test_one_floatsisf(0x7FFFFFFF, 0x4f000000); - try test_one_floatsisf(@bitCast(i32, @intCast(u32, 0x80000000)), 0xcf000000); -} - -test "floatsitf" { - try test_one_floatsitf(0, 0); - try test_one_floatsitf(0x7FFFFFFF, 0x401dfffffffc00000000000000000000); - try test_one_floatsitf(0x12345678, 0x401b2345678000000000000000000000); - try test_one_floatsitf(-0x12345678, 0xc01b2345678000000000000000000000); - try test_one_floatsitf(@bitCast(i32, @intCast(u32, 0xffffffff)), 0xbfff0000000000000000000000000000); - try test_one_floatsitf(@bitCast(i32, @intCast(u32, 0x80000000)), 0xc01e0000000000000000000000000000); -} diff --git a/lib/std/special/compiler_rt/floattidf.zig b/lib/std/special/compiler_rt/floattidf.zig deleted file mode 100644 index 85f84a23d6..0000000000 --- a/lib/std/special/compiler_rt/floattidf.zig +++ /dev/null @@ -1,71 +0,0 @@ -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const std = @import("std"); -const maxInt = std.math.maxInt; - -const DBL_MANT_DIG = 53; - -pub fn __floattidf(arg: i128) callconv(.C) f64 { - @setRuntimeSafety(is_test); - - if (arg == 0) - return 0.0; - - var ai = arg; - const N: u32 = 128; - const si = ai >> @intCast(u7, (N - 1)); - ai = ((ai ^ si) -% si); - var a = @bitCast(u128, ai); - - const sd = @bitCast(i32, N - @clz(u128, a)); // number of significant digits - var e: i32 = sd - 1; // exponent - if (sd > DBL_MANT_DIG) { - // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx - // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR - // 12345678901234567890123456 - // 1 = msb 1 bit - // P = bit DBL_MANT_DIG-1 bits to the right of 1 - // Q = bit DBL_MANT_DIG bits to the right of 1 - // R = "or" of all bits to the right of Q - switch (sd) { - DBL_MANT_DIG + 1 => { - a <<= 1; - }, - DBL_MANT_DIG + 2 => {}, - else => { - const shift1_amt = @intCast(i32, sd - (DBL_MANT_DIG + 2)); - const shift1_amt_u7 = @intCast(u7, shift1_amt); - - const shift2_amt = @intCast(i32, N + (DBL_MANT_DIG + 2)) - sd; - const shift2_amt_u7 = @intCast(u7, shift2_amt); - - a = (a >> shift1_amt_u7) | @boolToInt((a & (@intCast(u128, maxInt(u128)) >> shift2_amt_u7)) != 0); - }, - } - // finish - a |= @boolToInt((a & 4) != 0); // Or P into R - a += 1; // round - this step may add a significant bit - a >>= 2; // dump Q and R - // a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits - if ((a & (@as(u128, 1) << DBL_MANT_DIG)) != 0) { - a >>= 1; - e += 1; - } - // a is now rounded to DBL_MANT_DIG bits - } else { - a <<= @intCast(u7, DBL_MANT_DIG - sd); - // a is now rounded to DBL_MANT_DIG bits - } - - const s = @bitCast(u128, arg) >> (128 - 32); - const high: u64 = (@intCast(u64, s) & 0x80000000) | // sign - (@intCast(u32, (e + 1023)) << 20) | // exponent - (@truncate(u32, a >> 32) & 0x000fffff); // mantissa-high - const low: u64 = @truncate(u32, a); // mantissa-low - - return @bitCast(f64, low | (high << 32)); -} - -test { - _ = @import("floattidf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floattidf_test.zig b/lib/std/special/compiler_rt/floattidf_test.zig deleted file mode 100644 index 62b131744f..0000000000 --- a/lib/std/special/compiler_rt/floattidf_test.zig +++ /dev/null @@ -1,84 +0,0 @@ -const __floattidf = @import("floattidf.zig").__floattidf; -const testing = @import("std").testing; - -fn test__floattidf(a: i128, expected: f64) !void { - const x = __floattidf(a); - try testing.expect(x == expected); -} - -test "floattidf" { - try test__floattidf(0, 0.0); - - try test__floattidf(1, 1.0); - try test__floattidf(2, 2.0); - try test__floattidf(20, 20.0); - try test__floattidf(-1, -1.0); - try test__floattidf(-2, -2.0); - try test__floattidf(-20, -20.0); - - try test__floattidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floattidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); - try test__floattidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - try test__floattidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); - - try test__floattidf(make_ti(0x8000008000000000, 0), -0x1.FFFFFEp+126); - try test__floattidf(make_ti(0x8000000000000800, 0), -0x1.FFFFFFFFFFFFEp+126); - try test__floattidf(make_ti(0x8000010000000000, 0), -0x1.FFFFFCp+126); - try test__floattidf(make_ti(0x8000000000001000, 0), -0x1.FFFFFFFFFFFFCp+126); - - try test__floattidf(make_ti(0x8000000000000000, 0), -0x1.000000p+127); - try test__floattidf(make_ti(0x8000000000000001, 0), -0x1.000000p+127); - - try test__floattidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - - try test__floattidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); - try test__floattidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); - try test__floattidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); - try test__floattidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); - try test__floattidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); - - try test__floattidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); - try test__floattidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); - try test__floattidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); - try test__floattidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); - try test__floattidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); - - try test__floattidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); - try test__floattidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); - try test__floattidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); - try test__floattidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); - try test__floattidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); - try test__floattidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); - - try test__floattidf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496Dp+121); - try test__floattidf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496Ep+121); - try test__floattidf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496Fp+121); - try test__floattidf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496Fp+121); - try test__floattidf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496Fp+121); - try test__floattidf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); -} - -fn make_ti(high: u64, low: u64) i128 { - var result: u128 = high; - result <<= 64; - result |= low; - return @bitCast(i128, result); -} diff --git a/lib/std/special/compiler_rt/floattisf_test.zig b/lib/std/special/compiler_rt/floattisf_test.zig deleted file mode 100644 index 30b36c3f9f..0000000000 --- a/lib/std/special/compiler_rt/floattisf_test.zig +++ /dev/null @@ -1,60 +0,0 @@ -const __floattisf = @import("floatXisf.zig").__floattisf; -const testing = @import("std").testing; - -fn test__floattisf(a: i128, expected: f32) !void { - const x = __floattisf(a); - try testing.expect(x == expected); -} - -test "floattisf" { - try test__floattisf(0, 0.0); - - try test__floattisf(1, 1.0); - try test__floattisf(2, 2.0); - try test__floattisf(-1, -1.0); - try test__floattisf(-2, -2.0); - - try test__floattisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floattisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - - try test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000008000000000), -0x1.FFFFFEp+62); - try test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000010000000000), -0x1.FFFFFCp+62); - - try test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000000000000000), -0x1.000000p+63); - try test__floattisf(make_ti(0xFFFFFFFFFFFFFFFF, 0x8000000000000001), -0x1.000000p+63); - - try test__floattisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - - try test__floattisf(0x0007FB72EA000000, 0x1.FEDCBAp+50); - try test__floattisf(0x0007FB72EB000000, 0x1.FEDCBAp+50); - try test__floattisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50); - try test__floattisf(0x0007FB72EC000000, 0x1.FEDCBCp+50); - try test__floattisf(0x0007FB72E8000001, 0x1.FEDCBAp+50); - - try test__floattisf(0x0007FB72E6000000, 0x1.FEDCBAp+50); - try test__floattisf(0x0007FB72E7000000, 0x1.FEDCBAp+50); - try test__floattisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50); - try test__floattisf(0x0007FB72E4000001, 0x1.FEDCBAp+50); - try test__floattisf(0x0007FB72E4000000, 0x1.FEDCB8p+50); - - try test__floattisf(make_ti(0x0007FB72E8000000, 0), 0x1.FEDCBAp+114); - - try test__floattisf(make_ti(0x0007FB72EA000000, 0), 0x1.FEDCBAp+114); - try test__floattisf(make_ti(0x0007FB72EB000000, 0), 0x1.FEDCBAp+114); - try test__floattisf(make_ti(0x0007FB72EBFFFFFF, 0), 0x1.FEDCBAp+114); - try test__floattisf(make_ti(0x0007FB72EC000000, 0), 0x1.FEDCBCp+114); - try test__floattisf(make_ti(0x0007FB72E8000001, 0), 0x1.FEDCBAp+114); - - try test__floattisf(make_ti(0x0007FB72E6000000, 0), 0x1.FEDCBAp+114); - try test__floattisf(make_ti(0x0007FB72E7000000, 0), 0x1.FEDCBAp+114); - try test__floattisf(make_ti(0x0007FB72E7FFFFFF, 0), 0x1.FEDCBAp+114); - try test__floattisf(make_ti(0x0007FB72E4000001, 0), 0x1.FEDCBAp+114); - try test__floattisf(make_ti(0x0007FB72E4000000, 0), 0x1.FEDCB8p+114); -} - -fn make_ti(high: u64, low: u64) i128 { - var result: u128 = high; - result <<= 64; - result |= low; - return @bitCast(i128, result); -} diff --git a/lib/std/special/compiler_rt/floattitf.zig b/lib/std/special/compiler_rt/floattitf.zig deleted file mode 100644 index aa83f6686e..0000000000 --- a/lib/std/special/compiler_rt/floattitf.zig +++ /dev/null @@ -1,71 +0,0 @@ -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const std = @import("std"); -const maxInt = std.math.maxInt; - -const LDBL_MANT_DIG = 113; - -pub fn __floattitf(arg: i128) callconv(.C) f128 { - @setRuntimeSafety(is_test); - - if (arg == 0) - return 0.0; - - var ai = arg; - const N: u32 = 128; - const si = ai >> @intCast(u7, (N - 1)); - ai = ((ai ^ si) -% si); - var a = @bitCast(u128, ai); - - const sd = @bitCast(i32, N - @clz(u128, a)); // number of significant digits - var e: i32 = sd - 1; // exponent - if (sd > LDBL_MANT_DIG) { - // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx - // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR - // 12345678901234567890123456 - // 1 = msb 1 bit - // P = bit LDBL_MANT_DIG-1 bits to the right of 1 - // Q = bit LDBL_MANT_DIG bits to the right of 1 - // R = "or" of all bits to the right of Q - switch (sd) { - LDBL_MANT_DIG + 1 => { - a <<= 1; - }, - LDBL_MANT_DIG + 2 => {}, - else => { - const shift1_amt = @intCast(i32, sd - (LDBL_MANT_DIG + 2)); - const shift1_amt_u7 = @intCast(u7, shift1_amt); - - const shift2_amt = @intCast(i32, N + (LDBL_MANT_DIG + 2)) - sd; - const shift2_amt_u7 = @intCast(u7, shift2_amt); - - a = (a >> shift1_amt_u7) | @boolToInt((a & (@intCast(u128, maxInt(u128)) >> shift2_amt_u7)) != 0); - }, - } - // finish - a |= @boolToInt((a & 4) != 0); // Or P into R - a += 1; // round - this step may add a significant bit - a >>= 2; // dump Q and R - // a is now rounded to LDBL_MANT_DIG or LDBL_MANT_DIG+1 bits - if ((a & (@as(u128, 1) << LDBL_MANT_DIG)) != 0) { - a >>= 1; - e += 1; - } - // a is now rounded to LDBL_MANT_DIG bits - } else { - a <<= @intCast(u7, LDBL_MANT_DIG - sd); - // a is now rounded to LDBL_MANT_DIG bits - } - - const s = @bitCast(u128, arg) >> (128 - 64); - const high: u128 = (@intCast(u64, s) & 0x8000000000000000) | // sign - (@intCast(u64, (e + 16383)) << 48) | // exponent - (@truncate(u64, a >> 64) & 0x0000ffffffffffff); // mantissa-high - const low = @truncate(u64, a); // mantissa-low - - return @bitCast(f128, low | (high << 64)); -} - -test { - _ = @import("floattitf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floattitf_test.zig b/lib/std/special/compiler_rt/floattitf_test.zig deleted file mode 100644 index 76dfc8fbfc..0000000000 --- a/lib/std/special/compiler_rt/floattitf_test.zig +++ /dev/null @@ -1,96 +0,0 @@ -const __floattitf = @import("floattitf.zig").__floattitf; -const testing = @import("std").testing; - -fn test__floattitf(a: i128, expected: f128) !void { - const x = __floattitf(a); - try testing.expect(x == expected); -} - -test "floattitf" { - try test__floattitf(0, 0.0); - - try test__floattitf(1, 1.0); - try test__floattitf(2, 2.0); - try test__floattitf(20, 20.0); - try test__floattitf(-1, -1.0); - try test__floattitf(-2, -2.0); - try test__floattitf(-20, -20.0); - - try test__floattitf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floattitf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); - try test__floattitf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - try test__floattitf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); - - try test__floattitf(make_ti(0x8000008000000000, 0), -0x1.FFFFFEp+126); - try test__floattitf(make_ti(0x8000000000000800, 0), -0x1.FFFFFFFFFFFFEp+126); - try test__floattitf(make_ti(0x8000010000000000, 0), -0x1.FFFFFCp+126); - try test__floattitf(make_ti(0x8000000000001000, 0), -0x1.FFFFFFFFFFFFCp+126); - - try test__floattitf(make_ti(0x8000000000000000, 0), -0x1.000000p+127); - try test__floattitf(make_ti(0x8000000000000001, 0), -0x1.FFFFFFFFFFFFFFFCp+126); - - try test__floattitf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - - try test__floattitf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); - try test__floattitf(0x0007FB72EB000000, 0x1.FEDCBACp+50); - try test__floattitf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); - try test__floattitf(0x0007FB72EC000000, 0x1.FEDCBBp+50); - try test__floattitf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); - - try test__floattitf(0x0007FB72E6000000, 0x1.FEDCB98p+50); - try test__floattitf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); - try test__floattitf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); - try test__floattitf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); - try test__floattitf(0x0007FB72E4000000, 0x1.FEDCB9p+50); - - try test__floattitf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); - try test__floattitf(0x023479FD0E092DA1, 0x1.1A3CFE870496D08p+57); - try test__floattitf(0x023479FD0E092DB0, 0x1.1A3CFE870496D8p+57); - try test__floattitf(0x023479FD0E092DB8, 0x1.1A3CFE870496DCp+57); - try test__floattitf(0x023479FD0E092DB6, 0x1.1A3CFE870496DBp+57); - try test__floattitf(0x023479FD0E092DBF, 0x1.1A3CFE870496DF8p+57); - try test__floattitf(0x023479FD0E092DC1, 0x1.1A3CFE870496E08p+57); - try test__floattitf(0x023479FD0E092DC7, 0x1.1A3CFE870496E38p+57); - try test__floattitf(0x023479FD0E092DC8, 0x1.1A3CFE870496E4p+57); - try test__floattitf(0x023479FD0E092DCF, 0x1.1A3CFE870496E78p+57); - try test__floattitf(0x023479FD0E092DD0, 0x1.1A3CFE870496E8p+57); - try test__floattitf(0x023479FD0E092DD1, 0x1.1A3CFE870496E88p+57); - try test__floattitf(0x023479FD0E092DD8, 0x1.1A3CFE870496ECp+57); - try test__floattitf(0x023479FD0E092DDF, 0x1.1A3CFE870496EF8p+57); - try test__floattitf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); - - try test__floattitf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); - try test__floattitf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496D08p+121); - try test__floattitf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496D8p+121); - try test__floattitf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496DCp+121); - try test__floattitf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496DBp+121); - try test__floattitf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496DF8p+121); - try test__floattitf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496E08p+121); - try test__floattitf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496E38p+121); - try test__floattitf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496E4p+121); - try test__floattitf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496E78p+121); - try test__floattitf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496E8p+121); - try test__floattitf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496E88p+121); - try test__floattitf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496ECp+121); - try test__floattitf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496EF8p+121); - try test__floattitf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); - - try test__floattitf(make_ti(0, 0xFFFFFFFFFFFFFFFF), 0x1.FFFFFFFFFFFFFFFEp+63); - - try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC2801), 0x1.23456789ABCDEF0123456789ABC3p+124); - try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3000), 0x1.23456789ABCDEF0123456789ABC3p+124); - try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC37FF), 0x1.23456789ABCDEF0123456789ABC3p+124); - try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3800), 0x1.23456789ABCDEF0123456789ABC4p+124); - try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4000), 0x1.23456789ABCDEF0123456789ABC4p+124); - try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC47FF), 0x1.23456789ABCDEF0123456789ABC4p+124); - try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4800), 0x1.23456789ABCDEF0123456789ABC4p+124); - try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4801), 0x1.23456789ABCDEF0123456789ABC5p+124); - try test__floattitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC57FF), 0x1.23456789ABCDEF0123456789ABC5p+124); -} - -fn make_ti(high: u64, low: u64) i128 { - var result: u128 = high; - result <<= 64; - result |= low; - return @bitCast(i128, result); -} diff --git a/lib/std/special/compiler_rt/floatundidf.zig b/lib/std/special/compiler_rt/floatundidf.zig deleted file mode 100644 index 14e7434490..0000000000 --- a/lib/std/special/compiler_rt/floatundidf.zig +++ /dev/null @@ -1,29 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); - -const twop52: f64 = 0x1.0p52; -const twop84: f64 = 0x1.0p84; -const twop84_plus_twop52: f64 = 0x1.00000001p84; - -pub fn __floatundidf(a: u64) callconv(.C) f64 { - @setRuntimeSafety(builtin.is_test); - - if (a == 0) return 0; - - var high = @bitCast(u64, twop84); - var low = @bitCast(u64, twop52); - - high |= a >> 32; - low |= a & 0xFFFFFFFF; - - return (@bitCast(f64, high) - twop84_plus_twop52) + @bitCast(f64, low); -} - -pub fn __aeabi_ul2d(arg: u64) callconv(.AAPCS) f64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __floatundidf, .{arg}); -} - -test { - _ = @import("floatundidf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floatundidf_test.zig b/lib/std/special/compiler_rt/floatundidf_test.zig deleted file mode 100644 index 71bfdd3087..0000000000 --- a/lib/std/special/compiler_rt/floatundidf_test.zig +++ /dev/null @@ -1,50 +0,0 @@ -const __floatundidf = @import("floatundidf.zig").__floatundidf; -const testing = @import("std").testing; - -fn test__floatundidf(a: u64, expected: f64) !void { - const r = __floatundidf(a); - try testing.expect(r == expected); -} - -test "floatundidf" { - try test__floatundidf(0, 0.0); - try test__floatundidf(1, 1.0); - try test__floatundidf(2, 2.0); - try test__floatundidf(20, 20.0); - try test__floatundidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floatundidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); - try test__floatundidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - try test__floatundidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); - try test__floatundidf(0x8000008000000000, 0x1.000001p+63); - try test__floatundidf(0x8000000000000800, 0x1.0000000000001p+63); - try test__floatundidf(0x8000010000000000, 0x1.000002p+63); - try test__floatundidf(0x8000000000001000, 0x1.0000000000002p+63); - try test__floatundidf(0x8000000000000000, 0x1p+63); - try test__floatundidf(0x8000000000000001, 0x1p+63); - try test__floatundidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - try test__floatundidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); - try test__floatundidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); - try test__floatundidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); - try test__floatundidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); - try test__floatundidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); - try test__floatundidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); - try test__floatundidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); - try test__floatundidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); - try test__floatundidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); - try test__floatundidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); - try test__floatundidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); - try test__floatundidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); - try test__floatundidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); - try test__floatundidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); - try test__floatundidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); - try test__floatundidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); -} diff --git a/lib/std/special/compiler_rt/floatundisf.zig b/lib/std/special/compiler_rt/floatundisf.zig deleted file mode 100644 index ffbe3ef252..0000000000 --- a/lib/std/special/compiler_rt/floatundisf.zig +++ /dev/null @@ -1,94 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const maxInt = std.math.maxInt; - -const FLT_MANT_DIG = 24; - -inline fn floatundisf(arg: u64) f32 { - @setRuntimeSafety(builtin.is_test); - - if (arg == 0) return 0; - - var a = arg; - const N: usize = @typeInfo(@TypeOf(a)).Int.bits; - // Number of significant digits - const sd = N - @clz(u64, a); - // 8 exponent - var e = @intCast(u32, sd) - 1; - - if (sd > FLT_MANT_DIG) { - // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx - // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR - // 12345678901234567890123456 - // 1 = msb 1 bit - // P = bit FLT_MANT_DIG-1 bits to the right of 1 - // Q = bit FLT_MANT_DIG bits to the right of 1 - // R = "or" of all bits to the right of Q - switch (sd) { - FLT_MANT_DIG + 1 => a <<= 1, - FLT_MANT_DIG + 2 => {}, - else => { - const shift_amt = @intCast(u6, ((N + FLT_MANT_DIG + 2) - sd)); - const all_ones: u64 = maxInt(u64); - a = (a >> @intCast(u6, sd - (FLT_MANT_DIG + 2))) | - @boolToInt(a & (all_ones >> shift_amt) != 0); - }, - } - // Or P into R - a |= @boolToInt((a & 4) != 0); - // round - this step may add a significant bit - a += 1; - // dump Q and R - a >>= 2; - // a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits - if ((a & (@as(u64, 1) << FLT_MANT_DIG)) != 0) { - a >>= 1; - e += 1; - } - // a is now rounded to FLT_MANT_DIG bits - } else { - a <<= @intCast(u6, FLT_MANT_DIG - sd); - // a is now rounded to FLT_MANT_DIG bits - } - - const result: u32 = ((e + 127) << 23) | // exponent - @truncate(u32, a & 0x007FFFFF); // mantissa - return @bitCast(f32, result); -} - -pub fn __floatundisf(arg: u64) callconv(.C) f32 { - return floatundisf(arg); -} - -pub fn __aeabi_ul2f(arg: u64) callconv(.AAPCS) f32 { - return floatundisf(arg); -} - -fn test__floatundisf(a: u64, expected: f32) !void { - try std.testing.expectEqual(expected, __floatundisf(a)); -} - -test "floatundisf" { - try test__floatundisf(0, 0.0); - try test__floatundisf(1, 1.0); - try test__floatundisf(2, 2.0); - try test__floatundisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floatundisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - try test__floatundisf(0x8000008000000000, 0x1p+63); - try test__floatundisf(0x8000010000000000, 0x1.000002p+63); - try test__floatundisf(0x8000000000000000, 0x1p+63); - try test__floatundisf(0x8000000000000001, 0x1p+63); - try test__floatundisf(0xFFFFFFFFFFFFFFFE, 0x1p+64); - try test__floatundisf(0xFFFFFFFFFFFFFFFF, 0x1p+64); - try test__floatundisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - try test__floatundisf(0x0007FB72EA000000, 0x1.FEDCBAp+50); - try test__floatundisf(0x0007FB72EB000000, 0x1.FEDCBAp+50); - try test__floatundisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50); - try test__floatundisf(0x0007FB72EC000000, 0x1.FEDCBCp+50); - try test__floatundisf(0x0007FB72E8000001, 0x1.FEDCBAp+50); - try test__floatundisf(0x0007FB72E6000000, 0x1.FEDCBAp+50); - try test__floatundisf(0x0007FB72E7000000, 0x1.FEDCBAp+50); - try test__floatundisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50); - try test__floatundisf(0x0007FB72E4000001, 0x1.FEDCBAp+50); - try test__floatundisf(0x0007FB72E4000000, 0x1.FEDCB8p+50); -} diff --git a/lib/std/special/compiler_rt/floatunditf.zig b/lib/std/special/compiler_rt/floatunditf.zig deleted file mode 100644 index b304c8cdba..0000000000 --- a/lib/std/special/compiler_rt/floatunditf.zig +++ /dev/null @@ -1,28 +0,0 @@ -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const std = @import("std"); - -pub fn __floatunditf(a: u64) callconv(.C) f128 { - @setRuntimeSafety(is_test); - - if (a == 0) { - return 0; - } - - const mantissa_bits = std.math.floatMantissaBits(f128); - const exponent_bits = std.math.floatExponentBits(f128); - const exponent_bias = (1 << (exponent_bits - 1)) - 1; - const implicit_bit = 1 << mantissa_bits; - - const exp: u128 = (64 - 1) - @clz(u64, a); - const shift: u7 = mantissa_bits - @intCast(u7, exp); - - var result: u128 = (@intCast(u128, a) << shift) ^ implicit_bit; - result += (exp + exponent_bias) << mantissa_bits; - - return @bitCast(f128, result); -} - -test { - _ = @import("floatunditf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floatunditf_test.zig b/lib/std/special/compiler_rt/floatunditf_test.zig deleted file mode 100644 index ae6834c082..0000000000 --- a/lib/std/special/compiler_rt/floatunditf_test.zig +++ /dev/null @@ -1,32 +0,0 @@ -const __floatunditf = @import("floatunditf.zig").__floatunditf; - -fn test__floatunditf(a: u64, expected_hi: u64, expected_lo: u64) !void { - const x = __floatunditf(a); - - const x_repr = @bitCast(u128, x); - const x_hi = @intCast(u64, x_repr >> 64); - const x_lo = @truncate(u64, x_repr); - - if (x_hi == expected_hi and x_lo == expected_lo) { - return; - } - // nan repr - else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { - if ((x_hi & 0x7fff000000000000) == 0x7fff000000000000 and ((x_hi & 0xffffffffffff) > 0 or x_lo > 0)) { - return; - } - } - - @panic("__floatunditf test failure"); -} - -test "floatunditf" { - try test__floatunditf(0xffffffffffffffff, 0x403effffffffffff, 0xfffe000000000000); - try test__floatunditf(0xfffffffffffffffe, 0x403effffffffffff, 0xfffc000000000000); - try test__floatunditf(0x8000000000000000, 0x403e000000000000, 0x0); - try test__floatunditf(0x7fffffffffffffff, 0x403dffffffffffff, 0xfffc000000000000); - try test__floatunditf(0x123456789abcdef1, 0x403b23456789abcd, 0xef10000000000000); - try test__floatunditf(0x2, 0x4000000000000000, 0x0); - try test__floatunditf(0x1, 0x3fff000000000000, 0x0); - try test__floatunditf(0x0, 0x0, 0x0); -} diff --git a/lib/std/special/compiler_rt/floatunsidf.zig b/lib/std/special/compiler_rt/floatunsidf.zig deleted file mode 100644 index f474c1de8f..0000000000 --- a/lib/std/special/compiler_rt/floatunsidf.zig +++ /dev/null @@ -1,41 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const maxInt = std.math.maxInt; - -const implicitBit = @as(u64, 1) << 52; - -inline fn floatunsidf(arg: u32) f64 { - @setRuntimeSafety(builtin.is_test); - - if (arg == 0) return 0.0; - - // The exponent is the width of abs(a) - const exp = @as(u64, 31) - @clz(u32, arg); - // Shift a into the significand field and clear the implicit bit - const shift = @intCast(u6, 52 - exp); - const mant = @as(u64, arg) << shift ^ implicitBit; - - return @bitCast(f64, mant | (exp + 1023) << 52); -} - -pub fn __floatunsidf(arg: u32) callconv(.C) f64 { - return floatunsidf(arg); -} - -pub fn __aeabi_ui2d(arg: u32) callconv(.AAPCS) f64 { - return floatunsidf(arg); -} - -fn test_one_floatunsidf(a: u32, expected: u64) !void { - const r = __floatunsidf(a); - try std.testing.expect(@bitCast(u64, r) == expected); -} - -test "floatsidf" { - // Test the produced bit pattern - try test_one_floatunsidf(0, 0x0000000000000000); - try test_one_floatunsidf(1, 0x3ff0000000000000); - try test_one_floatunsidf(0x7FFFFFFF, 0x41dfffffffc00000); - try test_one_floatunsidf(@intCast(u32, 0x80000000), 0x41e0000000000000); - try test_one_floatunsidf(@intCast(u32, 0xFFFFFFFF), 0x41efffffffe00000); -} diff --git a/lib/std/special/compiler_rt/floatunsisf.zig b/lib/std/special/compiler_rt/floatunsisf.zig deleted file mode 100644 index d267baee01..0000000000 --- a/lib/std/special/compiler_rt/floatunsisf.zig +++ /dev/null @@ -1,61 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const maxInt = std.math.maxInt; - -const significandBits = 23; -const exponentBias = 127; -const implicitBit = @as(u32, 1) << significandBits; - -inline fn floatunsisf(arg: u32) f32 { - @setRuntimeSafety(builtin.is_test); - - if (arg == 0) return 0.0; - - // The exponent is the width of abs(a) - const exp = @as(u32, 31) - @clz(u32, arg); - - var mantissa: u32 = undefined; - if (exp <= significandBits) { - // Shift a into the significand field and clear the implicit bit - const shift = @intCast(u5, significandBits - exp); - mantissa = @as(u32, arg) << shift ^ implicitBit; - } else { - const shift = @intCast(u5, exp - significandBits); - // Round to the nearest number after truncation - mantissa = @as(u32, arg) >> shift ^ implicitBit; - // Align to the left and check if the truncated part is halfway over - const round = arg << @intCast(u5, 31 - shift); - mantissa += @boolToInt(round > 0x80000000); - // Tie to even - mantissa += mantissa & 1; - } - - // Use the addition instead of a or since we may have a carry from the - // mantissa to the exponent - var result = mantissa; - result += (exp + exponentBias) << significandBits; - - return @bitCast(f32, result); -} - -pub fn __floatunsisf(arg: u32) callconv(.C) f32 { - return floatunsisf(arg); -} - -pub fn __aeabi_ui2f(arg: u32) callconv(.AAPCS) f32 { - return floatunsisf(arg); -} - -fn test_one_floatunsisf(a: u32, expected: u32) !void { - const r = __floatunsisf(a); - try std.testing.expect(@bitCast(u32, r) == expected); -} - -test "floatunsisf" { - // Test the produced bit pattern - try test_one_floatunsisf(0, 0); - try test_one_floatunsisf(1, 0x3f800000); - try test_one_floatunsisf(0x7FFFFFFF, 0x4f000000); - try test_one_floatunsisf(0x80000000, 0x4f000000); - try test_one_floatunsisf(0xFFFFFFFF, 0x4f800000); -} diff --git a/lib/std/special/compiler_rt/floatunsitf.zig b/lib/std/special/compiler_rt/floatunsitf.zig deleted file mode 100644 index 8774d486ea..0000000000 --- a/lib/std/special/compiler_rt/floatunsitf.zig +++ /dev/null @@ -1,29 +0,0 @@ -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const std = @import("std"); - -pub fn __floatunsitf(a: u32) callconv(.C) f128 { - @setRuntimeSafety(is_test); - - if (a == 0) { - return 0; - } - - const mantissa_bits = std.math.floatMantissaBits(f128); - const exponent_bits = std.math.floatExponentBits(f128); - const exponent_bias = (1 << (exponent_bits - 1)) - 1; - const implicit_bit = 1 << mantissa_bits; - - const exp = (32 - 1) - @clz(u32, a); - const shift = mantissa_bits - @intCast(u7, exp); - - // TODO(#1148): @bitCast alignment error - var result align(16) = (@intCast(u128, a) << shift) ^ implicit_bit; - result += (@intCast(u128, exp) + exponent_bias) << mantissa_bits; - - return @bitCast(f128, result); -} - -test { - _ = @import("floatunsitf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floatunsitf_test.zig b/lib/std/special/compiler_rt/floatunsitf_test.zig deleted file mode 100644 index 7ae7c43281..0000000000 --- a/lib/std/special/compiler_rt/floatunsitf_test.zig +++ /dev/null @@ -1,28 +0,0 @@ -const __floatunsitf = @import("floatunsitf.zig").__floatunsitf; - -fn test__floatunsitf(a: u32, expected_hi: u64, expected_lo: u64) !void { - const x = __floatunsitf(a); - - const x_repr = @bitCast(u128, x); - const x_hi = @intCast(u64, x_repr >> 64); - const x_lo = @truncate(u64, x_repr); - - if (x_hi == expected_hi and x_lo == expected_lo) { - return; - } - // nan repr - else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { - if ((x_hi & 0x7fff000000000000) == 0x7fff000000000000 and ((x_hi & 0xffffffffffff) > 0 or x_lo > 0)) { - return; - } - } - - @panic("__floatunsitf test failure"); -} - -test "floatunsitf" { - try test__floatunsitf(0x7fffffff, 0x401dfffffffc0000, 0x0); - try test__floatunsitf(0, 0x0, 0x0); - try test__floatunsitf(0xffffffff, 0x401efffffffe0000, 0x0); - try test__floatunsitf(0x12345678, 0x401b234567800000, 0x0); -} diff --git a/lib/std/special/compiler_rt/floatuntidf.zig b/lib/std/special/compiler_rt/floatuntidf.zig deleted file mode 100644 index 2c4729a9d8..0000000000 --- a/lib/std/special/compiler_rt/floatuntidf.zig +++ /dev/null @@ -1,62 +0,0 @@ -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const std = @import("std"); -const maxInt = std.math.maxInt; - -const DBL_MANT_DIG = 53; - -pub fn __floatuntidf(arg: u128) callconv(.C) f64 { - @setRuntimeSafety(is_test); - - if (arg == 0) - return 0.0; - - var a = arg; - const N: u32 = @sizeOf(u128) * 8; - const sd = @bitCast(i32, N - @clz(u128, a)); // number of significant digits - var e: i32 = sd - 1; // exponent - if (sd > DBL_MANT_DIG) { - // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx - // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR - // 12345678901234567890123456 - // 1 = msb 1 bit - // P = bit DBL_MANT_DIG-1 bits to the right of 1 - // Q = bit DBL_MANT_DIG bits to the right of 1 - // R = "or" of all bits to the right of Q - switch (sd) { - DBL_MANT_DIG + 1 => { - a <<= 1; - }, - DBL_MANT_DIG + 2 => {}, - else => { - const shift_amt = @bitCast(i32, N + (DBL_MANT_DIG + 2)) - sd; - const shift_amt_u7 = @intCast(u7, shift_amt); - a = (a >> @intCast(u7, sd - (DBL_MANT_DIG + 2))) | - @boolToInt((a & (@as(u128, maxInt(u128)) >> shift_amt_u7)) != 0); - }, - } - // finish - a |= @boolToInt((a & 4) != 0); // Or P into R - a += 1; // round - this step may add a significant bit - a >>= 2; // dump Q and R - // a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits - if ((a & (@as(u128, 1) << DBL_MANT_DIG)) != 0) { - a >>= 1; - e += 1; - } - // a is now rounded to DBL_MANT_DIG bits - } else { - a <<= @intCast(u7, DBL_MANT_DIG - sd); - // a is now rounded to DBL_MANT_DIG bits - } - - const high: u64 = @bitCast(u32, (e + 1023) << 20) | // exponent - (@truncate(u32, a >> 32) & 0x000FFFFF); // mantissa-high - const low = @truncate(u32, a); // mantissa-low - - return @bitCast(f64, low | (high << 32)); -} - -test { - _ = @import("floatuntidf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floatuntidf_test.zig b/lib/std/special/compiler_rt/floatuntidf_test.zig deleted file mode 100644 index 5fc6a47150..0000000000 --- a/lib/std/special/compiler_rt/floatuntidf_test.zig +++ /dev/null @@ -1,81 +0,0 @@ -const __floatuntidf = @import("floatuntidf.zig").__floatuntidf; -const testing = @import("std").testing; - -fn test__floatuntidf(a: u128, expected: f64) !void { - const x = __floatuntidf(a); - try testing.expect(x == expected); -} - -test "floatuntidf" { - try test__floatuntidf(0, 0.0); - - try test__floatuntidf(1, 1.0); - try test__floatuntidf(2, 2.0); - try test__floatuntidf(20, 20.0); - - try test__floatuntidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floatuntidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); - try test__floatuntidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - try test__floatuntidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); - - try test__floatuntidf(make_ti(0x8000008000000000, 0), 0x1.000001p+127); - try test__floatuntidf(make_ti(0x8000000000000800, 0), 0x1.0000000000001p+127); - try test__floatuntidf(make_ti(0x8000010000000000, 0), 0x1.000002p+127); - try test__floatuntidf(make_ti(0x8000000000001000, 0), 0x1.0000000000002p+127); - - try test__floatuntidf(make_ti(0x8000000000000000, 0), 0x1.000000p+127); - try test__floatuntidf(make_ti(0x8000000000000001, 0), 0x1.0000000000000002p+127); - - try test__floatuntidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - - try test__floatuntidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); - try test__floatuntidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); - try test__floatuntidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); - try test__floatuntidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); - try test__floatuntidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); - - try test__floatuntidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); - try test__floatuntidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); - try test__floatuntidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); - try test__floatuntidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); - try test__floatuntidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); - - try test__floatuntidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); - try test__floatuntidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); - try test__floatuntidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); - try test__floatuntidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); - try test__floatuntidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); - try test__floatuntidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); - - try test__floatuntidf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496Dp+121); - try test__floatuntidf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496Ep+121); - try test__floatuntidf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496Fp+121); - try test__floatuntidf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496Fp+121); - try test__floatuntidf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496Fp+121); - try test__floatuntidf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); -} - -fn make_ti(high: u64, low: u64) u128 { - var result: u128 = high; - result <<= 64; - result |= low; - return result; -} diff --git a/lib/std/special/compiler_rt/floatuntisf.zig b/lib/std/special/compiler_rt/floatuntisf.zig deleted file mode 100644 index 2a54c2e0f3..0000000000 --- a/lib/std/special/compiler_rt/floatuntisf.zig +++ /dev/null @@ -1,61 +0,0 @@ -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const std = @import("std"); -const maxInt = std.math.maxInt; - -const FLT_MANT_DIG = 24; - -pub fn __floatuntisf(arg: u128) callconv(.C) f32 { - @setRuntimeSafety(is_test); - - if (arg == 0) - return 0.0; - - var a = arg; - const N: u32 = @sizeOf(u128) * 8; - const sd = @bitCast(i32, N - @clz(u128, a)); // number of significant digits - var e: i32 = sd - 1; // exponent - if (sd > FLT_MANT_DIG) { - // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx - // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR - // 12345678901234567890123456 - // 1 = msb 1 bit - // P = bit FLT_MANT_DIG-1 bits to the right of 1 - // Q = bit FLT_MANT_DIG bits to the right of 1 - // R = "or" of all bits to the right of Q - switch (sd) { - FLT_MANT_DIG + 1 => { - a <<= 1; - }, - FLT_MANT_DIG + 2 => {}, - else => { - const shift_amt = @bitCast(i32, N + (FLT_MANT_DIG + 2)) - sd; - const shift_amt_u7 = @intCast(u7, shift_amt); - a = (a >> @intCast(u7, sd - (FLT_MANT_DIG + 2))) | - @boolToInt((a & (@as(u128, maxInt(u128)) >> shift_amt_u7)) != 0); - }, - } - // finish - a |= @boolToInt((a & 4) != 0); // Or P into R - a += 1; // round - this step may add a significant bit - a >>= 2; // dump Q and R - // a is now rounded to FLT_MANT_DIG or FLT_MANT_DIG+1 bits - if ((a & (@as(u128, 1) << FLT_MANT_DIG)) != 0) { - a >>= 1; - e += 1; - } - // a is now rounded to FLT_MANT_DIG bits - } else { - a <<= @intCast(u7, FLT_MANT_DIG - sd); - // a is now rounded to FLT_MANT_DIG bits - } - - const high = @bitCast(u32, (e + 127) << 23); // exponent - const low = @truncate(u32, a) & 0x007fffff; // mantissa - - return @bitCast(f32, high | low); -} - -test { - _ = @import("floatuntisf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floatuntisf_test.zig b/lib/std/special/compiler_rt/floatuntisf_test.zig deleted file mode 100644 index dd06b7e3d7..0000000000 --- a/lib/std/special/compiler_rt/floatuntisf_test.zig +++ /dev/null @@ -1,72 +0,0 @@ -const __floatuntisf = @import("floatuntisf.zig").__floatuntisf; -const testing = @import("std").testing; - -fn test__floatuntisf(a: u128, expected: f32) !void { - const x = __floatuntisf(a); - try testing.expect(x == expected); -} - -test "floatuntisf" { - try test__floatuntisf(0, 0.0); - - try test__floatuntisf(1, 1.0); - try test__floatuntisf(2, 2.0); - try test__floatuntisf(20, 20.0); - - try test__floatuntisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floatuntisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - - try test__floatuntisf(make_ti(0x8000008000000000, 0), 0x1.000001p+127); - try test__floatuntisf(make_ti(0x8000000000000800, 0), 0x1.0p+127); - try test__floatuntisf(make_ti(0x8000010000000000, 0), 0x1.000002p+127); - - try test__floatuntisf(make_ti(0x8000000000000000, 0), 0x1.000000p+127); - - try test__floatuntisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - - try test__floatuntisf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); - try test__floatuntisf(0x0007FB72EB000000, 0x1.FEDCBACp+50); - - try test__floatuntisf(0x0007FB72EC000000, 0x1.FEDCBBp+50); - - try test__floatuntisf(0x0007FB72E6000000, 0x1.FEDCB98p+50); - try test__floatuntisf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); - try test__floatuntisf(0x0007FB72E4000000, 0x1.FEDCB9p+50); - - try test__floatuntisf(0xFFFFFFFFFFFFFFFE, 0x1p+64); - try test__floatuntisf(0xFFFFFFFFFFFFFFFF, 0x1p+64); - - try test__floatuntisf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - - try test__floatuntisf(0x0007FB72EA000000, 0x1.FEDCBAp+50); - try test__floatuntisf(0x0007FB72EB000000, 0x1.FEDCBAp+50); - try test__floatuntisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50); - try test__floatuntisf(0x0007FB72EC000000, 0x1.FEDCBCp+50); - try test__floatuntisf(0x0007FB72E8000001, 0x1.FEDCBAp+50); - - try test__floatuntisf(0x0007FB72E6000000, 0x1.FEDCBAp+50); - try test__floatuntisf(0x0007FB72E7000000, 0x1.FEDCBAp+50); - try test__floatuntisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50); - try test__floatuntisf(0x0007FB72E4000001, 0x1.FEDCBAp+50); - try test__floatuntisf(0x0007FB72E4000000, 0x1.FEDCB8p+50); - - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCB90000000000001), 0x1.FEDCBAp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBA0000000000000), 0x1.FEDCBAp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBAFFFFFFFFFFFFF), 0x1.FEDCBAp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBB0000000000000), 0x1.FEDCBCp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBB0000000000001), 0x1.FEDCBCp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBBFFFFFFFFFFFFF), 0x1.FEDCBCp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBC0000000000000), 0x1.FEDCBCp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBC0000000000001), 0x1.FEDCBCp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBD0000000000000), 0x1.FEDCBCp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBD0000000000001), 0x1.FEDCBEp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBDFFFFFFFFFFFFF), 0x1.FEDCBEp+76); - try test__floatuntisf(make_ti(0x0000000000001FED, 0xCBE0000000000000), 0x1.FEDCBEp+76); -} - -fn make_ti(high: u64, low: u64) u128 { - var result: u128 = high; - result <<= 64; - result |= low; - return result; -} diff --git a/lib/std/special/compiler_rt/floatuntitf.zig b/lib/std/special/compiler_rt/floatuntitf.zig deleted file mode 100644 index c35b4dcf74..0000000000 --- a/lib/std/special/compiler_rt/floatuntitf.zig +++ /dev/null @@ -1,62 +0,0 @@ -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const std = @import("std"); -const maxInt = std.math.maxInt; - -const LDBL_MANT_DIG = 113; - -pub fn __floatuntitf(arg: u128) callconv(.C) f128 { - @setRuntimeSafety(is_test); - - if (arg == 0) - return 0.0; - - var a = arg; - const N: u32 = @sizeOf(u128) * 8; - const sd = @bitCast(i32, N - @clz(u128, a)); // number of significant digits - var e: i32 = sd - 1; // exponent - if (sd > LDBL_MANT_DIG) { - // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx - // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR - // 12345678901234567890123456 - // 1 = msb 1 bit - // P = bit LDBL_MANT_DIG-1 bits to the right of 1 - // Q = bit LDBL_MANT_DIG bits to the right of 1 - // R = "or" of all bits to the right of Q - switch (sd) { - LDBL_MANT_DIG + 1 => { - a <<= 1; - }, - LDBL_MANT_DIG + 2 => {}, - else => { - const shift_amt = @bitCast(i32, N + (LDBL_MANT_DIG + 2)) - sd; - const shift_amt_u7 = @intCast(u7, shift_amt); - a = (a >> @intCast(u7, sd - (LDBL_MANT_DIG + 2))) | - @boolToInt((a & (@as(u128, maxInt(u128)) >> shift_amt_u7)) != 0); - }, - } - // finish - a |= @boolToInt((a & 4) != 0); // Or P into R - a += 1; // round - this step may add a significant bit - a >>= 2; // dump Q and R - // a is now rounded to LDBL_MANT_DIG or LDBL_MANT_DIG+1 bits - if ((a & (@as(u128, 1) << LDBL_MANT_DIG)) != 0) { - a >>= 1; - e += 1; - } - // a is now rounded to LDBL_MANT_DIG bits - } else { - a <<= @intCast(u7, LDBL_MANT_DIG - sd); - // a is now rounded to LDBL_MANT_DIG bits - } - - const high: u128 = (@intCast(u64, (e + 16383)) << 48) | // exponent - (@truncate(u64, a >> 64) & 0x0000ffffffffffff); // mantissa-high - const low = @truncate(u64, a); // mantissa-low - - return @bitCast(f128, low | (high << 64)); -} - -test { - _ = @import("floatuntitf_test.zig"); -} diff --git a/lib/std/special/compiler_rt/floatuntitf_test.zig b/lib/std/special/compiler_rt/floatuntitf_test.zig deleted file mode 100644 index 5afbf348c6..0000000000 --- a/lib/std/special/compiler_rt/floatuntitf_test.zig +++ /dev/null @@ -1,99 +0,0 @@ -const __floatuntitf = @import("floatuntitf.zig").__floatuntitf; -const testing = @import("std").testing; - -fn test__floatuntitf(a: u128, expected: f128) !void { - const x = __floatuntitf(a); - try testing.expect(x == expected); -} - -test "floatuntitf" { - try test__floatuntitf(0, 0.0); - - try test__floatuntitf(1, 1.0); - try test__floatuntitf(2, 2.0); - try test__floatuntitf(20, 20.0); - - try test__floatuntitf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); - try test__floatuntitf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); - try test__floatuntitf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); - try test__floatuntitf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); - try test__floatuntitf(0x7FFFFFFFFFFFFFFF, 0xF.FFFFFFFFFFFFFFEp+59); - try test__floatuntitf(0xFFFFFFFFFFFFFFFE, 0xF.FFFFFFFFFFFFFFEp+60); - try test__floatuntitf(0xFFFFFFFFFFFFFFFF, 0xF.FFFFFFFFFFFFFFFp+60); - - try test__floatuntitf(0x8000008000000000, 0x8.000008p+60); - try test__floatuntitf(0x8000000000000800, 0x8.0000000000008p+60); - try test__floatuntitf(0x8000010000000000, 0x8.00001p+60); - try test__floatuntitf(0x8000000000001000, 0x8.000000000001p+60); - - try test__floatuntitf(0x8000000000000000, 0x8p+60); - try test__floatuntitf(0x8000000000000001, 0x8.000000000000001p+60); - - try test__floatuntitf(0x0007FB72E8000000, 0x1.FEDCBAp+50); - - try test__floatuntitf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); - try test__floatuntitf(0x0007FB72EB000000, 0x1.FEDCBACp+50); - try test__floatuntitf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); - try test__floatuntitf(0x0007FB72EC000000, 0x1.FEDCBBp+50); - try test__floatuntitf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); - - try test__floatuntitf(0x0007FB72E6000000, 0x1.FEDCB98p+50); - try test__floatuntitf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); - try test__floatuntitf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); - try test__floatuntitf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); - try test__floatuntitf(0x0007FB72E4000000, 0x1.FEDCB9p+50); - - try test__floatuntitf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); - try test__floatuntitf(0x023479FD0E092DA1, 0x1.1A3CFE870496D08p+57); - try test__floatuntitf(0x023479FD0E092DB0, 0x1.1A3CFE870496D8p+57); - try test__floatuntitf(0x023479FD0E092DB8, 0x1.1A3CFE870496DCp+57); - try test__floatuntitf(0x023479FD0E092DB6, 0x1.1A3CFE870496DBp+57); - try test__floatuntitf(0x023479FD0E092DBF, 0x1.1A3CFE870496DF8p+57); - try test__floatuntitf(0x023479FD0E092DC1, 0x1.1A3CFE870496E08p+57); - try test__floatuntitf(0x023479FD0E092DC7, 0x1.1A3CFE870496E38p+57); - try test__floatuntitf(0x023479FD0E092DC8, 0x1.1A3CFE870496E4p+57); - try test__floatuntitf(0x023479FD0E092DCF, 0x1.1A3CFE870496E78p+57); - try test__floatuntitf(0x023479FD0E092DD0, 0x1.1A3CFE870496E8p+57); - try test__floatuntitf(0x023479FD0E092DD1, 0x1.1A3CFE870496E88p+57); - try test__floatuntitf(0x023479FD0E092DD8, 0x1.1A3CFE870496ECp+57); - try test__floatuntitf(0x023479FD0E092DDF, 0x1.1A3CFE870496EF8p+57); - try test__floatuntitf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); - - try test__floatuntitf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); - try test__floatuntitf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496D08p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496D8p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496DCp+121); - try test__floatuntitf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496DBp+121); - try test__floatuntitf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496DF8p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496E08p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496E38p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496E4p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496E78p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496E8p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496E88p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496ECp+121); - try test__floatuntitf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496EF8p+121); - try test__floatuntitf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); - - try test__floatuntitf(make_ti(0, 0xFFFFFFFFFFFFFFFF), 0x1.FFFFFFFFFFFFFFFEp+63); - - try test__floatuntitf(make_ti(0xFFFFFFFFFFFFFFFF, 0x0000000000000000), 0x1.FFFFFFFFFFFFFFFEp+127); - try test__floatuntitf(make_ti(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), 0x1.0000000000000000p+128); - - try test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC2801), 0x1.23456789ABCDEF0123456789ABC3p+124); - try test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3000), 0x1.23456789ABCDEF0123456789ABC3p+124); - try test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC37FF), 0x1.23456789ABCDEF0123456789ABC3p+124); - try test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC3800), 0x1.23456789ABCDEF0123456789ABC4p+124); - try test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4000), 0x1.23456789ABCDEF0123456789ABC4p+124); - try test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC47FF), 0x1.23456789ABCDEF0123456789ABC4p+124); - try test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4800), 0x1.23456789ABCDEF0123456789ABC4p+124); - try test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC4801), 0x1.23456789ABCDEF0123456789ABC5p+124); - try test__floatuntitf(make_ti(0x123456789ABCDEF0, 0x123456789ABC57FF), 0x1.23456789ABCDEF0123456789ABC5p+124); -} - -fn make_ti(high: u64, low: u64) u128 { - var result: u128 = high; - result <<= 64; - result |= low; - return result; -} diff --git a/lib/std/special/compiler_rt/sparc.zig b/lib/std/special/compiler_rt/sparc.zig index 3f2cbd86b5..3b33afd29a 100644 --- a/lib/std/special/compiler_rt/sparc.zig +++ b/lib/std/special/compiler_rt/sparc.zig @@ -66,19 +66,19 @@ pub fn _Qp_fge(a: *f128, b: *f128) callconv(.C) bool { // Conversion pub fn _Qp_itoq(c: *f128, a: i32) callconv(.C) void { - c.* = @import("floatsiXf.zig").__floatsitf(a); + c.* = @import("floatXiYf.zig").__floatsitf(a); } pub fn _Qp_uitoq(c: *f128, a: u32) callconv(.C) void { - c.* = @import("floatunsitf.zig").__floatunsitf(a); + c.* = @import("floatXiYf.zig").__floatunsitf(a); } pub fn _Qp_xtoq(c: *f128, a: i64) callconv(.C) void { - c.* = @import("floatditf.zig").__floatditf(a); + c.* = @import("floatXiYf.zig").__floatditf(a); } pub fn _Qp_uxtoq(c: *f128, a: u64) callconv(.C) void { - c.* = @import("floatunditf.zig").__floatunditf(a); + c.* = @import("floatXiYf.zig").__floatunditf(a); } pub fn _Qp_stoq(c: *f128, a: f32) callconv(.C) void { @@ -90,19 +90,19 @@ pub fn _Qp_dtoq(c: *f128, a: f64) callconv(.C) void { } pub fn _Qp_qtoi(a: *f128) callconv(.C) i32 { - return @import("fixtfsi.zig").__fixtfsi(a.*); + return @import("fixXfYi.zig").__fixtfsi(a.*); } pub fn _Qp_qtoui(a: *f128) callconv(.C) u32 { - return @import("fixunstfsi.zig").__fixunstfsi(a.*); + return @import("fixXfYi.zig").__fixunstfsi(a.*); } pub fn _Qp_qtox(a: *f128) callconv(.C) i64 { - return @import("fixtfdi.zig").__fixtfdi(a.*); + return @import("fixXfYi.zig").__fixtfdi(a.*); } pub fn _Qp_qtoux(a: *f128) callconv(.C) u64 { - return @import("fixunstfdi.zig").__fixunstfdi(a.*); + return @import("fixXfYi.zig").__fixunstfdi(a.*); } pub fn _Qp_qtos(a: *f128) callconv(.C) f32 { diff --git a/lib/std/special/compiler_rt/trunc_f80.zig b/lib/std/special/compiler_rt/trunc_f80.zig index 88381a28ee..107874aeeb 100644 --- a/lib/std/special/compiler_rt/trunc_f80.zig +++ b/lib/std/special/compiler_rt/trunc_f80.zig @@ -1,6 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); const native_arch = builtin.cpu.arch; +const testing = std.testing; // AArch64 is the only ABI (at the moment) to support f16 arguments without the // need for extending them to wider fp types. @@ -117,13 +118,12 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 { const src_abs_mask = src_sign_mask - 1; const round_mask = (1 << (src_sig_bits - dst_sig_bits)) - 1; const halfway = 1 << (src_sig_bits - dst_sig_bits - 1); - const src_qnan = 1 << (src_sig_bits - 1); - const src_nan_mask = src_qnan - 1; // Break a into a sign and representation of the absolute value const a_rep = @bitCast(u128, a); const a_abs = a_rep & src_abs_mask; const sign: u16 = if (a_rep & src_sign_mask != 0) 0x8000 else 0; + const integer_bit = 1 << 63; var res: std.math.F80 = undefined; @@ -133,27 +133,41 @@ pub fn __trunctfxf2(a: f128) callconv(.C) f80 { // bit and inserting the (truncated) trailing NaN field. res.exp = 0x7fff; res.fraction = 0x8000000000000000; - res.fraction |= @truncate(u64, (a_abs & src_qnan) << (src_sig_bits - dst_sig_bits)); - res.fraction |= @truncate(u64, (a_abs & src_nan_mask) << (src_sig_bits - dst_sig_bits)); + res.fraction |= @truncate(u64, a_abs >> (src_sig_bits - dst_sig_bits)); } else { // The exponent of a is within the range of normal numbers in the // destination format. We can convert by simply right-shifting with - // rounding and adjusting the exponent. - res.fraction = @truncate(u64, a_abs >> (src_sig_bits - dst_sig_bits)); + // rounding, adding the explicit integer bit, and adjusting the exponent + res.fraction = @truncate(u64, a_abs >> (src_sig_bits - dst_sig_bits)) | integer_bit; res.exp = @truncate(u16, a_abs >> src_sig_bits); const round_bits = a_abs & round_mask; if (round_bits > halfway) { // Round to nearest - const exp = @addWithOverflow(u64, res.fraction, 1, &res.fraction); - res.exp += @boolToInt(exp); + const carry = @boolToInt(@addWithOverflow(u64, res.fraction, 1, &res.fraction)); + res.exp += carry; + res.fraction |= @as(u64, carry) << 63; // Restore integer bit after carry } else if (round_bits == halfway) { // Ties to even - const exp = @addWithOverflow(u64, res.fraction, res.fraction & 1, &res.fraction); - res.exp += @boolToInt(exp); + const carry = @boolToInt(@addWithOverflow(u64, res.fraction, res.fraction & 1, &res.fraction)); + res.exp += carry; + res.fraction |= @as(u64, carry) << 63; // Restore integer bit after carry } + if (res.exp == 0) res.fraction &= ~@as(u64, integer_bit); // Remove integer bit for de-normals } res.exp |= sign; return std.math.make_f80(res); } + +fn test__trunctfxf2(a: f128, expected: f80) !void { + const x = __trunctfxf2(a); + try testing.expect(x == expected); +} + +test { + try test__trunctfxf2(1.5, 1.5); + try test__trunctfxf2(2.5, 2.5); + try test__trunctfxf2(-2.5, -2.5); + try test__trunctfxf2(0.0, 0.0); +} From 1c1cfe1533a6d38cf3fcdd998637afdbb12910a8 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Fri, 8 Apr 2022 19:48:24 -0700 Subject: [PATCH 1092/2031] Skip `@rem`/`@mod` tests on stage2, due to missing `fmodl` implementation --- lib/std/special/compiler_rt.zig | 15 ++++++++++++--- test/behavior/math.zig | 14 ++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 9ccf5619e1..b54a1e980e 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -721,11 +721,12 @@ comptime { @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage }); } - const fmodq = @import("compiler_rt/floatfmodq.zig").fmodq; if (!is_test) { - @export(fmodq, .{ .name = "fmodq", .linkage = linkage }); + @export(fmodl, .{ .name = "fmodl", .linkage = linkage }); if (long_double_is_f128) { - @export(fmodq, .{ .name = "fmodl", .linkage = linkage }); + @export(fmodl, .{ .name = "fmodq", .linkage = linkage }); + } else { + @export(fmodq, .{ .name = "fmodq", .linkage = linkage }); } @export(floorf, .{ .name = "floorf", .linkage = linkage }); @@ -880,6 +881,14 @@ fn ceill(x: c_longdouble) callconv(.C) c_longdouble { return math.ceil(x); } +const fmodq = @import("compiler_rt/floatfmodq.zig").fmodq; +fn fmodl(x: c_longdouble, y: c_longdouble) callconv(.C) c_longdouble { + if (!long_double_is_f128) { + @panic("TODO implement this"); + } + return @floatCast(c_longdouble, fmodq(x, y)); +} + // Avoid dragging in the runtime safety mechanisms into this .o file, // unless we're trying to test this file. pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn { diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 32945e452d..a41f638396 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -923,6 +923,8 @@ test "comptime float rem int" { } test "remainder division" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + comptime try remdiv(f16); comptime try remdiv(f32); comptime try remdiv(f64); @@ -938,11 +940,7 @@ fn remdiv(comptime T: type) !void { } test "float remainder division using @rem" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO comptime try frem(f16); comptime try frem(f32); @@ -973,11 +971,7 @@ fn frem(comptime T: type) !void { } test "float modulo division using @mod" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO comptime try fmod(f16); comptime try fmod(f32); From 04dd43934a94d087001659a577bda1f514e1df5d Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Fri, 8 Apr 2022 22:39:14 -0700 Subject: [PATCH 1093/2031] Skip some floatXiYf tests on non-x86 platforms These need to be skipped because of a bug with `@floatToInt` on stage1: https://github.com/ziglang/zig/issues/11408 --- lib/std/special/compiler_rt/floatXiYf_test.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/std/special/compiler_rt/floatXiYf_test.zig b/lib/std/special/compiler_rt/floatXiYf_test.zig index 400ecaf57e..cffa2a5b42 100644 --- a/lib/std/special/compiler_rt/floatXiYf_test.zig +++ b/lib/std/special/compiler_rt/floatXiYf_test.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); const testing = std.testing; const math = std.math; const floatXiYf = @import("floatXiYf.zig").floatXiYf; @@ -809,6 +810,9 @@ test "conversion to f32" { } test "conversion to f80" { + if (builtin.zig_backend == .stage1 and builtin.cpu.arch != .x86_64) + return error.SkipZigTest; // https://github.com/ziglang/zig/issues/11408 + try testing.expect(floatXiYf(f80, @as(i80, -12)) == -12); try testing.expect(@floatToInt(u80, floatXiYf(f80, @as(u64, math.maxInt(u64)) + 0)) == math.maxInt(u64) + 0); try testing.expect(@floatToInt(u80, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 1)) == math.maxInt(u64) + 1); From 319b5cbce5d4c43de8a6848ac5f5c8879ab9806e Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Sat, 9 Apr 2022 05:09:36 -0700 Subject: [PATCH 1094/2031] Do not detect byte-order using _BIG/_LITTLE_ENDIAN These defines are present on some FreeBSD systems, regardless of whether the system is big- or little- endian. This was causing the FreeBSD CI to be incorrectly flagged as big-endian. Resolves https://github.com/ziglang/zig/issues/11391 --- deps/SoftFloat-3e-prebuilt/platform.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/deps/SoftFloat-3e-prebuilt/platform.h b/deps/SoftFloat-3e-prebuilt/platform.h index ef99e21b97..588c548c60 100644 --- a/deps/SoftFloat-3e-prebuilt/platform.h +++ b/deps/SoftFloat-3e-prebuilt/platform.h @@ -17,8 +17,6 @@ #define BIGENDIAN 1 #elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define BIGENDIAN 1 -#elif defined(_BIG_ENDIAN) -#define BIGENDIAN 1 #elif defined(__sparc) #define BIGENDIAN 1 #elif defined(__sparc__) @@ -37,7 +35,9 @@ #define BIGENDIAN 1 #elif defined(__s390__) #define BIGENDIAN 1 -#elif defined(__LITTLE_ENDIAN__) +#endif + +#if defined(__LITTLE_ENDIAN__) #define LITTLEENDIAN 1 #elif defined(__ARMEL__) #define LITTLEENDIAN 1 @@ -53,8 +53,6 @@ #define LITTLEENDIAN 1 #elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define LITTLEENDIAN 1 -#elif defined(_LITTLE_ENDIAN) -#define LITTLEENDIAN 1 #elif defined(__i386__) #define LITTLEENDIAN 1 #elif defined(__alpha__) @@ -83,7 +81,11 @@ #define LITTLEENDIAN 1 #elif defined(__bfin__) #define LITTLEENDIAN 1 -#else +#endif + +#if defined(LITTLEENDIAN) && defined(BIGENDIAN) +#error unable to detect endianness +#elif !defined(LITTLEENDIAN) && !defined(BIGENDIAN) #error unable to detect endianness #endif From b0edd8752a00ea191decf302d9802b853d85fd4c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 12 Apr 2022 07:05:58 -0700 Subject: [PATCH 1095/2031] Liveness: modify encoding to support over 32 operands Prior to this, Liveness encoded `asm`, `call`, and `aggregate_init` with a single 32-bit integer, allowing up to 35 operands (3 are provided by the regular tomb_bits). However, the Zig language allows function calls with more than 35 arguments, inline assembly with more than 35 inputs, and anonymous tuples with more than 35 elements. The new encoding stores an index to the extra array instead of the bits directly, and then as many extra elements as needed to encode all the operands. The MSB is used as a flag to tell which element is the last one, allowing for 31 bits per element. Prior to this, print_air did not bother correctly printing tombstones for these instructions; now it does. In addition to updating the BigTomb iteration logic in the machine code backends, this commit extracts the common logic into the Liveness namespace. --- src/Liveness.zig | 71 ++++++++++++++++-- src/arch/aarch64/CodeGen.zig | 26 ++----- src/arch/arm/CodeGen.zig | 26 ++----- src/arch/riscv64/CodeGen.zig | 26 ++----- src/arch/x86_64/CodeGen.zig | 22 +----- src/codegen/c.zig | 2 +- src/print_air.zig | 16 +++- test/behavior/call.zig | 141 +++++++++++++++++++++++++++++++++++ 8 files changed, 240 insertions(+), 90 deletions(-) diff --git a/src/Liveness.zig b/src/Liveness.zig index 2ba59ec1de..d63c442482 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -178,11 +178,50 @@ pub fn deinit(l: *Liveness, gpa: Allocator) void { l.* = undefined; } +pub fn iterateBigTomb(l: Liveness, inst: Air.Inst.Index) BigTomb { + return .{ + .tomb_bits = l.getTombBits(inst), + .extra_start = l.special.get(inst) orelse 0, + .extra_offset = 0, + .extra = l.extra, + .bit_index = 0, + }; +} + /// How many tomb bits per AIR instruction. pub const bpi = 4; pub const Bpi = std.meta.Int(.unsigned, bpi); pub const OperandInt = std.math.Log2Int(Bpi); +/// Useful for decoders of Liveness information. +pub const BigTomb = struct { + tomb_bits: Liveness.Bpi, + bit_index: u32, + extra_start: u32, + extra_offset: u32, + extra: []const u32, + + /// Returns whether the next operand dies. + pub fn feed(bt: *BigTomb) bool { + const this_bit_index = bt.bit_index; + bt.bit_index += 1; + + const small_tombs = Liveness.bpi - 1; + if (this_bit_index < small_tombs) { + const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0; + return dies; + } + + const big_bit_index = this_bit_index - small_tombs; + while (big_bit_index - bt.extra_offset * 31 >= 31) { + bt.extra_offset += 1; + } + const dies = @truncate(u1, bt.extra[bt.extra_start + bt.extra_offset] >> + @intCast(u5, big_bit_index - bt.extra_offset * 31)) != 0; + return dies; + } +}; + /// In-progress data; on successful analysis converted into `Liveness`. const Analysis = struct { gpa: Allocator, @@ -428,6 +467,7 @@ fn analyzeInst( .inst = inst, .main_tomb = main_tomb, }; + defer extra_tombs.deinit(); try extra_tombs.feed(callee); for (args) |arg| { try extra_tombs.feed(arg); @@ -468,6 +508,7 @@ fn analyzeInst( .inst = inst, .main_tomb = main_tomb, }; + defer extra_tombs.deinit(); for (elements) |elem| { try extra_tombs.feed(elem); } @@ -555,6 +596,7 @@ fn analyzeInst( .inst = inst, .main_tomb = main_tomb, }; + defer extra_tombs.deinit(); for (outputs) |output| { if (output != .none) { try extra_tombs.feed(output); @@ -790,10 +832,10 @@ const ExtraTombs = struct { bit_index: usize = 0, tomb_bits: Bpi = 0, big_tomb_bits: u32 = 0, + big_tomb_bits_extra: std.ArrayListUnmanaged(u32) = .{}, fn feed(et: *ExtraTombs, op_ref: Air.Inst.Ref) !void { const this_bit_index = et.bit_index; - assert(this_bit_index < 32); // TODO mechanism for when there are greater than 32 operands et.bit_index += 1; const gpa = et.analysis.gpa; const op_index = Air.refToIndex(op_ref) orelse return; @@ -801,18 +843,37 @@ const ExtraTombs = struct { if (prev == null) { // Death. if (et.new_set) |ns| try ns.putNoClobber(gpa, op_index, {}); - if (this_bit_index < bpi - 1) { + const available_tomb_bits = bpi - 1; + if (this_bit_index < available_tomb_bits) { et.tomb_bits |= @as(Bpi, 1) << @intCast(OperandInt, this_bit_index); } else { - const big_bit_index = this_bit_index - (bpi - 1); - et.big_tomb_bits |= @as(u32, 1) << @intCast(u5, big_bit_index); + const big_bit_index = this_bit_index - available_tomb_bits; + while (big_bit_index >= (et.big_tomb_bits_extra.items.len + 1) * 31) { + // We need another element in the extra array. + try et.big_tomb_bits_extra.append(gpa, et.big_tomb_bits); + et.big_tomb_bits = 0; + } else { + const final_bit_index = big_bit_index - et.big_tomb_bits_extra.items.len * 31; + et.big_tomb_bits |= @as(u32, 1) << @intCast(u5, final_bit_index); + } } } } fn finish(et: *ExtraTombs) !void { et.tomb_bits |= @as(Bpi, @boolToInt(et.main_tomb)) << (bpi - 1); + // Signal the terminal big_tomb_bits element. + et.big_tomb_bits |= @as(u32, 1) << 31; + et.analysis.storeTombBits(et.inst, et.tomb_bits); - try et.analysis.special.put(et.analysis.gpa, et.inst, et.big_tomb_bits); + const extra_index = @intCast(u32, et.analysis.extra.items.len); + try et.analysis.extra.ensureUnusedCapacity(et.analysis.gpa, et.big_tomb_bits_extra.items.len + 1); + try et.analysis.special.put(et.analysis.gpa, et.inst, extra_index); + et.analysis.extra.appendSliceAssumeCapacity(et.big_tomb_bits_extra.items); + et.analysis.extra.appendAssumeCapacity(et.big_tomb_bits); + } + + fn deinit(et: *ExtraTombs) void { + et.big_tomb_bits_extra.deinit(et.analysis.gpa); } }; diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index ea946d6ba6..0aac47c6c5 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -202,26 +202,12 @@ const BlockData = struct { const BigTomb = struct { function: *Self, inst: Air.Inst.Index, - tomb_bits: Liveness.Bpi, - big_tomb_bits: u32, - bit_index: usize, + lbt: Liveness.BigTomb, fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void { - const this_bit_index = bt.bit_index; - bt.bit_index += 1; - - const op_int = @enumToInt(op_ref); - if (op_int < Air.Inst.Ref.typed_value_map.len) return; - const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); - - if (this_bit_index < Liveness.bpi - 1) { - const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0; - if (!dies) return; - } else { - const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1)); - const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0; - if (!dies) return; - } + const dies = bt.lbt.feed(); + const op_index = Air.refToIndex(op_ref) orelse return; + if (!dies) return; bt.function.processDeath(op_index); } @@ -3291,9 +3277,7 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT return BigTomb{ .function = self, .inst = inst, - .tomb_bits = self.liveness.getTombBits(inst), - .big_tomb_bits = self.liveness.special.get(inst) orelse 0, - .bit_index = 0, + .lbt = self.liveness.iterateBigTomb(inst), }; } diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 9a660ceff6..27f048999b 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -224,26 +224,12 @@ const BlockData = struct { const BigTomb = struct { function: *Self, inst: Air.Inst.Index, - tomb_bits: Liveness.Bpi, - big_tomb_bits: u32, - bit_index: usize, + lbt: Liveness.BigTomb, fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void { - const this_bit_index = bt.bit_index; - bt.bit_index += 1; - - const op_int = @enumToInt(op_ref); - if (op_int < Air.Inst.Ref.typed_value_map.len) return; - const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); - - if (this_bit_index < Liveness.bpi - 1) { - const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0; - if (!dies) return; - } else { - const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1)); - const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0; - if (!dies) return; - } + const dies = bt.lbt.feed(); + const op_index = Air.refToIndex(op_ref) orelse return; + if (!dies) return; bt.function.processDeath(op_index); } @@ -4076,9 +4062,7 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT return BigTomb{ .function = self, .inst = inst, - .tomb_bits = self.liveness.getTombBits(inst), - .big_tomb_bits = self.liveness.special.get(inst) orelse 0, - .bit_index = 0, + .lbt = self.liveness.iterateBigTomb(inst), }; } diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index a7d4c872a7..25a7a65f57 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -194,26 +194,12 @@ const Reloc = union(enum) { const BigTomb = struct { function: *Self, inst: Air.Inst.Index, - tomb_bits: Liveness.Bpi, - big_tomb_bits: u32, - bit_index: usize, + lbt: Liveness.BigTomb, fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void { - const this_bit_index = bt.bit_index; - bt.bit_index += 1; - - const op_int = @enumToInt(op_ref); - if (op_int < Air.Inst.Ref.typed_value_map.len) return; - const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); - - if (this_bit_index < Liveness.bpi - 1) { - const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0; - if (!dies) return; - } else { - const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1)); - const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0; - if (!dies) return; - } + const dies = bt.lbt.feed(); + const op_index = Air.refToIndex(op_ref) orelse return; + if (!dies) return; bt.function.processDeath(op_index); } @@ -2198,9 +2184,7 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT return BigTomb{ .function = self, .inst = inst, - .tomb_bits = self.liveness.getTombBits(inst), - .big_tomb_bits = self.liveness.special.get(inst) orelse 0, - .bit_index = 0, + .lbt = self.liveness.iterateBigTomb(inst), }; } diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index f52742ffa5..fb79097d54 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -272,24 +272,12 @@ const BlockData = struct { const BigTomb = struct { function: *Self, inst: Air.Inst.Index, - tomb_bits: Liveness.Bpi, - big_tomb_bits: u32, - bit_index: usize, + lbt: Liveness.BigTomb, fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void { - const this_bit_index = bt.bit_index; - bt.bit_index += 1; - + const dies = bt.lbt.feed(); const op_index = Air.refToIndex(op_ref) orelse return; - - if (this_bit_index < Liveness.bpi - 1) { - const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0; - if (!dies) return; - } else { - const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1)); - const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0; - if (!dies) return; - } + if (!dies) return; bt.function.processDeath(op_index); } @@ -4845,9 +4833,7 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT return BigTomb{ .function = self, .inst = inst, - .tomb_bits = self.liveness.getTombBits(inst), - .big_tomb_bits = self.liveness.special.get(inst) orelse 0, - .bit_index = 0, + .lbt = self.liveness.iterateBigTomb(inst), }; } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 4085305941..69288494bc 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1348,7 +1348,7 @@ pub const DeclGen = struct { return w.writeAll(name); }, .ErrorSet => { - comptime std.debug.assert(Type.initTag(.anyerror).abiSize(builtin.target) == 2); + comptime assert(Type.initTag(.anyerror).abiSize(builtin.target) == 2); return w.writeAll("uint16_t"); }, .ErrorUnion => { diff --git a/src/print_air.zig b/src/print_air.zig index 69d7b63b2a..82583a3c55 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -724,11 +724,21 @@ const Writer = struct { op_index: usize, operand: Air.Inst.Ref, ) @TypeOf(s).Error!void { - const dies = if (op_index < Liveness.bpi - 1) + const small_tomb_bits = Liveness.bpi - 1; + const dies = if (op_index < small_tomb_bits) w.liveness.operandDies(inst, @intCast(Liveness.OperandInt, op_index)) else blk: { - // TODO - break :blk false; + var extra_index = w.liveness.special.get(inst).?; + var tomb_op_index: usize = small_tomb_bits; + while (true) { + const bits = w.liveness.extra[extra_index]; + if (op_index < tomb_op_index + 31) { + break :blk @truncate(u1, bits >> @intCast(u5, op_index - tomb_op_index)) != 0; + } + if ((bits >> 31) != 0) break :blk false; + extra_index += 1; + tomb_op_index += 31; + } else unreachable; }; return w.writeInstRef(s, operand, dies); } diff --git a/test/behavior/call.zig b/test/behavior/call.zig index 119dc289b1..27d0bbf1d6 100644 --- a/test/behavior/call.zig +++ b/test/behavior/call.zig @@ -118,3 +118,144 @@ test "result location of function call argument through runtime condition and st .e = if (!runtime) .a else .b, }); } + +test "function call with 40 arguments" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest(thirty_nine: i32) !void { + const result = add( + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + thirty_nine, + 40, + ); + try expect(result == 820); + try expect(thirty_nine == 39); + } + + fn add( + a0: i32, + a1: i32, + a2: i32, + a3: i32, + a4: i32, + a5: i32, + a6: i32, + a7: i32, + a8: i32, + a9: i32, + a10: i32, + a11: i32, + a12: i32, + a13: i32, + a14: i32, + a15: i32, + a16: i32, + a17: i32, + a18: i32, + a19: i32, + a20: i32, + a21: i32, + a22: i32, + a23: i32, + a24: i32, + a25: i32, + a26: i32, + a27: i32, + a28: i32, + a29: i32, + a30: i32, + a31: i32, + a32: i32, + a33: i32, + a34: i32, + a35: i32, + a36: i32, + a37: i32, + a38: i32, + a39: i32, + a40: i32, + ) i32 { + return a0 + + a1 + + a2 + + a3 + + a4 + + a5 + + a6 + + a7 + + a8 + + a9 + + a10 + + a11 + + a12 + + a13 + + a14 + + a15 + + a16 + + a17 + + a18 + + a19 + + a20 + + a21 + + a22 + + a23 + + a24 + + a25 + + a26 + + a27 + + a28 + + a29 + + a30 + + a31 + + a32 + + a33 + + a34 + + a35 + + a36 + + a37 + + a38 + + a39 + + a40; + } + }; + try S.doTheTest(39); +} From 99657dca1fac04e5015203699e3eb0d656d66b09 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 12 Apr 2022 11:36:26 -0700 Subject: [PATCH 1096/2031] Sema: fix comptime equality of extern unions with same tag --- src/value.zig | 13 ++++++++----- test/behavior/union.zig | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/value.zig b/src/value.zig index c7960741f6..beb8bc7620 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2122,19 +2122,22 @@ pub const Value = extern union { const b_union = b.castTag(.@"union").?.data; switch (ty.containerLayout()) { .Packed, .Extern => { - // In this case, we must disregard mismatching tags and compare - // based on the in-memory bytes of the payloads. - @panic("TODO implement comparison of extern union values"); + const tag_ty = ty.unionTagTypeHypothetical(); + if (!a_union.tag.eql(b_union.tag, tag_ty, target)) { + // In this case, we must disregard mismatching tags and compare + // based on the in-memory bytes of the payloads. + @panic("TODO comptime comparison of extern union values with mismatching tags"); + } }, .Auto => { const tag_ty = ty.unionTagTypeHypothetical(); if (!a_union.tag.eql(b_union.tag, tag_ty, target)) { return false; } - const active_field_ty = ty.unionFieldType(a_union.tag, target); - return a_union.val.eql(b_union.val, active_field_ty, target); }, } + const active_field_ty = ty.unionFieldType(a_union.tag, target); + return a_union.val.eql(b_union.val, active_field_ty, target); }, else => {}, } else if (a_tag == .null_value or b_tag == .null_value) { diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 87efcbd5f1..8315ea8a22 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1168,3 +1168,18 @@ test "union with a large struct field" { var s: S = undefined; U.foo(U{ .s = s }); } + +test "comptime equality of extern unions with same tag" { + const S = struct { + const U = extern union { + a: i32, + b: f32, + }; + fn foo(comptime x: U) i32 { + return x.a; + } + }; + const a = S.U{ .a = 1234 }; + const b = S.U{ .a = 1234 }; + try expect(S.foo(a) == S.foo(b)); +} From ff7ef624f50824943a04dc4505c5313a4f9eae86 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 12 Apr 2022 12:19:32 -0700 Subject: [PATCH 1097/2031] Sema: fix comptime call with generic function as parameter --- src/Sema.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 503927f2a6..77e11d4c0c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6614,7 +6614,9 @@ fn zirParam( // partial type for generic functions but we still need to // detect if a function parameter is a generic function // to force the parent function to also be generic. - break :err error.GenericPoison; + if (!sema.inst_map.contains(inst)) { + break :err error.GenericPoison; + } } break :param_ty param_ty; } else |err| break :err err; From 7972bc8aa8edb7bd213bd5ccdc90d70fb730efd2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 12 Apr 2022 12:20:08 -0700 Subject: [PATCH 1098/2031] Sema: fix struct init ref --- src/Sema.zig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 77e11d4c0c..8142132679 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12694,7 +12694,12 @@ fn finishStructInit( } if (is_ref) { - const alloc = try block.addTy(.alloc, struct_ty); + const target = sema.mod.getTarget(); + const alloc_ty = try Type.ptr(sema.arena, target, .{ + .pointee_type = struct_ty, + .@"addrspace" = target_util.defaultAddressSpace(target, .local), + }); + const alloc = try block.addTy(.alloc, alloc_ty); for (field_inits) |field_init, i_usize| { const i = @intCast(u32, i_usize); const field_src = src; From 319555a6690a43de143698f13d6d107a7873dce0 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Tue, 12 Apr 2022 12:23:18 -0700 Subject: [PATCH 1099/2031] Add `floatFractionalBits` to replace `floatMantissaDigits` --- lib/std/math.zig | 2 +- lib/std/math/float.zig | 24 +++++++++++------------ lib/std/math/isnormal.zig | 2 +- lib/std/special/compiler_rt/fixXfYi.zig | 8 ++++---- lib/std/special/compiler_rt/floatXiYf.zig | 12 ++++++------ 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/std/math.zig b/lib/std/math.zig index 94e3ab6d2a..be49ba8030 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -38,7 +38,7 @@ pub const sqrt1_2 = 0.707106781186547524400844362104849039; pub const floatExponentBits = @import("math/float.zig").floatExponentBits; pub const floatMantissaBits = @import("math/float.zig").floatMantissaBits; -pub const floatMantissaDigits = @import("math/float.zig").floatMantissaDigits; +pub const floatFractionalBits = @import("math/float.zig").floatFractionalBits; pub const floatExponentMin = @import("math/float.zig").floatExponentMin; pub const floatExponentMax = @import("math/float.zig").floatExponentMax; pub const floatTrueMin = @import("math/float.zig").floatTrueMin; diff --git a/lib/std/math/float.zig b/lib/std/math/float.zig index 6d9c17d2a2..30e2268908 100644 --- a/lib/std/math/float.zig +++ b/lib/std/math/float.zig @@ -4,7 +4,7 @@ const expect = std.testing.expect; /// Creates a raw "1.0" mantissa for floating point type T. Used to dedupe f80 logic. fn mantissaOne(comptime T: type) comptime_int { - return if (floatMantissaDigits(T) == 64) 1 << 63 else 0; + return if (T == f80) 1 << floatFractionalBits(T) else 0; } /// Creates floating point type T from an unbiased exponent and raw mantissa. @@ -42,19 +42,19 @@ pub fn floatMantissaBits(comptime T: type) comptime_int { }; } -/// Returns the number of binary digits in the mantissa of floating point type T. -pub fn floatMantissaDigits(comptime T: type) comptime_int { +/// Returns the number of fractional bits in the mantissa of floating point type T. +pub fn floatFractionalBits(comptime T: type) comptime_int { assert(@typeInfo(T) == .Float); // standard IEEE floats have an implicit 0.m or 1.m integer part // f80 is special and has an explicitly stored bit in the MSB - // this function corresponds to `MANT_DIG' constants from C + // this function corresponds to `MANT_DIG - 1' from C return switch (@typeInfo(T).Float.bits) { - 16 => 11, - 32 => 24, - 64 => 53, - 80 => 64, - 128 => 113, + 16 => 10, + 32 => 23, + 64 => 52, + 80 => 63, + 128 => 112, else => @compileError("unknown floating point type " ++ @typeName(T)), }; } @@ -89,7 +89,7 @@ pub fn floatMax(comptime T: type) T { /// Returns the machine epsilon of floating point type T. pub fn floatEps(comptime T: type) T { - return reconstructFloat(T, -(floatMantissaDigits(T) - 1), mantissaOne(T)); + return reconstructFloat(T, -floatFractionalBits(T), mantissaOne(T)); } /// Returns the value inf for floating point type T. @@ -104,7 +104,7 @@ test "std.math.float" { try expect(@bitSizeOf(T) == size); // for machine epsilon, assert expmin <= -prec <= expmax - try expect(floatExponentMin(T) <= -(floatMantissaDigits(T) - 1)); - try expect(-(floatMantissaDigits(T) - 1) <= floatExponentMax(T)); + try expect(floatExponentMin(T) <= -floatFractionalBits(T)); + try expect(-floatFractionalBits(T) <= floatExponentMax(T)); } } diff --git a/lib/std/math/isnormal.zig b/lib/std/math/isnormal.zig index 7376b86eb9..42b2e1c188 100644 --- a/lib/std/math/isnormal.zig +++ b/lib/std/math/isnormal.zig @@ -41,7 +41,7 @@ test "math.isNormal" { try expect(!isNormal(@as(T, math.floatTrueMin(T)))); // largest subnormal - try expect(!isNormal(@bitCast(T, ~(~@as(TBits, 0) << math.floatMantissaDigits(T) - 1)))); + try expect(!isNormal(@bitCast(T, ~(~@as(TBits, 0) << math.floatFractionalBits(T))))); // non-finite numbers try expect(!isNormal(-math.inf(T))); diff --git a/lib/std/special/compiler_rt/fixXfYi.zig b/lib/std/special/compiler_rt/fixXfYi.zig index 9e46f74a53..01832ec56f 100644 --- a/lib/std/special/compiler_rt/fixXfYi.zig +++ b/lib/std/special/compiler_rt/fixXfYi.zig @@ -12,7 +12,7 @@ pub inline fn fixXfYi(comptime I: type, a: anytype) I { const rep_t = std.meta.Int(.unsigned, float_bits); const sig_bits = math.floatMantissaBits(F); const exp_bits = math.floatExponentBits(F); - const fractional_sig_bits = math.floatMantissaDigits(F) - 1; + const fractional_bits = math.floatFractionalBits(F); const implicit_bit = if (F != f80) (@as(rep_t, 1) << sig_bits) else 0; const max_exp = (1 << (exp_bits - 1)); @@ -42,10 +42,10 @@ pub inline fn fixXfYi(comptime I: type, a: anytype) I { // If 0 <= exponent < sig_bits, right shift to get the result. // Otherwise, shift left. var result: I = undefined; - if (exponent < fractional_sig_bits) { - result = @intCast(I, significand >> @intCast(Log2Int(rep_t), fractional_sig_bits - exponent)); + if (exponent < fractional_bits) { + result = @intCast(I, significand >> @intCast(Log2Int(rep_t), fractional_bits - exponent)); } else { - result = @intCast(I, significand) << @intCast(Log2Int(I), exponent - fractional_sig_bits); + result = @intCast(I, significand) << @intCast(Log2Int(I), exponent - fractional_bits); } if ((@typeInfo(I).Int.signedness == .signed) and negative) diff --git a/lib/std/special/compiler_rt/floatXiYf.zig b/lib/std/special/compiler_rt/floatXiYf.zig index c006a9df97..068413f715 100644 --- a/lib/std/special/compiler_rt/floatXiYf.zig +++ b/lib/std/special/compiler_rt/floatXiYf.zig @@ -17,9 +17,9 @@ pub fn floatXiYf(comptime T: type, x: anytype) T { const float_bits = @bitSizeOf(T); const int_bits = @bitSizeOf(@TypeOf(x)); const exp_bits = math.floatExponentBits(T); - const sig_bits = math.floatMantissaDigits(T) - 1; // Only counts the fractional bits + const fractional_bits = math.floatFractionalBits(T); const exp_bias = math.maxInt(std.meta.Int(.unsigned, exp_bits - 1)); - const implicit_bit = if (T != f80) @as(uT, 1) << sig_bits else 0; + const implicit_bit = if (T != f80) @as(uT, 1) << fractional_bits else 0; const max_exp = exp_bias; // Sign @@ -29,14 +29,14 @@ pub fn floatXiYf(comptime T: type, x: anytype) T { // Compute significand var exp = int_bits - @clz(Z, abs_val) - 1; - if (int_bits <= sig_bits or exp <= sig_bits) { - const shift_amt = sig_bits - @intCast(math.Log2Int(uT), exp); + if (int_bits <= fractional_bits or exp <= fractional_bits) { + const shift_amt = fractional_bits - @intCast(math.Log2Int(uT), exp); // Shift up result to line up with the significand - no rounding required result = (@intCast(uT, abs_val) << shift_amt); result ^= implicit_bit; // Remove implicit integer bit } else { - var shift_amt = @intCast(math.Log2Int(Z), exp - sig_bits); + var shift_amt = @intCast(math.Log2Int(Z), exp - fractional_bits); const exact_tie: bool = @ctz(Z, abs_val) == shift_amt - 1; // Shift down result and remove implicit integer bit @@ -53,7 +53,7 @@ pub fn floatXiYf(comptime T: type, x: anytype) T { result += (@as(uT, exp) + exp_bias) << math.floatMantissaBits(T); // If the result included a carry, we need to restore the explicit integer bit - if (T == f80) result |= 1 << sig_bits; + if (T == f80) result |= 1 << fractional_bits; return @bitCast(T, sign_bit | result); } From aaac8eae683172546ce9e018d8c419f62793f8d4 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Tue, 12 Apr 2022 10:54:27 -0700 Subject: [PATCH 1100/2031] Use 0-indexing for incremental compile error tests --- src/test.zig | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/test.zig b/src/test.zig index 3bb17f7605..8c2a15e844 100644 --- a/src/test.zig +++ b/src/test.zig @@ -656,10 +656,10 @@ pub const TestContext = struct { const Strategy = enum { /// Execute tests as independent compilations, unless they are explicitly - /// incremental ("foo.1.zig", "foo.2.zig", etc.) + /// incremental ("foo.0.zig", "foo.1.zig", etc.) independent, /// Execute all tests as incremental updates to a single compilation. Explicitly - /// incremental tests ("foo.1.zig", "foo.2.zig", etc.) still execute in order + /// incremental tests ("foo.0.zig", "foo.1.zig", etc.) still execute in order incremental, }; @@ -716,8 +716,8 @@ pub const TestContext = struct { }; } - /// Sort test filenames in-place, so that incremental test cases ("foo.1.zig", - /// "foo.2.zig", etc.) are contiguous and appear in numerical order. + /// Sort test filenames in-place, so that incremental test cases ("foo.0.zig", + /// "foo.1.zig", etc.) are contiguous and appear in numerical order. fn sortTestFilenames( filenames: [][]const u8, ) void { @@ -804,8 +804,8 @@ pub const TestContext = struct { } else { // This is not the same test sequence, so the new file must be the first file - // in a new sequence ("*.1.zig") or an independent test file ("*.zig") - if (new_parts.test_index != null and new_parts.test_index.? != 1) return error.InvalidIncrementalTestIndex; + // in a new sequence ("*.0.zig") or an independent test file ("*.zig") + if (new_parts.test_index != null and new_parts.test_index.? != 0) return error.InvalidIncrementalTestIndex; if (strategy == .independent) opt_case = null; // Generate a new independent test case for this update @@ -1239,7 +1239,7 @@ pub const TestContext = struct { if (all_errors.list.len != 0) { print( "\nCase '{s}': unexpected errors at update_index={d}:\n{s}\n", - .{ case.name, update_index + 1, hr }, + .{ case.name, update_index, hr }, ); for (all_errors.list) |err_msg| { switch (err_msg) { @@ -1401,7 +1401,7 @@ pub const TestContext = struct { } if (any_failed) { - print("\nupdate_index={d}\n", .{update_index + 1}); + print("\nupdate_index={d}\n", .{update_index}); return error.WrongCompileErrors; } }, @@ -1508,7 +1508,7 @@ pub const TestContext = struct { .cwd = tmp_dir_path, }) catch |err| { print("\nupdate_index={d} The following command failed with {s}:\n", .{ - update_index + 1, @errorName(err), + update_index, @errorName(err), }); dumpArgs(argv.items); return error.ChildProcessExecution; From baeff1762b90fc9a4cd4b1d6a7db6ba43fd35356 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 13 Apr 2022 13:50:35 +0200 Subject: [PATCH 1101/2031] stage2,x64: recursively mark decls as alive when lowering --- src/arch/x86_64/CodeGen.zig | 39 ++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index fb79097d54..71074edc2d 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -3903,17 +3903,17 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { const pl_op = self.air.instructions.items(.data)[inst].pl_op; const operand = pl_op.operand; const ty = self.air.typeOf(operand); + const mcv = try self.resolveInst(operand); - if (!self.liveness.operandDies(inst, 0)) { - const mcv = try self.resolveInst(operand); - const name = self.air.nullTerminatedString(pl_op.payload); + log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), mcv }); - const tag = self.air.instructions.items(.tag)[inst]; - switch (tag) { - .dbg_var_ptr => try self.genVarDbgInfo(ty.childType(), mcv, name), - .dbg_var_val => try self.genVarDbgInfo(ty, mcv, name), - else => unreachable, - } + const name = self.air.nullTerminatedString(pl_op.payload); + + const tag = self.air.instructions.items(.tag)[inst]; + switch (tag) { + .dbg_var_ptr => try self.genVarDbgInfo(ty.childType(), mcv, name), + .dbg_var_val => try self.genVarDbgInfo(ty, mcv, name), + else => unreachable, } return self.finishAir(inst, .dead, .{ operand, .none, .none }); @@ -6089,6 +6089,7 @@ fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCV } fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue { + log.debug("lowerDeclRef: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() }); const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); @@ -6100,7 +6101,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa } } - decl.alive = true; + decl.markAlive(); + if (self.bin_file.cast(link.File.Elf)) |elf_file| { const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; @@ -6120,8 +6122,6 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa } else { return self.fail("TODO codegen non-ELF const Decl pointer", .{}); } - - _ = tv; } fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { @@ -6144,6 +6144,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { } fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { + log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() }); if (typed_value.val.isUndef()) return MCValue{ .undef = {} }; const ptr_bits = self.target.cpu.arch.ptrBitWidth(); @@ -6181,8 +6182,6 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { .Bool => { return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; }, - .ComptimeInt => unreachable, // semantic analysis prevents this - .ComptimeFloat => unreachable, // semantic analysis prevents this .Optional => { if (typed_value.ty.isPtrLikeOptional()) { if (typed_value.val.isNull()) @@ -6243,6 +6242,18 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { } } }, + + .ComptimeInt => unreachable, + .ComptimeFloat => unreachable, + .Type => unreachable, + .EnumLiteral => unreachable, + .Void => unreachable, + .NoReturn => unreachable, + .Undefined => unreachable, + .Null => unreachable, + .BoundFn => unreachable, + .Opaque => unreachable, + else => {}, } From 4c50a27d682d3241ec73c7130d69d1d2f2553b86 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 13 Apr 2022 14:31:04 +0200 Subject: [PATCH 1102/2031] stage2,x64: generate debug info for local vars at hardcoded mem addr --- src/arch/x86_64/CodeGen.zig | 66 +++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 71074edc2d..a06f7941b4 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -3926,31 +3926,21 @@ fn genVarDbgInfo( name: [:0]const u8, ) !void { const name_with_null = name.ptr[0 .. name.len + 1]; - switch (mcv) { - .register => |reg| { - switch (self.debug_output) { - .dwarf => |dw| { - const dbg_info = &dw.dbg_info; - try dbg_info.ensureUnusedCapacity(3); - dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.variable)); + switch (self.debug_output) { + .dwarf => |dw| { + const dbg_info = &dw.dbg_info; + try dbg_info.append(@enumToInt(link.File.Dwarf.AbbrevKind.variable)); + + switch (mcv) { + .register => |reg| { + try dbg_info.ensureUnusedCapacity(2); dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1, // ULEB128 dwarf expression length reg.dwarfLocOp(), }); - try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); - try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string }, - .plan9 => {}, - .none => {}, - } - }, - .ptr_stack_offset, .stack_offset => |off| { - switch (self.debug_output) { - .dwarf => |dw| { - const dbg_info = &dw.dbg_info; - try dbg_info.ensureUnusedCapacity(8); - dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.variable)); + .ptr_stack_offset, .stack_offset => |off| { + try dbg_info.ensureUnusedCapacity(7); const fixup = dbg_info.items.len; dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1, // we will backpatch it after we encode the displacement in LEB128 @@ -3958,18 +3948,36 @@ fn genVarDbgInfo( }); leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable; dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); - try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); - try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string - }, - .plan9 => {}, - .none => {}, + .memory => |addr| { + const endian = self.target.cpu.arch.endian(); + const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8)); + try dbg_info.ensureUnusedCapacity(2 + ptr_width); + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1 + ptr_width, + DW.OP.addr, // literal address + }); + switch (ptr_width) { + 0...4 => { + try dbg_info.writer().writeInt(u32, @intCast(u32, addr), endian); + }, + 5...8 => { + try dbg_info.writer().writeInt(u64, addr, endian); + }, + else => unreachable, + } + }, + else => { + log.debug("TODO generate debug info for {}", .{mcv}); + }, } + + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string }, - else => { - log.debug("TODO generate debug info for {}", .{mcv}); - }, + .plan9 => {}, + .none => {}, } } From 0bf72833307bf500ebec1a08641600f12cda0434 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 13 Apr 2022 16:22:24 +0200 Subject: [PATCH 1103/2031] dwarf: gen debug info for arrays --- src/codegen.zig | 2 -- src/link/Dwarf.zig | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index 2ace45c8cb..68e1f3697f 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -203,7 +203,6 @@ pub fn generateSymbol( }, .Array => switch (typed_value.val.tag()) { .bytes => { - // TODO populate .debug_info for the array const payload = typed_value.val.castTag(.bytes).?; const len = @intCast(usize, typed_value.ty.arrayLenIncludingSentinel()); // The bytes payload already includes the sentinel, if any @@ -212,7 +211,6 @@ pub fn generateSymbol( return Result{ .appended = {} }; }, .aggregate => { - // TODO populate .debug_info for the array const elem_vals = typed_value.val.castTag(.aggregate).?.data; const elem_ty = typed_value.ty.elemType(); const len = @intCast(usize, typed_value.ty.arrayLenIncludingSentinel()); diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index fc392bfe3e..bd7f429177 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -270,6 +270,27 @@ pub const DeclState = struct { try self.addTypeReloc(atom, ty.childType(), @intCast(u32, index), null); } }, + .Array => { + // DW.AT.array_type + try dbg_info_buffer.append(@enumToInt(AbbrevKind.array_type)); + // DW.AT.name, DW.FORM.string + try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); + // DW.AT.type, DW.FORM.ref4 + var index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try self.addTypeReloc(atom, ty.childType(), @intCast(u32, index), null); + // DW.AT.subrange_type + try dbg_info_buffer.append(@enumToInt(AbbrevKind.array_dim)); + // DW.AT.type, DW.FORM.ref4 + index = dbg_info_buffer.items.len; + try dbg_info_buffer.resize(index + 4); + try self.addTypeReloc(atom, Type.usize, @intCast(u32, index), null); + // DW.AT.count, DW.FORM.udata + const len = ty.arrayLenIncludingSentinel(); + try leb128.writeULEB128(dbg_info_buffer.writer(), len); + // DW.AT.array_type delimit children + try dbg_info_buffer.append(0); + }, .Struct => blk: { // DW.AT.structure_type try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_type)); @@ -564,6 +585,8 @@ pub const AbbrevKind = enum(u8) { pad1, parameter, variable, + array_type, + array_dim, }; /// The reloc offset for the virtual address of a function in its Line Number Program. @@ -1357,6 +1380,18 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.AT.name, DW.FORM.string, 0, 0, // table sentinel + @enumToInt(AbbrevKind.array_type), + DW.TAG.array_type, DW.CHILDREN.yes, // header + DW.AT.name, DW.FORM.string, + DW.AT.type, DW.FORM.ref4, + 0, + 0, // table sentinel + @enumToInt(AbbrevKind.array_dim), + DW.TAG.subrange_type, DW.CHILDREN.no, // header + DW.AT.type, DW.FORM.ref4, + DW.AT.count, DW.FORM.udata, + 0, + 0, // table sentinel 0, 0, 0, // section sentinel From 3f912430bdddede8c3f6a9555b76499aa2dabb7e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 13 Apr 2022 16:24:56 +0200 Subject: [PATCH 1104/2031] stage2,x64: deref memory if referenced via GOT for local vars --- src/arch/x86_64/CodeGen.zig | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index a06f7941b4..32065fa1bb 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -3911,8 +3911,8 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { const tag = self.air.instructions.items(.tag)[inst]; switch (tag) { - .dbg_var_ptr => try self.genVarDbgInfo(ty.childType(), mcv, name), - .dbg_var_val => try self.genVarDbgInfo(ty, mcv, name), + .dbg_var_ptr => try self.genVarDbgInfo(tag, ty.childType(), mcv, name), + .dbg_var_val => try self.genVarDbgInfo(tag, ty, mcv, name), else => unreachable, } @@ -3921,6 +3921,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { fn genVarDbgInfo( self: *Self, + tag: Air.Inst.Tag, ty: Type, mcv: MCValue, name: [:0]const u8, @@ -3952,9 +3953,14 @@ fn genVarDbgInfo( .memory => |addr| { const endian = self.target.cpu.arch.endian(); const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8)); + const is_ptr = switch (tag) { + .dbg_var_ptr => true, + .dbg_var_val => false, + else => unreachable, + }; try dbg_info.ensureUnusedCapacity(2 + ptr_width); dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc - 1 + ptr_width, + 1 + ptr_width + @boolToInt(is_ptr), DW.OP.addr, // literal address }); switch (ptr_width) { @@ -3966,6 +3972,10 @@ fn genVarDbgInfo( }, else => unreachable, } + if (is_ptr) { + // We need deref the address as we point to the value via GOT entry. + try dbg_info.append(DW.OP.deref); + } }, else => { log.debug("TODO generate debug info for {}", .{mcv}); From edb428fae42ea82c49347fce6d48d80f1fed6ef1 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 13 Apr 2022 19:05:19 +0200 Subject: [PATCH 1105/2031] macho,x64: resolve debug info relocs for RIP-based addressing Sometimes we will want to generate debug info for a constant that has been lowered to memory and not copied anywhere else. For this we will need to defer resolution on PIE platforms until all locals (including GOT entries) have been allocated. --- src/arch/x86_64/CodeGen.zig | 11 ++++++- src/link/Dwarf.zig | 43 +++++++++++++++++++++++++++ src/link/MachO.zig | 8 +++++ src/link/MachO/DebugSymbols.zig | 52 ++++++++++++++++++++++++++++++++- 4 files changed, 112 insertions(+), 2 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 32065fa1bb..53a6bfc4d9 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -3950,7 +3950,7 @@ fn genVarDbgInfo( leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable; dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); }, - .memory => |addr| { + .memory, .got_load, .direct_load => { const endian = self.target.cpu.arch.endian(); const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8)); const is_ptr = switch (tag) { @@ -3963,6 +3963,11 @@ fn genVarDbgInfo( 1 + ptr_width + @boolToInt(is_ptr), DW.OP.addr, // literal address }); + const offset = @intCast(u32, dbg_info.items.len); + const addr = switch (mcv) { + .memory => |addr| addr, + else => 0, + }; switch (ptr_width) { 0...4 => { try dbg_info.writer().writeInt(u32, @intCast(u32, addr), endian); @@ -3976,6 +3981,10 @@ fn genVarDbgInfo( // We need deref the address as we point to the value via GOT entry. try dbg_info.append(DW.OP.deref); } + switch (mcv) { + .got_load, .direct_load => |index| try dw.addExprlocReloc(index, offset, is_ptr), + else => {}, + } }, else => { log.debug("TODO generate debug info for {}", .{mcv}); diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index bd7f429177..248521c544 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -79,6 +79,7 @@ pub const DeclState = struct { std.hash_map.default_max_load_percentage, ) = .{}, abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{}, + exprloc_relocs: std.ArrayListUnmanaged(ExprlocRelocation) = .{}, fn init(gpa: Allocator, target: std.Target) DeclState { return .{ @@ -97,6 +98,16 @@ pub const DeclState = struct { self.abbrev_table.deinit(self.gpa); self.abbrev_resolver.deinit(self.gpa); self.abbrev_relocs.deinit(self.gpa); + self.exprloc_relocs.deinit(self.gpa); + } + + pub fn addExprlocReloc(self: *DeclState, target: u32, offset: u32, is_ptr: bool) !void { + log.debug("{x}: target sym @{d}, via GOT {}", .{ offset, target, is_ptr }); + try self.exprloc_relocs.append(self.gpa, .{ + .@"type" = if (is_ptr) .got_load else .direct_load, + .target = target, + .offset = offset, + }); } pub fn addTypeReloc( @@ -549,6 +560,18 @@ pub const AbbrevRelocation = struct { addend: u32, }; +pub const ExprlocRelocation = struct { + /// Type of the relocation: direct load ref, or GOT load ref (via GOT table) + @"type": enum { + direct_load, + got_load, + }, + /// Index of the target in the linker's locals symbol table. + target: u32, + /// Offset within the debug info buffer where to patch up the address value. + offset: u32, +}; + pub const SrcFn = struct { /// Offset from the beginning of the Debug Line Program header that contains this function. off: u32, @@ -1009,6 +1032,26 @@ pub fn commitDeclState( } } + while (decl_state.exprloc_relocs.popOrNull()) |reloc| { + switch (self.tag) { + .macho => { + const macho_file = file.cast(File.MachO).?; + const d_sym = &macho_file.d_sym.?; + try d_sym.relocs.append(d_sym.base.base.allocator, .{ + .@"type" = switch (reloc.@"type") { + .direct_load => .direct_load, + .got_load => .got_load, + }, + .target = reloc.target, + .offset = reloc.offset + atom.off, + .addend = 0, + .prev_vaddr = 0, + }); + }, + else => unreachable, + } + } + try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items); } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index b193068361..d359a3fd5d 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -3472,6 +3472,9 @@ pub fn closeFiles(self: MachO) void { for (self.dylibs.items) |dylib| { dylib.file.close(); } + if (self.d_sym) |ds| { + ds.file.close(); + } } fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) void { @@ -4274,6 +4277,11 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { self.got_entries_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {}; self.got_entries.items[got_index] = .{ .target = .{ .local = 0 }, .atom = undefined }; _ = self.got_entries_table.swapRemove(.{ .local = decl.link.macho.local_sym_index }); + + if (self.d_sym) |*d_sym| { + d_sym.swapRemoveRelocs(decl.link.macho.local_sym_index); + } + log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, decl.link.macho.local_sym_index, diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 885f0ca6a8..aa7a29fcd1 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -59,6 +59,19 @@ debug_aranges_section_dirty: bool = false, debug_info_header_dirty: bool = false, debug_line_header_dirty: bool = false, +relocs: std.ArrayListUnmanaged(Reloc) = .{}, + +pub const Reloc = struct { + @"type": enum { + direct_load, + got_load, + }, + target: u32, + offset: u64, + addend: u32, + prev_vaddr: u64, +}; + /// You must call this function *after* `MachO.populateMissingMetadata()` /// has been called to get a viable debug symbols output. pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void { @@ -254,6 +267,30 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti // Zig source code. const module = options.module orelse return error.LinkingWithoutZigSourceUnimplemented; + for (self.relocs.items) |*reloc| { + const sym = switch (reloc.@"type") { + .direct_load => self.base.locals.items[reloc.target], + .got_load => blk: { + const got_index = self.base.got_entries_table.get(.{ .local = reloc.target }).?; + const got_entry = self.base.got_entries.items[got_index]; + break :blk self.base.locals.items[got_entry.atom.local_sym_index]; + }, + }; + if (sym.n_value == reloc.prev_vaddr) continue; + + const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; + const sect = &seg.sections.items[self.debug_info_section_index.?]; + const file_offset = sect.offset + reloc.offset; + log.debug("resolving relocation: {d}@{x} ('{s}') at offset {x}", .{ + reloc.target, + sym.n_value, + self.base.getString(sym.n_strx), + file_offset, + }); + try self.file.pwriteAll(mem.asBytes(&sym.n_value), file_offset); + reloc.prev_vaddr = sym.n_value; + } + if (self.debug_abbrev_section_dirty) { try self.dwarf.writeDbgAbbrev(&self.base.base); self.load_commands_dirty = true; @@ -330,7 +367,20 @@ pub fn deinit(self: *DebugSymbols, allocator: Allocator) void { } self.load_commands.deinit(allocator); self.dwarf.deinit(); - self.file.close(); + self.relocs.deinit(allocator); +} + +pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void { + // TODO re-implement using a hashmap with free lists + var last_index: usize = 0; + while (last_index < self.relocs.items.len) { + const reloc = self.relocs.items[last_index]; + if (reloc.target == target) { + _ = self.relocs.swapRemove(last_index); + } else { + last_index += 1; + } + } } fn copySegmentCommand( From 9c509f1526b4560b4e97367a18755a9d1ae6fcf7 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Tue, 12 Apr 2022 17:13:59 -0700 Subject: [PATCH 1106/2031] Enable passing 'Dir.rename directories' fs test on Windows Looks like d3f87f8ac01039722197a13a12342fc747a90567 fixed the standard cases of dir renaming, but the edge cases (renaming onto an existing empty/non-empty directory) are still behaving differently than on non-Windows. --- lib/std/fs/test.zig | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index ca512636ed..2899d0412e 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -434,9 +434,6 @@ test "Dir.rename files" { } test "Dir.rename directories" { - // TODO: Fix on Windows, see https://github.com/ziglang/zig/issues/6364 - if (builtin.os.tag == .windows) return error.SkipZigTest; - var tmp_dir = tmpDir(.{}); defer tmp_dir.cleanup(); From 497f8a60986eac6c5abd3230b34b975bd1701c53 Mon Sep 17 00:00:00 2001 From: Eric Shrewsberry Date: Wed, 13 Apr 2022 10:10:20 +0000 Subject: [PATCH 1107/2031] Fix bug in PriorityQueue::removeIndex() Fix to call siftDown on the removed index instead of always on index 0. Updated test to a test that fails before and passes now. PriorityDequeue does not have this issue. --- lib/std/priority_queue.zig | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/std/priority_queue.zig b/lib/std/priority_queue.zig index 9f82a15d28..52d9f59951 100644 --- a/lib/std/priority_queue.zig +++ b/lib/std/priority_queue.zig @@ -100,7 +100,7 @@ pub fn PriorityQueue(comptime T: type, comptime Context: type, comptime compareF const item = self.items[index]; self.items[index] = last; self.len -= 1; - siftDown(self, 0); + siftDown(self, index); return item; } @@ -460,22 +460,25 @@ test "std.PriorityQueue: remove at index" { var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); - try queue.add(3); - try queue.add(2); - try queue.add(1); + const items = [_]u32{ 2, 1, 8, 9, 3, 4, 5 }; + for (items) |e| { + _ = try queue.add(e); + } var it = queue.iterator(); - var elem = it.next(); var idx: usize = 0; - const two_idx = while (elem != null) : (elem = it.next()) { - if (elem.? == 2) + const two_idx = while (it.next()) |elem| { + if (elem == 2) break idx; idx += 1; } else unreachable; - + var sorted_items = [_]u32{ 1, 3, 4, 5, 8, 9 }; try expectEqual(queue.removeIndex(two_idx), 2); - try expectEqual(queue.remove(), 1); - try expectEqual(queue.remove(), 3); + + var i: usize = 0; + while (queue.removeOrNull()) |n| : (i += 1) { + try expectEqual(n, sorted_items[i]); + } try expectEqual(queue.removeOrNull(), null); } From 6ad9ac59e7461fa6bd906f7c4feb662509082c17 Mon Sep 17 00:00:00 2001 From: vi Date: Tue, 12 Apr 2022 23:58:04 -0600 Subject: [PATCH 1108/2031] std.math.float: fix f80-backed c_longdouble consts --- lib/std/math/float.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/math/float.zig b/lib/std/math/float.zig index 30e2268908..72c7f086ac 100644 --- a/lib/std/math/float.zig +++ b/lib/std/math/float.zig @@ -4,7 +4,7 @@ const expect = std.testing.expect; /// Creates a raw "1.0" mantissa for floating point type T. Used to dedupe f80 logic. fn mantissaOne(comptime T: type) comptime_int { - return if (T == f80) 1 << floatFractionalBits(T) else 0; + return if (@typeInfo(T).Float.bits == 80) 1 << floatFractionalBits(T) else 0; } /// Creates floating point type T from an unbiased exponent and raw mantissa. From 9b82e7f558d5aa66ac1dc285af2345d46cc3d4d6 Mon Sep 17 00:00:00 2001 From: Andrew Lee Date: Wed, 13 Apr 2022 10:50:46 -0600 Subject: [PATCH 1109/2031] std/bounded_array.zig: Add Writer interface --- lib/std/bounded_array.zig | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/std/bounded_array.zig b/lib/std/bounded_array.zig index 7c562b7c84..0b0efc55e4 100644 --- a/lib/std/bounded_array.zig +++ b/lib/std/bounded_array.zig @@ -239,6 +239,24 @@ pub fn BoundedArray(comptime T: type, comptime capacity: usize) type { assert(self.len <= capacity); mem.set(T, self.slice()[old_len..self.len], value); } + + pub const Writer = if (T != u8) + @compileError("The Writer interface is only defined for BoundedArray(u8, ...) " ++ + "but the given type is BoundedArray(" ++ @typeName(T) ++ ", ...)") + else + std.io.Writer(*Self, error{Overflow}, appendWrite); + + /// Initializes a writer which will write into the array. + pub fn writer(self: *Self) Writer { + return .{ .context = self }; + } + + /// Same as `appendSlice` except it returns the number of bytes written, which is always the same + /// as `m.len`. The purpose of this function existing is to match `std.io.Writer` API. + fn appendWrite(self: *Self, m: []const u8) error{Overflow}!usize { + try self.appendSlice(m); + return m.len; + } }; } @@ -336,4 +354,10 @@ test "BoundedArray" { const swapped = a.swapRemove(0); try testing.expectEqual(swapped, 0xdd); try testing.expectEqual(a.get(0), 0xee); + + while (a.popOrNull()) |_| {} + const w = a.writer(); + const s = "hello, this is a test string"; + try w.writeAll(s); + try testing.expectEqualStrings(s, a.constSlice()); } From 2a00df9c091498268b58dd671f646a5590439b7a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Apr 2022 00:36:54 -0700 Subject: [PATCH 1110/2031] Sema: fix generic instantiation false negatives The problem was that types of non-anytype parameters were being included as part of the check to see if generic function instantiations were equal. Now, Module.Fn additionally stores the information for whether each parameter is anytype or not. `generic_poison` cannot be used to signal this because the type is still needed for comptime arguments; in such case the type will not be present in the newly generated function prototype. This presented one additional challenge: we need to compare equality of two values where one of them is post-coercion and the other is not. So we make some minor adjustments to `Type.eql` to support this. I think this small complexity tradeoff is worth it because it means the compiler does much less work on the hot path that a generic function is called and there is already an existing matching instantiation. closes #11146 --- src/Module.zig | 42 ++++++---- src/Sema.zig | 163 +++++++++++++++++++++++++------------ src/value.zig | 21 ++++- test/behavior/generics.zig | 18 ++++ 4 files changed, 178 insertions(+), 66 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index fdf61c4bf6..83252c76ad 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1394,7 +1394,15 @@ pub const Fn = struct { /// there is a `TypedValue` here for each parameter of the function. /// Non-comptime parameters are marked with a `generic_poison` for the value. /// Non-anytype parameters are marked with a `generic_poison` for the type. - comptime_args: ?[*]TypedValue = null, + /// These never have .generic_poison for the Type + /// because the Type is needed to pass to `Type.eql` and for inserting comptime arguments + /// into the inst_map when analyzing the body of a generic function instantiation. + /// Instead, the is_anytype knowledge is communicated via `anytype_args`. + comptime_args: ?[*]TypedValue, + /// When comptime_args is null, this is undefined. Otherwise, this flags each + /// parameter and tells whether it is anytype. + /// TODO apply the same enhancement for param_names below to this field. + anytype_args: [*]bool, /// The ZIR instruction that is a function instruction. Use this to find /// the body. We store this rather than the body directly so that when ZIR /// is regenerated on update(), we can map this to the new corresponding @@ -4782,18 +4790,24 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem else => continue, }; - if (func.comptime_args) |comptime_args| { + + const param_ty = if (func.comptime_args) |comptime_args| t: { const arg_tv = comptime_args[total_param_index]; - if (arg_tv.val.tag() != .generic_poison) { - // We have a comptime value for this parameter. - const arg = try sema.addConstant(arg_tv.ty, arg_tv.val); - sema.inst_map.putAssumeCapacityNoClobber(inst, arg); - total_param_index += 1; - continue; - } - } - const param_type = fn_ty_info.param_types[runtime_param_index]; - const opt_opv = sema.typeHasOnePossibleValue(&inner_block, param.src, param_type) catch |err| switch (err) { + + const arg_val = if (arg_tv.val.tag() != .generic_poison) + arg_tv.val + else if (arg_tv.ty.onePossibleValue()) |opv| + opv + else + break :t arg_tv.ty; + + const arg = try sema.addConstant(arg_tv.ty, arg_val); + sema.inst_map.putAssumeCapacityNoClobber(inst, arg); + total_param_index += 1; + continue; + } else fn_ty_info.param_types[runtime_param_index]; + + const opt_opv = sema.typeHasOnePossibleValue(&inner_block, param.src, param_ty) catch |err| switch (err) { error.NeededSourceLocation => unreachable, error.GenericPoison => unreachable, error.ComptimeReturn => unreachable, @@ -4801,7 +4815,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem else => |e| return e, }; if (opt_opv) |opv| { - const arg = try sema.addConstant(param_type, opv); + const arg = try sema.addConstant(param_ty, opv); sema.inst_map.putAssumeCapacityNoClobber(inst, arg); total_param_index += 1; runtime_param_index += 1; @@ -4811,7 +4825,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem inner_block.instructions.appendAssumeCapacity(arg_index); sema.air_instructions.appendAssumeCapacity(.{ .tag = .arg, - .data = .{ .ty = param_type }, + .data = .{ .ty = param_ty }, }); sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index)); total_param_index += 1; diff --git a/src/Sema.zig b/src/Sema.zig index 8142132679..97fe3cb595 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4707,6 +4707,8 @@ const GenericCallAdapter = struct { generic_fn: *Module.Fn, precomputed_hash: u64, func_ty_info: Type.Payload.Function.Data, + /// Unlike comptime_args, the Type here is not always present. + /// .generic_poison is used to communicate non-anytype parameters. comptime_tvs: []const TypedValue, target: std.Target, @@ -4719,20 +4721,29 @@ const GenericCallAdapter = struct { const other_comptime_args = other_key.comptime_args.?; for (other_comptime_args[0..ctx.func_ty_info.param_types.len]) |other_arg, i| { - if (other_arg.ty.tag() != .generic_poison) { - // anytype parameter - if (!other_arg.ty.eql(ctx.comptime_tvs[i].ty, ctx.target)) { + const this_arg = ctx.comptime_tvs[i]; + const this_is_comptime = this_arg.val.tag() != .generic_poison; + const other_is_comptime = other_arg.val.tag() != .generic_poison; + const this_is_anytype = this_arg.ty.tag() != .generic_poison; + const other_is_anytype = other_key.anytype_args[i]; + + if (other_is_anytype != this_is_anytype) return false; + if (other_is_comptime != this_is_comptime) return false; + + if (this_is_anytype) { + // Both are anytype parameters. + if (!this_arg.ty.eql(other_arg.ty, ctx.target)) { return false; } - } - if (other_arg.val.tag() != .generic_poison) { - // comptime parameter - if (ctx.comptime_tvs[i].val.tag() == .generic_poison) { - // No match because the instantiation has a comptime parameter - // but the callsite does not. - return false; + if (this_is_comptime) { + // Both are comptime and anytype parameters with matching types. + if (!this_arg.val.eql(other_arg.val, other_arg.ty, ctx.target)) { + return false; + } } - if (!other_arg.val.eql(ctx.comptime_tvs[i].val, other_arg.ty, ctx.target)) { + } else if (this_is_comptime) { + // Both are comptime parameters but not anytype parameters. + if (!this_arg.val.eql(other_arg.val, other_arg.ty, ctx.target)) { return false; } } @@ -5227,28 +5238,61 @@ fn instantiateGenericCall( const comptime_tvs = try sema.arena.alloc(TypedValue, func_ty_info.param_types.len); const target = sema.mod.getTarget(); - for (func_ty_info.param_types) |param_ty, i| { - const is_comptime = func_ty_info.paramIsComptime(i); - if (is_comptime) { - const arg_src = call_src; // TODO better source location - const casted_arg = try sema.coerce(block, param_ty, uncasted_args[i], arg_src); - if (try sema.resolveMaybeUndefVal(block, arg_src, casted_arg)) |arg_val| { - if (param_ty.tag() != .generic_poison) { - arg_val.hash(param_ty, &hasher, target); + { + var i: usize = 0; + for (fn_info.param_body) |inst| { + var is_comptime = false; + var is_anytype = false; + switch (zir_tags[inst]) { + .param => { + is_comptime = func_ty_info.paramIsComptime(i); + }, + .param_comptime => { + is_comptime = true; + }, + .param_anytype => { + is_anytype = true; + is_comptime = func_ty_info.paramIsComptime(i); + }, + .param_anytype_comptime => { + is_anytype = true; + is_comptime = true; + }, + else => continue, + } + + if (is_comptime) { + const arg_src = call_src; // TODO better source location + const arg_ty = sema.typeOf(uncasted_args[i]); + const arg_val = try sema.resolveValue(block, arg_src, uncasted_args[i]); + arg_val.hash(arg_ty, &hasher, target); + if (is_anytype) { + arg_ty.hashWithHasher(&hasher, target); + comptime_tvs[i] = .{ + .ty = arg_ty, + .val = arg_val, + }; + } else { + comptime_tvs[i] = .{ + .ty = Type.initTag(.generic_poison), + .val = arg_val, + }; } + } else if (is_anytype) { + const arg_ty = sema.typeOf(uncasted_args[i]); + arg_ty.hashWithHasher(&hasher, target); comptime_tvs[i] = .{ - // This will be different than `param_ty` in the case of `generic_poison`. - .ty = sema.typeOf(casted_arg), - .val = arg_val, + .ty = arg_ty, + .val = Value.initTag(.generic_poison), }; } else { - return sema.failWithNeededComptime(block, arg_src); + comptime_tvs[i] = .{ + .ty = Type.initTag(.generic_poison), + .val = Value.initTag(.generic_poison), + }; } - } else { - comptime_tvs[i] = .{ - .ty = sema.typeOf(uncasted_args[i]), - .val = Value.initTag(.generic_poison), - }; + + i += 1; } } @@ -5411,19 +5455,48 @@ fn instantiateGenericCall( errdefer new_func.deinit(gpa); assert(new_func == new_module_func); + const anytype_args = try new_decl_arena_allocator.alloc(bool, func_ty_info.param_types.len); + new_func.anytype_args = anytype_args.ptr; arg_i = 0; for (fn_info.param_body) |inst| { + var is_comptime = false; + var is_anytype = false; switch (zir_tags[inst]) { - .param_comptime, .param_anytype_comptime, .param, .param_anytype => {}, + .param => { + is_comptime = func_ty_info.paramIsComptime(arg_i); + }, + .param_comptime => { + is_comptime = true; + }, + .param_anytype => { + is_anytype = true; + is_comptime = func_ty_info.paramIsComptime(arg_i); + }, + .param_anytype_comptime => { + is_anytype = true; + is_comptime = true; + }, else => continue, } + + // We populate the Type here regardless because it is needed by + // `GenericCallAdapter.eql` as well as function body analysis. + // Whether it is anytype is communicated by `anytype_args`. const arg = child_sema.inst_map.get(inst).?; const copied_arg_ty = try child_sema.typeOf(arg).copy(new_decl_arena_allocator); - if (child_sema.resolveMaybeUndefValAllowVariables( - &child_block, - .unneeded, - arg, - ) catch unreachable) |arg_val| { + anytype_args[arg_i] = is_anytype; + + const arg_src = call_src; // TODO: better source location + if (try sema.typeRequiresComptime(block, arg_src, copied_arg_ty)) { + is_comptime = true; + } + + if (is_comptime) { + const arg_val = (child_sema.resolveMaybeUndefValAllowVariables( + &child_block, + .unneeded, + arg, + ) catch unreachable).?; child_sema.comptime_args[arg_i] = .{ .ty = copied_arg_ty, .val = try arg_val.copy(new_decl_arena_allocator), @@ -5480,22 +5553,7 @@ fn instantiateGenericCall( const comptime_args = callee.comptime_args.?; const new_fn_info = callee.owner_decl.ty.fnInfo(); - const runtime_args_len = count: { - var count: u32 = 0; - var arg_i: usize = 0; - for (fn_info.param_body) |inst| { - switch (zir_tags[inst]) { - .param_comptime, .param_anytype_comptime, .param, .param_anytype => { - if (comptime_args[arg_i].val.tag() == .generic_poison) { - count += 1; - } - arg_i += 1; - }, - else => continue, - } - } - break :count count; - }; + const runtime_args_len = @intCast(u32, new_fn_info.param_types.len); const runtime_args = try sema.arena.alloc(Air.Inst.Ref, runtime_args_len); { var runtime_i: u32 = 0; @@ -5505,7 +5563,9 @@ fn instantiateGenericCall( .param_comptime, .param_anytype_comptime, .param, .param_anytype => {}, else => continue, } - const is_runtime = comptime_args[total_i].val.tag() == .generic_poison; + const is_runtime = comptime_args[total_i].val.tag() == .generic_poison and + comptime_args[total_i].ty.hasRuntimeBits() and + !comptime_args[total_i].ty.comptimeOnly(); if (is_runtime) { const param_ty = new_fn_info.param_types[runtime_i]; const arg_src = call_src; // TODO: better source location @@ -6562,6 +6622,7 @@ fn funcCommon( .zir_body_inst = func_inst, .owner_decl = sema.owner_decl, .comptime_args = comptime_args, + .anytype_args = undefined, .hash = hash, .lbrace_line = src_locs.lbrace_line, .rbrace_line = src_locs.rbrace_line, diff --git a/src/value.zig b/src/value.zig index beb8bc7620..713cacb7b0 100644 --- a/src/value.zig +++ b/src/value.zig @@ -954,6 +954,10 @@ pub const Value = extern union { assert(ty.enumFieldCount() == 1); break :blk 0; }, + .enum_literal => i: { + const name = val.castTag(.enum_literal).?.data; + break :i ty.enumFieldIndex(name).?; + }, // Assume it is already an integer and return it directly. else => return val, }; @@ -2023,6 +2027,11 @@ pub const Value = extern union { /// This function is used by hash maps and so treats floating-point NaNs as equal /// to each other, and not equal to other floating-point values. /// Similarly, it treats `undef` as a distinct value from all other values. + /// This function has to be able to support implicit coercion of `a` to `ty`. That is, + /// `ty` will be an exactly correct Type for `b` but it may be a post-coerced Type + /// for `a`. This function must act *as if* `a` has been coerced to `ty`. This complication + /// is required in order to make generic function instantiation effecient - specifically + /// the insertion into the monomorphized function table. pub fn eql(a: Value, b: Value, ty: Type, target: Target) bool { const a_tag = a.tag(); const b_tag = b.tag(); @@ -2200,8 +2209,18 @@ pub const Value = extern union { } return order(a, b, target).compare(.eq); }, - else => return order(a, b, target).compare(.eq), + .Optional => { + if (a.tag() != .opt_payload and b.tag() == .opt_payload) { + var buffer: Payload.SubValue = .{ + .base = .{ .tag = .opt_payload }, + .data = a, + }; + return eql(Value.initPayload(&buffer.base), b, ty, target); + } + }, + else => {}, } + return order(a, b, target).compare(.eq); } /// This function is used by hash maps and so treats floating-point NaNs as equal diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index 08f1e5bad9..2a18135fe0 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -306,3 +306,21 @@ test "anonymous struct return type referencing comptime parameter" { try expect(s.data == 1234); try expect(s.end == 5678); } + +test "generic function instantiation non-duplicates" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + const S = struct { + fn copy(comptime T: type, dest: []T, source: []const T) void { + @export(foo, .{ .name = "test_generic_instantiation_non_dupe" }); + for (source) |s, i| dest[i] = s; + } + + fn foo() callconv(.C) void {} + }; + var buffer: [100]u8 = undefined; + S.copy(u8, &buffer, "hello"); + S.copy(u8, &buffer, "hello2"); +} From 7a0022725342be8d3ea555a635da51f0e8c47b25 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 14 Apr 2022 15:15:09 +0200 Subject: [PATCH 1111/2031] elf: support --strip option When the user passes `--strip` option on the command line, we do not emit any debug info by skipping initializing the internal `Dwarf` module. --- src/link/Elf.zig | 315 ++++++++++++++++++++++++----------------------- 1 file changed, 160 insertions(+), 155 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 9e1ed0cf54..bb13aad3a6 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -100,11 +100,11 @@ offset_table: std.ArrayListUnmanaged(u64) = .{}, phdr_table_dirty: bool = false, shdr_table_dirty: bool = false, shstrtab_dirty: bool = false, -debug_strtab_dirty: bool = false, offset_table_count_dirty: bool = false, + +debug_strtab_dirty: bool = false, debug_abbrev_section_dirty: bool = false, debug_aranges_section_dirty: bool = false, - debug_info_header_dirty: bool = false, debug_line_header_dirty: bool = false, @@ -749,127 +749,129 @@ pub fn populateMissingMetadata(self: *Elf) !void { try self.writeSymbol(0); } - if (self.debug_str_section_index == null) { - self.debug_str_section_index = @intCast(u16, self.sections.items.len); - assert(self.dwarf.?.strtab.items.len == 0); - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".debug_str"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS, - .sh_addr = 0, - .sh_offset = 0, - .sh_size = 0, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = 1, - .sh_entsize = 1, - }); - self.debug_strtab_dirty = true; - self.shdr_table_dirty = true; - } + if (self.dwarf) |dw| { + if (self.debug_str_section_index == null) { + self.debug_str_section_index = @intCast(u16, self.sections.items.len); + assert(dw.strtab.items.len == 0); + try self.sections.append(self.base.allocator, .{ + .sh_name = try self.makeString(".debug_str"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS, + .sh_addr = 0, + .sh_offset = 0, + .sh_size = 0, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = 1, + .sh_entsize = 1, + }); + self.debug_strtab_dirty = true; + self.shdr_table_dirty = true; + } - if (self.debug_info_section_index == null) { - self.debug_info_section_index = @intCast(u16, self.sections.items.len); + if (self.debug_info_section_index == null) { + self.debug_info_section_index = @intCast(u16, self.sections.items.len); - const file_size_hint = 200; - const p_align = 1; - const off = self.findFreeSpace(file_size_hint, p_align); - log.debug("found .debug_info free space 0x{x} to 0x{x}", .{ - off, - off + file_size_hint, - }); - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".debug_info"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = file_size_hint, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = p_align, - .sh_entsize = 0, - }); - self.shdr_table_dirty = true; - self.debug_info_header_dirty = true; - } + const file_size_hint = 200; + const p_align = 1; + const off = self.findFreeSpace(file_size_hint, p_align); + log.debug("found .debug_info free space 0x{x} to 0x{x}", .{ + off, + off + file_size_hint, + }); + try self.sections.append(self.base.allocator, .{ + .sh_name = try self.makeString(".debug_info"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = off, + .sh_size = file_size_hint, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = p_align, + .sh_entsize = 0, + }); + self.shdr_table_dirty = true; + self.debug_info_header_dirty = true; + } - if (self.debug_abbrev_section_index == null) { - self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len); + if (self.debug_abbrev_section_index == null) { + self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len); - const file_size_hint = 128; - const p_align = 1; - const off = self.findFreeSpace(file_size_hint, p_align); - log.debug("found .debug_abbrev free space 0x{x} to 0x{x}", .{ - off, - off + file_size_hint, - }); - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".debug_abbrev"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = file_size_hint, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = p_align, - .sh_entsize = 0, - }); - self.shdr_table_dirty = true; - self.debug_abbrev_section_dirty = true; - } + const file_size_hint = 128; + const p_align = 1; + const off = self.findFreeSpace(file_size_hint, p_align); + log.debug("found .debug_abbrev free space 0x{x} to 0x{x}", .{ + off, + off + file_size_hint, + }); + try self.sections.append(self.base.allocator, .{ + .sh_name = try self.makeString(".debug_abbrev"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = off, + .sh_size = file_size_hint, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = p_align, + .sh_entsize = 0, + }); + self.shdr_table_dirty = true; + self.debug_abbrev_section_dirty = true; + } - if (self.debug_aranges_section_index == null) { - self.debug_aranges_section_index = @intCast(u16, self.sections.items.len); + if (self.debug_aranges_section_index == null) { + self.debug_aranges_section_index = @intCast(u16, self.sections.items.len); - const file_size_hint = 160; - const p_align = 16; - const off = self.findFreeSpace(file_size_hint, p_align); - log.debug("found .debug_aranges free space 0x{x} to 0x{x}", .{ - off, - off + file_size_hint, - }); - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".debug_aranges"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = file_size_hint, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = p_align, - .sh_entsize = 0, - }); - self.shdr_table_dirty = true; - self.debug_aranges_section_dirty = true; - } + const file_size_hint = 160; + const p_align = 16; + const off = self.findFreeSpace(file_size_hint, p_align); + log.debug("found .debug_aranges free space 0x{x} to 0x{x}", .{ + off, + off + file_size_hint, + }); + try self.sections.append(self.base.allocator, .{ + .sh_name = try self.makeString(".debug_aranges"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = off, + .sh_size = file_size_hint, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = p_align, + .sh_entsize = 0, + }); + self.shdr_table_dirty = true; + self.debug_aranges_section_dirty = true; + } - if (self.debug_line_section_index == null) { - self.debug_line_section_index = @intCast(u16, self.sections.items.len); + if (self.debug_line_section_index == null) { + self.debug_line_section_index = @intCast(u16, self.sections.items.len); - const file_size_hint = 250; - const p_align = 1; - const off = self.findFreeSpace(file_size_hint, p_align); - log.debug("found .debug_line free space 0x{x} to 0x{x}", .{ - off, - off + file_size_hint, - }); - try self.sections.append(self.base.allocator, .{ - .sh_name = try self.makeString(".debug_line"), - .sh_type = elf.SHT_PROGBITS, - .sh_flags = 0, - .sh_addr = 0, - .sh_offset = off, - .sh_size = file_size_hint, - .sh_link = 0, - .sh_info = 0, - .sh_addralign = p_align, - .sh_entsize = 0, - }); - self.shdr_table_dirty = true; - self.debug_line_header_dirty = true; + const file_size_hint = 250; + const p_align = 1; + const off = self.findFreeSpace(file_size_hint, p_align); + log.debug("found .debug_line free space 0x{x} to 0x{x}", .{ + off, + off + file_size_hint, + }); + try self.sections.append(self.base.allocator, .{ + .sh_name = try self.makeString(".debug_line"), + .sh_type = elf.SHT_PROGBITS, + .sh_flags = 0, + .sh_addr = 0, + .sh_offset = off, + .sh_size = file_size_hint, + .sh_link = 0, + .sh_info = 0, + .sh_addralign = p_align, + .sh_entsize = 0, + }); + self.shdr_table_dirty = true; + self.debug_line_header_dirty = true; + } } const shsize: u64 = switch (self.ptr_width) { @@ -1001,40 +1003,42 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { // mixing local and global symbols within a symbol table. try self.writeAllGlobalSymbols(); - if (self.debug_abbrev_section_dirty) { - try self.dwarf.?.writeDbgAbbrev(&self.base); - if (!self.shdr_table_dirty) { - // Then it won't get written with the others and we need to do it. - try self.writeSectHeader(self.debug_abbrev_section_index.?); + if (self.dwarf) |*dw| { + if (self.debug_abbrev_section_dirty) { + try dw.writeDbgAbbrev(&self.base); + if (!self.shdr_table_dirty) { + // Then it won't get written with the others and we need to do it. + try self.writeSectHeader(self.debug_abbrev_section_index.?); + } + self.debug_abbrev_section_dirty = false; } - self.debug_abbrev_section_dirty = false; - } - if (self.debug_info_header_dirty) { - // Currently only one compilation unit is supported, so the address range is simply - // identical to the main program header virtual address and memory size. - const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; - const low_pc = text_phdr.p_vaddr; - const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; - try self.dwarf.?.writeDbgInfoHeader(&self.base, module, low_pc, high_pc); - self.debug_info_header_dirty = false; - } - - if (self.debug_aranges_section_dirty) { - // Currently only one compilation unit is supported, so the address range is simply - // identical to the main program header virtual address and memory size. - const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; - try self.dwarf.?.writeDbgAranges(&self.base, text_phdr.p_vaddr, text_phdr.p_memsz); - if (!self.shdr_table_dirty) { - // Then it won't get written with the others and we need to do it. - try self.writeSectHeader(self.debug_aranges_section_index.?); + if (self.debug_info_header_dirty) { + // Currently only one compilation unit is supported, so the address range is simply + // identical to the main program header virtual address and memory size. + const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; + const low_pc = text_phdr.p_vaddr; + const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz; + try dw.writeDbgInfoHeader(&self.base, module, low_pc, high_pc); + self.debug_info_header_dirty = false; } - self.debug_aranges_section_dirty = false; - } - if (self.debug_line_header_dirty) { - try self.dwarf.?.writeDbgLineHeader(&self.base, module); - self.debug_line_header_dirty = false; + if (self.debug_aranges_section_dirty) { + // Currently only one compilation unit is supported, so the address range is simply + // identical to the main program header virtual address and memory size. + const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?]; + try dw.writeDbgAranges(&self.base, text_phdr.p_vaddr, text_phdr.p_memsz); + if (!self.shdr_table_dirty) { + // Then it won't get written with the others and we need to do it. + try self.writeSectHeader(self.debug_aranges_section_index.?); + } + self.debug_aranges_section_dirty = false; + } + + if (self.debug_line_header_dirty) { + try dw.writeDbgLineHeader(&self.base, module); + self.debug_line_header_dirty = false; + } } if (self.phdr_table_dirty) { @@ -1105,9 +1109,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { } } - { + if (self.dwarf) |dwarf| { const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?]; - const dwarf = self.dwarf.?; if (self.debug_strtab_dirty or dwarf.strtab.items.len != debug_strtab_sect.sh_size) { const allocated_size = self.allocatedSize(debug_strtab_sect.sh_offset); const needed_size = dwarf.strtab.items.len; @@ -2105,14 +2108,16 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al phdr.p_memsz = needed_size; phdr.p_filesz = needed_size; - // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address - // range of the compilation unit. When we expand the text section, this range changes, - // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty. - self.debug_info_header_dirty = true; - // This becomes dirty for the same reason. We could potentially make this more - // fine-grained with the addition of support for more compilation units. It is planned to - // model each package as a different compilation unit. - self.debug_aranges_section_dirty = true; + if (self.dwarf) |_| { + // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address + // range of the compilation unit. When we expand the text section, this range changes, + // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty. + self.debug_info_header_dirty = true; + // This becomes dirty for the same reason. We could potentially make this more + // fine-grained with the addition of support for more compilation units. It is planned to + // model each package as a different compilation unit. + self.debug_aranges_section_dirty = true; + } self.phdr_table_dirty = true; // TODO look into making only the one program header dirty self.shdr_table_dirty = true; // TODO look into making only the one section dirty From 4d77ef25f948cac32e4d5fd3f9df019e728fcb45 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 14 Apr 2022 15:16:51 +0200 Subject: [PATCH 1112/2031] elf: check symtab section headers size when writing globals Without this, it may happen we write the globals without extending the symtab section header's size. This can potentially lead to clobbering some data in the file, or simply omitting the globals from the symtab when displaying with support tooling such as `readelf`. --- src/link/Elf.zig | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index bb13aad3a6..add23113a7 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2799,6 +2799,22 @@ fn writeAllGlobalSymbols(self: *Elf) !void { .p32 => @sizeOf(elf.Elf32_Sym), .p64 => @sizeOf(elf.Elf64_Sym), }; + const sym_align: u16 = switch (self.ptr_width) { + .p32 => @alignOf(elf.Elf32_Sym), + .p64 => @alignOf(elf.Elf64_Sym), + }; + const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size; + if (needed_size > self.allocatedSize(syms_sect.sh_offset)) { + // Move all the symbols to a new file location. + const new_offset = self.findFreeSpace(needed_size, sym_align); + const existing_size = @as(u64, syms_sect.sh_info) * sym_size; + const amt = try self.base.file.?.copyRangeAll(syms_sect.sh_offset, self.base.file.?, new_offset, existing_size); + if (amt != existing_size) return error.InputOutput; + syms_sect.sh_offset = new_offset; + } + syms_sect.sh_size = needed_size; // anticipating adding the global symbols later + self.shdr_table_dirty = true; // TODO look into only writing one section + const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size; switch (self.ptr_width) { From 07397707392d27fbee5f1bf0a788937b66300bf0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Apr 2022 06:37:11 -0700 Subject: [PATCH 1113/2031] Sema: workaround for generic instantiation recurison bug --- src/type.zig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/type.zig b/src/type.zig index bcb6e63f6e..da0149967b 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1520,6 +1520,13 @@ pub const Type = extern union { ) @TypeOf(writer).Error!void { _ = options; comptime assert(unused_format_string.len == 0); + if (@import("builtin").zig_backend != .stage1) { + // This is disabled to work around a stage2 bug where this function recursively + // causes more generic function instantiations resulting in an infinite loop + // in the compiler. + try writer.writeAll("[TODO fix internal compiler bug regarding dump]"); + return; + } var ty = start_type; while (true) { const t = ty.tag(); From a30688ef2a136c5a127c706880e8389b9b32e5be Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 27 Mar 2022 08:20:15 +0700 Subject: [PATCH 1114/2031] stage2: sparcv9: Add some initial checks in codegen --- src/arch/sparcv9/CodeGen.zig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index db5811dfda..5d9be85d13 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -1,6 +1,7 @@ //! SPARCv9 codegen. //! This lowers AIR into MIR. const std = @import("std"); +const assert = std.debug.assert; const builtin = @import("builtin"); const link = @import("../../link.zig"); const Module = @import("../../Module.zig"); @@ -8,6 +9,7 @@ const Air = @import("../../Air.zig"); const Mir = @import("Mir.zig"); const Emit = @import("Emit.zig"); const Liveness = @import("../../Liveness.zig"); +const build_options = @import("build_options"); const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; const FnResult = @import("../../codegen.zig").FnResult; @@ -35,5 +37,11 @@ pub fn generate( _ = code; _ = debug_output; + if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { + @panic("Attempted to compile for architecture that was disabled by build configuration"); + } + + assert(module_fn.owner_decl.has_tv); + @panic("TODO implement SPARCv9 codegen"); } From a5a89fde1354892c6714c41ea691922bfa10c442 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 28 Mar 2022 20:31:40 +0700 Subject: [PATCH 1115/2031] stage2: sparcv9: Add skeleton codegen impl and necessary fields --- src/arch/sparcv9/CodeGen.zig | 287 +++++++++++++++++++++++++++++++++-- src/arch/sparcv9/Emit.zig | 37 +++++ src/arch/sparcv9/Mir.zig | 49 ++++++ 3 files changed, 362 insertions(+), 11 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 5d9be85d13..232b83b741 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -2,24 +2,198 @@ //! This lowers AIR into MIR. const std = @import("std"); const assert = std.debug.assert; +const mem = std.mem; +const Allocator = mem.Allocator; const builtin = @import("builtin"); const link = @import("../../link.zig"); const Module = @import("../../Module.zig"); +const ErrorMsg = Module.ErrorMsg; const Air = @import("../../Air.zig"); const Mir = @import("Mir.zig"); const Emit = @import("Emit.zig"); const Liveness = @import("../../Liveness.zig"); -const build_options = @import("build_options"); - +const Type = @import("../../type.zig").Type; const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; const FnResult = @import("../../codegen.zig").FnResult; const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; +const build_options = @import("build_options"); + const bits = @import("bits.zig"); const abi = @import("abi.zig"); +const Register = bits.Register; const Self = @This(); +const InnerError = error{ + OutOfMemory, + CodegenFail, + OutOfRegisters, +}; + +gpa: Allocator, +air: Air, +liveness: Liveness, +bin_file: *link.File, +target: *const std.Target, +mod_fn: *const Module.Fn, +code: *std.ArrayList(u8), +debug_output: DebugInfoOutput, +err_msg: ?*ErrorMsg, +args: []MCValue, +ret_mcv: MCValue, +fn_type: Type, +arg_index: usize, +src_loc: Module.SrcLoc, +stack_align: u32, + +/// MIR Instructions +mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, +/// MIR extra data +mir_extra: std.ArrayListUnmanaged(u32) = .{}, + +/// Byte offset within the source file of the ending curly. +end_di_line: u32, +end_di_column: u32, + +/// The value is an offset into the `Function` `code` from the beginning. +/// To perform the reloc, write 32-bit signed little-endian integer +/// which is a relative jump, based on the address following the reloc. +exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{}, + +/// Whenever there is a runtime branch, we push a Branch onto this stack, +/// and pop it off when the runtime branch joins. This provides an "overlay" +/// of the table of mappings from instructions to `MCValue` from within the branch. +/// This way we can modify the `MCValue` for an instruction in different ways +/// within different branches. Special consideration is needed when a branch +/// joins with its parent, to make sure all instructions have the same MCValue +/// across each runtime branch upon joining. +branch_stack: *std.ArrayList(Branch), + +// Key is the block instruction +blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, + +/// Maps offset to what is stored there. +stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, + +/// Offset from the stack base, representing the end of the stack frame. +max_end_stack: u32 = 0, +/// Represents the current end stack offset. If there is no existing slot +/// to place a new stack allocation, it goes here, and then bumps `max_end_stack`. +next_stack_offset: u32 = 0, + +/// Debug field, used to find bugs in the compiler. +air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, + +const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; + +const MCValue = union(enum) { + /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. + /// TODO Look into deleting this tag and using `dead` instead, since every use + /// of MCValue.none should be instead looking at the type and noticing it is 0 bits. + none, + /// Control flow will not allow this value to be observed. + unreach, + /// No more references to this value remain. + dead, + /// The value is undefined. + undef, + /// A pointer-sized integer that fits in a register. + /// If the type is a pointer, this is the pointer address in virtual address space. + immediate: u64, + /// The value is in a target-specific register. + register: Register, + /// The value is in memory at a hard-coded address. + /// If the type is a pointer, it means the pointer address is at this memory location. + memory: u64, + /// The value is one of the stack variables. + /// If the type is a pointer, it means the pointer address is in the stack at this offset. + stack_offset: u32, + /// The value is a pointer to one of the stack variables (payload is stack offset). + ptr_stack_offset: u32, + + fn isMemory(mcv: MCValue) bool { + return switch (mcv) { + .memory, .stack_offset => true, + else => false, + }; + } + + fn isImmediate(mcv: MCValue) bool { + return switch (mcv) { + .immediate => true, + else => false, + }; + } + + fn isMutable(mcv: MCValue) bool { + return switch (mcv) { + .none => unreachable, + .unreach => unreachable, + .dead => unreachable, + + .immediate, + .memory, + .ptr_stack_offset, + .undef, + => false, + + .register, + .stack_offset, + => true, + }; + } +}; + +const Branch = struct { + inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{}, + + fn deinit(self: *Branch, gpa: Allocator) void { + self.inst_table.deinit(gpa); + self.* = undefined; + } +}; + +const StackAllocation = struct { + inst: Air.Inst.Index, + /// TODO do we need size? should be determined by inst.ty.abiSize() + size: u32, +}; + +const BlockData = struct { + relocs: std.ArrayListUnmanaged(Reloc), + /// The first break instruction encounters `null` here and chooses a + /// machine code value for the block result, populating this field. + /// Following break instructions encounter that value and use it for + /// the location to store their block results. + mcv: MCValue, +}; + +const Reloc = union(enum) { + /// The value is an offset into the `Function` `code` from the beginning. + /// To perform the reloc, write 32-bit signed little-endian integer + /// which is a relative jump, based on the address following the reloc. + rel32: usize, + /// A branch in the ARM instruction set + arm_branch: struct { + pos: usize, + cond: @import("../arm/bits.zig").Condition, + }, +}; + +const CallMCValues = struct { + args: []MCValue, + return_value: MCValue, + stack_byte_count: u32, + stack_align: u32, + + fn deinit(self: *CallMCValues, func: *Self) void { + func.gpa.free(self.args); + self.* = undefined; + } +}; + + pub fn generate( bin_file: *link.File, src_loc: Module.SrcLoc, @@ -29,19 +203,110 @@ pub fn generate( code: *std.ArrayList(u8), debug_output: DebugInfoOutput, ) GenerateSymbolError!FnResult { - _ = bin_file; - _ = src_loc; - _ = module_fn; - _ = air; - _ = liveness; - _ = code; - _ = debug_output; - if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { @panic("Attempted to compile for architecture that was disabled by build configuration"); } assert(module_fn.owner_decl.has_tv); + const fn_type = module_fn.owner_decl.ty; - @panic("TODO implement SPARCv9 codegen"); + var branch_stack = std.ArrayList(Branch).init(bin_file.allocator); + defer { + assert(branch_stack.items.len == 1); + branch_stack.items[0].deinit(bin_file.allocator); + branch_stack.deinit(); + } + try branch_stack.append(.{}); + + var function = Self{ + .gpa = bin_file.allocator, + .air = air, + .liveness = liveness, + .target = &bin_file.options.target, + .bin_file = bin_file, + .mod_fn = module_fn, + .code = code, + .debug_output = debug_output, + .err_msg = null, + .args = undefined, // populated after `resolveCallingConventionValues` + .ret_mcv = undefined, // populated after `resolveCallingConventionValues` + .fn_type = fn_type, + .arg_index = 0, + .branch_stack = &branch_stack, + .src_loc = src_loc, + .stack_align = undefined, + .end_di_line = module_fn.rbrace_line, + .end_di_column = module_fn.rbrace_column, + }; + defer function.stack.deinit(bin_file.allocator); + defer function.blocks.deinit(bin_file.allocator); + defer function.exitlude_jump_relocs.deinit(bin_file.allocator); + + var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { + error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, + error.OutOfRegisters => return FnResult{ + .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + }, + else => |e| return e, + }; + defer call_info.deinit(&function); + + function.args = call_info.args; + function.ret_mcv = call_info.return_value; + function.stack_align = call_info.stack_align; + function.max_end_stack = call_info.stack_byte_count; + + function.gen() catch |err| switch (err) { + error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, + error.OutOfRegisters => return FnResult{ + .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + }, + else => |e| return e, + }; + + var mir = Mir{ + .instructions = function.mir_instructions.toOwnedSlice(), + .extra = function.mir_extra.toOwnedSlice(bin_file.allocator), + }; + defer mir.deinit(bin_file.allocator); + + var emit = Emit{ + .mir = mir, + .bin_file = bin_file, + .debug_output = debug_output, + .target = &bin_file.options.target, + .src_loc = src_loc, + .code = code, + .prev_di_pc = 0, + .prev_di_line = module_fn.lbrace_line, + .prev_di_column = module_fn.lbrace_column, + }; + defer emit.deinit(); + + emit.emitMir() catch |err| switch (err) { + error.EmitFail => return FnResult{ .fail = emit.err_msg.? }, + else => |e| return e, + }; + + if (function.err_msg) |em| { + return FnResult{ .fail = em }; + } else { + return FnResult{ .appended = {} }; + } +} + +/// Caller must call `CallMCValues.deinit`. +fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { + _ = self; + _ = fn_ty; + + @panic("TODO implement resolveCallingConventionValues"); +} + + +/// Caller must call `CallMCValues.deinit`. +fn gen(self: *Self) !void { + _ = self; + + @panic("TODO implement gen"); } diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index ba644ede7e..1821570701 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -1,6 +1,43 @@ //! This file contains the functionality for lowering SPARCv9 MIR into //! machine code +const std = @import("std"); +const link = @import("../../link.zig"); +const Module = @import("../../Module.zig"); +const ErrorMsg = Module.ErrorMsg; +const Liveness = @import("../../Liveness.zig"); +const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; + const Emit = @This(); const Mir = @import("Mir.zig"); const bits = @import("bits.zig"); + +mir: Mir, +bin_file: *link.File, +debug_output: DebugInfoOutput, +target: *const std.Target, +err_msg: ?*ErrorMsg = null, +src_loc: Module.SrcLoc, +code: *std.ArrayList(u8), + +prev_di_line: u32, +prev_di_column: u32, +/// Relative to the beginning of `code`. +prev_di_pc: usize, + +const InnerError = error{ + OutOfMemory, + EmitFail, +}; + +pub fn emitMir( + emit: *Emit, +) InnerError!void { + _ = emit; + + @panic("TODO implement emitMir"); +} + +pub fn deinit(emit: *Emit) void { + emit.* = undefined; +} diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index f0d3b1dfbd..0f80a60ecf 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -6,6 +6,55 @@ //! The main purpose of MIR is to postpone the assignment of offsets until Isel, //! so that, for example, the smaller encodings of jump instructions can be used. +const std = @import("std"); + const Mir = @This(); const bits = @import("bits.zig"); const Register = bits.Register; + +instructions: std.MultiArrayList(Inst).Slice, + +/// The meaning of this data is determined by `Inst.Tag` value. +extra: []const u32, + +pub const Inst = struct { + tag: Tag, + /// The meaning of this depends on `tag`. + data: Data, + + pub const Tag = enum(u16) { + /// Pseudo-instruction: End of prologue + dbg_prologue_end, + /// Pseudo-instruction: Beginning of epilogue + dbg_epilogue_begin, + /// Pseudo-instruction: Update debug line + dbg_line, + }; + + /// The position of an MIR instruction within the `Mir` instructions array. + pub const Index = u32; + + /// All instructions have a 4-byte payload, which is contained within + /// this union. `Tag` determines which union field is active, as well as + /// how to interpret the data within. + pub const Data = union { + /// No additional data + /// + /// Used by e.g. flushw + nop: void, + /// Debug info: line and column + /// + /// Used by e.g. dbg_line + dbg_line_column: struct { + line: u32, + column: u32, + }, + }; +}; + +pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { + mir.instructions.deinit(gpa); + gpa.free(mir.extra); + mir.* = undefined; +} + From 1ba52272167b12ea14f688df7d0d34940d98ff5b Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 28 Mar 2022 22:37:55 +0700 Subject: [PATCH 1116/2031] stage2: sparcv9: Initial resolveCallingConventionValues implementation --- src/arch/sparcv9/CodeGen.zig | 97 +++++++++++++++++++++++++++++++++--- src/arch/sparcv9/abi.zig | 4 +- 2 files changed, 92 insertions(+), 9 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 232b83b741..0c63d992ee 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -193,7 +193,6 @@ const CallMCValues = struct { } }; - pub fn generate( bin_file: *link.File, src_loc: Module.SrcLoc, @@ -242,7 +241,7 @@ pub fn generate( defer function.blocks.deinit(bin_file.allocator); defer function.exitlude_jump_relocs.deinit(bin_file.allocator); - var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { + var call_info = function.resolveCallingConventionValues(fn_type, false) catch |err| switch (err) { error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, error.OutOfRegisters => return FnResult{ .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), @@ -296,17 +295,101 @@ pub fn generate( } /// Caller must call `CallMCValues.deinit`. -fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { - _ = self; - _ = fn_ty; +fn resolveCallingConventionValues(self: *Self, fn_ty: Type, is_caller: bool) !CallMCValues { + const cc = fn_ty.fnCallingConvention(); + const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen()); + defer self.gpa.free(param_types); + fn_ty.fnParamTypes(param_types); + var result: CallMCValues = .{ + .args = try self.gpa.alloc(MCValue, param_types.len), + // These undefined values must be populated before returning from this function. + .return_value = undefined, + .stack_byte_count = undefined, + .stack_align = undefined, + }; + errdefer self.gpa.free(result.args); - @panic("TODO implement resolveCallingConventionValues"); + const ret_ty = fn_ty.fnReturnType(); + + switch (cc) { + .Naked => { + assert(result.args.len == 0); + result.return_value = .{ .unreach = {} }; + result.stack_byte_count = 0; + result.stack_align = 1; + return result; + }, + .Unspecified, .C => { + // SPARC Compliance Definition 2.4.1, Chapter 3 + // Low-Level System Information (64-bit psABI) - Function Calling Sequence + + var next_register: usize = 0; + var next_stack_offset: u32 = 0; + + // The caller puts the argument in %o0-%o5, which becomes %i0-%i5 inside the callee. + const argument_registers = if (is_caller) abi.c_abi_int_param_regs_caller_view else abi.c_abi_int_param_regs_callee_view; + + for (param_types) |ty, i| { + const param_size = @intCast(u32, ty.abiSize(self.target.*)); + if (param_size <= 8) { + if (next_register < argument_registers.len) { + result.args[i] = .{ .register = argument_registers[next_register] }; + next_register += 1; + } else { + result.args[i] = .{ .stack_offset = next_stack_offset }; + next_register += next_stack_offset; + } + } else if (param_size <= 16) { + if (next_register < argument_registers.len - 1) { + return self.fail("TODO MCValues with 2 registers", .{}); + } else if (next_register < argument_registers.len) { + return self.fail("TODO MCValues split register + stack", .{}); + } else { + result.args[i] = .{ .stack_offset = next_stack_offset }; + next_register += next_stack_offset; + } + } else { + result.args[i] = .{ .stack_offset = next_stack_offset }; + next_register += next_stack_offset; + } + } + + result.stack_byte_count = next_stack_offset; + result.stack_align = 16; + }, + else => return self.fail("TODO implement function parameters for {} on sparcv9", .{cc}), + } + + if (ret_ty.zigTypeTag() == .NoReturn) { + result.return_value = .{ .unreach = {} }; + } else if (!ret_ty.hasRuntimeBits()) { + result.return_value = .{ .none = {} }; + } else switch (cc) { + .Naked => unreachable, + .Unspecified, .C => { + const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); + // The callee puts the return values in %i0-%i3, which becomes %o0-%o3 inside the caller. + if (ret_ty_size <= 8) { + result.return_value = if (is_caller) .{ .register = abi.c_abi_int_return_regs_caller_view[0] } else .{ .register = abi.c_abi_int_return_regs_callee_view[0] }; + } else { + return self.fail("TODO support more return values for sparcv9", .{}); + } + }, + else => return self.fail("TODO implement function return values for {} on sparcv9", .{cc}), + } + return result; } - /// Caller must call `CallMCValues.deinit`. fn gen(self: *Self) !void { _ = self; @panic("TODO implement gen"); } + +fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { + @setCold(true); + assert(self.err_msg == null); + self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); + return error.CodegenFail; +} diff --git a/src/arch/sparcv9/abi.zig b/src/arch/sparcv9/abi.zig index 5c9ea979fc..ceac33c08c 100644 --- a/src/arch/sparcv9/abi.zig +++ b/src/arch/sparcv9/abi.zig @@ -8,5 +8,5 @@ pub const callee_preserved_regs = [_]Register{}; pub const c_abi_int_param_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 }; pub const c_abi_int_param_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5" }; -pub const c_abi_int_return_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 }; -pub const c_abi_int_return_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5" }; +pub const c_abi_int_return_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3 }; +pub const c_abi_int_return_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3" }; From cf13356dabd79219499bddb63551f61a67a510bb Mon Sep 17 00:00:00 2001 From: Koakuma Date: Tue, 29 Mar 2022 20:37:32 +0700 Subject: [PATCH 1117/2031] stage2: sparcv9: Mir extraData implementation --- src/arch/riscv64/Mir.zig | 1 + src/arch/sparcv9/Mir.zig | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index 5df3a86229..7b5049b7d4 100644 --- a/src/arch/riscv64/Mir.zig +++ b/src/arch/riscv64/Mir.zig @@ -144,3 +144,4 @@ pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end .end = i, }; } + diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index 0f80a60ecf..c8b9c6544f 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -58,3 +58,23 @@ pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { mir.* = undefined; } +/// Returns the requested data, as well as the new index which is at the start of the +/// trailers for the object. +pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } { + const fields = std.meta.fields(T); + var i: usize = index; + var result: T = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.field_type) { + u32 => mir.extra[i], + i32 => @bitCast(i32, mir.extra[i]), + else => @compileError("bad field type"), + }; + i += 1; + } + return .{ + .data = result, + .end = i, + }; +} + From 927706e6d0cf289be979bb6f12be6636c0e07f59 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Tue, 29 Mar 2022 20:54:23 +0700 Subject: [PATCH 1118/2031] stage2: sparcv9: Emit debug inst placeholder --- src/arch/sparcv9/Emit.zig | 54 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index 1821570701..2587b94c7c 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -7,6 +7,7 @@ const Module = @import("../../Module.zig"); const ErrorMsg = Module.ErrorMsg; const Liveness = @import("../../Liveness.zig"); const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; +const DW = std.dwarf; const Emit = @This(); const Mir = @import("Mir.zig"); @@ -33,11 +34,60 @@ const InnerError = error{ pub fn emitMir( emit: *Emit, ) InnerError!void { - _ = emit; + const mir_tags = emit.mir.instructions.items(.tag); - @panic("TODO implement emitMir"); + // Emit machine code + for (mir_tags) |tag, index| { + const inst = @intCast(u32, index); + switch (tag) { + .dbg_line => try emit.mirDbgLine(inst), + + .dbg_prologue_end => try emit.mirDebugPrologueEnd(), + .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(), + } + } } pub fn deinit(emit: *Emit) void { emit.* = undefined; } + +fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { + _ = self; + _ = line; + _ = column; + + @panic("TODO implement dbgAdvancePCAndLine"); +} + +fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column; + + switch (tag) { + .dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column), + else => unreachable, + } +} + +fn mirDebugPrologueEnd(self: *Emit) !void { + switch (self.debug_output) { + .dwarf => |dbg_out| { + try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); + try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); + }, + .plan9 => {}, + .none => {}, + } +} + +fn mirDebugEpilogueBegin(self: *Emit) !void { + switch (self.debug_output) { + .dwarf => |dbg_out| { + try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); + try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); + }, + .plan9 => {}, + .none => {}, + } +} From 94a84e783e32169268809e4aa4bdbfdff92d81e9 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 1 Apr 2022 19:28:24 +0700 Subject: [PATCH 1119/2031] stage2: sparcv9: Implement basic prologue/epilogue Mir emission --- src/arch/sparcv9/CodeGen.zig | 104 ++++++++++++++++++++++++++++++++++- src/arch/sparcv9/Emit.zig | 16 +++++- src/arch/sparcv9/Mir.zig | 47 +++++++++++++++- 3 files changed, 163 insertions(+), 4 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 0c63d992ee..c2aa082b11 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -382,9 +382,109 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type, is_caller: bool) !Ca /// Caller must call `CallMCValues.deinit`. fn gen(self: *Self) !void { - _ = self; + const cc = self.fn_type.fnCallingConvention(); + if (cc != .Naked) { + // TODO Finish function prologue and epilogue for sparcv9. - @panic("TODO implement gen"); + // TODO Backpatch stack offset + // save %sp, -176, %sp + _ = try self.addInst(.{ + .tag = .save, + .data = .{ + .arithmetic_3op = .{ + .is_imm = true, + .rd = .sp, + .rs1 = .sp, + .rs2_or_imm = .{ .imm = -176 }, + }, + }, + }); + + _ = try self.addInst(.{ + .tag = .dbg_prologue_end, + .data = .{ .nop = {} }, + }); + + try self.genBody(self.air.getMainBody()); + + _ = try self.addInst(.{ + .tag = .dbg_epilogue_begin, + .data = .{ .nop = {} }, + }); + + // exitlude jumps + if (self.exitlude_jump_relocs.items.len > 0 and + self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2) + { + // If the last Mir instruction (apart from the + // dbg_epilogue_begin) is the last exitlude jump + // relocation (which would just jump one instruction + // further), it can be safely removed + self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop()); + } + + for (self.exitlude_jump_relocs.items) |jmp_reloc| { + _ = jmp_reloc; + return self.fail("TODO add branches in sparcv9", .{}); + } + + // return %i7 + 8 + _ = try self.addInst(.{ + .tag = .@"return", + .data = .{ + .arithmetic_2op = .{ + .is_imm = true, + .rs1 = .@"i7", + .rs2_or_imm = .{ .imm = 8 }, + }, + }, + }); + + // TODO Find a way to fill this slot + // nop + _ = try self.addInst(.{ + .tag = .nop, + .data = .{ .nop = {} }, + }); + } else { + _ = try self.addInst(.{ + .tag = .dbg_prologue_end, + .data = .{ .nop = {} }, + }); + + try self.genBody(self.air.getMainBody()); + + _ = try self.addInst(.{ + .tag = .dbg_epilogue_begin, + .data = .{ .nop = {} }, + }); + } + + // Drop them off at the rbrace. + _ = try self.addInst(.{ + .tag = .dbg_line, + .data = .{ .dbg_line_column = .{ + .line = self.end_di_line, + .column = self.end_di_column, + } }, + }); +} + +fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { + _ = self; + _ = body; + + @panic("TODO implement genBody"); +} + +fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { + const gpa = self.gpa; + + try self.mir_instructions.ensureUnusedCapacity(gpa, 1); + + const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len); + self.mir_instructions.appendAssumeCapacity(inst); + return result_index; } fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index 2587b94c7c..28c172f329 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -2,6 +2,7 @@ //! machine code const std = @import("std"); +const assert = std.debug.assert; const link = @import("../../link.zig"); const Module = @import("../../Module.zig"); const ErrorMsg = Module.ErrorMsg; @@ -41,9 +42,15 @@ pub fn emitMir( const inst = @intCast(u32, index); switch (tag) { .dbg_line => try emit.mirDbgLine(inst), - .dbg_prologue_end => try emit.mirDebugPrologueEnd(), .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(), + + .nop => @panic("TODO implement nop"), + + .save => @panic("TODO implement save"), + .restore => @panic("TODO implement restore"), + + .@"return" => @panic("TODO implement return"), } } } @@ -91,3 +98,10 @@ fn mirDebugEpilogueBegin(self: *Emit) !void { .none => {}, } } + +fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { + @setCold(true); + assert(emit.err_msg == null); + emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args); + return error.EmitFail; +} diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index c8b9c6544f..43b008d189 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -29,6 +29,22 @@ pub const Inst = struct { dbg_epilogue_begin, /// Pseudo-instruction: Update debug line dbg_line, + + // All the real instructions are ordered by their section number + // in The SPARC Architecture Manual, Version 9. + + /// A.40 No Operation + /// It uses the nop field. + nop, + + /// A.46 SAVE and RESTORE + /// Those uses the arithmetic_3op field. + save, + restore, + + /// A.45 RETURN + /// It uses the arithmetic_2op field. + @"return", }; /// The position of an MIR instruction within the `Mir` instructions array. @@ -42,6 +58,36 @@ pub const Inst = struct { /// /// Used by e.g. flushw nop: void, + + /// Three operand arithmetic. + /// if is_imm true then it uses the imm field of rs2_or_imm, + /// otherwise it uses rs2 field. + /// + /// Used by e.g. add, sub + arithmetic_3op: struct { + is_imm: bool, + rd: Register, + rs1: Register, + rs2_or_imm: union { + rs2: Register, + imm: i13, + }, + }, + + /// Two operand arithmetic. + /// if is_imm true then it uses the imm field of rs2_or_imm, + /// otherwise it uses rs2 field. + /// + /// Used by e.g. return + arithmetic_2op: struct { + is_imm: bool, + rs1: Register, + rs2_or_imm: union { + rs2: Register, + imm: i13, + }, + }, + /// Debug info: line and column /// /// Used by e.g. dbg_line @@ -77,4 +123,3 @@ pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end .end = i, }; } - From 94d70bdb69d4459ea522bcef2bc02a56f5a92091 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 1 Apr 2022 19:30:37 +0700 Subject: [PATCH 1120/2031] stage2: sparcv9: Change ordering in Mir Tag --- src/arch/sparcv9/Mir.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index 43b008d189..0b7c2185eb 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -37,14 +37,14 @@ pub const Inst = struct { /// It uses the nop field. nop, + /// A.45 RETURN + /// It uses the arithmetic_2op field. + @"return", + /// A.46 SAVE and RESTORE /// Those uses the arithmetic_3op field. save, restore, - - /// A.45 RETURN - /// It uses the arithmetic_2op field. - @"return", }; /// The position of an MIR instruction within the `Mir` instructions array. From 18c98eb4293ced51689ad67f15575afc120ceee1 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 1 Apr 2022 19:58:49 +0700 Subject: [PATCH 1121/2031] stage2: sparcv9: Placeholder for Air instructions in genBody --- src/arch/sparcv9/CodeGen.zig | 182 ++++++++++++++++++++++++++++++++++- 1 file changed, 179 insertions(+), 3 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index c2aa082b11..286b67a145 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -471,10 +471,181 @@ fn gen(self: *Self) !void { } fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { - _ = self; - _ = body; + const air_tags = self.air.instructions.items(.tag); - @panic("TODO implement genBody"); + for (body) |inst| { + const old_air_bookkeeping = self.air_bookkeeping; + try self.ensureProcessDeathCapacity(Liveness.bpi); + + switch (air_tags[inst]) { + .arg => @panic("TODO implement arg"), + .add => @panic("TODO implement add"), + .addwrap => @panic("TODO implement addwrap"), + .add_sat => @panic("TODO implement add_sat"), + .sub => @panic("TODO implement sub"), + .subwrap => @panic("TODO implement subwrap"), + .sub_sat => @panic("TODO implement sub_sat"), + .mul => @panic("TODO implement mul"), + .mulwrap => @panic("TODO implement mulwrap"), + .mul_sat => @panic("TODO implement mul_sat"), + .div_float => @panic("TODO implement div_float"), + .div_trunc => @panic("TODO implement div_trunc"), + .div_floor => @panic("TODO implement div_floor"), + .div_exact => @panic("TODO implement div_exact"), + .rem => @panic("TODO implement rem"), + .mod => @panic("TODO implement mod"), + .ptr_add => @panic("TODO implement ptr_add"), + .ptr_sub => @panic("TODO implement ptr_sub"), + .max => @panic("TODO implement max"), + .min => @panic("TODO implement min"), + .add_with_overflow => @panic("TODO implement add_with_overflow"), + .sub_with_overflow => @panic("TODO implement sub_with_overflow"), + .mul_with_overflow => @panic("TODO implement mul_with_overflow"), + .shl_with_overflow => @panic("TODO implement shl_with_overflow"), + .alloc => @panic("TODO implement alloc"), + .ret_ptr => @panic("TODO implement ret_ptr"), + .assembly => @panic("TODO implement assembly"), + .bit_and => @panic("TODO implement bit_and"), + .bit_or => @panic("TODO implement bit_or"), + .shr => @panic("TODO implement shr"), + .shr_exact => @panic("TODO implement shr_exact"), + .shl => @panic("TODO implement shl"), + .shl_exact => @panic("TODO implement shl_exact"), + .shl_sat => @panic("TODO implement shl_sat"), + .xor => @panic("TODO implement xor"), + .not => @panic("TODO implement not"), + .bitcast => @panic("TODO implement bitcast"), + .block => @panic("TODO implement block"), + .loop => @panic("TODO implement loop"), + .br => @panic("TODO implement br"), + .breakpoint => @panic("TODO implement breakpoint"), + .ret_addr => @panic("TODO implement ret_addr"), + .frame_addr => @panic("TODO implement frame_addr"), + .call => @panic("TODO implement call"), + .call_always_tail => @panic("TODO implement call_always_tail"), + .call_never_tail => @panic("TODO implement call_never_tail"), + .call_never_inline => @panic("TODO implement call_never_inline"), + .clz => @panic("TODO implement clz"), + .ctz => @panic("TODO implement ctz"), + .popcount => @panic("TODO implement popcount"), + .byte_swap => @panic("TODO implement byte_swap"), + .bit_reverse => @panic("TODO implement bit_reverse"), + .sqrt => @panic("TODO implement sqrt"), + .sin => @panic("TODO implement sin"), + .cos => @panic("TODO implement cos"), + .exp => @panic("TODO implement exp"), + .exp2 => @panic("TODO implement exp2"), + .log => @panic("TODO implement log"), + .log2 => @panic("TODO implement log2"), + .log10 => @panic("TODO implement log10"), + .fabs => @panic("TODO implement fabs"), + .floor => @panic("TODO implement floor"), + .ceil => @panic("TODO implement ceil"), + .round => @panic("TODO implement round"), + .trunc_float => @panic("TODO implement trunc_float"), + .cmp_lt => @panic("TODO implement cmp_lt"), + .cmp_lte => @panic("TODO implement cmp_lte"), + .cmp_eq => @panic("TODO implement cmp_eq"), + .cmp_gte => @panic("TODO implement cmp_gte"), + .cmp_gt => @panic("TODO implement cmp_gt"), + .cmp_neq => @panic("TODO implement cmp_neq"), + .cmp_vector => @panic("TODO implement cmp_vector"), + .cond_br => @panic("TODO implement cond_br"), + .switch_br => @panic("TODO implement switch_br"), + .constant => @panic("TODO implement constant"), + .const_ty => @panic("TODO implement const_ty"), + .dbg_stmt => @panic("TODO implement dbg_stmt"), + .dbg_block_begin => @panic("TODO implement dbg_block_begin"), + .dbg_block_end => @panic("TODO implement dbg_block_end"), + .dbg_inline_begin => @panic("TODO implement dbg_inline_begin"), + .dbg_inline_end => @panic("TODO implement dbg_inline_end"), + .dbg_var_ptr => @panic("TODO implement dbg_var_ptr"), + .dbg_var_val => @panic("TODO implement dbg_var_val"), + .is_null => @panic("TODO implement is_null"), + .is_non_null => @panic("TODO implement is_non_null"), + .is_null_ptr => @panic("TODO implement is_null_ptr"), + .is_non_null_ptr => @panic("TODO implement is_non_null_ptr"), + .is_err => @panic("TODO implement is_err"), + .is_non_err => @panic("TODO implement is_non_err"), + .is_err_ptr => @panic("TODO implement is_err_ptr"), + .is_non_err_ptr => @panic("TODO implement is_non_err_ptr"), + .bool_and => @panic("TODO implement bool_and"), + .bool_or => @panic("TODO implement bool_or"), + .load => @panic("TODO implement load"), + .ptrtoint => @panic("TODO implement ptrtoint"), + .bool_to_int => @panic("TODO implement bool_to_int"), + .ret => @panic("TODO implement ret"), + .ret_load => @panic("TODO implement ret_load"), + .store => @panic("TODO implement store"), + .unreach => @panic("TODO implement unreach"), + .fptrunc => @panic("TODO implement fptrunc"), + .fpext => @panic("TODO implement fpext"), + .intcast => @panic("TODO implement intcast"), + .trunc => @panic("TODO implement trunc"), + .optional_payload => @panic("TODO implement optional_payload"), + .optional_payload_ptr => @panic("TODO implement optional_payload_ptr"), + .optional_payload_ptr_set => @panic("TODO implement optional_payload_ptr_set"), + .wrap_optional => @panic("TODO implement wrap_optional"), + .unwrap_errunion_payload => @panic("TODO implement unwrap_errunion_payload"), + .unwrap_errunion_err => @panic("TODO implement unwrap_errunion_err"), + .unwrap_errunion_payload_ptr => @panic("TODO implement unwrap_errunion_payload_ptr"), + .unwrap_errunion_err_ptr => @panic("TODO implement unwrap_errunion_err_ptr"), + .errunion_payload_ptr_set => @panic("TODO implement errunion_payload_ptr_set"), + .wrap_errunion_payload => @panic("TODO implement wrap_errunion_payload"), + .wrap_errunion_err => @panic("TODO implement wrap_errunion_err"), + .struct_field_ptr => @panic("TODO implement struct_field_ptr"), + .struct_field_ptr_index_0 => @panic("TODO implement struct_field_ptr_index_0"), + .struct_field_ptr_index_1 => @panic("TODO implement struct_field_ptr_index_1"), + .struct_field_ptr_index_2 => @panic("TODO implement struct_field_ptr_index_2"), + .struct_field_ptr_index_3 => @panic("TODO implement struct_field_ptr_index_3"), + .struct_field_val => @panic("TODO implement struct_field_val"), + .set_union_tag => @panic("TODO implement set_union_tag"), + .get_union_tag => @panic("TODO implement get_union_tag"), + .slice => @panic("TODO implement slice"), + .slice_len => @panic("TODO implement slice_len"), + .slice_ptr => @panic("TODO implement slice_ptr"), + .ptr_slice_len_ptr => @panic("TODO implement ptr_slice_len_ptr"), + .ptr_slice_ptr_ptr => @panic("TODO implement ptr_slice_ptr_ptr"), + .array_elem_val => @panic("TODO implement array_elem_val"), + .slice_elem_val => @panic("TODO implement slice_elem_val"), + .slice_elem_ptr => @panic("TODO implement slice_elem_ptr"), + .ptr_elem_val => @panic("TODO implement ptr_elem_val"), + .ptr_elem_ptr => @panic("TODO implement ptr_elem_ptr"), + .array_to_slice => @panic("TODO implement array_to_slice"), + .float_to_int => @panic("TODO implement float_to_int"), + .int_to_float => @panic("TODO implement int_to_float"), + .reduce => @panic("TODO implement reduce"), + .splat => @panic("TODO implement splat"), + .shuffle => @panic("TODO implement shuffle"), + .select => @panic("TODO implement select"), + .memset => @panic("TODO implement memset"), + .memcpy => @panic("TODO implement memcpy"), + .cmpxchg_weak => @panic("TODO implement cmpxchg_weak"), + .cmpxchg_strong => @panic("TODO implement cmpxchg_strong"), + .fence => @panic("TODO implement fence"), + .atomic_load => @panic("TODO implement atomic_load"), + .atomic_store_unordered => @panic("TODO implement atomic_store_unordered"), + .atomic_store_monotonic => @panic("TODO implement atomic_store_monotonic"), + .atomic_store_release => @panic("TODO implement atomic_store_release"), + .atomic_store_seq_cst => @panic("TODO implement atomic_store_seq_cst"), + .atomic_rmw => @panic("TODO implement atomic_rmw"), + .tag_name => @panic("TODO implement tag_name"), + .error_name => @panic("TODO implement error_name"), + .aggregate_init => @panic("TODO implement aggregate_init"), + .union_init => @panic("TODO implement union_init"), + .prefetch => @panic("TODO implement prefetch"), + .mul_add => @panic("TODO implement mul_add"), + .field_parent_ptr => @panic("TODO implement field_parent_ptr"), + + .wasm_memory_size, .wasm_memory_grow => unreachable, + } + + if (std.debug.runtime_safety) { + if (self.air_bookkeeping < old_air_bookkeeping + 1) { + std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[inst] }); + } + } + } } fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { @@ -487,6 +658,11 @@ fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { return result_index; } +fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { + const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table; + try table.ensureUnusedCapacity(self.gpa, additional_count); +} + fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { @setCold(true); assert(self.err_msg == null); From 71cd3466ec129404a9cc6679f25b0dde8623b094 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 1 Apr 2022 22:23:19 +0700 Subject: [PATCH 1122/2031] stage2: sparcv9: Adjust RegisterManager settings --- src/arch/sparcv9/CodeGen.zig | 5 ++++- src/arch/sparcv9/abi.zig | 19 ++++++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 286b67a145..33dc0e1f8f 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -16,6 +16,8 @@ const Type = @import("../../type.zig").Type; const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; const FnResult = @import("../../codegen.zig").FnResult; const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; +const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; +const RegisterManager = RegisterManagerFn(Self, Register, &abi.allocatable_regs); const build_options = @import("build_options"); @@ -73,6 +75,8 @@ branch_stack: *std.ArrayList(Branch), // Key is the block instruction blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, +register_manager: RegisterManager = .{}, + /// Maps offset to what is stored there. stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, @@ -380,7 +384,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type, is_caller: bool) !Ca return result; } -/// Caller must call `CallMCValues.deinit`. fn gen(self: *Self) !void { const cc = self.fn_type.fnCallingConvention(); if (cc != .Naked) { diff --git a/src/arch/sparcv9/abi.zig b/src/arch/sparcv9/abi.zig index ceac33c08c..4cb10a99ea 100644 --- a/src/arch/sparcv9/abi.zig +++ b/src/arch/sparcv9/abi.zig @@ -1,9 +1,22 @@ const bits = @import("bits.zig"); const Register = bits.Register; -// Register windowing mechanism will take care of preserving registers -// so no need to do it manually -pub const callee_preserved_regs = [_]Register{}; +// There are no callee-preserved registers since the windowing +// mechanism already takes care of them. +// We still need to preserve %o0-%o5, %g1, %g4, and %g5 before calling +// something, though, as those are shared with the callee and might be +// thrashed by it. +pub const caller_preserved_regs = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5, .g1, .g4, .g5 }; + +// Try to allocate i, l, o, then g sets of registers, in order of priority. +pub const allocatable_regs = [_]Register{ + // zig fmt: off + .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5", + .l0, .l1, .l2, .l3, .l4, .l5, .l6, .l7, + .o0, .o1, .o2, .o3, .o4, .o5, + .g1, .g4, .g5, + // zig fmt: on +}; pub const c_abi_int_param_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 }; pub const c_abi_int_param_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5" }; From 5ab6b5a77723d203644a7112cf37f3122c738986 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 1 Apr 2022 22:32:56 +0700 Subject: [PATCH 1123/2031] stage2: sparcv9: implement dbgAdvancePCAndLine --- src/arch/sparcv9/Emit.zig | 52 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index 28c172f329..8d870e43f5 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -9,6 +9,7 @@ const ErrorMsg = Module.ErrorMsg; const Liveness = @import("../../Liveness.zig"); const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; const DW = std.dwarf; +const leb128 = std.leb; const Emit = @This(); const Mir = @import("Mir.zig"); @@ -60,11 +61,54 @@ pub fn deinit(emit: *Emit) void { } fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { - _ = self; - _ = line; - _ = column; + const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line); + const delta_pc: usize = self.code.items.len - self.prev_di_pc; + switch (self.debug_output) { + .dwarf => |dbg_out| { + // TODO Look into using the DWARF special opcodes to compress this data. + // It lets you emit single-byte opcodes that add different numbers to + // both the PC and the line number at the same time. + try dbg_out.dbg_line.ensureUnusedCapacity(11); + dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); + leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable; + if (delta_line != 0) { + dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line); + leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; + } + dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); + self.prev_di_pc = self.code.items.len; + self.prev_di_line = line; + self.prev_di_column = column; + self.prev_di_pc = self.code.items.len; + }, + .plan9 => |dbg_out| { + if (delta_pc <= 0) return; // only do this when the pc changes + // we have already checked the target in the linker to make sure it is compatable + const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable; - @panic("TODO implement dbgAdvancePCAndLine"); + // increasing the line number + try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line); + // increasing the pc + const d_pc_p9 = @intCast(i64, delta_pc) - quant; + if (d_pc_p9 > 0) { + // minus one because if its the last one, we want to leave space to change the line which is one quanta + try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant); + if (dbg_out.pcop_change_index.*) |pci| + dbg_out.dbg_line.items[pci] += 1; + dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1); + } else if (d_pc_p9 == 0) { + // we don't need to do anything, because adding the quant does it for us + } else unreachable; + if (dbg_out.start_line.* == null) + dbg_out.start_line.* = self.prev_di_line; + dbg_out.end_line.* = line; + // only do this if the pc changed + self.prev_di_line = line; + self.prev_di_column = column; + self.prev_di_pc = self.code.items.len; + }, + .none => {}, + } } fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { From cec48f2cf1009653ac1097328b75beaf0bf198d2 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sat, 2 Apr 2022 08:44:56 +0700 Subject: [PATCH 1124/2031] stage2: sparcv9: Different formatting for genBody --- src/arch/sparcv9/CodeGen.zig | 337 ++++++++++++++++++----------------- 1 file changed, 178 insertions(+), 159 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 33dc0e1f8f..35d9f5bfa5 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -481,166 +481,185 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { try self.ensureProcessDeathCapacity(Liveness.bpi); switch (air_tags[inst]) { - .arg => @panic("TODO implement arg"), - .add => @panic("TODO implement add"), - .addwrap => @panic("TODO implement addwrap"), - .add_sat => @panic("TODO implement add_sat"), - .sub => @panic("TODO implement sub"), - .subwrap => @panic("TODO implement subwrap"), - .sub_sat => @panic("TODO implement sub_sat"), - .mul => @panic("TODO implement mul"), - .mulwrap => @panic("TODO implement mulwrap"), - .mul_sat => @panic("TODO implement mul_sat"), - .div_float => @panic("TODO implement div_float"), - .div_trunc => @panic("TODO implement div_trunc"), - .div_floor => @panic("TODO implement div_floor"), - .div_exact => @panic("TODO implement div_exact"), - .rem => @panic("TODO implement rem"), - .mod => @panic("TODO implement mod"), - .ptr_add => @panic("TODO implement ptr_add"), - .ptr_sub => @panic("TODO implement ptr_sub"), - .max => @panic("TODO implement max"), - .min => @panic("TODO implement min"), - .add_with_overflow => @panic("TODO implement add_with_overflow"), - .sub_with_overflow => @panic("TODO implement sub_with_overflow"), - .mul_with_overflow => @panic("TODO implement mul_with_overflow"), - .shl_with_overflow => @panic("TODO implement shl_with_overflow"), - .alloc => @panic("TODO implement alloc"), - .ret_ptr => @panic("TODO implement ret_ptr"), - .assembly => @panic("TODO implement assembly"), - .bit_and => @panic("TODO implement bit_and"), - .bit_or => @panic("TODO implement bit_or"), - .shr => @panic("TODO implement shr"), - .shr_exact => @panic("TODO implement shr_exact"), - .shl => @panic("TODO implement shl"), - .shl_exact => @panic("TODO implement shl_exact"), - .shl_sat => @panic("TODO implement shl_sat"), - .xor => @panic("TODO implement xor"), - .not => @panic("TODO implement not"), - .bitcast => @panic("TODO implement bitcast"), - .block => @panic("TODO implement block"), - .loop => @panic("TODO implement loop"), - .br => @panic("TODO implement br"), - .breakpoint => @panic("TODO implement breakpoint"), - .ret_addr => @panic("TODO implement ret_addr"), - .frame_addr => @panic("TODO implement frame_addr"), - .call => @panic("TODO implement call"), - .call_always_tail => @panic("TODO implement call_always_tail"), - .call_never_tail => @panic("TODO implement call_never_tail"), - .call_never_inline => @panic("TODO implement call_never_inline"), - .clz => @panic("TODO implement clz"), - .ctz => @panic("TODO implement ctz"), - .popcount => @panic("TODO implement popcount"), - .byte_swap => @panic("TODO implement byte_swap"), - .bit_reverse => @panic("TODO implement bit_reverse"), - .sqrt => @panic("TODO implement sqrt"), - .sin => @panic("TODO implement sin"), - .cos => @panic("TODO implement cos"), - .exp => @panic("TODO implement exp"), - .exp2 => @panic("TODO implement exp2"), - .log => @panic("TODO implement log"), - .log2 => @panic("TODO implement log2"), - .log10 => @panic("TODO implement log10"), - .fabs => @panic("TODO implement fabs"), - .floor => @panic("TODO implement floor"), - .ceil => @panic("TODO implement ceil"), - .round => @panic("TODO implement round"), - .trunc_float => @panic("TODO implement trunc_float"), - .cmp_lt => @panic("TODO implement cmp_lt"), - .cmp_lte => @panic("TODO implement cmp_lte"), - .cmp_eq => @panic("TODO implement cmp_eq"), - .cmp_gte => @panic("TODO implement cmp_gte"), - .cmp_gt => @panic("TODO implement cmp_gt"), - .cmp_neq => @panic("TODO implement cmp_neq"), - .cmp_vector => @panic("TODO implement cmp_vector"), - .cond_br => @panic("TODO implement cond_br"), - .switch_br => @panic("TODO implement switch_br"), - .constant => @panic("TODO implement constant"), - .const_ty => @panic("TODO implement const_ty"), - .dbg_stmt => @panic("TODO implement dbg_stmt"), - .dbg_block_begin => @panic("TODO implement dbg_block_begin"), - .dbg_block_end => @panic("TODO implement dbg_block_end"), - .dbg_inline_begin => @panic("TODO implement dbg_inline_begin"), - .dbg_inline_end => @panic("TODO implement dbg_inline_end"), - .dbg_var_ptr => @panic("TODO implement dbg_var_ptr"), - .dbg_var_val => @panic("TODO implement dbg_var_val"), - .is_null => @panic("TODO implement is_null"), - .is_non_null => @panic("TODO implement is_non_null"), - .is_null_ptr => @panic("TODO implement is_null_ptr"), - .is_non_null_ptr => @panic("TODO implement is_non_null_ptr"), - .is_err => @panic("TODO implement is_err"), - .is_non_err => @panic("TODO implement is_non_err"), - .is_err_ptr => @panic("TODO implement is_err_ptr"), - .is_non_err_ptr => @panic("TODO implement is_non_err_ptr"), - .bool_and => @panic("TODO implement bool_and"), - .bool_or => @panic("TODO implement bool_or"), - .load => @panic("TODO implement load"), - .ptrtoint => @panic("TODO implement ptrtoint"), - .bool_to_int => @panic("TODO implement bool_to_int"), - .ret => @panic("TODO implement ret"), - .ret_load => @panic("TODO implement ret_load"), - .store => @panic("TODO implement store"), - .unreach => @panic("TODO implement unreach"), - .fptrunc => @panic("TODO implement fptrunc"), - .fpext => @panic("TODO implement fpext"), - .intcast => @panic("TODO implement intcast"), - .trunc => @panic("TODO implement trunc"), - .optional_payload => @panic("TODO implement optional_payload"), - .optional_payload_ptr => @panic("TODO implement optional_payload_ptr"), - .optional_payload_ptr_set => @panic("TODO implement optional_payload_ptr_set"), - .wrap_optional => @panic("TODO implement wrap_optional"), - .unwrap_errunion_payload => @panic("TODO implement unwrap_errunion_payload"), - .unwrap_errunion_err => @panic("TODO implement unwrap_errunion_err"), - .unwrap_errunion_payload_ptr => @panic("TODO implement unwrap_errunion_payload_ptr"), - .unwrap_errunion_err_ptr => @panic("TODO implement unwrap_errunion_err_ptr"), - .errunion_payload_ptr_set => @panic("TODO implement errunion_payload_ptr_set"), - .wrap_errunion_payload => @panic("TODO implement wrap_errunion_payload"), - .wrap_errunion_err => @panic("TODO implement wrap_errunion_err"), - .struct_field_ptr => @panic("TODO implement struct_field_ptr"), - .struct_field_ptr_index_0 => @panic("TODO implement struct_field_ptr_index_0"), - .struct_field_ptr_index_1 => @panic("TODO implement struct_field_ptr_index_1"), - .struct_field_ptr_index_2 => @panic("TODO implement struct_field_ptr_index_2"), - .struct_field_ptr_index_3 => @panic("TODO implement struct_field_ptr_index_3"), - .struct_field_val => @panic("TODO implement struct_field_val"), - .set_union_tag => @panic("TODO implement set_union_tag"), - .get_union_tag => @panic("TODO implement get_union_tag"), - .slice => @panic("TODO implement slice"), - .slice_len => @panic("TODO implement slice_len"), - .slice_ptr => @panic("TODO implement slice_ptr"), - .ptr_slice_len_ptr => @panic("TODO implement ptr_slice_len_ptr"), - .ptr_slice_ptr_ptr => @panic("TODO implement ptr_slice_ptr_ptr"), - .array_elem_val => @panic("TODO implement array_elem_val"), - .slice_elem_val => @panic("TODO implement slice_elem_val"), - .slice_elem_ptr => @panic("TODO implement slice_elem_ptr"), - .ptr_elem_val => @panic("TODO implement ptr_elem_val"), - .ptr_elem_ptr => @panic("TODO implement ptr_elem_ptr"), - .array_to_slice => @panic("TODO implement array_to_slice"), - .float_to_int => @panic("TODO implement float_to_int"), - .int_to_float => @panic("TODO implement int_to_float"), - .reduce => @panic("TODO implement reduce"), - .splat => @panic("TODO implement splat"), - .shuffle => @panic("TODO implement shuffle"), - .select => @panic("TODO implement select"), - .memset => @panic("TODO implement memset"), - .memcpy => @panic("TODO implement memcpy"), - .cmpxchg_weak => @panic("TODO implement cmpxchg_weak"), - .cmpxchg_strong => @panic("TODO implement cmpxchg_strong"), - .fence => @panic("TODO implement fence"), - .atomic_load => @panic("TODO implement atomic_load"), - .atomic_store_unordered => @panic("TODO implement atomic_store_unordered"), - .atomic_store_monotonic => @panic("TODO implement atomic_store_monotonic"), - .atomic_store_release => @panic("TODO implement atomic_store_release"), - .atomic_store_seq_cst => @panic("TODO implement atomic_store_seq_cst"), - .atomic_rmw => @panic("TODO implement atomic_rmw"), - .tag_name => @panic("TODO implement tag_name"), - .error_name => @panic("TODO implement error_name"), - .aggregate_init => @panic("TODO implement aggregate_init"), - .union_init => @panic("TODO implement union_init"), - .prefetch => @panic("TODO implement prefetch"), - .mul_add => @panic("TODO implement mul_add"), - .field_parent_ptr => @panic("TODO implement field_parent_ptr"), + // zig fmt: off + .add, .ptr_add => @panic("TODO try self.airBinOp(inst)"), + .addwrap => @panic("TODO try self.airAddWrap(inst)"), + .add_sat => @panic("TODO try self.airAddSat(inst)"), + .sub, .ptr_sub => @panic("TODO try self.airBinOp(inst)"), + .subwrap => @panic("TODO try self.airSubWrap(inst)"), + .sub_sat => @panic("TODO try self.airSubSat(inst)"), + .mul => @panic("TODO try self.airMul(inst)"), + .mulwrap => @panic("TODO try self.airMulWrap(inst)"), + .mul_sat => @panic("TODO try self.airMulSat(inst)"), + .rem => @panic("TODO try self.airRem(inst)"), + .mod => @panic("TODO try self.airMod(inst)"), + .shl, .shl_exact => @panic("TODO try self.airShl(inst)"), + .shl_sat => @panic("TODO try self.airShlSat(inst)"), + .min => @panic("TODO try self.airMin(inst)"), + .max => @panic("TODO try self.airMax(inst)"), + .slice => @panic("TODO try self.airSlice(inst)"), - .wasm_memory_size, .wasm_memory_grow => unreachable, + .sqrt, + .sin, + .cos, + .exp, + .exp2, + .log, + .log2, + .log10, + .fabs, + .floor, + .ceil, + .round, + .trunc_float, + => @panic("TODO try self.airUnaryMath(inst)"), + + .add_with_overflow => @panic("TODO try self.airAddWithOverflow(inst)"), + .sub_with_overflow => @panic("TODO try self.airSubWithOverflow(inst)"), + .mul_with_overflow => @panic("TODO try self.airMulWithOverflow(inst)"), + .shl_with_overflow => @panic("TODO try self.airShlWithOverflow(inst)"), + + .div_float, .div_trunc, .div_floor, .div_exact => @panic("TODO try self.airDiv(inst)"), + + .cmp_lt => @panic("TODO try self.airCmp(inst, .lt)"), + .cmp_lte => @panic("TODO try self.airCmp(inst, .lte)"), + .cmp_eq => @panic("TODO try self.airCmp(inst, .eq)"), + .cmp_gte => @panic("TODO try self.airCmp(inst, .gte)"), + .cmp_gt => @panic("TODO try self.airCmp(inst, .gt)"), + .cmp_neq => @panic("TODO try self.airCmp(inst, .neq)"), + .cmp_vector => @panic("TODO try self.airCmpVector(inst)"), + + .bool_and => @panic("TODO try self.airBoolOp(inst)"), + .bool_or => @panic("TODO try self.airBoolOp(inst)"), + .bit_and => @panic("TODO try self.airBitAnd(inst)"), + .bit_or => @panic("TODO try self.airBitOr(inst)"), + .xor => @panic("TODO try self.airXor(inst)"), + .shr, .shr_exact => @panic("TODO try self.airShr(inst)"), + + .alloc => @panic("TODO try self.airAlloc(inst)"), + .ret_ptr => @panic("TODO try self.airRetPtr(inst)"), + .arg => @panic("TODO try self.airArg(inst)"), + .assembly => @panic("TODO try self.airAsm(inst)"), + .bitcast => @panic("TODO try self.airBitCast(inst)"), + .block => @panic("TODO try self.airBlock(inst)"), + .br => @panic("TODO try self.airBr(inst)"), + .breakpoint => @panic("TODO try self.airBreakpoint()"), + .ret_addr => @panic("TODO try self.airRetAddr(inst)"), + .frame_addr => @panic("TODO try self.airFrameAddress(inst)"), + .fence => @panic("TODO try self.airFence()"), + .cond_br => @panic("TODO try self.airCondBr(inst)"), + .dbg_stmt => @panic("TODO try self.airDbgStmt(inst)"), + .fptrunc => @panic("TODO try self.airFptrunc(inst)"), + .fpext => @panic("TODO try self.airFpext(inst)"), + .intcast => @panic("TODO try self.airIntCast(inst)"), + .trunc => @panic("TODO try self.airTrunc(inst)"), + .bool_to_int => @panic("TODO try self.airBoolToInt(inst)"), + .is_non_null => @panic("TODO try self.airIsNonNull(inst)"), + .is_non_null_ptr => @panic("TODO try self.airIsNonNullPtr(inst)"), + .is_null => @panic("TODO try self.airIsNull(inst)"), + .is_null_ptr => @panic("TODO try self.airIsNullPtr(inst)"), + .is_non_err => @panic("TODO try self.airIsNonErr(inst)"), + .is_non_err_ptr => @panic("TODO try self.airIsNonErrPtr(inst)"), + .is_err => @panic("TODO try self.airIsErr(inst)"), + .is_err_ptr => @panic("TODO try self.airIsErrPtr(inst)"), + .load => @panic("TODO try self.airLoad(inst)"), + .loop => @panic("TODO try self.airLoop(inst)"), + .not => @panic("TODO try self.airNot(inst)"), + .ptrtoint => @panic("TODO try self.airPtrToInt(inst)"), + .ret => @panic("TODO try self.airRet(inst)"), + .ret_load => @panic("TODO try self.airRetLoad(inst)"), + .store => @panic("TODO try self.airStore(inst)"), + .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"), + .struct_field_val=> @panic("TODO try self.airStructFieldVal(inst)"), + .array_to_slice => @panic("TODO try self.airArrayToSlice(inst)"), + .int_to_float => @panic("TODO try self.airIntToFloat(inst)"), + .float_to_int => @panic("TODO try self.airFloatToInt(inst)"), + .cmpxchg_strong => @panic("TODO try self.airCmpxchg(inst)"), + .cmpxchg_weak => @panic("TODO try self.airCmpxchg(inst)"), + .atomic_rmw => @panic("TODO try self.airAtomicRmw(inst)"), + .atomic_load => @panic("TODO try self.airAtomicLoad(inst)"), + .memcpy => @panic("TODO try self.airMemcpy(inst)"), + .memset => @panic("TODO try self.airMemset(inst)"), + .set_union_tag => @panic("TODO try self.airSetUnionTag(inst)"), + .get_union_tag => @panic("TODO try self.airGetUnionTag(inst)"), + .clz => @panic("TODO try self.airClz(inst)"), + .ctz => @panic("TODO try self.airCtz(inst)"), + .popcount => @panic("TODO try self.airPopcount(inst)"), + .byte_swap => @panic("TODO try self.airByteSwap(inst)"), + .bit_reverse => @panic("TODO try self.airBitReverse(inst)"), + .tag_name => @panic("TODO try self.airTagName(inst)"), + .error_name => @panic("TODO try self.airErrorName(inst)"), + .splat => @panic("TODO try self.airSplat(inst)"), + .select => @panic("TODO try self.airSelect(inst)"), + .shuffle => @panic("TODO try self.airShuffle(inst)"), + .reduce => @panic("TODO try self.airReduce(inst)"), + .aggregate_init => @panic("TODO try self.airAggregateInit(inst)"), + .union_init => @panic("TODO try self.airUnionInit(inst)"), + .prefetch => @panic("TODO try self.airPrefetch(inst)"), + .mul_add => @panic("TODO try self.airMulAdd(inst)"), + + .dbg_var_ptr, + .dbg_var_val, + => @panic("TODO try self.airDbgVar(inst)"), + + .dbg_inline_begin, + .dbg_inline_end, + => @panic("TODO try self.airDbgInline(inst)"), + + .dbg_block_begin, + .dbg_block_end, + => @panic("TODO try self.airDbgBlock(inst)"), + + .call => @panic("TODO try self.airCall(inst, .auto)"), + .call_always_tail => @panic("TODO try self.airCall(inst, .always_tail)"), + .call_never_tail => @panic("TODO try self.airCall(inst, .never_tail)"), + .call_never_inline => @panic("TODO try self.airCall(inst, .never_inline)"), + + .atomic_store_unordered => @panic("TODO try self.airAtomicStore(inst, .Unordered)"), + .atomic_store_monotonic => @panic("TODO try self.airAtomicStore(inst, .Monotonic)"), + .atomic_store_release => @panic("TODO try self.airAtomicStore(inst, .Release)"), + .atomic_store_seq_cst => @panic("TODO try self.airAtomicStore(inst, .SeqCst)"), + + .struct_field_ptr_index_0 => @panic("TODO try self.airStructFieldPtrIndex(inst, 0)"), + .struct_field_ptr_index_1 => @panic("TODO try self.airStructFieldPtrIndex(inst, 1)"), + .struct_field_ptr_index_2 => @panic("TODO try self.airStructFieldPtrIndex(inst, 2)"), + .struct_field_ptr_index_3 => @panic("TODO try self.airStructFieldPtrIndex(inst, 3)"), + + .field_parent_ptr => @panic("TODO try self.airFieldParentPtr(inst)"), + + .switch_br => @panic("TODO try self.airSwitch(inst)"), + .slice_ptr => @panic("TODO try self.airSlicePtr(inst)"), + .slice_len => @panic("TODO try self.airSliceLen(inst)"), + + .ptr_slice_len_ptr => @panic("TODO try self.airPtrSliceLenPtr(inst)"), + .ptr_slice_ptr_ptr => @panic("TODO try self.airPtrSlicePtrPtr(inst)"), + + .array_elem_val => @panic("TODO try self.airArrayElemVal(inst)"), + .slice_elem_val => @panic("TODO try self.airSliceElemVal(inst)"), + .slice_elem_ptr => @panic("TODO try self.airSliceElemPtr(inst)"), + .ptr_elem_val => @panic("TODO try self.airPtrElemVal(inst)"), + .ptr_elem_ptr => @panic("TODO try self.airPtrElemPtr(inst)"), + + .constant => unreachable, // excluded from function bodies + .const_ty => unreachable, // excluded from function bodies + .unreach => @panic("TODO self.finishAirBookkeeping()"), + + .optional_payload => @panic("TODO try self.airOptionalPayload(inst)"), + .optional_payload_ptr => @panic("TODO try self.airOptionalPayloadPtr(inst)"), + .optional_payload_ptr_set => @panic("TODO try self.airOptionalPayloadPtrSet(inst)"), + .unwrap_errunion_err => @panic("TODO try self.airUnwrapErrErr(inst)"), + .unwrap_errunion_payload => @panic("TODO try self.airUnwrapErrPayload(inst)"), + .unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"), + .unwrap_errunion_payload_ptr=> @panic("TODO try self.airUnwrapErrPayloadPtr(inst)"), + .errunion_payload_ptr_set => @panic("TODO try self.airErrUnionPayloadPtrSet(inst)"), + + .wrap_optional => @panic("TODO try self.airWrapOptional(inst)"), + .wrap_errunion_payload => @panic("TODO try self.airWrapErrUnionPayload(inst)"), + .wrap_errunion_err => @panic("TODO try self.airWrapErrUnionErr(inst)"), + + .wasm_memory_size => unreachable, + .wasm_memory_grow => unreachable, + // zig fmt: on } if (std.debug.runtime_safety) { From 1972a2b08063841bdd6dd411b4fb0c1b16225067 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sat, 2 Apr 2022 18:45:31 +0700 Subject: [PATCH 1125/2031] stage2: sparcv9: Add placeholders to generate a minimal program --- src/arch/sparcv9/CodeGen.zig | 740 +++++++++++++++++++++++++++++------ src/arch/sparcv9/Emit.zig | 26 +- src/arch/sparcv9/Mir.zig | 86 +++- 3 files changed, 712 insertions(+), 140 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 35d9f5bfa5..40d6db176f 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -2,11 +2,14 @@ //! This lowers AIR into MIR. const std = @import("std"); const assert = std.debug.assert; +const log = std.log.scoped(.codegen); +const math = std.math; const mem = std.mem; const Allocator = mem.Allocator; const builtin = @import("builtin"); const link = @import("../../link.zig"); const Module = @import("../../Module.zig"); +const TypedValue = @import("../../TypedValue.zig"); const ErrorMsg = Module.ErrorMsg; const Air = @import("../../Air.zig"); const Mir = @import("Mir.zig"); @@ -33,6 +36,11 @@ const InnerError = error{ OutOfRegisters, }; +const RegisterView = enum(u1) { + caller, + callee, +}; + gpa: Allocator, air: Air, liveness: Liveness, @@ -165,7 +173,7 @@ const StackAllocation = struct { }; const BlockData = struct { - relocs: std.ArrayListUnmanaged(Reloc), + relocs: std.ArrayListUnmanaged(Mir.Inst.Index), /// The first break instruction encounters `null` here and chooses a /// machine code value for the block result, populating this field. /// Following break instructions encounter that value and use it for @@ -173,18 +181,6 @@ const BlockData = struct { mcv: MCValue, }; -const Reloc = union(enum) { - /// The value is an offset into the `Function` `code` from the beginning. - /// To perform the reloc, write 32-bit signed little-endian integer - /// which is a relative jump, based on the address following the reloc. - rel32: usize, - /// A branch in the ARM instruction set - arm_branch: struct { - pos: usize, - cond: @import("../arm/bits.zig").Condition, - }, -}; - const CallMCValues = struct { args: []MCValue, return_value: MCValue, @@ -245,7 +241,7 @@ pub fn generate( defer function.blocks.deinit(bin_file.allocator); defer function.exitlude_jump_relocs.deinit(bin_file.allocator); - var call_info = function.resolveCallingConventionValues(fn_type, false) catch |err| switch (err) { + var call_info = function.resolveCallingConventionValues(fn_type, .callee) catch |err| switch (err) { error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, error.OutOfRegisters => return FnResult{ .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), @@ -298,92 +294,6 @@ pub fn generate( } } -/// Caller must call `CallMCValues.deinit`. -fn resolveCallingConventionValues(self: *Self, fn_ty: Type, is_caller: bool) !CallMCValues { - const cc = fn_ty.fnCallingConvention(); - const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen()); - defer self.gpa.free(param_types); - fn_ty.fnParamTypes(param_types); - var result: CallMCValues = .{ - .args = try self.gpa.alloc(MCValue, param_types.len), - // These undefined values must be populated before returning from this function. - .return_value = undefined, - .stack_byte_count = undefined, - .stack_align = undefined, - }; - errdefer self.gpa.free(result.args); - - const ret_ty = fn_ty.fnReturnType(); - - switch (cc) { - .Naked => { - assert(result.args.len == 0); - result.return_value = .{ .unreach = {} }; - result.stack_byte_count = 0; - result.stack_align = 1; - return result; - }, - .Unspecified, .C => { - // SPARC Compliance Definition 2.4.1, Chapter 3 - // Low-Level System Information (64-bit psABI) - Function Calling Sequence - - var next_register: usize = 0; - var next_stack_offset: u32 = 0; - - // The caller puts the argument in %o0-%o5, which becomes %i0-%i5 inside the callee. - const argument_registers = if (is_caller) abi.c_abi_int_param_regs_caller_view else abi.c_abi_int_param_regs_callee_view; - - for (param_types) |ty, i| { - const param_size = @intCast(u32, ty.abiSize(self.target.*)); - if (param_size <= 8) { - if (next_register < argument_registers.len) { - result.args[i] = .{ .register = argument_registers[next_register] }; - next_register += 1; - } else { - result.args[i] = .{ .stack_offset = next_stack_offset }; - next_register += next_stack_offset; - } - } else if (param_size <= 16) { - if (next_register < argument_registers.len - 1) { - return self.fail("TODO MCValues with 2 registers", .{}); - } else if (next_register < argument_registers.len) { - return self.fail("TODO MCValues split register + stack", .{}); - } else { - result.args[i] = .{ .stack_offset = next_stack_offset }; - next_register += next_stack_offset; - } - } else { - result.args[i] = .{ .stack_offset = next_stack_offset }; - next_register += next_stack_offset; - } - } - - result.stack_byte_count = next_stack_offset; - result.stack_align = 16; - }, - else => return self.fail("TODO implement function parameters for {} on sparcv9", .{cc}), - } - - if (ret_ty.zigTypeTag() == .NoReturn) { - result.return_value = .{ .unreach = {} }; - } else if (!ret_ty.hasRuntimeBits()) { - result.return_value = .{ .none = {} }; - } else switch (cc) { - .Naked => unreachable, - .Unspecified, .C => { - const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); - // The callee puts the return values in %i0-%i3, which becomes %o0-%o3 inside the caller. - if (ret_ty_size <= 8) { - result.return_value = if (is_caller) .{ .register = abi.c_abi_int_return_regs_caller_view[0] } else .{ .register = abi.c_abi_int_return_regs_callee_view[0] }; - } else { - return self.fail("TODO support more return values for sparcv9", .{}); - } - }, - else => return self.fail("TODO implement function return values for {} on sparcv9", .{cc}), - } - return result; -} - fn gen(self: *Self) !void { const cc = self.fn_type.fnCallingConvention(); if (cc != .Naked) { @@ -519,7 +429,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .mul_with_overflow => @panic("TODO try self.airMulWithOverflow(inst)"), .shl_with_overflow => @panic("TODO try self.airShlWithOverflow(inst)"), - .div_float, .div_trunc, .div_floor, .div_exact => @panic("TODO try self.airDiv(inst)"), + .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), .cmp_lt => @panic("TODO try self.airCmp(inst, .lt)"), .cmp_lte => @panic("TODO try self.airCmp(inst, .lte)"), @@ -537,18 +447,18 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .shr, .shr_exact => @panic("TODO try self.airShr(inst)"), .alloc => @panic("TODO try self.airAlloc(inst)"), - .ret_ptr => @panic("TODO try self.airRetPtr(inst)"), - .arg => @panic("TODO try self.airArg(inst)"), - .assembly => @panic("TODO try self.airAsm(inst)"), + .ret_ptr => try self.airRetPtr(inst), + .arg => try self.airArg(inst), + .assembly => try self.airAsm(inst), .bitcast => @panic("TODO try self.airBitCast(inst)"), - .block => @panic("TODO try self.airBlock(inst)"), + .block => try self.airBlock(inst), .br => @panic("TODO try self.airBr(inst)"), .breakpoint => @panic("TODO try self.airBreakpoint()"), .ret_addr => @panic("TODO try self.airRetAddr(inst)"), .frame_addr => @panic("TODO try self.airFrameAddress(inst)"), .fence => @panic("TODO try self.airFence()"), .cond_br => @panic("TODO try self.airCondBr(inst)"), - .dbg_stmt => @panic("TODO try self.airDbgStmt(inst)"), + .dbg_stmt => try self.airDbgStmt(inst), .fptrunc => @panic("TODO try self.airFptrunc(inst)"), .fpext => @panic("TODO try self.airFpext(inst)"), .intcast => @panic("TODO try self.airIntCast(inst)"), @@ -567,8 +477,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .not => @panic("TODO try self.airNot(inst)"), .ptrtoint => @panic("TODO try self.airPtrToInt(inst)"), .ret => @panic("TODO try self.airRet(inst)"), - .ret_load => @panic("TODO try self.airRetLoad(inst)"), - .store => @panic("TODO try self.airStore(inst)"), + .ret_load => try self.airRetLoad(inst), + .store => try self.airStore(inst), .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"), .struct_field_val=> @panic("TODO try self.airStructFieldVal(inst)"), .array_to_slice => @panic("TODO try self.airArrayToSlice(inst)"), @@ -600,20 +510,20 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .dbg_var_ptr, .dbg_var_val, - => @panic("TODO try self.airDbgVar(inst)"), + => try self.airDbgVar(inst), .dbg_inline_begin, .dbg_inline_end, - => @panic("TODO try self.airDbgInline(inst)"), + => try self.airDbgInline(inst), .dbg_block_begin, .dbg_block_end, - => @panic("TODO try self.airDbgBlock(inst)"), + => try self.airDbgBlock(inst), - .call => @panic("TODO try self.airCall(inst, .auto)"), + .call => try self.airCall(inst, .auto), .call_always_tail => @panic("TODO try self.airCall(inst, .always_tail)"), .call_never_tail => @panic("TODO try self.airCall(inst, .never_tail)"), - .call_never_inline => @panic("TODO try self.airCall(inst, .never_inline)"), + .call_never_inline => try self.airCall(inst, .never_inline), .atomic_store_unordered => @panic("TODO try self.airAtomicStore(inst, .Unordered)"), .atomic_store_monotonic => @panic("TODO try self.airAtomicStore(inst, .Monotonic)"), @@ -627,7 +537,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .field_parent_ptr => @panic("TODO try self.airFieldParentPtr(inst)"), - .switch_br => @panic("TODO try self.airSwitch(inst)"), + .switch_br => try self.airSwitch(inst), .slice_ptr => @panic("TODO try self.airSlicePtr(inst)"), .slice_len => @panic("TODO try self.airSliceLen(inst)"), @@ -642,7 +552,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .constant => unreachable, // excluded from function bodies .const_ty => unreachable, // excluded from function bodies - .unreach => @panic("TODO self.finishAirBookkeeping()"), + .unreach => self.finishAirBookkeeping(), .optional_payload => @panic("TODO try self.airOptionalPayload(inst)"), .optional_payload_ptr => @panic("TODO try self.airOptionalPayloadPtr(inst)"), @@ -670,6 +580,212 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { } } +fn airAsm(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Asm, ty_pl.payload); + const is_volatile = (extra.data.flags & 0x80000000) != 0; + const clobbers_len = @truncate(u31, extra.data.flags); + var extra_i: usize = extra.end; + const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); + extra_i += outputs.len; + const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); + extra_i += inputs.len; + + const dead = !is_volatile and self.liveness.isUnused(inst); + _ = dead; + _ = clobbers_len; + + return self.fail("TODO implement asm for {}", .{self.target.cpu.arch}); +} + +fn airArg(self: *Self, inst: Air.Inst.Index) !void { + const arg_index = self.arg_index; + self.arg_index += 1; + + const ty = self.air.typeOfIndex(inst); + _ = ty; + + const result = self.args[arg_index]; + // TODO support stack-only arguments + // TODO Copy registers to the stack + const mcv = result; + + _ = try self.addInst(.{ + .tag = .dbg_arg, + .data = .{ + .dbg_arg_info = .{ + .air_inst = inst, + .arg_index = arg_index, + }, + }, + }); + + if (self.liveness.isUnused(inst)) + return self.finishAirBookkeeping(); + + switch (mcv) { + .register => |reg| { + self.register_manager.getRegAssumeFree(reg, inst); + }, + else => {}, + } + + return self.finishAir(inst, mcv, .{ .none, .none, .none }); +} + +fn airBlock(self: *Self, inst: Air.Inst.Index) !void { + try self.blocks.putNoClobber(self.gpa, inst, .{ + // A block is a setup to be able to jump to the end. + .relocs = .{}, + // It also acts as a receptacle for break operands. + // Here we use `MCValue.none` to represent a null value so that the first + // break instruction will choose a MCValue for the block result and overwrite + // this field. Following break instructions will use that MCValue to put their + // block results. + .mcv = MCValue{ .none = {} }, + }); + defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa); + + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Block, ty_pl.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + try self.genBody(body); + + // relocations for `bpcc` instructions + const relocs = &self.blocks.getPtr(inst).?.relocs; + if (relocs.items.len > 0 and relocs.items[relocs.items.len - 1] == self.mir_instructions.len - 1) { + // If the last Mir instruction is the last relocation (which + // would just jump one instruction further), it can be safely + // removed + self.mir_instructions.orderedRemove(relocs.pop()); + } + for (relocs.items) |reloc| { + try self.performReloc(reloc); + } + + const result = self.blocks.getPtr(inst).?.mcv; + return self.finishAir(inst, result, .{ .none, .none, .none }); +} + +fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) !void { + if (modifier == .always_tail) return self.fail("TODO implement tail calls for {}", .{self.target.cpu.arch}); + + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const callee = pl_op.operand; + const extra = self.air.extraData(Air.Call, pl_op.payload); + const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end .. extra.end + extra.data.args_len]); + const ty = self.air.typeOf(callee); + const fn_ty = switch (ty.zigTypeTag()) { + .Fn => ty, + .Pointer => ty.childType(), + else => unreachable, + }; + + var info = try self.resolveCallingConventionValues(fn_ty, .caller); + defer info.deinit(self); + for (info.args) |mc_arg, arg_i| { + const arg = args[arg_i]; + const arg_ty = self.air.typeOf(arg); + const arg_mcv = try self.resolveInst(arg); + + switch (mc_arg) { + .none => continue, + .undef => unreachable, + .immediate => unreachable, + .unreach => unreachable, + .dead => unreachable, + .memory => unreachable, + .compare_flags_signed => unreachable, + .compare_flags_unsigned => unreachable, + .got_load => unreachable, + .direct_load => unreachable, + .register => |reg| { + try self.register_manager.getReg(reg, null); + try self.genSetReg(arg_ty, reg, arg_mcv); + }, + .stack_offset => { + return self.fail("TODO implement calling with parameters in memory", .{}); + }, + .ptr_stack_offset => { + return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); + }, + } + } + + return self.fail("TODO implement call for {}", .{self.target.cpu.arch}); +} + +fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { + // TODO emit debug info lexical block + return self.finishAir(inst, .dead, .{ .none, .none, .none }); +} + +fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const function = self.air.values[ty_pl.payload].castTag(.function).?.data; + // TODO emit debug info for function change + _ = function; + return self.finishAir(inst, .dead, .{ .none, .none, .none }); +} + +fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { + const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; + + _ = try self.addInst(.{ + .tag = .dbg_line, + .data = .{ + .dbg_line_column = .{ + .line = dbg_stmt.line, + .column = dbg_stmt.column, + }, + }, + }); + + return self.finishAirBookkeeping(); +} + +fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const name = self.air.nullTerminatedString(pl_op.payload); + const operand = pl_op.operand; + // TODO emit debug info for this variable + _ = name; + return self.finishAir(inst, .dead, .{ operand, .none, .none }); +} + +fn airDiv(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 div for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +} + +fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { + _ = inst; + return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch}); + //return self.finishAir(inst, .dead, .{ un_op, .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 airStore(self: *Self, inst: Air.Inst.Index) !void { + _ = self; + _ = inst; + + return self.fail("TODO implement store for {}", .{self.target.cpu.arch}); +} + +fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { + _ = self; + _ = inst; + + return self.fail("TODO implement switch for {}", .{self.target.cpu.arch}); +} + +// Common helper functions + fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { const gpa = self.gpa; @@ -680,6 +796,42 @@ fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { return result_index; } +fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 { + if (abi_align > self.stack_align) + self.stack_align = abi_align; + // TODO find a free slot instead of always appending + const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align); + self.next_stack_offset = offset + abi_size; + if (self.next_stack_offset > self.max_end_stack) + self.max_end_stack = self.next_stack_offset; + try self.stack.putNoClobber(self.gpa, offset, .{ + .inst = inst, + .size = abi_size, + }); + return offset; +} + +/// Use a pointer instruction as the basis for allocating stack memory. +fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { + const elem_ty = self.air.typeOfIndex(inst).elemType(); + + if (!elem_ty.hasRuntimeBits()) { + // As this stack item will never be dereferenced at runtime, + // return the stack offset 0. Stack offset 0 will be where all + // zero-sized stack allocations live as non-zero-sized + // allocations will always have an offset > 0. + return @as(u32, 0); + } + + const target = self.target.*; + const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); + }; + // TODO swap this for inst.ty.ptrAlign + const abi_align = elem_ty.abiAlignment(self.target.*); + return self.allocMem(inst, abi_size, abi_align); +} + fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table; try table.ensureUnusedCapacity(self.gpa, additional_count); @@ -691,3 +843,361 @@ fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); return error.CodegenFail; } + +/// Called when there are no operands, and the instruction is always unreferenced. +fn finishAirBookkeeping(self: *Self) void { + if (std.debug.runtime_safety) { + self.air_bookkeeping += 1; + } +} + +fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void { + var tomb_bits = self.liveness.getTombBits(inst); + for (operands) |op| { + const dies = @truncate(u1, tomb_bits) != 0; + tomb_bits >>= 1; + if (!dies) continue; + const op_int = @enumToInt(op); + if (op_int < Air.Inst.Ref.typed_value_map.len) continue; + const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); + self.processDeath(op_index); + } + const is_used = @truncate(u1, tomb_bits) == 0; + if (is_used) { + log.debug("%{d} => {}", .{ inst, result }); + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; + branch.inst_table.putAssumeCapacityNoClobber(inst, result); + + switch (result) { + .register => |reg| { + // In some cases (such as bitcast), an operand + // may be the same MCValue as the result. If + // that operand died and was a register, it + // was freed by processDeath. We have to + // "re-allocate" the register. + if (self.register_manager.isRegFree(reg)) { + self.register_manager.getRegAssumeFree(reg, inst); + } + }, + else => {}, + } + } + self.finishAirBookkeeping(); +} + +fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { + if (typed_value.val.isUndef()) + return MCValue{ .undef = {} }; + + if (typed_value.val.castTag(.decl_ref)) |payload| { + return self.lowerDeclRef(typed_value, payload.data); + } + if (typed_value.val.castTag(.decl_ref_mut)) |payload| { + return self.lowerDeclRef(typed_value, payload.data.decl); + } + const target = self.target.*; + + switch (typed_value.ty.zigTypeTag()) { + .Pointer => switch (typed_value.ty.ptrSize()) { + .Slice => { + return self.lowerUnnamedConst(typed_value); + }, + else => { + switch (typed_value.val.tag()) { + .int_u64 => { + return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; + }, + .slice => { + return self.lowerUnnamedConst(typed_value); + }, + else => { + return self.fail("TODO codegen more kinds of const pointers: {}", .{typed_value.val.tag()}); + }, + } + }, + }, + .Int => { + const info = typed_value.ty.intInfo(self.target.*); + if (info.bits <= 64) { + const unsigned = switch (info.signedness) { + .signed => blk: { + const signed = typed_value.val.toSignedInt(); + break :blk @bitCast(u64, signed); + }, + .unsigned => typed_value.val.toUnsignedInt(target), + }; + + return MCValue{ .immediate = unsigned }; + } else { + return self.lowerUnnamedConst(typed_value); + } + }, + .Bool => { + return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; + }, + .ComptimeInt => unreachable, // semantic analysis prevents this + .ComptimeFloat => unreachable, // semantic analysis prevents this + .Optional => { + if (typed_value.ty.isPtrLikeOptional()) { + if (typed_value.val.isNull()) + return MCValue{ .immediate = 0 }; + + var buf: Type.Payload.ElemType = undefined; + return self.genTypedValue(.{ + .ty = typed_value.ty.optionalChild(&buf), + .val = typed_value.val, + }); + } else if (typed_value.ty.abiSize(self.target.*) == 1) { + return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) }; + } + return self.fail("TODO non pointer optionals", .{}); + }, + .Enum => { + if (typed_value.val.castTag(.enum_field_index)) |field_index| { + switch (typed_value.ty.tag()) { + .enum_simple => { + return MCValue{ .immediate = field_index.data }; + }, + .enum_full, .enum_nonexhaustive => { + const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data; + if (enum_full.values.count() != 0) { + const tag_val = enum_full.values.keys()[field_index.data]; + return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val }); + } else { + return MCValue{ .immediate = field_index.data }; + } + }, + else => unreachable, + } + } else { + var int_tag_buffer: Type.Payload.Bits = undefined; + const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer); + return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val }); + } + }, + .ErrorSet => { + const err_name = typed_value.val.castTag(.@"error").?.data.name; + const module = self.bin_file.options.module.?; + const global_error_set = module.global_error_set; + const error_index = global_error_set.get(err_name).?; + return MCValue{ .immediate = error_index }; + }, + .ErrorUnion => { + const error_type = typed_value.ty.errorUnionSet(); + const payload_type = typed_value.ty.errorUnionPayload(); + + if (typed_value.val.castTag(.eu_payload)) |pl| { + if (!payload_type.hasRuntimeBits()) { + // We use the error type directly as the type. + return MCValue{ .immediate = 0 }; + } + + _ = pl; + return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty.fmtDebug()}); + } else { + if (!payload_type.hasRuntimeBits()) { + // We use the error type directly as the type. + return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); + } + + return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty.fmtDebug()}); + } + }, + .Struct => { + return self.lowerUnnamedConst(typed_value); + }, + else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}), + } +} + +fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { + // Treat each stack item as a "layer" on top of the previous one. + var i: usize = self.branch_stack.items.len; + while (true) { + i -= 1; + if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| { + assert(mcv != .dead); + return mcv; + } + } +} + +fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { + const tag = self.mir_instructions.items(.tag)[inst]; + switch (tag) { + .bpcc => self.mir_instructions.items(.data)[inst].branch_predict.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len), + else => unreachable, + } +} + +/// Asserts there is already capacity to insert into top branch inst_table. +fn processDeath(self: *Self, inst: Air.Inst.Index) void { + const air_tags = self.air.instructions.items(.tag); + if (air_tags[inst] == .constant) return; // Constants are immortal. + // When editing this function, note that the logic must synchronize with `reuseOperand`. + const prev_value = self.getResolvedInstValue(inst); + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; + branch.inst_table.putAssumeCapacity(inst, .dead); + switch (prev_value) { + .register => |reg| { + self.register_manager.freeReg(reg); + }, + else => {}, // TODO process stack allocation death + } +} + +/// Caller must call `CallMCValues.deinit`. +fn resolveCallingConventionValues(self: *Self, fn_ty: Type, role: RegisterView) !CallMCValues { + const cc = fn_ty.fnCallingConvention(); + const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen()); + defer self.gpa.free(param_types); + fn_ty.fnParamTypes(param_types); + var result: CallMCValues = .{ + .args = try self.gpa.alloc(MCValue, param_types.len), + // These undefined values must be populated before returning from this function. + .return_value = undefined, + .stack_byte_count = undefined, + .stack_align = undefined, + }; + errdefer self.gpa.free(result.args); + + const ret_ty = fn_ty.fnReturnType(); + + switch (cc) { + .Naked => { + assert(result.args.len == 0); + result.return_value = .{ .unreach = {} }; + result.stack_byte_count = 0; + result.stack_align = 1; + return result; + }, + .Unspecified, .C => { + // SPARC Compliance Definition 2.4.1, Chapter 3 + // Low-Level System Information (64-bit psABI) - Function Calling Sequence + + var next_register: usize = 0; + var next_stack_offset: u32 = 0; + + // The caller puts the argument in %o0-%o5, which becomes %i0-%i5 inside the callee. + const argument_registers = switch (role) { + .caller => abi.c_abi_int_param_regs_caller_view, + .callee => abi.c_abi_int_param_regs_callee_view, + }; + + for (param_types) |ty, i| { + const param_size = @intCast(u32, ty.abiSize(self.target.*)); + if (param_size <= 8) { + if (next_register < argument_registers.len) { + result.args[i] = .{ .register = argument_registers[next_register] }; + next_register += 1; + } else { + result.args[i] = .{ .stack_offset = next_stack_offset }; + next_register += next_stack_offset; + } + } else if (param_size <= 16) { + if (next_register < argument_registers.len - 1) { + return self.fail("TODO MCValues with 2 registers", .{}); + } else if (next_register < argument_registers.len) { + return self.fail("TODO MCValues split register + stack", .{}); + } else { + result.args[i] = .{ .stack_offset = next_stack_offset }; + next_register += next_stack_offset; + } + } else { + result.args[i] = .{ .stack_offset = next_stack_offset }; + next_register += next_stack_offset; + } + } + + result.stack_byte_count = next_stack_offset; + result.stack_align = 16; + + if (ret_ty.zigTypeTag() == .NoReturn) { + result.return_value = .{ .unreach = {} }; + } else if (!ret_ty.hasRuntimeBits()) { + result.return_value = .{ .none = {} }; + } else { + const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); + // The callee puts the return values in %i0-%i3, which becomes %o0-%o3 inside the caller. + if (ret_ty_size <= 8) { + result.return_value = switch (role) { + .caller => .{ .register = abi.c_abi_int_return_regs_caller_view[0] }, + .callee => .{ .register = abi.c_abi_int_return_regs_callee_view[0] }, + }; + } else { + return self.fail("TODO support more return values for sparcv9", .{}); + } + } + }, + else => return self.fail("TODO implement function parameters for {} on sparcv9", .{cc}), + } + + return result; +} + +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); + if (ref_int < Air.Inst.Ref.typed_value_map.len) { + const tv = Air.Inst.Ref.typed_value_map[ref_int]; + if (!tv.ty.hasRuntimeBits()) { + return MCValue{ .none = {} }; + } + return self.genTypedValue(tv); + } + + // If the type has no codegen bits, no need to store it. + const inst_ty = self.air.typeOf(inst); + if (!inst_ty.hasRuntimeBits()) + return MCValue{ .none = {} }; + + const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len); + switch (self.air.instructions.items(.tag)[inst_index]) { + .constant => { + // Constants have static lifetimes, so they are always memoized in the outer most table. + const branch = &self.branch_stack.items[0]; + const gop = try branch.inst_table.getOrPut(self.gpa, inst_index); + if (!gop.found_existing) { + const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl; + gop.value_ptr.* = try self.genTypedValue(.{ + .ty = inst_ty, + .val = self.air.values[ty_pl.payload], + }); + } + return gop.value_ptr.*; + }, + .const_ty => unreachable, + else => return self.getResolvedInstValue(inst_index), + } +} + +fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { + if (!self.liveness.operandDies(inst, op_index)) + return false; + + switch (mcv) { + .register => |reg| { + // If it's in the registers table, need to associate the register with the + // new instruction. + if (RegisterManager.indexOfRegIntoTracked(reg)) |index| { + if (!self.register_manager.isRegFree(reg)) { + self.register_manager.registers[index] = inst; + } + } + log.debug("%{d} => {} (reused)", .{ inst, reg }); + }, + .stack_offset => |off| { + log.debug("%{d} => stack offset {d} (reused)", .{ inst, off }); + }, + else => return false, + } + + // Prevent the operand deaths processing code from deallocating it. + self.liveness.clearOperandDeath(inst, op_index); + + // That makes us responsible for doing the rest of the stuff that processDeath would have done. + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; + branch.inst_table.putAssumeCapacity(Air.refToIndex(operand).?, .dead); + + return true; +} diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index 8d870e43f5..2192b21c10 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -42,16 +42,23 @@ pub fn emitMir( for (mir_tags) |tag, index| { const inst = @intCast(u32, index); switch (tag) { + .dbg_arg => try emit.mirDbgArg(inst), .dbg_line => try emit.mirDbgLine(inst), .dbg_prologue_end => try emit.mirDebugPrologueEnd(), .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(), - .nop => @panic("TODO implement nop"), + .bpcc => @panic("TODO implement sparcv9 bpcc"), - .save => @panic("TODO implement save"), - .restore => @panic("TODO implement restore"), + .call => @panic("TODO implement sparcv9 call"), - .@"return" => @panic("TODO implement return"), + .jmpl => @panic("TODO implement sparcv9 jmpl"), + + .nop => @panic("TODO implement sparcv9 nop"), + + .@"return" => @panic("TODO implement sparcv9 return"), + + .save => @panic("TODO implement sparcv9 save"), + .restore => @panic("TODO implement sparcv9 restore"), } } } @@ -111,6 +118,17 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { } } +fn mirDbgArg(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const dbg_arg_info = emit.mir.instructions.items(.data)[inst].dbg_arg_info; + _ = dbg_arg_info; + + switch (tag) { + .dbg_arg => {}, // TODO try emit.genArgDbgInfo(dbg_arg_info.air_inst, dbg_arg_info.arg_index), + else => unreachable, + } +} + fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column; diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index 0b7c2185eb..21c6224930 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -7,9 +7,14 @@ //! so that, for example, the smaller encodings of jump instructions can be used. const std = @import("std"); +const builtin = @import("builtin"); +const assert = std.debug.assert; const Mir = @This(); const bits = @import("bits.zig"); +const Air = @import("../../Air.zig"); + +const Instruction = bits.Instruction; const Register = bits.Register; instructions: std.MultiArrayList(Inst).Slice, @@ -23,6 +28,8 @@ pub const Inst = struct { data: Data, pub const Tag = enum(u16) { + /// Pseudo-instruction: Argument + dbg_arg, /// Pseudo-instruction: End of prologue dbg_prologue_end, /// Pseudo-instruction: Beginning of epilogue @@ -33,6 +40,18 @@ pub const Inst = struct { // All the real instructions are ordered by their section number // in The SPARC Architecture Manual, Version 9. + /// A.7 Branch on Integer Condition Codes with Prediction (BPcc) + /// It uses the branch_predict field. + bpcc, + + /// A.8 Call and Link + /// It uses the branch_link field. + call, + + /// A.24 Jump and Link + /// It uses the branch_link field. + jmpl, + /// A.40 No Operation /// It uses the nop field. nop, @@ -50,28 +69,24 @@ pub const Inst = struct { /// The position of an MIR instruction within the `Mir` instructions array. pub const Index = u32; - /// All instructions have a 4-byte payload, which is contained within + /// All instructions have a 8-byte payload, which is contained within /// this union. `Tag` determines which union field is active, as well as /// how to interpret the data within. pub const Data = union { - /// No additional data + /// Debug info: argument /// - /// Used by e.g. flushw - nop: void, + /// Used by e.g. dbg_arg + dbg_arg_info: struct { + air_inst: Air.Inst.Index, + arg_index: usize, + }, - /// Three operand arithmetic. - /// if is_imm true then it uses the imm field of rs2_or_imm, - /// otherwise it uses rs2 field. + /// Debug info: line and column /// - /// Used by e.g. add, sub - arithmetic_3op: struct { - is_imm: bool, - rd: Register, - rs1: Register, - rs2_or_imm: union { - rs2: Register, - imm: i13, - }, + /// Used by e.g. dbg_line + dbg_line_column: struct { + line: u32, + column: u32, }, /// Two operand arithmetic. @@ -88,13 +103,42 @@ pub const Inst = struct { }, }, - /// Debug info: line and column + /// Three operand arithmetic. + /// if is_imm true then it uses the imm field of rs2_or_imm, + /// otherwise it uses rs2 field. /// - /// Used by e.g. dbg_line - dbg_line_column: struct { - line: u32, - column: u32, + /// Used by e.g. add, sub + arithmetic_3op: struct { + is_imm: bool, + rd: Register, + rs1: Register, + rs2_or_imm: union { + rs2: Register, + imm: i13, + }, }, + + /// Branch and link (always unconditional). + /// Used by e.g. call + branch_link: struct { + inst: Index, + link: Register, + }, + + /// Branch with prediction. + /// Used by e.g. bpcc + branch_predict: struct { + annul: bool, + pt: bool, + ccr: Instruction.CCR, + cond: Instruction.Condition, + inst: Index, + }, + + /// No additional data + /// + /// Used by e.g. flushw + nop: void, }; }; From 42f4bd34216ae1ae03df0a56502919109e030136 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 4 Apr 2022 21:28:55 +0700 Subject: [PATCH 1126/2031] stage2: sparcv9: Add breakpoint, ret, and calling mechanism --- src/arch/sparcv9/CodeGen.zig | 441 +++++++++++++++++++++++++++-------- src/arch/sparcv9/Emit.zig | 13 ++ src/arch/sparcv9/Mir.zig | 85 ++++++- 3 files changed, 436 insertions(+), 103 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 40d6db176f..7d0a178f9b 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -453,7 +453,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .bitcast => @panic("TODO try self.airBitCast(inst)"), .block => try self.airBlock(inst), .br => @panic("TODO try self.airBr(inst)"), - .breakpoint => @panic("TODO try self.airBreakpoint()"), + .breakpoint => try self.airBreakpoint(), .ret_addr => @panic("TODO try self.airRetAddr(inst)"), .frame_addr => @panic("TODO try self.airFrameAddress(inst)"), .fence => @panic("TODO try self.airFence()"), @@ -476,7 +476,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .loop => @panic("TODO try self.airLoop(inst)"), .not => @panic("TODO try self.airNot(inst)"), .ptrtoint => @panic("TODO try self.airPtrToInt(inst)"), - .ret => @panic("TODO try self.airRet(inst)"), + .ret => try self.airRet(inst), .ret_load => try self.airRetLoad(inst), .store => try self.airStore(inst), .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"), @@ -667,6 +667,21 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ .none, .none, .none }); } +fn airBreakpoint(self: *Self) !void { + // ta 0x01 + _ = try self.addInst(.{ + .tag = .tcc, + .data = .{ + .trap = .{ + .is_imm = true, + .cond = 0b1000, // TODO need to look into changing this into an enum + .rs2_or_imm = .{ .imm = 0x01 }, + }, + }, + }); + return self.finishAirBookkeeping(); +} + fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) !void { if (modifier == .always_tail) return self.fail("TODO implement tail calls for {}", .{self.target.cpu.arch}); @@ -695,10 +710,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .unreach => unreachable, .dead => unreachable, .memory => unreachable, - .compare_flags_signed => unreachable, - .compare_flags_unsigned => unreachable, - .got_load => unreachable, - .direct_load => unreachable, .register => |reg| { try self.register_manager.getReg(reg, null); try self.genSetReg(arg_ty, reg, arg_mcv); @@ -712,6 +723,44 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. } } + // Due to incremental compilation, how function calls are generated depends + // on linking. + if (self.air.value(callee)) |func_value| { + if (self.bin_file.tag == link.File.Elf.base_tag) { + if (func_value.castTag(.function)) |func_payload| { + const func = func_payload.data; + const ptr_bits = self.target.cpu.arch.ptrBitWidth(); + const ptr_bytes: u64 = @divExact(ptr_bits, 8); + const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { + const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; + break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes); + } else unreachable; + + try self.genSetReg(Type.initTag(.usize), .o7, .{ .memory = got_addr }); + + _ = try self.addInst(.{ + .tag = .jmpl, + .data = .{ .branch_link_indirect = .{ .reg = .o7 } }, + }); + } else if (func_value.castTag(.extern_fn)) |_| { + return self.fail("TODO implement calling extern functions", .{}); + } else { + return self.fail("TODO implement calling bitcasted functions", .{}); + } + } else @panic("TODO SPARCv9 currently does not support non-ELF binaries"); + } else { + assert(ty.zigTypeTag() == .Pointer); + const mcv = try self.resolveInst(callee); + try self.genSetReg(ty, .o7, mcv); + + _ = try self.addInst(.{ + .tag = .jmpl, + .data = .{ .branch_link_indirect = .{ .reg = .o7 } }, + }); + } + + // TODO handle return value + return self.fail("TODO implement call for {}", .{self.target.cpu.arch}); } @@ -759,8 +808,17 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airRet(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + try self.ret(operand); + return self.finishAir(inst, .dead, .{ un_op, .none, .none }); +} + fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; + 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 }); } @@ -832,6 +890,37 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { return self.allocMem(inst, abi_size, abi_align); } +fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { + const elem_ty = self.air.typeOfIndex(inst); + const target = self.target.*; + const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); + }; + const abi_align = elem_ty.abiAlignment(self.target.*); + if (abi_align > self.stack_align) + self.stack_align = abi_align; + + if (reg_ok) { + // Make sure the type can fit in a register before we try to allocate one. + if (abi_size <= 8) { + if (self.register_manager.tryAllocReg(inst)) |reg| { + return MCValue{ .register = reg }; + } + } + } + const stack_offset = try self.allocMem(inst, abi_size, abi_align); + return MCValue{ .stack_offset = stack_offset }; +} + +/// Copies a value to a register without tracking the register. The register is not considered +/// allocated. A second call to `copyToTmpRegister` may return the same register. +/// This can have a side effect of spilling instructions to the stack to free up a register. +fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { + const reg = try self.register_manager.allocReg(null); + try self.genSetReg(ty, reg, mcv); + return reg; +} + fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table; try table.ensureUnusedCapacity(self.gpa, additional_count); @@ -885,37 +974,216 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live self.finishAirBookkeeping(); } +fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, off: i13, abi_size: u64) !void { + _ = value_reg; + _ = addr_reg; + _ = off; + + switch (abi_size) { + 1, 2, 4, 8 => return self.fail("TODO: A.27 Load Integer", .{}), + 3, 5, 6, 7 => return self.fail("TODO: genLoad for more abi_sizes", .{}), + else => unreachable, + } +} + +fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { + switch (mcv) { + .dead => unreachable, + .unreach, .none => return, // Nothing to do. + .undef => { + if (!self.wantSafety()) + return; // The already existing value will do just fine. + // Write the debug undefined value. + return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); + }, + .ptr_stack_offset => |off| { + const simm13 = math.cast(u12, off) catch + return self.fail("TODO larger stack offsets", .{}); + + _ = try self.addInst(.{ + .tag = .add, + .data = .{ + .arithmetic_3op = .{ + .is_imm = true, + .rd = reg, + .rs1 = .sp, + .rs2_or_imm = .{ .imm = simm13 }, + }, + }, + }); + }, + .immediate => |x| { + if (x <= math.maxInt(u12)) { + _ = try self.addInst(.{ + .tag = .@"or", + .data = .{ + .arithmetic_3op = .{ + .is_imm = true, + .rd = reg, + .rs1 = .g0, + .rs2_or_imm = .{ .imm = @truncate(u12, x) }, + }, + }, + }); + } else if (x <= math.maxInt(u32)) { + _ = try self.addInst(.{ + .tag = .sethi, + .data = .{ + .sethi = .{ + .rd = reg, + .imm = @truncate(u22, x >> 10), + }, + }, + }); + + _ = try self.addInst(.{ + .tag = .@"or", + .data = .{ + .arithmetic_3op = .{ + .is_imm = true, + .rd = reg, + .rs1 = reg, + .rs2_or_imm = .{ .imm = @truncate(u10, x) }, + }, + }, + }); + } else if (x <= math.maxInt(u44)) { + try self.genSetReg(ty, reg, .{ .immediate = @truncate(u32, x >> 12) }); + + _ = try self.addInst(.{ + .tag = .sllx, + .data = .{ + .shift = .{ + .is_imm = true, + .width = .shift64, + .rd = reg, + .rs1 = reg, + .rs2_or_imm = .{ .imm = 12 }, + }, + }, + }); + + _ = try self.addInst(.{ + .tag = .@"or", + .data = .{ + .arithmetic_3op = .{ + .is_imm = true, + .rd = reg, + .rs1 = reg, + .rs2_or_imm = .{ .imm = @truncate(u12, x) }, + }, + }, + }); + } else { + // Need to allocate a temporary register to load 64-bit immediates. + const tmp_reg = try self.register_manager.allocReg(null); + + try self.genSetReg(ty, tmp_reg, .{ .immediate = @truncate(u32, x) }); + try self.genSetReg(ty, reg, .{ .immediate = @truncate(u32, x >> 32) }); + + _ = try self.addInst(.{ + .tag = .sllx, + .data = .{ + .shift = .{ + .is_imm = true, + .width = .shift64, + .rd = reg, + .rs1 = reg, + .rs2_or_imm = .{ .imm = 32 }, + }, + }, + }); + + _ = try self.addInst(.{ + .tag = .@"or", + .data = .{ + .arithmetic_3op = .{ + .is_imm = false, + .rd = reg, + .rs1 = reg, + .rs2_or_imm = .{ .rs2 = tmp_reg }, + }, + }, + }); + } + }, + .register => |src_reg| { + // If the registers are the same, nothing to do. + if (src_reg.id() == reg.id()) + return; + + // or %g0, src, dst (aka mov src, dst) + _ = try self.addInst(.{ + .tag = .@"or", + .data = .{ + .arithmetic_3op = .{ + .is_imm = false, + .rd = reg, + .rs1 = .g0, + .rs2_or_imm = .{ .rs2 = src_reg }, + }, + }, + }); + }, + .memory => |addr| { + // The value is in memory at a hard-coded address. + // If the type is a pointer, it means the pointer address is at this memory location. + try self.genSetReg(ty, reg, .{ .immediate = addr }); + try self.genLoad(reg, reg, 0, ty.abiSize(self.target.*)); + }, + .stack_offset => |off| { + const simm13 = math.cast(u12, off) catch + return self.fail("TODO larger stack offsets", .{}); + try self.genLoad(reg, .sp, simm13, ty.abiSize(self.target.*)); + }, + } +} + +fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { + const abi_size = ty.abiSize(self.target.*); + switch (mcv) { + .dead => unreachable, + .unreach, .none => return, // Nothing to do. + .undef => { + if (!self.wantSafety()) + return; // The already existing value will do just fine. + // TODO Upgrade this to a memset call when we have that available. + switch (ty.abiSize(self.target.*)) { + 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }), + 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }), + 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }), + 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), + else => return self.fail("TODO implement memset", .{}), + } + }, + .immediate, + .ptr_stack_offset, + => { + const reg = try self.copyToTmpRegister(ty, mcv); + return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); + }, + .register => return self.fail("TODO implement storing types abi_size={}", .{abi_size}), + .memory, .stack_offset => return self.fail("TODO implement memcpy", .{}), + } +} + fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { if (typed_value.val.isUndef()) return MCValue{ .undef = {} }; if (typed_value.val.castTag(.decl_ref)) |payload| { - return self.lowerDeclRef(typed_value, payload.data); + _ = payload; + return self.fail("TODO implement lowerDeclRef", .{}); + // return self.lowerDeclRef(typed_value, payload.data); } if (typed_value.val.castTag(.decl_ref_mut)) |payload| { - return self.lowerDeclRef(typed_value, payload.data.decl); + _ = payload; + return self.fail("TODO implement lowerDeclRef", .{}); + // return self.lowerDeclRef(typed_value, payload.data.decl); } const target = self.target.*; switch (typed_value.ty.zigTypeTag()) { - .Pointer => switch (typed_value.ty.ptrSize()) { - .Slice => { - return self.lowerUnnamedConst(typed_value); - }, - else => { - switch (typed_value.val.tag()) { - .int_u64 => { - return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; - }, - .slice => { - return self.lowerUnnamedConst(typed_value); - }, - else => { - return self.fail("TODO codegen more kinds of const pointers: {}", .{typed_value.val.tag()}); - }, - } - }, - }, .Int => { const info = typed_value.ty.intInfo(self.target.*); if (info.bits <= 64) { @@ -929,83 +1197,11 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { return MCValue{ .immediate = unsigned }; } else { - return self.lowerUnnamedConst(typed_value); + return self.fail("TODO implement int genTypedValue of > 64 bits", .{}); } }, - .Bool => { - return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; - }, .ComptimeInt => unreachable, // semantic analysis prevents this .ComptimeFloat => unreachable, // semantic analysis prevents this - .Optional => { - if (typed_value.ty.isPtrLikeOptional()) { - if (typed_value.val.isNull()) - return MCValue{ .immediate = 0 }; - - var buf: Type.Payload.ElemType = undefined; - return self.genTypedValue(.{ - .ty = typed_value.ty.optionalChild(&buf), - .val = typed_value.val, - }); - } else if (typed_value.ty.abiSize(self.target.*) == 1) { - return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) }; - } - return self.fail("TODO non pointer optionals", .{}); - }, - .Enum => { - if (typed_value.val.castTag(.enum_field_index)) |field_index| { - switch (typed_value.ty.tag()) { - .enum_simple => { - return MCValue{ .immediate = field_index.data }; - }, - .enum_full, .enum_nonexhaustive => { - const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data; - if (enum_full.values.count() != 0) { - const tag_val = enum_full.values.keys()[field_index.data]; - return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val }); - } else { - return MCValue{ .immediate = field_index.data }; - } - }, - else => unreachable, - } - } else { - var int_tag_buffer: Type.Payload.Bits = undefined; - const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer); - return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val }); - } - }, - .ErrorSet => { - const err_name = typed_value.val.castTag(.@"error").?.data.name; - const module = self.bin_file.options.module.?; - const global_error_set = module.global_error_set; - const error_index = global_error_set.get(err_name).?; - return MCValue{ .immediate = error_index }; - }, - .ErrorUnion => { - const error_type = typed_value.ty.errorUnionSet(); - const payload_type = typed_value.ty.errorUnionPayload(); - - if (typed_value.val.castTag(.eu_payload)) |pl| { - if (!payload_type.hasRuntimeBits()) { - // We use the error type directly as the type. - return MCValue{ .immediate = 0 }; - } - - _ = pl; - return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty.fmtDebug()}); - } else { - if (!payload_type.hasRuntimeBits()) { - // We use the error type directly as the type. - return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); - } - - return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty.fmtDebug()}); - } - }, - .Struct => { - return self.lowerUnnamedConst(typed_value); - }, else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}), } } @@ -1171,6 +1367,18 @@ fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { } } +fn ret(self: *Self, mcv: MCValue) !void { + const ret_ty = self.fn_type.fnReturnType(); + try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); + + // Just add space for an instruction, patch this later + const index = try self.addInst(.{ + .tag = .nop, + .data = .{ .nop = {} }, + }); + try self.exitlude_jump_relocs.append(self.gpa, index); +} + fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { if (!self.liveness.operandDies(inst, op_index)) return false; @@ -1201,3 +1409,36 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind return true; } + +/// Sets the value without any modifications to register allocation metadata or stack allocation metadata. +fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { + switch (loc) { + .none => return, + .register => |reg| return self.genSetReg(ty, reg, val), + .stack_offset => |off| return self.genSetStack(ty, off, val), + .memory => { + return self.fail("TODO implement setRegOrMem for memory", .{}); + }, + else => unreachable, + } +} + +pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { + const stack_mcv = try self.allocRegOrMem(inst, false); + log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); + const reg_mcv = self.getResolvedInstValue(inst); + assert(reg == reg_mcv.register); + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; + try branch.inst_table.put(self.gpa, inst, stack_mcv); + try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); +} + +/// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`. +fn wantSafety(self: *Self) bool { + return switch (self.bin_file.options.optimize_mode) { + .Debug => true, + .ReleaseSafe => true, + .ReleaseFast => false, + .ReleaseSmall => false, + }; +} diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index 2192b21c10..4cb789b942 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -47,11 +47,16 @@ pub fn emitMir( .dbg_prologue_end => try emit.mirDebugPrologueEnd(), .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(), + .add => @panic("TODO implement sparcv9 add"), + .bpcc => @panic("TODO implement sparcv9 bpcc"), .call => @panic("TODO implement sparcv9 call"), .jmpl => @panic("TODO implement sparcv9 jmpl"), + .jmpl_i => @panic("TODO implement sparcv9 jmpl to reg"), + + .@"or" => @panic("TODO implement sparcv9 or"), .nop => @panic("TODO implement sparcv9 nop"), @@ -59,6 +64,14 @@ pub fn emitMir( .save => @panic("TODO implement sparcv9 save"), .restore => @panic("TODO implement sparcv9 restore"), + + .sethi => @panic("TODO implement sparcv9 sethi"), + + .sllx => @panic("TODO implement sparcv9 sllx"), + + .sub => @panic("TODO implement sparcv9 sub"), + + .tcc => @panic("TODO implement sparcv9 tcc"), } } } diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index 21c6224930..3ff675fc36 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -40,6 +40,11 @@ pub const Inst = struct { // All the real instructions are ordered by their section number // in The SPARC Architecture Manual, Version 9. + /// A.2 Add + /// Those uses the arithmetic_3op field. + // TODO add other operations. + add, + /// A.7 Branch on Integer Condition Codes with Prediction (BPcc) /// It uses the branch_predict field. bpcc, @@ -49,8 +54,16 @@ pub const Inst = struct { call, /// A.24 Jump and Link - /// It uses the branch_link field. + /// jmpl (far direct jump) uses the branch_link field, + /// while jmpl_i (indirect jump) uses the branch_link_indirect field. + /// Those two MIR instructions will be lowered into SPARCv9 jmpl instruction. jmpl, + jmpl_i, + + /// A.31 Logical Operations + /// Those uses the arithmetic_3op field. + // TODO add other operations. + @"or", /// A.40 No Operation /// It uses the nop field. @@ -64,6 +77,24 @@ pub const Inst = struct { /// Those uses the arithmetic_3op field. save, restore, + + /// A.48 SETHI + /// It uses the sethi field. + sethi, + + /// A.49 Shift + /// Those uses the shift field. + // TODO add other operations. + sllx, + + /// A.56 Subtract + /// Those uses the arithmetic_3op field. + // TODO add other operations. + sub, + + /// A.61 Trap on Integer Condition Codes (Tcc) + /// It uses the trap field. + tcc, }; /// The position of an MIR instruction within the `Mir` instructions array. @@ -72,6 +103,7 @@ pub const Inst = struct { /// All instructions have a 8-byte payload, which is contained within /// this union. `Tag` determines which union field is active, as well as /// how to interpret the data within. + // TODO this is a quick-n-dirty solution that needs to be cleaned up. pub const Data = union { /// Debug info: argument /// @@ -122,14 +154,21 @@ pub const Inst = struct { /// Used by e.g. call branch_link: struct { inst: Index, - link: Register, + link: Register = .o7, + }, + + /// Indirect branch and link (always unconditional). + /// Used by e.g. jmpl_i + branch_link_indirect: struct { + reg: Register, + link: Register = .o7, }, /// Branch with prediction. /// Used by e.g. bpcc branch_predict: struct { annul: bool, - pt: bool, + pt: bool = true, ccr: Instruction.CCR, cond: Instruction.Condition, inst: Index, @@ -139,6 +178,46 @@ pub const Inst = struct { /// /// Used by e.g. flushw nop: void, + + /// SETHI operands. + /// + /// Used by sethi + sethi: struct { + rd: Register, + imm: u22, + }, + + /// Shift operands. + /// if is_imm true then it uses the imm field of rs2_or_imm, + /// otherwise it uses rs2 field. + /// + /// Used by e.g. add, sub + shift: struct { + is_imm: bool, + width: Instruction.ShiftWidth, + rd: Register, + rs1: Register, + rs2_or_imm: union { + rs2: Register, + imm: u6, + }, + }, + + /// Trap. + /// if is_imm true then it uses the imm field of rs2_or_imm, + /// otherwise it uses rs2 field. + /// + /// Used by e.g. tcc + trap: struct { + is_imm: bool = true, + cond: Instruction.Condition, + ccr: Instruction.CCR = .icc, + rs1: Register = .g0, + rs2_or_imm: union { + rs2: Register, + imm: u8, + }, + }, }; }; From 5e2045cbe549c9020ebbbecc763699fc4a68c818 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 10 Apr 2022 11:06:29 +0700 Subject: [PATCH 1127/2031] stage2: sparcv9: Implement basic asm codegen --- src/arch/sparcv9/CodeGen.zig | 122 ++++++++++++++++++++++++++++++++--- 1 file changed, 113 insertions(+), 9 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 7d0a178f9b..7ff1473921 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -521,8 +521,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { => try self.airDbgBlock(inst), .call => try self.airCall(inst, .auto), - .call_always_tail => @panic("TODO try self.airCall(inst, .always_tail)"), - .call_never_tail => @panic("TODO try self.airCall(inst, .never_tail)"), + .call_always_tail => try self.airCall(inst, .always_tail), + .call_never_tail => try self.airCall(inst, .never_tail), .call_never_inline => try self.airCall(inst, .never_inline), .atomic_store_unordered => @panic("TODO try self.airAtomicStore(inst, .Unordered)"), @@ -586,16 +586,106 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const is_volatile = (extra.data.flags & 0x80000000) != 0; const clobbers_len = @truncate(u31, extra.data.flags); var extra_i: usize = extra.end; - const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); + const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..extra_i+extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); + const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..extra_i+extra.data.inputs_len]); extra_i += inputs.len; const dead = !is_volatile and self.liveness.isUnused(inst); - _ = dead; - _ = clobbers_len; + const result: MCValue = if (dead) .dead else result: { + if (outputs.len > 1) { + return self.fail("TODO implement codegen for asm with more than 1 output", .{}); + } - return self.fail("TODO implement asm for {}", .{self.target.cpu.arch}); + const output_constraint: ?[]const u8 = for (outputs) |output| { + if (output != .none) { + return self.fail("TODO implement codegen for non-expr asm", .{}); + } + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += constraint.len / 4 + 1; + + break constraint; + } else null; + + for (inputs) |input| { + const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += constraint.len / 4 + 1; + + if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { + return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); + } + const reg_name = constraint[1 .. constraint.len - 1]; + const reg = parseRegName(reg_name) orelse + return self.fail("unrecognized register: '{s}'", .{reg_name}); + + const arg_mcv = try self.resolveInst(input); + try self.register_manager.getReg(reg, null); + try self.genSetReg(self.air.typeOf(input), reg, arg_mcv); + } + + { + var clobber_i: u32 = 0; + while (clobber_i < clobbers_len) : (clobber_i += 1) { + const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += clobber.len / 4 + 1; + + // TODO honor these + } + } + + const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; + + if (mem.eql(u8, asm_source, "ta 0x6d")) { + _ = try self.addInst(.{ + .tag = .tcc, + .data = .{ + .trap = .{ + .is_imm = true, + .cond = 0b1000, // TODO need to look into changing this into an enum + .rs2_or_imm = .{ .imm = 0x6d }, + }, + }, + }); + } else { + return self.fail("TODO implement a full SPARCv9 assembly parsing", .{}); + } + + if (output_constraint) |output| { + if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { + return self.fail("unrecognized asm output constraint: '{s}'", .{output}); + } + const reg_name = output[2 .. output.len - 1]; + const reg = parseRegName(reg_name) orelse + return self.fail("unrecognized register: '{s}'", .{reg_name}); + break :result MCValue{ .register = reg }; + } else { + break :result MCValue{ .none = {} }; + } + }; + + simple: { + var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); + var buf_index: usize = 0; + for (outputs) |output| { + if (output == .none) continue; + + if (buf_index >= buf.len) break :simple; + buf[buf_index] = output; + buf_index += 1; + } + if (buf_index + inputs.len > buf.len) break :simple; + std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); + return self.finishAir(inst, result, buf); + } + + @panic("TODO implement asm return"); + //return self.fail("TODO implement asm return for {}", .{self.target.cpu.arch}); } fn airArg(self: *Self, inst: Air.Inst.Index) !void { @@ -759,9 +849,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. }); } - // TODO handle return value + const result = info.return_value; - return self.fail("TODO implement call for {}", .{self.target.cpu.arch}); + if (args.len + 1 <= Liveness.bpi - 1) { + var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); + buf[0] = callee; + std.mem.copy(Air.Inst.Ref, buf[1..], args); + return self.finishAir(inst, result, buf); + } + + @panic("TODO handle return value with BigTomb"); } fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { @@ -1218,6 +1315,13 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { } } +fn parseRegName(name: []const u8) ?Register { + if (@hasDecl(Register, "parseRegName")) { + return Register.parseRegName(name); + } + return std.meta.stringToEnum(Register, name); +} + fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { const tag = self.mir_instructions.items(.tag)[inst]; switch (tag) { From 7051970ad7e277d93c8bffee7221e3e8c45c8eab Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 10 Apr 2022 14:29:06 +0700 Subject: [PATCH 1128/2031] stage2: sparcv9: implement basic instruction lowering --- src/arch/sparcv9/CodeGen.zig | 4 +- src/arch/sparcv9/Emit.zig | 194 ++++++++++++++++++++++++++--------- src/arch/sparcv9/Mir.zig | 15 ++- src/arch/sparcv9/bits.zig | 75 +++++++++++++- 4 files changed, 231 insertions(+), 57 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 7ff1473921..193600804e 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -1270,12 +1270,12 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { if (typed_value.val.castTag(.decl_ref)) |payload| { _ = payload; - return self.fail("TODO implement lowerDeclRef", .{}); + return self.fail("TODO implement lowerDeclRef non-mut", .{}); // return self.lowerDeclRef(typed_value, payload.data); } if (typed_value.val.castTag(.decl_ref_mut)) |payload| { _ = payload; - return self.fail("TODO implement lowerDeclRef", .{}); + return self.fail("TODO implement lowerDeclRef mut", .{}); // return self.lowerDeclRef(typed_value, payload.data.decl); } const target = self.target.*; diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index 4cb789b942..7ff1aeb532 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -2,6 +2,7 @@ //! machine code const std = @import("std"); +const Endian = std.builtin.Endian; const assert = std.debug.assert; const link = @import("../../link.zig"); const Module = @import("../../Module.zig"); @@ -14,6 +15,8 @@ const leb128 = std.leb; const Emit = @This(); const Mir = @import("Mir.zig"); const bits = @import("bits.zig"); +const Instruction = bits.Instruction; +const Register = bits.Register; mir: Mir, bin_file: *link.File, @@ -47,7 +50,7 @@ pub fn emitMir( .dbg_prologue_end => try emit.mirDebugPrologueEnd(), .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(), - .add => @panic("TODO implement sparcv9 add"), + .add => try emit.mirArithmetic3Op(inst), .bpcc => @panic("TODO implement sparcv9 bpcc"), @@ -56,22 +59,22 @@ pub fn emitMir( .jmpl => @panic("TODO implement sparcv9 jmpl"), .jmpl_i => @panic("TODO implement sparcv9 jmpl to reg"), - .@"or" => @panic("TODO implement sparcv9 or"), + .@"or" => try emit.mirArithmetic3Op(inst), - .nop => @panic("TODO implement sparcv9 nop"), + .nop => try emit.mirNop(), - .@"return" => @panic("TODO implement sparcv9 return"), + .@"return" => try emit.mirArithmetic2Op(inst), - .save => @panic("TODO implement sparcv9 save"), - .restore => @panic("TODO implement sparcv9 restore"), + .save => try emit.mirArithmetic3Op(inst), + .restore => try emit.mirArithmetic3Op(inst), .sethi => @panic("TODO implement sparcv9 sethi"), .sllx => @panic("TODO implement sparcv9 sllx"), - .sub => @panic("TODO implement sparcv9 sub"), + .sub => try emit.mirArithmetic3Op(inst), - .tcc => @panic("TODO implement sparcv9 tcc"), + .tcc => try emit.mirTrap(inst), } } } @@ -80,6 +83,129 @@ pub fn deinit(emit: *Emit) void { emit.* = undefined; } +fn mirDbgArg(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const dbg_arg_info = emit.mir.instructions.items(.data)[inst].dbg_arg_info; + _ = dbg_arg_info; + + switch (tag) { + .dbg_arg => {}, // TODO try emit.genArgDbgInfo(dbg_arg_info.air_inst, dbg_arg_info.arg_index), + else => unreachable, + } +} + +fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column; + + switch (tag) { + .dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column), + else => unreachable, + } +} + +fn mirDebugPrologueEnd(self: *Emit) !void { + switch (self.debug_output) { + .dwarf => |dbg_out| { + try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); + try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); + }, + .plan9 => {}, + .none => {}, + } +} + +fn mirDebugEpilogueBegin(self: *Emit) !void { + switch (self.debug_output) { + .dwarf => |dbg_out| { + try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); + try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); + }, + .plan9 => {}, + .none => {}, + } +} + +fn mirArithmetic2Op(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const data = emit.mir.instructions.items(.data)[inst].arithmetic_2op; + + const rs1 = data.rs1; + + if (data.is_imm) { + const imm = data.rs2_or_imm.imm; + switch (tag) { + .@"return" => try emit.writeInstruction(Instruction.@"return"(i13, rs1, imm)), + else => unreachable, + } + } else { + const rs2 = data.rs2_or_imm.rs2; + switch (tag) { + .@"return" => try emit.writeInstruction(Instruction.@"return"(Register, rs1, rs2)), + else => unreachable, + } + } +} + +fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const data = emit.mir.instructions.items(.data)[inst].arithmetic_3op; + + const rd = data.rd; + const rs1 = data.rs1; + + if (data.is_imm) { + const imm = data.rs2_or_imm.imm; + switch (tag) { + .add => try emit.writeInstruction(Instruction.add(i13, rs1, imm, rd)), + .@"or" => try emit.writeInstruction(Instruction.@"or"(i13, rs1, imm, rd)), + .save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)), + .restore => try emit.writeInstruction(Instruction.restore(i13, rs1, imm, rd)), + .sub => try emit.writeInstruction(Instruction.sub(i13, rs1, imm, rd)), + else => unreachable, + } + } else { + const rs2 = data.rs2_or_imm.rs2; + switch (tag) { + .add => try emit.writeInstruction(Instruction.add(Register, rs1, rs2, rd)), + .@"or" => try emit.writeInstruction(Instruction.@"or"(Register, rs1, rs2, rd)), + .save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)), + .restore => try emit.writeInstruction(Instruction.restore(Register, rs1, rs2, rd)), + .sub => try emit.writeInstruction(Instruction.sub(Register, rs1, rs2, rd)), + else => unreachable, + } + } +} + +fn mirNop(emit: *Emit) !void { + try emit.writeInstruction(Instruction.nop()); +} + +fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const data = emit.mir.instructions.items(.data)[inst].trap; + + const cond = data.cond; + const ccr = data.ccr; + const rs1 = data.rs1; + + if (data.is_imm) { + const imm = data.rs2_or_imm.imm; + switch (tag) { + .tcc => try emit.writeInstruction(Instruction.trap(u7, cond, ccr, rs1, imm)), + else => unreachable, + } + } else { + const rs2 = data.rs2_or_imm.rs2; + switch (tag) { + .tcc => try emit.writeInstruction(Instruction.trap(Register, cond, ccr, rs1, rs2)), + else => unreachable, + } + } +} + +// Common helper functions + fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line); const delta_pc: usize = self.code.items.len - self.prev_di_pc; @@ -131,52 +257,18 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { } } -fn mirDbgArg(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const dbg_arg_info = emit.mir.instructions.items(.data)[inst].dbg_arg_info; - _ = dbg_arg_info; - - switch (tag) { - .dbg_arg => {}, // TODO try emit.genArgDbgInfo(dbg_arg_info.air_inst, dbg_arg_info.arg_index), - else => unreachable, - } -} - -fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column; - - switch (tag) { - .dbg_line => try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column), - else => unreachable, - } -} - -fn mirDebugPrologueEnd(self: *Emit) !void { - switch (self.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); - try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); - }, - .plan9 => {}, - .none => {}, - } -} - -fn mirDebugEpilogueBegin(self: *Emit) !void { - switch (self.debug_output) { - .dwarf => |dbg_out| { - try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); - try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); - }, - .plan9 => {}, - .none => {}, - } -} - fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { @setCold(true); assert(emit.err_msg == null); emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args); return error.EmitFail; } + +fn writeInstruction(emit: *Emit, instruction: Instruction) !void { + // SPARCv9 instructions are always arranged in BE regardless of the + // endianness mode the CPU is running in. + // This is to ease porting in case someone wants to do a LE SPARCv9 backend. + const endian = Endian.Big; + + std.mem.writeInt(u32, try emit.code.addManyAsArray(4), instruction.toU32(), endian); +} diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index 3ff675fc36..02974dfda3 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -167,7 +167,7 @@ pub const Inst = struct { /// Branch with prediction. /// Used by e.g. bpcc branch_predict: struct { - annul: bool, + annul: bool = false, pt: bool = true, ccr: Instruction.CCR, cond: Instruction.Condition, @@ -215,10 +215,21 @@ pub const Inst = struct { rs1: Register = .g0, rs2_or_imm: union { rs2: Register, - imm: u8, + imm: u7, }, }, }; + + // Make sure we don't accidentally make instructions bigger than expected. + // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks. + comptime { + if (builtin.mode != .Debug) { + // TODO clean up the definition of Data before enabling this. + // I'll do that after the PoC backend can produce usable binaries. + + // assert(@sizeOf(Data) == 8); + } + } }; pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { diff --git a/src/arch/sparcv9/bits.zig b/src/arch/sparcv9/bits.zig index 07cbf7fc91..952ddef4a9 100644 --- a/src/arch/sparcv9/bits.zig +++ b/src/arch/sparcv9/bits.zig @@ -546,6 +546,9 @@ pub const Instruction = union(enum) { }; } + // SPARCv9 Instruction formats. + // See section 6.2 of the SPARCv9 ISA manual. + fn format1(disp: i32) Instruction { const udisp = @bitCast(u32, disp); @@ -561,7 +564,7 @@ pub const Instruction = union(enum) { }; } - fn format2a(op2: u3, rd: Register, imm: u22) Instruction { + fn format2a(op2: u3, imm: u22, rd: Register) Instruction { return Instruction{ .format_2a = .{ .rd = rd.enc(), @@ -956,6 +959,74 @@ pub const Instruction = union(enum) { }, }; } + + // SPARCv9 Instruction definition. + // See appendix A of the SPARCv9 ISA manual. + + pub fn add(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch(s2) { + Register => format3a(0b10, 0b00_0000, rs1, rs2, rd), + i13 => format3b(0b10, 0b00_0000, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn @"or"(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch(s2) { + Register => format3a(0b10, 0b00_0010, rs1, rs2, rd), + i13 => format3b(0b10, 0b00_0010, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn nop() Instruction { + return sethi(0, .g0); + } + + pub fn @"return"(comptime s2: type, rs1: Register, rs2: s2) Instruction { + return switch(s2) { + Register => format3c(0b10, 0b11_1001, rs1, rs2), + i13 => format3d(0b10, 0b11_1001, rs1, rs2), + else => unreachable, + }; + } + + pub fn save(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch(s2) { + Register => format3a(0b10, 0b11_1100, rs1, rs2, rd), + i13 => format3b(0b10, 0b11_1100, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn restore(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch(s2) { + Register => format3a(0b10, 0b11_1101, rs1, rs2, rd), + i13 => format3b(0b10, 0b11_1101, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn sethi(imm: u22, rd: Register) Instruction { + return format2a(0b100, imm, rd); + } + + pub fn sub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch(s2) { + Register => format3a(0b10, 0b00_0100, rs1, rs2, rd), + i13 => format3b(0b10, 0b00_0100, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn trap(comptime s2: type, cond: Condition, ccr: CCR, rs1: Register, rs2: s2) Instruction { + // Tcc instructions abuse the rd field to store the conditionals. + return switch(s2) { + Register => format4a(0b11_1010, ccr, rs1, rs2, @intToEnum(Register, cond)), + u7 => format4e(0b00_0100, ccr, rs1, @intToEnum(Register, cond), rs2), + else => unreachable, + }; + } }; test "Serialize formats" { @@ -973,7 +1044,7 @@ test "Serialize formats" { .expected = 0b01_000000000000000000000000000001, }, .{ - .inst = Instruction.format2a(4, .g0, 0), + .inst = Instruction.format2a(4, 0, .g0), .expected = 0b00_00000_100_0000000000000000000000, }, .{ From ab2ea9fb09df6821a7b6cae7990b9bc5be7c1f61 Mon Sep 17 00:00:00 2001 From: Flandre Scarlet Date: Sun, 10 Apr 2022 19:03:37 +0700 Subject: [PATCH 1129/2031] stage2: sparcv9: Test failure error logging --- src/arch/sparcv9/bits.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/arch/sparcv9/bits.zig b/src/arch/sparcv9/bits.zig index 952ddef4a9..c472fc6b09 100644 --- a/src/arch/sparcv9/bits.zig +++ b/src/arch/sparcv9/bits.zig @@ -1167,6 +1167,10 @@ test "Serialize formats" { for (testcases) |case| { const actual = case.inst.toU32(); - try testing.expectEqual(case.expected, actual); + testing.expectEqual(case.expected, actual) catch |err| { + std.debug.print("error: {x}\n", .{err}); + std.debug.print("case: {x}\n", .{case}); + return err; + }; } } From cfd389f927112cbc81e71118493a7b9fba18192d Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 10 Apr 2022 19:04:16 +0700 Subject: [PATCH 1130/2031] stage2: sparcv9: zig fmt --- src/arch/sparcv9/CodeGen.zig | 4 ++-- src/arch/sparcv9/bits.zig | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 193600804e..635d7bb8f2 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -586,9 +586,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const is_volatile = (extra.data.flags & 0x80000000) != 0; const clobbers_len = @truncate(u31, extra.data.flags); var extra_i: usize = extra.end; - const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..extra_i+extra.data.outputs_len]); + const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i .. extra_i + extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..extra_i+extra.data.inputs_len]); + const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i .. extra_i + extra.data.inputs_len]); extra_i += inputs.len; const dead = !is_volatile and self.liveness.isUnused(inst); diff --git a/src/arch/sparcv9/bits.zig b/src/arch/sparcv9/bits.zig index c472fc6b09..83c560e584 100644 --- a/src/arch/sparcv9/bits.zig +++ b/src/arch/sparcv9/bits.zig @@ -964,7 +964,7 @@ pub const Instruction = union(enum) { // See appendix A of the SPARCv9 ISA manual. pub fn add(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { - return switch(s2) { + return switch (s2) { Register => format3a(0b10, 0b00_0000, rs1, rs2, rd), i13 => format3b(0b10, 0b00_0000, rs1, rs2, rd), else => unreachable, @@ -972,7 +972,7 @@ pub const Instruction = union(enum) { } pub fn @"or"(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { - return switch(s2) { + return switch (s2) { Register => format3a(0b10, 0b00_0010, rs1, rs2, rd), i13 => format3b(0b10, 0b00_0010, rs1, rs2, rd), else => unreachable, @@ -984,7 +984,7 @@ pub const Instruction = union(enum) { } pub fn @"return"(comptime s2: type, rs1: Register, rs2: s2) Instruction { - return switch(s2) { + return switch (s2) { Register => format3c(0b10, 0b11_1001, rs1, rs2), i13 => format3d(0b10, 0b11_1001, rs1, rs2), else => unreachable, @@ -992,7 +992,7 @@ pub const Instruction = union(enum) { } pub fn save(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { - return switch(s2) { + return switch (s2) { Register => format3a(0b10, 0b11_1100, rs1, rs2, rd), i13 => format3b(0b10, 0b11_1100, rs1, rs2, rd), else => unreachable, @@ -1000,7 +1000,7 @@ pub const Instruction = union(enum) { } pub fn restore(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { - return switch(s2) { + return switch (s2) { Register => format3a(0b10, 0b11_1101, rs1, rs2, rd), i13 => format3b(0b10, 0b11_1101, rs1, rs2, rd), else => unreachable, @@ -1012,7 +1012,7 @@ pub const Instruction = union(enum) { } pub fn sub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { - return switch(s2) { + return switch (s2) { Register => format3a(0b10, 0b00_0100, rs1, rs2, rd), i13 => format3b(0b10, 0b00_0100, rs1, rs2, rd), else => unreachable, @@ -1021,7 +1021,7 @@ pub const Instruction = union(enum) { pub fn trap(comptime s2: type, cond: Condition, ccr: CCR, rs1: Register, rs2: s2) Instruction { // Tcc instructions abuse the rd field to store the conditionals. - return switch(s2) { + return switch (s2) { Register => format4a(0b11_1010, ccr, rs1, rs2, @intToEnum(Register, cond)), u7 => format4e(0b00_0100, ccr, rs1, @intToEnum(Register, cond), rs2), else => unreachable, From 1f63afa7c9f57a4d7657890841bdca10c3763534 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 10 Apr 2022 20:10:06 +0700 Subject: [PATCH 1131/2031] stage2: sparcv9: Register the backend in stdlib & driver --- lib/std/builtin.zig | 6 +++++- lib/std/start.zig | 1 + src/Compilation.zig | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 7b66998dc1..f38fc4e155 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -716,6 +716,9 @@ pub const CompilerBackend = enum(u64) { /// The reference implementation self-hosted compiler of Zig, using the /// riscv64 backend. stage2_riscv64 = 9, + /// The reference implementation self-hosted compiler of Zig, using the + /// sparcv9 backend. + stage2_sparcv9 = 10, _, }; @@ -761,7 +764,8 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn builtin.zig_backend == .stage2_aarch64 or builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_x86 or - builtin.zig_backend == .stage2_riscv64) + builtin.zig_backend == .stage2_riscv64 or + builtin.zig_backend == .stage2_sparcv9) { while (true) { @breakpoint(); diff --git a/lib/std/start.zig b/lib/std/start.zig index cd247c915e..20f369476d 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -29,6 +29,7 @@ comptime { builtin.zig_backend == .stage2_aarch64 or builtin.zig_backend == .stage2_arm or builtin.zig_backend == .stage2_riscv64 or + builtin.zig_backend == .stage2_sparcv9 or (builtin.zig_backend == .stage2_llvm and native_os != .linux) or (builtin.zig_backend == .stage2_llvm and native_arch != .x86_64)) { diff --git a/src/Compilation.zig b/src/Compilation.zig index 338be582d8..6c486de36a 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -4531,6 +4531,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca .i386 => .stage2_x86, .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64, .riscv64 => .stage2_riscv64, + .sparcv9 => .stage2_sparcv9, else => .other, }; }; From 1467590e402b5b198ce7c81540263a8e08329e3c Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 10 Apr 2022 20:52:16 +0700 Subject: [PATCH 1132/2031] stage2: sparcv9: Implement enough instruction to compile simple exes --- src/arch/sparcv9/CodeGen.zig | 156 +++++++++++++++++++++++++++++++---- src/arch/sparcv9/Emit.zig | 26 +++++- src/arch/sparcv9/Mir.zig | 10 +++ src/arch/sparcv9/bits.zig | 32 +++++++ 4 files changed, 208 insertions(+), 16 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 635d7bb8f2..de7c786096 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -193,6 +193,43 @@ const CallMCValues = struct { } }; +const BigTomb = struct { + function: *Self, + inst: Air.Inst.Index, + tomb_bits: Liveness.Bpi, + big_tomb_bits: u32, + bit_index: usize, + + fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void { + const this_bit_index = bt.bit_index; + bt.bit_index += 1; + + const op_int = @enumToInt(op_ref); + if (op_int < Air.Inst.Ref.typed_value_map.len) return; + const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); + + if (this_bit_index < Liveness.bpi - 1) { + const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0; + if (!dies) return; + } else { + const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1)); + const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0; + if (!dies) return; + } + bt.function.processDeath(op_index); + } + + fn finishAir(bt: *BigTomb, result: MCValue) void { + const is_used = !bt.function.liveness.isUnused(bt.inst); + if (is_used) { + log.debug("%{d} => {}", .{ bt.inst, result }); + const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1]; + branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result); + } + bt.function.finishAirBookkeeping(); + } +}; + pub fn generate( bin_file: *link.File, src_loc: Module.SrcLoc, @@ -684,8 +721,16 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, buf); } - @panic("TODO implement asm return"); - //return self.fail("TODO implement asm return for {}", .{self.target.cpu.arch}); + var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); + for (outputs) |output| { + if (output == .none) continue; + + bt.feed(output); + } + for (inputs) |input| { + bt.feed(input); + } + return bt.finishAir(result); } fn airArg(self: *Self, inst: Air.Inst.Index) !void { @@ -1071,13 +1116,65 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live self.finishAirBookkeeping(); } -fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, off: i13, abi_size: u64) !void { - _ = value_reg; - _ = addr_reg; - _ = off; +fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_type: type, off: off_type, abi_size: u64) !void { + assert(off_type == Register or off_type == i13); + + const is_imm = (off_type == i13); + const rs2_or_imm = if (is_imm) .{ .imm = off } else .{ .rs2 = off }; switch (abi_size) { - 1, 2, 4, 8 => return self.fail("TODO: A.27 Load Integer", .{}), + 1 => { + _ = try self.addInst(.{ + .tag = .ldub, + .data = .{ + .arithmetic_3op = .{ + .is_imm = is_imm, + .rd = value_reg, + .rs1 = addr_reg, + .rs2_or_imm = rs2_or_imm, + }, + }, + }); + }, + 2 => { + _ = try self.addInst(.{ + .tag = .lduh, + .data = .{ + .arithmetic_3op = .{ + .is_imm = is_imm, + .rd = value_reg, + .rs1 = addr_reg, + .rs2_or_imm = rs2_or_imm, + }, + }, + }); + }, + 4 => { + _ = try self.addInst(.{ + .tag = .lduw, + .data = .{ + .arithmetic_3op = .{ + .is_imm = is_imm, + .rd = value_reg, + .rs1 = addr_reg, + .rs2_or_imm = rs2_or_imm, + }, + }, + }); + }, + 8 => { + _ = try self.addInst(.{ + .tag = .ldx, + .data = .{ + .arithmetic_3op = .{ + .is_imm = is_imm, + .rd = value_reg, + .rs1 = addr_reg, + .rs2_or_imm = rs2_or_imm, + }, + }, + }); + }, 3, 5, 6, 7 => return self.fail("TODO: genLoad for more abi_sizes", .{}), else => unreachable, } @@ -1226,12 +1323,12 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void // The value is in memory at a hard-coded address. // If the type is a pointer, it means the pointer address is at this memory location. try self.genSetReg(ty, reg, .{ .immediate = addr }); - try self.genLoad(reg, reg, 0, ty.abiSize(self.target.*)); + try self.genLoad(reg, reg, i13, 0, ty.abiSize(self.target.*)); }, .stack_offset => |off| { const simm13 = math.cast(u12, off) catch return self.fail("TODO larger stack offsets", .{}); - try self.genLoad(reg, .sp, simm13, ty.abiSize(self.target.*)); + try self.genLoad(reg, .sp, i13, simm13, ty.abiSize(self.target.*)); }, } } @@ -1269,14 +1366,10 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { return MCValue{ .undef = {} }; if (typed_value.val.castTag(.decl_ref)) |payload| { - _ = payload; - return self.fail("TODO implement lowerDeclRef non-mut", .{}); - // return self.lowerDeclRef(typed_value, payload.data); + return self.lowerDeclRef(typed_value, payload.data); } if (typed_value.val.castTag(.decl_ref_mut)) |payload| { - _ = payload; - return self.fail("TODO implement lowerDeclRef mut", .{}); - // return self.lowerDeclRef(typed_value, payload.data.decl); + return self.lowerDeclRef(typed_value, payload.data.decl); } const target = self.target.*; @@ -1315,6 +1408,39 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { } } +fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb { + try self.ensureProcessDeathCapacity(operand_count + 1); + return BigTomb{ + .function = self, + .inst = inst, + .tomb_bits = self.liveness.getTombBits(inst), + .big_tomb_bits = self.liveness.special.get(inst) orelse 0, + .bit_index = 0, + }; +} + +fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue { + const ptr_bits = self.target.cpu.arch.ptrBitWidth(); + const ptr_bytes: u64 = @divExact(ptr_bits, 8); + + // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`? + if (tv.ty.zigTypeTag() == .Pointer) blk: { + if (tv.ty.castPtrToFn()) |_| break :blk; + if (!tv.ty.elemType2().hasRuntimeBits()) { + return MCValue.none; + } + } + + decl.alive = true; + if (self.bin_file.cast(link.File.Elf)) |elf_file| { + const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; + const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; + return MCValue{ .memory = got_addr }; + } else { + return self.fail("TODO codegen non-ELF const Decl pointer", .{}); + } +} + fn parseRegName(name: []const u8) ?Register { if (@hasDecl(Register, "parseRegName")) { return Register.parseRegName(name); diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index 7ff1aeb532..b209ce1636 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -59,6 +59,11 @@ pub fn emitMir( .jmpl => @panic("TODO implement sparcv9 jmpl"), .jmpl_i => @panic("TODO implement sparcv9 jmpl to reg"), + .ldub => try emit.mirArithmetic3Op(inst), + .lduh => try emit.mirArithmetic3Op(inst), + .lduw => try emit.mirArithmetic3Op(inst), + .ldx => try emit.mirArithmetic3Op(inst), + .@"or" => try emit.mirArithmetic3Op(inst), .nop => try emit.mirNop(), @@ -68,7 +73,7 @@ pub fn emitMir( .save => try emit.mirArithmetic3Op(inst), .restore => try emit.mirArithmetic3Op(inst), - .sethi => @panic("TODO implement sparcv9 sethi"), + .sethi => try emit.mirSethi(inst), .sllx => @panic("TODO implement sparcv9 sllx"), @@ -158,6 +163,10 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { const imm = data.rs2_or_imm.imm; switch (tag) { .add => try emit.writeInstruction(Instruction.add(i13, rs1, imm, rd)), + .ldub => try emit.writeInstruction(Instruction.ldub(i13, rs1, imm, rd)), + .lduh => try emit.writeInstruction(Instruction.lduh(i13, rs1, imm, rd)), + .lduw => try emit.writeInstruction(Instruction.lduw(i13, rs1, imm, rd)), + .ldx => try emit.writeInstruction(Instruction.ldx(i13, rs1, imm, rd)), .@"or" => try emit.writeInstruction(Instruction.@"or"(i13, rs1, imm, rd)), .save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)), .restore => try emit.writeInstruction(Instruction.restore(i13, rs1, imm, rd)), @@ -168,6 +177,10 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { const rs2 = data.rs2_or_imm.rs2; switch (tag) { .add => try emit.writeInstruction(Instruction.add(Register, rs1, rs2, rd)), + .ldub => try emit.writeInstruction(Instruction.ldub(Register, rs1, rs2, rd)), + .lduh => try emit.writeInstruction(Instruction.lduh(Register, rs1, rs2, rd)), + .lduw => try emit.writeInstruction(Instruction.lduw(Register, rs1, rs2, rd)), + .ldx => try emit.writeInstruction(Instruction.ldx(Register, rs1, rs2, rd)), .@"or" => try emit.writeInstruction(Instruction.@"or"(Register, rs1, rs2, rd)), .save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)), .restore => try emit.writeInstruction(Instruction.restore(Register, rs1, rs2, rd)), @@ -181,6 +194,17 @@ fn mirNop(emit: *Emit) !void { try emit.writeInstruction(Instruction.nop()); } +fn mirSethi(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const data = emit.mir.instructions.items(.data)[inst].sethi; + + const imm = data.imm; + const rd = data.rd; + + assert(tag == .sethi); + try emit.writeInstruction(Instruction.sethi(imm, rd)); +} + fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; const data = emit.mir.instructions.items(.data)[inst].trap; diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index 02974dfda3..352019a8fa 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -60,6 +60,16 @@ pub const Inst = struct { jmpl, jmpl_i, + /// A.27 Load Integer + /// Those uses the arithmetic_3op field. + /// Note that the ldd variant of this instruction is deprecated, do not emit + /// it unless specifically requested (e.g. by inline assembly). + // TODO add other operations. + ldub, + lduh, + lduw, + ldx, + /// A.31 Logical Operations /// Those uses the arithmetic_3op field. // TODO add other operations. diff --git a/src/arch/sparcv9/bits.zig b/src/arch/sparcv9/bits.zig index 83c560e584..0e0ff71f86 100644 --- a/src/arch/sparcv9/bits.zig +++ b/src/arch/sparcv9/bits.zig @@ -979,6 +979,38 @@ pub const Instruction = union(enum) { }; } + pub fn ldub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b11, 0b00_0001, rs1, rs2, rd), + i13 => format3b(0b11, 0b00_0001, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn lduh(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b11, 0b00_0010, rs1, rs2, rd), + i13 => format3b(0b11, 0b00_0010, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn lduw(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b11, 0b00_0000, rs1, rs2, rd), + i13 => format3b(0b11, 0b00_0000, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn ldx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b11, 0b00_1011, rs1, rs2, rd), + i13 => format3b(0b11, 0b00_1011, rs1, rs2, rd), + else => unreachable, + }; + } + pub fn nop() Instruction { return sethi(0, .g0); } From dcb12a7941371cee3b62cc3215d89c1f96577372 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 11 Apr 2022 20:27:38 +0700 Subject: [PATCH 1133/2031] stage2: sparcv9: Use regular structs to encode instructions Currently packed structs still has endian-dependent behavior, so it results in code that is not portable across platforms (see also issue 10113). --- src/arch/sparcv9/Emit.zig | 2 +- src/arch/sparcv9/Mir.zig | 2 +- src/arch/sparcv9/bits.zig | 110 ++++++++++++++++++++------------------ 3 files changed, 60 insertions(+), 54 deletions(-) diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index b209ce1636..b811a3567f 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -290,7 +290,7 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { fn writeInstruction(emit: *Emit, instruction: Instruction) !void { // SPARCv9 instructions are always arranged in BE regardless of the - // endianness mode the CPU is running in. + // endianness mode the CPU is running in (Section 3.1 of the ISA specification). // This is to ease porting in case someone wants to do a LE SPARCv9 backend. const endian = Endian.Big; diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index 352019a8fa..c79ebdcac1 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -62,7 +62,7 @@ pub const Inst = struct { /// A.27 Load Integer /// Those uses the arithmetic_3op field. - /// Note that the ldd variant of this instruction is deprecated, do not emit + /// Note that the ldd variant of this instruction is deprecated, so do not emit /// it unless specifically requested (e.g. by inline assembly). // TODO add other operations. ldub, diff --git a/src/arch/sparcv9/bits.zig b/src/arch/sparcv9/bits.zig index 0e0ff71f86..3e62b68572 100644 --- a/src/arch/sparcv9/bits.zig +++ b/src/arch/sparcv9/bits.zig @@ -164,27 +164,33 @@ pub const Instruction = union(enum) { // name them with letters since there's no official naming scheme. // TODO: need to rename the minor formats to a more descriptive name. + // I am using regular structs instead of packed ones to avoid + // endianness-dependent behavior when constructing the actual + // assembly instructions. + // See also: https://github.com/ziglang/zig/issues/10113 + // TODO: change it back to packed structs once the issue is resolved. + // Format 1 (op = 1): CALL - format_1: packed struct { + format_1: struct { op: u2 = 0b01, disp30: u30, }, // Format 2 (op = 0): SETHI & Branches (Bicc, BPcc, BPr, FBfcc, FBPfcc) - format_2a: packed struct { + format_2a: struct { op: u2 = 0b00, rd: u5, op2: u3, imm22: u22, }, - format_2b: packed struct { + format_2b: struct { op: u2 = 0b00, a: u1, cond: u4, op2: u3, disp22: u22, }, - format_2c: packed struct { + format_2c: struct { op: u2 = 0b00, a: u1, cond: u4, @@ -194,7 +200,7 @@ pub const Instruction = union(enum) { p: u1, disp19: u19, }, - format_2d: packed struct { + format_2d: struct { op: u2 = 0b00, a: u1, fixed: u1 = 0b0, @@ -207,7 +213,7 @@ pub const Instruction = union(enum) { }, // Format 3 (op = 2 or 3): Arithmetic, Logical, MOVr, MEMBAR, Load, and Store - format_3a: packed struct { + format_3a: struct { op: u2, rd: u5, op3: u6, @@ -224,7 +230,7 @@ pub const Instruction = union(enum) { i: u1 = 0b1, simm13: u13, }, - format_3c: packed struct { + format_3c: struct { op: u2, reserved1: u5 = 0b00000, op3: u6, @@ -241,7 +247,7 @@ pub const Instruction = union(enum) { i: u1 = 0b1, simm13: u13, }, - format_3e: packed struct { + format_3e: struct { op: u2, rd: u5, op3: u6, @@ -260,7 +266,7 @@ pub const Instruction = union(enum) { rcond: u3, simm10: u10, }, - format_3g: packed struct { + format_3g: struct { op: u2, rd: u5, op3: u6, @@ -269,7 +275,7 @@ pub const Instruction = union(enum) { reserved: u8 = 0b00000000, rs2: u5, }, - format_3h: packed struct { + format_3h: struct { op: u2 = 0b10, fixed1: u5 = 0b00000, op3: u6 = 0b101000, @@ -279,7 +285,7 @@ pub const Instruction = union(enum) { cmask: u3, mmask: u4, }, - format_3i: packed struct { + format_3i: struct { op: u2, rd: u5, op3: u6, @@ -288,13 +294,13 @@ pub const Instruction = union(enum) { imm_asi: u8, rs2: u5, }, - format_3j: packed struct { + format_3j: struct { op: u2, impl_dep1: u5, op3: u6, impl_dep2: u19, }, - format_3k: packed struct { + format_3k: struct { op: u2, rd: u5, op3: u6, @@ -304,7 +310,7 @@ pub const Instruction = union(enum) { reserved: u7 = 0b0000000, rs2: u5, }, - format_3l: packed struct { + format_3l: struct { op: u2, rd: u5, op3: u6, @@ -314,7 +320,7 @@ pub const Instruction = union(enum) { reserved: u7 = 0b0000000, shcnt32: u5, }, - format_3m: packed struct { + format_3m: struct { op: u2, rd: u5, op3: u6, @@ -324,7 +330,7 @@ pub const Instruction = union(enum) { reserved: u6 = 0b000000, shcnt64: u6, }, - format_3n: packed struct { + format_3n: struct { op: u2, rd: u5, op3: u6, @@ -332,7 +338,7 @@ pub const Instruction = union(enum) { opf: u9, rs2: u5, }, - format_3o: packed struct { + format_3o: struct { op: u2, fixed: u3 = 0b000, cc1: u1, @@ -342,7 +348,7 @@ pub const Instruction = union(enum) { opf: u9, rs2: u5, }, - format_3p: packed struct { + format_3p: struct { op: u2, rd: u5, op3: u6, @@ -350,20 +356,20 @@ pub const Instruction = union(enum) { opf: u9, rs2: u5, }, - format_3q: packed struct { + format_3q: struct { op: u2, rd: u5, op3: u6, rs1: u5, reserved: u14 = 0b00000000000000, }, - format_3r: packed struct { + format_3r: struct { op: u2, fcn: u5, op3: u6, reserved: u19 = 0b0000000000000000000, }, - format_3s: packed struct { + format_3s: struct { op: u2, rd: u5, op3: u6, @@ -371,7 +377,7 @@ pub const Instruction = union(enum) { }, //Format 4 (op = 2): MOVcc, FMOVr, FMOVcc, and Tcc - format_4a: packed struct { + format_4a: struct { op: u2 = 0b10, rd: u5, op3: u6, @@ -392,7 +398,7 @@ pub const Instruction = union(enum) { cc0: u1, simm11: u11, }, - format_4c: packed struct { + format_4c: struct { op: u2 = 0b10, rd: u5, op3: u6, @@ -415,7 +421,7 @@ pub const Instruction = union(enum) { cc0: u1, simm11: u11, }, - format_4e: packed struct { + format_4e: struct { op: u2 = 0b10, rd: u5, op3: u6, @@ -426,7 +432,7 @@ pub const Instruction = union(enum) { reserved: u4 = 0b0000, sw_trap: u7, }, - format_4f: packed struct { + format_4f: struct { op: u2 = 0b10, rd: u5, op3: u6, @@ -436,7 +442,7 @@ pub const Instruction = union(enum) { opf_low: u5, rs2: u5, }, - format_4g: packed struct { + format_4g: struct { op: u2 = 0b10, rd: u5, op3: u6, @@ -512,37 +518,37 @@ pub const Instruction = union(enum) { pub fn toU32(self: Instruction) u32 { // TODO: Remove this once packed structs work. return switch (self) { - .format_1 => |v| @bitCast(u32, v), - .format_2a => |v| @bitCast(u32, v), - .format_2b => |v| @bitCast(u32, v), - .format_2c => |v| @bitCast(u32, v), - .format_2d => |v| @bitCast(u32, v), - .format_3a => |v| @bitCast(u32, v), + .format_1 => |v| (@as(u32, v.op) << 30) | @as(u32, v.disp30), + .format_2a => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op2) << 22) | @as(u32, v.imm22), + .format_2b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.a) << 29) | (@as(u32, v.cond) << 25) | (@as(u32, v.op2) << 22) | @as(u32, v.disp22), + .format_2c => |v| (@as(u32, v.op) << 30) | (@as(u32, v.a) << 29) | (@as(u32, v.cond) << 25) | (@as(u32, v.op2) << 22) | (@as(u32, v.cc1) << 21) | (@as(u32, v.cc0) << 20) | (@as(u32, v.p) << 19) | @as(u32, v.disp19), + .format_2d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.a) << 29) | (@as(u32, v.fixed) << 28) | (@as(u32, v.rcond) << 25) | (@as(u32, v.op2) << 22) | (@as(u32, v.d16hi) << 20) | (@as(u32, v.p) << 19) | (@as(u32, v.rs1) << 14) | @as(u32, v.d16lo), + .format_3a => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2), .format_3b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | @as(u32, v.simm13), - .format_3c => |v| @bitCast(u32, v), + .format_3c => |v| (@as(u32, v.op) << 30) | (@as(u32, v.reserved1) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved2) << 5) | @as(u32, v.rs2), .format_3d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.reserved) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | @as(u32, v.simm13), - .format_3e => |v| @bitCast(u32, v), + .format_3e => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.rcond) << 10) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2), .format_3f => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.rcond) << 10) | @as(u32, v.simm10), - .format_3g => |v| @bitCast(u32, v), - .format_3h => |v| @bitCast(u32, v), - .format_3i => |v| @bitCast(u32, v), - .format_3j => |v| @bitCast(u32, v), - .format_3k => |v| @bitCast(u32, v), - .format_3l => |v| @bitCast(u32, v), - .format_3m => |v| @bitCast(u32, v), - .format_3n => |v| @bitCast(u32, v), - .format_3o => |v| @bitCast(u32, v), - .format_3p => |v| @bitCast(u32, v), - .format_3q => |v| @bitCast(u32, v), - .format_3r => |v| @bitCast(u32, v), - .format_3s => |v| @bitCast(u32, v), - .format_4a => |v| @bitCast(u32, v), + .format_3g => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2), + .format_3h => |v| (@as(u32, v.op) << 30) | (@as(u32, v.fixed1) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.fixed2) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.reserved) << 7) | (@as(u32, v.cmask) << 4) | @as(u32, v.mmask), + .format_3i => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.imm_asi) << 5) | @as(u32, v.rs2), + .format_3j => |v| (@as(u32, v.op) << 30) | (@as(u32, v.impl_dep1) << 25) | (@as(u32, v.op3) << 19) | @as(u32, v.impl_dep2), + .format_3k => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.x) << 12) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2), + .format_3l => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.x) << 12) | (@as(u32, v.reserved) << 5) | @as(u32, v.shcnt32), + .format_3m => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.x) << 12) | (@as(u32, v.reserved) << 6) | @as(u32, v.shcnt64), + .format_3n => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.reserved) << 14) | (@as(u32, v.opf) << 5) | @as(u32, v.rs2), + .format_3o => |v| (@as(u32, v.op) << 30) | (@as(u32, v.fixed) << 27) | (@as(u32, v.cc1) << 26) | (@as(u32, v.cc0) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.opf) << 5) | @as(u32, v.rs2), + .format_3p => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.opf) << 5) | @as(u32, v.rs2), + .format_3q => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | @as(u32, v.reserved), + .format_3r => |v| (@as(u32, v.op) << 30) | (@as(u32, v.fcn) << 25) | (@as(u32, v.op3) << 19) | @as(u32, v.reserved), + .format_3s => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | @as(u32, v.reserved), + .format_4a => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2), .format_4b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | @as(u32, v.simm11), - .format_4c => |v| @bitCast(u32, v), + .format_4c => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.cc2) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | (@as(u32, v.reserved) << 5) | @as(u32, v.rs2), .format_4d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.cc2) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | @as(u32, v.simm11), - .format_4e => |v| @bitCast(u32, v), - .format_4f => |v| @bitCast(u32, v), - .format_4g => |v| @bitCast(u32, v), + .format_4e => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | (@as(u32, v.reserved) << 7) | @as(u32, v.sw_trap), + .format_4f => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.fixed) << 13) | (@as(u32, v.rcond) << 10) | (@as(u32, v.opf_low) << 5) | @as(u32, v.rs2), + .format_4g => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.fixed) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.opf_cc) << 11) | (@as(u32, v.opf_low) << 5) | @as(u32, v.rs2), }; } From b916ba18b6ace62fccc74eb11205946842bba66b Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 13 Apr 2022 19:39:21 +0700 Subject: [PATCH 1134/2031] stage2: sparcv9: Fix Tcc encoding --- src/arch/sparcv9/bits.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/sparcv9/bits.zig b/src/arch/sparcv9/bits.zig index 3e62b68572..bc8b8822b7 100644 --- a/src/arch/sparcv9/bits.zig +++ b/src/arch/sparcv9/bits.zig @@ -1061,7 +1061,7 @@ pub const Instruction = union(enum) { // Tcc instructions abuse the rd field to store the conditionals. return switch (s2) { Register => format4a(0b11_1010, ccr, rs1, rs2, @intToEnum(Register, cond)), - u7 => format4e(0b00_0100, ccr, rs1, @intToEnum(Register, cond), rs2), + u7 => format4e(0b11_1010, ccr, rs1, @intToEnum(Register, cond), rs2), else => unreachable, }; } From 43e69be196a808c66d2c9673e7293debec01ad5d Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 13 Apr 2022 19:56:39 +0700 Subject: [PATCH 1135/2031] stage2: sparcv9: Add exit2 implementation --- lib/std/start.zig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/std/start.zig b/lib/std/start.zig index 20f369476d..b261ed296e 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -166,6 +166,14 @@ fn exit2(code: usize) noreturn { : "rcx", "r11", "memory" ); }, + .sparcv9 => { + asm volatile ("ta 0x6d" + : + : [number] "{g1}" (1), + [arg1] "{o0}" (code) + : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory" + ); + }, else => @compileError("TODO"), }, // exits(0) From a6ce2fc3dce018964f73cf88f127ec43bf901b6e Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 14 Apr 2022 21:54:07 +0700 Subject: [PATCH 1136/2031] linker: ELF: Add page sizes for ppc64le and sparcv9 --- src/link/Elf.zig | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 9e1ed0cf54..bc46c6371c 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -65,7 +65,7 @@ phdr_load_rw_index: ?u16 = null, phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{}, entry_addr: ?u64 = null, -page_size: u16, +page_size: u32, shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, shstrtab_index: ?u16 = null, @@ -304,7 +304,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { }; const self = try gpa.create(Elf); errdefer gpa.destroy(self); - const page_size: u16 = 0x1000; // TODO ppc64le requires 64KB + + const page_size: u32 = switch (options.target.cpu.arch) { + .powerpc64le => 0x10000, + .sparcv9 => 0x2000, + else => 0x1000, + }; var dwarf: ?Dwarf = if (!options.strip and options.module != null) Dwarf.init(gpa, .elf, options.target) @@ -472,7 +477,7 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 { return min_pos - start; } -pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 { +pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u32) u64 { var start: u64 = 0; while (self.detectAllocCollision(start, object_size)) |item_end| { start = mem.alignForwardGeneric(u64, item_end, min_alignment); From 47b136e3b353932d754c359ae63a1206842d0d70 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 14 Apr 2022 21:55:56 +0700 Subject: [PATCH 1137/2031] stage2: Add SPARC function alignment This is based on @kubkon's suggestion. --- src/target.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/target.zig b/src/target.zig index 2eff4f8445..aafd65e327 100644 --- a/src/target.zig +++ b/src/target.zig @@ -669,6 +669,7 @@ pub fn defaultFunctionAlignment(target: std.Target) u32 { return switch (target.cpu.arch) { .arm, .armeb => 4, .aarch64, .aarch64_32, .aarch64_be => 4, + .sparc, .sparcel, .sparcv9 => 4, .riscv64 => 2, else => 1, }; From f6b95166ebfce9faf3cc0806c6feb089e6922a2e Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 14 Apr 2022 21:59:25 +0700 Subject: [PATCH 1138/2031] stage2: sparcv9: Add simple test case --- test/stage2/sparcv9.zig | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 test/stage2/sparcv9.zig diff --git a/test/stage2/sparcv9.zig b/test/stage2/sparcv9.zig new file mode 100644 index 0000000000..d5611a7fae --- /dev/null +++ b/test/stage2/sparcv9.zig @@ -0,0 +1,39 @@ +const std = @import("std"); +const TestContext = @import("../../src/test.zig").TestContext; + +const linux_sparcv9 = std.zig.CrossTarget{ + .cpu_arch = .sparcv9, + .os_tag = .linux, +}; + +pub fn addCases(ctx: *TestContext) !void { + { + var case = ctx.exe("sparcv9 hello world", linux_sparcv9); + // Regular old hello world + case.addCompareOutput( + \\const msg = "Hello, World!\n"; + \\ + \\pub export fn _start() noreturn { + \\ asm volatile ("ta 0x6d" + \\ : + \\ : [number] "{g1}" (4), + \\ [arg1] "{o0}" (1), + \\ [arg2] "{o1}" (@ptrToInt(msg)), + \\ [arg3] "{o2}" (msg.len) + \\ : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory" + \\ ); + \\ + \\ asm volatile ("ta 0x6d" + \\ : + \\ : [number] "{g1}" (1), + \\ [arg1] "{o0}" (0) + \\ : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory" + \\ ); + \\ + \\ unreachable; + \\} + , + "Hello, World!\n", + ); + } +} From 9201fbe85bff4ae40573b64382131f779a7ed85c Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 14 Apr 2022 22:34:51 +0700 Subject: [PATCH 1139/2031] stage2: sparcv9: Add cmp_lt_errors_len AIR inst & fix asm parsing --- src/arch/sparcv9/CodeGen.zig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index de7c786096..7de035bc5c 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -475,6 +475,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_gt => @panic("TODO try self.airCmp(inst, .gt)"), .cmp_neq => @panic("TODO try self.airCmp(inst, .neq)"), .cmp_vector => @panic("TODO try self.airCmpVector(inst)"), + .cmp_lt_errors_len => @panic("TODO try self.airCmpLtErrorsLen(inst)"), .bool_and => @panic("TODO try self.airBoolOp(inst)"), .bool_or => @panic("TODO try self.airBoolOp(inst)"), @@ -647,10 +648,12 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { } else null; for (inputs) |input| { - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(input_bytes, 0); + const input_name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += constraint.len / 4 + 1; + extra_i += (constraint.len + input_name.len + 1) / 4 + 1; if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); From 2ee83e76f7acc4275f654d2a99d0df2715cec17f Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 14 Apr 2022 23:15:56 +0700 Subject: [PATCH 1140/2031] stage2: Adjust line numbers in tests --- test/stage2/aarch64.zig | 2 +- test/stage2/x86_64.zig | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/stage2/aarch64.zig b/test/stage2/aarch64.zig index d2f40d922c..84334c2a29 100644 --- a/test/stage2/aarch64.zig +++ b/test/stage2/aarch64.zig @@ -159,7 +159,7 @@ pub fn addCases(ctx: *TestContext) !void { { var case = ctx.exe("hello world with updates", macos_aarch64); case.addError("", &[_][]const u8{ - ":108:9: error: struct 'tmp.tmp' has no member named 'main'", + ":109:9: error: struct 'tmp.tmp' has no member named 'main'", }); // Incorrect return type diff --git a/test/stage2/x86_64.zig b/test/stage2/x86_64.zig index a7ebce36d3..a15d2f8ca0 100644 --- a/test/stage2/x86_64.zig +++ b/test/stage2/x86_64.zig @@ -719,7 +719,7 @@ pub fn addCases(ctx: *TestContext) !void { ); switch (target.getOsTag()) { .linux => try case.files.append(.{ - .src = + .src = \\pub fn print() void { \\ asm volatile ("syscall" \\ : @@ -735,7 +735,7 @@ pub fn addCases(ctx: *TestContext) !void { .path = "print.zig", }), .macos => try case.files.append(.{ - .src = + .src = \\extern "c" fn write(usize, usize, usize) usize; \\ \\pub fn print() void { @@ -796,7 +796,7 @@ pub fn addCases(ctx: *TestContext) !void { ); switch (target.getOsTag()) { .linux => try case.files.append(.{ - .src = + .src = \\// dummy comment to make print be on line 2 \\fn print() void { \\ asm volatile ("syscall" @@ -813,7 +813,7 @@ pub fn addCases(ctx: *TestContext) !void { .path = "print.zig", }), .macos => try case.files.append(.{ - .src = + .src = \\extern "c" fn write(usize, usize, usize) usize; \\fn print() void { \\ _ = write(1, @ptrToInt("Hello, World!\n"), 14); @@ -1925,7 +1925,7 @@ fn addLinuxTestCases(ctx: *TestContext) !void { var case = ctx.exe("hello world with updates", linux_x64); case.addError("", &[_][]const u8{ - ":108:9: error: struct 'tmp.tmp' has no member named 'main'", + ":109:9: error: struct 'tmp.tmp' has no member named 'main'", }); // Incorrect return type @@ -2176,7 +2176,7 @@ fn addMacOsTestCases(ctx: *TestContext) !void { { var case = ctx.exe("darwin hello world with updates", macos_x64); case.addError("", &[_][]const u8{ - ":108:9: error: struct 'tmp.tmp' has no member named 'main'", + ":109:9: error: struct 'tmp.tmp' has no member named 'main'", }); // Incorrect return type From e791f062ba3bbae62175076a2b04c06099c9397a Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 14 Apr 2022 23:24:46 +0700 Subject: [PATCH 1141/2031] stage2: sparcv9: Load tests to the list of testcases --- test/cases.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/test/cases.zig b/test/cases.zig index 942119f780..0fb6f381dd 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -16,6 +16,7 @@ pub fn addCases(ctx: *TestContext) !void { try @import("stage2/riscv64.zig").addCases(ctx); try @import("stage2/plan9.zig").addCases(ctx); try @import("stage2/x86_64.zig").addCases(ctx); + try @import("stage2/sparcv9.zig").addCases(ctx); // https://github.com/ziglang/zig/issues/10968 //try @import("stage2/nvptx.zig").addCases(ctx); } From c07213269fe14235d75d8d768984e329cdfcb4fe Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 14 Apr 2022 23:26:03 +0700 Subject: [PATCH 1142/2031] stage2: zig fmt --- lib/std/start.zig | 2 +- src/arch/riscv64/Mir.zig | 1 - test/stage2/x86_64.zig | 8 ++++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/std/start.zig b/lib/std/start.zig index b261ed296e..f4a5cbb763 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -170,7 +170,7 @@ fn exit2(code: usize) noreturn { asm volatile ("ta 0x6d" : : [number] "{g1}" (1), - [arg1] "{o0}" (code) + [arg1] "{o0}" (code), : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory" ); }, diff --git a/src/arch/riscv64/Mir.zig b/src/arch/riscv64/Mir.zig index 7b5049b7d4..5df3a86229 100644 --- a/src/arch/riscv64/Mir.zig +++ b/src/arch/riscv64/Mir.zig @@ -144,4 +144,3 @@ pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end .end = i, }; } - diff --git a/test/stage2/x86_64.zig b/test/stage2/x86_64.zig index a15d2f8ca0..214b32b025 100644 --- a/test/stage2/x86_64.zig +++ b/test/stage2/x86_64.zig @@ -719,7 +719,7 @@ pub fn addCases(ctx: *TestContext) !void { ); switch (target.getOsTag()) { .linux => try case.files.append(.{ - .src = + .src = \\pub fn print() void { \\ asm volatile ("syscall" \\ : @@ -735,7 +735,7 @@ pub fn addCases(ctx: *TestContext) !void { .path = "print.zig", }), .macos => try case.files.append(.{ - .src = + .src = \\extern "c" fn write(usize, usize, usize) usize; \\ \\pub fn print() void { @@ -796,7 +796,7 @@ pub fn addCases(ctx: *TestContext) !void { ); switch (target.getOsTag()) { .linux => try case.files.append(.{ - .src = + .src = \\// dummy comment to make print be on line 2 \\fn print() void { \\ asm volatile ("syscall" @@ -813,7 +813,7 @@ pub fn addCases(ctx: *TestContext) !void { .path = "print.zig", }), .macos => try case.files.append(.{ - .src = + .src = \\extern "c" fn write(usize, usize, usize) usize; \\fn print() void { \\ _ = write(1, @ptrToInt("Hello, World!\n"), 14); From 25874747174da2b0e77b3b888d0f5a13aa1a317e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Apr 2022 10:12:45 -0700 Subject: [PATCH 1143/2031] stage2: progress towards stage3 * The `@bitCast` workaround is removed in favor of `@ptrCast` properly doing element casting for slice element types. This required an enhancement both to stage1 and stage2. * stage1 incorrectly accepts `.{}` instead of `{}`. stage2 code that abused this is fixed. * Make some parameters comptime to support functions in switch expressions (as opposed to making them function pointers). * Avoid relying on local temporaries being mutable. * Workarounds for when stage1 and stage2 disagree on function pointer types. * Workaround recursive formatting bug with a `@panic("TODO")`. * Remove unreachable `else` prongs for some inferred error sets. All in effort towards #89. --- lib/std/fmt.zig | 2 -- lib/std/io.zig | 1 - lib/std/rand.zig | 4 +--- src/AstGen.zig | 4 ++-- src/Liveness.zig | 8 ++++---- src/Module.zig | 4 ++-- src/Sema.zig | 34 +++++++++++++++++++++++++--------- src/arch/aarch64/CodeGen.zig | 13 ++++++++----- src/arch/arm/CodeGen.zig | 13 ++++++++----- src/arch/arm/Emit.zig | 25 ++++++++++++++++++------- src/arch/riscv64/CodeGen.zig | 8 ++++---- src/arch/wasm/CodeGen.zig | 4 ++-- src/arch/x86_64/CodeGen.zig | 22 ++++++++++++++-------- src/codegen/c.zig | 17 ++++++++--------- src/codegen/llvm.zig | 10 +++++----- src/link.zig | 7 ++++++- src/link/C.zig | 5 +++-- src/link/Elf.zig | 2 +- src/link/MachO/Object.zig | 1 - src/link/MachO/fat.zig | 1 - src/link/Plan9.zig | 2 +- src/link/Wasm.zig | 26 ++++++++++++++++---------- src/print_air.zig | 10 +++++----- src/stage1/ir.cpp | 6 ++++++ src/type.zig | 2 +- test/behavior/ptrcast.zig | 16 ++++++++++++++++ 26 files changed, 156 insertions(+), 91 deletions(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 03395c024c..3f230a445e 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -766,12 +766,10 @@ fn formatFloatValue( } else if (comptime std.mem.eql(u8, fmt, "d")) { formatFloatDecimal(value, options, buf_stream.writer()) catch |err| switch (err) { error.NoSpaceLeft => unreachable, - else => |e| return e, }; } else if (comptime std.mem.eql(u8, fmt, "x")) { formatFloatHexadecimal(value, options, buf_stream.writer()) catch |err| switch (err) { error.NoSpaceLeft => unreachable, - else => |e| return e, }; } else { @compileError("Unsupported format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'"); diff --git a/lib/std/io.zig b/lib/std/io.zig index fba1c289f7..50d134b856 100644 --- a/lib/std/io.zig +++ b/lib/std/io.zig @@ -9,7 +9,6 @@ const os = std.os; const fs = std.fs; const mem = std.mem; const meta = std.meta; -const trait = meta.trait; const File = std.fs.File; pub const Mode = enum { diff --git a/lib/std/rand.zig b/lib/std/rand.zig index cfac15a1fb..0c3a0fd2ba 100644 --- a/lib/std/rand.zig +++ b/lib/std/rand.zig @@ -66,9 +66,7 @@ pub const Random = struct { /// Returns a random value from an enum, evenly distributed. pub fn enumValue(r: Random, comptime EnumType: type) EnumType { - if (comptime !std.meta.trait.is(.Enum)(EnumType)) { - @compileError("Random.enumValue requires an enum type, not a " ++ @typeName(EnumType)); - } + comptime assert(@typeInfo(EnumType) == .Enum); // We won't use int -> enum casting because enum elements can have // arbitrary values. Instead we'll randomly pick one of the type's values. diff --git a/src/AstGen.zig b/src/AstGen.zig index 75882c761b..ccce4b0bc8 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -85,12 +85,12 @@ fn reserveExtra(astgen: *AstGen, size: usize) Allocator.Error!u32 { } fn appendRefs(astgen: *AstGen, refs: []const Zir.Inst.Ref) !void { - const coerced = @bitCast([]const u32, refs); + const coerced = @ptrCast([]const u32, refs); return astgen.extra.appendSlice(astgen.gpa, coerced); } fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const Zir.Inst.Ref) void { - const coerced = @bitCast([]const u32, refs); + const coerced = @ptrCast([]const u32, refs); astgen.extra.appendSliceAssumeCapacity(coerced); } diff --git a/src/Liveness.zig b/src/Liveness.zig index d63c442482..be4344ab90 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -454,7 +454,7 @@ fn analyzeInst( const inst_data = inst_datas[inst].pl_op; const callee = inst_data.operand; const extra = a.air.extraData(Air.Call, inst_data.payload); - const args = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..extra.data.args_len]); + const args = @ptrCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..extra.data.args_len]); if (args.len + 1 <= bpi - 1) { var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1); buf[0] = callee; @@ -495,7 +495,7 @@ fn analyzeInst( const ty_pl = inst_datas[inst].ty_pl; const aggregate_ty = a.air.getRefType(ty_pl.ty); const len = @intCast(usize, aggregate_ty.arrayLen()); - const elements = @bitCast([]const Air.Inst.Ref, a.air.extra[ty_pl.payload..][0..len]); + const elements = @ptrCast([]const Air.Inst.Ref, a.air.extra[ty_pl.payload..][0..len]); if (elements.len <= bpi - 1) { var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1); @@ -571,9 +571,9 @@ fn analyzeInst( .assembly => { const extra = a.air.extraData(Air.Asm, inst_datas[inst].ty_pl.payload); var extra_i: usize = extra.end; - const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.outputs_len]); + const outputs = @ptrCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.inputs_len]); + const inputs = @ptrCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; simple: { diff --git a/src/Module.zig b/src/Module.zig index 83252c76ad..53c72ccec2 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4593,7 +4593,7 @@ pub fn clearDecl( .c => .{ .c = {} }, .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, .spirv => .{ .spirv = .{} }, - .nvptx => .{ .nvptx = .{} }, + .nvptx => .{ .nvptx = {} }, }; } if (decl.getInnerNamespace()) |namespace| { @@ -4975,7 +4975,7 @@ pub fn allocateNewDecl( .c => .{ .c = {} }, .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, .spirv => .{ .spirv = .{} }, - .nvptx => .{ .nvptx = .{} }, + .nvptx => .{ .nvptx = {} }, }, .generation = 0, .is_pub = false, diff --git a/src/Sema.zig b/src/Sema.zig index 97fe3cb595..e00abd660b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14110,21 +14110,27 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air try sema.checkPtrType(block, dest_ty_src, dest_ty); try sema.checkPtrOperand(block, operand_src, operand_ty); - if (dest_ty.isSlice()) { + + const dest_is_slice = dest_ty.isSlice(); + const operand_is_slice = operand_ty.isSlice(); + if (dest_is_slice and !operand_is_slice) { return sema.fail(block, dest_ty_src, "illegal pointer cast to slice", .{}); } - const ptr = if (operand_ty.isSlice()) + const ptr = if (operand_is_slice and !dest_is_slice) try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty) else operand; - try sema.resolveTypeLayout(block, dest_ty_src, dest_ty.elemType2()); + const dest_elem_ty = dest_ty.elemType2(); + try sema.resolveTypeLayout(block, dest_ty_src, dest_elem_ty); const dest_align = dest_ty.ptrAlignment(target); - try sema.resolveTypeLayout(block, operand_src, operand_ty.elemType2()); + + const operand_elem_ty = operand_ty.elemType2(); + try sema.resolveTypeLayout(block, operand_src, operand_elem_ty); const operand_align = operand_ty.ptrAlignment(target); // If the destination is less aligned than the source, preserve the source alignment - var aligned_dest_ty = if (operand_align <= dest_align) dest_ty else blk: { + const aligned_dest_ty = if (operand_align <= dest_align) dest_ty else blk: { // Unwrap the pointer (or pointer-like optional) type, set alignment, and re-wrap into result if (dest_ty.zigTypeTag() == .Optional) { var buf: Type.Payload.ElemType = undefined; @@ -14138,6 +14144,16 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air } }; + if (dest_is_slice) { + const operand_elem_size = operand_elem_ty.abiSize(target); + const dest_elem_size = dest_elem_ty.abiSize(target); + if (operand_elem_size != dest_elem_size) { + // note that this is not implemented in stage1 so we should probably wait + // until that codebase is replaced before implementing this in stage2. + return sema.fail(block, dest_ty_src, "TODO: implement @ptrCast between slices changing the length", .{}); + } + } + return sema.coerceCompatiblePtrs(block, aligned_dest_ty, ptr, operand_src); } @@ -15743,7 +15759,7 @@ fn zirMinMax( sema: *Sema, block: *Block, inst: Zir.Inst.Index, - air_tag: Air.Inst.Tag, + comptime air_tag: Air.Inst.Tag, ) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; @@ -15763,7 +15779,7 @@ fn analyzeMinMax( src: LazySrcLoc, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, - air_tag: Air.Inst.Tag, + comptime air_tag: Air.Inst.Tag, lhs_src: LazySrcLoc, rhs_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { @@ -20976,7 +20992,7 @@ fn resolvePeerTypes( sema: *Sema, block: *Block, src: LazySrcLoc, - instructions: []Air.Inst.Ref, + instructions: []const Air.Inst.Ref, candidate_srcs: Module.PeerTypeCandidateSrc, ) !Type { switch (instructions.len) { @@ -22794,7 +22810,7 @@ pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 { } fn appendRefsAssumeCapacity(sema: *Sema, refs: []const Air.Inst.Ref) void { - const coerced = @bitCast([]const u32, refs); + const coerced = @ptrCast([]const u32, refs); sema.air_extra.appendSliceAssumeCapacity(coerced); } diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 0aac47c6c5..95d2a8a607 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -2398,7 +2398,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const pl_op = self.air.instructions.items(.data)[inst].pl_op; const callee = pl_op.operand; const extra = self.air.extraData(Air.Call, pl_op.payload); - const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); + const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); const ty = self.air.typeOf(callee); const fn_ty = switch (ty.zigTypeTag()) { @@ -2865,7 +2865,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { // TODO track the new register / stack allocation } - self.branch_stack.pop().deinit(self.gpa); + { + var item = self.branch_stack.pop(); + item.deinit(self.gpa); + } // We already took care of pl_op.operand earlier, so we're going // to pass .none here @@ -3162,9 +3165,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; const clobbers_len = @truncate(u31, extra.data.flags); var extra_i: usize = extra.end; - const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); + const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); + const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; const dead = !is_volatile and self.liveness.isUnused(inst); @@ -3686,7 +3689,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { const vector_ty = self.air.typeOfIndex(inst); const len = vector_ty.vectorLen(); const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); + const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); const result: MCValue = res: { if (self.liveness.isUnused(inst)) break :res MCValue.dead; return self.fail("TODO implement airAggregateInit for {}", .{self.target.cpu.arch}); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 27f048999b..f71ceaba89 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -3144,7 +3144,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const pl_op = self.air.instructions.items(.data)[inst].pl_op; const callee = pl_op.operand; const extra = self.air.extraData(Air.Call, pl_op.payload); - const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); + const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); const ty = self.air.typeOf(callee); const fn_ty = switch (ty.zigTypeTag()) { @@ -3650,7 +3650,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { // TODO track the new register / stack allocation } - self.branch_stack.pop().deinit(self.gpa); + { + var item = self.branch_stack.pop(); + item.deinit(self.gpa); + } // We already took care of pl_op.operand earlier, so we're going // to pass .none here @@ -3951,9 +3954,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; const clobbers_len = @truncate(u31, extra.data.flags); var extra_i: usize = extra.end; - const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); + const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); + const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; const dead = !is_volatile and self.liveness.isUnused(inst); @@ -4735,7 +4738,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { const vector_ty = self.air.typeOfIndex(inst); const len = vector_ty.vectorLen(); const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); + const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); const result: MCValue = res: { if (self.liveness.isUnused(inst)) break :res MCValue.dead; return self.fail("TODO implement airAggregateInit for arm", .{}); diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index 7c9e508e5f..209ab137a6 100644 --- a/src/arch/arm/Emit.zig +++ b/src/arch/arm/Emit.zig @@ -2,6 +2,7 @@ //! machine code const Emit = @This(); +const builtin = @import("builtin"); const std = @import("std"); const math = std.math; const Mir = @import("Mir.zig"); @@ -622,12 +623,17 @@ fn mirLoadStackArgument(emit: *Emit, inst: Mir.Inst.Index) !void { } else return emit.fail("TODO mirLoadStack larger offsets", .{}); const ldr = switch (tag) { - .ldr_stack_argument => Instruction.ldr, - .ldrb_stack_argument => Instruction.ldrb, + .ldr_stack_argument => &Instruction.ldr, + .ldrb_stack_argument => &Instruction.ldrb, else => unreachable, }; - try emit.writeInstruction(ldr( + const ldr_workaround = switch (builtin.zig_backend) { + .stage1 => ldr.*, + else => ldr, + }; + + try emit.writeInstruction(ldr_workaround( cond, r_stack_offset.rt, .fp, @@ -643,13 +649,18 @@ fn mirLoadStackArgument(emit: *Emit, inst: Mir.Inst.Index) !void { } else return emit.fail("TODO mirLoadStack larger offsets", .{}); const ldr = switch (tag) { - .ldrh_stack_argument => Instruction.ldrh, - .ldrsb_stack_argument => Instruction.ldrsb, - .ldrsh_stack_argument => Instruction.ldrsh, + .ldrh_stack_argument => &Instruction.ldrh, + .ldrsb_stack_argument => &Instruction.ldrsb, + .ldrsh_stack_argument => &Instruction.ldrsh, else => unreachable, }; - try emit.writeInstruction(ldr( + const ldr_workaround = switch (builtin.zig_backend) { + .stage1 => ldr.*, + else => ldr, + }; + + try emit.writeInstruction(ldr_workaround( cond, r_stack_offset.rt, .fp, diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 25a7a65f57..cf9e5fefcd 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1640,7 +1640,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const fn_ty = self.air.typeOf(pl_op.operand); const callee = pl_op.operand; const extra = self.air.extraData(Air.Call, pl_op.payload); - const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); + const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); var info = try self.resolveCallingConventionValues(fn_ty); defer info.deinit(self); @@ -2075,9 +2075,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; const clobbers_len = @truncate(u31, extra.data.flags); var extra_i: usize = extra.end; - const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); + const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); + const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; const dead = !is_volatile and self.liveness.isUnused(inst); @@ -2413,7 +2413,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { const vector_ty = self.air.typeOfIndex(inst); const len = vector_ty.vectorLen(); const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); + const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); const result: MCValue = res: { if (self.liveness.isUnused(inst)) break :res MCValue.dead; return self.fail("TODO implement airAggregateInit for riscv64", .{}); diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 38ab19cb60..b701299e73 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2425,7 +2425,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { var highest_maybe: ?i32 = null; while (case_i < switch_br.data.cases_len) : (case_i += 1) { const case = self.air.extraData(Air.SwitchBr.Case, extra_index); - const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); + const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; extra_index = case.end + items.len + case_body.len; const values = try self.gpa.alloc(CaseValue, items.len); @@ -3328,7 +3328,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const result_ty = self.air.typeOfIndex(inst); const len = @intCast(usize, result_ty.arrayLen()); - const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); + const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); switch (result_ty.zigTypeTag()) { .Vector => return self.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}), diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 53a6bfc4d9..10271df122 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -3501,7 +3501,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const pl_op = self.air.instructions.items(.data)[inst].pl_op; const callee = pl_op.operand; const extra = self.air.extraData(Air.Call, pl_op.payload); - const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); + const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); const ty = self.air.typeOf(callee); const fn_ty = switch (ty.zigTypeTag()) { @@ -3684,7 +3684,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .ops = (Mir.Ops{ .flags = 0b01, }).encode(), - .data = .{ .imm = @bitCast(i32, @intCast(u32, fn_got_addr)) }, + .data = .{ .imm = @intCast(u32, fn_got_addr) }, }); } else return self.fail("TODO implement calling extern fn on plan9", .{}); } else { @@ -4220,7 +4220,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { // TODO track the new register / stack allocation } - self.branch_stack.pop().deinit(self.gpa); + { + var item = self.branch_stack.pop(); + item.deinit(self.gpa); + } // We already took care of pl_op.operand earlier, so we're going // to pass .none here @@ -4562,7 +4565,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { while (case_i < switch_br.data.cases_len) : (case_i += 1) { const case = self.air.extraData(Air.SwitchBr.Case, extra_index); - const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); + const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; extra_index = case.end + items.len + case_body.len; @@ -4615,7 +4618,10 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { if (switch_br.data.else_body_len > 0) { const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; try self.branch_stack.append(.{}); - defer self.branch_stack.pop().deinit(self.gpa); + defer { + var item = self.branch_stack.pop(); + item.deinit(self.gpa); + } const else_deaths = liveness.deaths.len - 1; try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len); @@ -4705,9 +4711,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; const clobbers_len = @truncate(u31, extra.data.flags); var extra_i: usize = extra.end; - const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); + const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); + const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; const dead = !is_volatile and self.liveness.isUnused(inst); @@ -5975,7 +5981,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { const result_ty = self.air.typeOfIndex(inst); const len = @intCast(usize, result_ty.arrayLen()); const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); + const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); const abi_size = @intCast(u32, result_ty.abiSize(self.target.*)); const abi_align = result_ty.abiAlignment(self.target.*); const result: MCValue = res: { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 69288494bc..54f8285291 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -220,6 +220,9 @@ fn formatIdent( } pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) { + if (builtin.zig_backend != .stage1) { + @panic("TODO"); + } return .{ .data = ident }; } @@ -2310,7 +2313,6 @@ fn airWrapOp( const val = -1 * std.math.pow(i64, 2, @intCast(i64, bits - 1)); break :blk std.fmt.bufPrint(&min_buf, "{d}", .{val}) catch |err| switch (err) { error.NoSpaceLeft => unreachable, - else => |e| return e, }; }, }, @@ -2336,7 +2338,6 @@ fn airWrapOp( const val = std.math.pow(u64, 2, pow_bits) - 1; break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |err| switch (err) { error.NoSpaceLeft => unreachable, - else => |e| return e, }; }, }; @@ -2418,7 +2419,6 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { const val = -1 * std.math.pow(i65, 2, @intCast(i65, bits - 1)); break :blk std.fmt.bufPrint(&min_buf, "{d}", .{val}) catch |err| switch (err) { error.NoSpaceLeft => unreachable, - else => |e| return e, }; }, }, @@ -2444,7 +2444,6 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { const val = std.math.pow(u65, 2, pow_bits) - 1; break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |err| switch (err) { error.NoSpaceLeft => unreachable, - else => |e| return e, }; }, }; @@ -2702,7 +2701,7 @@ fn airCall( } const pl_op = f.air.instructions.items(.data)[inst].pl_op; const extra = f.air.extraData(Air.Call, pl_op.payload); - const args = @bitCast([]const Air.Inst.Ref, f.air.extra[extra.end..][0..extra.data.args_len]); + const args = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra.end..][0..extra.data.args_len]); const callee_ty = f.air.typeOf(pl_op.operand); const fn_ty = switch (callee_ty.zigTypeTag()) { .Fn => callee_ty, @@ -2959,7 +2958,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { var case_i: u32 = 0; while (case_i < switch_br.data.cases_len) : (case_i += 1) { const case = f.air.extraData(Air.SwitchBr.Case, extra_index); - const items = @bitCast([]const Air.Inst.Ref, f.air.extra[case.end..][0..case.data.items_len]); + const items = @ptrCast([]const Air.Inst.Ref, f.air.extra[case.end..][0..case.data.items_len]); const case_body = f.air.extra[case.end + items.len ..][0..case.data.body_len]; extra_index = case.end + case.data.items_len + case_body.len; @@ -2990,9 +2989,9 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; const clobbers_len = @truncate(u31, extra.data.flags); var extra_i: usize = extra.end; - const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.outputs_len]); + const outputs = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]); + const inputs = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; if (!is_volatile and f.liveness.isUnused(inst)) return CValue.none; @@ -3860,7 +3859,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const vector_ty = f.air.getRefType(ty_pl.ty); const len = vector_ty.vectorLen(); - const elements = @bitCast([]const Air.Inst.Ref, f.air.extra[ty_pl.payload..][0..len]); + const elements = @ptrCast([]const Air.Inst.Ref, f.air.extra[ty_pl.payload..][0..len]); const writer = f.object.writer(); const local = try f.allocLocal(inst_ty, .Const); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index db4c08f86f..f55d326a47 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3657,7 +3657,7 @@ pub const FuncGen = struct { fn airCall(self: *FuncGen, inst: Air.Inst.Index, attr: llvm.CallAttr) !?*const llvm.Value { const pl_op = self.air.instructions.items(.data)[inst].pl_op; const extra = self.air.extraData(Air.Call, pl_op.payload); - const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); + const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); const callee_ty = self.air.typeOf(pl_op.operand); const zig_fn_ty = switch (callee_ty.zigTypeTag()) { .Fn => callee_ty, @@ -4037,7 +4037,7 @@ pub const FuncGen = struct { while (case_i < switch_br.data.cases_len) : (case_i += 1) { const case = self.air.extraData(Air.SwitchBr.Case, extra_index); - const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); + const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; extra_index = case.end + case.data.items_len + case_body.len; @@ -4538,9 +4538,9 @@ pub const FuncGen = struct { if (!is_volatile and self.liveness.isUnused(inst)) return null; - const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); + const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); + const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; if (outputs.len > 1) { @@ -6660,7 +6660,7 @@ pub const FuncGen = struct { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const result_ty = self.air.typeOfIndex(inst); const len = @intCast(usize, result_ty.arrayLen()); - const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); + const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); const llvm_result_ty = try self.dg.llvmType(result_ty); const target = self.dg.module.getTarget(); diff --git a/src/link.zig b/src/link.zig index 139b12af99..dbef400189 100644 --- a/src/link.zig +++ b/src/link.zig @@ -649,6 +649,11 @@ pub const File = struct { } } + pub const UpdateDeclExportsError = error{ + OutOfMemory, + AnalysisFail, + }; + /// May be called before or after updateDecl, but must be called after /// allocateDeclIndexes for any given Decl. pub fn updateDeclExports( @@ -656,7 +661,7 @@ pub const File = struct { module: *Module, decl: *Module.Decl, exports: []const *Module.Export, - ) !void { + ) UpdateDeclExportsError!void { log.debug("updateDeclExports {*} ({s})", .{ decl, decl.name }); assert(decl.has_tv); switch (base.tag) { diff --git a/src/link/C.zig b/src/link/C.zig index 85b7c24487..229990fc8e 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -89,8 +89,9 @@ pub fn deinit(self: *C) void { pub fn freeDecl(self: *C, decl: *Module.Decl) void { const gpa = self.base.allocator; - if (self.decl_table.fetchSwapRemove(decl)) |*kv| { - kv.value.deinit(gpa); + if (self.decl_table.fetchSwapRemove(decl)) |kv| { + var decl_block = kv.value; + decl_block.deinit(gpa); } } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 9e1ed0cf54..4ae7ab64a4 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2482,7 +2482,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl try self.atom_by_index_table.putNoClobber(self.base.allocator, atom.local_sym_index, atom); const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{ - .none = .{}, + .none = {}, }, .{ .parent_atom_index = atom.local_sym_index, }); diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 6620c99b49..255d7053d4 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -625,7 +625,6 @@ pub fn parseDataInCode(self: *Object, allocator: Allocator) !void { while (true) { const dice = reader.readStruct(macho.data_in_code_entry) catch |err| switch (err) { error.EndOfStream => break, - else => |e| return e, }; try self.data_in_code_entries.append(allocator, dice); } diff --git a/src/link/MachO/fat.zig b/src/link/MachO/fat.zig index 89a2272dd1..1f8a6a2e84 100644 --- a/src/link/MachO/fat.zig +++ b/src/link/MachO/fat.zig @@ -40,7 +40,6 @@ pub fn getLibraryOffset(reader: anytype, target: std.Target) !u64 { // fine because we can keep looking for one that might match. const lib_arch = decodeArch(fat_arch.cputype, false) catch |err| switch (err) { error.UnsupportedCpuArchitecture => continue, - else => |e| return e, }; if (lib_arch == target.cpu.arch) { // We have found a matching architecture! diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 8096b2d38c..3269cb67d4 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -307,7 +307,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void { const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ .ty = decl.ty, .val = decl_val, - }, &code_buffer, .{ .none = .{} }, .{ + }, &code_buffer, .{ .none = {} }, .{ .parent_atom_index = @intCast(u32, sym_index), }); const code = switch (res) { diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index c717b42bb6..ffd3eef7b6 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2551,7 +2551,8 @@ fn emitSymbolTable(self: *Wasm, file: fs.File, arena: Allocator, symbol_table: * .iov_base = payload.items.ptr, .iov_len = payload.items.len, }; - try file.writevAll(&.{iovec}); + var iovecs = [_]std.os.iovec_const{iovec}; + try file.writevAll(&iovecs); } fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { @@ -2576,7 +2577,8 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { .iov_base = payload.items.ptr, .iov_len = payload.items.len, }; - try file.writevAll(&.{iovec}); + var iovecs = [_]std.os.iovec_const{iovec}; + try file.writevAll(&iovecs); } fn getULEB128Size(uint_value: anytype) u32 { @@ -2635,12 +2637,14 @@ fn emitCodeRelocations( var buf: [5]u8 = undefined; leb.writeUnsignedFixed(5, &buf, count); try payload.insertSlice(reloc_start, &buf); - const iovec: std.os.iovec_const = .{ - .iov_base = payload.items.ptr, - .iov_len = payload.items.len, + var iovecs = [_]std.os.iovec_const{ + .{ + .iov_base = payload.items.ptr, + .iov_len = payload.items.len, + }, }; const header_offset = try reserveCustomSectionHeader(file); - try file.writevAll(&.{iovec}); + try file.writevAll(&iovecs); const size = @intCast(u32, payload.items.len); try writeCustomSectionHeader(file, header_offset, size); } @@ -2694,12 +2698,14 @@ fn emitDataRelocations( var buf: [5]u8 = undefined; leb.writeUnsignedFixed(5, &buf, count); try payload.insertSlice(reloc_start, &buf); - const iovec: std.os.iovec_const = .{ - .iov_base = payload.items.ptr, - .iov_len = payload.items.len, + var iovecs = [_]std.os.iovec_const{ + .{ + .iov_base = payload.items.ptr, + .iov_len = payload.items.len, + }, }; const header_offset = try reserveCustomSectionHeader(file); - try file.writevAll(&.{iovec}); + try file.writevAll(&iovecs); const size = @intCast(u32, payload.items.len); try writeCustomSectionHeader(file, header_offset, size); } diff --git a/src/print_air.zig b/src/print_air.zig index 82583a3c55..8a1a8fa950 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -328,7 +328,7 @@ const Writer = struct { const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; const vector_ty = w.air.getRefType(ty_pl.ty); const len = @intCast(usize, vector_ty.arrayLen()); - const elements = @bitCast([]const Air.Inst.Ref, w.air.extra[ty_pl.payload..][0..len]); + const elements = @ptrCast([]const Air.Inst.Ref, w.air.extra[ty_pl.payload..][0..len]); try s.print("{}, [", .{vector_ty.fmtDebug()}); for (elements) |elem, i| { @@ -533,9 +533,9 @@ const Writer = struct { try s.writeAll(", volatile"); } - const outputs = @bitCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.outputs_len]); + const outputs = @ptrCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.inputs_len]); + const inputs = @ptrCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; for (outputs) |output| { @@ -604,7 +604,7 @@ const Writer = struct { fn writeCall(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const pl_op = w.air.instructions.items(.data)[inst].pl_op; const extra = w.air.extraData(Air.Call, pl_op.payload); - const args = @bitCast([]const Air.Inst.Ref, w.air.extra[extra.end..][0..extra.data.args_len]); + const args = @ptrCast([]const Air.Inst.Ref, w.air.extra[extra.end..][0..extra.data.args_len]); try w.writeOperand(s, inst, 0, pl_op.operand); try s.writeAll(", ["); for (args) |arg, i| { @@ -674,7 +674,7 @@ const Writer = struct { while (case_i < switch_br.data.cases_len) : (case_i += 1) { const case = w.air.extraData(Air.SwitchBr.Case, extra_index); - const items = @bitCast([]const Air.Inst.Ref, w.air.extra[case.end..][0..case.data.items_len]); + const items = @ptrCast([]const Air.Inst.Ref, w.air.extra[case.end..][0..case.data.items_len]); const case_body = w.air.extra[case.end + items.len ..][0..case.data.body_len]; extra_index = case.end + case.data.items_len + case_body.len; diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 11f47c592b..c3157b6539 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -23052,6 +23052,12 @@ static Stage1AirInst *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, Stage1ZirI if (type_is_invalid(src_type)) return ira->codegen->invalid_inst_gen; + // This logic is not quite right; this is just to get stage1 to accept valid code + // we use in the self-hosted compiler. + if (is_slice(dest_type) && is_slice(src_type)) { + return ir_analyze_bit_cast(ira, instruction->base.scope, instruction->base.source_node, ptr, dest_type); + } + bool keep_bigger_alignment = true; return ir_analyze_ptr_cast(ira, instruction->base.scope, instruction->base.source_node, ptr, instruction->ptr->source_node, dest_type, dest_type_value->source_node, diff --git a/src/type.zig b/src/type.zig index da0149967b..2a19f7375b 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1451,7 +1451,7 @@ pub const Type = extern union { var duped_names = Module.ErrorSet.NameMap{}; try duped_names.ensureTotalCapacity(allocator, names.len); for (names) |name| { - duped_names.putAssumeCapacityNoClobber(name, .{}); + duped_names.putAssumeCapacityNoClobber(name, {}); } return Tag.error_set_merged.create(allocator, duped_names); }, diff --git a/test/behavior/ptrcast.zig b/test/behavior/ptrcast.zig index d9a3892664..10138cff01 100644 --- a/test/behavior/ptrcast.zig +++ b/test/behavior/ptrcast.zig @@ -217,3 +217,19 @@ test "implicit optional pointer to optional anyopaque pointer" { var z = @ptrCast(*[4]u8, y); try expect(std.mem.eql(u8, z, "aoeu")); } + +test "@ptrCast slice to slice" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const S = struct { + fn foo(slice: []u32) []i32 { + return @ptrCast([]i32, slice); + } + }; + var buf: [4]u32 = .{ 0, 0, 0, 0 }; + const alias = S.foo(&buf); + alias[1] = 42; + try expect(buf[1] == 42); + try expect(alias.len == 4); +} From 321a1642693b74481e12cae8d7be089c6ef45cc1 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 12 Apr 2022 21:43:16 +0200 Subject: [PATCH 1144/2031] wasm-linker: Fix memory leak This fixes a memory leak when an object file contains one or more element sections which then contains one or more function indexes. This commit ensures the slice of index functions for each element section will be freed upon resource deallocation also. --- src/link/Wasm/Object.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index 011ec2e9e4..471a91e927 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -133,6 +133,9 @@ pub fn deinit(self: *Object, gpa: Allocator) void { gpa.free(self.memories); gpa.free(self.globals); gpa.free(self.exports); + for (self.elements) |el| { + gpa.free(el.func_indexes); + } gpa.free(self.elements); gpa.free(self.features); for (self.relocations.values()) |val| { From cf37101108e56412e958a8b597bae84649893b6f Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 13 Apr 2022 22:10:02 +0200 Subject: [PATCH 1145/2031] wasm-linker: Add function table indexes When linking with an object file, verify if a relocation is a table index relocation. If that's the case, add the relocation target to the function table. --- src/link/Wasm.zig | 27 ++++++++++++++++++++------- src/link/Wasm/Atom.zig | 2 +- src/link/Wasm/Object.zig | 15 ++++++--------- src/link/Wasm/types.zig | 12 ++++++++++++ 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index ffd3eef7b6..f13e59dc5e 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -101,8 +101,8 @@ exports: std.ArrayListUnmanaged(types.Export) = .{}, /// When this is non-zero, we must emit a table entry, /// as well as an 'elements' section. /// -/// Note: Key is symbol index, value represents the index into the table -function_table: std.AutoHashMapUnmanaged(u32, u32) = .{}, +/// Note: Key is symbol location, value represents the index into the table +function_table: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{}, /// All object files and their data which are linked into the final binary objects: std.ArrayListUnmanaged(Object) = .{}, @@ -363,6 +363,9 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { .index = sym_index, }; const sym_name = object.string_table.get(symbol.name); + if (mem.eql(u8, sym_name, "__indirect_function_table")) { + continue; + } const sym_name_index = try self.string_table.put(self.base.allocator, sym_name); if (symbol.isLocal()) { @@ -837,7 +840,7 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { /// Appends a new entry to the indirect function table pub fn addTableFunction(self: *Wasm, symbol_index: u32) !void { const index = @intCast(u32, self.function_table.count()); - try self.function_table.put(self.base.allocator, symbol_index, index); + try self.function_table.put(self.base.allocator, .{ .file = null, .index = symbol_index }, index); } /// Assigns indexes to all indirect functions. @@ -1017,6 +1020,9 @@ fn setupImports(self: *Wasm) !void { } const symbol = symbol_loc.getSymbol(self); + if (std.mem.eql(u8, symbol_loc.getName(self), "__indirect_function_table")) { + continue; + } if (symbol.tag == .data or !symbol.requiresImport()) { continue; } @@ -1166,13 +1172,20 @@ fn setupExports(self: *Wasm) !void { if (!symbol.isExported()) continue; const sym_name = sym_loc.getName(self); - const export_name = if (self.export_names.get(sym_loc)) |name| name else symbol.name; + const export_name = if (self.export_names.get(sym_loc)) |name| name else blk: { + if (sym_loc.file == null) break :blk symbol.name; + break :blk try self.string_table.put(self.base.allocator, sym_name); + }; const exp: types.Export = .{ .name = export_name, .kind = symbol.tag.externalType(), .index = symbol.index, }; - log.debug("Exporting symbol '{s}' as '{s}' at index: ({d})", .{ sym_name, self.string_table.get(exp.name), exp.index }); + log.debug("Exporting symbol '{s}' as '{s}' at index: ({d})", .{ + sym_name, + self.string_table.get(exp.name), + exp.index, + }); try self.exports.append(self.base.allocator, exp); } @@ -1767,8 +1780,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { try leb.writeULEB128(writer, @as(u8, 0)); try leb.writeULEB128(writer, @intCast(u32, self.function_table.count())); var symbol_it = self.function_table.keyIterator(); - while (symbol_it.next()) |symbol_index_ptr| { - try leb.writeULEB128(writer, self.symbols.items[symbol_index_ptr.*].index); + while (symbol_it.next()) |symbol_loc_ptr| { + try leb.writeULEB128(writer, symbol_loc_ptr.*.getSymbol(self).index); } try writeVecSectionHeader( diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index a3e1c25190..fc45648d9a 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -158,7 +158,7 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa .R_WASM_TABLE_INDEX_I64, .R_WASM_TABLE_INDEX_SLEB, .R_WASM_TABLE_INDEX_SLEB64, - => return wasm_bin.function_table.get(relocation.index) orelse 0, + => return wasm_bin.function_table.get(target_loc) orelse 0, .R_WASM_TYPE_INDEX_LEB => return wasm_bin.functions.items[symbol.index].type_index, .R_WASM_GLOBAL_INDEX_I32, .R_WASM_GLOBAL_INDEX_LEB, diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index 471a91e927..1d6dabd9a7 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -851,15 +851,12 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin reloc.offset -= relocatable_data.offset; try atom.relocs.append(gpa, reloc); - // TODO: Automatically append the target symbol to the indirect - // function table when the relocation is a table index. - // - // if (relocation.isTableIndex()) { - // try wasm_bin.elements.appendSymbol(gpa, .{ - // .file = object_index, - // .sym_index = relocation.index, - // }); - // } + if (relocation.isTableIndex()) { + try wasm_bin.function_table.putNoClobber(gpa, .{ + .file = object_index, + .index = relocation.index, + }, 0); + } } } diff --git a/src/link/Wasm/types.zig b/src/link/Wasm/types.zig index 1dda5cdb5c..2c99f0f003 100644 --- a/src/link/Wasm/types.zig +++ b/src/link/Wasm/types.zig @@ -67,6 +67,18 @@ pub const Relocation = struct { }; } + /// Returns true when the relocation represents a table index relocatable + pub fn isTableIndex(self: Relocation) bool { + return switch (self.relocation_type) { + .R_WASM_TABLE_INDEX_I32, + .R_WASM_TABLE_INDEX_I64, + .R_WASM_TABLE_INDEX_SLEB, + .R_WASM_TABLE_INDEX_SLEB64, + => true, + else => false, + }; + } + pub fn format(self: Relocation, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { _ = fmt; _ = options; From d66c61a2cf69665223815a1a12a1f93b30b99571 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 13 Apr 2022 22:14:48 +0200 Subject: [PATCH 1146/2031] wasm-linker: Prevent overalignment for segments Previously, the data segments were being aligned twice. This caused us to overalign the segment and therefore allocate a much larger size for each segment than was required. This fix ensures we align and set the size just once, ensuring semantically correct binaries as well as smaller binaries. --- src/link/Wasm.zig | 17 +++++++---------- src/link/Wasm/Object.zig | 5 ----- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index f13e59dc5e..961e382112 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -962,11 +962,6 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { const segment: *Segment = &self.segments.items[final_index]; segment.alignment = std.math.max(segment.alignment, atom.alignment); - segment.size = std.mem.alignForwardGeneric( - u32, - std.mem.alignForwardGeneric(u32, segment.size, atom.alignment) + atom.size, - segment.alignment, - ); if (self.atoms.getPtr(final_index)) |last| { last.*.next = atom; @@ -978,9 +973,10 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { } fn allocateAtoms(self: *Wasm) !void { - var it = self.atoms.valueIterator(); - while (it.next()) |current_atom| { - var atom: *Atom = current_atom.*.getFirst(); + var it = self.atoms.iterator(); + while (it.next()) |entry| { + const segment = &self.segments.items[entry.key_ptr.*]; + var atom: *Atom = entry.value_ptr.*.getFirst(); var offset: u32 = 0; while (true) { offset = std.mem.alignForwardGeneric(u32, offset, atom.alignment); @@ -996,6 +992,7 @@ fn allocateAtoms(self: *Wasm) !void { self.symbol_atom.putAssumeCapacity(atom.symbolLoc(), atom); // Update atom pointers atom = atom.next orelse break; } + segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment); } } @@ -1566,8 +1563,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { try self.objects.items[object_index].parseIntoAtoms(self.base.allocator, object_index, self); } - try self.setupMemory(); try self.allocateAtoms(); + try self.setupMemory(); self.mapFunctionTable(); try self.mergeSections(); try self.mergeTypes(); @@ -1832,7 +1829,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { segment_count += 1; const atom_index = entry.value_ptr.*; var atom: *Atom = self.atoms.getPtr(atom_index).?.*.getFirst(); - var segment = self.segments.items[atom_index]; + const segment = self.segments.items[atom_index]; // flag and index to memory section (currently, there can only be 1 memory section in wasm) try leb.writeULEB128(writer, @as(u32, 0)); diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index 1d6dabd9a7..9f8c046275 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -865,11 +865,6 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index]; segment.alignment = std.math.max(segment.alignment, atom.alignment); - segment.size = std.mem.alignForwardGeneric( - u32, - std.mem.alignForwardGeneric(u32, segment.size, atom.alignment) + atom.size, - segment.alignment, - ); if (wasm_bin.atoms.getPtr(final_index)) |last| { last.*.next = atom; From 274e2a1ef1513bb2960becf60854e6e4d574ff5c Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 15 Apr 2022 05:59:55 +0700 Subject: [PATCH 1147/2031] compiler_rt: atomics: Add TAS lock support for SPARC Some SPARC CPUs (particularly old and/or embedded ones) only has atomic TAS instruction available (`ldstub`). This adds support for emitting that instruction in the spinlock. --- lib/std/special/compiler_rt/atomics.zig | 30 ++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/lib/std/special/compiler_rt/atomics.zig b/lib/std/special/compiler_rt/atomics.zig index 7727d7af3d..accec2e35a 100644 --- a/lib/std/special/compiler_rt/atomics.zig +++ b/lib/std/special/compiler_rt/atomics.zig @@ -24,6 +24,13 @@ const supports_atomic_ops = switch (arch) { // load/store atomically. // Objects bigger than this threshold require the use of a lock. const largest_atomic_size = switch (arch) { + // On SPARC systems that lacks CAS and/or swap instructions, the only + // available atomic operation is a test-and-set (`ldstub`), so we force + // every atomic memory access to go through the lock. + // XXX: Check the presence of CAS/swap instructions and set this parameter + // accordingly. + .sparc, .sparcel, .sparcv9 => 0, + // XXX: On x86/x86_64 we could check the presence of cmpxchg8b/cmpxchg16b // and set this parameter accordingly. else => @sizeOf(usize), @@ -38,18 +45,35 @@ const SpinlockTable = struct { const Spinlock = struct { // Prevent false sharing by providing enough padding between two // consecutive spinlock elements - v: enum(usize) { Unlocked = 0, Locked } align(cache_line_size) = .Unlocked, + v: if (arch.isSPARC()) enum(u8) { Unlocked = 0, Locked = 255 } else enum(usize) { Unlocked = 0, Locked } align(cache_line_size) = .Unlocked, fn acquire(self: *@This()) void { while (true) { - switch (@atomicRmw(@TypeOf(self.v), &self.v, .Xchg, .Locked, .Acquire)) { + const flag = if (comptime arch.isSPARC()) + asm volatile ("ldstub [%[addr]], %[flag]" + : [flag] "=r" (-> @TypeOf(self.v)), + : [addr] "r" (&self.v), + : "memory" + ) + else + @atomicRmw(@TypeOf(self.v), &self.v, .Xchg, .Locked, .Acquire); + + switch (flag) { .Unlocked => break, .Locked => {}, } } } fn release(self: *@This()) void { - @atomicStore(@TypeOf(self.v), &self.v, .Unlocked, .Release); + if (comptime arch.isSPARC()) { + _ = asm volatile ("clr [%[addr]]" + : + : [addr] "r" (&self.v), + : "memory" + ); + } else { + @atomicStore(@TypeOf(self.v), &self.v, .Unlocked, .Release); + } } }; From 4ef1c1c705ceb2d89d0bf93d7a2403c165ae9dc0 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 15 Apr 2022 10:24:18 +0300 Subject: [PATCH 1148/2031] Sema: allow fieldType on optionals and error unions --- src/Sema.zig | 46 +++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index e00abd660b..1429f8ca71 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -13071,22 +13071,38 @@ fn fieldType( ) CompileError!Air.Inst.Ref { const resolved_ty = try sema.resolveTypeFields(block, ty_src, aggregate_ty); const target = sema.mod.getTarget(); - switch (resolved_ty.zigTypeTag()) { - .Struct => { - const struct_obj = resolved_ty.castTag(.@"struct").?.data; - const field = struct_obj.fields.get(field_name) orelse - return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name); - return sema.addType(field.ty); - }, - .Union => { - const union_obj = resolved_ty.cast(Type.Payload.Union).?.data; - const field = union_obj.fields.get(field_name) orelse - return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); - return sema.addType(field.ty); - }, - else => return sema.fail(block, ty_src, "expected struct or union; found '{}'", .{ + var cur_ty = resolved_ty; + while (true) { + switch (cur_ty.zigTypeTag()) { + .Struct => { + const struct_obj = cur_ty.castTag(.@"struct").?.data; + const field = struct_obj.fields.get(field_name) orelse + return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name); + return sema.addType(field.ty); + }, + .Union => { + const union_obj = cur_ty.cast(Type.Payload.Union).?.data; + const field = union_obj.fields.get(field_name) orelse + return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); + return sema.addType(field.ty); + }, + .Optional => { + if (cur_ty.castTag(.optional)) |some| { + // Struct/array init through optional requires the child type to not be a pointer. + // If the child of .optional is a pointer it'll error on the next loop. + cur_ty = some.data; + continue; + } + }, + .ErrorUnion => { + cur_ty = cur_ty.errorUnionPayload(); + continue; + }, + else => {}, + } + return sema.fail(block, ty_src, "expected struct or union; found '{}'", .{ resolved_ty.fmt(target), - }), + }); } } From 4911d39769e3b9ccf07062751106aa388bc97e48 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 15 Apr 2022 10:41:35 +0300 Subject: [PATCH 1149/2031] AstGen: handle rl_ty_inst for mutable variables --- src/AstGen.zig | 5 +++++ test/behavior/basic.zig | 20 +++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index ccce4b0bc8..a5351f8769 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2851,6 +2851,9 @@ fn varDecl( return &sub_scope.base; }, .keyword_var => { + const old_rl_ty_inst = gz.rl_ty_inst; + defer gz.rl_ty_inst = old_rl_ty_inst; + const is_comptime = var_decl.comptime_token != null or gz.force_comptime; var resolve_inferred_alloc: Zir.Inst.Ref = .none; const var_data: struct { @@ -2875,6 +2878,7 @@ fn varDecl( }); } }; + gz.rl_ty_inst = type_inst; break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } }; } else a: { const alloc = alloc: { @@ -2894,6 +2898,7 @@ fn varDecl( }); } }; + gz.rl_ty_inst = .none; resolve_inferred_alloc = alloc; break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } }; }; diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 134ca1a235..96aa6900ee 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -859,7 +859,6 @@ test "catch in block has correct result location" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const S = struct { fn open() error{A}!@This() { @@ -887,3 +886,22 @@ test "labeled block with runtime branch forwards its result location type to bre }; try expect(e == .b); } + +test "try in labeled block doesn't cast to wrong type" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + + const S = struct { + a: u32, + fn foo() anyerror!u32 { + return 1; + } + }; + const s: ?*S = blk: { + var a = try S.foo(); + + _ = a; + break :blk null; + }; + _ = s; +} From 6ad510d83271a2b8452a645bfe128b1019aca165 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 15 Apr 2022 10:46:19 +0300 Subject: [PATCH 1150/2031] update self hosted sources to language changes --- lib/std/os/linux/x86_64.zig | 7 ++++++- src/arch/sparcv9/CodeGen.zig | 6 +++--- src/link/Wasm/Object.zig | 3 ++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/std/os/linux/x86_64.zig b/lib/std/os/linux/x86_64.zig index 8913d10585..fa09919094 100644 --- a/lib/std/os/linux/x86_64.zig +++ b/lib/std/os/linux/x86_64.zig @@ -100,7 +100,12 @@ pub fn syscall6( } /// This matches the libc clone function. -pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; +pub extern fn clone(func: CloneFn, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; + +const CloneFn = switch (@import("builtin").zig_backend) { + .stage1 => fn (arg: usize) callconv(.C) u8, + else => *const fn (arg: usize) callconv(.C) u8, +}; pub const restore = restore_rt; diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 7de035bc5c..71c41bc67d 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -624,9 +624,9 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const is_volatile = (extra.data.flags & 0x80000000) != 0; const clobbers_len = @truncate(u31, extra.data.flags); var extra_i: usize = extra.end; - const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i .. extra_i + extra.data.outputs_len]); + const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i .. extra_i + extra.data.outputs_len]); extra_i += outputs.len; - const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i .. extra_i + extra.data.inputs_len]); + const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i .. extra_i + extra.data.inputs_len]); extra_i += inputs.len; const dead = !is_volatile and self.liveness.isUnused(inst); @@ -826,7 +826,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const pl_op = self.air.instructions.items(.data)[inst].pl_op; const callee = pl_op.operand; const extra = self.air.extraData(Air.Call, pl_op.payload); - const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end .. extra.end + extra.data.args_len]); + const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end .. extra.end + extra.data.args_len]); const ty = self.air.typeOf(callee); const fn_ty = switch (ty.zigTypeTag()) { .Fn => ty, diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index 9f8c046275..8abf78d825 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -312,7 +312,8 @@ fn Parser(comptime ReaderType: type) type { var section_index: u32 = 0; while (self.reader.reader().readByte()) |byte| : (section_index += 1) { const len = try readLeb(u32, self.reader.reader()); - const reader = std.io.limitedReader(self.reader.reader(), len).reader(); + var limited_reader = std.io.limitedReader(self.reader.reader(), len); + const reader = limited_reader.reader(); switch (@intToEnum(std.wasm.Section, byte)) { .custom => { const name_len = try readLeb(u32, reader); From 845a30624ffc8fab884e2b7000daddb7d49eb5ff Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 15 Apr 2022 11:13:27 +0300 Subject: [PATCH 1151/2031] std: add workaround for stage2 bug --- lib/std/math/big/int.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 7ca9b9ccb7..614cf04f9b 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -2063,7 +2063,8 @@ pub const Const = struct { // This is the inverse of calcDivLimbsBufferLen const available_len = (limbs.len / 3) - 2; - const biggest: Const = .{ + // TODO https://github.com/ziglang/zig/issues/11439 + const biggest = comptime Const{ .limbs = &([1]Limb{math.maxInt(Limb)} ** available_len), .positive = false, }; From 618398b7d3c0df13dcb3d87540e400665b2c02dc Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 6 Apr 2022 23:48:30 -0700 Subject: [PATCH 1152/2031] std.fs: prevent possible integer overflow in Dir.makePath The call to `makeDir` for the top-level component of `sub_path` can return `error.FileNotFound` if the directory represented by `self` has been deleted. Fixes #11397 --- lib/std/fs.zig | 2 +- lib/std/fs/test.zig | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 73efccbbfc..052343599e 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -1308,9 +1308,9 @@ pub const Dir = struct { if (end_index == sub_path.len) return; }, error.FileNotFound => { - if (end_index == 0) return err; // march end_index backward until next path component while (true) { + if (end_index == 0) return err; end_index -= 1; if (path.isSep(sub_path[end_index])) break; } diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 2899d0412e..82005152e5 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -610,6 +610,16 @@ test "makePath, put some files in it, deleteTree" { } } +test "makePath in a directory that no longer exists" { + if (builtin.os.tag == .windows) return error.SkipZigTest; // Windows returns FileBusy if attempting to remove an open dir + + var tmp = tmpDir(.{}); + defer tmp.cleanup(); + try tmp.parent_dir.deleteTree(&tmp.sub_path); + + try testing.expectError(error.FileNotFound, tmp.dir.makePath("sub-path")); +} + test "writev, readv" { var tmp = tmpDir(.{}); defer tmp.cleanup(); From 62d717e2ffb1e9a1127652521de57c2e18cf7d3b Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Wed, 13 Apr 2022 18:28:49 +0200 Subject: [PATCH 1153/2031] Add `std.unicode.replacement_character` --- lib/std/fmt.zig | 6 +++--- lib/std/unicode.zig | 10 +++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 3f230a445e..80a2e87c5c 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -966,10 +966,10 @@ pub fn formatUnicodeCodepoint( writer: anytype, ) !void { var buf: [4]u8 = undefined; - const len = std.unicode.utf8Encode(c, &buf) catch |err| switch (err) { + const len = unicode.utf8Encode(c, &buf) catch |err| switch (err) { error.Utf8CannotEncodeSurrogateHalf, error.CodepointTooLarge => { - // In case of error output the replacement char U+FFFD - return formatBuf(&[_]u8{ 0xef, 0xbf, 0xbd }, options, writer); + const len = unicode.utf8Encode(unicode.replacement_character, &buf) catch unreachable; + return formatBuf(buf[0..len], options, writer); }, }; return formatBuf(buf[0..len], options, writer); diff --git a/lib/std/unicode.zig b/lib/std/unicode.zig index 487f3defdf..81a7ed838f 100644 --- a/lib/std/unicode.zig +++ b/lib/std/unicode.zig @@ -3,6 +3,11 @@ const assert = std.debug.assert; const testing = std.testing; const mem = std.mem; +/// Use this to replace an unknown, unrecognized, or unrepresentable character. +/// +/// See also: https://en.wikipedia.org/wiki/Specials_(Unicode_block)#Replacement_character +pub const replacement_character: u21 = 0xFFFD; + /// Returns how many bytes the UTF-8 representation would require /// for the given codepoint. pub fn utf8CodepointSequenceLength(c: u21) !u3 { @@ -777,15 +782,14 @@ fn formatUtf16le( options: std.fmt.FormatOptions, writer: anytype, ) !void { - const unknown_codepoint = 0xfffd; _ = fmt; _ = options; var buf: [300]u8 = undefined; // just a random size I chose var it = Utf16LeIterator.init(utf16le); var u8len: usize = 0; - while (it.nextCodepoint() catch unknown_codepoint) |codepoint| { + while (it.nextCodepoint() catch replacement_character) |codepoint| { u8len += utf8Encode(codepoint, buf[u8len..]) catch - utf8Encode(unknown_codepoint, buf[u8len..]) catch unreachable; + utf8Encode(replacement_character, buf[u8len..]) catch unreachable; if (u8len + 3 >= buf.len) { try writer.writeAll(buf[0..u8len]); u8len = 0; From d9f9948b6536339747322b8a04431116b698892e Mon Sep 17 00:00:00 2001 From: iddev5 Date: Mon, 4 Apr 2022 18:34:19 +0530 Subject: [PATCH 1154/2031] std.build: add test for issue 10381 --- lib/std/build.zig | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/lib/std/build.zig b/lib/std/build.zig index f1287c7be5..902849a379 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -3492,3 +3492,58 @@ test "LibExeObjStep.addPackage" { const dupe = exe.packages.items[0]; try std.testing.expectEqualStrings(pkg_top.name, dupe.name); } + +test "build_runner issue 10381" { + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + const progstr = + \\ pub fn main() u8 { + \\ return 1; + \\ } + ; + + const buildstr = + \\ const std = @import("std"); + \\ pub fn build(b: *std.build.Builder) void { + \\ const exe = b.addExecutable("source", "source.zig"); + \\ exe.install(); + \\ const run_cmd = exe.run(); + \\ run_cmd.step.dependOn(b.getInstallStep()); + \\ const run_step = b.step("run", "Run"); + \\ run_step.dependOn(&run_cmd.step); + \\ } + ; + + const testing = std.testing; + const allocator = testing.allocator; + + var it = try std.process.argsWithAllocator(allocator); + defer it.deinit(); + const testargs = try testing.getTestArgs(&it); + + var tmpdir = testing.tmpDir(.{ .no_follow = true }); + defer tmpdir.cleanup(); + const tmpdir_path = try tmpdir.getFullPath(allocator); + defer allocator.free(tmpdir_path); + + try tmpdir.dir.writeFile("source.zig", progstr); + try tmpdir.dir.writeFile("build.zig", buildstr); + + const cwd_path = try std.process.getCwdAlloc(allocator); + defer allocator.free(cwd_path); + const lib_dir = try std.fs.path.join(allocator, &.{ cwd_path, "lib" }); + defer allocator.free(lib_dir); + + const result = try testing.runZigBuild(testargs.zigexec, .{ + .subcmd = "run", + .cwd = tmpdir_path, + .lib_dir = lib_dir, + }); + defer { + allocator.free(result.stdout); + allocator.free(result.stderr); + } + + try testing.expectEqual(result.term, .{ .Exited = 1 }); + try testing.expect(std.mem.indexOf(u8, result.stderr, "error: UnexpectedExitCode") == null); +} From 6aa89115f974b639d6006d8106a7df7d94f636ac Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 15 Apr 2022 19:31:55 +0700 Subject: [PATCH 1155/2031] ompiler_rt: atomics: Split long lines and add comment on constants --- lib/std/special/compiler_rt/atomics.zig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/std/special/compiler_rt/atomics.zig b/lib/std/special/compiler_rt/atomics.zig index accec2e35a..aa045a2ab9 100644 --- a/lib/std/special/compiler_rt/atomics.zig +++ b/lib/std/special/compiler_rt/atomics.zig @@ -43,9 +43,15 @@ const SpinlockTable = struct { const max_spinlocks = 64; const Spinlock = struct { + // SPARC ldstub instruction will write a 255 into the memory location. + // We'll use that as a sign that the lock is currently held. + // See also: Section B.7 in SPARCv8 spec & A.29 in SPARCv9 spec. + const sparc_lock: type = enum(u8) { Unlocked = 0, Locked = 255 }; + const other_lock: type = enum(usize) { Unlocked = 0, Locked }; + // Prevent false sharing by providing enough padding between two // consecutive spinlock elements - v: if (arch.isSPARC()) enum(u8) { Unlocked = 0, Locked = 255 } else enum(usize) { Unlocked = 0, Locked } align(cache_line_size) = .Unlocked, + v: if (arch.isSPARC()) sparc_lock else other_lock align(cache_line_size) = .Unlocked, fn acquire(self: *@This()) void { while (true) { From 5b283fba77d50b48d9b9895fac36ee3e1844bd1c Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 15 Apr 2022 19:40:36 +0700 Subject: [PATCH 1156/2031] compiler_rt: atomics: Add Leon CAS instruction check for SPARC atomics --- lib/std/special/compiler_rt/atomics.zig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/std/special/compiler_rt/atomics.zig b/lib/std/special/compiler_rt/atomics.zig index aa045a2ab9..07b15c3102 100644 --- a/lib/std/special/compiler_rt/atomics.zig +++ b/lib/std/special/compiler_rt/atomics.zig @@ -1,6 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); -const arch = builtin.cpu.arch; +const cpu = builtin.cpu; +const arch = cpu.arch; const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; @@ -27,9 +28,7 @@ const largest_atomic_size = switch (arch) { // On SPARC systems that lacks CAS and/or swap instructions, the only // available atomic operation is a test-and-set (`ldstub`), so we force // every atomic memory access to go through the lock. - // XXX: Check the presence of CAS/swap instructions and set this parameter - // accordingly. - .sparc, .sparcel, .sparcv9 => 0, + .sparc, .sparcel => if (cpu.features.featureSetHas(.hasleoncasa)) @sizeOf(usize) else 0, // XXX: On x86/x86_64 we could check the presence of cmpxchg8b/cmpxchg16b // and set this parameter accordingly. From fac2a2e7545db8d814baf2f011acee24bf9f95ee Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 15 Apr 2022 19:44:46 +0700 Subject: [PATCH 1157/2031] compiler_rt: atomics: Formatting change for flag definition --- lib/std/special/compiler_rt/atomics.zig | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/std/special/compiler_rt/atomics.zig b/lib/std/special/compiler_rt/atomics.zig index 07b15c3102..a60fcbbecd 100644 --- a/lib/std/special/compiler_rt/atomics.zig +++ b/lib/std/special/compiler_rt/atomics.zig @@ -54,14 +54,15 @@ const SpinlockTable = struct { fn acquire(self: *@This()) void { while (true) { - const flag = if (comptime arch.isSPARC()) - asm volatile ("ldstub [%[addr]], %[flag]" + const flag = if (comptime arch.isSPARC()) flag: { + break :flag asm volatile ("ldstub [%[addr]], %[flag]" : [flag] "=r" (-> @TypeOf(self.v)), : [addr] "r" (&self.v), : "memory" - ) - else - @atomicRmw(@TypeOf(self.v), &self.v, .Xchg, .Locked, .Acquire); + ); + } else flag: { + break :flag @atomicRmw(@TypeOf(self.v), &self.v, .Xchg, .Locked, .Acquire); + }; switch (flag) { .Unlocked => break, From 33956b8e558b1c422dab8f91643c0a9caae84501 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 15 Apr 2022 19:48:25 +0700 Subject: [PATCH 1158/2031] compiler_rt: atomics: clr -> clrb --- lib/std/special/compiler_rt/atomics.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/special/compiler_rt/atomics.zig b/lib/std/special/compiler_rt/atomics.zig index a60fcbbecd..20545d0791 100644 --- a/lib/std/special/compiler_rt/atomics.zig +++ b/lib/std/special/compiler_rt/atomics.zig @@ -72,7 +72,7 @@ const SpinlockTable = struct { } fn release(self: *@This()) void { if (comptime arch.isSPARC()) { - _ = asm volatile ("clr [%[addr]]" + _ = asm volatile ("clrb [%[addr]]" : : [addr] "r" (&self.v), : "memory" From ef7282bab41fdc95c4aee973de2ba595ca8195d2 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 15 Apr 2022 16:05:27 +0300 Subject: [PATCH 1159/2031] stage2 macho: workaround stage2 bugs --- src/Sema.zig | 3 ++- src/link/MachO.zig | 11 +++++++---- src/link/MachO/Archive.zig | 1 - src/link/MachO/DebugSymbols.zig | 3 ++- src/link/MachO/Dylib.zig | 2 +- src/link/MachO/Object.zig | 3 ++- 6 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 1429f8ca71..68cca08b95 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12417,7 +12417,8 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const tracy = trace(@src()); defer tracy.end(); - const src: LazySrcLoc = .unneeded; + // TODO better source location + const src: LazySrcLoc = sema.src; const elem_ty_src: LazySrcLoc = .unneeded; const inst_data = sema.code.instructions.items(.data)[inst].ptr_type; const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index d359a3fd5d..fa1ba2835a 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1317,6 +1317,9 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy error.EndOfStream, error.NotDylib => { try file.seekTo(0); + // TODO https://github.com/ziglang/zig/issues/11367 + if (@import("builtin").zig_backend != .stage1) return error.Unexpected; + var lib_stub = LibStub.loadFromFile(self.base.allocator, file) catch { dylib.deinit(self.base.allocator); return false; @@ -5747,7 +5750,6 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { while (true) { const inst = reader.readByte() catch |err| switch (err) { error.EndOfStream => break, - else => return err, }; const opcode: u8 = inst & macho.BIND_OPCODE_MASK; @@ -5875,7 +5877,7 @@ fn writeFunctionStarts(self: *MachO) !void { mem.set(u8, buffer, 0); var stream = std.io.fixedBufferStream(buffer); - var writer = stream.writer(); + const writer = stream.writer(); for (offsets.items) |offset| { try std.leb.writeULEB128(writer, offset); @@ -6236,7 +6238,8 @@ fn writeLoadCommands(self: *MachO) !void { var buffer = try self.base.allocator.alloc(u8, sizeofcmds); defer self.base.allocator.free(buffer); - var writer = std.io.fixedBufferStream(buffer).writer(); + var fib = std.io.fixedBufferStream(buffer); + const writer = fib.writer(); for (self.load_commands.items) |lc| { try lc.write(writer); } @@ -6416,7 +6419,7 @@ fn snapshotState(self: *MachO) !void { error.Unseekable => try out_file.writer().writeByte('['), else => |e| return e, } - var writer = out_file.writer(); + const writer = out_file.writer(); var snapshot = Snapshot{ .timestamp = std.time.nanoTimestamp(), diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index 1ebb122423..e8d981b4ae 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -171,7 +171,6 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) ! while (true) { const n_strx = symtab_reader.readIntLittle(u32) catch |err| switch (err) { error.EndOfStream => break, - else => |e| return e, }; const object_offset = try symtab_reader.readIntLittle(u32); diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index aa7a29fcd1..5fac857422 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -472,7 +472,8 @@ fn writeLoadCommands(self: *DebugSymbols, allocator: Allocator) !void { var buffer = try allocator.alloc(u8, sizeofcmds); defer allocator.free(buffer); - var writer = std.io.fixedBufferStream(buffer).writer(); + var fib = std.io.fixedBufferStream(buffer); + const writer = fib.writer(); for (self.load_commands.items) |lc| { try lc.write(writer); } diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 7593593cbc..26bac50144 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -64,7 +64,7 @@ pub const Id = struct { }; } - pub fn deinit(id: *Id, allocator: Allocator) void { + pub fn deinit(id: Id, allocator: Allocator) void { allocator.free(id.name); } diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 255d7053d4..c8ebb4b8b5 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -492,7 +492,8 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) ! mem.copy(u8, atom.code.items, code); } - try atom.parseRelocs(relocs, .{ + // TODO stage2 bug: @alignCast shouldn't be needed + try atom.parseRelocs(@alignCast(@alignOf(macho.relocation_info), relocs), .{ .base_addr = sect.addr, .allocator = allocator, .object = self, From dbe0d3d5790d9e21cf42696d4cea8cc477207592 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 15 Apr 2022 16:16:22 +0300 Subject: [PATCH 1160/2031] stage2 llvm: handle dollar signs in asm template --- src/codegen/llvm.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index f55d326a47..a9e1cb160b 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4637,6 +4637,7 @@ pub const FuncGen = struct { switch (state) { .start => switch (byte) { '%' => state = .percent, + '$' => try rendered_template.appendSlice("$$"), else => try rendered_template.append(byte), }, .percent => switch (byte) { From 52c8ac1a847f893bc54cab5621738c0aa16f7c9d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 15 Apr 2022 13:46:51 +0200 Subject: [PATCH 1161/2031] stage2: lower u128, and refactor some bits in x64 --- src/arch/x86_64/CodeGen.zig | 25 ++++++++++++++----------- src/codegen.zig | 18 ++++++++---------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 10271df122..f2eb1e2afd 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -155,6 +155,8 @@ pub const MCValue = union(enum) { .memory, .stack_offset, .ptr_stack_offset, + .direct_load, + .got_load, => true, else => false, }; @@ -3131,20 +3133,19 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: } fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { - const abi_size = dst_ty.abiSize(self.target.*); + const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); switch (dst_mcv) { .none => unreachable, .undef => unreachable, .dead, .unreach, .immediate => unreachable, .compare_flags_unsigned => unreachable, .compare_flags_signed => unreachable, - .ptr_stack_offset => unreachable, .register_overflow_unsigned => unreachable, .register_overflow_signed => unreachable, .register => |dst_reg| { switch (src_mcv) { .none => unreachable, - .undef => try self.genSetReg(dst_ty, dst_reg, .undef), + .undef => unreachable, .dead, .unreach => unreachable, .register_overflow_unsigned => unreachable, .register_overflow_signed => unreachable, @@ -3168,7 +3169,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC _ = try self.addInst(.{ .tag = mir_tag, .ops = (Mir.Ops{ - .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)), + .reg1 = registerAlias(dst_reg, abi_size), }).encode(), .data = .{ .imm = @truncate(u32, imm) }, }); @@ -3192,7 +3193,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC _ = try self.addInst(.{ .tag = mir_tag, .ops = (Mir.Ops{ - .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)), + .reg1 = registerAlias(dst_reg, abi_size), .reg2 = .rbp, .flags = 0b01, }).encode(), @@ -3201,19 +3202,18 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC }, } }, - .stack_offset => |off| { + .ptr_stack_offset, .stack_offset => |off| { if (off > math.maxInt(i32)) { return self.fail("stack offset too large", .{}); } if (abi_size > 8) { - return self.fail("TODO implement ADD/SUB/CMP for stack dst with large ABI", .{}); + return self.fail("TODO implement {} for stack dst with large ABI", .{mir_tag}); } switch (src_mcv) { .none => unreachable, - .undef => return self.genSetStack(dst_ty, off, .undef, .{}), + .undef => unreachable, .dead, .unreach => unreachable, - .ptr_stack_offset => unreachable, .register_overflow_unsigned => unreachable, .register_overflow_signed => unreachable, .register => |src_reg| { @@ -3221,7 +3221,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .tag = mir_tag, .ops = (Mir.Ops{ .reg1 = .rbp, - .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)), + .reg2 = registerAlias(src_reg, abi_size), .flags = 0b10, }).encode(), .data = .{ .imm = @bitCast(u32, -off) }, @@ -3257,7 +3257,10 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .data = .{ .payload = payload }, }); }, - .memory, .stack_offset => { + .memory, + .stack_offset, + .ptr_stack_offset, + => { return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{}); }, .got_load, .direct_load => { diff --git a/src/codegen.zig b/src/codegen.zig index 68e1f3697f..7fa0b2c0f8 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -426,22 +426,20 @@ pub fn generateSymbol( }, }, .Int => { - // TODO populate .debug_info for the integer - const info = typed_value.ty.intInfo(bin_file.options.target); + const info = typed_value.ty.intInfo(target); if (info.bits <= 8) { const x = @intCast(u8, typed_value.val.toUnsignedInt(target)); try code.append(x); return Result{ .appended = {} }; } if (info.bits > 64) { - return Result{ - .fail = try ErrorMsg.create( - bin_file.allocator, - src_loc, - "TODO implement generateSymbol for big ints ('{}')", - .{typed_value.ty.fmtDebug()}, - ), - }; + var bigint_buffer: Value.BigIntSpace = undefined; + const bigint = typed_value.val.toBigInt(&bigint_buffer, target); + const abi_size = try math.cast(usize, typed_value.ty.abiSize(target)); + const start = code.items.len; + try code.resize(start + abi_size); + bigint.writeTwosComplement(code.items[start..][0..abi_size], info.bits, abi_size, endian); + return Result{ .appended = {} }; } switch (info.signedness) { .unsigned => { From 02a43f325bfbccb73fb773f15b162d531c3c3ada Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Thu, 14 Apr 2022 07:42:49 -0300 Subject: [PATCH 1162/2031] std/math.zig: resolve missed optimization in rotates --- lib/std/math.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/std/math.zig b/lib/std/math.zig index be49ba8030..387faf4c97 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -548,8 +548,8 @@ pub fn rotr(comptime T: type, x: T, r: anytype) T { } else if (@typeInfo(T).Int.signedness == .signed) { @compileError("cannot rotate signed integer"); } else { - const ar = @mod(r, @typeInfo(T).Int.bits); - return shr(T, x, ar) | shl(T, x, @typeInfo(T).Int.bits - ar); + const ar = @intCast(Log2Int(T), @mod(r, @typeInfo(T).Int.bits)); + return x >> ar | x << (1 +% ~ar); } } @@ -576,8 +576,8 @@ pub fn rotl(comptime T: type, x: T, r: anytype) T { } else if (@typeInfo(T).Int.signedness == .signed) { @compileError("cannot rotate signed integer"); } else { - const ar = @mod(r, @typeInfo(T).Int.bits); - return shl(T, x, ar) | shr(T, x, @typeInfo(T).Int.bits - ar); + const ar = @intCast(Log2Int(T), @mod(r, @typeInfo(T).Int.bits)); + return x << ar | x >> 1 +% ~ar; } } From 7be62f695ffb468e19241a564d544d6a0adab829 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 15 Apr 2022 19:17:50 +0300 Subject: [PATCH 1163/2031] stage2 llvm: fix optional pointers to zero bit payloads --- lib/std/special/c.zig | 6 ++---- src/codegen/llvm.zig | 25 +++++++++++++++++++----- test/behavior/optional.zig | 39 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index d0639463a0..dfc2020334 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -32,10 +32,8 @@ comptime { @export(wasm_start, .{ .name = "_start", .linkage = .Strong }); } - if (builtin.zig_backend == .stage1) { // TODO remove this condition - if (native_os == .linux) { - @export(clone, .{ .name = "clone" }); - } + if (native_os == .linux) { + @export(clone, .{ .name = "clone" }); } @export(memset, .{ .name = "memset", .linkage = .Strong }); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a9e1cb160b..bb7554d363 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4732,10 +4732,11 @@ pub const FuncGen = struct { var buf: Type.Payload.ElemType = undefined; const payload_ty = optional_ty.optionalChild(&buf); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand; if (invert) { - return self.builder.buildNot(operand, ""); + return self.builder.buildNot(loaded, ""); } else { - return operand; + return loaded; } } @@ -4784,12 +4785,16 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); const optional_ty = self.air.typeOf(ty_op.operand).childType(); + const result_ty = self.air.getRefType(ty_op.ty); var buf: Type.Payload.ElemType = undefined; const payload_ty = optional_ty.optionalChild(&buf); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { // We have a pointer to a zero-bit value and we need to return // a pointer to a zero-bit value. - return operand; + + // TODO once we update to LLVM 14 this bitcast won't be necessary. + const res_ptr_ty = try self.dg.llvmType(result_ty); + return self.builder.buildBitCast(operand, res_ptr_ty, ""); } if (optional_ty.isPtrLikeOptional()) { // The payload and the optional are the same value. @@ -4807,13 +4812,17 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); const optional_ty = self.air.typeOf(ty_op.operand).childType(); + const result_ty = self.air.getRefType(ty_op.ty); var buf: Type.Payload.ElemType = undefined; const payload_ty = optional_ty.optionalChild(&buf); const non_null_bit = self.context.intType(1).constAllOnes(); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { // We have a pointer to a i1. We need to set it to 1 and then return the same pointer. _ = self.builder.buildStore(non_null_bit, operand); - return operand; + + // TODO once we update to LLVM 14 this bitcast won't be necessary. + const res_ptr_ty = try self.dg.llvmType(result_ty); + return self.builder.buildBitCast(operand, res_ptr_ty, ""); } if (optional_ty.isPtrLikeOptional()) { // The payload and the optional are the same value. @@ -4872,7 +4881,13 @@ pub const FuncGen = struct { const target = self.dg.module.getTarget(); const offset: u8 = if (payload_ty.abiAlignment(target) > Type.anyerror.abiSize(target)) 2 else 1; - if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return null; + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + if (!operand_is_ptr) return null; + + // TODO once we update to LLVM 14 this bitcast won't be necessary. + const res_ptr_ty = try self.dg.llvmType(result_ty); + return self.builder.buildBitCast(operand, res_ptr_ty, ""); + } if (operand_is_ptr or isByRef(payload_ty)) { return self.builder.buildStructGEP(operand, offset, ""); } diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index 9956c3f3ec..6219a6a5be 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -332,3 +332,42 @@ test "array of optional unaligned types" { i += 1; try expect(Enum.three == values[i].?.Num); } + +test "optional pointer to zero bit optional payload" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const B = struct { + fn foo(_: *@This()) void {} + }; + const A = struct { + b: ?B = .{}, + }; + var a: A = .{}; + var a_ptr = &a; + if (a_ptr.b) |*some| { + some.foo(); + } +} + +test "optional pointer to zero bit error union payload" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + const B = struct { + fn foo(_: *@This()) void {} + }; + const A = struct { + b: anyerror!B = .{}, + }; + var a: A = .{}; + var a_ptr = &a; + if (a_ptr.b) |*some| { + some.foo(); + } else |_| {} +} From 1c4c826a505b3f4863a0125f7915273ad2e7c1c0 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 15 Apr 2022 20:43:07 +0300 Subject: [PATCH 1164/2031] AstGen: fix defer generation in `breakExpr` --- src/AstGen.zig | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index a5351f8769..34b29b28fb 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1807,6 +1807,8 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn .@"break"; if (rhs == 0) { + try genDefers(parent_gz, scope, parent_scope, .normal_only); + _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); return Zir.Inst.Ref.unreachable_value; } @@ -1819,12 +1821,15 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn const prev_rvalue_noresult = parent_gz.rvalue_noresult; parent_gz.rvalue_noresult = .none; const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_loc, rhs, node); + const search_index = @intCast(Zir.Inst.Index, astgen.instructions.len); parent_gz.rvalue_noresult = prev_rvalue_noresult; + try genDefers(parent_gz, scope, parent_scope, .normal_only); + switch (block_gz.break_result_loc) { .block_ptr => { const br = try parent_gz.addBreak(break_tag, block_inst, operand); - try block_gz.labeled_breaks.append(astgen.gpa, br); + try block_gz.labeled_breaks.append(astgen.gpa, .{ .br = br, .search = search_index }); }, .ptr => { // In this case we don't have any mechanism to intercept it; @@ -1843,13 +1848,7 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, .namespace => break, - .defer_normal => { - const defer_scope = scope.cast(Scope.Defer).?; - scope = defer_scope.parent; - const expr_node = node_datas[defer_scope.defer_node].rhs; - try unusedResultDeferExpr(parent_gz, defer_scope, defer_scope.parent, expr_node); - }, - .defer_error => scope = scope.cast(Scope.Defer).?.parent, + .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, .top => unreachable, } } @@ -2030,7 +2029,7 @@ fn labeledBlockExpr( // The code took advantage of the result location as a pointer. // Turn the break instruction operands into void. for (block_scope.labeled_breaks.items) |br| { - zir_datas[br].@"break".operand = .void_value; + zir_datas[br.br].@"break".operand = .void_value; } try block_scope.setBlockBody(block_inst); @@ -2047,17 +2046,17 @@ fn labeledBlockExpr( for (block_scope.labeled_breaks.items) |br| { // We expect the `store_to_block_ptr` to be created between 1-3 instructions // prior to the break. - var search_index = br -| 3; - while (search_index < br) : (search_index += 1) { + var search_index = br.search -| 3; + while (search_index < br.search) : (search_index += 1) { if (zir_tags[search_index] == .store_to_block_ptr and zir_datas[search_index].bin.lhs == block_scope.rl_ptr) { zir_tags[search_index] = .as; zir_datas[search_index].bin = .{ .lhs = block_scope.rl_ty_inst, - .rhs = zir_datas[br].@"break".operand, + .rhs = zir_datas[br.br].@"break".operand, }; - zir_datas[br].@"break".operand = indexToRef(search_index); + zir_datas[br.br].@"break".operand = indexToRef(search_index); break; } } else unreachable; @@ -9719,7 +9718,7 @@ const GenZir = struct { break_count: usize = 0, /// Tracks `break :foo bar` instructions so they can possibly be elided later if /// the labeled block ends up not needing a result location pointer. - labeled_breaks: ArrayListUnmanaged(Zir.Inst.Index) = .{}, + labeled_breaks: ArrayListUnmanaged(struct { br: Zir.Inst.Index, search: Zir.Inst.Index }) = .{}, suspend_node: Ast.Node.Index = 0, nosuspend_node: Ast.Node.Index = 0, From 7284eb22dce8086674c449ccf434ecdced9e4e0d Mon Sep 17 00:00:00 2001 From: kprotty Date: Fri, 15 Apr 2022 15:26:51 -0500 Subject: [PATCH 1165/2031] treap: initial implementation --- CMakeLists.txt | 1 + lib/std/std.zig | 1 + lib/std/treap.zig | 294 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 296 insertions(+) create mode 100644 lib/std/treap.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 81b96d67ab..9c4982ea83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -539,6 +539,7 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/Thread/ResetEvent.zig" "${CMAKE_SOURCE_DIR}/lib/std/Thread/StaticResetEvent.zig" "${CMAKE_SOURCE_DIR}/lib/std/time.zig" + "${CMAKE_SOURCE_DIR}/lib/std/treap.zig" "${CMAKE_SOURCE_DIR}/lib/std/unicode.zig" "${CMAKE_SOURCE_DIR}/lib/std/zig.zig" "${CMAKE_SOURCE_DIR}/lib/std/zig/Ast.zig" diff --git a/lib/std/std.zig b/lib/std/std.zig index f34d108271..831a82cbbe 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -39,6 +39,7 @@ pub const StringArrayHashMapUnmanaged = array_hash_map.StringArrayHashMapUnmanag pub const TailQueue = @import("linked_list.zig").TailQueue; pub const Target = @import("target.zig").Target; pub const Thread = @import("Thread.zig"); +pub const Treap = @import("treap.zig").Treap; pub const Tz = @import("tz.zig").Tz; pub const array_hash_map = @import("array_hash_map.zig"); diff --git a/lib/std/treap.zig b/lib/std/treap.zig new file mode 100644 index 0000000000..27c5285c34 --- /dev/null +++ b/lib/std/treap.zig @@ -0,0 +1,294 @@ +const std = @import("std.zig"); +const assert = std.debug.assert; +const testing = std.testing; +const Order = std.math.Order; + +pub fn Treap(comptime Key: type, comptime compareFn: anytype) type { + return struct { + const Self = @This(); + + // Allow for compareFn to be fn(anytype, anytype) anytype + // which allows the convenient use of std.math.order. + fn compare(a: Key, b: Key) Order { + return compareFn(a, b); + } + + root: ?*Node = null, + prng: Prng = .{}, + + /// A customized pseudo random number generator for the treap. + /// This just helps reducing the memory size of the treap itself + /// as std.rand.DefaultPrng requires larger state (while producing better entropy for randomness to be fair). + const Prng = struct { + xorshift: usize = 0, + + fn random(self: *Prng, seed: usize) usize { + // Lazily seed the prng state + if (self.xorshift == 0) { + self.xorshift = seed; + } + + // Since we're using usize, decide the shifts by the integer's bit width. + const shifts = switch (@bitSizeOf(usize)) { + 64 => .{13, 7, 17}, + 32 => .{13, 17, 5}, + 16 => .{7, 9, 8}, + else => @compileError("platform not supported"), + }; + + self.xorshift ^= self.xorshift >> shifts[0]; + self.xorshift ^= self.xorshift << shifts[1]; + self.xorshift ^= self.xorshift >> shifts[2]; + + assert(self.xorshift != 0); + return self.xorshift; + } + }; + + /// A Node represents an item or point in the treap with a uniquely associated key. + pub const Node = struct { + key: Key, + priority: usize, + parent: ?*Node, + children: [2]?*Node, + }; + + /// Returns the smallest Node by key in the treap if there is one. + /// Use `getEntryForExisting()` to replace/remove this Node from the treap. + pub fn getMin(self: Self) ?*Node { + var node = self.root; + while (node) |current| { + node = current.children[0] orelse break; + } + return node; + } + + /// Returns the largest Node by key in the treap if there is one. + /// Use `getEntryForExisting()` to replace/remove this Node from the treap. + pub fn getMax(self: Self) ?*Node { + var node = self.root; + while (node) |current| { + node = current.children[1] orelse break; + } + return node; + } + + /// Lookup the Entry for the given key in the treap. + /// The Entry act's as a slot in the treap to insert/replace/remove the node associated with the key. + pub fn getEntryFor(self: *Self, key: Key) Entry { + var parent: ?*Node = undefined; + const node = self.find(key, &parent); + + return Entry{ + .key = key, + .treap = self, + .node = node, + .context = .{ .inserted_under = parent }, + }; + } + + /// Get an entry for a Node that currently exists in the treap. + /// It is undefined behavior if the Node is not currently inserted in the treap. + /// The Entry act's as a slot in the treap to insert/replace/remove the node associated with the key. + pub fn getEntryForExisting(self: *Self, node: *Node) Entry { + assert(node.priority != 0); + + return Entry{ + .key = node.key, + .treap = self, + .node = node, + .context = .{ .inserted_under = node.parent }, + }; + } + + /// An Entry represents a slot in the treap associated with a given key. + pub const Entry = struct { + key: Key, + treap: *Self, + node: ?*Node, + context: union(enum) { + /// A find() was called for this entry and the position in the treap is known. + inserted_under: ?*Node, + /// The entry's node was removed from the treap and a lookup must occur again for modification. + removed, + }, + + /// Returns the current Node at this Entry in the treap if there is one. + pub fn get(self: Entry) ?*Node { + return self.node; + } + + /// Update's the Node at this Entry in the treap with the new node. + pub fn set(self: *Entry, new_node: ?*Node) void { + // Update the entry's node reference after updating the treap below. + defer self.node = new_node; + + if (self.node) |old| { + if (new_node) |new| { + self.treap.replace(old, new); + return; + } + + self.treap.remove(old); + self.context = .removed; + return; + } + + if (new_node) |new| { + // A previous treap.remove() could have rebalanced the nodes + // so when inserting after a removal, we have to re-lookup the parent again. + // This lookup shouldn't find a node because we're yet to insert it.. + var parent: ?*Node = undefined; + switch (self.context) { + .inserted_under => |p| parent = p, + .removed => assert(self.treap.find(self.key, &parent) == null), + } + + self.treap.insert(self.key, parent, new); + self.context = .{ .inserted_under = parent }; + } + } + }; + + fn find(self: Self, key: Key, parent_ref: *?*Node) ?*Node { + var node = self.root; + parent_ref.* = null; + + // basic binary search while tracking the parent. + while (node) |current| { + const order = compare(key, current.key); + if (order == .eq) break; + + parent_ref.* = current; + node = current.children[@boolToInt(order == .gt)]; + } + + return node; + } + + fn insert(self: *Self, key: Key, parent: ?*Node, node: *Node) void { + // generate a random priority & prepare the node to be inserted into the tree + node.key = key; + node.priority = self.prng.random(@ptrToInt(node)); + node.parent = parent; + node.children = [_]?*Node{ null, null }; + + // point the parent at the new node + const link = if (parent) |p| &p.children[@boolToInt(compare(key, p.key) == .gt)] else &self.root; + assert(link.* == null); + link.* = node; + + // rotate the node up into the tree to balance it according to its priority + while (node.parent) |p| { + if (p.priority <= node.priority) break; + + const is_right = p.children[1] == @as(?*Node, node); + assert(p.children[@boolToInt(is_right)] == node); + + const rotate_right = !is_right; + self.rotate(p, rotate_right); + } + } + + fn replace(self: *Self, old: *Node, new: *Node) void { + // copy over the values from the old node + new.key = old.key; + new.priority = old.priority; + new.parent = old.parent; + new.children = old.children; + + // point the parent at the new node + const link = if (old.parent) |p| &p.children[@boolToInt(p.children[1] == old)] else &self.root; + assert(link.* == old); + link.* = new; + + // point the children's parent at the new node + for (old.children) |child_node| { + const child = child_node orelse continue; + assert(child.parent == old); + child.parent = new; + } + } + + fn remove(self: *Self, node: *Node) void { + // rotate the node down to be a leaf of the tree for removal, respecting priorities. + while (node.children[0] orelse node.children[1]) |_| { + self.rotate(node, rotate_right: { + const right = node.children[0] orelse break :rotate_right true; + const left = node.children[1] orelse break :rotate_right false; + break :rotate_right (left.priority < right.priority); + }); + } + + // node is a now a leaf; remove by nulling out the parent's reference to it. + const link = if (node.parent) |p| &p.children[@boolToInt(p.children[1] == node)] else &self.root; + assert(link.* == node); + link.* = null; + + // clean up after ourselves + node.key = undefined; + node.priority = 0; + node.parent = null; + node.children = [_]?*Node{ null, null }; + } + + fn rotate(self: *Self, node: *Node, right: bool) void { + // if right, converts the following: + // parent -> (node (target YY adjacent) XX) + // parent -> (target YY (node adjacent XX)) + // + // if left (!right), converts the following: + // parent -> (node (target YY adjacent) XX) + // parent -> (target YY (node adjacent XX)) + const parent = node.parent; + const target = node.children[@boolToInt(!right)] orelse unreachable; + const adjacent = target.children[@boolToInt(right)]; + + // do the rotation + target.children[@boolToInt(right)] = node; + node.parent = target; + node.children[@boolToInt(!right)] = adjacent; + if (adjacent) |adj| adj.parent = node; + + // fix the parent link + const link = if (parent) |p| &p.children[@boolToInt(p.children[1] == node)] else &self.root; + assert(link.* == node); + link.* = target; + } + }; +} + +const TestTreap = Treap(u64, std.math.order); +const TestNode = TestTreap.Node; + +test "std.Treap: insert, find, remove" { + var prng = std.rand.DefaultPrng.init(0xdeadbeef); + var rng = prng.random(); + + var treap = TestTreap{}; + var nodes: [6]TestNode = undefined; + + for (nodes) |*node| { + const key = rng.int(u64); + + var entry = treap.getEntryFor(key); + try testing.expectEqual(entry.key, key); + try testing.expectEqual(entry.get(), null); + + entry.set(node); + try testing.expectEqual(entry.key, key); + try testing.expectEqual(node.key, key); + try testing.expectEqual(entry.get(), node); + } + + for (nodes) |*node| { + const key = node.key; + + var entry = treap.getEntryFor(node.key); + try testing.expectEqual(entry.key, key); + try testing.expectEqual(entry.get(), node); + + var existingEntry = treap.getEntryForExisting(node); + try testing.expectEqual(entry, existingEntry); + } +} From 101aac92c258af9f25520c36f06b2ce8335d7f9a Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 15 Apr 2022 23:32:26 +0300 Subject: [PATCH 1166/2031] stage2: fix bugs preventing stage2 from building stage3 with LLVM --- lib/std/c.zig | 16 ++++++++++++---- lib/std/c/tokenizer.zig | 6 +++++- src/clang.zig | 11 ++++++++--- src/codegen/llvm.zig | 2 +- src/codegen/llvm/bindings.zig | 4 ++-- src/translate_c.zig | 8 +++++--- src/translate_c/ast.zig | 2 +- src/zig_clang.cpp | 5 +++++ src/zig_clang.h | 1 + 9 files changed, 40 insertions(+), 15 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 400feda63c..d98dbf0cde 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -219,7 +219,11 @@ pub extern "c" fn utimes(path: [*:0]const u8, times: *[2]c.timeval) c_int; pub extern "c" fn utimensat(dirfd: c.fd_t, pathname: [*:0]const u8, times: *[2]c.timespec, flags: u32) c_int; pub extern "c" fn futimens(fd: c.fd_t, times: *const [2]c.timespec) c_int; -pub extern "c" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const c.pthread_attr_t, start_routine: fn (?*anyopaque) callconv(.C) ?*anyopaque, noalias arg: ?*anyopaque) c.E; +pub extern "c" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const c.pthread_attr_t, start_routine: PThreadStartFn, noalias arg: ?*anyopaque) c.E; +const PThreadStartFn = if (builtin.zig_backend == .stage1) + fn (?*anyopaque) callconv(.C) ?*anyopaque +else + *const fn (?*anyopaque) callconv(.C) ?*anyopaque; pub extern "c" fn pthread_attr_init(attr: *c.pthread_attr_t) c.E; pub extern "c" fn pthread_attr_setstack(attr: *c.pthread_attr_t, stackaddr: *anyopaque, stacksize: usize) c.E; pub extern "c" fn pthread_attr_setstacksize(attr: *c.pthread_attr_t, stacksize: usize) c.E; @@ -229,10 +233,14 @@ pub extern "c" fn pthread_self() pthread_t; pub extern "c" fn pthread_join(thread: pthread_t, arg_return: ?*?*anyopaque) c.E; pub extern "c" fn pthread_detach(thread: pthread_t) c.E; pub extern "c" fn pthread_atfork( - prepare: ?fn () callconv(.C) void, - parent: ?fn () callconv(.C) void, - child: ?fn () callconv(.C) void, + prepare: ?PThreadForkFn, + parent: ?PThreadForkFn, + child: ?PThreadForkFn, ) c_int; +const PThreadForkFn = if (builtin.zig_backend == .stage1) + fn () callconv(.C) void +else + *const fn () callconv(.C) void; pub extern "c" fn pthread_key_create(key: *c.pthread_key_t, destructor: ?fn (value: *anyopaque) callconv(.C) void) c.E; pub extern "c" fn pthread_key_delete(key: c.pthread_key_t) c.E; pub extern "c" fn pthread_getspecific(key: c.pthread_key_t) ?*anyopaque; diff --git a/lib/std/c/tokenizer.zig b/lib/std/c/tokenizer.zig index 97d62d3803..de8b1a429c 100644 --- a/lib/std/c/tokenizer.zig +++ b/lib/std/c/tokenizer.zig @@ -126,7 +126,11 @@ pub const Token = struct { Keyword_error, Keyword_pragma, - pub fn symbol(id: std.meta.Tag(Id)) []const u8 { + pub fn symbol(id: Id) []const u8 { + return symbolName(id); + } + + pub fn symbolName(id: std.meta.Tag(Id)) []const u8 { return switch (id) { .Invalid => "Invalid", .Eof => "Eof", diff --git a/src/clang.zig b/src/clang.zig index 4c07c00226..64c7da5091 100644 --- a/src/clang.zig +++ b/src/clang.zig @@ -161,7 +161,12 @@ pub const ASTUnit = opaque { extern fn ZigClangASTUnit_getSourceManager(*ASTUnit) *SourceManager; pub const visitLocalTopLevelDecls = ZigClangASTUnit_visitLocalTopLevelDecls; - extern fn ZigClangASTUnit_visitLocalTopLevelDecls(*ASTUnit, context: ?*anyopaque, Fn: ?fn (?*anyopaque, *const Decl) callconv(.C) bool) bool; + extern fn ZigClangASTUnit_visitLocalTopLevelDecls(*ASTUnit, context: ?*anyopaque, Fn: ?VisitorFn) bool; + + const VisitorFn = if (@import("builtin").zig_backend == .stage1) + fn (?*anyopaque, *const Decl) callconv(.C) bool + else + *const fn (?*anyopaque, *const Decl) callconv(.C) bool; pub const getLocalPreprocessingEntities_begin = ZigClangASTUnit_getLocalPreprocessingEntities_begin; extern fn ZigClangASTUnit_getLocalPreprocessingEntities_begin(*ASTUnit) PreprocessingRecord.iterator; @@ -490,8 +495,8 @@ pub const FloatingLiteral = opaque { pub const getValueAsApproximateDouble = ZigClangFloatingLiteral_getValueAsApproximateDouble; extern fn ZigClangFloatingLiteral_getValueAsApproximateDouble(*const FloatingLiteral) f64; - pub const getBeginLoc = ZigClangIntegerLiteral_getBeginLoc; - extern fn ZigClangIntegerLiteral_getBeginLoc(*const FloatingLiteral) SourceLocation; + pub const getBeginLoc = ZigClangFloatingLiteral_getBeginLoc; + extern fn ZigClangFloatingLiteral_getBeginLoc(*const FloatingLiteral) SourceLocation; pub const getRawSemantics = ZigClangFloatingLiteral_getRawSemantics; extern fn ZigClangFloatingLiteral_getRawSemantics(*const FloatingLiteral) APFloatBaseSemantics; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index bb7554d363..364cccf335 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -7220,7 +7220,7 @@ pub const FuncGen = struct { return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); } - fn getIntrinsic(self: *FuncGen, name: []const u8, types: []*const llvm.Type) *const llvm.Value { + fn getIntrinsic(self: *FuncGen, name: []const u8, types: []const *const llvm.Type) *const llvm.Value { const id = llvm.lookupIntrinsicID(name.ptr, name.len); assert(id != 0); return self.llvmModule().getIntrinsicDeclaration(id, types.ptr, types.len); diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index b799d649b0..3863385a06 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -60,7 +60,7 @@ pub const Context = opaque { Packed: Bool, ) *const Type; - const structCreateNamed = LLVMStructCreateNamed; + pub const structCreateNamed = LLVMStructCreateNamed; extern fn LLVMStructCreateNamed(C: *const Context, Name: [*:0]const u8) *const Type; pub const constString = LLVMConstStringInContext; @@ -320,7 +320,7 @@ pub const Module = opaque { extern fn LLVMGetNamedFunction(*const Module, Name: [*:0]const u8) ?*const Value; pub const getIntrinsicDeclaration = LLVMGetIntrinsicDeclaration; - extern fn LLVMGetIntrinsicDeclaration(Mod: *const Module, ID: c_uint, ParamTypes: ?[*]*const Type, ParamCount: usize) *const Value; + extern fn LLVMGetIntrinsicDeclaration(Mod: *const Module, ID: c_uint, ParamTypes: ?[*]const *const Type, ParamCount: usize) *const Value; pub const printToString = LLVMPrintModuleToString; extern fn LLVMPrintModuleToString(*const Module) [*:0]const u8; diff --git a/src/translate_c.zig b/src/translate_c.zig index 39733dd246..72e0cabaf8 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -368,11 +368,13 @@ pub fn translate( resources_path: [*:0]const u8, zig_is_stage1: bool, ) !std.zig.Ast { + // TODO stage2 bug + var tmp = errors; const ast_unit = clang.LoadFromCommandLine( args_begin, args_end, - &errors.ptr, - &errors.len, + &tmp.ptr, + &tmp.len, resources_path, ) orelse { if (errors.len == 0) return error.ASTUnitFailure; @@ -5325,7 +5327,7 @@ const MacroCtx = struct { try self.fail( c, "unable to translate C expr: expected '{s}' instead got '{s}'", - .{ CToken.Id.symbol(expected_id), next_id.symbol() }, + .{ CToken.Id.symbolName(expected_id), next_id.symbol() }, ); return error.ParseError; } diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 66c2406187..3e30b33abc 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -779,7 +779,7 @@ const TokenTag = std.zig.Token.Tag; const Context = struct { gpa: Allocator, - buf: std.ArrayList(u8) = .{}, + buf: std.ArrayList(u8), nodes: std.zig.Ast.NodeList = .{}, extra_data: std.ArrayListUnmanaged(std.zig.Ast.Node.Index) = .{}, tokens: std.zig.Ast.TokenList = .{}, diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index 578548dfd4..ea3f09c8d5 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -2706,6 +2706,11 @@ double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatin return casted->getValueAsApproximateDouble(); } +struct ZigClangSourceLocation ZigClangFloatingLiteral_getBeginLoc(const struct ZigClangFloatingLiteral *self) { + auto casted = reinterpret_cast(self); + return bitcast(casted->getBeginLoc()); +} + ZigClangAPFloatBase_Semantics ZigClangFloatingLiteral_getRawSemantics(const ZigClangFloatingLiteral *self) { auto casted = reinterpret_cast(self); return static_cast(casted->getRawSemantics()); diff --git a/src/zig_clang.h b/src/zig_clang.h index 85a0aa5dbb..12eda66d07 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -1245,6 +1245,7 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDeclStmt_getBeginLoc(const st ZIG_EXTERN_C unsigned ZigClangAPFloat_convertToHexString(const struct ZigClangAPFloat *self, char *DST, unsigned HexDigits, bool UpperCase, enum ZigClangAPFloat_roundingMode RM); ZIG_EXTERN_C double ZigClangFloatingLiteral_getValueAsApproximateDouble(const ZigClangFloatingLiteral *self); +ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangFloatingLiteral_getBeginLoc(const struct ZigClangFloatingLiteral *); ZIG_EXTERN_C ZigClangAPFloatBase_Semantics ZigClangFloatingLiteral_getRawSemantics(const ZigClangFloatingLiteral *self); ZIG_EXTERN_C enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self); From 4d0303b99249adcc4acdd51fa9a10884f1e5ed61 Mon Sep 17 00:00:00 2001 From: kprotty Date: Fri, 15 Apr 2022 16:30:45 -0500 Subject: [PATCH 1167/2031] treap: fix + determinstically randomize test cases. --- lib/std/treap.zig | 152 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 128 insertions(+), 24 deletions(-) diff --git a/lib/std/treap.zig b/lib/std/treap.zig index 27c5285c34..7d08ef64b6 100644 --- a/lib/std/treap.zig +++ b/lib/std/treap.zig @@ -103,9 +103,13 @@ pub fn Treap(comptime Key: type, comptime compareFn: anytype) type { /// An Entry represents a slot in the treap associated with a given key. pub const Entry = struct { + /// The associated key for this entry. key: Key, + /// A reference to the treap this entry is apart of. treap: *Self, + /// The current node at this entry. node: ?*Node, + /// The current state of the entry. context: union(enum) { /// A find() was called for this entry and the position in the treap is known. inserted_under: ?*Node, @@ -113,11 +117,6 @@ pub fn Treap(comptime Key: type, comptime compareFn: anytype) type { removed, }, - /// Returns the current Node at this Entry in the treap if there is one. - pub fn get(self: Entry) ?*Node { - return self.node; - } - /// Update's the Node at this Entry in the treap with the new node. pub fn set(self: *Entry, new_node: ?*Node) void { // Update the entry's node reference after updating the treap below. @@ -182,7 +181,7 @@ pub fn Treap(comptime Key: type, comptime compareFn: anytype) type { while (node.parent) |p| { if (p.priority <= node.priority) break; - const is_right = p.children[1] == @as(?*Node, node); + const is_right = p.children[1] == node; assert(p.children[@boolToInt(is_right)] == node); const rotate_right = !is_right; @@ -214,8 +213,8 @@ pub fn Treap(comptime Key: type, comptime compareFn: anytype) type { // rotate the node down to be a leaf of the tree for removal, respecting priorities. while (node.children[0] orelse node.children[1]) |_| { self.rotate(node, rotate_right: { - const right = node.children[0] orelse break :rotate_right true; - const left = node.children[1] orelse break :rotate_right false; + const right = node.children[1] orelse break :rotate_right true; + const left = node.children[0] orelse break :rotate_right false; break :rotate_right (left.priority < right.priority); }); } @@ -244,10 +243,13 @@ pub fn Treap(comptime Key: type, comptime compareFn: anytype) type { const target = node.children[@boolToInt(!right)] orelse unreachable; const adjacent = target.children[@boolToInt(right)]; - // do the rotation + // rotate the children target.children[@boolToInt(right)] = node; - node.parent = target; node.children[@boolToInt(!right)] = adjacent; + + // rotate the parents + node.parent = target; + target.parent = parent; if (adjacent) |adj| adj.parent = node; // fix the parent link @@ -258,37 +260,139 @@ pub fn Treap(comptime Key: type, comptime compareFn: anytype) type { }; } +// For iterating a slice in a random order +// https://lemire.me/blog/2017/09/18/visiting-all-values-in-an-array-exactly-once-in-random-order/ +fn SliceIterRandomOrder(comptime T: type) type { + return struct { + rng: std.rand.Random, + slice: []T, + index: usize = undefined, + offset: usize = undefined, + co_prime: usize, + + const Self = @This(); + + pub fn init(slice: []T, rng: std.rand.Random) Self { + return Self{ + .rng = rng, + .slice = slice, + .co_prime = blk: { + if (slice.len == 0) break :blk 0; + var prime = slice.len / 2; + while (prime < slice.len) : (prime += 1) { + var gcd = [_]usize{ prime, slice.len }; + while (gcd[1] != 0) { + const temp = gcd; + gcd = [_]usize{ temp[1], temp[0] % temp[1] }; + } + if (gcd[0] == 1) break; + } + break :blk prime; + }, + }; + } + + pub fn reset(self: *Self) void { + self.index = 0; + self.offset = self.rng.int(usize); + } + + pub fn next(self: *Self) ?*T { + if (self.index >= self.slice.len) return null; + defer self.index += 1; + return &self.slice[((self.index *% self.co_prime) +% self.offset) % self.slice.len]; + } + }; +} + const TestTreap = Treap(u64, std.math.order); const TestNode = TestTreap.Node; -test "std.Treap: insert, find, remove" { - var prng = std.rand.DefaultPrng.init(0xdeadbeef); - var rng = prng.random(); - +test "std.Treap: insert, find, replace, remove" { var treap = TestTreap{}; - var nodes: [6]TestNode = undefined; + var nodes: [10]TestNode = undefined; - for (nodes) |*node| { - const key = rng.int(u64); + var prng = std.rand.DefaultPrng.init(0xdeadbeef); + var iter = SliceIterRandomOrder(TestNode).init(&nodes, prng.random()); + // insert check + iter.reset(); + while (iter.next()) |node| { + const key = prng.random().int(u64); + + // make sure the current entry is empty. var entry = treap.getEntryFor(key); try testing.expectEqual(entry.key, key); - try testing.expectEqual(entry.get(), null); + try testing.expectEqual(entry.node, null); + // insert the entry and make sure the fields are correct. entry.set(node); - try testing.expectEqual(entry.key, key); try testing.expectEqual(node.key, key); - try testing.expectEqual(entry.get(), node); + try testing.expectEqual(entry.key, key); + try testing.expectEqual(entry.node, node); } - for (nodes) |*node| { + // find check + iter.reset(); + while (iter.next()) |node| { const key = node.key; + // find the entry by-key and by-node after having been inserted. var entry = treap.getEntryFor(node.key); try testing.expectEqual(entry.key, key); - try testing.expectEqual(entry.get(), node); + try testing.expectEqual(entry.node, node); + try testing.expectEqual(entry.node, treap.getEntryForExisting(node).node); + } - var existingEntry = treap.getEntryForExisting(node); - try testing.expectEqual(entry, existingEntry); + // replace check + iter.reset(); + while (iter.next()) |node| { + const key = node.key; + + // find the entry by node since we already know it exists + var entry = treap.getEntryForExisting(node); + try testing.expectEqual(entry.key, key); + try testing.expectEqual(entry.node, node); + + var stub_node: TestNode = undefined; + + // replace the node with a stub_node and ensure future finds point to the stub_node. + entry.set(&stub_node); + try testing.expectEqual(entry.node, &stub_node); + try testing.expectEqual(entry.node, treap.getEntryFor(key).node); + try testing.expectEqual(entry.node, treap.getEntryForExisting(&stub_node).node); + + // replace the stub_node back to the node and ensure future finds point to the old node. + entry.set(node); + try testing.expectEqual(entry.node, node); + try testing.expectEqual(entry.node, treap.getEntryFor(key).node); + try testing.expectEqual(entry.node, treap.getEntryForExisting(node).node); + } + + // remove check + iter.reset(); + while (iter.next()) |node| { + const key = node.key; + + // find the entry by node since we already know it exists + var entry = treap.getEntryForExisting(node); + try testing.expectEqual(entry.key, key); + try testing.expectEqual(entry.node, node); + + // remove the node at the entry and ensure future finds point to it being removed. + entry.set(null); + try testing.expectEqual(entry.node, null); + try testing.expectEqual(entry.node, treap.getEntryFor(key).node); + + // insert the node back and ensure future finds point to the inserted node + entry.set(node); + try testing.expectEqual(entry.node, node); + try testing.expectEqual(entry.node, treap.getEntryFor(key).node); + try testing.expectEqual(entry.node, treap.getEntryForExisting(node).node); + + // remove the node again and make sure it was cleared after the insert + entry.set(null); + try testing.expectEqual(entry.node, null); + try testing.expectEqual(entry.node, treap.getEntryFor(key).node); } } From 33952dad1259d9f4af09ebbad094c9b27d565525 Mon Sep 17 00:00:00 2001 From: kprotty Date: Fri, 15 Apr 2022 17:01:01 -0500 Subject: [PATCH 1168/2031] treap: zig fmt --- lib/std/treap.zig | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/std/treap.zig b/lib/std/treap.zig index 7d08ef64b6..a74256356c 100644 --- a/lib/std/treap.zig +++ b/lib/std/treap.zig @@ -8,7 +8,7 @@ pub fn Treap(comptime Key: type, comptime compareFn: anytype) type { const Self = @This(); // Allow for compareFn to be fn(anytype, anytype) anytype - // which allows the convenient use of std.math.order. + // which allows the convenient use of std.math.order. fn compare(a: Key, b: Key) Order { return compareFn(a, b); } @@ -30,9 +30,9 @@ pub fn Treap(comptime Key: type, comptime compareFn: anytype) type { // Since we're using usize, decide the shifts by the integer's bit width. const shifts = switch (@bitSizeOf(usize)) { - 64 => .{13, 7, 17}, - 32 => .{13, 17, 5}, - 16 => .{7, 9, 8}, + 64 => .{ 13, 7, 17 }, + 32 => .{ 13, 17, 5 }, + 16 => .{ 7, 9, 8 }, else => @compileError("platform not supported"), }; @@ -42,7 +42,7 @@ pub fn Treap(comptime Key: type, comptime compareFn: anytype) type { assert(self.xorshift != 0); return self.xorshift; - } + } }; /// A Node represents an item or point in the treap with a uniquely associated key. @@ -164,7 +164,7 @@ pub fn Treap(comptime Key: type, comptime compareFn: anytype) type { return node; } - + fn insert(self: *Self, key: Key, parent: ?*Node, node: *Node) void { // generate a random priority & prepare the node to be inserted into the tree node.key = key; @@ -232,11 +232,11 @@ pub fn Treap(comptime Key: type, comptime compareFn: anytype) type { } fn rotate(self: *Self, node: *Node, right: bool) void { - // if right, converts the following: + // if right, converts the following: // parent -> (node (target YY adjacent) XX) // parent -> (target YY (node adjacent XX)) // - // if left (!right), converts the following: + // if left (!right), converts the following: // parent -> (node (target YY adjacent) XX) // parent -> (target YY (node adjacent XX)) const parent = node.parent; @@ -389,7 +389,7 @@ test "std.Treap: insert, find, replace, remove" { try testing.expectEqual(entry.node, node); try testing.expectEqual(entry.node, treap.getEntryFor(key).node); try testing.expectEqual(entry.node, treap.getEntryForExisting(node).node); - + // remove the node again and make sure it was cleared after the insert entry.set(null); try testing.expectEqual(entry.node, null); From 2ef9a0d9ae9bc4415492bc92c7391d69add8286e Mon Sep 17 00:00:00 2001 From: vi Date: Fri, 15 Apr 2022 16:50:00 -0600 Subject: [PATCH 1169/2031] std.math: enable f80 tests on freebsd --- lib/std/math/isfinite.zig | 3 --- lib/std/math/isinf.zig | 9 --------- lib/std/math/isnormal.zig | 3 --- 3 files changed, 15 deletions(-) diff --git a/lib/std/math/isfinite.zig b/lib/std/math/isfinite.zig index e9314213ce..67a67a4610 100644 --- a/lib/std/math/isfinite.zig +++ b/lib/std/math/isfinite.zig @@ -14,9 +14,6 @@ pub fn isFinite(x: anytype) bool { } test "math.isFinite" { - // TODO remove when #11391 is resolved - if (@import("builtin").os.tag == .freebsd) return error.SkipZigTest; - inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { // normals try expect(isFinite(@as(T, 1.0))); diff --git a/lib/std/math/isinf.zig b/lib/std/math/isinf.zig index e88b9810b6..7275740fcc 100644 --- a/lib/std/math/isinf.zig +++ b/lib/std/math/isinf.zig @@ -24,9 +24,6 @@ pub fn isNegativeInf(x: anytype) bool { } test "math.isInf" { - // TODO remove when #11391 is resolved - if (@import("builtin").os.tag == .freebsd) return error.SkipZigTest; - inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { try expect(!isInf(@as(T, 0.0))); try expect(!isInf(@as(T, -0.0))); @@ -38,9 +35,6 @@ test "math.isInf" { } test "math.isPositiveInf" { - // TODO remove when #11391 is resolved - if (@import("builtin").os.tag == .freebsd) return error.SkipZigTest; - inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { try expect(!isPositiveInf(@as(T, 0.0))); try expect(!isPositiveInf(@as(T, -0.0))); @@ -52,9 +46,6 @@ test "math.isPositiveInf" { } test "math.isNegativeInf" { - // TODO remove when #11391 is resolved - if (@import("builtin").os.tag == .freebsd) return error.SkipZigTest; - inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { try expect(!isNegativeInf(@as(T, 0.0))); try expect(!isNegativeInf(@as(T, -0.0))); diff --git a/lib/std/math/isnormal.zig b/lib/std/math/isnormal.zig index 42b2e1c188..e15d8a91cc 100644 --- a/lib/std/math/isnormal.zig +++ b/lib/std/math/isnormal.zig @@ -23,9 +23,6 @@ pub fn isNormal(x: anytype) bool { } test "math.isNormal" { - // TODO remove when #11391 is resolved - if (@import("builtin").os.tag == .freebsd) return error.SkipZigTest; - // TODO add `c_longdouble' when math.inf(T) supports it inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); From 4ad1480298d0f1b547a9bfffc28cc7a2dd6cafc5 Mon Sep 17 00:00:00 2001 From: vi Date: Fri, 15 Apr 2022 16:51:22 -0600 Subject: [PATCH 1170/2031] std.math.fabs: enable f80 testing --- lib/std/math/fabs.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/math/fabs.zig b/lib/std/math/fabs.zig index f1bb4be7e7..44918e75d9 100644 --- a/lib/std/math/fabs.zig +++ b/lib/std/math/fabs.zig @@ -21,8 +21,8 @@ pub fn fabs(x: anytype) @TypeOf(x) { } test "math.fabs" { - // TODO add support for f80 & c_longdouble here - inline for ([_]type{ f16, f32, f64, f128 }) |T| { + // TODO add support for c_longdouble here + inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { // normals try expect(fabs(@as(T, 1.0)) == 1.0); try expect(fabs(@as(T, -1.0)) == 1.0); From 3bfb1616db0883e6145a1fe1844eee3038bd4ca1 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Fri, 15 Apr 2022 19:22:35 +0200 Subject: [PATCH 1171/2031] stage2 ARM: move genArgDbgInfo back to CodeGen This removes the questionable Air -> Mir dependency that existed before. The x86_64 backend also performed this change. --- src/arch/arm/CodeGen.zig | 114 ++++++++++++++++++++++++++++++++++++--- src/arch/arm/Emit.zig | 106 ------------------------------------ src/arch/arm/Mir.zig | 10 ---- 3 files changed, 106 insertions(+), 124 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index f71ceaba89..d39c7d9176 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -49,6 +49,7 @@ gpa: Allocator, air: Air, liveness: Liveness, bin_file: *link.File, +debug_output: DebugInfoOutput, target: *const std.Target, mod_fn: *const Module.Fn, err_msg: ?*ErrorMsg, @@ -73,6 +74,12 @@ end_di_column: u32, /// which is a relative jump, based on the address following the reloc. exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{}, +/// For every argument, we postpone the creation of debug info for +/// later after all Mir instructions have been generated. Only then we +/// will know saved_regs_stack_space which is necessary in order to +/// address parameters passed on the stack. +dbg_arg_relocs: std.ArrayListUnmanaged(DbgArgReloc) = .{}, + /// Whenever there is a runtime branch, we push a Branch onto this stack, /// and pop it off when the runtime branch joins. This provides an "overlay" /// of the table of mappings from instructions to `MCValue` from within the branch. @@ -244,6 +251,11 @@ const BigTomb = struct { } }; +const DbgArgReloc = struct { + inst: Air.Inst.Index, + index: u32, +}; + const Self = @This(); pub fn generate( @@ -276,6 +288,7 @@ pub fn generate( .liveness = liveness, .target = &bin_file.options.target, .bin_file = bin_file, + .debug_output = debug_output, .mod_fn = module_fn, .err_msg = null, .args = undefined, // populated after `resolveCallingConventionValues` @@ -291,6 +304,7 @@ pub fn generate( defer function.stack.deinit(bin_file.allocator); defer function.blocks.deinit(bin_file.allocator); defer function.exitlude_jump_relocs.deinit(bin_file.allocator); + defer function.dbg_arg_relocs.deinit(bin_file.allocator); var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, @@ -314,6 +328,10 @@ pub fn generate( else => |e| return e, }; + for (function.dbg_arg_relocs.items) |reloc| { + try function.genArgDbgInfo(reloc.inst, reloc.index, call_info.stack_byte_count); + } + var mir = Mir{ .instructions = function.mir_instructions.toOwnedSlice(), .extra = function.mir_extra.toOwnedSlice(bin_file.allocator), @@ -323,7 +341,6 @@ pub fn generate( var emit = Emit{ .mir = mir, .bin_file = bin_file, - .function = &function, .debug_output = debug_output, .target = &bin_file.options.target, .src_loc = src_loc, @@ -3074,6 +3091,91 @@ fn genInlineMemcpy( // end: } +/// Adds a Type to the .debug_info at the current position. The bytes will be populated later, +/// after codegen for this symbol is done. +fn addDbgInfoTypeReloc(self: *Self, ty: Type) error{OutOfMemory}!void { + switch (self.debug_output) { + .dwarf => |dw| { + assert(ty.hasRuntimeBits()); + const dbg_info = &dw.dbg_info; + const index = dbg_info.items.len; + try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 + const atom = switch (self.bin_file.tag) { + .elf => &self.mod_fn.owner_decl.link.elf.dbg_info_atom, + .macho => unreachable, + else => unreachable, + }; + try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); + }, + .plan9 => {}, + .none => {}, + } +} + +fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, arg_index: u32, stack_byte_count: u32) error{OutOfMemory}!void { + const prologue_stack_space = stack_byte_count + self.saved_regs_stack_space; + + const mcv = self.args[arg_index]; + const ty = self.air.instructions.items(.data)[inst].ty; + const name = self.mod_fn.getParamName(arg_index); + const name_with_null = name.ptr[0 .. name.len + 1]; + + switch (mcv) { + .register => |reg| { + switch (self.debug_output) { + .dwarf => |dw| { + const dbg_info = &dw.dbg_info; + try dbg_info.ensureUnusedCapacity(3); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1, // ULEB128 dwarf expression length + reg.dwarfLocOp(), + }); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + }, + .plan9 => {}, + .none => {}, + } + }, + .stack_offset, + .stack_argument_offset, + => { + switch (self.debug_output) { + .dwarf => |dw| { + // const abi_size = @intCast(u32, ty.abiSize(self.target.*)); + const adjusted_stack_offset = switch (mcv) { + .stack_offset => |offset| -@intCast(i32, offset), + .stack_argument_offset => |offset| @intCast(i32, prologue_stack_space - offset), + else => unreachable, + }; + + const dbg_info = &dw.dbg_info; + try dbg_info.append(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); + + // Get length of the LEB128 stack offset + var counting_writer = std.io.countingWriter(std.io.null_writer); + leb128.writeILEB128(counting_writer.writer(), adjusted_stack_offset) catch unreachable; + + // DW.AT.location, DW.FORM.exprloc + // ULEB128 dwarf expression length + try leb128.writeULEB128(dbg_info.writer(), counting_writer.bytes_written + 1); + try dbg_info.append(DW.OP.breg11); + try leb128.writeILEB128(dbg_info.writer(), adjusted_stack_offset); + + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + }, + .plan9 => {}, + .none => {}, + } + }, + else => unreachable, // not a possible argument + } +} + fn airArg(self: *Self, inst: Air.Inst.Index) !void { const arg_index = self.arg_index; self.arg_index += 1; @@ -3094,13 +3196,9 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { else => result, }; - _ = try self.addInst(.{ - .tag = .dbg_arg, - .cond = undefined, - .data = .{ .dbg_arg_info = .{ - .air_inst = inst, - .arg_index = arg_index, - } }, + try self.dbg_arg_relocs.append(self.gpa, .{ + .inst = inst, + .index = arg_index, }); if (self.liveness.isUnused(inst)) diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index 209ab137a6..8db830bfba 100644 --- a/src/arch/arm/Emit.zig +++ b/src/arch/arm/Emit.zig @@ -9,7 +9,6 @@ const Mir = @import("Mir.zig"); const bits = @import("bits.zig"); const link = @import("../../link.zig"); const Module = @import("../../Module.zig"); -const Air = @import("../../Air.zig"); const Type = @import("../../type.zig").Type; const ErrorMsg = Module.ErrorMsg; const assert = std.debug.assert; @@ -23,7 +22,6 @@ const CodeGen = @import("CodeGen.zig"); mir: Mir, bin_file: *link.File, -function: *const CodeGen, debug_output: DebugInfoOutput, target: *const std.Target, err_msg: ?*ErrorMsg = null, @@ -102,8 +100,6 @@ pub fn emitMir( .blx => try emit.mirBranchExchange(inst), .bx => try emit.mirBranchExchange(inst), - .dbg_arg => try emit.mirDbgArg(inst), - .dbg_line => try emit.mirDbgLine(inst), .dbg_prologue_end => try emit.mirDebugPrologueEnd(), @@ -189,7 +185,6 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize { .dbg_line, .dbg_epilogue_begin, .dbg_prologue_end, - .dbg_arg, => return 0, else => return 4, } @@ -383,97 +378,6 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { } } -/// Adds a Type to the .debug_info at the current position. The bytes will be populated later, -/// after codegen for this symbol is done. -fn addDbgInfoTypeReloc(self: *Emit, ty: Type) !void { - switch (self.debug_output) { - .dwarf => |dw| { - assert(ty.hasRuntimeBits()); - const dbg_info = &dw.dbg_info; - const index = dbg_info.items.len; - try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 - const atom = switch (self.bin_file.tag) { - .elf => &self.function.mod_fn.owner_decl.link.elf.dbg_info_atom, - .macho => unreachable, - else => unreachable, - }; - try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); - }, - .plan9 => {}, - .none => {}, - } -} - -fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { - const mcv = self.function.args[arg_index]; - - const ty = self.function.air.instructions.items(.data)[inst].ty; - const name = self.function.mod_fn.getParamName(arg_index); - const name_with_null = name.ptr[0 .. name.len + 1]; - const target = self.target.*; - - switch (mcv) { - .register => |reg| { - switch (self.debug_output) { - .dwarf => |dw| { - const dbg_info = &dw.dbg_info; - try dbg_info.ensureUnusedCapacity(3); - dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); - dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc - 1, // ULEB128 dwarf expression length - reg.dwarfLocOp(), - }); - try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); - try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string - }, - .plan9 => {}, - .none => {}, - } - }, - .stack_offset, - .stack_argument_offset, - => { - switch (self.debug_output) { - .dwarf => |dw| { - const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(target)}); - }; - const adjusted_stack_offset = switch (mcv) { - .stack_offset => |offset| math.negateCast(offset + abi_size) catch { - return self.fail("Stack offset too large for arguments", .{}); - }, - .stack_argument_offset => |offset| math.cast(i32, self.prologue_stack_space - offset - abi_size) catch { - return self.fail("Stack offset too large for arguments", .{}); - }, - else => unreachable, - }; - - const dbg_info = &dw.dbg_info; - try dbg_info.append(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); - - // Get length of the LEB128 stack offset - var counting_writer = std.io.countingWriter(std.io.null_writer); - leb128.writeILEB128(counting_writer.writer(), adjusted_stack_offset) catch unreachable; - - // DW.AT.location, DW.FORM.exprloc - // ULEB128 dwarf expression length - try leb128.writeULEB128(dbg_info.writer(), counting_writer.bytes_written + 1); - try dbg_info.append(DW.OP.breg11); - try leb128.writeILEB128(dbg_info.writer(), adjusted_stack_offset); - - try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); - try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string - }, - .plan9 => {}, - .none => {}, - } - }, - else => unreachable, // not a possible argument - } -} - fn mirDataProcessing(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; const cond = emit.mir.instructions.items(.cond)[inst]; @@ -546,16 +450,6 @@ fn mirBranchExchange(emit: *Emit, inst: Mir.Inst.Index) !void { } } -fn mirDbgArg(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const dbg_arg_info = emit.mir.instructions.items(.data)[inst].dbg_arg_info; - - switch (tag) { - .dbg_arg => try emit.genArgDbgInfo(dbg_arg_info.air_inst, dbg_arg_info.arg_index), - else => unreachable, - } -} - fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column; diff --git a/src/arch/arm/Mir.zig b/src/arch/arm/Mir.zig index 209b3e508e..d5da7e5d4e 100644 --- a/src/arch/arm/Mir.zig +++ b/src/arch/arm/Mir.zig @@ -12,7 +12,6 @@ const builtin = @import("builtin"); const assert = std.debug.assert; const bits = @import("bits.zig"); -const Air = @import("../../Air.zig"); const Register = bits.Register; instructions: std.MultiArrayList(Inst).Slice, @@ -44,8 +43,6 @@ pub const Inst = struct { bx, /// Compare cmp, - /// Pseudo-instruction: Argument - dbg_arg, /// Pseudo-instruction: End of prologue dbg_prologue_end, /// Pseudo-instruction: Beginning of epilogue @@ -239,13 +236,6 @@ pub const Inst = struct { line: u32, column: u32, }, - /// Debug info: argument - /// - /// Used by e.g. dbg_arg - dbg_arg_info: struct { - air_inst: Air.Inst.Index, - arg_index: u32, - }, }; // Make sure we don't accidentally make instructions bigger than expected. From 88d87d65061e7ac171cc23623a25e05b0278a872 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 15 Apr 2022 21:26:55 +0200 Subject: [PATCH 1172/2031] stage2,macho: swap out inodes before checking for intermediary basename This way we avoid the infamous SIGKILL on arm64 macos. --- src/link.zig | 11 ++++------- src/link/MachO.zig | 5 +++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/link.zig b/src/link.zig index dbef400189..2d7e577b70 100644 --- a/src/link.zig +++ b/src/link.zig @@ -352,11 +352,6 @@ pub const File = struct { } switch (base.tag) { .macho => if (base.file) |f| { - if (base.intermediary_basename != null) { - // The file we have open is not the final file that we want to - // make executable, so we don't have to close it. - return; - } if (comptime builtin.target.isDarwin() and builtin.target.cpu.arch == .aarch64) { if (base.options.target.cpu.arch == .aarch64) { // XNU starting with Big Sur running on arm64 is caching inodes of running binaries. @@ -371,8 +366,10 @@ pub const File = struct { try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, emit.sub_path, .{}); } } - f.close(); - base.file = null; + if (base.intermediary_basename == null) { + f.close(); + base.file = null; + } }, .coff, .elf, .plan9 => if (base.file) |f| { if (base.intermediary_basename != null) { diff --git a/src/link/MachO.zig b/src/link/MachO.zig index fa1ba2835a..a7437ed576 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -423,7 +423,7 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { if (self.base.options.emit == null) { if (build_options.have_llvm) { if (self.llvm_object) |llvm_object| { - return try llvm_object.flushModule(comp); + try llvm_object.flushModule(comp); } } return; @@ -1116,7 +1116,8 @@ pub fn flushObject(self: *MachO, comp: *Compilation) !void { defer tracy.end(); if (build_options.have_llvm) - if (self.llvm_object) |llvm_object| return llvm_object.flushModule(comp); + if (self.llvm_object) |llvm_object| + return llvm_object.flushModule(comp); return error.TODOImplementWritingObjFiles; } From 1b5a43fdf71e596247ce0f730206c597b425be3e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 16 Apr 2022 00:17:40 +0200 Subject: [PATCH 1173/2031] start.zig: make sure macos uses full startup code --- lib/std/start.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/start.zig b/lib/std/start.zig index f4a5cbb763..5690337317 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -30,8 +30,8 @@ comptime { builtin.zig_backend == .stage2_arm or builtin.zig_backend == .stage2_riscv64 or builtin.zig_backend == .stage2_sparcv9 or - (builtin.zig_backend == .stage2_llvm and native_os != .linux) or - (builtin.zig_backend == .stage2_llvm and native_arch != .x86_64)) + (builtin.zig_backend == .stage2_llvm and native_os != .linux and native_os != .macos) or + (builtin.zig_backend == .stage2_llvm and native_arch != .x86_64 and native_arch != .aarch64)) { if (builtin.output_mode == .Exe) { if ((builtin.link_libc or builtin.object_format == .c) and @hasDecl(root, "main")) { From 897df18573c951b49fb6421a9e1b711cabfeda67 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 16 Apr 2022 01:55:41 +0200 Subject: [PATCH 1174/2031] stage2: fix @mulAdd on aarch64 Darwin According to Apple docs, the long double type is a double precision IEEE754 binary floating-point type, which makes it identical to the double type. This behavior contrasts to the standard specification, in which a long double is a quad-precision, IEEE754 binary, floating-point type. Thus, we need to take this into account when using the compiler intrinsics so that we select the correct function version for FloatMulAdd. --- lib/std/target.zig | 11 ++++++++++- src/stage1/target.cpp | 10 +++++++++- src/type.zig | 3 +-- test/behavior/cast.zig | 23 ++++++++++------------- 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/lib/std/target.zig b/lib/std/target.zig index 45a9c985b5..95fbd7e4e7 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1718,8 +1718,17 @@ pub const Target = struct { } return switch (F) { f128 => switch (target.cpu.arch) { + .aarch64 => { + // According to Apple's official guide: + // > The long double type is a double precision IEEE754 binary floating-point type, + // > which makes it identical to the double type. This behavior contrasts to the + // > standard specification, in which a long double is a quad-precision, IEEE754 + // > binary, floating-point type. + // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms + return !target.isDarwin(); + }, + .riscv64, - .aarch64, .aarch64_be, .aarch64_32, .s390x, diff --git a/src/stage1/target.cpp b/src/stage1/target.cpp index 51aac6c072..7ff3008911 100644 --- a/src/stage1/target.cpp +++ b/src/stage1/target.cpp @@ -1008,8 +1008,16 @@ bool target_long_double_is_f128(const ZigTarget *target) { return false; } switch (target->arch) { - case ZigLLVM_riscv64: case ZigLLVM_aarch64: + // According to Apple's official guide: + // > The long double type is a double precision IEEE754 binary floating-point type, + // > which makes it identical to the double type. This behavior contrasts to the + // > standard specification, in which a long double is a quad-precision, IEEE754 + // > binary, floating-point type. + // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms + return !target_os_is_darwin(target->os); + + case ZigLLVM_riscv64: case ZigLLVM_aarch64_be: case ZigLLVM_aarch64_32: case ZigLLVM_systemz: diff --git a/src/type.zig b/src/type.zig index b087f7ab23..72cf2cd534 100644 --- a/src/type.zig +++ b/src/type.zig @@ -6155,7 +6155,6 @@ pub const CType = enum { }, .linux, - .macos, .freebsd, .netbsd, .dragonfly, @@ -6198,7 +6197,7 @@ pub const CType = enum { .longlong, .ulonglong, .longdouble => return 64, }, - .ios, .tvos, .watchos => switch (self) { + .macos, .ios, .tvos, .watchos => switch (self) { .short, .ushort => return 16, .int, .uint => return 32, .long, .ulong, .longlong, .ulonglong => return 64, diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 8793e7cb19..a1bd41e7b0 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -79,19 +79,16 @@ test "comptime_int @intToFloat" { try expect(result == 1234.0); } - if (!((builtin.zig_backend == .stage2_aarch64 or builtin.zig_backend == .stage2_x86_64) and builtin.os.tag == .macos)) { - // TODO investigate why this traps on x86_64-macos and aarch64-macos - { - const result = @intToFloat(f128, 1234); - try expect(@TypeOf(result) == f128); - try expect(result == 1234.0); - } - // big comptime_int (> 64 bits) to f128 conversion - { - const result = @intToFloat(f128, 0x1_0000_0000_0000_0000); - try expect(@TypeOf(result) == f128); - try expect(result == 0x1_0000_0000_0000_0000.0); - } + { + const result = @intToFloat(f128, 1234); + try expect(@TypeOf(result) == f128); + try expect(result == 1234.0); + } + // big comptime_int (> 64 bits) to f128 conversion + { + const result = @intToFloat(f128, 0x1_0000_0000_0000_0000); + try expect(@TypeOf(result) == f128); + try expect(result == 0x1_0000_0000_0000_0000.0); } } From 7b090df6689909ba82ff46be4d21733f84f32c33 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Thu, 3 Mar 2022 12:25:21 -0700 Subject: [PATCH 1175/2031] stdlib std.os: Improve wasi-libc parity for WASI CWD emulation Two major changes here: 1. We store the CWD as a simple `[]const u8` and lookup Preopens for every absolute or CWD-referenced file operation, based on the Preopen with the longest match (i.e. most specific path) 2. Preorders are normalized to POSIX absolute paths at init time. Behavior depends on the "cwd_root" parameter of `initPreopensWasi`: `cwd_root` is used for any Preopens that start with "." For example: "./foo/bar" - inits to -> "{cwd_root}/foo/bar" "foo/bar" - inits to -> "/foo/bar" "/foo/bar" - inits to -> "/foo/bar" `cwd_root` must be an absolute path. Using "/" as `cwd_root` gives behavior similar to wasi-libc. --- doc/langref.html.in | 2 +- lib/std/fs/path.zig | 8 +- lib/std/fs/test.zig | 14 +-- lib/std/fs/wasi.zig | 110 ++++++++++++++++++------ lib/std/os.zig | 204 ++++++++++++-------------------------------- lib/std/os/test.zig | 14 +-- lib/std/testing.zig | 2 +- 7 files changed, 158 insertions(+), 196 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 0f90a88822..6ae2e456e4 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -11279,7 +11279,7 @@ pub fn main() !void { var preopens = PreopenList.init(gpa); defer preopens.deinit(); - try preopens.populate(); + try preopens.populate(null); for (preopens.asSlice()) |preopen, i| { std.debug.print("{}: {}\n", .{ i, preopen }); diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index bdd1c53ed5..d5583dcc80 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -735,7 +735,7 @@ pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) ![]u8 { test "resolve" { if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; - if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); + if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); const cwd = try process.getCwdAlloc(testing.allocator); defer testing.allocator.free(cwd); @@ -756,7 +756,7 @@ test "resolveWindows" { return error.SkipZigTest; } if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; - if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); + if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); if (native_os == .windows) { const cwd = try process.getCwdAlloc(testing.allocator); defer testing.allocator.free(cwd); @@ -802,7 +802,7 @@ test "resolveWindows" { test "resolvePosix" { if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; - if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); + if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); try testResolvePosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c"); try testResolvePosix(&[_][]const u8{ "/a/b", "c", "//d", "e///" }, "/d/e"); @@ -1216,7 +1216,7 @@ test "relative" { return error.SkipZigTest; } if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; - if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); + if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games"); try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", ".."); diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 82005152e5..b8e6c4d89a 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -47,7 +47,7 @@ fn testReadLink(dir: Dir, target_path: []const u8, symlink_path: []const u8) !vo test "accessAbsolute" { if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; - if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); + if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -66,7 +66,7 @@ test "accessAbsolute" { test "openDirAbsolute" { if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; - if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); + if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -103,7 +103,7 @@ test "openDir cwd parent .." { test "readLinkAbsolute" { if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; - if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); + if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -535,7 +535,7 @@ test "rename" { test "renameAbsolute" { if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; - if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); + if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); var tmp_dir = tmpDir(.{}); defer tmp_dir.cleanup(); @@ -980,7 +980,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" { test "walker" { if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; - if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); + if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); var tmp = tmpDir(.{ .iterate = true }); defer tmp.cleanup(); @@ -1031,7 +1031,7 @@ test "walker" { test ". and .. in fs.Dir functions" { if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; - if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); + if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -1060,7 +1060,7 @@ test ". and .. in fs.Dir functions" { test ". and .. in absolute functions" { if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest; - if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); + if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); var tmp = tmpDir(.{}); defer tmp.cleanup(); diff --git a/lib/std/fs/wasi.zig b/lib/std/fs/wasi.zig index 4754ded630..81a43062dc 100644 --- a/lib/std/fs/wasi.zig +++ b/lib/std/fs/wasi.zig @@ -3,6 +3,8 @@ const builtin = @import("builtin"); const os = std.os; const mem = std.mem; const math = std.math; +const fs = std.fs; +const assert = std.debug.assert; const Allocator = mem.Allocator; const wasi = std.os.wasi; const fd_t = wasi.fd_t; @@ -34,16 +36,27 @@ pub const PreopenType = union(PreopenTypeTag) { // Checks whether `other` refers to a subdirectory of `self` and, if so, // returns the relative path to `other` from `self` + // + // Expects `other` to be a canonical path, not containing "." or ".." pub fn getRelativePath(self: Self, other: PreopenType) ?[]const u8 { if (std.meta.activeTag(self) != std.meta.activeTag(other)) return null; switch (self) { - PreopenTypeTag.Dir => |this_path| { + PreopenTypeTag.Dir => |self_path| { const other_path = other.Dir; - if (mem.indexOfDiff(u8, this_path, other_path)) |index| { - if (index < this_path.len) return null; + if (mem.indexOfDiff(u8, self_path, other_path)) |index| { + if (index < self_path.len) return null; + } + + const rel_path = other_path[self_path.len..]; + if (rel_path.len == 0) { + return rel_path; + } else if (rel_path[0] == '/') { + return rel_path[1..]; + } else { + if (self_path[self_path.len - 1] != '/') return null; + return rel_path; } - return other_path[this_path.len..]; }, } } @@ -130,7 +143,22 @@ pub const PreopenList = struct { /// the preopen list still contains all valid preopened file descriptors that are valid /// for use. Therefore, it is fine to call `find`, `asSlice`, or `toOwnedSlice`. Finally, /// `deinit` still must be called! - pub fn populate(self: *Self) Error!void { + /// + /// Usage of `cwd_root`: + /// If provided, `cwd_root` is inserted as prefix for any Preopens that + /// begin with "." and all paths are normalized as POSIX-style absolute + /// paths. `cwd_root` must be an absolute path. + /// + /// For example: + /// "./foo/bar" -> "{cwd_root}/foo/bar" + /// "foo/bar" -> "/foo/bar" + /// "/foo/bar" -> "/foo/bar" + /// + /// If `cwd_root` is not provided, all preopen directories are unmodified. + /// + pub fn populate(self: *Self, cwd_root: ?[]const u8) Error!void { + if (cwd_root) |root| assert(fs.path.isAbsolute(root)); + // Clear contents if we're being called again for (self.toOwnedSlice()) |preopen| { switch (preopen.@"type") { @@ -140,6 +168,7 @@ pub const PreopenList = struct { errdefer self.deinit(); var fd: fd_t = 3; // start fd has to be beyond stdio fds + var path_buf: [fs.MAX_PATH_BYTES]u8 = undefined; while (true) { var buf: prestat_t = undefined; switch (wasi.fd_prestat_get(fd, &buf)) { @@ -156,14 +185,34 @@ pub const PreopenList = struct { else => |err| return os.unexpectedErrno(err), } const preopen_len = buf.u.dir.pr_name_len; - const path_buf = try self.buffer.allocator.alloc(u8, preopen_len); - mem.set(u8, path_buf, 0); - switch (wasi.fd_prestat_dir_name(fd, path_buf.ptr, preopen_len)) { + + mem.set(u8, path_buf[0..preopen_len], 0); + switch (wasi.fd_prestat_dir_name(fd, &path_buf, preopen_len)) { .SUCCESS => {}, else => |err| return os.unexpectedErrno(err), } - const preopen = Preopen.new(fd, PreopenType{ .Dir = path_buf }); + // Unfortunately, WASI runtimes (e.g. wasmer) are not consistent about whether the + // NULL sentinel is included in the reported Preopen name_len + const raw_path = if (path_buf[preopen_len - 1] == 0) blk: { + break :blk path_buf[0 .. preopen_len - 1]; + } else path_buf[0..preopen_len]; + + // If we were provided a CWD root to resolve against, we try to treat Preopen dirs as + // POSIX paths, relative to "/" or `cwd_root` depending on whether they start with "." + const path = if (cwd_root) |cwd| blk: { + const resolve_paths: [][]const u8 = if (raw_path[0] == '.') &.{ cwd, raw_path } else &.{ "/", raw_path }; + break :blk fs.path.resolve(self.buffer.allocator, resolve_paths) catch |err| switch (err) { + error.CurrentWorkingDirectoryUnlinked => unreachable, // root is absolute, so CWD not queried + else => |e| return e, + }; + } else blk: { + // If we were provided no CWD root, we preserve the preopen dir without resolving + break :blk try self.buffer.allocator.dupe(u8, raw_path); + }; + errdefer self.buffer.allocator.free(path); + const preopen = Preopen.new(fd, .{ .Dir = path }); + try self.buffer.append(preopen); fd = try math.add(fd_t, fd, 1); } @@ -171,27 +220,22 @@ pub const PreopenList = struct { /// Find a preopen which includes access to `preopen_type`. /// - /// If the preopen exists, `relative_path` is updated to point to the relative - /// portion of `preopen_type` and the matching Preopen is returned. If multiple - /// preopens match the provided resource, the most recent one is used. + /// If multiple preopens match the provided resource, the most specific + /// match is returned. More recent preopens take priority, as well. pub fn findContaining(self: Self, preopen_type: PreopenType) ?PreopenUri { - // Search in reverse, so that most recently added preopens take precedence - var k: usize = self.buffer.items.len; - while (k > 0) { - k -= 1; + var best_match: ?PreopenUri = null; - const preopen = self.buffer.items[k]; - if (preopen.@"type".getRelativePath(preopen_type)) |rel_path_orig| { - var rel_path = rel_path_orig; - while (rel_path.len > 0 and rel_path[0] == '/') rel_path = rel_path[1..]; - - return PreopenUri{ - .base = preopen, - .relative_path = if (rel_path.len == 0) "." else rel_path, - }; + for (self.buffer.items) |preopen| { + if (preopen.@"type".getRelativePath(preopen_type)) |rel_path| { + if (best_match == null or rel_path.len <= best_match.?.relative_path.len) { + best_match = PreopenUri{ + .base = preopen, + .relative_path = if (rel_path.len == 0) "." else rel_path, + }; + } } } - return null; + return best_match; } /// Find preopen by fd. If the preopen exists, return it. @@ -233,8 +277,20 @@ test "extracting WASI preopens" { var preopens = PreopenList.init(std.testing.allocator); defer preopens.deinit(); - try preopens.populate(); + try preopens.populate(null); const preopen = preopens.find(PreopenType{ .Dir = "." }) orelse unreachable; try std.testing.expect(preopen.@"type".eql(PreopenType{ .Dir = "." })); + + const po_type1 = PreopenType{ .Dir = "/" }; + try std.testing.expect(std.mem.eql(u8, po_type1.getRelativePath(.{ .Dir = "/" }).?, "")); + try std.testing.expect(std.mem.eql(u8, po_type1.getRelativePath(.{ .Dir = "/test/foobar" }).?, "test/foobar")); + + const po_type2 = PreopenType{ .Dir = "/test/foo" }; + try std.testing.expect(po_type2.getRelativePath(.{ .Dir = "/test/foobar" }) == null); + + const po_type3 = PreopenType{ .Dir = "/test" }; + try std.testing.expect(std.mem.eql(u8, po_type3.getRelativePath(.{ .Dir = "/test" }).?, "")); + try std.testing.expect(std.mem.eql(u8, po_type3.getRelativePath(.{ .Dir = "/test/" }).?, "")); + try std.testing.expect(std.mem.eql(u8, po_type3.getRelativePath(.{ .Dir = "/test/foo/bar" }).?, "foo/bar")); } diff --git a/lib/std/os.zig b/lib/std/os.zig index df42d14c3d..f9ee65a317 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1431,10 +1431,8 @@ var wasi_cwd = if (builtin.os.tag == .wasi and !builtin.link_libc) struct { preopens: ?PreopenList = null, // Memory buffer for storing the relative portion of the CWD path_buffer: [MAX_PATH_BYTES]u8 = undefined, - // Current Working Directory, stored as an fd_t and a relative path - cwd: ?RelativePathWasi = null, - // Preopen associated with `cwd`, if any - cwd_preopen: ?Preopen = null, + // The absolute path associated with the current working directory + cwd: []const u8 = "/", }{} else undefined; /// Initialize the available Preopen list on WASI and set the CWD to `cwd_init`. @@ -1444,31 +1442,33 @@ var wasi_cwd = if (builtin.os.tag == .wasi and !builtin.link_libc) struct { /// This must be called before using any relative or absolute paths with `std.os` /// functions, if you are on WASI without linking libc. /// +/// The current working directory is initialized to `cwd_root`, and `cwd_root` +/// is inserted as a prefix for any Preopens whose dir begins with "." +/// For example: +/// "./foo/bar" - canonicalizes to -> "{cwd_root}/foo/bar" +/// "foo/bar" - canonicalizes to -> "/foo/bar" +/// "/foo/bar" - canonicalizes to -> "/foo/bar" +/// +/// `cwd_root` must be an absolute path. For initialization behavior similar to +/// wasi-libc, use "/" as the `cwd_root` +/// /// `alloc` must not be a temporary or leak-detecting allocator, since `std.os` /// retains ownership of allocations internally and may never call free(). -pub fn initPreopensWasi(alloc: Allocator, cwd_init: ?[]const u8) !void { +pub fn initPreopensWasi(alloc: Allocator, cwd_root: []const u8) !void { if (builtin.os.tag == .wasi) { if (!builtin.link_libc) { - if (wasi_cwd.preopens == null) { - var preopen_list = PreopenList.init(alloc); - try preopen_list.populate(); - wasi_cwd.preopens = preopen_list; - } - if (cwd_init) |cwd| { - const preopen = wasi_cwd.preopens.?.findContaining(.{ .Dir = cwd }); - if (preopen) |po| { - wasi_cwd.cwd_preopen = po.base; - wasi_cwd.cwd = RelativePathWasi{ - .dir_fd = po.base.fd, - .relative_path = po.relative_path, - }; - } else { - // No matching preopen found - return error.FileNotFound; - } - } + var preopen_list = PreopenList.init(alloc); + errdefer preopen_list.deinit(); + try preopen_list.populate(cwd_root); + + var path_alloc = std.heap.FixedBufferAllocator.init(&wasi_cwd.path_buffer); + wasi_cwd.cwd = try path_alloc.allocator().dupe(u8, cwd_root); + + if (wasi_cwd.preopens) |preopens| preopens.deinit(); + wasi_cwd.preopens = preopen_list; } else { - if (cwd_init) |cwd| try chdir(cwd); + // wasi-libc defaults to an effective CWD root of "/" + if (!mem.eql(u8, cwd_root, "/")) return error.UnsupportedDirectory; } } } @@ -1477,69 +1477,22 @@ pub fn initPreopensWasi(alloc: Allocator, cwd_init: ?[]const u8) !void { /// /// For absolute paths, this automatically searches among available Preopens to find /// a match. For relative paths, it uses the "emulated" CWD. +/// Automatically looks up the correct Preopen corresponding to the provided path. pub fn resolvePathWasi(path: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) !RelativePathWasi { - // Note: Due to WASI's "sandboxed" file handles, operations with this RelativePathWasi - // will fail if the relative path navigates outside of `dir_fd` using ".." - return resolvePathAndGetWasiPreopen(path, null, out_buffer); -} - -fn resolvePathAndGetWasiPreopen(path: []const u8, preopen: ?*?Preopen, out_buffer: *[MAX_PATH_BYTES]u8) !RelativePathWasi { var allocator = std.heap.FixedBufferAllocator.init(out_buffer); var alloc = allocator.allocator(); - if (fs.path.isAbsolute(path) or wasi_cwd.cwd == null) { - if (wasi_cwd.preopens == null) @panic("On WASI, `initPreopensWasi` must be called to initialize preopens " ++ - "before using any CWD-relative or absolute paths.\n"); + const abs_path = fs.path.resolve(alloc, &.{ wasi_cwd.cwd, path }) catch return error.NameTooLong; + const preopen_uri = wasi_cwd.preopens.?.findContaining(.{ .Dir = abs_path }); - if (mem.startsWith(u8, path, "/preopens/fd/")) { - // "/preopens/fd/" is a special prefix, which refers to a Preopen directly by fd - const fd_start = "/preopens/fd/".len; - const fd_end = mem.indexOfScalarPos(u8, path, fd_start, '/') orelse path.len; - const fd = std.fmt.parseUnsigned(fd_t, path[fd_start..fd_end], 10) catch unreachable; - const rel_path = if (path.len > fd_end + 1) path[fd_end + 1 ..] else "."; - - if (preopen) |p| p.* = wasi_cwd.preopens.?.findByFd(fd); - return RelativePathWasi{ - .dir_fd = fd, - .relative_path = alloc.dupe(u8, rel_path) catch return error.NameTooLong, - }; - } - - // For any other absolute path, we need to lookup a containing Preopen - const abs_path = fs.path.resolve(alloc, &.{ "/", path }) catch return error.NameTooLong; - const preopen_uri = wasi_cwd.preopens.?.findContaining(.{ .Dir = abs_path }); - - if (preopen_uri) |po| { - if (preopen) |p| p.* = po.base; - return RelativePathWasi{ - .dir_fd = po.base.fd, - .relative_path = po.relative_path, - }; - } else { - // No matching preopen found - return error.AccessDenied; - } - } else { - const cwd = wasi_cwd.cwd.?; - - // If the path is empty or "." or "./", return CWD - if (std.mem.eql(u8, path, ".") or std.mem.eql(u8, path, "./")) { - return cwd; - } - - // First resolve a combined path, where the "/" corresponds to `cwd.dir_fd` - // not the true filesystem root - const paths = &.{ "/", cwd.relative_path, path }; - const resolved_path = fs.path.resolve(alloc, paths) catch return error.NameTooLong; - - // Strip off the fake root to get the relative path w.r.t. `cwd.dir_fd` - const resolved_relative_path = resolved_path[1..]; - - if (preopen) |p| p.* = wasi_cwd.cwd_preopen; + if (preopen_uri) |po| { return RelativePathWasi{ - .dir_fd = cwd.dir_fd, - .relative_path = resolved_relative_path, + .dir_fd = po.base.fd, + .relative_path = po.relative_path, }; + } else { + // No matching preopen found + return error.AccessDenied; } } @@ -1980,11 +1933,7 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { if (builtin.os.tag == .windows) { return windows.GetCurrentDirectory(out_buffer); } else if (builtin.os.tag == .wasi and !builtin.link_libc) { - var buf: [MAX_PATH_BYTES]u8 = undefined; - const path = realpathWasi(".", &buf) catch |err| switch (err) { - error.NameTooLong => return error.NameTooLong, - error.InvalidHandle => return error.CurrentWorkingDirectoryUnlinked, - }; + const path = wasi_cwd.cwd; if (out_buffer.len < path.len) return error.NameTooLong; std.mem.copy(u8, out_buffer, path); return out_buffer[0..path.len]; @@ -2949,16 +2898,17 @@ pub const ChangeCurDirError = error{ /// `dir_path` is recommended to be a UTF-8 encoded string. pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { if (builtin.os.tag == .wasi and !builtin.link_libc) { - var preopen: ?Preopen = null; - const path = try resolvePathAndGetWasiPreopen(dir_path, &preopen, &wasi_cwd.path_buffer); + var buf: [MAX_PATH_BYTES]u8 = undefined; + var alloc = std.heap.FixedBufferAllocator.init(&buf); + const path = try fs.resolve(alloc.allocator(), &.{ wasi_cwd.cwd, dir_path }); - const dirinfo = try fstatat(path.dir_fd, path.relative_path, 0); + const dirinfo = try fstatat(AT.FDCWD, path, 0); if (dirinfo.filetype != .DIRECTORY) { return error.NotDir; } - wasi_cwd.cwd_preopen = preopen; - wasi_cwd.cwd = path; + var cwd_alloc = std.heap.FixedBufferAllocator.init(&wasi_cwd.path_buffer); + wasi_cwd.cwd = try cwd_alloc.allocator().dupe(u8, path); return; } else if (builtin.os.tag == .windows) { var utf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined; @@ -3010,29 +2960,15 @@ pub const FchdirError = error{ } || UnexpectedError; pub fn fchdir(dirfd: fd_t) FchdirError!void { - if (builtin.os.tag == .wasi) { - // Check that this is a directory - const dirinfo = fstatat(dirfd, ".", 0) catch unreachable; - if (dirinfo.filetype != .DIRECTORY) { - return error.NotDir; - } - - wasi_cwd.cwd = .{ - .dir_fd = dirfd, - .relative_path = ".", - }; - wasi_cwd.cwd_preopen = null; - } else { - while (true) { - switch (errno(system.fchdir(dirfd))) { - .SUCCESS => return, - .ACCES => return error.AccessDenied, - .BADF => unreachable, - .NOTDIR => return error.NotDir, - .INTR => continue, - .IO => return error.FileSystem, - else => |err| return unexpectedErrno(err), - } + while (true) { + switch (errno(system.fchdir(dirfd))) { + .SUCCESS => return, + .ACCES => return error.AccessDenied, + .BADF => unreachable, + .NOTDIR => return error.NotDir, + .INTR => continue, + .IO => return error.FileSystem, + else => |err| return unexpectedErrno(err), } } } @@ -5102,47 +5038,17 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE const pathname_w = try windows.sliceToPrefixedFileW(pathname); return realpathW(pathname_w.span(), out_buffer); } else if (builtin.os.tag == .wasi and !builtin.link_libc) { - return realpathWasi(pathname, out_buffer); + var alloc = std.heap.FixedBufferAllocator.init(out_buffer); + + // NOTE: This emulation is incomplete. Symbolic links are not + // currently expanded during path canonicalization. + const paths = &.{ wasi_cwd.cwd, pathname }; + return fs.path.resolve(alloc.allocator(), paths) catch error.NameTooLong; } const pathname_c = try toPosixPath(pathname); return realpathZ(&pathname_c, out_buffer); } -/// Return an emulated canonicalized absolute pathname on WASI. -/// -/// NOTE: This emulation is incomplete. Symbolic links are not -/// currently expanded during path canonicalization. -fn realpathWasi(pathname: []const u8, out_buffer: []u8) ![]u8 { - var alloc = std.heap.FixedBufferAllocator.init(out_buffer); - if (fs.path.isAbsolute(pathname)) - return try fs.path.resolve(alloc.allocator(), &.{pathname}) catch error.NameTooLong; - if (wasi_cwd.cwd) |cwd| { - if (wasi_cwd.cwd_preopen) |po| { - var base_cwd_dir = switch (po.@"type") { - .Dir => |dir| dir, - }; - const paths: [][]const u8 = if (fs.path.isAbsolute(base_cwd_dir)) blk: { - break :blk &.{ base_cwd_dir, cwd.relative_path, pathname }; - } else blk: { - // No absolute path is associated with this preopen, so - // instead we use a special "/preopens/fd//" prefix - var buf: [16]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf); - std.fmt.formatInt(po.fd, 10, .lower, .{}, fbs.writer()) catch return error.NameTooLong; - break :blk &.{ "/preopens/fd/", fbs.getWritten(), cwd.relative_path, pathname }; - }; - - return fs.path.resolve(alloc.allocator(), paths) catch error.NameTooLong; - } else { - // The CWD is not rooted to an existing Preopen, - // so we have no way to know its absolute path - return error.InvalidHandle; - } - } else { - return try fs.path.resolve(alloc.allocator(), &.{ "/", pathname }) catch error.NameTooLong; - } -} - /// Same as `realpath` except `pathname` is null-terminated. pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { if (builtin.os.tag == .windows) { diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 8598c7b3be..ae0d14ef7e 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -49,7 +49,7 @@ test "chdir smoke test" { test "open smoke test" { if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; - if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); + if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); // TODO verify file attributes using `fstat` @@ -104,7 +104,7 @@ test "open smoke test" { test "openat smoke test" { if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; - if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); + if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); // TODO verify file attributes using `fstatat` @@ -141,7 +141,7 @@ test "openat smoke test" { test "symlink with relative paths" { if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; - if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); + if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); const cwd = fs.cwd(); cwd.deleteFile("file.txt") catch {}; @@ -197,7 +197,7 @@ test "link with relative paths" { if (builtin.link_libc) { return error.SkipZigTest; } else { - try os.initPreopensWasi(std.heap.page_allocator, "."); + try os.initPreopensWasi(std.heap.page_allocator, "/"); } }, .linux, .solaris => {}, @@ -237,7 +237,7 @@ test "link with relative paths" { test "linkat with different directories" { switch (native_os) { - .wasi => if (!builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."), + .wasi => if (!builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"), .linux, .solaris => {}, else => return error.SkipZigTest, } @@ -898,7 +898,7 @@ test "POSIX file locking with fcntl" { test "rename smoke test" { if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; - if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); + if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -955,7 +955,7 @@ test "rename smoke test" { test "access smoke test" { if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; - if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "."); + if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); var tmp = tmpDir(.{}); defer tmp.cleanup(); diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 56cc86d769..004e2d0fa7 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -380,7 +380,7 @@ fn getCwdOrWasiPreopen() std.fs.Dir { if (builtin.os.tag == .wasi and !builtin.link_libc) { var preopens = std.fs.wasi.PreopenList.init(allocator); defer preopens.deinit(); - preopens.populate() catch + preopens.populate(null) catch @panic("unable to make tmp dir for testing: unable to populate preopens"); const preopen = preopens.find(std.fs.wasi.PreopenType{ .Dir = "." }) orelse @panic("unable to make tmp dir for testing: didn't find '.' in the preopens"); From 2aeaa1cfc4c563a24525fb27fd2783bfd5da808a Mon Sep 17 00:00:00 2001 From: Yusuf Bham <10470872+fifty-six@users.noreply.github.com> Date: Sun, 17 Apr 2022 06:15:15 -0400 Subject: [PATCH 1176/2031] std.os.uefi: fix GUID formatting (#11452) --- lib/std/os/uefi.zig | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/std/os/uefi.zig b/lib/std/os/uefi.zig index 4644130e7f..386ad7796a 100644 --- a/lib/std/os/uefi.zig +++ b/lib/std/os/uefi.zig @@ -53,13 +53,19 @@ pub const Guid = extern struct { ) !void { _ = options; if (f.len == 0) { - return std.fmt.format(writer, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{ - self.time_low, - self.time_mid, - self.time_high_and_version, - self.clock_seq_high_and_reserved, - self.clock_seq_low, - self.node, + const fmt = std.fmt.fmtSliceHexLower; + + const time_low = @byteSwap(u32, self.time_low); + const time_mid = @byteSwap(u16, self.time_mid); + const time_high_and_version = @byteSwap(u16, self.time_high_and_version); + + return std.fmt.format(writer, "{:0>8}-{:0>4}-{:0>4}-{:0>2}{:0>2}-{:0>12}", .{ + fmt(std.mem.asBytes(&time_low)), + fmt(std.mem.asBytes(&time_mid)), + fmt(std.mem.asBytes(&time_high_and_version)), + fmt(std.mem.asBytes(&self.clock_seq_high_and_reserved)), + fmt(std.mem.asBytes(&self.clock_seq_low)), + fmt(std.mem.asBytes(&self.node)), }); } else { @compileError("Unknown format character: '" ++ f ++ "'"); @@ -133,3 +139,14 @@ pub const TimeCapabilities = extern struct { /// File Handle as specified in the EFI Shell Spec pub const FileHandle = *opaque {}; + +test "GUID formatting" { + var bytes = [_]u8{ 137, 60, 203, 50, 128, 128, 124, 66, 186, 19, 80, 73, 135, 59, 194, 135 }; + + var guid = @bitCast(Guid, bytes); + + var str = try std.fmt.allocPrint(std.testing.allocator, "{}", .{guid}); + defer std.testing.allocator.free(str); + + try std.testing.expect(std.mem.eql(u8, str, "32cb3c89-8080-427c-ba13-5049873bc287")); +} From a7c05c06bef1570546cae4c45a76027819c7fa09 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 17 Apr 2022 04:06:51 -0700 Subject: [PATCH 1177/2031] stage2: expose progress bar API to linker backends This gives us insight as to what is happening when we are waiting for things such as LLVM emit object and LLD linking. --- src/Compilation.zig | 30 ++++++++++++++++-------------- src/codegen/llvm.zig | 7 ++++++- src/link.zig | 44 ++++++++++++++++++++++---------------------- src/link/C.zig | 10 +++++++--- src/link/Coff.zig | 25 +++++++++++++++++-------- src/link/Elf.zig | 25 +++++++++++++++++-------- src/link/MachO.zig | 23 ++++++++++++++--------- src/link/NvPtx.zig | 8 ++++---- src/link/Plan9.zig | 10 +++++++--- src/link/SpirV.zig | 10 +++++++--- src/link/Wasm.zig | 25 +++++++++++++++++-------- 11 files changed, 134 insertions(+), 83 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 6c486de36a..f0e490c67d 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2084,7 +2084,13 @@ pub fn update(comp: *Compilation) !void { } } - try comp.performAllTheWork(); + // If the terminal is dumb, we dont want to show the user all the output. + var progress: std.Progress = .{ .dont_print_on_dumb = true }; + const main_progress_node = progress.start("", 0); + defer main_progress_node.end(); + if (comp.color == .off) progress.terminal = null; + + try comp.performAllTheWork(main_progress_node); if (!use_stage1) { if (comp.bin_file.options.module) |module| { @@ -2158,9 +2164,9 @@ pub fn update(comp: *Compilation) !void { .path = dir_path, }; - try comp.flush(); + try comp.flush(main_progress_node); } else { - try comp.flush(); + try comp.flush(main_progress_node); } // Failure here only means an unnecessary cache miss. @@ -2171,7 +2177,7 @@ pub fn update(comp: *Compilation) !void { assert(comp.bin_file.lock == null); comp.bin_file.lock = man.toOwnedLock(); } else { - try comp.flush(); + try comp.flush(main_progress_node); } // Unload all source files to save memory. @@ -2188,8 +2194,8 @@ pub fn update(comp: *Compilation) !void { } } -fn flush(comp: *Compilation) !void { - try comp.bin_file.flush(comp); // This is needed before reading the error flags. +fn flush(comp: *Compilation, prog_node: *std.Progress.Node) !void { + try comp.bin_file.flush(comp, prog_node); // This is needed before reading the error flags. comp.link_error_flags = comp.bin_file.errorFlags(); const use_stage1 = build_options.omit_stage2 or @@ -2590,14 +2596,10 @@ pub fn getCompileLogOutput(self: *Compilation) []const u8 { return module.compile_log_text.items; } -pub fn performAllTheWork(comp: *Compilation) error{ TimerUnsupported, OutOfMemory }!void { - // If the terminal is dumb, we dont want to show the user all the - // output. - var progress: std.Progress = .{ .dont_print_on_dumb = true }; - var main_progress_node = progress.start("", 0); - defer main_progress_node.end(); - if (comp.color == .off) progress.terminal = null; - +pub fn performAllTheWork( + comp: *Compilation, + main_progress_node: *std.Progress.Node, +) error{ TimerUnsupported, OutOfMemory }!void { // Here we queue up all the AstGen tasks first, followed by C object compilation. // We wait until the AstGen tasks are all completed before proceeding to the // (at least for now) single-threaded main work queue. However, C object compilation diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 364cccf335..cdb9addcff 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -469,7 +469,12 @@ pub const Object = struct { _ = builder.buildRet(is_lt); } - pub fn flushModule(self: *Object, comp: *Compilation) !void { + pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void { + var sub_prog_node = prog_node.start("LLVM Emit Object", 0); + sub_prog_node.activate(); + sub_prog_node.context.refresh(); + defer sub_prog_node.end(); + try self.genErrorNameTable(comp); try self.genCmpLtErrorsLenFunction(comp); diff --git a/src/link.zig b/src/link.zig index 2d7e577b70..c449a37ee6 100644 --- a/src/link.zig +++ b/src/link.zig @@ -573,7 +573,7 @@ pub const File = struct { /// Commit pending changes and write headers. Takes into account final output mode /// and `use_lld`, not only `effectiveOutputMode`. - pub fn flush(base: *File, comp: *Compilation) !void { + pub fn flush(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) !void { if (comp.clang_preprocessor_mode == .yes) { const emit = base.options.emit orelse return; // -fno-emit-bin // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case) @@ -591,32 +591,32 @@ pub const File = struct { const use_lld = build_options.have_llvm and base.options.use_lld; if (use_lld and base.options.output_mode == .Lib and base.options.link_mode == .Static) { - return base.linkAsArchive(comp); + return base.linkAsArchive(comp, prog_node); } switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).flush(comp), - .elf => return @fieldParentPtr(Elf, "base", base).flush(comp), - .macho => return @fieldParentPtr(MachO, "base", base).flush(comp), - .c => return @fieldParentPtr(C, "base", base).flush(comp), - .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp), - .spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp), - .plan9 => return @fieldParentPtr(Plan9, "base", base).flush(comp), - .nvptx => return @fieldParentPtr(NvPtx, "base", base).flush(comp), + .coff => return @fieldParentPtr(Coff, "base", base).flush(comp, prog_node), + .elf => return @fieldParentPtr(Elf, "base", base).flush(comp, prog_node), + .macho => return @fieldParentPtr(MachO, "base", base).flush(comp, prog_node), + .c => return @fieldParentPtr(C, "base", base).flush(comp, prog_node), + .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp, prog_node), + .spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp, prog_node), + .plan9 => return @fieldParentPtr(Plan9, "base", base).flush(comp, prog_node), + .nvptx => return @fieldParentPtr(NvPtx, "base", base).flush(comp, prog_node), } } /// Commit pending changes and write headers. Works based on `effectiveOutputMode` /// rather than final output mode. - pub fn flushModule(base: *File, comp: *Compilation) !void { + pub fn flushModule(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) !void { switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).flushModule(comp), - .elf => return @fieldParentPtr(Elf, "base", base).flushModule(comp), - .macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp), - .c => return @fieldParentPtr(C, "base", base).flushModule(comp), - .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp), - .spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp), - .plan9 => return @fieldParentPtr(Plan9, "base", base).flushModule(comp), - .nvptx => return @fieldParentPtr(NvPtx, "base", base).flushModule(comp), + .coff => return @fieldParentPtr(Coff, "base", base).flushModule(comp, prog_node), + .elf => return @fieldParentPtr(Elf, "base", base).flushModule(comp, prog_node), + .macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp, prog_node), + .c => return @fieldParentPtr(C, "base", base).flushModule(comp, prog_node), + .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp, prog_node), + .spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp, prog_node), + .plan9 => return @fieldParentPtr(Plan9, "base", base).flushModule(comp, prog_node), + .nvptx => return @fieldParentPtr(NvPtx, "base", base).flushModule(comp, prog_node), } } @@ -754,7 +754,7 @@ pub const File = struct { } } - pub fn linkAsArchive(base: *File, comp: *Compilation) !void { + pub fn linkAsArchive(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); @@ -787,9 +787,9 @@ pub const File = struct { } } if (base.options.object_format == .macho) { - try base.cast(MachO).?.flushObject(comp); + try base.cast(MachO).?.flushObject(comp, prog_node); } else { - try base.flushModule(comp); + try base.flushModule(comp, prog_node); } break :blk try fs.path.join(arena, &.{ fs.path.dirname(full_out_path_z).?, base.intermediary_basename.?, diff --git a/src/link/C.zig b/src/link/C.zig index 229990fc8e..63aa2b6030 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -235,14 +235,18 @@ pub fn updateDeclLineNumber(self: *C, module: *Module, decl: *Module.Decl) !void _ = decl; } -pub fn flush(self: *C, comp: *Compilation) !void { - return self.flushModule(comp); +pub fn flush(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void { + return self.flushModule(comp, prog_node); } -pub fn flushModule(self: *C, comp: *Compilation) !void { +pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); + var sub_prog_node = prog_node.start("Flush Module", 0); + sub_prog_node.activate(); + defer sub_prog_node.end(); + const gpa = comp.gpa; const module = self.base.options.module.?; diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 3293cf2dfc..a91f48dfbd 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -829,36 +829,40 @@ pub fn updateDeclExports( } } -pub fn flush(self: *Coff, comp: *Compilation) !void { +pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void { if (self.base.options.emit == null) { if (build_options.have_llvm) { if (self.llvm_object) |llvm_object| { - return try llvm_object.flushModule(comp); + return try llvm_object.flushModule(comp, prog_node); } } return; } if (build_options.have_llvm and self.base.options.use_lld) { - return self.linkWithLLD(comp); + return self.linkWithLLD(comp, prog_node); } else { switch (self.base.options.effectiveOutputMode()) { .Exe, .Obj => {}, .Lib => return error.TODOImplementWritingLibFiles, } - return self.flushModule(comp); + return self.flushModule(comp, prog_node); } } -pub fn flushModule(self: *Coff, comp: *Compilation) !void { +pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); if (build_options.have_llvm) { if (self.llvm_object) |llvm_object| { - return try llvm_object.flushModule(comp); + return try llvm_object.flushModule(comp, prog_node); } } + var sub_prog_node = prog_node.start("COFF Flush", 0); + sub_prog_node.activate(); + defer sub_prog_node.end(); + if (self.text_section_size_dirty) { // Write the new raw size in the .text header var buf: [4]u8 = undefined; @@ -892,7 +896,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void { } } -fn linkWithLLD(self: *Coff, comp: *Compilation) !void { +fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); @@ -924,7 +928,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { } } - try self.flushModule(comp); + try self.flushModule(comp, prog_node); if (fs.path.dirname(full_out_path)) |dirname| { break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? }); @@ -933,6 +937,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { } } else null; + var sub_prog_node = prog_node.start("LLD Link", 0); + sub_prog_node.activate(); + sub_prog_node.context.refresh(); + defer sub_prog_node.end(); + const is_lib = self.base.options.output_mode == .Lib; const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index e5e65011cd..a58321c0ec 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -929,35 +929,39 @@ pub fn populateMissingMetadata(self: *Elf) !void { } } -pub fn flush(self: *Elf, comp: *Compilation) !void { +pub fn flush(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { if (self.base.options.emit == null) { if (build_options.have_llvm) { if (self.llvm_object) |llvm_object| { - return try llvm_object.flushModule(comp); + return try llvm_object.flushModule(comp, prog_node); } } return; } const use_lld = build_options.have_llvm and self.base.options.use_lld; if (use_lld) { - return self.linkWithLLD(comp); + return self.linkWithLLD(comp, prog_node); } switch (self.base.options.output_mode) { - .Exe, .Obj => return self.flushModule(comp), + .Exe, .Obj => return self.flushModule(comp, prog_node), .Lib => return error.TODOImplementWritingLibFiles, } } -pub fn flushModule(self: *Elf, comp: *Compilation) !void { +pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); if (build_options.have_llvm) { if (self.llvm_object) |llvm_object| { - return try llvm_object.flushModule(comp); + return try llvm_object.flushModule(comp, prog_node); } } + var sub_prog_node = prog_node.start("ELF Flush", 0); + sub_prog_node.activate(); + defer sub_prog_node.end(); + // TODO This linker code currently assumes there is only 1 compilation unit and it // corresponds to the Zig source code. const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; @@ -1204,7 +1208,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { assert(!self.debug_strtab_dirty); } -fn linkWithLLD(self: *Elf, comp: *Compilation) !void { +fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); @@ -1236,7 +1240,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { } } - try self.flushModule(comp); + try self.flushModule(comp, prog_node); if (fs.path.dirname(full_out_path)) |dirname| { break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? }); @@ -1245,6 +1249,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { } } else null; + var sub_prog_node = prog_node.start("LLD Link", 0); + sub_prog_node.activate(); + sub_prog_node.context.refresh(); + defer sub_prog_node.end(); + const is_obj = self.base.options.output_mode == .Obj; const is_lib = self.base.options.output_mode == .Lib; const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; diff --git a/src/link/MachO.zig b/src/link/MachO.zig index a7437ed576..e604029382 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -419,33 +419,34 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO { return self; } -pub fn flush(self: *MachO, comp: *Compilation) !void { +pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void { if (self.base.options.emit == null) { if (build_options.have_llvm) { if (self.llvm_object) |llvm_object| { - try llvm_object.flushModule(comp); + try llvm_object.flushModule(comp, prog_node); } } return; } + if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Static) { if (build_options.have_llvm) { - return self.base.linkAsArchive(comp); + return self.base.linkAsArchive(comp, prog_node); } else { log.err("TODO: non-LLVM archiver for MachO object files", .{}); return error.TODOImplementWritingStaticLibFiles; } } - try self.flushModule(comp); + try self.flushModule(comp, prog_node); } -pub fn flushModule(self: *MachO, comp: *Compilation) !void { +pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; if (!use_stage1 and self.base.options.output_mode == .Obj) - return self.flushObject(comp); + return self.flushObject(comp, prog_node); var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); defer arena_allocator.deinit(); @@ -482,7 +483,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { const obj_basename = self.base.intermediary_basename orelse break :blk null; - try self.flushObject(comp); + try self.flushObject(comp, prog_node); if (fs.path.dirname(full_out_path)) |dirname| { break :blk try fs.path.join(arena, &.{ dirname, obj_basename }); @@ -491,6 +492,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { } } else null; + var sub_prog_node = prog_node.start("MachO Flush", 0); + sub_prog_node.activate(); + defer sub_prog_node.end(); + const is_lib = self.base.options.output_mode == .Lib; const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; @@ -1111,13 +1116,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { self.cold_start = false; } -pub fn flushObject(self: *MachO, comp: *Compilation) !void { +pub fn flushObject(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); if (build_options.have_llvm) if (self.llvm_object) |llvm_object| - return llvm_object.flushModule(comp); + return llvm_object.flushModule(comp, prog_node); return error.TODOImplementWritingObjFiles; } diff --git a/src/link/NvPtx.zig b/src/link/NvPtx.zig index 5d0f578d1d..b518dc3f68 100644 --- a/src/link/NvPtx.zig +++ b/src/link/NvPtx.zig @@ -97,11 +97,11 @@ pub fn freeDecl(self: *NvPtx, decl: *Module.Decl) void { return self.llvm_object.freeDecl(decl); } -pub fn flush(self: *NvPtx, comp: *Compilation) !void { - return self.flushModule(comp); +pub fn flush(self: *NvPtx, comp: *Compilation, prog_node: *std.Progress.Node) !void { + return self.flushModule(comp, prog_node); } -pub fn flushModule(self: *NvPtx, comp: *Compilation) !void { +pub fn flushModule(self: *NvPtx, comp: *Compilation, prog_node: *std.Progress.Node) !void { if (!build_options.have_llvm) return; if (build_options.skip_non_native) { @panic("Attempted to compile for architecture that was disabled by build configuration"); @@ -117,5 +117,5 @@ pub fn flushModule(self: *NvPtx, comp: *Compilation) !void { }; hack_comp.bin_file.options.emit = null; } - return try self.llvm_object.flushModule(hack_comp); + return try self.llvm_object.flushModule(hack_comp, prog_node); } diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 3269cb67d4..5d740dd2b9 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -351,7 +351,7 @@ fn updateFinish(self: *Plan9, decl: *Module.Decl) !void { } } -pub fn flush(self: *Plan9, comp: *Compilation) !void { +pub fn flush(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.Node) !void { assert(!self.base.options.use_lld); switch (self.base.options.effectiveOutputMode()) { @@ -360,7 +360,7 @@ pub fn flush(self: *Plan9, comp: *Compilation) !void { .Obj => return error.TODOImplementPlan9Objs, .Lib => return error.TODOImplementWritingLibFiles, } - return self.flushModule(comp); + return self.flushModule(comp, prog_node); } pub fn changeLine(l: *std.ArrayList(u8), delta_line: i32) !void { @@ -387,7 +387,7 @@ fn declCount(self: *Plan9) usize { return self.data_decl_table.count() + fn_decl_count; } -pub fn flushModule(self: *Plan9, comp: *Compilation) !void { +pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.Node) !void { if (build_options.skip_non_native and builtin.object_format != .plan9) { @panic("Attempted to compile for object format that was disabled by build configuration"); } @@ -396,6 +396,10 @@ pub fn flushModule(self: *Plan9, comp: *Compilation) !void { const tracy = trace(@src()); defer tracy.end(); + var sub_prog_node = prog_node.start("Flush Module", 0); + sub_prog_node.activate(); + defer sub_prog_node.end(); + log.debug("flushModule", .{}); defer assert(self.hdr.entry != 0x0); diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index 19cb1373ef..e4d032539f 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -174,15 +174,15 @@ pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void { self.decl_table.swapRemoveAt(index); } -pub fn flush(self: *SpirV, comp: *Compilation) !void { +pub fn flush(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.Node) !void { if (build_options.have_llvm and self.base.options.use_lld) { return error.LLD_LinkingIsTODO_ForSpirV; // TODO: LLD Doesn't support SpirV at all. } else { - return self.flushModule(comp); + return self.flushModule(comp, prog_node); } } -pub fn flushModule(self: *SpirV, comp: *Compilation) !void { +pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.Node) !void { if (build_options.skip_non_native) { @panic("Attempted to compile for architecture that was disabled by build configuration"); } @@ -190,6 +190,10 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { const tracy = trace(@src()); defer tracy.end(); + var sub_prog_node = prog_node.start("Flush Module", 0); + sub_prog_node.activate(); + defer sub_prog_node.end(); + const module = self.base.options.module.?; const target = comp.getTarget(); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 961e382112..1ed733774f 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -1477,32 +1477,36 @@ fn resetState(self: *Wasm) void { self.code_section_index = null; } -pub fn flush(self: *Wasm, comp: *Compilation) !void { +pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { if (self.base.options.emit == null) { if (build_options.have_llvm) { if (self.llvm_object) |llvm_object| { - return try llvm_object.flushModule(comp); + return try llvm_object.flushModule(comp, prog_node); } } return; } if (build_options.have_llvm and self.base.options.use_lld) { - return self.linkWithLLD(comp); + return self.linkWithLLD(comp, prog_node); } else { - return self.flushModule(comp); + return self.flushModule(comp, prog_node); } } -pub fn flushModule(self: *Wasm, comp: *Compilation) !void { +pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); if (build_options.have_llvm) { if (self.llvm_object) |llvm_object| { - return try llvm_object.flushModule(comp); + return try llvm_object.flushModule(comp, prog_node); } } + var sub_prog_node = prog_node.start("WASM Flush", 0); + sub_prog_node.activate(); + defer sub_prog_node.end(); + // ensure the error names table is populated when an error name is referenced try self.populateErrorNameTable(); @@ -2028,7 +2032,7 @@ fn emitImport(self: *Wasm, writer: anytype, import: types.Import) !void { } } -fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { +fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); @@ -2060,7 +2064,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { } } - try self.flushModule(comp); + try self.flushModule(comp, prog_node); if (fs.path.dirname(full_out_path)) |dirname| { break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? }); @@ -2069,6 +2073,11 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { } } else null; + var sub_prog_node = prog_node.start("LLD Link", 0); + sub_prog_node.activate(); + sub_prog_node.context.refresh(); + defer sub_prog_node.end(); + const is_obj = self.base.options.output_mode == .Obj; const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt and !is_obj) From f8d2b87fa122a948e2c8e1056e2ec4cfd4cf01bf Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Sun, 17 Apr 2022 15:56:59 -0700 Subject: [PATCH 1178/2031] Update doc comment of ArrayHashMap to include 4th arg in eql fns 4th argument was added in cf88cf2657d721c68055a284e8c498a18639f74c --- lib/std/array_hash_map.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/array_hash_map.zig b/lib/std/array_hash_map.zig index e582f55819..aed60a1f0d 100644 --- a/lib/std/array_hash_map.zig +++ b/lib/std/array_hash_map.zig @@ -66,11 +66,11 @@ pub fn hashString(s: []const u8) u32 { /// the alternative `std.HashMap`. /// Context must be a struct type with two member functions: /// hash(self, K) u32 -/// eql(self, K, K) bool +/// eql(self, K, K, usize) bool /// Adapted variants of many functions are provided. These variants /// take a pseudo key instead of a key. Their context must have the functions: /// hash(self, PseudoKey) u32 -/// eql(self, PseudoKey, K) bool +/// eql(self, PseudoKey, K, usize) bool pub fn ArrayHashMap( comptime K: type, comptime V: type, From b2344cc18e9fa5dfa1d19025caa2b0f583498734 Mon Sep 17 00:00:00 2001 From: Wojtek Mach Date: Mon, 18 Apr 2022 11:43:17 +0200 Subject: [PATCH 1179/2031] Support `--subsystem=x` instead of `--subsystem,x` --- src/main.zig | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main.zig b/src/main.zig index aec88c1175..e341a10f99 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1438,11 +1438,6 @@ fn buildOutputType( mem.eql(u8, linker_arg, "-static")) { force_static_libs = true; - } else if (mem.eql(u8, linker_arg, "--subsystem")) { - const next_arg = split_it.next() orelse { - fatal("expected parameter after {s}", .{linker_arg}); - }; - subsystem = try parseSubSystem(next_arg); } else { try linker_args.append(linker_arg); } @@ -1586,6 +1581,12 @@ fn buildOutputType( fatal("expected linker arg after '{s}'", .{arg}); } try rpath_list.append(linker_args.items[i]); + } else if (mem.eql(u8, arg, "--subsystem")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{s}'", .{arg}); + } + subsystem = try parseSubSystem(linker_args.items[i]); } else if (mem.eql(u8, arg, "-I") or mem.eql(u8, arg, "--dynamic-linker") or mem.eql(u8, arg, "-dynamic-linker")) From 2193f7c4a2b56f67bf07d27c3f715cf3969c392f Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Mon, 18 Apr 2022 13:11:37 +0200 Subject: [PATCH 1180/2031] wasm: Add support for debug info This implements basic DWARF output when building for the wasm target. Stacktraces, however, are currently not supported. --- lib/std/debug.zig | 4 ++++ src/stage1/codegen.cpp | 3 +++ src/stage1/target.cpp | 2 +- src/target.zig | 3 ++- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index e00c0a21a2..6a0d4dac93 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -114,6 +114,10 @@ pub fn detectTTYConfig() TTY.Config { pub fn dumpCurrentStackTrace(start_addr: ?usize) void { nosuspend { const stderr = io.getStdErr().writer(); + if (comptime builtin.target.isWasm()) { + stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return; + return; + } if (builtin.strip_debug_info) { stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; return; diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 40e4440102..3a83d93310 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -2645,6 +2645,9 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, Stage1Air *executabl Stage1AirInstSaveErrRetAddr *save_err_ret_addr_instruction) { assert(g->have_err_ret_tracing); + if ((target_is_wasm(g->zig_target) && g->zig_target->os != OsEmscripten) || target_is_bpf(g->zig_target)) { + return nullptr; + } LLVMValueRef return_err_fn = get_return_err_fn(g); bool is_llvm_alloca; diff --git a/src/stage1/target.cpp b/src/stage1/target.cpp index 7ff3008911..da6565f0be 100644 --- a/src/stage1/target.cpp +++ b/src/stage1/target.cpp @@ -1000,7 +1000,7 @@ ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) { } bool target_has_debug_info(const ZigTarget *target) { - return !target_is_wasm(target); + return true; } bool target_long_double_is_f128(const ZigTarget *target) { diff --git a/src/target.zig b/src/target.zig index aafd65e327..27ed1118db 100644 --- a/src/target.zig +++ b/src/target.zig @@ -455,7 +455,8 @@ pub fn classifyCompilerRtLibName(target: std.Target, name: []const u8) CompilerR } pub fn hasDebugInfo(target: std.Target) bool { - return !target.cpu.arch.isWasm(); + _ = target; + return true; } pub fn defaultCompilerRtOptimizeMode(target: std.Target) std.builtin.Mode { From 922d8378e724063063e709223e90e3a577e4566a Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Tue, 1 Mar 2022 10:41:38 -0700 Subject: [PATCH 1181/2031] stage2: Add limited WASI support for selfExePath and globalCacheDir This change adds support for locating the Zig executable and the library and global cache directories, based on looking in the fixed "/zig" and "/cache" directories. Since our argv[0] on WASI is just the basename (any absolute/relative path information is deleted by the runtime), there's very limited introspection we can do on WASI, so we rely on these fixed directories. These can be provided on the command-line using `--mapdir`, as follows: ``` wasmtime --mapdir=/cwd::. --mapdir=/cache::"$HOME/.cache/zig" --mapdir=/zig::./zig-out/ ./zig-out/bin/zig.wasm ``` --- lib/std/fs.zig | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/introspect.zig | 13 ++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 052343599e..a2873ef830 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -2547,6 +2547,15 @@ pub const SelfExePathError = os.ReadLinkError || os.SysCtlError || os.RealPathEr /// `selfExePath` except allocates the result on the heap. /// Caller owns returned memory. pub fn selfExePathAlloc(allocator: Allocator) ![]u8 { + if (builtin.os.tag == .wasi) { + var args = try std.process.argsWithAllocator(allocator); + defer args.deinit(); + // On WASI, argv[0] is always just the basename of the current executable + const exe_name = args.next() orelse return error.FileNotFound; + + var buf: [MAX_PATH_BYTES]u8 = undefined; + return allocator.dupe(u8, try selfExePathWasi(&buf, exe_name)); + } // Use of MAX_PATH_BYTES here is justified as, at least on one tested Linux // system, readlink will completely fail to return a result larger than // PATH_MAX even if given a sufficiently large buffer. This makes it @@ -2643,10 +2652,45 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; return out_buffer[0..end_index]; }, + .wasi => @compileError("std.fs.selfExePath not supported for WASI. Use std.fs.selfExePathAlloc instead."), else => @compileError("std.fs.selfExePath not supported for this target"), } } +/// WASI-specific implementation of selfExePath +/// +/// On WASI argv0 is always just the executable basename, so this function relies +/// using a fixed executable directory path: "/zig" +/// +/// This path can be configured in wasmtime using `--mapdir=/zig::/path/to/zig/dir/` +fn selfExePathWasi(out_buffer: []u8, exe_name: []const u8) SelfExePathError![]const u8 { + var allocator = std.heap.FixedBufferAllocator.init(out_buffer); + var alloc = allocator.allocator(); + + // Check these paths: + // 1. "/zig/{exe_name}" + // 2. "/zig/bin/{exe_name}" + const base_paths_to_check = &[_][]const u8{ "/zig", "/zig/bin" }; + + for (base_paths_to_check) |base_path| { + const test_path = path.join(alloc, &.{ base_path, path.basename(exe_name) }) catch continue; + + // Make sure it's a file we're pointing to + const file = os.fstatat(os.wasi.AT.FDCWD, test_path, 0) catch continue; + if (file.filetype != .REGULAR_FILE) continue; + + // Path seems to be valid, let's try to turn it into an absolute path + var real_path_buf: [MAX_PATH_BYTES]u8 = undefined; + if (os.realpath(test_path, &real_path_buf)) |real_path| { + if (real_path.len > out_buffer.len) + return error.NameTooLong; + mem.copy(u8, out_buffer, real_path); + return out_buffer[0..real_path.len]; + } else |_| continue; + } + return error.FileNotFound; +} + /// The result is UTF16LE-encoded. pub fn selfExePathW() [:0]const u16 { const image_path_name = &os.windows.peb().ProcessParameters.ImagePathName; diff --git a/src/introspect.zig b/src/introspect.zig index 562d6b04f4..c0de4dc7f5 100644 --- a/src/introspect.zig +++ b/src/introspect.zig @@ -1,6 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); const mem = std.mem; +const os = std.os; const fs = std.fs; const Compilation = @import("Compilation.zig"); @@ -80,5 +81,15 @@ pub fn resolveGlobalCacheDir(allocator: mem.Allocator) ![]u8 { } } - return fs.getAppDataDir(allocator, appname); + if (builtin.os.tag == .wasi) { + // On WASI, we have no way to get an App data dir, so we try to use a fixed + // Preopen path "/cache" as a last resort + const path = "/cache"; + + const file = os.fstatat(os.wasi.AT.FDCWD, path, 0) catch return error.CacheDirUnavailable; + if (file.filetype != .DIRECTORY) return error.CacheDirUnavailable; + return allocator.dupe(u8, path); + } else { + return fs.getAppDataDir(allocator, appname); + } } From 089651716c3d326f8540f4e415fc88443e42f5f2 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Tue, 1 Mar 2022 10:42:07 -0700 Subject: [PATCH 1182/2031] stage2: Bypass file locks in src/Cache.zig for WASI targets --- src/Cache.zig | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Cache.zig b/src/Cache.zig index 993114905e..37cd7a7529 100644 --- a/src/Cache.zig +++ b/src/Cache.zig @@ -762,18 +762,22 @@ pub const Manifest = struct { fn downgradeToSharedLock(self: *Manifest) !void { if (!self.have_exclusive_lock) return; - const manifest_file = self.manifest_file.?; - try manifest_file.downgradeLock(); + if (std.process.can_spawn or !builtin.single_threaded) { // Some targets (WASI) do not support flock + const manifest_file = self.manifest_file.?; + try manifest_file.downgradeLock(); + } self.have_exclusive_lock = false; } fn upgradeToExclusiveLock(self: *Manifest) !void { if (self.have_exclusive_lock) return; - const manifest_file = self.manifest_file.?; - // Here we intentionally have a period where the lock is released, in case there are - // other processes holding a shared lock. - manifest_file.unlock(); - try manifest_file.lock(.Exclusive); + if (std.process.can_spawn or !builtin.single_threaded) { // Some targets (WASI) do not support flock + const manifest_file = self.manifest_file.?; + // Here we intentionally have a period where the lock is released, in case there are + // other processes holding a shared lock. + manifest_file.unlock(); + try manifest_file.lock(.Exclusive); + } self.have_exclusive_lock = true; } From 692ccd01b4ebaa78f6702b15459437228e6a790d Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Tue, 1 Mar 2022 10:42:34 -0700 Subject: [PATCH 1183/2031] stage2: Initialize WASI preopens on startup --- src/main.zig | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main.zig b/src/main.zig index a647216b05..8696661017 100644 --- a/src/main.zig +++ b/src/main.zig @@ -162,6 +162,16 @@ pub fn main() anyerror!void { return mainArgs(gpa_tracy.allocator(), arena, args); } + // WASI: `--dir` instructs the WASM runtime to "preopen" a directory, making + // it available to the us, the guest program. This is the only way for us to + // access files/dirs on the host filesystem + if (builtin.os.tag == .wasi) { + // This sets our CWD to "/preopens/cwd" + // Dot-prefixed preopens like `--dir=.` are "mounted" at "/preopens/cwd" + // Other preopens like `--dir=lib` are "mounted" at "/" + try std.os.initPreopensWasi(std.heap.page_allocator, "/preopens/cwd"); + } + return mainArgs(gpa, arena, args); } From 3a63fa6b7f56a2f384ebd460e80c00e6bbd2efee Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Fri, 4 Mar 2022 10:23:27 -0700 Subject: [PATCH 1184/2031] stage2: Add 'zig.wasm' fallback for binary name --- lib/std/fs.zig | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index a2873ef830..f7a9249f1c 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -2663,7 +2663,7 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { /// using a fixed executable directory path: "/zig" /// /// This path can be configured in wasmtime using `--mapdir=/zig::/path/to/zig/dir/` -fn selfExePathWasi(out_buffer: []u8, exe_name: []const u8) SelfExePathError![]const u8 { +fn selfExePathWasi(out_buffer: []u8, argv0: []const u8) SelfExePathError![]const u8 { var allocator = std.heap.FixedBufferAllocator.init(out_buffer); var alloc = allocator.allocator(); @@ -2671,22 +2671,25 @@ fn selfExePathWasi(out_buffer: []u8, exe_name: []const u8) SelfExePathError![]co // 1. "/zig/{exe_name}" // 2. "/zig/bin/{exe_name}" const base_paths_to_check = &[_][]const u8{ "/zig", "/zig/bin" }; + const exe_names_to_check = &[_][]const u8{ path.basename(argv0), "zig.wasm" }; for (base_paths_to_check) |base_path| { - const test_path = path.join(alloc, &.{ base_path, path.basename(exe_name) }) catch continue; + for (exe_names_to_check) |exe_name| { + const test_path = path.join(alloc, &.{ base_path, exe_name }) catch continue; - // Make sure it's a file we're pointing to - const file = os.fstatat(os.wasi.AT.FDCWD, test_path, 0) catch continue; - if (file.filetype != .REGULAR_FILE) continue; + // Make sure it's a file we're pointing to + const file = os.fstatat(os.wasi.AT.FDCWD, test_path, 0) catch continue; + if (file.filetype != .REGULAR_FILE) continue; - // Path seems to be valid, let's try to turn it into an absolute path - var real_path_buf: [MAX_PATH_BYTES]u8 = undefined; - if (os.realpath(test_path, &real_path_buf)) |real_path| { - if (real_path.len > out_buffer.len) - return error.NameTooLong; - mem.copy(u8, out_buffer, real_path); - return out_buffer[0..real_path.len]; - } else |_| continue; + // Path seems to be valid, let's try to turn it into an absolute path + var real_path_buf: [MAX_PATH_BYTES]u8 = undefined; + if (os.realpath(test_path, &real_path_buf)) |real_path| { + if (real_path.len > out_buffer.len) + return error.NameTooLong; + mem.copy(u8, out_buffer, real_path); + return out_buffer[0..real_path.len]; + } else |_| continue; + } } return error.FileNotFound; } From b75d86027d589be632a831ec55565230818dc4ef Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Apr 2022 05:28:31 -0700 Subject: [PATCH 1185/2031] stage2: avoid binary bloat from GeneralPurposeAllocator In the case of not using it. --- src/main.zig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main.zig b/src/main.zig index 823eb91f33..0d61c3271e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -135,10 +135,9 @@ var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{ pub fn main() anyerror!void { crash_report.initialize(); - var gpa_need_deinit = false; + const use_gpa = build_options.force_gpa or !builtin.link_libc; const gpa = gpa: { - if (build_options.force_gpa or !builtin.link_libc) { - gpa_need_deinit = true; + if (use_gpa) { break :gpa general_purpose_allocator.allocator(); } // We would prefer to use raw libc allocator here, but cannot @@ -148,7 +147,7 @@ pub fn main() anyerror!void { } break :gpa std.heap.raw_c_allocator; }; - defer if (gpa_need_deinit) { + defer if (use_gpa) { _ = general_purpose_allocator.deinit(); }; var arena_instance = std.heap.ArenaAllocator.init(gpa); From c9858f833c2e1d5ef414af7e01d465baa88ef9cc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Apr 2022 06:28:49 -0700 Subject: [PATCH 1186/2031] stage2: fix building stage3 in release mode Previously, comptime function calls could cause a crash in the hash function due to a lazy value depending on an unresolved type. --- src/Module.zig | 2 +- src/Sema.zig | 31 +++++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 53c72ccec2..95ae55feb8 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -205,7 +205,7 @@ pub const MemoizedCall = struct { // The generic function Decl is guaranteed to be the first dependency // of each of its instantiations. - std.hash.autoHash(&hasher, @ptrToInt(key.func)); + std.hash.autoHash(&hasher, key.func); // This logic must be kept in sync with the logic in `analyzeCall` that // computes the hash. diff --git a/src/Sema.zig b/src/Sema.zig index 8789f0589e..a3eef2dc86 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5012,7 +5012,12 @@ fn analyzeCall( // parameter or return type. return error.GenericPoison; }, - else => {}, + else => { + // Needed so that lazy values do not trigger + // assertion due to type not being resolved + // when the hash function is called. + try sema.resolveLazyValue(&child_block, arg_src, arg_val); + }, } should_memoize = should_memoize and !arg_val.canMutateComptimeVarState(); memoized_call_key.args[arg_i] = .{ @@ -5039,7 +5044,12 @@ fn analyzeCall( // parameter or return type. return error.GenericPoison; }, - else => {}, + else => { + // Needed so that lazy values do not trigger + // assertion due to type not being resolved + // when the hash function is called. + try sema.resolveLazyValue(&child_block, arg_src, arg_val); + }, } should_memoize = should_memoize and !arg_val.canMutateComptimeVarState(); memoized_call_key.args[arg_i] = .{ @@ -21601,6 +21611,23 @@ pub fn resolveFnTypes( } } +/// Make it so that calling hash() and eql() on `val` will not assert due +/// to a type not having its layout resolved. +fn resolveLazyValue( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + val: Value, +) CompileError!void { + switch (val.tag()) { + .lazy_align => { + const ty = val.castTag(.lazy_align).?.data; + return sema.resolveTypeLayout(block, src, ty); + }, + else => return, + } +} + pub fn resolveTypeLayout( sema: *Sema, block: *Block, From edb4a07d4ddf01c7f4d589b0f427ba6b9e03134b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Apr 2022 06:54:46 -0700 Subject: [PATCH 1187/2031] Sema: fix not reserving enough memory for comptime shl --- src/value.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/value.zig b/src/value.zig index 713cacb7b0..ff2c0c271b 100644 --- a/src/value.zig +++ b/src/value.zig @@ -3987,7 +3987,7 @@ pub const Value = extern union { const shift = @intCast(usize, rhs.toUnsignedInt(target)); const limbs = try arena.alloc( std.math.big.Limb, - std.math.big.int.calcTwosCompLimbCount(info.bits), + std.math.big.int.calcTwosCompLimbCount(info.bits) + 1, ); var result_bigint = BigIntMutable{ .limbs = limbs, From d760cae2b1f77c239cc4b47a54124ff761aa8a7a Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Mon, 18 Apr 2022 20:22:43 -0700 Subject: [PATCH 1188/2031] compiler_rt: implement __mulxf3 for f80 --- lib/std/special/compiler_rt.zig | 11 +- lib/std/special/compiler_rt/mulXf3.zig | 131 +++++++++++++------- lib/std/special/compiler_rt/mulXf3_test.zig | 67 ++++++++++ 3 files changed, 158 insertions(+), 51 deletions(-) diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index b54a1e980e..375de6fced 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -226,23 +226,26 @@ comptime { @export(__addsf3, .{ .name = "__addsf3", .linkage = linkage }); const __adddf3 = @import("compiler_rt/addXf3.zig").__adddf3; @export(__adddf3, .{ .name = "__adddf3", .linkage = linkage }); - const __addtf3 = @import("compiler_rt/addXf3.zig").__addtf3; - @export(__addtf3, .{ .name = "__addtf3", .linkage = linkage }); const __addxf3 = @import("compiler_rt/addXf3.zig").__addxf3; @export(__addxf3, .{ .name = "__addxf3", .linkage = linkage }); + const __addtf3 = @import("compiler_rt/addXf3.zig").__addtf3; + @export(__addtf3, .{ .name = "__addtf3", .linkage = linkage }); + const __subsf3 = @import("compiler_rt/addXf3.zig").__subsf3; @export(__subsf3, .{ .name = "__subsf3", .linkage = linkage }); const __subdf3 = @import("compiler_rt/addXf3.zig").__subdf3; @export(__subdf3, .{ .name = "__subdf3", .linkage = linkage }); - const __subtf3 = @import("compiler_rt/addXf3.zig").__subtf3; - @export(__subtf3, .{ .name = "__subtf3", .linkage = linkage }); const __subxf3 = @import("compiler_rt/addXf3.zig").__subxf3; @export(__subxf3, .{ .name = "__subxf3", .linkage = linkage }); + const __subtf3 = @import("compiler_rt/addXf3.zig").__subtf3; + @export(__subtf3, .{ .name = "__subtf3", .linkage = linkage }); const __mulsf3 = @import("compiler_rt/mulXf3.zig").__mulsf3; @export(__mulsf3, .{ .name = "__mulsf3", .linkage = linkage }); const __muldf3 = @import("compiler_rt/mulXf3.zig").__muldf3; @export(__muldf3, .{ .name = "__muldf3", .linkage = linkage }); + const __mulxf3 = @import("compiler_rt/mulXf3.zig").__mulxf3; + @export(__mulxf3, .{ .name = "__mulxf3", .linkage = linkage }); const __multf3 = @import("compiler_rt/mulXf3.zig").__multf3; @export(__multf3, .{ .name = "__multf3", .linkage = linkage }); diff --git a/lib/std/special/compiler_rt/mulXf3.zig b/lib/std/special/compiler_rt/mulXf3.zig index 48c31f47b1..befd7b7b43 100644 --- a/lib/std/special/compiler_rt/mulXf3.zig +++ b/lib/std/special/compiler_rt/mulXf3.zig @@ -3,12 +3,16 @@ // https://github.com/llvm/llvm-project/blob/2ffb1b0413efa9a24eb3c49e710e36f92e2cb50b/compiler-rt/lib/builtins/fp_mul_impl.inc const std = @import("std"); +const math = std.math; const builtin = @import("builtin"); const compiler_rt = @import("../compiler_rt.zig"); pub fn __multf3(a: f128, b: f128) callconv(.C) f128 { return mulXf3(f128, a, b); } +pub fn __mulxf3(a: f80, b: f80) callconv(.C) f80 { + return mulXf3(f80, a, b); +} pub fn __muldf3(a: f64, b: f64) callconv(.C) f64 { return mulXf3(f64, a, b); } @@ -29,30 +33,36 @@ pub fn __aeabi_dmul(a: f64, b: f64) callconv(.C) f64 { fn mulXf3(comptime T: type, a: T, b: T) T { @setRuntimeSafety(builtin.is_test); const typeWidth = @typeInfo(T).Float.bits; + const significandBits = math.floatMantissaBits(T); + const fractionalBits = math.floatFractionalBits(T); + const exponentBits = math.floatExponentBits(T); + const Z = std.meta.Int(.unsigned, typeWidth); - const significandBits = std.math.floatMantissaBits(T); - const exponentBits = std.math.floatExponentBits(T); + // ZSignificand is large enough to contain the significand, including an explicit integer bit + const ZSignificand = PowerOfTwoSignificandZ(T); + const ZSignificandBits = @typeInfo(ZSignificand).Int.bits; + const roundBit = (1 << (ZSignificandBits - 1)); const signBit = (@as(Z, 1) << (significandBits + exponentBits)); const maxExponent = ((1 << exponentBits) - 1); const exponentBias = (maxExponent >> 1); - const implicitBit = (@as(Z, 1) << significandBits); - const quietBit = implicitBit >> 1; - const significandMask = implicitBit - 1; + const integerBit = (@as(ZSignificand, 1) << fractionalBits); + const quietBit = integerBit >> 1; + const significandMask = (@as(Z, 1) << significandBits) - 1; const absMask = signBit - 1; - const exponentMask = absMask ^ significandMask; - const qnanRep = exponentMask | quietBit; - const infRep = @bitCast(Z, std.math.inf(T)); + const qnanRep = @bitCast(Z, math.nan(T)) | quietBit; + const infRep = @bitCast(Z, math.inf(T)); + const minNormalRep = @bitCast(Z, math.floatMin(T)); const aExponent = @truncate(u32, (@bitCast(Z, a) >> significandBits) & maxExponent); const bExponent = @truncate(u32, (@bitCast(Z, b) >> significandBits) & maxExponent); const productSign: Z = (@bitCast(Z, a) ^ @bitCast(Z, b)) & signBit; - var aSignificand: Z = @bitCast(Z, a) & significandMask; - var bSignificand: Z = @bitCast(Z, b) & significandMask; + var aSignificand: ZSignificand = @intCast(ZSignificand, @bitCast(Z, a) & significandMask); + var bSignificand: ZSignificand = @intCast(ZSignificand, @bitCast(Z, b) & significandMask); var scale: i32 = 0; // Detect if a or b is zero, denormal, infinity, or NaN. @@ -93,38 +103,40 @@ fn mulXf3(comptime T: type, a: T, b: T) T { // one or both of a or b is denormal, the other (if applicable) is a // normal number. Renormalize one or both of a and b, and set scale to // include the necessary exponent adjustment. - if (aAbs < implicitBit) scale += normalize(T, &aSignificand); - if (bAbs < implicitBit) scale += normalize(T, &bSignificand); + if (aAbs < minNormalRep) scale += normalize(T, &aSignificand); + if (bAbs < minNormalRep) scale += normalize(T, &bSignificand); } // Or in the implicit significand bit. (If we fell through from the // denormal path it was already set by normalize( ), but setting it twice // won't hurt anything.) - aSignificand |= implicitBit; - bSignificand |= implicitBit; + aSignificand |= integerBit; + bSignificand |= integerBit; // Get the significand of a*b. Before multiplying the significands, shift // one of them left to left-align it in the field. Thus, the product will // have (exponentBits + 2) integral digits, all but two of which must be // zero. Normalizing this result is just a conditional left-shift by one // and bumping the exponent accordingly. - var productHi: Z = undefined; - var productLo: Z = undefined; - wideMultiply(Z, aSignificand, bSignificand << exponentBits, &productHi, &productLo); + var productHi: ZSignificand = undefined; + var productLo: ZSignificand = undefined; + const left_align_shift = ZSignificandBits - fractionalBits - 1; + wideMultiply(ZSignificand, aSignificand, bSignificand << left_align_shift, &productHi, &productLo); - var productExponent: i32 = @bitCast(i32, aExponent +% bExponent) -% exponentBias +% scale; + var productExponent: i32 = @intCast(i32, aExponent + bExponent) - exponentBias + scale; // Normalize the significand, adjust exponent if needed. - if ((productHi & implicitBit) != 0) { + if ((productHi & integerBit) != 0) { productExponent +%= 1; } else { - productHi = (productHi << 1) | (productLo >> (typeWidth - 1)); + productHi = (productHi << 1) | (productLo >> (ZSignificandBits - 1)); productLo = productLo << 1; } // If we have overflowed the type, return +/- infinity. if (productExponent >= maxExponent) return @bitCast(T, infRep | productSign); + var result: Z = undefined; if (productExponent <= 0) { // Result is denormal before rounding // @@ -133,35 +145,49 @@ fn mulXf3(comptime T: type, a: T, b: T) T { // handle this case separately, but we make it a special case to // simplify the shift logic. const shift: u32 = @truncate(u32, @as(Z, 1) -% @bitCast(u32, productExponent)); - if (shift >= typeWidth) return @bitCast(T, productSign); + if (shift >= ZSignificandBits) return @bitCast(T, productSign); // Otherwise, shift the significand of the result so that the round // bit is the high bit of productLo. - wideRightShiftWithSticky(Z, &productHi, &productLo, shift); + const sticky = wideShrWithTruncation(ZSignificand, &productHi, &productLo, shift); + productLo |= @boolToInt(sticky); + result = productHi; } else { // Result is normal before rounding; insert the exponent. - productHi &= significandMask; - productHi |= @as(Z, @bitCast(u32, productExponent)) << significandBits; + result = productHi & significandMask; + result |= @intCast(Z, productExponent) << significandBits; } - // Insert the sign of the result: - productHi |= productSign; - // Final rounding. The final result may overflow to infinity, or underflow // to zero, but those are the correct results in those cases. We use the // default IEEE-754 round-to-nearest, ties-to-even rounding mode. - if (productLo > signBit) productHi +%= 1; - if (productLo == signBit) productHi +%= productHi & 1; - return @bitCast(T, productHi); + if (productLo > roundBit) result +%= 1; + if (productLo == roundBit) result +%= result & 1; + + // Restore any explicit integer bit, if it was rounded off + if (significandBits != fractionalBits) { + if ((result >> significandBits) != 0) result |= integerBit; + } + + // Insert the sign of the result: + result |= productSign; + + return @bitCast(T, result); } fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { @setRuntimeSafety(builtin.is_test); switch (Z) { + u16 => { + // 16x16 --> 32 bit multiply + const product = @as(u32, a) * @as(u32, b); + hi.* = @intCast(u16, product >> 16); + lo.* = @truncate(u16, product); + }, u32 => { // 32x32 --> 64 bit multiply const product = @as(u64, a) * @as(u64, b); - hi.* = @truncate(u32, product >> 32); + hi.* = @intCast(u32, product >> 32); lo.* = @truncate(u32, product); }, u64 => { @@ -170,7 +196,7 @@ fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { return @truncate(u32, x); } fn hiWord(x: u64) u64 { - return @truncate(u32, x >> 32); + return @intCast(u32, x >> 32); } }; // 64x64 -> 128 wide multiply for platforms that don't have such an operation; @@ -264,34 +290,45 @@ fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { } } -fn normalize(comptime T: type, significand: *std.meta.Int(.unsigned, @typeInfo(T).Float.bits)) i32 { - @setRuntimeSafety(builtin.is_test); - const Z = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); - const significandBits = std.math.floatMantissaBits(T); - const implicitBit = @as(Z, 1) << significandBits; +/// Returns a power-of-two integer type that is large enough to contain +/// the significand of T, including an explicit integer bit +fn PowerOfTwoSignificandZ(comptime T: type) type { + const bits = math.ceilPowerOfTwoAssert(u16, math.floatFractionalBits(T) + 1); + return std.meta.Int(.unsigned, bits); +} - const shift = @clz(Z, significand.*) - @clz(Z, implicitBit); - significand.* <<= @intCast(std.math.Log2Int(Z), shift); +fn normalize(comptime T: type, significand: *PowerOfTwoSignificandZ(T)) i32 { + @setRuntimeSafety(builtin.is_test); + const Z = PowerOfTwoSignificandZ(T); + const integerBit = @as(Z, 1) << math.floatFractionalBits(T); + + const shift = @clz(Z, significand.*) - @clz(Z, integerBit); + significand.* <<= @intCast(math.Log2Int(Z), shift); return @as(i32, 1) - shift; } -fn wideRightShiftWithSticky(comptime Z: type, hi: *Z, lo: *Z, count: u32) void { +// Returns `true` if the right shift is inexact (i.e. any bit shifted out is non-zero) +// +// This is analogous to an shr version of `@shlWithOverflow` +fn wideShrWithTruncation(comptime Z: type, hi: *Z, lo: *Z, count: u32) bool { @setRuntimeSafety(builtin.is_test); const typeWidth = @typeInfo(Z).Int.bits; - const S = std.math.Log2Int(Z); + const S = math.Log2Int(Z); + var inexact = false; if (count < typeWidth) { - const sticky = @boolToInt((lo.* << @intCast(S, typeWidth -% count)) != 0); - lo.* = (hi.* << @intCast(S, typeWidth -% count)) | (lo.* >> @intCast(S, count)) | sticky; + inexact = (lo.* << @intCast(S, typeWidth -% count)) != 0; + lo.* = (hi.* << @intCast(S, typeWidth -% count)) | (lo.* >> @intCast(S, count)); hi.* = hi.* >> @intCast(S, count); } else if (count < 2 * typeWidth) { - const sticky = @boolToInt((hi.* << @intCast(S, 2 * typeWidth -% count) | lo.*) != 0); - lo.* = hi.* >> @intCast(S, count -% typeWidth) | sticky; + inexact = (hi.* << @intCast(S, 2 * typeWidth -% count) | lo.*) != 0; + lo.* = hi.* >> @intCast(S, count -% typeWidth); hi.* = 0; } else { - const sticky = @boolToInt((hi.* | lo.*) != 0); - lo.* = sticky; + inexact = (hi.* | lo.*) != 0; + lo.* = 0; hi.* = 0; } + return inexact; } test { diff --git a/lib/std/special/compiler_rt/mulXf3_test.zig b/lib/std/special/compiler_rt/mulXf3_test.zig index 396b69bcd0..2dd345e69e 100644 --- a/lib/std/special/compiler_rt/mulXf3_test.zig +++ b/lib/std/special/compiler_rt/mulXf3_test.zig @@ -2,10 +2,15 @@ // // https://github.com/llvm/llvm-project/blob/2ffb1b0413efa9a24eb3c49e710e36f92e2cb50b/compiler-rt/test/builtins/Unit/multf3_test.c +const std = @import("std"); +const math = std.math; const qnan128 = @bitCast(f128, @as(u128, 0x7fff800000000000) << 64); const inf128 = @bitCast(f128, @as(u128, 0x7fff000000000000) << 64); const __multf3 = @import("mulXf3.zig").__multf3; +const __mulxf3 = @import("mulXf3.zig").__mulxf3; +const __muldf3 = @import("mulXf3.zig").__muldf3; +const __mulsf3 = @import("mulXf3.zig").__mulsf3; // return true if equal // use two 64-bit integers intead of one 128-bit integer @@ -97,4 +102,66 @@ test "multf3" { 0x3f90000000000000, 0x0, ); + + try test__multf3(0x1.0000_0000_0000_0000_0000_0000_0001p+0, 0x1.8p+5, 0x4004_8000_0000_0000, 0x0000_0000_0000_0002); + try test__multf3(0x1.0000_0000_0000_0000_0000_0000_0002p+0, 0x1.8p+5, 0x4004_8000_0000_0000, 0x0000_0000_0000_0003); +} + +const qnan80 = @bitCast(f80, @bitCast(u80, math.nan(f80)) | (1 << (math.floatFractionalBits(f80) - 1))); + +fn test__mulxf3(a: f80, b: f80, expected: u80) !void { + const x = __mulxf3(a, b); + const rep = @bitCast(u80, x); + + if (rep == expected) + return; + + if (math.isNan(@bitCast(f80, expected)) and math.isNan(x)) + return; // We don't currently test NaN payload propagation + + return error.TestFailed; +} + +test "mulxf3" { + // NaN * any = NaN + try test__mulxf3(qnan80, 0x1.23456789abcdefp+5, @bitCast(u80, qnan80)); + try test__mulxf3(@bitCast(f80, @as(u80, 0x7fff_8000_8000_3000_0000)), 0x1.23456789abcdefp+5, @bitCast(u80, qnan80)); + + // any * NaN = NaN + try test__mulxf3(0x1.23456789abcdefp+5, qnan80, @bitCast(u80, qnan80)); + try test__mulxf3(0x1.23456789abcdefp+5, @bitCast(f80, @as(u80, 0x7fff_8000_8000_3000_0000)), @bitCast(u80, qnan80)); + + // NaN * inf = NaN + try test__mulxf3(qnan80, math.inf(f80), @bitCast(u80, qnan80)); + + // inf * NaN = NaN + try test__mulxf3(math.inf(f80), qnan80, @bitCast(u80, qnan80)); + + // inf * inf = inf + try test__mulxf3(math.inf(f80), math.inf(f80), @bitCast(u80, math.inf(f80))); + + // inf * -inf = -inf + try test__mulxf3(math.inf(f80), -math.inf(f80), @bitCast(u80, -math.inf(f80))); + + // -inf + inf = -inf + try test__mulxf3(-math.inf(f80), math.inf(f80), @bitCast(u80, -math.inf(f80))); + + // inf * any = inf + try test__mulxf3(math.inf(f80), 0x1.2335653452436234723489432abcdefp+5, @bitCast(u80, math.inf(f80))); + + // any * inf = inf + try test__mulxf3(0x1.2335653452436234723489432abcdefp+5, math.inf(f80), @bitCast(u80, math.inf(f80))); + + // any * any + try test__mulxf3(0x1.0p+0, 0x1.dcba987654321p+5, 0x4004_ee5d_4c3b_2a19_0800); + try test__mulxf3(0x1.0000_0000_0000_0004p+0, 0x1.8p+5, 0x4004_C000_0000_0000_0003); // exact + + try test__mulxf3(0x1.0000_0000_0000_0002p+0, 0x1.0p+5, 0x4004_8000_0000_0000_0001); // exact + try test__mulxf3(0x1.0000_0000_0000_0002p+0, 0x1.7ffep+5, 0x4004_BFFF_0000_0000_0001); // round down + try test__mulxf3(0x1.0000_0000_0000_0002p+0, 0x1.8p+5, 0x4004_C000_0000_0000_0002); // round up to even + try test__mulxf3(0x1.0000_0000_0000_0002p+0, 0x1.8002p+5, 0x4004_C001_0000_0000_0002); // round up + try test__mulxf3(0x1.0000_0000_0000_0002p+0, 0x1.0p+6, 0x4005_8000_0000_0000_0001); // exact + + try test__mulxf3(0x1.0000_0001p+0, 0x1.0000_0001p+0, 0x3FFF_8000_0001_0000_0000); // round down to even + try test__mulxf3(0x1.0000_0001p+0, 0x1.0000_0001_0002p+0, 0x3FFF_8000_0001_0001_0001); // round up } From b2950866b18916fe5e6e458b62ec294244fa2c2f Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Mon, 18 Apr 2022 20:23:02 -0700 Subject: [PATCH 1189/2031] compiler_rt: Fix rounding/NaN handling for f80 add/sub There were a few minor bugs in the rounding behavior and Inf/NaN handling for the f80 __addxf3 and __subtf3 functions. This change updates the original generic implementation to correctly handle f80 floats, including the explicit integer bit. --- lib/std/special/compiler_rt/addXf3.zig | 223 +++----------------- lib/std/special/compiler_rt/addXf3_test.zig | 78 ++++++- 2 files changed, 108 insertions(+), 193 deletions(-) diff --git a/lib/std/special/compiler_rt/addXf3.zig b/lib/std/special/compiler_rt/addXf3.zig index 13758afce7..c53d60600d 100644 --- a/lib/std/special/compiler_rt/addXf3.zig +++ b/lib/std/special/compiler_rt/addXf3.zig @@ -3,6 +3,7 @@ // https://github.com/llvm/llvm-project/blob/02d85149a05cb1f6dc49f0ba7a2ceca53718ae17/compiler-rt/lib/builtins/fp_add_impl.inc const std = @import("std"); +const math = std.math; const builtin = @import("builtin"); const compiler_rt = @import("../compiler_rt.zig"); @@ -14,6 +15,16 @@ pub fn __adddf3(a: f64, b: f64) callconv(.C) f64 { return addXf3(f64, a, b); } +pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { + return addXf3(f80, a, b); +} + +pub fn __subxf3(a: f80, b: f80) callconv(.C) f80 { + var b_rep = std.math.break_f80(b); + b_rep.exp ^= 0x8000; + return __addxf3(a, std.math.make_f80(b_rep)); +} + pub fn __addtf3(a: f128, b: f128) callconv(.C) f128 { return addXf3(f128, a, b); } @@ -58,10 +69,10 @@ fn normalize(comptime T: type, significand: *std.meta.Int(.unsigned, @typeInfo(T const bits = @typeInfo(T).Float.bits; const Z = std.meta.Int(.unsigned, bits); const S = std.meta.Int(.unsigned, bits - @clz(Z, @as(Z, bits) - 1)); - const significandBits = std.math.floatMantissaBits(T); - const implicitBit = @as(Z, 1) << significandBits; + const fractionalBits = math.floatFractionalBits(T); + const integerBit = @as(Z, 1) << fractionalBits; - const shift = @clz(std.meta.Int(.unsigned, bits), significand.*) - @clz(Z, implicitBit); + const shift = @clz(std.meta.Int(.unsigned, bits), significand.*) - @clz(Z, integerBit); significand.* <<= @intCast(S, shift); return 1 - shift; } @@ -73,26 +84,26 @@ fn addXf3(comptime T: type, a: T, b: T) T { const S = std.meta.Int(.unsigned, bits - @clz(Z, @as(Z, bits) - 1)); const typeWidth = bits; - const significandBits = std.math.floatMantissaBits(T); - const exponentBits = std.math.floatExponentBits(T); + const significandBits = math.floatMantissaBits(T); + const fractionalBits = math.floatFractionalBits(T); + const exponentBits = math.floatExponentBits(T); const signBit = (@as(Z, 1) << (significandBits + exponentBits)); const maxExponent = ((1 << exponentBits) - 1); - const implicitBit = (@as(Z, 1) << significandBits); - const quietBit = implicitBit >> 1; - const significandMask = implicitBit - 1; + const integerBit = (@as(Z, 1) << fractionalBits); + const quietBit = integerBit >> 1; + const significandMask = (@as(Z, 1) << significandBits) - 1; const absMask = signBit - 1; - const exponentMask = absMask ^ significandMask; - const qnanRep = exponentMask | quietBit; + const qnanRep = @bitCast(Z, math.nan(T)) | quietBit; var aRep = @bitCast(Z, a); var bRep = @bitCast(Z, b); const aAbs = aRep & absMask; const bAbs = bRep & absMask; - const infRep = @bitCast(Z, std.math.inf(T)); + const infRep = @bitCast(Z, math.inf(T)); // Detect if a or b is zero, infinity, or NaN. if (aAbs -% @as(Z, 1) >= infRep - @as(Z, 1) or @@ -157,12 +168,12 @@ fn addXf3(comptime T: type, a: T, b: T) T { // implicit significand bit. (If we fell through from the denormal path it // was already set by normalize( ), but setting it twice won't hurt // anything.) - aSignificand = (aSignificand | implicitBit) << 3; - bSignificand = (bSignificand | implicitBit) << 3; + aSignificand = (aSignificand | integerBit) << 3; + bSignificand = (bSignificand | integerBit) << 3; // Shift the significand of b by the difference in exponents, with a sticky // bottom bit to get rounding correct. - const @"align" = @intCast(Z, aExponent - bExponent); + const @"align" = @intCast(u32, aExponent - bExponent); if (@"align" != 0) { if (@"align" < typeWidth) { const sticky = if (bSignificand << @intCast(S, typeWidth - @"align") != 0) @as(Z, 1) else 0; @@ -178,8 +189,8 @@ fn addXf3(comptime T: type, a: T, b: T) T { // If partial cancellation occured, we need to left-shift the result // and adjust the exponent: - if (aSignificand < implicitBit << 3) { - const shift = @intCast(i32, @clz(Z, aSignificand)) - @intCast(i32, @clz(std.meta.Int(.unsigned, bits), implicitBit << 3)); + if (aSignificand < integerBit << 3) { + const shift = @intCast(i32, @clz(Z, aSignificand)) - @intCast(i32, @clz(std.meta.Int(.unsigned, bits), integerBit << 3)); aSignificand <<= @intCast(S, shift); aExponent -= shift; } @@ -188,7 +199,7 @@ fn addXf3(comptime T: type, a: T, b: T) T { // If the addition carried up, we need to right-shift the result and // adjust the exponent: - if (aSignificand & (implicitBit << 4) != 0) { + if (aSignificand & (integerBit << 4) != 0) { const sticky = aSignificand & 1; aSignificand = aSignificand >> 1 | sticky; aExponent += 1; @@ -210,7 +221,7 @@ fn addXf3(comptime T: type, a: T, b: T) T { // Low three bits are round, guard, and sticky. const roundGuardSticky = aSignificand & 0x7; - // Shift the significand into place, and mask off the implicit bit. + // Shift the significand into place, and mask off the integer bit, if it's implicit. var result = (aSignificand >> 3) & significandMask; // Insert the exponent and sign. @@ -222,180 +233,14 @@ fn addXf3(comptime T: type, a: T, b: T) T { if (roundGuardSticky > 0x4) result += 1; if (roundGuardSticky == 0x4) result += result & 1; + // Restore any explicit integer bit, if it was rounded off + if (significandBits != fractionalBits) { + if ((result >> significandBits) != 0) result |= integerBit; + } + return @bitCast(T, result); } -fn normalize_f80(exp: *i32, significand: *u80) void { - const shift = @clz(u64, @truncate(u64, significand.*)); - significand.* = (significand.* << shift); - exp.* += -@as(i8, shift); -} - -pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { - var a_rep = std.math.break_f80(a); - var b_rep = std.math.break_f80(b); - var a_exp: i32 = a_rep.exp & 0x7FFF; - var b_exp: i32 = b_rep.exp & 0x7FFF; - - const significand_bits = std.math.floatMantissaBits(f80); - const int_bit = 0x8000000000000000; - const significand_mask = 0x7FFFFFFFFFFFFFFF; - const qnan_bit = 0xC000000000000000; - const max_exp = 0x7FFF; - const sign_bit = 0x8000; - - // Detect if a or b is infinity, or NaN. - if (a_exp == max_exp) { - if (a_rep.fraction ^ int_bit == 0) { - if (b_exp == max_exp and (b_rep.fraction ^ int_bit == 0)) { - // +/-infinity + -/+infinity = qNaN - return std.math.qnan_f80; - } - // +/-infinity + anything = +/- infinity - return a; - } else { - std.debug.assert(a_rep.fraction & significand_mask != 0); - // NaN + anything = qNaN - a_rep.fraction |= qnan_bit; - return std.math.make_f80(a_rep); - } - } - if (b_exp == max_exp) { - if (b_rep.fraction ^ int_bit == 0) { - // anything + +/-infinity = +/-infinity - return b; - } else { - std.debug.assert(b_rep.fraction & significand_mask != 0); - // anything + NaN = qNaN - b_rep.fraction |= qnan_bit; - return std.math.make_f80(b_rep); - } - } - - const a_zero = (a_rep.fraction | @bitCast(u32, a_exp)) == 0; - const b_zero = (b_rep.fraction | @bitCast(u32, b_exp)) == 0; - if (a_zero) { - // zero + anything = anything - if (b_zero) { - // but we need to get the sign right for zero + zero - a_rep.exp &= b_rep.exp; - return std.math.make_f80(a_rep); - } else { - return b; - } - } else if (b_zero) { - // anything + zero = anything - return a; - } - - var a_int: u80 = a_rep.fraction | (@as(u80, a_rep.exp & max_exp) << significand_bits); - var b_int: u80 = b_rep.fraction | (@as(u80, b_rep.exp & max_exp) << significand_bits); - - // Swap a and b if necessary so that a has the larger absolute value. - if (b_int > a_int) { - const temp = a_rep; - a_rep = b_rep; - b_rep = temp; - } - - // Extract the exponent and significand from the (possibly swapped) a and b. - a_exp = a_rep.exp & max_exp; - b_exp = b_rep.exp & max_exp; - a_int = a_rep.fraction; - b_int = b_rep.fraction; - - // Normalize any denormals, and adjust the exponent accordingly. - normalize_f80(&a_exp, &a_int); - normalize_f80(&b_exp, &b_int); - - // The sign of the result is the sign of the larger operand, a. If they - // have opposite signs, we are performing a subtraction; otherwise addition. - const result_sign = a_rep.exp & sign_bit; - const subtraction = (a_rep.exp ^ b_rep.exp) & sign_bit != 0; - - // Shift the significands to give us round, guard and sticky, and or in the - // implicit significand bit. (If we fell through from the denormal path it - // was already set by normalize( ), but setting it twice won't hurt - // anything.) - a_int = a_int << 3; - b_int = b_int << 3; - - // Shift the significand of b by the difference in exponents, with a sticky - // bottom bit to get rounding correct. - const @"align" = @intCast(u80, a_exp - b_exp); - if (@"align" != 0) { - if (@"align" < 80) { - const sticky = if (b_int << @intCast(u7, 80 - @"align") != 0) @as(u80, 1) else 0; - b_int = (b_int >> @truncate(u7, @"align")) | sticky; - } else { - b_int = 1; // sticky; b is known to be non-zero. - } - } - if (subtraction) { - a_int -= b_int; - // If a == -b, return +zero. - if (a_int == 0) return 0.0; - - // If partial cancellation occurred, we need to left-shift the result - // and adjust the exponent: - if (a_int < int_bit << 3) { - const shift = @intCast(i32, @clz(u80, a_int)) - @intCast(i32, @clz(u80, @as(u80, int_bit) << 3)); - a_int <<= @intCast(u7, shift); - a_exp -= shift; - } - } else { // addition - a_int += b_int; - - // If the addition carried up, we need to right-shift the result and - // adjust the exponent: - if (a_int & (int_bit << 4) != 0) { - const sticky = a_int & 1; - a_int = a_int >> 1 | sticky; - a_exp += 1; - } - } - - // If we have overflowed the type, return +/- infinity: - if (a_exp >= max_exp) { - a_rep.exp = max_exp | result_sign; - a_rep.fraction = int_bit; // integer bit is set for +/-inf - return std.math.make_f80(a_rep); - } - - if (a_exp <= 0) { - // Result is denormal before rounding; the exponent is zero and we - // need to shift the significand. - const shift = @intCast(u80, 1 - a_exp); - const sticky = if (a_int << @intCast(u7, 80 - shift) != 0) @as(u1, 1) else 0; - a_int = a_int >> @intCast(u7, shift | sticky); - a_exp = 0; - } - - // Low three bits are round, guard, and sticky. - const round_guard_sticky = @truncate(u3, a_int); - - // Shift the significand into place. - a_int = @truncate(u64, a_int >> 3); - - // // Insert the exponent and sign. - a_int |= (@intCast(u80, a_exp) | result_sign) << significand_bits; - - // Final rounding. The result may overflow to infinity, but that is the - // correct result in that case. - if (round_guard_sticky > 0x4) a_int += 1; - if (round_guard_sticky == 0x4) a_int += a_int & 1; - - a_rep.fraction = @truncate(u64, a_int); - a_rep.exp = @truncate(u16, a_int >> significand_bits); - return std.math.make_f80(a_rep); -} - -pub fn __subxf3(a: f80, b: f80) callconv(.C) f80 { - var b_rep = std.math.break_f80(b); - b_rep.exp ^= 0x8000; - return __addxf3(a, std.math.make_f80(b_rep)); -} - test { _ = @import("addXf3_test.zig"); } diff --git a/lib/std/special/compiler_rt/addXf3_test.zig b/lib/std/special/compiler_rt/addXf3_test.zig index 70eb203cee..a4a18b41e0 100644 --- a/lib/std/special/compiler_rt/addXf3_test.zig +++ b/lib/std/special/compiler_rt/addXf3_test.zig @@ -3,8 +3,9 @@ // https://github.com/llvm/llvm-project/blob/02d85149a05cb1f6dc49f0ba7a2ceca53718ae17/compiler-rt/test/builtins/Unit/addtf3_test.c // https://github.com/llvm/llvm-project/blob/02d85149a05cb1f6dc49f0ba7a2ceca53718ae17/compiler-rt/test/builtins/Unit/subtf3_test.c +const std = @import("std"); +const math = std.math; const qnan128 = @bitCast(f128, @as(u128, 0x7fff800000000000) << 64); -const inf128 = @bitCast(f128, @as(u128, 0x7fff000000000000) << 64); const __addtf3 = @import("addXf3.zig").__addtf3; @@ -37,13 +38,14 @@ test "addtf3" { try test__addtf3(@bitCast(f128, (@as(u128, 0x7fff000000000000) << 64) | @as(u128, 0x800030000000)), 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0); // inf + inf = inf - try test__addtf3(inf128, inf128, 0x7fff000000000000, 0x0); + try test__addtf3(math.inf(f128), math.inf(f128), 0x7fff000000000000, 0x0); // inf + any = inf - try test__addtf3(inf128, 0x1.2335653452436234723489432abcdefp+5, 0x7fff000000000000, 0x0); + try test__addtf3(math.inf(f128), 0x1.2335653452436234723489432abcdefp+5, 0x7fff000000000000, 0x0); // any + any try test__addtf3(0x1.23456734245345543849abcdefp+5, 0x1.edcba52449872455634654321fp-1, 0x40042afc95c8b579, 0x61e58dd6c51eb77c); + try test__addtf3(0x1.edcba52449872455634654321fp-1, 0x1.23456734245345543849abcdefp+5, 0x40042afc95c8b579, 0x61e58dd6c51eb77c); } const __subtf3 = @import("addXf3.zig").__subtf3; @@ -78,8 +80,76 @@ test "subtf3" { try test__subtf3(@bitCast(f128, (@as(u128, 0x7fff000000000000) << 64) | @as(u128, 0x800030000000)), 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0); // inf - any = inf - try test__subtf3(inf128, 0x1.23456789abcdefp+5, 0x7fff000000000000, 0x0); + try test__subtf3(math.inf(f128), 0x1.23456789abcdefp+5, 0x7fff000000000000, 0x0); // any + any try test__subtf3(0x1.234567829a3bcdef5678ade36734p+5, 0x1.ee9d7c52354a6936ab8d7654321fp-1, 0x40041b8af1915166, 0xa44a7bca780a166c); + try test__subtf3(0x1.ee9d7c52354a6936ab8d7654321fp-1, 0x1.234567829a3bcdef5678ade36734p+5, 0xc0041b8af1915166, 0xa44a7bca780a166c); +} + +const __addxf3 = @import("addXf3.zig").__addxf3; +const qnan80 = @bitCast(f80, @bitCast(u80, math.nan(f80)) | (1 << (math.floatFractionalBits(f80) - 1))); + +fn test__addxf3(a: f80, b: f80, expected: u80) !void { + const x = __addxf3(a, b); + const rep = @bitCast(u80, x); + + if (rep == expected) + return; + + if (math.isNan(@bitCast(f80, expected)) and math.isNan(x)) + return; // We don't currently test NaN payload propagation + + return error.TestFailed; +} + +test "addxf3" { + // NaN + any = NaN + try test__addxf3(qnan80, 0x1.23456789abcdefp+5, @bitCast(u80, qnan80)); + try test__addxf3(@bitCast(f80, @as(u80, 0x7fff_8000_8000_3000_0000)), 0x1.23456789abcdefp+5, @bitCast(u80, qnan80)); + + // any + NaN = NaN + try test__addxf3(0x1.23456789abcdefp+5, qnan80, @bitCast(u80, qnan80)); + try test__addxf3(0x1.23456789abcdefp+5, @bitCast(f80, @as(u80, 0x7fff_8000_8000_3000_0000)), @bitCast(u80, qnan80)); + + // NaN + inf = NaN + try test__addxf3(qnan80, math.inf(f80), @bitCast(u80, qnan80)); + + // inf + NaN = NaN + try test__addxf3(math.inf(f80), qnan80, @bitCast(u80, qnan80)); + + // inf + inf = inf + try test__addxf3(math.inf(f80), math.inf(f80), @bitCast(u80, math.inf(f80))); + + // inf + -inf = NaN + try test__addxf3(math.inf(f80), -math.inf(f80), @bitCast(u80, qnan80)); + + // -inf + inf = NaN + try test__addxf3(-math.inf(f80), math.inf(f80), @bitCast(u80, qnan80)); + + // inf + any = inf + try test__addxf3(math.inf(f80), 0x1.2335653452436234723489432abcdefp+5, @bitCast(u80, math.inf(f80))); + + // any + inf = inf + try test__addxf3(0x1.2335653452436234723489432abcdefp+5, math.inf(f80), @bitCast(u80, math.inf(f80))); + + // any + any + try test__addxf3(0x1.23456789abcdp+5, 0x1.dcba987654321p+5, 0x4005_BFFFFFFFFFFFC400); + try test__addxf3(0x1.23456734245345543849abcdefp+5, 0x1.edcba52449872455634654321fp-1, 0x4004_957E_4AE4_5ABC_B0F3); + try test__addxf3(0x1.ffff_ffff_ffff_fffcp+0, 0x1.0p-63, 0x3FFF_FFFFFFFFFFFFFFFF); // exact + try test__addxf3(0x1.ffff_ffff_ffff_fffep+0, 0x0.0p0, 0x3FFF_FFFFFFFFFFFFFFFF); // exact + try test__addxf3(0x1.ffff_ffff_ffff_fffcp+0, 0x1.4p-63, 0x3FFF_FFFFFFFFFFFFFFFF); // round down + try test__addxf3(0x1.ffff_ffff_ffff_fffcp+0, 0x1.8p-63, 0x4000_8000000000000000); // round up to even + try test__addxf3(0x1.ffff_ffff_ffff_fffcp+0, 0x1.cp-63, 0x4000_8000000000000000); // round up + try test__addxf3(0x1.ffff_ffff_ffff_fffcp+0, 0x2.0p-63, 0x4000_8000000000000000); // exact + try test__addxf3(0x1.ffff_ffff_ffff_fffcp+0, 0x2.1p-63, 0x4000_8000000000000000); // round down + try test__addxf3(0x1.ffff_ffff_ffff_fffcp+0, 0x3.0p-63, 0x4000_8000000000000000); // round down to even + try test__addxf3(0x1.ffff_ffff_ffff_fffcp+0, 0x3.1p-63, 0x4000_8000000000000001); // round up + try test__addxf3(0x1.ffff_ffff_ffff_fffcp+0, 0x4.0p-63, 0x4000_8000000000000001); // exact + + try test__addxf3(0x1.0fff_ffff_ffff_fffep+0, 0x1.0p-63, 0x3FFF_8800000000000000); // exact + try test__addxf3(0x1.0fff_ffff_ffff_fffep+0, 0x1.7p-63, 0x3FFF_8800000000000000); // round down + try test__addxf3(0x1.0fff_ffff_ffff_fffep+0, 0x1.8p-63, 0x3FFF_8800000000000000); // round down to even + try test__addxf3(0x1.0fff_ffff_ffff_fffep+0, 0x1.9p-63, 0x3FFF_8800000000000001); // round up + try test__addxf3(0x1.0fff_ffff_ffff_fffep+0, 0x2.0p-63, 0x3FFF_8800000000000001); // exact } From bb9cd6db1cab40d5d5fba7c2ea4fc979efdefa44 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Mon, 18 Apr 2022 23:06:49 -0700 Subject: [PATCH 1190/2031] stage2: Move WASI/Zig-specific selfExePath to introspect.zig --- lib/std/fs.zig | 46 ---------------------------------------------- src/introspect.zig | 39 ++++++++++++++++++++++++++++++++++++++- src/main.zig | 6 +++--- src/print_env.zig | 2 +- 4 files changed, 42 insertions(+), 51 deletions(-) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index f7a9249f1c..8f3a0de808 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -2547,15 +2547,6 @@ pub const SelfExePathError = os.ReadLinkError || os.SysCtlError || os.RealPathEr /// `selfExePath` except allocates the result on the heap. /// Caller owns returned memory. pub fn selfExePathAlloc(allocator: Allocator) ![]u8 { - if (builtin.os.tag == .wasi) { - var args = try std.process.argsWithAllocator(allocator); - defer args.deinit(); - // On WASI, argv[0] is always just the basename of the current executable - const exe_name = args.next() orelse return error.FileNotFound; - - var buf: [MAX_PATH_BYTES]u8 = undefined; - return allocator.dupe(u8, try selfExePathWasi(&buf, exe_name)); - } // Use of MAX_PATH_BYTES here is justified as, at least on one tested Linux // system, readlink will completely fail to return a result larger than // PATH_MAX even if given a sufficiently large buffer. This makes it @@ -2657,43 +2648,6 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { } } -/// WASI-specific implementation of selfExePath -/// -/// On WASI argv0 is always just the executable basename, so this function relies -/// using a fixed executable directory path: "/zig" -/// -/// This path can be configured in wasmtime using `--mapdir=/zig::/path/to/zig/dir/` -fn selfExePathWasi(out_buffer: []u8, argv0: []const u8) SelfExePathError![]const u8 { - var allocator = std.heap.FixedBufferAllocator.init(out_buffer); - var alloc = allocator.allocator(); - - // Check these paths: - // 1. "/zig/{exe_name}" - // 2. "/zig/bin/{exe_name}" - const base_paths_to_check = &[_][]const u8{ "/zig", "/zig/bin" }; - const exe_names_to_check = &[_][]const u8{ path.basename(argv0), "zig.wasm" }; - - for (base_paths_to_check) |base_path| { - for (exe_names_to_check) |exe_name| { - const test_path = path.join(alloc, &.{ base_path, exe_name }) catch continue; - - // Make sure it's a file we're pointing to - const file = os.fstatat(os.wasi.AT.FDCWD, test_path, 0) catch continue; - if (file.filetype != .REGULAR_FILE) continue; - - // Path seems to be valid, let's try to turn it into an absolute path - var real_path_buf: [MAX_PATH_BYTES]u8 = undefined; - if (os.realpath(test_path, &real_path_buf)) |real_path| { - if (real_path.len > out_buffer.len) - return error.NameTooLong; - mem.copy(u8, out_buffer, real_path); - return out_buffer[0..real_path.len]; - } else |_| continue; - } - } - return error.FileNotFound; -} - /// The result is UTF16LE-encoded. pub fn selfExePathW() [:0]const u16 { const image_path_name = &os.windows.peb().ProcessParameters.ImagePathName; diff --git a/src/introspect.zig b/src/introspect.zig index c0de4dc7f5..74f0d45c80 100644 --- a/src/introspect.zig +++ b/src/introspect.zig @@ -33,9 +33,46 @@ fn testZigInstallPrefix(base_dir: fs.Dir) ?Compilation.Directory { return Compilation.Directory{ .handle = test_zig_dir, .path = "lib" }; } +/// This is a small wrapper around selfExePathAlloc that adds support for WASI +/// based on a hard-coded Preopen directory ("/zig") +pub fn findZigExePath(allocator: mem.Allocator) ![]u8 { + if (builtin.os.tag == .wasi) { + var args = try std.process.argsWithAllocator(allocator); + defer args.deinit(); + // On WASI, argv[0] is always just the basename of the current executable + const argv0 = args.next() orelse return error.FileNotFound; + + // Check these paths: + // 1. "/zig/{exe_name}" + // 2. "/zig/bin/{exe_name}" + const base_paths_to_check = &[_][]const u8{ "/zig", "/zig/bin" }; + const exe_names_to_check = &[_][]const u8{ fs.path.basename(argv0), "zig.wasm" }; + + for (base_paths_to_check) |base_path| { + for (exe_names_to_check) |exe_name| { + const test_path = fs.path.join(allocator, &.{ base_path, exe_name }) catch continue; + defer allocator.free(test_path); + + // Make sure it's a file we're pointing to + const file = os.fstatat(os.wasi.AT.FDCWD, test_path, 0) catch continue; + if (file.filetype != .REGULAR_FILE) continue; + + // Path seems to be valid, let's try to turn it into an absolute path + var real_path_buf: [fs.MAX_PATH_BYTES]u8 = undefined; + if (os.realpath(test_path, &real_path_buf)) |real_path| { + return allocator.dupe(u8, real_path); // Success: return absolute path + } else |_| continue; + } + } + return error.FileNotFound; + } + + return fs.selfExePathAlloc(allocator); +} + /// Both the directory handle and the path are newly allocated resources which the caller now owns. pub fn findZigLibDir(gpa: mem.Allocator) !Compilation.Directory { - const self_exe_path = try fs.selfExePathAlloc(gpa); + const self_exe_path = try findZigExePath(gpa); defer gpa.free(self_exe_path); return findZigLibDirFromSelfExe(gpa, self_exe_path); diff --git a/src/main.zig b/src/main.zig index 0d61c3271e..51765edb51 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2526,7 +2526,7 @@ fn buildOutputType( pkg_tree_root.table = .{}; } - const self_exe_path = try fs.selfExePathAlloc(arena); + const self_exe_path = try introspect.findZigExePath(arena); var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{ .path = lib_dir, .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| { @@ -3395,7 +3395,7 @@ pub fn cmdInit( } } } - const self_exe_path = try fs.selfExePathAlloc(arena); + const self_exe_path = try introspect.findZigExePath(arena); var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)}); }; @@ -3475,7 +3475,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi // We want to release all the locks before executing the child process, so we make a nice // big block here to ensure the cleanup gets run when we extract out our argv. const child_argv = argv: { - const self_exe_path = try fs.selfExePathAlloc(arena); + const self_exe_path = try introspect.findZigExePath(arena); var build_file: ?[]const u8 = null; var override_lib_dir: ?[]const u8 = null; diff --git a/src/print_env.zig b/src/print_env.zig index 15f038c50e..ad772d416b 100644 --- a/src/print_env.zig +++ b/src/print_env.zig @@ -6,7 +6,7 @@ const fatal = @import("main.zig").fatal; pub fn cmdEnv(gpa: Allocator, args: []const []const u8, stdout: std.fs.File.Writer) !void { _ = args; - const self_exe_path = try std.fs.selfExePathAlloc(gpa); + const self_exe_path = try introspect.findZigExePath(gpa); defer gpa.free(self_exe_path); var zig_lib_directory = introspect.findZigLibDirFromSelfExe(gpa, self_exe_path) catch |err| { From f8dc6fc416d27bc6d93d79689823a4278aacd5b2 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Mon, 18 Apr 2022 23:08:00 -0700 Subject: [PATCH 1191/2031] stage2: Only bypass `flock` on WASI --- src/Cache.zig | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Cache.zig b/src/Cache.zig index 37cd7a7529..0d4b51492d 100644 --- a/src/Cache.zig +++ b/src/Cache.zig @@ -762,7 +762,11 @@ pub const Manifest = struct { fn downgradeToSharedLock(self: *Manifest) !void { if (!self.have_exclusive_lock) return; - if (std.process.can_spawn or !builtin.single_threaded) { // Some targets (WASI) do not support flock + + // WASI does not currently support flock, so we bypass it here. + // TODO: If/when flock is supported on WASI, this check should be removed. + // See https://github.com/WebAssembly/wasi-filesystem/issues/2 + if (builtin.os.tag != .wasi or std.process.can_spawn or !builtin.single_threaded) { const manifest_file = self.manifest_file.?; try manifest_file.downgradeLock(); } @@ -771,7 +775,11 @@ pub const Manifest = struct { fn upgradeToExclusiveLock(self: *Manifest) !void { if (self.have_exclusive_lock) return; - if (std.process.can_spawn or !builtin.single_threaded) { // Some targets (WASI) do not support flock + + // WASI does not currently support flock, so we bypass it here. + // TODO: If/when flock is supported on WASI, this check should be removed. + // See https://github.com/WebAssembly/wasi-filesystem/issues/2 + if (builtin.os.tag != .wasi or std.process.can_spawn or !builtin.single_threaded) { const manifest_file = self.manifest_file.?; // Here we intentionally have a period where the lock is released, in case there are // other processes holding a shared lock. From 535d5624e48e31752729d00c469aca504cf50091 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Apr 2022 12:12:21 -0700 Subject: [PATCH 1192/2031] wasm: fix lowerDeclRefValue using wrong Decl --- src/arch/wasm/CodeGen.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index b701299e73..ab0deb9f63 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1929,7 +1929,7 @@ fn lowerParentPtrDecl(self: *Self, ptr_val: Value, decl: *Module.Decl) InnerErro fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!WValue { if (tv.ty.isSlice()) { - return WValue{ .memory = try self.bin_file.lowerUnnamedConst(self.decl, tv) }; + return WValue{ .memory = try self.bin_file.lowerUnnamedConst(decl, tv) }; } else if (decl.ty.zigTypeTag() != .Fn and !decl.ty.hasRuntimeBitsIgnoreComptime()) { return WValue{ .imm32 = 0xaaaaaaaa }; } From be08d2bdbd2f64ccb9ef0f985a57a4bf89b9aebb Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 17 Apr 2022 19:41:05 +0200 Subject: [PATCH 1193/2031] wasm: Fix unreachable paths When the last instruction is a debug instruction, the type of it is void. Similarly for 'noreturn' emit an 'unreachable' instruction to tell the wasm-validator the path cannot be reached. Also respect the '--strip' flag in the self-hosted wasm linker and not emit a 'name' section when the flag is set to `true`. --- lib/std/debug.zig | 36 ++++++++++++++++++++++++++++++++++-- src/arch/wasm/CodeGen.zig | 3 ++- src/link/Wasm.zig | 2 +- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 6a0d4dac93..b600f7245a 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -113,11 +113,14 @@ pub fn detectTTYConfig() TTY.Config { /// TODO multithreaded awareness pub fn dumpCurrentStackTrace(start_addr: ?usize) void { nosuspend { - const stderr = io.getStdErr().writer(); if (comptime builtin.target.isWasm()) { - stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return; + if (native_os == .wasi) { + const stderr = io.getStdErr().writer(); + stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return; + } return; } + const stderr = io.getStdErr().writer(); if (builtin.strip_debug_info) { stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; return; @@ -138,6 +141,13 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { /// TODO multithreaded awareness pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void { nosuspend { + if (comptime builtin.target.isWasm()) { + if (native_os == .wasi) { + const stderr = io.getStdErr().writer(); + stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return; + } + return; + } const stderr = io.getStdErr().writer(); if (builtin.strip_debug_info) { stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; @@ -208,6 +218,13 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT /// TODO multithreaded awareness pub fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void { nosuspend { + if (comptime builtin.target.isWasm()) { + if (native_os == .wasi) { + const stderr = io.getStdErr().writer(); + stderr.print("Unable to dump stack trace: not implemented for Wasm\n", .{}) catch return; + } + return; + } const stderr = io.getStdErr().writer(); if (builtin.strip_debug_info) { stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return; @@ -1138,6 +1155,8 @@ pub const DebugInfo = struct { return self.lookupModuleWin32(address); } else if (native_os == .haiku) { return self.lookupModuleHaiku(address); + } else if (comptime builtin.target.isWasm()) { + return self.lookupModuleWasm(address); } else { return self.lookupModuleDl(address); } @@ -1353,6 +1372,12 @@ pub const DebugInfo = struct { _ = address; @panic("TODO implement lookup module for Haiku"); } + + fn lookupModuleWasm(self: *DebugInfo, address: usize) !*ModuleDebugInfo { + _ = self; + _ = address; + @panic("TODO implement lookup module for Wasm"); + } }; pub const ModuleDebugInfo = switch (native_os) { @@ -1632,6 +1657,13 @@ pub const ModuleDebugInfo = switch (native_os) { return getSymbolFromDwarf(relocated_address, &self.dwarf); } }, + .wasi => struct { + pub fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo { + _ = self; + _ = address; + return SymbolInfo{}; + } + }, else => DW.DwarfInfo, }; diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index b701299e73..6cec3f4a87 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -871,7 +871,8 @@ fn genFunc(self: *Self) InnerError!void { // we emit an unreachable instruction to tell the stack validator that part will never be reached. if (func_type.returns.len != 0 and self.air.instructions.len > 0) { const inst = @intCast(u32, self.air.instructions.len - 1); - if (self.air.typeOfIndex(inst).isNoReturn()) { + const last_inst_ty = self.air.typeOfIndex(inst); + if (!last_inst_ty.hasRuntimeBitsIgnoreComptime() or last_inst_ty.isNoReturn()) { try self.addTag(.@"unreachable"); } } diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 1ed733774f..4f72dfe388 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -1897,7 +1897,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod if (data_section_index) |data_index| { try self.emitDataRelocations(file, arena, data_index, symbol_table); } - } else { + } else if (!self.base.options.strip) { try self.emitNameSection(file, arena); } } From c78daeb642e742af3ac42bac0468776ccc4cd452 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Fri, 15 Apr 2022 22:58:54 +0200 Subject: [PATCH 1194/2031] stage2 AArch64: add basic assertions to bits.zig for correct codegen Includes many fixes of errors discovered by adding these assertions --- src/arch/aarch64/CodeGen.zig | 76 ++++++++++++++++++++++-------------- src/arch/aarch64/Emit.zig | 44 ++++++++++++++++----- src/arch/aarch64/bits.zig | 75 ++++++++++++++++++++--------------- 3 files changed, 126 insertions(+), 69 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 95d2a8a607..0a5c5065e8 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -376,7 +376,7 @@ fn gen(self: *Self) !void { // mov fp, sp _ = try self.addInst(.{ .tag = .mov_to_from_sp, - .data = .{ .rr = .{ .rd = .x29, .rn = .xzr } }, + .data = .{ .rr = .{ .rd = .x29, .rn = .sp } }, }); // sub sp, sp, #reloc @@ -421,7 +421,7 @@ fn gen(self: *Self) !void { if (math.cast(u12, stack_size)) |size| { self.mir_instructions.set(backpatch_reloc, .{ .tag = .sub_immediate, - .data = .{ .rr_imm12_sh = .{ .rd = .xzr, .rn = .xzr, .imm12 = size } }, + .data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = size } }, }); } else |_| { return self.failSymbol("TODO AArch64: allow larger stacks", .{}); @@ -453,7 +453,7 @@ fn gen(self: *Self) !void { // add sp, sp, #stack_size _ = try self.addInst(.{ .tag = .add_immediate, - .data = .{ .rr_imm12_sh = .{ .rd = .xzr, .rn = .xzr, .imm12 = @intCast(u12, stack_size) } }, + .data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = @intCast(u12, stack_size) } }, }); // @@ -882,7 +882,8 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { - const reg = try self.register_manager.allocReg(null); + const raw_reg = try self.register_manager.allocReg(null); + const reg = registerAlias(raw_reg, ty.abiSize(self.target.*)); try self.genSetReg(ty, reg, mcv); return reg; } @@ -891,7 +892,9 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { /// `reg_owner` is the instruction that gets associated with the register in the register table. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { - const reg = try self.register_manager.allocReg(reg_owner); + const raw_reg = try self.register_manager.allocReg(reg_owner); + const ty = self.air.typeOfIndex(reg_owner); + const reg = registerAlias(raw_reg, ty.abiSize(self.target.*)); try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv); return MCValue{ .register = reg }; } @@ -1003,7 +1006,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { break :blk op_reg; } - break :blk try self.register_manager.allocReg(null); + const raw_reg = try self.register_manager.allocReg(null); + break :blk raw_reg.to32(); }; _ = try self.addInst(.{ @@ -1013,7 +1017,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { .rn = op_reg, .imms = 0b000000, .immr = 0b000000, - .n = 0b1, + .n = 0b0, } }, }); @@ -1035,7 +1039,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { break :blk op_reg; } - break :blk try self.register_manager.allocReg(null); + const raw_reg = try self.register_manager.allocReg(null); + break :blk registerAlias(raw_reg, operand_ty.abiSize(self.target.*)); }; _ = try self.addInst(.{ @@ -1124,7 +1129,8 @@ fn binOpRegister( break :inst Air.refToIndex(bin_op.lhs).?; } else null; - const reg = try self.register_manager.allocReg(track_inst); + const raw_reg = try self.register_manager.allocReg(track_inst); + const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); self.register_manager.freezeRegs(&.{reg}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -1139,7 +1145,8 @@ fn binOpRegister( break :inst Air.refToIndex(bin_op.rhs).?; } else null; - const reg = try self.register_manager.allocReg(track_inst); + const raw_reg = try self.register_manager.allocReg(track_inst); + const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*)); self.register_manager.freezeRegs(&.{reg}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -1156,7 +1163,8 @@ fn binOpRegister( } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { break :blk rhs_reg; } else { - break :blk try self.register_manager.allocReg(inst); + const raw_reg = try self.register_manager.allocReg(inst); + break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); } } else try self.register_manager.allocReg(null); @@ -1276,7 +1284,8 @@ fn binOpImmediate( ).?; } else null; - const reg = try self.register_manager.allocReg(track_inst); + const raw_reg = try self.register_manager.allocReg(track_inst); + const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); self.register_manager.freezeRegs(&.{reg}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -1298,7 +1307,8 @@ fn binOpImmediate( )) { break :blk lhs_reg; } else { - break :blk try self.register_manager.allocReg(inst); + const raw_reg = try self.register_manager.allocReg(inst); + break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); } } else try self.register_manager.allocReg(null), }; @@ -1965,7 +1975,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo }, .stack_offset => |off| { if (elem_size <= 8) { - const tmp_reg = try self.register_manager.allocReg(null); + const raw_tmp_reg = try self.register_manager.allocReg(null); + const tmp_reg = registerAlias(raw_tmp_reg, elem_size); self.register_manager.freezeRegs(&.{tmp_reg}); defer self.register_manager.unfreezeRegs(&.{tmp_reg}); @@ -2001,12 +2012,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .got_load, .direct_load, => { - const reg = try self.register_manager.allocReg(null); - self.register_manager.freezeRegs(&.{reg}); - defer self.register_manager.unfreezeRegs(&.{reg}); - - try self.genSetReg(ptr_ty, reg, ptr); - try self.load(dst_mcv, .{ .register = reg }, ptr_ty); + const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr); + try self.load(dst_mcv, .{ .register = addr_reg }, ptr_ty); }, } } @@ -2091,6 +2098,7 @@ fn genInlineMemcpy( fn airLoad(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const elem_ty = self.air.typeOfIndex(inst); + const elem_size = elem_ty.abiSize(self.target.*); const result: MCValue = result: { if (!elem_ty.hasRuntimeBits()) break :result MCValue.none; @@ -2101,9 +2109,12 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { break :result MCValue.dead; const dst_mcv: MCValue = blk: { - if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) { + if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) { // The MCValue that holds the pointer can be re-used as the value. - break :blk ptr; + break :blk switch (ptr) { + .register => |r| MCValue{ .register = registerAlias(r, elem_size) }, + else => ptr, + }; } else { break :blk try self.allocRegOrMem(inst, true); } @@ -2209,6 +2220,8 @@ fn genStrRegister(self: *Self, value_reg: Register, addr_reg: Register, abi_size } fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { + const abi_size = value_ty.abiSize(self.target.*); + switch (ptr) { .none => unreachable, .undef => unreachable, @@ -2226,14 +2239,14 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type self.register_manager.freezeRegs(&.{addr_reg}); defer self.register_manager.unfreezeRegs(&.{addr_reg}); - const abi_size = value_ty.abiSize(self.target.*); switch (value) { .register => |value_reg| { try self.genStrRegister(value_reg, addr_reg, abi_size); }, else => { if (abi_size <= 8) { - const tmp_reg = try self.register_manager.allocReg(null); + const raw_tmp_reg = try self.register_manager.allocReg(null); + const tmp_reg = registerAlias(raw_tmp_reg, abi_size); self.register_manager.freezeRegs(&.{tmp_reg}); defer self.register_manager.unfreezeRegs(&.{tmp_reg}); @@ -3522,8 +3535,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .memory => |addr| { // The value is in memory at a hard-coded address. // If the type is a pointer, it means the pointer address is at this memory location. - try self.genSetReg(ty, reg, .{ .immediate = addr }); - try self.genLdrRegister(reg, reg, ty.abiSize(self.target.*)); + try self.genSetReg(ty, reg.to64(), .{ .immediate = addr }); + try self.genLdrRegister(reg, reg.to64(), ty.abiSize(self.target.*)); }, .stack_offset => |off| { const abi_size = ty.abiSize(self.target.*); @@ -3998,6 +4011,12 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { var nsaa: u32 = 0; // Next stacked argument address for (param_types) |ty, i| { + const param_size = @intCast(u32, ty.abiSize(self.target.*)); + if (param_size == 0) { + result.args[i] = .{ .none = {} }; + continue; + } + // We round up NCRN only for non-Apple platforms which allow the 16-byte aligned // values to spread across odd-numbered registers. if (ty.abiAlignment(self.target.*) == 16 and !self.target.isDarwin()) { @@ -4005,10 +4024,9 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { ncrn += ncrn % 2; } - const param_size = @intCast(u32, ty.abiSize(self.target.*)); if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) { if (param_size <= 8) { - result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] }; + result.args[i] = .{ .register = registerAlias(c_abi_int_param_regs[ncrn], param_size) }; ncrn += 1; } else { return self.fail("TODO MCValues with multiple registers", .{}); @@ -4045,7 +4063,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { .Unspecified, .C => { const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); if (ret_ty_size <= 8) { - result.return_value = .{ .register = c_abi_int_return_regs[0] }; + result.return_value = .{ .register = registerAlias(c_abi_int_return_regs[0], ret_ty_size) }; } else { return self.fail("TODO support more return types for ARM backend", .{}); } diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index b62f9d8691..077330f0ca 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -457,8 +457,13 @@ fn mirAddSubtractImmediate(emit: *Emit, inst: Mir.Inst.Index) !void { const rn = r_imm12_sh.rn; const imm12 = r_imm12_sh.imm12; const sh = r_imm12_sh.sh == 1; + const zr: Register = switch (rn.size()) { + 32 => .wzr, + 64 => .xzr, + else => unreachable, + }; - try emit.writeInstruction(Instruction.subs(.xzr, rn, imm12, sh)); + try emit.writeInstruction(Instruction.subs(zr, rn, imm12, sh)); }, else => unreachable, } @@ -674,8 +679,13 @@ fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void { const rm = rr_imm6_shift.rm; const shift = rr_imm6_shift.shift; const imm6 = rr_imm6_shift.imm6; + const zr: Register = switch (rn.size()) { + 32 => .wzr, + 64 => .xzr, + else => unreachable, + }; - try emit.writeInstruction(Instruction.subsShiftedRegister(.xzr, rn, rm, shift, imm6)); + try emit.writeInstruction(Instruction.subsShiftedRegister(zr, rn, rm, shift, imm6)); }, else => unreachable, } @@ -686,7 +696,12 @@ fn mirConditionalSelect(emit: *Emit, inst: Mir.Inst.Index) !void { switch (tag) { .cset => { const r_cond = emit.mir.instructions.items(.data)[inst].r_cond; - try emit.writeInstruction(Instruction.csinc(r_cond.rd, .xzr, .xzr, r_cond.cond)); + const zr: Register = switch (r_cond.rd.size()) { + 32 => .wzr, + 64 => .xzr, + else => unreachable, + }; + try emit.writeInstruction(Instruction.csinc(r_cond.rd, zr, zr, r_cond.cond)); }, else => unreachable, } @@ -718,14 +733,14 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { // PC-relative displacement to the entry in memory. // adrp const offset = @intCast(u32, emit.code.items.len); - try emit.writeInstruction(Instruction.adrp(reg, 0)); + try emit.writeInstruction(Instruction.adrp(reg.to64(), 0)); switch (tag) { .load_memory_got => { // ldr reg, reg, offset try emit.writeInstruction(Instruction.ldr( reg, - reg, + reg.to64(), Instruction.LoadStoreOffset.imm(0), )); }, @@ -739,11 +754,11 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { // Note that this can potentially be optimised out by the codegen/linker if the // target address is appropriately aligned. // add reg, reg, offset - try emit.writeInstruction(Instruction.add(reg, reg, 0, false)); + try emit.writeInstruction(Instruction.add(reg.to64(), reg.to64(), 0, false)); // ldr reg, reg, offset try emit.writeInstruction(Instruction.ldr( reg, - reg, + reg.to64(), Instruction.LoadStoreOffset.imm(0), )); }, @@ -905,7 +920,13 @@ fn mirMoveRegister(emit: *Emit, inst: Mir.Inst.Index) !void { switch (tag) { .mov_register => { const rr = emit.mir.instructions.items(.data)[inst].rr; - try emit.writeInstruction(Instruction.orrShiftedRegister(rr.rd, .xzr, rr.rn, .lsl, 0)); + const zr: Register = switch (rr.rd.size()) { + 32 => .wzr, + 64 => .xzr, + else => unreachable, + }; + + try emit.writeInstruction(Instruction.orrShiftedRegister(rr.rd, zr, rr.rn, .lsl, 0)); }, .mov_to_from_sp => { const rr = emit.mir.instructions.items(.data)[inst].rr; @@ -917,8 +938,13 @@ fn mirMoveRegister(emit: *Emit, inst: Mir.Inst.Index) !void { const rm = rr_imm6_logical_shift.rm; const shift = rr_imm6_logical_shift.shift; const imm6 = rr_imm6_logical_shift.imm6; + const zr: Register = switch (rd.size()) { + 32 => .wzr, + 64 => .xzr, + else => unreachable, + }; - try emit.writeInstruction(Instruction.ornShiftedRegister(rd, .xzr, rm, shift, imm6)); + try emit.writeInstruction(Instruction.ornShiftedRegister(rd, zr, rm, shift, imm6)); }, else => unreachable, } diff --git a/src/arch/aarch64/bits.zig b/src/arch/aarch64/bits.zig index e28a8485ca..49bc09d8f2 100644 --- a/src/arch/aarch64/bits.zig +++ b/src/arch/aarch64/bits.zig @@ -695,6 +695,9 @@ pub const Instruction = union(enum) { offset: LoadStoreOffset, variant: LoadStoreVariant, ) Instruction { + assert(rn.size() == 64); + assert(rn.id() != Register.xzr.id()); + const off = offset.toU12(); const op1: u2 = blk: { switch (offset) { @@ -741,6 +744,9 @@ pub const Instruction = union(enum) { encoding: u2, load: bool, ) Instruction { + assert(rn.size() == 64); + assert(rn.id() != Register.xzr.id()); + switch (rt1.size()) { 32 => { assert(-256 <= offset and offset <= 252); @@ -849,38 +855,26 @@ pub const Instruction = union(enum) { shift: LogicalShiftedRegisterShift, amount: u6, ) Instruction { - switch (rd.size()) { - 32 => { - assert(amount < 32); - return Instruction{ - .logical_shifted_register = .{ - .rd = rd.enc(), - .rn = rn.enc(), - .imm6 = amount, - .rm = rm.enc(), - .n = n, - .shift = @enumToInt(shift), - .opc = opc, - .sf = 0b0, - }, - }; + assert(rd.size() == rn.size()); + assert(rd.size() == rm.size()); + if (rd.size() == 32) assert(amount < 32); + + return Instruction{ + .logical_shifted_register = .{ + .rd = rd.enc(), + .rn = rn.enc(), + .imm6 = amount, + .rm = rm.enc(), + .n = n, + .shift = @enumToInt(shift), + .opc = opc, + .sf = switch (rd.size()) { + 32 => 0b0, + 64 => 0b1, + else => unreachable, + }, }, - 64 => { - return Instruction{ - .logical_shifted_register = .{ - .rd = rd.enc(), - .rn = rn.enc(), - .imm6 = amount, - .rm = rm.enc(), - .n = n, - .shift = @enumToInt(shift), - .opc = opc, - .sf = 0b1, - }, - }; - }, - else => unreachable, // unexpected register size - } + }; } fn addSubtractImmediate( @@ -891,6 +885,9 @@ pub const Instruction = union(enum) { imm12: u12, shift: bool, ) Instruction { + assert(rd.size() == rn.size()); + assert(rn.id() != Register.xzr.id()); + return Instruction{ .add_subtract_immediate = .{ .rd = rd.enc(), @@ -916,6 +913,9 @@ pub const Instruction = union(enum) { immr: u6, n: u1, ) Instruction { + assert(rd.size() == rn.size()); + assert(!(rd.size() == 32 and n == 1)); + return Instruction{ .logical_immediate = .{ .rd = rd.enc(), @@ -941,6 +941,8 @@ pub const Instruction = union(enum) { immr: u6, imms: u6, ) Instruction { + assert(rd.size() == rn.size()); + return Instruction{ .bitfield = .{ .rd = rd.enc(), @@ -969,6 +971,9 @@ pub const Instruction = union(enum) { rm: Register, imm6: u6, ) Instruction { + assert(rd.size() == rn.size()); + assert(rd.size() == rm.size()); + return Instruction{ .add_subtract_shifted_register = .{ .rd = rd.enc(), @@ -994,6 +999,7 @@ pub const Instruction = union(enum) { offset: i21, ) Instruction { assert(offset & 0b11 == 0b00); + return Instruction{ .conditional_branch = .{ .cond = @enumToInt(cond), @@ -1010,6 +1016,7 @@ pub const Instruction = union(enum) { offset: i21, ) Instruction { assert(offset & 0b11 == 0b00); + return Instruction{ .compare_and_branch = .{ .rt = rt.enc(), @@ -1033,6 +1040,9 @@ pub const Instruction = union(enum) { rm: Register, cond: Condition, ) Instruction { + assert(rd.size() == rn.size()); + assert(rd.size() == rm.size()); + return Instruction{ .conditional_select = .{ .rd = rd.enc(), @@ -1085,6 +1095,9 @@ pub const Instruction = union(enum) { rn: Register, rm: Register, ) Instruction { + assert(rd.size() == rn.size()); + assert(rd.size() == rm.size()); + return Instruction{ .data_processing_2_source = .{ .rd = rd.enc(), From f95a8ddafa53d4cee591c82bd5ed3eb0eac4a51b Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 16 Apr 2022 18:58:48 +0200 Subject: [PATCH 1195/2031] stage2 AArch64: Implement basic truncate functionality --- src/arch/aarch64/CodeGen.zig | 122 ++++++++++++++++++++++++++++++----- src/arch/aarch64/Emit.zig | 40 ++++++++++++ src/arch/aarch64/Mir.zig | 38 +++++++++-- src/arch/aarch64/bits.zig | 75 +++++++++++++-------- test/behavior/basic.zig | 2 - 5 files changed, 224 insertions(+), 53 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 0a5c5065e8..3a73a54c54 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -939,14 +939,99 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch}); } +fn truncRegister( + self: *Self, + operand_reg: Register, + dest_reg: Register, + int_signedness: std.builtin.Signedness, + int_bits: u16, +) !void { + switch (int_bits) { + 1...31, 33...63 => { + _ = try self.addInst(.{ + .tag = switch (int_signedness) { + .signed => .sbfx, + .unsigned => .ubfx, + }, + .data = .{ .rr_lsb_width = .{ + .rd = dest_reg, + .rn = operand_reg, + .lsb = 0, + .width = @intCast(u6, int_bits), + } }, + }); + }, + 32, 64 => { + _ = try self.addInst(.{ + .tag = .mov_register, + .data = .{ .rr = .{ + .rd = dest_reg, + .rn = operand_reg, + } }, + }); + }, + else => unreachable, + } +} + +fn trunc( + self: *Self, + maybe_inst: ?Air.Inst.Index, + operand: MCValue, + operand_ty: Type, + dest_ty: Type, +) !MCValue { + const info_a = operand_ty.intInfo(self.target.*); + const info_b = dest_ty.intInfo(self.target.*); + + if (info_b.bits <= 64) { + const operand_reg = switch (operand) { + .register => |r| r, + else => operand_reg: { + if (info_a.bits <= 64) { + const raw_reg = try self.copyToTmpRegister(operand_ty, operand); + break :operand_reg registerAlias(raw_reg, operand_ty.abiSize(self.target.*)); + } else { + return self.fail("TODO load least significant word into register", .{}); + } + }, + }; + self.register_manager.freezeRegs(&.{operand_reg}); + defer self.register_manager.unfreezeRegs(&.{operand_reg}); + + const dest_reg = if (maybe_inst) |inst| blk: { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + + if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { + break :blk registerAlias(operand_reg, dest_ty.abiSize(self.target.*)); + } else { + const raw_reg = try self.register_manager.allocReg(inst); + break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); + } + } else blk: { + const raw_reg = try self.register_manager.allocReg(null); + break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); + }; + + try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits); + + return MCValue{ .register = dest_reg }; + } else { + return self.fail("TODO: truncate to ints > 32 bits", .{}); + } +} + fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - if (self.liveness.isUnused(inst)) - return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); - const operand = try self.resolveInst(ty_op.operand); - _ = operand; - return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch}); + const operand_ty = self.air.typeOf(ty_op.operand); + const dest_ty = self.air.typeOfIndex(inst); + + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { + break :blk try self.trunc(inst, operand, operand_ty, dest_ty); + }; + + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void { @@ -3483,23 +3568,26 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x) } }, }); - if (x > math.maxInt(u16)) { + if (x & 0x0000_0000_ffff_0000 != 0) { _ = try self.addInst(.{ .tag = .movk, .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 16), .hw = 1 } }, }); } - if (x > math.maxInt(u32)) { - _ = try self.addInst(.{ - .tag = .movk, - .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } }, - }); - } - if (x > math.maxInt(u48)) { - _ = try self.addInst(.{ - .tag = .movk, - .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } }, - }); + + if (reg.size() == 64) { + if (x & 0x0000_ffff_0000_0000 != 0) { + _ = try self.addInst(.{ + .tag = .movk, + .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } }, + }); + } + if (x & 0xffff_0000_0000_0000 != 0) { + _ = try self.addInst(.{ + .tag = .movk, + .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } }, + }); + } } }, .register => |src_reg| { diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 077330f0ca..ac731636bd 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -162,6 +162,17 @@ pub fn emitMir( .push_regs => try emit.mirPushPopRegs(inst), .pop_regs => try emit.mirPushPopRegs(inst), + + .sbfx, + .ubfx, + => try emit.mirBitfieldExtract(inst), + + .sxtb, + .sxth, + .sxtw, + .uxtb, + .uxth, + => try emit.mirExtend(inst), } } } @@ -1050,3 +1061,32 @@ fn mirPushPopRegs(emit: *Emit, inst: Mir.Inst.Index) !void { else => unreachable, } } + +fn mirBitfieldExtract(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const rr_lsb_width = emit.mir.instructions.items(.data)[inst].rr_lsb_width; + const rd = rr_lsb_width.rd; + const rn = rr_lsb_width.rn; + const lsb = rr_lsb_width.lsb; + const width = rr_lsb_width.width; + + switch (tag) { + .sbfx => try emit.writeInstruction(Instruction.sbfx(rd, rn, lsb, width)), + .ubfx => try emit.writeInstruction(Instruction.ubfx(rd, rn, lsb, width)), + else => unreachable, + } +} + +fn mirExtend(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const rr = emit.mir.instructions.items(.data)[inst].rr; + + switch (tag) { + .sxtb => try emit.writeInstruction(Instruction.sxtb(rr.rd, rr.rn)), + .sxth => try emit.writeInstruction(Instruction.sxth(rr.rd, rr.rn)), + .sxtw => try emit.writeInstruction(Instruction.sxtw(rr.rd, rr.rn)), + .uxtb => try emit.writeInstruction(Instruction.uxtb(rr.rd, rr.rn)), + .uxth => try emit.writeInstruction(Instruction.uxth(rr.rd, rr.rn)), + else => unreachable, + } +} diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index 6515a8da2e..dcde591a07 100644 --- a/src/arch/aarch64/Mir.zig +++ b/src/arch/aarch64/Mir.zig @@ -130,6 +130,14 @@ pub const Inst = struct { push_regs, /// Return from subroutine ret, + /// Signed bitfield extract + sbfx, + /// Signed extend byte + sxtb, + /// Signed extend halfword + sxth, + /// Signed extend word + sxtw, /// Store Pair of Registers stp, /// Pseudo-instruction: Store to stack @@ -156,6 +164,12 @@ pub const Inst = struct { sub_shifted_register, /// Supervisor Call svc, + /// Unsigned bitfield extract + ubfx, + /// Unsigned extend byte + uxtb, + /// Unsigned extend halfword + uxth, }; /// The position of an MIR instruction within the `Mir` instructions array. @@ -225,13 +239,6 @@ pub const Inst = struct { rt: Register, inst: Index, }, - /// Two registers - /// - /// Used by e.g. mov_register - rr: struct { - rd: Register, - rn: Register, - }, /// A register, an unsigned 12-bit immediate, and an optional shift /// /// Used by e.g. cmp_immediate @@ -240,6 +247,13 @@ pub const Inst = struct { imm12: u12, sh: u1 = 0, }, + /// Two registers + /// + /// Used by e.g. mov_register + rr: struct { + rd: Register, + rn: Register, + }, /// Two registers, an unsigned 12-bit immediate, and an optional shift /// /// Used by e.g. sub_immediate @@ -268,6 +282,16 @@ pub const Inst = struct { imm6: u6, shift: bits.Instruction.LogicalShiftedRegisterShift, }, + /// Two registers and a lsb (range 0-63) and a width (range + /// 1-64) + /// + /// Used by e.g. ubfx + rr_lsb_width: struct { + rd: Register, + rn: Register, + lsb: u6, + width: u7, + }, /// Two registers and a bitmask immediate /// /// Used by e.g. eor_immediate diff --git a/src/arch/aarch64/bits.zig b/src/arch/aarch64/bits.zig index 49bc09d8f2..f275776ac6 100644 --- a/src/arch/aarch64/bits.zig +++ b/src/arch/aarch64/bits.zig @@ -510,33 +510,23 @@ pub const Instruction = union(enum) { imm16: u16, shift: u6, ) Instruction { - switch (rd.size()) { - 32 => { - assert(shift % 16 == 0 and shift <= 16); - return Instruction{ - .move_wide_immediate = .{ - .rd = rd.enc(), - .imm16 = imm16, - .hw = @intCast(u2, shift / 16), - .opc = opc, - .sf = 0, - }, - }; + assert(shift % 16 == 0); + assert(!(rd.size() == 32 and shift > 16)); + assert(!(rd.size() == 64 and shift > 48)); + + return Instruction{ + .move_wide_immediate = .{ + .rd = rd.enc(), + .imm16 = imm16, + .hw = @intCast(u2, shift / 16), + .opc = opc, + .sf = switch (rd.size()) { + 32 => 0, + 64 => 1, + else => unreachable, // unexpected register size + }, }, - 64 => { - assert(shift % 16 == 0 and shift <= 48); - return Instruction{ - .move_wide_immediate = .{ - .rd = rd.enc(), - .imm16 = imm16, - .hw = @intCast(u2, shift / 16), - .opc = opc, - .sf = 1, - }, - }; - }, - else => unreachable, // unexpected register size - } + }; } fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction { @@ -914,7 +904,7 @@ pub const Instruction = union(enum) { n: u1, ) Instruction { assert(rd.size() == rn.size()); - assert(!(rd.size() == 32 and n == 1)); + assert(!(rd.size() == 32 and n != 0)); return Instruction{ .logical_immediate = .{ @@ -942,6 +932,8 @@ pub const Instruction = union(enum) { imms: u6, ) Instruction { assert(rd.size() == rn.size()); + assert(!(rd.size() == 64 and n != 1)); + assert(!(rd.size() == 32 and (n != 0 or immr >> 5 != 0 or immr >> 5 != 0))); return Instruction{ .bitfield = .{ @@ -1417,6 +1409,23 @@ pub const Instruction = union(enum) { return sbfm(rd, rn, shift, imms); } + pub fn sbfx(rd: Register, rn: Register, lsb: u6, width: u7) Instruction { + return sbfm(rd, rn, lsb, @intCast(u6, lsb + width - 1)); + } + + pub fn sxtb(rd: Register, rn: Register) Instruction { + return sbfm(rd, rn, 0, 7); + } + + pub fn sxth(rd: Register, rn: Register) Instruction { + return sbfm(rd, rn, 0, 15); + } + + pub fn sxtw(rd: Register, rn: Register) Instruction { + assert(rd.size() == 64); + return sbfm(rd, rn, 0, 31); + } + pub fn lslImmediate(rd: Register, rn: Register, shift: u6) Instruction { const size = @intCast(u6, rd.size() - 1); return ubfm(rd, rn, size - shift + 1, size - shift); @@ -1427,6 +1436,18 @@ pub const Instruction = union(enum) { return ubfm(rd, rn, shift, imms); } + pub fn ubfx(rd: Register, rn: Register, lsb: u6, width: u7) Instruction { + return ubfm(rd, rn, lsb, @intCast(u6, lsb + width - 1)); + } + + pub fn uxtb(rd: Register, rn: Register) Instruction { + return ubfm(rd, rn, 0, 7); + } + + pub fn uxth(rd: Register, rn: Register) Instruction { + return ubfm(rd, rn, 0, 15); + } + // Add/subtract (shifted register) pub fn addShiftedRegister( diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 96aa6900ee..39e6322e20 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -15,8 +15,6 @@ test "empty function with comments" { } test "truncate" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - try expect(testTruncate(0x10fd) == 0xfd); comptime try expect(testTruncate(0x10fd) == 0xfd); } From d9d9fea6aee7fd0f4dadb53e41f9884330229051 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 16 Apr 2022 23:03:11 +0200 Subject: [PATCH 1196/2031] stage2 AArch64: Add ldrsb, ldrsh instructions --- src/arch/aarch64/CodeGen.zig | 138 +++++++++++------------------------ src/arch/aarch64/Emit.zig | 14 +++- src/arch/aarch64/Mir.zig | 10 +++ src/arch/aarch64/bits.zig | 78 ++++++++++++++------ test/behavior/basic.zig | 2 - test/behavior/truncate.zig | 2 - 6 files changed, 119 insertions(+), 125 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 3a73a54c54..31be0cbe6a 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -2056,7 +2056,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .undef => unreachable, .compare_flags_signed, .compare_flags_unsigned => unreachable, .register => |dst_reg| { - try self.genLdrRegister(dst_reg, addr_reg, elem_size); + try self.genLdrRegister(dst_reg, addr_reg, elem_ty); }, .stack_offset => |off| { if (elem_size <= 8) { @@ -2210,98 +2210,47 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } -fn genLdrRegister(self: *Self, value_reg: Register, addr_reg: Register, abi_size: u64) !void { - switch (abi_size) { - 1 => { - _ = try self.addInst(.{ - .tag = .ldrb_immediate, - .data = .{ .load_store_register_immediate = .{ - .rt = value_reg.to32(), - .rn = addr_reg, - .offset = Instruction.LoadStoreOffset.none.immediate, - } }, - }); - }, - 2 => { - _ = try self.addInst(.{ - .tag = .ldrh_immediate, - .data = .{ .load_store_register_immediate = .{ - .rt = value_reg.to32(), - .rn = addr_reg, - .offset = Instruction.LoadStoreOffset.none.immediate, - } }, - }); - }, - 4 => { - _ = try self.addInst(.{ - .tag = .ldr_immediate, - .data = .{ .load_store_register_immediate = .{ - .rt = value_reg.to32(), - .rn = addr_reg, - .offset = Instruction.LoadStoreOffset.none.immediate, - } }, - }); - }, - 8 => { - _ = try self.addInst(.{ - .tag = .ldr_immediate, - .data = .{ .load_store_register_immediate = .{ - .rt = value_reg.to64(), - .rn = addr_reg, - .offset = Instruction.LoadStoreOffset.none.immediate, - } }, - }); - }, +fn genLdrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type) !void { + const abi_size = ty.abiSize(self.target.*); + + const tag: Mir.Inst.Tag = switch (abi_size) { + 1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb_immediate else .ldrb_immediate, + 2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh_immediate else .ldrh_immediate, + 4 => .ldr_immediate, + 8 => .ldr_immediate, 3, 5, 6, 7 => return self.fail("TODO: genLdrRegister for more abi_sizes", .{}), else => unreachable, - } + }; + + _ = try self.addInst(.{ + .tag = tag, + .data = .{ .load_store_register_immediate = .{ + .rt = value_reg, + .rn = addr_reg, + .offset = Instruction.LoadStoreOffset.none.immediate, + } }, + }); } -fn genStrRegister(self: *Self, value_reg: Register, addr_reg: Register, abi_size: u64) !void { - switch (abi_size) { - 1 => { - _ = try self.addInst(.{ - .tag = .strb_immediate, - .data = .{ .load_store_register_immediate = .{ - .rt = value_reg.to32(), - .rn = addr_reg, - .offset = Instruction.LoadStoreOffset.none.immediate, - } }, - }); - }, - 2 => { - _ = try self.addInst(.{ - .tag = .strh_immediate, - .data = .{ .load_store_register_immediate = .{ - .rt = value_reg.to32(), - .rn = addr_reg, - .offset = Instruction.LoadStoreOffset.none.immediate, - } }, - }); - }, - 4 => { - _ = try self.addInst(.{ - .tag = .str_immediate, - .data = .{ .load_store_register_immediate = .{ - .rt = value_reg.to32(), - .rn = addr_reg, - .offset = Instruction.LoadStoreOffset.none.immediate, - } }, - }); - }, - 8 => { - _ = try self.addInst(.{ - .tag = .str_immediate, - .data = .{ .load_store_register_immediate = .{ - .rt = value_reg.to64(), - .rn = addr_reg, - .offset = Instruction.LoadStoreOffset.none.immediate, - } }, - }); - }, +fn genStrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type) !void { + const abi_size = ty.abiSize(self.target.*); + + const tag: Mir.Inst.Tag = switch (abi_size) { + 1 => .strb_immediate, + 2 => .strh_immediate, + 4, 8 => .str_immediate, 3, 5, 6, 7 => return self.fail("TODO: genStrRegister for more abi_sizes", .{}), else => unreachable, - } + }; + + _ = try self.addInst(.{ + .tag = tag, + .data = .{ .load_store_register_immediate = .{ + .rt = value_reg, + .rn = addr_reg, + .offset = Instruction.LoadStoreOffset.none.immediate, + } }, + }); } fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { @@ -2326,7 +2275,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type switch (value) { .register => |value_reg| { - try self.genStrRegister(value_reg, addr_reg, abi_size); + try self.genStrRegister(value_reg, addr_reg, value_ty); }, else => { if (abi_size <= 8) { @@ -3624,7 +3573,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void // The value is in memory at a hard-coded address. // If the type is a pointer, it means the pointer address is at this memory location. try self.genSetReg(ty, reg.to64(), .{ .immediate = addr }); - try self.genLdrRegister(reg, reg.to64(), ty.abiSize(self.target.*)); + try self.genLdrRegister(reg, reg.to64(), ty); }, .stack_offset => |off| { const abi_size = ty.abiSize(self.target.*); @@ -3632,21 +3581,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void switch (abi_size) { 1, 2, 4, 8 => { const tag: Mir.Inst.Tag = switch (abi_size) { - 1 => .ldrb_stack, - 2 => .ldrh_stack, + 1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb_stack else .ldrb_stack, + 2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh_stack else .ldrh_stack, 4, 8 => .ldr_stack, else => unreachable, // unexpected abi size }; - const rt: Register = switch (abi_size) { - 1, 2, 4 => reg.to32(), - 8 => reg.to64(), - else => unreachable, // unexpected abi size - }; _ = try self.addInst(.{ .tag = tag, .data = .{ .load_store_stack = .{ - .rt = rt, + .rt = reg, .offset = @intCast(u32, off), } }, }); diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index ac731636bd..d9dfbc6fac 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -131,6 +131,8 @@ pub fn emitMir( .ldr_stack => try emit.mirLoadStoreStack(inst), .ldrb_stack => try emit.mirLoadStoreStack(inst), .ldrh_stack => try emit.mirLoadStoreStack(inst), + .ldrsb_stack => try emit.mirLoadStoreStack(inst), + .ldrsh_stack => try emit.mirLoadStoreStack(inst), .str_stack => try emit.mirLoadStoreStack(inst), .strb_stack => try emit.mirLoadStoreStack(inst), .strh_stack => try emit.mirLoadStoreStack(inst), @@ -145,6 +147,9 @@ pub fn emitMir( .ldr_immediate => try emit.mirLoadStoreRegisterImmediate(inst), .ldrb_immediate => try emit.mirLoadStoreRegisterImmediate(inst), .ldrh_immediate => try emit.mirLoadStoreRegisterImmediate(inst), + .ldrsb_immediate => try emit.mirLoadStoreRegisterImmediate(inst), + .ldrsh_immediate => try emit.mirLoadStoreRegisterImmediate(inst), + .ldrsw_immediate => try emit.mirLoadStoreRegisterImmediate(inst), .str_immediate => try emit.mirLoadStoreRegisterImmediate(inst), .strb_immediate => try emit.mirLoadStoreRegisterImmediate(inst), .strh_immediate => try emit.mirLoadStoreRegisterImmediate(inst), @@ -847,14 +852,14 @@ fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void { const raw_offset = emit.stack_size - load_store_stack.offset; const offset = switch (tag) { - .ldrb_stack, .strb_stack => blk: { + .ldrb_stack, .ldrsb_stack, .strb_stack => blk: { if (math.cast(u12, raw_offset)) |imm| { break :blk Instruction.LoadStoreOffset.imm(imm); } else |_| { return emit.fail("TODO load/store stack byte with larger offset", .{}); } }, - .ldrh_stack, .strh_stack => blk: { + .ldrh_stack, .ldrsh_stack, .strh_stack => blk: { assert(std.mem.isAlignedGeneric(u32, raw_offset, 2)); // misaligned stack entry if (math.cast(u12, @divExact(raw_offset, 2))) |imm| { break :blk Instruction.LoadStoreOffset.imm(imm); @@ -883,6 +888,8 @@ fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void { .ldr_stack => try emit.writeInstruction(Instruction.ldr(rt, .sp, offset)), .ldrb_stack => try emit.writeInstruction(Instruction.ldrb(rt, .sp, offset)), .ldrh_stack => try emit.writeInstruction(Instruction.ldrh(rt, .sp, offset)), + .ldrsb_stack => try emit.writeInstruction(Instruction.ldrsb(rt, .sp, offset)), + .ldrsh_stack => try emit.writeInstruction(Instruction.ldrsh(rt, .sp, offset)), .str_stack => try emit.writeInstruction(Instruction.str(rt, .sp, offset)), .strb_stack => try emit.writeInstruction(Instruction.strb(rt, .sp, offset)), .strh_stack => try emit.writeInstruction(Instruction.strh(rt, .sp, offset)), @@ -901,6 +908,9 @@ fn mirLoadStoreRegisterImmediate(emit: *Emit, inst: Mir.Inst.Index) !void { .ldr_immediate => try emit.writeInstruction(Instruction.ldr(rt, rn, offset)), .ldrb_immediate => try emit.writeInstruction(Instruction.ldrb(rt, rn, offset)), .ldrh_immediate => try emit.writeInstruction(Instruction.ldrh(rt, rn, offset)), + .ldrsb_immediate => try emit.writeInstruction(Instruction.ldrsb(rt, rn, offset)), + .ldrsh_immediate => try emit.writeInstruction(Instruction.ldrsh(rt, rn, offset)), + .ldrsw_immediate => try emit.writeInstruction(Instruction.ldrsw(rt, rn, offset)), .str_immediate => try emit.writeInstruction(Instruction.str(rt, rn, offset)), .strb_immediate => try emit.writeInstruction(Instruction.strb(rt, rn, offset)), .strh_immediate => try emit.writeInstruction(Instruction.strh(rt, rn, offset)), diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index dcde591a07..516ccca984 100644 --- a/src/arch/aarch64/Mir.zig +++ b/src/arch/aarch64/Mir.zig @@ -100,6 +100,16 @@ pub const Inst = struct { ldrh_immediate, /// Load Register Halfword (register) ldrh_register, + /// Load Register Signed Byte (immediate) + ldrsb_immediate, + /// Pseudo-instruction: Load signed byte from stack + ldrsb_stack, + /// Load Register Signed Halfword (immediate) + ldrsh_immediate, + /// Pseudo-instruction: Load signed halfword from stack + ldrsh_stack, + /// Load Register Signed Word (immediate) + ldrsw_immediate, /// Logical Shift Left (immediate) lsl_immediate, /// Logical Shift Left (register) diff --git a/src/arch/aarch64/bits.zig b/src/arch/aarch64/bits.zig index f275776ac6..0775ca1f7b 100644 --- a/src/arch/aarch64/bits.zig +++ b/src/arch/aarch64/bits.zig @@ -665,18 +665,24 @@ pub const Instruction = union(enum) { /// Which kind of load/store to perform const LoadStoreVariant = enum { - /// 32-bit or 64-bit + /// 32 bits or 64 bits str, - /// 16-bit, zero-extended - strh, - /// 8-bit, zero-extended + /// 8 bits, zero-extended strb, - /// 32-bit or 64-bit + /// 16 bits, zero-extended + strh, + /// 32 bits or 64 bits ldr, - /// 16-bit, zero-extended - ldrh, - /// 8-bit, zero-extended + /// 8 bits, zero-extended ldrb, + /// 16 bits, zero-extended + ldrh, + /// 8 bits, sign extended + ldrsb, + /// 16 bits, sign extended + ldrsh, + /// 32 bits, sign extended + ldrsw, }; fn loadStoreRegister( @@ -689,6 +695,7 @@ pub const Instruction = union(enum) { assert(rn.id() != Register.xzr.id()); const off = offset.toU12(); + const op1: u2 = blk: { switch (offset) { .immediate => |imm| switch (imm) { @@ -699,10 +706,35 @@ pub const Instruction = union(enum) { } break :blk 0b00; }; - const opc: u2 = switch (variant) { - .ldr, .ldrh, .ldrb => 0b01, - .str, .strh, .strb => 0b00, + + const opc: u2 = blk: { + switch (variant) { + .ldr, .ldrh, .ldrb => break :blk 0b01, + .str, .strh, .strb => break :blk 0b00, + .ldrsb, + .ldrsh, + => switch (rt.size()) { + 32 => break :blk 0b11, + 64 => break :blk 0b10, + else => unreachable, // unexpected register size + }, + .ldrsw => break :blk 0b10, + } }; + + const size: u2 = blk: { + switch (variant) { + .ldr, .str => switch (rt.size()) { + 32 => break :blk 0b10, + 64 => break :blk 0b11, + else => unreachable, // unexpected register size + }, + .ldrsw => break :blk 0b10, + .ldrh, .ldrsh, .strh => break :blk 0b01, + .ldrb, .ldrsb, .strb => break :blk 0b00, + } + }; + return Instruction{ .load_store_register = .{ .rt = rt.enc(), @@ -711,17 +743,7 @@ pub const Instruction = union(enum) { .opc = opc, .op1 = op1, .v = 0, - .size = blk: { - switch (variant) { - .ldr, .str => switch (rt.size()) { - 32 => break :blk 0b10, - 64 => break :blk 0b11, - else => unreachable, // unexpected register size - }, - .ldrh, .strh => break :blk 0b01, - .ldrb, .strb => break :blk 0b00, - } - }, + .size = size, }, }; } @@ -1150,6 +1172,18 @@ pub const Instruction = union(enum) { return loadStoreRegister(rt, rn, offset, .ldrb); } + pub fn ldrsb(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction { + return loadStoreRegister(rt, rn, offset, .ldrsb); + } + + pub fn ldrsh(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction { + return loadStoreRegister(rt, rn, offset, .ldrsh); + } + + pub fn ldrsw(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction { + return loadStoreRegister(rt, rn, offset, .ldrsw); + } + pub fn str(rt: Register, rn: Register, offset: LoadStoreOffset) Instruction { return loadStoreRegister(rt, rn, offset, .str); } diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 39e6322e20..d608cba98b 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -23,8 +23,6 @@ fn testTruncate(x: u32) u8 { } test "truncate to non-power-of-two integers" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - try testTrunc(u32, u1, 0b10101, 0b1); try testTrunc(u32, u1, 0b10110, 0b0); try testTrunc(u32, u2, 0b10101, 0b01); diff --git a/test/behavior/truncate.zig b/test/behavior/truncate.zig index 9971fd8804..1543f9fef2 100644 --- a/test/behavior/truncate.zig +++ b/test/behavior/truncate.zig @@ -49,8 +49,6 @@ test "truncate.i0.var" { } test "truncate on comptime integer" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - var x = @truncate(u16, 9999); try expect(x == 9999); var y = @truncate(u16, -21555); From 0a909a6712fac386c635043d0a25539f69b3998f Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sun, 17 Apr 2022 18:57:30 +0200 Subject: [PATCH 1197/2031] stage2 AArch64: implement addwrap, subwrap, mulwrap --- src/arch/aarch64/CodeGen.zig | 57 +++++++++++++++++++++++------------- test/behavior/math.zig | 8 ----- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 31be0cbe6a..049d8af572 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -512,13 +512,13 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off .add, .ptr_add => try self.airBinOp(inst), - .addwrap => try self.airAddWrap(inst), + .addwrap => try self.airBinOp(inst), .add_sat => try self.airAddSat(inst), .sub, .ptr_sub => try self.airBinOp(inst), - .subwrap => try self.airSubWrap(inst), + .subwrap => try self.airBinOp(inst), .sub_sat => try self.airSubSat(inst), .mul => try self.airBinOp(inst), - .mulwrap => try self.airMulWrap(inst), + .mulwrap => try self.airBinOp(inst), .mul_sat => try self.airMulSat(inst), .rem => try self.airRem(inst), .mod => try self.airMod(inst), @@ -1531,6 +1531,39 @@ fn binOp( else => unreachable, } }, + .addwrap, + .subwrap, + .mulwrap, + => { + const base_tag: Air.Inst.Tag = switch (tag) { + .addwrap => .add, + .subwrap => .sub, + .mulwrap => .mul, + else => unreachable, + }; + + // Generate an add/sub/mul + const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + + // Truncate if necessary + switch (lhs_ty.zigTypeTag()) { + .Vector => return self.fail("TODO binary operations on vectors", .{}), + .Int => { + const int_info = lhs_ty.intInfo(self.target.*); + if (int_info.bits <= 64) { + const result_reg = result.register; + + if (int_info.bits < 64) { + try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); + return result; + } else return result; + } else { + return self.fail("TODO binary operations on integers > u64/i64", .{}); + } + }, + else => unreachable, + } + }, // Bitwise operations on integers .bit_and, .bit_or, @@ -1633,36 +1666,18 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airAddWrap(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 addwrap for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airAddSat(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 add_sat for {}", .{self.target.cpu.arch}); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airSubWrap(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 subwrap for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airSubSat(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 sub_sat for {}", .{self.target.cpu.arch}); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airMulWrap(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 mulwrap for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airMulSat(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 mul_sat for {}", .{self.target.cpu.arch}); diff --git a/test/behavior/math.zig b/test/behavior/math.zig index a41f638396..ce316aeea2 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -499,8 +499,6 @@ fn mod(comptime T: type, a: T, b: T) T { } test "unsigned wrapping" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - try testUnsignedWrappingEval(maxInt(u32)); comptime try testUnsignedWrappingEval(maxInt(u32)); } @@ -512,8 +510,6 @@ fn testUnsignedWrappingEval(x: u32) !void { } test "signed wrapping" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - try testSignedWrappingEval(maxInt(i32)); comptime try testSignedWrappingEval(maxInt(i32)); } @@ -525,8 +521,6 @@ fn testSignedWrappingEval(x: i32) !void { } test "signed negation wrapping" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - try testSignedNegationWrappingEval(minInt(i16)); comptime try testSignedNegationWrappingEval(minInt(i16)); } @@ -537,8 +531,6 @@ fn testSignedNegationWrappingEval(x: i16) !void { } test "unsigned negation wrapping" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - try testUnsignedNegationWrappingEval(1); comptime try testUnsignedNegationWrappingEval(1); } From cbb13c023eb24d08b7f46119fad5e2aa1c7a63bb Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sun, 17 Apr 2022 20:47:01 +0200 Subject: [PATCH 1198/2031] stage2 AArch64: change binOp lowering mechanism to use Mir tags Mirrors e2e69803dc16efe11a6d42c6c49853e16a41fd0c for AArch64 --- src/arch/aarch64/CodeGen.zig | 157 +++++++++++++++++------------------ 1 file changed, 75 insertions(+), 82 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 049d8af572..b328c70f06 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -1193,7 +1193,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { /// Asserts that generating an instruction of that form is possible. fn binOpRegister( self: *Self, - tag: Air.Inst.Tag, + mir_tag: Mir.Inst.Tag, maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, @@ -1256,38 +1256,9 @@ fn binOpRegister( if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); - const mir_tag: Mir.Inst.Tag = switch (tag) { - .add, - .ptr_add, - => .add_shifted_register, - .sub, - .ptr_sub, - => .sub_shifted_register, - .cmp_eq => .cmp_shifted_register, - .mul => .mul, - .bit_and, - .bool_and, - => .and_shifted_register, - .bit_or, - .bool_or, - => .orr_shifted_register, - .shl, - .shl_exact, - => .lsl_register, - .shr, - .shr_exact, - => switch (lhs_ty.intInfo(self.target.*).signedness) { - .signed => Mir.Inst.Tag.asr_register, - .unsigned => Mir.Inst.Tag.lsr_register, - }, - .xor => .eor_shifted_register, - else => unreachable, - }; - const mir_data: Mir.Inst.Data = switch (tag) { - .add, - .sub, - .ptr_add, - .ptr_sub, + const mir_data: Mir.Inst.Data = switch (mir_tag) { + .add_shifted_register, + .sub_shifted_register, => .{ .rrr_imm6_shift = .{ .rd = dest_reg, .rn = lhs_reg, @@ -1295,27 +1266,24 @@ fn binOpRegister( .imm6 = 0, .shift = .lsl, } }, - .cmp_eq => .{ .rr_imm6_shift = .{ + .cmp_shifted_register => .{ .rr_imm6_shift = .{ .rn = lhs_reg, .rm = rhs_reg, .imm6 = 0, .shift = .lsl, } }, .mul, - .shl, - .shl_exact, - .shr, - .shr_exact, + .lsl_register, + .asr_register, + .lsr_register, => .{ .rrr = .{ .rd = dest_reg, .rn = lhs_reg, .rm = rhs_reg, } }, - .bit_and, - .bool_and, - .bit_or, - .bool_or, - .xor, + .and_shifted_register, + .orr_shifted_register, + .eor_shifted_register, => .{ .rrr_imm6_logical_shift = .{ .rd = dest_reg, .rn = lhs_reg, @@ -1348,7 +1316,7 @@ fn binOpRegister( /// Asserts that generating an instruction of that form is possible. fn binOpImmediate( self: *Self, - tag: Air.Inst.Tag, + mir_tag: Mir.Inst.Tag, maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, @@ -1379,8 +1347,8 @@ fn binOpImmediate( }; defer self.register_manager.unfreezeRegs(&.{lhs_reg}); - const dest_reg = switch (tag) { - .cmp_eq => undefined, // cmp has no destination register + const dest_reg = switch (mir_tag) { + .cmp_immediate => undefined, // cmp has no destination register else => if (maybe_inst) |inst| blk: { const bin_op = self.air.instructions.items(.data)[inst].bin_op; @@ -1400,39 +1368,23 @@ fn binOpImmediate( if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); - const mir_tag: Mir.Inst.Tag = switch (tag) { - .add => .add_immediate, - .sub => .sub_immediate, - .shl, - .shl_exact, - => .lsl_immediate, - .shr, - .shr_exact, - => switch (lhs_ty.intInfo(self.target.*).signedness) { - .signed => Mir.Inst.Tag.asr_immediate, - .unsigned => Mir.Inst.Tag.lsr_immediate, - }, - .cmp_eq => .cmp_immediate, - else => unreachable, - }; - const mir_data: Mir.Inst.Data = switch (tag) { - .add, - .sub, + const mir_data: Mir.Inst.Data = switch (mir_tag) { + .add_immediate, + .sub_immediate, => .{ .rr_imm12_sh = .{ .rd = dest_reg, .rn = lhs_reg, .imm12 = @intCast(u12, rhs.immediate), } }, - .shl, - .shl_exact, - .shr, - .shr_exact, + .lsl_immediate, + .asr_immediate, + .lsr_immediate, => .{ .rr_shift = .{ .rd = dest_reg, .rn = lhs_reg, .shift = @intCast(u6, rhs.immediate), } }, - .cmp_eq => .{ .r_imm12_sh = .{ + .cmp_immediate => .{ .r_imm12_sh = .{ .rn = lhs_reg, .imm12 = @intCast(u12, rhs.immediate), } }, @@ -1470,7 +1422,6 @@ fn binOp( ) InnerError!MCValue { const target = self.target.*; switch (tag) { - // Arithmetic operations on integers and floats .add, .sub, .cmp_eq, @@ -1498,13 +1449,26 @@ fn binOp( else => unreachable, }; + const mir_tag_register: Mir.Inst.Tag = switch (tag) { + .add => .add_shifted_register, + .sub => .sub_shifted_register, + .cmp_eq => .cmp_shifted_register, + else => unreachable, + }; + const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { + .add => .add_immediate, + .sub => .sub_immediate, + .cmp_eq => .cmp_immediate, + else => unreachable, + }; + if (rhs_immediate_ok) { - return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag_immediate, maybe_inst, lhs, rhs, lhs_ty, false); } else if (lhs_immediate_ok) { // swap lhs and rhs - return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true); + return try self.binOpImmediate(mir_tag_immediate, maybe_inst, rhs, lhs, rhs_ty, true); } else { - return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag_register, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); } } else { return self.fail("TODO binary operations on int with bits > 64", .{}); @@ -1523,7 +1487,7 @@ fn binOp( // TODO add optimisations for multiplication // with immediates, for example a * 2 can be // lowered to a << 1 - return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(.mul, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); } else { return self.fail("TODO binary operations on int with bits > 64", .{}); } @@ -1564,7 +1528,6 @@ fn binOp( else => unreachable, } }, - // Bitwise operations on integers .bit_and, .bit_or, .xor, @@ -1576,7 +1539,14 @@ fn binOp( const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 64) { // TODO implement bitwise operations with immediates - return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + const mir_tag: Mir.Inst.Tag = switch (tag) { + .bit_and => .and_shifted_register, + .bit_or => .orr_shifted_register, + .xor => .eor_shifted_register, + else => unreachable, + }; + + return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); } else { return self.fail("TODO binary operations on int with bits > 64", .{}); } @@ -1594,10 +1564,27 @@ fn binOp( if (int_info.bits <= 64) { const rhs_immediate_ok = rhs == .immediate; + const mir_tag_register: Mir.Inst.Tag = switch (tag) { + .shl => .lsl_register, + .shr => switch (lhs_ty.intInfo(self.target.*).signedness) { + .signed => Mir.Inst.Tag.asr_register, + .unsigned => Mir.Inst.Tag.lsr_register, + }, + else => unreachable, + }; + const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { + .shl => .lsl_immediate, + .shr => switch (lhs_ty.intInfo(self.target.*).signedness) { + .signed => Mir.Inst.Tag.asr_immediate, + .unsigned => Mir.Inst.Tag.lsr_immediate, + }, + else => unreachable, + }; + if (rhs_immediate_ok) { - return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag_immediate, maybe_inst, lhs, rhs, lhs_ty, false); } else { - return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag_register, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); } } else { return self.fail("TODO binary operations on int with bits > 64", .{}); @@ -1614,7 +1601,13 @@ fn binOp( assert(lhs != .immediate); // should have been handled by Sema assert(rhs != .immediate); // should have been handled by Sema - return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + const mir_tag_register: Mir.Inst.Tag = switch (tag) { + .bool_and => .and_shifted_register, + .bool_or => .orr_shifted_register, + else => unreachable, + }; + + return try self.binOpRegister(mir_tag_register, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); }, else => unreachable, } @@ -1632,9 +1625,9 @@ fn binOp( const elem_size = elem_ty.abiSize(self.target.*); if (elem_size == 1) { - const base_tag: Air.Inst.Tag = switch (tag) { - .ptr_add => .add, - .ptr_sub => .sub, + const base_tag: Mir.Inst.Tag = switch (tag) { + .ptr_add => .add_shifted_register, + .ptr_sub => .sub_shifted_register, else => unreachable, }; From fa85a739d9a85084fbb6d934c2c8803042fa7d48 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Mon, 18 Apr 2022 13:50:54 +0200 Subject: [PATCH 1199/2031] stage2 AArch64: fix shl, shr, shl_exact, shr_exact Introduces the necessary truncation after shift --- src/arch/aarch64/CodeGen.zig | 53 ++++++++++++++++++++++++++++-------- test/behavior/math.zig | 6 ---- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index b328c70f06..688f59e804 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -1138,6 +1138,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { } }, }); + try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits); + break :result MCValue{ .register = dest_reg }; } else { return self.fail("TODO AArch64 not on integers > u64/i64", .{}); @@ -1516,11 +1518,8 @@ fn binOp( const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 64) { const result_reg = result.register; - - if (int_info.bits < 64) { - try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); - return result; - } else return result; + try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); + return result; } else { return self.fail("TODO binary operations on integers > u64/i64", .{}); } @@ -1554,8 +1553,8 @@ fn binOp( else => unreachable, } }, - .shl, - .shr, + .shl_exact, + .shr_exact, => { switch (lhs_ty.zigTypeTag()) { .Vector => return self.fail("TODO binary operations on vectors", .{}), @@ -1565,16 +1564,16 @@ fn binOp( const rhs_immediate_ok = rhs == .immediate; const mir_tag_register: Mir.Inst.Tag = switch (tag) { - .shl => .lsl_register, - .shr => switch (lhs_ty.intInfo(self.target.*).signedness) { + .shl_exact => .lsl_register, + .shr_exact => switch (int_info.signedness) { .signed => Mir.Inst.Tag.asr_register, .unsigned => Mir.Inst.Tag.lsr_register, }, else => unreachable, }; const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { - .shl => .lsl_immediate, - .shr => switch (lhs_ty.intInfo(self.target.*).signedness) { + .shl_exact => .lsl_immediate, + .shr_exact => switch (int_info.signedness) { .signed => Mir.Inst.Tag.asr_immediate, .unsigned => Mir.Inst.Tag.lsr_immediate, }, @@ -1593,6 +1592,38 @@ fn binOp( else => unreachable, } }, + .shl, + .shr, + => { + const base_tag: Air.Inst.Tag = switch (tag) { + .shl => .shl_exact, + .shr => .shr_exact, + else => unreachable, + }; + + // Generate a shl_exact/shr_exact + const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + + // Truncate if necessary + switch (tag) { + .shr => return result, + .shl => switch (lhs_ty.zigTypeTag()) { + .Vector => return self.fail("TODO binary operations on vectors", .{}), + .Int => { + const int_info = lhs_ty.intInfo(self.target.*); + if (int_info.bits <= 64) { + const result_reg = result.register; + try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); + return result; + } else { + return self.fail("TODO binary operations on integers > u64/i64", .{}); + } + }, + else => unreachable, + }, + else => unreachable, + } + }, .bool_and, .bool_or, => { diff --git a/test/behavior/math.zig b/test/behavior/math.zig index ce316aeea2..f5494adfeb 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -363,7 +363,6 @@ fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int test "binary not" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try expect(comptime x: { break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101; @@ -851,8 +850,6 @@ test "quad hex float literal parsing accurate" { } test "truncating shift left" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - try testShlTrunc(maxInt(u16)); comptime try testShlTrunc(maxInt(u16)); } @@ -863,7 +860,6 @@ fn testShlTrunc(x: u16) !void { test "exact shift left" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testShlExact(0b00110101); comptime try testShlExact(0b00110101); @@ -875,7 +871,6 @@ fn testShlExact(x: u8) !void { test "exact shift right" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testShrExact(0b10110100); comptime try testShrExact(0b10110100); @@ -887,7 +882,6 @@ fn testShrExact(x: u8) !void { test "shift left/right on u0 operand" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { From 0c5ad335d293be638b8d34e671cf62f84b0babce Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 19 Apr 2022 14:40:27 -0700 Subject: [PATCH 1200/2031] build system: add -fstage1/-fno-stage1 to `zig build` So that people can start experimenting with compiling their projects with the self-hosted compiler. I expect this commit to be reverted after #89 is closed. --- lib/std/build.zig | 16 ++++++++++++++++ lib/std/special/build_runner.zig | 17 ++++++++++++----- src/main.zig | 18 ++++++++++++++++-- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index ff0f36b4a8..5966002799 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -44,6 +44,7 @@ pub const Builder = struct { /// The purpose of executing the command is for a human to read compile errors from the terminal prominent_compile_errors: bool, color: enum { auto, on, off } = .auto, + use_stage1: ?bool = null, invalid_user_input: bool, zig_exe: []const u8, default_step: *Step, @@ -1591,6 +1592,7 @@ pub const LibExeObjStep = struct { stack_size: ?u64 = null, want_lto: ?bool = null, + use_stage1: ?bool = null, output_path_source: GeneratedFile, output_lib_path_source: GeneratedFile, @@ -2338,6 +2340,20 @@ pub const LibExeObjStep = struct { try zig_args.append(@tagName(builder.color)); } + if (self.use_stage1) |stage1| { + if (stage1) { + try zig_args.append("-fstage1"); + } else { + try zig_args.append("-fno-stage1"); + } + } else if (builder.use_stage1) |stage1| { + if (stage1) { + try zig_args.append("-fstage1"); + } else { + try zig_args.append("-fno-stage1"); + } + } + if (self.entry_symbol_name) |entry| { try zig_args.append("--entry"); try zig_args.append(entry); diff --git a/lib/std/special/build_runner.zig b/lib/std/special/build_runner.zig index eb83ef8fcd..523723ddf2 100644 --- a/lib/std/special/build_runner.zig +++ b/lib/std/special/build_runner.zig @@ -24,19 +24,19 @@ pub fn main() !void { var arg_idx: usize = 1; const zig_exe = nextArg(args, &arg_idx) orelse { - std.debug.print("Expected first argument to be path to zig compiler\n", .{}); + std.debug.print("Expected path to zig compiler\n", .{}); return error.InvalidArgs; }; const build_root = nextArg(args, &arg_idx) orelse { - std.debug.print("Expected second argument to be build root directory path\n", .{}); + std.debug.print("Expected build root directory path\n", .{}); return error.InvalidArgs; }; const cache_root = nextArg(args, &arg_idx) orelse { - std.debug.print("Expected third argument to be cache root directory path\n", .{}); + std.debug.print("Expected cache root directory path\n", .{}); return error.InvalidArgs; }; const global_cache_root = nextArg(args, &arg_idx) orelse { - std.debug.print("Expected third argument to be global cache root directory path\n", .{}); + std.debug.print("Expected global cache root directory path\n", .{}); return error.InvalidArgs; }; @@ -181,6 +181,10 @@ pub fn main() !void { builder.enable_darling = true; } else if (mem.eql(u8, arg, "-fno-darling")) { builder.enable_darling = false; + } else if (mem.eql(u8, arg, "-fstage1")) { + builder.use_stage1 = true; + } else if (mem.eql(u8, arg, "-fno-stage1")) { + builder.use_stage1 = false; } else if (mem.eql(u8, arg, "--")) { builder.args = argsRest(args, arg_idx); break; @@ -302,8 +306,11 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void try out_stream.writeAll( \\ \\Advanced Options: + \\ -fstage1 Force using bootstrap compiler as the codegen backend + \\ -fno-stage1 Prevent using bootstrap compiler as the codegen backend \\ --build-file [file] Override path to build.zig - \\ --cache-dir [path] Override path to zig cache directory + \\ --cache-dir [path] Override path to local Zig cache directory + \\ --global-cache-dir [path] Override path to global Zig cache directory \\ --zig-lib-dir [arg] Override path to Zig lib directory \\ --debug-log [scope] Enable debugging the compiler \\ --verbose-link Enable compiler debug output for linking diff --git a/src/main.zig b/src/main.zig index 51765edb51..84a69b98f1 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3464,13 +3464,20 @@ pub const usage_build = \\ Build a project from build.zig. \\ \\Options: - \\ -h, --help Print this help and exit - \\ + \\ -fstage1 Force using bootstrap compiler as the codegen backend + \\ -fno-stage1 Prevent using bootstrap compiler as the codegen backend + \\ --build-file [file] Override path to build.zig + \\ --cache-dir [path] Override path to local Zig cache directory + \\ --global-cache-dir [path] Override path to global Zig cache directory + \\ --zig-lib-dir [arg] Override path to Zig lib directory + \\ --prominent-compile-errors Output compile errors formatted for a human to read + \\ -h, --help Print this help and exit \\ ; pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { var prominent_compile_errors: bool = false; + var use_stage1: ?bool = null; // We want to release all the locks before executing the child process, so we make a nice // big block here to ensure the cleanup gets run when we extract out our argv. @@ -3525,6 +3532,12 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi continue; } else if (mem.eql(u8, arg, "--prominent-compile-errors")) { prominent_compile_errors = true; + } else if (mem.eql(u8, arg, "-fstage1")) { + use_stage1 = true; + try child_argv.append(arg); + } else if (mem.eql(u8, arg, "-fno-stage1")) { + use_stage1 = false; + try child_argv.append(arg); } } try child_argv.append(arg); @@ -3665,6 +3678,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi .optimize_mode = .Debug, .self_exe_path = self_exe_path, .thread_pool = &thread_pool, + .use_stage1 = use_stage1, }) catch |err| { fatal("unable to create compilation: {s}", .{@errorName(err)}); }; From 859ae152bc83ca759f466794b8186fa0d3de5060 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Tue, 19 Apr 2022 23:27:34 +0200 Subject: [PATCH 1201/2031] array hash map: fix getOrPutAdapted on Managed array hash map --- lib/std/array_hash_map.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/array_hash_map.zig b/lib/std/array_hash_map.zig index aed60a1f0d..31860963af 100644 --- a/lib/std/array_hash_map.zig +++ b/lib/std/array_hash_map.zig @@ -185,7 +185,7 @@ pub fn ArrayHashMap( return self.unmanaged.getOrPutContext(self.allocator, key, self.ctx); } pub fn getOrPutAdapted(self: *Self, key: anytype, ctx: anytype) !GetOrPutResult { - return self.unmanaged.getOrPutContextAdapted(key, ctx, self.ctx); + return self.unmanaged.getOrPutContextAdapted(self.allocator, key, ctx, self.ctx); } /// If there is an existing item with `key`, then the result From 2fa7f6e502724487e13a0a0b482bf499352746b2 Mon Sep 17 00:00:00 2001 From: Yusuf Bham Date: Sat, 16 Apr 2022 16:38:27 -0400 Subject: [PATCH 1202/2031] std.os.uefi: Add `BlockIoProtocol` --- lib/std/os/uefi/protocols.zig | 3 +- .../os/uefi/protocols/block_io_protocol.zig | 80 +++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 lib/std/os/uefi/protocols/block_io_protocol.zig diff --git a/lib/std/os/uefi/protocols.zig b/lib/std/os/uefi/protocols.zig index 0c027f046b..9dc448bf8b 100644 --- a/lib/std/os/uefi/protocols.zig +++ b/lib/std/os/uefi/protocols.zig @@ -4,9 +4,10 @@ pub usingnamespace @import("protocols/device_path_protocol.zig"); pub usingnamespace @import("protocols/rng_protocol.zig"); pub usingnamespace @import("protocols/shell_parameters_protocol.zig"); -// Files +// Files / IO pub usingnamespace @import("protocols/simple_file_system_protocol.zig"); pub usingnamespace @import("protocols/file_protocol.zig"); +pub usingnamespace @import("protocols/block_io_protocol.zig"); // Text pub usingnamespace @import("protocols/simple_text_input_protocol.zig"); diff --git a/lib/std/os/uefi/protocols/block_io_protocol.zig b/lib/std/os/uefi/protocols/block_io_protocol.zig new file mode 100644 index 0000000000..938eb930da --- /dev/null +++ b/lib/std/os/uefi/protocols/block_io_protocol.zig @@ -0,0 +1,80 @@ +const std = @import("std"); +const uefi = std.os.uefi; +const Status = uefi.Status; + +const EfiBlockMedia = extern struct { + /// The current media ID. If the media changes, this value is changed. + media_id: u32, + + /// `true` if the media is removable; otherwise, `false`. + removable_media: bool, + /// `true` if there is a media currently present in the device + media_present: bool, + /// `true` if the `BlockIoProtocol` was produced to abstract + /// partition structures on the disk. `false` if the `BlockIoProtocol` was + /// produced to abstract the logical blocks on a hardware device. + logical_partition: bool, + /// `true` if the media is marked read-only otherwise, `false`. This field + /// shows the read-only status as of the most recent `WriteBlocks()` + read_only: bool, + /// `true` if the WriteBlocks() function caches write data. + write_caching: bool, + + /// The intrinsic block size of the device. If the media changes, then this + // field is updated. Returns the number of bytes per logical block. + block_size: u32, + /// Supplies the alignment requirement for any buffer used in a data + /// transfer. IoAlign values of 0 and 1 mean that the buffer can be + /// placed anywhere in memory. Otherwise, IoAlign must be a power of + /// 2, and the requirement is that the start address of a buffer must be + /// evenly divisible by IoAlign with no remainder. + io_align: u32, + /// The last LBA on the device. If the media changes, then this field is updated. + last_block: u64, + + // Revision 2 + lowest_aligned_lba: u64, + logical_blocks_per_physical_block: u32, + optimal_transfer_length_granularity: u32, +}; + +const BlockIoProtocol = extern struct { + const Self = @This(); + + revision: u64, + media: *EfiBlockMedia, + + _reset: fn (*BlockIoProtocol, extended_verification: bool) callconv(.C) Status, + _read_blocks: fn (*BlockIoProtocol, media_id: u32, lba: u64, buffer_size: usize, buf: [*]u8) callconv(.C) Status, + _write_blocks: fn (*BlockIoProtocol, media_id: u32, lba: u64, buffer_size: usize, buf: [*]u8) callconv(.C) Status, + _flush_blocks: fn (*BlockIoProtocol) callconv(.C) Status, + + /// Resets the block device hardware. + pub fn reset(self: *Self, extended_verification: bool) Status { + return self._reset(self, extended_verification); + } + + /// Reads the number of requested blocks from the device. + pub fn readBlocks(self: *Self, media_id: u32, lba: u64, buffer_size: usize, buf: [*]u8) Status { + return self._read_blocks(self, media_id, lba, buffer_size, buf); + } + + /// Writes a specified number of blocks to the device. + pub fn writeBlocks(self: *Self, media_id: u32, lba: u64, buffer_size: usize, buf: [*]u8) Status { + return self._write_blocks(self, media_id, lba, buffer_size, buf); + } + + /// Flushes all modified data to a physical block device. + pub fn flushBlocks(self: *Self) Status { + return self._flush_blocks(self); + } + + pub const guid align(8) = uefi.Guid{ + .time_low = 0x964e5b21, + .time_mid = 0x6459, + .time_high_and_version = 0x11d2, + .clock_seq_high_and_reserved = 0x8e, + .clock_seq_low = 0x39, + .node = [_]u8{ 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b }, + }; +}; From e3cbea934ee196b3833f4c4269bc3b2796c11928 Mon Sep 17 00:00:00 2001 From: protty <45520026+kprotty@users.noreply.github.com> Date: Tue, 19 Apr 2022 19:42:15 -0500 Subject: [PATCH 1203/2031] std.Thread.Futex improvements (#11464) * atomic: cache_line * Thread: Futex rewrite + more native platform support * Futex: tests compile * Futex: compiles and runs test * Futex: broadcast test * Futex: fix PosixImpl for tests * Futex: fix compile errors for bsd platforms * Futex: review changes + fix timeout=0 + more comments --- lib/std/Thread/Futex.zig | 1192 +++++++++++++++++++++++++------------- lib/std/atomic.zig | 44 +- lib/std/c/dragonfly.zig | 3 + lib/std/c/freebsd.zig | 40 ++ lib/std/c/openbsd.zig | 7 + 5 files changed, 882 insertions(+), 404 deletions(-) diff --git a/lib/std/Thread/Futex.zig b/lib/std/Thread/Futex.zig index d256fb5a43..a1c8ca71e4 100644 --- a/lib/std/Thread/Futex.zig +++ b/lib/std/Thread/Futex.zig @@ -7,9 +7,7 @@ const std = @import("../std.zig"); const builtin = @import("builtin"); const Futex = @This(); -const target = builtin.target; -const single_threaded = builtin.single_threaded; - +const os = std.os; const assert = std.debug.assert; const testing = std.testing; @@ -21,160 +19,152 @@ const spinLoopHint = std.atomic.spinLoopHint; /// - The caller is unblocked by a matching `wake()`. /// - The caller is unblocked spuriously by an arbitrary internal signal. /// -/// If `timeout` is provided, and the caller is blocked for longer than `timeout` nanoseconds`, `error.TimedOut` is returned. +/// The checking of `ptr` and `expect`, along with blocking the caller, is done atomically +/// and totally ordered (sequentially consistent) with respect to other wait()/wake() calls on the same `ptr`. +pub fn wait(ptr: *const Atomic(u32), expect: u32) void { + @setCold(true); + + Impl.wait(ptr, expect, null) catch |err| switch (err) { + error.Timeout => unreachable, // null timeout meant to wait forever + }; +} + +/// Checks if `ptr` still contains the value `expect` and, if so, blocks the caller until either: +/// - The value at `ptr` is no longer equal to `expect`. +/// - The caller is unblocked by a matching `wake()`. +/// - The caller is unblocked spuriously by an arbitrary internal signal. +/// - The caller blocks for longer than the given timeout. In which case, `error.Timeout` is returned. /// /// The checking of `ptr` and `expect`, along with blocking the caller, is done atomically /// and totally ordered (sequentially consistent) with respect to other wait()/wake() calls on the same `ptr`. -pub fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{TimedOut}!void { - if (single_threaded) { - // check whether the caller should block +pub fn timedWait(ptr: *const Atomic(u32), expect: u32, timeout_ns: u64) error{Timeout}!void { + @setCold(true); + + // Avoid calling into the OS for no-op timeouts. + if (timeout_ns == 0) { + if (ptr.load(.SeqCst) != expect) return; + return error.Timeout; + } + + return Impl.wait(ptr, expect, timeout_ns); +} + +/// Unblocks at most `max_waiters` callers blocked in a `wait()` call on `ptr`. +pub fn wake(ptr: *const Atomic(u32), max_waiters: u32) void { + @setCold(true); + + // Avoid calling into the OS if there's nothing to wake up. + if (max_waiters == 0) { + return; + } + + Impl.wake(ptr, max_waiters); +} + +const Impl = if (builtin.single_threaded) + SerialImpl +else if (builtin.os.tag == .windows) + WindowsImpl +else if (builtin.os.tag.isDarwin()) + DarwinImpl +else if (builtin.os.tag == .linux) + LinuxImpl +else if (builtin.os.tag == .freebsd) + FreebsdImpl +else if (builtin.os.tag == .openbsd) + OpenbsdImpl +else if (builtin.os.tag == .dragonfly) + DragonflyImpl +else if (std.Thread.use_pthreads) + PosixImpl +else + UnsupportedImpl; + +/// We can't do @compileError() in the `Impl` switch statement above as its eagerly evaluated. +/// So instead, we @compileError() on the methods themselves for platforms which don't support futex. +const UnsupportedImpl = struct { + fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{Timeout}!void { + return unsupported(.{ ptr, expect, timeout }); + } + + fn wake(ptr: *const Atomic(u32), max_waiters: u32) void { + return unsupported(.{ ptr, max_waiters }); + } + + fn unsupported(unused: anytype) noreturn { + _ = unused; + @compileError("Unsupported operating system " ++ @tagName(builtin.target.os.tag)); + } +}; + +const SerialImpl = struct { + fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{Timeout}!void { if (ptr.loadUnchecked() != expect) { return; } - // There are no other threads which could notify the caller on single_threaded. - // Therefor a wait() without a timeout would block indefinitely. - const timeout_ns = timeout orelse { - @panic("deadlock"); + // There are no threads to wake us up. + // So if we wait without a timeout we would never wake up. + const delay = timeout orelse { + unreachable; // deadlock detected }; - // Simulate blocking with the timeout knowing that: - // - no other thread can change the ptr value - // - no other thread could unblock us if we waiting on the ptr - std.time.sleep(timeout_ns); - return error.TimedOut; + std.time.sleep(delay); + return error.Timeout; } - // Avoid calling into the OS for no-op waits() - if (timeout) |timeout_ns| { - if (timeout_ns == 0) { - if (ptr.load(.SeqCst) != expect) return; - return error.TimedOut; - } - } - - return OsFutex.wait(ptr, expect, timeout); -} - -/// Unblocks at most `num_waiters` callers blocked in a `wait()` call on `ptr`. -/// `num_waiters` of 1 unblocks at most one `wait(ptr, ...)` and `maxInt(u32)` unblocks effectively all `wait(ptr, ...)`. -pub fn wake(ptr: *const Atomic(u32), num_waiters: u32) void { - if (single_threaded) return; - if (num_waiters == 0) return; - - return OsFutex.wake(ptr, num_waiters); -} - -const OsFutex = if (target.os.tag == .windows) - WindowsFutex -else if (target.os.tag == .linux) - LinuxFutex -else if (target.isDarwin()) - DarwinFutex -else if (builtin.link_libc) - PosixFutex -else - UnsupportedFutex; - -const UnsupportedFutex = struct { - fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{TimedOut}!void { - return unsupported(.{ ptr, expect, timeout }); - } - - fn wake(ptr: *const Atomic(u32), num_waiters: u32) void { - return unsupported(.{ ptr, num_waiters }); - } - - fn unsupported(unused: anytype) noreturn { - @compileLog("Unsupported operating system", target.os.tag); - _ = unused; - unreachable; + fn wake(ptr: *const Atomic(u32), max_waiters: u32) void { + // There are no other threads to possibly wake up + _ = ptr; + _ = max_waiters; } }; -const WindowsFutex = struct { - const windows = std.os.windows; - - fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{TimedOut}!void { - var timeout_value: windows.LARGE_INTEGER = undefined; - var timeout_ptr: ?*const windows.LARGE_INTEGER = null; +// We use WaitOnAddress through NtDll instead of API-MS-Win-Core-Synch-l1-2-0.dll +// as it's generally already a linked target and is autoloaded into all processes anyway. +const WindowsImpl = struct { + fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{Timeout}!void { + var timeout_value: os.windows.LARGE_INTEGER = undefined; + var timeout_ptr: ?*const os.windows.LARGE_INTEGER = null; // NTDLL functions work with time in units of 100 nanoseconds. - // Positive values for timeouts are absolute time while negative is relative. - if (timeout) |timeout_ns| { + // Positive values are absolute deadlines while negative values are relative durations. + if (timeout) |delay| { + timeout_value = @intCast(os.windows.LARGE_INTEGER, delay / 100); + timeout_value = -timeout_value; timeout_ptr = &timeout_value; - timeout_value = -@intCast(windows.LARGE_INTEGER, timeout_ns / 100); } - switch (windows.ntdll.RtlWaitOnAddress( + const rc = os.windows.ntdll.RtlWaitOnAddress( @ptrCast(?*const anyopaque, ptr), @ptrCast(?*const anyopaque, &expect), @sizeOf(@TypeOf(expect)), timeout_ptr, - )) { + ); + + switch (rc) { .SUCCESS => {}, - .TIMEOUT => return error.TimedOut, + .TIMEOUT => { + assert(timeout != null); + return error.Timeout; + }, else => unreachable, } } - fn wake(ptr: *const Atomic(u32), num_waiters: u32) void { + fn wake(ptr: *const Atomic(u32), max_waiters: u32) void { const address = @ptrCast(?*const anyopaque, ptr); - switch (num_waiters) { - 1 => windows.ntdll.RtlWakeAddressSingle(address), - else => windows.ntdll.RtlWakeAddressAll(address), + assert(max_waiters != 0); + + switch (max_waiters) { + 1 => os.windows.ntdll.RtlWakeAddressSingle(address), + else => os.windows.ntdll.RtlWakeAddressAll(address), } } }; -const LinuxFutex = struct { - const linux = std.os.linux; - - fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{TimedOut}!void { - var ts: std.os.timespec = undefined; - var ts_ptr: ?*std.os.timespec = null; - - // Futex timespec timeout is already in relative time. - if (timeout) |timeout_ns| { - ts_ptr = &ts; - ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), timeout_ns / std.time.ns_per_s); - ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), timeout_ns % std.time.ns_per_s); - } - - switch (linux.getErrno(linux.futex_wait( - @ptrCast(*const i32, ptr), - linux.FUTEX.PRIVATE_FLAG | linux.FUTEX.WAIT, - @bitCast(i32, expect), - ts_ptr, - ))) { - .SUCCESS => {}, // notified by `wake()` - .INTR => {}, // spurious wakeup - .AGAIN => {}, // ptr.* != expect - .TIMEDOUT => return error.TimedOut, - .INVAL => {}, // possibly timeout overflow - .FAULT => unreachable, - else => unreachable, - } - } - - fn wake(ptr: *const Atomic(u32), num_waiters: u32) void { - switch (linux.getErrno(linux.futex_wake( - @ptrCast(*const i32, ptr), - linux.FUTEX.PRIVATE_FLAG | linux.FUTEX.WAKE, - std.math.cast(i32, num_waiters) catch std.math.maxInt(i32), - ))) { - .SUCCESS => {}, // successful wake up - .INVAL => {}, // invalid futex_wait() on ptr done elsewhere - .FAULT => {}, // pointer became invalid while doing the wake - else => unreachable, - } - } -}; - -const DarwinFutex = struct { - const darwin = std.os.darwin; - - fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{TimedOut}!void { +const DarwinImpl = struct { + fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{Timeout}!void { // Darwin XNU 7195.50.7.100.1 introduced __ulock_wait2 and migrated code paths (notably pthread_cond_t) towards it: // https://github.com/apple/darwin-xnu/commit/d4061fb0260b3ed486147341b72468f836ed6c8f#diff-08f993cc40af475663274687b7c326cc6c3031e0db3ac8de7b24624610616be6 // @@ -183,58 +173,67 @@ const DarwinFutex = struct { // // ulock_wait() uses 32-bit micro-second timeouts where 0 = INFINITE or no-timeout // ulock_wait2() uses 64-bit nano-second timeouts (with the same convention) + const supports_ulock_wait2 = builtin.target.os.version_range.semver.min.major >= 11; + var timeout_ns: u64 = 0; - if (timeout) |timeout_value| { - // This should be checked by the caller. - assert(timeout_value != 0); - timeout_ns = timeout_value; + if (timeout) |delay| { + assert(delay != 0); // handled by timedWait() + timeout_ns = delay; } - const addr = @ptrCast(*const anyopaque, ptr); - const flags = darwin.UL_COMPARE_AND_WAIT | darwin.ULF_NO_ERRNO; + // If we're using `__ulock_wait` and `timeout` is too big to fit inside a `u32` count of // micro-seconds (around 70min), we'll request a shorter timeout. This is fine (users // should handle spurious wakeups), but we need to remember that we did so, so that - // we don't return `TimedOut` incorrectly. If that happens, we set this variable to + // we don't return `Timeout` incorrectly. If that happens, we set this variable to // true so that we we know to ignore the ETIMEDOUT result. var timeout_overflowed = false; + + const addr = @ptrCast(*const anyopaque, ptr); + const flags = os.darwin.UL_COMPARE_AND_WAIT | os.darwin.ULF_NO_ERRNO; const status = blk: { - if (target.os.version_range.semver.min.major >= 11) { - break :blk darwin.__ulock_wait2(flags, addr, expect, timeout_ns, 0); - } else { - const timeout_us = std.math.cast(u32, timeout_ns / std.time.ns_per_us) catch overflow: { - timeout_overflowed = true; - break :overflow std.math.maxInt(u32); - }; - break :blk darwin.__ulock_wait(flags, addr, expect, timeout_us); + if (supports_ulock_wait2) { + break :blk os.darwin.__ulock_wait2(flags, addr, expect, timeout_ns, 0); } + + const timeout_us = std.math.cast(u32, timeout_ns / std.time.ns_per_us) catch overflow: { + timeout_overflowed = true; + break :overflow std.math.maxInt(u32); + }; + + break :blk os.darwin.__ulock_wait(flags, addr, expect, timeout_us); }; if (status >= 0) return; switch (@intToEnum(std.os.E, -status)) { + // Wait was interrupted by the OS or other spurious signalling. .INTR => {}, - // Address of the futex is paged out. This is unlikely, but possible in theory, and + // Address of the futex was paged out. This is unlikely, but possible in theory, and // pthread/libdispatch on darwin bother to handle it. In this case we'll return // without waiting, but the caller should retry anyway. .FAULT => {}, - .TIMEDOUT => if (!timeout_overflowed) return error.TimedOut, + // Only report Timeout if we didn't have to cap the timeout + .TIMEDOUT => { + assert(timeout != null); + if (!timeout_overflowed) return error.Timeout; + }, else => unreachable, } } - fn wake(ptr: *const Atomic(u32), num_waiters: u32) void { - var flags: u32 = darwin.UL_COMPARE_AND_WAIT | darwin.ULF_NO_ERRNO; - if (num_waiters > 1) { - flags |= darwin.ULF_WAKE_ALL; + fn wake(ptr: *const Atomic(u32), max_waiters: u32) void { + var flags: u32 = os.darwin.UL_COMPARE_AND_WAIT | os.darwin.ULF_NO_ERRNO; + if (max_waiters > 1) { + flags |= os.darwin.ULF_WAKE_ALL; } while (true) { const addr = @ptrCast(*const anyopaque, ptr); - const status = darwin.__ulock_wake(flags, addr, 0); + const status = os.darwin.__ulock_wake(flags, addr, 0); if (status >= 0) return; switch (@intToEnum(std.os.E, -status)) { .INTR => continue, // spurious wake() - .FAULT => continue, // address of the lock was paged out + .FAULT => unreachable, // __ulock_wake doesn't generate EFAULT according to darwin pthread_cond_t .NOENT => return, // nothing was woken up .ALREADY => unreachable, // only for ULF_WAKE_THREAD else => unreachable, @@ -243,332 +242,723 @@ const DarwinFutex = struct { } }; -const PosixFutex = struct { - fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{TimedOut}!void { - const address = @ptrToInt(ptr); - const bucket = Bucket.from(address); - var waiter: List.Node = undefined; - - { - assert(std.c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS); - defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS); - - if (ptr.load(.SeqCst) != expect) { - return; - } - - waiter.data = .{ .address = address }; - bucket.list.prepend(&waiter); +// https://man7.org/linux/man-pages/man2/futex.2.html +const LinuxImpl = struct { + fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{Timeout}!void { + var ts: os.timespec = undefined; + if (timeout) |timeout_ns| { + ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), timeout_ns / std.time.ns_per_s); + ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), timeout_ns % std.time.ns_per_s); } - var timed_out = false; - waiter.data.wait(timeout) catch { - defer if (!timed_out) { - waiter.data.wait(null) catch unreachable; + const rc = os.linux.futex_wait( + @ptrCast(*const i32, &ptr.value), + os.linux.FUTEX.PRIVATE_FLAG | os.linux.FUTEX.WAIT, + @bitCast(i32, expect), + if (timeout != null) &ts else null, + ); + + switch (os.linux.getErrno(rc)) { + .SUCCESS => {}, // notified by `wake()` + .INTR => {}, // spurious wakeup + .AGAIN => {}, // ptr.* != expect + .TIMEDOUT => { + assert(timeout != null); + return error.Timeout; + }, + .INVAL => {}, // possibly timeout overflow + .FAULT => unreachable, // ptr was invalid + else => unreachable, + } + } + + fn wake(ptr: *const Atomic(u32), max_waiters: u32) void { + const rc = os.linux.futex_wake( + @ptrCast(*const i32, &ptr.value), + os.linux.FUTEX.PRIVATE_FLAG | os.linux.FUTEX.WAKE, + std.math.cast(i32, max_waiters) catch std.math.maxInt(i32), + ); + + switch (os.linux.getErrno(rc)) { + .SUCCESS => {}, // successful wake up + .INVAL => {}, // invalid futex_wait() on ptr done elsewhere + .FAULT => {}, // pointer became invalid while doing the wake + else => unreachable, + } + } +}; + +// https://www.freebsd.org/cgi/man.cgi?query=_umtx_op&sektion=2&n=1 +const FreebsdImpl = struct { + fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{Timeout}!void { + var tm_size: usize = 0; + var tm: os.freebsd._umtx_time = undefined; + var tm_ptr: ?*const os.freebsd._umtx_time = null; + + if (timeout) |timeout_ns| { + tm_ptr = &tm; + tm_size = @sizeOf(@TypeOf(tm)); + + tm._flags = 0; // use relative time not UMTX_ABSTIME + tm._clockid = os.CLOCK.MONOTONIC; + tm._timeout.tv_sec = @intCast(@TypeOf(tm._timeout.tv_sec), timeout_ns / std.time.ns_per_s); + tm._timeout.tv_nsec = @intCast(@TypeOf(tm._timeout.tv_nsec), timeout_ns % std.time.ns_per_s); + } + + const rc = os.freebsd._umtx_op( + @ptrToInt(&ptr.value), + @enumToInt(os.freebsd.UMTX_OP.WAIT_UINT_PRIVATE), + @as(c_ulong, expect), + tm_size, + @ptrToInt(tm_ptr), + ); + + switch (os.errno(rc)) { + .SUCCESS => {}, + .FAULT => unreachable, // one of the args points to invalid memory + .INVAL => unreachable, // arguments should be correct + .TIMEDOUT => { + assert(timeout != null); + return error.Timeout; + }, + .INTR => {}, // spurious wake + else => unreachable, + } + } + + fn wake(ptr: *const Atomic(u32), max_waiters: u32) void { + const rc = os.freebsd._umtx_op( + @ptrToInt(&ptr.value), + @enumToInt(os.freebsd.UMTX_OP.WAKE_PRIVATE), + @as(c_ulong, max_waiters), + 0, // there is no timeout struct + 0, // there is no timeout struct pointer + ); + + switch (os.errno(rc)) { + .SUCCESS => {}, + .FAULT => {}, // it's ok if the ptr doesn't point to valid memory + .INVAL => unreachable, // arguments should be correct + else => unreachable, + } + } +}; + +// https://man.openbsd.org/futex.2 +const OpenbsdImpl = struct { + fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{Timeout}!void { + var ts: os.timespec = undefined; + if (timeout) |timeout_ns| { + ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), timeout_ns / std.time.ns_per_s); + ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), timeout_ns % std.time.ns_per_s); + } + + const rc = os.openbsd.futex( + @ptrCast(*const volatile u32, &ptr.value), + os.openbsd.FUTEX_WAIT | os.openbsd.FUTEX_PRIVATE_FLAG, + @bitCast(c_int, expect), + if (timeout != null) &ts else null, + null, // FUTEX_WAIT takes no requeue address + ); + + switch (os.errno(rc)) { + .SUCCESS => {}, // woken up by wake + .NOSYS => unreachable, // the futex operation shouldn't be invalid + .FAULT => unreachable, // ptr was invalid + .AGAIN => {}, // ptr != expect + .INVAL => unreachable, // invalid timeout + .TIMEDOUT => { + assert(timeout != null); + return error.Timeout; + }, + .INTR => {}, // spurious wake from signal + .CANCELED => {}, // spurious wake from signal with SA_RESTART + else => unreachable, + } + } + + fn wake(ptr: *const Atomic(u32), max_waiters: u32) void { + const rc = os.openbsd.futex( + @ptrCast(*const volatile u32, &ptr.value), + os.openbsd.FUTEX_WAKE | os.openbsd.FUTEX_PRIVATE_FLAG, + std.math.cast(c_int, max_waiters) catch std.math.maxInt(c_int), + null, // FUTEX_WAKE takes no timeout ptr + null, // FUTEX_WAKE takes no requeue address + ); + + // returns number of threads woken up. + assert(rc >= 0); + } +}; + +// https://man.dragonflybsd.org/?command=umtx§ion=2 +const DragonflyImpl = struct { + fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{Timeout}!void { + // Dragonfly uses a scheme where 0 timeout means wait until signaled or spurious wake. + // It's reporting of timeout's is also unrealiable so we use an external timing source (Timer) instead. + var timeout_us: c_int = 0; + var timeout_overflowed = false; + var sleep_timer: std.time.Timer = undefined; + + if (timeout) |delay| { + assert(delay != 0); // handled by timedWait(). + timeout_us = std.math.cast(c_int, delay / std.time.ns_per_us) catch blk: { + timeout_overflowed = true; + break :blk std.math.maxInt(c_int); }; - assert(std.c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS); - defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS); - - if (waiter.data.address == address) { - timed_out = true; - bucket.list.remove(&waiter); + // Only need to record the start time if we can provide somewhat accurate error.Timeout's + if (!timeout_overflowed) { + sleep_timer = std.time.Timer.start() catch unreachable; } - }; + } - waiter.data.deinit(); - if (timed_out) { - return error.TimedOut; + const value = @bitCast(c_int, expect); + const addr = @ptrCast(*const volatile c_int, &ptr.value); + const rc = os.dragonfly.umtx_sleep(addr, value, timeout_us); + + switch (os.errno(rc)) { + .SUCCESS => {}, + .BUSY => {}, // ptr != expect + .AGAIN => { // maybe timed out, or paged out, or hit 2s kernel refresh + if (timeout) |timeout_ns| { + // Report error.Timeout only if we know the timeout duration has passed. + // If not, there's not much choice other than treating it as a spurious wake. + if (!timeout_overflowed and sleep_timer.read() >= timeout_ns) { + return error.Timeout; + } + } + }, + .INTR => {}, // spurious wake + .INVAL => unreachable, // invalid timeout + else => unreachable, } } - fn wake(ptr: *const Atomic(u32), num_waiters: u32) void { - const address = @ptrToInt(ptr); - const bucket = Bucket.from(address); - var can_notify = num_waiters; + fn wake(ptr: *const Atomic(u32), max_waiters: u32) void { + // A count of zero means wake all waiters. + assert(max_waiters != 0); + const to_wake = std.math.cast(c_int, max_waiters) catch 0; - var notified = List{}; - defer while (notified.popFirst()) |waiter| { - waiter.data.notify(); - }; - - assert(std.c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS); - defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS); - - var waiters = bucket.list.first; - while (waiters) |waiter| { - assert(waiter.data.address != null); - waiters = waiter.next; - - if (waiter.data.address != address) continue; - if (can_notify == 0) break; - can_notify -= 1; - - bucket.list.remove(waiter); - waiter.data.address = null; - notified.prepend(waiter); - } + // https://man.dragonflybsd.org/?command=umtx§ion=2 + // > umtx_wakeup() will generally return 0 unless the address is bad. + // We are fine with the address being bad (e.g. for Semaphore.post() where Semaphore.wait() frees the Semaphore) + const addr = @ptrCast(*const volatile c_int, &ptr.value); + _ = os.dragonfly.umtx_wakeup(addr, to_wake); } +}; - const Bucket = struct { - mutex: std.c.pthread_mutex_t = .{}, - list: List = .{}, +/// Modified version of linux's futex and Go's sema to implement userspace wait queues with pthread: +/// https://code.woboq.org/linux/linux/kernel/futex.c.html +/// https://go.dev/src/runtime/sema.go +const PosixImpl = struct { + const Event = struct { + cond: std.c.pthread_cond_t, + mutex: std.c.pthread_mutex_t, + state: enum { empty, waiting, notified }, - var buckets = [_]Bucket{.{}} ** 64; - - fn from(address: usize) *Bucket { - return &buckets[address % buckets.len]; - } - }; - - const List = std.TailQueue(struct { - address: ?usize, - state: State = .empty, - cond: std.c.pthread_cond_t = .{}, - mutex: std.c.pthread_mutex_t = .{}, - - const Self = @This(); - const State = enum { - empty, - waiting, - notified, - }; - - fn deinit(self: *Self) void { - _ = std.c.pthread_cond_destroy(&self.cond); - _ = std.c.pthread_mutex_destroy(&self.mutex); + fn init(self: *Event) void { + // Use static init instead of pthread_cond/mutex_init() since this is generally faster. + self.cond = .{}; + self.mutex = .{}; + self.state = .empty; } - fn wait(self: *Self, timeout: ?u64) error{TimedOut}!void { + fn deinit(self: *Event) void { + // Some platforms reportedly give EINVAL for statically initialized pthread types. + const rc = std.c.pthread_cond_destroy(&self.cond); + assert(rc == .SUCCESS or rc == .INVAL); + + const rm = std.c.pthread_mutex_destroy(&self.mutex); + assert(rm == .SUCCESS or rm == .INVAL); + + self.* = undefined; + } + + fn wait(self: *Event, timeout: ?u64) error{Timeout}!void { assert(std.c.pthread_mutex_lock(&self.mutex) == .SUCCESS); defer assert(std.c.pthread_mutex_unlock(&self.mutex) == .SUCCESS); - switch (self.state) { - .empty => self.state = .waiting, - .waiting => unreachable, - .notified => return, + // Early return if the event was already set. + if (self.state == .notified) { + return; } - var ts: std.os.timespec = undefined; - var ts_ptr: ?*const std.os.timespec = null; + // Compute the absolute timeout if one was specified. + // POSIX requires that REALTIME is used by default for the pthread timedwait functions. + // This can be changed with pthread_condattr_setclock, but it's an extension and may not be available everywhere. + var ts: os.timespec = undefined; if (timeout) |timeout_ns| { - ts_ptr = &ts; - std.os.clock_gettime(std.os.CLOCK.REALTIME, &ts) catch unreachable; - ts.tv_sec += @intCast(@TypeOf(ts.tv_sec), timeout_ns / std.time.ns_per_s); + os.clock_gettime(os.CLOCK.REALTIME, &ts) catch unreachable; + ts.tv_sec +|= @intCast(@TypeOf(ts.tv_sec), timeout_ns / std.time.ns_per_s); ts.tv_nsec += @intCast(@TypeOf(ts.tv_nsec), timeout_ns % std.time.ns_per_s); + if (ts.tv_nsec >= std.time.ns_per_s) { - ts.tv_sec += 1; + ts.tv_sec +|= 1; ts.tv_nsec -= std.time.ns_per_s; } } - while (true) { - switch (self.state) { - .empty => unreachable, - .waiting => {}, - .notified => return, - } + // Start waiting on the event - there can be only one thread waiting. + assert(self.state == .empty); + self.state = .waiting; - const ts_ref = ts_ptr orelse { - assert(std.c.pthread_cond_wait(&self.cond, &self.mutex) == .SUCCESS); - continue; + while (true) { + // Block using either pthread_cond_wait or pthread_cond_timewait if there's an absolute timeout. + const rc = blk: { + if (timeout == null) break :blk std.c.pthread_cond_wait(&self.cond, &self.mutex); + break :blk std.c.pthread_cond_timedwait(&self.cond, &self.mutex, &ts); }; - const rc = std.c.pthread_cond_timedwait(&self.cond, &self.mutex, ts_ref); + // After waking up, check if the event was set. + if (self.state == .notified) { + return; + } + + assert(self.state == .waiting); switch (rc) { .SUCCESS => {}, .TIMEDOUT => { + // If timed out, reset the event to avoid the set() thread doing an unnecessary signal(). self.state = .empty; - return error.TimedOut; + return error.Timeout; }, + .INVAL => unreachable, // cond, mutex, and potentially ts should all be valid + .PERM => unreachable, // mutex is locked when cond_*wait() functions are called else => unreachable, } } } - fn notify(self: *Self) void { + fn set(self: *Event) void { assert(std.c.pthread_mutex_lock(&self.mutex) == .SUCCESS); defer assert(std.c.pthread_mutex_unlock(&self.mutex) == .SUCCESS); - switch (self.state) { - .empty => self.state = .notified, - .waiting => { - self.state = .notified; - assert(std.c.pthread_cond_signal(&self.cond) == .SUCCESS); - }, - .notified => unreachable, + // Make sure that multiple calls to set() were not done on the same Event. + const old_state = self.state; + assert(old_state != .notified); + + // Mark the event as set and wake up the waiting thread if there was one. + // This must be done while the mutex as the wait() thread could deallocate + // the condition variable once it observes the new state, potentially causing a UAF if done unlocked. + self.state = .notified; + if (old_state == .waiting) { + assert(std.c.pthread_cond_signal(&self.cond) == .SUCCESS); } } - }); + }; + + const Treap = std.Treap(usize, std.math.order); + const Waiter = struct { + node: Treap.Node, + prev: ?*Waiter, + next: ?*Waiter, + tail: ?*Waiter, + is_queued: bool, + event: Event, + }; + + // An unordered set of Waiters + const WaitList = struct { + top: ?*Waiter = null, + len: usize = 0, + + fn push(self: *WaitList, waiter: *Waiter) void { + waiter.next = self.top; + self.top = waiter; + self.len += 1; + } + + fn pop(self: *WaitList) ?*Waiter { + const waiter = self.top orelse return null; + self.top = waiter.next; + self.len -= 1; + return waiter; + } + }; + + const WaitQueue = struct { + fn insert(treap: *Treap, address: usize, waiter: *Waiter) void { + // prepare the waiter to be inserted. + waiter.next = null; + waiter.is_queued = true; + + // Find the wait queue entry associated with the address. + // If there isn't a wait queue on the address, this waiter creates the queue. + var entry = treap.getEntryFor(address); + const entry_node = entry.node orelse { + waiter.prev = null; + waiter.tail = waiter; + entry.set(&waiter.node); + return; + }; + + // There's a wait queue on the address; get the queue head and tail. + const head = @fieldParentPtr(Waiter, "node", entry_node); + const tail = head.tail orelse unreachable; + + // Push the waiter to the tail by replacing it and linking to the previous tail. + head.tail = waiter; + tail.next = waiter; + waiter.prev = tail; + } + + fn remove(treap: *Treap, address: usize, max_waiters: usize) WaitList { + // Find the wait queue associated with this address and get the head/tail if any. + var entry = treap.getEntryFor(address); + var queue_head = if (entry.node) |node| @fieldParentPtr(Waiter, "node", node) else null; + const queue_tail = if (queue_head) |head| head.tail else null; + + // Once we're done updating the head, fix it's tail pointer and update the treap's queue head as well. + defer entry.set(blk: { + const new_head = queue_head orelse break :blk null; + new_head.tail = queue_tail; + break :blk &new_head.node; + }); + + var removed = WaitList{}; + while (removed.len < max_waiters) { + // dequeue and collect waiters from their wait queue. + const waiter = queue_head orelse break; + queue_head = waiter.next; + removed.push(waiter); + + // When dequeueing, we must mark is_queued as false. + // This ensures that a waiter which calls tryRemove() returns false. + assert(waiter.is_queued); + waiter.is_queued = false; + } + + return removed; + } + + fn tryRemove(treap: *Treap, address: usize, waiter: *Waiter) bool { + if (!waiter.is_queued) { + return false; + } + + queue_remove: { + // Find the wait queue associated with the address. + var entry = blk: { + // A waiter without a previous link means it's the queue head that's in the treap so we can avoid lookup. + if (waiter.prev == null) { + assert(waiter.node.key == address); + break :blk treap.getEntryForExisting(&waiter.node); + } + break :blk treap.getEntryFor(address); + }; + + // The queue head and tail must exist if we're removing a queued waiter. + const head = @fieldParentPtr(Waiter, "node", entry.node orelse unreachable); + const tail = head.tail orelse unreachable; + + // A waiter with a previous link is never the head of the queue. + if (waiter.prev) |prev| { + assert(waiter != head); + prev.next = waiter.next; + + // A waiter with both a previous and next link is in the middle. + // We only need to update the surrounding waiter's links to remove it. + if (waiter.next) |next| { + assert(waiter != tail); + next.prev = waiter.prev; + break :queue_remove; + } + + // A waiter with a previous but no next link means it's the tail of the queue. + // In that case, we need to update the head's tail reference. + assert(waiter == tail); + head.tail = waiter.prev; + break :queue_remove; + } + + // A waiter with no previous link means it's the queue head of queue. + // We must replace (or remove) the head waiter reference in the treap. + assert(waiter == head); + entry.set(blk: { + const new_head = waiter.next orelse break :blk null; + new_head.tail = head.tail; + break :blk &new_head.node; + }); + } + + // Mark the waiter as successfully removed. + waiter.is_queued = false; + return true; + } + }; + + const Bucket = struct { + mutex: std.c.pthread_mutex_t align(std.atomic.cache_line) = .{}, + pending: Atomic(usize) = Atomic(usize).init(0), + treap: Treap = .{}, + + // Global array of buckets that addresses map to. + // Bucket array size is pretty much arbitrary here, but it must be a power of two for fibonacci hashing. + var buckets = [_]Bucket{.{}} ** @bitSizeOf(usize); + + // https://github.com/Amanieu/parking_lot/blob/1cf12744d097233316afa6c8b7d37389e4211756/core/src/parking_lot.rs#L343-L353 + fn from(address: usize) *Bucket { + // The upper `@bitSizeOf(usize)` bits of the fibonacci golden ratio. + // Hashing this via (h * k) >> (64 - b) where k=golden-ration and b=bitsize-of-array + // evenly lays out h=hash values over the bit range even when the hash has poor entropy (identity-hash for pointers). + const max_multiplier_bits = @bitSizeOf(usize); + const fibonacci_multiplier = 0x9E3779B97F4A7C15 >> (64 - max_multiplier_bits); + + const max_bucket_bits = @ctz(usize, buckets.len); + comptime assert(std.math.isPowerOfTwo(buckets.len)); + + const index = (address *% fibonacci_multiplier) >> (max_multiplier_bits - max_bucket_bits); + return &buckets[index]; + } + }; + + const Address = struct { + fn from(ptr: *const Atomic(u32)) usize { + // Get the alignment of the pointer. + const alignment = @alignOf(Atomic(u32)); + comptime assert(std.math.isPowerOfTwo(alignment)); + + // Make sure the pointer is aligned, + // then cut off the zero bits from the alignment to get the unique address. + const addr = @ptrToInt(ptr); + assert(addr & (alignment - 1) == 0); + return addr >> @ctz(usize, alignment); + } + }; + + fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{Timeout}!void { + const address = Address.from(ptr); + const bucket = Bucket.from(address); + + // Announce that there's a waiter in the bucket before checking the ptr/expect condition. + // If the announcement is reordered after the ptr check, the waiter could deadlock: + // + // - T1: checks ptr == expect which is true + // - T2: updates ptr to != expect + // - T2: does Futex.wake(), sees no pending waiters, exits + // - T1: bumps pending waiters (was reordered after the ptr == expect check) + // - T1: goes to sleep and misses both the ptr change and T2's wake up + // + // SeqCst as Acquire barrier to ensure the announcement happens before the ptr check below. + // SeqCst as shared modification order to form a happens-before edge with the fence(.SeqCst)+load() in wake(). + var pending = bucket.pending.fetchAdd(1, .SeqCst); + assert(pending < std.math.maxInt(usize)); + + // If the wait gets cancelled, remove the pending count we previously added. + // This is done outside the mutex lock to keep the critical section short in case of contention. + var cancelled = false; + defer if (cancelled) { + pending = bucket.pending.fetchSub(1, .Monotonic); + assert(pending > 0); + }; + + var waiter: Waiter = undefined; + { + assert(std.c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS); + defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS); + + cancelled = ptr.load(.Monotonic) != expect; + if (cancelled) { + return; + } + + waiter.event.init(); + WaitQueue.insert(&bucket.treap, address, &waiter); + } + + defer { + assert(!waiter.is_queued); + waiter.event.deinit(); + } + + waiter.event.wait(timeout) catch { + // If we fail to cancel after a timeout, it means a wake() thread dequeued us and will wake us up. + // We must wait until the event is set as that's a signal that the wake() thread wont access the waiter memory anymore. + // If we return early without waiting, the waiter on the stack would be invalidated and the wake() thread risks a UAF. + defer if (!cancelled) waiter.event.wait(null) catch unreachable; + + assert(std.c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS); + defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS); + + cancelled = WaitQueue.tryRemove(&bucket.treap, address, &waiter); + if (cancelled) { + return error.Timeout; + } + }; + } + + fn wake(ptr: *const Atomic(u32), max_waiters: u32) void { + const address = Address.from(ptr); + const bucket = Bucket.from(address); + + // Quick check if there's even anything to wake up. + // The change to the ptr's value must happen before we check for pending waiters. + // If not, the wake() thread could miss a sleeping waiter and have it deadlock: + // + // - T2: p = has pending waiters (reordered before the ptr update) + // - T1: bump pending waiters + // - T1: if ptr == expected: sleep() + // - T2: update ptr != expected + // - T2: p is false from earlier so doesn't wake (T1 missed ptr update and T2 missed T1 sleeping) + // + // What we really want here is a Release load, but that doesn't exist under the C11 memory model. + // We could instead do `bucket.pending.fetchAdd(0, Release) == 0` which achieves effectively the same thing, + // but the RMW operation unconditionally stores which invalidates the cache-line for others causing unnecessary contention. + // + // Instead we opt to do a full-fence + load instead which avoids taking ownership of the cache-line. + // fence(SeqCst) effectively converts the ptr update to SeqCst and the pending load to SeqCst: creating a Store-Load barrier. + // + // The pending count increment in wait() must also now use SeqCst for the update + this pending load + // to be in the same modification order as our load isn't using Release/Acquire to guarantee it. + std.atomic.fence(.SeqCst); + if (bucket.pending.load(.Monotonic) == 0) { + return; + } + + // Keep a list of all the waiters notified and wake then up outside the mutex critical section. + var notified = WaitList{}; + defer if (notified.len > 0) { + const pending = bucket.pending.fetchSub(notified.len, .Monotonic); + assert(pending >= notified.len); + + while (notified.pop()) |waiter| { + assert(!waiter.is_queued); + waiter.event.set(); + } + }; + + assert(std.c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS); + defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS); + + // Another pending check again to avoid the WaitQueue lookup if not necessary. + if (bucket.pending.load(.Monotonic) > 0) { + notified = WaitQueue.remove(&bucket.treap, address, max_waiters); + } + } }; -test "Futex - wait/wake" { +test "Futex - smoke test" { var value = Atomic(u32).init(0); - Futex.wait(&value, 1, null) catch unreachable; - const wait_noop_result = Futex.wait(&value, 0, 0); - try testing.expectError(error.TimedOut, wait_noop_result); + // Try waits with invalid values. + Futex.wait(&value, 0xdeadbeef); + Futex.timedWait(&value, 0xdeadbeef, 0) catch {}; - const wait_longer_result = Futex.wait(&value, 0, std.time.ns_per_ms); - try testing.expectError(error.TimedOut, wait_longer_result); + // Try timeout waits. + try testing.expectError(error.Timeout, Futex.timedWait(&value, 0, 0)); + try testing.expectError(error.Timeout, Futex.timedWait(&value, 0, std.time.ns_per_ms)); + // Try wakes Futex.wake(&value, 0); Futex.wake(&value, 1); Futex.wake(&value, std.math.maxInt(u32)); } -test "Futex - Signal" { - if (single_threaded) { +test "Futex - signaling" { + // This test requires spawning threads + if (builtin.single_threaded) { return error.SkipZigTest; } + const num_threads = 4; + const num_iterations = 4; + const Paddle = struct { value: Atomic(u32) = Atomic(u32).init(0), current: u32 = 0, - fn run(self: *@This(), hit_to: *@This()) !void { - var iterations: usize = 4; - while (iterations > 0) : (iterations -= 1) { - var value: u32 = undefined; - while (true) { - value = self.value.load(.Acquire); - if (value != self.current) break; - Futex.wait(&self.value, self.current, null) catch unreachable; - } - - try testing.expectEqual(value, self.current + 1); - self.current = value; - - _ = hit_to.value.fetchAdd(1, .Release); - Futex.wake(&hit_to.value, 1); - } - } - }; - - var ping = Paddle{}; - var pong = Paddle{}; - - const t1 = try std.Thread.spawn(.{}, Paddle.run, .{ &ping, &pong }); - defer t1.join(); - - const t2 = try std.Thread.spawn(.{}, Paddle.run, .{ &pong, &ping }); - defer t2.join(); - - _ = ping.value.fetchAdd(1, .Release); - Futex.wake(&ping.value, 1); -} - -test "Futex - Broadcast" { - if (single_threaded) { - return error.SkipZigTest; - } - - const Context = struct { - threads: [4]std.Thread = undefined, - broadcast: Atomic(u32) = Atomic(u32).init(0), - notified: Atomic(usize) = Atomic(usize).init(0), - - const BROADCAST_EMPTY = 0; - const BROADCAST_SENT = 1; - const BROADCAST_RECEIVED = 2; - - fn runSender(self: *@This()) !void { - self.broadcast.store(BROADCAST_SENT, .Monotonic); - Futex.wake(&self.broadcast, @intCast(u32, self.threads.len)); - - while (true) { - const broadcast = self.broadcast.load(.Acquire); - if (broadcast == BROADCAST_RECEIVED) break; - try testing.expectEqual(broadcast, BROADCAST_SENT); - Futex.wait(&self.broadcast, broadcast, null) catch unreachable; - } - } - - fn runReceiver(self: *@This()) void { - while (true) { - const broadcast = self.broadcast.load(.Acquire); - if (broadcast == BROADCAST_SENT) break; - assert(broadcast == BROADCAST_EMPTY); - Futex.wait(&self.broadcast, broadcast, null) catch unreachable; - } - - const notified = self.notified.fetchAdd(1, .Monotonic); - if (notified + 1 == self.threads.len) { - self.broadcast.store(BROADCAST_RECEIVED, .Release); - Futex.wake(&self.broadcast, 1); - } - } - }; - - var ctx = Context{}; - for (ctx.threads) |*thread| - thread.* = try std.Thread.spawn(.{}, Context.runReceiver, .{&ctx}); - defer for (ctx.threads) |thread| - thread.join(); - - // Try to wait for the threads to start before running runSender(). - // NOTE: not actually needed for correctness. - std.time.sleep(16 * std.time.ns_per_ms); - try ctx.runSender(); - - const notified = ctx.notified.load(.Monotonic); - try testing.expectEqual(notified, ctx.threads.len); -} - -test "Futex - Chain" { - if (single_threaded) { - return error.SkipZigTest; - } - - const Signal = struct { - value: Atomic(u32) = Atomic(u32).init(0), - - fn wait(self: *@This()) void { - while (true) { - const value = self.value.load(.Acquire); - if (value == 1) break; - assert(value == 0); - Futex.wait(&self.value, 0, null) catch unreachable; - } - } - - fn notify(self: *@This()) void { - assert(self.value.load(.Unordered) == 0); - self.value.store(1, .Release); + fn hit(self: *@This()) void { + _ = self.value.fetchAdd(1, .Release); Futex.wake(&self.value, 1); } - }; - const Context = struct { - completed: Signal = .{}, - threads: [4]struct { - thread: std.Thread, - signal: Signal, - } = undefined, + fn run(self: *@This(), hit_to: *@This()) !void { + while (self.current < num_iterations) { + // Wait for the value to change from hit() + var new_value: u32 = undefined; + while (true) { + new_value = self.value.load(.Acquire); + if (new_value != self.current) break; + Futex.wait(&self.value, self.current); + } - fn run(self: *@This(), index: usize) void { - const this_signal = &self.threads[index].signal; + // change the internal "current" value + try testing.expectEqual(new_value, self.current + 1); + self.current = new_value; - var next_signal = &self.completed; - if (index + 1 < self.threads.len) { - next_signal = &self.threads[index + 1].signal; + // hit the next paddle + hit_to.hit(); } - - this_signal.wait(); - next_signal.notify(); } }; - var ctx = Context{}; - for (ctx.threads) |*entry, index| { - entry.signal = .{}; - entry.thread = try std.Thread.spawn(.{}, Context.run, .{ &ctx, index }); + var paddles = [_]Paddle{.{}} ** num_threads; + var threads = [_]std.Thread{undefined} ** num_threads; + + // Create a circle of paddles which hit each other + for (threads) |*t, i| { + const paddle = &paddles[i]; + const hit_to = &paddles[(i + 1) % paddles.len]; + t.* = try std.Thread.spawn(.{}, Paddle.run, .{ paddle, hit_to }); } - ctx.threads[0].signal.notify(); - ctx.completed.wait(); - - for (ctx.threads) |entry| { - entry.thread.join(); - } + // Hit the first paddle and wait for them all to complete by hitting each other for num_iterations. + paddles[0].hit(); + for (threads) |t| t.join(); + for (paddles) |p| try testing.expectEqual(p.current, num_iterations); +} + +test "Futex - broadcasting" { + // This test requires spawning threads + if (builtin.single_threaded) { + return error.SkipZigTest; + } + + const num_threads = 4; + const num_iterations = 4; + + const Barrier = struct { + count: Atomic(u32) = Atomic(u32).init(num_threads), + futex: Atomic(u32) = Atomic(u32).init(0), + + fn wait(self: *@This()) !void { + // Decrement the counter. + // Release ensures stuff before this barrier.wait() happens before the last one. + const count = self.count.fetchSub(1, .Release); + try testing.expect(count <= num_threads); + try testing.expect(count > 0); + + // First counter to reach zero wakes all other threads. + // Acquire for the last counter ensures stuff before previous barrier.wait()s happened before it. + // Release on futex update ensures stuff before all barrier.wait()'s happens before they all return. + if (count - 1 == 0) { + _ = self.count.load(.Acquire); // TODO: could be fence(Acquire) if not for TSAN + self.futex.store(1, .Release); + Futex.wake(&self.futex, num_threads - 1); + return; + } + + // Other threads wait until last counter wakes them up. + // Acquire on futex synchronizes with last barrier count to ensure stuff before all barrier.wait()'s happen before us. + while (self.futex.load(.Acquire) == 0) { + Futex.wait(&self.futex, 0); + } + } + }; + + const Broadcast = struct { + barriers: [num_iterations]Barrier = [_]Barrier{.{}} ** num_iterations, + threads: [num_threads]std.Thread = undefined, + + fn run(self: *@This()) !void { + for (self.barriers) |*barrier| { + try barrier.wait(); + } + } + }; + + var broadcast = Broadcast{}; + for (broadcast.threads) |*t| t.* = try std.Thread.spawn(.{}, Broadcast.run, .{&broadcast}); + for (broadcast.threads) |t| t.join(); } diff --git a/lib/std/atomic.zig b/lib/std/atomic.zig index b1b5789b02..ef1cce1774 100644 --- a/lib/std/atomic.zig +++ b/lib/std/atomic.zig @@ -1,5 +1,5 @@ const std = @import("std.zig"); -const target = @import("builtin").target; +const builtin = @import("builtin"); pub const Ordering = std.builtin.AtomicOrder; @@ -40,7 +40,7 @@ test "fence/compilerFence" { /// Signals to the processor that the caller is inside a busy-wait spin-loop. pub inline fn spinLoopHint() void { - switch (target.cpu.arch) { + switch (builtin.target.cpu.arch) { // No-op instruction that can hint to save (or share with a hardware-thread) // pipelining/power resources // https://software.intel.com/content/www/us/en/develop/articles/benefitting-power-and-performance-sleep-loops.html @@ -59,7 +59,7 @@ pub inline fn spinLoopHint() void { // `yield` was introduced in v6k but is also available on v6m. // https://www.keil.com/support/man/docs/armasm/armasm_dom1361289926796.htm .arm, .armeb, .thumb, .thumbeb => { - const can_yield = comptime std.Target.arm.featureSetHasAny(target.cpu.features, .{ + const can_yield = comptime std.Target.arm.featureSetHasAny(builtin.target.cpu.features, .{ .has_v6k, .has_v6m, }); if (can_yield) { @@ -80,3 +80,41 @@ test "spinLoopHint" { spinLoopHint(); } } + +/// The estimated size of the CPU's cache line when atomically updating memory. +/// Add this much padding or align to this boundary to avoid atomically-updated +/// memory from forcing cache invalidations on near, but non-atomic, memory. +/// +// https://en.wikipedia.org/wiki/False_sharing +// https://github.com/golang/go/search?q=CacheLinePadSize +pub const cache_line = switch (builtin.cpu.arch) { + // x86_64: Starting from Intel's Sandy Bridge, the spatial prefetcher pulls in pairs of 64-byte cache lines at a time. + // - https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf + // - https://github.com/facebook/folly/blob/1b5288e6eea6df074758f877c849b6e73bbb9fbb/folly/lang/Align.h#L107 + // + // aarch64: Some big.LITTLE ARM archs have "big" cores with 128-byte cache lines: + // - https://www.mono-project.com/news/2016/09/12/arm64-icache/ + // - https://cpufun.substack.com/p/more-m1-fun-hardware-information + // + // powerpc64: PPC has 128-byte cache lines + // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_ppc64x.go#L9 + .x86_64, .aarch64, .powerpc64 => 128, + + // These platforms reportedly have 32-byte cache lines + // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_arm.go#L7 + // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips.go#L7 + // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mipsle.go#L7 + // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips64x.go#L9 + // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_riscv64.go#L7 + .arm, .mips, .mips64, .riscv64 => 32, + + // This platform reportedly has 256-byte cache lines + // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_s390x.go#L7 + .s390x => 256, + + // Other x86 and WASM platforms have 64-byte cache lines. + // The rest of the architectures are assumed to be similar. + // - https://github.com/golang/go/blob/dda2991c2ea0c5914714469c4defc2562a907230/src/internal/cpu/cpu_x86.go#L9 + // - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_wasm.go#L7 + else => 64, +}; diff --git a/lib/std/c/dragonfly.zig b/lib/std/c/dragonfly.zig index 186525a9ce..2e388d4c80 100644 --- a/lib/std/c/dragonfly.zig +++ b/lib/std/c/dragonfly.zig @@ -36,6 +36,9 @@ pub const sem_t = ?*opaque {}; pub extern "c" fn pthread_setname_np(thread: std.c.pthread_t, name: [*:0]const u8) E; pub extern "c" fn pthread_getname_np(thread: std.c.pthread_t, name: [*:0]u8, len: usize) E; +pub extern "c" fn umtx_sleep(ptr: *const volatile c_int, value: c_int, timeout: c_int) c_int; +pub extern "c" fn umtx_wakeup(ptr: *const volatile c_int, count: c_int) c_int; + // See: // - https://gitweb.dragonflybsd.org/dragonfly.git/blob/HEAD:/include/unistd.h // - https://gitweb.dragonflybsd.org/dragonfly.git/blob/HEAD:/sys/sys/types.h diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index 8972b6d6dc..e40d7acd8d 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -62,6 +62,46 @@ pub const sem_t = extern struct { _padding: u32, }; +// https://github.com/freebsd/freebsd-src/blob/main/sys/sys/umtx.h +pub const UMTX_OP = enum(c_int) { + LOCK = 0, + UNLOCK = 1, + WAIT = 2, + WAKE = 3, + MUTEX_TRYLOCK = 4, + MUTEX_LOCK = 5, + MUTEX_UNLOCK = 6, + SET_CEILING = 7, + CV_WAIT = 8, + CV_SIGNAL = 9, + CV_BROADCAST = 10, + WAIT_UINT = 11, + RW_RDLOCK = 12, + RW_WRLOCK = 13, + RW_UNLOCK = 14, + WAIT_UINT_PRIVATE = 15, + WAKE_PRIVATE = 16, + MUTEX_WAIT = 17, + MUTEX_WAKE = 18, // deprecated + SEM_WAIT = 19, // deprecated + SEM_WAKE = 20, // deprecated + NWAKE_PRIVATE = 31, + MUTEX_WAKE2 = 22, + SEM2_WAIT = 23, + SEM2_WAKE = 24, + SHM = 25, + ROBUST_LISTS = 26, +}; + +pub const UMTX_ABSTIME = 0x01; +pub const _umtx_time = extern struct { + _timeout: timespec, + _flags: u32, + _clockid: u32, +}; + +pub extern "c" fn _umtx_op(obj: usize, op: c_int, val: c_ulong, uaddr: usize, uaddr2: usize) c_int; + pub const EAI = enum(c_int) { /// address family for hostname not supported ADDRFAMILY = 1, diff --git a/lib/std/c/openbsd.zig b/lib/std/c/openbsd.zig index 56807c7d68..0aa90c741a 100644 --- a/lib/std/c/openbsd.zig +++ b/lib/std/c/openbsd.zig @@ -45,6 +45,13 @@ pub extern "c" fn unveil(path: ?[*:0]const u8, permissions: ?[*:0]const u8) c_in pub extern "c" fn pthread_set_name_np(thread: std.c.pthread_t, name: [*:0]const u8) void; pub extern "c" fn pthread_get_name_np(thread: std.c.pthread_t, name: [*:0]u8, len: usize) void; +// https://github.com/openbsd/src/blob/2207c4325726fdc5c4bcd0011af0fdf7d3dab137/sys/sys/futex.h +pub const FUTEX_WAIT = 1; +pub const FUTEX_WAKE = 2; +pub const FUTEX_REQUEUE = 3; +pub const FUTEX_PRIVATE_FLAG = 128; +pub extern "c" fn futex(uaddr: ?*const volatile u32, op: c_int, val: c_int, timeout: ?*const timespec, uaddr2: ?*const volatile u32) c_int; + pub const login_cap_t = extern struct { lc_class: ?[*:0]const u8, lc_cap: ?[*:0]const u8, From 9e4cd1f4e6c3195665daa80c6a438cfd4daa92b1 Mon Sep 17 00:00:00 2001 From: Rabin Gaire Date: Wed, 20 Apr 2022 18:02:41 +0545 Subject: [PATCH 1204/2031] fix child process spawn on macos hangs issue When a child process with stdin, stdout behavior set to pipe is ran on macos it used to hang which has been fixed. Issue existed because we forgot to call `posix_spawn_file_actions_addclose` syscall on user exposed file descriptor which resulted on file descriptor not closing properly. --- lib/std/child_process.zig | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 8171ff7eea..aef96cbde3 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -593,9 +593,9 @@ pub const ChildProcess = struct { var actions = try os.posix_spawn.Actions.init(); defer actions.deinit(); - try setUpChildIoPosixSpawn(self.stdin_behavior, &actions, stdin_pipe[0], os.STDIN_FILENO, dev_null_fd); - try setUpChildIoPosixSpawn(self.stdout_behavior, &actions, stdout_pipe[1], os.STDOUT_FILENO, dev_null_fd); - try setUpChildIoPosixSpawn(self.stderr_behavior, &actions, stderr_pipe[1], os.STDERR_FILENO, dev_null_fd); + try setUpChildIoPosixSpawn(self.stdin_behavior, &actions, stdin_pipe, os.STDIN_FILENO, dev_null_fd); + try setUpChildIoPosixSpawn(self.stdout_behavior, &actions, stdout_pipe, os.STDOUT_FILENO, dev_null_fd); + try setUpChildIoPosixSpawn(self.stderr_behavior, &actions, stderr_pipe, os.STDERR_FILENO, dev_null_fd); if (self.cwd_dir) |cwd| { try actions.fchdir(cwd.fd); @@ -650,12 +650,16 @@ pub const ChildProcess = struct { fn setUpChildIoPosixSpawn( stdio: StdIo, actions: *os.posix_spawn.Actions, - pipe_fd: i32, + pipe_fd: [2]i32, std_fileno: i32, dev_null_fd: i32, ) !void { switch (stdio) { - .Pipe => try actions.dup2(pipe_fd, std_fileno), + .Pipe => { + const idx: usize = if (std_fileno == 0) 0 else 1; + try actions.dup2(pipe_fd[idx], std_fileno); + try actions.close(pipe_fd[1-idx]); + }, .Close => try actions.close(std_fileno), .Inherit => {}, .Ignore => try actions.dup2(dev_null_fd, std_fileno), From 711bf55eaa643c3d05640bebbf3e4315477b8ed8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Apr 2022 07:49:23 -0700 Subject: [PATCH 1205/2031] std: bring back SegmentedList I want to use it for the self-hosted compiler. --- lib/std/segmented_list.zig | 464 +++++++++++++++++++++++++++++++++++++ lib/std/std.zig | 1 + 2 files changed, 465 insertions(+) create mode 100644 lib/std/segmented_list.zig diff --git a/lib/std/segmented_list.zig b/lib/std/segmented_list.zig new file mode 100644 index 0000000000..ae3697904d --- /dev/null +++ b/lib/std/segmented_list.zig @@ -0,0 +1,464 @@ +const std = @import("std.zig"); +const assert = std.debug.assert; +const testing = std.testing; +const Allocator = std.mem.Allocator; + +// Imagine that `fn at(self: *Self, index: usize) &T` is a customer asking for a box +// from a warehouse, based on a flat array, boxes ordered from 0 to N - 1. +// But the warehouse actually stores boxes in shelves of increasing powers of 2 sizes. +// So when the customer requests a box index, we have to translate it to shelf index +// and box index within that shelf. Illustration: +// +// customer indexes: +// shelf 0: 0 +// shelf 1: 1 2 +// shelf 2: 3 4 5 6 +// shelf 3: 7 8 9 10 11 12 13 14 +// shelf 4: 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 +// shelf 5: 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 +// ... +// +// warehouse indexes: +// shelf 0: 0 +// shelf 1: 0 1 +// shelf 2: 0 1 2 3 +// shelf 3: 0 1 2 3 4 5 6 7 +// shelf 4: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +// shelf 5: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 +// ... +// +// With this arrangement, here are the equations to get the shelf index and +// box index based on customer box index: +// +// shelf_index = floor(log2(customer_index + 1)) +// shelf_count = ceil(log2(box_count + 1)) +// box_index = customer_index + 1 - 2 ** shelf +// shelf_size = 2 ** shelf_index +// +// Now we complicate it a little bit further by adding a preallocated shelf, which must be +// a power of 2: +// prealloc=4 +// +// customer indexes: +// prealloc: 0 1 2 3 +// shelf 0: 4 5 6 7 8 9 10 11 +// shelf 1: 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 +// shelf 2: 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 +// ... +// +// warehouse indexes: +// prealloc: 0 1 2 3 +// shelf 0: 0 1 2 3 4 5 6 7 +// shelf 1: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +// shelf 2: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 +// ... +// +// Now the equations are: +// +// shelf_index = floor(log2(customer_index + prealloc)) - log2(prealloc) - 1 +// shelf_count = ceil(log2(box_count + prealloc)) - log2(prealloc) - 1 +// box_index = customer_index + prealloc - 2 ** (log2(prealloc) + 1 + shelf) +// shelf_size = prealloc * 2 ** (shelf_index + 1) + +/// This is a stack data structure where pointers to indexes have the same lifetime as the data structure +/// itself, unlike ArrayList where push() invalidates all existing element pointers. +/// The tradeoff is that elements are not guaranteed to be contiguous. For that, use ArrayList. +/// Note however that most elements are contiguous, making this data structure cache-friendly. +/// +/// Because it never has to copy elements from an old location to a new location, it does not require +/// its elements to be copyable, and it avoids wasting memory when backed by an ArenaAllocator. +/// Note that the push() and pop() convenience methods perform a copy, but you can instead use +/// addOne(), at(), setCapacity(), and shrinkCapacity() to avoid copying items. +/// +/// This data structure has O(1) push and O(1) pop. +/// +/// It supports preallocated elements, making it especially well suited when the expected maximum +/// size is small. `prealloc_item_count` must be 0, or a power of 2. +pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type { + return struct { + const Self = @This(); + const ShelfIndex = std.math.Log2Int(usize); + + const prealloc_exp: ShelfIndex = blk: { + // we don't use the prealloc_exp constant when prealloc_item_count is 0 + // but lazy-init may still be triggered by other code so supply a value + if (prealloc_item_count == 0) { + break :blk 0; + } else { + assert(std.math.isPowerOfTwo(prealloc_item_count)); + const value = std.math.log2_int(usize, prealloc_item_count); + break :blk value; + } + }; + + prealloc_segment: [prealloc_item_count]T, + dynamic_segments: [][*]T, + allocator: Allocator, + len: usize, + + pub const prealloc_count = prealloc_item_count; + + fn AtType(comptime SelfType: type) type { + if (@typeInfo(SelfType).Pointer.is_const) { + return *const T; + } else { + return *T; + } + } + + /// Deinitialize with `deinit` + pub fn init(allocator: Allocator) Self { + return Self{ + .allocator = allocator, + .len = 0, + .prealloc_segment = undefined, + .dynamic_segments = &[_][*]T{}, + }; + } + + pub fn deinit(self: *Self) void { + self.freeShelves(@intCast(ShelfIndex, self.dynamic_segments.len), 0); + self.allocator.free(self.dynamic_segments); + self.* = undefined; + } + + pub fn at(self: anytype, i: usize) AtType(@TypeOf(self)) { + assert(i < self.len); + return self.uncheckedAt(i); + } + + pub fn count(self: Self) usize { + return self.len; + } + + pub fn push(self: *Self, item: T) !void { + const new_item_ptr = try self.addOne(); + new_item_ptr.* = item; + } + + pub fn pushMany(self: *Self, items: []const T) !void { + for (items) |item| { + try self.push(item); + } + } + + pub fn pop(self: *Self) ?T { + if (self.len == 0) return null; + + const index = self.len - 1; + const result = uncheckedAt(self, index).*; + self.len = index; + return result; + } + + pub fn addOne(self: *Self) !*T { + const new_length = self.len + 1; + try self.growCapacity(new_length); + const result = uncheckedAt(self, self.len); + self.len = new_length; + return result; + } + + /// Grows or shrinks capacity to match usage. + pub fn setCapacity(self: *Self, new_capacity: usize) !void { + if (prealloc_item_count != 0) { + if (new_capacity <= @as(usize, 1) << (prealloc_exp + @intCast(ShelfIndex, self.dynamic_segments.len))) { + return self.shrinkCapacity(new_capacity); + } + } + return self.growCapacity(new_capacity); + } + + /// Only grows capacity, or retains current capacity + pub fn growCapacity(self: *Self, new_capacity: usize) !void { + const new_cap_shelf_count = shelfCount(new_capacity); + const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len); + if (new_cap_shelf_count > old_shelf_count) { + self.dynamic_segments = try self.allocator.realloc(self.dynamic_segments, new_cap_shelf_count); + var i = old_shelf_count; + errdefer { + self.freeShelves(i, old_shelf_count); + self.dynamic_segments = self.allocator.shrink(self.dynamic_segments, old_shelf_count); + } + while (i < new_cap_shelf_count) : (i += 1) { + self.dynamic_segments[i] = (try self.allocator.alloc(T, shelfSize(i))).ptr; + } + } + } + + /// Only shrinks capacity or retains current capacity + pub fn shrinkCapacity(self: *Self, new_capacity: usize) void { + if (new_capacity <= prealloc_item_count) { + const len = @intCast(ShelfIndex, self.dynamic_segments.len); + self.freeShelves(len, 0); + self.allocator.free(self.dynamic_segments); + self.dynamic_segments = &[_][*]T{}; + return; + } + + const new_cap_shelf_count = shelfCount(new_capacity); + const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len); + assert(new_cap_shelf_count <= old_shelf_count); + if (new_cap_shelf_count == old_shelf_count) { + return; + } + + self.freeShelves(old_shelf_count, new_cap_shelf_count); + self.dynamic_segments = self.allocator.shrink(self.dynamic_segments, new_cap_shelf_count); + } + + pub fn shrink(self: *Self, new_len: usize) void { + assert(new_len <= self.len); + // TODO take advantage of the new realloc semantics + self.len = new_len; + } + + pub fn writeToSlice(self: *Self, dest: []T, start: usize) void { + const end = start + dest.len; + assert(end <= self.len); + + var i = start; + if (end <= prealloc_item_count) { + std.mem.copy(T, dest[i - start ..], self.prealloc_segment[i..end]); + return; + } else if (i < prealloc_item_count) { + std.mem.copy(T, dest[i - start ..], self.prealloc_segment[i..]); + i = prealloc_item_count; + } + + while (i < end) { + const shelf_index = shelfIndex(i); + const copy_start = boxIndex(i, shelf_index); + const copy_end = std.math.min(shelfSize(shelf_index), copy_start + end - i); + + std.mem.copy( + T, + dest[i - start ..], + self.dynamic_segments[shelf_index][copy_start..copy_end], + ); + + i += (copy_end - copy_start); + } + } + + pub fn uncheckedAt(self: anytype, index: usize) AtType(@TypeOf(self)) { + if (index < prealloc_item_count) { + return &self.prealloc_segment[index]; + } + const shelf_index = shelfIndex(index); + const box_index = boxIndex(index, shelf_index); + return &self.dynamic_segments[shelf_index][box_index]; + } + + fn shelfCount(box_count: usize) ShelfIndex { + if (prealloc_item_count == 0) { + return log2_int_ceil(usize, box_count + 1); + } + return log2_int_ceil(usize, box_count + prealloc_item_count) - prealloc_exp - 1; + } + + fn shelfSize(shelf_index: ShelfIndex) usize { + if (prealloc_item_count == 0) { + return @as(usize, 1) << shelf_index; + } + return @as(usize, 1) << (shelf_index + (prealloc_exp + 1)); + } + + fn shelfIndex(list_index: usize) ShelfIndex { + if (prealloc_item_count == 0) { + return std.math.log2_int(usize, list_index + 1); + } + return std.math.log2_int(usize, list_index + prealloc_item_count) - prealloc_exp - 1; + } + + fn boxIndex(list_index: usize, shelf_index: ShelfIndex) usize { + if (prealloc_item_count == 0) { + return (list_index + 1) - (@as(usize, 1) << shelf_index); + } + return list_index + prealloc_item_count - (@as(usize, 1) << ((prealloc_exp + 1) + shelf_index)); + } + + fn freeShelves(self: *Self, from_count: ShelfIndex, to_count: ShelfIndex) void { + var i = from_count; + while (i != to_count) { + i -= 1; + self.allocator.free(self.dynamic_segments[i][0..shelfSize(i)]); + } + } + + pub const Iterator = struct { + list: *Self, + index: usize, + box_index: usize, + shelf_index: ShelfIndex, + shelf_size: usize, + + pub fn next(it: *Iterator) ?*T { + if (it.index >= it.list.len) return null; + if (it.index < prealloc_item_count) { + const ptr = &it.list.prealloc_segment[it.index]; + it.index += 1; + if (it.index == prealloc_item_count) { + it.box_index = 0; + it.shelf_index = 0; + it.shelf_size = prealloc_item_count * 2; + } + return ptr; + } + + const ptr = &it.list.dynamic_segments[it.shelf_index][it.box_index]; + it.index += 1; + it.box_index += 1; + if (it.box_index == it.shelf_size) { + it.shelf_index += 1; + it.box_index = 0; + it.shelf_size *= 2; + } + return ptr; + } + + pub fn prev(it: *Iterator) ?*T { + if (it.index == 0) return null; + + it.index -= 1; + if (it.index < prealloc_item_count) return &it.list.prealloc_segment[it.index]; + + if (it.box_index == 0) { + it.shelf_index -= 1; + it.shelf_size /= 2; + it.box_index = it.shelf_size - 1; + } else { + it.box_index -= 1; + } + + return &it.list.dynamic_segments[it.shelf_index][it.box_index]; + } + + pub fn peek(it: *Iterator) ?*T { + if (it.index >= it.list.len) + return null; + if (it.index < prealloc_item_count) + return &it.list.prealloc_segment[it.index]; + + return &it.list.dynamic_segments[it.shelf_index][it.box_index]; + } + + pub fn set(it: *Iterator, index: usize) void { + it.index = index; + if (index < prealloc_item_count) return; + it.shelf_index = shelfIndex(index); + it.box_index = boxIndex(index, it.shelf_index); + it.shelf_size = shelfSize(it.shelf_index); + } + }; + + pub fn iterator(self: *Self, start_index: usize) Iterator { + var it = Iterator{ + .list = self, + .index = undefined, + .shelf_index = undefined, + .box_index = undefined, + .shelf_size = undefined, + }; + it.set(start_index); + return it; + } + }; +} + +test "basic usage" { + const a = std.testing.allocator; + + try testSegmentedList(0, a); + try testSegmentedList(1, a); + try testSegmentedList(2, a); + try testSegmentedList(4, a); + try testSegmentedList(8, a); + try testSegmentedList(16, a); +} + +fn testSegmentedList(comptime prealloc: usize, allocator: Allocator) !void { + var list = SegmentedList(i32, prealloc).init(allocator); + defer list.deinit(); + + { + var i: usize = 0; + while (i < 100) : (i += 1) { + try list.push(@intCast(i32, i + 1)); + try testing.expect(list.len == i + 1); + } + } + + { + var i: usize = 0; + while (i < 100) : (i += 1) { + try testing.expect(list.at(i).* == @intCast(i32, i + 1)); + } + } + + { + var it = list.iterator(0); + var x: i32 = 0; + while (it.next()) |item| { + x += 1; + try testing.expect(item.* == x); + } + try testing.expect(x == 100); + while (it.prev()) |item| : (x -= 1) { + try testing.expect(item.* == x); + } + try testing.expect(x == 0); + } + + try testing.expect(list.pop().? == 100); + try testing.expect(list.len == 99); + + try list.pushMany(&[_]i32{ 1, 2, 3 }); + try testing.expect(list.len == 102); + try testing.expect(list.pop().? == 3); + try testing.expect(list.pop().? == 2); + try testing.expect(list.pop().? == 1); + try testing.expect(list.len == 99); + + try list.pushMany(&[_]i32{}); + try testing.expect(list.len == 99); + + { + var i: i32 = 99; + while (list.pop()) |item| : (i -= 1) { + try testing.expect(item == i); + list.shrinkCapacity(list.len); + } + } + + { + var control: [100]i32 = undefined; + var dest: [100]i32 = undefined; + + var i: i32 = 0; + while (i < 100) : (i += 1) { + try list.push(i + 1); + control[@intCast(usize, i)] = i + 1; + } + + std.mem.set(i32, dest[0..], 0); + list.writeToSlice(dest[0..], 0); + try testing.expect(std.mem.eql(i32, control[0..], dest[0..])); + + std.mem.set(i32, dest[0..], 0); + list.writeToSlice(dest[50..], 50); + try testing.expect(std.mem.eql(i32, control[50..], dest[50..])); + } + + try list.setCapacity(0); +} + +/// TODO look into why this std.math function was changed in +/// fc9430f56798a53f9393a697f4ccd6bf9981b970. +fn log2_int_ceil(comptime T: type, x: T) std.math.Log2Int(T) { + assert(x != 0); + const log2_val = std.math.log2_int(T, x); + if (@as(T, 1) << log2_val == x) + return log2_val; + return log2_val + 1; +} diff --git a/lib/std/std.zig b/lib/std/std.zig index 831a82cbbe..32e16b40c2 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -29,6 +29,7 @@ pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceE pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue; pub const PriorityDequeue = @import("priority_dequeue.zig").PriorityDequeue; pub const Progress = @import("Progress.zig"); +pub const SegmentedList = @import("segmented_list.zig").SegmentedList; pub const SemanticVersion = @import("SemanticVersion.zig"); pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; pub const StaticBitSet = bit_set.StaticBitSet; From 99112b63bdeec512e164f289556b24fa1554a775 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Apr 2022 08:28:43 -0700 Subject: [PATCH 1206/2031] std.SegmentedList: breaking API changes * Remove the Allocator field; instead it must be passed in as a parameter to any function that needs it. * Rename `push` to `append` and `pushMany` to `appendSlice` to match the conventions set by ArrayList. --- lib/std/segmented_list.zig | 106 +++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 58 deletions(-) diff --git a/lib/std/segmented_list.zig b/lib/std/segmented_list.zig index ae3697904d..8bdfe7dceb 100644 --- a/lib/std/segmented_list.zig +++ b/lib/std/segmented_list.zig @@ -61,16 +61,16 @@ const Allocator = std.mem.Allocator; // shelf_size = prealloc * 2 ** (shelf_index + 1) /// This is a stack data structure where pointers to indexes have the same lifetime as the data structure -/// itself, unlike ArrayList where push() invalidates all existing element pointers. +/// itself, unlike ArrayList where append() invalidates all existing element pointers. /// The tradeoff is that elements are not guaranteed to be contiguous. For that, use ArrayList. /// Note however that most elements are contiguous, making this data structure cache-friendly. /// /// Because it never has to copy elements from an old location to a new location, it does not require /// its elements to be copyable, and it avoids wasting memory when backed by an ArenaAllocator. -/// Note that the push() and pop() convenience methods perform a copy, but you can instead use +/// Note that the append() and pop() convenience methods perform a copy, but you can instead use /// addOne(), at(), setCapacity(), and shrinkCapacity() to avoid copying items. /// -/// This data structure has O(1) push and O(1) pop. +/// This data structure has O(1) append and O(1) pop. /// /// It supports preallocated elements, making it especially well suited when the expected maximum /// size is small. `prealloc_item_count` must be 0, or a power of 2. @@ -91,10 +91,9 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } }; - prealloc_segment: [prealloc_item_count]T, - dynamic_segments: [][*]T, - allocator: Allocator, - len: usize, + prealloc_segment: [prealloc_item_count]T = undefined, + dynamic_segments: [][*]T = &[_][*]T{}, + len: usize = 0, pub const prealloc_count = prealloc_item_count; @@ -106,19 +105,9 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } } - /// Deinitialize with `deinit` - pub fn init(allocator: Allocator) Self { - return Self{ - .allocator = allocator, - .len = 0, - .prealloc_segment = undefined, - .dynamic_segments = &[_][*]T{}, - }; - } - - pub fn deinit(self: *Self) void { - self.freeShelves(@intCast(ShelfIndex, self.dynamic_segments.len), 0); - self.allocator.free(self.dynamic_segments); + pub fn deinit(self: *Self, allocator: Allocator) void { + self.freeShelves(allocator, @intCast(ShelfIndex, self.dynamic_segments.len), 0); + allocator.free(self.dynamic_segments); self.* = undefined; } @@ -131,14 +120,14 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type return self.len; } - pub fn push(self: *Self, item: T) !void { - const new_item_ptr = try self.addOne(); + pub fn append(self: *Self, allocator: Allocator, item: T) Allocator.Error!void { + const new_item_ptr = try self.addOne(allocator); new_item_ptr.* = item; } - pub fn pushMany(self: *Self, items: []const T) !void { + pub fn appendSlice(self: *Self, allocator: Allocator, items: []const T) Allocator.Error!void { for (items) |item| { - try self.push(item); + try self.append(allocator, item); } } @@ -151,47 +140,48 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type return result; } - pub fn addOne(self: *Self) !*T { + pub fn addOne(self: *Self, allocator: Allocator) Allocator.Error!*T { const new_length = self.len + 1; - try self.growCapacity(new_length); + try self.growCapacity(allocator, new_length); const result = uncheckedAt(self, self.len); self.len = new_length; return result; } /// Grows or shrinks capacity to match usage. - pub fn setCapacity(self: *Self, new_capacity: usize) !void { + /// TODO update this and related methods to match the conventions set by ArrayList + pub fn setCapacity(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void { if (prealloc_item_count != 0) { if (new_capacity <= @as(usize, 1) << (prealloc_exp + @intCast(ShelfIndex, self.dynamic_segments.len))) { - return self.shrinkCapacity(new_capacity); + return self.shrinkCapacity(allocator, new_capacity); } } - return self.growCapacity(new_capacity); + return self.growCapacity(allocator, new_capacity); } /// Only grows capacity, or retains current capacity - pub fn growCapacity(self: *Self, new_capacity: usize) !void { + pub fn growCapacity(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void { const new_cap_shelf_count = shelfCount(new_capacity); const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len); if (new_cap_shelf_count > old_shelf_count) { - self.dynamic_segments = try self.allocator.realloc(self.dynamic_segments, new_cap_shelf_count); + self.dynamic_segments = try allocator.realloc(self.dynamic_segments, new_cap_shelf_count); var i = old_shelf_count; errdefer { - self.freeShelves(i, old_shelf_count); - self.dynamic_segments = self.allocator.shrink(self.dynamic_segments, old_shelf_count); + self.freeShelves(allocator, i, old_shelf_count); + self.dynamic_segments = allocator.shrink(self.dynamic_segments, old_shelf_count); } while (i < new_cap_shelf_count) : (i += 1) { - self.dynamic_segments[i] = (try self.allocator.alloc(T, shelfSize(i))).ptr; + self.dynamic_segments[i] = (try allocator.alloc(T, shelfSize(i))).ptr; } } } /// Only shrinks capacity or retains current capacity - pub fn shrinkCapacity(self: *Self, new_capacity: usize) void { + pub fn shrinkCapacity(self: *Self, allocator: Allocator, new_capacity: usize) void { if (new_capacity <= prealloc_item_count) { const len = @intCast(ShelfIndex, self.dynamic_segments.len); - self.freeShelves(len, 0); - self.allocator.free(self.dynamic_segments); + self.freeShelves(allocator, len, 0); + allocator.free(self.dynamic_segments); self.dynamic_segments = &[_][*]T{}; return; } @@ -203,8 +193,8 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type return; } - self.freeShelves(old_shelf_count, new_cap_shelf_count); - self.dynamic_segments = self.allocator.shrink(self.dynamic_segments, new_cap_shelf_count); + self.freeShelves(allocator, old_shelf_count, new_cap_shelf_count); + self.dynamic_segments = allocator.shrink(self.dynamic_segments, new_cap_shelf_count); } pub fn shrink(self: *Self, new_len: usize) void { @@ -278,11 +268,11 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type return list_index + prealloc_item_count - (@as(usize, 1) << ((prealloc_exp + 1) + shelf_index)); } - fn freeShelves(self: *Self, from_count: ShelfIndex, to_count: ShelfIndex) void { + fn freeShelves(self: *Self, allocator: Allocator, from_count: ShelfIndex, to_count: ShelfIndex) void { var i = from_count; while (i != to_count) { i -= 1; - self.allocator.free(self.dynamic_segments[i][0..shelfSize(i)]); + allocator.free(self.dynamic_segments[i][0..shelfSize(i)]); } } @@ -367,24 +357,24 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } test "basic usage" { - const a = std.testing.allocator; - - try testSegmentedList(0, a); - try testSegmentedList(1, a); - try testSegmentedList(2, a); - try testSegmentedList(4, a); - try testSegmentedList(8, a); - try testSegmentedList(16, a); + try testSegmentedList(0); + try testSegmentedList(1); + try testSegmentedList(2); + try testSegmentedList(4); + try testSegmentedList(8); + try testSegmentedList(16); } -fn testSegmentedList(comptime prealloc: usize, allocator: Allocator) !void { - var list = SegmentedList(i32, prealloc).init(allocator); - defer list.deinit(); +fn testSegmentedList(comptime prealloc: usize) !void { + const gpa = std.testing.allocator; + + var list: SegmentedList(i32, prealloc) = .{}; + defer list.deinit(gpa); { var i: usize = 0; while (i < 100) : (i += 1) { - try list.push(@intCast(i32, i + 1)); + try list.append(gpa, @intCast(i32, i + 1)); try testing.expect(list.len == i + 1); } } @@ -413,21 +403,21 @@ fn testSegmentedList(comptime prealloc: usize, allocator: Allocator) !void { try testing.expect(list.pop().? == 100); try testing.expect(list.len == 99); - try list.pushMany(&[_]i32{ 1, 2, 3 }); + try list.appendSlice(gpa, &[_]i32{ 1, 2, 3 }); try testing.expect(list.len == 102); try testing.expect(list.pop().? == 3); try testing.expect(list.pop().? == 2); try testing.expect(list.pop().? == 1); try testing.expect(list.len == 99); - try list.pushMany(&[_]i32{}); + try list.appendSlice(gpa, &[_]i32{}); try testing.expect(list.len == 99); { var i: i32 = 99; while (list.pop()) |item| : (i -= 1) { try testing.expect(item == i); - list.shrinkCapacity(list.len); + list.shrinkCapacity(gpa, list.len); } } @@ -437,7 +427,7 @@ fn testSegmentedList(comptime prealloc: usize, allocator: Allocator) !void { var i: i32 = 0; while (i < 100) : (i += 1) { - try list.push(i + 1); + try list.append(gpa, i + 1); control[@intCast(usize, i)] = i + 1; } @@ -450,7 +440,7 @@ fn testSegmentedList(comptime prealloc: usize, allocator: Allocator) !void { try testing.expect(std.mem.eql(i32, control[50..], dest[50..])); } - try list.setCapacity(0); + try list.setCapacity(gpa, 0); } /// TODO look into why this std.math function was changed in From d976456ef665bf0aba3a83a8e7fccb4a92b2d3b2 Mon Sep 17 00:00:00 2001 From: Rabin Gaire Date: Wed, 20 Apr 2022 18:37:10 +0545 Subject: [PATCH 1207/2031] add test case for child_process spawn logic tests creating a child process with stdin/stdout behavior set to StdIo.Pipe. --- lib/std/child_process.zig | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index aef96cbde3..41d2cf2850 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -1379,3 +1379,49 @@ test "build and call child_process" { const ret_val = try child_proc.spawnAndWait(); try testing.expectEqual(ret_val, .{ .Exited = 0 }); } + +test "creating a child process with stdin/stdout behavior set to StdIo.Pipe" { + if (builtin.os.tag == .wasi) return error.SkipZigTest; + const testing = std.testing; + const allocator = testing.allocator; + + var child_process = try std.ChildProcess.init( + &[_][]const u8{ testing.zig_exe_path, "fmt", "--stdin" }, + allocator, + ); + defer child_process.deinit(); + child_process.stdin_behavior = .Pipe; + child_process.stdout_behavior = .Pipe; + + try child_process.spawn(); + + const input_program = + \\ const std = @import("std"); + \\ pub fn main() void { + \\ std.debug.print("Hello World", .{}); + \\ } + ; + + try child_process.stdin.?.writer().writeAll(input_program); + child_process.stdin.?.close(); + child_process.stdin = null; + + const out_bytes = try child_process.stdout.?.reader().readAllAlloc(allocator, std.math.maxInt(usize)); + defer allocator.free(out_bytes); + + switch (try child_process.wait()) { + .Exited => |code| if (code == 0) { + const expected_program = + \\const std = @import("std"); + \\pub fn main() void { + \\ std.debug.print("Hello World", .{}); + \\} + \\ + ; + try testing.expectEqualStrings(expected_program, out_bytes); + }, + else => { + try testing.expect(false); + } + } +} From 26153ce73a1b9c49bdf89055b8ab7f4d3173f153 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 21 Apr 2022 00:06:52 +0200 Subject: [PATCH 1208/2031] dwarf: clean up allocations in std.dwarf module While this code probably could do with some love and a redesign, this commit fixes the allocations by making sure we explicitly pass an allocator where required, and we use arenas for temporary or narrowly-scoped objects such as a `Die` (for `Die` in particular, not every `FormValue` will be allocated - we could duplicate, or we can use an arena which is the proposal of this commit). --- lib/std/debug.zig | 21 ++-- lib/std/dwarf.zig | 254 +++++++++++++++++++++++++------------- src/link/MachO/Object.zig | 4 +- 3 files changed, 185 insertions(+), 94 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index b600f7245a..c6ee812c19 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -30,10 +30,8 @@ pub const LineInfo = struct { line: u64, column: u64, file_name: []const u8, - allocator: ?mem.Allocator, - pub fn deinit(self: LineInfo) void { - const allocator = self.allocator orelse return; + pub fn deinit(self: LineInfo, allocator: mem.Allocator) void { allocator.free(self.file_name); } }; @@ -43,9 +41,9 @@ pub const SymbolInfo = struct { compile_unit_name: []const u8 = "???", line_info: ?LineInfo = null, - pub fn deinit(self: @This()) void { + pub fn deinit(self: @This(), allocator: mem.Allocator) void { if (self.line_info) |li| { - li.deinit(); + li.deinit(allocator); } } }; @@ -695,7 +693,7 @@ pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: anytype, address }; const symbol_info = try module.getSymbolAtAddress(address); - defer symbol_info.deinit(); + defer symbol_info.deinit(debug_info.allocator); return printLineInfo( out_stream, @@ -1568,10 +1566,17 @@ pub const ModuleDebugInfo = switch (native_os) { if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| { return SymbolInfo{ .symbol_name = o_file_di.getSymbolName(relocated_address_o) orelse "???", - .compile_unit_name = compile_unit.die.getAttrString(o_file_di, DW.AT.name) catch |err| switch (err) { + .compile_unit_name = compile_unit.die.getAttrString( + o_file_di, + DW.AT.name, + ) catch |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => "???", }, - .line_info = o_file_di.getLineNumberInfo(compile_unit.*, relocated_address_o + addr_off) catch |err| switch (err) { + .line_info = o_file_di.getLineNumberInfo( + self.allocator(), + compile_unit.*, + relocated_address_o + addr_off, + ) catch |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => null, else => return err, }, diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index 506f9eeef8..ac7cdc761c 100644 --- a/lib/std/dwarf.zig +++ b/lib/std/dwarf.zig @@ -7,8 +7,6 @@ const mem = std.mem; const math = std.math; const leb = @import("leb128.zig"); -const ArrayList = std.ArrayList; - pub const TAG = @import("dwarf/TAG.zig"); pub const AT = @import("dwarf/AT.zig"); pub const OP = @import("dwarf/OP.zig"); @@ -157,6 +155,12 @@ const PcRange = struct { const Func = struct { pc_range: ?PcRange, name: ?[]const u8, + + fn deinit(func: *Func, allocator: mem.Allocator) void { + if (func.name) |name| { + allocator.free(name); + } + } }; const CompileUnit = struct { @@ -166,19 +170,30 @@ const CompileUnit = struct { pc_range: ?PcRange, }; -const AbbrevTable = ArrayList(AbbrevTableEntry); +const AbbrevTable = std.ArrayList(AbbrevTableEntry); const AbbrevTableHeader = struct { // offset from .debug_abbrev offset: u64, table: AbbrevTable, + + fn deinit(header: *AbbrevTableHeader) void { + for (header.table.items) |*entry| { + entry.deinit(); + } + header.table.deinit(); + } }; const AbbrevTableEntry = struct { has_children: bool, abbrev_code: u64, tag_id: u64, - attrs: ArrayList(AbbrevAttr), + attrs: std.ArrayList(AbbrevAttr), + + fn deinit(entry: *AbbrevTableEntry) void { + entry.attrs.deinit(); + } }; const AbbrevAttr = struct { @@ -213,15 +228,22 @@ const Constant = struct { }; const Die = struct { + // Arena for Die's Attr's and FormValue's. + arena: std.heap.ArenaAllocator, tag_id: u64, has_children: bool, - attrs: ArrayList(Attr), + attrs: std.ArrayListUnmanaged(Attr) = .{}, const Attr = struct { id: u64, value: FormValue, }; + fn deinit(self: *Die, allocator: mem.Allocator) void { + self.arena.deinit(); + self.attrs.deinit(allocator); + } + fn getAttr(self: *const Die, id: u64) ?*const FormValue { for (self.attrs.items) |*attr| { if (attr.id == id) return &attr.value; @@ -292,7 +314,6 @@ const LineNumberProgram = struct { default_is_stmt: bool, target_address: u64, include_dirs: []const []const u8, - file_entries: *ArrayList(FileEntry), prev_valid: bool, prev_address: u64, @@ -323,7 +344,7 @@ const LineNumberProgram = struct { self.prev_end_sequence = undefined; } - pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: u64) LineNumberProgram { + pub fn init(is_stmt: bool, include_dirs: []const []const u8, target_address: u64) LineNumberProgram { return LineNumberProgram{ .address = 0, .file = 1, @@ -333,7 +354,6 @@ const LineNumberProgram = struct { .basic_block = false, .end_sequence = false, .include_dirs = include_dirs, - .file_entries = file_entries, .default_is_stmt = is_stmt, .target_address = target_address, .prev_valid = false, @@ -347,24 +367,28 @@ const LineNumberProgram = struct { }; } - pub fn checkLineMatch(self: *LineNumberProgram) !?debug.LineInfo { + pub fn checkLineMatch( + self: *LineNumberProgram, + allocator: mem.Allocator, + file_entries: []const FileEntry, + ) !?debug.LineInfo { if (self.prev_valid and self.target_address >= self.prev_address and self.target_address < self.address) { const file_entry = if (self.prev_file == 0) { return error.MissingDebugInfo; - } else if (self.prev_file - 1 >= self.file_entries.items.len) { + } else if (self.prev_file - 1 >= file_entries.len) { return error.InvalidDebugInfo; - } else &self.file_entries.items[self.prev_file - 1]; + } else &file_entries[self.prev_file - 1]; const dir_name = if (file_entry.dir_index >= self.include_dirs.len) { return error.InvalidDebugInfo; } else self.include_dirs[file_entry.dir_index]; - const file_name = try fs.path.join(self.file_entries.allocator, &[_][]const u8{ dir_name, file_entry.file_name }); - errdefer self.file_entries.allocator.free(file_name); + + const file_name = try fs.path.join(allocator, &[_][]const u8{ dir_name, file_entry.file_name }); + return debug.LineInfo{ .line = if (self.prev_line >= 0) @intCast(u64, self.prev_line) else 0, .column = self.prev_column, .file_name = file_name, - .allocator = self.file_entries.allocator, }; } @@ -419,8 +443,7 @@ fn parseFormValueBlock(allocator: mem.Allocator, in_stream: anytype, endian: std return parseFormValueBlockLen(allocator, in_stream, block_len); } -fn parseFormValueConstant(allocator: mem.Allocator, in_stream: anytype, signed: bool, endian: std.builtin.Endian, comptime size: i32) !FormValue { - _ = allocator; +fn parseFormValueConstant(in_stream: anytype, signed: bool, endian: std.builtin.Endian, comptime size: i32) !FormValue { // TODO: Please forgive me, I've worked around zig not properly spilling some intermediate values here. // `nosuspend` should be removed from all the function calls once it is fixed. return FormValue{ @@ -447,8 +470,7 @@ fn parseFormValueConstant(allocator: mem.Allocator, in_stream: anytype, signed: } // TODO the nosuspends here are workarounds -fn parseFormValueRef(allocator: mem.Allocator, in_stream: anytype, endian: std.builtin.Endian, size: i32) !FormValue { - _ = allocator; +fn parseFormValueRef(in_stream: anytype, endian: std.builtin.Endian, size: i32) !FormValue { return FormValue{ .Ref = switch (size) { 1 => try nosuspend in_stream.readInt(u8, endian), @@ -472,13 +494,13 @@ fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, en const block_len = try nosuspend leb.readULEB128(usize, in_stream); return parseFormValueBlockLen(allocator, in_stream, block_len); }, - FORM.data1 => parseFormValueConstant(allocator, in_stream, false, endian, 1), - FORM.data2 => parseFormValueConstant(allocator, in_stream, false, endian, 2), - FORM.data4 => parseFormValueConstant(allocator, in_stream, false, endian, 4), - FORM.data8 => parseFormValueConstant(allocator, in_stream, false, endian, 8), + FORM.data1 => parseFormValueConstant(in_stream, false, endian, 1), + FORM.data2 => parseFormValueConstant(in_stream, false, endian, 2), + FORM.data4 => parseFormValueConstant(in_stream, false, endian, 4), + FORM.data8 => parseFormValueConstant(in_stream, false, endian, 8), FORM.udata, FORM.sdata => { const signed = form_id == FORM.sdata; - return parseFormValueConstant(allocator, in_stream, signed, endian, -1); + return parseFormValueConstant(in_stream, signed, endian, -1); }, FORM.exprloc => { const size = try nosuspend leb.readULEB128(usize, in_stream); @@ -489,11 +511,11 @@ fn parseFormValue(allocator: mem.Allocator, in_stream: anytype, form_id: u64, en FORM.flag_present => FormValue{ .Flag = true }, FORM.sec_offset => FormValue{ .SecOffset = try readAddress(in_stream, endian, is_64) }, - FORM.ref1 => parseFormValueRef(allocator, in_stream, endian, 1), - FORM.ref2 => parseFormValueRef(allocator, in_stream, endian, 2), - FORM.ref4 => parseFormValueRef(allocator, in_stream, endian, 4), - FORM.ref8 => parseFormValueRef(allocator, in_stream, endian, 8), - FORM.ref_udata => parseFormValueRef(allocator, in_stream, endian, -1), + FORM.ref1 => parseFormValueRef(in_stream, endian, 1), + FORM.ref2 => parseFormValueRef(in_stream, endian, 2), + FORM.ref4 => parseFormValueRef(in_stream, endian, 4), + FORM.ref8 => parseFormValueRef(in_stream, endian, 8), + FORM.ref_udata => parseFormValueRef(in_stream, endian, -1), FORM.ref_addr => FormValue{ .RefAddr = try readAddress(in_stream, endian, is_64) }, FORM.ref_sig8 => FormValue{ .Ref = try nosuspend in_stream.readInt(u64, endian) }, @@ -536,12 +558,24 @@ pub const DwarfInfo = struct { debug_line_str: ?[]const u8, debug_ranges: ?[]const u8, // Filled later by the initializer - abbrev_table_list: ArrayList(AbbrevTableHeader) = undefined, - compile_unit_list: ArrayList(CompileUnit) = undefined, - func_list: ArrayList(Func) = undefined, + abbrev_table_list: std.ArrayListUnmanaged(AbbrevTableHeader) = .{}, + compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .{}, + func_list: std.ArrayListUnmanaged(Func) = .{}, - pub fn allocator(self: DwarfInfo) mem.Allocator { - return self.abbrev_table_list.allocator; + pub fn deinit(di: *DwarfInfo, allocator: mem.Allocator) void { + for (di.abbrev_table_list.items) |*abbrev| { + abbrev.deinit(); + } + di.abbrev_table_list.deinit(allocator); + for (di.compile_unit_list.items) |*cu| { + cu.die.deinit(allocator); + allocator.destroy(cu.die); + } + di.compile_unit_list.deinit(allocator); + for (di.func_list.items) |*func| { + func.deinit(allocator); + } + di.func_list.deinit(allocator); } pub fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 { @@ -556,12 +590,16 @@ pub const DwarfInfo = struct { return null; } - fn scanAllFunctions(di: *DwarfInfo) !void { + fn scanAllFunctions(di: *DwarfInfo, allocator: mem.Allocator) !void { var stream = io.fixedBufferStream(di.debug_info); const in = &stream.reader(); const seekable = &stream.seekableStream(); var this_unit_offset: u64 = 0; + var tmp_arena = std.heap.ArenaAllocator.init(allocator); + defer tmp_arena.deinit(); + const arena = tmp_arena.allocator(); + while (this_unit_offset < try seekable.getEndPos()) { try seekable.seekTo(this_unit_offset); @@ -580,26 +618,30 @@ pub const DwarfInfo = struct { const unit_type = try in.readInt(u8, di.endian); if (unit_type != UT.compile) return error.InvalidDebugInfo; address_size = try in.readByte(); - debug_abbrev_offset = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian); + debug_abbrev_offset = if (is_64) + try in.readInt(u64, di.endian) + else + try in.readInt(u32, di.endian); }, else => { - debug_abbrev_offset = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian); + debug_abbrev_offset = if (is_64) + try in.readInt(u64, di.endian) + else + try in.readInt(u32, di.endian); address_size = try in.readByte(); }, } if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; const compile_unit_pos = try seekable.getPos(); - const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset); + const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset); try seekable.seekTo(compile_unit_pos); const next_unit_pos = this_unit_offset + next_offset; while ((try seekable.getPos()) < next_unit_pos) { - const die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse continue; - defer die_obj.attrs.deinit(); - + const die_obj = (try di.parseDie(arena, in, abbrev_table, is_64)) orelse continue; const after_die_offset = try seekable.getPos(); switch (die_obj.tag_id) { @@ -607,23 +649,33 @@ pub const DwarfInfo = struct { const fn_name = x: { var depth: i32 = 3; var this_die_obj = die_obj; - // Prenvent endless loops + // Prevent endless loops while (depth > 0) : (depth -= 1) { if (this_die_obj.getAttr(AT.name)) |_| { const name = try this_die_obj.getAttrString(di, AT.name); - break :x name; + break :x try allocator.dupe(u8, name); } else if (this_die_obj.getAttr(AT.abstract_origin)) |_| { // Follow the DIE it points to and repeat const ref_offset = try this_die_obj.getAttrRef(AT.abstract_origin); if (ref_offset > next_offset) return error.InvalidDebugInfo; try seekable.seekTo(this_unit_offset + ref_offset); - this_die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; + this_die_obj = (try di.parseDie( + arena, + in, + abbrev_table, + is_64, + )) orelse return error.InvalidDebugInfo; } else if (this_die_obj.getAttr(AT.specification)) |_| { // Follow the DIE it points to and repeat const ref_offset = try this_die_obj.getAttrRef(AT.specification); if (ref_offset > next_offset) return error.InvalidDebugInfo; try seekable.seekTo(this_unit_offset + ref_offset); - this_die_obj = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; + this_die_obj = (try di.parseDie( + arena, + in, + abbrev_table, + is_64, + )) orelse return error.InvalidDebugInfo; } else { break :x null; } @@ -656,7 +708,7 @@ pub const DwarfInfo = struct { } }; - try di.func_list.append(Func{ + try di.func_list.append(allocator, Func{ .name = fn_name, .pc_range = pc_range, }); @@ -671,7 +723,7 @@ pub const DwarfInfo = struct { } } - fn scanAllCompileUnits(di: *DwarfInfo) !void { + fn scanAllCompileUnits(di: *DwarfInfo, allocator: mem.Allocator) !void { var stream = io.fixedBufferStream(di.debug_info); const in = &stream.reader(); const seekable = &stream.seekableStream(); @@ -695,22 +747,30 @@ pub const DwarfInfo = struct { const unit_type = try in.readInt(u8, di.endian); if (unit_type != UT.compile) return error.InvalidDebugInfo; address_size = try in.readByte(); - debug_abbrev_offset = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian); + debug_abbrev_offset = if (is_64) + try in.readInt(u64, di.endian) + else + try in.readInt(u32, di.endian); }, else => { - debug_abbrev_offset = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian); + debug_abbrev_offset = if (is_64) + try in.readInt(u64, di.endian) + else + try in.readInt(u32, di.endian); address_size = try in.readByte(); }, } if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; const compile_unit_pos = try seekable.getPos(); - const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset); + const abbrev_table = try di.getAbbrevTable(allocator, debug_abbrev_offset); try seekable.seekTo(compile_unit_pos); - const compile_unit_die = try di.allocator().create(Die); - compile_unit_die.* = (try di.parseDie(in, abbrev_table, is_64)) orelse return error.InvalidDebugInfo; + const compile_unit_die = try allocator.create(Die); + errdefer allocator.destroy(compile_unit_die); + compile_unit_die.* = (try di.parseDie(allocator, in, abbrev_table, is_64)) orelse + return error.InvalidDebugInfo; if (compile_unit_die.tag_id != TAG.compile_unit) return error.InvalidDebugInfo; @@ -738,7 +798,7 @@ pub const DwarfInfo = struct { } }; - try di.compile_unit_list.append(CompileUnit{ + try di.compile_unit_list.append(allocator, CompileUnit{ .version = version, .is_64 = is_64, .pc_range = pc_range, @@ -797,27 +857,33 @@ pub const DwarfInfo = struct { /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, /// seeks in the stream and parses it. - fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable { + fn getAbbrevTable(di: *DwarfInfo, allocator: mem.Allocator, abbrev_offset: u64) !*const AbbrevTable { for (di.abbrev_table_list.items) |*header| { if (header.offset == abbrev_offset) { return &header.table; } } - try di.abbrev_table_list.append(AbbrevTableHeader{ + try di.abbrev_table_list.append(allocator, AbbrevTableHeader{ .offset = abbrev_offset, - .table = try di.parseAbbrevTable(abbrev_offset), + .table = try di.parseAbbrevTable(allocator, abbrev_offset), }); return &di.abbrev_table_list.items[di.abbrev_table_list.items.len - 1].table; } - fn parseAbbrevTable(di: *DwarfInfo, offset: u64) !AbbrevTable { + fn parseAbbrevTable(di: *DwarfInfo, allocator: mem.Allocator, offset: u64) !AbbrevTable { var stream = io.fixedBufferStream(di.debug_abbrev); const in = &stream.reader(); const seekable = &stream.seekableStream(); try seekable.seekTo(offset); - var result = AbbrevTable.init(di.allocator()); - errdefer result.deinit(); + var result = AbbrevTable.init(allocator); + errdefer { + for (result.items) |*entry| { + entry.attrs.deinit(); + } + result.deinit(); + } + while (true) { const abbrev_code = try leb.readULEB128(u64, in); if (abbrev_code == 0) return result; @@ -825,7 +891,7 @@ pub const DwarfInfo = struct { .abbrev_code = abbrev_code, .tag_id = try leb.readULEB128(u64, in), .has_children = (try in.readByte()) == CHILDREN.yes, - .attrs = ArrayList(AbbrevAttr).init(di.allocator()), + .attrs = std.ArrayList(AbbrevAttr).init(allocator), }); const attrs = &result.items[result.items.len - 1].attrs; @@ -844,21 +910,34 @@ pub const DwarfInfo = struct { } } - fn parseDie(di: *DwarfInfo, in_stream: anytype, abbrev_table: *const AbbrevTable, is_64: bool) !?Die { + fn parseDie( + di: *DwarfInfo, + allocator: mem.Allocator, + in_stream: anytype, + abbrev_table: *const AbbrevTable, + is_64: bool, + ) !?Die { const abbrev_code = try leb.readULEB128(u64, in_stream); if (abbrev_code == 0) return null; const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; var result = Die{ + // Lives as long as the Die. + .arena = std.heap.ArenaAllocator.init(allocator), .tag_id = table_entry.tag_id, .has_children = table_entry.has_children, - .attrs = ArrayList(Die.Attr).init(di.allocator()), }; - try result.attrs.resize(table_entry.attrs.items.len); + try result.attrs.resize(allocator, table_entry.attrs.items.len); for (table_entry.attrs.items) |attr, i| { result.attrs.items[i] = Die.Attr{ .id = attr.attr_id, - .value = try parseFormValue(di.allocator(), in_stream, attr.form_id, di.endian, is_64), + .value = try parseFormValue( + result.arena.allocator(), + in_stream, + attr.form_id, + di.endian, + is_64, + ), }; if (attr.form_id == FORM.implicit_const) { result.attrs.items[i].value.Const.payload = @bitCast(u64, attr.payload); @@ -867,7 +946,12 @@ pub const DwarfInfo = struct { return result; } - pub fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: u64) !debug.LineInfo { + pub fn getLineNumberInfo( + di: *DwarfInfo, + allocator: mem.Allocator, + compile_unit: CompileUnit, + target_address: u64, + ) !debug.LineInfo { var stream = io.fixedBufferStream(di.debug_line); const in = &stream.reader(); const seekable = &stream.seekableStream(); @@ -906,8 +990,8 @@ pub const DwarfInfo = struct { const opcode_base = try in.readByte(); - const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1); - defer di.allocator().free(standard_opcode_lengths); + const standard_opcode_lengths = try allocator.alloc(u8, opcode_base - 1); + defer allocator.free(standard_opcode_lengths); { var i: usize = 0; @@ -916,19 +1000,28 @@ pub const DwarfInfo = struct { } } - var include_directories = ArrayList([]const u8).init(di.allocator()); + var tmp_arena = std.heap.ArenaAllocator.init(allocator); + defer tmp_arena.deinit(); + const arena = tmp_arena.allocator(); + + var include_directories = std.ArrayList([]const u8).init(arena); try include_directories.append(compile_unit_cwd); + while (true) { - const dir = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize)); + const dir = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize)); if (dir.len == 0) break; try include_directories.append(dir); } - var file_entries = ArrayList(FileEntry).init(di.allocator()); - var prog = LineNumberProgram.init(default_is_stmt, include_directories.items, &file_entries, target_address); + var file_entries = std.ArrayList(FileEntry).init(arena); + var prog = LineNumberProgram.init( + default_is_stmt, + include_directories.items, + target_address, + ); while (true) { - const file_name = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize)); + const file_name = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize)); if (file_name.len == 0) break; const dir_index = try leb.readULEB128(usize, in); const mtime = try leb.readULEB128(usize, in); @@ -955,7 +1048,7 @@ pub const DwarfInfo = struct { switch (sub_op) { LNE.end_sequence => { prog.end_sequence = true; - if (try prog.checkLineMatch()) |info| return info; + if (try prog.checkLineMatch(allocator, file_entries.items)) |info| return info; prog.reset(); }, LNE.set_address => { @@ -963,7 +1056,7 @@ pub const DwarfInfo = struct { prog.address = addr; }, LNE.define_file => { - const file_name = try in.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize)); + const file_name = try in.readUntilDelimiterAlloc(arena, 0, math.maxInt(usize)); const dir_index = try leb.readULEB128(usize, in); const mtime = try leb.readULEB128(usize, in); const len_bytes = try leb.readULEB128(usize, in); @@ -986,12 +1079,12 @@ pub const DwarfInfo = struct { const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range); prog.line += inc_line; prog.address += inc_addr; - if (try prog.checkLineMatch()) |info| return info; + if (try prog.checkLineMatch(allocator, file_entries.items)) |info| return info; prog.basic_block = false; } else { switch (opcode) { LNS.copy => { - if (try prog.checkLineMatch()) |info| return info; + if (try prog.checkLineMatch(allocator, file_entries.items)) |info| return info; prog.basic_block = false; }, LNS.advance_pc => { @@ -1068,13 +1161,8 @@ pub const DwarfInfo = struct { }; /// Initialize DWARF info. The caller has the responsibility to initialize most -/// the DwarfInfo fields before calling. These fields can be left undefined: -/// * abbrev_table_list -/// * compile_unit_list +/// the DwarfInfo fields before calling. pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator) !void { - di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator); - di.compile_unit_list = ArrayList(CompileUnit).init(allocator); - di.func_list = ArrayList(Func).init(allocator); - try di.scanAllFunctions(); - try di.scanAllCompileUnits(); + try di.scanAllFunctions(allocator); + try di.scanAllCompileUnits(allocator); } diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index c8ebb4b8b5..6bbc8cd9a4 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -131,9 +131,7 @@ const DebugInfo = struct { allocator.free(self.debug_line); allocator.free(self.debug_line_str); allocator.free(self.debug_ranges); - self.inner.abbrev_table_list.deinit(); - self.inner.compile_unit_list.deinit(); - self.inner.func_list.deinit(); + self.inner.deinit(allocator); } }; From 96c1314443bdf26442a2c9fdffa03f2afffbcb8e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 21 Apr 2022 00:45:01 +0200 Subject: [PATCH 1209/2031] debug: fix resource (de)allocation for MachO targets With this change, it is now possible to safely call `var di = std.debug.openSelfDebugInfo(gpa)`. Calling then `di.deinit()` on the object will correctly free all allocated resources. --- lib/std/debug.zig | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index c6ee812c19..58038ef522 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -692,7 +692,7 @@ pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: anytype, address else => return err, }; - const symbol_info = try module.getSymbolAtAddress(address); + const symbol_info = try module.getSymbolAtAddress(debug_info.allocator, address); defer symbol_info.deinit(debug_info.allocator); return printLineInfo( @@ -1142,7 +1142,12 @@ pub const DebugInfo = struct { } pub fn deinit(self: *DebugInfo) void { - // TODO: resources https://github.com/ziglang/zig/issues/4353 + var it = self.address_map.iterator(); + while (it.next()) |entry| { + const mdi = entry.value_ptr.*; + mdi.deinit(self.allocator); + self.allocator.destroy(mdi); + } self.address_map.deinit(); } @@ -1392,11 +1397,18 @@ pub const ModuleDebugInfo = switch (native_os) { addr_table: std.StringHashMap(u64), }; - pub fn allocator(self: @This()) mem.Allocator { - return self.ofiles.allocator; + fn deinit(self: *@This(), allocator: mem.Allocator) void { + var it = self.ofiles.iterator(); + while (it.next()) |entry| { + const ofile = entry.value_ptr; + ofile.di.deinit(allocator); + ofile.addr_table.deinit(); + } + self.ofiles.deinit(); + allocator.free(self.symbols); } - fn loadOFile(self: *@This(), o_file_path: []const u8) !OFileInfo { + fn loadOFile(self: *@This(), allocator: mem.Allocator, o_file_path: []const u8) !OFileInfo { const o_file = try fs.cwd().openFile(o_file_path, .{ .intended_io_mode = .blocking }); const mapped_mem = try mapWholeFile(o_file); @@ -1448,7 +1460,7 @@ pub const ModuleDebugInfo = switch (native_os) { )[0..symtabcmd.?.nsyms]; // TODO handle tentative (common) symbols - var addr_table = std.StringHashMap(u64).init(self.allocator()); + var addr_table = std.StringHashMap(u64).init(allocator); try addr_table.ensureTotalCapacity(@intCast(u32, symtab.len)); for (symtab) |sym| { if (sym.n_strx == 0) continue; @@ -1517,7 +1529,7 @@ pub const ModuleDebugInfo = switch (native_os) { null, }; - try DW.openDwarfDebugInfo(&di, self.allocator()); + try DW.openDwarfDebugInfo(&di, allocator); var info = OFileInfo{ .di = di, .addr_table = addr_table, @@ -1529,7 +1541,7 @@ pub const ModuleDebugInfo = switch (native_os) { return info; } - pub fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo { + pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo { nosuspend { // Translate the VA into an address into this object const relocated_address = address - self.base_address; @@ -1546,7 +1558,7 @@ pub const ModuleDebugInfo = switch (native_os) { // Check if its debug infos are already in the cache var o_file_info = self.ofiles.get(o_file_path) orelse - (self.loadOFile(o_file_path) catch |err| switch (err) { + (self.loadOFile(allocator, o_file_path) catch |err| switch (err) { error.FileNotFound, error.MissingDebugInfo, error.InvalidDebugInfo, @@ -1573,7 +1585,7 @@ pub const ModuleDebugInfo = switch (native_os) { error.MissingDebugInfo, error.InvalidDebugInfo => "???", }, .line_info = o_file_di.getLineNumberInfo( - self.allocator(), + allocator, compile_unit.*, relocated_address_o + addr_off, ) catch |err| switch (err) { From 4f527e5d36f66a83ff6a263a03f16e2c4d049f1e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 20 Apr 2022 17:16:32 -0700 Subject: [PATCH 1210/2031] std: fix missing hash map safety There was a missing compile error for calling ensureUnusedCapacity without a Context in the case that the Context is non-void. --- lib/std/array_hash_map.zig | 2 +- lib/std/hash_map.zig | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/std/array_hash_map.zig b/lib/std/array_hash_map.zig index 31860963af..304c98a2a9 100644 --- a/lib/std/array_hash_map.zig +++ b/lib/std/array_hash_map.zig @@ -798,7 +798,7 @@ pub fn ArrayHashMapUnmanaged( allocator: Allocator, additional_capacity: usize, ) !void { - if (@sizeOf(ByIndexContext) != 0) + if (@sizeOf(Context) != 0) @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call ensureTotalCapacityContext instead."); return self.ensureUnusedCapacityContext(allocator, additional_capacity, undefined); } diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig index 96df243f6e..eb24ef591b 100644 --- a/lib/std/hash_map.zig +++ b/lib/std/hash_map.zig @@ -913,6 +913,8 @@ pub fn HashMapUnmanaged( } pub fn ensureUnusedCapacity(self: *Self, allocator: Allocator, additional_size: Size) Allocator.Error!void { + if (@sizeOf(Context) != 0) + @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call ensureUnusedCapacityContext instead."); return ensureUnusedCapacityContext(self, allocator, additional_size, undefined); } pub fn ensureUnusedCapacityContext(self: *Self, allocator: Allocator, additional_size: Size, ctx: Context) Allocator.Error!void { From f7596ae9423e9de8276629803147e1a243f2177b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 19 Apr 2022 21:51:08 -0700 Subject: [PATCH 1211/2031] stage2: use indexes for Decl objects Rather than allocating Decl objects with an Allocator, we instead allocate them with a SegmentedList. This provides four advantages: * Stable memory so that one thread can access a Decl object while another thread allocates additional Decl objects from this list. * It allows us to use u32 indexes to reference Decl objects rather than pointers, saving memory in Type, Value, and dependency sets. * Using integers to reference Decl objects rather than pointers makes serialization trivial. * It provides a unique integer to be used for anonymous symbol names, avoiding multi-threaded contention on an atomic counter. --- lib/std/segmented_list.zig | 18 + src/Compilation.zig | 219 ++--- src/Module.zig | 910 +++++++++++--------- src/RangeSet.zig | 32 +- src/Sema.zig | 1564 +++++++++++++++++----------------- src/TypedValue.zig | 52 +- src/arch/aarch64/CodeGen.zig | 58 +- src/arch/arm/CodeGen.zig | 53 +- src/arch/riscv64/CodeGen.zig | 42 +- src/arch/sparcv9/CodeGen.zig | 26 +- src/arch/wasm/CodeGen.zig | 87 +- src/arch/x86_64/CodeGen.zig | 53 +- src/codegen.zig | 24 +- src/codegen/c.zig | 87 +- src/codegen/llvm.zig | 247 +++--- src/codegen/spirv.zig | 16 +- src/crash_report.zig | 15 +- src/link.zig | 98 ++- src/link/C.zig | 63 +- src/link/Coff.zig | 49 +- src/link/Dwarf.zig | 46 +- src/link/Elf.zig | 98 ++- src/link/MachO.zig | 112 ++- src/link/NvPtx.zig | 12 +- src/link/Plan9.zig | 94 +- src/link/SpirV.zig | 24 +- src/link/Wasm.zig | 112 ++- src/main.zig | 8 +- src/print_air.zig | 11 +- src/type.zig | 274 +++--- src/value.zig | 261 +++--- 31 files changed, 2584 insertions(+), 2181 deletions(-) diff --git a/lib/std/segmented_list.zig b/lib/std/segmented_list.zig index 8bdfe7dceb..27667353ef 100644 --- a/lib/std/segmented_list.zig +++ b/lib/std/segmented_list.zig @@ -148,6 +148,24 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type return result; } + /// Reduce length to `new_len`. + /// Invalidates pointers for the elements at index new_len and beyond. + pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void { + assert(new_len <= self.len); + self.len = new_len; + } + + /// Invalidates all element pointers. + pub fn clearRetainingCapacity(self: *Self) void { + self.items.len = 0; + } + + /// Invalidates all element pointers. + pub fn clearAndFree(self: *Self, allocator: Allocator) void { + self.setCapacity(allocator, 0) catch unreachable; + self.items.len = 0; + } + /// Grows or shrinks capacity to match usage. /// TODO update this and related methods to match the conventions set by ArrayList pub fn setCapacity(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void { diff --git a/src/Compilation.zig b/src/Compilation.zig index f0e490c67d..6019fc0856 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -191,22 +191,22 @@ pub const CSourceFile = struct { const Job = union(enum) { /// Write the constant value for a Decl to the output file. - codegen_decl: *Module.Decl, + codegen_decl: Module.Decl.Index, /// Write the machine code for a function to the output file. codegen_func: *Module.Fn, /// Render the .h file snippet for the Decl. - emit_h_decl: *Module.Decl, + emit_h_decl: Module.Decl.Index, /// The Decl needs to be analyzed and possibly export itself. /// It may have already be analyzed, or it may have been determined /// to be outdated; in this case perform semantic analysis again. - analyze_decl: *Module.Decl, + analyze_decl: Module.Decl.Index, /// The file that was loaded with `@embedFile` has changed on disk /// and has been re-loaded into memory. All Decls that depend on it /// need to be re-analyzed. update_embed_file: *Module.EmbedFile, /// The source file containing the Decl has been updated, and so the /// Decl may need its line number information updated in the debug info. - update_line_number: *Module.Decl, + update_line_number: Module.Decl.Index, /// The main source file for the package needs to be analyzed. analyze_pkg: *Package, @@ -2105,17 +2105,18 @@ pub fn update(comp: *Compilation) !void { // deletion set may grow as we call `clearDecl` within this loop, // and more unreferenced Decls are revealed. while (module.deletion_set.count() != 0) { - const decl = module.deletion_set.keys()[0]; + const decl_index = module.deletion_set.keys()[0]; + const decl = module.declPtr(decl_index); assert(decl.deletion_flag); assert(decl.dependants.count() == 0); const is_anon = if (decl.zir_decl_index == 0) blk: { - break :blk decl.src_namespace.anon_decls.swapRemove(decl); + break :blk decl.src_namespace.anon_decls.swapRemove(decl_index); } else false; - try module.clearDecl(decl, null); + try module.clearDecl(decl_index, null); if (is_anon) { - decl.destroy(module); + module.destroyDecl(decl_index); } } @@ -2444,13 +2445,15 @@ pub fn totalErrorCount(self: *Compilation) usize { // the previous parse success, including compile errors, but we cannot // emit them until the file succeeds parsing. for (module.failed_decls.keys()) |key| { - if (key.getFileScope().okToReportErrors()) { + const decl = module.declPtr(key); + if (decl.getFileScope().okToReportErrors()) { total += 1; } } if (module.emit_h) |emit_h| { for (emit_h.failed_decls.keys()) |key| { - if (key.getFileScope().okToReportErrors()) { + const decl = module.declPtr(key); + if (decl.getFileScope().okToReportErrors()) { total += 1; } } @@ -2529,9 +2532,10 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { { var it = module.failed_decls.iterator(); while (it.next()) |entry| { + const decl = module.declPtr(entry.key_ptr.*); // Skip errors for Decls within files that had a parse failure. // We'll try again once parsing succeeds. - if (entry.key_ptr.*.getFileScope().okToReportErrors()) { + if (decl.getFileScope().okToReportErrors()) { try AllErrors.add(module, &arena, &errors, entry.value_ptr.*.*); } } @@ -2539,9 +2543,10 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { if (module.emit_h) |emit_h| { var it = emit_h.failed_decls.iterator(); while (it.next()) |entry| { + const decl = module.declPtr(entry.key_ptr.*); // Skip errors for Decls within files that had a parse failure. // We'll try again once parsing succeeds. - if (entry.key_ptr.*.getFileScope().okToReportErrors()) { + if (decl.getFileScope().okToReportErrors()) { try AllErrors.add(module, &arena, &errors, entry.value_ptr.*.*); } } @@ -2564,7 +2569,8 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { const keys = module.compile_log_decls.keys(); const values = module.compile_log_decls.values(); // First one will be the error; subsequent ones will be notes. - const src_loc = keys[0].nodeOffsetSrcLoc(values[0]); + const err_decl = module.declPtr(keys[0]); + const src_loc = err_decl.nodeOffsetSrcLoc(values[0]); const err_msg = Module.ErrorMsg{ .src_loc = src_loc, .msg = "found compile log statement", @@ -2573,8 +2579,9 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { defer self.gpa.free(err_msg.notes); for (keys[1..]) |key, i| { + const note_decl = module.declPtr(key); err_msg.notes[i] = .{ - .src_loc = key.nodeOffsetSrcLoc(values[i + 1]), + .src_loc = note_decl.nodeOffsetSrcLoc(values[i + 1]), .msg = "also here", }; } @@ -2708,38 +2715,42 @@ pub fn performAllTheWork( fn processOneJob(comp: *Compilation, job: Job) !void { switch (job) { - .codegen_decl => |decl| switch (decl.analysis) { - .unreferenced => unreachable, - .in_progress => unreachable, - .outdated => unreachable, + .codegen_decl => |decl_index| { + if (build_options.omit_stage2) + @panic("sadly stage2 is omitted from this build to save memory on the CI server"); - .file_failure, - .sema_failure, - .codegen_failure, - .dependency_failure, - .sema_failure_retryable, - => return, + const module = comp.bin_file.options.module.?; + const decl = module.declPtr(decl_index); - .complete, .codegen_failure_retryable => { - if (build_options.omit_stage2) - @panic("sadly stage2 is omitted from this build to save memory on the CI server"); + switch (decl.analysis) { + .unreferenced => unreachable, + .in_progress => unreachable, + .outdated => unreachable, - const named_frame = tracy.namedFrame("codegen_decl"); - defer named_frame.end(); + .file_failure, + .sema_failure, + .codegen_failure, + .dependency_failure, + .sema_failure_retryable, + => return, - const module = comp.bin_file.options.module.?; - assert(decl.has_tv); + .complete, .codegen_failure_retryable => { + const named_frame = tracy.namedFrame("codegen_decl"); + defer named_frame.end(); - if (decl.alive) { - try module.linkerUpdateDecl(decl); + assert(decl.has_tv); + + if (decl.alive) { + try module.linkerUpdateDecl(decl_index); + return; + } + + // Instead of sending this decl to the linker, we actually will delete it + // because we found out that it in fact was never referenced. + module.deleteUnusedDecl(decl_index); return; - } - - // Instead of sending this decl to the linker, we actually will delete it - // because we found out that it in fact was never referenced. - module.deleteUnusedDecl(decl); - return; - }, + }, + } }, .codegen_func => |func| { if (build_options.omit_stage2) @@ -2754,68 +2765,73 @@ fn processOneJob(comp: *Compilation, job: Job) !void { error.AnalysisFail => return, }; }, - .emit_h_decl => |decl| switch (decl.analysis) { - .unreferenced => unreachable, - .in_progress => unreachable, - .outdated => unreachable, - - .file_failure, - .sema_failure, - .dependency_failure, - .sema_failure_retryable, - => return, - - // emit-h only requires semantic analysis of the Decl to be complete, - // it does not depend on machine code generation to succeed. - .codegen_failure, .codegen_failure_retryable, .complete => { - if (build_options.omit_stage2) - @panic("sadly stage2 is omitted from this build to save memory on the CI server"); - - const named_frame = tracy.namedFrame("emit_h_decl"); - defer named_frame.end(); - - const gpa = comp.gpa; - const module = comp.bin_file.options.module.?; - const emit_h = module.emit_h.?; - _ = try emit_h.decl_table.getOrPut(gpa, decl); - const decl_emit_h = decl.getEmitH(module); - const fwd_decl = &decl_emit_h.fwd_decl; - fwd_decl.shrinkRetainingCapacity(0); - var typedefs_arena = std.heap.ArenaAllocator.init(gpa); - defer typedefs_arena.deinit(); - - var dg: c_codegen.DeclGen = .{ - .gpa = gpa, - .module = module, - .error_msg = null, - .decl = decl, - .fwd_decl = fwd_decl.toManaged(gpa), - .typedefs = c_codegen.TypedefMap.initContext(gpa, .{ - .target = comp.getTarget(), - }), - .typedefs_arena = typedefs_arena.allocator(), - }; - defer dg.fwd_decl.deinit(); - defer dg.typedefs.deinit(); - - c_codegen.genHeader(&dg) catch |err| switch (err) { - error.AnalysisFail => { - try emit_h.failed_decls.put(gpa, decl, dg.error_msg.?); - return; - }, - else => |e| return e, - }; - - fwd_decl.* = dg.fwd_decl.moveToUnmanaged(); - fwd_decl.shrinkAndFree(gpa, fwd_decl.items.len); - }, - }, - .analyze_decl => |decl| { + .emit_h_decl => |decl_index| { if (build_options.omit_stage2) @panic("sadly stage2 is omitted from this build to save memory on the CI server"); const module = comp.bin_file.options.module.?; - module.ensureDeclAnalyzed(decl) catch |err| switch (err) { + const decl = module.declPtr(decl_index); + + switch (decl.analysis) { + .unreferenced => unreachable, + .in_progress => unreachable, + .outdated => unreachable, + + .file_failure, + .sema_failure, + .dependency_failure, + .sema_failure_retryable, + => return, + + // emit-h only requires semantic analysis of the Decl to be complete, + // it does not depend on machine code generation to succeed. + .codegen_failure, .codegen_failure_retryable, .complete => { + const named_frame = tracy.namedFrame("emit_h_decl"); + defer named_frame.end(); + + const gpa = comp.gpa; + const emit_h = module.emit_h.?; + _ = try emit_h.decl_table.getOrPut(gpa, decl_index); + const decl_emit_h = emit_h.declPtr(decl_index); + const fwd_decl = &decl_emit_h.fwd_decl; + fwd_decl.shrinkRetainingCapacity(0); + var typedefs_arena = std.heap.ArenaAllocator.init(gpa); + defer typedefs_arena.deinit(); + + var dg: c_codegen.DeclGen = .{ + .gpa = gpa, + .module = module, + .error_msg = null, + .decl_index = decl_index, + .decl = decl, + .fwd_decl = fwd_decl.toManaged(gpa), + .typedefs = c_codegen.TypedefMap.initContext(gpa, .{ + .mod = module, + }), + .typedefs_arena = typedefs_arena.allocator(), + }; + defer dg.fwd_decl.deinit(); + defer dg.typedefs.deinit(); + + c_codegen.genHeader(&dg) catch |err| switch (err) { + error.AnalysisFail => { + try emit_h.failed_decls.put(gpa, decl_index, dg.error_msg.?); + return; + }, + else => |e| return e, + }; + + fwd_decl.* = dg.fwd_decl.moveToUnmanaged(); + fwd_decl.shrinkAndFree(gpa, fwd_decl.items.len); + }, + } + }, + .analyze_decl => |decl_index| { + if (build_options.omit_stage2) + @panic("sadly stage2 is omitted from this build to save memory on the CI server"); + + const module = comp.bin_file.options.module.?; + module.ensureDeclAnalyzed(decl_index) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => return, }; @@ -2833,7 +2849,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { error.AnalysisFail => return, }; }, - .update_line_number => |decl| { + .update_line_number => |decl_index| { if (build_options.omit_stage2) @panic("sadly stage2 is omitted from this build to save memory on the CI server"); @@ -2842,9 +2858,10 @@ fn processOneJob(comp: *Compilation, job: Job) !void { const gpa = comp.gpa; const module = comp.bin_file.options.module.?; + const decl = module.declPtr(decl_index); comp.bin_file.updateDeclLineNumber(module, decl) catch |err| { try module.failed_decls.ensureUnusedCapacity(gpa, 1); - module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create( + module.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create( gpa, decl.srcLoc(), "unable to update line number: {s}", @@ -3472,7 +3489,7 @@ fn reportRetryableEmbedFileError( const mod = comp.bin_file.options.module.?; const gpa = mod.gpa; - const src_loc: Module.SrcLoc = embed_file.owner_decl.srcLoc(); + const src_loc: Module.SrcLoc = mod.declPtr(embed_file.owner_decl).srcLoc(); const err_msg = if (embed_file.pkg.root_src_directory.path) |dir_path| try Module.ErrorMsg.create( diff --git a/src/Module.zig b/src/Module.zig index 95ae55feb8..1119d73ab0 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -49,15 +49,15 @@ global_zir_cache: Compilation.Directory, /// Used by AstGen worker to load and store ZIR cache. local_zir_cache: Compilation.Directory, /// It's rare for a decl to be exported, so we save memory by having a sparse -/// map of Decl pointers to details about them being exported. +/// map of Decl indexes to details about them being exported. /// The Export memory is owned by the `export_owners` table; the slice itself /// is owned by this table. The slice is guaranteed to not be empty. -decl_exports: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{}, +decl_exports: std.AutoArrayHashMapUnmanaged(Decl.Index, []*Export) = .{}, /// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl /// is modified. Note that the key of this table is not the Decl being exported, but the Decl that /// is performing the export of another Decl. /// This table owns the Export memory. -export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{}, +export_owners: std.AutoArrayHashMapUnmanaged(Decl.Index, []*Export) = .{}, /// The set of all the Zig source files in the Module. We keep track of this in order /// to iterate over it and check which source files have been modified on the file system when /// an update is requested, as well as to cache `@import` results. @@ -89,10 +89,10 @@ align_stack_fns: std.AutoHashMapUnmanaged(*const Fn, SetAlignStack) = .{}, /// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator. /// Note that a Decl can succeed but the Fn it represents can fail. In this case, /// a Decl can have a failed_decls entry but have analysis status of success. -failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{}, +failed_decls: std.AutoArrayHashMapUnmanaged(Decl.Index, *ErrorMsg) = .{}, /// Keep track of one `@compileLog` callsite per owner Decl. /// The value is the AST node index offset from the Decl. -compile_log_decls: std.AutoArrayHashMapUnmanaged(*Decl, i32) = .{}, +compile_log_decls: std.AutoArrayHashMapUnmanaged(Decl.Index, i32) = .{}, /// Using a map here for consistency with the other fields here. /// The ErrorMsg memory is owned by the `File`, using Module's general purpose allocator. failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .{}, @@ -102,11 +102,9 @@ failed_embed_files: std.AutoArrayHashMapUnmanaged(*EmbedFile, *ErrorMsg) = .{}, /// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator. failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{}, -next_anon_name_index: usize = 0, - /// Candidates for deletion. After a semantic analysis update completes, this list /// contains Decls that need to be deleted if they end up having no references to them. -deletion_set: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, +deletion_set: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{}, /// Error tags and their values, tag names are duped with mod.gpa. /// Corresponds with `error_name_list`. @@ -137,7 +135,21 @@ compile_log_text: ArrayListUnmanaged(u8) = .{}, emit_h: ?*GlobalEmitH, -test_functions: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, +test_functions: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{}, + +/// Rather than allocating Decl objects with an Allocator, we instead allocate +/// them with this SegmentedList. This provides four advantages: +/// * Stable memory so that one thread can access a Decl object while another +/// thread allocates additional Decl objects from this list. +/// * It allows us to use u32 indexes to reference Decl objects rather than +/// pointers, saving memory in Type, Value, and dependency sets. +/// * Using integers to reference Decl objects rather than pointers makes +/// serialization trivial. +/// * It provides a unique integer to be used for anonymous symbol names, avoiding +/// multi-threaded contention on an atomic counter. +allocated_decls: std.SegmentedList(Decl, 0) = .{}, +/// When a Decl object is freed from `allocated_decls`, it is pushed into this stack. +decls_free_list: std.ArrayListUnmanaged(Decl.Index) = .{}, const MonomorphedFuncsSet = std.HashMapUnmanaged( *Fn, @@ -173,7 +185,7 @@ pub const MemoizedCallSet = std.HashMapUnmanaged( ); pub const MemoizedCall = struct { - target: std.Target, + module: *Module, pub const Key = struct { func: *Fn, @@ -191,7 +203,7 @@ pub const MemoizedCall = struct { assert(a.args.len == b.args.len); for (a.args) |a_arg, arg_i| { const b_arg = b.args[arg_i]; - if (!a_arg.eql(b_arg, ctx.target)) { + if (!a_arg.eql(b_arg, ctx.module)) { return false; } } @@ -210,7 +222,7 @@ pub const MemoizedCall = struct { // This logic must be kept in sync with the logic in `analyzeCall` that // computes the hash. for (key.args) |arg| { - arg.hash(&hasher, ctx.target); + arg.hash(&hasher, ctx.module); } return hasher.final(); @@ -231,9 +243,17 @@ pub const GlobalEmitH = struct { /// When emit_h is non-null, each Decl gets one more compile error slot for /// emit-h failing for that Decl. This table is also how we tell if a Decl has /// failed emit-h or succeeded. - failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{}, + failed_decls: std.AutoArrayHashMapUnmanaged(Decl.Index, *ErrorMsg) = .{}, /// Tracks all decls in order to iterate over them and emit .h code for them. - decl_table: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, + decl_table: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{}, + /// Similar to the allocated_decls field of Module, this is where `EmitH` objects + /// are allocated. There will be exactly one EmitH object per Decl object, with + /// identical indexes. + allocated_emit_h: std.SegmentedList(EmitH, 0) = .{}, + + pub fn declPtr(global_emit_h: *GlobalEmitH, decl_index: Decl.Index) *EmitH { + return global_emit_h.allocated_emit_h.at(@enumToInt(decl_index)); + } }; pub const ErrorInt = u32; @@ -244,12 +264,12 @@ pub const Export = struct { /// Represents the position of the export, if any, in the output file. link: link.File.Export, /// The Decl that performs the export. Note that this is *not* the Decl being exported. - owner_decl: *Decl, + owner_decl: Decl.Index, /// The Decl containing the export statement. Inline function calls /// may cause this to be different from the owner_decl. - src_decl: *Decl, + src_decl: Decl.Index, /// The Decl being exported. Note this is *not* the Decl performing the export. - exported_decl: *Decl, + exported_decl: Decl.Index, status: enum { in_progress, failed, @@ -259,22 +279,16 @@ pub const Export = struct { complete, }, - pub fn getSrcLoc(exp: Export) SrcLoc { + pub fn getSrcLoc(exp: Export, mod: *Module) SrcLoc { + const src_decl = mod.declPtr(exp.src_decl); return .{ - .file_scope = exp.src_decl.getFileScope(), - .parent_decl_node = exp.src_decl.src_node, + .file_scope = src_decl.getFileScope(), + .parent_decl_node = src_decl.src_node, .lazy = exp.src, }; } }; -/// When Module emit_h field is non-null, each Decl is allocated via this struct, so that -/// there can be EmitH state attached to each Decl. -pub const DeclPlusEmitH = struct { - decl: Decl, - emit_h: EmitH, -}; - pub const CaptureScope = struct { parent: ?*CaptureScope, @@ -458,38 +472,35 @@ pub const Decl = struct { /// typed_value may need to be regenerated. dependencies: DepsTable = .{}, - pub const DepsTable = std.AutoArrayHashMapUnmanaged(*Decl, void); + pub const Index = enum(u32) { + _, + + pub fn toOptional(i: Index) OptionalIndex { + return @intToEnum(OptionalIndex, @enumToInt(i)); + } + }; + + pub const OptionalIndex = enum(u32) { + none = std.math.maxInt(u32), + _, + + pub fn init(oi: ?Index) OptionalIndex { + return oi orelse .none; + } + + pub fn unwrap(oi: OptionalIndex) ?Index { + if (oi == .none) return null; + return @intToEnum(Index, @enumToInt(oi)); + } + }; + + pub const DepsTable = std.AutoArrayHashMapUnmanaged(Decl.Index, void); pub fn clearName(decl: *Decl, gpa: Allocator) void { gpa.free(mem.sliceTo(decl.name, 0)); decl.name = undefined; } - pub fn destroy(decl: *Decl, module: *Module) void { - const gpa = module.gpa; - log.debug("destroy {*} ({s})", .{ decl, decl.name }); - _ = module.test_functions.swapRemove(decl); - if (decl.deletion_flag) { - assert(module.deletion_set.swapRemove(decl)); - } - if (decl.has_tv) { - if (decl.getInnerNamespace()) |namespace| { - namespace.destroyDecls(module); - } - decl.clearValues(gpa); - } - decl.dependants.deinit(gpa); - decl.dependencies.deinit(gpa); - decl.clearName(gpa); - if (module.emit_h != null) { - const decl_plus_emit_h = @fieldParentPtr(DeclPlusEmitH, "decl", decl); - decl_plus_emit_h.emit_h.fwd_decl.deinit(gpa); - gpa.destroy(decl_plus_emit_h); - } else { - gpa.destroy(decl); - } - } - pub fn clearValues(decl: *Decl, gpa: Allocator) void { if (decl.getExternFn()) |extern_fn| { extern_fn.deinit(gpa); @@ -573,13 +584,6 @@ pub const Decl = struct { return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); } - /// Returns true if and only if the Decl is the top level struct associated with a File. - pub fn isRoot(decl: *const Decl) bool { - if (decl.src_namespace.parent != null) - return false; - return decl == decl.src_namespace.getDecl(); - } - pub fn relativeToLine(decl: Decl, offset: u32) u32 { return decl.src_line + offset; } @@ -622,20 +626,20 @@ pub const Decl = struct { return tree.tokens.items(.start)[decl.srcToken()]; } - pub fn renderFullyQualifiedName(decl: Decl, writer: anytype) !void { + pub fn renderFullyQualifiedName(decl: Decl, mod: *Module, writer: anytype) !void { const unqualified_name = mem.sliceTo(decl.name, 0); - return decl.src_namespace.renderFullyQualifiedName(unqualified_name, writer); + return decl.src_namespace.renderFullyQualifiedName(mod, unqualified_name, writer); } - pub fn renderFullyQualifiedDebugName(decl: Decl, writer: anytype) !void { + pub fn renderFullyQualifiedDebugName(decl: Decl, mod: *Module, writer: anytype) !void { const unqualified_name = mem.sliceTo(decl.name, 0); - return decl.src_namespace.renderFullyQualifiedDebugName(unqualified_name, writer); + return decl.src_namespace.renderFullyQualifiedDebugName(mod, unqualified_name, writer); } - pub fn getFullyQualifiedName(decl: Decl, gpa: Allocator) ![:0]u8 { - var buffer = std.ArrayList(u8).init(gpa); + pub fn getFullyQualifiedName(decl: Decl, mod: *Module) ![:0]u8 { + var buffer = std.ArrayList(u8).init(mod.gpa); defer buffer.deinit(); - try decl.renderFullyQualifiedName(buffer.writer()); + try decl.renderFullyQualifiedName(mod, buffer.writer()); return buffer.toOwnedSliceSentinel(0); } @@ -662,7 +666,6 @@ pub const Decl = struct { if (!decl.owns_tv) return null; const ty = (decl.val.castTag(.ty) orelse return null).data; const struct_obj = (ty.castTag(.@"struct") orelse return null).data; - assert(struct_obj.owner_decl == decl); return struct_obj; } @@ -672,7 +675,6 @@ pub const Decl = struct { if (!decl.owns_tv) return null; const ty = (decl.val.castTag(.ty) orelse return null).data; const union_obj = (ty.cast(Type.Payload.Union) orelse return null).data; - assert(union_obj.owner_decl == decl); return union_obj; } @@ -681,7 +683,6 @@ pub const Decl = struct { pub fn getFunction(decl: *const Decl) ?*Fn { if (!decl.owns_tv) return null; const func = (decl.val.castTag(.function) orelse return null).data; - assert(func.owner_decl == decl); return func; } @@ -690,16 +691,14 @@ pub const Decl = struct { pub fn getExternFn(decl: *const Decl) ?*ExternFn { if (!decl.owns_tv) return null; const extern_fn = (decl.val.castTag(.extern_fn) orelse return null).data; - assert(extern_fn.owner_decl == decl); return extern_fn; } /// If the Decl has a value and it is a variable, returns it, /// otherwise null. - pub fn getVariable(decl: *Decl) ?*Var { + pub fn getVariable(decl: *const Decl) ?*Var { if (!decl.owns_tv) return null; const variable = (decl.val.castTag(.variable) orelse return null).data; - assert(variable.owner_decl == decl); return variable; } @@ -712,12 +711,10 @@ pub const Decl = struct { switch (ty.tag()) { .@"struct" => { const struct_obj = ty.castTag(.@"struct").?.data; - assert(struct_obj.owner_decl == decl); return &struct_obj.namespace; }, .enum_full, .enum_nonexhaustive => { const enum_obj = ty.cast(Type.Payload.EnumFull).?.data; - assert(enum_obj.owner_decl == decl); return &enum_obj.namespace; }, .empty_struct => { @@ -725,12 +722,10 @@ pub const Decl = struct { }, .@"opaque" => { const opaque_obj = ty.cast(Type.Payload.Opaque).?.data; - assert(opaque_obj.owner_decl == decl); return &opaque_obj.namespace; }, .@"union", .union_tagged => { const union_obj = ty.cast(Type.Payload.Union).?.data; - assert(union_obj.owner_decl == decl); return &union_obj.namespace; }, @@ -757,17 +752,11 @@ pub const Decl = struct { return decl.src_namespace.file_scope; } - pub fn getEmitH(decl: *Decl, module: *Module) *EmitH { - assert(module.emit_h != null); - const decl_plus_emit_h = @fieldParentPtr(DeclPlusEmitH, "decl", decl); - return &decl_plus_emit_h.emit_h; - } - - pub fn removeDependant(decl: *Decl, other: *Decl) void { + pub fn removeDependant(decl: *Decl, other: Decl.Index) void { assert(decl.dependants.swapRemove(other)); } - pub fn removeDependency(decl: *Decl, other: *Decl) void { + pub fn removeDependency(decl: *Decl, other: Decl.Index) void { assert(decl.dependencies.swapRemove(other)); } @@ -790,16 +779,6 @@ pub const Decl = struct { return decl.ty.abiAlignment(target); } } - - pub fn markAlive(decl: *Decl) void { - if (decl.alive) return; - decl.alive = true; - - // This is the first time we are marking this Decl alive. We must - // therefore recurse into its value and mark any Decl it references - // as also alive, so that any Decl referenced does not get garbage collected. - decl.val.markReferencedDeclsAlive(); - } }; /// This state is attached to every Decl when Module emit_h is non-null. @@ -810,7 +789,7 @@ pub const EmitH = struct { /// Represents the data that an explicit error set syntax provides. pub const ErrorSet = struct { /// The Decl that corresponds to the error set itself. - owner_decl: *Decl, + owner_decl: Decl.Index, /// Offset from Decl node index, points to the error set AST node. node_offset: i32, /// The string bytes are stored in the owner Decl arena. @@ -819,10 +798,11 @@ pub const ErrorSet = struct { pub const NameMap = std.StringArrayHashMapUnmanaged(void); - pub fn srcLoc(self: ErrorSet) SrcLoc { + pub fn srcLoc(self: ErrorSet, mod: *Module) SrcLoc { + const owner_decl = mod.declPtr(self.owner_decl); return .{ - .file_scope = self.owner_decl.getFileScope(), - .parent_decl_node = self.owner_decl.src_node, + .file_scope = owner_decl.getFileScope(), + .parent_decl_node = owner_decl.src_node, .lazy = .{ .node_offset = self.node_offset }, }; } @@ -844,12 +824,12 @@ pub const PropertyBoolean = enum { no, yes, unknown, wip }; /// Represents the data that a struct declaration provides. pub const Struct = struct { - /// The Decl that corresponds to the struct itself. - owner_decl: *Decl, /// Set of field names in declaration order. fields: Fields, /// Represents the declarations inside this struct. namespace: Namespace, + /// The Decl that corresponds to the struct itself. + owner_decl: Decl.Index, /// Offset from `owner_decl`, points to the struct AST node. node_offset: i32, /// Index of the struct_decl ZIR instruction. @@ -900,30 +880,32 @@ pub const Struct = struct { } }; - pub fn getFullyQualifiedName(s: *Struct, gpa: Allocator) ![:0]u8 { - return s.owner_decl.getFullyQualifiedName(gpa); + pub fn getFullyQualifiedName(s: *Struct, mod: *Module) ![:0]u8 { + return mod.declPtr(s.owner_decl).getFullyQualifiedName(mod); } - pub fn srcLoc(s: Struct) SrcLoc { + pub fn srcLoc(s: Struct, mod: *Module) SrcLoc { + const owner_decl = mod.declPtr(s.owner_decl); return .{ - .file_scope = s.owner_decl.getFileScope(), - .parent_decl_node = s.owner_decl.src_node, + .file_scope = owner_decl.getFileScope(), + .parent_decl_node = owner_decl.src_node, .lazy = .{ .node_offset = s.node_offset }, }; } - pub fn fieldSrcLoc(s: Struct, gpa: Allocator, query: FieldSrcQuery) SrcLoc { + pub fn fieldSrcLoc(s: Struct, mod: *Module, query: FieldSrcQuery) SrcLoc { @setCold(true); - const tree = s.owner_decl.getFileScope().getTree(gpa) catch |err| { + const owner_decl = mod.declPtr(s.owner_decl); + const file = owner_decl.getFileScope(); + const tree = file.getTree(mod.gpa) catch |err| { // In this case we emit a warning + a less precise source location. log.warn("unable to load {s}: {s}", .{ - s.owner_decl.getFileScope().sub_file_path, @errorName(err), + file.sub_file_path, @errorName(err), }); - return s.srcLoc(); + return s.srcLoc(mod); }; - const node = s.owner_decl.relativeToNodeIndex(s.node_offset); + const node = owner_decl.relativeToNodeIndex(s.node_offset); const node_tags = tree.nodes.items(.tag); - const file = s.owner_decl.getFileScope(); switch (node_tags[node]) { .container_decl, .container_decl_trailing, @@ -1013,18 +995,19 @@ pub const Struct = struct { /// the number of fields. pub const EnumSimple = struct { /// The Decl that corresponds to the enum itself. - owner_decl: *Decl, - /// Set of field names in declaration order. - fields: NameMap, + owner_decl: Decl.Index, /// Offset from `owner_decl`, points to the enum decl AST node. node_offset: i32, + /// Set of field names in declaration order. + fields: NameMap, pub const NameMap = EnumFull.NameMap; - pub fn srcLoc(self: EnumSimple) SrcLoc { + pub fn srcLoc(self: EnumSimple, mod: *Module) SrcLoc { + const owner_decl = mod.declPtr(self.owner_decl); return .{ - .file_scope = self.owner_decl.getFileScope(), - .parent_decl_node = self.owner_decl.src_node, + .file_scope = owner_decl.getFileScope(), + .parent_decl_node = owner_decl.src_node, .lazy = .{ .node_offset = self.node_offset }, }; } @@ -1035,7 +1018,9 @@ pub const EnumSimple = struct { /// are explicitly provided. pub const EnumNumbered = struct { /// The Decl that corresponds to the enum itself. - owner_decl: *Decl, + owner_decl: Decl.Index, + /// Offset from `owner_decl`, points to the enum decl AST node. + node_offset: i32, /// An integer type which is used for the numerical value of the enum. /// Whether zig chooses this type or the user specifies it, it is stored here. tag_ty: Type, @@ -1045,16 +1030,15 @@ pub const EnumNumbered = struct { /// Entries are in declaration order, same as `fields`. /// If this hash map is empty, it means the enum tags are auto-numbered. values: ValueMap, - /// Offset from `owner_decl`, points to the enum decl AST node. - node_offset: i32, pub const NameMap = EnumFull.NameMap; pub const ValueMap = EnumFull.ValueMap; - pub fn srcLoc(self: EnumNumbered) SrcLoc { + pub fn srcLoc(self: EnumNumbered, mod: *Module) SrcLoc { + const owner_decl = mod.declPtr(self.owner_decl); return .{ - .file_scope = self.owner_decl.getFileScope(), - .parent_decl_node = self.owner_decl.src_node, + .file_scope = owner_decl.getFileScope(), + .parent_decl_node = owner_decl.src_node, .lazy = .{ .node_offset = self.node_offset }, }; } @@ -1064,7 +1048,9 @@ pub const EnumNumbered = struct { /// at least one tag value explicitly specified, or at least one declaration. pub const EnumFull = struct { /// The Decl that corresponds to the enum itself. - owner_decl: *Decl, + owner_decl: Decl.Index, + /// Offset from `owner_decl`, points to the enum decl AST node. + node_offset: i32, /// An integer type which is used for the numerical value of the enum. /// Whether zig chooses this type or the user specifies it, it is stored here. tag_ty: Type, @@ -1076,26 +1062,23 @@ pub const EnumFull = struct { values: ValueMap, /// Represents the declarations inside this enum. namespace: Namespace, - /// Offset from `owner_decl`, points to the enum decl AST node. - node_offset: i32, /// true if zig inferred this tag type, false if user specified it tag_ty_inferred: bool, pub const NameMap = std.StringArrayHashMapUnmanaged(void); pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.ArrayHashContext, false); - pub fn srcLoc(self: EnumFull) SrcLoc { + pub fn srcLoc(self: EnumFull, mod: *Module) SrcLoc { + const owner_decl = mod.declPtr(self.owner_decl); return .{ - .file_scope = self.owner_decl.getFileScope(), - .parent_decl_node = self.owner_decl.src_node, + .file_scope = owner_decl.getFileScope(), + .parent_decl_node = owner_decl.src_node, .lazy = .{ .node_offset = self.node_offset }, }; } }; pub const Union = struct { - /// The Decl that corresponds to the union itself. - owner_decl: *Decl, /// An enum type which is used for the tag of the union. /// This type is created even for untagged unions, even when the memory /// layout does not store the tag. @@ -1106,6 +1089,8 @@ pub const Union = struct { fields: Fields, /// Represents the declarations inside this union. namespace: Namespace, + /// The Decl that corresponds to the union itself. + owner_decl: Decl.Index, /// Offset from `owner_decl`, points to the union decl AST node. node_offset: i32, /// Index of the union_decl ZIR instruction. @@ -1145,30 +1130,32 @@ pub const Union = struct { pub const Fields = std.StringArrayHashMapUnmanaged(Field); - pub fn getFullyQualifiedName(s: *Union, gpa: Allocator) ![:0]u8 { - return s.owner_decl.getFullyQualifiedName(gpa); + pub fn getFullyQualifiedName(s: *Union, mod: *Module) ![:0]u8 { + return mod.declPtr(s.owner_decl).getFullyQualifiedName(mod); } - pub fn srcLoc(self: Union) SrcLoc { + pub fn srcLoc(self: Union, mod: *Module) SrcLoc { + const owner_decl = mod.declPtr(self.owner_decl); return .{ - .file_scope = self.owner_decl.getFileScope(), - .parent_decl_node = self.owner_decl.src_node, + .file_scope = owner_decl.getFileScope(), + .parent_decl_node = owner_decl.src_node, .lazy = .{ .node_offset = self.node_offset }, }; } - pub fn fieldSrcLoc(u: Union, gpa: Allocator, query: FieldSrcQuery) SrcLoc { + pub fn fieldSrcLoc(u: Union, mod: *Module, query: FieldSrcQuery) SrcLoc { @setCold(true); - const tree = u.owner_decl.getFileScope().getTree(gpa) catch |err| { + const owner_decl = mod.declPtr(u.owner_decl); + const file = owner_decl.getFileScope(); + const tree = file.getTree(mod.gpa) catch |err| { // In this case we emit a warning + a less precise source location. log.warn("unable to load {s}: {s}", .{ - u.owner_decl.getFileScope().sub_file_path, @errorName(err), + file.sub_file_path, @errorName(err), }); - return u.srcLoc(); + return u.srcLoc(mod); }; - const node = u.owner_decl.relativeToNodeIndex(u.node_offset); + const node = owner_decl.relativeToNodeIndex(u.node_offset); const node_tags = tree.nodes.items(.tag); - const file = u.owner_decl.getFileScope(); switch (node_tags[node]) { .container_decl, .container_decl_trailing, @@ -1348,22 +1335,23 @@ pub const Union = struct { pub const Opaque = struct { /// The Decl that corresponds to the opaque itself. - owner_decl: *Decl, - /// Represents the declarations inside this opaque. - namespace: Namespace, + owner_decl: Decl.Index, /// Offset from `owner_decl`, points to the opaque decl AST node. node_offset: i32, + /// Represents the declarations inside this opaque. + namespace: Namespace, - pub fn srcLoc(self: Opaque) SrcLoc { + pub fn srcLoc(self: Opaque, mod: *Module) SrcLoc { + const owner_decl = mod.declPtr(self.owner_decl); return .{ - .file_scope = self.owner_decl.getFileScope(), - .parent_decl_node = self.owner_decl.src_node, + .file_scope = owner_decl.getFileScope(), + .parent_decl_node = owner_decl.src_node, .lazy = .{ .node_offset = self.node_offset }, }; } - pub fn getFullyQualifiedName(s: *Opaque, gpa: Allocator) ![:0]u8 { - return s.owner_decl.getFullyQualifiedName(gpa); + pub fn getFullyQualifiedName(s: *Opaque, mod: *Module) ![:0]u8 { + return mod.declPtr(s.owner_decl).getFullyQualifiedName(mod); } }; @@ -1371,7 +1359,7 @@ pub const Opaque = struct { /// arena allocator. pub const ExternFn = struct { /// The Decl that corresponds to the function itself. - owner_decl: *Decl, + owner_decl: Decl.Index, /// Library name if specified. /// For example `extern "c" fn write(...) usize` would have 'c' as library name. /// Allocated with Module's allocator; outlives the ZIR code. @@ -1389,7 +1377,12 @@ pub const ExternFn = struct { /// instead. pub const Fn = struct { /// The Decl that corresponds to the function itself. - owner_decl: *Decl, + owner_decl: Decl.Index, + /// The ZIR instruction that is a function instruction. Use this to find + /// the body. We store this rather than the body directly so that when ZIR + /// is regenerated on update(), we can map this to the new corresponding + /// ZIR instruction. + zir_body_inst: Zir.Inst.Index, /// If this is not null, this function is a generic function instantiation, and /// there is a `TypedValue` here for each parameter of the function. /// Non-comptime parameters are marked with a `generic_poison` for the value. @@ -1403,11 +1396,6 @@ pub const Fn = struct { /// parameter and tells whether it is anytype. /// TODO apply the same enhancement for param_names below to this field. anytype_args: [*]bool, - /// The ZIR instruction that is a function instruction. Use this to find - /// the body. We store this rather than the body directly so that when ZIR - /// is regenerated on update(), we can map this to the new corresponding - /// ZIR instruction. - zir_body_inst: Zir.Inst.Index, /// Prefer to use `getParamName` to access this because of the future improvement /// we want to do mentioned in the TODO below. @@ -1537,8 +1525,9 @@ pub const Fn = struct { return func.param_names[index]; } - pub fn hasInferredErrorSet(func: Fn) bool { - const zir = func.owner_decl.getFileScope().zir; + pub fn hasInferredErrorSet(func: Fn, mod: *Module) bool { + const owner_decl = mod.declPtr(func.owner_decl); + const zir = owner_decl.getFileScope().zir; const zir_tags = zir.instructions.items(.tag); switch (zir_tags[func.zir_body_inst]) { .func => return false, @@ -1556,7 +1545,7 @@ pub const Fn = struct { pub const Var = struct { /// if is_extern == true this is undefined init: Value, - owner_decl: *Decl, + owner_decl: Decl.Index, /// Library name if specified. /// For example `extern "c" var stderrp = ...` would have 'c' as library name. @@ -1576,14 +1565,16 @@ pub const Var = struct { }; pub const DeclAdapter = struct { + mod: *Module, + pub fn hash(self: @This(), s: []const u8) u32 { _ = self; return @truncate(u32, std.hash.Wyhash.hash(0, s)); } - pub fn eql(self: @This(), a: []const u8, b_decl: *Decl, b_index: usize) bool { - _ = self; + pub fn eql(self: @This(), a: []const u8, b_decl_index: Decl.Index, b_index: usize) bool { _ = b_index; + const b_decl = self.mod.declPtr(b_decl_index); return mem.eql(u8, a, mem.sliceTo(b_decl.name, 0)); } }; @@ -1599,25 +1590,30 @@ pub const Namespace = struct { /// Declaration order is preserved via entry order. /// Key memory is owned by `decl.name`. /// Anonymous decls are not stored here; they are kept in `anon_decls` instead. - decls: std.ArrayHashMapUnmanaged(*Decl, void, DeclContext, true) = .{}, + decls: std.ArrayHashMapUnmanaged(Decl.Index, void, DeclContext, true) = .{}, - anon_decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, + anon_decls: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{}, /// Key is usingnamespace Decl itself. To find the namespace being included, /// the Decl Value has to be resolved as a Type which has a Namespace. /// Value is whether the usingnamespace decl is marked `pub`. - usingnamespace_set: std.AutoHashMapUnmanaged(*Decl, bool) = .{}, + usingnamespace_set: std.AutoHashMapUnmanaged(Decl.Index, bool) = .{}, const DeclContext = struct { - pub fn hash(self: @This(), decl: *Decl) u32 { - _ = self; + module: *Module, + + pub fn hash(ctx: @This(), decl_index: Decl.Index) u32 { + const decl = ctx.module.declPtr(decl_index); return @truncate(u32, std.hash.Wyhash.hash(0, mem.sliceTo(decl.name, 0))); } - pub fn eql(self: @This(), a: *Decl, b: *Decl, b_index: usize) bool { - _ = self; + pub fn eql(ctx: @This(), a_decl_index: Decl.Index, b_decl_index: Decl.Index, b_index: usize) bool { _ = b_index; - return mem.eql(u8, mem.sliceTo(a.name, 0), mem.sliceTo(b.name, 0)); + const a_decl = ctx.module.declPtr(a_decl_index); + const b_decl = ctx.module.declPtr(b_decl_index); + const a_name = mem.sliceTo(a_decl.name, 0); + const b_name = mem.sliceTo(b_decl.name, 0); + return mem.eql(u8, a_name, b_name); } }; @@ -1637,13 +1633,13 @@ pub const Namespace = struct { var anon_decls = ns.anon_decls; ns.anon_decls = .{}; - for (decls.keys()) |decl| { - decl.destroy(mod); + for (decls.keys()) |decl_index| { + mod.destroyDecl(decl_index); } decls.deinit(gpa); for (anon_decls.keys()) |key| { - key.destroy(mod); + mod.destroyDecl(key); } anon_decls.deinit(gpa); ns.usingnamespace_set.deinit(gpa); @@ -1652,7 +1648,7 @@ pub const Namespace = struct { pub fn deleteAllDecls( ns: *Namespace, mod: *Module, - outdated_decls: ?*std.AutoArrayHashMap(*Decl, void), + outdated_decls: ?*std.AutoArrayHashMap(Decl.Index, void), ) !void { const gpa = mod.gpa; @@ -1669,13 +1665,13 @@ pub const Namespace = struct { for (decls.keys()) |child_decl| { mod.clearDecl(child_decl, outdated_decls) catch @panic("out of memory"); - child_decl.destroy(mod); + mod.destroyDecl(child_decl); } decls.deinit(gpa); for (anon_decls.keys()) |child_decl| { mod.clearDecl(child_decl, outdated_decls) catch @panic("out of memory"); - child_decl.destroy(mod); + mod.destroyDecl(child_decl); } anon_decls.deinit(gpa); @@ -1685,12 +1681,14 @@ pub const Namespace = struct { // This renders e.g. "std.fs.Dir.OpenOptions" pub fn renderFullyQualifiedName( ns: Namespace, + mod: *Module, name: []const u8, writer: anytype, ) @TypeOf(writer).Error!void { if (ns.parent) |parent| { - const decl = ns.getDecl(); - try parent.renderFullyQualifiedName(mem.sliceTo(decl.name, 0), writer); + const decl_index = ns.getDeclIndex(); + const decl = mod.declPtr(decl_index); + try parent.renderFullyQualifiedName(mod, mem.sliceTo(decl.name, 0), writer); } else { try ns.file_scope.renderFullyQualifiedName(writer); } @@ -1703,13 +1701,15 @@ pub const Namespace = struct { /// This renders e.g. "std/fs.zig:Dir.OpenOptions" pub fn renderFullyQualifiedDebugName( ns: Namespace, + mod: *Module, name: []const u8, writer: anytype, ) @TypeOf(writer).Error!void { var separator_char: u8 = '.'; if (ns.parent) |parent| { - const decl = ns.getDecl(); - try parent.renderFullyQualifiedDebugName(mem.sliceTo(decl.name, 0), writer); + const decl_index = ns.getDeclIndex(); + const decl = mod.declPtr(decl_index); + try parent.renderFullyQualifiedDebugName(mod, mem.sliceTo(decl.name, 0), writer); } else { try ns.file_scope.renderFullyQualifiedDebugName(writer); separator_char = ':'; @@ -1720,12 +1720,14 @@ pub const Namespace = struct { } } - pub fn getDecl(ns: Namespace) *Decl { + pub fn getDeclIndex(ns: Namespace) Decl.Index { return ns.ty.getOwnerDecl(); } }; pub const File = struct { + /// The Decl of the struct that represents this File. + root_decl: Decl.OptionalIndex, status: enum { never_loaded, retryable_failure, @@ -1749,16 +1751,14 @@ pub const File = struct { zir: Zir, /// Package that this file is a part of, managed externally. pkg: *Package, - /// The Decl of the struct that represents this File. - root_decl: ?*Decl, /// Used by change detection algorithm, after astgen, contains the /// set of decls that existed in the previous ZIR but not in the new one. - deleted_decls: std.ArrayListUnmanaged(*Decl) = .{}, + deleted_decls: std.ArrayListUnmanaged(Decl.Index) = .{}, /// Used by change detection algorithm, after astgen, contains the /// set of decls that existed both in the previous ZIR and in the new one, /// but their source code has been modified. - outdated_decls: std.ArrayListUnmanaged(*Decl) = .{}, + outdated_decls: std.ArrayListUnmanaged(Decl.Index) = .{}, /// The most recent successful ZIR for this file, with no errors. /// This is only populated when a previously successful ZIR @@ -1798,8 +1798,8 @@ pub const File = struct { log.debug("deinit File {s}", .{file.sub_file_path}); file.deleted_decls.deinit(gpa); file.outdated_decls.deinit(gpa); - if (file.root_decl) |root_decl| { - root_decl.destroy(mod); + if (file.root_decl.unwrap()) |root_decl| { + mod.destroyDecl(root_decl); } gpa.free(file.sub_file_path); file.unload(gpa); @@ -1932,7 +1932,7 @@ pub const EmbedFile = struct { /// The Decl that was created from the `@embedFile` to own this resource. /// This is how zig knows what other Decl objects to invalidate if the file /// changes on disk. - owner_decl: *Decl, + owner_decl: Decl.Index, fn destroy(embed_file: *EmbedFile, mod: *Module) void { const gpa = mod.gpa; @@ -2776,6 +2776,7 @@ pub fn deinit(mod: *Module) void { } emit_h.failed_decls.deinit(gpa); emit_h.decl_table.deinit(gpa); + emit_h.allocated_emit_h.deinit(gpa); gpa.destroy(emit_h); } @@ -2827,6 +2828,52 @@ pub fn deinit(mod: *Module) void { } mod.memoized_calls.deinit(gpa); } + + mod.decls_free_list.deinit(gpa); + mod.allocated_decls.deinit(gpa); +} + +pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void { + const gpa = mod.gpa; + { + const decl = mod.declPtr(decl_index); + log.debug("destroy {*} ({s})", .{ decl, decl.name }); + _ = mod.test_functions.swapRemove(decl_index); + if (decl.deletion_flag) { + assert(mod.deletion_set.swapRemove(decl_index)); + } + if (decl.has_tv) { + if (decl.getInnerNamespace()) |namespace| { + namespace.destroyDecls(mod); + } + decl.clearValues(gpa); + } + decl.dependants.deinit(gpa); + decl.dependencies.deinit(gpa); + decl.clearName(gpa); + decl.* = undefined; + } + mod.decls_free_list.append(gpa, decl_index) catch { + // In order to keep `destroyDecl` a non-fallible function, we ignore memory + // allocation failures here, instead leaking the Decl until garbage collection. + }; + if (mod.emit_h) |mod_emit_h| { + const decl_emit_h = mod_emit_h.declPtr(decl_index); + decl_emit_h.fwd_decl.deinit(gpa); + decl_emit_h.* = undefined; + } +} + +pub fn declPtr(mod: *Module, decl_index: Decl.Index) *Decl { + return mod.allocated_decls.at(@enumToInt(decl_index)); +} + +/// Returns true if and only if the Decl is the top level struct associated with a File. +pub fn declIsRoot(mod: *Module, decl_index: Decl.Index) bool { + const decl = mod.declPtr(decl_index); + if (decl.src_namespace.parent != null) + return false; + return decl_index == decl.src_namespace.getDeclIndex(); } fn freeExportList(gpa: Allocator, export_list: []*Export) void { @@ -3230,14 +3277,14 @@ pub fn astGenFile(mod: *Module, file: *File) !void { // We do not need to hold any locks at this time because all the Decl and Namespace // objects being touched are specific to this File, and the only other concurrent // tasks are touching other File objects. - try updateZirRefs(gpa, file, prev_zir.*); + try updateZirRefs(mod, file, prev_zir.*); // At this point, `file.outdated_decls` and `file.deleted_decls` are populated, // and semantic analysis will deal with them properly. // No need to keep previous ZIR. prev_zir.deinit(gpa); gpa.destroy(prev_zir); file.prev_zir = null; - } else if (file.root_decl) |root_decl| { + } else if (file.root_decl.unwrap()) |root_decl| { // This is an update, but it is the first time the File has succeeded // ZIR. We must mark it outdated since we have already tried to // semantically analyze it. @@ -3251,7 +3298,8 @@ pub fn astGenFile(mod: *Module, file: *File) !void { /// * Decl.zir_index /// * Fn.zir_body_inst /// * Decl.zir_decl_index -fn updateZirRefs(gpa: Allocator, file: *File, old_zir: Zir) !void { +fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void { + const gpa = mod.gpa; const new_zir = file.zir; // Maps from old ZIR to new ZIR, struct_decl, enum_decl, etc. Any instruction which @@ -3268,10 +3316,10 @@ fn updateZirRefs(gpa: Allocator, file: *File, old_zir: Zir) !void { // Walk the Decl graph, updating ZIR indexes, strings, and populating // the deleted and outdated lists. - var decl_stack: std.ArrayListUnmanaged(*Decl) = .{}; + var decl_stack: std.ArrayListUnmanaged(Decl.Index) = .{}; defer decl_stack.deinit(gpa); - const root_decl = file.root_decl.?; + const root_decl = file.root_decl.unwrap().?; try decl_stack.append(gpa, root_decl); file.deleted_decls.clearRetainingCapacity(); @@ -3281,7 +3329,8 @@ fn updateZirRefs(gpa: Allocator, file: *File, old_zir: Zir) !void { // to re-generate ZIR for the File. try file.outdated_decls.append(gpa, root_decl); - while (decl_stack.popOrNull()) |decl| { + while (decl_stack.popOrNull()) |decl_index| { + const decl = mod.declPtr(decl_index); // Anonymous decls and the root decl have this set to 0. We still need // to walk them but we do not need to modify this value. // Anonymous decls should not be marked outdated. They will be re-generated @@ -3292,7 +3341,7 @@ fn updateZirRefs(gpa: Allocator, file: *File, old_zir: Zir) !void { log.debug("updateZirRefs {s}: delete {*} ({s})", .{ file.sub_file_path, decl, decl.name, }); - try file.deleted_decls.append(gpa, decl); + try file.deleted_decls.append(gpa, decl_index); continue; }; const old_hash = decl.contentsHashZir(old_zir); @@ -3302,7 +3351,7 @@ fn updateZirRefs(gpa: Allocator, file: *File, old_zir: Zir) !void { log.debug("updateZirRefs {s}: outdated {*} ({s}) {d} => {d}", .{ file.sub_file_path, decl, decl.name, old_zir_decl_index, new_zir_decl_index, }); - try file.outdated_decls.append(gpa, decl); + try file.outdated_decls.append(gpa, decl_index); } else { log.debug("updateZirRefs {s}: unchanged {*} ({s}) {d} => {d}", .{ file.sub_file_path, decl, decl.name, old_zir_decl_index, new_zir_decl_index, @@ -3314,21 +3363,21 @@ fn updateZirRefs(gpa: Allocator, file: *File, old_zir: Zir) !void { if (decl.getStruct()) |struct_obj| { struct_obj.zir_index = inst_map.get(struct_obj.zir_index) orelse { - try file.deleted_decls.append(gpa, decl); + try file.deleted_decls.append(gpa, decl_index); continue; }; } if (decl.getUnion()) |union_obj| { union_obj.zir_index = inst_map.get(union_obj.zir_index) orelse { - try file.deleted_decls.append(gpa, decl); + try file.deleted_decls.append(gpa, decl_index); continue; }; } if (decl.getFunction()) |func| { func.zir_body_inst = inst_map.get(func.zir_body_inst) orelse { - try file.deleted_decls.append(gpa, decl); + try file.deleted_decls.append(gpa, decl_index); continue; }; } @@ -3485,10 +3534,12 @@ pub fn mapOldZirToNew( /// However the resolution status of the Type may not be fully resolved. /// For example an inferred error set is not resolved until after `analyzeFnBody`. /// is called. -pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void { +pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { const tracy = trace(@src()); defer tracy.end(); + const decl = mod.declPtr(decl_index); + const subsequent_analysis = switch (decl.analysis) { .in_progress => unreachable, @@ -3507,15 +3558,16 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void { // The exports this Decl performs will be re-discovered, so we remove them here // prior to re-analysis. - mod.deleteDeclExports(decl); + mod.deleteDeclExports(decl_index); // Dependencies will be re-discovered, so we remove them here prior to re-analysis. - for (decl.dependencies.keys()) |dep| { - dep.removeDependant(decl); + for (decl.dependencies.keys()) |dep_index| { + const dep = mod.declPtr(dep_index); + dep.removeDependant(decl_index); if (dep.dependants.count() == 0 and !dep.deletion_flag) { log.debug("insert {*} ({s}) dependant {*} ({s}) into deletion set", .{ decl, decl.name, dep, dep.name, }); - try mod.markDeclForDeletion(dep); + try mod.markDeclForDeletion(dep_index); } } decl.dependencies.clearRetainingCapacity(); @@ -3530,7 +3582,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void { decl_prog_node.activate(); defer decl_prog_node.end(); - const type_changed = mod.semaDecl(decl) catch |err| switch (err) { + const type_changed = mod.semaDecl(decl_index) catch |err| switch (err) { error.AnalysisFail => { if (decl.analysis == .in_progress) { // If this decl caused the compile error, the analysis field would @@ -3545,7 +3597,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void { else => |e| { decl.analysis = .sema_failure_retryable; try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1); - mod.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( + mod.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create( mod.gpa, decl.srcLoc(), "unable to analyze: {s}", @@ -3559,7 +3611,8 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void { // We may need to chase the dependants and re-analyze them. // However, if the decl is a function, and the type is the same, we do not need to. if (type_changed or decl.ty.zigTypeTag() != .Fn) { - for (decl.dependants.keys()) |dep| { + for (decl.dependants.keys()) |dep_index| { + const dep = mod.declPtr(dep_index); switch (dep.analysis) { .unreferenced => unreachable, .in_progress => continue, // already doing analysis, ok @@ -3573,7 +3626,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void { .codegen_failure_retryable, .complete, => if (dep.generation != mod.generation) { - try mod.markOutdatedDecl(dep); + try mod.markOutdatedDecl(dep_index); }, } } @@ -3585,7 +3638,10 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void { const tracy = trace(@src()); defer tracy.end(); - switch (func.owner_decl.analysis) { + const decl_index = func.owner_decl; + const decl = mod.declPtr(decl_index); + + switch (decl.analysis) { .unreferenced => unreachable, .in_progress => unreachable, .outdated => unreachable, @@ -3607,13 +3663,12 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void { } const gpa = mod.gpa; - const decl = func.owner_decl; var tmp_arena = std.heap.ArenaAllocator.init(gpa); defer tmp_arena.deinit(); const sema_arena = tmp_arena.allocator(); - var air = mod.analyzeFnBody(decl, func, sema_arena) catch |err| switch (err) { + var air = mod.analyzeFnBody(func, sema_arena) catch |err| switch (err) { error.AnalysisFail => { if (func.state == .in_progress) { // If this decl caused the compile error, the analysis field would @@ -3635,7 +3690,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void { if (builtin.mode == .Debug and mod.comp.verbose_air) { std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name}); - @import("print_air.zig").dump(gpa, air, liveness); + @import("print_air.zig").dump(mod, air, liveness); std.debug.print("# End Function AIR: {s}\n\n", .{decl.name}); } @@ -3647,7 +3702,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void { }, else => { try mod.failed_decls.ensureUnusedCapacity(gpa, 1); - mod.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create( + mod.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create( gpa, decl.srcLoc(), "unable to codegen: {s}", @@ -3668,7 +3723,9 @@ pub fn updateEmbedFile(mod: *Module, embed_file: *EmbedFile) SemaError!void { // TODO we can potentially relax this if we store some more information along // with decl dependency edges - for (embed_file.owner_decl.dependants.keys()) |dep| { + const owner_decl = mod.declPtr(embed_file.owner_decl); + for (owner_decl.dependants.keys()) |dep_index| { + const dep = mod.declPtr(dep_index); switch (dep.analysis) { .unreferenced => unreachable, .in_progress => continue, // already doing analysis, ok @@ -3682,7 +3739,7 @@ pub fn updateEmbedFile(mod: *Module, embed_file: *EmbedFile) SemaError!void { .codegen_failure_retryable, .complete, => if (dep.generation != mod.generation) { - try mod.markOutdatedDecl(dep); + try mod.markOutdatedDecl(dep_index); }, } } @@ -3699,7 +3756,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { const tracy = trace(@src()); defer tracy.end(); - if (file.root_decl != null) return; + if (file.root_decl != .none) return; const gpa = mod.gpa; var new_decl_arena = std.heap.ArenaAllocator.init(gpa); @@ -3724,10 +3781,11 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { .file_scope = file, }, }; - const decl_name = try file.fullyQualifiedNameZ(gpa); - const new_decl = try mod.allocateNewDecl(decl_name, &struct_obj.namespace, 0, null); - file.root_decl = new_decl; - struct_obj.owner_decl = new_decl; + const new_decl_index = try mod.allocateNewDecl(&struct_obj.namespace, 0, null); + const new_decl = mod.declPtr(new_decl_index); + file.root_decl = new_decl_index.toOptional(); + struct_obj.owner_decl = new_decl_index; + new_decl.name = try file.fullyQualifiedNameZ(gpa); new_decl.src_line = 0; new_decl.is_pub = true; new_decl.is_exported = false; @@ -3757,6 +3815,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { .perm_arena = new_decl_arena_allocator, .code = file.zir, .owner_decl = new_decl, + .owner_decl_index = new_decl_index, .func = null, .fn_ret_ty = Type.void, .owner_func = null, @@ -3769,7 +3828,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { var block_scope: Sema.Block = .{ .parent = null, .sema = &sema, - .src_decl = new_decl, + .src_decl = new_decl_index, .namespace = &struct_obj.namespace, .wip_capture_scope = wip_captures.scope, .instructions = .{}, @@ -3808,10 +3867,12 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { /// Returns `true` if the Decl type changed. /// Returns `true` if this is the first time analyzing the Decl. /// Returns `false` otherwise. -fn semaDecl(mod: *Module, decl: *Decl) !bool { +fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { const tracy = trace(@src()); defer tracy.end(); + const decl = mod.declPtr(decl_index); + if (decl.getFileScope().status != .success_zir) { return error.AnalysisFail; } @@ -3838,13 +3899,14 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { .perm_arena = decl_arena_allocator, .code = zir, .owner_decl = decl, + .owner_decl_index = decl_index, .func = null, .fn_ret_ty = Type.void, .owner_func = null, }; defer sema.deinit(); - if (decl.isRoot()) { + if (mod.declIsRoot(decl_index)) { log.debug("semaDecl root {*} ({s})", .{ decl, decl.name }); const main_struct_inst = Zir.main_struct_inst; const struct_obj = decl.getStruct().?; @@ -3864,7 +3926,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { var block_scope: Sema.Block = .{ .parent = null, .sema = &sema, - .src_decl = decl, + .src_decl = decl_index, .namespace = decl.src_namespace, .wip_capture_scope = wip_captures.scope, .instructions = .{}, @@ -3922,15 +3984,15 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { const decl_arena_state = try decl_arena_allocator.create(std.heap.ArenaAllocator.State); if (decl.is_usingnamespace) { - if (!decl_tv.ty.eql(Type.type, target)) { + if (!decl_tv.ty.eql(Type.type, mod)) { return sema.fail(&block_scope, src, "expected type, found {}", .{ - decl_tv.ty.fmt(target), + decl_tv.ty.fmt(mod), }); } var buffer: Value.ToTypeBuffer = undefined; const ty = try decl_tv.val.toType(&buffer).copy(decl_arena_allocator); if (ty.getNamespace() == null) { - return sema.fail(&block_scope, src, "type {} has no namespace", .{ty.fmt(target)}); + return sema.fail(&block_scope, src, "type {} has no namespace", .{ty.fmt(mod)}); } decl.ty = Type.type; @@ -3949,7 +4011,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { if (decl_tv.val.castTag(.function)) |fn_payload| { const func = fn_payload.data; - const owns_tv = func.owner_decl == decl; + const owns_tv = func.owner_decl == decl_index; if (owns_tv) { var prev_type_has_bits = false; var prev_is_inline = false; @@ -3957,7 +4019,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { if (decl.has_tv) { prev_type_has_bits = decl.ty.isFnOrHasRuntimeBits(); - type_changed = !decl.ty.eql(decl_tv.ty, target); + type_changed = !decl.ty.eql(decl_tv.ty, mod); if (decl.getFunction()) |prev_func| { prev_is_inline = prev_func.state == .inline_only; } @@ -3982,13 +4044,13 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { // We don't fully codegen the decl until later, but we do need to reserve a global // offset table index for it. This allows us to codegen decls out of dependency // order, increasing how many computations can be done in parallel. - try mod.comp.bin_file.allocateDeclIndexes(decl); + try mod.comp.bin_file.allocateDeclIndexes(decl_index); try mod.comp.work_queue.writeItem(.{ .codegen_func = func }); if (type_changed and mod.emit_h != null) { - try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); + try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index }); } } else if (!prev_is_inline and prev_type_has_bits) { - mod.comp.bin_file.freeDecl(decl); + mod.comp.bin_file.freeDecl(decl_index); } const is_inline = decl.ty.fnCallingConvention() == .Inline; @@ -3999,14 +4061,14 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { } // The scope needs to have the decl in it. const options: std.builtin.ExportOptions = .{ .name = mem.sliceTo(decl.name, 0) }; - try sema.analyzeExport(&block_scope, export_src, options, decl); + try sema.analyzeExport(&block_scope, export_src, options, decl_index); } return type_changed or is_inline != prev_is_inline; } } var type_changed = true; if (decl.has_tv) { - type_changed = !decl.ty.eql(decl_tv.ty, target); + type_changed = !decl.ty.eql(decl_tv.ty, mod); decl.clearValues(gpa); } @@ -4016,7 +4078,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { switch (decl_tv.val.tag()) { .variable => { const variable = decl_tv.val.castTag(.variable).?.data; - if (variable.owner_decl == decl) { + if (variable.owner_decl == decl_index) { decl.owns_tv = true; queue_linker_work = true; @@ -4026,7 +4088,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { }, .extern_fn => { const extern_fn = decl_tv.val.castTag(.extern_fn).?.data; - if (extern_fn.owner_decl == decl) { + if (extern_fn.owner_decl == decl_index) { decl.owns_tv = true; queue_linker_work = true; is_extern = true; @@ -4065,11 +4127,11 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { // codegen backend wants full access to the Decl Type. try sema.resolveTypeFully(&block_scope, src, decl.ty); - try mod.comp.bin_file.allocateDeclIndexes(decl); - try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl }); + try mod.comp.bin_file.allocateDeclIndexes(decl_index); + try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index }); if (type_changed and mod.emit_h != null) { - try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); + try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index }); } } @@ -4077,15 +4139,18 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { const export_src = src; // TODO point to the export token // The scope needs to have the decl in it. const options: std.builtin.ExportOptions = .{ .name = mem.sliceTo(decl.name, 0) }; - try sema.analyzeExport(&block_scope, export_src, options, decl); + try sema.analyzeExport(&block_scope, export_src, options, decl_index); } return type_changed; } /// Returns the depender's index of the dependee. -pub fn declareDeclDependency(mod: *Module, depender: *Decl, dependee: *Decl) !void { - if (depender == dependee) return; +pub fn declareDeclDependency(mod: *Module, depender_index: Decl.Index, dependee_index: Decl.Index) !void { + if (depender_index == dependee_index) return; + + const depender = mod.declPtr(depender_index); + const dependee = mod.declPtr(dependee_index); log.debug("{*} ({s}) depends on {*} ({s})", .{ depender, depender.name, dependee, dependee.name, @@ -4096,11 +4161,11 @@ pub fn declareDeclDependency(mod: *Module, depender: *Decl, dependee: *Decl) !vo if (dependee.deletion_flag) { dependee.deletion_flag = false; - assert(mod.deletion_set.swapRemove(dependee)); + assert(mod.deletion_set.swapRemove(dependee_index)); } - dependee.dependants.putAssumeCapacity(depender, {}); - depender.dependencies.putAssumeCapacity(dependee, {}); + dependee.dependants.putAssumeCapacity(depender_index, {}); + depender.dependencies.putAssumeCapacity(dependee_index, {}); } pub const ImportFileResult = struct { @@ -4146,7 +4211,7 @@ pub fn importPkg(mod: *Module, pkg: *Package) !ImportFileResult { .zir = undefined, .status = .never_loaded, .pkg = pkg, - .root_decl = null, + .root_decl = .none, }; return ImportFileResult{ .file = new_file, @@ -4214,7 +4279,7 @@ pub fn importFile( .zir = undefined, .status = .never_loaded, .pkg = cur_file.pkg, - .root_decl = null, + .root_decl = .none, }; return ImportFileResult{ .file = new_file, @@ -4388,8 +4453,8 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi const line = iter.parent_decl.relativeToLine(line_off); const decl_name_index = zir.extra[decl_sub_index + 5]; const decl_doccomment_index = zir.extra[decl_sub_index + 7]; - const decl_index = zir.extra[decl_sub_index + 6]; - const decl_block_inst_data = zir.instructions.items(.data)[decl_index].pl_node; + const decl_zir_index = zir.extra[decl_sub_index + 6]; + const decl_block_inst_data = zir.instructions.items(.data)[decl_zir_index].pl_node; const decl_node = iter.parent_decl.relativeToNodeIndex(decl_block_inst_data.src_node); // Every Decl needs a name. @@ -4432,15 +4497,22 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi if (is_usingnamespace) try namespace.usingnamespace_set.ensureUnusedCapacity(gpa, 1); // We create a Decl for it regardless of analysis status. - const gop = try namespace.decls.getOrPutAdapted(gpa, @as([]const u8, mem.sliceTo(decl_name, 0)), DeclAdapter{}); + const gop = try namespace.decls.getOrPutContextAdapted( + gpa, + @as([]const u8, mem.sliceTo(decl_name, 0)), + DeclAdapter{ .mod = mod }, + Namespace.DeclContext{ .module = mod }, + ); if (!gop.found_existing) { - const new_decl = try mod.allocateNewDecl(decl_name, namespace, decl_node, iter.parent_decl.src_scope); + const new_decl_index = try mod.allocateNewDecl(namespace, decl_node, iter.parent_decl.src_scope); + const new_decl = mod.declPtr(new_decl_index); + new_decl.name = decl_name; if (is_usingnamespace) { - namespace.usingnamespace_set.putAssumeCapacity(new_decl, is_pub); + namespace.usingnamespace_set.putAssumeCapacity(new_decl_index, is_pub); } log.debug("scan new {*} ({s}) into {*}", .{ new_decl, decl_name, namespace }); new_decl.src_line = line; - gop.key_ptr.* = new_decl; + gop.key_ptr.* = new_decl_index; // Exported decls, comptime decls, usingnamespace decls, and // test decls if in test mode, get analyzed. const decl_pkg = namespace.file_scope.pkg; @@ -4451,7 +4523,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi // the test name filter. if (!mod.comp.bin_file.options.is_test) break :blk false; if (decl_pkg != mod.main_pkg) break :blk false; - try mod.test_functions.put(gpa, new_decl, {}); + try mod.test_functions.put(gpa, new_decl_index, {}); break :blk true; }, else => blk: { @@ -4459,12 +4531,12 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi if (!mod.comp.bin_file.options.is_test) break :blk false; if (decl_pkg != mod.main_pkg) break :blk false; // TODO check the name against --test-filter - try mod.test_functions.put(gpa, new_decl, {}); + try mod.test_functions.put(gpa, new_decl_index, {}); break :blk true; }, }; if (want_analysis) { - mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); + mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl_index }); } new_decl.is_pub = is_pub; new_decl.is_exported = is_exported; @@ -4476,7 +4548,8 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi return; } gpa.free(decl_name); - const decl = gop.key_ptr.*; + const decl_index = gop.key_ptr.*; + const decl = mod.declPtr(decl_index); log.debug("scan existing {*} ({s}) of {*}", .{ decl, decl.name, namespace }); // Update the AST node of the decl; even if its contents are unchanged, it may // have been re-ordered. @@ -4497,17 +4570,17 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi .elf => if (decl.fn_link.elf.len != 0) { // TODO Look into detecting when this would be unnecessary by storing enough state // in `Decl` to notice that the line number did not change. - mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); + mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); }, .macho => if (decl.fn_link.macho.len != 0) { // TODO Look into detecting when this would be unnecessary by storing enough state // in `Decl` to notice that the line number did not change. - mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); + mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); }, .plan9 => { // TODO Look into detecting when this would be unnecessary by storing enough state // in `Decl` to notice that the line number did not change. - mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); + mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); }, .c, .wasm, .spirv, .nvptx => {}, } @@ -4517,25 +4590,27 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi /// Make it as if the semantic analysis for this Decl never happened. pub fn clearDecl( mod: *Module, - decl: *Decl, - outdated_decls: ?*std.AutoArrayHashMap(*Decl, void), + decl_index: Decl.Index, + outdated_decls: ?*std.AutoArrayHashMap(Decl.Index, void), ) Allocator.Error!void { const tracy = trace(@src()); defer tracy.end(); + const decl = mod.declPtr(decl_index); log.debug("clearing {*} ({s})", .{ decl, decl.name }); const gpa = mod.gpa; try mod.deletion_set.ensureUnusedCapacity(gpa, decl.dependencies.count()); if (outdated_decls) |map| { - _ = map.swapRemove(decl); + _ = map.swapRemove(decl_index); try map.ensureUnusedCapacity(decl.dependants.count()); } // Remove itself from its dependencies. - for (decl.dependencies.keys()) |dep| { - dep.removeDependant(decl); + for (decl.dependencies.keys()) |dep_index| { + const dep = mod.declPtr(dep_index); + dep.removeDependant(decl_index); if (dep.dependants.count() == 0 and !dep.deletion_flag) { log.debug("insert {*} ({s}) dependant {*} ({s}) into deletion set", .{ decl, decl.name, dep, dep.name, @@ -4543,35 +4618,36 @@ pub fn clearDecl( // We don't recursively perform a deletion here, because during the update, // another reference to it may turn up. dep.deletion_flag = true; - mod.deletion_set.putAssumeCapacity(dep, {}); + mod.deletion_set.putAssumeCapacity(dep_index, {}); } } decl.dependencies.clearRetainingCapacity(); // Anything that depends on this deleted decl needs to be re-analyzed. - for (decl.dependants.keys()) |dep| { - dep.removeDependency(decl); + for (decl.dependants.keys()) |dep_index| { + const dep = mod.declPtr(dep_index); + dep.removeDependency(decl_index); if (outdated_decls) |map| { - map.putAssumeCapacity(dep, {}); + map.putAssumeCapacity(dep_index, {}); } } decl.dependants.clearRetainingCapacity(); - if (mod.failed_decls.fetchSwapRemove(decl)) |kv| { + if (mod.failed_decls.fetchSwapRemove(decl_index)) |kv| { kv.value.destroy(gpa); } if (mod.emit_h) |emit_h| { - if (emit_h.failed_decls.fetchSwapRemove(decl)) |kv| { + if (emit_h.failed_decls.fetchSwapRemove(decl_index)) |kv| { kv.value.destroy(gpa); } - assert(emit_h.decl_table.swapRemove(decl)); + assert(emit_h.decl_table.swapRemove(decl_index)); } - _ = mod.compile_log_decls.swapRemove(decl); - mod.deleteDeclExports(decl); + _ = mod.compile_log_decls.swapRemove(decl_index); + mod.deleteDeclExports(decl_index); if (decl.has_tv) { if (decl.ty.isFnOrHasRuntimeBits()) { - mod.comp.bin_file.freeDecl(decl); + mod.comp.bin_file.freeDecl(decl_index); // TODO instead of a union, put this memory trailing Decl objects, // and allow it to be variably sized. @@ -4604,15 +4680,16 @@ pub fn clearDecl( if (decl.deletion_flag) { decl.deletion_flag = false; - assert(mod.deletion_set.swapRemove(decl)); + assert(mod.deletion_set.swapRemove(decl_index)); } decl.analysis = .unreferenced; } /// This function is exclusively called for anonymous decls. -pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void { - log.debug("deleteUnusedDecl {*} ({s})", .{ decl, decl.name }); +pub fn deleteUnusedDecl(mod: *Module, decl_index: Decl.Index) void { + const decl = mod.declPtr(decl_index); + log.debug("deleteUnusedDecl {d} ({s})", .{ decl_index, decl.name }); // TODO: remove `allocateDeclIndexes` and make the API that the linker backends // are required to notice the first time `updateDecl` happens and keep track @@ -4626,55 +4703,58 @@ pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void { .c => {}, // this linker backend has already migrated to the new API else => if (decl.has_tv) { if (decl.ty.isFnOrHasRuntimeBits()) { - mod.comp.bin_file.freeDecl(decl); + mod.comp.bin_file.freeDecl(decl_index); } }, } - assert(!decl.isRoot()); - assert(decl.src_namespace.anon_decls.swapRemove(decl)); + assert(!mod.declIsRoot(decl_index)); + assert(decl.src_namespace.anon_decls.swapRemove(decl_index)); const dependants = decl.dependants.keys(); for (dependants) |dep| { - dep.removeDependency(decl); + mod.declPtr(dep).removeDependency(decl_index); } for (decl.dependencies.keys()) |dep| { - dep.removeDependant(decl); + mod.declPtr(dep).removeDependant(decl_index); } - decl.destroy(mod); + mod.destroyDecl(decl_index); } /// We don't perform a deletion here, because this Decl or another one /// may end up referencing it before the update is complete. -fn markDeclForDeletion(mod: *Module, decl: *Decl) !void { +fn markDeclForDeletion(mod: *Module, decl_index: Decl.Index) !void { + const decl = mod.declPtr(decl_index); decl.deletion_flag = true; - try mod.deletion_set.put(mod.gpa, decl, {}); + try mod.deletion_set.put(mod.gpa, decl_index, {}); } /// Cancel the creation of an anon decl and delete any references to it. /// If other decls depend on this decl, they must be aborted first. -pub fn abortAnonDecl(mod: *Module, decl: *Decl) void { +pub fn abortAnonDecl(mod: *Module, decl_index: Decl.Index) void { + const decl = mod.declPtr(decl_index); log.debug("abortAnonDecl {*} ({s})", .{ decl, decl.name }); - assert(!decl.isRoot()); - assert(decl.src_namespace.anon_decls.swapRemove(decl)); + assert(!mod.declIsRoot(decl_index)); + assert(decl.src_namespace.anon_decls.swapRemove(decl_index)); // An aborted decl must not have dependants -- they must have // been aborted first and removed from this list. assert(decl.dependants.count() == 0); - for (decl.dependencies.keys()) |dep| { - dep.removeDependant(decl); + for (decl.dependencies.keys()) |dep_index| { + const dep = mod.declPtr(dep_index); + dep.removeDependant(decl_index); } - decl.destroy(mod); + mod.destroyDecl(decl_index); } /// Delete all the Export objects that are caused by this Decl. Re-analysis of /// this Decl will cause them to be re-created (or not). -fn deleteDeclExports(mod: *Module, decl: *Decl) void { - const kv = mod.export_owners.fetchSwapRemove(decl) orelse return; +fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void { + const kv = mod.export_owners.fetchSwapRemove(decl_index) orelse return; for (kv.value) |exp| { if (mod.decl_exports.getPtr(exp.exported_decl)) |value_ptr| { @@ -4683,7 +4763,7 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void { var i: usize = 0; var new_len = list.len; while (i < new_len) { - if (list[i].owner_decl == decl) { + if (list[i].owner_decl == decl_index) { mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]); new_len -= 1; } else { @@ -4713,11 +4793,13 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void { mod.gpa.free(kv.value); } -pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) SemaError!Air { +pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air { const tracy = trace(@src()); defer tracy.end(); const gpa = mod.gpa; + const decl_index = func.owner_decl; + const decl = mod.declPtr(decl_index); // Use the Decl's arena for captured values. var decl_arena = decl.value_arena.?.promote(gpa); @@ -4731,8 +4813,9 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem .perm_arena = decl_arena_allocator, .code = decl.getFileScope().zir, .owner_decl = decl, + .owner_decl_index = decl_index, .func = func, - .fn_ret_ty = func.owner_decl.ty.fnReturnType(), + .fn_ret_ty = decl.ty.fnReturnType(), .owner_func = func, }; defer sema.deinit(); @@ -4748,7 +4831,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem var inner_block: Sema.Block = .{ .parent = null, .sema = &sema, - .src_decl = decl, + .src_decl = decl_index, .namespace = decl.src_namespace, .wip_capture_scope = wip_captures.scope, .instructions = .{}, @@ -4903,10 +4986,11 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem }; } -fn markOutdatedDecl(mod: *Module, decl: *Decl) !void { +fn markOutdatedDecl(mod: *Module, decl_index: Decl.Index) !void { + const decl = mod.declPtr(decl_index); log.debug("mark outdated {*} ({s})", .{ decl, decl.name }); - try mod.comp.work_queue.writeItem(.{ .analyze_decl = decl }); - if (mod.failed_decls.fetchSwapRemove(decl)) |kv| { + try mod.comp.work_queue.writeItem(.{ .analyze_decl = decl_index }); + if (mod.failed_decls.fetchSwapRemove(decl_index)) |kv| { kv.value.destroy(mod.gpa); } if (decl.has_tv and decl.owns_tv) { @@ -4916,33 +5000,43 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void { } } if (mod.emit_h) |emit_h| { - if (emit_h.failed_decls.fetchSwapRemove(decl)) |kv| { + if (emit_h.failed_decls.fetchSwapRemove(decl_index)) |kv| { kv.value.destroy(mod.gpa); } } - _ = mod.compile_log_decls.swapRemove(decl); + _ = mod.compile_log_decls.swapRemove(decl_index); decl.analysis = .outdated; } pub fn allocateNewDecl( mod: *Module, - name: [:0]const u8, namespace: *Namespace, src_node: Ast.Node.Index, src_scope: ?*CaptureScope, -) !*Decl { - // If we have emit-h then we must allocate a bigger structure to store the emit-h state. - const new_decl: *Decl = if (mod.emit_h != null) blk: { - const parent_struct = try mod.gpa.create(DeclPlusEmitH); - parent_struct.* = .{ - .emit_h = .{}, - .decl = undefined, +) !Decl.Index { + const decl_and_index: struct { + new_decl: *Decl, + decl_index: Decl.Index, + } = if (mod.decls_free_list.popOrNull()) |decl_index| d: { + break :d .{ + .new_decl = mod.declPtr(decl_index), + .decl_index = decl_index, }; - break :blk &parent_struct.decl; - } else try mod.gpa.create(Decl); + } else d: { + const decl = try mod.allocated_decls.addOne(mod.gpa); + errdefer mod.allocated_decls.shrinkRetainingCapacity(mod.allocated_decls.len - 1); + if (mod.emit_h) |mod_emit_h| { + const decl_emit_h = try mod_emit_h.allocated_emit_h.addOne(mod.gpa); + decl_emit_h.* = .{}; + } + break :d .{ + .new_decl = decl, + .decl_index = @intToEnum(Decl.Index, mod.allocated_decls.len - 1), + }; + }; - new_decl.* = .{ - .name = name, + decl_and_index.new_decl.* = .{ + .name = undefined, .src_namespace = namespace, .src_node = src_node, .src_line = undefined, @@ -4986,7 +5080,7 @@ pub fn allocateNewDecl( .is_usingnamespace = false, }; - return new_decl; + return decl_and_index.decl_index; } /// Get error value for error tag `name`. @@ -5010,18 +5104,9 @@ pub fn getErrorValue(mod: *Module, name: []const u8) !std.StringHashMapUnmanaged }; } -/// Takes ownership of `name` even if it returns an error. -pub fn createAnonymousDeclNamed( - mod: *Module, - block: *Sema.Block, - typed_value: TypedValue, - name: [:0]u8, -) !*Decl { - return mod.createAnonymousDeclFromDeclNamed(block.src_decl, block.namespace, block.wip_capture_scope, typed_value, name); -} - -pub fn createAnonymousDecl(mod: *Module, block: *Sema.Block, typed_value: TypedValue) !*Decl { - return mod.createAnonymousDeclFromDecl(block.src_decl, block.namespace, block.wip_capture_scope, typed_value); +pub fn createAnonymousDecl(mod: *Module, block: *Sema.Block, typed_value: TypedValue) !Decl.Index { + const src_decl = mod.declPtr(block.src_decl); + return mod.createAnonymousDeclFromDecl(src_decl, block.namespace, block.wip_capture_scope, typed_value); } pub fn createAnonymousDeclFromDecl( @@ -5030,30 +5115,31 @@ pub fn createAnonymousDeclFromDecl( namespace: *Namespace, src_scope: ?*CaptureScope, tv: TypedValue, -) !*Decl { - const name_index = mod.getNextAnonNameIndex(); +) !Decl.Index { + const new_decl_index = try mod.allocateNewDecl(namespace, src_decl.src_node, src_scope); + errdefer mod.destroyDecl(new_decl_index); const name = try std.fmt.allocPrintZ(mod.gpa, "{s}__anon_{d}", .{ - src_decl.name, name_index, + src_decl.name, @enumToInt(new_decl_index), }); - return mod.createAnonymousDeclFromDeclNamed(src_decl, namespace, src_scope, tv, name); + try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, tv, name); + return new_decl_index; } /// Takes ownership of `name` even if it returns an error. -pub fn createAnonymousDeclFromDeclNamed( +pub fn initNewAnonDecl( mod: *Module, - src_decl: *Decl, + new_decl_index: Decl.Index, + src_line: u32, namespace: *Namespace, - src_scope: ?*CaptureScope, typed_value: TypedValue, name: [:0]u8, -) !*Decl { +) !void { errdefer mod.gpa.free(name); - try namespace.anon_decls.ensureUnusedCapacity(mod.gpa, 1); + const new_decl = mod.declPtr(new_decl_index); - const new_decl = try mod.allocateNewDecl(name, namespace, src_decl.src_node, src_scope); - - new_decl.src_line = src_decl.src_line; + new_decl.name = name; + new_decl.src_line = src_line; new_decl.ty = typed_value.ty; new_decl.val = typed_value.val; new_decl.@"align" = 0; @@ -5062,22 +5148,16 @@ pub fn createAnonymousDeclFromDeclNamed( new_decl.analysis = .complete; new_decl.generation = mod.generation; - namespace.anon_decls.putAssumeCapacityNoClobber(new_decl, {}); + try namespace.anon_decls.putNoClobber(mod.gpa, new_decl_index, {}); // The Decl starts off with alive=false and the codegen backend will set alive=true // if the Decl is referenced by an instruction or another constant. Otherwise, // the Decl will be garbage collected by the `codegen_decl` task instead of sent // to the linker. if (typed_value.ty.isFnOrHasRuntimeBits()) { - try mod.comp.bin_file.allocateDeclIndexes(new_decl); - try mod.comp.anon_work_queue.writeItem(.{ .codegen_decl = new_decl }); + try mod.comp.bin_file.allocateDeclIndexes(new_decl_index); + try mod.comp.anon_work_queue.writeItem(.{ .codegen_decl = new_decl_index }); } - - return new_decl; -} - -pub fn getNextAnonNameIndex(mod: *Module) usize { - return @atomicRmw(usize, &mod.next_anon_name_index, .Add, 1, .Monotonic); } pub fn makeIntType(arena: Allocator, signedness: std.builtin.Signedness, bits: u16) !Type { @@ -5339,12 +5419,12 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { // for the outdated decls, but we cannot queue up the tasks until after // we find out which ones have been deleted, otherwise there would be // deleted Decl pointers in the work queue. - var outdated_decls = std.AutoArrayHashMap(*Decl, void).init(mod.gpa); + var outdated_decls = std.AutoArrayHashMap(Decl.Index, void).init(mod.gpa); defer outdated_decls.deinit(); for (mod.import_table.values()) |file| { try outdated_decls.ensureUnusedCapacity(file.outdated_decls.items.len); - for (file.outdated_decls.items) |decl| { - outdated_decls.putAssumeCapacity(decl, {}); + for (file.outdated_decls.items) |decl_index| { + outdated_decls.putAssumeCapacity(decl_index, {}); } file.outdated_decls.clearRetainingCapacity(); @@ -5356,15 +5436,16 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { // it may be both in this `deleted_decls` set, as well as in the // `Module.deletion_set`. To avoid deleting it twice, we remove it from the // deletion set at this time. - for (file.deleted_decls.items) |decl| { + for (file.deleted_decls.items) |decl_index| { + const decl = mod.declPtr(decl_index); log.debug("deleted from source: {*} ({s})", .{ decl, decl.name }); // Remove from the namespace it resides in, preserving declaration order. assert(decl.zir_decl_index != 0); - _ = decl.src_namespace.decls.orderedRemoveAdapted(@as([]const u8, mem.sliceTo(decl.name, 0)), DeclAdapter{}); + _ = decl.src_namespace.decls.orderedRemoveAdapted(@as([]const u8, mem.sliceTo(decl.name, 0)), DeclAdapter{ .mod = mod }); - try mod.clearDecl(decl, &outdated_decls); - decl.destroy(mod); + try mod.clearDecl(decl_index, &outdated_decls); + mod.destroyDecl(decl_index); } file.deleted_decls.clearRetainingCapacity(); } @@ -5393,13 +5474,13 @@ pub fn processExports(mod: *Module) !void { if (gop.found_existing) { new_export.status = .failed_retryable; try mod.failed_exports.ensureUnusedCapacity(gpa, 1); - const src_loc = new_export.getSrcLoc(); + const src_loc = new_export.getSrcLoc(mod); const msg = try ErrorMsg.create(gpa, src_loc, "exported symbol collision: {s}", .{ new_export.options.name, }); errdefer msg.destroy(gpa); const other_export = gop.value_ptr.*; - const other_src_loc = other_export.getSrcLoc(); + const other_src_loc = other_export.getSrcLoc(mod); try mod.errNoteNonLazy(other_src_loc, msg, "other symbol here", .{}); mod.failed_exports.putAssumeCapacityNoClobber(new_export, msg); new_export.status = .failed; @@ -5413,7 +5494,7 @@ pub fn processExports(mod: *Module) !void { const new_export = exports[0]; new_export.status = .failed_retryable; try mod.failed_exports.ensureUnusedCapacity(gpa, 1); - const src_loc = new_export.getSrcLoc(); + const src_loc = new_export.getSrcLoc(mod); const msg = try ErrorMsg.create(gpa, src_loc, "unable to export: {s}", .{ @errorName(err), }); @@ -5427,12 +5508,14 @@ pub fn populateTestFunctions(mod: *Module) !void { const gpa = mod.gpa; const builtin_pkg = mod.main_pkg.table.get("builtin").?; const builtin_file = (mod.importPkg(builtin_pkg) catch unreachable).file; - const builtin_namespace = builtin_file.root_decl.?.src_namespace; - const decl = builtin_namespace.decls.getKeyAdapted(@as([]const u8, "test_functions"), DeclAdapter{}).?; + const root_decl = mod.declPtr(builtin_file.root_decl.unwrap().?); + const builtin_namespace = root_decl.src_namespace; + const decl_index = builtin_namespace.decls.getKeyAdapted(@as([]const u8, "test_functions"), DeclAdapter{ .mod = mod }).?; + const decl = mod.declPtr(decl_index); var buf: Type.SlicePtrFieldTypeBuffer = undefined; const tmp_test_fn_ty = decl.ty.slicePtrFieldType(&buf).elemType(); - const array_decl = d: { + const array_decl_index = d: { // Add mod.test_functions to an array decl then make the test_functions // decl reference it as a slice. var new_decl_arena = std.heap.ArenaAllocator.init(gpa); @@ -5440,50 +5523,52 @@ pub fn populateTestFunctions(mod: *Module) !void { const arena = new_decl_arena.allocator(); const test_fn_vals = try arena.alloc(Value, mod.test_functions.count()); - const array_decl = try mod.createAnonymousDeclFromDecl(decl, decl.src_namespace, null, .{ + const array_decl_index = try mod.createAnonymousDeclFromDecl(decl, decl.src_namespace, null, .{ .ty = try Type.Tag.array.create(arena, .{ .len = test_fn_vals.len, .elem_type = try tmp_test_fn_ty.copy(arena), }), .val = try Value.Tag.aggregate.create(arena, test_fn_vals), }); + const array_decl = mod.declPtr(array_decl_index); // Add a dependency on each test name and function pointer. try array_decl.dependencies.ensureUnusedCapacity(gpa, test_fn_vals.len * 2); - for (mod.test_functions.keys()) |test_decl, i| { + for (mod.test_functions.keys()) |test_decl_index, i| { + const test_decl = mod.declPtr(test_decl_index); const test_name_slice = mem.sliceTo(test_decl.name, 0); - const test_name_decl = n: { + const test_name_decl_index = n: { var name_decl_arena = std.heap.ArenaAllocator.init(gpa); errdefer name_decl_arena.deinit(); const bytes = try name_decl_arena.allocator().dupe(u8, test_name_slice); - const test_name_decl = try mod.createAnonymousDeclFromDecl(array_decl, array_decl.src_namespace, null, .{ + const test_name_decl_index = try mod.createAnonymousDeclFromDecl(array_decl, array_decl.src_namespace, null, .{ .ty = try Type.Tag.array_u8.create(name_decl_arena.allocator(), bytes.len), .val = try Value.Tag.bytes.create(name_decl_arena.allocator(), bytes), }); - try test_name_decl.finalizeNewArena(&name_decl_arena); - break :n test_name_decl; + try mod.declPtr(test_name_decl_index).finalizeNewArena(&name_decl_arena); + break :n test_name_decl_index; }; - array_decl.dependencies.putAssumeCapacityNoClobber(test_decl, {}); - array_decl.dependencies.putAssumeCapacityNoClobber(test_name_decl, {}); - try mod.linkerUpdateDecl(test_name_decl); + array_decl.dependencies.putAssumeCapacityNoClobber(test_decl_index, {}); + array_decl.dependencies.putAssumeCapacityNoClobber(test_name_decl_index, {}); + try mod.linkerUpdateDecl(test_name_decl_index); const field_vals = try arena.create([3]Value); field_vals.* = .{ try Value.Tag.slice.create(arena, .{ - .ptr = try Value.Tag.decl_ref.create(arena, test_name_decl), + .ptr = try Value.Tag.decl_ref.create(arena, test_name_decl_index), .len = try Value.Tag.int_u64.create(arena, test_name_slice.len), }), // name - try Value.Tag.decl_ref.create(arena, test_decl), // func + try Value.Tag.decl_ref.create(arena, test_decl_index), // func Value.initTag(.null_value), // async_frame_size }; test_fn_vals[i] = try Value.Tag.aggregate.create(arena, field_vals); } try array_decl.finalizeNewArena(&new_decl_arena); - break :d array_decl; + break :d array_decl_index; }; - try mod.linkerUpdateDecl(array_decl); + try mod.linkerUpdateDecl(array_decl_index); { var new_decl_arena = std.heap.ArenaAllocator.init(gpa); @@ -5493,7 +5578,7 @@ pub fn populateTestFunctions(mod: *Module) !void { // This copy accesses the old Decl Type/Value so it must be done before `clearValues`. const new_ty = try Type.Tag.const_slice.create(arena, try tmp_test_fn_ty.copy(arena)); const new_val = try Value.Tag.slice.create(arena, .{ - .ptr = try Value.Tag.decl_ref.create(arena, array_decl), + .ptr = try Value.Tag.decl_ref.create(arena, array_decl_index), .len = try Value.Tag.int_u64.create(arena, mod.test_functions.count()), }); @@ -5506,15 +5591,17 @@ pub fn populateTestFunctions(mod: *Module) !void { try decl.finalizeNewArena(&new_decl_arena); } - try mod.linkerUpdateDecl(decl); + try mod.linkerUpdateDecl(decl_index); } -pub fn linkerUpdateDecl(mod: *Module, decl: *Decl) !void { +pub fn linkerUpdateDecl(mod: *Module, decl_index: Decl.Index) !void { const comp = mod.comp; if (comp.bin_file.options.emit == null) return; - comp.bin_file.updateDecl(mod, decl) catch |err| switch (err) { + const decl = mod.declPtr(decl_index); + + comp.bin_file.updateDecl(mod, decl_index) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => { decl.analysis = .codegen_failure; @@ -5523,7 +5610,7 @@ pub fn linkerUpdateDecl(mod: *Module, decl: *Decl) !void { else => { const gpa = mod.gpa; try mod.failed_decls.ensureUnusedCapacity(gpa, 1); - mod.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( + mod.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create( gpa, decl.srcLoc(), "unable to codegen: {s}", @@ -5566,3 +5653,64 @@ fn reportRetryableFileError( } gop.value_ptr.* = err_msg; } + +pub fn markReferencedDeclsAlive(mod: *Module, val: Value) void { + switch (val.tag()) { + .decl_ref_mut => return mod.markDeclIndexAlive(val.castTag(.decl_ref_mut).?.data.decl_index), + .extern_fn => return mod.markDeclIndexAlive(val.castTag(.extern_fn).?.data.owner_decl), + .function => return mod.markDeclIndexAlive(val.castTag(.function).?.data.owner_decl), + .variable => return mod.markDeclIndexAlive(val.castTag(.variable).?.data.owner_decl), + .decl_ref => return mod.markDeclIndexAlive(val.cast(Value.Payload.Decl).?.data), + + .repeated, + .eu_payload, + .opt_payload, + .empty_array_sentinel, + => return mod.markReferencedDeclsAlive(val.cast(Value.Payload.SubValue).?.data), + + .eu_payload_ptr, + .opt_payload_ptr, + => return mod.markReferencedDeclsAlive(val.cast(Value.Payload.PayloadPtr).?.data.container_ptr), + + .slice => { + const slice = val.cast(Value.Payload.Slice).?.data; + mod.markReferencedDeclsAlive(slice.ptr); + mod.markReferencedDeclsAlive(slice.len); + }, + + .elem_ptr => { + const elem_ptr = val.cast(Value.Payload.ElemPtr).?.data; + return mod.markReferencedDeclsAlive(elem_ptr.array_ptr); + }, + .field_ptr => { + const field_ptr = val.cast(Value.Payload.FieldPtr).?.data; + return mod.markReferencedDeclsAlive(field_ptr.container_ptr); + }, + .aggregate => { + for (val.castTag(.aggregate).?.data) |field_val| { + mod.markReferencedDeclsAlive(field_val); + } + }, + .@"union" => { + const data = val.cast(Value.Payload.Union).?.data; + mod.markReferencedDeclsAlive(data.tag); + mod.markReferencedDeclsAlive(data.val); + }, + + else => {}, + } +} + +pub fn markDeclAlive(mod: *Module, decl: *Decl) void { + if (decl.alive) return; + decl.alive = true; + + // This is the first time we are marking this Decl alive. We must + // therefore recurse into its value and mark any Decl it references + // as also alive, so that any Decl referenced does not get garbage collected. + mod.markReferencedDeclsAlive(decl.val); +} + +fn markDeclIndexAlive(mod: *Module, decl_index: Decl.Index) void { + return mod.markDeclAlive(mod.declPtr(decl_index)); +} diff --git a/src/RangeSet.zig b/src/RangeSet.zig index 79bd22fd7f..5b4c654529 100644 --- a/src/RangeSet.zig +++ b/src/RangeSet.zig @@ -1,12 +1,14 @@ const std = @import("std"); const Order = std.math.Order; + +const RangeSet = @This(); +const Module = @import("Module.zig"); +const SwitchProngSrc = @import("Module.zig").SwitchProngSrc; const Type = @import("type.zig").Type; const Value = @import("value.zig").Value; -const RangeSet = @This(); -const SwitchProngSrc = @import("Module.zig").SwitchProngSrc; ranges: std.ArrayList(Range), -target: std.Target, +module: *Module, pub const Range = struct { first: Value, @@ -14,10 +16,10 @@ pub const Range = struct { src: SwitchProngSrc, }; -pub fn init(allocator: std.mem.Allocator, target: std.Target) RangeSet { +pub fn init(allocator: std.mem.Allocator, module: *Module) RangeSet { return .{ .ranges = std.ArrayList(Range).init(allocator), - .target = target, + .module = module, }; } @@ -32,11 +34,9 @@ pub fn add( ty: Type, src: SwitchProngSrc, ) !?SwitchProngSrc { - const target = self.target; - for (self.ranges.items) |range| { - if (last.compare(.gte, range.first, ty, target) and - first.compare(.lte, range.last, ty, target)) + if (last.compare(.gte, range.first, ty, self.module) and + first.compare(.lte, range.last, ty, self.module)) { return range.src; // They overlap. } @@ -49,26 +49,24 @@ pub fn add( return null; } -const LessThanContext = struct { ty: Type, target: std.Target }; +const LessThanContext = struct { ty: Type, module: *Module }; /// Assumes a and b do not overlap fn lessThan(ctx: LessThanContext, a: Range, b: Range) bool { - return a.first.compare(.lt, b.first, ctx.ty, ctx.target); + return a.first.compare(.lt, b.first, ctx.ty, ctx.module); } pub fn spans(self: *RangeSet, first: Value, last: Value, ty: Type) !bool { if (self.ranges.items.len == 0) return false; - const target = self.target; - std.sort.sort(Range, self.ranges.items, LessThanContext{ .ty = ty, - .target = target, + .module = self.module, }, lessThan); - if (!self.ranges.items[0].first.eql(first, ty, target) or - !self.ranges.items[self.ranges.items.len - 1].last.eql(last, ty, target)) + if (!self.ranges.items[0].first.eql(first, ty, self.module) or + !self.ranges.items[self.ranges.items.len - 1].last.eql(last, ty, self.module)) { return false; } @@ -78,6 +76,8 @@ pub fn spans(self: *RangeSet, first: Value, last: Value, ty: Type) !bool { var counter = try std.math.big.int.Managed.init(self.ranges.allocator); defer counter.deinit(); + const target = self.module.getTarget(); + // look for gaps for (self.ranges.items[1..]) |cur, i| { // i starts counting from the second item. diff --git a/src/Sema.zig b/src/Sema.zig index a3eef2dc86..14511fe82d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -24,6 +24,7 @@ inst_map: InstMap = .{}, /// and `src_decl` of `Block` is the `Decl` of the callee. /// This `Decl` owns the arena memory of this `Sema`. owner_decl: *Decl, +owner_decl_index: Decl.Index, /// For an inline or comptime function call, this will be the root parent function /// which contains the callsite. Corresponds to `owner_decl`. owner_func: ?*Module.Fn, @@ -47,7 +48,7 @@ comptime_break_inst: Zir.Inst.Index = undefined, /// access to the source location set by the previous instruction which did /// contain a mapped source location. src: LazySrcLoc = .{ .token_offset = 0 }, -decl_val_table: std.AutoHashMapUnmanaged(*Decl, Air.Inst.Ref) = .{}, +decl_val_table: std.AutoHashMapUnmanaged(Decl.Index, Air.Inst.Ref) = .{}, /// When doing a generic function instantiation, this array collects a /// `Value` object for each parameter that is comptime known and thus elided /// from the generated function. This memory is allocated by a parent `Sema` and @@ -111,10 +112,6 @@ pub const Block = struct { parent: ?*Block, /// Shared among all child blocks. sema: *Sema, - /// This Decl is the Decl according to the Zig source code corresponding to this Block. - /// This can vary during inline or comptime function calls. See `Sema.owner_decl` - /// for the one that will be the same for all Block instances. - src_decl: *Decl, /// The namespace to use for lookups from this source block /// When analyzing fields, this is different from src_decl.src_namepsace. namespace: *Namespace, @@ -130,6 +127,10 @@ pub const Block = struct { /// If runtime_index is not 0 then one of these is guaranteed to be non null. runtime_cond: ?LazySrcLoc = null, runtime_loop: ?LazySrcLoc = null, + /// This Decl is the Decl according to the Zig source code corresponding to this Block. + /// This can vary during inline or comptime function calls. See `Sema.owner_decl` + /// for the one that will be the same for all Block instances. + src_decl: Decl.Index, /// Non zero if a non-inline loop or a runtime conditional have been encountered. /// Stores to to comptime variables are only allowed when var.runtime_index <= runtime_index. runtime_index: u32 = 0, @@ -512,20 +513,21 @@ pub const Block = struct { } /// `alignment` value of 0 means to use ABI alignment. - pub fn finish(wad: *WipAnonDecl, ty: Type, val: Value, alignment: u32) !*Decl { + pub fn finish(wad: *WipAnonDecl, ty: Type, val: Value, alignment: u32) !Decl.Index { const sema = wad.block.sema; // Do this ahead of time because `createAnonymousDecl` depends on calling // `type.hasRuntimeBits()`. _ = try sema.typeHasRuntimeBits(wad.block, wad.src, ty); - const new_decl = try sema.mod.createAnonymousDecl(wad.block, .{ + const new_decl_index = try sema.mod.createAnonymousDecl(wad.block, .{ .ty = ty, .val = val, }); + const new_decl = sema.mod.declPtr(new_decl_index); new_decl.@"align" = alignment; - errdefer sema.mod.abortAnonDecl(new_decl); + errdefer sema.mod.abortAnonDecl(new_decl_index); try new_decl.finalizeNewArena(&wad.new_decl_arena); wad.finished = true; - return new_decl; + return new_decl_index; } }; }; @@ -676,7 +678,7 @@ fn analyzeBodyInner( crash_info.setBodyIndex(i); const inst = body[i]; std.log.scoped(.sema_zir).debug("sema ZIR {s} %{d}", .{ - block.src_decl.src_namespace.file_scope.sub_file_path, inst, + sema.mod.declPtr(block.src_decl).src_namespace.file_scope.sub_file_path, inst, }); const air_inst: Air.Inst.Ref = switch (tags[inst]) { // zig fmt: off @@ -1383,8 +1385,7 @@ pub fn resolveConstString( const wanted_type = Type.initTag(.const_slice_u8); const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); const val = try sema.resolveConstValue(block, src, coerced_inst); - const target = sema.mod.getTarget(); - return val.toAllocatedBytes(wanted_type, sema.arena, target); + return val.toAllocatedBytes(wanted_type, sema.arena, sema.mod); } pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type { @@ -1538,28 +1539,24 @@ fn failWithDivideByZero(sema: *Sema, block: *Block, src: LazySrcLoc) CompileErro } fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: Type, rhs_ty: Type) CompileError { - const target = sema.mod.getTarget(); return sema.fail(block, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ - lhs_ty.fmt(target), rhs_ty.fmt(target), + lhs_ty.fmt(sema.mod), rhs_ty.fmt(sema.mod), }); } fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, optional_ty: Type) CompileError { - const target = sema.mod.getTarget(); - return sema.fail(block, src, "expected optional type, found {}", .{optional_ty.fmt(target)}); + return sema.fail(block, src, "expected optional type, found {}", .{optional_ty.fmt(sema.mod)}); } fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError { - const target = sema.mod.getTarget(); return sema.fail(block, src, "type '{}' does not support array initialization syntax", .{ - ty.fmt(target), + ty.fmt(sema.mod), }); } fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError { - const target = sema.mod.getTarget(); return sema.fail(block, src, "type '{}' does not support struct initialization syntax", .{ - ty.fmt(target), + ty.fmt(sema.mod), }); } @@ -1570,9 +1567,8 @@ fn failWithErrorSetCodeMissing( dest_err_set_ty: Type, src_err_set_ty: Type, ) CompileError { - const target = sema.mod.getTarget(); return sema.fail(block, src, "expected type '{}', found type '{}'", .{ - dest_err_set_ty.fmt(target), src_err_set_ty.fmt(target), + dest_err_set_ty.fmt(sema.mod), src_err_set_ty.fmt(sema.mod), }); } @@ -1586,7 +1582,9 @@ fn errNote( comptime format: []const u8, args: anytype, ) error{OutOfMemory}!void { - return sema.mod.errNoteNonLazy(src.toSrcLoc(block.src_decl), parent, format, args); + const mod = sema.mod; + const src_decl = mod.declPtr(block.src_decl); + return mod.errNoteNonLazy(src.toSrcLoc(src_decl), parent, format, args); } fn addFieldErrNote( @@ -1598,10 +1596,12 @@ fn addFieldErrNote( comptime format: []const u8, args: anytype, ) !void { - const decl = container_ty.getOwnerDecl(); + const mod = sema.mod; + const decl_index = container_ty.getOwnerDecl(); + const decl = mod.declPtr(decl_index); const tree = try sema.getAstTree(block); const field_src = enumFieldSrcLoc(decl, tree.*, container_ty.getNodeOffset(), field_index); - try sema.mod.errNoteNonLazy(field_src.toSrcLoc(decl), parent, format, args); + try mod.errNoteNonLazy(field_src.toSrcLoc(decl), parent, format, args); } fn errMsg( @@ -1611,7 +1611,9 @@ fn errMsg( comptime format: []const u8, args: anytype, ) error{OutOfMemory}!*Module.ErrorMsg { - return Module.ErrorMsg.create(sema.gpa, src.toSrcLoc(block.src_decl), format, args); + const mod = sema.mod; + const src_decl = mod.declPtr(block.src_decl); + return Module.ErrorMsg.create(sema.gpa, src.toSrcLoc(src_decl), format, args); } pub fn fail( @@ -1654,7 +1656,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, block: *Block, err_msg: *Module.ErrorMsg) sema.owner_decl.analysis = .sema_failure; sema.owner_decl.generation = mod.generation; } - const gop = mod.failed_decls.getOrPutAssumeCapacity(sema.owner_decl); + const gop = mod.failed_decls.getOrPutAssumeCapacity(sema.owner_decl_index); if (gop.found_existing) { // If there are multiple errors for the same Decl, prefer the first one added. err_msg.destroy(mod.gpa); @@ -1756,7 +1758,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE try inferred_alloc.stored_inst_list.append(sema.arena, operand); try sema.requireRuntimeBlock(block, src); - const ptr_ty = try Type.ptr(sema.arena, target, .{ + const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = pointee_ty, .@"align" = inferred_alloc.alignment, .@"addrspace" = addr_space, @@ -1770,7 +1772,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE // The alloc will turn into a Decl. var anon_decl = try block.startAnonDecl(src); defer anon_decl.deinit(); - iac.data.decl = try anon_decl.finish( + iac.data.decl_index = try anon_decl.finish( try pointee_ty.copy(anon_decl.arena()), Value.undef, iac.data.alignment, @@ -1778,7 +1780,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE if (iac.data.alignment != 0) { try sema.resolveTypeLayout(block, src, pointee_ty); } - const ptr_ty = try Type.ptr(sema.arena, target, .{ + const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = pointee_ty, .@"align" = iac.data.alignment, .@"addrspace" = addr_space, @@ -1786,7 +1788,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE return sema.addConstant( ptr_ty, try Value.Tag.decl_ref_mut.create(sema.arena, .{ - .decl = iac.data.decl, + .decl_index = iac.data.decl_index, .runtime_index = block.runtime_index, }), ); @@ -1827,7 +1829,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE } } - const ptr_ty = try Type.ptr(sema.arena, target, .{ + const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = pointee_ty, .@"addrspace" = addr_space, }); @@ -1848,7 +1850,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE } const ty_op = air_datas[trash_inst].ty_op; const operand_ty = sema.typeOf(ty_op.operand); - const ptr_operand_ty = try Type.ptr(sema.arena, target, .{ + const ptr_operand_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = operand_ty, .@"addrspace" = addr_space, }); @@ -1924,18 +1926,19 @@ fn zirStructDecl( errdefer new_decl_arena.deinit(); const new_decl_arena_allocator = new_decl_arena.allocator(); + const mod = sema.mod; const struct_obj = try new_decl_arena_allocator.create(Module.Struct); const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj); const struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty); - const type_name = try sema.createTypeName(block, small.name_strategy, "struct"); - const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = struct_val, - }, type_name); + }, small.name_strategy, "struct"); + const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - errdefer sema.mod.abortAnonDecl(new_decl); + errdefer mod.abortAnonDecl(new_decl_index); struct_obj.* = .{ - .owner_decl = new_decl, + .owner_decl = new_decl_index, .fields = .{}, .node_offset = src.node_offset, .zir_index = inst, @@ -1953,15 +1956,23 @@ fn zirStructDecl( }); try sema.analyzeStructDecl(new_decl, inst, struct_obj); try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl); + return sema.analyzeDeclVal(block, src, new_decl_index); } -fn createTypeName( +fn createAnonymousDeclTypeNamed( sema: *Sema, block: *Block, + typed_value: TypedValue, name_strategy: Zir.Inst.NameStrategy, anon_prefix: []const u8, -) ![:0]u8 { +) !Decl.Index { + const mod = sema.mod; + const namespace = block.namespace; + const src_scope = block.wip_capture_scope; + const src_decl = mod.declPtr(block.src_decl); + const new_decl_index = try mod.allocateNewDecl(namespace, src_decl.src_node, src_scope); + errdefer mod.destroyDecl(new_decl_index); + switch (name_strategy) { .anon => { // It would be neat to have "struct:line:column" but this name has @@ -1970,20 +1981,24 @@ fn createTypeName( // semantically analyzed. // This name is also used as the key in the parent namespace so it cannot be // renamed. - const name_index = sema.mod.getNextAnonNameIndex(); - return std.fmt.allocPrintZ(sema.gpa, "{s}__{s}_{d}", .{ - block.src_decl.name, anon_prefix, name_index, + const name = try std.fmt.allocPrintZ(sema.gpa, "{s}__{s}_{d}", .{ + src_decl.name, anon_prefix, @enumToInt(new_decl_index), }); + try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name); + return new_decl_index; + }, + .parent => { + const name = try sema.gpa.dupeZ(u8, mem.sliceTo(sema.mod.declPtr(block.src_decl).name, 0)); + try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name); + return new_decl_index; }, - .parent => return sema.gpa.dupeZ(u8, mem.sliceTo(block.src_decl.name, 0)), .func => { - const target = sema.mod.getTarget(); const fn_info = sema.code.getFnInfo(sema.func.?.zir_body_inst); const zir_tags = sema.code.instructions.items(.tag); var buf = std.ArrayList(u8).init(sema.gpa); defer buf.deinit(); - try buf.appendSlice(mem.sliceTo(block.src_decl.name, 0)); + try buf.appendSlice(mem.sliceTo(sema.mod.declPtr(block.src_decl).name, 0)); try buf.appendSlice("("); var arg_i: usize = 0; @@ -1995,7 +2010,7 @@ fn createTypeName( const arg_val = sema.resolveConstMaybeUndefVal(block, .unneeded, arg) catch unreachable; if (arg_i != 0) try buf.appendSlice(","); - try buf.writer().print("{}", .{arg_val.fmtValue(sema.typeOf(arg), target)}); + try buf.writer().print("{}", .{arg_val.fmtValue(sema.typeOf(arg), sema.mod)}); arg_i += 1; continue; @@ -2004,7 +2019,9 @@ fn createTypeName( }; try buf.appendSlice(")"); - return buf.toOwnedSliceSentinel(0); + const name = try buf.toOwnedSliceSentinel(0); + try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name); + return new_decl_index; }, } } @@ -2064,16 +2081,16 @@ fn zirEnumDecl( }; const enum_ty = Type.initPayload(&enum_ty_payload.base); const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); - const type_name = try sema.createTypeName(block, small.name_strategy, "enum"); - const new_decl = try mod.createAnonymousDeclNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = enum_val, - }, type_name); + }, small.name_strategy, "enum"); + const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - errdefer mod.abortAnonDecl(new_decl); + errdefer mod.abortAnonDecl(new_decl_index); enum_obj.* = .{ - .owner_decl = new_decl, + .owner_decl = new_decl_index, .tag_ty = Type.@"null", .tag_ty_inferred = true, .fields = .{}, @@ -2101,7 +2118,7 @@ fn zirEnumDecl( enum_obj.tag_ty_inferred = false; } try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl); + return sema.analyzeDeclVal(block, src, new_decl_index); } extra_index += body.len; @@ -2116,8 +2133,13 @@ fn zirEnumDecl( // should be the enum itself. const prev_owner_decl = sema.owner_decl; + const prev_owner_decl_index = sema.owner_decl_index; sema.owner_decl = new_decl; - defer sema.owner_decl = prev_owner_decl; + sema.owner_decl_index = new_decl_index; + defer { + sema.owner_decl = prev_owner_decl; + sema.owner_decl_index = prev_owner_decl_index; + } const prev_owner_func = sema.owner_func; sema.owner_func = null; @@ -2133,7 +2155,7 @@ fn zirEnumDecl( var enum_block: Block = .{ .parent = null, .sema = sema, - .src_decl = new_decl, + .src_decl = new_decl_index, .namespace = &enum_obj.namespace, .wip_capture_scope = wip_captures.scope, .instructions = .{}, @@ -2168,7 +2190,7 @@ fn zirEnumDecl( if (any_values) { try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{ .ty = enum_obj.tag_ty, - .target = target, + .mod = mod, }); } @@ -2196,8 +2218,8 @@ fn zirEnumDecl( const gop = enum_obj.fields.getOrPutAssumeCapacity(field_name); if (gop.found_existing) { const tree = try sema.getAstTree(block); - const field_src = enumFieldSrcLoc(block.src_decl, tree.*, src.node_offset, field_i); - const other_tag_src = enumFieldSrcLoc(block.src_decl, tree.*, src.node_offset, gop.index); + const field_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset, field_i); + const other_tag_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset, gop.index); const msg = msg: { const msg = try sema.errMsg(block, field_src, "duplicate enum tag", .{}); errdefer msg.destroy(gpa); @@ -2218,7 +2240,7 @@ fn zirEnumDecl( const copied_tag_val = try tag_val.copy(new_decl_arena_allocator); enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{ .ty = enum_obj.tag_ty, - .target = target, + .mod = mod, }); } else if (any_values) { const tag_val = if (last_tag_val) |val| @@ -2229,13 +2251,13 @@ fn zirEnumDecl( const copied_tag_val = try tag_val.copy(new_decl_arena_allocator); enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{ .ty = enum_obj.tag_ty, - .target = target, + .mod = mod, }); } } try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl); + return sema.analyzeDeclVal(block, src, new_decl_index); } fn zirUnionDecl( @@ -2279,15 +2301,16 @@ fn zirUnionDecl( }; const union_ty = Type.initPayload(&union_payload.base); const union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty); - const type_name = try sema.createTypeName(block, small.name_strategy, "union"); - const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ + const mod = sema.mod; + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = union_val, - }, type_name); + }, small.name_strategy, "union"); + const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - errdefer sema.mod.abortAnonDecl(new_decl); + errdefer mod.abortAnonDecl(new_decl_index); union_obj.* = .{ - .owner_decl = new_decl, + .owner_decl = new_decl_index, .tag_ty = Type.initTag(.@"null"), .fields = .{}, .node_offset = src.node_offset, @@ -2304,10 +2327,10 @@ fn zirUnionDecl( &union_obj.namespace, new_decl, new_decl.name, }); - _ = try sema.mod.scanNamespace(&union_obj.namespace, extra_index, decls_len, new_decl); + _ = try mod.scanNamespace(&union_obj.namespace, extra_index, decls_len, new_decl); try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl); + return sema.analyzeDeclVal(block, src, new_decl_index); } fn zirOpaqueDecl( @@ -2347,16 +2370,16 @@ fn zirOpaqueDecl( }; const opaque_ty = Type.initPayload(&opaque_ty_payload.base); const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty); - const type_name = try sema.createTypeName(block, small.name_strategy, "opaque"); - const new_decl = try mod.createAnonymousDeclNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = opaque_val, - }, type_name); + }, small.name_strategy, "opaque"); + const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - errdefer mod.abortAnonDecl(new_decl); + errdefer mod.abortAnonDecl(new_decl_index); opaque_obj.* = .{ - .owner_decl = new_decl, + .owner_decl = new_decl_index, .node_offset = src.node_offset, .namespace = .{ .parent = block.namespace, @@ -2371,7 +2394,7 @@ fn zirOpaqueDecl( extra_index = try mod.scanNamespace(&opaque_obj.namespace, extra_index, decls_len, new_decl); try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl); + return sema.analyzeDeclVal(block, src, new_decl_index); } fn zirErrorSetDecl( @@ -2395,13 +2418,14 @@ fn zirErrorSetDecl( const error_set = try new_decl_arena_allocator.create(Module.ErrorSet); const error_set_ty = try Type.Tag.error_set.create(new_decl_arena_allocator, error_set); const error_set_val = try Value.Tag.ty.create(new_decl_arena_allocator, error_set_ty); - const type_name = try sema.createTypeName(block, name_strategy, "error"); - const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ + const mod = sema.mod; + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = error_set_val, - }, type_name); + }, name_strategy, "error"); + const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - errdefer sema.mod.abortAnonDecl(new_decl); + errdefer mod.abortAnonDecl(new_decl_index); var names = Module.ErrorSet.NameMap{}; try names.ensureUnusedCapacity(new_decl_arena_allocator, extra.data.fields_len); @@ -2410,7 +2434,7 @@ fn zirErrorSetDecl( const extra_index_end = extra_index + (extra.data.fields_len * 2); while (extra_index < extra_index_end) : (extra_index += 2) { // +2 to skip over doc_string const str_index = sema.code.extra[extra_index]; - const kv = try sema.mod.getErrorValue(sema.code.nullTerminatedString(str_index)); + const kv = try mod.getErrorValue(sema.code.nullTerminatedString(str_index)); const result = names.getOrPutAssumeCapacity(kv.key); assert(!result.found_existing); // verified in AstGen } @@ -2419,12 +2443,12 @@ fn zirErrorSetDecl( Module.ErrorSet.sortNames(&names); error_set.* = .{ - .owner_decl = new_decl, + .owner_decl = new_decl_index, .node_offset = inst_data.src_node, .names = names, }; try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl); + return sema.analyzeDeclVal(block, src, new_decl_index); } fn zirRetPtr( @@ -2444,7 +2468,7 @@ fn zirRetPtr( } const target = sema.mod.getTarget(); - const ptr_type = try Type.ptr(sema.arena, target, .{ + const ptr_type = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = sema.fn_ret_ty, .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); @@ -2535,14 +2559,13 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE else object_ty; - const target = sema.mod.getTarget(); if (!array_ty.isIndexable()) { const msg = msg: { const msg = try sema.errMsg( block, src, "type '{}' does not support indexing", - .{array_ty.fmt(target)}, + .{array_ty.fmt(sema.mod)}, ); errdefer msg.destroy(sema.gpa); try sema.errNote( @@ -2598,7 +2621,7 @@ fn zirAllocExtended( return sema.addConstant( inferred_alloc_ty, try Value.Tag.inferred_alloc_comptime.create(sema.arena, .{ - .decl = undefined, + .decl_index = undefined, .alignment = alignment, }), ); @@ -2612,7 +2635,7 @@ fn zirAllocExtended( const target = sema.mod.getTarget(); try sema.requireRuntimeBlock(block, src); try sema.resolveTypeLayout(block, src, var_ty); - const ptr_type = try Type.ptr(sema.arena, target, .{ + const ptr_type = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = var_ty, .@"align" = alignment, .@"addrspace" = target_util.defaultAddressSpace(target, .local), @@ -2649,7 +2672,7 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro const ptr_ty = sema.typeOf(ptr); var ptr_info = ptr_ty.ptrInfo().data; ptr_info.mutable = false; - const const_ptr_ty = try Type.ptr(sema.arena, sema.mod.getTarget(), ptr_info); + const const_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_info); if (try sema.resolveMaybeUndefVal(block, inst_data.src(), ptr)) |val| { return sema.addConstant(const_ptr_ty, val); @@ -2669,7 +2692,7 @@ fn zirAllocInferredComptime( return sema.addConstant( inferred_alloc_ty, try Value.Tag.inferred_alloc_comptime.create(sema.arena, .{ - .decl = undefined, + .decl_index = undefined, .alignment = 0, }), ); @@ -2687,7 +2710,7 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I return sema.analyzeComptimeAlloc(block, var_ty, 0, ty_src); } const target = sema.mod.getTarget(); - const ptr_type = try Type.ptr(sema.arena, target, .{ + const ptr_type = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = var_ty, .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); @@ -2709,7 +2732,7 @@ fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } try sema.validateVarType(block, ty_src, var_ty, false); const target = sema.mod.getTarget(); - const ptr_type = try Type.ptr(sema.arena, target, .{ + const ptr_type = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = var_ty, .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); @@ -2735,7 +2758,7 @@ fn zirAllocInferred( return sema.addConstant( inferred_alloc_ty, try Value.Tag.inferred_alloc_comptime.create(sema.arena, .{ - .decl = undefined, + .decl_index = undefined, .alignment = 0, }), ); @@ -2776,11 +2799,12 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com switch (ptr_val.tag()) { .inferred_alloc_comptime => { const iac = ptr_val.castTag(.inferred_alloc_comptime).?; - const decl = iac.data.decl; - try sema.mod.declareDeclDependency(sema.owner_decl, decl); + const decl_index = iac.data.decl_index; + try sema.mod.declareDeclDependency(sema.owner_decl_index, decl_index); + const decl = sema.mod.declPtr(decl_index); const final_elem_ty = try decl.ty.copy(sema.arena); - const final_ptr_ty = try Type.ptr(sema.arena, target, .{ + const final_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = final_elem_ty, .mutable = var_is_mut, .@"align" = iac.data.alignment, @@ -2791,11 +2815,11 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com if (var_is_mut) { sema.air_values.items[value_index] = try Value.Tag.decl_ref_mut.create(sema.arena, .{ - .decl = decl, + .decl_index = decl_index, .runtime_index = block.runtime_index, }); } else { - sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, decl); + sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, decl_index); } }, .inferred_alloc => { @@ -2803,7 +2827,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com const peer_inst_list = inferred_alloc.data.stored_inst_list.items; const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none); - const final_ptr_ty = try Type.ptr(sema.arena, target, .{ + const final_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = final_elem_ty, .mutable = var_is_mut, .@"align" = inferred_alloc.data.alignment, @@ -2873,22 +2897,22 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com if (store_op.lhs != Air.indexToRef(bitcast_inst)) break :ct; if (air_datas[bitcast_inst].ty_op.operand != Air.indexToRef(const_inst)) break :ct; - const new_decl = d: { + const new_decl_index = d: { var anon_decl = try block.startAnonDecl(src); defer anon_decl.deinit(); - const new_decl = try anon_decl.finish( + const new_decl_index = try anon_decl.finish( try final_elem_ty.copy(anon_decl.arena()), try store_val.copy(anon_decl.arena()), inferred_alloc.data.alignment, ); - break :d new_decl; + break :d new_decl_index; }; - try sema.mod.declareDeclDependency(sema.owner_decl, new_decl); + try sema.mod.declareDeclDependency(sema.owner_decl_index, new_decl_index); // Even though we reuse the constant instruction, we still remove it from the // block so that codegen does not see it. block.instructions.shrinkRetainingCapacity(block.instructions.items.len - 3); - sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, new_decl); + sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, new_decl_index); // if bitcast ty ref needs to be made const, make_ptr_const // ZIR handles it later, so we can just use the ty ref here. air_datas[ptr_inst].ty_pl.ty = air_datas[bitcast_inst].ty_op.ty; @@ -3218,10 +3242,11 @@ fn validateStructInit( } if (root_msg) |msg| { - const fqn = try struct_obj.getFullyQualifiedName(gpa); + const mod = sema.mod; + const fqn = try struct_obj.getFullyQualifiedName(mod); defer gpa.free(fqn); - try sema.mod.errNoteNonLazy( - struct_obj.srcLoc(), + try mod.errNoteNonLazy( + struct_obj.srcLoc(mod), msg, "struct '{s}' declared here", .{fqn}, @@ -3325,10 +3350,10 @@ fn validateStructInit( } if (root_msg) |msg| { - const fqn = try struct_obj.getFullyQualifiedName(gpa); + const fqn = try struct_obj.getFullyQualifiedName(sema.mod); defer gpa.free(fqn); try sema.mod.errNoteNonLazy( - struct_obj.srcLoc(), + struct_obj.srcLoc(sema.mod), msg, "struct '{s}' declared here", .{fqn}, @@ -3497,9 +3522,8 @@ fn failWithBadMemberAccess( else => unreachable, }; const msg = msg: { - const target = sema.mod.getTarget(); const msg = try sema.errMsg(block, field_src, "{s} '{}' has no member named '{s}'", .{ - kw_name, agg_ty.fmt(target), field_name, + kw_name, agg_ty.fmt(sema.mod), field_name, }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, agg_ty); @@ -3517,7 +3541,7 @@ fn failWithBadStructFieldAccess( ) CompileError { const gpa = sema.gpa; - const fqn = try struct_obj.getFullyQualifiedName(gpa); + const fqn = try struct_obj.getFullyQualifiedName(sema.mod); defer gpa.free(fqn); const msg = msg: { @@ -3528,7 +3552,7 @@ fn failWithBadStructFieldAccess( .{ field_name, fqn }, ); errdefer msg.destroy(gpa); - try sema.mod.errNoteNonLazy(struct_obj.srcLoc(), msg, "struct declared here", .{}); + try sema.mod.errNoteNonLazy(struct_obj.srcLoc(sema.mod), msg, "struct declared here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(block, msg); @@ -3543,7 +3567,7 @@ fn failWithBadUnionFieldAccess( ) CompileError { const gpa = sema.gpa; - const fqn = try union_obj.getFullyQualifiedName(gpa); + const fqn = try union_obj.getFullyQualifiedName(sema.mod); defer gpa.free(fqn); const msg = msg: { @@ -3554,14 +3578,14 @@ fn failWithBadUnionFieldAccess( .{ field_name, fqn }, ); errdefer msg.destroy(gpa); - try sema.mod.errNoteNonLazy(union_obj.srcLoc(), msg, "union declared here", .{}); + try sema.mod.errNoteNonLazy(union_obj.srcLoc(sema.mod), msg, "union declared here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(block, msg); } fn addDeclaredHereNote(sema: *Sema, parent: *Module.ErrorMsg, decl_ty: Type) !void { - const src_loc = decl_ty.declSrcLocOrNull() orelse return; + const src_loc = decl_ty.declSrcLocOrNull(sema.mod) orelse return; const category = switch (decl_ty.zigTypeTag()) { .Union => "union", .Struct => "struct", @@ -3645,7 +3669,7 @@ fn storeToInferredAlloc( try inferred_alloc.data.stored_inst_list.append(sema.arena, operand); // Create a runtime bitcast instruction with exactly the type the pointer wants. const target = sema.mod.getTarget(); - const ptr_ty = try Type.ptr(sema.arena, target, .{ + const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = operand_ty, .@"align" = inferred_alloc.data.alignment, .@"addrspace" = target_util.defaultAddressSpace(target, .local), @@ -3670,7 +3694,7 @@ fn storeToInferredAllocComptime( } var anon_decl = try block.startAnonDecl(src); defer anon_decl.deinit(); - iac.data.decl = try anon_decl.finish( + iac.data.decl_index = try anon_decl.finish( try operand_ty.copy(anon_decl.arena()), try operand_val.copy(anon_decl.arena()), iac.data.alignment, @@ -3869,7 +3893,6 @@ fn zirCompileLog( const src_node = extra.data.src_node; const src: LazySrcLoc = .{ .node_offset = src_node }; const args = sema.code.refSlice(extra.end, extended.small); - const target = sema.mod.getTarget(); for (args) |arg_ref, i| { if (i != 0) try writer.print(", ", .{}); @@ -3878,15 +3901,15 @@ fn zirCompileLog( const arg_ty = sema.typeOf(arg); if (try sema.resolveMaybeUndefVal(block, src, arg)) |val| { try writer.print("@as({}, {})", .{ - arg_ty.fmt(target), val.fmtValue(arg_ty, target), + arg_ty.fmt(sema.mod), val.fmtValue(arg_ty, sema.mod), }); } else { - try writer.print("@as({}, [runtime value])", .{arg_ty.fmt(target)}); + try writer.print("@as({}, [runtime value])", .{arg_ty.fmt(sema.mod)}); } } try writer.print("\n", .{}); - const gop = try sema.mod.compile_log_decls.getOrPut(sema.gpa, sema.owner_decl); + const gop = try sema.mod.compile_log_decls.getOrPut(sema.gpa, sema.owner_decl_index); if (!gop.found_existing) { gop.value_ptr.* = src_node; } @@ -3996,7 +4019,8 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr // Ignore the result, all the relevant operations have written to c_import_buf already. _ = try sema.analyzeBodyBreak(&child_block, body); - const c_import_res = sema.mod.comp.cImport(c_import_buf.items) catch |err| + const mod = sema.mod; + const c_import_res = mod.comp.cImport(c_import_buf.items) catch |err| return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); if (c_import_res.errors.len != 0) { @@ -4004,12 +4028,12 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr const msg = try sema.errMsg(&child_block, src, "C import failed", .{}); errdefer msg.destroy(sema.gpa); - if (!sema.mod.comp.bin_file.options.link_libc) + if (!mod.comp.bin_file.options.link_libc) try sema.errNote(&child_block, src, msg, "libc headers not available; compilation does not link against libc", .{}); for (c_import_res.errors) |_| { // TODO integrate with LazySrcLoc - // try sema.mod.errNoteNonLazy(.{}, msg, "{s}", .{clang_err.msg_ptr[0..clang_err.msg_len]}); + // try mod.errNoteNonLazy(.{}, msg, "{s}", .{clang_err.msg_ptr[0..clang_err.msg_len]}); // if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)", // clang_err.line + 1, // clang_err.column + 1, @@ -4027,20 +4051,21 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr error.OutOfMemory => return error.OutOfMemory, else => unreachable, // we pass null for root_src_dir_path }; - const std_pkg = sema.mod.main_pkg.table.get("std").?; - const builtin_pkg = sema.mod.main_pkg.table.get("builtin").?; + const std_pkg = mod.main_pkg.table.get("std").?; + const builtin_pkg = mod.main_pkg.table.get("builtin").?; try c_import_pkg.add(sema.gpa, "builtin", builtin_pkg); try c_import_pkg.add(sema.gpa, "std", std_pkg); - const result = sema.mod.importPkg(c_import_pkg) catch |err| + const result = mod.importPkg(c_import_pkg) catch |err| return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); - sema.mod.astGenFile(result.file) catch |err| + mod.astGenFile(result.file) catch |err| return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); - try sema.mod.semaFile(result.file); - const file_root_decl = result.file.root_decl.?; - try sema.mod.declareDeclDependency(sema.owner_decl, file_root_decl); + try mod.semaFile(result.file); + const file_root_decl_index = result.file.root_decl.unwrap().?; + const file_root_decl = mod.declPtr(file_root_decl_index); + try mod.declareDeclDependency(sema.owner_decl_index, file_root_decl_index); return sema.addConstant(file_root_decl.ty, file_root_decl.val); } @@ -4139,6 +4164,7 @@ fn analyzeBlockBody( defer tracy.end(); const gpa = sema.gpa; + const mod = sema.mod; // Blocks must terminate with noreturn instruction. assert(child_block.instructions.items.len != 0); @@ -4173,16 +4199,16 @@ fn analyzeBlockBody( const type_src = src; // TODO: better source location const valid_rt = try sema.validateRunTimeType(child_block, type_src, resolved_ty, false); - const target = sema.mod.getTarget(); if (!valid_rt) { const msg = msg: { - const msg = try sema.errMsg(child_block, type_src, "value with comptime only type '{}' depends on runtime control flow", .{resolved_ty.fmt(target)}); + const msg = try sema.errMsg(child_block, type_src, "value with comptime only type '{}' depends on runtime control flow", .{resolved_ty.fmt(mod)}); errdefer msg.destroy(sema.gpa); const runtime_src = child_block.runtime_cond orelse child_block.runtime_loop.?; try sema.errNote(child_block, runtime_src, msg, "runtime control flow here", .{}); - try sema.explainWhyTypeIsComptime(child_block, type_src, msg, type_src.toSrcLoc(child_block.src_decl), resolved_ty); + const child_src_decl = mod.declPtr(child_block.src_decl); + try sema.explainWhyTypeIsComptime(child_block, type_src, msg, type_src.toSrcLoc(child_src_decl), resolved_ty); break :msg msg; }; @@ -4204,7 +4230,7 @@ fn analyzeBlockBody( const br_operand = sema.air_instructions.items(.data)[br].br.operand; const br_operand_src = src; const br_operand_ty = sema.typeOf(br_operand); - if (br_operand_ty.eql(resolved_ty, target)) { + if (br_operand_ty.eql(resolved_ty, mod)) { // No type coercion needed. continue; } @@ -4262,9 +4288,9 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void if (extra.namespace != .none) { return sema.fail(block, src, "TODO: implement exporting with field access", .{}); } - const decl = try sema.lookupIdentifier(block, operand_src, decl_name); + const decl_index = try sema.lookupIdentifier(block, operand_src, decl_name); const options = try sema.resolveExportOptions(block, options_src, extra.options); - try sema.analyzeExport(block, src, options, decl); + try sema.analyzeExport(block, src, options, decl_index); } fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { @@ -4278,11 +4304,11 @@ fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const operand = try sema.resolveInstConst(block, operand_src, extra.operand); const options = try sema.resolveExportOptions(block, options_src, extra.options); - const decl = switch (operand.val.tag()) { + const decl_index = switch (operand.val.tag()) { .function => operand.val.castTag(.function).?.data.owner_decl, else => return sema.fail(block, operand_src, "TODO implement exporting arbitrary Value objects", .{}), // TODO put this Value into an anonymous Decl and then export it. }; - try sema.analyzeExport(block, src, options, decl); + try sema.analyzeExport(block, src, options, decl_index); } pub fn analyzeExport( @@ -4290,18 +4316,18 @@ pub fn analyzeExport( block: *Block, src: LazySrcLoc, borrowed_options: std.builtin.ExportOptions, - exported_decl: *Decl, + exported_decl_index: Decl.Index, ) !void { const Export = Module.Export; const mod = sema.mod; - const target = mod.getTarget(); - try mod.ensureDeclAnalyzed(exported_decl); + try mod.ensureDeclAnalyzed(exported_decl_index); + const exported_decl = mod.declPtr(exported_decl_index); // TODO run the same checks as we do for C ABI struct fields switch (exported_decl.ty.zigTypeTag()) { .Fn, .Int, .Enum, .Struct, .Union, .Array, .Float => {}, else => return sema.fail(block, src, "unable to export type '{}'", .{ - exported_decl.ty.fmt(target), + exported_decl.ty.fmt(sema.mod), }), } @@ -4319,13 +4345,6 @@ pub fn analyzeExport( const section: ?[]const u8 = if (borrowed_options.section) |s| try gpa.dupe(u8, s) else null; errdefer if (section) |s| gpa.free(s); - const src_decl = block.src_decl; - const owner_decl = sema.owner_decl; - - log.debug("exporting Decl '{s}' as symbol '{s}' from Decl '{s}'", .{ - exported_decl.name, symbol_name, owner_decl.name, - }); - new_export.* = .{ .options = .{ .name = symbol_name, @@ -4343,14 +4362,14 @@ pub fn analyzeExport( .spirv => .{ .spirv = {} }, .nvptx => .{ .nvptx = {} }, }, - .owner_decl = owner_decl, - .src_decl = src_decl, - .exported_decl = exported_decl, + .owner_decl = sema.owner_decl_index, + .src_decl = block.src_decl, + .exported_decl = exported_decl_index, .status = .in_progress, }; // Add to export_owners table. - const eo_gop = mod.export_owners.getOrPutAssumeCapacity(owner_decl); + const eo_gop = mod.export_owners.getOrPutAssumeCapacity(sema.owner_decl_index); if (!eo_gop.found_existing) { eo_gop.value_ptr.* = &[0]*Export{}; } @@ -4359,7 +4378,7 @@ pub fn analyzeExport( errdefer eo_gop.value_ptr.* = gpa.shrink(eo_gop.value_ptr.*, eo_gop.value_ptr.len - 1); // Add to exported_decl table. - const de_gop = mod.decl_exports.getOrPutAssumeCapacity(exported_decl); + const de_gop = mod.decl_exports.getOrPutAssumeCapacity(exported_decl_index); if (!de_gop.found_existing) { de_gop.value_ptr.* = &[0]*Export{}; } @@ -4381,7 +4400,8 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const func = sema.owner_func orelse return sema.fail(block, src, "@setAlignStack outside function body", .{}); - switch (func.owner_decl.ty.fnCallingConvention()) { + const fn_owner_decl = sema.mod.declPtr(func.owner_decl); + switch (fn_owner_decl.ty.fnCallingConvention()) { .Naked => return sema.fail(block, src, "@setAlignStack in naked function", .{}), .Inline => return sema.fail(block, src, "@setAlignStack in inline function", .{}), else => {}, @@ -4561,8 +4581,8 @@ fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const inst_data = sema.code.instructions.items(.data)[inst].str_tok; const src = inst_data.src(); const decl_name = inst_data.get(sema.code); - const decl = try sema.lookupIdentifier(block, src, decl_name); - return sema.analyzeDeclRef(decl); + const decl_index = try sema.lookupIdentifier(block, src, decl_name); + return sema.analyzeDeclRef(decl_index); } fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -4573,11 +4593,11 @@ fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air return sema.analyzeDeclVal(block, src, decl); } -fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: []const u8) !*Decl { +fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: []const u8) !Decl.Index { var namespace = block.namespace; while (true) { - if (try sema.lookupInNamespace(block, src, namespace, name, false)) |decl| { - return decl; + if (try sema.lookupInNamespace(block, src, namespace, name, false)) |decl_index| { + return decl_index; } namespace = namespace.parent orelse break; } @@ -4593,12 +4613,13 @@ fn lookupInNamespace( namespace: *Namespace, ident_name: []const u8, observe_usingnamespace: bool, -) CompileError!?*Decl { +) CompileError!?Decl.Index { const mod = sema.mod; - const namespace_decl = namespace.getDecl(); + const namespace_decl_index = namespace.getDeclIndex(); + const namespace_decl = sema.mod.declPtr(namespace_decl_index); if (namespace_decl.analysis == .file_failure) { - try mod.declareDeclDependency(sema.owner_decl, namespace_decl); + try mod.declareDeclDependency(sema.owner_decl_index, namespace_decl_index); return error.AnalysisFail; } @@ -4610,7 +4631,7 @@ fn lookupInNamespace( defer checked_namespaces.deinit(gpa); // Keep track of name conflicts for error notes. - var candidates: std.ArrayListUnmanaged(*Decl) = .{}; + var candidates: std.ArrayListUnmanaged(Decl.Index) = .{}; defer candidates.deinit(gpa); try checked_namespaces.put(gpa, namespace, {}); @@ -4618,23 +4639,25 @@ fn lookupInNamespace( while (check_i < checked_namespaces.count()) : (check_i += 1) { const check_ns = checked_namespaces.keys()[check_i]; - if (check_ns.decls.getKeyAdapted(ident_name, Module.DeclAdapter{})) |decl| { + if (check_ns.decls.getKeyAdapted(ident_name, Module.DeclAdapter{ .mod = mod })) |decl_index| { // Skip decls which are not marked pub, which are in a different // file than the `a.b`/`@hasDecl` syntax. + const decl = mod.declPtr(decl_index); if (decl.is_pub or src_file == decl.getFileScope()) { - try candidates.append(gpa, decl); + try candidates.append(gpa, decl_index); } } var it = check_ns.usingnamespace_set.iterator(); while (it.next()) |entry| { - const sub_usingnamespace_decl = entry.key_ptr.*; + const sub_usingnamespace_decl_index = entry.key_ptr.*; + const sub_usingnamespace_decl = mod.declPtr(sub_usingnamespace_decl_index); const sub_is_pub = entry.value_ptr.*; if (!sub_is_pub and src_file != sub_usingnamespace_decl.getFileScope()) { // Skip usingnamespace decls which are not marked pub, which are in // a different file than the `a.b`/`@hasDecl` syntax. continue; } - try sema.ensureDeclAnalyzed(sub_usingnamespace_decl); + try sema.ensureDeclAnalyzed(sub_usingnamespace_decl_index); const ns_ty = sub_usingnamespace_decl.val.castTag(.ty).?.data; const sub_ns = ns_ty.getNamespace().?; try checked_namespaces.put(gpa, sub_ns, {}); @@ -4644,15 +4667,16 @@ fn lookupInNamespace( switch (candidates.items.len) { 0 => {}, 1 => { - const decl = candidates.items[0]; - try mod.declareDeclDependency(sema.owner_decl, decl); - return decl; + const decl_index = candidates.items[0]; + try mod.declareDeclDependency(sema.owner_decl_index, decl_index); + return decl_index; }, else => { const msg = msg: { const msg = try sema.errMsg(block, src, "ambiguous reference", .{}); errdefer msg.destroy(gpa); - for (candidates.items) |candidate| { + for (candidates.items) |candidate_index| { + const candidate = mod.declPtr(candidate_index); const src_loc = candidate.srcLoc(); try mod.errNoteNonLazy(src_loc, msg, "declared here", .{}); } @@ -4661,9 +4685,9 @@ fn lookupInNamespace( return sema.failWithOwnedErrorMsg(block, msg); }, } - } else if (namespace.decls.getKeyAdapted(ident_name, Module.DeclAdapter{})) |decl| { - try mod.declareDeclDependency(sema.owner_decl, decl); - return decl; + } else if (namespace.decls.getKeyAdapted(ident_name, Module.DeclAdapter{ .mod = mod })) |decl_index| { + try mod.declareDeclDependency(sema.owner_decl_index, decl_index); + return decl_index; } log.debug("{*} ({s}) depends on non-existence of '{s}' in {*} ({s})", .{ @@ -4672,7 +4696,7 @@ fn lookupInNamespace( // TODO This dependency is too strong. Really, it should only be a dependency // on the non-existence of `ident_name` in the namespace. We can lessen the number of // outdated declarations by making this dependency more sophisticated. - try mod.declareDeclDependency(sema.owner_decl, namespace_decl); + try mod.declareDeclDependency(sema.owner_decl_index, namespace_decl_index); return null; } @@ -4725,13 +4749,14 @@ const GenericCallAdapter = struct { /// Unlike comptime_args, the Type here is not always present. /// .generic_poison is used to communicate non-anytype parameters. comptime_tvs: []const TypedValue, - target: std.Target, + module: *Module, pub fn eql(ctx: @This(), adapted_key: void, other_key: *Module.Fn) bool { _ = adapted_key; // The generic function Decl is guaranteed to be the first dependency // of each of its instantiations. - const generic_owner_decl = other_key.owner_decl.dependencies.keys()[0]; + const other_owner_decl = ctx.module.declPtr(other_key.owner_decl); + const generic_owner_decl = other_owner_decl.dependencies.keys()[0]; if (ctx.generic_fn.owner_decl != generic_owner_decl) return false; const other_comptime_args = other_key.comptime_args.?; @@ -4747,18 +4772,18 @@ const GenericCallAdapter = struct { if (this_is_anytype) { // Both are anytype parameters. - if (!this_arg.ty.eql(other_arg.ty, ctx.target)) { + if (!this_arg.ty.eql(other_arg.ty, ctx.module)) { return false; } if (this_is_comptime) { // Both are comptime and anytype parameters with matching types. - if (!this_arg.val.eql(other_arg.val, other_arg.ty, ctx.target)) { + if (!this_arg.val.eql(other_arg.val, other_arg.ty, ctx.module)) { return false; } } } else if (this_is_comptime) { // Both are comptime parameters but not anytype parameters. - if (!this_arg.val.eql(other_arg.val, other_arg.ty, ctx.target)) { + if (!this_arg.val.eql(other_arg.val, other_arg.ty, ctx.module)) { return false; } } @@ -4787,7 +4812,6 @@ fn analyzeCall( const mod = sema.mod; const callee_ty = sema.typeOf(func); - const target = sema.mod.getTarget(); const func_ty = func_ty: { switch (callee_ty.zigTypeTag()) { .Fn => break :func_ty callee_ty, @@ -4799,7 +4823,7 @@ fn analyzeCall( }, else => {}, } - return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty.fmt(target)}); + return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty.fmt(sema.mod)}); }; const func_ty_info = func_ty.fnInfo(); @@ -4891,7 +4915,7 @@ fn analyzeCall( const result: Air.Inst.Ref = if (is_inline_call) res: { const func_val = try sema.resolveConstValue(block, func_src, func); const module_fn = switch (func_val.tag()) { - .decl_ref => func_val.castTag(.decl_ref).?.data.val.castTag(.function).?.data, + .decl_ref => mod.declPtr(func_val.castTag(.decl_ref).?.data).val.castTag(.function).?.data, .function => func_val.castTag(.function).?.data, .extern_fn => return sema.fail(block, call_src, "{s} call of extern function", .{ @as([]const u8, if (is_comptime_call) "comptime" else "inline"), @@ -4922,7 +4946,8 @@ fn analyzeCall( // In order to save a bit of stack space, directly modify Sema rather // than create a child one. const parent_zir = sema.code; - sema.code = module_fn.owner_decl.getFileScope().zir; + const fn_owner_decl = mod.declPtr(module_fn.owner_decl); + sema.code = fn_owner_decl.getFileScope().zir; defer sema.code = parent_zir; const parent_inst_map = sema.inst_map; @@ -4936,14 +4961,14 @@ fn analyzeCall( sema.func = module_fn; defer sema.func = parent_func; - var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, module_fn.owner_decl.src_scope); + var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, fn_owner_decl.src_scope); defer wip_captures.deinit(); var child_block: Block = .{ .parent = null, .sema = sema, .src_decl = module_fn.owner_decl, - .namespace = module_fn.owner_decl.src_namespace, + .namespace = fn_owner_decl.src_namespace, .wip_capture_scope = wip_captures.scope, .instructions = .{}, .label = null, @@ -4976,7 +5001,7 @@ fn analyzeCall( // comptime state. var should_memoize = true; - var new_fn_info = module_fn.owner_decl.ty.fnInfo(); + var new_fn_info = fn_owner_decl.ty.fnInfo(); new_fn_info.param_types = try sema.arena.alloc(Type, new_fn_info.param_types.len); new_fn_info.comptime_params = (try sema.arena.alloc(bool, new_fn_info.param_types.len)).ptr; @@ -5073,7 +5098,7 @@ fn analyzeCall( const bare_return_type = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst); // Create a fresh inferred error set type for inline/comptime calls. const fn_ret_ty = blk: { - if (module_fn.hasInferredErrorSet()) { + if (module_fn.hasInferredErrorSet(mod)) { const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode); node.data = .{ .func = module_fn }; if (parent_func) |some| { @@ -5097,7 +5122,7 @@ fn analyzeCall( // bug generating invalid LLVM IR. const res2: Air.Inst.Ref = res2: { if (should_memoize and is_comptime_call) { - if (mod.memoized_calls.getContext(memoized_call_key, .{ .target = target })) |result| { + if (mod.memoized_calls.getContext(memoized_call_key, .{ .module = mod })) |result| { const ty_inst = try sema.addType(fn_ret_ty); try sema.air_values.append(gpa, result.val); sema.air_instructions.set(block_inst, .{ @@ -5150,7 +5175,13 @@ fn analyzeCall( }; if (!is_comptime_call) { - try sema.emitDbgInline(block, module_fn, parent_func.?, parent_func.?.owner_decl.ty, .dbg_inline_end); + try sema.emitDbgInline( + block, + module_fn, + parent_func.?, + mod.declPtr(parent_func.?.owner_decl).ty, + .dbg_inline_end, + ); } if (should_memoize and is_comptime_call) { @@ -5172,7 +5203,7 @@ fn analyzeCall( try mod.memoized_calls.putContext(gpa, memoized_call_key, .{ .val = try result_val.copy(arena), .arena = arena_allocator.state, - }, .{ .target = sema.mod.getTarget() }); + }, .{ .module = mod }); delete_memoized_call_key = false; } } @@ -5239,13 +5270,14 @@ fn instantiateGenericCall( const func_val = try sema.resolveConstValue(block, func_src, func); const module_fn = switch (func_val.tag()) { .function => func_val.castTag(.function).?.data, - .decl_ref => func_val.castTag(.decl_ref).?.data.val.castTag(.function).?.data, + .decl_ref => mod.declPtr(func_val.castTag(.decl_ref).?.data).val.castTag(.function).?.data, else => unreachable, }; // Check the Module's generic function map with an adapted context, so that we // can match against `uncasted_args` rather than doing the work below to create a // generic Scope only to junk it if it matches an existing instantiation. - const namespace = module_fn.owner_decl.src_namespace; + const fn_owner_decl = mod.declPtr(module_fn.owner_decl); + const namespace = fn_owner_decl.src_namespace; const fn_zir = namespace.file_scope.zir; const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst); const zir_tags = fn_zir.instructions.items(.tag); @@ -5261,7 +5293,6 @@ fn instantiateGenericCall( std.hash.autoHash(&hasher, @ptrToInt(module_fn)); const comptime_tvs = try sema.arena.alloc(TypedValue, func_ty_info.param_types.len); - const target = sema.mod.getTarget(); { var i: usize = 0; @@ -5290,9 +5321,9 @@ fn instantiateGenericCall( const arg_src = call_src; // TODO better source location const arg_ty = sema.typeOf(uncasted_args[i]); const arg_val = try sema.resolveValue(block, arg_src, uncasted_args[i]); - arg_val.hash(arg_ty, &hasher, target); + arg_val.hash(arg_ty, &hasher, mod); if (is_anytype) { - arg_ty.hashWithHasher(&hasher, target); + arg_ty.hashWithHasher(&hasher, mod); comptime_tvs[i] = .{ .ty = arg_ty, .val = arg_val, @@ -5305,7 +5336,7 @@ fn instantiateGenericCall( } } else if (is_anytype) { const arg_ty = sema.typeOf(uncasted_args[i]); - arg_ty.hashWithHasher(&hasher, target); + arg_ty.hashWithHasher(&hasher, mod); comptime_tvs[i] = .{ .ty = arg_ty, .val = Value.initTag(.generic_poison), @@ -5328,7 +5359,7 @@ fn instantiateGenericCall( .precomputed_hash = precomputed_hash, .func_ty_info = func_ty_info, .comptime_tvs = comptime_tvs, - .target = target, + .module = mod, }; const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter); const callee = if (!gop.found_existing) callee: { @@ -5343,37 +5374,40 @@ fn instantiateGenericCall( try namespace.anon_decls.ensureUnusedCapacity(gpa, 1); // Create a Decl for the new function. - const src_decl = namespace.getDecl(); + const src_decl_index = namespace.getDeclIndex(); + const src_decl = mod.declPtr(src_decl_index); + const new_decl_index = try mod.allocateNewDecl(namespace, fn_owner_decl.src_node, src_decl.src_scope); + errdefer mod.destroyDecl(new_decl_index); + const new_decl = mod.declPtr(new_decl_index); // TODO better names for generic function instantiations - const name_index = mod.getNextAnonNameIndex(); const decl_name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{ - module_fn.owner_decl.name, name_index, + fn_owner_decl.name, @enumToInt(new_decl_index), }); - const new_decl = try mod.allocateNewDecl(decl_name, namespace, module_fn.owner_decl.src_node, src_decl.src_scope); - errdefer new_decl.destroy(mod); - new_decl.src_line = module_fn.owner_decl.src_line; - new_decl.is_pub = module_fn.owner_decl.is_pub; - new_decl.is_exported = module_fn.owner_decl.is_exported; - new_decl.has_align = module_fn.owner_decl.has_align; - new_decl.has_linksection_or_addrspace = module_fn.owner_decl.has_linksection_or_addrspace; - new_decl.@"addrspace" = module_fn.owner_decl.@"addrspace"; - new_decl.zir_decl_index = module_fn.owner_decl.zir_decl_index; + new_decl.name = decl_name; + new_decl.src_line = fn_owner_decl.src_line; + new_decl.is_pub = fn_owner_decl.is_pub; + new_decl.is_exported = fn_owner_decl.is_exported; + new_decl.has_align = fn_owner_decl.has_align; + new_decl.has_linksection_or_addrspace = fn_owner_decl.has_linksection_or_addrspace; + new_decl.@"addrspace" = fn_owner_decl.@"addrspace"; + new_decl.zir_decl_index = fn_owner_decl.zir_decl_index; new_decl.alive = true; // This Decl is called at runtime. new_decl.analysis = .in_progress; new_decl.generation = mod.generation; - namespace.anon_decls.putAssumeCapacityNoClobber(new_decl, {}); - errdefer assert(namespace.anon_decls.orderedRemove(new_decl)); + namespace.anon_decls.putAssumeCapacityNoClobber(new_decl_index, {}); + errdefer assert(namespace.anon_decls.orderedRemove(new_decl_index)); // The generic function Decl is guaranteed to be the first dependency // of each of its instantiations. assert(new_decl.dependencies.keys().len == 0); - try mod.declareDeclDependency(new_decl, module_fn.owner_decl); + try mod.declareDeclDependency(new_decl_index, module_fn.owner_decl); // Resolving the new function type below will possibly declare more decl dependencies // and so we remove them all here in case of error. errdefer { - for (new_decl.dependencies.keys()) |dep| { - dep.removeDependant(new_decl); + for (new_decl.dependencies.keys()) |dep_index| { + const dep = mod.declPtr(dep_index); + dep.removeDependant(new_decl_index); } } @@ -5392,6 +5426,7 @@ fn instantiateGenericCall( .perm_arena = new_decl_arena_allocator, .code = fn_zir, .owner_decl = new_decl, + .owner_decl_index = new_decl_index, .func = null, .fn_ret_ty = Type.void, .owner_func = null, @@ -5407,7 +5442,7 @@ fn instantiateGenericCall( var child_block: Block = .{ .parent = null, .sema = &child_sema, - .src_decl = new_decl, + .src_decl = new_decl_index, .namespace = namespace, .wip_capture_scope = wip_captures.scope, .instructions = .{}, @@ -5564,7 +5599,7 @@ fn instantiateGenericCall( // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field // will be populated, ensuring it will have `analyzeBody` called with the ZIR // parameters mapped appropriately. - try mod.comp.bin_file.allocateDeclIndexes(new_decl); + try mod.comp.bin_file.allocateDeclIndexes(new_decl_index); try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func }); try new_decl.finalizeNewArena(&new_decl_arena); @@ -5577,7 +5612,7 @@ fn instantiateGenericCall( try sema.requireRuntimeBlock(block, call_src); const comptime_args = callee.comptime_args.?; - const new_fn_info = callee.owner_decl.ty.fnInfo(); + const new_fn_info = mod.declPtr(callee.owner_decl).ty.fnInfo(); const runtime_args_len = @intCast(u32, new_fn_info.param_types.len); const runtime_args = try sema.arena.alloc(Air.Inst.Ref, runtime_args_len); { @@ -5700,8 +5735,7 @@ fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const bin_inst = sema.code.instructions.items(.data)[inst].bin; const len = try sema.resolveInt(block, .unneeded, bin_inst.lhs, Type.usize); const elem_type = try sema.resolveType(block, .unneeded, bin_inst.rhs); - const target = sema.mod.getTarget(); - const array_ty = try Type.array(sema.arena, len, null, elem_type, target); + const array_ty = try Type.array(sema.arena, len, null, elem_type, sema.mod); return sema.addType(array_ty); } @@ -5720,8 +5754,7 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil const uncasted_sentinel = sema.resolveInst(extra.sentinel); const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src); const sentinel_val = try sema.resolveConstValue(block, sentinel_src, sentinel); - const target = sema.mod.getTarget(); - const array_ty = try Type.array(sema.arena, len, sentinel_val, elem_type, target); + const array_ty = try Type.array(sema.arena, len, sentinel_val, elem_type, sema.mod); return sema.addType(array_ty); } @@ -5748,14 +5781,13 @@ fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; const error_set = try sema.resolveType(block, lhs_src, extra.lhs); const payload = try sema.resolveType(block, rhs_src, extra.rhs); - const target = sema.mod.getTarget(); if (error_set.zigTypeTag() != .ErrorSet) { return sema.fail(block, lhs_src, "expected error set type, found {}", .{ - error_set.fmt(target), + error_set.fmt(sema.mod), }); } - const err_union_ty = try Type.errorUnion(sema.arena, error_set, payload, target); + const err_union_ty = try Type.errorUnion(sema.arena, error_set, payload, sema.mod); return sema.addType(err_union_ty); } @@ -5862,11 +5894,10 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr } const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs); const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs); - const target = sema.mod.getTarget(); if (lhs_ty.zigTypeTag() != .ErrorSet) - return sema.fail(block, lhs_src, "expected error set type, found {}", .{lhs_ty.fmt(target)}); + return sema.fail(block, lhs_src, "expected error set type, found {}", .{lhs_ty.fmt(sema.mod)}); if (rhs_ty.zigTypeTag() != .ErrorSet) - return sema.fail(block, rhs_src, "expected error set type, found {}", .{rhs_ty.fmt(target)}); + return sema.fail(block, rhs_src, "expected error set type, found {}", .{rhs_ty.fmt(sema.mod)}); // Anything merged with anyerror is anyerror. if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) { @@ -5912,7 +5943,6 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); - const target = sema.mod.getTarget(); const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag()) { .Enum => operand, @@ -5929,7 +5959,7 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A }, else => { return sema.fail(block, operand_src, "expected enum or tagged union, found {}", .{ - operand_ty.fmt(target), + operand_ty.fmt(sema.mod), }); }, }; @@ -5953,7 +5983,6 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const target = sema.mod.getTarget(); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const src = inst_data.src(); @@ -5963,7 +5992,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const operand = sema.resolveInst(extra.rhs); if (dest_ty.zigTypeTag() != .Enum) { - return sema.fail(block, dest_ty_src, "expected enum, found {}", .{dest_ty.fmt(target)}); + return sema.fail(block, dest_ty_src, "expected enum, found {}", .{dest_ty.fmt(sema.mod)}); } if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |int_val| { @@ -5973,17 +6002,17 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A if (int_val.isUndef()) { return sema.failWithUseOfUndef(block, operand_src); } - if (!dest_ty.enumHasInt(int_val, target)) { + if (!dest_ty.enumHasInt(int_val, sema.mod)) { const msg = msg: { const msg = try sema.errMsg( block, src, "enum '{}' has no tag with value {}", - .{ dest_ty.fmt(target), int_val.fmtValue(sema.typeOf(operand), target) }, + .{ dest_ty.fmt(sema.mod), int_val.fmtValue(sema.typeOf(operand), sema.mod) }, ); errdefer msg.destroy(sema.gpa); try sema.mod.errNoteNonLazy( - dest_ty.declSrcLoc(), + dest_ty.declSrcLoc(sema.mod), msg, "enum declared here", .{}, @@ -6028,14 +6057,13 @@ fn analyzeOptionalPayloadPtr( const optional_ptr_ty = sema.typeOf(optional_ptr); assert(optional_ptr_ty.zigTypeTag() == .Pointer); - const target = sema.mod.getTarget(); const opt_type = optional_ptr_ty.elemType(); if (opt_type.zigTypeTag() != .Optional) { - return sema.fail(block, src, "expected optional type, found {}", .{opt_type.fmt(target)}); + return sema.fail(block, src, "expected optional type, found {}", .{opt_type.fmt(sema.mod)}); } const child_type = try opt_type.optionalChildAlloc(sema.arena); - const child_pointer = try Type.ptr(sema.arena, target, .{ + const child_pointer = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = child_type, .mutable = !optional_ptr_ty.isConstPtr(), .@"addrspace" = optional_ptr_ty.ptrAddressSpace(), @@ -6106,8 +6134,7 @@ fn zirOptionalPayload( return sema.failWithExpectedOptionalType(block, src, operand_ty); } const ptr_info = operand_ty.ptrInfo().data; - const target = sema.mod.getTarget(); - break :t try Type.ptr(sema.arena, target, .{ + break :t try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = try ptr_info.pointee_type.copy(sema.arena), .@"align" = ptr_info.@"align", .@"addrspace" = ptr_info.@"addrspace", @@ -6154,9 +6181,8 @@ fn zirErrUnionPayload( const operand_src = src; const operand_ty = sema.typeOf(operand); if (operand_ty.zigTypeTag() != .ErrorUnion) { - const target = sema.mod.getTarget(); return sema.fail(block, operand_src, "expected error union type, found '{}'", .{ - operand_ty.fmt(target), + operand_ty.fmt(sema.mod), }); } @@ -6205,15 +6231,14 @@ fn analyzeErrUnionPayloadPtr( const operand_ty = sema.typeOf(operand); assert(operand_ty.zigTypeTag() == .Pointer); - const target = sema.mod.getTarget(); if (operand_ty.elemType().zigTypeTag() != .ErrorUnion) { return sema.fail(block, src, "expected error union type, found {}", .{ - operand_ty.elemType().fmt(target), + operand_ty.elemType().fmt(sema.mod), }); } const payload_ty = operand_ty.elemType().errorUnionPayload(); - const operand_pointer_ty = try Type.ptr(sema.arena, target, .{ + const operand_pointer_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = payload_ty, .mutable = !operand_ty.isConstPtr(), .@"addrspace" = operand_ty.ptrAddressSpace(), @@ -6272,10 +6297,9 @@ fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro const src = inst_data.src(); const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); - const target = sema.mod.getTarget(); if (operand_ty.zigTypeTag() != .ErrorUnion) { return sema.fail(block, src, "expected error union type, found '{}'", .{ - operand_ty.fmt(target), + operand_ty.fmt(sema.mod), }); } @@ -6302,9 +6326,8 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE assert(operand_ty.zigTypeTag() == .Pointer); if (operand_ty.elemType().zigTypeTag() != .ErrorUnion) { - const target = sema.mod.getTarget(); return sema.fail(block, src, "expected error union type, found {}", .{ - operand_ty.elemType().fmt(target), + operand_ty.elemType().fmt(sema.mod), }); } @@ -6329,10 +6352,9 @@ fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com const src = inst_data.src(); const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); - const target = sema.mod.getTarget(); if (operand_ty.zigTypeTag() != .ErrorUnion) { return sema.fail(block, src, "expected error union type, found '{}'", .{ - operand_ty.fmt(target), + operand_ty.fmt(sema.mod), }); } if (operand_ty.errorUnionPayload().zigTypeTag() != .Void) { @@ -6606,7 +6628,7 @@ fn funcCommon( errdefer sema.gpa.destroy(new_extern_fn); new_extern_fn.* = Module.ExternFn{ - .owner_decl = sema.owner_decl, + .owner_decl = sema.owner_decl_index, .lib_name = null, }; @@ -6645,7 +6667,7 @@ fn funcCommon( new_func.* = .{ .state = anal_state, .zir_body_inst = func_inst, - .owner_decl = sema.owner_decl, + .owner_decl = sema.owner_decl_index, .comptime_args = comptime_args, .anytype_args = undefined, .hash = hash, @@ -6838,8 +6860,7 @@ fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const ptr = sema.resolveInst(inst_data.operand); const ptr_ty = sema.typeOf(ptr); if (!ptr_ty.isPtrAtRuntime()) { - const target = sema.mod.getTarget(); - return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(target)}); + return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)}); } if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |ptr_val| { return sema.addConstant(Type.usize, ptr_val); @@ -7018,7 +7039,6 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const target = sema.mod.getTarget(); const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); switch (dest_ty.zigTypeTag()) { @@ -7038,10 +7058,10 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .Type, .Undefined, .Void, - => return sema.fail(block, dest_ty_src, "invalid type '{}' for @bitCast", .{dest_ty.fmt(target)}), + => return sema.fail(block, dest_ty_src, "invalid type '{}' for @bitCast", .{dest_ty.fmt(sema.mod)}), .Pointer => return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}', use @ptrCast to cast to a pointer", .{ - dest_ty.fmt(target), + dest_ty.fmt(sema.mod), }), .Struct, .Union => if (dest_ty.containerLayout() == .Auto) { const container = switch (dest_ty.zigTypeTag()) { @@ -7050,7 +7070,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air else => unreachable, }; return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}', {s} does not have a guaranteed in-memory layout", .{ - dest_ty.fmt(target), container, + dest_ty.fmt(sema.mod), container, }); }, .BoundFn => @panic("TODO remove this type from the language and compiler"), @@ -7088,7 +7108,7 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A block, dest_ty_src, "expected float type, found '{}'", - .{dest_ty.fmt(target)}, + .{dest_ty.fmt(sema.mod)}, ), }; @@ -7099,7 +7119,7 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A block, operand_src, "expected float type, found '{}'", - .{operand_ty.fmt(target)}, + .{operand_ty.fmt(sema.mod)}, ), } @@ -7241,7 +7261,6 @@ fn zirSwitchCapture( const operand_ptr = sema.resolveInst(cond_info.operand); const operand_ptr_ty = sema.typeOf(operand_ptr); const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty; - const target = sema.mod.getTarget(); const operand = if (operand_is_ref) try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src) @@ -7277,7 +7296,7 @@ fn zirSwitchCapture( // Previous switch validation ensured this will succeed const first_item_val = sema.resolveConstValue(block, .unneeded, first_item) catch unreachable; - const first_field_index = @intCast(u32, enum_ty.enumTagFieldIndex(first_item_val, target).?); + const first_field_index = @intCast(u32, enum_ty.enumTagFieldIndex(first_item_val, sema.mod).?); const first_field = union_obj.fields.values()[first_field_index]; for (items[1..]) |item| { @@ -7285,16 +7304,16 @@ fn zirSwitchCapture( // Previous switch validation ensured this will succeed const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable; - const field_index = enum_ty.enumTagFieldIndex(item_val, target).?; + const field_index = enum_ty.enumTagFieldIndex(item_val, sema.mod).?; const field = union_obj.fields.values()[field_index]; - if (!field.ty.eql(first_field.ty, target)) { + if (!field.ty.eql(first_field.ty, sema.mod)) { const first_item_src = switch_src; // TODO better source location const item_src = switch_src; const msg = msg: { const msg = try sema.errMsg(block, switch_src, "capture group with incompatible types", .{}); errdefer msg.destroy(sema.gpa); - try sema.errNote(block, first_item_src, msg, "type '{}' here", .{first_field.ty.fmt(target)}); - try sema.errNote(block, item_src, msg, "type '{}' here", .{field.ty.fmt(target)}); + try sema.errNote(block, first_item_src, msg, "type '{}' here", .{first_field.ty.fmt(sema.mod)}); + try sema.errNote(block, item_src, msg, "type '{}' here", .{field.ty.fmt(sema.mod)}); break :msg msg; }; return sema.failWithOwnedErrorMsg(block, msg); @@ -7304,7 +7323,7 @@ fn zirSwitchCapture( if (is_ref) { assert(operand_is_ref); - const field_ty_ptr = try Type.ptr(sema.arena, target, .{ + const field_ty_ptr = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = first_field.ty, .@"addrspace" = .generic, .mutable = operand_ptr_ty.ptrIsMutable(), @@ -7388,7 +7407,6 @@ fn zirSwitchCond( else operand_ptr; const operand_ty = sema.typeOf(operand); - const target = sema.mod.getTarget(); switch (operand_ty.zigTypeTag()) { .Type, @@ -7436,7 +7454,7 @@ fn zirSwitchCond( .Vector, .Frame, .AnyFrame, - => return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(target)}), + => return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(sema.mod)}), } } @@ -7588,10 +7606,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError ); } try sema.mod.errNoteNonLazy( - operand_ty.declSrcLoc(), + operand_ty.declSrcLoc(sema.mod), msg, "enum '{}' declared here", - .{operand_ty.fmt(target)}, + .{operand_ty.fmt(sema.mod)}, ); break :msg msg; }; @@ -7705,10 +7723,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError if (maybe_msg) |msg| { try sema.mod.errNoteNonLazy( - operand_ty.declSrcLoc(), + operand_ty.declSrcLoc(sema.mod), msg, "error set '{}' declared here", - .{operand_ty.fmt(target)}, + .{operand_ty.fmt(sema.mod)}, ); return sema.failWithOwnedErrorMsg(block, msg); } @@ -7738,7 +7756,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError }, .Union => return sema.fail(block, src, "TODO validate switch .Union", .{}), .Int, .ComptimeInt => { - var range_set = RangeSet.init(gpa, target); + var range_set = RangeSet.init(gpa, sema.mod); defer range_set.deinit(); var extra_index: usize = special.end; @@ -7914,13 +7932,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError block, src, "else prong required when switching on type '{}'", - .{operand_ty.fmt(target)}, + .{operand_ty.fmt(sema.mod)}, ); } var seen_values = ValueSrcMap.initContext(gpa, .{ .ty = operand_ty, - .target = target, + .mod = sema.mod, }); defer seen_values.deinit(); @@ -7985,7 +8003,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError .ComptimeFloat, .Float, => return sema.fail(block, operand_src, "invalid switch operand type '{}'", .{ - operand_ty.fmt(target), + operand_ty.fmt(sema.mod), }), } @@ -8035,7 +8053,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const item = sema.resolveInst(item_ref); // Validation above ensured these will succeed. const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable; - if (operand_val.eql(item_val, operand_ty, target)) { + if (operand_val.eql(item_val, operand_ty, sema.mod)) { return sema.resolveBlockBody(block, src, &child_block, body, inst, merges); } } @@ -8057,7 +8075,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const item = sema.resolveInst(item_ref); // Validation above ensured these will succeed. const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable; - if (operand_val.eql(item_val, operand_ty, target)) { + if (operand_val.eql(item_val, operand_ty, sema.mod)) { return sema.resolveBlockBody(block, src, &child_block, body, inst, merges); } } @@ -8072,8 +8090,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError // Validation above ensured these will succeed. const first_tv = sema.resolveInstConst(&child_block, .unneeded, item_first) catch unreachable; const last_tv = sema.resolveInstConst(&child_block, .unneeded, item_last) catch unreachable; - if (Value.compare(operand_val, .gte, first_tv.val, operand_ty, target) and - Value.compare(operand_val, .lte, last_tv.val, operand_ty, target)) + if (Value.compare(operand_val, .gte, first_tv.val, operand_ty, sema.mod) and + Value.compare(operand_val, .lte, last_tv.val, operand_ty, sema.mod)) { return sema.resolveBlockBody(block, src, &child_block, body, inst, merges); } @@ -8385,7 +8403,7 @@ fn resolveSwitchItemVal( return TypedValue{ .ty = item_ty, .val = val }; } else |err| switch (err) { error.NeededSourceLocation => { - const src = switch_prong_src.resolve(sema.gpa, block.src_decl, switch_node_offset, range_expand); + const src = switch_prong_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_node_offset, range_expand); return TypedValue{ .ty = item_ty, .val = try sema.resolveConstValue(block, src, item), @@ -8434,19 +8452,18 @@ fn validateSwitchItemEnum( switch_prong_src: Module.SwitchProngSrc, ) CompileError!void { const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none); - const target = sema.mod.getTarget(); - const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val, target) orelse { + const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val, sema.mod) orelse { const msg = msg: { - const src = switch_prong_src.resolve(sema.gpa, block.src_decl, src_node_offset, .none); + const src = switch_prong_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), src_node_offset, .none); const msg = try sema.errMsg( block, src, "enum '{}' has no tag with value '{}'", - .{ item_tv.ty.fmt(target), item_tv.val.fmtValue(item_tv.ty, target) }, + .{ item_tv.ty.fmt(sema.mod), item_tv.val.fmtValue(item_tv.ty, sema.mod) }, ); errdefer msg.destroy(sema.gpa); try sema.mod.errNoteNonLazy( - item_tv.ty.declSrcLoc(), + item_tv.ty.declSrcLoc(sema.mod), msg, "enum declared here", .{}, @@ -8487,8 +8504,9 @@ fn validateSwitchDupe( ) CompileError!void { const prev_prong_src = maybe_prev_src orelse return; const gpa = sema.gpa; - const src = switch_prong_src.resolve(gpa, block.src_decl, src_node_offset, .none); - const prev_src = prev_prong_src.resolve(gpa, block.src_decl, src_node_offset, .none); + const block_src_decl = sema.mod.declPtr(block.src_decl); + const src = switch_prong_src.resolve(gpa, block_src_decl, src_node_offset, .none); + const prev_src = prev_prong_src.resolve(gpa, block_src_decl, src_node_offset, .none); const msg = msg: { const msg = try sema.errMsg( block, @@ -8525,7 +8543,8 @@ fn validateSwitchItemBool( false_count.* += 1; } if (true_count.* + false_count.* > 2) { - const src = switch_prong_src.resolve(sema.gpa, block.src_decl, src_node_offset, .none); + const block_src_decl = sema.mod.declPtr(block.src_decl); + const src = switch_prong_src.resolve(sema.gpa, block_src_decl, src_node_offset, .none); return sema.fail(block, src, "duplicate switch value", .{}); } } @@ -8558,13 +8577,12 @@ fn validateSwitchNoRange( const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset }; const range_src: LazySrcLoc = .{ .node_offset_switch_range = src_node_offset }; - const target = sema.mod.getTarget(); const msg = msg: { const msg = try sema.errMsg( block, operand_src, "ranges not allowed when switching on type '{}'", - .{operand_ty.fmt(target)}, + .{operand_ty.fmt(sema.mod)}, ); errdefer msg.destroy(sema.gpa); try sema.errNote( @@ -8587,7 +8605,6 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const unresolved_ty = try sema.resolveType(block, ty_src, extra.lhs); const field_name = try sema.resolveConstString(block, name_src, extra.rhs); const ty = try sema.resolveTypeFields(block, ty_src, unresolved_ty); - const target = sema.mod.getTarget(); const has_field = hf: { if (ty.isSlice()) { @@ -8610,7 +8627,7 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .Enum => ty.enumFields().contains(field_name), .Array => mem.eql(u8, field_name, "len"), else => return sema.fail(block, ty_src, "type '{}' does not support '@hasField'", .{ - ty.fmt(target), + ty.fmt(sema.mod), }), }; }; @@ -8633,7 +8650,8 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air try checkNamespaceType(sema, block, lhs_src, container_type); const namespace = container_type.getNamespace() orelse return Air.Inst.Ref.bool_false; - if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl| { + if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| { + const decl = sema.mod.declPtr(decl_index); if (decl.is_pub or decl.getFileScope() == block.getFileScope()) { return Air.Inst.Ref.bool_true; } @@ -8661,8 +8679,9 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. }, }; try mod.semaFile(result.file); - const file_root_decl = result.file.root_decl.?; - try mod.declareDeclDependency(sema.owner_decl, file_root_decl); + const file_root_decl_index = result.file.root_decl.unwrap().?; + const file_root_decl = mod.declPtr(file_root_decl_index); + try mod.declareDeclDependency(sema.owner_decl_index, file_root_decl_index); return sema.addConstant(file_root_decl.ty, file_root_decl.val); } @@ -8763,7 +8782,7 @@ fn zirShl( } const int_info = scalar_ty.intInfo(target); const truncated = try shifted.intTrunc(lhs_ty, sema.arena, int_info.signedness, int_info.bits, target); - if (truncated.compare(.eq, shifted, lhs_ty, target)) { + if (truncated.compare(.eq, shifted, lhs_ty, sema.mod)) { break :val shifted; } return sema.addConstUndef(lhs_ty); @@ -8927,7 +8946,7 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. if (scalar_type.zigTypeTag() != .Int) { return sema.fail(block, src, "unable to perform binary not operation on type '{}'", .{ - operand_type.fmt(target), + operand_type.fmt(sema.mod), }); } @@ -8939,7 +8958,7 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. var elem_val_buf: Value.ElemValueBuffer = undefined; const elems = try sema.arena.alloc(Value, vec_len); for (elems) |*elem, i| { - const elem_val = val.elemValueBuffer(i, &elem_val_buf); + const elem_val = val.elemValueBuffer(sema.mod, i, &elem_val_buf); elem.* = try elem_val.bitwiseNot(scalar_type, sema.arena, target); } return sema.addConstant( @@ -9047,14 +9066,13 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; - const target = sema.mod.getTarget(); const lhs_info = (try sema.getArrayCatInfo(block, lhs_src, lhs)) orelse - return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty.fmt(target)}); + return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty.fmt(sema.mod)}); const rhs_info = (try sema.getArrayCatInfo(block, rhs_src, rhs)) orelse - return sema.fail(block, rhs_src, "expected array, found '{}'", .{rhs_ty.fmt(target)}); - if (!lhs_info.elem_type.eql(rhs_info.elem_type, target)) { + return sema.fail(block, rhs_src, "expected array, found '{}'", .{rhs_ty.fmt(sema.mod)}); + if (!lhs_info.elem_type.eql(rhs_info.elem_type, sema.mod)) { return sema.fail(block, rhs_src, "expected array of type '{}', found '{}'", .{ - lhs_info.elem_type.fmt(target), rhs_ty.fmt(target), + lhs_info.elem_type.fmt(sema.mod), rhs_ty.fmt(sema.mod), }); } @@ -9062,7 +9080,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // will catch this if it is a problem. var res_sent: ?Value = null; if (rhs_info.sentinel != null and lhs_info.sentinel != null) { - if (rhs_info.sentinel.?.eql(lhs_info.sentinel.?, lhs_info.elem_type, target)) { + if (rhs_info.sentinel.?.eql(lhs_info.sentinel.?, lhs_info.elem_type, sema.mod)) { res_sent = lhs_info.sentinel.?; } } @@ -9084,14 +9102,14 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai { var i: usize = 0; while (i < lhs_len) : (i += 1) { - const val = try lhs_sub_val.elemValue(sema.arena, i); + const val = try lhs_sub_val.elemValue(sema.mod, sema.arena, i); buf[i] = try val.copy(anon_decl.arena()); } } { var i: usize = 0; while (i < rhs_len) : (i += 1) { - const val = try rhs_sub_val.elemValue(sema.arena, i); + const val = try rhs_sub_val.elemValue(sema.mod, sema.arena, i); buf[lhs_len + i] = try val.copy(anon_decl.arena()); } } @@ -9123,7 +9141,6 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, inst: Air.Inst.Ref) !?Type.ArrayInfo { const t = sema.typeOf(inst); - const target = sema.mod.getTarget(); return switch (t.zigTypeTag()) { .Array => t.arrayInfo(), .Pointer => blk: { @@ -9133,7 +9150,7 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, inst: Air.Inst.R return Type.ArrayInfo{ .elem_type = t.childType(), .sentinel = t.sentinel(), - .len = val.sliceLen(target), + .len = val.sliceLen(sema.mod), }; } if (ptrinfo.pointee_type.zigTypeTag() != .Array) return null; @@ -9229,10 +9246,9 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (lhs_ty.isTuple()) { return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor); } - const target = sema.mod.getTarget(); const mulinfo = (try sema.getArrayCatInfo(block, lhs_src, lhs)) orelse - return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty.fmt(target)}); + return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty.fmt(sema.mod)}); const final_len_u64 = std.math.mul(u64, mulinfo.len, factor) catch return sema.fail(block, rhs_src, "operation results in overflow", .{}); @@ -9264,7 +9280,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // Optimization for the common pattern of a single element repeated N times, such // as zero-filling a byte array. const val = if (lhs_len == 1) blk: { - const elem_val = try lhs_sub_val.elemValue(sema.arena, 0); + const elem_val = try lhs_sub_val.elemValue(sema.mod, sema.arena, 0); const copied_val = try elem_val.copy(anon_decl.arena()); break :blk try Value.Tag.repeated.create(anon_decl.arena(), copied_val); } else blk: { @@ -9273,7 +9289,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai while (i < factor) : (i += 1) { var j: usize = 0; while (j < lhs_len) : (j += 1) { - const val = try lhs_sub_val.elemValue(sema.arena, j); + const val = try lhs_sub_val.elemValue(sema.mod, sema.arena, j); buf[lhs_len * i + j] = try val.copy(anon_decl.arena()); } } @@ -9310,9 +9326,8 @@ fn zirNegate( const rhs_ty = sema.typeOf(rhs); const rhs_scalar_ty = rhs_ty.scalarType(); - const target = sema.mod.getTarget(); if (tag_override == .sub and rhs_scalar_ty.isUnsignedInt()) { - return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(target)}); + return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(sema.mod)}); } const lhs = if (rhs_ty.zigTypeTag() == .Vector) @@ -9364,12 +9379,13 @@ fn zirOverflowArithmetic( const ptr = sema.resolveInst(extra.ptr); const lhs_ty = sema.typeOf(lhs); - const target = sema.mod.getTarget(); + const mod = sema.mod; + const target = mod.getTarget(); // Note, the types of lhs/rhs (also for shifting)/ptr are already correct as ensured by astgen. const dest_ty = lhs_ty; if (dest_ty.zigTypeTag() != .Int) { - return sema.fail(block, src, "expected integer type, found '{}'", .{dest_ty.fmt(target)}); + return sema.fail(block, src, "expected integer type, found '{}'", .{dest_ty.fmt(mod)}); } const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs); @@ -9445,7 +9461,7 @@ fn zirOverflowArithmetic( if (!lhs_val.isUndef()) { if (lhs_val.compareWithZero(.eq)) { break :result .{ .overflowed = .no, .wrapped = lhs }; - } else if (lhs_val.compare(.eq, Value.one, dest_ty, target)) { + } else if (lhs_val.compare(.eq, Value.one, dest_ty, mod)) { break :result .{ .overflowed = .no, .wrapped = rhs }; } } @@ -9455,7 +9471,7 @@ fn zirOverflowArithmetic( if (!rhs_val.isUndef()) { if (rhs_val.compareWithZero(.eq)) { break :result .{ .overflowed = .no, .wrapped = rhs }; - } else if (rhs_val.compare(.eq, Value.one, dest_ty, target)) { + } else if (rhs_val.compare(.eq, Value.one, dest_ty, mod)) { break :result .{ .overflowed = .no, .wrapped = lhs }; } } @@ -9596,7 +9612,8 @@ fn analyzeArithmetic( }); } - const target = sema.mod.getTarget(); + const mod = sema.mod; + const target = mod.getTarget(); const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs); const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs); const rs: struct { src: LazySrcLoc, air_tag: Air.Inst.Tag } = rs: { @@ -9834,7 +9851,7 @@ fn analyzeArithmetic( if (lhs_val.isUndef()) { if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compare(.neq, Value.negative_one, resolved_type, target)) { + if (rhs_val.compare(.neq, Value.negative_one, resolved_type, mod)) { return sema.addConstUndef(resolved_type); } } @@ -9909,7 +9926,7 @@ fn analyzeArithmetic( if (lhs_val.isUndef()) { if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compare(.neq, Value.negative_one, resolved_type, target)) { + if (rhs_val.compare(.neq, Value.negative_one, resolved_type, mod)) { return sema.addConstUndef(resolved_type); } } @@ -9972,7 +9989,7 @@ fn analyzeArithmetic( if (lhs_val.isUndef()) { if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compare(.neq, Value.negative_one, resolved_type, target)) { + if (rhs_val.compare(.neq, Value.negative_one, resolved_type, mod)) { return sema.addConstUndef(resolved_type); } } @@ -10062,7 +10079,7 @@ fn analyzeArithmetic( if (lhs_val.compareWithZero(.eq)) { return sema.addConstant(resolved_type, Value.zero); } - if (lhs_val.compare(.eq, Value.one, resolved_type, target)) { + if (lhs_val.compare(.eq, Value.one, resolved_type, mod)) { return casted_rhs; } } @@ -10078,7 +10095,7 @@ fn analyzeArithmetic( if (rhs_val.compareWithZero(.eq)) { return sema.addConstant(resolved_type, Value.zero); } - if (rhs_val.compare(.eq, Value.one, resolved_type, target)) { + if (rhs_val.compare(.eq, Value.one, resolved_type, mod)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { @@ -10113,7 +10130,7 @@ fn analyzeArithmetic( if (lhs_val.compareWithZero(.eq)) { return sema.addConstant(resolved_type, Value.zero); } - if (lhs_val.compare(.eq, Value.one, resolved_type, target)) { + if (lhs_val.compare(.eq, Value.one, resolved_type, mod)) { return casted_rhs; } } @@ -10125,7 +10142,7 @@ fn analyzeArithmetic( if (rhs_val.compareWithZero(.eq)) { return sema.addConstant(resolved_type, Value.zero); } - if (rhs_val.compare(.eq, Value.one, resolved_type, target)) { + if (rhs_val.compare(.eq, Value.one, resolved_type, mod)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { @@ -10149,7 +10166,7 @@ fn analyzeArithmetic( if (lhs_val.compareWithZero(.eq)) { return sema.addConstant(resolved_type, Value.zero); } - if (lhs_val.compare(.eq, Value.one, resolved_type, target)) { + if (lhs_val.compare(.eq, Value.one, resolved_type, mod)) { return casted_rhs; } } @@ -10161,7 +10178,7 @@ fn analyzeArithmetic( if (rhs_val.compareWithZero(.eq)) { return sema.addConstant(resolved_type, Value.zero); } - if (rhs_val.compare(.eq, Value.one, resolved_type, target)) { + if (rhs_val.compare(.eq, Value.one, resolved_type, mod)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { @@ -10431,7 +10448,7 @@ fn analyzePtrArithmetic( if (air_tag == .ptr_sub) { return sema.fail(block, op_src, "TODO implement Sema comptime pointer subtraction", .{}); } - const new_ptr_val = try ptr_val.elemPtr(ptr_ty, sema.arena, offset_int, target); + const new_ptr_val = try ptr_val.elemPtr(ptr_ty, sema.arena, offset_int, sema.mod); return sema.addConstant(new_ptr_ty, new_ptr_val); } else break :rs offset_src; } else break :rs ptr_src; @@ -10605,7 +10622,6 @@ fn zirCmpEq( const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; const lhs = sema.resolveInst(extra.lhs); const rhs = sema.resolveInst(extra.rhs); - const target = sema.mod.getTarget(); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); @@ -10630,7 +10646,7 @@ fn zirCmpEq( if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) { const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty; - return sema.fail(block, src, "comparison of '{}' with null", .{non_null_type.fmt(target)}); + return sema.fail(block, src, "comparison of '{}' with null", .{non_null_type.fmt(sema.mod)}); } if (lhs_ty_tag == .Union and (rhs_ty_tag == .EnumLiteral or rhs_ty_tag == .Enum)) { @@ -10670,7 +10686,7 @@ fn zirCmpEq( if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) { const lhs_as_type = try sema.analyzeAsType(block, lhs_src, lhs); const rhs_as_type = try sema.analyzeAsType(block, rhs_src, rhs); - if (lhs_as_type.eql(rhs_as_type, target) == (op == .eq)) { + if (lhs_as_type.eql(rhs_as_type, sema.mod) == (op == .eq)) { return Air.Inst.Ref.bool_true; } else { return Air.Inst.Ref.bool_false; @@ -10747,10 +10763,9 @@ fn analyzeCmp( } const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]LazySrcLoc{ lhs_src, rhs_src } }); - const target = sema.mod.getTarget(); if (!resolved_type.isSelfComparable(is_equality_cmp)) { return sema.fail(block, src, "{s} operator not allowed for type '{}'", .{ - @tagName(op), resolved_type.fmt(target), + @tagName(op), resolved_type.fmt(sema.mod), }); } const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); @@ -10768,7 +10783,6 @@ fn cmpSelf( rhs_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { const resolved_type = sema.typeOf(casted_lhs); - const target = sema.mod.getTarget(); const runtime_src: LazySrcLoc = src: { if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { if (lhs_val.isUndef()) return sema.addConstUndef(Type.bool); @@ -10777,11 +10791,11 @@ fn cmpSelf( if (resolved_type.zigTypeTag() == .Vector) { const result_ty = try Type.vector(sema.arena, resolved_type.vectorLen(), Type.@"bool"); - const cmp_val = try lhs_val.compareVector(op, rhs_val, resolved_type, sema.arena, target); + const cmp_val = try lhs_val.compareVector(op, rhs_val, resolved_type, sema.arena, sema.mod); return sema.addConstant(result_ty, cmp_val); } - if (lhs_val.compare(op, rhs_val, resolved_type, target)) { + if (lhs_val.compare(op, rhs_val, resolved_type, sema.mod)) { return Air.Inst.Ref.bool_true; } else { return Air.Inst.Ref.bool_false; @@ -10849,7 +10863,7 @@ fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. .Null, .BoundFn, .Opaque, - => return sema.fail(block, src, "no size available for type '{}'", .{operand_ty.fmt(target)}), + => return sema.fail(block, src, "no size available for type '{}'", .{operand_ty.fmt(sema.mod)}), .Type, .EnumLiteral, @@ -10892,9 +10906,9 @@ fn zirThis( block: *Block, extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { - const this_decl = block.namespace.getDecl(); + const this_decl_index = block.namespace.getDeclIndex(); const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; - return sema.analyzeDeclVal(block, src, this_decl); + return sema.analyzeDeclVal(block, src, this_decl_index); } fn zirClosureCapture( @@ -10927,7 +10941,7 @@ fn zirClosureGet( ) CompileError!Air.Inst.Ref { // TODO CLOSURE: Test this with inline functions const inst_data = sema.code.instructions.items(.data)[inst].inst_node; - var scope: *CaptureScope = block.src_decl.src_scope.?; + var scope: *CaptureScope = sema.mod.declPtr(block.src_decl).src_scope.?; // Note: The target closure must be in this scope list. // If it's not here, the zir is invalid, or the list is broken. const tv = while (true) { @@ -10973,11 +10987,12 @@ fn zirBuiltinSrc( const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; const extra = sema.code.extraData(Zir.Inst.LineColumn, extended.operand).data; const func = sema.func orelse return sema.fail(block, src, "@src outside function", .{}); + const fn_owner_decl = sema.mod.declPtr(func.owner_decl); const func_name_val = blk: { var anon_decl = try block.startAnonDecl(src); defer anon_decl.deinit(); - const name = std.mem.span(func.owner_decl.name); + const name = std.mem.span(fn_owner_decl.name); const bytes = try anon_decl.arena().dupe(u8, name[0 .. name.len + 1]); const new_decl = try anon_decl.finish( try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len - 1), @@ -10990,7 +11005,7 @@ fn zirBuiltinSrc( const file_name_val = blk: { var anon_decl = try block.startAnonDecl(src); defer anon_decl.deinit(); - const name = try func.owner_decl.getFileScope().fullPathZ(anon_decl.arena()); + const name = try fn_owner_decl.getFileScope().fullPathZ(anon_decl.arena()); const new_decl = try anon_decl.finish( try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), name.len), try Value.Tag.bytes.create(anon_decl.arena(), name[0 .. name.len + 1]), @@ -11118,24 +11133,26 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } const args_val = v: { - const fn_info_decl = (try sema.namespaceLookup( + const fn_info_decl_index = (try sema.namespaceLookup( block, src, type_info_ty.getNamespace().?, "Fn", )).?; - try sema.mod.declareDeclDependency(sema.owner_decl, fn_info_decl); - try sema.ensureDeclAnalyzed(fn_info_decl); + try sema.mod.declareDeclDependency(sema.owner_decl_index, fn_info_decl_index); + try sema.ensureDeclAnalyzed(fn_info_decl_index); + const fn_info_decl = sema.mod.declPtr(fn_info_decl_index); var fn_ty_buffer: Value.ToTypeBuffer = undefined; const fn_ty = fn_info_decl.val.toType(&fn_ty_buffer); - const param_info_decl = (try sema.namespaceLookup( + const param_info_decl_index = (try sema.namespaceLookup( block, src, fn_ty.getNamespace().?, "Param", )).?; - try sema.mod.declareDeclDependency(sema.owner_decl, param_info_decl); - try sema.ensureDeclAnalyzed(param_info_decl); + try sema.mod.declareDeclDependency(sema.owner_decl_index, param_info_decl_index); + try sema.ensureDeclAnalyzed(param_info_decl_index); + const param_info_decl = sema.mod.declPtr(param_info_decl_index); var param_buffer: Value.ToTypeBuffer = undefined; const param_ty = param_info_decl.val.toType(¶m_buffer); const new_decl = try params_anon_decl.finish( @@ -11307,14 +11324,15 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // Get the Error type const error_field_ty = t: { - const set_field_ty_decl = (try sema.namespaceLookup( + const set_field_ty_decl_index = (try sema.namespaceLookup( block, src, type_info_ty.getNamespace().?, "Error", )).?; - try sema.mod.declareDeclDependency(sema.owner_decl, set_field_ty_decl); - try sema.ensureDeclAnalyzed(set_field_ty_decl); + try sema.mod.declareDeclDependency(sema.owner_decl_index, set_field_ty_decl_index); + try sema.ensureDeclAnalyzed(set_field_ty_decl_index); + const set_field_ty_decl = sema.mod.declPtr(set_field_ty_decl_index); var buffer: Value.ToTypeBuffer = undefined; break :t try set_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena()); }; @@ -11416,14 +11434,15 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai defer fields_anon_decl.deinit(); const enum_field_ty = t: { - const enum_field_ty_decl = (try sema.namespaceLookup( + const enum_field_ty_decl_index = (try sema.namespaceLookup( block, src, type_info_ty.getNamespace().?, "EnumField", )).?; - try sema.mod.declareDeclDependency(sema.owner_decl, enum_field_ty_decl); - try sema.ensureDeclAnalyzed(enum_field_ty_decl); + try sema.mod.declareDeclDependency(sema.owner_decl_index, enum_field_ty_decl_index); + try sema.ensureDeclAnalyzed(enum_field_ty_decl_index); + const enum_field_ty_decl = sema.mod.declPtr(enum_field_ty_decl_index); var buffer: Value.ToTypeBuffer = undefined; break :t try enum_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena()); }; @@ -11514,14 +11533,15 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai defer fields_anon_decl.deinit(); const union_field_ty = t: { - const union_field_ty_decl = (try sema.namespaceLookup( + const union_field_ty_decl_index = (try sema.namespaceLookup( block, src, type_info_ty.getNamespace().?, "UnionField", )).?; - try sema.mod.declareDeclDependency(sema.owner_decl, union_field_ty_decl); - try sema.ensureDeclAnalyzed(union_field_ty_decl); + try sema.mod.declareDeclDependency(sema.owner_decl_index, union_field_ty_decl_index); + try sema.ensureDeclAnalyzed(union_field_ty_decl_index); + const union_field_ty_decl = sema.mod.declPtr(union_field_ty_decl_index); var buffer: Value.ToTypeBuffer = undefined; break :t try union_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena()); }; @@ -11621,14 +11641,15 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai defer fields_anon_decl.deinit(); const struct_field_ty = t: { - const struct_field_ty_decl = (try sema.namespaceLookup( + const struct_field_ty_decl_index = (try sema.namespaceLookup( block, src, type_info_ty.getNamespace().?, "StructField", )).?; - try sema.mod.declareDeclDependency(sema.owner_decl, struct_field_ty_decl); - try sema.ensureDeclAnalyzed(struct_field_ty_decl); + try sema.mod.declareDeclDependency(sema.owner_decl_index, struct_field_ty_decl_index); + try sema.ensureDeclAnalyzed(struct_field_ty_decl_index); + const struct_field_ty_decl = sema.mod.declPtr(struct_field_ty_decl_index); var buffer: Value.ToTypeBuffer = undefined; break :t try struct_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena()); }; @@ -11811,14 +11832,15 @@ fn typeInfoDecls( defer decls_anon_decl.deinit(); const declaration_ty = t: { - const declaration_ty_decl = (try sema.namespaceLookup( + const declaration_ty_decl_index = (try sema.namespaceLookup( block, src, type_info_ty.getNamespace().?, "Declaration", )).?; - try sema.mod.declareDeclDependency(sema.owner_decl, declaration_ty_decl); - try sema.ensureDeclAnalyzed(declaration_ty_decl); + try sema.mod.declareDeclDependency(sema.owner_decl_index, declaration_ty_decl_index); + try sema.ensureDeclAnalyzed(declaration_ty_decl_index); + const declaration_ty_decl = sema.mod.declPtr(declaration_ty_decl_index); var buffer: Value.ToTypeBuffer = undefined; break :t try declaration_ty_decl.val.toType(&buffer).copy(decls_anon_decl.arena()); }; @@ -11827,7 +11849,8 @@ fn typeInfoDecls( const decls_len = if (opt_namespace) |ns| ns.decls.count() else 0; const decls_vals = try decls_anon_decl.arena().alloc(Value, decls_len); for (decls_vals) |*decls_val, i| { - const decl = opt_namespace.?.decls.keys()[i]; + const decl_index = opt_namespace.?.decls.keys()[i]; + const decl = sema.mod.declPtr(decl_index); const name_val = v: { var anon_decl = try block.startAnonDecl(src); defer anon_decl.deinit(); @@ -11947,12 +11970,11 @@ fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) Compi }, else => {}, } - const target = sema.mod.getTarget(); return sema.fail( block, src, "bit shifting operation expected integer type, found '{}'", - .{operand.fmt(target)}, + .{operand.fmt(sema.mod)}, ); } @@ -12426,8 +12448,7 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const inst_data = sema.code.instructions.items(.data)[inst].ptr_type_simple; const elem_type = try sema.resolveType(block, .unneeded, inst_data.elem_type); - const target = sema.mod.getTarget(); - const ty = try Type.ptr(sema.arena, target, .{ + const ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = elem_type, .@"addrspace" = .generic, .mutable = inst_data.is_mutable, @@ -12466,7 +12487,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air // Check if this happens to be the lazy alignment of our element type, in // which case we can make this 0 without resolving it. if (val.castTag(.lazy_align)) |payload| { - if (payload.data.eql(unresolved_elem_ty, target)) { + if (payload.data.eql(unresolved_elem_ty, sema.mod)) { break :blk 0; } } @@ -12505,7 +12526,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air try sema.resolveTypeLayout(block, elem_ty_src, elem_ty); break :t elem_ty; }; - const ty = try Type.ptr(sema.arena, target, .{ + const ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = elem_ty, .sentinel = sentinel, .@"align" = abi_align, @@ -12754,10 +12775,10 @@ fn finishStructInit( const gpa = sema.gpa; if (root_msg) |msg| { - const fqn = try struct_obj.getFullyQualifiedName(gpa); + const fqn = try struct_obj.getFullyQualifiedName(sema.mod); defer gpa.free(fqn); try sema.mod.errNoteNonLazy( - struct_obj.srcLoc(), + struct_obj.srcLoc(sema.mod), msg, "struct '{s}' declared here", .{fqn}, @@ -12782,7 +12803,7 @@ fn finishStructInit( if (is_ref) { const target = sema.mod.getTarget(); - const alloc_ty = try Type.ptr(sema.arena, target, .{ + const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = struct_ty, .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); @@ -12851,7 +12872,7 @@ fn zirStructInitAnon( if (is_ref) { const target = sema.mod.getTarget(); - const alloc_ty = try Type.ptr(sema.arena, target, .{ + const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = tuple_ty, .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); @@ -12862,7 +12883,7 @@ fn zirStructInitAnon( const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); extra_index = item.end; - const field_ptr_ty = try Type.ptr(sema.arena, target, .{ + const field_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ .mutable = true, .@"addrspace" = target_util.defaultAddressSpace(target, .local), .pointee_type = field_ty, @@ -12949,13 +12970,13 @@ fn zirArrayInit( if (is_ref) { const target = sema.mod.getTarget(); - const alloc_ty = try Type.ptr(sema.arena, target, .{ + const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = array_ty, .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); const alloc = try block.addTy(.alloc, alloc_ty); - const elem_ptr_ty = try Type.ptr(sema.arena, target, .{ + const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ .mutable = true, .@"addrspace" = target_util.defaultAddressSpace(target, .local), .pointee_type = elem_ty, @@ -13017,14 +13038,14 @@ fn zirArrayInitAnon( if (is_ref) { const target = sema.mod.getTarget(); - const alloc_ty = try Type.ptr(sema.arena, target, .{ + const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = tuple_ty, .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); const alloc = try block.addTy(.alloc, alloc_ty); for (operands) |operand, i_usize| { const i = @intCast(u32, i_usize); - const field_ptr_ty = try Type.ptr(sema.arena, target, .{ + const field_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ .mutable = true, .@"addrspace" = target_util.defaultAddressSpace(target, .local), .pointee_type = types[i], @@ -13096,7 +13117,6 @@ fn fieldType( ty_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { const resolved_ty = try sema.resolveTypeFields(block, ty_src, aggregate_ty); - const target = sema.mod.getTarget(); var cur_ty = resolved_ty; while (true) { switch (cur_ty.zigTypeTag()) { @@ -13127,7 +13147,7 @@ fn fieldType( else => {}, } return sema.fail(block, ty_src, "expected struct or union; found '{}'", .{ - resolved_ty.fmt(target), + resolved_ty.fmt(sema.mod), }); } } @@ -13216,10 +13236,10 @@ fn zirUnaryMath( const scalar_ty = operand_ty.scalarType(); switch (scalar_ty.zigTypeTag()) { .ComptimeFloat, .Float => {}, - else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{scalar_ty.fmt(target)}), + else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{scalar_ty.fmt(sema.mod)}), } }, - else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{operand_ty.fmt(target)}), + else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{operand_ty.fmt(sema.mod)}), } switch (operand_ty.zigTypeTag()) { @@ -13234,7 +13254,7 @@ fn zirUnaryMath( var elem_buf: Value.ElemValueBuffer = undefined; const elems = try sema.arena.alloc(Value, vec_len); for (elems) |*elem, i| { - const elem_val = val.elemValueBuffer(i, &elem_buf); + const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf); elem.* = try eval(elem_val, scalar_ty, sema.arena, target); } return sema.addConstant( @@ -13267,7 +13287,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const src = inst_data.src(); const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); - const target = sema.mod.getTarget(); + const mod = sema.mod; try sema.resolveTypeLayout(block, operand_src, operand_ty); const enum_ty = switch (operand_ty.zigTypeTag()) { @@ -13278,31 +13298,33 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air }, .Enum => operand_ty, .Union => operand_ty.unionTagType() orelse { - const decl = operand_ty.getOwnerDecl(); + const decl_index = operand_ty.getOwnerDecl(); + const decl = mod.declPtr(decl_index); const msg = msg: { const msg = try sema.errMsg(block, src, "union '{s}' is untagged", .{ decl.name, }); errdefer msg.destroy(sema.gpa); - try sema.mod.errNoteNonLazy(decl.srcLoc(), msg, "declared here", .{}); + try mod.errNoteNonLazy(decl.srcLoc(), msg, "declared here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(block, msg); }, else => return sema.fail(block, operand_src, "expected enum or union; found {}", .{ - operand_ty.fmt(target), + operand_ty.fmt(mod), }), }; - const enum_decl = enum_ty.getOwnerDecl(); + const enum_decl_index = enum_ty.getOwnerDecl(); const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src); if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| { - const field_index = enum_ty.enumTagFieldIndex(val, target) orelse { + const field_index = enum_ty.enumTagFieldIndex(val, mod) orelse { + const enum_decl = mod.declPtr(enum_decl_index); const msg = msg: { const msg = try sema.errMsg(block, src, "no field with value {} in enum '{s}'", .{ casted_operand, enum_decl.name, }); errdefer msg.destroy(sema.gpa); - try sema.mod.errNoteNonLazy(enum_decl.srcLoc(), msg, "declared here", .{}); + try mod.errNoteNonLazy(enum_decl.srcLoc(), msg, "declared here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(block, msg); @@ -13317,6 +13339,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air } fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const type_info_ty = try sema.resolveBuiltinTypeFields(block, src, "Type"); @@ -13326,8 +13349,8 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const val = try sema.resolveConstValue(block, operand_src, type_info); const union_val = val.cast(Value.Payload.Union).?.data; const tag_ty = type_info_ty.unionTagType().?; - const target = sema.mod.getTarget(); - const tag_index = tag_ty.enumTagFieldIndex(union_val.tag, target).?; + const target = mod.getTarget(); + const tag_index = tag_ty.enumTagFieldIndex(union_val.tag, mod).?; switch (@intToEnum(std.builtin.TypeId, tag_index)) { .Type => return Air.Inst.Ref.type_type, .Void => return Air.Inst.Ref.void_type, @@ -13406,14 +13429,14 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I return sema.fail(block, src, "sentinels are only allowed on slices and unknown-length pointers", .{}); } const sentinel_ptr_val = sentinel_val.castTag(.opt_payload).?.data; - const ptr_ty = try Type.ptr(sema.arena, target, .{ + const ptr_ty = try Type.ptr(sema.arena, mod, .{ .@"addrspace" = .generic, .pointee_type = child_ty, }); actual_sentinel = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?; } - const ty = try Type.ptr(sema.arena, target, .{ + const ty = try Type.ptr(sema.arena, mod, .{ .size = ptr_size, .mutable = !is_const_val.toBool(), .@"volatile" = is_volatile_val.toBool(), @@ -13439,14 +13462,14 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I var buffer: Value.ToTypeBuffer = undefined; const child_ty = try child_val.toType(&buffer).copy(sema.arena); const sentinel = if (sentinel_val.castTag(.opt_payload)) |p| blk: { - const ptr_ty = try Type.ptr(sema.arena, target, .{ + const ptr_ty = try Type.ptr(sema.arena, mod, .{ .@"addrspace" = .generic, .pointee_type = child_ty, }); break :blk (try sema.pointerDeref(block, src, p.data, ptr_ty)).?; } else null; - const ty = try Type.array(sema.arena, len, sentinel, child_ty, target); + const ty = try Type.array(sema.arena, len, sentinel, child_ty, sema.mod); return sema.addType(ty); }, .Optional => { @@ -13483,8 +13506,9 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const payload_val = union_val.val.optionalValue() orelse return sema.addType(Type.initTag(.anyerror)); const slice_val = payload_val.castTag(.slice).?.data; - const decl = slice_val.ptr.pointerDecl().?; - try sema.ensureDeclAnalyzed(decl); + const decl_index = slice_val.ptr.pointerDecl().?; + try sema.ensureDeclAnalyzed(decl_index); + const decl = mod.declPtr(decl_index); const array_val = decl.val.castTag(.aggregate).?.data; var names: Module.ErrorSet.NameMap = .{}; @@ -13494,9 +13518,9 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I // TODO use reflection instead of magic numbers here // error_set: type, const name_val = struct_val[0]; - const name_str = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, target); + const name_str = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, sema.mod); - const kv = try sema.mod.getErrorValue(name_str); + const kv = try mod.getErrorValue(name_str); names.putAssumeCapacityNoClobber(kv.key, {}); } @@ -13518,7 +13542,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const is_tuple_val = struct_val[3]; // Decls - if (decls_val.sliceLen(target) > 0) { + if (decls_val.sliceLen(mod) > 0) { return sema.fail(block, src, "reified structs must have no decls", .{}); } @@ -13548,11 +13572,10 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I } // Decls - if (decls_val.sliceLen(target) > 0) { + if (decls_val.sliceLen(mod) > 0) { return sema.fail(block, src, "reified enums must have no decls", .{}); } - const mod = sema.mod; const gpa = sema.gpa; var new_decl_arena = std.heap.ArenaAllocator.init(gpa); errdefer new_decl_arena.deinit(); @@ -13572,20 +13595,20 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I }; const enum_ty = Type.initPayload(&enum_ty_payload.base); const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); - const type_name = try sema.createTypeName(block, .anon, "enum"); - const new_decl = try mod.createAnonymousDeclNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = enum_val, - }, type_name); + }, .anon, "enum"); + const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - errdefer mod.abortAnonDecl(new_decl); + errdefer mod.abortAnonDecl(new_decl_index); // Enum tag type var buffer: Value.ToTypeBuffer = undefined; const int_tag_ty = try tag_type_val.toType(&buffer).copy(new_decl_arena_allocator); enum_obj.* = .{ - .owner_decl = new_decl, + .owner_decl = new_decl_index, .tag_ty = int_tag_ty, .tag_ty_inferred = false, .fields = .{}, @@ -13599,17 +13622,17 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I }; // Fields - const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target)); + const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod)); if (fields_len > 0) { try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{ .ty = enum_obj.tag_ty, - .target = target, + .mod = mod, }); var i: usize = 0; while (i < fields_len) : (i += 1) { - const elem_val = try fields_val.elemValue(sema.arena, i); + const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i); const field_struct_val = elem_val.castTag(.aggregate).?.data; // TODO use reflection instead of magic numbers here // name: []const u8 @@ -13620,7 +13643,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const field_name = try name_val.toAllocatedBytes( Type.initTag(.const_slice_u8), new_decl_arena_allocator, - target, + sema.mod, ); const gop = enum_obj.fields.getOrPutAssumeCapacity(field_name); @@ -13632,13 +13655,13 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const copied_tag_val = try value_val.copy(new_decl_arena_allocator); enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{ .ty = enum_obj.tag_ty, - .target = target, + .mod = mod, }); } } try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl); + return sema.analyzeDeclVal(block, src, new_decl_index); }, .Opaque => { const struct_val = union_val.val.castTag(.aggregate).?.data; @@ -13646,11 +13669,10 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const decls_val = struct_val[0]; // Decls - if (decls_val.sliceLen(target) > 0) { + if (decls_val.sliceLen(mod) > 0) { return sema.fail(block, src, "reified opaque must have no decls", .{}); } - const mod = sema.mod; var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); errdefer new_decl_arena.deinit(); const new_decl_arena_allocator = new_decl_arena.allocator(); @@ -13663,16 +13685,16 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I }; const opaque_ty = Type.initPayload(&opaque_ty_payload.base); const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty); - const type_name = try sema.createTypeName(block, .anon, "opaque"); - const new_decl = try mod.createAnonymousDeclNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = opaque_val, - }, type_name); + }, .anon, "opaque"); + const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - errdefer mod.abortAnonDecl(new_decl); + errdefer mod.abortAnonDecl(new_decl_index); opaque_obj.* = .{ - .owner_decl = new_decl, + .owner_decl = new_decl_index, .node_offset = src.node_offset, .namespace = .{ .parent = block.namespace, @@ -13682,7 +13704,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I }; try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl); + return sema.analyzeDeclVal(block, src, new_decl_index); }, .Union => { // TODO use reflection instead of magic numbers here @@ -13697,7 +13719,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const decls_val = struct_val[3]; // Decls - if (decls_val.sliceLen(target) > 0) { + if (decls_val.sliceLen(mod) > 0) { return sema.fail(block, src, "reified unions must have no decls", .{}); } @@ -13714,15 +13736,15 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I }; const union_ty = Type.initPayload(&union_payload.base); const new_union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty); - const type_name = try sema.createTypeName(block, .anon, "union"); - const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = new_union_val, - }, type_name); + }, .anon, "union"); + const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - errdefer sema.mod.abortAnonDecl(new_decl); + errdefer mod.abortAnonDecl(new_decl_index); union_obj.* = .{ - .owner_decl = new_decl, + .owner_decl = new_decl_index, .tag_ty = Type.initTag(.@"null"), .fields = .{}, .node_offset = src.node_offset, @@ -13737,7 +13759,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I }; // Tag type - const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target)); + const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod)); union_obj.tag_ty = if (tag_type_val.optionalValue()) |payload_val| blk: { var buffer: Value.ToTypeBuffer = undefined; break :blk try payload_val.toType(&buffer).copy(new_decl_arena_allocator); @@ -13749,7 +13771,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I var i: usize = 0; while (i < fields_len) : (i += 1) { - const elem_val = try fields_val.elemValue(sema.arena, i); + const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i); const field_struct_val = elem_val.castTag(.aggregate).?.data; // TODO use reflection instead of magic numbers here // name: []const u8 @@ -13762,7 +13784,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const field_name = try name_val.toAllocatedBytes( Type.initTag(.const_slice_u8), new_decl_arena_allocator, - target, + sema.mod, ); const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); @@ -13780,7 +13802,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I } try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl); + return sema.analyzeDeclVal(block, src, new_decl_index); }, .Fn => return sema.fail(block, src, "TODO: Sema.zirReify for Fn", .{}), .BoundFn => @panic("TODO delete BoundFn from the language"), @@ -13794,9 +13816,7 @@ fn reifyTuple( src: LazySrcLoc, fields_val: Value, ) CompileError!Air.Inst.Ref { - const target = sema.mod.getTarget(); - - const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target)); + const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(sema.mod)); if (fields_len == 0) return sema.addType(Type.initTag(.empty_struct_literal)); const types = try sema.arena.alloc(Type, fields_len); @@ -13808,7 +13828,7 @@ fn reifyTuple( var i: usize = 0; while (i < fields_len) : (i += 1) { - const elem_val = try fields_val.elemValue(sema.arena, i); + const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i); const field_struct_val = elem_val.castTag(.aggregate).?.data; // TODO use reflection instead of magic numbers here // name: []const u8 @@ -13821,7 +13841,7 @@ fn reifyTuple( const field_name = try name_val.toAllocatedBytes( Type.initTag(.const_slice_u8), sema.arena, - target, + sema.mod, ); const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch |err| { @@ -13850,7 +13870,7 @@ fn reifyTuple( const default_val = if (default_value_val.optionalValue()) |opt_val| blk: { const payload_val = if (opt_val.pointerDecl()) |opt_decl| - opt_decl.val + sema.mod.declPtr(opt_decl).val else opt_val; break :blk try payload_val.copy(sema.arena); @@ -13883,15 +13903,16 @@ fn reifyStruct( const struct_obj = try new_decl_arena_allocator.create(Module.Struct); const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj); const new_struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty); - const type_name = try sema.createTypeName(block, .anon, "struct"); - const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ + const mod = sema.mod; + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = new_struct_val, - }, type_name); + }, .anon, "struct"); + const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - errdefer sema.mod.abortAnonDecl(new_decl); + errdefer mod.abortAnonDecl(new_decl_index); struct_obj.* = .{ - .owner_decl = new_decl, + .owner_decl = new_decl_index, .fields = .{}, .node_offset = src.node_offset, .zir_index = inst, @@ -13905,14 +13926,14 @@ fn reifyStruct( }, }; - const target = sema.mod.getTarget(); + const target = mod.getTarget(); // Fields - const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target)); + const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod)); try struct_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); var i: usize = 0; while (i < fields_len) : (i += 1) { - const elem_val = try fields_val.elemValue(sema.arena, i); + const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i); const field_struct_val = elem_val.castTag(.aggregate).?.data; // TODO use reflection instead of magic numbers here // name: []const u8 @@ -13929,7 +13950,7 @@ fn reifyStruct( const field_name = try name_val.toAllocatedBytes( Type.initTag(.const_slice_u8), new_decl_arena_allocator, - target, + mod, ); const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name); @@ -13940,7 +13961,7 @@ fn reifyStruct( const default_val = if (default_value_val.optionalValue()) |opt_val| blk: { const payload_val = if (opt_val.pointerDecl()) |opt_decl| - opt_decl.val + mod.declPtr(opt_decl).val else opt_val; break :blk try payload_val.copy(new_decl_arena_allocator); @@ -13957,7 +13978,7 @@ fn reifyStruct( } try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl); + return sema.analyzeDeclVal(block, src, new_decl_index); } fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -13968,8 +13989,7 @@ fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded); defer anon_decl.deinit(); - const target = sema.mod.getTarget(); - const bytes = try ty.nameAllocArena(anon_decl.arena(), target); + const bytes = try ty.nameAllocArena(anon_decl.arena(), sema.mod); const new_decl = try anon_decl.finish( try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), @@ -14010,7 +14030,7 @@ fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! error.FloatCannotFit => { return sema.fail(block, operand_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), - dest_ty.fmt(target), + dest_ty.fmt(sema.mod), }); }, else => |e| return e, @@ -14064,9 +14084,9 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| { const addr = val.toUnsignedInt(target); if (!type_res.isAllowzeroPtr() and addr == 0) - return sema.fail(block, operand_src, "pointer type '{}' does not allow address zero", .{type_res.fmt(target)}); + return sema.fail(block, operand_src, "pointer type '{}' does not allow address zero", .{type_res.fmt(sema.mod)}); if (addr != 0 and addr % ptr_align != 0) - return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{type_res.fmt(target)}); + return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{type_res.fmt(sema.mod)}); const val_payload = try sema.arena.create(Value.Payload.U64); val_payload.* = .{ @@ -14110,7 +14130,6 @@ fn zirErrSetCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); const operand = sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); - const target = sema.mod.getTarget(); try sema.checkErrorSetType(block, dest_ty_src, dest_ty); try sema.checkErrorSetType(block, operand_src, operand_ty); @@ -14124,7 +14143,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! block, src, "error.{s} not a member of error set '{}'", - .{ error_name, dest_ty.fmt(target) }, + .{ error_name, dest_ty.fmt(sema.mod) }, ); } } @@ -14178,11 +14197,11 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air var buf: Type.Payload.ElemType = undefined; var dest_ptr_info = dest_ty.optionalChild(&buf).ptrInfo().data; dest_ptr_info.@"align" = operand_align; - break :blk try Type.optional(sema.arena, try Type.ptr(sema.arena, target, dest_ptr_info)); + break :blk try Type.optional(sema.arena, try Type.ptr(sema.arena, sema.mod, dest_ptr_info)); } else { var dest_ptr_info = dest_ty.ptrInfo().data; dest_ptr_info.@"align" = operand_align; - break :blk try Type.ptr(sema.arena, target, dest_ptr_info); + break :blk try Type.ptr(sema.arena, sema.mod, dest_ptr_info); } }; @@ -14235,7 +14254,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (operand_info.signedness != dest_info.signedness) { return sema.fail(block, operand_src, "expected {s} integer type, found '{}'", .{ - @tagName(dest_info.signedness), operand_ty.fmt(target), + @tagName(dest_info.signedness), operand_ty.fmt(sema.mod), }); } if (operand_info.bits < dest_info.bits) { @@ -14244,7 +14263,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, "destination type '{}' has more bits than source type '{}'", - .{ dest_ty.fmt(target), operand_ty.fmt(target) }, + .{ dest_ty.fmt(sema.mod), operand_ty.fmt(sema.mod) }, ); errdefer msg.destroy(sema.gpa); try sema.errNote(block, dest_ty_src, msg, "destination type has {d} bits", .{ @@ -14270,7 +14289,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai var elem_buf: Value.ElemValueBuffer = undefined; const elems = try sema.arena.alloc(Value, operand_ty.vectorLen()); for (elems) |*elem, i| { - const elem_val = val.elemValueBuffer(i, &elem_buf); + const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf); elem.* = try elem_val.intTrunc(operand_scalar_ty, sema.arena, dest_info.signedness, dest_info.bits, target); } return sema.addConstant( @@ -14302,8 +14321,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A // TODO insert safety check that the alignment is correct const ptr_info = ptr_ty.ptrInfo().data; - const target = sema.mod.getTarget(); - const dest_ty = try Type.ptr(sema.arena, target, .{ + const dest_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = ptr_info.pointee_type, .@"align" = dest_align, .@"addrspace" = ptr_info.@"addrspace", @@ -14346,7 +14364,7 @@ fn zirBitCount( const elems = try sema.arena.alloc(Value, vec_len); const scalar_ty = operand_ty.scalarType(); for (elems) |*elem, i| { - const elem_val = val.elemValueBuffer(i, &elem_buf); + const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf); const count = comptimeOp(elem_val, scalar_ty, target); elem.* = try Value.Tag.int_u64.create(sema.arena, count); } @@ -14386,7 +14404,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, ty_src, "@byteSwap requires the number of bits to be evenly divisible by 8, but {} has {} bits", - .{ scalar_ty.fmt(target), bits }, + .{ scalar_ty.fmt(sema.mod), bits }, ); } @@ -14414,7 +14432,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai var elem_buf: Value.ElemValueBuffer = undefined; const elems = try sema.arena.alloc(Value, vec_len); for (elems) |*elem, i| { - const elem_val = val.elemValueBuffer(i, &elem_buf); + const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf); elem.* = try elem_val.byteSwap(operand_ty, target, sema.arena); } return sema.addConstant( @@ -14462,7 +14480,7 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! var elem_buf: Value.ElemValueBuffer = undefined; const elems = try sema.arena.alloc(Value, vec_len); for (elems) |*elem, i| { - const elem_val = val.elemValueBuffer(i, &elem_buf); + const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf); elem.* = try elem_val.bitReverse(operand_ty, target, sema.arena); } return sema.addConstant( @@ -14506,7 +14524,7 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 block, lhs_src, "expected struct type, found '{}'", - .{ty.fmt(target)}, + .{ty.fmt(sema.mod)}, ); } @@ -14516,7 +14534,7 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 block, rhs_src, "struct '{}' has no field '{s}'", - .{ ty.fmt(target), field_name }, + .{ ty.fmt(sema.mod), field_name }, ); }; @@ -14542,20 +14560,18 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 } fn checkNamespaceType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { - const target = sema.mod.getTarget(); switch (ty.zigTypeTag()) { .Struct, .Enum, .Union, .Opaque => return, - else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{}'", .{ty.fmt(target)}), + else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{}'", .{ty.fmt(sema.mod)}), } } /// Returns `true` if the type was a comptime_int. fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { - const target = sema.mod.getTarget(); switch (try ty.zigTypeTagOrPoison()) { .ComptimeInt => return true, .Int => return false, - else => return sema.fail(block, src, "expected integer type, found '{}'", .{ty.fmt(target)}), + else => return sema.fail(block, src, "expected integer type, found '{}'", .{ty.fmt(sema.mod)}), } } @@ -14565,7 +14581,6 @@ fn checkPtrOperand( ty_src: LazySrcLoc, ty: Type, ) CompileError!void { - const target = sema.mod.getTarget(); switch (ty.zigTypeTag()) { .Pointer => return, .Fn => { @@ -14574,7 +14589,7 @@ fn checkPtrOperand( block, ty_src, "expected pointer, found {}", - .{ty.fmt(target)}, + .{ty.fmt(sema.mod)}, ); errdefer msg.destroy(sema.gpa); @@ -14587,7 +14602,7 @@ fn checkPtrOperand( .Optional => if (ty.isPtrLikeOptional()) return, else => {}, } - return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(target)}); + return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(sema.mod)}); } fn checkPtrType( @@ -14596,7 +14611,6 @@ fn checkPtrType( ty_src: LazySrcLoc, ty: Type, ) CompileError!void { - const target = sema.mod.getTarget(); switch (ty.zigTypeTag()) { .Pointer => return, .Fn => { @@ -14605,7 +14619,7 @@ fn checkPtrType( block, ty_src, "expected pointer type, found '{}'", - .{ty.fmt(target)}, + .{ty.fmt(sema.mod)}, ); errdefer msg.destroy(sema.gpa); @@ -14618,7 +14632,7 @@ fn checkPtrType( .Optional => if (ty.isPtrLikeOptional()) return, else => {}, } - return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(target)}); + return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(sema.mod)}); } fn checkVectorElemType( @@ -14631,8 +14645,7 @@ fn checkVectorElemType( .Int, .Float, .Bool => return, else => if (ty.isPtrAtRuntime()) return, } - const target = sema.mod.getTarget(); - return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty.fmt(target)}); + return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty.fmt(sema.mod)}); } fn checkFloatType( @@ -14641,10 +14654,9 @@ fn checkFloatType( ty_src: LazySrcLoc, ty: Type, ) CompileError!void { - const target = sema.mod.getTarget(); switch (ty.zigTypeTag()) { .ComptimeInt, .ComptimeFloat, .Float => {}, - else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty.fmt(target)}), + else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty.fmt(sema.mod)}), } } @@ -14654,14 +14666,13 @@ fn checkNumericType( ty_src: LazySrcLoc, ty: Type, ) CompileError!void { - const target = sema.mod.getTarget(); switch (ty.zigTypeTag()) { .ComptimeFloat, .Float, .ComptimeInt, .Int => {}, .Vector => switch (ty.childType().zigTypeTag()) { .ComptimeFloat, .Float, .ComptimeInt, .Int => {}, else => |t| return sema.fail(block, ty_src, "expected number, found '{}'", .{t}), }, - else => return sema.fail(block, ty_src, "expected number, found '{}'", .{ty.fmt(target)}), + else => return sema.fail(block, ty_src, "expected number, found '{}'", .{ty.fmt(sema.mod)}), } } @@ -14697,7 +14708,7 @@ fn checkAtomicOperandType( block, ty_src, "expected bool, integer, float, enum, or pointer type; found {}", - .{ty.fmt(target)}, + .{ty.fmt(sema.mod)}, ); }, }; @@ -14761,7 +14772,6 @@ fn checkIntOrVector( operand_src: LazySrcLoc, ) CompileError!Type { const operand_ty = sema.typeOf(operand); - const target = sema.mod.getTarget(); switch (try operand_ty.zigTypeTagOrPoison()) { .Int => return operand_ty, .Vector => { @@ -14769,12 +14779,12 @@ fn checkIntOrVector( switch (try elem_ty.zigTypeTagOrPoison()) { .Int => return elem_ty, else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{ - elem_ty.fmt(target), + elem_ty.fmt(sema.mod), }), } }, else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{ - operand_ty.fmt(target), + operand_ty.fmt(sema.mod), }), } } @@ -14786,7 +14796,6 @@ fn checkIntOrVectorAllowComptime( operand_src: LazySrcLoc, ) CompileError!Type { const operand_ty = sema.typeOf(operand); - const target = sema.mod.getTarget(); switch (try operand_ty.zigTypeTagOrPoison()) { .Int, .ComptimeInt => return operand_ty, .Vector => { @@ -14794,21 +14803,20 @@ fn checkIntOrVectorAllowComptime( switch (try elem_ty.zigTypeTagOrPoison()) { .Int, .ComptimeInt => return elem_ty, else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{ - elem_ty.fmt(target), + elem_ty.fmt(sema.mod), }), } }, else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{ - operand_ty.fmt(target), + operand_ty.fmt(sema.mod), }), } } fn checkErrorSetType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { - const target = sema.mod.getTarget(); switch (ty.zigTypeTag()) { .ErrorSet => return, - else => return sema.fail(block, src, "expected error set type, found '{}'", .{ty.fmt(target)}), + else => return sema.fail(block, src, "expected error set type, found '{}'", .{ty.fmt(sema.mod)}), } } @@ -14892,10 +14900,9 @@ fn checkVectorizableBinaryOperands( return sema.failWithOwnedErrorMsg(block, msg); } } else { - const target = sema.mod.getTarget(); const msg = msg: { const msg = try sema.errMsg(block, src, "mixed scalar and vector operands: {} and {}", .{ - lhs_ty.fmt(target), rhs_ty.fmt(target), + lhs_ty.fmt(sema.mod), rhs_ty.fmt(sema.mod), }); errdefer msg.destroy(sema.gpa); if (lhs_is_vector) { @@ -14934,9 +14941,8 @@ fn resolveExportOptions( return sema.fail(block, src, "TODO: implement exporting with linksection", .{}); } const name_ty = Type.initTag(.const_slice_u8); - const target = sema.mod.getTarget(); return std.builtin.ExportOptions{ - .name = try name_val.toAllocatedBytes(name_ty, sema.arena, target), + .name = try name_val.toAllocatedBytes(name_ty, sema.arena, sema.mod), .linkage = linkage_val.toEnum(std.builtin.GlobalLinkage), .section = null, // TODO }; @@ -14995,13 +15001,12 @@ fn zirCmpxchg( const ptr_ty = sema.typeOf(ptr); const elem_ty = ptr_ty.elemType(); try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty); - const target = sema.mod.getTarget(); if (elem_ty.zigTypeTag() == .Float) { return sema.fail( block, elem_ty_src, "expected bool, integer, enum, or pointer type; found '{}'", - .{elem_ty.fmt(target)}, + .{elem_ty.fmt(sema.mod)}, ); } const expected_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.expected_value), expected_src); @@ -15038,7 +15043,7 @@ fn zirCmpxchg( return sema.addConstUndef(result_ty); } const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; - const result_val = if (stored_val.eql(expected_val, elem_ty, target)) blk: { + const result_val = if (stored_val.eql(expected_val, elem_ty, sema.mod)) blk: { try sema.storePtr(block, src, ptr, new_value); break :blk Value.@"null"; } else try Value.Tag.opt_payload.create(sema.arena, stored_val); @@ -15103,7 +15108,7 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const target = sema.mod.getTarget(); if (operand_ty.zigTypeTag() != .Vector) { - return sema.fail(block, operand_src, "expected vector, found {}", .{operand_ty.fmt(target)}); + return sema.fail(block, operand_src, "expected vector, found {}", .{operand_ty.fmt(sema.mod)}); } const scalar_ty = operand_ty.childType(); @@ -15113,13 +15118,13 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. .And, .Or, .Xor => switch (scalar_ty.zigTypeTag()) { .Int, .Bool => {}, else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found {}", .{ - @tagName(operation), operand_ty.fmt(target), + @tagName(operation), operand_ty.fmt(sema.mod), }), }, .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag()) { .Int, .Float => {}, else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found {}", .{ - @tagName(operation), operand_ty.fmt(target), + @tagName(operation), operand_ty.fmt(sema.mod), }), }, } @@ -15134,11 +15139,11 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| { if (operand_val.isUndef()) return sema.addConstUndef(scalar_ty); - var accum: Value = try operand_val.elemValue(sema.arena, 0); + var accum: Value = try operand_val.elemValue(sema.mod, sema.arena, 0); var elem_buf: Value.ElemValueBuffer = undefined; var i: u32 = 1; while (i < vec_len) : (i += 1) { - const elem_val = operand_val.elemValueBuffer(i, &elem_buf); + const elem_val = operand_val.elemValueBuffer(sema.mod, i, &elem_buf); switch (operation) { .And => accum = try accum.bitwiseAnd(elem_val, scalar_ty, sema.arena, target), .Or => accum = try accum.bitwiseOr(elem_val, scalar_ty, sema.arena, target), @@ -15174,11 +15179,10 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air var b = sema.resolveInst(extra.b); var mask = sema.resolveInst(extra.mask); var mask_ty = sema.typeOf(mask); - const target = sema.mod.getTarget(); const mask_len = switch (sema.typeOf(mask).zigTypeTag()) { .Array, .Vector => sema.typeOf(mask).arrayLen(), - else => return sema.fail(block, mask_src, "expected vector or array, found {}", .{sema.typeOf(mask).fmt(target)}), + else => return sema.fail(block, mask_src, "expected vector or array, found {}", .{sema.typeOf(mask).fmt(sema.mod)}), }; mask_ty = try Type.Tag.vector.create(sema.arena, .{ .len = mask_len, @@ -15210,21 +15214,20 @@ fn analyzeShuffle( .elem_type = elem_ty, }); - const target = sema.mod.getTarget(); var maybe_a_len = switch (sema.typeOf(a).zigTypeTag()) { .Array, .Vector => sema.typeOf(a).arrayLen(), .Undefined => null, else => return sema.fail(block, a_src, "expected vector or array with element type {}, found {}", .{ - elem_ty.fmt(target), - sema.typeOf(a).fmt(target), + elem_ty.fmt(sema.mod), + sema.typeOf(a).fmt(sema.mod), }), }; var maybe_b_len = switch (sema.typeOf(b).zigTypeTag()) { .Array, .Vector => sema.typeOf(b).arrayLen(), .Undefined => null, else => return sema.fail(block, b_src, "expected vector or array with element type {}, found {}", .{ - elem_ty.fmt(target), - sema.typeOf(b).fmt(target), + elem_ty.fmt(sema.mod), + sema.typeOf(b).fmt(sema.mod), }), }; if (maybe_a_len == null and maybe_b_len == null) { @@ -15253,7 +15256,7 @@ fn analyzeShuffle( var i: usize = 0; while (i < mask_len) : (i += 1) { var buf: Value.ElemValueBuffer = undefined; - const elem = mask.elemValueBuffer(i, &buf); + const elem = mask.elemValueBuffer(sema.mod, i, &buf); if (elem.isUndef()) continue; const int = elem.toSignedInt(); var unsigned: u32 = undefined; @@ -15272,7 +15275,7 @@ fn analyzeShuffle( try sema.errNote(block, operand_info[chosen][1], msg, "selected index {d} out of bounds of {}", .{ unsigned, - operand_info[chosen][2].fmt(target), + operand_info[chosen][2].fmt(sema.mod), }); if (chosen == 1) { @@ -15292,7 +15295,7 @@ fn analyzeShuffle( i = 0; while (i < mask_len) : (i += 1) { var buf: Value.ElemValueBuffer = undefined; - const mask_elem_val = mask.elemValueBuffer(i, &buf); + const mask_elem_val = mask.elemValueBuffer(sema.mod, i, &buf); if (mask_elem_val.isUndef()) { values[i] = Value.undef; continue; @@ -15300,9 +15303,9 @@ fn analyzeShuffle( const int = mask_elem_val.toSignedInt(); const unsigned = if (int >= 0) @intCast(u32, int) else @intCast(u32, ~int); if (int >= 0) { - values[i] = try a_val.elemValue(sema.arena, unsigned); + values[i] = try a_val.elemValue(sema.mod, sema.arena, unsigned); } else { - values[i] = try b_val.elemValue(sema.arena, unsigned); + values[i] = try b_val.elemValue(sema.mod, sema.arena, unsigned); } } const res_val = try Value.Tag.aggregate.create(sema.arena, values); @@ -15358,7 +15361,6 @@ fn analyzeShuffle( fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Select, inst_data.payload_index).data; - const target = sema.mod.getTarget(); const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const pred_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; @@ -15372,7 +15374,7 @@ fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const vec_len_u64 = switch (try pred_ty.zigTypeTagOrPoison()) { .Vector, .Array => pred_ty.arrayLen(), - else => return sema.fail(block, pred_src, "expected vector or array, found '{}'", .{pred_ty.fmt(target)}), + else => return sema.fail(block, pred_src, "expected vector or array, found '{}'", .{pred_ty.fmt(sema.mod)}), }; const vec_len = try sema.usizeCast(block, pred_src, vec_len_u64); @@ -15399,12 +15401,12 @@ fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. var buf: Value.ElemValueBuffer = undefined; const elems = try sema.gpa.alloc(Value, vec_len); for (elems) |*elem, i| { - const pred_elem_val = pred_val.elemValueBuffer(i, &buf); + const pred_elem_val = pred_val.elemValueBuffer(sema.mod, i, &buf); const should_choose_a = pred_elem_val.toBool(); if (should_choose_a) { - elem.* = a_val.elemValueBuffer(i, &buf); + elem.* = a_val.elemValueBuffer(sema.mod, i, &buf); } else { - elem.* = b_val.elemValueBuffer(i, &buf); + elem.* = b_val.elemValueBuffer(sema.mod, i, &buf); } } @@ -15630,7 +15632,7 @@ fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. switch (ty.zigTypeTag()) { .ComptimeFloat, .Float, .Vector => {}, - else => return sema.fail(block, src, "expected vector of floats or float type, found '{}'", .{ty.fmt(target)}), + else => return sema.fail(block, src, "expected vector of floats or float type, found '{}'", .{ty.fmt(sema.mod)}), } const runtime_src = if (maybe_mulend1) |mulend1_val| rs: { @@ -15704,10 +15706,9 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError break :modifier modifier_val.toEnum(std.builtin.CallOptions.Modifier); }; - const target = sema.mod.getTarget(); const args_ty = sema.typeOf(args); if (!args_ty.isTuple() and args_ty.tag() != .empty_struct_literal) { - return sema.fail(block, args_src, "expected a tuple, found {}", .{args_ty.fmt(target)}); + return sema.fail(block, args_src, "expected a tuple, found {}", .{args_ty.fmt(sema.mod)}); } var resolved_args: []Air.Inst.Ref = undefined; @@ -15744,10 +15745,9 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const field_name = try sema.resolveConstString(block, name_src, extra.field_name); const field_ptr = sema.resolveInst(extra.field_ptr); const field_ptr_ty = sema.typeOf(field_ptr); - const target = sema.mod.getTarget(); if (struct_ty.zigTypeTag() != .Struct) { - return sema.fail(block, ty_src, "expected struct type, found '{}'", .{struct_ty.fmt(target)}); + return sema.fail(block, ty_src, "expected struct type, found '{}'", .{struct_ty.fmt(sema.mod)}); } try sema.resolveTypeLayout(block, ty_src, struct_ty); @@ -15756,7 +15756,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr return sema.failWithBadStructFieldAccess(block, struct_obj, name_src, field_name); if (field_ptr_ty.zigTypeTag() != .Pointer) { - return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{field_ptr_ty.fmt(target)}); + return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{field_ptr_ty.fmt(sema.mod)}); } const field = struct_obj.fields.values()[field_index]; const field_ptr_ty_info = field_ptr_ty.ptrInfo().data; @@ -15773,11 +15773,11 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr ptr_ty_data.@"align" = field.abi_align; } - const actual_field_ptr_ty = try Type.ptr(sema.arena, target, ptr_ty_data); + const actual_field_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data); const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, ptr_src); ptr_ty_data.pointee_type = struct_ty; - const result_ptr = try Type.ptr(sema.arena, target, ptr_ty_data); + const result_ptr = try Type.ptr(sema.arena, sema.mod, ptr_ty_data); if (try sema.resolveDefinedValue(block, src, casted_field_ptr)) |field_ptr_val| { const payload = field_ptr_val.castTag(.field_ptr).?.data; @@ -15850,8 +15850,8 @@ fn analyzeMinMax( var rhs_buf: Value.ElemValueBuffer = undefined; const elems = try sema.arena.alloc(Value, vec_len); for (elems) |*elem, i| { - const lhs_elem_val = lhs_val.elemValueBuffer(i, &lhs_buf); - const rhs_elem_val = rhs_val.elemValueBuffer(i, &rhs_buf); + const lhs_elem_val = lhs_val.elemValueBuffer(sema.mod, i, &lhs_buf); + const rhs_elem_val = rhs_val.elemValueBuffer(sema.mod, i, &rhs_buf); elem.* = opFunc(lhs_elem_val, rhs_elem_val, target); } return sema.addConstant( @@ -15878,18 +15878,17 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; const dest_ptr = sema.resolveInst(extra.dest); const dest_ptr_ty = sema.typeOf(dest_ptr); - const target = sema.mod.getTarget(); try sema.checkPtrOperand(block, dest_src, dest_ptr_ty); if (dest_ptr_ty.isConstPtr()) { - return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(target)}); + return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)}); } const uncasted_src_ptr = sema.resolveInst(extra.source); const uncasted_src_ptr_ty = sema.typeOf(uncasted_src_ptr); try sema.checkPtrOperand(block, src_src, uncasted_src_ptr_ty); const src_ptr_info = uncasted_src_ptr_ty.ptrInfo().data; - const wanted_src_ptr_ty = try Type.ptr(sema.arena, target, .{ + const wanted_src_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = dest_ptr_ty.elemType2(), .@"align" = src_ptr_info.@"align", .@"addrspace" = src_ptr_info.@"addrspace", @@ -15936,10 +15935,9 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; const dest_ptr = sema.resolveInst(extra.dest); const dest_ptr_ty = sema.typeOf(dest_ptr); - const target = sema.mod.getTarget(); try sema.checkPtrOperand(block, dest_src, dest_ptr_ty); if (dest_ptr_ty.isConstPtr()) { - return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(target)}); + return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)}); } const elem_ty = dest_ptr_ty.elemType2(); const value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.byte), value_src); @@ -16057,7 +16055,7 @@ fn zirVarExtended( }); new_var.* = .{ - .owner_decl = sema.owner_decl, + .owner_decl = sema.owner_decl_index, .init = init_val, .is_extern = small.is_extern, .is_mutable = true, // TODO get rid of this unused field @@ -16294,7 +16292,7 @@ fn zirBuiltinExtern( var ty = try sema.resolveType(block, ty_src, extra.lhs); const options_inst = sema.resolveInst(extra.rhs); - const target = sema.mod.getTarget(); + const mod = sema.mod; const options = options: { const extern_options_ty = try sema.getBuiltinType(block, options_src, "ExternOptions"); @@ -16315,11 +16313,11 @@ fn zirBuiltinExtern( var library_name: ?[]const u8 = null; if (!library_name_val.isNull()) { const payload = library_name_val.castTag(.opt_payload).?.data; - library_name = try payload.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, target); + library_name = try payload.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, mod); } break :options std.builtin.ExternOptions{ - .name = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, target), + .name = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, mod), .library_name = library_name, .linkage = linkage_val.toEnum(std.builtin.GlobalLinkage), .is_thread_local = is_thread_local_val.toBool(), @@ -16344,8 +16342,10 @@ fn zirBuiltinExtern( // TODO check duplicate extern - const new_decl = try sema.mod.allocateNewDecl(try sema.gpa.dupeZ(u8, options.name), sema.owner_decl.src_namespace, sema.owner_decl.src_node, null); - errdefer new_decl.destroy(sema.mod); + const new_decl_index = try mod.allocateNewDecl(sema.owner_decl.src_namespace, sema.owner_decl.src_node, null); + errdefer mod.destroyDecl(new_decl_index); + const new_decl = mod.declPtr(new_decl_index); + new_decl.name = try sema.gpa.dupeZ(u8, options.name); var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); errdefer new_decl_arena.deinit(); @@ -16355,7 +16355,7 @@ fn zirBuiltinExtern( errdefer new_decl_arena_allocator.destroy(new_var); new_var.* = .{ - .owner_decl = sema.owner_decl, + .owner_decl = sema.owner_decl_index, .init = Value.initTag(.unreachable_value), .is_extern = true, .is_mutable = false, // TODO get rid of this unused field @@ -16378,13 +16378,13 @@ fn zirBuiltinExtern( new_decl.@"linksection" = null; new_decl.has_tv = true; new_decl.analysis = .complete; - new_decl.generation = sema.mod.generation; + new_decl.generation = mod.generation; const arena_state = try new_decl_arena_allocator.create(std.heap.ArenaAllocator.State); arena_state.* = new_decl_arena.state; new_decl.value_arena = arena_state; - const ref = try sema.analyzeDeclRef(new_decl); + const ref = try sema.analyzeDeclRef(new_decl_index); try sema.requireRuntimeBlock(block, src); return block.addBitCast(ty, ref); } @@ -16412,12 +16412,14 @@ fn validateVarType( ) CompileError!void { if (try sema.validateRunTimeType(block, src, var_ty, is_extern)) return; - const target = sema.mod.getTarget(); + const mod = sema.mod; + const msg = msg: { - const msg = try sema.errMsg(block, src, "variable of type '{}' must be const or comptime", .{var_ty.fmt(target)}); + const msg = try sema.errMsg(block, src, "variable of type '{}' must be const or comptime", .{var_ty.fmt(mod)}); errdefer msg.destroy(sema.gpa); - try sema.explainWhyTypeIsComptime(block, src, msg, src.toSrcLoc(block.src_decl), var_ty); + const src_decl = mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsComptime(block, src, msg, src.toSrcLoc(src_decl), var_ty); break :msg msg; }; @@ -16489,7 +16491,6 @@ fn explainWhyTypeIsComptime( ty: Type, ) CompileError!void { const mod = sema.mod; - const target = mod.getTarget(); switch (ty.zigTypeTag()) { .Bool, .Int, @@ -16503,7 +16504,7 @@ fn explainWhyTypeIsComptime( .Fn => { try mod.errNoteNonLazy(src_loc, msg, "use '*const {}' for a function pointer type", .{ - ty.fmt(target), + ty.fmt(sema.mod), }); }, @@ -16534,7 +16535,7 @@ fn explainWhyTypeIsComptime( if (ty.castTag(.@"struct")) |payload| { const struct_obj = payload.data; for (struct_obj.fields.values()) |field, i| { - const field_src_loc = struct_obj.fieldSrcLoc(sema.gpa, .{ + const field_src_loc = struct_obj.fieldSrcLoc(sema.mod, .{ .index = i, .range = .type, }); @@ -16551,7 +16552,7 @@ fn explainWhyTypeIsComptime( if (ty.cast(Type.Payload.Union)) |payload| { const union_obj = payload.data; for (union_obj.fields.values()) |field, i| { - const field_src_loc = union_obj.fieldSrcLoc(sema.gpa, .{ + const field_src_loc = union_obj.fieldSrcLoc(sema.mod, .{ .index = i, .range = .type, }); @@ -16668,7 +16669,7 @@ fn panicWithMsg( const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); const target = mod.getTarget(); - const ptr_stack_trace_ty = try Type.ptr(arena, target, .{ + const ptr_stack_trace_ty = try Type.ptr(arena, mod, .{ .pointee_type = stack_trace_ty, .@"addrspace" = target_util.defaultAddressSpace(target, .global_constant), // TODO might need a place that is more dynamic }); @@ -16748,8 +16749,6 @@ fn fieldVal( else object_ty; - const target = sema.mod.getTarget(); - switch (inner_ty.zigTypeTag()) { .Array => { if (mem.eql(u8, field_name, "len")) { @@ -16762,7 +16761,7 @@ fn fieldVal( block, field_name_src, "no member named '{s}' in '{}'", - .{ field_name, object_ty.fmt(target) }, + .{ field_name, object_ty.fmt(sema.mod) }, ); } }, @@ -16786,7 +16785,7 @@ fn fieldVal( block, field_name_src, "no member named '{s}' in '{}'", - .{ field_name, object_ty.fmt(target) }, + .{ field_name, object_ty.fmt(sema.mod) }, ); } } else if (ptr_info.pointee_type.zigTypeTag() == .Array) { @@ -16800,7 +16799,7 @@ fn fieldVal( block, field_name_src, "no member named '{s}' in '{}'", - .{ field_name, ptr_info.pointee_type.fmt(target) }, + .{ field_name, ptr_info.pointee_type.fmt(sema.mod) }, ); } } @@ -16822,7 +16821,7 @@ fn fieldVal( break :blk entry.key_ptr.*; } return sema.fail(block, src, "no error named '{s}' in '{}'", .{ - field_name, child_type.fmt(target), + field_name, child_type.fmt(sema.mod), }); } else (try sema.mod.getErrorValue(field_name)).key; @@ -16876,10 +16875,10 @@ fn fieldVal( else => unreachable, }; return sema.fail(block, src, "{s} '{}' has no member named '{s}'", .{ - kw_name, child_type.fmt(target), field_name, + kw_name, child_type.fmt(sema.mod), field_name, }); }, - else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(target)}), + else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(sema.mod)}), } }, .Struct => if (is_pointer_to) { @@ -16898,7 +16897,7 @@ fn fieldVal( }, else => {}, } - return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(target)}); + return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(sema.mod)}); } fn fieldPtr( @@ -16912,12 +16911,11 @@ fn fieldPtr( // When editing this function, note that there is corresponding logic to be edited // in `fieldVal`. This function takes a pointer and returns a pointer. - const target = sema.mod.getTarget(); const object_ptr_src = src; // TODO better source location const object_ptr_ty = sema.typeOf(object_ptr); const object_ty = switch (object_ptr_ty.zigTypeTag()) { .Pointer => object_ptr_ty.elemType(), - else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty.fmt(target)}), + else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty.fmt(sema.mod)}), }; // Zig allows dereferencing a single pointer during field lookup. Note that @@ -16945,7 +16943,7 @@ fn fieldPtr( block, field_name_src, "no member named '{s}' in '{}'", - .{ field_name, object_ty.fmt(target) }, + .{ field_name, object_ty.fmt(sema.mod) }, ); } }, @@ -16971,7 +16969,7 @@ fn fieldPtr( } try sema.requireRuntimeBlock(block, src); - const result_ty = try Type.ptr(sema.arena, target, .{ + const result_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = slice_ptr_ty, .mutable = object_ptr_ty.ptrIsMutable(), .@"addrspace" = object_ptr_ty.ptrAddressSpace(), @@ -16985,13 +16983,13 @@ fn fieldPtr( return sema.analyzeDeclRef(try anon_decl.finish( Type.usize, - try Value.Tag.int_u64.create(anon_decl.arena(), val.sliceLen(target)), + try Value.Tag.int_u64.create(anon_decl.arena(), val.sliceLen(sema.mod)), 0, // default alignment )); } try sema.requireRuntimeBlock(block, src); - const result_ty = try Type.ptr(sema.arena, target, .{ + const result_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = Type.usize, .mutable = object_ptr_ty.ptrIsMutable(), .@"addrspace" = object_ptr_ty.ptrAddressSpace(), @@ -17003,7 +17001,7 @@ fn fieldPtr( block, field_name_src, "no member named '{s}' in '{}'", - .{ field_name, object_ty.fmt(target) }, + .{ field_name, object_ty.fmt(sema.mod) }, ); } }, @@ -17027,7 +17025,7 @@ fn fieldPtr( break :blk entry.key_ptr.*; } return sema.fail(block, src, "no error named '{s}' in '{}'", .{ - field_name, child_type.fmt(target), + field_name, child_type.fmt(sema.mod), }); } else (try sema.mod.getErrorValue(field_name)).key; @@ -17085,7 +17083,7 @@ fn fieldPtr( } return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); }, - else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(target)}), + else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(sema.mod)}), } }, .Struct => { @@ -17104,7 +17102,7 @@ fn fieldPtr( }, else => {}, } - return sema.fail(block, src, "type '{}' does not support field access (fieldPtr, {}.{s})", .{ object_ty.fmt(target), object_ptr_ty.fmt(target), field_name }); + return sema.fail(block, src, "type '{}' does not support field access (fieldPtr, {}.{s})", .{ object_ty.fmt(sema.mod), object_ptr_ty.fmt(sema.mod), field_name }); } fn fieldCallBind( @@ -17118,13 +17116,12 @@ fn fieldCallBind( // When editing this function, note that there is corresponding logic to be edited // in `fieldVal`. This function takes a pointer and returns a pointer. - const target = sema.mod.getTarget(); const raw_ptr_src = src; // TODO better source location const raw_ptr_ty = sema.typeOf(raw_ptr); const inner_ty = if (raw_ptr_ty.zigTypeTag() == .Pointer and raw_ptr_ty.ptrSize() == .One) raw_ptr_ty.childType() else - return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(target)}); + return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(sema.mod)}); // Optionally dereference a second pointer to get the concrete type. const is_double_ptr = inner_ty.zigTypeTag() == .Pointer and inner_ty.ptrSize() == .One; @@ -17184,7 +17181,7 @@ fn fieldCallBind( first_param_type.zigTypeTag() == .Pointer and (first_param_type.ptrSize() == .One or first_param_type.ptrSize() == .C) and - first_param_type.childType().eql(concrete_ty, target))) + first_param_type.childType().eql(concrete_ty, sema.mod))) { // zig fmt: on // TODO: bound fn calls on rvalues should probably @@ -17195,7 +17192,7 @@ fn fieldCallBind( .arg0_inst = object_ptr, }); return sema.addConstant(ty, value); - } else if (first_param_type.eql(concrete_ty, target)) { + } else if (first_param_type.eql(concrete_ty, sema.mod)) { var deref = try sema.analyzeLoad(block, src, object_ptr, src); const ty = Type.Tag.bound_fn.init(); const value = try Value.Tag.bound_fn.create(arena, .{ @@ -17211,7 +17208,7 @@ fn fieldCallBind( else => {}, } - return sema.fail(block, src, "type '{}' has no field or member function named '{s}'", .{ concrete_ty.fmt(target), field_name }); + return sema.fail(block, src, "type '{}' has no field or member function named '{s}'", .{ concrete_ty.fmt(sema.mod), field_name }); } fn finishFieldCallBind( @@ -17224,8 +17221,7 @@ fn finishFieldCallBind( object_ptr: Air.Inst.Ref, ) CompileError!Air.Inst.Ref { const arena = sema.arena; - const target = sema.mod.getTarget(); - const ptr_field_ty = try Type.ptr(arena, target, .{ + const ptr_field_ty = try Type.ptr(arena, sema.mod, .{ .pointee_type = field_ty, .mutable = ptr_ty.ptrIsMutable(), .@"addrspace" = ptr_ty.ptrAddressSpace(), @@ -17254,9 +17250,10 @@ fn namespaceLookup( src: LazySrcLoc, namespace: *Namespace, decl_name: []const u8, -) CompileError!?*Decl { +) CompileError!?Decl.Index { const gpa = sema.gpa; - if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl| { + if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| { + const decl = sema.mod.declPtr(decl_index); if (!decl.is_pub and decl.getFileScope() != block.getFileScope()) { const msg = msg: { const msg = try sema.errMsg(block, src, "'{s}' is not marked 'pub'", .{ @@ -17268,7 +17265,7 @@ fn namespaceLookup( }; return sema.failWithOwnedErrorMsg(block, msg); } - return decl; + return decl_index; } return null; } @@ -17377,7 +17374,7 @@ fn structFieldPtrByIndex( ptr_ty_data.@"align" = field.abi_align; } - const ptr_field_ty = try Type.ptr(sema.arena, target, ptr_ty_data); + const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data); if (field.is_comptime) { var anon_decl = try block.startAnonDecl(field_src); @@ -17476,15 +17473,14 @@ fn tupleFieldIndex( field_name: []const u8, field_name_src: LazySrcLoc, ) CompileError!u32 { - const target = sema.mod.getTarget(); const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch |err| { return sema.fail(block, field_name_src, "tuple {} has no such field '{s}': {s}", .{ - tuple_ty.fmt(target), field_name, @errorName(err), + tuple_ty.fmt(sema.mod), field_name, @errorName(err), }); }; if (field_index >= tuple_ty.structFieldCount()) { return sema.fail(block, field_name_src, "tuple {} has no such field '{s}'", .{ - tuple_ty.fmt(target), field_name, + tuple_ty.fmt(sema.mod), field_name, }); } return field_index; @@ -17535,8 +17531,7 @@ fn unionFieldPtr( const union_obj = union_ty.cast(Type.Payload.Union).?.data; const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); const field = union_obj.fields.values()[field_index]; - const target = sema.mod.getTarget(); - const ptr_field_ty = try Type.ptr(arena, target, .{ + const ptr_field_ty = try Type.ptr(arena, sema.mod, .{ .pointee_type = field.ty, .mutable = union_ptr_ty.ptrIsMutable(), .@"addrspace" = union_ptr_ty.ptrAddressSpace(), @@ -17559,7 +17554,7 @@ fn unionFieldPtr( // .data = field_index, //}; //const field_tag = Value.initPayload(&field_tag_buf.base); - //const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, target); + //const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, mod); //if (!tag_matches) { // // TODO enhance this saying which one was active // // and which one was accessed, and showing where the union was declared. @@ -17608,8 +17603,7 @@ fn unionFieldVal( .data = field_index, }; const field_tag = Value.initPayload(&field_tag_buf.base); - const target = sema.mod.getTarget(); - const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, target); + const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod); switch (union_obj.layout) { .Auto => { if (tag_matches) { @@ -17630,7 +17624,7 @@ fn unionFieldVal( if (tag_matches) { return sema.addConstant(field.ty, tag_and_val.val); } else { - const old_ty = union_ty.unionFieldType(tag_and_val.tag, target); + const old_ty = union_ty.unionFieldType(tag_and_val.tag, sema.mod); const new_val = try sema.bitCastVal(block, src, tag_and_val.val, old_ty, field.ty, 0); return sema.addConstant(field.ty, new_val); } @@ -17655,17 +17649,17 @@ fn elemPtr( const target = sema.mod.getTarget(); const indexable_ty = switch (indexable_ptr_ty.zigTypeTag()) { .Pointer => indexable_ptr_ty.elemType(), - else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(target)}), + else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(sema.mod)}), }; if (!indexable_ty.isIndexable()) { - return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty.fmt(target)}); + return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty.fmt(sema.mod)}); } switch (indexable_ty.zigTypeTag()) { .Pointer => { // In all below cases, we have to deref the ptr operand to get the actual indexable pointer. const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); - const result_ty = try indexable_ty.elemPtrType(sema.arena, target); + const result_ty = try indexable_ty.elemPtrType(sema.arena, sema.mod); switch (indexable_ty.ptrSize()) { .Slice => return sema.elemPtrSlice(block, indexable_ptr_src, indexable, elem_index_src, elem_index), .Many, .C => { @@ -17676,7 +17670,7 @@ fn elemPtr( const ptr_val = maybe_ptr_val orelse break :rs indexable_ptr_src; const index_val = maybe_index_val orelse break :rs elem_index_src; const index = @intCast(usize, index_val.toUnsignedInt(target)); - const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, target); + const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, sema.mod); return sema.addConstant(result_ty, elem_ptr); }; @@ -17713,7 +17707,7 @@ fn elemVal( const target = sema.mod.getTarget(); if (!indexable_ty.isIndexable()) { - return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty.fmt(target)}); + return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty.fmt(sema.mod)}); } // TODO in case of a vector of pointers, we need to detect whether the element @@ -17731,7 +17725,7 @@ fn elemVal( const indexable_val = maybe_indexable_val orelse break :rs indexable_src; const index_val = maybe_index_val orelse break :rs elem_index_src; const index = @intCast(usize, index_val.toUnsignedInt(target)); - const elem_ptr_val = try indexable_val.elemPtr(indexable_ty, sema.arena, index, target); + const elem_ptr_val = try indexable_val.elemPtr(indexable_ty, sema.arena, index, sema.mod); if (try sema.pointerDeref(block, indexable_src, elem_ptr_val, indexable_ty)) |elem_val| { return sema.addConstant(indexable_ty.elemType2(), elem_val); } @@ -17785,8 +17779,7 @@ fn tupleFieldPtr( } const field_ty = tuple_fields.types[field_index]; - const target = sema.mod.getTarget(); - const ptr_field_ty = try Type.ptr(sema.arena, target, .{ + const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = field_ty, .mutable = tuple_ptr_ty.ptrIsMutable(), .@"addrspace" = tuple_ptr_ty.ptrAddressSpace(), @@ -17881,7 +17874,7 @@ fn elemValArray( } if (maybe_index_val) |index_val| { const index = @intCast(usize, index_val.toUnsignedInt(target)); - const elem_val = try array_val.elemValue(sema.arena, index); + const elem_val = try array_val.elemValue(sema.mod, sema.arena, index); return sema.addConstant(elem_ty, elem_val); } } @@ -17914,7 +17907,7 @@ fn elemPtrArray( const array_sent = array_ty.sentinel() != null; const array_len = array_ty.arrayLen(); const array_len_s = array_len + @boolToInt(array_sent); - const elem_ptr_ty = try array_ptr_ty.elemPtrType(sema.arena, target); + const elem_ptr_ty = try array_ptr_ty.elemPtrType(sema.arena, sema.mod); if (array_len_s == 0) { return sema.fail(block, elem_index_src, "indexing into empty array", .{}); @@ -17937,7 +17930,7 @@ fn elemPtrArray( } if (maybe_index_val) |index_val| { const index = @intCast(usize, index_val.toUnsignedInt(target)); - const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, index, target); + const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, index, sema.mod); return sema.addConstant(elem_ptr_ty, elem_ptr); } } @@ -17977,7 +17970,7 @@ fn elemValSlice( if (maybe_slice_val) |slice_val| { runtime_src = elem_index_src; - const slice_len = slice_val.sliceLen(target); + const slice_len = slice_val.sliceLen(sema.mod); const slice_len_s = slice_len + @boolToInt(slice_sent); if (slice_len_s == 0) { return sema.fail(block, elem_index_src, "indexing into empty slice", .{}); @@ -17988,7 +17981,7 @@ fn elemValSlice( const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); } - const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index, target); + const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index, sema.mod); if (try sema.pointerDeref(block, slice_src, elem_ptr_val, slice_ty)) |elem_val| { return sema.addConstant(elem_ty, elem_val); } @@ -17999,7 +17992,7 @@ fn elemValSlice( try sema.requireRuntimeBlock(block, runtime_src); if (block.wantSafety()) { const len_inst = if (maybe_slice_val) |slice_val| - try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(target)) + try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod)) else try block.addTyOp(.slice_len, Type.usize, slice); const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; @@ -18020,7 +18013,7 @@ fn elemPtrSlice( const target = sema.mod.getTarget(); const slice_ty = sema.typeOf(slice); const slice_sent = slice_ty.sentinel() != null; - const elem_ptr_ty = try slice_ty.elemPtrType(sema.arena, target); + const elem_ptr_ty = try slice_ty.elemPtrType(sema.arena, sema.mod); const maybe_undef_slice_val = try sema.resolveMaybeUndefVal(block, slice_src, slice); // index must be defined since it can index out of bounds @@ -18030,7 +18023,7 @@ fn elemPtrSlice( if (slice_val.isUndef()) { return sema.addConstUndef(elem_ptr_ty); } - const slice_len = slice_val.sliceLen(target); + const slice_len = slice_val.sliceLen(sema.mod); const slice_len_s = slice_len + @boolToInt(slice_sent); if (slice_len_s == 0) { return sema.fail(block, elem_index_src, "indexing into empty slice", .{}); @@ -18041,7 +18034,7 @@ fn elemPtrSlice( const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); } - const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index, target); + const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index, sema.mod); return sema.addConstant(elem_ptr_ty, elem_ptr_val); } } @@ -18052,7 +18045,7 @@ fn elemPtrSlice( const len_inst = len: { if (maybe_undef_slice_val) |slice_val| if (!slice_val.isUndef()) - break :len try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(target)); + break :len try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod)); break :len try block.addTyOp(.slice_len, Type.usize, slice); }; const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; @@ -18079,7 +18072,7 @@ fn coerce( const inst_ty = try sema.resolveTypeFields(block, inst_src, sema.typeOf(inst)); const target = sema.mod.getTarget(); // If the types are the same, we can return the operand. - if (dest_ty.eql(inst_ty, target)) + if (dest_ty.eql(inst_ty, sema.mod)) return inst; const arena = sema.arena; @@ -18185,7 +18178,7 @@ fn coerce( // *[N:s]T to [*]T if (dest_info.sentinel) |dst_sentinel| { if (array_ty.sentinel()) |src_sentinel| { - if (src_sentinel.eql(dst_sentinel, dst_elem_type, target)) { + if (src_sentinel.eql(dst_sentinel, dst_elem_type, sema.mod)) { return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); } } @@ -18254,7 +18247,7 @@ fn coerce( } if (inst_info.size == .Slice) { if (dest_info.sentinel == null or inst_info.sentinel == null or - !dest_info.sentinel.?.eql(inst_info.sentinel.?, dest_info.pointee_type, target)) + !dest_info.sentinel.?.eql(inst_info.sentinel.?, dest_info.pointee_type, sema.mod)) break :p; const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); @@ -18334,7 +18327,7 @@ fn coerce( } if (dest_info.sentinel == null or inst_info.sentinel == null or - !dest_info.sentinel.?.eql(inst_info.sentinel.?, dest_info.pointee_type, target)) + !dest_info.sentinel.?.eql(inst_info.sentinel.?, dest_info.pointee_type, sema.mod)) break :p; const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); @@ -18347,11 +18340,16 @@ fn coerce( const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :float; if (val.floatHasFraction()) { - return sema.fail(block, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val.fmtValue(inst_ty, target), dest_ty.fmt(target) }); + return sema.fail( + block, + inst_src, + "fractional component prevents float value {} from coercion to type '{}'", + .{ val.fmtValue(inst_ty, sema.mod), dest_ty.fmt(sema.mod) }, + ); } const result_val = val.floatToInt(sema.arena, inst_ty, dest_ty, target) catch |err| switch (err) { error.FloatCannotFit => { - return sema.fail(block, inst_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty.fmt(target) }); + return sema.fail(block, inst_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty.fmt(sema.mod) }); }, else => |e| return e, }; @@ -18361,7 +18359,7 @@ fn coerce( if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| { // comptime known integer to other number if (!val.intFitsInType(dest_ty, target)) { - return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_ty.fmt(target), val.fmtValue(inst_ty, target) }); + return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) }); } return try sema.addConstant(dest_ty, val); } @@ -18391,12 +18389,12 @@ fn coerce( .Float => { if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| { const result_val = try val.floatCast(sema.arena, dest_ty, target); - if (!val.eql(result_val, dest_ty, target)) { + if (!val.eql(result_val, dest_ty, sema.mod)) { return sema.fail( block, inst_src, "type {} cannot represent float value {}", - .{ dest_ty.fmt(target), val.fmtValue(inst_ty, target) }, + .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) }, ); } return try sema.addConstant(dest_ty, result_val); @@ -18415,12 +18413,12 @@ fn coerce( const result_val = try val.intToFloat(sema.arena, inst_ty, dest_ty, target); // TODO implement this compile error //const int_again_val = try result_val.floatToInt(sema.arena, inst_ty); - //if (!int_again_val.eql(val, inst_ty, target)) { + //if (!int_again_val.eql(val, inst_ty, mod)) { // return sema.fail( // block, // inst_src, // "type {} cannot represent integer value {}", - // .{ dest_ty.fmt(target), val }, + // .{ dest_ty.fmt(sema.mod), val }, // ); //} return try sema.addConstant(dest_ty, result_val); @@ -18441,11 +18439,11 @@ fn coerce( block, inst_src, "enum '{}' has no field named '{s}'", - .{ dest_ty.fmt(target), bytes }, + .{ dest_ty.fmt(sema.mod), bytes }, ); errdefer msg.destroy(sema.gpa); try sema.mod.errNoteNonLazy( - dest_ty.declSrcLoc(), + dest_ty.declSrcLoc(sema.mod), msg, "enum declared here", .{}, @@ -18462,7 +18460,7 @@ fn coerce( .Union => blk: { // union to its own tag type const union_tag_ty = inst_ty.unionTagType() orelse break :blk; - if (union_tag_ty.eql(dest_ty, target)) { + if (union_tag_ty.eql(dest_ty, sema.mod)) { return sema.unionToTag(block, dest_ty, inst, inst_src); } }, @@ -18557,7 +18555,7 @@ fn coerce( return sema.addConstUndef(dest_ty); } - return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty.fmt(target), inst_ty.fmt(target) }); + return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod) }); } const InMemoryCoercionResult = enum { @@ -18586,7 +18584,7 @@ fn coerceInMemoryAllowed( dest_src: LazySrcLoc, src_src: LazySrcLoc, ) CompileError!InMemoryCoercionResult { - if (dest_ty.eql(src_ty, target)) + if (dest_ty.eql(src_ty, sema.mod)) return .ok; // Differently-named integers with the same number of bits. @@ -18650,7 +18648,7 @@ fn coerceInMemoryAllowed( } const ok_sent = dest_info.sentinel == null or (src_info.sentinel != null and - dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.elem_type, target)); + dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.elem_type, sema.mod)); if (!ok_sent) { return .no_match; } @@ -18893,7 +18891,7 @@ fn coerceInMemoryAllowedPtrs( const ok_sent = dest_info.sentinel == null or src_info.size == .C or (src_info.sentinel != null and - dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type, target)); + dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type, sema.mod)); if (!ok_sent) { return .no_match; } @@ -18934,7 +18932,7 @@ fn coerceInMemoryAllowedPtrs( // resolved and we compare the alignment numerically. alignment: { if (src_info.@"align" == 0 and dest_info.@"align" == 0 and - dest_info.pointee_type.eql(src_info.pointee_type, target)) + dest_info.pointee_type.eql(src_info.pointee_type, sema.mod)) { break :alignment; } @@ -19089,8 +19087,7 @@ fn obtainBitCastedVectorPtr(sema: *Sema, ptr: Air.Inst.Ref) ?Air.Inst.Ref { // We have a pointer-to-array and a pointer-to-vector. If the elements and // lengths match, return the result. const vector_ty = sema.typeOf(prev_ptr).childType(); - const target = sema.mod.getTarget(); - if (array_ty.childType().eql(vector_ty.childType(), target) and + if (array_ty.childType().eql(vector_ty.childType(), sema.mod) and array_ty.arrayLen() == vector_ty.vectorLen()) { return prev_ptr; @@ -19114,8 +19111,8 @@ fn storePtrVal( const bitcasted_val = try sema.bitCastVal(block, src, operand_val, operand_ty, mut_kit.ty, 0); - const arena = mut_kit.beginArena(sema.gpa); - defer mut_kit.finishArena(); + const arena = mut_kit.beginArena(sema.mod); + defer mut_kit.finishArena(sema.mod); mut_kit.val.* = try bitcasted_val.copy(arena); } @@ -19126,13 +19123,15 @@ const ComptimePtrMutationKit = struct { ty: Type, decl_arena: std.heap.ArenaAllocator = undefined, - fn beginArena(self: *ComptimePtrMutationKit, gpa: Allocator) Allocator { - self.decl_arena = self.decl_ref_mut.decl.value_arena.?.promote(gpa); + fn beginArena(self: *ComptimePtrMutationKit, mod: *Module) Allocator { + const decl = mod.declPtr(self.decl_ref_mut.decl_index); + self.decl_arena = decl.value_arena.?.promote(mod.gpa); return self.decl_arena.allocator(); } - fn finishArena(self: *ComptimePtrMutationKit) void { - self.decl_ref_mut.decl.value_arena.?.* = self.decl_arena.state; + fn finishArena(self: *ComptimePtrMutationKit, mod: *Module) void { + const decl = mod.declPtr(self.decl_ref_mut.decl_index); + decl.value_arena.?.* = self.decl_arena.state; self.decl_arena = undefined; } }; @@ -19154,10 +19153,11 @@ fn beginComptimePtrMutation( switch (ptr_val.tag()) { .decl_ref_mut => { const decl_ref_mut = ptr_val.castTag(.decl_ref_mut).?.data; + const decl = sema.mod.declPtr(decl_ref_mut.decl_index); return ComptimePtrMutationKit{ .decl_ref_mut = decl_ref_mut, - .val = &decl_ref_mut.decl.val, - .ty = decl_ref_mut.decl.ty, + .val = &decl.val, + .ty = decl.ty, }; }, .elem_ptr => { @@ -19178,8 +19178,8 @@ fn beginComptimePtrMutation( // An array has been initialized to undefined at comptime and now we // are for the first time setting an element. We must change the representation // of the array from `undef` to `array`. - const arena = parent.beginArena(sema.gpa); - defer parent.finishArena(); + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); const array_len_including_sentinel = try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel()); @@ -19200,8 +19200,8 @@ fn beginComptimePtrMutation( // If we wanted to avoid this, there would need to be special detection // elsewhere to identify when writing a value to an array element that is stored // using the `bytes` tag, and handle it without making a call to this function. - const arena = parent.beginArena(sema.gpa); - defer parent.finishArena(); + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); const bytes = parent.val.castTag(.bytes).?.data; const dest_len = parent.ty.arrayLenIncludingSentinel(); @@ -19229,8 +19229,8 @@ fn beginComptimePtrMutation( // need to be special detection elsewhere to identify when writing a value to an // array element that is stored using the `repeated` tag, and handle it // without making a call to this function. - const arena = parent.beginArena(sema.gpa); - defer parent.finishArena(); + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); const repeated_val = try parent.val.castTag(.repeated).?.data.copy(arena); const array_len_including_sentinel = @@ -19281,8 +19281,8 @@ fn beginComptimePtrMutation( // A struct or union has been initialized to undefined at comptime and now we // are for the first time setting a field. We must change the representation // of the struct/union from `undef` to `struct`/`union`. - const arena = parent.beginArena(sema.gpa); - defer parent.finishArena(); + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); switch (parent.ty.zigTypeTag()) { .Struct => { @@ -19322,8 +19322,8 @@ fn beginComptimePtrMutation( }, .@"union" => { // We need to set the active field of the union. - const arena = parent.beginArena(sema.gpa); - defer parent.finishArena(); + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); const payload = &parent.val.castTag(.@"union").?.data; payload.tag = try Value.Tag.enum_field_index.create(arena, field_index); @@ -19347,8 +19347,8 @@ fn beginComptimePtrMutation( // An error union has been initialized to undefined at comptime and now we // are for the first time setting the payload. We must change the // representation of the error union from `undef` to `opt_payload`. - const arena = parent.beginArena(sema.gpa); - defer parent.finishArena(); + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); const payload = try arena.create(Value.Payload.SubValue); payload.* = .{ @@ -19380,8 +19380,8 @@ fn beginComptimePtrMutation( // An optional has been initialized to undefined at comptime and now we // are for the first time setting the payload. We must change the // representation of the optional from `undef` to `opt_payload`. - const arena = parent.beginArena(sema.gpa); - defer parent.finishArena(); + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); const payload = try arena.create(Value.Payload.SubValue); payload.* = .{ @@ -19451,12 +19451,13 @@ fn beginComptimePtrLoad( .decl_ref, .decl_ref_mut, => blk: { - const decl = switch (ptr_val.tag()) { + const decl_index = switch (ptr_val.tag()) { .decl_ref => ptr_val.castTag(.decl_ref).?.data, - .decl_ref_mut => ptr_val.castTag(.decl_ref_mut).?.data.decl, + .decl_ref_mut => ptr_val.castTag(.decl_ref_mut).?.data.decl_index, else => unreachable, }; const is_mutable = ptr_val.tag() == .decl_ref_mut; + const decl = sema.mod.declPtr(decl_index); const decl_tv = try decl.typedValue(); if (decl_tv.val.tag() == .variable) return error.RuntimeLoad; @@ -19477,7 +19478,9 @@ fn beginComptimePtrLoad( // This code assumes that elem_ptrs have been "flattened" in order for direct dereference // to succeed, meaning that elem ptrs of the same elem_ty are coalesced. Here we check that // our parent is not an elem_ptr with the same elem_ty, since that would be "unflattened" - if (elem_ptr.array_ptr.castTag(.elem_ptr)) |parent_elem_ptr| assert(!(parent_elem_ptr.data.elem_ty.eql(elem_ty, target))); + if (elem_ptr.array_ptr.castTag(.elem_ptr)) |parent_elem_ptr| { + assert(!(parent_elem_ptr.data.elem_ty.eql(elem_ty, sema.mod))); + } if (elem_ptr.index != 0) { if (elem_ty.hasWellDefinedLayout()) { @@ -19510,11 +19513,11 @@ fn beginComptimePtrLoad( if (maybe_array_ty) |load_ty| { // It's possible that we're loading a [N]T, in which case we'd like to slice // the pointee array directly from our parent array. - if (load_ty.isArrayOrVector() and load_ty.childType().eql(elem_ty, target)) { + if (load_ty.isArrayOrVector() and load_ty.childType().eql(elem_ty, sema.mod)) { const N = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel()); deref.pointee = if (elem_ptr.index + N <= check_len) TypedValue{ - .ty = try Type.array(sema.arena, N, null, elem_ty, target), - .val = try array_tv.val.sliceArray(sema.arena, elem_ptr.index, elem_ptr.index + N), + .ty = try Type.array(sema.arena, N, null, elem_ty, sema.mod), + .val = try array_tv.val.sliceArray(sema.mod, sema.arena, elem_ptr.index, elem_ptr.index + N), } else null; break :blk deref; } @@ -19522,7 +19525,7 @@ fn beginComptimePtrLoad( deref.pointee = if (elem_ptr.index < check_len) TypedValue{ .ty = elem_ty, - .val = try array_tv.val.elemValue(sema.arena, elem_ptr.index), + .val = try array_tv.val.elemValue(sema.mod, sema.arena, elem_ptr.index), } else null; break :blk deref; }, @@ -19637,9 +19640,9 @@ fn bitCast( if (old_bits != dest_bits) { return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{}' has {d} bits but source type '{}' has {d} bits", .{ - dest_ty.fmt(target), + dest_ty.fmt(sema.mod), dest_bits, - old_ty.fmt(target), + old_ty.fmt(sema.mod), old_bits, }); } @@ -19662,7 +19665,7 @@ pub fn bitCastVal( buffer_offset: usize, ) !Value { const target = sema.mod.getTarget(); - if (old_ty.eql(new_ty, target)) return val; + if (old_ty.eql(new_ty, sema.mod)) return val; // For types with well-defined memory layouts, we serialize them a byte buffer, // then deserialize to the new type. @@ -19718,12 +19721,11 @@ fn coerceEnumToUnion( inst_src: LazySrcLoc, ) !Air.Inst.Ref { const inst_ty = sema.typeOf(inst); - const target = sema.mod.getTarget(); const tag_ty = union_ty.unionTagType() orelse { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{ - union_ty.fmt(target), inst_ty.fmt(target), + union_ty.fmt(sema.mod), inst_ty.fmt(sema.mod), }); errdefer msg.destroy(sema.gpa); try sema.errNote(block, union_ty_src, msg, "cannot coerce enum to untagged union", .{}); @@ -19736,10 +19738,10 @@ fn coerceEnumToUnion( const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src); if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| { const union_obj = union_ty.cast(Type.Payload.Union).?.data; - const field_index = union_obj.tag_ty.enumTagFieldIndex(val, target) orelse { + const field_index = union_obj.tag_ty.enumTagFieldIndex(val, sema.mod) orelse { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "union {} has no tag with value {}", .{ - union_ty.fmt(target), val.fmtValue(tag_ty, target), + union_ty.fmt(sema.mod), val.fmtValue(tag_ty, sema.mod), }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, union_ty); @@ -19753,7 +19755,7 @@ fn coerceEnumToUnion( const msg = msg: { const field_name = union_obj.fields.keys()[field_index]; const msg = try sema.errMsg(block, inst_src, "coercion from enum '{}' to union '{}' must initialize '{}' field '{s}'", .{ - inst_ty.fmt(target), union_ty.fmt(target), field_ty.fmt(target), field_name, + inst_ty.fmt(sema.mod), union_ty.fmt(sema.mod), field_ty.fmt(sema.mod), field_name, }); errdefer msg.destroy(sema.gpa); @@ -19775,7 +19777,7 @@ fn coerceEnumToUnion( if (tag_ty.isNonexhaustiveEnum()) { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "runtime coercion to union {} from non-exhaustive enum", .{ - union_ty.fmt(target), + union_ty.fmt(sema.mod), }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, tag_ty); @@ -19795,7 +19797,7 @@ fn coerceEnumToUnion( block, inst_src, "runtime coercion from enum '{}' to union '{}' which has non-void fields", - .{ tag_ty.fmt(target), union_ty.fmt(target) }, + .{ tag_ty.fmt(sema.mod), union_ty.fmt(sema.mod) }, ); errdefer msg.destroy(sema.gpa); @@ -19804,7 +19806,7 @@ fn coerceEnumToUnion( while (it.next()) |field| { const field_name = field.key_ptr.*; const field_ty = field.value_ptr.ty; - try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' has type '{}'", .{ field_name, field_ty.fmt(target) }); + try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' has type '{}'", .{ field_name, field_ty.fmt(sema.mod) }); field_index += 1; } try sema.addDeclaredHereNote(msg, union_ty); @@ -19892,7 +19894,7 @@ fn coerceArrayLike( if (dest_len != inst_len) { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{ - dest_ty.fmt(target), inst_ty.fmt(target), + dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod), }); errdefer msg.destroy(sema.gpa); try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len}); @@ -19959,12 +19961,11 @@ fn coerceTupleToArray( const inst_ty = sema.typeOf(inst); const inst_len = inst_ty.arrayLen(); const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen()); - const target = sema.mod.getTarget(); if (dest_len != inst_len) { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{ - dest_ty.fmt(target), inst_ty.fmt(target), + dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod), }); errdefer msg.destroy(sema.gpa); try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len}); @@ -20017,8 +20018,7 @@ fn coerceTupleToSlicePtrs( const tuple_ty = sema.typeOf(ptr_tuple).childType(); const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src); const slice_info = slice_ty.ptrInfo().data; - const target = sema.mod.getTarget(); - const array_ty = try Type.array(sema.arena, tuple_ty.structFieldCount(), slice_info.sentinel, slice_info.pointee_type, target); + const array_ty = try Type.array(sema.arena, tuple_ty.structFieldCount(), slice_info.sentinel, slice_info.pointee_type, sema.mod); const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src); if (slice_info.@"align" != 0) { return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{}); @@ -20141,23 +20141,23 @@ fn analyzeDeclVal( sema: *Sema, block: *Block, src: LazySrcLoc, - decl: *Decl, + decl_index: Decl.Index, ) CompileError!Air.Inst.Ref { - if (sema.decl_val_table.get(decl)) |result| { + if (sema.decl_val_table.get(decl_index)) |result| { return result; } - const decl_ref = try sema.analyzeDeclRef(decl); + const decl_ref = try sema.analyzeDeclRef(decl_index); const result = try sema.analyzeLoad(block, src, decl_ref, src); if (Air.refToIndex(result)) |index| { if (sema.air_instructions.items(.tag)[index] == .constant) { - try sema.decl_val_table.put(sema.gpa, decl, result); + try sema.decl_val_table.put(sema.gpa, decl_index, result); } } return result; } -fn ensureDeclAnalyzed(sema: *Sema, decl: *Decl) CompileError!void { - sema.mod.ensureDeclAnalyzed(decl) catch |err| { +fn ensureDeclAnalyzed(sema: *Sema, decl_index: Decl.Index) CompileError!void { + sema.mod.ensureDeclAnalyzed(decl_index) catch |err| { if (sema.owner_func) |owner_func| { owner_func.state = .dependency_failure; } else { @@ -20186,7 +20186,7 @@ fn refValue(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, val: Value) ! try val.copy(anon_decl.arena()), 0, // default alignment ); - try sema.mod.declareDeclDependency(sema.owner_decl, decl); + try sema.mod.declareDeclDependency(sema.owner_decl_index, decl); return try Value.Tag.decl_ref.create(sema.arena, decl); } @@ -20197,29 +20197,29 @@ fn optRefValue(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, opt_val: ? return result; } -fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref { - try sema.mod.declareDeclDependency(sema.owner_decl, decl); - try sema.ensureDeclAnalyzed(decl); +fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref { + try sema.mod.declareDeclDependency(sema.owner_decl_index, decl_index); + try sema.ensureDeclAnalyzed(decl_index); - const target = sema.mod.getTarget(); + const decl = sema.mod.declPtr(decl_index); const decl_tv = try decl.typedValue(); if (decl_tv.val.castTag(.variable)) |payload| { const variable = payload.data; - const ty = try Type.ptr(sema.arena, target, .{ + const ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = decl_tv.ty, .mutable = variable.is_mutable, .@"addrspace" = decl.@"addrspace", .@"align" = decl.@"align", }); - return sema.addConstant(ty, try Value.Tag.decl_ref.create(sema.arena, decl)); + return sema.addConstant(ty, try Value.Tag.decl_ref.create(sema.arena, decl_index)); } return sema.addConstant( - try Type.ptr(sema.arena, target, .{ + try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = decl_tv.ty, .mutable = false, .@"addrspace" = decl.@"addrspace", }), - try Value.Tag.decl_ref.create(sema.arena, decl), + try Value.Tag.decl_ref.create(sema.arena, decl_index), ); } @@ -20243,13 +20243,12 @@ fn analyzeRef( try sema.requireRuntimeBlock(block, src); const address_space = target_util.defaultAddressSpace(sema.mod.getTarget(), .local); - const target = sema.mod.getTarget(); - const ptr_type = try Type.ptr(sema.arena, target, .{ + const ptr_type = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = operand_ty, .mutable = false, .@"addrspace" = address_space, }); - const mut_ptr_type = try Type.ptr(sema.arena, target, .{ + const mut_ptr_type = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = operand_ty, .@"addrspace" = address_space, }); @@ -20267,11 +20266,10 @@ fn analyzeLoad( ptr: Air.Inst.Ref, ptr_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { - const target = sema.mod.getTarget(); const ptr_ty = sema.typeOf(ptr); const elem_ty = switch (ptr_ty.zigTypeTag()) { .Pointer => ptr_ty.childType(), - else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(target)}), + else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)}), }; if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { if (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) |elem_val| { @@ -20310,8 +20308,7 @@ fn analyzeSliceLen( if (slice_val.isUndef()) { return sema.addConstUndef(Type.usize); } - const target = sema.mod.getTarget(); - return sema.addIntUnsigned(Type.usize, slice_val.sliceLen(target)); + return sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod)); } try sema.requireRuntimeBlock(block, src); return block.addTyOp(.slice_len, Type.usize, slice_inst); @@ -20417,8 +20414,9 @@ fn analyzeSlice( const target = sema.mod.getTarget(); const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag()) { .Pointer => ptr_ptr_ty.elemType(), - else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ptr_ty.fmt(target)}), + else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ptr_ty.fmt(sema.mod)}), }; + const mod = sema.mod; var array_ty = ptr_ptr_child_ty; var slice_ty = ptr_ptr_ty; @@ -20465,7 +20463,7 @@ fn analyzeSlice( elem_ty = ptr_ptr_child_ty.childType(); }, }, - else => return sema.fail(block, ptr_src, "slice of non-array type '{}'", .{ptr_ptr_child_ty.fmt(target)}), + else => return sema.fail(block, ptr_src, "slice of non-array type '{}'", .{ptr_ptr_child_ty.fmt(mod)}), } const ptr = if (slice_ty.isSlice()) @@ -20492,7 +20490,7 @@ fn analyzeSlice( sema.arena, array_ty.arrayLenIncludingSentinel(), ); - if (end_val.compare(.gt, len_s_val, Type.usize, target)) { + if (end_val.compare(.gt, len_s_val, Type.usize, mod)) { const sentinel_label: []const u8 = if (array_ty.sentinel() != null) " +1 (sentinel)" else @@ -20503,8 +20501,8 @@ fn analyzeSlice( end_src, "end index {} out of bounds for array of length {}{s}", .{ - end_val.fmtValue(Type.usize, target), - len_val.fmtValue(Type.usize, target), + end_val.fmtValue(Type.usize, mod), + len_val.fmtValue(Type.usize, mod), sentinel_label, }, ); @@ -20513,7 +20511,7 @@ fn analyzeSlice( // end_is_len is only true if we are NOT using the sentinel // length. For sentinel-length, we don't want the type to // contain the sentinel. - if (end_val.eql(len_val, Type.usize, target)) { + if (end_val.eql(len_val, Type.usize, mod)) { end_is_len = true; } } @@ -20529,10 +20527,10 @@ fn analyzeSlice( const has_sentinel = slice_ty.sentinel() != null; var int_payload: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, - .data = slice_val.sliceLen(target) + @boolToInt(has_sentinel), + .data = slice_val.sliceLen(mod) + @boolToInt(has_sentinel), }; const slice_len_val = Value.initPayload(&int_payload.base); - if (end_val.compare(.gt, slice_len_val, Type.usize, target)) { + if (end_val.compare(.gt, slice_len_val, Type.usize, mod)) { const sentinel_label: []const u8 = if (has_sentinel) " +1 (sentinel)" else @@ -20543,8 +20541,8 @@ fn analyzeSlice( end_src, "end index {} out of bounds for slice of length {d}{s}", .{ - end_val.fmtValue(Type.usize, target), - slice_val.sliceLen(target), + end_val.fmtValue(Type.usize, mod), + slice_val.sliceLen(mod), sentinel_label, }, ); @@ -20557,7 +20555,7 @@ fn analyzeSlice( int_payload.data -= 1; } - if (end_val.eql(slice_len_val, Type.usize, target)) { + if (end_val.eql(slice_len_val, Type.usize, mod)) { end_is_len = true; } } @@ -20590,14 +20588,14 @@ fn analyzeSlice( // requirement: start <= end if (try sema.resolveDefinedValue(block, src, end)) |end_val| { if (try sema.resolveDefinedValue(block, src, start)) |start_val| { - if (start_val.compare(.gt, end_val, Type.usize, target)) { + if (start_val.compare(.gt, end_val, Type.usize, mod)) { return sema.fail( block, start_src, "start index {} is larger than end index {}", .{ - start_val.fmtValue(Type.usize, target), - end_val.fmtValue(Type.usize, target), + start_val.fmtValue(Type.usize, mod), + end_val.fmtValue(Type.usize, mod), }, ); } @@ -20613,8 +20611,8 @@ fn analyzeSlice( if (opt_new_len_val) |new_len_val| { const new_len_int = new_len_val.toUnsignedInt(target); - const return_ty = try Type.ptr(sema.arena, target, .{ - .pointee_type = try Type.array(sema.arena, new_len_int, sentinel, elem_ty, target), + const return_ty = try Type.ptr(sema.arena, mod, .{ + .pointee_type = try Type.array(sema.arena, new_len_int, sentinel, elem_ty, mod), .sentinel = null, .@"align" = new_ptr_ty_info.@"align", .@"addrspace" = new_ptr_ty_info.@"addrspace", @@ -20641,7 +20639,7 @@ fn analyzeSlice( return sema.fail(block, ptr_src, "non-zero length slice of undefined pointer", .{}); } - const return_ty = try Type.ptr(sema.arena, target, .{ + const return_ty = try Type.ptr(sema.arena, mod, .{ .pointee_type = elem_ty, .sentinel = sentinel, .@"align" = new_ptr_ty_info.@"align", @@ -20667,7 +20665,7 @@ fn analyzeSlice( if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| { // we don't need to add one for sentinels because the // underlying value data includes the sentinel - break :blk try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(target)); + break :blk try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod)); } const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice); @@ -20920,7 +20918,6 @@ fn cmpVector( try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); const result_ty = try Type.vector(sema.arena, lhs_ty.vectorLen(), Type.@"bool"); - const target = sema.mod.getTarget(); const runtime_src: LazySrcLoc = src: { if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { @@ -20928,7 +20925,7 @@ fn cmpVector( if (lhs_val.isUndef() or rhs_val.isUndef()) { return sema.addConstUndef(result_ty); } - const cmp_val = try lhs_val.compareVector(op, rhs_val, lhs_ty, sema.arena, target); + const cmp_val = try lhs_val.compareVector(op, rhs_val, lhs_ty, sema.arena, sema.mod); return sema.addConstant(result_ty, cmp_val); } else { break :src rhs_src; @@ -21080,7 +21077,7 @@ fn resolvePeerTypes( const candidate_ty_tag = try candidate_ty.zigTypeTagOrPoison(); const chosen_ty_tag = try chosen_ty.zigTypeTagOrPoison(); - if (candidate_ty.eql(chosen_ty, target)) + if (candidate_ty.eql(chosen_ty, sema.mod)) continue; switch (candidate_ty_tag) { @@ -21496,27 +21493,27 @@ fn resolvePeerTypes( // the source locations. const chosen_src = candidate_srcs.resolve( sema.gpa, - block.src_decl, + sema.mod.declPtr(block.src_decl), chosen_i, ); const candidate_src = candidate_srcs.resolve( sema.gpa, - block.src_decl, + sema.mod.declPtr(block.src_decl), candidate_i + 1, ); const msg = msg: { const msg = try sema.errMsg(block, src, "incompatible types: '{}' and '{}'", .{ - chosen_ty.fmt(target), - candidate_ty.fmt(target), + chosen_ty.fmt(sema.mod), + candidate_ty.fmt(sema.mod), }); errdefer msg.destroy(sema.gpa); if (chosen_src) |src_loc| - try sema.errNote(block, src_loc, msg, "type '{}' here", .{chosen_ty.fmt(target)}); + try sema.errNote(block, src_loc, msg, "type '{}' here", .{chosen_ty.fmt(sema.mod)}); if (candidate_src) |src_loc| - try sema.errNote(block, src_loc, msg, "type '{}' here", .{candidate_ty.fmt(target)}); + try sema.errNote(block, src_loc, msg, "type '{}' here", .{candidate_ty.fmt(sema.mod)}); break :msg msg; }; @@ -21538,13 +21535,13 @@ fn resolvePeerTypes( else => unreachable, }; - const new_ptr_ty = try Type.ptr(sema.arena, target, info.data); + const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data); const opt_ptr_ty = if (any_are_null) try Type.optional(sema.arena, new_ptr_ty) else new_ptr_ty; const set_ty = err_set_ty orelse return opt_ptr_ty; - return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, target); + return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, sema.mod); } if (seen_const) { @@ -21554,24 +21551,24 @@ fn resolvePeerTypes( const ptr_ty = chosen_ty.errorUnionPayload(); var info = ptr_ty.ptrInfo(); info.data.mutable = false; - const new_ptr_ty = try Type.ptr(sema.arena, target, info.data); + const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data); const opt_ptr_ty = if (any_are_null) try Type.optional(sema.arena, new_ptr_ty) else new_ptr_ty; const set_ty = err_set_ty orelse chosen_ty.errorUnionSet(); - return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, target); + return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, sema.mod); }, .Pointer => { var info = chosen_ty.ptrInfo(); info.data.mutable = false; - const new_ptr_ty = try Type.ptr(sema.arena, target, info.data); + const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data); const opt_ptr_ty = if (any_are_null) try Type.optional(sema.arena, new_ptr_ty) else new_ptr_ty; const set_ty = err_set_ty orelse return opt_ptr_ty; - return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, target); + return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, sema.mod); }, else => return chosen_ty, } @@ -21583,16 +21580,16 @@ fn resolvePeerTypes( else => try Type.optional(sema.arena, chosen_ty), }; const set_ty = err_set_ty orelse return opt_ty; - return try Type.errorUnion(sema.arena, set_ty, opt_ty, target); + return try Type.errorUnion(sema.arena, set_ty, opt_ty, sema.mod); } if (err_set_ty) |ty| switch (chosen_ty.zigTypeTag()) { .ErrorSet => return ty, .ErrorUnion => { const payload_ty = chosen_ty.errorUnionPayload(); - return try Type.errorUnion(sema.arena, ty, payload_ty, target); + return try Type.errorUnion(sema.arena, ty, payload_ty, sema.mod); }, - else => return try Type.errorUnion(sema.arena, ty, chosen_ty, target), + else => return try Type.errorUnion(sema.arena, ty, chosen_ty, sema.mod), }; return chosen_ty; @@ -21670,12 +21667,11 @@ fn resolveStructLayout( ) CompileError!void { const resolved_ty = try sema.resolveTypeFields(block, src, ty); if (resolved_ty.castTag(.@"struct")) |payload| { - const target = sema.mod.getTarget(); const struct_obj = payload.data; switch (struct_obj.status) { .none, .have_field_types => {}, .field_types_wip, .layout_wip => { - return sema.fail(block, src, "struct {} depends on itself", .{ty.fmt(target)}); + return sema.fail(block, src, "struct {} depends on itself", .{ty.fmt(sema.mod)}); }, .have_layout, .fully_resolved_wip, .fully_resolved => return, } @@ -21703,11 +21699,10 @@ fn resolveUnionLayout( ) CompileError!void { const resolved_ty = try sema.resolveTypeFields(block, src, ty); const union_obj = resolved_ty.cast(Type.Payload.Union).?.data; - const target = sema.mod.getTarget(); switch (union_obj.status) { .none, .have_field_types => {}, .field_types_wip, .layout_wip => { - return sema.fail(block, src, "union {} depends on itself", .{ty.fmt(target)}); + return sema.fail(block, src, "union {} depends on itself", .{ty.fmt(sema.mod)}); }, .have_layout, .fully_resolved_wip, .fully_resolved => return, } @@ -21774,10 +21769,6 @@ fn resolveStructFully( .fully_resolved_wip, .fully_resolved => return, } - log.debug("resolveStructFully {*} ('{s}')", .{ - struct_obj.owner_decl, struct_obj.owner_decl.name, - }); - { // After we have resolve struct layout we have to go over the fields again to // make sure pointer fields get their child types resolved as well. @@ -21866,11 +21857,10 @@ fn resolveTypeFieldsStruct( ty: Type, struct_obj: *Module.Struct, ) CompileError!void { - const target = sema.mod.getTarget(); switch (struct_obj.status) { .none => {}, .field_types_wip => { - return sema.fail(block, src, "struct {} depends on itself", .{ty.fmt(target)}); + return sema.fail(block, src, "struct {} depends on itself", .{ty.fmt(sema.mod)}); }, .have_field_types, .have_layout, @@ -21897,11 +21887,10 @@ fn resolveTypeFieldsUnion( ty: Type, union_obj: *Module.Union, ) CompileError!void { - const target = sema.mod.getTarget(); switch (union_obj.status) { .none => {}, .field_types_wip => { - return sema.fail(block, src, "union {} depends on itself", .{ty.fmt(target)}); + return sema.fail(block, src, "union {} depends on itself", .{ty.fmt(sema.mod)}); }, .have_field_types, .have_layout, @@ -21945,7 +21934,8 @@ fn resolveInferredErrorSet( // `*Module.Fn`. Not only is the function not relevant to the inferred error set // in this case, it may be a generic function which would cause an assertion failure // if we called `ensureFuncBodyAnalyzed` on it here. - if (ies.func.owner_decl.ty.fnInfo().return_type.errorUnionSet().castTag(.error_set_inferred).?.data == ies) { + const ies_func_owner_decl = sema.mod.declPtr(ies.func.owner_decl); + if (ies_func_owner_decl.ty.fnInfo().return_type.errorUnionSet().castTag(.error_set_inferred).?.data == ies) { // In this case we are dealing with the actual InferredErrorSet object that // corresponds to the function, not one created to track an inline/comptime call. try sema.ensureFuncBodyAnalyzed(ies.func); @@ -21986,7 +21976,7 @@ fn semaStructFields( defer tracy.end(); const gpa = mod.gpa; - const decl = struct_obj.owner_decl; + const decl_index = struct_obj.owner_decl; const zir = struct_obj.namespace.file_scope.zir; const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended; assert(extended.opcode == .struct_decl); @@ -22026,6 +22016,7 @@ fn semaStructFields( } extra_index += body.len; + const decl = mod.declPtr(decl_index); var decl_arena = decl.value_arena.?.promote(gpa); defer decl.value_arena.?.* = decl_arena.state; const decl_arena_allocator = decl_arena.allocator(); @@ -22040,6 +22031,7 @@ fn semaStructFields( .perm_arena = decl_arena_allocator, .code = zir, .owner_decl = decl, + .owner_decl_index = decl_index, .func = null, .fn_ret_ty = Type.void, .owner_func = null, @@ -22052,7 +22044,7 @@ fn semaStructFields( var block_scope: Block = .{ .parent = null, .sema = &sema, - .src_decl = decl, + .src_decl = decl_index, .namespace = &struct_obj.namespace, .wip_capture_scope = wip_captures.scope, .instructions = .{}, @@ -22171,7 +22163,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil defer tracy.end(); const gpa = mod.gpa; - const decl = union_obj.owner_decl; + const decl_index = union_obj.owner_decl; const zir = union_obj.namespace.file_scope.zir; const extended = zir.instructions.items(.data)[union_obj.zir_index].extended; assert(extended.opcode == .union_decl); @@ -22217,8 +22209,10 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil } extra_index += body.len; - var decl_arena = union_obj.owner_decl.value_arena.?.promote(gpa); - defer union_obj.owner_decl.value_arena.?.* = decl_arena.state; + const decl = mod.declPtr(decl_index); + + var decl_arena = decl.value_arena.?.promote(gpa); + defer decl.value_arena.?.* = decl_arena.state; const decl_arena_allocator = decl_arena.allocator(); var analysis_arena = std.heap.ArenaAllocator.init(gpa); @@ -22231,6 +22225,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil .perm_arena = decl_arena_allocator, .code = zir, .owner_decl = decl, + .owner_decl_index = decl_index, .func = null, .fn_ret_ty = Type.void, .owner_func = null, @@ -22243,7 +22238,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil var block_scope: Block = .{ .parent = null, .sema = &sema, - .src_decl = decl, + .src_decl = decl_index, .namespace = &union_obj.namespace, .wip_capture_scope = wip_captures.scope, .instructions = .{}, @@ -22353,7 +22348,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil const copied_val = try val.copy(decl_arena_allocator); map.putAssumeCapacityContext(copied_val, {}, .{ .ty = int_tag_ty, - .target = target, + .mod = mod, }); } else { const val = if (last_tag_val) |val| @@ -22365,7 +22360,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil const copied_val = try val.copy(decl_arena_allocator); map.putAssumeCapacityContext(copied_val, {}, .{ .ty = int_tag_ty, - .target = target, + .mod = mod, }); } } @@ -22411,7 +22406,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil const enum_has_field = names.orderedRemove(field_name); if (!enum_has_field) { const msg = msg: { - const msg = try sema.errMsg(block, src, "enum '{}' has no field named '{s}'", .{ union_obj.tag_ty.fmt(target), field_name }); + const msg = try sema.errMsg(block, src, "enum '{}' has no field named '{s}'", .{ union_obj.tag_ty.fmt(sema.mod), field_name }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, union_obj.tag_ty); break :msg msg; @@ -22475,15 +22470,16 @@ fn generateUnionTagTypeNumbered( const enum_ty = Type.initPayload(&enum_ty_payload.base); const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); // TODO better type name - const new_decl = try mod.createAnonymousDecl(block, .{ + const new_decl_index = try mod.createAnonymousDecl(block, .{ .ty = Type.type, .val = enum_val, }); + const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - errdefer mod.abortAnonDecl(new_decl); + errdefer mod.abortAnonDecl(new_decl_index); enum_obj.* = .{ - .owner_decl = new_decl, + .owner_decl = new_decl_index, .tag_ty = int_ty, .fields = .{}, .values = .{}, @@ -22493,7 +22489,7 @@ fn generateUnionTagTypeNumbered( try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{ .ty = int_ty, - .target = sema.mod.getTarget(), + .mod = mod, }); try new_decl.finalizeNewArena(&new_decl_arena); return enum_ty; @@ -22515,15 +22511,16 @@ fn generateUnionTagTypeSimple(sema: *Sema, block: *Block, fields_len: usize) !Ty const enum_ty = Type.initPayload(&enum_ty_payload.base); const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); // TODO better type name - const new_decl = try mod.createAnonymousDecl(block, .{ + const new_decl_index = try mod.createAnonymousDecl(block, .{ .ty = Type.type, .val = enum_val, }); + const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - errdefer mod.abortAnonDecl(new_decl); + errdefer mod.abortAnonDecl(new_decl_index); enum_obj.* = .{ - .owner_decl = new_decl, + .owner_decl = new_decl_index, .fields = .{}, .node_offset = 0, }; @@ -22545,7 +22542,7 @@ fn getBuiltin( const opt_builtin_inst = try sema.namespaceLookupRef( block, src, - std_file.root_decl.?.src_namespace, + mod.declPtr(std_file.root_decl.unwrap().?).src_namespace, "builtin", ); const builtin_inst = try sema.analyzeLoad(block, src, opt_builtin_inst.?, src); @@ -22984,8 +22981,7 @@ fn analyzeComptimeAlloc( // Needed to make an anon decl with type `var_type` (the `finish()` call below). _ = try sema.typeHasOnePossibleValue(block, src, var_type); - const target = sema.mod.getTarget(); - const ptr_type = try Type.ptr(sema.arena, target, .{ + const ptr_type = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = var_type, .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .global_constant), .@"align" = alignment, @@ -22994,7 +22990,7 @@ fn analyzeComptimeAlloc( var anon_decl = try block.startAnonDecl(src); defer anon_decl.deinit(); - const decl = try anon_decl.finish( + const decl_index = try anon_decl.finish( try var_type.copy(anon_decl.arena()), // There will be stores before the first load, but they may be to sub-elements or // sub-fields. So we need to initialize with undef to allow the mechanism to expand @@ -23002,12 +22998,13 @@ fn analyzeComptimeAlloc( Value.undef, alignment, ); + const decl = sema.mod.declPtr(decl_index); decl.@"align" = alignment; - try sema.mod.declareDeclDependency(sema.owner_decl, decl); + try sema.mod.declareDeclDependency(sema.owner_decl_index, decl_index); return sema.addConstant(ptr_type, try Value.Tag.decl_ref_mut.create(sema.arena, .{ .runtime_index = block.runtime_index, - .decl = decl, + .decl_index = decl_index, })); } @@ -23099,7 +23096,7 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr // The type is not in-memory coercible or the direct dereference failed, so it must // be bitcast according to the pointer type we are performing the load through. if (!load_ty.hasWellDefinedLayout()) - return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{load_ty.fmt(target)}); + return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{load_ty.fmt(sema.mod)}); const load_sz = try sema.typeAbiSize(block, src, load_ty); @@ -23114,11 +23111,11 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr if (deref.ty_without_well_defined_layout) |bad_ty| { // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem // is that some type we encountered when de-referencing does not have a well-defined layout. - return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{bad_ty.fmt(target)}); + return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{bad_ty.fmt(sema.mod)}); } else { // If all encountered types had well-defined layouts, the parent is the root decl and it just // wasn't big enough for the load. - return sema.fail(block, src, "dereference of {} exceeds bounds of containing decl of type {}", .{ ptr_ty.fmt(target), deref.parent.?.tv.ty.fmt(target) }); + return sema.fail(block, src, "dereference of {} exceeds bounds of containing decl of type {}", .{ ptr_ty.fmt(sema.mod), deref.parent.?.tv.ty.fmt(sema.mod) }); } } @@ -23484,9 +23481,8 @@ fn anonStructFieldIndex( return @intCast(u32, i); } } - const target = sema.mod.getTarget(); return sema.fail(block, field_src, "anonymous struct {} has no such field '{s}'", .{ - struct_ty.fmt(target), field_name, + struct_ty.fmt(sema.mod), field_name, }); } diff --git a/src/TypedValue.zig b/src/TypedValue.zig index b4be670e19..b0d5d77010 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -1,6 +1,7 @@ const std = @import("std"); const Type = @import("type.zig").Type; const Value = @import("value.zig").Value; +const Module = @import("Module.zig"); const Allocator = std.mem.Allocator; const TypedValue = @This(); const Target = std.Target; @@ -31,13 +32,13 @@ pub fn copy(self: TypedValue, arena: Allocator) error{OutOfMemory}!TypedValue { }; } -pub fn eql(a: TypedValue, b: TypedValue, target: std.Target) bool { - if (!a.ty.eql(b.ty, target)) return false; - return a.val.eql(b.val, a.ty, target); +pub fn eql(a: TypedValue, b: TypedValue, mod: *Module) bool { + if (!a.ty.eql(b.ty, mod)) return false; + return a.val.eql(b.val, a.ty, mod); } -pub fn hash(tv: TypedValue, hasher: *std.hash.Wyhash, target: std.Target) void { - return tv.val.hash(tv.ty, hasher, target); +pub fn hash(tv: TypedValue, hasher: *std.hash.Wyhash, mod: *Module) void { + return tv.val.hash(tv.ty, hasher, mod); } pub fn enumToInt(tv: TypedValue, buffer: *Value.Payload.U64) Value { @@ -48,7 +49,7 @@ const max_aggregate_items = 100; const FormatContext = struct { tv: TypedValue, - target: Target, + mod: *Module, }; pub fn format( @@ -59,7 +60,7 @@ pub fn format( ) !void { _ = options; comptime std.debug.assert(fmt.len == 0); - return ctx.tv.print(writer, 3, ctx.target); + return ctx.tv.print(writer, 3, ctx.mod); } /// Prints the Value according to the Type, not according to the Value Tag. @@ -67,8 +68,9 @@ pub fn print( tv: TypedValue, writer: anytype, level: u8, - target: std.Target, + mod: *Module, ) @TypeOf(writer).Error!void { + const target = mod.getTarget(); var val = tv.val; var ty = tv.ty; while (true) switch (val.tag()) { @@ -156,7 +158,7 @@ pub fn print( try print(.{ .ty = fields[i].ty, .val = vals[i], - }, writer, level - 1, target); + }, writer, level - 1, mod); } return writer.writeAll(" }"); } else { @@ -170,7 +172,7 @@ pub fn print( try print(.{ .ty = elem_ty, .val = vals[i], - }, writer, level - 1, target); + }, writer, level - 1, mod); } return writer.writeAll(" }"); } @@ -185,12 +187,12 @@ pub fn print( try print(.{ .ty = ty.unionTagType().?, .val = union_val.tag, - }, writer, level - 1, target); + }, writer, level - 1, mod); try writer.writeAll(" = "); try print(.{ - .ty = ty.unionFieldType(union_val.tag, target), + .ty = ty.unionFieldType(union_val.tag, mod), .val = union_val.val, - }, writer, level - 1, target); + }, writer, level - 1, mod); return writer.writeAll(" }"); }, @@ -205,7 +207,7 @@ pub fn print( }, .bool_true => return writer.writeAll("true"), .bool_false => return writer.writeAll("false"), - .ty => return val.castTag(.ty).?.data.print(writer, target), + .ty => return val.castTag(.ty).?.data.print(writer, mod), .int_type => { const int_type = val.castTag(.int_type).?.data; return writer.print("{s}{d}", .{ @@ -222,28 +224,32 @@ pub fn print( const x = sub_ty.abiAlignment(target); return writer.print("{d}", .{x}); }, - .function => return writer.print("(function '{s}')", .{val.castTag(.function).?.data.owner_decl.name}), + .function => return writer.print("(function '{s}')", .{ + mod.declPtr(val.castTag(.function).?.data.owner_decl).name, + }), .extern_fn => return writer.writeAll("(extern function)"), .variable => return writer.writeAll("(variable)"), .decl_ref_mut => { - const decl = val.castTag(.decl_ref_mut).?.data.decl; + const decl_index = val.castTag(.decl_ref_mut).?.data.decl_index; + const decl = mod.declPtr(decl_index); if (level == 0) { return writer.print("(decl ref mut '{s}')", .{decl.name}); } return print(.{ .ty = decl.ty, .val = decl.val, - }, writer, level - 1, target); + }, writer, level - 1, mod); }, .decl_ref => { - const decl = val.castTag(.decl_ref).?.data; + const decl_index = val.castTag(.decl_ref).?.data; + const decl = mod.declPtr(decl_index); if (level == 0) { return writer.print("(decl ref '{s}')", .{decl.name}); } return print(.{ .ty = decl.ty, .val = decl.val, - }, writer, level - 1, target); + }, writer, level - 1, mod); }, .elem_ptr => { const elem_ptr = val.castTag(.elem_ptr).?.data; @@ -251,7 +257,7 @@ pub fn print( try print(.{ .ty = elem_ptr.elem_ty, .val = elem_ptr.array_ptr, - }, writer, level - 1, target); + }, writer, level - 1, mod); return writer.print("[{}]", .{elem_ptr.index}); }, .field_ptr => { @@ -260,7 +266,7 @@ pub fn print( try print(.{ .ty = field_ptr.container_ty, .val = field_ptr.container_ptr, - }, writer, level - 1, target); + }, writer, level - 1, mod); if (field_ptr.container_ty.zigTypeTag() == .Struct) { const field_name = field_ptr.container_ty.structFields().keys()[field_ptr.field_index]; @@ -288,7 +294,7 @@ pub fn print( }; while (i < max_aggregate_items) : (i += 1) { if (i != 0) try writer.writeAll(", "); - try print(elem_tv, writer, level - 1, target); + try print(elem_tv, writer, level - 1, mod); } return writer.writeAll(" }"); }, @@ -300,7 +306,7 @@ pub fn print( try print(.{ .ty = ty.elemType2(), .val = ty.sentinel().?, - }, writer, level - 1, target); + }, writer, level - 1, mod); return writer.writeAll(" }"); }, .slice => return writer.writeAll("(slice)"), diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 688f59e804..fc37ae00dd 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -237,8 +237,10 @@ pub fn generate( @panic("Attempted to compile for architecture that was disabled by build configuration"); } - assert(module_fn.owner_decl.has_tv); - const fn_type = module_fn.owner_decl.ty; + const mod = bin_file.options.module.?; + const fn_owner_decl = mod.declPtr(module_fn.owner_decl); + assert(fn_owner_decl.has_tv); + const fn_type = fn_owner_decl.ty; var branch_stack = std.ArrayList(Branch).init(bin_file.allocator); defer { @@ -819,9 +821,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { return @as(u32, 0); } - const target = self.target.*; const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); + const mod = self.bin_file.options.module.?; + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; // TODO swap this for inst.ty.ptrAlign const abi_align = elem_ty.abiAlignment(self.target.*); @@ -830,9 +832,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const elem_ty = self.air.typeOfIndex(inst); - const target = self.target.*; const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); + const mod = self.bin_file.options.module.?; + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; const abi_align = elem_ty.abiAlignment(self.target.*); if (abi_align > self.stack_align) @@ -1422,7 +1424,7 @@ fn binOp( lhs_ty: Type, rhs_ty: Type, ) InnerError!MCValue { - const target = self.target.*; + const mod = self.bin_file.options.module.?; switch (tag) { .add, .sub, @@ -1432,7 +1434,7 @@ fn binOp( .Float => return self.fail("TODO binary operations on floats", .{}), .Vector => return self.fail("TODO binary operations on vectors", .{}), .Int => { - assert(lhs_ty.eql(rhs_ty, target)); + assert(lhs_ty.eql(rhs_ty, mod)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 64) { // Only say yes if the operation is @@ -1483,7 +1485,7 @@ fn binOp( switch (lhs_ty.zigTypeTag()) { .Vector => return self.fail("TODO binary operations on vectors", .{}), .Int => { - assert(lhs_ty.eql(rhs_ty, target)); + assert(lhs_ty.eql(rhs_ty, mod)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 64) { // TODO add optimisations for multiplication @@ -1534,7 +1536,7 @@ fn binOp( switch (lhs_ty.zigTypeTag()) { .Vector => return self.fail("TODO binary operations on vectors", .{}), .Int => { - assert(lhs_ty.eql(rhs_ty, target)); + assert(lhs_ty.eql(rhs_ty, mod)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 64) { // TODO implement bitwise operations with immediates @@ -2425,12 +2427,12 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { const ty = self.air.typeOfIndex(inst); const result = self.args[arg_index]; - const target = self.target.*; const mcv = switch (result) { // Copy registers to the stack .register => |reg| blk: { + const mod = self.bin_file.options.module.?; const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(target)}); + return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(mod)}); }; const abi_align = ty.abiAlignment(self.target.*); const stack_offset = try self.allocMem(inst, abi_size, abi_align); @@ -2537,17 +2539,19 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. // Due to incremental compilation, how function calls are generated depends // on linking. + const mod = self.bin_file.options.module.?; if (self.air.value(callee)) |func_value| { if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) { if (func_value.castTag(.function)) |func_payload| { const func = func_payload.data; const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); + const fn_owner_decl = mod.declPtr(func.owner_decl); const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; - break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes); + break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes); } else if (self.bin_file.cast(link.File.Coff)) |coff_file| - coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes + coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes else unreachable; @@ -2565,8 +2569,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { if (func_value.castTag(.function)) |func_payload| { const func = func_payload.data; + const fn_owner_decl = mod.declPtr(func.owner_decl); try self.genSetReg(Type.initTag(.u64), .x30, .{ - .got_load = func.owner_decl.link.macho.local_sym_index, + .got_load = fn_owner_decl.link.macho.local_sym_index, }); // blr x30 _ = try self.addInst(.{ @@ -2575,7 +2580,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. }); } else if (func_value.castTag(.extern_fn)) |func_payload| { const extern_fn = func_payload.data; - const decl_name = extern_fn.owner_decl.name; + const decl_name = mod.declPtr(extern_fn.owner_decl).name; if (extern_fn.lib_name) |lib_name| { log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{ decl_name, @@ -2588,7 +2593,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .tag = .call_extern, .data = .{ .extern_fn = .{ - .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index, + .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.local_sym_index, .sym_name = n_strx, }, }, @@ -2602,7 +2607,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); const got_addr = p9.bases.data; - const got_index = func_payload.data.owner_decl.link.plan9.got_index.?; + const got_index = mod.declPtr(func_payload.data.owner_decl).link.plan9.got_index.?; const fn_got_addr = got_addr + got_index * ptr_bytes; try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr }); @@ -3478,12 +3483,13 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro .direct_load => .load_memory_ptr_direct, else => unreachable, }; + const mod = self.bin_file.options.module.?; _ = try self.addInst(.{ .tag = tag, .data = .{ .payload = try self.addExtra(Mir.LoadMemoryPie{ .register = @enumToInt(src_reg), - .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index, + .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.local_sym_index, .sym_index = sym_index, }), }, @@ -3597,12 +3603,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .direct_load => .load_memory_direct, else => unreachable, }; + const mod = self.bin_file.options.module.?; _ = try self.addInst(.{ .tag = tag, .data = .{ .payload = try self.addExtra(Mir.LoadMemoryPie{ .register = @enumToInt(reg), - .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index, + .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.local_sym_index, .sym_index = sym_index, }), }, @@ -3860,7 +3867,7 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { } } -fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue { +fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue { const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); @@ -3872,7 +3879,10 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa } } - decl.alive = true; + const mod = self.bin_file.options.module.?; + const decl = mod.declPtr(decl_index); + mod.markDeclAlive(decl); + if (self.bin_file.cast(link.File.Elf)) |elf_file| { const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; @@ -3886,7 +3896,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; return MCValue{ .memory = got_addr }; } else if (self.bin_file.cast(link.File.Plan9)) |p9| { - try p9.seeDecl(decl); + try p9.seeDecl(decl_index); const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; return MCValue{ .memory = got_addr }; } else { @@ -3922,7 +3932,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { return self.lowerDeclRef(typed_value, payload.data); } if (typed_value.val.castTag(.decl_ref_mut)) |payload| { - return self.lowerDeclRef(typed_value, payload.data.decl); + return self.lowerDeclRef(typed_value, payload.data.decl_index); } const target = self.target.*; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index d39c7d9176..54de053475 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -271,8 +271,10 @@ pub fn generate( @panic("Attempted to compile for architecture that was disabled by build configuration"); } - assert(module_fn.owner_decl.has_tv); - const fn_type = module_fn.owner_decl.ty; + const mod = bin_file.options.module.?; + const fn_owner_decl = mod.declPtr(module_fn.owner_decl); + assert(fn_owner_decl.has_tv); + const fn_type = fn_owner_decl.ty; var branch_stack = std.ArrayList(Branch).init(bin_file.allocator); defer { @@ -838,9 +840,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { return @as(u32, 0); } - const target = self.target.*; const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); + const mod = self.bin_file.options.module.?; + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; // TODO swap this for inst.ty.ptrAlign const abi_align = elem_ty.abiAlignment(self.target.*); @@ -849,9 +851,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const elem_ty = self.air.typeOfIndex(inst); - const target = self.target.*; const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); + const mod = self.bin_file.options.module.?; + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; const abi_align = elem_ty.abiAlignment(self.target.*); if (abi_align > self.stack_align) @@ -1204,7 +1206,8 @@ fn minMax( .Float => return self.fail("TODO ARM min/max on floats", .{}), .Vector => return self.fail("TODO ARM min/max on vectors", .{}), .Int => { - assert(lhs_ty.eql(rhs_ty, self.target.*)); + const mod = self.bin_file.options.module.?; + assert(lhs_ty.eql(rhs_ty, mod)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 32) { const lhs_is_register = lhs == .register; @@ -1372,7 +1375,8 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { switch (lhs_ty.zigTypeTag()) { .Vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}), .Int => { - assert(lhs_ty.eql(rhs_ty, self.target.*)); + const mod = self.bin_file.options.module.?; + assert(lhs_ty.eql(rhs_ty, mod)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits < 32) { const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); @@ -1472,7 +1476,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { switch (lhs_ty.zigTypeTag()) { .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}), .Int => { - assert(lhs_ty.eql(rhs_ty, self.target.*)); + const mod = self.bin_file.options.module.?; + assert(lhs_ty.eql(rhs_ty, mod)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 16) { const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); @@ -2682,7 +2687,6 @@ fn binOp( lhs_ty: Type, rhs_ty: Type, ) InnerError!MCValue { - const target = self.target.*; switch (tag) { .add, .sub, @@ -2692,7 +2696,8 @@ fn binOp( .Float => return self.fail("TODO ARM binary operations on floats", .{}), .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), .Int => { - assert(lhs_ty.eql(rhs_ty, target)); + const mod = self.bin_file.options.module.?; + assert(lhs_ty.eql(rhs_ty, mod)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 32) { // Only say yes if the operation is @@ -2740,7 +2745,8 @@ fn binOp( .Float => return self.fail("TODO ARM binary operations on floats", .{}), .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), .Int => { - assert(lhs_ty.eql(rhs_ty, target)); + const mod = self.bin_file.options.module.?; + assert(lhs_ty.eql(rhs_ty, mod)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 32) { // TODO add optimisations for multiplication @@ -2794,7 +2800,8 @@ fn binOp( switch (lhs_ty.zigTypeTag()) { .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), .Int => { - assert(lhs_ty.eql(rhs_ty, target)); + const mod = self.bin_file.options.module.?; + assert(lhs_ty.eql(rhs_ty, mod)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 32) { const lhs_immediate_ok = lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null; @@ -3100,8 +3107,9 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) error{OutOfMemory}!void { const dbg_info = &dw.dbg_info; const index = dbg_info.items.len; try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 + const mod = self.bin_file.options.module.?; const atom = switch (self.bin_file.tag) { - .elf => &self.mod_fn.owner_decl.link.elf.dbg_info_atom, + .elf => &mod.declPtr(self.mod_fn.owner_decl).link.elf.dbg_info_atom, .macho => unreachable, else => unreachable, }; @@ -3318,11 +3326,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const func = func_payload.data; const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); + const mod = self.bin_file.options.module.?; + const fn_owner_decl = mod.declPtr(func.owner_decl); const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; - break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes); + break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes); } else if (self.bin_file.cast(link.File.Coff)) |coff_file| - coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes + coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes else unreachable; @@ -4924,11 +4934,14 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { } } -fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue { +fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue { const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); - decl.alive = true; + const mod = self.bin_file.options.module.?; + const decl = mod.declPtr(decl_index); + mod.markDeclAlive(decl); + if (self.bin_file.cast(link.File.Elf)) |elf_file| { const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; @@ -4939,7 +4952,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; return MCValue{ .memory = got_addr }; } else if (self.bin_file.cast(link.File.Plan9)) |p9| { - try p9.seeDecl(decl); + try p9.seeDecl(decl_index); const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; return MCValue{ .memory = got_addr }; } else { @@ -4976,7 +4989,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { return self.lowerDeclRef(typed_value, payload.data); } if (typed_value.val.castTag(.decl_ref_mut)) |payload| { - return self.lowerDeclRef(typed_value, payload.data.decl); + return self.lowerDeclRef(typed_value, payload.data.decl_index); } const target = self.target.*; diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index cf9e5fefcd..15377378cd 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -229,8 +229,10 @@ pub fn generate( @panic("Attempted to compile for architecture that was disabled by build configuration"); } - assert(module_fn.owner_decl.has_tv); - const fn_type = module_fn.owner_decl.ty; + const mod = bin_file.options.module.?; + const fn_owner_decl = mod.declPtr(module_fn.owner_decl); + assert(fn_owner_decl.has_tv); + const fn_type = fn_owner_decl.ty; var branch_stack = std.ArrayList(Branch).init(bin_file.allocator); defer { @@ -738,8 +740,9 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { const dbg_info = &dw.dbg_info; const index = dbg_info.items.len; try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 + const mod = self.bin_file.options.module.?; const atom = switch (self.bin_file.tag) { - .elf => &self.mod_fn.owner_decl.link.elf.dbg_info_atom, + .elf => &mod.declPtr(self.mod_fn.owner_decl).link.elf.dbg_info_atom, .macho => unreachable, else => unreachable, }; @@ -768,9 +771,9 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u /// Use a pointer instruction as the basis for allocating stack memory. fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { const elem_ty = self.air.typeOfIndex(inst).elemType(); - const target = self.target.*; const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); + const mod = self.bin_file.options.module.?; + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; // TODO swap this for inst.ty.ptrAlign const abi_align = elem_ty.abiAlignment(self.target.*); @@ -779,9 +782,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const elem_ty = self.air.typeOfIndex(inst); - const target = self.target.*; const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); + const mod = self.bin_file.options.module.?; + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; const abi_align = elem_ty.abiAlignment(self.target.*); if (abi_align > self.stack_align) @@ -1037,7 +1040,8 @@ fn binOp( .Float => return self.fail("TODO binary operations on floats", .{}), .Vector => return self.fail("TODO binary operations on vectors", .{}), .Int => { - assert(lhs_ty.eql(rhs_ty, self.target.*)); + const mod = self.bin_file.options.module.?; + assert(lhs_ty.eql(rhs_ty, mod)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 64) { // TODO immediate operands @@ -1679,11 +1683,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); + const mod = self.bin_file.options.module.?; + const fn_owner_decl = mod.declPtr(func.owner_decl); const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; - break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes); + break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes); } else if (self.bin_file.cast(link.File.Coff)) |coff_file| - coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes + coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes else unreachable; @@ -1768,7 +1774,8 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); const ty = self.air.typeOf(bin_op.lhs); - assert(ty.eql(self.air.typeOf(bin_op.rhs), self.target.*)); + const mod = self.bin_file.options.module.?; + assert(ty.eql(self.air.typeOf(bin_op.rhs), mod)); if (ty.zigTypeTag() == .ErrorSet) return self.fail("TODO implement cmp for errors", .{}); @@ -2501,10 +2508,12 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { } } -fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue { +fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue { const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); - decl.alive = true; + const mod = self.bin_file.options.module.?; + const decl = mod.declPtr(decl_index); + mod.markDeclAlive(decl); if (self.bin_file.cast(link.File.Elf)) |elf_file| { const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; @@ -2517,7 +2526,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; return MCValue{ .memory = got_addr }; } else if (self.bin_file.cast(link.File.Plan9)) |p9| { - try p9.seeDecl(decl); + try p9.seeDecl(decl_index); const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; return MCValue{ .memory = got_addr }; } else { @@ -2534,7 +2543,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { return self.lowerDeclRef(typed_value, payload.data); } if (typed_value.val.castTag(.decl_ref_mut)) |payload| { - return self.lowerDeclRef(typed_value, payload.data.decl); + return self.lowerDeclRef(typed_value, payload.data.decl_index); } const target = self.target.*; const ptr_bits = self.target.cpu.arch.ptrBitWidth(); @@ -2544,7 +2553,8 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { var buf: Type.SlicePtrFieldTypeBuffer = undefined; const ptr_type = typed_value.ty.slicePtrFieldType(&buf); const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val }); - const slice_len = typed_value.val.sliceLen(target); + const mod = self.bin_file.options.module.?; + const slice_len = typed_value.val.sliceLen(mod); // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean // the Sema code needs to use anonymous Decls or alloca instructions to store data. const ptr_imm = ptr_mcv.memory; diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 71c41bc67d..7e1ecefbb7 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -243,8 +243,10 @@ pub fn generate( @panic("Attempted to compile for architecture that was disabled by build configuration"); } - assert(module_fn.owner_decl.has_tv); - const fn_type = module_fn.owner_decl.ty; + const mod = bin_file.options.module.?; + const fn_owner_decl = mod.declPtr(module_fn.owner_decl); + assert(fn_owner_decl.has_tv); + const fn_type = fn_owner_decl.ty; var branch_stack = std.ArrayList(Branch).init(bin_file.allocator); defer { @@ -871,7 +873,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const ptr_bytes: u64 = @divExact(ptr_bits, 8); const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; - break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes); + const mod = self.bin_file.options.module.?; + break :blk @intCast(u32, got.p_vaddr + mod.declPtr(func.owner_decl).link.elf.offset_table_index * ptr_bytes); } else unreachable; try self.genSetReg(Type.initTag(.usize), .o7, .{ .memory = got_addr }); @@ -1026,9 +1029,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { return @as(u32, 0); } - const target = self.target.*; const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); + const mod = self.bin_file.options.module.?; + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; // TODO swap this for inst.ty.ptrAlign const abi_align = elem_ty.abiAlignment(self.target.*); @@ -1037,9 +1040,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const elem_ty = self.air.typeOfIndex(inst); - const target = self.target.*; const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); + const mod = self.bin_file.options.module.?; + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; const abi_align = elem_ty.abiAlignment(self.target.*); if (abi_align > self.stack_align) @@ -1372,7 +1375,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { return self.lowerDeclRef(typed_value, payload.data); } if (typed_value.val.castTag(.decl_ref_mut)) |payload| { - return self.lowerDeclRef(typed_value, payload.data.decl); + return self.lowerDeclRef(typed_value, payload.data.decl_index); } const target = self.target.*; @@ -1422,7 +1425,7 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT }; } -fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue { +fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue { const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); @@ -1434,7 +1437,10 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa } } - decl.alive = true; + const mod = self.bin_file.options.module.?; + const decl = mod.declPtr(decl_index); + + mod.markDeclAlive(decl); if (self.bin_file.cast(link.File.Elf)) |elf_file| { const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index e58a3a6d65..4586f5624b 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -538,6 +538,10 @@ const Self = @This(); /// Reference to the function declaration the code /// section belongs to decl: *Decl, +decl_index: Decl.Index, +/// Current block depth. Used to calculate the relative difference between a break +/// and block +block_depth: u32 = 0, air: Air, liveness: Liveness, gpa: mem.Allocator, @@ -559,9 +563,6 @@ local_index: u32 = 0, arg_index: u32 = 0, /// If codegen fails, an error messages will be allocated and saved in `err_msg` err_msg: *Module.ErrorMsg, -/// Current block depth. Used to calculate the relative difference between a break -/// and block -block_depth: u32 = 0, /// List of all locals' types generated throughout this declaration /// used to emit locals count at start of 'code' section. locals: std.ArrayListUnmanaged(u8), @@ -644,7 +645,7 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue { // In the other cases, we will simply lower the constant to a value that fits // into a single local (such as a pointer, integer, bool, etc). const result = if (isByRef(ty, self.target)) blk: { - const sym_index = try self.bin_file.lowerUnnamedConst(self.decl, .{ .ty = ty, .val = val }); + const sym_index = try self.bin_file.lowerUnnamedConst(.{ .ty = ty, .val = val }, self.decl_index); break :blk WValue{ .memory = sym_index }; } else try self.lowerConstant(val, ty); @@ -838,7 +839,8 @@ pub fn generate( .liveness = liveness, .values = .{}, .code = code, - .decl = func.owner_decl, + .decl_index = func.owner_decl, + .decl = bin_file.options.module.?.declPtr(func.owner_decl), .err_msg = undefined, .locals = .{}, .target = bin_file.options.target, @@ -1022,8 +1024,9 @@ fn allocStack(self: *Self, ty: Type) !WValue { } const abi_size = std.math.cast(u32, ty.abiSize(self.target)) catch { + const module = self.bin_file.base.options.module.?; return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ - ty.fmt(self.target), ty.abiSize(self.target), + ty.fmt(module), ty.abiSize(self.target), }); }; const abi_align = ty.abiAlignment(self.target); @@ -1056,8 +1059,9 @@ fn allocStackPtr(self: *Self, inst: Air.Inst.Index) !WValue { const abi_alignment = ptr_ty.ptrAlignment(self.target); const abi_size = std.math.cast(u32, pointee_ty.abiSize(self.target)) catch { + const module = self.bin_file.base.options.module.?; return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ - pointee_ty.fmt(self.target), pointee_ty.abiSize(self.target), + pointee_ty.fmt(module), pointee_ty.abiSize(self.target), }); }; if (abi_alignment > self.stack_alignment) { @@ -1542,20 +1546,21 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const ret_ty = fn_ty.fnReturnType(); const first_param_sret = isByRef(ret_ty, self.target); - const target: ?*Decl = blk: { + const callee: ?*Decl = blk: { const func_val = self.air.value(pl_op.operand) orelse break :blk null; + const module = self.bin_file.base.options.module.?; if (func_val.castTag(.function)) |func| { - break :blk func.data.owner_decl; + break :blk module.declPtr(func.data.owner_decl); } else if (func_val.castTag(.extern_fn)) |extern_fn| { - const ext_decl = extern_fn.data.owner_decl; + const ext_decl = module.declPtr(extern_fn.data.owner_decl); var func_type = try genFunctype(self.gpa, ext_decl.ty, self.target); defer func_type.deinit(self.gpa); ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); try self.bin_file.addOrUpdateImport(ext_decl); break :blk ext_decl; } else if (func_val.castTag(.decl_ref)) |decl_ref| { - break :blk decl_ref.data; + break :blk module.declPtr(decl_ref.data); } return self.fail("Expected a function, but instead found type '{s}'", .{func_val.tag()}); }; @@ -1580,7 +1585,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. } } - if (target) |direct| { + if (callee) |direct| { try self.addLabel(.call, direct.link.wasm.sym_index); } else { // in this case we call a function pointer @@ -1837,16 +1842,16 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WValue { switch (ptr_val.tag()) { .decl_ref_mut => { - const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl; - return self.lowerParentPtrDecl(ptr_val, decl); + const decl_index = ptr_val.castTag(.decl_ref_mut).?.data.decl_index; + return self.lowerParentPtrDecl(ptr_val, decl_index); }, .decl_ref => { - const decl = ptr_val.castTag(.decl_ref).?.data; - return self.lowerParentPtrDecl(ptr_val, decl); + const decl_index = ptr_val.castTag(.decl_ref).?.data; + return self.lowerParentPtrDecl(ptr_val, decl_index); }, .variable => { - const decl = ptr_val.castTag(.variable).?.data.owner_decl; - return self.lowerParentPtrDecl(ptr_val, decl); + const decl_index = ptr_val.castTag(.variable).?.data.owner_decl; + return self.lowerParentPtrDecl(ptr_val, decl_index); }, .field_ptr => { const field_ptr = ptr_val.castTag(.field_ptr).?.data; @@ -1918,24 +1923,31 @@ fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WV } } -fn lowerParentPtrDecl(self: *Self, ptr_val: Value, decl: *Module.Decl) InnerError!WValue { - decl.markAlive(); +fn lowerParentPtrDecl(self: *Self, ptr_val: Value, decl_index: Module.Decl.Index) InnerError!WValue { + const module = self.bin_file.base.options.module.?; + const decl = module.declPtr(decl_index); + module.markDeclAlive(decl); var ptr_ty_payload: Type.Payload.ElemType = .{ .base = .{ .tag = .single_mut_pointer }, .data = decl.ty, }; const ptr_ty = Type.initPayload(&ptr_ty_payload.base); - return self.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl); + return self.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl_index); } -fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!WValue { +fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!WValue { if (tv.ty.isSlice()) { - return WValue{ .memory = try self.bin_file.lowerUnnamedConst(decl, tv) }; - } else if (decl.ty.zigTypeTag() != .Fn and !decl.ty.hasRuntimeBitsIgnoreComptime()) { + return WValue{ .memory = try self.bin_file.lowerUnnamedConst(tv, decl_index) }; + } + + const module = self.bin_file.base.options.module.?; + const decl = module.declPtr(decl_index); + if (decl.ty.zigTypeTag() != .Fn and !decl.ty.hasRuntimeBitsIgnoreComptime()) { return WValue{ .imm32 = 0xaaaaaaaa }; } - decl.markAlive(); + module.markDeclAlive(decl); + const target_sym_index = decl.link.wasm.sym_index; if (decl.ty.zigTypeTag() == .Fn) { try self.bin_file.addTableFunction(target_sym_index); @@ -1946,12 +1958,12 @@ fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { if (val.isUndefDeep()) return self.emitUndefined(ty); if (val.castTag(.decl_ref)) |decl_ref| { - const decl = decl_ref.data; - return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl); + const decl_index = decl_ref.data; + return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index); } - if (val.castTag(.decl_ref_mut)) |decl_ref| { - const decl = decl_ref.data.decl; - return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl); + if (val.castTag(.decl_ref_mut)) |decl_ref_mut| { + const decl_index = decl_ref_mut.data.decl_index; + return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index); } const target = self.target; @@ -2347,8 +2359,9 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const struct_ptr = try self.resolveInst(extra.data.struct_operand); const struct_ty = self.air.typeOf(extra.data.struct_operand).childType(); const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, self.target)) catch { + const module = self.bin_file.base.options.module.?; return self.fail("Field type '{}' too big to fit into stack frame", .{ - struct_ty.structFieldType(extra.data.field_index).fmt(self.target), + struct_ty.structFieldType(extra.data.field_index).fmt(module), }); }; return self.structFieldPtr(struct_ptr, offset); @@ -2360,8 +2373,9 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr const struct_ty = self.air.typeOf(ty_op.operand).childType(); const field_ty = struct_ty.structFieldType(index); const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch { + const module = self.bin_file.base.options.module.?; return self.fail("Field type '{}' too big to fit into stack frame", .{ - field_ty.fmt(self.target), + field_ty.fmt(module), }); }; return self.structFieldPtr(struct_ptr, offset); @@ -2387,7 +2401,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const field_ty = struct_ty.structFieldType(field_index); if (!field_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} }; const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch { - return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(self.target)}); + const module = self.bin_file.base.options.module.?; + return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(module)}); }; if (isByRef(field_ty, self.target)) { @@ -2782,7 +2797,8 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue } const offset = std.math.cast(u32, opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) catch { - return self.fail("Optional type {} too big to fit into stack frame", .{opt_ty.fmt(self.target)}); + const module = self.bin_file.base.options.module.?; + return self.fail("Optional type {} too big to fit into stack frame", .{opt_ty.fmt(module)}); }; try self.emitWValue(operand); @@ -2811,7 +2827,8 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return operand; } const offset = std.math.cast(u32, op_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) catch { - return self.fail("Optional type {} too big to fit into stack frame", .{op_ty.fmt(self.target)}); + const module = self.bin_file.base.options.module.?; + return self.fail("Optional type {} too big to fit into stack frame", .{op_ty.fmt(module)}); }; // Create optional type, set the non-null bit, and store the operand inside the optional type diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index f2eb1e2afd..4097352975 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -309,8 +309,10 @@ pub fn generate( @panic("Attempted to compile for architecture that was disabled by build configuration"); } - assert(module_fn.owner_decl.has_tv); - const fn_type = module_fn.owner_decl.ty; + const mod = bin_file.options.module.?; + const fn_owner_decl = mod.declPtr(module_fn.owner_decl); + assert(fn_owner_decl.has_tv); + const fn_type = fn_owner_decl.ty; var branch_stack = std.ArrayList(Branch).init(bin_file.allocator); defer { @@ -396,14 +398,14 @@ pub fn generate( if (builtin.mode == .Debug and bin_file.options.module.?.comp.verbose_mir) { const w = std.io.getStdErr().writer(); - w.print("# Begin Function MIR: {s}:\n", .{module_fn.owner_decl.name}) catch {}; + w.print("# Begin Function MIR: {s}:\n", .{fn_owner_decl.name}) catch {}; const PrintMir = @import("PrintMir.zig"); const print = PrintMir{ .mir = mir, .bin_file = bin_file, }; print.printMir(w, function.mir_to_air_map, air) catch {}; // we don't care if the debug printing fails - w.print("# End Function MIR: {s}\n\n", .{module_fn.owner_decl.name}) catch {}; + w.print("# End Function MIR: {s}\n\n", .{fn_owner_decl.name}) catch {}; } if (function.err_msg) |em| { @@ -915,9 +917,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { return self.allocMem(inst, @sizeOf(usize), @alignOf(usize)); } - const target = self.target.*; const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); + const mod = self.bin_file.options.module.?; + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; // TODO swap this for inst.ty.ptrAlign const abi_align = ptr_ty.ptrAlignment(self.target.*); @@ -926,9 +928,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const elem_ty = self.air.typeOfIndex(inst); - const target = self.target.*; const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { - return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); + const mod = self.bin_file.options.module.?; + return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; const abi_align = elem_ty.abiAlignment(self.target.*); if (abi_align > self.stack_align) @@ -2650,6 +2652,8 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue .direct_load => 0b01, else => unreachable, }; + const mod = self.bin_file.options.module.?; + const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl); _ = try self.addInst(.{ .tag = .lea_pie, .ops = (Mir.Ops{ @@ -2658,7 +2662,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue }).encode(), .data = .{ .load_reloc = .{ - .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index, + .atom_index = fn_owner_decl.link.macho.local_sym_index, .sym_index = sym_index, }, }, @@ -3583,17 +3587,19 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. // Due to incremental compilation, how function calls are generated depends // on linking. + const mod = self.bin_file.options.module.?; if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) { if (self.air.value(callee)) |func_value| { if (func_value.castTag(.function)) |func_payload| { const func = func_payload.data; const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); + const fn_owner_decl = mod.declPtr(func.owner_decl); const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; - break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes); + break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes); } else if (self.bin_file.cast(link.File.Coff)) |coff_file| - @intCast(u32, coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes) + @intCast(u32, coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes) else unreachable; _ = try self.addInst(.{ @@ -3625,8 +3631,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. if (self.air.value(callee)) |func_value| { if (func_value.castTag(.function)) |func_payload| { const func = func_payload.data; + const fn_owner_decl = mod.declPtr(func.owner_decl); try self.genSetReg(Type.initTag(.usize), .rax, .{ - .got_load = func.owner_decl.link.macho.local_sym_index, + .got_load = fn_owner_decl.link.macho.local_sym_index, }); // callq *%rax _ = try self.addInst(.{ @@ -3639,7 +3646,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. }); } else if (func_value.castTag(.extern_fn)) |func_payload| { const extern_fn = func_payload.data; - const decl_name = extern_fn.owner_decl.name; + const decl_name = mod.declPtr(extern_fn.owner_decl).name; if (extern_fn.lib_name) |lib_name| { log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{ decl_name, @@ -3652,7 +3659,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .ops = undefined, .data = .{ .extern_fn = .{ - .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index, + .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.local_sym_index, .sym_name = n_strx, }, }, @@ -3680,7 +3687,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); const got_addr = p9.bases.data; - const got_index = func_payload.data.owner_decl.link.plan9.got_index.?; + const got_index = mod.declPtr(func_payload.data.owner_decl).link.plan9.got_index.?; const fn_got_addr = got_addr + got_index * ptr_bytes; _ = try self.addInst(.{ .tag = .call, @@ -4012,9 +4019,11 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { const dbg_info = &dw.dbg_info; const index = dbg_info.items.len; try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 + const mod = self.bin_file.options.module.?; + const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl); const atom = switch (self.bin_file.tag) { - .elf => &self.mod_fn.owner_decl.link.elf.dbg_info_atom, - .macho => &self.mod_fn.owner_decl.link.macho.dbg_info_atom, + .elf => &fn_owner_decl.link.elf.dbg_info_atom, + .macho => &fn_owner_decl.link.macho.dbg_info_atom, else => unreachable, }; try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); @@ -6124,7 +6133,7 @@ fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCV return mcv; } -fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue { +fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue { log.debug("lowerDeclRef: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() }); const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); @@ -6137,7 +6146,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa } } - decl.markAlive(); + const module = self.bin_file.options.module.?; + const decl = module.declPtr(decl_index); + module.markDeclAlive(decl); if (self.bin_file.cast(link.File.Elf)) |elf_file| { const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; @@ -6152,7 +6163,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; return MCValue{ .memory = got_addr }; } else if (self.bin_file.cast(link.File.Plan9)) |p9| { - try p9.seeDecl(decl); + try p9.seeDecl(decl_index); const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; return MCValue{ .memory = got_addr }; } else { @@ -6189,7 +6200,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { return self.lowerDeclRef(typed_value, payload.data); } if (typed_value.val.castTag(.decl_ref_mut)) |payload| { - return self.lowerDeclRef(typed_value, payload.data.decl); + return self.lowerDeclRef(typed_value, payload.data.decl_index); } const target = self.target.*; diff --git a/src/codegen.zig b/src/codegen.zig index 7fa0b2c0f8..debd7b5e9d 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -347,7 +347,9 @@ pub fn generateSymbol( switch (container_ptr.tag()) { .decl_ref => { - const decl = container_ptr.castTag(.decl_ref).?.data; + const decl_index = container_ptr.castTag(.decl_ref).?.data; + const mod = bin_file.options.module.?; + const decl = mod.declPtr(decl_index); const addend = blk: { switch (decl.ty.tag()) { .@"struct" => { @@ -364,7 +366,7 @@ pub fn generateSymbol( }, } }; - return lowerDeclRef(bin_file, src_loc, typed_value, decl, code, debug_output, .{ + return lowerDeclRef(bin_file, src_loc, typed_value, decl_index, code, debug_output, .{ .parent_atom_index = reloc_info.parent_atom_index, .addend = (reloc_info.addend orelse 0) + addend, }); @@ -400,8 +402,8 @@ pub fn generateSymbol( switch (array_ptr.tag()) { .decl_ref => { - const decl = array_ptr.castTag(.decl_ref).?.data; - return lowerDeclRef(bin_file, src_loc, typed_value, decl, code, debug_output, .{ + const decl_index = array_ptr.castTag(.decl_ref).?.data; + return lowerDeclRef(bin_file, src_loc, typed_value, decl_index, code, debug_output, .{ .parent_atom_index = reloc_info.parent_atom_index, .addend = (reloc_info.addend orelse 0) + addend, }); @@ -589,7 +591,8 @@ pub fn generateSymbol( } const union_ty = typed_value.ty.cast(Type.Payload.Union).?.data; - const field_index = union_ty.tag_ty.enumTagFieldIndex(union_obj.tag, target).?; + const mod = bin_file.options.module.?; + const field_index = union_ty.tag_ty.enumTagFieldIndex(union_obj.tag, mod).?; assert(union_ty.haveFieldTypes()); const field_ty = union_ty.fields.values()[field_index].ty; if (!field_ty.hasRuntimeBits()) { @@ -772,12 +775,13 @@ fn lowerDeclRef( bin_file: *link.File, src_loc: Module.SrcLoc, typed_value: TypedValue, - decl: *Module.Decl, + decl_index: Module.Decl.Index, code: *std.ArrayList(u8), debug_output: DebugInfoOutput, reloc_info: RelocInfo, ) GenerateSymbolError!Result { const target = bin_file.options.target; + const module = bin_file.options.module.?; if (typed_value.ty.isSlice()) { // generate ptr var buf: Type.SlicePtrFieldTypeBuffer = undefined; @@ -796,7 +800,7 @@ fn lowerDeclRef( // generate length var slice_len: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, - .data = typed_value.val.sliceLen(target), + .data = typed_value.val.sliceLen(module), }; switch (try generateSymbol(bin_file, src_loc, .{ .ty = Type.usize, @@ -813,14 +817,16 @@ fn lowerDeclRef( } const ptr_width = target.cpu.arch.ptrBitWidth(); + const decl = module.declPtr(decl_index); const is_fn_body = decl.ty.zigTypeTag() == .Fn; if (!is_fn_body and !decl.ty.hasRuntimeBits()) { try code.writer().writeByteNTimes(0xaa, @divExact(ptr_width, 8)); return Result{ .appended = {} }; } - decl.markAlive(); - const vaddr = try bin_file.getDeclVAddr(decl, .{ + module.markDeclAlive(decl); + + const vaddr = try bin_file.getDeclVAddr(decl_index, .{ .parent_atom_index = reloc_info.parent_atom_index, .offset = code.items.len, .addend = reloc_info.addend orelse 0, diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 54f8285291..a0b1bc30b9 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -32,8 +32,8 @@ pub const CValue = union(enum) { /// Index into the parameters arg: usize, /// By-value - decl: *Decl, - decl_ref: *Decl, + decl: Decl.Index, + decl_ref: Decl.Index, /// An undefined (void *) pointer (cannot be dereferenced) undefined_ptr: void, /// Render the slice as an identifier (using fmtIdent) @@ -58,7 +58,7 @@ pub const TypedefMap = std.ArrayHashMap( const FormatTypeAsCIdentContext = struct { ty: Type, - target: std.Target, + mod: *Module, }; /// TODO make this not cut off at 128 bytes @@ -71,14 +71,14 @@ fn formatTypeAsCIdentifier( _ = fmt; _ = options; var buffer = [1]u8{0} ** 128; - var buf = std.fmt.bufPrint(&buffer, "{}", .{data.ty.fmt(data.target)}) catch &buffer; + var buf = std.fmt.bufPrint(&buffer, "{}", .{data.ty.fmt(data.mod)}) catch &buffer; return formatIdent(buf, "", .{}, writer); } -pub fn typeToCIdentifier(ty: Type, target: std.Target) std.fmt.Formatter(formatTypeAsCIdentifier) { +pub fn typeToCIdentifier(ty: Type, mod: *Module) std.fmt.Formatter(formatTypeAsCIdentifier) { return .{ .data = .{ .ty = ty, - .target = target, + .mod = mod, } }; } @@ -349,6 +349,7 @@ pub const DeclGen = struct { gpa: std.mem.Allocator, module: *Module, decl: *Decl, + decl_index: Decl.Index, fwd_decl: std.ArrayList(u8), error_msg: ?*Module.ErrorMsg, /// The key of this map is Type which has references to typedefs_arena. @@ -376,10 +377,8 @@ pub const DeclGen = struct { writer: anytype, ty: Type, val: Value, - decl: *Decl, + decl_index: Decl.Index, ) error{ OutOfMemory, AnalysisFail }!void { - const target = dg.module.getTarget(); - if (ty.isSlice()) { try writer.writeByte('('); try dg.renderTypecast(writer, ty); @@ -387,11 +386,12 @@ pub const DeclGen = struct { var buf: Type.SlicePtrFieldTypeBuffer = undefined; try dg.renderValue(writer, ty.slicePtrFieldType(&buf), val.slicePtr()); try writer.writeAll(", "); - try writer.print("{d}", .{val.sliceLen(target)}); + try writer.print("{d}", .{val.sliceLen(dg.module)}); try writer.writeAll("}"); return; } + const decl = dg.module.declPtr(decl_index); assert(decl.has_tv); // We shouldn't cast C function pointers as this is UB (when you call // them). The analysis until now should ensure that the C function @@ -399,21 +399,21 @@ pub const DeclGen = struct { // somewhere and we should let the C compiler tell us about it. if (ty.castPtrToFn() == null) { // Determine if we must pointer cast. - if (ty.eql(decl.ty, target)) { + if (ty.eql(decl.ty, dg.module)) { try writer.writeByte('&'); - try dg.renderDeclName(writer, decl); + try dg.renderDeclName(writer, decl_index); return; } try writer.writeAll("(("); try dg.renderTypecast(writer, ty); try writer.writeAll(")&"); - try dg.renderDeclName(writer, decl); + try dg.renderDeclName(writer, decl_index); try writer.writeByte(')'); return; } - try dg.renderDeclName(writer, decl); + try dg.renderDeclName(writer, decl_index); } fn renderInt128( @@ -471,13 +471,13 @@ pub const DeclGen = struct { try writer.writeByte(')'); switch (ptr_val.tag()) { .decl_ref_mut, .decl_ref, .variable => { - const decl = switch (ptr_val.tag()) { + const decl_index = switch (ptr_val.tag()) { .decl_ref => ptr_val.castTag(.decl_ref).?.data, - .decl_ref_mut => ptr_val.castTag(.decl_ref_mut).?.data.decl, + .decl_ref_mut => ptr_val.castTag(.decl_ref_mut).?.data.decl_index, .variable => ptr_val.castTag(.variable).?.data.owner_decl, else => unreachable, }; - try dg.renderDeclValue(writer, ptr_ty, ptr_val, decl); + try dg.renderDeclValue(writer, ptr_ty, ptr_val, decl_index); }, .field_ptr => { const field_ptr = ptr_val.castTag(.field_ptr).?.data; @@ -685,7 +685,7 @@ pub const DeclGen = struct { var index: usize = 0; while (index < ai.len) : (index += 1) { if (index != 0) try writer.writeAll(","); - const elem_val = try val.elemValue(arena_allocator, index); + const elem_val = try val.elemValue(dg.module, arena_allocator, index); try dg.renderValue(writer, ai.elem_type, elem_val); } if (ai.sentinel) |s| { @@ -837,7 +837,7 @@ pub const DeclGen = struct { try writer.writeAll(".payload = {"); } - const index = union_ty.tag_ty.enumTagFieldIndex(union_obj.tag, target).?; + const index = union_ty.tag_ty.enumTagFieldIndex(union_obj.tag, dg.module).?; const field_ty = ty.unionFields().values()[index].ty; const field_name = ty.unionFields().keys()[index]; if (field_ty.hasRuntimeBits()) { @@ -889,7 +889,7 @@ pub const DeclGen = struct { try w.writeAll("void"); } try w.writeAll(" "); - try dg.renderDeclName(w, dg.decl); + try dg.renderDeclName(w, dg.decl_index); try w.writeAll("("); const param_len = dg.decl.ty.fnParamLen(); @@ -927,8 +927,7 @@ pub const DeclGen = struct { try bw.writeAll(" (*"); const name_start = buffer.items.len; - const target = dg.module.getTarget(); - try bw.print("zig_F_{s})(", .{typeToCIdentifier(t, target)}); + try bw.print("zig_F_{s})(", .{typeToCIdentifier(t, dg.module)}); const name_end = buffer.items.len - 2; const param_len = fn_info.param_types.len; @@ -982,11 +981,10 @@ pub const DeclGen = struct { try bw.writeAll("; size_t len; } "); const name_index = buffer.items.len; - const target = dg.module.getTarget(); if (t.isConstPtr()) { - try bw.print("zig_L_{s}", .{typeToCIdentifier(child_type, target)}); + try bw.print("zig_L_{s}", .{typeToCIdentifier(child_type, dg.module)}); } else { - try bw.print("zig_M_{s}", .{typeToCIdentifier(child_type, target)}); + try bw.print("zig_M_{s}", .{typeToCIdentifier(child_type, dg.module)}); } if (ptr_sentinel) |s| { try bw.writeAll("_s_"); @@ -1009,7 +1007,7 @@ pub const DeclGen = struct { fn renderStructTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { const struct_obj = t.castTag(.@"struct").?.data; // Handle 0 bit types elsewhere. - const fqn = try struct_obj.getFullyQualifiedName(dg.typedefs.allocator); + const fqn = try struct_obj.getFullyQualifiedName(dg.module); defer dg.typedefs.allocator.free(fqn); var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); @@ -1072,8 +1070,7 @@ pub const DeclGen = struct { try buffer.appendSlice("} "); const name_start = buffer.items.len; - const target = dg.module.getTarget(); - try writer.print("zig_T_{};\n", .{typeToCIdentifier(t, target)}); + try writer.print("zig_T_{};\n", .{typeToCIdentifier(t, dg.module)}); const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); @@ -1090,7 +1087,7 @@ pub const DeclGen = struct { fn renderUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { const union_ty = t.cast(Type.Payload.Union).?.data; - const fqn = try union_ty.getFullyQualifiedName(dg.typedefs.allocator); + const fqn = try union_ty.getFullyQualifiedName(dg.module); defer dg.typedefs.allocator.free(fqn); const target = dg.module.getTarget(); @@ -1157,7 +1154,6 @@ pub const DeclGen = struct { try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, 0); try bw.writeAll("; uint16_t error; } "); const name_index = buffer.items.len; - const target = dg.module.getTarget(); if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| { const func = inf_err_set_payload.data.func; try bw.writeAll("zig_E_"); @@ -1165,7 +1161,7 @@ pub const DeclGen = struct { try bw.writeAll(";\n"); } else { try bw.print("zig_E_{s}_{s};\n", .{ - typeToCIdentifier(err_set_type, target), typeToCIdentifier(child_type, target), + typeToCIdentifier(err_set_type, dg.module), typeToCIdentifier(child_type, dg.module), }); } @@ -1195,8 +1191,7 @@ pub const DeclGen = struct { try dg.renderType(bw, elem_type); const name_start = buffer.items.len + 1; - const target = dg.module.getTarget(); - try bw.print(" zig_A_{s}_{d}", .{ typeToCIdentifier(elem_type, target), c_len }); + try bw.print(" zig_A_{s}_{d}", .{ typeToCIdentifier(elem_type, dg.module), c_len }); const name_end = buffer.items.len; try bw.print("[{d}];\n", .{c_len}); @@ -1224,8 +1219,7 @@ pub const DeclGen = struct { try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, 0); try bw.writeAll("; bool is_null; } "); const name_index = buffer.items.len; - const target = dg.module.getTarget(); - try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type, target)}); + try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type, dg.module)}); const rendered = buffer.toOwnedSlice(); errdefer dg.typedefs.allocator.free(rendered); @@ -1535,16 +1529,17 @@ pub const DeclGen = struct { } } - fn renderDeclName(dg: DeclGen, writer: anytype, decl: *Decl) !void { - decl.markAlive(); + fn renderDeclName(dg: DeclGen, writer: anytype, decl_index: Decl.Index) !void { + const decl = dg.module.declPtr(decl_index); + dg.module.markDeclAlive(decl); - if (dg.module.decl_exports.get(decl)) |exports| { + if (dg.module.decl_exports.get(decl_index)) |exports| { return writer.writeAll(exports[0].options.name); } else if (decl.val.tag() == .extern_fn) { return writer.writeAll(mem.sliceTo(decl.name, 0)); } else { const gpa = dg.module.gpa; - const name = try decl.getFullyQualifiedName(gpa); + const name = try decl.getFullyQualifiedName(dg.module); defer gpa.free(name); return writer.print("{ }", .{fmtIdent(name)}); } @@ -1616,7 +1611,11 @@ pub fn genDecl(o: *Object) !void { try fwd_decl_writer.writeAll("zig_threadlocal "); } - const decl_c_value: CValue = if (is_global) .{ .bytes = mem.span(o.dg.decl.name) } else .{ .decl = o.dg.decl }; + const decl_c_value: CValue = if (is_global) .{ + .bytes = mem.span(o.dg.decl.name), + } else .{ + .decl = o.dg.decl_index, + }; try o.dg.renderTypeAndName(fwd_decl_writer, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align"); try fwd_decl_writer.writeAll(";\n"); @@ -1641,7 +1640,7 @@ pub fn genDecl(o: *Object) !void { // TODO ask the Decl if it is const // https://github.com/ziglang/zig/issues/7582 - const decl_c_value: CValue = .{ .decl = o.dg.decl }; + const decl_c_value: CValue = .{ .decl = o.dg.decl_index }; try o.dg.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut, o.dg.decl.@"align"); try writer.writeAll(" = "); @@ -2234,13 +2233,12 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { if (src_val_is_undefined) return try airStoreUndefined(f, dest_ptr); - const target = f.object.dg.module.getTarget(); const writer = f.object.writer(); if (lhs_child_type.zigTypeTag() == .Array) { // For this memcpy to safely work we need the rhs to have the same // underlying type as the lhs (i.e. they must both be arrays of the same underlying type). const rhs_type = f.air.typeOf(bin_op.rhs); - assert(rhs_type.eql(lhs_child_type, target)); + assert(rhs_type.eql(lhs_child_type, f.object.dg.module)); // If the source is a constant, writeCValue will emit a brace initialization // so work around this by initializing into new local. @@ -2780,7 +2778,8 @@ fn airDbgInline(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const writer = f.object.writer(); const function = f.air.values[ty_pl.payload].castTag(.function).?.data; - try writer.print("/* dbg func:{s} */\n", .{function.owner_decl.name}); + const mod = f.object.dg.module; + try writer.print("/* dbg func:{s} */\n", .{mod.declPtr(function.owner_decl).name}); return CValue.none; } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index cdb9addcff..ba637cb99e 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -161,6 +161,7 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![:0]u8 { pub const Object = struct { gpa: Allocator, + module: *Module, llvm_module: *const llvm.Module, di_builder: ?*llvm.DIBuilder, /// One of these mappings: @@ -181,7 +182,7 @@ pub const Object = struct { /// version of the name and incorrectly get function not found in the llvm module. /// * it works for functions not all globals. /// Therefore, this table keeps track of the mapping. - decl_map: std.AutoHashMapUnmanaged(*const Module.Decl, *const llvm.Value), + decl_map: std.AutoHashMapUnmanaged(Module.Decl.Index, *const llvm.Value), /// Maps Zig types to LLVM types. The table memory itself is backed by the GPA of /// the compiler, but the Type/Value memory here is backed by `type_map_arena`. /// TODO we need to remove entries from this map in response to incremental compilation @@ -340,6 +341,7 @@ pub const Object = struct { return Object{ .gpa = gpa, + .module = options.module.?, .llvm_module = llvm_module, .di_map = .{}, .di_builder = opt_di_builder, @@ -568,18 +570,20 @@ pub const Object = struct { air: Air, liveness: Liveness, ) !void { - const decl = func.owner_decl; + const decl_index = func.owner_decl; + const decl = module.declPtr(decl_index); var dg: DeclGen = .{ .context = o.context, .object = o, .module = module, + .decl_index = decl_index, .decl = decl, .err_msg = null, .gpa = module.gpa, }; - const llvm_func = try dg.resolveLlvmFunction(decl); + const llvm_func = try dg.resolveLlvmFunction(decl_index); if (module.align_stack_fns.get(func)) |align_info| { dg.addFnAttrInt(llvm_func, "alignstack", align_info.alignment); @@ -632,7 +636,7 @@ pub const Object = struct { const line_number = decl.src_line + 1; const is_internal_linkage = decl.val.tag() != .extern_fn and - !dg.module.decl_exports.contains(decl); + !dg.module.decl_exports.contains(decl_index); const noret_bit: c_uint = if (fn_info.return_type.isNoReturn()) llvm.DIFlags.NoReturn else @@ -684,48 +688,51 @@ pub const Object = struct { fg.genBody(air.getMainBody()) catch |err| switch (err) { error.CodegenFail => { decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, dg.err_msg.?); + try module.failed_decls.put(module.gpa, decl_index, dg.err_msg.?); dg.err_msg = null; return; }, else => |e| return e, }; - const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; - try o.updateDeclExports(module, decl, decl_exports); + const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{}; + try o.updateDeclExports(module, decl_index, decl_exports); } - pub fn updateDecl(self: *Object, module: *Module, decl: *Module.Decl) !void { + pub fn updateDecl(self: *Object, module: *Module, decl_index: Module.Decl.Index) !void { + const decl = module.declPtr(decl_index); var dg: DeclGen = .{ .context = self.context, .object = self, .module = module, .decl = decl, + .decl_index = decl_index, .err_msg = null, .gpa = module.gpa, }; dg.genDecl() catch |err| switch (err) { error.CodegenFail => { decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, dg.err_msg.?); + try module.failed_decls.put(module.gpa, decl_index, dg.err_msg.?); dg.err_msg = null; return; }, else => |e| return e, }; - const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; - try self.updateDeclExports(module, decl, decl_exports); + const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{}; + try self.updateDeclExports(module, decl_index, decl_exports); } pub fn updateDeclExports( self: *Object, - module: *const Module, - decl: *const Module.Decl, + module: *Module, + decl_index: Module.Decl.Index, exports: []const *Module.Export, ) !void { // If the module does not already have the function, we ignore this function call // because we call `updateDeclExports` at the end of `updateFunc` and `updateDecl`. - const llvm_global = self.decl_map.get(decl) orelse return; + const llvm_global = self.decl_map.get(decl_index) orelse return; + const decl = module.declPtr(decl_index); if (decl.isExtern()) { llvm_global.setValueName(decl.name); llvm_global.setUnnamedAddr(.False); @@ -798,7 +805,7 @@ pub const Object = struct { } } } else { - const fqn = try decl.getFullyQualifiedName(module.gpa); + const fqn = try decl.getFullyQualifiedName(module); defer module.gpa.free(fqn); llvm_global.setValueName2(fqn.ptr, fqn.len); llvm_global.setLinkage(.Internal); @@ -814,8 +821,8 @@ pub const Object = struct { } } - pub fn freeDecl(self: *Object, decl: *Module.Decl) void { - const llvm_value = self.decl_map.get(decl) orelse return; + pub fn freeDecl(self: *Object, decl_index: Module.Decl.Index) void { + const llvm_value = self.decl_map.get(decl_index) orelse return; llvm_value.deleteGlobal(); } @@ -847,7 +854,7 @@ pub const Object = struct { const gpa = o.gpa; // Be careful not to reference this `gop` variable after any recursive calls // to `lowerDebugType`. - const gop = try o.di_type_map.getOrPutContext(gpa, ty, .{ .target = o.target }); + const gop = try o.di_type_map.getOrPutContext(gpa, ty, .{ .mod = o.module }); if (gop.found_existing) { const annotated = gop.value_ptr.*; const di_type = annotated.toDIType(); @@ -860,7 +867,7 @@ pub const Object = struct { }; return o.lowerDebugTypeImpl(entry, resolve, di_type); } - errdefer assert(o.di_type_map.orderedRemoveContext(ty, .{ .target = o.target })); + errdefer assert(o.di_type_map.orderedRemoveContext(ty, .{ .mod = o.module })); // The Type memory is ephemeral; since we want to store a longer-lived // reference, we need to copy it here. gop.key_ptr.* = try ty.copy(o.type_map_arena.allocator()); @@ -891,7 +898,7 @@ pub const Object = struct { .Int => { const info = ty.intInfo(target); assert(info.bits != 0); - const name = try ty.nameAlloc(gpa, target); + const name = try ty.nameAlloc(gpa, o.module); defer gpa.free(name); const dwarf_encoding: c_uint = switch (info.signedness) { .signed => DW.ATE.signed, @@ -902,13 +909,14 @@ pub const Object = struct { return di_type; }, .Enum => { - const owner_decl = ty.getOwnerDecl(); + const owner_decl_index = ty.getOwnerDecl(); + const owner_decl = o.module.declPtr(owner_decl_index); if (!ty.hasRuntimeBitsIgnoreComptime()) { - const enum_di_ty = try o.makeEmptyNamespaceDIType(owner_decl); + const enum_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index); // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` // means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(enum_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(enum_di_ty), .{ .mod = o.module }); return enum_di_ty; } @@ -938,7 +946,7 @@ pub const Object = struct { const di_file = try o.getDIFile(gpa, owner_decl.src_namespace.file_scope); const di_scope = try o.namespaceToDebugScope(owner_decl.src_namespace); - const name = try ty.nameAlloc(gpa, target); + const name = try ty.nameAlloc(gpa, o.module); defer gpa.free(name); var buffer: Type.Payload.Bits = undefined; const int_ty = ty.intTagType(&buffer); @@ -956,12 +964,12 @@ pub const Object = struct { "", ); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(enum_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(enum_di_ty), .{ .mod = o.module }); return enum_di_ty; }, .Float => { const bits = ty.floatBits(target); - const name = try ty.nameAlloc(gpa, target); + const name = try ty.nameAlloc(gpa, o.module); defer gpa.free(name); const di_type = dib.createBasicType(name, bits, DW.ATE.float); gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_type); @@ -1009,7 +1017,7 @@ pub const Object = struct { const bland_ptr_ty = Type.initPayload(&payload.base); const ptr_di_ty = try o.lowerDebugType(bland_ptr_ty, resolve); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.init(ptr_di_ty, resolve), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.init(ptr_di_ty, resolve), .{ .mod = o.module }); return ptr_di_ty; } @@ -1018,7 +1026,7 @@ pub const Object = struct { const ptr_ty = ty.slicePtrFieldType(&buf); const len_ty = Type.usize; - const name = try ty.nameAlloc(gpa, target); + const name = try ty.nameAlloc(gpa, o.module); defer gpa.free(name); const di_file: ?*llvm.DIFile = null; const line = 0; @@ -1089,12 +1097,12 @@ pub const Object = struct { ); dib.replaceTemporary(fwd_decl, full_di_ty); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module }); return full_di_ty; } const elem_di_ty = try o.lowerDebugType(ptr_info.pointee_type, .fwd); - const name = try ty.nameAlloc(gpa, target); + const name = try ty.nameAlloc(gpa, o.module); defer gpa.free(name); const ptr_di_ty = dib.createPointerType( elem_di_ty, @@ -1103,7 +1111,7 @@ pub const Object = struct { name, ); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .mod = o.module }); return ptr_di_ty; }, .Opaque => { @@ -1112,9 +1120,10 @@ pub const Object = struct { gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_ty); return di_ty; } - const name = try ty.nameAlloc(gpa, target); + const name = try ty.nameAlloc(gpa, o.module); defer gpa.free(name); - const owner_decl = ty.getOwnerDecl(); + const owner_decl_index = ty.getOwnerDecl(); + const owner_decl = o.module.declPtr(owner_decl_index); const opaque_di_ty = dib.createForwardDeclType( DW.TAG.structure_type, name, @@ -1124,7 +1133,7 @@ pub const Object = struct { ); // The recursive call to `lowerDebugType` va `namespaceToDebugScope` // means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(opaque_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(opaque_di_ty), .{ .mod = o.module }); return opaque_di_ty; }, .Array => { @@ -1135,7 +1144,7 @@ pub const Object = struct { @intCast(c_int, ty.arrayLen()), ); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(array_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(array_di_ty), .{ .mod = o.module }); return array_di_ty; }, .Vector => { @@ -1146,11 +1155,11 @@ pub const Object = struct { ty.vectorLen(), ); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(vector_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(vector_di_ty), .{ .mod = o.module }); return vector_di_ty; }, .Optional => { - const name = try ty.nameAlloc(gpa, target); + const name = try ty.nameAlloc(gpa, o.module); defer gpa.free(name); var buf: Type.Payload.ElemType = undefined; const child_ty = ty.optionalChild(&buf); @@ -1162,7 +1171,7 @@ pub const Object = struct { if (ty.isPtrLikeOptional()) { const ptr_di_ty = try o.lowerDebugType(child_ty, resolve); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .mod = o.module }); return ptr_di_ty; } @@ -1235,7 +1244,7 @@ pub const Object = struct { ); dib.replaceTemporary(fwd_decl, full_di_ty); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module }); return full_di_ty; }, .ErrorUnion => { @@ -1244,10 +1253,10 @@ pub const Object = struct { if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { const err_set_di_ty = try o.lowerDebugType(err_set_ty, .full); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(err_set_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(err_set_di_ty), .{ .mod = o.module }); return err_set_di_ty; } - const name = try ty.nameAlloc(gpa, target); + const name = try ty.nameAlloc(gpa, o.module); defer gpa.free(name); const di_file: ?*llvm.DIFile = null; const line = 0; @@ -1332,7 +1341,7 @@ pub const Object = struct { ); dib.replaceTemporary(fwd_decl, full_di_ty); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module }); return full_di_ty; }, .ErrorSet => { @@ -1344,7 +1353,7 @@ pub const Object = struct { }, .Struct => { const compile_unit_scope = o.di_compile_unit.?.toScope(); - const name = try ty.nameAlloc(gpa, target); + const name = try ty.nameAlloc(gpa, o.module); defer gpa.free(name); if (ty.castTag(.@"struct")) |payload| { @@ -1431,7 +1440,7 @@ pub const Object = struct { ); dib.replaceTemporary(fwd_decl, full_di_ty); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module }); return full_di_ty; } @@ -1445,23 +1454,23 @@ pub const Object = struct { // into. Therefore we can satisfy this by making an empty namespace, // rather than changing the frontend to unnecessarily resolve the // struct field types. - const owner_decl = ty.getOwnerDecl(); - const struct_di_ty = try o.makeEmptyNamespaceDIType(owner_decl); + const owner_decl_index = ty.getOwnerDecl(); + const struct_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index); dib.replaceTemporary(fwd_decl, struct_di_ty); // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` // means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(struct_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(struct_di_ty), .{ .mod = o.module }); return struct_di_ty; } } if (!ty.hasRuntimeBitsIgnoreComptime()) { - const owner_decl = ty.getOwnerDecl(); - const struct_di_ty = try o.makeEmptyNamespaceDIType(owner_decl); + const owner_decl_index = ty.getOwnerDecl(); + const struct_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index); dib.replaceTemporary(fwd_decl, struct_di_ty); // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` // means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(struct_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(struct_di_ty), .{ .mod = o.module }); return struct_di_ty; } @@ -1516,14 +1525,14 @@ pub const Object = struct { ); dib.replaceTemporary(fwd_decl, full_di_ty); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module }); return full_di_ty; }, .Union => { const compile_unit_scope = o.di_compile_unit.?.toScope(); - const owner_decl = ty.getOwnerDecl(); + const owner_decl_index = ty.getOwnerDecl(); - const name = try ty.nameAlloc(gpa, target); + const name = try ty.nameAlloc(gpa, o.module); defer gpa.free(name); const fwd_decl = opt_fwd_decl orelse blk: { @@ -1540,11 +1549,11 @@ pub const Object = struct { }; if (!ty.hasRuntimeBitsIgnoreComptime()) { - const union_di_ty = try o.makeEmptyNamespaceDIType(owner_decl); + const union_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index); dib.replaceTemporary(fwd_decl, union_di_ty); // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` // means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .mod = o.module }); return union_di_ty; } @@ -1572,7 +1581,7 @@ pub const Object = struct { dib.replaceTemporary(fwd_decl, full_di_ty); // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` // means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module }); return full_di_ty; } @@ -1626,7 +1635,7 @@ pub const Object = struct { if (layout.tag_size == 0) { dib.replaceTemporary(fwd_decl, union_di_ty); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .mod = o.module }); return union_di_ty; } @@ -1685,7 +1694,7 @@ pub const Object = struct { ); dib.replaceTemporary(fwd_decl, full_di_ty); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module }); return full_di_ty; }, .Fn => { @@ -1733,7 +1742,7 @@ pub const Object = struct { 0, ); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(fn_di_ty), .{ .target = o.target }); + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(fn_di_ty), .{ .mod = o.module }); return fn_di_ty; }, .ComptimeInt => unreachable, @@ -1762,7 +1771,8 @@ pub const Object = struct { /// This is to be used instead of void for debug info types, to avoid tripping /// Assertion `!isa(Scope) && "shouldn't make a namespace scope for a type"' /// when targeting CodeView (Windows). - fn makeEmptyNamespaceDIType(o: *Object, decl: *const Module.Decl) !*llvm.DIType { + fn makeEmptyNamespaceDIType(o: *Object, decl_index: Module.Decl.Index) !*llvm.DIType { + const decl = o.module.declPtr(decl_index); const fields: [0]*llvm.DIType = .{}; return o.di_builder.?.createStructType( try o.namespaceToDebugScope(decl.src_namespace), @@ -1787,6 +1797,7 @@ pub const DeclGen = struct { object: *Object, module: *Module, decl: *Module.Decl, + decl_index: Module.Decl.Index, gpa: Allocator, err_msg: ?*Module.ErrorMsg, @@ -1804,6 +1815,7 @@ pub const DeclGen = struct { fn genDecl(dg: *DeclGen) !void { const decl = dg.decl; + const decl_index = dg.decl_index; assert(decl.has_tv); log.debug("gen: {s} type: {}, value: {}", .{ @@ -1817,7 +1829,7 @@ pub const DeclGen = struct { _ = try dg.resolveLlvmFunction(extern_fn.data.owner_decl); } else { const target = dg.module.getTarget(); - var global = try dg.resolveGlobalDecl(decl); + var global = try dg.resolveGlobalDecl(decl_index); global.setAlignment(decl.getAlignment(target)); assert(decl.has_tv); const init_val = if (decl.val.castTag(.variable)) |payload| init_val: { @@ -1858,7 +1870,7 @@ pub const DeclGen = struct { // old uses. const new_global_ptr = new_global.constBitCast(global.typeOf()); global.replaceAllUsesWith(new_global_ptr); - dg.object.decl_map.putAssumeCapacity(decl, new_global); + dg.object.decl_map.putAssumeCapacity(decl_index, new_global); new_global.takeName(global); global.deleteGlobal(); global = new_global; @@ -1869,7 +1881,7 @@ pub const DeclGen = struct { const di_file = try dg.object.getDIFile(dg.gpa, decl.src_namespace.file_scope); const line_number = decl.src_line + 1; - const is_internal_linkage = !dg.module.decl_exports.contains(decl); + const is_internal_linkage = !dg.module.decl_exports.contains(decl_index); const di_global = dib.createGlobalVariable( di_file.toScope(), decl.name, @@ -1888,12 +1900,10 @@ pub const DeclGen = struct { /// If the llvm function does not exist, create it. /// Note that this can be called before the function's semantic analysis has /// completed, so if any attributes rely on that, they must be done in updateFunc, not here. - fn resolveLlvmFunction(dg: *DeclGen, decl: *Module.Decl) !*const llvm.Value { - return dg.resolveLlvmFunctionExtra(decl, decl.ty); - } - - fn resolveLlvmFunctionExtra(dg: *DeclGen, decl: *Module.Decl, zig_fn_type: Type) !*const llvm.Value { - const gop = try dg.object.decl_map.getOrPut(dg.gpa, decl); + fn resolveLlvmFunction(dg: *DeclGen, decl_index: Module.Decl.Index) !*const llvm.Value { + const decl = dg.module.declPtr(decl_index); + const zig_fn_type = decl.ty; + const gop = try dg.object.decl_map.getOrPut(dg.gpa, decl_index); if (gop.found_existing) return gop.value_ptr.*; assert(decl.has_tv); @@ -1903,7 +1913,7 @@ pub const DeclGen = struct { const fn_type = try dg.llvmType(zig_fn_type); - const fqn = try decl.getFullyQualifiedName(dg.gpa); + const fqn = try decl.getFullyQualifiedName(dg.module); defer dg.gpa.free(fqn); const llvm_addrspace = dg.llvmAddressSpace(decl.@"addrspace"); @@ -1996,12 +2006,13 @@ pub const DeclGen = struct { // TODO add target-cpu and target-features fn attributes } - fn resolveGlobalDecl(dg: *DeclGen, decl: *Module.Decl) Error!*const llvm.Value { - const gop = try dg.object.decl_map.getOrPut(dg.gpa, decl); + fn resolveGlobalDecl(dg: *DeclGen, decl_index: Module.Decl.Index) Error!*const llvm.Value { + const gop = try dg.object.decl_map.getOrPut(dg.gpa, decl_index); if (gop.found_existing) return gop.value_ptr.*; - errdefer assert(dg.object.decl_map.remove(decl)); + errdefer assert(dg.object.decl_map.remove(decl_index)); - const fqn = try decl.getFullyQualifiedName(dg.gpa); + const decl = dg.module.declPtr(decl_index); + const fqn = try decl.getFullyQualifiedName(dg.module); defer dg.gpa.free(fqn); const llvm_type = try dg.llvmType(decl.ty); @@ -2122,7 +2133,7 @@ pub const DeclGen = struct { }, .Opaque => switch (t.tag()) { .@"opaque" => { - const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .target = target }); + const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .mod = dg.module }); if (gop.found_existing) return gop.value_ptr.*; // The Type memory is ephemeral; since we want to store a longer-lived @@ -2130,7 +2141,7 @@ pub const DeclGen = struct { gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator()); const opaque_obj = t.castTag(.@"opaque").?.data; - const name = try opaque_obj.getFullyQualifiedName(gpa); + const name = try opaque_obj.getFullyQualifiedName(dg.module); defer gpa.free(name); const llvm_struct_ty = dg.context.structCreateNamed(name); @@ -2191,7 +2202,7 @@ pub const DeclGen = struct { return dg.context.intType(16); }, .Struct => { - const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .target = target }); + const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .mod = dg.module }); if (gop.found_existing) return gop.value_ptr.*; // The Type memory is ephemeral; since we want to store a longer-lived @@ -2260,7 +2271,7 @@ pub const DeclGen = struct { return int_llvm_ty; } - const name = try struct_obj.getFullyQualifiedName(gpa); + const name = try struct_obj.getFullyQualifiedName(dg.module); defer gpa.free(name); const llvm_struct_ty = dg.context.structCreateNamed(name); @@ -2314,7 +2325,7 @@ pub const DeclGen = struct { return llvm_struct_ty; }, .Union => { - const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .target = target }); + const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .mod = dg.module }); if (gop.found_existing) return gop.value_ptr.*; // The Type memory is ephemeral; since we want to store a longer-lived @@ -2330,7 +2341,7 @@ pub const DeclGen = struct { return enum_tag_llvm_ty; } - const name = try union_obj.getFullyQualifiedName(gpa); + const name = try union_obj.getFullyQualifiedName(dg.module); defer gpa.free(name); const llvm_union_ty = dg.context.structCreateNamed(name); @@ -2439,7 +2450,7 @@ pub const DeclGen = struct { // TODO this duplicates code with Pointer but they should share the handling // of the tv.val.tag() and then Int should do extra constPtrToInt on top .Int => switch (tv.val.tag()) { - .decl_ref_mut => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref_mut).?.data.decl), + .decl_ref_mut => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref_mut).?.data.decl_index), .decl_ref => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref).?.data), else => { var bigint_space: Value.BigIntSpace = undefined; @@ -2524,12 +2535,13 @@ pub const DeclGen = struct { } }, .Pointer => switch (tv.val.tag()) { - .decl_ref_mut => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref_mut).?.data.decl), + .decl_ref_mut => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref_mut).?.data.decl_index), .decl_ref => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref).?.data), .variable => { - const decl = tv.val.castTag(.variable).?.data.owner_decl; - decl.markAlive(); - const val = try dg.resolveGlobalDecl(decl); + const decl_index = tv.val.castTag(.variable).?.data.owner_decl; + const decl = dg.module.declPtr(decl_index); + dg.module.markDeclAlive(decl); + const val = try dg.resolveGlobalDecl(decl_index); const llvm_var_type = try dg.llvmType(tv.ty); const llvm_addrspace = dg.llvmAddressSpace(decl.@"addrspace"); const llvm_type = llvm_var_type.pointerType(llvm_addrspace); @@ -2683,13 +2695,14 @@ pub const DeclGen = struct { return dg.context.constStruct(&fields, fields.len, .False); }, .Fn => { - const fn_decl = switch (tv.val.tag()) { + const fn_decl_index = switch (tv.val.tag()) { .extern_fn => tv.val.castTag(.extern_fn).?.data.owner_decl, .function => tv.val.castTag(.function).?.data.owner_decl, else => unreachable, }; - fn_decl.markAlive(); - return dg.resolveLlvmFunction(fn_decl); + const fn_decl = dg.module.declPtr(fn_decl_index); + dg.module.markDeclAlive(fn_decl); + return dg.resolveLlvmFunction(fn_decl_index); }, .ErrorSet => { const llvm_ty = try dg.llvmType(tv.ty); @@ -2911,7 +2924,7 @@ pub const DeclGen = struct { }); } const union_obj = tv.ty.cast(Type.Payload.Union).?.data; - const field_index = union_obj.tag_ty.enumTagFieldIndex(tag_and_val.tag, target).?; + const field_index = union_obj.tag_ty.enumTagFieldIndex(tag_and_val.tag, dg.module).?; assert(union_obj.haveFieldTypes()); const field_ty = union_obj.fields.values()[field_index].ty; const payload = p: { @@ -3049,17 +3062,22 @@ pub const DeclGen = struct { llvm_ptr: *const llvm.Value, }; - fn lowerParentPtrDecl(dg: *DeclGen, ptr_val: Value, decl: *Module.Decl, ptr_child_ty: Type) Error!*const llvm.Value { - decl.markAlive(); + fn lowerParentPtrDecl( + dg: *DeclGen, + ptr_val: Value, + decl_index: Module.Decl.Index, + ptr_child_ty: Type, + ) Error!*const llvm.Value { + const decl = dg.module.declPtr(decl_index); + dg.module.markDeclAlive(decl); var ptr_ty_payload: Type.Payload.ElemType = .{ .base = .{ .tag = .single_mut_pointer }, .data = decl.ty, }; const ptr_ty = Type.initPayload(&ptr_ty_payload.base); - const llvm_ptr = try dg.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl); + const llvm_ptr = try dg.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl_index); - const target = dg.module.getTarget(); - if (ptr_child_ty.eql(decl.ty, target)) { + if (ptr_child_ty.eql(decl.ty, dg.module)) { return llvm_ptr; } else { return llvm_ptr.constBitCast((try dg.llvmType(ptr_child_ty)).pointerType(0)); @@ -3071,7 +3089,7 @@ pub const DeclGen = struct { var bitcast_needed: bool = undefined; const llvm_ptr = switch (ptr_val.tag()) { .decl_ref_mut => { - const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl; + const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl_index; return dg.lowerParentPtrDecl(ptr_val, decl, ptr_child_ty); }, .decl_ref => { @@ -3123,7 +3141,7 @@ pub const DeclGen = struct { }, .Struct => { const field_ty = parent_ty.structFieldType(field_index); - bitcast_needed = !field_ty.eql(ptr_child_ty, target); + bitcast_needed = !field_ty.eql(ptr_child_ty, dg.module); var ty_buf: Type.Payload.Pointer = undefined; const llvm_field_index = llvmFieldIndex(parent_ty, field_index, target, &ty_buf).?; @@ -3139,7 +3157,7 @@ pub const DeclGen = struct { .elem_ptr => blk: { const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; const parent_llvm_ptr = try dg.lowerParentPtr(elem_ptr.array_ptr, elem_ptr.elem_ty); - bitcast_needed = !elem_ptr.elem_ty.eql(ptr_child_ty, target); + bitcast_needed = !elem_ptr.elem_ty.eql(ptr_child_ty, dg.module); const llvm_usize = try dg.llvmType(Type.usize); const indices: [1]*const llvm.Value = .{ @@ -3153,7 +3171,7 @@ pub const DeclGen = struct { var buf: Type.Payload.ElemType = undefined; const payload_ty = opt_payload_ptr.container_ty.optionalChild(&buf); - bitcast_needed = !payload_ty.eql(ptr_child_ty, target); + bitcast_needed = !payload_ty.eql(ptr_child_ty, dg.module); if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.isPtrLikeOptional()) { // In this case, we represent pointer to optional the same as pointer @@ -3173,7 +3191,7 @@ pub const DeclGen = struct { const parent_llvm_ptr = try dg.lowerParentPtr(eu_payload_ptr.container_ptr, eu_payload_ptr.container_ty); const payload_ty = eu_payload_ptr.container_ty.errorUnionPayload(); - bitcast_needed = !payload_ty.eql(ptr_child_ty, target); + bitcast_needed = !payload_ty.eql(ptr_child_ty, dg.module); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { // In this case, we represent pointer to error union the same as pointer @@ -3201,15 +3219,14 @@ pub const DeclGen = struct { fn lowerDeclRefValue( self: *DeclGen, tv: TypedValue, - decl: *Module.Decl, + decl_index: Module.Decl.Index, ) Error!*const llvm.Value { - const target = self.module.getTarget(); if (tv.ty.isSlice()) { var buf: Type.SlicePtrFieldTypeBuffer = undefined; const ptr_ty = tv.ty.slicePtrFieldType(&buf); var slice_len: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, - .data = tv.val.sliceLen(target), + .data = tv.val.sliceLen(self.module), }; const fields: [2]*const llvm.Value = .{ try self.genTypedValue(.{ @@ -3229,8 +3246,9 @@ pub const DeclGen = struct { // const bar = foo; // ... &bar; // `bar` is just an alias and we actually want to lower a reference to `foo`. + const decl = self.module.declPtr(decl_index); if (decl.val.castTag(.function)) |func| { - if (func.data.owner_decl != decl) { + if (func.data.owner_decl != decl_index) { return self.lowerDeclRefValue(tv, func.data.owner_decl); } } @@ -3240,12 +3258,12 @@ pub const DeclGen = struct { return self.lowerPtrToVoid(tv.ty); } - decl.markAlive(); + self.module.markDeclAlive(decl); const llvm_val = if (is_fn_body) - try self.resolveLlvmFunction(decl) + try self.resolveLlvmFunction(decl_index) else - try self.resolveGlobalDecl(decl); + try self.resolveGlobalDecl(decl_index); const llvm_type = try self.llvmType(tv.ty); if (tv.ty.zigTypeTag() == .Int) { @@ -4405,7 +4423,8 @@ pub const FuncGen = struct { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const func = self.air.values[ty_pl.payload].castTag(.function).?.data; - const decl = func.owner_decl; + const decl_index = func.owner_decl; + const decl = self.dg.module.declPtr(decl_index); const di_file = try self.dg.object.getDIFile(self.gpa, decl.src_namespace.file_scope); self.di_file = di_file; const line_number = decl.src_line + 1; @@ -4417,10 +4436,10 @@ pub const FuncGen = struct { .base_line = self.base_line, }); - const fqn = try decl.getFullyQualifiedName(self.gpa); + const fqn = try decl.getFullyQualifiedName(self.dg.module); defer self.gpa.free(fqn); - const is_internal_linkage = !self.dg.module.decl_exports.contains(decl); + const is_internal_linkage = !self.dg.module.decl_exports.contains(decl_index); const subprogram = dib.createFunction( di_file.toScope(), decl.name, @@ -4447,7 +4466,8 @@ pub const FuncGen = struct { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const func = self.air.values[ty_pl.payload].castTag(.function).?.data; - const decl = func.owner_decl; + const mod = self.dg.module; + const decl = mod.declPtr(func.owner_decl); const di_file = try self.dg.object.getDIFile(self.gpa, decl.src_namespace.file_scope); self.di_file = di_file; const old = self.dbg_inlined.pop(); @@ -5887,7 +5907,7 @@ pub const FuncGen = struct { if (self.dg.object.di_builder) |dib| { const src_index = self.getSrcArgIndex(self.arg_index - 1); const func = self.dg.decl.getFunction().?; - const lbrace_line = func.owner_decl.src_line + func.lbrace_line + 1; + const lbrace_line = self.dg.module.declPtr(func.owner_decl).src_line + func.lbrace_line + 1; const lbrace_col = func.lbrace_column + 1; const di_local_var = dib.createParameterVariable( self.di_scope.?, @@ -6430,8 +6450,9 @@ pub const FuncGen = struct { const operand = try self.resolveInst(un_op); const enum_ty = self.air.typeOf(un_op); + const mod = self.dg.module; const llvm_fn_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{ - try enum_ty.getOwnerDecl().getFullyQualifiedName(arena), + try mod.declPtr(enum_ty.getOwnerDecl()).getFullyQualifiedName(mod), }); const llvm_fn = try self.getEnumTagNameFunction(enum_ty, llvm_fn_name); @@ -6617,7 +6638,7 @@ pub const FuncGen = struct { for (values) |*val, i| { var buf: Value.ElemValueBuffer = undefined; - const elem = mask.elemValueBuffer(i, &buf); + const elem = mask.elemValueBuffer(self.dg.module, i, &buf); if (elem.isUndef()) { val.* = llvm_i32.getUndef(); } else { diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 6072c59845..791c2dc187 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -633,7 +633,13 @@ pub const DeclGen = struct { return result_id.toRef(); } - fn airArithOp(self: *DeclGen, inst: Air.Inst.Index, comptime fop: Opcode, comptime sop: Opcode, comptime uop: Opcode) !IdRef { + fn airArithOp( + self: *DeclGen, + inst: Air.Inst.Index, + comptime fop: Opcode, + comptime sop: Opcode, + comptime uop: Opcode, + ) !IdRef { // LHS and RHS are guaranteed to have the same type, and AIR guarantees // the result to be the same as the LHS and RHS, which matches SPIR-V. const ty = self.air.typeOfIndex(inst); @@ -644,10 +650,8 @@ pub const DeclGen = struct { const result_id = self.spv.allocId(); const result_type_id = try self.resolveTypeId(ty); - const target = self.getTarget(); - - assert(self.air.typeOf(bin_op.lhs).eql(ty, target)); - assert(self.air.typeOf(bin_op.rhs).eql(ty, target)); + assert(self.air.typeOf(bin_op.lhs).eql(ty, self.module)); + assert(self.air.typeOf(bin_op.rhs).eql(ty, self.module)); // Binary operations are generally applicable to both scalar and vector operations // in SPIR-V, but int and float versions of operations require different opcodes. @@ -694,7 +698,7 @@ pub const DeclGen = struct { const result_id = self.spv.allocId(); const result_type_id = try self.resolveTypeId(Type.initTag(.bool)); const op_ty = self.air.typeOf(bin_op.lhs); - assert(op_ty.eql(self.air.typeOf(bin_op.rhs), self.getTarget())); + assert(op_ty.eql(self.air.typeOf(bin_op.rhs), self.module)); // Comparisons are generally applicable to both scalar and vector operations in SPIR-V, // but int and float versions of operations require different opcodes. diff --git a/src/crash_report.zig b/src/crash_report.zig index 472e52b04d..e9d4022bba 100644 --- a/src/crash_report.zig +++ b/src/crash_report.zig @@ -90,9 +90,11 @@ fn dumpStatusReport() !void { const stderr = io.getStdErr().writer(); const block: *Sema.Block = anal.block; + const mod = anal.sema.mod; + const block_src_decl = mod.declPtr(block.src_decl); try stderr.writeAll("Analyzing "); - try writeFullyQualifiedDeclWithFile(block.src_decl, stderr); + try writeFullyQualifiedDeclWithFile(mod, block_src_decl, stderr); try stderr.writeAll("\n"); print_zir.renderInstructionContext( @@ -100,7 +102,7 @@ fn dumpStatusReport() !void { anal.body, anal.body_index, block.namespace.file_scope, - block.src_decl.src_node, + block_src_decl.src_node, 6, // indent stderr, ) catch |err| switch (err) { @@ -115,13 +117,14 @@ fn dumpStatusReport() !void { while (parent) |curr| { fba.reset(); try stderr.writeAll(" in "); - try writeFullyQualifiedDeclWithFile(curr.block.src_decl, stderr); + const curr_block_src_decl = mod.declPtr(curr.block.src_decl); + try writeFullyQualifiedDeclWithFile(mod, curr_block_src_decl, stderr); try stderr.writeAll("\n > "); print_zir.renderSingleInstruction( allocator, curr.body[curr.body_index], curr.block.namespace.file_scope, - curr.block.src_decl.src_node, + curr_block_src_decl.src_node, 6, // indent stderr, ) catch |err| switch (err) { @@ -146,10 +149,10 @@ fn writeFilePath(file: *Module.File, stream: anytype) !void { try stream.writeAll(file.sub_file_path); } -fn writeFullyQualifiedDeclWithFile(decl: *Decl, stream: anytype) !void { +fn writeFullyQualifiedDeclWithFile(mod: *Module, decl: *Decl, stream: anytype) !void { try writeFilePath(decl.getFileScope(), stream); try stream.writeAll(": "); - try decl.renderFullyQualifiedDebugName(stream); + try decl.renderFullyQualifiedDebugName(mod, stream); } pub fn compilerPanic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn { diff --git a/src/link.zig b/src/link.zig index c449a37ee6..08c8446219 100644 --- a/src/link.zig +++ b/src/link.zig @@ -417,17 +417,18 @@ pub const File = struct { /// Called from within the CodeGen to lower a local variable instantion as an unnamed /// constant. Returns the symbol index of the lowered constant in the read-only section /// of the final binary. - pub fn lowerUnnamedConst(base: *File, tv: TypedValue, decl: *Module.Decl) UpdateDeclError!u32 { + pub fn lowerUnnamedConst(base: *File, tv: TypedValue, decl_index: Module.Decl.Index) UpdateDeclError!u32 { + const decl = base.options.module.?.declPtr(decl_index); log.debug("lowerUnnamedConst {*} ({s})", .{ decl, decl.name }); switch (base.tag) { // zig fmt: off - .coff => return @fieldParentPtr(Coff, "base", base).lowerUnnamedConst(tv, decl), - .elf => return @fieldParentPtr(Elf, "base", base).lowerUnnamedConst(tv, decl), - .macho => return @fieldParentPtr(MachO, "base", base).lowerUnnamedConst(tv, decl), - .plan9 => return @fieldParentPtr(Plan9, "base", base).lowerUnnamedConst(tv, decl), + .coff => return @fieldParentPtr(Coff, "base", base).lowerUnnamedConst(tv, decl_index), + .elf => return @fieldParentPtr(Elf, "base", base).lowerUnnamedConst(tv, decl_index), + .macho => return @fieldParentPtr(MachO, "base", base).lowerUnnamedConst(tv, decl_index), + .plan9 => return @fieldParentPtr(Plan9, "base", base).lowerUnnamedConst(tv, decl_index), .spirv => unreachable, .c => unreachable, - .wasm => unreachable, + .wasm => return @fieldParentPtr(Wasm, "base", base).lowerUnnamedConst(tv, decl_index), .nvptx => unreachable, // zig fmt: on } @@ -435,19 +436,20 @@ pub const File = struct { /// May be called before or after updateDeclExports but must be called /// after allocateDeclIndexes for any given Decl. - pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) UpdateDeclError!void { + pub fn updateDecl(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void { + const decl = module.declPtr(decl_index); log.debug("updateDecl {*} ({s}), type={}", .{ decl, decl.name, decl.ty.fmtDebug() }); assert(decl.has_tv); switch (base.tag) { // zig fmt: off - .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl), - .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl), - .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl), - .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl), - .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl), - .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl), - .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDecl(module, decl), - .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDecl(module, decl), + .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl_index), + .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl_index), + .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl_index), + .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl_index), + .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl_index), + .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl_index), + .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDecl(module, decl_index), + .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDecl(module, decl_index), // zig fmt: on } } @@ -455,8 +457,9 @@ pub const File = struct { /// May be called before or after updateDeclExports but must be called /// after allocateDeclIndexes for any given Decl. pub fn updateFunc(base: *File, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) UpdateDeclError!void { + const owner_decl = module.declPtr(func.owner_decl); log.debug("updateFunc {*} ({s}), type={}", .{ - func.owner_decl, func.owner_decl.name, func.owner_decl.ty.fmtDebug(), + owner_decl, owner_decl.name, owner_decl.ty.fmtDebug(), }); switch (base.tag) { // zig fmt: off @@ -492,19 +495,20 @@ pub const File = struct { /// TODO we're transitioning to deleting this function and instead having /// each linker backend notice the first time updateDecl or updateFunc is called, or /// a callee referenced from AIR. - pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) error{OutOfMemory}!void { + pub fn allocateDeclIndexes(base: *File, decl_index: Module.Decl.Index) error{OutOfMemory}!void { + const decl = base.options.module.?.declPtr(decl_index); log.debug("allocateDeclIndexes {*} ({s})", .{ decl, decl.name }); switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl), - .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl), - .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl) catch |err| switch (err) { + .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl_index), + .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl_index), + .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl_index) catch |err| switch (err) { // remap this error code because we are transitioning away from // `allocateDeclIndexes`. error.Overflow => return error.OutOfMemory, error.OutOfMemory => return error.OutOfMemory, }, - .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl), - .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl), + .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl_index), + .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl_index), .c, .spirv, .nvptx => {}, } } @@ -621,17 +625,16 @@ pub const File = struct { } /// Called when a Decl is deleted from the Module. - pub fn freeDecl(base: *File, decl: *Module.Decl) void { - log.debug("freeDecl {*} ({s})", .{ decl, decl.name }); + pub fn freeDecl(base: *File, decl_index: Module.Decl.Index) void { switch (base.tag) { - .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl), - .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl), - .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl), - .c => @fieldParentPtr(C, "base", base).freeDecl(decl), - .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl), - .spirv => @fieldParentPtr(SpirV, "base", base).freeDecl(decl), - .plan9 => @fieldParentPtr(Plan9, "base", base).freeDecl(decl), - .nvptx => @fieldParentPtr(NvPtx, "base", base).freeDecl(decl), + .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl_index), + .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl_index), + .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl_index), + .c => @fieldParentPtr(C, "base", base).freeDecl(decl_index), + .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl_index), + .spirv => @fieldParentPtr(SpirV, "base", base).freeDecl(decl_index), + .plan9 => @fieldParentPtr(Plan9, "base", base).freeDecl(decl_index), + .nvptx => @fieldParentPtr(NvPtx, "base", base).freeDecl(decl_index), } } @@ -656,20 +659,21 @@ pub const File = struct { pub fn updateDeclExports( base: *File, module: *Module, - decl: *Module.Decl, + decl_index: Module.Decl.Index, exports: []const *Module.Export, ) UpdateDeclExportsError!void { + const decl = module.declPtr(decl_index); log.debug("updateDeclExports {*} ({s})", .{ decl, decl.name }); assert(decl.has_tv); switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports), - .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports), - .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports), - .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl, exports), - .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports), - .spirv => return @fieldParentPtr(SpirV, "base", base).updateDeclExports(module, decl, exports), - .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclExports(module, decl, exports), - .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDeclExports(module, decl, exports), + .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl_index, exports), + .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl_index, exports), + .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl_index, exports), + .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl_index, exports), + .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl_index, exports), + .spirv => return @fieldParentPtr(SpirV, "base", base).updateDeclExports(module, decl_index, exports), + .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclExports(module, decl_index, exports), + .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDeclExports(module, decl_index, exports), } } @@ -683,14 +687,14 @@ pub const File = struct { /// The linker is passed information about the containing atom, `parent_atom_index`, and offset within it's /// memory buffer, `offset`, so that it can make a note of potential relocation sites, should the /// `Decl`'s address was not yet resolved, or the containing atom gets moved in virtual memory. - pub fn getDeclVAddr(base: *File, decl: *const Module.Decl, reloc_info: RelocInfo) !u64 { + pub fn getDeclVAddr(base: *File, decl_index: Module.Decl.Index, reloc_info: RelocInfo) !u64 { switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl, reloc_info), - .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl, reloc_info), - .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl, reloc_info), - .plan9 => return @fieldParentPtr(Plan9, "base", base).getDeclVAddr(decl, reloc_info), + .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl_index, reloc_info), + .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl_index, reloc_info), + .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl_index, reloc_info), + .plan9 => return @fieldParentPtr(Plan9, "base", base).getDeclVAddr(decl_index, reloc_info), .c => unreachable, - .wasm => return @fieldParentPtr(Wasm, "base", base).getDeclVAddr(decl, reloc_info), + .wasm => return @fieldParentPtr(Wasm, "base", base).getDeclVAddr(decl_index, reloc_info), .spirv => unreachable, .nvptx => unreachable, } diff --git a/src/link/C.zig b/src/link/C.zig index 63aa2b6030..4159a577d2 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -21,7 +21,7 @@ base: link.File, /// This linker backend does not try to incrementally link output C source code. /// Instead, it tracks all declarations in this table, and iterates over it /// in the flush function, stitching pre-rendered pieces of C code together. -decl_table: std.AutoArrayHashMapUnmanaged(*const Module.Decl, DeclBlock) = .{}, +decl_table: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclBlock) = .{}, /// Stores Type/Value data for `typedefs` to reference. /// Accumulates allocations and then there is a periodic garbage collection after flush(). arena: std.heap.ArenaAllocator, @@ -87,9 +87,9 @@ pub fn deinit(self: *C) void { self.arena.deinit(); } -pub fn freeDecl(self: *C, decl: *Module.Decl) void { +pub fn freeDecl(self: *C, decl_index: Module.Decl.Index) void { const gpa = self.base.allocator; - if (self.decl_table.fetchSwapRemove(decl)) |kv| { + if (self.decl_table.fetchSwapRemove(decl_index)) |kv| { var decl_block = kv.value; decl_block.deinit(gpa); } @@ -99,8 +99,8 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes const tracy = trace(@src()); defer tracy.end(); - const decl = func.owner_decl; - const gop = try self.decl_table.getOrPut(self.base.allocator, decl); + const decl_index = func.owner_decl; + const gop = try self.decl_table.getOrPut(self.base.allocator, decl_index); if (!gop.found_existing) { gop.value_ptr.* = .{}; } @@ -126,9 +126,10 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes .gpa = module.gpa, .module = module, .error_msg = null, - .decl = decl, + .decl_index = decl_index, + .decl = module.declPtr(decl_index), .fwd_decl = fwd_decl.toManaged(module.gpa), - .typedefs = typedefs.promoteContext(module.gpa, .{ .target = module.getTarget() }), + .typedefs = typedefs.promoteContext(module.gpa, .{ .mod = module }), .typedefs_arena = self.arena.allocator(), }, .code = code.toManaged(module.gpa), @@ -150,7 +151,7 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes codegen.genFunc(&function) catch |err| switch (err) { error.AnalysisFail => { - try module.failed_decls.put(module.gpa, decl, function.object.dg.error_msg.?); + try module.failed_decls.put(module.gpa, decl_index, function.object.dg.error_msg.?); return; }, else => |e| return e, @@ -166,11 +167,11 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes code.shrinkAndFree(module.gpa, code.items.len); } -pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { +pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !void { const tracy = trace(@src()); defer tracy.end(); - const gop = try self.decl_table.getOrPut(self.base.allocator, decl); + const gop = try self.decl_table.getOrPut(self.base.allocator, decl_index); if (!gop.found_existing) { gop.value_ptr.* = .{}; } @@ -186,14 +187,17 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { typedefs.clearRetainingCapacity(); code.shrinkRetainingCapacity(0); + const decl = module.declPtr(decl_index); + var object: codegen.Object = .{ .dg = .{ .gpa = module.gpa, .module = module, .error_msg = null, + .decl_index = decl_index, .decl = decl, .fwd_decl = fwd_decl.toManaged(module.gpa), - .typedefs = typedefs.promoteContext(module.gpa, .{ .target = module.getTarget() }), + .typedefs = typedefs.promoteContext(module.gpa, .{ .mod = module }), .typedefs_arena = self.arena.allocator(), }, .code = code.toManaged(module.gpa), @@ -211,7 +215,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { codegen.genDecl(&object) catch |err| switch (err) { error.AnalysisFail => { - try module.failed_decls.put(module.gpa, decl, object.dg.error_msg.?); + try module.failed_decls.put(module.gpa, decl_index, object.dg.error_msg.?); return; }, else => |e| return e, @@ -287,14 +291,14 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) const decl_keys = self.decl_table.keys(); const decl_values = self.decl_table.values(); - for (decl_keys) |decl| { - assert(decl.has_tv); - f.remaining_decls.putAssumeCapacityNoClobber(decl, {}); + for (decl_keys) |decl_index| { + assert(module.declPtr(decl_index).has_tv); + f.remaining_decls.putAssumeCapacityNoClobber(decl_index, {}); } while (f.remaining_decls.popOrNull()) |kv| { - const decl = kv.key; - try flushDecl(self, &f, decl); + const decl_index = kv.key; + try flushDecl(self, &f, decl_index); } f.all_buffers.items[err_typedef_index] = .{ @@ -305,7 +309,8 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) // Now the function bodies. try f.all_buffers.ensureUnusedCapacity(gpa, f.fn_count); - for (decl_keys) |decl, i| { + for (decl_keys) |decl_index, i| { + const decl = module.declPtr(decl_index); if (decl.getFunction() != null) { const decl_block = &decl_values[i]; const buf = decl_block.code.items; @@ -325,7 +330,7 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) } const Flush = struct { - remaining_decls: std.AutoArrayHashMapUnmanaged(*const Module.Decl, void) = .{}, + remaining_decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, void) = .{}, typedefs: Typedefs = .{}, err_typedef_buf: std.ArrayListUnmanaged(u8) = .{}, /// We collect a list of buffers to write, and write them all at once with pwritev 😎 @@ -354,7 +359,9 @@ const FlushDeclError = error{ }; /// Assumes `decl` was in the `remaining_decls` set, and has already been removed. -fn flushDecl(self: *C, f: *Flush, decl: *const Module.Decl) FlushDeclError!void { +fn flushDecl(self: *C, f: *Flush, decl_index: Module.Decl.Index) FlushDeclError!void { + const module = self.base.options.module.?; + const decl = module.declPtr(decl_index); // Before flushing any particular Decl we must ensure its // dependencies are already flushed, so that the order in the .c // file comes out correctly. @@ -364,15 +371,17 @@ fn flushDecl(self: *C, f: *Flush, decl: *const Module.Decl) FlushDeclError!void } } - const decl_block = self.decl_table.getPtr(decl).?; + const decl_block = self.decl_table.getPtr(decl_index).?; const gpa = self.base.allocator; if (decl_block.typedefs.count() != 0) { - try f.typedefs.ensureUnusedCapacity(gpa, @intCast(u32, decl_block.typedefs.count())); + try f.typedefs.ensureUnusedCapacityContext(gpa, @intCast(u32, decl_block.typedefs.count()), .{ + .mod = module, + }); var it = decl_block.typedefs.iterator(); while (it.next()) |new| { const gop = f.typedefs.getOrPutAssumeCapacityContext(new.key_ptr.*, .{ - .target = self.base.options.target, + .mod = module, }); if (!gop.found_existing) { try f.err_typedef_buf.appendSlice(gpa, new.value_ptr.rendered); @@ -417,8 +426,8 @@ pub fn flushEmitH(module: *Module) !void { .iov_len = zig_h.len, }); - for (emit_h.decl_table.keys()) |decl| { - const decl_emit_h = decl.getEmitH(module); + for (emit_h.decl_table.keys()) |decl_index| { + const decl_emit_h = emit_h.declPtr(decl_index); const buf = decl_emit_h.fwd_decl.items; all_buffers.appendAssumeCapacity(.{ .iov_base = buf.ptr, @@ -442,11 +451,11 @@ pub fn flushEmitH(module: *Module) !void { pub fn updateDeclExports( self: *C, module: *Module, - decl: *Module.Decl, + decl_index: Module.Decl.Index, exports: []const *Module.Export, ) !void { _ = exports; - _ = decl; + _ = decl_index; _ = module; _ = self; } diff --git a/src/link/Coff.zig b/src/link/Coff.zig index a91f48dfbd..246918515d 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -418,11 +418,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff { return self; } -pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void { +pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void { if (self.llvm_object) |_| return; try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1); + const decl = self.base.options.module.?.declPtr(decl_index); if (self.offset_table_free_list.popOrNull()) |i| { decl.link.coff.offset_table_index = i; } else { @@ -674,7 +675,8 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); - const decl = func.owner_decl; + const decl_index = func.owner_decl; + const decl = module.declPtr(decl_index); const res = try codegen.generateFunction( &self.base, decl.srcLoc(), @@ -688,7 +690,7 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live .appended => code_buffer.items, .fail => |em| { decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, em); + try module.failed_decls.put(module.gpa, decl_index, em); return; }, }; @@ -696,24 +698,26 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live return self.finishUpdateDecl(module, func.owner_decl, code); } -pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl: *Module.Decl) !u32 { +pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 { _ = self; _ = tv; - _ = decl; + _ = decl_index; log.debug("TODO lowerUnnamedConst for Coff", .{}); return error.AnalysisFail; } -pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { +pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void { if (build_options.skip_non_native and builtin.object_format != .coff) { @panic("Attempted to compile for object format that was disabled by build configuration"); } if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl); + if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl_index); } const tracy = trace(@src()); defer tracy.end(); + const decl = module.declPtr(decl_index); + if (decl.val.tag() == .extern_fn) { return; // TODO Should we do more when front-end analyzed extern decl? } @@ -735,15 +739,16 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { .appended => code_buffer.items, .fail => |em| { decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, em); + try module.failed_decls.put(module.gpa, decl_index, em); return; }, }; - return self.finishUpdateDecl(module, decl, code); + return self.finishUpdateDecl(module, decl_index, code); } -fn finishUpdateDecl(self: *Coff, module: *Module, decl: *Module.Decl, code: []const u8) !void { +fn finishUpdateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index, code: []const u8) !void { + const decl = module.declPtr(decl_index); const required_alignment = decl.ty.abiAlignment(self.base.options.target); const curr_size = decl.link.coff.size; if (curr_size != 0) { @@ -778,15 +783,18 @@ fn finishUpdateDecl(self: *Coff, module: *Module, decl: *Module.Decl, code: []co try self.base.file.?.pwriteAll(code, self.section_data_offset + self.offset_table_size + decl.link.coff.text_offset); // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. - const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; - return self.updateDeclExports(module, decl, decl_exports); + const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{}; + return self.updateDeclExports(module, decl_index, decl_exports); } -pub fn freeDecl(self: *Coff, decl: *Module.Decl) void { +pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl); + if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); } + const mod = self.base.options.module.?; + const decl = mod.declPtr(decl_index); + // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. self.freeTextBlock(&decl.link.coff); self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {}; @@ -795,16 +803,17 @@ pub fn freeDecl(self: *Coff, decl: *Module.Decl) void { pub fn updateDeclExports( self: *Coff, module: *Module, - decl: *Module.Decl, + decl_index: Module.Decl.Index, exports: []const *Module.Export, ) !void { if (build_options.skip_non_native and builtin.object_format != .coff) { @panic("Attempted to compile for object format that was disabled by build configuration"); } if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports); + if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl_index, exports); } + const decl = module.declPtr(decl_index); for (exports) |exp| { if (exp.options.section) |section_name| { if (!mem.eql(u8, section_name, ".text")) { @@ -1474,8 +1483,14 @@ fn findLib(self: *Coff, arena: Allocator, name: []const u8) !?[]const u8 { return null; } -pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl, reloc_info: link.File.RelocInfo) !u64 { +pub fn getDeclVAddr( + self: *Coff, + decl_index: Module.Decl.Index, + reloc_info: link.File.RelocInfo, +) !u64 { _ = reloc_info; + const mod = self.base.options.module.?; + const decl = mod.declPtr(decl_index); assert(self.llvm_object == null); return self.text_section_virtual_address + decl.link.coff.text_offset; } diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 248521c544..97fc090b9a 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -67,7 +67,7 @@ pub const Atom = struct { /// Decl's inner Atom is assigned an offset within the DWARF section. pub const DeclState = struct { gpa: Allocator, - target: std.Target, + mod: *Module, dbg_line: std.ArrayList(u8), dbg_info: std.ArrayList(u8), abbrev_type_arena: std.heap.ArenaAllocator, @@ -81,10 +81,10 @@ pub const DeclState = struct { abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{}, exprloc_relocs: std.ArrayListUnmanaged(ExprlocRelocation) = .{}, - fn init(gpa: Allocator, target: std.Target) DeclState { + fn init(gpa: Allocator, mod: *Module) DeclState { return .{ .gpa = gpa, - .target = target, + .mod = mod, .dbg_line = std.ArrayList(u8).init(gpa), .dbg_info = std.ArrayList(u8).init(gpa), .abbrev_type_arena = std.heap.ArenaAllocator.init(gpa), @@ -118,7 +118,7 @@ pub const DeclState = struct { addend: ?u32, ) !void { const resolv = self.abbrev_resolver.getContext(ty, .{ - .target = self.target, + .mod = self.mod, }) orelse blk: { const sym_index = @intCast(u32, self.abbrev_table.items.len); try self.abbrev_table.append(self.gpa, .{ @@ -128,10 +128,10 @@ pub const DeclState = struct { }); log.debug("@{d}: {}", .{ sym_index, ty.fmtDebug() }); try self.abbrev_resolver.putNoClobberContext(self.gpa, ty, sym_index, .{ - .target = self.target, + .mod = self.mod, }); break :blk self.abbrev_resolver.getContext(ty, .{ - .target = self.target, + .mod = self.mod, }).?; }; const add: u32 = addend orelse 0; @@ -153,8 +153,8 @@ pub const DeclState = struct { ) error{OutOfMemory}!void { const arena = self.abbrev_type_arena.allocator(); const dbg_info_buffer = &self.dbg_info; - const target = self.target; - const target_endian = self.target.cpu.arch.endian(); + const target = module.getTarget(); + const target_endian = target.cpu.arch.endian(); switch (ty.zigTypeTag()) { .NoReturn => unreachable, @@ -181,7 +181,7 @@ pub const DeclState = struct { // DW.AT.byte_size, DW.FORM.data1 dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); + try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(module)}); }, .Optional => { if (ty.isPtrLikeOptional()) { @@ -192,7 +192,7 @@ pub const DeclState = struct { // DW.AT.byte_size, DW.FORM.data1 dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); + try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(module)}); } else { // Non-pointer optionals are structs: struct { .maybe = *, .val = * } var buf = try arena.create(Type.Payload.ElemType); @@ -203,7 +203,7 @@ pub const DeclState = struct { const abi_size = ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); + try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(module)}); // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(7); dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); @@ -242,7 +242,7 @@ pub const DeclState = struct { // DW.AT.byte_size, DW.FORM.sdata dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize) * 2); // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); + try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(module)}); // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(5); dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); @@ -285,7 +285,7 @@ pub const DeclState = struct { // DW.AT.array_type try dbg_info_buffer.append(@enumToInt(AbbrevKind.array_type)); // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); + try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(module)}); // DW.AT.type, DW.FORM.ref4 var index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); @@ -312,7 +312,7 @@ pub const DeclState = struct { switch (ty.tag()) { .tuple, .anon_struct => { // DW.AT.name, DW.FORM.string - try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); + try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(module)}); const fields = ty.tupleFields(); for (fields.types) |field, field_index| { @@ -331,7 +331,7 @@ pub const DeclState = struct { }, else => { // DW.AT.name, DW.FORM.string - const struct_name = try ty.nameAllocArena(arena, target); + const struct_name = try ty.nameAllocArena(arena, module); try dbg_info_buffer.ensureUnusedCapacity(struct_name.len + 1); dbg_info_buffer.appendSliceAssumeCapacity(struct_name); dbg_info_buffer.appendAssumeCapacity(0); @@ -372,7 +372,7 @@ pub const DeclState = struct { const abi_size = ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); // DW.AT.name, DW.FORM.string - const enum_name = try ty.nameAllocArena(arena, target); + const enum_name = try ty.nameAllocArena(arena, module); try dbg_info_buffer.ensureUnusedCapacity(enum_name.len + 1); dbg_info_buffer.appendSliceAssumeCapacity(enum_name); dbg_info_buffer.appendAssumeCapacity(0); @@ -410,7 +410,7 @@ pub const DeclState = struct { const payload_offset = if (layout.tag_align >= layout.payload_align) layout.tag_size else 0; const tag_offset = if (layout.tag_align >= layout.payload_align) 0 else layout.payload_size; const is_tagged = layout.tag_size > 0; - const union_name = try ty.nameAllocArena(arena, target); + const union_name = try ty.nameAllocArena(arena, module); // TODO this is temporary to match current state of unions in Zig - we don't yet have // safety checks implemented meaning the implicit tag is not yet stored and generated @@ -491,7 +491,7 @@ pub const DeclState = struct { self.abbrev_type_arena.allocator(), module, ty, - self.target, + target, &self.dbg_info, ); }, @@ -507,7 +507,7 @@ pub const DeclState = struct { // DW.AT.byte_size, DW.FORM.sdata try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); // DW.AT.name, DW.FORM.string - const name = try ty.nameAllocArena(arena, target); + const name = try ty.nameAllocArena(arena, module); try dbg_info_buffer.writer().print("{s}\x00", .{name}); // DW.AT.member @@ -654,17 +654,17 @@ pub fn deinit(self: *Dwarf) void { /// Initializes Decl's state and its matching output buffers. /// Call this before `commitDeclState`. -pub fn initDeclState(self: *Dwarf, decl: *Module.Decl) !DeclState { +pub fn initDeclState(self: *Dwarf, mod: *Module, decl: *Module.Decl) !DeclState { const tracy = trace(@src()); defer tracy.end(); - const decl_name = try decl.getFullyQualifiedName(self.allocator); + const decl_name = try decl.getFullyQualifiedName(mod); defer self.allocator.free(decl_name); log.debug("initDeclState {s}{*}", .{ decl_name, decl }); const gpa = self.allocator; - var decl_state = DeclState.init(gpa, self.target); + var decl_state = DeclState.init(gpa, mod); errdefer decl_state.deinit(); const dbg_line_buffer = &decl_state.dbg_line; const dbg_info_buffer = &decl_state.dbg_info; @@ -2133,7 +2133,7 @@ fn addDbgInfoErrorSet( const abi_size = ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); // DW.AT.name, DW.FORM.string - const name = try ty.nameAllocArena(arena, target); + const name = try ty.nameAllocArena(arena, module); try dbg_info_buffer.writer().print("{s}\x00", .{name}); // DW.AT.enumerator diff --git a/src/link/Elf.zig b/src/link/Elf.zig index a58321c0ec..144ac24b9b 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -134,7 +134,7 @@ atom_free_lists: std.AutoHashMapUnmanaged(u16, std.ArrayListUnmanaged(*TextBlock /// We store them here so that we can properly dispose of any allocated /// memory within the atom in the incremental linker. /// TODO consolidate this. -decls: std.AutoHashMapUnmanaged(*Module.Decl, ?u16) = .{}, +decls: std.AutoHashMapUnmanaged(Module.Decl.Index, ?u16) = .{}, /// List of atoms that are owned directly by the linker. /// Currently these are only atoms that are the result of linking @@ -178,7 +178,7 @@ const Reloc = struct { }; const RelocTable = std.AutoHashMapUnmanaged(*TextBlock, std.ArrayListUnmanaged(Reloc)); -const UnnamedConstTable = std.AutoHashMapUnmanaged(*Module.Decl, std.ArrayListUnmanaged(*TextBlock)); +const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*TextBlock)); /// When allocating, the ideal_capacity is calculated by /// actual_capacity + (actual_capacity / ideal_factor) @@ -389,7 +389,10 @@ pub fn deinit(self: *Elf) void { } } -pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl, reloc_info: File.RelocInfo) !u64 { +pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 { + const mod = self.base.options.module.?; + const decl = mod.declPtr(decl_index); + assert(self.llvm_object == null); assert(decl.link.elf.local_sym_index != 0); @@ -2189,15 +2192,17 @@ fn allocateLocalSymbol(self: *Elf) !u32 { return index; } -pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void { +pub fn allocateDeclIndexes(self: *Elf, decl_index: Module.Decl.Index) !void { if (self.llvm_object) |_| return; + const mod = self.base.options.module.?; + const decl = mod.declPtr(decl_index); if (decl.link.elf.local_sym_index != 0) return; try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1); - try self.decls.putNoClobber(self.base.allocator, decl, null); + try self.decls.putNoClobber(self.base.allocator, decl_index, null); - const decl_name = try decl.getFullyQualifiedName(self.base.allocator); + const decl_name = try decl.getFullyQualifiedName(mod); defer self.base.allocator.free(decl_name); log.debug("allocating symbol indexes for {s}", .{decl_name}); @@ -2214,8 +2219,8 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void { self.offset_table.items[decl.link.elf.offset_table_index] = 0; } -fn freeUnnamedConsts(self: *Elf, decl: *Module.Decl) void { - const unnamed_consts = self.unnamed_const_atoms.getPtr(decl) orelse return; +fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void { + const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; for (unnamed_consts.items) |atom| { self.freeTextBlock(atom, self.phdr_load_ro_index.?); self.local_symbol_free_list.append(self.base.allocator, atom.local_sym_index) catch {}; @@ -2225,15 +2230,18 @@ fn freeUnnamedConsts(self: *Elf, decl: *Module.Decl) void { unnamed_consts.clearAndFree(self.base.allocator); } -pub fn freeDecl(self: *Elf, decl: *Module.Decl) void { +pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void { if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl); + if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); } - const kv = self.decls.fetchRemove(decl); + const mod = self.base.options.module.?; + const decl = mod.declPtr(decl_index); + + const kv = self.decls.fetchRemove(decl_index); if (kv.?.value) |index| { self.freeTextBlock(&decl.link.elf, index); - self.freeUnnamedConsts(decl); + self.freeUnnamedConsts(decl_index); } // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. @@ -2274,14 +2282,17 @@ fn getDeclPhdrIndex(self: *Elf, decl: *Module.Decl) !u16 { return phdr_index; } -fn updateDeclCode(self: *Elf, decl: *Module.Decl, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym { - const decl_name = try decl.getFullyQualifiedName(self.base.allocator); +fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym { + const mod = self.base.options.module.?; + const decl = mod.declPtr(decl_index); + + const decl_name = try decl.getFullyQualifiedName(mod); defer self.base.allocator.free(decl_name); log.debug("updateDeclCode {s}{*}", .{ decl_name, decl }); const required_alignment = decl.ty.abiAlignment(self.base.options.target); - const decl_ptr = self.decls.getPtr(decl).?; + const decl_ptr = self.decls.getPtr(decl_index).?; if (decl_ptr.* == null) { decl_ptr.* = try self.getDeclPhdrIndex(decl); } @@ -2355,10 +2366,11 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); - const decl = func.owner_decl; - self.freeUnnamedConsts(decl); + const decl_index = func.owner_decl; + const decl = module.declPtr(decl_index); + self.freeUnnamedConsts(decl_index); - var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(decl) else null; + var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl) else null; defer if (decl_state) |*ds| ds.deinit(); const res = if (decl_state) |*ds| @@ -2372,11 +2384,11 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven .appended => code_buffer.items, .fail => |em| { decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, em); + try module.failed_decls.put(module.gpa, decl_index, em); return; }, }; - const local_sym = try self.updateDeclCode(decl, code, elf.STT_FUNC); + const local_sym = try self.updateDeclCode(decl_index, code, elf.STT_FUNC); if (decl_state) |*ds| { try self.dwarf.?.commitDeclState( &self.base, @@ -2389,21 +2401,23 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven } // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. - const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; - return self.updateDeclExports(module, decl, decl_exports); + const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{}; + return self.updateDeclExports(module, decl_index, decl_exports); } -pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { +pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !void { if (build_options.skip_non_native and builtin.object_format != .elf) { @panic("Attempted to compile for object format that was disabled by build configuration"); } if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl); + if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl_index); } const tracy = trace(@src()); defer tracy.end(); + const decl = module.declPtr(decl_index); + if (decl.val.tag() == .extern_fn) { return; // TODO Should we do more when front-end analyzed extern decl? } @@ -2414,12 +2428,12 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { } } - assert(!self.unnamed_const_atoms.contains(decl)); + assert(!self.unnamed_const_atoms.contains(decl_index)); var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); - var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(decl) else null; + var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl) else null; defer if (decl_state) |*ds| ds.deinit(); // TODO implement .debug_info for global variables @@ -2446,12 +2460,12 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { .appended => code_buffer.items, .fail => |em| { decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, em); + try module.failed_decls.put(module.gpa, decl_index, em); return; }, }; - const local_sym = try self.updateDeclCode(decl, code, elf.STT_OBJECT); + const local_sym = try self.updateDeclCode(decl_index, code, elf.STT_OBJECT); if (decl_state) |*ds| { try self.dwarf.?.commitDeclState( &self.base, @@ -2464,16 +2478,18 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { } // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. - const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; - return self.updateDeclExports(module, decl, decl_exports); + const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{}; + return self.updateDeclExports(module, decl_index, decl_exports); } -pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl) !u32 { +pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 { var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); - const module = self.base.options.module.?; - const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl); + const mod = self.base.options.module.?; + const decl = mod.declPtr(decl_index); + + const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl_index); if (!gop.found_existing) { gop.value_ptr.* = .{}; } @@ -2485,7 +2501,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl try self.managed_atoms.append(self.base.allocator, atom); const name_str_index = blk: { - const decl_name = try decl.getFullyQualifiedName(self.base.allocator); + const decl_name = try decl.getFullyQualifiedName(mod); defer self.base.allocator.free(decl_name); const index = unnamed_consts.items.len; @@ -2510,7 +2526,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl .appended => code_buffer.items, .fail => |em| { decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, em); + try mod.failed_decls.put(mod.gpa, decl_index, em); log.err("{s}", .{em.msg}); return error.AnalysisFail; }, @@ -2547,24 +2563,25 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl pub fn updateDeclExports( self: *Elf, module: *Module, - decl: *Module.Decl, + decl_index: Module.Decl.Index, exports: []const *Module.Export, ) !void { if (build_options.skip_non_native and builtin.object_format != .elf) { @panic("Attempted to compile for object format that was disabled by build configuration"); } if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports); + if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl_index, exports); } const tracy = trace(@src()); defer tracy.end(); try self.global_symbols.ensureUnusedCapacity(self.base.allocator, exports.len); + const decl = module.declPtr(decl_index); if (decl.link.elf.local_sym_index == 0) return; const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index]; - const decl_ptr = self.decls.getPtr(decl).?; + const decl_ptr = self.decls.getPtr(decl_index).?; if (decl_ptr.* == null) { decl_ptr.* = try self.getDeclPhdrIndex(decl); } @@ -2633,12 +2650,11 @@ pub fn updateDeclExports( } /// Must be called only after a successful call to `updateDecl`. -pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Decl) !void { - _ = module; +pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl) !void { const tracy = trace(@src()); defer tracy.end(); - const decl_name = try decl.getFullyQualifiedName(self.base.allocator); + const decl_name = try decl.getFullyQualifiedName(mod); defer self.base.allocator.free(decl_name); log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl }); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index e604029382..52ffba954d 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -247,14 +247,14 @@ unnamed_const_atoms: UnnamedConstTable = .{}, /// We store them here so that we can properly dispose of any allocated /// memory within the atom in the incremental linker. /// TODO consolidate this. -decls: std.AutoArrayHashMapUnmanaged(*Module.Decl, ?MatchingSection) = .{}, +decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{}, const Entry = struct { target: Atom.Relocation.Target, atom: *Atom, }; -const UnnamedConstTable = std.AutoHashMapUnmanaged(*Module.Decl, std.ArrayListUnmanaged(*Atom)); +const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom)); const PendingUpdate = union(enum) { resolve_undef: u32, @@ -3451,10 +3451,15 @@ pub fn deinit(self: *MachO) void { } self.atom_free_lists.deinit(self.base.allocator); } - for (self.decls.keys()) |decl| { - decl.link.macho.deinit(self.base.allocator); + if (self.base.options.module) |mod| { + for (self.decls.keys()) |decl_index| { + const decl = mod.declPtr(decl_index); + decl.link.macho.deinit(self.base.allocator); + } + self.decls.deinit(self.base.allocator); + } else { + assert(self.decls.count() == 0); } - self.decls.deinit(self.base.allocator); { var it = self.unnamed_const_atoms.valueIterator(); @@ -3652,13 +3657,14 @@ pub fn allocateTlvPtrEntry(self: *MachO, target: Atom.Relocation.Target) !u32 { return index; } -pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { +pub fn allocateDeclIndexes(self: *MachO, decl_index: Module.Decl.Index) !void { if (self.llvm_object) |_| return; + const decl = self.base.options.module.?.declPtr(decl_index); if (decl.link.macho.local_sym_index != 0) return; decl.link.macho.local_sym_index = try self.allocateLocalSymbol(); try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.macho.local_sym_index, &decl.link.macho); - try self.decls.putNoClobber(self.base.allocator, decl, null); + try self.decls.putNoClobber(self.base.allocator, decl_index, null); const got_target = .{ .local = decl.link.macho.local_sym_index }; const got_index = try self.allocateGotEntry(got_target); @@ -3676,8 +3682,9 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv const tracy = trace(@src()); defer tracy.end(); - const decl = func.owner_decl; - self.freeUnnamedConsts(decl); + const decl_index = func.owner_decl; + const decl = module.declPtr(decl_index); + self.freeUnnamedConsts(decl_index); // TODO clearing the code and relocs buffer should probably be orchestrated // in a different, smarter, more automatic way somewhere else, in a more centralised @@ -3690,7 +3697,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv defer code_buffer.deinit(); var decl_state = if (self.d_sym) |*d_sym| - try d_sym.dwarf.initDeclState(decl) + try d_sym.dwarf.initDeclState(module, decl) else null; defer if (decl_state) |*ds| ds.deinit(); @@ -3708,12 +3715,12 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv }, .fail => |em| { decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, em); + try module.failed_decls.put(module.gpa, decl_index, em); return; }, } - const symbol = try self.placeDecl(decl, decl.link.macho.code.items.len); + const symbol = try self.placeDecl(decl_index, decl.link.macho.code.items.len); if (decl_state) |*ds| { try self.d_sym.?.dwarf.commitDeclState( @@ -3728,22 +3735,23 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv // Since we updated the vaddr and the size, each corresponding export symbol also // needs to be updated. - const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; - try self.updateDeclExports(module, decl, decl_exports); + const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{}; + try self.updateDeclExports(module, decl_index, decl_exports); } -pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.Decl) !u32 { +pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 { var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); const module = self.base.options.module.?; - const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl); + const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl_index); if (!gop.found_existing) { gop.value_ptr.* = .{}; } const unnamed_consts = gop.value_ptr; - const decl_name = try decl.getFullyQualifiedName(self.base.allocator); + const decl = module.declPtr(decl_index); + const decl_name = try decl.getFullyQualifiedName(module); defer self.base.allocator.free(decl_name); const name_str_index = blk: { @@ -3769,7 +3777,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.De .appended => code_buffer.items, .fail => |em| { decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, em); + try module.failed_decls.put(module.gpa, decl_index, em); log.err("{s}", .{em.msg}); return error.AnalysisFail; }, @@ -3800,16 +3808,18 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.De return atom.local_sym_index; } -pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { +pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void { if (build_options.skip_non_native and builtin.object_format != .macho) { @panic("Attempted to compile for object format that was disabled by build configuration"); } if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl); + if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl_index); } const tracy = trace(@src()); defer tracy.end(); + const decl = module.declPtr(decl_index); + if (decl.val.tag() == .extern_fn) { return; // TODO Should we do more when front-end analyzed extern decl? } @@ -3824,7 +3834,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { defer code_buffer.deinit(); var decl_state: ?Dwarf.DeclState = if (self.d_sym) |*d_sym| - try d_sym.dwarf.initDeclState(decl) + try d_sym.dwarf.initDeclState(module, decl) else null; defer if (decl_state) |*ds| ds.deinit(); @@ -3862,12 +3872,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { }, .fail => |em| { decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, em); + try module.failed_decls.put(module.gpa, decl_index, em); return; }, } }; - const symbol = try self.placeDecl(decl, code.len); + const symbol = try self.placeDecl(decl_index, code.len); if (decl_state) |*ds| { try self.d_sym.?.dwarf.commitDeclState( @@ -3882,13 +3892,13 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { // Since we updated the vaddr and the size, each corresponding export symbol also // needs to be updated. - const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; - try self.updateDeclExports(module, decl, decl_exports); + const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{}; + try self.updateDeclExports(module, decl_index, decl_exports); } /// Checks if the value, or any of its embedded values stores a pointer, and thus requires /// a rebase opcode for the dynamic linker. -fn needsPointerRebase(ty: Type, val: Value, target: std.Target) bool { +fn needsPointerRebase(ty: Type, val: Value, mod: *Module) bool { if (ty.zigTypeTag() == .Fn) { return false; } @@ -3903,8 +3913,8 @@ fn needsPointerRebase(ty: Type, val: Value, target: std.Target) bool { if (ty.arrayLen() == 0) return false; const elem_ty = ty.childType(); var elem_value_buf: Value.ElemValueBuffer = undefined; - const elem_val = val.elemValueBuffer(0, &elem_value_buf); - return needsPointerRebase(elem_ty, elem_val, target); + const elem_val = val.elemValueBuffer(mod, 0, &elem_value_buf); + return needsPointerRebase(elem_ty, elem_val, mod); }, .Struct => { const fields = ty.structFields().values(); @@ -3912,7 +3922,7 @@ fn needsPointerRebase(ty: Type, val: Value, target: std.Target) bool { if (val.castTag(.aggregate)) |payload| { const field_values = payload.data; for (field_values) |field_val, i| { - if (needsPointerRebase(fields[i].ty, field_val, target)) return true; + if (needsPointerRebase(fields[i].ty, field_val, mod)) return true; } else return false; } else return false; }, @@ -3921,18 +3931,18 @@ fn needsPointerRebase(ty: Type, val: Value, target: std.Target) bool { const sub_val = payload.data; var buffer: Type.Payload.ElemType = undefined; const sub_ty = ty.optionalChild(&buffer); - return needsPointerRebase(sub_ty, sub_val, target); + return needsPointerRebase(sub_ty, sub_val, mod); } else return false; }, .Union => { const union_obj = val.cast(Value.Payload.Union).?.data; - const active_field_ty = ty.unionFieldType(union_obj.tag, target); - return needsPointerRebase(active_field_ty, union_obj.val, target); + const active_field_ty = ty.unionFieldType(union_obj.tag, mod); + return needsPointerRebase(active_field_ty, union_obj.val, mod); }, .ErrorUnion => { if (val.castTag(.eu_payload)) |payload| { const payload_ty = ty.errorUnionPayload(); - return needsPointerRebase(payload_ty, payload.data, target); + return needsPointerRebase(payload_ty, payload.data, mod); } else return false; }, else => return false, @@ -3942,6 +3952,7 @@ fn needsPointerRebase(ty: Type, val: Value, target: std.Target) bool { fn getMatchingSectionAtom(self: *MachO, atom: *Atom, name: []const u8, ty: Type, val: Value) !MatchingSection { const code = atom.code.items; const target = self.base.options.target; + const mod = self.base.options.module.?; const alignment = ty.abiAlignment(target); const align_log_2 = math.log2(alignment); const zig_ty = ty.zigTypeTag(); @@ -3969,7 +3980,7 @@ fn getMatchingSectionAtom(self: *MachO, atom: *Atom, name: []const u8, ty: Type, }; } - if (needsPointerRebase(ty, val, target)) { + if (needsPointerRebase(ty, val, mod)) { break :blk (try self.getMatchingSection(.{ .segname = makeStaticString("__DATA_CONST"), .sectname = makeStaticString("__const"), @@ -4025,15 +4036,17 @@ fn getMatchingSectionAtom(self: *MachO, atom: *Atom, name: []const u8, ty: Type, return match; } -fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 { +fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*macho.nlist_64 { + const module = self.base.options.module.?; + const decl = module.declPtr(decl_index); const required_alignment = decl.ty.abiAlignment(self.base.options.target); assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes() const symbol = &self.locals.items[decl.link.macho.local_sym_index]; - const sym_name = try decl.getFullyQualifiedName(self.base.allocator); + const sym_name = try decl.getFullyQualifiedName(module); defer self.base.allocator.free(sym_name); - const decl_ptr = self.decls.getPtr(decl).?; + const decl_ptr = self.decls.getPtr(decl_index).?; if (decl_ptr.* == null) { decl_ptr.* = try self.getMatchingSectionAtom(&decl.link.macho, sym_name, decl.ty, decl.val); } @@ -4101,19 +4114,20 @@ pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.D pub fn updateDeclExports( self: *MachO, module: *Module, - decl: *Module.Decl, + decl_index: Module.Decl.Index, exports: []const *Module.Export, ) !void { if (build_options.skip_non_native and builtin.object_format != .macho) { @panic("Attempted to compile for object format that was disabled by build configuration"); } if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports); + if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl_index, exports); } const tracy = trace(@src()); defer tracy.end(); try self.globals.ensureUnusedCapacity(self.base.allocator, exports.len); + const decl = module.declPtr(decl_index); if (decl.link.macho.local_sym_index == 0) return; const decl_sym = &self.locals.items[decl.link.macho.local_sym_index]; @@ -4250,9 +4264,8 @@ pub fn deleteExport(self: *MachO, exp: Export) void { global.n_value = 0; } -fn freeUnnamedConsts(self: *MachO, decl: *Module.Decl) void { - log.debug("freeUnnamedConsts for decl {*}", .{decl}); - const unnamed_consts = self.unnamed_const_atoms.getPtr(decl) orelse return; +fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void { + const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; for (unnamed_consts.items) |atom| { self.freeAtom(atom, .{ .seg = self.text_segment_cmd_index.?, @@ -4267,15 +4280,17 @@ fn freeUnnamedConsts(self: *MachO, decl: *Module.Decl) void { unnamed_consts.clearAndFree(self.base.allocator); } -pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { +pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void { if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl); + if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); } + const mod = self.base.options.module.?; + const decl = mod.declPtr(decl_index); log.debug("freeDecl {*}", .{decl}); - const kv = self.decls.fetchSwapRemove(decl); + const kv = self.decls.fetchSwapRemove(decl_index); if (kv.?.value) |match| { self.freeAtom(&decl.link.macho, match, false); - self.freeUnnamedConsts(decl); + self.freeUnnamedConsts(decl_index); } // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. if (decl.link.macho.local_sym_index != 0) { @@ -4307,7 +4322,10 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void { } } -pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl, reloc_info: File.RelocInfo) !u64 { +pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 { + const mod = self.base.options.module.?; + const decl = mod.declPtr(decl_index); + assert(self.llvm_object == null); assert(decl.link.macho.local_sym_index != 0); diff --git a/src/link/NvPtx.zig b/src/link/NvPtx.zig index b518dc3f68..bd86d87201 100644 --- a/src/link/NvPtx.zig +++ b/src/link/NvPtx.zig @@ -74,27 +74,27 @@ pub fn updateFunc(self: *NvPtx, module: *Module, func: *Module.Fn, air: Air, liv try self.llvm_object.updateFunc(module, func, air, liveness); } -pub fn updateDecl(self: *NvPtx, module: *Module, decl: *Module.Decl) !void { +pub fn updateDecl(self: *NvPtx, module: *Module, decl_index: Module.Decl.Index) !void { if (!build_options.have_llvm) return; - return self.llvm_object.updateDecl(module, decl); + return self.llvm_object.updateDecl(module, decl_index); } pub fn updateDeclExports( self: *NvPtx, module: *Module, - decl: *const Module.Decl, + decl_index: Module.Decl.Index, exports: []const *Module.Export, ) !void { if (!build_options.have_llvm) return; if (build_options.skip_non_native and builtin.object_format != .nvptx) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - return self.llvm_object.updateDeclExports(module, decl, exports); + return self.llvm_object.updateDeclExports(module, decl_index, exports); } -pub fn freeDecl(self: *NvPtx, decl: *Module.Decl) void { +pub fn freeDecl(self: *NvPtx, decl_index: Module.Decl.Index) void { if (!build_options.have_llvm) return; - return self.llvm_object.freeDecl(decl); + return self.llvm_object.freeDecl(decl_index); } pub fn flush(self: *NvPtx, comp: *Compilation, prog_node: *std.Progress.Node) !void { diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index 5d740dd2b9..16f7841c2d 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -59,9 +59,9 @@ path_arena: std.heap.ArenaAllocator, /// If we group the decls by file, it makes it really easy to do this (put the symbol in the correct place) fn_decl_table: std.AutoArrayHashMapUnmanaged( *Module.File, - struct { sym_index: u32, functions: std.AutoArrayHashMapUnmanaged(*Module.Decl, FnDeclOutput) = .{} }, + struct { sym_index: u32, functions: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, FnDeclOutput) = .{} }, ) = .{}, -data_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{}, +data_decl_table: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, []const u8) = .{}, hdr: aout.ExecHdr = undefined, @@ -162,11 +162,13 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Plan9 { return self; } -fn putFn(self: *Plan9, decl: *Module.Decl, out: FnDeclOutput) !void { +fn putFn(self: *Plan9, decl_index: Module.Decl.Index, out: FnDeclOutput) !void { const gpa = self.base.allocator; + const mod = self.base.options.module.?; + const decl = mod.declPtr(decl_index); const fn_map_res = try self.fn_decl_table.getOrPut(gpa, decl.getFileScope()); if (fn_map_res.found_existing) { - try fn_map_res.value_ptr.functions.put(gpa, decl, out); + try fn_map_res.value_ptr.functions.put(gpa, decl_index, out); } else { const file = decl.getFileScope(); const arena = self.path_arena.allocator(); @@ -178,7 +180,7 @@ fn putFn(self: *Plan9, decl: *Module.Decl, out: FnDeclOutput) !void { break :blk @intCast(u32, self.syms.items.len - 1); }, }; - try fn_map_res.value_ptr.functions.put(gpa, decl, out); + try fn_map_res.value_ptr.functions.put(gpa, decl_index, out); var a = std.ArrayList(u8).init(arena); errdefer a.deinit(); @@ -229,9 +231,10 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv @panic("Attempted to compile for object format that was disabled by build configuration"); } - const decl = func.owner_decl; + const decl_index = func.owner_decl; + const decl = module.declPtr(decl_index); - try self.seeDecl(decl); + try self.seeDecl(decl_index); log.debug("codegen decl {*} ({s})", .{ decl, decl.name }); var code_buffer = std.ArrayList(u8).init(self.base.allocator); @@ -262,7 +265,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv .appended => code_buffer.toOwnedSlice(), .fail => |em| { decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, em); + try module.failed_decls.put(module.gpa, decl_index, em); return; }, }; @@ -272,19 +275,21 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv .start_line = start_line.?, .end_line = end_line, }; - try self.putFn(decl, out); + try self.putFn(decl_index, out); return self.updateFinish(decl); } -pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl: *Module.Decl) !u32 { +pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.Index) !u32 { _ = self; _ = tv; - _ = decl; + _ = decl_index; log.debug("TODO lowerUnnamedConst for Plan9", .{}); return error.AnalysisFail; } -pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void { +pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index) !void { + const decl = module.declPtr(decl_index); + if (decl.val.tag() == .extern_fn) { return; // TODO Should we do more when front-end analyzed extern decl? } @@ -295,7 +300,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void { } } - try self.seeDecl(decl); + try self.seeDecl(decl_index); log.debug("codegen decl {*} ({s})", .{ decl, decl.name }); @@ -315,13 +320,13 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void { .appended => code_buffer.items, .fail => |em| { decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, em); + try module.failed_decls.put(module.gpa, decl_index, em); return; }, }; var duped_code = try self.base.allocator.dupe(u8, code); errdefer self.base.allocator.free(duped_code); - try self.data_decl_table.put(self.base.allocator, decl, duped_code); + try self.data_decl_table.put(self.base.allocator, decl_index, duped_code); return self.updateFinish(decl); } /// called at the end of update{Decl,Func} @@ -435,7 +440,8 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No while (it_file.next()) |fentry| { var it = fentry.value_ptr.functions.iterator(); while (it.next()) |entry| { - const decl = entry.key_ptr.*; + const decl_index = entry.key_ptr.*; + const decl = mod.declPtr(decl_index); const out = entry.value_ptr.*; log.debug("write text decl {*} ({s}), lines {d} to {d}", .{ decl, decl.name, out.start_line + 1, out.end_line }); { @@ -462,7 +468,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); } self.syms.items[decl.link.plan9.sym_index.?].value = off; - if (mod.decl_exports.get(decl)) |exports| { + if (mod.decl_exports.get(decl_index)) |exports| { try self.addDeclExports(mod, decl, exports); } } @@ -482,7 +488,8 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No { var it = self.data_decl_table.iterator(); while (it.next()) |entry| { - const decl = entry.key_ptr.*; + const decl_index = entry.key_ptr.*; + const decl = mod.declPtr(decl_index); const code = entry.value_ptr.*; log.debug("write data decl {*} ({s})", .{ decl, decl.name }); @@ -498,7 +505,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); } self.syms.items[decl.link.plan9.sym_index.?].value = off; - if (mod.decl_exports.get(decl)) |exports| { + if (mod.decl_exports.get(decl_index)) |exports| { try self.addDeclExports(mod, decl, exports); } } @@ -564,24 +571,25 @@ fn addDeclExports( } } -pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void { +pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void { // TODO audit the lifetimes of decls table entries. It's possible to get // allocateDeclIndexes and then freeDecl without any updateDecl in between. // However that is planned to change, see the TODO comment in Module.zig // in the deleteUnusedDecl function. + const mod = self.base.options.module.?; + const decl = mod.declPtr(decl_index); const is_fn = (decl.val.tag() == .function); if (is_fn) { - var symidx_and_submap = - self.fn_decl_table.get(decl.getFileScope()).?; + var symidx_and_submap = self.fn_decl_table.get(decl.getFileScope()).?; var submap = symidx_and_submap.functions; - _ = submap.swapRemove(decl); + _ = submap.swapRemove(decl_index); if (submap.count() == 0) { self.syms.items[symidx_and_submap.sym_index] = aout.Sym.undefined_symbol; self.syms_index_free_list.append(self.base.allocator, symidx_and_submap.sym_index) catch {}; submap.deinit(self.base.allocator); } } else { - _ = self.data_decl_table.swapRemove(decl); + _ = self.data_decl_table.swapRemove(decl_index); } if (decl.link.plan9.got_index) |i| { // TODO: if this catch {} is triggered, an assertion in flushModule will be triggered, because got_index_free_list will have the wrong length @@ -593,7 +601,9 @@ pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void { } } -pub fn seeDecl(self: *Plan9, decl: *Module.Decl) !void { +pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !void { + const mod = self.base.options.module.?; + const decl = mod.declPtr(decl_index); if (decl.link.plan9.got_index == null) { if (self.got_index_free_list.popOrNull()) |i| { decl.link.plan9.got_index = i; @@ -607,14 +617,13 @@ pub fn seeDecl(self: *Plan9, decl: *Module.Decl) !void { pub fn updateDeclExports( self: *Plan9, module: *Module, - decl: *Module.Decl, + decl_index: Module.Decl.Index, exports: []const *Module.Export, ) !void { - try self.seeDecl(decl); + try self.seeDecl(decl_index); // we do all the things in flush _ = self; _ = module; - _ = decl; _ = exports; } pub fn deinit(self: *Plan9) void { @@ -709,14 +718,18 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { }); } } + + const mod = self.base.options.module.?; + // write the data symbols { var it = self.data_decl_table.iterator(); while (it.next()) |entry| { - const decl = entry.key_ptr.*; + const decl_index = entry.key_ptr.*; + const decl = mod.declPtr(decl_index); const sym = self.syms.items[decl.link.plan9.sym_index.?]; try self.writeSym(writer, sym); - if (self.base.options.module.?.decl_exports.get(decl)) |exports| { + if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| { for (exports) |e| { try self.writeSym(writer, self.syms.items[e.link.plan9.?]); } @@ -737,10 +750,11 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { // write all the decls come from the file of the z symbol var submap_it = symidx_and_submap.functions.iterator(); while (submap_it.next()) |entry| { - const decl = entry.key_ptr.*; + const decl_index = entry.key_ptr.*; + const decl = mod.declPtr(decl_index); const sym = self.syms.items[decl.link.plan9.sym_index.?]; try self.writeSym(writer, sym); - if (self.base.options.module.?.decl_exports.get(decl)) |exports| { + if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| { for (exports) |e| { const s = self.syms.items[e.link.plan9.?]; if (mem.eql(u8, s.name, "_start")) @@ -754,12 +768,18 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { } /// this will be removed, moved to updateFinish -pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void { +pub fn allocateDeclIndexes(self: *Plan9, decl_index: Module.Decl.Index) !void { _ = self; - _ = decl; + _ = decl_index; } -pub fn getDeclVAddr(self: *Plan9, decl: *const Module.Decl, reloc_info: link.File.RelocInfo) !u64 { +pub fn getDeclVAddr( + self: *Plan9, + decl_index: Module.Decl.Index, + reloc_info: link.File.RelocInfo, +) !u64 { _ = reloc_info; + const mod = self.base.options.module.?; + const decl = mod.declPtr(decl_index); if (decl.ty.zigTypeTag() == .Fn) { var start = self.bases.text; var it_file = self.fn_decl_table.iterator(); @@ -767,7 +787,7 @@ pub fn getDeclVAddr(self: *Plan9, decl: *const Module.Decl, reloc_info: link.Fil var symidx_and_submap = fentry.value_ptr; var submap_it = symidx_and_submap.functions.iterator(); while (submap_it.next()) |entry| { - if (entry.key_ptr.* == decl) return start; + if (entry.key_ptr.* == decl_index) return start; start += entry.value_ptr.code.len; } } @@ -776,7 +796,7 @@ pub fn getDeclVAddr(self: *Plan9, decl: *const Module.Decl, reloc_info: link.Fil var start = self.bases.data + self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8; var it = self.data_decl_table.iterator(); while (it.next()) |kv| { - if (decl == kv.key_ptr.*) return start; + if (decl_index == kv.key_ptr.*) return start; start += kv.value_ptr.len; } unreachable; diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index e4d032539f..e295dceb55 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -54,7 +54,7 @@ base: link.File, /// This linker backend does not try to incrementally link output SPIR-V code. /// Instead, it tracks all declarations in this table, and iterates over it /// in the flush function. -decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, DeclGenContext) = .{}, +decl_table: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclGenContext) = .{}, const DeclGenContext = struct { air: Air, @@ -145,29 +145,31 @@ pub fn updateFunc(self: *SpirV, module: *Module, func: *Module.Fn, air: Air, liv }; } -pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void { +pub fn updateDecl(self: *SpirV, module: *Module, decl_index: Module.Decl.Index) !void { if (build_options.skip_non_native) { @panic("Attempted to compile for architecture that was disabled by build configuration"); } _ = module; // Keep track of all decls so we can iterate over them on flush(). - _ = try self.decl_table.getOrPut(self.base.allocator, decl); + _ = try self.decl_table.getOrPut(self.base.allocator, decl_index); } pub fn updateDeclExports( self: *SpirV, module: *Module, - decl: *const Module.Decl, + decl_index: Module.Decl.Index, exports: []const *Module.Export, ) !void { _ = self; _ = module; - _ = decl; + _ = decl_index; _ = exports; } -pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void { - const index = self.decl_table.getIndex(decl).?; +pub fn freeDecl(self: *SpirV, decl_index: Module.Decl.Index) void { + const index = self.decl_table.getIndex(decl_index).?; + const module = self.base.options.module.?; + const decl = module.declPtr(decl_index); if (decl.val.tag() == .function) { self.decl_table.values()[index].deinit(self.base.allocator); } @@ -208,7 +210,8 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No // TODO: We're allocating an ID unconditionally now, are there // declarations which don't generate a result? // TODO: fn_link is used here, but thats probably not the right field. It will work anyway though. - for (self.decl_table.keys()) |decl| { + for (self.decl_table.keys()) |decl_index| { + const decl = module.declPtr(decl_index); if (decl.has_tv) { decl.fn_link.spirv.id = spv.allocId(); } @@ -220,7 +223,8 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No var it = self.decl_table.iterator(); while (it.next()) |entry| { - const decl = entry.key_ptr.*; + const decl_index = entry.key_ptr.*; + const decl = module.declPtr(decl_index); if (!decl.has_tv) continue; const air = entry.value_ptr.air; @@ -228,7 +232,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No // Note, if `decl` is not a function, air/liveness may be undefined. if (try decl_gen.gen(decl, air, liveness)) |msg| { - try module.failed_decls.put(module.gpa, decl, msg); + try module.failed_decls.put(module.gpa, decl_index, msg); return; // TODO: Attempt to generate more decls? } } diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 4f72dfe388..fad7543b0e 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -48,7 +48,7 @@ host_name: []const u8 = "env", /// List of all `Decl` that are currently alive. /// This is ment for bookkeeping so we can safely cleanup all codegen memory /// when calling `deinit` -decls: std.AutoHashMapUnmanaged(*Module.Decl, void) = .{}, +decls: std.AutoHashMapUnmanaged(Module.Decl.Index, void) = .{}, /// List of all symbols generated by Zig code. symbols: std.ArrayListUnmanaged(Symbol) = .{}, /// List of symbol indexes which are free to be used. @@ -429,9 +429,11 @@ pub fn deinit(self: *Wasm) void { if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa); } + const mod = self.base.options.module.?; var decl_it = self.decls.keyIterator(); - while (decl_it.next()) |decl_ptr| { - decl_ptr.*.link.wasm.deinit(gpa); + while (decl_it.next()) |decl_index_ptr| { + const decl = mod.declPtr(decl_index_ptr.*); + decl.link.wasm.deinit(gpa); } for (self.func_types.items) |*func_type| { @@ -476,12 +478,13 @@ pub fn deinit(self: *Wasm) void { self.string_table.deinit(gpa); } -pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void { +pub fn allocateDeclIndexes(self: *Wasm, decl_index: Module.Decl.Index) !void { if (self.llvm_object) |_| return; + const decl = self.base.options.module.?.declPtr(decl_index); if (decl.link.wasm.sym_index != 0) return; try self.symbols.ensureUnusedCapacity(self.base.allocator, 1); - try self.decls.putNoClobber(self.base.allocator, decl, {}); + try self.decls.putNoClobber(self.base.allocator, decl_index, {}); const atom = &decl.link.wasm; @@ -502,14 +505,15 @@ pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void { try self.symbol_atom.putNoClobber(self.base.allocator, atom.symbolLoc(), atom); } -pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { +pub fn updateFunc(self: *Wasm, mod: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { if (build_options.skip_non_native and builtin.object_format != .wasm) { @panic("Attempted to compile for object format that was disabled by build configuration"); } if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(module, func, air, liveness); + if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func, air, liveness); } - const decl = func.owner_decl; + const decl_index = func.owner_decl; + const decl = mod.declPtr(decl_index); assert(decl.link.wasm.sym_index != 0); // Must call allocateDeclIndexes() decl.link.wasm.clear(); @@ -530,7 +534,7 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live .appended => code_writer.items, .fail => |em| { decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, em); + try mod.failed_decls.put(mod.gpa, decl_index, em); return; }, }; @@ -540,14 +544,15 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live // Generate code for the Decl, storing it in memory to be later written to // the file on flush(). -pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { +pub fn updateDecl(self: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !void { if (build_options.skip_non_native and builtin.object_format != .wasm) { @panic("Attempted to compile for object format that was disabled by build configuration"); } if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl); + if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index); } + const decl = mod.declPtr(decl_index); assert(decl.link.wasm.sym_index != 0); // Must call allocateDeclIndexes() decl.link.wasm.clear(); @@ -580,7 +585,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { .appended => code_writer.items, .fail => |em| { decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, em); + try mod.failed_decls.put(mod.gpa, decl_index, em); return; }, }; @@ -590,12 +595,13 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void { if (code.len == 0) return; + const mod = self.base.options.module.?; const atom: *Atom = &decl.link.wasm; atom.size = @intCast(u32, code.len); atom.alignment = decl.ty.abiAlignment(self.base.options.target); const symbol = &self.symbols.items[atom.sym_index]; - const full_name = try decl.getFullyQualifiedName(self.base.allocator); + const full_name = try decl.getFullyQualifiedName(mod); defer self.base.allocator.free(full_name); symbol.name = try self.string_table.put(self.base.allocator, full_name); try atom.code.appendSlice(self.base.allocator, code); @@ -606,12 +612,15 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void { /// Lowers a constant typed value to a local symbol and atom. /// Returns the symbol index of the local /// The given `decl` is the parent decl whom owns the constant. -pub fn lowerUnnamedConst(self: *Wasm, decl: *Module.Decl, tv: TypedValue) !u32 { +pub fn lowerUnnamedConst(self: *Wasm, tv: TypedValue, decl_index: Module.Decl.Index) !u32 { assert(tv.ty.zigTypeTag() != .Fn); // cannot create local symbols for functions + const mod = self.base.options.module.?; + const decl = mod.declPtr(decl_index); + // Create and initialize a new local symbol and atom const local_index = decl.link.wasm.locals.items.len; - const fqdn = try decl.getFullyQualifiedName(self.base.allocator); + const fqdn = try decl.getFullyQualifiedName(mod); defer self.base.allocator.free(fqdn); const name = try std.fmt.allocPrintZ(self.base.allocator, "__unnamed_{s}_{d}", .{ fqdn, local_index }); defer self.base.allocator.free(name); @@ -641,7 +650,6 @@ pub fn lowerUnnamedConst(self: *Wasm, decl: *Module.Decl, tv: TypedValue) !u32 { var value_bytes = std.ArrayList(u8).init(self.base.allocator); defer value_bytes.deinit(); - const module = self.base.options.module.?; const result = try codegen.generateSymbol( &self.base, decl.srcLoc(), @@ -658,7 +666,7 @@ pub fn lowerUnnamedConst(self: *Wasm, decl: *Module.Decl, tv: TypedValue) !u32 { .appended => value_bytes.items, .fail => |em| { decl.analysis = .codegen_failure; - try module.failed_decls.put(module.gpa, decl, em); + try mod.failed_decls.put(mod.gpa, decl_index, em); return error.AnalysisFail; }, }; @@ -672,9 +680,11 @@ pub fn lowerUnnamedConst(self: *Wasm, decl: *Module.Decl, tv: TypedValue) !u32 { /// Returns the given pointer address pub fn getDeclVAddr( self: *Wasm, - decl: *const Module.Decl, + decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo, ) !u64 { + const mod = self.base.options.module.?; + const decl = mod.declPtr(decl_index); const target_symbol_index = decl.link.wasm.sym_index; assert(target_symbol_index != 0); assert(reloc_info.parent_atom_index != 0); @@ -722,21 +732,23 @@ pub fn deleteExport(self: *Wasm, exp: Export) void { pub fn updateDeclExports( self: *Wasm, - module: *Module, - decl: *const Module.Decl, + mod: *Module, + decl_index: Module.Decl.Index, exports: []const *Module.Export, ) !void { if (build_options.skip_non_native and builtin.object_format != .wasm) { @panic("Attempted to compile for object format that was disabled by build configuration"); } if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports); + if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(mod, decl_index, exports); } + const decl = mod.declPtr(decl_index); + for (exports) |exp| { if (exp.options.section) |section| { - try module.failed_exports.putNoClobber(module.gpa, exp, try Module.ErrorMsg.create( - module.gpa, + try mod.failed_exports.putNoClobber(mod.gpa, exp, try Module.ErrorMsg.create( + mod.gpa, decl.srcLoc(), "Unimplemented: ExportOptions.section '{s}'", .{section}, @@ -754,8 +766,8 @@ pub fn updateDeclExports( // are strong symbols, we have a linker error. // In the other case we replace one with the other. if (!exp_is_weak and !existing_sym.isWeak()) { - try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create( - module.gpa, + try mod.failed_exports.put(mod.gpa, exp, try Module.ErrorMsg.create( + mod.gpa, decl.srcLoc(), \\LinkError: symbol '{s}' defined multiple times \\ first definition in '{s}' @@ -773,8 +785,9 @@ pub fn updateDeclExports( } } - const sym_index = exp.exported_decl.link.wasm.sym_index; - const sym_loc = exp.exported_decl.link.wasm.symbolLoc(); + const exported_decl = mod.declPtr(exp.exported_decl); + const sym_index = exported_decl.link.wasm.sym_index; + const sym_loc = exported_decl.link.wasm.symbolLoc(); const symbol = sym_loc.getSymbol(self); switch (exp.options.linkage) { .Internal => { @@ -786,8 +799,8 @@ pub fn updateDeclExports( }, .Strong => {}, // symbols are strong by default .LinkOnce => { - try module.failed_exports.putNoClobber(module.gpa, exp, try Module.ErrorMsg.create( - module.gpa, + try mod.failed_exports.putNoClobber(mod.gpa, exp, try Module.ErrorMsg.create( + mod.gpa, decl.srcLoc(), "Unimplemented: LinkOnce", .{}, @@ -813,13 +826,15 @@ pub fn updateDeclExports( } } -pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { +pub fn freeDecl(self: *Wasm, decl_index: Module.Decl.Index) void { if (build_options.have_llvm) { - if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl); + if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); } + const mod = self.base.options.module.?; + const decl = mod.declPtr(decl_index); const atom = &decl.link.wasm; self.symbols_free_list.append(self.base.allocator, atom.sym_index) catch {}; - _ = self.decls.remove(decl); + _ = self.decls.remove(decl_index); self.symbols.items[atom.sym_index].tag = .dead; for (atom.locals.items) |local_atom| { const local_symbol = &self.symbols.items[local_atom.sym_index]; @@ -1414,8 +1429,8 @@ fn populateErrorNameTable(self: *Wasm) !void { // Addend for each relocation to the table var addend: u32 = 0; - const module = self.base.options.module.?; - for (module.error_name_list.items) |error_name| { + const mod = self.base.options.module.?; + for (mod.error_name_list.items) |error_name| { const len = @intCast(u32, error_name.len + 1); // names are 0-termianted const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); @@ -1456,9 +1471,11 @@ fn resetState(self: *Wasm) void { for (self.segment_info.items) |*segment_info| { self.base.allocator.free(segment_info.name); } + const mod = self.base.options.module.?; var decl_it = self.decls.keyIterator(); - while (decl_it.next()) |decl| { - const atom = &decl.*.link.wasm; + while (decl_it.next()) |decl_index_ptr| { + const decl = mod.declPtr(decl_index_ptr.*); + const atom = &decl.link.wasm; atom.next = null; atom.prev = null; @@ -1546,12 +1563,14 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod defer self.resetState(); try self.setupStart(); try self.setupImports(); + const mod = self.base.options.module.?; var decl_it = self.decls.keyIterator(); - while (decl_it.next()) |decl| { - if (decl.*.isExtern()) continue; + while (decl_it.next()) |decl_index_ptr| { + const decl = mod.declPtr(decl_index_ptr.*); + if (decl.isExtern()) continue; const atom = &decl.*.link.wasm; - if (decl.*.ty.zigTypeTag() == .Fn) { - try self.parseAtom(atom, .{ .function = decl.*.fn_link.wasm }); + if (decl.ty.zigTypeTag() == .Fn) { + try self.parseAtom(atom, .{ .function = decl.fn_link.wasm }); } else { try self.parseAtom(atom, .data); } @@ -2045,7 +2064,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. - const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { + const module_obj_path: ?[]const u8 = if (self.base.options.module) |mod| blk: { const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; if (use_stage1) { const obj_basename = try std.zig.binNameAlloc(arena, .{ @@ -2054,7 +2073,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! .output_mode = .Obj, }); switch (self.base.options.cache_mode) { - .incremental => break :blk try module.zig_cache_artifact_directory.join( + .incremental => break :blk try mod.zig_cache_artifact_directory.join( arena, &[_][]const u8{obj_basename}, ), @@ -2253,7 +2272,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! } if (auto_export_symbols) { - if (self.base.options.module) |module| { + if (self.base.options.module) |mod| { // when we use stage1, we use the exports that stage1 provided us. // For stage2, we can directly retrieve them from the module. const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; @@ -2264,14 +2283,15 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! } else { const skip_export_non_fn = target.os.tag == .wasi and self.base.options.wasi_exec_model == .command; - for (module.decl_exports.values()) |exports| { + for (mod.decl_exports.values()) |exports| { for (exports) |exprt| { - if (skip_export_non_fn and exprt.exported_decl.ty.zigTypeTag() != .Fn) { + const exported_decl = mod.declPtr(exprt.exported_decl); + if (skip_export_non_fn and exported_decl.ty.zigTypeTag() != .Fn) { // skip exporting symbols when we're building a WASI command // and the symbol is not a function continue; } - const symbol_name = exprt.exported_decl.name; + const symbol_name = exported_decl.name; const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name}); try argv.append(arg); } diff --git a/src/main.zig b/src/main.zig index 84a69b98f1..e47ff0e272 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3892,7 +3892,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void .tree_loaded = true, .zir = undefined, .pkg = undefined, - .root_decl = null, + .root_decl = .none, }; file.pkg = try Package.create(gpa, null, file.sub_file_path); @@ -4098,7 +4098,7 @@ fn fmtPathFile( .tree_loaded = true, .zir = undefined, .pkg = undefined, - .root_decl = null, + .root_decl = .none, }; file.pkg = try Package.create(fmt.gpa, null, file.sub_file_path); @@ -4757,7 +4757,7 @@ pub fn cmdAstCheck( .tree = undefined, .zir = undefined, .pkg = undefined, - .root_decl = null, + .root_decl = .none, }; if (zig_source_file) |file_name| { var f = fs.cwd().openFile(file_name, .{}) catch |err| { @@ -4910,7 +4910,7 @@ pub fn cmdChangelist( .tree = undefined, .zir = undefined, .pkg = undefined, - .root_decl = null, + .root_decl = .none, }; file.pkg = try Package.create(gpa, null, file.sub_file_path); diff --git a/src/print_air.zig b/src/print_air.zig index 8a1a8fa950..27d222f262 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -7,7 +7,7 @@ const Value = @import("value.zig").Value; const Air = @import("Air.zig"); const Liveness = @import("Liveness.zig"); -pub fn dump(gpa: Allocator, air: Air, liveness: Liveness) void { +pub fn dump(module: *Module, air: Air, liveness: Liveness) void { const instruction_bytes = air.instructions.len * // Here we don't use @sizeOf(Air.Inst.Data) because it would include // the debug safety tag but we want to measure release size. @@ -41,11 +41,12 @@ pub fn dump(gpa: Allocator, air: Air, liveness: Liveness) void { liveness.special.count(), fmtIntSizeBin(liveness_special_bytes), }); // zig fmt: on - var arena = std.heap.ArenaAllocator.init(gpa); + var arena = std.heap.ArenaAllocator.init(module.gpa); defer arena.deinit(); var writer: Writer = .{ - .gpa = gpa, + .module = module, + .gpa = module.gpa, .arena = arena.allocator(), .air = air, .liveness = liveness, @@ -58,6 +59,7 @@ pub fn dump(gpa: Allocator, air: Air, liveness: Liveness) void { } const Writer = struct { + module: *Module, gpa: Allocator, arena: Allocator, air: Air, @@ -591,7 +593,8 @@ const Writer = struct { fn writeDbgInline(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; const function = w.air.values[ty_pl.payload].castTag(.function).?.data; - try s.print("{s}", .{function.owner_decl.name}); + const owner_decl = w.module.declPtr(function.owner_decl); + try s.print("{s}", .{owner_decl.name}); } fn writeDbgVar(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { diff --git a/src/type.zig b/src/type.zig index 72cf2cd534..c96c512d9a 100644 --- a/src/type.zig +++ b/src/type.zig @@ -521,7 +521,7 @@ pub const Type = extern union { } } - pub fn eql(a: Type, b: Type, target: Target) bool { + pub fn eql(a: Type, b: Type, mod: *Module) bool { // As a shortcut, if the small tags / addresses match, we're done. if (a.tag_if_small_enough == b.tag_if_small_enough) return true; @@ -637,7 +637,7 @@ pub const Type = extern union { const a_info = a.fnInfo(); const b_info = b.fnInfo(); - if (!eql(a_info.return_type, b_info.return_type, target)) + if (!eql(a_info.return_type, b_info.return_type, mod)) return false; if (a_info.cc != b_info.cc) @@ -663,7 +663,7 @@ pub const Type = extern union { if (a_param_ty.tag() == .generic_poison) continue; if (b_param_ty.tag() == .generic_poison) continue; - if (!eql(a_param_ty, b_param_ty, target)) + if (!eql(a_param_ty, b_param_ty, mod)) return false; } @@ -681,13 +681,13 @@ pub const Type = extern union { if (a.arrayLen() != b.arrayLen()) return false; const elem_ty = a.elemType(); - if (!elem_ty.eql(b.elemType(), target)) + if (!elem_ty.eql(b.elemType(), mod)) return false; const sentinel_a = a.sentinel(); const sentinel_b = b.sentinel(); if (sentinel_a) |sa| { if (sentinel_b) |sb| { - return sa.eql(sb, elem_ty, target); + return sa.eql(sb, elem_ty, mod); } else { return false; } @@ -718,7 +718,7 @@ pub const Type = extern union { const info_a = a.ptrInfo().data; const info_b = b.ptrInfo().data; - if (!info_a.pointee_type.eql(info_b.pointee_type, target)) + if (!info_a.pointee_type.eql(info_b.pointee_type, mod)) return false; if (info_a.@"align" != info_b.@"align") return false; @@ -741,7 +741,7 @@ pub const Type = extern union { const sentinel_b = info_b.sentinel; if (sentinel_a) |sa| { if (sentinel_b) |sb| { - if (!sa.eql(sb, info_a.pointee_type, target)) + if (!sa.eql(sb, info_a.pointee_type, mod)) return false; } else { return false; @@ -762,7 +762,7 @@ pub const Type = extern union { var buf_a: Payload.ElemType = undefined; var buf_b: Payload.ElemType = undefined; - return a.optionalChild(&buf_a).eql(b.optionalChild(&buf_b), target); + return a.optionalChild(&buf_a).eql(b.optionalChild(&buf_b), mod); }, .anyerror_void_error_union, .error_union => { @@ -770,18 +770,18 @@ pub const Type = extern union { const a_set = a.errorUnionSet(); const b_set = b.errorUnionSet(); - if (!a_set.eql(b_set, target)) return false; + if (!a_set.eql(b_set, mod)) return false; const a_payload = a.errorUnionPayload(); const b_payload = b.errorUnionPayload(); - if (!a_payload.eql(b_payload, target)) return false; + if (!a_payload.eql(b_payload, mod)) return false; return true; }, .anyframe_T => { if (b.zigTypeTag() != .AnyFrame) return false; - return a.childType().eql(b.childType(), target); + return a.childType().eql(b.childType(), mod); }, .empty_struct => { @@ -804,7 +804,7 @@ pub const Type = extern union { for (a_tuple.types) |a_ty, i| { const b_ty = b_tuple.types[i]; - if (!eql(a_ty, b_ty, target)) return false; + if (!eql(a_ty, b_ty, mod)) return false; } for (a_tuple.values) |a_val, i| { @@ -820,7 +820,7 @@ pub const Type = extern union { if (b_val.tag() == .unreachable_value) { return false; } else { - if (!Value.eql(a_val, b_val, ty, target)) return false; + if (!Value.eql(a_val, b_val, ty, mod)) return false; } } } @@ -840,7 +840,7 @@ pub const Type = extern union { for (a_struct_obj.types) |a_ty, i| { const b_ty = b_struct_obj.types[i]; - if (!eql(a_ty, b_ty, target)) return false; + if (!eql(a_ty, b_ty, mod)) return false; } for (a_struct_obj.values) |a_val, i| { @@ -856,7 +856,7 @@ pub const Type = extern union { if (b_val.tag() == .unreachable_value) { return false; } else { - if (!Value.eql(a_val, b_val, ty, target)) return false; + if (!Value.eql(a_val, b_val, ty, mod)) return false; } } } @@ -911,13 +911,13 @@ pub const Type = extern union { } } - pub fn hash(self: Type, target: Target) u64 { + pub fn hash(self: Type, mod: *Module) u64 { var hasher = std.hash.Wyhash.init(0); - self.hashWithHasher(&hasher, target); + self.hashWithHasher(&hasher, mod); return hasher.final(); } - pub fn hashWithHasher(ty: Type, hasher: *std.hash.Wyhash, target: Target) void { + pub fn hashWithHasher(ty: Type, hasher: *std.hash.Wyhash, mod: *Module) void { switch (ty.tag()) { .generic_poison => unreachable, @@ -1036,7 +1036,7 @@ pub const Type = extern union { std.hash.autoHash(hasher, std.builtin.TypeId.Fn); const fn_info = ty.fnInfo(); - hashWithHasher(fn_info.return_type, hasher, target); + hashWithHasher(fn_info.return_type, hasher, mod); std.hash.autoHash(hasher, fn_info.alignment); std.hash.autoHash(hasher, fn_info.cc); std.hash.autoHash(hasher, fn_info.is_var_args); @@ -1046,7 +1046,7 @@ pub const Type = extern union { for (fn_info.param_types) |param_ty, i| { std.hash.autoHash(hasher, fn_info.paramIsComptime(i)); if (param_ty.tag() == .generic_poison) continue; - hashWithHasher(param_ty, hasher, target); + hashWithHasher(param_ty, hasher, mod); } }, @@ -1059,8 +1059,8 @@ pub const Type = extern union { const elem_ty = ty.elemType(); std.hash.autoHash(hasher, ty.arrayLen()); - hashWithHasher(elem_ty, hasher, target); - hashSentinel(ty.sentinel(), elem_ty, hasher, target); + hashWithHasher(elem_ty, hasher, mod); + hashSentinel(ty.sentinel(), elem_ty, hasher, mod); }, .vector => { @@ -1068,7 +1068,7 @@ pub const Type = extern union { const elem_ty = ty.elemType(); std.hash.autoHash(hasher, ty.vectorLen()); - hashWithHasher(elem_ty, hasher, target); + hashWithHasher(elem_ty, hasher, mod); }, .single_const_pointer_to_comptime_int, @@ -1092,8 +1092,8 @@ pub const Type = extern union { std.hash.autoHash(hasher, std.builtin.TypeId.Pointer); const info = ty.ptrInfo().data; - hashWithHasher(info.pointee_type, hasher, target); - hashSentinel(info.sentinel, info.pointee_type, hasher, target); + hashWithHasher(info.pointee_type, hasher, mod); + hashSentinel(info.sentinel, info.pointee_type, hasher, mod); std.hash.autoHash(hasher, info.@"align"); std.hash.autoHash(hasher, info.@"addrspace"); std.hash.autoHash(hasher, info.bit_offset); @@ -1111,22 +1111,22 @@ pub const Type = extern union { std.hash.autoHash(hasher, std.builtin.TypeId.Optional); var buf: Payload.ElemType = undefined; - hashWithHasher(ty.optionalChild(&buf), hasher, target); + hashWithHasher(ty.optionalChild(&buf), hasher, mod); }, .anyerror_void_error_union, .error_union => { std.hash.autoHash(hasher, std.builtin.TypeId.ErrorUnion); const set_ty = ty.errorUnionSet(); - hashWithHasher(set_ty, hasher, target); + hashWithHasher(set_ty, hasher, mod); const payload_ty = ty.errorUnionPayload(); - hashWithHasher(payload_ty, hasher, target); + hashWithHasher(payload_ty, hasher, mod); }, .anyframe_T => { std.hash.autoHash(hasher, std.builtin.TypeId.AnyFrame); - hashWithHasher(ty.childType(), hasher, target); + hashWithHasher(ty.childType(), hasher, mod); }, .empty_struct => { @@ -1145,10 +1145,10 @@ pub const Type = extern union { std.hash.autoHash(hasher, tuple.types.len); for (tuple.types) |field_ty, i| { - hashWithHasher(field_ty, hasher, target); + hashWithHasher(field_ty, hasher, mod); const field_val = tuple.values[i]; if (field_val.tag() == .unreachable_value) continue; - field_val.hash(field_ty, hasher, target); + field_val.hash(field_ty, hasher, mod); } }, .anon_struct => { @@ -1160,9 +1160,9 @@ pub const Type = extern union { const field_name = struct_obj.names[i]; const field_val = struct_obj.values[i]; hasher.update(field_name); - hashWithHasher(field_ty, hasher, target); + hashWithHasher(field_ty, hasher, mod); if (field_val.tag() == .unreachable_value) continue; - field_val.hash(field_ty, hasher, target); + field_val.hash(field_ty, hasher, mod); } }, @@ -1210,35 +1210,35 @@ pub const Type = extern union { } } - fn hashSentinel(opt_val: ?Value, ty: Type, hasher: *std.hash.Wyhash, target: Target) void { + fn hashSentinel(opt_val: ?Value, ty: Type, hasher: *std.hash.Wyhash, mod: *Module) void { if (opt_val) |s| { std.hash.autoHash(hasher, true); - s.hash(ty, hasher, target); + s.hash(ty, hasher, mod); } else { std.hash.autoHash(hasher, false); } } pub const HashContext64 = struct { - target: Target, + mod: *Module, pub fn hash(self: @This(), t: Type) u64 { - return t.hash(self.target); + return t.hash(self.mod); } pub fn eql(self: @This(), a: Type, b: Type) bool { - return a.eql(b, self.target); + return a.eql(b, self.mod); } }; pub const HashContext32 = struct { - target: Target, + mod: *Module, pub fn hash(self: @This(), t: Type) u32 { - return @truncate(u32, t.hash(self.target)); + return @truncate(u32, t.hash(self.mod)); } pub fn eql(self: @This(), a: Type, b: Type, b_index: usize) bool { _ = b_index; - return a.eql(b, self.target); + return a.eql(b, self.mod); } }; @@ -1483,16 +1483,16 @@ pub const Type = extern union { @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()"); } - pub fn fmt(ty: Type, target: Target) std.fmt.Formatter(format2) { + pub fn fmt(ty: Type, module: *Module) std.fmt.Formatter(format2) { return .{ .data = .{ .ty = ty, - .target = target, + .module = module, } }; } const FormatContext = struct { ty: Type, - target: Target, + module: *Module, }; fn format2( @@ -1503,7 +1503,7 @@ pub const Type = extern union { ) !void { comptime assert(unused_format_string.len == 0); _ = options; - return print(ctx.ty, writer, ctx.target); + return print(ctx.ty, writer, ctx.module); } pub fn fmtDebug(ty: Type) std.fmt.Formatter(dump) { @@ -1579,27 +1579,39 @@ pub const Type = extern union { .@"struct" => { const struct_obj = ty.castTag(.@"struct").?.data; - return struct_obj.owner_decl.renderFullyQualifiedName(writer); + return writer.print("({s} decl={d})", .{ + @tagName(t), struct_obj.owner_decl, + }); }, .@"union", .union_tagged => { const union_obj = ty.cast(Payload.Union).?.data; - return union_obj.owner_decl.renderFullyQualifiedName(writer); + return writer.print("({s} decl={d})", .{ + @tagName(t), union_obj.owner_decl, + }); }, .enum_full, .enum_nonexhaustive => { const enum_full = ty.cast(Payload.EnumFull).?.data; - return enum_full.owner_decl.renderFullyQualifiedName(writer); + return writer.print("({s} decl={d})", .{ + @tagName(t), enum_full.owner_decl, + }); }, .enum_simple => { const enum_simple = ty.castTag(.enum_simple).?.data; - return enum_simple.owner_decl.renderFullyQualifiedName(writer); + return writer.print("({s} decl={d})", .{ + @tagName(t), enum_simple.owner_decl, + }); }, .enum_numbered => { const enum_numbered = ty.castTag(.enum_numbered).?.data; - return enum_numbered.owner_decl.renderFullyQualifiedName(writer); + return writer.print("({s} decl={d})", .{ + @tagName(t), enum_numbered.owner_decl, + }); }, .@"opaque" => { - // TODO use declaration name - return writer.writeAll("opaque {}"); + const opaque_obj = ty.castTag(.@"opaque").?.data; + return writer.print("({s} decl={d})", .{ + @tagName(t), opaque_obj.owner_decl, + }); }, .anyerror_void_error_union => return writer.writeAll("anyerror!void"), @@ -1845,7 +1857,9 @@ pub const Type = extern union { }, .error_set_inferred => { const func = ty.castTag(.error_set_inferred).?.data.func; - return writer.print("@typeInfo(@typeInfo(@TypeOf({s})).Fn.return_type.?).ErrorUnion.error_set", .{func.owner_decl.name}); + return writer.print("({s} func={d})", .{ + @tagName(t), func.owner_decl, + }); }, .error_set_merged => { const names = ty.castTag(.error_set_merged).?.data.keys(); @@ -1871,15 +1885,15 @@ pub const Type = extern union { pub const nameAllocArena = nameAlloc; - pub fn nameAlloc(ty: Type, ally: Allocator, target: Target) Allocator.Error![:0]const u8 { + pub fn nameAlloc(ty: Type, ally: Allocator, module: *Module) Allocator.Error![:0]const u8 { var buffer = std.ArrayList(u8).init(ally); defer buffer.deinit(); - try ty.print(buffer.writer(), target); + try ty.print(buffer.writer(), module); return buffer.toOwnedSliceSentinel(0); } /// Prints a name suitable for `@typeName`. - pub fn print(ty: Type, writer: anytype, target: Target) @TypeOf(writer).Error!void { + pub fn print(ty: Type, writer: anytype, mod: *Module) @TypeOf(writer).Error!void { const t = ty.tag(); switch (t) { .inferred_alloc_const => unreachable, @@ -1946,32 +1960,38 @@ pub const Type = extern union { .empty_struct => { const namespace = ty.castTag(.empty_struct).?.data; - try namespace.renderFullyQualifiedName("", writer); + try namespace.renderFullyQualifiedName(mod, "", writer); }, .@"struct" => { const struct_obj = ty.castTag(.@"struct").?.data; - try struct_obj.owner_decl.renderFullyQualifiedName(writer); + const decl = mod.declPtr(struct_obj.owner_decl); + try decl.renderFullyQualifiedName(mod, writer); }, .@"union", .union_tagged => { const union_obj = ty.cast(Payload.Union).?.data; - try union_obj.owner_decl.renderFullyQualifiedName(writer); + const decl = mod.declPtr(union_obj.owner_decl); + try decl.renderFullyQualifiedName(mod, writer); }, .enum_full, .enum_nonexhaustive => { const enum_full = ty.cast(Payload.EnumFull).?.data; - try enum_full.owner_decl.renderFullyQualifiedName(writer); + const decl = mod.declPtr(enum_full.owner_decl); + try decl.renderFullyQualifiedName(mod, writer); }, .enum_simple => { const enum_simple = ty.castTag(.enum_simple).?.data; - try enum_simple.owner_decl.renderFullyQualifiedName(writer); + const decl = mod.declPtr(enum_simple.owner_decl); + try decl.renderFullyQualifiedName(mod, writer); }, .enum_numbered => { const enum_numbered = ty.castTag(.enum_numbered).?.data; - try enum_numbered.owner_decl.renderFullyQualifiedName(writer); + const decl = mod.declPtr(enum_numbered.owner_decl); + try decl.renderFullyQualifiedName(mod, writer); }, .@"opaque" => { const opaque_obj = ty.cast(Payload.Opaque).?.data; - try opaque_obj.owner_decl.renderFullyQualifiedName(writer); + const decl = mod.declPtr(opaque_obj.owner_decl); + try decl.renderFullyQualifiedName(mod, writer); }, .anyerror_void_error_union => try writer.writeAll("anyerror!void"), @@ -1990,7 +2010,8 @@ pub const Type = extern union { const func = ty.castTag(.error_set_inferred).?.data.func; try writer.writeAll("@typeInfo(@typeInfo(@TypeOf("); - try func.owner_decl.renderFullyQualifiedName(writer); + const owner_decl = mod.declPtr(func.owner_decl); + try owner_decl.renderFullyQualifiedName(mod, writer); try writer.writeAll(")).Fn.return_type.?).ErrorUnion.error_set"); }, @@ -1999,7 +2020,7 @@ pub const Type = extern union { try writer.writeAll("fn("); for (fn_info.param_types) |param_ty, i| { if (i != 0) try writer.writeAll(", "); - try print(param_ty, writer, target); + try print(param_ty, writer, mod); } if (fn_info.is_var_args) { if (fn_info.param_types.len != 0) { @@ -2016,14 +2037,14 @@ pub const Type = extern union { if (fn_info.alignment != 0) { try writer.print("align({d}) ", .{fn_info.alignment}); } - try print(fn_info.return_type, writer, target); + try print(fn_info.return_type, writer, mod); }, .error_union => { const error_union = ty.castTag(.error_union).?.data; - try print(error_union.error_set, writer, target); + try print(error_union.error_set, writer, mod); try writer.writeAll("!"); - try print(error_union.payload, writer, target); + try print(error_union.payload, writer, mod); }, .array_u8 => { @@ -2037,21 +2058,21 @@ pub const Type = extern union { .vector => { const payload = ty.castTag(.vector).?.data; try writer.print("@Vector({d}, ", .{payload.len}); - try print(payload.elem_type, writer, target); + try print(payload.elem_type, writer, mod); try writer.writeAll(")"); }, .array => { const payload = ty.castTag(.array).?.data; try writer.print("[{d}]", .{payload.len}); - try print(payload.elem_type, writer, target); + try print(payload.elem_type, writer, mod); }, .array_sentinel => { const payload = ty.castTag(.array_sentinel).?.data; try writer.print("[{d}:{}]", .{ payload.len, - payload.sentinel.fmtValue(payload.elem_type, target), + payload.sentinel.fmtValue(payload.elem_type, mod), }); - try print(payload.elem_type, writer, target); + try print(payload.elem_type, writer, mod); }, .tuple => { const tuple = ty.castTag(.tuple).?.data; @@ -2063,9 +2084,9 @@ pub const Type = extern union { if (val.tag() != .unreachable_value) { try writer.writeAll("comptime "); } - try print(field_ty, writer, target); + try print(field_ty, writer, mod); if (val.tag() != .unreachable_value) { - try writer.print(" = {}", .{val.fmtValue(field_ty, target)}); + try writer.print(" = {}", .{val.fmtValue(field_ty, mod)}); } } try writer.writeAll("}"); @@ -2083,10 +2104,10 @@ pub const Type = extern union { try writer.writeAll(anon_struct.names[i]); try writer.writeAll(": "); - try print(field_ty, writer, target); + try print(field_ty, writer, mod); if (val.tag() != .unreachable_value) { - try writer.print(" = {}", .{val.fmtValue(field_ty, target)}); + try writer.print(" = {}", .{val.fmtValue(field_ty, mod)}); } } try writer.writeAll("}"); @@ -2106,8 +2127,8 @@ pub const Type = extern union { if (info.sentinel) |s| switch (info.size) { .One, .C => unreachable, - .Many => try writer.print("[*:{}]", .{s.fmtValue(info.pointee_type, target)}), - .Slice => try writer.print("[:{}]", .{s.fmtValue(info.pointee_type, target)}), + .Many => try writer.print("[*:{}]", .{s.fmtValue(info.pointee_type, mod)}), + .Slice => try writer.print("[:{}]", .{s.fmtValue(info.pointee_type, mod)}), } else switch (info.size) { .One => try writer.writeAll("*"), .Many => try writer.writeAll("[*]"), @@ -2129,7 +2150,7 @@ pub const Type = extern union { if (info.@"volatile") try writer.writeAll("volatile "); if (info.@"allowzero" and info.size != .C) try writer.writeAll("allowzero "); - try print(info.pointee_type, writer, target); + try print(info.pointee_type, writer, mod); }, .int_signed => { @@ -2143,22 +2164,22 @@ pub const Type = extern union { .optional => { const child_type = ty.castTag(.optional).?.data; try writer.writeByte('?'); - try print(child_type, writer, target); + try print(child_type, writer, mod); }, .optional_single_mut_pointer => { const pointee_type = ty.castTag(.optional_single_mut_pointer).?.data; try writer.writeAll("?*"); - try print(pointee_type, writer, target); + try print(pointee_type, writer, mod); }, .optional_single_const_pointer => { const pointee_type = ty.castTag(.optional_single_const_pointer).?.data; try writer.writeAll("?*const "); - try print(pointee_type, writer, target); + try print(pointee_type, writer, mod); }, .anyframe_T => { const return_type = ty.castTag(.anyframe_T).?.data; try writer.print("anyframe->", .{}); - try print(return_type, writer, target); + try print(return_type, writer, mod); }, .error_set => { const names = ty.castTag(.error_set).?.data.names.keys(); @@ -3834,8 +3855,8 @@ pub const Type = extern union { /// For [*]T, returns *T /// For []T, returns *T /// Handles const-ness and address spaces in particular. - pub fn elemPtrType(ptr_ty: Type, arena: Allocator, target: Target) !Type { - return try Type.ptr(arena, target, .{ + pub fn elemPtrType(ptr_ty: Type, arena: Allocator, mod: *Module) !Type { + return try Type.ptr(arena, mod, .{ .pointee_type = ptr_ty.elemType2(), .mutable = ptr_ty.ptrIsMutable(), .@"addrspace" = ptr_ty.ptrAddressSpace(), @@ -3948,9 +3969,9 @@ pub const Type = extern union { return union_obj.fields; } - pub fn unionFieldType(ty: Type, enum_tag: Value, target: Target) Type { + pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) Type { const union_obj = ty.cast(Payload.Union).?.data; - const index = union_obj.tag_ty.enumTagFieldIndex(enum_tag, target).?; + const index = union_obj.tag_ty.enumTagFieldIndex(enum_tag, mod).?; assert(union_obj.haveFieldTypes()); return union_obj.fields.values()[index].ty; } @@ -4970,20 +4991,20 @@ pub const Type = extern union { /// Asserts `ty` is an enum. `enum_tag` can either be `enum_field_index` or /// an integer which represents the enum value. Returns the field index in /// declaration order, or `null` if `enum_tag` does not match any field. - pub fn enumTagFieldIndex(ty: Type, enum_tag: Value, target: Target) ?usize { + pub fn enumTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?usize { if (enum_tag.castTag(.enum_field_index)) |payload| { return @as(usize, payload.data); } const S = struct { - fn fieldWithRange(int_ty: Type, int_val: Value, end: usize, tg: Target) ?usize { + fn fieldWithRange(int_ty: Type, int_val: Value, end: usize, m: *Module) ?usize { if (int_val.compareWithZero(.lt)) return null; var end_payload: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = end, }; const end_val = Value.initPayload(&end_payload.base); - if (int_val.compare(.gte, end_val, int_ty, tg)) return null; - return @intCast(usize, int_val.toUnsignedInt(tg)); + if (int_val.compare(.gte, end_val, int_ty, m)) return null; + return @intCast(usize, int_val.toUnsignedInt(m.getTarget())); } }; switch (ty.tag()) { @@ -4991,11 +5012,11 @@ pub const Type = extern union { const enum_full = ty.cast(Payload.EnumFull).?.data; const tag_ty = enum_full.tag_ty; if (enum_full.values.count() == 0) { - return S.fieldWithRange(tag_ty, enum_tag, enum_full.fields.count(), target); + return S.fieldWithRange(tag_ty, enum_tag, enum_full.fields.count(), mod); } else { return enum_full.values.getIndexContext(enum_tag, .{ .ty = tag_ty, - .target = target, + .mod = mod, }); } }, @@ -5003,11 +5024,11 @@ pub const Type = extern union { const enum_obj = ty.castTag(.enum_numbered).?.data; const tag_ty = enum_obj.tag_ty; if (enum_obj.values.count() == 0) { - return S.fieldWithRange(tag_ty, enum_tag, enum_obj.fields.count(), target); + return S.fieldWithRange(tag_ty, enum_tag, enum_obj.fields.count(), mod); } else { return enum_obj.values.getIndexContext(enum_tag, .{ .ty = tag_ty, - .target = target, + .mod = mod, }); } }, @@ -5020,7 +5041,7 @@ pub const Type = extern union { .data = bits, }; const tag_ty = Type.initPayload(&buffer.base); - return S.fieldWithRange(tag_ty, enum_tag, fields_len, target); + return S.fieldWithRange(tag_ty, enum_tag, fields_len, mod); }, .atomic_order, .atomic_rmw_op, @@ -5224,32 +5245,35 @@ pub const Type = extern union { } } - pub fn declSrcLoc(ty: Type) Module.SrcLoc { - return declSrcLocOrNull(ty).?; + pub fn declSrcLoc(ty: Type, mod: *Module) Module.SrcLoc { + return declSrcLocOrNull(ty, mod).?; } - pub fn declSrcLocOrNull(ty: Type) ?Module.SrcLoc { + pub fn declSrcLocOrNull(ty: Type, mod: *Module) ?Module.SrcLoc { switch (ty.tag()) { .enum_full, .enum_nonexhaustive => { const enum_full = ty.cast(Payload.EnumFull).?.data; - return enum_full.srcLoc(); + return enum_full.srcLoc(mod); + }, + .enum_numbered => { + const enum_numbered = ty.castTag(.enum_numbered).?.data; + return enum_numbered.srcLoc(mod); }, - .enum_numbered => return ty.castTag(.enum_numbered).?.data.srcLoc(), .enum_simple => { const enum_simple = ty.castTag(.enum_simple).?.data; - return enum_simple.srcLoc(); + return enum_simple.srcLoc(mod); }, .@"struct" => { const struct_obj = ty.castTag(.@"struct").?.data; - return struct_obj.srcLoc(); + return struct_obj.srcLoc(mod); }, .error_set => { const error_set = ty.castTag(.error_set).?.data; - return error_set.srcLoc(); + return error_set.srcLoc(mod); }, .@"union", .union_tagged => { const union_obj = ty.cast(Payload.Union).?.data; - return union_obj.srcLoc(); + return union_obj.srcLoc(mod); }, .atomic_order, .atomic_rmw_op, @@ -5268,7 +5292,7 @@ pub const Type = extern union { } } - pub fn getOwnerDecl(ty: Type) *Module.Decl { + pub fn getOwnerDecl(ty: Type) Module.Decl.Index { switch (ty.tag()) { .enum_full, .enum_nonexhaustive => { const enum_full = ty.cast(Payload.EnumFull).?.data; @@ -5357,30 +5381,30 @@ pub const Type = extern union { } /// Asserts the type is an enum. - pub fn enumHasInt(ty: Type, int: Value, target: Target) bool { + pub fn enumHasInt(ty: Type, int: Value, mod: *Module) bool { const S = struct { - fn intInRange(tag_ty: Type, int_val: Value, end: usize, tg: Target) bool { + fn intInRange(tag_ty: Type, int_val: Value, end: usize, m: *Module) bool { if (int_val.compareWithZero(.lt)) return false; var end_payload: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = end, }; const end_val = Value.initPayload(&end_payload.base); - if (int_val.compare(.gte, end_val, tag_ty, tg)) return false; + if (int_val.compare(.gte, end_val, tag_ty, m)) return false; return true; } }; switch (ty.tag()) { - .enum_nonexhaustive => return int.intFitsInType(ty, target), + .enum_nonexhaustive => return int.intFitsInType(ty, mod.getTarget()), .enum_full => { const enum_full = ty.castTag(.enum_full).?.data; const tag_ty = enum_full.tag_ty; if (enum_full.values.count() == 0) { - return S.intInRange(tag_ty, int, enum_full.fields.count(), target); + return S.intInRange(tag_ty, int, enum_full.fields.count(), mod); } else { return enum_full.values.containsContext(int, .{ .ty = tag_ty, - .target = target, + .mod = mod, }); } }, @@ -5388,11 +5412,11 @@ pub const Type = extern union { const enum_obj = ty.castTag(.enum_numbered).?.data; const tag_ty = enum_obj.tag_ty; if (enum_obj.values.count() == 0) { - return S.intInRange(tag_ty, int, enum_obj.fields.count(), target); + return S.intInRange(tag_ty, int, enum_obj.fields.count(), mod); } else { return enum_obj.values.containsContext(int, .{ .ty = tag_ty, - .target = target, + .mod = mod, }); } }, @@ -5405,7 +5429,7 @@ pub const Type = extern union { .data = bits, }; const tag_ty = Type.initPayload(&buffer.base); - return S.intInRange(tag_ty, int, fields_len, target); + return S.intInRange(tag_ty, int, fields_len, mod); }, .atomic_order, .atomic_rmw_op, @@ -5937,7 +5961,9 @@ pub const Type = extern union { pub const @"anyopaque" = initTag(.anyopaque); pub const @"null" = initTag(.@"null"); - pub fn ptr(arena: Allocator, target: Target, data: Payload.Pointer.Data) !Type { + pub fn ptr(arena: Allocator, mod: *Module, data: Payload.Pointer.Data) !Type { + const target = mod.getTarget(); + var d = data; if (d.size == .C) { @@ -5967,7 +5993,7 @@ pub const Type = extern union { d.bit_offset == 0 and d.host_size == 0 and !d.@"allowzero" and !d.@"volatile") { if (d.sentinel) |sent| { - if (!d.mutable and d.pointee_type.eql(Type.u8, target)) { + if (!d.mutable and d.pointee_type.eql(Type.u8, mod)) { switch (d.size) { .Slice => { if (sent.compareWithZero(.eq)) { @@ -5982,7 +6008,7 @@ pub const Type = extern union { else => {}, } } - } else if (!d.mutable and d.pointee_type.eql(Type.u8, target)) { + } else if (!d.mutable and d.pointee_type.eql(Type.u8, mod)) { switch (d.size) { .Slice => return Type.initTag(.const_slice_u8), .Many => return Type.initTag(.manyptr_const_u8), @@ -6016,11 +6042,11 @@ pub const Type = extern union { len: u64, sent: ?Value, elem_type: Type, - target: Target, + mod: *Module, ) Allocator.Error!Type { - if (elem_type.eql(Type.u8, target)) { + if (elem_type.eql(Type.u8, mod)) { if (sent) |some| { - if (some.eql(Value.zero, elem_type, target)) { + if (some.eql(Value.zero, elem_type, mod)) { return Tag.array_u8_sentinel_0.create(arena, len); } } else { @@ -6067,11 +6093,11 @@ pub const Type = extern union { arena: Allocator, error_set: Type, payload: Type, - target: Target, + mod: *Module, ) Allocator.Error!Type { assert(error_set.zigTypeTag() == .ErrorSet); - if (error_set.eql(Type.@"anyerror", target) and - payload.eql(Type.void, target)) + if (error_set.eql(Type.@"anyerror", mod) and + payload.eql(Type.void, mod)) { return Type.initTag(.anyerror_void_error_union); } diff --git a/src/value.zig b/src/value.zig index ff2c0c271b..bb7b742290 100644 --- a/src/value.zig +++ b/src/value.zig @@ -731,16 +731,16 @@ pub const Value = extern union { .int_i64 => return std.fmt.formatIntValue(val.castTag(.int_i64).?.data, "", options, out_stream), .int_big_positive => return out_stream.print("{}", .{val.castTag(.int_big_positive).?.asBigInt()}), .int_big_negative => return out_stream.print("{}", .{val.castTag(.int_big_negative).?.asBigInt()}), - .function => return out_stream.print("(function '{s}')", .{val.castTag(.function).?.data.owner_decl.name}), + .function => return out_stream.print("(function decl={d})", .{val.castTag(.function).?.data.owner_decl}), .extern_fn => return out_stream.writeAll("(extern function)"), .variable => return out_stream.writeAll("(variable)"), .decl_ref_mut => { - const decl = val.castTag(.decl_ref_mut).?.data.decl; - return out_stream.print("(decl_ref_mut '{s}')", .{decl.name}); + const decl_index = val.castTag(.decl_ref_mut).?.data.decl_index; + return out_stream.print("(decl_ref_mut {d})", .{decl_index}); }, .decl_ref => { - const decl = val.castTag(.decl_ref).?.data; - return out_stream.print("(decl ref '{s}')", .{decl.name}); + const decl_index = val.castTag(.decl_ref).?.data; + return out_stream.print("(decl_ref {d})", .{decl_index}); }, .elem_ptr => { const elem_ptr = val.castTag(.elem_ptr).?.data; @@ -798,16 +798,17 @@ pub const Value = extern union { return .{ .data = val }; } - pub fn fmtValue(val: Value, ty: Type, target: Target) std.fmt.Formatter(TypedValue.format) { + pub fn fmtValue(val: Value, ty: Type, mod: *Module) std.fmt.Formatter(TypedValue.format) { return .{ .data = .{ .tv = .{ .ty = ty, .val = val }, - .target = target, + .mod = mod, } }; } /// Asserts that the value is representable as an array of bytes. /// Copies the value into a freshly allocated slice of memory, which is owned by the caller. - pub fn toAllocatedBytes(val: Value, ty: Type, allocator: Allocator, target: Target) ![]u8 { + pub fn toAllocatedBytes(val: Value, ty: Type, allocator: Allocator, mod: *Module) ![]u8 { + const target = mod.getTarget(); switch (val.tag()) { .bytes => { const bytes = val.castTag(.bytes).?.data; @@ -823,25 +824,26 @@ pub const Value = extern union { return result; }, .decl_ref => { - const decl = val.castTag(.decl_ref).?.data; + const decl_index = val.castTag(.decl_ref).?.data; + const decl = mod.declPtr(decl_index); const decl_val = try decl.value(); - return decl_val.toAllocatedBytes(decl.ty, allocator, target); + return decl_val.toAllocatedBytes(decl.ty, allocator, mod); }, .the_only_possible_value => return &[_]u8{}, .slice => { const slice = val.castTag(.slice).?.data; - return arrayToAllocatedBytes(slice.ptr, slice.len.toUnsignedInt(target), allocator, target); + return arrayToAllocatedBytes(slice.ptr, slice.len.toUnsignedInt(target), allocator, mod); }, - else => return arrayToAllocatedBytes(val, ty.arrayLen(), allocator, target), + else => return arrayToAllocatedBytes(val, ty.arrayLen(), allocator, mod), } } - fn arrayToAllocatedBytes(val: Value, len: u64, allocator: Allocator, target: Target) ![]u8 { + fn arrayToAllocatedBytes(val: Value, len: u64, allocator: Allocator, mod: *Module) ![]u8 { const result = try allocator.alloc(u8, @intCast(usize, len)); var elem_value_buf: ElemValueBuffer = undefined; for (result) |*elem, i| { - const elem_val = val.elemValueBuffer(i, &elem_value_buf); - elem.* = @intCast(u8, elem_val.toUnsignedInt(target)); + const elem_val = val.elemValueBuffer(mod, i, &elem_value_buf); + elem.* = @intCast(u8, elem_val.toUnsignedInt(mod.getTarget())); } return result; } @@ -1164,7 +1166,7 @@ pub const Value = extern union { var elem_value_buf: ElemValueBuffer = undefined; var buf_off: usize = 0; while (elem_i < len) : (elem_i += 1) { - const elem_val = val.elemValueBuffer(elem_i, &elem_value_buf); + const elem_val = val.elemValueBuffer(mod, elem_i, &elem_value_buf); writeToMemory(elem_val, elem_ty, mod, buffer[buf_off..]); buf_off += elem_size; } @@ -1975,34 +1977,47 @@ pub const Value = extern union { /// Asserts the values are comparable. Both operands have type `ty`. /// Vector results will be reduced with AND. - pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, target: Target) bool { + pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, mod: *Module) bool { if (ty.zigTypeTag() == .Vector) { var i: usize = 0; while (i < ty.vectorLen()) : (i += 1) { - if (!compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType(), target)) { + if (!compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType(), mod)) { return false; } } return true; } - return compareScalar(lhs, op, rhs, ty, target); + return compareScalar(lhs, op, rhs, ty, mod); } /// Asserts the values are comparable. Both operands have type `ty`. - pub fn compareScalar(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, target: Target) bool { + pub fn compareScalar( + lhs: Value, + op: std.math.CompareOperator, + rhs: Value, + ty: Type, + mod: *Module, + ) bool { return switch (op) { - .eq => lhs.eql(rhs, ty, target), - .neq => !lhs.eql(rhs, ty, target), - else => compareHetero(lhs, op, rhs, target), + .eq => lhs.eql(rhs, ty, mod), + .neq => !lhs.eql(rhs, ty, mod), + else => compareHetero(lhs, op, rhs, mod.getTarget()), }; } /// Asserts the values are comparable vectors of type `ty`. - pub fn compareVector(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { + pub fn compareVector( + lhs: Value, + op: std.math.CompareOperator, + rhs: Value, + ty: Type, + allocator: Allocator, + mod: *Module, + ) !Value { assert(ty.zigTypeTag() == .Vector); const result_data = try allocator.alloc(Value, ty.vectorLen()); for (result_data) |*scalar, i| { - const res_bool = compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType(), target); + const res_bool = compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType(), mod); scalar.* = if (res_bool) Value.@"true" else Value.@"false"; } return Value.Tag.aggregate.create(allocator, result_data); @@ -2032,7 +2047,8 @@ pub const Value = extern union { /// for `a`. This function must act *as if* `a` has been coerced to `ty`. This complication /// is required in order to make generic function instantiation effecient - specifically /// the insertion into the monomorphized function table. - pub fn eql(a: Value, b: Value, ty: Type, target: Target) bool { + pub fn eql(a: Value, b: Value, ty: Type, mod: *Module) bool { + const target = mod.getTarget(); const a_tag = a.tag(); const b_tag = b.tag(); if (a_tag == b_tag) switch (a_tag) { @@ -2052,31 +2068,31 @@ pub const Value = extern union { const a_payload = a.castTag(.opt_payload).?.data; const b_payload = b.castTag(.opt_payload).?.data; var buffer: Type.Payload.ElemType = undefined; - return eql(a_payload, b_payload, ty.optionalChild(&buffer), target); + return eql(a_payload, b_payload, ty.optionalChild(&buffer), mod); }, .slice => { const a_payload = a.castTag(.slice).?.data; const b_payload = b.castTag(.slice).?.data; - if (!eql(a_payload.len, b_payload.len, Type.usize, target)) return false; + if (!eql(a_payload.len, b_payload.len, Type.usize, mod)) return false; var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; const ptr_ty = ty.slicePtrFieldType(&ptr_buf); - return eql(a_payload.ptr, b_payload.ptr, ptr_ty, target); + return eql(a_payload.ptr, b_payload.ptr, ptr_ty, mod); }, .elem_ptr => { const a_payload = a.castTag(.elem_ptr).?.data; const b_payload = b.castTag(.elem_ptr).?.data; if (a_payload.index != b_payload.index) return false; - return eql(a_payload.array_ptr, b_payload.array_ptr, ty, target); + return eql(a_payload.array_ptr, b_payload.array_ptr, ty, mod); }, .field_ptr => { const a_payload = a.castTag(.field_ptr).?.data; const b_payload = b.castTag(.field_ptr).?.data; if (a_payload.field_index != b_payload.field_index) return false; - return eql(a_payload.container_ptr, b_payload.container_ptr, ty, target); + return eql(a_payload.container_ptr, b_payload.container_ptr, ty, mod); }, .@"error" => { const a_name = a.castTag(.@"error").?.data.name; @@ -2086,7 +2102,7 @@ pub const Value = extern union { .eu_payload => { const a_payload = a.castTag(.eu_payload).?.data; const b_payload = b.castTag(.eu_payload).?.data; - return eql(a_payload, b_payload, ty.errorUnionPayload(), target); + return eql(a_payload, b_payload, ty.errorUnionPayload(), mod); }, .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"), .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"), @@ -2104,7 +2120,7 @@ pub const Value = extern union { const types = ty.tupleFields().types; assert(types.len == a_field_vals.len); for (types) |field_ty, i| { - if (!eql(a_field_vals[i], b_field_vals[i], field_ty, target)) return false; + if (!eql(a_field_vals[i], b_field_vals[i], field_ty, mod)) return false; } return true; } @@ -2113,7 +2129,7 @@ pub const Value = extern union { const fields = ty.structFields().values(); assert(fields.len == a_field_vals.len); for (fields) |field, i| { - if (!eql(a_field_vals[i], b_field_vals[i], field.ty, target)) return false; + if (!eql(a_field_vals[i], b_field_vals[i], field.ty, mod)) return false; } return true; } @@ -2122,7 +2138,7 @@ pub const Value = extern union { for (a_field_vals) |a_elem, i| { const b_elem = b_field_vals[i]; - if (!eql(a_elem, b_elem, elem_ty, target)) return false; + if (!eql(a_elem, b_elem, elem_ty, mod)) return false; } return true; }, @@ -2132,7 +2148,7 @@ pub const Value = extern union { switch (ty.containerLayout()) { .Packed, .Extern => { const tag_ty = ty.unionTagTypeHypothetical(); - if (!a_union.tag.eql(b_union.tag, tag_ty, target)) { + if (!a_union.tag.eql(b_union.tag, tag_ty, mod)) { // In this case, we must disregard mismatching tags and compare // based on the in-memory bytes of the payloads. @panic("TODO comptime comparison of extern union values with mismatching tags"); @@ -2140,13 +2156,13 @@ pub const Value = extern union { }, .Auto => { const tag_ty = ty.unionTagTypeHypothetical(); - if (!a_union.tag.eql(b_union.tag, tag_ty, target)) { + if (!a_union.tag.eql(b_union.tag, tag_ty, mod)) { return false; } }, } - const active_field_ty = ty.unionFieldType(a_union.tag, target); - return a_union.val.eql(b_union.val, active_field_ty, target); + const active_field_ty = ty.unionFieldType(a_union.tag, mod); + return a_union.val.eql(b_union.val, active_field_ty, mod); }, else => {}, } else if (a_tag == .null_value or b_tag == .null_value) { @@ -2171,7 +2187,7 @@ pub const Value = extern union { var buf_b: ToTypeBuffer = undefined; const a_type = a.toType(&buf_a); const b_type = b.toType(&buf_b); - return a_type.eql(b_type, target); + return a_type.eql(b_type, mod); }, .Enum => { var buf_a: Payload.U64 = undefined; @@ -2180,7 +2196,7 @@ pub const Value = extern union { const b_val = b.enumToInt(ty, &buf_b); var buf_ty: Type.Payload.Bits = undefined; const int_ty = ty.intTagType(&buf_ty); - return eql(a_val, b_val, int_ty, target); + return eql(a_val, b_val, int_ty, mod); }, .Array, .Vector => { const len = ty.arrayLen(); @@ -2189,9 +2205,9 @@ pub const Value = extern union { var a_buf: ElemValueBuffer = undefined; var b_buf: ElemValueBuffer = undefined; while (i < len) : (i += 1) { - const a_elem = elemValueBuffer(a, i, &a_buf); - const b_elem = elemValueBuffer(b, i, &b_buf); - if (!eql(a_elem, b_elem, elem_ty, target)) return false; + const a_elem = elemValueBuffer(a, mod, i, &a_buf); + const b_elem = elemValueBuffer(b, mod, i, &b_buf); + if (!eql(a_elem, b_elem, elem_ty, mod)) return false; } return true; }, @@ -2215,7 +2231,7 @@ pub const Value = extern union { .base = .{ .tag = .opt_payload }, .data = a, }; - return eql(Value.initPayload(&buffer.base), b, ty, target); + return eql(Value.initPayload(&buffer.base), b, ty, mod); } }, else => {}, @@ -2225,7 +2241,7 @@ pub const Value = extern union { /// This function is used by hash maps and so treats floating-point NaNs as equal /// to each other, and not equal to other floating-point values. - pub fn hash(val: Value, ty: Type, hasher: *std.hash.Wyhash, target: Target) void { + pub fn hash(val: Value, ty: Type, hasher: *std.hash.Wyhash, mod: *Module) void { const zig_ty_tag = ty.zigTypeTag(); std.hash.autoHash(hasher, zig_ty_tag); if (val.isUndef()) return; @@ -2242,7 +2258,7 @@ pub const Value = extern union { .Type => { var buf: ToTypeBuffer = undefined; - return val.toType(&buf).hashWithHasher(hasher, target); + return val.toType(&buf).hashWithHasher(hasher, mod); }, .Float, .ComptimeFloat => { // Normalize the float here because this hash must match eql semantics. @@ -2263,11 +2279,11 @@ pub const Value = extern union { const slice = val.castTag(.slice).?.data; var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; const ptr_ty = ty.slicePtrFieldType(&ptr_buf); - hash(slice.ptr, ptr_ty, hasher, target); - hash(slice.len, Type.usize, hasher, target); + hash(slice.ptr, ptr_ty, hasher, mod); + hash(slice.len, Type.usize, hasher, mod); }, - else => return hashPtr(val, hasher, target), + else => return hashPtr(val, hasher, mod.getTarget()), }, .Array, .Vector => { const len = ty.arrayLen(); @@ -2275,15 +2291,15 @@ pub const Value = extern union { var index: usize = 0; var elem_value_buf: ElemValueBuffer = undefined; while (index < len) : (index += 1) { - const elem_val = val.elemValueBuffer(index, &elem_value_buf); - elem_val.hash(elem_ty, hasher, target); + const elem_val = val.elemValueBuffer(mod, index, &elem_value_buf); + elem_val.hash(elem_ty, hasher, mod); } }, .Struct => { if (ty.isTupleOrAnonStruct()) { const fields = ty.tupleFields(); for (fields.values) |field_val, i| { - field_val.hash(fields.types[i], hasher, target); + field_val.hash(fields.types[i], hasher, mod); } return; } @@ -2292,13 +2308,13 @@ pub const Value = extern union { switch (val.tag()) { .empty_struct_value => { for (fields) |field| { - field.default_val.hash(field.ty, hasher, target); + field.default_val.hash(field.ty, hasher, mod); } }, .aggregate => { const field_values = val.castTag(.aggregate).?.data; for (field_values) |field_val, i| { - field_val.hash(fields[i].ty, hasher, target); + field_val.hash(fields[i].ty, hasher, mod); } }, else => unreachable, @@ -2310,7 +2326,7 @@ pub const Value = extern union { const sub_val = payload.data; var buffer: Type.Payload.ElemType = undefined; const sub_ty = ty.optionalChild(&buffer); - sub_val.hash(sub_ty, hasher, target); + sub_val.hash(sub_ty, hasher, mod); } else { std.hash.autoHash(hasher, false); // non-null } @@ -2319,14 +2335,14 @@ pub const Value = extern union { if (val.tag() == .@"error") { std.hash.autoHash(hasher, false); // error const sub_ty = ty.errorUnionSet(); - val.hash(sub_ty, hasher, target); + val.hash(sub_ty, hasher, mod); return; } if (val.castTag(.eu_payload)) |payload| { std.hash.autoHash(hasher, true); // payload const sub_ty = ty.errorUnionPayload(); - payload.data.hash(sub_ty, hasher, target); + payload.data.hash(sub_ty, hasher, mod); return; } else unreachable; }, @@ -2339,15 +2355,15 @@ pub const Value = extern union { .Enum => { var enum_space: Payload.U64 = undefined; const int_val = val.enumToInt(ty, &enum_space); - hashInt(int_val, hasher, target); + hashInt(int_val, hasher, mod.getTarget()); }, .Union => { const union_obj = val.cast(Payload.Union).?.data; if (ty.unionTagType()) |tag_ty| { - union_obj.tag.hash(tag_ty, hasher, target); + union_obj.tag.hash(tag_ty, hasher, mod); } - const active_field_ty = ty.unionFieldType(union_obj.tag, target); - union_obj.val.hash(active_field_ty, hasher, target); + const active_field_ty = ty.unionFieldType(union_obj.tag, mod); + union_obj.val.hash(active_field_ty, hasher, mod); }, .Fn => { const func: *Module.Fn = val.castTag(.function).?.data; @@ -2372,30 +2388,30 @@ pub const Value = extern union { pub const ArrayHashContext = struct { ty: Type, - target: Target, + mod: *Module, pub fn hash(self: @This(), val: Value) u32 { - const other_context: HashContext = .{ .ty = self.ty, .target = self.target }; + const other_context: HashContext = .{ .ty = self.ty, .mod = self.mod }; return @truncate(u32, other_context.hash(val)); } pub fn eql(self: @This(), a: Value, b: Value, b_index: usize) bool { _ = b_index; - return a.eql(b, self.ty, self.target); + return a.eql(b, self.ty, self.mod); } }; pub const HashContext = struct { ty: Type, - target: Target, + mod: *Module, pub fn hash(self: @This(), val: Value) u64 { var hasher = std.hash.Wyhash.init(0); - val.hash(self.ty, &hasher, self.target); + val.hash(self.ty, &hasher, self.mod); return hasher.final(); } pub fn eql(self: @This(), a: Value, b: Value) bool { - return a.eql(b, self.ty, self.target); + return a.eql(b, self.ty, self.mod); } }; @@ -2434,9 +2450,9 @@ pub const Value = extern union { /// Gets the decl referenced by this pointer. If the pointer does not point /// to a decl, or if it points to some part of a decl (like field_ptr or element_ptr), /// this function returns null. - pub fn pointerDecl(val: Value) ?*Module.Decl { + pub fn pointerDecl(val: Value) ?Module.Decl.Index { return switch (val.tag()) { - .decl_ref_mut => val.castTag(.decl_ref_mut).?.data.decl, + .decl_ref_mut => val.castTag(.decl_ref_mut).?.data.decl_index, .extern_fn => val.castTag(.extern_fn).?.data.owner_decl, .function => val.castTag(.function).?.data.owner_decl, .variable => val.castTag(.variable).?.data.owner_decl, @@ -2462,7 +2478,7 @@ pub const Value = extern union { .function, .variable, => { - const decl: *Module.Decl = ptr_val.pointerDecl().?; + const decl: Module.Decl.Index = ptr_val.pointerDecl().?; std.hash.autoHash(hasher, decl); }, @@ -2505,53 +2521,6 @@ pub const Value = extern union { } } - pub fn markReferencedDeclsAlive(val: Value) void { - switch (val.tag()) { - .decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.markAlive(), - .extern_fn => return val.castTag(.extern_fn).?.data.owner_decl.markAlive(), - .function => return val.castTag(.function).?.data.owner_decl.markAlive(), - .variable => return val.castTag(.variable).?.data.owner_decl.markAlive(), - .decl_ref => return val.cast(Payload.Decl).?.data.markAlive(), - - .repeated, - .eu_payload, - .opt_payload, - .empty_array_sentinel, - => return markReferencedDeclsAlive(val.cast(Payload.SubValue).?.data), - - .eu_payload_ptr, - .opt_payload_ptr, - => return markReferencedDeclsAlive(val.cast(Payload.PayloadPtr).?.data.container_ptr), - - .slice => { - const slice = val.cast(Payload.Slice).?.data; - markReferencedDeclsAlive(slice.ptr); - markReferencedDeclsAlive(slice.len); - }, - - .elem_ptr => { - const elem_ptr = val.cast(Payload.ElemPtr).?.data; - return markReferencedDeclsAlive(elem_ptr.array_ptr); - }, - .field_ptr => { - const field_ptr = val.cast(Payload.FieldPtr).?.data; - return markReferencedDeclsAlive(field_ptr.container_ptr); - }, - .aggregate => { - for (val.castTag(.aggregate).?.data) |field_val| { - markReferencedDeclsAlive(field_val); - } - }, - .@"union" => { - const data = val.cast(Payload.Union).?.data; - markReferencedDeclsAlive(data.tag); - markReferencedDeclsAlive(data.val); - }, - - else => {}, - } - } - pub fn slicePtr(val: Value) Value { return switch (val.tag()) { .slice => val.castTag(.slice).?.data.ptr, @@ -2561,11 +2530,12 @@ pub const Value = extern union { }; } - pub fn sliceLen(val: Value, target: Target) u64 { + pub fn sliceLen(val: Value, mod: *Module) u64 { return switch (val.tag()) { - .slice => val.castTag(.slice).?.data.len.toUnsignedInt(target), + .slice => val.castTag(.slice).?.data.len.toUnsignedInt(mod.getTarget()), .decl_ref => { - const decl = val.castTag(.decl_ref).?.data; + const decl_index = val.castTag(.decl_ref).?.data; + const decl = mod.declPtr(decl_index); if (decl.ty.zigTypeTag() == .Array) { return decl.ty.arrayLen(); } else { @@ -2599,18 +2569,19 @@ pub const Value = extern union { /// Asserts the value is a single-item pointer to an array, or an array, /// or an unknown-length pointer, and returns the element value at the index. - pub fn elemValue(val: Value, arena: Allocator, index: usize) !Value { - return elemValueAdvanced(val, index, arena, undefined); + pub fn elemValue(val: Value, mod: *Module, arena: Allocator, index: usize) !Value { + return elemValueAdvanced(val, mod, index, arena, undefined); } pub const ElemValueBuffer = Payload.U64; - pub fn elemValueBuffer(val: Value, index: usize, buffer: *ElemValueBuffer) Value { - return elemValueAdvanced(val, index, null, buffer) catch unreachable; + pub fn elemValueBuffer(val: Value, mod: *Module, index: usize, buffer: *ElemValueBuffer) Value { + return elemValueAdvanced(val, mod, index, null, buffer) catch unreachable; } pub fn elemValueAdvanced( val: Value, + mod: *Module, index: usize, arena: ?Allocator, buffer: *ElemValueBuffer, @@ -2643,13 +2614,13 @@ pub const Value = extern union { .repeated => return val.castTag(.repeated).?.data, .aggregate => return val.castTag(.aggregate).?.data[index], - .slice => return val.castTag(.slice).?.data.ptr.elemValueAdvanced(index, arena, buffer), + .slice => return val.castTag(.slice).?.data.ptr.elemValueAdvanced(mod, index, arena, buffer), - .decl_ref => return val.castTag(.decl_ref).?.data.val.elemValueAdvanced(index, arena, buffer), - .decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.val.elemValueAdvanced(index, arena, buffer), + .decl_ref => return mod.declPtr(val.castTag(.decl_ref).?.data).val.elemValueAdvanced(mod, index, arena, buffer), + .decl_ref_mut => return mod.declPtr(val.castTag(.decl_ref_mut).?.data.decl_index).val.elemValueAdvanced(mod, index, arena, buffer), .elem_ptr => { const data = val.castTag(.elem_ptr).?.data; - return data.array_ptr.elemValueAdvanced(index + data.index, arena, buffer); + return data.array_ptr.elemValueAdvanced(mod, index + data.index, arena, buffer); }, // The child type of arrays which have only one possible value need @@ -2661,18 +2632,24 @@ pub const Value = extern union { } // Asserts that the provided start/end are in-bounds. - pub fn sliceArray(val: Value, arena: Allocator, start: usize, end: usize) error{OutOfMemory}!Value { + pub fn sliceArray( + val: Value, + mod: *Module, + arena: Allocator, + start: usize, + end: usize, + ) error{OutOfMemory}!Value { return switch (val.tag()) { .empty_array_sentinel => if (start == 0 and end == 1) val else Value.initTag(.empty_array), .bytes => Tag.bytes.create(arena, val.castTag(.bytes).?.data[start..end]), .aggregate => Tag.aggregate.create(arena, val.castTag(.aggregate).?.data[start..end]), - .slice => sliceArray(val.castTag(.slice).?.data.ptr, arena, start, end), + .slice => sliceArray(val.castTag(.slice).?.data.ptr, mod, arena, start, end), - .decl_ref => sliceArray(val.castTag(.decl_ref).?.data.val, arena, start, end), - .decl_ref_mut => sliceArray(val.castTag(.decl_ref_mut).?.data.decl.val, arena, start, end), + .decl_ref => sliceArray(mod.declPtr(val.castTag(.decl_ref).?.data).val, mod, arena, start, end), + .decl_ref_mut => sliceArray(mod.declPtr(val.castTag(.decl_ref_mut).?.data.decl_index).val, mod, arena, start, end), .elem_ptr => blk: { const elem_ptr = val.castTag(.elem_ptr).?.data; - break :blk sliceArray(elem_ptr.array_ptr, arena, start + elem_ptr.index, end + elem_ptr.index); + break :blk sliceArray(elem_ptr.array_ptr, mod, arena, start + elem_ptr.index, end + elem_ptr.index); }, .repeated, @@ -2718,7 +2695,13 @@ pub const Value = extern union { } /// Returns a pointer to the element value at the index. - pub fn elemPtr(val: Value, ty: Type, arena: Allocator, index: usize, target: Target) Allocator.Error!Value { + pub fn elemPtr( + val: Value, + ty: Type, + arena: Allocator, + index: usize, + mod: *Module, + ) Allocator.Error!Value { const elem_ty = ty.elemType2(); const ptr_val = switch (val.tag()) { .slice => val.castTag(.slice).?.data.ptr, @@ -2727,7 +2710,7 @@ pub const Value = extern union { if (ptr_val.tag() == .elem_ptr) { const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; - if (elem_ptr.elem_ty.eql(elem_ty, target)) { + if (elem_ptr.elem_ty.eql(elem_ty, mod)) { return Tag.elem_ptr.create(arena, .{ .array_ptr = elem_ptr.array_ptr, .elem_ty = elem_ptr.elem_ty, @@ -5059,7 +5042,7 @@ pub const Value = extern union { pub const Decl = struct { base: Payload, - data: *Module.Decl, + data: Module.Decl.Index, }; pub const Variable = struct { @@ -5079,7 +5062,7 @@ pub const Value = extern union { data: Data, pub const Data = struct { - decl: *Module.Decl, + decl_index: Module.Decl.Index, runtime_index: u32, }; }; @@ -5215,7 +5198,7 @@ pub const Value = extern union { base: Payload = .{ .tag = base_tag }, data: struct { - decl: *Module.Decl, + decl_index: Module.Decl.Index, /// 0 means ABI-aligned. alignment: u16, }, From 31758f79db2c9e1122fd40bdda2243311830a5d4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 20 Apr 2022 18:14:38 -0700 Subject: [PATCH 1212/2031] link: Wasm: don't assume we have a zig module --- src/link/Wasm.zig | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index fad7543b0e..3c53e91587 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -429,11 +429,14 @@ pub fn deinit(self: *Wasm) void { if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa); } - const mod = self.base.options.module.?; - var decl_it = self.decls.keyIterator(); - while (decl_it.next()) |decl_index_ptr| { - const decl = mod.declPtr(decl_index_ptr.*); - decl.link.wasm.deinit(gpa); + if (self.base.options.module) |mod| { + var decl_it = self.decls.keyIterator(); + while (decl_it.next()) |decl_index_ptr| { + const decl = mod.declPtr(decl_index_ptr.*); + decl.link.wasm.deinit(gpa); + } + } else { + assert(self.decls.count() == 0); } for (self.func_types.items) |*func_type| { From 4ece507b5aa3d89adb3b99258f089aa8f48184b2 Mon Sep 17 00:00:00 2001 From: Rabin Gaire Date: Thu, 21 Apr 2022 13:28:41 +0545 Subject: [PATCH 1213/2031] update test message and using unreachable keyword for unintended test code path --- lib/std/child_process.zig | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 41d2cf2850..62d141bf16 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -1380,7 +1380,7 @@ test "build and call child_process" { try testing.expectEqual(ret_val, .{ .Exited = 0 }); } -test "creating a child process with stdin/stdout behavior set to StdIo.Pipe" { +test "creating a child process with stdin and stdout behavior set to StdIo.Pipe" { if (builtin.os.tag == .wasi) return error.SkipZigTest; const testing = std.testing; const allocator = testing.allocator; @@ -1420,8 +1420,6 @@ test "creating a child process with stdin/stdout behavior set to StdIo.Pipe" { ; try testing.expectEqualStrings(expected_program, out_bytes); }, - else => { - try testing.expect(false); - } + else => unreachable } } From 50ec55faaf4b218c4ded8a73864bbd0c85571e23 Mon Sep 17 00:00:00 2001 From: Rabin Gaire Date: Thu, 21 Apr 2022 14:12:08 +0545 Subject: [PATCH 1214/2031] ran zig fmt on changes --- lib/std/child_process.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 62d141bf16..f2b978ba9f 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -658,7 +658,7 @@ pub const ChildProcess = struct { .Pipe => { const idx: usize = if (std_fileno == 0) 0 else 1; try actions.dup2(pipe_fd[idx], std_fileno); - try actions.close(pipe_fd[1-idx]); + try actions.close(pipe_fd[1 - idx]); }, .Close => try actions.close(std_fileno), .Inherit => {}, @@ -1408,7 +1408,7 @@ test "creating a child process with stdin and stdout behavior set to StdIo.Pipe" const out_bytes = try child_process.stdout.?.reader().readAllAlloc(allocator, std.math.maxInt(usize)); defer allocator.free(out_bytes); - + switch (try child_process.wait()) { .Exited => |code| if (code == 0) { const expected_program = @@ -1420,6 +1420,6 @@ test "creating a child process with stdin and stdout behavior set to StdIo.Pipe" ; try testing.expectEqualStrings(expected_program, out_bytes); }, - else => unreachable + else => unreachable, } } From 28ca203b7132ba0513a3854bd3bcbd0ee9bca067 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 21 Apr 2022 10:46:04 +0200 Subject: [PATCH 1215/2031] debug: fix resource (de)allocation for Elf and Coff targets With this change, it is now possible to safely call `var di = std.debug.openSelfDebugInfo(gpa)`. Calling then `di.deinit()` on the object will correctly free all allocated resources. Ensure we store the result of `mmap` with correct alignment. --- lib/std/coff.zig | 11 ++++++----- lib/std/debug.zig | 46 ++++++++++++++++++++++++++++++++++------------ lib/std/pdb.zig | 1 - 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/lib/std/coff.zig b/lib/std/coff.zig index a2da0552be..8fabd8e214 100644 --- a/lib/std/coff.zig +++ b/lib/std/coff.zig @@ -4,8 +4,6 @@ const mem = std.mem; const os = std.os; const File = std.fs.File; -const ArrayList = std.ArrayList; - // CoffHeader.machine values // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=vs.85).aspx const IMAGE_FILE_MACHINE_I386 = 0x014c; @@ -117,7 +115,7 @@ pub const Coff = struct { coff_header: CoffHeader, pe_header: OptionalHeader, - sections: ArrayList(Section), + sections: std.ArrayListUnmanaged(Section) = .{}, guid: [16]u8, age: u32, @@ -128,12 +126,15 @@ pub const Coff = struct { .allocator = allocator, .coff_header = undefined, .pe_header = undefined, - .sections = ArrayList(Section).init(allocator), .guid = undefined, .age = undefined, }; } + pub fn deinit(self: *Coff) void { + self.sections.deinit(self.allocator); + } + pub fn loadHeader(self: *Coff) !void { const pe_pointer_offset = 0x3C; @@ -291,7 +292,7 @@ pub const Coff = struct { if (self.sections.items.len == self.coff_header.number_of_sections) return; - try self.sections.ensureTotalCapacityPrecise(self.coff_header.number_of_sections); + try self.sections.ensureTotalCapacityPrecise(self.allocator, self.coff_header.number_of_sections); const in = self.in_file.reader(); diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 58038ef522..5ee4973c1a 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -41,7 +41,7 @@ pub const SymbolInfo = struct { compile_unit_name: []const u8 = "???", line_info: ?LineInfo = null, - pub fn deinit(self: @This(), allocator: mem.Allocator) void { + pub fn deinit(self: SymbolInfo, allocator: mem.Allocator) void { if (self.line_info) |li| { li.deinit(allocator); } @@ -50,6 +50,13 @@ pub const SymbolInfo = struct { const PdbOrDwarf = union(enum) { pdb: pdb.Pdb, dwarf: DW.DwarfInfo, + + fn deinit(self: *PdbOrDwarf, allocator: mem.Allocator) void { + switch (self.*) { + .pdb => |*inner| inner.deinit(), + .dwarf => |*inner| inner.deinit(allocator), + } + } }; var stderr_mutex = std.Thread.Mutex{}; @@ -793,6 +800,7 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_file: File) !ModuleDebugInfo errdefer coff_file.close(); const coff_obj = try allocator.create(coff.Coff); + errdefer allocator.destroy(coff_obj); coff_obj.* = coff.Coff.init(allocator, coff_file); var di = ModuleDebugInfo{ @@ -1386,7 +1394,7 @@ pub const DebugInfo = struct { pub const ModuleDebugInfo = switch (native_os) { .macos, .ios, .watchos, .tvos => struct { base_address: usize, - mapped_memory: []const u8, + mapped_memory: []align(mem.page_size) const u8, symbols: []const MachoSymbol, strings: [:0]const u8, ofiles: OFileTable, @@ -1406,6 +1414,7 @@ pub const ModuleDebugInfo = switch (native_os) { } self.ofiles.deinit(); allocator.free(self.symbols); + os.munmap(self.mapped_memory); } fn loadOFile(self: *@This(), allocator: mem.Allocator, o_file_path: []const u8) !OFileInfo { @@ -1609,18 +1618,20 @@ pub const ModuleDebugInfo = switch (native_os) { debug_data: PdbOrDwarf, coff: *coff.Coff, - pub fn allocator(self: @This()) mem.Allocator { - return self.coff.allocator; + fn deinit(self: *@This(), allocator: mem.Allocator) void { + self.debug_data.deinit(allocator); + self.coff.deinit(); + allocator.destroy(self.coff); } - pub fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo { + pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo { // Translate the VA into an address into this object const relocated_address = address - self.base_address; switch (self.debug_data) { .dwarf => |*dwarf| { const dwarf_address = relocated_address + self.coff.pe_header.image_base; - return getSymbolFromDwarf(dwarf_address, dwarf); + return getSymbolFromDwarf(allocator, dwarf_address, dwarf); }, .pdb => { // fallthrough to pdb handling @@ -1666,17 +1677,28 @@ pub const ModuleDebugInfo = switch (native_os) { .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris => struct { base_address: usize, dwarf: DW.DwarfInfo, - mapped_memory: []const u8, + mapped_memory: []align(mem.page_size) const u8, - pub fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo { + fn deinit(self: *@This(), allocator: mem.Allocator) void { + self.dwarf.deinit(allocator); + os.munmap(self.mapped_memory); + } + + pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo { // Translate the VA into an address into this object const relocated_address = address - self.base_address; - return getSymbolFromDwarf(relocated_address, &self.dwarf); + return getSymbolFromDwarf(allocator, relocated_address, &self.dwarf); } }, .wasi => struct { - pub fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo { + fn deinit(self: *@This(), allocator: mem.Allocator) void { _ = self; + _ = allocator; + } + + pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo { + _ = self; + _ = allocator; _ = address; return SymbolInfo{}; } @@ -1684,14 +1706,14 @@ pub const ModuleDebugInfo = switch (native_os) { else => DW.DwarfInfo, }; -fn getSymbolFromDwarf(address: u64, di: *DW.DwarfInfo) !SymbolInfo { +fn getSymbolFromDwarf(allocator: mem.Allocator, address: u64, di: *DW.DwarfInfo) !SymbolInfo { if (nosuspend di.findCompileUnit(address)) |compile_unit| { return SymbolInfo{ .symbol_name = nosuspend di.getSymbolName(address) orelse "???", .compile_unit_name = compile_unit.die.getAttrString(di, DW.AT.name) catch |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => "???", }, - .line_info = nosuspend di.getLineNumberInfo(compile_unit.*, address) catch |err| switch (err) { + .line_info = nosuspend di.getLineNumberInfo(allocator, compile_unit.*, address) catch |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => null, else => return err, }, diff --git a/lib/std/pdb.zig b/lib/std/pdb.zig index 03f78cb179..88ae849109 100644 --- a/lib/std/pdb.zig +++ b/lib/std/pdb.zig @@ -764,7 +764,6 @@ pub const Pdb = struct { const flags = @ptrCast(*LineNumberEntry.Flags, &line_num_entry.Flags); return debug.LineInfo{ - .allocator = self.allocator, .file_name = source_file_name, .line = flags.Start, .column = column, From bedd7efa2bca82aa1c101ca4144b6bce65c9ab87 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 21 Apr 2022 10:14:23 +0200 Subject: [PATCH 1216/2031] debug: add smoke test --- lib/std/debug.zig | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 5ee4973c1a..683219c78d 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -682,7 +682,6 @@ test "machoSearchSymbols" { try testing.expectEqual(&symbols[2], machoSearchSymbols(&symbols, 5000).?); } -/// TODO resources https://github.com/ziglang/zig/issues/4353 pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: anytype, address: usize, tty_config: TTY.Config) !void { const module = debug_info.getModuleForAddress(address) catch |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => { @@ -768,7 +767,6 @@ pub const OpenSelfDebugInfoError = error{ UnsupportedOperatingSystem, }; -/// TODO resources https://github.com/ziglang/zig/issues/4353 pub fn openSelfDebugInfo(allocator: mem.Allocator) anyerror!DebugInfo { nosuspend { if (builtin.strip_debug_info) @@ -793,7 +791,6 @@ pub fn openSelfDebugInfo(allocator: mem.Allocator) anyerror!DebugInfo { /// This takes ownership of coff_file: users of this function should not close /// it themselves, even on error. -/// TODO resources https://github.com/ziglang/zig/issues/4353 /// TODO it's weird to take ownership even on error, rework this code. fn readCoffDebugInfo(allocator: mem.Allocator, coff_file: File) !ModuleDebugInfo { nosuspend { @@ -863,7 +860,6 @@ fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 { /// This takes ownership of elf_file: users of this function should not close /// it themselves, even on error. -/// TODO resources https://github.com/ziglang/zig/issues/4353 /// TODO it's weird to take ownership even on error, rework this code. pub fn readElfDebugInfo(allocator: mem.Allocator, elf_file: File) !ModuleDebugInfo { nosuspend { @@ -937,7 +933,6 @@ pub fn readElfDebugInfo(allocator: mem.Allocator, elf_file: File) !ModuleDebugIn } } -/// TODO resources https://github.com/ziglang/zig/issues/4353 /// This takes ownership of macho_file: users of this function should not close /// it themselves, even on error. /// TODO it's weird to take ownership even on error, rework this code. @@ -1934,3 +1929,16 @@ pub fn dumpStackPointerAddr(prefix: []const u8) void { ); std.debug.print("{} sp = 0x{x}\n", .{ prefix, sp }); } + +test "#4353: std.debug should manage resources correctly" { + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + const writer = std.io.null_writer; + var di = try openSelfDebugInfo(testing.allocator); + defer di.deinit(); + try printSourceAtAddress(&di, writer, showMyTrace(), detectTTYConfig()); +} + +noinline fn showMyTrace() usize { + return @returnAddress(); +} From 42ed34d1f6a1f6487981749eb6c31deba198aa67 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 21 Apr 2022 12:04:27 +0200 Subject: [PATCH 1217/2031] macho: create LlvmObject in createEmpty only Prior to this change we would also create it in `openPath`, but as `openPath` internally calls `createEmpty` we would end up with a memory leak. --- src/link/MachO.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 52ffba954d..7caccb1fb8 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -321,7 +321,6 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { // TODO this intermediary_basename isn't enough; in the case of `zig build-exe`, // we also want to put the intermediary object file in the cache while the // main emit directory is the cwd. - self.llvm_object = try LlvmObject.create(allocator, options); self.base.intermediary_basename = try std.fmt.allocPrint(allocator, "{s}{s}", .{ emit.sub_path, options.object_format.fileExt(options.target.cpu.arch), }); From ebfddbaf0038c62f9560a8bf7134b401fb0e4d19 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Apr 2022 03:41:33 -0700 Subject: [PATCH 1218/2031] CI: additionally test building stage3 --- ci/zinc/linux_test.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ci/zinc/linux_test.sh b/ci/zinc/linux_test.sh index 739d85cff0..b0f65aefd3 100755 --- a/ci/zinc/linux_test.sh +++ b/ci/zinc/linux_test.sh @@ -48,7 +48,10 @@ cd $WORKSPACE $ZIG fmt --check . --exclude test/compile_errors/ # Build stage2 standalone so that we can test stage2 against stage2 compiler-rt. -$ZIG build -p stage2 -Denable-llvm -Duse-zig-libcxx +$ZIG build -p stage2 -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL" + +# Ensure that stage2 can build itself. +./stage2/bin/zig build -p stage3 -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL" stage2/bin/zig test test/behavior.zig -I test -fLLVM stage2/bin/zig test test/behavior.zig -I test -fno-LLVM @@ -95,8 +98,7 @@ tidy --drop-empty-elements no -qe zig-cache/langref.html $ZIG build \ --prefix "$RELEASE_STAGING" \ --search-prefix "$DEPS_LOCAL" \ - -Denable-llvm \ - -Duse-zig-libcxx \ + -Dstatic-llvm \ -Drelease \ -Dstrip \ -Dtarget="$TARGET" \ From 6d0283e6bc0493ca40fc752433973d50210023d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Thu, 21 Apr 2022 21:33:42 +0300 Subject: [PATCH 1219/2031] [doc update] add size_t (#11482) For those souls looking for a zig `size_t` equivalent, and not lucky/educated enough (that was me yesterday) to know it's the same as `uintptr_t`. From a recent discussion on IRC. --- doc/langref.html.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 6ae2e456e4..5cccced446 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -672,8 +672,8 @@ pub fn main() void { {#syntax#}usize{#endsyntax#} - uintptr_t - unsigned pointer sized integer + uintptr_t, size_t + unsigned pointer sized integer. Also see #5185 From 41654a318db526465754ae084cc37896382eb0d2 Mon Sep 17 00:00:00 2001 From: Yusuf Bham Date: Sun, 17 Apr 2022 11:43:45 -0400 Subject: [PATCH 1220/2031] std.mem: add concatWithSentinel --- lib/std/mem.zig | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 88d0e877b8..eec9d4d61f 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -1941,13 +1941,29 @@ test "joinZ" { /// Copies each T from slices into a new slice that exactly holds all the elements. pub fn concat(allocator: Allocator, comptime T: type, slices: []const []const T) ![]T { - if (slices.len == 0) return &[0]T{}; + return concatMaybeSentinel(allocator, T, slices, null); +} + +/// Copies each T from slices into a new slice that exactly holds all the elements. +pub fn concatWithSentinel(allocator: Allocator, comptime T: type, slices: []const []const T, comptime s: T) ![:s]T { + const ret = try concatMaybeSentinel(allocator, T, slices, s); + return ret[0 .. ret.len - 1 :s]; +} + +/// Copies each T from slices into a new slice that exactly holds all the elements as well as the sentinel. +pub fn concatMaybeSentinel(allocator: Allocator, comptime T: type, slices: []const []const T, comptime s: ?T) ![]T { + if (slices.len == 0) return if (s) |sentinel| try allocator.dupe(T, &[1]T{sentinel}) else &[0]T{}; const total_len = blk: { var sum: usize = 0; for (slices) |slice| { sum += slice.len; } + + if (s) |_| { + sum += 1; + } + break :blk sum; }; @@ -1960,6 +1976,10 @@ pub fn concat(allocator: Allocator, comptime T: type, slices: []const []const T) buf_index += slice.len; } + if (s) |sentinel| { + buf[buf.len - 1] = sentinel; + } + // No need for shrink since buf is exactly the correct size. return buf; } @@ -1980,6 +2000,26 @@ test "concat" { defer testing.allocator.free(str); try testing.expect(eql(u32, str, &[_]u32{ 0, 1, 2, 3, 4, 5 })); } + { + const str = try concatWithSentinel(testing.allocator, u8, &[_][]const u8{ "abc", "def", "ghi" }, 0); + defer testing.allocator.free(str); + try testing.expectEqualSentinel(u8, 0, str, "abcdefghi"); + } + { + const slice = try concatWithSentinel(testing.allocator, u8, &[_][]const u8{}, 0); + defer testing.allocator.free(slice); + try testing.expectEqualSentinel(u8, 0, slice, &[_:0]u8{}); + } + { + const slice = try concatWithSentinel(testing.allocator, u32, &[_][]const u32{ + &[_]u32{ 0, 1 }, + &[_]u32{ 2, 3, 4 }, + &[_]u32{}, + &[_]u32{5}, + }, 2); + defer testing.allocator.free(slice); + try testing.expectEqualSentinel(u32, 2, slice, &[_:2]u32{ 0, 1, 2, 3, 4, 5 }); + } } test "testStringEquality" { From 74bfb8ba07cea0029b86f147834c2b271b38eba7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 21 Apr 2022 21:19:01 +0200 Subject: [PATCH 1221/2031] pdb: fix resource mgmt --- lib/std/pdb.zig | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/std/pdb.zig b/lib/std/pdb.zig index 88ae849109..46078d6252 100644 --- a/lib/std/pdb.zig +++ b/lib/std/pdb.zig @@ -498,6 +498,15 @@ pub const Pdb = struct { symbols: []u8, subsect_info: []u8, checksum_offset: ?usize, + + pub fn deinit(self: *Module, allocator: mem.Allocator) void { + allocator.free(self.module_name); + allocator.free(self.obj_file_name); + if (self.populated) { + allocator.free(self.symbols); + allocator.free(self.subsect_info); + } + } }; pub fn init(allocator: mem.Allocator, path: []const u8) !Pdb { @@ -519,6 +528,10 @@ pub const Pdb = struct { pub fn deinit(self: *Pdb) void { self.in_file.close(); + self.msf.deinit(self.allocator); + for (self.modules) |*module| { + module.deinit(self.allocator); + } self.allocator.free(self.modules); self.allocator.free(self.sect_contribs); } @@ -941,6 +954,14 @@ const Msf = struct { .streams = streams, }; } + + fn deinit(self: *Msf, allocator: mem.Allocator) void { + allocator.free(self.directory.blocks); + for (self.streams) |*stream| { + allocator.free(stream.blocks); + } + allocator.free(self.streams); + } }; fn blockCountFromSize(size: u32, block_size: u32) u32 { From ffaa045429c946eaafc199e82b8831cb1e2f637d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Apr 2022 11:17:26 -0700 Subject: [PATCH 1222/2031] stage2: add zig_backend to the cache hash This makes stage2 and stage3 have different cache namespaces, so that building something with stage3 does not try to reuse the same cached artifacts as were produced by stage2. This makes sense since the code of stage3 is produced by the self-hosted compiler, whereas the code of stage2 is produced by the bootstrap compiler. Note also that stage4 and stage3 will share the same zig_backend, end hence cache namespace. Ideally stage4 and stage3 are identical binaries, so this checks out. --- src/Compilation.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Compilation.zig b/src/Compilation.zig index 6019fc0856..bfe52cd59e 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1224,6 +1224,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { // This is shared hasher state common to zig source and all C source files. cache.hash.addBytes(build_options.version); + cache.hash.add(builtin.zig_backend); cache.hash.addBytes(options.zig_lib_directory.path orelse "."); cache.hash.add(options.optimize_mode); cache.hash.add(options.target.cpu.arch); From 9c5fe5b5a435729f2bfc61a45dc6ebd0969faf89 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Apr 2022 20:22:41 -0700 Subject: [PATCH 1223/2031] LLVM: C calling convention lowering fixes For parameters and return types of functions with the C calling convention, the LLVM backend now has a special lowering for the function type that makes the function adhere to the C ABI. The AIR instruction lowerings for call, ret, and ret_load are adjusted to bitcast the real type to the ABI type if necessary. More work on this will need to be done, however, this improvement is enough that stage3 now passes all the same behavior tests that stage2 passes - notably, translate-c no longer has a segfault due to C ABI issues with Zig's Clang C API wrapper. --- src/arch/x86_64/abi.zig | 6 +- src/codegen/llvm.zig | 321 +++++++++++++++++++++++++++++++++++----- src/translate_c.zig | 16 +- src/zig_clang.h | 3 +- 4 files changed, 300 insertions(+), 46 deletions(-) diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index 4afa3ad792..f388c5c93a 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -106,15 +106,15 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class { return result; }, 128 => { - // "Arguments of types__float128,_Decimal128and__m128are + // "Arguments of types__float128, _Decimal128 and__m128 are // split into two halves. The least significant ones belong - // to class SSE, the mostsignificant one to class SSEUP." + // to class SSE, the most significant one to class SSEUP." result[0] = .sse; result[1] = .sseup; return result; }, else => { - // "The 64-bit mantissa of arguments of typelong double + // "The 64-bit mantissa of arguments of type long double // belongs to classX87, the 16-bit exponent plus 6 bytes // of padding belongs to class X87UP." result[0] = .x87; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index ba637cb99e..b2abc536ef 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -21,6 +21,7 @@ const Value = @import("../value.zig").Value; const Type = @import("../type.zig").Type; const LazySrcLoc = Module.LazySrcLoc; const CType = @import("../type.zig").CType; +const x86_64_abi = @import("../arch/x86_64/abi.zig"); const Error = error{ OutOfMemory, CodegenFail }; @@ -2391,27 +2392,20 @@ pub const DeclGen = struct { }, .Fn => { const fn_info = t.fnInfo(); - const sret = firstParamSRet(fn_info, target); - const return_type = fn_info.return_type; - const llvm_sret_ty = if (return_type.hasRuntimeBitsIgnoreComptime()) - try dg.llvmType(return_type) - else - dg.context.voidType(); - const llvm_ret_ty = if (sret) dg.context.voidType() else llvm_sret_ty; + const llvm_ret_ty = try lowerFnRetTy(dg, fn_info); var llvm_params = std.ArrayList(*const llvm.Type).init(dg.gpa); defer llvm_params.deinit(); - if (sret) { + if (firstParamSRet(fn_info, target)) { + const llvm_sret_ty = try dg.llvmType(fn_info.return_type); try llvm_params.append(llvm_sret_ty.pointerType(0)); } for (fn_info.param_types) |param_ty| { if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; - const raw_llvm_ty = try dg.llvmType(param_ty); - const actual_llvm_ty = if (!isByRef(param_ty)) raw_llvm_ty else raw_llvm_ty.pointerType(0); - try llvm_params.append(actual_llvm_ty); + try llvm_params.append(try lowerFnParamTy(dg, fn_info.cc, param_ty)); } return llvm.functionType( @@ -3704,24 +3698,45 @@ pub const FuncGen = struct { break :blk ret_ptr; }; - if (fn_info.is_var_args) { - for (args) |arg| { - try llvm_args.append(try self.resolveInst(arg)); - } - } else { - for (args) |arg, i| { - const param_ty = fn_info.param_types[i]; - if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; + for (args) |arg| { + const param_ty = self.air.typeOf(arg); + if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; - try llvm_args.append(try self.resolveInst(arg)); + const llvm_arg = try self.resolveInst(arg); + const abi_llvm_ty = try lowerFnParamTy(self.dg, fn_info.cc, param_ty); + const param_llvm_ty = llvm_arg.typeOf(); + if (abi_llvm_ty == param_llvm_ty) { + try llvm_args.append(llvm_arg); + continue; } + + // In this case the function param type is honoring the calling convention + // by having a different LLVM type than the usual one. We solve this here + // at the callsite by bitcasting a pointer to our canonical type, then + // loading it if necessary. + const alignment = param_ty.abiAlignment(target); + const ptr_abi_ty = abi_llvm_ty.pointerType(0); + + const casted_ptr = if (isByRef(param_ty)) + self.builder.buildBitCast(llvm_arg, ptr_abi_ty, "") + else p: { + const arg_ptr = self.buildAlloca(param_llvm_ty); + arg_ptr.setAlignment(alignment); + const store_inst = self.builder.buildStore(llvm_arg, arg_ptr); + store_inst.setAlignment(alignment); + break :p self.builder.buildBitCast(arg_ptr, ptr_abi_ty, ""); + }; + + const load_inst = self.builder.buildLoad(casted_ptr, ""); + load_inst.setAlignment(alignment); + try llvm_args.append(load_inst); } const call = self.builder.buildCall( llvm_fn, llvm_args.items.ptr, @intCast(c_uint, llvm_args.items.len), - toLlvmCallConv(zig_fn_ty.fnCallingConvention(), target), + toLlvmCallConv(fn_info.cc, target), attr, "", ); @@ -3735,8 +3750,9 @@ pub const FuncGen = struct { return null; } + const llvm_ret_ty = try self.dg.llvmType(return_type); + if (ret_ptr) |rp| { - const llvm_ret_ty = try self.dg.llvmType(return_type); call.setCallSret(llvm_ret_ty); if (isByRef(return_type)) { return rp; @@ -3748,10 +3764,31 @@ pub const FuncGen = struct { } } + const abi_ret_ty = try lowerFnRetTy(self.dg, fn_info); + + if (abi_ret_ty != llvm_ret_ty) { + // In this case the function return type is honoring the calling convention by having + // a different LLVM type than the usual one. We solve this here at the callsite + // by bitcasting a pointer to our canonical type, then loading it if necessary. + const rp = self.buildAlloca(llvm_ret_ty); + const alignment = return_type.abiAlignment(target); + rp.setAlignment(alignment); + const ptr_abi_ty = abi_ret_ty.pointerType(0); + const casted_ptr = self.builder.buildBitCast(rp, ptr_abi_ty, ""); + const store_inst = self.builder.buildStore(call, casted_ptr); + store_inst.setAlignment(alignment); + if (isByRef(return_type)) { + return rp; + } else { + const load_inst = self.builder.buildLoad(rp, ""); + load_inst.setAlignment(alignment); + return load_inst; + } + } + if (isByRef(return_type)) { // our by-ref status disagrees with sret so we must allocate, store, // and return the allocation pointer. - const llvm_ret_ty = try self.dg.llvmType(return_type); const rp = self.buildAlloca(llvm_ret_ty); const alignment = return_type.abiAlignment(target); rp.setAlignment(alignment); @@ -3781,8 +3818,26 @@ pub const FuncGen = struct { _ = self.builder.buildRetVoid(); return null; } + const fn_info = self.dg.decl.ty.fnInfo(); + const abi_ret_ty = try lowerFnRetTy(self.dg, fn_info); const operand = try self.resolveInst(un_op); - _ = self.builder.buildRet(operand); + const llvm_ret_ty = operand.typeOf(); + if (abi_ret_ty == llvm_ret_ty) { + _ = self.builder.buildRet(operand); + return null; + } + + const target = self.dg.module.getTarget(); + const alignment = ret_ty.abiAlignment(target); + const ptr_abi_ty = abi_ret_ty.pointerType(0); + const rp = self.buildAlloca(llvm_ret_ty); + rp.setAlignment(alignment); + const store_inst = self.builder.buildStore(operand, rp); + store_inst.setAlignment(alignment); + const casted_ptr = self.builder.buildBitCast(rp, ptr_abi_ty, ""); + const load_inst = self.builder.buildLoad(casted_ptr, ""); + load_inst.setAlignment(alignment); + _ = self.builder.buildRet(load_inst); return null; } @@ -3794,9 +3849,16 @@ pub const FuncGen = struct { _ = self.builder.buildRetVoid(); return null; } - const target = self.dg.module.getTarget(); const ptr = try self.resolveInst(un_op); - const loaded = self.builder.buildLoad(ptr, ""); + const target = self.dg.module.getTarget(); + const fn_info = self.dg.decl.ty.fnInfo(); + const abi_ret_ty = try lowerFnRetTy(self.dg, fn_info); + const llvm_ret_ty = try self.dg.llvmType(ret_ty); + const casted_ptr = if (abi_ret_ty == llvm_ret_ty) ptr else p: { + const ptr_abi_ty = abi_ret_ty.pointerType(0); + break :p self.builder.buildBitCast(ptr, ptr_abi_ty, ""); + }; + const loaded = self.builder.buildLoad(casted_ptr, ""); loaded.setAlignment(ret_ty.abiAlignment(target)); _ = self.builder.buildRet(loaded); return null; @@ -7711,20 +7773,211 @@ fn llvmFieldIndex( return null; } } + fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool { switch (fn_info.cc) { .Unspecified, .Inline => return isByRef(fn_info.return_type), - .C => {}, + .C => switch (target.cpu.arch) { + .mips, .mipsel => return false, + .x86_64 => switch (target.os.tag) { + .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory, + else => return x86_64_abi.classifySystemV(fn_info.return_type, target)[0] == .memory, + }, + else => return false, // TODO investigate C ABI for other architectures + }, else => return false, } - const x86_64_abi = @import("../arch/x86_64/abi.zig"); - switch (target.cpu.arch) { - .mips, .mipsel => return false, - .x86_64 => switch (target.os.tag) { - .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory, - else => return x86_64_abi.classifySystemV(fn_info.return_type, target)[0] == .memory, +} + +/// In order to support the C calling convention, some return types need to be lowered +/// completely differently in the function prototype to honor the C ABI, and then +/// be effectively bitcasted to the actual return type. +fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm.Type { + if (!fn_info.return_type.hasRuntimeBitsIgnoreComptime()) { + return dg.context.voidType(); + } + const target = dg.module.getTarget(); + switch (fn_info.cc) { + .Unspecified, .Inline => { + if (isByRef(fn_info.return_type)) { + return dg.context.voidType(); + } else { + return dg.llvmType(fn_info.return_type); + } }, - else => return false, // TODO investigate C ABI for other architectures + .C => { + const is_scalar = switch (fn_info.return_type.zigTypeTag()) { + .Void, + .Bool, + .NoReturn, + .Int, + .Float, + .Pointer, + .Optional, + .ErrorSet, + .Enum, + .AnyFrame, + .Vector, + => true, + + else => false, + }; + switch (target.cpu.arch) { + .mips, .mipsel => return dg.llvmType(fn_info.return_type), + .x86_64 => switch (target.os.tag) { + .windows => switch (x86_64_abi.classifyWindows(fn_info.return_type, target)) { + .integer => { + if (is_scalar) { + return dg.llvmType(fn_info.return_type); + } else { + const abi_size = fn_info.return_type.abiSize(target); + return dg.context.intType(@intCast(c_uint, abi_size * 8)); + } + }, + .memory => return dg.context.voidType(), + .sse => return dg.llvmType(fn_info.return_type), + else => unreachable, + }, + else => { + if (is_scalar) { + return dg.llvmType(fn_info.return_type); + } + const classes = x86_64_abi.classifySystemV(fn_info.return_type, target); + if (classes[0] == .memory) { + return dg.context.voidType(); + } + var llvm_types_buffer: [8]*const llvm.Type = undefined; + var llvm_types_index: u32 = 0; + for (classes) |class| { + switch (class) { + .integer => { + llvm_types_buffer[llvm_types_index] = dg.context.intType(64); + llvm_types_index += 1; + }, + .sse => { + @panic("TODO"); + }, + .sseup => { + @panic("TODO"); + }, + .x87 => { + @panic("TODO"); + }, + .x87up => { + @panic("TODO"); + }, + .complex_x87 => { + @panic("TODO"); + }, + .memory => unreachable, // handled above + .none => break, + } + } + if (classes[0] == .integer and classes[1] == .none) { + return llvm_types_buffer[0]; + } + return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False); + }, + }, + // TODO investigate C ABI for other architectures + else => return dg.llvmType(fn_info.return_type), + } + }, + else => return dg.llvmType(fn_info.return_type), + } +} + +fn lowerFnParamTy(dg: *DeclGen, cc: std.builtin.CallingConvention, ty: Type) !*const llvm.Type { + assert(ty.hasRuntimeBitsIgnoreComptime()); + const target = dg.module.getTarget(); + switch (cc) { + .Unspecified, .Inline => { + const raw_llvm_ty = try dg.llvmType(ty); + if (isByRef(ty)) { + return raw_llvm_ty.pointerType(0); + } else { + return raw_llvm_ty; + } + }, + .C => { + const is_scalar = switch (ty.zigTypeTag()) { + .Void, + .Bool, + .NoReturn, + .Int, + .Float, + .Pointer, + .Optional, + .ErrorSet, + .Enum, + .AnyFrame, + .Vector, + => true, + + else => false, + }; + switch (target.cpu.arch) { + .mips, .mipsel => return dg.llvmType(ty), + .x86_64 => switch (target.os.tag) { + .windows => switch (x86_64_abi.classifyWindows(ty, target)) { + .integer => { + if (is_scalar) { + return dg.llvmType(ty); + } else { + const abi_size = ty.abiSize(target); + return dg.context.intType(@intCast(c_uint, abi_size * 8)); + } + }, + .memory => return (try dg.llvmType(ty)).pointerType(0), + .sse => return dg.llvmType(ty), + else => unreachable, + }, + else => { + if (is_scalar) { + return dg.llvmType(ty); + } + const classes = x86_64_abi.classifySystemV(ty, target); + if (classes[0] == .memory) { + return (try dg.llvmType(ty)).pointerType(0); + } + var llvm_types_buffer: [8]*const llvm.Type = undefined; + var llvm_types_index: u32 = 0; + for (classes) |class| { + switch (class) { + .integer => { + llvm_types_buffer[llvm_types_index] = dg.context.intType(64); + llvm_types_index += 1; + }, + .sse => { + @panic("TODO"); + }, + .sseup => { + @panic("TODO"); + }, + .x87 => { + @panic("TODO"); + }, + .x87up => { + @panic("TODO"); + }, + .complex_x87 => { + @panic("TODO"); + }, + .memory => unreachable, // handled above + .none => break, + } + } + if (classes[0] == .integer and classes[1] == .none) { + return llvm_types_buffer[0]; + } + return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False); + }, + }, + // TODO investigate C ABI for other architectures + else => return dg.llvmType(ty), + } + }, + else => return dg.llvmType(ty), } } diff --git a/src/translate_c.zig b/src/translate_c.zig index 72e0cabaf8..e09ebea4d7 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -384,16 +384,16 @@ pub fn translate( // For memory that has the same lifetime as the Ast that we return // from this function. - var arena = std.heap.ArenaAllocator.init(gpa); - errdefer arena.deinit(); - const arena_allocator = arena.allocator(); + var arena_allocator = std.heap.ArenaAllocator.init(gpa); + errdefer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); var context = Context{ .gpa = gpa, - .arena = arena_allocator, + .arena = arena, .source_manager = ast_unit.getSourceManager(), .alias_list = AliasList.init(gpa), - .global_scope = try arena_allocator.create(Scope.Root), + .global_scope = try arena.create(Scope.Root), .clang_context = ast_unit.getASTContext(), .pattern_list = try PatternList.init(gpa), .zig_is_stage1 = zig_is_stage1, @@ -412,9 +412,9 @@ pub fn translate( inline for (@typeInfo(std.zig.c_builtins).Struct.decls) |decl| { if (decl.is_pub) { - const builtin = try Tag.pub_var_simple.create(context.arena, .{ + const builtin = try Tag.pub_var_simple.create(arena, .{ .name = decl.name, - .init = try Tag.import_c_builtin.create(context.arena, decl.name), + .init = try Tag.import_c_builtin.create(arena, decl.name), }); try addTopLevelDecl(&context, decl.name, builtin); } @@ -431,7 +431,7 @@ pub fn translate( try addMacros(&context); for (context.alias_list.items) |alias| { if (!context.global_scope.sym_table.contains(alias.alias)) { - const node = try Tag.alias.create(context.arena, .{ .actual = alias.alias, .mangled = alias.name }); + const node = try Tag.alias.create(arena, .{ .actual = alias.alias, .mangled = alias.name }); try addTopLevelDecl(&context, alias.alias, node); } } diff --git a/src/zig_clang.h b/src/zig_clang.h index 12eda66d07..267edfaea8 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -1050,7 +1050,8 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangLexer_getLocForEndOfToken(str const ZigClangSourceManager *, const ZigClangASTUnit *); // Can return null. -ZIG_EXTERN_C struct ZigClangASTUnit *ZigClangLoadFromCommandLine(const char **args_begin, const char **args_end, +ZIG_EXTERN_C struct ZigClangASTUnit *ZigClangLoadFromCommandLine( + const char **args_begin, const char **args_end, struct Stage2ErrorMsg **errors_ptr, size_t *errors_len, const char *resources_path); ZIG_EXTERN_C void ZigClangASTUnit_delete(struct ZigClangASTUnit *); ZIG_EXTERN_C void ZigClangErrorMsg_delete(struct Stage2ErrorMsg *ptr, size_t len); From c992b259085070b4faef10f498840d2d3ce394fa Mon Sep 17 00:00:00 2001 From: Annika L Date: Fri, 1 Apr 2022 00:28:46 -0700 Subject: [PATCH 1224/2031] C backend: Fix array declarations --- src/codegen/c.zig | 66 ++++++++++++++++++++++++----------------- test/behavior/array.zig | 18 ++++++++++- test/behavior/fn.zig | 11 ++++++- 3 files changed, 66 insertions(+), 29 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index a0b1bc30b9..464f144f5a 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -61,6 +61,11 @@ const FormatTypeAsCIdentContext = struct { mod: *Module, }; +const ValueRenderLocation = enum { + FunctionArgument, + Other, +}; + /// TODO make this not cut off at 128 bytes fn formatTypeAsCIdentifier( data: FormatTypeAsCIdentContext, @@ -259,7 +264,7 @@ pub const Function = struct { 0, ); try writer.writeAll(" = "); - try f.object.dg.renderValue(writer, ty, val); + try f.object.dg.renderValue(writer, ty, val, .Other); try writer.writeAll(";\n "); return decl_c_value; }, @@ -298,7 +303,7 @@ pub const Function = struct { .constant => |inst| { const ty = f.air.typeOf(inst); const val = f.air.value(inst).?; - return f.object.dg.renderValue(w, ty, val); + return f.object.dg.renderValue(w, ty, val, .Other); }, else => return f.object.dg.writeCValue(w, c_value), } @@ -310,7 +315,7 @@ pub const Function = struct { const ty = f.air.typeOf(inst); const val = f.air.value(inst).?; try w.writeAll("(*"); - try f.object.dg.renderValue(w, ty, val); + try f.object.dg.renderValue(w, ty, val, .Other); return w.writeByte(')'); }, else => return f.object.dg.writeCValueDeref(w, c_value), @@ -384,7 +389,7 @@ pub const DeclGen = struct { try dg.renderTypecast(writer, ty); try writer.writeAll("){"); var buf: Type.SlicePtrFieldTypeBuffer = undefined; - try dg.renderValue(writer, ty.slicePtrFieldType(&buf), val.slicePtr()); + try dg.renderValue(writer, ty.slicePtrFieldType(&buf), val.slicePtr(), .Other); try writer.writeAll(", "); try writer.print("{d}", .{val.sliceLen(dg.module)}); try writer.writeAll("}"); @@ -544,6 +549,7 @@ pub const DeclGen = struct { writer: anytype, ty: Type, val: Value, + location: ValueRenderLocation, ) error{ OutOfMemory, AnalysisFail }!void { const target = dg.module.getTarget(); if (val.isUndefDeep()) { @@ -636,9 +642,9 @@ pub const DeclGen = struct { try writer.writeByte('('); try dg.renderTypecast(writer, ty); try writer.writeAll("){"); - try dg.renderValue(writer, ty.slicePtrFieldType(&buf), slice.ptr); + try dg.renderValue(writer, ty.slicePtrFieldType(&buf), slice.ptr, location); try writer.writeAll(", "); - try dg.renderValue(writer, Type.usize, slice.len); + try dg.renderValue(writer, Type.usize, slice.len, location); try writer.writeAll("}"); }, .function => { @@ -670,7 +676,7 @@ pub const DeclGen = struct { try writer.writeByte('{'); const ai = ty.arrayInfo(); if (ai.sentinel) |s| { - try dg.renderValue(writer, ai.elem_type, s); + try dg.renderValue(writer, ai.elem_type, s, location); } try writer.writeByte('}'); }, @@ -680,17 +686,23 @@ pub const DeclGen = struct { defer arena.deinit(); const arena_allocator = arena.allocator(); + if (location == .FunctionArgument) { + try writer.writeByte('('); + try dg.renderTypecast(writer, ty); + try writer.writeByte(')'); + } + try writer.writeByte('{'); const ai = ty.arrayInfo(); var index: usize = 0; while (index < ai.len) : (index += 1) { if (index != 0) try writer.writeAll(","); const elem_val = try val.elemValue(dg.module, arena_allocator, index); - try dg.renderValue(writer, ai.elem_type, elem_val); + try dg.renderValue(writer, ai.elem_type, elem_val, .Other); } if (ai.sentinel) |s| { if (index != 0) try writer.writeAll(","); - try dg.renderValue(writer, ai.elem_type, s); + try dg.renderValue(writer, ai.elem_type, s, .Other); } try writer.writeByte('}'); }, @@ -701,7 +713,7 @@ pub const DeclGen = struct { var opt_buf: Type.Payload.ElemType = undefined; const payload_type = ty.optionalChild(&opt_buf); if (ty.isPtrLikeOptional()) { - return dg.renderValue(writer, payload_type, val); + return dg.renderValue(writer, payload_type, val, location); } if (payload_type.abiSize(target) == 0) { const is_null = val.castTag(.opt_payload) == null; @@ -713,7 +725,7 @@ pub const DeclGen = struct { if (val.castTag(.opt_payload)) |pl| { const payload_val = pl.data; try writer.writeAll(" .is_null = false, .payload = "); - try dg.renderValue(writer, payload_type, payload_val); + try dg.renderValue(writer, payload_type, payload_val, location); try writer.writeAll(" }"); } else { try writer.writeAll(" .is_null = true }"); @@ -740,7 +752,7 @@ pub const DeclGen = struct { if (!payload_type.hasRuntimeBits()) { // We use the error type directly as the type. const err_val = if (val.errorUnionIsPayload()) Value.initTag(.zero) else val; - return dg.renderValue(writer, error_type, err_val); + return dg.renderValue(writer, error_type, err_val, location); } try writer.writeByte('('); @@ -749,11 +761,11 @@ pub const DeclGen = struct { if (val.castTag(.eu_payload)) |pl| { const payload_val = pl.data; try writer.writeAll(" .payload = "); - try dg.renderValue(writer, payload_type, payload_val); + try dg.renderValue(writer, payload_type, payload_val, location); try writer.writeAll(", .error = 0 }"); } else { try writer.writeAll(" .error = "); - try dg.renderValue(writer, error_type, val); + try dg.renderValue(writer, error_type, val, location); try writer.writeAll(" }"); } }, @@ -767,7 +779,7 @@ pub const DeclGen = struct { const enum_full = ty.cast(Type.Payload.EnumFull).?.data; if (enum_full.values.count() != 0) { const tag_val = enum_full.values.keys()[field_index]; - return dg.renderValue(writer, enum_full.tag_ty, tag_val); + return dg.renderValue(writer, enum_full.tag_ty, tag_val, location); } else { return writer.print("{d}", .{field_index}); } @@ -776,7 +788,7 @@ pub const DeclGen = struct { const enum_obj = ty.castTag(.enum_numbered).?.data; if (enum_obj.values.count() != 0) { const tag_val = enum_obj.values.keys()[field_index]; - return dg.renderValue(writer, enum_obj.tag_ty, tag_val); + return dg.renderValue(writer, enum_obj.tag_ty, tag_val, location); } else { return writer.print("{d}", .{field_index}); } @@ -787,7 +799,7 @@ pub const DeclGen = struct { else => { var int_tag_ty_buffer: Type.Payload.Bits = undefined; const int_tag_ty = ty.intTagType(&int_tag_ty_buffer); - return dg.renderValue(writer, int_tag_ty, val); + return dg.renderValue(writer, int_tag_ty, val, location); }, } }, @@ -814,7 +826,7 @@ pub const DeclGen = struct { if (!field_ty.hasRuntimeBits()) continue; if (i != 0) try writer.writeAll(","); - try dg.renderValue(writer, field_ty, field_val); + try dg.renderValue(writer, field_ty, field_val, location); } try writer.writeAll("}"); @@ -831,7 +843,7 @@ pub const DeclGen = struct { if (ty.unionTagType()) |tag_ty| { if (layout.tag_size != 0) { try writer.writeAll(".tag = "); - try dg.renderValue(writer, tag_ty, union_obj.tag); + try dg.renderValue(writer, tag_ty, union_obj.tag, location); try writer.writeAll(", "); } try writer.writeAll(".payload = {"); @@ -842,7 +854,7 @@ pub const DeclGen = struct { const field_name = ty.unionFields().keys()[index]; if (field_ty.hasRuntimeBits()) { try writer.print(".{ } = ", .{fmtIdent(field_name)}); - try dg.renderValue(writer, field_ty, union_obj.val); + try dg.renderValue(writer, field_ty, union_obj.val, location); } if (ty.unionTagType()) |_| { try writer.writeAll("}"); @@ -988,7 +1000,7 @@ pub const DeclGen = struct { } if (ptr_sentinel) |s| { try bw.writeAll("_s_"); - try dg.renderValue(bw, child_type, s); + try dg.renderValue(bw, child_type, s, .Other); } try bw.writeAll(";\n"); @@ -1629,7 +1641,7 @@ pub fn genDecl(o: *Object) !void { try o.dg.renderTypeAndName(w, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align"); try w.writeAll(" = "); if (variable.init.tag() != .unreachable_value) { - try o.dg.renderValue(w, tv.ty, variable.init); + try o.dg.renderValue(w, tv.ty, variable.init, .Other); } try w.writeAll(";"); try o.indent_writer.insertNewline(); @@ -1644,7 +1656,7 @@ pub fn genDecl(o: *Object) !void { try o.dg.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut, o.dg.decl.@"align"); try writer.writeAll(" = "); - try o.dg.renderValue(writer, tv.ty, tv.val); + try o.dg.renderValue(writer, tv.ty, tv.val, .Other); try writer.writeAll(";\n"); } } @@ -2748,7 +2760,7 @@ fn airCall( try writer.writeAll(", "); } if (f.air.value(arg)) |val| { - try f.object.dg.renderValue(writer, f.air.typeOf(arg), val); + try f.object.dg.renderValue(writer, f.air.typeOf(arg), val, .FunctionArgument); } else { const val = try f.resolveInst(arg); try f.writeCValue(writer, val); @@ -2964,7 +2976,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { for (items) |item| { try f.object.indent_writer.insertNewline(); try writer.writeAll("case "); - try f.object.dg.renderValue(writer, condition_ty, f.air.value(item).?); + try f.object.dg.renderValue(writer, condition_ty, f.air.value(item).?, .Other); try writer.writeAll(": "); } // The case body must be noreturn so we don't need to insert a break. @@ -3414,14 +3426,14 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { try f.writeCValueDeref(writer, operand); try writer.writeAll(" = "); - try f.object.dg.renderValue(writer, error_ty, Value.zero); + try f.object.dg.renderValue(writer, error_ty, Value.zero, .Other); try writer.writeAll(";\n "); return operand; } try f.writeCValueDeref(writer, operand); try writer.writeAll(".error = "); - try f.object.dg.renderValue(writer, error_ty, Value.zero); + try f.object.dg.renderValue(writer, error_ty, Value.zero, .Other); try writer.writeAll(";\n"); // Then return the payload pointer (only if it is used) diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 13a4763a91..bf5e74e819 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -148,7 +148,7 @@ test "void arrays" { try expect(array.len == 4); } -test "nested arrays" { +test "nested arrays of strings" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @@ -162,6 +162,22 @@ test "nested arrays" { } } +test "nested arrays of integers" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + + const array_of_numbers = [_][2]u8{ + [2]u8{ 1, 2 }, + [2]u8{ 3, 4 }, + }; + + try expect(array_of_numbers[0][0] == 1); + try expect(array_of_numbers[0][1] == 2); + try expect(array_of_numbers[1][0] == 3); + try expect(array_of_numbers[1][1] == 4); +} + test "implicit comptime in array type size" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 7d02229ff4..eabb6c77f4 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -349,7 +349,6 @@ fn numberLiteralArg(a: anytype) !void { } test "function call with anon list literal" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; @@ -364,9 +363,19 @@ test "function call with anon list literal" { try expect(vec[1] == 8); try expect(vec[2] == 7); } + + fn doThe2DTest() !void { + try consume2DVec(.{ .{ 9, 8 }, .{ 7, 6 } }); + } + + fn consume2DVec(vec: [2][2]f32) !void { + _ = vec; + } }; try S.doTheTest(); comptime try S.doTheTest(); + try S.doThe2DTest(); + comptime try S.doThe2DTest(); } test "ability to give comptime types and non comptime types to same parameter" { From 0e830b16306945ffc74ef95198eb74743ab9f184 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 22 Apr 2022 07:52:21 -0700 Subject: [PATCH 1225/2031] clean up behavior tests Split big test into the two separate things it is testing. Add missing checks to the test which revealed the test is not actually passing yet for the C backend. --- test/behavior/array.zig | 6 +++--- test/behavior/fn.zig | 30 +++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/test/behavior/array.zig b/test/behavior/array.zig index bf5e74e819..728e05271a 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -163,9 +163,9 @@ test "nested arrays of strings" { } test "nested arrays of integers" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const array_of_numbers = [_][2]u8{ [2]u8{ 1, 2 }, diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index eabb6c77f4..26185f6ac1 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -349,9 +349,9 @@ fn numberLiteralArg(a: anytype) !void { } test "function call with anon list literal" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -363,19 +363,31 @@ test "function call with anon list literal" { try expect(vec[1] == 8); try expect(vec[2] == 7); } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} - fn doThe2DTest() !void { - try consume2DVec(.{ .{ 9, 8 }, .{ 7, 6 } }); +test "function call with anon list literal - 2D" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + try consumeVec(.{ .{ 9, 8 }, .{ 7, 6 } }); } - fn consume2DVec(vec: [2][2]f32) !void { - _ = vec; + fn consumeVec(vec: [2][2]f32) !void { + try expect(vec[0][0] == 9); + try expect(vec[0][1] == 8); + try expect(vec[1][0] == 7); + try expect(vec[1][1] == 6); } }; try S.doTheTest(); comptime try S.doTheTest(); - try S.doThe2DTest(); - comptime try S.doThe2DTest(); } test "ability to give comptime types and non comptime types to same parameter" { From a430630002bf02162ccbf8d3eb10fd73e490cefd Mon Sep 17 00:00:00 2001 From: Tom Read Cutting Date: Fri, 22 Apr 2022 16:12:51 +0100 Subject: [PATCH 1226/2031] Fix C include files not being in `whole` cache (#11365) --- src/Cache.zig | 2 +- src/Compilation.zig | 10 +++++++++- src/Module.zig | 4 ++++ src/stage1.zig | 9 +++++---- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Cache.zig b/src/Cache.zig index 0d4b51492d..2438c5528d 100644 --- a/src/Cache.zig +++ b/src/Cache.zig @@ -690,7 +690,7 @@ pub const Manifest = struct { while (true) { switch (it.next() orelse return) { .target, .target_must_resolve => return, - .prereq => |bytes| try self.addFilePost(bytes), + .prereq => |file_path| try self.addFilePost(file_path), else => |err| { try err.printError(error_buf.writer()); log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items }); diff --git a/src/Compilation.zig b/src/Compilation.zig index bfe52cd59e..d7181c7a0d 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -44,6 +44,7 @@ bin_file: *link.File, c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{}, /// This is a pointer to a local variable inside `update()`. whole_cache_manifest: ?*Cache.Manifest = null, +whole_cache_manifest_mutex: std.Thread.Mutex = .{}, link_error_flags: link.File.ErrorFlags = .{}, @@ -1962,8 +1963,8 @@ pub fn update(comp: *Compilation) !void { // We are about to obtain this lock, so here we give other processes a chance first. comp.bin_file.releaseLock(); - comp.whole_cache_manifest = &man; man = comp.cache_parent.obtain(); + comp.whole_cache_manifest = &man; try comp.addNonIncrementalStuffToCacheManifest(&man); const is_hit = man.hit() catch |err| { @@ -3352,6 +3353,8 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { const dep_basename = std.fs.path.basename(out_dep_path); try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); if (comp.whole_cache_manifest) |whole_cache_manifest| { + comp.whole_cache_manifest_mutex.lock(); + defer comp.whole_cache_manifest_mutex.unlock(); try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); } @@ -3693,6 +3696,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P const dep_basename = std.fs.path.basename(dep_file_path); // Add the files depended on to the cache system. try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); + if (comp.whole_cache_manifest) |whole_cache_manifest| { + comp.whole_cache_manifest_mutex.lock(); + defer comp.whole_cache_manifest_mutex.unlock(); + try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); + } // Just to save disk space, we delete the file because it is never needed again. zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { log.warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) }); diff --git a/src/Module.zig b/src/Module.zig index 1119d73ab0..e637a55dfb 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3855,6 +3855,8 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { }); errdefer gpa.free(resolved_path); + mod.comp.whole_cache_manifest_mutex.lock(); + defer mod.comp.whole_cache_manifest_mutex.unlock(); try man.addFilePostContents(resolved_path, source.bytes, source.stat); } } else { @@ -4336,6 +4338,8 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb if (mod.comp.whole_cache_manifest) |man| { const copied_resolved_path = try gpa.dupe(u8, resolved_path); errdefer gpa.free(copied_resolved_path); + mod.comp.whole_cache_manifest_mutex.lock(); + defer mod.comp.whole_cache_manifest_mutex.unlock(); try man.addFilePostContents(copied_resolved_path, bytes, stat); } diff --git a/src/stage1.zig b/src/stage1.zig index 005dc312ba..2533b242c7 100644 --- a/src/stage1.zig +++ b/src/stage1.zig @@ -455,10 +455,11 @@ export fn stage2_fetch_file( const comp = @intToPtr(*Compilation, stage1.userdata); const file_path = path_ptr[0..path_len]; const max_file_size = std.math.maxInt(u32); - const contents = if (comp.whole_cache_manifest) |man| - man.addFilePostFetch(file_path, max_file_size) catch return null - else - std.fs.cwd().readFileAlloc(comp.gpa, file_path, max_file_size) catch return null; + const contents = if (comp.whole_cache_manifest) |man| blk: { + comp.whole_cache_manifest_mutex.lock(); + defer comp.whole_cache_manifest_mutex.unlock(); + break :blk man.addFilePostFetch(file_path, max_file_size) catch return null; + } else std.fs.cwd().readFileAlloc(comp.gpa, file_path, max_file_size) catch return null; result_len.* = contents.len; // TODO https://github.com/ziglang/zig/issues/3328#issuecomment-716749475 if (contents.len == 0) return @intToPtr(?[*]const u8, 0x1); From 06310e3d4eb47fed88b175891cb5865bb050f020 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 22 Apr 2022 08:19:51 -0700 Subject: [PATCH 1227/2031] Revert "Fix C include files not being in `whole` cache (#11365)" This reverts commit a430630002bf02162ccbf8d3eb10fd73e490cefd. Wait a minute, I'm sorry, I need to revert this. The whole premise of this change is broken because the point of the hash is that it tells whether the same compilation has been done before. This requires items to be added to the hash in the same sequence every time. This means that introducing a lock is fundamentally broken because the order needs to be the same in future runs of the compiler, and not decided by threads racing against each other. The proper solution to this is to, in whole cache mode, append the hash inputs to some data structure, and then after the compilation is complete, do some kind of sorting on the hash inputs so that they will be the same order every time, then apply them in sequence. No lock on the Cache object is needed for this scheme. --- src/Cache.zig | 2 +- src/Compilation.zig | 10 +--------- src/Module.zig | 4 ---- src/stage1.zig | 9 ++++----- 4 files changed, 6 insertions(+), 19 deletions(-) diff --git a/src/Cache.zig b/src/Cache.zig index 2438c5528d..0d4b51492d 100644 --- a/src/Cache.zig +++ b/src/Cache.zig @@ -690,7 +690,7 @@ pub const Manifest = struct { while (true) { switch (it.next() orelse return) { .target, .target_must_resolve => return, - .prereq => |file_path| try self.addFilePost(file_path), + .prereq => |bytes| try self.addFilePost(bytes), else => |err| { try err.printError(error_buf.writer()); log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items }); diff --git a/src/Compilation.zig b/src/Compilation.zig index d7181c7a0d..bfe52cd59e 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -44,7 +44,6 @@ bin_file: *link.File, c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{}, /// This is a pointer to a local variable inside `update()`. whole_cache_manifest: ?*Cache.Manifest = null, -whole_cache_manifest_mutex: std.Thread.Mutex = .{}, link_error_flags: link.File.ErrorFlags = .{}, @@ -1963,8 +1962,8 @@ pub fn update(comp: *Compilation) !void { // We are about to obtain this lock, so here we give other processes a chance first. comp.bin_file.releaseLock(); - man = comp.cache_parent.obtain(); comp.whole_cache_manifest = &man; + man = comp.cache_parent.obtain(); try comp.addNonIncrementalStuffToCacheManifest(&man); const is_hit = man.hit() catch |err| { @@ -3353,8 +3352,6 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { const dep_basename = std.fs.path.basename(out_dep_path); try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); if (comp.whole_cache_manifest) |whole_cache_manifest| { - comp.whole_cache_manifest_mutex.lock(); - defer comp.whole_cache_manifest_mutex.unlock(); try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); } @@ -3696,11 +3693,6 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P const dep_basename = std.fs.path.basename(dep_file_path); // Add the files depended on to the cache system. try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); - if (comp.whole_cache_manifest) |whole_cache_manifest| { - comp.whole_cache_manifest_mutex.lock(); - defer comp.whole_cache_manifest_mutex.unlock(); - try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); - } // Just to save disk space, we delete the file because it is never needed again. zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { log.warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) }); diff --git a/src/Module.zig b/src/Module.zig index e637a55dfb..1119d73ab0 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3855,8 +3855,6 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { }); errdefer gpa.free(resolved_path); - mod.comp.whole_cache_manifest_mutex.lock(); - defer mod.comp.whole_cache_manifest_mutex.unlock(); try man.addFilePostContents(resolved_path, source.bytes, source.stat); } } else { @@ -4338,8 +4336,6 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb if (mod.comp.whole_cache_manifest) |man| { const copied_resolved_path = try gpa.dupe(u8, resolved_path); errdefer gpa.free(copied_resolved_path); - mod.comp.whole_cache_manifest_mutex.lock(); - defer mod.comp.whole_cache_manifest_mutex.unlock(); try man.addFilePostContents(copied_resolved_path, bytes, stat); } diff --git a/src/stage1.zig b/src/stage1.zig index 2533b242c7..005dc312ba 100644 --- a/src/stage1.zig +++ b/src/stage1.zig @@ -455,11 +455,10 @@ export fn stage2_fetch_file( const comp = @intToPtr(*Compilation, stage1.userdata); const file_path = path_ptr[0..path_len]; const max_file_size = std.math.maxInt(u32); - const contents = if (comp.whole_cache_manifest) |man| blk: { - comp.whole_cache_manifest_mutex.lock(); - defer comp.whole_cache_manifest_mutex.unlock(); - break :blk man.addFilePostFetch(file_path, max_file_size) catch return null; - } else std.fs.cwd().readFileAlloc(comp.gpa, file_path, max_file_size) catch return null; + const contents = if (comp.whole_cache_manifest) |man| + man.addFilePostFetch(file_path, max_file_size) catch return null + else + std.fs.cwd().readFileAlloc(comp.gpa, file_path, max_file_size) catch return null; result_len.* = contents.len; // TODO https://github.com/ziglang/zig/issues/3328#issuecomment-716749475 if (contents.len == 0) return @intToPtr(?[*]const u8, 0x1); From fa42fcbc1af8b8713b52a5693c4dfa595ec241b7 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Mon, 28 Mar 2022 13:08:50 -0700 Subject: [PATCH 1228/2031] Sema: combine signed->unsigned and shrinkage runtime checks in intCast --- src/Sema.zig | 52 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 14511fe82d..f0719a38d8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6971,6 +6971,7 @@ fn intCast( operand_src: LazySrcLoc, runtime_safety: bool, ) CompileError!Air.Inst.Ref { + // TODO: Add support for vectors const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_ty); _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand)); @@ -7006,25 +7007,42 @@ fn intCast( const wanted_info = dest_ty.intInfo(target); const actual_bits = actual_info.bits; const wanted_bits = wanted_info.bits; + const actual_value_bits = actual_bits - @boolToInt(actual_info.signedness == .signed); + const wanted_value_bits = wanted_bits - @boolToInt(wanted_info.signedness == .signed); - // requirement: signed to unsigned >= 0 - if (actual_info.signedness == .signed and - wanted_info.signedness == .unsigned) - { - const zero_inst = try sema.addConstant(sema.typeOf(operand), Value.zero); - const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst); - try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data); + // range shrinkage + // requirement: int value fits into target type + if (wanted_value_bits < actual_value_bits) { + const dest_max_val = try dest_ty.maxInt(sema.arena, target); + const dest_max = try sema.addConstant(operand_ty, dest_max_val); + const diff = try block.addBinOp(.subwrap, dest_max, operand); + + if (actual_info.signedness == .signed) { + // Reinterpret the sign-bit as part of the value. This will make + // negative differences (`operand` > `dest_max`) appear too big. + const unsigned_operand_ty = try Type.Tag.int_unsigned.create(sema.arena, actual_bits); + const diff_unsigned = try block.addBitCast(unsigned_operand_ty, diff); + + // If the destination type is signed, then we need to double its + // range to account for negative values. + const dest_range_val = if (wanted_info.signedness == .signed) range_val: { + const range_minus_one = try dest_max_val.shl(Value.one, unsigned_operand_ty, sema.arena, target); + break :range_val try range_minus_one.intAdd(Value.one, unsigned_operand_ty, sema.arena, target); + } else dest_max_val; + const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val); + + const is_in_range = try block.addBinOp(.cmp_lte, diff_unsigned, dest_range); + try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data); + } else { + const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max); + try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data); + } } - - // requirement: unsigned int value fits into target type - if (actual_bits > wanted_bits or - (actual_bits == wanted_bits and - actual_info.signedness == .unsigned and - wanted_info.signedness == .signed)) - { - const max_int = try dest_ty.maxInt(sema.arena, target); - const max_int_inst = try sema.addConstant(operand_ty, max_int); - const is_in_range = try block.addBinOp(.cmp_lte, operand, max_int_inst); + // no shrinkage, yes sign loss + // requirement: signed to unsigned >= 0 + else if (actual_info.signedness == .signed and wanted_info.signedness == .unsigned) { + const zero_inst = try sema.addConstant(operand_ty, Value.zero); + const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst); try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data); } } From eac09ac3506cd1bbd7cde92adb6c4a7ef424f717 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Fri, 22 Apr 2022 22:22:30 -0700 Subject: [PATCH 1229/2031] CBE: disable int128 test since airWrapOp is needed for runtime safety --- test/behavior/int128.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/test/behavior/int128.zig b/test/behavior/int128.zig index f57999511c..08c6dd0e4d 100644 --- a/test/behavior/int128.zig +++ b/test/behavior/int128.zig @@ -46,6 +46,7 @@ test "int128" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var buff: i128 = -1; try expect(buff < 0 and (buff + 1) == 0); From daef82d06fd6b30d2cab7f5a6723cf2e3c7b48c6 Mon Sep 17 00:00:00 2001 From: Morritz <12800230+Morritz@users.noreply.github.com> Date: Sat, 23 Apr 2022 23:58:27 +0200 Subject: [PATCH 1230/2031] add GetProcessTimes binding to the kernel32.zig (#11488) --- lib/std/os/windows/kernel32.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/std/os/windows/kernel32.zig b/lib/std/os/windows/kernel32.zig index f68ac87896..9e6f5df97b 100644 --- a/lib/std/os/windows/kernel32.zig +++ b/lib/std/os/windows/kernel32.zig @@ -216,6 +216,9 @@ pub extern "kernel32" fn GetFullPathNameW( pub extern "kernel32" fn GetOverlappedResult(hFile: HANDLE, lpOverlapped: *OVERLAPPED, lpNumberOfBytesTransferred: *DWORD, bWait: BOOL) callconv(WINAPI) BOOL; pub extern "kernel32" fn GetProcessHeap() callconv(WINAPI) ?HANDLE; + +pub extern "kernel32" fn GetProcessTimes(in_hProcess: HANDLE, out_lpCreationTime: *FILETIME, out_lpExitTime: *FILETIME, out_lpKernelTime: *FILETIME, out_lpUserTime: *FILETIME) callconv(WINAPI) BOOL; + pub extern "kernel32" fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: *DWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) callconv(WINAPI) BOOL; pub extern "kernel32" fn GetQueuedCompletionStatusEx( CompletionPort: HANDLE, From 963ac60918b39cafdda3cb99eff4cd9d20edd839 Mon Sep 17 00:00:00 2001 From: protty <45520026+kprotty@users.noreply.github.com> Date: Sat, 23 Apr 2022 19:35:56 -0500 Subject: [PATCH 1231/2031] std.Thread: Mutex and Condition improvements (#11497) * Thread: minor cleanups * Thread: rewrite Mutex * Thread: introduce Futex.Deadline * Thread: Condition rewrite + cleanup * Mutex: optimize lock fast path * Condition: more docs * Thread: more mutex + condition docs * Thread: remove broken Condition test * Thread: zig fmt * address review comments + fix Thread.DummyMutex in GPA * Atomic: disable bitRmw x86 inline asm for stage2 * GPA: typo mutex_init * Thread: remove noalias on stuff * Thread: comment typos + clarifications --- lib/std/Thread.zig | 27 +- lib/std/Thread/Condition.zig | 793 ++++++++++++--------- lib/std/Thread/Futex.zig | 69 +- lib/std/Thread/Mutex.zig | 495 +++++++------ lib/std/atomic/Atomic.zig | 168 ++--- lib/std/heap/general_purpose_allocator.zig | 11 +- lib/std/os/windows.zig | 13 +- 7 files changed, 873 insertions(+), 703 deletions(-) diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index e28471d6b3..db7117fdd7 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -459,9 +459,8 @@ const UnsupportedImpl = struct { } fn unsupported(unusued: anytype) noreturn { - @compileLog("Unsupported operating system", target.os.tag); _ = unusued; - unreachable; + @compileError("Unsupported operating system " ++ @tagName(target.os.tag)); } }; @@ -1188,27 +1187,3 @@ test "Thread.detach" { event.wait(); try std.testing.expectEqual(value, 1); } - -fn testWaitForSignal(mutex: *Mutex, cond: *Condition) void { - mutex.lock(); - defer mutex.unlock(); - cond.signal(); - cond.wait(mutex); -} - -test "Condition.signal" { - if (builtin.single_threaded) return error.SkipZigTest; - - var mutex = Mutex{}; - var cond = Condition{}; - - var thread: Thread = undefined; - { - mutex.lock(); - defer mutex.unlock(); - thread = try Thread.spawn(.{}, testWaitForSignal, .{ &mutex, &cond }); - cond.wait(&mutex); - cond.signal(); - } - thread.join(); -} diff --git a/lib/std/Thread/Condition.zig b/lib/std/Thread/Condition.zig index fb48db8e53..d5855ec066 100644 --- a/lib/std/Thread/Condition.zig +++ b/lib/std/Thread/Condition.zig @@ -1,411 +1,538 @@ -//! A condition provides a way for a kernel thread to block until it is signaled -//! to wake up. Spurious wakeups are possible. -//! This API supports static initialization and does not require deinitialization. - -impl: Impl = .{}, +//! Condition variables are used with a Mutex to efficiently wait for an arbitrary condition to occur. +//! It does this by atomically unlocking the mutex, blocking the thread until notified, and finally re-locking the mutex. +//! Condition can be statically initialized and is at most `@sizeOf(u64)` large. +//! +//! Example: +//! ``` +//! var m = Mutex{}; +//! var c = Condition{}; +//! var predicate = false; +//! +//! fn consumer() void { +//! m.lock(); +//! defer m.unlock(); +//! +//! while (!predicate) { +//! c.wait(&mutex); +//! } +//! } +//! +//! fn producer() void { +//! m.lock(); +//! defer m.unlock(); +//! +//! predicate = true; +//! c.signal(); +//! } +//! +//! const thread = try std.Thread.spawn(.{}, producer, .{}); +//! consumer(); +//! thread.join(); +//! ``` +//! +//! Note that condition variables can only reliably unblock threads that are sequenced before them using the same Mutex. +//! This means that the following is allowed to deadlock: +//! ``` +//! thread-1: mutex.lock() +//! thread-1: condition.wait(&mutex) +//! +//! thread-2: // mutex.lock() (without this, the following signal may not see the waiting thread-1) +//! thread-2: // mutex.unlock() (this is optional for correctness once locked above, as signal can be called without holding the mutex) +//! thread-2: condition.signal() +//! ``` const std = @import("../std.zig"); const builtin = @import("builtin"); const Condition = @This(); -const windows = std.os.windows; -const linux = std.os.linux; const Mutex = std.Thread.Mutex; + +const os = std.os; const assert = std.debug.assert; const testing = std.testing; +const Atomic = std.atomic.Atomic; +const Futex = std.Thread.Futex; -pub fn wait(cond: *Condition, mutex: *Mutex) void { - cond.impl.wait(mutex); +impl: Impl = .{}, + +/// Atomically releases the Mutex, blocks the caller thread, then re-acquires the Mutex on return. +/// "Atomically" here refers to accesses done on the Condition after acquiring the Mutex. +/// +/// The Mutex must be locked by the caller's thread when this function is called. +/// A Mutex can have multiple Conditions waiting with it concurrently, but not the opposite. +/// It is undefined behavior for multiple threads to wait ith different mutexes using the same Condition concurrently. +/// Once threads have finished waiting with one Mutex, the Condition can be used to wait with another Mutex. +/// +/// A blocking call to wait() is unblocked from one of the following conditions: +/// - a spurious ("at random") wake up occurs +/// - a future call to `signal()` or `broadcast()` which has acquired the Mutex and is sequenced after this `wait()`. +/// +/// Given wait() can be interrupted spuriously, the blocking condition should be checked continuously +/// irrespective of any notifications from `signal()` or `broadcast()`. +pub fn wait(self: *Condition, mutex: *Mutex) void { + self.impl.wait(mutex, null) catch |err| switch (err) { + error.Timeout => unreachable, // no timeout provided so we shouldn't have timed-out + }; } -pub fn timedWait(cond: *Condition, mutex: *Mutex, timeout_ns: u64) error{TimedOut}!void { - try cond.impl.timedWait(mutex, timeout_ns); +/// Atomically releases the Mutex, blocks the caller thread, then re-acquires the Mutex on return. +/// "Atomically" here refers to accesses done on the Condition after acquiring the Mutex. +/// +/// The Mutex must be locked by the caller's thread when this function is called. +/// A Mutex can have multiple Conditions waiting with it concurrently, but not the opposite. +/// It is undefined behavior for multiple threads to wait ith different mutexes using the same Condition concurrently. +/// Once threads have finished waiting with one Mutex, the Condition can be used to wait with another Mutex. +/// +/// A blocking call to `timedWait()` is unblocked from one of the following conditions: +/// - a spurious ("at random") wake occurs +/// - the caller was blocked for around `timeout_ns` nanoseconds, in which `error.Timeout` is returned. +/// - a future call to `signal()` or `broadcast()` which has acquired the Mutex and is sequenced after this `timedWait()`. +/// +/// Given `timedWait()` can be interrupted spuriously, the blocking condition should be checked continuously +/// irrespective of any notifications from `signal()` or `broadcast()`. +pub fn timedWait(self: *Condition, mutex: *Mutex, timeout_ns: u64) error{Timeout}!void { + return self.impl.wait(mutex, timeout_ns); } -pub fn signal(cond: *Condition) void { - cond.impl.signal(); +/// Unblocks at least one thread blocked in a call to `wait()` or `timedWait()` with a given Mutex. +/// The blocked thread must be sequenced before this call with respect to acquiring the same Mutex in order to be observable for unblocking. +/// `signal()` can be called with or without the relevant Mutex being acquired and have no "effect" if there's no observable blocked threads. +pub fn signal(self: *Condition) void { + self.impl.wake(.one); } -pub fn broadcast(cond: *Condition) void { - cond.impl.broadcast(); +/// Unblocks all threads currently blocked in a call to `wait()` or `timedWait()` with a given Mutex. +/// The blocked threads must be sequenced before this call with respect to acquiring the same Mutex in order to be observable for unblocking. +/// `broadcast()` can be called with or without the relevant Mutex being acquired and have no "effect" if there's no observable blocked threads. +pub fn broadcast(self: *Condition) void { + self.impl.wake(.all); } const Impl = if (builtin.single_threaded) - SingleThreadedCondition + SingleThreadedImpl else if (builtin.os.tag == .windows) - WindowsCondition -else if (std.Thread.use_pthreads) - PthreadCondition + WindowsImpl else - AtomicCondition; + FutexImpl; -pub const SingleThreadedCondition = struct { - pub fn wait(cond: *SingleThreadedCondition, mutex: *Mutex) void { - _ = cond; - _ = mutex; - unreachable; // deadlock detected - } +const Notify = enum { + one, // wake up only one thread + all, // wake up all threads +}; - pub fn timedWait(cond: *SingleThreadedCondition, mutex: *Mutex, timeout_ns: u64) error{TimedOut}!void { - _ = cond; +const SingleThreadedImpl = struct { + fn wait(self: *Impl, mutex: *Mutex, timeout: ?u64) error{Timeout}!void { + _ = self; _ = mutex; - _ = timeout_ns; + + // There are no other threads to wake us up. + // So if we wait without a timeout we would never wake up. + const timeout_ns = timeout orelse { + unreachable; // deadlock detected + }; + std.time.sleep(timeout_ns); - return error.TimedOut; + return error.Timeout; } - pub fn signal(cond: *SingleThreadedCondition) void { - _ = cond; - } - - pub fn broadcast(cond: *SingleThreadedCondition) void { - _ = cond; + fn wake(self: *Impl, comptime notify: Notify) void { + // There are no other threads to wake up. + _ = self; + _ = notify; } }; -pub const WindowsCondition = struct { - cond: windows.CONDITION_VARIABLE = windows.CONDITION_VARIABLE_INIT, +const WindowsImpl = struct { + condition: os.windows.CONDITION_VARIABLE = .{}, - pub fn wait(cond: *WindowsCondition, mutex: *Mutex) void { - const rc = windows.kernel32.SleepConditionVariableSRW( - &cond.cond, + fn wait(self: *Impl, mutex: *Mutex, timeout: ?u64) error{Timeout}!void { + var timeout_overflowed = false; + var timeout_ms: os.windows.DWORD = os.windows.INFINITE; + + if (timeout) |timeout_ns| { + // Round the nanoseconds to the nearest millisecond, + // then saturating cast it to windows DWORD for use in kernel32 call. + const ms = (timeout_ns +| (std.time.ns_per_ms / 2)) / std.time.ns_per_ms; + timeout_ms = std.math.cast(os.windows.DWORD, ms) catch std.math.maxInt(os.windows.DWORD); + + // Track if the timeout overflowed into INFINITE and make sure not to wait forever. + if (timeout_ms == os.windows.INFINITE) { + timeout_overflowed = true; + timeout_ms -= 1; + } + } + + const rc = os.windows.kernel32.SleepConditionVariableSRW( + &self.condition, &mutex.impl.srwlock, - windows.INFINITE, - @as(windows.ULONG, 0), + timeout_ms, + 0, // the srwlock was assumed to acquired in exclusive mode not shared ); - assert(rc != windows.FALSE); + + // Return error.Timeout if we know the timeout elapsed correctly. + if (rc == os.windows.FALSE) { + assert(os.windows.kernel32.GetLastError() == .TIMEOUT); + if (!timeout_overflowed) return error.Timeout; + } } - pub fn timedWait(cond: *WindowsCondition, mutex: *Mutex, timeout_ns: u64) error{TimedOut}!void { - var timeout_checked = std.math.cast(windows.DWORD, timeout_ns / std.time.ns_per_ms) catch overflow: { - break :overflow std.math.maxInt(windows.DWORD); - }; - - // Handle the case where timeout is INFINITE, otherwise SleepConditionVariableSRW's time-out never elapses - const timeout_overflowed = timeout_checked == windows.INFINITE; - timeout_checked -= @boolToInt(timeout_overflowed); - - const rc = windows.kernel32.SleepConditionVariableSRW( - &cond.cond, - &mutex.impl.srwlock, - timeout_checked, - @as(windows.ULONG, 0), - ); - if (rc == windows.FALSE and windows.kernel32.GetLastError() == windows.Win32Error.TIMEOUT) return error.TimedOut; - assert(rc != windows.FALSE); - } - - pub fn signal(cond: *WindowsCondition) void { - windows.kernel32.WakeConditionVariable(&cond.cond); - } - - pub fn broadcast(cond: *WindowsCondition) void { - windows.kernel32.WakeAllConditionVariable(&cond.cond); + fn wake(self: *Impl, comptime notify: Notify) void { + switch (notify) { + .one => os.windows.kernel32.WakeConditionVariable(&self.condition), + .all => os.windows.kernel32.WakeAllConditionVariable(&self.condition), + } } }; -pub const PthreadCondition = struct { - cond: std.c.pthread_cond_t = .{}, +const FutexImpl = struct { + state: Atomic(u32) = Atomic(u32).init(0), + epoch: Atomic(u32) = Atomic(u32).init(0), - pub fn wait(cond: *PthreadCondition, mutex: *Mutex) void { - const rc = std.c.pthread_cond_wait(&cond.cond, &mutex.impl.pthread_mutex); - assert(rc == .SUCCESS); - } + const one_waiter = 1; + const waiter_mask = 0xffff; - pub fn timedWait(cond: *PthreadCondition, mutex: *Mutex, timeout_ns: u64) error{TimedOut}!void { - var ts: std.os.timespec = undefined; - std.os.clock_gettime(std.os.CLOCK.REALTIME, &ts) catch unreachable; - ts.tv_sec += @intCast(@TypeOf(ts.tv_sec), timeout_ns / std.time.ns_per_s); - ts.tv_nsec += @intCast(@TypeOf(ts.tv_nsec), timeout_ns % std.time.ns_per_s); - if (ts.tv_nsec >= std.time.ns_per_s) { - ts.tv_sec += 1; - ts.tv_nsec -= std.time.ns_per_s; - } + const one_signal = 1 << 16; + const signal_mask = 0xffff << 16; - const rc = std.c.pthread_cond_timedwait(&cond.cond, &mutex.impl.pthread_mutex, &ts); - return switch (rc) { - .SUCCESS => {}, - .TIMEDOUT => error.TimedOut, - else => unreachable, - }; - } + fn wait(self: *Impl, mutex: *Mutex, timeout: ?u64) error{Timeout}!void { + // Register that we're waiting on the state by incrementing the wait count. + // This assumes that there can be at most ((1<<16)-1) or 65,355 threads concurrently waiting on the same Condvar. + // If this is hit in practice, then this condvar not working is the least of your concerns. + var state = self.state.fetchAdd(one_waiter, .Monotonic); + assert(state & waiter_mask != waiter_mask); + state += one_waiter; - pub fn signal(cond: *PthreadCondition) void { - const rc = std.c.pthread_cond_signal(&cond.cond); - assert(rc == .SUCCESS); - } - - pub fn broadcast(cond: *PthreadCondition) void { - const rc = std.c.pthread_cond_broadcast(&cond.cond); - assert(rc == .SUCCESS); - } -}; - -pub const AtomicCondition = struct { - pending: bool = false, - queue_mutex: Mutex = .{}, - queue_list: QueueList = .{}, - - pub const QueueList = std.SinglyLinkedList(QueueItem); - - pub const QueueItem = struct { - futex: i32 = 0, - dequeued: bool = false, - - fn wait(cond: *@This()) void { - while (@atomicLoad(i32, &cond.futex, .Acquire) == 0) { - switch (builtin.os.tag) { - .linux => { - switch (linux.getErrno(linux.futex_wait( - &cond.futex, - linux.FUTEX.PRIVATE_FLAG | linux.FUTEX.WAIT, - 0, - null, - ))) { - .SUCCESS => {}, - .INTR => {}, - .AGAIN => {}, - else => unreachable, - } - }, - else => std.atomic.spinLoopHint(), - } - } - } - - pub fn timedWait(cond: *@This(), timeout_ns: u64) error{TimedOut}!void { - const start_time = std.time.nanoTimestamp(); - while (@atomicLoad(i32, &cond.futex, .Acquire) == 0) { - switch (builtin.os.tag) { - .linux => { - var ts: std.os.timespec = undefined; - ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), timeout_ns / std.time.ns_per_s); - ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), timeout_ns % std.time.ns_per_s); - switch (linux.getErrno(linux.futex_wait( - &cond.futex, - linux.FUTEX.PRIVATE_FLAG | linux.FUTEX.WAIT, - 0, - &ts, - ))) { - .SUCCESS => {}, - .INTR => {}, - .AGAIN => {}, - .TIMEDOUT => return error.TimedOut, - .INVAL => {}, // possibly timeout overflow - .FAULT => unreachable, - else => unreachable, - } - }, - else => { - if (std.time.nanoTimestamp() - start_time >= timeout_ns) { - return error.TimedOut; - } - std.atomic.spinLoopHint(); - }, - } - } - } - - fn notify(cond: *@This()) void { - @atomicStore(i32, &cond.futex, 1, .Release); - - switch (builtin.os.tag) { - .linux => { - switch (linux.getErrno(linux.futex_wake( - &cond.futex, - linux.FUTEX.PRIVATE_FLAG | linux.FUTEX.WAKE, - 1, - ))) { - .SUCCESS => {}, - .FAULT => {}, - else => unreachable, - } - }, - else => {}, - } - } - }; - - pub fn wait(cond: *AtomicCondition, mutex: *Mutex) void { - var waiter = QueueList.Node{ .data = .{} }; - - { - cond.queue_mutex.lock(); - defer cond.queue_mutex.unlock(); - - cond.queue_list.prepend(&waiter); - @atomicStore(bool, &cond.pending, true, .SeqCst); - } - - mutex.unlock(); - waiter.data.wait(); - mutex.lock(); - } - - pub fn timedWait(cond: *AtomicCondition, mutex: *Mutex, timeout_ns: u64) error{TimedOut}!void { - var waiter = QueueList.Node{ .data = .{} }; - - { - cond.queue_mutex.lock(); - defer cond.queue_mutex.unlock(); - - cond.queue_list.prepend(&waiter); - @atomicStore(bool, &cond.pending, true, .SeqCst); - } - - var timed_out = false; + // Temporarily release the mutex in order to block on the condition variable. mutex.unlock(); defer mutex.lock(); - waiter.data.timedWait(timeout_ns) catch |err| switch (err) { - error.TimedOut => { - defer if (!timed_out) { - waiter.data.wait(); - }; - cond.queue_mutex.lock(); - defer cond.queue_mutex.unlock(); - if (!waiter.data.dequeued) { - timed_out = true; - cond.queue_list.remove(&waiter); - } - }, - else => unreachable, - }; + var futex_deadline = Futex.Deadline.init(timeout); + while (true) { + // Try to wake up by consuming a signal and decremented the waiter we added previously. + // Acquire barrier ensures code before the wake() which added the signal happens before we decrement it and return. + while (state & signal_mask != 0) { + const new_state = state - one_waiter - one_signal; + state = self.state.tryCompareAndSwap(state, new_state, .Acquire, .Monotonic) orelse return; + } - if (timed_out) { - return error.TimedOut; + // Observe the epoch, then check the state again to see if we should wake up. + // The epoch must be observed before we check the state or we could potentially miss a wake() and deadlock: + // + // - T1: s = LOAD(&state) + // - T2: UPDATE(&s, signal) + // - T2: UPDATE(&epoch, 1) + FUTEX_WAKE(&epoch) + // - T1: e = LOAD(&epoch) (was reordered after the state load) + // - T1: s & signals == 0 -> FUTEX_WAIT(&epoch, e) (missed the state update + the epoch change) + // + // Acquire barrier to ensure the epoch load happens before the state load. + const epoch = self.epoch.load(.Acquire); + state = self.state.load(.Monotonic); + if (state & signal_mask != 0) { + continue; + } + + futex_deadline.wait(&self.epoch, epoch) catch |err| switch (err) { + // On timeout, we must decrement the waiter we added above. + error.Timeout => { + while (true) { + // If there's a signal when we're timing out, consume it and report being woken up instead. + // Acquire barrier ensures code before the wake() which added the signal happens before we decrement it and return. + while (state & signal_mask != 0) { + const new_state = state - one_waiter - one_signal; + state = self.state.tryCompareAndSwap(state, new_state, .Acquire, .Monotonic) orelse return; + } + + // Remove the waiter we added and officially return timed out. + const new_state = state - one_waiter; + state = self.state.tryCompareAndSwap(state, new_state, .Monotonic, .Monotonic) orelse return err; + } + }, + }; } } - pub fn signal(cond: *AtomicCondition) void { - if (@atomicLoad(bool, &cond.pending, .SeqCst) == false) - return; + fn wake(self: *Impl, comptime notify: Notify) void { + var state = self.state.load(.Monotonic); + while (true) { + const waiters = (state & waiter_mask) / one_waiter; + const signals = (state & signal_mask) / one_signal; - const maybe_waiter = blk: { - cond.queue_mutex.lock(); - defer cond.queue_mutex.unlock(); - - const maybe_waiter = cond.queue_list.popFirst(); - if (maybe_waiter) |waiter| { - waiter.data.dequeued = true; - } - @atomicStore(bool, &cond.pending, cond.queue_list.first != null, .SeqCst); - break :blk maybe_waiter; - }; - - if (maybe_waiter) |waiter| { - waiter.data.notify(); - } - } - - pub fn broadcast(cond: *AtomicCondition) void { - if (@atomicLoad(bool, &cond.pending, .SeqCst) == false) - return; - - @atomicStore(bool, &cond.pending, false, .SeqCst); - - var waiters = blk: { - cond.queue_mutex.lock(); - defer cond.queue_mutex.unlock(); - - const waiters = cond.queue_list; - - var it = waiters.first; - while (it) |node| : (it = node.next) { - node.data.dequeued = true; + // Reserves which waiters to wake up by incrementing the signals count. + // Therefor, the signals count is always less than or equal to the waiters count. + // We don't need to Futex.wake if there's nothing to wake up or if other wake() threads have reserved to wake up the current waiters. + const wakeable = waiters - signals; + if (wakeable == 0) { + return; } - cond.queue_list = .{}; - break :blk waiters; - }; + const to_wake = switch (notify) { + .one => 1, + .all => wakeable, + }; - while (waiters.popFirst()) |waiter| { - waiter.data.notify(); + // Reserve the amount of waiters to wake by incrementing the signals count. + // Release barrier ensures code before the wake() happens before the signal it posted and consumed by the wait() threads. + const new_state = state + (one_signal * to_wake); + state = self.state.tryCompareAndSwap(state, new_state, .Release, .Monotonic) orelse { + // Wake up the waiting threads we reserved above by changing the epoch value. + // NOTE: a waiting thread could miss a wake up if *exactly* ((1<<32)-1) wake()s happen between it observing the epoch and sleeping on it. + // This is very unlikely due to how many precise amount of Futex.wake() calls that would be between the waiting thread's potential preemption. + // + // Release barrier ensures the signal being added to the state happens before the epoch is changed. + // If not, the waiting thread could potentially deadlock from missing both the state and epoch change: + // + // - T2: UPDATE(&epoch, 1) (reordered before the state change) + // - T1: e = LOAD(&epoch) + // - T1: s = LOAD(&state) + // - T2: UPDATE(&state, signal) + FUTEX_WAKE(&epoch) + // - T1: s & signals == 0 -> FUTEX_WAIT(&epoch, e) (missed both epoch change and state change) + _ = self.epoch.fetchAdd(1, .Release); + Futex.wake(&self.epoch, to_wake); + return; + }; } } }; -test "Thread.Condition" { +test "Condition - smoke test" { + var mutex = Mutex{}; + var cond = Condition{}; + + // Try to wake outside the mutex + defer cond.signal(); + defer cond.broadcast(); + + mutex.lock(); + defer mutex.unlock(); + + // Try to wait with a timeout (should not deadlock) + try testing.expectError(error.Timeout, cond.timedWait(&mutex, 0)); + try testing.expectError(error.Timeout, cond.timedWait(&mutex, std.time.ns_per_ms)); + + // Try to wake inside the mutex. + cond.signal(); + cond.broadcast(); +} + +// Inspired from: https://github.com/Amanieu/parking_lot/pull/129 +test "Condition - wait and signal" { + // This test requires spawning threads if (builtin.single_threaded) { return error.SkipZigTest; } - const TestContext = struct { - cond: *Condition, - cond_main: *Condition, - mutex: *Mutex, - n: *i32, - fn worker(ctx: *@This()) void { - ctx.mutex.lock(); - ctx.n.* += 1; - ctx.cond_main.signal(); - ctx.cond.wait(ctx.mutex); - ctx.n.* -= 1; - ctx.cond_main.signal(); - ctx.mutex.unlock(); + const num_threads = 4; + + const MultiWait = struct { + mutex: Mutex = .{}, + cond: Condition = .{}, + threads: [num_threads]std.Thread = undefined, + + fn run(self: *@This()) void { + self.mutex.lock(); + defer self.mutex.unlock(); + + self.cond.wait(&self.mutex); + self.cond.timedWait(&self.mutex, std.time.ns_per_ms) catch {}; + self.cond.signal(); } }; - const num_threads = 3; - var threads: [num_threads]std.Thread = undefined; - var cond = Condition{}; - var cond_main = Condition{}; - var mut = Mutex{}; - var n: i32 = 0; - var ctx = TestContext{ .cond = &cond, .cond_main = &cond_main, .mutex = &mut, .n = &n }; - mut.lock(); - for (threads) |*t| t.* = try std.Thread.spawn(.{}, TestContext.worker, .{&ctx}); - cond_main.wait(&mut); - while (n < num_threads) cond_main.wait(&mut); + var multi_wait = MultiWait{}; + for (multi_wait.threads) |*t| { + t.* = try std.Thread.spawn(.{}, MultiWait.run, .{&multi_wait}); + } - cond.signal(); - cond_main.wait(&mut); - try testing.expect(n == (num_threads - 1)); + std.time.sleep(100 * std.time.ns_per_ms); - cond.broadcast(); - while (n > 0) cond_main.wait(&mut); - try testing.expect(n == 0); - - for (threads) |t| t.join(); + multi_wait.cond.signal(); + for (multi_wait.threads) |t| { + t.join(); + } } -test "Thread.Condition.timedWait" { +test "Condition - signal" { + // This test requires spawning threads if (builtin.single_threaded) { return error.SkipZigTest; } - var cond = Condition{}; - var mut = Mutex{}; + const num_threads = 4; - // Expect a timeout, as the condition variable is never signaled - { - mut.lock(); - defer mut.unlock(); - try testing.expectError(error.TimedOut, cond.timedWait(&mut, 10 * std.time.ns_per_ms)); + const SignalTest = struct { + mutex: Mutex = .{}, + cond: Condition = .{}, + notified: bool = false, + threads: [num_threads]std.Thread = undefined, + + fn run(self: *@This()) void { + self.mutex.lock(); + defer self.mutex.unlock(); + + // Use timedWait() a few times before using wait() + // to test multiple threads timing out frequently. + var i: usize = 0; + while (!self.notified) : (i +%= 1) { + if (i < 5) { + self.cond.timedWait(&self.mutex, 1) catch {}; + } else { + self.cond.wait(&self.mutex); + } + } + + // Once we received the signal, notify another thread (inside the lock). + assert(self.notified); + self.cond.signal(); + } + }; + + var signal_test = SignalTest{}; + for (signal_test.threads) |*t| { + t.* = try std.Thread.spawn(.{}, SignalTest.run, .{&signal_test}); } - // Expect a signal before timeout { - const TestContext = struct { - cond: *Condition, - mutex: *Mutex, - n: *u32, - fn worker(ctx: *@This()) void { - ctx.mutex.lock(); - defer ctx.mutex.unlock(); - ctx.n.* = 1; - ctx.cond.signal(); - } - }; + // Wait for a bit in hopes that the spawned threads start queuing up on the condvar + std.time.sleep(10 * std.time.ns_per_ms); - var n: u32 = 0; + // Wake up one of them (outside the lock) after setting notified=true. + defer signal_test.cond.signal(); - var ctx = TestContext{ .cond = &cond, .mutex = &mut, .n = &n }; - mut.lock(); - var thread = try std.Thread.spawn(.{}, TestContext.worker, .{&ctx}); - // Looped check to handle spurious wakeups - while (n != 1) try cond.timedWait(&mut, 500 * std.time.ns_per_ms); - mut.unlock(); - try testing.expect(n == 1); - thread.join(); + signal_test.mutex.lock(); + defer signal_test.mutex.unlock(); + + try testing.expect(!signal_test.notified); + signal_test.notified = true; + } + + for (signal_test.threads) |t| { + t.join(); + } +} + +test "Condition - multi signal" { + // This test requires spawning threads + if (builtin.single_threaded) { + return error.SkipZigTest; + } + + const num_threads = 4; + const num_iterations = 4; + + const Paddle = struct { + mutex: Mutex = .{}, + cond: Condition = .{}, + value: u32 = 0, + + fn hit(self: *@This()) void { + defer self.cond.signal(); + + self.mutex.lock(); + defer self.mutex.unlock(); + + self.value += 1; + } + + fn run(self: *@This(), hit_to: *@This()) !void { + self.mutex.lock(); + defer self.mutex.unlock(); + + var current: u32 = 0; + while (current < num_iterations) : (current += 1) { + // Wait for the value to change from hit() + while (self.value == current) { + self.cond.wait(&self.mutex); + } + + // hit the next paddle + try testing.expectEqual(self.value, current + 1); + hit_to.hit(); + } + } + }; + + var paddles = [_]Paddle{.{}} ** num_threads; + var threads = [_]std.Thread{undefined} ** num_threads; + + // Create a circle of paddles which hit each other + for (threads) |*t, i| { + const paddle = &paddles[i]; + const hit_to = &paddles[(i + 1) % paddles.len]; + t.* = try std.Thread.spawn(.{}, Paddle.run, .{ paddle, hit_to }); + } + + // Hit the first paddle and wait for them all to complete by hitting each other for num_iterations. + paddles[0].hit(); + for (threads) |t| t.join(); + + // The first paddle will be hit one last time by the last paddle. + for (paddles) |p, i| { + const expected = @as(u32, num_iterations) + @boolToInt(i == 0); + try testing.expectEqual(p.value, expected); + } +} + +test "Condition - broadcasting" { + // This test requires spawning threads + if (builtin.single_threaded) { + return error.SkipZigTest; + } + + const num_threads = 10; + + const BroadcastTest = struct { + mutex: Mutex = .{}, + cond: Condition = .{}, + completed: Condition = .{}, + count: usize = 0, + threads: [num_threads]std.Thread = undefined, + + fn run(self: *@This()) void { + self.mutex.lock(); + defer self.mutex.unlock(); + + // The last broadcast thread to start tells the main test thread it's completed. + self.count += 1; + if (self.count == num_threads) { + self.completed.signal(); + } + + // Waits for the count to reach zero after the main test thread observes it at num_threads. + // Tries to use timedWait() a bit before falling back to wait() to test multiple threads timing out. + var i: usize = 0; + while (self.count != 0) : (i +%= 1) { + if (i < 10) { + self.cond.timedWait(&self.mutex, 1) catch {}; + } else { + self.cond.wait(&self.mutex); + } + } + } + }; + + var broadcast_test = BroadcastTest{}; + for (broadcast_test.threads) |*t| { + t.* = try std.Thread.spawn(.{}, BroadcastTest.run, .{&broadcast_test}); + } + + { + broadcast_test.mutex.lock(); + defer broadcast_test.mutex.unlock(); + + // Wait for all the broadcast threads to spawn. + // timedWait() to detect any potential deadlocks. + while (broadcast_test.count != num_threads) { + try broadcast_test.completed.timedWait( + &broadcast_test.mutex, + 1 * std.time.ns_per_s, + ); + } + + // Reset the counter and wake all the threads to exit. + broadcast_test.count = 0; + broadcast_test.cond.broadcast(); + } + + for (broadcast_test.threads) |t| { + t.join(); } } diff --git a/lib/std/Thread/Futex.zig b/lib/std/Thread/Futex.zig index a1c8ca71e4..33eb30ba9d 100644 --- a/lib/std/Thread/Futex.zig +++ b/lib/std/Thread/Futex.zig @@ -10,14 +10,12 @@ const Futex = @This(); const os = std.os; const assert = std.debug.assert; const testing = std.testing; - const Atomic = std.atomic.Atomic; -const spinLoopHint = std.atomic.spinLoopHint; /// Checks if `ptr` still contains the value `expect` and, if so, blocks the caller until either: /// - The value at `ptr` is no longer equal to `expect`. /// - The caller is unblocked by a matching `wake()`. -/// - The caller is unblocked spuriously by an arbitrary internal signal. +/// - The caller is unblocked spuriously ("at random"). /// /// The checking of `ptr` and `expect`, along with blocking the caller, is done atomically /// and totally ordered (sequentially consistent) with respect to other wait()/wake() calls on the same `ptr`. @@ -32,7 +30,7 @@ pub fn wait(ptr: *const Atomic(u32), expect: u32) void { /// Checks if `ptr` still contains the value `expect` and, if so, blocks the caller until either: /// - The value at `ptr` is no longer equal to `expect`. /// - The caller is unblocked by a matching `wake()`. -/// - The caller is unblocked spuriously by an arbitrary internal signal. +/// - The caller is unblocked spuriously ("at random"). /// - The caller blocks for longer than the given timeout. In which case, `error.Timeout` is returned. /// /// The checking of `ptr` and `expect`, along with blocking the caller, is done atomically @@ -62,7 +60,7 @@ pub fn wake(ptr: *const Atomic(u32), max_waiters: u32) void { } const Impl = if (builtin.single_threaded) - SerialImpl + SingleThreadedImpl else if (builtin.os.tag == .windows) WindowsImpl else if (builtin.os.tag.isDarwin()) @@ -97,7 +95,7 @@ const UnsupportedImpl = struct { } }; -const SerialImpl = struct { +const SingleThreadedImpl = struct { fn wait(ptr: *const Atomic(u32), expect: u32, timeout: ?u64) error{Timeout}!void { if (ptr.loadUnchecked() != expect) { return; @@ -804,7 +802,7 @@ const PosixImpl = struct { // // What we really want here is a Release load, but that doesn't exist under the C11 memory model. // We could instead do `bucket.pending.fetchAdd(0, Release) == 0` which achieves effectively the same thing, - // but the RMW operation unconditionally stores which invalidates the cache-line for others causing unnecessary contention. + // but the RMW operation unconditionally marks the cache-line as modified for others causing unnecessary fetching/contention. // // Instead we opt to do a full-fence + load instead which avoids taking ownership of the cache-line. // fence(SeqCst) effectively converts the ptr update to SeqCst and the pending load to SeqCst: creating a Store-Load barrier. @@ -962,3 +960,60 @@ test "Futex - broadcasting" { for (broadcast.threads) |*t| t.* = try std.Thread.spawn(.{}, Broadcast.run, .{&broadcast}); for (broadcast.threads) |t| t.join(); } + +/// Deadline is used to wait efficiently for a pointer's value to change using Futex and a fixed timeout. +/// +/// Futex's timedWait() api uses a relative duration which suffers from over-waiting +/// when used in a loop which is often required due to the possibility of spurious wakeups. +/// +/// Deadline instead converts the relative timeout to an absolute one so that multiple calls +/// to Futex timedWait() can block for and report more accurate error.Timeouts. +pub const Deadline = struct { + timeout: ?u64, + started: std.time.Timer, + + /// Create the deadline to expire after the given amount of time in nanoseconds passes. + /// Pass in `null` to have the deadline call `Futex.wait()` and never expire. + pub fn init(expires_in_ns: ?u64) Deadline { + var deadline: Deadline = undefined; + deadline.timeout = expires_in_ns; + + // std.time.Timer is required to be supported for somewhat accurate reportings of error.Timeout. + if (deadline.timeout != null) { + deadline.started = std.time.Timer.start() catch unreachable; + } + + return deadline; + } + + /// Wait until either: + /// - the `ptr`'s value changes from `expect`. + /// - `Futex.wake()` is called on the `ptr`. + /// - A spurious wake occurs. + /// - The deadline expires; In which case `error.Timeout` is returned. + pub fn wait(self: *Deadline, ptr: *const Atomic(u32), expect: u32) error{Timeout}!void { + @setCold(true); + + // Check if we actually have a timeout to wait until. + // If not just wait "forever". + const timeout_ns = self.timeout orelse { + return Futex.wait(ptr, expect); + }; + + // Get how much time has passed since we started waiting + // then subtract that from the init() timeout to get how much longer to wait. + // Use overflow to detect when we've been waiting longer than the init() timeout. + const elapsed_ns = self.started.read(); + const until_timeout_ns = std.math.sub(u64, timeout_ns, elapsed_ns) catch 0; + return Futex.timedWait(ptr, expect, until_timeout_ns); + } +}; + +test "Futex - Deadline" { + var deadline = Deadline.init(100 * std.time.ns_per_ms); + var futex_word = Atomic(u32).init(0); + + while (true) { + deadline.wait(&futex_word, 0) catch break; + } +} diff --git a/lib/std/Thread/Mutex.zig b/lib/std/Thread/Mutex.zig index c8f5c0534d..35d754ad19 100644 --- a/lib/std/Thread/Mutex.zig +++ b/lib/std/Thread/Mutex.zig @@ -1,288 +1,285 @@ -//! Lock may be held only once. If the same thread tries to acquire -//! the same mutex twice, it deadlocks. This type supports static -//! initialization and is at most `@sizeOf(usize)` in size. When an -//! application is built in single threaded release mode, all the -//! functions are no-ops. In single threaded debug mode, there is -//! deadlock detection. +//! Mutex is a synchronization primitive which enforces atomic access to a shared region of code known as the "critical section". +//! It does this by blocking ensuring only one thread is in the critical section at any given point in time by blocking the others. +//! Mutex can be statically initialized and is at most `@sizeOf(u64)` large. +//! Use `lock()` or `tryLock()` to enter the critical section and `unlock()` to leave it. //! -//! Example usage: +//! Example: +//! ``` //! var m = Mutex{}; //! -//! m.lock(); -//! defer m.release(); -//! ... critical code +//! { +//! m.lock(); +//! defer m.unlock(); +//! // ... critical section code +//! } //! -//! Non-blocking: //! if (m.tryLock()) { //! defer m.unlock(); -//! // ... critical section -//! } else { -//! // ... lock not acquired +//! // ... critical section code //! } +//! ``` + +const std = @import("../std.zig"); +const builtin = @import("builtin"); +const Mutex = @This(); + +const os = std.os; +const assert = std.debug.assert; +const testing = std.testing; +const Atomic = std.atomic.Atomic; +const Futex = std.Thread.Futex; impl: Impl = .{}, -const Mutex = @This(); -const std = @import("../std.zig"); -const builtin = @import("builtin"); -const os = std.os; -const assert = std.debug.assert; -const windows = os.windows; -const linux = os.linux; -const testing = std.testing; -const StaticResetEvent = std.thread.StaticResetEvent; - -/// Try to acquire the mutex without blocking. Returns `false` if the mutex is -/// unavailable. Otherwise returns `true`. Call `unlock` on the mutex to release. -pub fn tryLock(m: *Mutex) bool { - return m.impl.tryLock(); +/// Tries to acquire the mutex without blocking the caller's thread. +/// Returns `false` if the calling thread would have to block to acquire it. +/// Otherwise, returns `true` and the caller should `unlock()` the Mutex to release it. +pub fn tryLock(self: *Mutex) bool { + return self.impl.tryLock(); } -/// Acquire the mutex. Deadlocks if the mutex is already -/// held by the calling thread. -pub fn lock(m: *Mutex) void { - m.impl.lock(); +/// Acquires the mutex, blocking the caller's thread until it can. +/// It is undefined behavior if the mutex is already held by the caller's thread. +/// Once acquired, call `unlock()` on the Mutex to release it. +pub fn lock(self: *Mutex) void { + self.impl.lock(); } -pub fn unlock(m: *Mutex) void { - m.impl.unlock(); +/// Releases the mutex which was previously acquired with `lock()` or `tryLock()`. +/// It is undefined behavior if the mutex is unlocked from a different thread that it was locked from. +pub fn unlock(self: *Mutex) void { + self.impl.unlock(); } const Impl = if (builtin.single_threaded) - Dummy + SingleThreadedImpl else if (builtin.os.tag == .windows) - WindowsMutex -else if (std.Thread.use_pthreads) - PthreadMutex + WindowsImpl +else if (builtin.os.tag.isDarwin()) + DarwinImpl else - AtomicMutex; + FutexImpl; -pub const AtomicMutex = struct { - state: State = .unlocked, +const SingleThreadedImpl = struct { + is_locked: bool = false, - const State = enum(i32) { - unlocked, - locked, - waiting, - }; - - pub fn tryLock(m: *AtomicMutex) bool { - return @cmpxchgStrong( - State, - &m.state, - .unlocked, - .locked, - .Acquire, - .Monotonic, - ) == null; - } - - pub fn lock(m: *AtomicMutex) void { - switch (@atomicRmw(State, &m.state, .Xchg, .locked, .Acquire)) { - .unlocked => {}, - else => |s| m.lockSlow(s), - } - } - - pub fn unlock(m: *AtomicMutex) void { - switch (@atomicRmw(State, &m.state, .Xchg, .unlocked, .Release)) { - .unlocked => unreachable, - .locked => {}, - .waiting => m.unlockSlow(), - } - } - - fn lockSlow(m: *AtomicMutex, current_state: State) void { - @setCold(true); - var new_state = current_state; - - var spin: u8 = 0; - while (spin < 100) : (spin += 1) { - const state = @cmpxchgWeak( - State, - &m.state, - .unlocked, - new_state, - .Acquire, - .Monotonic, - ) orelse return; - - switch (state) { - .unlocked => {}, - .locked => {}, - .waiting => break, - } - - var iter = std.math.min(32, spin + 1); - while (iter > 0) : (iter -= 1) - std.atomic.spinLoopHint(); - } - - new_state = .waiting; - while (true) { - switch (@atomicRmw(State, &m.state, .Xchg, new_state, .Acquire)) { - .unlocked => return, - else => {}, - } - switch (builtin.os.tag) { - .linux => { - switch (linux.getErrno(linux.futex_wait( - @ptrCast(*const i32, &m.state), - linux.FUTEX.PRIVATE_FLAG | linux.FUTEX.WAIT, - @enumToInt(new_state), - null, - ))) { - .SUCCESS => {}, - .INTR => {}, - .AGAIN => {}, - else => unreachable, - } - }, - else => std.atomic.spinLoopHint(), - } - } - } - - fn unlockSlow(m: *AtomicMutex) void { - @setCold(true); - - switch (builtin.os.tag) { - .linux => { - switch (linux.getErrno(linux.futex_wake( - @ptrCast(*const i32, &m.state), - linux.FUTEX.PRIVATE_FLAG | linux.FUTEX.WAKE, - 1, - ))) { - .SUCCESS => {}, - .FAULT => unreachable, // invalid pointer passed to futex_wake - else => unreachable, - } - }, - else => {}, - } - } -}; - -pub const PthreadMutex = struct { - pthread_mutex: std.c.pthread_mutex_t = .{}, - - /// Try to acquire the mutex without blocking. Returns true if - /// the mutex is unavailable. Otherwise returns false. Call - /// release when done. - pub fn tryLock(m: *PthreadMutex) bool { - return std.c.pthread_mutex_trylock(&m.pthread_mutex) == .SUCCESS; - } - - /// Acquire the mutex. Will deadlock if the mutex is already - /// held by the calling thread. - pub fn lock(m: *PthreadMutex) void { - switch (std.c.pthread_mutex_lock(&m.pthread_mutex)) { - .SUCCESS => {}, - .INVAL => unreachable, - .BUSY => unreachable, - .AGAIN => unreachable, - .DEADLK => unreachable, - .PERM => unreachable, - else => unreachable, - } - } - - pub fn unlock(m: *PthreadMutex) void { - switch (std.c.pthread_mutex_unlock(&m.pthread_mutex)) { - .SUCCESS => return, - .INVAL => unreachable, - .AGAIN => unreachable, - .PERM => unreachable, - else => unreachable, - } - } -}; - -/// This has the sematics as `Mutex`, however it does not actually do any -/// synchronization. Operations are safety-checked no-ops. -pub const Dummy = struct { - locked: @TypeOf(lock_init) = lock_init, - - const lock_init = if (std.debug.runtime_safety) false else {}; - - /// Try to acquire the mutex without blocking. Returns false if - /// the mutex is unavailable. Otherwise returns true. - pub fn tryLock(m: *Dummy) bool { - if (std.debug.runtime_safety) { - if (m.locked) return false; - m.locked = true; - } + fn tryLock(self: *Impl) bool { + if (self.is_locked) return false; + self.is_locked = true; return true; } - /// Acquire the mutex. Will deadlock if the mutex is already - /// held by the calling thread. - pub fn lock(m: *Dummy) void { - if (!m.tryLock()) { - @panic("deadlock detected"); + fn lock(self: *Impl) void { + if (!self.tryLock()) { + unreachable; // deadlock detected } } - pub fn unlock(m: *Dummy) void { - if (std.debug.runtime_safety) { - m.locked = false; + fn unlock(self: *Impl) void { + assert(self.is_locked); + self.is_locked = false; + } +}; + +// SRWLOCK on windows is almost always faster than Futex solution. +// It also implements an efficient Condition with requeue support for us. +const WindowsImpl = struct { + srwlock: os.windows.SRWLOCK = .{}, + + fn tryLock(self: *Impl) bool { + return os.windows.kernel32.TryAcquireSRWLockExclusive(&self.srwlock) != os.windows.FALSE; + } + + fn lock(self: *Impl) void { + os.windows.kernel32.AcquireSRWLockExclusive(&self.srwlock); + } + + fn unlock(self: *Impl) void { + os.windows.kernel32.ReleaseSRWLockExclusive(&self.srwlock); + } +}; + +// os_unfair_lock on darwin supports priority inheritance and is generally faster than Futex solutions. +const DarwinImpl = struct { + oul: os.darwin.os_unfair_lock = .{}, + + fn tryLock(self: *Impl) bool { + return os.darwin.os_unfair_lock_trylock(&self.oul); + } + + fn lock(self: *Impl) void { + os.darwin.os_unfair_lock_lock(&self.oul); + } + + fn unlock(self: *Impl) void { + os.darwin.os_unfair_lock_unlock(&self.oul); + } +}; + +const FutexImpl = struct { + state: Atomic(u32) = Atomic(u32).init(unlocked), + + const unlocked = 0b00; + const locked = 0b01; + const contended = 0b11; // must contain the `locked` bit for x86 optimization below + + fn tryLock(self: *Impl) bool { + // Lock with compareAndSwap instead of tryCompareAndSwap to avoid reporting spurious CAS failure. + return self.lockFast("compareAndSwap"); + } + + fn lock(self: *Impl) void { + // Lock with tryCompareAndSwap instead of compareAndSwap due to being more inline-able on LL/SC archs like ARM. + if (!self.lockFast("tryCompareAndSwap")) { + self.lockSlow(); + } + } + + inline fn lockFast(self: *Impl, comptime casFn: []const u8) bool { + // On x86, use `lock bts` instead of `lock cmpxchg` as: + // - they both seem to mark the cache-line as modified regardless: https://stackoverflow.com/a/63350048 + // - `lock bts` is smaller instruction-wise which makes it better for inlining + if (comptime builtin.target.cpu.arch.isX86()) { + const locked_bit = @ctz(u32, @as(u32, locked)); + return self.state.bitSet(locked_bit, .Acquire) == 0; + } + + // Acquire barrier ensures grabbing the lock happens before the critical section + // and that the previous lock holder's critical section happens before we grab the lock. + return @field(self.state, casFn)(unlocked, locked, .Acquire, .Monotonic) == null; + } + + fn lockSlow(self: *Impl) void { + @setCold(true); + + // Avoid doing an atomic swap below if we already know the state is contended. + // An atomic swap unconditionally stores which marks the cache-line as modified unnecessarily. + if (self.state.load(.Monotonic) == contended) { + Futex.wait(&self.state, contended); + } + + // Try to acquire the lock while also telling the existing lock holder that there are threads waiting. + // + // Once we sleep on the Futex, we must acquire the mutex using `contended` rather than `locked`. + // If not, threads sleeping on the Futex wouldn't see the state change in unlock and potentially deadlock. + // The downside is that the last mutex unlocker will see `contended` and do an unnecessary Futex wake + // but this is better than having to wake all waiting threads on mutex unlock. + // + // Acquire barrier ensures grabbing the lock happens before the critical section + // and that the previous lock holder's critical section happens before we grab the lock. + while (self.state.swap(contended, .Acquire) != unlocked) { + Futex.wait(&self.state, contended); + } + } + + fn unlock(self: *Impl) void { + // Unlock the mutex and wake up a waiting thread if any. + // + // A waiting thread will acquire with `contended` instead of `locked` + // which ensures that it wakes up another thread on the next unlock(). + // + // Release barrier ensures the critical section happens before we let go of the lock + // and that our critical section happens before the next lock holder grabs the lock. + const state = self.state.swap(unlocked, .Release); + assert(state != unlocked); + + if (state == contended) { + Futex.wake(&self.state, 1); } } }; -pub const WindowsMutex = struct { - srwlock: windows.SRWLOCK = windows.SRWLOCK_INIT, - - pub fn tryLock(m: *WindowsMutex) bool { - return windows.kernel32.TryAcquireSRWLockExclusive(&m.srwlock) != windows.FALSE; - } - - pub fn lock(m: *WindowsMutex) void { - windows.kernel32.AcquireSRWLockExclusive(&m.srwlock); - } - - pub fn unlock(m: *WindowsMutex) void { - windows.kernel32.ReleaseSRWLockExclusive(&m.srwlock); - } -}; - -const TestContext = struct { - mutex: *Mutex, - data: i128, - - const incr_count = 10000; -}; - -test "basic usage" { +test "Mutex - smoke test" { var mutex = Mutex{}; - var context = TestContext{ - .mutex = &mutex, - .data = 0, + try testing.expect(mutex.tryLock()); + try testing.expect(!mutex.tryLock()); + mutex.unlock(); + + mutex.lock(); + try testing.expect(!mutex.tryLock()); + mutex.unlock(); +} + +// A counter which is incremented without atomic instructions +const NonAtomicCounter = struct { + // direct u128 could maybe use xmm ops on x86 which are atomic + value: [2]u64 = [_]u64{ 0, 0 }, + + fn get(self: NonAtomicCounter) u128 { + return @bitCast(u128, self.value); + } + + fn inc(self: *NonAtomicCounter) void { + for (@bitCast([2]u64, self.get() + 1)) |v, i| { + @ptrCast(*volatile u64, &self.value[i]).* = v; + } + } +}; + +test "Mutex - many uncontended" { + // This test requires spawning threads. + if (builtin.single_threaded) { + return error.SkipZigTest; + } + + const num_threads = 4; + const num_increments = 1000; + + const Runner = struct { + mutex: Mutex = .{}, + thread: std.Thread = undefined, + counter: NonAtomicCounter = .{}, + + fn run(self: *@This()) void { + var i: usize = num_increments; + while (i > 0) : (i -= 1) { + self.mutex.lock(); + defer self.mutex.unlock(); + + self.counter.inc(); + } + } }; + var runners = [_]Runner{.{}} ** num_threads; + for (runners) |*r| r.thread = try std.Thread.spawn(.{}, Runner.run, .{r}); + for (runners) |r| r.thread.join(); + for (runners) |r| try testing.expectEqual(r.counter.get(), num_increments); +} + +test "Mutex - many contended" { + // This test requires spawning threads. if (builtin.single_threaded) { - worker(&context); - try testing.expect(context.data == TestContext.incr_count); - } else { - const thread_count = 10; - var threads: [thread_count]std.Thread = undefined; - for (threads) |*t| { - t.* = try std.Thread.spawn(.{}, worker, .{&context}); + return error.SkipZigTest; + } + + const num_threads = 4; + const num_increments = 1000; + + const Runner = struct { + mutex: Mutex = .{}, + counter: NonAtomicCounter = .{}, + + fn run(self: *@This()) void { + var i: usize = num_increments; + while (i > 0) : (i -= 1) { + // Occasionally hint to let another thread run. + defer if (i % 100 == 0) std.Thread.yield() catch {}; + + self.mutex.lock(); + defer self.mutex.unlock(); + + self.counter.inc(); + } } - for (threads) |t| - t.join(); + }; - try testing.expect(context.data == thread_count * TestContext.incr_count); - } -} - -fn worker(ctx: *TestContext) void { - var i: usize = 0; - while (i != TestContext.incr_count) : (i += 1) { - ctx.mutex.lock(); - defer ctx.mutex.unlock(); - - ctx.data += 1; - } + var runner = Runner{}; + + var threads: [num_threads]std.Thread = undefined; + for (threads) |*t| t.* = try std.Thread.spawn(.{}, Runner.run, .{&runner}); + for (threads) |t| t.join(); + + try testing.expectEqual(runner.counter.get(), num_increments * num_threads); } diff --git a/lib/std/atomic/Atomic.zig b/lib/std/atomic/Atomic.zig index 336230333a..c396281e91 100644 --- a/lib/std/atomic/Atomic.zig +++ b/lib/std/atomic/Atomic.zig @@ -1,7 +1,7 @@ const std = @import("../std.zig"); +const builtin = @import("builtin"); const testing = std.testing; -const target = @import("builtin").target; const Ordering = std.atomic.Ordering; pub fn Atomic(comptime T: type) type { @@ -164,87 +164,13 @@ pub fn Atomic(comptime T: type) type { return bitRmw(self, .Toggle, bit, ordering); } - inline fn bitRmw( - self: *Self, - comptime op: BitRmwOp, - bit: Bit, - comptime ordering: Ordering, - ) u1 { + inline fn bitRmw(self: *Self, comptime op: BitRmwOp, bit: Bit, comptime ordering: Ordering) u1 { // x86 supports dedicated bitwise instructions - if (comptime target.cpu.arch.isX86() and @sizeOf(T) >= 2 and @sizeOf(T) <= 8) { - const old_bit: u8 = switch (@sizeOf(T)) { - 2 => switch (op) { - .Set => asm volatile ("lock btsw %[bit], %[ptr]" - // LLVM doesn't support u1 flag register return values - : [result] "={@ccc}" (-> u8), - : [ptr] "*p" (&self.value), - [bit] "X" (@as(T, bit)), - : "cc", "memory" - ), - .Reset => asm volatile ("lock btrw %[bit], %[ptr]" - // LLVM doesn't support u1 flag register return values - : [result] "={@ccc}" (-> u8), - : [ptr] "*p" (&self.value), - [bit] "X" (@as(T, bit)), - : "cc", "memory" - ), - .Toggle => asm volatile ("lock btcw %[bit], %[ptr]" - // LLVM doesn't support u1 flag register return values - : [result] "={@ccc}" (-> u8), - : [ptr] "*p" (&self.value), - [bit] "X" (@as(T, bit)), - : "cc", "memory" - ), - }, - 4 => switch (op) { - .Set => asm volatile ("lock btsl %[bit], %[ptr]" - // LLVM doesn't support u1 flag register return values - : [result] "={@ccc}" (-> u8), - : [ptr] "*p" (&self.value), - [bit] "X" (@as(T, bit)), - : "cc", "memory" - ), - .Reset => asm volatile ("lock btrl %[bit], %[ptr]" - // LLVM doesn't support u1 flag register return values - : [result] "={@ccc}" (-> u8), - : [ptr] "*p" (&self.value), - [bit] "X" (@as(T, bit)), - : "cc", "memory" - ), - .Toggle => asm volatile ("lock btcl %[bit], %[ptr]" - // LLVM doesn't support u1 flag register return values - : [result] "={@ccc}" (-> u8), - : [ptr] "*p" (&self.value), - [bit] "X" (@as(T, bit)), - : "cc", "memory" - ), - }, - 8 => switch (op) { - .Set => asm volatile ("lock btsq %[bit], %[ptr]" - // LLVM doesn't support u1 flag register return values - : [result] "={@ccc}" (-> u8), - : [ptr] "*p" (&self.value), - [bit] "X" (@as(T, bit)), - : "cc", "memory" - ), - .Reset => asm volatile ("lock btrq %[bit], %[ptr]" - // LLVM doesn't support u1 flag register return values - : [result] "={@ccc}" (-> u8), - : [ptr] "*p" (&self.value), - [bit] "X" (@as(T, bit)), - : "cc", "memory" - ), - .Toggle => asm volatile ("lock btcq %[bit], %[ptr]" - // LLVM doesn't support u1 flag register return values - : [result] "={@ccc}" (-> u8), - : [ptr] "*p" (&self.value), - [bit] "X" (@as(T, bit)), - : "cc", "memory" - ), - }, - else => @compileError("Invalid atomic type " ++ @typeName(T)), - }; - return @intCast(u1, old_bit); + if (comptime builtin.target.cpu.arch.isX86() and @sizeOf(T) >= 2 and @sizeOf(T) <= 8) { + // TODO: stage2 currently doesn't like the inline asm this function emits. + if (builtin.zig_backend == .stage1) { + return x86BitRmw(self, op, bit, ordering); + } } const mask = @as(T, 1) << bit; @@ -256,6 +182,86 @@ pub fn Atomic(comptime T: type) type { return @boolToInt(value & mask != 0); } + + inline fn x86BitRmw(self: *Self, comptime op: BitRmwOp, bit: Bit, comptime ordering: Ordering) u1 { + const old_bit: u8 = switch (@sizeOf(T)) { + 2 => switch (op) { + .Set => asm volatile ("lock btsw %[bit], %[ptr]" + // LLVM doesn't support u1 flag register return values + : [result] "={@ccc}" (-> u8), + : [ptr] "*p" (&self.value), + [bit] "X" (@as(T, bit)), + : "cc", "memory" + ), + .Reset => asm volatile ("lock btrw %[bit], %[ptr]" + // LLVM doesn't support u1 flag register return values + : [result] "={@ccc}" (-> u8), + : [ptr] "*p" (&self.value), + [bit] "X" (@as(T, bit)), + : "cc", "memory" + ), + .Toggle => asm volatile ("lock btcw %[bit], %[ptr]" + // LLVM doesn't support u1 flag register return values + : [result] "={@ccc}" (-> u8), + : [ptr] "*p" (&self.value), + [bit] "X" (@as(T, bit)), + : "cc", "memory" + ), + }, + 4 => switch (op) { + .Set => asm volatile ("lock btsl %[bit], %[ptr]" + // LLVM doesn't support u1 flag register return values + : [result] "={@ccc}" (-> u8), + : [ptr] "*p" (&self.value), + [bit] "X" (@as(T, bit)), + : "cc", "memory" + ), + .Reset => asm volatile ("lock btrl %[bit], %[ptr]" + // LLVM doesn't support u1 flag register return values + : [result] "={@ccc}" (-> u8), + : [ptr] "*p" (&self.value), + [bit] "X" (@as(T, bit)), + : "cc", "memory" + ), + .Toggle => asm volatile ("lock btcl %[bit], %[ptr]" + // LLVM doesn't support u1 flag register return values + : [result] "={@ccc}" (-> u8), + : [ptr] "*p" (&self.value), + [bit] "X" (@as(T, bit)), + : "cc", "memory" + ), + }, + 8 => switch (op) { + .Set => asm volatile ("lock btsq %[bit], %[ptr]" + // LLVM doesn't support u1 flag register return values + : [result] "={@ccc}" (-> u8), + : [ptr] "*p" (&self.value), + [bit] "X" (@as(T, bit)), + : "cc", "memory" + ), + .Reset => asm volatile ("lock btrq %[bit], %[ptr]" + // LLVM doesn't support u1 flag register return values + : [result] "={@ccc}" (-> u8), + : [ptr] "*p" (&self.value), + [bit] "X" (@as(T, bit)), + : "cc", "memory" + ), + .Toggle => asm volatile ("lock btcq %[bit], %[ptr]" + // LLVM doesn't support u1 flag register return values + : [result] "={@ccc}" (-> u8), + : [ptr] "*p" (&self.value), + [bit] "X" (@as(T, bit)), + : "cc", "memory" + ), + }, + else => @compileError("Invalid atomic type " ++ @typeName(T)), + }; + + // TODO: emit appropriate tsan fence if compiling with tsan + _ = ordering; + + return @intCast(u1, old_bit); + } }); }; } diff --git a/lib/std/heap/general_purpose_allocator.zig b/lib/std/heap/general_purpose_allocator.zig index 7359fe402f..11d897ac1b 100644 --- a/lib/std/heap/general_purpose_allocator.zig +++ b/lib/std/heap/general_purpose_allocator.zig @@ -151,12 +151,12 @@ pub const Config = struct { /// What type of mutex you'd like to use, for thread safety. /// when specfied, the mutex type must have the same shape as `std.Thread.Mutex` and - /// `std.Thread.Mutex.Dummy`, and have no required fields. Specifying this field causes + /// `DummyMutex`, and have no required fields. Specifying this field causes /// the `thread_safe` field to be ignored. /// /// when null (default): /// * the mutex type defaults to `std.Thread.Mutex` when thread_safe is enabled. - /// * the mutex type defaults to `std.Thread.Mutex.Dummy` otherwise. + /// * the mutex type defaults to `DummyMutex` otherwise. MutexType: ?type = null, /// This is a temporary debugging trick you can use to turn segfaults into more helpful @@ -198,7 +198,12 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { else if (config.thread_safe) std.Thread.Mutex{} else - std.Thread.Mutex.Dummy{}; + DummyMutex{}; + + const DummyMutex = struct { + fn lock(_: *DummyMutex) void {} + fn unlock(_: *DummyMutex) void {} + }; const stack_n = config.stack_trace_frames; const one_trace_size = @sizeOf(usize) * stack_n; diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index b1a2262083..800a16f660 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -3680,10 +3680,15 @@ pub const OBJECT_NAME_INFORMATION = extern struct { Name: UNICODE_STRING, }; -pub const SRWLOCK = usize; -pub const SRWLOCK_INIT: SRWLOCK = 0; -pub const CONDITION_VARIABLE = usize; -pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = 0; +pub const SRWLOCK_INIT = SRWLOCK{}; +pub const SRWLOCK = extern struct { + Ptr: ?PVOID = null, +}; + +pub const CONDITION_VARIABLE_INIT = CONDITION_VARIABLE{}; +pub const CONDITION_VARIABLE = extern struct { + Ptr: ?PVOID = null, +}; pub const FILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 0x1; pub const FILE_SKIP_SET_EVENT_ON_HANDLE = 0x2; From 76311aebff0f76dba96f589438957a32c364a011 Mon Sep 17 00:00:00 2001 From: jagt Date: Sat, 23 Apr 2022 10:12:06 +0800 Subject: [PATCH 1232/2031] std: fix crypto and hash benchmark --- lib/std/crypto/benchmark.zig | 26 +++++++++++++------------- lib/std/hash/benchmark.zig | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig index 50a031af84..ddc5bc1b1d 100644 --- a/lib/std/crypto/benchmark.zig +++ b/lib/std/crypto/benchmark.zig @@ -297,32 +297,32 @@ pub fn benchmarkAes8(comptime Aes: anytype, comptime count: comptime_int) !u64 { } const CryptoPwhash = struct { - hashFn: @compileError("anytype fields are removed from the language"), - params: @compileError("anytype fields are removed from the language"), + ty: type, + params: *const anyopaque, name: []const u8, }; const bcrypt_params = crypto.pwhash.bcrypt.Params{ .rounds_log = 12 }; const pwhashes = [_]CryptoPwhash{ - .{ .hashFn = crypto.pwhash.bcrypt.strHash, .params = bcrypt_params, .name = "bcrypt" }, + .{ .ty = crypto.pwhash.bcrypt, .params = &bcrypt_params, .name = "bcrypt" }, .{ - .hashFn = crypto.pwhash.scrypt.strHash, - .params = crypto.pwhash.scrypt.Params.interactive, + .ty = crypto.pwhash.scrypt, + .params = &crypto.pwhash.scrypt.Params.interactive, .name = "scrypt", }, .{ - .hashFn = crypto.pwhash.argon2.strHash, - .params = crypto.pwhash.argon2.Params.interactive_2id, + .ty = crypto.pwhash.argon2, + .params = &crypto.pwhash.argon2.Params.interactive_2id, .name = "argon2", }, }; fn benchmarkPwhash( - comptime hashFn: anytype, - comptime params: anytype, + comptime ty: anytype, + comptime params: *const anyopaque, comptime count: comptime_int, ) !f64 { const password = "testpass" ** 2; - const opts = .{ .allocator = std.testing.allocator, .params = params, .encoding = .phc }; + const opts = .{ .allocator = std.testing.allocator, .params = @ptrCast(*const ty.Params, params).*, .encoding = .phc }; var buf: [256]u8 = undefined; var timer = try Timer.start(); @@ -330,7 +330,7 @@ fn benchmarkPwhash( { var i: usize = 0; while (i < count) : (i += 1) { - _ = try hashFn(password, opts, &buf); + _ = try ty.strHash(password, opts, &buf); mem.doNotOptimizeAway(&buf); } } @@ -463,8 +463,8 @@ pub fn main() !void { inline for (pwhashes) |H| { if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) { - const throughput = try benchmarkPwhash(H.hashFn, H.params, mode(64)); - try stdout.print("{s:>17}: {d:.3} s/ops\n", .{ H.name, throughput }); + const throughput = try benchmarkPwhash(H.ty, H.params, mode(64)); + try stdout.print("{s:>17}: {d:10.3} s/ops\n", .{ H.name, throughput }); } } } diff --git a/lib/std/hash/benchmark.zig b/lib/std/hash/benchmark.zig index f6f1da1894..a7c0410b53 100644 --- a/lib/std/hash/benchmark.zig +++ b/lib/std/hash/benchmark.zig @@ -230,7 +230,7 @@ pub fn main() !void { inline for (hashes) |H| { if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) { if (!test_iterative_only or H.has_iterative_api) { - try stdout.print("{}\n", .{H.name}); + try stdout.print("{s}\n", .{H.name}); // Always reseed prior to every call so we are hashing the same buffer contents. // This allows easier comparison between different implementations. From ab658e32bdfa234b6c33f0718cac7a23fbd4074f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 24 Apr 2022 20:07:39 -0700 Subject: [PATCH 1233/2031] Sema: fix export with Internal linkage The Export tables should only be populated with non-internal exports. --- src/Sema.zig | 4 ++++ test/behavior/export.zig | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index 14511fe82d..72256d6ae5 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4321,6 +4321,10 @@ pub fn analyzeExport( const Export = Module.Export; const mod = sema.mod; + if (borrowed_options.linkage == .Internal) { + return; + } + try mod.ensureDeclAnalyzed(exported_decl_index); const exported_decl = mod.declPtr(exported_decl_index); // TODO run the same checks as we do for C ABI struct fields diff --git a/test/behavior/export.zig b/test/behavior/export.zig index 5f8ccf02d4..3943fd2834 100644 --- a/test/behavior/export.zig +++ b/test/behavior/export.zig @@ -45,3 +45,13 @@ test "exporting enum type and value" { }; try expect(S.e == .two); } + +test "exporting with internal linkage" { + const S = struct { + fn foo() callconv(.C) void {} + comptime { + @export(foo, .{ .name = "exporting_with_internal_linkage_foo", .linkage = .Internal }); + } + }; + S.foo(); +} From 6c0114e04436404b1c59b66125733350b4be2f5b Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Mon, 25 Apr 2022 13:25:15 -0700 Subject: [PATCH 1234/2031] compiler_rt: Implement fmodx for f80 --- lib/std/special/compiler_rt.zig | 14 +-- lib/std/special/compiler_rt/divdf3.zig | 7 +- .../compiler_rt/{floatfmodq.zig => fmodq.zig} | 10 +- .../{floatfmodq_test.zig => fmodq_test.zig} | 16 ++- lib/std/special/compiler_rt/fmodx.zig | 108 ++++++++++++++++++ lib/std/special/compiler_rt/fmodx_test.zig | 51 +++++++++ 6 files changed, 185 insertions(+), 21 deletions(-) rename lib/std/special/compiler_rt/{floatfmodq.zig => fmodq.zig} (93%) rename lib/std/special/compiler_rt/{floatfmodq_test.zig => fmodq_test.zig} (67%) create mode 100644 lib/std/special/compiler_rt/fmodx.zig create mode 100644 lib/std/special/compiler_rt/fmodx_test.zig diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 375de6fced..0f5d6c4d23 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -726,6 +726,11 @@ comptime { if (!is_test) { @export(fmodl, .{ .name = "fmodl", .linkage = linkage }); + if (long_double_is_f80) { + @export(fmodl, .{ .name = "fmodx", .linkage = linkage }); + } else { + @export(fmodx, .{ .name = "fmodx", .linkage = linkage }); + } if (long_double_is_f128) { @export(fmodl, .{ .name = "fmodq", .linkage = linkage }); } else { @@ -884,13 +889,8 @@ fn ceill(x: c_longdouble) callconv(.C) c_longdouble { return math.ceil(x); } -const fmodq = @import("compiler_rt/floatfmodq.zig").fmodq; -fn fmodl(x: c_longdouble, y: c_longdouble) callconv(.C) c_longdouble { - if (!long_double_is_f128) { - @panic("TODO implement this"); - } - return @floatCast(c_longdouble, fmodq(x, y)); -} +const fmodq = @import("compiler_rt/fmodq.zig").fmodq; +const fmodx = @import("compiler_rt/fmodx.zig").fmodx; // Avoid dragging in the runtime safety mechanisms into this .o file, // unless we're trying to test this file. diff --git a/lib/std/special/compiler_rt/divdf3.zig b/lib/std/special/compiler_rt/divdf3.zig index 2148902de2..137f5c02f9 100644 --- a/lib/std/special/compiler_rt/divdf3.zig +++ b/lib/std/special/compiler_rt/divdf3.zig @@ -314,12 +314,11 @@ pub fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { pub fn normalize(comptime T: type, significand: *std.meta.Int(.unsigned, @typeInfo(T).Float.bits)) i32 { @setRuntimeSafety(builtin.is_test); const Z = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); - const significandBits = std.math.floatMantissaBits(T); - const implicitBit = @as(Z, 1) << significandBits; + const integerBit = @as(Z, 1) << std.math.floatFractionalBits(T); - const shift = @clz(Z, significand.*) - @clz(Z, implicitBit); + const shift = @clz(Z, significand.*) - @clz(Z, integerBit); significand.* <<= @intCast(std.math.Log2Int(Z), shift); - return 1 - shift; + return @as(i32, 1) - shift; } pub fn __aeabi_ddiv(a: f64, b: f64) callconv(.AAPCS) f64 { diff --git a/lib/std/special/compiler_rt/floatfmodq.zig b/lib/std/special/compiler_rt/fmodq.zig similarity index 93% rename from lib/std/special/compiler_rt/floatfmodq.zig rename to lib/std/special/compiler_rt/fmodq.zig index b8da727c90..3f56c49796 100644 --- a/lib/std/special/compiler_rt/floatfmodq.zig +++ b/lib/std/special/compiler_rt/fmodq.zig @@ -27,7 +27,7 @@ pub fn fmodq(a: f128, b: f128) callconv(.C) f128 { const signA = aPtr_u16[exp_and_sign_index] & 0x8000; var expA = @intCast(i32, (aPtr_u16[exp_and_sign_index] & 0x7fff)); - var expB = bPtr_u16[exp_and_sign_index] & 0x7fff; + var expB = @intCast(i32, (bPtr_u16[exp_and_sign_index] & 0x7fff)); // There are 3 cases where the answer is undefined, check for: // - fmodq(val, 0) @@ -55,12 +55,12 @@ pub fn fmodq(a: f128, b: f128) callconv(.C) f128 { if (expA == 0) { amod *= 0x1p120; - expA = aPtr_u16[exp_and_sign_index] -% 120; + expA = @as(i32, aPtr_u16[exp_and_sign_index]) - 120; } if (expB == 0) { bmod *= 0x1p120; - expB = bPtr_u16[exp_and_sign_index] -% 120; + expB = @as(i32, bPtr_u16[exp_and_sign_index]) - 120; } // OR in extra non-stored mantissa digit @@ -73,7 +73,7 @@ pub fn fmodq(a: f128, b: f128) callconv(.C) f128 { var high = highA -% highB; var low = lowA -% lowB; if (lowA < lowB) { - high = highA -% 1; + high -%= 1; } if (high >> 63 == 0) { if ((high | low) == 0) { @@ -122,5 +122,5 @@ pub fn fmodq(a: f128, b: f128) callconv(.C) f128 { } test { - _ = @import("floatfmodq_test.zig"); + _ = @import("fmodq_test.zig"); } diff --git a/lib/std/special/compiler_rt/floatfmodq_test.zig b/lib/std/special/compiler_rt/fmodq_test.zig similarity index 67% rename from lib/std/special/compiler_rt/floatfmodq_test.zig rename to lib/std/special/compiler_rt/fmodq_test.zig index a272b797e3..b8baf8ae9b 100644 --- a/lib/std/special/compiler_rt/floatfmodq_test.zig +++ b/lib/std/special/compiler_rt/fmodq_test.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const fmodq = @import("floatfmodq.zig"); +const fmodq = @import("fmodq.zig"); const testing = std.testing; fn test_fmodq(a: f128, b: f128, exp: f128) !void { @@ -34,12 +34,18 @@ test "fmodq" { try test_fmodq(-0.0, 1.0, -0.0); try test_fmodq(7046119.0, 5558362.0, 1487757.0); try test_fmodq(9010357.0, 1957236.0, 1181413.0); + try test_fmodq(5192296858534827628530496329220095, 10.0, 5.0); + try test_fmodq(5192296858534827628530496329220095, 922337203681230954775807, 220474884073715748246157); // Denormals - const a: f128 = 0xedcb34a235253948765432134674p-16494; - const b: f128 = 0x5d2e38791cfbc0737402da5a9518p-16494; - const exp: f128 = 0x336ec3affb2db8618e4e7d5e1c44p-16494; - try test_fmodq(a, b, exp); + const a1: f128 = 0xedcb34a235253948765432134674p-16494; + const b1: f128 = 0x5d2e38791cfbc0737402da5a9518p-16494; + const exp1: f128 = 0x336ec3affb2db8618e4e7d5e1c44p-16494; + try test_fmodq(a1, b1, exp1); + const a2: f128 = 0x0.7654_3210_fdec_ba98_7654_3210_fdecp-16382; + const b2: f128 = 0x0.0012_fdac_bdef_1234_fdec_3222_1111p-16382; + const exp2: f128 = 0x0.0001_aecd_9d66_4a6e_67b7_d7d0_a901p-16382; + try test_fmodq(a2, b2, exp2); try test_fmodq_nans(); try test_fmodq_infs(); diff --git a/lib/std/special/compiler_rt/fmodx.zig b/lib/std/special/compiler_rt/fmodx.zig new file mode 100644 index 0000000000..efe16f9f16 --- /dev/null +++ b/lib/std/special/compiler_rt/fmodx.zig @@ -0,0 +1,108 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const math = std.math; +const normalize = @import("divdf3.zig").normalize; + +// fmodx - floating modulo large, returns the remainder of division for f80 types +// Logic and flow heavily inspired by MUSL fmodl for 113 mantissa digits +pub fn fmodx(a: f80, b: f80) callconv(.C) f80 { + @setRuntimeSafety(builtin.is_test); + + const T = f80; + const Z = std.meta.Int(.unsigned, @bitSizeOf(T)); + + const significandBits = math.floatMantissaBits(T); + const fractionalBits = math.floatFractionalBits(T); + const exponentBits = math.floatExponentBits(T); + + const signBit = (@as(Z, 1) << (significandBits + exponentBits)); + const maxExponent = ((1 << exponentBits) - 1); + + var aRep = @bitCast(Z, a); + var bRep = @bitCast(Z, b); + + const signA = aRep & signBit; + var expA = @intCast(i32, (@bitCast(Z, a) >> significandBits) & maxExponent); + var expB = @intCast(i32, (@bitCast(Z, b) >> significandBits) & maxExponent); + + // There are 3 cases where the answer is undefined, check for: + // - fmodx(val, 0) + // - fmodx(val, NaN) + // - fmodx(inf, val) + // The sign on checked values does not matter. + // Doing (a * b) / (a * b) procudes undefined results + // because the three cases always produce undefined calculations: + // - 0 / 0 + // - val * NaN + // - inf / inf + if (b == 0 or math.isNan(b) or expA == maxExponent) { + return (a * b) / (a * b); + } + + // Remove the sign from both + aRep &= ~signBit; + bRep &= ~signBit; + if (aRep <= bRep) { + if (aRep == bRep) { + return 0 * a; + } + return a; + } + + if (expA == 0) expA = normalize(f80, &aRep); + if (expB == 0) expB = normalize(f80, &bRep); + + var highA: u64 = 0; + var highB: u64 = 0; + var lowA: u64 = @truncate(u64, aRep); + var lowB: u64 = @truncate(u64, bRep); + + while (expA > expB) : (expA -= 1) { + var high = highA -% highB; + var low = lowA -% lowB; + if (lowA < lowB) { + high -%= 1; + } + if (high >> 63 == 0) { + if ((high | low) == 0) { + return 0 * a; + } + highA = 2 *% high + (low >> 63); + lowA = 2 *% low; + } else { + highA = 2 *% highA + (lowA >> 63); + lowA = 2 *% lowA; + } + } + + var high = highA -% highB; + var low = lowA -% lowB; + if (lowA < lowB) { + high -%= 1; + } + if (high >> 63 == 0) { + if ((high | low) == 0) { + return 0 * a; + } + highA = high; + lowA = low; + } + + while ((lowA >> fractionalBits) == 0) { + lowA = 2 *% lowA; + expA = expA - 1; + } + + // Combine the exponent with the sign and significand, normalize if happened to be denormalized + if (expA < -fractionalBits) { + return @bitCast(T, signA); + } else if (expA <= 0) { + return @bitCast(T, (lowA >> @intCast(math.Log2Int(u64), 1 - expA)) | signA); + } else { + return @bitCast(T, lowA | (@as(Z, @intCast(u16, expA)) << significandBits) | signA); + } +} + +test { + _ = @import("fmodx_test.zig"); +} diff --git a/lib/std/special/compiler_rt/fmodx_test.zig b/lib/std/special/compiler_rt/fmodx_test.zig new file mode 100644 index 0000000000..a5d0887ea4 --- /dev/null +++ b/lib/std/special/compiler_rt/fmodx_test.zig @@ -0,0 +1,51 @@ +const std = @import("std"); +const fmodx = @import("fmodx.zig"); +const testing = std.testing; + +fn test_fmodx(a: f80, b: f80, exp: f80) !void { + const res = fmodx.fmodx(a, b); + try testing.expect(exp == res); +} + +fn test_fmodx_nans() !void { + try testing.expect(std.math.isNan(fmodx.fmodx(1.0, std.math.nan(f80)))); + try testing.expect(std.math.isNan(fmodx.fmodx(1.0, -std.math.nan(f80)))); + try testing.expect(std.math.isNan(fmodx.fmodx(std.math.nan(f80), 1.0))); + try testing.expect(std.math.isNan(fmodx.fmodx(-std.math.nan(f80), 1.0))); +} + +fn test_fmodx_infs() !void { + try testing.expect(fmodx.fmodx(1.0, std.math.inf(f80)) == 1.0); + try testing.expect(fmodx.fmodx(1.0, -std.math.inf(f80)) == 1.0); + try testing.expect(std.math.isNan(fmodx.fmodx(std.math.inf(f80), 1.0))); + try testing.expect(std.math.isNan(fmodx.fmodx(-std.math.inf(f80), 1.0))); +} + +test "fmodx" { + try test_fmodx(6.4, 4.0, 2.4); + try test_fmodx(6.4, -4.0, 2.4); + try test_fmodx(-6.4, 4.0, -2.4); + try test_fmodx(-6.4, -4.0, -2.4); + try test_fmodx(3.0, 2.0, 1.0); + try test_fmodx(-5.0, 3.0, -2.0); + try test_fmodx(3.0, 2.0, 1.0); + try test_fmodx(1.0, 2.0, 1.0); + try test_fmodx(0.0, 1.0, 0.0); + try test_fmodx(-0.0, 1.0, -0.0); + try test_fmodx(7046119.0, 5558362.0, 1487757.0); + try test_fmodx(9010357.0, 1957236.0, 1181413.0); + try test_fmodx(9223372036854775807, 10.0, 7.0); + + // Denormals + const a1: f80 = 0x0.76e5_9a51_1a92_9ca4p-16381; + const b1: f80 = 0x0.2e97_1c3c_8e7d_e03ap-16381; + const exp1: f80 = 0x0.19b7_61d7_fd96_dc30p-16381; + try test_fmodx(a1, b1, exp1); + const a2: f80 = 0x0.76e5_9a51_1a92_9ca4p-16381; + const b2: f80 = 0x0.0e97_1c3c_8e7d_e03ap-16381; + const exp2: f80 = 0x0.022c_b86c_a6a3_9ad4p-16381; + try test_fmodx(a2, b2, exp2); + + try test_fmodx_nans(); + try test_fmodx_infs(); +} From d930e015c75d60e22d0a116a1f4fa0df4d68b6b6 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Mon, 25 Apr 2022 16:31:43 -0700 Subject: [PATCH 1235/2031] compiler_rt: Implement __divxf3 for f80 --- lib/std/special/compiler_rt.zig | 16 +- lib/std/special/compiler_rt/divxf3.zig | 202 ++++++++++++++++++++ lib/std/special/compiler_rt/divxf3_test.zig | 65 +++++++ 3 files changed, 274 insertions(+), 9 deletions(-) create mode 100644 lib/std/special/compiler_rt/divxf3.zig create mode 100644 lib/std/special/compiler_rt/divxf3_test.zig diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 0f5d6c4d23..6a44d859c9 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -253,6 +253,8 @@ comptime { @export(__divsf3, .{ .name = "__divsf3", .linkage = linkage }); const __divdf3 = @import("compiler_rt/divdf3.zig").__divdf3; @export(__divdf3, .{ .name = "__divdf3", .linkage = linkage }); + const __divxf3 = @import("compiler_rt/divxf3.zig").__divxf3; + @export(__divxf3, .{ .name = "__divxf3", .linkage = linkage }); const __divtf3 = @import("compiler_rt/divtf3.zig").__divtf3; @export(__divtf3, .{ .name = "__divtf3", .linkage = linkage }); @@ -725,17 +727,13 @@ comptime { } if (!is_test) { - @export(fmodl, .{ .name = "fmodl", .linkage = linkage }); if (long_double_is_f80) { - @export(fmodl, .{ .name = "fmodx", .linkage = linkage }); - } else { - @export(fmodx, .{ .name = "fmodx", .linkage = linkage }); - } - if (long_double_is_f128) { - @export(fmodl, .{ .name = "fmodq", .linkage = linkage }); - } else { - @export(fmodq, .{ .name = "fmodq", .linkage = linkage }); + @export(fmodx, .{ .name = "fmodl", .linkage = linkage }); + } else if (long_double_is_f128) { + @export(fmodq, .{ .name = "fmodl", .linkage = linkage }); } + @export(fmodx, .{ .name = "fmodx", .linkage = linkage }); + @export(fmodq, .{ .name = "fmodq", .linkage = linkage }); @export(floorf, .{ .name = "floorf", .linkage = linkage }); @export(floor, .{ .name = "floor", .linkage = linkage }); diff --git a/lib/std/special/compiler_rt/divxf3.zig b/lib/std/special/compiler_rt/divxf3.zig new file mode 100644 index 0000000000..5f5ed667ec --- /dev/null +++ b/lib/std/special/compiler_rt/divxf3.zig @@ -0,0 +1,202 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const normalize = @import("divdf3.zig").normalize; +const wideMultiply = @import("divdf3.zig").wideMultiply; + +pub fn __divxf3(a: f80, b: f80) callconv(.C) f80 { + @setRuntimeSafety(builtin.is_test); + const T = f80; + const Z = std.meta.Int(.unsigned, @bitSizeOf(T)); + + const significandBits = std.math.floatMantissaBits(T); + const fractionalBits = std.math.floatFractionalBits(T); + const exponentBits = std.math.floatExponentBits(T); + + const signBit = (@as(Z, 1) << (significandBits + exponentBits)); + const maxExponent = ((1 << exponentBits) - 1); + const exponentBias = (maxExponent >> 1); + + const integerBit = (@as(Z, 1) << fractionalBits); + const quietBit = integerBit >> 1; + const significandMask = (@as(Z, 1) << significandBits) - 1; + + const absMask = signBit - 1; + const qnanRep = @bitCast(Z, std.math.nan(T)) | quietBit; + const infRep = @bitCast(Z, std.math.inf(T)); + + const aExponent = @truncate(u32, (@bitCast(Z, a) >> significandBits) & maxExponent); + const bExponent = @truncate(u32, (@bitCast(Z, b) >> significandBits) & maxExponent); + const quotientSign: Z = (@bitCast(Z, a) ^ @bitCast(Z, b)) & signBit; + + var aSignificand: Z = @bitCast(Z, a) & significandMask; + var bSignificand: Z = @bitCast(Z, b) & significandMask; + var scale: i32 = 0; + + // Detect if a or b is zero, denormal, infinity, or NaN. + if (aExponent -% 1 >= maxExponent - 1 or bExponent -% 1 >= maxExponent - 1) { + const aAbs: Z = @bitCast(Z, a) & absMask; + const bAbs: Z = @bitCast(Z, b) & absMask; + + // NaN / anything = qNaN + if (aAbs > infRep) return @bitCast(T, @bitCast(Z, a) | quietBit); + // anything / NaN = qNaN + if (bAbs > infRep) return @bitCast(T, @bitCast(Z, b) | quietBit); + + if (aAbs == infRep) { + // infinity / infinity = NaN + if (bAbs == infRep) { + return @bitCast(T, qnanRep); + } + // infinity / anything else = +/- infinity + else { + return @bitCast(T, aAbs | quotientSign); + } + } + + // anything else / infinity = +/- 0 + if (bAbs == infRep) return @bitCast(T, quotientSign); + + if (aAbs == 0) { + // zero / zero = NaN + if (bAbs == 0) { + return @bitCast(T, qnanRep); + } + // zero / anything else = +/- zero + else { + return @bitCast(T, quotientSign); + } + } + // anything else / zero = +/- infinity + if (bAbs == 0) return @bitCast(T, infRep | quotientSign); + + // one or both of a or b is denormal, the other (if applicable) is a + // normal number. Renormalize one or both of a and b, and set scale to + // include the necessary exponent adjustment. + if (aAbs < integerBit) scale +%= normalize(T, &aSignificand); + if (bAbs < integerBit) scale -%= normalize(T, &bSignificand); + } + var quotientExponent: i32 = @bitCast(i32, aExponent -% bExponent) +% scale; + + // Align the significand of b as a Q63 fixed-point number in the range + // [1, 2.0) and get a Q64 approximate reciprocal using a small minimax + // polynomial approximation: reciprocal = 3/4 + 1/sqrt(2) - b/2. This + // is accurate to about 3.5 binary digits. + const q63b = @intCast(u64, bSignificand); + var recip64 = @as(u64, 0x7504f333F9DE6484) -% q63b; + // 0x7504f333F9DE6484 / 2^64 + 1 = 3/4 + 1/sqrt(2) + + // Now refine the reciprocal estimate using a Newton-Raphson iteration: + // + // x1 = x0 * (2 - x0 * b) + // + // This doubles the number of correct binary digits in the approximation + // with each iteration. + var correction64: u64 = undefined; + correction64 = @truncate(u64, ~(@as(u128, recip64) *% q63b >> 64) +% 1); + recip64 = @truncate(u64, @as(u128, recip64) *% correction64 >> 63); + correction64 = @truncate(u64, ~(@as(u128, recip64) *% q63b >> 64) +% 1); + recip64 = @truncate(u64, @as(u128, recip64) *% correction64 >> 63); + correction64 = @truncate(u64, ~(@as(u128, recip64) *% q63b >> 64) +% 1); + recip64 = @truncate(u64, @as(u128, recip64) *% correction64 >> 63); + correction64 = @truncate(u64, ~(@as(u128, recip64) *% q63b >> 64) +% 1); + recip64 = @truncate(u64, @as(u128, recip64) *% correction64 >> 63); + correction64 = @truncate(u64, ~(@as(u128, recip64) *% q63b >> 64) +% 1); + recip64 = @truncate(u64, @as(u128, recip64) *% correction64 >> 63); + + // The reciprocal may have overflowed to zero if the upper half of b is + // exactly 1.0. This would sabatoge the full-width final stage of the + // computation that follows, so we adjust the reciprocal down by one bit. + recip64 -%= 1; + + // We need to perform one more iteration to get us to 112 binary digits; + // The last iteration needs to happen with extra precision. + + // NOTE: This operation is equivalent to __multi3, which is not implemented + // in some architechures + var reciprocal: u128 = undefined; + var correction: u128 = undefined; + var dummy: u128 = undefined; + wideMultiply(u128, recip64, q63b, &dummy, &correction); + + correction = -%correction; + + const cHi = @truncate(u64, correction >> 64); + const cLo = @truncate(u64, correction); + + var r64cH: u128 = undefined; + var r64cL: u128 = undefined; + wideMultiply(u128, recip64, cHi, &dummy, &r64cH); + wideMultiply(u128, recip64, cLo, &dummy, &r64cL); + + reciprocal = r64cH + (r64cL >> 64); + + // Adjust the final 128-bit reciprocal estimate downward to ensure that it + // is strictly smaller than the infinitely precise exact reciprocal. Because + // the computation of the Newton-Raphson step is truncating at every step, + // this adjustment is small; most of the work is already done. + reciprocal -%= 2; + + // The numerical reciprocal is accurate to within 2^-112, lies in the + // interval [0.5, 1.0), and is strictly smaller than the true reciprocal + // of b. Multiplying a by this reciprocal thus gives a numerical q = a/b + // in Q127 with the following properties: + // + // 1. q < a/b + // 2. q is in the interval [0.5, 2.0) + // 3. The error in q is bounded away from 2^-63 (actually, we have + // many bits to spare, but this is all we need). + + // We need a 128 x 128 multiply high to compute q. + var quotient128: u128 = undefined; + var quotientLo: u128 = undefined; + wideMultiply(u128, aSignificand << 2, reciprocal, "ient128, "ientLo); + + // Two cases: quotient is in [0.5, 1.0) or quotient is in [1.0, 2.0). + // Right shift the quotient if it falls in the [1,2) range and adjust the + // exponent accordingly. + var quotient: u64 = if (quotient128 < (integerBit << 1)) b: { + quotientExponent -= 1; + break :b @intCast(u64, quotient128); + } else @intCast(u64, quotient128 >> 1); + + // We are going to compute a residual of the form + // + // r = a - q*b + // + // We know from the construction of q that r satisfies: + // + // 0 <= r < ulp(q)*b + // + // If r is greater than 1/2 ulp(q)*b, then q rounds up. Otherwise, we + // already have the correct result. The exact halfway case cannot occur. + var residual: u64 = -%(quotient *% q63b); + + const writtenExponent = quotientExponent + exponentBias; + if (writtenExponent >= maxExponent) { + // If we have overflowed the exponent, return infinity. + return @bitCast(T, infRep | quotientSign); + } else if (writtenExponent < 1) { + if (writtenExponent == 0) { + // Check whether the rounded result is normal. + if (residual > (bSignificand >> 1)) { // round + if (quotient == (integerBit - 1)) // If the rounded result is normal, return it + return @bitCast(T, @bitCast(Z, std.math.floatMin(T)) | quotientSign); + } + } + // Flush denormals to zero. In the future, it would be nice to add + // code to round them correctly. + return @bitCast(T, quotientSign); + } else { + const round = @boolToInt(residual > (bSignificand >> 1)); + // Insert the exponent + var absResult = quotient | (@intCast(Z, writtenExponent) << significandBits); + // Round + absResult +%= round; + // Insert the sign and return + return @bitCast(T, absResult | quotientSign | integerBit); + } +} + +test { + _ = @import("divxf3_test.zig"); +} diff --git a/lib/std/special/compiler_rt/divxf3_test.zig b/lib/std/special/compiler_rt/divxf3_test.zig new file mode 100644 index 0000000000..b79b90c6fb --- /dev/null +++ b/lib/std/special/compiler_rt/divxf3_test.zig @@ -0,0 +1,65 @@ +const std = @import("std"); +const math = std.math; +const testing = std.testing; + +const __divxf3 = @import("divxf3.zig").__divxf3; + +fn compareResult(result: f80, expected: u80) bool { + const rep = @bitCast(u80, result); + + if (rep == expected) return true; + // test other possible NaN representations (signal NaN) + if (math.isNan(result) and math.isNan(@bitCast(f80, expected))) return true; + + return false; +} + +fn expect__divxf3_result(a: f80, b: f80, expected: u80) !void { + const x = __divxf3(a, b); + const ret = compareResult(x, expected); + try testing.expect(ret == true); +} + +fn test__divxf3(a: f80, b: f80) !void { + const integerBit = 1 << math.floatFractionalBits(f80); + const x = __divxf3(a, b); + + // Next float (assuming normal, non-zero result) + const x_plus_eps = @bitCast(f80, (@bitCast(u80, x) + 1) | integerBit); + // Prev float (assuming normal, non-zero result) + const x_minus_eps = @bitCast(f80, (@bitCast(u80, x) - 1) | integerBit); + + // Make sure result is more accurate than the adjacent floats + const err_x = std.math.fabs(@mulAdd(f80, x, b, -a)); + const err_x_plus_eps = std.math.fabs(@mulAdd(f80, x_plus_eps, b, -a)); + const err_x_minus_eps = std.math.fabs(@mulAdd(f80, x_minus_eps, b, -a)); + + try testing.expect(err_x_minus_eps > err_x); + try testing.expect(err_x_plus_eps > err_x); +} + +test "divxf3" { + // qNaN / any = qNaN + try expect__divxf3_result(math.qnan_f80, 0x1.23456789abcdefp+5, 0x7fffC000000000000000); + // NaN / any = NaN + try expect__divxf3_result(math.nan_f80, 0x1.23456789abcdefp+5, 0x7fffC000000000000000); + // inf / any(except inf and nan) = inf + try expect__divxf3_result(math.inf(f80), 0x1.23456789abcdefp+5, 0x7fff8000000000000000); + // inf / inf = nan + try expect__divxf3_result(math.inf(f80), math.inf(f80), 0x7fffC000000000000000); + // inf / nan = nan + try expect__divxf3_result(math.inf(f80), math.nan(f80), 0x7fffC000000000000000); + + try test__divxf3(0x1.a23b45362464523375893ab4cdefp+5, 0x1.eedcbaba3a94546558237654321fp-1); + try test__divxf3(0x1.a2b34c56d745382f9abf2c3dfeffp-50, 0x1.ed2c3ba15935332532287654321fp-9); + try test__divxf3(0x1.2345f6aaaa786555f42432abcdefp+456, 0x1.edacbba9874f765463544dd3621fp+6400); + try test__divxf3(0x1.2d3456f789ba6322bc665544edefp-234, 0x1.eddcdba39f3c8b7a36564354321fp-4455); + try test__divxf3(0x1.2345f6b77b7a8953365433abcdefp+234, 0x1.edcba987d6bb3aa467754354321fp-4055); + try test__divxf3(0x1.a23b45362464523375893ab4cdefp+5, 0x1.a2b34c56d745382f9abf2c3dfeffp-50); + try test__divxf3(0x1.a23b45362464523375893ab4cdefp+5, 0x1.1234567890abcdef987654321123p0); + try test__divxf3(0x1.a23b45362464523375893ab4cdefp+5, 0x1.12394205810257120adae8929f23p+16); + try test__divxf3(0x1.a23b45362464523375893ab4cdefp+5, 0x1.febdcefa1231245f9abf2c3dfeffp-50); + + // Result rounds down to zero + try expect__divxf3_result(6.72420628622418701252535563464350521E-4932, 2.0, 0x0); +} From f5540778b56636cffd89f794c6b1a9266d7d36f8 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Mon, 25 Apr 2022 16:32:42 -0700 Subject: [PATCH 1236/2031] stdlib: Fix hex-float printing for f80 --- lib/std/fmt.zig | 7 ++++--- lib/std/math/signbit.zig | 6 ++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 80a2e87c5c..994350ffa7 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -1124,6 +1124,7 @@ pub fn formatFloatHexadecimal( const TU = std.meta.Int(.unsigned, std.meta.bitCount(T)); const mantissa_bits = math.floatMantissaBits(T); + const fractional_bits = math.floatFractionalBits(T); const exponent_bits = math.floatExponentBits(T); const mantissa_mask = (1 << mantissa_bits) - 1; const exponent_mask = (1 << exponent_bits) - 1; @@ -1155,14 +1156,14 @@ pub fn formatFloatHexadecimal( // Adjust the exponent for printing. exponent += 1; } else { - // Add the implicit 1. - mantissa |= 1 << mantissa_bits; + if (fractional_bits == mantissa_bits) + mantissa |= 1 << fractional_bits; // Add the implicit integer bit. } // Fill in zeroes to round the mantissa width to a multiple of 4. if (T == f16) mantissa <<= 2 else if (T == f32) mantissa <<= 1; - const mantissa_digits = (mantissa_bits + 3) / 4; + const mantissa_digits = (fractional_bits + 3) / 4; if (options.precision) |precision| { // Round if needed. diff --git a/lib/std/math/signbit.zig b/lib/std/math/signbit.zig index 056add5ae6..a72d6ab119 100644 --- a/lib/std/math/signbit.zig +++ b/lib/std/math/signbit.zig @@ -9,6 +9,7 @@ pub fn signbit(x: anytype) bool { f16 => signbit16(x), f32 => signbit32(x), f64 => signbit64(x), + f80 => signbit80(x), f128 => signbit128(x), else => @compileError("signbit not implemented for " ++ @typeName(T)), }; @@ -29,6 +30,11 @@ fn signbit64(x: f64) bool { return bits >> 63 != 0; } +fn signbit80(x: f80) bool { + const bits = @bitCast(u80, x); + return bits >> 79 != 0; +} + fn signbit128(x: f128) bool { const bits = @bitCast(u128, x); return bits >> 127 != 0; From 96d86e346517b1917165b6f817b2147524a9a0b5 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Mon, 25 Apr 2022 16:42:13 -0700 Subject: [PATCH 1237/2031] compiler_rt: Fix rounding edge case for __mulxf3 --- lib/std/special/compiler_rt/README.md | 34 ++++++++++----------- lib/std/special/compiler_rt/addXf3.zig | 12 +++----- lib/std/special/compiler_rt/addXf3_test.zig | 2 ++ lib/std/special/compiler_rt/mulXf3.zig | 10 +++++- lib/std/special/compiler_rt/mulXf3_test.zig | 4 +++ 5 files changed, 37 insertions(+), 25 deletions(-) diff --git a/lib/std/special/compiler_rt/README.md b/lib/std/special/compiler_rt/README.md index 7115a5756f..cf1bf29522 100644 --- a/lib/std/special/compiler_rt/README.md +++ b/lib/std/special/compiler_rt/README.md @@ -152,53 +152,53 @@ Bugs should be solved by trying to duplicate the bug upstream, if possible. - todo todo __fixsfsi // convert a to i32, rounding towards zero - todo todo __fixdfsi // - todo todo __fixtfsi // -- none none __fixxfsi // missing +- todo todo __fixxfsi // - todo todo __fixsfdi // convert a to i64, rounding towards zero - todo todo __fixdfdi // - todo todo __fixtfdi // -- none none __fixxfdi // missing +- todo todo __fixxfdi // - todo todo __fixsfti // convert a to i128, rounding towards zero - todo todo __fixdfti // - todo todo __fixtfdi // -- none none __fixxfti // missing +- todo todo __fixxfti // - __fixunssfsi // convert to u32, rounding towards zero. negative values become 0. - __fixunsdfsi // - __fixunstfsi // -- __fixunsxfsi // missing +- __fixunsxfsi // - __fixunssfdi // convert to u64, rounding towards zero. negative values become 0. - __fixunsdfdi // - __fixunstfdi // -- __fixunsxfdi // missing +- __fixunsxfdi // - __fixunssfti // convert to u128, rounding towards zero. negative values become 0. - __fixunsdfti // - __fixunstfdi // -- __fixunsxfti // missing +- __fixunsxfti // - __floatsisf // convert i32 to floating point - __floatsidf // - __floatsitf // -- __floatsixf // missing +- __floatsixf // - __floatdisf // convert i64 to floating point - __floatdidf // - __floatditf // -- __floatdixf // missing +- __floatdixf // - __floattisf // convert i128 to floating point - __floattidf // -- __floattixf // missing +- __floattixf // -- __floatunsisf // convert i32 to floating point +- __floatunsisf // convert u32 to floating point - __floatunsidf // - __floatunsitf // -- __floatunsixf // missing -- __floatundisf // convert i64 to floating point +- __floatunsixf // +- __floatundisf // convert u64 to floating point - __floatundidf // - __floatunditf // -- __floatundixf // missing -- __floatuntisf // convert i128 to floating point +- __floatundixf // +- __floatuntisf // convert u128 to floating point - __floatuntidf // - __floatuntitf // -- __floatuntixf // missing +- __floatuntixf // #### Float Comparison - __cmpsf2 // return (a-1,(a==b)=>0,(a>b)=>1,Nan=>1 dont rely on this @@ -242,11 +242,11 @@ Bugs should be solved by trying to duplicate the bug upstream, if possible. - __mulsf3 // a * b - __muldf3 // a * b - __multf3 // a * b -- __mulxf3 // a * b missing +- __mulxf3 // a * b - __divsf3 // a / b - __divdf3 // a / b - __divtf3 // a / b -- __divxf3 // a / b missing +- __divxf3 // a / b - __negsf2 // -a symbol-level compatibility: libgcc uses this for the rl78 - __negdf2 // -a unnecessary: can be lowered directly to a xor - __negtf2 // -a diff --git a/lib/std/special/compiler_rt/addXf3.zig b/lib/std/special/compiler_rt/addXf3.zig index c53d60600d..1a9de0fb74 100644 --- a/lib/std/special/compiler_rt/addXf3.zig +++ b/lib/std/special/compiler_rt/addXf3.zig @@ -74,7 +74,7 @@ fn normalize(comptime T: type, significand: *std.meta.Int(.unsigned, @typeInfo(T const shift = @clz(std.meta.Int(.unsigned, bits), significand.*) - @clz(Z, integerBit); significand.* <<= @intCast(S, shift); - return 1 - shift; + return @as(i32, 1) - shift; } // TODO: restore inline keyword, see: https://github.com/ziglang/zig/issues/2154 @@ -210,12 +210,10 @@ fn addXf3(comptime T: type, a: T, b: T) T { if (aExponent >= maxExponent) return @bitCast(T, infRep | resultSign); if (aExponent <= 0) { - // Result is denormal before rounding; the exponent is zero and we - // need to shift the significand. - const shift = @intCast(Z, 1 - aExponent); - const sticky = if (aSignificand << @intCast(S, typeWidth - shift) != 0) @as(Z, 1) else 0; - aSignificand = aSignificand >> @intCast(S, shift | sticky); - aExponent = 0; + // Result is denormal; the exponent and round/sticky bits are zero. + // All we need to do is shift the significand and apply the correct sign. + aSignificand >>= @intCast(S, 4 - aExponent); + return @bitCast(T, resultSign | aSignificand); } // Low three bits are round, guard, and sticky. diff --git a/lib/std/special/compiler_rt/addXf3_test.zig b/lib/std/special/compiler_rt/addXf3_test.zig index a4a18b41e0..f38c9d6018 100644 --- a/lib/std/special/compiler_rt/addXf3_test.zig +++ b/lib/std/special/compiler_rt/addXf3_test.zig @@ -152,4 +152,6 @@ test "addxf3" { try test__addxf3(0x1.0fff_ffff_ffff_fffep+0, 0x1.8p-63, 0x3FFF_8800000000000000); // round down to even try test__addxf3(0x1.0fff_ffff_ffff_fffep+0, 0x1.9p-63, 0x3FFF_8800000000000001); // round up try test__addxf3(0x1.0fff_ffff_ffff_fffep+0, 0x2.0p-63, 0x3FFF_8800000000000001); // exact + try test__addxf3(0x0.ffff_ffff_ffff_fffcp-16382, 0x0.0000_0000_0000_0002p-16382, 0x0000_7FFFFFFFFFFFFFFF); // exact + try test__addxf3(0x0.1fff_ffff_ffff_fffcp-16382, 0x0.0000_0000_0000_0002p-16382, 0x0000_0FFFFFFFFFFFFFFF); // exact } diff --git a/lib/std/special/compiler_rt/mulXf3.zig b/lib/std/special/compiler_rt/mulXf3.zig index befd7b7b43..147efd0ab5 100644 --- a/lib/std/special/compiler_rt/mulXf3.zig +++ b/lib/std/special/compiler_rt/mulXf3.zig @@ -152,6 +152,10 @@ fn mulXf3(comptime T: type, a: T, b: T) T { const sticky = wideShrWithTruncation(ZSignificand, &productHi, &productLo, shift); productLo |= @boolToInt(sticky); result = productHi; + + // We include the integer bit so that rounding will carry to the exponent, + // but it will be removed later if the result is still denormal + if (significandBits != fractionalBits) result |= integerBit; } else { // Result is normal before rounding; insert the exponent. result = productHi & significandMask; @@ -166,7 +170,11 @@ fn mulXf3(comptime T: type, a: T, b: T) T { // Restore any explicit integer bit, if it was rounded off if (significandBits != fractionalBits) { - if ((result >> significandBits) != 0) result |= integerBit; + if ((result >> significandBits) != 0) { + result |= integerBit; + } else { + result &= ~integerBit; + } } // Insert the sign of the result: diff --git a/lib/std/special/compiler_rt/mulXf3_test.zig b/lib/std/special/compiler_rt/mulXf3_test.zig index 2dd345e69e..6b4f8ca953 100644 --- a/lib/std/special/compiler_rt/mulXf3_test.zig +++ b/lib/std/special/compiler_rt/mulXf3_test.zig @@ -105,6 +105,7 @@ test "multf3" { try test__multf3(0x1.0000_0000_0000_0000_0000_0000_0001p+0, 0x1.8p+5, 0x4004_8000_0000_0000, 0x0000_0000_0000_0002); try test__multf3(0x1.0000_0000_0000_0000_0000_0000_0002p+0, 0x1.8p+5, 0x4004_8000_0000_0000, 0x0000_0000_0000_0003); + try test__multf3(2.0, math.floatTrueMin(f128), 0x0000_0000_0000_0000, 0x0000_0000_0000_0002); } const qnan80 = @bitCast(f80, @bitCast(u80, math.nan(f80)) | (1 << (math.floatFractionalBits(f80) - 1))); @@ -164,4 +165,7 @@ test "mulxf3" { try test__mulxf3(0x1.0000_0001p+0, 0x1.0000_0001p+0, 0x3FFF_8000_0001_0000_0000); // round down to even try test__mulxf3(0x1.0000_0001p+0, 0x1.0000_0001_0002p+0, 0x3FFF_8000_0001_0001_0001); // round up + try test__mulxf3(0x0.8000_0000_0000_0000p-16382, 2.0, 0x0001_8000_0000_0000_0000); // denormal -> normal + try test__mulxf3(0x0.7fff_ffff_ffff_fffep-16382, 0x2.0000_0000_0000_0008p0, 0x0001_8000_0000_0000_0000); // denormal -> normal + try test__mulxf3(0x0.7fff_ffff_ffff_fffep-16382, 0x1.0000_0000_0000_0000p0, 0x0000_3FFF_FFFF_FFFF_FFFF); // denormal -> denormal } From 28c05b0a53e451cbe06d321f4e2f92cf3b1a7be4 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Mon, 25 Apr 2022 17:49:56 -0700 Subject: [PATCH 1238/2031] compiler_rt: Bypass `fmodx` impl. on stage2 This function is codegen'd incorrectly in stage2, since it fails to generate the correct soft-float operations. This will be fixed once issue #11161 is implemented --- lib/std/special/compiler_rt.zig | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 6a44d859c9..93e0ffbe1a 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -731,8 +731,13 @@ comptime { @export(fmodx, .{ .name = "fmodl", .linkage = linkage }); } else if (long_double_is_f128) { @export(fmodq, .{ .name = "fmodl", .linkage = linkage }); + } else { + @export(fmodl, .{ .name = "fmodl", .linkage = linkage }); + } + if (long_double_is_f80 or builtin.zig_backend == .stage1) { + // TODO: https://github.com/ziglang/zig/issues/11161 + @export(fmodx, .{ .name = "fmodx", .linkage = linkage }); } - @export(fmodx, .{ .name = "fmodx", .linkage = linkage }); @export(fmodq, .{ .name = "fmodq", .linkage = linkage }); @export(floorf, .{ .name = "floorf", .linkage = linkage }); @@ -889,6 +894,12 @@ fn ceill(x: c_longdouble) callconv(.C) c_longdouble { const fmodq = @import("compiler_rt/fmodq.zig").fmodq; const fmodx = @import("compiler_rt/fmodx.zig").fmodx; +fn fmodl(x: c_longdouble, y: c_longdouble) callconv(.C) c_longdouble { + if (!long_double_is_f128) { + @panic("TODO implement this"); + } + return @floatCast(c_longdouble, fmodq(x, y)); +} // Avoid dragging in the runtime safety mechanisms into this .o file, // unless we're trying to test this file. From cb49af6c9a6b790ec341cb081d1fec381d44e9a4 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 23 Apr 2022 20:45:10 +0200 Subject: [PATCH 1239/2031] wasm: Initial C-ABI implementation This implements the C-ABI convention as specified by: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md While not an official specification, it's the ABI that is output by clang/LLVM. As we use LLVM to compile compiler-rt, and want to integrate with C-libraries, we follow the same convention when the calling convention results in 'C'. --- src/arch/wasm/abi.zig | 102 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 src/arch/wasm/abi.zig diff --git a/src/arch/wasm/abi.zig b/src/arch/wasm/abi.zig new file mode 100644 index 0000000000..99398fd00c --- /dev/null +++ b/src/arch/wasm/abi.zig @@ -0,0 +1,102 @@ +//! Classifies Zig types to follow the C-ABI for Wasm. +//! The convention for Wasm's C-ABI can be found at the tool-conventions repo: +//! https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md +//! When not targeting the C-ABI, Zig is allowed to do derail from this convention. +//! Note: Above mentioned document is not an official specification, therefore called a convention. + +const std = @import("std"); +const Type = @import("../../type.zig").Type; +const Target = std.Target; + +/// Defines how to pass a type as part of a function signature, +/// both for parameters as well as return values. +pub const Class = enum { direct, indirect, none }; + +const none: [2]Class = .{ .none, .none }; +const memory: [2]Class = .{ .indirect, .none }; +const direct: [2]Class = .{ .direct, .none }; + +/// Classifies a given Zig type to determine how they must be passed +/// or returned as value within a wasm function. +/// When all elements result in `.none`, no value must be passed in or returned. +pub fn classifyType(ty: Type, target: Target) [2]Class { + if (!ty.hasRuntimeBitsIgnoreComptime()) return none; + switch (ty.zigTypeTag()) { + .Struct => { + // When the (maybe) scalar type exceeds max 'direct' integer size + if (ty.abiSize(target) > 8) return memory; + // When the struct type is non-scalar + if (ty.structFieldCount() > 1) return memory; + // When the struct's alignment is non-natural + const field = ty.structFields().values()[0]; + if (field.abi_align != 0) { + if (field.abi_align > field.ty.abiAlignment(target)) { + return memory; + } + } + if (field.ty.isInt() or field.ty.isAnyFloat()) { + return direct; + } + return classifyType(field.ty, target); + }, + .Int, .Enum, .ErrorSet, .Vector => { + const int_bits = ty.intInfo(target).bits; + if (int_bits <= 64) return direct; + if (int_bits > 64 and int_bits <= 128) return .{ .direct, .direct }; + return memory; + }, + .Float => { + const float_bits = ty.floatBits(target); + if (float_bits <= 64) return direct; + if (float_bits > 64 and float_bits <= 128) return .{ .direct, .direct }; + return memory; + }, + .Bool => return direct, + .ErrorUnion => { + const has_tag = ty.errorUnionSet().hasRuntimeBitsIgnoreComptime(); + const has_pl = ty.errorUnionPayload().hasRuntimeBitsIgnoreComptime(); + if (!has_pl) return direct; + if (!has_tag) { + return classifyType(ty.errorUnionPayload(), target); + } + return memory; + }, + .Optional => { + if (ty.isPtrLikeOptional()) return direct; + var buf: Type.Payload.ElemType = undefined; + const pl_has_bits = ty.optionalChild(&buf).hasRuntimeBitsIgnoreComptime(); + if (!pl_has_bits) return direct; + return memory; + }, + .Pointer => { + // Slices act like struct and will be passed by reference + if (ty.isSlice()) return memory; + return direct; + }, + .Array => { + if (ty.arrayLen() == 1) return direct; + return memory; + }, + .Union => { + const layout = ty.unionGetLayout(target); + if (layout.payload_size == 0 and layout.tag_size != 0) { + return classifyType(ty.unionTagType().?, target); + } + return classifyType(ty.errorUnionPayload(), target); + }, + .AnyFrame, .Frame => return direct, + + .NoReturn, + .Void, + .Type, + .ComptimeFloat, + .ComptimeInt, + .Undefined, + .Null, + .BoundFn, + .Fn, + .Opaque, + .EnumLiteral, + => unreachable, + } +} From 5f2d0d414dc44af7bda0e8d805d038e8f1a6f9d3 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 24 Apr 2022 21:49:12 +0200 Subject: [PATCH 1240/2031] wasm: Implement codegen for C-ABI This implements passing arguments and storing return values correctly for the C-ABI as specified by the tool-convention: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md There's definitely room for better codegen in follow-up commits. --- src/arch/wasm/CodeGen.zig | 228 +++++++++++++++++++++++++++++++------- src/arch/wasm/abi.zig | 29 ++++- 2 files changed, 209 insertions(+), 48 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 4586f5624b..8eadfe6cd8 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -21,6 +21,7 @@ const Air = @import("../../Air.zig"); const Liveness = @import("../../Liveness.zig"); const Mir = @import("Mir.zig"); const Emit = @import("Emit.zig"); +const abi = @import("abi.zig"); /// Wasm Value, created when generating an instruction const WValue = union(enum) { @@ -722,18 +723,15 @@ fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype { const bits = ty.floatBits(target); if (bits == 16 or bits == 32) break :blk wasm.Valtype.f32; if (bits == 64) break :blk wasm.Valtype.f64; + if (bits == 128) break :blk wasm.Valtype.i64; return wasm.Valtype.i32; // represented as pointer to stack }, - .Int => blk: { + .Int, .Enum => blk: { const info = ty.intInfo(target); if (info.bits <= 32) break :blk wasm.Valtype.i32; - if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64; + if (info.bits > 32 and info.bits <= 128) break :blk wasm.Valtype.i64; break :blk wasm.Valtype.i32; // represented as pointer to stack }, - .Enum => { - var buf: Type.Payload.Bits = undefined; - return typeToValtype(ty.intTagType(&buf), target); - }, else => wasm.Valtype.i32, // all represented as reference/immediate }; } @@ -787,33 +785,46 @@ fn allocLocal(self: *Self, ty: Type) InnerError!WValue { /// Generates a `wasm.Type` from a given function type. /// Memory is owned by the caller. -fn genFunctype(gpa: Allocator, fn_ty: Type, target: std.Target) !wasm.Type { +fn genFunctype(gpa: Allocator, fn_info: Type.Payload.Function.Data, target: std.Target) !wasm.Type { var params = std.ArrayList(wasm.Valtype).init(gpa); defer params.deinit(); var returns = std.ArrayList(wasm.Valtype).init(gpa); defer returns.deinit(); - const return_type = fn_ty.fnReturnType(); - const want_sret = isByRef(return_type, target); - - if (want_sret) { - try params.append(typeToValtype(return_type, target)); - } - - // param types - if (fn_ty.fnParamLen() != 0) { - const fn_params = try gpa.alloc(Type, fn_ty.fnParamLen()); - defer gpa.free(fn_params); - fn_ty.fnParamTypes(fn_params); - for (fn_params) |param_type| { - if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; - try params.append(typeToValtype(param_type, target)); + if (firstParamSRet(fn_info, target)) { + try params.append(typeToValtype(fn_info.return_type, target)); + } else if (fn_info.return_type.hasRuntimeBitsIgnoreComptime()) { + if (fn_info.cc == .C) { + const res_classes = abi.classifyType(fn_info.return_type, target); + assert(res_classes[0] == .direct and res_classes[1] == .none); + const scalar_type = abi.scalarType(fn_info.return_type, target); + try returns.append(typeToValtype(scalar_type, target)); + } else { + try returns.append(typeToValtype(fn_info.return_type, target)); } } - // return type - if (!want_sret and return_type.hasRuntimeBitsIgnoreComptime()) { - try returns.append(typeToValtype(return_type, target)); + // param types + if (fn_info.param_types.len != 0) { + for (fn_info.param_types) |param_type| { + if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; + + switch (fn_info.cc) { + .C => { + const param_classes = abi.classifyType(param_type, target); + for (param_classes) |class| { + if (class == .none) continue; + if (class == .direct) { + const scalar_type = abi.scalarType(param_type, target); + try params.append(typeToValtype(scalar_type, target)); + } else { + try params.append(typeToValtype(param_type, target)); + } + } + }, + else => try params.append(typeToValtype(param_type, target)), + } + } } return wasm.Type{ @@ -857,7 +868,7 @@ pub fn generate( } fn genFunc(self: *Self) InnerError!void { - var func_type = try genFunctype(self.gpa, self.decl.ty, self.target); + var func_type = try genFunctype(self.gpa, self.decl.ty.fnInfo(), self.target); defer func_type.deinit(self.gpa); self.decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); @@ -957,21 +968,22 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu .args = &.{}, .return_value = .none, }; + if (cc == .Naked) return result; + var args = std.ArrayList(WValue).init(self.gpa); defer args.deinit(); - const ret_ty = fn_ty.fnReturnType(); // Check if we store the result as a pointer to the stack rather than // by value - if (isByRef(ret_ty, self.target)) { + if (firstParamSRet(fn_ty.fnInfo(), self.target)) { // the sret arg will be passed as first argument, therefore we // set the `return_value` before allocating locals for regular args. result.return_value = .{ .local = self.local_index }; self.local_index += 1; } + switch (cc) { - .Naked => return result, - .Unspecified, .C => { + .Unspecified => { for (param_types) |ty| { if (!ty.hasRuntimeBitsIgnoreComptime()) { continue; @@ -981,12 +993,105 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu self.local_index += 1; } }, - else => return self.fail("TODO implement function parameters for cc '{}' on wasm", .{cc}), + .C => { + for (param_types) |ty| { + const ty_classes = abi.classifyType(ty, self.target); + for (ty_classes) |class| { + if (class == .none) continue; + try args.append(.{ .local = self.local_index }); + self.local_index += 1; + } + } + }, + else => return self.fail("calling convention '{s}' not supported for Wasm", .{@tagName(cc)}), } result.args = args.toOwnedSlice(); return result; } +fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool { + switch (fn_info.cc) { + .Unspecified, .Inline => return isByRef(fn_info.return_type, target), + .C => { + const ty_classes = abi.classifyType(fn_info.return_type, target); + if (ty_classes[0] == .indirect) return true; + if (ty_classes[0] == .direct and ty_classes[1] == .direct) return true; + return false; + }, + else => return false, + } +} + +/// Lowers a Zig type and its value based on a given calling convention to ensure +/// it matches the ABI. +fn lowerArg(self: *Self, cc: std.builtin.CallingConvention, ty: Type, value: WValue) !void { + if (cc != .C) { + return self.lowerToStack(value); + } + + const ty_classes = abi.classifyType(ty, self.target); + assert(ty_classes[0] != .none); + switch (ty.zigTypeTag()) { + .Struct, .Union => { + if (ty_classes[0] == .indirect) { + return self.lowerToStack(value); + } + assert(ty_classes[0] == .direct); + const scalar_type = abi.scalarType(ty, self.target); + const abi_size = scalar_type.abiSize(self.target); + const opcode = buildOpcode(.{ + .op = .load, + .width = @intCast(u8, abi_size), + .signedness = if (scalar_type.isSignedInt()) .signed else .unsigned, + .valtype1 = typeToValtype(scalar_type, self.target), + }); + try self.emitWValue(value); + try self.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{ + .offset = value.offset(), + .alignment = scalar_type.abiAlignment(self.target), + }); + }, + .Int, .Float => { + if (ty_classes[1] == .none) { + return self.lowerToStack(value); + } + assert(ty_classes[0] == .direct and ty_classes[1] == .direct); + assert(ty.abiSize(self.target) == 16); + // in this case we have an integer or float that must be lowered as 2 i64's. + try self.emitWValue(value); + try self.addMemArg(.i64_load, .{ .offset = value.offset(), .alignment = 16 }); + try self.emitWValue(value); + try self.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 16 }); + }, + else => return self.lowerToStack(value), + } +} + +/// Lowers a `WValue` to the stack. This means when the `value` results in +/// `.stack_offset` we calculate the pointer of this offset and use that. +/// The value is left on the stack, and not stored in any temporary. +fn lowerToStack(self: *Self, value: WValue) !void { + switch (value) { + .stack_offset => |offset| { + try self.emitWValue(value); + if (offset > 0) { + switch (self.arch()) { + .wasm32 => { + try self.addImm32(@bitCast(i32, offset)); + try self.addTag(.i32_add); + }, + .wasm64 => { + try self.addImm64(offset); + try self.addTag(.i64_add); + }, + else => unreachable, + } + } + }, + else => try self.emitWValue(value), + } +} + /// Creates a local for the initial stack value /// Asserts `initial_stack_value` is `.none` fn initializeStack(self: *Self) !void { @@ -1489,11 +1594,31 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); + const ret_ty = self.decl.ty.fnReturnType(); // result must be stored in the stack and we return a pointer // to the stack instead if (self.return_value != .none) { try self.store(self.return_value, operand, self.decl.ty.fnReturnType(), 0); + } else if (self.decl.ty.fnInfo().cc == .C and ret_ty.hasRuntimeBitsIgnoreComptime()) { + switch (ret_ty.zigTypeTag()) { + // Aggregate types can be lowered as a singular value + .Struct, .Union => { + const scalar_type = abi.scalarType(ret_ty, self.target); + try self.emitWValue(operand); + const opcode = buildOpcode(.{ + .op = .load, + .width = @intCast(u8, scalar_type.abiSize(self.target)), + .signedness = if (scalar_type.isSignedInt()) .signed else .unsigned, + .valtype1 = typeToValtype(scalar_type, self.target), + }); + try self.addMemArg(Mir.Inst.Tag.fromOpcode(opcode), .{ + .offset = operand.offset(), + .alignment = scalar_type.abiAlignment(self.target), + }); + }, + else => try self.emitWValue(operand), + } } else { try self.emitWValue(operand); } @@ -1509,9 +1634,10 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return self.allocStack(Type.usize); // create pointer to void } - if (isByRef(child_type, self.target)) { + if (firstParamSRet(self.decl.ty.fnInfo(), self.target)) { return self.return_value; } + return self.allocStackPtr(inst); } @@ -1521,7 +1647,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ret_ty = self.air.typeOf(un_op).childType(); if (!ret_ty.hasRuntimeBitsIgnoreComptime()) return WValue.none; - if (!isByRef(ret_ty, self.target)) { + if (!firstParamSRet(self.decl.ty.fnInfo(), self.target)) { const result = try self.load(operand, ret_ty, 0); try self.emitWValue(result); } @@ -1544,7 +1670,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. else => unreachable, }; const ret_ty = fn_ty.fnReturnType(); - const first_param_sret = isByRef(ret_ty, self.target); + const first_param_sret = firstParamSRet(fn_ty.fnInfo(), self.target); const callee: ?*Decl = blk: { const func_val = self.air.value(pl_op.operand) orelse break :blk null; @@ -1554,7 +1680,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. break :blk module.declPtr(func.data.owner_decl); } else if (func_val.castTag(.extern_fn)) |extern_fn| { const ext_decl = module.declPtr(extern_fn.data.owner_decl); - var func_type = try genFunctype(self.gpa, ext_decl.ty, self.target); + var func_type = try genFunctype(self.gpa, ext_decl.ty.fnInfo(), self.target); defer func_type.deinit(self.gpa); ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); try self.bin_file.addOrUpdateImport(ext_decl); @@ -1579,10 +1705,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const arg_ty = self.air.typeOf(arg_ref); if (!arg_ty.hasRuntimeBitsIgnoreComptime()) continue; - switch (arg_val) { - .stack_offset => try self.emitWValue(try self.buildPointerOffset(arg_val, 0, .new)), - else => try self.emitWValue(arg_val), - } + try self.lowerArg(fn_ty.fnInfo().cc, arg_ty, arg_val); } if (callee) |direct| { @@ -1594,7 +1717,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const operand = try self.resolveInst(pl_op.operand); try self.emitWValue(operand); - var fn_type = try genFunctype(self.gpa, fn_ty, self.target); + var fn_type = try genFunctype(self.gpa, fn_ty.fnInfo(), self.target); defer fn_type.deinit(self.gpa); const fn_type_index = try self.bin_file.putOrGetFuncType(fn_type); @@ -1608,6 +1731,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. return WValue.none; } else if (first_param_sret) { return sret; + // TODO: Make this less fragile and optimize + } else if (fn_ty.fnInfo().cc == .C and ret_ty.zigTypeTag() == .Struct or ret_ty.zigTypeTag() == .Union) { + const result_local = try self.allocLocal(ret_ty); + try self.addLabel(.local_set, result_local.local); + const scalar_type = abi.scalarType(ret_ty, self.target); + const result = try self.allocStack(scalar_type); + try self.store(result, result_local, scalar_type, 0); + return result; } else { const result_local = try self.allocLocal(ret_ty); try self.addLabel(.local_set, result_local.local); @@ -1749,9 +1880,20 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { } fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { - _ = inst; - defer self.arg_index += 1; - return self.args[self.arg_index]; + const arg = self.args[self.arg_index]; + const cc = self.decl.ty.fnInfo().cc; + if (cc == .C) { + const ty = self.air.typeOfIndex(inst); + const arg_classes = abi.classifyType(ty, self.target); + for (arg_classes) |class| { + if (class != .none) { + self.arg_index += 1; + } + } + } else { + self.arg_index += 1; + } + return arg; } fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { diff --git a/src/arch/wasm/abi.zig b/src/arch/wasm/abi.zig index 99398fd00c..63b319613b 100644 --- a/src/arch/wasm/abi.zig +++ b/src/arch/wasm/abi.zig @@ -52,6 +52,7 @@ pub fn classifyType(ty: Type, target: Target) [2]Class { return memory; }, .Bool => return direct, + .Array => return memory, .ErrorUnion => { const has_tag = ty.errorUnionSet().hasRuntimeBitsIgnoreComptime(); const has_pl = ty.errorUnionPayload().hasRuntimeBitsIgnoreComptime(); @@ -73,16 +74,13 @@ pub fn classifyType(ty: Type, target: Target) [2]Class { if (ty.isSlice()) return memory; return direct; }, - .Array => { - if (ty.arrayLen() == 1) return direct; - return memory; - }, .Union => { const layout = ty.unionGetLayout(target); if (layout.payload_size == 0 and layout.tag_size != 0) { return classifyType(ty.unionTagType().?, target); } - return classifyType(ty.errorUnionPayload(), target); + if (ty.unionFields().count() > 1) return memory; + return classifyType(ty.unionFields().values()[0].ty, target); }, .AnyFrame, .Frame => return direct, @@ -100,3 +98,24 @@ pub fn classifyType(ty: Type, target: Target) [2]Class { => unreachable, } } + +/// Returns the scalar type a given type can represent. +/// Asserts given type can be represented as scalar, such as +/// a struct with a single scalar field. +pub fn scalarType(ty: Type, target: std.Target) Type { + switch (ty.zigTypeTag()) { + .Struct => { + std.debug.assert(ty.structFieldCount() == 1); + return scalarType(ty.structFieldType(0), target); + }, + .Union => { + const layout = ty.unionGetLayout(target); + if (layout.payload_size == 0 and layout.tag_size != 0) { + return scalarType(ty.unionTagType().?, target); + } + std.debug.assert(ty.unionFields().count() == 1); + return scalarType(ty.unionFields().values()[0].ty, target); + }, + else => return ty, + } +} From 18f30346291bd2471e07924af161de080935dd60 Mon Sep 17 00:00:00 2001 From: protty <45520026+kprotty@users.noreply.github.com> Date: Tue, 26 Apr 2022 16:48:56 -0500 Subject: [PATCH 1241/2031] std.Thread: ResetEvent improvements (#11523) * std: start removing redundant ResetEvents * src: fix other uses of std.Thread.ResetEvent * src: add builtin.sanitize_thread for tsan detection * atomic: add Atomic.fence for proper fencing with tsan * Thread: remove the other ResetEvent's and rewrite the current one * Thread: ResetEvent docs * zig fmt + WaitGroup.reset() fix * src: fix build issues for ResetEvent + tsan * Thread: ResetEvent tests * Thread: ResetEvent module doc * Atomic: replace llvm *p memory constraint with *m * panicking: handle spurious wakeups in futex.wait() when waiting for abort() * zig fmt --- CMakeLists.txt | 2 - lib/std/Thread.zig | 32 +- lib/std/Thread/AutoResetEvent.zig | 222 -------------- lib/std/Thread/Futex.zig | 2 +- lib/std/Thread/ResetEvent.zig | 450 ++++++++++++++-------------- lib/std/Thread/StaticResetEvent.zig | 395 ------------------------ lib/std/atomic/Atomic.zig | 80 ++++- lib/std/debug.zig | 10 +- lib/std/event/loop.zig | 33 +- lib/std/fs/test.zig | 37 +-- src/Compilation.zig | 17 +- src/ThreadPool.zig | 141 ++++----- src/WaitGroup.zig | 49 +-- src/crash_report.zig | 11 +- src/stage1/codegen.cpp | 1 + 15 files changed, 433 insertions(+), 1049 deletions(-) delete mode 100644 lib/std/Thread/AutoResetEvent.zig delete mode 100644 lib/std/Thread/StaticResetEvent.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c4982ea83..463718b31c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -533,11 +533,9 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/target/wasm.zig" "${CMAKE_SOURCE_DIR}/lib/std/target/x86.zig" "${CMAKE_SOURCE_DIR}/lib/std/Thread.zig" - "${CMAKE_SOURCE_DIR}/lib/std/Thread/AutoResetEvent.zig" "${CMAKE_SOURCE_DIR}/lib/std/Thread/Futex.zig" "${CMAKE_SOURCE_DIR}/lib/std/Thread/Mutex.zig" "${CMAKE_SOURCE_DIR}/lib/std/Thread/ResetEvent.zig" - "${CMAKE_SOURCE_DIR}/lib/std/Thread/StaticResetEvent.zig" "${CMAKE_SOURCE_DIR}/lib/std/time.zig" "${CMAKE_SOURCE_DIR}/lib/std/treap.zig" "${CMAKE_SOURCE_DIR}/lib/std/unicode.zig" diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index db7117fdd7..45d2ac0040 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -10,10 +10,8 @@ const assert = std.debug.assert; const target = builtin.target; const Atomic = std.atomic.Atomic; -pub const AutoResetEvent = @import("Thread/AutoResetEvent.zig"); pub const Futex = @import("Thread/Futex.zig"); pub const ResetEvent = @import("Thread/ResetEvent.zig"); -pub const StaticResetEvent = @import("Thread/StaticResetEvent.zig"); pub const Mutex = @import("Thread/Mutex.zig"); pub const Semaphore = @import("Thread/Semaphore.zig"); pub const Condition = @import("Thread/Condition.zig"); @@ -1078,17 +1076,13 @@ test "setName, getName" { if (builtin.single_threaded) return error.SkipZigTest; const Context = struct { - start_wait_event: ResetEvent = undefined, - test_done_event: ResetEvent = undefined, + start_wait_event: ResetEvent = .{}, + test_done_event: ResetEvent = .{}, + thread_done_event: ResetEvent = .{}, done: std.atomic.Atomic(bool) = std.atomic.Atomic(bool).init(false), thread: Thread = undefined, - fn init(self: *@This()) !void { - try self.start_wait_event.init(); - try self.test_done_event.init(); - } - pub fn run(ctx: *@This()) !void { // Wait for the main thread to have set the thread field in the context. ctx.start_wait_event.wait(); @@ -1104,16 +1098,14 @@ test "setName, getName" { // Signal our test is done ctx.test_done_event.set(); - while (!ctx.done.load(.SeqCst)) { - std.time.sleep(5 * std.time.ns_per_ms); - } + // wait for the thread to property exit + ctx.thread_done_event.wait(); } }; var context = Context{}; - try context.init(); - var thread = try spawn(.{}, Context.run, .{&context}); + context.thread = thread; context.start_wait_event.set(); context.test_done_event.wait(); @@ -1139,16 +1131,14 @@ test "setName, getName" { }, } - context.done.store(true, .SeqCst); + context.thread_done_event.set(); thread.join(); } test "std.Thread" { // Doesn't use testing.refAllDecls() since that would pull in the compileError spinLoopHint. - _ = AutoResetEvent; _ = Futex; _ = ResetEvent; - _ = StaticResetEvent; _ = Mutex; _ = Semaphore; _ = Condition; @@ -1163,9 +1153,7 @@ test "Thread.join" { if (builtin.single_threaded) return error.SkipZigTest; var value: usize = 0; - var event: ResetEvent = undefined; - try event.init(); - defer event.deinit(); + var event = ResetEvent{}; const thread = try Thread.spawn(.{}, testIncrementNotify, .{ &value, &event }); thread.join(); @@ -1177,9 +1165,7 @@ test "Thread.detach" { if (builtin.single_threaded) return error.SkipZigTest; var value: usize = 0; - var event: ResetEvent = undefined; - try event.init(); - defer event.deinit(); + var event = ResetEvent{}; const thread = try Thread.spawn(.{}, testIncrementNotify, .{ &value, &event }); thread.detach(); diff --git a/lib/std/Thread/AutoResetEvent.zig b/lib/std/Thread/AutoResetEvent.zig deleted file mode 100644 index f22b1e3db0..0000000000 --- a/lib/std/Thread/AutoResetEvent.zig +++ /dev/null @@ -1,222 +0,0 @@ -//! Similar to `StaticResetEvent` but on `set()` it also (atomically) does `reset()`. -//! Unlike StaticResetEvent, `wait()` can only be called by one thread (MPSC-like). -//! -//! AutoResetEvent has 3 possible states: -//! - UNSET: the AutoResetEvent is currently unset -//! - SET: the AutoResetEvent was notified before a wait() was called -//! - : there is an active waiter waiting for a notification. -//! -//! When attempting to wait: -//! if the event is unset, it registers a ResetEvent pointer to be notified when the event is set -//! if the event is already set, then it consumes the notification and resets the event. -//! -//! When attempting to notify: -//! if the event is unset, then we set the event -//! if theres a waiting ResetEvent, then we unset the event and notify the ResetEvent -//! -//! This ensures that the event is automatically reset after a wait() has been issued -//! and avoids the race condition when using StaticResetEvent in the following scenario: -//! thread 1 | thread 2 -//! StaticResetEvent.wait() | -//! | StaticResetEvent.set() -//! | StaticResetEvent.set() -//! StaticResetEvent.reset() | -//! StaticResetEvent.wait() | (missed the second .set() notification above) - -state: usize = UNSET, - -const std = @import("../std.zig"); -const builtin = @import("builtin"); -const testing = std.testing; -const assert = std.debug.assert; -const StaticResetEvent = std.Thread.StaticResetEvent; -const AutoResetEvent = @This(); - -const UNSET = 0; -const SET = 1; - -/// the minimum alignment for the `*StaticResetEvent` created by wait*() -const event_align = std.math.max(@alignOf(StaticResetEvent), 2); - -pub fn wait(self: *AutoResetEvent) void { - self.waitFor(null) catch unreachable; -} - -pub fn timedWait(self: *AutoResetEvent, timeout: u64) error{TimedOut}!void { - return self.waitFor(timeout); -} - -fn waitFor(self: *AutoResetEvent, timeout: ?u64) error{TimedOut}!void { - // lazily initialized StaticResetEvent - var reset_event: StaticResetEvent align(event_align) = undefined; - var has_reset_event = false; - - var state = @atomicLoad(usize, &self.state, .SeqCst); - while (true) { - // consume a notification if there is any - if (state == SET) { - @atomicStore(usize, &self.state, UNSET, .SeqCst); - return; - } - - // check if theres currently a pending ResetEvent pointer already registered - if (state != UNSET) { - unreachable; // multiple waiting threads on the same AutoResetEvent - } - - // lazily initialize the ResetEvent if it hasn't been already - if (!has_reset_event) { - has_reset_event = true; - reset_event = .{}; - } - - // Since the AutoResetEvent currently isnt set, - // try to register our ResetEvent on it to wait - // for a set() call from another thread. - if (@cmpxchgWeak( - usize, - &self.state, - UNSET, - @ptrToInt(&reset_event), - .SeqCst, - .SeqCst, - )) |new_state| { - state = new_state; - continue; - } - - // if no timeout was specified, then just wait forever - const timeout_ns = timeout orelse { - reset_event.wait(); - return; - }; - - // wait with a timeout and return if signalled via set() - switch (reset_event.timedWait(timeout_ns)) { - .event_set => return, - .timed_out => {}, - } - - // If we timed out, we need to transition the AutoResetEvent back to UNSET. - // If we don't, then when we return, a set() thread could observe a pointer to an invalid ResetEvent. - state = @cmpxchgStrong( - usize, - &self.state, - @ptrToInt(&reset_event), - UNSET, - .SeqCst, - .SeqCst, - ) orelse return error.TimedOut; - - // We didn't manage to unregister ourselves from the state. - if (state == SET) { - unreachable; // AutoResetEvent notified without waking up the waiting thread - } else if (state != UNSET) { - unreachable; // multiple waiting threads on the same AutoResetEvent observed when timing out - } - - // This menas a set() thread saw our ResetEvent pointer, acquired it, and is trying to wake it up. - // We need to wait for it to wake up our ResetEvent before we can return and invalidate it. - // We don't return error.TimedOut here as it technically notified us while we were "timing out". - reset_event.wait(); - return; - } -} - -pub fn set(self: *AutoResetEvent) void { - var state = @atomicLoad(usize, &self.state, .SeqCst); - while (true) { - // If the AutoResetEvent is already set, there is nothing else left to do - if (state == SET) { - return; - } - - // If the AutoResetEvent isn't set, - // then try to leave a notification for the wait() thread that we set() it. - if (state == UNSET) { - state = @cmpxchgWeak( - usize, - &self.state, - UNSET, - SET, - .SeqCst, - .SeqCst, - ) orelse return; - continue; - } - - // There is a ResetEvent pointer registered on the AutoResetEvent event thats waiting. - // Try to acquire ownership of it so that we can wake it up. - // This also resets the AutoResetEvent so that there is no race condition as defined above. - if (@cmpxchgWeak( - usize, - &self.state, - state, - UNSET, - .SeqCst, - .SeqCst, - )) |new_state| { - state = new_state; - continue; - } - - const reset_event = @intToPtr(*align(event_align) StaticResetEvent, state); - reset_event.set(); - return; - } -} - -test "basic usage" { - // test local code paths - { - var event = AutoResetEvent{}; - try testing.expectError(error.TimedOut, event.timedWait(1)); - event.set(); - event.wait(); - } - - // test cross-thread signaling - if (builtin.single_threaded) - return; - - const Context = struct { - value: u128 = 0, - in: AutoResetEvent = AutoResetEvent{}, - out: AutoResetEvent = AutoResetEvent{}, - - const Self = @This(); - - fn sender(self: *Self) !void { - try testing.expect(self.value == 0); - self.value = 1; - self.out.set(); - - self.in.wait(); - try testing.expect(self.value == 2); - self.value = 3; - self.out.set(); - - self.in.wait(); - try testing.expect(self.value == 4); - } - - fn receiver(self: *Self) !void { - self.out.wait(); - try testing.expect(self.value == 1); - self.value = 2; - self.in.set(); - - self.out.wait(); - try testing.expect(self.value == 3); - self.value = 4; - self.in.set(); - } - }; - - var context = Context{}; - const send_thread = try std.Thread.spawn(.{}, Context.sender, .{&context}); - const recv_thread = try std.Thread.spawn(.{}, Context.receiver, .{&context}); - - send_thread.join(); - recv_thread.join(); -} diff --git a/lib/std/Thread/Futex.zig b/lib/std/Thread/Futex.zig index 33eb30ba9d..bbe6b813ba 100644 --- a/lib/std/Thread/Futex.zig +++ b/lib/std/Thread/Futex.zig @@ -809,7 +809,7 @@ const PosixImpl = struct { // // The pending count increment in wait() must also now use SeqCst for the update + this pending load // to be in the same modification order as our load isn't using Release/Acquire to guarantee it. - std.atomic.fence(.SeqCst); + bucket.pending.fence(.SeqCst); if (bucket.pending.load(.Monotonic) == 0) { return; } diff --git a/lib/std/Thread/ResetEvent.zig b/lib/std/Thread/ResetEvent.zig index 7fc4f3c2b4..87232c29cf 100644 --- a/lib/std/Thread/ResetEvent.zig +++ b/lib/std/Thread/ResetEvent.zig @@ -1,291 +1,281 @@ -//! A thread-safe resource which supports blocking until signaled. -//! This API is for kernel threads, not evented I/O. -//! This API requires being initialized at runtime, and initialization -//! can fail. Once initialized, the core operations cannot fail. -//! If you need an abstraction that cannot fail to be initialized, see -//! `std.Thread.StaticResetEvent`. However if you can handle initialization failure, -//! it is preferred to use `ResetEvent`. +//! ResetEvent is a thread-safe bool which can be set to true/false ("set"/"unset"). +//! It can also block threads until the "bool" is set with cancellation via timed waits. +//! ResetEvent can be statically initialized and is at most `@sizeOf(u64)` large. -const ResetEvent = @This(); const std = @import("../std.zig"); const builtin = @import("builtin"); -const testing = std.testing; -const assert = std.debug.assert; -const c = std.c; +const ResetEvent = @This(); + const os = std.os; -const time = std.time; +const assert = std.debug.assert; +const testing = std.testing; +const Atomic = std.atomic.Atomic; +const Futex = std.Thread.Futex; -impl: Impl, +impl: Impl = .{}, -pub const Impl = if (builtin.single_threaded) - std.Thread.StaticResetEvent.DebugEvent -else if (builtin.target.isDarwin()) - DarwinEvent -else if (std.Thread.use_pthreads) - PosixEvent +/// Returns if the ResetEvent was set(). +/// Once reset() is called, this returns false until the next set(). +/// The memory accesses before the set() can be said to happen before isSet() returns true. +pub fn isSet(self: *const ResetEvent) bool { + return self.impl.isSet(); +} + +/// Block's the callers thread until the ResetEvent is set(). +/// This is effectively a more efficient version of `while (!isSet()) {}`. +/// The memory accesses before the set() can be said to happen before wait() returns. +pub fn wait(self: *ResetEvent) void { + self.impl.wait(null) catch |err| switch (err) { + error.Timeout => unreachable, // no timeout provided so we shouldn't have timed-out + }; +} + +/// Block's the callers thread until the ResetEvent is set(), or until the corresponding timeout expires. +/// If the timeout expires before the ResetEvent is set, `error.Timeout` is returned. +/// This is effectively a more efficient version of `while (!isSet()) {}`. +/// The memory accesses before the set() can be said to happen before timedWait() returns without error. +pub fn timedWait(self: *ResetEvent, timeout_ns: u64) error{Timeout}!void { + return self.impl.wait(timeout_ns); +} + +/// Marks the ResetEvent as "set" and unblocks any threads in `wait()` or `timedWait()` to observe the new state. +/// The ResetEvent says "set" until reset() is called, making future set() calls do nothing semantically. +/// The memory accesses before set() can be said to happen before isSet() returns true or wait()/timedWait() return successfully. +pub fn set(self: *ResetEvent) void { + self.impl.set(); +} + +/// Unmarks the ResetEvent from its "set" state if set() was called previously. +/// It is undefined behavior is reset() is called while threads are blocked in wait() or timedWait(). +/// Concurrent calls to set(), isSet() and reset() are allowed. +pub fn reset(self: *ResetEvent) void { + self.impl.reset(); +} + +const Impl = if (builtin.single_threaded) + SingleThreadedImpl else - std.Thread.StaticResetEvent.AtomicEvent; + FutexImpl; -pub const InitError = error{SystemResources}; +const SingleThreadedImpl = struct { + is_set: bool = false, -/// After `init`, it is legal to call any other function. -pub fn init(ev: *ResetEvent) InitError!void { - return ev.impl.init(); -} + fn isSet(self: *const Impl) bool { + return self.is_set; + } -/// This function is not thread-safe. -/// After `deinit`, the only legal function to call is `init`. -pub fn deinit(ev: *ResetEvent) void { - return ev.impl.deinit(); -} + fn wait(self: *Impl, timeout: ?u64) error{Timeout}!void { + if (self.isSet()) { + return; + } -/// Sets the event if not already set and wakes up all the threads waiting on -/// the event. It is safe to call `set` multiple times before calling `wait`. -/// However it is illegal to call `set` after `wait` is called until the event -/// is `reset`. This function is thread-safe. -pub fn set(ev: *ResetEvent) void { - return ev.impl.set(); -} - -/// Resets the event to its original, unset state. -/// This function is *not* thread-safe. It is equivalent to calling -/// `deinit` followed by `init` but without the possibility of failure. -pub fn reset(ev: *ResetEvent) void { - return ev.impl.reset(); -} - -/// Wait for the event to be set by blocking the current thread. -/// Thread-safe. No spurious wakeups. -/// Upon return from `wait`, the only functions available to be called -/// in `ResetEvent` are `reset` and `deinit`. -pub fn wait(ev: *ResetEvent) void { - return ev.impl.wait(); -} - -pub const TimedWaitResult = enum { event_set, timed_out }; - -/// Wait for the event to be set by blocking the current thread. -/// A timeout in nanoseconds can be provided as a hint for how -/// long the thread should block on the unset event before returning -/// `TimedWaitResult.timed_out`. -/// Thread-safe. No precision of timing is guaranteed. -/// Upon return from `wait`, the only functions available to be called -/// in `ResetEvent` are `reset` and `deinit`. -pub fn timedWait(ev: *ResetEvent, timeout_ns: u64) TimedWaitResult { - return ev.impl.timedWait(timeout_ns); -} - -/// Apple has decided to not support POSIX semaphores, so we go with a -/// different approach using Grand Central Dispatch. This API is exposed -/// by libSystem so it is guaranteed to be available on all Darwin platforms. -pub const DarwinEvent = struct { - sem: c.dispatch_semaphore_t = undefined, - - pub fn init(ev: *DarwinEvent) !void { - ev.* = .{ - .sem = c.dispatch_semaphore_create(0) orelse return error.SystemResources, + // There are no other threads to wake us up. + // So if we wait without a timeout we would never wake up. + const timeout_ns = timeout orelse { + unreachable; // deadlock detected }; + + std.time.sleep(timeout_ns); + return error.Timeout; } - pub fn deinit(ev: *DarwinEvent) void { - c.dispatch_release(ev.sem); - ev.* = undefined; + fn set(self: *Impl) void { + self.is_set = true; } - pub fn set(ev: *DarwinEvent) void { - // Empirically this returns the numerical value of the semaphore. - _ = c.dispatch_semaphore_signal(ev.sem); - } - - pub fn wait(ev: *DarwinEvent) void { - assert(c.dispatch_semaphore_wait(ev.sem, c.DISPATCH_TIME_FOREVER) == 0); - } - - pub fn timedWait(ev: *DarwinEvent, timeout_ns: u64) TimedWaitResult { - const t = c.dispatch_time(c.DISPATCH_TIME_NOW, @intCast(i64, timeout_ns)); - if (c.dispatch_semaphore_wait(ev.sem, t) != 0) { - return .timed_out; - } else { - return .event_set; - } - } - - pub fn reset(ev: *DarwinEvent) void { - // Keep calling until the semaphore goes back down to 0. - while (c.dispatch_semaphore_wait(ev.sem, c.DISPATCH_TIME_NOW) == 0) {} + fn reset(self: *Impl) void { + self.is_set = false; } }; -/// POSIX semaphores must be initialized at runtime because they are allowed to -/// be implemented as file descriptors, in which case initialization would require -/// a syscall to open the fd. -pub const PosixEvent = struct { - sem: c.sem_t = undefined, +const FutexImpl = struct { + state: Atomic(u32) = Atomic(u32).init(unset), - pub fn init(ev: *PosixEvent) !void { - switch (c.getErrno(c.sem_init(&ev.sem, 0, 0))) { - .SUCCESS => return, - else => return error.SystemResources, + const unset = 0; + const waiting = 1; + const is_set = 2; + + fn isSet(self: *const Impl) bool { + // Acquire barrier ensures memory accesses before set() happen before we return true. + return self.state.load(.Acquire) == is_set; + } + + fn wait(self: *Impl, timeout: ?u64) error{Timeout}!void { + // Outline the slow path to allow isSet() to be inlined + if (!self.isSet()) { + return self.waitUntilSet(timeout); } } - pub fn deinit(ev: *PosixEvent) void { - assert(c.sem_destroy(&ev.sem) == 0); - ev.* = undefined; - } + fn waitUntilSet(self: *Impl, timeout: ?u64) error{Timeout}!void { + @setCold(true); - pub fn set(ev: *PosixEvent) void { - assert(c.sem_post(&ev.sem) == 0); - } + // Try to set the state from `unset` to `waiting` to indicate + // to the set() thread that others are blocked on the ResetEvent. + // We avoid using any strict barriers until the end when we know the ResetEvent is set. + var state = self.state.load(.Monotonic); + if (state == unset) { + state = self.state.compareAndSwap(state, waiting, .Monotonic, .Monotonic) orelse waiting; + } - pub fn wait(ev: *PosixEvent) void { - while (true) { - switch (c.getErrno(c.sem_wait(&ev.sem))) { - .SUCCESS => return, - .INTR => continue, - .INVAL => unreachable, - else => unreachable, + // Wait until the ResetEvent is set since the state is waiting. + if (state == waiting) { + var futex_deadline = Futex.Deadline.init(timeout); + while (true) { + const wait_result = futex_deadline.wait(&self.state, waiting); + + // Check if the ResetEvent was set before possibly reporting error.Timeout below. + state = self.state.load(.Monotonic); + if (state != waiting) { + break; + } + + try wait_result; } } + + // Acquire barrier ensures memory accesses before set() happen before we return. + assert(state == is_set); + self.state.fence(.Acquire); } - pub fn timedWait(ev: *PosixEvent, timeout_ns: u64) TimedWaitResult { - var ts: os.timespec = undefined; - var timeout_abs = timeout_ns; - os.clock_gettime(os.CLOCK.REALTIME, &ts) catch return .timed_out; - timeout_abs += @intCast(u64, ts.tv_sec) * time.ns_per_s; - timeout_abs += @intCast(u64, ts.tv_nsec); - ts.tv_sec = @intCast(@TypeOf(ts.tv_sec), @divFloor(timeout_abs, time.ns_per_s)); - ts.tv_nsec = @intCast(@TypeOf(ts.tv_nsec), @mod(timeout_abs, time.ns_per_s)); - while (true) { - switch (c.getErrno(c.sem_timedwait(&ev.sem, &ts))) { - .SUCCESS => return .event_set, - .INTR => continue, - .INVAL => unreachable, - .TIMEDOUT => return .timed_out, - else => unreachable, - } + fn set(self: *Impl) void { + // Quick check if the ResetEvent is already set before doing the atomic swap below. + // set() could be getting called quite often and multiple threads calling swap() increases contention unnecessarily. + if (self.state.load(.Monotonic) == is_set) { + return; + } + + // Mark the ResetEvent as set and unblock all waiters waiting on it if any. + // Release barrier ensures memory accesses before set() happen before the ResetEvent is observed to be "set". + if (self.state.swap(is_set, .Release) == waiting) { + Futex.wake(&self.state, std.math.maxInt(u32)); } } - pub fn reset(ev: *PosixEvent) void { - while (true) { - switch (c.getErrno(c.sem_trywait(&ev.sem))) { - .SUCCESS => continue, // Need to make it go to zero. - .INTR => continue, - .INVAL => unreachable, - .AGAIN => return, // The semaphore currently has the value zero. - else => unreachable, - } - } + fn reset(self: *Impl) void { + self.state.store(unset, .Monotonic); } }; -test "basic usage" { - var event: ResetEvent = undefined; - try event.init(); - defer event.deinit(); +test "ResetEvent - smoke test" { + // make sure the event is unset + var event = ResetEvent{}; + try testing.expectEqual(false, event.isSet()); - // test event setting + // make sure the event gets set event.set(); + try testing.expectEqual(true, event.isSet()); - // test event resetting + // make sure the event gets unset again event.reset(); + try testing.expectEqual(false, event.isSet()); - // test event waiting (non-blocking) + // waits should timeout as there's no other thread to set the event + try testing.expectError(error.Timeout, event.timedWait(0)); + try testing.expectError(error.Timeout, event.timedWait(std.time.ns_per_ms)); + + // set the event again and make sure waits complete event.set(); event.wait(); - event.reset(); + try event.timedWait(std.time.ns_per_ms); + try testing.expectEqual(true, event.isSet()); +} - event.set(); - try testing.expectEqual(TimedWaitResult.event_set, event.timedWait(1)); - - // test cross-thread signaling - if (builtin.single_threaded) - return; +test "ResetEvent - signaling" { + // This test requires spawning threads + if (builtin.single_threaded) { + return error.SkipZigTest; + } const Context = struct { - const Self = @This(); + in: ResetEvent = .{}, + out: ResetEvent = .{}, + value: usize = 0, - value: u128, - in: ResetEvent, - out: ResetEvent, - - fn init(self: *Self) !void { - self.* = .{ - .value = 0, - .in = undefined, - .out = undefined, - }; - try self.in.init(); - try self.out.init(); - } - - fn deinit(self: *Self) void { - self.in.deinit(); - self.out.deinit(); - self.* = undefined; - } - - fn sender(self: *Self) !void { - // update value and signal input - try testing.expect(self.value == 0); - self.value = 1; - self.in.set(); - - // wait for receiver to update value and signal output - self.out.wait(); - try testing.expect(self.value == 2); - - // update value and signal final input - self.value = 3; - self.in.set(); - } - - fn receiver(self: *Self) !void { - // wait for sender to update value and signal input + fn input(self: *@This()) !void { + // wait for the value to become 1 self.in.wait(); - try testing.expect(self.value == 1); - - // update value and signal output self.in.reset(); + try testing.expectEqual(self.value, 1); + + // bump the value and wake up output() self.value = 2; self.out.set(); - // wait for sender to update value and signal final input + // wait for output to receive 2, bump the value and wake us up with 3 self.in.wait(); - try testing.expect(self.value == 3); - } + self.in.reset(); + try testing.expectEqual(self.value, 3); - fn sleeper(self: *Self) void { - self.in.set(); - time.sleep(time.ns_per_ms * 2); - self.value = 5; + // bump the value and wake up output() for it to see 4 + self.value = 4; self.out.set(); } - fn timedWaiter(self: *Self) !void { - self.in.wait(); - try testing.expectEqual(TimedWaitResult.timed_out, self.out.timedWait(time.ns_per_us)); - try self.out.timedWait(time.ns_per_ms * 100); - try testing.expect(self.value == 5); + fn output(self: *@This()) !void { + // start with 0 and bump the value for input to see 1 + try testing.expectEqual(self.value, 0); + self.value = 1; + self.in.set(); + + // wait for input to receive 1, bump the value to 2 and wake us up + self.out.wait(); + self.out.reset(); + try testing.expectEqual(self.value, 2); + + // bump the value to 3 for input to see (rhymes) + self.value = 3; + self.in.set(); + + // wait for input to bump the value to 4 and receive no more (rhymes) + self.out.wait(); + self.out.reset(); + try testing.expectEqual(self.value, 4); } }; - var context: Context = undefined; - try context.init(); - defer context.deinit(); - const receiver = try std.Thread.spawn(.{}, Context.receiver, .{&context}); - defer receiver.join(); - try context.sender(); + var ctx = Context{}; - if (false) { - // I have now observed this fail on macOS, Windows, and Linux. - // https://github.com/ziglang/zig/issues/7009 - var timed = Context.init(); - defer timed.deinit(); - const sleeper = try std.Thread.spawn(.{}, Context.sleeper, .{&timed}); - defer sleeper.join(); - try timed.timedWaiter(); - } + const thread = try std.Thread.spawn(.{}, Context.output, .{&ctx}); + defer thread.join(); + + try ctx.input(); +} + +test "ResetEvent - broadcast" { + // This test requires spawning threads + if (builtin.single_threaded) { + return error.SkipZigTest; + } + + const num_threads = 10; + const Barrier = struct { + event: ResetEvent = .{}, + counter: Atomic(usize) = Atomic(usize).init(num_threads), + + fn wait(self: *@This()) void { + if (self.counter.fetchSub(1, .AcqRel) == 1) { + self.event.set(); + } + } + }; + + const Context = struct { + start_barrier: Barrier = .{}, + finish_barrier: Barrier = .{}, + + fn run(self: *@This()) void { + self.start_barrier.wait(); + self.finish_barrier.wait(); + } + }; + + var ctx = Context{}; + var threads: [num_threads - 1]std.Thread = undefined; + + for (threads) |*t| t.* = try std.Thread.spawn(.{}, Context.run, .{&ctx}); + defer for (threads) |t| t.join(); + + ctx.run(); } diff --git a/lib/std/Thread/StaticResetEvent.zig b/lib/std/Thread/StaticResetEvent.zig deleted file mode 100644 index fcd13f6ac7..0000000000 --- a/lib/std/Thread/StaticResetEvent.zig +++ /dev/null @@ -1,395 +0,0 @@ -//! A thread-safe resource which supports blocking until signaled. -//! This API is for kernel threads, not evented I/O. -//! This API is statically initializable. It cannot fail to be initialized -//! and it requires no deinitialization. The downside is that it may not -//! integrate as cleanly into other synchronization APIs, or, in a worst case, -//! may be forced to fall back on spin locking. As a rule of thumb, prefer -//! to use `std.Thread.ResetEvent` when possible, and use `StaticResetEvent` when -//! the logic needs stronger API guarantees. - -const std = @import("../std.zig"); -const builtin = @import("builtin"); -const StaticResetEvent = @This(); -const assert = std.debug.assert; -const os = std.os; -const time = std.time; -const linux = std.os.linux; -const windows = std.os.windows; -const testing = std.testing; - -impl: Impl = .{}, - -pub const Impl = if (builtin.single_threaded) - DebugEvent -else - AtomicEvent; - -/// Sets the event if not already set and wakes up all the threads waiting on -/// the event. It is safe to call `set` multiple times before calling `wait`. -/// However it is illegal to call `set` after `wait` is called until the event -/// is `reset`. This function is thread-safe. -pub fn set(ev: *StaticResetEvent) void { - return ev.impl.set(); -} - -/// Wait for the event to be set by blocking the current thread. -/// Thread-safe. No spurious wakeups. -/// Upon return from `wait`, the only function available to be called -/// in `StaticResetEvent` is `reset`. -pub fn wait(ev: *StaticResetEvent) void { - return ev.impl.wait(); -} - -/// Resets the event to its original, unset state. -/// This function is *not* thread-safe. It is equivalent to calling -/// `deinit` followed by `init` but without the possibility of failure. -pub fn reset(ev: *StaticResetEvent) void { - return ev.impl.reset(); -} - -pub const TimedWaitResult = std.Thread.ResetEvent.TimedWaitResult; - -/// Wait for the event to be set by blocking the current thread. -/// A timeout in nanoseconds can be provided as a hint for how -/// long the thread should block on the unset event before returning -/// `TimedWaitResult.timed_out`. -/// Thread-safe. No precision of timing is guaranteed. -/// Upon return from `timedWait`, the only function available to be called -/// in `StaticResetEvent` is `reset`. -pub fn timedWait(ev: *StaticResetEvent, timeout_ns: u64) TimedWaitResult { - return ev.impl.timedWait(timeout_ns); -} - -/// For single-threaded builds, we use this to detect deadlocks. -/// In unsafe modes this ends up being no-ops. -pub const DebugEvent = struct { - state: State = State.unset, - - const State = enum { - unset, - set, - waited, - }; - - /// This function is provided so that this type can be re-used inside - /// `std.Thread.ResetEvent`. - pub fn init(ev: *DebugEvent) void { - ev.* = .{}; - } - - /// This function is provided so that this type can be re-used inside - /// `std.Thread.ResetEvent`. - pub fn deinit(ev: *DebugEvent) void { - ev.* = undefined; - } - - pub fn set(ev: *DebugEvent) void { - switch (ev.state) { - .unset => ev.state = .set, - .set => {}, - .waited => unreachable, // Not allowed to call `set` until `reset`. - } - } - - pub fn wait(ev: *DebugEvent) void { - switch (ev.state) { - .unset => unreachable, // Deadlock detected. - .set => return, - .waited => unreachable, // Not allowed to call `wait` until `reset`. - } - } - - pub fn timedWait(ev: *DebugEvent, timeout: u64) TimedWaitResult { - _ = timeout; - switch (ev.state) { - .unset => return .timed_out, - .set => return .event_set, - .waited => unreachable, // Not allowed to call `wait` until `reset`. - } - } - - pub fn reset(ev: *DebugEvent) void { - ev.state = .unset; - } -}; - -pub const AtomicEvent = struct { - waiters: u32 = 0, - - const WAKE = 1 << 0; - const WAIT = 1 << 1; - - /// This function is provided so that this type can be re-used inside - /// `std.Thread.ResetEvent`. - pub fn init(ev: *AtomicEvent) void { - ev.* = .{}; - } - - /// This function is provided so that this type can be re-used inside - /// `std.Thread.ResetEvent`. - pub fn deinit(ev: *AtomicEvent) void { - ev.* = undefined; - } - - pub fn set(ev: *AtomicEvent) void { - const waiters = @atomicRmw(u32, &ev.waiters, .Xchg, WAKE, .Release); - if (waiters >= WAIT) { - return Futex.wake(&ev.waiters, waiters >> 1); - } - } - - pub fn wait(ev: *AtomicEvent) void { - switch (ev.timedWait(null)) { - .timed_out => unreachable, - .event_set => return, - } - } - - pub fn timedWait(ev: *AtomicEvent, timeout: ?u64) TimedWaitResult { - var waiters = @atomicLoad(u32, &ev.waiters, .Acquire); - while (waiters != WAKE) { - waiters = @cmpxchgWeak(u32, &ev.waiters, waiters, waiters + WAIT, .Acquire, .Acquire) orelse { - if (Futex.wait(&ev.waiters, timeout)) |_| { - return .event_set; - } else |_| { - return .timed_out; - } - }; - } - return .event_set; - } - - pub fn reset(ev: *AtomicEvent) void { - @atomicStore(u32, &ev.waiters, 0, .Monotonic); - } - - pub const Futex = switch (builtin.os.tag) { - .windows => WindowsFutex, - .linux => LinuxFutex, - else => SpinFutex, - }; - - pub const SpinFutex = struct { - fn wake(waiters: *u32, wake_count: u32) void { - _ = waiters; - _ = wake_count; - } - - fn wait(waiters: *u32, timeout: ?u64) !void { - var timer: time.Timer = undefined; - if (timeout != null) - timer = time.Timer.start() catch return error.TimedOut; - - while (@atomicLoad(u32, waiters, .Acquire) != WAKE) { - std.Thread.yield() catch std.atomic.spinLoopHint(); - if (timeout) |timeout_ns| { - if (timer.read() >= timeout_ns) - return error.TimedOut; - } - } - } - }; - - pub const LinuxFutex = struct { - fn wake(waiters: *u32, wake_count: u32) void { - _ = wake_count; - const waiting = std.math.maxInt(i32); // wake_count - const ptr = @ptrCast(*const i32, waiters); - const rc = linux.futex_wake(ptr, linux.FUTEX.WAKE | linux.FUTEX.PRIVATE_FLAG, waiting); - assert(linux.getErrno(rc) == .SUCCESS); - } - - fn wait(waiters: *u32, timeout: ?u64) !void { - var ts: linux.timespec = undefined; - var ts_ptr: ?*linux.timespec = null; - if (timeout) |timeout_ns| { - ts_ptr = &ts; - ts.tv_sec = @intCast(isize, timeout_ns / time.ns_per_s); - ts.tv_nsec = @intCast(isize, timeout_ns % time.ns_per_s); - } - - while (true) { - const waiting = @atomicLoad(u32, waiters, .Acquire); - if (waiting == WAKE) - return; - const expected = @intCast(i32, waiting); - const ptr = @ptrCast(*const i32, waiters); - const rc = linux.futex_wait(ptr, linux.FUTEX.WAIT | linux.FUTEX.PRIVATE_FLAG, expected, ts_ptr); - switch (linux.getErrno(rc)) { - .SUCCESS => continue, - .TIMEDOUT => return error.TimedOut, - .INTR => continue, - .AGAIN => return, - else => unreachable, - } - } - } - }; - - pub const WindowsFutex = struct { - pub fn wake(waiters: *u32, wake_count: u32) void { - const handle = getEventHandle() orelse return SpinFutex.wake(waiters, wake_count); - const key = @ptrCast(*const anyopaque, waiters); - - var waiting = wake_count; - while (waiting != 0) : (waiting -= 1) { - const rc = windows.ntdll.NtReleaseKeyedEvent(handle, key, windows.FALSE, null); - assert(rc == .SUCCESS); - } - } - - pub fn wait(waiters: *u32, timeout: ?u64) !void { - const handle = getEventHandle() orelse return SpinFutex.wait(waiters, timeout); - const key = @ptrCast(*const anyopaque, waiters); - - // NT uses timeouts in units of 100ns with negative value being relative - var timeout_ptr: ?*windows.LARGE_INTEGER = null; - var timeout_value: windows.LARGE_INTEGER = undefined; - if (timeout) |timeout_ns| { - timeout_ptr = &timeout_value; - timeout_value = -@intCast(windows.LARGE_INTEGER, timeout_ns / 100); - } - - // NtWaitForKeyedEvent doesnt have spurious wake-ups - var rc = windows.ntdll.NtWaitForKeyedEvent(handle, key, windows.FALSE, timeout_ptr); - switch (rc) { - .TIMEOUT => { - // update the wait count to signal that we're not waiting anymore. - // if the .set() thread already observed that we are, perform a - // matching NtWaitForKeyedEvent so that the .set() thread doesn't - // deadlock trying to run NtReleaseKeyedEvent above. - var waiting = @atomicLoad(u32, waiters, .Monotonic); - while (true) { - if (waiting == WAKE) { - rc = windows.ntdll.NtWaitForKeyedEvent(handle, key, windows.FALSE, null); - assert(rc == windows.NTSTATUS.WAIT_0); - break; - } else { - waiting = @cmpxchgWeak(u32, waiters, waiting, waiting - WAIT, .Acquire, .Monotonic) orelse break; - continue; - } - } - return error.TimedOut; - }, - windows.NTSTATUS.WAIT_0 => {}, - else => unreachable, - } - } - - var event_handle: usize = EMPTY; - const EMPTY = ~@as(usize, 0); - const LOADING = EMPTY - 1; - - pub fn getEventHandle() ?windows.HANDLE { - var handle = @atomicLoad(usize, &event_handle, .Monotonic); - while (true) { - switch (handle) { - EMPTY => handle = @cmpxchgWeak(usize, &event_handle, EMPTY, LOADING, .Acquire, .Monotonic) orelse { - const handle_ptr = @ptrCast(*windows.HANDLE, &handle); - const access_mask = windows.GENERIC_READ | windows.GENERIC_WRITE; - if (windows.ntdll.NtCreateKeyedEvent(handle_ptr, access_mask, null, 0) != .SUCCESS) - handle = 0; - @atomicStore(usize, &event_handle, handle, .Monotonic); - return @intToPtr(?windows.HANDLE, handle); - }, - LOADING => { - std.Thread.yield() catch std.atomic.spinLoopHint(); - handle = @atomicLoad(usize, &event_handle, .Monotonic); - }, - else => { - return @intToPtr(?windows.HANDLE, handle); - }, - } - } - } - }; -}; - -test "basic usage" { - var event = StaticResetEvent{}; - - // test event setting - event.set(); - - // test event resetting - event.reset(); - - // test event waiting (non-blocking) - event.set(); - event.wait(); - event.reset(); - - event.set(); - try testing.expectEqual(TimedWaitResult.event_set, event.timedWait(1)); - - // test cross-thread signaling - if (builtin.single_threaded) - return; - - const Context = struct { - const Self = @This(); - - value: u128 = 0, - in: StaticResetEvent = .{}, - out: StaticResetEvent = .{}, - - fn sender(self: *Self) !void { - // update value and signal input - try testing.expect(self.value == 0); - self.value = 1; - self.in.set(); - - // wait for receiver to update value and signal output - self.out.wait(); - try testing.expect(self.value == 2); - - // update value and signal final input - self.value = 3; - self.in.set(); - } - - fn receiver(self: *Self) !void { - // wait for sender to update value and signal input - self.in.wait(); - try testing.expect(self.value == 1); - - // update value and signal output - self.in.reset(); - self.value = 2; - self.out.set(); - - // wait for sender to update value and signal final input - self.in.wait(); - try testing.expect(self.value == 3); - } - - fn sleeper(self: *Self) void { - self.in.set(); - time.sleep(time.ns_per_ms * 2); - self.value = 5; - self.out.set(); - } - - fn timedWaiter(self: *Self) !void { - self.in.wait(); - try testing.expectEqual(TimedWaitResult.timed_out, self.out.timedWait(time.ns_per_us)); - try self.out.timedWait(time.ns_per_ms * 100); - try testing.expect(self.value == 5); - } - }; - - var context = Context{}; - const receiver = try std.Thread.spawn(.{}, Context.receiver, .{&context}); - defer receiver.join(); - try context.sender(); - - if (false) { - // I have now observed this fail on macOS, Windows, and Linux. - // https://github.com/ziglang/zig/issues/7009 - var timed = Context.init(); - defer timed.deinit(); - const sleeper = try std.Thread.spawn(.{}, Context.sleeper, .{&timed}); - defer sleeper.join(); - try timed.timedWaiter(); - } -} diff --git a/lib/std/atomic/Atomic.zig b/lib/std/atomic/Atomic.zig index c396281e91..7e62a9928c 100644 --- a/lib/std/atomic/Atomic.zig +++ b/lib/std/atomic/Atomic.zig @@ -14,19 +14,66 @@ pub fn Atomic(comptime T: type) type { return .{ .value = value }; } + /// Perform an atomic fence which uses the atomic value as a hint for the modification order. + /// Use this when you want to imply a fence on an atomic variable without necessarily performing a memory access. + /// + /// Example: + /// ``` + /// const RefCount = struct { + /// count: Atomic(usize), + /// dropFn: *const fn(*RefCount) void, + /// + /// fn ref(self: *RefCount) void { + /// _ = self.count.fetchAdd(1, .Monotonic); // no ordering necessary, just updating a counter + /// } + /// + /// fn unref(self: *RefCount) void { + /// // Release ensures code before unref() happens-before the count is decremented as dropFn could be called by then. + /// if (self.count.fetchSub(1, .Release)) { + /// // Acquire ensures count decrement and code before previous unrefs()s happens-before we call dropFn below. + /// // NOTE: another alterative is to use .AcqRel on the fetchSub count decrement but it's extra barrier in possibly hot path. + /// self.count.fence(.Acquire); + /// (self.dropFn)(self); + /// } + /// } + /// }; + /// ``` + pub inline fn fence(self: *Self, comptime ordering: Ordering) void { + // LLVM's ThreadSanitizer doesn't support the normal fences so we specialize for it. + if (builtin.sanitize_thread) { + const tsan = struct { + extern "c" fn __tsan_acquire(addr: *anyopaque) void; + extern "c" fn __tsan_release(addr: *anyopaque) void; + }; + + const addr = @ptrCast(*anyopaque, self); + return switch (ordering) { + .Unordered, .Monotonic => @compileError(@tagName(ordering) ++ " only applies to atomic loads and stores"), + .Acquire => tsan.__tsan_acquire(addr), + .Release => tsan.__tsan_release(addr), + .AcqRel, .SeqCst => { + tsan.__tsan_acquire(addr); + tsan.__tsan_release(addr); + }, + }; + } + + return std.atomic.fence(ordering); + } + /// Non-atomically load from the atomic value without synchronization. /// Care must be taken to avoid data-races when interacting with other atomic operations. - pub fn loadUnchecked(self: Self) T { + pub inline fn loadUnchecked(self: Self) T { return self.value; } /// Non-atomically store to the atomic value without synchronization. /// Care must be taken to avoid data-races when interacting with other atomic operations. - pub fn storeUnchecked(self: *Self, value: T) void { + pub inline fn storeUnchecked(self: *Self, value: T) void { self.value = value; } - pub fn load(self: *const Self, comptime ordering: Ordering) T { + pub inline fn load(self: *const Self, comptime ordering: Ordering) T { return switch (ordering) { .AcqRel => @compileError(@tagName(ordering) ++ " implies " ++ @tagName(Ordering.Release) ++ " which is only allowed on atomic stores"), .Release => @compileError(@tagName(ordering) ++ " is only allowed on atomic stores"), @@ -34,7 +81,7 @@ pub fn Atomic(comptime T: type) type { }; } - pub fn store(self: *Self, value: T, comptime ordering: Ordering) void { + pub inline fn store(self: *Self, value: T, comptime ordering: Ordering) void { return switch (ordering) { .AcqRel => @compileError(@tagName(ordering) ++ " implies " ++ @tagName(Ordering.Acquire) ++ " which is only allowed on atomic loads"), .Acquire => @compileError(@tagName(ordering) ++ " is only allowed on atomic loads"), @@ -189,21 +236,21 @@ pub fn Atomic(comptime T: type) type { .Set => asm volatile ("lock btsw %[bit], %[ptr]" // LLVM doesn't support u1 flag register return values : [result] "={@ccc}" (-> u8), - : [ptr] "*p" (&self.value), + : [ptr] "*m" (&self.value), [bit] "X" (@as(T, bit)), : "cc", "memory" ), .Reset => asm volatile ("lock btrw %[bit], %[ptr]" // LLVM doesn't support u1 flag register return values : [result] "={@ccc}" (-> u8), - : [ptr] "*p" (&self.value), + : [ptr] "*m" (&self.value), [bit] "X" (@as(T, bit)), : "cc", "memory" ), .Toggle => asm volatile ("lock btcw %[bit], %[ptr]" // LLVM doesn't support u1 flag register return values : [result] "={@ccc}" (-> u8), - : [ptr] "*p" (&self.value), + : [ptr] "*m" (&self.value), [bit] "X" (@as(T, bit)), : "cc", "memory" ), @@ -212,21 +259,21 @@ pub fn Atomic(comptime T: type) type { .Set => asm volatile ("lock btsl %[bit], %[ptr]" // LLVM doesn't support u1 flag register return values : [result] "={@ccc}" (-> u8), - : [ptr] "*p" (&self.value), + : [ptr] "*m" (&self.value), [bit] "X" (@as(T, bit)), : "cc", "memory" ), .Reset => asm volatile ("lock btrl %[bit], %[ptr]" // LLVM doesn't support u1 flag register return values : [result] "={@ccc}" (-> u8), - : [ptr] "*p" (&self.value), + : [ptr] "*m" (&self.value), [bit] "X" (@as(T, bit)), : "cc", "memory" ), .Toggle => asm volatile ("lock btcl %[bit], %[ptr]" // LLVM doesn't support u1 flag register return values : [result] "={@ccc}" (-> u8), - : [ptr] "*p" (&self.value), + : [ptr] "*m" (&self.value), [bit] "X" (@as(T, bit)), : "cc", "memory" ), @@ -235,21 +282,21 @@ pub fn Atomic(comptime T: type) type { .Set => asm volatile ("lock btsq %[bit], %[ptr]" // LLVM doesn't support u1 flag register return values : [result] "={@ccc}" (-> u8), - : [ptr] "*p" (&self.value), + : [ptr] "*m" (&self.value), [bit] "X" (@as(T, bit)), : "cc", "memory" ), .Reset => asm volatile ("lock btrq %[bit], %[ptr]" // LLVM doesn't support u1 flag register return values : [result] "={@ccc}" (-> u8), - : [ptr] "*p" (&self.value), + : [ptr] "*m" (&self.value), [bit] "X" (@as(T, bit)), : "cc", "memory" ), .Toggle => asm volatile ("lock btcq %[bit], %[ptr]" // LLVM doesn't support u1 flag register return values : [result] "={@ccc}" (-> u8), - : [ptr] "*p" (&self.value), + : [ptr] "*m" (&self.value), [bit] "X" (@as(T, bit)), : "cc", "memory" ), @@ -266,6 +313,13 @@ pub fn Atomic(comptime T: type) type { }; } +test "Atomic.fence" { + inline for (.{ .Acquire, .Release, .AcqRel, .SeqCst }) |ordering| { + var x = Atomic(usize).init(0); + x.fence(ordering); + } +} + fn atomicIntTypes() []const type { comptime var bytes = 1; comptime var types: []const type = &[_]type{}; diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 683219c78d..347a9c433d 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -292,7 +292,7 @@ pub fn panicExtra( /// Non-zero whenever the program triggered a panic. /// The counter is incremented/decremented atomically. -var panicking: u8 = 0; +var panicking = std.atomic.Atomic(u8).init(0); // Locked to avoid interleaving panic messages from multiple threads. var panic_mutex = std.Thread.Mutex{}; @@ -316,7 +316,7 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize 0 => { panic_stage = 1; - _ = @atomicRmw(u8, &panicking, .Add, 1, .SeqCst); + _ = panicking.fetchAdd(1, .SeqCst); // Make sure to release the mutex when done { @@ -337,13 +337,13 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize dumpCurrentStackTrace(first_trace_addr); } - if (@atomicRmw(u8, &panicking, .Sub, 1, .SeqCst) != 1) { + if (panicking.fetchSub(1, .SeqCst) != 1) { // Another thread is panicking, wait for the last one to finish // and call abort() // Sleep forever without hammering the CPU - var event: std.Thread.StaticResetEvent = .{}; - event.wait(); + var futex = std.atomic.Atomic(u32).init(0); + while (true) std.Thread.Futex.wait(&futex, 0); unreachable; } }, diff --git a/lib/std/event/loop.zig b/lib/std/event/loop.zig index 1eaa95d249..038ead12b5 100644 --- a/lib/std/event/loop.zig +++ b/lib/std/event/loop.zig @@ -8,6 +8,7 @@ const os = std.os; const windows = os.windows; const maxInt = std.math.maxInt; const Thread = std.Thread; +const Atomic = std.atomic.Atomic; const is_windows = builtin.os.tag == .windows; @@ -168,11 +169,9 @@ pub const Loop = struct { .fs_end_request = .{ .data = .{ .msg = .end, .finish = .NoAction } }, .fs_queue = std.atomic.Queue(Request).init(), .fs_thread = undefined, - .fs_thread_wakeup = undefined, + .fs_thread_wakeup = .{}, .delay_queue = undefined, }; - try self.fs_thread_wakeup.init(); - errdefer self.fs_thread_wakeup.deinit(); errdefer self.arena.deinit(); // We need at least one of these in case the fs thread wants to use onNextTick @@ -202,7 +201,6 @@ pub const Loop = struct { pub fn deinit(self: *Loop) void { self.deinitOsData(); - self.fs_thread_wakeup.deinit(); self.arena.deinit(); self.* = undefined; } @@ -723,9 +721,7 @@ pub const Loop = struct { extra_thread.join(); } - @atomicStore(bool, &self.delay_queue.is_running, false, .SeqCst); - self.delay_queue.event.set(); - self.delay_queue.thread.join(); + self.delay_queue.deinit(); } /// Runs the provided function asynchronously. The function's frame is allocated @@ -851,8 +847,8 @@ pub const Loop = struct { timer: std.time.Timer, waiters: Waiters, thread: std.Thread, - event: std.Thread.AutoResetEvent, - is_running: bool, + event: std.Thread.ResetEvent, + is_running: Atomic(bool), /// Initialize the delay queue by spawning the timer thread /// and starting any timer resources. @@ -862,11 +858,19 @@ pub const Loop = struct { .waiters = DelayQueue.Waiters{ .entries = std.atomic.Queue(anyframe).init(), }, - .event = std.Thread.AutoResetEvent{}, - .is_running = true, - // Must be last so that it can read the other state, such as `is_running`. - .thread = try std.Thread.spawn(.{}, DelayQueue.run, .{self}), + .thread = undefined, + .event = .{}, + .is_running = Atomic(bool).init(true), }; + + // Must be after init so that it can read the other state, such as `is_running`. + self.thread = try std.Thread.spawn(.{}, DelayQueue.run, .{self}); + } + + fn deinit(self: *DelayQueue) void { + self.is_running.store(false, .SeqCst); + self.event.set(); + self.thread.join(); } /// Entry point for the timer thread @@ -874,7 +878,8 @@ pub const Loop = struct { fn run(self: *DelayQueue) void { const loop = @fieldParentPtr(Loop, "delay_queue", self); - while (@atomicLoad(bool, &self.is_running, .SeqCst)) { + while (self.is_running.load(.SeqCst)) { + self.event.reset(); const now = self.timer.read(); if (self.waiters.popExpired(now)) |entry| { diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index b8e6c4d89a..f4003c1803 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -917,7 +917,7 @@ test "open file with exclusive and shared nonblocking lock" { try testing.expectError(error.WouldBlock, file2); } -test "open file with exclusive lock twice, make sure it waits" { +test "open file with exclusive lock twice, make sure second lock waits" { if (builtin.single_threaded) return error.SkipZigTest; if (std.io.is_async) { @@ -934,30 +934,33 @@ test "open file with exclusive lock twice, make sure it waits" { errdefer file.close(); const S = struct { - fn checkFn(dir: *fs.Dir, evt: *std.Thread.ResetEvent) !void { + fn checkFn(dir: *fs.Dir, started: *std.Thread.ResetEvent, locked: *std.Thread.ResetEvent) !void { + started.set(); const file1 = try dir.createFile(filename, .{ .lock = .Exclusive }); - defer file1.close(); - evt.set(); + + locked.set(); + file1.close(); } }; - var evt: std.Thread.ResetEvent = undefined; - try evt.init(); - defer evt.deinit(); + var started = std.Thread.ResetEvent{}; + var locked = std.Thread.ResetEvent{}; - const t = try std.Thread.spawn(.{}, S.checkFn, .{ &tmp.dir, &evt }); + const t = try std.Thread.spawn(.{}, S.checkFn, .{ + &tmp.dir, + &started, + &locked, + }); defer t.join(); - const SLEEP_TIMEOUT_NS = 10 * std.time.ns_per_ms; - // Make sure we've slept enough. - var timer = try std.time.Timer.start(); - while (true) { - std.time.sleep(SLEEP_TIMEOUT_NS); - if (timer.read() >= SLEEP_TIMEOUT_NS) break; - } + // Wait for the spawned thread to start trying to acquire the exclusive file lock. + // Then wait a bit to make sure that can't acquire it since we currently hold the file lock. + started.wait(); + try testing.expectError(error.Timeout, locked.timedWait(10 * std.time.ns_per_ms)); + + // Release the file lock which should unlock the thread to lock it and set the locked event. file.close(); - // No timeout to avoid failures on heavily loaded systems. - evt.wait(); + locked.wait(); } test "open file with exclusive nonblocking lock twice (absolute paths)" { diff --git a/src/Compilation.zig b/src/Compilation.zig index bfe52cd59e..b0c8f5f475 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -163,8 +163,8 @@ emit_llvm_bc: ?EmitLoc, emit_analysis: ?EmitLoc, emit_docs: ?EmitLoc, -work_queue_wait_group: WaitGroup, -astgen_wait_group: WaitGroup, +work_queue_wait_group: WaitGroup = .{}, +astgen_wait_group: WaitGroup = .{}, /// Exported symbol names. This is only for when the target is wasm. /// TODO: Remove this when Stage2 becomes the default compiler as it will already have this information. @@ -1674,19 +1674,11 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .test_evented_io = options.test_evented_io, .debug_compiler_runtime_libs = options.debug_compiler_runtime_libs, .debug_compile_errors = options.debug_compile_errors, - .work_queue_wait_group = undefined, - .astgen_wait_group = undefined, }; break :comp comp; }; errdefer comp.destroy(); - try comp.work_queue_wait_group.init(); - errdefer comp.work_queue_wait_group.deinit(); - - try comp.astgen_wait_group.init(); - errdefer comp.astgen_wait_group.deinit(); - // Add a `CObject` for each `c_source_files`. try comp.c_object_table.ensureTotalCapacity(gpa, options.c_source_files.len); for (options.c_source_files) |c_source_file| { @@ -1894,9 +1886,6 @@ pub fn destroy(self: *Compilation) void { self.cache_parent.manifest_dir.close(); if (self.owned_link_dir) |*dir| dir.close(); - self.work_queue_wait_group.deinit(); - self.astgen_wait_group.deinit(); - for (self.export_symbol_names.items) |symbol_name| { gpa.free(symbol_name); } @@ -4701,6 +4690,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca \\pub const link_libcpp = {}; \\pub const have_error_return_tracing = {}; \\pub const valgrind_support = {}; + \\pub const sanitize_thread = {}; \\pub const position_independent_code = {}; \\pub const position_independent_executable = {}; \\pub const strip_debug_info = {}; @@ -4713,6 +4703,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca comp.bin_file.options.link_libcpp, comp.bin_file.options.error_return_tracing, comp.bin_file.options.valgrind, + comp.bin_file.options.tsan, comp.bin_file.options.pic, comp.bin_file.options.pie, comp.bin_file.options.strip, diff --git a/src/ThreadPool.zig b/src/ThreadPool.zig index 36d004cfc6..7d1c8420af 100644 --- a/src/ThreadPool.zig +++ b/src/ThreadPool.zig @@ -3,13 +3,12 @@ const builtin = @import("builtin"); const ThreadPool = @This(); mutex: std.Thread.Mutex = .{}, +cond: std.Thread.Condition = .{}, +run_queue: RunQueue = .{}, is_running: bool = true, allocator: std.mem.Allocator, -workers: []Worker, -run_queue: RunQueue = .{}, -idle_queue: IdleQueue = .{}, +threads: []std.Thread, -const IdleQueue = std.SinglyLinkedList(std.Thread.ResetEvent); const RunQueue = std.SinglyLinkedList(Runnable); const Runnable = struct { runFn: RunProto, @@ -20,89 +19,52 @@ const RunProto = switch (builtin.zig_backend) { else => *const fn (*Runnable) void, }; -const Worker = struct { - pool: *ThreadPool, - thread: std.Thread, - /// The node is for this worker only and must have an already initialized event - /// when the thread is spawned. - idle_node: IdleQueue.Node, - - fn run(worker: *Worker) void { - const pool = worker.pool; - - while (true) { - pool.mutex.lock(); - - if (pool.run_queue.popFirst()) |run_node| { - pool.mutex.unlock(); - (run_node.data.runFn)(&run_node.data); - continue; - } - - if (pool.is_running) { - worker.idle_node.data.reset(); - - pool.idle_queue.prepend(&worker.idle_node); - pool.mutex.unlock(); - - worker.idle_node.data.wait(); - continue; - } - - pool.mutex.unlock(); - return; - } - } -}; - pub fn init(self: *ThreadPool, allocator: std.mem.Allocator) !void { self.* = .{ .allocator = allocator, - .workers = &[_]Worker{}, + .threads = &[_]std.Thread{}, }; - if (builtin.single_threaded) + + if (builtin.single_threaded) { return; - - const worker_count = std.math.max(1, std.Thread.getCpuCount() catch 1); - self.workers = try allocator.alloc(Worker, worker_count); - errdefer allocator.free(self.workers); - - var worker_index: usize = 0; - errdefer self.destroyWorkers(worker_index); - while (worker_index < worker_count) : (worker_index += 1) { - const worker = &self.workers[worker_index]; - worker.pool = self; - - // Each worker requires its ResetEvent to be pre-initialized. - try worker.idle_node.data.init(); - errdefer worker.idle_node.data.deinit(); - - worker.thread = try std.Thread.spawn(.{}, Worker.run, .{worker}); } -} -fn destroyWorkers(self: *ThreadPool, spawned: usize) void { - if (builtin.single_threaded) - return; + const thread_count = std.math.max(1, std.Thread.getCpuCount() catch 1); + self.threads = try allocator.alloc(std.Thread, thread_count); + errdefer allocator.free(self.threads); - for (self.workers[0..spawned]) |*worker| { - worker.thread.join(); - worker.idle_node.data.deinit(); + // kill and join any threads we spawned previously on error. + var spawned: usize = 0; + errdefer self.join(spawned); + + for (self.threads) |*thread| { + thread.* = try std.Thread.spawn(.{}, worker, .{self}); + spawned += 1; } } pub fn deinit(self: *ThreadPool) void { + self.join(self.threads.len); // kill and join all threads. + self.* = undefined; +} + +fn join(self: *ThreadPool, spawned: usize) void { { self.mutex.lock(); defer self.mutex.unlock(); + // ensure future worker threads exit the dequeue loop self.is_running = false; - while (self.idle_queue.popFirst()) |idle_node| - idle_node.data.set(); } - self.destroyWorkers(self.workers.len); - self.allocator.free(self.workers); + // wake up any sleeping threads (this can be done outside the mutex) + // then wait for all the threads we know are spawned to complete. + self.cond.broadcast(); + for (self.threads[0..spawned]) |thread| { + thread.join(); + } + + self.allocator.free(self.threads); } pub fn spawn(self: *ThreadPool, comptime func: anytype, args: anytype) !void { @@ -122,24 +84,51 @@ pub fn spawn(self: *ThreadPool, comptime func: anytype, args: anytype) !void { const closure = @fieldParentPtr(@This(), "run_node", run_node); @call(.{}, func, closure.arguments); + // The thread pool's allocator is protected by the mutex. const mutex = &closure.pool.mutex; mutex.lock(); defer mutex.unlock(); + closure.pool.allocator.destroy(closure); } }; + { + self.mutex.lock(); + defer self.mutex.unlock(); + + const closure = try self.allocator.create(Closure); + closure.* = .{ + .arguments = args, + .pool = self, + }; + + self.run_queue.prepend(&closure.run_node); + } + + // Notify waiting threads outside the lock to try and keep the critical section small. + self.cond.signal(); +} + +fn worker(self: *ThreadPool) void { self.mutex.lock(); defer self.mutex.unlock(); - const closure = try self.allocator.create(Closure); - closure.* = .{ - .arguments = args, - .pool = self, - }; + while (true) { + while (self.run_queue.popFirst()) |run_node| { + // Temporarily unlock the mutex in order to execute the run_node + self.mutex.unlock(); + defer self.mutex.lock(); - self.run_queue.prepend(&closure.run_node); + const runFn = run_node.data.runFn; + runFn(&run_node.data); + } - if (self.idle_queue.popFirst()) |idle_node| - idle_node.data.set(); + // Stop executing instead of waiting if the thread pool is no longer running. + if (self.is_running) { + self.cond.wait(&self.mutex); + } else { + break; + } + } } diff --git a/src/WaitGroup.zig b/src/WaitGroup.zig index e4126b1ab3..860d0a8b4c 100644 --- a/src/WaitGroup.zig +++ b/src/WaitGroup.zig @@ -1,56 +1,39 @@ const std = @import("std"); +const Atomic = std.atomic.Atomic; +const assert = std.debug.assert; const WaitGroup = @This(); -mutex: std.Thread.Mutex = .{}, -counter: usize = 0, -event: std.Thread.ResetEvent, +const is_waiting: usize = 1 << 0; +const one_pending: usize = 1 << 1; -pub fn init(self: *WaitGroup) !void { - self.* = .{ - .mutex = .{}, - .counter = 0, - .event = undefined, - }; - try self.event.init(); -} - -pub fn deinit(self: *WaitGroup) void { - self.event.deinit(); - self.* = undefined; -} +state: Atomic(usize) = Atomic(usize).init(0), +event: std.Thread.ResetEvent = .{}, pub fn start(self: *WaitGroup) void { - self.mutex.lock(); - defer self.mutex.unlock(); - - self.counter += 1; + const state = self.state.fetchAdd(one_pending, .Monotonic); + assert((state / one_pending) < (std.math.maxInt(usize) / one_pending)); } pub fn finish(self: *WaitGroup) void { - self.mutex.lock(); - defer self.mutex.unlock(); + const state = self.state.fetchSub(one_pending, .Release); + assert((state / one_pending) > 0); - self.counter -= 1; - - if (self.counter == 0) { + if (state == (one_pending | is_waiting)) { + self.state.fence(.Acquire); self.event.set(); } } pub fn wait(self: *WaitGroup) void { - while (true) { - self.mutex.lock(); + var state = self.state.fetchAdd(is_waiting, .Acquire); + assert(state & is_waiting == 0); - if (self.counter == 0) { - self.mutex.unlock(); - return; - } - - self.mutex.unlock(); + if ((state / one_pending) > 0) { self.event.wait(); } } pub fn reset(self: *WaitGroup) void { + self.state.store(0, .Monotonic); self.event.reset(); } diff --git a/src/crash_report.zig b/src/crash_report.zig index e9d4022bba..d38f436aa0 100644 --- a/src/crash_report.zig +++ b/src/crash_report.zig @@ -362,7 +362,7 @@ const PanicSwitch = struct { /// Updated atomically before taking the panic_mutex. /// In recoverable cases, the program will not abort /// until all panicking threads have dumped their traces. - var panicking: u8 = 0; + var panicking = std.atomic.Atomic(u8).init(0); // Locked to avoid interleaving panic messages from multiple threads. var panic_mutex = std.Thread.Mutex{}; @@ -430,7 +430,7 @@ const PanicSwitch = struct { }; state.* = new_state; - _ = @atomicRmw(u8, &panicking, .Add, 1, .SeqCst); + _ = panicking.fetchAdd(1, .SeqCst); state.recover_stage = .release_ref_count; @@ -512,13 +512,14 @@ const PanicSwitch = struct { noinline fn releaseRefCount(state: *volatile PanicState) noreturn { state.recover_stage = .abort; - if (@atomicRmw(u8, &panicking, .Sub, 1, .SeqCst) != 1) { + if (panicking.fetchSub(1, .SeqCst) != 1) { // Another thread is panicking, wait for the last one to finish // and call abort() // Sleep forever without hammering the CPU - var event: std.Thread.StaticResetEvent = .{}; - event.wait(); + var futex = std.atomic.Atomic(u32).init(0); + while (true) std.Thread.Futex.wait(&futex, 0); + // This should be unreachable, recurse into recoverAbort. @panic("event.wait() returned"); } diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 3138e93c4c..a2efed6bde 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -9993,6 +9993,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { buf_appendf(contents, "pub const link_libcpp = %s;\n", bool_to_str(g->link_libcpp)); buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing)); buf_appendf(contents, "pub const valgrind_support = false;\n"); + buf_appendf(contents, "pub const sanitize_thread = false;\n"); buf_appendf(contents, "pub const position_independent_code = %s;\n", bool_to_str(g->have_pic)); buf_appendf(contents, "pub const position_independent_executable = %s;\n", bool_to_str(g->have_pie)); buf_appendf(contents, "pub const strip_debug_info = %s;\n", bool_to_str(g->strip_debug_symbols)); From cea310c9082a985b103953dd84579aa1a6dce6ea Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Mon, 25 Apr 2022 19:08:39 +0200 Subject: [PATCH 1242/2031] Remove usage of inline for from print_targets.cmdTargets This function was one of the biggest zig functions in a debug build of the compiler. $ bloaty stage3-debug/bin/zig -d symbols --tsv -n 10000000 | rg -v '(llvm|clang|std|lld|\(anonymous namespace\))::|\[section ' | sort -h -k 3 ... translate_c.ast.renderNode 86168 86219 main.buildOutputType 177959 178004 InfoTable 184832 184870 AArch64SVEIntrinsicMap 188544 188596 print_targets.cmdTargets__anon_4735 319156 319216 __static_initialization_and_destruction_0() 486666 489582 MatchTable1 621884 621997 OperandMatchTable 1139622 1139861 MatchTable0 1899764 1900141 --- lib/std/meta.zig | 27 +++++++++++++++++++++++++++ src/print_targets.zig | 27 ++++++++++++--------------- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 33f690f7aa..9940fafba0 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -568,6 +568,33 @@ test "std.meta.fieldNames" { try testing.expectEqualSlices(u8, u1names[1], "b"); } +/// Given an enum or error set type, returns a pointer to an array containing all tags for that +/// enum or error set. +pub fn tags(comptime T: type) *const [fields(T).len]T { + comptime { + const fieldInfos = fields(T); + var res: [fieldInfos.len]T = undefined; + for (fieldInfos) |field, i| { + res[i] = @field(T, field.name); + } + return &res; + } +} + +test "std.meta.tags" { + const E1 = enum { A, B }; + const E2 = error{A}; + + const e1_tags = tags(E1); + const e2_tags = tags(E2); + + try testing.expect(e1_tags.len == 2); + try testing.expectEqual(E1.A, e1_tags[0]); + try testing.expectEqual(E1.B, e1_tags[1]); + try testing.expect(e2_tags.len == 1); + try testing.expectEqual(E2.A, e2_tags[0]); +} + pub fn FieldEnum(comptime T: type) type { const field_infos = fields(T); var enumFields: [field_infos.len]std.builtin.Type.EnumField = undefined; diff --git a/src/print_targets.zig b/src/print_targets.zig index 07335c4668..02f894d401 100644 --- a/src/print_targets.zig +++ b/src/print_targets.zig @@ -2,6 +2,7 @@ const std = @import("std"); const fs = std.fs; const io = std.io; const mem = std.mem; +const meta = std.meta; const Allocator = mem.Allocator; const Target = std.Target; const target = @import("target.zig"); @@ -35,27 +36,25 @@ pub fn cmdTargets( try jws.objectField("arch"); try jws.beginArray(); - { - inline for (@typeInfo(Target.Cpu.Arch).Enum.fields) |field| { - try jws.arrayElem(); - try jws.emitString(field.name); - } + for (meta.fieldNames(Target.Cpu.Arch)) |field| { + try jws.arrayElem(); + try jws.emitString(field); } try jws.endArray(); try jws.objectField("os"); try jws.beginArray(); - inline for (@typeInfo(Target.Os.Tag).Enum.fields) |field| { + for (meta.fieldNames(Target.Os.Tag)) |field| { try jws.arrayElem(); - try jws.emitString(field.name); + try jws.emitString(field); } try jws.endArray(); try jws.objectField("abi"); try jws.beginArray(); - inline for (@typeInfo(Target.Abi).Enum.fields) |field| { + for (meta.fieldNames(Target.Abi)) |field| { try jws.arrayElem(); - try jws.emitString(field.name); + try jws.emitString(field); } try jws.endArray(); @@ -84,10 +83,9 @@ pub fn cmdTargets( try jws.objectField("cpus"); try jws.beginObject(); - inline for (@typeInfo(Target.Cpu.Arch).Enum.fields) |field| { - try jws.objectField(field.name); + for (meta.tags(Target.Cpu.Arch)) |arch| { + try jws.objectField(@tagName(arch)); try jws.beginObject(); - const arch = @field(Target.Cpu.Arch, field.name); for (arch.allCpuModels()) |model| { try jws.objectField(model.name); try jws.beginArray(); @@ -105,10 +103,9 @@ pub fn cmdTargets( try jws.objectField("cpuFeatures"); try jws.beginObject(); - inline for (@typeInfo(Target.Cpu.Arch).Enum.fields) |field| { - try jws.objectField(field.name); + for (meta.tags(Target.Cpu.Arch)) |arch| { + try jws.objectField(@tagName(arch)); try jws.beginArray(); - const arch = @field(Target.Cpu.Arch, field.name); for (arch.allFeaturesList()) |feature| { try jws.arrayElem(); try jws.emitString(feature.name); From 6c0719fd5f29f524b789d5038fdc9fc0878f2e6a Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Tue, 26 Apr 2022 17:00:52 +0200 Subject: [PATCH 1243/2031] std.meta: deprecate bitCount() for @bitSizeOf() --- lib/std/meta.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 9940fafba0..e6ce746f19 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -97,6 +97,8 @@ test "std.meta.stringToEnum" { try testing.expect(null == stringToEnum(E1, "C")); } +/// Deprecated, use `@bitSizeOf()`. +/// TODO Remove this after zig 0.10.0 is released. pub fn bitCount(comptime T: type) comptime_int { return switch (@typeInfo(T)) { .Bool => 1, From 6f4343b61afe36a709e713735947561a2b76bce8 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Tue, 26 Apr 2022 17:08:54 +0200 Subject: [PATCH 1244/2031] std: replace usage of std.meta.bitCount() with @bitSizeOf() --- lib/std/Thread/RwLock.zig | 4 ++-- lib/std/atomic/Atomic.zig | 6 +++--- lib/std/crypto/pcurves/common.zig | 2 +- lib/std/crypto/scrypt.zig | 2 +- lib/std/event/wait_group.zig | 2 +- lib/std/fmt.zig | 2 +- lib/std/heap.zig | 2 +- lib/std/io/bit_reader.zig | 8 ++++---- lib/std/io/bit_writer.zig | 6 +++--- lib/std/math.zig | 4 ++-- lib/std/os.zig | 2 +- src/codegen/spirv.zig | 2 +- src/codegen/spirv/Section.zig | 2 +- 13 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/std/Thread/RwLock.zig b/lib/std/Thread/RwLock.zig index 6cce7d1217..201e1cc860 100644 --- a/lib/std/Thread/RwLock.zig +++ b/lib/std/Thread/RwLock.zig @@ -167,10 +167,10 @@ pub const DefaultRwLock = struct { const IS_WRITING: usize = 1; const WRITER: usize = 1 << 1; - const READER: usize = 1 << (1 + std.meta.bitCount(Count)); + const READER: usize = 1 << (1 + @bitSizeOf(Count)); const WRITER_MASK: usize = std.math.maxInt(Count) << @ctz(usize, WRITER); const READER_MASK: usize = std.math.maxInt(Count) << @ctz(usize, READER); - const Count = std.meta.Int(.unsigned, @divFloor(std.meta.bitCount(usize) - 1, 2)); + const Count = std.meta.Int(.unsigned, @divFloor(@bitSizeOf(usize) - 1, 2)); pub fn tryLock(rwl: *DefaultRwLock) bool { if (rwl.mutex.tryLock()) { diff --git a/lib/std/atomic/Atomic.zig b/lib/std/atomic/Atomic.zig index 7e62a9928c..57866d21d6 100644 --- a/lib/std/atomic/Atomic.zig +++ b/lib/std/atomic/Atomic.zig @@ -542,7 +542,7 @@ test "Atomic.bitSet" { inline for (atomicIntTypes()) |Int| { inline for (atomic_rmw_orderings) |ordering| { var x = Atomic(Int).init(0); - const bit_array = @as([std.meta.bitCount(Int)]void, undefined); + const bit_array = @as([@bitSizeOf(Int)]void, undefined); for (bit_array) |_, bit_index| { const bit = @intCast(std.math.Log2Int(Int), bit_index); @@ -572,7 +572,7 @@ test "Atomic.bitReset" { inline for (atomicIntTypes()) |Int| { inline for (atomic_rmw_orderings) |ordering| { var x = Atomic(Int).init(0); - const bit_array = @as([std.meta.bitCount(Int)]void, undefined); + const bit_array = @as([@bitSizeOf(Int)]void, undefined); for (bit_array) |_, bit_index| { const bit = @intCast(std.math.Log2Int(Int), bit_index); @@ -603,7 +603,7 @@ test "Atomic.bitToggle" { inline for (atomicIntTypes()) |Int| { inline for (atomic_rmw_orderings) |ordering| { var x = Atomic(Int).init(0); - const bit_array = @as([std.meta.bitCount(Int)]void, undefined); + const bit_array = @as([@bitSizeOf(Int)]void, undefined); for (bit_array) |_, bit_index| { const bit = @intCast(std.math.Log2Int(Int), bit_index); diff --git a/lib/std/crypto/pcurves/common.zig b/lib/std/crypto/pcurves/common.zig index 917fd70fbd..5eb4e48623 100644 --- a/lib/std/crypto/pcurves/common.zig +++ b/lib/std/crypto/pcurves/common.zig @@ -235,7 +235,7 @@ pub fn Field(comptime params: FieldParams) type { } var v_opp: Limbs = undefined; fiat.opp(&v_opp, v); - fiat.selectznz(&v, @truncate(u1, f[f.len - 1] >> (meta.bitCount(Word) - 1)), v, v_opp); + fiat.selectznz(&v, @truncate(u1, f[f.len - 1] >> (@bitSizeOf(Word) - 1)), v, v_opp); var fe: Fe = undefined; fiat.mul(&fe.limbs, v, precomp); return fe; diff --git a/lib/std/crypto/scrypt.zig b/lib/std/crypto/scrypt.zig index a9506d3d23..31c8b754ff 100644 --- a/lib/std/crypto/scrypt.zig +++ b/lib/std/crypto/scrypt.zig @@ -348,7 +348,7 @@ const crypt_format = struct { } } - fn intDecode(comptime T: type, src: *const [(meta.bitCount(T) + 5) / 6]u8) !T { + fn intDecode(comptime T: type, src: *const [(@bitSizeOf(T) + 5) / 6]u8) !T { var v: T = 0; for (src) |x, i| { const vi = mem.indexOfScalar(u8, &map64, x) orelse return EncodingError.InvalidEncoding; diff --git a/lib/std/event/wait_group.zig b/lib/std/event/wait_group.zig index 4cc82cf98e..c88b01c812 100644 --- a/lib/std/event/wait_group.zig +++ b/lib/std/event/wait_group.zig @@ -14,7 +14,7 @@ const Loop = std.event.Loop; /// `begin` will return error.Overflow when the limit is reached, even /// if the integer type has not has not overflowed. /// By default `max_value` is set to std.math.maxInt(CounterType). -pub const WaitGroup = WaitGroupGeneric(std.meta.bitCount(usize)); +pub const WaitGroup = WaitGroupGeneric(@bitSizeOf(usize)); pub fn WaitGroupGeneric(comptime counter_size: u16) type { const CounterType = std.meta.Int(.unsigned, counter_size); diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 994350ffa7..6fa8cfdfa3 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -1121,7 +1121,7 @@ pub fn formatFloatHexadecimal( } const T = @TypeOf(value); - const TU = std.meta.Int(.unsigned, std.meta.bitCount(T)); + const TU = std.meta.Int(.unsigned, @bitSizeOf(T)); const mantissa_bits = math.floatMantissaBits(T); const fractional_bits = math.floatFractionalBits(T); diff --git a/lib/std/heap.zig b/lib/std/heap.zig index deb866fd61..aaf87367d8 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -1181,7 +1181,7 @@ pub fn testAllocatorLargeAlignment(base_allocator: mem.Allocator) !void { // very near usize? if (mem.page_size << 2 > maxInt(usize)) return; - const USizeShift = std.meta.Int(.unsigned, std.math.log2(std.meta.bitCount(usize))); + const USizeShift = std.meta.Int(.unsigned, std.math.log2(@bitSizeOf(usize))); const large_align = @as(u29, mem.page_size << 2); var align_mask: usize = undefined; diff --git a/lib/std/io/bit_reader.zig b/lib/std/io/bit_reader.zig index 83b6c03876..aebb189942 100644 --- a/lib/std/io/bit_reader.zig +++ b/lib/std/io/bit_reader.zig @@ -17,9 +17,9 @@ pub fn BitReader(endian: std.builtin.Endian, comptime ReaderType: type) type { pub const Reader = io.Reader(*Self, Error, read); const Self = @This(); - const u8_bit_count = meta.bitCount(u8); - const u7_bit_count = meta.bitCount(u7); - const u4_bit_count = meta.bitCount(u4); + const u8_bit_count = @bitSizeOf(u8); + const u7_bit_count = @bitSizeOf(u7); + const u4_bit_count = @bitSizeOf(u4); pub fn init(forward_reader: ReaderType) Self { return Self{ @@ -47,7 +47,7 @@ pub fn BitReader(endian: std.builtin.Endian, comptime ReaderType: type) type { //by extending the buffer to a minimum of u8 we can cover a number of edge cases // related to shifting and casting. - const u_bit_count = comptime meta.bitCount(U); + const u_bit_count = @bitSizeOf(U); const buf_bit_count = bc: { assert(u_bit_count >= bits); break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count; diff --git a/lib/std/io/bit_writer.zig b/lib/std/io/bit_writer.zig index ea8f984f0e..d71afe5fe5 100644 --- a/lib/std/io/bit_writer.zig +++ b/lib/std/io/bit_writer.zig @@ -17,8 +17,8 @@ pub fn BitWriter(endian: std.builtin.Endian, comptime WriterType: type) type { pub const Writer = io.Writer(*Self, Error, write); const Self = @This(); - const u8_bit_count = meta.bitCount(u8); - const u4_bit_count = meta.bitCount(u4); + const u8_bit_count = @bitSizeOf(u8); + const u4_bit_count = @bitSizeOf(u4); pub fn init(forward_writer: WriterType) Self { return Self{ @@ -39,7 +39,7 @@ pub fn BitWriter(endian: std.builtin.Endian, comptime WriterType: type) type { //by extending the buffer to a minimum of u8 we can cover a number of edge cases // related to shifting and casting. - const u_bit_count = comptime meta.bitCount(U); + const u_bit_count = @bitSizeOf(U); const buf_bit_count = bc: { assert(u_bit_count >= bits); break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count; diff --git a/lib/std/math.zig b/lib/std/math.zig index 387faf4c97..b229c8973e 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -947,10 +947,10 @@ test "absCast" { /// Returns the negation of the integer parameter. /// Result is a signed integer. -pub fn negateCast(x: anytype) !std.meta.Int(.signed, std.meta.bitCount(@TypeOf(x))) { +pub fn negateCast(x: anytype) !std.meta.Int(.signed, @bitSizeOf(@TypeOf(x))) { if (@typeInfo(@TypeOf(x)).Int.signedness == .signed) return negate(x); - const int = std.meta.Int(.signed, std.meta.bitCount(@TypeOf(x))); + const int = std.meta.Int(.signed, @bitSizeOf(@TypeOf(x))); if (x > -minInt(int)) return error.Overflow; if (x == -minInt(int)) return minInt(int); diff --git a/lib/std/os.zig b/lib/std/os.zig index f9ee65a317..03f00a5d30 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -5561,7 +5561,7 @@ pub fn res_mkquery( // Make a reasonably unpredictable id var ts: timespec = undefined; clock_gettime(CLOCK.REALTIME, &ts) catch {}; - const UInt = std.meta.Int(.unsigned, std.meta.bitCount(@TypeOf(ts.tv_nsec))); + const UInt = std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(ts.tv_nsec))); const unsec = @bitCast(UInt, ts.tv_nsec); const id = @truncate(u32, unsec + unsec / 65536); q[0] = @truncate(u8, id / 256); diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 791c2dc187..0dc39db134 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -341,7 +341,7 @@ pub const DeclGen = struct { // We can just use toSignedInt/toUnsignedInt here as it returns u64 - a type large enough to hold any // SPIR-V native type (up to i/u64 with Int64). If SPIR-V ever supports native ints of a larger size, this // might need to be updated. - assert(self.largestSupportedIntBits() <= std.meta.bitCount(u64)); + assert(self.largestSupportedIntBits() <= @bitSizeOf(u64)); // Note, value is required to be sign-extended, so we don't need to mask off the upper bits. // See https://www.khronos.org/registry/SPIR-V/specs/unified1/SPIRV.html#Literal diff --git a/src/codegen/spirv/Section.zig b/src/codegen/spirv/Section.zig index 83383e442a..6ed2de92f0 100644 --- a/src/codegen/spirv/Section.zig +++ b/src/codegen/spirv/Section.zig @@ -170,7 +170,7 @@ fn writeString(section: *Section, str: []const u8) void { var j: usize = 0; while (j < @sizeOf(Word) and i + j < str.len) : (j += 1) { - word |= @as(Word, str[i + j]) << @intCast(Log2Word, j * std.meta.bitCount(u8)); + word |= @as(Word, str[i + j]) << @intCast(Log2Word, j * @bitSizeOf(u8)); } section.instructions.appendAssumeCapacity(word); From 41dd2beaacade94c5c98400a4a655aea07b9e2f3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Apr 2022 10:13:55 -0700 Subject: [PATCH 1245/2031] compiler-rt: math functions reorg * unify the logic for exporting math functions from compiler-rt, with the appropriate suffixes and prefixes. - add all missing f128 and f80 exports. Functions with missing implementations call other functions and have TODO comments. - also add f16 functions * move math functions from freestanding libc to compiler-rt (#7265) * enable all the f128 and f80 code in the stage2 compiler and behavior tests (#11161). * update std lib to use builtins rather than `std.math`. --- CMakeLists.txt | 34 +- lib/std/fmt/errol.zig | 8 +- lib/std/math.zig | 31 +- lib/std/math/acos.zig | 8 +- lib/std/math/acosh.zig | 12 +- lib/std/math/asin.zig | 8 +- lib/std/math/asinh.zig | 12 +- lib/std/math/atan.zig | 4 +- lib/std/math/atan2.zig | 4 +- lib/std/math/complex.zig | 2 +- lib/std/math/complex/atan.zig | 4 +- lib/std/math/complex/cosh.zig | 8 +- lib/std/math/complex/exp.zig | 12 +- lib/std/math/complex/ldexp.zig | 4 +- lib/std/math/complex/log.zig | 2 +- lib/std/math/complex/sinh.zig | 8 +- lib/std/math/complex/sqrt.zig | 18 +- lib/std/math/complex/tanh.zig | 8 +- lib/std/math/cosh.zig | 4 +- lib/std/math/expo2.zig | 4 +- lib/std/math/fabs.zig | 45 -- lib/std/math/hypot.zig | 4 +- lib/std/math/ln.zig | 171 +---- lib/std/math/log.zig | 12 +- lib/std/math/log10.zig | 196 +----- lib/std/math/log2.zig | 184 +----- lib/std/math/nan.zig | 28 +- lib/std/math/pow.zig | 12 +- lib/std/math/round.zig | 185 ------ lib/std/math/trunc.zig | 141 ----- lib/std/rand/ziggurat.zig | 16 +- lib/std/special/c.zig | 591 ------------------ lib/std/special/compiler_rt.zig | 141 ++--- .../{math => special/compiler_rt}/ceil.zig | 100 ++- lib/std/{math => special/compiler_rt}/cos.zig | 96 ++- lib/std/special/compiler_rt/divxf3_test.zig | 6 +- lib/std/{math => special/compiler_rt}/exp.zig | 68 +- .../{math => special/compiler_rt}/exp2.zig | 250 ++++---- lib/std/special/compiler_rt/fabs.zig | 29 + .../{math => special/compiler_rt}/floor.zig | 125 ++-- lib/std/{math => special/compiler_rt}/fma.zig | 186 +++--- lib/std/special/compiler_rt/fmax.zig | 43 ++ lib/std/special/compiler_rt/fmin.zig | 43 ++ lib/std/special/compiler_rt/fmod.zig | 351 +++++++++++ lib/std/special/compiler_rt/fmodq.zig | 126 ---- lib/std/special/compiler_rt/fmodq_test.zig | 20 +- lib/std/special/compiler_rt/fmodx.zig | 108 ---- lib/std/special/compiler_rt/fmodx_test.zig | 20 +- lib/std/special/compiler_rt/log.zig | 168 +++++ lib/std/special/compiler_rt/log10.zig | 196 ++++++ lib/std/special/compiler_rt/log2.zig | 185 ++++++ .../compiler_rt/rem_pio2.zig} | 10 +- .../compiler_rt/rem_pio2_large.zig} | 232 ++++--- .../compiler_rt/rem_pio2f.zig} | 10 +- lib/std/special/compiler_rt/round.zig | 169 +++++ lib/std/{math => special/compiler_rt}/sin.zig | 114 ++-- lib/std/special/compiler_rt/sincos.zig | 24 + lib/std/special/compiler_rt/sqrt.zig | 284 +++++++++ lib/std/{math => special/compiler_rt}/tan.zig | 100 +-- .../compiler_rt/trig.zig} | 188 +++--- lib/std/special/compiler_rt/trunc.zig | 124 ++++ lib/std/testing.zig | 2 +- src/Sema.zig | 4 +- src/translate_c.zig | 2 +- src/value.zig | 112 +--- test/behavior/math.zig | 70 ++- 66 files changed, 2617 insertions(+), 2869 deletions(-) delete mode 100644 lib/std/math/fabs.zig delete mode 100644 lib/std/math/round.zig delete mode 100644 lib/std/math/trunc.zig rename lib/std/{math => special/compiler_rt}/ceil.zig (52%) rename lib/std/{math => special/compiler_rt}/cos.zig (52%) rename lib/std/{math => special/compiler_rt}/exp.zig (74%) rename lib/std/{math => special/compiler_rt}/exp2.zig (89%) create mode 100644 lib/std/special/compiler_rt/fabs.zig rename lib/std/{math => special/compiler_rt}/floor.zig (52%) rename lib/std/{math => special/compiler_rt}/fma.zig (76%) create mode 100644 lib/std/special/compiler_rt/fmax.zig create mode 100644 lib/std/special/compiler_rt/fmin.zig create mode 100644 lib/std/special/compiler_rt/fmod.zig delete mode 100644 lib/std/special/compiler_rt/fmodq.zig delete mode 100644 lib/std/special/compiler_rt/fmodx.zig create mode 100644 lib/std/special/compiler_rt/log.zig create mode 100644 lib/std/special/compiler_rt/log10.zig create mode 100644 lib/std/special/compiler_rt/log2.zig rename lib/std/{math/__rem_pio2.zig => special/compiler_rt/rem_pio2.zig} (95%) rename lib/std/{math/__rem_pio2_large.zig => special/compiler_rt/rem_pio2_large.zig} (75%) rename lib/std/{math/__rem_pio2f.zig => special/compiler_rt/rem_pio2f.zig} (88%) create mode 100644 lib/std/special/compiler_rt/round.zig rename lib/std/{math => special/compiler_rt}/sin.zig (56%) create mode 100644 lib/std/special/compiler_rt/sincos.zig create mode 100644 lib/std/special/compiler_rt/sqrt.zig rename lib/std/{math => special/compiler_rt}/tan.zig (53%) rename lib/std/{math/__trig.zig => special/compiler_rt/trig.zig} (60%) create mode 100644 lib/std/special/compiler_rt/trunc.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 463718b31c..aad902f953 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -445,7 +445,6 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/math/big.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/big/int.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/float.zig" - "${CMAKE_SOURCE_DIR}/lib/std/math/floor.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/frexp.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/isinf.zig" "${CMAKE_SOURCE_DIR}/lib/std/math/isnan.zig" @@ -482,20 +481,40 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/absv.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/addXf3.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/addo.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/arm.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/atomics.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/aulldiv.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/aullrem.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/bswap.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/ceil.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/clear_cache.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/cmp.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/compareXf2.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/cos.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/count0bits.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divdf3.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divsf3.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divtf3.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divti3.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divxf3.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/emutls.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/exp.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/exp2.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/extendXfYf2.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/extend_f80.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fabs.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixXfYi.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatXiYf.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floor.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fma.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fmax.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fmin.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fmod.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/int.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/log.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/log10.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/log2.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/modti3.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/mulXf3.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/muldi3.zig" @@ -507,9 +526,22 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/os_version_check.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/parity.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/popcount.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/rem_pio2.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/rem_pio2_large.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/rem_pio2f.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/round.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/shift.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/sin.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/sincos.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/sparc.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/sqrt.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/stack_probe.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/subo.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/tan.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/trig.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/trunc.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/truncXfYf2.zig" + "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/trunc_f80.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivmod.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivmodti4.zig" "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivti3.zig" diff --git a/lib/std/fmt/errol.zig b/lib/std/fmt/errol.zig index 29dd2b7a63..1ce72de0fc 100644 --- a/lib/std/fmt/errol.zig +++ b/lib/std/fmt/errol.zig @@ -113,7 +113,7 @@ fn errolSlow(val: f64, buffer: []u8) FloatDecimal { // normalize the midpoint const e = math.frexp(val).exponent; - var exp = @floatToInt(i16, math.floor(307 + @intToFloat(f64, e) * 0.30103)); + var exp = @floatToInt(i16, @floor(307 + @intToFloat(f64, e) * 0.30103)); if (exp < 20) { exp = 20; } else if (@intCast(usize, exp) >= lookup_table.len) { @@ -170,10 +170,10 @@ fn errolSlow(val: f64, buffer: []u8) FloatDecimal { // digit generation var buf_index: usize = 0; while (true) { - var hdig = @floatToInt(u8, math.floor(high.val)); + var hdig = @floatToInt(u8, @floor(high.val)); if ((high.val == @intToFloat(f64, hdig)) and (high.off < 0)) hdig -= 1; - var ldig = @floatToInt(u8, math.floor(low.val)); + var ldig = @floatToInt(u8, @floor(low.val)); if ((low.val == @intToFloat(f64, ldig)) and (low.off < 0)) ldig -= 1; if (ldig != hdig) break; @@ -187,7 +187,7 @@ fn errolSlow(val: f64, buffer: []u8) FloatDecimal { } const tmp = (high.val + low.val) / 2.0; - var mdig = @floatToInt(u8, math.floor(tmp + 0.5)); + var mdig = @floatToInt(u8, @floor(tmp + 0.5)); if ((@intToFloat(f64, mdig) - tmp) == 0.5 and (mdig & 0x1) != 0) mdig -= 1; buffer[buf_index] = mdig + '0'; diff --git a/lib/std/math.zig b/lib/std/math.zig index b229c8973e..214ade39ce 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -138,7 +138,7 @@ pub fn approxEqAbs(comptime T: type, x: T, y: T, tolerance: T) bool { if (isNan(x) or isNan(y)) return false; - return fabs(x - y) <= tolerance; + return @fabs(x - y) <= tolerance; } /// Performs an approximate comparison of two floating point values `x` and `y`. @@ -166,7 +166,7 @@ pub fn approxEqRel(comptime T: type, x: T, y: T, tolerance: T) bool { if (isNan(x) or isNan(y)) return false; - return fabs(x - y) <= max(fabs(x), fabs(y)) * tolerance; + return @fabs(x - y) <= max(@fabs(x), @fabs(y)) * tolerance; } pub fn approxEq(comptime T: type, x: T, y: T, tolerance: T) bool { @@ -233,11 +233,6 @@ pub fn raiseDivByZero() void { pub const isNan = @import("math/isnan.zig").isNan; pub const isSignalNan = @import("math/isnan.zig").isSignalNan; -pub const fabs = @import("math/fabs.zig").fabs; -pub const ceil = @import("math/ceil.zig").ceil; -pub const floor = @import("math/floor.zig").floor; -pub const trunc = @import("math/trunc.zig").trunc; -pub const round = @import("math/round.zig").round; pub const frexp = @import("math/frexp.zig").frexp; pub const Frexp = @import("math/frexp.zig").Frexp; pub const modf = @import("math/modf.zig").modf; @@ -261,8 +256,6 @@ pub const asin = @import("math/asin.zig").asin; pub const atan = @import("math/atan.zig").atan; pub const atan2 = @import("math/atan2.zig").atan2; pub const hypot = @import("math/hypot.zig").hypot; -pub const exp = @import("math/exp.zig").exp; -pub const exp2 = @import("math/exp2.zig").exp2; pub const expm1 = @import("math/expm1.zig").expm1; pub const ilogb = @import("math/ilogb.zig").ilogb; pub const ln = @import("math/ln.zig").ln; @@ -270,16 +263,12 @@ pub const log = @import("math/log.zig").log; pub const log2 = @import("math/log2.zig").log2; pub const log10 = @import("math/log10.zig").log10; pub const log1p = @import("math/log1p.zig").log1p; -pub const fma = @import("math/fma.zig").fma; pub const asinh = @import("math/asinh.zig").asinh; pub const acosh = @import("math/acosh.zig").acosh; pub const atanh = @import("math/atanh.zig").atanh; pub const sinh = @import("math/sinh.zig").sinh; pub const cosh = @import("math/cosh.zig").cosh; pub const tanh = @import("math/tanh.zig").tanh; -pub const cos = @import("math/cos.zig").cos; -pub const sin = @import("math/sin.zig").sin; -pub const tan = @import("math/tan.zig").tan; pub const complex = @import("math/complex.zig"); pub const Complex = complex.Complex; @@ -716,17 +705,6 @@ fn testAbsInt() !void { try testing.expect((absInt(@as(i32, 10)) catch unreachable) == 10); } -pub const absFloat = fabs; - -test "absFloat" { - try testAbsFloat(); - comptime try testAbsFloat(); -} -fn testAbsFloat() !void { - try testing.expect(absFloat(@as(f32, -10.05)) == 10.05); - try testing.expect(absFloat(@as(f32, 10.05)) == 10.05); -} - /// Divide numerator by denominator, rounding toward zero. Returns an /// error on overflow or when denominator is zero. pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T { @@ -1400,11 +1378,6 @@ test "order.compare" { try testing.expect(order(1, 0).compare(.neq)); } -test "comptime sin and ln" { - const v = comptime (sin(@as(f32, 1)) + ln(@as(f32, 5))); - try testing.expect(v == sin(@as(f32, 1)) + ln(@as(f32, 5))); -} - /// Returns a mask of all ones if value is true, /// and a mask of all zeroes if value is false. /// Compiles to one instruction for register sized integers. diff --git a/lib/std/math/acos.zig b/lib/std/math/acos.zig index b90ba9c78e..e88bed7227 100644 --- a/lib/std/math/acos.zig +++ b/lib/std/math/acos.zig @@ -64,14 +64,14 @@ fn acos32(x: f32) f32 { // x < -0.5 if (hx >> 31 != 0) { const z = (1 + x) * 0.5; - const s = math.sqrt(z); + const s = @sqrt(z); const w = r32(z) * s - pio2_lo; return 2 * (pio2_hi - (s + w)); } // x > 0.5 const z = (1.0 - x) * 0.5; - const s = math.sqrt(z); + const s = @sqrt(z); const jx = @bitCast(u32, s); const df = @bitCast(f32, jx & 0xFFFFF000); const c = (z - df * df) / (s + df); @@ -133,14 +133,14 @@ fn acos64(x: f64) f64 { // x < -0.5 if (hx >> 31 != 0) { const z = (1.0 + x) * 0.5; - const s = math.sqrt(z); + const s = @sqrt(z); const w = r64(z) * s - pio2_lo; return 2 * (pio2_hi - (s + w)); } // x > 0.5 const z = (1.0 - x) * 0.5; - const s = math.sqrt(z); + const s = @sqrt(z); const jx = @bitCast(u64, s); const df = @bitCast(f64, jx & 0xFFFFFFFF00000000); const c = (z - df * df) / (s + df); diff --git a/lib/std/math/acosh.zig b/lib/std/math/acosh.zig index e42f4fd5d3..a78130d2ef 100644 --- a/lib/std/math/acosh.zig +++ b/lib/std/math/acosh.zig @@ -29,15 +29,15 @@ fn acosh32(x: f32) f32 { // |x| < 2, invalid if x < 1 or nan if (i < 0x3F800000 + (1 << 23)) { - return math.log1p(x - 1 + math.sqrt((x - 1) * (x - 1) + 2 * (x - 1))); + return math.log1p(x - 1 + @sqrt((x - 1) * (x - 1) + 2 * (x - 1))); } // |x| < 0x1p12 else if (i < 0x3F800000 + (12 << 23)) { - return math.ln(2 * x - 1 / (x + math.sqrt(x * x - 1))); + return @log(2 * x - 1 / (x + @sqrt(x * x - 1))); } // |x| >= 0x1p12 else { - return math.ln(x) + 0.693147180559945309417232121458176568; + return @log(x) + 0.693147180559945309417232121458176568; } } @@ -47,15 +47,15 @@ fn acosh64(x: f64) f64 { // |x| < 2, invalid if x < 1 or nan if (e < 0x3FF + 1) { - return math.log1p(x - 1 + math.sqrt((x - 1) * (x - 1) + 2 * (x - 1))); + return math.log1p(x - 1 + @sqrt((x - 1) * (x - 1) + 2 * (x - 1))); } // |x| < 0x1p26 else if (e < 0x3FF + 26) { - return math.ln(2 * x - 1 / (x + math.sqrt(x * x - 1))); + return @log(2 * x - 1 / (x + @sqrt(x * x - 1))); } // |x| >= 0x1p26 or nan else { - return math.ln(x) + 0.693147180559945309417232121458176568; + return @log(x) + 0.693147180559945309417232121458176568; } } diff --git a/lib/std/math/asin.zig b/lib/std/math/asin.zig index 0849fac72e..48ad04c579 100644 --- a/lib/std/math/asin.zig +++ b/lib/std/math/asin.zig @@ -60,8 +60,8 @@ fn asin32(x: f32) f32 { } // 1 > |x| >= 0.5 - const z = (1 - math.fabs(x)) * 0.5; - const s = math.sqrt(z); + const z = (1 - @fabs(x)) * 0.5; + const s = @sqrt(z); const fx = pio2 - 2 * (s + s * r32(z)); if (hx >> 31 != 0) { @@ -119,8 +119,8 @@ fn asin64(x: f64) f64 { } // 1 > |x| >= 0.5 - const z = (1 - math.fabs(x)) * 0.5; - const s = math.sqrt(z); + const z = (1 - @fabs(x)) * 0.5; + const s = @sqrt(z); const r = r64(z); var fx: f64 = undefined; diff --git a/lib/std/math/asinh.zig b/lib/std/math/asinh.zig index 8717ebbb66..65028ef5d9 100644 --- a/lib/std/math/asinh.zig +++ b/lib/std/math/asinh.zig @@ -39,15 +39,15 @@ fn asinh32(x: f32) f32 { // |x| >= 0x1p12 or inf or nan if (i >= 0x3F800000 + (12 << 23)) { - rx = math.ln(rx) + 0.69314718055994530941723212145817656; + rx = @log(rx) + 0.69314718055994530941723212145817656; } // |x| >= 2 else if (i >= 0x3F800000 + (1 << 23)) { - rx = math.ln(2 * x + 1 / (math.sqrt(x * x + 1) + x)); + rx = @log(2 * x + 1 / (@sqrt(x * x + 1) + x)); } // |x| >= 0x1p-12, up to 1.6ulp error else if (i >= 0x3F800000 - (12 << 23)) { - rx = math.log1p(x + x * x / (math.sqrt(x * x + 1) + 1)); + rx = math.log1p(x + x * x / (@sqrt(x * x + 1) + 1)); } // |x| < 0x1p-12, inexact if x != 0 else { @@ -70,15 +70,15 @@ fn asinh64(x: f64) f64 { // |x| >= 0x1p26 or inf or nan if (e >= 0x3FF + 26) { - rx = math.ln(rx) + 0.693147180559945309417232121458176568; + rx = @log(rx) + 0.693147180559945309417232121458176568; } // |x| >= 2 else if (e >= 0x3FF + 1) { - rx = math.ln(2 * x + 1 / (math.sqrt(x * x + 1) + x)); + rx = @log(2 * x + 1 / (@sqrt(x * x + 1) + x)); } // |x| >= 0x1p-12, up to 1.6ulp error else if (e >= 0x3FF - 26) { - rx = math.log1p(x + x * x / (math.sqrt(x * x + 1) + 1)); + rx = math.log1p(x + x * x / (@sqrt(x * x + 1) + 1)); } // |x| < 0x1p-12, inexact if x != 0 else { diff --git a/lib/std/math/atan.zig b/lib/std/math/atan.zig index c67e6fe8e0..3a13d943e8 100644 --- a/lib/std/math/atan.zig +++ b/lib/std/math/atan.zig @@ -73,7 +73,7 @@ fn atan32(x_: f32) f32 { } id = null; } else { - x = math.fabs(x); + x = @fabs(x); // |x| < 1.1875 if (ix < 0x3F980000) { // 7/16 <= |x| < 11/16 @@ -171,7 +171,7 @@ fn atan64(x_: f64) f64 { } id = null; } else { - x = math.fabs(x); + x = @fabs(x); // |x| < 1.1875 if (ix < 0x3FF30000) { // 7/16 <= |x| < 11/16 diff --git a/lib/std/math/atan2.zig b/lib/std/math/atan2.zig index d440d65e04..b9b37e7da4 100644 --- a/lib/std/math/atan2.zig +++ b/lib/std/math/atan2.zig @@ -108,7 +108,7 @@ fn atan2_32(y: f32, x: f32) f32 { if ((m & 2) != 0 and iy + (26 << 23) < ix) { break :z 0.0; } else { - break :z math.atan(math.fabs(y / x)); + break :z math.atan(@fabs(y / x)); } }; @@ -198,7 +198,7 @@ fn atan2_64(y: f64, x: f64) f64 { if ((m & 2) != 0 and iy +% (64 << 20) < ix) { break :z 0.0; } else { - break :z math.atan(math.fabs(y / x)); + break :z math.atan(@fabs(y / x)); } }; diff --git a/lib/std/math/complex.zig b/lib/std/math/complex.zig index 42342faa3e..2fd1cf15a1 100644 --- a/lib/std/math/complex.zig +++ b/lib/std/math/complex.zig @@ -115,7 +115,7 @@ pub fn Complex(comptime T: type) type { /// Returns the magnitude of a complex number. pub fn magnitude(self: Self) T { - return math.sqrt(self.re * self.re + self.im * self.im); + return @sqrt(self.re * self.re + self.im * self.im); } }; } diff --git a/lib/std/math/complex/atan.zig b/lib/std/math/complex/atan.zig index 484b41edf5..929b98aebd 100644 --- a/lib/std/math/complex/atan.zig +++ b/lib/std/math/complex/atan.zig @@ -66,7 +66,7 @@ fn atan32(z: Complex(f32)) Complex(f32) { t = y + 1.0; a = (x2 + (t * t)) / a; - return Complex(f32).init(w, 0.25 * math.ln(a)); + return Complex(f32).init(w, 0.25 * @log(a)); } fn redupif64(x: f64) f64 { @@ -115,7 +115,7 @@ fn atan64(z: Complex(f64)) Complex(f64) { t = y + 1.0; a = (x2 + (t * t)) / a; - return Complex(f64).init(w, 0.25 * math.ln(a)); + return Complex(f64).init(w, 0.25 * @log(a)); } const epsilon = 0.0001; diff --git a/lib/std/math/complex/cosh.zig b/lib/std/math/complex/cosh.zig index 46f7a714a2..719d0f28cd 100644 --- a/lib/std/math/complex/cosh.zig +++ b/lib/std/math/complex/cosh.zig @@ -44,12 +44,12 @@ fn cosh32(z: Complex(f32)) Complex(f32) { // |x|>= 9, so cosh(x) ~= exp(|x|) if (ix < 0x42b17218) { // x < 88.7: exp(|x|) won't overflow - const h = math.exp(math.fabs(x)) * 0.5; + const h = @exp(@fabs(x)) * 0.5; return Complex(f32).init(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y)); } // x < 192.7: scale to avoid overflow else if (ix < 0x4340b1e7) { - const v = Complex(f32).init(math.fabs(x), y); + const v = Complex(f32).init(@fabs(x), y); const r = ldexp_cexp(v, -1); return Complex(f32).init(r.re, r.im * math.copysign(f32, 1, x)); } @@ -112,12 +112,12 @@ fn cosh64(z: Complex(f64)) Complex(f64) { // |x|>= 22, so cosh(x) ~= exp(|x|) if (ix < 0x40862e42) { // x < 710: exp(|x|) won't overflow - const h = math.exp(math.fabs(x)) * 0.5; + const h = @exp(@fabs(x)) * 0.5; return Complex(f64).init(h * math.cos(y), math.copysign(f64, h, x) * math.sin(y)); } // x < 1455: scale to avoid overflow else if (ix < 0x4096bbaa) { - const v = Complex(f64).init(math.fabs(x), y); + const v = Complex(f64).init(@fabs(x), y); const r = ldexp_cexp(v, -1); return Complex(f64).init(r.re, r.im * math.copysign(f64, 1, x)); } diff --git a/lib/std/math/complex/exp.zig b/lib/std/math/complex/exp.zig index ce25025ded..4ed731d85c 100644 --- a/lib/std/math/complex/exp.zig +++ b/lib/std/math/complex/exp.zig @@ -33,7 +33,7 @@ fn exp32(z: Complex(f32)) Complex(f32) { const hy = @bitCast(u32, y) & 0x7fffffff; // cexp(x + i0) = exp(x) + i0 if (hy == 0) { - return Complex(f32).init(math.exp(x), y); + return Complex(f32).init(@exp(x), y); } const hx = @bitCast(u32, x); @@ -63,7 +63,7 @@ fn exp32(z: Complex(f32)) Complex(f32) { // - x = +-inf // - x = nan else { - const exp_x = math.exp(x); + const exp_x = @exp(x); return Complex(f32).init(exp_x * math.cos(y), exp_x * math.sin(y)); } } @@ -81,7 +81,7 @@ fn exp64(z: Complex(f64)) Complex(f64) { // cexp(x + i0) = exp(x) + i0 if (hy | ly == 0) { - return Complex(f64).init(math.exp(x), y); + return Complex(f64).init(@exp(x), y); } const fx = @bitCast(u64, x); @@ -114,13 +114,13 @@ fn exp64(z: Complex(f64)) Complex(f64) { // - x = +-inf // - x = nan else { - const exp_x = math.exp(x); + const exp_x = @exp(x); return Complex(f64).init(exp_x * math.cos(y), exp_x * math.sin(y)); } } test "complex.cexp32" { - const tolerance_f32 = math.sqrt(math.floatEps(f32)); + const tolerance_f32 = @sqrt(math.floatEps(f32)); { const a = Complex(f32).init(5, 3); @@ -140,7 +140,7 @@ test "complex.cexp32" { } test "complex.cexp64" { - const tolerance_f64 = math.sqrt(math.floatEps(f64)); + const tolerance_f64 = @sqrt(math.floatEps(f64)); { const a = Complex(f64).init(5, 3); diff --git a/lib/std/math/complex/ldexp.zig b/lib/std/math/complex/ldexp.zig index db710a0438..1c2d06b858 100644 --- a/lib/std/math/complex/ldexp.zig +++ b/lib/std/math/complex/ldexp.zig @@ -26,7 +26,7 @@ fn frexp_exp32(x: f32, expt: *i32) f32 { const k = 235; // reduction constant const kln2 = 162.88958740; // k * ln2 - const exp_x = math.exp(x - kln2); + const exp_x = @exp(x - kln2); const hx = @bitCast(u32, exp_x); // TODO zig should allow this cast implicitly because it should know the value is in range expt.* = @intCast(i32, hx >> 23) - (0x7f + 127) + k; @@ -54,7 +54,7 @@ fn frexp_exp64(x: f64, expt: *i32) f64 { const k = 1799; // reduction constant const kln2 = 1246.97177782734161156; // k * ln2 - const exp_x = math.exp(x - kln2); + const exp_x = @exp(x - kln2); const fx = @bitCast(u64, exp_x); const hx = @intCast(u32, fx >> 32); diff --git a/lib/std/math/complex/log.zig b/lib/std/math/complex/log.zig index 90c51058cf..6d1b06d272 100644 --- a/lib/std/math/complex/log.zig +++ b/lib/std/math/complex/log.zig @@ -10,7 +10,7 @@ pub fn log(z: anytype) Complex(@TypeOf(z.re)) { const r = cmath.abs(z); const phi = cmath.arg(z); - return Complex(T).init(math.ln(r), phi); + return Complex(T).init(@log(r), phi); } const epsilon = 0.0001; diff --git a/lib/std/math/complex/sinh.zig b/lib/std/math/complex/sinh.zig index 851af3e62e..b21f6e59eb 100644 --- a/lib/std/math/complex/sinh.zig +++ b/lib/std/math/complex/sinh.zig @@ -44,12 +44,12 @@ fn sinh32(z: Complex(f32)) Complex(f32) { // |x|>= 9, so cosh(x) ~= exp(|x|) if (ix < 0x42b17218) { // x < 88.7: exp(|x|) won't overflow - const h = math.exp(math.fabs(x)) * 0.5; + const h = @exp(@fabs(x)) * 0.5; return Complex(f32).init(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y)); } // x < 192.7: scale to avoid overflow else if (ix < 0x4340b1e7) { - const v = Complex(f32).init(math.fabs(x), y); + const v = Complex(f32).init(@fabs(x), y); const r = ldexp_cexp(v, -1); return Complex(f32).init(r.re * math.copysign(f32, 1, x), r.im); } @@ -111,12 +111,12 @@ fn sinh64(z: Complex(f64)) Complex(f64) { // |x|>= 22, so cosh(x) ~= exp(|x|) if (ix < 0x40862e42) { // x < 710: exp(|x|) won't overflow - const h = math.exp(math.fabs(x)) * 0.5; + const h = @exp(@fabs(x)) * 0.5; return Complex(f64).init(math.copysign(f64, h, x) * math.cos(y), h * math.sin(y)); } // x < 1455: scale to avoid overflow else if (ix < 0x4096bbaa) { - const v = Complex(f64).init(math.fabs(x), y); + const v = Complex(f64).init(@fabs(x), y); const r = ldexp_cexp(v, -1); return Complex(f64).init(r.re * math.copysign(f64, 1, x), r.im); } diff --git a/lib/std/math/complex/sqrt.zig b/lib/std/math/complex/sqrt.zig index 4f16e631b8..ab24e2d60d 100644 --- a/lib/std/math/complex/sqrt.zig +++ b/lib/std/math/complex/sqrt.zig @@ -43,7 +43,7 @@ fn sqrt32(z: Complex(f32)) Complex(f32) { // sqrt(-inf + i nan) = nan +- inf i // sqrt(-inf + iy) = 0 + inf i if (math.signbit(x)) { - return Complex(f32).init(math.fabs(x - y), math.copysign(f32, x, y)); + return Complex(f32).init(@fabs(x - y), math.copysign(f32, x, y)); } else { return Complex(f32).init(x, math.copysign(f32, y - y, y)); } @@ -56,15 +56,15 @@ fn sqrt32(z: Complex(f32)) Complex(f32) { const dy = @as(f64, y); if (dx >= 0) { - const t = math.sqrt((dx + math.hypot(f64, dx, dy)) * 0.5); + const t = @sqrt((dx + math.hypot(f64, dx, dy)) * 0.5); return Complex(f32).init( @floatCast(f32, t), @floatCast(f32, dy / (2.0 * t)), ); } else { - const t = math.sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5); + const t = @sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5); return Complex(f32).init( - @floatCast(f32, math.fabs(y) / (2.0 * t)), + @floatCast(f32, @fabs(y) / (2.0 * t)), @floatCast(f32, math.copysign(f64, t, y)), ); } @@ -94,7 +94,7 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { // sqrt(-inf + i nan) = nan +- inf i // sqrt(-inf + iy) = 0 + inf i if (math.signbit(x)) { - return Complex(f64).init(math.fabs(x - y), math.copysign(f64, x, y)); + return Complex(f64).init(@fabs(x - y), math.copysign(f64, x, y)); } else { return Complex(f64).init(x, math.copysign(f64, y - y, y)); } @@ -104,7 +104,7 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { // scale to avoid overflow var scale = false; - if (math.fabs(x) >= threshold or math.fabs(y) >= threshold) { + if (@fabs(x) >= threshold or @fabs(y) >= threshold) { x *= 0.25; y *= 0.25; scale = true; @@ -112,11 +112,11 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { var result: Complex(f64) = undefined; if (x >= 0) { - const t = math.sqrt((x + math.hypot(f64, x, y)) * 0.5); + const t = @sqrt((x + math.hypot(f64, x, y)) * 0.5); result = Complex(f64).init(t, y / (2.0 * t)); } else { - const t = math.sqrt((-x + math.hypot(f64, x, y)) * 0.5); - result = Complex(f64).init(math.fabs(y) / (2.0 * t), math.copysign(f64, t, y)); + const t = @sqrt((-x + math.hypot(f64, x, y)) * 0.5); + result = Complex(f64).init(@fabs(y) / (2.0 * t), math.copysign(f64, t, y)); } if (scale) { diff --git a/lib/std/math/complex/tanh.zig b/lib/std/math/complex/tanh.zig index 0960c66679..e61ec1e95b 100644 --- a/lib/std/math/complex/tanh.zig +++ b/lib/std/math/complex/tanh.zig @@ -44,7 +44,7 @@ fn tanh32(z: Complex(f32)) Complex(f32) { // x >= 11 if (ix >= 0x41300000) { - const exp_mx = math.exp(-math.fabs(x)); + const exp_mx = @exp(-@fabs(x)); return Complex(f32).init(math.copysign(f32, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx); } @@ -52,7 +52,7 @@ fn tanh32(z: Complex(f32)) Complex(f32) { const t = math.tan(y); const beta = 1.0 + t * t; const s = math.sinh(x); - const rho = math.sqrt(1 + s * s); + const rho = @sqrt(1 + s * s); const den = 1 + beta * s * s; return Complex(f32).init((beta * rho * s) / den, t / den); @@ -87,7 +87,7 @@ fn tanh64(z: Complex(f64)) Complex(f64) { // x >= 22 if (ix >= 0x40360000) { - const exp_mx = math.exp(-math.fabs(x)); + const exp_mx = @exp(-@fabs(x)); return Complex(f64).init(math.copysign(f64, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx); } @@ -95,7 +95,7 @@ fn tanh64(z: Complex(f64)) Complex(f64) { const t = math.tan(y); const beta = 1.0 + t * t; const s = math.sinh(x); - const rho = math.sqrt(1 + s * s); + const rho = @sqrt(1 + s * s); const den = 1 + beta * s * s; return Complex(f64).init((beta * rho * s) / den, t / den); diff --git a/lib/std/math/cosh.zig b/lib/std/math/cosh.zig index c71e82ea1c..d633f2fa0c 100644 --- a/lib/std/math/cosh.zig +++ b/lib/std/math/cosh.zig @@ -45,7 +45,7 @@ fn cosh32(x: f32) f32 { // |x| < log(FLT_MAX) if (ux < 0x42B17217) { - const t = math.exp(ax); + const t = @exp(ax); return 0.5 * (t + 1 / t); } @@ -77,7 +77,7 @@ fn cosh64(x: f64) f64 { // |x| < log(DBL_MAX) if (w < 0x40862E42) { - const t = math.exp(ax); + const t = @exp(ax); // NOTE: If x > log(0x1p26) then 1/t is not required. return 0.5 * (t + 1 / t); } diff --git a/lib/std/math/expo2.zig b/lib/std/math/expo2.zig index f404570fb6..4345233173 100644 --- a/lib/std/math/expo2.zig +++ b/lib/std/math/expo2.zig @@ -22,7 +22,7 @@ fn expo2f(x: f32) f32 { const u = (0x7F + k / 2) << 23; const scale = @bitCast(f32, u); - return math.exp(x - kln2) * scale * scale; + return @exp(x - kln2) * scale * scale; } fn expo2d(x: f64) f64 { @@ -31,5 +31,5 @@ fn expo2d(x: f64) f64 { const u = (0x3FF + k / 2) << 20; const scale = @bitCast(f64, @as(u64, u) << 32); - return math.exp(x - kln2) * scale * scale; + return @exp(x - kln2) * scale * scale; } diff --git a/lib/std/math/fabs.zig b/lib/std/math/fabs.zig deleted file mode 100644 index 44918e75d9..0000000000 --- a/lib/std/math/fabs.zig +++ /dev/null @@ -1,45 +0,0 @@ -const std = @import("../std.zig"); -const math = std.math; -const expect = std.testing.expect; - -/// Returns the absolute value of x. -/// -/// Special Cases: -/// - fabs(+-inf) = +inf -/// - fabs(nan) = nan -pub fn fabs(x: anytype) @TypeOf(x) { - const T = @TypeOf(x); - const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); - if (@typeInfo(T) != .Float) { - @compileError("fabs not implemented for " ++ @typeName(T)); - } - - const float_bits = @bitCast(TBits, x); - const remove_sign = ~@as(TBits, 0) >> 1; - - return @bitCast(T, float_bits & remove_sign); -} - -test "math.fabs" { - // TODO add support for c_longdouble here - inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { - // normals - try expect(fabs(@as(T, 1.0)) == 1.0); - try expect(fabs(@as(T, -1.0)) == 1.0); - try expect(fabs(math.floatMin(T)) == math.floatMin(T)); - try expect(fabs(-math.floatMin(T)) == math.floatMin(T)); - try expect(fabs(math.floatMax(T)) == math.floatMax(T)); - try expect(fabs(-math.floatMax(T)) == math.floatMax(T)); - - // subnormals - try expect(fabs(@as(T, 0.0)) == 0.0); - try expect(fabs(@as(T, -0.0)) == 0.0); - try expect(fabs(math.floatTrueMin(T)) == math.floatTrueMin(T)); - try expect(fabs(-math.floatTrueMin(T)) == math.floatTrueMin(T)); - - // non-finite numbers - try expect(math.isPositiveInf(fabs(math.inf(T)))); - try expect(math.isPositiveInf(fabs(-math.inf(T)))); - try expect(math.isNan(fabs(math.nan(T)))); - } -} diff --git a/lib/std/math/hypot.zig b/lib/std/math/hypot.zig index e47a191892..981f6143fe 100644 --- a/lib/std/math/hypot.zig +++ b/lib/std/math/hypot.zig @@ -56,7 +56,7 @@ fn hypot32(x: f32, y: f32) f32 { yy *= 0x1.0p-90; } - return z * math.sqrt(@floatCast(f32, @as(f64, x) * x + @as(f64, y) * y)); + return z * @sqrt(@floatCast(f32, @as(f64, x) * x + @as(f64, y) * y)); } fn sq(hi: *f64, lo: *f64, x: f64) void { @@ -117,7 +117,7 @@ fn hypot64(x: f64, y: f64) f64 { sq(&hx, &lx, x); sq(&hy, &ly, y); - return z * math.sqrt(ly + lx + hy + hx); + return z * @sqrt(ly + lx + hy + hx); } test "math.hypot" { diff --git a/lib/std/math/ln.zig b/lib/std/math/ln.zig index bb352cd6e1..65db861587 100644 --- a/lib/std/math/ln.zig +++ b/lib/std/math/ln.zig @@ -1,12 +1,6 @@ -// Ported from musl, which is licensed under the MIT license: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/lnf.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/ln.c - const std = @import("../std.zig"); const math = std.math; -const expect = std.testing.expect; +const testing = std.testing; /// Returns the natural logarithm of x. /// @@ -15,175 +9,26 @@ const expect = std.testing.expect; /// - ln(0) = -inf /// - ln(x) = nan if x < 0 /// - ln(nan) = nan +/// TODO remove this in favor of `@log`. pub fn ln(x: anytype) @TypeOf(x) { const T = @TypeOf(x); switch (@typeInfo(T)) { .ComptimeFloat => { - return @as(comptime_float, ln_64(x)); - }, - .Float => { - return switch (T) { - f32 => ln_32(x), - f64 => ln_64(x), - else => @compileError("ln not implemented for " ++ @typeName(T)), - }; + return @as(comptime_float, @log(x)); }, + .Float => return @log(x), .ComptimeInt => { - return @as(comptime_int, math.floor(ln_64(@as(f64, x)))); + return @as(comptime_int, @floor(@log(@as(f64, x)))); }, .Int => |IntType| switch (IntType.signedness) { .signed => @compileError("ln not implemented for signed integers"), - .unsigned => return @as(T, math.floor(ln_64(@as(f64, x)))), + .unsigned => return @as(T, @floor(@log(@as(f64, x)))), }, else => @compileError("ln not implemented for " ++ @typeName(T)), } } -pub fn ln_32(x_: f32) f32 { - const ln2_hi: f32 = 6.9313812256e-01; - const ln2_lo: f32 = 9.0580006145e-06; - const Lg1: f32 = 0xaaaaaa.0p-24; - const Lg2: f32 = 0xccce13.0p-25; - const Lg3: f32 = 0x91e9ee.0p-25; - const Lg4: f32 = 0xf89e26.0p-26; - - var x = x_; - var ix = @bitCast(u32, x); - var k: i32 = 0; - - // x < 2^(-126) - if (ix < 0x00800000 or ix >> 31 != 0) { - // log(+-0) = -inf - if (ix << 1 == 0) { - return -math.inf(f32); - } - // log(-#) = nan - if (ix >> 31 != 0) { - return math.nan(f32); - } - - // subnormal, scale x - k -= 25; - x *= 0x1.0p25; - ix = @bitCast(u32, x); - } else if (ix >= 0x7F800000) { - return x; - } else if (ix == 0x3F800000) { - return 0; - } - - // x into [sqrt(2) / 2, sqrt(2)] - ix += 0x3F800000 - 0x3F3504F3; - k += @intCast(i32, ix >> 23) - 0x7F; - ix = (ix & 0x007FFFFF) + 0x3F3504F3; - x = @bitCast(f32, ix); - - const f = x - 1.0; - const s = f / (2.0 + f); - const z = s * s; - const w = z * z; - const t1 = w * (Lg2 + w * Lg4); - const t2 = z * (Lg1 + w * Lg3); - const R = t2 + t1; - const hfsq = 0.5 * f * f; - const dk = @intToFloat(f32, k); - - return s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi; -} - -pub fn ln_64(x_: f64) f64 { - const ln2_hi: f64 = 6.93147180369123816490e-01; - const ln2_lo: f64 = 1.90821492927058770002e-10; - const Lg1: f64 = 6.666666666666735130e-01; - const Lg2: f64 = 3.999999999940941908e-01; - const Lg3: f64 = 2.857142874366239149e-01; - const Lg4: f64 = 2.222219843214978396e-01; - const Lg5: f64 = 1.818357216161805012e-01; - const Lg6: f64 = 1.531383769920937332e-01; - const Lg7: f64 = 1.479819860511658591e-01; - - var x = x_; - var ix = @bitCast(u64, x); - var hx = @intCast(u32, ix >> 32); - var k: i32 = 0; - - if (hx < 0x00100000 or hx >> 31 != 0) { - // log(+-0) = -inf - if (ix << 1 == 0) { - return -math.inf(f64); - } - // log(-#) = nan - if (hx >> 31 != 0) { - return math.nan(f64); - } - - // subnormal, scale x - k -= 54; - x *= 0x1.0p54; - hx = @intCast(u32, @bitCast(u64, ix) >> 32); - } else if (hx >= 0x7FF00000) { - return x; - } else if (hx == 0x3FF00000 and ix << 32 == 0) { - return 0; - } - - // x into [sqrt(2) / 2, sqrt(2)] - hx += 0x3FF00000 - 0x3FE6A09E; - k += @intCast(i32, hx >> 20) - 0x3FF; - hx = (hx & 0x000FFFFF) + 0x3FE6A09E; - ix = (@as(u64, hx) << 32) | (ix & 0xFFFFFFFF); - x = @bitCast(f64, ix); - - const f = x - 1.0; - const hfsq = 0.5 * f * f; - const s = f / (2.0 + f); - const z = s * s; - const w = z * z; - const t1 = w * (Lg2 + w * (Lg4 + w * Lg6)); - const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7))); - const R = t2 + t1; - const dk = @intToFloat(f64, k); - - return s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi; -} - test "math.ln" { - try expect(ln(@as(f32, 0.2)) == ln_32(0.2)); - try expect(ln(@as(f64, 0.2)) == ln_64(0.2)); -} - -test "math.ln32" { - const epsilon = 0.000001; - - try expect(math.approxEqAbs(f32, ln_32(0.2), -1.609438, epsilon)); - try expect(math.approxEqAbs(f32, ln_32(0.8923), -0.113953, epsilon)); - try expect(math.approxEqAbs(f32, ln_32(1.5), 0.405465, epsilon)); - try expect(math.approxEqAbs(f32, ln_32(37.45), 3.623007, epsilon)); - try expect(math.approxEqAbs(f32, ln_32(89.123), 4.490017, epsilon)); - try expect(math.approxEqAbs(f32, ln_32(123123.234375), 11.720941, epsilon)); -} - -test "math.ln64" { - const epsilon = 0.000001; - - try expect(math.approxEqAbs(f64, ln_64(0.2), -1.609438, epsilon)); - try expect(math.approxEqAbs(f64, ln_64(0.8923), -0.113953, epsilon)); - try expect(math.approxEqAbs(f64, ln_64(1.5), 0.405465, epsilon)); - try expect(math.approxEqAbs(f64, ln_64(37.45), 3.623007, epsilon)); - try expect(math.approxEqAbs(f64, ln_64(89.123), 4.490017, epsilon)); - try expect(math.approxEqAbs(f64, ln_64(123123.234375), 11.720941, epsilon)); -} - -test "math.ln32.special" { - try expect(math.isPositiveInf(ln_32(math.inf(f32)))); - try expect(math.isNegativeInf(ln_32(0.0))); - try expect(math.isNan(ln_32(-1.0))); - try expect(math.isNan(ln_32(math.nan(f32)))); -} - -test "math.ln64.special" { - try expect(math.isPositiveInf(ln_64(math.inf(f64)))); - try expect(math.isNegativeInf(ln_64(0.0))); - try expect(math.isNan(ln_64(-1.0))); - try expect(math.isNan(ln_64(math.nan(f64)))); + try testing.expect(ln(@as(f32, 0.2)) == @log(0.2)); + try testing.expect(ln(@as(f64, 0.2)) == @log(0.2)); } diff --git a/lib/std/math/log.zig b/lib/std/math/log.zig index 6336726b39..ad2763fa54 100644 --- a/lib/std/math/log.zig +++ b/lib/std/math/log.zig @@ -15,28 +15,28 @@ pub fn log(comptime T: type, base: T, x: T) T { } else if (base == 10) { return math.log10(x); } else if ((@typeInfo(T) == .Float or @typeInfo(T) == .ComptimeFloat) and base == math.e) { - return math.ln(x); + return @log(x); } const float_base = math.lossyCast(f64, base); switch (@typeInfo(T)) { .ComptimeFloat => { - return @as(comptime_float, math.ln(@as(f64, x)) / math.ln(float_base)); + return @as(comptime_float, @log(@as(f64, x)) / @log(float_base)); }, .ComptimeInt => { - return @as(comptime_int, math.floor(math.ln(@as(f64, x)) / math.ln(float_base))); + return @as(comptime_int, @floor(@log(@as(f64, x)) / @log(float_base))); }, // TODO implement integer log without using float math .Int => |IntType| switch (IntType.signedness) { .signed => @compileError("log not implemented for signed integers"), - .unsigned => return @floatToInt(T, math.floor(math.ln(@intToFloat(f64, x)) / math.ln(float_base))), + .unsigned => return @floatToInt(T, @floor(@log(@intToFloat(f64, x)) / @log(float_base))), }, .Float => { switch (T) { - f32 => return @floatCast(f32, math.ln(@as(f64, x)) / math.ln(float_base)), - f64 => return math.ln(x) / math.ln(float_base), + f32 => return @floatCast(f32, @log(@as(f64, x)) / @log(float_base)), + f64 => return @log(x) / @log(float_base), else => @compileError("log not implemented for " ++ @typeName(T)), } }, diff --git a/lib/std/math/log10.zig b/lib/std/math/log10.zig index 84eced85f0..4f13426079 100644 --- a/lib/std/math/log10.zig +++ b/lib/std/math/log10.zig @@ -1,9 +1,3 @@ -// Ported from musl, which is licensed under the MIT license: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/log10f.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/log10.c - const std = @import("../std.zig"); const math = std.math; const testing = std.testing; @@ -20,198 +14,16 @@ pub fn log10(x: anytype) @TypeOf(x) { const T = @TypeOf(x); switch (@typeInfo(T)) { .ComptimeFloat => { - return @as(comptime_float, log10_64(x)); - }, - .Float => { - return switch (T) { - f32 => log10_32(x), - f64 => log10_64(x), - else => @compileError("log10 not implemented for " ++ @typeName(T)), - }; + return @as(comptime_float, @log10(x)); }, + .Float => return @log10(x), .ComptimeInt => { - return @as(comptime_int, math.floor(log10_64(@as(f64, x)))); + return @as(comptime_int, @floor(@log10(@as(f64, x)))); }, .Int => |IntType| switch (IntType.signedness) { .signed => @compileError("log10 not implemented for signed integers"), - .unsigned => return @floatToInt(T, math.floor(log10_64(@intToFloat(f64, x)))), + .unsigned => return @floatToInt(T, @floor(@log10(@intToFloat(f64, x)))), }, else => @compileError("log10 not implemented for " ++ @typeName(T)), } } - -pub fn log10_32(x_: f32) f32 { - const ivln10hi: f32 = 4.3432617188e-01; - const ivln10lo: f32 = -3.1689971365e-05; - const log10_2hi: f32 = 3.0102920532e-01; - const log10_2lo: f32 = 7.9034151668e-07; - const Lg1: f32 = 0xaaaaaa.0p-24; - const Lg2: f32 = 0xccce13.0p-25; - const Lg3: f32 = 0x91e9ee.0p-25; - const Lg4: f32 = 0xf89e26.0p-26; - - var x = x_; - var u = @bitCast(u32, x); - var ix = u; - var k: i32 = 0; - - // x < 2^(-126) - if (ix < 0x00800000 or ix >> 31 != 0) { - // log(+-0) = -inf - if (ix << 1 == 0) { - return -math.inf(f32); - } - // log(-#) = nan - if (ix >> 31 != 0) { - return math.nan(f32); - } - - k -= 25; - x *= 0x1.0p25; - ix = @bitCast(u32, x); - } else if (ix >= 0x7F800000) { - return x; - } else if (ix == 0x3F800000) { - return 0; - } - - // x into [sqrt(2) / 2, sqrt(2)] - ix += 0x3F800000 - 0x3F3504F3; - k += @intCast(i32, ix >> 23) - 0x7F; - ix = (ix & 0x007FFFFF) + 0x3F3504F3; - x = @bitCast(f32, ix); - - const f = x - 1.0; - const s = f / (2.0 + f); - const z = s * s; - const w = z * z; - const t1 = w * (Lg2 + w * Lg4); - const t2 = z * (Lg1 + w * Lg3); - const R = t2 + t1; - const hfsq = 0.5 * f * f; - - var hi = f - hfsq; - u = @bitCast(u32, hi); - u &= 0xFFFFF000; - hi = @bitCast(f32, u); - const lo = f - hi - hfsq + s * (hfsq + R); - const dk = @intToFloat(f32, k); - - return dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi + hi * ivln10hi + dk * log10_2hi; -} - -pub fn log10_64(x_: f64) f64 { - const ivln10hi: f64 = 4.34294481878168880939e-01; - const ivln10lo: f64 = 2.50829467116452752298e-11; - const log10_2hi: f64 = 3.01029995663611771306e-01; - const log10_2lo: f64 = 3.69423907715893078616e-13; - const Lg1: f64 = 6.666666666666735130e-01; - const Lg2: f64 = 3.999999999940941908e-01; - const Lg3: f64 = 2.857142874366239149e-01; - const Lg4: f64 = 2.222219843214978396e-01; - const Lg5: f64 = 1.818357216161805012e-01; - const Lg6: f64 = 1.531383769920937332e-01; - const Lg7: f64 = 1.479819860511658591e-01; - - var x = x_; - var ix = @bitCast(u64, x); - var hx = @intCast(u32, ix >> 32); - var k: i32 = 0; - - if (hx < 0x00100000 or hx >> 31 != 0) { - // log(+-0) = -inf - if (ix << 1 == 0) { - return -math.inf(f32); - } - // log(-#) = nan - if (hx >> 31 != 0) { - return math.nan(f32); - } - - // subnormal, scale x - k -= 54; - x *= 0x1.0p54; - hx = @intCast(u32, @bitCast(u64, x) >> 32); - } else if (hx >= 0x7FF00000) { - return x; - } else if (hx == 0x3FF00000 and ix << 32 == 0) { - return 0; - } - - // x into [sqrt(2) / 2, sqrt(2)] - hx += 0x3FF00000 - 0x3FE6A09E; - k += @intCast(i32, hx >> 20) - 0x3FF; - hx = (hx & 0x000FFFFF) + 0x3FE6A09E; - ix = (@as(u64, hx) << 32) | (ix & 0xFFFFFFFF); - x = @bitCast(f64, ix); - - const f = x - 1.0; - const hfsq = 0.5 * f * f; - const s = f / (2.0 + f); - const z = s * s; - const w = z * z; - const t1 = w * (Lg2 + w * (Lg4 + w * Lg6)); - const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7))); - const R = t2 + t1; - - // hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f) - var hi = f - hfsq; - var hii = @bitCast(u64, hi); - hii &= @as(u64, maxInt(u64)) << 32; - hi = @bitCast(f64, hii); - const lo = f - hi - hfsq + s * (hfsq + R); - - // val_hi + val_lo ~ log10(1 + f) + k * log10(2) - var val_hi = hi * ivln10hi; - const dk = @intToFloat(f64, k); - const y = dk * log10_2hi; - var val_lo = dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi; - - // Extra precision multiplication - const ww = y + val_hi; - val_lo += (y - ww) + val_hi; - val_hi = ww; - - return val_lo + val_hi; -} - -test "math.log10" { - try testing.expect(log10(@as(f32, 0.2)) == log10_32(0.2)); - try testing.expect(log10(@as(f64, 0.2)) == log10_64(0.2)); -} - -test "math.log10_32" { - const epsilon = 0.000001; - - try testing.expect(math.approxEqAbs(f32, log10_32(0.2), -0.698970, epsilon)); - try testing.expect(math.approxEqAbs(f32, log10_32(0.8923), -0.049489, epsilon)); - try testing.expect(math.approxEqAbs(f32, log10_32(1.5), 0.176091, epsilon)); - try testing.expect(math.approxEqAbs(f32, log10_32(37.45), 1.573452, epsilon)); - try testing.expect(math.approxEqAbs(f32, log10_32(89.123), 1.94999, epsilon)); - try testing.expect(math.approxEqAbs(f32, log10_32(123123.234375), 5.09034, epsilon)); -} - -test "math.log10_64" { - const epsilon = 0.000001; - - try testing.expect(math.approxEqAbs(f64, log10_64(0.2), -0.698970, epsilon)); - try testing.expect(math.approxEqAbs(f64, log10_64(0.8923), -0.049489, epsilon)); - try testing.expect(math.approxEqAbs(f64, log10_64(1.5), 0.176091, epsilon)); - try testing.expect(math.approxEqAbs(f64, log10_64(37.45), 1.573452, epsilon)); - try testing.expect(math.approxEqAbs(f64, log10_64(89.123), 1.94999, epsilon)); - try testing.expect(math.approxEqAbs(f64, log10_64(123123.234375), 5.09034, epsilon)); -} - -test "math.log10_32.special" { - try testing.expect(math.isPositiveInf(log10_32(math.inf(f32)))); - try testing.expect(math.isNegativeInf(log10_32(0.0))); - try testing.expect(math.isNan(log10_32(-1.0))); - try testing.expect(math.isNan(log10_32(math.nan(f32)))); -} - -test "math.log10_64.special" { - try testing.expect(math.isPositiveInf(log10_64(math.inf(f64)))); - try testing.expect(math.isNegativeInf(log10_64(0.0))); - try testing.expect(math.isNan(log10_64(-1.0))); - try testing.expect(math.isNan(log10_64(math.nan(f64)))); -} diff --git a/lib/std/math/log2.zig b/lib/std/math/log2.zig index 556c16f5cf..c83b170208 100644 --- a/lib/std/math/log2.zig +++ b/lib/std/math/log2.zig @@ -1,13 +1,6 @@ -// Ported from musl, which is licensed under the MIT license: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/log2f.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/log2.c - const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; -const maxInt = std.math.maxInt; /// Returns the base-2 logarithm of x. /// @@ -20,15 +13,9 @@ pub fn log2(x: anytype) @TypeOf(x) { const T = @TypeOf(x); switch (@typeInfo(T)) { .ComptimeFloat => { - return @as(comptime_float, log2_64(x)); - }, - .Float => { - return switch (T) { - f32 => log2_32(x), - f64 => log2_64(x), - else => @compileError("log2 not implemented for " ++ @typeName(T)), - }; + return @as(comptime_float, @log2(x)); }, + .Float => return @log2(x), .ComptimeInt => comptime { var result = 0; var x_shifted = x; @@ -46,168 +33,7 @@ pub fn log2(x: anytype) @TypeOf(x) { } } -pub fn log2_32(x_: f32) f32 { - const ivln2hi: f32 = 1.4428710938e+00; - const ivln2lo: f32 = -1.7605285393e-04; - const Lg1: f32 = 0xaaaaaa.0p-24; - const Lg2: f32 = 0xccce13.0p-25; - const Lg3: f32 = 0x91e9ee.0p-25; - const Lg4: f32 = 0xf89e26.0p-26; - - var x = x_; - var u = @bitCast(u32, x); - var ix = u; - var k: i32 = 0; - - // x < 2^(-126) - if (ix < 0x00800000 or ix >> 31 != 0) { - // log(+-0) = -inf - if (ix << 1 == 0) { - return -math.inf(f32); - } - // log(-#) = nan - if (ix >> 31 != 0) { - return math.nan(f32); - } - - k -= 25; - x *= 0x1.0p25; - ix = @bitCast(u32, x); - } else if (ix >= 0x7F800000) { - return x; - } else if (ix == 0x3F800000) { - return 0; - } - - // x into [sqrt(2) / 2, sqrt(2)] - ix += 0x3F800000 - 0x3F3504F3; - k += @intCast(i32, ix >> 23) - 0x7F; - ix = (ix & 0x007FFFFF) + 0x3F3504F3; - x = @bitCast(f32, ix); - - const f = x - 1.0; - const s = f / (2.0 + f); - const z = s * s; - const w = z * z; - const t1 = w * (Lg2 + w * Lg4); - const t2 = z * (Lg1 + w * Lg3); - const R = t2 + t1; - const hfsq = 0.5 * f * f; - - var hi = f - hfsq; - u = @bitCast(u32, hi); - u &= 0xFFFFF000; - hi = @bitCast(f32, u); - const lo = f - hi - hfsq + s * (hfsq + R); - return (lo + hi) * ivln2lo + lo * ivln2hi + hi * ivln2hi + @intToFloat(f32, k); -} - -pub fn log2_64(x_: f64) f64 { - const ivln2hi: f64 = 1.44269504072144627571e+00; - const ivln2lo: f64 = 1.67517131648865118353e-10; - const Lg1: f64 = 6.666666666666735130e-01; - const Lg2: f64 = 3.999999999940941908e-01; - const Lg3: f64 = 2.857142874366239149e-01; - const Lg4: f64 = 2.222219843214978396e-01; - const Lg5: f64 = 1.818357216161805012e-01; - const Lg6: f64 = 1.531383769920937332e-01; - const Lg7: f64 = 1.479819860511658591e-01; - - var x = x_; - var ix = @bitCast(u64, x); - var hx = @intCast(u32, ix >> 32); - var k: i32 = 0; - - if (hx < 0x00100000 or hx >> 31 != 0) { - // log(+-0) = -inf - if (ix << 1 == 0) { - return -math.inf(f64); - } - // log(-#) = nan - if (hx >> 31 != 0) { - return math.nan(f64); - } - - // subnormal, scale x - k -= 54; - x *= 0x1.0p54; - hx = @intCast(u32, @bitCast(u64, x) >> 32); - } else if (hx >= 0x7FF00000) { - return x; - } else if (hx == 0x3FF00000 and ix << 32 == 0) { - return 0; - } - - // x into [sqrt(2) / 2, sqrt(2)] - hx += 0x3FF00000 - 0x3FE6A09E; - k += @intCast(i32, hx >> 20) - 0x3FF; - hx = (hx & 0x000FFFFF) + 0x3FE6A09E; - ix = (@as(u64, hx) << 32) | (ix & 0xFFFFFFFF); - x = @bitCast(f64, ix); - - const f = x - 1.0; - const hfsq = 0.5 * f * f; - const s = f / (2.0 + f); - const z = s * s; - const w = z * z; - const t1 = w * (Lg2 + w * (Lg4 + w * Lg6)); - const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7))); - const R = t2 + t1; - - // hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f) - var hi = f - hfsq; - var hii = @bitCast(u64, hi); - hii &= @as(u64, maxInt(u64)) << 32; - hi = @bitCast(f64, hii); - const lo = f - hi - hfsq + s * (hfsq + R); - - var val_hi = hi * ivln2hi; - var val_lo = (lo + hi) * ivln2lo + lo * ivln2hi; - - // spadd(val_hi, val_lo, y) - const y = @intToFloat(f64, k); - const ww = y + val_hi; - val_lo += (y - ww) + val_hi; - val_hi = ww; - - return val_lo + val_hi; -} - -test "math.log2" { - try expect(log2(@as(f32, 0.2)) == log2_32(0.2)); - try expect(log2(@as(f64, 0.2)) == log2_64(0.2)); -} - -test "math.log2_32" { - const epsilon = 0.000001; - - try expect(math.approxEqAbs(f32, log2_32(0.2), -2.321928, epsilon)); - try expect(math.approxEqAbs(f32, log2_32(0.8923), -0.164399, epsilon)); - try expect(math.approxEqAbs(f32, log2_32(1.5), 0.584962, epsilon)); - try expect(math.approxEqAbs(f32, log2_32(37.45), 5.226894, epsilon)); - try expect(math.approxEqAbs(f32, log2_32(123123.234375), 16.909744, epsilon)); -} - -test "math.log2_64" { - const epsilon = 0.000001; - - try expect(math.approxEqAbs(f64, log2_64(0.2), -2.321928, epsilon)); - try expect(math.approxEqAbs(f64, log2_64(0.8923), -0.164399, epsilon)); - try expect(math.approxEqAbs(f64, log2_64(1.5), 0.584962, epsilon)); - try expect(math.approxEqAbs(f64, log2_64(37.45), 5.226894, epsilon)); - try expect(math.approxEqAbs(f64, log2_64(123123.234375), 16.909744, epsilon)); -} - -test "math.log2_32.special" { - try expect(math.isPositiveInf(log2_32(math.inf(f32)))); - try expect(math.isNegativeInf(log2_32(0.0))); - try expect(math.isNan(log2_32(-1.0))); - try expect(math.isNan(log2_32(math.nan(f32)))); -} - -test "math.log2_64.special" { - try expect(math.isPositiveInf(log2_64(math.inf(f64)))); - try expect(math.isNegativeInf(log2_64(0.0))); - try expect(math.isNan(log2_64(-1.0))); - try expect(math.isNan(log2_64(math.nan(f64)))); +test "log2" { + try expect(log2(@as(f32, 0.2)) == @log2(0.2)); + try expect(log2(@as(f64, 0.2)) == @log2(0.2)); } diff --git a/lib/std/math/nan.zig b/lib/std/math/nan.zig index 634af1f0d6..329f67b74e 100644 --- a/lib/std/math/nan.zig +++ b/lib/std/math/nan.zig @@ -2,13 +2,13 @@ const math = @import("../math.zig"); /// Returns the nan representation for type T. pub fn nan(comptime T: type) T { - return switch (T) { - f16 => math.nan_f16, - f32 => math.nan_f32, - f64 => math.nan_f64, - f80 => math.nan_f80, - f128 => math.nan_f128, - else => @compileError("nan not implemented for " ++ @typeName(T)), + return switch (@typeInfo(T).Float.bits) { + 16 => math.nan_f16, + 32 => math.nan_f32, + 64 => math.nan_f64, + 80 => math.nan_f80, + 128 => math.nan_f128, + else => @compileError("unreachable"), }; } @@ -16,12 +16,12 @@ pub fn nan(comptime T: type) T { pub fn snan(comptime T: type) T { // Note: A signalling nan is identical to a standard right now by may have a different bit // representation in the future when required. - return switch (T) { - f16 => @bitCast(f16, math.nan_u16), - f32 => @bitCast(f32, math.nan_u32), - f64 => @bitCast(f64, math.nan_u64), - f80 => @bitCast(f80, math.nan_u80), - f128 => @bitCast(f128, math.nan_u128), - else => @compileError("snan not implemented for " ++ @typeName(T)), + return switch (@typeInfo(T).Float.bits) { + 16 => math.nan_u16, + 32 => math.nan_u32, + 64 => math.nan_u64, + 80 => math.nan_u80, + 128 => math.nan_u128, + else => @compileError("unreachable"), }; } diff --git a/lib/std/math/pow.zig b/lib/std/math/pow.zig index 040abf9a44..48c6636926 100644 --- a/lib/std/math/pow.zig +++ b/lib/std/math/pow.zig @@ -82,7 +82,7 @@ pub fn pow(comptime T: type, x: T, y: T) T { } // pow(x, +inf) = +0 for |x| < 1 // pow(x, -inf) = +0 for |x| > 1 - else if ((math.fabs(x) < 1) == math.isPositiveInf(y)) { + else if ((@fabs(x) < 1) == math.isPositiveInf(y)) { return 0; } // pow(x, -inf) = +inf for |x| < 1 @@ -108,14 +108,14 @@ pub fn pow(comptime T: type, x: T, y: T) T { // special case sqrt if (y == 0.5) { - return math.sqrt(x); + return @sqrt(x); } if (y == -0.5) { - return 1 / math.sqrt(x); + return 1 / @sqrt(x); } - const r1 = math.modf(math.fabs(y)); + const r1 = math.modf(@fabs(y)); var yi = r1.ipart; var yf = r1.fpart; @@ -123,7 +123,7 @@ pub fn pow(comptime T: type, x: T, y: T) T { return math.nan(T); } if (yi >= 1 << (@typeInfo(T).Float.bits - 1)) { - return math.exp(y * math.ln(x)); + return @exp(y * @log(x)); } // a = a1 * 2^ae @@ -136,7 +136,7 @@ pub fn pow(comptime T: type, x: T, y: T) T { yf -= 1; yi += 1; } - a1 = math.exp(yf * math.ln(x)); + a1 = @exp(yf * @log(x)); } // a *= x^yi diff --git a/lib/std/math/round.zig b/lib/std/math/round.zig deleted file mode 100644 index be33a9cfbd..0000000000 --- a/lib/std/math/round.zig +++ /dev/null @@ -1,185 +0,0 @@ -// Ported from musl, which is licensed under the MIT license: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/roundf.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/round.c - -const expect = std.testing.expect; -const std = @import("../std.zig"); -const math = std.math; - -/// Returns x rounded to the nearest integer, rounding half away from zero. -/// -/// Special Cases: -/// - round(+-0) = +-0 -/// - round(+-inf) = +-inf -/// - round(nan) = nan -pub fn round(x: anytype) @TypeOf(x) { - const T = @TypeOf(x); - return switch (T) { - f32 => round32(x), - f64 => round64(x), - f128 => round128(x), - - // TODO this is not correct for some targets - c_longdouble => @floatCast(c_longdouble, round128(x)), - - else => @compileError("round not implemented for " ++ @typeName(T)), - }; -} - -fn round32(x_: f32) f32 { - const f32_toint = 1.0 / math.floatEps(f32); - - var x = x_; - const u = @bitCast(u32, x); - const e = (u >> 23) & 0xFF; - var y: f32 = undefined; - - if (e >= 0x7F + 23) { - return x; - } - if (u >> 31 != 0) { - x = -x; - } - if (e < 0x7F - 1) { - math.doNotOptimizeAway(x + f32_toint); - return 0 * @bitCast(f32, u); - } - - y = x + f32_toint - f32_toint - x; - if (y > 0.5) { - y = y + x - 1; - } else if (y <= -0.5) { - y = y + x + 1; - } else { - y = y + x; - } - - if (u >> 31 != 0) { - return -y; - } else { - return y; - } -} - -fn round64(x_: f64) f64 { - const f64_toint = 1.0 / math.floatEps(f64); - - var x = x_; - const u = @bitCast(u64, x); - const e = (u >> 52) & 0x7FF; - var y: f64 = undefined; - - if (e >= 0x3FF + 52) { - return x; - } - if (u >> 63 != 0) { - x = -x; - } - if (e < 0x3ff - 1) { - math.doNotOptimizeAway(x + f64_toint); - return 0 * @bitCast(f64, u); - } - - y = x + f64_toint - f64_toint - x; - if (y > 0.5) { - y = y + x - 1; - } else if (y <= -0.5) { - y = y + x + 1; - } else { - y = y + x; - } - - if (u >> 63 != 0) { - return -y; - } else { - return y; - } -} - -fn round128(x_: f128) f128 { - const f128_toint = 1.0 / math.floatEps(f128); - - var x = x_; - const u = @bitCast(u128, x); - const e = (u >> 112) & 0x7FFF; - var y: f128 = undefined; - - if (e >= 0x3FFF + 112) { - return x; - } - if (u >> 127 != 0) { - x = -x; - } - if (e < 0x3FFF - 1) { - math.doNotOptimizeAway(x + f128_toint); - return 0 * @bitCast(f128, u); - } - - y = x + f128_toint - f128_toint - x; - if (y > 0.5) { - y = y + x - 1; - } else if (y <= -0.5) { - y = y + x + 1; - } else { - y = y + x; - } - - if (u >> 127 != 0) { - return -y; - } else { - return y; - } -} - -test "math.round" { - try expect(round(@as(f32, 1.3)) == round32(1.3)); - try expect(round(@as(f64, 1.3)) == round64(1.3)); - try expect(round(@as(f128, 1.3)) == round128(1.3)); -} - -test "math.round32" { - try expect(round32(1.3) == 1.0); - try expect(round32(-1.3) == -1.0); - try expect(round32(0.2) == 0.0); - try expect(round32(1.8) == 2.0); -} - -test "math.round64" { - try expect(round64(1.3) == 1.0); - try expect(round64(-1.3) == -1.0); - try expect(round64(0.2) == 0.0); - try expect(round64(1.8) == 2.0); -} - -test "math.round128" { - try expect(round128(1.3) == 1.0); - try expect(round128(-1.3) == -1.0); - try expect(round128(0.2) == 0.0); - try expect(round128(1.8) == 2.0); -} - -test "math.round32.special" { - try expect(round32(0.0) == 0.0); - try expect(round32(-0.0) == -0.0); - try expect(math.isPositiveInf(round32(math.inf(f32)))); - try expect(math.isNegativeInf(round32(-math.inf(f32)))); - try expect(math.isNan(round32(math.nan(f32)))); -} - -test "math.round64.special" { - try expect(round64(0.0) == 0.0); - try expect(round64(-0.0) == -0.0); - try expect(math.isPositiveInf(round64(math.inf(f64)))); - try expect(math.isNegativeInf(round64(-math.inf(f64)))); - try expect(math.isNan(round64(math.nan(f64)))); -} - -test "math.round128.special" { - try expect(round128(0.0) == 0.0); - try expect(round128(-0.0) == -0.0); - try expect(math.isPositiveInf(round128(math.inf(f128)))); - try expect(math.isNegativeInf(round128(-math.inf(f128)))); - try expect(math.isNan(round128(math.nan(f128)))); -} diff --git a/lib/std/math/trunc.zig b/lib/std/math/trunc.zig deleted file mode 100644 index 32bd7fb0aa..0000000000 --- a/lib/std/math/trunc.zig +++ /dev/null @@ -1,141 +0,0 @@ -// Ported from musl, which is licensed under the MIT license: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/truncf.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/trunc.c - -const std = @import("../std.zig"); -const math = std.math; -const expect = std.testing.expect; -const maxInt = std.math.maxInt; - -/// Returns the integer value of x. -/// -/// Special Cases: -/// - trunc(+-0) = +-0 -/// - trunc(+-inf) = +-inf -/// - trunc(nan) = nan -pub fn trunc(x: anytype) @TypeOf(x) { - const T = @TypeOf(x); - return switch (T) { - f32 => trunc32(x), - f64 => trunc64(x), - f128 => trunc128(x), - - // TODO this is not correct for some targets - c_longdouble => @floatCast(c_longdouble, trunc128(x)), - - else => @compileError("trunc not implemented for " ++ @typeName(T)), - }; -} - -fn trunc32(x: f32) f32 { - const u = @bitCast(u32, x); - var e = @intCast(i32, ((u >> 23) & 0xFF)) - 0x7F + 9; - var m: u32 = undefined; - - if (e >= 23 + 9) { - return x; - } - if (e < 9) { - e = 1; - } - - m = @as(u32, maxInt(u32)) >> @intCast(u5, e); - if (u & m == 0) { - return x; - } else { - math.doNotOptimizeAway(x + 0x1p120); - return @bitCast(f32, u & ~m); - } -} - -fn trunc64(x: f64) f64 { - const u = @bitCast(u64, x); - var e = @intCast(i32, ((u >> 52) & 0x7FF)) - 0x3FF + 12; - var m: u64 = undefined; - - if (e >= 52 + 12) { - return x; - } - if (e < 12) { - e = 1; - } - - m = @as(u64, maxInt(u64)) >> @intCast(u6, e); - if (u & m == 0) { - return x; - } else { - math.doNotOptimizeAway(x + 0x1p120); - return @bitCast(f64, u & ~m); - } -} - -fn trunc128(x: f128) f128 { - const u = @bitCast(u128, x); - var e = @intCast(i32, ((u >> 112) & 0x7FFF)) - 0x3FFF + 16; - var m: u128 = undefined; - - if (e >= 112 + 16) { - return x; - } - if (e < 16) { - e = 1; - } - - m = @as(u128, maxInt(u128)) >> @intCast(u7, e); - if (u & m == 0) { - return x; - } else { - math.doNotOptimizeAway(x + 0x1p120); - return @bitCast(f128, u & ~m); - } -} - -test "math.trunc" { - try expect(trunc(@as(f32, 1.3)) == trunc32(1.3)); - try expect(trunc(@as(f64, 1.3)) == trunc64(1.3)); - try expect(trunc(@as(f128, 1.3)) == trunc128(1.3)); -} - -test "math.trunc32" { - try expect(trunc32(1.3) == 1.0); - try expect(trunc32(-1.3) == -1.0); - try expect(trunc32(0.2) == 0.0); -} - -test "math.trunc64" { - try expect(trunc64(1.3) == 1.0); - try expect(trunc64(-1.3) == -1.0); - try expect(trunc64(0.2) == 0.0); -} - -test "math.trunc128" { - try expect(trunc128(1.3) == 1.0); - try expect(trunc128(-1.3) == -1.0); - try expect(trunc128(0.2) == 0.0); -} - -test "math.trunc32.special" { - try expect(trunc32(0.0) == 0.0); // 0x3F800000 - try expect(trunc32(-0.0) == -0.0); - try expect(math.isPositiveInf(trunc32(math.inf(f32)))); - try expect(math.isNegativeInf(trunc32(-math.inf(f32)))); - try expect(math.isNan(trunc32(math.nan(f32)))); -} - -test "math.trunc64.special" { - try expect(trunc64(0.0) == 0.0); - try expect(trunc64(-0.0) == -0.0); - try expect(math.isPositiveInf(trunc64(math.inf(f64)))); - try expect(math.isNegativeInf(trunc64(-math.inf(f64)))); - try expect(math.isNan(trunc64(math.nan(f64)))); -} - -test "math.trunc128.special" { - try expect(trunc128(0.0) == 0.0); - try expect(trunc128(-0.0) == -0.0); - try expect(math.isPositiveInf(trunc128(math.inf(f128)))); - try expect(math.isNegativeInf(trunc128(-math.inf(f128)))); - try expect(math.isNan(trunc128(math.nan(f128)))); -} diff --git a/lib/std/rand/ziggurat.zig b/lib/std/rand/ziggurat.zig index 5c18d0023b..b05ce7fd73 100644 --- a/lib/std/rand/ziggurat.zig +++ b/lib/std/rand/ziggurat.zig @@ -33,7 +33,7 @@ pub fn next_f64(random: Random, comptime tables: ZigTable) f64 { }; const x = u * tables.x[i]; - const test_x = if (tables.is_symmetric) math.fabs(x) else x; + const test_x = if (tables.is_symmetric) @fabs(x) else x; // equivalent to |u| < tables.x[i+1] / tables.x[i] (or u < tables.x[i+1] / tables.x[i]) if (test_x < tables.x[i + 1]) { @@ -106,18 +106,18 @@ const norm_r = 3.6541528853610088; const norm_v = 0.00492867323399; fn norm_f(x: f64) f64 { - return math.exp(-x * x / 2.0); + return @exp(-x * x / 2.0); } fn norm_f_inv(y: f64) f64 { - return math.sqrt(-2.0 * math.ln(y)); + return @sqrt(-2.0 * @log(y)); } fn norm_zero_case(random: Random, u: f64) f64 { var x: f64 = 1; var y: f64 = 0; while (-2.0 * y < x * x) { - x = math.ln(random.float(f64)) / norm_r; - y = math.ln(random.float(f64)); + x = @log(random.float(f64)) / norm_r; + y = @log(random.float(f64)); } if (u < 0) { @@ -151,13 +151,13 @@ const exp_r = 7.69711747013104972; const exp_v = 0.0039496598225815571993; fn exp_f(x: f64) f64 { - return math.exp(-x); + return @exp(-x); } fn exp_f_inv(y: f64) f64 { - return -math.ln(y); + return -@log(y); } fn exp_zero_case(random: Random, _: f64) f64 { - return exp_r - math.ln(random.float(f64)); + return exp_r - @log(random.float(f64)); } test "exp dist sanity" { diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index dfc2020334..525bdd267d 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -12,7 +12,6 @@ const maxInt = std.math.maxInt; const native_os = builtin.os.tag; const native_arch = builtin.cpu.arch; const native_abi = builtin.abi; -const long_double_is_f128 = builtin.target.longDoubleIs(f128); const is_wasm = switch (native_arch) { .wasm32, .wasm64 => true, @@ -55,53 +54,6 @@ comptime { } else if (is_msvc) { @export(_fltused, .{ .name = "_fltused", .linkage = .Strong }); } - - @export(trunc, .{ .name = "trunc", .linkage = .Strong }); - @export(truncf, .{ .name = "truncf", .linkage = .Strong }); - @export(truncl, .{ .name = "truncl", .linkage = .Strong }); - - @export(log, .{ .name = "log", .linkage = .Strong }); - @export(logf, .{ .name = "logf", .linkage = .Strong }); - - @export(sin, .{ .name = "sin", .linkage = .Strong }); - @export(sinf, .{ .name = "sinf", .linkage = .Strong }); - - @export(cos, .{ .name = "cos", .linkage = .Strong }); - @export(cosf, .{ .name = "cosf", .linkage = .Strong }); - - @export(exp, .{ .name = "exp", .linkage = .Strong }); - @export(expf, .{ .name = "expf", .linkage = .Strong }); - - @export(exp2, .{ .name = "exp2", .linkage = .Strong }); - @export(exp2f, .{ .name = "exp2f", .linkage = .Strong }); - - @export(log2, .{ .name = "log2", .linkage = .Strong }); - @export(log2f, .{ .name = "log2f", .linkage = .Strong }); - - @export(log10, .{ .name = "log10", .linkage = .Strong }); - @export(log10f, .{ .name = "log10f", .linkage = .Strong }); - - @export(fmod, .{ .name = "fmod", .linkage = .Strong }); - @export(fmodf, .{ .name = "fmodf", .linkage = .Strong }); - - @export(sincos, .{ .name = "sincos", .linkage = .Strong }); - @export(sincosf, .{ .name = "sincosf", .linkage = .Strong }); - - @export(fabs, .{ .name = "fabs", .linkage = .Strong }); - @export(fabsf, .{ .name = "fabsf", .linkage = .Strong }); - - @export(round, .{ .name = "round", .linkage = .Strong }); - @export(roundf, .{ .name = "roundf", .linkage = .Strong }); - @export(roundl, .{ .name = "roundl", .linkage = .Strong }); - - @export(fmin, .{ .name = "fmin", .linkage = .Strong }); - @export(fminf, .{ .name = "fminf", .linkage = .Strong }); - - @export(fmax, .{ .name = "fmax", .linkage = .Strong }); - @export(fmaxf, .{ .name = "fmaxf", .linkage = .Strong }); - - @export(sqrt, .{ .name = "sqrt", .linkage = .Strong }); - @export(sqrtf, .{ .name = "sqrtf", .linkage = .Strong }); } // Avoid dragging in the runtime safety mechanisms into this .o file, @@ -352,549 +304,6 @@ test "strncmp" { try std.testing.expect(strncmp("\xff", "\x02", 1) == 253); } -fn trunc(a: f64) callconv(.C) f64 { - return math.trunc(a); -} - -fn truncf(a: f32) callconv(.C) f32 { - return math.trunc(a); -} - -fn truncl(a: c_longdouble) callconv(.C) c_longdouble { - if (!long_double_is_f128) { - @panic("TODO implement this"); - } - return math.trunc(a); -} - -fn log(a: f64) callconv(.C) f64 { - return math.ln(a); -} - -fn logf(a: f32) callconv(.C) f32 { - return math.ln(a); -} - -fn sin(a: f64) callconv(.C) f64 { - return math.sin(a); -} - -fn sinf(a: f32) callconv(.C) f32 { - return math.sin(a); -} - -fn cos(a: f64) callconv(.C) f64 { - return math.cos(a); -} - -fn cosf(a: f32) callconv(.C) f32 { - return math.cos(a); -} - -fn exp(a: f64) callconv(.C) f64 { - return math.exp(a); -} - -fn expf(a: f32) callconv(.C) f32 { - return math.exp(a); -} - -fn exp2(a: f64) callconv(.C) f64 { - return math.exp2(a); -} - -fn exp2f(a: f32) callconv(.C) f32 { - return math.exp2(a); -} - -fn log2(a: f64) callconv(.C) f64 { - return math.log2(a); -} - -fn log2f(a: f32) callconv(.C) f32 { - return math.log2(a); -} - -fn log10(a: f64) callconv(.C) f64 { - return math.log10(a); -} - -fn log10f(a: f32) callconv(.C) f32 { - return math.log10(a); -} - -fn fmodf(x: f32, y: f32) callconv(.C) f32 { - return generic_fmod(f32, x, y); -} -fn fmod(x: f64, y: f64) callconv(.C) f64 { - return generic_fmod(f64, x, y); -} - -fn generic_fmod(comptime T: type, x: T, y: T) T { - @setRuntimeSafety(false); - - const bits = @typeInfo(T).Float.bits; - const uint = std.meta.Int(.unsigned, bits); - const log2uint = math.Log2Int(uint); - const digits = if (T == f32) 23 else 52; - const exp_bits = if (T == f32) 9 else 12; - const bits_minus_1 = bits - 1; - const mask = if (T == f32) 0xff else 0x7ff; - var ux = @bitCast(uint, x); - var uy = @bitCast(uint, y); - var ex = @intCast(i32, (ux >> digits) & mask); - var ey = @intCast(i32, (uy >> digits) & mask); - const sx = if (T == f32) @intCast(u32, ux & 0x80000000) else @intCast(i32, ux >> bits_minus_1); - var i: uint = undefined; - - if (uy << 1 == 0 or isNan(@bitCast(T, uy)) or ex == mask) - return (x * y) / (x * y); - - if (ux << 1 <= uy << 1) { - if (ux << 1 == uy << 1) - return 0 * x; - return x; - } - - // normalize x and y - if (ex == 0) { - i = ux << exp_bits; - while (i >> bits_minus_1 == 0) : ({ - ex -= 1; - i <<= 1; - }) {} - ux <<= @intCast(log2uint, @bitCast(u32, -ex + 1)); - } else { - ux &= maxInt(uint) >> exp_bits; - ux |= 1 << digits; - } - if (ey == 0) { - i = uy << exp_bits; - while (i >> bits_minus_1 == 0) : ({ - ey -= 1; - i <<= 1; - }) {} - uy <<= @intCast(log2uint, @bitCast(u32, -ey + 1)); - } else { - uy &= maxInt(uint) >> exp_bits; - uy |= 1 << digits; - } - - // x mod y - while (ex > ey) : (ex -= 1) { - i = ux -% uy; - if (i >> bits_minus_1 == 0) { - if (i == 0) - return 0 * x; - ux = i; - } - ux <<= 1; - } - i = ux -% uy; - if (i >> bits_minus_1 == 0) { - if (i == 0) - return 0 * x; - ux = i; - } - while (ux >> digits == 0) : ({ - ux <<= 1; - ex -= 1; - }) {} - - // scale result up - if (ex > 0) { - ux -%= 1 << digits; - ux |= @as(uint, @bitCast(u32, ex)) << digits; - } else { - ux >>= @intCast(log2uint, @bitCast(u32, -ex + 1)); - } - if (T == f32) { - ux |= sx; - } else { - ux |= @intCast(uint, sx) << bits_minus_1; - } - return @bitCast(T, ux); -} - -test "fmod, fmodf" { - inline for ([_]type{ f32, f64 }) |T| { - const nan_val = math.nan(T); - const inf_val = math.inf(T); - - try std.testing.expect(isNan(generic_fmod(T, nan_val, 1.0))); - try std.testing.expect(isNan(generic_fmod(T, 1.0, nan_val))); - try std.testing.expect(isNan(generic_fmod(T, inf_val, 1.0))); - try std.testing.expect(isNan(generic_fmod(T, 0.0, 0.0))); - try std.testing.expect(isNan(generic_fmod(T, 1.0, 0.0))); - - try std.testing.expectEqual(@as(T, 0.0), generic_fmod(T, 0.0, 2.0)); - try std.testing.expectEqual(@as(T, -0.0), generic_fmod(T, -0.0, 2.0)); - - try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, 10.0)); - try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, -10.0)); - try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, 10.0)); - try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, -10.0)); - } -} - -fn sincos(a: f64, r_sin: *f64, r_cos: *f64) callconv(.C) void { - r_sin.* = math.sin(a); - r_cos.* = math.cos(a); -} - -fn sincosf(a: f32, r_sin: *f32, r_cos: *f32) callconv(.C) void { - r_sin.* = math.sin(a); - r_cos.* = math.cos(a); -} - -fn fabs(a: f64) callconv(.C) f64 { - return math.fabs(a); -} - -fn fabsf(a: f32) callconv(.C) f32 { - return math.fabs(a); -} - -fn roundf(a: f32) callconv(.C) f32 { - return math.round(a); -} - -fn round(a: f64) callconv(.C) f64 { - return math.round(a); -} - -fn roundl(a: c_longdouble) callconv(.C) c_longdouble { - if (!long_double_is_f128) { - @panic("TODO implement this"); - } - return math.round(a); -} - -fn fminf(x: f32, y: f32) callconv(.C) f32 { - return generic_fmin(f32, x, y); -} - -fn fmin(x: f64, y: f64) callconv(.C) f64 { - return generic_fmin(f64, x, y); -} - -fn generic_fmin(comptime T: type, x: T, y: T) T { - if (isNan(x)) - return y; - if (isNan(y)) - return x; - return if (x < y) x else y; -} - -test "fmin, fminf" { - inline for ([_]type{ f32, f64 }) |T| { - const nan_val = math.nan(T); - - try std.testing.expect(isNan(generic_fmin(T, nan_val, nan_val))); - try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, nan_val, 1.0)); - try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, nan_val)); - - try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, 10.0)); - try std.testing.expectEqual(@as(T, -1.0), generic_fmin(T, 1.0, -1.0)); - } -} - -fn fmaxf(x: f32, y: f32) callconv(.C) f32 { - return generic_fmax(f32, x, y); -} - -fn fmax(x: f64, y: f64) callconv(.C) f64 { - return generic_fmax(f64, x, y); -} - -fn generic_fmax(comptime T: type, x: T, y: T) T { - if (isNan(x)) - return y; - if (isNan(y)) - return x; - return if (x < y) y else x; -} - -test "fmax, fmaxf" { - inline for ([_]type{ f32, f64 }) |T| { - const nan_val = math.nan(T); - - try std.testing.expect(isNan(generic_fmax(T, nan_val, nan_val))); - try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, nan_val, 1.0)); - try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, nan_val)); - - try std.testing.expectEqual(@as(T, 10.0), generic_fmax(T, 1.0, 10.0)); - try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, -1.0)); - } -} - -// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound -// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are -// potentially some edge cases remaining that are not handled in the same way. -fn sqrt(x: f64) callconv(.C) f64 { - const tiny: f64 = 1.0e-300; - const sign: u32 = 0x80000000; - const u = @bitCast(u64, x); - - var ix0 = @intCast(u32, u >> 32); - var ix1 = @intCast(u32, u & 0xFFFFFFFF); - - // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan - if (ix0 & 0x7FF00000 == 0x7FF00000) { - return x * x + x; - } - - // sqrt(+-0) = +-0 - if (x == 0.0) { - return x; - } - // sqrt(-ve) = snan - if (ix0 & sign != 0) { - return math.snan(f64); - } - - // normalize x - var m = @intCast(i32, ix0 >> 20); - if (m == 0) { - // subnormal - while (ix0 == 0) { - m -= 21; - ix0 |= ix1 >> 11; - ix1 <<= 21; - } - - // subnormal - var i: u32 = 0; - while (ix0 & 0x00100000 == 0) : (i += 1) { - ix0 <<= 1; - } - m -= @intCast(i32, i) - 1; - ix0 |= ix1 >> @intCast(u5, 32 - i); - ix1 <<= @intCast(u5, i); - } - - // unbias exponent - m -= 1023; - ix0 = (ix0 & 0x000FFFFF) | 0x00100000; - if (m & 1 != 0) { - ix0 += ix0 + (ix1 >> 31); - ix1 = ix1 +% ix1; - } - m >>= 1; - - // sqrt(x) bit by bit - ix0 += ix0 + (ix1 >> 31); - ix1 = ix1 +% ix1; - - var q: u32 = 0; - var q1: u32 = 0; - var s0: u32 = 0; - var s1: u32 = 0; - var r: u32 = 0x00200000; - var t: u32 = undefined; - var t1: u32 = undefined; - - while (r != 0) { - t = s0 +% r; - if (t <= ix0) { - s0 = t + r; - ix0 -= t; - q += r; - } - ix0 = ix0 +% ix0 +% (ix1 >> 31); - ix1 = ix1 +% ix1; - r >>= 1; - } - - r = sign; - while (r != 0) { - t1 = s1 +% r; - t = s0; - if (t < ix0 or (t == ix0 and t1 <= ix1)) { - s1 = t1 +% r; - if (t1 & sign == sign and s1 & sign == 0) { - s0 += 1; - } - ix0 -= t; - if (ix1 < t1) { - ix0 -= 1; - } - ix1 = ix1 -% t1; - q1 += r; - } - ix0 = ix0 +% ix0 +% (ix1 >> 31); - ix1 = ix1 +% ix1; - r >>= 1; - } - - // rounding direction - if (ix0 | ix1 != 0) { - var z = 1.0 - tiny; // raise inexact - if (z >= 1.0) { - z = 1.0 + tiny; - if (q1 == 0xFFFFFFFF) { - q1 = 0; - q += 1; - } else if (z > 1.0) { - if (q1 == 0xFFFFFFFE) { - q += 1; - } - q1 += 2; - } else { - q1 += q1 & 1; - } - } - } - - ix0 = (q >> 1) + 0x3FE00000; - ix1 = q1 >> 1; - if (q & 1 != 0) { - ix1 |= 0x80000000; - } - - // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same - // behaviour at least. - var iix0 = @intCast(i32, ix0); - iix0 = iix0 +% (m << 20); - - const uz = (@intCast(u64, iix0) << 32) | ix1; - return @bitCast(f64, uz); -} - -test "sqrt" { - const V = [_]f64{ - 0.0, - 4.089288054930154, - 7.538757127071935, - 8.97780793672623, - 5.304443821913729, - 5.682408965311888, - 0.5846878579110049, - 3.650338664297043, - 0.3178091951800732, - 7.1505232436382835, - 3.6589165881946464, - }; - - // Note that @sqrt will either generate the sqrt opcode (if supported by the - // target ISA) or a call to `sqrtf` otherwise. - for (V) |val| - try std.testing.expectEqual(@sqrt(val), sqrt(val)); -} - -test "sqrt special" { - try std.testing.expect(std.math.isPositiveInf(sqrt(std.math.inf(f64)))); - try std.testing.expect(sqrt(0.0) == 0.0); - try std.testing.expect(sqrt(-0.0) == -0.0); - try std.testing.expect(isNan(sqrt(-1.0))); - try std.testing.expect(isNan(sqrt(std.math.nan(f64)))); -} - -fn sqrtf(x: f32) callconv(.C) f32 { - const tiny: f32 = 1.0e-30; - const sign: i32 = @bitCast(i32, @as(u32, 0x80000000)); - var ix: i32 = @bitCast(i32, x); - - if ((ix & 0x7F800000) == 0x7F800000) { - return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan - } - - // zero - if (ix <= 0) { - if (ix & ~sign == 0) { - return x; // sqrt (+-0) = +-0 - } - if (ix < 0) { - return math.snan(f32); - } - } - - // normalize - var m = ix >> 23; - if (m == 0) { - // subnormal - var i: i32 = 0; - while (ix & 0x00800000 == 0) : (i += 1) { - ix <<= 1; - } - m -= i - 1; - } - - m -= 127; // unbias exponent - ix = (ix & 0x007FFFFF) | 0x00800000; - - if (m & 1 != 0) { // odd m, double x to even - ix += ix; - } - - m >>= 1; // m = [m / 2] - - // sqrt(x) bit by bit - ix += ix; - var q: i32 = 0; // q = sqrt(x) - var s: i32 = 0; - var r: i32 = 0x01000000; // r = moving bit right -> left - - while (r != 0) { - const t = s + r; - if (t <= ix) { - s = t + r; - ix -= t; - q += r; - } - ix += ix; - r >>= 1; - } - - // floating add to find rounding direction - if (ix != 0) { - var z = 1.0 - tiny; // inexact - if (z >= 1.0) { - z = 1.0 + tiny; - if (z > 1.0) { - q += 2; - } else { - if (q & 1 != 0) { - q += 1; - } - } - } - } - - ix = (q >> 1) + 0x3f000000; - ix += m << 23; - return @bitCast(f32, ix); -} - -test "sqrtf" { - const V = [_]f32{ - 0.0, - 4.089288054930154, - 7.538757127071935, - 8.97780793672623, - 5.304443821913729, - 5.682408965311888, - 0.5846878579110049, - 3.650338664297043, - 0.3178091951800732, - 7.1505232436382835, - 3.6589165881946464, - }; - - // Note that @sqrt will either generate the sqrt opcode (if supported by the - // target ISA) or a call to `sqrtf` otherwise. - for (V) |val| - try std.testing.expectEqual(@sqrt(val), sqrtf(val)); -} - -test "sqrtf special" { - try std.testing.expect(std.math.isPositiveInf(sqrtf(std.math.inf(f32)))); - try std.testing.expect(sqrtf(0.0) == 0.0); - try std.testing.expect(sqrtf(-0.0) == -0.0); - try std.testing.expect(isNan(sqrtf(-1.0))); - try std.testing.expect(isNan(sqrtf(std.math.nan(f32)))); -} - // TODO we should be able to put this directly in std/linux/x86_64.zig but // it causes a segfault in release mode. this is a workaround of calling it // across .o file boundaries. fix comptime @ptrCast of nakedcc functions. diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 93e0ffbe1a..dccb9264bd 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -19,9 +19,6 @@ const strong_linkage = if (is_test) else std.builtin.GlobalLinkage.Strong; -const long_double_is_f80 = builtin.target.longDoubleIs(f80); -const long_double_is_f128 = builtin.target.longDoubleIs(f128); - comptime { // These files do their own comptime exporting logic. _ = @import("compiler_rt/atomics.zig"); @@ -726,42 +723,25 @@ comptime { @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage }); } - if (!is_test) { - if (long_double_is_f80) { - @export(fmodx, .{ .name = "fmodl", .linkage = linkage }); - } else if (long_double_is_f128) { - @export(fmodq, .{ .name = "fmodl", .linkage = linkage }); - } else { - @export(fmodl, .{ .name = "fmodl", .linkage = linkage }); - } - if (long_double_is_f80 or builtin.zig_backend == .stage1) { - // TODO: https://github.com/ziglang/zig/issues/11161 - @export(fmodx, .{ .name = "fmodx", .linkage = linkage }); - } - @export(fmodq, .{ .name = "fmodq", .linkage = linkage }); - - @export(floorf, .{ .name = "floorf", .linkage = linkage }); - @export(floor, .{ .name = "floor", .linkage = linkage }); - @export(floorl, .{ .name = "floorl", .linkage = linkage }); - - @export(ceilf, .{ .name = "ceilf", .linkage = linkage }); - @export(ceil, .{ .name = "ceil", .linkage = linkage }); - @export(ceill, .{ .name = "ceill", .linkage = linkage }); - - @export(fma, .{ .name = "fma", .linkage = linkage }); - @export(fmaf, .{ .name = "fmaf", .linkage = linkage }); - @export(fmal, .{ .name = "fmal", .linkage = linkage }); - if (long_double_is_f80) { - @export(fmal, .{ .name = "__fmax", .linkage = linkage }); - } else { - @export(__fmax, .{ .name = "__fmax", .linkage = linkage }); - } - if (long_double_is_f128) { - @export(fmal, .{ .name = "fmaq", .linkage = linkage }); - } else { - @export(fmaq, .{ .name = "fmaq", .linkage = linkage }); - } - } + mathExport("ceil", @import("./compiler_rt/ceil.zig")); + mathExport("cos", @import("./compiler_rt/cos.zig")); + mathExport("exp", @import("./compiler_rt/exp.zig")); + mathExport("exp2", @import("./compiler_rt/exp2.zig")); + mathExport("fabs", @import("./compiler_rt/fabs.zig")); + mathExport("floor", @import("./compiler_rt/floor.zig")); + mathExport("fma", @import("./compiler_rt/fma.zig")); + mathExport("fmax", @import("./compiler_rt/fmax.zig")); + mathExport("fmin", @import("./compiler_rt/fmin.zig")); + mathExport("fmod", @import("./compiler_rt/fmod.zig")); + mathExport("log", @import("./compiler_rt/log.zig")); + mathExport("log10", @import("./compiler_rt/log10.zig")); + mathExport("log2", @import("./compiler_rt/log2.zig")); + mathExport("round", @import("./compiler_rt/round.zig")); + mathExport("sin", @import("./compiler_rt/sin.zig")); + mathExport("sincos", @import("./compiler_rt/sincos.zig")); + mathExport("sqrt", @import("./compiler_rt/sqrt.zig")); + mathExport("tan", @import("./compiler_rt/tan.zig")); + mathExport("trunc", @import("./compiler_rt/trunc.zig")); if (arch.isSPARC()) { // SPARC systems use a different naming scheme @@ -842,63 +822,44 @@ comptime { @export(__unordtf2, .{ .name = "__unordkf2", .linkage = linkage }); // LLVM PPC backend lowers f128 fma to `fmaf128`. - @export(fmal, .{ .name = "fmaf128", .linkage = linkage }); + const fmaq = @import("./compiler_rt/fma.zig").fmaq; + @export(fmaq, .{ .name = "fmaf128", .linkage = linkage }); } } -const math = std.math; +inline fn mathExport(double_name: []const u8, comptime import: type) void { + const half_name = "__" ++ double_name ++ "h"; + const half_fn = @field(import, half_name); + const float_name = double_name ++ "f"; + const float_fn = @field(import, float_name); + const double_fn = @field(import, double_name); + const long_double_name = double_name ++ "l"; + const xf80_name = "__" ++ double_name ++ "x"; + const xf80_fn = @field(import, xf80_name); + const quad_name = double_name ++ "q"; + const quad_fn = @field(import, quad_name); -fn fmaf(a: f32, b: f32, c: f32) callconv(.C) f32 { - return math.fma(f32, a, b, c); -} -fn fma(a: f64, b: f64, c: f64) callconv(.C) f64 { - return math.fma(f64, a, b, c); -} -fn __fmax(a: f80, b: f80, c: f80) callconv(.C) f80 { - return math.fma(f80, a, b, c); -} -fn fmaq(a: f128, b: f128, c: f128) callconv(.C) f128 { - return math.fma(f128, a, b, c); -} -fn fmal(a: c_longdouble, b: c_longdouble, c: c_longdouble) callconv(.C) c_longdouble { - return math.fma(c_longdouble, a, b, c); -} + @export(half_fn, .{ .name = half_name, .linkage = linkage }); + @export(float_fn, .{ .name = float_name, .linkage = linkage }); + @export(double_fn, .{ .name = double_name, .linkage = linkage }); + @export(xf80_fn, .{ .name = xf80_name, .linkage = linkage }); + @export(quad_fn, .{ .name = quad_name, .linkage = linkage }); -// TODO add intrinsics for these (and probably the double version too) -// and have the math stuff use the intrinsic. same as @mod and @rem -fn floorf(x: f32) callconv(.C) f32 { - return math.floor(x); -} -fn floor(x: f64) callconv(.C) f64 { - return math.floor(x); -} -fn floorl(x: c_longdouble) callconv(.C) c_longdouble { - if (!long_double_is_f128) { - @panic("TODO implement this"); + const pairs = .{ + .{ f16, half_fn }, + .{ f32, float_fn }, + .{ f64, double_fn }, + .{ f80, xf80_fn }, + .{ f128, quad_fn }, + }; + + inline for (pairs) |pair| { + const F = pair[0]; + const func = pair[1]; + if (builtin.target.longDoubleIs(F)) { + @export(func, .{ .name = long_double_name, .linkage = linkage }); + } } - return math.floor(x); -} - -fn ceilf(x: f32) callconv(.C) f32 { - return math.ceil(x); -} -fn ceil(x: f64) callconv(.C) f64 { - return math.ceil(x); -} -fn ceill(x: c_longdouble) callconv(.C) c_longdouble { - if (!long_double_is_f128) { - @panic("TODO implement this"); - } - return math.ceil(x); -} - -const fmodq = @import("compiler_rt/fmodq.zig").fmodq; -const fmodx = @import("compiler_rt/fmodx.zig").fmodx; -fn fmodl(x: c_longdouble, y: c_longdouble) callconv(.C) c_longdouble { - if (!long_double_is_f128) { - @panic("TODO implement this"); - } - return @floatCast(c_longdouble, fmodq(x, y)); } // Avoid dragging in the runtime safety mechanisms into this .o file, diff --git a/lib/std/math/ceil.zig b/lib/std/special/compiler_rt/ceil.zig similarity index 52% rename from lib/std/math/ceil.zig rename to lib/std/special/compiler_rt/ceil.zig index 686be8e58d..c7087a2c3a 100644 --- a/lib/std/math/ceil.zig +++ b/lib/std/special/compiler_rt/ceil.zig @@ -4,31 +4,16 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c // https://git.musl-libc.org/cgit/musl/tree/src/math/ceil.c -const std = @import("../std.zig"); +const std = @import("std"); const math = std.math; const expect = std.testing.expect; -/// Returns the least integer value greater than of equal to x. -/// -/// Special Cases: -/// - ceil(+-0) = +-0 -/// - ceil(+-inf) = +-inf -/// - ceil(nan) = nan -pub fn ceil(x: anytype) @TypeOf(x) { - const T = @TypeOf(x); - return switch (T) { - f32 => ceil32(x), - f64 => ceil64(x), - f128 => ceil128(x), - - // TODO this is not correct for some targets - c_longdouble => @floatCast(c_longdouble, ceil128(x)), - - else => @compileError("ceil not implemented for " ++ @typeName(T)), - }; +pub fn __ceilh(x: f16) callconv(.C) f16 { + // TODO: more efficient implementation + return @floatCast(f16, ceilf(x)); } -fn ceil32(x: f32) f32 { +pub fn ceilf(x: f32) callconv(.C) f32 { var u = @bitCast(u32, x); var e = @intCast(i32, (u >> 23) & 0xFF) - 0x7F; var m: u32 = undefined; @@ -61,7 +46,7 @@ fn ceil32(x: f32) f32 { } } -fn ceil64(x: f64) f64 { +pub fn ceil(x: f64) callconv(.C) f64 { const f64_toint = 1.0 / math.floatEps(f64); const u = @bitCast(u64, x); @@ -92,7 +77,12 @@ fn ceil64(x: f64) f64 { } } -fn ceil128(x: f128) f128 { +pub fn __ceilx(x: f80) callconv(.C) f80 { + // TODO: more efficient implementation + return @floatCast(f80, ceilq(x)); +} + +pub fn ceilq(x: f128) callconv(.C) f128 { const f128_toint = 1.0 / math.floatEps(f128); const u = @bitCast(u128, x); @@ -121,50 +111,44 @@ fn ceil128(x: f128) f128 { } } -test "math.ceil" { - try expect(ceil(@as(f32, 0.0)) == ceil32(0.0)); - try expect(ceil(@as(f64, 0.0)) == ceil64(0.0)); - try expect(ceil(@as(f128, 0.0)) == ceil128(0.0)); +test "ceil32" { + try expect(ceilf(1.3) == 2.0); + try expect(ceilf(-1.3) == -1.0); + try expect(ceilf(0.2) == 1.0); } -test "math.ceil32" { - try expect(ceil32(1.3) == 2.0); - try expect(ceil32(-1.3) == -1.0); - try expect(ceil32(0.2) == 1.0); +test "ceil64" { + try expect(ceil(1.3) == 2.0); + try expect(ceil(-1.3) == -1.0); + try expect(ceil(0.2) == 1.0); } -test "math.ceil64" { - try expect(ceil64(1.3) == 2.0); - try expect(ceil64(-1.3) == -1.0); - try expect(ceil64(0.2) == 1.0); +test "ceil128" { + try expect(ceilq(1.3) == 2.0); + try expect(ceilq(-1.3) == -1.0); + try expect(ceilq(0.2) == 1.0); } -test "math.ceil128" { - try expect(ceil128(1.3) == 2.0); - try expect(ceil128(-1.3) == -1.0); - try expect(ceil128(0.2) == 1.0); +test "ceil32.special" { + try expect(ceilf(0.0) == 0.0); + try expect(ceilf(-0.0) == -0.0); + try expect(math.isPositiveInf(ceilf(math.inf(f32)))); + try expect(math.isNegativeInf(ceilf(-math.inf(f32)))); + try expect(math.isNan(ceilf(math.nan(f32)))); } -test "math.ceil32.special" { - try expect(ceil32(0.0) == 0.0); - try expect(ceil32(-0.0) == -0.0); - try expect(math.isPositiveInf(ceil32(math.inf(f32)))); - try expect(math.isNegativeInf(ceil32(-math.inf(f32)))); - try expect(math.isNan(ceil32(math.nan(f32)))); +test "ceil64.special" { + try expect(ceil(0.0) == 0.0); + try expect(ceil(-0.0) == -0.0); + try expect(math.isPositiveInf(ceil(math.inf(f64)))); + try expect(math.isNegativeInf(ceil(-math.inf(f64)))); + try expect(math.isNan(ceil(math.nan(f64)))); } -test "math.ceil64.special" { - try expect(ceil64(0.0) == 0.0); - try expect(ceil64(-0.0) == -0.0); - try expect(math.isPositiveInf(ceil64(math.inf(f64)))); - try expect(math.isNegativeInf(ceil64(-math.inf(f64)))); - try expect(math.isNan(ceil64(math.nan(f64)))); -} - -test "math.ceil128.special" { - try expect(ceil128(0.0) == 0.0); - try expect(ceil128(-0.0) == -0.0); - try expect(math.isPositiveInf(ceil128(math.inf(f128)))); - try expect(math.isNegativeInf(ceil128(-math.inf(f128)))); - try expect(math.isNan(ceil128(math.nan(f128)))); +test "ceil128.special" { + try expect(ceilq(0.0) == 0.0); + try expect(ceilq(-0.0) == -0.0); + try expect(math.isPositiveInf(ceilq(math.inf(f128)))); + try expect(math.isNegativeInf(ceilq(-math.inf(f128)))); + try expect(math.isNan(ceilq(math.nan(f128)))); } diff --git a/lib/std/math/cos.zig b/lib/std/special/compiler_rt/cos.zig similarity index 52% rename from lib/std/math/cos.zig rename to lib/std/special/compiler_rt/cos.zig index 22bae0daee..295f6a47ea 100644 --- a/lib/std/math/cos.zig +++ b/lib/std/special/compiler_rt/cos.zig @@ -1,32 +1,17 @@ -// Ported from musl, which is licensed under the MIT license: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/cosf.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/cos.c - -const std = @import("../std.zig"); +const std = @import("std"); const math = std.math; const expect = std.testing.expect; -const kernel = @import("__trig.zig"); -const __rem_pio2 = @import("__rem_pio2.zig").__rem_pio2; -const __rem_pio2f = @import("__rem_pio2f.zig").__rem_pio2f; +const kernel = @import("trig.zig"); +const rem_pio2 = @import("rem_pio2.zig").rem_pio2; +const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; -/// Returns the cosine of the radian value x. -/// -/// Special Cases: -/// - cos(+-inf) = nan -/// - cos(nan) = nan -pub fn cos(x: anytype) @TypeOf(x) { - const T = @TypeOf(x); - return switch (T) { - f32 => cos32(x), - f64 => cos64(x), - else => @compileError("cos not implemented for " ++ @typeName(T)), - }; +pub fn __cosh(a: f16) callconv(.C) f16 { + // TODO: more efficient implementation + return @floatCast(f16, cosf(a)); } -fn cos32(x: f32) f32 { +pub fn cosf(x: f32) callconv(.C) f32 { // Small multiples of pi/2 rounded to double precision. const c1pio2: f64 = 1.0 * math.pi / 2.0; // 0x3FF921FB, 0x54442D18 const c2pio2: f64 = 2.0 * math.pi / 2.0; // 0x400921FB, 0x54442D18 @@ -74,7 +59,7 @@ fn cos32(x: f32) f32 { } var y: f64 = undefined; - const n = __rem_pio2f(x, &y); + const n = rem_pio2f(x, &y); return switch (n & 3) { 0 => kernel.__cosdf(y), 1 => kernel.__sindf(-y), @@ -83,7 +68,7 @@ fn cos32(x: f32) f32 { }; } -fn cos64(x: f64) f64 { +pub fn cos(x: f64) callconv(.C) f64 { var ix = @bitCast(u64, x) >> 32; ix &= 0x7fffffff; @@ -103,7 +88,7 @@ fn cos64(x: f64) f64 { } var y: [2]f64 = undefined; - const n = __rem_pio2(x, &y); + const n = rem_pio2(x, &y); return switch (n & 3) { 0 => kernel.__cos(y[0], y[1]), 1 => -kernel.__sin(y[0], y[1], 1), @@ -112,43 +97,48 @@ fn cos64(x: f64) f64 { }; } -test "math.cos" { - try expect(cos(@as(f32, 0.0)) == cos32(0.0)); - try expect(cos(@as(f64, 0.0)) == cos64(0.0)); +pub fn __cosx(a: f80) callconv(.C) f80 { + // TODO: more efficient implementation + return @floatCast(f80, cosq(a)); } -test "math.cos32" { +pub fn cosq(a: f128) callconv(.C) f128 { + // TODO: more correct implementation + return cos(@floatCast(f64, a)); +} + +test "cos32" { const epsilon = 0.00001; - try expect(math.approxEqAbs(f32, cos32(0.0), 1.0, epsilon)); - try expect(math.approxEqAbs(f32, cos32(0.2), 0.980067, epsilon)); - try expect(math.approxEqAbs(f32, cos32(0.8923), 0.627623, epsilon)); - try expect(math.approxEqAbs(f32, cos32(1.5), 0.070737, epsilon)); - try expect(math.approxEqAbs(f32, cos32(-1.5), 0.070737, epsilon)); - try expect(math.approxEqAbs(f32, cos32(37.45), 0.969132, epsilon)); - try expect(math.approxEqAbs(f32, cos32(89.123), 0.400798, epsilon)); + try expect(math.approxEqAbs(f32, cosf(0.0), 1.0, epsilon)); + try expect(math.approxEqAbs(f32, cosf(0.2), 0.980067, epsilon)); + try expect(math.approxEqAbs(f32, cosf(0.8923), 0.627623, epsilon)); + try expect(math.approxEqAbs(f32, cosf(1.5), 0.070737, epsilon)); + try expect(math.approxEqAbs(f32, cosf(-1.5), 0.070737, epsilon)); + try expect(math.approxEqAbs(f32, cosf(37.45), 0.969132, epsilon)); + try expect(math.approxEqAbs(f32, cosf(89.123), 0.400798, epsilon)); } -test "math.cos64" { +test "cos64" { const epsilon = 0.000001; - try expect(math.approxEqAbs(f64, cos64(0.0), 1.0, epsilon)); - try expect(math.approxEqAbs(f64, cos64(0.2), 0.980067, epsilon)); - try expect(math.approxEqAbs(f64, cos64(0.8923), 0.627623, epsilon)); - try expect(math.approxEqAbs(f64, cos64(1.5), 0.070737, epsilon)); - try expect(math.approxEqAbs(f64, cos64(-1.5), 0.070737, epsilon)); - try expect(math.approxEqAbs(f64, cos64(37.45), 0.969132, epsilon)); - try expect(math.approxEqAbs(f64, cos64(89.123), 0.40080, epsilon)); + try expect(math.approxEqAbs(f64, cos(0.0), 1.0, epsilon)); + try expect(math.approxEqAbs(f64, cos(0.2), 0.980067, epsilon)); + try expect(math.approxEqAbs(f64, cos(0.8923), 0.627623, epsilon)); + try expect(math.approxEqAbs(f64, cos(1.5), 0.070737, epsilon)); + try expect(math.approxEqAbs(f64, cos(-1.5), 0.070737, epsilon)); + try expect(math.approxEqAbs(f64, cos(37.45), 0.969132, epsilon)); + try expect(math.approxEqAbs(f64, cos(89.123), 0.40080, epsilon)); } -test "math.cos32.special" { - try expect(math.isNan(cos32(math.inf(f32)))); - try expect(math.isNan(cos32(-math.inf(f32)))); - try expect(math.isNan(cos32(math.nan(f32)))); +test "cos32.special" { + try expect(math.isNan(cosf(math.inf(f32)))); + try expect(math.isNan(cosf(-math.inf(f32)))); + try expect(math.isNan(cosf(math.nan(f32)))); } -test "math.cos64.special" { - try expect(math.isNan(cos64(math.inf(f64)))); - try expect(math.isNan(cos64(-math.inf(f64)))); - try expect(math.isNan(cos64(math.nan(f64)))); +test "cos64.special" { + try expect(math.isNan(cos(math.inf(f64)))); + try expect(math.isNan(cos(-math.inf(f64)))); + try expect(math.isNan(cos(math.nan(f64)))); } diff --git a/lib/std/special/compiler_rt/divxf3_test.zig b/lib/std/special/compiler_rt/divxf3_test.zig index b79b90c6fb..0ed2b74217 100644 --- a/lib/std/special/compiler_rt/divxf3_test.zig +++ b/lib/std/special/compiler_rt/divxf3_test.zig @@ -30,9 +30,9 @@ fn test__divxf3(a: f80, b: f80) !void { const x_minus_eps = @bitCast(f80, (@bitCast(u80, x) - 1) | integerBit); // Make sure result is more accurate than the adjacent floats - const err_x = std.math.fabs(@mulAdd(f80, x, b, -a)); - const err_x_plus_eps = std.math.fabs(@mulAdd(f80, x_plus_eps, b, -a)); - const err_x_minus_eps = std.math.fabs(@mulAdd(f80, x_minus_eps, b, -a)); + const err_x = @fabs(@mulAdd(f80, x, b, -a)); + const err_x_plus_eps = @fabs(@mulAdd(f80, x_plus_eps, b, -a)); + const err_x_minus_eps = @fabs(@mulAdd(f80, x_minus_eps, b, -a)); try testing.expect(err_x_minus_eps > err_x); try testing.expect(err_x_plus_eps > err_x); diff --git a/lib/std/math/exp.zig b/lib/std/special/compiler_rt/exp.zig similarity index 74% rename from lib/std/math/exp.zig rename to lib/std/special/compiler_rt/exp.zig index 71a492c7ad..0f129dfd4c 100644 --- a/lib/std/math/exp.zig +++ b/lib/std/special/compiler_rt/exp.zig @@ -4,25 +4,16 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/expf.c // https://git.musl-libc.org/cgit/musl/tree/src/math/exp.c -const std = @import("../std.zig"); +const std = @import("std"); const math = std.math; const expect = std.testing.expect; -/// Returns e raised to the power of x (e^x). -/// -/// Special Cases: -/// - exp(+inf) = +inf -/// - exp(nan) = nan -pub fn exp(x: anytype) @TypeOf(x) { - const T = @TypeOf(x); - return switch (T) { - f32 => exp32(x), - f64 => exp64(x), - else => @compileError("exp not implemented for " ++ @typeName(T)), - }; +pub fn __exph(a: f16) callconv(.C) f16 { + // TODO: more efficient implementation + return @floatCast(f16, expf(a)); } -fn exp32(x_: f32) f32 { +pub fn expf(x_: f32) callconv(.C) f32 { const half = [_]f32{ 0.5, -0.5 }; const ln2hi = 6.9314575195e-1; const ln2lo = 1.4286067653e-6; @@ -97,7 +88,7 @@ fn exp32(x_: f32) f32 { } } -fn exp64(x_: f64) f64 { +pub fn exp(x_: f64) callconv(.C) f64 { const half = [_]f64{ 0.5, -0.5 }; const ln2hi: f64 = 6.93147180369123816490e-01; const ln2lo: f64 = 1.90821492927058770002e-10; @@ -181,37 +172,42 @@ fn exp64(x_: f64) f64 { } } -test "math.exp" { - try expect(exp(@as(f32, 0.0)) == exp32(0.0)); - try expect(exp(@as(f64, 0.0)) == exp64(0.0)); +pub fn __expx(a: f80) callconv(.C) f80 { + // TODO: more efficient implementation + return @floatCast(f80, expq(a)); } -test "math.exp32" { +pub fn expq(a: f128) callconv(.C) f128 { + // TODO: more correct implementation + return exp(@floatCast(f64, a)); +} + +test "exp32" { const epsilon = 0.000001; - try expect(exp32(0.0) == 1.0); - try expect(math.approxEqAbs(f32, exp32(0.0), 1.0, epsilon)); - try expect(math.approxEqAbs(f32, exp32(0.2), 1.221403, epsilon)); - try expect(math.approxEqAbs(f32, exp32(0.8923), 2.440737, epsilon)); - try expect(math.approxEqAbs(f32, exp32(1.5), 4.481689, epsilon)); + try expect(expf(0.0) == 1.0); + try expect(math.approxEqAbs(f32, expf(0.0), 1.0, epsilon)); + try expect(math.approxEqAbs(f32, expf(0.2), 1.221403, epsilon)); + try expect(math.approxEqAbs(f32, expf(0.8923), 2.440737, epsilon)); + try expect(math.approxEqAbs(f32, expf(1.5), 4.481689, epsilon)); } -test "math.exp64" { +test "exp64" { const epsilon = 0.000001; - try expect(exp64(0.0) == 1.0); - try expect(math.approxEqAbs(f64, exp64(0.0), 1.0, epsilon)); - try expect(math.approxEqAbs(f64, exp64(0.2), 1.221403, epsilon)); - try expect(math.approxEqAbs(f64, exp64(0.8923), 2.440737, epsilon)); - try expect(math.approxEqAbs(f64, exp64(1.5), 4.481689, epsilon)); + try expect(exp(0.0) == 1.0); + try expect(math.approxEqAbs(f64, exp(0.0), 1.0, epsilon)); + try expect(math.approxEqAbs(f64, exp(0.2), 1.221403, epsilon)); + try expect(math.approxEqAbs(f64, exp(0.8923), 2.440737, epsilon)); + try expect(math.approxEqAbs(f64, exp(1.5), 4.481689, epsilon)); } -test "math.exp32.special" { - try expect(math.isPositiveInf(exp32(math.inf(f32)))); - try expect(math.isNan(exp32(math.nan(f32)))); +test "exp32.special" { + try expect(math.isPositiveInf(expf(math.inf(f32)))); + try expect(math.isNan(expf(math.nan(f32)))); } -test "math.exp64.special" { - try expect(math.isPositiveInf(exp64(math.inf(f64)))); - try expect(math.isNan(exp64(math.nan(f64)))); +test "exp64.special" { + try expect(math.isPositiveInf(exp(math.inf(f64)))); + try expect(math.isNan(exp(math.nan(f64)))); } diff --git a/lib/std/math/exp2.zig b/lib/std/special/compiler_rt/exp2.zig similarity index 89% rename from lib/std/math/exp2.zig rename to lib/std/special/compiler_rt/exp2.zig index 76530ec61f..53432a831d 100644 --- a/lib/std/math/exp2.zig +++ b/lib/std/special/compiler_rt/exp2.zig @@ -4,44 +4,16 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/exp2f.c // https://git.musl-libc.org/cgit/musl/tree/src/math/exp2.c -const std = @import("../std.zig"); +const std = @import("std"); const math = std.math; const expect = std.testing.expect; -/// Returns 2 raised to the power of x (2^x). -/// -/// Special Cases: -/// - exp2(+inf) = +inf -/// - exp2(nan) = nan -pub fn exp2(x: anytype) @TypeOf(x) { - const T = @TypeOf(x); - return switch (T) { - f32 => exp2_32(x), - f64 => exp2_64(x), - else => @compileError("exp2 not implemented for " ++ @typeName(T)), - }; +pub fn __exp2h(x: f16) callconv(.C) f16 { + // TODO: more efficient implementation + return @floatCast(f16, exp2f(x)); } -const exp2ft = [_]f64{ - 0x1.6a09e667f3bcdp-1, - 0x1.7a11473eb0187p-1, - 0x1.8ace5422aa0dbp-1, - 0x1.9c49182a3f090p-1, - 0x1.ae89f995ad3adp-1, - 0x1.c199bdd85529cp-1, - 0x1.d5818dcfba487p-1, - 0x1.ea4afa2a490dap-1, - 0x1.0000000000000p+0, - 0x1.0b5586cf9890fp+0, - 0x1.172b83c7d517bp+0, - 0x1.2387a6e756238p+0, - 0x1.306fe0a31b715p+0, - 0x1.3dea64c123422p+0, - 0x1.4bfdad5362a27p+0, - 0x1.5ab07dd485429p+0, -}; - -fn exp2_32(x: f32) f32 { +pub fn exp2f(x: f32) callconv(.C) f32 { const tblsiz = @intCast(u32, exp2ft.len); const redux: f32 = 0x1.8p23 / @intToFloat(f32, tblsiz); const P1: f32 = 0x1.62e430p-1; @@ -98,6 +70,104 @@ fn exp2_32(x: f32) f32 { return @floatCast(f32, r * uk); } +pub fn exp2(x: f64) callconv(.C) f64 { + const tblsiz: u32 = @intCast(u32, exp2dt.len / 2); + const redux: f64 = 0x1.8p52 / @intToFloat(f64, tblsiz); + const P1: f64 = 0x1.62e42fefa39efp-1; + const P2: f64 = 0x1.ebfbdff82c575p-3; + const P3: f64 = 0x1.c6b08d704a0a6p-5; + const P4: f64 = 0x1.3b2ab88f70400p-7; + const P5: f64 = 0x1.5d88003875c74p-10; + + const ux = @bitCast(u64, x); + const ix = @intCast(u32, ux >> 32) & 0x7FFFFFFF; + + // TODO: This should be handled beneath. + if (math.isNan(x)) { + return math.nan(f64); + } + + // |x| >= 1022 or nan + if (ix >= 0x408FF000) { + // x >= 1024 or nan + if (ix >= 0x40900000 and ux >> 63 == 0) { + math.raiseOverflow(); + return math.inf(f64); + } + // -inf or -nan + if (ix >= 0x7FF00000) { + return -1 / x; + } + // x <= -1022 + if (ux >> 63 != 0) { + // underflow + if (x <= -1075 or x - 0x1.0p52 + 0x1.0p52 != x) { + math.doNotOptimizeAway(@floatCast(f32, -0x1.0p-149 / x)); + } + if (x <= -1075) { + return 0; + } + } + } + // |x| < 0x1p-54 + else if (ix < 0x3C900000) { + return 1.0 + x; + } + + // NOTE: musl relies on unsafe behaviours which are replicated below + // (addition overflow, division truncation, casting). Appears that this + // produces the intended result but should confirm how GCC/Clang handle this + // to ensure. + + // reduce x + var uf: f64 = x + redux; + // NOTE: musl performs an implicit 64-bit to 32-bit u32 truncation here + var i_0: u32 = @truncate(u32, @bitCast(u64, uf)); + i_0 +%= tblsiz / 2; + + const k: u32 = i_0 / tblsiz * tblsiz; + const ik: i32 = @divTrunc(@bitCast(i32, k), tblsiz); + i_0 %= tblsiz; + uf -= redux; + + // r = exp2(y) = exp2t[i_0] * p(z - eps[i]) + var z: f64 = x - uf; + const t: f64 = exp2dt[@intCast(usize, 2 * i_0)]; + z -= exp2dt[@intCast(usize, 2 * i_0 + 1)]; + const r: f64 = t + t * z * (P1 + z * (P2 + z * (P3 + z * (P4 + z * P5)))); + + return math.scalbn(r, ik); +} + +pub fn __exp2x(x: f80) callconv(.C) f80 { + // TODO: more efficient implementation + return @floatCast(f80, exp2q(x)); +} + +pub fn exp2q(x: f128) callconv(.C) f128 { + // TODO: more correct implementation + return exp2(@floatCast(f64, x)); +} + +const exp2ft = [_]f64{ + 0x1.6a09e667f3bcdp-1, + 0x1.7a11473eb0187p-1, + 0x1.8ace5422aa0dbp-1, + 0x1.9c49182a3f090p-1, + 0x1.ae89f995ad3adp-1, + 0x1.c199bdd85529cp-1, + 0x1.d5818dcfba487p-1, + 0x1.ea4afa2a490dap-1, + 0x1.0000000000000p+0, + 0x1.0b5586cf9890fp+0, + 0x1.172b83c7d517bp+0, + 0x1.2387a6e756238p+0, + 0x1.306fe0a31b715p+0, + 0x1.3dea64c123422p+0, + 0x1.4bfdad5362a27p+0, + 0x1.5ab07dd485429p+0, +}; + const exp2dt = [_]f64{ // exp2(z + eps) eps 0x1.6a09e667f3d5dp-1, 0x1.9880p-44, @@ -358,108 +428,34 @@ const exp2dt = [_]f64{ 0x1.690f4b19e9471p+0, -0x1.9780p-45, }; -fn exp2_64(x: f64) f64 { - const tblsiz: u32 = @intCast(u32, exp2dt.len / 2); - const redux: f64 = 0x1.8p52 / @intToFloat(f64, tblsiz); - const P1: f64 = 0x1.62e42fefa39efp-1; - const P2: f64 = 0x1.ebfbdff82c575p-3; - const P3: f64 = 0x1.c6b08d704a0a6p-5; - const P4: f64 = 0x1.3b2ab88f70400p-7; - const P5: f64 = 0x1.5d88003875c74p-10; - - const ux = @bitCast(u64, x); - const ix = @intCast(u32, ux >> 32) & 0x7FFFFFFF; - - // TODO: This should be handled beneath. - if (math.isNan(x)) { - return math.nan(f64); - } - - // |x| >= 1022 or nan - if (ix >= 0x408FF000) { - // x >= 1024 or nan - if (ix >= 0x40900000 and ux >> 63 == 0) { - math.raiseOverflow(); - return math.inf(f64); - } - // -inf or -nan - if (ix >= 0x7FF00000) { - return -1 / x; - } - // x <= -1022 - if (ux >> 63 != 0) { - // underflow - if (x <= -1075 or x - 0x1.0p52 + 0x1.0p52 != x) { - math.doNotOptimizeAway(@floatCast(f32, -0x1.0p-149 / x)); - } - if (x <= -1075) { - return 0; - } - } - } - // |x| < 0x1p-54 - else if (ix < 0x3C900000) { - return 1.0 + x; - } - - // NOTE: musl relies on unsafe behaviours which are replicated below - // (addition overflow, division truncation, casting). Appears that this - // produces the intended result but should confirm how GCC/Clang handle this - // to ensure. - - // reduce x - var uf: f64 = x + redux; - // NOTE: musl performs an implicit 64-bit to 32-bit u32 truncation here - var i_0: u32 = @truncate(u32, @bitCast(u64, uf)); - i_0 +%= tblsiz / 2; - - const k: u32 = i_0 / tblsiz * tblsiz; - const ik: i32 = @divTrunc(@bitCast(i32, k), tblsiz); - i_0 %= tblsiz; - uf -= redux; - - // r = exp2(y) = exp2t[i_0] * p(z - eps[i]) - var z: f64 = x - uf; - const t: f64 = exp2dt[@intCast(usize, 2 * i_0)]; - z -= exp2dt[@intCast(usize, 2 * i_0 + 1)]; - const r: f64 = t + t * z * (P1 + z * (P2 + z * (P3 + z * (P4 + z * P5)))); - - return math.scalbn(r, ik); -} - -test "math.exp2" { - try expect(exp2(@as(f32, 0.8923)) == exp2_32(0.8923)); - try expect(exp2(@as(f64, 0.8923)) == exp2_64(0.8923)); -} - -test "math.exp2_32" { +test "exp2_32" { const epsilon = 0.000001; - try expect(exp2_32(0.0) == 1.0); - try expect(math.approxEqAbs(f32, exp2_32(0.2), 1.148698, epsilon)); - try expect(math.approxEqAbs(f32, exp2_32(0.8923), 1.856133, epsilon)); - try expect(math.approxEqAbs(f32, exp2_32(1.5), 2.828427, epsilon)); - try expect(math.approxEqAbs(f32, exp2_32(37.45), 187747237888, epsilon)); - try expect(math.approxEqAbs(f32, exp2_32(-1), 0.5, epsilon)); + try expect(exp2f(0.0) == 1.0); + try expect(math.approxEqAbs(f32, exp2f(0.2), 1.148698, epsilon)); + try expect(math.approxEqAbs(f32, exp2f(0.8923), 1.856133, epsilon)); + try expect(math.approxEqAbs(f32, exp2f(1.5), 2.828427, epsilon)); + try expect(math.approxEqAbs(f32, exp2f(37.45), 187747237888, epsilon)); + try expect(math.approxEqAbs(f32, exp2f(-1), 0.5, epsilon)); } -test "math.exp2_64" { +test "exp2_64" { const epsilon = 0.000001; - try expect(exp2_64(0.0) == 1.0); - try expect(math.approxEqAbs(f64, exp2_64(0.2), 1.148698, epsilon)); - try expect(math.approxEqAbs(f64, exp2_64(0.8923), 1.856133, epsilon)); - try expect(math.approxEqAbs(f64, exp2_64(1.5), 2.828427, epsilon)); - try expect(math.approxEqAbs(f64, exp2_64(-1), 0.5, epsilon)); - try expect(math.approxEqAbs(f64, exp2_64(-0x1.a05cc754481d1p-2), 0x1.824056efc687cp-1, epsilon)); + try expect(exp2(0.0) == 1.0); + try expect(math.approxEqAbs(f64, exp2(0.2), 1.148698, epsilon)); + try expect(math.approxEqAbs(f64, exp2(0.8923), 1.856133, epsilon)); + try expect(math.approxEqAbs(f64, exp2(1.5), 2.828427, epsilon)); + try expect(math.approxEqAbs(f64, exp2(-1), 0.5, epsilon)); + try expect(math.approxEqAbs(f64, exp2(-0x1.a05cc754481d1p-2), 0x1.824056efc687cp-1, epsilon)); } -test "math.exp2_32.special" { - try expect(math.isPositiveInf(exp2_32(math.inf(f32)))); - try expect(math.isNan(exp2_32(math.nan(f32)))); +test "exp2_32.special" { + try expect(math.isPositiveInf(exp2f(math.inf(f32)))); + try expect(math.isNan(exp2f(math.nan(f32)))); } -test "math.exp2_64.special" { - try expect(math.isPositiveInf(exp2_64(math.inf(f64)))); - try expect(math.isNan(exp2_64(math.nan(f64)))); +test "exp2_64.special" { + try expect(math.isPositiveInf(exp2(math.inf(f64)))); + try expect(math.isNan(exp2(math.nan(f64)))); } diff --git a/lib/std/special/compiler_rt/fabs.zig b/lib/std/special/compiler_rt/fabs.zig new file mode 100644 index 0000000000..fbef81fc9a --- /dev/null +++ b/lib/std/special/compiler_rt/fabs.zig @@ -0,0 +1,29 @@ +const std = @import("std"); + +pub fn __fabsh(a: f16) callconv(.C) f16 { + return generic_fabs(a); +} + +pub fn fabsf(a: f32) callconv(.C) f32 { + return generic_fabs(a); +} + +pub fn fabs(a: f64) callconv(.C) f64 { + return generic_fabs(a); +} + +pub fn __fabsx(a: f80) callconv(.C) f80 { + return generic_fabs(a); +} + +pub fn fabsq(a: f128) callconv(.C) f128 { + return generic_fabs(a); +} + +inline fn generic_fabs(x: anytype) @TypeOf(x) { + const T = @TypeOf(x); + const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); + const float_bits = @bitCast(TBits, x); + const remove_sign = ~@as(TBits, 0) >> 1; + return @bitCast(T, float_bits & remove_sign); +} diff --git a/lib/std/math/floor.zig b/lib/std/special/compiler_rt/floor.zig similarity index 52% rename from lib/std/math/floor.zig rename to lib/std/special/compiler_rt/floor.zig index ab5ca3583b..f6df164b58 100644 --- a/lib/std/math/floor.zig +++ b/lib/std/special/compiler_rt/floor.zig @@ -4,32 +4,11 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/floorf.c // https://git.musl-libc.org/cgit/musl/tree/src/math/floor.c -const expect = std.testing.expect; -const std = @import("../std.zig"); +const std = @import("std"); const math = std.math; +const expect = std.testing.expect; -/// Returns the greatest integer value less than or equal to x. -/// -/// Special Cases: -/// - floor(+-0) = +-0 -/// - floor(+-inf) = +-inf -/// - floor(nan) = nan -pub fn floor(x: anytype) @TypeOf(x) { - const T = @TypeOf(x); - return switch (T) { - f16 => floor16(x), - f32 => floor32(x), - f64 => floor64(x), - f128 => floor128(x), - - // TODO this is not correct for some targets - c_longdouble => @floatCast(c_longdouble, floor128(x)), - - else => @compileError("floor not implemented for " ++ @typeName(T)), - }; -} - -fn floor16(x: f16) f16 { +pub fn __floorh(x: f16) callconv(.C) f16 { var u = @bitCast(u16, x); const e = @intCast(i16, (u >> 10) & 31) - 15; var m: u16 = undefined; @@ -63,7 +42,7 @@ fn floor16(x: f16) f16 { } } -fn floor32(x: f32) f32 { +pub fn floorf(x: f32) callconv(.C) f32 { var u = @bitCast(u32, x); const e = @intCast(i32, (u >> 23) & 0xFF) - 0x7F; var m: u32 = undefined; @@ -97,7 +76,7 @@ fn floor32(x: f32) f32 { } } -fn floor64(x: f64) f64 { +pub fn floor(x: f64) callconv(.C) f64 { const f64_toint = 1.0 / math.floatEps(f64); const u = @bitCast(u64, x); @@ -128,7 +107,12 @@ fn floor64(x: f64) f64 { } } -fn floor128(x: f128) f128 { +pub fn __floorx(x: f80) callconv(.C) f80 { + // TODO: more efficient implementation + return @floatCast(f80, floorq(x)); +} + +pub fn floorq(x: f128) callconv(.C) f128 { const f128_toint = 1.0 / math.floatEps(f128); const u = @bitCast(u128, x); @@ -157,65 +141,58 @@ fn floor128(x: f128) f128 { } } -test "math.floor" { - try expect(floor(@as(f16, 1.3)) == floor16(1.3)); - try expect(floor(@as(f32, 1.3)) == floor32(1.3)); - try expect(floor(@as(f64, 1.3)) == floor64(1.3)); - try expect(floor(@as(f128, 1.3)) == floor128(1.3)); +test "floor16" { + try expect(__floorh(1.3) == 1.0); + try expect(__floorh(-1.3) == -2.0); + try expect(__floorh(0.2) == 0.0); } -test "math.floor16" { - try expect(floor16(1.3) == 1.0); - try expect(floor16(-1.3) == -2.0); - try expect(floor16(0.2) == 0.0); +test "floor32" { + try expect(floorf(1.3) == 1.0); + try expect(floorf(-1.3) == -2.0); + try expect(floorf(0.2) == 0.0); } -test "math.floor32" { - try expect(floor32(1.3) == 1.0); - try expect(floor32(-1.3) == -2.0); - try expect(floor32(0.2) == 0.0); +test "floor64" { + try expect(floor(1.3) == 1.0); + try expect(floor(-1.3) == -2.0); + try expect(floor(0.2) == 0.0); } -test "math.floor64" { - try expect(floor64(1.3) == 1.0); - try expect(floor64(-1.3) == -2.0); - try expect(floor64(0.2) == 0.0); +test "floor128" { + try expect(floorq(1.3) == 1.0); + try expect(floorq(-1.3) == -2.0); + try expect(floorq(0.2) == 0.0); } -test "math.floor128" { - try expect(floor128(1.3) == 1.0); - try expect(floor128(-1.3) == -2.0); - try expect(floor128(0.2) == 0.0); +test "floor16.special" { + try expect(__floorh(0.0) == 0.0); + try expect(__floorh(-0.0) == -0.0); + try expect(math.isPositiveInf(__floorh(math.inf(f16)))); + try expect(math.isNegativeInf(__floorh(-math.inf(f16)))); + try expect(math.isNan(__floorh(math.nan(f16)))); } -test "math.floor16.special" { - try expect(floor16(0.0) == 0.0); - try expect(floor16(-0.0) == -0.0); - try expect(math.isPositiveInf(floor16(math.inf(f16)))); - try expect(math.isNegativeInf(floor16(-math.inf(f16)))); - try expect(math.isNan(floor16(math.nan(f16)))); +test "floor32.special" { + try expect(floorf(0.0) == 0.0); + try expect(floorf(-0.0) == -0.0); + try expect(math.isPositiveInf(floorf(math.inf(f32)))); + try expect(math.isNegativeInf(floorf(-math.inf(f32)))); + try expect(math.isNan(floorf(math.nan(f32)))); } -test "math.floor32.special" { - try expect(floor32(0.0) == 0.0); - try expect(floor32(-0.0) == -0.0); - try expect(math.isPositiveInf(floor32(math.inf(f32)))); - try expect(math.isNegativeInf(floor32(-math.inf(f32)))); - try expect(math.isNan(floor32(math.nan(f32)))); +test "floor64.special" { + try expect(floor(0.0) == 0.0); + try expect(floor(-0.0) == -0.0); + try expect(math.isPositiveInf(floor(math.inf(f64)))); + try expect(math.isNegativeInf(floor(-math.inf(f64)))); + try expect(math.isNan(floor(math.nan(f64)))); } -test "math.floor64.special" { - try expect(floor64(0.0) == 0.0); - try expect(floor64(-0.0) == -0.0); - try expect(math.isPositiveInf(floor64(math.inf(f64)))); - try expect(math.isNegativeInf(floor64(-math.inf(f64)))); - try expect(math.isNan(floor64(math.nan(f64)))); -} - -test "math.floor128.special" { - try expect(floor128(0.0) == 0.0); - try expect(floor128(-0.0) == -0.0); - try expect(math.isPositiveInf(floor128(math.inf(f128)))); - try expect(math.isNegativeInf(floor128(-math.inf(f128)))); - try expect(math.isNan(floor128(math.nan(f128)))); +test "floor128.special" { + try expect(floorq(0.0) == 0.0); + try expect(floorq(-0.0) == -0.0); + try expect(math.isPositiveInf(floorq(math.inf(f128)))); + try expect(math.isNegativeInf(floorq(-math.inf(f128)))); + try expect(math.isNan(floorq(math.nan(f128)))); } diff --git a/lib/std/math/fma.zig b/lib/std/special/compiler_rt/fma.zig similarity index 76% rename from lib/std/math/fma.zig rename to lib/std/special/compiler_rt/fma.zig index 7afc6e557e..4c603bf095 100644 --- a/lib/std/math/fma.zig +++ b/lib/std/special/compiler_rt/fma.zig @@ -5,27 +5,16 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/fmaf.c // https://git.musl-libc.org/cgit/musl/tree/src/math/fma.c -const std = @import("../std.zig"); +const std = @import("std"); const math = std.math; const expect = std.testing.expect; -/// Returns x * y + z with a single rounding error. -pub fn fma(comptime T: type, x: T, y: T, z: T) T { - return switch (T) { - f32 => fma32(x, y, z), - f64 => fma64(x, y, z), - f128 => fma128(x, y, z), - - // TODO this is not correct for some targets - c_longdouble => @floatCast(c_longdouble, fma128(x, y, z)), - - f80 => @floatCast(f80, fma128(x, y, z)), - - else => @compileError("fma not implemented for " ++ @typeName(T)), - }; +pub fn __fmah(x: f16, y: f16, z: f16) callconv(.C) f16 { + // TODO: more efficient implementation + return @floatCast(f16, fmaf(x, y, z)); } -fn fma32(x: f32, y: f32, z: f32) f32 { +pub fn fmaf(x: f32, y: f32, z: f32) callconv(.C) f32 { const xy = @as(f64, x) * y; const xy_z = xy + z; const u = @bitCast(u64, xy_z); @@ -39,8 +28,8 @@ fn fma32(x: f32, y: f32, z: f32) f32 { } } -// NOTE: Upstream fma.c has been rewritten completely to raise fp exceptions more accurately. -fn fma64(x: f64, y: f64, z: f64) f64 { +/// NOTE: Upstream fma.c has been rewritten completely to raise fp exceptions more accurately. +pub fn fma(x: f64, y: f64, z: f64) callconv(.C) f64 { if (!math.isFinite(x) or !math.isFinite(y)) { return x * y + z; } @@ -87,6 +76,65 @@ fn fma64(x: f64, y: f64, z: f64) f64 { } } +pub fn __fmax(a: f80, b: f80, c: f80) callconv(.C) f80 { + // TODO: more efficient implementation + return @floatCast(f80, fmaq(a, b, c)); +} + +/// Fused multiply-add: Compute x * y + z with a single rounding error. +/// +/// We use scaling to avoid overflow/underflow, along with the +/// canonical precision-doubling technique adapted from: +/// +/// Dekker, T. A Floating-Point Technique for Extending the +/// Available Precision. Numer. Math. 18, 224-242 (1971). +pub fn fmaq(x: f128, y: f128, z: f128) callconv(.C) f128 { + if (!math.isFinite(x) or !math.isFinite(y)) { + return x * y + z; + } + if (!math.isFinite(z)) { + return z; + } + if (x == 0.0 or y == 0.0) { + return x * y + z; + } + if (z == 0.0) { + return x * y; + } + + const x1 = math.frexp(x); + var ex = x1.exponent; + var xs = x1.significand; + const x2 = math.frexp(y); + var ey = x2.exponent; + var ys = x2.significand; + const x3 = math.frexp(z); + var ez = x3.exponent; + var zs = x3.significand; + + var spread = ex + ey - ez; + if (spread <= 113 * 2) { + zs = math.scalbn(zs, -spread); + } else { + zs = math.copysign(f128, math.floatMin(f128), zs); + } + + const xy = dd_mul128(xs, ys); + const r = dd_add128(xy.hi, zs); + spread = ex + ey; + + if (r.hi == 0.0) { + return xy.hi + zs + math.scalbn(xy.lo, spread); + } + + const adj = add_adjusted128(r.lo, xy.lo); + if (spread + math.ilogb(r.hi) > -16383) { + return math.scalbn(r.hi + adj, spread); + } else { + return add_and_denorm128(r.hi, adj, spread); + } +} + const dd = struct { hi: f64, lo: f64, @@ -242,98 +290,38 @@ fn dd_mul128(a: f128, b: f128) dd128 { return ret; } -/// Fused multiply-add: Compute x * y + z with a single rounding error. -/// -/// We use scaling to avoid overflow/underflow, along with the -/// canonical precision-doubling technique adapted from: -/// -/// Dekker, T. A Floating-Point Technique for Extending the -/// Available Precision. Numer. Math. 18, 224-242 (1971). -fn fma128(x: f128, y: f128, z: f128) f128 { - if (!math.isFinite(x) or !math.isFinite(y)) { - return x * y + z; - } - if (!math.isFinite(z)) { - return z; - } - if (x == 0.0 or y == 0.0) { - return x * y + z; - } - if (z == 0.0) { - return x * y; - } - - const x1 = math.frexp(x); - var ex = x1.exponent; - var xs = x1.significand; - const x2 = math.frexp(y); - var ey = x2.exponent; - var ys = x2.significand; - const x3 = math.frexp(z); - var ez = x3.exponent; - var zs = x3.significand; - - var spread = ex + ey - ez; - if (spread <= 113 * 2) { - zs = math.scalbn(zs, -spread); - } else { - zs = math.copysign(f128, math.floatMin(f128), zs); - } - - const xy = dd_mul128(xs, ys); - const r = dd_add128(xy.hi, zs); - spread = ex + ey; - - if (r.hi == 0.0) { - return xy.hi + zs + math.scalbn(xy.lo, spread); - } - - const adj = add_adjusted128(r.lo, xy.lo); - if (spread + math.ilogb(r.hi) > -16383) { - return math.scalbn(r.hi + adj, spread); - } else { - return add_and_denorm128(r.hi, adj, spread); - } -} - -test "type dispatch" { - try expect(fma(f32, 0.0, 1.0, 1.0) == fma32(0.0, 1.0, 1.0)); - try expect(fma(f64, 0.0, 1.0, 1.0) == fma64(0.0, 1.0, 1.0)); - try expect(fma(f128, 0.0, 1.0, 1.0) == fma128(0.0, 1.0, 1.0)); -} - test "32" { const epsilon = 0.000001; - try expect(math.approxEqAbs(f32, fma32(0.0, 5.0, 9.124), 9.124, epsilon)); - try expect(math.approxEqAbs(f32, fma32(0.2, 5.0, 9.124), 10.124, epsilon)); - try expect(math.approxEqAbs(f32, fma32(0.8923, 5.0, 9.124), 13.5855, epsilon)); - try expect(math.approxEqAbs(f32, fma32(1.5, 5.0, 9.124), 16.624, epsilon)); - try expect(math.approxEqAbs(f32, fma32(37.45, 5.0, 9.124), 196.374004, epsilon)); - try expect(math.approxEqAbs(f32, fma32(89.123, 5.0, 9.124), 454.739005, epsilon)); - try expect(math.approxEqAbs(f32, fma32(123123.234375, 5.0, 9.124), 615625.295875, epsilon)); + try expect(math.approxEqAbs(f32, fmaf(0.0, 5.0, 9.124), 9.124, epsilon)); + try expect(math.approxEqAbs(f32, fmaf(0.2, 5.0, 9.124), 10.124, epsilon)); + try expect(math.approxEqAbs(f32, fmaf(0.8923, 5.0, 9.124), 13.5855, epsilon)); + try expect(math.approxEqAbs(f32, fmaf(1.5, 5.0, 9.124), 16.624, epsilon)); + try expect(math.approxEqAbs(f32, fmaf(37.45, 5.0, 9.124), 196.374004, epsilon)); + try expect(math.approxEqAbs(f32, fmaf(89.123, 5.0, 9.124), 454.739005, epsilon)); + try expect(math.approxEqAbs(f32, fmaf(123123.234375, 5.0, 9.124), 615625.295875, epsilon)); } test "64" { const epsilon = 0.000001; - try expect(math.approxEqAbs(f64, fma64(0.0, 5.0, 9.124), 9.124, epsilon)); - try expect(math.approxEqAbs(f64, fma64(0.2, 5.0, 9.124), 10.124, epsilon)); - try expect(math.approxEqAbs(f64, fma64(0.8923, 5.0, 9.124), 13.5855, epsilon)); - try expect(math.approxEqAbs(f64, fma64(1.5, 5.0, 9.124), 16.624, epsilon)); - try expect(math.approxEqAbs(f64, fma64(37.45, 5.0, 9.124), 196.374, epsilon)); - try expect(math.approxEqAbs(f64, fma64(89.123, 5.0, 9.124), 454.739, epsilon)); - try expect(math.approxEqAbs(f64, fma64(123123.234375, 5.0, 9.124), 615625.295875, epsilon)); + try expect(math.approxEqAbs(f64, fma(0.0, 5.0, 9.124), 9.124, epsilon)); + try expect(math.approxEqAbs(f64, fma(0.2, 5.0, 9.124), 10.124, epsilon)); + try expect(math.approxEqAbs(f64, fma(0.8923, 5.0, 9.124), 13.5855, epsilon)); + try expect(math.approxEqAbs(f64, fma(1.5, 5.0, 9.124), 16.624, epsilon)); + try expect(math.approxEqAbs(f64, fma(37.45, 5.0, 9.124), 196.374, epsilon)); + try expect(math.approxEqAbs(f64, fma(89.123, 5.0, 9.124), 454.739, epsilon)); + try expect(math.approxEqAbs(f64, fma(123123.234375, 5.0, 9.124), 615625.295875, epsilon)); } test "128" { const epsilon = 0.000001; - try expect(math.approxEqAbs(f128, fma128(0.0, 5.0, 9.124), 9.124, epsilon)); - try expect(math.approxEqAbs(f128, fma128(0.2, 5.0, 9.124), 10.124, epsilon)); - try expect(math.approxEqAbs(f128, fma128(0.8923, 5.0, 9.124), 13.5855, epsilon)); - try expect(math.approxEqAbs(f128, fma128(1.5, 5.0, 9.124), 16.624, epsilon)); - try expect(math.approxEqAbs(f128, fma128(37.45, 5.0, 9.124), 196.374, epsilon)); - try expect(math.approxEqAbs(f128, fma128(89.123, 5.0, 9.124), 454.739, epsilon)); - try expect(math.approxEqAbs(f128, fma128(123123.234375, 5.0, 9.124), 615625.295875, epsilon)); + try expect(math.approxEqAbs(f128, fmaq(0.0, 5.0, 9.124), 9.124, epsilon)); + try expect(math.approxEqAbs(f128, fmaq(0.2, 5.0, 9.124), 10.124, epsilon)); + try expect(math.approxEqAbs(f128, fmaq(0.8923, 5.0, 9.124), 13.5855, epsilon)); + try expect(math.approxEqAbs(f128, fmaq(1.5, 5.0, 9.124), 16.624, epsilon)); + try expect(math.approxEqAbs(f128, fmaq(37.45, 5.0, 9.124), 196.374, epsilon)); + try expect(math.approxEqAbs(f128, fmaq(89.123, 5.0, 9.124), 454.739, epsilon)); + try expect(math.approxEqAbs(f128, fmaq(123123.234375, 5.0, 9.124), 615625.295875, epsilon)); } diff --git a/lib/std/special/compiler_rt/fmax.zig b/lib/std/special/compiler_rt/fmax.zig new file mode 100644 index 0000000000..a5bd68cd74 --- /dev/null +++ b/lib/std/special/compiler_rt/fmax.zig @@ -0,0 +1,43 @@ +const std = @import("std"); +const math = std.math; + +pub fn __fmaxh(x: f16, y: f16) callconv(.C) f16 { + return generic_fmax(f16, x, y); +} + +pub fn fmaxf(x: f32, y: f32) callconv(.C) f32 { + return generic_fmax(f32, x, y); +} + +pub fn fmax(x: f64, y: f64) callconv(.C) f64 { + return generic_fmax(f64, x, y); +} + +pub fn __fmaxx(x: f80, y: f80) callconv(.C) f80 { + return generic_fmax(f80, x, y); +} + +pub fn fmaxq(x: f128, y: f128) callconv(.C) f128 { + return generic_fmax(f128, x, y); +} + +inline fn generic_fmax(comptime T: type, x: T, y: T) T { + if (math.isNan(x)) + return y; + if (math.isNan(y)) + return x; + return if (x < y) y else x; +} + +test "generic_fmax" { + inline for ([_]type{ f32, f64, c_longdouble, f80, f128 }) |T| { + const nan_val = math.nan(T); + + try std.testing.expect(math.isNan(generic_fmax(T, nan_val, nan_val))); + try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, nan_val, 1.0)); + try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, nan_val)); + + try std.testing.expectEqual(@as(T, 10.0), generic_fmax(T, 1.0, 10.0)); + try std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, -1.0)); + } +} diff --git a/lib/std/special/compiler_rt/fmin.zig b/lib/std/special/compiler_rt/fmin.zig new file mode 100644 index 0000000000..cc4dbf082b --- /dev/null +++ b/lib/std/special/compiler_rt/fmin.zig @@ -0,0 +1,43 @@ +const std = @import("std"); +const math = std.math; + +pub fn __fminh(x: f16, y: f16) callconv(.C) f16 { + return generic_fmin(f16, x, y); +} + +pub fn fminf(x: f32, y: f32) callconv(.C) f32 { + return generic_fmin(f32, x, y); +} + +pub fn fmin(x: f64, y: f64) callconv(.C) f64 { + return generic_fmin(f64, x, y); +} + +pub fn __fminx(x: f80, y: f80) callconv(.C) f80 { + return generic_fmin(f80, x, y); +} + +pub fn fminq(x: f128, y: f128) callconv(.C) f128 { + return generic_fmin(f128, x, y); +} + +inline fn generic_fmin(comptime T: type, x: T, y: T) T { + if (math.isNan(x)) + return y; + if (math.isNan(y)) + return x; + return if (x < y) x else y; +} + +test "generic_fmin" { + inline for ([_]type{ f32, f64, c_longdouble, f80, f128 }) |T| { + const nan_val = math.nan(T); + + try std.testing.expect(math.isNan(generic_fmin(T, nan_val, nan_val))); + try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, nan_val, 1.0)); + try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, nan_val)); + + try std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, 10.0)); + try std.testing.expectEqual(@as(T, -1.0), generic_fmin(T, 1.0, -1.0)); + } +} diff --git a/lib/std/special/compiler_rt/fmod.zig b/lib/std/special/compiler_rt/fmod.zig new file mode 100644 index 0000000000..b9a5710b9c --- /dev/null +++ b/lib/std/special/compiler_rt/fmod.zig @@ -0,0 +1,351 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const math = std.math; +const assert = std.debug.assert; +const normalize = @import("divdf3.zig").normalize; + +pub fn __fmodh(x: f16, y: f16) callconv(.C) f16 { + // TODO: more efficient implementation + return @floatCast(f16, fmodf(x, y)); +} + +pub fn fmodf(x: f32, y: f32) callconv(.C) f32 { + return generic_fmod(f32, x, y); +} + +pub fn fmod(x: f64, y: f64) callconv(.C) f64 { + return generic_fmod(f64, x, y); +} + +/// fmodx - floating modulo large, returns the remainder of division for f80 types +/// Logic and flow heavily inspired by MUSL fmodl for 113 mantissa digits +pub fn __fmodx(a: f80, b: f80) callconv(.C) f80 { + @setRuntimeSafety(builtin.is_test); + + const T = f80; + const Z = std.meta.Int(.unsigned, @bitSizeOf(T)); + + const significandBits = math.floatMantissaBits(T); + const fractionalBits = math.floatFractionalBits(T); + const exponentBits = math.floatExponentBits(T); + + const signBit = (@as(Z, 1) << (significandBits + exponentBits)); + const maxExponent = ((1 << exponentBits) - 1); + + var aRep = @bitCast(Z, a); + var bRep = @bitCast(Z, b); + + const signA = aRep & signBit; + var expA = @intCast(i32, (@bitCast(Z, a) >> significandBits) & maxExponent); + var expB = @intCast(i32, (@bitCast(Z, b) >> significandBits) & maxExponent); + + // There are 3 cases where the answer is undefined, check for: + // - fmodx(val, 0) + // - fmodx(val, NaN) + // - fmodx(inf, val) + // The sign on checked values does not matter. + // Doing (a * b) / (a * b) procudes undefined results + // because the three cases always produce undefined calculations: + // - 0 / 0 + // - val * NaN + // - inf / inf + if (b == 0 or math.isNan(b) or expA == maxExponent) { + return (a * b) / (a * b); + } + + // Remove the sign from both + aRep &= ~signBit; + bRep &= ~signBit; + if (aRep <= bRep) { + if (aRep == bRep) { + return 0 * a; + } + return a; + } + + if (expA == 0) expA = normalize(f80, &aRep); + if (expB == 0) expB = normalize(f80, &bRep); + + var highA: u64 = 0; + var highB: u64 = 0; + var lowA: u64 = @truncate(u64, aRep); + var lowB: u64 = @truncate(u64, bRep); + + while (expA > expB) : (expA -= 1) { + var high = highA -% highB; + var low = lowA -% lowB; + if (lowA < lowB) { + high -%= 1; + } + if (high >> 63 == 0) { + if ((high | low) == 0) { + return 0 * a; + } + highA = 2 *% high + (low >> 63); + lowA = 2 *% low; + } else { + highA = 2 *% highA + (lowA >> 63); + lowA = 2 *% lowA; + } + } + + var high = highA -% highB; + var low = lowA -% lowB; + if (lowA < lowB) { + high -%= 1; + } + if (high >> 63 == 0) { + if ((high | low) == 0) { + return 0 * a; + } + highA = high; + lowA = low; + } + + while ((lowA >> fractionalBits) == 0) { + lowA = 2 *% lowA; + expA = expA - 1; + } + + // Combine the exponent with the sign and significand, normalize if happened to be denormalized + if (expA < -fractionalBits) { + return @bitCast(T, signA); + } else if (expA <= 0) { + return @bitCast(T, (lowA >> @intCast(math.Log2Int(u64), 1 - expA)) | signA); + } else { + return @bitCast(T, lowA | (@as(Z, @intCast(u16, expA)) << significandBits) | signA); + } +} + +/// fmodq - floating modulo large, returns the remainder of division for f128 types +/// Logic and flow heavily inspired by MUSL fmodl for 113 mantissa digits +pub fn fmodq(a: f128, b: f128) callconv(.C) f128 { + @setRuntimeSafety(builtin.is_test); + var amod = a; + var bmod = b; + const aPtr_u64 = @ptrCast([*]u64, &amod); + const bPtr_u64 = @ptrCast([*]u64, &bmod); + const aPtr_u16 = @ptrCast([*]u16, &amod); + const bPtr_u16 = @ptrCast([*]u16, &bmod); + + const exp_and_sign_index = comptime switch (builtin.target.cpu.arch.endian()) { + .Little => 7, + .Big => 0, + }; + const low_index = comptime switch (builtin.target.cpu.arch.endian()) { + .Little => 0, + .Big => 1, + }; + const high_index = comptime switch (builtin.target.cpu.arch.endian()) { + .Little => 1, + .Big => 0, + }; + + const signA = aPtr_u16[exp_and_sign_index] & 0x8000; + var expA = @intCast(i32, (aPtr_u16[exp_and_sign_index] & 0x7fff)); + var expB = @intCast(i32, (bPtr_u16[exp_and_sign_index] & 0x7fff)); + + // There are 3 cases where the answer is undefined, check for: + // - fmodq(val, 0) + // - fmodq(val, NaN) + // - fmodq(inf, val) + // The sign on checked values does not matter. + // Doing (a * b) / (a * b) procudes undefined results + // because the three cases always produce undefined calculations: + // - 0 / 0 + // - val * NaN + // - inf / inf + if (b == 0 or std.math.isNan(b) or expA == 0x7fff) { + return (a * b) / (a * b); + } + + // Remove the sign from both + aPtr_u16[exp_and_sign_index] = @bitCast(u16, @intCast(i16, expA)); + bPtr_u16[exp_and_sign_index] = @bitCast(u16, @intCast(i16, expB)); + if (amod <= bmod) { + if (amod == bmod) { + return 0 * a; + } + return a; + } + + if (expA == 0) { + amod *= 0x1p120; + expA = @as(i32, aPtr_u16[exp_and_sign_index]) - 120; + } + + if (expB == 0) { + bmod *= 0x1p120; + expB = @as(i32, bPtr_u16[exp_and_sign_index]) - 120; + } + + // OR in extra non-stored mantissa digit + var highA: u64 = (aPtr_u64[high_index] & (std.math.maxInt(u64) >> 16)) | 1 << 48; + var highB: u64 = (bPtr_u64[high_index] & (std.math.maxInt(u64) >> 16)) | 1 << 48; + var lowA: u64 = aPtr_u64[low_index]; + var lowB: u64 = bPtr_u64[low_index]; + + while (expA > expB) : (expA -= 1) { + var high = highA -% highB; + var low = lowA -% lowB; + if (lowA < lowB) { + high -%= 1; + } + if (high >> 63 == 0) { + if ((high | low) == 0) { + return 0 * a; + } + highA = 2 *% high + (low >> 63); + lowA = 2 *% low; + } else { + highA = 2 *% highA + (lowA >> 63); + lowA = 2 *% lowA; + } + } + + var high = highA -% highB; + var low = lowA -% lowB; + if (lowA < lowB) { + high -= 1; + } + if (high >> 63 == 0) { + if ((high | low) == 0) { + return 0 * a; + } + highA = high; + lowA = low; + } + + while (highA >> 48 == 0) { + highA = 2 *% highA + (lowA >> 63); + lowA = 2 *% lowA; + expA = expA - 1; + } + + // Overwrite the current amod with the values in highA and lowA + aPtr_u64[high_index] = highA; + aPtr_u64[low_index] = lowA; + + // Combine the exponent with the sign, normalize if happend to be denormalized + if (expA <= 0) { + aPtr_u16[exp_and_sign_index] = @truncate(u16, @bitCast(u32, (expA +% 120))) | signA; + amod *= 0x1p-120; + } else { + aPtr_u16[exp_and_sign_index] = @truncate(u16, @bitCast(u32, expA)) | signA; + } + + return amod; +} + +inline fn generic_fmod(comptime T: type, x: T, y: T) T { + @setRuntimeSafety(false); + + const bits = @typeInfo(T).Float.bits; + const uint = std.meta.Int(.unsigned, bits); + const log2uint = math.Log2Int(uint); + comptime assert(T == f32 or T == f64); + const digits = if (T == f32) 23 else 52; + const exp_bits = if (T == f32) 9 else 12; + const bits_minus_1 = bits - 1; + const mask = if (T == f32) 0xff else 0x7ff; + var ux = @bitCast(uint, x); + var uy = @bitCast(uint, y); + var ex = @intCast(i32, (ux >> digits) & mask); + var ey = @intCast(i32, (uy >> digits) & mask); + const sx = if (T == f32) @intCast(u32, ux & 0x80000000) else @intCast(i32, ux >> bits_minus_1); + var i: uint = undefined; + + if (uy << 1 == 0 or math.isNan(@bitCast(T, uy)) or ex == mask) + return (x * y) / (x * y); + + if (ux << 1 <= uy << 1) { + if (ux << 1 == uy << 1) + return 0 * x; + return x; + } + + // normalize x and y + if (ex == 0) { + i = ux << exp_bits; + while (i >> bits_minus_1 == 0) : ({ + ex -= 1; + i <<= 1; + }) {} + ux <<= @intCast(log2uint, @bitCast(u32, -ex + 1)); + } else { + ux &= math.maxInt(uint) >> exp_bits; + ux |= 1 << digits; + } + if (ey == 0) { + i = uy << exp_bits; + while (i >> bits_minus_1 == 0) : ({ + ey -= 1; + i <<= 1; + }) {} + uy <<= @intCast(log2uint, @bitCast(u32, -ey + 1)); + } else { + uy &= math.maxInt(uint) >> exp_bits; + uy |= 1 << digits; + } + + // x mod y + while (ex > ey) : (ex -= 1) { + i = ux -% uy; + if (i >> bits_minus_1 == 0) { + if (i == 0) + return 0 * x; + ux = i; + } + ux <<= 1; + } + i = ux -% uy; + if (i >> bits_minus_1 == 0) { + if (i == 0) + return 0 * x; + ux = i; + } + while (ux >> digits == 0) : ({ + ux <<= 1; + ex -= 1; + }) {} + + // scale result up + if (ex > 0) { + ux -%= 1 << digits; + ux |= @as(uint, @bitCast(u32, ex)) << digits; + } else { + ux >>= @intCast(log2uint, @bitCast(u32, -ex + 1)); + } + if (T == f32) { + ux |= sx; + } else { + ux |= @intCast(uint, sx) << bits_minus_1; + } + return @bitCast(T, ux); +} + +test "fmod, fmodf" { + inline for ([_]type{ f32, f64 }) |T| { + const nan_val = math.nan(T); + const inf_val = math.inf(T); + + try std.testing.expect(math.isNan(generic_fmod(T, nan_val, 1.0))); + try std.testing.expect(math.isNan(generic_fmod(T, 1.0, nan_val))); + try std.testing.expect(math.isNan(generic_fmod(T, inf_val, 1.0))); + try std.testing.expect(math.isNan(generic_fmod(T, 0.0, 0.0))); + try std.testing.expect(math.isNan(generic_fmod(T, 1.0, 0.0))); + + try std.testing.expectEqual(@as(T, 0.0), generic_fmod(T, 0.0, 2.0)); + try std.testing.expectEqual(@as(T, -0.0), generic_fmod(T, -0.0, 2.0)); + + try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, 10.0)); + try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, -10.0)); + try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, 10.0)); + try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, -10.0)); + } +} + +test { + _ = @import("fmodq_test.zig"); + _ = @import("fmodx_test.zig"); +} diff --git a/lib/std/special/compiler_rt/fmodq.zig b/lib/std/special/compiler_rt/fmodq.zig deleted file mode 100644 index 3f56c49796..0000000000 --- a/lib/std/special/compiler_rt/fmodq.zig +++ /dev/null @@ -1,126 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); - -// fmodq - floating modulo large, returns the remainder of division for f128 types -// Logic and flow heavily inspired by MUSL fmodl for 113 mantissa digits -pub fn fmodq(a: f128, b: f128) callconv(.C) f128 { - @setRuntimeSafety(builtin.is_test); - var amod = a; - var bmod = b; - const aPtr_u64 = @ptrCast([*]u64, &amod); - const bPtr_u64 = @ptrCast([*]u64, &bmod); - const aPtr_u16 = @ptrCast([*]u16, &amod); - const bPtr_u16 = @ptrCast([*]u16, &bmod); - - const exp_and_sign_index = comptime switch (builtin.target.cpu.arch.endian()) { - .Little => 7, - .Big => 0, - }; - const low_index = comptime switch (builtin.target.cpu.arch.endian()) { - .Little => 0, - .Big => 1, - }; - const high_index = comptime switch (builtin.target.cpu.arch.endian()) { - .Little => 1, - .Big => 0, - }; - - const signA = aPtr_u16[exp_and_sign_index] & 0x8000; - var expA = @intCast(i32, (aPtr_u16[exp_and_sign_index] & 0x7fff)); - var expB = @intCast(i32, (bPtr_u16[exp_and_sign_index] & 0x7fff)); - - // There are 3 cases where the answer is undefined, check for: - // - fmodq(val, 0) - // - fmodq(val, NaN) - // - fmodq(inf, val) - // The sign on checked values does not matter. - // Doing (a * b) / (a * b) procudes undefined results - // because the three cases always produce undefined calculations: - // - 0 / 0 - // - val * NaN - // - inf / inf - if (b == 0 or std.math.isNan(b) or expA == 0x7fff) { - return (a * b) / (a * b); - } - - // Remove the sign from both - aPtr_u16[exp_and_sign_index] = @bitCast(u16, @intCast(i16, expA)); - bPtr_u16[exp_and_sign_index] = @bitCast(u16, @intCast(i16, expB)); - if (amod <= bmod) { - if (amod == bmod) { - return 0 * a; - } - return a; - } - - if (expA == 0) { - amod *= 0x1p120; - expA = @as(i32, aPtr_u16[exp_and_sign_index]) - 120; - } - - if (expB == 0) { - bmod *= 0x1p120; - expB = @as(i32, bPtr_u16[exp_and_sign_index]) - 120; - } - - // OR in extra non-stored mantissa digit - var highA: u64 = (aPtr_u64[high_index] & (std.math.maxInt(u64) >> 16)) | 1 << 48; - var highB: u64 = (bPtr_u64[high_index] & (std.math.maxInt(u64) >> 16)) | 1 << 48; - var lowA: u64 = aPtr_u64[low_index]; - var lowB: u64 = bPtr_u64[low_index]; - - while (expA > expB) : (expA -= 1) { - var high = highA -% highB; - var low = lowA -% lowB; - if (lowA < lowB) { - high -%= 1; - } - if (high >> 63 == 0) { - if ((high | low) == 0) { - return 0 * a; - } - highA = 2 *% high + (low >> 63); - lowA = 2 *% low; - } else { - highA = 2 *% highA + (lowA >> 63); - lowA = 2 *% lowA; - } - } - - var high = highA -% highB; - var low = lowA -% lowB; - if (lowA < lowB) { - high -= 1; - } - if (high >> 63 == 0) { - if ((high | low) == 0) { - return 0 * a; - } - highA = high; - lowA = low; - } - - while (highA >> 48 == 0) { - highA = 2 *% highA + (lowA >> 63); - lowA = 2 *% lowA; - expA = expA - 1; - } - - // Overwrite the current amod with the values in highA and lowA - aPtr_u64[high_index] = highA; - aPtr_u64[low_index] = lowA; - - // Combine the exponent with the sign, normalize if happend to be denormalized - if (expA <= 0) { - aPtr_u16[exp_and_sign_index] = @truncate(u16, @bitCast(u32, (expA +% 120))) | signA; - amod *= 0x1p-120; - } else { - aPtr_u16[exp_and_sign_index] = @truncate(u16, @bitCast(u32, expA)) | signA; - } - - return amod; -} - -test { - _ = @import("fmodq_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fmodq_test.zig b/lib/std/special/compiler_rt/fmodq_test.zig index b8baf8ae9b..07ddb8d182 100644 --- a/lib/std/special/compiler_rt/fmodq_test.zig +++ b/lib/std/special/compiler_rt/fmodq_test.zig @@ -1,24 +1,24 @@ const std = @import("std"); -const fmodq = @import("fmodq.zig"); +const fmod = @import("fmod.zig"); const testing = std.testing; fn test_fmodq(a: f128, b: f128, exp: f128) !void { - const res = fmodq.fmodq(a, b); + const res = fmod.fmodq(a, b); try testing.expect(exp == res); } fn test_fmodq_nans() !void { - try testing.expect(std.math.isNan(fmodq.fmodq(1.0, std.math.nan(f128)))); - try testing.expect(std.math.isNan(fmodq.fmodq(1.0, -std.math.nan(f128)))); - try testing.expect(std.math.isNan(fmodq.fmodq(std.math.nan(f128), 1.0))); - try testing.expect(std.math.isNan(fmodq.fmodq(-std.math.nan(f128), 1.0))); + try testing.expect(std.math.isNan(fmod.fmodq(1.0, std.math.nan(f128)))); + try testing.expect(std.math.isNan(fmod.fmodq(1.0, -std.math.nan(f128)))); + try testing.expect(std.math.isNan(fmod.fmodq(std.math.nan(f128), 1.0))); + try testing.expect(std.math.isNan(fmod.fmodq(-std.math.nan(f128), 1.0))); } fn test_fmodq_infs() !void { - try testing.expect(fmodq.fmodq(1.0, std.math.inf(f128)) == 1.0); - try testing.expect(fmodq.fmodq(1.0, -std.math.inf(f128)) == 1.0); - try testing.expect(std.math.isNan(fmodq.fmodq(std.math.inf(f128), 1.0))); - try testing.expect(std.math.isNan(fmodq.fmodq(-std.math.inf(f128), 1.0))); + try testing.expect(fmod.fmodq(1.0, std.math.inf(f128)) == 1.0); + try testing.expect(fmod.fmodq(1.0, -std.math.inf(f128)) == 1.0); + try testing.expect(std.math.isNan(fmod.fmodq(std.math.inf(f128), 1.0))); + try testing.expect(std.math.isNan(fmod.fmodq(-std.math.inf(f128), 1.0))); } test "fmodq" { diff --git a/lib/std/special/compiler_rt/fmodx.zig b/lib/std/special/compiler_rt/fmodx.zig deleted file mode 100644 index efe16f9f16..0000000000 --- a/lib/std/special/compiler_rt/fmodx.zig +++ /dev/null @@ -1,108 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const math = std.math; -const normalize = @import("divdf3.zig").normalize; - -// fmodx - floating modulo large, returns the remainder of division for f80 types -// Logic and flow heavily inspired by MUSL fmodl for 113 mantissa digits -pub fn fmodx(a: f80, b: f80) callconv(.C) f80 { - @setRuntimeSafety(builtin.is_test); - - const T = f80; - const Z = std.meta.Int(.unsigned, @bitSizeOf(T)); - - const significandBits = math.floatMantissaBits(T); - const fractionalBits = math.floatFractionalBits(T); - const exponentBits = math.floatExponentBits(T); - - const signBit = (@as(Z, 1) << (significandBits + exponentBits)); - const maxExponent = ((1 << exponentBits) - 1); - - var aRep = @bitCast(Z, a); - var bRep = @bitCast(Z, b); - - const signA = aRep & signBit; - var expA = @intCast(i32, (@bitCast(Z, a) >> significandBits) & maxExponent); - var expB = @intCast(i32, (@bitCast(Z, b) >> significandBits) & maxExponent); - - // There are 3 cases where the answer is undefined, check for: - // - fmodx(val, 0) - // - fmodx(val, NaN) - // - fmodx(inf, val) - // The sign on checked values does not matter. - // Doing (a * b) / (a * b) procudes undefined results - // because the three cases always produce undefined calculations: - // - 0 / 0 - // - val * NaN - // - inf / inf - if (b == 0 or math.isNan(b) or expA == maxExponent) { - return (a * b) / (a * b); - } - - // Remove the sign from both - aRep &= ~signBit; - bRep &= ~signBit; - if (aRep <= bRep) { - if (aRep == bRep) { - return 0 * a; - } - return a; - } - - if (expA == 0) expA = normalize(f80, &aRep); - if (expB == 0) expB = normalize(f80, &bRep); - - var highA: u64 = 0; - var highB: u64 = 0; - var lowA: u64 = @truncate(u64, aRep); - var lowB: u64 = @truncate(u64, bRep); - - while (expA > expB) : (expA -= 1) { - var high = highA -% highB; - var low = lowA -% lowB; - if (lowA < lowB) { - high -%= 1; - } - if (high >> 63 == 0) { - if ((high | low) == 0) { - return 0 * a; - } - highA = 2 *% high + (low >> 63); - lowA = 2 *% low; - } else { - highA = 2 *% highA + (lowA >> 63); - lowA = 2 *% lowA; - } - } - - var high = highA -% highB; - var low = lowA -% lowB; - if (lowA < lowB) { - high -%= 1; - } - if (high >> 63 == 0) { - if ((high | low) == 0) { - return 0 * a; - } - highA = high; - lowA = low; - } - - while ((lowA >> fractionalBits) == 0) { - lowA = 2 *% lowA; - expA = expA - 1; - } - - // Combine the exponent with the sign and significand, normalize if happened to be denormalized - if (expA < -fractionalBits) { - return @bitCast(T, signA); - } else if (expA <= 0) { - return @bitCast(T, (lowA >> @intCast(math.Log2Int(u64), 1 - expA)) | signA); - } else { - return @bitCast(T, lowA | (@as(Z, @intCast(u16, expA)) << significandBits) | signA); - } -} - -test { - _ = @import("fmodx_test.zig"); -} diff --git a/lib/std/special/compiler_rt/fmodx_test.zig b/lib/std/special/compiler_rt/fmodx_test.zig index a5d0887ea4..4bb1b5654a 100644 --- a/lib/std/special/compiler_rt/fmodx_test.zig +++ b/lib/std/special/compiler_rt/fmodx_test.zig @@ -1,24 +1,24 @@ const std = @import("std"); -const fmodx = @import("fmodx.zig"); +const fmod = @import("fmod.zig"); const testing = std.testing; fn test_fmodx(a: f80, b: f80, exp: f80) !void { - const res = fmodx.fmodx(a, b); + const res = fmod.__fmodx(a, b); try testing.expect(exp == res); } fn test_fmodx_nans() !void { - try testing.expect(std.math.isNan(fmodx.fmodx(1.0, std.math.nan(f80)))); - try testing.expect(std.math.isNan(fmodx.fmodx(1.0, -std.math.nan(f80)))); - try testing.expect(std.math.isNan(fmodx.fmodx(std.math.nan(f80), 1.0))); - try testing.expect(std.math.isNan(fmodx.fmodx(-std.math.nan(f80), 1.0))); + try testing.expect(std.math.isNan(fmod.__fmodx(1.0, std.math.nan(f80)))); + try testing.expect(std.math.isNan(fmod.__fmodx(1.0, -std.math.nan(f80)))); + try testing.expect(std.math.isNan(fmod.__fmodx(std.math.nan(f80), 1.0))); + try testing.expect(std.math.isNan(fmod.__fmodx(-std.math.nan(f80), 1.0))); } fn test_fmodx_infs() !void { - try testing.expect(fmodx.fmodx(1.0, std.math.inf(f80)) == 1.0); - try testing.expect(fmodx.fmodx(1.0, -std.math.inf(f80)) == 1.0); - try testing.expect(std.math.isNan(fmodx.fmodx(std.math.inf(f80), 1.0))); - try testing.expect(std.math.isNan(fmodx.fmodx(-std.math.inf(f80), 1.0))); + try testing.expect(fmod.__fmodx(1.0, std.math.inf(f80)) == 1.0); + try testing.expect(fmod.__fmodx(1.0, -std.math.inf(f80)) == 1.0); + try testing.expect(std.math.isNan(fmod.__fmodx(std.math.inf(f80), 1.0))); + try testing.expect(std.math.isNan(fmod.__fmodx(-std.math.inf(f80), 1.0))); } test "fmodx" { diff --git a/lib/std/special/compiler_rt/log.zig b/lib/std/special/compiler_rt/log.zig new file mode 100644 index 0000000000..8b09baac2e --- /dev/null +++ b/lib/std/special/compiler_rt/log.zig @@ -0,0 +1,168 @@ +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +// +// https://git.musl-libc.org/cgit/musl/tree/src/math/lnf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/ln.c + +const std = @import("std"); +const math = std.math; +const testing = std.testing; + +pub fn __logh(a: f16) callconv(.C) f16 { + // TODO: more efficient implementation + return @floatCast(f16, logf(a)); +} + +pub fn logf(x_: f32) callconv(.C) f32 { + const ln2_hi: f32 = 6.9313812256e-01; + const ln2_lo: f32 = 9.0580006145e-06; + const Lg1: f32 = 0xaaaaaa.0p-24; + const Lg2: f32 = 0xccce13.0p-25; + const Lg3: f32 = 0x91e9ee.0p-25; + const Lg4: f32 = 0xf89e26.0p-26; + + var x = x_; + var ix = @bitCast(u32, x); + var k: i32 = 0; + + // x < 2^(-126) + if (ix < 0x00800000 or ix >> 31 != 0) { + // log(+-0) = -inf + if (ix << 1 == 0) { + return -math.inf(f32); + } + // log(-#) = nan + if (ix >> 31 != 0) { + return math.nan(f32); + } + + // subnormal, scale x + k -= 25; + x *= 0x1.0p25; + ix = @bitCast(u32, x); + } else if (ix >= 0x7F800000) { + return x; + } else if (ix == 0x3F800000) { + return 0; + } + + // x into [sqrt(2) / 2, sqrt(2)] + ix += 0x3F800000 - 0x3F3504F3; + k += @intCast(i32, ix >> 23) - 0x7F; + ix = (ix & 0x007FFFFF) + 0x3F3504F3; + x = @bitCast(f32, ix); + + const f = x - 1.0; + const s = f / (2.0 + f); + const z = s * s; + const w = z * z; + const t1 = w * (Lg2 + w * Lg4); + const t2 = z * (Lg1 + w * Lg3); + const R = t2 + t1; + const hfsq = 0.5 * f * f; + const dk = @intToFloat(f32, k); + + return s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi; +} + +pub fn log(x_: f64) callconv(.C) f64 { + const ln2_hi: f64 = 6.93147180369123816490e-01; + const ln2_lo: f64 = 1.90821492927058770002e-10; + const Lg1: f64 = 6.666666666666735130e-01; + const Lg2: f64 = 3.999999999940941908e-01; + const Lg3: f64 = 2.857142874366239149e-01; + const Lg4: f64 = 2.222219843214978396e-01; + const Lg5: f64 = 1.818357216161805012e-01; + const Lg6: f64 = 1.531383769920937332e-01; + const Lg7: f64 = 1.479819860511658591e-01; + + var x = x_; + var ix = @bitCast(u64, x); + var hx = @intCast(u32, ix >> 32); + var k: i32 = 0; + + if (hx < 0x00100000 or hx >> 31 != 0) { + // log(+-0) = -inf + if (ix << 1 == 0) { + return -math.inf(f64); + } + // log(-#) = nan + if (hx >> 31 != 0) { + return math.nan(f64); + } + + // subnormal, scale x + k -= 54; + x *= 0x1.0p54; + hx = @intCast(u32, @bitCast(u64, ix) >> 32); + } else if (hx >= 0x7FF00000) { + return x; + } else if (hx == 0x3FF00000 and ix << 32 == 0) { + return 0; + } + + // x into [sqrt(2) / 2, sqrt(2)] + hx += 0x3FF00000 - 0x3FE6A09E; + k += @intCast(i32, hx >> 20) - 0x3FF; + hx = (hx & 0x000FFFFF) + 0x3FE6A09E; + ix = (@as(u64, hx) << 32) | (ix & 0xFFFFFFFF); + x = @bitCast(f64, ix); + + const f = x - 1.0; + const hfsq = 0.5 * f * f; + const s = f / (2.0 + f); + const z = s * s; + const w = z * z; + const t1 = w * (Lg2 + w * (Lg4 + w * Lg6)); + const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7))); + const R = t2 + t1; + const dk = @intToFloat(f64, k); + + return s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi; +} + +pub fn __logx(a: f80) callconv(.C) f80 { + // TODO: more efficient implementation + return @floatCast(f80, logq(a)); +} + +pub fn logq(a: f128) callconv(.C) f128 { + // TODO: more correct implementation + return log(@floatCast(f64, a)); +} + +test "ln32" { + const epsilon = 0.000001; + + try testing.expect(math.approxEqAbs(f32, logf(0.2), -1.609438, epsilon)); + try testing.expect(math.approxEqAbs(f32, logf(0.8923), -0.113953, epsilon)); + try testing.expect(math.approxEqAbs(f32, logf(1.5), 0.405465, epsilon)); + try testing.expect(math.approxEqAbs(f32, logf(37.45), 3.623007, epsilon)); + try testing.expect(math.approxEqAbs(f32, logf(89.123), 4.490017, epsilon)); + try testing.expect(math.approxEqAbs(f32, logf(123123.234375), 11.720941, epsilon)); +} + +test "ln64" { + const epsilon = 0.000001; + + try testing.expect(math.approxEqAbs(f64, log(0.2), -1.609438, epsilon)); + try testing.expect(math.approxEqAbs(f64, log(0.8923), -0.113953, epsilon)); + try testing.expect(math.approxEqAbs(f64, log(1.5), 0.405465, epsilon)); + try testing.expect(math.approxEqAbs(f64, log(37.45), 3.623007, epsilon)); + try testing.expect(math.approxEqAbs(f64, log(89.123), 4.490017, epsilon)); + try testing.expect(math.approxEqAbs(f64, log(123123.234375), 11.720941, epsilon)); +} + +test "ln32.special" { + try testing.expect(math.isPositiveInf(logf(math.inf(f32)))); + try testing.expect(math.isNegativeInf(logf(0.0))); + try testing.expect(math.isNan(logf(-1.0))); + try testing.expect(math.isNan(logf(math.nan(f32)))); +} + +test "ln64.special" { + try testing.expect(math.isPositiveInf(log(math.inf(f64)))); + try testing.expect(math.isNegativeInf(log(0.0))); + try testing.expect(math.isNan(log(-1.0))); + try testing.expect(math.isNan(log(math.nan(f64)))); +} diff --git a/lib/std/special/compiler_rt/log10.zig b/lib/std/special/compiler_rt/log10.zig new file mode 100644 index 0000000000..ce06d8c649 --- /dev/null +++ b/lib/std/special/compiler_rt/log10.zig @@ -0,0 +1,196 @@ +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +// +// https://git.musl-libc.org/cgit/musl/tree/src/math/log10f.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/log10.c + +const std = @import("std"); +const math = std.math; +const testing = std.testing; +const maxInt = std.math.maxInt; + +pub fn __log10h(a: f16) callconv(.C) f16 { + // TODO: more efficient implementation + return @floatCast(f16, log10f(a)); +} + +pub fn log10f(x_: f32) callconv(.C) f32 { + const ivln10hi: f32 = 4.3432617188e-01; + const ivln10lo: f32 = -3.1689971365e-05; + const log10_2hi: f32 = 3.0102920532e-01; + const log10_2lo: f32 = 7.9034151668e-07; + const Lg1: f32 = 0xaaaaaa.0p-24; + const Lg2: f32 = 0xccce13.0p-25; + const Lg3: f32 = 0x91e9ee.0p-25; + const Lg4: f32 = 0xf89e26.0p-26; + + var x = x_; + var u = @bitCast(u32, x); + var ix = u; + var k: i32 = 0; + + // x < 2^(-126) + if (ix < 0x00800000 or ix >> 31 != 0) { + // log(+-0) = -inf + if (ix << 1 == 0) { + return -math.inf(f32); + } + // log(-#) = nan + if (ix >> 31 != 0) { + return math.nan(f32); + } + + k -= 25; + x *= 0x1.0p25; + ix = @bitCast(u32, x); + } else if (ix >= 0x7F800000) { + return x; + } else if (ix == 0x3F800000) { + return 0; + } + + // x into [sqrt(2) / 2, sqrt(2)] + ix += 0x3F800000 - 0x3F3504F3; + k += @intCast(i32, ix >> 23) - 0x7F; + ix = (ix & 0x007FFFFF) + 0x3F3504F3; + x = @bitCast(f32, ix); + + const f = x - 1.0; + const s = f / (2.0 + f); + const z = s * s; + const w = z * z; + const t1 = w * (Lg2 + w * Lg4); + const t2 = z * (Lg1 + w * Lg3); + const R = t2 + t1; + const hfsq = 0.5 * f * f; + + var hi = f - hfsq; + u = @bitCast(u32, hi); + u &= 0xFFFFF000; + hi = @bitCast(f32, u); + const lo = f - hi - hfsq + s * (hfsq + R); + const dk = @intToFloat(f32, k); + + return dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi + hi * ivln10hi + dk * log10_2hi; +} + +pub fn log10(x_: f64) callconv(.C) f64 { + const ivln10hi: f64 = 4.34294481878168880939e-01; + const ivln10lo: f64 = 2.50829467116452752298e-11; + const log10_2hi: f64 = 3.01029995663611771306e-01; + const log10_2lo: f64 = 3.69423907715893078616e-13; + const Lg1: f64 = 6.666666666666735130e-01; + const Lg2: f64 = 3.999999999940941908e-01; + const Lg3: f64 = 2.857142874366239149e-01; + const Lg4: f64 = 2.222219843214978396e-01; + const Lg5: f64 = 1.818357216161805012e-01; + const Lg6: f64 = 1.531383769920937332e-01; + const Lg7: f64 = 1.479819860511658591e-01; + + var x = x_; + var ix = @bitCast(u64, x); + var hx = @intCast(u32, ix >> 32); + var k: i32 = 0; + + if (hx < 0x00100000 or hx >> 31 != 0) { + // log(+-0) = -inf + if (ix << 1 == 0) { + return -math.inf(f32); + } + // log(-#) = nan + if (hx >> 31 != 0) { + return math.nan(f32); + } + + // subnormal, scale x + k -= 54; + x *= 0x1.0p54; + hx = @intCast(u32, @bitCast(u64, x) >> 32); + } else if (hx >= 0x7FF00000) { + return x; + } else if (hx == 0x3FF00000 and ix << 32 == 0) { + return 0; + } + + // x into [sqrt(2) / 2, sqrt(2)] + hx += 0x3FF00000 - 0x3FE6A09E; + k += @intCast(i32, hx >> 20) - 0x3FF; + hx = (hx & 0x000FFFFF) + 0x3FE6A09E; + ix = (@as(u64, hx) << 32) | (ix & 0xFFFFFFFF); + x = @bitCast(f64, ix); + + const f = x - 1.0; + const hfsq = 0.5 * f * f; + const s = f / (2.0 + f); + const z = s * s; + const w = z * z; + const t1 = w * (Lg2 + w * (Lg4 + w * Lg6)); + const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7))); + const R = t2 + t1; + + // hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f) + var hi = f - hfsq; + var hii = @bitCast(u64, hi); + hii &= @as(u64, maxInt(u64)) << 32; + hi = @bitCast(f64, hii); + const lo = f - hi - hfsq + s * (hfsq + R); + + // val_hi + val_lo ~ log10(1 + f) + k * log10(2) + var val_hi = hi * ivln10hi; + const dk = @intToFloat(f64, k); + const y = dk * log10_2hi; + var val_lo = dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi; + + // Extra precision multiplication + const ww = y + val_hi; + val_lo += (y - ww) + val_hi; + val_hi = ww; + + return val_lo + val_hi; +} + +pub fn __log10x(a: f80) callconv(.C) f80 { + // TODO: more efficient implementation + return @floatCast(f80, log10q(a)); +} + +pub fn log10q(a: f128) callconv(.C) f128 { + // TODO: more correct implementation + return log10(@floatCast(f64, a)); +} + +test "log10_32" { + const epsilon = 0.000001; + + try testing.expect(math.approxEqAbs(f32, log10f(0.2), -0.698970, epsilon)); + try testing.expect(math.approxEqAbs(f32, log10f(0.8923), -0.049489, epsilon)); + try testing.expect(math.approxEqAbs(f32, log10f(1.5), 0.176091, epsilon)); + try testing.expect(math.approxEqAbs(f32, log10f(37.45), 1.573452, epsilon)); + try testing.expect(math.approxEqAbs(f32, log10f(89.123), 1.94999, epsilon)); + try testing.expect(math.approxEqAbs(f32, log10f(123123.234375), 5.09034, epsilon)); +} + +test "log10_64" { + const epsilon = 0.000001; + + try testing.expect(math.approxEqAbs(f64, log10(0.2), -0.698970, epsilon)); + try testing.expect(math.approxEqAbs(f64, log10(0.8923), -0.049489, epsilon)); + try testing.expect(math.approxEqAbs(f64, log10(1.5), 0.176091, epsilon)); + try testing.expect(math.approxEqAbs(f64, log10(37.45), 1.573452, epsilon)); + try testing.expect(math.approxEqAbs(f64, log10(89.123), 1.94999, epsilon)); + try testing.expect(math.approxEqAbs(f64, log10(123123.234375), 5.09034, epsilon)); +} + +test "log10_32.special" { + try testing.expect(math.isPositiveInf(log10f(math.inf(f32)))); + try testing.expect(math.isNegativeInf(log10f(0.0))); + try testing.expect(math.isNan(log10f(-1.0))); + try testing.expect(math.isNan(log10f(math.nan(f32)))); +} + +test "log10_64.special" { + try testing.expect(math.isPositiveInf(log10(math.inf(f64)))); + try testing.expect(math.isNegativeInf(log10(0.0))); + try testing.expect(math.isNan(log10(-1.0))); + try testing.expect(math.isNan(log10(math.nan(f64)))); +} diff --git a/lib/std/special/compiler_rt/log2.zig b/lib/std/special/compiler_rt/log2.zig new file mode 100644 index 0000000000..2c2d620c3d --- /dev/null +++ b/lib/std/special/compiler_rt/log2.zig @@ -0,0 +1,185 @@ +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +// +// https://git.musl-libc.org/cgit/musl/tree/src/math/log2f.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/log2.c + +const std = @import("std"); +const math = std.math; +const expect = std.testing.expect; +const maxInt = std.math.maxInt; + +pub fn __log2h(a: f16) callconv(.C) f16 { + // TODO: more efficient implementation + return @floatCast(f16, log2f(a)); +} + +pub fn log2f(x_: f32) callconv(.C) f32 { + const ivln2hi: f32 = 1.4428710938e+00; + const ivln2lo: f32 = -1.7605285393e-04; + const Lg1: f32 = 0xaaaaaa.0p-24; + const Lg2: f32 = 0xccce13.0p-25; + const Lg3: f32 = 0x91e9ee.0p-25; + const Lg4: f32 = 0xf89e26.0p-26; + + var x = x_; + var u = @bitCast(u32, x); + var ix = u; + var k: i32 = 0; + + // x < 2^(-126) + if (ix < 0x00800000 or ix >> 31 != 0) { + // log(+-0) = -inf + if (ix << 1 == 0) { + return -math.inf(f32); + } + // log(-#) = nan + if (ix >> 31 != 0) { + return math.nan(f32); + } + + k -= 25; + x *= 0x1.0p25; + ix = @bitCast(u32, x); + } else if (ix >= 0x7F800000) { + return x; + } else if (ix == 0x3F800000) { + return 0; + } + + // x into [sqrt(2) / 2, sqrt(2)] + ix += 0x3F800000 - 0x3F3504F3; + k += @intCast(i32, ix >> 23) - 0x7F; + ix = (ix & 0x007FFFFF) + 0x3F3504F3; + x = @bitCast(f32, ix); + + const f = x - 1.0; + const s = f / (2.0 + f); + const z = s * s; + const w = z * z; + const t1 = w * (Lg2 + w * Lg4); + const t2 = z * (Lg1 + w * Lg3); + const R = t2 + t1; + const hfsq = 0.5 * f * f; + + var hi = f - hfsq; + u = @bitCast(u32, hi); + u &= 0xFFFFF000; + hi = @bitCast(f32, u); + const lo = f - hi - hfsq + s * (hfsq + R); + return (lo + hi) * ivln2lo + lo * ivln2hi + hi * ivln2hi + @intToFloat(f32, k); +} + +pub fn log2(x_: f64) callconv(.C) f64 { + const ivln2hi: f64 = 1.44269504072144627571e+00; + const ivln2lo: f64 = 1.67517131648865118353e-10; + const Lg1: f64 = 6.666666666666735130e-01; + const Lg2: f64 = 3.999999999940941908e-01; + const Lg3: f64 = 2.857142874366239149e-01; + const Lg4: f64 = 2.222219843214978396e-01; + const Lg5: f64 = 1.818357216161805012e-01; + const Lg6: f64 = 1.531383769920937332e-01; + const Lg7: f64 = 1.479819860511658591e-01; + + var x = x_; + var ix = @bitCast(u64, x); + var hx = @intCast(u32, ix >> 32); + var k: i32 = 0; + + if (hx < 0x00100000 or hx >> 31 != 0) { + // log(+-0) = -inf + if (ix << 1 == 0) { + return -math.inf(f64); + } + // log(-#) = nan + if (hx >> 31 != 0) { + return math.nan(f64); + } + + // subnormal, scale x + k -= 54; + x *= 0x1.0p54; + hx = @intCast(u32, @bitCast(u64, x) >> 32); + } else if (hx >= 0x7FF00000) { + return x; + } else if (hx == 0x3FF00000 and ix << 32 == 0) { + return 0; + } + + // x into [sqrt(2) / 2, sqrt(2)] + hx += 0x3FF00000 - 0x3FE6A09E; + k += @intCast(i32, hx >> 20) - 0x3FF; + hx = (hx & 0x000FFFFF) + 0x3FE6A09E; + ix = (@as(u64, hx) << 32) | (ix & 0xFFFFFFFF); + x = @bitCast(f64, ix); + + const f = x - 1.0; + const hfsq = 0.5 * f * f; + const s = f / (2.0 + f); + const z = s * s; + const w = z * z; + const t1 = w * (Lg2 + w * (Lg4 + w * Lg6)); + const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7))); + const R = t2 + t1; + + // hi + lo = f - hfsq + s * (hfsq + R) ~ log(1 + f) + var hi = f - hfsq; + var hii = @bitCast(u64, hi); + hii &= @as(u64, maxInt(u64)) << 32; + hi = @bitCast(f64, hii); + const lo = f - hi - hfsq + s * (hfsq + R); + + var val_hi = hi * ivln2hi; + var val_lo = (lo + hi) * ivln2lo + lo * ivln2hi; + + // spadd(val_hi, val_lo, y) + const y = @intToFloat(f64, k); + const ww = y + val_hi; + val_lo += (y - ww) + val_hi; + val_hi = ww; + + return val_lo + val_hi; +} + +pub fn __log2x(a: f80) callconv(.C) f80 { + // TODO: more efficient implementation + return @floatCast(f80, log2q(a)); +} + +pub fn log2q(a: f128) callconv(.C) f128 { + return math.log2(a); +} + +test "log2_32" { + const epsilon = 0.000001; + + try expect(math.approxEqAbs(f32, log2f(0.2), -2.321928, epsilon)); + try expect(math.approxEqAbs(f32, log2f(0.8923), -0.164399, epsilon)); + try expect(math.approxEqAbs(f32, log2f(1.5), 0.584962, epsilon)); + try expect(math.approxEqAbs(f32, log2f(37.45), 5.226894, epsilon)); + try expect(math.approxEqAbs(f32, log2f(123123.234375), 16.909744, epsilon)); +} + +test "log2_64" { + const epsilon = 0.000001; + + try expect(math.approxEqAbs(f64, log2(0.2), -2.321928, epsilon)); + try expect(math.approxEqAbs(f64, log2(0.8923), -0.164399, epsilon)); + try expect(math.approxEqAbs(f64, log2(1.5), 0.584962, epsilon)); + try expect(math.approxEqAbs(f64, log2(37.45), 5.226894, epsilon)); + try expect(math.approxEqAbs(f64, log2(123123.234375), 16.909744, epsilon)); +} + +test "log2_32.special" { + try expect(math.isPositiveInf(log2f(math.inf(f32)))); + try expect(math.isNegativeInf(log2f(0.0))); + try expect(math.isNan(log2f(-1.0))); + try expect(math.isNan(log2f(math.nan(f32)))); +} + +test "log2_64.special" { + try expect(math.isPositiveInf(log2(math.inf(f64)))); + try expect(math.isNegativeInf(log2(0.0))); + try expect(math.isNan(log2(-1.0))); + try expect(math.isNan(log2(math.nan(f64)))); +} diff --git a/lib/std/math/__rem_pio2.zig b/lib/std/special/compiler_rt/rem_pio2.zig similarity index 95% rename from lib/std/math/__rem_pio2.zig rename to lib/std/special/compiler_rt/rem_pio2.zig index f01d8fe94a..73d477ee12 100644 --- a/lib/std/math/__rem_pio2.zig +++ b/lib/std/special/compiler_rt/rem_pio2.zig @@ -3,8 +3,8 @@ // // https://git.musl-libc.org/cgit/musl/tree/src/math/__rem_pio2.c -const std = @import("../std.zig"); -const __rem_pio2_large = @import("__rem_pio2_large.zig").__rem_pio2_large; +const std = @import("std"); +const rem_pio2_large = @import("rem_pio2_large.zig").rem_pio2_large; const math = std.math; const toint = 1.5 / math.floatEps(f64); @@ -82,10 +82,10 @@ fn medium(ix: u32, x: f64, y: *[2]f64) i32 { // Returns the remainder of x rem pi/2 in y[0]+y[1] // -// use __rem_pio2_large() for large x +// use rem_pio2_large() for large x // // caller must handle the case when reduction is not needed: |x| ~<= pi/4 */ -pub fn __rem_pio2(x: f64, y: *[2]f64) i32 { +pub fn rem_pio2(x: f64, y: *[2]f64) i32 { var z: f64 = undefined; var tx: [3]f64 = undefined; var ty: [2]f64 = undefined; @@ -186,7 +186,7 @@ pub fn __rem_pio2(x: f64, y: *[2]f64) i32 { while (tx[U(i)] == 0.0) { i -= 1; } - n = __rem_pio2_large(tx[0..], ty[0..], @intCast(i32, (ix >> 20)) - (0x3ff + 23), i + 1, 1); + n = rem_pio2_large(tx[0..], ty[0..], @intCast(i32, (ix >> 20)) - (0x3ff + 23), i + 1, 1); if (sign) { y[0] = -ty[0]; y[1] = -ty[1]; diff --git a/lib/std/math/__rem_pio2_large.zig b/lib/std/special/compiler_rt/rem_pio2_large.zig similarity index 75% rename from lib/std/math/__rem_pio2_large.zig rename to lib/std/special/compiler_rt/rem_pio2_large.zig index 140e85f7f6..c8a53b741c 100644 --- a/lib/std/math/__rem_pio2_large.zig +++ b/lib/std/special/compiler_rt/rem_pio2_large.zig @@ -3,23 +3,22 @@ // // https://git.musl-libc.org/cgit/musl/tree/src/math/__rem_pio2_large.c -const std = @import("../std.zig"); +const std = @import("std"); const math = std.math; const init_jk = [_]i32{ 3, 4, 4, 6 }; // initial value for jk -// -// Table of constants for 2/pi, 396 Hex digits (476 decimal) of 2/pi -// -// integer array, contains the (24*i)-th to (24*i+23)-th -// bit of 2/pi after binary point. The corresponding -// floating value is -// -// ipio2[i] * 2^(-24(i+1)). -// -// NB: This table must have at least (e0-3)/24 + jk terms. -// For quad precision (e0 <= 16360, jk = 6), this is 686. /// +/// Table of constants for 2/pi, 396 Hex digits (476 decimal) of 2/pi +/// +/// integer array, contains the (24*i)-th to (24*i+23)-th +/// bit of 2/pi after binary point. The corresponding +/// floating value is +/// +/// ipio2[i] * 2^(-24(i+1)). +/// +/// NB: This table must have at least (e0-3)/24 + jk terms. +/// For quad precision (e0 <= 16360, jk = 6), this is 686. const ipio2 = [_]i32{ 0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62, 0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A, @@ -33,7 +32,6 @@ const ipio2 = [_]i32{ 0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880, 0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B, - //#if LDBL_MAX_EXP > 1024 0x47C419, 0xC367CD, 0xDCE809, 0x2A8359, 0xC4768B, 0x961CA6, 0xDDAF44, 0xD15719, 0x053EA5, 0xFF0705, 0x3F7E33, 0xE832C2, 0xDE4F98, 0x327DBB, 0xC33D26, 0xEF6B1E, 0x5EF89F, 0x3A1F35, @@ -137,9 +135,7 @@ const ipio2 = [_]i32{ 0x237C7E, 0x32B90F, 0x8EF5A7, 0xE75614, 0x08F121, 0x2A9DB5, 0x4D7E6F, 0x5119A5, 0xABF9B5, 0xD6DF82, 0x61DD96, 0x023616, 0x9F3AC4, 0xA1A283, 0x6DED72, 0x7A8D39, 0xA9B882, 0x5C326B, - 0x5B2746, 0xED3400, 0x7700D2, 0x55F4FC, 0x4D5901, - 0x8071E0, - //#endif + 0x5B2746, 0xED3400, 0x7700D2, 0x55F4FC, 0x4D5901, 0x8071E0, }; const PIo2 = [_]f64{ @@ -157,109 +153,109 @@ fn U(x: anytype) usize { return @intCast(usize, x); } -// Returns the last three digits of N with y = x - N*pi/2 so that |y| < pi/2. -// -// The method is to compute the integer (mod 8) and fraction parts of -// (2/pi)*x without doing the full multiplication. In general we -// skip the part of the product that are known to be a huge integer ( -// more accurately, = 0 mod 8 ). Thus the number of operations are -// independent of the exponent of the input. -// -// (2/pi) is represented by an array of 24-bit integers in ipio2[]. -// -// Input parameters: -// x[] The input value (must be positive) is broken into nx -// pieces of 24-bit integers in double precision format. -// x[i] will be the i-th 24 bit of x. The scaled exponent -// of x[0] is given in input parameter e0 (i.e., x[0]*2^e0 -// match x's up to 24 bits. -// -// Example of breaking a double positive z into x[0]+x[1]+x[2]: -// e0 = ilogb(z)-23 -// z = scalbn(z,-e0) -// for i = 0,1,2 -// x[i] = floor(z) -// z = (z-x[i])*2**24 -// -// -// y[] ouput result in an array of double precision numbers. -// The dimension of y[] is: -// 24-bit precision 1 -// 53-bit precision 2 -// 64-bit precision 2 -// 113-bit precision 3 -// The actual value is the sum of them. Thus for 113-bit -// precison, one may have to do something like: -// -// long double t,w,r_head, r_tail; -// t = (long double)y[2] + (long double)y[1]; -// w = (long double)y[0]; -// r_head = t+w; -// r_tail = w - (r_head - t); -// -// e0 The exponent of x[0]. Must be <= 16360 or you need to -// expand the ipio2 table. -// -// nx dimension of x[] -// -// prec an integer indicating the precision: -// 0 24 bits (single) -// 1 53 bits (double) -// 2 64 bits (extended) -// 3 113 bits (quad) -// -// Here is the description of some local variables: -// -// jk jk+1 is the initial number of terms of ipio2[] needed -// in the computation. The minimum and recommended value -// for jk is 3,4,4,6 for single, double, extended, and quad. -// jk+1 must be 2 larger than you might expect so that our -// recomputation test works. (Up to 24 bits in the integer -// part (the 24 bits of it that we compute) and 23 bits in -// the fraction part may be lost to cancelation before we -// recompute.) -// -// jz local integer variable indicating the number of -// terms of ipio2[] used. -// -// jx nx - 1 -// -// jv index for pointing to the suitable ipio2[] for the -// computation. In general, we want -// ( 2^e0*x[0] * ipio2[jv-1]*2^(-24jv) )/8 -// is an integer. Thus -// e0-3-24*jv >= 0 or (e0-3)/24 >= jv -// Hence jv = max(0,(e0-3)/24). -// -// jp jp+1 is the number of terms in PIo2[] needed, jp = jk. -// -// q[] double array with integral value, representing the -// 24-bits chunk of the product of x and 2/pi. -// -// q0 the corresponding exponent of q[0]. Note that the -// exponent for q[i] would be q0-24*i. -// -// PIo2[] double precision array, obtained by cutting pi/2 -// into 24 bits chunks. -// -// f[] ipio2[] in floating point -// -// iq[] integer array by breaking up q[] in 24-bits chunk. -// -// fq[] final product of x*(2/pi) in fq[0],..,fq[jk] -// -// ih integer. If >0 it indicates q[] is >= 0.5, hence -// it also indicates the *sign* of the result. -// +/// Returns the last three digits of N with y = x - N*pi/2 so that |y| < pi/2. /// -// -// Constants: -// The hexadecimal values are the intended ones for the following -// constants. The decimal values may be used, provided that the -// compiler will convert from decimal to binary accurately enough -// to produce the hexadecimal values shown. +/// The method is to compute the integer (mod 8) and fraction parts of +/// (2/pi)*x without doing the full multiplication. In general we +/// skip the part of the product that are known to be a huge integer ( +/// more accurately, = 0 mod 8 ). Thus the number of operations are +/// independent of the exponent of the input. /// -pub fn __rem_pio2_large(x: []f64, y: []f64, e0: i32, nx: i32, prec: usize) i32 { +/// (2/pi) is represented by an array of 24-bit integers in ipio2[]. +/// +/// Input parameters: +/// x[] The input value (must be positive) is broken into nx +/// pieces of 24-bit integers in double precision format. +/// x[i] will be the i-th 24 bit of x. The scaled exponent +/// of x[0] is given in input parameter e0 (i.e., x[0]*2^e0 +/// match x's up to 24 bits. +/// +/// Example of breaking a double positive z into x[0]+x[1]+x[2]: +/// e0 = ilogb(z)-23 +/// z = scalbn(z,-e0) +/// for i = 0,1,2 +/// x[i] = floor(z) +/// z = (z-x[i])*2**24 +/// +/// +/// y[] ouput result in an array of double precision numbers. +/// The dimension of y[] is: +/// 24-bit precision 1 +/// 53-bit precision 2 +/// 64-bit precision 2 +/// 113-bit precision 3 +/// The actual value is the sum of them. Thus for 113-bit +/// precison, one may have to do something like: +/// +/// long double t,w,r_head, r_tail; +/// t = (long double)y[2] + (long double)y[1]; +/// w = (long double)y[0]; +/// r_head = t+w; +/// r_tail = w - (r_head - t); +/// +/// e0 The exponent of x[0]. Must be <= 16360 or you need to +/// expand the ipio2 table. +/// +/// nx dimension of x[] +/// +/// prec an integer indicating the precision: +/// 0 24 bits (single) +/// 1 53 bits (double) +/// 2 64 bits (extended) +/// 3 113 bits (quad) +/// +/// Here is the description of some local variables: +/// +/// jk jk+1 is the initial number of terms of ipio2[] needed +/// in the computation. The minimum and recommended value +/// for jk is 3,4,4,6 for single, double, extended, and quad. +/// jk+1 must be 2 larger than you might expect so that our +/// recomputation test works. (Up to 24 bits in the integer +/// part (the 24 bits of it that we compute) and 23 bits in +/// the fraction part may be lost to cancelation before we +/// recompute.) +/// +/// jz local integer variable indicating the number of +/// terms of ipio2[] used. +/// +/// jx nx - 1 +/// +/// jv index for pointing to the suitable ipio2[] for the +/// computation. In general, we want +/// ( 2^e0*x[0] * ipio2[jv-1]*2^(-24jv) )/8 +/// is an integer. Thus +/// e0-3-24*jv >= 0 or (e0-3)/24 >= jv +/// Hence jv = max(0,(e0-3)/24). +/// +/// jp jp+1 is the number of terms in PIo2[] needed, jp = jk. +/// +/// q[] double array with integral value, representing the +/// 24-bits chunk of the product of x and 2/pi. +/// +/// q0 the corresponding exponent of q[0]. Note that the +/// exponent for q[i] would be q0-24*i. +/// +/// PIo2[] double precision array, obtained by cutting pi/2 +/// into 24 bits chunks. +/// +/// f[] ipio2[] in floating point +/// +/// iq[] integer array by breaking up q[] in 24-bits chunk. +/// +/// fq[] final product of x*(2/pi) in fq[0],..,fq[jk] +/// +/// ih integer. If >0 it indicates q[] is >= 0.5, hence +/// it also indicates the *sign* of the result. +/// +/// +/// +/// Constants: +/// The hexadecimal values are the intended ones for the following +/// constants. The decimal values may be used, provided that the +/// compiler will convert from decimal to binary accurately enough +/// to produce the hexadecimal values shown. +/// +pub fn rem_pio2_large(x: []f64, y: []f64, e0: i32, nx: i32, prec: usize) i32 { var jz: i32 = undefined; var jx: i32 = undefined; var jv: i32 = undefined; @@ -333,7 +329,7 @@ pub fn __rem_pio2_large(x: []f64, y: []f64, e0: i32, nx: i32, prec: usize) i32 { // compute n z = math.scalbn(z, q0); // actual value of z - z -= 8.0 * math.floor(z * 0.125); // trim off integer >= 8 + z -= 8.0 * @floor(z * 0.125); // trim off integer >= 8 n = @floatToInt(i32, z); z -= @intToFloat(f64, n); ih = 0; diff --git a/lib/std/math/__rem_pio2f.zig b/lib/std/special/compiler_rt/rem_pio2f.zig similarity index 88% rename from lib/std/math/__rem_pio2f.zig rename to lib/std/special/compiler_rt/rem_pio2f.zig index 5867fb30d9..34397dd734 100644 --- a/lib/std/math/__rem_pio2f.zig +++ b/lib/std/special/compiler_rt/rem_pio2f.zig @@ -3,8 +3,8 @@ // // https://git.musl-libc.org/cgit/musl/tree/src/math/__rem_pio2f.c -const std = @import("../std.zig"); -const __rem_pio2_large = @import("__rem_pio2_large.zig").__rem_pio2_large; +const std = @import("std"); +const rem_pio2_large = @import("rem_pio2_large.zig").rem_pio2_large; const math = std.math; const toint = 1.5 / math.floatEps(f64); @@ -19,8 +19,8 @@ const pio2_1t = 1.58932547735281966916e-08; // 0x3E5110b4, 0x611A6263 // Returns the remainder of x rem pi/2 in *y // use double precision for everything except passing x -// use __rem_pio2_large() for large x -pub fn __rem_pio2f(x: f32, y: *f64) i32 { +// use rem_pio2_large() for large x +pub fn rem_pio2f(x: f32, y: *f64) i32 { var tx: [1]f64 = undefined; var ty: [1]f64 = undefined; var @"fn": f64 = undefined; @@ -60,7 +60,7 @@ pub fn __rem_pio2f(x: f32, y: *f64) i32 { e0 = (ix >> 23) - (0x7f + 23); // e0 = ilogb(|x|)-23, positive ui = ix - (e0 << 23); tx[0] = @bitCast(f32, ui); - n = __rem_pio2_large(&tx, &ty, @intCast(i32, e0), 1, 0); + n = rem_pio2_large(&tx, &ty, @intCast(i32, e0), 1, 0); if (sign) { y.* = -ty[0]; return -n; diff --git a/lib/std/special/compiler_rt/round.zig b/lib/std/special/compiler_rt/round.zig new file mode 100644 index 0000000000..99201efcf8 --- /dev/null +++ b/lib/std/special/compiler_rt/round.zig @@ -0,0 +1,169 @@ +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +// +// https://git.musl-libc.org/cgit/musl/tree/src/math/roundf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/round.c + +const std = @import("std"); +const math = std.math; +const expect = std.testing.expect; + +pub fn __roundh(x: f16) callconv(.C) f16 { + // TODO: more efficient implementation + return @floatCast(f16, roundf(x)); +} + +pub fn roundf(x_: f32) callconv(.C) f32 { + const f32_toint = 1.0 / math.floatEps(f32); + + var x = x_; + const u = @bitCast(u32, x); + const e = (u >> 23) & 0xFF; + var y: f32 = undefined; + + if (e >= 0x7F + 23) { + return x; + } + if (u >> 31 != 0) { + x = -x; + } + if (e < 0x7F - 1) { + math.doNotOptimizeAway(x + f32_toint); + return 0 * @bitCast(f32, u); + } + + y = x + f32_toint - f32_toint - x; + if (y > 0.5) { + y = y + x - 1; + } else if (y <= -0.5) { + y = y + x + 1; + } else { + y = y + x; + } + + if (u >> 31 != 0) { + return -y; + } else { + return y; + } +} + +pub fn round(x_: f64) callconv(.C) f64 { + const f64_toint = 1.0 / math.floatEps(f64); + + var x = x_; + const u = @bitCast(u64, x); + const e = (u >> 52) & 0x7FF; + var y: f64 = undefined; + + if (e >= 0x3FF + 52) { + return x; + } + if (u >> 63 != 0) { + x = -x; + } + if (e < 0x3ff - 1) { + math.doNotOptimizeAway(x + f64_toint); + return 0 * @bitCast(f64, u); + } + + y = x + f64_toint - f64_toint - x; + if (y > 0.5) { + y = y + x - 1; + } else if (y <= -0.5) { + y = y + x + 1; + } else { + y = y + x; + } + + if (u >> 63 != 0) { + return -y; + } else { + return y; + } +} + +pub fn __roundx(x: f80) callconv(.C) f80 { + // TODO: more efficient implementation + return @floatCast(f80, roundq(x)); +} + +pub fn roundq(x_: f128) callconv(.C) f128 { + const f128_toint = 1.0 / math.floatEps(f128); + + var x = x_; + const u = @bitCast(u128, x); + const e = (u >> 112) & 0x7FFF; + var y: f128 = undefined; + + if (e >= 0x3FFF + 112) { + return x; + } + if (u >> 127 != 0) { + x = -x; + } + if (e < 0x3FFF - 1) { + math.doNotOptimizeAway(x + f128_toint); + return 0 * @bitCast(f128, u); + } + + y = x + f128_toint - f128_toint - x; + if (y > 0.5) { + y = y + x - 1; + } else if (y <= -0.5) { + y = y + x + 1; + } else { + y = y + x; + } + + if (u >> 127 != 0) { + return -y; + } else { + return y; + } +} + +test "round32" { + try expect(roundf(1.3) == 1.0); + try expect(roundf(-1.3) == -1.0); + try expect(roundf(0.2) == 0.0); + try expect(roundf(1.8) == 2.0); +} + +test "round64" { + try expect(round(1.3) == 1.0); + try expect(round(-1.3) == -1.0); + try expect(round(0.2) == 0.0); + try expect(round(1.8) == 2.0); +} + +test "round128" { + try expect(roundq(1.3) == 1.0); + try expect(roundq(-1.3) == -1.0); + try expect(roundq(0.2) == 0.0); + try expect(roundq(1.8) == 2.0); +} + +test "round32.special" { + try expect(roundf(0.0) == 0.0); + try expect(roundf(-0.0) == -0.0); + try expect(math.isPositiveInf(roundf(math.inf(f32)))); + try expect(math.isNegativeInf(roundf(-math.inf(f32)))); + try expect(math.isNan(roundf(math.nan(f32)))); +} + +test "round64.special" { + try expect(round(0.0) == 0.0); + try expect(round(-0.0) == -0.0); + try expect(math.isPositiveInf(round(math.inf(f64)))); + try expect(math.isNegativeInf(round(-math.inf(f64)))); + try expect(math.isNan(round(math.nan(f64)))); +} + +test "round128.special" { + try expect(roundq(0.0) == 0.0); + try expect(roundq(-0.0) == -0.0); + try expect(math.isPositiveInf(roundq(math.inf(f128)))); + try expect(math.isNegativeInf(roundq(-math.inf(f128)))); + try expect(math.isNan(roundq(math.nan(f128)))); +} diff --git a/lib/std/math/sin.zig b/lib/std/special/compiler_rt/sin.zig similarity index 56% rename from lib/std/math/sin.zig rename to lib/std/special/compiler_rt/sin.zig index cf663b1d9e..aa77a961c7 100644 --- a/lib/std/math/sin.zig +++ b/lib/std/special/compiler_rt/sin.zig @@ -3,31 +3,21 @@ // // https://git.musl-libc.org/cgit/musl/tree/src/math/sinf.c // https://git.musl-libc.org/cgit/musl/tree/src/math/sin.c -// -const std = @import("../std.zig"); + +const std = @import("std"); const math = std.math; const expect = std.testing.expect; -const kernel = @import("__trig.zig"); -const __rem_pio2 = @import("__rem_pio2.zig").__rem_pio2; -const __rem_pio2f = @import("__rem_pio2f.zig").__rem_pio2f; +const kernel = @import("trig.zig"); +const rem_pio2 = @import("rem_pio2.zig").rem_pio2; +const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; -/// Returns the sine of the radian value x. -/// -/// Special Cases: -/// - sin(+-0) = +-0 -/// - sin(+-inf) = nan -/// - sin(nan) = nan -pub fn sin(x: anytype) @TypeOf(x) { - const T = @TypeOf(x); - return switch (T) { - f32 => sin32(x), - f64 => sin64(x), - else => @compileError("sin not implemented for " ++ @typeName(T)), - }; +pub fn __sinh(x: f16) callconv(.C) f16 { + // TODO: more efficient implementation + return @floatCast(f16, sinf(x)); } -fn sin32(x: f32) f32 { +pub fn sinf(x: f32) callconv(.C) f32 { // Small multiples of pi/2 rounded to double precision. const s1pio2: f64 = 1.0 * math.pi / 2.0; // 0x3FF921FB, 0x54442D18 const s2pio2: f64 = 2.0 * math.pi / 2.0; // 0x400921FB, 0x54442D18 @@ -73,7 +63,7 @@ fn sin32(x: f32) f32 { } var y: f64 = undefined; - const n = __rem_pio2f(x, &y); + const n = rem_pio2f(x, &y); return switch (n & 3) { 0 => kernel.__sindf(y), 1 => kernel.__cosdf(y), @@ -82,7 +72,7 @@ fn sin32(x: f32) f32 { }; } -fn sin64(x: f64) f64 { +pub fn sin(x: f64) callconv(.C) f64 { var ix = @bitCast(u64, x) >> 32; ix &= 0x7fffffff; @@ -102,7 +92,7 @@ fn sin64(x: f64) f64 { } var y: [2]f64 = undefined; - const n = __rem_pio2(x, &y); + const n = rem_pio2(x, &y); return switch (n & 3) { 0 => kernel.__sin(y[0], y[1], 1), 1 => kernel.__cos(y[0], y[1]), @@ -111,58 +101,68 @@ fn sin64(x: f64) f64 { }; } -test "math.sin" { - try expect(sin(@as(f32, 0.0)) == sin32(0.0)); - try expect(sin(@as(f64, 0.0)) == sin64(0.0)); +pub fn __sinx(x: f80) callconv(.C) f80 { + // TODO: more efficient implementation + return @floatCast(f80, sinq(x)); +} + +pub fn sinq(x: f128) callconv(.C) f128 { + // TODO: more correct implementation + return sin(@floatCast(f64, x)); +} + +test "sin" { + try expect(sin(@as(f32, 0.0)) == sinf(0.0)); + try expect(sin(@as(f64, 0.0)) == sin(0.0)); try expect(comptime (math.sin(@as(f64, 2))) == math.sin(@as(f64, 2))); } -test "math.sin32" { +test "sin32" { const epsilon = 0.00001; - try expect(math.approxEqAbs(f32, sin32(0.0), 0.0, epsilon)); - try expect(math.approxEqAbs(f32, sin32(0.2), 0.198669, epsilon)); - try expect(math.approxEqAbs(f32, sin32(0.8923), 0.778517, epsilon)); - try expect(math.approxEqAbs(f32, sin32(1.5), 0.997495, epsilon)); - try expect(math.approxEqAbs(f32, sin32(-1.5), -0.997495, epsilon)); - try expect(math.approxEqAbs(f32, sin32(37.45), -0.246544, epsilon)); - try expect(math.approxEqAbs(f32, sin32(89.123), 0.916166, epsilon)); + try expect(math.approxEqAbs(f32, sinf(0.0), 0.0, epsilon)); + try expect(math.approxEqAbs(f32, sinf(0.2), 0.198669, epsilon)); + try expect(math.approxEqAbs(f32, sinf(0.8923), 0.778517, epsilon)); + try expect(math.approxEqAbs(f32, sinf(1.5), 0.997495, epsilon)); + try expect(math.approxEqAbs(f32, sinf(-1.5), -0.997495, epsilon)); + try expect(math.approxEqAbs(f32, sinf(37.45), -0.246544, epsilon)); + try expect(math.approxEqAbs(f32, sinf(89.123), 0.916166, epsilon)); } -test "math.sin64" { +test "sin64" { const epsilon = 0.000001; - try expect(math.approxEqAbs(f64, sin64(0.0), 0.0, epsilon)); - try expect(math.approxEqAbs(f64, sin64(0.2), 0.198669, epsilon)); - try expect(math.approxEqAbs(f64, sin64(0.8923), 0.778517, epsilon)); - try expect(math.approxEqAbs(f64, sin64(1.5), 0.997495, epsilon)); - try expect(math.approxEqAbs(f64, sin64(-1.5), -0.997495, epsilon)); - try expect(math.approxEqAbs(f64, sin64(37.45), -0.246543, epsilon)); - try expect(math.approxEqAbs(f64, sin64(89.123), 0.916166, epsilon)); + try expect(math.approxEqAbs(f64, sin(0.0), 0.0, epsilon)); + try expect(math.approxEqAbs(f64, sin(0.2), 0.198669, epsilon)); + try expect(math.approxEqAbs(f64, sin(0.8923), 0.778517, epsilon)); + try expect(math.approxEqAbs(f64, sin(1.5), 0.997495, epsilon)); + try expect(math.approxEqAbs(f64, sin(-1.5), -0.997495, epsilon)); + try expect(math.approxEqAbs(f64, sin(37.45), -0.246543, epsilon)); + try expect(math.approxEqAbs(f64, sin(89.123), 0.916166, epsilon)); } -test "math.sin32.special" { - try expect(sin32(0.0) == 0.0); - try expect(sin32(-0.0) == -0.0); - try expect(math.isNan(sin32(math.inf(f32)))); - try expect(math.isNan(sin32(-math.inf(f32)))); - try expect(math.isNan(sin32(math.nan(f32)))); +test "sin32.special" { + try expect(sinf(0.0) == 0.0); + try expect(sinf(-0.0) == -0.0); + try expect(math.isNan(sinf(math.inf(f32)))); + try expect(math.isNan(sinf(-math.inf(f32)))); + try expect(math.isNan(sinf(math.nan(f32)))); } -test "math.sin64.special" { - try expect(sin64(0.0) == 0.0); - try expect(sin64(-0.0) == -0.0); - try expect(math.isNan(sin64(math.inf(f64)))); - try expect(math.isNan(sin64(-math.inf(f64)))); - try expect(math.isNan(sin64(math.nan(f64)))); +test "sin64.special" { + try expect(sin(0.0) == 0.0); + try expect(sin(-0.0) == -0.0); + try expect(math.isNan(sin(math.inf(f64)))); + try expect(math.isNan(sin(-math.inf(f64)))); + try expect(math.isNan(sin(math.nan(f64)))); } -test "math.sin32 #9901" { +test "sin32 #9901" { const float = @bitCast(f32, @as(u32, 0b11100011111111110000000000000000)); - _ = std.math.sin(float); + _ = sinf(float); } -test "math.sin64 #9901" { +test "sin64 #9901" { const float = @bitCast(f64, @as(u64, 0b1111111101000001000000001111110111111111100000000000000000000001)); - _ = std.math.sin(float); + _ = sin(float); } diff --git a/lib/std/special/compiler_rt/sincos.zig b/lib/std/special/compiler_rt/sincos.zig new file mode 100644 index 0000000000..fae326f182 --- /dev/null +++ b/lib/std/special/compiler_rt/sincos.zig @@ -0,0 +1,24 @@ +pub fn __sincosh(a: f16, r_sin: *f16, r_cos: *f16) callconv(.C) void { + r_sin.* = @sin(a); + r_cos.* = @cos(a); +} + +pub fn sincosf(a: f32, r_sin: *f32, r_cos: *f32) callconv(.C) void { + r_sin.* = @sin(a); + r_cos.* = @cos(a); +} + +pub fn sincos(a: f64, r_sin: *f64, r_cos: *f64) callconv(.C) void { + r_sin.* = @sin(a); + r_cos.* = @cos(a); +} + +pub fn __sincosx(a: f80, r_sin: *f80, r_cos: *f80) callconv(.C) void { + r_sin.* = @sin(a); + r_cos.* = @cos(a); +} + +pub fn sincosq(a: f128, r_sin: *f128, r_cos: *f128) callconv(.C) void { + r_sin.* = @sin(a); + r_cos.* = @cos(a); +} diff --git a/lib/std/special/compiler_rt/sqrt.zig b/lib/std/special/compiler_rt/sqrt.zig new file mode 100644 index 0000000000..ba07beb86e --- /dev/null +++ b/lib/std/special/compiler_rt/sqrt.zig @@ -0,0 +1,284 @@ +const std = @import("std"); +const math = std.math; + +pub fn __sqrth(x: f16) callconv(.C) f16 { + // TODO: more efficient implementation + return @floatCast(f16, sqrtf(x)); +} + +pub fn sqrtf(x: f32) callconv(.C) f32 { + const tiny: f32 = 1.0e-30; + const sign: i32 = @bitCast(i32, @as(u32, 0x80000000)); + var ix: i32 = @bitCast(i32, x); + + if ((ix & 0x7F800000) == 0x7F800000) { + return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan + } + + // zero + if (ix <= 0) { + if (ix & ~sign == 0) { + return x; // sqrt (+-0) = +-0 + } + if (ix < 0) { + return math.snan(f32); + } + } + + // normalize + var m = ix >> 23; + if (m == 0) { + // subnormal + var i: i32 = 0; + while (ix & 0x00800000 == 0) : (i += 1) { + ix <<= 1; + } + m -= i - 1; + } + + m -= 127; // unbias exponent + ix = (ix & 0x007FFFFF) | 0x00800000; + + if (m & 1 != 0) { // odd m, double x to even + ix += ix; + } + + m >>= 1; // m = [m / 2] + + // sqrt(x) bit by bit + ix += ix; + var q: i32 = 0; // q = sqrt(x) + var s: i32 = 0; + var r: i32 = 0x01000000; // r = moving bit right -> left + + while (r != 0) { + const t = s + r; + if (t <= ix) { + s = t + r; + ix -= t; + q += r; + } + ix += ix; + r >>= 1; + } + + // floating add to find rounding direction + if (ix != 0) { + var z = 1.0 - tiny; // inexact + if (z >= 1.0) { + z = 1.0 + tiny; + if (z > 1.0) { + q += 2; + } else { + if (q & 1 != 0) { + q += 1; + } + } + } + } + + ix = (q >> 1) + 0x3f000000; + ix += m << 23; + return @bitCast(f32, ix); +} + +/// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound +/// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are +/// potentially some edge cases remaining that are not handled in the same way. +pub fn sqrt(x: f64) callconv(.C) f64 { + const tiny: f64 = 1.0e-300; + const sign: u32 = 0x80000000; + const u = @bitCast(u64, x); + + var ix0 = @intCast(u32, u >> 32); + var ix1 = @intCast(u32, u & 0xFFFFFFFF); + + // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan + if (ix0 & 0x7FF00000 == 0x7FF00000) { + return x * x + x; + } + + // sqrt(+-0) = +-0 + if (x == 0.0) { + return x; + } + // sqrt(-ve) = snan + if (ix0 & sign != 0) { + return math.snan(f64); + } + + // normalize x + var m = @intCast(i32, ix0 >> 20); + if (m == 0) { + // subnormal + while (ix0 == 0) { + m -= 21; + ix0 |= ix1 >> 11; + ix1 <<= 21; + } + + // subnormal + var i: u32 = 0; + while (ix0 & 0x00100000 == 0) : (i += 1) { + ix0 <<= 1; + } + m -= @intCast(i32, i) - 1; + ix0 |= ix1 >> @intCast(u5, 32 - i); + ix1 <<= @intCast(u5, i); + } + + // unbias exponent + m -= 1023; + ix0 = (ix0 & 0x000FFFFF) | 0x00100000; + if (m & 1 != 0) { + ix0 += ix0 + (ix1 >> 31); + ix1 = ix1 +% ix1; + } + m >>= 1; + + // sqrt(x) bit by bit + ix0 += ix0 + (ix1 >> 31); + ix1 = ix1 +% ix1; + + var q: u32 = 0; + var q1: u32 = 0; + var s0: u32 = 0; + var s1: u32 = 0; + var r: u32 = 0x00200000; + var t: u32 = undefined; + var t1: u32 = undefined; + + while (r != 0) { + t = s0 +% r; + if (t <= ix0) { + s0 = t + r; + ix0 -= t; + q += r; + } + ix0 = ix0 +% ix0 +% (ix1 >> 31); + ix1 = ix1 +% ix1; + r >>= 1; + } + + r = sign; + while (r != 0) { + t1 = s1 +% r; + t = s0; + if (t < ix0 or (t == ix0 and t1 <= ix1)) { + s1 = t1 +% r; + if (t1 & sign == sign and s1 & sign == 0) { + s0 += 1; + } + ix0 -= t; + if (ix1 < t1) { + ix0 -= 1; + } + ix1 = ix1 -% t1; + q1 += r; + } + ix0 = ix0 +% ix0 +% (ix1 >> 31); + ix1 = ix1 +% ix1; + r >>= 1; + } + + // rounding direction + if (ix0 | ix1 != 0) { + var z = 1.0 - tiny; // raise inexact + if (z >= 1.0) { + z = 1.0 + tiny; + if (q1 == 0xFFFFFFFF) { + q1 = 0; + q += 1; + } else if (z > 1.0) { + if (q1 == 0xFFFFFFFE) { + q += 1; + } + q1 += 2; + } else { + q1 += q1 & 1; + } + } + } + + ix0 = (q >> 1) + 0x3FE00000; + ix1 = q1 >> 1; + if (q & 1 != 0) { + ix1 |= 0x80000000; + } + + // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same + // behaviour at least. + var iix0 = @intCast(i32, ix0); + iix0 = iix0 +% (m << 20); + + const uz = (@intCast(u64, iix0) << 32) | ix1; + return @bitCast(f64, uz); +} + +pub fn __sqrtx(x: f80) callconv(.C) f80 { + // TODO: more efficient implementation + return @floatCast(f80, sqrtq(x)); +} + +pub fn sqrtq(x: f128) callconv(.C) f128 { + // TODO: more correct implementation + return sqrt(@floatCast(f64, x)); +} + +test "sqrtf" { + const V = [_]f32{ + 0.0, + 4.089288054930154, + 7.538757127071935, + 8.97780793672623, + 5.304443821913729, + 5.682408965311888, + 0.5846878579110049, + 3.650338664297043, + 0.3178091951800732, + 7.1505232436382835, + 3.6589165881946464, + }; + + // Note that @sqrt will either generate the sqrt opcode (if supported by the + // target ISA) or a call to `sqrtf` otherwise. + for (V) |val| + try std.testing.expectEqual(@sqrt(val), sqrtf(val)); +} + +test "sqrtf special" { + try std.testing.expect(math.isPositiveInf(sqrtf(math.inf(f32)))); + try std.testing.expect(sqrtf(0.0) == 0.0); + try std.testing.expect(sqrtf(-0.0) == -0.0); + try std.testing.expect(math.isNan(sqrtf(-1.0))); + try std.testing.expect(math.isNan(sqrtf(math.nan(f32)))); +} + +test "sqrt" { + const V = [_]f64{ + 0.0, + 4.089288054930154, + 7.538757127071935, + 8.97780793672623, + 5.304443821913729, + 5.682408965311888, + 0.5846878579110049, + 3.650338664297043, + 0.3178091951800732, + 7.1505232436382835, + 3.6589165881946464, + }; + + // Note that @sqrt will either generate the sqrt opcode (if supported by the + // target ISA) or a call to `sqrtf` otherwise. + for (V) |val| + try std.testing.expectEqual(@sqrt(val), sqrt(val)); +} + +test "sqrt special" { + try std.testing.expect(math.isPositiveInf(sqrt(math.inf(f64)))); + try std.testing.expect(sqrt(0.0) == 0.0); + try std.testing.expect(sqrt(-0.0) == -0.0); + try std.testing.expect(math.isNan(sqrt(-1.0))); + try std.testing.expect(math.isNan(sqrt(math.nan(f64)))); +} diff --git a/lib/std/math/tan.zig b/lib/std/special/compiler_rt/tan.zig similarity index 53% rename from lib/std/math/tan.zig rename to lib/std/special/compiler_rt/tan.zig index fd5950df7c..d99f00b99e 100644 --- a/lib/std/math/tan.zig +++ b/lib/std/special/compiler_rt/tan.zig @@ -5,30 +5,20 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/tan.c // https://golang.org/src/math/tan.go -const std = @import("../std.zig"); +const std = @import("std"); const math = std.math; const expect = std.testing.expect; -const kernel = @import("__trig.zig"); -const __rem_pio2 = @import("__rem_pio2.zig").__rem_pio2; -const __rem_pio2f = @import("__rem_pio2f.zig").__rem_pio2f; +const kernel = @import("trig.zig"); +const rem_pio2 = @import("rem_pio2.zig").rem_pio2; +const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; -/// Returns the tangent of the radian value x. -/// -/// Special Cases: -/// - tan(+-0) = +-0 -/// - tan(+-inf) = nan -/// - tan(nan) = nan -pub fn tan(x: anytype) @TypeOf(x) { - const T = @TypeOf(x); - return switch (T) { - f32 => tan32(x), - f64 => tan64(x), - else => @compileError("tan not implemented for " ++ @typeName(T)), - }; +pub fn __tanh(x: f16) callconv(.C) f16 { + // TODO: more efficient implementation + return @floatCast(f16, tanf(x)); } -fn tan32(x: f32) f32 { +pub fn tanf(x: f32) callconv(.C) f32 { // Small multiples of pi/2 rounded to double precision. const t1pio2: f64 = 1.0 * math.pi / 2.0; // 0x3FF921FB, 0x54442D18 const t2pio2: f64 = 2.0 * math.pi / 2.0; // 0x400921FB, 0x54442D18 @@ -68,11 +58,11 @@ fn tan32(x: f32) f32 { } var y: f64 = undefined; - const n = __rem_pio2f(x, &y); + const n = rem_pio2f(x, &y); return kernel.__tandf(y, n & 1 != 0); } -fn tan64(x: f64) f64 { +pub fn tan(x: f64) callconv(.C) f64 { var ix = @bitCast(u64, x) >> 32; ix &= 0x7fffffff; @@ -92,49 +82,59 @@ fn tan64(x: f64) f64 { } var y: [2]f64 = undefined; - const n = __rem_pio2(x, &y); + const n = rem_pio2(x, &y); return kernel.__tan(y[0], y[1], n & 1 != 0); } -test "math.tan" { - try expect(tan(@as(f32, 0.0)) == tan32(0.0)); - try expect(tan(@as(f64, 0.0)) == tan64(0.0)); +pub fn __tanx(x: f80) callconv(.C) f80 { + // TODO: more efficient implementation + return @floatCast(f80, tanq(x)); } -test "math.tan32" { +pub fn tanq(x: f128) callconv(.C) f128 { + // TODO: more correct implementation + return tan(@floatCast(f64, x)); +} + +test "tan" { + try expect(tan(@as(f32, 0.0)) == tanf(0.0)); + try expect(tan(@as(f64, 0.0)) == tan(0.0)); +} + +test "tan32" { const epsilon = 0.00001; - try expect(math.approxEqAbs(f32, tan32(0.0), 0.0, epsilon)); - try expect(math.approxEqAbs(f32, tan32(0.2), 0.202710, epsilon)); - try expect(math.approxEqAbs(f32, tan32(0.8923), 1.240422, epsilon)); - try expect(math.approxEqAbs(f32, tan32(1.5), 14.101420, epsilon)); - try expect(math.approxEqAbs(f32, tan32(37.45), -0.254397, epsilon)); - try expect(math.approxEqAbs(f32, tan32(89.123), 2.285852, epsilon)); + try expect(math.approxEqAbs(f32, tanf(0.0), 0.0, epsilon)); + try expect(math.approxEqAbs(f32, tanf(0.2), 0.202710, epsilon)); + try expect(math.approxEqAbs(f32, tanf(0.8923), 1.240422, epsilon)); + try expect(math.approxEqAbs(f32, tanf(1.5), 14.101420, epsilon)); + try expect(math.approxEqAbs(f32, tanf(37.45), -0.254397, epsilon)); + try expect(math.approxEqAbs(f32, tanf(89.123), 2.285852, epsilon)); } -test "math.tan64" { +test "tan64" { const epsilon = 0.000001; - try expect(math.approxEqAbs(f64, tan64(0.0), 0.0, epsilon)); - try expect(math.approxEqAbs(f64, tan64(0.2), 0.202710, epsilon)); - try expect(math.approxEqAbs(f64, tan64(0.8923), 1.240422, epsilon)); - try expect(math.approxEqAbs(f64, tan64(1.5), 14.101420, epsilon)); - try expect(math.approxEqAbs(f64, tan64(37.45), -0.254397, epsilon)); - try expect(math.approxEqAbs(f64, tan64(89.123), 2.2858376, epsilon)); + try expect(math.approxEqAbs(f64, tan(0.0), 0.0, epsilon)); + try expect(math.approxEqAbs(f64, tan(0.2), 0.202710, epsilon)); + try expect(math.approxEqAbs(f64, tan(0.8923), 1.240422, epsilon)); + try expect(math.approxEqAbs(f64, tan(1.5), 14.101420, epsilon)); + try expect(math.approxEqAbs(f64, tan(37.45), -0.254397, epsilon)); + try expect(math.approxEqAbs(f64, tan(89.123), 2.2858376, epsilon)); } -test "math.tan32.special" { - try expect(tan32(0.0) == 0.0); - try expect(tan32(-0.0) == -0.0); - try expect(math.isNan(tan32(math.inf(f32)))); - try expect(math.isNan(tan32(-math.inf(f32)))); - try expect(math.isNan(tan32(math.nan(f32)))); +test "tan32.special" { + try expect(tanf(0.0) == 0.0); + try expect(tanf(-0.0) == -0.0); + try expect(math.isNan(tanf(math.inf(f32)))); + try expect(math.isNan(tanf(-math.inf(f32)))); + try expect(math.isNan(tanf(math.nan(f32)))); } -test "math.tan64.special" { - try expect(tan64(0.0) == 0.0); - try expect(tan64(-0.0) == -0.0); - try expect(math.isNan(tan64(math.inf(f64)))); - try expect(math.isNan(tan64(-math.inf(f64)))); - try expect(math.isNan(tan64(math.nan(f64)))); +test "tan64.special" { + try expect(tan(0.0) == 0.0); + try expect(tan(-0.0) == -0.0); + try expect(math.isNan(tan(math.inf(f64)))); + try expect(math.isNan(tan(-math.inf(f64)))); + try expect(math.isNan(tan(math.nan(f64)))); } diff --git a/lib/std/math/__trig.zig b/lib/std/special/compiler_rt/trig.zig similarity index 60% rename from lib/std/math/__trig.zig rename to lib/std/special/compiler_rt/trig.zig index 0c08ed58bd..8ece83515e 100644 --- a/lib/std/math/__trig.zig +++ b/lib/std/special/compiler_rt/trig.zig @@ -8,41 +8,41 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/__tand.c // https://git.musl-libc.org/cgit/musl/tree/src/math/__tandf.c -// kernel cos function on [-pi/4, pi/4], pi/4 ~ 0.785398164 -// Input x is assumed to be bounded by ~pi/4 in magnitude. -// Input y is the tail of x. -// -// Algorithm -// 1. Since cos(-x) = cos(x), we need only to consider positive x. -// 2. if x < 2^-27 (hx<0x3e400000 0), return 1 with inexact if x!=0. -// 3. cos(x) is approximated by a polynomial of degree 14 on -// [0,pi/4] -// 4 14 -// cos(x) ~ 1 - x*x/2 + C1*x + ... + C6*x -// where the remez error is -// -// | 2 4 6 8 10 12 14 | -58 -// |cos(x)-(1-.5*x +C1*x +C2*x +C3*x +C4*x +C5*x +C6*x )| <= 2 -// | | -// -// 4 6 8 10 12 14 -// 4. let r = C1*x +C2*x +C3*x +C4*x +C5*x +C6*x , then -// cos(x) ~ 1 - x*x/2 + r -// since cos(x+y) ~ cos(x) - sin(x)*y -// ~ cos(x) - x*y, -// a correction term is necessary in cos(x) and hence -// cos(x+y) = 1 - (x*x/2 - (r - x*y)) -// For better accuracy, rearrange to -// cos(x+y) ~ w + (tmp + (r-x*y)) -// where w = 1 - x*x/2 and tmp is a tiny correction term -// (1 - x*x/2 == w + tmp exactly in infinite precision). -// The exactness of w + tmp in infinite precision depends on w -// and tmp having the same precision as x. If they have extra -// precision due to compiler bugs, then the extra precision is -// only good provided it is retained in all terms of the final -// expression for cos(). Retention happens in all cases tested -// under FreeBSD, so don't pessimize things by forcibly clipping -// any extra precision in w. +/// kernel cos function on [-pi/4, pi/4], pi/4 ~ 0.785398164 +/// Input x is assumed to be bounded by ~pi/4 in magnitude. +/// Input y is the tail of x. +/// +/// Algorithm +/// 1. Since cos(-x) = cos(x), we need only to consider positive x. +/// 2. if x < 2^-27 (hx<0x3e400000 0), return 1 with inexact if x!=0. +/// 3. cos(x) is approximated by a polynomial of degree 14 on +/// [0,pi/4] +/// 4 14 +/// cos(x) ~ 1 - x*x/2 + C1*x + ... + C6*x +/// where the remez error is +/// +/// | 2 4 6 8 10 12 14 | -58 +/// |cos(x)-(1-.5*x +C1*x +C2*x +C3*x +C4*x +C5*x +C6*x )| <= 2 +/// | | +/// +/// 4 6 8 10 12 14 +/// 4. let r = C1*x +C2*x +C3*x +C4*x +C5*x +C6*x , then +/// cos(x) ~ 1 - x*x/2 + r +/// since cos(x+y) ~ cos(x) - sin(x)*y +/// ~ cos(x) - x*y, +/// a correction term is necessary in cos(x) and hence +/// cos(x+y) = 1 - (x*x/2 - (r - x*y)) +/// For better accuracy, rearrange to +/// cos(x+y) ~ w + (tmp + (r-x*y)) +/// where w = 1 - x*x/2 and tmp is a tiny correction term +/// (1 - x*x/2 == w + tmp exactly in infinite precision). +/// The exactness of w + tmp in infinite precision depends on w +/// and tmp having the same precision as x. If they have extra +/// precision due to compiler bugs, then the extra precision is +/// only good provided it is retained in all terms of the final +/// expression for cos(). Retention happens in all cases tested +/// under FreeBSD, so don't pessimize things by forcibly clipping +/// any extra precision in w. pub fn __cos(x: f64, y: f64) f64 { const C1 = 4.16666666666666019037e-02; // 0x3FA55555, 0x5555554C const C2 = -1.38888888888741095749e-03; // 0xBF56C16C, 0x16C15177 @@ -73,33 +73,33 @@ pub fn __cosdf(x: f64) f32 { return @floatCast(f32, ((1.0 + z * C0) + w * C1) + (w * z) * r); } -// kernel sin function on ~[-pi/4, pi/4] (except on -0), pi/4 ~ 0.7854 -// Input x is assumed to be bounded by ~pi/4 in magnitude. -// Input y is the tail of x. -// Input iy indicates whether y is 0. (if iy=0, y assume to be 0). -// -// Algorithm -// 1. Since sin(-x) = -sin(x), we need only to consider positive x. -// 2. Callers must return sin(-0) = -0 without calling here since our -// odd polynomial is not evaluated in a way that preserves -0. -// Callers may do the optimization sin(x) ~ x for tiny x. -// 3. sin(x) is approximated by a polynomial of degree 13 on -// [0,pi/4] -// 3 13 -// sin(x) ~ x + S1*x + ... + S6*x -// where -// -// |sin(x) 2 4 6 8 10 12 | -58 -// |----- - (1+S1*x +S2*x +S3*x +S4*x +S5*x +S6*x )| <= 2 -// | x | -// -// 4. sin(x+y) = sin(x) + sin'(x')*y -// ~ sin(x) + (1-x*x/2)*y -// For better accuracy, let -// 3 2 2 2 2 -// r = x *(S2+x *(S3+x *(S4+x *(S5+x *S6)))) -// then 3 2 -// sin(x) = x + (S1*x + (x *(r-y/2)+y)) +/// kernel sin function on ~[-pi/4, pi/4] (except on -0), pi/4 ~ 0.7854 +/// Input x is assumed to be bounded by ~pi/4 in magnitude. +/// Input y is the tail of x. +/// Input iy indicates whether y is 0. (if iy=0, y assume to be 0). +/// +/// Algorithm +/// 1. Since sin(-x) = -sin(x), we need only to consider positive x. +/// 2. Callers must return sin(-0) = -0 without calling here since our +/// odd polynomial is not evaluated in a way that preserves -0. +/// Callers may do the optimization sin(x) ~ x for tiny x. +/// 3. sin(x) is approximated by a polynomial of degree 13 on +/// [0,pi/4] +/// 3 13 +/// sin(x) ~ x + S1*x + ... + S6*x +/// where +/// +/// |sin(x) 2 4 6 8 10 12 | -58 +/// |----- - (1+S1*x +S2*x +S3*x +S4*x +S5*x +S6*x )| <= 2 +/// | x | +/// +/// 4. sin(x+y) = sin(x) + sin'(x')*y +/// ~ sin(x) + (1-x*x/2)*y +/// For better accuracy, let +/// 3 2 2 2 2 +/// r = x *(S2+x *(S3+x *(S4+x *(S5+x *S6)))) +/// then 3 2 +/// sin(x) = x + (S1*x + (x *(r-y/2)+y)) pub fn __sin(x: f64, y: f64, iy: i32) f64 { const S1 = -1.66666666666666324348e-01; // 0xBFC55555, 0x55555549 const S2 = 8.33333333332248946124e-03; // 0x3F811111, 0x1110F8A6 @@ -134,38 +134,38 @@ pub fn __sindf(x: f64) f32 { return @floatCast(f32, (x + s * (S1 + z * S2)) + s * w * r); } -// kernel tan function on ~[-pi/4, pi/4] (except on -0), pi/4 ~ 0.7854 -// Input x is assumed to be bounded by ~pi/4 in magnitude. -// Input y is the tail of x. -// Input odd indicates whether tan (if odd = 0) or -1/tan (if odd = 1) is returned. -// -// Algorithm -// 1. Since tan(-x) = -tan(x), we need only to consider positive x. -// 2. Callers must return tan(-0) = -0 without calling here since our -// odd polynomial is not evaluated in a way that preserves -0. -// Callers may do the optimization tan(x) ~ x for tiny x. -// 3. tan(x) is approximated by a odd polynomial of degree 27 on -// [0,0.67434] -// 3 27 -// tan(x) ~ x + T1*x + ... + T13*x -// where -// -// |tan(x) 2 4 26 | -59.2 -// |----- - (1+T1*x +T2*x +.... +T13*x )| <= 2 -// | x | -// -// Note: tan(x+y) = tan(x) + tan'(x)*y -// ~ tan(x) + (1+x*x)*y -// Therefore, for better accuracy in computing tan(x+y), let -// 3 2 2 2 2 -// r = x *(T2+x *(T3+x *(...+x *(T12+x *T13)))) -// then -// 3 2 -// tan(x+y) = x + (T1*x + (x *(r+y)+y)) -// -// 4. For x in [0.67434,pi/4], let y = pi/4 - x, then -// tan(x) = tan(pi/4-y) = (1-tan(y))/(1+tan(y)) -// = 1 - 2*(tan(y) - (tan(y)^2)/(1+tan(y))) +/// kernel tan function on ~[-pi/4, pi/4] (except on -0), pi/4 ~ 0.7854 +/// Input x is assumed to be bounded by ~pi/4 in magnitude. +/// Input y is the tail of x. +/// Input odd indicates whether tan (if odd = 0) or -1/tan (if odd = 1) is returned. +/// +/// Algorithm +/// 1. Since tan(-x) = -tan(x), we need only to consider positive x. +/// 2. Callers must return tan(-0) = -0 without calling here since our +/// odd polynomial is not evaluated in a way that preserves -0. +/// Callers may do the optimization tan(x) ~ x for tiny x. +/// 3. tan(x) is approximated by a odd polynomial of degree 27 on +/// [0,0.67434] +/// 3 27 +/// tan(x) ~ x + T1*x + ... + T13*x +/// where +/// +/// |tan(x) 2 4 26 | -59.2 +/// |----- - (1+T1*x +T2*x +.... +T13*x )| <= 2 +/// | x | +/// +/// Note: tan(x+y) = tan(x) + tan'(x)*y +/// ~ tan(x) + (1+x*x)*y +/// Therefore, for better accuracy in computing tan(x+y), let +/// 3 2 2 2 2 +/// r = x *(T2+x *(T3+x *(...+x *(T12+x *T13)))) +/// then +/// 3 2 +/// tan(x+y) = x + (T1*x + (x *(r+y)+y)) +/// +/// 4. For x in [0.67434,pi/4], let y = pi/4 - x, then +/// tan(x) = tan(pi/4-y) = (1-tan(y))/(1+tan(y)) +/// = 1 - 2*(tan(y) - (tan(y)^2)/(1+tan(y))) pub fn __tan(x_: f64, y_: f64, odd: bool) f64 { var x = x_; var y = y_; diff --git a/lib/std/special/compiler_rt/trunc.zig b/lib/std/special/compiler_rt/trunc.zig new file mode 100644 index 0000000000..5406f9a02d --- /dev/null +++ b/lib/std/special/compiler_rt/trunc.zig @@ -0,0 +1,124 @@ +// Ported from musl, which is licensed under the MIT license: +// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +// +// https://git.musl-libc.org/cgit/musl/tree/src/math/truncf.c +// https://git.musl-libc.org/cgit/musl/tree/src/math/trunc.c + +const std = @import("std"); +const math = std.math; +const expect = std.testing.expect; + +pub fn __trunch(x: f16) callconv(.C) f16 { + // TODO: more efficient implementation + return @floatCast(f16, truncf(x)); +} + +pub fn truncf(x: f32) callconv(.C) f32 { + const u = @bitCast(u32, x); + var e = @intCast(i32, ((u >> 23) & 0xFF)) - 0x7F + 9; + var m: u32 = undefined; + + if (e >= 23 + 9) { + return x; + } + if (e < 9) { + e = 1; + } + + m = @as(u32, math.maxInt(u32)) >> @intCast(u5, e); + if (u & m == 0) { + return x; + } else { + math.doNotOptimizeAway(x + 0x1p120); + return @bitCast(f32, u & ~m); + } +} + +pub fn trunc(x: f64) callconv(.C) f64 { + const u = @bitCast(u64, x); + var e = @intCast(i32, ((u >> 52) & 0x7FF)) - 0x3FF + 12; + var m: u64 = undefined; + + if (e >= 52 + 12) { + return x; + } + if (e < 12) { + e = 1; + } + + m = @as(u64, math.maxInt(u64)) >> @intCast(u6, e); + if (u & m == 0) { + return x; + } else { + math.doNotOptimizeAway(x + 0x1p120); + return @bitCast(f64, u & ~m); + } +} + +pub fn __truncx(x: f80) callconv(.C) f80 { + // TODO: more efficient implementation + return @floatCast(f80, truncq(x)); +} + +pub fn truncq(x: f128) callconv(.C) f128 { + const u = @bitCast(u128, x); + var e = @intCast(i32, ((u >> 112) & 0x7FFF)) - 0x3FFF + 16; + var m: u128 = undefined; + + if (e >= 112 + 16) { + return x; + } + if (e < 16) { + e = 1; + } + + m = @as(u128, math.maxInt(u128)) >> @intCast(u7, e); + if (u & m == 0) { + return x; + } else { + math.doNotOptimizeAway(x + 0x1p120); + return @bitCast(f128, u & ~m); + } +} + +test "trunc32" { + try expect(truncf(1.3) == 1.0); + try expect(truncf(-1.3) == -1.0); + try expect(truncf(0.2) == 0.0); +} + +test "trunc64" { + try expect(trunc(1.3) == 1.0); + try expect(trunc(-1.3) == -1.0); + try expect(trunc(0.2) == 0.0); +} + +test "trunc128" { + try expect(truncq(1.3) == 1.0); + try expect(truncq(-1.3) == -1.0); + try expect(truncq(0.2) == 0.0); +} + +test "trunc32.special" { + try expect(truncf(0.0) == 0.0); // 0x3F800000 + try expect(truncf(-0.0) == -0.0); + try expect(math.isPositiveInf(truncf(math.inf(f32)))); + try expect(math.isNegativeInf(truncf(-math.inf(f32)))); + try expect(math.isNan(truncf(math.nan(f32)))); +} + +test "trunc64.special" { + try expect(trunc(0.0) == 0.0); + try expect(trunc(-0.0) == -0.0); + try expect(math.isPositiveInf(trunc(math.inf(f64)))); + try expect(math.isNegativeInf(trunc(-math.inf(f64)))); + try expect(math.isNan(trunc(math.nan(f64)))); +} + +test "trunc128.special" { + try expect(truncq(0.0) == 0.0); + try expect(truncq(-0.0) == -0.0); + try expect(math.isPositiveInf(truncq(math.inf(f128)))); + try expect(math.isNegativeInf(truncq(-math.inf(f128)))); + try expect(math.isNan(truncq(math.nan(f128)))); +} diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 004e2d0fa7..cfdf300c04 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -265,7 +265,7 @@ pub fn expectApproxEqRel(expected: anytype, actual: @TypeOf(expected), tolerance test "expectApproxEqRel" { inline for ([_]type{ f16, f32, f64, f128 }) |T| { const eps_value = comptime math.epsilon(T); - const sqrt_eps_value = comptime math.sqrt(eps_value); + const sqrt_eps_value = comptime @sqrt(eps_value); const pos_x: T = 12.0; const pos_y: T = pos_x + 2 * eps_value; diff --git a/src/Sema.zig b/src/Sema.zig index 8abcbd47ed..5d1d51b58f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14051,7 +14051,7 @@ fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const result_val = val.floatToInt(sema.arena, operand_ty, dest_ty, target) catch |err| switch (err) { error.FloatCannotFit => { return sema.fail(block, operand_src, "integer value {d} cannot be stored in type '{}'", .{ - std.math.floor(val.toFloat(f64)), + @floor(val.toFloat(f64)), dest_ty.fmt(sema.mod), }); }, @@ -18371,7 +18371,7 @@ fn coerce( } const result_val = val.floatToInt(sema.arena, inst_ty, dest_ty, target) catch |err| switch (err) { error.FloatCannotFit => { - return sema.fail(block, inst_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty.fmt(sema.mod) }); + return sema.fail(block, inst_src, "integer value {d} cannot be stored in type '{}'", .{ @floor(val.toFloat(f64)), dest_ty.fmt(sema.mod) }); }, else => |e| return e, }; diff --git a/src/translate_c.zig b/src/translate_c.zig index e09ebea4d7..0139ec8ec3 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -3998,7 +3998,7 @@ fn transFloatingLiteral(c: *Context, scope: *Scope, expr: *const clang.FloatingL var dbl = expr.getValueAsApproximateDouble(); const is_negative = dbl < 0; if (is_negative) dbl = -dbl; - const str = if (dbl == std.math.floor(dbl)) + const str = if (dbl == @floor(dbl)) try std.fmt.allocPrint(c.arena, "{d}.0", .{dbl}) else try std.fmt.allocPrint(c.arena, "{d}", .{dbl}); diff --git a/src/value.zig b/src/value.zig index bb7b742290..e951b075c0 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1155,6 +1155,7 @@ pub const Value = extern union { 16 => return floatWriteToMemory(f16, val.toFloat(f16), target, buffer), 32 => return floatWriteToMemory(f32, val.toFloat(f32), target, buffer), 64 => return floatWriteToMemory(f64, val.toFloat(f64), target, buffer), + 80 => return floatWriteToMemory(f80, val.toFloat(f80), target, buffer), 128 => return floatWriteToMemory(f128, val.toFloat(f128), target, buffer), else => unreachable, }, @@ -1379,25 +1380,21 @@ pub const Value = extern union { } fn floatWriteToMemory(comptime F: type, f: F, target: Target, buffer: []u8) void { + const endian = target.cpu.arch.endian(); if (F == f80) { - switch (target.cpu.arch) { - .i386, .x86_64 => { - const repr = std.math.break_f80(f); - std.mem.writeIntLittle(u64, buffer[0..8], repr.fraction); - std.mem.writeIntLittle(u16, buffer[8..10], repr.exp); - // TODO set the rest of the bytes to undefined. should we use 0xaa - // or is there a different way? - return; - }, - else => {}, - } + const repr = std.math.break_f80(f); + std.mem.writeInt(u64, buffer[0..8], repr.fraction, endian); + std.mem.writeInt(u16, buffer[8..10], repr.exp, endian); + // TODO set the rest of the bytes to undefined. should we use 0xaa + // or is there a different way? + return; } const Int = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = @typeInfo(F).Float.bits, } }); const int = @bitCast(Int, f); - std.mem.writeInt(Int, buffer[0..@sizeOf(Int)], int, target.cpu.arch.endian()); + std.mem.writeInt(Int, buffer[0..@sizeOf(Int)], int, endian); } fn floatReadFromMemory(comptime F: type, target: Target, buffer: []const u8) F { @@ -2869,9 +2866,7 @@ pub const Value = extern union { 16 => return Value.Tag.float_16.create(arena, @intToFloat(f16, x)), 32 => return Value.Tag.float_32.create(arena, @intToFloat(f32, x)), 64 => return Value.Tag.float_64.create(arena, @intToFloat(f64, x)), - // We can't lower this properly on non-x86 llvm backends yet - //80 => return Value.Tag.float_80.create(arena, @intToFloat(f80, x)), - 80 => @panic("TODO f80 intToFloat"), + 80 => return Value.Tag.float_80.create(arena, @intToFloat(f80, x)), 128 => return Value.Tag.float_128.create(arena, @intToFloat(f128, x)), else => unreachable, } @@ -2908,9 +2903,9 @@ pub const Value = extern union { } const isNegative = std.math.signbit(value); - value = std.math.fabs(value); + value = @fabs(value); - const floored = std.math.floor(value); + const floored = @floor(value); var rational = try std.math.big.Rational.init(arena); defer rational.deinit(); @@ -2941,7 +2936,7 @@ pub const Value = extern union { return 1; } - const w_value = std.math.fabs(scalar); + const w_value = @fabs(scalar); return @divFloor(@floatToInt(std.math.big.Limb, std.math.log2(w_value)), @typeInfo(std.math.big.Limb).Int.bits) + 1; } @@ -3737,9 +3732,6 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @rem(lhs_val, rhs_val)); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt __remx"); - } const lhs_val = lhs.toFloat(f80); const rhs_val = rhs.toFloat(f80); return Value.Tag.float_80.create(arena, @rem(lhs_val, rhs_val)); @@ -3782,9 +3774,6 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @mod(lhs_val, rhs_val)); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt __modx"); - } const lhs_val = lhs.toFloat(f80); const rhs_val = rhs.toFloat(f80); return Value.Tag.float_80.create(arena, @mod(lhs_val, rhs_val)); @@ -4198,9 +4187,6 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, lhs_val / rhs_val); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt __divxf3"); - } const lhs_val = lhs.toFloat(f80); const rhs_val = rhs.toFloat(f80); return Value.Tag.float_80.create(arena, lhs_val / rhs_val); @@ -4255,9 +4241,6 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @divFloor(lhs_val, rhs_val)); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt __floorx"); - } const lhs_val = lhs.toFloat(f80); const rhs_val = rhs.toFloat(f80); return Value.Tag.float_80.create(arena, @divFloor(lhs_val, rhs_val)); @@ -4312,9 +4295,6 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @divTrunc(lhs_val, rhs_val)); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt __truncx"); - } const lhs_val = lhs.toFloat(f80); const rhs_val = rhs.toFloat(f80); return Value.Tag.float_80.create(arena, @divTrunc(lhs_val, rhs_val)); @@ -4369,9 +4349,6 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, lhs_val * rhs_val); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt __mulxf3"); - } const lhs_val = lhs.toFloat(f80); const rhs_val = rhs.toFloat(f80); return Value.Tag.float_80.create(arena, lhs_val * rhs_val); @@ -4411,16 +4388,10 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @sqrt(f)); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt __sqrtx"); - } const f = val.toFloat(f80); return Value.Tag.float_80.create(arena, @sqrt(f)); }, 128 => { - if (true) { - @panic("TODO implement compiler_rt sqrtq"); - } const f = val.toFloat(f128); return Value.Tag.float_128.create(arena, @sqrt(f)); }, @@ -4454,16 +4425,10 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @sin(f)); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt sin for f80"); - } const f = val.toFloat(f80); return Value.Tag.float_80.create(arena, @sin(f)); }, 128 => { - if (true) { - @panic("TODO implement compiler_rt sin for f128"); - } const f = val.toFloat(f128); return Value.Tag.float_128.create(arena, @sin(f)); }, @@ -4497,16 +4462,10 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @cos(f)); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt cos for f80"); - } const f = val.toFloat(f80); return Value.Tag.float_80.create(arena, @cos(f)); }, 128 => { - if (true) { - @panic("TODO implement compiler_rt cos for f128"); - } const f = val.toFloat(f128); return Value.Tag.float_128.create(arena, @cos(f)); }, @@ -4540,16 +4499,10 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @exp(f)); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt exp for f80"); - } const f = val.toFloat(f80); return Value.Tag.float_80.create(arena, @exp(f)); }, 128 => { - if (true) { - @panic("TODO implement compiler_rt exp for f128"); - } const f = val.toFloat(f128); return Value.Tag.float_128.create(arena, @exp(f)); }, @@ -4583,16 +4536,10 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @exp2(f)); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt exp2 for f80"); - } const f = val.toFloat(f80); return Value.Tag.float_80.create(arena, @exp2(f)); }, 128 => { - if (true) { - @panic("TODO implement compiler_rt exp2 for f128"); - } const f = val.toFloat(f128); return Value.Tag.float_128.create(arena, @exp2(f)); }, @@ -4626,16 +4573,10 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @log(f)); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt log for f80"); - } const f = val.toFloat(f80); return Value.Tag.float_80.create(arena, @log(f)); }, 128 => { - if (true) { - @panic("TODO implement compiler_rt log for f128"); - } const f = val.toFloat(f128); return Value.Tag.float_128.create(arena, @log(f)); }, @@ -4669,16 +4610,10 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @log2(f)); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt log2 for f80"); - } const f = val.toFloat(f80); return Value.Tag.float_80.create(arena, @log2(f)); }, 128 => { - if (true) { - @panic("TODO implement compiler_rt log2 for f128"); - } const f = val.toFloat(f128); return Value.Tag.float_128.create(arena, @log2(f)); }, @@ -4712,16 +4647,10 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @log10(f)); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt log10 for f80"); - } const f = val.toFloat(f80); return Value.Tag.float_80.create(arena, @log10(f)); }, 128 => { - if (true) { - @panic("TODO implement compiler_rt log10 for f128"); - } const f = val.toFloat(f128); return Value.Tag.float_128.create(arena, @log10(f)); }, @@ -4755,9 +4684,6 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @fabs(f)); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt fabs for f80 (__fabsx)"); - } const f = val.toFloat(f80); return Value.Tag.float_80.create(arena, @fabs(f)); }, @@ -4795,9 +4721,6 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @floor(f)); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt floor for f80 (__floorx)"); - } const f = val.toFloat(f80); return Value.Tag.float_80.create(arena, @floor(f)); }, @@ -4835,9 +4758,6 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @ceil(f)); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt ceil for f80"); - } const f = val.toFloat(f80); return Value.Tag.float_80.create(arena, @ceil(f)); }, @@ -4875,9 +4795,6 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @round(f)); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt round for f80"); - } const f = val.toFloat(f80); return Value.Tag.float_80.create(arena, @round(f)); }, @@ -4915,9 +4832,6 @@ pub const Value = extern union { return Value.Tag.float_64.create(arena, @trunc(f)); }, 80 => { - if (true) { - @panic("TODO implement compiler_rt trunc for f80"); - } const f = val.toFloat(f80); return Value.Tag.float_80.create(arena, @trunc(f)); }, diff --git a/test/behavior/math.zig b/test/behavior/math.zig index f5494adfeb..d15121e89b 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -6,6 +6,7 @@ const expectEqualSlices = std.testing.expectEqualSlices; const maxInt = std.math.maxInt; const minInt = std.math.minInt; const mem = std.mem; +const math = std.math; test "assignment operators" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -947,13 +948,13 @@ fn frem(comptime T: type) !void { else => unreachable, }; - try expect(std.math.fabs(@rem(@as(T, 6.9), @as(T, 4.0)) - @as(T, 2.9)) < epsilon); - try expect(std.math.fabs(@rem(@as(T, -6.9), @as(T, 4.0)) - @as(T, -2.9)) < epsilon); - try expect(std.math.fabs(@rem(@as(T, -5.0), @as(T, 3.0)) - @as(T, -2.0)) < epsilon); - try expect(std.math.fabs(@rem(@as(T, 3.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon); - try expect(std.math.fabs(@rem(@as(T, 1.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon); - try expect(std.math.fabs(@rem(@as(T, 0.0), @as(T, 1.0)) - @as(T, 0.0)) < epsilon); - try expect(std.math.fabs(@rem(@as(T, -0.0), @as(T, 1.0)) - @as(T, -0.0)) < epsilon); + try expect(@fabs(@rem(@as(T, 6.9), @as(T, 4.0)) - @as(T, 2.9)) < epsilon); + try expect(@fabs(@rem(@as(T, -6.9), @as(T, 4.0)) - @as(T, -2.9)) < epsilon); + try expect(@fabs(@rem(@as(T, -5.0), @as(T, 3.0)) - @as(T, -2.0)) < epsilon); + try expect(@fabs(@rem(@as(T, 3.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon); + try expect(@fabs(@rem(@as(T, 1.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon); + try expect(@fabs(@rem(@as(T, 0.0), @as(T, 1.0)) - @as(T, 0.0)) < epsilon); + try expect(@fabs(@rem(@as(T, -0.0), @as(T, 1.0)) - @as(T, -0.0)) < epsilon); } test "float modulo division using @mod" { @@ -978,13 +979,13 @@ fn fmod(comptime T: type) !void { else => unreachable, }; - try expect(std.math.fabs(@mod(@as(T, 6.9), @as(T, 4.0)) - @as(T, 2.9)) < epsilon); - try expect(std.math.fabs(@mod(@as(T, -6.9), @as(T, 4.0)) - @as(T, 1.1)) < epsilon); - try expect(std.math.fabs(@mod(@as(T, -5.0), @as(T, 3.0)) - @as(T, 1.0)) < epsilon); - try expect(std.math.fabs(@mod(@as(T, 3.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon); - try expect(std.math.fabs(@mod(@as(T, 1.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon); - try expect(std.math.fabs(@mod(@as(T, 0.0), @as(T, 1.0)) - @as(T, 0.0)) < epsilon); - try expect(std.math.fabs(@mod(@as(T, -0.0), @as(T, 1.0)) - @as(T, -0.0)) < epsilon); + try expect(@fabs(@mod(@as(T, 6.9), @as(T, 4.0)) - @as(T, 2.9)) < epsilon); + try expect(@fabs(@mod(@as(T, -6.9), @as(T, 4.0)) - @as(T, 1.1)) < epsilon); + try expect(@fabs(@mod(@as(T, -5.0), @as(T, 3.0)) - @as(T, 1.0)) < epsilon); + try expect(@fabs(@mod(@as(T, 3.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon); + try expect(@fabs(@mod(@as(T, 1.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon); + try expect(@fabs(@mod(@as(T, 0.0), @as(T, 1.0)) - @as(T, 0.0)) < epsilon); + try expect(@fabs(@mod(@as(T, -0.0), @as(T, 1.0)) - @as(T, -0.0)) < epsilon); } test "@sqrt" { @@ -1288,8 +1289,8 @@ test "NaN comparison f80" { } fn testNanEqNan(comptime F: type) !void { - var nan1 = std.math.nan(F); - var nan2 = std.math.nan(F); + var nan1 = math.nan(F); + var nan2 = math.nan(F); try expect(nan1 != nan2); try expect(!(nan1 == nan2)); try expect(!(nan1 > nan2)); @@ -1346,3 +1347,40 @@ test "signed zeros are represented properly" { try S.doTheTest(); comptime try S.doTheTest(); } + +test "comptime sin and ln" { + const v = comptime (@sin(@as(f32, 1)) + @log(@as(f32, 5))); + try expect(v == @sin(@as(f32, 1)) + @log(@as(f32, 5))); +} + +test "fabs" { + inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| { + // normals + try expect(@fabs(@as(T, 1.0)) == 1.0); + try expect(@fabs(@as(T, -1.0)) == 1.0); + try expect(@fabs(math.floatMin(T)) == math.floatMin(T)); + try expect(@fabs(-math.floatMin(T)) == math.floatMin(T)); + try expect(@fabs(math.floatMax(T)) == math.floatMax(T)); + try expect(@fabs(-math.floatMax(T)) == math.floatMax(T)); + + // subnormals + try expect(@fabs(@as(T, 0.0)) == 0.0); + try expect(@fabs(@as(T, -0.0)) == 0.0); + try expect(@fabs(math.floatTrueMin(T)) == math.floatTrueMin(T)); + try expect(@fabs(-math.floatTrueMin(T)) == math.floatTrueMin(T)); + + // non-finite numbers + try expect(math.isPositiveInf(@fabs(math.inf(T)))); + try expect(math.isPositiveInf(@fabs(-math.inf(T)))); + try expect(math.isNan(@fabs(math.nan(T)))); + } +} + +test "absFloat" { + try testAbsFloat(); + comptime try testAbsFloat(); +} +fn testAbsFloat() !void { + try expect(@fabs(@as(f32, -10.05)) == @as(f32, 10.05)); + try expect(@fabs(@as(f32, 10.05)) == @as(f32, 10.05)); +} From 0e8242b905514f838f36757f2f890241e7448554 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Tue, 26 Apr 2022 15:16:37 -0700 Subject: [PATCH 1246/2031] stage1: Manually lower softfloat ops when needed Updates stage1 to manually lower softfloat operations for all unary floating point operations, extension/truncation, and arithmetic. --- src/stage1/analyze.cpp | 4 +- src/stage1/analyze.hpp | 2 +- src/stage1/codegen.cpp | 714 ++++++++++++++++++++++------------------ src/stage1/ir.cpp | 4 +- src/stage1/ir_print.cpp | 4 +- 5 files changed, 395 insertions(+), 333 deletions(-) diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp index 15a8fdf81e..73e3cd0da6 100644 --- a/src/stage1/analyze.cpp +++ b/src/stage1/analyze.cpp @@ -10375,7 +10375,7 @@ void ZigValue::dump() { // float ops that take a single argument //TODO Powi, Pow, minnum, maxnum, maximum, minimum, copysign, lround, llround, lrint, llrint -const char *float_op_to_name(BuiltinFnId op) { +const char *float_un_op_to_name(BuiltinFnId op) { switch (op) { case BuiltinFnIdSqrt: return "sqrt"; @@ -10405,6 +10405,8 @@ const char *float_op_to_name(BuiltinFnId op) { return "nearbyint"; case BuiltinFnIdRound: return "round"; + case BuiltinFnIdMulAdd: + return "fma"; default: zig_unreachable(); } diff --git a/src/stage1/analyze.hpp b/src/stage1/analyze.hpp index 6d584ff361..64e0e199f8 100644 --- a/src/stage1/analyze.hpp +++ b/src/stage1/analyze.hpp @@ -307,7 +307,7 @@ void copy_const_val(CodeGen *g, ZigValue *dest, ZigValue *src); bool type_has_optional_repr(ZigType *ty); bool is_opt_err_set(ZigType *ty); bool type_is_numeric(ZigType *ty); -const char *float_op_to_name(BuiltinFnId op); +const char *float_un_op_to_name(BuiltinFnId op); #define src_assert(OK, SOURCE_NODE) src_assert_impl((OK), (SOURCE_NODE), __FILE__, __LINE__) diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index a2efed6bde..88e73baa3c 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -869,7 +869,7 @@ static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn name = "fma"; num_args = 3; } else if (fn_id == ZigLLVMFnIdFloatOp) { - name = float_op_to_name(op); + name = float_un_op_to_name(op); num_args = 1; } else { zig_unreachable(); @@ -1604,8 +1604,49 @@ static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType * return nullptr; } +static const char *get_compiler_rt_type_abbrev(ZigType *type) { + uint16_t bits; + if (type->id == ZigTypeIdFloat) { + bits = type->data.floating.bit_count; + } else if (type->id == ZigTypeIdInt) { + bits = type->data.integral.bit_count; + } else { + zig_unreachable(); + } + switch (bits) { + case 16: + return "h"; + case 32: + return "s"; + case 64: + return "d"; + case 80: + return "x"; + case 128: + return "t"; + default: + zig_unreachable(); + } +} -static LLVMValueRef gen_soft_f80_widen_or_shorten(CodeGen *g, ZigType *actual_type, +static const char *get_math_h_type_abbrev(CodeGen *g, ZigType *float_type) { + if (float_type == g->builtin_types.entry_f16) + return "h"; // Non-standard + else if (float_type == g->builtin_types.entry_f32) + return "s"; + else if (float_type == g->builtin_types.entry_f64) + return ""; + else if (float_type == g->builtin_types.entry_f80) + return "x"; // Non-standard + else if (float_type == g->builtin_types.entry_c_longdouble) + return "l"; + else if (float_type == g->builtin_types.entry_f128) + return "q"; // Non-standard + else + zig_unreachable(); +} + +static LLVMValueRef gen_soft_float_widen_or_shorten(CodeGen *g, ZigType *actual_type, ZigType *wanted_type, LLVMValueRef expr_val) { ZigType *scalar_actual_type = (actual_type->id == ZigTypeIdVector) ? @@ -1615,87 +1656,47 @@ static LLVMValueRef gen_soft_f80_widen_or_shorten(CodeGen *g, ZigType *actual_ty uint64_t actual_bits = scalar_actual_type->data.floating.bit_count; uint64_t wanted_bits = scalar_wanted_type->data.floating.bit_count; - - LLVMTypeRef param_type; - LLVMTypeRef return_type; - const char *func_name; + if (actual_bits == wanted_bits) + return expr_val; LLVMValueRef result; bool castTruncatedToF16 = false; - if (actual_bits == wanted_bits) { - return expr_val; - } else if (actual_bits == 80) { - param_type = g->builtin_types.entry_f80->llvm_type; - switch (wanted_bits) { - case 16: - // Only Arm has a native f16 type, other platforms soft-implement it - // using u16 instead. - if (target_is_arm(g->zig_target)) { - return_type = g->builtin_types.entry_f16->llvm_type; - } else { - return_type = g->builtin_types.entry_u16->llvm_type; - castTruncatedToF16 = true; - } - func_name = "__truncxfhf2"; - break; - case 32: - return_type = g->builtin_types.entry_f32->llvm_type; - func_name = "__truncxfsf2"; - break; - case 64: - return_type = g->builtin_types.entry_f64->llvm_type; - func_name = "__truncxfdf2"; - break; - case 128: - return_type = g->builtin_types.entry_f128->llvm_type; - func_name = "__extendxftf2"; - break; - default: - zig_unreachable(); - } - } else if (wanted_bits == 80) { - return_type = g->builtin_types.entry_f80->llvm_type; - switch (actual_bits) { - case 16: - // Only Arm has a native f16 type, other platforms soft-implement it - // using u16 instead. - if (target_is_arm(g->zig_target)) { - param_type = g->builtin_types.entry_f16->llvm_type; - } else { - param_type = g->builtin_types.entry_u16->llvm_type; - expr_val = LLVMBuildBitCast(g->builder, expr_val, param_type, ""); - } - func_name = "__extendhfxf2"; - break; - case 32: - param_type = g->builtin_types.entry_f32->llvm_type; - func_name = "__extendsfxf2"; - break; - case 64: - param_type = g->builtin_types.entry_f64->llvm_type; - func_name = "__extenddfxf2"; - break; - case 128: - param_type = g->builtin_types.entry_f128->llvm_type; - func_name = "__trunctfxf2"; - break; - default: - zig_unreachable(); - } + char fn_name[64]; + if (wanted_bits < actual_bits) { + sprintf(fn_name, "__trunc%sf%sf2", + get_compiler_rt_type_abbrev(scalar_actual_type), + get_compiler_rt_type_abbrev(scalar_wanted_type)); } else { - zig_unreachable(); + sprintf(fn_name, "__extend%sf%sf2", + get_compiler_rt_type_abbrev(scalar_actual_type), + get_compiler_rt_type_abbrev(scalar_wanted_type)); } - LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, func_name); + LLVMTypeRef return_type = scalar_wanted_type->llvm_type; + LLVMTypeRef param_type = scalar_actual_type->llvm_type; + + if (!target_is_arm(g->zig_target)) { + // Only Arm has a native f16 type, other platforms soft-implement it using u16 instead. + if (scalar_wanted_type == g->builtin_types.entry_f16) { + return_type = g->builtin_types.entry_u16->llvm_type; + castTruncatedToF16 = true; + } + if (scalar_actual_type == g->builtin_types.entry_f16) { + param_type = g->builtin_types.entry_u16->llvm_type; + expr_val = LLVMBuildBitCast(g->builder, expr_val, param_type, ""); + } + } + + LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, fn_name); if (func_ref == nullptr) { LLVMTypeRef fn_type = LLVMFunctionType(return_type, ¶m_type, 1, false); - func_ref = LLVMAddFunction(g->module, func_name, fn_type); + func_ref = LLVMAddFunction(g->module, fn_name, fn_type); } result = LLVMBuildCall(g->builder, func_ref, &expr_val, 1, ""); - // On non-Arm platforms we need to bitcast __truncxfhf2 result back to f16 + // On non-Arm platforms we need to bitcast __trunc<>fhf2 result back to f16 if (castTruncatedToF16) { result = LLVMBuildBitCast(g->builder, result, g->builtin_types.entry_f16->llvm_type, ""); } @@ -1721,7 +1722,7 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z || scalar_wanted_type == g->builtin_types.entry_f80) && !target_has_f80(g->zig_target)) { - return gen_soft_f80_widen_or_shorten(g, actual_type, wanted_type, expr_val); + return gen_soft_float_widen_or_shorten(g, actual_type, wanted_type, expr_val); } actual_bits = scalar_actual_type->data.floating.bit_count; wanted_bits = scalar_wanted_type->data.floating.bit_count; @@ -2978,10 +2979,50 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *operand_type, return result; } -static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) { - assert(type_entry->id == ZigTypeIdFloat || type_entry->id == ZigTypeIdVector); - LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op); - return LLVMBuildCall(g->builder, floor_fn, &val, 1, ""); +static LLVMValueRef get_soft_float_fn(CodeGen *g, const char *name, int param_count, LLVMTypeRef param_type, LLVMTypeRef return_type) { + LLVMValueRef existing_llvm_fn = LLVMGetNamedFunction(g->module, name); + if (existing_llvm_fn != nullptr) return existing_llvm_fn; + LLVMValueRef existing_llvm_alias = LLVMGetNamedGlobalAlias(g->module, name, strlen(name)); + if (existing_llvm_alias != nullptr) return LLVMAliasGetAliasee(existing_llvm_alias); + + LLVMTypeRef param_types[3] = { param_type, param_type, param_type }; + LLVMTypeRef fn_type = LLVMFunctionType(return_type, param_types, param_count, false); + return LLVMAddFunction(g->module, name, fn_type); +} + +static LLVMValueRef gen_soft_float_un_op(CodeGen *g, LLVMValueRef op, ZigType *operand_type, BuiltinFnId op_id) { + uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0; + + char fn_name[64]; + sprintf(fn_name, "%s%s", float_un_op_to_name(op_id), get_math_h_type_abbrev(g, operand_type)); + LLVMValueRef func_ref = get_soft_float_fn(g, fn_name, 1, operand_type->llvm_type, operand_type->llvm_type); + + LLVMValueRef result; + if (vector_len == 0) { + return LLVMBuildCall(g->builder, func_ref, &op, 1, ""); + } else { + result = build_alloca(g, operand_type, "", 0); + LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; + for (uint32_t i = 0; i < vector_len; i++) { + LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); + LLVMValueRef param = LLVMBuildExtractElement(g->builder, op, index_value, ""); + LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, ¶m, 1, ""); + LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), + call_result, index_value, ""); + } + return LLVMBuildLoad(g->builder, result, ""); + } +} + +static LLVMValueRef gen_float_un_op(CodeGen *g, LLVMValueRef operand, ZigType *operand_type, BuiltinFnId op) { + assert(operand_type->id == ZigTypeIdFloat || operand_type->id == ZigTypeIdVector); + ZigType *elem_type = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.elem_type : operand_type; + if ((elem_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) || + (elem_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) { + return gen_soft_float_un_op(g, operand, operand_type, op); + } + LLVMValueRef float_op_fn = get_float_fn(g, operand_type, ZigLLVMFnIdFloatOp, op); + return LLVMBuildCall(g->builder, float_op_fn, &operand, 1, ""); } enum DivKind { @@ -3088,7 +3129,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast case DivKindExact: if (want_runtime_safety) { // Safety check: a / b == floor(a / b) - LLVMValueRef floored = gen_float_op(g, result, operand_type, BuiltinFnIdFloor); + LLVMValueRef floored = gen_float_un_op(g, result, operand_type, BuiltinFnIdFloor); LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); @@ -3105,9 +3146,9 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast } return result; case DivKindTrunc: - return gen_float_op(g, result, operand_type, BuiltinFnIdTrunc); + return gen_float_un_op(g, result, operand_type, BuiltinFnIdTrunc); case DivKindFloor: - return gen_float_op(g, result, operand_type, BuiltinFnIdFloor); + return gen_float_un_op(g, result, operand_type, BuiltinFnIdFloor); } zig_unreachable(); } @@ -3269,17 +3310,7 @@ static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type } } -static LLVMValueRef get_soft_f80_bin_op_func(CodeGen *g, const char *name, int param_count, LLVMTypeRef return_type) { - LLVMValueRef existing_llvm_fn = LLVMGetNamedFunction(g->module, name); - if (existing_llvm_fn != nullptr) return existing_llvm_fn; - - LLVMTypeRef float_type_ref = g->builtin_types.entry_f80->llvm_type; - LLVMTypeRef param_types[2] = { float_type_ref, float_type_ref }; - LLVMTypeRef fn_type = LLVMFunctionType(return_type, param_types, param_count, false); - return LLVMAddFunction(g->module, name, fn_type); -} - -enum SoftF80Icmp { +enum Icmp { NONE, EQ_ZERO, NE_ZERO, @@ -3289,7 +3320,7 @@ enum SoftF80Icmp { EQ_ONE, }; -static LLVMValueRef add_f80_icmp(CodeGen *g, LLVMValueRef val, SoftF80Icmp kind) { +static LLVMValueRef add_icmp(CodeGen *g, LLVMValueRef val, Icmp kind) { switch (kind) { case NONE: return val; @@ -3322,22 +3353,123 @@ static LLVMValueRef add_f80_icmp(CodeGen *g, LLVMValueRef val, SoftF80Icmp kind) } } -static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, - Stage1AirInstBinOp *bin_op_instruction) -{ - IrBinOp op_id = bin_op_instruction->op_id; - Stage1AirInst *op1 = bin_op_instruction->op1; - Stage1AirInst *op2 = bin_op_instruction->op2; - uint32_t vector_len = op1->value->type->id == ZigTypeIdVector ? op1->value->type->data.vector.len : 0; +static LLVMValueRef gen_soft_int_to_float_op(CodeGen *g, LLVMValueRef value_ref, ZigType *operand_type, ZigType *result_type) { + uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0; - LLVMValueRef op1_value = ir_llvm_value(g, op1); - LLVMValueRef op2_value = ir_llvm_value(g, op2); + // Handle integers of non-pot bitsize by widening them. + const size_t bitsize = operand_type->data.integral.bit_count; + const bool is_signed = operand_type->data.integral.is_signed; + if (bitsize < 32 || !is_power_of_2(bitsize)) { + const size_t wider_bitsize = bitsize < 32 ? 32 : round_to_next_power_of_2(bitsize); + ZigType *const wider_type = get_int_type(g, is_signed, wider_bitsize); + value_ref = gen_widen_or_shorten(g, false, operand_type, wider_type, value_ref); + operand_type = wider_type; + } + assert(bitsize <= 128); - bool div_exact_safety_check = false; - LLVMTypeRef return_type = g->builtin_types.entry_f80->llvm_type; + const char *int_compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(operand_type); + const char *float_compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(result_type); + + char fn_name[64]; + if (is_signed) { + sprintf(fn_name, "__float%si%sf", int_compiler_rt_type_abbrev, float_compiler_rt_type_abbrev); + } else { + sprintf(fn_name, "__floatun%si%sf", int_compiler_rt_type_abbrev, float_compiler_rt_type_abbrev); + } + + int param_count = 1; + LLVMValueRef func_ref = get_soft_float_fn(g, fn_name, param_count, operand_type->llvm_type, result_type->llvm_type); + + LLVMValueRef result; + if (vector_len == 0) { + LLVMValueRef params[1] = {value_ref}; + result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); + } else { + ZigType *alloca_ty = operand_type; + result = build_alloca(g, alloca_ty, "", 0); + + LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; + for (uint32_t i = 0; i < vector_len; i++) { + LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); + LLVMValueRef params[1] = { + LLVMBuildExtractElement(g->builder, value_ref, index_value, ""), + }; + LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); + LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), + call_result, index_value, ""); + } + + result = LLVMBuildLoad(g->builder, result, ""); + } + return result; +} + +static LLVMValueRef gen_soft_float_to_int_op(CodeGen *g, LLVMValueRef value_ref, ZigType *operand_type, ZigType *result_type) { + uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0; + + // Handle integers of non-pot bitsize by truncating a sufficiently wide pot integer + const size_t bitsize = result_type->data.integral.bit_count; + const bool is_signed = result_type->data.integral.is_signed; + ZigType * wider_type = result_type; + if (bitsize < 32 || !is_power_of_2(bitsize)) { + const size_t wider_bitsize = bitsize < 32 ? 32 : round_to_next_power_of_2(bitsize); + wider_type = get_int_type(g, is_signed, wider_bitsize); + } + assert(bitsize <= 128); + + const char *float_compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(operand_type); + const char *int_compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(wider_type); + + char fn_name[64]; + if (is_signed) { + sprintf(fn_name, "__fix%sf%si", float_compiler_rt_type_abbrev, int_compiler_rt_type_abbrev); + } else { + sprintf(fn_name, "__fixuns%sf%si", float_compiler_rt_type_abbrev, int_compiler_rt_type_abbrev); + } + + int param_count = 1; + LLVMValueRef func_ref = get_soft_float_fn(g, fn_name, param_count, operand_type->llvm_type, wider_type->llvm_type); + + LLVMValueRef result; + if (vector_len == 0) { + LLVMValueRef params[1] = {value_ref}; + result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); + } else { + ZigType *alloca_ty = operand_type; + result = build_alloca(g, alloca_ty, "", 0); + + LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; + for (uint32_t i = 0; i < vector_len; i++) { + LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); + LLVMValueRef params[1] = { + LLVMBuildExtractElement(g->builder, value_ref, index_value, ""), + }; + LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); + LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), + call_result, index_value, ""); + } + + result = LLVMBuildLoad(g->builder, result, ""); + } + + // Handle integers of non-pot bitsize by shortening them on the output + if (result_type != wider_type) { + return gen_widen_or_shorten(g, false, wider_type, result_type, result); + } + return result; +} + +static LLVMValueRef gen_soft_float_bin_op(CodeGen *g, LLVMValueRef op1_value, LLVMValueRef op2_value, ZigType *operand_type, IrBinOp op_id) { + uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0; + + LLVMTypeRef return_type = operand_type->llvm_type; int param_count = 2; - const char *func_name; - SoftF80Icmp res_icmp = NONE; + + const char *compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(operand_type); + const char *math_h_type_abbrev = get_math_h_type_abbrev(g, operand_type); + + char fn_name[64]; + Icmp res_icmp = NONE; switch (op_id) { case IrBinOpInvalid: case IrBinOpArrayCat: @@ -3362,152 +3494,129 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, zig_unreachable(); case IrBinOpCmpEq: return_type = g->builtin_types.entry_i32->llvm_type; - func_name = "__eqxf2"; + sprintf(fn_name, "__eq%sf2", compiler_rt_type_abbrev); res_icmp = EQ_ZERO; break; case IrBinOpCmpNotEq: return_type = g->builtin_types.entry_i32->llvm_type; - func_name = "__nexf2"; + sprintf(fn_name, "__ne%sf2", compiler_rt_type_abbrev); res_icmp = NE_ZERO; break; case IrBinOpCmpLessOrEq: return_type = g->builtin_types.entry_i32->llvm_type; - func_name = "__lexf2"; + sprintf(fn_name, "__le%sf2", compiler_rt_type_abbrev); res_icmp = LE_ZERO; break; case IrBinOpCmpLessThan: return_type = g->builtin_types.entry_i32->llvm_type; - func_name = "__lexf2"; + sprintf(fn_name, "__le%sf2", compiler_rt_type_abbrev); res_icmp = EQ_NEG; break; case IrBinOpCmpGreaterOrEq: return_type = g->builtin_types.entry_i32->llvm_type; - func_name = "__gexf2"; + sprintf(fn_name, "__ge%sf2", compiler_rt_type_abbrev); res_icmp = GE_ZERO; break; case IrBinOpCmpGreaterThan: return_type = g->builtin_types.entry_i32->llvm_type; - func_name = "__gexf2"; + sprintf(fn_name, "__ge%sf2", compiler_rt_type_abbrev); res_icmp = EQ_ONE; break; case IrBinOpMaximum: - func_name = "__fmaxx"; + sprintf(fn_name, "fmax%s", math_h_type_abbrev); break; case IrBinOpMinimum: - func_name = "__fminx"; + sprintf(fn_name, "fmin%s", math_h_type_abbrev); break; case IrBinOpMult: - func_name = "__mulxf3"; + sprintf(fn_name, "__mul%sf3", compiler_rt_type_abbrev); break; case IrBinOpAdd: - func_name = "__addxf3"; + sprintf(fn_name, "__add%sf3", compiler_rt_type_abbrev); break; case IrBinOpSub: - func_name = "__subxf3"; + sprintf(fn_name, "__sub%sf3", compiler_rt_type_abbrev); break; case IrBinOpDivUnspecified: - func_name = "__divxf3"; - break; case IrBinOpDivExact: - func_name = "__divxf3"; - div_exact_safety_check = bin_op_instruction->safety_check_on && - ir_want_runtime_safety(g, &bin_op_instruction->base); - break; case IrBinOpDivTrunc: - param_count = 1; - func_name = "__truncx"; - break; case IrBinOpDivFloor: - param_count = 1; - func_name = "__floorx"; + sprintf(fn_name, "__div%sf3", compiler_rt_type_abbrev); break; case IrBinOpRemRem: - param_count = 1; - func_name = "__remx"; - break; case IrBinOpRemMod: - param_count = 1; - func_name = "__modx"; + sprintf(fn_name, "fmod%s", math_h_type_abbrev); break; default: zig_unreachable(); } - LLVMValueRef func_ref = get_soft_f80_bin_op_func(g, func_name, param_count, return_type); + LLVMValueRef func_ref = get_soft_float_fn(g, fn_name, param_count, operand_type->llvm_type, return_type); LLVMValueRef result; if (vector_len == 0) { LLVMValueRef params[2] = {op1_value, op2_value}; result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); - result = add_f80_icmp(g, result, res_icmp); + result = add_icmp(g, result, res_icmp); } else { - ZigType *alloca_ty = op1->value->type; + ZigType *alloca_ty = operand_type; if (res_icmp != NONE) alloca_ty = get_vector_type(g, vector_len, g->builtin_types.entry_bool); result = build_alloca(g, alloca_ty, "", 0); - } - - LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; - for (uint32_t i = 0; i < vector_len; i++) { - LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); - LLVMValueRef params[2] = { - LLVMBuildExtractElement(g->builder, op1_value, index_value, ""), - LLVMBuildExtractElement(g->builder, op2_value, index_value, ""), - }; - LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); - call_result = add_f80_icmp(g, call_result, res_icmp); - LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), - call_result, index_value, ""); - } - - if (div_exact_safety_check) { - // Safety check: a / b == floor(a / b) - LLVMValueRef floor_func = get_soft_f80_bin_op_func(g, "__floorx", 1, return_type); - LLVMValueRef eq_func = get_soft_f80_bin_op_func(g, "__eqxf2", 2, g->builtin_types.entry_i32->llvm_type); - - LLVMValueRef ok_bit; - if (vector_len == 0) { - LLVMValueRef floored = LLVMBuildCall(g->builder, floor_func, &result, 1, ""); - - LLVMValueRef params[2] = {result, floored}; - ok_bit = LLVMBuildCall(g->builder, eq_func, params, 2, ""); - } else { - ZigType *bool_vec_ty = get_vector_type(g, vector_len, g->builtin_types.entry_bool); - ok_bit = build_alloca(g, bool_vec_ty, "", 0); - } + LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; for (uint32_t i = 0; i < vector_len; i++) { LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); - LLVMValueRef div_res = LLVMBuildExtractElement(g->builder, - LLVMBuildLoad(g->builder, result, ""), index_value, ""); - LLVMValueRef params[2] = { - div_res, - LLVMBuildCall(g->builder, floor_func, &div_res, 1, ""), + LLVMBuildExtractElement(g->builder, op1_value, index_value, ""), + LLVMBuildExtractElement(g->builder, op2_value, index_value, ""), }; - LLVMValueRef cmp_res = LLVMBuildCall(g->builder, eq_func, params, 2, ""); - cmp_res = LLVMBuildTrunc(g->builder, cmp_res, g->builtin_types.entry_bool->llvm_type, ""); - LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, ok_bit, ""), - cmp_res, index_value, ""); + LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); + call_result = add_icmp(g, call_result, res_icmp); + LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), + call_result, index_value, ""); } - if (vector_len != 0) { - ok_bit = ZigLLVMBuildAndReduce(g->builder, LLVMBuildLoad(g->builder, ok_bit, "")); - } - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); - - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdExactDivisionRemainder); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - - if (vector_len != 0) { result = LLVMBuildLoad(g->builder, result, ""); } - return result; + + // Some operations are implemented as compound ops and require us to perform some + // more operations before we obtain the final result + switch (op_id) { + case IrBinOpDivTrunc: + return gen_float_un_op(g, result, operand_type, BuiltinFnIdTrunc); + case IrBinOpDivFloor: + return gen_float_un_op(g, result, operand_type, BuiltinFnIdFloor); + case IrBinOpRemMod: + { + LLVMValueRef b = gen_soft_float_bin_op(g, result, op2_value, operand_type, IrBinOpAdd); + LLVMValueRef wrapped_result = gen_soft_float_bin_op(g, b, op2_value, operand_type, IrBinOpRemRem); + LLVMValueRef zero = LLVMConstNull(operand_type->llvm_type); + LLVMValueRef ltz = gen_soft_float_bin_op(g, op1_value, zero, operand_type, IrBinOpCmpLessThan); + + return LLVMBuildSelect(g->builder, ltz, wrapped_result, result, ""); + } + case IrBinOpDivExact: + { + LLVMValueRef floored = gen_float_un_op(g, result, operand_type, BuiltinFnIdFloor); + LLVMValueRef ok_bit = gen_soft_float_bin_op(g, result, floored, operand_type, IrBinOpCmpEq); + if (vector_len != 0) { + ok_bit = ZigLLVMBuildAndReduce(g->builder, ok_bit); + } + + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdExactDivisionRemainder); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + return result; + default: + return result; + } + zig_unreachable(); } static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable, @@ -3519,8 +3628,13 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable, ZigType *operand_type = op1->value->type; ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; - if (scalar_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) { - return ir_render_soft_f80_bin_op(g, executable, bin_op_instruction); + if ((scalar_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) || + (scalar_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) { + // LLVM incorrectly lowers the soft float calls for f128 as if they operated on `long double`. + // On some targets this will be incorrect, so we manually lower the call ourselves. + LLVMValueRef op1_value = ir_llvm_value(g, op1); + LLVMValueRef op2_value = ir_llvm_value(g, op2); + return gen_soft_float_bin_op(g, op1_value, op2_value, operand_type, op_id); } @@ -3828,10 +3942,17 @@ static LLVMValueRef ir_render_cast(CodeGen *g, Stage1Air *executable, } case CastOpIntToFloat: assert(actual_type->id == ZigTypeIdInt); - if (actual_type->data.integral.is_signed) { - return LLVMBuildSIToFP(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); - } else { - return LLVMBuildUIToFP(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + { + if ((wanted_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) || + (wanted_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) { + return gen_soft_int_to_float_op(g, expr_val, actual_type, wanted_type); + } else { + if (actual_type->data.integral.is_signed) { + return LLVMBuildSIToFP(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + } else { + return LLVMBuildUIToFP(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + } + } } case CastOpFloatToInt: { assert(wanted_type->id == ZigTypeIdInt); @@ -3840,18 +3961,28 @@ static LLVMValueRef ir_render_cast(CodeGen *g, Stage1Air *executable, bool want_safety = ir_want_runtime_safety(g, &cast_instruction->base); LLVMValueRef result; - if (wanted_type->data.integral.is_signed) { - result = LLVMBuildFPToSI(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + if ((actual_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) || + (actual_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) { + result = gen_soft_float_to_int_op(g, expr_val, actual_type, wanted_type); } else { - result = LLVMBuildFPToUI(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + if (wanted_type->data.integral.is_signed) { + result = LLVMBuildFPToSI(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + } else { + result = LLVMBuildFPToUI(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + } } if (want_safety) { LLVMValueRef back_to_float; - if (wanted_type->data.integral.is_signed) { - back_to_float = LLVMBuildSIToFP(g->builder, result, LLVMTypeOf(expr_val), ""); + if ((actual_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) || + (actual_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) { + back_to_float = gen_soft_int_to_float_op(g, result, wanted_type, actual_type); } else { - back_to_float = LLVMBuildUIToFP(g->builder, result, LLVMTypeOf(expr_val), ""); + if (wanted_type->data.integral.is_signed) { + back_to_float = LLVMBuildSIToFP(g->builder, result, LLVMTypeOf(expr_val), ""); + } else { + back_to_float = LLVMBuildUIToFP(g->builder, result, LLVMTypeOf(expr_val), ""); + } } LLVMValueRef difference = LLVMBuildFSub(g->builder, expr_val, back_to_float, ""); LLVMValueRef one_pos = LLVMConstReal(LLVMTypeOf(expr_val), 1.0f); @@ -4151,42 +4282,46 @@ static LLVMValueRef ir_render_binary_not(CodeGen *g, Stage1Air *executable, return LLVMBuildNot(g->builder, operand, ""); } -static LLVMValueRef ir_gen_soft_f80_neg(CodeGen *g, ZigType *op_type, LLVMValueRef operand) { - uint32_t vector_len = op_type->id == ZigTypeIdVector ? op_type->data.vector.len : 0; +static LLVMValueRef gen_soft_float_neg(CodeGen *g, ZigType *operand_type, LLVMValueRef operand) { + uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0; + uint16_t num_bits = operand_type->data.floating.bit_count; - LLVMTypeRef llvm_i80 = LLVMIntType(80); - LLVMValueRef sign_mask = LLVMConstInt(llvm_i80, 1, false); - sign_mask = LLVMConstShl(sign_mask, LLVMConstInt(llvm_i80, 79, false)); + ZigType *iX_type = get_int_type(g, true, num_bits); + LLVMValueRef sign_mask = LLVMConstInt(iX_type->llvm_type, 1, false); + sign_mask = LLVMConstShl(sign_mask, LLVMConstInt(iX_type->llvm_type, num_bits - 1, false)); - LLVMValueRef result; if (vector_len == 0) { - result = LLVMBuildXor(g->builder, operand, sign_mask, ""); - } else { - result = build_alloca(g, op_type, "", 0); - } + LLVMValueRef bitcasted_operand = LLVMBuildBitCast(g->builder, operand, iX_type->llvm_type, ""); + LLVMValueRef result = LLVMBuildXor(g->builder, bitcasted_operand, sign_mask, ""); - LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; - for (uint32_t i = 0; i < vector_len; i++) { - LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); - LLVMValueRef xor_operand = LLVMBuildExtractElement(g->builder, operand, index_value, ""); - LLVMValueRef xor_result = LLVMBuildXor(g->builder, xor_operand, sign_mask, ""); - LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), - xor_result, index_value, ""); + return LLVMBuildBitCast(g->builder, result, operand_type->llvm_type, ""); + } else { + LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; + ZigType *iX_vector_type = get_vector_type(g, vector_len, iX_type); + + LLVMValueRef result = build_alloca(g, iX_vector_type, "", 0); + LLVMValueRef bitcasted_operand = LLVMBuildBitCast(g->builder, operand, iX_vector_type->llvm_type, ""); + for (uint32_t i = 0; i < vector_len; i++) { + LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); + LLVMValueRef elem = LLVMBuildExtractElement(g->builder, bitcasted_operand, index_value, ""); + LLVMValueRef result_elem = LLVMBuildXor(g->builder, elem, sign_mask, ""); + LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), + result_elem, index_value, ""); + } + return LLVMBuildBitCast(g->builder, LLVMBuildLoad(g->builder, result, ""), operand_type->llvm_type, ""); } - if (vector_len != 0) { - result = LLVMBuildLoad(g->builder, result, ""); - } - return result; } -static LLVMValueRef ir_gen_negation(CodeGen *g, Stage1AirInst *inst, Stage1AirInst *operand, bool wrapping) { +static LLVMValueRef gen_negation(CodeGen *g, Stage1AirInst *inst, Stage1AirInst *operand, bool wrapping) { LLVMValueRef llvm_operand = ir_llvm_value(g, operand); ZigType *operand_type = operand->value->type; ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; - if (scalar_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) - return ir_gen_soft_f80_neg(g, operand_type, llvm_operand); + if ((scalar_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) || + (scalar_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) { + return gen_soft_float_neg(g, operand_type, llvm_operand); + } if (scalar_type->id == ZigTypeIdFloat) { ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, inst)); @@ -4210,7 +4345,7 @@ static LLVMValueRef ir_gen_negation(CodeGen *g, Stage1AirInst *inst, Stage1AirIn static LLVMValueRef ir_render_negation(CodeGen *g, Stage1Air *executable, Stage1AirInstNegation *inst) { - return ir_gen_negation(g, &inst->base, inst->operand, inst->wrapping); + return gen_negation(g, &inst->base, inst->operand, inst->wrapping); } static LLVMValueRef ir_render_bool_not(CodeGen *g, Stage1Air *executable, Stage1AirInstBoolNot *instruction) { @@ -7024,110 +7159,34 @@ static LLVMValueRef ir_render_atomic_store(CodeGen *g, Stage1Air *executable, return nullptr; } -static LLVMValueRef ir_render_soft_f80_float_op(CodeGen *g, Stage1Air *executable, Stage1AirInstFloatOp *instruction) { - ZigType *op_type = instruction->operand->value->type; - uint32_t vector_len = op_type->id == ZigTypeIdVector ? op_type->data.vector.len : 0; - - const char *func_name; - switch (instruction->fn_id) { - case BuiltinFnIdSqrt: - func_name = "__sqrtx"; - break; - case BuiltinFnIdSin: - func_name = "__sinx"; - break; - case BuiltinFnIdCos: - func_name = "__cosx"; - break; - case BuiltinFnIdExp: - func_name = "__expx"; - break; - case BuiltinFnIdExp2: - func_name = "__exp2x"; - break; - case BuiltinFnIdLog: - func_name = "__logx"; - break; - case BuiltinFnIdLog2: - func_name = "__log2x"; - break; - case BuiltinFnIdLog10: - func_name = "__log10x"; - break; - case BuiltinFnIdFabs: - func_name = "__fabsx"; - break; - case BuiltinFnIdFloor: - func_name = "__floorx"; - break; - case BuiltinFnIdCeil: - func_name = "__ceilx"; - break; - case BuiltinFnIdTrunc: - func_name = "__truncx"; - break; - case BuiltinFnIdNearbyInt: - func_name = "__nearbyintx"; - break; - case BuiltinFnIdRound: - func_name = "__roundx"; - break; - default: - zig_unreachable(); - } - - - LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, func_name); - if (func_ref == nullptr) { - LLVMTypeRef f80_ref = g->builtin_types.entry_f80->llvm_type; - LLVMTypeRef fn_type = LLVMFunctionType(f80_ref, &f80_ref, 1, false); - func_ref = LLVMAddFunction(g->module, func_name, fn_type); - } - - LLVMValueRef operand = ir_llvm_value(g, instruction->operand); - LLVMValueRef result; - if (vector_len == 0) { - result = LLVMBuildCall(g->builder, func_ref, &operand, 1, ""); - } else { - result = build_alloca(g, instruction->operand->value->type, "", 0); - } - - LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; - for (uint32_t i = 0; i < vector_len; i++) { - LLVMValueRef index_value = LLVMConstInt(usize_ref, i, false); - LLVMValueRef param = LLVMBuildExtractElement(g->builder, operand, index_value, ""); - LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, ¶m, 1, ""); - LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), - call_result, index_value, ""); - } - if (vector_len != 0) { - result = LLVMBuildLoad(g->builder, result, ""); - } - return result; -} - static LLVMValueRef ir_render_float_op(CodeGen *g, Stage1Air *executable, Stage1AirInstFloatOp *instruction) { - ZigType *op_type = instruction->operand->value->type; - op_type = op_type->id == ZigTypeIdVector ? op_type->data.vector.elem_type : op_type; - if (op_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) { - return ir_render_soft_f80_float_op(g, executable, instruction); - } LLVMValueRef operand = ir_llvm_value(g, instruction->operand); - LLVMValueRef fn_val = get_float_fn(g, instruction->base.value->type, ZigLLVMFnIdFloatOp, instruction->fn_id); - return LLVMBuildCall(g->builder, fn_val, &operand, 1, ""); + ZigType *operand_type = instruction->operand->value->type; + return gen_float_un_op(g, operand, operand_type, instruction->fn_id); } -static LLVMValueRef ir_render_soft_f80_mul_add(CodeGen *g, Stage1Air *executable, Stage1AirInstMulAdd *instruction) { - ZigType *op_type = instruction->op1->value->type; - uint32_t vector_len = op_type->id == ZigTypeIdVector ? op_type->data.vector.len : 0; +static LLVMValueRef ir_render_soft_mul_add(CodeGen *g, Stage1Air *executable, Stage1AirInstMulAdd *instruction, ZigType *float_type) { + ZigType *operand_type = instruction->op1->value->type; + uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0; - const char *func_name = "__fmax"; - LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, func_name); + const char *fn_name; + if (float_type == g->builtin_types.entry_f32) + fn_name = "fmaf"; + else if (float_type == g->builtin_types.entry_f64) + fn_name = "fma"; + else if (float_type == g->builtin_types.entry_f80) + fn_name = "__fmax"; + else if (float_type == g->builtin_types.entry_f128) + fn_name = "fmaq"; + else + zig_unreachable(); + + LLVMValueRef func_ref = LLVMGetNamedFunction(g->module, fn_name); if (func_ref == nullptr) { - LLVMTypeRef f80_ref = g->builtin_types.entry_f80->llvm_type; - LLVMTypeRef params[3] = { f80_ref, f80_ref, f80_ref }; - LLVMTypeRef fn_type = LLVMFunctionType(f80_ref, params, 3, false); - func_ref = LLVMAddFunction(g->module, func_name, fn_type); + LLVMTypeRef float_type_ref = float_type->llvm_type; + LLVMTypeRef params[3] = { float_type_ref, float_type_ref, float_type_ref }; + LLVMTypeRef fn_type = LLVMFunctionType(float_type_ref, params, 3, false); + func_ref = LLVMAddFunction(g->module, fn_name, fn_type); } LLVMValueRef op1 = ir_llvm_value(g, instruction->op1); @@ -7161,10 +7220,11 @@ static LLVMValueRef ir_render_soft_f80_mul_add(CodeGen *g, Stage1Air *executable } static LLVMValueRef ir_render_mul_add(CodeGen *g, Stage1Air *executable, Stage1AirInstMulAdd *instruction) { - ZigType *op_type = instruction->op1->value->type; - op_type = op_type->id == ZigTypeIdVector ? op_type->data.vector.elem_type : op_type; - if (op_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) { - return ir_render_soft_f80_mul_add(g, executable, instruction); + ZigType *operand_type = instruction->op1->value->type; + operand_type = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.elem_type : operand_type; + if ((operand_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) || + (operand_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) { + return ir_render_soft_mul_add(g, executable, instruction, operand_type); } LLVMValueRef op1 = ir_llvm_value(g, instruction->op1); LLVMValueRef op2 = ir_llvm_value(g, instruction->op2); diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index b8ae1ea93e..874d068c03 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -24300,7 +24300,7 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_ case BuiltinFnIdLog2: return ir_add_error_node(ira, source_node, buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026", - float_op_to_name(fop), buf_ptr(&float_type->name))); + float_un_op_to_name(fop), buf_ptr(&float_type->name))); default: zig_unreachable(); } @@ -24344,7 +24344,7 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_ case BuiltinFnIdLog2: return ir_add_error_node(ira, source_node, buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026", - float_op_to_name(fop), buf_ptr(&float_type->name))); + float_un_op_to_name(fop), buf_ptr(&float_type->name))); default: zig_unreachable(); } diff --git a/src/stage1/ir_print.cpp b/src/stage1/ir_print.cpp index 5c7727da0c..9296242a3e 100644 --- a/src/stage1/ir_print.cpp +++ b/src/stage1/ir_print.cpp @@ -2558,13 +2558,13 @@ static void ir_print_add_implicit_return_type(IrPrintSrc *irp, Stage1ZirInstAddI } static void ir_print_float_op(IrPrintSrc *irp, Stage1ZirInstFloatOp *instruction) { - fprintf(irp->f, "@%s(", float_op_to_name(instruction->fn_id)); + fprintf(irp->f, "@%s(", float_un_op_to_name(instruction->fn_id)); ir_print_other_inst_src(irp, instruction->operand); fprintf(irp->f, ")"); } static void ir_print_float_op(IrPrintGen *irp, Stage1AirInstFloatOp *instruction) { - fprintf(irp->f, "@%s(", float_op_to_name(instruction->fn_id)); + fprintf(irp->f, "@%s(", float_un_op_to_name(instruction->fn_id)); ir_print_other_inst_gen(irp, instruction->operand); fprintf(irp->f, ")"); } From 7d6a6ce87fde465ffc3bd6b0a8bb6e039c67268f Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Tue, 26 Apr 2022 15:19:55 -0700 Subject: [PATCH 1247/2031] stage2: Manually lower softfloat ops when needed Updates stage2 to manually lower softfloat operations for all unary floating point operations and arithmetic. Softfloat support still needs to be added for conversion operators (float<->float and int<->float) --- src/codegen/llvm.zig | 412 ++++++++++++++++++++++------------ src/codegen/llvm/bindings.zig | 11 + src/zig_llvm.cpp | 4 + src/zig_llvm.h | 1 + 4 files changed, 286 insertions(+), 142 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index b2abc536ef..bf7f4e2ce1 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3518,19 +3518,19 @@ pub const FuncGen = struct { .shr => try self.airShr(inst, false), .shr_exact => try self.airShr(inst, true), - .sqrt => try self.airUnaryOp(inst, "sqrt"), - .sin => try self.airUnaryOp(inst, "sin"), - .cos => try self.airUnaryOp(inst, "cos"), - .exp => try self.airUnaryOp(inst, "exp"), - .exp2 => try self.airUnaryOp(inst, "exp2"), - .log => try self.airUnaryOp(inst, "log"), - .log2 => try self.airUnaryOp(inst, "log2"), - .log10 => try self.airUnaryOp(inst, "log10"), - .fabs => try self.airUnaryOp(inst, "fabs"), - .floor => try self.airUnaryOp(inst, "floor"), - .ceil => try self.airUnaryOp(inst, "ceil"), - .round => try self.airUnaryOp(inst, "round"), - .trunc_float => try self.airUnaryOp(inst, "trunc"), + .sqrt => try self.airUnaryOp(inst, .sqrt), + .sin => try self.airUnaryOp(inst, .sin), + .cos => try self.airUnaryOp(inst, .cos), + .exp => try self.airUnaryOp(inst, .exp), + .exp2 => try self.airUnaryOp(inst, .exp2), + .log => try self.airUnaryOp(inst, .log), + .log2 => try self.airUnaryOp(inst, .log2), + .log10 => try self.airUnaryOp(inst, .log10), + .fabs => try self.airUnaryOp(inst, .fabs), + .floor => try self.airUnaryOp(inst, .floor), + .ceil => try self.airUnaryOp(inst, .ceil), + .round => try self.airUnaryOp(inst, .round), + .trunc_float => try self.airUnaryOp(inst, .trunc), .cmp_eq => try self.airCmp(inst, .eq), .cmp_gt => try self.airCmp(inst, .gt), @@ -3905,7 +3905,7 @@ pub const FuncGen = struct { rhs: *const llvm.Value, operand_ty: Type, op: math.CompareOperator, - ) *const llvm.Value { + ) Allocator.Error!*const llvm.Value { var int_buffer: Type.Payload.Bits = undefined; var opt_buffer: Type.Payload.ElemType = undefined; @@ -3947,7 +3947,7 @@ pub const FuncGen = struct { self.builder.positionBuilderAtEnd(both_pl_block); const lhs_payload = self.optPayloadHandle(lhs, is_by_ref); const rhs_payload = self.optPayloadHandle(rhs, is_by_ref); - const payload_cmp = self.cmp(lhs_payload, rhs_payload, payload_ty, op); + const payload_cmp = try self.cmp(lhs_payload, rhs_payload, payload_ty, op); _ = self.builder.buildBr(end_block); const both_pl_block_end = self.builder.getInsertBlock(); @@ -3983,17 +3983,7 @@ pub const FuncGen = struct { ); return phi_node; }, - .Float => { - const operation: llvm.RealPredicate = switch (op) { - .eq => .OEQ, - .neq => .UNE, - .lt => .OLT, - .lte => .OLE, - .gt => .OGT, - .gte => .OGE, - }; - return self.builder.buildFCmp(operation, lhs, rhs, ""); - }, + .Float => return self.buildFloatCmp(op, operand_ty, &.{ lhs, rhs }), else => unreachable, }; const is_signed = int_ty.isSignedInt(); @@ -5221,7 +5211,7 @@ pub const FuncGen = struct { const inst_ty = self.air.typeOfIndex(inst); const scalar_ty = inst_ty.scalarType(); - if (scalar_ty.isAnyFloat()) return self.builder.buildFAdd(lhs, rhs, ""); + if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.add, inst_ty, &.{ lhs, rhs }); if (scalar_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, ""); return self.builder.buildNUWAdd(lhs, rhs, ""); } @@ -5260,7 +5250,7 @@ pub const FuncGen = struct { const inst_ty = self.air.typeOfIndex(inst); const scalar_ty = inst_ty.scalarType(); - if (scalar_ty.isAnyFloat()) return self.builder.buildFSub(lhs, rhs, ""); + if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.sub, inst_ty, &.{ lhs, rhs }); if (scalar_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, ""); return self.builder.buildNUWSub(lhs, rhs, ""); } @@ -5298,7 +5288,7 @@ pub const FuncGen = struct { const inst_ty = self.air.typeOfIndex(inst); const scalar_ty = inst_ty.scalarType(); - if (scalar_ty.isAnyFloat()) return self.builder.buildFMul(lhs, rhs, ""); + if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.mul, inst_ty, &.{ lhs, rhs }); if (scalar_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, ""); return self.builder.buildNUWMul(lhs, rhs, ""); } @@ -5333,8 +5323,9 @@ pub const FuncGen = struct { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); + const inst_ty = self.air.typeOfIndex(inst); - return self.builder.buildFDiv(lhs, rhs, ""); + return self.buildFloatOp(.div, inst_ty, &.{ lhs, rhs }); } fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { @@ -5347,8 +5338,8 @@ pub const FuncGen = struct { const scalar_ty = inst_ty.scalarType(); if (scalar_ty.isRuntimeFloat()) { - const result = self.builder.buildFDiv(lhs, rhs, ""); - return self.callTrunc(result, inst_ty); + const result = try self.buildFloatOp(.div, inst_ty, &.{ lhs, rhs }); + return self.buildFloatOp(.trunc, inst_ty, &.{result}); } if (scalar_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, ""); return self.builder.buildUDiv(lhs, rhs, ""); @@ -5364,8 +5355,8 @@ pub const FuncGen = struct { const scalar_ty = inst_ty.scalarType(); if (scalar_ty.isRuntimeFloat()) { - const result = self.builder.buildFDiv(lhs, rhs, ""); - return try self.callFloor(result, inst_ty); + const result = try self.buildFloatOp(.div, inst_ty, &.{ lhs, rhs }); + return self.buildFloatOp(.floor, inst_ty, &.{result}); } if (scalar_ty.isSignedInt()) { // const d = @divTrunc(a, b); @@ -5395,7 +5386,7 @@ pub const FuncGen = struct { const inst_ty = self.air.typeOfIndex(inst); const scalar_ty = inst_ty.scalarType(); - if (scalar_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, ""); + if (scalar_ty.isRuntimeFloat()) return self.buildFloatOp(.div, inst_ty, &.{ lhs, rhs }); if (scalar_ty.isSignedInt()) return self.builder.buildExactSDiv(lhs, rhs, ""); return self.builder.buildExactUDiv(lhs, rhs, ""); } @@ -5409,7 +5400,7 @@ pub const FuncGen = struct { const inst_ty = self.air.typeOfIndex(inst); const scalar_ty = inst_ty.scalarType(); - if (scalar_ty.isRuntimeFloat()) return self.builder.buildFRem(lhs, rhs, ""); + if (scalar_ty.isRuntimeFloat()) return self.buildFloatOp(.rem, inst_ty, &.{ lhs, rhs }); if (scalar_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, ""); return self.builder.buildURem(lhs, rhs, ""); } @@ -5425,11 +5416,11 @@ pub const FuncGen = struct { const scalar_ty = inst_ty.scalarType(); if (scalar_ty.isRuntimeFloat()) { - const a = self.builder.buildFRem(lhs, rhs, ""); - const b = self.builder.buildFAdd(a, rhs, ""); - const c = self.builder.buildFRem(b, rhs, ""); + const a = try self.buildFloatOp(.rem, inst_ty, &.{ lhs, rhs }); + const b = try self.buildFloatOp(.add, inst_ty, &.{ a, rhs }); + const c = try self.buildFloatOp(.rem, inst_ty, &.{ b, rhs }); const zero = inst_llvm_ty.constNull(); - const ltz = self.builder.buildFCmp(.OLT, lhs, zero, ""); + const ltz = try self.buildFloatCmp(.lt, inst_ty, &.{ lhs, zero }); return self.builder.buildSelect(ltz, c, a, ""); } if (scalar_ty.isSignedInt()) { @@ -5508,6 +5499,241 @@ pub const FuncGen = struct { return result_struct; } + fn buildElementwiseCall( + self: *FuncGen, + llvm_fn: *const llvm.Value, + args_vectors: []const *const llvm.Value, + result_vector: *const llvm.Value, + vector_len: usize, + ) !*const llvm.Value { + const args_len = @intCast(c_uint, args_vectors.len); + const llvm_i32 = self.context.intType(32); + assert(args_len <= 8); + + var i: usize = 0; + var result = result_vector; + while (i < vector_len) : (i += 1) { + const index_i32 = llvm_i32.constInt(i, .False); + + var args: [8]*const llvm.Value = undefined; + for (args_vectors) |arg_vector, k| { + args[k] = self.builder.buildExtractElement(arg_vector, index_i32, ""); + } + const result_elem = self.builder.buildCall(llvm_fn, args[0..], args_len, .C, .Auto, ""); + result = self.builder.buildInsertElement(result, result_elem, index_i32, ""); + } + return result; + } + + fn getLibcFunction( + self: *FuncGen, + fn_name: [:0]const u8, + param_types: []const *const llvm.Type, + return_type: *const llvm.Type, + ) *const llvm.Value { + return self.dg.object.llvm_module.getNamedFunction(fn_name.ptr) orelse b: { + const alias = self.dg.object.llvm_module.getNamedGlobalAlias(fn_name.ptr, fn_name.len); + break :b if (alias) |a| a.getAliasee() else null; + } orelse b: { + const params_len = @intCast(c_uint, param_types.len); + const fn_type = llvm.functionType(return_type, param_types.ptr, params_len, .False); + const f = self.dg.object.llvm_module.addFunction(fn_name, fn_type); + break :b f; + }; + } + + fn getMathHTypeAbbrev(ty: Type) []const u8 { + return switch (ty.tag()) { + .f16 => "h", // Non-standard + .f32 => "s", + .f64 => "", + .f80 => "x", // Non-standard + .c_longdouble => "l", + .f128 => "q", // Non-standard (mimics convention in GCC libquadmath) + else => unreachable, + }; + } + + fn getCompilerRtTypeAbbrev(ty: Type, target: std.Target) []const u8 { + return switch (ty.floatBits(target)) { + 16 => "h", + 32 => "s", + 64 => "d", + 80 => "x", + 128 => "t", + else => unreachable, + }; + } + + /// Creates a floating point comparison by lowering to the appropriate + /// hardware instruction or softfloat routine for the target + fn buildFloatCmp( + self: *FuncGen, + pred: math.CompareOperator, + ty: Type, + params: []const *const llvm.Value, + ) !*const llvm.Value { + const target = self.dg.module.getTarget(); + const scalar_ty = ty.scalarType(); + const scalar_llvm_ty = try self.dg.llvmType(scalar_ty); + + // LLVM does not support all floating point comparisons for all targets, so we + // may need to manually generate a libc call + const intrinsics_allowed = switch (scalar_ty.tag()) { + .f80 => target.longDoubleIs(f80) and backendSupportsF80(target), + .f128 => target.longDoubleIs(f128), + else => true, + }; + if (intrinsics_allowed) { + const llvm_predicate: llvm.RealPredicate = switch (pred) { + .eq => .OEQ, + .neq => .UNE, + .lt => .OLT, + .lte => .OLE, + .gt => .OGT, + .gte => .OGE, + }; + return self.builder.buildFCmp(llvm_predicate, params[0], params[1], ""); + } + + const compiler_rt_type_abbrev = getCompilerRtTypeAbbrev(scalar_ty, target); + var fn_name_buf: [64]u8 = undefined; + const fn_base_name = switch (pred) { + .neq => "ne", + .eq => "eq", + .lt => "lt", + .lte => "le", + .gt => "gt", + .gte => "ge", + }; + const fn_name = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f2", .{ fn_base_name, compiler_rt_type_abbrev }) catch unreachable; + + assert(params.len == 2); + const param_types = [2]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty }; + const llvm_i32 = self.context.intType(32); + const libc_fn = self.getLibcFunction(fn_name, param_types[0..], llvm_i32); + + const zero = llvm_i32.constInt(0, .False); + const int_pred: llvm.IntPredicate = switch (pred) { + .eq => .EQ, + .neq => .NE, + .lt => .SLT, + .lte => .SLE, + .gt => .SGT, + .gte => .SGE, + }; + + if (ty.zigTypeTag() == .Vector) { + const vec_len = ty.vectorLen(); + const vector_result_ty = llvm_i32.vectorType(vec_len); + + var result = vector_result_ty.getUndef(); + result = try self.buildElementwiseCall(libc_fn, params[0..], result, vec_len); + + const zero_vector = self.builder.buildVectorSplat(zero, vec_len, ""); + return self.builder.buildICmp(int_pred, result, zero_vector, ""); + } + + const result = self.builder.buildCall(libc_fn, params.ptr, 2, .C, .Auto, ""); + return self.builder.buildICmp(int_pred, result, zero, ""); + } + + /// Creates a floating point operation (add, sub, fma, sqrt, exp, etc.) + /// by lowering to the appropriate hardware instruction or softfloat + /// routine for the target + fn buildFloatOp( + self: *FuncGen, + comptime op: @TypeOf(.EnumLiteral), + ty: Type, + params: []const *const llvm.Value, + ) !*const llvm.Value { + const target = self.dg.module.getTarget(); + const scalar_ty = ty.scalarType(); + const llvm_ty = try self.dg.llvmType(ty); + const scalar_llvm_ty = try self.dg.llvmType(scalar_ty); + + const Strat = union(enum) { + intrinsic: []const u8, + libc: [:0]const u8, + }; + + // LLVM does not support all relevant intrinsics for all targets, so we + // may need to manually generate a libc call + const intrinsics_allowed = switch (scalar_ty.tag()) { + .f80 => target.longDoubleIs(f80) and backendSupportsF80(target), + .f128 => target.longDoubleIs(f128), + else => true, + }; + const strat: Strat = if (intrinsics_allowed) b: { + // Some operations are dedicated LLVM instructions, not available as intrinsics + switch (op) { + .add => return self.builder.buildFAdd(params[0], params[1], ""), + .sub => return self.builder.buildFSub(params[0], params[1], ""), + .mul => return self.builder.buildFMul(params[0], params[1], ""), + .div => return self.builder.buildFDiv(params[0], params[1], ""), + .rem => return self.builder.buildFRem(params[0], params[1], ""), + else => {}, + } + // All other operations are available as intrinsics + break :b .{ + .intrinsic = "llvm." ++ switch (op) { + .max => "maximum", + .min => "minimum", + .fma, .sqrt, .sin, .cos, .exp, .exp2, .log, .log2, .log10, .fabs, .floor, .ceil, .round, .trunc => @tagName(op), + .add, .sub, .mul, .div, .rem => unreachable, + else => unreachable, + }, + }; + } else b: { + const math_h_type_abbrev = getMathHTypeAbbrev(scalar_ty); + const compiler_rt_type_abbrev = getCompilerRtTypeAbbrev(scalar_ty, target); + var fn_name_buf: [64]u8 = undefined; + break :b switch (op) { + .fma => Strat{ + .libc = switch (scalar_ty.floatBits(target)) { + 80 => "__fmax", + else => std.fmt.bufPrintZ(&fn_name_buf, "fma{s}", .{math_h_type_abbrev}) catch unreachable, + }, + }, + .add, .sub, .div, .mul => Strat{ + .libc = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f3", .{ @tagName(op), compiler_rt_type_abbrev }) catch unreachable, + }, + .rem => Strat{ + .libc = std.fmt.bufPrintZ(&fn_name_buf, "fmod{s}", .{math_h_type_abbrev}) catch unreachable, + }, + .max, .min => Strat{ + .libc = std.fmt.bufPrintZ(&fn_name_buf, "f{s}{s}", .{ @tagName(op), math_h_type_abbrev }) catch unreachable, + }, + .sqrt, .sin, .cos, .exp, .exp2, .log, .log2, .log10, .fabs, .floor, .ceil, .round, .trunc => Strat{ + .libc = std.fmt.bufPrintZ(&fn_name_buf, "{s}{s}", .{ @tagName(op), math_h_type_abbrev }) catch unreachable, + }, + else => unreachable, + }; + }; + + var llvm_fn: *const llvm.Value = switch (strat) { + .intrinsic => |fn_name| self.getIntrinsic(fn_name, &.{llvm_ty}), + .libc => |fn_name| b: { + assert(params.len == switch (op) { + .fma => 3, + .add, .sub, .div, .mul, .rem, .max, .min => 2, + .sqrt, .sin, .cos, .exp, .exp2, .log, .log2, .log10, .fabs, .floor, .ceil, .round, .trunc => 1, + else => unreachable, + }); + const param_types = [3]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty, scalar_llvm_ty }; + const libc_fn = self.getLibcFunction(fn_name, param_types[0..params.len], scalar_llvm_ty); + if (ty.zigTypeTag() == .Vector) { + const result = llvm_ty.getUndef(); + return self.buildElementwiseCall(libc_fn, params[0..], result, ty.vectorLen()); + } + + break :b libc_fn; + }, + }; + const params_len = @intCast(c_uint, params.len); + return self.builder.buildCall(llvm_fn, params.ptr, params_len, .C, .Auto, ""); + } + fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; @@ -5519,64 +5745,7 @@ pub const FuncGen = struct { const addend = try self.resolveInst(pl_op.operand); const ty = self.air.typeOfIndex(inst); - const llvm_ty = try self.dg.llvmType(ty); - const scalar_ty = ty.scalarType(); - const target = self.dg.module.getTarget(); - - const Strat = union(enum) { - intrinsic, - libc: [*:0]const u8, - }; - - const strat: Strat = switch (scalar_ty.floatBits(target)) { - 16, 32, 64 => Strat.intrinsic, - 80 => if (CType.longdouble.sizeInBits(target) == 80) Strat{ .intrinsic = {} } else Strat{ .libc = "__fmax" }, - // LLVM always lowers the fma builtin for f128 to fmal, which is for `long double`. - // On some targets this will be correct; on others it will be incorrect. - 128 => if (CType.longdouble.sizeInBits(target) == 128) Strat{ .intrinsic = {} } else Strat{ .libc = "fmaq" }, - else => unreachable, - }; - - switch (strat) { - .intrinsic => { - const llvm_fn = self.getIntrinsic("llvm.fma", &.{llvm_ty}); - const params = [_]*const llvm.Value{ mulend1, mulend2, addend }; - return self.builder.buildCall(llvm_fn, ¶ms, params.len, .C, .Auto, ""); - }, - .libc => |fn_name| { - const scalar_llvm_ty = try self.dg.llvmType(scalar_ty); - const llvm_fn = self.dg.object.llvm_module.getNamedFunction(fn_name) orelse b: { - const param_types = [_]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty, scalar_llvm_ty }; - const fn_type = llvm.functionType(scalar_llvm_ty, ¶m_types, param_types.len, .False); - break :b self.dg.object.llvm_module.addFunction(fn_name, fn_type); - }; - - if (ty.zigTypeTag() == .Vector) { - const llvm_i32 = self.context.intType(32); - const vector_llvm_ty = try self.dg.llvmType(ty); - - var i: usize = 0; - var vector = vector_llvm_ty.getUndef(); - while (i < ty.vectorLen()) : (i += 1) { - const index_i32 = llvm_i32.constInt(i, .False); - - const mulend1_elem = self.builder.buildExtractElement(mulend1, index_i32, ""); - const mulend2_elem = self.builder.buildExtractElement(mulend2, index_i32, ""); - const addend_elem = self.builder.buildExtractElement(addend, index_i32, ""); - - const params = [_]*const llvm.Value{ mulend1_elem, mulend2_elem, addend_elem }; - const mul_add = self.builder.buildCall(llvm_fn, ¶ms, params.len, .C, .Auto, ""); - - vector = self.builder.buildInsertElement(vector, mul_add, index_i32, ""); - } - - return vector; - } else { - const params = [_]*const llvm.Value{ mulend1, mulend2, addend }; - return self.builder.buildCall(llvm_fn, ¶ms, params.len, .C, .Auto, ""); - } - }, - } + return self.buildFloatOp(.fma, ty, &.{ mulend1, mulend2, addend }); } fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { @@ -6381,14 +6550,15 @@ pub const FuncGen = struct { } } - fn airUnaryOp(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*const llvm.Value { + fn airUnaryOp(self: *FuncGen, inst: Air.Inst.Index, comptime op: @TypeOf(.EnumLiteral)) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); const operand_ty = self.air.typeOf(un_op); - return self.callFloatUnary(operand, operand_ty, llvm_fn_name); + const params = [_]*const llvm.Value{operand}; + return self.buildFloatOp(op, operand_ty, ¶ms); } fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*const llvm.Value { @@ -7191,48 +7361,6 @@ pub const FuncGen = struct { return self.builder.buildExtractValue(opt_handle, 0, ""); } - fn callFloor(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value { - return self.callFloatUnary(arg, ty, "floor"); - } - - fn callCeil(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value { - return self.callFloatUnary(arg, ty, "ceil"); - } - - fn callTrunc(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value { - return self.callFloatUnary(arg, ty, "trunc"); - } - - fn callFloatUnary( - self: *FuncGen, - arg: *const llvm.Value, - ty: Type, - name: []const u8, - ) !*const llvm.Value { - const target = self.dg.module.getTarget(); - - var fn_name_buf: [100]u8 = undefined; - const llvm_fn_name = switch (ty.zigTypeTag()) { - .Vector => std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.v{d}f{d}", .{ - name, ty.vectorLen(), ty.childType().floatBits(target), - }) catch unreachable, - .Float => std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.f{d}", .{ - name, ty.floatBits(target), - }) catch unreachable, - else => unreachable, - }; - - const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: { - const operand_llvm_ty = try self.dg.llvmType(ty); - const param_types = [_]*const llvm.Type{operand_llvm_ty}; - const fn_type = llvm.functionType(operand_llvm_ty, ¶m_types, param_types.len, .False); - break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type); - }; - - const args: [1]*const llvm.Value = .{arg}; - return self.builder.buildCall(llvm_fn, &args, args.len, .C, .Auto, ""); - } - fn fieldPtr( self: *FuncGen, inst: Air.Inst.Index, diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 3863385a06..81b5863aa0 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -295,6 +295,9 @@ pub const Type = opaque { pub const countStructElementTypes = LLVMCountStructElementTypes; extern fn LLVMCountStructElementTypes(StructTy: *const Type) c_uint; + + pub const getVectorSize = LLVMGetVectorSize; + extern fn LLVMGetVectorSize(VectorTy: *const Type) c_uint; }; pub const Module = opaque { @@ -675,6 +678,14 @@ pub const Builder = opaque { Name: [*:0]const u8, ) *const Value; + pub const buildVectorSplat = LLVMBuildVectorSplat; + extern fn LLVMBuildVectorSplat( + *const Builder, + EltVal: *const Value, + ElementCount: c_uint, + Name: [*:0]const u8, + ) *const Value; + pub const buildPtrToInt = LLVMBuildPtrToInt; extern fn LLVMBuildPtrToInt( *const Builder, diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 0b0b33b8d9..78082abf88 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -541,6 +541,10 @@ LLVMValueRef ZigLLVMBuildUShlSat(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRe return wrap(call_inst); } +LLVMValueRef LLVMBuildVectorSplat(LLVMBuilderRef B, unsigned elem_count, LLVMValueRef V, const char *Name) { + return wrap(unwrap(B)->CreateVectorSplat(elem_count, unwrap(V), Name)); +} + void ZigLLVMFnSetSubprogram(LLVMValueRef fn, ZigLLVMDISubprogram *subprogram) { assert( isa(unwrap(fn)) ); Function *unwrapped_function = reinterpret_cast(unwrap(fn)); diff --git a/src/zig_llvm.h b/src/zig_llvm.h index 63d184c417..90dcd1de39 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -149,6 +149,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildSMulFixSat(LLVMBuilderRef B, LLVMValueRef ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildUMulFixSat(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS, const char *name); ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildUShlSat(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name); ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildSShlSat(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name); +ZIG_EXTERN_C LLVMValueRef LLVMBuildVectorSplat(LLVMBuilderRef B, unsigned elem_count, LLVMValueRef V, const char *Name); ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, From 087aedfa38e0eb2a8f0e3055be18625017060b29 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Apr 2022 13:51:53 -0700 Subject: [PATCH 1248/2031] stage2: fix recent LLVM backend code * std.math.snan: fix compilation error. Also make it and nan inline. * LLVM: use a proper enum type for float op instead of enum literal. Also various cleanups. * LLVM: use LLVMBuildVectorSplat for vector splat AIR instruction. - also the bindings had parameter order wrong * LLVM: additionally handle f16 lowering. For now all targets report OK but I think we will need to add some exceptions to this list. --- lib/std/math/nan.zig | 17 +-- src/codegen/llvm.zig | 260 ++++++++++++++++++---------------- src/codegen/llvm/bindings.zig | 5 +- 3 files changed, 147 insertions(+), 135 deletions(-) diff --git a/lib/std/math/nan.zig b/lib/std/math/nan.zig index 329f67b74e..8a27937242 100644 --- a/lib/std/math/nan.zig +++ b/lib/std/math/nan.zig @@ -1,7 +1,7 @@ const math = @import("../math.zig"); /// Returns the nan representation for type T. -pub fn nan(comptime T: type) T { +pub inline fn nan(comptime T: type) T { return switch (@typeInfo(T).Float.bits) { 16 => math.nan_f16, 32 => math.nan_f32, @@ -13,15 +13,8 @@ pub fn nan(comptime T: type) T { } /// Returns the signalling nan representation for type T. -pub fn snan(comptime T: type) T { - // Note: A signalling nan is identical to a standard right now by may have a different bit - // representation in the future when required. - return switch (@typeInfo(T).Float.bits) { - 16 => math.nan_u16, - 32 => math.nan_u32, - 64 => math.nan_u64, - 80 => math.nan_u80, - 128 => math.nan_u128, - else => @compileError("unreachable"), - }; +/// Note: A signalling nan is identical to a standard right now by may have a different bit +/// representation in the future when required. +pub inline fn snan(comptime T: type) T { + return nan(T); } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index bf7f4e2ce1..de25a7821d 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3983,7 +3983,7 @@ pub const FuncGen = struct { ); return phi_node; }, - .Float => return self.buildFloatCmp(op, operand_ty, &.{ lhs, rhs }), + .Float => return self.buildFloatCmp(op, operand_ty, .{ lhs, rhs }), else => unreachable, }; const is_signed = int_ty.isSignedInt(); @@ -5211,7 +5211,7 @@ pub const FuncGen = struct { const inst_ty = self.air.typeOfIndex(inst); const scalar_ty = inst_ty.scalarType(); - if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.add, inst_ty, &.{ lhs, rhs }); + if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.add, inst_ty, 2, .{ lhs, rhs }); if (scalar_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, ""); return self.builder.buildNUWAdd(lhs, rhs, ""); } @@ -5250,7 +5250,7 @@ pub const FuncGen = struct { const inst_ty = self.air.typeOfIndex(inst); const scalar_ty = inst_ty.scalarType(); - if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.sub, inst_ty, &.{ lhs, rhs }); + if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.sub, inst_ty, 2, .{ lhs, rhs }); if (scalar_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, ""); return self.builder.buildNUWSub(lhs, rhs, ""); } @@ -5288,7 +5288,7 @@ pub const FuncGen = struct { const inst_ty = self.air.typeOfIndex(inst); const scalar_ty = inst_ty.scalarType(); - if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.mul, inst_ty, &.{ lhs, rhs }); + if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.mul, inst_ty, 2, .{ lhs, rhs }); if (scalar_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, ""); return self.builder.buildNUWMul(lhs, rhs, ""); } @@ -5325,7 +5325,7 @@ pub const FuncGen = struct { const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); - return self.buildFloatOp(.div, inst_ty, &.{ lhs, rhs }); + return self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs }); } fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { @@ -5338,8 +5338,8 @@ pub const FuncGen = struct { const scalar_ty = inst_ty.scalarType(); if (scalar_ty.isRuntimeFloat()) { - const result = try self.buildFloatOp(.div, inst_ty, &.{ lhs, rhs }); - return self.buildFloatOp(.trunc, inst_ty, &.{result}); + const result = try self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs }); + return self.buildFloatOp(.trunc, inst_ty, 1, .{result}); } if (scalar_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, ""); return self.builder.buildUDiv(lhs, rhs, ""); @@ -5355,8 +5355,8 @@ pub const FuncGen = struct { const scalar_ty = inst_ty.scalarType(); if (scalar_ty.isRuntimeFloat()) { - const result = try self.buildFloatOp(.div, inst_ty, &.{ lhs, rhs }); - return self.buildFloatOp(.floor, inst_ty, &.{result}); + const result = try self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs }); + return self.buildFloatOp(.floor, inst_ty, 1, .{result}); } if (scalar_ty.isSignedInt()) { // const d = @divTrunc(a, b); @@ -5386,7 +5386,7 @@ pub const FuncGen = struct { const inst_ty = self.air.typeOfIndex(inst); const scalar_ty = inst_ty.scalarType(); - if (scalar_ty.isRuntimeFloat()) return self.buildFloatOp(.div, inst_ty, &.{ lhs, rhs }); + if (scalar_ty.isRuntimeFloat()) return self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs }); if (scalar_ty.isSignedInt()) return self.builder.buildExactSDiv(lhs, rhs, ""); return self.builder.buildExactUDiv(lhs, rhs, ""); } @@ -5400,7 +5400,7 @@ pub const FuncGen = struct { const inst_ty = self.air.typeOfIndex(inst); const scalar_ty = inst_ty.scalarType(); - if (scalar_ty.isRuntimeFloat()) return self.buildFloatOp(.rem, inst_ty, &.{ lhs, rhs }); + if (scalar_ty.isRuntimeFloat()) return self.buildFloatOp(.fmod, inst_ty, 2, .{ lhs, rhs }); if (scalar_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, ""); return self.builder.buildURem(lhs, rhs, ""); } @@ -5416,11 +5416,11 @@ pub const FuncGen = struct { const scalar_ty = inst_ty.scalarType(); if (scalar_ty.isRuntimeFloat()) { - const a = try self.buildFloatOp(.rem, inst_ty, &.{ lhs, rhs }); - const b = try self.buildFloatOp(.add, inst_ty, &.{ a, rhs }); - const c = try self.buildFloatOp(.rem, inst_ty, &.{ b, rhs }); + const a = try self.buildFloatOp(.fmod, inst_ty, 2, .{ lhs, rhs }); + const b = try self.buildFloatOp(.add, inst_ty, 2, .{ a, rhs }); + const c = try self.buildFloatOp(.fmod, inst_ty, 2, .{ b, rhs }); const zero = inst_llvm_ty.constNull(); - const ltz = try self.buildFloatCmp(.lt, inst_ty, &.{ lhs, zero }); + const ltz = try self.buildFloatCmp(.lt, inst_ty, .{ lhs, zero }); return self.builder.buildSelect(ltz, c, a, ""); } if (scalar_ty.isSignedInt()) { @@ -5508,18 +5508,18 @@ pub const FuncGen = struct { ) !*const llvm.Value { const args_len = @intCast(c_uint, args_vectors.len); const llvm_i32 = self.context.intType(32); - assert(args_len <= 8); + assert(args_len <= 3); var i: usize = 0; var result = result_vector; while (i < vector_len) : (i += 1) { const index_i32 = llvm_i32.constInt(i, .False); - var args: [8]*const llvm.Value = undefined; + var args: [3]*const llvm.Value = undefined; for (args_vectors) |arg_vector, k| { args[k] = self.builder.buildExtractElement(arg_vector, index_i32, ""); } - const result_elem = self.builder.buildCall(llvm_fn, args[0..], args_len, .C, .Auto, ""); + const result_elem = self.builder.buildCall(llvm_fn, &args, args_len, .C, .Auto, ""); result = self.builder.buildInsertElement(result, result_elem, index_i32, ""); } return result; @@ -5542,20 +5542,27 @@ pub const FuncGen = struct { }; } - fn getMathHTypeAbbrev(ty: Type) []const u8 { - return switch (ty.tag()) { - .f16 => "h", // Non-standard - .f32 => "s", - .f64 => "", - .f80 => "x", // Non-standard - .c_longdouble => "l", - .f128 => "q", // Non-standard (mimics convention in GCC libquadmath) + fn libcFloatPrefix(float_bits: u16) []const u8 { + return switch (float_bits) { + 16, 80 => "__", + 32, 64, 128 => "", else => unreachable, }; } - fn getCompilerRtTypeAbbrev(ty: Type, target: std.Target) []const u8 { - return switch (ty.floatBits(target)) { + fn libcFloatSuffix(float_bits: u16) []const u8 { + return switch (float_bits) { + 16 => "h", // Non-standard + 32 => "s", + 64 => "", + 80 => "x", // Non-standard + 128 => "q", // Non-standard (mimics convention in GCC libquadmath) + else => unreachable, + }; + } + + fn compilerRtFloatAbbrev(float_bits: u16) []const u8 { + return switch (float_bits) { 16 => "h", 32 => "s", 64 => "d", @@ -5571,20 +5578,13 @@ pub const FuncGen = struct { self: *FuncGen, pred: math.CompareOperator, ty: Type, - params: []const *const llvm.Value, + params: [2]*const llvm.Value, ) !*const llvm.Value { const target = self.dg.module.getTarget(); const scalar_ty = ty.scalarType(); const scalar_llvm_ty = try self.dg.llvmType(scalar_ty); - // LLVM does not support all floating point comparisons for all targets, so we - // may need to manually generate a libc call - const intrinsics_allowed = switch (scalar_ty.tag()) { - .f80 => target.longDoubleIs(f80) and backendSupportsF80(target), - .f128 => target.longDoubleIs(f128), - else => true, - }; - if (intrinsics_allowed) { + if (intrinsicsAllowed(scalar_ty, target)) { const llvm_predicate: llvm.RealPredicate = switch (pred) { .eq => .OEQ, .neq => .UNE, @@ -5596,7 +5596,8 @@ pub const FuncGen = struct { return self.builder.buildFCmp(llvm_predicate, params[0], params[1], ""); } - const compiler_rt_type_abbrev = getCompilerRtTypeAbbrev(scalar_ty, target); + const float_bits = scalar_ty.floatBits(target); + const compiler_rt_float_abbrev = compilerRtFloatAbbrev(float_bits); var fn_name_buf: [64]u8 = undefined; const fn_base_name = switch (pred) { .neq => "ne", @@ -5606,9 +5607,10 @@ pub const FuncGen = struct { .gt => "gt", .gte => "ge", }; - const fn_name = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f2", .{ fn_base_name, compiler_rt_type_abbrev }) catch unreachable; + const fn_name = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f2", .{ + fn_base_name, compiler_rt_float_abbrev, + }) catch unreachable; - assert(params.len == 2); const param_types = [2]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty }; const llvm_i32 = self.context.intType(32); const libc_fn = self.getLibcFunction(fn_name, param_types[0..], llvm_i32); @@ -5628,110 +5630,119 @@ pub const FuncGen = struct { const vector_result_ty = llvm_i32.vectorType(vec_len); var result = vector_result_ty.getUndef(); - result = try self.buildElementwiseCall(libc_fn, params[0..], result, vec_len); + result = try self.buildElementwiseCall(libc_fn, ¶ms, result, vec_len); - const zero_vector = self.builder.buildVectorSplat(zero, vec_len, ""); + const zero_vector = self.builder.buildVectorSplat(vec_len, zero, ""); return self.builder.buildICmp(int_pred, result, zero_vector, ""); } - const result = self.builder.buildCall(libc_fn, params.ptr, 2, .C, .Auto, ""); + const result = self.builder.buildCall(libc_fn, ¶ms, params.len, .C, .Auto, ""); return self.builder.buildICmp(int_pred, result, zero, ""); } + const FloatOp = enum { + add, + ceil, + cos, + div, + exp, + exp2, + fabs, + floor, + fma, + log, + log10, + log2, + fmax, + fmin, + mul, + fmod, + round, + sin, + sqrt, + sub, + trunc, + }; + + const FloatOpStrat = union(enum) { + intrinsic: []const u8, + libc: [:0]const u8, + }; + /// Creates a floating point operation (add, sub, fma, sqrt, exp, etc.) /// by lowering to the appropriate hardware instruction or softfloat /// routine for the target fn buildFloatOp( self: *FuncGen, - comptime op: @TypeOf(.EnumLiteral), + comptime op: FloatOp, ty: Type, - params: []const *const llvm.Value, + comptime params_len: usize, + params: [params_len]*const llvm.Value, ) !*const llvm.Value { const target = self.dg.module.getTarget(); const scalar_ty = ty.scalarType(); const llvm_ty = try self.dg.llvmType(ty); const scalar_llvm_ty = try self.dg.llvmType(scalar_ty); - const Strat = union(enum) { - intrinsic: []const u8, - libc: [:0]const u8, - }; - - // LLVM does not support all relevant intrinsics for all targets, so we - // may need to manually generate a libc call - const intrinsics_allowed = switch (scalar_ty.tag()) { - .f80 => target.longDoubleIs(f80) and backendSupportsF80(target), - .f128 => target.longDoubleIs(f128), - else => true, - }; - const strat: Strat = if (intrinsics_allowed) b: { + const intrinsics_allowed = intrinsicsAllowed(scalar_ty, target); + var fn_name_buf: [64]u8 = undefined; + const strat: FloatOpStrat = if (intrinsics_allowed) switch (op) { // Some operations are dedicated LLVM instructions, not available as intrinsics - switch (op) { - .add => return self.builder.buildFAdd(params[0], params[1], ""), - .sub => return self.builder.buildFSub(params[0], params[1], ""), - .mul => return self.builder.buildFMul(params[0], params[1], ""), - .div => return self.builder.buildFDiv(params[0], params[1], ""), - .rem => return self.builder.buildFRem(params[0], params[1], ""), - else => {}, - } - // All other operations are available as intrinsics - break :b .{ - .intrinsic = "llvm." ++ switch (op) { - .max => "maximum", - .min => "minimum", - .fma, .sqrt, .sin, .cos, .exp, .exp2, .log, .log2, .log10, .fabs, .floor, .ceil, .round, .trunc => @tagName(op), - .add, .sub, .mul, .div, .rem => unreachable, - else => unreachable, - }, - }; + .add => return self.builder.buildFAdd(params[0], params[1], ""), + .sub => return self.builder.buildFSub(params[0], params[1], ""), + .mul => return self.builder.buildFMul(params[0], params[1], ""), + .div => return self.builder.buildFDiv(params[0], params[1], ""), + .fmod => return self.builder.buildFRem(params[0], params[1], ""), + .fmax => return self.builder.buildMaxNum(params[0], params[1], ""), + .fmin => return self.builder.buildMinNum(params[0], params[1], ""), + else => .{ .intrinsic = "llvm." ++ @tagName(op) }, } else b: { - const math_h_type_abbrev = getMathHTypeAbbrev(scalar_ty); - const compiler_rt_type_abbrev = getCompilerRtTypeAbbrev(scalar_ty, target); - var fn_name_buf: [64]u8 = undefined; + const float_bits = scalar_ty.floatBits(target); break :b switch (op) { - .fma => Strat{ - .libc = switch (scalar_ty.floatBits(target)) { - 80 => "__fmax", - else => std.fmt.bufPrintZ(&fn_name_buf, "fma{s}", .{math_h_type_abbrev}) catch unreachable, - }, + .add, .sub, .div, .mul => FloatOpStrat{ + .libc = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f3", .{ + @tagName(op), compilerRtFloatAbbrev(float_bits), + }) catch unreachable, }, - .add, .sub, .div, .mul => Strat{ - .libc = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f3", .{ @tagName(op), compiler_rt_type_abbrev }) catch unreachable, + .ceil, + .cos, + .exp, + .exp2, + .fabs, + .floor, + .fma, + .fmax, + .fmin, + .fmod, + .log, + .log10, + .log2, + .round, + .sin, + .sqrt, + .trunc, + => FloatOpStrat{ + .libc = std.fmt.bufPrintZ(&fn_name_buf, "{s}{s}{s}", .{ + libcFloatPrefix(float_bits), @tagName(op), libcFloatSuffix(float_bits), + }) catch unreachable, }, - .rem => Strat{ - .libc = std.fmt.bufPrintZ(&fn_name_buf, "fmod{s}", .{math_h_type_abbrev}) catch unreachable, - }, - .max, .min => Strat{ - .libc = std.fmt.bufPrintZ(&fn_name_buf, "f{s}{s}", .{ @tagName(op), math_h_type_abbrev }) catch unreachable, - }, - .sqrt, .sin, .cos, .exp, .exp2, .log, .log2, .log10, .fabs, .floor, .ceil, .round, .trunc => Strat{ - .libc = std.fmt.bufPrintZ(&fn_name_buf, "{s}{s}", .{ @tagName(op), math_h_type_abbrev }) catch unreachable, - }, - else => unreachable, }; }; - var llvm_fn: *const llvm.Value = switch (strat) { + const llvm_fn: *const llvm.Value = switch (strat) { .intrinsic => |fn_name| self.getIntrinsic(fn_name, &.{llvm_ty}), .libc => |fn_name| b: { - assert(params.len == switch (op) { - .fma => 3, - .add, .sub, .div, .mul, .rem, .max, .min => 2, - .sqrt, .sin, .cos, .exp, .exp2, .log, .log2, .log10, .fabs, .floor, .ceil, .round, .trunc => 1, - else => unreachable, - }); const param_types = [3]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty, scalar_llvm_ty }; const libc_fn = self.getLibcFunction(fn_name, param_types[0..params.len], scalar_llvm_ty); if (ty.zigTypeTag() == .Vector) { const result = llvm_ty.getUndef(); - return self.buildElementwiseCall(libc_fn, params[0..], result, ty.vectorLen()); + return self.buildElementwiseCall(libc_fn, ¶ms, result, ty.vectorLen()); } break :b libc_fn; }, }; - const params_len = @intCast(c_uint, params.len); - return self.builder.buildCall(llvm_fn, params.ptr, params_len, .C, .Auto, ""); + return self.builder.buildCall(llvm_fn, ¶ms, params_len, .C, .Auto, ""); } fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { @@ -5745,7 +5756,7 @@ pub const FuncGen = struct { const addend = try self.resolveInst(pl_op.operand); const ty = self.air.typeOfIndex(inst); - return self.buildFloatOp(.fma, ty, &.{ mulend1, mulend2, addend }); + return self.buildFloatOp(.fma, ty, 3, .{ mulend1, mulend2, addend }); } fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { @@ -6550,15 +6561,14 @@ pub const FuncGen = struct { } } - fn airUnaryOp(self: *FuncGen, inst: Air.Inst.Index, comptime op: @TypeOf(.EnumLiteral)) !?*const llvm.Value { + fn airUnaryOp(self: *FuncGen, inst: Air.Inst.Index, comptime op: FloatOp) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); const operand_ty = self.air.typeOf(un_op); - const params = [_]*const llvm.Value{operand}; - return self.buildFloatOp(op, operand_ty, ¶ms); + return self.buildFloatOp(op, operand_ty, 1, .{operand}); } fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*const llvm.Value { @@ -6822,17 +6832,9 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const scalar = try self.resolveInst(ty_op.operand); - const scalar_ty = self.air.typeOf(ty_op.operand); const vector_ty = self.air.typeOfIndex(inst); const len = vector_ty.vectorLen(); - const scalar_llvm_ty = try self.dg.llvmType(scalar_ty); - const op_llvm_ty = scalar_llvm_ty.vectorType(1); - const u32_llvm_ty = self.context.intType(32); - const mask_llvm_ty = u32_llvm_ty.vectorType(len); - const undef_vector = op_llvm_ty.getUndef(); - const u32_zero = u32_llvm_ty.constNull(); - const op_vector = self.builder.buildInsertElement(undef_vector, scalar, u32_zero, ""); - return self.builder.buildShuffleVector(op_vector, undef_vector, mask_llvm_ty.constNull(), ""); + return self.builder.buildVectorSplat(len, scalar, ""); } fn airSelect(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { @@ -8183,6 +8185,26 @@ fn backendSupportsF80(target: std.Target) bool { }; } +/// This function returns true if we expect LLVM to lower f16 correctly +/// and false if we expect LLVM to crash if it counters an f16 type or +/// if it produces miscompilations. +fn backendSupportsF16(target: std.Target) bool { + return switch (target.cpu.arch) { + else => true, + }; +} + +/// LLVM does not support all relevant intrinsics for all targets, so we +/// may need to manually generate a libc call +fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool { + return switch (scalar_ty.tag()) { + .f16 => backendSupportsF16(target), + .f80 => target.longDoubleIs(f80) and backendSupportsF80(target), + .f128 => target.longDoubleIs(f128), + else => true, + }; +} + /// We need to insert extra padding if LLVM's isn't enough. /// However we don't want to ever call LLVMABIAlignmentOfType or /// LLVMABISizeOfType because these functions will trip assertions diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 81b5863aa0..b8dc3e1830 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -295,9 +295,6 @@ pub const Type = opaque { pub const countStructElementTypes = LLVMCountStructElementTypes; extern fn LLVMCountStructElementTypes(StructTy: *const Type) c_uint; - - pub const getVectorSize = LLVMGetVectorSize; - extern fn LLVMGetVectorSize(VectorTy: *const Type) c_uint; }; pub const Module = opaque { @@ -681,8 +678,8 @@ pub const Builder = opaque { pub const buildVectorSplat = LLVMBuildVectorSplat; extern fn LLVMBuildVectorSplat( *const Builder, - EltVal: *const Value, ElementCount: c_uint, + EltVal: *const Value, Name: [*:0]const u8, ) *const Value; From c4eaff6665132287d05272bef8890e4607ff017c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Apr 2022 14:29:59 -0700 Subject: [PATCH 1249/2031] disable failing behavior tests --- test/behavior/math.zig | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/test/behavior/math.zig b/test/behavior/math.zig index d15121e89b..942d5f8ca7 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1354,6 +1354,12 @@ test "comptime sin and ln" { } test "fabs" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| { // normals try expect(@fabs(@as(T, 1.0)) == 1.0); @@ -1377,10 +1383,19 @@ test "fabs" { } test "absFloat" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + try testAbsFloat(); comptime try testAbsFloat(); } fn testAbsFloat() !void { - try expect(@fabs(@as(f32, -10.05)) == @as(f32, 10.05)); - try expect(@fabs(@as(f32, 10.05)) == @as(f32, 10.05)); + try testAbsFloatOne(-10.05, 10.05); + try testAbsFloatOne(10.05, 10.05); +} +fn testAbsFloatOne(in: f32, out: f32) !void { + try expect(@fabs(@as(f32, in)) == @as(f32, out)); } From 09f1d62bdfb5794534b21d1cd9dafc4822697d60 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Apr 2022 16:45:23 -0700 Subject: [PATCH 1250/2031] add new builtin function `@tan` The reason for having `@tan` is that we already have `@sin` and `@cos` because some targets have machine code instructions for them, but in the case that the implementation needs to go into compiler-rt, sin, cos, and tan all share a common dependency which includes a table of data. To avoid duplicating this table of data, we promote tan to become a builtin alongside sin and cos. ZIR: The tag enum is at capacity so this commit moves `field_call_bind_named` to be `extended`. I measured this as one of the least used tags in the zig codebase. Fix libc math suffix for `f32` being wrong in both stage1 and stage2. stage1: add missing libc prefix for float functions. --- doc/langref.html.in | 16 +++++++- lib/std/math/complex/tanh.zig | 4 +- src/Air.zig | 8 +++- src/AstGen.zig | 6 ++- src/BuiltinFn.zig | 8 ++++ src/Liveness.zig | 1 + src/Sema.zig | 70 +++++++++++++++++------------------ src/Zir.zig | 33 +++++++++++------ src/arch/aarch64/CodeGen.zig | 1 + src/arch/arm/CodeGen.zig | 1 + src/arch/riscv64/CodeGen.zig | 1 + src/arch/sparcv9/CodeGen.zig | 1 + src/arch/wasm/CodeGen.zig | 1 + src/arch/x86_64/CodeGen.zig | 1 + src/codegen/c.zig | 1 + src/codegen/llvm.zig | 7 +++- src/print_air.zig | 1 + src/print_zir.zig | 12 +++++- src/stage1/all_types.hpp | 1 + src/stage1/analyze.cpp | 2 + src/stage1/astgen.cpp | 1 + src/stage1/codegen.cpp | 41 +++++++++++++++----- src/stage1/ir.cpp | 11 ++++++ src/value.zig | 38 +++++++++++++++++++ test/behavior/bugs/920.zig | 5 +-- 25 files changed, 203 insertions(+), 69 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 5cccced446..3c5de6c8d2 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -8026,7 +8026,7 @@ fn func(y: *i32) void { only rounds once, and is thus more accurate.

- Supports Floats and Vectors of floats. + Supports {#link|Floats#} and {#link|Vectors#} of floats.

{#header_close#} @@ -9440,6 +9440,7 @@ fn doTheTest() !void { some float operations are not yet implemented for all float types.

{#header_close#} + {#header_open|@cos#}
{#syntax#}@cos(value: anytype) @TypeOf(value){#endsyntax#}

@@ -9451,6 +9452,19 @@ fn doTheTest() !void { some float operations are not yet implemented for all float types.

{#header_close#} + + {#header_open|@tan#} +
{#syntax#}@tan(value: anytype) @TypeOf(value){#endsyntax#}
+

+ Tangent trigonometric function on a floating point number. + Uses a dedicated hardware instruction when available. +

+

+ Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that + some float operations are not yet implemented for all float types. +

+ {#header_close#} + {#header_open|@exp#}
{#syntax#}@exp(value: anytype) @TypeOf(value){#endsyntax#}

diff --git a/lib/std/math/complex/tanh.zig b/lib/std/math/complex/tanh.zig index e61ec1e95b..d5195d6c73 100644 --- a/lib/std/math/complex/tanh.zig +++ b/lib/std/math/complex/tanh.zig @@ -49,7 +49,7 @@ fn tanh32(z: Complex(f32)) Complex(f32) { } // Kahan's algorithm - const t = math.tan(y); + const t = @tan(y); const beta = 1.0 + t * t; const s = math.sinh(x); const rho = @sqrt(1 + s * s); @@ -92,7 +92,7 @@ fn tanh64(z: Complex(f64)) Complex(f64) { } // Kahan's algorithm - const t = math.tan(y); + const t = @tan(y); const beta = 1.0 + t * t; const s = math.sinh(x); const rho = @sqrt(1 + s * s); diff --git a/src/Air.zig b/src/Air.zig index d02491ff89..0968d95180 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -249,12 +249,15 @@ pub const Inst = struct { /// Square root of a floating point number. /// Uses the `un_op` field. sqrt, - /// Sine a floating point number. + /// Sine function on a floating point number. /// Uses the `un_op` field. sin, - /// Cosine a floating point number. + /// Cosine function on a floating point number. /// Uses the `un_op` field. cos, + /// Tangent function on a floating point number. + /// Uses the `un_op` field. + tan, /// Base e exponential of a floating point number. /// Uses the `un_op` field. exp, @@ -921,6 +924,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .sqrt, .sin, .cos, + .tan, .exp, .exp2, .log, diff --git a/src/AstGen.zig b/src/AstGen.zig index 34b29b28fb..230b46a489 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2237,7 +2237,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .field_call_bind, .field_ptr_named, .field_val_named, - .field_call_bind_named, .func, .func_inferred, .int, @@ -2329,6 +2328,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .sqrt, .sin, .cos, + .tan, .exp, .exp2, .log, @@ -7259,6 +7259,7 @@ fn builtinCall( .sqrt => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sqrt), .sin => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sin), .cos => return simpleUnOp(gz, scope, rl, node, .none, params[0], .cos), + .tan => return simpleUnOp(gz, scope, rl, node, .none, params[0], .tan), .exp => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp), .exp2 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp2), .log => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log), @@ -7947,7 +7948,8 @@ fn calleeExpr( if (std.mem.eql(u8, builtin_name, "@field") and params.len == 2) { const lhs = try expr(gz, scope, .ref, params[0]); const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]); - return gz.addPlNode(.field_call_bind_named, node, Zir.Inst.FieldNamed{ + return gz.addExtendedPayload(.field_call_bind_named, Zir.Inst.FieldNamedNode{ + .node = gz.nodeIndexToRelative(node), .lhs = lhs, .field_name = field_name, }); diff --git a/src/BuiltinFn.zig b/src/BuiltinFn.zig index 3bf7224fab..04cad19354 100644 --- a/src/BuiltinFn.zig +++ b/src/BuiltinFn.zig @@ -89,6 +89,7 @@ pub const Tag = enum { sqrt, sin, cos, + tan, exp, exp2, log, @@ -771,6 +772,13 @@ pub const list = list: { .param_count = 1, }, }, + .{ + "@tan", + .{ + .tag = .tan, + .param_count = 1, + }, + }, .{ "@exp", .{ diff --git a/src/Liveness.zig b/src/Liveness.zig index be4344ab90..e606c15b4b 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -422,6 +422,7 @@ fn analyzeInst( .sqrt, .sin, .cos, + .tan, .exp, .exp2, .log, diff --git a/src/Sema.zig b/src/Sema.zig index 5d1d51b58f..3fa0353e9d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -743,7 +743,6 @@ fn analyzeBodyInner( .field_val => try sema.zirFieldVal(block, inst), .field_val_named => try sema.zirFieldValNamed(block, inst), .field_call_bind => try sema.zirFieldCallBind(block, inst), - .field_call_bind_named => try sema.zirFieldCallBindNamed(block, inst), .func => try sema.zirFunc(block, inst, false), .func_inferred => try sema.zirFunc(block, inst, true), .import => try sema.zirImport(block, inst), @@ -855,6 +854,7 @@ fn analyzeBodyInner( .sqrt => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt), .sin => try sema.zirUnaryMath(block, inst, .sin, Value.sin), .cos => try sema.zirUnaryMath(block, inst, .cos, Value.cos), + .tan => try sema.zirUnaryMath(block, inst, .tan, Value.tan), .exp => try sema.zirUnaryMath(block, inst, .exp, Value.exp), .exp2 => try sema.zirUnaryMath(block, inst, .exp2, Value.exp2), .log => try sema.zirUnaryMath(block, inst, .log, Value.log), @@ -910,35 +910,36 @@ fn analyzeBodyInner( const extended = datas[inst].extended; break :ext switch (extended.opcode) { // zig fmt: off - .func => try sema.zirFuncExtended( block, extended, inst), - .variable => try sema.zirVarExtended( block, extended), - .struct_decl => try sema.zirStructDecl( block, extended, inst), - .enum_decl => try sema.zirEnumDecl( block, extended), - .union_decl => try sema.zirUnionDecl( block, extended, inst), - .opaque_decl => try sema.zirOpaqueDecl( block, extended), - .ret_ptr => try sema.zirRetPtr( block, extended), - .ret_type => try sema.zirRetType( block, extended), - .this => try sema.zirThis( block, extended), - .ret_addr => try sema.zirRetAddr( block, extended), - .builtin_src => try sema.zirBuiltinSrc( block, extended), - .error_return_trace => try sema.zirErrorReturnTrace( block, extended), - .frame => try sema.zirFrame( block, extended), - .frame_address => try sema.zirFrameAddress( block, extended), - .alloc => try sema.zirAllocExtended( block, extended), - .builtin_extern => try sema.zirBuiltinExtern( block, extended), - .@"asm" => try sema.zirAsm( block, extended), - .typeof_peer => try sema.zirTypeofPeer( block, extended), - .compile_log => try sema.zirCompileLog( block, extended), - .add_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), - .sub_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), - .mul_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), - .shl_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), - .c_undef => try sema.zirCUndef( block, extended), - .c_include => try sema.zirCInclude( block, extended), - .c_define => try sema.zirCDefine( block, extended), - .wasm_memory_size => try sema.zirWasmMemorySize( block, extended), - .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended), - .prefetch => try sema.zirPrefetch( block, extended), + .func => try sema.zirFuncExtended( block, extended, inst), + .variable => try sema.zirVarExtended( block, extended), + .struct_decl => try sema.zirStructDecl( block, extended, inst), + .enum_decl => try sema.zirEnumDecl( block, extended), + .union_decl => try sema.zirUnionDecl( block, extended, inst), + .opaque_decl => try sema.zirOpaqueDecl( block, extended), + .ret_ptr => try sema.zirRetPtr( block, extended), + .ret_type => try sema.zirRetType( block, extended), + .this => try sema.zirThis( block, extended), + .ret_addr => try sema.zirRetAddr( block, extended), + .builtin_src => try sema.zirBuiltinSrc( block, extended), + .error_return_trace => try sema.zirErrorReturnTrace( block, extended), + .frame => try sema.zirFrame( block, extended), + .frame_address => try sema.zirFrameAddress( block, extended), + .alloc => try sema.zirAllocExtended( block, extended), + .builtin_extern => try sema.zirBuiltinExtern( block, extended), + .@"asm" => try sema.zirAsm( block, extended), + .typeof_peer => try sema.zirTypeofPeer( block, extended), + .compile_log => try sema.zirCompileLog( block, extended), + .add_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), + .sub_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), + .mul_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), + .shl_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), + .c_undef => try sema.zirCUndef( block, extended), + .c_include => try sema.zirCInclude( block, extended), + .c_define => try sema.zirCDefine( block, extended), + .wasm_memory_size => try sema.zirWasmMemorySize( block, extended), + .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended), + .prefetch => try sema.zirPrefetch( block, extended), + .field_call_bind_named => try sema.zirFieldCallBindNamed(block, extended), // zig fmt: on .dbg_block_begin => { dbg_block_begins += 1; @@ -6938,14 +6939,13 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src); } -fn zirFieldCallBindNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { +fn zirFieldCallBindNamed(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; - const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; + const extra = sema.code.extraData(Zir.Inst.FieldNamedNode, extended.operand).data; + const src: LazySrcLoc = .{ .node_offset = extra.node }; + const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; const object_ptr = sema.resolveInst(extra.lhs); const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); return sema.fieldCallBind(block, src, object_ptr, field_name, field_name_src); diff --git a/src/Zir.zig b/src/Zir.zig index 8fe5276792..f4c62a6f24 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -407,15 +407,6 @@ pub const Inst = struct { /// The field name is a comptime instruction. Used by @field. /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed. field_val_named, - /// Given a pointer to a struct or object that contains virtual fields, returns the - /// named field. If there is no named field, searches in the type for a decl that - /// matches the field name. The decl is resolved and we ensure that it's a function - /// which can accept the object as the first parameter, with one pointer fixup. If - /// all of that works, this instruction produces a special "bound function" value - /// which contains both the function and the saved first parameter value. - /// Bound functions may only be used as the function parameter to a `call` or - /// `builtin_call` instruction. Any other use is invalid zir and may crash the compiler. - field_call_bind_named, /// Returns a function type, or a function instance, depending on whether /// the body_len is 0. Calling convention is auto. /// Uses the `pl_node` union field. `payload_index` points to a `Func`. @@ -797,6 +788,8 @@ pub const Inst = struct { sin, /// Implement builtin `@cos`. Uses `un_node`. cos, + /// Implement builtin `@tan`. Uses `un_node`. + tan, /// Implement builtin `@exp`. Uses `un_node`. exp, /// Implement builtin `@exp2`. Uses `un_node`. @@ -1069,7 +1062,6 @@ pub const Inst = struct { .field_call_bind, .field_ptr_named, .field_val_named, - .field_call_bind_named, .func, .func_inferred, .has_decl, @@ -1179,6 +1171,7 @@ pub const Inst = struct { .sqrt, .sin, .cos, + .tan, .exp, .exp2, .log, @@ -1358,7 +1351,6 @@ pub const Inst = struct { .field_call_bind, .field_ptr_named, .field_val_named, - .field_call_bind_named, .func, .func_inferred, .has_decl, @@ -1451,6 +1443,7 @@ pub const Inst = struct { .sqrt, .sin, .cos, + .tan, .exp, .exp2, .log, @@ -1607,7 +1600,6 @@ pub const Inst = struct { .field_ptr_named = .pl_node, .field_val_named = .pl_node, .field_call_bind = .pl_node, - .field_call_bind_named = .pl_node, .func = .pl_node, .func_inferred = .pl_node, .import = .str_tok, @@ -1713,6 +1705,7 @@ pub const Inst = struct { .sqrt = .un_node, .sin = .un_node, .cos = .un_node, + .tan = .un_node, .exp = .un_node, .exp2 = .un_node, .log = .un_node, @@ -1928,6 +1921,16 @@ pub const Inst = struct { dbg_block_begin, /// Marks the end of a semantic scope for debug info variables. dbg_block_end, + /// Given a pointer to a struct or object that contains virtual fields, returns the + /// named field. If there is no named field, searches in the type for a decl that + /// matches the field name. The decl is resolved and we ensure that it's a function + /// which can accept the object as the first parameter, with one pointer fixup. If + /// all of that works, this instruction produces a special "bound function" value + /// which contains both the function and the saved first parameter value. + /// Bound functions may only be used as the function parameter to a `call` or + /// `builtin_call` instruction. Any other use is invalid zir and may crash the compiler. + /// Uses `pl_node` field. The AST node is the `@field` builtin. Payload is FieldNamedNode. + field_call_bind_named, pub const InstData = struct { opcode: Extended, @@ -2963,6 +2966,12 @@ pub const Inst = struct { field_name: Ref, }; + pub const FieldNamedNode = struct { + node: i32, + lhs: Ref, + field_name: Ref, + }; + pub const As = struct { dest_type: Ref, operand: Ref, diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index fc37ae00dd..5ed7b63db3 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -533,6 +533,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .sqrt, .sin, .cos, + .tan, .exp, .exp2, .log, diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 54de053475..73f51f6481 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -571,6 +571,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .sqrt, .sin, .cos, + .tan, .exp, .exp2, .log, diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 15377378cd..61fddee207 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -500,6 +500,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .sqrt, .sin, .cos, + .tan, .exp, .exp2, .log, diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 7e1ecefbb7..bcd8cf8eeb 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -451,6 +451,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .sqrt, .sin, .cos, + .tan, .exp, .exp2, .log, diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 8eadfe6cd8..5171dfb460 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1559,6 +1559,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .sqrt, .sin, .cos, + .tan, .exp, .exp2, .log, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 4097352975..0103f5382f 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -656,6 +656,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .sqrt, .sin, .cos, + .tan, .exp, .exp2, .log, diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 464f144f5a..46fee271cc 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1749,6 +1749,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .sqrt, .sin, .cos, + .tan, .exp, .exp2, .log, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index de25a7821d..c9ea5bebac 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3521,6 +3521,7 @@ pub const FuncGen = struct { .sqrt => try self.airUnaryOp(inst, .sqrt), .sin => try self.airUnaryOp(inst, .sin), .cos => try self.airUnaryOp(inst, .cos), + .tan => try self.airUnaryOp(inst, .tan), .exp => try self.airUnaryOp(inst, .exp), .exp2 => try self.airUnaryOp(inst, .exp2), .log => try self.airUnaryOp(inst, .log), @@ -5553,7 +5554,7 @@ pub const FuncGen = struct { fn libcFloatSuffix(float_bits: u16) []const u8 { return switch (float_bits) { 16 => "h", // Non-standard - 32 => "s", + 32 => "f", 64 => "", 80 => "x", // Non-standard 128 => "q", // Non-standard (mimics convention in GCC libquadmath) @@ -5661,6 +5662,7 @@ pub const FuncGen = struct { sin, sqrt, sub, + tan, trunc, }; @@ -5684,7 +5686,7 @@ pub const FuncGen = struct { const llvm_ty = try self.dg.llvmType(ty); const scalar_llvm_ty = try self.dg.llvmType(scalar_ty); - const intrinsics_allowed = intrinsicsAllowed(scalar_ty, target); + const intrinsics_allowed = op != .tan and intrinsicsAllowed(scalar_ty, target); var fn_name_buf: [64]u8 = undefined; const strat: FloatOpStrat = if (intrinsics_allowed) switch (op) { // Some operations are dedicated LLVM instructions, not available as intrinsics @@ -5720,6 +5722,7 @@ pub const FuncGen = struct { .round, .sin, .sqrt, + .tan, .trunc, => FloatOpStrat{ .libc = std.fmt.bufPrintZ(&fn_name_buf, "{s}{s}{s}", .{ diff --git a/src/print_air.zig b/src/print_air.zig index 27d222f262..6e336e138b 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -158,6 +158,7 @@ const Writer = struct { .sqrt, .sin, .cos, + .tan, .exp, .exp2, .log, diff --git a/src/print_zir.zig b/src/print_zir.zig index e85e69fe7f..776aeffbdc 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -207,6 +207,7 @@ const Writer = struct { .sqrt, .sin, .cos, + .tan, .exp, .exp2, .log, @@ -400,7 +401,6 @@ const Writer = struct { .field_ptr_named, .field_val_named, - .field_call_bind_named, => try self.writePlNodeFieldNamed(stream, inst), .as_node => try self.writeAs(stream, inst), @@ -509,6 +509,16 @@ const Writer = struct { try stream.writeAll(")) "); try self.writeSrc(stream, src); }, + + .field_call_bind_named => { + const extra = self.code.extraData(Zir.Inst.FieldNamedNode, extended.operand).data; + const src: LazySrcLoc = .{ .node_offset = extra.node }; + try self.writeInstRef(stream, extra.lhs); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.field_name); + try stream.writeAll(") "); + try self.writeSrc(stream, src); + }, } } diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp index cbefcd1078..398693e6d8 100644 --- a/src/stage1/all_types.hpp +++ b/src/stage1/all_types.hpp @@ -1768,6 +1768,7 @@ enum BuiltinFnId { BuiltinFnIdSqrt, BuiltinFnIdSin, BuiltinFnIdCos, + BuiltinFnIdTan, BuiltinFnIdExp, BuiltinFnIdExp2, BuiltinFnIdLog, diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp index 73e3cd0da6..6e49c91fd8 100644 --- a/src/stage1/analyze.cpp +++ b/src/stage1/analyze.cpp @@ -10383,6 +10383,8 @@ const char *float_un_op_to_name(BuiltinFnId op) { return "sin"; case BuiltinFnIdCos: return "cos"; + case BuiltinFnIdTan: + return "tan"; case BuiltinFnIdExp: return "exp"; case BuiltinFnIdExp2: diff --git a/src/stage1/astgen.cpp b/src/stage1/astgen.cpp index 35566e2143..367bed69cf 100644 --- a/src/stage1/astgen.cpp +++ b/src/stage1/astgen.cpp @@ -4497,6 +4497,7 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast case BuiltinFnIdSqrt: case BuiltinFnIdSin: case BuiltinFnIdCos: + case BuiltinFnIdTan: case BuiltinFnIdExp: case BuiltinFnIdExp2: case BuiltinFnIdLog: diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 88e73baa3c..34ae82eb82 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -1629,11 +1629,28 @@ static const char *get_compiler_rt_type_abbrev(ZigType *type) { } } -static const char *get_math_h_type_abbrev(CodeGen *g, ZigType *float_type) { +static const char *libc_float_prefix(CodeGen *g, ZigType *float_type) { + if (float_type == g->builtin_types.entry_f16) + return "__"; + else if (float_type == g->builtin_types.entry_f32) + return ""; + else if (float_type == g->builtin_types.entry_f64) + return ""; + else if (float_type == g->builtin_types.entry_f80) + return "__"; + else if (float_type == g->builtin_types.entry_c_longdouble) + return "l"; + else if (float_type == g->builtin_types.entry_f128) + return ""; + else + zig_unreachable(); +} + +static const char *libc_float_suffix(CodeGen *g, ZigType *float_type) { if (float_type == g->builtin_types.entry_f16) return "h"; // Non-standard else if (float_type == g->builtin_types.entry_f32) - return "s"; + return "f"; else if (float_type == g->builtin_types.entry_f64) return ""; else if (float_type == g->builtin_types.entry_f80) @@ -2992,10 +3009,12 @@ static LLVMValueRef get_soft_float_fn(CodeGen *g, const char *name, int param_co static LLVMValueRef gen_soft_float_un_op(CodeGen *g, LLVMValueRef op, ZigType *operand_type, BuiltinFnId op_id) { uint32_t vector_len = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.len : 0; + ZigType *scalar_type = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.elem_type : operand_type; char fn_name[64]; - sprintf(fn_name, "%s%s", float_un_op_to_name(op_id), get_math_h_type_abbrev(g, operand_type)); - LLVMValueRef func_ref = get_soft_float_fn(g, fn_name, 1, operand_type->llvm_type, operand_type->llvm_type); + sprintf(fn_name, "%s%s%s", libc_float_prefix(g, scalar_type), + float_un_op_to_name(op_id), libc_float_suffix(g, scalar_type)); + LLVMValueRef func_ref = get_soft_float_fn(g, fn_name, 1, scalar_type->llvm_type, scalar_type->llvm_type); LLVMValueRef result; if (vector_len == 0) { @@ -3018,7 +3037,9 @@ static LLVMValueRef gen_float_un_op(CodeGen *g, LLVMValueRef operand, ZigType *o assert(operand_type->id == ZigTypeIdFloat || operand_type->id == ZigTypeIdVector); ZigType *elem_type = operand_type->id == ZigTypeIdVector ? operand_type->data.vector.elem_type : operand_type; if ((elem_type == g->builtin_types.entry_f80 && !target_has_f80(g->zig_target)) || - (elem_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target))) { + (elem_type == g->builtin_types.entry_f128 && !target_long_double_is_f128(g->zig_target)) || + op == BuiltinFnIdTan) + { return gen_soft_float_un_op(g, operand, operand_type, op); } LLVMValueRef float_op_fn = get_float_fn(g, operand_type, ZigLLVMFnIdFloatOp, op); @@ -3466,7 +3487,8 @@ static LLVMValueRef gen_soft_float_bin_op(CodeGen *g, LLVMValueRef op1_value, LL int param_count = 2; const char *compiler_rt_type_abbrev = get_compiler_rt_type_abbrev(operand_type); - const char *math_h_type_abbrev = get_math_h_type_abbrev(g, operand_type); + const char *math_float_prefix = libc_float_prefix(g, operand_type); + const char *math_float_suffix = libc_float_suffix(g, operand_type); char fn_name[64]; Icmp res_icmp = NONE; @@ -3523,10 +3545,10 @@ static LLVMValueRef gen_soft_float_bin_op(CodeGen *g, LLVMValueRef op1_value, LL res_icmp = EQ_ONE; break; case IrBinOpMaximum: - sprintf(fn_name, "fmax%s", math_h_type_abbrev); + sprintf(fn_name, "%sfmax%s", math_float_prefix, math_float_suffix); break; case IrBinOpMinimum: - sprintf(fn_name, "fmin%s", math_h_type_abbrev); + sprintf(fn_name, "%sfmin%s", math_float_prefix, math_float_suffix); break; case IrBinOpMult: sprintf(fn_name, "__mul%sf3", compiler_rt_type_abbrev); @@ -3545,7 +3567,7 @@ static LLVMValueRef gen_soft_float_bin_op(CodeGen *g, LLVMValueRef op1_value, LL break; case IrBinOpRemRem: case IrBinOpRemMod: - sprintf(fn_name, "fmod%s", math_h_type_abbrev); + sprintf(fn_name, "%sfmod%s", math_float_prefix, math_float_suffix); break; default: zig_unreachable(); @@ -9810,6 +9832,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 1); create_builtin_fn(g, BuiltinFnIdSin, "sin", 1); create_builtin_fn(g, BuiltinFnIdCos, "cos", 1); + create_builtin_fn(g, BuiltinFnIdTan, "tan", 1); create_builtin_fn(g, BuiltinFnIdExp, "exp", 1); create_builtin_fn(g, BuiltinFnIdExp2, "exp2", 1); create_builtin_fn(g, BuiltinFnIdLog, "log", 1); diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 874d068c03..1eef354864 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -24132,6 +24132,9 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_ case BuiltinFnIdCos: out_val->data.x_f16 = zig_double_to_f16(cos(zig_f16_to_double(op->data.x_f16))); break; + case BuiltinFnIdTan: + out_val->data.x_f16 = zig_double_to_f16(tan(zig_f16_to_double(op->data.x_f16))); + break; case BuiltinFnIdExp: out_val->data.x_f16 = zig_double_to_f16(exp(zig_f16_to_double(op->data.x_f16))); break; @@ -24181,6 +24184,9 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_ case BuiltinFnIdCos: out_val->data.x_f32 = cosf(op->data.x_f32); break; + case BuiltinFnIdTan: + out_val->data.x_f32 = tanf(op->data.x_f32); + break; case BuiltinFnIdExp: out_val->data.x_f32 = expf(op->data.x_f32); break; @@ -24230,6 +24236,9 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_ case BuiltinFnIdCos: out_val->data.x_f64 = cos(op->data.x_f64); break; + case BuiltinFnIdTan: + out_val->data.x_f64 = tan(op->data.x_f64); + break; case BuiltinFnIdExp: out_val->data.x_f64 = exp(op->data.x_f64); break; @@ -24293,6 +24302,7 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_ case BuiltinFnIdNearbyInt: case BuiltinFnIdSin: case BuiltinFnIdCos: + case BuiltinFnIdTan: case BuiltinFnIdExp: case BuiltinFnIdExp2: case BuiltinFnIdLog: @@ -24337,6 +24347,7 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_ case BuiltinFnIdNearbyInt: case BuiltinFnIdSin: case BuiltinFnIdCos: + case BuiltinFnIdTan: case BuiltinFnIdExp: case BuiltinFnIdExp2: case BuiltinFnIdLog: diff --git a/src/value.zig b/src/value.zig index e951b075c0..a39984d1d4 100644 --- a/src/value.zig +++ b/src/value.zig @@ -4473,6 +4473,44 @@ pub const Value = extern union { } } + pub fn tan(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try tanScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return tanScalar(val, float_type, arena, target); + } + + pub fn tanScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + switch (float_type.floatBits(target)) { + 16 => { + const f = val.toFloat(f16); + return Value.Tag.float_16.create(arena, @tan(f)); + }, + 32 => { + const f = val.toFloat(f32); + return Value.Tag.float_32.create(arena, @tan(f)); + }, + 64 => { + const f = val.toFloat(f64); + return Value.Tag.float_64.create(arena, @tan(f)); + }, + 80 => { + const f = val.toFloat(f80); + return Value.Tag.float_80.create(arena, @tan(f)); + }, + 128 => { + const f = val.toFloat(f128); + return Value.Tag.float_128.create(arena, @tan(f)); + }, + else => unreachable, + } + } + + pub fn exp(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { if (float_type.zigTypeTag() == .Vector) { const result_data = try arena.alloc(Value, float_type.vectorLen()); diff --git a/test/behavior/bugs/920.zig b/test/behavior/bugs/920.zig index 380d42e5de..5a7cadc595 100644 --- a/test/behavior/bugs/920.zig +++ b/test/behavior/bugs/920.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const math = std.math; const Random = std.rand.Random; const ZigTable = struct { @@ -40,10 +39,10 @@ const norm_r = 3.6541528853610088; const norm_v = 0.00492867323399; fn norm_f(x: f64) f64 { - return math.exp(-x * x / 2.0); + return @exp(-x * x / 2.0); } fn norm_f_inv(y: f64) f64 { - return math.sqrt(-2.0 * math.ln(y)); + return @sqrt(-2.0 * @log(y)); } fn norm_zero_case(random: *Random, u: f64) f64 { _ = random; From 758ec9bdd4585b28f50cb7beb01b1cfc4e3cf1a9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Apr 2022 17:24:36 -0700 Subject: [PATCH 1251/2031] enable newly passing behavior tests closes #11030 --- src/value.zig | 1 - test/behavior/floatop.zig | 20 +--- test/behavior/math.zig | 202 ++++++++++++++++++++++++-------------- test/behavior/muladd.zig | 21 ++-- 4 files changed, 147 insertions(+), 97 deletions(-) diff --git a/src/value.zig b/src/value.zig index a39984d1d4..d2de389de9 100644 --- a/src/value.zig +++ b/src/value.zig @@ -4510,7 +4510,6 @@ pub const Value = extern union { } } - pub fn exp(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { if (float_type.zigTypeTag() == .Vector) { const result_data = try arena.alloc(Value, float_type.vectorLen()); diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 0700b47c61..cc978f3b8d 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -609,16 +609,11 @@ test "negation f64" { } test "negation f80" { - if (builtin.zig_backend != .stage1) { - // This test case exercises @intToFloat f80 in the compiler implementation. - // https://github.com/ziglang/zig/issues/11030 - return error.SkipZigTest; - } - - if (builtin.os.tag == .freebsd) { - // TODO file issue to track this failure - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -641,11 +636,6 @@ test "negation f128" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.os.tag == .freebsd) { - // TODO file issue to track this failure - return error.SkipZigTest; - } - const S = struct { fn doTheTest() !void { var a: f128 = 1; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 942d5f8ca7..0479015eee 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -910,32 +910,52 @@ test "comptime float rem int" { } test "remainder division" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO comptime try remdiv(f16); comptime try remdiv(f32); comptime try remdiv(f64); + comptime try remdiv(f80); comptime try remdiv(f128); try remdiv(f16); try remdiv(f64); + try remdiv(f80); try remdiv(f128); } fn remdiv(comptime T: type) !void { try expect(@as(T, 1) == @as(T, 1) % @as(T, 2)); + try remdivOne(T, 1, 1, 2); + try expect(@as(T, 1) == @as(T, 7) % @as(T, 3)); + try remdivOne(T, 1, 7, 3); +} + +fn remdivOne(comptime T: type, a: T, b: T, c: T) !void { + try expect(a == @rem(b, c)); + try expect(a == @mod(b, c)); } test "float remainder division using @rem" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO comptime try frem(f16); comptime try frem(f32); comptime try frem(f64); + comptime try frem(f80); comptime try frem(f128); try frem(f16); try frem(f32); try frem(f64); + try frem(f80); try frem(f128); } @@ -944,29 +964,40 @@ fn frem(comptime T: type) !void { f16 => 1.0, f32 => 0.001, f64 => 0.00001, + f80 => 0.000001, f128 => 0.0000001, else => unreachable, }; - try expect(@fabs(@rem(@as(T, 6.9), @as(T, 4.0)) - @as(T, 2.9)) < epsilon); - try expect(@fabs(@rem(@as(T, -6.9), @as(T, 4.0)) - @as(T, -2.9)) < epsilon); - try expect(@fabs(@rem(@as(T, -5.0), @as(T, 3.0)) - @as(T, -2.0)) < epsilon); - try expect(@fabs(@rem(@as(T, 3.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon); - try expect(@fabs(@rem(@as(T, 1.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon); - try expect(@fabs(@rem(@as(T, 0.0), @as(T, 1.0)) - @as(T, 0.0)) < epsilon); - try expect(@fabs(@rem(@as(T, -0.0), @as(T, 1.0)) - @as(T, -0.0)) < epsilon); + try fremOne(T, 6.9, 4.0, 2.9, epsilon); + try fremOne(T, -6.9, 4.0, -2.9, epsilon); + try fremOne(T, -5.0, 3.0, -2.0, epsilon); + try fremOne(T, 3.0, 2.0, 1.0, epsilon); + try fremOne(T, 1.0, 2.0, 1.0, epsilon); + try fremOne(T, 0.0, 1.0, 0.0, epsilon); + try fremOne(T, -0.0, 1.0, -0.0, epsilon); +} + +fn fremOne(comptime T: type, a: T, b: T, c: T, epsilon: T) !void { + try expect(@fabs(@rem(a, b) - c) < epsilon); } test "float modulo division using @mod" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO comptime try fmod(f16); comptime try fmod(f32); comptime try fmod(f64); + comptime try fmod(f80); comptime try fmod(f128); try fmod(f16); try fmod(f32); try fmod(f64); + try fmod(f80); try fmod(f128); } @@ -975,17 +1006,22 @@ fn fmod(comptime T: type) !void { f16 => 1.0, f32 => 0.001, f64 => 0.00001, + f80 => 0.000001, f128 => 0.0000001, else => unreachable, }; - try expect(@fabs(@mod(@as(T, 6.9), @as(T, 4.0)) - @as(T, 2.9)) < epsilon); - try expect(@fabs(@mod(@as(T, -6.9), @as(T, 4.0)) - @as(T, 1.1)) < epsilon); - try expect(@fabs(@mod(@as(T, -5.0), @as(T, 3.0)) - @as(T, 1.0)) < epsilon); - try expect(@fabs(@mod(@as(T, 3.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon); - try expect(@fabs(@mod(@as(T, 1.0), @as(T, 2.0)) - @as(T, 1.0)) < epsilon); - try expect(@fabs(@mod(@as(T, 0.0), @as(T, 1.0)) - @as(T, 0.0)) < epsilon); - try expect(@fabs(@mod(@as(T, -0.0), @as(T, 1.0)) - @as(T, -0.0)) < epsilon); + try fmodOne(T, 6.9, 4.0, 2.9, epsilon); + try fmodOne(T, -6.9, 4.0, 1.1, epsilon); + try fmodOne(T, -5.0, 3.0, 1.0, epsilon); + try fmodOne(T, 3.0, 2.0, 1.0, epsilon); + try fmodOne(T, 1.0, 2.0, 1.0, epsilon); + try fmodOne(T, 0.0, 1.0, 0.0, epsilon); + try fmodOne(T, -0.0, 1.0, -0.0, epsilon); +} + +fn fmodOne(comptime T: type, a: T, b: T, c: T, epsilon: T) !void { + try expect(@fabs(@mod(@as(T, a), @as(T, b)) - @as(T, c)) < epsilon); } test "@sqrt" { @@ -1037,10 +1073,11 @@ test "@fabs" { } test "@fabs f80" { - if (true) { - // https://github.com/ziglang/zig/issues/11030 - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try testFabs(f80, 12.0); comptime try testFabs(f80, 12.0); @@ -1053,7 +1090,11 @@ fn testFabs(comptime T: type, x: T) !void { } test "@floor" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try testFloor(f64, 12.0); comptime try testFloor(f64, 12.0); @@ -1069,23 +1110,24 @@ test "@floor" { } test "@floor f80" { - if (true) { - // https://github.com/ziglang/zig/issues/11030 - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + try testFloor(f80, 12.0); comptime try testFloor(f80, 12.0); } test "@floor f128" { - if (builtin.zig_backend == .stage1) { - // Fails because it incorrectly lowers to a floorl function call. - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - testFloor(f128, 12.0); + try testFloor(f128, 12.0); comptime try testFloor(f128, 12.0); } @@ -1096,7 +1138,11 @@ fn testFloor(comptime T: type, x: T) !void { } test "@ceil" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try testCeil(f64, 12.0); comptime try testCeil(f64, 12.0); @@ -1112,24 +1158,24 @@ test "@ceil" { } test "@ceil f80" { - if (true) { - // https://github.com/ziglang/zig/issues/11030 - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try testCeil(f80, 12.0); comptime try testCeil(f80, 12.0); } test "@ceil f128" { - if (builtin.zig_backend == .stage1) { - // Fails because it incorrectly lowers to a ceill function call. - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - testCeil(f128, 12.0); + try testCeil(f128, 12.0); comptime try testCeil(f128, 12.0); } @@ -1140,7 +1186,11 @@ fn testCeil(comptime T: type, x: T) !void { } test "@trunc" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try testTrunc(f64, 12.0); comptime try testTrunc(f64, 12.0); @@ -1156,10 +1206,11 @@ test "@trunc" { } test "@trunc f80" { - if (true) { - // https://github.com/ziglang/zig/issues/11030 - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try testTrunc(f80, 12.0); comptime try testTrunc(f80, 12.0); @@ -1172,14 +1223,13 @@ test "@trunc f80" { } test "@trunc f128" { - if (builtin.zig_backend == .stage1) { - // Fails because it incorrectly lowers to a truncl function call. - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - testTrunc(f128, 12.0); + try testTrunc(f128, 12.0); comptime try testTrunc(f128, 12.0); } @@ -1198,7 +1248,11 @@ fn testTrunc(comptime T: type, x: T) !void { } test "@round" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try testRound(f64, 12.0); comptime try testRound(f64, 12.0); @@ -1214,24 +1268,24 @@ test "@round" { } test "@round f80" { - if (true) { - // https://github.com/ziglang/zig/issues/11030 - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO try testRound(f80, 12.0); comptime try testRound(f80, 12.0); } test "@round f128" { - if (builtin.zig_backend == .stage1) { - // Fails because it incorrectly lowers to a roundl function call. - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - testRound(f128, 12.0); + try testRound(f128, 12.0); comptime try testRound(f128, 12.0); } @@ -1280,10 +1334,12 @@ test "NaN comparison" { } test "NaN comparison f80" { - if (true) { - // https://github.com/ziglang/zig/issues/11030 - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + try testNanEqNan(f80); comptime try testNanEqNan(f80); } diff --git a/test/behavior/muladd.zig b/test/behavior/muladd.zig index a7e7c3b816..01ef8c7d29 100644 --- a/test/behavior/muladd.zig +++ b/test/behavior/muladd.zig @@ -32,6 +32,7 @@ test "@mulAdd f16" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + comptime try testMulAdd16(); try testMulAdd16(); } @@ -44,10 +45,12 @@ fn testMulAdd16() !void { } test "@mulAdd f80" { - if (true) { - // https://github.com/ziglang/zig/issues/11030 - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage1) return error.SkipZigTest; comptime try testMulAdd80(); try testMulAdd80(); @@ -173,10 +176,12 @@ fn vector80() !void { } test "vector f80" { - if (true) { - // https://github.com/ziglang/zig/issues/11030 - return error.SkipZigTest; - } + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO comptime try vector80(); try vector80(); From dc62dde9826a94c161e20bf56e23940dc3f2f0dc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Apr 2022 17:58:26 -0700 Subject: [PATCH 1252/2031] behavior test: use expectApproxEqAbs instead of expectEqual This is to account for the small differences in math functions of different libcs. For example, if the compiler links against glibc, but the target is musl libc, then these values might be slightly different. Arguably, this is a bug in the compiler because comptime should emulate the target, including rounding errors in libc math functions. However that behavior is not what this particular test is intended to cover. --- test/behavior/bugs/920.zig | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/behavior/bugs/920.zig b/test/behavior/bugs/920.zig index 5a7cadc595..19fce71549 100644 --- a/test/behavior/bugs/920.zig +++ b/test/behavior/bugs/920.zig @@ -63,6 +63,13 @@ test "bug 920 fixed" { }; for (NormalDist1.f) |_, i| { - try std.testing.expectEqual(NormalDist1.f[i], NormalDist.f[i]); + // Here we use `expectApproxEqAbs` instead of `expectEqual` to account for the small + // differences in math functions of different libcs. For example, if the compiler + // links against glibc, but the target is musl libc, then these values might be + // slightly different. + // Arguably, this is a bug in the compiler because comptime should emulate the target, + // including rounding errors in libc math functions. However that behavior is not + // what this particular test is intended to cover. + try std.testing.expectApproxEqAbs(NormalDist1.f[i], NormalDist.f[i], @sqrt(std.math.floatEps(f64))); } } From 1ac21cdec5747e2c7788c566a2998b2bee54eb47 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Apr 2022 18:14:44 -0700 Subject: [PATCH 1253/2031] compiler-rt: avoid symbol conflicts Weak aliases don't work on Windows, so we avoid exporting the `l` alias on this platform for functions we know will collide. --- lib/std/special/compiler_rt.zig | 54 ++++++++++++++++++--------------- src/stage1/codegen.cpp | 47 ++++++++++++---------------- 2 files changed, 48 insertions(+), 53 deletions(-) diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index dccb9264bd..f0b0c49152 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -723,25 +723,25 @@ comptime { @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage }); } - mathExport("ceil", @import("./compiler_rt/ceil.zig")); - mathExport("cos", @import("./compiler_rt/cos.zig")); - mathExport("exp", @import("./compiler_rt/exp.zig")); - mathExport("exp2", @import("./compiler_rt/exp2.zig")); - mathExport("fabs", @import("./compiler_rt/fabs.zig")); - mathExport("floor", @import("./compiler_rt/floor.zig")); - mathExport("fma", @import("./compiler_rt/fma.zig")); - mathExport("fmax", @import("./compiler_rt/fmax.zig")); - mathExport("fmin", @import("./compiler_rt/fmin.zig")); - mathExport("fmod", @import("./compiler_rt/fmod.zig")); - mathExport("log", @import("./compiler_rt/log.zig")); - mathExport("log10", @import("./compiler_rt/log10.zig")); - mathExport("log2", @import("./compiler_rt/log2.zig")); - mathExport("round", @import("./compiler_rt/round.zig")); - mathExport("sin", @import("./compiler_rt/sin.zig")); - mathExport("sincos", @import("./compiler_rt/sincos.zig")); - mathExport("sqrt", @import("./compiler_rt/sqrt.zig")); - mathExport("tan", @import("./compiler_rt/tan.zig")); - mathExport("trunc", @import("./compiler_rt/trunc.zig")); + mathExport("ceil", @import("./compiler_rt/ceil.zig"), true); + mathExport("cos", @import("./compiler_rt/cos.zig"), true); + mathExport("exp", @import("./compiler_rt/exp.zig"), true); + mathExport("exp2", @import("./compiler_rt/exp2.zig"), true); + mathExport("fabs", @import("./compiler_rt/fabs.zig"), true); + mathExport("floor", @import("./compiler_rt/floor.zig"), true); + mathExport("fma", @import("./compiler_rt/fma.zig"), true); + mathExport("fmax", @import("./compiler_rt/fmax.zig"), true); + mathExport("fmin", @import("./compiler_rt/fmin.zig"), true); + mathExport("fmod", @import("./compiler_rt/fmod.zig"), true); + mathExport("log", @import("./compiler_rt/log.zig"), true); + mathExport("log10", @import("./compiler_rt/log10.zig"), true); + mathExport("log2", @import("./compiler_rt/log2.zig"), true); + mathExport("round", @import("./compiler_rt/round.zig"), true); + mathExport("sin", @import("./compiler_rt/sin.zig"), true); + mathExport("sincos", @import("./compiler_rt/sincos.zig"), true); + mathExport("sqrt", @import("./compiler_rt/sqrt.zig"), true); + mathExport("tan", @import("./compiler_rt/tan.zig"), false); + mathExport("trunc", @import("./compiler_rt/trunc.zig"), true); if (arch.isSPARC()) { // SPARC systems use a different naming scheme @@ -827,7 +827,7 @@ comptime { } } -inline fn mathExport(double_name: []const u8, comptime import: type) void { +inline fn mathExport(double_name: []const u8, comptime import: type, is_standard: bool) void { const half_name = "__" ++ double_name ++ "h"; const half_fn = @field(import, half_name); const float_name = double_name ++ "f"; @@ -853,11 +853,15 @@ inline fn mathExport(double_name: []const u8, comptime import: type) void { .{ f128, quad_fn }, }; - inline for (pairs) |pair| { - const F = pair[0]; - const func = pair[1]; - if (builtin.target.longDoubleIs(F)) { - @export(func, .{ .name = long_double_name, .linkage = linkage }); + // Weak aliases don't work on Windows, so we avoid exporting the `l` alias + // on this platform for functions we know will collide. + if (builtin.os.tag != .windows or !builtin.link_libc or !is_standard) { + inline for (pairs) |pair| { + const F = pair[0]; + const func = pair[1]; + if (builtin.target.longDoubleIs(F)) { + @export(func, .{ .name = long_double_name, .linkage = linkage }); + } } } } diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 34ae82eb82..c575aff53d 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -1630,37 +1630,28 @@ static const char *get_compiler_rt_type_abbrev(ZigType *type) { } static const char *libc_float_prefix(CodeGen *g, ZigType *float_type) { - if (float_type == g->builtin_types.entry_f16) - return "__"; - else if (float_type == g->builtin_types.entry_f32) - return ""; - else if (float_type == g->builtin_types.entry_f64) - return ""; - else if (float_type == g->builtin_types.entry_f80) - return "__"; - else if (float_type == g->builtin_types.entry_c_longdouble) - return "l"; - else if (float_type == g->builtin_types.entry_f128) - return ""; - else - zig_unreachable(); + switch (float_type->data.floating.bit_count) { + case 16: + case 80: + return "__"; + case 32: + case 64: + case 128: + return ""; + default: + zig_unreachable(); + } } static const char *libc_float_suffix(CodeGen *g, ZigType *float_type) { - if (float_type == g->builtin_types.entry_f16) - return "h"; // Non-standard - else if (float_type == g->builtin_types.entry_f32) - return "f"; - else if (float_type == g->builtin_types.entry_f64) - return ""; - else if (float_type == g->builtin_types.entry_f80) - return "x"; // Non-standard - else if (float_type == g->builtin_types.entry_c_longdouble) - return "l"; - else if (float_type == g->builtin_types.entry_f128) - return "q"; // Non-standard - else - zig_unreachable(); + switch (float_type->size_in_bits) { + case 16: return "h"; // Non-standard + case 32: return "f"; + case 64: return ""; + case 80: return "x"; // Non-standard + case 128: return "q"; // Non-standard + default: zig_unreachable(); + } } static LLVMValueRef gen_soft_float_widen_or_shorten(CodeGen *g, ZigType *actual_type, From 51f8ce182573e940ee090c1df146a83de63d6b5c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Apr 2022 18:26:47 -0700 Subject: [PATCH 1254/2031] compiler-rt: fix powerpc f128 suffix --- lib/std/special/compiler_rt.zig | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index f0b0c49152..f509e584f5 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -8,6 +8,7 @@ const abi = builtin.abi; const is_gnu = abi.isGnu(); const is_mingw = os_tag == .windows and is_gnu; const is_darwin = std.Target.Os.Tag.isDarwin(os_tag); +const is_ppc = arch.isPPC() or arch.isPPC64(); const linkage = if (is_test) std.builtin.GlobalLinkage.Internal @@ -795,7 +796,7 @@ comptime { @export(_Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage }); } - if ((arch.isPPC() or arch.isPPC64()) and !is_test) { + if (is_ppc and !is_test) { @export(__addtf3, .{ .name = "__addkf3", .linkage = linkage }); @export(__subtf3, .{ .name = "__subkf3", .linkage = linkage }); @export(__multf3, .{ .name = "__mulkf3", .linkage = linkage }); @@ -820,10 +821,6 @@ comptime { @export(__letf2, .{ .name = "__lekf2", .linkage = linkage }); @export(__getf2, .{ .name = "__gtkf2", .linkage = linkage }); @export(__unordtf2, .{ .name = "__unordkf2", .linkage = linkage }); - - // LLVM PPC backend lowers f128 fma to `fmaf128`. - const fmaq = @import("./compiler_rt/fma.zig").fmaq; - @export(fmaq, .{ .name = "fmaf128", .linkage = linkage }); } } @@ -845,6 +842,8 @@ inline fn mathExport(double_name: []const u8, comptime import: type, is_standard @export(xf80_fn, .{ .name = xf80_name, .linkage = linkage }); @export(quad_fn, .{ .name = quad_name, .linkage = linkage }); + if (is_test) return; + const pairs = .{ .{ f16, half_fn }, .{ f32, float_fn }, @@ -864,6 +863,11 @@ inline fn mathExport(double_name: []const u8, comptime import: type, is_standard } } } + + if (is_ppc and is_standard) { + // LLVM PPC backend lowers f128 ops with the suffix `f128` instead of `l`. + @export(quad_fn, .{ .name = double_name ++ "f128", .linkage = linkage }); + } } // Avoid dragging in the runtime safety mechanisms into this .o file, From 1cd799317b4c5f19ab2cf5413be5d280de6a8476 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Apr 2022 18:28:33 -0700 Subject: [PATCH 1255/2031] compiler-rt: remove invalid test This was leftover from testing std.math.sin --- lib/std/special/compiler_rt/sin.zig | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/std/special/compiler_rt/sin.zig b/lib/std/special/compiler_rt/sin.zig index aa77a961c7..a0876d30a0 100644 --- a/lib/std/special/compiler_rt/sin.zig +++ b/lib/std/special/compiler_rt/sin.zig @@ -111,12 +111,6 @@ pub fn sinq(x: f128) callconv(.C) f128 { return sin(@floatCast(f64, x)); } -test "sin" { - try expect(sin(@as(f32, 0.0)) == sinf(0.0)); - try expect(sin(@as(f64, 0.0)) == sin(0.0)); - try expect(comptime (math.sin(@as(f64, 2))) == math.sin(@as(f64, 2))); -} - test "sin32" { const epsilon = 0.00001; From 7d8067878dd4200d73052b8ffd80e2f23c32c2cc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Apr 2022 18:41:53 -0700 Subject: [PATCH 1256/2031] disable failing behavior test This is a new test added in this branch but it is not yet passing for i386-windows with the stage1 compiler. --- test/behavior/math.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 0479015eee..85cf7f5643 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1416,6 +1416,12 @@ test "fabs" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage1 and builtin.os.tag == .windows and + builtin.cpu.arch == .i386) + { + return error.SkipZigTest; + } + inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| { // normals try expect(@fabs(@as(T, 1.0)) == 1.0); From c26f7550f037e44fea51983eeb3f0adbb34d1d38 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Apr 2022 19:34:33 -0700 Subject: [PATCH 1257/2031] stage1: add missing comptime float ops These are only as accurate as f64 even for f128 comptime functions. This is OK for now; improvements will come with the launch of self-hosted (#89) and enhancements to compiler-rt implementations. --- src/stage1/ir.cpp | 95 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 82 insertions(+), 13 deletions(-) diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 1eef354864..f7ab5e12fa 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -24337,25 +24337,94 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, Scope *scope, AstNode *source_ break; case BuiltinFnIdCeil: f128M_roundToInt(in, softfloat_round_max, false, out); - break; + break; case BuiltinFnIdTrunc: f128M_trunc(in, out); break; case BuiltinFnIdRound: f128M_roundToInt(in, softfloat_round_near_maxMag, false, out); break; - case BuiltinFnIdNearbyInt: - case BuiltinFnIdSin: - case BuiltinFnIdCos: - case BuiltinFnIdTan: - case BuiltinFnIdExp: - case BuiltinFnIdExp2: - case BuiltinFnIdLog: - case BuiltinFnIdLog10: - case BuiltinFnIdLog2: - return ir_add_error_node(ira, source_node, - buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026", - float_un_op_to_name(fop), buf_ptr(&float_type->name))); + case BuiltinFnIdNearbyInt: { + float64_t f64_value = f128M_to_f64(in); + double double_value; + memcpy(&double_value, &f64_value, sizeof(double)); + double_value = nearbyint(double_value); + memcpy(&f64_value, &double_value, sizeof(double)); + f64_to_f128M(f64_value, out); + break; + } + case BuiltinFnIdSin: { + float64_t f64_value = f128M_to_f64(in); + double double_value; + memcpy(&double_value, &f64_value, sizeof(double)); + double_value = sin(double_value); + memcpy(&f64_value, &double_value, sizeof(double)); + f64_to_f128M(f64_value, out); + break; + } + case BuiltinFnIdCos: { + float64_t f64_value = f128M_to_f64(in); + double double_value; + memcpy(&double_value, &f64_value, sizeof(double)); + double_value = cos(double_value); + memcpy(&f64_value, &double_value, sizeof(double)); + f64_to_f128M(f64_value, out); + break; + } + case BuiltinFnIdTan: { + float64_t f64_value = f128M_to_f64(in); + double double_value; + memcpy(&double_value, &f64_value, sizeof(double)); + double_value = tan(double_value); + memcpy(&f64_value, &double_value, sizeof(double)); + f64_to_f128M(f64_value, out); + break; + } + case BuiltinFnIdExp: { + float64_t f64_value = f128M_to_f64(in); + double double_value; + memcpy(&double_value, &f64_value, sizeof(double)); + double_value = exp(double_value); + memcpy(&f64_value, &double_value, sizeof(double)); + f64_to_f128M(f64_value, out); + break; + } + case BuiltinFnIdExp2: { + float64_t f64_value = f128M_to_f64(in); + double double_value; + memcpy(&double_value, &f64_value, sizeof(double)); + double_value = exp2(double_value); + memcpy(&f64_value, &double_value, sizeof(double)); + f64_to_f128M(f64_value, out); + break; + } + case BuiltinFnIdLog: { + float64_t f64_value = f128M_to_f64(in); + double double_value; + memcpy(&double_value, &f64_value, sizeof(double)); + double_value = log(double_value); + memcpy(&f64_value, &double_value, sizeof(double)); + f64_to_f128M(f64_value, out); + break; + } + case BuiltinFnIdLog10: { + float64_t f64_value = f128M_to_f64(in); + double double_value; + memcpy(&double_value, &f64_value, sizeof(double)); + double_value = log10(double_value); + memcpy(&f64_value, &double_value, sizeof(double)); + f64_to_f128M(f64_value, out); + break; + } + case BuiltinFnIdLog2: { + float64_t f64_value = f128M_to_f64(in); + double double_value; + memcpy(&double_value, &f64_value, sizeof(double)); + double_value = log2(double_value); + memcpy(&f64_value, &double_value, sizeof(double)); + f64_to_f128M(f64_value, out); + break; + } default: zig_unreachable(); } From 0ffe82e624f9334473758349219fe1dbcf3edb3b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Apr 2022 19:35:28 -0700 Subject: [PATCH 1258/2031] std: use float builtins instead of std.math --- lib/std/json.zig | 2 +- lib/std/math/complex/cosh.zig | 16 ++++++++-------- lib/std/math/complex/exp.zig | 8 ++++---- lib/std/math/complex/ldexp.zig | 8 ++++---- lib/std/math/complex/sinh.zig | 16 ++++++++-------- lib/std/math/complex/tanh.zig | 8 ++++---- 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/lib/std/json.zig b/lib/std/json.zig index c18f38754a..b670e488b2 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -1655,7 +1655,7 @@ fn parseInternal( if (numberToken.is_integer) return try std.fmt.parseInt(T, numberToken.slice(tokens.slice, tokens.i - 1), 10); const float = try std.fmt.parseFloat(f128, numberToken.slice(tokens.slice, tokens.i - 1)); - if (std.math.round(float) != float) return error.InvalidNumber; + if (@round(float) != float) return error.InvalidNumber; if (float > std.math.maxInt(T) or float < std.math.minInt(T)) return error.Overflow; return @floatToInt(T, float); }, diff --git a/lib/std/math/complex/cosh.zig b/lib/std/math/complex/cosh.zig index 719d0f28cd..65cfc4a528 100644 --- a/lib/std/math/complex/cosh.zig +++ b/lib/std/math/complex/cosh.zig @@ -38,14 +38,14 @@ fn cosh32(z: Complex(f32)) Complex(f32) { } // small x: normal case if (ix < 0x41100000) { - return Complex(f32).init(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y)); + return Complex(f32).init(math.cosh(x) * @cos(y), math.sinh(x) * @sin(y)); } // |x|>= 9, so cosh(x) ~= exp(|x|) if (ix < 0x42b17218) { // x < 88.7: exp(|x|) won't overflow const h = @exp(@fabs(x)) * 0.5; - return Complex(f32).init(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y)); + return Complex(f32).init(math.copysign(f32, h, x) * @cos(y), h * @sin(y)); } // x < 192.7: scale to avoid overflow else if (ix < 0x4340b1e7) { @@ -56,7 +56,7 @@ fn cosh32(z: Complex(f32)) Complex(f32) { // x >= 192.7: result always overflows else { const h = 0x1p127 * x; - return Complex(f32).init(h * h * math.cos(y), h * math.sin(y)); + return Complex(f32).init(h * h * @cos(y), h * @sin(y)); } } @@ -79,7 +79,7 @@ fn cosh32(z: Complex(f32)) Complex(f32) { if (iy >= 0x7f800000) { return Complex(f32).init(x * x, x * (y - y)); } - return Complex(f32).init((x * x) * math.cos(y), x * math.sin(y)); + return Complex(f32).init((x * x) * @cos(y), x * @sin(y)); } return Complex(f32).init((x * x) * (y - y), (x + x) * (y - y)); @@ -106,14 +106,14 @@ fn cosh64(z: Complex(f64)) Complex(f64) { } // small x: normal case if (ix < 0x40360000) { - return Complex(f64).init(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y)); + return Complex(f64).init(math.cosh(x) * @cos(y), math.sinh(x) * @sin(y)); } // |x|>= 22, so cosh(x) ~= exp(|x|) if (ix < 0x40862e42) { // x < 710: exp(|x|) won't overflow const h = @exp(@fabs(x)) * 0.5; - return Complex(f64).init(h * math.cos(y), math.copysign(f64, h, x) * math.sin(y)); + return Complex(f64).init(h * @cos(y), math.copysign(f64, h, x) * @sin(y)); } // x < 1455: scale to avoid overflow else if (ix < 0x4096bbaa) { @@ -124,7 +124,7 @@ fn cosh64(z: Complex(f64)) Complex(f64) { // x >= 1455: result always overflows else { const h = 0x1p1023; - return Complex(f64).init(h * h * math.cos(y), h * math.sin(y)); + return Complex(f64).init(h * h * @cos(y), h * @sin(y)); } } @@ -147,7 +147,7 @@ fn cosh64(z: Complex(f64)) Complex(f64) { if (iy >= 0x7ff00000) { return Complex(f64).init(x * x, x * (y - y)); } - return Complex(f64).init(x * x * math.cos(y), x * math.sin(y)); + return Complex(f64).init(x * x * @cos(y), x * @sin(y)); } return Complex(f64).init((x * x) * (y - y), (x + x) * (y - y)); diff --git a/lib/std/math/complex/exp.zig b/lib/std/math/complex/exp.zig index 4ed731d85c..84ee251d0e 100644 --- a/lib/std/math/complex/exp.zig +++ b/lib/std/math/complex/exp.zig @@ -39,7 +39,7 @@ fn exp32(z: Complex(f32)) Complex(f32) { const hx = @bitCast(u32, x); // cexp(0 + iy) = cos(y) + isin(y) if ((hx & 0x7fffffff) == 0) { - return Complex(f32).init(math.cos(y), math.sin(y)); + return Complex(f32).init(@cos(y), @sin(y)); } if (hy >= 0x7f800000) { @@ -64,7 +64,7 @@ fn exp32(z: Complex(f32)) Complex(f32) { // - x = nan else { const exp_x = @exp(x); - return Complex(f32).init(exp_x * math.cos(y), exp_x * math.sin(y)); + return Complex(f32).init(exp_x * @cos(y), exp_x * @sin(y)); } } @@ -90,7 +90,7 @@ fn exp64(z: Complex(f64)) Complex(f64) { // cexp(0 + iy) = cos(y) + isin(y) if ((hx & 0x7fffffff) | lx == 0) { - return Complex(f64).init(math.cos(y), math.sin(y)); + return Complex(f64).init(@cos(y), @sin(y)); } if (hy >= 0x7ff00000) { @@ -115,7 +115,7 @@ fn exp64(z: Complex(f64)) Complex(f64) { // - x = nan else { const exp_x = @exp(x); - return Complex(f64).init(exp_x * math.cos(y), exp_x * math.sin(y)); + return Complex(f64).init(exp_x * @cos(y), exp_x * @sin(y)); } } diff --git a/lib/std/math/complex/ldexp.zig b/lib/std/math/complex/ldexp.zig index 1c2d06b858..c196d4afe6 100644 --- a/lib/std/math/complex/ldexp.zig +++ b/lib/std/math/complex/ldexp.zig @@ -45,8 +45,8 @@ fn ldexp_cexp32(z: Complex(f32), expt: i32) Complex(f32) { const scale2 = @bitCast(f32, (0x7f + half_expt2) << 23); return Complex(f32).init( - math.cos(z.im) * exp_x * scale1 * scale2, - math.sin(z.im) * exp_x * scale1 * scale2, + @cos(z.im) * exp_x * scale1 * scale2, + @sin(z.im) * exp_x * scale1 * scale2, ); } @@ -78,7 +78,7 @@ fn ldexp_cexp64(z: Complex(f64), expt: i32) Complex(f64) { const scale2 = @bitCast(f64, (0x3ff + half_expt2) << (20 + 32)); return Complex(f64).init( - math.cos(z.im) * exp_x * scale1 * scale2, - math.sin(z.im) * exp_x * scale1 * scale2, + @cos(z.im) * exp_x * scale1 * scale2, + @sin(z.im) * exp_x * scale1 * scale2, ); } diff --git a/lib/std/math/complex/sinh.zig b/lib/std/math/complex/sinh.zig index b21f6e59eb..1569565ecc 100644 --- a/lib/std/math/complex/sinh.zig +++ b/lib/std/math/complex/sinh.zig @@ -38,14 +38,14 @@ fn sinh32(z: Complex(f32)) Complex(f32) { } // small x: normal case if (ix < 0x41100000) { - return Complex(f32).init(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y)); + return Complex(f32).init(math.sinh(x) * @cos(y), math.cosh(x) * @sin(y)); } // |x|>= 9, so cosh(x) ~= exp(|x|) if (ix < 0x42b17218) { // x < 88.7: exp(|x|) won't overflow const h = @exp(@fabs(x)) * 0.5; - return Complex(f32).init(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y)); + return Complex(f32).init(math.copysign(f32, h, x) * @cos(y), h * @sin(y)); } // x < 192.7: scale to avoid overflow else if (ix < 0x4340b1e7) { @@ -56,7 +56,7 @@ fn sinh32(z: Complex(f32)) Complex(f32) { // x >= 192.7: result always overflows else { const h = 0x1p127 * x; - return Complex(f32).init(h * math.cos(y), h * h * math.sin(y)); + return Complex(f32).init(h * @cos(y), h * h * @sin(y)); } } @@ -79,7 +79,7 @@ fn sinh32(z: Complex(f32)) Complex(f32) { if (iy >= 0x7f800000) { return Complex(f32).init(x * x, x * (y - y)); } - return Complex(f32).init(x * math.cos(y), math.inf(f32) * math.sin(y)); + return Complex(f32).init(x * @cos(y), math.inf(f32) * @sin(y)); } return Complex(f32).init((x * x) * (y - y), (x + x) * (y - y)); @@ -105,14 +105,14 @@ fn sinh64(z: Complex(f64)) Complex(f64) { } // small x: normal case if (ix < 0x40360000) { - return Complex(f64).init(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y)); + return Complex(f64).init(math.sinh(x) * @cos(y), math.cosh(x) * @sin(y)); } // |x|>= 22, so cosh(x) ~= exp(|x|) if (ix < 0x40862e42) { // x < 710: exp(|x|) won't overflow const h = @exp(@fabs(x)) * 0.5; - return Complex(f64).init(math.copysign(f64, h, x) * math.cos(y), h * math.sin(y)); + return Complex(f64).init(math.copysign(f64, h, x) * @cos(y), h * @sin(y)); } // x < 1455: scale to avoid overflow else if (ix < 0x4096bbaa) { @@ -123,7 +123,7 @@ fn sinh64(z: Complex(f64)) Complex(f64) { // x >= 1455: result always overflows else { const h = 0x1p1023 * x; - return Complex(f64).init(h * math.cos(y), h * h * math.sin(y)); + return Complex(f64).init(h * @cos(y), h * h * @sin(y)); } } @@ -146,7 +146,7 @@ fn sinh64(z: Complex(f64)) Complex(f64) { if (iy >= 0x7ff00000) { return Complex(f64).init(x * x, x * (y - y)); } - return Complex(f64).init(x * math.cos(y), math.inf(f64) * math.sin(y)); + return Complex(f64).init(x * @cos(y), math.inf(f64) * @sin(y)); } return Complex(f64).init((x * x) * (y - y), (x + x) * (y - y)); diff --git a/lib/std/math/complex/tanh.zig b/lib/std/math/complex/tanh.zig index d5195d6c73..2ed2cb9609 100644 --- a/lib/std/math/complex/tanh.zig +++ b/lib/std/math/complex/tanh.zig @@ -33,7 +33,7 @@ fn tanh32(z: Complex(f32)) Complex(f32) { return Complex(f32).init(x, r); } const xx = @bitCast(f32, hx - 0x40000000); - const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y); + const r = if (math.isInf(y)) y else @sin(y) * @cos(y); return Complex(f32).init(xx, math.copysign(f32, 0, r)); } @@ -45,7 +45,7 @@ fn tanh32(z: Complex(f32)) Complex(f32) { // x >= 11 if (ix >= 0x41300000) { const exp_mx = @exp(-@fabs(x)); - return Complex(f32).init(math.copysign(f32, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx); + return Complex(f32).init(math.copysign(f32, 1, x), 4 * @sin(y) * @cos(y) * exp_mx * exp_mx); } // Kahan's algorithm @@ -76,7 +76,7 @@ fn tanh64(z: Complex(f64)) Complex(f64) { } const xx = @bitCast(f64, (@as(u64, hx - 0x40000000) << 32) | lx); - const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y); + const r = if (math.isInf(y)) y else @sin(y) * @cos(y); return Complex(f64).init(xx, math.copysign(f64, 0, r)); } @@ -88,7 +88,7 @@ fn tanh64(z: Complex(f64)) Complex(f64) { // x >= 22 if (ix >= 0x40360000) { const exp_mx = @exp(-@fabs(x)); - return Complex(f64).init(math.copysign(f64, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx); + return Complex(f64).init(math.copysign(f64, 1, x), 4 * @sin(y) * @cos(y) * exp_mx * exp_mx); } // Kahan's algorithm From 0b2ed45f5fd69cb5f3afb3118726b17859bda0f5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Apr 2022 20:24:08 -0700 Subject: [PATCH 1259/2031] compiler-rt: inline sin and cos into sincos This avoids the optimizer turning sincos into a recursive call to itself. --- lib/std/special/compiler_rt/sincos.zig | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/std/special/compiler_rt/sincos.zig b/lib/std/special/compiler_rt/sincos.zig index fae326f182..0518e021ea 100644 --- a/lib/std/special/compiler_rt/sincos.zig +++ b/lib/std/special/compiler_rt/sincos.zig @@ -1,24 +1,27 @@ +const sin = @import("sin.zig"); +const cos = @import("cos.zig"); + pub fn __sincosh(a: f16, r_sin: *f16, r_cos: *f16) callconv(.C) void { - r_sin.* = @sin(a); - r_cos.* = @cos(a); + r_sin.* = sin.__sinh(a); + r_cos.* = cos.__cosh(a); } pub fn sincosf(a: f32, r_sin: *f32, r_cos: *f32) callconv(.C) void { - r_sin.* = @sin(a); - r_cos.* = @cos(a); + r_sin.* = sin.sinf(a); + r_cos.* = cos.cosf(a); } pub fn sincos(a: f64, r_sin: *f64, r_cos: *f64) callconv(.C) void { - r_sin.* = @sin(a); - r_cos.* = @cos(a); + r_sin.* = sin.sin(a); + r_cos.* = cos.cos(a); } pub fn __sincosx(a: f80, r_sin: *f80, r_cos: *f80) callconv(.C) void { - r_sin.* = @sin(a); - r_cos.* = @cos(a); + r_sin.* = sin.__sinx(a); + r_cos.* = cos.__cosx(a); } pub fn sincosq(a: f128, r_sin: *f128, r_cos: *f128) callconv(.C) void { - r_sin.* = @sin(a); - r_cos.* = @cos(a); + r_sin.* = sin.sinq(a); + r_cos.* = cos.cosq(a); } From f7f03c699d2dee4c3eec2108e159bbe300e24c6f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Apr 2022 22:37:07 -0700 Subject: [PATCH 1260/2031] compiler-rt: provide actual sincos implementations --- lib/std/special/compiler_rt/cos.zig | 34 ++-- lib/std/special/compiler_rt/sin.zig | 34 ++-- lib/std/special/compiler_rt/sincos.zig | 245 +++++++++++++++++++++++-- 3 files changed, 264 insertions(+), 49 deletions(-) diff --git a/lib/std/special/compiler_rt/cos.zig b/lib/std/special/compiler_rt/cos.zig index 295f6a47ea..957e5f9c91 100644 --- a/lib/std/special/compiler_rt/cos.zig +++ b/lib/std/special/compiler_rt/cos.zig @@ -2,7 +2,7 @@ const std = @import("std"); const math = std.math; const expect = std.testing.expect; -const kernel = @import("trig.zig"); +const trig = @import("trig.zig"); const rem_pio2 = @import("rem_pio2.zig").rem_pio2; const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; @@ -28,27 +28,27 @@ pub fn cosf(x: f32) callconv(.C) f32 { math.doNotOptimizeAway(x + 0x1p120); return 1.0; } - return kernel.__cosdf(x); + return trig.__cosdf(x); } if (ix <= 0x407b53d1) { // |x| ~<= 5*pi/4 if (ix > 0x4016cbe3) { // |x| ~> 3*pi/4 - return -kernel.__cosdf(if (sign) x + c2pio2 else x - c2pio2); + return -trig.__cosdf(if (sign) x + c2pio2 else x - c2pio2); } else { if (sign) { - return kernel.__sindf(x + c1pio2); + return trig.__sindf(x + c1pio2); } else { - return kernel.__sindf(c1pio2 - x); + return trig.__sindf(c1pio2 - x); } } } if (ix <= 0x40e231d5) { // |x| ~<= 9*pi/4 if (ix > 0x40afeddf) { // |x| ~> 7*pi/4 - return kernel.__cosdf(if (sign) x + c4pio2 else x - c4pio2); + return trig.__cosdf(if (sign) x + c4pio2 else x - c4pio2); } else { if (sign) { - return kernel.__sindf(-x - c3pio2); + return trig.__sindf(-x - c3pio2); } else { - return kernel.__sindf(x - c3pio2); + return trig.__sindf(x - c3pio2); } } } @@ -61,10 +61,10 @@ pub fn cosf(x: f32) callconv(.C) f32 { var y: f64 = undefined; const n = rem_pio2f(x, &y); return switch (n & 3) { - 0 => kernel.__cosdf(y), - 1 => kernel.__sindf(-y), - 2 => -kernel.__cosdf(y), - else => kernel.__sindf(y), + 0 => trig.__cosdf(y), + 1 => trig.__sindf(-y), + 2 => -trig.__cosdf(y), + else => trig.__sindf(y), }; } @@ -79,7 +79,7 @@ pub fn cos(x: f64) callconv(.C) f64 { math.doNotOptimizeAway(x + 0x1p120); return 1.0; } - return kernel.__cos(x, 0); + return trig.__cos(x, 0); } // cos(Inf or NaN) is NaN @@ -90,10 +90,10 @@ pub fn cos(x: f64) callconv(.C) f64 { var y: [2]f64 = undefined; const n = rem_pio2(x, &y); return switch (n & 3) { - 0 => kernel.__cos(y[0], y[1]), - 1 => -kernel.__sin(y[0], y[1], 1), - 2 => -kernel.__cos(y[0], y[1]), - else => kernel.__sin(y[0], y[1], 1), + 0 => trig.__cos(y[0], y[1]), + 1 => -trig.__sin(y[0], y[1], 1), + 2 => -trig.__cos(y[0], y[1]), + else => trig.__sin(y[0], y[1], 1), }; } diff --git a/lib/std/special/compiler_rt/sin.zig b/lib/std/special/compiler_rt/sin.zig index a0876d30a0..3d5572a59f 100644 --- a/lib/std/special/compiler_rt/sin.zig +++ b/lib/std/special/compiler_rt/sin.zig @@ -8,7 +8,7 @@ const std = @import("std"); const math = std.math; const expect = std.testing.expect; -const kernel = @import("trig.zig"); +const trig = @import("trig.zig"); const rem_pio2 = @import("rem_pio2.zig").rem_pio2; const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; @@ -34,27 +34,27 @@ pub fn sinf(x: f32) callconv(.C) f32 { math.doNotOptimizeAway(if (ix < 0x00800000) x / 0x1p120 else x + 0x1p120); return x; } - return kernel.__sindf(x); + return trig.__sindf(x); } if (ix <= 0x407b53d1) { // |x| ~<= 5*pi/4 if (ix <= 0x4016cbe3) { // |x| ~<= 3pi/4 if (sign) { - return -kernel.__cosdf(x + s1pio2); + return -trig.__cosdf(x + s1pio2); } else { - return kernel.__cosdf(x - s1pio2); + return trig.__cosdf(x - s1pio2); } } - return kernel.__sindf(if (sign) -(x + s2pio2) else -(x - s2pio2)); + return trig.__sindf(if (sign) -(x + s2pio2) else -(x - s2pio2)); } if (ix <= 0x40e231d5) { // |x| ~<= 9*pi/4 if (ix <= 0x40afeddf) { // |x| ~<= 7*pi/4 if (sign) { - return kernel.__cosdf(x + s3pio2); + return trig.__cosdf(x + s3pio2); } else { - return -kernel.__cosdf(x - s3pio2); + return -trig.__cosdf(x - s3pio2); } } - return kernel.__sindf(if (sign) x + s4pio2 else x - s4pio2); + return trig.__sindf(if (sign) x + s4pio2 else x - s4pio2); } // sin(Inf or NaN) is NaN @@ -65,10 +65,10 @@ pub fn sinf(x: f32) callconv(.C) f32 { var y: f64 = undefined; const n = rem_pio2f(x, &y); return switch (n & 3) { - 0 => kernel.__sindf(y), - 1 => kernel.__cosdf(y), - 2 => kernel.__sindf(-y), - else => -kernel.__cosdf(y), + 0 => trig.__sindf(y), + 1 => trig.__cosdf(y), + 2 => trig.__sindf(-y), + else => -trig.__cosdf(y), }; } @@ -83,7 +83,7 @@ pub fn sin(x: f64) callconv(.C) f64 { math.doNotOptimizeAway(if (ix < 0x00100000) x / 0x1p120 else x + 0x1p120); return x; } - return kernel.__sin(x, 0.0, 0); + return trig.__sin(x, 0.0, 0); } // sin(Inf or NaN) is NaN @@ -94,10 +94,10 @@ pub fn sin(x: f64) callconv(.C) f64 { var y: [2]f64 = undefined; const n = rem_pio2(x, &y); return switch (n & 3) { - 0 => kernel.__sin(y[0], y[1], 1), - 1 => kernel.__cos(y[0], y[1]), - 2 => -kernel.__sin(y[0], y[1], 1), - else => -kernel.__cos(y[0], y[1]), + 0 => trig.__sin(y[0], y[1], 1), + 1 => trig.__cos(y[0], y[1]), + 2 => -trig.__sin(y[0], y[1], 1), + else => -trig.__cos(y[0], y[1]), }; } diff --git a/lib/std/special/compiler_rt/sincos.zig b/lib/std/special/compiler_rt/sincos.zig index 0518e021ea..31ebd0d1d0 100644 --- a/lib/std/special/compiler_rt/sincos.zig +++ b/lib/std/special/compiler_rt/sincos.zig @@ -1,27 +1,242 @@ +const std = @import("std"); +const math = std.math; const sin = @import("sin.zig"); const cos = @import("cos.zig"); +const trig = @import("trig.zig"); +const rem_pio2 = @import("rem_pio2.zig").rem_pio2; +const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; -pub fn __sincosh(a: f16, r_sin: *f16, r_cos: *f16) callconv(.C) void { - r_sin.* = sin.__sinh(a); - r_cos.* = cos.__cosh(a); +pub fn __sincosh(x: f16, r_sin: *f16, r_cos: *f16) callconv(.C) void { + // TODO: more efficient implementation + var big_sin: f32 = undefined; + var big_cos: f32 = undefined; + sincosf(x, &big_sin, &big_cos); + r_sin.* = @floatCast(f16, big_sin); + r_cos.* = @floatCast(f16, big_cos); } -pub fn sincosf(a: f32, r_sin: *f32, r_cos: *f32) callconv(.C) void { - r_sin.* = sin.sinf(a); - r_cos.* = cos.cosf(a); +pub fn sincosf(x: f32, r_sin: *f32, r_cos: *f32) callconv(.C) void { + const sc1pio2: f64 = 1.0 * math.pi / 2.0; // 0x3FF921FB, 0x54442D18 + const sc2pio2: f64 = 2.0 * math.pi / 2.0; // 0x400921FB, 0x54442D18 + const sc3pio2: f64 = 3.0 * math.pi / 2.0; // 0x4012D97C, 0x7F3321D2 + const sc4pio2: f64 = 4.0 * math.pi / 2.0; // 0x401921FB, 0x54442D18 + + const pre_ix = @bitCast(u32, x); + const sign = pre_ix >> 31 != 0; + const ix = pre_ix & 0x7fffffff; + + // |x| ~<= pi/4 + if (ix <= 0x3f490fda) { + // |x| < 2**-12 + if (ix < 0x39800000) { + // raise inexact if x!=0 and underflow if subnormal + math.doNotOptimizeAway(if (ix < 0x00100000) x / 0x1p120 else x + 0x1p120); + r_sin.* = x; + r_cos.* = 1.0; + return; + } + r_sin.* = trig.__sindf(x); + r_cos.* = trig.__cosdf(x); + return; + } + + // |x| ~<= 5*pi/4 + if (ix <= 0x407b53d1) { + // |x| ~<= 3pi/4 + if (ix <= 0x4016cbe3) { + if (sign) { + r_sin.* = -trig.__cosdf(x + sc1pio2); + r_cos.* = trig.__sindf(x + sc1pio2); + } else { + r_sin.* = trig.__cosdf(sc1pio2 - x); + r_cos.* = trig.__sindf(sc1pio2 - x); + } + return; + } + // -sin(x+c) is not correct if x+c could be 0: -0 vs +0 + r_sin.* = -trig.__sindf(if (sign) x + sc2pio2 else x - sc2pio2); + r_cos.* = -trig.__cosdf(if (sign) x + sc2pio2 else x - sc2pio2); + return; + } + + // |x| ~<= 9*pi/4 + if (ix <= 0x40e231d5) { + // |x| ~<= 7*pi/4 + if (ix <= 0x40afeddf) { + if (sign) { + r_sin.* = trig.__cosdf(x + sc3pio2); + r_cos.* = -trig.__sindf(x + sc3pio2); + } else { + r_sin.* = -trig.__cosdf(x - sc3pio2); + r_cos.* = trig.__sindf(x - sc3pio2); + } + return; + } + r_sin.* = trig.__sindf(if (sign) x + sc4pio2 else x - sc4pio2); + r_cos.* = trig.__cosdf(if (sign) x + sc4pio2 else x - sc4pio2); + return; + } + + // sin(Inf or NaN) is NaN + if (ix >= 0x7f800000) { + const result = x - x; + r_sin.* = result; + r_cos.* = result; + return; + } + + // general argument reduction needed + var y: f64 = undefined; + const n = rem_pio2f(x, &y); + const s = trig.__sindf(y); + const c = trig.__cosdf(y); + switch (n & 3) { + 0 => { + r_sin.* = s; + r_cos.* = c; + }, + 1 => { + r_sin.* = c; + r_cos.* = -s; + }, + 2 => { + r_sin.* = -s; + r_cos.* = -c; + }, + else => { + r_sin.* = -c; + r_cos.* = s; + }, + } } -pub fn sincos(a: f64, r_sin: *f64, r_cos: *f64) callconv(.C) void { - r_sin.* = sin.sin(a); - r_cos.* = cos.cos(a); +pub fn sincos(x: f64, r_sin: *f64, r_cos: *f64) callconv(.C) void { + const ix = @truncate(u32, @bitCast(u64, x) >> 32) & 0x7fffffff; + + // |x| ~< pi/4 + if (ix <= 0x3fe921fb) { + // if |x| < 2**-27 * sqrt(2) + if (ix < 0x3e46a09e) { + // raise inexact if x != 0 and underflow if subnormal + math.doNotOptimizeAway(if (ix < 0x00100000) x / 0x1p120 else x + 0x1p120); + r_sin.* = x; + r_cos.* = 1.0; + return; + } + r_sin.* = trig.__sin(x, 0.0, 0); + r_cos.* = trig.__cos(x, 0.0); + return; + } + + // sincos(Inf or NaN) is NaN + if (ix >= 0x7ff00000) { + const result = x - x; + r_sin.* = result; + r_cos.* = result; + return; + } + + // argument reduction needed + var y: [2]f64 = undefined; + const n = rem_pio2(x, &y); + const s = trig.__sin(y[0], y[1], 1); + const c = trig.__cos(y[0], y[1]); + switch (n & 3) { + 0 => { + r_sin.* = s; + r_cos.* = c; + }, + 1 => { + r_sin.* = c; + r_cos.* = -s; + }, + 2 => { + r_sin.* = -s; + r_cos.* = -c; + }, + else => { + r_sin.* = -c; + r_cos.* = s; + }, + } } -pub fn __sincosx(a: f80, r_sin: *f80, r_cos: *f80) callconv(.C) void { - r_sin.* = sin.__sinx(a); - r_cos.* = cos.__cosx(a); +pub fn __sincosx(x: f80, r_sin: *f80, r_cos: *f80) callconv(.C) void { + // TODO: more efficient implementation + //return sincos_generic(f80, x, r_sin, r_cos); + var big_sin: f128 = undefined; + var big_cos: f128 = undefined; + sincosq(x, &big_sin, &big_cos); + r_sin.* = @floatCast(f80, big_sin); + r_cos.* = @floatCast(f80, big_cos); } -pub fn sincosq(a: f128, r_sin: *f128, r_cos: *f128) callconv(.C) void { - r_sin.* = sin.sinq(a); - r_cos.* = cos.cosq(a); +pub fn sincosq(x: f128, r_sin: *f128, r_cos: *f128) callconv(.C) void { + // TODO: more correct implementation + //return sincos_generic(f128, x, r_sin, r_cos); + var small_sin: f64 = undefined; + var small_cos: f64 = undefined; + sincos(@floatCast(f64, x), &small_sin, &small_cos); + r_sin.* = small_sin; + r_cos.* = small_cos; +} + +const rem_pio2_generic = @compileError("TODO"); + +/// Ported from musl sincosl.c. Needs the following dependencies to be complete: +/// * rem_pio2_generic ported from __rem_pio2l.c +/// * trig.sin_generic ported from __sinl.c +/// * trig.cos_generic ported from __cosl.c +inline fn sincos_generic(comptime F: type, x: F, r_sin: *F, r_cos: *F) void { + const sc1pio4: F = 1.0 * math.pi / 4.0; + const bits = @typeInfo(F).Float.bits; + const I = std.meta.Int(.unsigned, bits); + const ix = @bitCast(I, x) & (math.maxInt(I) >> 1); + const se = @truncate(u16, ix >> (bits - 16)); + + if (se == 0x7fff) { + const result = x - x; + r_sin.* = result; + r_cos.* = result; + return; + } + + if (@bitCast(F, ix) < sc1pio4) { + if (se < 0x3fff - math.floatFractionalBits(F) - 1) { + // raise underflow if subnormal + if (se == 0) { + math.doNotOptimizeAway(x * 0x1p-120); + } + r_sin.* = x; + // raise inexact if x!=0 + r_cos.* = 1.0 + x; + return; + } + r_sin.* = trig.sin_generic(F, x, 0, 0); + r_cos.* = trig.cos_generic(F, x, 0); + return; + } + + var y: [2]F = undefined; + const n = rem_pio2_generic(F, x, &y); + const s = trig.sin_generic(F, y[0], y[1], 1); + const c = trig.cos_generic(F, y[0], y[1]); + switch (n & 3) { + 0 => { + r_sin.* = s; + r_cos.* = c; + }, + 1 => { + r_sin.* = c; + r_cos.* = -s; + }, + 2 => { + r_sin.* = -s; + r_cos.* = -c; + }, + else => { + r_sin.* = -c; + r_cos.* = s; + }, + } } From 9d098657a069b36e3eed9bc63c3421c031be7348 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Apr 2022 22:57:12 -0700 Subject: [PATCH 1261/2031] stage1: fix i386-windows f80 sizeof/alignof --- src/stage1/codegen.cpp | 7 +++++-- test/behavior/math.zig | 6 ------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index c575aff53d..9d46a660bc 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -9586,10 +9586,13 @@ static void define_builtin_types(CodeGen *g) { switch (g->zig_target->arch) { case ZigLLVM_x86: case ZigLLVM_x86_64: - if (g->zig_target->abi != ZigLLVM_MSVC) + if (g->zig_target->abi != ZigLLVM_MSVC) { add_fp_entry(g, "c_longdouble", 80, LLVMX86FP80Type(), &g->builtin_types.entry_c_longdouble); - else + g->builtin_types.entry_c_longdouble->abi_size = g->builtin_types.entry_f80->abi_size; + g->builtin_types.entry_c_longdouble->abi_align = g->builtin_types.entry_f80->abi_align; + } else { add_fp_entry(g, "c_longdouble", 64, LLVMDoubleType(), &g->builtin_types.entry_c_longdouble); + } break; case ZigLLVM_arm: case ZigLLVM_armeb: diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 85cf7f5643..0479015eee 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1416,12 +1416,6 @@ test "fabs" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage1 and builtin.os.tag == .windows and - builtin.cpu.arch == .i386) - { - return error.SkipZigTest; - } - inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| { // normals try expect(@fabs(@as(T, 1.0)) == 1.0); From 11911f55a73a49e2fda85bddd38d1993b93547c9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Apr 2022 23:33:16 -0700 Subject: [PATCH 1262/2031] stage1: fix incorrect struct padding Before this change, struct {f80, f80} targeting i386-windows-msvc lowers to ```llvm %"std.testing.struct:78:61.6" = type { x86_fp80, [6 x i8], x86_fp80, [6 x i8] } ``` which has an incorrect ABI size of 40. After this change, the struct lowers to ```llvm %"std.testing.struct:78:61.6" = type { x86_fp80, [4 x i8], x86_fp80, [4 x i8] } ``` which has the correct ABI size of 32, and properly aligns the second field to 16 bytes. The other place that calculates field padding (lowering of constant values in codegen.cpp) already correctly calls LLVMABISizeOfType rather than LLVMStoreSizeOfType. This fixes the compiler-rt tests for i386-windows in this branch. --- src/stage1/analyze.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp index 6e49c91fd8..aef4966ee7 100644 --- a/src/stage1/analyze.cpp +++ b/src/stage1/analyze.cpp @@ -8928,7 +8928,7 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS assert(next_offset >= llvm_next_offset); if (next_offset > llvm_next_offset) { - size_t pad_bytes = next_offset - (field->offset + LLVMStoreSizeOfType(g->target_data_ref, llvm_type)); + size_t pad_bytes = next_offset - (field->offset + LLVMABISizeOfType(g->target_data_ref, llvm_type)); if (pad_bytes != 0) { LLVMTypeRef pad_llvm_type = LLVMArrayType(LLVMInt8Type(), pad_bytes); element_types[gen_field_index] = pad_llvm_type; From 095d51164f53ee7f23aae9dbe08270eacf61d97b Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 28 Apr 2022 17:45:33 +0300 Subject: [PATCH 1263/2031] Sema: fix slice field modification at comptime --- src/Sema.zig | 131 ++++++++++++++++++++++++++++------------ src/value.zig | 12 ++++ test/behavior/slice.zig | 11 ++++ 3 files changed, 114 insertions(+), 40 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 8abcbd47ed..f341ae727d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -16979,44 +16979,44 @@ fn fieldPtr( const buf = try sema.arena.create(Type.SlicePtrFieldTypeBuffer); const slice_ptr_ty = inner_ty.slicePtrFieldType(buf); - if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| { - var anon_decl = try block.startAnonDecl(src); - defer anon_decl.deinit(); - - return sema.analyzeDeclRef(try anon_decl.finish( - try slice_ptr_ty.copy(anon_decl.arena()), - try val.slicePtr().copy(anon_decl.arena()), - 0, // default alignment - )); - } - try sema.requireRuntimeBlock(block, src); - const result_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = slice_ptr_ty, .mutable = object_ptr_ty.ptrIsMutable(), .@"addrspace" = object_ptr_ty.ptrAddressSpace(), }); - return block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr); - } else if (mem.eql(u8, field_name, "len")) { if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| { - var anon_decl = try block.startAnonDecl(src); - defer anon_decl.deinit(); - - return sema.analyzeDeclRef(try anon_decl.finish( - Type.usize, - try Value.Tag.int_u64.create(anon_decl.arena(), val.sliceLen(sema.mod)), - 0, // default alignment - )); + return sema.addConstant( + result_ty, + try Value.Tag.field_ptr.create(sema.arena, .{ + .container_ptr = val, + .container_ty = inner_ty, + .field_index = Value.Payload.Slice.ptr_index, + }), + ); } try sema.requireRuntimeBlock(block, src); + return block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr); + } else if (mem.eql(u8, field_name, "len")) { const result_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = Type.usize, .mutable = object_ptr_ty.ptrIsMutable(), .@"addrspace" = object_ptr_ty.ptrAddressSpace(), }); + if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| { + return sema.addConstant( + result_ty, + try Value.Tag.field_ptr.create(sema.arena, .{ + .container_ptr = val, + .container_ty = inner_ty, + .field_index = Value.Payload.Slice.len_index, + }), + ); + } + try sema.requireRuntimeBlock(block, src); + return block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr); } else { return sema.fail( @@ -19297,7 +19297,6 @@ fn beginComptimePtrMutation( const field_ptr = ptr_val.castTag(.field_ptr).?.data; var parent = try beginComptimePtrMutation(sema, block, src, field_ptr.container_ptr); const field_index = @intCast(u32, field_ptr.field_index); - const field_ty = parent.ty.structFieldType(field_index); switch (parent.val.tag()) { .undef => { // A struct or union has been initialized to undefined at comptime and now we @@ -19316,7 +19315,7 @@ fn beginComptimePtrMutation( return ComptimePtrMutationKit{ .decl_ref_mut = parent.decl_ref_mut, .val = &fields[field_index], - .ty = field_ty, + .ty = parent.ty.structFieldType(field_index), }; }, .Union => { @@ -19331,16 +19330,37 @@ fn beginComptimePtrMutation( return ComptimePtrMutationKit{ .decl_ref_mut = parent.decl_ref_mut, .val = &payload.data.val, - .ty = field_ty, + .ty = parent.ty.structFieldType(field_index), }; }, + .Pointer => { + assert(parent.ty.isSlice()); + parent.val.* = try Value.Tag.slice.create(arena, .{ + .ptr = Value.undef, + .len = Value.undef, + }); + + switch (field_index) { + Value.Payload.Slice.ptr_index => return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .val = &parent.val.castTag(.slice).?.data.ptr, + .ty = parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)), + }, + Value.Payload.Slice.len_index => return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .val = &parent.val.castTag(.slice).?.data.len, + .ty = Type.usize, + }, + else => unreachable, + } + }, else => unreachable, } }, .aggregate => return ComptimePtrMutationKit{ .decl_ref_mut = parent.decl_ref_mut, .val = &parent.val.castTag(.aggregate).?.data[field_index], - .ty = field_ty, + .ty = parent.ty.structFieldType(field_index), }, .@"union" => { // We need to set the active field of the union. @@ -19353,9 +19373,22 @@ fn beginComptimePtrMutation( return ComptimePtrMutationKit{ .decl_ref_mut = parent.decl_ref_mut, .val = &payload.val, - .ty = field_ty, + .ty = parent.ty.structFieldType(field_index), }; }, + .slice => switch (field_index) { + Value.Payload.Slice.ptr_index => return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .val = &parent.val.castTag(.slice).?.data.ptr, + .ty = parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)), + }, + Value.Payload.Slice.len_index => return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .val = &parent.val.castTag(.slice).?.data.len, + .ty = Type.usize, + }, + else => unreachable, + }, else => unreachable, } @@ -19555,7 +19588,6 @@ fn beginComptimePtrLoad( .field_ptr => blk: { const field_ptr = ptr_val.castTag(.field_ptr).?.data; const field_index = @intCast(u32, field_ptr.field_index); - const field_ty = field_ptr.container_ty.structFieldType(field_index); var deref = try beginComptimePtrLoad(sema, block, src, field_ptr.container_ptr, field_ptr.container_ty); if (field_ptr.container_ty.hasWellDefinedLayout()) { @@ -19570,19 +19602,38 @@ fn beginComptimePtrLoad( deref.ty_without_well_defined_layout = field_ptr.container_ty; } - if (deref.pointee) |*tv| { - const coerce_in_mem_ok = - (try sema.coerceInMemoryAllowed(block, field_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or - (try sema.coerceInMemoryAllowed(block, tv.ty, field_ptr.container_ty, false, target, src, src)) == .ok; - if (coerce_in_mem_ok) { - deref.pointee = TypedValue{ - .ty = field_ty, - .val = tv.val.fieldValue(tv.ty, field_index), - }; - break :blk deref; - } + const tv = &(deref.pointee orelse { + deref.pointee = null; + break :blk deref; + }); + const coerce_in_mem_ok = + (try sema.coerceInMemoryAllowed(block, field_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or + (try sema.coerceInMemoryAllowed(block, tv.ty, field_ptr.container_ty, false, target, src, src)) == .ok; + if (!coerce_in_mem_ok) { + deref.pointee = null; + break :blk deref; + } + + if (field_ptr.container_ty.isSlice()) { + const slice_val = tv.val.castTag(.slice).?.data; + deref.pointee = switch (field_index) { + Value.Payload.Slice.ptr_index => TypedValue{ + .ty = field_ptr.container_ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)), + .val = slice_val.ptr, + }, + Value.Payload.Slice.len_index => TypedValue{ + .ty = Type.usize, + .val = slice_val.len, + }, + else => unreachable, + }; + } else { + const field_ty = field_ptr.container_ty.structFieldType(field_index); + deref.pointee = TypedValue{ + .ty = field_ty, + .val = tv.val.fieldValue(tv.ty, field_index), + }; } - deref.pointee = null; break :blk deref; }, diff --git a/src/value.zig b/src/value.zig index bb7b742290..7cd19c5d25 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2542,6 +2542,15 @@ pub const Value = extern union { return 1; } }, + .decl_ref_mut => { + const decl_index = val.castTag(.decl_ref_mut).?.data.decl_index; + const decl = mod.declPtr(decl_index); + if (decl.ty.zigTypeTag() == .Array) { + return decl.ty.arrayLen(); + } else { + return 1; + } + }, else => unreachable, }; } @@ -5116,6 +5125,9 @@ pub const Value = extern union { ptr: Value, len: Value, }, + + pub const ptr_index = 0; + pub const len_index = 1; }; pub const Ty = struct { diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 09d15e3ac5..18c769f27c 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -682,3 +682,14 @@ test "slicing slice with sentinel as end index" { try S.do(); comptime try S.do(); } + +test "slice len modification at comptime" { + comptime { + var buf: [10]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + var items: []u8 = buf[0..0]; + items.len += 2; + try expect(items.len == 2); + try expect(items[0] == 0); + try expect(items[1] == 1); + } +} From f8940a05aeb347deb858b848e33f73ef285c3298 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 28 Apr 2022 17:48:05 +0300 Subject: [PATCH 1264/2031] TypedValue: pretty print slices --- src/TypedValue.zig | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/TypedValue.zig b/src/TypedValue.zig index b0d5d77010..43c26b254e 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -146,7 +146,8 @@ pub fn print( if (ty.zigTypeTag() == .Struct) { try writer.writeAll(".{ "); const struct_fields = ty.structFields(); - const max_len = std.math.min(struct_fields.count(), max_aggregate_items); + const len = struct_fields.count(); + const max_len = std.math.min(len, max_aggregate_items); const field_names = struct_fields.keys(); const fields = struct_fields.values(); @@ -160,11 +161,15 @@ pub fn print( .val = vals[i], }, writer, level - 1, mod); } + if (len > max_aggregate_items) { + try writer.writeAll(", ..."); + } return writer.writeAll(" }"); } else { try writer.writeAll(".{ "); const elem_ty = ty.elemType2(); - const max_len = std.math.min(ty.arrayLen(), max_aggregate_items); + const len = ty.arrayLen(); + const max_len = std.math.min(len, max_aggregate_items); var i: u32 = 0; while (i < max_len) : (i += 1) { @@ -174,6 +179,9 @@ pub fn print( .val = vals[i], }, writer, level - 1, mod); } + if (len > max_aggregate_items) { + try writer.writeAll(", ..."); + } return writer.writeAll(" }"); } }, @@ -292,10 +300,15 @@ pub fn print( .ty = ty.elemType2(), .val = val.castTag(.repeated).?.data, }; - while (i < max_aggregate_items) : (i += 1) { + const len = ty.arrayLen(); + const max_len = std.math.min(len, max_aggregate_items); + while (i < max_len) : (i += 1) { if (i != 0) try writer.writeAll(", "); try print(elem_tv, writer, level - 1, mod); } + if (len > max_aggregate_items) { + try writer.writeAll(", ..."); + } return writer.writeAll(" }"); }, .empty_array_sentinel => { @@ -309,7 +322,27 @@ pub fn print( }, writer, level - 1, mod); return writer.writeAll(" }"); }, - .slice => return writer.writeAll("(slice)"), + .slice => { + const payload = val.castTag(.slice).?.data; + try writer.writeAll(".{ "); + const elem_ty = ty.elemType2(); + const len = payload.len.toUnsignedInt(target); + const max_len = std.math.min(len, max_aggregate_items); + + var i: u32 = 0; + while (i < max_len) : (i += 1) { + if (i != 0) try writer.writeAll(", "); + var buf: Value.ElemValueBuffer = undefined; + try print(.{ + .ty = elem_ty, + .val = payload.ptr.elemValueBuffer(mod, i, &buf), + }, writer, level - 1, mod); + } + if (len > max_aggregate_items) { + try writer.writeAll(", ..."); + } + return writer.writeAll(" }"); + }, .float_16 => return writer.print("{}", .{val.castTag(.float_16).?.data}), .float_32 => return writer.print("{}", .{val.castTag(.float_32).?.data}), .float_64 => return writer.print("{}", .{val.castTag(.float_64).?.data}), From f7bc8900bfef87f67230ac5af7edb2d7766b3ba3 Mon Sep 17 00:00:00 2001 From: Erik Hugne Date: Mon, 25 Apr 2022 22:45:12 +0200 Subject: [PATCH 1265/2031] std.coff: parse out codebase and entrypoint from optionalheader --- lib/std/coff.zig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/std/coff.zig b/lib/std/coff.zig index 8fabd8e214..7c077a9ec8 100644 --- a/lib/std/coff.zig +++ b/lib/std/coff.zig @@ -197,7 +197,11 @@ pub const Coff = struct { const opt_header_pos = try self.in_file.getPos(); self.pe_header.magic = try in.readIntLittle(u16); - // All we care about is the image base value and PDB info + try self.in_file.seekTo(opt_header_pos + 16); + self.pe_header.entry_addr = try in.readIntLittle(u32); + try self.in_file.seekTo(opt_header_pos + 20); + self.pe_header.code_base = try in.readIntLittle(u32); + // The header structure is different for 32 or 64 bit var num_rva_pos: u64 = undefined; if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { @@ -374,6 +378,8 @@ const OptionalHeader = struct { magic: u16, data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory, + entry_addr: u32, + code_base: u32, image_base: u64, }; From 8e3add8736be683b450c2754bedb064811baed0e Mon Sep 17 00:00:00 2001 From: Erik Hugne Date: Thu, 28 Apr 2022 17:34:15 +0200 Subject: [PATCH 1266/2031] std.build: make no_dll_export_fns accessible in build step --- lib/std/build.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/std/build.zig b/lib/std/build.zig index 5966002799..f4ef5be888 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1583,6 +1583,7 @@ pub const LibExeObjStep = struct { red_zone: ?bool = null, omit_frame_pointer: ?bool = null, + no_dll_export_fns: bool = false, subsystem: ?std.Target.SubSystem = null, @@ -2639,6 +2640,9 @@ pub const LibExeObjStep = struct { try zig_args.append("-fno-omit-frame-pointer"); } } + if (self.no_dll_export_fns) { + try zig_args.append("-fno-dll-export-fns"); + } if (self.disable_sanitize_c) { try zig_args.append("-fno-sanitize-c"); } From 7f13f5cd5f5a518638b15d7225eae2d88ec1efb5 Mon Sep 17 00:00:00 2001 From: matu3ba Date: Thu, 28 Apr 2022 17:37:30 +0200 Subject: [PATCH 1267/2031] std.testing: add writeZigFile for TmpDir * remove need for manual string concatenation for building binaries in test blocks * include small program snippet to show how to get binary path with subslicing --- lib/std/child_process.zig | 17 +++++------------ lib/std/testing.zig | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index f2b978ba9f..f3c35dde2f 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -1357,23 +1357,16 @@ test "build and call child_process" { var it = try std.process.argsWithAllocator(allocator); defer it.deinit(); // no-op unless WASI or Windows const testargs = try testing.getTestArgs(&it); - var tmp = testing.tmpDir(.{ .no_follow = true }); // ie zig-cache/tmp/8DLgoSEqz593PAEE defer tmp.cleanup(); - const tmpdirpath = try tmp.getFullPath(allocator); - defer allocator.free(tmpdirpath); const child_name = "child"; // no need for suffixes (.exe, .wasm) due to '-femit-bin' - const suffix_zig = ".zig"; - const child_path = try fs.path.join(allocator, &[_][]const u8{ tmpdirpath, child_name }); - defer allocator.free(child_path); - const child_zig = try mem.concat(allocator, u8, &[_][]const u8{ child_path, suffix_zig }); - defer allocator.free(child_zig); - - try tmp.dir.writeFile("child.zig", childstr); - try testing.buildExe(testargs.zigexec, child_zig, child_path); + const zigfile_path = try tmp.writeZigFile(allocator, childstr, child_name); + defer allocator.free(zigfile_path); + const binary = zigfile_path[0 .. zigfile_path.len - 4]; // '.zig' is 4 characters + try testing.buildExe(testargs.zigexec, zigfile_path, binary); // spawn compiled file as child_process with argument 'hello world' + expect success - const args = [_][]const u8{ child_path, "hello world" }; + const args = [_][]const u8{ binary, "hello world" }; var child_proc = try ChildProcess.init(&args, allocator); defer child_proc.deinit(); const ret_val = try child_proc.spawnAndWait(); diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 004e2d0fa7..df2d6c7a43 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -374,6 +374,43 @@ pub const TmpDir = struct { self.parent_dir.close(); self.* = undefined; } + + /// Writes program string as zig file into tmp directory + /// Caller owns memory + /// + /// ``` + /// const progstr = "pub fn main() void {}\n"; + /// var it = try std.process.argsWithAllocator(std.testing.allocator); + /// defer it.deinit(); // no-op unless WASI or Windows + /// const testargs = try std.testing.getTestArgs(&it); + /// var tmp = std.testing.tmpDir(.{ .no_follow = true }); // ie zig-cache/tmp/8DLgoSEqz593PAEE + /// defer tmp.cleanup(); + /// const zigfile_path = try tmp.writeZigFile(std.testing.allocator, progstr, "bruh"); + /// defer std.testing.allocator.free(zigfile_path); + /// const binary = zigfile_path[0 .. zigfile_path.len - 4]; // '.zig' is 4 characters + /// try std.testing.buildExe(testargs.zigexec, zigfile_path, binary); + /// ``` + pub fn writeZigFile( + self: *TmpDir, + alloc: std.mem.Allocator, + progstr: []const u8, + filename: []const u8, + ) ![]const u8 { + const tmpdir_path = try self.getFullPath(alloc); + defer alloc.free(tmpdir_path); + const suffix_zig = ".zig"; + const zigfile_path = try std.mem.concat(alloc, u8, &[_][]const u8{ + tmpdir_path, + std.fs.path.sep_str, + filename, + suffix_zig, + }); + errdefer alloc.free(zigfile_path); + const zigfile = try std.mem.concat(alloc, u8, &[_][]const u8{ filename, suffix_zig }); + defer alloc.free(zigfile); + try self.dir.writeFile(zigfile, progstr); + return zigfile_path; + } }; fn getCwdOrWasiPreopen() std.fs.Dir { From d5fcb509881e1b022d2bcef303b53b4f67db1c9a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 28 Apr 2022 18:34:37 +0200 Subject: [PATCH 1268/2031] fmt: fix formatting of lib/std/x/net/bpf.zig --- lib/std/x/net/bpf.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/x/net/bpf.zig b/lib/std/x/net/bpf.zig index a99390ab62..e0204b3bc5 100644 --- a/lib/std/x/net/bpf.zig +++ b/lib/std/x/net/bpf.zig @@ -105,7 +105,7 @@ //! (010) ret #262144 ; copy 0x40000 //! (011) ret #0 ; skip packet //! ``` -//! +//! //! Here we can make a few observations: //! //! - The problem "filter only tcp packets" has essentially been transformed @@ -248,7 +248,7 @@ pub const JGT = 0x20; /// jge x, Lt, Lf: pc += (a >= x) ? jt : jf. pub const JGE = 0x30; /// jset #k, Lt, Lf: pc += (a & k > 0) ? jt : jf. -/// jset x, Lt, Lf: pc += (a & x > 0) ? jt : jf. +/// jset x, Lt, Lf: pc += (a & x > 0) ? jt : jf. pub const JSET = 0x40; // Miscellaneous operations/register copy. From d8d12d51ecd9e893755ced927c79eb9a6c036b31 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 26 Apr 2022 15:17:41 +0200 Subject: [PATCH 1269/2031] test: abstract away test manifest parser into separate struct --- src/test.zig | 300 +++++++++++++++++++++++++++++++++++++ test/incremental/add.0.zig | 12 ++ test/incremental/add.1.zig | 14 ++ test/incremental/add.2.zig | 14 ++ 4 files changed, 340 insertions(+) create mode 100644 test/incremental/add.0.zig create mode 100644 test/incremental/add.1.zig create mode 100644 test/incremental/add.2.zig diff --git a/src/test.zig b/src/test.zig index 8c2a15e844..c0a7fe6dc0 100644 --- a/src/test.zig +++ b/src/test.zig @@ -72,6 +72,17 @@ test { } } + { + const dir_path = try std.fs.path.join(arena, &.{ + std.fs.path.dirname(@src().file).?, "..", "test", "incremental", + }); + + var dir = try std.fs.cwd().openDir(dir_path, .{ .iterate = true }); + defer dir.close(); + + ctx.addTestCasesFromDir(dir); + } + try @import("test_cases").addCases(&ctx); try ctx.run(); @@ -154,6 +165,125 @@ const ErrorMsg = union(enum) { } }; +/// Manifest syntax example: +/// (see https://github.com/ziglang/zig/issues/11288) +/// +/// error +/// backend=stage1,stage2 +/// output_mode=exe +/// +/// :3:19: error: foo +/// +/// run +/// target=x86_64-linux,aarch64-macos +/// +/// I am expected stdout! Hello! +/// +/// cli +/// +/// build test +const TestManifest = struct { + @"type": Type, + config_map: std.StringHashMap([]const u8), + trailing_bytes: []const u8 = "", + + const Type = enum { + @"error", + run, + cli, + }; + + const TrailingIterator = struct { + inner: std.mem.TokenIterator(u8), + + fn next(self: *TrailingIterator) ?[]const u8 { + const next_inner = self.inner.next() orelse return null; + return std.mem.trim(u8, next_inner, " \t"); + } + }; + + fn ConfigValueIterator(comptime T: type, comptime ParseFn: type) type { + return struct { + inner: std.mem.SplitIterator(u8), + parse_fn: ParseFn, + + fn next(self: *@This()) ?T { + const next_raw = self.inner.next() orelse return null; + return self.parse_fn(next_raw); + } + }; + } + + fn parse(arena: Allocator, bytes: []const u8) !TestManifest { + var it = std.mem.tokenize(u8, bytes, "\r\n"); + + // First line is the test type + const tt: Type = blk: { + const line = it.next() orelse return error.MissingTestCaseType; + const raw = std.mem.trim(u8, line[2..], " \t"); + if (std.mem.eql(u8, raw, "error")) { + break :blk .@"error"; + } else if (std.mem.eql(u8, raw, "run")) { + break :blk .run; + } else if (std.mem.eql(u8, raw, "cli")) { + break :blk .cli; + } else { + std.log.warn("unknown test case type requested: {s}", .{raw}); + return error.UnknownTestCaseType; + } + }; + + var manifest: TestManifest = .{ + .@"type" = tt, + .config_map = std.StringHashMap([]const u8).init(arena), + }; + + // Any subsequent line until a blank comment line is key=value(s) pair + while (it.next()) |line| { + const trimmed = std.mem.trim(u8, line[2..], " \t"); + if (trimmed.len == 0) break; + + // Parse key=value(s) + var kv_it = std.mem.split(u8, trimmed, "="); + const key = kv_it.next() orelse return error.MissingKeyForConfig; + try manifest.config_map.putNoClobber(key, kv_it.next() orelse return error.MissingValuesForConfig); + } + + // Finally, trailing is expected output + manifest.trailing_bytes = bytes[it.index..]; + + return manifest; + } + + fn getConfigValues( + self: TestManifest, + key: []const u8, + comptime T: type, + parse_fn: anytype, + ) ?ConfigValueIterator(T, @TypeOf(parse_fn)) { + const bytes = self.config_map.get(key) orelse return null; + return ConfigValueIterator(T, @TypeOf(parse_fn)){ + .inner = std.mem.split(u8, bytes, ","), + .parse_fn = parse_fn, + }; + } + + fn trailing(self: TestManifest) TrailingIterator { + return .{ + .inner = std.mem.tokenize(u8, self.trailing_bytes, "\r\n"), + }; + } + + fn trailingAlloc(self: TestManifest, arena: Allocator) ![]const []const u8 { + var out = std.ArrayList([]const u8).init(arena); + var it = self.trailing(); + while (it.next()) |line| { + try out.append(line); + } + return out.toOwnedSlice(); + } +}; + pub const TestContext = struct { arena: Allocator, cases: std.ArrayList(Case), @@ -197,6 +327,10 @@ pub const TestContext = struct { stage1, stage2, llvm, + + fn parse(str: []const u8) ?Backend { + return std.meta.stringToEnum(Backend, str); + } }; /// A `Case` consists of a list of `Update`. The same `Compilation` is used for each @@ -661,6 +795,10 @@ pub const TestContext = struct { /// Execute all tests as incremental updates to a single compilation. Explicitly /// incremental tests ("foo.0.zig", "foo.1.zig", etc.) still execute in order incremental, + + fn parse(str: []const u8) ?Strategy { + return std.meta.stringToEnum(Strategy, str); + } }; /// Adds a compile-error test for each file in the provided directory, using the @@ -689,6 +827,15 @@ pub const TestContext = struct { }; } + pub fn addTestCasesFromDir(ctx: *TestContext, dir: std.fs.Dir) void { + var current_file: []const u8 = "none"; + addTestCasesFromDirInner(ctx, dir, ¤t_file) catch |err| { + std.debug.panic("test harness failed to process file '{s}': {s}\n", .{ + current_file, @errorName(err), + }); + }; + } + /// For a filename in the format ".X." or ".", returns /// "", "" and X parsed as a decimal number. If X is not present, or /// cannot be parsed as a decimal number, it is treated as part of @@ -749,6 +896,159 @@ pub const TestContext = struct { std.sort.sort([]const u8, filenames, Context{}, Context.lessThan); } + fn addTestCasesFromDirInner( + ctx: *TestContext, + dir: std.fs.Dir, + /// This is kept up to date with the currently being processed file so + /// that if any errors occur the caller knows it happened during this file. + current_file: *[]const u8, + ) !void { + var opt_case: ?*Case = null; + + var it = dir.iterate(); + var filenames = std.ArrayList([]const u8).init(ctx.arena); + defer filenames.deinit(); + + while (try it.next()) |entry| { + if (entry.kind != .File) continue; + + // Ignore stuff such as .swp files + switch (Compilation.classifyFileExt(entry.name)) { + .unknown => continue, + else => {}, + } + try filenames.append(try ctx.arena.dupe(u8, entry.name)); + } + + // Sort filenames, so that incremental tests are contiguous and in-order + sortTestFilenames(filenames.items); + + var prev_filename: []const u8 = ""; + for (filenames.items) |filename| { + current_file.* = filename; + + { // First, check if this file is part of an incremental update sequence + + // Split filename into ".." + const prev_parts = getTestFileNameParts(prev_filename); + const new_parts = getTestFileNameParts(filename); + + // If base_name and file_ext match, these files are in the same test sequence + // and the new one should be the incremented version of the previous test + if (std.mem.eql(u8, prev_parts.base_name, new_parts.base_name) and + std.mem.eql(u8, prev_parts.file_ext, new_parts.file_ext)) + { + + // This is "foo.X.zig" followed by "foo.Y.zig". Make sure that X = Y + 1 + if (prev_parts.test_index == null) return error.InvalidIncrementalTestIndex; + if (new_parts.test_index == null) return error.InvalidIncrementalTestIndex; + if (new_parts.test_index.? != prev_parts.test_index.? + 1) return error.InvalidIncrementalTestIndex; + } else { + + // This is not the same test sequence, so the new file must be the first file + // in a new sequence ("*.0.zig") or an independent test file ("*.zig") + if (new_parts.test_index != null and new_parts.test_index.? != 0) return error.InvalidIncrementalTestIndex; + + // if (strategy == .independent) + // opt_case = null; // Generate a new independent test case for this update + } + } + prev_filename = filename; + + const max_file_size = 10 * 1024 * 1024; + const src = try dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0); + + // The manifest is the last contiguous block of comments in the file + // We scan for the beginning by searching backward for the first non-empty line that does not start with "//" + var manifest_start: ?usize = null; + var manifest_end: usize = src.len; + if (src.len > 0) { + var cursor: usize = src.len - 1; + while (true) { + // Move to beginning of line + while (cursor > 0 and src[cursor - 1] != '\n') cursor -= 1; + + if (std.mem.startsWith(u8, src[cursor..], "//")) { + manifest_start = cursor; // Contiguous comment line, include in manifest + } else { + if (manifest_start != null) break; // Encountered non-comment line, end of manifest + + // We ignore all-whitespace lines following the comment block, but anything else + // means that there is no manifest present. + if (std.mem.trim(u8, src[cursor..manifest_end], " \r\n\t").len == 0) { + manifest_end = cursor; + } else break; // If it's not whitespace, there is no manifest + } + + // Move to previous line + if (cursor != 0) cursor -= 1 else break; + } + } + + if (manifest_start) |start| { + // Parse the manifest + var mani = try TestManifest.parse(ctx.arena, src[start..manifest_end]); + const strategy = mani.getConfigValues("strategy", Strategy, Strategy.parse).?.next().?; + const backend = mani.getConfigValues("backend", Backend, Backend.parse).?.next().?; + + switch (mani.@"type") { + .@"error" => { + const case = opt_case orelse case: { + const case = try ctx.cases.addOne(); + case.* = .{ + .name = "none", + .target = .{}, + .backend = backend, + .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), + .is_test = false, + .output_mode = .Obj, + .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), + }; + opt_case = case; + break :case case; + }; + const errors = try mani.trailingAlloc(ctx.arena); + + switch (strategy) { + .independent => { + case.addError(src, errors); + }, + .incremental => { + case.addErrorNamed("update", src, errors); + }, + } + }, + .run => { + const case = opt_case orelse case: { + const case = try ctx.cases.addOne(); + case.* = .{ + .name = "none", + .target = .{}, + .backend = backend, + .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), + .is_test = false, + .output_mode = .Exe, + .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), + }; + opt_case = case; + break :case case; + }; + + var output = std.ArrayList(u8).init(ctx.arena); + var trailing_it = mani.trailing(); + while (trailing_it.next()) |line| { + try output.appendSlice(line); + } + case.addCompareOutput(src, output.toOwnedSlice()); + }, + .cli => @panic("TODO cli tests"), + } + } else { + return error.MissingManifest; + } + } + } + fn addErrorCasesFromDirInner( ctx: *TestContext, name: []const u8, diff --git a/test/incremental/add.0.zig b/test/incremental/add.0.zig new file mode 100644 index 0000000000..f271bfed84 --- /dev/null +++ b/test/incremental/add.0.zig @@ -0,0 +1,12 @@ +pub fn main() void { + add(3, 4); +} + +fn add(a: u32, b: u32) void { + if (a + b != 7) unreachable; +} + +// run +// backend=stage2 +// strategy=incremental +// diff --git a/test/incremental/add.1.zig b/test/incremental/add.1.zig new file mode 100644 index 0000000000..a8cc3fa836 --- /dev/null +++ b/test/incremental/add.1.zig @@ -0,0 +1,14 @@ +pub fn main() void { + if (x - 7 != 0) unreachable; +} + +fn add(a: u32, b: u32) u32 { + return a + b; +} + +const x = add(3, 4); + +// run +// backend=stage2 +// strategy=incremental +// diff --git a/test/incremental/add.2.zig b/test/incremental/add.2.zig new file mode 100644 index 0000000000..0f4c18e28f --- /dev/null +++ b/test/incremental/add.2.zig @@ -0,0 +1,14 @@ +pub fn main() void { + var x: usize = 3; + const y = add(1, 2, x); + if (y - 6 != 0) unreachable; +} + +inline fn add(a: usize, b: usize, c: usize) usize { + return a + b + c; +} + +// run +// backend=stage2 +// strategy=incremental +// From d305a6fb550d68f4ee57bbb86f482969b819653b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 26 Apr 2022 18:47:11 +0200 Subject: [PATCH 1270/2031] test: parse all of manifest in TestManifest abstraction --- src/test.zig | 186 +++++++++++++++++++++++++++------------------------ 1 file changed, 97 insertions(+), 89 deletions(-) diff --git a/src/test.zig b/src/test.zig index c0a7fe6dc0..08875ac909 100644 --- a/src/test.zig +++ b/src/test.zig @@ -215,7 +215,37 @@ const TestManifest = struct { } fn parse(arena: Allocator, bytes: []const u8) !TestManifest { - var it = std.mem.tokenize(u8, bytes, "\r\n"); + // The manifest is the last contiguous block of comments in the file + // We scan for the beginning by searching backward for the first non-empty line that does not start with "//" + var start: ?usize = null; + var end: usize = bytes.len; + if (bytes.len > 0) { + var cursor: usize = bytes.len - 1; + while (true) { + // Move to beginning of line + while (cursor > 0 and bytes[cursor - 1] != '\n') cursor -= 1; + + if (std.mem.startsWith(u8, bytes[cursor..], "//")) { + start = cursor; // Contiguous comment line, include in manifest + } else { + if (start != null) break; // Encountered non-comment line, end of manifest + + // We ignore all-whitespace lines following the comment block, but anything else + // means that there is no manifest present. + if (std.mem.trim(u8, bytes[cursor..end], " \r\n\t").len == 0) { + end = cursor; + } else break; // If it's not whitespace, there is no manifest + } + + // Move to previous line + if (cursor != 0) cursor -= 1 else break; + } + } + + const actual_start = start orelse return error.MissingTestManifest; + const manifest_bytes = bytes[actual_start..end]; + + var it = std.mem.tokenize(u8, manifest_bytes, "\r\n"); // First line is the test type const tt: Type = blk: { @@ -250,20 +280,29 @@ const TestManifest = struct { } // Finally, trailing is expected output - manifest.trailing_bytes = bytes[it.index..]; + manifest.trailing_bytes = manifest_bytes[it.index..]; return manifest; } - fn getConfigValues( + fn getConfigForKey( self: TestManifest, key: []const u8, comptime T: type, - parse_fn: anytype, - ) ?ConfigValueIterator(T, @TypeOf(parse_fn)) { - const bytes = self.config_map.get(key) orelse return null; + parse_fn: fn ([]const u8) ?T, + ) ConfigValueIterator(T, @TypeOf(parse_fn)) { + const delimiter = ","; + var inner: std.mem.SplitIterator(u8) = if (self.config_map.get(key)) |bytes| .{ + .buffer = bytes, + .delimiter = delimiter, + .index = 0, + } else .{ + .buffer = undefined, + .delimiter = delimiter, + .index = null, + }; return ConfigValueIterator(T, @TypeOf(parse_fn)){ - .inner = std.mem.split(u8, bytes, ","), + .inner = inner, .parse_fn = parse_fn, }; } @@ -958,93 +997,62 @@ pub const TestContext = struct { const max_file_size = 10 * 1024 * 1024; const src = try dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0); - // The manifest is the last contiguous block of comments in the file - // We scan for the beginning by searching backward for the first non-empty line that does not start with "//" - var manifest_start: ?usize = null; - var manifest_end: usize = src.len; - if (src.len > 0) { - var cursor: usize = src.len - 1; - while (true) { - // Move to beginning of line - while (cursor > 0 and src[cursor - 1] != '\n') cursor -= 1; + // Parse the manifest + var manifest = try TestManifest.parse(ctx.arena, src); + const strategy = manifest.getConfigForKey("strategy", Strategy, Strategy.parse).next().?; + const backend = manifest.getConfigForKey("backend", Backend, Backend.parse).next().?; - if (std.mem.startsWith(u8, src[cursor..], "//")) { - manifest_start = cursor; // Contiguous comment line, include in manifest - } else { - if (manifest_start != null) break; // Encountered non-comment line, end of manifest + switch (manifest.@"type") { + .@"error" => { + const case = opt_case orelse case: { + const case = try ctx.cases.addOne(); + case.* = .{ + .name = "none", + .target = .{}, + .backend = backend, + .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), + .is_test = false, + .output_mode = .Obj, + .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), + }; + opt_case = case; + break :case case; + }; + const errors = try manifest.trailingAlloc(ctx.arena); - // We ignore all-whitespace lines following the comment block, but anything else - // means that there is no manifest present. - if (std.mem.trim(u8, src[cursor..manifest_end], " \r\n\t").len == 0) { - manifest_end = cursor; - } else break; // If it's not whitespace, there is no manifest + switch (strategy) { + .independent => { + case.addError(src, errors); + }, + .incremental => { + case.addErrorNamed("update", src, errors); + }, } - - // Move to previous line - if (cursor != 0) cursor -= 1 else break; - } - } - - if (manifest_start) |start| { - // Parse the manifest - var mani = try TestManifest.parse(ctx.arena, src[start..manifest_end]); - const strategy = mani.getConfigValues("strategy", Strategy, Strategy.parse).?.next().?; - const backend = mani.getConfigValues("backend", Backend, Backend.parse).?.next().?; - - switch (mani.@"type") { - .@"error" => { - const case = opt_case orelse case: { - const case = try ctx.cases.addOne(); - case.* = .{ - .name = "none", - .target = .{}, - .backend = backend, - .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), - .is_test = false, - .output_mode = .Obj, - .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), - }; - opt_case = case; - break :case case; + }, + .run => { + const case = opt_case orelse case: { + const case = try ctx.cases.addOne(); + case.* = .{ + .name = "none", + .target = .{}, + .backend = backend, + .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), + .is_test = false, + .output_mode = .Exe, + .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), }; - const errors = try mani.trailingAlloc(ctx.arena); + opt_case = case; + break :case case; + }; - switch (strategy) { - .independent => { - case.addError(src, errors); - }, - .incremental => { - case.addErrorNamed("update", src, errors); - }, - } - }, - .run => { - const case = opt_case orelse case: { - const case = try ctx.cases.addOne(); - case.* = .{ - .name = "none", - .target = .{}, - .backend = backend, - .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), - .is_test = false, - .output_mode = .Exe, - .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), - }; - opt_case = case; - break :case case; - }; - - var output = std.ArrayList(u8).init(ctx.arena); - var trailing_it = mani.trailing(); - while (trailing_it.next()) |line| { - try output.appendSlice(line); - } - case.addCompareOutput(src, output.toOwnedSlice()); - }, - .cli => @panic("TODO cli tests"), - } - } else { - return error.MissingManifest; + var output = std.ArrayList(u8).init(ctx.arena); + var trailing_it = manifest.trailing(); + while (trailing_it.next()) |line| { + try output.appendSlice(line); + } + case.addCompareOutput(src, output.toOwnedSlice()); + }, + .cli => @panic("TODO cli tests"), } } } From f41dd3617e3f630c3d5ec7745784076ccc0c3025 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 26 Apr 2022 18:58:16 +0200 Subject: [PATCH 1271/2031] test: pass Strategy per directory of tests --- src/test.zig | 19 ++++++++----------- test/incremental/add.0.zig | 1 - test/incremental/add.1.zig | 2 -- test/incremental/add.2.zig | 2 -- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/test.zig b/src/test.zig index 08875ac909..9840967bfc 100644 --- a/src/test.zig +++ b/src/test.zig @@ -80,7 +80,7 @@ test { var dir = try std.fs.cwd().openDir(dir_path, .{ .iterate = true }); defer dir.close(); - ctx.addTestCasesFromDir(dir); + ctx.addTestCasesFromDir(dir, .incremental); } try @import("test_cases").addCases(&ctx); @@ -834,10 +834,6 @@ pub const TestContext = struct { /// Execute all tests as incremental updates to a single compilation. Explicitly /// incremental tests ("foo.0.zig", "foo.1.zig", etc.) still execute in order incremental, - - fn parse(str: []const u8) ?Strategy { - return std.meta.stringToEnum(Strategy, str); - } }; /// Adds a compile-error test for each file in the provided directory, using the @@ -866,9 +862,9 @@ pub const TestContext = struct { }; } - pub fn addTestCasesFromDir(ctx: *TestContext, dir: std.fs.Dir) void { + pub fn addTestCasesFromDir(ctx: *TestContext, dir: std.fs.Dir, strategy: Strategy) void { var current_file: []const u8 = "none"; - addTestCasesFromDirInner(ctx, dir, ¤t_file) catch |err| { + addTestCasesFromDirInner(ctx, dir, strategy, ¤t_file) catch |err| { std.debug.panic("test harness failed to process file '{s}': {s}\n", .{ current_file, @errorName(err), }); @@ -938,6 +934,7 @@ pub const TestContext = struct { fn addTestCasesFromDirInner( ctx: *TestContext, dir: std.fs.Dir, + strategy: Strategy, /// This is kept up to date with the currently being processed file so /// that if any errors occur the caller knows it happened during this file. current_file: *[]const u8, @@ -988,8 +985,8 @@ pub const TestContext = struct { // in a new sequence ("*.0.zig") or an independent test file ("*.zig") if (new_parts.test_index != null and new_parts.test_index.? != 0) return error.InvalidIncrementalTestIndex; - // if (strategy == .independent) - // opt_case = null; // Generate a new independent test case for this update + if (strategy == .independent) + opt_case = null; // Generate a new independent test case for this update } } prev_filename = filename; @@ -999,13 +996,12 @@ pub const TestContext = struct { // Parse the manifest var manifest = try TestManifest.parse(ctx.arena, src); - const strategy = manifest.getConfigForKey("strategy", Strategy, Strategy.parse).next().?; - const backend = manifest.getConfigForKey("backend", Backend, Backend.parse).next().?; switch (manifest.@"type") { .@"error" => { const case = opt_case orelse case: { const case = try ctx.cases.addOne(); + const backend = manifest.getConfigForKey("backend", Backend, Backend.parse).next().?; case.* = .{ .name = "none", .target = .{}, @@ -1032,6 +1028,7 @@ pub const TestContext = struct { .run => { const case = opt_case orelse case: { const case = try ctx.cases.addOne(); + const backend = manifest.getConfigForKey("backend", Backend, Backend.parse).next().?; case.* = .{ .name = "none", .target = .{}, diff --git a/test/incremental/add.0.zig b/test/incremental/add.0.zig index f271bfed84..179e335044 100644 --- a/test/incremental/add.0.zig +++ b/test/incremental/add.0.zig @@ -8,5 +8,4 @@ fn add(a: u32, b: u32) void { // run // backend=stage2 -// strategy=incremental // diff --git a/test/incremental/add.1.zig b/test/incremental/add.1.zig index a8cc3fa836..d90dc6884d 100644 --- a/test/incremental/add.1.zig +++ b/test/incremental/add.1.zig @@ -9,6 +9,4 @@ fn add(a: u32, b: u32) u32 { const x = add(3, 4); // run -// backend=stage2 -// strategy=incremental // diff --git a/test/incremental/add.2.zig b/test/incremental/add.2.zig index 0f4c18e28f..6643d811fc 100644 --- a/test/incremental/add.2.zig +++ b/test/incremental/add.2.zig @@ -9,6 +9,4 @@ inline fn add(a: usize, b: usize, c: usize) usize { } // run -// backend=stage2 -// strategy=incremental // From bc370311cb76f570debf38f5d822a268f1dace83 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 26 Apr 2022 22:54:42 +0200 Subject: [PATCH 1272/2031] test: add comptime struct holding default config values --- src/test.zig | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/test.zig b/src/test.zig index 9840967bfc..d1ed3d5c28 100644 --- a/src/test.zig +++ b/src/test.zig @@ -165,6 +165,41 @@ const ErrorMsg = union(enum) { } }; +const TestManifestConfigDefaults = struct { + /// Asserts if the key doesn't exist - yep, it's an oversight alright. + fn get(@"type": TestManifest.Type, key: []const u8) []const u8 { + if (std.mem.eql(u8, key, "backend")) { + return "stage2"; + } else if (std.mem.eql(u8, key, "target")) { + comptime { + var defaults: []const u8 = ""; + // TODO should we only return "mainstream" targets by default here? + // Linux + inline for (&[_][]const u8{ "x86_64", "arm", "aarch64" }) |arch| { + inline for (&[_][]const u8{ "gnu", "musl" }) |abi| { + defaults = defaults ++ arch ++ "-linux-" ++ abi ++ ","; + } + } + // macOS + inline for (&[_][]const u8{ "x86_64", "aarch64" }) |arch| { + defaults = defaults ++ arch ++ "-macos" ++ ","; + } + // Wasm + defaults = defaults ++ "wasm32-wasi"; + return defaults[0 .. defaults.len - 2]; + } + } else if (std.mem.eql(u8, key, "output_mode")) { + return switch (@"type") { + .@"error" => "Obj", + .run => "Exe", + .cli => @panic("TODO test harness for CLI tests"), + }; + } else if (std.mem.eql(u8, key, "is_test")) { + return "0"; + } else unreachable; + } +}; + /// Manifest syntax example: /// (see https://github.com/ziglang/zig/issues/11288) /// From 46db5e2a44b38c92e97a438c4b6a67627cc12d16 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 26 Apr 2022 22:55:11 +0200 Subject: [PATCH 1273/2031] test: unroll into multiple cases, provide default parsers Provide default parsers for obvious config options such as `CrossTarget` or `Backend` (or any enum for that matter). Unroll iterator loops into multiple cases - we need to create a Cartesian product for all possibilities specified in the test manifest. --- src/test.zig | 169 ++++++++++++++++++++++--------------- test/incremental/add.0.zig | 1 - 2 files changed, 99 insertions(+), 71 deletions(-) diff --git a/src/test.zig b/src/test.zig index d1ed3d5c28..fb0c0fff35 100644 --- a/src/test.zig +++ b/src/test.zig @@ -237,10 +237,10 @@ const TestManifest = struct { } }; - fn ConfigValueIterator(comptime T: type, comptime ParseFn: type) type { + fn ConfigValueIterator(comptime T: type) type { return struct { inner: std.mem.SplitIterator(u8), - parse_fn: ParseFn, + parse_fn: ParseFn(T), fn next(self: *@This()) ?T { const next_raw = self.inner.next() orelse return null; @@ -320,26 +320,32 @@ const TestManifest = struct { return manifest; } + fn getConfigForKeyCustomParser( + self: TestManifest, + key: []const u8, + comptime T: type, + parse_fn: ParseFn(T), + ) ConfigValueIterator(T) { + const bytes = self.config_map.get(key) orelse TestManifestConfigDefaults.get(self.@"type", key); + return ConfigValueIterator(T){ + .inner = std.mem.split(u8, bytes, ","), + .parse_fn = parse_fn, + }; + } + fn getConfigForKey( self: TestManifest, key: []const u8, comptime T: type, - parse_fn: fn ([]const u8) ?T, - ) ConfigValueIterator(T, @TypeOf(parse_fn)) { - const delimiter = ","; - var inner: std.mem.SplitIterator(u8) = if (self.config_map.get(key)) |bytes| .{ - .buffer = bytes, - .delimiter = delimiter, - .index = 0, - } else .{ - .buffer = undefined, - .delimiter = delimiter, - .index = null, - }; - return ConfigValueIterator(T, @TypeOf(parse_fn)){ - .inner = inner, - .parse_fn = parse_fn, - }; + ) ConfigValueIterator(T) { + return self.getConfigForKeyCustomParser(key, T, getDefaultParser(T)); + } + + fn getConfigForKeyAssertSingle(self: TestManifest, key: []const u8, comptime T: type) T { + var it = self.getConfigForKey(key, T); + const res = it.next().?; + assert(it.next() == null); + return res; } fn trailing(self: TestManifest) TrailingIterator { @@ -356,6 +362,40 @@ const TestManifest = struct { } return out.toOwnedSlice(); } + + fn ParseFn(comptime T: type) type { + return fn ([]const u8) ?T; + } + + fn getDefaultParser(comptime T: type) ParseFn(T) { + switch (@typeInfo(T)) { + .Int => return struct { + fn parse(str: []const u8) ?T { + return std.fmt.parseInt(T, str, 0) catch null; + } + }.parse, + .Bool => return struct { + fn parse(str: []const u8) ?T { + const as_int = std.fmt.parseInt(u1, str, 0) catch return null; + return as_int > 0; + } + }.parse, + .Enum => return struct { + fn parse(str: []const u8) ?T { + return std.meta.stringToEnum(T, str); + } + }.parse, + .Struct => if (comptime std.mem.eql(u8, @typeName(T), "CrossTarget")) return struct { + fn parse(str: []const u8) ?T { + var opts = CrossTarget.ParseOptions{ + .arch_os_abi = str, + }; + return CrossTarget.parse(opts) catch null; + } + }.parse else @compileError("no default parser for " ++ @typeName(T)), + else => @compileError("no default parser for " ++ @typeName(T)), + } + } }; pub const TestContext = struct { @@ -401,10 +441,6 @@ pub const TestContext = struct { stage1, stage2, llvm, - - fn parse(str: []const u8) ?Backend { - return std.meta.stringToEnum(Backend, str); - } }; /// A `Case` consists of a list of `Update`. The same `Compilation` is used for each @@ -899,7 +935,7 @@ pub const TestContext = struct { pub fn addTestCasesFromDir(ctx: *TestContext, dir: std.fs.Dir, strategy: Strategy) void { var current_file: []const u8 = "none"; - addTestCasesFromDirInner(ctx, dir, strategy, ¤t_file) catch |err| { + ctx.addTestCasesFromDirInner(dir, strategy, ¤t_file) catch |err| { std.debug.panic("test harness failed to process file '{s}': {s}\n", .{ current_file, @errorName(err), }); @@ -974,11 +1010,10 @@ pub const TestContext = struct { /// that if any errors occur the caller knows it happened during this file. current_file: *[]const u8, ) !void { - var opt_case: ?*Case = null; + var cases = std.ArrayList(*Case).init(ctx.arena); var it = dir.iterate(); var filenames = std.ArrayList([]const u8).init(ctx.arena); - defer filenames.deinit(); while (try it.next()) |entry| { if (entry.kind != .File) continue; @@ -1021,7 +1056,7 @@ pub const TestContext = struct { if (new_parts.test_index != null and new_parts.test_index.? != 0) return error.InvalidIncrementalTestIndex; if (strategy == .independent) - opt_case = null; // Generate a new independent test case for this update + cases.clearRetainingCapacity(); // Generate a new independent test case for this update } } prev_filename = filename; @@ -1032,59 +1067,53 @@ pub const TestContext = struct { // Parse the manifest var manifest = try TestManifest.parse(ctx.arena, src); - switch (manifest.@"type") { - .@"error" => { - const case = opt_case orelse case: { + if (cases.items.len == 0) { + var backends = manifest.getConfigForKey("backend", Backend); + var targets = manifest.getConfigForKey("target", CrossTarget); + const is_test = manifest.getConfigForKeyAssertSingle("is_test", bool); + const output_mode = manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode); + + // Cross-product to get all possible test combinations + while (backends.next()) |backend| { + while (targets.next()) |target| { const case = try ctx.cases.addOne(); - const backend = manifest.getConfigForKey("backend", Backend, Backend.parse).next().?; case.* = .{ .name = "none", - .target = .{}, + .target = target, .backend = backend, .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), - .is_test = false, - .output_mode = .Obj, + .is_test = is_test, + .output_mode = output_mode, .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), }; - opt_case = case; - break :case case; - }; - const errors = try manifest.trailingAlloc(ctx.arena); - - switch (strategy) { - .independent => { - case.addError(src, errors); - }, - .incremental => { - case.addErrorNamed("update", src, errors); - }, + try cases.append(case); } - }, - .run => { - const case = opt_case orelse case: { - const case = try ctx.cases.addOne(); - const backend = manifest.getConfigForKey("backend", Backend, Backend.parse).next().?; - case.* = .{ - .name = "none", - .target = .{}, - .backend = backend, - .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), - .is_test = false, - .output_mode = .Exe, - .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), - }; - opt_case = case; - break :case case; - }; + } + } - var output = std.ArrayList(u8).init(ctx.arena); - var trailing_it = manifest.trailing(); - while (trailing_it.next()) |line| { - try output.appendSlice(line); - } - case.addCompareOutput(src, output.toOwnedSlice()); - }, - .cli => @panic("TODO cli tests"), + for (cases.items) |case| { + switch (manifest.@"type") { + .@"error" => { + const errors = try manifest.trailingAlloc(ctx.arena); + switch (strategy) { + .independent => { + case.addError(src, errors); + }, + .incremental => { + case.addErrorNamed("update", src, errors); + }, + } + }, + .run => { + var output = std.ArrayList(u8).init(ctx.arena); + var trailing_it = manifest.trailing(); + while (trailing_it.next()) |line| { + try output.appendSlice(line); + } + case.addCompareOutput(src, output.toOwnedSlice()); + }, + .cli => @panic("TODO cli tests"), + } } } } diff --git a/test/incremental/add.0.zig b/test/incremental/add.0.zig index 179e335044..e8c2196813 100644 --- a/test/incremental/add.0.zig +++ b/test/incremental/add.0.zig @@ -7,5 +7,4 @@ fn add(a: u32, b: u32) void { } // run -// backend=stage2 // From c1a98cd65d48693c9cfce5fbc5e5f5a1dbf4c1b7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 27 Apr 2022 12:41:05 +0200 Subject: [PATCH 1274/2031] test: set case name from initial filename for a sequence Port more incremental tests. --- src/test.zig | 61 +++++++++-------- ...ing_numbers_at_runtime_and_comptime.0.zig} | 0 ...ing_numbers_at_runtime_and_comptime.1.zig} | 0 ...ing_numbers_at_runtime_and_comptime.2.zig} | 0 test/incremental/assert_function.0.zig | 15 +++++ test/incremental/assert_function.1.zig | 17 +++++ test/incremental/assert_function.10.zig | 27 ++++++++ test/incremental/assert_function.11.zig | 66 +++++++++++++++++++ test/incremental/assert_function.12.zig | 47 +++++++++++++ test/incremental/assert_function.13.zig | 19 ++++++ test/incremental/assert_function.14.zig | 17 +++++ test/incremental/assert_function.15.zig | 10 +++ test/incremental/assert_function.16.zig | 11 ++++ test/incremental/assert_function.17.zig | 11 ++++ test/incremental/assert_function.18.zig | 35 ++++++++++ test/incremental/assert_function.2.zig | 21 ++++++ test/incremental/assert_function.3.zig | 22 +++++++ test/incremental/assert_function.4.zig | 15 +++++ test/incremental/assert_function.5.zig | 19 ++++++ test/incremental/assert_function.6.zig | 9 +++ test/incremental/assert_function.7.zig | 40 +++++++++++ test/incremental/assert_function.8.zig | 36 ++++++++++ test/incremental/assert_function.9.zig | 22 +++++++ ...ying_numbers_at_runtime_and_comptime.0.zig | 11 ++++ ...ying_numbers_at_runtime_and_comptime.1.zig | 12 ++++ ...ying_numbers_at_runtime_and_comptime.2.zig | 12 ++++ .../subtracting_numbers_at_runtime.zig | 10 +++ test/incremental/unused_vars.zig | 7 ++ 28 files changed, 546 insertions(+), 26 deletions(-) rename test/incremental/{add.0.zig => adding_numbers_at_runtime_and_comptime.0.zig} (100%) rename test/incremental/{add.1.zig => adding_numbers_at_runtime_and_comptime.1.zig} (100%) rename test/incremental/{add.2.zig => adding_numbers_at_runtime_and_comptime.2.zig} (100%) create mode 100644 test/incremental/assert_function.0.zig create mode 100644 test/incremental/assert_function.1.zig create mode 100644 test/incremental/assert_function.10.zig create mode 100644 test/incremental/assert_function.11.zig create mode 100644 test/incremental/assert_function.12.zig create mode 100644 test/incremental/assert_function.13.zig create mode 100644 test/incremental/assert_function.14.zig create mode 100644 test/incremental/assert_function.15.zig create mode 100644 test/incremental/assert_function.16.zig create mode 100644 test/incremental/assert_function.17.zig create mode 100644 test/incremental/assert_function.18.zig create mode 100644 test/incremental/assert_function.2.zig create mode 100644 test/incremental/assert_function.3.zig create mode 100644 test/incremental/assert_function.4.zig create mode 100644 test/incremental/assert_function.5.zig create mode 100644 test/incremental/assert_function.6.zig create mode 100644 test/incremental/assert_function.7.zig create mode 100644 test/incremental/assert_function.8.zig create mode 100644 test/incremental/assert_function.9.zig create mode 100644 test/incremental/multiplying_numbers_at_runtime_and_comptime.0.zig create mode 100644 test/incremental/multiplying_numbers_at_runtime_and_comptime.1.zig create mode 100644 test/incremental/multiplying_numbers_at_runtime_and_comptime.2.zig create mode 100644 test/incremental/subtracting_numbers_at_runtime.zig create mode 100644 test/incremental/unused_vars.zig diff --git a/src/test.zig b/src/test.zig index fb0c0fff35..a701976381 100644 --- a/src/test.zig +++ b/src/test.zig @@ -233,7 +233,7 @@ const TestManifest = struct { fn next(self: *TrailingIterator) ?[]const u8 { const next_inner = self.inner.next() orelse return null; - return std.mem.trim(u8, next_inner, " \t"); + return std.mem.trim(u8, next_inner[2..], " \t"); } }; @@ -1033,31 +1033,25 @@ pub const TestContext = struct { for (filenames.items) |filename| { current_file.* = filename; - { // First, check if this file is part of an incremental update sequence + // First, check if this file is part of an incremental update sequence + // Split filename into ".." + const prev_parts = getTestFileNameParts(prev_filename); + const new_parts = getTestFileNameParts(filename); - // Split filename into ".." - const prev_parts = getTestFileNameParts(prev_filename); - const new_parts = getTestFileNameParts(filename); - - // If base_name and file_ext match, these files are in the same test sequence - // and the new one should be the incremented version of the previous test - if (std.mem.eql(u8, prev_parts.base_name, new_parts.base_name) and - std.mem.eql(u8, prev_parts.file_ext, new_parts.file_ext)) - { - - // This is "foo.X.zig" followed by "foo.Y.zig". Make sure that X = Y + 1 - if (prev_parts.test_index == null) return error.InvalidIncrementalTestIndex; - if (new_parts.test_index == null) return error.InvalidIncrementalTestIndex; - if (new_parts.test_index.? != prev_parts.test_index.? + 1) return error.InvalidIncrementalTestIndex; - } else { - - // This is not the same test sequence, so the new file must be the first file - // in a new sequence ("*.0.zig") or an independent test file ("*.zig") - if (new_parts.test_index != null and new_parts.test_index.? != 0) return error.InvalidIncrementalTestIndex; - - if (strategy == .independent) - cases.clearRetainingCapacity(); // Generate a new independent test case for this update - } + // If base_name and file_ext match, these files are in the same test sequence + // and the new one should be the incremented version of the previous test + if (std.mem.eql(u8, prev_parts.base_name, new_parts.base_name) and + std.mem.eql(u8, prev_parts.file_ext, new_parts.file_ext)) + { + // This is "foo.X.zig" followed by "foo.Y.zig". Make sure that X = Y + 1 + if (prev_parts.test_index == null) return error.InvalidIncrementalTestIndex; + if (new_parts.test_index == null) return error.InvalidIncrementalTestIndex; + if (new_parts.test_index.? != prev_parts.test_index.? + 1) return error.InvalidIncrementalTestIndex; + } else { + // This is not the same test sequence, so the new file must be the first file + // in a new sequence ("*.0.zig") or an independent test file ("*.zig") + if (new_parts.test_index != null and new_parts.test_index.? != 0) return error.InvalidIncrementalTestIndex; + cases.clearRetainingCapacity(); } prev_filename = filename; @@ -1073,12 +1067,23 @@ pub const TestContext = struct { const is_test = manifest.getConfigForKeyAssertSingle("is_test", bool); const output_mode = manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode); + const name_prefix = blk: { + const ext_index = std.mem.lastIndexOfScalar(u8, current_file.*, '.') orelse + return error.InvalidFilename; + const index = std.mem.lastIndexOfScalar(u8, current_file.*[0..ext_index], '.') orelse ext_index; + break :blk current_file.*[0..index]; + }; + // Cross-product to get all possible test combinations while (backends.next()) |backend| { while (targets.next()) |target| { + const name = try std.fmt.allocPrint(ctx.arena, "{s} ({s})", .{ + name_prefix, + try target.zigTriple(ctx.arena), + }); const case = try ctx.cases.addOne(); case.* = .{ - .name = "none", + .name = name, .target = target, .backend = backend, .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), @@ -1109,6 +1114,10 @@ pub const TestContext = struct { var trailing_it = manifest.trailing(); while (trailing_it.next()) |line| { try output.appendSlice(line); + try output.append('\n'); + } + if (output.items.len > 0) { + try output.resize(output.items.len - 1); } case.addCompareOutput(src, output.toOwnedSlice()); }, diff --git a/test/incremental/add.0.zig b/test/incremental/adding_numbers_at_runtime_and_comptime.0.zig similarity index 100% rename from test/incremental/add.0.zig rename to test/incremental/adding_numbers_at_runtime_and_comptime.0.zig diff --git a/test/incremental/add.1.zig b/test/incremental/adding_numbers_at_runtime_and_comptime.1.zig similarity index 100% rename from test/incremental/add.1.zig rename to test/incremental/adding_numbers_at_runtime_and_comptime.1.zig diff --git a/test/incremental/add.2.zig b/test/incremental/adding_numbers_at_runtime_and_comptime.2.zig similarity index 100% rename from test/incremental/add.2.zig rename to test/incremental/adding_numbers_at_runtime_and_comptime.2.zig diff --git a/test/incremental/assert_function.0.zig b/test/incremental/assert_function.0.zig new file mode 100644 index 0000000000..889c9b1572 --- /dev/null +++ b/test/incremental/assert_function.0.zig @@ -0,0 +1,15 @@ +pub fn main() void { + add(3, 4); +} + +fn add(a: u32, b: u32) void { + assert(a + b == 7); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// target=x86_64-linux,x86_64-macos +// diff --git a/test/incremental/assert_function.1.zig b/test/incremental/assert_function.1.zig new file mode 100644 index 0000000000..ac2df25d85 --- /dev/null +++ b/test/incremental/assert_function.1.zig @@ -0,0 +1,17 @@ +pub fn main() void { + add(3, 4); +} + +fn add(a: u32, b: u32) void { + const c = a + b; // 7 + const d = a + c; // 10 + const e = d + b; // 14 + assert(e == 14); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/assert_function.10.zig b/test/incremental/assert_function.10.zig new file mode 100644 index 0000000000..b3f1610cd6 --- /dev/null +++ b/test/incremental/assert_function.10.zig @@ -0,0 +1,27 @@ +pub fn main() void { + assert(add(3, 4) == 116); +} + +fn add(a: u32, b: u32) u32 { + const x: u32 = blk: { + const c = a + b; // 7 + const d = a + c; // 10 + const e = d + b; // 14 + const f = d + e; // 24 + const g = e + f; // 38 + const h = f + g; // 62 + const i = g + h; // 100 + const j = i + d; // 110 + break :blk j; + }; + const y = x + a; // 113 + const z = y + a; // 116 + return z; +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/assert_function.11.zig b/test/incremental/assert_function.11.zig new file mode 100644 index 0000000000..d64130a677 --- /dev/null +++ b/test/incremental/assert_function.11.zig @@ -0,0 +1,66 @@ +pub fn main() void { + assert(add(3, 4) == 1221); + assert(mul(3, 4) == 21609); +} + +fn add(a: u32, b: u32) u32 { + const x: u32 = blk: { + const c = a + b; // 7 + const d = a + c; // 10 + const e = d + b; // 14 + const f = d + e; // 24 + const g = e + f; // 38 + const h = f + g; // 62 + const i = g + h; // 100 + const j = i + d; // 110 + const k = i + j; // 210 + const l = j + k; // 320 + const m = l + c; // 327 + const n = m + d; // 337 + const o = n + e; // 351 + const p = o + f; // 375 + const q = p + g; // 413 + const r = q + h; // 475 + const s = r + i; // 575 + const t = s + j; // 685 + const u = t + k; // 895 + const v = u + l; // 1215 + break :blk v; + }; + const y = x + a; // 1218 + const z = y + a; // 1221 + return z; +} + +fn mul(a: u32, b: u32) u32 { + const x: u32 = blk: { + const c = a * a * a * a; // 81 + const d = a * a * a * b; // 108 + const e = a * a * b * a; // 108 + const f = a * a * b * b; // 144 + const g = a * b * a * a; // 108 + const h = a * b * a * b; // 144 + const i = a * b * b * a; // 144 + const j = a * b * b * b; // 192 + const k = b * a * a * a; // 108 + const l = b * a * a * b; // 144 + const m = b * a * b * a; // 144 + const n = b * a * b * b; // 192 + const o = b * b * a * a; // 144 + const p = b * b * a * b; // 192 + const q = b * b * b * a; // 192 + const r = b * b * b * b; // 256 + const s = c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r; // 2401 + break :blk s; + }; + const y = x * a; // 7203 + const z = y * a; // 21609 + return z; +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/assert_function.12.zig b/test/incremental/assert_function.12.zig new file mode 100644 index 0000000000..4f64c1e062 --- /dev/null +++ b/test/incremental/assert_function.12.zig @@ -0,0 +1,47 @@ +pub fn main() void { + assert(add(3, 4) == 791); + assert(add(4, 3) == 79); +} + +fn add(a: u32, b: u32) u32 { + const x: u32 = if (a < b) blk: { + const c = a + b; // 7 + const d = a + c; // 10 + const e = d + b; // 14 + const f = d + e; // 24 + const g = e + f; // 38 + const h = f + g; // 62 + const i = g + h; // 100 + const j = i + d; // 110 + const k = i + j; // 210 + const l = k + c; // 217 + const m = l + d; // 227 + const n = m + e; // 241 + const o = n + f; // 265 + const p = o + g; // 303 + const q = p + h; // 365 + const r = q + i; // 465 + const s = r + j; // 575 + const t = s + k; // 785 + break :blk t; + } else blk: { + const t = b + b + a; // 10 + const c = a + t; // 14 + const d = c + t; // 24 + const e = d + t; // 34 + const f = e + t; // 44 + const g = f + t; // 54 + const h = c + g; // 68 + break :blk h + b; // 71 + }; + const y = x + a; // 788, 75 + const z = y + a; // 791, 79 + return z; +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/assert_function.13.zig b/test/incremental/assert_function.13.zig new file mode 100644 index 0000000000..240abf0108 --- /dev/null +++ b/test/incremental/assert_function.13.zig @@ -0,0 +1,19 @@ +pub fn main() void { + const ignore = + \\ cool thx + \\ + ; + _ = ignore; + add('ぁ', '\x03'); +} + +fn add(a: u32, b: u32) void { + assert(a + b == 12356); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/assert_function.14.zig b/test/incremental/assert_function.14.zig new file mode 100644 index 0000000000..d25100dcce --- /dev/null +++ b/test/incremental/assert_function.14.zig @@ -0,0 +1,17 @@ +pub fn main() void { + add(aa, bb); +} + +const aa = 'ぁ'; +const bb = '\x03'; + +fn add(a: u32, b: u32) void { + assert(a + b == 12356); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/assert_function.15.zig b/test/incremental/assert_function.15.zig new file mode 100644 index 0000000000..33ae1ed5af --- /dev/null +++ b/test/incremental/assert_function.15.zig @@ -0,0 +1,10 @@ +pub fn main() void { + assert("hello"[0] == 'h'); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/assert_function.16.zig b/test/incremental/assert_function.16.zig new file mode 100644 index 0000000000..eef1136423 --- /dev/null +++ b/test/incremental/assert_function.16.zig @@ -0,0 +1,11 @@ +const hello = "hello".*; +pub fn main() void { + assert(hello[1] == 'e'); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/assert_function.17.zig b/test/incremental/assert_function.17.zig new file mode 100644 index 0000000000..ac9dce3079 --- /dev/null +++ b/test/incremental/assert_function.17.zig @@ -0,0 +1,11 @@ +pub fn main() void { + var i: u64 = 0xFFEEDDCCBBAA9988; + assert(i == 0xFFEEDDCCBBAA9988); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/assert_function.18.zig b/test/incremental/assert_function.18.zig new file mode 100644 index 0000000000..31cf1207f3 --- /dev/null +++ b/test/incremental/assert_function.18.zig @@ -0,0 +1,35 @@ +const builtin = @import("builtin"); + +extern "c" fn write(usize, usize, usize) usize; + +pub fn main() void { + for ("hello") |_| print(); +} + +fn print() void { + switch (builtin.os.tag) { + .linux => { + asm volatile ("syscall" + : + : [number] "{rax}" (1), + [arg1] "{rdi}" (1), + [arg2] "{rsi}" (@ptrToInt("hello\n")), + [arg3] "{rdx}" (6), + : "rcx", "r11", "memory" + ); + }, + .macos => { + _ = write(1, @ptrToInt("hello\n"), 6); + }, + else => unreachable, + } +} + +// run +// +// hello +// hello +// hello +// hello +// hello +// diff --git a/test/incremental/assert_function.2.zig b/test/incremental/assert_function.2.zig new file mode 100644 index 0000000000..8c1c510486 --- /dev/null +++ b/test/incremental/assert_function.2.zig @@ -0,0 +1,21 @@ +pub fn main() void { + add(3, 4); +} + +fn add(a: u32, b: u32) void { + const c = a + b; // 7 + const d = a + c; // 10 + const e = d + b; // 14 + const f = d + e; // 24 + const g = e + f; // 38 + const h = f + g; // 62 + const i = g + h; // 100 + assert(i == 100); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/assert_function.3.zig b/test/incremental/assert_function.3.zig new file mode 100644 index 0000000000..a6829f8e02 --- /dev/null +++ b/test/incremental/assert_function.3.zig @@ -0,0 +1,22 @@ +pub fn main() void { + add(3, 4); +} + +fn add(a: u32, b: u32) void { + const c = a + b; // 7 + const d = a + c; // 10 + const e = d + b; // 14 + const f = d + e; // 24 + const g = e + f; // 38 + const h = f + g; // 62 + const i = g + h; // 100 + const j = i + d; // 110 + assert(j == 110); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/assert_function.4.zig b/test/incremental/assert_function.4.zig new file mode 100644 index 0000000000..69df4354c3 --- /dev/null +++ b/test/incremental/assert_function.4.zig @@ -0,0 +1,15 @@ +pub fn main() void { + assert(add(3, 4) == 7); + assert(add(20, 10) == 30); +} + +fn add(a: u32, b: u32) u32 { + return a + b; +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/assert_function.5.zig b/test/incremental/assert_function.5.zig new file mode 100644 index 0000000000..89f3f7df4f --- /dev/null +++ b/test/incremental/assert_function.5.zig @@ -0,0 +1,19 @@ +pub fn main() void { + assert(add(3, 4) == 7); + assert(add(20, 10) == 30); +} + +fn add(a: u32, b: u32) u32 { + var x: u32 = undefined; + x = 0; + x += a; + x += b; + return x; +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/assert_function.6.zig b/test/incremental/assert_function.6.zig new file mode 100644 index 0000000000..1b1b75e68e --- /dev/null +++ b/test/incremental/assert_function.6.zig @@ -0,0 +1,9 @@ +pub fn main() void { + const a: u32 = 2; + const b: ?u32 = a; + const c = b.?; + if (c != 2) unreachable; +} + +// run +// diff --git a/test/incremental/assert_function.7.zig b/test/incremental/assert_function.7.zig new file mode 100644 index 0000000000..f2f3ffcc3d --- /dev/null +++ b/test/incremental/assert_function.7.zig @@ -0,0 +1,40 @@ +const builtin = @import("builtin"); + +extern "c" fn write(usize, usize, usize) usize; + +pub fn main() void { + var i: u32 = 0; + while (i < 4) : (i += 1) print(); + assert(i == 4); +} + +fn print() void { + switch (builtin.os.tag) { + .linux => { + asm volatile ("syscall" + : + : [number] "{rax}" (1), + [arg1] "{rdi}" (1), + [arg2] "{rsi}" (@ptrToInt("hello\n")), + [arg3] "{rdx}" (6), + : "rcx", "r11", "memory" + ); + }, + .macos => { + _ = write(1, @ptrToInt("hello\n"), 6); + }, + else => unreachable, + } +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// +// hello +// hello +// hello +// hello +// diff --git a/test/incremental/assert_function.8.zig b/test/incremental/assert_function.8.zig new file mode 100644 index 0000000000..c8c30cfac6 --- /dev/null +++ b/test/incremental/assert_function.8.zig @@ -0,0 +1,36 @@ +const builtin = @import("builtin"); + +extern "c" fn write(usize, usize, usize) usize; + +pub fn main() void { + var i: u32 = 0; + inline while (i < 4) : (i += 1) print(); + assert(i == 4); +} + +fn print() void { + switch (builtin.os.tag) { + .linux => { + asm volatile ("syscall" + : + : [number] "{rax}" (1), + [arg1] "{rdi}" (1), + [arg2] "{rsi}" (@ptrToInt("hello\n")), + [arg3] "{rdx}" (6), + : "rcx", "r11", "memory" + ); + }, + .macos => { + _ = write(1, @ptrToInt("hello\n"), 6); + }, + else => unreachable, + } +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// error +// +// :3:21: error: unable to resolve comptime value diff --git a/test/incremental/assert_function.9.zig b/test/incremental/assert_function.9.zig new file mode 100644 index 0000000000..c754bb7711 --- /dev/null +++ b/test/incremental/assert_function.9.zig @@ -0,0 +1,22 @@ +pub fn main() void { + assert(add(3, 4) == 20); +} + +fn add(a: u32, b: u32) u32 { + const x: u32 = blk: { + const c = a + b; // 7 + const d = a + c; // 10 + const e = d + b; // 14 + break :blk e; + }; + const y = x + a; // 17 + const z = y + a; // 20 + return z; +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/multiplying_numbers_at_runtime_and_comptime.0.zig b/test/incremental/multiplying_numbers_at_runtime_and_comptime.0.zig new file mode 100644 index 0000000000..f113928982 --- /dev/null +++ b/test/incremental/multiplying_numbers_at_runtime_and_comptime.0.zig @@ -0,0 +1,11 @@ +pub fn main() void { + mul(3, 4); +} + +fn mul(a: u32, b: u32) void { + if (a * b != 12) unreachable; +} + +// run +// target=x86_64-linux,x86_64-macos +// diff --git a/test/incremental/multiplying_numbers_at_runtime_and_comptime.1.zig b/test/incremental/multiplying_numbers_at_runtime_and_comptime.1.zig new file mode 100644 index 0000000000..1dff884900 --- /dev/null +++ b/test/incremental/multiplying_numbers_at_runtime_and_comptime.1.zig @@ -0,0 +1,12 @@ +pub fn main() void { + if (x - 12 != 0) unreachable; +} + +fn mul(a: u32, b: u32) u32 { + return a * b; +} + +const x = mul(3, 4); + +// run +// diff --git a/test/incremental/multiplying_numbers_at_runtime_and_comptime.2.zig b/test/incremental/multiplying_numbers_at_runtime_and_comptime.2.zig new file mode 100644 index 0000000000..31093729ba --- /dev/null +++ b/test/incremental/multiplying_numbers_at_runtime_and_comptime.2.zig @@ -0,0 +1,12 @@ +pub fn main() void { + var x: usize = 5; + const y = mul(2, 3, x); + if (y - 30 != 0) unreachable; +} + +inline fn mul(a: usize, b: usize, c: usize) usize { + return a * b * c; +} + +// run +// diff --git a/test/incremental/subtracting_numbers_at_runtime.zig b/test/incremental/subtracting_numbers_at_runtime.zig new file mode 100644 index 0000000000..5ea81a34b8 --- /dev/null +++ b/test/incremental/subtracting_numbers_at_runtime.zig @@ -0,0 +1,10 @@ +pub fn main() void { + sub(7, 4); +} + +fn sub(a: u32, b: u32) void { + if (a - b != 3) unreachable; +} + +// run +// diff --git a/test/incremental/unused_vars.zig b/test/incremental/unused_vars.zig new file mode 100644 index 0000000000..85e168d19a --- /dev/null +++ b/test/incremental/unused_vars.zig @@ -0,0 +1,7 @@ +pub fn main() void { + const x = 1; +} + +// error +// +// :2:11: error: unused local constant From 81e90c7acbc90b06a9810b5bfe5812d06a537898 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 27 Apr 2022 13:21:20 +0200 Subject: [PATCH 1275/2031] test: fix pointer invalidation bug in the harness --- src/test.zig | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/test.zig b/src/test.zig index a701976381..a1edae01da 100644 --- a/src/test.zig +++ b/src/test.zig @@ -1010,7 +1010,7 @@ pub const TestContext = struct { /// that if any errors occur the caller knows it happened during this file. current_file: *[]const u8, ) !void { - var cases = std.ArrayList(*Case).init(ctx.arena); + var cases = std.ArrayList(usize).init(ctx.arena); var it = dir.iterate(); var filenames = std.ArrayList([]const u8).init(ctx.arena); @@ -1081,8 +1081,8 @@ pub const TestContext = struct { name_prefix, try target.zigTriple(ctx.arena), }); - const case = try ctx.cases.addOne(); - case.* = .{ + const next = ctx.cases.items.len; + try ctx.cases.append(.{ .name = name, .target = target, .backend = backend, @@ -1090,13 +1090,14 @@ pub const TestContext = struct { .is_test = is_test, .output_mode = output_mode, .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), - }; - try cases.append(case); + }); + try cases.append(next); } } } - for (cases.items) |case| { + for (cases.items) |case_index| { + const case = &ctx.cases.items[case_index]; switch (manifest.@"type") { .@"error" => { const errors = try manifest.trailingAlloc(ctx.arena); From 0998185f7762e029e037cbd77243965a78b2299e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 27 Apr 2022 13:21:32 +0200 Subject: [PATCH 1276/2031] test: adjust error location for assert_function test --- test/incremental/assert_function.8.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/incremental/assert_function.8.zig b/test/incremental/assert_function.8.zig index c8c30cfac6..f845de9aa0 100644 --- a/test/incremental/assert_function.8.zig +++ b/test/incremental/assert_function.8.zig @@ -33,4 +33,4 @@ pub fn assert(ok: bool) void { // error // -// :3:21: error: unable to resolve comptime value +// :7:21: error: unable to resolve comptime value From 133708d9396defc46630adc6acd24a493277b7d5 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 27 Apr 2022 13:25:16 +0200 Subject: [PATCH 1277/2031] test: leave a todo note to explicitly specify ABI for targets in the future --- src/test.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test.zig b/src/test.zig index a1edae01da..e138afecb9 100644 --- a/src/test.zig +++ b/src/test.zig @@ -174,11 +174,11 @@ const TestManifestConfigDefaults = struct { comptime { var defaults: []const u8 = ""; // TODO should we only return "mainstream" targets by default here? + // TODO we should also specify ABIs explicitly as the backends are + // getting more and more complete // Linux inline for (&[_][]const u8{ "x86_64", "arm", "aarch64" }) |arch| { - inline for (&[_][]const u8{ "gnu", "musl" }) |abi| { - defaults = defaults ++ arch ++ "-linux-" ++ abi ++ ","; - } + defaults = defaults ++ arch ++ "-linux-" ++ ","; } // macOS inline for (&[_][]const u8{ "x86_64", "aarch64" }) |arch| { From 97b781955eca7c3fc6ee2713f8b5e355645fff58 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 27 Apr 2022 15:37:54 +0200 Subject: [PATCH 1278/2031] test: fix incorrect default target spec; port all incremental tests --- src/test.zig | 2 +- ..._slice_element_by_index_slice_elem_val.zig | 17 +++++++++ test/incremental/ambiguous_reference.zig | 13 +++++++ .../bad_inferred_variable_type.zig | 9 +++++ test/incremental/break_continue.0.zig | 9 +++++ test/incremental/break_continue.1.zig | 8 +++++ test/incremental/break_continue.2.zig | 10 ++++++ test/incremental/break_continue.3.zig | 10 ++++++ test/incremental/catch_at_comptime.0.zig | 11 ++++++ test/incremental/catch_at_comptime.1.zig | 11 ++++++ test/incremental/catch_at_comptime.2.zig | 11 ++++++ test/incremental/catch_at_comptime.3.zig | 10 ++++++ test/incremental/catch_at_comptime.4.zig | 10 ++++++ test/incremental/compile_error.zig | 7 ++++ ...ompile_error_in_inline_fn_call_fixed.0.zig | 16 +++++++++ ...ompile_error_in_inline_fn_call_fixed.1.zig | 13 +++++++ test/incremental/compile_log.0.zig | 17 +++++++++ test/incremental/compile_log.1.zig | 16 +++++++++ test/incremental/comptime_var.0.zig | 12 +++++++ test/incremental/comptime_var.1.zig | 13 +++++++ test/incremental/comptime_var.2.zig | 34 ++++++++++++++++++ test/incremental/comptime_var.3.zig | 10 ++++++ test/incremental/comptime_var.4.zig | 9 +++++ test/incremental/comptime_var.5.zig | 15 ++++++++ test/incremental/comptime_var.6.zig | 32 +++++++++++++++++ test/incremental/double_ampersand.0.zig | 6 ++++ test/incremental/double_ampersand.1.zig | 11 ++++++ test/incremental/double_ampersand.2.zig | 7 ++++ .../extern_variable_has_no_type.0.zig | 9 +++++ .../extern_variable_has_no_type.1.zig | 8 +++++ test/incremental/function_redeclaration.zig | 14 ++++++++ .../global_variable_redeclaration.zig | 8 +++++ ...ello_world_with_updates_x86_64_linux.0.zig | 5 +++ ...ello_world_with_updates_x86_64_linux.1.zig | 5 +++ ...ello_world_with_updates_x86_64_linux.2.zig | 32 +++++++++++++++++ ...ello_world_with_updates_x86_64_linux.3.zig | 20 +++++++++++ ...ello_world_with_updates_x86_64_linux.4.zig | 20 +++++++++++ ...ello_world_with_updates_x86_64_linux.5.zig | 22 ++++++++++++ ...ello_world_with_updates_x86_64_macos.0.zig | 5 +++ ...ello_world_with_updates_x86_64_macos.1.zig | 5 +++ ...ello_world_with_updates_x86_64_macos.2.zig | 19 ++++++++++ ...ello_world_with_updates_x86_64_macos.3.zig | 16 +++++++++ ...ello_world_with_updates_x86_64_macos.4.zig | 22 ++++++++++++ ...ello_world_with_updates_x86_64_macos.5.zig | 16 +++++++++ ...ello_world_with_updates_x86_64_macos.6.zig | 18 ++++++++++ .../inline_assembly_x86_64_linux.0.zig | 16 +++++++++ .../inline_assembly_x86_64_linux.1.zig | 15 ++++++++ .../inline_assembly_x86_64_linux.2.zig | 12 +++++++ .../inline_assembly_x86_64_linux.3.zig | 12 +++++++ .../inner_func_accessing_outer_var.zig | 15 ++++++++ test/incremental/int_to_ptr.0.zig | 8 +++++ test/incremental/int_to_ptr.1.zig | 7 ++++ ...ee_preserved_regs_working_x86_64_linux.zig | 31 ++++++++++++++++ ...7_miscompilation_with_bool_return_type.zig | 18 ++++++++++ .../load_store_via_pointer_deref.0.zig | 16 +++++++++ .../load_store_via_pointer_deref.1.zig | 16 +++++++++ .../load_store_via_pointer_deref.2.zig | 16 +++++++++ .../lower_unnamed_consts_structs.0.zig | 25 +++++++++++++ .../lower_unnamed_consts_structs.1.zig | 35 +++++++++++++++++++ .../lower_unnamed_consts_structs.2.zig | 25 +++++++++++++ test/incremental/merge_error_sets.0.zig | 18 ++++++++++ test/incremental/merge_error_sets.1.zig | 9 +++++ ...ion_and_it_gets_updated_x86_64_linux.0.zig | 13 +++++++ ...ion_and_it_gets_updated_x86_64_linux.1.zig | 12 +++++++ test/incremental/optional_payload.0.zig | 19 ++++++++++ test/incremental/optional_payload.1.zig | 17 +++++++++ test/incremental/optional_payload.2.zig | 18 ++++++++++ test/incremental/optional_payload.3.zig | 18 ++++++++++ test/incremental/orelse_at_comptime.0.zig | 11 ++++++ test/incremental/orelse_at_comptime.1.zig | 11 ++++++ test/incremental/passing_u0_to_function.zig | 9 +++++ .../recursive_inline_function.0.zig | 12 +++++++ .../recursive_inline_function.1.zig | 16 +++++++++ test/incremental/redundant_comptime.0.zig | 7 ++++ test/incremental/redundant_comptime.1.zig | 9 +++++ test/incremental/returns_in_try.zig | 16 +++++++++ test/incremental/runtime_bitwise_and.zig | 16 +++++++++ test/incremental/runtime_bitwise_or.zig | 16 +++++++++ ..._vars_of_different_abi_size_to_stack.0.zig | 16 +++++++++ ..._vars_of_different_abi_size_to_stack.1.zig | 16 +++++++++ ..._vars_of_different_abi_size_to_stack.2.zig | 16 +++++++++ ...g_an_address_space_on_a_local_variable.zig | 8 +++++ .../try_in_comptime_in_struct_in_test.zig | 13 +++++++ test/incremental/type_of.0.zig | 13 +++++++ test/incremental/type_of.1.zig | 11 ++++++ test/incremental/type_of.2.zig | 9 +++++ test/incremental/unused_labels.0.zig | 8 +++++ test/incremental/unused_labels.1.zig | 7 ++++ test/incremental/unused_labels.2.zig | 7 ++++ test/incremental/unused_labels.3.zig | 8 +++++ .../unwrap_error_union_simple_errors.0.zig | 10 ++++++ .../unwrap_error_union_simple_errors.1.zig | 11 ++++++ test/incremental/variable_shadowing.0.zig | 9 +++++ test/incremental/variable_shadowing.1.zig | 9 +++++ test/incremental/variable_shadowing.2.zig | 13 +++++++ test/incremental/variable_shadowing.3.zig | 10 ++++++ test/incremental/variable_shadowing.4.zig | 10 ++++++ test/incremental/variable_shadowing.5.zig | 10 ++++++ test/incremental/variable_shadowing.6.zig | 13 +++++++ test/incremental/variable_shadowing.7.zig | 9 +++++ test/incremental/variable_shadowing.8.zig | 9 +++++ test/incremental/variable_shadowing.9.zig | 9 +++++ 102 files changed, 1360 insertions(+), 1 deletion(-) create mode 100644 test/incremental/access_slice_element_by_index_slice_elem_val.zig create mode 100644 test/incremental/ambiguous_reference.zig create mode 100644 test/incremental/bad_inferred_variable_type.zig create mode 100644 test/incremental/break_continue.0.zig create mode 100644 test/incremental/break_continue.1.zig create mode 100644 test/incremental/break_continue.2.zig create mode 100644 test/incremental/break_continue.3.zig create mode 100644 test/incremental/catch_at_comptime.0.zig create mode 100644 test/incremental/catch_at_comptime.1.zig create mode 100644 test/incremental/catch_at_comptime.2.zig create mode 100644 test/incremental/catch_at_comptime.3.zig create mode 100644 test/incremental/catch_at_comptime.4.zig create mode 100644 test/incremental/compile_error.zig create mode 100644 test/incremental/compile_error_in_inline_fn_call_fixed.0.zig create mode 100644 test/incremental/compile_error_in_inline_fn_call_fixed.1.zig create mode 100644 test/incremental/compile_log.0.zig create mode 100644 test/incremental/compile_log.1.zig create mode 100644 test/incremental/comptime_var.0.zig create mode 100644 test/incremental/comptime_var.1.zig create mode 100644 test/incremental/comptime_var.2.zig create mode 100644 test/incremental/comptime_var.3.zig create mode 100644 test/incremental/comptime_var.4.zig create mode 100644 test/incremental/comptime_var.5.zig create mode 100644 test/incremental/comptime_var.6.zig create mode 100644 test/incremental/double_ampersand.0.zig create mode 100644 test/incremental/double_ampersand.1.zig create mode 100644 test/incremental/double_ampersand.2.zig create mode 100644 test/incremental/extern_variable_has_no_type.0.zig create mode 100644 test/incremental/extern_variable_has_no_type.1.zig create mode 100644 test/incremental/function_redeclaration.zig create mode 100644 test/incremental/global_variable_redeclaration.zig create mode 100644 test/incremental/hello_world_with_updates_x86_64_linux.0.zig create mode 100644 test/incremental/hello_world_with_updates_x86_64_linux.1.zig create mode 100644 test/incremental/hello_world_with_updates_x86_64_linux.2.zig create mode 100644 test/incremental/hello_world_with_updates_x86_64_linux.3.zig create mode 100644 test/incremental/hello_world_with_updates_x86_64_linux.4.zig create mode 100644 test/incremental/hello_world_with_updates_x86_64_linux.5.zig create mode 100644 test/incremental/hello_world_with_updates_x86_64_macos.0.zig create mode 100644 test/incremental/hello_world_with_updates_x86_64_macos.1.zig create mode 100644 test/incremental/hello_world_with_updates_x86_64_macos.2.zig create mode 100644 test/incremental/hello_world_with_updates_x86_64_macos.3.zig create mode 100644 test/incremental/hello_world_with_updates_x86_64_macos.4.zig create mode 100644 test/incremental/hello_world_with_updates_x86_64_macos.5.zig create mode 100644 test/incremental/hello_world_with_updates_x86_64_macos.6.zig create mode 100644 test/incremental/inline_assembly_x86_64_linux.0.zig create mode 100644 test/incremental/inline_assembly_x86_64_linux.1.zig create mode 100644 test/incremental/inline_assembly_x86_64_linux.2.zig create mode 100644 test/incremental/inline_assembly_x86_64_linux.3.zig create mode 100644 test/incremental/inner_func_accessing_outer_var.zig create mode 100644 test/incremental/int_to_ptr.0.zig create mode 100644 test/incremental/int_to_ptr.1.zig create mode 100644 test/incremental/issue_10138_callee_preserved_regs_working_x86_64_linux.zig create mode 100644 test/incremental/issue_7187_miscompilation_with_bool_return_type.zig create mode 100644 test/incremental/load_store_via_pointer_deref.0.zig create mode 100644 test/incremental/load_store_via_pointer_deref.1.zig create mode 100644 test/incremental/load_store_via_pointer_deref.2.zig create mode 100644 test/incremental/lower_unnamed_consts_structs.0.zig create mode 100644 test/incremental/lower_unnamed_consts_structs.1.zig create mode 100644 test/incremental/lower_unnamed_consts_structs.2.zig create mode 100644 test/incremental/merge_error_sets.0.zig create mode 100644 test/incremental/merge_error_sets.1.zig create mode 100644 test/incremental/only_1_function_and_it_gets_updated_x86_64_linux.0.zig create mode 100644 test/incremental/only_1_function_and_it_gets_updated_x86_64_linux.1.zig create mode 100644 test/incremental/optional_payload.0.zig create mode 100644 test/incremental/optional_payload.1.zig create mode 100644 test/incremental/optional_payload.2.zig create mode 100644 test/incremental/optional_payload.3.zig create mode 100644 test/incremental/orelse_at_comptime.0.zig create mode 100644 test/incremental/orelse_at_comptime.1.zig create mode 100644 test/incremental/passing_u0_to_function.zig create mode 100644 test/incremental/recursive_inline_function.0.zig create mode 100644 test/incremental/recursive_inline_function.1.zig create mode 100644 test/incremental/redundant_comptime.0.zig create mode 100644 test/incremental/redundant_comptime.1.zig create mode 100644 test/incremental/returns_in_try.zig create mode 100644 test/incremental/runtime_bitwise_and.zig create mode 100644 test/incremental/runtime_bitwise_or.zig create mode 100644 test/incremental/saving_vars_of_different_abi_size_to_stack.0.zig create mode 100644 test/incremental/saving_vars_of_different_abi_size_to_stack.1.zig create mode 100644 test/incremental/saving_vars_of_different_abi_size_to_stack.2.zig create mode 100644 test/incremental/setting_an_address_space_on_a_local_variable.zig create mode 100644 test/incremental/try_in_comptime_in_struct_in_test.zig create mode 100644 test/incremental/type_of.0.zig create mode 100644 test/incremental/type_of.1.zig create mode 100644 test/incremental/type_of.2.zig create mode 100644 test/incremental/unused_labels.0.zig create mode 100644 test/incremental/unused_labels.1.zig create mode 100644 test/incremental/unused_labels.2.zig create mode 100644 test/incremental/unused_labels.3.zig create mode 100644 test/incremental/unwrap_error_union_simple_errors.0.zig create mode 100644 test/incremental/unwrap_error_union_simple_errors.1.zig create mode 100644 test/incremental/variable_shadowing.0.zig create mode 100644 test/incremental/variable_shadowing.1.zig create mode 100644 test/incremental/variable_shadowing.2.zig create mode 100644 test/incremental/variable_shadowing.3.zig create mode 100644 test/incremental/variable_shadowing.4.zig create mode 100644 test/incremental/variable_shadowing.5.zig create mode 100644 test/incremental/variable_shadowing.6.zig create mode 100644 test/incremental/variable_shadowing.7.zig create mode 100644 test/incremental/variable_shadowing.8.zig create mode 100644 test/incremental/variable_shadowing.9.zig diff --git a/src/test.zig b/src/test.zig index e138afecb9..2f3389d7ff 100644 --- a/src/test.zig +++ b/src/test.zig @@ -178,7 +178,7 @@ const TestManifestConfigDefaults = struct { // getting more and more complete // Linux inline for (&[_][]const u8{ "x86_64", "arm", "aarch64" }) |arch| { - defaults = defaults ++ arch ++ "-linux-" ++ ","; + defaults = defaults ++ arch ++ "-linux" ++ ","; } // macOS inline for (&[_][]const u8{ "x86_64", "aarch64" }) |arch| { diff --git a/test/incremental/access_slice_element_by_index_slice_elem_val.zig b/test/incremental/access_slice_element_by_index_slice_elem_val.zig new file mode 100644 index 0000000000..a0bdae0826 --- /dev/null +++ b/test/incremental/access_slice_element_by_index_slice_elem_val.zig @@ -0,0 +1,17 @@ +var array = [_]usize{ 0, 42, 123, 34 }; +var slice: []const usize = &array; + +pub fn main() void { + assert(slice[0] == 0); + assert(slice[1] == 42); + assert(slice[2] == 123); + assert(slice[3] == 34); +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// run +// target=x86_64-linux,x86_64-macos +// diff --git a/test/incremental/ambiguous_reference.zig b/test/incremental/ambiguous_reference.zig new file mode 100644 index 0000000000..66144a5645 --- /dev/null +++ b/test/incremental/ambiguous_reference.zig @@ -0,0 +1,13 @@ +const T = struct { + const T = struct { + fn f() void { + _ = T; + } + }; +}; + +// error +// +// :4:17: error: ambiguous reference +// :2:5: note: declared here +// :1:1: note: also declared here diff --git a/test/incremental/bad_inferred_variable_type.zig b/test/incremental/bad_inferred_variable_type.zig new file mode 100644 index 0000000000..47ceb9b638 --- /dev/null +++ b/test/incremental/bad_inferred_variable_type.zig @@ -0,0 +1,9 @@ +pub fn main() void { + var x = null; + _ = x; +} + +// error +// output_mode=Exe +// +// :2:9: error: variable of type '@TypeOf(null)' must be const or comptime diff --git a/test/incremental/break_continue.0.zig b/test/incremental/break_continue.0.zig new file mode 100644 index 0000000000..ac0abdb52d --- /dev/null +++ b/test/incremental/break_continue.0.zig @@ -0,0 +1,9 @@ +pub fn main() void { + while (true) { + break; + } +} + +// run +// target=x86_64-linux,x86_64-macos,aarch64-linux,aarch64-macos +// diff --git a/test/incremental/break_continue.1.zig b/test/incremental/break_continue.1.zig new file mode 100644 index 0000000000..9e4f79bd14 --- /dev/null +++ b/test/incremental/break_continue.1.zig @@ -0,0 +1,8 @@ +pub fn main() void { + foo: while (true) { + break :foo; + } +} + +// run +// diff --git a/test/incremental/break_continue.2.zig b/test/incremental/break_continue.2.zig new file mode 100644 index 0000000000..ba12f33e3f --- /dev/null +++ b/test/incremental/break_continue.2.zig @@ -0,0 +1,10 @@ +pub fn main() void { + var i: u64 = 0; + while (true) : (i += 1) { + if (i == 4) return; + continue; + } +} + +// run +// diff --git a/test/incremental/break_continue.3.zig b/test/incremental/break_continue.3.zig new file mode 100644 index 0000000000..03e224d265 --- /dev/null +++ b/test/incremental/break_continue.3.zig @@ -0,0 +1,10 @@ +pub fn main() void { + var i: u64 = 0; + foo: while (true) : (i += 1) { + if (i == 4) return; + continue :foo; + } +} + +// run +// diff --git a/test/incremental/catch_at_comptime.0.zig b/test/incremental/catch_at_comptime.0.zig new file mode 100644 index 0000000000..af1545832d --- /dev/null +++ b/test/incremental/catch_at_comptime.0.zig @@ -0,0 +1,11 @@ +pub fn main() void { + const i: anyerror!u64 = 0; + const caught = i catch 5; + assert(caught == 0); +} +fn assert(b: bool) void { + if (!b) unreachable; +} + +// run +// diff --git a/test/incremental/catch_at_comptime.1.zig b/test/incremental/catch_at_comptime.1.zig new file mode 100644 index 0000000000..e05caeae6d --- /dev/null +++ b/test/incremental/catch_at_comptime.1.zig @@ -0,0 +1,11 @@ +pub fn main() void { + const i: anyerror!u64 = error.B; + const caught = i catch 5; + assert(caught == 5); +} +fn assert(b: bool) void { + if (!b) unreachable; +} + +// run +// diff --git a/test/incremental/catch_at_comptime.2.zig b/test/incremental/catch_at_comptime.2.zig new file mode 100644 index 0000000000..326e6f0b61 --- /dev/null +++ b/test/incremental/catch_at_comptime.2.zig @@ -0,0 +1,11 @@ +pub fn main() void { + const a: anyerror!comptime_int = 42; + const b: *const comptime_int = &(a catch unreachable); + assert(b.* == 42); +} +fn assert(b: bool) void { + if (!b) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/catch_at_comptime.3.zig b/test/incremental/catch_at_comptime.3.zig new file mode 100644 index 0000000000..d00317f697 --- /dev/null +++ b/test/incremental/catch_at_comptime.3.zig @@ -0,0 +1,10 @@ +pub fn main() void { + const a: anyerror!u32 = error.B; + _ = &(a catch |err| assert(err == error.B)); +} +fn assert(b: bool) void { + if (!b) unreachable; +} + +// run +// diff --git a/test/incremental/catch_at_comptime.4.zig b/test/incremental/catch_at_comptime.4.zig new file mode 100644 index 0000000000..57cb602641 --- /dev/null +++ b/test/incremental/catch_at_comptime.4.zig @@ -0,0 +1,10 @@ +pub fn main() void { + const a: anyerror!u32 = error.Bar; + a catch |err| assert(err == error.Bar); +} +fn assert(b: bool) void { + if (!b) unreachable; +} + +// run +// diff --git a/test/incremental/compile_error.zig b/test/incremental/compile_error.zig new file mode 100644 index 0000000000..dab0f7f7cc --- /dev/null +++ b/test/incremental/compile_error.zig @@ -0,0 +1,7 @@ +export fn foo() void { + @compileError("this is an error"); +} + +// error +// +// :2:5: error: this is an error diff --git a/test/incremental/compile_error_in_inline_fn_call_fixed.0.zig b/test/incremental/compile_error_in_inline_fn_call_fixed.0.zig new file mode 100644 index 0000000000..1848490b07 --- /dev/null +++ b/test/incremental/compile_error_in_inline_fn_call_fixed.0.zig @@ -0,0 +1,16 @@ +pub fn main() void { + var x: usize = 3; + const y = add(10, 2, x); + if (y - 6 != 0) unreachable; +} + +inline fn add(a: usize, b: usize, c: usize) usize { + if (a == 10) @compileError("bad"); + return a + b + c; +} + +// error +// output_mode=Exe +// +// :8:18: error: bad +// :3:18: note: called from here diff --git a/test/incremental/compile_error_in_inline_fn_call_fixed.1.zig b/test/incremental/compile_error_in_inline_fn_call_fixed.1.zig new file mode 100644 index 0000000000..1e61c6c790 --- /dev/null +++ b/test/incremental/compile_error_in_inline_fn_call_fixed.1.zig @@ -0,0 +1,13 @@ +pub fn main() void { + var x: usize = 3; + const y = add(1, 2, x); + if (y - 6 != 0) unreachable; +} + +inline fn add(a: usize, b: usize, c: usize) usize { + if (a == 10) @compileError("bad"); + return a + b + c; +} + +// run +// diff --git a/test/incremental/compile_log.0.zig b/test/incremental/compile_log.0.zig new file mode 100644 index 0000000000..c213ebb930 --- /dev/null +++ b/test/incremental/compile_log.0.zig @@ -0,0 +1,17 @@ +export fn _start() noreturn { + const b = true; + var f: u32 = 1; + @compileLog(b, 20, f, x); + @compileLog(1000); + var bruh: usize = true; + _ = bruh; + unreachable; +} +export fn other() void { + @compileLog(1234); +} +fn x() void {} + +// error +// +// :6:23: error: expected usize, found bool diff --git a/test/incremental/compile_log.1.zig b/test/incremental/compile_log.1.zig new file mode 100644 index 0000000000..12e6641542 --- /dev/null +++ b/test/incremental/compile_log.1.zig @@ -0,0 +1,16 @@ +export fn _start() noreturn { + const b = true; + var f: u32 = 1; + @compileLog(b, 20, f, x); + @compileLog(1000); + unreachable; +} +export fn other() void { + @compileLog(1234); +} +fn x() void {} + +// error +// +// :9:5: error: found compile log statement +// :4:5: note: also here diff --git a/test/incremental/comptime_var.0.zig b/test/incremental/comptime_var.0.zig new file mode 100644 index 0000000000..019cf78abb --- /dev/null +++ b/test/incremental/comptime_var.0.zig @@ -0,0 +1,12 @@ +pub fn main() void { + var a: u32 = 0; + comptime var b: u32 = 0; + if (a == 0) b = 3; +} + +// error +// output_mode=Exe +// target=x86_64-linux,x86_64-macos +// +// :4:21: error: store to comptime variable depends on runtime condition +// :4:11: note: runtime condition here diff --git a/test/incremental/comptime_var.1.zig b/test/incremental/comptime_var.1.zig new file mode 100644 index 0000000000..efc51aafe3 --- /dev/null +++ b/test/incremental/comptime_var.1.zig @@ -0,0 +1,13 @@ +pub fn main() void { + var a: u32 = 0; + comptime var b: u32 = 0; + switch (a) { + 0 => {}, + else => b = 3, + } +} + +// error +// +// :6:21: error: store to comptime variable depends on runtime condition +// :4:13: note: runtime condition here diff --git a/test/incremental/comptime_var.2.zig b/test/incremental/comptime_var.2.zig new file mode 100644 index 0000000000..e91c0540ef --- /dev/null +++ b/test/incremental/comptime_var.2.zig @@ -0,0 +1,34 @@ +const builtin = @import("builtin"); + +extern "c" fn write(usize, usize, usize) usize; + +pub fn main() void { + comptime var len: u32 = 5; + print(len); + len += 9; + print(len); +} + +fn print(len: usize) void { + switch (builtin.os.tag) { + .linux => { + asm volatile ("syscall" + : + : [number] "{rax}" (1), + [arg1] "{rdi}" (1), + [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), + [arg3] "{rdx}" (len), + : "rcx", "r11", "memory" + ); + }, + .macos => { + _ = write(1, @ptrToInt("Hello, World!\n"), len); + }, + else => unreachable, + } +} + +// run +// +// HelloHello, World! +// diff --git a/test/incremental/comptime_var.3.zig b/test/incremental/comptime_var.3.zig new file mode 100644 index 0000000000..d4e6d85d9d --- /dev/null +++ b/test/incremental/comptime_var.3.zig @@ -0,0 +1,10 @@ +comptime { + var x: i32 = 1; + x += 1; + if (x != 1) unreachable; +} +pub fn main() void {} + +// error +// +// :4:17: error: unable to resolve comptime value diff --git a/test/incremental/comptime_var.4.zig b/test/incremental/comptime_var.4.zig new file mode 100644 index 0000000000..74da6ef448 --- /dev/null +++ b/test/incremental/comptime_var.4.zig @@ -0,0 +1,9 @@ +pub fn main() void { + comptime var i: u64 = 0; + while (i < 5) : (i += 1) {} +} + +// error +// +// :3:24: error: cannot store to comptime variable in non-inline loop +// :3:5: note: non-inline loop here diff --git a/test/incremental/comptime_var.5.zig b/test/incremental/comptime_var.5.zig new file mode 100644 index 0000000000..76a06f3d4b --- /dev/null +++ b/test/incremental/comptime_var.5.zig @@ -0,0 +1,15 @@ +pub fn main() void { + var a: u32 = 0; + if (a == 0) { + comptime var b: u32 = 0; + b = 1; + } +} +comptime { + var x: i32 = 1; + x += 1; + if (x != 2) unreachable; +} + +// run +// diff --git a/test/incremental/comptime_var.6.zig b/test/incremental/comptime_var.6.zig new file mode 100644 index 0000000000..0eb743a05b --- /dev/null +++ b/test/incremental/comptime_var.6.zig @@ -0,0 +1,32 @@ +const builtin = @import("builtin"); + +extern "c" fn write(usize, usize, usize) usize; + +pub fn main() void { + comptime var i: u64 = 2; + inline while (i < 6) : (i += 1) { + print(i); + } +} +fn print(len: usize) void { + switch (builtin.os.tag) { + .linux => { + asm volatile ("syscall" + : + : [number] "{rax}" (1), + [arg1] "{rdi}" (1), + [arg2] "{rsi}" (@ptrToInt("Hello")), + [arg3] "{rdx}" (len), + : "rcx", "r11", "memory" + ); + }, + .macos => { + _ = write(1, @ptrToInt("Hello"), len); + }, + else => unreachable, + } +} + +// run +// +// HeHelHellHello diff --git a/test/incremental/double_ampersand.0.zig b/test/incremental/double_ampersand.0.zig new file mode 100644 index 0000000000..b14c30ecb0 --- /dev/null +++ b/test/incremental/double_ampersand.0.zig @@ -0,0 +1,6 @@ +pub const a = if (true && false) 1 else 2; + +// error +// output_mode=Exe +// +// :1:24: error: ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND diff --git a/test/incremental/double_ampersand.1.zig b/test/incremental/double_ampersand.1.zig new file mode 100644 index 0000000000..cd5bba02fd --- /dev/null +++ b/test/incremental/double_ampersand.1.zig @@ -0,0 +1,11 @@ +pub fn main() void { + const a = true; + const b = false; + _ = a & &b; +} + +// error +// +// :4:11: error: incompatible types: 'bool' and '*const bool' +// :4:9: note: type 'bool' here +// :4:13: note: type '*const bool' here diff --git a/test/incremental/double_ampersand.2.zig b/test/incremental/double_ampersand.2.zig new file mode 100644 index 0000000000..c6f461d661 --- /dev/null +++ b/test/incremental/double_ampersand.2.zig @@ -0,0 +1,7 @@ +pub fn main() void { + const b: u8 = 1; + _ = &&b; +} + +// run +// diff --git a/test/incremental/extern_variable_has_no_type.0.zig b/test/incremental/extern_variable_has_no_type.0.zig new file mode 100644 index 0000000000..2f03067b3a --- /dev/null +++ b/test/incremental/extern_variable_has_no_type.0.zig @@ -0,0 +1,9 @@ +comptime { + const x = foo + foo; + _ = x; +} +extern var foo: i32; + +// error +// +// :2:15: error: unable to resolve comptime value diff --git a/test/incremental/extern_variable_has_no_type.1.zig b/test/incremental/extern_variable_has_no_type.1.zig new file mode 100644 index 0000000000..f5c31244e5 --- /dev/null +++ b/test/incremental/extern_variable_has_no_type.1.zig @@ -0,0 +1,8 @@ +export fn entry() void { + _ = foo; +} +extern var foo; + +// error +// +// :4:8: error: unable to infer variable type diff --git a/test/incremental/function_redeclaration.zig b/test/incremental/function_redeclaration.zig new file mode 100644 index 0000000000..a9664646c9 --- /dev/null +++ b/test/incremental/function_redeclaration.zig @@ -0,0 +1,14 @@ +// dummy comment +fn entry() void {} +fn entry() void {} + +fn foo() void { + var foo = 1234; +} + +// error +// +// :3:1: error: redeclaration of 'entry' +// :2:1: note: other declaration here +// :6:9: error: local shadows declaration of 'foo' +// :5:1: note: declared here diff --git a/test/incremental/global_variable_redeclaration.zig b/test/incremental/global_variable_redeclaration.zig new file mode 100644 index 0000000000..9a0d5939fb --- /dev/null +++ b/test/incremental/global_variable_redeclaration.zig @@ -0,0 +1,8 @@ +// dummy comment +var foo = false; +var foo = true; + +// error +// +// :3:1: error: redeclaration of 'foo' +// :2:1: note: other declaration here diff --git a/test/incremental/hello_world_with_updates_x86_64_linux.0.zig b/test/incremental/hello_world_with_updates_x86_64_linux.0.zig new file mode 100644 index 0000000000..960fae5e64 --- /dev/null +++ b/test/incremental/hello_world_with_updates_x86_64_linux.0.zig @@ -0,0 +1,5 @@ +// error +// output_mode=Exe +// target=x86_64-linux +// +// :109:9: error: struct 'tmp.tmp' has no member named 'main' diff --git a/test/incremental/hello_world_with_updates_x86_64_linux.1.zig b/test/incremental/hello_world_with_updates_x86_64_linux.1.zig new file mode 100644 index 0000000000..0f347b7f50 --- /dev/null +++ b/test/incremental/hello_world_with_updates_x86_64_linux.1.zig @@ -0,0 +1,5 @@ +pub export fn _start() noreturn {} + +// error +// +// :1:34: error: expected noreturn, found void diff --git a/test/incremental/hello_world_with_updates_x86_64_linux.2.zig b/test/incremental/hello_world_with_updates_x86_64_linux.2.zig new file mode 100644 index 0000000000..fcea1870ce --- /dev/null +++ b/test/incremental/hello_world_with_updates_x86_64_linux.2.zig @@ -0,0 +1,32 @@ +pub export fn _start() noreturn { + print(); + + exit(); +} + +fn print() void { + asm volatile ("syscall" + : + : [number] "{rax}" (1), + [arg1] "{rdi}" (1), + [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), + [arg3] "{rdx}" (14), + : "rcx", "r11", "memory" + ); + return; +} + +fn exit() noreturn { + asm volatile ("syscall" + : + : [number] "{rax}" (231), + [arg1] "{rdi}" (0), + : "rcx", "r11", "memory" + ); + unreachable; +} + +// run +// +// Hello, World! +// diff --git a/test/incremental/hello_world_with_updates_x86_64_linux.3.zig b/test/incremental/hello_world_with_updates_x86_64_linux.3.zig new file mode 100644 index 0000000000..7812023372 --- /dev/null +++ b/test/incremental/hello_world_with_updates_x86_64_linux.3.zig @@ -0,0 +1,20 @@ +pub fn main() void { + print(); +} + +fn print() void { + asm volatile ("syscall" + : + : [number] "{rax}" (1), + [arg1] "{rdi}" (1), + [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), + [arg3] "{rdx}" (14), + : "rcx", "r11", "memory" + ); + return; +} + +// run +// +// Hello, World! +// diff --git a/test/incremental/hello_world_with_updates_x86_64_linux.4.zig b/test/incremental/hello_world_with_updates_x86_64_linux.4.zig new file mode 100644 index 0000000000..cdf47012b8 --- /dev/null +++ b/test/incremental/hello_world_with_updates_x86_64_linux.4.zig @@ -0,0 +1,20 @@ +pub fn main() void { + print(); +} + +fn print() void { + asm volatile ("syscall" + : + : [number] "{rax}" (1), + [arg1] "{rdi}" (1), + [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")), + [arg3] "{rdx}" (104), + : "rcx", "r11", "memory" + ); + return; +} + +// run +// +// What is up? This is a longer message that will force the data to be relocated in virtual address space. +// diff --git a/test/incremental/hello_world_with_updates_x86_64_linux.5.zig b/test/incremental/hello_world_with_updates_x86_64_linux.5.zig new file mode 100644 index 0000000000..68c1e305f7 --- /dev/null +++ b/test/incremental/hello_world_with_updates_x86_64_linux.5.zig @@ -0,0 +1,22 @@ +pub fn main() void { + print(); + print(); +} + +fn print() void { + asm volatile ("syscall" + : + : [number] "{rax}" (1), + [arg1] "{rdi}" (1), + [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")), + [arg3] "{rdx}" (104), + : "rcx", "r11", "memory" + ); + return; +} + +// run +// +// What is up? This is a longer message that will force the data to be relocated in virtual address space. +// What is up? This is a longer message that will force the data to be relocated in virtual address space. +// diff --git a/test/incremental/hello_world_with_updates_x86_64_macos.0.zig b/test/incremental/hello_world_with_updates_x86_64_macos.0.zig new file mode 100644 index 0000000000..34440c4603 --- /dev/null +++ b/test/incremental/hello_world_with_updates_x86_64_macos.0.zig @@ -0,0 +1,5 @@ +// error +// output_mode=Exe +// target=x86_64-macos +// +// :109:9: error: struct 'tmp.tmp' has no member named 'main' diff --git a/test/incremental/hello_world_with_updates_x86_64_macos.1.zig b/test/incremental/hello_world_with_updates_x86_64_macos.1.zig new file mode 100644 index 0000000000..909fc9ccfb --- /dev/null +++ b/test/incremental/hello_world_with_updates_x86_64_macos.1.zig @@ -0,0 +1,5 @@ +pub export fn main() noreturn {} + +// error +// +// :1:32: error: expected noreturn, found void diff --git a/test/incremental/hello_world_with_updates_x86_64_macos.2.zig b/test/incremental/hello_world_with_updates_x86_64_macos.2.zig new file mode 100644 index 0000000000..fb8cb39edd --- /dev/null +++ b/test/incremental/hello_world_with_updates_x86_64_macos.2.zig @@ -0,0 +1,19 @@ +extern "c" fn write(usize, usize, usize) usize; +extern "c" fn exit(usize) noreturn; + +pub export fn main() noreturn { + print(); + + exit(0); +} + +fn print() void { + const msg = @ptrToInt("Hello, World!\n"); + const len = 14; + _ = write(1, msg, len); +} + +// run +// +// Hello, World! +// diff --git a/test/incremental/hello_world_with_updates_x86_64_macos.3.zig b/test/incremental/hello_world_with_updates_x86_64_macos.3.zig new file mode 100644 index 0000000000..f6e233886b --- /dev/null +++ b/test/incremental/hello_world_with_updates_x86_64_macos.3.zig @@ -0,0 +1,16 @@ +extern "c" fn write(usize, usize, usize) usize; + +pub fn main() void { + print(); +} + +fn print() void { + const msg = @ptrToInt("Hello, World!\n"); + const len = 14; + _ = write(1, msg, len); +} + +// run +// +// Hello, World! +// diff --git a/test/incremental/hello_world_with_updates_x86_64_macos.4.zig b/test/incremental/hello_world_with_updates_x86_64_macos.4.zig new file mode 100644 index 0000000000..f89cef7354 --- /dev/null +++ b/test/incremental/hello_world_with_updates_x86_64_macos.4.zig @@ -0,0 +1,22 @@ +extern "c" fn write(usize, usize, usize) usize; + +pub fn main() void { + print(); + print(); + print(); + print(); +} + +fn print() void { + const msg = @ptrToInt("Hello, World!\n"); + const len = 14; + _ = write(1, msg, len); +} + +// run +// +// Hello, World! +// Hello, World! +// Hello, World! +// Hello, World! +// diff --git a/test/incremental/hello_world_with_updates_x86_64_macos.5.zig b/test/incremental/hello_world_with_updates_x86_64_macos.5.zig new file mode 100644 index 0000000000..0d7f97578a --- /dev/null +++ b/test/incremental/hello_world_with_updates_x86_64_macos.5.zig @@ -0,0 +1,16 @@ +extern "c" fn write(usize, usize, usize) usize; + +pub fn main() void { + print(); +} + +fn print() void { + const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n"); + const len = 104; + _ = write(1, msg, len); +} + +// run +// +// What is up? This is a longer message that will force the data to be relocated in virtual address space. +// diff --git a/test/incremental/hello_world_with_updates_x86_64_macos.6.zig b/test/incremental/hello_world_with_updates_x86_64_macos.6.zig new file mode 100644 index 0000000000..3ce2cb7176 --- /dev/null +++ b/test/incremental/hello_world_with_updates_x86_64_macos.6.zig @@ -0,0 +1,18 @@ +extern "c" fn write(usize, usize, usize) usize; + +pub fn main() void { + print(); + print(); +} + +fn print() void { + const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n"); + const len = 104; + _ = write(1, msg, len); +} + +// run +// +// What is up? This is a longer message that will force the data to be relocated in virtual address space. +// What is up? This is a longer message that will force the data to be relocated in virtual address space. +// diff --git a/test/incremental/inline_assembly_x86_64_linux.0.zig b/test/incremental/inline_assembly_x86_64_linux.0.zig new file mode 100644 index 0000000000..e3c0f3badb --- /dev/null +++ b/test/incremental/inline_assembly_x86_64_linux.0.zig @@ -0,0 +1,16 @@ +pub fn main() void { + const number = 1234; + const x = asm volatile ("syscall" + : [o] "{rax}" (-> number), + : [number] "{rax}" (231), + [arg1] "{rdi}" (60), + : "rcx", "r11", "memory" + ); + _ = x; +} + +// error +// output_mode=Exe +// target=x86_64-linux +// +// :4:27: error: expected type, found comptime_int diff --git a/test/incremental/inline_assembly_x86_64_linux.1.zig b/test/incremental/inline_assembly_x86_64_linux.1.zig new file mode 100644 index 0000000000..b35014b0f6 --- /dev/null +++ b/test/incremental/inline_assembly_x86_64_linux.1.zig @@ -0,0 +1,15 @@ +const S = struct { + comptime { + asm volatile ( + \\zig_moment: + \\syscall + ); + } +}; +pub fn main() void { + _ = S; +} + +// error +// +// :3:13: error: volatile is meaningless on global assembly diff --git a/test/incremental/inline_assembly_x86_64_linux.2.zig b/test/incremental/inline_assembly_x86_64_linux.2.zig new file mode 100644 index 0000000000..1695265ab4 --- /dev/null +++ b/test/incremental/inline_assembly_x86_64_linux.2.zig @@ -0,0 +1,12 @@ +pub fn main() void { + var bruh: u32 = 1; + asm ("" + : + : [bruh] "{rax}" (4) + : "memory" + ); +} + +// error +// +// :3:5: error: assembly expression with no output must be marked volatile diff --git a/test/incremental/inline_assembly_x86_64_linux.3.zig b/test/incremental/inline_assembly_x86_64_linux.3.zig new file mode 100644 index 0000000000..765eaeb97e --- /dev/null +++ b/test/incremental/inline_assembly_x86_64_linux.3.zig @@ -0,0 +1,12 @@ +pub fn main() void {} +comptime { + asm ("" + : + : [bruh] "{rax}" (4) + : "memory" + ); +} + +// error +// +// :3:5: error: global assembly cannot have inputs, outputs, or clobbers diff --git a/test/incremental/inner_func_accessing_outer_var.zig b/test/incremental/inner_func_accessing_outer_var.zig new file mode 100644 index 0000000000..e30cf58ef8 --- /dev/null +++ b/test/incremental/inner_func_accessing_outer_var.zig @@ -0,0 +1,15 @@ +pub fn f() void { + var bar: bool = true; + const S = struct { + fn baz() bool { + return bar; + } + }; + _ = S; +} + +// error +// +// :5:20: error: mutable 'bar' not accessible from here +// :2:9: note: declared mutable here +// :3:15: note: crosses namespace boundary here diff --git a/test/incremental/int_to_ptr.0.zig b/test/incremental/int_to_ptr.0.zig new file mode 100644 index 0000000000..b7c70b90a4 --- /dev/null +++ b/test/incremental/int_to_ptr.0.zig @@ -0,0 +1,8 @@ +pub fn main() void { + _ = @intToPtr(*u8, 0); +} + +// error +// output_mode=Exe +// +// :2:24: error: pointer type '*u8' does not allow address zero diff --git a/test/incremental/int_to_ptr.1.zig b/test/incremental/int_to_ptr.1.zig new file mode 100644 index 0000000000..72c8c8c9d6 --- /dev/null +++ b/test/incremental/int_to_ptr.1.zig @@ -0,0 +1,7 @@ +pub fn main() void { + _ = @intToPtr(*u32, 2); +} + +// error +// +// :2:25: error: pointer type '*u32' requires aligned address diff --git a/test/incremental/issue_10138_callee_preserved_regs_working_x86_64_linux.zig b/test/incremental/issue_10138_callee_preserved_regs_working_x86_64_linux.zig new file mode 100644 index 0000000000..4c045496da --- /dev/null +++ b/test/incremental/issue_10138_callee_preserved_regs_working_x86_64_linux.zig @@ -0,0 +1,31 @@ +pub fn main() void { + const fd = open(); + _ = write(fd, "a", 1); + _ = close(fd); +} + +fn open() usize { + return 42; +} + +fn write(fd: usize, a: [*]const u8, len: usize) usize { + return syscall4(.WRITE, fd, @ptrToInt(a), len); +} + +fn syscall4(n: enum { WRITE }, a: usize, b: usize, c: usize) usize { + _ = n; + _ = a; + _ = b; + _ = c; + return 23; +} + +fn close(fd: usize) usize { + if (fd != 42) + unreachable; + return 0; +} + +// run +// target=x86_64-linux +// diff --git a/test/incremental/issue_7187_miscompilation_with_bool_return_type.zig b/test/incremental/issue_7187_miscompilation_with_bool_return_type.zig new file mode 100644 index 0000000000..3aed8685bf --- /dev/null +++ b/test/incremental/issue_7187_miscompilation_with_bool_return_type.zig @@ -0,0 +1,18 @@ +pub fn main() void { + var x: usize = 1; + var y: bool = getFalse(); + _ = y; + + assert(x == 1); +} + +fn getFalse() bool { + return false; +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// run +// diff --git a/test/incremental/load_store_via_pointer_deref.0.zig b/test/incremental/load_store_via_pointer_deref.0.zig new file mode 100644 index 0000000000..96aedf3196 --- /dev/null +++ b/test/incremental/load_store_via_pointer_deref.0.zig @@ -0,0 +1,16 @@ +pub fn main() void { + var x: u32 = undefined; + set(&x); + assert(x == 123); +} + +fn set(x: *u32) void { + x.* = 123; +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// run +// diff --git a/test/incremental/load_store_via_pointer_deref.1.zig b/test/incremental/load_store_via_pointer_deref.1.zig new file mode 100644 index 0000000000..e311092744 --- /dev/null +++ b/test/incremental/load_store_via_pointer_deref.1.zig @@ -0,0 +1,16 @@ +pub fn main() void { + var x: u16 = undefined; + set(&x); + assert(x == 123); +} + +fn set(x: *u16) void { + x.* = 123; +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// run +// diff --git a/test/incremental/load_store_via_pointer_deref.2.zig b/test/incremental/load_store_via_pointer_deref.2.zig new file mode 100644 index 0000000000..3b1484faa1 --- /dev/null +++ b/test/incremental/load_store_via_pointer_deref.2.zig @@ -0,0 +1,16 @@ +pub fn main() void { + var x: u8 = undefined; + set(&x); + assert(x == 123); +} + +fn set(x: *u8) void { + x.* = 123; +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// run +// diff --git a/test/incremental/lower_unnamed_consts_structs.0.zig b/test/incremental/lower_unnamed_consts_structs.0.zig new file mode 100644 index 0000000000..3123299646 --- /dev/null +++ b/test/incremental/lower_unnamed_consts_structs.0.zig @@ -0,0 +1,25 @@ +const Foo = struct { + a: u8, + b: u32, + + fn first(self: *Foo) u8 { + return self.a; + } + + fn second(self: *Foo) u32 { + return self.b; + } +}; + +pub fn main() void { + var foo = Foo{ .a = 1, .b = 5 }; + assert(foo.first() == 1); + assert(foo.second() == 5); +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// run +// diff --git a/test/incremental/lower_unnamed_consts_structs.1.zig b/test/incremental/lower_unnamed_consts_structs.1.zig new file mode 100644 index 0000000000..37afdc8a09 --- /dev/null +++ b/test/incremental/lower_unnamed_consts_structs.1.zig @@ -0,0 +1,35 @@ +const Foo = struct { + a: u8, + b: u32, + + fn first(self: *Foo) u8 { + return self.a; + } + + fn second(self: *Foo) u32 { + return self.b; + } +}; + +pub fn main() void { + var foo = Foo{ .a = 1, .b = 5 }; + assert(foo.first() == 1); + assert(foo.second() == 5); + + foo.a = 10; + foo.b = 255; + + assert(foo.first() == 10); + assert(foo.second() == 255); + + var foo2 = Foo{ .a = 15, .b = 255 }; + assert(foo2.first() == 15); + assert(foo2.second() == 255); +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// run +// diff --git a/test/incremental/lower_unnamed_consts_structs.2.zig b/test/incremental/lower_unnamed_consts_structs.2.zig new file mode 100644 index 0000000000..b437c4a030 --- /dev/null +++ b/test/incremental/lower_unnamed_consts_structs.2.zig @@ -0,0 +1,25 @@ +const Foo = struct { + a: u8, + b: u32, + + fn first(self: *Foo) u8 { + return self.a; + } + + fn second(self: *Foo) u32 { + return self.b; + } +}; + +pub fn main() void { + var foo2 = Foo{ .a = 15, .b = 255 }; + assert(foo2.first() == 15); + assert(foo2.second() == 255); +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// run +// diff --git a/test/incremental/merge_error_sets.0.zig b/test/incremental/merge_error_sets.0.zig new file mode 100644 index 0000000000..f1f3b96883 --- /dev/null +++ b/test/incremental/merge_error_sets.0.zig @@ -0,0 +1,18 @@ +pub fn main() void { + const E = error{ A, B, D } || error{ A, B, C }; + E.A catch {}; + E.B catch {}; + E.C catch {}; + E.D catch {}; + const E2 = error{ X, Y } || @TypeOf(error.Z); + E2.X catch {}; + E2.Y catch {}; + E2.Z catch {}; + assert(anyerror || error{Z} == anyerror); +} +fn assert(b: bool) void { + if (!b) unreachable; +} + +// run +// diff --git a/test/incremental/merge_error_sets.1.zig b/test/incremental/merge_error_sets.1.zig new file mode 100644 index 0000000000..81c1cad134 --- /dev/null +++ b/test/incremental/merge_error_sets.1.zig @@ -0,0 +1,9 @@ +pub fn main() void { + const z = true || false; + _ = z; +} + +// error +// +// :2:15: error: expected error set type, found 'bool' +// :2:20: note: '||' merges error sets; 'or' performs boolean OR diff --git a/test/incremental/only_1_function_and_it_gets_updated_x86_64_linux.0.zig b/test/incremental/only_1_function_and_it_gets_updated_x86_64_linux.0.zig new file mode 100644 index 0000000000..ecba1c8133 --- /dev/null +++ b/test/incremental/only_1_function_and_it_gets_updated_x86_64_linux.0.zig @@ -0,0 +1,13 @@ +pub export fn _start() noreturn { + asm volatile ("syscall" + : + : [number] "{rax}" (60), // exit + [arg1] "{rdi}" (0), + : "rcx", "r11", "memory" + ); + unreachable; +} + +// run +// target=x86_64-linux +// diff --git a/test/incremental/only_1_function_and_it_gets_updated_x86_64_linux.1.zig b/test/incremental/only_1_function_and_it_gets_updated_x86_64_linux.1.zig new file mode 100644 index 0000000000..529acbcf38 --- /dev/null +++ b/test/incremental/only_1_function_and_it_gets_updated_x86_64_linux.1.zig @@ -0,0 +1,12 @@ +pub export fn _start() noreturn { + asm volatile ("syscall" + : + : [number] "{rax}" (231), // exit_group + [arg1] "{rdi}" (0), + : "rcx", "r11", "memory" + ); + unreachable; +} + +// run +// diff --git a/test/incremental/optional_payload.0.zig b/test/incremental/optional_payload.0.zig new file mode 100644 index 0000000000..65296875ff --- /dev/null +++ b/test/incremental/optional_payload.0.zig @@ -0,0 +1,19 @@ +pub fn main() void { + var x: u32 = undefined; + const maybe_x = byPtr(&x); + assert(maybe_x != null); + maybe_x.?.* = 123; + assert(x == 123); +} + +fn byPtr(x: *u32) ?*u32 { + return x; +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// run +// target=x86_64-linux,x86_64-macos +// diff --git a/test/incremental/optional_payload.1.zig b/test/incremental/optional_payload.1.zig new file mode 100644 index 0000000000..2d4dafffb9 --- /dev/null +++ b/test/incremental/optional_payload.1.zig @@ -0,0 +1,17 @@ +pub fn main() void { + var x: u32 = undefined; + const maybe_x = byPtr(&x); + assert(maybe_x == null); +} + +fn byPtr(x: *u32) ?*u32 { + _ = x; + return null; +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// run +// diff --git a/test/incremental/optional_payload.2.zig b/test/incremental/optional_payload.2.zig new file mode 100644 index 0000000000..608310923b --- /dev/null +++ b/test/incremental/optional_payload.2.zig @@ -0,0 +1,18 @@ +pub fn main() void { + var x: u8 = undefined; + const maybe_x = byPtr(&x); + assert(maybe_x != null); + maybe_x.?.* = 255; + assert(x == 255); +} + +fn byPtr(x: *u8) ?*u8 { + return x; +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// run +// diff --git a/test/incremental/optional_payload.3.zig b/test/incremental/optional_payload.3.zig new file mode 100644 index 0000000000..b81ec398c7 --- /dev/null +++ b/test/incremental/optional_payload.3.zig @@ -0,0 +1,18 @@ +pub fn main() void { + var x: i8 = undefined; + const maybe_x = byPtr(&x); + assert(maybe_x != null); + maybe_x.?.* = -1; + assert(x == -1); +} + +fn byPtr(x: *i8) ?*i8 { + return x; +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// run +// diff --git a/test/incremental/orelse_at_comptime.0.zig b/test/incremental/orelse_at_comptime.0.zig new file mode 100644 index 0000000000..5397ca3b0a --- /dev/null +++ b/test/incremental/orelse_at_comptime.0.zig @@ -0,0 +1,11 @@ +pub fn main() void { + const i: ?u64 = 0; + const result = i orelse 5; + assert(result == 0); +} +fn assert(b: bool) void { + if (!b) unreachable; +} + +// run +// diff --git a/test/incremental/orelse_at_comptime.1.zig b/test/incremental/orelse_at_comptime.1.zig new file mode 100644 index 0000000000..7d4fcd3178 --- /dev/null +++ b/test/incremental/orelse_at_comptime.1.zig @@ -0,0 +1,11 @@ +pub fn main() void { + const i: ?u64 = null; + const result = i orelse 5; + assert(result == 5); +} +fn assert(b: bool) void { + if (!b) unreachable; +} + +// run +// diff --git a/test/incremental/passing_u0_to_function.zig b/test/incremental/passing_u0_to_function.zig new file mode 100644 index 0000000000..f84c30be8a --- /dev/null +++ b/test/incremental/passing_u0_to_function.zig @@ -0,0 +1,9 @@ +pub fn main() void { + doNothing(0); +} +fn doNothing(arg: u0) void { + _ = arg; +} + +// run +// diff --git a/test/incremental/recursive_inline_function.0.zig b/test/incremental/recursive_inline_function.0.zig new file mode 100644 index 0000000000..c8333c9065 --- /dev/null +++ b/test/incremental/recursive_inline_function.0.zig @@ -0,0 +1,12 @@ +pub fn main() void { + const y = fibonacci(7); + if (y - 21 != 0) unreachable; +} + +inline fn fibonacci(n: usize) usize { + if (n <= 2) return n; + return fibonacci(n - 2) + fibonacci(n - 1); +} + +// run +// diff --git a/test/incremental/recursive_inline_function.1.zig b/test/incremental/recursive_inline_function.1.zig new file mode 100644 index 0000000000..139da1c371 --- /dev/null +++ b/test/incremental/recursive_inline_function.1.zig @@ -0,0 +1,16 @@ +// This additionally tests that the compile error reports the correct source location. +// Without storing source locations relative to the owner decl, the compile error +// here would be off by 2 bytes (from the "7" -> "999"). +pub fn main() void { + const y = fibonacci(999); + if (y - 21 != 0) unreachable; +} + +inline fn fibonacci(n: usize) usize { + if (n <= 2) return n; + return fibonacci(n - 2) + fibonacci(n - 1); +} + +// error +// +// :11:21: error: evaluation exceeded 1000 backwards branches diff --git a/test/incremental/redundant_comptime.0.zig b/test/incremental/redundant_comptime.0.zig new file mode 100644 index 0000000000..c1ecbf7ace --- /dev/null +++ b/test/incremental/redundant_comptime.0.zig @@ -0,0 +1,7 @@ +pub fn main() void { + var a: comptime u32 = 0; +} + +// error +// +// :2:12: error: redundant comptime keyword in already comptime scope diff --git a/test/incremental/redundant_comptime.1.zig b/test/incremental/redundant_comptime.1.zig new file mode 100644 index 0000000000..79226ae8a6 --- /dev/null +++ b/test/incremental/redundant_comptime.1.zig @@ -0,0 +1,9 @@ +pub fn main() void { + comptime { + var a: u32 = comptime 0; + } +} + +// error +// +// :3:22: error: redundant comptime keyword in already comptime scope diff --git a/test/incremental/returns_in_try.zig b/test/incremental/returns_in_try.zig new file mode 100644 index 0000000000..de5a9a3258 --- /dev/null +++ b/test/incremental/returns_in_try.zig @@ -0,0 +1,16 @@ +pub fn main() !void { + try a(); + try b(); +} + +pub fn a() !void { + defer try b(); +} +pub fn b() !void { + defer return a(); +} + +// error +// +// :7:11: error: 'try' not allowed inside defer expression +// :10:11: error: cannot return from defer expression diff --git a/test/incremental/runtime_bitwise_and.zig b/test/incremental/runtime_bitwise_and.zig new file mode 100644 index 0000000000..3ba3d3124c --- /dev/null +++ b/test/incremental/runtime_bitwise_and.zig @@ -0,0 +1,16 @@ +pub fn main() void { + var i: u32 = 10; + var j: u32 = 11; + assert(i & 1 == 0); + assert(j & 1 == 1); + var m1: u32 = 0b1111; + var m2: u32 = 0b0000; + assert(m1 & 0b1010 == 0b1010); + assert(m2 & 0b1010 == 0b0000); +} +fn assert(b: bool) void { + if (!b) unreachable; +} + +// run +// diff --git a/test/incremental/runtime_bitwise_or.zig b/test/incremental/runtime_bitwise_or.zig new file mode 100644 index 0000000000..b97c2acf02 --- /dev/null +++ b/test/incremental/runtime_bitwise_or.zig @@ -0,0 +1,16 @@ +pub fn main() void { + var i: u32 = 10; + var j: u32 = 11; + assert(i | 1 == 11); + assert(j | 1 == 11); + var m1: u32 = 0b1111; + var m2: u32 = 0b0000; + assert(m1 | 0b1010 == 0b1111); + assert(m2 | 0b1010 == 0b1010); +} +fn assert(b: bool) void { + if (!b) unreachable; +} + +// run +// diff --git a/test/incremental/saving_vars_of_different_abi_size_to_stack.0.zig b/test/incremental/saving_vars_of_different_abi_size_to_stack.0.zig new file mode 100644 index 0000000000..6134622a0e --- /dev/null +++ b/test/incremental/saving_vars_of_different_abi_size_to_stack.0.zig @@ -0,0 +1,16 @@ +pub fn main() void { + assert(callMe(2) == 24); +} + +fn callMe(a: u8) u8 { + var b: u8 = a + 10; + const c = 2 * b; + return c; +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/saving_vars_of_different_abi_size_to_stack.1.zig b/test/incremental/saving_vars_of_different_abi_size_to_stack.1.zig new file mode 100644 index 0000000000..4d87d7b9c5 --- /dev/null +++ b/test/incremental/saving_vars_of_different_abi_size_to_stack.1.zig @@ -0,0 +1,16 @@ +pub fn main() void { + assert(callMe(2) == 24); +} + +fn callMe(a: u16) u16 { + var b: u16 = a + 10; + const c = 2 * b; + return c; +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/saving_vars_of_different_abi_size_to_stack.2.zig b/test/incremental/saving_vars_of_different_abi_size_to_stack.2.zig new file mode 100644 index 0000000000..b665227962 --- /dev/null +++ b/test/incremental/saving_vars_of_different_abi_size_to_stack.2.zig @@ -0,0 +1,16 @@ +pub fn main() void { + assert(callMe(2) == 24); +} + +fn callMe(a: u32) u32 { + var b: u32 = a + 10; + const c = 2 * b; + return c; +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/setting_an_address_space_on_a_local_variable.zig b/test/incremental/setting_an_address_space_on_a_local_variable.zig new file mode 100644 index 0000000000..dad282dc31 --- /dev/null +++ b/test/incremental/setting_an_address_space_on_a_local_variable.zig @@ -0,0 +1,8 @@ +export fn entry() i32 { + var foo: i32 addrspace(".general") = 1234; + return foo; +} + +// error +// +// :2:28: error: cannot set address space of local variable 'foo' diff --git a/test/incremental/try_in_comptime_in_struct_in_test.zig b/test/incremental/try_in_comptime_in_struct_in_test.zig new file mode 100644 index 0000000000..e88aa13a7b --- /dev/null +++ b/test/incremental/try_in_comptime_in_struct_in_test.zig @@ -0,0 +1,13 @@ +test "@unionInit on union w/ tag but no fields" { + const S = struct { + comptime { + try expect(false); + } + }; + _ = S; +} + +// error +// is_test=1 +// +// :4:13: error: 'try' outside function scope diff --git a/test/incremental/type_of.0.zig b/test/incremental/type_of.0.zig new file mode 100644 index 0000000000..d8a97d1444 --- /dev/null +++ b/test/incremental/type_of.0.zig @@ -0,0 +1,13 @@ +pub fn main() void { + var x: usize = 0; + _ = x; + const z = @TypeOf(x, @as(u128, 5)); + assert(z == u128); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/type_of.1.zig b/test/incremental/type_of.1.zig new file mode 100644 index 0000000000..86d7d87a19 --- /dev/null +++ b/test/incremental/type_of.1.zig @@ -0,0 +1,11 @@ +pub fn main() void { + const z = @TypeOf(true); + assert(z == bool); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/type_of.2.zig b/test/incremental/type_of.2.zig new file mode 100644 index 0000000000..cdbc8121fd --- /dev/null +++ b/test/incremental/type_of.2.zig @@ -0,0 +1,9 @@ +pub fn main() void { + _ = @TypeOf(true, 1); +} + +// error +// +// :2:9: error: incompatible types: 'bool' and 'comptime_int' +// :2:17: note: type 'bool' here +// :2:23: note: type 'comptime_int' here diff --git a/test/incremental/unused_labels.0.zig b/test/incremental/unused_labels.0.zig new file mode 100644 index 0000000000..9afa5facdd --- /dev/null +++ b/test/incremental/unused_labels.0.zig @@ -0,0 +1,8 @@ +comptime { + foo: {} +} + +// error +// output_mode=Exe +// +// :2:5: error: unused block label diff --git a/test/incremental/unused_labels.1.zig b/test/incremental/unused_labels.1.zig new file mode 100644 index 0000000000..c7ff576875 --- /dev/null +++ b/test/incremental/unused_labels.1.zig @@ -0,0 +1,7 @@ +comptime { + foo: while (true) {} +} + +// error +// +// :2:5: error: unused while loop label diff --git a/test/incremental/unused_labels.2.zig b/test/incremental/unused_labels.2.zig new file mode 100644 index 0000000000..babe3c7b0a --- /dev/null +++ b/test/incremental/unused_labels.2.zig @@ -0,0 +1,7 @@ +comptime { + foo: for ("foo") |_| {} +} + +// error +// +// :2:5: error: unused for loop label diff --git a/test/incremental/unused_labels.3.zig b/test/incremental/unused_labels.3.zig new file mode 100644 index 0000000000..e4f3ac05a4 --- /dev/null +++ b/test/incremental/unused_labels.3.zig @@ -0,0 +1,8 @@ +comptime { + blk: {blk: {}} +} + +// error +// +// :2:11: error: redefinition of label 'blk' +// :2:5: note: previous definition here diff --git a/test/incremental/unwrap_error_union_simple_errors.0.zig b/test/incremental/unwrap_error_union_simple_errors.0.zig new file mode 100644 index 0000000000..a1e2da9340 --- /dev/null +++ b/test/incremental/unwrap_error_union_simple_errors.0.zig @@ -0,0 +1,10 @@ +pub fn main() void { + maybeErr() catch unreachable; +} + +fn maybeErr() !void { + return; +} + +// run +// diff --git a/test/incremental/unwrap_error_union_simple_errors.1.zig b/test/incremental/unwrap_error_union_simple_errors.1.zig new file mode 100644 index 0000000000..830ee629bc --- /dev/null +++ b/test/incremental/unwrap_error_union_simple_errors.1.zig @@ -0,0 +1,11 @@ +pub fn main() void { + maybeErr() catch return; + unreachable; +} + +fn maybeErr() !void { + return error.NoWay; +} + +// run +// diff --git a/test/incremental/variable_shadowing.0.zig b/test/incremental/variable_shadowing.0.zig new file mode 100644 index 0000000000..0ab53a78f9 --- /dev/null +++ b/test/incremental/variable_shadowing.0.zig @@ -0,0 +1,9 @@ +pub fn main() void { + var i: u32 = 10; + var i: u32 = 10; +} + +// error +// +// :3:9: error: redeclaration of local variable 'i' +// :2:9: note: previous declaration here diff --git a/test/incremental/variable_shadowing.1.zig b/test/incremental/variable_shadowing.1.zig new file mode 100644 index 0000000000..af24af038d --- /dev/null +++ b/test/incremental/variable_shadowing.1.zig @@ -0,0 +1,9 @@ +var testing: i64 = 10; +pub fn main() void { + var testing: i64 = 20; +} + +// error +// +// :3:9: error: local shadows declaration of 'testing' +// :1:1: note: declared here diff --git a/test/incremental/variable_shadowing.2.zig b/test/incremental/variable_shadowing.2.zig new file mode 100644 index 0000000000..a44372f9b9 --- /dev/null +++ b/test/incremental/variable_shadowing.2.zig @@ -0,0 +1,13 @@ +fn a() type { + return struct { + pub fn b() void { + const c = 6; + const c = 69; + } + }; +} + +// error +// +// :5:19: error: redeclaration of local constant 'c' +// :4:19: note: previous declaration here diff --git a/test/incremental/variable_shadowing.3.zig b/test/incremental/variable_shadowing.3.zig new file mode 100644 index 0000000000..89288705e1 --- /dev/null +++ b/test/incremental/variable_shadowing.3.zig @@ -0,0 +1,10 @@ +pub fn main() void { + var i = 0; + for ("n") |_, i| { + } +} + +// error +// +// :3:19: error: redeclaration of local variable 'i' +// :2:9: note: previous declaration here diff --git a/test/incremental/variable_shadowing.4.zig b/test/incremental/variable_shadowing.4.zig new file mode 100644 index 0000000000..b798cc13c1 --- /dev/null +++ b/test/incremental/variable_shadowing.4.zig @@ -0,0 +1,10 @@ +pub fn main() void { + var i = 0; + for ("n") |i| { + } +} + +// error +// +// :3:16: error: redeclaration of local variable 'i' +// :2:9: note: previous declaration here diff --git a/test/incremental/variable_shadowing.5.zig b/test/incremental/variable_shadowing.5.zig new file mode 100644 index 0000000000..484d218ea2 --- /dev/null +++ b/test/incremental/variable_shadowing.5.zig @@ -0,0 +1,10 @@ +pub fn main() void { + var i = 0; + while ("n") |i| { + } +} + +// error +// +// :3:18: error: redeclaration of local variable 'i' +// :2:9: note: previous declaration here diff --git a/test/incremental/variable_shadowing.6.zig b/test/incremental/variable_shadowing.6.zig new file mode 100644 index 0000000000..3e53c4da2f --- /dev/null +++ b/test/incremental/variable_shadowing.6.zig @@ -0,0 +1,13 @@ +pub fn main() void { + var i = 0; + while ("n") |bruh| { + _ = bruh; + } else |i| { + + } +} + +// error +// +// :5:13: error: redeclaration of local variable 'i' +// :2:9: note: previous declaration here diff --git a/test/incremental/variable_shadowing.7.zig b/test/incremental/variable_shadowing.7.zig new file mode 100644 index 0000000000..95888839a7 --- /dev/null +++ b/test/incremental/variable_shadowing.7.zig @@ -0,0 +1,9 @@ +pub fn main() void { + var i = 0; + if (true) |i| {} +} + +// error +// +// :3:16: error: redeclaration of local variable 'i' +// :2:9: note: previous declaration here diff --git a/test/incremental/variable_shadowing.8.zig b/test/incremental/variable_shadowing.8.zig new file mode 100644 index 0000000000..57e616e1d6 --- /dev/null +++ b/test/incremental/variable_shadowing.8.zig @@ -0,0 +1,9 @@ +pub fn main() void { + var i = 0; + if (true) |i| {} else |e| {} +} + +// error +// +// :3:16: error: redeclaration of local variable 'i' +// :2:9: note: previous declaration here diff --git a/test/incremental/variable_shadowing.9.zig b/test/incremental/variable_shadowing.9.zig new file mode 100644 index 0000000000..b8a1198aed --- /dev/null +++ b/test/incremental/variable_shadowing.9.zig @@ -0,0 +1,9 @@ +pub fn main() void { + var i = 0; + if (true) |_| {} else |i| {} +} + +// error +// +// :3:28: error: redeclaration of local variable 'i' +// :2:9: note: previous declaration here From e4a8a665040b22b52519b68433ed58b1e34f841d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 27 Apr 2022 17:53:24 +0200 Subject: [PATCH 1279/2031] test: remove incremental tests that were ported to the new harness --- test/stage2/x86_64.zig | 2295 +--------------------------------------- 1 file changed, 33 insertions(+), 2262 deletions(-) diff --git a/test/stage2/x86_64.zig b/test/stage2/x86_64.zig index 214b32b025..a4c506400a 100644 --- a/test/stage2/x86_64.zig +++ b/test/stage2/x86_64.zig @@ -16,2273 +16,44 @@ const all_targets: []const CrossTarget = &[_]CrossTarget{ }; pub fn addCases(ctx: *TestContext) !void { - try addLinuxTestCases(ctx); - try addMacOsTestCases(ctx); - - // Common tests for (all_targets) |target| { - { - var case = ctx.exe("adding numbers at runtime and comptime", target); - case.addCompareOutput( - \\pub fn main() void { - \\ add(3, 4); - \\} - \\ - \\fn add(a: u32, b: u32) void { - \\ if (a + b != 7) unreachable; - \\} - , - "", - ); - // comptime function call - case.addCompareOutput( - \\pub fn main() void { - \\ if (x - 7 != 0) unreachable; - \\} - \\ - \\fn add(a: u32, b: u32) u32 { - \\ return a + b; - \\} - \\ - \\const x = add(3, 4); - , - "", - ); - // Inline function call - case.addCompareOutput( - \\pub fn main() void { - \\ var x: usize = 3; - \\ const y = add(1, 2, x); - \\ if (y - 6 != 0) unreachable; - \\} - \\ - \\fn add(a: usize, b: usize, c: usize) callconv(.Inline) usize { - \\ return a + b + c; - \\} - , - "", - ); - } - - { - var case = ctx.exe("subtracting numbers at runtime", target); - case.addCompareOutput( - \\pub fn main() void { - \\ sub(7, 4); - \\} - \\ - \\fn sub(a: u32, b: u32) void { - \\ if (a - b != 3) unreachable; - \\} - , - "", - ); - } - - { - var case = ctx.exe("unused vars", target); - case.addError( - \\pub fn main() void { - \\ const x = 1; - \\} - , &.{":2:11: error: unused local constant"}); - } - - { - var case = ctx.exe("multiplying numbers at runtime and comptime", target); - case.addCompareOutput( - \\pub fn main() void { - \\ mul(3, 4); - \\} - \\ - \\fn mul(a: u32, b: u32) void { - \\ if (a * b != 12) unreachable; - \\} - , - "", - ); - // comptime function call - case.addCompareOutput( - \\pub fn main() void { - \\ if (x - 12 != 0) unreachable; - \\} - \\ - \\fn mul(a: u32, b: u32) u32 { - \\ return a * b; - \\} - \\ - \\const x = mul(3, 4); - , - "", - ); - // Inline function call - case.addCompareOutput( - \\pub fn main() void { - \\ var x: usize = 5; - \\ const y = mul(2, 3, x); - \\ if (y - 30 != 0) unreachable; - \\} - \\ - \\fn mul(a: usize, b: usize, c: usize) callconv(.Inline) usize { - \\ return a * b * c; - \\} - , - "", - ); - } - - { - var case = ctx.exe("assert function", target); - case.addCompareOutput( - \\pub fn main() void { - \\ add(3, 4); - \\} - \\ - \\fn add(a: u32, b: u32) void { - \\ assert(a + b == 7); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Tests copying a register. For the `c = a + b`, it has to - // preserve both a and b, because they are both used later. - case.addCompareOutput( - \\pub fn main() void { - \\ add(3, 4); - \\} - \\ - \\fn add(a: u32, b: u32) void { - \\ const c = a + b; // 7 - \\ const d = a + c; // 10 - \\ const e = d + b; // 14 - \\ assert(e == 14); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // More stress on the liveness detection. - case.addCompareOutput( - \\pub fn main() void { - \\ add(3, 4); - \\} - \\ - \\fn add(a: u32, b: u32) void { - \\ const c = a + b; // 7 - \\ const d = a + c; // 10 - \\ const e = d + b; // 14 - \\ const f = d + e; // 24 - \\ const g = e + f; // 38 - \\ const h = f + g; // 62 - \\ const i = g + h; // 100 - \\ assert(i == 100); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Requires a second move. The register allocator should figure out to re-use rax. - case.addCompareOutput( - \\pub fn main() void { - \\ add(3, 4); - \\} - \\ - \\fn add(a: u32, b: u32) void { - \\ const c = a + b; // 7 - \\ const d = a + c; // 10 - \\ const e = d + b; // 14 - \\ const f = d + e; // 24 - \\ const g = e + f; // 38 - \\ const h = f + g; // 62 - \\ const i = g + h; // 100 - \\ const j = i + d; // 110 - \\ assert(j == 110); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Now we test integer return values. - case.addCompareOutput( - \\pub fn main() void { - \\ assert(add(3, 4) == 7); - \\ assert(add(20, 10) == 30); - \\} - \\ - \\fn add(a: u32, b: u32) u32 { - \\ return a + b; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Local mutable variables. - case.addCompareOutput( - \\pub fn main() void { - \\ assert(add(3, 4) == 7); - \\ assert(add(20, 10) == 30); - \\} - \\ - \\fn add(a: u32, b: u32) u32 { - \\ var x: u32 = undefined; - \\ x = 0; - \\ x += a; - \\ x += b; - \\ return x; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Optionals - case.addCompareOutput( - \\pub fn main() void { - \\ const a: u32 = 2; - \\ const b: ?u32 = a; - \\ const c = b.?; - \\ if (c != 2) unreachable; - \\} - , - "", - ); - - switch (target.getOsTag()) { - .linux => { - // While loops - case.addCompareOutput( - \\pub fn main() void { - \\ var i: u32 = 0; - \\ while (i < 4) : (i += 1) print(); - \\ assert(i == 4); - \\} - \\ - \\fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("hello\n")), - \\ [arg3] "{rdx}" (6) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "hello\nhello\nhello\nhello\n", - ); - - // inline while requires the condition to be comptime known. - case.addError( - \\pub fn main() void { - \\ var i: u32 = 0; - \\ inline while (i < 4) : (i += 1) print(); - \\ assert(i == 4); - \\} - \\ - \\fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("hello\n")), - \\ [arg3] "{rdx}" (6) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , &[_][]const u8{":3:21: error: unable to resolve comptime value"}); - }, - .macos => { - // While loops - case.addCompareOutput( - \\extern "c" fn write(usize, usize, usize) usize; - \\ - \\pub fn main() void { - \\ var i: u32 = 0; - \\ while (i < 4) : (i += 1) print(); - \\ assert(i == 4); - \\} - \\ - \\fn print() void { - \\ _ = write(1, @ptrToInt("hello\n"), 6); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "hello\nhello\nhello\nhello\n", - ); - - // inline while requires the condition to be comptime known. - case.addError( - \\extern "c" fn write(usize, usize, usize) usize; - \\ - \\pub fn main() void { - \\ var i: u32 = 0; - \\ inline while (i < 4) : (i += 1) print(); - \\ assert(i == 4); - \\} - \\ - \\fn print() void { - \\ _ = write(1, @ptrToInt("hello\n"), 6); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , &[_][]const u8{":5:21: error: unable to resolve comptime value"}); - }, - else => unreachable, - } - - // Labeled blocks (no conditional branch) - case.addCompareOutput( - \\pub fn main() void { - \\ assert(add(3, 4) == 20); - \\} - \\ - \\fn add(a: u32, b: u32) u32 { - \\ const x: u32 = blk: { - \\ const c = a + b; // 7 - \\ const d = a + c; // 10 - \\ const e = d + b; // 14 - \\ break :blk e; - \\ }; - \\ const y = x + a; // 17 - \\ const z = y + a; // 20 - \\ return z; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // This catches a possible bug in the logic for re-using dying operands. - case.addCompareOutput( - \\pub fn main() void { - \\ assert(add(3, 4) == 116); - \\} - \\ - \\fn add(a: u32, b: u32) u32 { - \\ const x: u32 = blk: { - \\ const c = a + b; // 7 - \\ const d = a + c; // 10 - \\ const e = d + b; // 14 - \\ const f = d + e; // 24 - \\ const g = e + f; // 38 - \\ const h = f + g; // 62 - \\ const i = g + h; // 100 - \\ const j = i + d; // 110 - \\ break :blk j; - \\ }; - \\ const y = x + a; // 113 - \\ const z = y + a; // 116 - \\ return z; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Spilling registers to the stack. - case.addCompareOutput( - \\pub fn main() void { - \\ assert(add(3, 4) == 1221); - \\ assert(mul(3, 4) == 21609); - \\} - \\ - \\fn add(a: u32, b: u32) u32 { - \\ const x: u32 = blk: { - \\ const c = a + b; // 7 - \\ const d = a + c; // 10 - \\ const e = d + b; // 14 - \\ const f = d + e; // 24 - \\ const g = e + f; // 38 - \\ const h = f + g; // 62 - \\ const i = g + h; // 100 - \\ const j = i + d; // 110 - \\ const k = i + j; // 210 - \\ const l = j + k; // 320 - \\ const m = l + c; // 327 - \\ const n = m + d; // 337 - \\ const o = n + e; // 351 - \\ const p = o + f; // 375 - \\ const q = p + g; // 413 - \\ const r = q + h; // 475 - \\ const s = r + i; // 575 - \\ const t = s + j; // 685 - \\ const u = t + k; // 895 - \\ const v = u + l; // 1215 - \\ break :blk v; - \\ }; - \\ const y = x + a; // 1218 - \\ const z = y + a; // 1221 - \\ return z; - \\} - \\ - \\fn mul(a: u32, b: u32) u32 { - \\ const x: u32 = blk: { - \\ const c = a * a * a * a; // 81 - \\ const d = a * a * a * b; // 108 - \\ const e = a * a * b * a; // 108 - \\ const f = a * a * b * b; // 144 - \\ const g = a * b * a * a; // 108 - \\ const h = a * b * a * b; // 144 - \\ const i = a * b * b * a; // 144 - \\ const j = a * b * b * b; // 192 - \\ const k = b * a * a * a; // 108 - \\ const l = b * a * a * b; // 144 - \\ const m = b * a * b * a; // 144 - \\ const n = b * a * b * b; // 192 - \\ const o = b * b * a * a; // 144 - \\ const p = b * b * a * b; // 192 - \\ const q = b * b * b * a; // 192 - \\ const r = b * b * b * b; // 256 - \\ const s = c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r; // 2401 - \\ break :blk s; - \\ }; - \\ const y = x * a; // 7203 - \\ const z = y * a; // 21609 - \\ return z; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Reusing the registers of dead operands playing nicely with conditional branching. - case.addCompareOutput( - \\pub fn main() void { - \\ assert(add(3, 4) == 791); - \\ assert(add(4, 3) == 79); - \\} - \\ - \\fn add(a: u32, b: u32) u32 { - \\ const x: u32 = if (a < b) blk: { - \\ const c = a + b; // 7 - \\ const d = a + c; // 10 - \\ const e = d + b; // 14 - \\ const f = d + e; // 24 - \\ const g = e + f; // 38 - \\ const h = f + g; // 62 - \\ const i = g + h; // 100 - \\ const j = i + d; // 110 - \\ const k = i + j; // 210 - \\ const l = k + c; // 217 - \\ const m = l + d; // 227 - \\ const n = m + e; // 241 - \\ const o = n + f; // 265 - \\ const p = o + g; // 303 - \\ const q = p + h; // 365 - \\ const r = q + i; // 465 - \\ const s = r + j; // 575 - \\ const t = s + k; // 785 - \\ break :blk t; - \\ } else blk: { - \\ const t = b + b + a; // 10 - \\ const c = a + t; // 14 - \\ const d = c + t; // 24 - \\ const e = d + t; // 34 - \\ const f = e + t; // 44 - \\ const g = f + t; // 54 - \\ const h = c + g; // 68 - \\ break :blk h + b; // 71 - \\ }; - \\ const y = x + a; // 788, 75 - \\ const z = y + a; // 791, 79 - \\ return z; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Character literals and multiline strings. - case.addCompareOutput( - \\pub fn main() void { - \\ const ignore = - \\ \\ cool thx - \\ \\ - \\ ; - \\ _ = ignore; - \\ add('ぁ', '\x03'); - \\} - \\ - \\fn add(a: u32, b: u32) void { - \\ assert(a + b == 12356); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Global const. - case.addCompareOutput( - \\pub fn main() void { - \\ add(aa, bb); - \\} - \\ - \\const aa = 'ぁ'; - \\const bb = '\x03'; - \\ - \\fn add(a: u32, b: u32) void { - \\ assert(a + b == 12356); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Array access. - case.addCompareOutput( - \\pub fn main() void { - \\ assert("hello"[0] == 'h'); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Array access to a global array. - case.addCompareOutput( - \\const hello = "hello".*; - \\pub fn main() void { - \\ assert(hello[1] == 'e'); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // 64bit set stack - case.addCompareOutput( - \\pub fn main() void { - \\ var i: u64 = 0xFFEEDDCCBBAA9988; - \\ assert(i == 0xFFEEDDCCBBAA9988); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - switch (target.getOsTag()) { - .linux => { - // Basic for loop - case.addCompareOutput( - \\pub fn main() void { - \\ for ("hello") |_| print(); - \\} - \\ - \\fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("hello\n")), - \\ [arg3] "{rdx}" (6) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - , - "hello\nhello\nhello\nhello\nhello\n", - ); - }, - .macos => { - // Basic for loop - case.addCompareOutput( - \\extern "c" fn write(usize, usize, usize) usize; - \\ - \\pub fn main() void { - \\ for ("hello") |_| print(); - \\} - \\ - \\fn print() void { - \\ _ = write(1, @ptrToInt("hello\n"), 6); - \\} - , - "hello\nhello\nhello\nhello\nhello\n", - ); - }, - else => unreachable, - } - } - - { - var case = ctx.exe("@TypeOf", target); - case.addCompareOutput( - \\pub fn main() void { - \\ var x: usize = 0; - \\ _ = x; - \\ const z = @TypeOf(x, @as(u128, 5)); - \\ assert(z == u128); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - case.addCompareOutput( - \\pub fn main() void { - \\ const z = @TypeOf(true); - \\ assert(z == bool); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - case.addError( - \\pub fn main() void { - \\ _ = @TypeOf(true, 1); - \\} - , &[_][]const u8{ - ":2:9: error: incompatible types: 'bool' and 'comptime_int'", - ":2:17: note: type 'bool' here", - ":2:23: note: type 'comptime_int' here", - }); - } - - { - var case = ctx.exe("basic import", target); - case.addCompareOutput( - \\pub fn main() void { - \\ @import("print.zig").print(); - \\} - , - "Hello, World!\n", - ); - switch (target.getOsTag()) { - .linux => try case.files.append(.{ - .src = - \\pub fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (@as(usize, 1)), - \\ [arg1] "{rdi}" (@as(usize, 1)), - \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{rdx}" (@as(usize, 14)) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - , - .path = "print.zig", - }), - .macos => try case.files.append(.{ - .src = - \\extern "c" fn write(usize, usize, usize) usize; - \\ - \\pub fn print() void { - \\ _ = write(1, @ptrToInt("Hello, World!\n"), 14); - \\} - , - .path = "print.zig", - }), - else => unreachable, - } - } - - { - var case = ctx.exe("redundant comptime", target); - case.addError( - \\pub fn main() void { - \\ var a: comptime u32 = 0; - \\} - , - &.{":2:12: error: redundant comptime keyword in already comptime scope"}, - ); - case.addError( - \\pub fn main() void { - \\ comptime { - \\ var a: u32 = comptime 0; - \\ } - \\} - , - &.{":3:22: error: redundant comptime keyword in already comptime scope"}, - ); - } - { - var case = ctx.exe("try in comptime in struct in test", target); - case.addError( - \\test "@unionInit on union w/ tag but no fields" { - \\ const S = struct { - \\ comptime { - \\ try expect(false); - \\ } - \\ }; - \\ _ = S; - \\} - , - &.{":4:13: error: 'try' outside function scope"}, - ); - } - { - var case = ctx.exe("import private", target); - case.addError( - \\pub fn main() void { - \\ @import("print.zig").print(); - \\} - , - &.{ - ":2:25: error: 'print' is not marked 'pub'", - "print.zig:2:1: note: declared here", - }, - ); - switch (target.getOsTag()) { - .linux => try case.files.append(.{ - .src = - \\// dummy comment to make print be on line 2 - \\fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (@as(usize, 1)), - \\ [arg1] "{rdi}" (@as(usize, 1)), - \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{rdx}" (@as(usize, 14)) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - , - .path = "print.zig", - }), - .macos => try case.files.append(.{ - .src = - \\extern "c" fn write(usize, usize, usize) usize; - \\fn print() void { - \\ _ = write(1, @ptrToInt("Hello, World!\n"), 14); - \\} - , - .path = "print.zig", - }), - else => unreachable, - } - } - - ctx.compileError("function redeclaration", target, - \\// dummy comment - \\fn entry() void {} - \\fn entry() void {} - \\ - \\fn foo() void { - \\ var foo = 1234; + // TODO port this to the new test harness + var case = ctx.exe("basic import", target); + case.addCompareOutput( + \\pub fn main() void { + \\ @import("print.zig").print(); \\} - , &[_][]const u8{ - ":3:1: error: redeclaration of 'entry'", - ":2:1: note: other declaration here", - ":6:9: error: local shadows declaration of 'foo'", - ":5:1: note: declared here", - }); - - ctx.compileError("returns in try", target, - \\pub fn main() !void { - \\ try a(); - \\ try b(); - \\} - \\ - \\pub fn a() !void { - \\ defer try b(); - \\} - \\pub fn b() !void { - \\ defer return a(); - \\} - , &[_][]const u8{ - ":7:8: error: 'try' not allowed inside defer expression", - ":10:8: error: cannot return from defer expression", - }); - - ctx.compileError("ambiguous references", target, - \\const T = struct { - \\ const T = struct { - \\ fn f() void { - \\ _ = T; - \\ } - \\ }; - \\}; - , &.{ - ":4:17: error: ambiguous reference", - ":2:5: note: declared here", - ":1:1: note: also declared here", - }); - - ctx.compileError("inner func accessing outer var", target, - \\pub fn f() void { - \\ var bar: bool = true; - \\ const S = struct { - \\ fn baz() bool { - \\ return bar; - \\ } - \\ }; - \\ _ = S; - \\} - , &.{ - ":5:20: error: mutable 'bar' not accessible from here", - ":2:9: note: declared mutable here", - ":3:15: note: crosses namespace boundary here", - }); - - ctx.compileError("global variable redeclaration", target, - \\// dummy comment - \\var foo = false; - \\var foo = true; - , &[_][]const u8{ - ":3:1: error: redeclaration of 'foo'", - ":2:1: note: other declaration here", - }); - - ctx.compileError("compileError", target, - \\export fn foo() void { - \\ @compileError("this is an error"); - \\} - , &[_][]const u8{":2:3: error: this is an error"}); - - { - var case = ctx.exe("intToPtr", target); - case.addError( - \\pub fn main() void { - \\ _ = @intToPtr(*u8, 0); - \\} - , &[_][]const u8{ - ":2:24: error: pointer type '*u8' does not allow address zero", - }); - case.addError( - \\pub fn main() void { - \\ _ = @intToPtr(*u32, 2); - \\} - , &[_][]const u8{ - ":2:25: error: pointer type '*u32' requires aligned address", - }); - } - - { - var case = ctx.obj("variable shadowing", target); - case.addError( - \\pub fn main() void { - \\ var i: u32 = 10; - \\ var i: u32 = 10; - \\} - , &[_][]const u8{ - ":3:9: error: redeclaration of local variable 'i'", - ":2:9: note: previous declaration here", - }); - case.addError( - \\var testing: i64 = 10; - \\pub fn main() void { - \\ var testing: i64 = 20; - \\} - , &[_][]const u8{ - ":3:9: error: local shadows declaration of 'testing'", - ":1:1: note: declared here", - }); - case.addError( - \\fn a() type { - \\ return struct { - \\ pub fn b() void { - \\ const c = 6; - \\ const c = 69; - \\ } - \\ }; - \\} - , &[_][]const u8{ - ":5:19: error: redeclaration of local constant 'c'", - ":4:19: note: previous declaration here", - }); - case.addError( - \\pub fn main() void { - \\ var i = 0; - \\ for ("n") |_, i| { - \\ } - \\} - , &[_][]const u8{ - ":3:19: error: redeclaration of local variable 'i'", - ":2:9: note: previous declaration here", - }); - case.addError( - \\pub fn main() void { - \\ var i = 0; - \\ for ("n") |i| { - \\ } - \\} - , &[_][]const u8{ - ":3:16: error: redeclaration of local variable 'i'", - ":2:9: note: previous declaration here", - }); - case.addError( - \\pub fn main() void { - \\ var i = 0; - \\ while ("n") |i| { - \\ } - \\} - , &[_][]const u8{ - ":3:18: error: redeclaration of local variable 'i'", - ":2:9: note: previous declaration here", - }); - case.addError( - \\pub fn main() void { - \\ var i = 0; - \\ while ("n") |bruh| { - \\ _ = bruh; - \\ } else |i| { - \\ - \\ } - \\} - , &[_][]const u8{ - ":5:13: error: redeclaration of local variable 'i'", - ":2:9: note: previous declaration here", - }); - case.addError( - \\pub fn main() void { - \\ var i = 0; - \\ if (true) |i| {} - \\} - , &[_][]const u8{ - ":3:16: error: redeclaration of local variable 'i'", - ":2:9: note: previous declaration here", - }); - case.addError( - \\pub fn main() void { - \\ var i = 0; - \\ if (true) |i| {} else |e| {} - \\} - , &[_][]const u8{ - ":3:16: error: redeclaration of local variable 'i'", - ":2:9: note: previous declaration here", - }); - case.addError( - \\pub fn main() void { - \\ var i = 0; - \\ if (true) |_| {} else |i| {} - \\} - , &[_][]const u8{ - ":3:28: error: redeclaration of local variable 'i'", - ":2:9: note: previous declaration here", - }); - } - - { - // TODO make the test harness support checking the compile log output too - var case = ctx.obj("@compileLog", target); - // The other compile error prevents emission of a "found compile log" statement. - case.addError( - \\export fn _start() noreturn { - \\ const b = true; - \\ var f: u32 = 1; - \\ @compileLog(b, 20, f, x); - \\ @compileLog(1000); - \\ var bruh: usize = true; - \\ _ = bruh; - \\ unreachable; - \\} - \\export fn other() void { - \\ @compileLog(1234); - \\} - \\fn x() void {} - , &[_][]const u8{ - ":6:23: error: expected usize, found bool", - }); - - // Now only compile log statements remain. One per Decl. - case.addError( - \\export fn _start() noreturn { - \\ const b = true; - \\ var f: u32 = 1; - \\ @compileLog(b, 20, f, x); - \\ @compileLog(1000); - \\ unreachable; - \\} - \\export fn other() void { - \\ @compileLog(1234); - \\} - \\fn x() void {} - , &[_][]const u8{ - ":9:5: error: found compile log statement", - ":4:5: note: also here", - }); - } - - { - var case = ctx.obj("extern variable has no type", target); - case.addError( - \\comptime { - \\ const x = foo + foo; - \\ _ = x; - \\} - \\extern var foo: i32; - , &[_][]const u8{":2:15: error: unable to resolve comptime value"}); - case.addError( - \\export fn entry() void { - \\ _ = foo; - \\} - \\extern var foo; - , &[_][]const u8{":4:8: error: unable to infer variable type"}); - } - - { - var case = ctx.exe("break/continue", target); - - // Break out of loop - case.addCompareOutput( - \\pub fn main() void { - \\ while (true) { - \\ break; - \\ } - \\} - , - "", - ); - case.addCompareOutput( - \\pub fn main() void { - \\ foo: while (true) { - \\ break :foo; - \\ } - \\} - , - "", - ); - - // Continue in loop - case.addCompareOutput( - \\pub fn main() void { - \\ var i: u64 = 0; - \\ while (true) : (i+=1) { - \\ if (i == 4) return; - \\ continue; - \\ } - \\} - , - "", - ); - case.addCompareOutput( - \\pub fn main() void { - \\ var i: u64 = 0; - \\ foo: while (true) : (i+=1) { - \\ if (i == 4) return; - \\ continue :foo; - \\ } - \\} - , - "", - ); - } - - { - var case = ctx.exe("unused labels", target); - case.addError( - \\comptime { - \\ foo: {} - \\} - , &[_][]const u8{":2:5: error: unused block label"}); - case.addError( - \\comptime { - \\ foo: while (true) {} - \\} - , &[_][]const u8{":2:5: error: unused while loop label"}); - case.addError( - \\comptime { - \\ foo: for ("foo") |_| {} - \\} - , &[_][]const u8{":2:5: error: unused for loop label"}); - case.addError( - \\comptime { - \\ blk: {blk: {}} - \\} - , &[_][]const u8{ - ":2:11: error: redefinition of label 'blk'", - ":2:5: note: previous definition here", - }); - } - - { - var case = ctx.exe("bad inferred variable type", target); - case.addError( - \\pub fn main() void { - \\ var x = null; - \\ _ = x; - \\} - , &[_][]const u8{ - ":2:9: error: variable of type '@TypeOf(null)' must be const or comptime", - }); - } - - { - var case = ctx.exe("compile error in inline fn call fixed", target); - case.addError( - \\pub fn main() void { - \\ var x: usize = 3; - \\ const y = add(10, 2, x); - \\ if (y - 6 != 0) unreachable; - \\} - \\ - \\fn add(a: usize, b: usize, c: usize) callconv(.Inline) usize { - \\ if (a == 10) @compileError("bad"); - \\ return a + b + c; - \\} - , &[_][]const u8{ - ":8:18: error: bad", - ":3:18: note: called from here", - }); - - case.addCompareOutput( - \\pub fn main() void { - \\ var x: usize = 3; - \\ const y = add(1, 2, x); - \\ if (y - 6 != 0) unreachable; - \\} - \\ - \\fn add(a: usize, b: usize, c: usize) callconv(.Inline) usize { - \\ if (a == 10) @compileError("bad"); - \\ return a + b + c; - \\} - , - "", - ); - } - { - var case = ctx.exe("recursive inline function", target); - case.addCompareOutput( - \\pub fn main() void { - \\ const y = fibonacci(7); - \\ if (y - 21 != 0) unreachable; - \\} - \\ - \\fn fibonacci(n: usize) callconv(.Inline) usize { - \\ if (n <= 2) return n; - \\ return fibonacci(n - 2) + fibonacci(n - 1); - \\} - , - "", - ); - // This additionally tests that the compile error reports the correct source location. - // Without storing source locations relative to the owner decl, the compile error - // here would be off by 2 bytes (from the "7" -> "999"). - case.addError( - \\pub fn main() void { - \\ const y = fibonacci(999); - \\ if (y - 21 != 0) unreachable; - \\} - \\ - \\fn fibonacci(n: usize) callconv(.Inline) usize { - \\ if (n <= 2) return n; - \\ return fibonacci(n - 2) + fibonacci(n - 1); - \\} - , &[_][]const u8{":8:21: error: evaluation exceeded 1000 backwards branches"}); - } - { - var case = ctx.exe("orelse at comptime", target); - case.addCompareOutput( - \\pub fn main() void { - \\ const i: ?u64 = 0; - \\ const result = i orelse 5; - \\ assert(result == 0); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , - "", - ); - case.addCompareOutput( - \\pub fn main() void { - \\ const i: ?u64 = null; - \\ const result = i orelse 5; - \\ assert(result == 5); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , - "", - ); - } - - { - var case = ctx.exe("passing u0 to function", target); - case.addCompareOutput( - \\pub fn main() void { - \\ doNothing(0); - \\} - \\fn doNothing(arg: u0) void { - \\ _ = arg; - \\} - , - "", - ); - } - - { - var case = ctx.exe("catch at comptime", target); - case.addCompareOutput( - \\pub fn main() void { - \\ const i: anyerror!u64 = 0; - \\ const caught = i catch 5; - \\ assert(caught == 0); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , - "", - ); - - case.addCompareOutput( - \\pub fn main() void { - \\ const i: anyerror!u64 = error.B; - \\ const caught = i catch 5; - \\ assert(caught == 5); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , - "", - ); - - case.addCompareOutput( - \\pub fn main() void { - \\ const a: anyerror!comptime_int = 42; - \\ const b: *const comptime_int = &(a catch unreachable); - \\ assert(b.* == 42); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; // assertion failure - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ const a: anyerror!u32 = error.B; - \\ _ = &(a catch |err| assert(err == error.B)); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ const a: anyerror!u32 = error.Bar; - \\ a catch |err| assert(err == error.Bar); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , ""); - } - - { - var case = ctx.exe("runtime bitwise and", target); - - case.addCompareOutput( - \\pub fn main() void { - \\ var i: u32 = 10; - \\ var j: u32 = 11; - \\ assert(i & 1 == 0); - \\ assert(j & 1 == 1); - \\ var m1: u32 = 0b1111; - \\ var m2: u32 = 0b0000; - \\ assert(m1 & 0b1010 == 0b1010); - \\ assert(m2 & 0b1010 == 0b0000); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , - "", - ); - } - - { - var case = ctx.exe("runtime bitwise or", target); - - case.addCompareOutput( - \\pub fn main() void { - \\ var i: u32 = 10; - \\ var j: u32 = 11; - \\ assert(i | 1 == 11); - \\ assert(j | 1 == 11); - \\ var m1: u32 = 0b1111; - \\ var m2: u32 = 0b0000; - \\ assert(m1 | 0b1010 == 0b1111); - \\ assert(m2 | 0b1010 == 0b1010); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , - "", - ); - } - - { - var case = ctx.exe("merge error sets", target); - - case.addCompareOutput( - \\pub fn main() void { - \\ const E = error{ A, B, D } || error { A, B, C }; - \\ E.A catch {}; - \\ E.B catch {}; - \\ E.C catch {}; - \\ E.D catch {}; - \\ const E2 = error { X, Y } || @TypeOf(error.Z); - \\ E2.X catch {}; - \\ E2.Y catch {}; - \\ E2.Z catch {}; - \\ assert(anyerror || error { Z } == anyerror); - \\} - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , - "", - ); - case.addError( - \\pub fn main() void { - \\ const z = true || false; - \\ _ = z; - \\} - , &.{ - ":2:15: error: expected error set type, found 'bool'", - ":2:20: note: '||' merges error sets; 'or' performs boolean OR", - }); - } - - { - var case = ctx.exe("comptime var", target); - - case.addError( - \\pub fn main() void { - \\ var a: u32 = 0; - \\ comptime var b: u32 = 0; - \\ if (a == 0) b = 3; - \\} - , &.{ - ":4:21: error: store to comptime variable depends on runtime condition", - ":4:11: note: runtime condition here", - }); - - case.addError( - \\pub fn main() void { - \\ var a: u32 = 0; - \\ comptime var b: u32 = 0; - \\ switch (a) { - \\ 0 => {}, - \\ else => b = 3, - \\ } - \\} - , &.{ - ":6:21: error: store to comptime variable depends on runtime condition", - ":4:13: note: runtime condition here", - }); - - switch (target.getOsTag()) { - .linux => case.addCompareOutput( - \\pub fn main() void { - \\ comptime var len: u32 = 5; - \\ print(len); - \\ len += 9; - \\ print(len); - \\} - \\ - \\fn print(len: usize) void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{rdx}" (len) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - , "HelloHello, World!\n"), - .macos => case.addCompareOutput( - \\extern "c" fn write(usize, usize, usize) usize; - \\ - \\pub fn main() void { - \\ comptime var len: u32 = 5; - \\ print(len); - \\ len += 9; - \\ print(len); - \\} - \\ - \\fn print(len: usize) void { - \\ _ = write(1, @ptrToInt("Hello, World!\n"), len); - \\} - , "HelloHello, World!\n"), - - else => unreachable, - } - - case.addError( - \\comptime { - \\ var x: i32 = 1; - \\ x += 1; - \\ if (x != 1) unreachable; - \\} - \\pub fn main() void {} - , &.{":4:17: error: unable to resolve comptime value"}); - - case.addError( - \\pub fn main() void { - \\ comptime var i: u64 = 0; - \\ while (i < 5) : (i += 1) {} - \\} - , &.{ - ":3:24: error: cannot store to comptime variable in non-inline loop", - ":3:5: note: non-inline loop here", - }); - - case.addCompareOutput( - \\pub fn main() void { - \\ var a: u32 = 0; - \\ if (a == 0) { - \\ comptime var b: u32 = 0; - \\ b = 1; - \\ } - \\} - \\comptime { - \\ var x: i32 = 1; - \\ x += 1; - \\ if (x != 2) unreachable; - \\} - , ""); - - switch (target.getOsTag()) { - .linux => case.addCompareOutput( - \\pub fn main() void { - \\ comptime var i: u64 = 2; - \\ inline while (i < 6) : (i+=1) { - \\ print(i); - \\ } - \\} - \\fn print(len: usize) void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("Hello")), - \\ [arg3] "{rdx}" (len) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - , "HeHelHellHello"), - .macos => case.addCompareOutput( - \\extern "c" fn write(usize, usize, usize) usize; - \\ - \\pub fn main() void { - \\ comptime var i: u64 = 2; - \\ inline while (i < 6) : (i+=1) { - \\ print(i); - \\ } - \\} - \\fn print(len: usize) void { - \\ _ = write(1, @ptrToInt("Hello"), len); - \\} - , "HeHelHellHello"), - else => unreachable, - } - } - - { - var case = ctx.exe("double ampersand", target); - - case.addError( - \\pub const a = if (true && false) 1 else 2; - , &[_][]const u8{":1:24: error: ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND"}); - - case.addError( - \\pub fn main() void { - \\ const a = true; - \\ const b = false; - \\ _ = a & &b; - \\} - , &[_][]const u8{ - ":4:11: error: incompatible types: 'bool' and '*const bool'", - ":4:9: note: type 'bool' here", - ":4:13: note: type '*const bool' here", - }); - - case.addCompareOutput( - \\pub fn main() void { - \\ const b: u8 = 1; - \\ _ = &&b; - \\} - , ""); - } - - { - var case = ctx.exe("setting an address space on a local variable", target); - case.addError( - \\export fn entry() i32 { - \\ var foo: i32 addrspace(".general") = 1234; - \\ return foo; - \\} - , &[_][]const u8{ - ":2:28: error: cannot set address space of local variable 'foo'", - }); - } - - { - var case = ctx.exe("saving vars of different ABI size to stack", target); - - case.addCompareOutput( - \\pub fn main() void { - \\ assert(callMe(2) == 24); - \\} - \\ - \\fn callMe(a: u8) u8 { - \\ var b: u8 = a + 10; - \\ const c = 2 * b; - \\ return c; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - case.addCompareOutput( - \\pub fn main() void { - \\ assert(callMe(2) == 24); - \\} - \\ - \\fn callMe(a: u16) u16 { - \\ var b: u16 = a + 10; - \\ const c = 2 * b; - \\ return c; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - case.addCompareOutput( - \\pub fn main() void { - \\ assert(callMe(2) == 24); - \\} - \\ - \\fn callMe(a: u32) u32 { - \\ var b: u32 = a + 10; - \\ const c = 2 * b; - \\ return c; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - } - { - var case = ctx.exe("issue 7187: miscompilation with bool return type", target); - case.addCompareOutput( - \\pub fn main() void { - \\ var x: usize = 1; - \\ var y: bool = getFalse(); - \\ _ = y; - \\ - \\ assert(x == 1); - \\} - \\ - \\fn getFalse() bool { - \\ return false; - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , ""); - } - - { - var case = ctx.exe("load-store via pointer deref", target); - case.addCompareOutput( - \\pub fn main() void { - \\ var x: u32 = undefined; - \\ set(&x); - \\ assert(x == 123); - \\} - \\ - \\fn set(x: *u32) void { - \\ x.* = 123; - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , ""); - case.addCompareOutput( - \\pub fn main() void { - \\ var x: u16 = undefined; - \\ set(&x); - \\ assert(x == 123); - \\} - \\ - \\fn set(x: *u16) void { - \\ x.* = 123; - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , ""); - case.addCompareOutput( - \\pub fn main() void { - \\ var x: u8 = undefined; - \\ set(&x); - \\ assert(x == 123); - \\} - \\ - \\fn set(x: *u8) void { - \\ x.* = 123; - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , ""); - } - - { - var case = ctx.exe("optional payload", target); - case.addCompareOutput( - \\pub fn main() void { - \\ var x: u32 = undefined; - \\ const maybe_x = byPtr(&x); - \\ assert(maybe_x != null); - \\ maybe_x.?.* = 123; - \\ assert(x == 123); - \\} - \\ - \\fn byPtr(x: *u32) ?*u32 { - \\ return x; - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , ""); - case.addCompareOutput( - \\pub fn main() void { - \\ var x: u32 = undefined; - \\ const maybe_x = byPtr(&x); - \\ assert(maybe_x == null); - \\} - \\ - \\fn byPtr(x: *u32) ?*u32 { - \\ _ = x; - \\ return null; - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , ""); - case.addCompareOutput( - \\pub fn main() void { - \\ var x: u8 = undefined; - \\ const maybe_x = byPtr(&x); - \\ assert(maybe_x != null); - \\ maybe_x.?.* = 255; - \\ assert(x == 255); - \\} - \\ - \\fn byPtr(x: *u8) ?*u8 { - \\ return x; - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , ""); - case.addCompareOutput( - \\pub fn main() void { - \\ var x: i8 = undefined; - \\ const maybe_x = byPtr(&x); - \\ assert(maybe_x != null); - \\ maybe_x.?.* = -1; - \\ assert(x == -1); - \\} - \\ - \\fn byPtr(x: *i8) ?*i8 { - \\ return x; - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , ""); - } - - { - var case = ctx.exe("unwrap error union - simple errors", target); - case.addCompareOutput( - \\pub fn main() void { - \\ maybeErr() catch unreachable; - \\} - \\ - \\fn maybeErr() !void { + , + "Hello, World!\n", + ); + switch (target.getOsTag()) { + .linux => try case.files.append(.{ + .src = + \\pub fn print() void { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (@as(usize, 1)), + \\ [arg1] "{rdi}" (@as(usize, 1)), + \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), + \\ [arg3] "{rdx}" (@as(usize, 14)) + \\ : "rcx", "r11", "memory" + \\ ); \\ return; \\} - , ""); - case.addCompareOutput( - \\pub fn main() void { - \\ maybeErr() catch return; - \\ unreachable; + , + .path = "print.zig", + }), + .macos => try case.files.append(.{ + .src = + \\extern "c" fn write(usize, usize, usize) usize; + \\ + \\pub fn print() void { + \\ _ = write(1, @ptrToInt("Hello, World!\n"), 14); \\} - \\ - \\fn maybeErr() !void { - \\ return error.NoWay; - \\} - , ""); - } - - { - var case = ctx.exe("access slice element by index - slice_elem_val", target); - case.addCompareOutput( - \\var array = [_]usize{ 0, 42, 123, 34 }; - \\var slice: []const usize = &array; - \\ - \\pub fn main() void { - \\ assert(slice[0] == 0); - \\ assert(slice[1] == 42); - \\ assert(slice[2] == 123); - \\ assert(slice[3] == 34); - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , ""); - } - - { - var case = ctx.exe("lower unnamed constants - structs", target); - case.addCompareOutput( - \\const Foo = struct { - \\ a: u8, - \\ b: u32, - \\ - \\ fn first(self: *Foo) u8 { - \\ return self.a; - \\ } - \\ - \\ fn second(self: *Foo) u32 { - \\ return self.b; - \\ } - \\}; - \\ - \\pub fn main() void { - \\ var foo = Foo{ .a = 1, .b = 5 }; - \\ assert(foo.first() == 1); - \\ assert(foo.second() == 5); - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , ""); - - case.addCompareOutput( - \\const Foo = struct { - \\ a: u8, - \\ b: u32, - \\ - \\ fn first(self: *Foo) u8 { - \\ return self.a; - \\ } - \\ - \\ fn second(self: *Foo) u32 { - \\ return self.b; - \\ } - \\}; - \\ - \\pub fn main() void { - \\ var foo = Foo{ .a = 1, .b = 5 }; - \\ assert(foo.first() == 1); - \\ assert(foo.second() == 5); - \\ - \\ foo.a = 10; - \\ foo.b = 255; - \\ - \\ assert(foo.first() == 10); - \\ assert(foo.second() == 255); - \\ - \\ var foo2 = Foo{ .a = 15, .b = 255 }; - \\ assert(foo2.first() == 15); - \\ assert(foo2.second() == 255); - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , ""); - - case.addCompareOutput( - \\const Foo = struct { - \\ a: u8, - \\ b: u32, - \\ - \\ fn first(self: *Foo) u8 { - \\ return self.a; - \\ } - \\ - \\ fn second(self: *Foo) u32 { - \\ return self.b; - \\ } - \\}; - \\ - \\pub fn main() void { - \\ var foo2 = Foo{ .a = 15, .b = 255 }; - \\ assert(foo2.first() == 15); - \\ assert(foo2.second() == 255); - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , ""); + , + .path = "print.zig", + }), + else => unreachable, } } } - -fn addLinuxTestCases(ctx: *TestContext) !void { - // Linux tests - { - var case = ctx.exe("hello world with updates", linux_x64); - - case.addError("", &[_][]const u8{ - ":109:9: error: struct 'tmp.tmp' has no member named 'main'", - }); - - // Incorrect return type - case.addError( - \\pub export fn _start() noreturn { - \\} - , &[_][]const u8{":2:1: error: expected noreturn, found void"}); - - // Regular old hello world - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ print(); - \\ - \\ exit(); - \\} - \\ - \\fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{rdx}" (14) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - \\ - \\fn exit() noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (0) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "Hello, World!\n", - ); - - // Convert to pub fn main - case.addCompareOutput( - \\pub fn main() void { - \\ print(); - \\} - \\ - \\fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{rdx}" (14) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - , - "Hello, World!\n", - ); - - // Now change the message only - case.addCompareOutput( - \\pub fn main() void { - \\ print(); - \\} - \\ - \\fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")), - \\ [arg3] "{rdx}" (104) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - , - "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n", - ); - // Now we print it twice. - case.addCompareOutput( - \\pub fn main() void { - \\ print(); - \\ print(); - \\} - \\ - \\fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")), - \\ [arg3] "{rdx}" (104) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - , - \\What is up? This is a longer message that will force the data to be relocated in virtual address space. - \\What is up? This is a longer message that will force the data to be relocated in virtual address space. - \\ - ); - } - - { - var case = ctx.exe("adding numbers at comptime", linux_x64); - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (1), - \\ [arg1] "{rdi}" (1), - \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{rdx}" (10 + 4) - \\ : "rcx", "r11", "memory" - \\ ); - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (@as(usize, 230) + @as(usize, 1)), - \\ [arg1] "{rdi}" (0) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "Hello, World!\n", - ); - } - - { - var case = ctx.exe("only 1 function and it gets updated", linux_x64); - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (60), // exit - \\ [arg1] "{rdi}" (0) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "", - ); - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), // exit_group - \\ [arg1] "{rdi}" (0) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "", - ); - } - { - var case = ctx.exe("inline assembly", linux_x64); - - case.addError( - \\pub fn main() void { - \\ const number = 1234; - \\ const x = asm volatile ("syscall" - \\ : [o] "{rax}" (-> number) - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (60) - \\ : "rcx", "r11", "memory" - \\ ); - \\ _ = x; - \\} - , &[_][]const u8{":4:27: error: expected type, found comptime_int"}); - case.addError( - \\const S = struct { - \\ comptime { - \\ asm volatile ( - \\ \\zig_moment: - \\ \\syscall - \\ ); - \\ } - \\}; - \\pub fn main() void { - \\ _ = S; - \\} - , &.{":3:13: error: volatile is meaningless on global assembly"}); - case.addError( - \\pub fn main() void { - \\ var bruh: u32 = 1; - \\ asm ("" - \\ : - \\ : [bruh] "{rax}" (4) - \\ : "memory" - \\ ); - \\} - , &.{":3:5: error: assembly expression with no output must be marked volatile"}); - case.addError( - \\pub fn main() void {} - \\comptime { - \\ asm ("" - \\ : - \\ : [bruh] "{rax}" (4) - \\ : "memory" - \\ ); - \\} - , &.{":3:5: error: global assembly cannot have inputs, outputs, or clobbers"}); - } - - { - var case = ctx.exe("issue 10138: callee preserved regs working", linux_x64); - case.addCompareOutput( - \\pub fn main() void { - \\ const fd = open(); - \\ _ = write(fd, "a", 1); - \\ _ = close(fd); - \\} - \\ - \\fn open() usize { - \\ return 42; - \\} - \\ - \\fn write(fd: usize, a: [*]const u8, len: usize) usize { - \\ return syscall4(.WRITE, fd, @ptrToInt(a), len); - \\} - \\ - \\fn syscall4(n: enum { WRITE }, a: usize, b: usize, c: usize) usize { - \\ _ = n; - \\ _ = a; - \\ _ = b; - \\ _ = c; - \\ return 23; - \\} - \\ - \\fn close(fd: usize) usize { - \\ if (fd != 42) - \\ unreachable; - \\ return 0; - \\} - , ""); - } -} - -fn addMacOsTestCases(ctx: *TestContext) !void { - // macOS tests - { - var case = ctx.exe("darwin hello world with updates", macos_x64); - case.addError("", &[_][]const u8{ - ":109:9: error: struct 'tmp.tmp' has no member named 'main'", - }); - - // Incorrect return type - case.addError( - \\pub export fn main() noreturn { - \\} - , &[_][]const u8{ - ":2:1: error: expected noreturn, found void", - }); - - // Regular old hello world - case.addCompareOutput( - \\extern "c" fn write(usize, usize, usize) usize; - \\extern "c" fn exit(usize) noreturn; - \\ - \\pub export fn main() noreturn { - \\ print(); - \\ - \\ exit(0); - \\} - \\ - \\fn print() void { - \\ const msg = @ptrToInt("Hello, World!\n"); - \\ const len = 14; - \\ _ = write(1, msg, len); - \\} - , - "Hello, World!\n", - ); - - // Now using start.zig without an explicit extern exit fn - case.addCompareOutput( - \\extern "c" fn write(usize, usize, usize) usize; - \\ - \\pub fn main() void { - \\ print(); - \\} - \\ - \\fn print() void { - \\ const msg = @ptrToInt("Hello, World!\n"); - \\ const len = 14; - \\ _ = write(1, msg, len); - \\} - , - "Hello, World!\n", - ); - - // Print it 4 times and force growth and realloc. - case.addCompareOutput( - \\extern "c" fn write(usize, usize, usize) usize; - \\ - \\pub fn main() void { - \\ print(); - \\ print(); - \\ print(); - \\ print(); - \\} - \\ - \\fn print() void { - \\ const msg = @ptrToInt("Hello, World!\n"); - \\ const len = 14; - \\ _ = write(1, msg, len); - \\} - , - \\Hello, World! - \\Hello, World! - \\Hello, World! - \\Hello, World! - \\ - ); - - // Print it once, and change the message. - case.addCompareOutput( - \\extern "c" fn write(usize, usize, usize) usize; - \\ - \\pub fn main() void { - \\ print(); - \\} - \\ - \\fn print() void { - \\ const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n"); - \\ const len = 104; - \\ _ = write(1, msg, len); - \\} - , - "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n", - ); - - // Now we print it twice. - case.addCompareOutput( - \\extern "c" fn write(usize, usize, usize) usize; - \\ - \\pub fn main() void { - \\ print(); - \\ print(); - \\} - \\ - \\fn print() void { - \\ const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n"); - \\ const len = 104; - \\ _ = write(1, msg, len); - \\} - , - \\What is up? This is a longer message that will force the data to be relocated in virtual address space. - \\What is up? This is a longer message that will force the data to be relocated in virtual address space. - \\ - ); - } -} From c8a0d8ff2b8557e3e7c3956a37293a0ce06dd3d0 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 27 Apr 2022 17:58:53 +0200 Subject: [PATCH 1280/2031] ci: ignore fmt errors in test/incremental/ --- ci/zinc/linux_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/zinc/linux_test.sh b/ci/zinc/linux_test.sh index b0f65aefd3..ec999a483a 100755 --- a/ci/zinc/linux_test.sh +++ b/ci/zinc/linux_test.sh @@ -45,7 +45,7 @@ cd $WORKSPACE # Look for non-conforming code formatting. # Formatting errors can be fixed by running `zig fmt` on the files printed here. -$ZIG fmt --check . --exclude test/compile_errors/ +$ZIG fmt --check . --exclude test/compile_errors/ --exclude test/incremental/ # Build stage2 standalone so that we can test stage2 against stage2 compiler-rt. $ZIG build -p stage2 -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL" From 8d5acf769336051a18c4905b4fc258349092b36e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 27 Apr 2022 21:29:25 +0200 Subject: [PATCH 1281/2031] test: recursively walk dir with tests Prune incremental tests by moving non-incremental behavior tests to behavior test suite instead. --- src/test.zig | 6 +++--- test/behavior.zig | 2 ++ .../bugs/10138.zig} | 13 ++++++++----- test/behavior/bugs/7187.zig | 18 ++++++++++++++++++ ...s_slice_element_by_index_slice_elem_val.zig | 17 ----------------- ...87_miscompilation_with_bool_return_type.zig | 18 ------------------ .../load_store_via_pointer_deref.0.zig | 16 ---------------- .../load_store_via_pointer_deref.1.zig | 16 ---------------- .../load_store_via_pointer_deref.2.zig | 16 ---------------- ...g_vars_of_different_abi_size_to_stack.0.zig | 16 ---------------- ...g_vars_of_different_abi_size_to_stack.1.zig | 16 ---------------- ...g_vars_of_different_abi_size_to_stack.2.zig | 16 ---------------- .../subtracting_numbers_at_runtime.zig | 10 ---------- .../unwrap_error_union_simple_errors.0.zig | 10 ---------- .../unwrap_error_union_simple_errors.1.zig | 11 ----------- .../hello_world_with_updates.0.zig} | 0 .../hello_world_with_updates.1.zig} | 0 .../hello_world_with_updates.2.zig} | 0 .../hello_world_with_updates.3.zig} | 0 .../hello_world_with_updates.4.zig} | 0 .../hello_world_with_updates.5.zig} | 0 .../inline_assembly.0.zig} | 0 .../inline_assembly.1.zig} | 0 .../inline_assembly.2.zig} | 0 .../inline_assembly.3.zig} | 0 .../only_1_function_and_it_gets_updated.0.zig} | 0 .../only_1_function_and_it_gets_updated.1.zig} | 0 .../hello_world_with_updates.0.zig} | 0 .../hello_world_with_updates.1.zig} | 0 .../hello_world_with_updates.2.zig} | 0 .../hello_world_with_updates.3.zig} | 0 .../hello_world_with_updates.4.zig} | 0 .../hello_world_with_updates.5.zig} | 0 .../hello_world_with_updates.6.zig} | 0 34 files changed, 31 insertions(+), 170 deletions(-) rename test/{incremental/issue_10138_callee_preserved_regs_working_x86_64_linux.zig => behavior/bugs/10138.zig} (54%) create mode 100644 test/behavior/bugs/7187.zig delete mode 100644 test/incremental/access_slice_element_by_index_slice_elem_val.zig delete mode 100644 test/incremental/issue_7187_miscompilation_with_bool_return_type.zig delete mode 100644 test/incremental/load_store_via_pointer_deref.0.zig delete mode 100644 test/incremental/load_store_via_pointer_deref.1.zig delete mode 100644 test/incremental/load_store_via_pointer_deref.2.zig delete mode 100644 test/incremental/saving_vars_of_different_abi_size_to_stack.0.zig delete mode 100644 test/incremental/saving_vars_of_different_abi_size_to_stack.1.zig delete mode 100644 test/incremental/saving_vars_of_different_abi_size_to_stack.2.zig delete mode 100644 test/incremental/subtracting_numbers_at_runtime.zig delete mode 100644 test/incremental/unwrap_error_union_simple_errors.0.zig delete mode 100644 test/incremental/unwrap_error_union_simple_errors.1.zig rename test/incremental/{hello_world_with_updates_x86_64_linux.0.zig => x86_64-linux/hello_world_with_updates.0.zig} (100%) rename test/incremental/{hello_world_with_updates_x86_64_linux.1.zig => x86_64-linux/hello_world_with_updates.1.zig} (100%) rename test/incremental/{hello_world_with_updates_x86_64_linux.2.zig => x86_64-linux/hello_world_with_updates.2.zig} (100%) rename test/incremental/{hello_world_with_updates_x86_64_linux.3.zig => x86_64-linux/hello_world_with_updates.3.zig} (100%) rename test/incremental/{hello_world_with_updates_x86_64_linux.4.zig => x86_64-linux/hello_world_with_updates.4.zig} (100%) rename test/incremental/{hello_world_with_updates_x86_64_linux.5.zig => x86_64-linux/hello_world_with_updates.5.zig} (100%) rename test/incremental/{inline_assembly_x86_64_linux.0.zig => x86_64-linux/inline_assembly.0.zig} (100%) rename test/incremental/{inline_assembly_x86_64_linux.1.zig => x86_64-linux/inline_assembly.1.zig} (100%) rename test/incremental/{inline_assembly_x86_64_linux.2.zig => x86_64-linux/inline_assembly.2.zig} (100%) rename test/incremental/{inline_assembly_x86_64_linux.3.zig => x86_64-linux/inline_assembly.3.zig} (100%) rename test/incremental/{only_1_function_and_it_gets_updated_x86_64_linux.0.zig => x86_64-linux/only_1_function_and_it_gets_updated.0.zig} (100%) rename test/incremental/{only_1_function_and_it_gets_updated_x86_64_linux.1.zig => x86_64-linux/only_1_function_and_it_gets_updated.1.zig} (100%) rename test/incremental/{hello_world_with_updates_x86_64_macos.0.zig => x86_64-macos/hello_world_with_updates.0.zig} (100%) rename test/incremental/{hello_world_with_updates_x86_64_macos.1.zig => x86_64-macos/hello_world_with_updates.1.zig} (100%) rename test/incremental/{hello_world_with_updates_x86_64_macos.2.zig => x86_64-macos/hello_world_with_updates.2.zig} (100%) rename test/incremental/{hello_world_with_updates_x86_64_macos.3.zig => x86_64-macos/hello_world_with_updates.3.zig} (100%) rename test/incremental/{hello_world_with_updates_x86_64_macos.4.zig => x86_64-macos/hello_world_with_updates.4.zig} (100%) rename test/incremental/{hello_world_with_updates_x86_64_macos.5.zig => x86_64-macos/hello_world_with_updates.5.zig} (100%) rename test/incremental/{hello_world_with_updates_x86_64_macos.6.zig => x86_64-macos/hello_world_with_updates.6.zig} (100%) diff --git a/src/test.zig b/src/test.zig index 2f3389d7ff..8ede838a5e 100644 --- a/src/test.zig +++ b/src/test.zig @@ -1012,18 +1012,18 @@ pub const TestContext = struct { ) !void { var cases = std.ArrayList(usize).init(ctx.arena); - var it = dir.iterate(); + var it = try dir.walk(ctx.arena); var filenames = std.ArrayList([]const u8).init(ctx.arena); while (try it.next()) |entry| { if (entry.kind != .File) continue; // Ignore stuff such as .swp files - switch (Compilation.classifyFileExt(entry.name)) { + switch (Compilation.classifyFileExt(entry.basename)) { .unknown => continue, else => {}, } - try filenames.append(try ctx.arena.dupe(u8, entry.name)); + try filenames.append(try ctx.arena.dupe(u8, entry.path)); } // Sort filenames, so that incremental tests are contiguous and in-order diff --git a/test/behavior.zig b/test/behavior.zig index 828a56a81f..4d8b3587bc 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -69,8 +69,10 @@ test { _ = @import("behavior/bugs/7003.zig"); _ = @import("behavior/bugs/7027.zig"); _ = @import("behavior/bugs/7047.zig"); + _ = @import("behavior/bugs/7187.zig"); _ = @import("behavior/bugs/7250.zig"); _ = @import("behavior/bugs/9584.zig"); + _ = @import("behavior/bugs/10138.zig"); _ = @import("behavior/bugs/10147.zig"); _ = @import("behavior/bugs/10970.zig"); _ = @import("behavior/bugs/11046.zig"); diff --git a/test/incremental/issue_10138_callee_preserved_regs_working_x86_64_linux.zig b/test/behavior/bugs/10138.zig similarity index 54% rename from test/incremental/issue_10138_callee_preserved_regs_working_x86_64_linux.zig rename to test/behavior/bugs/10138.zig index 4c045496da..88dc5acde7 100644 --- a/test/incremental/issue_10138_callee_preserved_regs_working_x86_64_linux.zig +++ b/test/behavior/bugs/10138.zig @@ -1,4 +1,11 @@ -pub fn main() void { +const std = @import("std"); +const builtin = @import("builtin"); + +test "registers get overwritten when ignoring return" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.cpu.arch != .x86_64 or builtin.os.tag != .linux) return error.SkipZigTest; + const fd = open(); _ = write(fd, "a", 1); _ = close(fd); @@ -25,7 +32,3 @@ fn close(fd: usize) usize { unreachable; return 0; } - -// run -// target=x86_64-linux -// diff --git a/test/behavior/bugs/7187.zig b/test/behavior/bugs/7187.zig new file mode 100644 index 0000000000..86f5aa6706 --- /dev/null +++ b/test/behavior/bugs/7187.zig @@ -0,0 +1,18 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const expect = std.testing.expect; + +test "miscompilation with bool return type" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + + var x: usize = 1; + var y: bool = getFalse(); + _ = y; + + try expect(x == 1); +} + +fn getFalse() bool { + return false; +} diff --git a/test/incremental/access_slice_element_by_index_slice_elem_val.zig b/test/incremental/access_slice_element_by_index_slice_elem_val.zig deleted file mode 100644 index a0bdae0826..0000000000 --- a/test/incremental/access_slice_element_by_index_slice_elem_val.zig +++ /dev/null @@ -1,17 +0,0 @@ -var array = [_]usize{ 0, 42, 123, 34 }; -var slice: []const usize = &array; - -pub fn main() void { - assert(slice[0] == 0); - assert(slice[1] == 42); - assert(slice[2] == 123); - assert(slice[3] == 34); -} - -fn assert(ok: bool) void { - if (!ok) unreachable; -} - -// run -// target=x86_64-linux,x86_64-macos -// diff --git a/test/incremental/issue_7187_miscompilation_with_bool_return_type.zig b/test/incremental/issue_7187_miscompilation_with_bool_return_type.zig deleted file mode 100644 index 3aed8685bf..0000000000 --- a/test/incremental/issue_7187_miscompilation_with_bool_return_type.zig +++ /dev/null @@ -1,18 +0,0 @@ -pub fn main() void { - var x: usize = 1; - var y: bool = getFalse(); - _ = y; - - assert(x == 1); -} - -fn getFalse() bool { - return false; -} - -fn assert(ok: bool) void { - if (!ok) unreachable; -} - -// run -// diff --git a/test/incremental/load_store_via_pointer_deref.0.zig b/test/incremental/load_store_via_pointer_deref.0.zig deleted file mode 100644 index 96aedf3196..0000000000 --- a/test/incremental/load_store_via_pointer_deref.0.zig +++ /dev/null @@ -1,16 +0,0 @@ -pub fn main() void { - var x: u32 = undefined; - set(&x); - assert(x == 123); -} - -fn set(x: *u32) void { - x.* = 123; -} - -fn assert(ok: bool) void { - if (!ok) unreachable; -} - -// run -// diff --git a/test/incremental/load_store_via_pointer_deref.1.zig b/test/incremental/load_store_via_pointer_deref.1.zig deleted file mode 100644 index e311092744..0000000000 --- a/test/incremental/load_store_via_pointer_deref.1.zig +++ /dev/null @@ -1,16 +0,0 @@ -pub fn main() void { - var x: u16 = undefined; - set(&x); - assert(x == 123); -} - -fn set(x: *u16) void { - x.* = 123; -} - -fn assert(ok: bool) void { - if (!ok) unreachable; -} - -// run -// diff --git a/test/incremental/load_store_via_pointer_deref.2.zig b/test/incremental/load_store_via_pointer_deref.2.zig deleted file mode 100644 index 3b1484faa1..0000000000 --- a/test/incremental/load_store_via_pointer_deref.2.zig +++ /dev/null @@ -1,16 +0,0 @@ -pub fn main() void { - var x: u8 = undefined; - set(&x); - assert(x == 123); -} - -fn set(x: *u8) void { - x.* = 123; -} - -fn assert(ok: bool) void { - if (!ok) unreachable; -} - -// run -// diff --git a/test/incremental/saving_vars_of_different_abi_size_to_stack.0.zig b/test/incremental/saving_vars_of_different_abi_size_to_stack.0.zig deleted file mode 100644 index 6134622a0e..0000000000 --- a/test/incremental/saving_vars_of_different_abi_size_to_stack.0.zig +++ /dev/null @@ -1,16 +0,0 @@ -pub fn main() void { - assert(callMe(2) == 24); -} - -fn callMe(a: u8) u8 { - var b: u8 = a + 10; - const c = 2 * b; - return c; -} - -pub fn assert(ok: bool) void { - if (!ok) unreachable; // assertion failure -} - -// run -// diff --git a/test/incremental/saving_vars_of_different_abi_size_to_stack.1.zig b/test/incremental/saving_vars_of_different_abi_size_to_stack.1.zig deleted file mode 100644 index 4d87d7b9c5..0000000000 --- a/test/incremental/saving_vars_of_different_abi_size_to_stack.1.zig +++ /dev/null @@ -1,16 +0,0 @@ -pub fn main() void { - assert(callMe(2) == 24); -} - -fn callMe(a: u16) u16 { - var b: u16 = a + 10; - const c = 2 * b; - return c; -} - -pub fn assert(ok: bool) void { - if (!ok) unreachable; // assertion failure -} - -// run -// diff --git a/test/incremental/saving_vars_of_different_abi_size_to_stack.2.zig b/test/incremental/saving_vars_of_different_abi_size_to_stack.2.zig deleted file mode 100644 index b665227962..0000000000 --- a/test/incremental/saving_vars_of_different_abi_size_to_stack.2.zig +++ /dev/null @@ -1,16 +0,0 @@ -pub fn main() void { - assert(callMe(2) == 24); -} - -fn callMe(a: u32) u32 { - var b: u32 = a + 10; - const c = 2 * b; - return c; -} - -pub fn assert(ok: bool) void { - if (!ok) unreachable; // assertion failure -} - -// run -// diff --git a/test/incremental/subtracting_numbers_at_runtime.zig b/test/incremental/subtracting_numbers_at_runtime.zig deleted file mode 100644 index 5ea81a34b8..0000000000 --- a/test/incremental/subtracting_numbers_at_runtime.zig +++ /dev/null @@ -1,10 +0,0 @@ -pub fn main() void { - sub(7, 4); -} - -fn sub(a: u32, b: u32) void { - if (a - b != 3) unreachable; -} - -// run -// diff --git a/test/incremental/unwrap_error_union_simple_errors.0.zig b/test/incremental/unwrap_error_union_simple_errors.0.zig deleted file mode 100644 index a1e2da9340..0000000000 --- a/test/incremental/unwrap_error_union_simple_errors.0.zig +++ /dev/null @@ -1,10 +0,0 @@ -pub fn main() void { - maybeErr() catch unreachable; -} - -fn maybeErr() !void { - return; -} - -// run -// diff --git a/test/incremental/unwrap_error_union_simple_errors.1.zig b/test/incremental/unwrap_error_union_simple_errors.1.zig deleted file mode 100644 index 830ee629bc..0000000000 --- a/test/incremental/unwrap_error_union_simple_errors.1.zig +++ /dev/null @@ -1,11 +0,0 @@ -pub fn main() void { - maybeErr() catch return; - unreachable; -} - -fn maybeErr() !void { - return error.NoWay; -} - -// run -// diff --git a/test/incremental/hello_world_with_updates_x86_64_linux.0.zig b/test/incremental/x86_64-linux/hello_world_with_updates.0.zig similarity index 100% rename from test/incremental/hello_world_with_updates_x86_64_linux.0.zig rename to test/incremental/x86_64-linux/hello_world_with_updates.0.zig diff --git a/test/incremental/hello_world_with_updates_x86_64_linux.1.zig b/test/incremental/x86_64-linux/hello_world_with_updates.1.zig similarity index 100% rename from test/incremental/hello_world_with_updates_x86_64_linux.1.zig rename to test/incremental/x86_64-linux/hello_world_with_updates.1.zig diff --git a/test/incremental/hello_world_with_updates_x86_64_linux.2.zig b/test/incremental/x86_64-linux/hello_world_with_updates.2.zig similarity index 100% rename from test/incremental/hello_world_with_updates_x86_64_linux.2.zig rename to test/incremental/x86_64-linux/hello_world_with_updates.2.zig diff --git a/test/incremental/hello_world_with_updates_x86_64_linux.3.zig b/test/incremental/x86_64-linux/hello_world_with_updates.3.zig similarity index 100% rename from test/incremental/hello_world_with_updates_x86_64_linux.3.zig rename to test/incremental/x86_64-linux/hello_world_with_updates.3.zig diff --git a/test/incremental/hello_world_with_updates_x86_64_linux.4.zig b/test/incremental/x86_64-linux/hello_world_with_updates.4.zig similarity index 100% rename from test/incremental/hello_world_with_updates_x86_64_linux.4.zig rename to test/incremental/x86_64-linux/hello_world_with_updates.4.zig diff --git a/test/incremental/hello_world_with_updates_x86_64_linux.5.zig b/test/incremental/x86_64-linux/hello_world_with_updates.5.zig similarity index 100% rename from test/incremental/hello_world_with_updates_x86_64_linux.5.zig rename to test/incremental/x86_64-linux/hello_world_with_updates.5.zig diff --git a/test/incremental/inline_assembly_x86_64_linux.0.zig b/test/incremental/x86_64-linux/inline_assembly.0.zig similarity index 100% rename from test/incremental/inline_assembly_x86_64_linux.0.zig rename to test/incremental/x86_64-linux/inline_assembly.0.zig diff --git a/test/incremental/inline_assembly_x86_64_linux.1.zig b/test/incremental/x86_64-linux/inline_assembly.1.zig similarity index 100% rename from test/incremental/inline_assembly_x86_64_linux.1.zig rename to test/incremental/x86_64-linux/inline_assembly.1.zig diff --git a/test/incremental/inline_assembly_x86_64_linux.2.zig b/test/incremental/x86_64-linux/inline_assembly.2.zig similarity index 100% rename from test/incremental/inline_assembly_x86_64_linux.2.zig rename to test/incremental/x86_64-linux/inline_assembly.2.zig diff --git a/test/incremental/inline_assembly_x86_64_linux.3.zig b/test/incremental/x86_64-linux/inline_assembly.3.zig similarity index 100% rename from test/incremental/inline_assembly_x86_64_linux.3.zig rename to test/incremental/x86_64-linux/inline_assembly.3.zig diff --git a/test/incremental/only_1_function_and_it_gets_updated_x86_64_linux.0.zig b/test/incremental/x86_64-linux/only_1_function_and_it_gets_updated.0.zig similarity index 100% rename from test/incremental/only_1_function_and_it_gets_updated_x86_64_linux.0.zig rename to test/incremental/x86_64-linux/only_1_function_and_it_gets_updated.0.zig diff --git a/test/incremental/only_1_function_and_it_gets_updated_x86_64_linux.1.zig b/test/incremental/x86_64-linux/only_1_function_and_it_gets_updated.1.zig similarity index 100% rename from test/incremental/only_1_function_and_it_gets_updated_x86_64_linux.1.zig rename to test/incremental/x86_64-linux/only_1_function_and_it_gets_updated.1.zig diff --git a/test/incremental/hello_world_with_updates_x86_64_macos.0.zig b/test/incremental/x86_64-macos/hello_world_with_updates.0.zig similarity index 100% rename from test/incremental/hello_world_with_updates_x86_64_macos.0.zig rename to test/incremental/x86_64-macos/hello_world_with_updates.0.zig diff --git a/test/incremental/hello_world_with_updates_x86_64_macos.1.zig b/test/incremental/x86_64-macos/hello_world_with_updates.1.zig similarity index 100% rename from test/incremental/hello_world_with_updates_x86_64_macos.1.zig rename to test/incremental/x86_64-macos/hello_world_with_updates.1.zig diff --git a/test/incremental/hello_world_with_updates_x86_64_macos.2.zig b/test/incremental/x86_64-macos/hello_world_with_updates.2.zig similarity index 100% rename from test/incremental/hello_world_with_updates_x86_64_macos.2.zig rename to test/incremental/x86_64-macos/hello_world_with_updates.2.zig diff --git a/test/incremental/hello_world_with_updates_x86_64_macos.3.zig b/test/incremental/x86_64-macos/hello_world_with_updates.3.zig similarity index 100% rename from test/incremental/hello_world_with_updates_x86_64_macos.3.zig rename to test/incremental/x86_64-macos/hello_world_with_updates.3.zig diff --git a/test/incremental/hello_world_with_updates_x86_64_macos.4.zig b/test/incremental/x86_64-macos/hello_world_with_updates.4.zig similarity index 100% rename from test/incremental/hello_world_with_updates_x86_64_macos.4.zig rename to test/incremental/x86_64-macos/hello_world_with_updates.4.zig diff --git a/test/incremental/hello_world_with_updates_x86_64_macos.5.zig b/test/incremental/x86_64-macos/hello_world_with_updates.5.zig similarity index 100% rename from test/incremental/hello_world_with_updates_x86_64_macos.5.zig rename to test/incremental/x86_64-macos/hello_world_with_updates.5.zig diff --git a/test/incremental/hello_world_with_updates_x86_64_macos.6.zig b/test/incremental/x86_64-macos/hello_world_with_updates.6.zig similarity index 100% rename from test/incremental/hello_world_with_updates_x86_64_macos.6.zig rename to test/incremental/x86_64-macos/hello_world_with_updates.6.zig From 815dc2c6e77eb6035921bb59fee0844b095ee064 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 27 Apr 2022 21:33:59 +0200 Subject: [PATCH 1282/2031] test: enable wasm32-wasi incremental tests --- src/test.zig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/test.zig b/src/test.zig index 8ede838a5e..1bb3aaa4a1 100644 --- a/src/test.zig +++ b/src/test.zig @@ -165,6 +165,12 @@ const ErrorMsg = union(enum) { } }; +/// Default config values for known test manifest key-value pairings. +/// Currently handled defaults are: +/// * backend +/// * target +/// * output_mode +/// * is_test const TestManifestConfigDefaults = struct { /// Asserts if the key doesn't exist - yep, it's an oversight alright. fn get(@"type": TestManifest.Type, key: []const u8) []const u8 { @@ -186,7 +192,7 @@ const TestManifestConfigDefaults = struct { } // Wasm defaults = defaults ++ "wasm32-wasi"; - return defaults[0 .. defaults.len - 2]; + return defaults; } } else if (std.mem.eql(u8, key, "output_mode")) { return switch (@"type") { From 8e05e6a1ed3089ce1161cca9980f9939440a421b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 27 Apr 2022 22:59:55 +0200 Subject: [PATCH 1283/2031] test: migrate wasm incremental tests --- test/cases.zig | 1 - test/incremental/binary_operands.0.zig | 9 + test/incremental/binary_operands.1.zig | 8 + test/incremental/binary_operands.10.zig | 13 + test/incremental/binary_operands.11.zig | 9 + test/incremental/binary_operands.12.zig | 8 + test/incremental/binary_operands.13.zig | 8 + test/incremental/binary_operands.14.zig | 13 + test/incremental/binary_operands.15.zig | 8 + test/incremental/binary_operands.16.zig | 8 + test/incremental/binary_operands.17.zig | 8 + test/incremental/binary_operands.18.zig | 9 + test/incremental/binary_operands.19.zig | 9 + test/incremental/binary_operands.2.zig | 8 + test/incremental/binary_operands.20.zig | 9 + test/incremental/binary_operands.21.zig | 9 + test/incremental/binary_operands.22.zig | 9 + test/incremental/binary_operands.23.zig | 9 + test/incremental/binary_operands.24.zig | 9 + test/incremental/binary_operands.25.zig | 9 + test/incremental/binary_operands.3.zig | 7 + test/incremental/binary_operands.4.zig | 12 + test/incremental/binary_operands.5.zig | 8 + test/incremental/binary_operands.6.zig | 8 + test/incremental/binary_operands.7.zig | 8 + test/incremental/binary_operands.8.zig | 7 + test/incremental/binary_operands.9.zig | 12 + test/incremental/enum_values.0.zig | 18 + test/incremental/enum_values.1.zig | 22 + test/incremental/function_calls.0.zig | 12 + test/incremental/function_calls.1.zig | 15 + test/incremental/function_calls.2.zig | 14 + test/incremental/function_calls.3.zig | 10 + test/incremental/wasm-wasi/conditions.0.zig | 11 + test/incremental/wasm-wasi/conditions.1.zig | 12 + test/incremental/wasm-wasi/conditions.2.zig | 12 + test/incremental/wasm-wasi/conditions.3.zig | 16 + test/incremental/wasm-wasi/conditions.4.zig | 15 + test/incremental/wasm-wasi/conditions.5.zig | 20 + test/incremental/wasm-wasi/error_unions.0.zig | 15 + test/incremental/wasm-wasi/error_unions.1.zig | 8 + test/incremental/wasm-wasi/error_unions.2.zig | 8 + test/incremental/wasm-wasi/error_unions.3.zig | 12 + test/incremental/wasm-wasi/error_unions.4.zig | 12 + test/incremental/wasm-wasi/error_unions.5.zig | 12 + test/incremental/wasm-wasi/locals.0.zig | 14 + test/incremental/wasm-wasi/locals.1.zig | 17 + test/incremental/wasm-wasi/optionals.0.zig | 12 + test/incremental/wasm-wasi/optionals.1.zig | 11 + test/incremental/wasm-wasi/optionals.2.zig | 7 + test/incremental/wasm-wasi/optionals.3.zig | 8 + test/incremental/wasm-wasi/optionals.4.zig | 13 + test/incremental/wasm-wasi/pointers.0.zig | 14 + test/incremental/wasm-wasi/pointers.1.zig | 18 + test/incremental/wasm-wasi/structs.0.zig | 10 + test/incremental/wasm-wasi/structs.1.zig | 10 + test/incremental/wasm-wasi/structs.2.zig | 9 + test/incremental/wasm-wasi/structs.3.zig | 12 + test/incremental/wasm-wasi/structs.4.zig | 11 + test/incremental/wasm-wasi/switch.0.zig | 15 + test/incremental/wasm-wasi/switch.1.zig | 14 + test/incremental/wasm-wasi/switch.2.zig | 14 + test/incremental/wasm-wasi/switch.3.zig | 15 + test/incremental/wasm-wasi/while_loops.0.zig | 12 + test/incremental/wasm-wasi/while_loops.1.zig | 11 + test/incremental/wasm-wasi/while_loops.2.zig | 12 + test/stage2/wasm.zig | 796 ------------------ 67 files changed, 737 insertions(+), 797 deletions(-) create mode 100644 test/incremental/binary_operands.0.zig create mode 100644 test/incremental/binary_operands.1.zig create mode 100644 test/incremental/binary_operands.10.zig create mode 100644 test/incremental/binary_operands.11.zig create mode 100644 test/incremental/binary_operands.12.zig create mode 100644 test/incremental/binary_operands.13.zig create mode 100644 test/incremental/binary_operands.14.zig create mode 100644 test/incremental/binary_operands.15.zig create mode 100644 test/incremental/binary_operands.16.zig create mode 100644 test/incremental/binary_operands.17.zig create mode 100644 test/incremental/binary_operands.18.zig create mode 100644 test/incremental/binary_operands.19.zig create mode 100644 test/incremental/binary_operands.2.zig create mode 100644 test/incremental/binary_operands.20.zig create mode 100644 test/incremental/binary_operands.21.zig create mode 100644 test/incremental/binary_operands.22.zig create mode 100644 test/incremental/binary_operands.23.zig create mode 100644 test/incremental/binary_operands.24.zig create mode 100644 test/incremental/binary_operands.25.zig create mode 100644 test/incremental/binary_operands.3.zig create mode 100644 test/incremental/binary_operands.4.zig create mode 100644 test/incremental/binary_operands.5.zig create mode 100644 test/incremental/binary_operands.6.zig create mode 100644 test/incremental/binary_operands.7.zig create mode 100644 test/incremental/binary_operands.8.zig create mode 100644 test/incremental/binary_operands.9.zig create mode 100644 test/incremental/enum_values.0.zig create mode 100644 test/incremental/enum_values.1.zig create mode 100644 test/incremental/function_calls.0.zig create mode 100644 test/incremental/function_calls.1.zig create mode 100644 test/incremental/function_calls.2.zig create mode 100644 test/incremental/function_calls.3.zig create mode 100644 test/incremental/wasm-wasi/conditions.0.zig create mode 100644 test/incremental/wasm-wasi/conditions.1.zig create mode 100644 test/incremental/wasm-wasi/conditions.2.zig create mode 100644 test/incremental/wasm-wasi/conditions.3.zig create mode 100644 test/incremental/wasm-wasi/conditions.4.zig create mode 100644 test/incremental/wasm-wasi/conditions.5.zig create mode 100644 test/incremental/wasm-wasi/error_unions.0.zig create mode 100644 test/incremental/wasm-wasi/error_unions.1.zig create mode 100644 test/incremental/wasm-wasi/error_unions.2.zig create mode 100644 test/incremental/wasm-wasi/error_unions.3.zig create mode 100644 test/incremental/wasm-wasi/error_unions.4.zig create mode 100644 test/incremental/wasm-wasi/error_unions.5.zig create mode 100644 test/incremental/wasm-wasi/locals.0.zig create mode 100644 test/incremental/wasm-wasi/locals.1.zig create mode 100644 test/incremental/wasm-wasi/optionals.0.zig create mode 100644 test/incremental/wasm-wasi/optionals.1.zig create mode 100644 test/incremental/wasm-wasi/optionals.2.zig create mode 100644 test/incremental/wasm-wasi/optionals.3.zig create mode 100644 test/incremental/wasm-wasi/optionals.4.zig create mode 100644 test/incremental/wasm-wasi/pointers.0.zig create mode 100644 test/incremental/wasm-wasi/pointers.1.zig create mode 100644 test/incremental/wasm-wasi/structs.0.zig create mode 100644 test/incremental/wasm-wasi/structs.1.zig create mode 100644 test/incremental/wasm-wasi/structs.2.zig create mode 100644 test/incremental/wasm-wasi/structs.3.zig create mode 100644 test/incremental/wasm-wasi/structs.4.zig create mode 100644 test/incremental/wasm-wasi/switch.0.zig create mode 100644 test/incremental/wasm-wasi/switch.1.zig create mode 100644 test/incremental/wasm-wasi/switch.2.zig create mode 100644 test/incremental/wasm-wasi/switch.3.zig create mode 100644 test/incremental/wasm-wasi/while_loops.0.zig create mode 100644 test/incremental/wasm-wasi/while_loops.1.zig create mode 100644 test/incremental/wasm-wasi/while_loops.2.zig delete mode 100644 test/stage2/wasm.zig diff --git a/test/cases.zig b/test/cases.zig index 0fb6f381dd..cc77104fb9 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -12,7 +12,6 @@ pub fn addCases(ctx: *TestContext) !void { try @import("stage2/arm.zig").addCases(ctx); try @import("stage2/aarch64.zig").addCases(ctx); try @import("stage2/llvm.zig").addCases(ctx); - try @import("stage2/wasm.zig").addCases(ctx); try @import("stage2/riscv64.zig").addCases(ctx); try @import("stage2/plan9.zig").addCases(ctx); try @import("stage2/x86_64.zig").addCases(ctx); diff --git a/test/incremental/binary_operands.0.zig b/test/incremental/binary_operands.0.zig new file mode 100644 index 0000000000..e3688232aa --- /dev/null +++ b/test/incremental/binary_operands.0.zig @@ -0,0 +1,9 @@ +pub fn main() void { + var i: u8 = 5; + i += 20; + if (i != 25) unreachable; +} + +// run +// target=wasm32-wasi +// diff --git a/test/incremental/binary_operands.1.zig b/test/incremental/binary_operands.1.zig new file mode 100644 index 0000000000..fdee492d22 --- /dev/null +++ b/test/incremental/binary_operands.1.zig @@ -0,0 +1,8 @@ +pub fn main() void { + var i: i32 = 2147483647; + if (i +% 1 != -2147483648) unreachable; + return; +} + +// run +// diff --git a/test/incremental/binary_operands.10.zig b/test/incremental/binary_operands.10.zig new file mode 100644 index 0000000000..63e045c258 --- /dev/null +++ b/test/incremental/binary_operands.10.zig @@ -0,0 +1,13 @@ +pub fn main() void { + var i: u32 = 5; + i *= 7; + var result: u32 = foo(i, 10); + if (result != 350) unreachable; + return; +} +fn foo(x: u32, y: u32) u32 { + return x * y; +} + +// run +// diff --git a/test/incremental/binary_operands.11.zig b/test/incremental/binary_operands.11.zig new file mode 100644 index 0000000000..f461bcc0af --- /dev/null +++ b/test/incremental/binary_operands.11.zig @@ -0,0 +1,9 @@ +pub fn main() void { + var i: i32 = 2147483647; + const result = i *% 2; + if (result != -2) unreachable; + return; +} + +// run +// diff --git a/test/incremental/binary_operands.12.zig b/test/incremental/binary_operands.12.zig new file mode 100644 index 0000000000..0ffb5e2230 --- /dev/null +++ b/test/incremental/binary_operands.12.zig @@ -0,0 +1,8 @@ +pub fn main() void { + var i: u3 = 3; + if (i *% 3 != 1) unreachable; + return; +} + +// run +// diff --git a/test/incremental/binary_operands.13.zig b/test/incremental/binary_operands.13.zig new file mode 100644 index 0000000000..e62107b47e --- /dev/null +++ b/test/incremental/binary_operands.13.zig @@ -0,0 +1,8 @@ +pub fn main() void { + var i: i4 = 3; + if (i *% 3 != 1) unreachable; + return; +} + +// run +// diff --git a/test/incremental/binary_operands.14.zig b/test/incremental/binary_operands.14.zig new file mode 100644 index 0000000000..fcc7245ad9 --- /dev/null +++ b/test/incremental/binary_operands.14.zig @@ -0,0 +1,13 @@ +pub fn main() void { + var i: u32 = 352; + i /= 7; // i = 50 + var result: u32 = foo(i, 7); + if (result != 7) unreachable; + return; +} +fn foo(x: u32, y: u32) u32 { + return x / y; +} + +// run +// diff --git a/test/incremental/binary_operands.15.zig b/test/incremental/binary_operands.15.zig new file mode 100644 index 0000000000..c709e5dfd2 --- /dev/null +++ b/test/incremental/binary_operands.15.zig @@ -0,0 +1,8 @@ +pub fn main() u8 { + var i: u8 = 5; + i &= 6; + return i - 4; +} + +// run +// diff --git a/test/incremental/binary_operands.16.zig b/test/incremental/binary_operands.16.zig new file mode 100644 index 0000000000..fe1813b877 --- /dev/null +++ b/test/incremental/binary_operands.16.zig @@ -0,0 +1,8 @@ +pub fn main() u8 { + var i: u8 = 5; + i |= 6; + return i - 7; +} + +// run +// diff --git a/test/incremental/binary_operands.17.zig b/test/incremental/binary_operands.17.zig new file mode 100644 index 0000000000..439d3d6d6b --- /dev/null +++ b/test/incremental/binary_operands.17.zig @@ -0,0 +1,8 @@ +pub fn main() u8 { + var i: u8 = 5; + i ^= 6; + return i - 3; +} + +// run +// diff --git a/test/incremental/binary_operands.18.zig b/test/incremental/binary_operands.18.zig new file mode 100644 index 0000000000..c21aa586c5 --- /dev/null +++ b/test/incremental/binary_operands.18.zig @@ -0,0 +1,9 @@ +pub fn main() void { + var b: bool = false; + b = b or false; + if (b) unreachable; + return; +} + +// run +// diff --git a/test/incremental/binary_operands.19.zig b/test/incremental/binary_operands.19.zig new file mode 100644 index 0000000000..e95470391c --- /dev/null +++ b/test/incremental/binary_operands.19.zig @@ -0,0 +1,9 @@ +pub fn main() void { + var b: bool = true; + b = b or false; + if (!b) unreachable; + return; +} + +// run +// diff --git a/test/incremental/binary_operands.2.zig b/test/incremental/binary_operands.2.zig new file mode 100644 index 0000000000..7407ed7d95 --- /dev/null +++ b/test/incremental/binary_operands.2.zig @@ -0,0 +1,8 @@ +pub fn main() void { + var i: i4 = 7; + if (i +% 1 != 0) unreachable; + return; +} + +// run +// diff --git a/test/incremental/binary_operands.20.zig b/test/incremental/binary_operands.20.zig new file mode 100644 index 0000000000..9238bf97c0 --- /dev/null +++ b/test/incremental/binary_operands.20.zig @@ -0,0 +1,9 @@ +pub fn main() void { + var b: bool = false; + b = b or true; + if (!b) unreachable; + return; +} + +// run +// diff --git a/test/incremental/binary_operands.21.zig b/test/incremental/binary_operands.21.zig new file mode 100644 index 0000000000..a2d427bda9 --- /dev/null +++ b/test/incremental/binary_operands.21.zig @@ -0,0 +1,9 @@ +pub fn main() void { + var b: bool = true; + b = b or true; + if (!b) unreachable; + return; +} + +// run +// diff --git a/test/incremental/binary_operands.22.zig b/test/incremental/binary_operands.22.zig new file mode 100644 index 0000000000..aa8439a361 --- /dev/null +++ b/test/incremental/binary_operands.22.zig @@ -0,0 +1,9 @@ +pub fn main() void { + var b: bool = false; + b = b and false; + if (b) unreachable; + return; +} + +// run +// diff --git a/test/incremental/binary_operands.23.zig b/test/incremental/binary_operands.23.zig new file mode 100644 index 0000000000..8596ea7709 --- /dev/null +++ b/test/incremental/binary_operands.23.zig @@ -0,0 +1,9 @@ +pub fn main() void { + var b: bool = true; + b = b and false; + if (b) unreachable; + return; +} + +// run +// diff --git a/test/incremental/binary_operands.24.zig b/test/incremental/binary_operands.24.zig new file mode 100644 index 0000000000..08dc4430a2 --- /dev/null +++ b/test/incremental/binary_operands.24.zig @@ -0,0 +1,9 @@ +pub fn main() void { + var b: bool = false; + b = b and true; + if (b) unreachable; + return; +} + +// run +// diff --git a/test/incremental/binary_operands.25.zig b/test/incremental/binary_operands.25.zig new file mode 100644 index 0000000000..8aa0acfdb5 --- /dev/null +++ b/test/incremental/binary_operands.25.zig @@ -0,0 +1,9 @@ +pub fn main() void { + var b: bool = true; + b = b and true; + if (!b) unreachable; + return; +} + +// run +// diff --git a/test/incremental/binary_operands.3.zig b/test/incremental/binary_operands.3.zig new file mode 100644 index 0000000000..0297f1e7a4 --- /dev/null +++ b/test/incremental/binary_operands.3.zig @@ -0,0 +1,7 @@ +pub fn main() u8 { + var i: u8 = 255; + return i +% 1; +} + +// run +// diff --git a/test/incremental/binary_operands.4.zig b/test/incremental/binary_operands.4.zig new file mode 100644 index 0000000000..f0f8910e14 --- /dev/null +++ b/test/incremental/binary_operands.4.zig @@ -0,0 +1,12 @@ +pub fn main() u8 { + var i: u8 = 5; + i += 20; + var result: u8 = foo(i, 10); + return result - 35; +} +fn foo(x: u8, y: u8) u8 { + return x + y; +} + +// run +// diff --git a/test/incremental/binary_operands.5.zig b/test/incremental/binary_operands.5.zig new file mode 100644 index 0000000000..51ab437475 --- /dev/null +++ b/test/incremental/binary_operands.5.zig @@ -0,0 +1,8 @@ +pub fn main() u8 { + var i: u8 = 20; + i -= 5; + return i - 15; +} + +// run +// diff --git a/test/incremental/binary_operands.6.zig b/test/incremental/binary_operands.6.zig new file mode 100644 index 0000000000..5c9c5be68a --- /dev/null +++ b/test/incremental/binary_operands.6.zig @@ -0,0 +1,8 @@ +pub fn main() void { + var i: i32 = -2147483648; + if (i -% 1 != 2147483647) unreachable; + return; +} + +// run +// diff --git a/test/incremental/binary_operands.7.zig b/test/incremental/binary_operands.7.zig new file mode 100644 index 0000000000..634a47fb0c --- /dev/null +++ b/test/incremental/binary_operands.7.zig @@ -0,0 +1,8 @@ +pub fn main() void { + var i: i7 = -64; + if (i -% 1 != 63) unreachable; + return; +} + +// run +// diff --git a/test/incremental/binary_operands.8.zig b/test/incremental/binary_operands.8.zig new file mode 100644 index 0000000000..a20f664694 --- /dev/null +++ b/test/incremental/binary_operands.8.zig @@ -0,0 +1,7 @@ +pub fn main() void { + var i: u4 = 0; + if (i -% 1 != 15) unreachable; +} + +// run +// diff --git a/test/incremental/binary_operands.9.zig b/test/incremental/binary_operands.9.zig new file mode 100644 index 0000000000..e6c4f5d52b --- /dev/null +++ b/test/incremental/binary_operands.9.zig @@ -0,0 +1,12 @@ +pub fn main() u8 { + var i: u8 = 5; + i -= 3; + var result: u8 = foo(i, 10); + return result - 8; +} +fn foo(x: u8, y: u8) u8 { + return y - x; +} + +// run +// diff --git a/test/incremental/enum_values.0.zig b/test/incremental/enum_values.0.zig new file mode 100644 index 0000000000..d96841dd8c --- /dev/null +++ b/test/incremental/enum_values.0.zig @@ -0,0 +1,18 @@ +const Number = enum { One, Two, Three }; + +pub fn main() void { + var number1 = Number.One; + var number2: Number = .Two; + if (false) { + number1; + number2; + } + const number3 = @intToEnum(Number, 2); + if (@enumToInt(number3) != 2) { + unreachable; + } + return; +} + +// run +// diff --git a/test/incremental/enum_values.1.zig b/test/incremental/enum_values.1.zig new file mode 100644 index 0000000000..47b9e21a27 --- /dev/null +++ b/test/incremental/enum_values.1.zig @@ -0,0 +1,22 @@ +const Number = enum { One, Two, Three }; + +pub fn main() void { + var number1 = Number.One; + var number2: Number = .Two; + const number3 = @intToEnum(Number, 2); + assert(number1 != number2); + assert(number2 != number3); + assert(@enumToInt(number1) == 0); + assert(@enumToInt(number2) == 1); + assert(@enumToInt(number3) == 2); + var x: Number = .Two; + assert(number2 == x); + + return; +} +fn assert(val: bool) void { + if (!val) unreachable; +} + +// run +// diff --git a/test/incremental/function_calls.0.zig b/test/incremental/function_calls.0.zig new file mode 100644 index 0000000000..eb388bc49f --- /dev/null +++ b/test/incremental/function_calls.0.zig @@ -0,0 +1,12 @@ +pub fn main() void { + foo(); + bar(); +} +fn foo() void { + bar(); + bar(); +} +fn bar() void {} + +// run +// diff --git a/test/incremental/function_calls.1.zig b/test/incremental/function_calls.1.zig new file mode 100644 index 0000000000..60bc061bca --- /dev/null +++ b/test/incremental/function_calls.1.zig @@ -0,0 +1,15 @@ +pub fn main() void { + bar(); + foo(); + foo(); + bar(); + foo(); + bar(); +} +fn foo() void { + bar(); +} +fn bar() void {} + +// run +// diff --git a/test/incremental/function_calls.2.zig b/test/incremental/function_calls.2.zig new file mode 100644 index 0000000000..ce5627378a --- /dev/null +++ b/test/incremental/function_calls.2.zig @@ -0,0 +1,14 @@ +pub fn main() void { + bar(); + foo(); + return; +} +fn foo() void { + bar(); + bar(); + bar(); +} +fn bar() void {} + +// run +// diff --git a/test/incremental/function_calls.3.zig b/test/incremental/function_calls.3.zig new file mode 100644 index 0000000000..0316d9042d --- /dev/null +++ b/test/incremental/function_calls.3.zig @@ -0,0 +1,10 @@ +pub fn main() void { + foo(10, 20); +} +fn foo(x: u8, y: u8) void { + _ = x; + _ = y; +} + +// run +// diff --git a/test/incremental/wasm-wasi/conditions.0.zig b/test/incremental/wasm-wasi/conditions.0.zig new file mode 100644 index 0000000000..1167ea85a4 --- /dev/null +++ b/test/incremental/wasm-wasi/conditions.0.zig @@ -0,0 +1,11 @@ +pub fn main() u8 { + var i: u8 = 5; + if (i > @as(u8, 4)) { + i += 10; + } + return i - 15; +} + +// run +// target=wasm32-wasi +// diff --git a/test/incremental/wasm-wasi/conditions.1.zig b/test/incremental/wasm-wasi/conditions.1.zig new file mode 100644 index 0000000000..f9f03808b9 --- /dev/null +++ b/test/incremental/wasm-wasi/conditions.1.zig @@ -0,0 +1,12 @@ +pub fn main() u8 { + var i: u8 = 5; + if (i < @as(u8, 4)) { + i += 10; + } else { + i = 2; + } + return i - 2; +} + +// run +// diff --git a/test/incremental/wasm-wasi/conditions.2.zig b/test/incremental/wasm-wasi/conditions.2.zig new file mode 100644 index 0000000000..a80ead9ea5 --- /dev/null +++ b/test/incremental/wasm-wasi/conditions.2.zig @@ -0,0 +1,12 @@ +pub fn main() u8 { + var i: u8 = 5; + if (i < @as(u8, 4)) { + i += 10; + } else if (i == @as(u8, 5)) { + i = 20; + } + return i - 20; +} + +// run +// diff --git a/test/incremental/wasm-wasi/conditions.3.zig b/test/incremental/wasm-wasi/conditions.3.zig new file mode 100644 index 0000000000..bd016a47f3 --- /dev/null +++ b/test/incremental/wasm-wasi/conditions.3.zig @@ -0,0 +1,16 @@ +pub fn main() u8 { + var i: u8 = 11; + if (i < @as(u8, 4)) { + i += 10; + } else { + if (i > @as(u8, 10)) { + i += 20; + } else { + i = 20; + } + } + return i - 31; +} + +// run +// diff --git a/test/incremental/wasm-wasi/conditions.4.zig b/test/incremental/wasm-wasi/conditions.4.zig new file mode 100644 index 0000000000..e1bd9ce0f7 --- /dev/null +++ b/test/incremental/wasm-wasi/conditions.4.zig @@ -0,0 +1,15 @@ +pub fn main() void { + assert(foo(true) != @as(i32, 30)); +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +fn foo(ok: bool) i32 { + const x = if (ok) @as(i32, 20) else @as(i32, 10); + return x; +} + +// run +// diff --git a/test/incremental/wasm-wasi/conditions.5.zig b/test/incremental/wasm-wasi/conditions.5.zig new file mode 100644 index 0000000000..2d5ba338c7 --- /dev/null +++ b/test/incremental/wasm-wasi/conditions.5.zig @@ -0,0 +1,20 @@ +pub fn main() void { + assert(foo(false) == @as(i32, 20)); + assert(foo(true) == @as(i32, 30)); +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +fn foo(ok: bool) i32 { + const val: i32 = blk: { + var x: i32 = 1; + if (!ok) break :blk x + @as(i32, 9); + break :blk x + @as(i32, 19); + }; + return val + 10; +} + +// run +// diff --git a/test/incremental/wasm-wasi/error_unions.0.zig b/test/incremental/wasm-wasi/error_unions.0.zig new file mode 100644 index 0000000000..355698e4b6 --- /dev/null +++ b/test/incremental/wasm-wasi/error_unions.0.zig @@ -0,0 +1,15 @@ +pub fn main() void { + var e1 = error.Foo; + var e2 = error.Bar; + assert(e1 != e2); + assert(e1 == error.Foo); + assert(e2 == error.Bar); +} + +fn assert(b: bool) void { + if (!b) unreachable; +} + +// run +// target=wasm32-wasi +// diff --git a/test/incremental/wasm-wasi/error_unions.1.zig b/test/incremental/wasm-wasi/error_unions.1.zig new file mode 100644 index 0000000000..526e1a110d --- /dev/null +++ b/test/incremental/wasm-wasi/error_unions.1.zig @@ -0,0 +1,8 @@ +pub fn main() u8 { + var e: anyerror!u8 = 5; + const i = e catch 10; + return i - 5; +} + +// run +// diff --git a/test/incremental/wasm-wasi/error_unions.2.zig b/test/incremental/wasm-wasi/error_unions.2.zig new file mode 100644 index 0000000000..b66c7d78c0 --- /dev/null +++ b/test/incremental/wasm-wasi/error_unions.2.zig @@ -0,0 +1,8 @@ +pub fn main() u8 { + var e: anyerror!u8 = error.Foo; + const i = e catch 10; + return i - 10; +} + +// run +// diff --git a/test/incremental/wasm-wasi/error_unions.3.zig b/test/incremental/wasm-wasi/error_unions.3.zig new file mode 100644 index 0000000000..d37f826272 --- /dev/null +++ b/test/incremental/wasm-wasi/error_unions.3.zig @@ -0,0 +1,12 @@ +pub fn main() u8 { + var e = foo(); + const i = e catch 69; + return i - 5; +} + +fn foo() anyerror!u8 { + return 5; +} + +// run +// diff --git a/test/incremental/wasm-wasi/error_unions.4.zig b/test/incremental/wasm-wasi/error_unions.4.zig new file mode 100644 index 0000000000..bbc8dd34da --- /dev/null +++ b/test/incremental/wasm-wasi/error_unions.4.zig @@ -0,0 +1,12 @@ +pub fn main() u8 { + var e = foo(); + const i = e catch 69; + return i - 69; +} + +fn foo() anyerror!u8 { + return error.Bruh; +} + +// run +// diff --git a/test/incremental/wasm-wasi/error_unions.5.zig b/test/incremental/wasm-wasi/error_unions.5.zig new file mode 100644 index 0000000000..96d52d1de1 --- /dev/null +++ b/test/incremental/wasm-wasi/error_unions.5.zig @@ -0,0 +1,12 @@ +pub fn main() u8 { + var e = foo(); + const i = e catch 42; + return i - 42; +} + +fn foo() anyerror!u8 { + return error.Dab; +} + +// run +// diff --git a/test/incremental/wasm-wasi/locals.0.zig b/test/incremental/wasm-wasi/locals.0.zig new file mode 100644 index 0000000000..f2df242792 --- /dev/null +++ b/test/incremental/wasm-wasi/locals.0.zig @@ -0,0 +1,14 @@ +pub fn main() void { + var i: u8 = 5; + var y: f32 = 42.0; + var x: u8 = 10; + if (false) { + y; + x; + } + if (i != 5) unreachable; +} + +// run +// target=wasm32-wasi +// diff --git a/test/incremental/wasm-wasi/locals.1.zig b/test/incremental/wasm-wasi/locals.1.zig new file mode 100644 index 0000000000..f7e59e1c02 --- /dev/null +++ b/test/incremental/wasm-wasi/locals.1.zig @@ -0,0 +1,17 @@ +pub fn main() void { + var i: u8 = 5; + var y: f32 = 42.0; + _ = y; + var x: u8 = 10; + foo(i, x); + i = x; + if (i != 10) unreachable; +} +fn foo(x: u8, y: u8) void { + _ = y; + var i: u8 = 10; + i = x; +} + +// run +// diff --git a/test/incremental/wasm-wasi/optionals.0.zig b/test/incremental/wasm-wasi/optionals.0.zig new file mode 100644 index 0000000000..e602bf1c80 --- /dev/null +++ b/test/incremental/wasm-wasi/optionals.0.zig @@ -0,0 +1,12 @@ +pub fn main() u8 { + var x: ?u8 = 5; + var y: u8 = 0; + if (x) |val| { + y = val; + } + return y - 5; +} + +// run +// target=wasm32-wasi +// diff --git a/test/incremental/wasm-wasi/optionals.1.zig b/test/incremental/wasm-wasi/optionals.1.zig new file mode 100644 index 0000000000..26e81b8909 --- /dev/null +++ b/test/incremental/wasm-wasi/optionals.1.zig @@ -0,0 +1,11 @@ +pub fn main() u8 { + var x: ?u8 = null; + var y: u8 = 0; + if (x) |val| { + y = val; + } + return y; +} + +// run +// diff --git a/test/incremental/wasm-wasi/optionals.2.zig b/test/incremental/wasm-wasi/optionals.2.zig new file mode 100644 index 0000000000..b610c6d2e9 --- /dev/null +++ b/test/incremental/wasm-wasi/optionals.2.zig @@ -0,0 +1,7 @@ +pub fn main() u8 { + var x: ?u8 = 5; + return x.? - 5; +} + +// run +// diff --git a/test/incremental/wasm-wasi/optionals.3.zig b/test/incremental/wasm-wasi/optionals.3.zig new file mode 100644 index 0000000000..d513c10282 --- /dev/null +++ b/test/incremental/wasm-wasi/optionals.3.zig @@ -0,0 +1,8 @@ +pub fn main() u8 { + var x: u8 = 5; + var y: ?u8 = x; + return y.? - 5; +} + +// run +// diff --git a/test/incremental/wasm-wasi/optionals.4.zig b/test/incremental/wasm-wasi/optionals.4.zig new file mode 100644 index 0000000000..af3400b322 --- /dev/null +++ b/test/incremental/wasm-wasi/optionals.4.zig @@ -0,0 +1,13 @@ +pub fn main() u8 { + var val: ?u8 = 5; + while (val) |*v| { + v.* -= 1; + if (v.* == 2) { + val = null; + } + } + return 0; +} + +// run +// diff --git a/test/incremental/wasm-wasi/pointers.0.zig b/test/incremental/wasm-wasi/pointers.0.zig new file mode 100644 index 0000000000..958ee5ac88 --- /dev/null +++ b/test/incremental/wasm-wasi/pointers.0.zig @@ -0,0 +1,14 @@ +pub fn main() u8 { + var x: u8 = 0; + + foo(&x); + return x - 2; +} + +fn foo(x: *u8) void { + x.* = 2; +} + +// run +// target=wasm32-wasi +// diff --git a/test/incremental/wasm-wasi/pointers.1.zig b/test/incremental/wasm-wasi/pointers.1.zig new file mode 100644 index 0000000000..b3649cf520 --- /dev/null +++ b/test/incremental/wasm-wasi/pointers.1.zig @@ -0,0 +1,18 @@ +pub fn main() u8 { + var x: u8 = 0; + + foo(&x); + bar(&x); + return x - 4; +} + +fn foo(x: *u8) void { + x.* = 2; +} + +fn bar(x: *u8) void { + x.* += 2; +} + +// run +// diff --git a/test/incremental/wasm-wasi/structs.0.zig b/test/incremental/wasm-wasi/structs.0.zig new file mode 100644 index 0000000000..03a8938d9e --- /dev/null +++ b/test/incremental/wasm-wasi/structs.0.zig @@ -0,0 +1,10 @@ +const Example = struct { x: u8 }; + +pub fn main() u8 { + var example: Example = .{ .x = 5 }; + return example.x - 5; +} + +// run +// target=wasm32-wasi +// diff --git a/test/incremental/wasm-wasi/structs.1.zig b/test/incremental/wasm-wasi/structs.1.zig new file mode 100644 index 0000000000..8d4250e4a2 --- /dev/null +++ b/test/incremental/wasm-wasi/structs.1.zig @@ -0,0 +1,10 @@ +const Example = struct { x: u8 }; + +pub fn main() u8 { + var example: Example = .{ .x = 5 }; + example.x = 10; + return example.x - 10; +} + +// run +// diff --git a/test/incremental/wasm-wasi/structs.2.zig b/test/incremental/wasm-wasi/structs.2.zig new file mode 100644 index 0000000000..b1d3cbf080 --- /dev/null +++ b/test/incremental/wasm-wasi/structs.2.zig @@ -0,0 +1,9 @@ +const Example = struct { x: u8, y: u8 }; + +pub fn main() u8 { + var example: Example = .{ .x = 5, .y = 10 }; + return example.y + example.x - 15; +} + +// run +// diff --git a/test/incremental/wasm-wasi/structs.3.zig b/test/incremental/wasm-wasi/structs.3.zig new file mode 100644 index 0000000000..558b5a1e2d --- /dev/null +++ b/test/incremental/wasm-wasi/structs.3.zig @@ -0,0 +1,12 @@ +const Example = struct { x: u8, y: u8 }; + +pub fn main() u8 { + var example: Example = .{ .x = 5, .y = 10 }; + var example2: Example = .{ .x = 10, .y = 20 }; + + example = example2; + return example.y + example.x - 30; +} + +// run +// diff --git a/test/incremental/wasm-wasi/structs.4.zig b/test/incremental/wasm-wasi/structs.4.zig new file mode 100644 index 0000000000..2d37446c56 --- /dev/null +++ b/test/incremental/wasm-wasi/structs.4.zig @@ -0,0 +1,11 @@ +const Example = struct { x: u8, y: u8 }; + +pub fn main() u8 { + var example: Example = .{ .x = 5, .y = 10 }; + + example = .{ .x = 10, .y = 20 }; + return example.y + example.x - 30; +} + +// run +// diff --git a/test/incremental/wasm-wasi/switch.0.zig b/test/incremental/wasm-wasi/switch.0.zig new file mode 100644 index 0000000000..d0c0e43f8f --- /dev/null +++ b/test/incremental/wasm-wasi/switch.0.zig @@ -0,0 +1,15 @@ +pub fn main() u8 { + var val: u8 = 1; + var a: u8 = switch (val) { + 0, 1 => 2, + 2 => 3, + 3 => 4, + else => 5, + }; + + return a - 2; +} + +// run +// target=wasm32-wasi +// diff --git a/test/incremental/wasm-wasi/switch.1.zig b/test/incremental/wasm-wasi/switch.1.zig new file mode 100644 index 0000000000..fe503bf212 --- /dev/null +++ b/test/incremental/wasm-wasi/switch.1.zig @@ -0,0 +1,14 @@ +pub fn main() u8 { + var val: u8 = 2; + var a: u8 = switch (val) { + 0, 1 => 2, + 2 => 3, + 3 => 4, + else => 5, + }; + + return a - 3; +} + +// run +// diff --git a/test/incremental/wasm-wasi/switch.2.zig b/test/incremental/wasm-wasi/switch.2.zig new file mode 100644 index 0000000000..24f40d8402 --- /dev/null +++ b/test/incremental/wasm-wasi/switch.2.zig @@ -0,0 +1,14 @@ +pub fn main() u8 { + var val: u8 = 10; + var a: u8 = switch (val) { + 0, 1 => 2, + 2 => 3, + 3 => 4, + else => 5, + }; + + return a - 5; +} + +// run +// diff --git a/test/incremental/wasm-wasi/switch.3.zig b/test/incremental/wasm-wasi/switch.3.zig new file mode 100644 index 0000000000..e0b55db3bf --- /dev/null +++ b/test/incremental/wasm-wasi/switch.3.zig @@ -0,0 +1,15 @@ +const MyEnum = enum { One, Two, Three }; + +pub fn main() u8 { + var val: MyEnum = .Two; + var a: u8 = switch (val) { + .One => 1, + .Two => 2, + .Three => 3, + }; + + return a - 2; +} + +// run +// diff --git a/test/incremental/wasm-wasi/while_loops.0.zig b/test/incremental/wasm-wasi/while_loops.0.zig new file mode 100644 index 0000000000..721b127ff5 --- /dev/null +++ b/test/incremental/wasm-wasi/while_loops.0.zig @@ -0,0 +1,12 @@ +pub fn main() u8 { + var i: u8 = 0; + while (i < @as(u8, 5)) { + i += 1; + } + + return i - 5; +} + +// run +// target=wasm32-wasi +// diff --git a/test/incremental/wasm-wasi/while_loops.1.zig b/test/incremental/wasm-wasi/while_loops.1.zig new file mode 100644 index 0000000000..715a813b65 --- /dev/null +++ b/test/incremental/wasm-wasi/while_loops.1.zig @@ -0,0 +1,11 @@ +pub fn main() u8 { + var i: u8 = 0; + while (i < @as(u8, 10)) { + var x: u8 = 1; + i += x; + } + return i - 10; +} + +// run +// diff --git a/test/incremental/wasm-wasi/while_loops.2.zig b/test/incremental/wasm-wasi/while_loops.2.zig new file mode 100644 index 0000000000..7ad8101c37 --- /dev/null +++ b/test/incremental/wasm-wasi/while_loops.2.zig @@ -0,0 +1,12 @@ +pub fn main() u8 { + var i: u8 = 0; + while (i < @as(u8, 10)) { + var x: u8 = 1; + i += x; + if (i == @as(u8, 5)) break; + } + return i - 5; +} + +// run +// diff --git a/test/stage2/wasm.zig b/test/stage2/wasm.zig deleted file mode 100644 index 7c7e203b40..0000000000 --- a/test/stage2/wasm.zig +++ /dev/null @@ -1,796 +0,0 @@ -const std = @import("std"); -const TestContext = @import("../../src/test.zig").TestContext; - -const wasi = std.zig.CrossTarget{ - .cpu_arch = .wasm32, - .os_tag = .wasi, -}; - -pub fn addCases(ctx: *TestContext) !void { - { - var case = ctx.exe("wasm function calls", wasi); - - case.addCompareOutput( - \\pub fn main() void { - \\ foo(); - \\ bar(); - \\} - \\fn foo() void { - \\ bar(); - \\ bar(); - \\} - \\fn bar() void {} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ bar(); - \\ foo(); - \\ foo(); - \\ bar(); - \\ foo(); - \\ bar(); - \\} - \\fn foo() void { - \\ bar(); - \\} - \\fn bar() void {} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ bar(); - \\ foo(); - \\ return; - \\} - \\fn foo() void { - \\ bar(); - \\ bar(); - \\ bar(); - \\} - \\fn bar() void {} - , - "", - ); - - case.addCompareOutput( - \\pub fn main() void { - \\ foo(10, 20); - \\} - \\fn foo(x: u8, y: u8) void { _ = x; _ = y; } - , ""); - } - - { - var case = ctx.exe("wasm locals", wasi); - - case.addCompareOutput( - \\pub fn main() void { - \\ var i: u8 = 5; - \\ var y: f32 = 42.0; - \\ var x: u8 = 10; - \\ if (false) { - \\ y; - \\ x; - \\ } - \\ if (i != 5) unreachable; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var i: u8 = 5; - \\ var y: f32 = 42.0; - \\ _ = y; - \\ var x: u8 = 10; - \\ foo(i, x); - \\ i = x; - \\ if (i != 10) unreachable; - \\} - \\fn foo(x: u8, y: u8) void { - \\ _ = y; - \\ var i: u8 = 10; - \\ i = x; - \\} - , ""); - } - - { - var case = ctx.exe("wasm binary operands", wasi); - - case.addCompareOutput( - \\pub fn main() void { - \\ var i: u8 = 5; - \\ i += 20; - \\ if (i != 25) unreachable; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var i: i32 = 2147483647; - \\ if (i +% 1 != -2147483648) unreachable; - \\ return; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var i: i4 = 7; - \\ if (i +% 1 != 0) unreachable; - \\ return; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var i: u8 = 255; - \\ return i +% 1; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var i: u8 = 5; - \\ i += 20; - \\ var result: u8 = foo(i, 10); - \\ return result - 35; - \\} - \\fn foo(x: u8, y: u8) u8 { - \\ return x + y; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var i: u8 = 20; - \\ i -= 5; - \\ return i - 15; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var i: i32 = -2147483648; - \\ if (i -% 1 != 2147483647) unreachable; - \\ return; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var i: i7 = -64; - \\ if (i -% 1 != 63) unreachable; - \\ return; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var i: u4 = 0; - \\ if(i -% 1 != 15) unreachable; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var i: u8 = 5; - \\ i -= 3; - \\ var result: u8 = foo(i, 10); - \\ return result - 8; - \\} - \\fn foo(x: u8, y: u8) u8 { - \\ return y - x; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var i: u32 = 5; - \\ i *= 7; - \\ var result: u32 = foo(i, 10); - \\ if (result != 350) unreachable; - \\ return; - \\} - \\fn foo(x: u32, y: u32) u32 { - \\ return x * y; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var i: i32 = 2147483647; - \\ const result = i *% 2; - \\ if (result != -2) unreachable; - \\ return; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var i: u3 = 3; - \\ if (i *% 3 != 1) unreachable; - \\ return; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var i: i4 = 3; - \\ if (i *% 3 != 1) unreachable; - \\ return; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var i: u32 = 352; - \\ i /= 7; // i = 50 - \\ var result: u32 = foo(i, 7); - \\ if (result != 7) unreachable; - \\ return; - \\} - \\fn foo(x: u32, y: u32) u32 { - \\ return x / y; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var i: u8 = 5; - \\ i &= 6; - \\ return i - 4; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var i: u8 = 5; - \\ i |= 6; - \\ return i - 7; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var i: u8 = 5; - \\ i ^= 6; - \\ return i - 3; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var b: bool = false; - \\ b = b or false; - \\ if (b) unreachable; - \\ return; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var b: bool = true; - \\ b = b or false; - \\ if (!b) unreachable; - \\ return; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var b: bool = false; - \\ b = b or true; - \\ if (!b) unreachable; - \\ return; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var b: bool = true; - \\ b = b or true; - \\ if (!b) unreachable; - \\ return; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var b: bool = false; - \\ b = b and false; - \\ if (b) unreachable; - \\ return; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var b: bool = true; - \\ b = b and false; - \\ if (b) unreachable; - \\ return; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var b: bool = false; - \\ b = b and true; - \\ if (b) unreachable; - \\ return; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ var b: bool = true; - \\ b = b and true; - \\ if (!b) unreachable; - \\ return; - \\} - , ""); - } - - { - var case = ctx.exe("wasm conditions", wasi); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var i: u8 = 5; - \\ if (i > @as(u8, 4)) { - \\ i += 10; - \\ } - \\ return i - 15; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var i: u8 = 5; - \\ if (i < @as(u8, 4)) { - \\ i += 10; - \\ } else { - \\ i = 2; - \\ } - \\ return i - 2; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var i: u8 = 5; - \\ if (i < @as(u8, 4)) { - \\ i += 10; - \\ } else if(i == @as(u8, 5)) { - \\ i = 20; - \\ } - \\ return i - 20; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var i: u8 = 11; - \\ if (i < @as(u8, 4)) { - \\ i += 10; - \\ } else { - \\ if (i > @as(u8, 10)) { - \\ i += 20; - \\ } else { - \\ i = 20; - \\ } - \\ } - \\ return i - 31; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ assert(foo(true) != @as(i32, 30)); - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - \\ - \\fn foo(ok: bool) i32 { - \\ const x = if(ok) @as(i32, 20) else @as(i32, 10); - \\ return x; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() void { - \\ assert(foo(false) == @as(i32, 20)); - \\ assert(foo(true) == @as(i32, 30)); - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - \\ - \\fn foo(ok: bool) i32 { - \\ const val: i32 = blk: { - \\ var x: i32 = 1; - \\ if (!ok) break :blk x + @as(i32, 9); - \\ break :blk x + @as(i32, 19); - \\ }; - \\ return val + 10; - \\} - , ""); - } - - { - var case = ctx.exe("wasm while loops", wasi); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var i: u8 = 0; - \\ while(i < @as(u8, 5)){ - \\ i += 1; - \\ } - \\ - \\ return i - 5; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var i: u8 = 0; - \\ while(i < @as(u8, 10)){ - \\ var x: u8 = 1; - \\ i += x; - \\ } - \\ return i - 10; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var i: u8 = 0; - \\ while(i < @as(u8, 10)){ - \\ var x: u8 = 1; - \\ i += x; - \\ if (i == @as(u8, 5)) break; - \\ } - \\ return i - 5; - \\} - , ""); - } - - { - var case = ctx.exe("wasm enum values", wasi); - - case.addCompareOutput( - \\const Number = enum { One, Two, Three }; - \\ - \\pub fn main() void { - \\ var number1 = Number.One; - \\ var number2: Number = .Two; - \\ if (false) { - \\ number1; - \\ number2; - \\ } - \\ const number3 = @intToEnum(Number, 2); - \\ if (@enumToInt(number3) != 2) { - \\ unreachable; - \\ } - \\ return; - \\} - , ""); - - case.addCompareOutput( - \\const Number = enum { One, Two, Three }; - \\ - \\pub fn main() void { - \\ var number1 = Number.One; - \\ var number2: Number = .Two; - \\ const number3 = @intToEnum(Number, 2); - \\ assert(number1 != number2); - \\ assert(number2 != number3); - \\ assert(@enumToInt(number1) == 0); - \\ assert(@enumToInt(number2) == 1); - \\ assert(@enumToInt(number3) == 2); - \\ var x: Number = .Two; - \\ assert(number2 == x); - \\ - \\ return; - \\} - \\fn assert(val: bool) void { - \\ if(!val) unreachable; - \\} - , ""); - } - - { - var case = ctx.exe("wasm structs", wasi); - - case.addCompareOutput( - \\const Example = struct { x: u8 }; - \\ - \\pub fn main() u8 { - \\ var example: Example = .{ .x = 5 }; - \\ return example.x - 5; - \\} - , ""); - - case.addCompareOutput( - \\const Example = struct { x: u8 }; - \\ - \\pub fn main() u8 { - \\ var example: Example = .{ .x = 5 }; - \\ example.x = 10; - \\ return example.x - 10; - \\} - , ""); - - case.addCompareOutput( - \\const Example = struct { x: u8, y: u8 }; - \\ - \\pub fn main() u8 { - \\ var example: Example = .{ .x = 5, .y = 10 }; - \\ return example.y + example.x - 15; - \\} - , ""); - - case.addCompareOutput( - \\const Example = struct { x: u8, y: u8 }; - \\ - \\pub fn main() u8 { - \\ var example: Example = .{ .x = 5, .y = 10 }; - \\ var example2: Example = .{ .x = 10, .y = 20 }; - \\ - \\ example = example2; - \\ return example.y + example.x - 30; - \\} - , ""); - - case.addCompareOutput( - \\const Example = struct { x: u8, y: u8 }; - \\ - \\pub fn main() u8 { - \\ var example: Example = .{ .x = 5, .y = 10 }; - \\ - \\ example = .{ .x = 10, .y = 20 }; - \\ return example.y + example.x - 30; - \\} - , ""); - } - - { - var case = ctx.exe("wasm switch", wasi); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var val: u8 = 1; - \\ var a: u8 = switch (val) { - \\ 0, 1 => 2, - \\ 2 => 3, - \\ 3 => 4, - \\ else => 5, - \\ }; - \\ - \\ return a - 2; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var val: u8 = 2; - \\ var a: u8 = switch (val) { - \\ 0, 1 => 2, - \\ 2 => 3, - \\ 3 => 4, - \\ else => 5, - \\ }; - \\ - \\ return a - 3; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var val: u8 = 10; - \\ var a: u8 = switch (val) { - \\ 0, 1 => 2, - \\ 2 => 3, - \\ 3 => 4, - \\ else => 5, - \\ }; - \\ - \\ return a - 5; - \\} - , ""); - - case.addCompareOutput( - \\const MyEnum = enum { One, Two, Three }; - \\ - \\pub fn main() u8 { - \\ var val: MyEnum = .Two; - \\ var a: u8 = switch (val) { - \\ .One => 1, - \\ .Two => 2, - \\ .Three => 3, - \\ }; - \\ - \\ return a - 2; - \\} - , ""); - } - - { - var case = ctx.exe("wasm error unions", wasi); - - case.addCompareOutput( - \\pub fn main() void { - \\ var e1 = error.Foo; - \\ var e2 = error.Bar; - \\ assert(e1 != e2); - \\ assert(e1 == error.Foo); - \\ assert(e2 == error.Bar); - \\} - \\ - \\fn assert(b: bool) void { - \\ if (!b) unreachable; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var e: anyerror!u8 = 5; - \\ const i = e catch 10; - \\ return i - 5; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var e: anyerror!u8 = error.Foo; - \\ const i = e catch 10; - \\ return i - 10; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var e = foo(); - \\ const i = e catch 69; - \\ return i - 5; - \\} - \\ - \\fn foo() anyerror!u8 { - \\ return 5; - \\} - , ""); - } - - { - var case = ctx.exe("wasm error union part 2", wasi); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var e = foo(); - \\ const i = e catch 69; - \\ return i - 69; - \\} - \\ - \\fn foo() anyerror!u8 { - \\ return error.Bruh; - \\} - , ""); - case.addCompareOutput( - \\pub fn main() u8 { - \\ var e = foo(); - \\ const i = e catch 42; - \\ return i - 42; - \\} - \\ - \\fn foo() anyerror!u8 { - \\ return error.Dab; - \\} - , ""); - } - - { - var case = ctx.exe("wasm integer widening", wasi); - - case.addCompareOutput( - \\pub fn main() void{ - \\ var x: u8 = 5; - \\ var y: u64 = x; - \\ _ = y; - \\ return; - \\} - , ""); - } - - { - var case = ctx.exe("wasm optionals", wasi); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var x: ?u8 = 5; - \\ var y: u8 = 0; - \\ if (x) |val| { - \\ y = val; - \\ } - \\ return y - 5; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var x: ?u8 = null; - \\ var y: u8 = 0; - \\ if (x) |val| { - \\ y = val; - \\ } - \\ return y; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var x: ?u8 = 5; - \\ return x.? - 5; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var x: u8 = 5; - \\ var y: ?u8 = x; - \\ return y.? - 5; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var val: ?u8 = 5; - \\ while (val) |*v| { - \\ v.* -= 1; - \\ if (v.* == 2) { - \\ val = null; - \\ } - \\ } - \\ return 0; - \\} - , ""); - } - - { - var case = ctx.exe("wasm pointers", wasi); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var x: u8 = 0; - \\ - \\ foo(&x); - \\ return x - 2; - \\} - \\ - \\fn foo(x: *u8)void { - \\ x.* = 2; - \\} - , ""); - - case.addCompareOutput( - \\pub fn main() u8 { - \\ var x: u8 = 0; - \\ - \\ foo(&x); - \\ bar(&x); - \\ return x - 4; - \\} - \\ - \\fn foo(x: *u8)void { - \\ x.* = 2; - \\} - \\ - \\fn bar(x: *u8) void { - \\ x.* += 2; - \\} - , ""); - } -} From 7e17cbbda5de49759f7b130320578ed96b3810a1 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 27 Apr 2022 23:06:11 +0200 Subject: [PATCH 1284/2031] test: migrate riscv64 incremental tests --- test/cases.zig | 1 - .../hello_world_with_updates.0.zig | 21 ++++++++++++ .../hello_world_with_updates.1.zig | 27 +++++++++++++++ test/stage2/riscv64.zig | 33 ------------------- 4 files changed, 48 insertions(+), 34 deletions(-) create mode 100644 test/incremental/riscv64-linux/hello_world_with_updates.0.zig create mode 100644 test/incremental/riscv64-linux/hello_world_with_updates.1.zig delete mode 100644 test/stage2/riscv64.zig diff --git a/test/cases.zig b/test/cases.zig index cc77104fb9..fc38794cdd 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -12,7 +12,6 @@ pub fn addCases(ctx: *TestContext) !void { try @import("stage2/arm.zig").addCases(ctx); try @import("stage2/aarch64.zig").addCases(ctx); try @import("stage2/llvm.zig").addCases(ctx); - try @import("stage2/riscv64.zig").addCases(ctx); try @import("stage2/plan9.zig").addCases(ctx); try @import("stage2/x86_64.zig").addCases(ctx); try @import("stage2/sparcv9.zig").addCases(ctx); diff --git a/test/incremental/riscv64-linux/hello_world_with_updates.0.zig b/test/incremental/riscv64-linux/hello_world_with_updates.0.zig new file mode 100644 index 0000000000..dd119fd1f4 --- /dev/null +++ b/test/incremental/riscv64-linux/hello_world_with_updates.0.zig @@ -0,0 +1,21 @@ +pub fn main() void { + print(); +} + +fn print() void { + asm volatile ("ecall" + : + : [number] "{a7}" (64), + [arg1] "{a0}" (1), + [arg2] "{a1}" (@ptrToInt("Hello, World!\n")), + [arg3] "{a2}" ("Hello, World!\n".len), + : "rcx", "r11", "memory" + ); + return; +} + +// run +// target=riscv64-linux +// +// Hello, World! +// diff --git a/test/incremental/riscv64-linux/hello_world_with_updates.1.zig b/test/incremental/riscv64-linux/hello_world_with_updates.1.zig new file mode 100644 index 0000000000..26718738a9 --- /dev/null +++ b/test/incremental/riscv64-linux/hello_world_with_updates.1.zig @@ -0,0 +1,27 @@ +pub fn main() void { + print(); + print(); + print(); + print(); +} + +fn print() void { + asm volatile ("ecall" + : + : [number] "{a7}" (64), + [arg1] "{a0}" (1), + [arg2] "{a1}" (@ptrToInt("Hello, World!\n")), + [arg3] "{a2}" ("Hello, World!\n".len), + : "rcx", "r11", "memory" + ); + return; +} + +// run +// target=riscv64-linux +// +// Hello, World! +// Hello, World! +// Hello, World! +// Hello, World! +// diff --git a/test/stage2/riscv64.zig b/test/stage2/riscv64.zig deleted file mode 100644 index c492f1d6b7..0000000000 --- a/test/stage2/riscv64.zig +++ /dev/null @@ -1,33 +0,0 @@ -const std = @import("std"); -const TestContext = @import("../../src/test.zig").TestContext; - -const linux_riscv64 = std.zig.CrossTarget{ - .cpu_arch = .riscv64, - .os_tag = .linux, -}; - -pub fn addCases(ctx: *TestContext) !void { - { - var case = ctx.exe("riscv64 hello world", linux_riscv64); - // Regular old hello world - case.addCompareOutput( - \\pub fn main() void { - \\ print(); - \\} - \\ - \\fn print() void { - \\ asm volatile ("ecall" - \\ : - \\ : [number] "{a7}" (64), - \\ [arg1] "{a0}" (1), - \\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{a2}" ("Hello, World!\n".len) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - , - "Hello, World!\n", - ); - } -} From fc4fbfe8e1689417da8130f02045e5cc9e120a1a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 27 Apr 2022 23:36:29 +0200 Subject: [PATCH 1285/2031] test: migrate aarch64 incremental tests --- test/cases.zig | 1 - .../aarch64-linux/conditional_branches.0.zig | 26 ++ .../aarch64-linux/conditional_branches.1.zig | 25 ++ .../hello_world_with_updates.0.zig | 31 ++ .../hello_world_with_updates.1.zig | 36 +++ .../hello_world_with_updates.2.zig | 21 ++ .../hello_world_with_updates.0.zig | 5 + .../hello_world_with_updates.1.zig | 5 + .../hello_world_with_updates.2.zig | 19 ++ .../hello_world_with_updates.3.zig | 16 ++ .../hello_world_with_updates.4.zig | 22 ++ .../hello_world_with_updates.5.zig | 16 ++ .../hello_world_with_updates.6.zig | 18 ++ test/incremental/large_add_function.zig | 38 +++ test/stage2/aarch64.zig | 271 ------------------ 15 files changed, 278 insertions(+), 272 deletions(-) create mode 100644 test/incremental/aarch64-linux/conditional_branches.0.zig create mode 100644 test/incremental/aarch64-linux/conditional_branches.1.zig create mode 100644 test/incremental/aarch64-linux/hello_world_with_updates.0.zig create mode 100644 test/incremental/aarch64-linux/hello_world_with_updates.1.zig create mode 100644 test/incremental/aarch64-linux/hello_world_with_updates.2.zig create mode 100644 test/incremental/aarch64-macos/hello_world_with_updates.0.zig create mode 100644 test/incremental/aarch64-macos/hello_world_with_updates.1.zig create mode 100644 test/incremental/aarch64-macos/hello_world_with_updates.2.zig create mode 100644 test/incremental/aarch64-macos/hello_world_with_updates.3.zig create mode 100644 test/incremental/aarch64-macos/hello_world_with_updates.4.zig create mode 100644 test/incremental/aarch64-macos/hello_world_with_updates.5.zig create mode 100644 test/incremental/aarch64-macos/hello_world_with_updates.6.zig create mode 100644 test/incremental/large_add_function.zig delete mode 100644 test/stage2/aarch64.zig diff --git a/test/cases.zig b/test/cases.zig index fc38794cdd..c615d84099 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -10,7 +10,6 @@ pub fn addCases(ctx: *TestContext) !void { try @import("compile_errors.zig").addCases(ctx); try @import("stage2/cbe.zig").addCases(ctx); try @import("stage2/arm.zig").addCases(ctx); - try @import("stage2/aarch64.zig").addCases(ctx); try @import("stage2/llvm.zig").addCases(ctx); try @import("stage2/plan9.zig").addCases(ctx); try @import("stage2/x86_64.zig").addCases(ctx); diff --git a/test/incremental/aarch64-linux/conditional_branches.0.zig b/test/incremental/aarch64-linux/conditional_branches.0.zig new file mode 100644 index 0000000000..28435b9d4a --- /dev/null +++ b/test/incremental/aarch64-linux/conditional_branches.0.zig @@ -0,0 +1,26 @@ +pub fn main() void { + foo(123); +} + +fn foo(x: u64) void { + if (x > 42) { + print(); + } +} + +fn print() void { + asm volatile ("svc #0" + : + : [number] "{x8}" (64), + [arg1] "{x0}" (1), + [arg2] "{x1}" (@ptrToInt("Hello, World!\n")), + [arg3] "{x2}" ("Hello, World!\n".len), + : "memory", "cc" + ); +} + +// run +// target=aarch64-linux +// +// Hello, World! +// diff --git a/test/incremental/aarch64-linux/conditional_branches.1.zig b/test/incremental/aarch64-linux/conditional_branches.1.zig new file mode 100644 index 0000000000..e03758fcb7 --- /dev/null +++ b/test/incremental/aarch64-linux/conditional_branches.1.zig @@ -0,0 +1,25 @@ +pub fn main() void { + foo(true); +} + +fn foo(x: bool) void { + if (x) { + print(); + } +} + +fn print() void { + asm volatile ("svc #0" + : + : [number] "{x8}" (64), + [arg1] "{x0}" (1), + [arg2] "{x1}" (@ptrToInt("Hello, World!\n")), + [arg3] "{x2}" ("Hello, World!\n".len), + : "memory", "cc" + ); +} + +// run +// +// Hello, World! +// diff --git a/test/incremental/aarch64-linux/hello_world_with_updates.0.zig b/test/incremental/aarch64-linux/hello_world_with_updates.0.zig new file mode 100644 index 0000000000..7a87d74dc3 --- /dev/null +++ b/test/incremental/aarch64-linux/hello_world_with_updates.0.zig @@ -0,0 +1,31 @@ +pub export fn _start() noreturn { + print(); + exit(0); +} + +fn print() void { + asm volatile ("svc #0" + : + : [number] "{x8}" (64), + [arg1] "{x0}" (1), + [arg2] "{x1}" (@ptrToInt("Hello, World!\n")), + [arg3] "{x2}" ("Hello, World!\n".len), + : "memory", "cc" + ); +} + +fn exit(ret: usize) noreturn { + asm volatile ("svc #0" + : + : [number] "{x8}" (93), + [arg1] "{x0}" (ret), + : "memory", "cc" + ); + unreachable; +} + +// run +// target=aarch64-linux +// +// Hello, World! +// diff --git a/test/incremental/aarch64-linux/hello_world_with_updates.1.zig b/test/incremental/aarch64-linux/hello_world_with_updates.1.zig new file mode 100644 index 0000000000..a5ca0d9926 --- /dev/null +++ b/test/incremental/aarch64-linux/hello_world_with_updates.1.zig @@ -0,0 +1,36 @@ +pub export fn _start() noreturn { + print(); + print(); + print(); + print(); + exit(0); +} + +fn print() void { + asm volatile ("svc #0" + : + : [number] "{x8}" (64), + [arg1] "{x0}" (1), + [arg2] "{x1}" (@ptrToInt("Hello, World!\n")), + [arg3] "{x2}" ("Hello, World!\n".len), + : "memory", "cc" + ); +} + +fn exit(ret: usize) noreturn { + asm volatile ("svc #0" + : + : [number] "{x8}" (93), + [arg1] "{x0}" (ret), + : "memory", "cc" + ); + unreachable; +} + +// run +// +// Hello, World! +// Hello, World! +// Hello, World! +// Hello, World! +// diff --git a/test/incremental/aarch64-linux/hello_world_with_updates.2.zig b/test/incremental/aarch64-linux/hello_world_with_updates.2.zig new file mode 100644 index 0000000000..71f0f160bc --- /dev/null +++ b/test/incremental/aarch64-linux/hello_world_with_updates.2.zig @@ -0,0 +1,21 @@ +pub fn main() void { + print(); + print(); +} + +fn print() void { + asm volatile ("svc #0" + : + : [number] "{x8}" (64), + [arg1] "{x0}" (1), + [arg2] "{x1}" (@ptrToInt("Hello, World!\n")), + [arg3] "{x2}" ("Hello, World!\n".len), + : "memory", "cc" + ); +} + +// run +// +// Hello, World! +// Hello, World! +// diff --git a/test/incremental/aarch64-macos/hello_world_with_updates.0.zig b/test/incremental/aarch64-macos/hello_world_with_updates.0.zig new file mode 100644 index 0000000000..11a379a9a9 --- /dev/null +++ b/test/incremental/aarch64-macos/hello_world_with_updates.0.zig @@ -0,0 +1,5 @@ +// error +// output_mode=Exe +// target=aarch64-macos +// +// :109:9: error: struct 'tmp.tmp' has no member named 'main' diff --git a/test/incremental/aarch64-macos/hello_world_with_updates.1.zig b/test/incremental/aarch64-macos/hello_world_with_updates.1.zig new file mode 100644 index 0000000000..909fc9ccfb --- /dev/null +++ b/test/incremental/aarch64-macos/hello_world_with_updates.1.zig @@ -0,0 +1,5 @@ +pub export fn main() noreturn {} + +// error +// +// :1:32: error: expected noreturn, found void diff --git a/test/incremental/aarch64-macos/hello_world_with_updates.2.zig b/test/incremental/aarch64-macos/hello_world_with_updates.2.zig new file mode 100644 index 0000000000..fb8cb39edd --- /dev/null +++ b/test/incremental/aarch64-macos/hello_world_with_updates.2.zig @@ -0,0 +1,19 @@ +extern "c" fn write(usize, usize, usize) usize; +extern "c" fn exit(usize) noreturn; + +pub export fn main() noreturn { + print(); + + exit(0); +} + +fn print() void { + const msg = @ptrToInt("Hello, World!\n"); + const len = 14; + _ = write(1, msg, len); +} + +// run +// +// Hello, World! +// diff --git a/test/incremental/aarch64-macos/hello_world_with_updates.3.zig b/test/incremental/aarch64-macos/hello_world_with_updates.3.zig new file mode 100644 index 0000000000..f6e233886b --- /dev/null +++ b/test/incremental/aarch64-macos/hello_world_with_updates.3.zig @@ -0,0 +1,16 @@ +extern "c" fn write(usize, usize, usize) usize; + +pub fn main() void { + print(); +} + +fn print() void { + const msg = @ptrToInt("Hello, World!\n"); + const len = 14; + _ = write(1, msg, len); +} + +// run +// +// Hello, World! +// diff --git a/test/incremental/aarch64-macos/hello_world_with_updates.4.zig b/test/incremental/aarch64-macos/hello_world_with_updates.4.zig new file mode 100644 index 0000000000..f89cef7354 --- /dev/null +++ b/test/incremental/aarch64-macos/hello_world_with_updates.4.zig @@ -0,0 +1,22 @@ +extern "c" fn write(usize, usize, usize) usize; + +pub fn main() void { + print(); + print(); + print(); + print(); +} + +fn print() void { + const msg = @ptrToInt("Hello, World!\n"); + const len = 14; + _ = write(1, msg, len); +} + +// run +// +// Hello, World! +// Hello, World! +// Hello, World! +// Hello, World! +// diff --git a/test/incremental/aarch64-macos/hello_world_with_updates.5.zig b/test/incremental/aarch64-macos/hello_world_with_updates.5.zig new file mode 100644 index 0000000000..0d7f97578a --- /dev/null +++ b/test/incremental/aarch64-macos/hello_world_with_updates.5.zig @@ -0,0 +1,16 @@ +extern "c" fn write(usize, usize, usize) usize; + +pub fn main() void { + print(); +} + +fn print() void { + const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n"); + const len = 104; + _ = write(1, msg, len); +} + +// run +// +// What is up? This is a longer message that will force the data to be relocated in virtual address space. +// diff --git a/test/incremental/aarch64-macos/hello_world_with_updates.6.zig b/test/incremental/aarch64-macos/hello_world_with_updates.6.zig new file mode 100644 index 0000000000..3ce2cb7176 --- /dev/null +++ b/test/incremental/aarch64-macos/hello_world_with_updates.6.zig @@ -0,0 +1,18 @@ +extern "c" fn write(usize, usize, usize) usize; + +pub fn main() void { + print(); + print(); +} + +fn print() void { + const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n"); + const len = 104; + _ = write(1, msg, len); +} + +// run +// +// What is up? This is a longer message that will force the data to be relocated in virtual address space. +// What is up? This is a longer message that will force the data to be relocated in virtual address space. +// diff --git a/test/incremental/large_add_function.zig b/test/incremental/large_add_function.zig new file mode 100644 index 0000000000..8b0e9b879b --- /dev/null +++ b/test/incremental/large_add_function.zig @@ -0,0 +1,38 @@ +pub fn main() void { + assert(add(3, 4) == 791); +} + +fn add(a: u32, b: u32) u32 { + const x: u32 = blk: { + const c = a + b; // 7 + const d = a + c; // 10 + const e = d + b; // 14 + const f = d + e; // 24 + const g = e + f; // 38 + const h = f + g; // 62 + const i = g + h; // 100 + const j = i + d; // 110 + const k = i + j; // 210 + const l = k + c; // 217 + const m = l + d; // 227 + const n = m + e; // 241 + const o = n + f; // 265 + const p = o + g; // 303 + const q = p + h; // 365 + const r = q + i; // 465 + const s = r + j; // 575 + const t = s + k; // 785 + break :blk t; + }; + const y = x + a; // 788 + const z = y + a; // 791 + return z; +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// run +// target=aarch64-linux,aarch64-macos +// diff --git a/test/stage2/aarch64.zig b/test/stage2/aarch64.zig deleted file mode 100644 index 84334c2a29..0000000000 --- a/test/stage2/aarch64.zig +++ /dev/null @@ -1,271 +0,0 @@ -const std = @import("std"); -const CrossTarget = std.zig.CrossTarget; -const TestContext = @import("../../src/test.zig").TestContext; - -const linux_aarch64 = CrossTarget{ - .cpu_arch = .aarch64, - .os_tag = .linux, -}; -const macos_aarch64 = CrossTarget{ - .cpu_arch = .aarch64, - .os_tag = .macos, -}; - -pub fn addCases(ctx: *TestContext) !void { - // Linux tests - { - var case = ctx.exe("linux_aarch64 hello world", linux_aarch64); - // Regular old hello world - case.addCompareOutput( - \\pub fn main() void { - \\ print(); - \\} - \\ - \\fn print() void { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{x8}" (64), - \\ [arg1] "{x0}" (1), - \\ [arg2] "{x1}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{x2}" ("Hello, World!\n".len) - \\ : "memory", "cc" - \\ ); - \\} - , - "Hello, World!\n", - ); - } - - { - var case = ctx.exe("exit fn taking argument", linux_aarch64); - - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ exit(0); - \\} - \\ - \\fn exit(ret: usize) noreturn { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{x8}" (93), - \\ [arg1] "{x0}" (ret) - \\ : "memory", "cc" - \\ ); - \\ unreachable; - \\} - , - "", - ); - } - - { - var case = ctx.exe("conditional branches", linux_aarch64); - - case.addCompareOutput( - \\pub fn main() void { - \\ foo(123); - \\} - \\ - \\fn foo(x: u64) void { - \\ if (x > 42) { - \\ print(); - \\ } - \\} - \\ - \\fn print() void { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{x8}" (64), - \\ [arg1] "{x0}" (1), - \\ [arg2] "{x1}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{x2}" ("Hello, World!\n".len), - \\ : "memory", "cc" - \\ ); - \\} - , - "Hello, World!\n", - ); - - case.addCompareOutput( - \\pub fn main() void { - \\ foo(true); - \\} - \\ - \\fn foo(x: bool) void { - \\ if (x) { - \\ print(); - \\ } - \\} - \\ - \\fn print() void { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{x8}" (64), - \\ [arg1] "{x0}" (1), - \\ [arg2] "{x1}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{x2}" ("Hello, World!\n".len), - \\ : "memory", "cc" - \\ ); - \\} - , - "Hello, World!\n", - ); - } - - { - var case = ctx.exe("large add function", linux_aarch64); - - case.addCompareOutput( - \\pub fn main() void { - \\ assert(add(3, 4) == 791); - \\} - \\ - \\fn add(a: u32, b: u32) u32 { - \\ const x: u32 = blk: { - \\ const c = a + b; // 7 - \\ const d = a + c; // 10 - \\ const e = d + b; // 14 - \\ const f = d + e; // 24 - \\ const g = e + f; // 38 - \\ const h = f + g; // 62 - \\ const i = g + h; // 100 - \\ const j = i + d; // 110 - \\ const k = i + j; // 210 - \\ const l = k + c; // 217 - \\ const m = l + d; // 227 - \\ const n = m + e; // 241 - \\ const o = n + f; // 265 - \\ const p = o + g; // 303 - \\ const q = p + h; // 365 - \\ const r = q + i; // 465 - \\ const s = r + j; // 575 - \\ const t = s + k; // 785 - \\ break :blk t; - \\ }; - \\ const y = x + a; // 788 - \\ const z = y + a; // 791 - \\ return z; - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , - "", - ); - } - - // macOS tests - { - var case = ctx.exe("hello world with updates", macos_aarch64); - case.addError("", &[_][]const u8{ - ":109:9: error: struct 'tmp.tmp' has no member named 'main'", - }); - - // Incorrect return type - case.addError( - \\pub export fn main() noreturn { - \\} - , &[_][]const u8{ - ":2:1: error: expected noreturn, found void", - }); - - // Regular old hello world - case.addCompareOutput( - \\extern "c" fn write(usize, usize, usize) usize; - \\extern "c" fn exit(usize) noreturn; - \\ - \\pub export fn main() noreturn { - \\ print(); - \\ - \\ exit(0); - \\} - \\ - \\fn print() void { - \\ const msg = @ptrToInt("Hello, World!\n"); - \\ const len = 14; - \\ _ = write(1, msg, len); - \\} - , - "Hello, World!\n", - ); - - // Now using start.zig without an explicit extern exit fn - case.addCompareOutput( - \\extern "c" fn write(usize, usize, usize) usize; - \\ - \\pub fn main() void { - \\ print(); - \\} - \\ - \\fn print() void { - \\ const msg = @ptrToInt("Hello, World!\n"); - \\ const len = 14; - \\ _ = write(1, msg, len); - \\} - , - "Hello, World!\n", - ); - - // Print it 4 times and force growth and realloc. - case.addCompareOutput( - \\extern "c" fn write(usize, usize, usize) usize; - \\ - \\pub fn main() void { - \\ print(); - \\ print(); - \\ print(); - \\ print(); - \\} - \\ - \\fn print() void { - \\ const msg = @ptrToInt("Hello, World!\n"); - \\ const len = 14; - \\ _ = write(1, msg, len); - \\} - , - \\Hello, World! - \\Hello, World! - \\Hello, World! - \\Hello, World! - \\ - ); - - // Print it once, and change the message. - case.addCompareOutput( - \\extern "c" fn write(usize, usize, usize) usize; - \\ - \\pub fn main() void { - \\ print(); - \\} - \\ - \\fn print() void { - \\ const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n"); - \\ const len = 104; - \\ _ = write(1, msg, len); - \\} - , - "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n", - ); - - // Now we print it twice. - case.addCompareOutput( - \\extern "c" fn write(usize, usize, usize) usize; - \\ - \\pub fn main() void { - \\ print(); - \\ print(); - \\} - \\ - \\fn print() void { - \\ const msg = @ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n"); - \\ const len = 104; - \\ _ = write(1, msg, len); - \\} - , - \\What is up? This is a longer message that will force the data to be relocated in virtual address space. - \\What is up? This is a longer message that will force the data to be relocated in virtual address space. - \\ - ); - } -} From ed51a5d02af58b3d11b555233781d71e6bde60af Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 28 Apr 2022 00:09:26 +0200 Subject: [PATCH 1286/2031] test: migrate arm incremental tests --- test/cases.zig | 1 - .../arm-linux/arithmetic_operations.0.zig | 21 + .../arm-linux/arithmetic_operations.1.zig | 20 + .../arm-linux/arithmetic_operations.2.zig | 20 + .../arm-linux/arithmetic_operations.3.zig | 20 + .../arm-linux/arithmetic_operations.4.zig | 20 + .../arm-linux/arithmetic_operations.5.zig | 15 + .../arm-linux/arithmetic_operations.6.zig | 21 + test/incremental/arm-linux/errors.0.zig | 21 + test/incremental/arm-linux/errors.1.zig | 24 + test/incremental/arm-linux/errors.2.zig | 27 + test/incremental/arm-linux/errors.3.zig | 12 + .../arm-linux/function_pointers.zig | 44 + .../arm-linux/hello_world_with_updates.0.zig | 32 + .../arm-linux/hello_world_with_updates.1.zig | 37 + .../arm-linux/hello_world_with_updates.2.zig | 22 + .../parameters_and_return_values.0.zig | 28 + .../parameters_and_return_values.1.zig | 14 + test/incremental/arm-linux/print_u32s.zig | 40 + .../arm-linux/spilling_registers.0.zig | 38 + .../arm-linux/spilling_registers.1.zig | 37 + test/incremental/non_leaf_functions.zig | 12 + test/incremental/recursive_fibonacci.zig | 24 + ...rn_values_in_callee_preserved_register.zig | 22 + test/stage2/arm.zig | 798 ------------------ 25 files changed, 571 insertions(+), 799 deletions(-) create mode 100644 test/incremental/arm-linux/arithmetic_operations.0.zig create mode 100644 test/incremental/arm-linux/arithmetic_operations.1.zig create mode 100644 test/incremental/arm-linux/arithmetic_operations.2.zig create mode 100644 test/incremental/arm-linux/arithmetic_operations.3.zig create mode 100644 test/incremental/arm-linux/arithmetic_operations.4.zig create mode 100644 test/incremental/arm-linux/arithmetic_operations.5.zig create mode 100644 test/incremental/arm-linux/arithmetic_operations.6.zig create mode 100644 test/incremental/arm-linux/errors.0.zig create mode 100644 test/incremental/arm-linux/errors.1.zig create mode 100644 test/incremental/arm-linux/errors.2.zig create mode 100644 test/incremental/arm-linux/errors.3.zig create mode 100644 test/incremental/arm-linux/function_pointers.zig create mode 100644 test/incremental/arm-linux/hello_world_with_updates.0.zig create mode 100644 test/incremental/arm-linux/hello_world_with_updates.1.zig create mode 100644 test/incremental/arm-linux/hello_world_with_updates.2.zig create mode 100644 test/incremental/arm-linux/parameters_and_return_values.0.zig create mode 100644 test/incremental/arm-linux/parameters_and_return_values.1.zig create mode 100644 test/incremental/arm-linux/print_u32s.zig create mode 100644 test/incremental/arm-linux/spilling_registers.0.zig create mode 100644 test/incremental/arm-linux/spilling_registers.1.zig create mode 100644 test/incremental/non_leaf_functions.zig create mode 100644 test/incremental/recursive_fibonacci.zig create mode 100644 test/incremental/save_function_return_values_in_callee_preserved_register.zig delete mode 100644 test/stage2/arm.zig diff --git a/test/cases.zig b/test/cases.zig index c615d84099..f90233df0f 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -9,7 +9,6 @@ const TestContext = @import("../src/test.zig").TestContext; pub fn addCases(ctx: *TestContext) !void { try @import("compile_errors.zig").addCases(ctx); try @import("stage2/cbe.zig").addCases(ctx); - try @import("stage2/arm.zig").addCases(ctx); try @import("stage2/llvm.zig").addCases(ctx); try @import("stage2/plan9.zig").addCases(ctx); try @import("stage2/x86_64.zig").addCases(ctx); diff --git a/test/incremental/arm-linux/arithmetic_operations.0.zig b/test/incremental/arm-linux/arithmetic_operations.0.zig new file mode 100644 index 0000000000..51a2dbd700 --- /dev/null +++ b/test/incremental/arm-linux/arithmetic_operations.0.zig @@ -0,0 +1,21 @@ +pub fn main() void { + print(2, 4); + print(1, 7); +} + +fn print(a: u32, b: u32) void { + asm volatile ("svc #0" + : + : [number] "{r7}" (4), + [arg3] "{r2}" (a + b), + [arg1] "{r0}" (1), + [arg2] "{r1}" (@ptrToInt("123456789")), + : "memory" + ); + return; +} + +// run +// target=arm-linux +// +// 12345612345678 diff --git a/test/incremental/arm-linux/arithmetic_operations.1.zig b/test/incremental/arm-linux/arithmetic_operations.1.zig new file mode 100644 index 0000000000..1e3793ed38 --- /dev/null +++ b/test/incremental/arm-linux/arithmetic_operations.1.zig @@ -0,0 +1,20 @@ +pub fn main() void { + print(10, 5); + print(4, 3); +} + +fn print(a: u32, b: u32) void { + asm volatile ("svc #0" + : + : [number] "{r7}" (4), + [arg3] "{r2}" (a - b), + [arg1] "{r0}" (1), + [arg2] "{r1}" (@ptrToInt("123456789")), + : "memory" + ); + return; +} + +// run +// +// 123451 diff --git a/test/incremental/arm-linux/arithmetic_operations.2.zig b/test/incremental/arm-linux/arithmetic_operations.2.zig new file mode 100644 index 0000000000..6b81e62198 --- /dev/null +++ b/test/incremental/arm-linux/arithmetic_operations.2.zig @@ -0,0 +1,20 @@ +pub fn main() void { + print(8, 9); + print(3, 7); +} + +fn print(a: u32, b: u32) void { + asm volatile ("svc #0" + : + : [number] "{r7}" (4), + [arg3] "{r2}" (a & b), + [arg1] "{r0}" (1), + [arg2] "{r1}" (@ptrToInt("123456789")), + : "memory" + ); + return; +} + +// run +// +// 12345678123 diff --git a/test/incremental/arm-linux/arithmetic_operations.3.zig b/test/incremental/arm-linux/arithmetic_operations.3.zig new file mode 100644 index 0000000000..23089127f5 --- /dev/null +++ b/test/incremental/arm-linux/arithmetic_operations.3.zig @@ -0,0 +1,20 @@ +pub fn main() void { + print(4, 2); + print(3, 7); +} + +fn print(a: u32, b: u32) void { + asm volatile ("svc #0" + : + : [number] "{r7}" (4), + [arg3] "{r2}" (a | b), + [arg1] "{r0}" (1), + [arg2] "{r1}" (@ptrToInt("123456789")), + : "memory" + ); + return; +} + +// run +// +// 1234561234567 diff --git a/test/incremental/arm-linux/arithmetic_operations.4.zig b/test/incremental/arm-linux/arithmetic_operations.4.zig new file mode 100644 index 0000000000..ce5133c465 --- /dev/null +++ b/test/incremental/arm-linux/arithmetic_operations.4.zig @@ -0,0 +1,20 @@ +pub fn main() void { + print(42, 42); + print(3, 5); +} + +fn print(a: u32, b: u32) void { + asm volatile ("svc #0" + : + : [number] "{r7}" (4), + [arg3] "{r2}" (a ^ b), + [arg1] "{r0}" (1), + [arg2] "{r1}" (@ptrToInt("123456789")), + : "memory" + ); + return; +} + +// run +// +// 123456 diff --git a/test/incremental/arm-linux/arithmetic_operations.5.zig b/test/incremental/arm-linux/arithmetic_operations.5.zig new file mode 100644 index 0000000000..b1c18ca7da --- /dev/null +++ b/test/incremental/arm-linux/arithmetic_operations.5.zig @@ -0,0 +1,15 @@ +pub fn main() void { + var x: u32 = 1; + assert(x << 1 == 2); + + x <<= 1; + assert(x << 2 == 8); + assert(x << 3 == 16); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/arm-linux/arithmetic_operations.6.zig b/test/incremental/arm-linux/arithmetic_operations.6.zig new file mode 100644 index 0000000000..e03731173c --- /dev/null +++ b/test/incremental/arm-linux/arithmetic_operations.6.zig @@ -0,0 +1,21 @@ +pub fn main() void { + var a: u32 = 1024; + assert(a >> 1 == 512); + + a >>= 1; + assert(a >> 2 == 128); + assert(a >> 3 == 64); + assert(a >> 4 == 32); + assert(a >> 5 == 16); + assert(a >> 6 == 8); + assert(a >> 7 == 4); + assert(a >> 8 == 2); + assert(a >> 9 == 1); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/arm-linux/errors.0.zig b/test/incremental/arm-linux/errors.0.zig new file mode 100644 index 0000000000..3e20d1d973 --- /dev/null +++ b/test/incremental/arm-linux/errors.0.zig @@ -0,0 +1,21 @@ +pub fn main() void { + foo() catch print(); +} + +fn foo() anyerror!void {} + +fn print() void { + asm volatile ("svc #0" + : + : [number] "{r7}" (4), + [arg1] "{r0}" (1), + [arg2] "{r1}" (@ptrToInt("Hello, World!\n")), + [arg3] "{r2}" ("Hello, World!\n".len), + : "memory" + ); + return; +} + +// run +// target=arm-linux +// diff --git a/test/incremental/arm-linux/errors.1.zig b/test/incremental/arm-linux/errors.1.zig new file mode 100644 index 0000000000..43fbb12675 --- /dev/null +++ b/test/incremental/arm-linux/errors.1.zig @@ -0,0 +1,24 @@ +pub fn main() void { + foo() catch print(); +} + +fn foo() anyerror!void { + return error.Test; +} + +fn print() void { + asm volatile ("svc #0" + : + : [number] "{r7}" (4), + [arg1] "{r0}" (1), + [arg2] "{r1}" (@ptrToInt("Hello, World!\n")), + [arg3] "{r2}" ("Hello, World!\n".len), + : "memory" + ); + return; +} + +// run +// +// Hello, World! +// diff --git a/test/incremental/arm-linux/errors.2.zig b/test/incremental/arm-linux/errors.2.zig new file mode 100644 index 0000000000..a1b9aef97d --- /dev/null +++ b/test/incremental/arm-linux/errors.2.zig @@ -0,0 +1,27 @@ +pub fn main() void { + foo() catch |err| { + assert(err == error.Foo); + assert(err != error.Bar); + assert(err != error.Baz); + }; + bar() catch |err| { + assert(err != error.Foo); + assert(err == error.Bar); + assert(err != error.Baz); + }; +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +fn foo() anyerror!void { + return error.Foo; +} + +fn bar() anyerror!void { + return error.Bar; +} + +// run +// diff --git a/test/incremental/arm-linux/errors.3.zig b/test/incremental/arm-linux/errors.3.zig new file mode 100644 index 0000000000..390696fcfc --- /dev/null +++ b/test/incremental/arm-linux/errors.3.zig @@ -0,0 +1,12 @@ +pub fn main() void { + foo() catch unreachable; +} + +fn foo() anyerror!void { + try bar(); +} + +fn bar() anyerror!void {} + +// run +// diff --git a/test/incremental/arm-linux/function_pointers.zig b/test/incremental/arm-linux/function_pointers.zig new file mode 100644 index 0000000000..0ef5df6745 --- /dev/null +++ b/test/incremental/arm-linux/function_pointers.zig @@ -0,0 +1,44 @@ +const PrintFn = *const fn () void; + +pub fn main() void { + var printFn: PrintFn = stopSayingThat; + var i: u32 = 0; + while (i < 4) : (i += 1) printFn(); + + printFn = moveEveryZig; + printFn(); +} + +fn stopSayingThat() void { + asm volatile ("svc #0" + : + : [number] "{r7}" (4), + [arg1] "{r0}" (1), + [arg2] "{r1}" (@ptrToInt("Hello, my name is Inigo Montoya; you killed my father, prepare to die.\n")), + [arg3] "{r2}" ("Hello, my name is Inigo Montoya; you killed my father, prepare to die.\n".len), + : "memory" + ); + return; +} + +fn moveEveryZig() void { + asm volatile ("svc #0" + : + : [number] "{r7}" (4), + [arg1] "{r0}" (1), + [arg2] "{r1}" (@ptrToInt("All your codebase are belong to us\n")), + [arg3] "{r2}" ("All your codebase are belong to us\n".len), + : "memory" + ); + return; +} + +// run +// target=arm-linux +// +// Hello, my name is Inigo Montoya; you killed my father, prepare to die. +// Hello, my name is Inigo Montoya; you killed my father, prepare to die. +// Hello, my name is Inigo Montoya; you killed my father, prepare to die. +// Hello, my name is Inigo Montoya; you killed my father, prepare to die. +// All your codebase are belong to us +// diff --git a/test/incremental/arm-linux/hello_world_with_updates.0.zig b/test/incremental/arm-linux/hello_world_with_updates.0.zig new file mode 100644 index 0000000000..4ffdc2ae0f --- /dev/null +++ b/test/incremental/arm-linux/hello_world_with_updates.0.zig @@ -0,0 +1,32 @@ +pub export fn _start() noreturn { + print(); + exit(); +} + +fn print() void { + asm volatile ("svc #0" + : + : [number] "{r7}" (4), + [arg1] "{r0}" (1), + [arg2] "{r1}" (@ptrToInt("Hello, World!\n")), + [arg3] "{r2}" (14), + : "memory" + ); + return; +} + +fn exit() noreturn { + asm volatile ("svc #0" + : + : [number] "{r7}" (1), + [arg1] "{r0}" (0), + : "memory" + ); + unreachable; +} + +// run +// target=arm-linux +// +// Hello, World! +// diff --git a/test/incremental/arm-linux/hello_world_with_updates.1.zig b/test/incremental/arm-linux/hello_world_with_updates.1.zig new file mode 100644 index 0000000000..8372d757ad --- /dev/null +++ b/test/incremental/arm-linux/hello_world_with_updates.1.zig @@ -0,0 +1,37 @@ +pub export fn _start() noreturn { + print(); + print(); + print(); + print(); + exit(); +} + +fn print() void { + asm volatile ("svc #0" + : + : [number] "{r7}" (4), + [arg1] "{r0}" (1), + [arg2] "{r1}" (@ptrToInt("Hello, World!\n")), + [arg3] "{r2}" (14), + : "memory" + ); + return; +} + +fn exit() noreturn { + asm volatile ("svc #0" + : + : [number] "{r7}" (1), + [arg1] "{r0}" (0), + : "memory" + ); + unreachable; +} + +// run +// +// Hello, World! +// Hello, World! +// Hello, World! +// Hello, World! +// diff --git a/test/incremental/arm-linux/hello_world_with_updates.2.zig b/test/incremental/arm-linux/hello_world_with_updates.2.zig new file mode 100644 index 0000000000..0b97bf2360 --- /dev/null +++ b/test/incremental/arm-linux/hello_world_with_updates.2.zig @@ -0,0 +1,22 @@ +pub fn main() void { + print(); + print(); +} + +fn print() void { + asm volatile ("svc #0" + : + : [number] "{r7}" (4), + [arg1] "{r0}" (1), + [arg2] "{r1}" (@ptrToInt("Hello, World!\n")), + [arg3] "{r2}" (14), + : "memory" + ); + return; +} + +// run +// +// Hello, World! +// Hello, World! +// diff --git a/test/incremental/arm-linux/parameters_and_return_values.0.zig b/test/incremental/arm-linux/parameters_and_return_values.0.zig new file mode 100644 index 0000000000..6a306d802c --- /dev/null +++ b/test/incremental/arm-linux/parameters_and_return_values.0.zig @@ -0,0 +1,28 @@ +pub fn main() void { + print(id(14)); +} + +fn id(x: u32) u32 { + return x; +} + +// TODO: The parameters to the asm statement in print() had to +// be in a specific order because otherwise the write to r0 +// would overwrite the len parameter which resides in r0 +fn print(len: u32) void { + asm volatile ("svc #0" + : + : [number] "{r7}" (4), + [arg3] "{r2}" (len), + [arg1] "{r0}" (1), + [arg2] "{r1}" (@ptrToInt("Hello, World!\n")), + : "memory" + ); + return; +} + +// run +// target=arm-linux +// +// Hello, World! +// diff --git a/test/incremental/arm-linux/parameters_and_return_values.1.zig b/test/incremental/arm-linux/parameters_and_return_values.1.zig new file mode 100644 index 0000000000..f2bc35d22e --- /dev/null +++ b/test/incremental/arm-linux/parameters_and_return_values.1.zig @@ -0,0 +1,14 @@ +pub fn main() void { + assert(add(1, 2, 3, 4, 5, 6) == 21); +} + +fn add(a: u32, b: u32, c: u32, d: u32, e: u32, f: u32) u32 { + return a + b + c + d + e + f; +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/arm-linux/print_u32s.zig b/test/incremental/arm-linux/print_u32s.zig new file mode 100644 index 0000000000..92fad75ddf --- /dev/null +++ b/test/incremental/arm-linux/print_u32s.zig @@ -0,0 +1,40 @@ +pub fn main() void { + printNumberHex(0x00000000); + printNumberHex(0xaaaaaaaa); + printNumberHex(0xdeadbeef); + printNumberHex(0x31415926); +} + +fn printNumberHex(x: u32) void { + var i: u5 = 28; + while (true) : (i -= 4) { + const digit = (x >> i) & 0xf; + asm volatile ("svc #0" + : + : [number] "{r7}" (4), + [arg1] "{r0}" (1), + [arg2] "{r1}" (@ptrToInt("0123456789abcdef") + digit), + [arg3] "{r2}" (1), + : "memory" + ); + + if (i == 0) break; + } + asm volatile ("svc #0" + : + : [number] "{r7}" (4), + [arg1] "{r0}" (1), + [arg2] "{r1}" (@ptrToInt("\n")), + [arg3] "{r2}" (1), + : "memory" + ); +} + +// run +// target=arm-linux +// +// 00000000 +// aaaaaaaa +// deadbeef +// 31415926 +// diff --git a/test/incremental/arm-linux/spilling_registers.0.zig b/test/incremental/arm-linux/spilling_registers.0.zig new file mode 100644 index 0000000000..ac74e071ad --- /dev/null +++ b/test/incremental/arm-linux/spilling_registers.0.zig @@ -0,0 +1,38 @@ +pub fn main() void { + assert(add(3, 4) == 791); +} + +fn add(a: u32, b: u32) u32 { + const x: u32 = blk: { + const c = a + b; // 7 + const d = a + c; // 10 + const e = d + b; // 14 + const f = d + e; // 24 + const g = e + f; // 38 + const h = f + g; // 62 + const i = g + h; // 100 + const j = i + d; // 110 + const k = i + j; // 210 + const l = k + c; // 217 + const m = l + d; // 227 + const n = m + e; // 241 + const o = n + f; // 265 + const p = o + g; // 303 + const q = p + h; // 365 + const r = q + i; // 465 + const s = r + j; // 575 + const t = s + k; // 785 + break :blk t; + }; + const y = x + a; // 788 + const z = y + a; // 791 + return z; +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// run +// target=arm-linux +// diff --git a/test/incremental/arm-linux/spilling_registers.1.zig b/test/incremental/arm-linux/spilling_registers.1.zig new file mode 100644 index 0000000000..f401a5d7b7 --- /dev/null +++ b/test/incremental/arm-linux/spilling_registers.1.zig @@ -0,0 +1,37 @@ +pub fn main() void { + assert(addMul(3, 4) == 357747496); +} + +fn addMul(a: u32, b: u32) u32 { + const x: u32 = blk: { + const c = a + b; // 7 + const d = a + c; // 10 + const e = d + b; // 14 + const f = d + e; // 24 + const g = e + f; // 38 + const h = f + g; // 62 + const i = g + h; // 100 + const j = i + d; // 110 + const k = i + j; // 210 + const l = k + c; // 217 + const m = l * d; // 2170 + const n = m + e; // 2184 + const o = n * f; // 52416 + const p = o + g; // 52454 + const q = p * h; // 3252148 + const r = q + i; // 3252248 + const s = r * j; // 357747280 + const t = s + k; // 357747490 + break :blk t; + }; + const y = x + a; // 357747493 + const z = y + a; // 357747496 + return z; +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// run +// diff --git a/test/incremental/non_leaf_functions.zig b/test/incremental/non_leaf_functions.zig new file mode 100644 index 0000000000..cb1737130e --- /dev/null +++ b/test/incremental/non_leaf_functions.zig @@ -0,0 +1,12 @@ +pub fn main() void { + foo(); +} + +fn foo() void { + bar(); +} + +fn bar() void {} + +// run +// diff --git a/test/incremental/recursive_fibonacci.zig b/test/incremental/recursive_fibonacci.zig new file mode 100644 index 0000000000..4e284e3fc1 --- /dev/null +++ b/test/incremental/recursive_fibonacci.zig @@ -0,0 +1,24 @@ +pub fn main() void { + assert(fib(0) == 0); + assert(fib(1) == 1); + assert(fib(2) == 1); + assert(fib(3) == 2); + assert(fib(10) == 55); + assert(fib(20) == 6765); +} + +fn fib(n: u32) u32 { + if (n < 2) { + return n; + } else { + return fib(n - 2) + fib(n - 1); + } +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// run +// target=arm-linux,x86_64-linux,x86_64-macos,wasm32-wasi +// diff --git a/test/incremental/save_function_return_values_in_callee_preserved_register.zig b/test/incremental/save_function_return_values_in_callee_preserved_register.zig new file mode 100644 index 0000000000..a201903808 --- /dev/null +++ b/test/incremental/save_function_return_values_in_callee_preserved_register.zig @@ -0,0 +1,22 @@ +pub fn main() void { + assert(foo() == 43); +} + +fn foo() u32 { + return bar() + baz(42); +} + +fn bar() u32 { + return 1; +} + +fn baz(x: u32) u32 { + return x; +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// run +// diff --git a/test/stage2/arm.zig b/test/stage2/arm.zig deleted file mode 100644 index a0afcf9d6f..0000000000 --- a/test/stage2/arm.zig +++ /dev/null @@ -1,798 +0,0 @@ -const std = @import("std"); -const TestContext = @import("../../src/test.zig").TestContext; - -const linux_arm = std.zig.CrossTarget{ - .cpu_arch = .arm, - .os_tag = .linux, -}; - -pub fn addCases(ctx: *TestContext) !void { - { - var case = ctx.exe("linux_arm hello world", linux_arm); - // Hello world using _start and inline asm. - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ print(); - \\ exit(); - \\} - \\ - \\fn print() void { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{r7}" (4), - \\ [arg1] "{r0}" (1), - \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{r2}" (14) - \\ : "memory" - \\ ); - \\ return; - \\} - \\ - \\fn exit() noreturn { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{r7}" (1), - \\ [arg1] "{r0}" (0) - \\ : "memory" - \\ ); - \\ unreachable; - \\} - , - "Hello, World!\n", - ); - } - - { - var case = ctx.exe("parameters and return values", linux_arm); - // Testing simple parameters and return values - // - // TODO: The parameters to the asm statement in print() had to - // be in a specific order because otherwise the write to r0 - // would overwrite the len parameter which resides in r0 - case.addCompareOutput( - \\pub fn main() void { - \\ print(id(14)); - \\} - \\ - \\fn id(x: u32) u32 { - \\ return x; - \\} - \\ - \\fn print(len: u32) void { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{r7}" (4), - \\ [arg3] "{r2}" (len), - \\ [arg1] "{r0}" (1), - \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")) - \\ : "memory" - \\ ); - \\ return; - \\} - , - "Hello, World!\n", - ); - - case.addCompareOutput( - \\pub fn main() void { - \\ assert(add(1, 2, 3, 4, 5, 6) == 21); - \\} - \\ - \\fn add(a: u32, b: u32, c: u32, d: u32, e: u32, f: u32) u32 { - \\ return a + b + c + d + e + f; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - } - - { - var case = ctx.exe("non-leaf functions", linux_arm); - // Testing non-leaf functions - case.addCompareOutput( - \\pub fn main() void { - \\ foo(); - \\} - \\ - \\fn foo() void { - \\ bar(); - \\} - \\ - \\fn bar() void {} - , - "", - ); - } - - { - var case = ctx.exe("arithmetic operations", linux_arm); - - // Add two numbers - case.addCompareOutput( - \\pub fn main() void { - \\ print(2, 4); - \\ print(1, 7); - \\} - \\ - \\fn print(a: u32, b: u32) void { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{r7}" (4), - \\ [arg3] "{r2}" (a + b), - \\ [arg1] "{r0}" (1), - \\ [arg2] "{r1}" (@ptrToInt("123456789")) - \\ : "memory" - \\ ); - \\ return; - \\} - , - "12345612345678", - ); - - // Subtract two numbers - case.addCompareOutput( - \\pub fn main() void { - \\ print(10, 5); - \\ print(4, 3); - \\} - \\ - \\fn print(a: u32, b: u32) void { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{r7}" (4), - \\ [arg3] "{r2}" (a - b), - \\ [arg1] "{r0}" (1), - \\ [arg2] "{r1}" (@ptrToInt("123456789")) - \\ : "memory" - \\ ); - \\ return; - \\} - , - "123451", - ); - - // Bitwise And - case.addCompareOutput( - \\pub fn main() void { - \\ print(8, 9); - \\ print(3, 7); - \\} - \\ - \\fn print(a: u32, b: u32) void { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{r7}" (4), - \\ [arg3] "{r2}" (a & b), - \\ [arg1] "{r0}" (1), - \\ [arg2] "{r1}" (@ptrToInt("123456789")) - \\ : "memory" - \\ ); - \\ return; - \\} - , - "12345678123", - ); - - // Bitwise Or - case.addCompareOutput( - \\pub fn main() void { - \\ print(4, 2); - \\ print(3, 7); - \\} - \\ - \\fn print(a: u32, b: u32) void { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{r7}" (4), - \\ [arg3] "{r2}" (a | b), - \\ [arg1] "{r0}" (1), - \\ [arg2] "{r1}" (@ptrToInt("123456789")) - \\ : "memory" - \\ ); - \\ return; - \\} - , - "1234561234567", - ); - - // Bitwise Xor - case.addCompareOutput( - \\pub fn main() void { - \\ print(42, 42); - \\ print(3, 5); - \\} - \\ - \\fn print(a: u32, b: u32) void { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{r7}" (4), - \\ [arg3] "{r2}" (a ^ b), - \\ [arg1] "{r0}" (1), - \\ [arg2] "{r1}" (@ptrToInt("123456789")) - \\ : "memory" - \\ ); - \\ return; - \\} - , - "123456", - ); - - // Bit Shift Left - case.addCompareOutput( - \\pub fn main() void { - \\ var x: u32 = 1; - \\ assert(x << 1 == 2); - \\ - \\ x <<= 1; - \\ assert(x << 2 == 8); - \\ assert(x << 3 == 16); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - // Bit Shift Right - case.addCompareOutput( - \\pub fn main() void { - \\ var a: u32 = 1024; - \\ assert(a >> 1 == 512); - \\ - \\ a >>= 1; - \\ assert(a >> 2 == 128); - \\ assert(a >> 3 == 64); - \\ assert(a >> 4 == 32); - \\ assert(a >> 5 == 16); - \\ assert(a >> 6 == 8); - \\ assert(a >> 7 == 4); - \\ assert(a >> 8 == 2); - \\ assert(a >> 9 == 1); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - } - - { - var case = ctx.exe("if statements", linux_arm); - // Simple if statement in assert - case.addCompareOutput( - \\pub fn main() void { - \\ var x: u32 = 123; - \\ var y: u32 = 42; - \\ assert(x > y); - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , - "", - ); - } - - { - var case = ctx.exe("while loops", linux_arm); - // Simple while loop with assert - case.addCompareOutput( - \\pub fn main() void { - \\ var x: u32 = 2020; - \\ var i: u32 = 0; - \\ while (x > 0) { - \\ x -= 2; - \\ i += 1; - \\ } - \\ assert(i == 1010); - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , - "", - ); - } - - { - var case = ctx.exe("integer multiplication", linux_arm); - // Simple u32 integer multiplication - case.addCompareOutput( - \\pub fn main() void { - \\ assert(mul(1, 1) == 1); - \\ assert(mul(42, 1) == 42); - \\ assert(mul(1, 42) == 42); - \\ assert(mul(123, 42) == 5166); - \\} - \\ - \\fn mul(x: u32, y: u32) u32 { - \\ return x * y; - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , - "", - ); - } - - { - var case = ctx.exe("save function return values in callee preserved register", linux_arm); - // Here, it is necessary to save the result of bar() into a - // callee preserved register, otherwise it will be overwritten - // by the first parameter to baz. - case.addCompareOutput( - \\pub fn main() void { - \\ assert(foo() == 43); - \\} - \\ - \\fn foo() u32 { - \\ return bar() + baz(42); - \\} - \\ - \\fn bar() u32 { - \\ return 1; - \\} - \\ - \\fn baz(x: u32) u32 { - \\ return x; - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , - "", - ); - } - - { - var case = ctx.exe("enums", linux_arm); - case.addCompareOutput( - \\const Number = enum { one, two, three }; - \\ - \\pub fn main() void { - \\ var x: Number = .one; - \\ var y = Number.two; - \\ var z = @intToEnum(Number, 2); - \\ assert(@enumToInt(x) == 0); - \\ assert(@enumToInt(y) == 1); - \\ assert(@enumToInt(z) == 2); - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - - case.addCompareOutput( - \\const Number = enum { one, two, three }; - \\ - \\pub fn main() void { - \\ var x: Number = .one; - \\ var y = Number.two; - \\ assert(@enumToInt(x) < @enumToInt(y)); - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - } - - { - var case = ctx.exe("recursive fibonacci", linux_arm); - case.addCompareOutput( - \\pub fn main() void { - \\ assert(fib(0) == 0); - \\ assert(fib(1) == 1); - \\ assert(fib(2) == 1); - \\ assert(fib(3) == 2); - \\ assert(fib(10) == 55); - \\ assert(fib(20) == 6765); - \\} - \\ - \\fn fib(n: u32) u32 { - \\ if (n < 2) { - \\ return n; - \\ } else { - \\ return fib(n - 2) + fib(n - 1); - \\ } - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , - "", - ); - } - - { - var case = ctx.exe("spilling registers", linux_arm); - case.addCompareOutput( - \\pub fn main() void { - \\ assert(add(3, 4) == 791); - \\} - \\ - \\fn add(a: u32, b: u32) u32 { - \\ const x: u32 = blk: { - \\ const c = a + b; // 7 - \\ const d = a + c; // 10 - \\ const e = d + b; // 14 - \\ const f = d + e; // 24 - \\ const g = e + f; // 38 - \\ const h = f + g; // 62 - \\ const i = g + h; // 100 - \\ const j = i + d; // 110 - \\ const k = i + j; // 210 - \\ const l = k + c; // 217 - \\ const m = l + d; // 227 - \\ const n = m + e; // 241 - \\ const o = n + f; // 265 - \\ const p = o + g; // 303 - \\ const q = p + h; // 365 - \\ const r = q + i; // 465 - \\ const s = r + j; // 575 - \\ const t = s + k; // 785 - \\ break :blk t; - \\ }; - \\ const y = x + a; // 788 - \\ const z = y + a; // 791 - \\ return z; - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , - "", - ); - - case.addCompareOutput( - \\pub fn main() void { - \\ assert(addMul(3, 4) == 357747496); - \\} - \\ - \\fn addMul(a: u32, b: u32) u32 { - \\ const x: u32 = blk: { - \\ const c = a + b; // 7 - \\ const d = a + c; // 10 - \\ const e = d + b; // 14 - \\ const f = d + e; // 24 - \\ const g = e + f; // 38 - \\ const h = f + g; // 62 - \\ const i = g + h; // 100 - \\ const j = i + d; // 110 - \\ const k = i + j; // 210 - \\ const l = k + c; // 217 - \\ const m = l * d; // 2170 - \\ const n = m + e; // 2184 - \\ const o = n * f; // 52416 - \\ const p = o + g; // 52454 - \\ const q = p * h; // 3252148 - \\ const r = q + i; // 3252248 - \\ const s = r * j; // 357747280 - \\ const t = s + k; // 357747490 - \\ break :blk t; - \\ }; - \\ const y = x + a; // 357747493 - \\ const z = y + a; // 357747496 - \\ return z; - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , - "", - ); - } - - { - var case = ctx.exe("print u32s", linux_arm); - case.addCompareOutput( - \\pub fn main() void { - \\ printNumberHex(0x00000000); - \\ printNumberHex(0xaaaaaaaa); - \\ printNumberHex(0xdeadbeef); - \\ printNumberHex(0x31415926); - \\} - \\ - \\fn printNumberHex(x: u32) void { - \\ var i: u5 = 28; - \\ while (true) : (i -= 4) { - \\ const digit = (x >> i) & 0xf; - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{r7}" (4), - \\ [arg1] "{r0}" (1), - \\ [arg2] "{r1}" (@ptrToInt("0123456789abcdef") + digit), - \\ [arg3] "{r2}" (1) - \\ : "memory" - \\ ); - \\ - \\ if (i == 0) break; - \\ } - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{r7}" (4), - \\ [arg1] "{r0}" (1), - \\ [arg2] "{r1}" (@ptrToInt("\n")), - \\ [arg3] "{r2}" (1) - \\ : "memory" - \\ ); - \\} - , - \\00000000 - \\aaaaaaaa - \\deadbeef - \\31415926 - \\ - , - ); - } - - { - var case = ctx.exe("save compare flags", linux_arm); - case.addCompareOutput( - \\pub fn main() void { - \\ foo(2, 1); - \\} - \\ - \\fn foo(x: u32, y: u32) void { - \\ const b = x > y; - \\ assert(b); - \\ assert(b); - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - , - "", - ); - } - - { - var case = ctx.exe("optionals", linux_arm); - case.addCompareOutput( - \\var x: u32 = 42; - \\ - \\pub fn main() void { - \\ var p: ?*u32 = null; - \\ assert(p == null); - \\ p = &x; - \\ assert(p != null); - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , - "", - ); - } - - { - var case = ctx.exe("errors", linux_arm); - case.addCompareOutput( - \\pub fn main() void { - \\ foo() catch print(); - \\} - \\ - \\fn foo() anyerror!void {} - \\ - \\fn print() void { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{r7}" (4), - \\ [arg1] "{r0}" (1), - \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{r2}" ("Hello, World!\n".len), - \\ : "memory" - \\ ); - \\ return; - \\} - , - "", - ); - - case.addCompareOutput( - \\pub fn main() void { - \\ foo() catch print(); - \\} - \\ - \\fn foo() anyerror!void { - \\ return error.Test; - \\} - \\ - \\fn print() void { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{r7}" (4), - \\ [arg1] "{r0}" (1), - \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{r2}" ("Hello, World!\n".len), - \\ : "memory" - \\ ); - \\ return; - \\} - , - "Hello, World!\n", - ); - - case.addCompareOutput( - \\pub fn main() void { - \\ foo() catch |err| { - \\ assert(err == error.Foo); - \\ assert(err != error.Bar); - \\ assert(err != error.Baz); - \\ }; - \\ bar() catch |err| { - \\ assert(err != error.Foo); - \\ assert(err == error.Bar); - \\ assert(err != error.Baz); - \\ }; - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - \\ - \\fn foo() anyerror!void { - \\ return error.Foo; - \\} - \\ - \\fn bar() anyerror!void { - \\ return error.Bar; - \\} - , - "", - ); - - case.addCompareOutput( - \\pub fn main() void { - \\ foo() catch unreachable; - \\} - \\ - \\fn foo() anyerror!void { - \\ try bar(); - \\} - \\ - \\fn bar() anyerror!void {} - , - "", - ); - } - - { - var case = ctx.exe("slices", linux_arm); - case.addCompareOutput( - \\var array = [_]u32{ 0, 42, 123, 69 }; - \\var s: []const u32 = &array; - \\ - \\pub fn main() void { - \\ assert(s[0] == 0); - \\ assert(s[1] == 42); - \\ assert(s[2] == 123); - \\ assert(s[3] == 69); - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , - "", - ); - } - - { - var case = ctx.exe("structs", linux_arm); - case.addCompareOutput( - \\var array = [_]SomeStruct{ - \\ .{ .a = 0, .b = 42, .c = 69 }, - \\ .{ .a = 1, .b = 2, .c = 3 }, - \\ .{ .a = 123, .b = 456, .c = 789 }, - \\}; - \\var s: []const SomeStruct = &array; - \\ - \\var some_struct: SomeStruct = .{ - \\ .a = 0, - \\ .b = 42, - \\ .c = 69, - \\}; - \\ - \\const SomeStruct = struct { - \\ a: u32, - \\ b: u32, - \\ c: u32, - \\}; - \\ - \\pub fn main() void { - \\ assert(some_struct.a == 0); - \\ assert(some_struct.b == 42); - \\ assert(some_struct.c == 69); - \\ - \\ assert(s[0].a == 0); - \\ assert(s[0].b == 42); - \\ assert(s[0].c == 69); - \\ assert(s[1].a == 1); - \\ assert(s[1].b == 2); - \\ assert(s[1].c == 3); - \\ assert(s[2].a == 123); - \\ assert(s[2].b == 456); - \\ assert(s[2].c == 789); - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - , - "", - ); - } - - { - var case = ctx.exe("function pointers", linux_arm); - case.addCompareOutput( - \\const PrintFn = *const fn () void; - \\ - \\pub fn main() void { - \\ var printFn: PrintFn = stopSayingThat; - \\ var i: u32 = 0; - \\ while (i < 4) : (i += 1) printFn(); - \\ - \\ printFn = moveEveryZig; - \\ printFn(); - \\} - \\ - \\fn stopSayingThat() void { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{r7}" (4), - \\ [arg1] "{r0}" (1), - \\ [arg2] "{r1}" (@ptrToInt("Hello, my name is Inigo Montoya; you killed my father, prepare to die.\n")), - \\ [arg3] "{r2}" ("Hello, my name is Inigo Montoya; you killed my father, prepare to die.\n".len), - \\ : "memory" - \\ ); - \\ return; - \\} - \\ - \\fn moveEveryZig() void { - \\ asm volatile ("svc #0" - \\ : - \\ : [number] "{r7}" (4), - \\ [arg1] "{r0}" (1), - \\ [arg2] "{r1}" (@ptrToInt("All your codebase are belong to us\n")), - \\ [arg3] "{r2}" ("All your codebase are belong to us\n".len), - \\ : "memory" - \\ ); - \\ return; - \\} - , - \\Hello, my name is Inigo Montoya; you killed my father, prepare to die. - \\Hello, my name is Inigo Montoya; you killed my father, prepare to die. - \\Hello, my name is Inigo Montoya; you killed my father, prepare to die. - \\Hello, my name is Inigo Montoya; you killed my father, prepare to die. - \\All your codebase are belong to us - \\ - , - ); - } -} From 495bb12e6a5b5783fa14cc08c2c940ee60b80607 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 28 Apr 2022 00:26:30 +0200 Subject: [PATCH 1287/2031] test: migrate plan9 and sparcv9 incremental tests --- test/cases.zig | 2 - test/incremental/plan9/exit.zig | 5 ++ .../plan9/hello_world_with_updates.0.zig | 28 ++++++++++ .../plan9/hello_world_with_updates.1.zig | 10 ++++ .../incremental/sparcv9-linux/hello_world.zig | 27 +++++++++ test/stage2/plan9.zig | 55 ------------------- test/stage2/sparcv9.zig | 39 ------------- 7 files changed, 70 insertions(+), 96 deletions(-) create mode 100644 test/incremental/plan9/exit.zig create mode 100644 test/incremental/plan9/hello_world_with_updates.0.zig create mode 100644 test/incremental/plan9/hello_world_with_updates.1.zig create mode 100644 test/incremental/sparcv9-linux/hello_world.zig delete mode 100644 test/stage2/plan9.zig delete mode 100644 test/stage2/sparcv9.zig diff --git a/test/cases.zig b/test/cases.zig index f90233df0f..2d4da15789 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -10,9 +10,7 @@ pub fn addCases(ctx: *TestContext) !void { try @import("compile_errors.zig").addCases(ctx); try @import("stage2/cbe.zig").addCases(ctx); try @import("stage2/llvm.zig").addCases(ctx); - try @import("stage2/plan9.zig").addCases(ctx); try @import("stage2/x86_64.zig").addCases(ctx); - try @import("stage2/sparcv9.zig").addCases(ctx); // https://github.com/ziglang/zig/issues/10968 //try @import("stage2/nvptx.zig").addCases(ctx); } diff --git a/test/incremental/plan9/exit.zig b/test/incremental/plan9/exit.zig new file mode 100644 index 0000000000..735818fb83 --- /dev/null +++ b/test/incremental/plan9/exit.zig @@ -0,0 +1,5 @@ +pub fn main() void {} + +// run +// target=x86_64-plan9,aarch64-plan9 +// diff --git a/test/incremental/plan9/hello_world_with_updates.0.zig b/test/incremental/plan9/hello_world_with_updates.0.zig new file mode 100644 index 0000000000..7e7c373251 --- /dev/null +++ b/test/incremental/plan9/hello_world_with_updates.0.zig @@ -0,0 +1,28 @@ +pub fn main() void { + const str = "Hello World!\n"; + asm volatile ( + \\push $0 + \\push %%r10 + \\push %%r11 + \\push $1 + \\push $0 + \\syscall + \\pop %%r11 + \\pop %%r11 + \\pop %%r11 + \\pop %%r11 + \\pop %%r11 + : + // pwrite + : [syscall_number] "{rbp}" (51), + [hey] "{r11}" (@ptrToInt(str)), + [strlen] "{r10}" (str.len), + : "rcx", "rbp", "r11", "memory" + ); +} + +// run +// target=x86_64-plan9 +// +// Hello World +// diff --git a/test/incremental/plan9/hello_world_with_updates.1.zig b/test/incremental/plan9/hello_world_with_updates.1.zig new file mode 100644 index 0000000000..4111a8dc08 --- /dev/null +++ b/test/incremental/plan9/hello_world_with_updates.1.zig @@ -0,0 +1,10 @@ +const std = @import("std"); +pub fn main() void { + const str = "Hello World!\n"; + _ = std.os.plan9.pwrite(1, str, str.len, 0); +} + +// run +// +// Hello World +// diff --git a/test/incremental/sparcv9-linux/hello_world.zig b/test/incremental/sparcv9-linux/hello_world.zig new file mode 100644 index 0000000000..327a8c56ad --- /dev/null +++ b/test/incremental/sparcv9-linux/hello_world.zig @@ -0,0 +1,27 @@ +const msg = "Hello, World!\n"; + +pub export fn _start() noreturn { + asm volatile ("ta 0x6d" + : + : [number] "{g1}" (4), + [arg1] "{o0}" (1), + [arg2] "{o1}" (@ptrToInt(msg)), + [arg3] "{o2}" (msg.len), + : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory" + ); + + asm volatile ("ta 0x6d" + : + : [number] "{g1}" (1), + [arg1] "{o0}" (0), + : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory" + ); + + unreachable; +} + +// run +// target=sparcv9-linux +// +// Hello, World! +// diff --git a/test/stage2/plan9.zig b/test/stage2/plan9.zig deleted file mode 100644 index c82f95aa7b..0000000000 --- a/test/stage2/plan9.zig +++ /dev/null @@ -1,55 +0,0 @@ -const std = @import("std"); -const TestContext = @import("../../src/test.zig").TestContext; - -pub fn addCases(ctx: *TestContext) !void { - const x64: std.zig.CrossTarget = .{ - .cpu_arch = .x86_64, - .os_tag = .plan9, - }; - const aarch64: std.zig.CrossTarget = .{ - .cpu_arch = .aarch64, - .os_tag = .plan9, - }; - { - var case = ctx.exe("plan9: exiting correctly x64", x64); - case.addCompareOutput("pub fn main() void {}", ""); - } - { - var case = ctx.exe("plan9: exiting correctly arm", aarch64); - case.addCompareOutput("pub fn main() void {}", ""); - } - { - var case = ctx.exe("plan9: hello world", x64); - case.addCompareOutput( - \\pub fn main() void { - \\ const str = "Hello World!\n"; - \\ asm volatile ( - \\ \\push $0 - \\ \\push %%r10 - \\ \\push %%r11 - \\ \\push $1 - \\ \\push $0 - \\ \\syscall - \\ \\pop %%r11 - \\ \\pop %%r11 - \\ \\pop %%r11 - \\ \\pop %%r11 - \\ \\pop %%r11 - \\ : - \\ // pwrite - \\ : [syscall_number] "{rbp}" (51), - \\ [hey] "{r11}" (@ptrToInt(str)), - \\ [strlen] "{r10}" (str.len) - \\ : "rcx", "rbp", "r11", "memory" - \\ ); - \\} - , "Hello World\n"); - case.addCompareOutput( - \\const std = @import("std"); - \\pub fn main() void { - \\ const str = "Hello World!\n"; - \\ _ = std.os.plan9.pwrite(1, str, str.len, 0); - \\} - , "Hello World\n"); - } -} diff --git a/test/stage2/sparcv9.zig b/test/stage2/sparcv9.zig deleted file mode 100644 index d5611a7fae..0000000000 --- a/test/stage2/sparcv9.zig +++ /dev/null @@ -1,39 +0,0 @@ -const std = @import("std"); -const TestContext = @import("../../src/test.zig").TestContext; - -const linux_sparcv9 = std.zig.CrossTarget{ - .cpu_arch = .sparcv9, - .os_tag = .linux, -}; - -pub fn addCases(ctx: *TestContext) !void { - { - var case = ctx.exe("sparcv9 hello world", linux_sparcv9); - // Regular old hello world - case.addCompareOutput( - \\const msg = "Hello, World!\n"; - \\ - \\pub export fn _start() noreturn { - \\ asm volatile ("ta 0x6d" - \\ : - \\ : [number] "{g1}" (4), - \\ [arg1] "{o0}" (1), - \\ [arg2] "{o1}" (@ptrToInt(msg)), - \\ [arg3] "{o2}" (msg.len) - \\ : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory" - \\ ); - \\ - \\ asm volatile ("ta 0x6d" - \\ : - \\ : [number] "{g1}" (1), - \\ [arg1] "{o0}" (0) - \\ : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory" - \\ ); - \\ - \\ unreachable; - \\} - , - "Hello, World!\n", - ); - } -} From 2cd456f8f451bc73d719381bcb0f3242a1de2e04 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 28 Apr 2022 00:54:20 +0200 Subject: [PATCH 1288/2031] test: correctly handle multiple backends To correctly handle multiple backends crossed with multiple targets, we need to push all elements in separate allocated arrays rather than operate on raw iterators. Hence, introduce `getConfigForKeyAlloc`. --- src/test.zig | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/test.zig b/src/test.zig index 1bb3aaa4a1..0377d8823f 100644 --- a/src/test.zig +++ b/src/test.zig @@ -347,6 +347,21 @@ const TestManifest = struct { return self.getConfigForKeyCustomParser(key, T, getDefaultParser(T)); } + fn getConfigForKeyAlloc( + self: TestManifest, + allocator: Allocator, + key: []const u8, + comptime T: type, + ) error{OutOfMemory}![]const T { + var out = std.ArrayList(T).init(allocator); + defer out.deinit(); + var it = self.getConfigForKey(key, T); + while (it.next()) |item| { + try out.append(item); + } + return out.toOwnedSlice(); + } + fn getConfigForKeyAssertSingle(self: TestManifest, key: []const u8, comptime T: type) T { var it = self.getConfigForKey(key, T); const res = it.next().?; @@ -360,8 +375,9 @@ const TestManifest = struct { }; } - fn trailingAlloc(self: TestManifest, arena: Allocator) ![]const []const u8 { - var out = std.ArrayList([]const u8).init(arena); + fn trailingAlloc(self: TestManifest, allocator: Allocator) error{OutOfMemory}![]const []const u8 { + var out = std.ArrayList([]const u8).init(allocator); + defer out.deinit(); var it = self.trailing(); while (it.next()) |line| { try out.append(line); @@ -1068,8 +1084,8 @@ pub const TestContext = struct { var manifest = try TestManifest.parse(ctx.arena, src); if (cases.items.len == 0) { - var backends = manifest.getConfigForKey("backend", Backend); - var targets = manifest.getConfigForKey("target", CrossTarget); + const backends = try manifest.getConfigForKeyAlloc(ctx.arena, "backend", Backend); + const targets = try manifest.getConfigForKeyAlloc(ctx.arena, "target", CrossTarget); const is_test = manifest.getConfigForKeyAssertSingle("is_test", bool); const output_mode = manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode); @@ -1081,10 +1097,11 @@ pub const TestContext = struct { }; // Cross-product to get all possible test combinations - while (backends.next()) |backend| { - while (targets.next()) |target| { - const name = try std.fmt.allocPrint(ctx.arena, "{s} ({s})", .{ + for (backends) |backend| { + for (targets) |target| { + const name = try std.fmt.allocPrint(ctx.arena, "{s} ({s}, {s})", .{ name_prefix, + @tagName(backend), try target.zigTriple(ctx.arena), }); const next = ctx.cases.items.len; @@ -1095,6 +1112,7 @@ pub const TestContext = struct { .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), .is_test = is_test, .output_mode = output_mode, + .link_libc = backend == .llvm, .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), }); try cases.append(next); From 5a5648c0f03058e1d3878cc8c072af968fc90aa8 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 28 Apr 2022 10:46:05 +0200 Subject: [PATCH 1289/2031] test: migrate llvm incremental tests --- test/cases.zig | 2 - ...ess_chaining_pointer_to_optional_array.zig | 12 + ..._pointer_access_chaining_array_pointer.zig | 12 + ...spaces_pointer_access_chaining_complex.zig | 13 + ...pointer_access_chaining_struct_pointer.zig | 13 + .../any_typed_null_to_any_typed_optional.zig | 11 + test/incremental/llvm/blocks.zig | 22 + ..._multiple_pointers_with_address_spaces.zig | 12 + ...ment_address_space_reading_and_writing.zig | 48 ++ test/incremental/llvm/for_loop.zig | 16 + test/incremental/llvm/hello_world.zig | 12 + .../llvm/invalid_address_space_coercion.zig | 13 + ...ace_when_taking_address_of_dereference.zig | 13 + test/incremental/llvm/nested_blocks.zig | 24 + test/incremental/llvm/optionals.zig | 45 ++ .../llvm/pointer_keeps_address_space.zig | 12 + ...ace_when_taking_address_of_dereference.zig | 12 + ...ress_space_coerces_to_implicit_pointer.zig | 12 + .../pointer_with_different_address_spaces.zig | 13 + ...pointers_with_different_address_spaces.zig | 13 + test/incremental/llvm/rem.zig | 15 + .../llvm/shift_right_plus_left.0.zig | 12 + .../llvm/shift_right_plus_left.1.zig | 10 + .../llvm/simple_addition_and_subtraction.zig | 20 + test/incremental/llvm/simple_if_statement.zig | 16 + test/incremental/llvm/while_loops.zig | 18 + test/stage2/llvm.zig | 438 ------------------ test/stage2/x86_64.zig | 59 --- 28 files changed, 419 insertions(+), 499 deletions(-) create mode 100644 test/incremental/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig create mode 100644 test/incremental/llvm/address_spaces_pointer_access_chaining_array_pointer.zig create mode 100644 test/incremental/llvm/address_spaces_pointer_access_chaining_complex.zig create mode 100644 test/incremental/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig create mode 100644 test/incremental/llvm/any_typed_null_to_any_typed_optional.zig create mode 100644 test/incremental/llvm/blocks.zig create mode 100644 test/incremental/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig create mode 100644 test/incremental/llvm/f_segment_address_space_reading_and_writing.zig create mode 100644 test/incremental/llvm/for_loop.zig create mode 100644 test/incremental/llvm/hello_world.zig create mode 100644 test/incremental/llvm/invalid_address_space_coercion.zig create mode 100644 test/incremental/llvm/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig create mode 100644 test/incremental/llvm/nested_blocks.zig create mode 100644 test/incremental/llvm/optionals.zig create mode 100644 test/incremental/llvm/pointer_keeps_address_space.zig create mode 100644 test/incremental/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig create mode 100644 test/incremental/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig create mode 100644 test/incremental/llvm/pointer_with_different_address_spaces.zig create mode 100644 test/incremental/llvm/pointers_with_different_address_spaces.zig create mode 100644 test/incremental/llvm/rem.zig create mode 100644 test/incremental/llvm/shift_right_plus_left.0.zig create mode 100644 test/incremental/llvm/shift_right_plus_left.1.zig create mode 100644 test/incremental/llvm/simple_addition_and_subtraction.zig create mode 100644 test/incremental/llvm/simple_if_statement.zig create mode 100644 test/incremental/llvm/while_loops.zig delete mode 100644 test/stage2/llvm.zig delete mode 100644 test/stage2/x86_64.zig diff --git a/test/cases.zig b/test/cases.zig index 2d4da15789..3e340dcddb 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -9,8 +9,6 @@ const TestContext = @import("../src/test.zig").TestContext; pub fn addCases(ctx: *TestContext) !void { try @import("compile_errors.zig").addCases(ctx); try @import("stage2/cbe.zig").addCases(ctx); - try @import("stage2/llvm.zig").addCases(ctx); - try @import("stage2/x86_64.zig").addCases(ctx); // https://github.com/ziglang/zig/issues/10968 //try @import("stage2/nvptx.zig").addCases(ctx); } diff --git a/test/incremental/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig b/test/incremental/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig new file mode 100644 index 0000000000..592c82691d --- /dev/null +++ b/test/incremental/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig @@ -0,0 +1,12 @@ +fn entry(a: *addrspace(.gs) ?[1]i32) *addrspace(.gs) i32 { + return &a.*.?[0]; +} +pub fn main() void { + _ = entry; +} + +// error +// output_mode=Exe +// backend=llvm +// target=x86_64-linux +// diff --git a/test/incremental/llvm/address_spaces_pointer_access_chaining_array_pointer.zig b/test/incremental/llvm/address_spaces_pointer_access_chaining_array_pointer.zig new file mode 100644 index 0000000000..2e342dc852 --- /dev/null +++ b/test/incremental/llvm/address_spaces_pointer_access_chaining_array_pointer.zig @@ -0,0 +1,12 @@ +fn entry(a: *addrspace(.gs) [1]i32) *addrspace(.gs) i32 { + return &a[0]; +} +pub fn main() void { + _ = entry; +} + +// error +// output_mode=Exe +// backend=stage2,llvm +// target=x86_64-linux +// diff --git a/test/incremental/llvm/address_spaces_pointer_access_chaining_complex.zig b/test/incremental/llvm/address_spaces_pointer_access_chaining_complex.zig new file mode 100644 index 0000000000..5e616bd1da --- /dev/null +++ b/test/incremental/llvm/address_spaces_pointer_access_chaining_complex.zig @@ -0,0 +1,13 @@ +const A = struct { a: ?[1]i32 }; +fn entry(a: *addrspace(.gs) [1]A) *addrspace(.gs) i32 { + return &a[0].a.?[0]; +} +pub fn main() void { + _ = entry; +} + +// error +// output_mode=Exe +// backend=llvm +// target=x86_64-linux +// diff --git a/test/incremental/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig b/test/incremental/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig new file mode 100644 index 0000000000..519833a0e8 --- /dev/null +++ b/test/incremental/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig @@ -0,0 +1,13 @@ +const A = struct { a: i32 }; +fn entry(a: *addrspace(.gs) A) *addrspace(.gs) i32 { + return &a.a; +} +pub fn main() void { + _ = entry; +} + +// error +// output_mode=Exe +// backend=stage2,llvm +// target=x86_64-linux +// diff --git a/test/incremental/llvm/any_typed_null_to_any_typed_optional.zig b/test/incremental/llvm/any_typed_null_to_any_typed_optional.zig new file mode 100644 index 0000000000..af37ebf25d --- /dev/null +++ b/test/incremental/llvm/any_typed_null_to_any_typed_optional.zig @@ -0,0 +1,11 @@ +pub fn main() void { + var a: ?*anyopaque = undefined; + a = @as(?usize, null); +} + +// error +// output_mode=Exe +// backend=stage2,llvm +// target=x86_64-linux +// +// :3:21: error: expected *anyopaque, found ?usize diff --git a/test/incremental/llvm/blocks.zig b/test/incremental/llvm/blocks.zig new file mode 100644 index 0000000000..a2fbfeb618 --- /dev/null +++ b/test/incremental/llvm/blocks.zig @@ -0,0 +1,22 @@ +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +fn foo(ok: bool) i32 { + const val: i32 = blk: { + var x: i32 = 1; + if (!ok) break :blk x + 9; + break :blk x + 19; + }; + return val + 10; +} + +pub fn main() void { + assert(foo(false) == 20); + assert(foo(true) == 30); +} + +// run +// backend=stage2,llvm +// target=x86_64-linux +// diff --git a/test/incremental/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig b/test/incremental/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig new file mode 100644 index 0000000000..bc5c3d5b81 --- /dev/null +++ b/test/incremental/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig @@ -0,0 +1,12 @@ +fn entry(a: *addrspace(.fs) *addrspace(.gs) *i32) *i32 { + return a.*.*; +} +pub fn main() void { + _ = entry; +} + +// error +// output_mode=Exe +// backend=stage2,llvm +// target=x86_64-linux +// diff --git a/test/incremental/llvm/f_segment_address_space_reading_and_writing.zig b/test/incremental/llvm/f_segment_address_space_reading_and_writing.zig new file mode 100644 index 0000000000..62db92668b --- /dev/null +++ b/test/incremental/llvm/f_segment_address_space_reading_and_writing.zig @@ -0,0 +1,48 @@ +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +fn setFs(value: c_ulong) void { + asm volatile ( + \\syscall + : + : [number] "{rax}" (158), + [code] "{rdi}" (0x1002), + [val] "{rsi}" (value), + : "rcx", "r11", "memory" + ); +} + +fn getFs() c_ulong { + var result: c_ulong = undefined; + asm volatile ( + \\syscall + : + : [number] "{rax}" (158), + [code] "{rdi}" (0x1003), + [ptr] "{rsi}" (@ptrToInt(&result)), + : "rcx", "r11", "memory" + ); + return result; +} + +var test_value: u64 = 12345; + +pub fn main() void { + const orig_fs = getFs(); + + setFs(@ptrToInt(&test_value)); + assert(getFs() == @ptrToInt(&test_value)); + + var test_ptr = @intToPtr(*allowzero addrspace(.fs) u64, 0); + assert(test_ptr.* == 12345); + test_ptr.* = 98765; + assert(test_value == 98765); + + setFs(orig_fs); +} + +// run +// backend=llvm +// target=x86_64-linux +// diff --git a/test/incremental/llvm/for_loop.zig b/test/incremental/llvm/for_loop.zig new file mode 100644 index 0000000000..f58350f59c --- /dev/null +++ b/test/incremental/llvm/for_loop.zig @@ -0,0 +1,16 @@ +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +pub fn main() void { + var x: u32 = 0; + for ("hello") |_| { + x += 1; + } + assert("hello".len == x); +} + +// run +// backend=stage2,llvm +// target=x86_64-linux +// diff --git a/test/incremental/llvm/hello_world.zig b/test/incremental/llvm/hello_world.zig new file mode 100644 index 0000000000..bcd9d9f795 --- /dev/null +++ b/test/incremental/llvm/hello_world.zig @@ -0,0 +1,12 @@ +extern fn puts(s: [*:0]const u8) c_int; + +pub fn main() void { + _ = puts("hello world!"); +} + +// run +// backend=llvm +// target=x86_64-linux +// +// hello world! +// diff --git a/test/incremental/llvm/invalid_address_space_coercion.zig b/test/incremental/llvm/invalid_address_space_coercion.zig new file mode 100644 index 0000000000..e46b327bcf --- /dev/null +++ b/test/incremental/llvm/invalid_address_space_coercion.zig @@ -0,0 +1,13 @@ +fn entry(a: *addrspace(.gs) i32) *i32 { + return a; +} +pub fn main() void { + _ = entry; +} + +// error +// output_mode=Exe +// backend=stage2,llvm +// target=x86_64-linux +// +// :2:12: error: expected *i32, found *addrspace(.gs) i32 diff --git a/test/incremental/llvm/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig b/test/incremental/llvm/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig new file mode 100644 index 0000000000..18b3bebc4d --- /dev/null +++ b/test/incremental/llvm/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig @@ -0,0 +1,13 @@ +fn entry(a: *addrspace(.gs) i32) *i32 { + return &a.*; +} +pub fn main() void { + _ = entry; +} + +// error +// output_mode=Exe +// backend=stage2,llvm +// target=x86_64-linux +// +// :2:12: error: expected *i32, found *addrspace(.gs) i32 diff --git a/test/incremental/llvm/nested_blocks.zig b/test/incremental/llvm/nested_blocks.zig new file mode 100644 index 0000000000..974315df96 --- /dev/null +++ b/test/incremental/llvm/nested_blocks.zig @@ -0,0 +1,24 @@ +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +fn foo(ok: bool) i32 { + var val: i32 = blk: { + const val2: i32 = another: { + if (!ok) break :blk 10; + break :another 10; + }; + break :blk val2 + 10; + }; + return val; +} + +pub fn main() void { + assert(foo(false) == 10); + assert(foo(true) == 20); +} + +// run +// backend=stage2, llvm +// target=x86_64-linux +// diff --git a/test/incremental/llvm/optionals.zig b/test/incremental/llvm/optionals.zig new file mode 100644 index 0000000000..05c221a8a8 --- /dev/null +++ b/test/incremental/llvm/optionals.zig @@ -0,0 +1,45 @@ +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +pub fn main() void { + var opt_val: ?i32 = 10; + var null_val: ?i32 = null; + + var val1: i32 = opt_val.?; + const val1_1: i32 = opt_val.?; + var ptr_val1 = &(opt_val.?); + const ptr_val1_1 = &(opt_val.?); + + var val2: i32 = null_val orelse 20; + const val2_2: i32 = null_val orelse 20; + + var value: i32 = 20; + var ptr_val2 = &(null_val orelse value); + + const val3 = opt_val orelse 30; + var val3_var = opt_val orelse 30; + + assert(val1 == 10); + assert(val1_1 == 10); + assert(ptr_val1.* == 10); + assert(ptr_val1_1.* == 10); + + assert(val2 == 20); + assert(val2_2 == 20); + assert(ptr_val2.* == 20); + + assert(val3 == 10); + assert(val3_var == 10); + + (null_val orelse val2) = 1234; + assert(val2 == 1234); + + (opt_val orelse val2) = 5678; + assert(opt_val.? == 5678); +} + +// run +// backend=llvm +// target=x86_64-linux +// diff --git a/test/incremental/llvm/pointer_keeps_address_space.zig b/test/incremental/llvm/pointer_keeps_address_space.zig new file mode 100644 index 0000000000..3d3718c83e --- /dev/null +++ b/test/incremental/llvm/pointer_keeps_address_space.zig @@ -0,0 +1,12 @@ +fn entry(a: *addrspace(.gs) i32) *addrspace(.gs) i32 { + return a; +} +pub fn main() void { + _ = entry; +} + +// error +// output_mode=Exe +// backend=stage2,llvm +// target=x86_64-linux +// diff --git a/test/incremental/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig b/test/incremental/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig new file mode 100644 index 0000000000..541522e3af --- /dev/null +++ b/test/incremental/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig @@ -0,0 +1,12 @@ +fn entry(a: *addrspace(.gs) i32) *addrspace(.gs) i32 { + return &a.*; +} +pub fn main() void { + _ = entry; +} + +// error +// output_mode=Exe +// backend=stage2,llvm +// target=x86_64-linux +// diff --git a/test/incremental/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig b/test/incremental/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig new file mode 100644 index 0000000000..ff8ae13dab --- /dev/null +++ b/test/incremental/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig @@ -0,0 +1,12 @@ +fn entry(a: *addrspace(.generic) i32) *i32 { + return a; +} +pub fn main() void { + _ = entry; +} + +// error +// output_mode=Exe +// backend=stage2,llvm +// target=x86_64-linux +// diff --git a/test/incremental/llvm/pointer_with_different_address_spaces.zig b/test/incremental/llvm/pointer_with_different_address_spaces.zig new file mode 100644 index 0000000000..eaeb669775 --- /dev/null +++ b/test/incremental/llvm/pointer_with_different_address_spaces.zig @@ -0,0 +1,13 @@ +fn entry(a: *addrspace(.gs) i32) *addrspace(.fs) i32 { + return a; +} +pub fn main() void { + _ = entry; +} + +// error +// output_mode=Exe +// backend=stage2,llvm +// target=x86_64-linux +// +// :2:12: error: expected *addrspace(.fs) i32, found *addrspace(.gs) i32 diff --git a/test/incremental/llvm/pointers_with_different_address_spaces.zig b/test/incremental/llvm/pointers_with_different_address_spaces.zig new file mode 100644 index 0000000000..aed6093ec7 --- /dev/null +++ b/test/incremental/llvm/pointers_with_different_address_spaces.zig @@ -0,0 +1,13 @@ +fn entry(a: ?*addrspace(.gs) i32) *i32 { + return a.?; +} +pub fn main() void { + _ = entry; +} + +// error +// output_mode=Exe +// backend=stage2,llvm +// target=x86_64-linux +// +// :2:13: error: expected *i32, found *addrspace(.gs) i32 diff --git a/test/incremental/llvm/rem.zig b/test/incremental/llvm/rem.zig new file mode 100644 index 0000000000..679932d3c2 --- /dev/null +++ b/test/incremental/llvm/rem.zig @@ -0,0 +1,15 @@ +fn assert(ok: bool) void { + if (!ok) unreachable; +} +fn rem(lhs: i32, rhs: i32, expected: i32) bool { + return @rem(lhs, rhs) == expected; +} +pub fn main() void { + assert(rem(-5, 3, -2)); + assert(rem(5, 3, 2)); +} + +// run +// backend=stage2,llvm +// target=x86_64-linux +// diff --git a/test/incremental/llvm/shift_right_plus_left.0.zig b/test/incremental/llvm/shift_right_plus_left.0.zig new file mode 100644 index 0000000000..91d324d084 --- /dev/null +++ b/test/incremental/llvm/shift_right_plus_left.0.zig @@ -0,0 +1,12 @@ +pub fn main() void { + var i: u32 = 16; + assert(i >> 1, 8); +} +fn assert(a: u32, b: u32) void { + if (a != b) unreachable; +} + +// run +// backend=llvm +// target=x86_64-linux +// diff --git a/test/incremental/llvm/shift_right_plus_left.1.zig b/test/incremental/llvm/shift_right_plus_left.1.zig new file mode 100644 index 0000000000..994b67b9d0 --- /dev/null +++ b/test/incremental/llvm/shift_right_plus_left.1.zig @@ -0,0 +1,10 @@ +pub fn main() void { + var i: u32 = 16; + assert(i << 1, 32); +} +fn assert(a: u32, b: u32) void { + if (a != b) unreachable; +} + +// run +// diff --git a/test/incremental/llvm/simple_addition_and_subtraction.zig b/test/incremental/llvm/simple_addition_and_subtraction.zig new file mode 100644 index 0000000000..59011c9d00 --- /dev/null +++ b/test/incremental/llvm/simple_addition_and_subtraction.zig @@ -0,0 +1,20 @@ +fn add(a: i32, b: i32) i32 { + return a + b; +} + +pub fn main() void { + var a: i32 = -5; + const x = add(a, 7); + var y = add(2, 0); + y -= x; + assert(y == 0); +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +// run +// backend=stage2,llvm +// target=x86_64-linux +// diff --git a/test/incremental/llvm/simple_if_statement.zig b/test/incremental/llvm/simple_if_statement.zig new file mode 100644 index 0000000000..436fd48e3f --- /dev/null +++ b/test/incremental/llvm/simple_if_statement.zig @@ -0,0 +1,16 @@ +fn add(a: i32, b: i32) i32 { + return a + b; +} + +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +pub fn main() void { + assert(add(1, 2) == 3); +} + +// run +// backend=stage2,llvm +// target=x86_64-linux +// diff --git a/test/incremental/llvm/while_loops.zig b/test/incremental/llvm/while_loops.zig new file mode 100644 index 0000000000..3c092be628 --- /dev/null +++ b/test/incremental/llvm/while_loops.zig @@ -0,0 +1,18 @@ +fn assert(ok: bool) void { + if (!ok) unreachable; +} + +pub fn main() void { + var sum: u32 = 0; + var i: u32 = 0; + while (i < 5) : (i += 1) { + sum += i; + } + assert(sum == 10); + assert(i == 5); +} + +// run +// backend=stage2,llvm +// target=x86_64-linux +// diff --git a/test/stage2/llvm.zig b/test/stage2/llvm.zig deleted file mode 100644 index bf538efaca..0000000000 --- a/test/stage2/llvm.zig +++ /dev/null @@ -1,438 +0,0 @@ -const std = @import("std"); -const TestContext = @import("../../src/test.zig").TestContext; -const build_options = @import("build_options"); - -// These tests should work with all platforms, but we're using linux_x64 for -// now for consistency. Will be expanded eventually. -const linux_x64 = std.zig.CrossTarget{ - .cpu_arch = .x86_64, - .os_tag = .linux, -}; - -pub fn addCases(ctx: *TestContext) !void { - { - var case = ctx.exeUsingLlvmBackend("simple addition and subtraction", linux_x64); - - case.addCompareOutput( - \\fn add(a: i32, b: i32) i32 { - \\ return a + b; - \\} - \\ - \\pub export fn main() c_int { - \\ var a: i32 = -5; - \\ const x = add(a, 7); - \\ var y = add(2, 0); - \\ y -= x; - \\ return y; - \\} - , ""); - } - - { - var case = ctx.exeUsingLlvmBackend("shift right + left", linux_x64); - - case.addCompareOutput( - \\pub export fn main() c_int { - \\ var i: u32 = 16; - \\ assert(i >> 1, 8); - \\ return 0; - \\} - \\fn assert(a: u32, b: u32) void { - \\ if (a != b) unreachable; - \\} - , ""); - case.addCompareOutput( - \\pub export fn main() c_int { - \\ var i: u32 = 16; - \\ assert(i << 1, 32); - \\ return 0; - \\} - \\fn assert(a: u32, b: u32) void { - \\ if (a != b) unreachable; - \\} - , ""); - } - - { - var case = ctx.exeUsingLlvmBackend("llvm hello world", linux_x64); - - case.addCompareOutput( - \\extern fn puts(s: [*:0]const u8) c_int; - \\ - \\pub export fn main() c_int { - \\ _ = puts("hello world!"); - \\ return 0; - \\} - , "hello world!" ++ std.cstr.line_sep); - } - - { - var case = ctx.exeUsingLlvmBackend("simple if statement", linux_x64); - - case.addCompareOutput( - \\fn add(a: i32, b: i32) i32 { - \\ return a + b; - \\} - \\ - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - \\ - \\pub export fn main() c_int { - \\ assert(add(1,2) == 3); - \\ return 0; - \\} - , ""); - } - - { - var case = ctx.exeUsingLlvmBackend("blocks", linux_x64); - - case.addCompareOutput( - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - \\ - \\fn foo(ok: bool) i32 { - \\ const val: i32 = blk: { - \\ var x: i32 = 1; - \\ if (!ok) break :blk x + 9; - \\ break :blk x + 19; - \\ }; - \\ return val + 10; - \\} - \\ - \\pub export fn main() c_int { - \\ assert(foo(false) == 20); - \\ assert(foo(true) == 30); - \\ return 0; - \\} - , ""); - } - - { - var case = ctx.exeUsingLlvmBackend("nested blocks", linux_x64); - - case.addCompareOutput( - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - \\ - \\fn foo(ok: bool) i32 { - \\ var val: i32 = blk: { - \\ const val2: i32 = another: { - \\ if (!ok) break :blk 10; - \\ break :another 10; - \\ }; - \\ break :blk val2 + 10; - \\ }; - \\ return val; - \\} - \\ - \\pub export fn main() c_int { - \\ assert(foo(false) == 10); - \\ assert(foo(true) == 20); - \\ return 0; - \\} - , ""); - } - - { - var case = ctx.exeUsingLlvmBackend("while loops", linux_x64); - - case.addCompareOutput( - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - \\ - \\pub export fn main() c_int { - \\ var sum: u32 = 0; - \\ var i: u32 = 0; - \\ while (i < 5) : (i += 1) { - \\ sum += i; - \\ } - \\ assert(sum == 10); - \\ assert(i == 5); - \\ return 0; - \\} - , ""); - } - - { - var case = ctx.exeUsingLlvmBackend("optionals", linux_x64); - - case.addCompareOutput( - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - \\ - \\pub export fn main() c_int { - \\ var opt_val: ?i32 = 10; - \\ var null_val: ?i32 = null; - \\ - \\ var val1: i32 = opt_val.?; - \\ const val1_1: i32 = opt_val.?; - \\ var ptr_val1 = &(opt_val.?); - \\ const ptr_val1_1 = &(opt_val.?); - \\ - \\ var val2: i32 = null_val orelse 20; - \\ const val2_2: i32 = null_val orelse 20; - \\ - \\ var value: i32 = 20; - \\ var ptr_val2 = &(null_val orelse value); - \\ - \\ const val3 = opt_val orelse 30; - \\ var val3_var = opt_val orelse 30; - \\ - \\ assert(val1 == 10); - \\ assert(val1_1 == 10); - \\ assert(ptr_val1.* == 10); - \\ assert(ptr_val1_1.* == 10); - \\ - \\ assert(val2 == 20); - \\ assert(val2_2 == 20); - \\ assert(ptr_val2.* == 20); - \\ - \\ assert(val3 == 10); - \\ assert(val3_var == 10); - \\ - \\ (null_val orelse val2) = 1234; - \\ assert(val2 == 1234); - \\ - \\ (opt_val orelse val2) = 5678; - \\ assert(opt_val.? == 5678); - \\ - \\ return 0; - \\} - , ""); - } - - { - var case = ctx.exeUsingLlvmBackend("for loop", linux_x64); - - case.addCompareOutput( - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - \\ - \\pub export fn main() c_int { - \\ var x: u32 = 0; - \\ for ("hello") |_| { - \\ x += 1; - \\ } - \\ assert("hello".len == x); - \\ return 0; - \\} - , ""); - } - - { - var case = ctx.exeUsingLlvmBackend("@rem", linux_x64); - case.addCompareOutput( - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - \\fn rem(lhs: i32, rhs: i32, expected: i32) bool { - \\ return @rem(lhs, rhs) == expected; - \\} - \\pub export fn main() c_int { - \\ assert(rem(-5, 3, -2)); - \\ assert(rem(5, 3, 2)); - \\ return 0; - \\} - , ""); - } - - { - var case = ctx.exeUsingLlvmBackend("invalid address space coercion", linux_x64); - case.addError( - \\fn entry(a: *addrspace(.gs) i32) *i32 { - \\ return a; - \\} - \\pub export fn main() void { _ = entry; } - , &[_][]const u8{ - ":2:12: error: expected *i32, found *addrspace(.gs) i32", - }); - } - - { - var case = ctx.exeUsingLlvmBackend("pointer keeps address space", linux_x64); - case.compiles( - \\fn entry(a: *addrspace(.gs) i32) *addrspace(.gs) i32 { - \\ return a; - \\} - \\pub export fn main() void { _ = entry; } - ); - } - - { - var case = ctx.exeUsingLlvmBackend("pointer to explicit generic address space coerces to implicit pointer", linux_x64); - case.compiles( - \\fn entry(a: *addrspace(.generic) i32) *i32 { - \\ return a; - \\} - \\pub export fn main() void { _ = entry; } - ); - } - - { - var case = ctx.exeUsingLlvmBackend("pointers with different address spaces", linux_x64); - case.addError( - \\fn entry(a: *addrspace(.gs) i32) *addrspace(.fs) i32 { - \\ return a; - \\} - \\pub export fn main() void { _ = entry; } - , &[_][]const u8{ - ":2:12: error: expected *addrspace(.fs) i32, found *addrspace(.gs) i32", - }); - } - - { - var case = ctx.exeUsingLlvmBackend("pointers with different address spaces", linux_x64); - case.addError( - \\fn entry(a: ?*addrspace(.gs) i32) *i32 { - \\ return a.?; - \\} - \\pub export fn main() void { _ = entry; } - , &[_][]const u8{ - ":2:13: error: expected *i32, found *addrspace(.gs) i32", - }); - } - - { - var case = ctx.exeUsingLlvmBackend("invalid pointer keeps address space when taking address of dereference", linux_x64); - case.addError( - \\fn entry(a: *addrspace(.gs) i32) *i32 { - \\ return &a.*; - \\} - \\pub export fn main() void { _ = entry; } - , &[_][]const u8{ - ":2:12: error: expected *i32, found *addrspace(.gs) i32", - }); - } - - { - var case = ctx.exeUsingLlvmBackend("pointer keeps address space when taking address of dereference", linux_x64); - case.compiles( - \\fn entry(a: *addrspace(.gs) i32) *addrspace(.gs) i32 { - \\ return &a.*; - \\} - \\pub export fn main() void { _ = entry; } - ); - } - - { - var case = ctx.exeUsingLlvmBackend("address spaces pointer access chaining: array pointer", linux_x64); - case.compiles( - \\fn entry(a: *addrspace(.gs) [1]i32) *addrspace(.gs) i32 { - \\ return &a[0]; - \\} - \\pub export fn main() void { _ = entry; } - ); - } - - { - var case = ctx.exeUsingLlvmBackend("address spaces pointer access chaining: pointer to optional array", linux_x64); - case.compiles( - \\fn entry(a: *addrspace(.gs) ?[1]i32) *addrspace(.gs) i32 { - \\ return &a.*.?[0]; - \\} - \\pub export fn main() void { _ = entry; } - ); - } - - { - var case = ctx.exeUsingLlvmBackend("address spaces pointer access chaining: struct pointer", linux_x64); - case.compiles( - \\const A = struct{ a: i32 }; - \\fn entry(a: *addrspace(.gs) A) *addrspace(.gs) i32 { - \\ return &a.a; - \\} - \\pub export fn main() void { _ = entry; } - ); - } - - { - var case = ctx.exeUsingLlvmBackend("address spaces pointer access chaining: complex", linux_x64); - case.compiles( - \\const A = struct{ a: ?[1]i32 }; - \\fn entry(a: *addrspace(.gs) [1]A) *addrspace(.gs) i32 { - \\ return &a[0].a.?[0]; - \\} - \\pub export fn main() void { _ = entry; } - ); - } - - { - var case = ctx.exeUsingLlvmBackend("dereferencing through multiple pointers with address spaces", linux_x64); - case.compiles( - \\fn entry(a: *addrspace(.fs) *addrspace(.gs) *i32) *i32 { - \\ return a.*.*; - \\} - \\pub export fn main() void { _ = entry; } - ); - } - - { - var case = ctx.exeUsingLlvmBackend("f segment address space reading and writing", linux_x64); - case.addCompareOutput( - \\fn assert(ok: bool) void { - \\ if (!ok) unreachable; - \\} - \\ - \\fn setFs(value: c_ulong) void { - \\ asm volatile ( - \\ \\syscall - \\ : - \\ : [number] "{rax}" (158), - \\ [code] "{rdi}" (0x1002), - \\ [val] "{rsi}" (value), - \\ : "rcx", "r11", "memory" - \\ ); - \\} - \\ - \\fn getFs() c_ulong { - \\ var result: c_ulong = undefined; - \\ asm volatile ( - \\ \\syscall - \\ : - \\ : [number] "{rax}" (158), - \\ [code] "{rdi}" (0x1003), - \\ [ptr] "{rsi}" (@ptrToInt(&result)), - \\ : "rcx", "r11", "memory" - \\ ); - \\ return result; - \\} - \\ - \\var test_value: u64 = 12345; - \\ - \\pub export fn main() c_int { - \\ const orig_fs = getFs(); - \\ - \\ setFs(@ptrToInt(&test_value)); - \\ assert(getFs() == @ptrToInt(&test_value)); - \\ - \\ var test_ptr = @intToPtr(*allowzero addrspace(.fs) u64, 0); - \\ assert(test_ptr.* == 12345); - \\ test_ptr.* = 98765; - \\ assert(test_value == 98765); - \\ - \\ setFs(orig_fs); - \\ return 0; - \\} - , ""); - } - - { - // This worked in stage1 and we expressly do not want this to work in stage2 - var case = ctx.exeUsingLlvmBackend("any typed null to any typed optional", linux_x64); - case.addError( - \\pub export fn main() void { - \\ var a: ?*anyopaque = undefined; - \\ a = @as(?usize, null); - \\} - , &[_][]const u8{ - ":3:21: error: expected *anyopaque, found ?usize", - }); - } -} diff --git a/test/stage2/x86_64.zig b/test/stage2/x86_64.zig deleted file mode 100644 index a4c506400a..0000000000 --- a/test/stage2/x86_64.zig +++ /dev/null @@ -1,59 +0,0 @@ -const std = @import("std"); -const CrossTarget = std.zig.CrossTarget; -const TestContext = @import("../../src/test.zig").TestContext; - -const linux_x64 = std.zig.CrossTarget{ - .cpu_arch = .x86_64, - .os_tag = .linux, -}; -const macos_x64 = CrossTarget{ - .cpu_arch = .x86_64, - .os_tag = .macos, -}; -const all_targets: []const CrossTarget = &[_]CrossTarget{ - linux_x64, - macos_x64, -}; - -pub fn addCases(ctx: *TestContext) !void { - for (all_targets) |target| { - // TODO port this to the new test harness - var case = ctx.exe("basic import", target); - case.addCompareOutput( - \\pub fn main() void { - \\ @import("print.zig").print(); - \\} - , - "Hello, World!\n", - ); - switch (target.getOsTag()) { - .linux => try case.files.append(.{ - .src = - \\pub fn print() void { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (@as(usize, 1)), - \\ [arg1] "{rdi}" (@as(usize, 1)), - \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{rdx}" (@as(usize, 14)) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - , - .path = "print.zig", - }), - .macos => try case.files.append(.{ - .src = - \\extern "c" fn write(usize, usize, usize) usize; - \\ - \\pub fn print() void { - \\ _ = write(1, @ptrToInt("Hello, World!\n"), 14); - \\} - , - .path = "print.zig", - }), - else => unreachable, - } - } -} From 2875216f8e01c4e71a76fe840a0b298f0d42758e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 28 Apr 2022 11:42:14 +0200 Subject: [PATCH 1290/2031] test: fix x86_64-macos failures This is just a temporary fix - I would like to unify all of x86_64 tests across linux and macos OSes. --- test/incremental/assert_function.8.zig | 36 ---------- test/incremental/comptime_var.2.zig | 34 ---------- test/incremental/comptime_var.6.zig | 32 --------- ...ess_chaining_pointer_to_optional_array.zig | 2 +- ..._pointer_access_chaining_array_pointer.zig | 2 +- ...spaces_pointer_access_chaining_complex.zig | 2 +- ...pointer_access_chaining_struct_pointer.zig | 2 +- .../any_typed_null_to_any_typed_optional.zig | 2 +- test/incremental/llvm/blocks.zig | 2 +- ..._multiple_pointers_with_address_spaces.zig | 2 +- test/incremental/llvm/for_loop.zig | 2 +- test/incremental/llvm/hello_world.zig | 2 +- .../llvm/invalid_address_space_coercion.zig | 2 +- ...ace_when_taking_address_of_dereference.zig | 2 +- test/incremental/llvm/nested_blocks.zig | 2 +- test/incremental/llvm/optionals.zig | 2 +- .../llvm/pointer_keeps_address_space.zig | 2 +- ...ace_when_taking_address_of_dereference.zig | 2 +- ...ress_space_coerces_to_implicit_pointer.zig | 2 +- .../pointer_with_different_address_spaces.zig | 2 +- ...pointers_with_different_address_spaces.zig | 2 +- test/incremental/llvm/rem.zig | 2 +- .../llvm/shift_right_plus_left.0.zig | 2 +- .../llvm/simple_addition_and_subtraction.zig | 2 +- test/incremental/llvm/simple_if_statement.zig | 2 +- test/incremental/llvm/while_loops.zig | 2 +- .../{ => x86_64-linux}/assert_function.0.zig | 2 +- .../{ => x86_64-linux}/assert_function.1.zig | 0 .../{ => x86_64-linux}/assert_function.10.zig | 0 .../{ => x86_64-linux}/assert_function.11.zig | 0 .../{ => x86_64-linux}/assert_function.12.zig | 0 .../{ => x86_64-linux}/assert_function.13.zig | 0 .../{ => x86_64-linux}/assert_function.14.zig | 0 .../{ => x86_64-linux}/assert_function.15.zig | 0 .../{ => x86_64-linux}/assert_function.16.zig | 0 .../{ => x86_64-linux}/assert_function.17.zig | 0 .../{ => x86_64-linux}/assert_function.18.zig | 0 .../{ => x86_64-linux}/assert_function.2.zig | 0 .../{ => x86_64-linux}/assert_function.3.zig | 0 .../{ => x86_64-linux}/assert_function.4.zig | 0 .../{ => x86_64-linux}/assert_function.5.zig | 0 .../{ => x86_64-linux}/assert_function.6.zig | 0 .../x86_64-linux/assert_function.7.zig | 28 ++++++++ .../x86_64-linux/assert_function.8.zig | 24 +++++++ .../{ => x86_64-linux}/assert_function.9.zig | 0 .../{ => x86_64-linux}/comptime_var.0.zig | 2 +- .../{ => x86_64-linux}/comptime_var.1.zig | 0 .../x86_64-linux/comptime_var.2.zig | 22 +++++++ .../{ => x86_64-linux}/comptime_var.3.zig | 0 .../{ => x86_64-linux}/comptime_var.4.zig | 0 .../{ => x86_64-linux}/comptime_var.5.zig | 0 .../x86_64-linux/comptime_var.6.zig | 20 ++++++ .../x86_64-macos/assert_function.0.zig | 15 +++++ .../x86_64-macos/assert_function.1.zig | 17 +++++ .../x86_64-macos/assert_function.10.zig | 27 ++++++++ .../x86_64-macos/assert_function.11.zig | 66 +++++++++++++++++++ .../x86_64-macos/assert_function.12.zig | 47 +++++++++++++ .../x86_64-macos/assert_function.13.zig | 19 ++++++ .../x86_64-macos/assert_function.14.zig | 17 +++++ .../x86_64-macos/assert_function.15.zig | 10 +++ .../x86_64-macos/assert_function.16.zig | 11 ++++ .../x86_64-macos/assert_function.17.zig | 11 ++++ .../assert_function.18.zig} | 9 +-- .../x86_64-macos/assert_function.2.zig | 21 ++++++ .../x86_64-macos/assert_function.3.zig | 22 +++++++ .../x86_64-macos/assert_function.4.zig | 15 +++++ .../x86_64-macos/assert_function.5.zig | 19 ++++++ .../x86_64-macos/assert_function.6.zig | 9 +++ .../x86_64-macos/assert_function.7.zig | 23 +++++++ .../x86_64-macos/assert_function.8.zig | 19 ++++++ .../x86_64-macos/assert_function.9.zig | 22 +++++++ .../x86_64-macos/comptime_var.0.zig | 12 ++++ .../x86_64-macos/comptime_var.1.zig | 13 ++++ .../x86_64-macos/comptime_var.2.zig | 17 +++++ .../x86_64-macos/comptime_var.3.zig | 10 +++ .../x86_64-macos/comptime_var.4.zig | 9 +++ .../x86_64-macos/comptime_var.5.zig | 15 +++++ .../x86_64-macos/comptime_var.6.zig | 15 +++++ 78 files changed, 602 insertions(+), 134 deletions(-) delete mode 100644 test/incremental/assert_function.8.zig delete mode 100644 test/incremental/comptime_var.2.zig delete mode 100644 test/incremental/comptime_var.6.zig rename test/incremental/{ => x86_64-linux}/assert_function.0.zig (83%) rename test/incremental/{ => x86_64-linux}/assert_function.1.zig (100%) rename test/incremental/{ => x86_64-linux}/assert_function.10.zig (100%) rename test/incremental/{ => x86_64-linux}/assert_function.11.zig (100%) rename test/incremental/{ => x86_64-linux}/assert_function.12.zig (100%) rename test/incremental/{ => x86_64-linux}/assert_function.13.zig (100%) rename test/incremental/{ => x86_64-linux}/assert_function.14.zig (100%) rename test/incremental/{ => x86_64-linux}/assert_function.15.zig (100%) rename test/incremental/{ => x86_64-linux}/assert_function.16.zig (100%) rename test/incremental/{ => x86_64-linux}/assert_function.17.zig (100%) rename test/incremental/{ => x86_64-linux}/assert_function.18.zig (100%) rename test/incremental/{ => x86_64-linux}/assert_function.2.zig (100%) rename test/incremental/{ => x86_64-linux}/assert_function.3.zig (100%) rename test/incremental/{ => x86_64-linux}/assert_function.4.zig (100%) rename test/incremental/{ => x86_64-linux}/assert_function.5.zig (100%) rename test/incremental/{ => x86_64-linux}/assert_function.6.zig (100%) create mode 100644 test/incremental/x86_64-linux/assert_function.7.zig create mode 100644 test/incremental/x86_64-linux/assert_function.8.zig rename test/incremental/{ => x86_64-linux}/assert_function.9.zig (100%) rename test/incremental/{ => x86_64-linux}/comptime_var.0.zig (86%) rename test/incremental/{ => x86_64-linux}/comptime_var.1.zig (100%) create mode 100644 test/incremental/x86_64-linux/comptime_var.2.zig rename test/incremental/{ => x86_64-linux}/comptime_var.3.zig (100%) rename test/incremental/{ => x86_64-linux}/comptime_var.4.zig (100%) rename test/incremental/{ => x86_64-linux}/comptime_var.5.zig (100%) create mode 100644 test/incremental/x86_64-linux/comptime_var.6.zig create mode 100644 test/incremental/x86_64-macos/assert_function.0.zig create mode 100644 test/incremental/x86_64-macos/assert_function.1.zig create mode 100644 test/incremental/x86_64-macos/assert_function.10.zig create mode 100644 test/incremental/x86_64-macos/assert_function.11.zig create mode 100644 test/incremental/x86_64-macos/assert_function.12.zig create mode 100644 test/incremental/x86_64-macos/assert_function.13.zig create mode 100644 test/incremental/x86_64-macos/assert_function.14.zig create mode 100644 test/incremental/x86_64-macos/assert_function.15.zig create mode 100644 test/incremental/x86_64-macos/assert_function.16.zig create mode 100644 test/incremental/x86_64-macos/assert_function.17.zig rename test/incremental/{assert_function.7.zig => x86_64-macos/assert_function.18.zig} (80%) create mode 100644 test/incremental/x86_64-macos/assert_function.2.zig create mode 100644 test/incremental/x86_64-macos/assert_function.3.zig create mode 100644 test/incremental/x86_64-macos/assert_function.4.zig create mode 100644 test/incremental/x86_64-macos/assert_function.5.zig create mode 100644 test/incremental/x86_64-macos/assert_function.6.zig create mode 100644 test/incremental/x86_64-macos/assert_function.7.zig create mode 100644 test/incremental/x86_64-macos/assert_function.8.zig create mode 100644 test/incremental/x86_64-macos/assert_function.9.zig create mode 100644 test/incremental/x86_64-macos/comptime_var.0.zig create mode 100644 test/incremental/x86_64-macos/comptime_var.1.zig create mode 100644 test/incremental/x86_64-macos/comptime_var.2.zig create mode 100644 test/incremental/x86_64-macos/comptime_var.3.zig create mode 100644 test/incremental/x86_64-macos/comptime_var.4.zig create mode 100644 test/incremental/x86_64-macos/comptime_var.5.zig create mode 100644 test/incremental/x86_64-macos/comptime_var.6.zig diff --git a/test/incremental/assert_function.8.zig b/test/incremental/assert_function.8.zig deleted file mode 100644 index f845de9aa0..0000000000 --- a/test/incremental/assert_function.8.zig +++ /dev/null @@ -1,36 +0,0 @@ -const builtin = @import("builtin"); - -extern "c" fn write(usize, usize, usize) usize; - -pub fn main() void { - var i: u32 = 0; - inline while (i < 4) : (i += 1) print(); - assert(i == 4); -} - -fn print() void { - switch (builtin.os.tag) { - .linux => { - asm volatile ("syscall" - : - : [number] "{rax}" (1), - [arg1] "{rdi}" (1), - [arg2] "{rsi}" (@ptrToInt("hello\n")), - [arg3] "{rdx}" (6), - : "rcx", "r11", "memory" - ); - }, - .macos => { - _ = write(1, @ptrToInt("hello\n"), 6); - }, - else => unreachable, - } -} - -pub fn assert(ok: bool) void { - if (!ok) unreachable; // assertion failure -} - -// error -// -// :7:21: error: unable to resolve comptime value diff --git a/test/incremental/comptime_var.2.zig b/test/incremental/comptime_var.2.zig deleted file mode 100644 index e91c0540ef..0000000000 --- a/test/incremental/comptime_var.2.zig +++ /dev/null @@ -1,34 +0,0 @@ -const builtin = @import("builtin"); - -extern "c" fn write(usize, usize, usize) usize; - -pub fn main() void { - comptime var len: u32 = 5; - print(len); - len += 9; - print(len); -} - -fn print(len: usize) void { - switch (builtin.os.tag) { - .linux => { - asm volatile ("syscall" - : - : [number] "{rax}" (1), - [arg1] "{rdi}" (1), - [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), - [arg3] "{rdx}" (len), - : "rcx", "r11", "memory" - ); - }, - .macos => { - _ = write(1, @ptrToInt("Hello, World!\n"), len); - }, - else => unreachable, - } -} - -// run -// -// HelloHello, World! -// diff --git a/test/incremental/comptime_var.6.zig b/test/incremental/comptime_var.6.zig deleted file mode 100644 index 0eb743a05b..0000000000 --- a/test/incremental/comptime_var.6.zig +++ /dev/null @@ -1,32 +0,0 @@ -const builtin = @import("builtin"); - -extern "c" fn write(usize, usize, usize) usize; - -pub fn main() void { - comptime var i: u64 = 2; - inline while (i < 6) : (i += 1) { - print(i); - } -} -fn print(len: usize) void { - switch (builtin.os.tag) { - .linux => { - asm volatile ("syscall" - : - : [number] "{rax}" (1), - [arg1] "{rdi}" (1), - [arg2] "{rsi}" (@ptrToInt("Hello")), - [arg3] "{rdx}" (len), - : "rcx", "r11", "memory" - ); - }, - .macos => { - _ = write(1, @ptrToInt("Hello"), len); - }, - else => unreachable, - } -} - -// run -// -// HeHelHellHello diff --git a/test/incremental/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig b/test/incremental/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig index 592c82691d..cf43513159 100644 --- a/test/incremental/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig +++ b/test/incremental/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig @@ -8,5 +8,5 @@ pub fn main() void { // error // output_mode=Exe // backend=llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // diff --git a/test/incremental/llvm/address_spaces_pointer_access_chaining_array_pointer.zig b/test/incremental/llvm/address_spaces_pointer_access_chaining_array_pointer.zig index 2e342dc852..5907c1dad5 100644 --- a/test/incremental/llvm/address_spaces_pointer_access_chaining_array_pointer.zig +++ b/test/incremental/llvm/address_spaces_pointer_access_chaining_array_pointer.zig @@ -8,5 +8,5 @@ pub fn main() void { // error // output_mode=Exe // backend=stage2,llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // diff --git a/test/incremental/llvm/address_spaces_pointer_access_chaining_complex.zig b/test/incremental/llvm/address_spaces_pointer_access_chaining_complex.zig index 5e616bd1da..ece0614f73 100644 --- a/test/incremental/llvm/address_spaces_pointer_access_chaining_complex.zig +++ b/test/incremental/llvm/address_spaces_pointer_access_chaining_complex.zig @@ -9,5 +9,5 @@ pub fn main() void { // error // output_mode=Exe // backend=llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // diff --git a/test/incremental/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig b/test/incremental/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig index 519833a0e8..9175bcbc0e 100644 --- a/test/incremental/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig +++ b/test/incremental/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig @@ -9,5 +9,5 @@ pub fn main() void { // error // output_mode=Exe // backend=stage2,llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // diff --git a/test/incremental/llvm/any_typed_null_to_any_typed_optional.zig b/test/incremental/llvm/any_typed_null_to_any_typed_optional.zig index af37ebf25d..c155d04497 100644 --- a/test/incremental/llvm/any_typed_null_to_any_typed_optional.zig +++ b/test/incremental/llvm/any_typed_null_to_any_typed_optional.zig @@ -6,6 +6,6 @@ pub fn main() void { // error // output_mode=Exe // backend=stage2,llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // // :3:21: error: expected *anyopaque, found ?usize diff --git a/test/incremental/llvm/blocks.zig b/test/incremental/llvm/blocks.zig index a2fbfeb618..c64909c7fe 100644 --- a/test/incremental/llvm/blocks.zig +++ b/test/incremental/llvm/blocks.zig @@ -18,5 +18,5 @@ pub fn main() void { // run // backend=stage2,llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // diff --git a/test/incremental/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig b/test/incremental/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig index bc5c3d5b81..8f36700757 100644 --- a/test/incremental/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig +++ b/test/incremental/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig @@ -8,5 +8,5 @@ pub fn main() void { // error // output_mode=Exe // backend=stage2,llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // diff --git a/test/incremental/llvm/for_loop.zig b/test/incremental/llvm/for_loop.zig index f58350f59c..e7e701aafa 100644 --- a/test/incremental/llvm/for_loop.zig +++ b/test/incremental/llvm/for_loop.zig @@ -12,5 +12,5 @@ pub fn main() void { // run // backend=stage2,llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // diff --git a/test/incremental/llvm/hello_world.zig b/test/incremental/llvm/hello_world.zig index bcd9d9f795..4243191b0f 100644 --- a/test/incremental/llvm/hello_world.zig +++ b/test/incremental/llvm/hello_world.zig @@ -6,7 +6,7 @@ pub fn main() void { // run // backend=llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // // hello world! // diff --git a/test/incremental/llvm/invalid_address_space_coercion.zig b/test/incremental/llvm/invalid_address_space_coercion.zig index e46b327bcf..6b72f1d5a8 100644 --- a/test/incremental/llvm/invalid_address_space_coercion.zig +++ b/test/incremental/llvm/invalid_address_space_coercion.zig @@ -8,6 +8,6 @@ pub fn main() void { // error // output_mode=Exe // backend=stage2,llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // // :2:12: error: expected *i32, found *addrspace(.gs) i32 diff --git a/test/incremental/llvm/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig b/test/incremental/llvm/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig index 18b3bebc4d..26f68ce789 100644 --- a/test/incremental/llvm/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig +++ b/test/incremental/llvm/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig @@ -8,6 +8,6 @@ pub fn main() void { // error // output_mode=Exe // backend=stage2,llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // // :2:12: error: expected *i32, found *addrspace(.gs) i32 diff --git a/test/incremental/llvm/nested_blocks.zig b/test/incremental/llvm/nested_blocks.zig index 974315df96..47b6c5069b 100644 --- a/test/incremental/llvm/nested_blocks.zig +++ b/test/incremental/llvm/nested_blocks.zig @@ -20,5 +20,5 @@ pub fn main() void { // run // backend=stage2, llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // diff --git a/test/incremental/llvm/optionals.zig b/test/incremental/llvm/optionals.zig index 05c221a8a8..7d52fc0f17 100644 --- a/test/incremental/llvm/optionals.zig +++ b/test/incremental/llvm/optionals.zig @@ -41,5 +41,5 @@ pub fn main() void { // run // backend=llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // diff --git a/test/incremental/llvm/pointer_keeps_address_space.zig b/test/incremental/llvm/pointer_keeps_address_space.zig index 3d3718c83e..bfd40566f8 100644 --- a/test/incremental/llvm/pointer_keeps_address_space.zig +++ b/test/incremental/llvm/pointer_keeps_address_space.zig @@ -8,5 +8,5 @@ pub fn main() void { // error // output_mode=Exe // backend=stage2,llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // diff --git a/test/incremental/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig b/test/incremental/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig index 541522e3af..8114e86c5d 100644 --- a/test/incremental/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig +++ b/test/incremental/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig @@ -8,5 +8,5 @@ pub fn main() void { // error // output_mode=Exe // backend=stage2,llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // diff --git a/test/incremental/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig b/test/incremental/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig index ff8ae13dab..78bc3e4bd6 100644 --- a/test/incremental/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig +++ b/test/incremental/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig @@ -8,5 +8,5 @@ pub fn main() void { // error // output_mode=Exe // backend=stage2,llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // diff --git a/test/incremental/llvm/pointer_with_different_address_spaces.zig b/test/incremental/llvm/pointer_with_different_address_spaces.zig index eaeb669775..8fbd8b07cf 100644 --- a/test/incremental/llvm/pointer_with_different_address_spaces.zig +++ b/test/incremental/llvm/pointer_with_different_address_spaces.zig @@ -8,6 +8,6 @@ pub fn main() void { // error // output_mode=Exe // backend=stage2,llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // // :2:12: error: expected *addrspace(.fs) i32, found *addrspace(.gs) i32 diff --git a/test/incremental/llvm/pointers_with_different_address_spaces.zig b/test/incremental/llvm/pointers_with_different_address_spaces.zig index aed6093ec7..39e5e6a6d1 100644 --- a/test/incremental/llvm/pointers_with_different_address_spaces.zig +++ b/test/incremental/llvm/pointers_with_different_address_spaces.zig @@ -8,6 +8,6 @@ pub fn main() void { // error // output_mode=Exe // backend=stage2,llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // // :2:13: error: expected *i32, found *addrspace(.gs) i32 diff --git a/test/incremental/llvm/rem.zig b/test/incremental/llvm/rem.zig index 679932d3c2..9804338ea6 100644 --- a/test/incremental/llvm/rem.zig +++ b/test/incremental/llvm/rem.zig @@ -11,5 +11,5 @@ pub fn main() void { // run // backend=stage2,llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // diff --git a/test/incremental/llvm/shift_right_plus_left.0.zig b/test/incremental/llvm/shift_right_plus_left.0.zig index 91d324d084..23b733c493 100644 --- a/test/incremental/llvm/shift_right_plus_left.0.zig +++ b/test/incremental/llvm/shift_right_plus_left.0.zig @@ -8,5 +8,5 @@ fn assert(a: u32, b: u32) void { // run // backend=llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // diff --git a/test/incremental/llvm/simple_addition_and_subtraction.zig b/test/incremental/llvm/simple_addition_and_subtraction.zig index 59011c9d00..8a6f419f4a 100644 --- a/test/incremental/llvm/simple_addition_and_subtraction.zig +++ b/test/incremental/llvm/simple_addition_and_subtraction.zig @@ -16,5 +16,5 @@ fn assert(ok: bool) void { // run // backend=stage2,llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // diff --git a/test/incremental/llvm/simple_if_statement.zig b/test/incremental/llvm/simple_if_statement.zig index 436fd48e3f..73cf08cda2 100644 --- a/test/incremental/llvm/simple_if_statement.zig +++ b/test/incremental/llvm/simple_if_statement.zig @@ -12,5 +12,5 @@ pub fn main() void { // run // backend=stage2,llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // diff --git a/test/incremental/llvm/while_loops.zig b/test/incremental/llvm/while_loops.zig index 3c092be628..3b9276cf7f 100644 --- a/test/incremental/llvm/while_loops.zig +++ b/test/incremental/llvm/while_loops.zig @@ -14,5 +14,5 @@ pub fn main() void { // run // backend=stage2,llvm -// target=x86_64-linux +// target=x86_64-linux,x86_64-macos // diff --git a/test/incremental/assert_function.0.zig b/test/incremental/x86_64-linux/assert_function.0.zig similarity index 83% rename from test/incremental/assert_function.0.zig rename to test/incremental/x86_64-linux/assert_function.0.zig index 889c9b1572..ab34f5328d 100644 --- a/test/incremental/assert_function.0.zig +++ b/test/incremental/x86_64-linux/assert_function.0.zig @@ -11,5 +11,5 @@ pub fn assert(ok: bool) void { } // run -// target=x86_64-linux,x86_64-macos +// target=x86_64-linux // diff --git a/test/incremental/assert_function.1.zig b/test/incremental/x86_64-linux/assert_function.1.zig similarity index 100% rename from test/incremental/assert_function.1.zig rename to test/incremental/x86_64-linux/assert_function.1.zig diff --git a/test/incremental/assert_function.10.zig b/test/incremental/x86_64-linux/assert_function.10.zig similarity index 100% rename from test/incremental/assert_function.10.zig rename to test/incremental/x86_64-linux/assert_function.10.zig diff --git a/test/incremental/assert_function.11.zig b/test/incremental/x86_64-linux/assert_function.11.zig similarity index 100% rename from test/incremental/assert_function.11.zig rename to test/incremental/x86_64-linux/assert_function.11.zig diff --git a/test/incremental/assert_function.12.zig b/test/incremental/x86_64-linux/assert_function.12.zig similarity index 100% rename from test/incremental/assert_function.12.zig rename to test/incremental/x86_64-linux/assert_function.12.zig diff --git a/test/incremental/assert_function.13.zig b/test/incremental/x86_64-linux/assert_function.13.zig similarity index 100% rename from test/incremental/assert_function.13.zig rename to test/incremental/x86_64-linux/assert_function.13.zig diff --git a/test/incremental/assert_function.14.zig b/test/incremental/x86_64-linux/assert_function.14.zig similarity index 100% rename from test/incremental/assert_function.14.zig rename to test/incremental/x86_64-linux/assert_function.14.zig diff --git a/test/incremental/assert_function.15.zig b/test/incremental/x86_64-linux/assert_function.15.zig similarity index 100% rename from test/incremental/assert_function.15.zig rename to test/incremental/x86_64-linux/assert_function.15.zig diff --git a/test/incremental/assert_function.16.zig b/test/incremental/x86_64-linux/assert_function.16.zig similarity index 100% rename from test/incremental/assert_function.16.zig rename to test/incremental/x86_64-linux/assert_function.16.zig diff --git a/test/incremental/assert_function.17.zig b/test/incremental/x86_64-linux/assert_function.17.zig similarity index 100% rename from test/incremental/assert_function.17.zig rename to test/incremental/x86_64-linux/assert_function.17.zig diff --git a/test/incremental/assert_function.18.zig b/test/incremental/x86_64-linux/assert_function.18.zig similarity index 100% rename from test/incremental/assert_function.18.zig rename to test/incremental/x86_64-linux/assert_function.18.zig diff --git a/test/incremental/assert_function.2.zig b/test/incremental/x86_64-linux/assert_function.2.zig similarity index 100% rename from test/incremental/assert_function.2.zig rename to test/incremental/x86_64-linux/assert_function.2.zig diff --git a/test/incremental/assert_function.3.zig b/test/incremental/x86_64-linux/assert_function.3.zig similarity index 100% rename from test/incremental/assert_function.3.zig rename to test/incremental/x86_64-linux/assert_function.3.zig diff --git a/test/incremental/assert_function.4.zig b/test/incremental/x86_64-linux/assert_function.4.zig similarity index 100% rename from test/incremental/assert_function.4.zig rename to test/incremental/x86_64-linux/assert_function.4.zig diff --git a/test/incremental/assert_function.5.zig b/test/incremental/x86_64-linux/assert_function.5.zig similarity index 100% rename from test/incremental/assert_function.5.zig rename to test/incremental/x86_64-linux/assert_function.5.zig diff --git a/test/incremental/assert_function.6.zig b/test/incremental/x86_64-linux/assert_function.6.zig similarity index 100% rename from test/incremental/assert_function.6.zig rename to test/incremental/x86_64-linux/assert_function.6.zig diff --git a/test/incremental/x86_64-linux/assert_function.7.zig b/test/incremental/x86_64-linux/assert_function.7.zig new file mode 100644 index 0000000000..e6d18a5c3f --- /dev/null +++ b/test/incremental/x86_64-linux/assert_function.7.zig @@ -0,0 +1,28 @@ +pub fn main() void { + var i: u32 = 0; + while (i < 4) : (i += 1) print(); + assert(i == 4); +} + +fn print() void { + asm volatile ("syscall" + : + : [number] "{rax}" (1), + [arg1] "{rdi}" (1), + [arg2] "{rsi}" (@ptrToInt("hello\n")), + [arg3] "{rdx}" (6), + : "rcx", "r11", "memory" + ); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// +// hello +// hello +// hello +// hello +// diff --git a/test/incremental/x86_64-linux/assert_function.8.zig b/test/incremental/x86_64-linux/assert_function.8.zig new file mode 100644 index 0000000000..4ce3ec0844 --- /dev/null +++ b/test/incremental/x86_64-linux/assert_function.8.zig @@ -0,0 +1,24 @@ +pub fn main() void { + var i: u32 = 0; + inline while (i < 4) : (i += 1) print(); + assert(i == 4); +} + +fn print() void { + asm volatile ("syscall" + : + : [number] "{rax}" (1), + [arg1] "{rdi}" (1), + [arg2] "{rsi}" (@ptrToInt("hello\n")), + [arg3] "{rdx}" (6), + : "rcx", "r11", "memory" + ); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// error +// +// :5:21: error: unable to resolve comptime value diff --git a/test/incremental/assert_function.9.zig b/test/incremental/x86_64-linux/assert_function.9.zig similarity index 100% rename from test/incremental/assert_function.9.zig rename to test/incremental/x86_64-linux/assert_function.9.zig diff --git a/test/incremental/comptime_var.0.zig b/test/incremental/x86_64-linux/comptime_var.0.zig similarity index 86% rename from test/incremental/comptime_var.0.zig rename to test/incremental/x86_64-linux/comptime_var.0.zig index 019cf78abb..45f87ecaa1 100644 --- a/test/incremental/comptime_var.0.zig +++ b/test/incremental/x86_64-linux/comptime_var.0.zig @@ -6,7 +6,7 @@ pub fn main() void { // error // output_mode=Exe -// target=x86_64-linux,x86_64-macos +// target=x86_64-linux // // :4:21: error: store to comptime variable depends on runtime condition // :4:11: note: runtime condition here diff --git a/test/incremental/comptime_var.1.zig b/test/incremental/x86_64-linux/comptime_var.1.zig similarity index 100% rename from test/incremental/comptime_var.1.zig rename to test/incremental/x86_64-linux/comptime_var.1.zig diff --git a/test/incremental/x86_64-linux/comptime_var.2.zig b/test/incremental/x86_64-linux/comptime_var.2.zig new file mode 100644 index 0000000000..72bdfad90c --- /dev/null +++ b/test/incremental/x86_64-linux/comptime_var.2.zig @@ -0,0 +1,22 @@ +pub fn main() void { + comptime var len: u32 = 5; + print(len); + len += 9; + print(len); +} + +fn print(len: usize) void { + asm volatile ("syscall" + : + : [number] "{rax}" (1), + [arg1] "{rdi}" (1), + [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), + [arg3] "{rdx}" (len), + : "rcx", "r11", "memory" + ); +} + +// run +// +// HelloHello, World! +// diff --git a/test/incremental/comptime_var.3.zig b/test/incremental/x86_64-linux/comptime_var.3.zig similarity index 100% rename from test/incremental/comptime_var.3.zig rename to test/incremental/x86_64-linux/comptime_var.3.zig diff --git a/test/incremental/comptime_var.4.zig b/test/incremental/x86_64-linux/comptime_var.4.zig similarity index 100% rename from test/incremental/comptime_var.4.zig rename to test/incremental/x86_64-linux/comptime_var.4.zig diff --git a/test/incremental/comptime_var.5.zig b/test/incremental/x86_64-linux/comptime_var.5.zig similarity index 100% rename from test/incremental/comptime_var.5.zig rename to test/incremental/x86_64-linux/comptime_var.5.zig diff --git a/test/incremental/x86_64-linux/comptime_var.6.zig b/test/incremental/x86_64-linux/comptime_var.6.zig new file mode 100644 index 0000000000..2790949561 --- /dev/null +++ b/test/incremental/x86_64-linux/comptime_var.6.zig @@ -0,0 +1,20 @@ +pub fn main() void { + comptime var i: u64 = 2; + inline while (i < 6) : (i += 1) { + print(i); + } +} +fn print(len: usize) void { + asm volatile ("syscall" + : + : [number] "{rax}" (1), + [arg1] "{rdi}" (1), + [arg2] "{rsi}" (@ptrToInt("Hello")), + [arg3] "{rdx}" (len), + : "rcx", "r11", "memory" + ); +} + +// run +// +// HeHelHellHello diff --git a/test/incremental/x86_64-macos/assert_function.0.zig b/test/incremental/x86_64-macos/assert_function.0.zig new file mode 100644 index 0000000000..20e5cf0b97 --- /dev/null +++ b/test/incremental/x86_64-macos/assert_function.0.zig @@ -0,0 +1,15 @@ +pub fn main() void { + add(3, 4); +} + +fn add(a: u32, b: u32) void { + assert(a + b == 7); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// target=x86_64-macos +// diff --git a/test/incremental/x86_64-macos/assert_function.1.zig b/test/incremental/x86_64-macos/assert_function.1.zig new file mode 100644 index 0000000000..ac2df25d85 --- /dev/null +++ b/test/incremental/x86_64-macos/assert_function.1.zig @@ -0,0 +1,17 @@ +pub fn main() void { + add(3, 4); +} + +fn add(a: u32, b: u32) void { + const c = a + b; // 7 + const d = a + c; // 10 + const e = d + b; // 14 + assert(e == 14); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/x86_64-macos/assert_function.10.zig b/test/incremental/x86_64-macos/assert_function.10.zig new file mode 100644 index 0000000000..b3f1610cd6 --- /dev/null +++ b/test/incremental/x86_64-macos/assert_function.10.zig @@ -0,0 +1,27 @@ +pub fn main() void { + assert(add(3, 4) == 116); +} + +fn add(a: u32, b: u32) u32 { + const x: u32 = blk: { + const c = a + b; // 7 + const d = a + c; // 10 + const e = d + b; // 14 + const f = d + e; // 24 + const g = e + f; // 38 + const h = f + g; // 62 + const i = g + h; // 100 + const j = i + d; // 110 + break :blk j; + }; + const y = x + a; // 113 + const z = y + a; // 116 + return z; +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/x86_64-macos/assert_function.11.zig b/test/incremental/x86_64-macos/assert_function.11.zig new file mode 100644 index 0000000000..d64130a677 --- /dev/null +++ b/test/incremental/x86_64-macos/assert_function.11.zig @@ -0,0 +1,66 @@ +pub fn main() void { + assert(add(3, 4) == 1221); + assert(mul(3, 4) == 21609); +} + +fn add(a: u32, b: u32) u32 { + const x: u32 = blk: { + const c = a + b; // 7 + const d = a + c; // 10 + const e = d + b; // 14 + const f = d + e; // 24 + const g = e + f; // 38 + const h = f + g; // 62 + const i = g + h; // 100 + const j = i + d; // 110 + const k = i + j; // 210 + const l = j + k; // 320 + const m = l + c; // 327 + const n = m + d; // 337 + const o = n + e; // 351 + const p = o + f; // 375 + const q = p + g; // 413 + const r = q + h; // 475 + const s = r + i; // 575 + const t = s + j; // 685 + const u = t + k; // 895 + const v = u + l; // 1215 + break :blk v; + }; + const y = x + a; // 1218 + const z = y + a; // 1221 + return z; +} + +fn mul(a: u32, b: u32) u32 { + const x: u32 = blk: { + const c = a * a * a * a; // 81 + const d = a * a * a * b; // 108 + const e = a * a * b * a; // 108 + const f = a * a * b * b; // 144 + const g = a * b * a * a; // 108 + const h = a * b * a * b; // 144 + const i = a * b * b * a; // 144 + const j = a * b * b * b; // 192 + const k = b * a * a * a; // 108 + const l = b * a * a * b; // 144 + const m = b * a * b * a; // 144 + const n = b * a * b * b; // 192 + const o = b * b * a * a; // 144 + const p = b * b * a * b; // 192 + const q = b * b * b * a; // 192 + const r = b * b * b * b; // 256 + const s = c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r; // 2401 + break :blk s; + }; + const y = x * a; // 7203 + const z = y * a; // 21609 + return z; +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/x86_64-macos/assert_function.12.zig b/test/incremental/x86_64-macos/assert_function.12.zig new file mode 100644 index 0000000000..4f64c1e062 --- /dev/null +++ b/test/incremental/x86_64-macos/assert_function.12.zig @@ -0,0 +1,47 @@ +pub fn main() void { + assert(add(3, 4) == 791); + assert(add(4, 3) == 79); +} + +fn add(a: u32, b: u32) u32 { + const x: u32 = if (a < b) blk: { + const c = a + b; // 7 + const d = a + c; // 10 + const e = d + b; // 14 + const f = d + e; // 24 + const g = e + f; // 38 + const h = f + g; // 62 + const i = g + h; // 100 + const j = i + d; // 110 + const k = i + j; // 210 + const l = k + c; // 217 + const m = l + d; // 227 + const n = m + e; // 241 + const o = n + f; // 265 + const p = o + g; // 303 + const q = p + h; // 365 + const r = q + i; // 465 + const s = r + j; // 575 + const t = s + k; // 785 + break :blk t; + } else blk: { + const t = b + b + a; // 10 + const c = a + t; // 14 + const d = c + t; // 24 + const e = d + t; // 34 + const f = e + t; // 44 + const g = f + t; // 54 + const h = c + g; // 68 + break :blk h + b; // 71 + }; + const y = x + a; // 788, 75 + const z = y + a; // 791, 79 + return z; +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/x86_64-macos/assert_function.13.zig b/test/incremental/x86_64-macos/assert_function.13.zig new file mode 100644 index 0000000000..240abf0108 --- /dev/null +++ b/test/incremental/x86_64-macos/assert_function.13.zig @@ -0,0 +1,19 @@ +pub fn main() void { + const ignore = + \\ cool thx + \\ + ; + _ = ignore; + add('ぁ', '\x03'); +} + +fn add(a: u32, b: u32) void { + assert(a + b == 12356); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/x86_64-macos/assert_function.14.zig b/test/incremental/x86_64-macos/assert_function.14.zig new file mode 100644 index 0000000000..d25100dcce --- /dev/null +++ b/test/incremental/x86_64-macos/assert_function.14.zig @@ -0,0 +1,17 @@ +pub fn main() void { + add(aa, bb); +} + +const aa = 'ぁ'; +const bb = '\x03'; + +fn add(a: u32, b: u32) void { + assert(a + b == 12356); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/x86_64-macos/assert_function.15.zig b/test/incremental/x86_64-macos/assert_function.15.zig new file mode 100644 index 0000000000..33ae1ed5af --- /dev/null +++ b/test/incremental/x86_64-macos/assert_function.15.zig @@ -0,0 +1,10 @@ +pub fn main() void { + assert("hello"[0] == 'h'); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/x86_64-macos/assert_function.16.zig b/test/incremental/x86_64-macos/assert_function.16.zig new file mode 100644 index 0000000000..eef1136423 --- /dev/null +++ b/test/incremental/x86_64-macos/assert_function.16.zig @@ -0,0 +1,11 @@ +const hello = "hello".*; +pub fn main() void { + assert(hello[1] == 'e'); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/x86_64-macos/assert_function.17.zig b/test/incremental/x86_64-macos/assert_function.17.zig new file mode 100644 index 0000000000..ac9dce3079 --- /dev/null +++ b/test/incremental/x86_64-macos/assert_function.17.zig @@ -0,0 +1,11 @@ +pub fn main() void { + var i: u64 = 0xFFEEDDCCBBAA9988; + assert(i == 0xFFEEDDCCBBAA9988); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/assert_function.7.zig b/test/incremental/x86_64-macos/assert_function.18.zig similarity index 80% rename from test/incremental/assert_function.7.zig rename to test/incremental/x86_64-macos/assert_function.18.zig index f2f3ffcc3d..31cf1207f3 100644 --- a/test/incremental/assert_function.7.zig +++ b/test/incremental/x86_64-macos/assert_function.18.zig @@ -3,9 +3,7 @@ const builtin = @import("builtin"); extern "c" fn write(usize, usize, usize) usize; pub fn main() void { - var i: u32 = 0; - while (i < 4) : (i += 1) print(); - assert(i == 4); + for ("hello") |_| print(); } fn print() void { @@ -27,14 +25,11 @@ fn print() void { } } -pub fn assert(ok: bool) void { - if (!ok) unreachable; // assertion failure -} - // run // // hello // hello // hello // hello +// hello // diff --git a/test/incremental/x86_64-macos/assert_function.2.zig b/test/incremental/x86_64-macos/assert_function.2.zig new file mode 100644 index 0000000000..8c1c510486 --- /dev/null +++ b/test/incremental/x86_64-macos/assert_function.2.zig @@ -0,0 +1,21 @@ +pub fn main() void { + add(3, 4); +} + +fn add(a: u32, b: u32) void { + const c = a + b; // 7 + const d = a + c; // 10 + const e = d + b; // 14 + const f = d + e; // 24 + const g = e + f; // 38 + const h = f + g; // 62 + const i = g + h; // 100 + assert(i == 100); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/x86_64-macos/assert_function.3.zig b/test/incremental/x86_64-macos/assert_function.3.zig new file mode 100644 index 0000000000..a6829f8e02 --- /dev/null +++ b/test/incremental/x86_64-macos/assert_function.3.zig @@ -0,0 +1,22 @@ +pub fn main() void { + add(3, 4); +} + +fn add(a: u32, b: u32) void { + const c = a + b; // 7 + const d = a + c; // 10 + const e = d + b; // 14 + const f = d + e; // 24 + const g = e + f; // 38 + const h = f + g; // 62 + const i = g + h; // 100 + const j = i + d; // 110 + assert(j == 110); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/x86_64-macos/assert_function.4.zig b/test/incremental/x86_64-macos/assert_function.4.zig new file mode 100644 index 0000000000..69df4354c3 --- /dev/null +++ b/test/incremental/x86_64-macos/assert_function.4.zig @@ -0,0 +1,15 @@ +pub fn main() void { + assert(add(3, 4) == 7); + assert(add(20, 10) == 30); +} + +fn add(a: u32, b: u32) u32 { + return a + b; +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/x86_64-macos/assert_function.5.zig b/test/incremental/x86_64-macos/assert_function.5.zig new file mode 100644 index 0000000000..89f3f7df4f --- /dev/null +++ b/test/incremental/x86_64-macos/assert_function.5.zig @@ -0,0 +1,19 @@ +pub fn main() void { + assert(add(3, 4) == 7); + assert(add(20, 10) == 30); +} + +fn add(a: u32, b: u32) u32 { + var x: u32 = undefined; + x = 0; + x += a; + x += b; + return x; +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/x86_64-macos/assert_function.6.zig b/test/incremental/x86_64-macos/assert_function.6.zig new file mode 100644 index 0000000000..1b1b75e68e --- /dev/null +++ b/test/incremental/x86_64-macos/assert_function.6.zig @@ -0,0 +1,9 @@ +pub fn main() void { + const a: u32 = 2; + const b: ?u32 = a; + const c = b.?; + if (c != 2) unreachable; +} + +// run +// diff --git a/test/incremental/x86_64-macos/assert_function.7.zig b/test/incremental/x86_64-macos/assert_function.7.zig new file mode 100644 index 0000000000..27ba37029a --- /dev/null +++ b/test/incremental/x86_64-macos/assert_function.7.zig @@ -0,0 +1,23 @@ +extern "c" fn write(usize, usize, usize) usize; + +pub fn main() void { + var i: u32 = 0; + while (i < 4) : (i += 1) print(); + assert(i == 4); +} + +fn print() void { + _ = write(1, @ptrToInt("hello\n"), 6); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// +// hello +// hello +// hello +// hello +// diff --git a/test/incremental/x86_64-macos/assert_function.8.zig b/test/incremental/x86_64-macos/assert_function.8.zig new file mode 100644 index 0000000000..801cd2e3be --- /dev/null +++ b/test/incremental/x86_64-macos/assert_function.8.zig @@ -0,0 +1,19 @@ +extern "c" fn write(usize, usize, usize) usize; + +pub fn main() void { + var i: u32 = 0; + inline while (i < 4) : (i += 1) print(); + assert(i == 4); +} + +fn print() void { + _ = write(1, @ptrToInt("hello\n"), 6); +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// error +// +// :5:21: error: unable to resolve comptime value diff --git a/test/incremental/x86_64-macos/assert_function.9.zig b/test/incremental/x86_64-macos/assert_function.9.zig new file mode 100644 index 0000000000..c754bb7711 --- /dev/null +++ b/test/incremental/x86_64-macos/assert_function.9.zig @@ -0,0 +1,22 @@ +pub fn main() void { + assert(add(3, 4) == 20); +} + +fn add(a: u32, b: u32) u32 { + const x: u32 = blk: { + const c = a + b; // 7 + const d = a + c; // 10 + const e = d + b; // 14 + break :blk e; + }; + const y = x + a; // 17 + const z = y + a; // 20 + return z; +} + +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +// run +// diff --git a/test/incremental/x86_64-macos/comptime_var.0.zig b/test/incremental/x86_64-macos/comptime_var.0.zig new file mode 100644 index 0000000000..da82b35c6f --- /dev/null +++ b/test/incremental/x86_64-macos/comptime_var.0.zig @@ -0,0 +1,12 @@ +pub fn main() void { + var a: u32 = 0; + comptime var b: u32 = 0; + if (a == 0) b = 3; +} + +// error +// output_mode=Exe +// target=x86_64-macos +// +// :4:21: error: store to comptime variable depends on runtime condition +// :4:11: note: runtime condition here diff --git a/test/incremental/x86_64-macos/comptime_var.1.zig b/test/incremental/x86_64-macos/comptime_var.1.zig new file mode 100644 index 0000000000..efc51aafe3 --- /dev/null +++ b/test/incremental/x86_64-macos/comptime_var.1.zig @@ -0,0 +1,13 @@ +pub fn main() void { + var a: u32 = 0; + comptime var b: u32 = 0; + switch (a) { + 0 => {}, + else => b = 3, + } +} + +// error +// +// :6:21: error: store to comptime variable depends on runtime condition +// :4:13: note: runtime condition here diff --git a/test/incremental/x86_64-macos/comptime_var.2.zig b/test/incremental/x86_64-macos/comptime_var.2.zig new file mode 100644 index 0000000000..abd34255cd --- /dev/null +++ b/test/incremental/x86_64-macos/comptime_var.2.zig @@ -0,0 +1,17 @@ +extern "c" fn write(usize, usize, usize) usize; + +pub fn main() void { + comptime var len: u32 = 5; + print(len); + len += 9; + print(len); +} + +fn print(len: usize) void { + _ = write(1, @ptrToInt("Hello, World!\n"), len); +} + +// run +// +// HelloHello, World! +// diff --git a/test/incremental/x86_64-macos/comptime_var.3.zig b/test/incremental/x86_64-macos/comptime_var.3.zig new file mode 100644 index 0000000000..d4e6d85d9d --- /dev/null +++ b/test/incremental/x86_64-macos/comptime_var.3.zig @@ -0,0 +1,10 @@ +comptime { + var x: i32 = 1; + x += 1; + if (x != 1) unreachable; +} +pub fn main() void {} + +// error +// +// :4:17: error: unable to resolve comptime value diff --git a/test/incremental/x86_64-macos/comptime_var.4.zig b/test/incremental/x86_64-macos/comptime_var.4.zig new file mode 100644 index 0000000000..74da6ef448 --- /dev/null +++ b/test/incremental/x86_64-macos/comptime_var.4.zig @@ -0,0 +1,9 @@ +pub fn main() void { + comptime var i: u64 = 0; + while (i < 5) : (i += 1) {} +} + +// error +// +// :3:24: error: cannot store to comptime variable in non-inline loop +// :3:5: note: non-inline loop here diff --git a/test/incremental/x86_64-macos/comptime_var.5.zig b/test/incremental/x86_64-macos/comptime_var.5.zig new file mode 100644 index 0000000000..76a06f3d4b --- /dev/null +++ b/test/incremental/x86_64-macos/comptime_var.5.zig @@ -0,0 +1,15 @@ +pub fn main() void { + var a: u32 = 0; + if (a == 0) { + comptime var b: u32 = 0; + b = 1; + } +} +comptime { + var x: i32 = 1; + x += 1; + if (x != 2) unreachable; +} + +// run +// diff --git a/test/incremental/x86_64-macos/comptime_var.6.zig b/test/incremental/x86_64-macos/comptime_var.6.zig new file mode 100644 index 0000000000..381e609bd1 --- /dev/null +++ b/test/incremental/x86_64-macos/comptime_var.6.zig @@ -0,0 +1,15 @@ +extern "c" fn write(usize, usize, usize) usize; + +pub fn main() void { + comptime var i: u64 = 2; + inline while (i < 6) : (i += 1) { + print(i); + } +} +fn print(len: usize) void { + _ = write(1, @ptrToInt("Hello"), len); +} + +// run +// +// HeHelHellHello From 3c19f694d92c5a3d7f6e3a6d857252406df0d6e3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 28 Apr 2022 12:10:56 +0200 Subject: [PATCH 1291/2031] test: fix incorrect error loc in assert_function x86_64-linux test --- test/incremental/x86_64-linux/assert_function.8.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/incremental/x86_64-linux/assert_function.8.zig b/test/incremental/x86_64-linux/assert_function.8.zig index 4ce3ec0844..ae618cc063 100644 --- a/test/incremental/x86_64-linux/assert_function.8.zig +++ b/test/incremental/x86_64-linux/assert_function.8.zig @@ -21,4 +21,4 @@ pub fn assert(ok: bool) void { // error // -// :5:21: error: unable to resolve comptime value +// :3:21: error: unable to resolve comptime value From c8d0fb0b212f2c618321caeeac315c1792b06035 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 28 Apr 2022 12:11:49 +0200 Subject: [PATCH 1292/2031] test: migrate stage2 independent compile errors to test manifest parser --- src/test.zig | 6 +++--- .../constant_inside_comptime_function_has_compile_error.zig | 2 +- test/compile_errors/stage2/duplicate-unused_labels.zig | 2 +- test/compile_errors/stage2/embed_outside_package.zig | 2 +- test/compile_errors/stage2/import_outside_package.zig | 2 +- test/compile_errors/stage2/out_of_bounds_index.zig | 2 +- test/compile_errors/stage2/slice_of_null_pointer.zig | 2 +- test/compile_errors/stage2/struct_duplicate_field_name.zig | 2 +- .../stage2/union_access_of_inactive_field.zig | 4 ++-- test/compile_errors/stage2/union_duplicate_enum_field.zig | 4 ++-- .../stage2/union_duplicate_field_definition.zig | 2 +- test/compile_errors/stage2/union_enum_field_missing.zig | 2 +- test/compile_errors/stage2/union_extra_field.zig | 2 +- .../stage2/union_runtime_coercion_from_enum.zig | 2 +- 14 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/test.zig b/src/test.zig index 0377d8823f..3579370c04 100644 --- a/src/test.zig +++ b/src/test.zig @@ -42,12 +42,12 @@ test { defer compile_errors_dir.close(); { - var stage2_dir = try compile_errors_dir.openDir("stage2", .{ .iterate = true }); - defer stage2_dir.close(); + var dir = try compile_errors_dir.openDir("stage2", .{ .iterate = true }); + defer dir.close(); // TODO make this incremental once the bug is solved that it triggers // See: https://github.com/ziglang/zig/issues/11344 - ctx.addErrorCasesFromDir("stage2", stage2_dir, .stage2, .Obj, false, .independent); + ctx.addTestCasesFromDir(dir, .independent); } if (!skip_stage1) { diff --git a/test/compile_errors/stage2/constant_inside_comptime_function_has_compile_error.zig b/test/compile_errors/stage2/constant_inside_comptime_function_has_compile_error.zig index 969b73713f..98d6104670 100644 --- a/test/compile_errors/stage2/constant_inside_comptime_function_has_compile_error.zig +++ b/test/compile_errors/stage2/constant_inside_comptime_function_has_compile_error.zig @@ -14,7 +14,7 @@ export fn entry() void { _ = allocator; } -// constant inside comptime function has compile error +// error // // :4:5: error: unreachable code // :4:25: note: control flow is diverted here diff --git a/test/compile_errors/stage2/duplicate-unused_labels.zig b/test/compile_errors/stage2/duplicate-unused_labels.zig index 82afe4e854..44ab48480d 100644 --- a/test/compile_errors/stage2/duplicate-unused_labels.zig +++ b/test/compile_errors/stage2/duplicate-unused_labels.zig @@ -17,7 +17,7 @@ comptime { blk: for(@as([0]void, undefined)) |_| {} } -// duplicate/unused labels +// error // // :2:12: error: redefinition of label 'blk' // :2:5: note: previous definition here diff --git a/test/compile_errors/stage2/embed_outside_package.zig b/test/compile_errors/stage2/embed_outside_package.zig index 8df6b3d9af..9cf1a8b905 100644 --- a/test/compile_errors/stage2/embed_outside_package.zig +++ b/test/compile_errors/stage2/embed_outside_package.zig @@ -2,6 +2,6 @@ export fn a() usize { return @embedFile("/root/foo").len; } -// embed outside package +// error // //:2:23: error: embed of file outside package path: '/root/foo' diff --git a/test/compile_errors/stage2/import_outside_package.zig b/test/compile_errors/stage2/import_outside_package.zig index f9de9202de..6b70778754 100644 --- a/test/compile_errors/stage2/import_outside_package.zig +++ b/test/compile_errors/stage2/import_outside_package.zig @@ -2,6 +2,6 @@ export fn a() usize { return @import("../../above.zig").len; } -// import outside package +// error // // :2:20: error: import of file outside package path: '../../above.zig' diff --git a/test/compile_errors/stage2/out_of_bounds_index.zig b/test/compile_errors/stage2/out_of_bounds_index.zig index 3c34bb5d0f..614c445d46 100644 --- a/test/compile_errors/stage2/out_of_bounds_index.zig +++ b/test/compile_errors/stage2/out_of_bounds_index.zig @@ -20,7 +20,7 @@ comptime { _ = slice; } -// out of bounds indexing +// error // // :4:26: error: end index 6 out of bounds for slice of length 4 +1 (sentinel) // :9:22: error: end index 6 out of bounds for array of length 4 +1 (sentinel) diff --git a/test/compile_errors/stage2/slice_of_null_pointer.zig b/test/compile_errors/stage2/slice_of_null_pointer.zig index 1e3f0d6aee..a33a904842 100644 --- a/test/compile_errors/stage2/slice_of_null_pointer.zig +++ b/test/compile_errors/stage2/slice_of_null_pointer.zig @@ -5,6 +5,6 @@ comptime { _ = y; } -// slice of null C pointer +// error // // :4:14: error: slice of null pointer diff --git a/test/compile_errors/stage2/struct_duplicate_field_name.zig b/test/compile_errors/stage2/struct_duplicate_field_name.zig index 274dce4e4a..e7bed78d06 100644 --- a/test/compile_errors/stage2/struct_duplicate_field_name.zig +++ b/test/compile_errors/stage2/struct_duplicate_field_name.zig @@ -8,7 +8,7 @@ export fn entry() void { _ = s; } -// duplicate struct field name +// error // // :3:5: error: duplicate struct field: 'foo' // :2:5: note: other field here diff --git a/test/compile_errors/stage2/union_access_of_inactive_field.zig b/test/compile_errors/stage2/union_access_of_inactive_field.zig index 34fa661d79..881279e1c3 100644 --- a/test/compile_errors/stage2/union_access_of_inactive_field.zig +++ b/test/compile_errors/stage2/union_access_of_inactive_field.zig @@ -3,12 +3,12 @@ const U = union { b: u64, }; comptime { - var u: U = .{.a = {}}; + var u: U = .{ .a = {} }; const v = u.b; _ = v; } -// access of inactive union field +// error // // :7:16: error: access of union field 'b' while field 'a' is active // :1:11: note: union declared here diff --git a/test/compile_errors/stage2/union_duplicate_enum_field.zig b/test/compile_errors/stage2/union_duplicate_enum_field.zig index 9044f9e97e..5a08256edb 100644 --- a/test/compile_errors/stage2/union_duplicate_enum_field.zig +++ b/test/compile_errors/stage2/union_duplicate_enum_field.zig @@ -1,4 +1,4 @@ -const E = enum {a, b}; +const E = enum { a, b }; const U = union(E) { a: u32, a: u32, @@ -9,7 +9,7 @@ export fn foo() void { _ = u; } -// union with enum and duplicate fields +// error // // :4:5: error: duplicate union field: 'a' // :3:5: note: other field here diff --git a/test/compile_errors/stage2/union_duplicate_field_definition.zig b/test/compile_errors/stage2/union_duplicate_field_definition.zig index 6ad2ae4f4e..7aab7c4695 100644 --- a/test/compile_errors/stage2/union_duplicate_field_definition.zig +++ b/test/compile_errors/stage2/union_duplicate_field_definition.zig @@ -8,7 +8,7 @@ export fn entry() void { _ = u; } -// duplicate union field name +// error // // :3:5: error: duplicate union field: 'foo' // :2:5: note: other field here diff --git a/test/compile_errors/stage2/union_enum_field_missing.zig b/test/compile_errors/stage2/union_enum_field_missing.zig index b29ca83d3a..638c9fec26 100644 --- a/test/compile_errors/stage2/union_enum_field_missing.zig +++ b/test/compile_errors/stage2/union_enum_field_missing.zig @@ -13,7 +13,7 @@ export fn entry() usize { return @sizeOf(U); } -// enum field missing in union +// error // // :7:1: error: enum field(s) missing in union // :4:5: note: field 'c' missing, declared here diff --git a/test/compile_errors/stage2/union_extra_field.zig b/test/compile_errors/stage2/union_extra_field.zig index e8ba581aad..cdfa482208 100644 --- a/test/compile_errors/stage2/union_extra_field.zig +++ b/test/compile_errors/stage2/union_extra_field.zig @@ -13,7 +13,7 @@ export fn entry() usize { return @sizeOf(U); } -// union extra field +// error // // :6:1: error: enum 'tmp.E' has no field named 'd' // :1:11: note: enum declared here diff --git a/test/compile_errors/stage2/union_runtime_coercion_from_enum.zig b/test/compile_errors/stage2/union_runtime_coercion_from_enum.zig index f7e96834fd..56cd2db83b 100644 --- a/test/compile_errors/stage2/union_runtime_coercion_from_enum.zig +++ b/test/compile_errors/stage2/union_runtime_coercion_from_enum.zig @@ -14,7 +14,7 @@ export fn doTheTest() u64 { return u.b; } -// runtime coercion from enum to union +// error // // :13:19: error: runtime coercion from enum 'tmp.E' to union 'tmp.U' which has non-void fields // :6:5: note: field 'a' has type 'u32' From 62625d9d9518644bcf4fa94e1d5c67edde22b91b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 28 Apr 2022 14:44:34 +0200 Subject: [PATCH 1293/2031] test: migrate stage1 compile error tests to updated test manifest --- src/test.zig | 35 +-- test/cases.zig | 5 - test/compile_errors.zig | 218 ------------------ .../stage1/exe/main_missing_name.zig | 5 +- .../exe/missing_main_fn_in_executable.zig | 5 +- .../stage1/exe/private_main_fn.zig | 5 +- ..._ABI_compatible_type_or_has_align_attr.zig | 4 +- .../stage1/obj/C_pointer_to_anyopaque.zig | 4 +- .../stage1/obj/Frame_of_generic_function.zig | 4 +- ...nus_for_unsigned_types_a_compile_error.zig | 4 +- ...e_6823_dont_allow_._to_be_followed_by_.zig | 4 +- ...5_windows_tcp_server_compilation_error.zig | 4 +- ...ccess_non-existent_member_of_error_set.zig | 4 +- ..._runtime_parameter_from_outer_function.zig | 4 +- .../obj/add_assign_on_undefined_value.zig | 4 +- .../stage1/obj/add_on_undefined_value.zig | 4 +- .../add_overflow_in_function_evaluation.zig | 4 +- .../add_wrap_assign_on_undefined_value.zig | 4 +- .../obj/add_wrap_on_undefined_value.zig | 4 +- .../stage1/obj/addition_with_non_numbers.zig | 4 +- .../stage1/obj/address_of_number_literal.zig | 4 +- .../alignCast_expects_pointer_or_slice.zig | 4 +- ...r_function_pointers_is_a_compile_error.zig | 9 + .../obj/aligned_variable_of_zero-bit_type.zig | 4 +- .../obj/alignment_of_enum_field_specified.zig | 4 +- .../stage1/obj/ambiguous_decl_reference.zig | 4 +- .../stage1/obj/and_on_undefined_value.zig | 4 +- .../stage1/obj/array_access_of_non_array.zig | 4 +- .../stage1/obj/array_access_of_type.zig | 4 +- .../array_access_of_undeclared_identifier.zig | 4 +- .../array_access_with_non_integer_index.zig | 4 +- .../array_concatenation_with_wrong_type.zig | 4 +- .../obj/array_in_c_exported_function.zig | 4 +- .../stage1/obj/asm_at_compile_time.zig | 4 +- .../assign_inline_fn_to_non-comptime_var.zig | 4 +- .../assign_null_to_non-optional_pointer.zig | 4 +- .../obj/assign_through_constant_pointer.zig | 4 +- .../obj/assign_through_constant_slice.zig | 4 +- .../stage1/obj/assign_to_constant_field.zig | 4 +- .../obj/assign_to_constant_variable.zig | 4 +- .../obj/assign_to_invalid_dereference.zig | 4 +- .../obj/assign_too_big_number_to_u16.zig | 4 +- .../stage1/obj/assign_unreachable.zig | 4 +- ...th_a_function_that_returns_an_optional.zig | 4 +- ...sync_function_depends_on_its_own_frame.zig | 4 +- ...on_indirectly_depends_on_its_own_frame.zig | 4 +- ...rings_of_atomicStore_Acquire_or_AcqRel.zig | 4 +- ..._cmpxchg-failure_stricter_than_success.zig | 4 +- ..._cmpxchg-success_Monotonic_or_stricter.zig | 4 +- ...orderings_of_fence_Acquire_or_stricter.zig | 4 +- .../obj/atomicrmw_with_bool_op_not_.Xchg.zig | 4 +- .../obj/atomicrmw_with_enum_op_not_.Xchg.zig | 4 +- ...w_with_float_op_not_.Xchg_.Add_or_.Sub.zig | 4 +- .../attempt_to_cast_enum_literal_to_error.zig | 4 +- ...ver_comptime_variable_from_outer_scope.zig | 4 +- .../attempt_to_create_17_bit_float_type.zig | 4 +- ...n-integer_non-float_or_non-vector_type.zig | 4 +- ...attempt_to_use_0_bit_type_in_extern_fn.zig | 4 +- .../stage1/obj/attempted_double_ampersand.zig | 4 +- ...ttempted_double_pipe_on_boolean_values.zig | 4 +- ..._implicit_cast_from_T_to_slice_const_T.zig | 4 +- ...cit_cast_from_const_T_to_array_len_1_T.zig | 4 +- ...d_implicit_cast_from_const_T_to_sliceT.zig | 4 +- .../stage1/obj/bad_alignCast_at_comptime.zig | 4 +- .../stage1/obj/bad_alignment_in_asynccall.zig | 12 + ...licit_cast_from_array_pointer_to_slice.zig | 4 +- .../stage1/obj/bad_alignment_type.zig | 4 +- ..._function_which_references_local_const.zig | 4 +- test/compile_errors/stage1/obj/bad_import.zig | 4 +- .../stage1/obj/bad_usage_of_call.zig | 4 +- .../obj/bin_and_assign_on_undefined_value.zig | 4 +- .../stage1/obj/bin_and_on_undefined_value.zig | 4 +- .../stage1/obj/bin_not_on_undefined_value.zig | 4 +- .../obj/bin_or_assign_on_undefined_value.zig | 4 +- .../stage1/obj/bin_or_on_undefined_value.zig | 4 +- .../obj/bin_xor_assign_on_undefined_value.zig | 4 +- .../stage1/obj/bin_xor_on_undefined_value.zig | 4 +- .../obj/binary_not_on_number_literal.zig | 4 +- ...tCast_same_size_but_bit_count_mismatch.zig | 4 +- .../stage1/obj/bitCast_to_enum_type.zig | 4 +- ...h_different_sizes_inside_an_expression.zig | 4 +- ...t_shifting_only_works_on_integer_types.zig | 4 +- .../stage1/obj/bogus_compile_var.zig | 4 +- .../stage1/obj/bogus_method_call_on_slice.zig | 4 +- .../obj/bool_not_on_undefined_value.zig | 4 +- .../stage1/obj/branch_on_undefined_value.zig | 4 +- .../stage1/obj/cImport_with_bogus_include.zig | 4 +- .../stage1/obj/call_assigned_to_constant.zig | 4 +- ...l_with_new_stack_on_unsupported_target.zig | 11 + ...aapcs_aapcsvfp_on_unsupported_platform.zig | 11 + ...conv_interrupt_on_unsupported_platform.zig | 7 + ...allconv_signal_on_unsupported_platform.zig | 7 + ...all_thiscall_on_unsupported_platform-0.zig | 23 ++ ...all_thiscall_on_unsupported_platform-1.zig | 11 + ...onv_vectorcall_on_unsupported_platform.zig | 7 + ...generic_function_only_known_at_runtime.zig | 4 +- ...function_with_naked_calling_convention.zig | 4 +- ...ction_passing_array_instead_of_pointer.zig | 4 +- .../cannot_break_out_of_defer_expression.zig | 4 +- ...annot_continue_out_of_defer_expression.zig | 4 +- ..._prong_with_incompatible_payload_types.zig | 4 +- ...um_literal_to_enum_but_it_doesnt_match.zig | 4 +- ...et_to_error_union_of_smaller_error_set.zig | 4 +- .../cast_global_error_set_to_error_set.zig | 4 +- ...cast_negative_integer_literal_to_usize.zig | 4 +- ...ast_negative_value_to_unsigned_integer.zig | 4 +- .../stage1/obj/cast_unreachable.zig | 4 +- ..._bit_offset_pointer_to_regular_pointer.zig | 4 +- .../stage1/obj/catch_on_undefined_value.zig | 4 +- .../obj/chained_comparison_operators.zig | 4 +- .../stage1/obj/cmpxchg_with_float.zig | 4 +- .../colliding_invalid_top_level_functions.zig | 4 +- ...nal_to_non-optional_with_invalid_types.zig | 4 +- ...ng_a_non-optional_pointer_against_null.zig | 4 +- ...nst_undefined_produces_undefined_value.zig | 4 +- ...parison_operators_with_undefined_value.zig | 4 +- ...rison_with_error_union_and_error_value.zig | 4 +- .../obj/compile-time_division_by_zero.zig | 4 +- ...ompile-time_remainder_division_by_zero.zig | 4 +- ...traceback_of_references_that_caused_it.zig | 4 +- ..._tagged_enum_doesnt_crash_the_compiler.zig | 4 +- ...ompile_error_in_struct_init_expression.zig | 4 +- ...ting_return_type_of_inferred_error_set.zig | 4 +- .../compile_errors/stage1/obj/compile_log.zig | 4 +- ...mpile_log_a_pointer_to_an_opaque_value.zig | 4 +- ...ction_which_must_be_comptime_evaluated.zig | 4 +- ...nt_warning_deduplication_in_generic_fn.zig | 4 +- .../obj/compile_time_division_by_zero.zig | 4 +- ...st_enum_to_union_but_field_has_payload.zig | 4 +- ...comptime_continue_inside_runtime_catch.zig | 4 +- ...mptime_continue_inside_runtime_if_bool.zig | 4 +- ...ptime_continue_inside_runtime_if_error.zig | 4 +- ...me_continue_inside_runtime_if_optional.zig | 4 +- ...omptime_continue_inside_runtime_switch.zig | 4 +- ...ime_continue_inside_runtime_while_bool.zig | 4 +- ...me_continue_inside_runtime_while_error.zig | 4 +- ...continue_inside_runtime_while_optional.zig | 4 +- .../obj/comptime_float_in_asm_input.zig | 4 +- .../obj/comptime_implicit_cast_f64_to_f32.zig | 4 +- .../stage1/obj/comptime_int_in_asm_input.zig | 4 +- .../comptime_ptrcast_of_zero-sized_type.zig | 4 +- ...atch_memory_at_target_index_terminated.zig | 4 +- ...ch_memory_at_target_index_unterminated.zig | 4 +- ...entinel_does_not_match_target-sentinel.zig | 4 +- ...e-sentinel_is_out_of_bounds_terminated.zig | 4 +- ...sentinel_is_out_of_bounds_unterminated.zig | 4 +- .../comptime_slice_of_an_undefined_slice.zig | 4 +- ...lice_of_undefined_pointer_non-zero_len.zig | 4 +- .../comptime_struct_field_no_init_value.zig | 4 +- .../obj/const_frame_cast_to_anyframe.zig | 4 +- ...const_is_a_statement_not_an_expression.zig | 4 +- .../obj/container_init_with_non-type.zig | 4 +- ...trol_flow_uses_comptime_var_at_runtime.zig | 4 +- ...ntrol_reaches_end_of_non-void_function.zig | 4 +- .../stage1/obj/declaration_between_fields.zig | 4 +- ...e_as_primitive_must_use_special_syntax.zig | 4 +- .../obj/deduplicate_undeclared_identifier.zig | 4 +- .../stage1/obj/deref_on_undefined_value.zig | 4 +- .../obj/deref_slice_and_get_len_field.zig | 4 +- .../stage1/obj/dereference_an_array.zig | 4 +- .../dereference_unknown_length_pointer.zig | 4 +- .../stage1/obj/direct_struct_loop.zig | 4 +- ...edding_opaque_type_in_struct_and_union.zig | 4 +- ...ted_pointer_to_null-terminated_pointer.zig | 4 +- .../stage1/obj/discarding_error_value.zig | 4 +- .../obj/div_assign_on_undefined_value.zig | 4 +- .../stage1/obj/div_on_undefined_value.zig | 4 +- .../stage1/obj/division_by_zero.zig | 4 +- ...licit_cast_double_pointer_to_anyopaque.zig | 4 +- .../double_optional_on_main_return_value.zig | 4 +- .../obj/duplicate_boolean_switch_value.zig | 4 +- .../stage1/obj/duplicate_enum_field.zig | 4 +- .../stage1/obj/duplicate_error_in_switch.zig | 4 +- .../duplicate_error_value_in_error_set.zig | 4 +- ...icate_field_in_struct_value_expression.zig | 4 +- .../stage1/obj/duplicate_struct_field.zig | 4 +- .../stage1/obj/duplicate_union_field.zig | 4 +- .../stage1/obj/embedFile_with_bogus_file.zig | 4 +- .../stage1/obj/empty_for_loop_body.zig | 4 +- .../stage1/obj/empty_if_body.zig | 4 +- .../stage1/obj/empty_switch_on_an_integer.zig | 4 +- .../stage1/obj/empty_while_loop_body.zig | 4 +- .../endless_loop_in_function_evaluation.zig | 4 +- .../obj/enum_field_value_references_enum.zig | 4 +- ...field_count_range_but_not_matching_tag.zig | 4 +- .../stage1/obj/enum_value_already_taken.zig | 4 +- .../stage1/obj/enum_with_0_fields.zig | 4 +- ...eclarations_unavailable_for_reify_type.zig | 4 +- ...uality_but_sets_have_no_common_members.zig | 4 +- .../obj/error_not_handled_in_switch.zig | 4 +- ...for_function_parameter_incompatibility.zig | 4 +- ..._union_operator_with_non_error_set_LHS.zig | 4 +- .../obj/error_when_evaluating_return_type.zig | 4 +- .../exceeded_maximum_bit_width_of_integer.zig | 4 +- ...ger_when_there_is_a_fraction_component.zig | 4 +- ..._known_at_comptime_violates_error_sets.zig | 4 +- ...xplicitly_casting_non_tag_type_to_enum.zig | 4 +- ...xport_function_with_comptime_parameter.zig | 4 +- .../stage1/obj/export_generic_function.zig | 4 +- .../stage1/obj/exported_async_function.zig | 4 +- ...enum_without_explicit_integer_tag_type.zig | 4 +- .../obj/extern_function_pointer_mismatch.zig | 4 +- ...xtern_function_with_comptime_parameter.zig | 4 +- ...mpatible_but_inferred_integer_tag_type.zig | 4 +- ...non-extern-compatible_integer_tag_type.zig | 4 +- .../obj/extern_union_field_missing_type.zig | 4 +- .../obj/extern_union_given_enum_tag_type.zig | 4 +- .../obj/extern_variable_has_no_type.zig | 4 +- .../obj/fieldParentPtr-bad_field_name.zig | 4 +- ...comptime_field_ptr_not_based_on_struct.zig | 4 +- ...ldParentPtr-comptime_wrong_field_index.zig | 4 +- ...ParentPtr-field_pointer_is_not_pointer.zig | 4 +- .../stage1/obj/fieldParentPtr-non_struct.zig | 4 +- .../obj/field_access_of_opaque_type.zig | 4 +- .../stage1/obj/field_access_of_slices.zig | 4 +- ...field_access_of_unknown_length_pointer.zig | 4 +- .../obj/field_type_supplied_in_an_enum.zig | 4 +- .../stage1/obj/floatToInt_comptime_safety.zig | 4 +- .../obj/float_literal_too_large_error.zig | 4 +- ...float_literal_too_small_error_denormal.zig | 4 +- .../obj/for_loop_body_expression_ignored.zig | 4 +- ..._called_outside_of_function_definition.zig | 4 +- .../obj/frame_causes_function_to_be_async.zig | 4 +- .../obj/function_alignment_non_power_of_2.zig | 4 +- ...nction_call_assigned_to_incorrect_type.zig | 4 +- .../obj/function_parameter_is_opaque.zig | 4 +- .../obj/function_prototype_with_no_body.zig | 4 +- .../obj/function_returning_opaque_type.zig | 4 +- ..._ccc_indirectly_calling_async_function.zig | 4 +- .../obj/function_with_invalid_return_type.zig | 4 +- ...h_non-extern_non-packed_enum_parameter.zig | 4 +- ...non-extern_non-packed_struct_parameter.zig | 4 +- ..._non-extern_non-packed_union_parameter.zig | 4 +- ..._as_parameter_without_comptime_keyword.zig | 4 +- ...nction_call_assigned_to_incorrect_type.zig | 4 +- ..._instance_with_non-constant_expression.zig | 4 +- ...generic_function_returning_opaque_type.zig | 4 +- ...n_where_return_type_is_self-referenced.zig | 4 +- ...obal_variable_alignment_non_power_of_2.zig | 4 +- ...nitializer_must_be_constant_expression.zig | 4 +- .../stage1/obj/hasDecl_with_non-container.zig | 4 +- .../obj/if_condition_is_bool_not_int.zig | 4 +- .../ignored_assert-err-ok_return_value.zig | 4 +- .../obj/ignored_comptime_statement_value.zig | 4 +- .../stage1/obj/ignored_comptime_value.zig | 4 +- .../obj/ignored_deferred_function_call.zig | 4 +- .../obj/ignored_deferred_statement_value.zig | 4 +- ...nored_expression_in_while_continuation.zig | 4 +- .../stage1/obj/ignored_return_value.zig | 4 +- .../stage1/obj/ignored_statement_value.zig | 4 +- .../obj/illegal_comparison_of_types.zig | 4 +- ..._and_Zig_pointer-bad_const-align-child.zig | 4 +- ...icit_cast_const_array_to_mutable_slice.zig | 4 +- ...licit_cast_from_array_to_mutable_slice.zig | 4 +- .../obj/implicit_cast_from_f64_to_f32.zig | 4 +- ...mplicit_cast_of_error_set_not_a_subset.zig | 4 +- ...ers_which_would_mess_up_null_semantics.zig | 4 +- ..._casting_null_c_pointer_to_zig_pointer.zig | 4 +- ...casting_too_big_integers_to_C_pointers.zig | 4 +- ...ing_undefined_c_pointer_to_zig_pointer.zig | 4 +- .../obj/implicit_dependency_on_libc.zig | 11 + .../obj/implicit_semicolon-block_expr.zig | 4 +- .../implicit_semicolon-block_statement.zig | 4 +- ...implicit_semicolon-comptime_expression.zig | 4 +- .../implicit_semicolon-comptime_statement.zig | 4 +- .../stage1/obj/implicit_semicolon-defer.zig | 4 +- .../obj/implicit_semicolon-for_expression.zig | 4 +- .../obj/implicit_semicolon-for_statement.zig | 4 +- ...t_semicolon-if-else-if-else_expression.zig | 4 +- ...it_semicolon-if-else-if-else_statement.zig | 4 +- ...plicit_semicolon-if-else-if_expression.zig | 4 +- ...mplicit_semicolon-if-else-if_statement.zig | 4 +- .../implicit_semicolon-if-else_expression.zig | 4 +- .../implicit_semicolon-if-else_statement.zig | 4 +- .../obj/implicit_semicolon-if_expression.zig | 4 +- .../obj/implicit_semicolon-if_statement.zig | 4 +- .../implicit_semicolon-test_expression.zig | 4 +- .../obj/implicit_semicolon-test_statement.zig | 4 +- ...it_semicolon-while-continue_expression.zig | 4 +- ...cit_semicolon-while-continue_statement.zig | 4 +- .../implicit_semicolon-while_expression.zig | 4 +- .../implicit_semicolon-while_statement.zig | 4 +- .../implicitly_casting_enum_to_tag_type.zig | 4 +- ...mplicitly_increasing_pointer_alignment.zig | 4 +- .../implicitly_increasing_slice_alignment.zig | 4 +- .../obj/import_outside_package_path.zig | 4 +- .../stage1/obj/incompatible_sentinels.zig | 29 +++ .../stage1/obj/incorrect_return_type.zig | 4 +- .../increase_pointer_alignment_in_ptrCast.zig | 4 +- ...indexing_a_undefined_slice_at_comptime.zig | 4 +- .../obj/indexing_an_array_of_size_zero.zig | 4 +- ..._array_of_size_zero_with_runtime_index.zig | 4 +- .../obj/indexing_single-item_pointer.zig | 4 +- ..._recursion_of_async_functions_detected.zig | 4 +- .../stage1/obj/indirect_struct_loop.zig | 4 +- .../obj/inferred_array_size_invalid_here.zig | 4 +- ...nferring_error_set_of_function_pointer.zig | 4 +- .../initializing_array_with_struct_syntax.zig | 4 +- ...an_invalid_struct_that_contains_itself.zig | 4 +- .../obj/intToPtr_with_misaligned_address.zig | 4 +- .../obj/int_to_err_global_invalid_number.zig | 4 +- .../int_to_err_non_global_invalid_number.zig | 4 +- .../stage1/obj/int_to_ptr_of_0_bits.zig | 4 +- .../obj/integer_cast_truncates_bits.zig | 4 +- .../stage1/obj/integer_overflow_error.zig | 4 +- .../stage1/obj/integer_underflow_error.zig | 4 +- .../stage1/obj/invalid_break_expression.zig | 4 +- .../stage1/obj/invalid_builtin_fn.zig | 4 +- ...nvalid_cast_from_integral_type_to_enum.zig | 4 +- ...valid_comparison_for_function_pointers.zig | 4 +- .../obj/invalid_continue_expression.zig | 4 +- .../obj/invalid_deref_on_switch_target.zig | 4 +- .../obj/invalid_empty_unicode_escape.zig | 4 +- .../invalid_exponent_in_float_literal-1.zig | 4 +- .../invalid_exponent_in_float_literal-2.zig | 4 +- .../obj/invalid_field_access_in_comptime.zig | 4 +- ...valid_field_in_struct_value_expression.zig | 4 +- .../stage1/obj/invalid_float_literal.zig | 4 +- .../obj/invalid_legacy_unicode_escape.zig | 4 +- .../stage1/obj/invalid_maybe_type.zig | 4 +- .../obj/invalid_member_of_builtin_enum.zig | 4 +- .../obj/invalid_multiple_dereferences.zig | 4 +- ...invalid_optional_type_in_extern_struct.zig | 4 +- .../obj/invalid_pointer_for_var_type.zig | 4 +- .../stage1/obj/invalid_pointer_syntax.zig | 4 +- .../stage1/obj/invalid_shift_amount_error.zig | 4 +- .../stage1/obj/invalid_struct_field.zig | 4 +- .../invalid_suspend_in_exported_function.zig | 4 +- .../stage1/obj/invalid_type.zig | 4 +- .../obj/invalid_type_used_in_array_type.zig | 4 +- ...nderscore_placement_in_float_literal-1.zig | 4 +- ...derscore_placement_in_float_literal-10.zig | 4 +- ...derscore_placement_in_float_literal-11.zig | 4 +- ...derscore_placement_in_float_literal-12.zig | 4 +- ...derscore_placement_in_float_literal-13.zig | 4 +- ...derscore_placement_in_float_literal-14.zig | 4 +- ...nderscore_placement_in_float_literal-2.zig | 4 +- ...nderscore_placement_in_float_literal-3.zig | 4 +- ...nderscore_placement_in_float_literal-4.zig | 4 +- ...nderscore_placement_in_float_literal-5.zig | 4 +- ...nderscore_placement_in_float_literal-6.zig | 4 +- ...nderscore_placement_in_float_literal-7.zig | 4 +- ...nderscore_placement_in_float_literal-9.zig | 4 +- ..._underscore_placement_in_int_literal-1.zig | 4 +- ..._underscore_placement_in_int_literal-2.zig | 4 +- ..._underscore_placement_in_int_literal-3.zig | 4 +- ..._underscore_placement_in_int_literal-4.zig | 4 +- ...invalid_union_field_access_in_comptime.zig | 4 +- ...gnostic_string_for_top_level_decl_type.zig | 4 +- ..._from_undefined_array_pointer_to_slice.zig | 4 +- ..._3818_bitcast_from_parray-slice_to_u16.zig | 4 +- ...terminated-slice_to_terminated-pointer.zig | 4 +- ...d_by_typeInfo_and_passed_into_function.zig | 4 +- ...ional_anyopaque_to_anyopaque_must_fail.zig | 4 +- ...time_slice-len_increment_beyond_bounds.zig | 4 +- ..._9346_return_outside_of_function_scope.zig | 4 +- .../stage1/obj/labeled_break_not_found.zig | 4 +- .../stage1/obj/labeled_continue_not_found.zig | 4 +- ...zy_pointer_with_undefined_element_type.zig | 4 +- .../stage1/obj/libc_headers_note.zig | 12 + ...es_from_comptime_reinterpreted_pointer.zig | 4 +- ...tor_pointer_with_unknown_runtime_index.zig | 4 +- ...local_shadows_global_that_occurs_later.zig | 4 +- .../obj/local_variable_redeclaration.zig | 4 +- .../local_variable_redeclares_parameter.zig | 4 +- .../obj/local_variable_shadowing_global.zig | 4 +- .../locally_shadowing_a_primitive_type.zig | 4 +- .../main_function_with_bogus_args_type.zig | 4 +- ...hod_call_with_first_arg_type_primitive.zig | 4 +- ...ll_with_first_arg_type_wrong_container.zig | 4 +- .../obj/missing_boolean_switch_value.zig | 4 +- ..._const_in_slice_with_nested_array_type.zig | 4 +- .../stage1/obj/missing_else_clause.zig | 4 +- ...ssing_field_in_struct_value_expression.zig | 4 +- .../obj/missing_function_call_param.zig | 4 +- .../stage1/obj/missing_function_name.zig | 4 +- .../stage1/obj/missing_param_name.zig | 4 +- ...ing_parameter_name_of_generic_function.zig | 4 +- .../obj/missing_result_type_for_phi_node.zig | 4 +- ...elled_type_with_pointer_only_reference.zig | 4 +- .../obj/mod_assign_on_undefined_value.zig | 4 +- .../stage1/obj/mod_on_undefined_value.zig | 4 +- .../mul_overflow_in_function_evaluation.zig | 4 +- .../obj/mult_assign_on_undefined_value.zig | 4 +- .../stage1/obj/mult_on_undefined_value.zig | 4 +- .../mult_wrap_assign_on_undefined_value.zig | 4 +- .../obj/mult_wrap_on_undefined_value.zig | 4 +- .../obj/multiple_function_definitions.zig | 4 +- .../stage1/obj/negate_on_undefined_value.zig | 4 +- .../obj/negate_wrap_on_undefined_value.zig | 4 +- ...gation_overflow_in_function_evaluation.zig | 4 +- .../stage1/obj/nested_error_set_mismatch.zig | 4 +- ...se_prong_on_switch_on_global_error_set.zig | 4 +- .../obj/noalias_on_non_pointer_param.zig | 4 +- ...eventually_is_inferred_to_become_async.zig | 4 +- ...h_struct_return_value_outside_function.zig | 4 +- ...ion_in_struct_literal_outside_function.zig | 4 +- .../obj/non-const_switch_number_literal.zig | 4 +- ...of_things_that_require_const_variables.zig | 4 +- .../obj/non-enum_tag_type_passed_to_union.zig | 4 +- .../obj/non-extern_function_with_var_args.zig | 4 +- ..._loop_on_a_type_that_requires_comptime.zig | 4 +- ...teger_tag_type_to_automatic_union_enum.zig | 4 +- .../obj/non-pure_function_returns_type.zig | 4 +- ...c_function_pointer_passed_to_asyncCall.zig | 4 +- .../non_compile_time_array_concatenation.zig | 4 +- .../non_constant_expression_in_array_size.zig | 4 +- ...sets_used_in_merge_error_sets_operator.zig | 4 +- .../obj/non_float_passed_to_floatToInt.zig | 4 +- .../obj/non_int_passed_to_intToFloat.zig | 4 +- .../obj/non_pointer_given_to_ptrToInt.zig | 4 +- .../stage1/obj/normal_string_with_newline.zig | 4 +- .../stage1/obj/offsetOf-bad_field_name.zig | 4 +- .../stage1/obj/offsetOf-non_struct.zig | 4 +- ...binary_operator_allowed_for_error_sets.zig | 4 +- .../stage1/obj/opaque_type_with_field.zig | 4 +- ...ional_pointer_to_void_in_extern_struct.zig | 4 +- .../stage1/obj/or_on_undefined_value.zig | 4 +- .../stage1/obj/orelse_on_undefined_value.zig | 4 +- ...ange_comptime_int_passed_to_floatToInt.zig | 4 +- .../obj/overflow_in_enum_value_allocation.zig | 4 +- .../obj/packed_union_given_enum_tag_type.zig | 4 +- ...cked_union_with_automatic_layout_field.zig | 4 +- .../obj/panic_called_at_compile_time.zig | 4 +- .../stage1/obj/parameter_redeclaration.zig | 4 +- .../stage1/obj/parameter_shadowing_global.zig | 4 +- .../obj/pass_const_ptr_to_mutable_ptr_fn.zig | 4 +- ..._not-aligned-enough_pointer_to_cmpxchg.zig | 4 +- ...sing_an_under-aligned_function_pointer.zig | 4 +- ...ast_const_pointer_to_mutable_C_pointer.zig | 4 +- ...pointer_arithmetic_on_pointer-to-array.zig | 4 +- ..._when_coercing_pointer_to_anon_literal.zig | 4 +- .../stage1/obj/pointer_to_noreturn.zig | 4 +- .../stage1/obj/popCount-non-integer.zig | 4 +- ...bad_implicit_casting_of_anyframe_types.zig | 4 +- ...ives_take_precedence_over_declarations.zig | 4 +- ...Cast_a_0_bit_type_to_a_non-_0_bit_type.zig | 4 +- .../obj/ptrCast_discards_const_qualifier.zig | 4 +- .../ptrToInt_0_to_non_optional_pointer.zig | 4 +- .../stage1/obj/ptrToInt_on_void.zig | 4 +- .../stage1/obj/ptrcast_to_non-pointer.zig | 4 +- ...e_operator_in_switch_used_on_error_set.zig | 4 +- ...ading_past_end_of_pointer_casted_array.zig | 4 +- .../obj/recursive_inferred_error_set.zig | 4 +- .../stage1/obj/redefinition_of_enums.zig | 4 +- .../obj/redefinition_of_global_variables.zig | 4 +- .../stage1/obj/redefinition_of_struct.zig | 4 +- ...efer_to_the_type_of_a_generic_function.zig | 4 +- .../referring_to_a_struct_that_is_invalid.zig | 4 +- ...when_assigning_a_value_within_a_struct.zig | 4 +- .../reify_type.Fn_with_is_generic_true.zig | 4 +- ...th_is_var_args_true_and_non-C_callconv.zig | 4 +- .../reify_type.Fn_with_return_type_null.zig | 4 +- ...ype.Pointer_with_invalid_address_space.zig | 4 +- ...austive_enum_with_non-integer_tag_type.zig | 4 +- ...xhaustive_enum_with_undefined_tag_type.zig | 4 +- ...e_for_exhaustive_enum_with_zero_fields.zig | 4 +- ...for_tagged_union_with_extra_enum_field.zig | 4 +- ...or_tagged_union_with_extra_union_field.zig | 4 +- ...reify_type_for_union_with_opaque_field.zig | 4 +- .../reify_type_for_union_with_zero_fields.zig | 4 +- .../reify_type_union_payload_is_undefined.zig | 4 +- .../stage1/obj/reify_type_with_Type.Int.zig | 4 +- ...eify_type_with_non-constant_expression.zig | 4 +- .../stage1/obj/reify_type_with_undefined.zig | 4 +- ...ompatibility_mismatching_handle_is_ptr.zig | 4 +- ...mismatching_handle_is_ptr_generic_call.zig | 4 +- .../obj/return_from_defer_expression.zig | 4 +- ...turning_error_from_void_async_function.zig | 4 +- .../runtime-known_async_function_called.zig | 4 +- ...own_function_called_with_async_keyword.zig | 4 +- ...ime_assignment_to_comptime_struct_type.zig | 4 +- ...time_assignment_to_comptime_union_type.zig | 4 +- ...ast_to_union_which_has_non-void_fields.zig | 4 +- ...runtime_index_into_comptime_type_slice.zig | 4 +- ...ating_arithmetic_does_not_allow_floats.zig | 4 +- ...oes_not_allow_negative_rhs_at_comptime.zig | 4 +- ...oes_not_allow_negative_rhs_at_comptime.zig | 4 +- .../obj/setAlignStack_in_inline_function.zig | 4 +- .../obj/setAlignStack_in_naked_function.zig | 4 +- .../obj/setAlignStack_outside_function.zig | 4 +- .../stage1/obj/setAlignStack_set_twice.zig | 4 +- .../stage1/obj/setAlignStack_too_big.zig | 4 +- .../obj/setFloatMode_twice_for_same_scope.zig | 4 +- .../setRuntimeSafety_twice_for_same_scope.zig | 4 +- .../setting_a_section_on_a_local_variable.zig | 4 +- ...shift_amount_has_to_be_an_integer_type.zig | 4 +- .../shift_by_negative_comptime_integer.zig | 4 +- .../shift_left_assign_on_undefined_value.zig | 4 +- .../obj/shift_left_on_undefined_value.zig | 4 +- .../shift_right_assign_on_undefined_value.zig | 4 +- .../obj/shift_right_on_undefined_value.zig | 4 +- ...fting_RHS_is_log2_of_LHS_int_bit_width.zig | 4 +- ...ing_without_int_type_or_comptime_known.zig | 4 +- .../stage1/obj/shlExact_shifts_out_1_bits.zig | 4 +- .../stage1/obj/shrExact_shifts_out_1_bits.zig | 4 +- .../stage1/obj/signed_integer_division.zig | 4 +- .../obj/signed_integer_remainder_division.zig | 4 +- .../stage1/obj/sizeOf_bad_type.zig | 4 +- ...ce_cannot_have_its_bytes_reinterpreted.zig | 4 +- .../obj/slice_passed_as_array_init_type.zig | 4 +- ...e_passed_as_array_init_type_with_elems.zig | 4 +- .../stage1/obj/slice_sentinel_mismatch-1.zig | 4 +- .../stage1/obj/slice_sentinel_mismatch-2.zig | 4 +- .../slicing_of_global_undefined_pointer.zig | 4 +- .../obj/slicing_single-item_pointer.zig | 4 +- ...pecify_enum_tag_type_that_is_too_small.zig | 4 +- .../obj/specify_non-integer_enum_tag_type.zig | 4 +- .../stage1/obj/src_outside_function.zig | 4 +- .../std.fmt_error_for_unused_arguments.zig | 4 +- ...tor_pointer_with_unknown_runtime_index.zig | 4 +- ...in_compile_time_variable_then_using_it.zig | 4 +- ...t_depends_on_itself_via_optional_field.zig | 4 +- .../stage1/obj/struct_field_missing_type.zig | 4 +- .../obj/struct_init_syntax_for_array.zig | 4 +- ...eclarations_unavailable_for_reify_type.zig | 4 +- .../stage1/obj/struct_with_invalid_field.zig | 4 +- .../obj/sub_assign_on_undefined_value.zig | 4 +- .../stage1/obj/sub_on_undefined_value.zig | 4 +- .../sub_overflow_in_function_evaluation.zig | 4 +- .../sub_wrap_assign_on_undefined_value.zig | 4 +- .../obj/sub_wrap_on_undefined_value.zig | 4 +- .../obj/suspend_inside_suspend_block.zig | 4 +- ...expression-duplicate_enumeration_prong.zig | 4 +- ...te_enumeration_prong_when_else_present.zig | 4 +- ...duplicate_or_overlapping_integer_value.zig | 4 +- .../obj/switch_expression-duplicate_type.zig | 4 +- ...expression-duplicate_type_struct_alias.zig | 4 +- ...h_expression-missing_enumeration_prong.zig | 4 +- ...switch_expression-multiple_else_prongs.zig | 4 +- ...pression-non_exhaustive_integer_prongs.zig | 4 +- ...on-switch_on_pointer_type_with_no_else.zig | 4 +- ...expression-unreachable_else_prong_bool.zig | 4 +- ...expression-unreachable_else_prong_enum.zig | 4 +- ...ession-unreachable_else_prong_range_i8.zig | 4 +- ...ession-unreachable_else_prong_range_u8.zig | 4 +- ...h_expression-unreachable_else_prong_u1.zig | 4 +- ...h_expression-unreachable_else_prong_u2.zig | 4 +- ...ch_on_enum_with_1_field_with_no_prongs.zig | 4 +- .../switch_on_union_with_no_attached_enum.zig | 4 +- ...itch_with_invalid_expression_parameter.zig | 4 +- .../switch_with_overlapping_case_ranges.zig | 4 +- ...d_on_union_with_no_associated_enum_tag.zig | 4 +- .../obj/take_slice_of_invalid_dereference.zig | 4 +- ...ing_bit_offset_of_void_field_in_struct.zig | 4 +- ...ng_byte_offset_of_void_field_in_struct.zig | 4 +- .../obj/threadlocal_qualifier_on_const.zig | 4 +- .../obj/top_level_decl_dependency_loop.zig | 4 +- .../stage1/obj/truncate_sign_mismatch.zig | 4 +- .../stage1/obj/truncate_undefined_value.zig | 4 +- ...in_function_with_non_error_return_type.zig | 4 +- .../obj/type_checking_function_pointers.zig | 4 +- .../obj/type_variables_must_be_constant.zig | 4 +- .../stage1/obj/undeclared_identifier.zig | 4 +- ...ntifier_error_should_mark_fn_as_impure.zig | 4 +- ...clared_identifier_in_unanalyzed_branch.zig | 4 +- .../undefined_as_field_type_is_rejected.zig | 4 +- .../stage1/obj/undefined_function_call.zig | 4 +- .../underscore_is_not_a_declarable_symbol.zig | 4 +- ...rscore_should_not_be_usable_inside_for.zig | 4 +- ...core_should_not_be_usable_inside_while.zig | 4 +- ...should_not_be_usable_inside_while_else.zig | 4 +- .../union_auto-enum_value_already_taken.zig | 4 +- .../union_enum_field_does_not_match_enum.zig | 4 +- .../union_fields_with_value_assignments.zig | 4 +- .../stage1/obj/union_with_0_fields.zig | 4 +- .../union_with_specified_enum_omits_field.zig | 4 +- ...ith_too_small_explicit_signed_tag_type.zig | 4 +- ...h_too_small_explicit_unsigned_tag_type.zig | 4 +- .../obj/unknown_length_pointer_to_opaque.zig | 4 +- .../obj/unreachable_code-double_break.zig | 4 +- .../obj/unreachable_code-nested_returns.zig | 4 +- .../stage1/obj/unreachable_code.zig | 4 +- .../obj/unreachable_executed_at_comptime.zig | 4 +- .../stage1/obj/unreachable_parameter.zig | 4 +- .../stage1/obj/unreachable_variable.zig | 4 +- .../stage1/obj/unreachable_with_return.zig | 4 +- ...fier_at_start_of_asm_output_constraint.zig | 4 +- .../obj/unused_variable_error_on_errdefer.zig | 4 +- ...use_anyopaque_as_return_type_of_fn_ptr.zig | 4 +- ...to_assign_null_to_non-nullable_pointer.zig | 4 +- ..._invalid_number_literal_as_array_index.zig | 4 +- ...omptime-known_undefined_function_value.zig | 4 +- .../obj/use_of_undeclared_identifier.zig | 4 +- ..._unknown_len_ptr_type_instead_of_array.zig | 4 +- ...types_in_function_call_raises_an_error.zig | 4 +- .../obj/usingnamespace_with_wrong_type.zig | 4 +- .../stage1/obj/variable_has_wrong_type.zig | 4 +- ...line_assembly_template_cannot_be_found.zig | 12 + ...lization_compile_error_then_referenced.zig | 4 +- .../obj/variable_with_type_noreturn.zig | 4 +- .../stage1/obj/vector_index_out_of_bounds.zig | 4 +- .../obj/volatile_on_global_assembly.zig | 4 +- ...is_a_compile_error_in_non-Wasm_targets.zig | 4 +- ...is_a_compile_error_in_non-Wasm_targets.zig | 4 +- .../while_expected_bool_got_error_union.zig | 4 +- .../obj/while_expected_bool_got_optional.zig | 4 +- .../while_expected_error_union_got_bool.zig | 4 +- ...hile_expected_error_union_got_optional.zig | 4 +- .../obj/while_expected_optional_got_bool.zig | 4 +- ...hile_expected_optional_got_error_union.zig | 4 +- .../while_loop_body_expression_ignored.zig | 4 +- .../obj/write_to_const_global_variable.zig | 4 +- .../wrong_frame_type_used_for_async_call.zig | 4 +- .../stage1/obj/wrong_function_type.zig | 4 +- ...ializer_for_union_payload_of_type_type.zig | 4 +- .../stage1/obj/wrong_number_of_arguments.zig | 4 +- ...number_of_arguments_for_method_fn_call.zig | 4 +- ...wrong_panic_signature_generic_function.zig | 4 +- ...wrong_panic_signature_runtime_function.zig | 4 +- ...ointer_coerced_to_pointer_to_opaque_{}.zig | 4 +- .../stage1/obj/wrong_return_type_for_main.zig | 4 +- .../obj/wrong_size_to_an_array_literal.zig | 4 +- ...g_type_for_argument_tuple_to_asyncCall.zig | 4 +- .../stage1/obj/wrong_type_for_reify_type.zig | 4 +- ...wrong_type_for_result_ptr_to_asyncCall.zig | 4 +- .../stage1/obj/wrong_type_passed_to_panic.zig | 4 +- .../stage1/obj/wrong_type_to_hasField.zig | 4 +- ..._given_to_atomic_order_args_in_cmpxchg.zig | 4 +- .../obj/wrong_types_given_to_export.zig | 4 +- .../test/access_invalid_typeInfo_decl.zig | 5 +- .../test/alignCast_of_zero_sized_types.zig | 5 +- .../stage1/test/bad_splat_type.zig | 5 +- .../test/binary_OR_operator_on_error_sets.zig | 5 +- ...ts_non_comptime-known_fn-always_inline.zig | 5 +- ...cts_non_comptime-known_fn-compile_time.zig | 5 +- ...en_optional_T_where_T_is_not_a_pointer.zig | 5 +- .../combination_of_nosuspend_and_async.zig | 5 +- ...n_of_non-tagged_union_and_enum_literal.zig | 5 +- ...mptime_vector_overflow_shows_the_index.zig | 5 +- ...cate_field_in_anonymous_struct_literal.zig | 5 +- ..._initializer_doesnt_crash_the_compiler.zig | 5 +- ...rors_in_for_loop_bodies_are_propagated.zig | 5 +- .../test/export_with_empty_name_string.zig | 5 +- .../helpful_return_type_error_message.zig | 5 +- ...float_conversion_to_comptime_int-float.zig | 5 +- .../stage1/test/invalid_assignments.zig | 5 +- .../stage1/test/invalid_float_casts.zig | 5 +- .../stage1/test/invalid_int_casts.zig | 5 +- .../invalid_non-exhaustive_enum_to_union.zig | 5 +- .../test/invalid_pointer_with_reify_type.zig | 5 +- .../stage1/test/nested_vectors.zig | 5 +- ...xhaustive_enum_marker_assigned_a_value.zig | 5 +- .../stage1/test/non-exhaustive_enums.zig | 5 +- .../stage1/test/not_an_enum_type.zig | 5 +- ...truct_with_fields_of_not_allowed_types.zig | 5 +- ...rToInt_with_pointer_to_zero-sized_type.zig | 5 +- .../test/reassign_to_array_parameter.zig | 5 +- .../test/reassign_to_slice_parameter.zig | 5 +- .../test/reassign_to_struct_parameter.zig | 5 +- .../stage1/test/reference_to_const_data.zig | 5 +- ...ify_typeOf_with_incompatible_arguments.zig | 5 +- .../test/reify_typeOf_with_no_arguments.zig | 5 +- ..._extern_function_definitions_with_body.zig | 5 +- ...ect_extern_variables_with_initializers.zig | 5 +- ...n_returning_type_crashes_compiler_2655.zig | 5 +- .../test/return_invalid_type_from_test.zig | 5 +- ...ift_on_type_with_non-power-of-two_size.zig | 5 +- ...elected_index_past_first_vector_length.zig | 5 +- .../switch_ranges_endpoints_are_validated.zig | 5 +- ...hing_with_exhaustive_enum_has___prong_.zig | 5 +- .../switching_with_non-exhaustive_enums.zig | 5 +- ...n_invalid_value_of_non-exhaustive_enum.zig | 5 +- ...e_mismatch_in_C_prototype_with_varargs.zig | 5 +- ...type_mismatch_with_tuple_concatenation.zig | 5 +- ...de_comptime_function_has_compile_error.zig | 1 + .../stage2/duplicate-unused_labels.zig | 1 + .../stage2/embed_outside_package.zig | 1 + .../stage2/import_outside_package.zig | 1 + .../stage2/out_of_bounds_index.zig | 1 + .../stage2/slice_of_null_pointer.zig | 1 + .../stage2/struct_duplicate_field_name.zig | 1 + .../stage2/union_access_of_inactive_field.zig | 1 + .../stage2/union_duplicate_enum_field.zig | 1 + .../union_duplicate_field_definition.zig | 1 + .../stage2/union_enum_field_missing.zig | 1 + .../stage2/union_extra_field.zig | 1 + .../union_runtime_coercion_from_enum.zig | 1 + 678 files changed, 2175 insertions(+), 902 deletions(-) create mode 100644 test/compile_errors/stage1/obj/align_n_expr_function_pointers_is_a_compile_error.zig create mode 100644 test/compile_errors/stage1/obj/bad_alignment_in_asynccall.zig create mode 100644 test/compile_errors/stage1/obj/call_with_new_stack_on_unsupported_target.zig create mode 100644 test/compile_errors/stage1/obj/callconv_apcs_aapcs_aapcsvfp_on_unsupported_platform.zig create mode 100644 test/compile_errors/stage1/obj/callconv_interrupt_on_unsupported_platform.zig create mode 100644 test/compile_errors/stage1/obj/callconv_signal_on_unsupported_platform.zig create mode 100644 test/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-0.zig create mode 100644 test/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-1.zig create mode 100644 test/compile_errors/stage1/obj/callconv_vectorcall_on_unsupported_platform.zig create mode 100644 test/compile_errors/stage1/obj/implicit_dependency_on_libc.zig create mode 100644 test/compile_errors/stage1/obj/incompatible_sentinels.zig create mode 100644 test/compile_errors/stage1/obj/libc_headers_note.zig create mode 100644 test/compile_errors/stage1/obj/variable_in_inline_assembly_template_cannot_be_found.zig diff --git a/src/test.zig b/src/test.zig index 3579370c04..adcfeb8dff 100644 --- a/src/test.zig +++ b/src/test.zig @@ -34,15 +34,12 @@ test { var ctx = TestContext.init(std.testing.allocator, arena); defer ctx.deinit(); - const compile_errors_dir_path = try std.fs.path.join(arena, &.{ - std.fs.path.dirname(@src().file).?, "..", "test", "compile_errors", - }); - - var compile_errors_dir = try std.fs.cwd().openDir(compile_errors_dir_path, .{}); - defer compile_errors_dir.close(); - { - var dir = try compile_errors_dir.openDir("stage2", .{ .iterate = true }); + const dir_path = try std.fs.path.join(arena, &.{ + std.fs.path.dirname(@src().file).?, "..", "test", "compile_errors", + }); + + var dir = try std.fs.cwd().openDir(dir_path, .{ .iterate = true }); defer dir.close(); // TODO make this incremental once the bug is solved that it triggers @@ -50,28 +47,6 @@ test { ctx.addTestCasesFromDir(dir, .independent); } - if (!skip_stage1) { - var stage1_dir = try compile_errors_dir.openDir("stage1", .{}); - defer stage1_dir.close(); - - const Config = struct { - name: []const u8, - is_test: bool, - output_mode: std.builtin.OutputMode, - }; - - for ([_]Config{ - .{ .name = "obj", .is_test = false, .output_mode = .Obj }, - .{ .name = "exe", .is_test = false, .output_mode = .Exe }, - .{ .name = "test", .is_test = true, .output_mode = .Exe }, - }) |config| { - var dir = try stage1_dir.openDir(config.name, .{ .iterate = true }); - defer dir.close(); - - ctx.addErrorCasesFromDir("stage1", dir, .stage1, config.output_mode, config.is_test, .independent); - } - } - { const dir_path = try std.fs.path.join(arena, &.{ std.fs.path.dirname(@src().file).?, "..", "test", "incremental", diff --git a/test/cases.zig b/test/cases.zig index 3e340dcddb..65eec90f1b 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -1,11 +1,6 @@ const std = @import("std"); const TestContext = @import("../src/test.zig").TestContext; -// Self-hosted has differing levels of support for various architectures. For now we pass explicit -// target parameters to each test case. At some point we will take this to the next level and have -// a set of targets that all test cases run on unless specifically overridden. For now, each test -// case applies to only the specified target. - pub fn addCases(ctx: *TestContext) !void { try @import("compile_errors.zig").addCases(ctx); try @import("stage2/cbe.zig").addCases(ctx); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index d0e4156f18..f79d2b8e92 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3,207 +3,6 @@ const builtin = @import("builtin"); const TestContext = @import("../src/test.zig").TestContext; pub fn addCases(ctx: *TestContext) !void { - { - const case = ctx.obj("callconv(.Interrupt) on unsupported platform", .{ - .cpu_arch = .aarch64, - .os_tag = .linux, - .abi = .none, - }); - case.backend = .stage1; - case.addError( - \\export fn entry() callconv(.Interrupt) void {} - , &[_][]const u8{ - "tmp.zig:1:28: error: callconv 'Interrupt' is only available on x86, x86_64, AVR, and MSP430, not aarch64", - }); - } - { - var case = ctx.obj("callconv(.Signal) on unsupported platform", .{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .none, - }); - case.backend = .stage1; - case.addError( - \\export fn entry() callconv(.Signal) void {} - , &[_][]const u8{ - "tmp.zig:1:28: error: callconv 'Signal' is only available on AVR, not x86_64", - }); - } - { - const case = ctx.obj("callconv(.Stdcall, .Fastcall, .Thiscall) on unsupported platform", .{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .none, - }); - case.backend = .stage1; - case.addError( - \\const F1 = fn () callconv(.Stdcall) void; - \\const F2 = fn () callconv(.Fastcall) void; - \\const F3 = fn () callconv(.Thiscall) void; - \\export fn entry1() void { var a: F1 = undefined; _ = a; } - \\export fn entry2() void { var a: F2 = undefined; _ = a; } - \\export fn entry3() void { var a: F3 = undefined; _ = a; } - , &[_][]const u8{ - "tmp.zig:1:27: error: callconv 'Stdcall' is only available on x86, not x86_64", - "tmp.zig:2:27: error: callconv 'Fastcall' is only available on x86, not x86_64", - "tmp.zig:3:27: error: callconv 'Thiscall' is only available on x86, not x86_64", - }); - } - { - const case = ctx.obj("callconv(.Stdcall, .Fastcall, .Thiscall) on unsupported platform", .{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .none, - }); - case.backend = .stage1; - case.addError( - \\export fn entry1() callconv(.Stdcall) void {} - \\export fn entry2() callconv(.Fastcall) void {} - \\export fn entry3() callconv(.Thiscall) void {} - , &[_][]const u8{ - "tmp.zig:1:29: error: callconv 'Stdcall' is only available on x86, not x86_64", - "tmp.zig:2:29: error: callconv 'Fastcall' is only available on x86, not x86_64", - "tmp.zig:3:29: error: callconv 'Thiscall' is only available on x86, not x86_64", - }); - } - { - const case = ctx.obj("callconv(.Vectorcall) on unsupported platform", .{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .none, - }); - case.backend = .stage1; - case.addError( - \\export fn entry() callconv(.Vectorcall) void {} - , &[_][]const u8{ - "tmp.zig:1:28: error: callconv 'Vectorcall' is only available on x86 and AArch64, not x86_64", - }); - } - { - const case = ctx.obj("callconv(.APCS, .AAPCS, .AAPCSVFP) on unsupported platform", .{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .none, - }); - case.backend = .stage1; - case.addError( - \\export fn entry1() callconv(.APCS) void {} - \\export fn entry2() callconv(.AAPCS) void {} - \\export fn entry3() callconv(.AAPCSVFP) void {} - , &[_][]const u8{ - "tmp.zig:1:29: error: callconv 'APCS' is only available on ARM, not x86_64", - "tmp.zig:2:29: error: callconv 'AAPCS' is only available on ARM, not x86_64", - "tmp.zig:3:29: error: callconv 'AAPCSVFP' is only available on ARM, not x86_64", - }); - } - - { - const case = ctx.obj("call with new stack on unsupported target", .{ - .cpu_arch = .wasm32, - .os_tag = .wasi, - .abi = .none, - }); - case.backend = .stage1; - case.addError( - \\var buf: [10]u8 align(16) = undefined; - \\export fn entry() void { - \\ @call(.{.stack = &buf}, foo, .{}); - \\} - \\fn foo() void {} - , &[_][]const u8{ - "tmp.zig:3:5: error: target arch 'wasm32' does not support calling with a new stack", - }); - } - - // Note: One of the error messages here is backwards. It would be nice to fix, but that's not - // going to stop me from merging this branch which fixes a bunch of other stuff. - ctx.objErrStage1("incompatible sentinels", - \\export fn entry1(ptr: [*:255]u8) [*:0]u8 { - \\ return ptr; - \\} - \\export fn entry2(ptr: [*]u8) [*:0]u8 { - \\ return ptr; - \\} - \\export fn entry3() void { - \\ var array: [2:0]u8 = [_:255]u8{1, 2}; - \\ _ = array; - \\} - \\export fn entry4() void { - \\ var array: [2:0]u8 = [_]u8{1, 2}; - \\ _ = array; - \\} - , &[_][]const u8{ - "tmp.zig:2:12: error: expected type '[*:0]u8', found '[*:255]u8'", - "tmp.zig:2:12: note: destination pointer requires a terminating '0' sentinel, but source pointer has a terminating '255' sentinel", - "tmp.zig:5:12: error: expected type '[*:0]u8', found '[*]u8'", - "tmp.zig:5:12: note: destination pointer requires a terminating '0' sentinel", - - "tmp.zig:8:35: error: expected type '[2:255]u8', found '[2:0]u8'", - "tmp.zig:8:35: note: destination array requires a terminating '255' sentinel, but source array has a terminating '0' sentinel", - "tmp.zig:12:31: error: expected type '[2:0]u8', found '[2]u8'", - "tmp.zig:12:31: note: destination array requires a terminating '0' sentinel", - }); - - { - const case = ctx.obj("variable in inline assembly template cannot be found", .{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .gnu, - }); - case.backend = .stage1; - case.addError( - \\export fn entry() void { - \\ var sp = asm volatile ( - \\ "mov %[foo], sp" - \\ : [bar] "=r" (-> usize) - \\ ); - \\ _ = sp; - \\} - , &[_][]const u8{ - "tmp.zig:2:14: error: could not find 'foo' in the inputs or outputs", - }); - } - - { - const case = ctx.obj("bad alignment in @asyncCall", .{ - .cpu_arch = .aarch64, - .os_tag = .linux, - .abi = .none, - }); - case.backend = .stage1; - case.addError( - \\export fn entry() void { - \\ var ptr: fn () callconv(.Async) void = func; - \\ var bytes: [64]u8 = undefined; - \\ _ = @asyncCall(&bytes, {}, ptr, .{}); - \\} - \\fn func() callconv(.Async) void {} - , &[_][]const u8{ - "tmp.zig:4:21: error: expected type '[]align(8) u8', found '*[64]u8'", - }); - } - - if (builtin.os.tag == .linux) { - ctx.testErrStage1("implicit dependency on libc", - \\extern "c" fn exit(u8) void; - \\export fn entry() void { - \\ exit(0); - \\} - , &[_][]const u8{ - "tmp.zig:3:5: error: dependency on libc must be explicitly specified in the build command", - }); - - ctx.testErrStage1("libc headers note", - \\const c = @cImport(@cInclude("stdio.h")); - \\export fn entry() void { - \\ _ = c.printf("hello, world!\n"); - \\} - , &[_][]const u8{ - "tmp.zig:1:11: error: C import failed", - "tmp.zig:1:11: note: libc headers not available; compilation does not link against libc", - }); - } - { const case = ctx.obj("wrong same named struct", .{}); case.backend = .stage1; @@ -366,21 +165,4 @@ pub fn addCases(ctx: *TestContext) !void { //, &[_][]const u8{ // "tmp.zig:4:1: error: unable to inline function", //}); - - { - const case = ctx.obj("align(N) expr function pointers is a compile error", .{ - .cpu_arch = .wasm32, - .os_tag = .freestanding, - .abi = .none, - }); - case.backend = .stage1; - - case.addError( - \\export fn foo() align(1) void { - \\ return; - \\} - , &[_][]const u8{ - "tmp.zig:1:23: error: align(N) expr is not allowed on function prototypes in wasm32/wasm64", - }); - } } diff --git a/test/compile_errors/stage1/exe/main_missing_name.zig b/test/compile_errors/stage1/exe/main_missing_name.zig index c029665367..1a53e43cfb 100644 --- a/test/compile_errors/stage1/exe/main_missing_name.zig +++ b/test/compile_errors/stage1/exe/main_missing_name.zig @@ -1,5 +1,8 @@ pub fn (main) void {} -// main missing name +// error +// backend=stage1 +// target=native +// output_mode=Exe // // tmp.zig:1:5: error: missing function name diff --git a/test/compile_errors/stage1/exe/missing_main_fn_in_executable.zig b/test/compile_errors/stage1/exe/missing_main_fn_in_executable.zig index eb9ab469fd..65813abac3 100644 --- a/test/compile_errors/stage1/exe/missing_main_fn_in_executable.zig +++ b/test/compile_errors/stage1/exe/missing_main_fn_in_executable.zig @@ -1,5 +1,8 @@ -// missing main fn in executable +// error +// backend=stage1 +// target=native +// output_mode=Exe // // error: root source file has no member called 'main' diff --git a/test/compile_errors/stage1/exe/private_main_fn.zig b/test/compile_errors/stage1/exe/private_main_fn.zig index da617899a5..5a178389e8 100644 --- a/test/compile_errors/stage1/exe/private_main_fn.zig +++ b/test/compile_errors/stage1/exe/private_main_fn.zig @@ -1,6 +1,9 @@ fn main() void {} -// private main fn +// error +// backend=stage1 +// target=native +// output_mode=Exe // // error: 'main' is private // tmp.zig:1:1: note: declared here diff --git a/test/compile_errors/stage1/obj/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig b/test/compile_errors/stage1/obj/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig index 24fd0d1b9f..72c58adbd6 100644 --- a/test/compile_errors/stage1/obj/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig +++ b/test/compile_errors/stage1/obj/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig @@ -5,6 +5,8 @@ export fn a() void { _ = t; } -// C pointer pointing to non C ABI compatible type or has align attr +// error +// backend=stage1 +// target=native // // tmp.zig:3:19: error: C pointers cannot point to non-C-ABI-compatible type 'Foo' diff --git a/test/compile_errors/stage1/obj/C_pointer_to_anyopaque.zig b/test/compile_errors/stage1/obj/C_pointer_to_anyopaque.zig index 3012996145..36c9789d20 100644 --- a/test/compile_errors/stage1/obj/C_pointer_to_anyopaque.zig +++ b/test/compile_errors/stage1/obj/C_pointer_to_anyopaque.zig @@ -4,6 +4,8 @@ export fn a() void { _ = y; } -// C pointer to anyopaque +// error +// backend=stage1 +// target=native // // tmp.zig:3:16: error: C pointers cannot point to opaque types diff --git a/test/compile_errors/stage1/obj/Frame_of_generic_function.zig b/test/compile_errors/stage1/obj/Frame_of_generic_function.zig index b9b2280dd7..d90e53eb46 100644 --- a/test/compile_errors/stage1/obj/Frame_of_generic_function.zig +++ b/test/compile_errors/stage1/obj/Frame_of_generic_function.zig @@ -7,6 +7,8 @@ fn func(comptime T: type) void { _ = x; } -// @Frame() of generic function +// error +// backend=stage1 +// target=native // // tmp.zig:2:16: error: @Frame() of generic function diff --git a/test/compile_errors/stage1/obj/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig b/test/compile_errors/stage1/obj/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig index d01e68e4eb..e1d498e65f 100644 --- a/test/compile_errors/stage1/obj/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig +++ b/test/compile_errors/stage1/obj/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig @@ -8,7 +8,9 @@ export fn f2(x: V(4, u32)) V(4, u32) { return -y; } -// Issue #5586: Make unary minus for unsigned types a compile error +// error +// backend=stage1 +// target=native // // tmp.zig:3:12: error: negation of type 'u32' // tmp.zig:8:12: error: negation of type 'u32' diff --git a/test/compile_errors/stage1/obj/Issue_6823_dont_allow_._to_be_followed_by_.zig b/test/compile_errors/stage1/obj/Issue_6823_dont_allow_._to_be_followed_by_.zig index c21df43362..1aa0996ba4 100644 --- a/test/compile_errors/stage1/obj/Issue_6823_dont_allow_._to_be_followed_by_.zig +++ b/test/compile_errors/stage1/obj/Issue_6823_dont_allow_._to_be_followed_by_.zig @@ -3,6 +3,8 @@ fn foo() void { _ = sequence; } -// Issue #6823: don't allow .* to be followed by ** +// error +// backend=stage1 +// target=native // // tmp.zig:2:28: error: '.*' cannot be followed by '*'. Are you missing a space? diff --git a/test/compile_errors/stage1/obj/Issue_9165_windows_tcp_server_compilation_error.zig b/test/compile_errors/stage1/obj/Issue_9165_windows_tcp_server_compilation_error.zig index e3b9d4b009..3d150693f5 100644 --- a/test/compile_errors/stage1/obj/Issue_9165_windows_tcp_server_compilation_error.zig +++ b/test/compile_errors/stage1/obj/Issue_9165_windows_tcp_server_compilation_error.zig @@ -9,6 +9,8 @@ pub fn main() !void { } } -// Issue #9165: windows tcp server compilation error +// error +// backend=stage1 +// target=native // // error: Unsupported OS diff --git a/test/compile_errors/stage1/obj/access_non-existent_member_of_error_set.zig b/test/compile_errors/stage1/obj/access_non-existent_member_of_error_set.zig index 42a54b08cd..caddf36a84 100644 --- a/test/compile_errors/stage1/obj/access_non-existent_member_of_error_set.zig +++ b/test/compile_errors/stage1/obj/access_non-existent_member_of_error_set.zig @@ -4,6 +4,8 @@ comptime { _ = z; } -// access non-existent member of error set +// error +// backend=stage1 +// target=native // // tmp.zig:3:18: error: no error named 'Bar' in 'Foo' diff --git a/test/compile_errors/stage1/obj/accessing_runtime_parameter_from_outer_function.zig b/test/compile_errors/stage1/obj/accessing_runtime_parameter_from_outer_function.zig index 68b23c6a43..73a9e94d44 100644 --- a/test/compile_errors/stage1/obj/accessing_runtime_parameter_from_outer_function.zig +++ b/test/compile_errors/stage1/obj/accessing_runtime_parameter_from_outer_function.zig @@ -12,7 +12,9 @@ export fn entry() void { _ = x; } -// accessing runtime parameter from outer function +// error +// backend=stage1 +// target=native // // tmp.zig:4:24: error: 'y' not accessible from inner function // tmp.zig:3:28: note: crossed function definition here diff --git a/test/compile_errors/stage1/obj/add_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/add_assign_on_undefined_value.zig index 0076cf1bab..38e4fef81b 100644 --- a/test/compile_errors/stage1/obj/add_assign_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/add_assign_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { a += a; } -// add assign on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/add_on_undefined_value.zig b/test/compile_errors/stage1/obj/add_on_undefined_value.zig index 5f64c6999b..4a66f9c102 100644 --- a/test/compile_errors/stage1/obj/add_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/add_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = a + a; } -// add on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/add_overflow_in_function_evaluation.zig b/test/compile_errors/stage1/obj/add_overflow_in_function_evaluation.zig index b01e005a04..d4fa312482 100644 --- a/test/compile_errors/stage1/obj/add_overflow_in_function_evaluation.zig +++ b/test/compile_errors/stage1/obj/add_overflow_in_function_evaluation.zig @@ -5,6 +5,8 @@ fn add(a: u16, b: u16) u16 { export fn entry() usize { return @sizeOf(@TypeOf(y)); } -// add overflow in function evaluation +// error +// backend=stage1 +// target=native // // tmp.zig:3:14: error: operation caused overflow diff --git a/test/compile_errors/stage1/obj/add_wrap_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/add_wrap_assign_on_undefined_value.zig index 37dbf9106f..5f9a4c61fc 100644 --- a/test/compile_errors/stage1/obj/add_wrap_assign_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/add_wrap_assign_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { a +%= a; } -// add wrap assign on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/add_wrap_on_undefined_value.zig b/test/compile_errors/stage1/obj/add_wrap_on_undefined_value.zig index 3ba9bd11c6..582b8ec1cf 100644 --- a/test/compile_errors/stage1/obj/add_wrap_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/add_wrap_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = a +% a; } -// add wrap on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/addition_with_non_numbers.zig b/test/compile_errors/stage1/obj/addition_with_non_numbers.zig index 296b9f4103..8f343926f2 100644 --- a/test/compile_errors/stage1/obj/addition_with_non_numbers.zig +++ b/test/compile_errors/stage1/obj/addition_with_non_numbers.zig @@ -5,6 +5,8 @@ const x = Foo {.field = 1} + Foo {.field = 2}; export fn entry() usize { return @sizeOf(@TypeOf(x)); } -// addition with non numbers +// error +// backend=stage1 +// target=native // // tmp.zig:4:28: error: invalid operands to binary expression: 'Foo' and 'Foo' diff --git a/test/compile_errors/stage1/obj/address_of_number_literal.zig b/test/compile_errors/stage1/obj/address_of_number_literal.zig index ee61f2180f..76d321929d 100644 --- a/test/compile_errors/stage1/obj/address_of_number_literal.zig +++ b/test/compile_errors/stage1/obj/address_of_number_literal.zig @@ -3,6 +3,8 @@ const y = &x; fn foo() *const i32 { return y; } export fn entry() usize { return @sizeOf(@TypeOf(foo)); } -// address of number literal +// error +// backend=stage1 +// target=native // // tmp.zig:3:30: error: expected type '*const i32', found '*const comptime_int' diff --git a/test/compile_errors/stage1/obj/alignCast_expects_pointer_or_slice.zig b/test/compile_errors/stage1/obj/alignCast_expects_pointer_or_slice.zig index 91b269cef4..9f76d3ca4f 100644 --- a/test/compile_errors/stage1/obj/alignCast_expects_pointer_or_slice.zig +++ b/test/compile_errors/stage1/obj/alignCast_expects_pointer_or_slice.zig @@ -2,6 +2,8 @@ export fn entry() void { @alignCast(4, @as(u32, 3)); } -// @alignCast expects pointer or slice +// error +// backend=stage1 +// target=native // // tmp.zig:2:19: error: expected pointer or slice, found 'u32' diff --git a/test/compile_errors/stage1/obj/align_n_expr_function_pointers_is_a_compile_error.zig b/test/compile_errors/stage1/obj/align_n_expr_function_pointers_is_a_compile_error.zig new file mode 100644 index 0000000000..bd41b3e657 --- /dev/null +++ b/test/compile_errors/stage1/obj/align_n_expr_function_pointers_is_a_compile_error.zig @@ -0,0 +1,9 @@ +export fn foo() align(1) void { + return; +} + +// error +// backend=stage1 +// target=wasm32-freestanding-none +// +// tmp.zig:1:23: error: align(N) expr is not allowed on function prototypes in wasm32/wasm64 diff --git a/test/compile_errors/stage1/obj/aligned_variable_of_zero-bit_type.zig b/test/compile_errors/stage1/obj/aligned_variable_of_zero-bit_type.zig index 90e690056d..f09f92aca7 100644 --- a/test/compile_errors/stage1/obj/aligned_variable_of_zero-bit_type.zig +++ b/test/compile_errors/stage1/obj/aligned_variable_of_zero-bit_type.zig @@ -3,6 +3,8 @@ export fn f() void { _ = s; } -// aligned variable of zero-bit type +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: variable 's' of zero-bit type 'struct:2:12' has no in-memory representation, it cannot be aligned diff --git a/test/compile_errors/stage1/obj/alignment_of_enum_field_specified.zig b/test/compile_errors/stage1/obj/alignment_of_enum_field_specified.zig index cf5cdfd213..9b6f1e452d 100644 --- a/test/compile_errors/stage1/obj/alignment_of_enum_field_specified.zig +++ b/test/compile_errors/stage1/obj/alignment_of_enum_field_specified.zig @@ -7,6 +7,8 @@ export fn entry1() void { _ = x; } -// alignment of enum field specified +// error +// backend=stage1 +// target=native // // tmp.zig:3:7: error: expected ',' after field diff --git a/test/compile_errors/stage1/obj/ambiguous_decl_reference.zig b/test/compile_errors/stage1/obj/ambiguous_decl_reference.zig index 421c41d2c2..82e6a0a24c 100644 --- a/test/compile_errors/stage1/obj/ambiguous_decl_reference.zig +++ b/test/compile_errors/stage1/obj/ambiguous_decl_reference.zig @@ -12,7 +12,9 @@ export fn entry() void { bar(); } -// ambiguous decl reference +// error +// backend=stage1 +// target=native // // tmp.zig:5:13: error: ambiguous reference // tmp.zig:7:9: note: declared here diff --git a/test/compile_errors/stage1/obj/and_on_undefined_value.zig b/test/compile_errors/stage1/obj/and_on_undefined_value.zig index 82363848f3..6071d81062 100644 --- a/test/compile_errors/stage1/obj/and_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/and_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = a and a; } -// and on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/array_access_of_non_array.zig b/test/compile_errors/stage1/obj/array_access_of_non_array.zig index e4420debcd..7045f2fdbd 100644 --- a/test/compile_errors/stage1/obj/array_access_of_non_array.zig +++ b/test/compile_errors/stage1/obj/array_access_of_non_array.zig @@ -7,7 +7,9 @@ export fn g() void { _ = bad[0]; } -// array access of non array +// error +// backend=stage1 +// target=native // // tmp.zig:3:8: error: array access of non-array type 'bool' // tmp.zig:7:12: error: array access of non-array type 'bool' diff --git a/test/compile_errors/stage1/obj/array_access_of_type.zig b/test/compile_errors/stage1/obj/array_access_of_type.zig index 82708eed6c..c0029f2492 100644 --- a/test/compile_errors/stage1/obj/array_access_of_type.zig +++ b/test/compile_errors/stage1/obj/array_access_of_type.zig @@ -3,6 +3,8 @@ export fn foo() void { _ = b; } -// array access of type +// error +// backend=stage1 +// target=native // // tmp.zig:2:14: error: array access of non-array type 'type' diff --git a/test/compile_errors/stage1/obj/array_access_of_undeclared_identifier.zig b/test/compile_errors/stage1/obj/array_access_of_undeclared_identifier.zig index 193a7bf5de..7d16a0d60e 100644 --- a/test/compile_errors/stage1/obj/array_access_of_undeclared_identifier.zig +++ b/test/compile_errors/stage1/obj/array_access_of_undeclared_identifier.zig @@ -2,6 +2,8 @@ export fn f() void { i[i] = i[i]; } -// array access of undeclared identifier +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: use of undeclared identifier 'i' diff --git a/test/compile_errors/stage1/obj/array_access_with_non_integer_index.zig b/test/compile_errors/stage1/obj/array_access_with_non_integer_index.zig index 1a3e17d896..de0a58656c 100644 --- a/test/compile_errors/stage1/obj/array_access_with_non_integer_index.zig +++ b/test/compile_errors/stage1/obj/array_access_with_non_integer_index.zig @@ -9,7 +9,9 @@ export fn g() void { _ = array[bad]; } -// array access with non integer index +// error +// backend=stage1 +// target=native // // tmp.zig:4:11: error: expected type 'usize', found 'bool' // tmp.zig:9:15: error: expected type 'usize', found 'bool' diff --git a/test/compile_errors/stage1/obj/array_concatenation_with_wrong_type.zig b/test/compile_errors/stage1/obj/array_concatenation_with_wrong_type.zig index 91036e5f25..ccddc7efbd 100644 --- a/test/compile_errors/stage1/obj/array_concatenation_with_wrong_type.zig +++ b/test/compile_errors/stage1/obj/array_concatenation_with_wrong_type.zig @@ -4,6 +4,8 @@ const a = derp ++ "foo"; export fn entry() usize { return @sizeOf(@TypeOf(a)); } -// array concatenation with wrong type +// error +// backend=stage1 +// target=native // // tmp.zig:3:11: error: expected array, found 'usize' diff --git a/test/compile_errors/stage1/obj/array_in_c_exported_function.zig b/test/compile_errors/stage1/obj/array_in_c_exported_function.zig index fe03d220b1..5137755474 100644 --- a/test/compile_errors/stage1/obj/array_in_c_exported_function.zig +++ b/test/compile_errors/stage1/obj/array_in_c_exported_function.zig @@ -6,7 +6,9 @@ export fn zig_return_array() [10]u8 { return "1234567890".*; } -// array in c exported function +// error +// backend=stage1 +// target=native // // tmp.zig:1:24: error: parameter of type '[10]u8' not allowed in function with calling convention 'C' // tmp.zig:5:30: error: return type '[10]u8' not allowed in function with calling convention 'C' diff --git a/test/compile_errors/stage1/obj/asm_at_compile_time.zig b/test/compile_errors/stage1/obj/asm_at_compile_time.zig index e08b500c6e..fcfd5e7da6 100644 --- a/test/compile_errors/stage1/obj/asm_at_compile_time.zig +++ b/test/compile_errors/stage1/obj/asm_at_compile_time.zig @@ -10,6 +10,8 @@ fn doSomeAsm() void { ); } -// asm at compile time +// error +// backend=stage1 +// target=native // // tmp.zig:6:5: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/assign_inline_fn_to_non-comptime_var.zig b/test/compile_errors/stage1/obj/assign_inline_fn_to_non-comptime_var.zig index aa7bb91d1b..7714bb7282 100644 --- a/test/compile_errors/stage1/obj/assign_inline_fn_to_non-comptime_var.zig +++ b/test/compile_errors/stage1/obj/assign_inline_fn_to_non-comptime_var.zig @@ -4,7 +4,9 @@ export fn entry() void { } fn b() callconv(.Inline) void { } -// assign inline fn to non-comptime var +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: functions marked inline must be stored in const or comptime var // tmp.zig:5:1: note: declared here diff --git a/test/compile_errors/stage1/obj/assign_null_to_non-optional_pointer.zig b/test/compile_errors/stage1/obj/assign_null_to_non-optional_pointer.zig index 9c1aaf5031..110d6c2629 100644 --- a/test/compile_errors/stage1/obj/assign_null_to_non-optional_pointer.zig +++ b/test/compile_errors/stage1/obj/assign_null_to_non-optional_pointer.zig @@ -2,6 +2,8 @@ const a: *u8 = null; export fn entry() usize { return @sizeOf(@TypeOf(a)); } -// assign null to non-optional pointer +// error +// backend=stage1 +// target=native // // tmp.zig:1:16: error: expected type '*u8', found '@Type(.Null)' diff --git a/test/compile_errors/stage1/obj/assign_through_constant_pointer.zig b/test/compile_errors/stage1/obj/assign_through_constant_pointer.zig index 452e3ea617..69ba383843 100644 --- a/test/compile_errors/stage1/obj/assign_through_constant_pointer.zig +++ b/test/compile_errors/stage1/obj/assign_through_constant_pointer.zig @@ -3,6 +3,8 @@ export fn f() void { cstr[0] = 'W'; } -// assign through constant pointer +// error +// backend=stage1 +// target=native // // tmp.zig:3:13: error: cannot assign to constant diff --git a/test/compile_errors/stage1/obj/assign_through_constant_slice.zig b/test/compile_errors/stage1/obj/assign_through_constant_slice.zig index e36991897b..55d2e7e7c7 100644 --- a/test/compile_errors/stage1/obj/assign_through_constant_slice.zig +++ b/test/compile_errors/stage1/obj/assign_through_constant_slice.zig @@ -3,6 +3,8 @@ export fn f() void { cstr[0] = 'W'; } -// assign through constant slice +// error +// backend=stage1 +// target=native // // tmp.zig:3:13: error: cannot assign to constant diff --git a/test/compile_errors/stage1/obj/assign_to_constant_field.zig b/test/compile_errors/stage1/obj/assign_to_constant_field.zig index f371655fb5..610818118a 100644 --- a/test/compile_errors/stage1/obj/assign_to_constant_field.zig +++ b/test/compile_errors/stage1/obj/assign_to_constant_field.zig @@ -6,6 +6,8 @@ export fn derp() void { f.field = 0; } -// assign to constant field +// error +// backend=stage1 +// target=native // // tmp.zig:6:15: error: cannot assign to constant diff --git a/test/compile_errors/stage1/obj/assign_to_constant_variable.zig b/test/compile_errors/stage1/obj/assign_to_constant_variable.zig index 1e8dd6c06f..531f669c1a 100644 --- a/test/compile_errors/stage1/obj/assign_to_constant_variable.zig +++ b/test/compile_errors/stage1/obj/assign_to_constant_variable.zig @@ -3,6 +3,8 @@ export fn f() void { a = 4; } -// assign to constant variable +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: cannot assign to constant diff --git a/test/compile_errors/stage1/obj/assign_to_invalid_dereference.zig b/test/compile_errors/stage1/obj/assign_to_invalid_dereference.zig index 6466f7afc8..7fef5db83c 100644 --- a/test/compile_errors/stage1/obj/assign_to_invalid_dereference.zig +++ b/test/compile_errors/stage1/obj/assign_to_invalid_dereference.zig @@ -2,6 +2,8 @@ export fn entry() void { 'a'.* = 1; } -// assign to invalid dereference +// error +// backend=stage1 +// target=native // // tmp.zig:2:8: error: attempt to dereference non-pointer type 'comptime_int' diff --git a/test/compile_errors/stage1/obj/assign_too_big_number_to_u16.zig b/test/compile_errors/stage1/obj/assign_too_big_number_to_u16.zig index 13b5280588..3138ed5faf 100644 --- a/test/compile_errors/stage1/obj/assign_too_big_number_to_u16.zig +++ b/test/compile_errors/stage1/obj/assign_too_big_number_to_u16.zig @@ -3,6 +3,8 @@ export fn foo() void { _ = vga_mem; } -// assign too big number to u16 +// error +// backend=stage1 +// target=native // // tmp.zig:2:24: error: integer value 753664 cannot be coerced to type 'u16' diff --git a/test/compile_errors/stage1/obj/assign_unreachable.zig b/test/compile_errors/stage1/obj/assign_unreachable.zig index 78e2305abc..f87adcd994 100644 --- a/test/compile_errors/stage1/obj/assign_unreachable.zig +++ b/test/compile_errors/stage1/obj/assign_unreachable.zig @@ -2,7 +2,9 @@ export fn f() void { const a = return; } -// assign unreachable +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: unreachable code // tmp.zig:2:15: note: control flow is diverted here diff --git a/test/compile_errors/stage1/obj/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig b/test/compile_errors/stage1/obj/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig index f93650821b..8285991c05 100644 --- a/test/compile_errors/stage1/obj/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig +++ b/test/compile_errors/stage1/obj/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig @@ -14,6 +14,8 @@ export fn entry() void { _ = s; } -// assigning to struct or union fields that are not optionals with a function that returns an optional +// error +// backend=stage1 +// target=native // // tmp.zig:11:27: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type 'u8', found '?u8' diff --git a/test/compile_errors/stage1/obj/async_function_depends_on_its_own_frame.zig b/test/compile_errors/stage1/obj/async_function_depends_on_its_own_frame.zig index 0d1cd7f77e..4dde5860ba 100644 --- a/test/compile_errors/stage1/obj/async_function_depends_on_its_own_frame.zig +++ b/test/compile_errors/stage1/obj/async_function_depends_on_its_own_frame.zig @@ -6,6 +6,8 @@ fn amain() callconv(.Async) void { _ = x; } -// async function depends on its own frame +// error +// backend=stage1 +// target=native // // tmp.zig:4:1: error: cannot resolve '@Frame(amain)': function not fully analyzed yet diff --git a/test/compile_errors/stage1/obj/async_function_indirectly_depends_on_its_own_frame.zig b/test/compile_errors/stage1/obj/async_function_indirectly_depends_on_its_own_frame.zig index 809409f524..4dac0d4fa3 100644 --- a/test/compile_errors/stage1/obj/async_function_indirectly_depends_on_its_own_frame.zig +++ b/test/compile_errors/stage1/obj/async_function_indirectly_depends_on_its_own_frame.zig @@ -9,7 +9,9 @@ fn other() void { _ = x; } -// async function indirectly depends on its own frame +// error +// backend=stage1 +// target=native // // tmp.zig:4:1: error: unable to determine async function frame of 'amain' // tmp.zig:5:10: note: analysis of function 'other' depends on the frame diff --git a/test/compile_errors/stage1/obj/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig b/test/compile_errors/stage1/obj/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig index 7d97c2813e..dfc7ff5036 100644 --- a/test/compile_errors/stage1/obj/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig +++ b/test/compile_errors/stage1/obj/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig @@ -3,6 +3,8 @@ export fn entry() void { @atomicStore(u32, &x, 1, .Acquire); } -// atomic orderings of atomicStore Acquire or AcqRel +// error +// backend=stage1 +// target=native // // tmp.zig:3:30: error: @atomicStore atomic ordering must not be Acquire or AcqRel diff --git a/test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig b/test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig index d6d9ab478b..929950e754 100644 --- a/test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig +++ b/test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig @@ -4,6 +4,8 @@ export fn f() void { while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {} } -// atomic orderings of cmpxchg - failure stricter than success +// error +// backend=stage1 +// target=native // // tmp.zig:4:81: error: failure atomic ordering must be no stricter than success diff --git a/test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig b/test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig index 770c08f3fe..fe3344e536 100644 --- a/test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig +++ b/test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig @@ -4,6 +4,8 @@ export fn f() void { while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {} } -// atomic orderings of cmpxchg - success Monotonic or stricter +// error +// backend=stage1 +// target=native // // tmp.zig:4:58: error: success atomic ordering must be Monotonic or stricter diff --git a/test/compile_errors/stage1/obj/atomic_orderings_of_fence_Acquire_or_stricter.zig b/test/compile_errors/stage1/obj/atomic_orderings_of_fence_Acquire_or_stricter.zig index 34ee200eeb..8a01871a80 100644 --- a/test/compile_errors/stage1/obj/atomic_orderings_of_fence_Acquire_or_stricter.zig +++ b/test/compile_errors/stage1/obj/atomic_orderings_of_fence_Acquire_or_stricter.zig @@ -2,6 +2,8 @@ export fn entry() void { @fence(.Monotonic); } -// atomic orderings of fence Acquire or stricter +// error +// backend=stage1 +// target=native // // tmp.zig:2:12: error: atomic ordering must be Acquire or stricter diff --git a/test/compile_errors/stage1/obj/atomicrmw_with_bool_op_not_.Xchg.zig b/test/compile_errors/stage1/obj/atomicrmw_with_bool_op_not_.Xchg.zig index 03309737cf..0b5b0cd3c1 100644 --- a/test/compile_errors/stage1/obj/atomicrmw_with_bool_op_not_.Xchg.zig +++ b/test/compile_errors/stage1/obj/atomicrmw_with_bool_op_not_.Xchg.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = @atomicRmw(bool, &x, .Add, true, .SeqCst); } -// atomicrmw with bool op not .Xchg +// error +// backend=stage1 +// target=native // // tmp.zig:3:30: error: @atomicRmw with bool only allowed with .Xchg diff --git a/test/compile_errors/stage1/obj/atomicrmw_with_enum_op_not_.Xchg.zig b/test/compile_errors/stage1/obj/atomicrmw_with_enum_op_not_.Xchg.zig index 95e6a7d95d..adbf4ca45e 100644 --- a/test/compile_errors/stage1/obj/atomicrmw_with_enum_op_not_.Xchg.zig +++ b/test/compile_errors/stage1/obj/atomicrmw_with_enum_op_not_.Xchg.zig @@ -9,6 +9,8 @@ export fn entry() void { _ = @atomicRmw(E, &x, .Add, .b, .SeqCst); } -// atomicrmw with enum op not .Xchg +// error +// backend=stage1 +// target=native // // tmp.zig:9:27: error: @atomicRmw with enum only allowed with .Xchg diff --git a/test/compile_errors/stage1/obj/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig b/test/compile_errors/stage1/obj/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig index a7359dbf32..4f3ebfcef6 100644 --- a/test/compile_errors/stage1/obj/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig +++ b/test/compile_errors/stage1/obj/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = @atomicRmw(f32, &x, .And, 2, .SeqCst); } -// atomicrmw with float op not .Xchg, .Add or .Sub +// error +// backend=stage1 +// target=native // // tmp.zig:3:29: error: @atomicRmw with float only allowed with .Xchg, .Add and .Sub diff --git a/test/compile_errors/stage1/obj/attempt_to_cast_enum_literal_to_error.zig b/test/compile_errors/stage1/obj/attempt_to_cast_enum_literal_to_error.zig index fd120af3eb..f1c30815fc 100644 --- a/test/compile_errors/stage1/obj/attempt_to_cast_enum_literal_to_error.zig +++ b/test/compile_errors/stage1/obj/attempt_to_cast_enum_literal_to_error.zig @@ -4,6 +4,8 @@ export fn entry() void { } } -// attempt to cast enum literal to error +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: expected type 'error{Hi}', found '@Type(.EnumLiteral)' diff --git a/test/compile_errors/stage1/obj/attempt_to_close_over_comptime_variable_from_outer_scope.zig b/test/compile_errors/stage1/obj/attempt_to_close_over_comptime_variable_from_outer_scope.zig index 5eb5db3dc8..58eb9d19d2 100644 --- a/test/compile_errors/stage1/obj/attempt_to_close_over_comptime_variable_from_outer_scope.zig +++ b/test/compile_errors/stage1/obj/attempt_to_close_over_comptime_variable_from_outer_scope.zig @@ -5,7 +5,9 @@ fn SimpleList(comptime L: usize) type { }; } -// attempt to close over comptime variable from outer scope +// error +// backend=stage1 +// target=native // // tmp.zig:4:19: error: mutable 'T' not accessible from here // tmp.zig:2:9: note: declared mutable here diff --git a/test/compile_errors/stage1/obj/attempt_to_create_17_bit_float_type.zig b/test/compile_errors/stage1/obj/attempt_to_create_17_bit_float_type.zig index bdea578696..ba7393fcb7 100644 --- a/test/compile_errors/stage1/obj/attempt_to_create_17_bit_float_type.zig +++ b/test/compile_errors/stage1/obj/attempt_to_create_17_bit_float_type.zig @@ -3,6 +3,8 @@ comptime { _ = @Type(.{ .Float = .{ .bits = 17 } }); } -// attempt to create 17 bit float type +// error +// backend=stage1 +// target=native // // tmp.zig:3:16: error: 17-bit float unsupported diff --git a/test/compile_errors/stage1/obj/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig b/test/compile_errors/stage1/obj/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig index 00ee26033d..e9bbf921ab 100644 --- a/test/compile_errors/stage1/obj/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig +++ b/test/compile_errors/stage1/obj/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig @@ -7,6 +7,8 @@ export fn entry() void { _ = x; } -// attempt to negate a non-integer, non-float or non-vector type +// error +// backend=stage1 +// target=native // // tmp.zig:6:15: error: negation of type 'anyerror!u32' diff --git a/test/compile_errors/stage1/obj/attempt_to_use_0_bit_type_in_extern_fn.zig b/test/compile_errors/stage1/obj/attempt_to_use_0_bit_type_in_extern_fn.zig index d7767e25e9..021cd97f19 100644 --- a/test/compile_errors/stage1/obj/attempt_to_use_0_bit_type_in_extern_fn.zig +++ b/test/compile_errors/stage1/obj/attempt_to_use_0_bit_type_in_extern_fn.zig @@ -9,7 +9,9 @@ export fn entry2() void { bar(&{}); } -// attempt to use 0 bit type in extern fn +// error +// backend=stage1 +// target=native // // tmp.zig:1:23: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'C' // tmp.zig:7:11: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'C' diff --git a/test/compile_errors/stage1/obj/attempted_double_ampersand.zig b/test/compile_errors/stage1/obj/attempted_double_ampersand.zig index cc92bc2fae..c749b172c6 100644 --- a/test/compile_errors/stage1/obj/attempted_double_ampersand.zig +++ b/test/compile_errors/stage1/obj/attempted_double_ampersand.zig @@ -5,6 +5,8 @@ export fn entry(a: bool, b: bool) i32 { return 5678; } -// attempted `&&` +// error +// backend=stage1 +// target=native // // tmp.zig:2:11: error: ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND diff --git a/test/compile_errors/stage1/obj/attempted_double_pipe_on_boolean_values.zig b/test/compile_errors/stage1/obj/attempted_double_pipe_on_boolean_values.zig index 8e67b4950d..b991b1b61b 100644 --- a/test/compile_errors/stage1/obj/attempted_double_pipe_on_boolean_values.zig +++ b/test/compile_errors/stage1/obj/attempted_double_pipe_on_boolean_values.zig @@ -5,7 +5,9 @@ export fn entry(a: bool, b: bool) i32 { return 5678; } -// attempted `||` on boolean values +// error +// backend=stage1 +// target=native // // tmp.zig:2:9: error: expected error set type, found 'bool' // tmp.zig:2:11: note: `||` merges error sets; `or` performs boolean OR diff --git a/test/compile_errors/stage1/obj/attempted_implicit_cast_from_T_to_slice_const_T.zig b/test/compile_errors/stage1/obj/attempted_implicit_cast_from_T_to_slice_const_T.zig index 4776104928..c3b0847129 100644 --- a/test/compile_errors/stage1/obj/attempted_implicit_cast_from_T_to_slice_const_T.zig +++ b/test/compile_errors/stage1/obj/attempted_implicit_cast_from_T_to_slice_const_T.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = x; } -// attempted implicit cast from T to [*]const T +// error +// backend=stage1 +// target=native // // tmp.zig:2:30: error: expected type '[*]const bool', found 'bool' diff --git a/test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_array_len_1_T.zig b/test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_array_len_1_T.zig index d389c572ca..760866dd65 100644 --- a/test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_array_len_1_T.zig +++ b/test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_array_len_1_T.zig @@ -6,7 +6,9 @@ export fn entry(byte: u8) void { _ = byte; } -// attempted implicit cast from *const T to *[1]T +// error +// backend=stage1 +// target=native // // tmp.zig:4:22: error: expected type '*[1]i32', found '*const i32' // tmp.zig:4:22: note: cast discards const qualifier diff --git a/test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_sliceT.zig b/test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_sliceT.zig index 70d1cbece2..3ff9937983 100644 --- a/test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_sliceT.zig +++ b/test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_sliceT.zig @@ -4,6 +4,8 @@ export fn entry() void { _ = x; } -// attempted implicit cast from *const T to []T +// error +// backend=stage1 +// target=native // // tmp.zig:3:23: error: expected type '[]u32', found '*const u32' diff --git a/test/compile_errors/stage1/obj/bad_alignCast_at_comptime.zig b/test/compile_errors/stage1/obj/bad_alignCast_at_comptime.zig index 7396dfc4dd..f1ed25e191 100644 --- a/test/compile_errors/stage1/obj/bad_alignCast_at_comptime.zig +++ b/test/compile_errors/stage1/obj/bad_alignCast_at_comptime.zig @@ -4,6 +4,8 @@ comptime { _ = aligned; } -// bad @alignCast at comptime +// error +// backend=stage1 +// target=native // // tmp.zig:3:35: error: pointer address 0x1 is not aligned to 4 bytes diff --git a/test/compile_errors/stage1/obj/bad_alignment_in_asynccall.zig b/test/compile_errors/stage1/obj/bad_alignment_in_asynccall.zig new file mode 100644 index 0000000000..3999140009 --- /dev/null +++ b/test/compile_errors/stage1/obj/bad_alignment_in_asynccall.zig @@ -0,0 +1,12 @@ +export fn entry() void { + var ptr: fn () callconv(.Async) void = func; + var bytes: [64]u8 = undefined; + _ = @asyncCall(&bytes, {}, ptr, .{}); +} +fn func() callconv(.Async) void {} + +// error +// backend=stage1 +// target=aarch64-linux-none +// +// tmp.zig:4:21: error: expected type '[]align(8) u8', found '*[64]u8' diff --git a/test/compile_errors/stage1/obj/bad_alignment_in_implicit_cast_from_array_pointer_to_slice.zig b/test/compile_errors/stage1/obj/bad_alignment_in_implicit_cast_from_array_pointer_to_slice.zig index ecfc5523d0..12064bd9ab 100644 --- a/test/compile_errors/stage1/obj/bad_alignment_in_implicit_cast_from_array_pointer_to_slice.zig +++ b/test/compile_errors/stage1/obj/bad_alignment_in_implicit_cast_from_array_pointer_to_slice.zig @@ -4,6 +4,8 @@ export fn a() void { _ = y; } -// bad alignment in implicit cast from array pointer to slice +// error +// backend=stage1 +// target=native // // tmp.zig:3:30: error: expected type '[]align(16) u8', found '*[10]u8' diff --git a/test/compile_errors/stage1/obj/bad_alignment_type.zig b/test/compile_errors/stage1/obj/bad_alignment_type.zig index 902ffc79d6..a4d8b7d68f 100644 --- a/test/compile_errors/stage1/obj/bad_alignment_type.zig +++ b/test/compile_errors/stage1/obj/bad_alignment_type.zig @@ -7,7 +7,9 @@ export fn entry2() void { _ = x; } -// bad alignment type +// error +// backend=stage1 +// target=native // // tmp.zig:2:20: error: expected type 'u29', found 'bool' // tmp.zig:6:19: error: fractional component prevents float value 12.340000 from being casted to type 'u29' diff --git a/test/compile_errors/stage1/obj/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig b/test/compile_errors/stage1/obj/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig index f32301f9ff..7e44ad5057 100644 --- a/test/compile_errors/stage1/obj/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig +++ b/test/compile_errors/stage1/obj/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig @@ -10,6 +10,8 @@ export fn entry() void { _ = Block; } -// bad identifier in function with struct defined inside function which references local const +// error +// backend=stage1 +// target=native // // tmp.zig:8:5: error: use of undeclared identifier 'bogus' diff --git a/test/compile_errors/stage1/obj/bad_import.zig b/test/compile_errors/stage1/obj/bad_import.zig index f6e93559b2..1a7ab5f00b 100644 --- a/test/compile_errors/stage1/obj/bad_import.zig +++ b/test/compile_errors/stage1/obj/bad_import.zig @@ -1,5 +1,7 @@ const bogus = @import("bogus-does-not-exist.zig",); -// bad import +// error +// backend=stage1 +// target=native // // tmp.zig:1:23: error: unable to load '${DIR}bogus-does-not-exist.zig': FileNotFound diff --git a/test/compile_errors/stage1/obj/bad_usage_of_call.zig b/test/compile_errors/stage1/obj/bad_usage_of_call.zig index 928c39b5a8..c77ccf1a52 100644 --- a/test/compile_errors/stage1/obj/bad_usage_of_call.zig +++ b/test/compile_errors/stage1/obj/bad_usage_of_call.zig @@ -19,7 +19,9 @@ fn bar() callconv(.Inline) void {} fn baz1() void {} fn baz2() void {} -// bad usage of @call +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected tuple or struct, found 'void' // tmp.zig:5:14: error: unable to perform 'never_inline' call at compile-time diff --git a/test/compile_errors/stage1/obj/bin_and_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/bin_and_assign_on_undefined_value.zig index d9dbdea741..1076f9abdb 100644 --- a/test/compile_errors/stage1/obj/bin_and_assign_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/bin_and_assign_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { a &= a; } -// bin and assign on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/bin_and_on_undefined_value.zig b/test/compile_errors/stage1/obj/bin_and_on_undefined_value.zig index 6775cd56ea..a53d9a10ac 100644 --- a/test/compile_errors/stage1/obj/bin_and_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/bin_and_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = a & a; } -// bin and on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/bin_not_on_undefined_value.zig b/test/compile_errors/stage1/obj/bin_not_on_undefined_value.zig index 0e543c4f00..7e0f554f0b 100644 --- a/test/compile_errors/stage1/obj/bin_not_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/bin_not_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = ~a; } -// bin not on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:10: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/bin_or_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/bin_or_assign_on_undefined_value.zig index bfbeb7ce3e..cc61fc03e1 100644 --- a/test/compile_errors/stage1/obj/bin_or_assign_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/bin_or_assign_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { a |= a; } -// bin or assign on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/bin_or_on_undefined_value.zig b/test/compile_errors/stage1/obj/bin_or_on_undefined_value.zig index 7a62ace9dd..7ac30bcb4e 100644 --- a/test/compile_errors/stage1/obj/bin_or_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/bin_or_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = a | a; } -// bin or on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/bin_xor_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/bin_xor_assign_on_undefined_value.zig index bea5e1e089..37e3eae321 100644 --- a/test/compile_errors/stage1/obj/bin_xor_assign_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/bin_xor_assign_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { a ^= a; } -// bin xor assign on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/bin_xor_on_undefined_value.zig b/test/compile_errors/stage1/obj/bin_xor_on_undefined_value.zig index f6ddb24521..f1656b6013 100644 --- a/test/compile_errors/stage1/obj/bin_xor_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/bin_xor_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = a ^ a; } -// bin xor on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/binary_not_on_number_literal.zig b/test/compile_errors/stage1/obj/binary_not_on_number_literal.zig index 12f5953f16..34ddf9adfa 100644 --- a/test/compile_errors/stage1/obj/binary_not_on_number_literal.zig +++ b/test/compile_errors/stage1/obj/binary_not_on_number_literal.zig @@ -4,6 +4,8 @@ var block_aligned_stuff: usize = (4 + TINY_QUANTUM_SIZE) & ~(TINY_QUANTUM_SIZE - export fn entry() usize { return @sizeOf(@TypeOf(block_aligned_stuff)); } -// binary not on number literal +// error +// backend=stage1 +// target=native // // tmp.zig:3:60: error: unable to perform binary not operation on type 'comptime_int' diff --git a/test/compile_errors/stage1/obj/bitCast_same_size_but_bit_count_mismatch.zig b/test/compile_errors/stage1/obj/bitCast_same_size_but_bit_count_mismatch.zig index e4945a1194..9150dc3285 100644 --- a/test/compile_errors/stage1/obj/bitCast_same_size_but_bit_count_mismatch.zig +++ b/test/compile_errors/stage1/obj/bitCast_same_size_but_bit_count_mismatch.zig @@ -3,6 +3,8 @@ export fn entry(byte: u8) void { _ = oops; } -// @bitCast same size but bit count mismatch +// error +// backend=stage1 +// target=native // // tmp.zig:2:25: error: destination type 'u7' has 7 bits but source type 'u8' has 8 bits diff --git a/test/compile_errors/stage1/obj/bitCast_to_enum_type.zig b/test/compile_errors/stage1/obj/bitCast_to_enum_type.zig index dbb73eba18..4d63ab9e01 100644 --- a/test/compile_errors/stage1/obj/bitCast_to_enum_type.zig +++ b/test/compile_errors/stage1/obj/bitCast_to_enum_type.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = y; } -// bitCast to enum type +// error +// backend=stage1 +// target=native // // tmp.zig:2:24: error: cannot cast a value of type 'y' diff --git a/test/compile_errors/stage1/obj/bitCast_with_different_sizes_inside_an_expression.zig b/test/compile_errors/stage1/obj/bitCast_with_different_sizes_inside_an_expression.zig index bb5c0c5936..64caef7cd0 100644 --- a/test/compile_errors/stage1/obj/bitCast_with_different_sizes_inside_an_expression.zig +++ b/test/compile_errors/stage1/obj/bitCast_with_different_sizes_inside_an_expression.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = foo; } -// @bitCast with different sizes inside an expression +// error +// backend=stage1 +// target=native // // tmp.zig:2:25: error: destination type 'u8' has size 1 but source type 'f32' has size 4 diff --git a/test/compile_errors/stage1/obj/bit_shifting_only_works_on_integer_types.zig b/test/compile_errors/stage1/obj/bit_shifting_only_works_on_integer_types.zig index 98fa3128aa..7d1c5873a1 100644 --- a/test/compile_errors/stage1/obj/bit_shifting_only_works_on_integer_types.zig +++ b/test/compile_errors/stage1/obj/bit_shifting_only_works_on_integer_types.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = x; } -// bit shifting only works on integer types +// error +// backend=stage1 +// target=native // // tmp.zig:2:16: error: bit shifting operation expected integer type, found '*const u8' diff --git a/test/compile_errors/stage1/obj/bogus_compile_var.zig b/test/compile_errors/stage1/obj/bogus_compile_var.zig index 05777b9ecd..845d943c7d 100644 --- a/test/compile_errors/stage1/obj/bogus_compile_var.zig +++ b/test/compile_errors/stage1/obj/bogus_compile_var.zig @@ -1,6 +1,8 @@ const x = @import("builtin").bogus; export fn entry() usize { return @sizeOf(@TypeOf(x)); } -// bogus compile var +// error +// backend=stage1 +// target=native // // tmp.zig:1:29: error: container 'builtin' has no member called 'bogus' diff --git a/test/compile_errors/stage1/obj/bogus_method_call_on_slice.zig b/test/compile_errors/stage1/obj/bogus_method_call_on_slice.zig index 8d34a09817..3ede578b34 100644 --- a/test/compile_errors/stage1/obj/bogus_method_call_on_slice.zig +++ b/test/compile_errors/stage1/obj/bogus_method_call_on_slice.zig @@ -4,6 +4,8 @@ fn f(m: []const u8) void { } export fn entry() usize { return @sizeOf(@TypeOf(f)); } -// bogus method call on slice +// error +// backend=stage1 +// target=native // // tmp.zig:3:6: error: no member named 'copy' in '[]const u8' diff --git a/test/compile_errors/stage1/obj/bool_not_on_undefined_value.zig b/test/compile_errors/stage1/obj/bool_not_on_undefined_value.zig index 71ea7d5e96..5c99a9739b 100644 --- a/test/compile_errors/stage1/obj/bool_not_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/bool_not_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = !a; } -// bool not on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:10: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/branch_on_undefined_value.zig b/test/compile_errors/stage1/obj/branch_on_undefined_value.zig index d02ac22a2a..f4b922a332 100644 --- a/test/compile_errors/stage1/obj/branch_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/branch_on_undefined_value.zig @@ -2,6 +2,8 @@ const x = if (undefined) true else false; export fn entry() usize { return @sizeOf(@TypeOf(x)); } -// branch on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:1:15: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/cImport_with_bogus_include.zig b/test/compile_errors/stage1/obj/cImport_with_bogus_include.zig index 89627b354f..141f21cb9a 100644 --- a/test/compile_errors/stage1/obj/cImport_with_bogus_include.zig +++ b/test/compile_errors/stage1/obj/cImport_with_bogus_include.zig @@ -1,7 +1,9 @@ const c = @cImport(@cInclude("bogus.h")); export fn entry() usize { return @sizeOf(@TypeOf(c.bogo)); } -// @cImport with bogus include +// error +// backend=stage1 +// target=native // // tmp.zig:1:11: error: C import failed // .h:1:10: note: 'bogus.h' file not found diff --git a/test/compile_errors/stage1/obj/call_assigned_to_constant.zig b/test/compile_errors/stage1/obj/call_assigned_to_constant.zig index fb2febadb2..ca4e8b67cd 100644 --- a/test/compile_errors/stage1/obj/call_assigned_to_constant.zig +++ b/test/compile_errors/stage1/obj/call_assigned_to_constant.zig @@ -16,7 +16,9 @@ export fn entry1() void { baz = bar(42); } -// call assigned to constant +// error +// backend=stage1 +// target=native // // tmp.zig:12:14: error: cannot assign to constant // tmp.zig:16:14: error: cannot assign to constant diff --git a/test/compile_errors/stage1/obj/call_with_new_stack_on_unsupported_target.zig b/test/compile_errors/stage1/obj/call_with_new_stack_on_unsupported_target.zig new file mode 100644 index 0000000000..7cb504a3f9 --- /dev/null +++ b/test/compile_errors/stage1/obj/call_with_new_stack_on_unsupported_target.zig @@ -0,0 +1,11 @@ +var buf: [10]u8 align(16) = undefined; +export fn entry() void { + @call(.{ .stack = &buf }, foo, .{}); +} +fn foo() void {} + +// error +// backend=stage1 +// target=wasm32-wasi-none +// +// tmp.zig:3:5: error: target arch 'wasm32' does not support calling with a new stack diff --git a/test/compile_errors/stage1/obj/callconv_apcs_aapcs_aapcsvfp_on_unsupported_platform.zig b/test/compile_errors/stage1/obj/callconv_apcs_aapcs_aapcsvfp_on_unsupported_platform.zig new file mode 100644 index 0000000000..fa06287803 --- /dev/null +++ b/test/compile_errors/stage1/obj/callconv_apcs_aapcs_aapcsvfp_on_unsupported_platform.zig @@ -0,0 +1,11 @@ +export fn entry1() callconv(.APCS) void {} +export fn entry2() callconv(.AAPCS) void {} +export fn entry3() callconv(.AAPCSVFP) void {} + +// error +// backend=stage1 +// target=x86_64-linux-none +// +// tmp.zig:1:29: error: callconv 'APCS' is only available on ARM, not x86_64 +// tmp.zig:2:29: error: callconv 'AAPCS' is only available on ARM, not x86_64 +// tmp.zig:3:29: error: callconv 'AAPCSVFP' is only available on ARM, not x86_64 diff --git a/test/compile_errors/stage1/obj/callconv_interrupt_on_unsupported_platform.zig b/test/compile_errors/stage1/obj/callconv_interrupt_on_unsupported_platform.zig new file mode 100644 index 0000000000..8304fb90c1 --- /dev/null +++ b/test/compile_errors/stage1/obj/callconv_interrupt_on_unsupported_platform.zig @@ -0,0 +1,7 @@ +export fn entry() callconv(.Interrupt) void {} + +// error +// backend=stage1 +// target=aarch64-linux-none +// +// tmp.zig:1:28: error: callconv 'Interrupt' is only available on x86, x86_64, AVR, and MSP430, not aarch64 diff --git a/test/compile_errors/stage1/obj/callconv_signal_on_unsupported_platform.zig b/test/compile_errors/stage1/obj/callconv_signal_on_unsupported_platform.zig new file mode 100644 index 0000000000..4b19b188fd --- /dev/null +++ b/test/compile_errors/stage1/obj/callconv_signal_on_unsupported_platform.zig @@ -0,0 +1,7 @@ +export fn entry() callconv(.Signal) void {} + +// error +// backend=stage1 +// target=x86_64-linux-none +// +// tmp.zig:1:28: error: callconv 'Signal' is only available on AVR, not x86_64 diff --git a/test/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-0.zig b/test/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-0.zig new file mode 100644 index 0000000000..f4994107cd --- /dev/null +++ b/test/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-0.zig @@ -0,0 +1,23 @@ +const F1 = fn () callconv(.Stdcall) void; +const F2 = fn () callconv(.Fastcall) void; +const F3 = fn () callconv(.Thiscall) void; +export fn entry1() void { + var a: F1 = undefined; + _ = a; +} +export fn entry2() void { + var a: F2 = undefined; + _ = a; +} +export fn entry3() void { + var a: F3 = undefined; + _ = a; +} + +// error +// backend=stage1 +// target=x86_64-linux-none +// +// tmp.zig:1:27: error: callconv 'Stdcall' is only available on x86, not x86_64 +// tmp.zig:2:27: error: callconv 'Fastcall' is only available on x86, not x86_64 +// tmp.zig:3:27: error: callconv 'Thiscall' is only available on x86, not x86_64 diff --git a/test/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-1.zig b/test/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-1.zig new file mode 100644 index 0000000000..aa23f9ae51 --- /dev/null +++ b/test/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-1.zig @@ -0,0 +1,11 @@ +export fn entry1() callconv(.Stdcall) void {} +export fn entry2() callconv(.Fastcall) void {} +export fn entry3() callconv(.Thiscall) void {} + +// error +// backend=stage1 +// target=x86_64-linux-none +// +// tmp.zig:1:29: error: callconv 'Stdcall' is only available on x86, not x86_64 +// tmp.zig:2:29: error: callconv 'Fastcall' is only available on x86, not x86_64 +// tmp.zig:3:29: error: callconv 'Thiscall' is only available on x86, not x86_64 diff --git a/test/compile_errors/stage1/obj/callconv_vectorcall_on_unsupported_platform.zig b/test/compile_errors/stage1/obj/callconv_vectorcall_on_unsupported_platform.zig new file mode 100644 index 0000000000..e60e6ab42c --- /dev/null +++ b/test/compile_errors/stage1/obj/callconv_vectorcall_on_unsupported_platform.zig @@ -0,0 +1,7 @@ +export fn entry() callconv(.Vectorcall) void {} + +// error +// backend=stage1 +// target=x86_64-linux-none +// +// tmp.zig:1:28: error: callconv 'Vectorcall' is only available on x86 and AArch64, not x86_64 diff --git a/test/compile_errors/stage1/obj/calling_a_generic_function_only_known_at_runtime.zig b/test/compile_errors/stage1/obj/calling_a_generic_function_only_known_at_runtime.zig index c31b18bb6f..3081f0f1e1 100644 --- a/test/compile_errors/stage1/obj/calling_a_generic_function_only_known_at_runtime.zig +++ b/test/compile_errors/stage1/obj/calling_a_generic_function_only_known_at_runtime.zig @@ -7,6 +7,8 @@ pub fn main() !void { foos[0](true); } -// calling a generic function only known at runtime +// error +// backend=stage1 +// target=native // // tmp.zig:7:9: error: calling a generic function requires compile-time known function value diff --git a/test/compile_errors/stage1/obj/calling_function_with_naked_calling_convention.zig b/test/compile_errors/stage1/obj/calling_function_with_naked_calling_convention.zig index c99e5b7831..401f84e687 100644 --- a/test/compile_errors/stage1/obj/calling_function_with_naked_calling_convention.zig +++ b/test/compile_errors/stage1/obj/calling_function_with_naked_calling_convention.zig @@ -3,7 +3,9 @@ export fn entry() void { } fn foo() callconv(.Naked) void { } -// calling function with naked calling convention +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: unable to call function with naked calling convention // tmp.zig:4:1: note: declared here diff --git a/test/compile_errors/stage1/obj/calling_var_args_extern_function_passing_array_instead_of_pointer.zig b/test/compile_errors/stage1/obj/calling_var_args_extern_function_passing_array_instead_of_pointer.zig index e3cc4425db..a71ab051f2 100644 --- a/test/compile_errors/stage1/obj/calling_var_args_extern_function_passing_array_instead_of_pointer.zig +++ b/test/compile_errors/stage1/obj/calling_var_args_extern_function_passing_array_instead_of_pointer.zig @@ -3,6 +3,8 @@ export fn entry() void { } pub extern fn foo(format: *const u8, ...) void; -// calling var args extern function, passing array instead of pointer +// error +// backend=stage1 +// target=native // // tmp.zig:2:16: error: expected type '*const u8', found '[5:0]u8' diff --git a/test/compile_errors/stage1/obj/cannot_break_out_of_defer_expression.zig b/test/compile_errors/stage1/obj/cannot_break_out_of_defer_expression.zig index 002a7717d3..3c7ae4fa2f 100644 --- a/test/compile_errors/stage1/obj/cannot_break_out_of_defer_expression.zig +++ b/test/compile_errors/stage1/obj/cannot_break_out_of_defer_expression.zig @@ -6,6 +6,8 @@ export fn foo() void { } } -// cannot break out of defer expression +// error +// backend=stage1 +// target=native // // tmp.zig:4:13: error: cannot break out of defer expression diff --git a/test/compile_errors/stage1/obj/cannot_continue_out_of_defer_expression.zig b/test/compile_errors/stage1/obj/cannot_continue_out_of_defer_expression.zig index 8adb7eafd6..56b8ced05b 100644 --- a/test/compile_errors/stage1/obj/cannot_continue_out_of_defer_expression.zig +++ b/test/compile_errors/stage1/obj/cannot_continue_out_of_defer_expression.zig @@ -6,6 +6,8 @@ export fn foo() void { } } -// cannot continue out of defer expression +// error +// backend=stage1 +// target=native // // tmp.zig:4:13: error: cannot continue out of defer expression diff --git a/test/compile_errors/stage1/obj/capture_group_on_switch_prong_with_incompatible_payload_types.zig b/test/compile_errors/stage1/obj/capture_group_on_switch_prong_with_incompatible_payload_types.zig index 8a4c94f503..ce2068d6c7 100644 --- a/test/compile_errors/stage1/obj/capture_group_on_switch_prong_with_incompatible_payload_types.zig +++ b/test/compile_errors/stage1/obj/capture_group_on_switch_prong_with_incompatible_payload_types.zig @@ -12,7 +12,9 @@ comptime { } } -// capture group on switch prong with incompatible payload types +// error +// backend=stage1 +// target=native // // tmp.zig:8:20: error: capture group with incompatible types // tmp.zig:8:9: note: type 'usize' here diff --git a/test/compile_errors/stage1/obj/cast_enum_literal_to_enum_but_it_doesnt_match.zig b/test/compile_errors/stage1/obj/cast_enum_literal_to_enum_but_it_doesnt_match.zig index 10717ff392..ba53646efc 100644 --- a/test/compile_errors/stage1/obj/cast_enum_literal_to_enum_but_it_doesnt_match.zig +++ b/test/compile_errors/stage1/obj/cast_enum_literal_to_enum_but_it_doesnt_match.zig @@ -7,7 +7,9 @@ export fn entry() void { _ = x; } -// cast enum literal to enum but it doesn't match +// error +// backend=stage1 +// target=native // // tmp.zig:6:20: error: enum 'Foo' has no field named 'c' // tmp.zig:1:13: note: 'Foo' declared here diff --git a/test/compile_errors/stage1/obj/cast_error_union_of_global_error_set_to_error_union_of_smaller_error_set.zig b/test/compile_errors/stage1/obj/cast_error_union_of_global_error_set_to_error_union_of_smaller_error_set.zig index fbb68c30c8..08962afc85 100644 --- a/test/compile_errors/stage1/obj/cast_error_union_of_global_error_set_to_error_union_of_smaller_error_set.zig +++ b/test/compile_errors/stage1/obj/cast_error_union_of_global_error_set_to_error_union_of_smaller_error_set.zig @@ -7,7 +7,9 @@ fn foo() anyerror!i32 { return error.B; } -// cast error union of global error set to error union of smaller error set +// error +// backend=stage1 +// target=native // // tmp.zig:3:35: error: expected type 'SmallErrorSet!i32', found 'anyerror!i32' // tmp.zig:3:35: note: error set 'anyerror' cannot cast into error set 'SmallErrorSet' diff --git a/test/compile_errors/stage1/obj/cast_global_error_set_to_error_set.zig b/test/compile_errors/stage1/obj/cast_global_error_set_to_error_set.zig index f37ffce229..be9487cc5a 100644 --- a/test/compile_errors/stage1/obj/cast_global_error_set_to_error_set.zig +++ b/test/compile_errors/stage1/obj/cast_global_error_set_to_error_set.zig @@ -7,7 +7,9 @@ fn foo() anyerror { return error.B; } -// cast global error set to error set +// error +// backend=stage1 +// target=native // // tmp.zig:3:31: error: expected type 'SmallErrorSet', found 'anyerror' // tmp.zig:3:31: note: cannot cast global error set into smaller set diff --git a/test/compile_errors/stage1/obj/cast_negative_integer_literal_to_usize.zig b/test/compile_errors/stage1/obj/cast_negative_integer_literal_to_usize.zig index 37e42c062c..763e0c8a23 100644 --- a/test/compile_errors/stage1/obj/cast_negative_integer_literal_to_usize.zig +++ b/test/compile_errors/stage1/obj/cast_negative_integer_literal_to_usize.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = x; } -// cast negative integer literal to usize +// error +// backend=stage1 +// target=native // // tmp.zig:2:26: error: cannot cast negative value -10 to unsigned integer type 'usize' diff --git a/test/compile_errors/stage1/obj/cast_negative_value_to_unsigned_integer.zig b/test/compile_errors/stage1/obj/cast_negative_value_to_unsigned_integer.zig index e740d406f6..d2e3187140 100644 --- a/test/compile_errors/stage1/obj/cast_negative_value_to_unsigned_integer.zig +++ b/test/compile_errors/stage1/obj/cast_negative_value_to_unsigned_integer.zig @@ -9,7 +9,9 @@ export fn entry1() void { _ = unsigned; } -// cast negative value to unsigned integer +// error +// backend=stage1 +// target=native // // tmp.zig:3:22: error: attempt to cast negative value to unsigned integer // tmp.zig:8:27: error: cannot cast negative value -1 to unsigned integer type 'u32' diff --git a/test/compile_errors/stage1/obj/cast_unreachable.zig b/test/compile_errors/stage1/obj/cast_unreachable.zig index 94fa121770..b043108737 100644 --- a/test/compile_errors/stage1/obj/cast_unreachable.zig +++ b/test/compile_errors/stage1/obj/cast_unreachable.zig @@ -3,7 +3,9 @@ fn f() i32 { } export fn entry() void { _ = f(); } -// cast unreachable +// error +// backend=stage1 +// target=native // // tmp.zig:2:12: error: unreachable code // tmp.zig:2:21: note: control flow is diverted here diff --git a/test/compile_errors/stage1/obj/casting_bit_offset_pointer_to_regular_pointer.zig b/test/compile_errors/stage1/obj/casting_bit_offset_pointer_to_regular_pointer.zig index 9b6c5ea0e6..f109cdf295 100644 --- a/test/compile_errors/stage1/obj/casting_bit_offset_pointer_to_regular_pointer.zig +++ b/test/compile_errors/stage1/obj/casting_bit_offset_pointer_to_regular_pointer.zig @@ -14,6 +14,8 @@ fn bar(x: *const u3) u3 { export fn entry() usize { return @sizeOf(@TypeOf(foo)); } -// casting bit offset pointer to regular pointer +// error +// backend=stage1 +// target=native // // tmp.zig:8:26: error: expected type '*const u3', found '*align(:3:1) const u3' diff --git a/test/compile_errors/stage1/obj/catch_on_undefined_value.zig b/test/compile_errors/stage1/obj/catch_on_undefined_value.zig index 9d22d07219..53a5f1c2a9 100644 --- a/test/compile_errors/stage1/obj/catch_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/catch_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = a catch false; } -// catch on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:11: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/chained_comparison_operators.zig b/test/compile_errors/stage1/obj/chained_comparison_operators.zig index 89e1ea1e75..1e4ed8d82e 100644 --- a/test/compile_errors/stage1/obj/chained_comparison_operators.zig +++ b/test/compile_errors/stage1/obj/chained_comparison_operators.zig @@ -2,6 +2,8 @@ export fn a(value: u32) bool { return 1 < value < 1000; } -// chained comparison operators +// error +// backend=stage1 +// target=native // // tmp.zig:2:22: error: comparison operators cannot be chained diff --git a/test/compile_errors/stage1/obj/cmpxchg_with_float.zig b/test/compile_errors/stage1/obj/cmpxchg_with_float.zig index 8926ffc36b..11f2dc21c3 100644 --- a/test/compile_errors/stage1/obj/cmpxchg_with_float.zig +++ b/test/compile_errors/stage1/obj/cmpxchg_with_float.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = @cmpxchgWeak(f32, &x, 1, 2, .SeqCst, .SeqCst); } -// cmpxchg with float +// error +// backend=stage1 +// target=native // // tmp.zig:3:22: error: expected bool, integer, enum or pointer type, found 'f32' diff --git a/test/compile_errors/stage1/obj/colliding_invalid_top_level_functions.zig b/test/compile_errors/stage1/obj/colliding_invalid_top_level_functions.zig index f4e5d24c46..344edbaa9f 100644 --- a/test/compile_errors/stage1/obj/colliding_invalid_top_level_functions.zig +++ b/test/compile_errors/stage1/obj/colliding_invalid_top_level_functions.zig @@ -2,7 +2,9 @@ fn func() bogus {} fn func() bogus {} export fn entry() usize { return @sizeOf(@TypeOf(func)); } -// colliding invalid top level functions +// error +// backend=stage1 +// target=native // // tmp.zig:2:1: error: redeclaration of 'func' // tmp.zig:1:1: note: other declaration here diff --git a/test/compile_errors/stage1/obj/compare_optional_to_non-optional_with_invalid_types.zig b/test/compile_errors/stage1/obj/compare_optional_to_non-optional_with_invalid_types.zig index 8713cf4501..32e1fe9b24 100644 --- a/test/compile_errors/stage1/obj/compare_optional_to_non-optional_with_invalid_types.zig +++ b/test/compile_errors/stage1/obj/compare_optional_to_non-optional_with_invalid_types.zig @@ -22,7 +22,9 @@ export fn invalidChildType() void { _ = (x == y); } -// compare optional to non-optional with invalid types +// error +// backend=stage1 +// target=native // // :4:12: error: cannot compare types '?i32' and 'comptime_int' // :4:12: note: optional child type 'i32' must be the same as non-optional type 'comptime_int' diff --git a/test/compile_errors/stage1/obj/comparing_a_non-optional_pointer_against_null.zig b/test/compile_errors/stage1/obj/comparing_a_non-optional_pointer_against_null.zig index 69a5120135..62b5708034 100644 --- a/test/compile_errors/stage1/obj/comparing_a_non-optional_pointer_against_null.zig +++ b/test/compile_errors/stage1/obj/comparing_a_non-optional_pointer_against_null.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = &x == null; } -// comparing a non-optional pointer against null +// error +// backend=stage1 +// target=native // // tmp.zig:3:12: error: comparison of '*i32' with null diff --git a/test/compile_errors/stage1/obj/comparing_against_undefined_produces_undefined_value.zig b/test/compile_errors/stage1/obj/comparing_against_undefined_produces_undefined_value.zig index c329f51ef7..8bad61e6d1 100644 --- a/test/compile_errors/stage1/obj/comparing_against_undefined_produces_undefined_value.zig +++ b/test/compile_errors/stage1/obj/comparing_against_undefined_produces_undefined_value.zig @@ -2,6 +2,8 @@ export fn entry() void { if (2 == undefined) {} } -// comparing against undefined produces undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:2:11: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/comparison_operators_with_undefined_value.zig b/test/compile_errors/stage1/obj/comparison_operators_with_undefined_value.zig index 6021382fd4..818a575fc8 100644 --- a/test/compile_errors/stage1/obj/comparison_operators_with_undefined_value.zig +++ b/test/compile_errors/stage1/obj/comparison_operators_with_undefined_value.zig @@ -35,7 +35,9 @@ comptime { if (a <= a) x += 1; } -// comparison operators with undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:5:11: error: use of undefined value here causes undefined behavior // tmp.zig:11:11: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/comparison_with_error_union_and_error_value.zig b/test/compile_errors/stage1/obj/comparison_with_error_union_and_error_value.zig index 260ccc6f01..16f3b21c3d 100644 --- a/test/compile_errors/stage1/obj/comparison_with_error_union_and_error_value.zig +++ b/test/compile_errors/stage1/obj/comparison_with_error_union_and_error_value.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = number_or_error == error.SomethingAwful; } -// comparison with error union and error value +// error +// backend=stage1 +// target=native // // tmp.zig:3:25: error: operator not allowed for type 'anyerror!i32' diff --git a/test/compile_errors/stage1/obj/compile-time_division_by_zero.zig b/test/compile_errors/stage1/obj/compile-time_division_by_zero.zig index 578dbc7b4a..e16f5f8cf4 100644 --- a/test/compile_errors/stage1/obj/compile-time_division_by_zero.zig +++ b/test/compile_errors/stage1/obj/compile-time_division_by_zero.zig @@ -5,6 +5,8 @@ comptime { _ = c; } -// compile-time division by zero +// error +// backend=stage1 +// target=native // // tmp.zig:4:17: error: division by zero diff --git a/test/compile_errors/stage1/obj/compile-time_remainder_division_by_zero.zig b/test/compile_errors/stage1/obj/compile-time_remainder_division_by_zero.zig index 015d229110..63f0def52c 100644 --- a/test/compile_errors/stage1/obj/compile-time_remainder_division_by_zero.zig +++ b/test/compile_errors/stage1/obj/compile-time_remainder_division_by_zero.zig @@ -5,6 +5,8 @@ comptime { _ = c; } -// compile-time remainder division by zero +// error +// backend=stage1 +// target=native // // tmp.zig:4:17: error: division by zero diff --git a/test/compile_errors/stage1/obj/compileError_shows_traceback_of_references_that_caused_it.zig b/test/compile_errors/stage1/obj/compileError_shows_traceback_of_references_that_caused_it.zig index 4273741ad2..c58e9b7768 100644 --- a/test/compile_errors/stage1/obj/compileError_shows_traceback_of_references_that_caused_it.zig +++ b/test/compile_errors/stage1/obj/compileError_shows_traceback_of_references_that_caused_it.zig @@ -7,6 +7,8 @@ export fn entry() i32 { return bar; } -// @compileError shows traceback of references that caused it +// error +// backend=stage1 +// target=native // // tmp.zig:1:13: error: aoeu diff --git a/test/compile_errors/stage1/obj/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig b/test/compile_errors/stage1/obj/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig index 0770b3b6a4..36e1ee1118 100644 --- a/test/compile_errors/stage1/obj/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig +++ b/test/compile_errors/stage1/obj/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig @@ -10,6 +10,8 @@ pub fn main () void { comptime testCompileLog(Bar{.X = 123}); } -// compileLog of tagged enum doesn't crash the compiler +// error +// backend=stage1 +// target=native // // tmp.zig:6:5: error: found compile log statement diff --git a/test/compile_errors/stage1/obj/compile_error_in_struct_init_expression.zig b/test/compile_errors/stage1/obj/compile_error_in_struct_init_expression.zig index 6215c9ad76..041d09b002 100644 --- a/test/compile_errors/stage1/obj/compile_error_in_struct_init_expression.zig +++ b/test/compile_errors/stage1/obj/compile_error_in_struct_init_expression.zig @@ -9,6 +9,8 @@ export fn entry() void { _ = x; } -// compile error in struct init expression +// error +// backend=stage1 +// target=native // // tmp.zig:2:14: error: use of undeclared identifier 'crap' diff --git a/test/compile_errors/stage1/obj/compile_error_when_evaluating_return_type_of_inferred_error_set.zig b/test/compile_errors/stage1/obj/compile_error_when_evaluating_return_type_of_inferred_error_set.zig index 44d5b6a389..7b1d11b0a4 100644 --- a/test/compile_errors/stage1/obj/compile_error_when_evaluating_return_type_of_inferred_error_set.zig +++ b/test/compile_errors/stage1/obj/compile_error_when_evaluating_return_type_of_inferred_error_set.zig @@ -7,6 +7,8 @@ export fn entry() void { _ = car; } -// compile error when evaluating return type of inferred error set +// error +// backend=stage1 +// target=native // // tmp.zig:2:11: error: use of undeclared identifier 'SymbolThatDoesNotExist' diff --git a/test/compile_errors/stage1/obj/compile_log.zig b/test/compile_errors/stage1/obj/compile_log.zig index ef1cba4502..7f5d522407 100644 --- a/test/compile_errors/stage1/obj/compile_log.zig +++ b/test/compile_errors/stage1/obj/compile_log.zig @@ -7,7 +7,9 @@ fn bar(a: i32, b: []const u8) void { @compileLog("end",); } -// compile log +// error +// backend=stage1 +// target=native // // tmp.zig:5:5: error: found compile log statement // tmp.zig:6:5: error: found compile log statement diff --git a/test/compile_errors/stage1/obj/compile_log_a_pointer_to_an_opaque_value.zig b/test/compile_errors/stage1/obj/compile_log_a_pointer_to_an_opaque_value.zig index 15b7825a91..d585712b8a 100644 --- a/test/compile_errors/stage1/obj/compile_log_a_pointer_to_an_opaque_value.zig +++ b/test/compile_errors/stage1/obj/compile_log_a_pointer_to_an_opaque_value.zig @@ -2,6 +2,8 @@ export fn entry() void { @compileLog(@ptrCast(*const anyopaque, &entry)); } -// compile log a pointer to an opaque value +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: found compile log statement diff --git a/test/compile_errors/stage1/obj/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig b/test/compile_errors/stage1/obj/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig index f848ed7c7c..19b96048cb 100644 --- a/test/compile_errors/stage1/obj/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig +++ b/test/compile_errors/stage1/obj/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig @@ -7,6 +7,8 @@ export fn entry() void { _ = @typeName(Foo(i32)); } -// compile log statement inside function which must be comptime evaluated +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: found compile log statement diff --git a/test/compile_errors/stage1/obj/compile_log_statement_warning_deduplication_in_generic_fn.zig b/test/compile_errors/stage1/obj/compile_log_statement_warning_deduplication_in_generic_fn.zig index 05ac76724c..991021932d 100644 --- a/test/compile_errors/stage1/obj/compile_log_statement_warning_deduplication_in_generic_fn.zig +++ b/test/compile_errors/stage1/obj/compile_log_statement_warning_deduplication_in_generic_fn.zig @@ -7,6 +7,8 @@ fn inner(comptime n: usize) void { inline while (i < n) : (i += 1) { @compileLog("!@#$"); } } -// compile log statement warning deduplication in generic fn +// error +// backend=stage1 +// target=native // // tmp.zig:7:39: error: found compile log statement diff --git a/test/compile_errors/stage1/obj/compile_time_division_by_zero.zig b/test/compile_errors/stage1/obj/compile_time_division_by_zero.zig index 17187c47bf..7f7e168f02 100644 --- a/test/compile_errors/stage1/obj/compile_time_division_by_zero.zig +++ b/test/compile_errors/stage1/obj/compile_time_division_by_zero.zig @@ -5,6 +5,8 @@ fn foo(x: u32) u32 { export fn entry() usize { return @sizeOf(@TypeOf(y)); } -// compile time division by zero +// error +// backend=stage1 +// target=native // // tmp.zig:3:14: error: division by zero diff --git a/test/compile_errors/stage1/obj/comptime_cast_enum_to_union_but_field_has_payload.zig b/test/compile_errors/stage1/obj/comptime_cast_enum_to_union_but_field_has_payload.zig index 36a2079a01..c3cf9e76e5 100644 --- a/test/compile_errors/stage1/obj/comptime_cast_enum_to_union_but_field_has_payload.zig +++ b/test/compile_errors/stage1/obj/comptime_cast_enum_to_union_but_field_has_payload.zig @@ -9,7 +9,9 @@ export fn entry() void { _ = x; } -// comptime cast enum to union but field has payload +// error +// backend=stage1 +// target=native // // tmp.zig:8:26: error: cast to union 'Value' must initialize 'i32' field 'A' // tmp.zig:3:5: note: field 'A' declared here diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig index 18c78c858c..7eefeb80b4 100644 --- a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig +++ b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig @@ -8,7 +8,9 @@ fn bad() !void { return error.Bad; } -// comptime continue inside runtime catch +// error +// backend=stage1 +// target=native // // tmp.zig:4:21: error: comptime control flow inside runtime block // tmp.zig:4:15: note: runtime block created here diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig index f6ec1b98b1..9ace5ddceb 100644 --- a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig +++ b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig @@ -7,7 +7,9 @@ export fn entry() void { } } -// comptime continue inside runtime if bool +// error +// backend=stage1 +// target=native // // tmp.zig:5:22: error: comptime control flow inside runtime block // tmp.zig:5:9: note: runtime block created here diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig index 556a8769a5..554ba3c43e 100644 --- a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig +++ b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig @@ -7,7 +7,9 @@ export fn entry() void { } } -// comptime continue inside runtime if error +// error +// backend=stage1 +// target=native // // tmp.zig:5:20: error: comptime control flow inside runtime block // tmp.zig:5:9: note: runtime block created here diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig index 4f35398a2b..32c71e5c77 100644 --- a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig +++ b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig @@ -7,7 +7,9 @@ export fn entry() void { } } -// comptime continue inside runtime if optional +// error +// backend=stage1 +// target=native // // tmp.zig:5:20: error: comptime control flow inside runtime block // tmp.zig:5:9: note: runtime block created here diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig index 259cbd602e..d145897b41 100644 --- a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig +++ b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig @@ -10,7 +10,9 @@ export fn entry() void { } } -// comptime continue inside runtime switch +// error +// backend=stage1 +// target=native // // tmp.zig:6:19: error: comptime control flow inside runtime block // tmp.zig:5:9: note: runtime block created here diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig index 5d86394815..8e57854728 100644 --- a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig +++ b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig @@ -7,7 +7,9 @@ export fn entry() void { } } -// comptime continue inside runtime while bool +// error +// backend=stage1 +// target=native // // tmp.zig:5:25: error: comptime control flow inside runtime block // tmp.zig:5:9: note: runtime block created here diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig index 0ba49cb3ca..818455c354 100644 --- a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig +++ b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig @@ -9,7 +9,9 @@ export fn entry() void { } } -// comptime continue inside runtime while error +// error +// backend=stage1 +// target=native // // tmp.zig:6:13: error: comptime control flow inside runtime block // tmp.zig:5:9: note: runtime block created here diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig index b2cfd2f69d..ed22cc2cac 100644 --- a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig +++ b/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig @@ -7,7 +7,9 @@ export fn entry() void { } } -// comptime continue inside runtime while optional +// error +// backend=stage1 +// target=native // // tmp.zig:5:23: error: comptime control flow inside runtime block // tmp.zig:5:9: note: runtime block created here diff --git a/test/compile_errors/stage1/obj/comptime_float_in_asm_input.zig b/test/compile_errors/stage1/obj/comptime_float_in_asm_input.zig index c2333586b4..92ffadc4f7 100644 --- a/test/compile_errors/stage1/obj/comptime_float_in_asm_input.zig +++ b/test/compile_errors/stage1/obj/comptime_float_in_asm_input.zig @@ -2,6 +2,8 @@ export fn foo() void { asm volatile ("" : : [bar]"r"(3.17) : ""); } -// comptime_float in asm input +// error +// backend=stage1 +// target=native // // tmp.zig:2:35: error: expected sized integer or sized float, found comptime_float diff --git a/test/compile_errors/stage1/obj/comptime_implicit_cast_f64_to_f32.zig b/test/compile_errors/stage1/obj/comptime_implicit_cast_f64_to_f32.zig index f98c45a348..af5d49c7cb 100644 --- a/test/compile_errors/stage1/obj/comptime_implicit_cast_f64_to_f32.zig +++ b/test/compile_errors/stage1/obj/comptime_implicit_cast_f64_to_f32.zig @@ -4,6 +4,8 @@ export fn entry() void { _ = y; } -// comptime implicit cast f64 to f32 +// error +// backend=stage1 +// target=native // // tmp.zig:3:20: error: cast of value 16777217.000000 to type 'f32' loses information diff --git a/test/compile_errors/stage1/obj/comptime_int_in_asm_input.zig b/test/compile_errors/stage1/obj/comptime_int_in_asm_input.zig index 5a587e5a77..3e3ccf8c27 100644 --- a/test/compile_errors/stage1/obj/comptime_int_in_asm_input.zig +++ b/test/compile_errors/stage1/obj/comptime_int_in_asm_input.zig @@ -2,6 +2,8 @@ export fn foo() void { asm volatile ("" : : [bar]"r"(3) : ""); } -// comptime_int in asm input +// error +// backend=stage1 +// target=native // // tmp.zig:2:35: error: expected sized integer or sized float, found comptime_int diff --git a/test/compile_errors/stage1/obj/comptime_ptrcast_of_zero-sized_type.zig b/test/compile_errors/stage1/obj/comptime_ptrcast_of_zero-sized_type.zig index 1d3f91fe91..231e735cfa 100644 --- a/test/compile_errors/stage1/obj/comptime_ptrcast_of_zero-sized_type.zig +++ b/test/compile_errors/stage1/obj/comptime_ptrcast_of_zero-sized_type.zig @@ -5,6 +5,8 @@ fn foo() void { } comptime { foo(); } -// comptime ptrcast of zero-sized type +// error +// backend=stage1 +// target=native // // tmp.zig:3:21: error: '*const struct:2:17' and '[*]const u8' do not have the same in-memory representation diff --git a/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig b/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig index 622e0cf76b..598d23a305 100644 --- a/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig +++ b/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig @@ -54,7 +54,9 @@ export fn foo_slice() void { } } -// comptime slice-sentinel does not match memory at target index (terminated) +// error +// backend=stage1 +// target=native // // :4:29: error: slice-sentinel does not match memory at target index // :12:29: error: slice-sentinel does not match memory at target index diff --git a/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig b/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig index e9451cf9aa..d6b469aaf1 100644 --- a/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig +++ b/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig @@ -54,7 +54,9 @@ export fn foo_slice() void { } } -// comptime slice-sentinel does not match memory at target index (unterminated) +// error +// backend=stage1 +// target=native // // :4:29: error: slice-sentinel does not match memory at target index // :12:29: error: slice-sentinel does not match memory at target index diff --git a/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_target-sentinel.zig b/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_target-sentinel.zig index 597a493705..b204cfc684 100644 --- a/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_target-sentinel.zig +++ b/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_target-sentinel.zig @@ -54,7 +54,9 @@ export fn foo_slice() void { } } -// comptime slice-sentinel does not match target-sentinel +// error +// backend=stage1 +// target=native // // :4:29: error: slice-sentinel does not match target-sentinel // :12:29: error: slice-sentinel does not match target-sentinel diff --git a/test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_terminated.zig b/test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_terminated.zig index d5fc64ea84..82c19126c0 100644 --- a/test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_terminated.zig +++ b/test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_terminated.zig @@ -54,7 +54,9 @@ export fn foo_slice() void { } } -// comptime slice-sentinel is out of bounds (terminated) +// error +// backend=stage1 +// target=native // // :4:29: error: out of bounds slice // :12:29: error: out of bounds slice diff --git a/test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig b/test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig index 05a499ca8a..952b17600a 100644 --- a/test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig +++ b/test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig @@ -54,7 +54,9 @@ export fn foo_slice() void { } } -// comptime slice-sentinel is out of bounds (unterminated) +// error +// backend=stage1 +// target=native // // :4:29: error: slice-sentinel is out of bounds // :12:29: error: slice-sentinel is out of bounds diff --git a/test/compile_errors/stage1/obj/comptime_slice_of_an_undefined_slice.zig b/test/compile_errors/stage1/obj/comptime_slice_of_an_undefined_slice.zig index b56feddc02..4aa519f41e 100644 --- a/test/compile_errors/stage1/obj/comptime_slice_of_an_undefined_slice.zig +++ b/test/compile_errors/stage1/obj/comptime_slice_of_an_undefined_slice.zig @@ -4,6 +4,8 @@ comptime { _ = b; } -// comptime slice of an undefined slice +// error +// backend=stage1 +// target=native // // tmp.zig:3:14: error: slice of undefined diff --git a/test/compile_errors/stage1/obj/comptime_slice_of_undefined_pointer_non-zero_len.zig b/test/compile_errors/stage1/obj/comptime_slice_of_undefined_pointer_non-zero_len.zig index bcddd1d866..ec7fbdc6e2 100644 --- a/test/compile_errors/stage1/obj/comptime_slice_of_undefined_pointer_non-zero_len.zig +++ b/test/compile_errors/stage1/obj/comptime_slice_of_undefined_pointer_non-zero_len.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = slice; } -// comptime slice of undefined pointer non-zero len +// error +// backend=stage1 +// target=native // // tmp.zig:2:41: error: non-zero length slice of undefined pointer diff --git a/test/compile_errors/stage1/obj/comptime_struct_field_no_init_value.zig b/test/compile_errors/stage1/obj/comptime_struct_field_no_init_value.zig index c3de76a794..b889911a4d 100644 --- a/test/compile_errors/stage1/obj/comptime_struct_field_no_init_value.zig +++ b/test/compile_errors/stage1/obj/comptime_struct_field_no_init_value.zig @@ -6,6 +6,8 @@ export fn entry() void { _ = f; } -// comptime struct field, no init value +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: comptime field without default initialization value diff --git a/test/compile_errors/stage1/obj/const_frame_cast_to_anyframe.zig b/test/compile_errors/stage1/obj/const_frame_cast_to_anyframe.zig index 1e6cdba507..affdb953d7 100644 --- a/test/compile_errors/stage1/obj/const_frame_cast_to_anyframe.zig +++ b/test/compile_errors/stage1/obj/const_frame_cast_to_anyframe.zig @@ -11,7 +11,9 @@ fn func() void { suspend {} } -// const frame cast to anyframe +// error +// backend=stage1 +// target=native // // tmp.zig:3:12: error: expected type 'anyframe', found '*const @Frame(func)' // tmp.zig:7:24: error: expected type 'anyframe', found '*const @Frame(func)' diff --git a/test/compile_errors/stage1/obj/const_is_a_statement_not_an_expression.zig b/test/compile_errors/stage1/obj/const_is_a_statement_not_an_expression.zig index 4396407dd1..b90878abeb 100644 --- a/test/compile_errors/stage1/obj/const_is_a_statement_not_an_expression.zig +++ b/test/compile_errors/stage1/obj/const_is_a_statement_not_an_expression.zig @@ -2,6 +2,8 @@ export fn f() void { (const a = 0); } -// const is a statement, not an expression +// error +// backend=stage1 +// target=native // // tmp.zig:2:6: error: expected expression, found 'const' diff --git a/test/compile_errors/stage1/obj/container_init_with_non-type.zig b/test/compile_errors/stage1/obj/container_init_with_non-type.zig index 6464555df5..3364b2f5b6 100644 --- a/test/compile_errors/stage1/obj/container_init_with_non-type.zig +++ b/test/compile_errors/stage1/obj/container_init_with_non-type.zig @@ -3,6 +3,8 @@ const a = zero{1}; export fn entry() usize { return @sizeOf(@TypeOf(a)); } -// container init with non-type +// error +// backend=stage1 +// target=native // // tmp.zig:2:11: error: expected type 'type', found 'i32' diff --git a/test/compile_errors/stage1/obj/control_flow_uses_comptime_var_at_runtime.zig b/test/compile_errors/stage1/obj/control_flow_uses_comptime_var_at_runtime.zig index ee2c0234c3..cb2c3e9f6e 100644 --- a/test/compile_errors/stage1/obj/control_flow_uses_comptime_var_at_runtime.zig +++ b/test/compile_errors/stage1/obj/control_flow_uses_comptime_var_at_runtime.zig @@ -7,7 +7,9 @@ export fn foo() void { fn bar() void { } -// control flow uses comptime var at runtime +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: control flow attempts to use compile-time variable at runtime // tmp.zig:3:24: note: compile-time variable assigned here diff --git a/test/compile_errors/stage1/obj/control_reaches_end_of_non-void_function.zig b/test/compile_errors/stage1/obj/control_reaches_end_of_non-void_function.zig index 13d2876823..e15c1bd40b 100644 --- a/test/compile_errors/stage1/obj/control_reaches_end_of_non-void_function.zig +++ b/test/compile_errors/stage1/obj/control_reaches_end_of_non-void_function.zig @@ -1,6 +1,8 @@ fn a() i32 {} export fn entry() void { _ = a(); } -// control reaches end of non-void function +// error +// backend=stage1 +// target=native // // tmp.zig:1:12: error: expected type 'i32', found 'void' diff --git a/test/compile_errors/stage1/obj/declaration_between_fields.zig b/test/compile_errors/stage1/obj/declaration_between_fields.zig index 6e6b60f2e4..25a1ff5abc 100644 --- a/test/compile_errors/stage1/obj/declaration_between_fields.zig +++ b/test/compile_errors/stage1/obj/declaration_between_fields.zig @@ -15,7 +15,9 @@ comptime { _ = S; } -// declaration between fields +// error +// backend=stage1 +// target=native // // tmp.zig:9:5: error: declarations are not allowed between container fields // tmp.zig:5:5: note: field before declarations here diff --git a/test/compile_errors/stage1/obj/declaration_with_same_name_as_primitive_must_use_special_syntax.zig b/test/compile_errors/stage1/obj/declaration_with_same_name_as_primitive_must_use_special_syntax.zig index c10f397da4..46b0257ed5 100644 --- a/test/compile_errors/stage1/obj/declaration_with_same_name_as_primitive_must_use_special_syntax.zig +++ b/test/compile_errors/stage1/obj/declaration_with_same_name_as_primitive_must_use_special_syntax.zig @@ -4,7 +4,9 @@ export fn entry() void { _ = a; } -// declaration with same name as primitive must use special syntax +// error +// backend=stage1 +// target=native // // tmp.zig:1:7: error: name shadows primitive 'u8' // tmp.zig:1:7: note: consider using @"u8" to disambiguate diff --git a/test/compile_errors/stage1/obj/deduplicate_undeclared_identifier.zig b/test/compile_errors/stage1/obj/deduplicate_undeclared_identifier.zig index 1c9d6c3d31..1e3b3bf6db 100644 --- a/test/compile_errors/stage1/obj/deduplicate_undeclared_identifier.zig +++ b/test/compile_errors/stage1/obj/deduplicate_undeclared_identifier.zig @@ -5,6 +5,8 @@ export fn b() void { x += 1; } -// deduplicate undeclared identifier +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: use of undeclared identifier 'x' diff --git a/test/compile_errors/stage1/obj/deref_on_undefined_value.zig b/test/compile_errors/stage1/obj/deref_on_undefined_value.zig index 607f6ea5e0..f64d567a26 100644 --- a/test/compile_errors/stage1/obj/deref_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/deref_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = a.*; } -// deref on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: attempt to dereference undefined value diff --git a/test/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig b/test/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig index 2fa5ff6133..98097597cc 100644 --- a/test/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig +++ b/test/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = a.*.len; } -// deref slice and get len field +// error +// backend=stage1 +// target=native // // tmp.zig:3:10: error: attempt to dereference non-pointer type '[]u8' diff --git a/test/compile_errors/stage1/obj/dereference_an_array.zig b/test/compile_errors/stage1/obj/dereference_an_array.zig index 713c655784..0dd91f70e5 100644 --- a/test/compile_errors/stage1/obj/dereference_an_array.zig +++ b/test/compile_errors/stage1/obj/dereference_an_array.zig @@ -7,6 +7,8 @@ pub fn pass(in: []u8) []u8 { export fn entry() usize { return @sizeOf(@TypeOf(pass)); } -// dereference an array +// error +// backend=stage1 +// target=native // // tmp.zig:4:10: error: attempt to dereference non-pointer type '[10]u8' diff --git a/test/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig b/test/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig index 22ad181fb2..c305e4bc98 100644 --- a/test/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig +++ b/test/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig @@ -2,6 +2,8 @@ export fn entry(x: [*]i32) i32 { return x.*; } -// dereference unknown length pointer +// error +// backend=stage1 +// target=native // // tmp.zig:2:13: error: index syntax required for unknown-length pointer type '[*]i32' diff --git a/test/compile_errors/stage1/obj/direct_struct_loop.zig b/test/compile_errors/stage1/obj/direct_struct_loop.zig index 25b3c724c3..3062e617d6 100644 --- a/test/compile_errors/stage1/obj/direct_struct_loop.zig +++ b/test/compile_errors/stage1/obj/direct_struct_loop.zig @@ -1,6 +1,8 @@ const A = struct { a : A, }; export fn entry() usize { return @sizeOf(A); } -// direct struct loop +// error +// backend=stage1 +// target=native // // tmp.zig:1:11: error: struct 'A' depends on itself diff --git a/test/compile_errors/stage1/obj/directly_embedding_opaque_type_in_struct_and_union.zig b/test/compile_errors/stage1/obj/directly_embedding_opaque_type_in_struct_and_union.zig index 811fc00f50..8b8d84ad2a 100644 --- a/test/compile_errors/stage1/obj/directly_embedding_opaque_type_in_struct_and_union.zig +++ b/test/compile_errors/stage1/obj/directly_embedding_opaque_type_in_struct_and_union.zig @@ -20,7 +20,9 @@ export fn c() void { _ = qux; } -// directly embedding opaque type in struct and union +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: opaque types have unknown size and therefore cannot be directly embedded in structs // tmp.zig:7:5: error: opaque types have unknown size and therefore cannot be directly embedded in unions diff --git a/test/compile_errors/stage1/obj/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig b/test/compile_errors/stage1/obj/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig index a8467ddc87..1d0a6216dd 100644 --- a/test/compile_errors/stage1/obj/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig +++ b/test/compile_errors/stage1/obj/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig @@ -5,6 +5,8 @@ pub fn main() void { _ = puts(no_zero_ptr); } -// disallow coercion from non-null-terminated pointer to null-terminated pointer +// error +// backend=stage1 +// target=native // // tmp.zig:5:14: error: expected type '[*:0]const u8', found '[*]const u8' diff --git a/test/compile_errors/stage1/obj/discarding_error_value.zig b/test/compile_errors/stage1/obj/discarding_error_value.zig index 966a43a47c..dcfa22e8cb 100644 --- a/test/compile_errors/stage1/obj/discarding_error_value.zig +++ b/test/compile_errors/stage1/obj/discarding_error_value.zig @@ -5,6 +5,8 @@ fn foo() !void { return error.OutOfMemory; } -// discarding error value +// error +// backend=stage1 +// target=native // // tmp.zig:2:12: error: error is discarded. consider using `try`, `catch`, or `if` diff --git a/test/compile_errors/stage1/obj/div_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/div_assign_on_undefined_value.zig index 40c31649fb..5f18e6286a 100644 --- a/test/compile_errors/stage1/obj/div_assign_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/div_assign_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { a /= a; } -// div assign on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/div_on_undefined_value.zig b/test/compile_errors/stage1/obj/div_on_undefined_value.zig index 17af3fa221..9857b1c779 100644 --- a/test/compile_errors/stage1/obj/div_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/div_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = a / a; } -// div on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/division_by_zero.zig b/test/compile_errors/stage1/obj/division_by_zero.zig index 178d1d935b..3023f0c6f3 100644 --- a/test/compile_errors/stage1/obj/division_by_zero.zig +++ b/test/compile_errors/stage1/obj/division_by_zero.zig @@ -8,7 +8,9 @@ export fn entry2() usize { return @sizeOf(@TypeOf(lit_float_x)); } export fn entry3() usize { return @sizeOf(@TypeOf(int_x)); } export fn entry4() usize { return @sizeOf(@TypeOf(float_x)); } -// division by zero +// error +// backend=stage1 +// target=native // // tmp.zig:1:21: error: division by zero // tmp.zig:2:25: error: division by zero diff --git a/test/compile_errors/stage1/obj/dont_implicit_cast_double_pointer_to_anyopaque.zig b/test/compile_errors/stage1/obj/dont_implicit_cast_double_pointer_to_anyopaque.zig index 5894103f1c..f61dec2ee0 100644 --- a/test/compile_errors/stage1/obj/dont_implicit_cast_double_pointer_to_anyopaque.zig +++ b/test/compile_errors/stage1/obj/dont_implicit_cast_double_pointer_to_anyopaque.zig @@ -6,6 +6,8 @@ export fn entry() void { _ = ptr2; } -// don't implicit cast double pointer to *anyopaque +// error +// backend=stage1 +// target=native // // tmp.zig:5:29: error: expected type '*anyopaque', found '**u32' diff --git a/test/compile_errors/stage1/obj/double_optional_on_main_return_value.zig b/test/compile_errors/stage1/obj/double_optional_on_main_return_value.zig index c85706bc87..c61ac400ba 100644 --- a/test/compile_errors/stage1/obj/double_optional_on_main_return_value.zig +++ b/test/compile_errors/stage1/obj/double_optional_on_main_return_value.zig @@ -1,6 +1,8 @@ pub fn main() ??void { } -// double ?? on main return value +// error +// backend=stage1 +// target=native // // error: expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8' diff --git a/test/compile_errors/stage1/obj/duplicate_boolean_switch_value.zig b/test/compile_errors/stage1/obj/duplicate_boolean_switch_value.zig index 8c496d5e19..5b85c0a2eb 100644 --- a/test/compile_errors/stage1/obj/duplicate_boolean_switch_value.zig +++ b/test/compile_errors/stage1/obj/duplicate_boolean_switch_value.zig @@ -15,7 +15,9 @@ comptime { _ = x; } -// duplicate boolean switch value +// error +// backend=stage1 +// target=native // // tmp.zig:5:9: error: duplicate switch value // tmp.zig:13:9: error: duplicate switch value diff --git a/test/compile_errors/stage1/obj/duplicate_enum_field.zig b/test/compile_errors/stage1/obj/duplicate_enum_field.zig index 2aee3cc2dc..cd024270bd 100644 --- a/test/compile_errors/stage1/obj/duplicate_enum_field.zig +++ b/test/compile_errors/stage1/obj/duplicate_enum_field.zig @@ -8,7 +8,9 @@ export fn entry() void { _ = a; } -// duplicate enum field +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: duplicate enum field: 'Bar' // tmp.zig:2:5: note: other field here diff --git a/test/compile_errors/stage1/obj/duplicate_error_in_switch.zig b/test/compile_errors/stage1/obj/duplicate_error_in_switch.zig index bca59056c3..140a14ec81 100644 --- a/test/compile_errors/stage1/obj/duplicate_error_in_switch.zig +++ b/test/compile_errors/stage1/obj/duplicate_error_in_switch.zig @@ -14,7 +14,9 @@ fn foo(x: i32) !void { } } -// duplicate error in switch +// error +// backend=stage1 +// target=native // // tmp.zig:5:14: error: duplicate switch value: '@typeInfo(@typeInfo(@TypeOf(foo)).Fn.return_type.?).ErrorUnion.error_set.Foo' // tmp.zig:3:14: note: other value here diff --git a/test/compile_errors/stage1/obj/duplicate_error_value_in_error_set.zig b/test/compile_errors/stage1/obj/duplicate_error_value_in_error_set.zig index 90457562b2..30d3bee8ab 100644 --- a/test/compile_errors/stage1/obj/duplicate_error_value_in_error_set.zig +++ b/test/compile_errors/stage1/obj/duplicate_error_value_in_error_set.zig @@ -7,7 +7,9 @@ export fn entry() void { _ = a; } -// duplicate error value in error set +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: duplicate error set field 'Bar' // tmp.zig:2:5: note: previous declaration here diff --git a/test/compile_errors/stage1/obj/duplicate_field_in_struct_value_expression.zig b/test/compile_errors/stage1/obj/duplicate_field_in_struct_value_expression.zig index 6df71430d9..1c0bac75e2 100644 --- a/test/compile_errors/stage1/obj/duplicate_field_in_struct_value_expression.zig +++ b/test/compile_errors/stage1/obj/duplicate_field_in_struct_value_expression.zig @@ -13,6 +13,8 @@ export fn f() void { _ = a; } -// duplicate field in struct value expression +// error +// backend=stage1 +// target=native // // tmp.zig:11:9: error: duplicate field diff --git a/test/compile_errors/stage1/obj/duplicate_struct_field.zig b/test/compile_errors/stage1/obj/duplicate_struct_field.zig index d1fd05aacc..93afdef70e 100644 --- a/test/compile_errors/stage1/obj/duplicate_struct_field.zig +++ b/test/compile_errors/stage1/obj/duplicate_struct_field.zig @@ -7,7 +7,9 @@ export fn entry() void { _ = a; } -// duplicate struct field +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: duplicate struct field: 'Bar' // tmp.zig:2:5: note: other field here diff --git a/test/compile_errors/stage1/obj/duplicate_union_field.zig b/test/compile_errors/stage1/obj/duplicate_union_field.zig index a8bff106c6..5cbdcb7820 100644 --- a/test/compile_errors/stage1/obj/duplicate_union_field.zig +++ b/test/compile_errors/stage1/obj/duplicate_union_field.zig @@ -7,7 +7,9 @@ export fn entry() void { _ = a; } -// duplicate union field +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: duplicate union field: 'Bar' // tmp.zig:2:5: note: other field here diff --git a/test/compile_errors/stage1/obj/embedFile_with_bogus_file.zig b/test/compile_errors/stage1/obj/embedFile_with_bogus_file.zig index e50e091909..a03949b40a 100644 --- a/test/compile_errors/stage1/obj/embedFile_with_bogus_file.zig +++ b/test/compile_errors/stage1/obj/embedFile_with_bogus_file.zig @@ -2,6 +2,8 @@ const resource = @embedFile("bogus.txt",); export fn entry() usize { return @sizeOf(@TypeOf(resource)); } -// @embedFile with bogus file +// error +// backend=stage1 +// target=native // // tmp.zig:1:29: error: unable to find ' diff --git a/test/compile_errors/stage1/obj/empty_for_loop_body.zig b/test/compile_errors/stage1/obj/empty_for_loop_body.zig index 6e042f71e4..b824c93131 100644 --- a/test/compile_errors/stage1/obj/empty_for_loop_body.zig +++ b/test/compile_errors/stage1/obj/empty_for_loop_body.zig @@ -2,6 +2,8 @@ export fn a() void { for(undefined) |x|; } -// empty for loop body +// error +// backend=stage1 +// target=native // // tmp.zig:2:23: error: expected block or assignment, found ';' diff --git a/test/compile_errors/stage1/obj/empty_if_body.zig b/test/compile_errors/stage1/obj/empty_if_body.zig index a81973396f..0a7b5b1b8b 100644 --- a/test/compile_errors/stage1/obj/empty_if_body.zig +++ b/test/compile_errors/stage1/obj/empty_if_body.zig @@ -2,6 +2,8 @@ export fn a() void { if(true); } -// empty if body +// error +// backend=stage1 +// target=native // // tmp.zig:2:13: error: expected block or assignment, found ';' diff --git a/test/compile_errors/stage1/obj/empty_switch_on_an_integer.zig b/test/compile_errors/stage1/obj/empty_switch_on_an_integer.zig index eb0bdbab19..a7b8e2a81b 100644 --- a/test/compile_errors/stage1/obj/empty_switch_on_an_integer.zig +++ b/test/compile_errors/stage1/obj/empty_switch_on_an_integer.zig @@ -3,6 +3,8 @@ export fn entry() void { switch(x) {} } -// empty switch on an integer +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: switch must handle all possibilities diff --git a/test/compile_errors/stage1/obj/empty_while_loop_body.zig b/test/compile_errors/stage1/obj/empty_while_loop_body.zig index 3a186f1a2b..01b2132518 100644 --- a/test/compile_errors/stage1/obj/empty_while_loop_body.zig +++ b/test/compile_errors/stage1/obj/empty_while_loop_body.zig @@ -2,6 +2,8 @@ export fn a() void { while(true); } -// empty while loop body +// error +// backend=stage1 +// target=native // // tmp.zig:2:16: error: expected block or assignment, found ';' diff --git a/test/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig b/test/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig index fa422ba588..302ee242e0 100644 --- a/test/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig +++ b/test/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig @@ -5,6 +5,8 @@ fn fibonacci(x: i32) i32 { export fn entry() usize { return @sizeOf(@TypeOf(seventh_fib_number)); } -// endless loop in function evaluation +// error +// backend=stage1 +// target=native // // tmp.zig:3:21: error: evaluation exceeded 1000 backwards branches diff --git a/test/compile_errors/stage1/obj/enum_field_value_references_enum.zig b/test/compile_errors/stage1/obj/enum_field_value_references_enum.zig index 02dcfc1f9d..ebf32db5e0 100644 --- a/test/compile_errors/stage1/obj/enum_field_value_references_enum.zig +++ b/test/compile_errors/stage1/obj/enum_field_value_references_enum.zig @@ -8,6 +8,8 @@ export fn entry() void { } const D = 1; -// enum field value references enum +// error +// backend=stage1 +// target=native // // tmp.zig:1:17: error: enum 'Foo' depends on itself diff --git a/test/compile_errors/stage1/obj/enum_in_field_count_range_but_not_matching_tag.zig b/test/compile_errors/stage1/obj/enum_in_field_count_range_but_not_matching_tag.zig index dc90c467df..e783cbee52 100644 --- a/test/compile_errors/stage1/obj/enum_in_field_count_range_but_not_matching_tag.zig +++ b/test/compile_errors/stage1/obj/enum_in_field_count_range_but_not_matching_tag.zig @@ -7,7 +7,9 @@ export fn entry() void { _ = x; } -// enum in field count range but not matching tag +// error +// backend=stage1 +// target=native // // tmp.zig:6:13: error: enum 'Foo' has no tag matching integer value 0 // tmp.zig:1:13: note: 'Foo' declared here diff --git a/test/compile_errors/stage1/obj/enum_value_already_taken.zig b/test/compile_errors/stage1/obj/enum_value_already_taken.zig index 9939553c41..60e88f0062 100644 --- a/test/compile_errors/stage1/obj/enum_value_already_taken.zig +++ b/test/compile_errors/stage1/obj/enum_value_already_taken.zig @@ -10,7 +10,9 @@ export fn entry() void { _ = x; } -// enum value already taken +// error +// backend=stage1 +// target=native // // tmp.zig:6:5: error: enum tag value 60 already taken // tmp.zig:4:5: note: other occurrence here diff --git a/test/compile_errors/stage1/obj/enum_with_0_fields.zig b/test/compile_errors/stage1/obj/enum_with_0_fields.zig index 10559d15b3..9ee18f51ca 100644 --- a/test/compile_errors/stage1/obj/enum_with_0_fields.zig +++ b/test/compile_errors/stage1/obj/enum_with_0_fields.zig @@ -1,5 +1,7 @@ const Foo = enum {}; -// enum with 0 fields +// error +// backend=stage1 +// target=native // // tmp.zig:1:13: error: enum declarations must have at least one tag diff --git a/test/compile_errors/stage1/obj/enum_with_declarations_unavailable_for_reify_type.zig b/test/compile_errors/stage1/obj/enum_with_declarations_unavailable_for_reify_type.zig index 4a27caab3d..ffe9a2a78a 100644 --- a/test/compile_errors/stage1/obj/enum_with_declarations_unavailable_for_reify_type.zig +++ b/test/compile_errors/stage1/obj/enum_with_declarations_unavailable_for_reify_type.zig @@ -2,6 +2,8 @@ export fn entry() void { _ = @Type(@typeInfo(enum { foo, const bar = 1; })); } -// enum with declarations unavailable for @Type +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: Type.Enum.decls must be empty for @Type diff --git a/test/compile_errors/stage1/obj/error_equality_but_sets_have_no_common_members.zig b/test/compile_errors/stage1/obj/error_equality_but_sets_have_no_common_members.zig index 433f6d50e3..d42169b503 100644 --- a/test/compile_errors/stage1/obj/error_equality_but_sets_have_no_common_members.zig +++ b/test/compile_errors/stage1/obj/error_equality_but_sets_have_no_common_members.zig @@ -9,6 +9,8 @@ fn foo(x: Set1) void { } } -// error equality but sets have no common members +// error +// backend=stage1 +// target=native // // tmp.zig:7:11: error: error sets 'Set1' and 'Set2' have no common errors diff --git a/test/compile_errors/stage1/obj/error_not_handled_in_switch.zig b/test/compile_errors/stage1/obj/error_not_handled_in_switch.zig index a69c538eac..12ee35daef 100644 --- a/test/compile_errors/stage1/obj/error_not_handled_in_switch.zig +++ b/test/compile_errors/stage1/obj/error_not_handled_in_switch.zig @@ -12,7 +12,9 @@ fn foo(x: i32) !void { } } -// error not handled in switch +// error +// backend=stage1 +// target=native // // tmp.zig:2:26: error: error.Baz not handled in switch // tmp.zig:2:26: error: error.Bar not handled in switch diff --git a/test/compile_errors/stage1/obj/error_note_for_function_parameter_incompatibility.zig b/test/compile_errors/stage1/obj/error_note_for_function_parameter_incompatibility.zig index 5014d01a3d..edc421d8f4 100644 --- a/test/compile_errors/stage1/obj/error_note_for_function_parameter_incompatibility.zig +++ b/test/compile_errors/stage1/obj/error_note_for_function_parameter_incompatibility.zig @@ -4,7 +4,9 @@ export fn entry() void { do_the_thing(bar); } -// error note for function parameter incompatibility +// error +// backend=stage1 +// target=native // // tmp.zig:4:18: error: expected type 'fn(i32) void', found 'fn(bool) void // tmp.zig:4:18: note: parameter 0: 'bool' cannot cast into 'i32' diff --git a/test/compile_errors/stage1/obj/error_union_operator_with_non_error_set_LHS.zig b/test/compile_errors/stage1/obj/error_union_operator_with_non_error_set_LHS.zig index 40b5872fc8..785e42fb9b 100644 --- a/test/compile_errors/stage1/obj/error_union_operator_with_non_error_set_LHS.zig +++ b/test/compile_errors/stage1/obj/error_union_operator_with_non_error_set_LHS.zig @@ -4,6 +4,8 @@ comptime { _ = x; } -// error union operator with non error set LHS +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: expected error set type, found type 'i32' diff --git a/test/compile_errors/stage1/obj/error_when_evaluating_return_type.zig b/test/compile_errors/stage1/obj/error_when_evaluating_return_type.zig index 60baa1c5b4..60731740b7 100644 --- a/test/compile_errors/stage1/obj/error_when_evaluating_return_type.zig +++ b/test/compile_errors/stage1/obj/error_when_evaluating_return_type.zig @@ -10,6 +10,8 @@ export fn entry() void { _ = rule_set; } -// error when evaluating return type +// error +// backend=stage1 +// target=native // // tmp.zig:2:19: error: expected type 'i32', found 'type' diff --git a/test/compile_errors/stage1/obj/exceeded_maximum_bit_width_of_integer.zig b/test/compile_errors/stage1/obj/exceeded_maximum_bit_width_of_integer.zig index e1784de63e..2a3c795f81 100644 --- a/test/compile_errors/stage1/obj/exceeded_maximum_bit_width_of_integer.zig +++ b/test/compile_errors/stage1/obj/exceeded_maximum_bit_width_of_integer.zig @@ -7,7 +7,9 @@ export fn entry2() void { _ = x; } -// exceeded maximum bit width of integer +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: primitive integer type 'u65536' exceeds maximum bit width of 65535 // tmp.zig:6:12: error: primitive integer type 'i65536' exceeds maximum bit width of 65535 diff --git a/test/compile_errors/stage1/obj/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig b/test/compile_errors/stage1/obj/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig index eaa2be84c7..696fef57fa 100644 --- a/test/compile_errors/stage1/obj/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig +++ b/test/compile_errors/stage1/obj/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig @@ -2,6 +2,8 @@ export fn entry() i32 { return @as(i32, 12.34); } -// explicit cast float literal to integer when there is a fraction component +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: fractional component prevents float value 12.340000 from being casted to type 'i32' diff --git a/test/compile_errors/stage1/obj/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig b/test/compile_errors/stage1/obj/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig index 68d78e0f92..4a41920107 100644 --- a/test/compile_errors/stage1/obj/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig +++ b/test/compile_errors/stage1/obj/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig @@ -6,6 +6,8 @@ comptime { _ = y; } -// explicit error set cast known at comptime violates error sets +// error +// backend=stage1 +// target=native // // tmp.zig:5:13: error: error.B not a member of error set 'Set2' diff --git a/test/compile_errors/stage1/obj/explicitly_casting_non_tag_type_to_enum.zig b/test/compile_errors/stage1/obj/explicitly_casting_non_tag_type_to_enum.zig index e7c4d5f5d9..1999fd70a7 100644 --- a/test/compile_errors/stage1/obj/explicitly_casting_non_tag_type_to_enum.zig +++ b/test/compile_errors/stage1/obj/explicitly_casting_non_tag_type_to_enum.zig @@ -11,6 +11,8 @@ export fn entry() void { _ = x; } -// explicitly casting non tag type to enum +// error +// backend=stage1 +// target=native // // tmp.zig:10:31: error: expected integer type, found 'f32' diff --git a/test/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig b/test/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig index 0f300f324b..ccfda1ba67 100644 --- a/test/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig +++ b/test/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig @@ -2,6 +2,8 @@ export fn foo(comptime x: i32, y: i32) i32{ return x + y; } -// export function with comptime parameter +// error +// backend=stage1 +// target=native // // tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'C' diff --git a/test/compile_errors/stage1/obj/export_generic_function.zig b/test/compile_errors/stage1/obj/export_generic_function.zig index 3fb4375ed4..9f6bcd4cf4 100644 --- a/test/compile_errors/stage1/obj/export_generic_function.zig +++ b/test/compile_errors/stage1/obj/export_generic_function.zig @@ -3,6 +3,8 @@ export fn foo(num: anytype) i32 { return 0; } -// export generic function +// error +// backend=stage1 +// target=native // // tmp.zig:1:15: error: parameter of type 'anytype' not allowed in function with calling convention 'C' diff --git a/test/compile_errors/stage1/obj/exported_async_function.zig b/test/compile_errors/stage1/obj/exported_async_function.zig index 296a5950e9..b9ac591227 100644 --- a/test/compile_errors/stage1/obj/exported_async_function.zig +++ b/test/compile_errors/stage1/obj/exported_async_function.zig @@ -1,5 +1,7 @@ export fn foo() callconv(.Async) void {} -// exported async function +// error +// backend=stage1 +// target=native // // tmp.zig:1:1: error: exported function cannot be async diff --git a/test/compile_errors/stage1/obj/exported_enum_without_explicit_integer_tag_type.zig b/test/compile_errors/stage1/obj/exported_enum_without_explicit_integer_tag_type.zig index 3d6e63db43..c432bedd6b 100644 --- a/test/compile_errors/stage1/obj/exported_enum_without_explicit_integer_tag_type.zig +++ b/test/compile_errors/stage1/obj/exported_enum_without_explicit_integer_tag_type.zig @@ -7,7 +7,9 @@ comptime { @export(e, .{ .name = "e" }); } -// exported enum without explicit integer tag type +// error +// backend=stage1 +// target=native // // tmp.zig:3:13: error: exported enum without explicit integer tag type // tmp.zig:7:13: error: exported enum value without explicit integer tag type diff --git a/test/compile_errors/stage1/obj/extern_function_pointer_mismatch.zig b/test/compile_errors/stage1/obj/extern_function_pointer_mismatch.zig index f93a24db34..888b59a626 100644 --- a/test/compile_errors/stage1/obj/extern_function_pointer_mismatch.zig +++ b/test/compile_errors/stage1/obj/extern_function_pointer_mismatch.zig @@ -5,6 +5,8 @@ export fn c(x: i32) i32 {return x + 2;} export fn entry() usize { return @sizeOf(@TypeOf(fns)); } -// extern function pointer mismatch +// error +// backend=stage1 +// target=native // // tmp.zig:1:37: error: expected type 'fn(i32) i32', found 'fn(i32) callconv(.C) i32' diff --git a/test/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig b/test/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig index 3dfdfe663e..93e26b9543 100644 --- a/test/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig +++ b/test/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig @@ -4,6 +4,8 @@ fn f() i32 { } export fn entry() usize { return @sizeOf(@TypeOf(f)); } -// extern function with comptime parameter +// error +// backend=stage1 +// target=native // // tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'C' diff --git a/test/compile_errors/stage1/obj/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig b/test/compile_errors/stage1/obj/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig index e65c438da3..8f45a0e5dc 100644 --- a/test/compile_errors/stage1/obj/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig +++ b/test/compile_errors/stage1/obj/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig @@ -36,6 +36,8 @@ export fn entry() void { _ = s; } -// extern struct with extern-compatible but inferred integer tag type +// error +// backend=stage1 +// target=native // // tmp.zig:31:5: error: extern structs cannot contain fields of type 'E' diff --git a/test/compile_errors/stage1/obj/extern_struct_with_non-extern-compatible_integer_tag_type.zig b/test/compile_errors/stage1/obj/extern_struct_with_non-extern-compatible_integer_tag_type.zig index 6d698ce8e3..aa508056a1 100644 --- a/test/compile_errors/stage1/obj/extern_struct_with_non-extern-compatible_integer_tag_type.zig +++ b/test/compile_errors/stage1/obj/extern_struct_with_non-extern-compatible_integer_tag_type.zig @@ -7,6 +7,8 @@ export fn entry() void { _ = s; } -// extern struct with non-extern-compatible integer tag type +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: extern structs cannot contain fields of type 'E' diff --git a/test/compile_errors/stage1/obj/extern_union_field_missing_type.zig b/test/compile_errors/stage1/obj/extern_union_field_missing_type.zig index 1287ce0159..9d8532f8a1 100644 --- a/test/compile_errors/stage1/obj/extern_union_field_missing_type.zig +++ b/test/compile_errors/stage1/obj/extern_union_field_missing_type.zig @@ -6,6 +6,8 @@ export fn entry() void { _ = a; } -// extern union field missing type +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: union field missing type diff --git a/test/compile_errors/stage1/obj/extern_union_given_enum_tag_type.zig b/test/compile_errors/stage1/obj/extern_union_given_enum_tag_type.zig index 5b38d77eb6..86ac42cccf 100644 --- a/test/compile_errors/stage1/obj/extern_union_given_enum_tag_type.zig +++ b/test/compile_errors/stage1/obj/extern_union_given_enum_tag_type.zig @@ -13,6 +13,8 @@ export fn entry() void { _ = a; } -// extern union given enum tag type +// error +// backend=stage1 +// target=native // // tmp.zig:6:30: error: extern union does not support enum tag type diff --git a/test/compile_errors/stage1/obj/extern_variable_has_no_type.zig b/test/compile_errors/stage1/obj/extern_variable_has_no_type.zig index f9d3952275..58d67f14e1 100644 --- a/test/compile_errors/stage1/obj/extern_variable_has_no_type.zig +++ b/test/compile_errors/stage1/obj/extern_variable_has_no_type.zig @@ -3,6 +3,8 @@ pub export fn entry() void { foo; } -// extern variable has no type +// error +// backend=stage1 +// target=native // // tmp.zig:1:8: error: unable to infer variable type diff --git a/test/compile_errors/stage1/obj/fieldParentPtr-bad_field_name.zig b/test/compile_errors/stage1/obj/fieldParentPtr-bad_field_name.zig index 8c40641113..73022167f5 100644 --- a/test/compile_errors/stage1/obj/fieldParentPtr-bad_field_name.zig +++ b/test/compile_errors/stage1/obj/fieldParentPtr-bad_field_name.zig @@ -5,6 +5,8 @@ export fn foo(a: *i32) *Foo { return @fieldParentPtr(Foo, "a", a); } -// @fieldParentPtr - bad field name +// error +// backend=stage1 +// target=native // // tmp.zig:5:33: error: struct 'Foo' has no field 'a' diff --git a/test/compile_errors/stage1/obj/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig b/test/compile_errors/stage1/obj/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig index a8a5431352..9375f4639a 100644 --- a/test/compile_errors/stage1/obj/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig +++ b/test/compile_errors/stage1/obj/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig @@ -10,6 +10,8 @@ comptime { _ = another_foo_ptr; } -// @fieldParentPtr - comptime field ptr not based on struct +// error +// backend=stage1 +// target=native // // tmp.zig:9:55: error: pointer value not based on parent struct diff --git a/test/compile_errors/stage1/obj/fieldParentPtr-comptime_wrong_field_index.zig b/test/compile_errors/stage1/obj/fieldParentPtr-comptime_wrong_field_index.zig index 11bb7282fc..c322543dc0 100644 --- a/test/compile_errors/stage1/obj/fieldParentPtr-comptime_wrong_field_index.zig +++ b/test/compile_errors/stage1/obj/fieldParentPtr-comptime_wrong_field_index.zig @@ -9,6 +9,8 @@ comptime { _ = another_foo_ptr; } -// @fieldParentPtr - comptime wrong field index +// error +// backend=stage1 +// target=native // // tmp.zig:8:29: error: field 'b' has index 1 but pointer value is index 0 of struct 'Foo' diff --git a/test/compile_errors/stage1/obj/fieldParentPtr-field_pointer_is_not_pointer.zig b/test/compile_errors/stage1/obj/fieldParentPtr-field_pointer_is_not_pointer.zig index 8db9075b7b..71360e5681 100644 --- a/test/compile_errors/stage1/obj/fieldParentPtr-field_pointer_is_not_pointer.zig +++ b/test/compile_errors/stage1/obj/fieldParentPtr-field_pointer_is_not_pointer.zig @@ -5,6 +5,8 @@ export fn foo(a: i32) *Foo { return @fieldParentPtr(Foo, "a", a); } -// @fieldParentPtr - field pointer is not pointer +// error +// backend=stage1 +// target=native // // tmp.zig:5:38: error: expected pointer, found 'i32' diff --git a/test/compile_errors/stage1/obj/fieldParentPtr-non_struct.zig b/test/compile_errors/stage1/obj/fieldParentPtr-non_struct.zig index 325fbe5b3b..16b9a9b332 100644 --- a/test/compile_errors/stage1/obj/fieldParentPtr-non_struct.zig +++ b/test/compile_errors/stage1/obj/fieldParentPtr-non_struct.zig @@ -3,6 +3,8 @@ export fn foo(a: *i32) *Foo { return @fieldParentPtr(Foo, "a", a); } -// @fieldParentPtr - non struct +// error +// backend=stage1 +// target=native // // tmp.zig:3:28: error: expected struct type, found 'i32' diff --git a/test/compile_errors/stage1/obj/field_access_of_opaque_type.zig b/test/compile_errors/stage1/obj/field_access_of_opaque_type.zig index 3354c0f471..963c89dafe 100644 --- a/test/compile_errors/stage1/obj/field_access_of_opaque_type.zig +++ b/test/compile_errors/stage1/obj/field_access_of_opaque_type.zig @@ -9,6 +9,8 @@ fn bar(x: *MyType) bool { return x.blah; } -// field access of opaque type +// error +// backend=stage1 +// target=native // // tmp.zig:9:13: error: no member named 'blah' in opaque type 'MyType' diff --git a/test/compile_errors/stage1/obj/field_access_of_slices.zig b/test/compile_errors/stage1/obj/field_access_of_slices.zig index 3e79e1f0e0..45ca711367 100644 --- a/test/compile_errors/stage1/obj/field_access_of_slices.zig +++ b/test/compile_errors/stage1/obj/field_access_of_slices.zig @@ -4,6 +4,8 @@ export fn entry() void { _ = info; } -// field access of slices +// error +// backend=stage1 +// target=native // // tmp.zig:3:32: error: type 'type' does not support field access diff --git a/test/compile_errors/stage1/obj/field_access_of_unknown_length_pointer.zig b/test/compile_errors/stage1/obj/field_access_of_unknown_length_pointer.zig index 7097c13605..f9a37cabcc 100644 --- a/test/compile_errors/stage1/obj/field_access_of_unknown_length_pointer.zig +++ b/test/compile_errors/stage1/obj/field_access_of_unknown_length_pointer.zig @@ -6,6 +6,8 @@ export fn entry(foo: [*]Foo) void { foo.a += 1; } -// field access of unknown length pointer +// error +// backend=stage1 +// target=native // // tmp.zig:6:8: error: type '[*]Foo' does not support field access diff --git a/test/compile_errors/stage1/obj/field_type_supplied_in_an_enum.zig b/test/compile_errors/stage1/obj/field_type_supplied_in_an_enum.zig index cb308307bb..d6ba214c0c 100644 --- a/test/compile_errors/stage1/obj/field_type_supplied_in_an_enum.zig +++ b/test/compile_errors/stage1/obj/field_type_supplied_in_an_enum.zig @@ -4,7 +4,9 @@ const Letter = enum { C, }; -// field type supplied in an enum +// error +// backend=stage1 +// target=native // // tmp.zig:2:8: error: enum fields do not have types // tmp.zig:1:16: note: consider 'union(enum)' here to make it a tagged union diff --git a/test/compile_errors/stage1/obj/floatToInt_comptime_safety.zig b/test/compile_errors/stage1/obj/floatToInt_comptime_safety.zig index e54047fb2f..1e5fa746a1 100644 --- a/test/compile_errors/stage1/obj/floatToInt_comptime_safety.zig +++ b/test/compile_errors/stage1/obj/floatToInt_comptime_safety.zig @@ -8,7 +8,9 @@ comptime { _ = @floatToInt(u8, @as(f32, 256.1)); } -// @floatToInt comptime safety +// error +// backend=stage1 +// target=native // // tmp.zig:2:9: error: integer value '-129' cannot be stored in type 'i8' // tmp.zig:5:9: error: integer value '-1' cannot be stored in type 'u8' diff --git a/test/compile_errors/stage1/obj/float_literal_too_large_error.zig b/test/compile_errors/stage1/obj/float_literal_too_large_error.zig index d7be9874ac..c3865c3110 100644 --- a/test/compile_errors/stage1/obj/float_literal_too_large_error.zig +++ b/test/compile_errors/stage1/obj/float_literal_too_large_error.zig @@ -3,6 +3,8 @@ comptime { _ = a; } -// float literal too large error +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: float literal out of range of any type diff --git a/test/compile_errors/stage1/obj/float_literal_too_small_error_denormal.zig b/test/compile_errors/stage1/obj/float_literal_too_small_error_denormal.zig index 2657775be0..0b0bfee1de 100644 --- a/test/compile_errors/stage1/obj/float_literal_too_small_error_denormal.zig +++ b/test/compile_errors/stage1/obj/float_literal_too_small_error_denormal.zig @@ -3,6 +3,8 @@ comptime { _ = a; } -// float literal too small error (denormal) +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: float literal out of range of any type diff --git a/test/compile_errors/stage1/obj/for_loop_body_expression_ignored.zig b/test/compile_errors/stage1/obj/for_loop_body_expression_ignored.zig index fffbb13604..6281d4b276 100644 --- a/test/compile_errors/stage1/obj/for_loop_body_expression_ignored.zig +++ b/test/compile_errors/stage1/obj/for_loop_body_expression_ignored.zig @@ -10,7 +10,9 @@ export fn f2() void { _ = x; } -// for loop body expression ignored +// error +// backend=stage1 +// target=native // // tmp.zig:5:30: error: expression value is ignored // tmp.zig:9:30: error: expression value is ignored diff --git a/test/compile_errors/stage1/obj/frame_called_outside_of_function_definition.zig b/test/compile_errors/stage1/obj/frame_called_outside_of_function_definition.zig index a76fff93a8..d140998152 100644 --- a/test/compile_errors/stage1/obj/frame_called_outside_of_function_definition.zig +++ b/test/compile_errors/stage1/obj/frame_called_outside_of_function_definition.zig @@ -4,6 +4,8 @@ export fn entry() bool { return handle_undef == handle_dummy; } -// @frame() called outside of function definition +// error +// backend=stage1 +// target=native // // tmp.zig:2:30: error: @frame() called outside of function definition diff --git a/test/compile_errors/stage1/obj/frame_causes_function_to_be_async.zig b/test/compile_errors/stage1/obj/frame_causes_function_to_be_async.zig index b45ff6965e..f8493b08b2 100644 --- a/test/compile_errors/stage1/obj/frame_causes_function_to_be_async.zig +++ b/test/compile_errors/stage1/obj/frame_causes_function_to_be_async.zig @@ -5,7 +5,9 @@ fn func() void { _ = @frame(); } -// @frame() causes function to be async +// error +// backend=stage1 +// target=native // // tmp.zig:1:1: error: function with calling convention 'C' cannot be async // tmp.zig:5:9: note: @frame() causes function to be async diff --git a/test/compile_errors/stage1/obj/function_alignment_non_power_of_2.zig b/test/compile_errors/stage1/obj/function_alignment_non_power_of_2.zig index 949e11c115..617939a120 100644 --- a/test/compile_errors/stage1/obj/function_alignment_non_power_of_2.zig +++ b/test/compile_errors/stage1/obj/function_alignment_non_power_of_2.zig @@ -1,6 +1,8 @@ extern fn foo() align(3) void; export fn entry() void { return foo(); } -// function alignment non power of 2 +// error +// backend=stage1 +// target=native // // tmp.zig:1:23: error: alignment value 3 is not a power of 2 diff --git a/test/compile_errors/stage1/obj/function_call_assigned_to_incorrect_type.zig b/test/compile_errors/stage1/obj/function_call_assigned_to_incorrect_type.zig index 4257b802c1..51bc82ffb8 100644 --- a/test/compile_errors/stage1/obj/function_call_assigned_to_incorrect_type.zig +++ b/test/compile_errors/stage1/obj/function_call_assigned_to_incorrect_type.zig @@ -6,6 +6,8 @@ fn concat() [16]f32 { return [1]f32{0}**16; } -// function call assigned to incorrect type +// error +// backend=stage1 +// target=native // // tmp.zig:3:17: error: expected type '[4]f32', found '[16]f32' diff --git a/test/compile_errors/stage1/obj/function_parameter_is_opaque.zig b/test/compile_errors/stage1/obj/function_parameter_is_opaque.zig index 12e50ca59b..5b52370d86 100644 --- a/test/compile_errors/stage1/obj/function_parameter_is_opaque.zig +++ b/test/compile_errors/stage1/obj/function_parameter_is_opaque.zig @@ -19,7 +19,9 @@ export fn entry4() void { _ = bar; } -// function parameter is opaque +// error +// backend=stage1 +// target=native // // tmp.zig:3:28: error: parameter of opaque type 'FooType' not allowed // tmp.zig:8:28: error: parameter of type '@Type(.Null)' not allowed diff --git a/test/compile_errors/stage1/obj/function_prototype_with_no_body.zig b/test/compile_errors/stage1/obj/function_prototype_with_no_body.zig index 07597af015..7926faaa44 100644 --- a/test/compile_errors/stage1/obj/function_prototype_with_no_body.zig +++ b/test/compile_errors/stage1/obj/function_prototype_with_no_body.zig @@ -3,6 +3,8 @@ export fn entry() void { foo(); } -// function prototype with no body +// error +// backend=stage1 +// target=native // // tmp.zig:1:1: error: non-extern function has no body diff --git a/test/compile_errors/stage1/obj/function_returning_opaque_type.zig b/test/compile_errors/stage1/obj/function_returning_opaque_type.zig index 956a4ed224..e2c084b517 100644 --- a/test/compile_errors/stage1/obj/function_returning_opaque_type.zig +++ b/test/compile_errors/stage1/obj/function_returning_opaque_type.zig @@ -9,7 +9,9 @@ export fn baz() !@TypeOf(undefined) { return error.InvalidValue; } -// function returning opaque type +// error +// backend=stage1 +// target=native // // tmp.zig:2:18: error: Opaque return type 'FooType' not allowed // tmp.zig:1:1: note: type declared here diff --git a/test/compile_errors/stage1/obj/function_with_ccc_indirectly_calling_async_function.zig b/test/compile_errors/stage1/obj/function_with_ccc_indirectly_calling_async_function.zig index 1ccf486be6..9fadb992b4 100644 --- a/test/compile_errors/stage1/obj/function_with_ccc_indirectly_calling_async_function.zig +++ b/test/compile_errors/stage1/obj/function_with_ccc_indirectly_calling_async_function.zig @@ -8,7 +8,9 @@ fn bar() void { suspend {} } -// function with ccc indirectly calling async function +// error +// backend=stage1 +// target=native // // tmp.zig:1:1: error: function with calling convention 'C' cannot be async // tmp.zig:2:8: note: async function call here diff --git a/test/compile_errors/stage1/obj/function_with_invalid_return_type.zig b/test/compile_errors/stage1/obj/function_with_invalid_return_type.zig index acedbac7d1..897b298a3d 100644 --- a/test/compile_errors/stage1/obj/function_with_invalid_return_type.zig +++ b/test/compile_errors/stage1/obj/function_with_invalid_return_type.zig @@ -1,5 +1,7 @@ export fn foo() boid {} -// function with invalid return type +// error +// backend=stage1 +// target=native // // tmp.zig:1:17: error: use of undeclared identifier 'boid' diff --git a/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig b/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig index 9c1913305d..8de26c08b8 100644 --- a/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig +++ b/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig @@ -1,6 +1,8 @@ const Foo = enum { A, B, C }; export fn entry(foo: Foo) void { _ = foo; } -// function with non-extern non-packed enum parameter +// error +// backend=stage1 +// target=native // // tmp.zig:2:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C' diff --git a/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig b/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig index eb2617f279..ecb4a36267 100644 --- a/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig +++ b/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig @@ -5,6 +5,8 @@ const Foo = struct { }; export fn entry(foo: Foo) void { _ = foo; } -// function with non-extern non-packed struct parameter +// error +// backend=stage1 +// target=native // // tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C' diff --git a/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig b/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig index 662ffd349b..bfe2dc6cfa 100644 --- a/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig +++ b/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig @@ -5,6 +5,8 @@ const Foo = union { }; export fn entry(foo: Foo) void { _ = foo; } -// function with non-extern non-packed union parameter +// error +// backend=stage1 +// target=native // // tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C' diff --git a/test/compile_errors/stage1/obj/generic_fn_as_parameter_without_comptime_keyword.zig b/test/compile_errors/stage1/obj/generic_fn_as_parameter_without_comptime_keyword.zig index fceea0961b..72e27e3a0d 100644 --- a/test/compile_errors/stage1/obj/generic_fn_as_parameter_without_comptime_keyword.zig +++ b/test/compile_errors/stage1/obj/generic_fn_as_parameter_without_comptime_keyword.zig @@ -4,6 +4,8 @@ export fn entry() void { f(g); } -// generic fn as parameter without comptime keyword +// error +// backend=stage1 +// target=native // // tmp.zig:1:9: error: parameter of type 'fn(anytype) anytype' must be declared comptime diff --git a/test/compile_errors/stage1/obj/generic_function_call_assigned_to_incorrect_type.zig b/test/compile_errors/stage1/obj/generic_function_call_assigned_to_incorrect_type.zig index b48d5def57..72ed66b20a 100644 --- a/test/compile_errors/stage1/obj/generic_function_call_assigned_to_incorrect_type.zig +++ b/test/compile_errors/stage1/obj/generic_function_call_assigned_to_incorrect_type.zig @@ -6,6 +6,8 @@ fn myAlloc(comptime arg: type) anyerror!arg{ unreachable; } -// generic function call assigned to incorrect type +// error +// backend=stage1 +// target=native // // tmp.zig:3:18: error: expected type '[]i32', found 'anyerror!i32 diff --git a/test/compile_errors/stage1/obj/generic_function_instance_with_non-constant_expression.zig b/test/compile_errors/stage1/obj/generic_function_instance_with_non-constant_expression.zig index 3698370b57..9cdd653766 100644 --- a/test/compile_errors/stage1/obj/generic_function_instance_with_non-constant_expression.zig +++ b/test/compile_errors/stage1/obj/generic_function_instance_with_non-constant_expression.zig @@ -5,6 +5,8 @@ fn test1(a: i32, b: i32) i32 { export fn entry() usize { return @sizeOf(@TypeOf(test1)); } -// generic function instance with non-constant expression +// error +// backend=stage1 +// target=native // // tmp.zig:3:16: error: runtime value cannot be passed to comptime arg diff --git a/test/compile_errors/stage1/obj/generic_function_returning_opaque_type.zig b/test/compile_errors/stage1/obj/generic_function_returning_opaque_type.zig index c109a5ce7c..247c1a49bf 100644 --- a/test/compile_errors/stage1/obj/generic_function_returning_opaque_type.zig +++ b/test/compile_errors/stage1/obj/generic_function_returning_opaque_type.zig @@ -12,7 +12,9 @@ export fn baz() void { _ = generic(@TypeOf(undefined)); } -// generic function returning opaque type +// error +// backend=stage1 +// target=native // // tmp.zig:6:16: error: call to generic function with Opaque return type 'FooType' not allowed // tmp.zig:2:1: note: function declared here diff --git a/test/compile_errors/stage1/obj/generic_function_where_return_type_is_self-referenced.zig b/test/compile_errors/stage1/obj/generic_function_where_return_type_is_self-referenced.zig index 18237a5023..c8d31ec8df 100644 --- a/test/compile_errors/stage1/obj/generic_function_where_return_type_is_self-referenced.zig +++ b/test/compile_errors/stage1/obj/generic_function_where_return_type_is_self-referenced.zig @@ -8,6 +8,8 @@ export fn entry() void { _ = t; } -// generic function where return type is self-referenced +// error +// backend=stage1 +// target=native // // tmp.zig:1:29: error: evaluation exceeded 1000 backwards branches diff --git a/test/compile_errors/stage1/obj/global_variable_alignment_non_power_of_2.zig b/test/compile_errors/stage1/obj/global_variable_alignment_non_power_of_2.zig index a6c4ccaa98..f23f340b16 100644 --- a/test/compile_errors/stage1/obj/global_variable_alignment_non_power_of_2.zig +++ b/test/compile_errors/stage1/obj/global_variable_alignment_non_power_of_2.zig @@ -1,6 +1,8 @@ const some_data: [100]u8 align(3) = undefined; export fn entry() usize { return @sizeOf(@TypeOf(some_data)); } -// global variable alignment non power of 2 +// error +// backend=stage1 +// target=native // // tmp.zig:1:32: error: alignment value 3 is not a power of 2 diff --git a/test/compile_errors/stage1/obj/global_variable_initializer_must_be_constant_expression.zig b/test/compile_errors/stage1/obj/global_variable_initializer_must_be_constant_expression.zig index 1d6c20f3af..d01e828da6 100644 --- a/test/compile_errors/stage1/obj/global_variable_initializer_must_be_constant_expression.zig +++ b/test/compile_errors/stage1/obj/global_variable_initializer_must_be_constant_expression.zig @@ -2,6 +2,8 @@ extern fn foo() i32; const x = foo(); export fn entry() i32 { return x; } -// global variable initializer must be constant expression +// error +// backend=stage1 +// target=native // // tmp.zig:2:11: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/hasDecl_with_non-container.zig b/test/compile_errors/stage1/obj/hasDecl_with_non-container.zig index 50f1231997..e9aeacdd4e 100644 --- a/test/compile_errors/stage1/obj/hasDecl_with_non-container.zig +++ b/test/compile_errors/stage1/obj/hasDecl_with_non-container.zig @@ -2,6 +2,8 @@ export fn entry() void { _ = @hasDecl(i32, "hi"); } -// @hasDecl with non-container +// error +// backend=stage1 +// target=native // // tmp.zig:2:18: error: expected struct, enum, or union; found 'i32' diff --git a/test/compile_errors/stage1/obj/if_condition_is_bool_not_int.zig b/test/compile_errors/stage1/obj/if_condition_is_bool_not_int.zig index e8b7c61417..53afb2792a 100644 --- a/test/compile_errors/stage1/obj/if_condition_is_bool_not_int.zig +++ b/test/compile_errors/stage1/obj/if_condition_is_bool_not_int.zig @@ -2,6 +2,8 @@ export fn f() void { if (0) {} } -// if condition is bool, not int +// error +// backend=stage1 +// target=native // // tmp.zig:2:9: error: expected type 'bool', found 'comptime_int' diff --git a/test/compile_errors/stage1/obj/ignored_assert-err-ok_return_value.zig b/test/compile_errors/stage1/obj/ignored_assert-err-ok_return_value.zig index 53fbcaa8b5..4d45ae1304 100644 --- a/test/compile_errors/stage1/obj/ignored_assert-err-ok_return_value.zig +++ b/test/compile_errors/stage1/obj/ignored_assert-err-ok_return_value.zig @@ -3,6 +3,8 @@ export fn foo() void { } fn bar() anyerror!i32 { return 0; } -// ignored assert-err-ok return value +// error +// backend=stage1 +// target=native // // tmp.zig:2:11: error: expression value is ignored diff --git a/test/compile_errors/stage1/obj/ignored_comptime_statement_value.zig b/test/compile_errors/stage1/obj/ignored_comptime_statement_value.zig index 80925d6c84..0e82c9ae3e 100644 --- a/test/compile_errors/stage1/obj/ignored_comptime_statement_value.zig +++ b/test/compile_errors/stage1/obj/ignored_comptime_statement_value.zig @@ -2,6 +2,8 @@ export fn foo() void { comptime {1;} } -// ignored comptime statement value +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: expression value is ignored diff --git a/test/compile_errors/stage1/obj/ignored_comptime_value.zig b/test/compile_errors/stage1/obj/ignored_comptime_value.zig index 375a5d242c..0306ea72ff 100644 --- a/test/compile_errors/stage1/obj/ignored_comptime_value.zig +++ b/test/compile_errors/stage1/obj/ignored_comptime_value.zig @@ -2,6 +2,8 @@ export fn foo() void { comptime 1; } -// ignored comptime value +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: expression value is ignored diff --git a/test/compile_errors/stage1/obj/ignored_deferred_function_call.zig b/test/compile_errors/stage1/obj/ignored_deferred_function_call.zig index 58b85da985..41812bfda9 100644 --- a/test/compile_errors/stage1/obj/ignored_deferred_function_call.zig +++ b/test/compile_errors/stage1/obj/ignored_deferred_function_call.zig @@ -3,6 +3,8 @@ export fn foo() void { } fn bar() anyerror!i32 { return 0; } -// ignored deferred function call +// error +// backend=stage1 +// target=native // // tmp.zig:2:14: error: error is ignored. consider using `try`, `catch`, or `if` diff --git a/test/compile_errors/stage1/obj/ignored_deferred_statement_value.zig b/test/compile_errors/stage1/obj/ignored_deferred_statement_value.zig index effc79b039..1042c4f40a 100644 --- a/test/compile_errors/stage1/obj/ignored_deferred_statement_value.zig +++ b/test/compile_errors/stage1/obj/ignored_deferred_statement_value.zig @@ -2,6 +2,8 @@ export fn foo() void { defer {1;} } -// ignored deferred statement value +// error +// backend=stage1 +// target=native // // tmp.zig:2:12: error: expression value is ignored diff --git a/test/compile_errors/stage1/obj/ignored_expression_in_while_continuation.zig b/test/compile_errors/stage1/obj/ignored_expression_in_while_continuation.zig index fb205924e3..43f3713fc6 100644 --- a/test/compile_errors/stage1/obj/ignored_expression_in_while_continuation.zig +++ b/test/compile_errors/stage1/obj/ignored_expression_in_while_continuation.zig @@ -13,7 +13,9 @@ fn bad() anyerror!void { return error.Bad; } -// ignored expression in while continuation +// error +// backend=stage1 +// target=native // // tmp.zig:2:24: error: error is ignored. consider using `try`, `catch`, or `if` // tmp.zig:6:25: error: error is ignored. consider using `try`, `catch`, or `if` diff --git a/test/compile_errors/stage1/obj/ignored_return_value.zig b/test/compile_errors/stage1/obj/ignored_return_value.zig index 9c8cfa0aa4..b918523b37 100644 --- a/test/compile_errors/stage1/obj/ignored_return_value.zig +++ b/test/compile_errors/stage1/obj/ignored_return_value.zig @@ -3,6 +3,8 @@ export fn foo() void { } fn bar() i32 { return 0; } -// ignored return value +// error +// backend=stage1 +// target=native // // tmp.zig:2:8: error: expression value is ignored diff --git a/test/compile_errors/stage1/obj/ignored_statement_value.zig b/test/compile_errors/stage1/obj/ignored_statement_value.zig index 7855cf584b..a0e540a92b 100644 --- a/test/compile_errors/stage1/obj/ignored_statement_value.zig +++ b/test/compile_errors/stage1/obj/ignored_statement_value.zig @@ -2,6 +2,8 @@ export fn foo() void { 1; } -// ignored statement value +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: expression value is ignored diff --git a/test/compile_errors/stage1/obj/illegal_comparison_of_types.zig b/test/compile_errors/stage1/obj/illegal_comparison_of_types.zig index 462664a400..d04eaec3b5 100644 --- a/test/compile_errors/stage1/obj/illegal_comparison_of_types.zig +++ b/test/compile_errors/stage1/obj/illegal_comparison_of_types.zig @@ -12,7 +12,9 @@ fn bad_eql_2(a: *const EnumWithData, b: *const EnumWithData) bool { export fn entry1() usize { return @sizeOf(@TypeOf(bad_eql_1)); } export fn entry2() usize { return @sizeOf(@TypeOf(bad_eql_2)); } -// illegal comparison of types +// error +// backend=stage1 +// target=native // // tmp.zig:2:14: error: operator not allowed for type '[]u8' // tmp.zig:9:16: error: operator not allowed for type 'EnumWithData' diff --git a/test/compile_errors/stage1/obj/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig b/test/compile_errors/stage1/obj/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig index 1dd751f989..6ff155b591 100644 --- a/test/compile_errors/stage1/obj/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig +++ b/test/compile_errors/stage1/obj/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig @@ -29,7 +29,9 @@ export fn f() void { _ = x; } -// implicit cast between C pointer and Zig pointer - bad const/align/child +// error +// backend=stage1 +// target=native // // tmp.zig:3:27: error: cast increases pointer alignment // tmp.zig:8:18: error: cast discards const qualifier diff --git a/test/compile_errors/stage1/obj/implicit_cast_const_array_to_mutable_slice.zig b/test/compile_errors/stage1/obj/implicit_cast_const_array_to_mutable_slice.zig index f38c2c9fe1..3f22cbed6b 100644 --- a/test/compile_errors/stage1/obj/implicit_cast_const_array_to_mutable_slice.zig +++ b/test/compile_errors/stage1/obj/implicit_cast_const_array_to_mutable_slice.zig @@ -4,7 +4,9 @@ export fn entry() void { _ = sliceA; } -// implicit cast const array to mutable slice +// error +// backend=stage1 +// target=native // // tmp.zig:3:27: error: cannot cast pointer to array literal to slice type '[]u8' // tmp.zig:3:27: note: cast discards const qualifier diff --git a/test/compile_errors/stage1/obj/implicit_cast_from_array_to_mutable_slice.zig b/test/compile_errors/stage1/obj/implicit_cast_from_array_to_mutable_slice.zig index 60fc5baf1f..012555928c 100644 --- a/test/compile_errors/stage1/obj/implicit_cast_from_array_to_mutable_slice.zig +++ b/test/compile_errors/stage1/obj/implicit_cast_from_array_to_mutable_slice.zig @@ -4,6 +4,8 @@ export fn entry() void { foo(global_array); } -// implicit cast from array to mutable slice +// error +// backend=stage1 +// target=native // // tmp.zig:4:9: error: expected type '[]i32', found '[10]i32' diff --git a/test/compile_errors/stage1/obj/implicit_cast_from_f64_to_f32.zig b/test/compile_errors/stage1/obj/implicit_cast_from_f64_to_f32.zig index d1d4417f34..f18a46b7f5 100644 --- a/test/compile_errors/stage1/obj/implicit_cast_from_f64_to_f32.zig +++ b/test/compile_errors/stage1/obj/implicit_cast_from_f64_to_f32.zig @@ -3,6 +3,8 @@ var y: f32 = x; export fn entry() usize { return @sizeOf(@TypeOf(y)); } -// implicit cast from f64 to f32 +// error +// backend=stage1 +// target=native // // tmp.zig:2:14: error: expected type 'f32', found 'f64' diff --git a/test/compile_errors/stage1/obj/implicit_cast_of_error_set_not_a_subset.zig b/test/compile_errors/stage1/obj/implicit_cast_of_error_set_not_a_subset.zig index 9cfce6524f..5958a41639 100644 --- a/test/compile_errors/stage1/obj/implicit_cast_of_error_set_not_a_subset.zig +++ b/test/compile_errors/stage1/obj/implicit_cast_of_error_set_not_a_subset.zig @@ -8,7 +8,9 @@ fn foo(set1: Set1) void { _ = x; } -// implicit cast of error set not a subset +// error +// backend=stage1 +// target=native // // tmp.zig:7:19: error: expected type 'Set2', found 'Set1' // tmp.zig:1:23: note: 'error.B' not a member of destination error set diff --git a/test/compile_errors/stage1/obj/implicit_casting_C_pointers_which_would_mess_up_null_semantics.zig b/test/compile_errors/stage1/obj/implicit_casting_C_pointers_which_would_mess_up_null_semantics.zig index cdc35e3963..760bab41da 100644 --- a/test/compile_errors/stage1/obj/implicit_casting_C_pointers_which_would_mess_up_null_semantics.zig +++ b/test/compile_errors/stage1/obj/implicit_casting_C_pointers_which_would_mess_up_null_semantics.zig @@ -14,7 +14,9 @@ export fn entry2() void { _ = c_ptr; } -// implicit casting C pointers which would mess up null semantics +// error +// backend=stage1 +// target=native // // tmp.zig:6:24: error: expected type '*const [*]const u8', found '[*c]const [*c]const u8' // tmp.zig:6:24: note: pointer type child '[*c]const u8' cannot cast into pointer type child '[*]const u8' diff --git a/test/compile_errors/stage1/obj/implicit_casting_null_c_pointer_to_zig_pointer.zig b/test/compile_errors/stage1/obj/implicit_casting_null_c_pointer_to_zig_pointer.zig index 29dd6d06f9..0bd38d868b 100644 --- a/test/compile_errors/stage1/obj/implicit_casting_null_c_pointer_to_zig_pointer.zig +++ b/test/compile_errors/stage1/obj/implicit_casting_null_c_pointer_to_zig_pointer.zig @@ -4,6 +4,8 @@ comptime { _ = zig_ptr; } -// implicit casting null c pointer to zig pointer +// error +// backend=stage1 +// target=native // // tmp.zig:3:24: error: null pointer casted to type '*u8' diff --git a/test/compile_errors/stage1/obj/implicit_casting_too_big_integers_to_C_pointers.zig b/test/compile_errors/stage1/obj/implicit_casting_too_big_integers_to_C_pointers.zig index 3deb817b76..f142540ff6 100644 --- a/test/compile_errors/stage1/obj/implicit_casting_too_big_integers_to_C_pointers.zig +++ b/test/compile_errors/stage1/obj/implicit_casting_too_big_integers_to_C_pointers.zig @@ -8,7 +8,9 @@ export fn b() void { _ = ptr; } -// implicit casting too big integers to C pointers +// error +// backend=stage1 +// target=native // // tmp.zig:2:33: error: integer value 18446744073709551617 cannot be coerced to type 'usize' // tmp.zig:7:23: error: integer type 'u65' too big for implicit @intToPtr to type '[*c]u8' diff --git a/test/compile_errors/stage1/obj/implicit_casting_undefined_c_pointer_to_zig_pointer.zig b/test/compile_errors/stage1/obj/implicit_casting_undefined_c_pointer_to_zig_pointer.zig index 4c19f57442..5554146fa5 100644 --- a/test/compile_errors/stage1/obj/implicit_casting_undefined_c_pointer_to_zig_pointer.zig +++ b/test/compile_errors/stage1/obj/implicit_casting_undefined_c_pointer_to_zig_pointer.zig @@ -4,6 +4,8 @@ comptime { _ = zig_ptr; } -// implicit casting undefined c pointer to zig pointer +// error +// backend=stage1 +// target=native // // tmp.zig:3:24: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/implicit_dependency_on_libc.zig b/test/compile_errors/stage1/obj/implicit_dependency_on_libc.zig new file mode 100644 index 0000000000..f069f78551 --- /dev/null +++ b/test/compile_errors/stage1/obj/implicit_dependency_on_libc.zig @@ -0,0 +1,11 @@ +extern "c" fn exit(u8) void; +export fn entry() void { + exit(0); +} + +// error +// backend=stage1 +// target=native-linux +// is_test=1 +// +// tmp.zig:3:5: error: dependency on libc must be explicitly specified in the build command diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-block_expr.zig b/test/compile_errors/stage1/obj/implicit_semicolon-block_expr.zig index bacd484c0c..a57da8ac0b 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-block_expr.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-block_expr.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - block expr +// error +// backend=stage1 +// target=native // // tmp.zig:4:11: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-block_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-block_statement.zig index dca8998ee6..2d9d850e75 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-block_statement.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-block_statement.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - block statement +// error +// backend=stage1 +// target=native // // tmp.zig:4:9: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-comptime_expression.zig b/test/compile_errors/stage1/obj/implicit_semicolon-comptime_expression.zig index a3f679a59a..b4cd4de849 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-comptime_expression.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-comptime_expression.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - comptime expression +// error +// backend=stage1 +// target=native // // tmp.zig:4:20: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-comptime_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-comptime_statement.zig index 299b081e4b..3b83ef92eb 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-comptime_statement.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-comptime_statement.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - comptime statement +// error +// backend=stage1 +// target=native // // tmp.zig:4:18: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-defer.zig b/test/compile_errors/stage1/obj/implicit_semicolon-defer.zig index 7e58d930d6..0ded4fcf26 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-defer.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-defer.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - defer +// error +// backend=stage1 +// target=native // // tmp.zig:4:15: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-for_expression.zig b/test/compile_errors/stage1/obj/implicit_semicolon-for_expression.zig index 3e332ef189..964a48d30b 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-for_expression.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-for_expression.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - for expression +// error +// backend=stage1 +// target=native // // tmp.zig:4:26: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-for_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-for_statement.zig index 092c28889f..1320e4cc46 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-for_statement.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-for_statement.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - for statement +// error +// backend=stage1 +// target=native // // tmp.zig:4:24: error: expected ';' or 'else' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_expression.zig b/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_expression.zig index fd55529a8f..40b1a33d78 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_expression.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_expression.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - if-else-if-else expression +// error +// backend=stage1 +// target=native // // tmp.zig:4:45: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_statement.zig index 3d59e38aa9..8444fe654d 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_statement.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_statement.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - if-else-if-else statement +// error +// backend=stage1 +// target=native // // tmp.zig:4:47: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_expression.zig b/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_expression.zig index 2caaad52b8..7466d64691 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_expression.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_expression.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - if-else-if expression +// error +// backend=stage1 +// target=native // // tmp.zig:4:37: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_statement.zig index 263aa36da3..34e116b3f7 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_statement.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_statement.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - if-else-if statement +// error +// backend=stage1 +// target=native // // tmp.zig:4:37: error: expected ';' or 'else' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if-else_expression.zig b/test/compile_errors/stage1/obj/implicit_semicolon-if-else_expression.zig index 12d993f6a7..3c1e0add62 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-if-else_expression.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-if-else_expression.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - if-else expression +// error +// backend=stage1 +// target=native // // tmp.zig:4:28: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if-else_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-if-else_statement.zig index 401fc46a51..e9c52fb6bb 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-if-else_statement.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-if-else_statement.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - if-else statement +// error +// backend=stage1 +// target=native // // tmp.zig:4:28: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if_expression.zig b/test/compile_errors/stage1/obj/implicit_semicolon-if_expression.zig index b3a81e9d36..2082588e0e 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-if_expression.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-if_expression.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - if expression +// error +// backend=stage1 +// target=native // // tmp.zig:4:20: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-if_statement.zig index e543925d78..26cefd0237 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-if_statement.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-if_statement.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - if statement +// error +// backend=stage1 +// target=native // // tmp.zig:4:18: error: expected ';' or 'else' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-test_expression.zig b/test/compile_errors/stage1/obj/implicit_semicolon-test_expression.zig index 1702bec048..c31f3520c1 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-test_expression.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-test_expression.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - test expression +// error +// backend=stage1 +// target=native // // tmp.zig:4:26: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-test_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-test_statement.zig index 8715ca9ac0..8abb1cbcb1 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-test_statement.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-test_statement.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - test statement +// error +// backend=stage1 +// target=native // // tmp.zig:4:24: error: expected ';' or 'else' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-while-continue_expression.zig b/test/compile_errors/stage1/obj/implicit_semicolon-while-continue_expression.zig index b1a7bdbab8..4d3219eb66 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-while-continue_expression.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-while-continue_expression.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - while-continue expression +// error +// backend=stage1 +// target=native // // tmp.zig:4:28: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-while-continue_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-while-continue_statement.zig index 601f1f0318..c0b3370357 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-while-continue_statement.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-while-continue_statement.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - while-continue statement +// error +// backend=stage1 +// target=native // // tmp.zig:4:26: error: expected ';' or 'else' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-while_expression.zig b/test/compile_errors/stage1/obj/implicit_semicolon-while_expression.zig index 9580889bd7..39f49319d5 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-while_expression.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-while_expression.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - while expression +// error +// backend=stage1 +// target=native // // tmp.zig:4:23: error: expected ';' after statement diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-while_statement.zig b/test/compile_errors/stage1/obj/implicit_semicolon-while_statement.zig index ab64dd991d..6c253ecf56 100644 --- a/test/compile_errors/stage1/obj/implicit_semicolon-while_statement.zig +++ b/test/compile_errors/stage1/obj/implicit_semicolon-while_statement.zig @@ -5,6 +5,8 @@ export fn entry() void { var bad = {}; } -// implicit semicolon - while statement +// error +// backend=stage1 +// target=native // // tmp.zig:4:18: error: expected ';' or 'else' after statement diff --git a/test/compile_errors/stage1/obj/implicitly_casting_enum_to_tag_type.zig b/test/compile_errors/stage1/obj/implicitly_casting_enum_to_tag_type.zig index 26806a3914..a40615f99b 100644 --- a/test/compile_errors/stage1/obj/implicitly_casting_enum_to_tag_type.zig +++ b/test/compile_errors/stage1/obj/implicitly_casting_enum_to_tag_type.zig @@ -10,6 +10,8 @@ export fn entry() void { _ = x; } -// implicitly casting enum to tag type +// error +// backend=stage1 +// target=native // // tmp.zig:9:22: error: expected type 'u2', found 'Small' diff --git a/test/compile_errors/stage1/obj/implicitly_increasing_pointer_alignment.zig b/test/compile_errors/stage1/obj/implicitly_increasing_pointer_alignment.zig index 8f743a3b59..3743eef0b2 100644 --- a/test/compile_errors/stage1/obj/implicitly_increasing_pointer_alignment.zig +++ b/test/compile_errors/stage1/obj/implicitly_increasing_pointer_alignment.zig @@ -12,6 +12,8 @@ fn bar(x: *u32) void { x.* += 1; } -// implicitly increasing pointer alignment +// error +// backend=stage1 +// target=native // // tmp.zig:8:13: error: expected type '*u32', found '*align(1) u32' diff --git a/test/compile_errors/stage1/obj/implicitly_increasing_slice_alignment.zig b/test/compile_errors/stage1/obj/implicitly_increasing_slice_alignment.zig index c1d4ec2a0f..2e5c201b5d 100644 --- a/test/compile_errors/stage1/obj/implicitly_increasing_slice_alignment.zig +++ b/test/compile_errors/stage1/obj/implicitly_increasing_slice_alignment.zig @@ -13,7 +13,9 @@ fn bar(x: []u32) void { x[0] += 1; } -// implicitly increasing slice alignment +// error +// backend=stage1 +// target=native // // tmp.zig:9:26: error: cast increases pointer alignment // tmp.zig:9:26: note: '*align(1) u32' has alignment 1 diff --git a/test/compile_errors/stage1/obj/import_outside_package_path.zig b/test/compile_errors/stage1/obj/import_outside_package_path.zig index 843de67b63..5a712c2e01 100644 --- a/test/compile_errors/stage1/obj/import_outside_package_path.zig +++ b/test/compile_errors/stage1/obj/import_outside_package_path.zig @@ -2,6 +2,8 @@ comptime{ _ = @import("../a.zig"); } -// import outside package path +// error +// backend=stage1 +// target=native // // tmp.zig:2:9: error: import of file outside package path: '../a.zig' diff --git a/test/compile_errors/stage1/obj/incompatible_sentinels.zig b/test/compile_errors/stage1/obj/incompatible_sentinels.zig new file mode 100644 index 0000000000..a292ae3d7c --- /dev/null +++ b/test/compile_errors/stage1/obj/incompatible_sentinels.zig @@ -0,0 +1,29 @@ +// Note: One of the error messages here is backwards. It would be nice to fix, but that's not +// going to stop me from merging this branch which fixes a bunch of other stuff. +export fn entry1(ptr: [*:255]u8) [*:0]u8 { + return ptr; +} +export fn entry2(ptr: [*]u8) [*:0]u8 { + return ptr; +} +export fn entry3() void { + var array: [2:0]u8 = [_:255]u8{ 1, 2 }; + _ = array; +} +export fn entry4() void { + var array: [2:0]u8 = [_]u8{ 1, 2 }; + _ = array; +} + +// error +// backend=stage1 +// target=native +// +// tmp.zig:4:12: error: expected type '[*:0]u8', found '[*:255]u8' +// tmp.zig:4:12: note: destination pointer requires a terminating '0' sentinel, but source pointer has a terminating '255' sentinel +// tmp.zig:7:12: error: expected type '[*:0]u8', found '[*]u8' +// tmp.zig:7:12: note: destination pointer requires a terminating '0' sentinel +// tmp.zig:10:35: error: expected type '[2:255]u8', found '[2:0]u8' +// tmp.zig:10:35: note: destination array requires a terminating '255' sentinel, but source array has a terminating '0' sentinel +// tmp.zig:14:31: error: expected type '[2:0]u8', found '[2]u8' +// tmp.zig:14:31: note: destination array requires a terminating '0' sentinel diff --git a/test/compile_errors/stage1/obj/incorrect_return_type.zig b/test/compile_errors/stage1/obj/incorrect_return_type.zig index 86c5f23dc3..b25e2a8ea4 100644 --- a/test/compile_errors/stage1/obj/incorrect_return_type.zig +++ b/test/compile_errors/stage1/obj/incorrect_return_type.zig @@ -14,6 +14,8 @@ unreachable; } -// incorrect return type +// error +// backend=stage1 +// target=native // // tmp.zig:8:16: error: expected type 'A', found 'B' diff --git a/test/compile_errors/stage1/obj/increase_pointer_alignment_in_ptrCast.zig b/test/compile_errors/stage1/obj/increase_pointer_alignment_in_ptrCast.zig index 01d8ac821e..f3f67ca4c6 100644 --- a/test/compile_errors/stage1/obj/increase_pointer_alignment_in_ptrCast.zig +++ b/test/compile_errors/stage1/obj/increase_pointer_alignment_in_ptrCast.zig @@ -4,7 +4,9 @@ export fn entry() u32 { return ptr.*; } -// increase pointer alignment in @ptrCast +// error +// backend=stage1 +// target=native // // tmp.zig:3:17: error: cast increases pointer alignment // tmp.zig:3:38: note: '*u8' has alignment 1 diff --git a/test/compile_errors/stage1/obj/indexing_a_undefined_slice_at_comptime.zig b/test/compile_errors/stage1/obj/indexing_a_undefined_slice_at_comptime.zig index 4653d8668e..70b0c68658 100644 --- a/test/compile_errors/stage1/obj/indexing_a_undefined_slice_at_comptime.zig +++ b/test/compile_errors/stage1/obj/indexing_a_undefined_slice_at_comptime.zig @@ -3,6 +3,8 @@ comptime { slice[0] = 2; } -// indexing a undefined slice at comptime +// error +// backend=stage1 +// target=native // // tmp.zig:3:10: error: index 0 outside slice of size 0 diff --git a/test/compile_errors/stage1/obj/indexing_an_array_of_size_zero.zig b/test/compile_errors/stage1/obj/indexing_an_array_of_size_zero.zig index 9066985de5..dfb2e7c1c3 100644 --- a/test/compile_errors/stage1/obj/indexing_an_array_of_size_zero.zig +++ b/test/compile_errors/stage1/obj/indexing_an_array_of_size_zero.zig @@ -4,6 +4,8 @@ export fn foo() void { _ = pointer; } -// indexing an array of size zero +// error +// backend=stage1 +// target=native // // tmp.zig:3:27: error: accessing a zero length array is not allowed diff --git a/test/compile_errors/stage1/obj/indexing_an_array_of_size_zero_with_runtime_index.zig b/test/compile_errors/stage1/obj/indexing_an_array_of_size_zero_with_runtime_index.zig index c5f3acb3cc..f50931312e 100644 --- a/test/compile_errors/stage1/obj/indexing_an_array_of_size_zero_with_runtime_index.zig +++ b/test/compile_errors/stage1/obj/indexing_an_array_of_size_zero_with_runtime_index.zig @@ -5,6 +5,8 @@ export fn foo() void { _ = pointer; } -// indexing an array of size zero with runtime index +// error +// backend=stage1 +// target=native // // tmp.zig:4:27: error: accessing a zero length array is not allowed diff --git a/test/compile_errors/stage1/obj/indexing_single-item_pointer.zig b/test/compile_errors/stage1/obj/indexing_single-item_pointer.zig index e228083964..bc7951ec96 100644 --- a/test/compile_errors/stage1/obj/indexing_single-item_pointer.zig +++ b/test/compile_errors/stage1/obj/indexing_single-item_pointer.zig @@ -2,6 +2,8 @@ export fn entry(ptr: *i32) i32 { return ptr[1]; } -// indexing single-item pointer +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: index of single-item pointer diff --git a/test/compile_errors/stage1/obj/indirect_recursion_of_async_functions_detected.zig b/test/compile_errors/stage1/obj/indirect_recursion_of_async_functions_detected.zig index 5765f7aae1..d845dc7930 100644 --- a/test/compile_errors/stage1/obj/indirect_recursion_of_async_functions_detected.zig +++ b/test/compile_errors/stage1/obj/indirect_recursion_of_async_functions_detected.zig @@ -27,7 +27,9 @@ fn rangeSumIndirect(x: i32) i32 { return child + 1; } -// indirect recursion of async functions detected +// error +// backend=stage1 +// target=native // // tmp.zig:8:1: error: '@Frame(rangeSum)' depends on itself // tmp.zig:15:33: note: when analyzing type '@Frame(rangeSum)' here diff --git a/test/compile_errors/stage1/obj/indirect_struct_loop.zig b/test/compile_errors/stage1/obj/indirect_struct_loop.zig index 903a1bda39..12214923d0 100644 --- a/test/compile_errors/stage1/obj/indirect_struct_loop.zig +++ b/test/compile_errors/stage1/obj/indirect_struct_loop.zig @@ -3,6 +3,8 @@ const B = struct { c : C, }; const C = struct { a : A, }; export fn entry() usize { return @sizeOf(A); } -// indirect struct loop +// error +// backend=stage1 +// target=native // // tmp.zig:1:11: error: struct 'A' depends on itself diff --git a/test/compile_errors/stage1/obj/inferred_array_size_invalid_here.zig b/test/compile_errors/stage1/obj/inferred_array_size_invalid_here.zig index a45b6aa027..2b2d944eab 100644 --- a/test/compile_errors/stage1/obj/inferred_array_size_invalid_here.zig +++ b/test/compile_errors/stage1/obj/inferred_array_size_invalid_here.zig @@ -8,7 +8,9 @@ export fn entry2() void { _ = a; } -// inferred array size invalid here +// error +// backend=stage1 +// target=native // // tmp.zig:2:16: error: unable to infer array size // tmp.zig:6:35: error: unable to infer array size diff --git a/test/compile_errors/stage1/obj/inferring_error_set_of_function_pointer.zig b/test/compile_errors/stage1/obj/inferring_error_set_of_function_pointer.zig index ead5afd248..b91242322d 100644 --- a/test/compile_errors/stage1/obj/inferring_error_set_of_function_pointer.zig +++ b/test/compile_errors/stage1/obj/inferring_error_set_of_function_pointer.zig @@ -2,6 +2,8 @@ comptime { const z: ?fn()!void = null; } -// inferring error set of function pointer +// error +// backend=stage1 +// target=native // // tmp.zig:2:19: error: function prototype may not have inferred error set diff --git a/test/compile_errors/stage1/obj/initializing_array_with_struct_syntax.zig b/test/compile_errors/stage1/obj/initializing_array_with_struct_syntax.zig index 15412ac5ab..2884c6193e 100644 --- a/test/compile_errors/stage1/obj/initializing_array_with_struct_syntax.zig +++ b/test/compile_errors/stage1/obj/initializing_array_with_struct_syntax.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = x; } -// initializing array with struct syntax +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: initializing array with struct syntax diff --git a/test/compile_errors/stage1/obj/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig b/test/compile_errors/stage1/obj/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig index c39908c9b5..dd6909b1c2 100644 --- a/test/compile_errors/stage1/obj/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig +++ b/test/compile_errors/stage1/obj/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig @@ -8,6 +8,8 @@ export fn entry() usize { return @sizeOf(@TypeOf(foo.x)); } -// instantiating an undefined value for an invalid struct that contains itself +// error +// backend=stage1 +// target=native // // tmp.zig:1:13: error: struct 'Foo' depends on itself diff --git a/test/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig b/test/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig index a04f318f0c..16b6bf565e 100644 --- a/test/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig +++ b/test/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig @@ -3,6 +3,8 @@ pub fn main() void { _ = y; } -// intToPtr with misaligned address +// error +// backend=stage1 +// target=native // // tmp.zig:2:13: error: pointer type '[*]align(4) u8' requires aligned address diff --git a/test/compile_errors/stage1/obj/int_to_err_global_invalid_number.zig b/test/compile_errors/stage1/obj/int_to_err_global_invalid_number.zig index 180da60ab2..5c078a751c 100644 --- a/test/compile_errors/stage1/obj/int_to_err_global_invalid_number.zig +++ b/test/compile_errors/stage1/obj/int_to_err_global_invalid_number.zig @@ -8,6 +8,8 @@ comptime { _ = y; } -// int to err global invalid number +// error +// backend=stage1 +// target=native // // tmp.zig:7:13: error: integer value 3 represents no error diff --git a/test/compile_errors/stage1/obj/int_to_err_non_global_invalid_number.zig b/test/compile_errors/stage1/obj/int_to_err_non_global_invalid_number.zig index a6627891d0..8c1e547746 100644 --- a/test/compile_errors/stage1/obj/int_to_err_non_global_invalid_number.zig +++ b/test/compile_errors/stage1/obj/int_to_err_non_global_invalid_number.zig @@ -12,6 +12,8 @@ comptime { _ = y; } -// int to err non global invalid number +// error +// backend=stage1 +// target=native // // tmp.zig:11:13: error: error.B not a member of error set 'Set2' diff --git a/test/compile_errors/stage1/obj/int_to_ptr_of_0_bits.zig b/test/compile_errors/stage1/obj/int_to_ptr_of_0_bits.zig index 7047600c1b..7b65cdf836 100644 --- a/test/compile_errors/stage1/obj/int_to_ptr_of_0_bits.zig +++ b/test/compile_errors/stage1/obj/int_to_ptr_of_0_bits.zig @@ -4,6 +4,8 @@ export fn foo() void { _ = y; } -// int to ptr of 0 bits +// error +// backend=stage1 +// target=native // // tmp.zig:3:30: error: type '*void' has 0 bits and cannot store information diff --git a/test/compile_errors/stage1/obj/integer_cast_truncates_bits.zig b/test/compile_errors/stage1/obj/integer_cast_truncates_bits.zig index a5c8036152..e8ef418624 100644 --- a/test/compile_errors/stage1/obj/integer_cast_truncates_bits.zig +++ b/test/compile_errors/stage1/obj/integer_cast_truncates_bits.zig @@ -19,7 +19,9 @@ export fn entry4() void { _ = unsigned; } -// integer cast truncates bits +// error +// backend=stage1 +// target=native // // tmp.zig:3:18: error: cast from 'u16' to 'u8' truncates bits // tmp.zig:8:22: error: integer value 300 cannot be coerced to type 'u8' diff --git a/test/compile_errors/stage1/obj/integer_overflow_error.zig b/test/compile_errors/stage1/obj/integer_overflow_error.zig index 568cc5034b..117d77ed93 100644 --- a/test/compile_errors/stage1/obj/integer_overflow_error.zig +++ b/test/compile_errors/stage1/obj/integer_overflow_error.zig @@ -1,6 +1,8 @@ const x : u8 = 300; export fn entry() usize { return @sizeOf(@TypeOf(x)); } -// integer overflow error +// error +// backend=stage1 +// target=native // // tmp.zig:1:16: error: integer value 300 cannot be coerced to type 'u8' diff --git a/test/compile_errors/stage1/obj/integer_underflow_error.zig b/test/compile_errors/stage1/obj/integer_underflow_error.zig index 01171589d9..15bd4c472b 100644 --- a/test/compile_errors/stage1/obj/integer_underflow_error.zig +++ b/test/compile_errors/stage1/obj/integer_underflow_error.zig @@ -2,6 +2,8 @@ export fn entry() void { _ = @intToPtr(*anyopaque, ~@as(usize, @import("std").math.maxInt(usize)) - 1); } -// integer underflow error +// error +// backend=stage1 +// target=native // // :2:78: error: operation caused overflow diff --git a/test/compile_errors/stage1/obj/invalid_break_expression.zig b/test/compile_errors/stage1/obj/invalid_break_expression.zig index b6d27d6ea5..efb76a34f7 100644 --- a/test/compile_errors/stage1/obj/invalid_break_expression.zig +++ b/test/compile_errors/stage1/obj/invalid_break_expression.zig @@ -2,6 +2,8 @@ export fn f() void { break; } -// invalid break expression +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: break expression outside loop diff --git a/test/compile_errors/stage1/obj/invalid_builtin_fn.zig b/test/compile_errors/stage1/obj/invalid_builtin_fn.zig index e10baf5965..8f0e3633bb 100644 --- a/test/compile_errors/stage1/obj/invalid_builtin_fn.zig +++ b/test/compile_errors/stage1/obj/invalid_builtin_fn.zig @@ -2,6 +2,8 @@ fn f() @bogus(foo) { } export fn entry() void { _ = f(); } -// invalid builtin fn +// error +// backend=stage1 +// target=native // // tmp.zig:1:8: error: invalid builtin function: '@bogus' diff --git a/test/compile_errors/stage1/obj/invalid_cast_from_integral_type_to_enum.zig b/test/compile_errors/stage1/obj/invalid_cast_from_integral_type_to_enum.zig index 476a4929ef..81dbd88e3b 100644 --- a/test/compile_errors/stage1/obj/invalid_cast_from_integral_type_to_enum.zig +++ b/test/compile_errors/stage1/obj/invalid_cast_from_integral_type_to_enum.zig @@ -10,6 +10,8 @@ fn foo(x: usize) void { } } -// invalid cast from integral type to enum +// error +// backend=stage1 +// target=native // // tmp.zig:9:10: error: expected type 'usize', found 'E' diff --git a/test/compile_errors/stage1/obj/invalid_comparison_for_function_pointers.zig b/test/compile_errors/stage1/obj/invalid_comparison_for_function_pointers.zig index f7827ed0e9..6d64b6cdaf 100644 --- a/test/compile_errors/stage1/obj/invalid_comparison_for_function_pointers.zig +++ b/test/compile_errors/stage1/obj/invalid_comparison_for_function_pointers.zig @@ -3,6 +3,8 @@ const invalid = foo > foo; export fn entry() usize { return @sizeOf(@TypeOf(invalid)); } -// invalid comparison for function pointers +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: operator not allowed for type 'fn() void' diff --git a/test/compile_errors/stage1/obj/invalid_continue_expression.zig b/test/compile_errors/stage1/obj/invalid_continue_expression.zig index 9bd40c516a..00f3c5a3d5 100644 --- a/test/compile_errors/stage1/obj/invalid_continue_expression.zig +++ b/test/compile_errors/stage1/obj/invalid_continue_expression.zig @@ -2,6 +2,8 @@ export fn f() void { continue; } -// invalid continue expression +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: continue expression outside loop diff --git a/test/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig b/test/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig index 47dc0a52c1..966a881543 100644 --- a/test/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig +++ b/test/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig @@ -10,6 +10,8 @@ const Tile = enum { Filled, }; -// invalid deref on switch target +// error +// backend=stage1 +// target=native // // tmp.zig:3:17: error: attempt to dereference non-pointer type 'Tile' diff --git a/test/compile_errors/stage1/obj/invalid_empty_unicode_escape.zig b/test/compile_errors/stage1/obj/invalid_empty_unicode_escape.zig index d8853110a4..57dd16a63d 100644 --- a/test/compile_errors/stage1/obj/invalid_empty_unicode_escape.zig +++ b/test/compile_errors/stage1/obj/invalid_empty_unicode_escape.zig @@ -2,6 +2,8 @@ export fn entry() void { const a = '\u{}'; } -// invalid empty unicode escape +// error +// backend=stage1 +// target=native // // tmp.zig:2:19: error: empty unicode escape sequence diff --git a/test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-1.zig b/test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-1.zig index 4f13de2a62..431401c534 100644 --- a/test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-1.zig +++ b/test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-1.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid exponent in float literal - 1 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:28: note: invalid byte: 'a' diff --git a/test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-2.zig b/test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-2.zig index f612d8c510..088b43546c 100644 --- a/test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-2.zig +++ b/test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-2.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid exponent in float literal - 2 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:29: note: invalid byte: 'F' diff --git a/test/compile_errors/stage1/obj/invalid_field_access_in_comptime.zig b/test/compile_errors/stage1/obj/invalid_field_access_in_comptime.zig index e8395b5e7a..2630a704ed 100644 --- a/test/compile_errors/stage1/obj/invalid_field_access_in_comptime.zig +++ b/test/compile_errors/stage1/obj/invalid_field_access_in_comptime.zig @@ -1,5 +1,7 @@ comptime { var x = doesnt_exist.whatever; _ = x; } -// invalid field access in comptime +// error +// backend=stage1 +// target=native // // tmp.zig:1:20: error: use of undeclared identifier 'doesnt_exist' diff --git a/test/compile_errors/stage1/obj/invalid_field_in_struct_value_expression.zig b/test/compile_errors/stage1/obj/invalid_field_in_struct_value_expression.zig index 41511f0d0a..fae0a90b51 100644 --- a/test/compile_errors/stage1/obj/invalid_field_in_struct_value_expression.zig +++ b/test/compile_errors/stage1/obj/invalid_field_in_struct_value_expression.zig @@ -12,6 +12,8 @@ export fn f() void { _ = a; } -// invalid field in struct value expression +// error +// backend=stage1 +// target=native // // tmp.zig:10:9: error: no member named 'foo' in struct 'A' diff --git a/test/compile_errors/stage1/obj/invalid_float_literal.zig b/test/compile_errors/stage1/obj/invalid_float_literal.zig index 5d9a26032f..709609a027 100644 --- a/test/compile_errors/stage1/obj/invalid_float_literal.zig +++ b/test/compile_errors/stage1/obj/invalid_float_literal.zig @@ -6,6 +6,8 @@ pub fn main() void { std.debug.assert(bad_float < 1.0); } -// invalid float literal +// error +// backend=stage1 +// target=native // // tmp.zig:5:29: error: expected expression, found '.' diff --git a/test/compile_errors/stage1/obj/invalid_legacy_unicode_escape.zig b/test/compile_errors/stage1/obj/invalid_legacy_unicode_escape.zig index 58e1390345..b81a814a60 100644 --- a/test/compile_errors/stage1/obj/invalid_legacy_unicode_escape.zig +++ b/test/compile_errors/stage1/obj/invalid_legacy_unicode_escape.zig @@ -2,7 +2,9 @@ export fn entry() void { const a = '\U1234'; } -// invalid legacy unicode escape +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: expected expression, found 'invalid bytes' // tmp.zig:2:18: note: invalid byte: '1' diff --git a/test/compile_errors/stage1/obj/invalid_maybe_type.zig b/test/compile_errors/stage1/obj/invalid_maybe_type.zig index 9edc313526..cb075c36b7 100644 --- a/test/compile_errors/stage1/obj/invalid_maybe_type.zig +++ b/test/compile_errors/stage1/obj/invalid_maybe_type.zig @@ -2,6 +2,8 @@ export fn f() void { if (true) |x| { _ = x; } } -// invalid maybe type +// error +// backend=stage1 +// target=native // // tmp.zig:2:9: error: expected optional type, found 'bool' diff --git a/test/compile_errors/stage1/obj/invalid_member_of_builtin_enum.zig b/test/compile_errors/stage1/obj/invalid_member_of_builtin_enum.zig index 32888ff84b..166ebfc782 100644 --- a/test/compile_errors/stage1/obj/invalid_member_of_builtin_enum.zig +++ b/test/compile_errors/stage1/obj/invalid_member_of_builtin_enum.zig @@ -4,6 +4,8 @@ export fn entry() void { _ = foo; } -// invalid member of builtin enum +// error +// backend=stage1 +// target=native // // tmp.zig:3:29: error: container 'std.builtin.Mode' has no member called 'x86' diff --git a/test/compile_errors/stage1/obj/invalid_multiple_dereferences.zig b/test/compile_errors/stage1/obj/invalid_multiple_dereferences.zig index bdf5ccb228..f8a0b8013f 100644 --- a/test/compile_errors/stage1/obj/invalid_multiple_dereferences.zig +++ b/test/compile_errors/stage1/obj/invalid_multiple_dereferences.zig @@ -11,7 +11,9 @@ pub const Box = struct { field: i32, }; -// invalid multiple dereferences +// error +// backend=stage1 +// target=native // // tmp.zig:3:8: error: attempt to dereference non-pointer type 'Box' // tmp.zig:8:13: error: attempt to dereference non-pointer type 'Box' diff --git a/test/compile_errors/stage1/obj/invalid_optional_type_in_extern_struct.zig b/test/compile_errors/stage1/obj/invalid_optional_type_in_extern_struct.zig index 17d9477abf..dc2e2690b8 100644 --- a/test/compile_errors/stage1/obj/invalid_optional_type_in_extern_struct.zig +++ b/test/compile_errors/stage1/obj/invalid_optional_type_in_extern_struct.zig @@ -3,6 +3,8 @@ const stroo = extern struct { }; export fn testf(fluff: *stroo) void { _ = fluff; } -// invalid optional type in extern struct +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: extern structs cannot contain fields of type '?[*c]u8' diff --git a/test/compile_errors/stage1/obj/invalid_pointer_for_var_type.zig b/test/compile_errors/stage1/obj/invalid_pointer_for_var_type.zig index 599aef54f2..caf8c70797 100644 --- a/test/compile_errors/stage1/obj/invalid_pointer_for_var_type.zig +++ b/test/compile_errors/stage1/obj/invalid_pointer_for_var_type.zig @@ -6,6 +6,8 @@ export fn f() void { } } -// invalid pointer for var type +// error +// backend=stage1 +// target=native // // tmp.zig:2:13: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/invalid_pointer_syntax.zig b/test/compile_errors/stage1/obj/invalid_pointer_syntax.zig index d4b68200b6..a4c823bde1 100644 --- a/test/compile_errors/stage1/obj/invalid_pointer_syntax.zig +++ b/test/compile_errors/stage1/obj/invalid_pointer_syntax.zig @@ -2,6 +2,8 @@ export fn foo() void { var guid: *:0 const u8 = undefined; } -// invalid pointer syntax +// error +// backend=stage1 +// target=native // // tmp.zig:2:16: error: expected type expression, found ':' diff --git a/test/compile_errors/stage1/obj/invalid_shift_amount_error.zig b/test/compile_errors/stage1/obj/invalid_shift_amount_error.zig index f4fe1cb2ff..7c84f2db38 100644 --- a/test/compile_errors/stage1/obj/invalid_shift_amount_error.zig +++ b/test/compile_errors/stage1/obj/invalid_shift_amount_error.zig @@ -4,6 +4,8 @@ fn f() u16 { } export fn entry() u16 { return f(); } -// invalid shift amount error +// error +// backend=stage1 +// target=native // // tmp.zig:3:17: error: integer value 8 cannot be coerced to type 'u3' diff --git a/test/compile_errors/stage1/obj/invalid_struct_field.zig b/test/compile_errors/stage1/obj/invalid_struct_field.zig index 2d85914907..6264125bca 100644 --- a/test/compile_errors/stage1/obj/invalid_struct_field.zig +++ b/test/compile_errors/stage1/obj/invalid_struct_field.zig @@ -11,7 +11,9 @@ export fn g() void { _ = y; } -// invalid struct field +// error +// backend=stage1 +// target=native // // tmp.zig:4:6: error: no member named 'foo' in struct 'A' // tmp.zig:10:16: error: no member named 'bar' in struct 'A' diff --git a/test/compile_errors/stage1/obj/invalid_suspend_in_exported_function.zig b/test/compile_errors/stage1/obj/invalid_suspend_in_exported_function.zig index da808563a5..cfcbfba889 100644 --- a/test/compile_errors/stage1/obj/invalid_suspend_in_exported_function.zig +++ b/test/compile_errors/stage1/obj/invalid_suspend_in_exported_function.zig @@ -7,7 +7,9 @@ fn func() void { suspend {} } -// invalid suspend in exported function +// error +// backend=stage1 +// target=native // // tmp.zig:1:1: error: function with calling convention 'C' cannot be async // tmp.zig:3:18: note: await here is a suspend point diff --git a/test/compile_errors/stage1/obj/invalid_type.zig b/test/compile_errors/stage1/obj/invalid_type.zig index 0307300517..8a2952e5f2 100644 --- a/test/compile_errors/stage1/obj/invalid_type.zig +++ b/test/compile_errors/stage1/obj/invalid_type.zig @@ -1,6 +1,8 @@ fn a() bogus {} export fn entry() void { _ = a(); } -// invalid type +// error +// backend=stage1 +// target=native // // tmp.zig:1:8: error: use of undeclared identifier 'bogus' diff --git a/test/compile_errors/stage1/obj/invalid_type_used_in_array_type.zig b/test/compile_errors/stage1/obj/invalid_type_used_in_array_type.zig index 26f0e6a7db..3a7bc4272c 100644 --- a/test/compile_errors/stage1/obj/invalid_type_used_in_array_type.zig +++ b/test/compile_errors/stage1/obj/invalid_type_used_in_array_type.zig @@ -7,6 +7,8 @@ export fn entry() void { _ = a; } -// invalid type used in array type +// error +// backend=stage1 +// target=native // // tmp.zig:2:12: error: use of undeclared identifier 'SomeNonexistentType' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-1.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-1.zig index 5cea540104..a3e2827f31 100644 --- a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-1.zig +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-1.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid underscore placement in float literal - 1 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:23: note: invalid byte: '_' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-10.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-10.zig index d789579a2b..7d33244483 100644 --- a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-10.zig +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-10.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid underscore placement in float literal - 10 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:25: note: invalid byte: '_' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-11.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-11.zig index 197b114090..fa4f5b8975 100644 --- a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-11.zig +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-11.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid underscore placement in float literal - 11 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:28: note: invalid byte: '_' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-12.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-12.zig index 1d25c9f4b4..2d36187711 100644 --- a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-12.zig +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-12.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid underscore placement in float literal - 12 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:23: note: invalid byte: 'x' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-13.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-13.zig index c3de75ed32..21b044121c 100644 --- a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-13.zig +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-13.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid underscore placement in float literal - 13 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:23: note: invalid byte: '_' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-14.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-14.zig index cbb967e926..adb0727eba 100644 --- a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-14.zig +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-14.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid underscore placement in float literal - 14 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:27: note: invalid byte: 'p' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-2.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-2.zig index e83c1b420b..16618c6a18 100644 --- a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-2.zig +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-2.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid underscore placement in float literal - 2 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:23: note: invalid byte: '.' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-3.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-3.zig index f9ca8e0d7c..09e9e20b4b 100644 --- a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-3.zig +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-3.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid underscore placement in float literal - 3 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:25: note: invalid byte: ';' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-4.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-4.zig index 20e8d3692e..278ee35f5f 100644 --- a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-4.zig +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-4.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid underscore placement in float literal - 4 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:25: note: invalid byte: '_' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-5.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-5.zig index 3719f54e06..4912c15e50 100644 --- a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-5.zig +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-5.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid underscore placement in float literal - 5 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:26: note: invalid byte: '_' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-6.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-6.zig index 64a439538a..3386c860b9 100644 --- a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-6.zig +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-6.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid underscore placement in float literal - 6 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:26: note: invalid byte: '_' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-7.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-7.zig index 311b27339a..c0e57424a9 100644 --- a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-7.zig +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-7.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid underscore placement in float literal - 7 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:28: note: invalid byte: ';' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-9.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-9.zig index ecd1149f22..f7475332c1 100644 --- a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-9.zig +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-9.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid underscore placement in float literal - 9 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:23: note: invalid byte: '_' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-1.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-1.zig index 47da090745..9cea337db4 100644 --- a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-1.zig +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-1.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid underscore placement in int literal - 1 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:26: note: invalid byte: ';' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-2.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-2.zig index bb230daa9e..5f02479f2e 100644 --- a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-2.zig +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-2.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid underscore placement in int literal - 2 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:28: note: invalid byte: ';' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-3.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-3.zig index 7808b6ee4d..aed4cb9068 100644 --- a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-3.zig +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-3.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid underscore placement in int literal - 3 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:28: note: invalid byte: ';' diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-4.zig b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-4.zig index 51ddb3d061..dce2771048 100644 --- a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-4.zig +++ b/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-4.zig @@ -3,7 +3,9 @@ fn main() void { _ = bad; } -// invalid underscore placement in int literal - 4 +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected expression, found 'invalid bytes' // tmp.zig:2:28: note: invalid byte: ';' diff --git a/test/compile_errors/stage1/obj/invalid_union_field_access_in_comptime.zig b/test/compile_errors/stage1/obj/invalid_union_field_access_in_comptime.zig index d570dca0b9..6d55f3b04f 100644 --- a/test/compile_errors/stage1/obj/invalid_union_field_access_in_comptime.zig +++ b/test/compile_errors/stage1/obj/invalid_union_field_access_in_comptime.zig @@ -8,6 +8,8 @@ comptime { _ = bar_val; } -// invalid union field access in comptime +// error +// backend=stage1 +// target=native // // tmp.zig:7:24: error: accessing union field 'Bar' while field 'Baz' is set diff --git a/test/compile_errors/stage1/obj/issue_2032_compile_diagnostic_string_for_top_level_decl_type.zig b/test/compile_errors/stage1/obj/issue_2032_compile_diagnostic_string_for_top_level_decl_type.zig index 9e353a4d38..c742cb9b77 100644 --- a/test/compile_errors/stage1/obj/issue_2032_compile_diagnostic_string_for_top_level_decl_type.zig +++ b/test/compile_errors/stage1/obj/issue_2032_compile_diagnostic_string_for_top_level_decl_type.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = foo; } -// compile diagnostic string for top level decl type (issue 2032) +// error +// backend=stage1 +// target=native // // tmp.zig:2:27: error: type 'u32' does not support array initialization diff --git a/test/compile_errors/stage1/obj/issue_2687_coerce_from_undefined_array_pointer_to_slice.zig b/test/compile_errors/stage1/obj/issue_2687_coerce_from_undefined_array_pointer_to_slice.zig index 7c125b85ce..21fbb6b4fe 100644 --- a/test/compile_errors/stage1/obj/issue_2687_coerce_from_undefined_array_pointer_to_slice.zig +++ b/test/compile_errors/stage1/obj/issue_2687_coerce_from_undefined_array_pointer_to_slice.zig @@ -18,7 +18,9 @@ export fn foo3() void { } } -// issue #2687: coerce from undefined array pointer to slice +// error +// backend=stage1 +// target=native // // tmp.zig:3:19: error: use of undefined value here causes undefined behavior // tmp.zig:9:23: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig b/test/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig index 1c9d9cd1b1..cdc1def677 100644 --- a/test/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig +++ b/test/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig @@ -9,7 +9,9 @@ export fn foo2() void { _ = word; } -// issue #3818: bitcast from parray/slice to u16 +// error +// backend=stage1 +// target=native // // tmp.zig:3:42: error: unable to @bitCast from pointer type '*[2]u8' // tmp.zig:8:32: error: destination type 'u16' has size 2 but source type '[]const u8' has size 16 diff --git a/test/compile_errors/stage1/obj/issue_4207_coerce_from_non-terminated-slice_to_terminated-pointer.zig b/test/compile_errors/stage1/obj/issue_4207_coerce_from_non-terminated-slice_to_terminated-pointer.zig index bfb0595cce..d53f7731a0 100644 --- a/test/compile_errors/stage1/obj/issue_4207_coerce_from_non-terminated-slice_to_terminated-pointer.zig +++ b/test/compile_errors/stage1/obj/issue_4207_coerce_from_non-terminated-slice_to_terminated-pointer.zig @@ -3,7 +3,9 @@ export fn foo() [*:0]const u8 { return buffer[0..]; } -// issue #4207: coerce from non-terminated-slice to terminated-pointer +// error +// backend=stage1 +// target=native // // :3:18: error: expected type '[*:0]const u8', found '*[64]u8' // :3:18: note: destination pointer requires a terminating '0' sentinel diff --git a/test/compile_errors/stage1/obj/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig b/test/compile_errors/stage1/obj/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig index aa839bc72c..a622ceb44e 100644 --- a/test/compile_errors/stage1/obj/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig +++ b/test/compile_errors/stage1/obj/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig @@ -8,7 +8,9 @@ export fn foo() void { comptime ignore(@typeInfo(MyStruct).Struct.fields[0]); } -// issue #5221: invalid struct init type referenced by @typeInfo and passed into function +// error +// backend=stage1 +// target=native // // :5:28: error: cannot cast pointer to array literal to slice type '[]u8' // :5:28: note: cast discards const qualifier diff --git a/test/compile_errors/stage1/obj/issue_5618_coercion_of_optional_anyopaque_to_anyopaque_must_fail.zig b/test/compile_errors/stage1/obj/issue_5618_coercion_of_optional_anyopaque_to_anyopaque_must_fail.zig index daa8a91f40..9ba9910838 100644 --- a/test/compile_errors/stage1/obj/issue_5618_coercion_of_optional_anyopaque_to_anyopaque_must_fail.zig +++ b/test/compile_errors/stage1/obj/issue_5618_coercion_of_optional_anyopaque_to_anyopaque_must_fail.zig @@ -4,6 +4,8 @@ export fn foo() void { v = u; } -// Issue #5618: coercion of ?*anyopaque to *anyopaque must fail. +// error +// backend=stage1 +// target=native // // tmp.zig:4:9: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type '*anyopaque', found '?*anyopaque' diff --git a/test/compile_errors/stage1/obj/issue_7810-comptime_slice-len_increment_beyond_bounds.zig b/test/compile_errors/stage1/obj/issue_7810-comptime_slice-len_increment_beyond_bounds.zig index 533ac928b4..c8b2f0b09e 100644 --- a/test/compile_errors/stage1/obj/issue_7810-comptime_slice-len_increment_beyond_bounds.zig +++ b/test/compile_errors/stage1/obj/issue_7810-comptime_slice-len_increment_beyond_bounds.zig @@ -7,6 +7,8 @@ export fn foo_slice_len_increment_beyond_bounds() void { } } -// comptime slice-len increment beyond bounds +// error +// backend=stage1 +// target=native // // :6:12: error: out of bounds slice diff --git a/test/compile_errors/stage1/obj/issue_9346_return_outside_of_function_scope.zig b/test/compile_errors/stage1/obj/issue_9346_return_outside_of_function_scope.zig index 6be92e2ee8..d12bff2b49 100644 --- a/test/compile_errors/stage1/obj/issue_9346_return_outside_of_function_scope.zig +++ b/test/compile_errors/stage1/obj/issue_9346_return_outside_of_function_scope.zig @@ -1,5 +1,7 @@ pub const empty = return 1; -// issue #9346: return outside of function scope +// error +// backend=stage1 +// target=native // // tmp.zig:1:19: error: 'return' outside function scope diff --git a/test/compile_errors/stage1/obj/labeled_break_not_found.zig b/test/compile_errors/stage1/obj/labeled_break_not_found.zig index 0f4632d74d..754e1864b0 100644 --- a/test/compile_errors/stage1/obj/labeled_break_not_found.zig +++ b/test/compile_errors/stage1/obj/labeled_break_not_found.zig @@ -6,6 +6,8 @@ export fn entry() void { } } -// labeled break not found +// error +// backend=stage1 +// target=native // // tmp.zig:4:20: error: label not found: 'outer' diff --git a/test/compile_errors/stage1/obj/labeled_continue_not_found.zig b/test/compile_errors/stage1/obj/labeled_continue_not_found.zig index 8f95a0fce1..b112e9b355 100644 --- a/test/compile_errors/stage1/obj/labeled_continue_not_found.zig +++ b/test/compile_errors/stage1/obj/labeled_continue_not_found.zig @@ -7,6 +7,8 @@ export fn entry() void { } } -// labeled continue not found +// error +// backend=stage1 +// target=native // // tmp.zig:5:23: error: label not found: 'outer' diff --git a/test/compile_errors/stage1/obj/lazy_pointer_with_undefined_element_type.zig b/test/compile_errors/stage1/obj/lazy_pointer_with_undefined_element_type.zig index 47b271a05e..733a3ca2dd 100644 --- a/test/compile_errors/stage1/obj/lazy_pointer_with_undefined_element_type.zig +++ b/test/compile_errors/stage1/obj/lazy_pointer_with_undefined_element_type.zig @@ -5,6 +5,8 @@ export fn foo() void { _ = I; } -// lazy pointer with undefined element type +// error +// backend=stage1 +// target=native // // :3:28: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/libc_headers_note.zig b/test/compile_errors/stage1/obj/libc_headers_note.zig new file mode 100644 index 0000000000..c8d9a9cbf3 --- /dev/null +++ b/test/compile_errors/stage1/obj/libc_headers_note.zig @@ -0,0 +1,12 @@ +const c = @cImport(@cInclude("stdio.h")); +export fn entry() void { + _ = c.printf("hello, world!\n"); +} + +// error +// backend=stage1 +// is_test=1 +// target=native-linux +// +// tmp.zig:1:11: error: C import failed +// tmp.zig:1:11: note: libc headers not available; compilation does not link against libc diff --git a/test/compile_errors/stage1/obj/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig b/test/compile_errors/stage1/obj/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig index 2c5e1c7327..b275dbbeed 100644 --- a/test/compile_errors/stage1/obj/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig +++ b/test/compile_errors/stage1/obj/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig @@ -6,6 +6,8 @@ export fn entry() void { _ = int_val; } -// load too many bytes from comptime reinterpreted pointer +// error +// backend=stage1 +// target=native // // tmp.zig:5:28: error: attempt to read 8 bytes from pointer to f32 which is 4 bytes diff --git a/test/compile_errors/stage1/obj/load_vector_pointer_with_unknown_runtime_index.zig b/test/compile_errors/stage1/obj/load_vector_pointer_with_unknown_runtime_index.zig index fe28fa9102..8d703b09e6 100644 --- a/test/compile_errors/stage1/obj/load_vector_pointer_with_unknown_runtime_index.zig +++ b/test/compile_errors/stage1/obj/load_vector_pointer_with_unknown_runtime_index.zig @@ -10,6 +10,8 @@ fn loadv(ptr: anytype) i32 { return ptr.*; } -// load vector pointer with unknown runtime index +// error +// backend=stage1 +// target=native // // tmp.zig:10:12: error: unable to determine vector element index of type '*align(16:0:4:?) i32 diff --git a/test/compile_errors/stage1/obj/local_shadows_global_that_occurs_later.zig b/test/compile_errors/stage1/obj/local_shadows_global_that_occurs_later.zig index 2265a34f18..4b9b58871f 100644 --- a/test/compile_errors/stage1/obj/local_shadows_global_that_occurs_later.zig +++ b/test/compile_errors/stage1/obj/local_shadows_global_that_occurs_later.zig @@ -4,7 +4,9 @@ pub fn main() void { } fn foo() void {} -// local shadows global that occurs later +// error +// backend=stage1 +// target=native // // tmp.zig:2:9: error: local shadows declaration of 'foo' // tmp.zig:5:1: note: declared here diff --git a/test/compile_errors/stage1/obj/local_variable_redeclaration.zig b/test/compile_errors/stage1/obj/local_variable_redeclaration.zig index 2367963edb..79eba0d428 100644 --- a/test/compile_errors/stage1/obj/local_variable_redeclaration.zig +++ b/test/compile_errors/stage1/obj/local_variable_redeclaration.zig @@ -3,7 +3,9 @@ export fn f() void { var a = 0; } -// local variable redeclaration +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: redeclaration of local constant 'a' // tmp.zig:2:11: note: previous declaration here diff --git a/test/compile_errors/stage1/obj/local_variable_redeclares_parameter.zig b/test/compile_errors/stage1/obj/local_variable_redeclares_parameter.zig index e624e14e31..854ef080c5 100644 --- a/test/compile_errors/stage1/obj/local_variable_redeclares_parameter.zig +++ b/test/compile_errors/stage1/obj/local_variable_redeclares_parameter.zig @@ -3,7 +3,9 @@ fn f(a : i32) void { } export fn entry() void { f(1); } -// local variable redeclares parameter +// error +// backend=stage1 +// target=native // // tmp.zig:2:11: error: redeclaration of function parameter 'a' // tmp.zig:1:6: note: previous declaration here diff --git a/test/compile_errors/stage1/obj/local_variable_shadowing_global.zig b/test/compile_errors/stage1/obj/local_variable_shadowing_global.zig index c824cb3df7..3f1af3a4cb 100644 --- a/test/compile_errors/stage1/obj/local_variable_shadowing_global.zig +++ b/test/compile_errors/stage1/obj/local_variable_shadowing_global.zig @@ -6,7 +6,9 @@ export fn entry() void { _ = Bar; } -// local variable shadowing global +// error +// backend=stage1 +// target=native // // tmp.zig:5:9: error: local shadows declaration of 'Bar' // tmp.zig:2:1: note: declared here diff --git a/test/compile_errors/stage1/obj/locally_shadowing_a_primitive_type.zig b/test/compile_errors/stage1/obj/locally_shadowing_a_primitive_type.zig index 457cea418c..e66a2082ea 100644 --- a/test/compile_errors/stage1/obj/locally_shadowing_a_primitive_type.zig +++ b/test/compile_errors/stage1/obj/locally_shadowing_a_primitive_type.zig @@ -4,7 +4,9 @@ export fn foo() void { _ = a; } -// locally shadowing a primitive type +// error +// backend=stage1 +// target=native // // tmp.zig:2:11: error: name shadows primitive 'u8' // tmp.zig:2:11: note: consider using @"u8" to disambiguate diff --git a/test/compile_errors/stage1/obj/main_function_with_bogus_args_type.zig b/test/compile_errors/stage1/obj/main_function_with_bogus_args_type.zig index 3954c4b846..5808ab90ec 100644 --- a/test/compile_errors/stage1/obj/main_function_with_bogus_args_type.zig +++ b/test/compile_errors/stage1/obj/main_function_with_bogus_args_type.zig @@ -1,5 +1,7 @@ pub fn main(args: [][]bogus) !void {_ = args;} -// main function with bogus args type +// error +// backend=stage1 +// target=native // // tmp.zig:1:23: error: use of undeclared identifier 'bogus' diff --git a/test/compile_errors/stage1/obj/method_call_with_first_arg_type_primitive.zig b/test/compile_errors/stage1/obj/method_call_with_first_arg_type_primitive.zig index 9763e84fba..316be2d899 100644 --- a/test/compile_errors/stage1/obj/method_call_with_first_arg_type_primitive.zig +++ b/test/compile_errors/stage1/obj/method_call_with_first_arg_type_primitive.zig @@ -14,6 +14,8 @@ export fn f() void { derp.init(); } -// method call with first arg type primitive +// error +// backend=stage1 +// target=native // // tmp.zig:14:5: error: expected type 'i32', found 'Foo' diff --git a/test/compile_errors/stage1/obj/method_call_with_first_arg_type_wrong_container.zig b/test/compile_errors/stage1/obj/method_call_with_first_arg_type_wrong_container.zig index ccbc7e32e3..04ab89fb6c 100644 --- a/test/compile_errors/stage1/obj/method_call_with_first_arg_type_wrong_container.zig +++ b/test/compile_errors/stage1/obj/method_call_with_first_arg_type_wrong_container.zig @@ -23,6 +23,8 @@ export fn foo() void { x.init(); } -// method call with first arg type wrong container +// error +// backend=stage1 +// target=native // // tmp.zig:23:5: error: expected type '*Allocator', found '*List' diff --git a/test/compile_errors/stage1/obj/missing_boolean_switch_value.zig b/test/compile_errors/stage1/obj/missing_boolean_switch_value.zig index 63a12c4e6f..2e9d4b48ce 100644 --- a/test/compile_errors/stage1/obj/missing_boolean_switch_value.zig +++ b/test/compile_errors/stage1/obj/missing_boolean_switch_value.zig @@ -11,7 +11,9 @@ comptime { _ = x; } -// missing boolean switch value +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: switch must handle all possibilities // tmp.zig:8:15: error: switch must handle all possibilities diff --git a/test/compile_errors/stage1/obj/missing_const_in_slice_with_nested_array_type.zig b/test/compile_errors/stage1/obj/missing_const_in_slice_with_nested_array_type.zig index 4f552284d7..af9d690d43 100644 --- a/test/compile_errors/stage1/obj/missing_const_in_slice_with_nested_array_type.zig +++ b/test/compile_errors/stage1/obj/missing_const_in_slice_with_nested_array_type.zig @@ -11,6 +11,8 @@ export fn entry() void { _ = geo_data; } -// missing const in slice with nested array type +// error +// backend=stage1 +// target=native // // tmp.zig:4:30: error: array literal requires address-of operator (&) to coerce to slice type '[][2]f32' diff --git a/test/compile_errors/stage1/obj/missing_else_clause.zig b/test/compile_errors/stage1/obj/missing_else_clause.zig index 28adfd21e8..f331933437 100644 --- a/test/compile_errors/stage1/obj/missing_else_clause.zig +++ b/test/compile_errors/stage1/obj/missing_else_clause.zig @@ -8,7 +8,9 @@ fn g(b: bool) void { } export fn entry() void { f(true); g(true); } -// missing else clause +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected type 'i32', found 'void' // tmp.zig:6:15: error: incompatible types: 'i32' and 'void' diff --git a/test/compile_errors/stage1/obj/missing_field_in_struct_value_expression.zig b/test/compile_errors/stage1/obj/missing_field_in_struct_value_expression.zig index bea4021a5a..9eb4246c8f 100644 --- a/test/compile_errors/stage1/obj/missing_field_in_struct_value_expression.zig +++ b/test/compile_errors/stage1/obj/missing_field_in_struct_value_expression.zig @@ -13,6 +13,8 @@ export fn f() void { _ = a; } -// missing field in struct value expression +// error +// backend=stage1 +// target=native // // tmp.zig:9:17: error: missing field: 'x' diff --git a/test/compile_errors/stage1/obj/missing_function_call_param.zig b/test/compile_errors/stage1/obj/missing_function_call_param.zig index 45a7259814..9e08ed7140 100644 --- a/test/compile_errors/stage1/obj/missing_function_call_param.zig +++ b/test/compile_errors/stage1/obj/missing_function_call_param.zig @@ -24,6 +24,8 @@ fn f(foo: *const Foo, index: usize) void { export fn entry() usize { return @sizeOf(@TypeOf(f)); } -// missing function call param +// error +// backend=stage1 +// target=native // // tmp.zig:20:34: error: expected 1 argument(s), found 0 diff --git a/test/compile_errors/stage1/obj/missing_function_name.zig b/test/compile_errors/stage1/obj/missing_function_name.zig index 194f5eb6a8..cd9a3ca7ee 100644 --- a/test/compile_errors/stage1/obj/missing_function_name.zig +++ b/test/compile_errors/stage1/obj/missing_function_name.zig @@ -1,6 +1,8 @@ fn () void {} export fn entry() usize { return @sizeOf(@TypeOf(f)); } -// missing function name +// error +// backend=stage1 +// target=native // // tmp.zig:1:1: error: missing function name diff --git a/test/compile_errors/stage1/obj/missing_param_name.zig b/test/compile_errors/stage1/obj/missing_param_name.zig index 1c379dd245..febb16d2ca 100644 --- a/test/compile_errors/stage1/obj/missing_param_name.zig +++ b/test/compile_errors/stage1/obj/missing_param_name.zig @@ -1,6 +1,8 @@ fn f(i32) void {} export fn entry() usize { return @sizeOf(@TypeOf(f)); } -// missing param name +// error +// backend=stage1 +// target=native // // tmp.zig:1:6: error: missing parameter name diff --git a/test/compile_errors/stage1/obj/missing_parameter_name_of_generic_function.zig b/test/compile_errors/stage1/obj/missing_parameter_name_of_generic_function.zig index 3a3ccd28a8..041c0c4a47 100644 --- a/test/compile_errors/stage1/obj/missing_parameter_name_of_generic_function.zig +++ b/test/compile_errors/stage1/obj/missing_parameter_name_of_generic_function.zig @@ -4,6 +4,8 @@ export fn entry() void { dump(a); } -// missing parameter name of generic function +// error +// backend=stage1 +// target=native // // tmp.zig:1:9: error: missing parameter name diff --git a/test/compile_errors/stage1/obj/missing_result_type_for_phi_node.zig b/test/compile_errors/stage1/obj/missing_result_type_for_phi_node.zig index 3ef99e0859..33bc1d0d9c 100644 --- a/test/compile_errors/stage1/obj/missing_result_type_for_phi_node.zig +++ b/test/compile_errors/stage1/obj/missing_result_type_for_phi_node.zig @@ -5,6 +5,8 @@ export fn entry() void { foo() catch 0; } -// missing result type for phi node +// error +// backend=stage1 +// target=native // // tmp.zig:5:17: error: integer value 0 cannot be coerced to type 'void' diff --git a/test/compile_errors/stage1/obj/misspelled_type_with_pointer_only_reference.zig b/test/compile_errors/stage1/obj/misspelled_type_with_pointer_only_reference.zig index 6b3f647c02..8e2055a829 100644 --- a/test/compile_errors/stage1/obj/misspelled_type_with_pointer_only_reference.zig +++ b/test/compile_errors/stage1/obj/misspelled_type_with_pointer_only_reference.zig @@ -30,6 +30,8 @@ fn foo() void { export fn entry() usize { return @sizeOf(@TypeOf(foo)); } -// misspelled type with pointer only reference +// error +// backend=stage1 +// target=native // // tmp.zig:5:16: error: use of undeclared identifier 'JsonList' diff --git a/test/compile_errors/stage1/obj/mod_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/mod_assign_on_undefined_value.zig index 3165970d44..c127e256e8 100644 --- a/test/compile_errors/stage1/obj/mod_assign_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/mod_assign_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { a %= a; } -// mod assign on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/mod_on_undefined_value.zig b/test/compile_errors/stage1/obj/mod_on_undefined_value.zig index 3b33f6ed82..8bd7774a46 100644 --- a/test/compile_errors/stage1/obj/mod_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/mod_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = a % a; } -// mod on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/mul_overflow_in_function_evaluation.zig b/test/compile_errors/stage1/obj/mul_overflow_in_function_evaluation.zig index ea8eea5ab5..8209b33f94 100644 --- a/test/compile_errors/stage1/obj/mul_overflow_in_function_evaluation.zig +++ b/test/compile_errors/stage1/obj/mul_overflow_in_function_evaluation.zig @@ -5,6 +5,8 @@ fn mul(a: u16, b: u16) u16 { export fn entry() usize { return @sizeOf(@TypeOf(y)); } -// mul overflow in function evaluation +// error +// backend=stage1 +// target=native // // tmp.zig:3:14: error: operation caused overflow diff --git a/test/compile_errors/stage1/obj/mult_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/mult_assign_on_undefined_value.zig index 4617c5f62b..04040b9756 100644 --- a/test/compile_errors/stage1/obj/mult_assign_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/mult_assign_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { a *= a; } -// mult assign on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/mult_on_undefined_value.zig b/test/compile_errors/stage1/obj/mult_on_undefined_value.zig index 836b59e19d..0dcd2b02f2 100644 --- a/test/compile_errors/stage1/obj/mult_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/mult_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = a * a; } -// mult on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/mult_wrap_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/mult_wrap_assign_on_undefined_value.zig index 38bd38bf79..db54af9f0b 100644 --- a/test/compile_errors/stage1/obj/mult_wrap_assign_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/mult_wrap_assign_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { a *%= a; } -// mult wrap assign on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/mult_wrap_on_undefined_value.zig b/test/compile_errors/stage1/obj/mult_wrap_on_undefined_value.zig index 0c0ca46540..9ad09f7e93 100644 --- a/test/compile_errors/stage1/obj/mult_wrap_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/mult_wrap_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = a *% a; } -// mult wrap on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/multiple_function_definitions.zig b/test/compile_errors/stage1/obj/multiple_function_definitions.zig index c4efefce8d..baa4a43048 100644 --- a/test/compile_errors/stage1/obj/multiple_function_definitions.zig +++ b/test/compile_errors/stage1/obj/multiple_function_definitions.zig @@ -2,7 +2,9 @@ fn a() void {} fn a() void {} export fn entry() void { a(); } -// multiple function definitions +// error +// backend=stage1 +// target=native // // tmp.zig:2:1: error: redeclaration of 'a' // tmp.zig:1:1: note: other declaration here diff --git a/test/compile_errors/stage1/obj/negate_on_undefined_value.zig b/test/compile_errors/stage1/obj/negate_on_undefined_value.zig index 7e7e392104..f8d5f6128d 100644 --- a/test/compile_errors/stage1/obj/negate_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/negate_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = -a; } -// negate on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:10: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/negate_wrap_on_undefined_value.zig b/test/compile_errors/stage1/obj/negate_wrap_on_undefined_value.zig index f49c6eac34..4ca32956de 100644 --- a/test/compile_errors/stage1/obj/negate_wrap_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/negate_wrap_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = -%a; } -// negate wrap on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:11: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/negation_overflow_in_function_evaluation.zig b/test/compile_errors/stage1/obj/negation_overflow_in_function_evaluation.zig index ca69b6fb5d..f83e0de6ae 100644 --- a/test/compile_errors/stage1/obj/negation_overflow_in_function_evaluation.zig +++ b/test/compile_errors/stage1/obj/negation_overflow_in_function_evaluation.zig @@ -5,6 +5,8 @@ fn neg(x: i8) i8 { export fn entry() usize { return @sizeOf(@TypeOf(y)); } -// negation overflow in function evaluation +// error +// backend=stage1 +// target=native // // tmp.zig:3:12: error: negation caused overflow diff --git a/test/compile_errors/stage1/obj/nested_error_set_mismatch.zig b/test/compile_errors/stage1/obj/nested_error_set_mismatch.zig index 6c677049df..cd037635bc 100644 --- a/test/compile_errors/stage1/obj/nested_error_set_mismatch.zig +++ b/test/compile_errors/stage1/obj/nested_error_set_mismatch.zig @@ -10,7 +10,9 @@ fn foo() ?OtherError!i32 { return null; } -// nested error set mismatch +// error +// backend=stage1 +// target=native // // tmp.zig:5:34: error: expected type '?NextError!i32', found '?OtherError!i32' // tmp.zig:5:34: note: optional type child 'OtherError!i32' cannot cast into optional type child 'NextError!i32' diff --git a/test/compile_errors/stage1/obj/no_else_prong_on_switch_on_global_error_set.zig b/test/compile_errors/stage1/obj/no_else_prong_on_switch_on_global_error_set.zig index 7de3f189f0..39f4399caf 100644 --- a/test/compile_errors/stage1/obj/no_else_prong_on_switch_on_global_error_set.zig +++ b/test/compile_errors/stage1/obj/no_else_prong_on_switch_on_global_error_set.zig @@ -7,6 +7,8 @@ fn foo(a: anyerror) void { } } -// no else prong on switch on global error set +// error +// backend=stage1 +// target=native // // tmp.zig:5:5: error: else prong required when switching on type 'anyerror' diff --git a/test/compile_errors/stage1/obj/noalias_on_non_pointer_param.zig b/test/compile_errors/stage1/obj/noalias_on_non_pointer_param.zig index 3419dc8c99..ca84aca73f 100644 --- a/test/compile_errors/stage1/obj/noalias_on_non_pointer_param.zig +++ b/test/compile_errors/stage1/obj/noalias_on_non_pointer_param.zig @@ -1,6 +1,8 @@ fn f(noalias x: i32) void { _ = x; } export fn entry() void { f(1234); } -// noalias on non pointer param +// error +// backend=stage1 +// target=native // // tmp.zig:1:6: error: noalias on non-pointer parameter diff --git a/test/compile_errors/stage1/obj/non-async_function_pointer_eventually_is_inferred_to_become_async.zig b/test/compile_errors/stage1/obj/non-async_function_pointer_eventually_is_inferred_to_become_async.zig index 93391daaef..e18b420028 100644 --- a/test/compile_errors/stage1/obj/non-async_function_pointer_eventually_is_inferred_to_become_async.zig +++ b/test/compile_errors/stage1/obj/non-async_function_pointer_eventually_is_inferred_to_become_async.zig @@ -6,7 +6,9 @@ fn func() void { suspend {} } -// non-async function pointer eventually is inferred to become async +// error +// backend=stage1 +// target=native // // tmp.zig:5:1: error: 'func' cannot be async // tmp.zig:3:20: note: required to be non-async here diff --git a/test/compile_errors/stage1/obj/non-const_expression_function_call_with_struct_return_value_outside_function.zig b/test/compile_errors/stage1/obj/non-const_expression_function_call_with_struct_return_value_outside_function.zig index d6a463e71f..25e33d1435 100644 --- a/test/compile_errors/stage1/obj/non-const_expression_function_call_with_struct_return_value_outside_function.zig +++ b/test/compile_errors/stage1/obj/non-const_expression_function_call_with_struct_return_value_outside_function.zig @@ -10,6 +10,8 @@ var global_side_effect = false; export fn entry() usize { return @sizeOf(@TypeOf(a)); } -// non-const expression function call with struct return value outside function +// error +// backend=stage1 +// target=native // // tmp.zig:6:26: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/non-const_expression_in_struct_literal_outside_function.zig b/test/compile_errors/stage1/obj/non-const_expression_in_struct_literal_outside_function.zig index ef7b1309c3..46b94f1f96 100644 --- a/test/compile_errors/stage1/obj/non-const_expression_in_struct_literal_outside_function.zig +++ b/test/compile_errors/stage1/obj/non-const_expression_in_struct_literal_outside_function.zig @@ -6,6 +6,8 @@ extern fn get_it() i32; export fn entry() usize { return @sizeOf(@TypeOf(a)); } -// non-const expression in struct literal outside function +// error +// backend=stage1 +// target=native // // tmp.zig:4:21: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/non-const_switch_number_literal.zig b/test/compile_errors/stage1/obj/non-const_switch_number_literal.zig index aecaa7dcb6..005db548ed 100644 --- a/test/compile_errors/stage1/obj/non-const_switch_number_literal.zig +++ b/test/compile_errors/stage1/obj/non-const_switch_number_literal.zig @@ -10,6 +10,8 @@ fn bar() i32 { return 2; } -// non-const switch number literal +// error +// backend=stage1 +// target=native // // tmp.zig:5:17: error: cannot store runtime value in type 'comptime_int' diff --git a/test/compile_errors/stage1/obj/non-const_variables_of_things_that_require_const_variables.zig b/test/compile_errors/stage1/obj/non-const_variables_of_things_that_require_const_variables.zig index 0598e04e4b..73ef29aa54 100644 --- a/test/compile_errors/stage1/obj/non-const_variables_of_things_that_require_const_variables.zig +++ b/test/compile_errors/stage1/obj/non-const_variables_of_things_that_require_const_variables.zig @@ -35,7 +35,9 @@ const Foo = struct { fn bar(self: *const Foo) void {_ = self;} }; -// non-const variables of things that require const variables +// error +// backend=stage1 +// target=native // // tmp.zig:2:4: error: variable of type '*const comptime_int' must be const or comptime // tmp.zig:6:4: error: variable of type '@Type(.Undefined)' must be const or comptime diff --git a/test/compile_errors/stage1/obj/non-enum_tag_type_passed_to_union.zig b/test/compile_errors/stage1/obj/non-enum_tag_type_passed_to_union.zig index 3161f16b28..e188a3f819 100644 --- a/test/compile_errors/stage1/obj/non-enum_tag_type_passed_to_union.zig +++ b/test/compile_errors/stage1/obj/non-enum_tag_type_passed_to_union.zig @@ -6,6 +6,8 @@ export fn entry() void { _ = x; } -// non-enum tag type passed to union +// error +// backend=stage1 +// target=native // // tmp.zig:1:19: error: expected enum tag type, found 'u32' diff --git a/test/compile_errors/stage1/obj/non-extern_function_with_var_args.zig b/test/compile_errors/stage1/obj/non-extern_function_with_var_args.zig index 02b99383c1..9e5463d451 100644 --- a/test/compile_errors/stage1/obj/non-extern_function_with_var_args.zig +++ b/test/compile_errors/stage1/obj/non-extern_function_with_var_args.zig @@ -3,6 +3,8 @@ export fn entry() void { foo(); } -// non-extern function with var args +// error +// backend=stage1 +// target=native // // tmp.zig:1:14: error: expected type expression, found '...' diff --git a/test/compile_errors/stage1/obj/non-inline_for_loop_on_a_type_that_requires_comptime.zig b/test/compile_errors/stage1/obj/non-inline_for_loop_on_a_type_that_requires_comptime.zig index 9190e7cea8..d2b4e28ea6 100644 --- a/test/compile_errors/stage1/obj/non-inline_for_loop_on_a_type_that_requires_comptime.zig +++ b/test/compile_errors/stage1/obj/non-inline_for_loop_on_a_type_that_requires_comptime.zig @@ -7,6 +7,8 @@ export fn entry() void { for (xx) |f| { _ = f;} } -// non-inline for loop on a type that requires comptime +// error +// backend=stage1 +// target=native // // tmp.zig:7:5: error: values of type 'Foo' must be comptime known, but index value is runtime known diff --git a/test/compile_errors/stage1/obj/non-integer_tag_type_to_automatic_union_enum.zig b/test/compile_errors/stage1/obj/non-integer_tag_type_to_automatic_union_enum.zig index a84ca36f6c..41d5e14163 100644 --- a/test/compile_errors/stage1/obj/non-integer_tag_type_to_automatic_union_enum.zig +++ b/test/compile_errors/stage1/obj/non-integer_tag_type_to_automatic_union_enum.zig @@ -6,6 +6,8 @@ export fn entry() void { _ = x; } -// non-integer tag type to automatic union enum +// error +// backend=stage1 +// target=native // // tmp.zig:1:24: error: expected integer tag type, found 'f32' diff --git a/test/compile_errors/stage1/obj/non-pure_function_returns_type.zig b/test/compile_errors/stage1/obj/non-pure_function_returns_type.zig index 0925207b9f..9d1c642273 100644 --- a/test/compile_errors/stage1/obj/non-pure_function_returns_type.zig +++ b/test/compile_errors/stage1/obj/non-pure_function_returns_type.zig @@ -17,6 +17,8 @@ export fn function_with_return_type_type() void { list.length = 10; } -// non-pure function returns type +// error +// backend=stage1 +// target=native // // tmp.zig:3:7: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/non_async_function_pointer_passed_to_asyncCall.zig b/test/compile_errors/stage1/obj/non_async_function_pointer_passed_to_asyncCall.zig index 73c4fd96ca..d8ab4087e0 100644 --- a/test/compile_errors/stage1/obj/non_async_function_pointer_passed_to_asyncCall.zig +++ b/test/compile_errors/stage1/obj/non_async_function_pointer_passed_to_asyncCall.zig @@ -5,6 +5,8 @@ export fn entry() void { } fn afunc() void { } -// non async function pointer passed to @asyncCall +// error +// backend=stage1 +// target=native // // tmp.zig:4:32: error: expected async function, found 'fn() void' diff --git a/test/compile_errors/stage1/obj/non_compile_time_array_concatenation.zig b/test/compile_errors/stage1/obj/non_compile_time_array_concatenation.zig index c119cc03f7..e70e6daf32 100644 --- a/test/compile_errors/stage1/obj/non_compile_time_array_concatenation.zig +++ b/test/compile_errors/stage1/obj/non_compile_time_array_concatenation.zig @@ -4,6 +4,8 @@ fn f() []u8 { var s: [10]u8 = undefined; export fn entry() usize { return @sizeOf(@TypeOf(f)); } -// non compile time array concatenation +// error +// backend=stage1 +// target=native // // tmp.zig:2:12: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/non_constant_expression_in_array_size.zig b/test/compile_errors/stage1/obj/non_constant_expression_in_array_size.zig index fe28921265..a0cb8e8ce1 100644 --- a/test/compile_errors/stage1/obj/non_constant_expression_in_array_size.zig +++ b/test/compile_errors/stage1/obj/non_constant_expression_in_array_size.zig @@ -6,7 +6,9 @@ fn get() usize { return global_var; } export fn entry() usize { return @sizeOf(@TypeOf(Foo)); } -// non constant expression in array size +// error +// backend=stage1 +// target=native // // tmp.zig:5:25: error: cannot store runtime value in compile time variable // tmp.zig:2:12: note: called from here diff --git a/test/compile_errors/stage1/obj/non_error_sets_used_in_merge_error_sets_operator.zig b/test/compile_errors/stage1/obj/non_error_sets_used_in_merge_error_sets_operator.zig index c600bea8b0..fc3431820c 100644 --- a/test/compile_errors/stage1/obj/non_error_sets_used_in_merge_error_sets_operator.zig +++ b/test/compile_errors/stage1/obj/non_error_sets_used_in_merge_error_sets_operator.zig @@ -7,7 +7,9 @@ export fn bar() void { _ = Errors; } -// non error sets used in merge error sets operator +// error +// backend=stage1 +// target=native // // tmp.zig:2:20: error: expected error set type, found type 'u8' // tmp.zig:2:23: note: `||` merges error sets; `or` performs boolean OR diff --git a/test/compile_errors/stage1/obj/non_float_passed_to_floatToInt.zig b/test/compile_errors/stage1/obj/non_float_passed_to_floatToInt.zig index cad658ba5a..c91025906a 100644 --- a/test/compile_errors/stage1/obj/non_float_passed_to_floatToInt.zig +++ b/test/compile_errors/stage1/obj/non_float_passed_to_floatToInt.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = x; } -// non float passed to @floatToInt +// error +// backend=stage1 +// target=native // // tmp.zig:2:32: error: expected float type, found 'i32' diff --git a/test/compile_errors/stage1/obj/non_int_passed_to_intToFloat.zig b/test/compile_errors/stage1/obj/non_int_passed_to_intToFloat.zig index 8b985c2b0b..8f6acca51e 100644 --- a/test/compile_errors/stage1/obj/non_int_passed_to_intToFloat.zig +++ b/test/compile_errors/stage1/obj/non_int_passed_to_intToFloat.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = x; } -// non int passed to @intToFloat +// error +// backend=stage1 +// target=native // // tmp.zig:2:32: error: expected int type, found 'comptime_float' diff --git a/test/compile_errors/stage1/obj/non_pointer_given_to_ptrToInt.zig b/test/compile_errors/stage1/obj/non_pointer_given_to_ptrToInt.zig index 1d4d3087da..7824c0f796 100644 --- a/test/compile_errors/stage1/obj/non_pointer_given_to_ptrToInt.zig +++ b/test/compile_errors/stage1/obj/non_pointer_given_to_ptrToInt.zig @@ -2,6 +2,8 @@ export fn entry(x: i32) usize { return @ptrToInt(x); } -// non pointer given to @ptrToInt +// error +// backend=stage1 +// target=native // // tmp.zig:2:22: error: expected pointer, found 'i32' diff --git a/test/compile_errors/stage1/obj/normal_string_with_newline.zig b/test/compile_errors/stage1/obj/normal_string_with_newline.zig index dafc041744..f6c1d1b1b9 100644 --- a/test/compile_errors/stage1/obj/normal_string_with_newline.zig +++ b/test/compile_errors/stage1/obj/normal_string_with_newline.zig @@ -1,7 +1,9 @@ const foo = "a b"; -// normal string with newline +// error +// backend=stage1 +// target=native // // tmp.zig:1:13: error: expected expression, found 'invalid bytes' // tmp.zig:1:15: note: invalid byte: '\n' diff --git a/test/compile_errors/stage1/obj/offsetOf-bad_field_name.zig b/test/compile_errors/stage1/obj/offsetOf-bad_field_name.zig index 3b301e2677..de5b82c22c 100644 --- a/test/compile_errors/stage1/obj/offsetOf-bad_field_name.zig +++ b/test/compile_errors/stage1/obj/offsetOf-bad_field_name.zig @@ -5,6 +5,8 @@ export fn foo() usize { return @offsetOf(Foo, "a",); } -// @offsetOf - bad field name +// error +// backend=stage1 +// target=native // // tmp.zig:5:27: error: struct 'Foo' has no field 'a' diff --git a/test/compile_errors/stage1/obj/offsetOf-non_struct.zig b/test/compile_errors/stage1/obj/offsetOf-non_struct.zig index 173119407a..15275b827b 100644 --- a/test/compile_errors/stage1/obj/offsetOf-non_struct.zig +++ b/test/compile_errors/stage1/obj/offsetOf-non_struct.zig @@ -3,6 +3,8 @@ export fn foo() usize { return @offsetOf(Foo, "a",); } -// @offsetOf - non struct +// error +// backend=stage1 +// target=native // // tmp.zig:3:22: error: expected struct type, found 'i32' diff --git a/test/compile_errors/stage1/obj/only_equality_binary_operator_allowed_for_error_sets.zig b/test/compile_errors/stage1/obj/only_equality_binary_operator_allowed_for_error_sets.zig index 58512ef2a6..ff9c4d18a2 100644 --- a/test/compile_errors/stage1/obj/only_equality_binary_operator_allowed_for_error_sets.zig +++ b/test/compile_errors/stage1/obj/only_equality_binary_operator_allowed_for_error_sets.zig @@ -3,6 +3,8 @@ comptime { _ = z; } -// only equality binary operator allowed for error sets +// error +// backend=stage1 +// target=native // // tmp.zig:2:23: error: operator not allowed for errors diff --git a/test/compile_errors/stage1/obj/opaque_type_with_field.zig b/test/compile_errors/stage1/obj/opaque_type_with_field.zig index 748d5c0736..0b87091de9 100644 --- a/test/compile_errors/stage1/obj/opaque_type_with_field.zig +++ b/test/compile_errors/stage1/obj/opaque_type_with_field.zig @@ -4,6 +4,8 @@ export fn entry() void { _ = foo; } -// opaque type with field +// error +// backend=stage1 +// target=native // // tmp.zig:1:25: error: opaque types cannot have fields diff --git a/test/compile_errors/stage1/obj/optional_pointer_to_void_in_extern_struct.zig b/test/compile_errors/stage1/obj/optional_pointer_to_void_in_extern_struct.zig index f4ebb577d9..552f672ad6 100644 --- a/test/compile_errors/stage1/obj/optional_pointer_to_void_in_extern_struct.zig +++ b/test/compile_errors/stage1/obj/optional_pointer_to_void_in_extern_struct.zig @@ -7,6 +7,8 @@ const Bar = extern struct { }; export fn entry(bar: *Bar) void {_ = bar;} -// optional pointer to void in extern struct +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: extern structs cannot contain fields of type '?*const void' diff --git a/test/compile_errors/stage1/obj/or_on_undefined_value.zig b/test/compile_errors/stage1/obj/or_on_undefined_value.zig index ac636bff9e..11e6c41dea 100644 --- a/test/compile_errors/stage1/obj/or_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/or_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = a or a; } -// or on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/orelse_on_undefined_value.zig b/test/compile_errors/stage1/obj/orelse_on_undefined_value.zig index 28e7c4b425..3042c208eb 100644 --- a/test/compile_errors/stage1/obj/orelse_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/orelse_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = a orelse false; } -// orelse on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:11: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/out_of_range_comptime_int_passed_to_floatToInt.zig b/test/compile_errors/stage1/obj/out_of_range_comptime_int_passed_to_floatToInt.zig index 581bb58ffd..1a1dd541c9 100644 --- a/test/compile_errors/stage1/obj/out_of_range_comptime_int_passed_to_floatToInt.zig +++ b/test/compile_errors/stage1/obj/out_of_range_comptime_int_passed_to_floatToInt.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = x; } -// out of range comptime_int passed to @floatToInt +// error +// backend=stage1 +// target=native // // tmp.zig:2:31: error: integer value 200 cannot be coerced to type 'i8' diff --git a/test/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig b/test/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig index 5c737e06a2..c5dc5c1dcf 100644 --- a/test/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig +++ b/test/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig @@ -7,6 +7,8 @@ pub fn main() void { _ = y; } -// overflow in enum value allocation +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: enumeration value 256 too large for type 'u8' diff --git a/test/compile_errors/stage1/obj/packed_union_given_enum_tag_type.zig b/test/compile_errors/stage1/obj/packed_union_given_enum_tag_type.zig index f0bc445295..fceb7af65c 100644 --- a/test/compile_errors/stage1/obj/packed_union_given_enum_tag_type.zig +++ b/test/compile_errors/stage1/obj/packed_union_given_enum_tag_type.zig @@ -13,6 +13,8 @@ export fn entry() void { _ = a; } -// packed union given enum tag type +// error +// backend=stage1 +// target=native // // tmp.zig:6:30: error: packed union does not support enum tag type diff --git a/test/compile_errors/stage1/obj/packed_union_with_automatic_layout_field.zig b/test/compile_errors/stage1/obj/packed_union_with_automatic_layout_field.zig index 4450531a5b..99ad6ca306 100644 --- a/test/compile_errors/stage1/obj/packed_union_with_automatic_layout_field.zig +++ b/test/compile_errors/stage1/obj/packed_union_with_automatic_layout_field.zig @@ -11,6 +11,8 @@ export fn entry() void { _ = a; } -// packed union with automatic layout field +// error +// backend=stage1 +// target=native // // tmp.zig:6:5: error: non-packed, non-extern struct 'Foo' not allowed in packed union; no guaranteed in-memory representation diff --git a/test/compile_errors/stage1/obj/panic_called_at_compile_time.zig b/test/compile_errors/stage1/obj/panic_called_at_compile_time.zig index 6dd32cdf59..7c16932913 100644 --- a/test/compile_errors/stage1/obj/panic_called_at_compile_time.zig +++ b/test/compile_errors/stage1/obj/panic_called_at_compile_time.zig @@ -4,6 +4,8 @@ export fn entry() void { } } -// @panic called at compile time +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: encountered @panic at compile-time diff --git a/test/compile_errors/stage1/obj/parameter_redeclaration.zig b/test/compile_errors/stage1/obj/parameter_redeclaration.zig index 01fc507afc..528eee8374 100644 --- a/test/compile_errors/stage1/obj/parameter_redeclaration.zig +++ b/test/compile_errors/stage1/obj/parameter_redeclaration.zig @@ -2,7 +2,9 @@ fn f(a : i32, a : i32) void { } export fn entry() void { f(1, 2); } -// parameter redeclaration +// error +// backend=stage1 +// target=native // // tmp.zig:1:15: error: redeclaration of function parameter 'a' // tmp.zig:1:6: note: previous declaration here diff --git a/test/compile_errors/stage1/obj/parameter_shadowing_global.zig b/test/compile_errors/stage1/obj/parameter_shadowing_global.zig index a58a0b93ce..ce417d7188 100644 --- a/test/compile_errors/stage1/obj/parameter_shadowing_global.zig +++ b/test/compile_errors/stage1/obj/parameter_shadowing_global.zig @@ -4,7 +4,9 @@ export fn entry() void { f(1234); } -// parameter shadowing global +// error +// backend=stage1 +// target=native // // tmp.zig:2:6: error: local shadows declaration of 'Foo' // tmp.zig:1:1: note: declared here diff --git a/test/compile_errors/stage1/obj/pass_const_ptr_to_mutable_ptr_fn.zig b/test/compile_errors/stage1/obj/pass_const_ptr_to_mutable_ptr_fn.zig index ff0057bb48..3209bb7321 100644 --- a/test/compile_errors/stage1/obj/pass_const_ptr_to_mutable_ptr_fn.zig +++ b/test/compile_errors/stage1/obj/pass_const_ptr_to_mutable_ptr_fn.zig @@ -10,6 +10,8 @@ fn ptrEql(a: *[]const u8, b: *[]const u8) bool { export fn entry() usize { return @sizeOf(@TypeOf(foo)); } -// pass const ptr to mutable ptr fn +// error +// backend=stage1 +// target=native // // tmp.zig:4:19: error: expected type '*[]const u8', found '*const []const u8' diff --git a/test/compile_errors/stage1/obj/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig b/test/compile_errors/stage1/obj/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig index 40564998b7..c1725f1ee8 100644 --- a/test/compile_errors/stage1/obj/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig +++ b/test/compile_errors/stage1/obj/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig @@ -5,6 +5,8 @@ export fn entry() bool { return x == 5678; } -// passing a not-aligned-enough pointer to cmpxchg +// error +// backend=stage1 +// target=native // // tmp.zig:4:32: error: expected type '*i32', found '*align(1) i32' diff --git a/test/compile_errors/stage1/obj/passing_an_under-aligned_function_pointer.zig b/test/compile_errors/stage1/obj/passing_an_under-aligned_function_pointer.zig index f6c0e52cc0..d67bd42a72 100644 --- a/test/compile_errors/stage1/obj/passing_an_under-aligned_function_pointer.zig +++ b/test/compile_errors/stage1/obj/passing_an_under-aligned_function_pointer.zig @@ -6,6 +6,8 @@ fn testImplicitlyDecreaseFnAlign(ptr: fn () align(8) i32, answer: i32) void { } fn alignedSmall() align(4) i32 { return 1234; } -// passing an under-aligned function pointer +// error +// backend=stage1 +// target=native // // tmp.zig:2:35: error: expected type 'fn() align(8) i32', found 'fn() align(4) i32' diff --git a/test/compile_errors/stage1/obj/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig b/test/compile_errors/stage1/obj/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig index 78f072c0d0..7d18e760c3 100644 --- a/test/compile_errors/stage1/obj/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig +++ b/test/compile_errors/stage1/obj/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig @@ -3,7 +3,9 @@ export fn func() void { strValue = strValue orelse ""; } -// peer cast then implicit cast const pointer to mutable C pointer +// error +// backend=stage1 +// target=native // // tmp.zig:3:32: error: expected type '[*c]u8', found '*const [0:0]u8' // tmp.zig:3:32: note: cast discards const qualifier diff --git a/test/compile_errors/stage1/obj/pointer_arithmetic_on_pointer-to-array.zig b/test/compile_errors/stage1/obj/pointer_arithmetic_on_pointer-to-array.zig index b293ae2b32..6542aa6968 100644 --- a/test/compile_errors/stage1/obj/pointer_arithmetic_on_pointer-to-array.zig +++ b/test/compile_errors/stage1/obj/pointer_arithmetic_on_pointer-to-array.zig @@ -5,6 +5,8 @@ export fn foo() void { _ = z; } -// pointer arithmetic on pointer-to-array +// error +// backend=stage1 +// target=native // // tmp.zig:4:17: error: integer value 1 cannot be coerced to type '*[10]u8' diff --git a/test/compile_errors/stage1/obj/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig b/test/compile_errors/stage1/obj/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig index 079844ac7f..8456c8afcb 100644 --- a/test/compile_errors/stage1/obj/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig +++ b/test/compile_errors/stage1/obj/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig @@ -12,7 +12,9 @@ comptime { _ = c; } -// pointer attributes checked when coercing pointer to anon literal +// error +// backend=stage1 +// target=native // // tmp.zig:2:31: error: cannot cast pointer to array literal to slice type '[][]const u8' // tmp.zig:2:31: note: cast discards const qualifier diff --git a/test/compile_errors/stage1/obj/pointer_to_noreturn.zig b/test/compile_errors/stage1/obj/pointer_to_noreturn.zig index 3b945919fc..5afcaacaad 100644 --- a/test/compile_errors/stage1/obj/pointer_to_noreturn.zig +++ b/test/compile_errors/stage1/obj/pointer_to_noreturn.zig @@ -1,6 +1,8 @@ fn a() *noreturn {} export fn entry() void { _ = a(); } -// pointer to noreturn +// error +// backend=stage1 +// target=native // // tmp.zig:1:9: error: pointer to noreturn not allowed diff --git a/test/compile_errors/stage1/obj/popCount-non-integer.zig b/test/compile_errors/stage1/obj/popCount-non-integer.zig index 694b794927..a58a98faa3 100644 --- a/test/compile_errors/stage1/obj/popCount-non-integer.zig +++ b/test/compile_errors/stage1/obj/popCount-non-integer.zig @@ -2,6 +2,8 @@ export fn entry(x: f32) u32 { return @popCount(f32, x); } -// @popCount - non-integer +// error +// backend=stage1 +// target=native // // tmp.zig:2:22: error: expected integer type, found 'f32' diff --git a/test/compile_errors/stage1/obj/prevent_bad_implicit_casting_of_anyframe_types.zig b/test/compile_errors/stage1/obj/prevent_bad_implicit_casting_of_anyframe_types.zig index 14bdb9cb11..2e21d45c12 100644 --- a/test/compile_errors/stage1/obj/prevent_bad_implicit_casting_of_anyframe_types.zig +++ b/test/compile_errors/stage1/obj/prevent_bad_implicit_casting_of_anyframe_types.zig @@ -15,7 +15,9 @@ export fn c() void { } fn func() void {} -// prevent bad implicit casting of anyframe types +// error +// backend=stage1 +// target=native // // tmp.zig:3:28: error: expected type 'anyframe->i32', found 'anyframe' // tmp.zig:8:28: error: expected type 'anyframe->i32', found 'i32' diff --git a/test/compile_errors/stage1/obj/primitives_take_precedence_over_declarations.zig b/test/compile_errors/stage1/obj/primitives_take_precedence_over_declarations.zig index 3517c07917..6f6107e88c 100644 --- a/test/compile_errors/stage1/obj/primitives_take_precedence_over_declarations.zig +++ b/test/compile_errors/stage1/obj/primitives_take_precedence_over_declarations.zig @@ -4,6 +4,8 @@ export fn entry() void { _ = a; } -// primitives take precedence over declarations +// error +// backend=stage1 +// target=native // // tmp.zig:3:19: error: integer value 300 cannot be coerced to type 'u8' diff --git a/test/compile_errors/stage1/obj/ptrCast_a_0_bit_type_to_a_non-_0_bit_type.zig b/test/compile_errors/stage1/obj/ptrCast_a_0_bit_type_to_a_non-_0_bit_type.zig index 63d0656f21..2fc30d4a32 100644 --- a/test/compile_errors/stage1/obj/ptrCast_a_0_bit_type_to_a_non-_0_bit_type.zig +++ b/test/compile_errors/stage1/obj/ptrCast_a_0_bit_type_to_a_non-_0_bit_type.zig @@ -4,7 +4,9 @@ export fn entry() bool { return p == null; } -// @ptrCast a 0 bit type to a non- 0 bit type +// error +// backend=stage1 +// target=native // // tmp.zig:3:15: error: '*u0' and '?*u0' do not have the same in-memory representation // tmp.zig:3:31: note: '*u0' has no in-memory bits diff --git a/test/compile_errors/stage1/obj/ptrCast_discards_const_qualifier.zig b/test/compile_errors/stage1/obj/ptrCast_discards_const_qualifier.zig index 1517fd4bd8..1a90ff73f8 100644 --- a/test/compile_errors/stage1/obj/ptrCast_discards_const_qualifier.zig +++ b/test/compile_errors/stage1/obj/ptrCast_discards_const_qualifier.zig @@ -4,6 +4,8 @@ export fn entry() void { _ = y; } -// @ptrCast discards const qualifier +// error +// backend=stage1 +// target=native // // tmp.zig:3:15: error: cast discards const qualifier diff --git a/test/compile_errors/stage1/obj/ptrToInt_0_to_non_optional_pointer.zig b/test/compile_errors/stage1/obj/ptrToInt_0_to_non_optional_pointer.zig index fde1e53c1a..b8a5d1b0aa 100644 --- a/test/compile_errors/stage1/obj/ptrToInt_0_to_non_optional_pointer.zig +++ b/test/compile_errors/stage1/obj/ptrToInt_0_to_non_optional_pointer.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = b; } -// @ptrToInt 0 to non optional pointer +// error +// backend=stage1 +// target=native // // tmp.zig:2:13: error: pointer type '*i32' does not allow address zero diff --git a/test/compile_errors/stage1/obj/ptrToInt_on_void.zig b/test/compile_errors/stage1/obj/ptrToInt_on_void.zig index fd43053103..5012be65e0 100644 --- a/test/compile_errors/stage1/obj/ptrToInt_on_void.zig +++ b/test/compile_errors/stage1/obj/ptrToInt_on_void.zig @@ -2,6 +2,8 @@ export fn entry() bool { return @ptrToInt(&{}) == @ptrToInt(&{}); } -// @ptrToInt on *void +// error +// backend=stage1 +// target=native // // tmp.zig:2:23: error: pointer to size 0 type has no address diff --git a/test/compile_errors/stage1/obj/ptrcast_to_non-pointer.zig b/test/compile_errors/stage1/obj/ptrcast_to_non-pointer.zig index 74894d5e8a..7e9e9a99b7 100644 --- a/test/compile_errors/stage1/obj/ptrcast_to_non-pointer.zig +++ b/test/compile_errors/stage1/obj/ptrcast_to_non-pointer.zig @@ -2,6 +2,8 @@ export fn entry(a: *i32) usize { return @ptrCast(usize, a); } -// ptrcast to non-pointer +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: expected pointer, found 'usize' diff --git a/test/compile_errors/stage1/obj/range_operator_in_switch_used_on_error_set.zig b/test/compile_errors/stage1/obj/range_operator_in_switch_used_on_error_set.zig index 4f2949c50c..8108f42979 100644 --- a/test/compile_errors/stage1/obj/range_operator_in_switch_used_on_error_set.zig +++ b/test/compile_errors/stage1/obj/range_operator_in_switch_used_on_error_set.zig @@ -12,6 +12,8 @@ fn foo(x: i32) !void { } } -// range operator in switch used on error set +// error +// backend=stage1 +// target=native // // tmp.zig:3:17: error: operator not allowed for errors diff --git a/test/compile_errors/stage1/obj/reading_past_end_of_pointer_casted_array.zig b/test/compile_errors/stage1/obj/reading_past_end_of_pointer_casted_array.zig index 558797d69b..cece3345c3 100644 --- a/test/compile_errors/stage1/obj/reading_past_end_of_pointer_casted_array.zig +++ b/test/compile_errors/stage1/obj/reading_past_end_of_pointer_casted_array.zig @@ -6,6 +6,8 @@ comptime { _ = deref; } -// reading past end of pointer casted array +// error +// backend=stage1 +// target=native // // tmp.zig:5:26: error: attempt to read 4 bytes from [4]u8 at index 1 which is 3 bytes diff --git a/test/compile_errors/stage1/obj/recursive_inferred_error_set.zig b/test/compile_errors/stage1/obj/recursive_inferred_error_set.zig index 8059833923..89a2a54cfa 100644 --- a/test/compile_errors/stage1/obj/recursive_inferred_error_set.zig +++ b/test/compile_errors/stage1/obj/recursive_inferred_error_set.zig @@ -5,6 +5,8 @@ fn foo() !void { try foo(); } -// recursive inferred error set +// error +// backend=stage1 +// target=native // // tmp.zig:5:5: error: cannot resolve inferred error set '@typeInfo(@typeInfo(@TypeOf(foo)).Fn.return_type.?).ErrorUnion.error_set': function 'foo' not fully analyzed yet diff --git a/test/compile_errors/stage1/obj/redefinition_of_enums.zig b/test/compile_errors/stage1/obj/redefinition_of_enums.zig index b4033536c3..a95eb88935 100644 --- a/test/compile_errors/stage1/obj/redefinition_of_enums.zig +++ b/test/compile_errors/stage1/obj/redefinition_of_enums.zig @@ -1,7 +1,9 @@ const A = enum {x}; const A = enum {x}; -// redefinition of enums +// error +// backend=stage1 +// target=native // // tmp.zig:2:1: error: redeclaration of 'A' // tmp.zig:1:1: note: other declaration here diff --git a/test/compile_errors/stage1/obj/redefinition_of_global_variables.zig b/test/compile_errors/stage1/obj/redefinition_of_global_variables.zig index b2267423b0..3651946ffe 100644 --- a/test/compile_errors/stage1/obj/redefinition_of_global_variables.zig +++ b/test/compile_errors/stage1/obj/redefinition_of_global_variables.zig @@ -1,7 +1,9 @@ var a : i32 = 1; var a : i32 = 2; -// redefinition of global variables +// error +// backend=stage1 +// target=native // // tmp.zig:2:1: error: redeclaration of 'a' // tmp.zig:1:1: note: other declaration here diff --git a/test/compile_errors/stage1/obj/redefinition_of_struct.zig b/test/compile_errors/stage1/obj/redefinition_of_struct.zig index 8c169356c9..8367a05e43 100644 --- a/test/compile_errors/stage1/obj/redefinition_of_struct.zig +++ b/test/compile_errors/stage1/obj/redefinition_of_struct.zig @@ -1,7 +1,9 @@ const A = struct { x : i32, }; const A = struct { y : i32, }; -// redefinition of struct +// error +// backend=stage1 +// target=native // // tmp.zig:2:1: error: redeclaration of 'A' // tmp.zig:1:1: note: other declaration here diff --git a/test/compile_errors/stage1/obj/refer_to_the_type_of_a_generic_function.zig b/test/compile_errors/stage1/obj/refer_to_the_type_of_a_generic_function.zig index 6e23854cd6..99ab1da8a3 100644 --- a/test/compile_errors/stage1/obj/refer_to_the_type_of_a_generic_function.zig +++ b/test/compile_errors/stage1/obj/refer_to_the_type_of_a_generic_function.zig @@ -4,6 +4,8 @@ export fn entry() void { f(i32); } -// refer to the type of a generic function +// error +// backend=stage1 +// target=native // // tmp.zig:4:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/referring_to_a_struct_that_is_invalid.zig b/test/compile_errors/stage1/obj/referring_to_a_struct_that_is_invalid.zig index 5f646461d8..433650cddb 100644 --- a/test/compile_errors/stage1/obj/referring_to_a_struct_that_is_invalid.zig +++ b/test/compile_errors/stage1/obj/referring_to_a_struct_that_is_invalid.zig @@ -10,6 +10,8 @@ fn assert(ok: bool) void { if (!ok) unreachable; } -// referring to a struct that is invalid +// error +// backend=stage1 +// target=native // // tmp.zig:10:14: error: reached unreachable code diff --git a/test/compile_errors/stage1/obj/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig b/test/compile_errors/stage1/obj/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig index a714b10ef6..8be44fbe05 100644 --- a/test/compile_errors/stage1/obj/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig +++ b/test/compile_errors/stage1/obj/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig @@ -14,6 +14,8 @@ export fn entry() void { _ = afoo; } -// regression test #2980: base type u32 is not type checked properly when assigning a value within a struct +// error +// backend=stage1 +// target=native // // tmp.zig:12:25: error: cannot convert error union to payload type. consider using `try`, `catch`, or `if`. expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(get_uval)).Fn.return_type.?).ErrorUnion.error_set!u32' diff --git a/test/compile_errors/stage1/obj/reify_type.Fn_with_is_generic_true.zig b/test/compile_errors/stage1/obj/reify_type.Fn_with_is_generic_true.zig index ed33158649..f2849b5eb4 100644 --- a/test/compile_errors/stage1/obj/reify_type.Fn_with_is_generic_true.zig +++ b/test/compile_errors/stage1/obj/reify_type.Fn_with_is_generic_true.zig @@ -10,6 +10,8 @@ const Foo = @Type(.{ }); comptime { _ = Foo; } -// @Type(.Fn) with is_generic = true +// error +// backend=stage1 +// target=native // // tmp.zig:1:20: error: Type.Fn.is_generic must be false for @Type diff --git a/test/compile_errors/stage1/obj/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig b/test/compile_errors/stage1/obj/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig index a57484075f..4d449e9eb9 100644 --- a/test/compile_errors/stage1/obj/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig +++ b/test/compile_errors/stage1/obj/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig @@ -10,6 +10,8 @@ const Foo = @Type(.{ }); comptime { _ = Foo; } -// @Type(.Fn) with is_var_args = true and non-C callconv +// error +// backend=stage1 +// target=native // // tmp.zig:1:20: error: varargs functions must have C calling convention diff --git a/test/compile_errors/stage1/obj/reify_type.Fn_with_return_type_null.zig b/test/compile_errors/stage1/obj/reify_type.Fn_with_return_type_null.zig index f8ddea52cf..98cbc37d41 100644 --- a/test/compile_errors/stage1/obj/reify_type.Fn_with_return_type_null.zig +++ b/test/compile_errors/stage1/obj/reify_type.Fn_with_return_type_null.zig @@ -10,6 +10,8 @@ const Foo = @Type(.{ }); comptime { _ = Foo; } -// @Type(.Fn) with return_type = null +// error +// backend=stage1 +// target=native // // tmp.zig:1:20: error: Type.Fn.return_type must be non-null for @Type diff --git a/test/compile_errors/stage1/obj/reify_type.Pointer_with_invalid_address_space.zig b/test/compile_errors/stage1/obj/reify_type.Pointer_with_invalid_address_space.zig index 78160fae58..1ca97ce250 100644 --- a/test/compile_errors/stage1/obj/reify_type.Pointer_with_invalid_address_space.zig +++ b/test/compile_errors/stage1/obj/reify_type.Pointer_with_invalid_address_space.zig @@ -11,6 +11,8 @@ export fn entry() void { }}); } -// @Type(.Pointer) with invalid address space +// error +// backend=stage1 +// target=native // // tmp.zig:2:16: error: address space 'gs' not available in stage 1 compiler, must be .generic diff --git a/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig b/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig index e77c2eb627..56d05578be 100644 --- a/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig +++ b/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig @@ -11,6 +11,8 @@ export fn entry() void { _ = @intToEnum(Tag, 0); } -// @Type for exhaustive enum with non-integer tag type +// error +// backend=stage1 +// target=native // // tmp.zig:1:20: error: Type.Enum.tag_type must be an integer type, not 'bool' diff --git a/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig b/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig index 6a00516387..e6454d2ee5 100644 --- a/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig +++ b/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig @@ -11,6 +11,8 @@ export fn entry() void { _ = @intToEnum(Tag, 0); } -// @Type for exhaustive enum with undefined tag type +// error +// backend=stage1 +// target=native // // tmp.zig:1:20: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_zero_fields.zig b/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_zero_fields.zig index 87689247aa..d3ce70c1b0 100644 --- a/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_zero_fields.zig +++ b/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_zero_fields.zig @@ -11,6 +11,8 @@ export fn entry() void { _ = @intToEnum(Tag, 0); } -// @Type for exhaustive enum with zero fields +// error +// backend=stage1 +// target=native // // tmp.zig:1:20: error: enums must have 1 or more fields diff --git a/test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_enum_field.zig b/test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_enum_field.zig index f9155d7161..0c56cb91ea 100644 --- a/test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_enum_field.zig +++ b/test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_enum_field.zig @@ -27,6 +27,8 @@ export fn entry() void { tagged = .{ .unsigned = 1 }; } -// @Type for tagged union with extra enum field +// error +// backend=stage1 +// target=native // // tmp.zig:14:23: error: enum field missing: 'arst' diff --git a/test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_union_field.zig b/test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_union_field.zig index 9cd82213df..63cf1f178e 100644 --- a/test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_union_field.zig +++ b/test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_union_field.zig @@ -27,7 +27,9 @@ export fn entry() void { tagged = .{ .unsigned = 1 }; } -// @Type for tagged union with extra union field +// error +// backend=stage1 +// target=native // // tmp.zig:13:23: error: enum field not found: 'arst' // tmp.zig:1:20: note: enum declared here diff --git a/test/compile_errors/stage1/obj/reify_type_for_union_with_opaque_field.zig b/test/compile_errors/stage1/obj/reify_type_for_union_with_opaque_field.zig index 374ccb544b..d3f4f6da4b 100644 --- a/test/compile_errors/stage1/obj/reify_type_for_union_with_opaque_field.zig +++ b/test/compile_errors/stage1/obj/reify_type_for_union_with_opaque_field.zig @@ -12,6 +12,8 @@ export fn entry() void { _ = Untagged{}; } -// @Type for union with opaque field +// error +// backend=stage1 +// target=native // // tmp.zig:1:25: error: opaque types have unknown size and therefore cannot be directly embedded in unions diff --git a/test/compile_errors/stage1/obj/reify_type_for_union_with_zero_fields.zig b/test/compile_errors/stage1/obj/reify_type_for_union_with_zero_fields.zig index 8243ff3292..578f902697 100644 --- a/test/compile_errors/stage1/obj/reify_type_for_union_with_zero_fields.zig +++ b/test/compile_errors/stage1/obj/reify_type_for_union_with_zero_fields.zig @@ -10,6 +10,8 @@ export fn entry() void { _ = Untagged{}; } -// @Type for union with zero fields +// error +// backend=stage1 +// target=native // // tmp.zig:1:25: error: unions must have 1 or more fields diff --git a/test/compile_errors/stage1/obj/reify_type_union_payload_is_undefined.zig b/test/compile_errors/stage1/obj/reify_type_union_payload_is_undefined.zig index 43261dc32f..47be31c711 100644 --- a/test/compile_errors/stage1/obj/reify_type_union_payload_is_undefined.zig +++ b/test/compile_errors/stage1/obj/reify_type_union_payload_is_undefined.zig @@ -3,6 +3,8 @@ const Foo = @Type(.{ }); comptime { _ = Foo; } -// @Type() union payload is undefined +// error +// backend=stage1 +// target=native // // tmp.zig:1:20: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/reify_type_with_Type.Int.zig b/test/compile_errors/stage1/obj/reify_type_with_Type.Int.zig index cea09d3f19..116bd86e0f 100644 --- a/test/compile_errors/stage1/obj/reify_type_with_Type.Int.zig +++ b/test/compile_errors/stage1/obj/reify_type_with_Type.Int.zig @@ -6,6 +6,8 @@ export fn entry() void { }); } -// @Type with Type.Int +// error +// backend=stage1 +// target=native // // tmp.zig:3:31: error: expected type 'std.builtin.Type', found 'std.builtin.Type.Int' diff --git a/test/compile_errors/stage1/obj/reify_type_with_non-constant_expression.zig b/test/compile_errors/stage1/obj/reify_type_with_non-constant_expression.zig index a58b2a6b33..7eec6b395a 100644 --- a/test/compile_errors/stage1/obj/reify_type_with_non-constant_expression.zig +++ b/test/compile_errors/stage1/obj/reify_type_with_non-constant_expression.zig @@ -4,6 +4,8 @@ export fn entry() void { _ = @Type(globalTypeInfo); } -// @Type with non-constant expression +// error +// backend=stage1 +// target=native // // tmp.zig:4:15: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/reify_type_with_undefined.zig b/test/compile_errors/stage1/obj/reify_type_with_undefined.zig index 984c9ac555..1de93ccdf6 100644 --- a/test/compile_errors/stage1/obj/reify_type_with_undefined.zig +++ b/test/compile_errors/stage1/obj/reify_type_with_undefined.zig @@ -12,7 +12,9 @@ comptime { }); } -// @Type with undefined +// error +// backend=stage1 +// target=native // // tmp.zig:2:16: error: use of undefined value here causes undefined behavior // tmp.zig:5:16: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr.zig b/test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr.zig index a45fd9f41a..18dd6382fc 100644 --- a/test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr.zig +++ b/test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr.zig @@ -11,6 +11,8 @@ pub const Container = struct { not_optional: i32, }; -// result location incompatibility mismatching handle_is_ptr +// error +// backend=stage1 +// target=native // // tmp.zig:3:36: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type 'i32', found '?i32' diff --git a/test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig b/test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig index 8b913bd15c..75811c58e1 100644 --- a/test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig +++ b/test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig @@ -11,6 +11,8 @@ pub const Container = struct { not_optional: i32, }; -// result location incompatibility mismatching handle_is_ptr (generic call) +// error +// backend=stage1 +// target=native // // tmp.zig:3:36: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type 'i32', found '?i32' diff --git a/test/compile_errors/stage1/obj/return_from_defer_expression.zig b/test/compile_errors/stage1/obj/return_from_defer_expression.zig index 0f4ac8e071..cbe9542176 100644 --- a/test/compile_errors/stage1/obj/return_from_defer_expression.zig +++ b/test/compile_errors/stage1/obj/return_from_defer_expression.zig @@ -14,6 +14,8 @@ pub fn maybeInt() ?i32 { export fn entry() usize { return @sizeOf(@TypeOf(testTrickyDefer)); } -// return from defer expression +// error +// backend=stage1 +// target=native // // tmp.zig:4:11: error: 'try' not allowed inside defer expression diff --git a/test/compile_errors/stage1/obj/returning_error_from_void_async_function.zig b/test/compile_errors/stage1/obj/returning_error_from_void_async_function.zig index a00422dd12..691c89a347 100644 --- a/test/compile_errors/stage1/obj/returning_error_from_void_async_function.zig +++ b/test/compile_errors/stage1/obj/returning_error_from_void_async_function.zig @@ -5,6 +5,8 @@ fn amain() callconv(.Async) void { return error.ShouldBeCompileError; } -// returning error from void async function +// error +// backend=stage1 +// target=native // // tmp.zig:5:17: error: expected type 'void', found 'error{ShouldBeCompileError}' diff --git a/test/compile_errors/stage1/obj/runtime-known_async_function_called.zig b/test/compile_errors/stage1/obj/runtime-known_async_function_called.zig index 52afd83a11..90685ce566 100644 --- a/test/compile_errors/stage1/obj/runtime-known_async_function_called.zig +++ b/test/compile_errors/stage1/obj/runtime-known_async_function_called.zig @@ -7,6 +7,8 @@ fn amain() void { } fn afunc() callconv(.Async) void {} -// runtime-known async function called +// error +// backend=stage1 +// target=native // // tmp.zig:6:12: error: function is not comptime-known; @asyncCall required diff --git a/test/compile_errors/stage1/obj/runtime-known_function_called_with_async_keyword.zig b/test/compile_errors/stage1/obj/runtime-known_function_called_with_async_keyword.zig index 79f0420a83..c66d0f9cbb 100644 --- a/test/compile_errors/stage1/obj/runtime-known_function_called_with_async_keyword.zig +++ b/test/compile_errors/stage1/obj/runtime-known_function_called_with_async_keyword.zig @@ -5,6 +5,8 @@ export fn entry() void { fn afunc() callconv(.Async) void { } -// runtime-known function called with async keyword +// error +// backend=stage1 +// target=native // // tmp.zig:3:15: error: function is not comptime-known; @asyncCall required diff --git a/test/compile_errors/stage1/obj/runtime_assignment_to_comptime_struct_type.zig b/test/compile_errors/stage1/obj/runtime_assignment_to_comptime_struct_type.zig index f8a3fd3ba1..62b09673eb 100644 --- a/test/compile_errors/stage1/obj/runtime_assignment_to_comptime_struct_type.zig +++ b/test/compile_errors/stage1/obj/runtime_assignment_to_comptime_struct_type.zig @@ -8,6 +8,8 @@ export fn f() void { _ = foo; } -// runtime assignment to comptime struct type +// error +// backend=stage1 +// target=native // // tmp.zig:7:23: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/runtime_assignment_to_comptime_union_type.zig b/test/compile_errors/stage1/obj/runtime_assignment_to_comptime_union_type.zig index bc4f093b0d..af13f8b8ce 100644 --- a/test/compile_errors/stage1/obj/runtime_assignment_to_comptime_union_type.zig +++ b/test/compile_errors/stage1/obj/runtime_assignment_to_comptime_union_type.zig @@ -8,6 +8,8 @@ export fn f() void { _ = foo; } -// runtime assignment to comptime union type +// error +// backend=stage1 +// target=native // // tmp.zig:7:23: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/obj/runtime_cast_to_union_which_has_non-void_fields.zig b/test/compile_errors/stage1/obj/runtime_cast_to_union_which_has_non-void_fields.zig index 8be361f2fe..d25c815c7e 100644 --- a/test/compile_errors/stage1/obj/runtime_cast_to_union_which_has_non-void_fields.zig +++ b/test/compile_errors/stage1/obj/runtime_cast_to_union_which_has_non-void_fields.zig @@ -12,7 +12,9 @@ fn foo(l: Letter) void { _ = x; } -// runtime cast to union which has non-void fields +// error +// backend=stage1 +// target=native // // tmp.zig:11:20: error: runtime cast to union 'Value' which has non-void fields // tmp.zig:3:5: note: field 'A' has type 'i32' diff --git a/test/compile_errors/stage1/obj/runtime_index_into_comptime_type_slice.zig b/test/compile_errors/stage1/obj/runtime_index_into_comptime_type_slice.zig index 4b9538c1a1..379767c7f3 100644 --- a/test/compile_errors/stage1/obj/runtime_index_into_comptime_type_slice.zig +++ b/test/compile_errors/stage1/obj/runtime_index_into_comptime_type_slice.zig @@ -10,6 +10,8 @@ export fn entry() void { _ = field; } -// runtime index into comptime type slice +// error +// backend=stage1 +// target=native // // tmp.zig:9:51: error: values of type 'std.builtin.Type.StructField' must be comptime known, but index value is runtime known diff --git a/test/compile_errors/stage1/obj/saturating_arithmetic_does_not_allow_floats.zig b/test/compile_errors/stage1/obj/saturating_arithmetic_does_not_allow_floats.zig index 57ee724e74..7bda134af6 100644 --- a/test/compile_errors/stage1/obj/saturating_arithmetic_does_not_allow_floats.zig +++ b/test/compile_errors/stage1/obj/saturating_arithmetic_does_not_allow_floats.zig @@ -2,6 +2,8 @@ export fn a() void { _ = @as(f32, 1.0) +| @as(f32, 1.0); } -// saturating arithmetic does not allow floats +// error +// backend=stage1 +// target=native // // error: invalid operands to binary expression: 'f32' and 'f32' diff --git a/test/compile_errors/stage1/obj/saturating_shl_assign_does_not_allow_negative_rhs_at_comptime.zig b/test/compile_errors/stage1/obj/saturating_shl_assign_does_not_allow_negative_rhs_at_comptime.zig index 9a46367ae5..7b94f8e832 100644 --- a/test/compile_errors/stage1/obj/saturating_shl_assign_does_not_allow_negative_rhs_at_comptime.zig +++ b/test/compile_errors/stage1/obj/saturating_shl_assign_does_not_allow_negative_rhs_at_comptime.zig @@ -5,6 +5,8 @@ export fn a() void { } } -// saturating shl assign does not allow negative rhs at comptime +// error +// backend=stage1 +// target=native // // error: shift by negative value -2 diff --git a/test/compile_errors/stage1/obj/saturating_shl_does_not_allow_negative_rhs_at_comptime.zig b/test/compile_errors/stage1/obj/saturating_shl_does_not_allow_negative_rhs_at_comptime.zig index ffe715a8e3..ac1ce7d3a0 100644 --- a/test/compile_errors/stage1/obj/saturating_shl_does_not_allow_negative_rhs_at_comptime.zig +++ b/test/compile_errors/stage1/obj/saturating_shl_does_not_allow_negative_rhs_at_comptime.zig @@ -2,6 +2,8 @@ export fn a() void { _ = @as(i32, 1) <<| @as(i32, -2); } -// saturating shl does not allow negative rhs at comptime +// error +// backend=stage1 +// target=native // // error: shift by negative value -2 diff --git a/test/compile_errors/stage1/obj/setAlignStack_in_inline_function.zig b/test/compile_errors/stage1/obj/setAlignStack_in_inline_function.zig index 9261ecdb18..8490298aa3 100644 --- a/test/compile_errors/stage1/obj/setAlignStack_in_inline_function.zig +++ b/test/compile_errors/stage1/obj/setAlignStack_in_inline_function.zig @@ -5,6 +5,8 @@ fn foo() callconv(.Inline) void { @setAlignStack(16); } -// @setAlignStack in inline function +// error +// backend=stage1 +// target=native // // tmp.zig:5:5: error: @setAlignStack in inline function diff --git a/test/compile_errors/stage1/obj/setAlignStack_in_naked_function.zig b/test/compile_errors/stage1/obj/setAlignStack_in_naked_function.zig index d7146625c5..b9adc84ed5 100644 --- a/test/compile_errors/stage1/obj/setAlignStack_in_naked_function.zig +++ b/test/compile_errors/stage1/obj/setAlignStack_in_naked_function.zig @@ -2,6 +2,8 @@ export fn entry() callconv(.Naked) void { @setAlignStack(16); } -// @setAlignStack in naked function +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: @setAlignStack in naked function diff --git a/test/compile_errors/stage1/obj/setAlignStack_outside_function.zig b/test/compile_errors/stage1/obj/setAlignStack_outside_function.zig index c9ac12e6e0..754756012b 100644 --- a/test/compile_errors/stage1/obj/setAlignStack_outside_function.zig +++ b/test/compile_errors/stage1/obj/setAlignStack_outside_function.zig @@ -2,6 +2,8 @@ comptime { @setAlignStack(16); } -// @setAlignStack outside function +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: @setAlignStack outside function diff --git a/test/compile_errors/stage1/obj/setAlignStack_set_twice.zig b/test/compile_errors/stage1/obj/setAlignStack_set_twice.zig index de8cde7c01..244db9d962 100644 --- a/test/compile_errors/stage1/obj/setAlignStack_set_twice.zig +++ b/test/compile_errors/stage1/obj/setAlignStack_set_twice.zig @@ -3,7 +3,9 @@ export fn entry() void { @setAlignStack(16); } -// @setAlignStack set twice +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: alignstack set twice // tmp.zig:2:5: note: first set here diff --git a/test/compile_errors/stage1/obj/setAlignStack_too_big.zig b/test/compile_errors/stage1/obj/setAlignStack_too_big.zig index 669ec45204..601631e158 100644 --- a/test/compile_errors/stage1/obj/setAlignStack_too_big.zig +++ b/test/compile_errors/stage1/obj/setAlignStack_too_big.zig @@ -2,6 +2,8 @@ export fn entry() void { @setAlignStack(511 + 1); } -// @setAlignStack too big +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: attempt to @setAlignStack(512); maximum is 256 diff --git a/test/compile_errors/stage1/obj/setFloatMode_twice_for_same_scope.zig b/test/compile_errors/stage1/obj/setFloatMode_twice_for_same_scope.zig index 3e55604030..41fe6d4d6c 100644 --- a/test/compile_errors/stage1/obj/setFloatMode_twice_for_same_scope.zig +++ b/test/compile_errors/stage1/obj/setFloatMode_twice_for_same_scope.zig @@ -3,7 +3,9 @@ export fn foo() void { @setFloatMode(@import("std").builtin.FloatMode.Optimized); } -// @setFloatMode twice for same scope +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: float mode set twice for same scope // tmp.zig:2:5: note: first set here diff --git a/test/compile_errors/stage1/obj/setRuntimeSafety_twice_for_same_scope.zig b/test/compile_errors/stage1/obj/setRuntimeSafety_twice_for_same_scope.zig index 741a214cde..03a93b68d5 100644 --- a/test/compile_errors/stage1/obj/setRuntimeSafety_twice_for_same_scope.zig +++ b/test/compile_errors/stage1/obj/setRuntimeSafety_twice_for_same_scope.zig @@ -3,7 +3,9 @@ export fn foo() void { @setRuntimeSafety(false); } -// @setRuntimeSafety twice for same scope +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: runtime safety set twice for same scope // tmp.zig:2:5: note: first set here diff --git a/test/compile_errors/stage1/obj/setting_a_section_on_a_local_variable.zig b/test/compile_errors/stage1/obj/setting_a_section_on_a_local_variable.zig index eb3df0e214..90d605dc40 100644 --- a/test/compile_errors/stage1/obj/setting_a_section_on_a_local_variable.zig +++ b/test/compile_errors/stage1/obj/setting_a_section_on_a_local_variable.zig @@ -3,6 +3,8 @@ export fn entry() i32 { return foo; } -// setting a section on a local variable +// error +// backend=stage1 +// target=native // // tmp.zig:2:30: error: cannot set section of local variable 'foo' diff --git a/test/compile_errors/stage1/obj/shift_amount_has_to_be_an_integer_type.zig b/test/compile_errors/stage1/obj/shift_amount_has_to_be_an_integer_type.zig index 6e54405619..53794dba1b 100644 --- a/test/compile_errors/stage1/obj/shift_amount_has_to_be_an_integer_type.zig +++ b/test/compile_errors/stage1/obj/shift_amount_has_to_be_an_integer_type.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = x; } -// shift amount has to be an integer type +// error +// backend=stage1 +// target=native // // tmp.zig:2:21: error: shift amount has to be an integer type, but found '*const u8' diff --git a/test/compile_errors/stage1/obj/shift_by_negative_comptime_integer.zig b/test/compile_errors/stage1/obj/shift_by_negative_comptime_integer.zig index 41f0ff80aa..3425cf565e 100644 --- a/test/compile_errors/stage1/obj/shift_by_negative_comptime_integer.zig +++ b/test/compile_errors/stage1/obj/shift_by_negative_comptime_integer.zig @@ -3,6 +3,8 @@ comptime { _ = a; } -// shift by negative comptime integer +// error +// backend=stage1 +// target=native // // tmp.zig:2:18: error: shift by negative value -1 diff --git a/test/compile_errors/stage1/obj/shift_left_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/shift_left_assign_on_undefined_value.zig index a23d0e190c..575bf0ee6b 100644 --- a/test/compile_errors/stage1/obj/shift_left_assign_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/shift_left_assign_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { a >>= 2; } -// shift left assign on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/shift_left_on_undefined_value.zig b/test/compile_errors/stage1/obj/shift_left_on_undefined_value.zig index aa0891133b..abd06fbbab 100644 --- a/test/compile_errors/stage1/obj/shift_left_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/shift_left_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = a << 2; } -// shift left on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/shift_right_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/shift_right_assign_on_undefined_value.zig index 65f4fe50b8..575bf0ee6b 100644 --- a/test/compile_errors/stage1/obj/shift_right_assign_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/shift_right_assign_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { a >>= 2; } -// shift right assign on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/shift_right_on_undefined_value.zig b/test/compile_errors/stage1/obj/shift_right_on_undefined_value.zig index ccd2563c84..e24cbe473a 100644 --- a/test/compile_errors/stage1/obj/shift_right_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/shift_right_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = a >> 2; } -// shift right on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/shifting_RHS_is_log2_of_LHS_int_bit_width.zig b/test/compile_errors/stage1/obj/shifting_RHS_is_log2_of_LHS_int_bit_width.zig index 8a5d6cfee4..d9b9bbfce3 100644 --- a/test/compile_errors/stage1/obj/shifting_RHS_is_log2_of_LHS_int_bit_width.zig +++ b/test/compile_errors/stage1/obj/shifting_RHS_is_log2_of_LHS_int_bit_width.zig @@ -2,6 +2,8 @@ export fn entry(x: u8, y: u8) u8 { return x << y; } -// shifting RHS is log2 of LHS int bit width +// error +// backend=stage1 +// target=native // // tmp.zig:2:17: error: expected type 'u3', found 'u8' diff --git a/test/compile_errors/stage1/obj/shifting_without_int_type_or_comptime_known.zig b/test/compile_errors/stage1/obj/shifting_without_int_type_or_comptime_known.zig index 380a08a95c..4d875575d0 100644 --- a/test/compile_errors/stage1/obj/shifting_without_int_type_or_comptime_known.zig +++ b/test/compile_errors/stage1/obj/shifting_without_int_type_or_comptime_known.zig @@ -2,6 +2,8 @@ export fn entry(x: u8) u8 { return 0x11 << x; } -// shifting without int type or comptime known +// error +// backend=stage1 +// target=native // // tmp.zig:2:17: error: LHS of shift must be a fixed-width integer type, or RHS must be compile-time known diff --git a/test/compile_errors/stage1/obj/shlExact_shifts_out_1_bits.zig b/test/compile_errors/stage1/obj/shlExact_shifts_out_1_bits.zig index bd6e48f25c..953c5fec50 100644 --- a/test/compile_errors/stage1/obj/shlExact_shifts_out_1_bits.zig +++ b/test/compile_errors/stage1/obj/shlExact_shifts_out_1_bits.zig @@ -3,6 +3,8 @@ comptime { _ = x; } -// @shlExact shifts out 1 bits +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: operation caused overflow diff --git a/test/compile_errors/stage1/obj/shrExact_shifts_out_1_bits.zig b/test/compile_errors/stage1/obj/shrExact_shifts_out_1_bits.zig index 004bf0c1c3..223db76630 100644 --- a/test/compile_errors/stage1/obj/shrExact_shifts_out_1_bits.zig +++ b/test/compile_errors/stage1/obj/shrExact_shifts_out_1_bits.zig @@ -3,6 +3,8 @@ comptime { _ = x; } -// @shrExact shifts out 1 bits +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: exact shift shifted out 1 bits diff --git a/test/compile_errors/stage1/obj/signed_integer_division.zig b/test/compile_errors/stage1/obj/signed_integer_division.zig index 524f9fe1db..3eebbf2248 100644 --- a/test/compile_errors/stage1/obj/signed_integer_division.zig +++ b/test/compile_errors/stage1/obj/signed_integer_division.zig @@ -2,6 +2,8 @@ export fn foo(a: i32, b: i32) i32 { return a / b; } -// signed integer division +// error +// backend=stage1 +// target=native // // tmp.zig:2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact diff --git a/test/compile_errors/stage1/obj/signed_integer_remainder_division.zig b/test/compile_errors/stage1/obj/signed_integer_remainder_division.zig index e060b780d0..5877672954 100644 --- a/test/compile_errors/stage1/obj/signed_integer_remainder_division.zig +++ b/test/compile_errors/stage1/obj/signed_integer_remainder_division.zig @@ -2,6 +2,8 @@ export fn foo(a: i32, b: i32) i32 { return a % b; } -// signed integer remainder division +// error +// backend=stage1 +// target=native // // tmp.zig:2:14: error: remainder division with 'i32' and 'i32': signed integers and floats must use @rem or @mod diff --git a/test/compile_errors/stage1/obj/sizeOf_bad_type.zig b/test/compile_errors/stage1/obj/sizeOf_bad_type.zig index 1b336cf657..f8c397f768 100644 --- a/test/compile_errors/stage1/obj/sizeOf_bad_type.zig +++ b/test/compile_errors/stage1/obj/sizeOf_bad_type.zig @@ -2,6 +2,8 @@ export fn entry() usize { return @sizeOf(@TypeOf(null)); } -// @sizeOf bad type +// error +// backend=stage1 +// target=native // // tmp.zig:2:20: error: no size available for type '@Type(.Null)' diff --git a/test/compile_errors/stage1/obj/slice_cannot_have_its_bytes_reinterpreted.zig b/test/compile_errors/stage1/obj/slice_cannot_have_its_bytes_reinterpreted.zig index 4f7670ba74..8fcf4f505c 100644 --- a/test/compile_errors/stage1/obj/slice_cannot_have_its_bytes_reinterpreted.zig +++ b/test/compile_errors/stage1/obj/slice_cannot_have_its_bytes_reinterpreted.zig @@ -4,6 +4,8 @@ export fn foo() void { _ = value; } -// slice cannot have its bytes reinterpreted +// error +// backend=stage1 +// target=native // // :3:52: error: slice '[]const u8' cannot have its bytes reinterpreted diff --git a/test/compile_errors/stage1/obj/slice_passed_as_array_init_type.zig b/test/compile_errors/stage1/obj/slice_passed_as_array_init_type.zig index 0d29120ca4..7ad29b1900 100644 --- a/test/compile_errors/stage1/obj/slice_passed_as_array_init_type.zig +++ b/test/compile_errors/stage1/obj/slice_passed_as_array_init_type.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = x; } -// slice passed as array init type +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: array literal requires address-of operator (&) to coerce to slice type '[]u8' diff --git a/test/compile_errors/stage1/obj/slice_passed_as_array_init_type_with_elems.zig b/test/compile_errors/stage1/obj/slice_passed_as_array_init_type_with_elems.zig index 375ed55a72..0da4b3fc9d 100644 --- a/test/compile_errors/stage1/obj/slice_passed_as_array_init_type_with_elems.zig +++ b/test/compile_errors/stage1/obj/slice_passed_as_array_init_type_with_elems.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = x; } -// slice passed as array init type with elems +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: array literal requires address-of operator (&) to coerce to slice type '[]u8' diff --git a/test/compile_errors/stage1/obj/slice_sentinel_mismatch-1.zig b/test/compile_errors/stage1/obj/slice_sentinel_mismatch-1.zig index 1e3c2450cb..d0c47097da 100644 --- a/test/compile_errors/stage1/obj/slice_sentinel_mismatch-1.zig +++ b/test/compile_errors/stage1/obj/slice_sentinel_mismatch-1.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = y; } -// slice sentinel mismatch - 1 +// error +// backend=stage1 +// target=native // // tmp.zig:2:37: error: expected type '[:1]const u8', found '*const [2:2]u8' diff --git a/test/compile_errors/stage1/obj/slice_sentinel_mismatch-2.zig b/test/compile_errors/stage1/obj/slice_sentinel_mismatch-2.zig index f1d73eb9df..5f8d312a3c 100644 --- a/test/compile_errors/stage1/obj/slice_sentinel_mismatch-2.zig +++ b/test/compile_errors/stage1/obj/slice_sentinel_mismatch-2.zig @@ -4,7 +4,9 @@ fn foo() [:0]u8 { } comptime { _ = foo; } -// slice sentinel mismatch - 2 +// error +// backend=stage1 +// target=native // // tmp.zig:3:12: error: expected type '[:0]u8', found '[]u8' // tmp.zig:3:12: note: destination pointer requires a terminating '0' sentinel diff --git a/test/compile_errors/stage1/obj/slicing_of_global_undefined_pointer.zig b/test/compile_errors/stage1/obj/slicing_of_global_undefined_pointer.zig index d52bfdf6a5..8bbc0f85eb 100644 --- a/test/compile_errors/stage1/obj/slicing_of_global_undefined_pointer.zig +++ b/test/compile_errors/stage1/obj/slicing_of_global_undefined_pointer.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = buf[0..1]; } -// slicing of global undefined pointer +// error +// backend=stage1 +// target=native // // tmp.zig:3:12: error: non-zero length slice of undefined pointer diff --git a/test/compile_errors/stage1/obj/slicing_single-item_pointer.zig b/test/compile_errors/stage1/obj/slicing_single-item_pointer.zig index f6bbe752b1..d8c82fa3af 100644 --- a/test/compile_errors/stage1/obj/slicing_single-item_pointer.zig +++ b/test/compile_errors/stage1/obj/slicing_single-item_pointer.zig @@ -3,6 +3,8 @@ export fn entry(ptr: *i32) void { _ = slice; } -// slicing single-item pointer +// error +// backend=stage1 +// target=native // // tmp.zig:2:22: error: slice of single-item pointer diff --git a/test/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig b/test/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig index ca7c305d38..dc7077c8f8 100644 --- a/test/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig +++ b/test/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig @@ -11,6 +11,8 @@ export fn entry() void { _ = x; } -// specify enum tag type that is too small +// error +// backend=stage1 +// target=native // // tmp.zig:6:5: error: enumeration value 4 too large for type 'u2' diff --git a/test/compile_errors/stage1/obj/specify_non-integer_enum_tag_type.zig b/test/compile_errors/stage1/obj/specify_non-integer_enum_tag_type.zig index 095a0812c1..333647e1e3 100644 --- a/test/compile_errors/stage1/obj/specify_non-integer_enum_tag_type.zig +++ b/test/compile_errors/stage1/obj/specify_non-integer_enum_tag_type.zig @@ -9,6 +9,8 @@ export fn entry() void { _ = x; } -// specify non-integer enum tag type +// error +// backend=stage1 +// target=native // // tmp.zig:1:21: error: expected integer, found 'f32' diff --git a/test/compile_errors/stage1/obj/src_outside_function.zig b/test/compile_errors/stage1/obj/src_outside_function.zig index 5359135066..7f8c7ae72f 100644 --- a/test/compile_errors/stage1/obj/src_outside_function.zig +++ b/test/compile_errors/stage1/obj/src_outside_function.zig @@ -2,6 +2,8 @@ comptime { @src(); } -// @src outside function +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: @src outside function diff --git a/test/compile_errors/stage1/obj/std.fmt_error_for_unused_arguments.zig b/test/compile_errors/stage1/obj/std.fmt_error_for_unused_arguments.zig index 68299c2177..21603ab3d3 100644 --- a/test/compile_errors/stage1/obj/std.fmt_error_for_unused_arguments.zig +++ b/test/compile_errors/stage1/obj/std.fmt_error_for_unused_arguments.zig @@ -2,6 +2,8 @@ export fn entry() void { @import("std").debug.print("{d} {d} {d} {d} {d}", .{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}); } -// std.fmt error for unused arguments +// error +// backend=stage1 +// target=native // // ?:?:?: error: 10 unused arguments in '{d} {d} {d} {d} {d}' diff --git a/test/compile_errors/stage1/obj/store_vector_pointer_with_unknown_runtime_index.zig b/test/compile_errors/stage1/obj/store_vector_pointer_with_unknown_runtime_index.zig index 6ef71c863a..57e91631b1 100644 --- a/test/compile_errors/stage1/obj/store_vector_pointer_with_unknown_runtime_index.zig +++ b/test/compile_errors/stage1/obj/store_vector_pointer_with_unknown_runtime_index.zig @@ -9,6 +9,8 @@ fn storev(ptr: anytype, val: i32) void { ptr.* = val; } -// store vector pointer with unknown runtime index +// error +// backend=stage1 +// target=native // // tmp.zig:9:8: error: unable to determine vector element index of type '*align(16:0:4:?) i32 diff --git a/test/compile_errors/stage1/obj/storing_runtime_value_in_compile_time_variable_then_using_it.zig b/test/compile_errors/stage1/obj/storing_runtime_value_in_compile_time_variable_then_using_it.zig index 318db771d5..2d85209358 100644 --- a/test/compile_errors/stage1/obj/storing_runtime_value_in_compile_time_variable_then_using_it.zig +++ b/test/compile_errors/stage1/obj/storing_runtime_value_in_compile_time_variable_then_using_it.zig @@ -42,6 +42,8 @@ export fn entry() void { } } -// storing runtime value in compile time variable then using it +// error +// backend=stage1 +// target=native // // tmp.zig:38:29: error: cannot store runtime value in compile time variable diff --git a/test/compile_errors/stage1/obj/struct_depends_on_itself_via_optional_field.zig b/test/compile_errors/stage1/obj/struct_depends_on_itself_via_optional_field.zig index 13ee95155d..46086172f7 100644 --- a/test/compile_errors/stage1/obj/struct_depends_on_itself_via_optional_field.zig +++ b/test/compile_errors/stage1/obj/struct_depends_on_itself_via_optional_field.zig @@ -10,7 +10,9 @@ export fn entry() void { _ = obj; } -// struct depends on itself via optional field +// error +// backend=stage1 +// target=native // // tmp.zig:1:17: error: struct 'LhsExpr' depends on itself // tmp.zig:5:5: note: while checking this field diff --git a/test/compile_errors/stage1/obj/struct_field_missing_type.zig b/test/compile_errors/stage1/obj/struct_field_missing_type.zig index 669718a3d4..175f1c7df7 100644 --- a/test/compile_errors/stage1/obj/struct_field_missing_type.zig +++ b/test/compile_errors/stage1/obj/struct_field_missing_type.zig @@ -6,6 +6,8 @@ export fn entry() void { _ = a; } -// struct field missing type +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: struct field missing type diff --git a/test/compile_errors/stage1/obj/struct_init_syntax_for_array.zig b/test/compile_errors/stage1/obj/struct_init_syntax_for_array.zig index 4eda9622ab..6362e05114 100644 --- a/test/compile_errors/stage1/obj/struct_init_syntax_for_array.zig +++ b/test/compile_errors/stage1/obj/struct_init_syntax_for_array.zig @@ -3,6 +3,8 @@ comptime { _ = foo; } -// struct init syntax for array +// error +// backend=stage1 +// target=native // // tmp.zig:1:13: error: initializing array with struct syntax diff --git a/test/compile_errors/stage1/obj/struct_with_declarations_unavailable_for_reify_type.zig b/test/compile_errors/stage1/obj/struct_with_declarations_unavailable_for_reify_type.zig index 656a9b9f62..409020ca25 100644 --- a/test/compile_errors/stage1/obj/struct_with_declarations_unavailable_for_reify_type.zig +++ b/test/compile_errors/stage1/obj/struct_with_declarations_unavailable_for_reify_type.zig @@ -2,6 +2,8 @@ export fn entry() void { _ = @Type(@typeInfo(struct { const foo = 1; })); } -// struct with declarations unavailable for @Type +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: Type.Struct.decls must be empty for @Type diff --git a/test/compile_errors/stage1/obj/struct_with_invalid_field.zig b/test/compile_errors/stage1/obj/struct_with_invalid_field.zig index a2a632fbd9..dcc9d26f65 100644 --- a/test/compile_errors/stage1/obj/struct_with_invalid_field.zig +++ b/test/compile_errors/stage1/obj/struct_with_invalid_field.zig @@ -23,6 +23,8 @@ export fn entry() void { _ = a; } -// struct with invalid field +// error +// backend=stage1 +// target=native // // tmp.zig:14:17: error: use of undeclared identifier 'HeaderValue' diff --git a/test/compile_errors/stage1/obj/sub_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/sub_assign_on_undefined_value.zig index 01ef9313e5..74b705a154 100644 --- a/test/compile_errors/stage1/obj/sub_assign_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/sub_assign_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { a -= a; } -// sub assign on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/sub_on_undefined_value.zig b/test/compile_errors/stage1/obj/sub_on_undefined_value.zig index 80746abf0e..08c590ee3f 100644 --- a/test/compile_errors/stage1/obj/sub_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/sub_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = a - a; } -// sub on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/sub_overflow_in_function_evaluation.zig b/test/compile_errors/stage1/obj/sub_overflow_in_function_evaluation.zig index 1084f1a111..dc259e6dcf 100644 --- a/test/compile_errors/stage1/obj/sub_overflow_in_function_evaluation.zig +++ b/test/compile_errors/stage1/obj/sub_overflow_in_function_evaluation.zig @@ -5,6 +5,8 @@ fn sub(a: u16, b: u16) u16 { export fn entry() usize { return @sizeOf(@TypeOf(y)); } -// sub overflow in function evaluation +// error +// backend=stage1 +// target=native // // tmp.zig:3:14: error: operation caused overflow diff --git a/test/compile_errors/stage1/obj/sub_wrap_assign_on_undefined_value.zig b/test/compile_errors/stage1/obj/sub_wrap_assign_on_undefined_value.zig index 3c7fd4faa5..41aa1ca14b 100644 --- a/test/compile_errors/stage1/obj/sub_wrap_assign_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/sub_wrap_assign_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { a -%= a; } -// sub wrap assign on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/sub_wrap_on_undefined_value.zig b/test/compile_errors/stage1/obj/sub_wrap_on_undefined_value.zig index 367b4c9c0e..68e4e78512 100644 --- a/test/compile_errors/stage1/obj/sub_wrap_on_undefined_value.zig +++ b/test/compile_errors/stage1/obj/sub_wrap_on_undefined_value.zig @@ -3,6 +3,8 @@ comptime { _ = a -% a; } -// sub wrap on undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/suspend_inside_suspend_block.zig b/test/compile_errors/stage1/obj/suspend_inside_suspend_block.zig index 2e807268ce..4adffd2f25 100644 --- a/test/compile_errors/stage1/obj/suspend_inside_suspend_block.zig +++ b/test/compile_errors/stage1/obj/suspend_inside_suspend_block.zig @@ -8,7 +8,9 @@ fn foo() void { } } -// suspend inside suspend block +// error +// backend=stage1 +// target=native // // tmp.zig:6:9: error: cannot suspend inside suspend block // tmp.zig:5:5: note: other suspend block here diff --git a/test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong.zig b/test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong.zig index c5db39f5b2..056aebe3dd 100644 --- a/test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong.zig +++ b/test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong.zig @@ -16,7 +16,9 @@ fn f(n: Number) i32 { export fn entry() usize { return @sizeOf(@TypeOf(f)); } -// switch expression - duplicate enumeration prong +// error +// backend=stage1 +// target=native // // tmp.zig:13:15: error: duplicate switch value // tmp.zig:10:15: note: other value here diff --git a/test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong_when_else_present.zig b/test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong_when_else_present.zig index 22a0c189c1..4d891c3917 100644 --- a/test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong_when_else_present.zig +++ b/test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong_when_else_present.zig @@ -17,7 +17,9 @@ fn f(n: Number) i32 { export fn entry() usize { return @sizeOf(@TypeOf(f)); } -// switch expression - duplicate enumeration prong when else present +// error +// backend=stage1 +// target=native // // tmp.zig:13:15: error: duplicate switch value // tmp.zig:10:15: note: other value here diff --git a/test/compile_errors/stage1/obj/switch_expression-duplicate_or_overlapping_integer_value.zig b/test/compile_errors/stage1/obj/switch_expression-duplicate_or_overlapping_integer_value.zig index 06ef7de62e..2810bc13e1 100644 --- a/test/compile_errors/stage1/obj/switch_expression-duplicate_or_overlapping_integer_value.zig +++ b/test/compile_errors/stage1/obj/switch_expression-duplicate_or_overlapping_integer_value.zig @@ -8,7 +8,9 @@ fn foo(x: u8) u8 { } export fn entry() usize { return @sizeOf(@TypeOf(foo)); } -// switch expression - duplicate or overlapping integer value +// error +// backend=stage1 +// target=native // // tmp.zig:6:9: error: duplicate switch value // tmp.zig:5:14: note: previous value here diff --git a/test/compile_errors/stage1/obj/switch_expression-duplicate_type.zig b/test/compile_errors/stage1/obj/switch_expression-duplicate_type.zig index 9ac9feaeaf..b84c4622a2 100644 --- a/test/compile_errors/stage1/obj/switch_expression-duplicate_type.zig +++ b/test/compile_errors/stage1/obj/switch_expression-duplicate_type.zig @@ -9,7 +9,9 @@ fn foo(comptime T: type, x: T) u8 { } export fn entry() usize { return @sizeOf(@TypeOf(foo(u32, 0))); } -// switch expression - duplicate type +// error +// backend=stage1 +// target=native // // tmp.zig:6:9: error: duplicate switch value // tmp.zig:4:9: note: previous value here diff --git a/test/compile_errors/stage1/obj/switch_expression-duplicate_type_struct_alias.zig b/test/compile_errors/stage1/obj/switch_expression-duplicate_type_struct_alias.zig index b92336cac5..057e674e5a 100644 --- a/test/compile_errors/stage1/obj/switch_expression-duplicate_type_struct_alias.zig +++ b/test/compile_errors/stage1/obj/switch_expression-duplicate_type_struct_alias.zig @@ -13,7 +13,9 @@ fn foo(comptime T: type, x: T) u8 { } export fn entry() usize { return @sizeOf(@TypeOf(foo(u32, 0))); } -// switch expression - duplicate type (struct alias) +// error +// backend=stage1 +// target=native // // tmp.zig:10:9: error: duplicate switch value // tmp.zig:8:9: note: previous value here diff --git a/test/compile_errors/stage1/obj/switch_expression-missing_enumeration_prong.zig b/test/compile_errors/stage1/obj/switch_expression-missing_enumeration_prong.zig index b6cd60c22b..1a13730223 100644 --- a/test/compile_errors/stage1/obj/switch_expression-missing_enumeration_prong.zig +++ b/test/compile_errors/stage1/obj/switch_expression-missing_enumeration_prong.zig @@ -14,6 +14,8 @@ fn f(n: Number) i32 { export fn entry() usize { return @sizeOf(@TypeOf(f)); } -// switch expression - missing enumeration prong +// error +// backend=stage1 +// target=native // // tmp.zig:8:5: error: enumeration value 'Number.Four' not handled in switch diff --git a/test/compile_errors/stage1/obj/switch_expression-multiple_else_prongs.zig b/test/compile_errors/stage1/obj/switch_expression-multiple_else_prongs.zig index 6a7e274b56..8267222092 100644 --- a/test/compile_errors/stage1/obj/switch_expression-multiple_else_prongs.zig +++ b/test/compile_errors/stage1/obj/switch_expression-multiple_else_prongs.zig @@ -9,6 +9,8 @@ export fn entry() void { f(1234); } -// switch expression - multiple else prongs +// error +// backend=stage1 +// target=native // // tmp.zig:5:9: error: multiple else prongs in switch expression diff --git a/test/compile_errors/stage1/obj/switch_expression-non_exhaustive_integer_prongs.zig b/test/compile_errors/stage1/obj/switch_expression-non_exhaustive_integer_prongs.zig index 88fb8548de..940338edb9 100644 --- a/test/compile_errors/stage1/obj/switch_expression-non_exhaustive_integer_prongs.zig +++ b/test/compile_errors/stage1/obj/switch_expression-non_exhaustive_integer_prongs.zig @@ -5,6 +5,8 @@ fn foo(x: u8) void { } export fn entry() usize { return @sizeOf(@TypeOf(foo)); } -// switch expression - non exhaustive integer prongs +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: switch must handle all possibilities diff --git a/test/compile_errors/stage1/obj/switch_expression-switch_on_pointer_type_with_no_else.zig b/test/compile_errors/stage1/obj/switch_expression-switch_on_pointer_type_with_no_else.zig index ec7565b882..6a357beabe 100644 --- a/test/compile_errors/stage1/obj/switch_expression-switch_on_pointer_type_with_no_else.zig +++ b/test/compile_errors/stage1/obj/switch_expression-switch_on_pointer_type_with_no_else.zig @@ -6,6 +6,8 @@ fn foo(x: *u8) void { const y: u8 = 100; export fn entry() usize { return @sizeOf(@TypeOf(foo)); } -// switch expression - switch on pointer type with no else +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: else prong required when switching on type '*u8' diff --git a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_bool.zig b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_bool.zig index 8c36a3c289..2ee39f1531 100644 --- a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_bool.zig +++ b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_bool.zig @@ -7,6 +7,8 @@ fn foo(x: bool) void { } export fn entry() usize { return @sizeOf(@TypeOf(foo)); } -// switch expression - unreachable else prong (bool) +// error +// backend=stage1 +// target=native // // tmp.zig:5:9: error: unreachable else prong, all cases already handled diff --git a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_enum.zig b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_enum.zig index 8207d05234..bcf840ab97 100644 --- a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_enum.zig +++ b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_enum.zig @@ -17,6 +17,8 @@ fn foo(x: u8) void { export fn entry() usize { return @sizeOf(@TypeOf(foo)); } -// switch expression - unreachable else prong (enum) +// error +// backend=stage1 +// target=native // // tmp.zig:14:9: error: unreachable else prong, all cases already handled diff --git a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_i8.zig b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_i8.zig index 8750e57fae..f0c9d7a08a 100644 --- a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_i8.zig +++ b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_i8.zig @@ -10,6 +10,8 @@ fn foo(x: i8) void { } export fn entry() usize { return @sizeOf(@TypeOf(foo)); } -// switch expression - unreachable else prong (range i8) +// error +// backend=stage1 +// target=native // // tmp.zig:8:9: error: unreachable else prong, all cases already handled diff --git a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_u8.zig b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_u8.zig index 280663ae51..9342ce8c32 100644 --- a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_u8.zig +++ b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_u8.zig @@ -10,6 +10,8 @@ fn foo(x: u8) void { } export fn entry() usize { return @sizeOf(@TypeOf(foo)); } -// switch expression - unreachable else prong (range u8) +// error +// backend=stage1 +// target=native // // tmp.zig:8:9: error: unreachable else prong, all cases already handled diff --git a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u1.zig b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u1.zig index 58c4f378bf..90abf5074b 100644 --- a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u1.zig +++ b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u1.zig @@ -7,6 +7,8 @@ fn foo(x: u1) void { } export fn entry() usize { return @sizeOf(@TypeOf(foo)); } -// switch expression - unreachable else prong (u1) +// error +// backend=stage1 +// target=native // // tmp.zig:5:9: error: unreachable else prong, all cases already handled diff --git a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u2.zig b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u2.zig index ec1804316e..a523046e16 100644 --- a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u2.zig +++ b/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u2.zig @@ -9,6 +9,8 @@ fn foo(x: u2) void { } export fn entry() usize { return @sizeOf(@TypeOf(foo)); } -// switch expression - unreachable else prong (u2) +// error +// backend=stage1 +// target=native // // tmp.zig:7:9: error: unreachable else prong, all cases already handled diff --git a/test/compile_errors/stage1/obj/switch_on_enum_with_1_field_with_no_prongs.zig b/test/compile_errors/stage1/obj/switch_on_enum_with_1_field_with_no_prongs.zig index fbfc039f59..97f7de121d 100644 --- a/test/compile_errors/stage1/obj/switch_on_enum_with_1_field_with_no_prongs.zig +++ b/test/compile_errors/stage1/obj/switch_on_enum_with_1_field_with_no_prongs.zig @@ -5,6 +5,8 @@ export fn entry() void { switch (f) {} } -// switch on enum with 1 field with no prongs +// error +// backend=stage1 +// target=native // // tmp.zig:5:5: error: enumeration value 'Foo.M' not handled in switch diff --git a/test/compile_errors/stage1/obj/switch_on_union_with_no_attached_enum.zig b/test/compile_errors/stage1/obj/switch_on_union_with_no_attached_enum.zig index f1ae750b4b..684861a898 100644 --- a/test/compile_errors/stage1/obj/switch_on_union_with_no_attached_enum.zig +++ b/test/compile_errors/stage1/obj/switch_on_union_with_no_attached_enum.zig @@ -14,7 +14,9 @@ fn foo(a: *const Payload) void { } } -// switch on union with no attached enum +// error +// backend=stage1 +// target=native // // tmp.zig:11:14: error: switch on union which has no attached enum // tmp.zig:1:17: note: consider 'union(enum)' here diff --git a/test/compile_errors/stage1/obj/switch_with_invalid_expression_parameter.zig b/test/compile_errors/stage1/obj/switch_with_invalid_expression_parameter.zig index f65879bc7a..1eb74519f5 100644 --- a/test/compile_errors/stage1/obj/switch_with_invalid_expression_parameter.zig +++ b/test/compile_errors/stage1/obj/switch_with_invalid_expression_parameter.zig @@ -10,6 +10,8 @@ fn Test(comptime T: type) void { _ = x; } -// switch with invalid expression parameter +// error +// backend=stage1 +// target=native // // tmp.zig:7:17: error: switch on type 'type' provides no expression parameter diff --git a/test/compile_errors/stage1/obj/switch_with_overlapping_case_ranges.zig b/test/compile_errors/stage1/obj/switch_with_overlapping_case_ranges.zig index 2818d766a7..1b03d3fe54 100644 --- a/test/compile_errors/stage1/obj/switch_with_overlapping_case_ranges.zig +++ b/test/compile_errors/stage1/obj/switch_with_overlapping_case_ranges.zig @@ -6,6 +6,8 @@ export fn entry() void { } } -// switch with overlapping case ranges +// error +// backend=stage1 +// target=native // // tmp.zig:5:9: error: duplicate switch value diff --git a/test/compile_errors/stage1/obj/tagName_used_on_union_with_no_associated_enum_tag.zig b/test/compile_errors/stage1/obj/tagName_used_on_union_with_no_associated_enum_tag.zig index bd03a97cfd..efd9b8ceef 100644 --- a/test/compile_errors/stage1/obj/tagName_used_on_union_with_no_associated_enum_tag.zig +++ b/test/compile_errors/stage1/obj/tagName_used_on_union_with_no_associated_enum_tag.zig @@ -8,7 +8,9 @@ export fn entry() void { _ = tagName; } -// @tagName used on union with no associated enum tag +// error +// backend=stage1 +// target=native // // tmp.zig:7:19: error: union has no associated enum // tmp.zig:1:18: note: declared here diff --git a/test/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig b/test/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig index 9beda30e2f..c039be3737 100644 --- a/test/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig +++ b/test/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = x; } -// take slice of invalid dereference +// error +// backend=stage1 +// target=native // // tmp.zig:2:18: error: attempt to dereference non-pointer type 'comptime_int' diff --git a/test/compile_errors/stage1/obj/taking_bit_offset_of_void_field_in_struct.zig b/test/compile_errors/stage1/obj/taking_bit_offset_of_void_field_in_struct.zig index 936a110a61..e404c3bdd6 100644 --- a/test/compile_errors/stage1/obj/taking_bit_offset_of_void_field_in_struct.zig +++ b/test/compile_errors/stage1/obj/taking_bit_offset_of_void_field_in_struct.zig @@ -6,6 +6,8 @@ export fn foo() void { _ = fieldOffset; } -// taking bit offset of void field in struct +// error +// backend=stage1 +// target=native // // tmp.zig:5:45: error: zero-bit field 'val' in struct 'Empty' has no offset diff --git a/test/compile_errors/stage1/obj/taking_byte_offset_of_void_field_in_struct.zig b/test/compile_errors/stage1/obj/taking_byte_offset_of_void_field_in_struct.zig index d71dbc1502..eb7d83efff 100644 --- a/test/compile_errors/stage1/obj/taking_byte_offset_of_void_field_in_struct.zig +++ b/test/compile_errors/stage1/obj/taking_byte_offset_of_void_field_in_struct.zig @@ -6,6 +6,8 @@ export fn foo() void { _ = fieldOffset; } -// taking byte offset of void field in struct +// error +// backend=stage1 +// target=native // // tmp.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset diff --git a/test/compile_errors/stage1/obj/threadlocal_qualifier_on_const.zig b/test/compile_errors/stage1/obj/threadlocal_qualifier_on_const.zig index fa99e592ae..d34ba06879 100644 --- a/test/compile_errors/stage1/obj/threadlocal_qualifier_on_const.zig +++ b/test/compile_errors/stage1/obj/threadlocal_qualifier_on_const.zig @@ -3,6 +3,8 @@ export fn entry() i32 { return x; } -// threadlocal qualifier on const +// error +// backend=stage1 +// target=native // // tmp.zig:1:1: error: threadlocal variable cannot be constant diff --git a/test/compile_errors/stage1/obj/top_level_decl_dependency_loop.zig b/test/compile_errors/stage1/obj/top_level_decl_dependency_loop.zig index 4ecdc6f67d..ac70285c9c 100644 --- a/test/compile_errors/stage1/obj/top_level_decl_dependency_loop.zig +++ b/test/compile_errors/stage1/obj/top_level_decl_dependency_loop.zig @@ -5,6 +5,8 @@ export fn entry() void { _ = c; } -// top level decl dependency loop +// error +// backend=stage1 +// target=native // // tmp.zig:2:19: error: dependency loop detected diff --git a/test/compile_errors/stage1/obj/truncate_sign_mismatch.zig b/test/compile_errors/stage1/obj/truncate_sign_mismatch.zig index a60ca4bc7a..8434d9ed16 100644 --- a/test/compile_errors/stage1/obj/truncate_sign_mismatch.zig +++ b/test/compile_errors/stage1/obj/truncate_sign_mismatch.zig @@ -15,7 +15,9 @@ export fn entry4() u8 { return @truncate(u8, x); } -// truncate sign mismatch +// error +// backend=stage1 +// target=native // // tmp.zig:3:26: error: expected signed integer type, found 'u32' // tmp.zig:7:26: error: expected unsigned integer type, found 'i32' diff --git a/test/compile_errors/stage1/obj/truncate_undefined_value.zig b/test/compile_errors/stage1/obj/truncate_undefined_value.zig index 9d3913f9c3..67a1f17ba7 100644 --- a/test/compile_errors/stage1/obj/truncate_undefined_value.zig +++ b/test/compile_errors/stage1/obj/truncate_undefined_value.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = z; } -// @truncate undefined value +// error +// backend=stage1 +// target=native // // tmp.zig:2:27: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/try_in_function_with_non_error_return_type.zig b/test/compile_errors/stage1/obj/try_in_function_with_non_error_return_type.zig index d799f769b6..c00fa2709f 100644 --- a/test/compile_errors/stage1/obj/try_in_function_with_non_error_return_type.zig +++ b/test/compile_errors/stage1/obj/try_in_function_with_non_error_return_type.zig @@ -3,6 +3,8 @@ export fn f() void { } fn something() anyerror!void { } -// try in function with non error return type +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: expected type 'void', found 'anyerror' diff --git a/test/compile_errors/stage1/obj/type_checking_function_pointers.zig b/test/compile_errors/stage1/obj/type_checking_function_pointers.zig index d9bcde6bab..b88b6bdb52 100644 --- a/test/compile_errors/stage1/obj/type_checking_function_pointers.zig +++ b/test/compile_errors/stage1/obj/type_checking_function_pointers.zig @@ -6,6 +6,8 @@ export fn entry() void { a(c); } -// type checking function pointers +// error +// backend=stage1 +// target=native // // tmp.zig:6:7: error: expected type 'fn(*const u8) void', found 'fn(u8) void' diff --git a/test/compile_errors/stage1/obj/type_variables_must_be_constant.zig b/test/compile_errors/stage1/obj/type_variables_must_be_constant.zig index 33846d6da8..f8cadf8520 100644 --- a/test/compile_errors/stage1/obj/type_variables_must_be_constant.zig +++ b/test/compile_errors/stage1/obj/type_variables_must_be_constant.zig @@ -3,6 +3,8 @@ export fn entry() foo { return 1; } -// type variables must be constant +// error +// backend=stage1 +// target=native // // tmp.zig:1:1: error: variable of type 'type' must be constant diff --git a/test/compile_errors/stage1/obj/undeclared_identifier.zig b/test/compile_errors/stage1/obj/undeclared_identifier.zig index 36f46a22f4..297a618eb8 100644 --- a/test/compile_errors/stage1/obj/undeclared_identifier.zig +++ b/test/compile_errors/stage1/obj/undeclared_identifier.zig @@ -4,6 +4,8 @@ export fn a() void { c; } -// undeclared identifier +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: use of undeclared identifier 'b' diff --git a/test/compile_errors/stage1/obj/undeclared_identifier_error_should_mark_fn_as_impure.zig b/test/compile_errors/stage1/obj/undeclared_identifier_error_should_mark_fn_as_impure.zig index fa629bdcb8..697b7ccb55 100644 --- a/test/compile_errors/stage1/obj/undeclared_identifier_error_should_mark_fn_as_impure.zig +++ b/test/compile_errors/stage1/obj/undeclared_identifier_error_should_mark_fn_as_impure.zig @@ -5,6 +5,8 @@ fn test_a_thing() void { bad_fn_call(); } -// undeclared identifier error should mark fn as impure +// error +// backend=stage1 +// target=native // // tmp.zig:5:5: error: use of undeclared identifier 'bad_fn_call' diff --git a/test/compile_errors/stage1/obj/undeclared_identifier_in_unanalyzed_branch.zig b/test/compile_errors/stage1/obj/undeclared_identifier_in_unanalyzed_branch.zig index 55d952e0fd..2efa8ca115 100644 --- a/test/compile_errors/stage1/obj/undeclared_identifier_in_unanalyzed_branch.zig +++ b/test/compile_errors/stage1/obj/undeclared_identifier_in_unanalyzed_branch.zig @@ -4,6 +4,8 @@ export fn a() void { } } -// undeclared identifier in unanalyzed branch +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: use of undeclared identifier 'lol_this_doesnt_exist' diff --git a/test/compile_errors/stage1/obj/undefined_as_field_type_is_rejected.zig b/test/compile_errors/stage1/obj/undefined_as_field_type_is_rejected.zig index 4035955387..b6eb059661 100644 --- a/test/compile_errors/stage1/obj/undefined_as_field_type_is_rejected.zig +++ b/test/compile_errors/stage1/obj/undefined_as_field_type_is_rejected.zig @@ -6,6 +6,8 @@ export fn entry1() void { _ = foo; } -// undefined as field type is rejected +// error +// backend=stage1 +// target=native // // tmp.zig:2:8: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/undefined_function_call.zig b/test/compile_errors/stage1/obj/undefined_function_call.zig index ebf76aafbd..a9627961fe 100644 --- a/test/compile_errors/stage1/obj/undefined_function_call.zig +++ b/test/compile_errors/stage1/obj/undefined_function_call.zig @@ -2,6 +2,8 @@ export fn a() void { b(); } -// undefined function call +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: use of undeclared identifier 'b' diff --git a/test/compile_errors/stage1/obj/underscore_is_not_a_declarable_symbol.zig b/test/compile_errors/stage1/obj/underscore_is_not_a_declarable_symbol.zig index bfcfbc3bcc..f4318af8fd 100644 --- a/test/compile_errors/stage1/obj/underscore_is_not_a_declarable_symbol.zig +++ b/test/compile_errors/stage1/obj/underscore_is_not_a_declarable_symbol.zig @@ -3,6 +3,8 @@ export fn f1() usize { return _; } -// `_` is not a declarable symbol +// error +// backend=stage1 +// target=native // // tmp.zig:2:9: error: '_' used as an identifier without @"_" syntax diff --git a/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_for.zig b/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_for.zig index be2ae559f3..fe89c8372d 100644 --- a/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_for.zig +++ b/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_for.zig @@ -6,6 +6,8 @@ export fn returns() void { } } -// `_` should not be usable inside for +// error +// backend=stage1 +// target=native // // tmp.zig:4:20: error: '_' used as an identifier without @"_" syntax diff --git a/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while.zig b/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while.zig index d93ed28c9e..7f694e0603 100644 --- a/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while.zig +++ b/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while.zig @@ -9,6 +9,8 @@ fn optionalReturn() ?u32 { return 1; } -// `_` should not be usable inside while +// error +// backend=stage1 +// target=native // // tmp.zig:4:20: error: '_' used as an identifier without @"_" syntax diff --git a/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while_else.zig b/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while_else.zig index e1e4c7b4c1..87a705ee69 100644 --- a/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while_else.zig +++ b/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while_else.zig @@ -11,6 +11,8 @@ fn optionalReturnError() !?u32 { return error.optionalReturnError; } -// `_` should not be usable inside while else +// error +// backend=stage1 +// target=native // // tmp.zig:6:17: error: '_' used as an identifier without @"_" syntax diff --git a/test/compile_errors/stage1/obj/union_auto-enum_value_already_taken.zig b/test/compile_errors/stage1/obj/union_auto-enum_value_already_taken.zig index 377290bd7e..3b00f95ff9 100644 --- a/test/compile_errors/stage1/obj/union_auto-enum_value_already_taken.zig +++ b/test/compile_errors/stage1/obj/union_auto-enum_value_already_taken.zig @@ -10,7 +10,9 @@ export fn entry() void { _ = x; } -// union auto-enum value already taken +// error +// backend=stage1 +// target=native // // tmp.zig:6:9: error: enum tag value 60 already taken // tmp.zig:4:9: note: other occurrence here diff --git a/test/compile_errors/stage1/obj/union_enum_field_does_not_match_enum.zig b/test/compile_errors/stage1/obj/union_enum_field_does_not_match_enum.zig index a924932c9f..38260091dc 100644 --- a/test/compile_errors/stage1/obj/union_enum_field_does_not_match_enum.zig +++ b/test/compile_errors/stage1/obj/union_enum_field_does_not_match_enum.zig @@ -14,7 +14,9 @@ export fn entry() void { _ = a; } -// union enum field does not match enum +// error +// backend=stage1 +// target=native // // tmp.zig:10:5: error: enum field not found: 'D' // tmp.zig:1:16: note: enum declared here diff --git a/test/compile_errors/stage1/obj/union_fields_with_value_assignments.zig b/test/compile_errors/stage1/obj/union_fields_with_value_assignments.zig index 0cf67a0eca..94568d55c3 100644 --- a/test/compile_errors/stage1/obj/union_fields_with_value_assignments.zig +++ b/test/compile_errors/stage1/obj/union_fields_with_value_assignments.zig @@ -6,7 +6,9 @@ export fn entry() void { _ = x; } -// union fields with value assignments +// error +// backend=stage1 +// target=native // // tmp.zig:1:24: error: explicitly valued tagged union missing integer tag type // tmp.zig:2:14: note: tag value specified here diff --git a/test/compile_errors/stage1/obj/union_with_0_fields.zig b/test/compile_errors/stage1/obj/union_with_0_fields.zig index 36086e8cce..a6f3d69faa 100644 --- a/test/compile_errors/stage1/obj/union_with_0_fields.zig +++ b/test/compile_errors/stage1/obj/union_with_0_fields.zig @@ -1,5 +1,7 @@ const Foo = union {}; -// union with 0 fields +// error +// backend=stage1 +// target=native // // tmp.zig:1:13: error: union declarations must have at least one tag diff --git a/test/compile_errors/stage1/obj/union_with_specified_enum_omits_field.zig b/test/compile_errors/stage1/obj/union_with_specified_enum_omits_field.zig index c1c1d38f02..0bb677a18c 100644 --- a/test/compile_errors/stage1/obj/union_with_specified_enum_omits_field.zig +++ b/test/compile_errors/stage1/obj/union_with_specified_enum_omits_field.zig @@ -11,7 +11,9 @@ export fn entry() usize { return @sizeOf(Payload); } -// union with specified enum omits field +// error +// backend=stage1 +// target=native // // tmp.zig:6:17: error: enum field missing: 'C' // tmp.zig:4:5: note: declared here diff --git a/test/compile_errors/stage1/obj/union_with_too_small_explicit_signed_tag_type.zig b/test/compile_errors/stage1/obj/union_with_too_small_explicit_signed_tag_type.zig index a3e75c25f6..97f4d48f6b 100644 --- a/test/compile_errors/stage1/obj/union_with_too_small_explicit_signed_tag_type.zig +++ b/test/compile_errors/stage1/obj/union_with_too_small_explicit_signed_tag_type.zig @@ -8,7 +8,9 @@ export fn entry() void { _ = U{ .D = 1 }; } -// union with too small explicit signed tag type +// error +// backend=stage1 +// target=native // // tmp.zig:1:22: error: specified integer tag type cannot represent every field // tmp.zig:1:22: note: type i2 cannot fit values in range 0...3 diff --git a/test/compile_errors/stage1/obj/union_with_too_small_explicit_unsigned_tag_type.zig b/test/compile_errors/stage1/obj/union_with_too_small_explicit_unsigned_tag_type.zig index 6f13dcda8a..c1496ade86 100644 --- a/test/compile_errors/stage1/obj/union_with_too_small_explicit_unsigned_tag_type.zig +++ b/test/compile_errors/stage1/obj/union_with_too_small_explicit_unsigned_tag_type.zig @@ -9,7 +9,9 @@ export fn entry() void { _ = U{ .E = 1 }; } -// union with too small explicit unsigned tag type +// error +// backend=stage1 +// target=native // // tmp.zig:1:22: error: specified integer tag type cannot represent every field // tmp.zig:1:22: note: type u2 cannot fit values in range 0...4 diff --git a/test/compile_errors/stage1/obj/unknown_length_pointer_to_opaque.zig b/test/compile_errors/stage1/obj/unknown_length_pointer_to_opaque.zig index a847bae599..d155b986d7 100644 --- a/test/compile_errors/stage1/obj/unknown_length_pointer_to_opaque.zig +++ b/test/compile_errors/stage1/obj/unknown_length_pointer_to_opaque.zig @@ -1,5 +1,7 @@ export const T = [*]opaque {}; -// unknown length pointer to opaque +// error +// backend=stage1 +// target=native // // tmp.zig:1:21: error: unknown-length pointer to opaque diff --git a/test/compile_errors/stage1/obj/unreachable_code-double_break.zig b/test/compile_errors/stage1/obj/unreachable_code-double_break.zig index b030c1d24d..0a19337af8 100644 --- a/test/compile_errors/stage1/obj/unreachable_code-double_break.zig +++ b/test/compile_errors/stage1/obj/unreachable_code-double_break.zig @@ -4,7 +4,9 @@ export fn a() void { }; } -// unreachable code - double break +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: unreachable code // tmp.zig:3:20: note: control flow is diverted here diff --git a/test/compile_errors/stage1/obj/unreachable_code-nested_returns.zig b/test/compile_errors/stage1/obj/unreachable_code-nested_returns.zig index 7bfa8eef64..8a80fcd161 100644 --- a/test/compile_errors/stage1/obj/unreachable_code-nested_returns.zig +++ b/test/compile_errors/stage1/obj/unreachable_code-nested_returns.zig @@ -2,7 +2,9 @@ export fn a() i32 { return return 1; } -// unreachable code - nested returns +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: unreachable code // tmp.zig:2:12: note: control flow is diverted here diff --git a/test/compile_errors/stage1/obj/unreachable_code.zig b/test/compile_errors/stage1/obj/unreachable_code.zig index 779cd90379..30a92d06bf 100644 --- a/test/compile_errors/stage1/obj/unreachable_code.zig +++ b/test/compile_errors/stage1/obj/unreachable_code.zig @@ -5,7 +5,9 @@ export fn a() void { fn b() void {} -// unreachable code +// error +// backend=stage1 +// target=native // // tmp.zig:3:6: error: unreachable code // tmp.zig:2:5: note: control flow is diverted here diff --git a/test/compile_errors/stage1/obj/unreachable_executed_at_comptime.zig b/test/compile_errors/stage1/obj/unreachable_executed_at_comptime.zig index 857ab0417f..34cb354e94 100644 --- a/test/compile_errors/stage1/obj/unreachable_executed_at_comptime.zig +++ b/test/compile_errors/stage1/obj/unreachable_executed_at_comptime.zig @@ -8,7 +8,9 @@ export fn entry() void { _ = foo(-42); } -// unreachable executed at comptime +// error +// backend=stage1 +// target=native // // tmp.zig:4:9: error: reached unreachable code // tmp.zig:8:12: note: called from here diff --git a/test/compile_errors/stage1/obj/unreachable_parameter.zig b/test/compile_errors/stage1/obj/unreachable_parameter.zig index 3feed62a1e..296194f7e3 100644 --- a/test/compile_errors/stage1/obj/unreachable_parameter.zig +++ b/test/compile_errors/stage1/obj/unreachable_parameter.zig @@ -1,6 +1,8 @@ fn f(a: noreturn) void { _ = a; } export fn entry() void { f(); } -// unreachable parameter +// error +// backend=stage1 +// target=native // // tmp.zig:1:9: error: parameter of type 'noreturn' not allowed diff --git a/test/compile_errors/stage1/obj/unreachable_variable.zig b/test/compile_errors/stage1/obj/unreachable_variable.zig index 8e053acd06..7f822a8bce 100644 --- a/test/compile_errors/stage1/obj/unreachable_variable.zig +++ b/test/compile_errors/stage1/obj/unreachable_variable.zig @@ -3,6 +3,8 @@ export fn f() void { _ = a; } -// unreachable variable +// error +// backend=stage1 +// target=native // // tmp.zig:2:25: error: expected type 'noreturn', found 'void' diff --git a/test/compile_errors/stage1/obj/unreachable_with_return.zig b/test/compile_errors/stage1/obj/unreachable_with_return.zig index ac9ecc93e3..6410d24913 100644 --- a/test/compile_errors/stage1/obj/unreachable_with_return.zig +++ b/test/compile_errors/stage1/obj/unreachable_with_return.zig @@ -1,6 +1,8 @@ fn a() noreturn {return;} export fn entry() void { a(); } -// unreachable with return +// error +// backend=stage1 +// target=native // // tmp.zig:1:18: error: expected type 'noreturn', found 'void' diff --git a/test/compile_errors/stage1/obj/unsupported_modifier_at_start_of_asm_output_constraint.zig b/test/compile_errors/stage1/obj/unsupported_modifier_at_start_of_asm_output_constraint.zig index c85850a61b..7c70fc5095 100644 --- a/test/compile_errors/stage1/obj/unsupported_modifier_at_start_of_asm_output_constraint.zig +++ b/test/compile_errors/stage1/obj/unsupported_modifier_at_start_of_asm_output_constraint.zig @@ -3,6 +3,8 @@ export fn foo() void { asm volatile ("" : [baz]"+r"(bar) : : ""); } -// unsupported modifier at start of asm output constraint +// error +// backend=stage1 +// target=native // // tmp.zig:3:5: error: invalid modifier starting output constraint for 'baz': '+', only '=' is supported. Compiler TODO: see https://github.com/ziglang/zig/issues/215 diff --git a/test/compile_errors/stage1/obj/unused_variable_error_on_errdefer.zig b/test/compile_errors/stage1/obj/unused_variable_error_on_errdefer.zig index eb92776938..b85d5729dc 100644 --- a/test/compile_errors/stage1/obj/unused_variable_error_on_errdefer.zig +++ b/test/compile_errors/stage1/obj/unused_variable_error_on_errdefer.zig @@ -6,6 +6,8 @@ export fn entry() void { foo() catch unreachable; } -// unused variable error on errdefer +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: unused variable: 'a' diff --git a/test/compile_errors/stage1/obj/use_anyopaque_as_return_type_of_fn_ptr.zig b/test/compile_errors/stage1/obj/use_anyopaque_as_return_type_of_fn_ptr.zig index f54bc14914..ef2787ca75 100644 --- a/test/compile_errors/stage1/obj/use_anyopaque_as_return_type_of_fn_ptr.zig +++ b/test/compile_errors/stage1/obj/use_anyopaque_as_return_type_of_fn_ptr.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = a; } -// use anyopaque as return type of fn ptr +// error +// backend=stage1 +// target=native // // tmp.zig:2:20: error: return type cannot be opaque diff --git a/test/compile_errors/stage1/obj/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig b/test/compile_errors/stage1/obj/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig index a8e01dbde0..4263f38e3e 100644 --- a/test/compile_errors/stage1/obj/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig +++ b/test/compile_errors/stage1/obj/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig @@ -7,6 +7,8 @@ export fn entry() void { _ = y; } -// use implicit casts to assign null to non-nullable pointer +// error +// backend=stage1 +// target=native // // tmp.zig:4:23: error: expected type '*?*i32', found '**i32' diff --git a/test/compile_errors/stage1/obj/use_invalid_number_literal_as_array_index.zig b/test/compile_errors/stage1/obj/use_invalid_number_literal_as_array_index.zig index 5ec655737e..b1d0aaa6f4 100644 --- a/test/compile_errors/stage1/obj/use_invalid_number_literal_as_array_index.zig +++ b/test/compile_errors/stage1/obj/use_invalid_number_literal_as_array_index.zig @@ -4,6 +4,8 @@ export fn entry() void { _ = arr; } -// use invalid number literal as array index +// error +// backend=stage1 +// target=native // // tmp.zig:1:1: error: unable to infer variable type diff --git a/test/compile_errors/stage1/obj/use_of_comptime-known_undefined_function_value.zig b/test/compile_errors/stage1/obj/use_of_comptime-known_undefined_function_value.zig index 5facc7e753..3500b13dfc 100644 --- a/test/compile_errors/stage1/obj/use_of_comptime-known_undefined_function_value.zig +++ b/test/compile_errors/stage1/obj/use_of_comptime-known_undefined_function_value.zig @@ -6,6 +6,8 @@ export fn entry() void { command.exec(); } -// use of comptime-known undefined function value +// error +// backend=stage1 +// target=native // // tmp.zig:6:12: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/use_of_undeclared_identifier.zig b/test/compile_errors/stage1/obj/use_of_undeclared_identifier.zig index e36453acc7..c1297731a3 100644 --- a/test/compile_errors/stage1/obj/use_of_undeclared_identifier.zig +++ b/test/compile_errors/stage1/obj/use_of_undeclared_identifier.zig @@ -2,6 +2,8 @@ export fn f() void { b = 3; } -// use of undeclared identifier +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: use of undeclared identifier 'b' diff --git a/test/compile_errors/stage1/obj/using_an_unknown_len_ptr_type_instead_of_array.zig b/test/compile_errors/stage1/obj/using_an_unknown_len_ptr_type_instead_of_array.zig index 983995b098..3124b057a8 100644 --- a/test/compile_errors/stage1/obj/using_an_unknown_len_ptr_type_instead_of_array.zig +++ b/test/compile_errors/stage1/obj/using_an_unknown_len_ptr_type_instead_of_array.zig @@ -6,6 +6,8 @@ comptime { _ = resolutions; } -// using an unknown len ptr type instead of array +// error +// backend=stage1 +// target=native // // tmp.zig:1:21: error: expected array type or [_], found '[*][*]const u8' diff --git a/test/compile_errors/stage1/obj/using_invalid_types_in_function_call_raises_an_error.zig b/test/compile_errors/stage1/obj/using_invalid_types_in_function_call_raises_an_error.zig index 182b5c59b0..f1fda15ea7 100644 --- a/test/compile_errors/stage1/obj/using_invalid_types_in_function_call_raises_an_error.zig +++ b/test/compile_errors/stage1/obj/using_invalid_types_in_function_call_raises_an_error.zig @@ -4,6 +4,8 @@ export fn entry() void { func(MenuEffect.ThisDoesNotExist); } -// using invalid types in function call raises an error +// error +// backend=stage1 +// target=native // // tmp.zig:1:20: error: enum declarations must have at least one tag diff --git a/test/compile_errors/stage1/obj/usingnamespace_with_wrong_type.zig b/test/compile_errors/stage1/obj/usingnamespace_with_wrong_type.zig index a74a8e6124..86b5f0dcfb 100644 --- a/test/compile_errors/stage1/obj/usingnamespace_with_wrong_type.zig +++ b/test/compile_errors/stage1/obj/usingnamespace_with_wrong_type.zig @@ -1,5 +1,7 @@ usingnamespace void; -// usingnamespace with wrong type +// error +// backend=stage1 +// target=native // // tmp.zig:1:1: error: expected struct, enum, or union; found 'void' diff --git a/test/compile_errors/stage1/obj/variable_has_wrong_type.zig b/test/compile_errors/stage1/obj/variable_has_wrong_type.zig index 1e20dde269..52dcf150de 100644 --- a/test/compile_errors/stage1/obj/variable_has_wrong_type.zig +++ b/test/compile_errors/stage1/obj/variable_has_wrong_type.zig @@ -3,6 +3,8 @@ export fn f() i32 { return a; } -// variable has wrong type +// error +// backend=stage1 +// target=native // // tmp.zig:3:12: error: expected type 'i32', found '*const [1:0]u8' diff --git a/test/compile_errors/stage1/obj/variable_in_inline_assembly_template_cannot_be_found.zig b/test/compile_errors/stage1/obj/variable_in_inline_assembly_template_cannot_be_found.zig new file mode 100644 index 0000000000..7636b501fd --- /dev/null +++ b/test/compile_errors/stage1/obj/variable_in_inline_assembly_template_cannot_be_found.zig @@ -0,0 +1,12 @@ +export fn entry() void { + var sp = asm volatile ("mov %[foo], sp" + : [bar] "=r" (-> usize), + ); + _ = sp; +} + +// error +// backend=stage1 +// target=x86_64-linux-gnu +// +// tmp.zig:2:14: error: could not find 'foo' in the inputs or outputs diff --git a/test/compile_errors/stage1/obj/variable_initialization_compile_error_then_referenced.zig b/test/compile_errors/stage1/obj/variable_initialization_compile_error_then_referenced.zig index d96b67bc3f..91b70481a1 100644 --- a/test/compile_errors/stage1/obj/variable_initialization_compile_error_then_referenced.zig +++ b/test/compile_errors/stage1/obj/variable_initialization_compile_error_then_referenced.zig @@ -12,6 +12,8 @@ export fn entry() void { _ = S; } -// variable initialization compile error then referenced +// error +// backend=stage1 +// target=native // // tmp.zig:2:12: error: use of undeclared identifier 'T' diff --git a/test/compile_errors/stage1/obj/variable_with_type_noreturn.zig b/test/compile_errors/stage1/obj/variable_with_type_noreturn.zig index b6cc65593b..e2ba9183be 100644 --- a/test/compile_errors/stage1/obj/variable_with_type_noreturn.zig +++ b/test/compile_errors/stage1/obj/variable_with_type_noreturn.zig @@ -2,7 +2,9 @@ export fn entry9() void { var z: noreturn = return; } -// variable with type 'noreturn' +// error +// backend=stage1 +// target=native // // tmp.zig:2:5: error: unreachable code // tmp.zig:2:23: note: control flow is diverted here diff --git a/test/compile_errors/stage1/obj/vector_index_out_of_bounds.zig b/test/compile_errors/stage1/obj/vector_index_out_of_bounds.zig index efdd8b5fe9..fdffd8b455 100644 --- a/test/compile_errors/stage1/obj/vector_index_out_of_bounds.zig +++ b/test/compile_errors/stage1/obj/vector_index_out_of_bounds.zig @@ -3,6 +3,8 @@ export fn entry() void { _ = x; } -// vector index out of bounds +// error +// backend=stage1 +// target=native // // tmp.zig:2:62: error: index 3 outside vector of size 3 diff --git a/test/compile_errors/stage1/obj/volatile_on_global_assembly.zig b/test/compile_errors/stage1/obj/volatile_on_global_assembly.zig index 8fc7ddfcc2..2157fe6a71 100644 --- a/test/compile_errors/stage1/obj/volatile_on_global_assembly.zig +++ b/test/compile_errors/stage1/obj/volatile_on_global_assembly.zig @@ -2,6 +2,8 @@ comptime { asm volatile (""); } -// volatile on global assembly +// error +// backend=stage1 +// target=native // // tmp.zig:2:9: error: volatile is meaningless on global assembly diff --git a/test/compile_errors/stage1/obj/wasmMemoryGrow_is_a_compile_error_in_non-Wasm_targets.zig b/test/compile_errors/stage1/obj/wasmMemoryGrow_is_a_compile_error_in_non-Wasm_targets.zig index 363b36a0af..6eda076f12 100644 --- a/test/compile_errors/stage1/obj/wasmMemoryGrow_is_a_compile_error_in_non-Wasm_targets.zig +++ b/test/compile_errors/stage1/obj/wasmMemoryGrow_is_a_compile_error_in_non-Wasm_targets.zig @@ -3,6 +3,8 @@ export fn foo() void { return; } -// wasmMemoryGrow is a compile error in non-Wasm targets +// error +// backend=stage1 +// target=native // // tmp.zig:2:9: error: @wasmMemoryGrow is a wasm32 feature only diff --git a/test/compile_errors/stage1/obj/wasmMemorySize_is_a_compile_error_in_non-Wasm_targets.zig b/test/compile_errors/stage1/obj/wasmMemorySize_is_a_compile_error_in_non-Wasm_targets.zig index 419173bd01..9bdf81073f 100644 --- a/test/compile_errors/stage1/obj/wasmMemorySize_is_a_compile_error_in_non-Wasm_targets.zig +++ b/test/compile_errors/stage1/obj/wasmMemorySize_is_a_compile_error_in_non-Wasm_targets.zig @@ -3,6 +3,8 @@ export fn foo() void { return; } -// wasmMemorySize is a compile error in non-Wasm targets +// error +// backend=stage1 +// target=native // // tmp.zig:2:9: error: @wasmMemorySize is a wasm32 feature only diff --git a/test/compile_errors/stage1/obj/while_expected_bool_got_error_union.zig b/test/compile_errors/stage1/obj/while_expected_bool_got_error_union.zig index e79296694d..9fc327e6c9 100644 --- a/test/compile_errors/stage1/obj/while_expected_bool_got_error_union.zig +++ b/test/compile_errors/stage1/obj/while_expected_bool_got_error_union.zig @@ -3,6 +3,8 @@ export fn foo() void { } fn bar() anyerror!i32 { return 1; } -// while expected bool, got error union +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: expected type 'bool', found 'anyerror!i32' diff --git a/test/compile_errors/stage1/obj/while_expected_bool_got_optional.zig b/test/compile_errors/stage1/obj/while_expected_bool_got_optional.zig index 7a3d184a1d..5948d62786 100644 --- a/test/compile_errors/stage1/obj/while_expected_bool_got_optional.zig +++ b/test/compile_errors/stage1/obj/while_expected_bool_got_optional.zig @@ -3,6 +3,8 @@ export fn foo() void { } fn bar() ?i32 { return 1; } -// while expected bool, got optional +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: expected type 'bool', found '?i32' diff --git a/test/compile_errors/stage1/obj/while_expected_error_union_got_bool.zig b/test/compile_errors/stage1/obj/while_expected_error_union_got_bool.zig index 8f92276af6..b8a72e9793 100644 --- a/test/compile_errors/stage1/obj/while_expected_error_union_got_bool.zig +++ b/test/compile_errors/stage1/obj/while_expected_error_union_got_bool.zig @@ -3,6 +3,8 @@ export fn foo() void { } fn bar() bool { return true; } -// while expected error union, got bool +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: expected error union type, found 'bool' diff --git a/test/compile_errors/stage1/obj/while_expected_error_union_got_optional.zig b/test/compile_errors/stage1/obj/while_expected_error_union_got_optional.zig index a67bf1ae16..c933dc9509 100644 --- a/test/compile_errors/stage1/obj/while_expected_error_union_got_optional.zig +++ b/test/compile_errors/stage1/obj/while_expected_error_union_got_optional.zig @@ -3,6 +3,8 @@ export fn foo() void { } fn bar() ?i32 { return 1; } -// while expected error union, got optional +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: expected error union type, found '?i32' diff --git a/test/compile_errors/stage1/obj/while_expected_optional_got_bool.zig b/test/compile_errors/stage1/obj/while_expected_optional_got_bool.zig index 6fc0d880ce..0458d1ba01 100644 --- a/test/compile_errors/stage1/obj/while_expected_optional_got_bool.zig +++ b/test/compile_errors/stage1/obj/while_expected_optional_got_bool.zig @@ -3,6 +3,8 @@ export fn foo() void { } fn bar() bool { return true; } -// while expected optional, got bool +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: expected optional type, found 'bool' diff --git a/test/compile_errors/stage1/obj/while_expected_optional_got_error_union.zig b/test/compile_errors/stage1/obj/while_expected_optional_got_error_union.zig index 472b3b81bc..7cdbd2cccf 100644 --- a/test/compile_errors/stage1/obj/while_expected_optional_got_error_union.zig +++ b/test/compile_errors/stage1/obj/while_expected_optional_got_error_union.zig @@ -3,6 +3,8 @@ export fn foo() void { } fn bar() anyerror!i32 { return 1; } -// while expected optional, got error union +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: expected optional type, found 'anyerror!i32' diff --git a/test/compile_errors/stage1/obj/while_loop_body_expression_ignored.zig b/test/compile_errors/stage1/obj/while_loop_body_expression_ignored.zig index 621c0d41d7..9542cbc62f 100644 --- a/test/compile_errors/stage1/obj/while_loop_body_expression_ignored.zig +++ b/test/compile_errors/stage1/obj/while_loop_body_expression_ignored.zig @@ -13,7 +13,9 @@ export fn f3() void { while (x) |_| returns() else |_| unreachable; } -// while loop body expression ignored +// error +// backend=stage1 +// target=native // // tmp.zig:5:25: error: expression value is ignored // tmp.zig:9:26: error: expression value is ignored diff --git a/test/compile_errors/stage1/obj/write_to_const_global_variable.zig b/test/compile_errors/stage1/obj/write_to_const_global_variable.zig index 327b3d02d0..4ce93e6b51 100644 --- a/test/compile_errors/stage1/obj/write_to_const_global_variable.zig +++ b/test/compile_errors/stage1/obj/write_to_const_global_variable.zig @@ -4,6 +4,8 @@ fn f() void { } export fn entry() void { f(); } -// write to const global variable +// error +// backend=stage1 +// target=native // // tmp.zig:3:9: error: cannot assign to constant diff --git a/test/compile_errors/stage1/obj/wrong_frame_type_used_for_async_call.zig b/test/compile_errors/stage1/obj/wrong_frame_type_used_for_async_call.zig index ba150c45cc..c02c20f495 100644 --- a/test/compile_errors/stage1/obj/wrong_frame_type_used_for_async_call.zig +++ b/test/compile_errors/stage1/obj/wrong_frame_type_used_for_async_call.zig @@ -9,6 +9,8 @@ fn bar() void { suspend {} } -// wrong frame type used for async call +// error +// backend=stage1 +// target=native // // tmp.zig:3:13: error: expected type '*@Frame(bar)', found '*@Frame(foo)' diff --git a/test/compile_errors/stage1/obj/wrong_function_type.zig b/test/compile_errors/stage1/obj/wrong_function_type.zig index c2238bb649..58f4477601 100644 --- a/test/compile_errors/stage1/obj/wrong_function_type.zig +++ b/test/compile_errors/stage1/obj/wrong_function_type.zig @@ -4,6 +4,8 @@ fn b() i32 {return 1;} fn c() i32 {return 2;} export fn entry() usize { return @sizeOf(@TypeOf(fns)); } -// wrong function type +// error +// backend=stage1 +// target=native // // tmp.zig:1:28: error: expected type 'fn() void', found 'fn() i32' diff --git a/test/compile_errors/stage1/obj/wrong_initializer_for_union_payload_of_type_type.zig b/test/compile_errors/stage1/obj/wrong_initializer_for_union_payload_of_type_type.zig index 1d56094b6d..2e7b8f2e40 100644 --- a/test/compile_errors/stage1/obj/wrong_initializer_for_union_payload_of_type_type.zig +++ b/test/compile_errors/stage1/obj/wrong_initializer_for_union_payload_of_type_type.zig @@ -9,6 +9,8 @@ export fn entry() void { v.u.A = U{ .A = i32 }; } -// wrong initializer for union payload of type 'type' +// error +// backend=stage1 +// target=native // // tmp.zig:9:8: error: use of undefined value here causes undefined behavior diff --git a/test/compile_errors/stage1/obj/wrong_number_of_arguments.zig b/test/compile_errors/stage1/obj/wrong_number_of_arguments.zig index 4cb13fdd12..f25c9dae08 100644 --- a/test/compile_errors/stage1/obj/wrong_number_of_arguments.zig +++ b/test/compile_errors/stage1/obj/wrong_number_of_arguments.zig @@ -3,6 +3,8 @@ export fn a() void { } fn c(d: i32, e: i32, f: i32) void { _ = d; _ = e; _ = f; } -// wrong number of arguments +// error +// backend=stage1 +// target=native // // tmp.zig:2:6: error: expected 3 argument(s), found 1 diff --git a/test/compile_errors/stage1/obj/wrong_number_of_arguments_for_method_fn_call.zig b/test/compile_errors/stage1/obj/wrong_number_of_arguments_for_method_fn_call.zig index 25449feb22..7371223863 100644 --- a/test/compile_errors/stage1/obj/wrong_number_of_arguments_for_method_fn_call.zig +++ b/test/compile_errors/stage1/obj/wrong_number_of_arguments_for_method_fn_call.zig @@ -7,6 +7,8 @@ fn f(foo: *const Foo) void { } export fn entry() usize { return @sizeOf(@TypeOf(f)); } -// wrong number of arguments for method fn call +// error +// backend=stage1 +// target=native // // tmp.zig:6:15: error: expected 2 argument(s), found 3 diff --git a/test/compile_errors/stage1/obj/wrong_panic_signature_generic_function.zig b/test/compile_errors/stage1/obj/wrong_panic_signature_generic_function.zig index faaa24ba60..45de2fc6cb 100644 --- a/test/compile_errors/stage1/obj/wrong_panic_signature_generic_function.zig +++ b/test/compile_errors/stage1/obj/wrong_panic_signature_generic_function.zig @@ -4,7 +4,9 @@ pub fn panic(comptime msg: []const u8, error_return_trace: ?*builtin.StackTrace) } const builtin = @import("std").builtin; -// wrong panic signature, generic function +// error +// backend=stage1 +// target=native // // error: expected type 'fn([]const u8, ?*std.builtin.StackTrace) noreturn', found 'fn([]const u8,anytype) anytype' // note: only one of the functions is generic diff --git a/test/compile_errors/stage1/obj/wrong_panic_signature_runtime_function.zig b/test/compile_errors/stage1/obj/wrong_panic_signature_runtime_function.zig index 92553c3104..5cf224c4a8 100644 --- a/test/compile_errors/stage1/obj/wrong_panic_signature_runtime_function.zig +++ b/test/compile_errors/stage1/obj/wrong_panic_signature_runtime_function.zig @@ -3,6 +3,8 @@ test "" {} pub fn panic() void {} -// wrong panic signature, runtime function +// error +// backend=stage1 +// target=native // // error: expected type 'fn([]const u8, ?*std.builtin.StackTrace) noreturn', found 'fn() void' diff --git a/test/compile_errors/stage1/obj/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig b/test/compile_errors/stage1/obj/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig index ed88e3be28..3f6b0e750b 100644 --- a/test/compile_errors/stage1/obj/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig +++ b/test/compile_errors/stage1/obj/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig @@ -5,6 +5,8 @@ export fn foo() void { bar(@ptrCast(*anyopaque, &x)); } -// wrong pointer coerced to pointer to opaque {} +// error +// backend=stage1 +// target=native // // tmp.zig:5:9: error: expected type '*Derp', found '*anyopaque' diff --git a/test/compile_errors/stage1/obj/wrong_return_type_for_main.zig b/test/compile_errors/stage1/obj/wrong_return_type_for_main.zig index bf335874db..218ef0b65d 100644 --- a/test/compile_errors/stage1/obj/wrong_return_type_for_main.zig +++ b/test/compile_errors/stage1/obj/wrong_return_type_for_main.zig @@ -1,5 +1,7 @@ pub fn main() f32 { } -// wrong return type for main +// error +// backend=stage1 +// target=native // // error: expected return type of main to be 'void', '!void', 'noreturn', 'u8', or '!u8' diff --git a/test/compile_errors/stage1/obj/wrong_size_to_an_array_literal.zig b/test/compile_errors/stage1/obj/wrong_size_to_an_array_literal.zig index 68cb263212..909d894587 100644 --- a/test/compile_errors/stage1/obj/wrong_size_to_an_array_literal.zig +++ b/test/compile_errors/stage1/obj/wrong_size_to_an_array_literal.zig @@ -3,6 +3,8 @@ comptime { _ = array; } -// wrong size to an array literal +// error +// backend=stage1 +// target=native // // tmp.zig:2:31: error: index 2 outside array of size 2 diff --git a/test/compile_errors/stage1/obj/wrong_type_for_argument_tuple_to_asyncCall.zig b/test/compile_errors/stage1/obj/wrong_type_for_argument_tuple_to_asyncCall.zig index d9e45a69b7..7a9be0a8cc 100644 --- a/test/compile_errors/stage1/obj/wrong_type_for_argument_tuple_to_asyncCall.zig +++ b/test/compile_errors/stage1/obj/wrong_type_for_argument_tuple_to_asyncCall.zig @@ -7,6 +7,8 @@ fn foo() i32 { return 0; } -// wrong type for argument tuple to @asyncCall +// error +// backend=stage1 +// target=native // // tmp.zig:3:33: error: expected tuple or struct, found 'void' diff --git a/test/compile_errors/stage1/obj/wrong_type_for_reify_type.zig b/test/compile_errors/stage1/obj/wrong_type_for_reify_type.zig index 2d8ea02326..b0c1d92659 100644 --- a/test/compile_errors/stage1/obj/wrong_type_for_reify_type.zig +++ b/test/compile_errors/stage1/obj/wrong_type_for_reify_type.zig @@ -2,6 +2,8 @@ export fn entry() void { _ = @Type(0); } -// wrong type for @Type +// error +// backend=stage1 +// target=native // // tmp.zig:2:15: error: expected type 'std.builtin.Type', found 'comptime_int' diff --git a/test/compile_errors/stage1/obj/wrong_type_for_result_ptr_to_asyncCall.zig b/test/compile_errors/stage1/obj/wrong_type_for_result_ptr_to_asyncCall.zig index 07c5ae8a0b..b4193d4de1 100644 --- a/test/compile_errors/stage1/obj/wrong_type_for_result_ptr_to_asyncCall.zig +++ b/test/compile_errors/stage1/obj/wrong_type_for_result_ptr_to_asyncCall.zig @@ -9,6 +9,8 @@ fn foo() i32 { return 1234; } -// wrong type for result ptr to @asyncCall +// error +// backend=stage1 +// target=native // // tmp.zig:6:37: error: expected type '*i32', found 'bool' diff --git a/test/compile_errors/stage1/obj/wrong_type_passed_to_panic.zig b/test/compile_errors/stage1/obj/wrong_type_passed_to_panic.zig index b16d563571..ed237f81df 100644 --- a/test/compile_errors/stage1/obj/wrong_type_passed_to_panic.zig +++ b/test/compile_errors/stage1/obj/wrong_type_passed_to_panic.zig @@ -3,6 +3,8 @@ export fn entry() void { @panic(e); } -// wrong type passed to @panic +// error +// backend=stage1 +// target=native // // tmp.zig:3:12: error: expected type '[]const u8', found 'error{Foo}' diff --git a/test/compile_errors/stage1/obj/wrong_type_to_hasField.zig b/test/compile_errors/stage1/obj/wrong_type_to_hasField.zig index a79fec2a21..680c19a6fb 100644 --- a/test/compile_errors/stage1/obj/wrong_type_to_hasField.zig +++ b/test/compile_errors/stage1/obj/wrong_type_to_hasField.zig @@ -2,6 +2,8 @@ export fn entry() bool { return @hasField(i32, "hi"); } -// wrong type to @hasField +// error +// backend=stage1 +// target=native // // tmp.zig:2:22: error: type 'i32' does not support @hasField diff --git a/test/compile_errors/stage1/obj/wrong_types_given_to_atomic_order_args_in_cmpxchg.zig b/test/compile_errors/stage1/obj/wrong_types_given_to_atomic_order_args_in_cmpxchg.zig index 47cdaa372f..b0f484e2fd 100644 --- a/test/compile_errors/stage1/obj/wrong_types_given_to_atomic_order_args_in_cmpxchg.zig +++ b/test/compile_errors/stage1/obj/wrong_types_given_to_atomic_order_args_in_cmpxchg.zig @@ -3,6 +3,8 @@ export fn entry() void { while (!@cmpxchgWeak(i32, &x, 1234, 5678, @as(u32, 1234), @as(u32, 1234))) {} } -// wrong types given to atomic order args in cmpxchg +// error +// backend=stage1 +// target=native // // tmp.zig:3:47: error: expected type 'std.builtin.AtomicOrder', found 'u32' diff --git a/test/compile_errors/stage1/obj/wrong_types_given_to_export.zig b/test/compile_errors/stage1/obj/wrong_types_given_to_export.zig index cdd7ab4631..c06116204f 100644 --- a/test/compile_errors/stage1/obj/wrong_types_given_to_export.zig +++ b/test/compile_errors/stage1/obj/wrong_types_given_to_export.zig @@ -3,6 +3,8 @@ comptime { @export(entry, .{.name = "entry", .linkage = @as(u32, 1234) }); } -// wrong types given to @export +// error +// backend=stage1 +// target=native // // tmp.zig:3:59: error: expected type 'std.builtin.GlobalLinkage', found 'comptime_int' diff --git a/test/compile_errors/stage1/test/access_invalid_typeInfo_decl.zig b/test/compile_errors/stage1/test/access_invalid_typeInfo_decl.zig index 2fc5730af0..40e31a0855 100644 --- a/test/compile_errors/stage1/test/access_invalid_typeInfo_decl.zig +++ b/test/compile_errors/stage1/test/access_invalid_typeInfo_decl.zig @@ -3,6 +3,9 @@ test "Crash" { _ = @typeInfo(@This()).Struct.decls[0]; } -// access invalid @typeInfo decl +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:1:11: error: use of undeclared identifier 'B' diff --git a/test/compile_errors/stage1/test/alignCast_of_zero_sized_types.zig b/test/compile_errors/stage1/test/alignCast_of_zero_sized_types.zig index 4c05571202..f83d22759c 100644 --- a/test/compile_errors/stage1/test/alignCast_of_zero_sized_types.zig +++ b/test/compile_errors/stage1/test/alignCast_of_zero_sized_types.zig @@ -17,7 +17,10 @@ export fn qux() void { _ = @alignCast(2, a); } -// @alignCast of zero sized types +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:3:23: error: cannot adjust alignment of zero sized type '*void' // tmp.zig:7:23: error: cannot adjust alignment of zero sized type '?*void' diff --git a/test/compile_errors/stage1/test/bad_splat_type.zig b/test/compile_errors/stage1/test/bad_splat_type.zig index 9a16bbc73d..94e3d052a6 100644 --- a/test/compile_errors/stage1/test/bad_splat_type.zig +++ b/test/compile_errors/stage1/test/bad_splat_type.zig @@ -4,6 +4,9 @@ export fn entry() void { _ = v; } -// bad @splat type +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:3:23: error: vector element type must be integer, float, bool, or pointer; 'comptime_int' is invalid diff --git a/test/compile_errors/stage1/test/binary_OR_operator_on_error_sets.zig b/test/compile_errors/stage1/test/binary_OR_operator_on_error_sets.zig index d9894a32a0..eb2d21c6ed 100644 --- a/test/compile_errors/stage1/test/binary_OR_operator_on_error_sets.zig +++ b/test/compile_errors/stage1/test/binary_OR_operator_on_error_sets.zig @@ -5,6 +5,9 @@ export fn entry() void { _ = x; } -// binary OR operator on error sets +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:2:18: error: invalid operands to binary expression: 'error{A}' and 'error{B}' diff --git a/test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-always_inline.zig b/test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-always_inline.zig index ea822f8d91..a010414a57 100644 --- a/test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-always_inline.zig +++ b/test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-always_inline.zig @@ -3,6 +3,9 @@ pub export fn entry() void { @call(.{ .modifier = .always_inline }, call_me, .{}); } -// @call rejects non comptime-known fn - always_inline +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:3:5: error: the specified modifier requires a comptime-known function diff --git a/test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-compile_time.zig b/test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-compile_time.zig index 3c50830787..b3ffcc6ec2 100644 --- a/test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-compile_time.zig +++ b/test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-compile_time.zig @@ -3,6 +3,9 @@ pub export fn entry() void { @call(.{ .modifier = .compile_time }, call_me, .{}); } -// @call rejects non comptime-known fn - compile_time +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:3:5: error: the specified modifier requires a comptime-known function diff --git a/test/compile_errors/stage1/test/cast_between_optional_T_where_T_is_not_a_pointer.zig b/test/compile_errors/stage1/test/cast_between_optional_T_where_T_is_not_a_pointer.zig index 8f5ca1c25d..b8392ff5db 100644 --- a/test/compile_errors/stage1/test/cast_between_optional_T_where_T_is_not_a_pointer.zig +++ b/test/compile_errors/stage1/test/cast_between_optional_T_where_T_is_not_a_pointer.zig @@ -6,7 +6,10 @@ export fn entry() void { a = b; } -// cast between ?T where T is not a pointer +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:6:9: error: expected type '?fn(i8) void', found '?fn(u64) void' // tmp.zig:6:9: note: optional type child 'fn(u64) void' cannot cast into optional type child 'fn(i8) void' diff --git a/test/compile_errors/stage1/test/combination_of_nosuspend_and_async.zig b/test/compile_errors/stage1/test/combination_of_nosuspend_and_async.zig index a164fb621b..43731c9648 100644 --- a/test/compile_errors/stage1/test/combination_of_nosuspend_and_async.zig +++ b/test/compile_errors/stage1/test/combination_of_nosuspend_and_async.zig @@ -7,7 +7,10 @@ export fn entry() void { } fn foo() void {} -// combination of nosuspend and async +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:4:9: error: suspend inside nosuspend block // tmp.zig:2:5: note: nosuspend block here diff --git a/test/compile_errors/stage1/test/comparison_of_non-tagged_union_and_enum_literal.zig b/test/compile_errors/stage1/test/comparison_of_non-tagged_union_and_enum_literal.zig index b3c1506705..4898ac744c 100644 --- a/test/compile_errors/stage1/test/comparison_of_non-tagged_union_and_enum_literal.zig +++ b/test/compile_errors/stage1/test/comparison_of_non-tagged_union_and_enum_literal.zig @@ -5,7 +5,10 @@ export fn entry() void { _ = ok; } -// comparison of non-tagged union and enum literal +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:4:16: error: comparison of union and enum literal is only valid for tagged union types // tmp.zig:2:15: note: type U is not a tagged union diff --git a/test/compile_errors/stage1/test/comptime_vector_overflow_shows_the_index.zig b/test/compile_errors/stage1/test/comptime_vector_overflow_shows_the_index.zig index a638c05b3b..a1f026ab70 100644 --- a/test/compile_errors/stage1/test/comptime_vector_overflow_shows_the_index.zig +++ b/test/compile_errors/stage1/test/comptime_vector_overflow_shows_the_index.zig @@ -5,7 +5,10 @@ comptime { _ = x; } -// comptime vector overflow shows the index +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:4:15: error: operation caused overflow // tmp.zig:4:15: note: when computing vector element at index 2 diff --git a/test/compile_errors/stage1/test/duplicate_field_in_anonymous_struct_literal.zig b/test/compile_errors/stage1/test/duplicate_field_in_anonymous_struct_literal.zig index 1b671d2ad3..7b6821d669 100644 --- a/test/compile_errors/stage1/test/duplicate_field_in_anonymous_struct_literal.zig +++ b/test/compile_errors/stage1/test/duplicate_field_in_anonymous_struct_literal.zig @@ -10,7 +10,10 @@ export fn entry() void { _ = anon; } -// duplicate field in anonymous struct literal +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:7:13: error: duplicate field // tmp.zig:4:13: note: other field here diff --git a/test/compile_errors/stage1/test/error_in_struct_initializer_doesnt_crash_the_compiler.zig b/test/compile_errors/stage1/test/error_in_struct_initializer_doesnt_crash_the_compiler.zig index e37b887121..2858fd6a01 100644 --- a/test/compile_errors/stage1/test/error_in_struct_initializer_doesnt_crash_the_compiler.zig +++ b/test/compile_errors/stage1/test/error_in_struct_initializer_doesnt_crash_the_compiler.zig @@ -7,6 +7,9 @@ pub export fn entry() void { _ = a; } -// error in struct initializer doesn't crash the compiler +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:4:9: error: duplicate struct field: 'e' diff --git a/test/compile_errors/stage1/test/errors_in_for_loop_bodies_are_propagated.zig b/test/compile_errors/stage1/test/errors_in_for_loop_bodies_are_propagated.zig index 32e99cf410..d8be29e39b 100644 --- a/test/compile_errors/stage1/test/errors_in_for_loop_bodies_are_propagated.zig +++ b/test/compile_errors/stage1/test/errors_in_for_loop_bodies_are_propagated.zig @@ -3,6 +3,9 @@ pub export fn entry() void { for (arr) |bits| _ = @popCount(bits); } -// errors in for loop bodies are propagated +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:3:26: error: expected 2 arguments, found 1 diff --git a/test/compile_errors/stage1/test/export_with_empty_name_string.zig b/test/compile_errors/stage1/test/export_with_empty_name_string.zig index 424ee6e4b5..b882d11723 100644 --- a/test/compile_errors/stage1/test/export_with_empty_name_string.zig +++ b/test/compile_errors/stage1/test/export_with_empty_name_string.zig @@ -3,6 +3,9 @@ comptime { @export(entry, .{ .name = "" }); } -// @export with empty name string +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:3:5: error: exported symbol name cannot be empty diff --git a/test/compile_errors/stage1/test/helpful_return_type_error_message.zig b/test/compile_errors/stage1/test/helpful_return_type_error_message.zig index 6f1f9639da..0e97c09de2 100644 --- a/test/compile_errors/stage1/test/helpful_return_type_error_message.zig +++ b/test/compile_errors/stage1/test/helpful_return_type_error_message.zig @@ -15,7 +15,10 @@ export fn quux() u32 { buf = bar(); } -// helpful return type error message +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:2:17: error: expected type 'u32', found 'error{Ohno}' // tmp.zig:1:17: note: function cannot return an error diff --git a/test/compile_errors/stage1/test/int-float_conversion_to_comptime_int-float.zig b/test/compile_errors/stage1/test/int-float_conversion_to_comptime_int-float.zig index eeb2e4798e..de439168ae 100644 --- a/test/compile_errors/stage1/test/int-float_conversion_to_comptime_int-float.zig +++ b/test/compile_errors/stage1/test/int-float_conversion_to_comptime_int-float.zig @@ -7,7 +7,10 @@ export fn bar() void { _ = @intToFloat(comptime_float, a); } -// int/float conversion to comptime_int/float +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:3:35: error: unable to evaluate constant expression // tmp.zig:7:37: error: unable to evaluate constant expression diff --git a/test/compile_errors/stage1/test/invalid_assignments.zig b/test/compile_errors/stage1/test/invalid_assignments.zig index 203784c554..75f45a8bcb 100644 --- a/test/compile_errors/stage1/test/invalid_assignments.zig +++ b/test/compile_errors/stage1/test/invalid_assignments.zig @@ -10,7 +10,10 @@ export fn entry4() void { 2 + 2 = 3; } -// invalid assignments +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:3:6: error: invalid left-hand side to assignment // tmp.zig:7:7: error: invalid left-hand side to assignment diff --git a/test/compile_errors/stage1/test/invalid_float_casts.zig b/test/compile_errors/stage1/test/invalid_float_casts.zig index 0e2509dcaf..2fe8003728 100644 --- a/test/compile_errors/stage1/test/invalid_float_casts.zig +++ b/test/compile_errors/stage1/test/invalid_float_casts.zig @@ -15,7 +15,10 @@ export fn qux() void { _ = @floatCast(f32, a); } -// invalid float casts +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:3:36: error: unable to evaluate constant expression // tmp.zig:7:21: error: expected integer type, found 'f32' diff --git a/test/compile_errors/stage1/test/invalid_int_casts.zig b/test/compile_errors/stage1/test/invalid_int_casts.zig index 6870ecc723..9945751ef5 100644 --- a/test/compile_errors/stage1/test/invalid_int_casts.zig +++ b/test/compile_errors/stage1/test/invalid_int_casts.zig @@ -15,7 +15,10 @@ export fn qux() void { _ = @intCast(u32, a); } -// invalid int casts +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:3:32: error: unable to evaluate constant expression // tmp.zig:7:21: error: expected float type, found 'u32' diff --git a/test/compile_errors/stage1/test/invalid_non-exhaustive_enum_to_union.zig b/test/compile_errors/stage1/test/invalid_non-exhaustive_enum_to_union.zig index f78a9e62ff..804e83a66a 100644 --- a/test/compile_errors/stage1/test/invalid_non-exhaustive_enum_to_union.zig +++ b/test/compile_errors/stage1/test/invalid_non-exhaustive_enum_to_union.zig @@ -18,7 +18,10 @@ export fn bar() void { _ = u; } -// invalid non-exhaustive enum to union +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:12:16: error: runtime cast to union 'U' from non-exhaustive enum // tmp.zig:17:16: error: no tag by value 15 diff --git a/test/compile_errors/stage1/test/invalid_pointer_with_reify_type.zig b/test/compile_errors/stage1/test/invalid_pointer_with_reify_type.zig index febf9685fd..bf1cf6bd6f 100644 --- a/test/compile_errors/stage1/test/invalid_pointer_with_reify_type.zig +++ b/test/compile_errors/stage1/test/invalid_pointer_with_reify_type.zig @@ -11,6 +11,9 @@ export fn entry() void { }}); } -// invalid pointer with @Type +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:2:16: error: sentinels are only allowed on slices and unknown-length pointers diff --git a/test/compile_errors/stage1/test/nested_vectors.zig b/test/compile_errors/stage1/test/nested_vectors.zig index a05e9b0c5b..9e87ac814b 100644 --- a/test/compile_errors/stage1/test/nested_vectors.zig +++ b/test/compile_errors/stage1/test/nested_vectors.zig @@ -5,6 +5,9 @@ export fn entry() void { _ = v; } -// nested vectors +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:3:23: error: vector element type must be integer, float, bool, or pointer; '@Vector(4, u8)' is invalid diff --git a/test/compile_errors/stage1/test/non-exhaustive_enum_marker_assigned_a_value.zig b/test/compile_errors/stage1/test/non-exhaustive_enum_marker_assigned_a_value.zig index 53037ff181..609af248df 100644 --- a/test/compile_errors/stage1/test/non-exhaustive_enum_marker_assigned_a_value.zig +++ b/test/compile_errors/stage1/test/non-exhaustive_enum_marker_assigned_a_value.zig @@ -10,7 +10,10 @@ const B = enum { }; comptime { _ = A; _ = B; } -// non-exhaustive enum marker assigned a value +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:4:9: error: '_' is used to mark an enum as non-exhaustive and cannot be assigned a value // tmp.zig:6:11: error: non-exhaustive enum missing integer tag type diff --git a/test/compile_errors/stage1/test/non-exhaustive_enums.zig b/test/compile_errors/stage1/test/non-exhaustive_enums.zig index 49ceff3178..3b5f8af7cb 100644 --- a/test/compile_errors/stage1/test/non-exhaustive_enums.zig +++ b/test/compile_errors/stage1/test/non-exhaustive_enums.zig @@ -13,7 +13,10 @@ pub export fn entry() void { _ = C; } -// non-exhaustive enums +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:3:5: error: '_' field of non-exhaustive enum must be last // tmp.zig:6:11: error: non-exhaustive enum specifies every value diff --git a/test/compile_errors/stage1/test/not_an_enum_type.zig b/test/compile_errors/stage1/test/not_an_enum_type.zig index f92c3d44e1..99c733b4d0 100644 --- a/test/compile_errors/stage1/test/not_an_enum_type.zig +++ b/test/compile_errors/stage1/test/not_an_enum_type.zig @@ -12,6 +12,9 @@ const Error = union(enum) { const InvalidToken = struct {}; const ExpectedVarDeclOrFn = struct {}; -// not an enum type +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:4:9: error: expected type '@typeInfo(Error).Union.tag_type.?', found 'type' diff --git a/test/compile_errors/stage1/test/packed_struct_with_fields_of_not_allowed_types.zig b/test/compile_errors/stage1/test/packed_struct_with_fields_of_not_allowed_types.zig index dc156342f4..2951d26c52 100644 --- a/test/compile_errors/stage1/test/packed_struct_with_fields_of_not_allowed_types.zig +++ b/test/compile_errors/stage1/test/packed_struct_with_fields_of_not_allowed_types.zig @@ -59,7 +59,10 @@ const Enum = enum { B, }; -// packed struct with fields of not allowed types +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:2:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation // tmp.zig:5:5: error: array of 'u24' not allowed in packed struct due to padding bits (must be padded from 48 to 64 bits) diff --git a/test/compile_errors/stage1/test/ptrToInt_with_pointer_to_zero-sized_type.zig b/test/compile_errors/stage1/test/ptrToInt_with_pointer_to_zero-sized_type.zig index 63ba217247..7c2ba184c4 100644 --- a/test/compile_errors/stage1/test/ptrToInt_with_pointer_to_zero-sized_type.zig +++ b/test/compile_errors/stage1/test/ptrToInt_with_pointer_to_zero-sized_type.zig @@ -4,6 +4,9 @@ export fn entry() void { _ = x; } -// @ptrToInt with pointer to zero-sized type +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:3:23: error: pointer to size 0 type has no address diff --git a/test/compile_errors/stage1/test/reassign_to_array_parameter.zig b/test/compile_errors/stage1/test/reassign_to_array_parameter.zig index a222150a2c..f3b51d2c0f 100644 --- a/test/compile_errors/stage1/test/reassign_to_array_parameter.zig +++ b/test/compile_errors/stage1/test/reassign_to_array_parameter.zig @@ -5,6 +5,9 @@ export fn entry() void { reassign(.{1, 2, 3}); } -// reassign to array parameter +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:2:15: error: cannot assign to constant diff --git a/test/compile_errors/stage1/test/reassign_to_slice_parameter.zig b/test/compile_errors/stage1/test/reassign_to_slice_parameter.zig index a8e555182a..34bd5541a1 100644 --- a/test/compile_errors/stage1/test/reassign_to_slice_parameter.zig +++ b/test/compile_errors/stage1/test/reassign_to_slice_parameter.zig @@ -5,6 +5,9 @@ export fn entry() void { reassign("foo"); } -// reassign to slice parameter +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:2:10: error: cannot assign to constant diff --git a/test/compile_errors/stage1/test/reassign_to_struct_parameter.zig b/test/compile_errors/stage1/test/reassign_to_struct_parameter.zig index 018548ab32..994833d989 100644 --- a/test/compile_errors/stage1/test/reassign_to_struct_parameter.zig +++ b/test/compile_errors/stage1/test/reassign_to_struct_parameter.zig @@ -8,6 +8,9 @@ export fn entry() void { reassign(S{.x = 3}); } -// reassign to struct parameter +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:5:10: error: cannot assign to constant diff --git a/test/compile_errors/stage1/test/reference_to_const_data.zig b/test/compile_errors/stage1/test/reference_to_const_data.zig index d785eb648e..6c98aefd7a 100644 --- a/test/compile_errors/stage1/test/reference_to_const_data.zig +++ b/test/compile_errors/stage1/test/reference_to_const_data.zig @@ -19,7 +19,10 @@ export fn qux() void { ptr.x = 2; } -// reference to const data +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:3:14: error: cannot assign to constant // tmp.zig:7:13: error: cannot assign to constant diff --git a/test/compile_errors/stage1/test/reify_typeOf_with_incompatible_arguments.zig b/test/compile_errors/stage1/test/reify_typeOf_with_incompatible_arguments.zig index 97ee7aaf1b..2013a07e53 100644 --- a/test/compile_errors/stage1/test/reify_typeOf_with_incompatible_arguments.zig +++ b/test/compile_errors/stage1/test/reify_typeOf_with_incompatible_arguments.zig @@ -4,6 +4,9 @@ export fn entry() void { _ = @TypeOf(var_1, var_2); } -// @TypeOf with incompatible arguments +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:4:9: error: incompatible types: 'f32' and 'u32' diff --git a/test/compile_errors/stage1/test/reify_typeOf_with_no_arguments.zig b/test/compile_errors/stage1/test/reify_typeOf_with_no_arguments.zig index e18b9e66b7..0626d45393 100644 --- a/test/compile_errors/stage1/test/reify_typeOf_with_no_arguments.zig +++ b/test/compile_errors/stage1/test/reify_typeOf_with_no_arguments.zig @@ -2,6 +2,9 @@ export fn entry() void { _ = @TypeOf(); } -// @TypeOf with no arguments +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:2:9: error: expected at least 1 argument, found 0 diff --git a/test/compile_errors/stage1/test/reject_extern_function_definitions_with_body.zig b/test/compile_errors/stage1/test/reject_extern_function_definitions_with_body.zig index 024080a6bc..a7b92e8399 100644 --- a/test/compile_errors/stage1/test/reject_extern_function_definitions_with_body.zig +++ b/test/compile_errors/stage1/test/reject_extern_function_definitions_with_body.zig @@ -2,6 +2,9 @@ extern "c" fn definitelyNotInLibC(a: i32, b: i32) i32 { return a + b; } -// reject extern function definitions with body +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:1:1: error: extern functions have no body diff --git a/test/compile_errors/stage1/test/reject_extern_variables_with_initializers.zig b/test/compile_errors/stage1/test/reject_extern_variables_with_initializers.zig index d3f65ff0cb..54239e0948 100644 --- a/test/compile_errors/stage1/test/reject_extern_variables_with_initializers.zig +++ b/test/compile_errors/stage1/test/reject_extern_variables_with_initializers.zig @@ -1,5 +1,8 @@ extern var foo: int = 2; -// reject extern variables with initializers +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:1:23: error: extern variables have no initializers diff --git a/test/compile_errors/stage1/test/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig b/test/compile_errors/stage1/test/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig index c419a6ae83..7499939b4a 100644 --- a/test/compile_errors/stage1/test/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig +++ b/test/compile_errors/stage1/test/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig @@ -6,6 +6,9 @@ test "1" { _ = A().a; } -// repeated invalid field access to generic function returning type crashes compiler. #2655 +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:2:12: error: use of undeclared identifier 'Q' diff --git a/test/compile_errors/stage1/test/return_invalid_type_from_test.zig b/test/compile_errors/stage1/test/return_invalid_type_from_test.zig index e7aeab3be7..590ac30918 100644 --- a/test/compile_errors/stage1/test/return_invalid_type_from_test.zig +++ b/test/compile_errors/stage1/test/return_invalid_type_from_test.zig @@ -1,5 +1,8 @@ test "example" { return 1; } -// return invalid type from test +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:1:25: error: expected type 'void', found 'comptime_int' diff --git a/test/compile_errors/stage1/test/shift_on_type_with_non-power-of-two_size.zig b/test/compile_errors/stage1/test/shift_on_type_with_non-power-of-two_size.zig index d76406223d..222cb22563 100644 --- a/test/compile_errors/stage1/test/shift_on_type_with_non-power-of-two_size.zig +++ b/test/compile_errors/stage1/test/shift_on_type_with_non-power-of-two_size.zig @@ -23,7 +23,10 @@ export fn entry() void { S.d(); } -// shift on type with non-power-of-two size +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:5:19: error: RHS of shift is too large for LHS type // tmp.zig:9:19: error: RHS of shift is too large for LHS type diff --git a/test/compile_errors/stage1/test/shuffle_with_selected_index_past_first_vector_length.zig b/test/compile_errors/stage1/test/shuffle_with_selected_index_past_first_vector_length.zig index c45eb4a0e2..8b0c5ba780 100644 --- a/test/compile_errors/stage1/test/shuffle_with_selected_index_past_first_vector_length.zig +++ b/test/compile_errors/stage1/test/shuffle_with_selected_index_past_first_vector_length.zig @@ -5,7 +5,10 @@ export fn entry() void { _ = z; } -// @shuffle with selected index past first vector length +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:4:39: error: mask index '4' has out-of-bounds selection // tmp.zig:4:27: note: selected index '7' out of bounds of @Vector(4, u32) diff --git a/test/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig b/test/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig index 4c0545ea92..a44d59feed 100644 --- a/test/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig +++ b/test/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig @@ -7,7 +7,10 @@ pub export fn entry() void { } } -// switch ranges endpoints are validated +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:4:9: error: range start value is greater than the end value // tmp.zig:5:9: error: range start value is greater than the end value diff --git a/test/compile_errors/stage1/test/switching_with_exhaustive_enum_has___prong_.zig b/test/compile_errors/stage1/test/switching_with_exhaustive_enum_has___prong_.zig index b132b7834e..0ca489bdb8 100644 --- a/test/compile_errors/stage1/test/switching_with_exhaustive_enum_has___prong_.zig +++ b/test/compile_errors/stage1/test/switching_with_exhaustive_enum_has___prong_.zig @@ -11,6 +11,9 @@ pub export fn entry() void { } } -// switching with exhaustive enum has '_' prong +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:7:5: error: switch on exhaustive enum has `_` prong diff --git a/test/compile_errors/stage1/test/switching_with_non-exhaustive_enums.zig b/test/compile_errors/stage1/test/switching_with_non-exhaustive_enums.zig index 53cd88e68c..ed46c5f2f1 100644 --- a/test/compile_errors/stage1/test/switching_with_non-exhaustive_enums.zig +++ b/test/compile_errors/stage1/test/switching_with_non-exhaustive_enums.zig @@ -25,7 +25,10 @@ pub export fn entry() void { } } -// switching with non-exhaustive enums +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:12:5: error: enumeration value 'E.b' not handled in switch // tmp.zig:16:5: error: switch on non-exhaustive enum must include `else` or `_` prong diff --git a/test/compile_errors/stage1/test/tagName_on_invalid_value_of_non-exhaustive_enum.zig b/test/compile_errors/stage1/test/tagName_on_invalid_value_of_non-exhaustive_enum.zig index 139c7c7a23..a98470b681 100644 --- a/test/compile_errors/stage1/test/tagName_on_invalid_value_of_non-exhaustive_enum.zig +++ b/test/compile_errors/stage1/test/tagName_on_invalid_value_of_non-exhaustive_enum.zig @@ -3,6 +3,9 @@ test "enum" { _ = @tagName(@intToEnum(E, 5)); } -// @tagName on invalid value of non-exhaustive enum +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:3:18: error: no tag by value 5 diff --git a/test/compile_errors/stage1/test/type_mismatch_in_C_prototype_with_varargs.zig b/test/compile_errors/stage1/test/type_mismatch_in_C_prototype_with_varargs.zig index 437cca8772..1f78ef3999 100644 --- a/test/compile_errors/stage1/test/type_mismatch_in_C_prototype_with_varargs.zig +++ b/test/compile_errors/stage1/test/type_mismatch_in_C_prototype_with_varargs.zig @@ -6,6 +6,9 @@ export fn main() void { _ = x; } -// type mismatch in C prototype with varargs +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:5:22: error: expected type 'fn([*c]u8, ...) callconv(.C) void', found 'fn([*:0]u8, ...) callconv(.C) void' diff --git a/test/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig b/test/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig index ce5e4d4629..5983224676 100644 --- a/test/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig +++ b/test/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig @@ -3,6 +3,9 @@ export fn entry() void { x = x ++ .{ 1, 2, 3 }; } -// type mismatch with tuple concatenation +// error +// backend=stage1 +// target=native +// is_test=1 // // tmp.zig:3:11: error: expected type 'struct:2:14', found 'struct:3:11' diff --git a/test/compile_errors/stage2/constant_inside_comptime_function_has_compile_error.zig b/test/compile_errors/stage2/constant_inside_comptime_function_has_compile_error.zig index 98d6104670..2b67390b05 100644 --- a/test/compile_errors/stage2/constant_inside_comptime_function_has_compile_error.zig +++ b/test/compile_errors/stage2/constant_inside_comptime_function_has_compile_error.zig @@ -15,6 +15,7 @@ export fn entry() void { } // error +// target=native // // :4:5: error: unreachable code // :4:25: note: control flow is diverted here diff --git a/test/compile_errors/stage2/duplicate-unused_labels.zig b/test/compile_errors/stage2/duplicate-unused_labels.zig index 44ab48480d..4bfc6c5960 100644 --- a/test/compile_errors/stage2/duplicate-unused_labels.zig +++ b/test/compile_errors/stage2/duplicate-unused_labels.zig @@ -18,6 +18,7 @@ comptime { } // error +// target=native // // :2:12: error: redefinition of label 'blk' // :2:5: note: previous definition here diff --git a/test/compile_errors/stage2/embed_outside_package.zig b/test/compile_errors/stage2/embed_outside_package.zig index 9cf1a8b905..2003adb061 100644 --- a/test/compile_errors/stage2/embed_outside_package.zig +++ b/test/compile_errors/stage2/embed_outside_package.zig @@ -3,5 +3,6 @@ export fn a() usize { } // error +// target=native // //:2:23: error: embed of file outside package path: '/root/foo' diff --git a/test/compile_errors/stage2/import_outside_package.zig b/test/compile_errors/stage2/import_outside_package.zig index 6b70778754..59a75e2745 100644 --- a/test/compile_errors/stage2/import_outside_package.zig +++ b/test/compile_errors/stage2/import_outside_package.zig @@ -3,5 +3,6 @@ export fn a() usize { } // error +// target=native // // :2:20: error: import of file outside package path: '../../above.zig' diff --git a/test/compile_errors/stage2/out_of_bounds_index.zig b/test/compile_errors/stage2/out_of_bounds_index.zig index 614c445d46..ea9bc6521d 100644 --- a/test/compile_errors/stage2/out_of_bounds_index.zig +++ b/test/compile_errors/stage2/out_of_bounds_index.zig @@ -21,6 +21,7 @@ comptime { } // error +// target=native // // :4:26: error: end index 6 out of bounds for slice of length 4 +1 (sentinel) // :9:22: error: end index 6 out of bounds for array of length 4 +1 (sentinel) diff --git a/test/compile_errors/stage2/slice_of_null_pointer.zig b/test/compile_errors/stage2/slice_of_null_pointer.zig index a33a904842..7f2c74a8a6 100644 --- a/test/compile_errors/stage2/slice_of_null_pointer.zig +++ b/test/compile_errors/stage2/slice_of_null_pointer.zig @@ -6,5 +6,6 @@ comptime { } // error +// target=native // // :4:14: error: slice of null pointer diff --git a/test/compile_errors/stage2/struct_duplicate_field_name.zig b/test/compile_errors/stage2/struct_duplicate_field_name.zig index e7bed78d06..7e4434f1d9 100644 --- a/test/compile_errors/stage2/struct_duplicate_field_name.zig +++ b/test/compile_errors/stage2/struct_duplicate_field_name.zig @@ -9,6 +9,7 @@ export fn entry() void { } // error +// target=native // // :3:5: error: duplicate struct field: 'foo' // :2:5: note: other field here diff --git a/test/compile_errors/stage2/union_access_of_inactive_field.zig b/test/compile_errors/stage2/union_access_of_inactive_field.zig index 881279e1c3..4f72914216 100644 --- a/test/compile_errors/stage2/union_access_of_inactive_field.zig +++ b/test/compile_errors/stage2/union_access_of_inactive_field.zig @@ -9,6 +9,7 @@ comptime { } // error +// target=native // // :7:16: error: access of union field 'b' while field 'a' is active // :1:11: note: union declared here diff --git a/test/compile_errors/stage2/union_duplicate_enum_field.zig b/test/compile_errors/stage2/union_duplicate_enum_field.zig index 5a08256edb..44eb58e014 100644 --- a/test/compile_errors/stage2/union_duplicate_enum_field.zig +++ b/test/compile_errors/stage2/union_duplicate_enum_field.zig @@ -10,6 +10,7 @@ export fn foo() void { } // error +// target=native // // :4:5: error: duplicate union field: 'a' // :3:5: note: other field here diff --git a/test/compile_errors/stage2/union_duplicate_field_definition.zig b/test/compile_errors/stage2/union_duplicate_field_definition.zig index 7aab7c4695..7993f27141 100644 --- a/test/compile_errors/stage2/union_duplicate_field_definition.zig +++ b/test/compile_errors/stage2/union_duplicate_field_definition.zig @@ -9,6 +9,7 @@ export fn entry() void { } // error +// target=native // // :3:5: error: duplicate union field: 'foo' // :2:5: note: other field here diff --git a/test/compile_errors/stage2/union_enum_field_missing.zig b/test/compile_errors/stage2/union_enum_field_missing.zig index 638c9fec26..25ec477894 100644 --- a/test/compile_errors/stage2/union_enum_field_missing.zig +++ b/test/compile_errors/stage2/union_enum_field_missing.zig @@ -14,6 +14,7 @@ export fn entry() usize { } // error +// target=native // // :7:1: error: enum field(s) missing in union // :4:5: note: field 'c' missing, declared here diff --git a/test/compile_errors/stage2/union_extra_field.zig b/test/compile_errors/stage2/union_extra_field.zig index cdfa482208..e3bc0deb33 100644 --- a/test/compile_errors/stage2/union_extra_field.zig +++ b/test/compile_errors/stage2/union_extra_field.zig @@ -14,6 +14,7 @@ export fn entry() usize { } // error +// target=native // // :6:1: error: enum 'tmp.E' has no field named 'd' // :1:11: note: enum declared here diff --git a/test/compile_errors/stage2/union_runtime_coercion_from_enum.zig b/test/compile_errors/stage2/union_runtime_coercion_from_enum.zig index 56cd2db83b..9020e9d5d7 100644 --- a/test/compile_errors/stage2/union_runtime_coercion_from_enum.zig +++ b/test/compile_errors/stage2/union_runtime_coercion_from_enum.zig @@ -15,6 +15,7 @@ export fn doTheTest() u64 { } // error +// target=native // // :13:19: error: runtime coercion from enum 'tmp.E' to union 'tmp.U' which has non-void fields // :6:5: note: field 'a' has type 'u32' From d25f06a71c058aa4ff8bf40749345028bda6e017 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 28 Apr 2022 18:22:57 +0200 Subject: [PATCH 1294/2031] test: remove redundant codepaths from test harness --- src/test.zig | 170 ++------------------------------------------------- 1 file changed, 4 insertions(+), 166 deletions(-) diff --git a/src/test.zig b/src/test.zig index adcfeb8dff..758347ac18 100644 --- a/src/test.zig +++ b/src/test.zig @@ -904,32 +904,12 @@ pub const TestContext = struct { incremental, }; - /// Adds a compile-error test for each file in the provided directory, using the - /// selected backend and output mode. If `one_test_case_per_file` is true, a new - /// test case is created for each file. Otherwise, a single test case is used for - /// all tests. + /// Adds a test for each file in the provided directory, using the selected strategy. + /// Recurses nested directories. /// /// Each file should include a test manifest as a contiguous block of comments at - /// the end of the file. The first line should be the test case name, followed by - /// a blank line, then one expected errors on each line in the form - /// `:line:column: error: message` - pub fn addErrorCasesFromDir( - ctx: *TestContext, - name: []const u8, - dir: std.fs.Dir, - backend: Backend, - output_mode: std.builtin.OutputMode, - is_test: bool, - strategy: Strategy, - ) void { - var current_file: []const u8 = "none"; - addErrorCasesFromDirInner(ctx, name, dir, backend, output_mode, is_test, strategy, ¤t_file) catch |err| { - std.debug.panic("test harness failed to process file '{s}': {s}\n", .{ - current_file, @errorName(err), - }); - }; - } - + /// the end of the file. The first line should be the test type, followed by a set of + /// key-value config values, followed by a blank line, then the expected output. pub fn addTestCasesFromDir(ctx: *TestContext, dir: std.fs.Dir, strategy: Strategy) void { var current_file: []const u8 = "none"; ctx.addTestCasesFromDirInner(dir, strategy, ¤t_file) catch |err| { @@ -1127,148 +1107,6 @@ pub const TestContext = struct { } } - fn addErrorCasesFromDirInner( - ctx: *TestContext, - name: []const u8, - dir: std.fs.Dir, - backend: Backend, - output_mode: std.builtin.OutputMode, - is_test: bool, - strategy: Strategy, - /// This is kept up to date with the currently being processed file so - /// that if any errors occur the caller knows it happened during this file. - current_file: *[]const u8, - ) !void { - var opt_case: ?*Case = null; - - var it = dir.iterate(); - var filenames = std.ArrayList([]const u8).init(ctx.arena); - defer filenames.deinit(); - - while (try it.next()) |entry| { - if (entry.kind != .File) continue; - - // Ignore stuff such as .swp files - switch (Compilation.classifyFileExt(entry.name)) { - .unknown => continue, - else => {}, - } - try filenames.append(try ctx.arena.dupe(u8, entry.name)); - } - - // Sort filenames, so that incremental tests are contiguous and in-order - sortTestFilenames(filenames.items); - - var prev_filename: []const u8 = ""; - for (filenames.items) |filename| { - current_file.* = filename; - - { // First, check if this file is part of an incremental update sequence - - // Split filename into ".." - const prev_parts = getTestFileNameParts(prev_filename); - const new_parts = getTestFileNameParts(filename); - - // If base_name and file_ext match, these files are in the same test sequence - // and the new one should be the incremented version of the previous test - if (std.mem.eql(u8, prev_parts.base_name, new_parts.base_name) and - std.mem.eql(u8, prev_parts.file_ext, new_parts.file_ext)) - { - - // This is "foo.X.zig" followed by "foo.Y.zig". Make sure that X = Y + 1 - if (prev_parts.test_index == null) return error.InvalidIncrementalTestIndex; - if (new_parts.test_index == null) return error.InvalidIncrementalTestIndex; - if (new_parts.test_index.? != prev_parts.test_index.? + 1) return error.InvalidIncrementalTestIndex; - } else { - - // This is not the same test sequence, so the new file must be the first file - // in a new sequence ("*.0.zig") or an independent test file ("*.zig") - if (new_parts.test_index != null and new_parts.test_index.? != 0) return error.InvalidIncrementalTestIndex; - - if (strategy == .independent) - opt_case = null; // Generate a new independent test case for this update - } - } - prev_filename = filename; - - const max_file_size = 10 * 1024 * 1024; - const src = try dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0); - - // The manifest is the last contiguous block of comments in the file - // We scan for the beginning by searching backward for the first non-empty line that does not start with "//" - var manifest_start: ?usize = null; - var manifest_end: usize = src.len; - if (src.len > 0) { - var cursor: usize = src.len - 1; - while (true) { - // Move to beginning of line - while (cursor > 0 and src[cursor - 1] != '\n') cursor -= 1; - - if (std.mem.startsWith(u8, src[cursor..], "//")) { - manifest_start = cursor; // Contiguous comment line, include in manifest - } else { - if (manifest_start != null) break; // Encountered non-comment line, end of manifest - - // We ignore all-whitespace lines following the comment block, but anything else - // means that there is no manifest present. - if (std.mem.trim(u8, src[cursor..manifest_end], " \r\n\t").len == 0) { - manifest_end = cursor; - } else break; // If it's not whitespace, there is no manifest - } - - // Move to previous line - if (cursor != 0) cursor -= 1 else break; - } - } - - var errors = std.ArrayList([]const u8).init(ctx.arena); - - if (manifest_start) |start| { - // Due to the above processing, we know that this is a contiguous block of comments - // and do not need to re-validate the leading "//" on each line - var manifest_it = std.mem.tokenize(u8, src[start..manifest_end], "\r\n"); - - // First line is the test case name - const first_line = manifest_it.next() orelse return error.MissingTestCaseName; - const case_name = try std.mem.concat(ctx.arena, u8, &.{ name, ": ", std.mem.trim(u8, first_line[2..], " \t") }); - - // If the second line is present, it should be blank - if (manifest_it.next()) |second_line| { - if (std.mem.trim(u8, second_line[2..], " \t").len != 0) return error.SecondLineNotBlank; - } - - // All following lines are expected error messages - while (manifest_it.next()) |line| try errors.append(try ctx.arena.dupe(u8, std.mem.trim(u8, line[2..], " \t"))); - - const case = opt_case orelse case: { - ctx.cases.append(TestContext.Case{ - .name = name, - .target = .{}, - .backend = backend, - .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), - .is_test = is_test, - .output_mode = output_mode, - .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), - }) catch @panic("out of memory"); - const case = &ctx.cases.items[ctx.cases.items.len - 1]; - opt_case = case; - break :case case; - }; - switch (strategy) { - .independent => { - case.name = case_name; - case.addError(src, errors.items); - }, - .incremental => { - case.addErrorNamed(case_name, src, errors.items); - }, - } - } else { - return error.MissingManifest; - } - } - } - fn init(gpa: Allocator, arena: Allocator) TestContext { return .{ .cases = std.ArrayList(Case).init(gpa), From 9181c98225dccbe698efafffd9a7bc6f50b8b46d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 28 Apr 2022 11:13:33 -0700 Subject: [PATCH 1295/2031] std.build: fix dll-export-fns API We have a pattern using `?bool` for the -f/-fno- style flags. Fixup for 8e3add8736be683b450c2754bedb064811baed0e. --- lib/std/build.zig | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index c126fa50b4..ea7f495404 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1583,7 +1583,7 @@ pub const LibExeObjStep = struct { red_zone: ?bool = null, omit_frame_pointer: ?bool = null, - no_dll_export_fns: bool = false, + dll_export_fns: ?bool = null, subsystem: ?std.Target.SubSystem = null, @@ -2640,8 +2640,12 @@ pub const LibExeObjStep = struct { try zig_args.append("-fno-omit-frame-pointer"); } } - if (self.no_dll_export_fns) { - try zig_args.append("-fno-dll-export-fns"); + if (self.dll_export_fns) |dll_export_fns| { + if (dll_export_fns) { + try zig_args.append("-fdll-export-fns"); + } else { + try zig_args.append("-fno-dll-export-fns"); + } } if (self.disable_sanitize_c) { try zig_args.append("-fno-sanitize-c"); From 3052597a734f87727fa7f1a0e92247f100df3e96 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 28 Apr 2022 11:20:53 -0700 Subject: [PATCH 1296/2031] Revert "std.testing: add writeZigFile for TmpDir" This reverts commit 7f13f5cd5f5a518638b15d7225eae2d88ec1efb5. I'd like to review this one before it goes in. This is an awfully specific API that I don't think belongs in std.testing. Also I don't want any code snippets in doc strings. We have doctests for that. --- lib/std/child_process.zig | 17 ++++++++++++----- lib/std/testing.zig | 37 ------------------------------------- 2 files changed, 12 insertions(+), 42 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index f3c35dde2f..f2b978ba9f 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -1357,16 +1357,23 @@ test "build and call child_process" { var it = try std.process.argsWithAllocator(allocator); defer it.deinit(); // no-op unless WASI or Windows const testargs = try testing.getTestArgs(&it); + var tmp = testing.tmpDir(.{ .no_follow = true }); // ie zig-cache/tmp/8DLgoSEqz593PAEE defer tmp.cleanup(); + const tmpdirpath = try tmp.getFullPath(allocator); + defer allocator.free(tmpdirpath); const child_name = "child"; // no need for suffixes (.exe, .wasm) due to '-femit-bin' - const zigfile_path = try tmp.writeZigFile(allocator, childstr, child_name); - defer allocator.free(zigfile_path); + const suffix_zig = ".zig"; + const child_path = try fs.path.join(allocator, &[_][]const u8{ tmpdirpath, child_name }); + defer allocator.free(child_path); + const child_zig = try mem.concat(allocator, u8, &[_][]const u8{ child_path, suffix_zig }); + defer allocator.free(child_zig); + + try tmp.dir.writeFile("child.zig", childstr); + try testing.buildExe(testargs.zigexec, child_zig, child_path); - const binary = zigfile_path[0 .. zigfile_path.len - 4]; // '.zig' is 4 characters - try testing.buildExe(testargs.zigexec, zigfile_path, binary); // spawn compiled file as child_process with argument 'hello world' + expect success - const args = [_][]const u8{ binary, "hello world" }; + const args = [_][]const u8{ child_path, "hello world" }; var child_proc = try ChildProcess.init(&args, allocator); defer child_proc.deinit(); const ret_val = try child_proc.spawnAndWait(); diff --git a/lib/std/testing.zig b/lib/std/testing.zig index b90fbc2e4f..016e84acd8 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -374,43 +374,6 @@ pub const TmpDir = struct { self.parent_dir.close(); self.* = undefined; } - - /// Writes program string as zig file into tmp directory - /// Caller owns memory - /// - /// ``` - /// const progstr = "pub fn main() void {}\n"; - /// var it = try std.process.argsWithAllocator(std.testing.allocator); - /// defer it.deinit(); // no-op unless WASI or Windows - /// const testargs = try std.testing.getTestArgs(&it); - /// var tmp = std.testing.tmpDir(.{ .no_follow = true }); // ie zig-cache/tmp/8DLgoSEqz593PAEE - /// defer tmp.cleanup(); - /// const zigfile_path = try tmp.writeZigFile(std.testing.allocator, progstr, "bruh"); - /// defer std.testing.allocator.free(zigfile_path); - /// const binary = zigfile_path[0 .. zigfile_path.len - 4]; // '.zig' is 4 characters - /// try std.testing.buildExe(testargs.zigexec, zigfile_path, binary); - /// ``` - pub fn writeZigFile( - self: *TmpDir, - alloc: std.mem.Allocator, - progstr: []const u8, - filename: []const u8, - ) ![]const u8 { - const tmpdir_path = try self.getFullPath(alloc); - defer alloc.free(tmpdir_path); - const suffix_zig = ".zig"; - const zigfile_path = try std.mem.concat(alloc, u8, &[_][]const u8{ - tmpdir_path, - std.fs.path.sep_str, - filename, - suffix_zig, - }); - errdefer alloc.free(zigfile_path); - const zigfile = try std.mem.concat(alloc, u8, &[_][]const u8{ filename, suffix_zig }); - defer alloc.free(zigfile); - try self.dir.writeFile(zigfile, progstr); - return zigfile_path; - } }; fn getCwdOrWasiPreopen() std.fs.Dir { From c5e847744c82953d44189a2c7094c8257fdf5b09 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 28 Apr 2022 14:11:53 -0700 Subject: [PATCH 1297/2031] Revert "Merge pull request #11214 from iddev5/ay-build-runner" This reverts commit 75c9936737a6ba991d4ef187ddc9d51bc0ad0998, reversing changes made to 7f13f5cd5f5a518638b15d7225eae2d88ec1efb5. I don't think `runZigBuild` belongs in std.testing. We already have `test/standalone/*` for this. Additionally test names should explain what they are testing rather than referencing GitHub issue numbers. --- lib/std/build.zig | 55 -------------------------------- lib/std/special/build_runner.zig | 2 +- lib/std/testing.zig | 30 ----------------- 3 files changed, 1 insertion(+), 86 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index ea7f495404..453f20c333 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -3586,58 +3586,3 @@ test "LibExeObjStep.addPackage" { const dupe = exe.packages.items[0]; try std.testing.expectEqualStrings(pkg_top.name, dupe.name); } - -test "build_runner issue 10381" { - if (builtin.os.tag == .wasi) return error.SkipZigTest; - - const progstr = - \\ pub fn main() u8 { - \\ return 1; - \\ } - ; - - const buildstr = - \\ const std = @import("std"); - \\ pub fn build(b: *std.build.Builder) void { - \\ const exe = b.addExecutable("source", "source.zig"); - \\ exe.install(); - \\ const run_cmd = exe.run(); - \\ run_cmd.step.dependOn(b.getInstallStep()); - \\ const run_step = b.step("run", "Run"); - \\ run_step.dependOn(&run_cmd.step); - \\ } - ; - - const testing = std.testing; - const allocator = testing.allocator; - - var it = try std.process.argsWithAllocator(allocator); - defer it.deinit(); - const testargs = try testing.getTestArgs(&it); - - var tmpdir = testing.tmpDir(.{ .no_follow = true }); - defer tmpdir.cleanup(); - const tmpdir_path = try tmpdir.getFullPath(allocator); - defer allocator.free(tmpdir_path); - - try tmpdir.dir.writeFile("source.zig", progstr); - try tmpdir.dir.writeFile("build.zig", buildstr); - - const cwd_path = try std.process.getCwdAlloc(allocator); - defer allocator.free(cwd_path); - const lib_dir = try std.fs.path.join(allocator, &.{ cwd_path, "lib" }); - defer allocator.free(lib_dir); - - const result = try testing.runZigBuild(testargs.zigexec, .{ - .subcmd = "run", - .cwd = tmpdir_path, - .lib_dir = lib_dir, - }); - defer { - allocator.free(result.stdout); - allocator.free(result.stderr); - } - - try testing.expectEqual(result.term, .{ .Exited = 1 }); - try testing.expect(std.mem.indexOf(u8, result.stderr, "error: UnexpectedExitCode") == null); -} diff --git a/lib/std/special/build_runner.zig b/lib/std/special/build_runner.zig index e844866c79..523723ddf2 100644 --- a/lib/std/special/build_runner.zig +++ b/lib/std/special/build_runner.zig @@ -209,7 +209,7 @@ pub fn main() !void { error.InvalidStepName => { return usageAndErr(builder, true, stderr_stream); }, - error.UnexpectedExitCode, error.UncleanExit => process.exit(1), + error.UncleanExit => process.exit(1), else => return err, } }; diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 016e84acd8..cfdf300c04 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -450,36 +450,6 @@ pub fn buildExe(zigexec: []const u8, zigfile: []const u8, binfile: []const u8) ! try expectEqual(ret_val, .{ .Exited = 0 }); } -/// Spawns a zig build runner process 'zigexec build subcmd' and -/// expects success -/// If specified, runs zig build in the cwd path -/// If specified, uses the specified lib_dir for zig standard library -/// instead of compiler's default library directory -pub fn runZigBuild(zigexec: []const u8, options: struct { - subcmd: ?[]const u8 = null, - cwd: ?[]const u8 = null, - lib_dir: ?[]const u8 = null, -}) !std.ChildProcess.ExecResult { - var args = std.ArrayList([]const u8).init(allocator); - defer args.deinit(); - - try args.appendSlice(&.{ zigexec, "build" }); - if (options.subcmd) |subcmd| try args.append(subcmd); - if (options.lib_dir) |lib_dir| try args.append(lib_dir); - - var result = try std.ChildProcess.exec(.{ - .allocator = allocator, - .argv = args.items, - .cwd = if (options.cwd) |c| c else null, - }); - errdefer { - allocator.free(result.stdout); - allocator.free(result.stderr); - } - - return result; -} - test "expectEqual nested array" { const a = [2][2]f32{ [_]f32{ 1.0, 0.0 }, From 18d6523888ef08bc66eb808075d13c5e00b8fcf4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 28 Apr 2022 17:11:14 -0700 Subject: [PATCH 1298/2031] compiler-rt: upgrade to stage2 fn ptr semantics --- lib/std/special/compiler_rt/clzsi2_test.zig | 20 ++++++++++++++------ lib/std/special/compiler_rt/truncXfYf2.zig | 5 ++++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/lib/std/special/compiler_rt/clzsi2_test.zig b/lib/std/special/compiler_rt/clzsi2_test.zig index 7d07b3e9c1..9ad5d3afe7 100644 --- a/lib/std/special/compiler_rt/clzsi2_test.zig +++ b/lib/std/special/compiler_rt/clzsi2_test.zig @@ -1,13 +1,21 @@ +const builtin = @import("builtin"); const clz = @import("count0bits.zig"); const testing = @import("std").testing; fn test__clzsi2(a: u32, expected: i32) !void { - // XXX At high optimization levels this test may be horribly miscompiled if - // one of the naked implementations is selected. - var nakedClzsi2 = clz.__clzsi2; - var actualClzsi2 = @ptrCast(fn (a: i32) callconv(.C) i32, nakedClzsi2); - var x = @bitCast(i32, a); - var result = actualClzsi2(x); + const nakedClzsi2 = clz.__clzsi2; + const fnProto = fn (a: i32) callconv(.C) i32; + const fnProtoPtr = switch (builtin.zig_backend) { + .stage1 => fnProto, + else => *const fnProto, + }; + const fn_ptr = switch (builtin.zig_backend) { + .stage1 => nakedClzsi2, + else => &nakedClzsi2, + }; + const actualClzsi2 = @ptrCast(fnProtoPtr, fn_ptr); + const x = @bitCast(i32, a); + const result = actualClzsi2(x); try testing.expectEqual(expected, result); } diff --git a/lib/std/special/compiler_rt/truncXfYf2.zig b/lib/std/special/compiler_rt/truncXfYf2.zig index fea1aeb60a..bf324269a6 100644 --- a/lib/std/special/compiler_rt/truncXfYf2.zig +++ b/lib/std/special/compiler_rt/truncXfYf2.zig @@ -4,6 +4,8 @@ const native_arch = builtin.cpu.arch; // AArch64 is the only ABI (at the moment) to support f16 arguments without the // need for extending them to wider fp types. +// TODO remove this; do this type selection in the language rather than +// here in compiler-rt. pub const F16T = if (native_arch.isAARCH64()) f16 else u16; pub fn __truncsfhf2(a: f32) callconv(.C) F16T { @@ -140,7 +142,8 @@ inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t } } - const result: dst_rep_t align(@alignOf(dst_t)) = absResult | @truncate(dst_rep_t, sign >> @intCast(SrcShift, srcBits - dstBits)); + const result: dst_rep_t align(@alignOf(dst_t)) = absResult | + @truncate(dst_rep_t, sign >> @intCast(SrcShift, srcBits - dstBits)); return @bitCast(dst_t, result); } From d8e99164d35fbbcd7e33773e9f35be70d184eaf5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 28 Apr 2022 17:11:42 -0700 Subject: [PATCH 1299/2031] AstGen: encode negativity into float literals rather than a separate negation instruction. closes #11545 --- src/AstGen.zig | 79 +++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 43 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 230b46a489..c9fd82bd5f 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -694,11 +694,11 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr .bool_and => return boolBinOp(gz, scope, rl, node, .bool_br_and), .bool_or => return boolBinOp(gz, scope, rl, node, .bool_br_or), - .bool_not => return boolNot(gz, scope, rl, node), - .bit_not => return bitNot(gz, scope, rl, node), + .bool_not => return simpleUnOp(gz, scope, rl, node, bool_rl, node_datas[node].lhs, .bool_not), + .bit_not => return simpleUnOp(gz, scope, rl, node, .none, node_datas[node].lhs, .bit_not), - .negation => return negation(gz, scope, rl, node, .negate), - .negation_wrap => return negation(gz, scope, rl, node, .negate_wrap), + .negation => return negation(gz, scope, rl, node), + .negation_wrap => return simpleUnOp(gz, scope, rl, node, .none, node_datas[node].lhs, .negate_wrap), .identifier => return identifier(gz, scope, rl, node), @@ -748,7 +748,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr }, .@"return" => return ret(gz, scope, node), .field_access => return fieldAccess(gz, scope, rl, node), - .float_literal => return floatLiteral(gz, rl, node), + .float_literal => return floatLiteral(gz, rl, node, .positive), .if_simple => return ifExpr(gz, scope, rl.br(), node, tree.ifSimple(node)), .@"if" => return ifExpr(gz, scope, rl.br(), node, tree.ifFull(node)), @@ -3029,42 +3029,6 @@ fn assignShiftSat(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerE _ = try gz.addBin(.store, lhs_ptr, result); } -fn boolNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { - const astgen = gz.astgen; - const tree = astgen.tree; - const node_datas = tree.nodes.items(.data); - - const operand = try expr(gz, scope, bool_rl, node_datas[node].lhs); - const result = try gz.addUnNode(.bool_not, operand, node); - return rvalue(gz, rl, result, node); -} - -fn bitNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { - const astgen = gz.astgen; - const tree = astgen.tree; - const node_datas = tree.nodes.items(.data); - - const operand = try expr(gz, scope, .none, node_datas[node].lhs); - const result = try gz.addUnNode(.bit_not, operand, node); - return rvalue(gz, rl, result, node); -} - -fn negation( - gz: *GenZir, - scope: *Scope, - rl: ResultLoc, - node: Ast.Node.Index, - tag: Zir.Inst.Tag, -) InnerError!Zir.Inst.Ref { - const astgen = gz.astgen; - const tree = astgen.tree; - const node_datas = tree.nodes.items(.data); - - const operand = try expr(gz, scope, .none, node_datas[node].lhs); - const result = try gz.addUnNode(tag, operand, node); - return rvalue(gz, rl, result, node); -} - fn ptrType( gz: *GenZir, scope: *Scope, @@ -6728,14 +6692,16 @@ fn integerLiteral(gz: *GenZir, rl: ResultLoc, node: Ast.Node.Index) InnerError!Z return rvalue(gz, rl, result, node); } -fn floatLiteral(gz: *GenZir, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { +const Sign = enum { negative, positive }; + +fn floatLiteral(gz: *GenZir, rl: ResultLoc, node: Ast.Node.Index, sign: Sign) InnerError!Zir.Inst.Ref { const astgen = gz.astgen; const tree = astgen.tree; const main_tokens = tree.nodes.items(.main_token); const main_token = main_tokens[node]; const bytes = tree.tokenSlice(main_token); - const float_number: f128 = if (bytes.len > 2 and bytes[1] == 'x') hex: { + const unsigned_float_number: f128 = if (bytes.len > 2 and bytes[1] == 'x') hex: { assert(bytes[0] == '0'); // validated by tokenizer break :hex std.fmt.parseHexFloat(f128, bytes) catch |err| switch (err) { error.InvalidCharacter => unreachable, // validated by tokenizer @@ -6744,6 +6710,10 @@ fn floatLiteral(gz: *GenZir, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir } else std.fmt.parseFloat(f128, bytes) catch |err| switch (err) { error.InvalidCharacter => unreachable, // validated by tokenizer }; + const float_number = switch (sign) { + .negative => -unsigned_float_number, + .positive => unsigned_float_number, + }; // If the value fits into a f64 without losing any precision, store it that way. @setFloatMode(.Strict); const smaller_float = @floatCast(f64, float_number); @@ -7651,6 +7621,29 @@ fn simpleUnOp( return rvalue(gz, rl, result, node); } +fn negation( + gz: *GenZir, + scope: *Scope, + rl: ResultLoc, + node: Ast.Node.Index, +) InnerError!Zir.Inst.Ref { + const astgen = gz.astgen; + const tree = astgen.tree; + const node_tags = tree.nodes.items(.tag); + const node_datas = tree.nodes.items(.data); + + // Check for float literal as the sub-expression because we want to preserve + // its negativity rather than having it go through comptime subtraction. + const operand_node = node_datas[node].lhs; + if (node_tags[operand_node] == .float_literal) { + return floatLiteral(gz, rl, operand_node, .negative); + } + + const operand = try expr(gz, scope, .none, operand_node); + const result = try gz.addUnNode(.negate, operand, node); + return rvalue(gz, rl, result, node); +} + fn cmpxchg( gz: *GenZir, scope: *Scope, From a242906696a29b7ce251bd416bb995ca37e95d92 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 28 Apr 2022 20:39:33 -0700 Subject: [PATCH 1300/2031] compiler-rt: restore stage1 workaround 18d6523888ef08bc66eb808075d13c5e00b8fcf4 regressed compiler-rt tests for stage1 because it removed a workaround. I updated the comment to better explain what exactly the workaround is so that it won't happen again. --- lib/std/special/compiler_rt/clzsi2_test.zig | 32 ++++++++++++--------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/lib/std/special/compiler_rt/clzsi2_test.zig b/lib/std/special/compiler_rt/clzsi2_test.zig index 9ad5d3afe7..ef64e24fe1 100644 --- a/lib/std/special/compiler_rt/clzsi2_test.zig +++ b/lib/std/special/compiler_rt/clzsi2_test.zig @@ -3,20 +3,24 @@ const clz = @import("count0bits.zig"); const testing = @import("std").testing; fn test__clzsi2(a: u32, expected: i32) !void { - const nakedClzsi2 = clz.__clzsi2; - const fnProto = fn (a: i32) callconv(.C) i32; - const fnProtoPtr = switch (builtin.zig_backend) { - .stage1 => fnProto, - else => *const fnProto, - }; - const fn_ptr = switch (builtin.zig_backend) { - .stage1 => nakedClzsi2, - else => &nakedClzsi2, - }; - const actualClzsi2 = @ptrCast(fnProtoPtr, fn_ptr); - const x = @bitCast(i32, a); - const result = actualClzsi2(x); - try testing.expectEqual(expected, result); + // stage1 and stage2 diverge on function pointer semantics + switch (builtin.zig_backend) { + .stage1 => { + // Use of `var` here is working around a stage1 bug. + var nakedClzsi2 = clz.__clzsi2; + var actualClzsi2 = @ptrCast(fn (a: i32) callconv(.C) i32, nakedClzsi2); + var x = @bitCast(i32, a); + var result = actualClzsi2(x); + try testing.expectEqual(expected, result); + }, + else => { + const nakedClzsi2 = clz.__clzsi2; + const actualClzsi2 = @ptrCast(*const fn (a: i32) callconv(.C) i32, &nakedClzsi2); + const x = @bitCast(i32, a); + const result = actualClzsi2(x); + try testing.expectEqual(expected, result); + }, + } } test "clzsi2" { From 2409041e627012baa770acfd21c407ef9ba46777 Mon Sep 17 00:00:00 2001 From: Stephen Gregoratto Date: Fri, 29 Apr 2022 12:37:04 +1000 Subject: [PATCH 1301/2031] Seccomp fixups re: #10717 - Add type annotation for AUDIT.current. - Make unsupported archs a compile error. --- lib/std/os/linux.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index dd488edf42..de79091f53 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -5421,7 +5421,7 @@ pub const AUDIT = struct { const _64BIT = 0x80000000; const _LE = 0x40000000; - pub const current = switch (native_arch) { + pub const current: AUDIT.ARCH = switch (native_arch) { .i386 => .I386, .x86_64 => .X86_64, .aarch64 => .AARCH64, @@ -5433,7 +5433,7 @@ pub const AUDIT = struct { .powerpc => .PPC, .powerpc64 => .PPC64, .powerpc64le => .PPC64LE, - else => undefined, + else => @compileError("unsupported architecture"), }; AARCH64 = toAudit(.aarch64), From fda143d5d81da852af73386a2100e18784bd0d3c Mon Sep 17 00:00:00 2001 From: Daniele Cocca Date: Thu, 28 Apr 2022 22:59:16 +0100 Subject: [PATCH 1302/2031] CBE: fix renderValue() for struct fields with no runtime bits These shouldn't count towards the total emitted, or the stray comma separators would cause compilation errors. --- src/codegen/c.zig | 6 ++++-- test/behavior/struct.zig | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 46fee271cc..8f73daca46 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -821,12 +821,14 @@ pub const DeclGen = struct { try dg.renderTypecast(writer, ty); try writer.writeAll("){"); - for (field_vals) |field_val, i| { - const field_ty = ty.structFieldType(i); + var i: usize = 0; + for (field_vals) |field_val, field_index| { + const field_ty = ty.structFieldType(field_index); if (!field_ty.hasRuntimeBits()) continue; if (i != 0) try writer.writeAll(","); try dg.renderValue(writer, field_ty, field_val, location); + i += 1; } try writer.writeAll("}"); diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index f2b11937b0..89f6e20aa0 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -299,7 +299,6 @@ test "struct point to self" { test "void struct fields" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const foo = VoidStructFieldsFoo{ From 0e49142ce478c13f3ec701900cd894b3536471a1 Mon Sep 17 00:00:00 2001 From: Natalia Cholewa Date: Sun, 24 Apr 2022 19:09:10 +0200 Subject: [PATCH 1303/2031] std.SegmentedList: add constIterator --- lib/std/segmented_list.zig | 134 +++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 52 deletions(-) diff --git a/lib/std/segmented_list.zig b/lib/std/segmented_list.zig index 27667353ef..72e956a637 100644 --- a/lib/std/segmented_list.zig +++ b/lib/std/segmented_list.zig @@ -294,71 +294,75 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } } - pub const Iterator = struct { - list: *Self, - index: usize, - box_index: usize, - shelf_index: ShelfIndex, - shelf_size: usize, + pub const Iterator = BaseIterator(*Self, *T); + pub const ConstIterator = BaseIterator(*const Self, *const T); + fn BaseIterator(comptime SelfType: type, comptime ElementPtr: type) type { + return struct { + list: SelfType, + index: usize, + box_index: usize, + shelf_index: ShelfIndex, + shelf_size: usize, - pub fn next(it: *Iterator) ?*T { - if (it.index >= it.list.len) return null; - if (it.index < prealloc_item_count) { - const ptr = &it.list.prealloc_segment[it.index]; + pub fn next(it: *@This()) ?ElementPtr { + if (it.index >= it.list.len) return null; + if (it.index < prealloc_item_count) { + const ptr = &it.list.prealloc_segment[it.index]; + it.index += 1; + if (it.index == prealloc_item_count) { + it.box_index = 0; + it.shelf_index = 0; + it.shelf_size = prealloc_item_count * 2; + } + return ptr; + } + + const ptr = &it.list.dynamic_segments[it.shelf_index][it.box_index]; it.index += 1; - if (it.index == prealloc_item_count) { + it.box_index += 1; + if (it.box_index == it.shelf_size) { + it.shelf_index += 1; it.box_index = 0; - it.shelf_index = 0; - it.shelf_size = prealloc_item_count * 2; + it.shelf_size *= 2; } return ptr; } - const ptr = &it.list.dynamic_segments[it.shelf_index][it.box_index]; - it.index += 1; - it.box_index += 1; - if (it.box_index == it.shelf_size) { - it.shelf_index += 1; - it.box_index = 0; - it.shelf_size *= 2; - } - return ptr; - } + pub fn prev(it: *@This()) ?ElementPtr { + if (it.index == 0) return null; - pub fn prev(it: *Iterator) ?*T { - if (it.index == 0) return null; + it.index -= 1; + if (it.index < prealloc_item_count) return &it.list.prealloc_segment[it.index]; - it.index -= 1; - if (it.index < prealloc_item_count) return &it.list.prealloc_segment[it.index]; + if (it.box_index == 0) { + it.shelf_index -= 1; + it.shelf_size /= 2; + it.box_index = it.shelf_size - 1; + } else { + it.box_index -= 1; + } - if (it.box_index == 0) { - it.shelf_index -= 1; - it.shelf_size /= 2; - it.box_index = it.shelf_size - 1; - } else { - it.box_index -= 1; + return &it.list.dynamic_segments[it.shelf_index][it.box_index]; } - return &it.list.dynamic_segments[it.shelf_index][it.box_index]; - } + pub fn peek(it: *@This()) ?ElementPtr { + if (it.index >= it.list.len) + return null; + if (it.index < prealloc_item_count) + return &it.list.prealloc_segment[it.index]; - pub fn peek(it: *Iterator) ?*T { - if (it.index >= it.list.len) - return null; - if (it.index < prealloc_item_count) - return &it.list.prealloc_segment[it.index]; + return &it.list.dynamic_segments[it.shelf_index][it.box_index]; + } - return &it.list.dynamic_segments[it.shelf_index][it.box_index]; - } - - pub fn set(it: *Iterator, index: usize) void { - it.index = index; - if (index < prealloc_item_count) return; - it.shelf_index = shelfIndex(index); - it.box_index = boxIndex(index, it.shelf_index); - it.shelf_size = shelfSize(it.shelf_index); - } - }; + pub fn set(it: *@This(), index: usize) void { + it.index = index; + if (index < prealloc_item_count) return; + it.shelf_index = shelfIndex(index); + it.box_index = boxIndex(index, it.shelf_index); + it.shelf_size = shelfSize(it.shelf_index); + } + }; + } pub fn iterator(self: *Self, start_index: usize) Iterator { var it = Iterator{ @@ -371,10 +375,22 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type it.set(start_index); return it; } + + pub fn constIterator(self: *const Self, start_index: usize) ConstIterator { + var it = ConstIterator{ + .list = self, + .index = undefined, + .shelf_index = undefined, + .box_index = undefined, + .shelf_size = undefined, + }; + it.set(start_index); + return it; + } }; } -test "basic usage" { +test "SegmentedList basic usage" { try testSegmentedList(0); try testSegmentedList(1); try testSegmentedList(2); @@ -418,6 +434,20 @@ fn testSegmentedList(comptime prealloc: usize) !void { try testing.expect(x == 0); } + { + var it = list.constIterator(0); + var x: i32 = 0; + while (it.next()) |item| { + x += 1; + try testing.expect(item.* == x); + } + try testing.expect(x == 100); + while (it.prev()) |item| : (x -= 1) { + try testing.expect(item.* == x); + } + try testing.expect(x == 0); + } + try testing.expect(list.pop().? == 100); try testing.expect(list.len == 99); From a6f254ec3e106e6496a84c89375427e6adc9065f Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 29 Apr 2022 10:38:47 +0300 Subject: [PATCH 1304/2031] stage2: fix comptime unreachable --- src/AstGen.zig | 2 +- src/Sema.zig | 9 +++++---- src/Zir.zig | 6 +----- src/print_zir.zig | 2 -- test/compile_errors/stage2/comptime_unreachable.zig | 7 +++++++ test/incremental/x86_64-linux/comptime_var.3.zig | 2 +- test/incremental/x86_64-macos/comptime_var.3.zig | 2 +- 7 files changed, 16 insertions(+), 14 deletions(-) create mode 100644 test/compile_errors/stage2/comptime_unreachable.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index c9fd82bd5f..006dbc5778 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -740,7 +740,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr _ = try gz.addAsIndex(.{ .tag = .@"unreachable", .data = .{ .@"unreachable" = .{ - .safety = true, + .force_comptime = gz.force_comptime, .src_node = gz.nodeIndexToRelative(node), } }, }); diff --git a/src/Sema.zig b/src/Sema.zig index c9d8f090ae..884f3ab847 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12337,14 +12337,15 @@ fn addRuntimeBreak(sema: *Sema, child_block: *Block, break_data: BreakData) !voi } fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { - const tracy = trace(@src()); - defer tracy.end(); - const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable"; const src = inst_data.src(); + + if (block.is_comptime or inst_data.force_comptime) { + return sema.fail(block, src, "reached unreachable code", .{}); + } try sema.requireRuntimeBlock(block, src); // TODO Add compile error for @optimizeFor occurring too late in a scope. - try block.addUnreachable(src, inst_data.safety); + try block.addUnreachable(src, true); return always_noreturn; } diff --git a/src/Zir.zig b/src/Zir.zig index f4c62a6f24..fc638ef869 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -2502,11 +2502,7 @@ pub const Inst = struct { /// Offset from Decl AST node index. /// `Tag` determines which kind of AST node this points to. src_node: i32, - /// `false`: Not safety checked - the compiler will assume the - /// correctness of this instruction. - /// `true`: In safety-checked modes, this will generate a call - /// to the panic function unless it can be proven unreachable by the compiler. - safety: bool, + force_comptime: bool, pub fn src(self: @This()) LazySrcLoc { return .{ .node_offset = self.src_node }; diff --git a/src/print_zir.zig b/src/print_zir.zig index 776aeffbdc..ed1eeeeba3 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -2091,8 +2091,6 @@ const Writer = struct { fn writeUnreachable(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].@"unreachable"; - const safety_str = if (inst_data.safety) "safe" else "unsafe"; - try stream.print("{s}) ", .{safety_str}); try self.writeSrc(stream, inst_data.src()); } diff --git a/test/compile_errors/stage2/comptime_unreachable.zig b/test/compile_errors/stage2/comptime_unreachable.zig new file mode 100644 index 0000000000..6086dd85c6 --- /dev/null +++ b/test/compile_errors/stage2/comptime_unreachable.zig @@ -0,0 +1,7 @@ +pub export fn entry() void { + comptime unreachable; +} + +// error +// +// :2:14: error: reached unreachable code diff --git a/test/incremental/x86_64-linux/comptime_var.3.zig b/test/incremental/x86_64-linux/comptime_var.3.zig index d4e6d85d9d..5d25d6556e 100644 --- a/test/incremental/x86_64-linux/comptime_var.3.zig +++ b/test/incremental/x86_64-linux/comptime_var.3.zig @@ -7,4 +7,4 @@ pub fn main() void {} // error // -// :4:17: error: unable to resolve comptime value +// :4:17: error: reached unreachable code diff --git a/test/incremental/x86_64-macos/comptime_var.3.zig b/test/incremental/x86_64-macos/comptime_var.3.zig index d4e6d85d9d..5d25d6556e 100644 --- a/test/incremental/x86_64-macos/comptime_var.3.zig +++ b/test/incremental/x86_64-macos/comptime_var.3.zig @@ -7,4 +7,4 @@ pub fn main() void {} // error // -// :4:17: error: unable to resolve comptime value +// :4:17: error: reached unreachable code From 1d455896cb24165c5a3e0b3e10934c60a285589d Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 29 Apr 2022 12:23:14 +0300 Subject: [PATCH 1305/2031] Zir: move more common instructions out of extended --- src/AstGen.zig | 83 ++++++++++++++++++--------- src/Sema.zig | 139 +++++++++++++++++++++++----------------------- src/Zir.zig | 86 ++++++++++++++-------------- src/print_zir.zig | 35 +++++++----- 4 files changed, 192 insertions(+), 151 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 006dbc5778..b0cd976b00 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1078,8 +1078,14 @@ fn awaitExpr( }); } const operand = try expr(gz, scope, .none, rhs_node); - const tag: Zir.Inst.Tag = if (gz.nosuspend_node != 0) .await_nosuspend else .@"await"; - const result = try gz.addUnNode(tag, operand, node); + const result = if (gz.nosuspend_node != 0) + try gz.addExtendedPayload(.await_nosuspend, Zir.Inst.UnNode{ + .node = gz.nodeIndexToRelative(node), + .operand = operand, + }) + else + try gz.addUnNode(.@"await", operand, node); + return rvalue(gz, rl, result, node); } @@ -2349,7 +2355,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .int_to_ptr, .float_cast, .int_cast, - .err_set_cast, .ptr_cast, .truncate, .align_cast, @@ -2386,15 +2391,24 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .c_import, .@"resume", .@"await", - .await_nosuspend, .ret_err_value_code, - .extended, .closure_get, .array_base_ptr, .field_base_ptr, .param_type, + .ret_ptr, + .ret_type, => break :b false, + .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) { + .breakpoint, + .fence, + .set_align_stack, + .set_float_mode, + => break :b true, + else => break :b false, + }, + // ZIR instructions that are always `noreturn`. .@"break", .break_inline, @@ -2415,11 +2429,11 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner }, // ZIR instructions that are always `void`. - .breakpoint, - .fence, .dbg_stmt, .dbg_var_ptr, .dbg_var_val, + .dbg_block_begin, + .dbg_block_end, .ensure_result_used, .ensure_result_non_error, .@"export", @@ -2436,9 +2450,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .validate_struct_init_comptime, .validate_array_init, .validate_array_init_comptime, - .set_align_stack, .set_cold, - .set_float_mode, .set_runtime_safety, .closure_capture, .memcpy, @@ -6310,9 +6322,9 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref } const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node, true)) .{ - .ptr = try gz.addNodeExtended(.ret_ptr, node), + .ptr = try gz.addNode(.ret_ptr, node), } else .{ - .ty = try gz.addNodeExtended(.ret_type, node), + .ty = try gz.addNode(.ret_type, node), }; const prev_anon_name_strategy = gz.anon_name_strategy; gz.anon_name_strategy = .func; @@ -7183,7 +7195,26 @@ fn builtinCall( }, .fence => { const order = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[0]); - const result = try gz.addUnNode(.fence, order, node); + const result = try gz.addExtendedPayload(.fence, Zir.Inst.UnNode{ + .node = gz.nodeIndexToRelative(node), + .operand = order, + }); + return rvalue(gz, rl, result, node); + }, + .set_float_mode => { + const order = try expr(gz, scope, .{ .coerced_ty = .float_mode_type }, params[0]); + const result = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{ + .node = gz.nodeIndexToRelative(node), + .operand = order, + }); + return rvalue(gz, rl, result, node); + }, + .set_align_stack => { + const order = try expr(gz, scope, align_rl, params[0]); + const result = try gz.addExtendedPayload(.set_align_stack, Zir.Inst.UnNode{ + .node = gz.nodeIndexToRelative(node), + .operand = order, + }); return rvalue(gz, rl, result, node); }, @@ -7198,14 +7229,13 @@ fn builtinCall( return rvalue(gz, rl, result, node); }, - .breakpoint => return simpleNoOpVoid(gz, rl, node, .breakpoint), - // zig fmt: off .This => return rvalue(gz, rl, try gz.addNodeExtended(.this, node), node), .return_address => return rvalue(gz, rl, try gz.addNodeExtended(.ret_addr, node), node), .error_return_trace => return rvalue(gz, rl, try gz.addNodeExtended(.error_return_trace, node), node), .frame => return rvalue(gz, rl, try gz.addNodeExtended(.frame, node), node), .frame_address => return rvalue(gz, rl, try gz.addNodeExtended(.frame_address, node), node), + .breakpoint => return rvalue(gz, rl, try gz.addNodeExtended(.breakpoint, node), node), .type_info => return simpleUnOpType(gz, scope, rl, node, params[0], .type_info), .size_of => return simpleUnOpType(gz, scope, rl, node, params[0], .size_of), @@ -7222,9 +7252,7 @@ fn builtinCall( .embed_file => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .embed_file), .error_name => return simpleUnOp(gz, scope, rl, node, .{ .ty = .anyerror_type }, params[0], .error_name), .panic => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .panic), - .set_align_stack => return simpleUnOp(gz, scope, rl, node, align_rl, params[0], .set_align_stack), .set_cold => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .set_cold), - .set_float_mode => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .float_mode_type }, params[0], .set_float_mode), .set_runtime_safety => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .set_runtime_safety), .sqrt => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sqrt), .sin => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sin), @@ -7252,7 +7280,6 @@ fn builtinCall( .int_to_enum => return typeCast(gz, scope, rl, node, params[0], params[1], .int_to_enum), .float_cast => return typeCast(gz, scope, rl, node, params[0], params[1], .float_cast), .int_cast => return typeCast(gz, scope, rl, node, params[0], params[1], .int_cast), - .err_set_cast => return typeCast(gz, scope, rl, node, params[0], params[1], .err_set_cast), .ptr_cast => return typeCast(gz, scope, rl, node, params[0], params[1], .ptr_cast), .truncate => return typeCast(gz, scope, rl, node, params[0], params[1], .truncate), // zig fmt: on @@ -7266,6 +7293,14 @@ fn builtinCall( }); return rvalue(gz, rl, result, node); }, + .err_set_cast => { + const result = try gz.addExtendedPayload(.err_set_cast, Zir.Inst.BinNode{ + .lhs = try typeExpr(gz, scope, params[0]), + .rhs = try expr(gz, scope, .none, params[1]), + .node = gz.nodeIndexToRelative(node), + }); + return rvalue(gz, rl, result, node); + }, // zig fmt: off .has_decl => return hasDeclOrField(gz, scope, rl, node, params[0], params[1], .has_decl), @@ -8940,7 +8975,8 @@ fn rvalue( const result = r: { if (refToIndex(raw_result)) |result_index| { const zir_tags = gz.astgen.instructions.items(.tag); - if (zir_tags[result_index].isAlwaysVoid()) { + const data = gz.astgen.instructions.items(.data)[result_index]; + if (zir_tags[result_index].isAlwaysVoid(data)) { break :r Zir.Inst.Ref.void_value; } } @@ -10893,9 +10929,7 @@ const GenZir = struct { fn addDbgBlockBegin(gz: *GenZir) !void { if (gz.force_comptime) return; - _ = try gz.add(.{ .tag = .extended, .data = .{ - .extended = .{ .opcode = .dbg_block_begin, .small = undefined, .operand = undefined }, - } }); + _ = try gz.add(.{ .tag = .dbg_block_begin, .data = undefined }); } fn addDbgBlockEnd(gz: *GenZir) !void { @@ -10903,18 +10937,15 @@ const GenZir = struct { const gpa = gz.astgen.gpa; const tags = gz.astgen.instructions.items(.tag); - const data = gz.astgen.instructions.items(.data); const last_inst = gz.instructions.items[gz.instructions.items.len - 1]; // remove dbg_block_begin immediately followed by dbg_block_end - if (tags[last_inst] == .extended and data[last_inst].extended.opcode == .dbg_block_begin) { + if (tags[last_inst] == .dbg_block_begin) { _ = gz.instructions.pop(); return; } const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); - try gz.astgen.instructions.append(gpa, .{ .tag = .extended, .data = .{ - .extended = .{ .opcode = .dbg_block_end, .small = undefined, .operand = undefined }, - } }); + try gz.astgen.instructions.append(gpa, .{ .tag = .dbg_block_end, .data = undefined }); try gz.instructions.insert(gpa, gz.instructions.items.len - 1, new_index); } diff --git a/src/Sema.zig b/src/Sema.zig index 884f3ab847..40b9698d49 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -819,7 +819,6 @@ fn analyzeBodyInner( .int_to_ptr => try sema.zirIntToPtr(block, inst), .float_cast => try sema.zirFloatCast(block, inst), .int_cast => try sema.zirIntCast(block, inst), - .err_set_cast => try sema.zirErrSetCast(block, inst), .ptr_cast => try sema.zirPtrCast(block, inst), .truncate => try sema.zirTruncate(block, inst), .align_cast => try sema.zirAlignCast(block, inst), @@ -842,8 +841,7 @@ fn analyzeBodyInner( .field_parent_ptr => try sema.zirFieldParentPtr(block, inst), .builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst), .@"resume" => try sema.zirResume(block, inst), - .@"await" => try sema.zirAwait(block, inst, false), - .await_nosuspend => try sema.zirAwait(block, inst, true), + .@"await" => try sema.zirAwait(block, inst), .array_base_ptr => try sema.zirArrayBasePtr(block, inst), .field_base_ptr => try sema.zirFieldBasePtr(block, inst), @@ -894,6 +892,9 @@ fn analyzeBodyInner( .shl_exact => try sema.zirShl(block, inst, .shl_exact), .shl_sat => try sema.zirShl(block, inst, .shl_sat), + .ret_ptr => try sema.zirRetPtr(block, inst), + .ret_type => try sema.zirRetType(block, inst), + // Instructions that we know to *always* be noreturn based solely on their tag. // These functions match the return type of analyzeBody so that we can // tail call them here. @@ -916,8 +917,6 @@ fn analyzeBodyInner( .enum_decl => try sema.zirEnumDecl( block, extended), .union_decl => try sema.zirUnionDecl( block, extended, inst), .opaque_decl => try sema.zirOpaqueDecl( block, extended), - .ret_ptr => try sema.zirRetPtr( block, extended), - .ret_type => try sema.zirRetType( block, extended), .this => try sema.zirThis( block, extended), .ret_addr => try sema.zirRetAddr( block, extended), .builtin_src => try sema.zirBuiltinSrc( block, extended), @@ -940,16 +939,28 @@ fn analyzeBodyInner( .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended), .prefetch => try sema.zirPrefetch( block, extended), .field_call_bind_named => try sema.zirFieldCallBindNamed(block, extended), + .err_set_cast => try sema.zirErrSetCast( block, extended), + .await_nosuspend => try sema.zirAwaitNosuspend( block, extended), // zig fmt: on - .dbg_block_begin => { - dbg_block_begins += 1; - try sema.zirDbgBlockBegin(block); + .fence => { + try sema.zirFence(block, extended); i += 1; continue; }, - .dbg_block_end => { - dbg_block_begins -= 1; - try sema.zirDbgBlockEnd(block); + .set_float_mode => { + try sema.zirSetFloatMode(block, extended); + i += 1; + continue; + }, + .set_align_stack => { + try sema.zirSetAlignStack(block, extended); + i += 1; + continue; + }, + .breakpoint => { + if (!block.is_comptime) { + _ = try block.addNoOp(.breakpoint); + } i += 1; continue; }, @@ -961,18 +972,6 @@ fn analyzeBodyInner( // continue the loop. // We also know that they cannot be referenced later, so we avoid // putting them into the map. - .breakpoint => { - if (!block.is_comptime) { - _ = try block.addNoOp(.breakpoint); - } - i += 1; - continue; - }, - .fence => { - try sema.zirFence(block, inst); - i += 1; - continue; - }, .dbg_stmt => { try sema.zirDbgStmt(block, inst); i += 1; @@ -988,6 +987,18 @@ fn analyzeBodyInner( i += 1; continue; }, + .dbg_block_begin => { + dbg_block_begins += 1; + try sema.zirDbgBlockBegin(block); + i += 1; + continue; + }, + .dbg_block_end => { + dbg_block_begins -= 1; + try sema.zirDbgBlockEnd(block); + i += 1; + continue; + }, .ensure_err_payload_void => { try sema.zirEnsureErrPayloadVoid(block, inst); i += 1; @@ -1078,21 +1089,11 @@ fn analyzeBodyInner( i += 1; continue; }, - .set_align_stack => { - try sema.zirSetAlignStack(block, inst); - i += 1; - continue; - }, .set_cold => { try sema.zirSetCold(block, inst); i += 1; continue; }, - .set_float_mode => { - try sema.zirSetFloatMode(block, inst); - i += 1; - continue; - }, .set_runtime_safety => { try sema.zirSetRuntimeSafety(block, inst); i += 1; @@ -2452,15 +2453,12 @@ fn zirErrorSetDecl( return sema.analyzeDeclVal(block, src, new_decl_index); } -fn zirRetPtr( - sema: *Sema, - block: *Block, - extended: Zir.Inst.Extended.InstData, -) CompileError!Air.Inst.Ref { +fn zirRetPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); - const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; + const inst_data = sema.code.instructions.items(.data)[inst].node; + const src: LazySrcLoc = .{ .node_offset = inst_data }; try sema.requireFunctionBlock(block, src); if (block.is_comptime) { @@ -2493,15 +2491,12 @@ fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins return sema.analyzeRef(block, inst_data.src(), operand); } -fn zirRetType( - sema: *Sema, - block: *Block, - extended: Zir.Inst.Extended.InstData, -) CompileError!Air.Inst.Ref { +fn zirRetType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); - const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; + const inst_data = sema.code.instructions.items(.data)[inst].node; + const src: LazySrcLoc = .{ .node_offset = inst_data }; try sema.requireFunctionBlock(block, src); return sema.addType(sema.fn_ret_ty); } @@ -3746,9 +3741,7 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) { if (Zir.refToIndex(extra.lhs)) |ptr_index| { - if (zir_tags[ptr_index] == .extended and - zir_datas[ptr_index].extended.opcode == .ret_ptr) - { + if (zir_tags[ptr_index] == .ret_ptr) { try sema.addToInferredErrorSet(operand); } } @@ -4392,11 +4385,11 @@ pub fn analyzeExport( errdefer de_gop.value_ptr.* = gpa.shrink(de_gop.value_ptr.*, de_gop.value_ptr.len - 1); } -fn zirSetAlignStack(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { - const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const src: LazySrcLoc = inst_data.src(); - const alignment = try sema.resolveAlign(block, operand_src, inst_data.operand); +fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { + const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; + const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; + const src: LazySrcLoc = .{ .node_offset = extra.node }; + const alignment = try sema.resolveAlign(block, operand_src, extra.operand); if (alignment > 256) { return sema.fail(block, src, "attempt to @setAlignStack({d}); maximum is 256", .{ alignment, @@ -4433,10 +4426,10 @@ fn zirSetCold(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!voi func.is_cold = is_cold; } -fn zirSetFloatMode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { - const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const src: LazySrcLoc = inst_data.src(); - const float_mode = try sema.resolveBuiltinEnum(block, src, inst_data.operand, "FloatMode"); +fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { + const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; + const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; + const float_mode = try sema.resolveBuiltinEnum(block, src, extra.operand, "FloatMode"); switch (float_mode) { .Strict => return, .Optimized => { @@ -4451,12 +4444,12 @@ fn zirSetRuntimeSafety(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compile block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand); } -fn zirFence(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { +fn zirFence(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { if (block.is_comptime) return; - const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const order = try sema.resolveAtomicOrder(block, order_src, inst_data.operand); + const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; + const order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; + const order = try sema.resolveAtomicOrder(block, order_src, extra.operand); if (@enumToInt(order) < @enumToInt(std.builtin.AtomicOrder.Acquire)) { return sema.fail(block, order_src, "atomic ordering must be Acquire or stricter", .{}); @@ -14144,12 +14137,11 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return block.addBitCast(type_res, operand_coerced); } -fn zirErrSetCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; - const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; +fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { + const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; + const src: LazySrcLoc = .{ .node_offset = extra.node }; + const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; + const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); const operand = sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); @@ -16007,15 +15999,24 @@ fn zirAwait( sema: *Sema, block: *Block, inst: Zir.Inst.Index, - is_nosuspend: bool, ) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - _ = is_nosuspend; return sema.fail(block, src, "TODO: Sema.zirAwait", .{}); } +fn zirAwaitNosuspend( + sema: *Sema, + block: *Block, + extended: Zir.Inst.Extended.InstData, +) CompileError!Air.Inst.Ref { + const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; + const src: LazySrcLoc = .{ .node_offset = extra.node }; + + return sema.fail(block, src, "TODO: Sema.zirAwaitNosuspend", .{}); +} + fn zirVarExtended( sema: *Sema, block: *Block, diff --git a/src/Zir.zig b/src/Zir.zig index fc638ef869..29732d7907 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -278,8 +278,6 @@ pub const Inst = struct { /// break instruction in a block, and the target block is the parent. /// Uses the `break` union field. break_inline, - /// Uses the `node` union field. - breakpoint, /// Function call. /// Uses `pl_node`. AST node is the function call. Payload is `Call`. call, @@ -331,6 +329,10 @@ pub const Inst = struct { /// Same as `dbg_var_ptr` but the local is always a const and the operand /// is the local's value. dbg_var_val, + /// Marks the beginning of a semantic scope for debug info variables. + dbg_block_begin, + /// Marks the end of a semantic scope for debug info variables. + dbg_block_end, /// Uses a name to identify a Decl and takes a pointer to it. /// Uses the `str_tok` union field. decl_ref, @@ -499,6 +501,12 @@ pub const Inst = struct { /// this instruction; a following 'ret' instruction will do the diversion. /// Uses the `str_tok` union field. ret_err_value_code, + /// Obtains a pointer to the return value. + /// Uses the `node` union field. + ret_ptr, + /// Obtains the return type of the in-scope function. + /// Uses the `node` union field. + ret_type, /// Create a pointer type that does not have a sentinel, alignment, address space, or bit range specified. /// Uses the `ptr_type_simple` union field. ptr_type_simple, @@ -744,8 +752,6 @@ pub const Inst = struct { size_of, /// Implements the `@bitSizeOf` builtin. Uses `un_node`. bit_size_of, - /// Implements the `@fence` builtin. Uses `un_node`. - fence, /// Implement builtin `@ptrToInt`. Uses `un_node`. /// Convert a pointer to a `usize` integer. @@ -774,12 +780,8 @@ pub const Inst = struct { error_name, /// Implement builtin `@panic`. Uses `un_node`. panic, - /// Implement builtin `@setAlignStack`. Uses `un_node`. - set_align_stack, /// Implement builtin `@setCold`. Uses `un_node`. set_cold, - /// Implement builtin `@setFloatMode`. Uses `un_node`. - set_float_mode, /// Implement builtin `@setRuntimeSafety`. Uses `un_node`. set_runtime_safety, /// Implement builtin `@sqrt`. Uses `un_node`. @@ -843,9 +845,6 @@ pub const Inst = struct { /// Convert an integer value to another integer type, asserting that the destination type /// can hold the same mathematical value. int_cast, - /// Implements the `@errSetCast` builtin. - /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. - err_set_cast, /// Implements the `@ptrCast` builtin. /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. ptr_cast, @@ -972,7 +971,6 @@ pub const Inst = struct { /// Implements `resume` syntax. Uses `un_node` field. @"resume", @"await", - await_nosuspend, /// When a type or function refers to a comptime value from an outer /// scope, that forms a closure over comptime value. The outer scope @@ -1028,8 +1026,6 @@ pub const Inst = struct { .bool_br_and, .bool_br_or, .bool_not, - .breakpoint, - .fence, .call, .cmp_lt, .cmp_lte, @@ -1044,6 +1040,8 @@ pub const Inst = struct { .dbg_stmt, .dbg_var_ptr, .dbg_var_val, + .dbg_block_begin, + .dbg_block_end, .decl_ref, .decl_val, .load, @@ -1164,9 +1162,7 @@ pub const Inst = struct { .bool_to_int, .embed_file, .error_name, - .set_align_stack, .set_cold, - .set_float_mode, .set_runtime_safety, .sqrt, .sin, @@ -1192,7 +1188,6 @@ pub const Inst = struct { .int_to_ptr, .float_cast, .int_cast, - .err_set_cast, .ptr_cast, .truncate, .align_cast, @@ -1231,11 +1226,12 @@ pub const Inst = struct { .c_import, .@"resume", .@"await", - .await_nosuspend, .ret_err_value_code, .extended, .closure_get, .closure_capture, + .ret_ptr, + .ret_type, => false, .@"break", @@ -1258,13 +1254,13 @@ pub const Inst = struct { /// AstGen uses this to find out if `Ref.void_value` should be used in place /// of the result of a given instruction. This allows Sema to forego adding /// the instruction to the map after analysis. - pub fn isAlwaysVoid(tag: Tag) bool { + pub fn isAlwaysVoid(tag: Tag, data: Data) bool { return switch (tag) { - .breakpoint, - .fence, .dbg_stmt, .dbg_var_ptr, .dbg_var_val, + .dbg_block_begin, + .dbg_block_end, .ensure_result_used, .ensure_result_non_error, .ensure_err_payload_void, @@ -1283,9 +1279,7 @@ pub const Inst = struct { .validate_array_init_comptime, .@"export", .export_value, - .set_align_stack, .set_cold, - .set_float_mode, .set_runtime_safety, .memcpy, .memset, @@ -1464,7 +1458,6 @@ pub const Inst = struct { .int_to_ptr, .float_cast, .int_cast, - .err_set_cast, .ptr_cast, .truncate, .align_cast, @@ -1500,9 +1493,7 @@ pub const Inst = struct { .c_import, .@"resume", .@"await", - .await_nosuspend, .ret_err_value_code, - .extended, .closure_get, .closure_capture, .@"break", @@ -1514,11 +1505,18 @@ pub const Inst = struct { .ret_load, .ret_tok, .ret_err_value, + .ret_ptr, + .ret_type, .@"unreachable", .repeat, .repeat_inline, .panic, => false, + + .extended => switch (data.extended.opcode) { + .breakpoint, .fence => true, + else => false, + }, }; } @@ -1563,7 +1561,6 @@ pub const Inst = struct { .bool_br_or = .bool_br, .@"break" = .@"break", .break_inline = .@"break", - .breakpoint = .node, .call = .pl_node, .cmp_lt = .pl_node, .cmp_lte = .pl_node, @@ -1580,6 +1577,8 @@ pub const Inst = struct { .dbg_stmt = .dbg_stmt, .dbg_var_ptr = .str_op, .dbg_var_val = .str_op, + .dbg_block_begin = .tok, + .dbg_block_end = .tok, .decl_ref = .str_tok, .decl_val = .str_tok, .load = .un_node, @@ -1623,6 +1622,8 @@ pub const Inst = struct { .ret_tok = .un_tok, .ret_err_value = .str_tok, .ret_err_value_code = .str_tok, + .ret_ptr = .node, + .ret_type = .node, .ptr_type_simple = .ptr_type_simple, .ptr_type = .ptr_type, .slice_start = .pl_node, @@ -1685,7 +1686,6 @@ pub const Inst = struct { .type_info = .un_node, .size_of = .un_node, .bit_size_of = .un_node, - .fence = .un_node, .ptr_to_int = .un_node, .error_to_int = .un_node, @@ -1698,9 +1698,7 @@ pub const Inst = struct { .embed_file = .un_node, .error_name = .un_node, .panic = .un_node, - .set_align_stack = .un_node, .set_cold = .un_node, - .set_float_mode = .un_node, .set_runtime_safety = .un_node, .sqrt = .un_node, .sin = .un_node, @@ -1728,7 +1726,6 @@ pub const Inst = struct { .int_to_enum = .pl_node, .float_cast = .pl_node, .int_cast = .pl_node, - .err_set_cast = .pl_node, .ptr_cast = .pl_node, .truncate = .pl_node, .align_cast = .pl_node, @@ -1788,7 +1785,6 @@ pub const Inst = struct { .@"resume" = .un_node, .@"await" = .un_node, - .await_nosuspend = .un_node, .closure_capture = .un_tok, .closure_get = .inst_node, @@ -1834,12 +1830,6 @@ pub const Inst = struct { /// `operand` is payload index to `OpaqueDecl`. /// `small` is `OpaqueDecl.Small`. opaque_decl, - /// Obtains a pointer to the return value. - /// `operand` is `src_node: i32`. - ret_ptr, - /// Obtains the return type of the in-scope function. - /// `operand` is `src_node: i32`. - ret_type, /// Implements the `@This` builtin. /// `operand` is `src_node: i32`. this, @@ -1917,10 +1907,6 @@ pub const Inst = struct { /// The `@prefetch` builtin. /// `operand` is payload index to `BinNode`. prefetch, - /// Marks the beginning of a semantic scope for debug info variables. - dbg_block_begin, - /// Marks the end of a semantic scope for debug info variables. - dbg_block_end, /// Given a pointer to a struct or object that contains virtual fields, returns the /// named field. If there is no named field, searches in the type for a decl that /// matches the field name. The decl is resolved and we ensure that it's a function @@ -1931,6 +1917,22 @@ pub const Inst = struct { /// `builtin_call` instruction. Any other use is invalid zir and may crash the compiler. /// Uses `pl_node` field. The AST node is the `@field` builtin. Payload is FieldNamedNode. field_call_bind_named, + /// Implements the `@fence` builtin. + /// `operand` is payload index to `UnNode`. + fence, + /// Implement builtin `@setFloatMode`. + /// `operand` is payload index to `UnNode`. + set_float_mode, + /// Implement builtin `@setAlignStack`. + /// `operand` is payload index to `UnNode`. + set_align_stack, + /// Implements the `@errSetCast` builtin. + /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand. + err_set_cast, + /// `operand` is payload index to `UnNode`. + await_nosuspend, + /// `operand` is `src_node: i32`. + breakpoint, pub const InstData = struct { opcode: Extended, diff --git a/src/print_zir.zig b/src/print_zir.zig index ed1eeeeba3..a65361652d 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -200,9 +200,7 @@ const Writer = struct { .embed_file, .error_name, .panic, - .set_align_stack, .set_cold, - .set_float_mode, .set_runtime_safety, .sqrt, .sin, @@ -231,8 +229,6 @@ const Writer = struct { .elem_type, .@"resume", .@"await", - .await_nosuspend, - .fence, .switch_cond, .switch_cond_ref, .array_base_ptr, @@ -343,7 +339,6 @@ const Writer = struct { .int_to_enum, .float_cast, .int_cast, - .err_set_cast, .ptr_cast, .truncate, .align_cast, @@ -405,13 +400,14 @@ const Writer = struct { .as_node => try self.writeAs(stream, inst), - .breakpoint, .repeat, .repeat_inline, .alloc_inferred, .alloc_inferred_mut, .alloc_inferred_comptime, .alloc_inferred_comptime_mut, + .ret_ptr, + .ret_type, => try self.writeNode(stream, inst), .error_value, @@ -444,6 +440,10 @@ const Writer = struct { .dbg_stmt => try self.writeDbgStmt(stream, inst), + .dbg_block_begin, + .dbg_block_end, + => try stream.writeAll("))"), + .closure_get => try self.writeInstNode(stream, inst), .extended => try self.writeExtended(stream, inst), @@ -454,13 +454,12 @@ const Writer = struct { const extended = self.code.instructions.items(.data)[inst].extended; try stream.print("{s}(", .{@tagName(extended.opcode)}); switch (extended.opcode) { - .ret_ptr, - .ret_type, .this, .ret_addr, .error_return_trace, .frame, .frame_address, + .breakpoint, => try self.writeExtNode(stream, extended), .builtin_src => { @@ -469,10 +468,6 @@ const Writer = struct { try stream.print(":{d}:{d}", .{ inst_data.line + 1, inst_data.column + 1 }); }, - .dbg_block_begin, - .dbg_block_end, - => try stream.writeAll("))"), - .@"asm" => try self.writeAsm(stream, extended), .func => try self.writeFuncExtended(stream, extended), .variable => try self.writeVarExtended(stream, extended), @@ -492,7 +487,14 @@ const Writer = struct { .enum_decl => try self.writeEnumDecl(stream, extended), .opaque_decl => try self.writeOpaqueDecl(stream, extended), - .c_undef, .c_include, .wasm_memory_size => { + .await_nosuspend, + .c_undef, + .c_include, + .fence, + .set_float_mode, + .set_align_stack, + .wasm_memory_size, + => { const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src: LazySrcLoc = .{ .node_offset = inst_data.node }; try self.writeInstRef(stream, inst_data.operand); @@ -500,7 +502,12 @@ const Writer = struct { try self.writeSrc(stream, src); }, - .builtin_extern, .c_define, .wasm_memory_grow, .prefetch => { + .builtin_extern, + .c_define, + .err_set_cast, + .wasm_memory_grow, + .prefetch, + => { const inst_data = self.code.extraData(Zir.Inst.BinNode, extended.operand).data; const src: LazySrcLoc = .{ .node_offset = inst_data.node }; try self.writeInstRef(stream, inst_data.lhs); From 596f7df02e78adf334eed4a1f14eafa31ca611b9 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 29 Apr 2022 12:23:46 +0300 Subject: [PATCH 1306/2031] Zir: turn extended func into func_extended --- src/AstGen.zig | 29 +++++++++++----------- src/Module.zig | 8 +++--- src/Sema.zig | 32 +++++++++++------------- src/Zir.zig | 62 +++++++++++++++++++++++------------------------ src/print_zir.zig | 24 +++++++++--------- 5 files changed, 75 insertions(+), 80 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index b0cd976b00..f74a2b2d39 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -72,6 +72,7 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void { i32 => @bitCast(u32, @field(extra, field.name)), Zir.Inst.Call.Flags => @bitCast(u32, @field(extra, field.name)), Zir.Inst.SwitchBlock.Bits => @bitCast(u32, @field(extra, field.name)), + Zir.Inst.ExtendedFunc.Bits => @bitCast(u32, @field(extra, field.name)), else => @compileError("bad field type"), }; i += 1; @@ -2245,6 +2246,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .field_val_named, .func, .func_inferred, + .func_extended, .int, .int_big, .float, @@ -10023,10 +10025,18 @@ const GenZir = struct { @boolToInt(args.cc != .none), ); const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedFunc{ - .src_node = gz.nodeIndexToRelative(args.src_node), .param_block = args.param_block, .ret_body_len = @intCast(u32, ret_ty.len), .body_len = @intCast(u32, body.len), + .bits = .{ + .is_var_args = args.is_var_args, + .is_inferred_error = args.is_inferred_error, + .has_lib_name = args.lib_name != 0, + .has_cc = args.cc != .none, + .has_align = args.align_inst != .none, + .is_test = args.is_test, + .is_extern = args.is_extern, + }, }); if (args.lib_name != 0) { astgen.extra.appendAssumeCapacity(args.lib_name); @@ -10050,19 +10060,10 @@ const GenZir = struct { astgen.instructions.items(.data)[args.ret_br].@"break".block_inst = new_index; } astgen.instructions.appendAssumeCapacity(.{ - .tag = .extended, - .data = .{ .extended = .{ - .opcode = .func, - .small = @bitCast(u16, Zir.Inst.ExtendedFunc.Small{ - .is_var_args = args.is_var_args, - .is_inferred_error = args.is_inferred_error, - .has_lib_name = args.lib_name != 0, - .has_cc = args.cc != .none, - .has_align = args.align_inst != .none, - .is_test = args.is_test, - .is_extern = args.is_extern, - }), - .operand = payload_index, + .tag = .func_extended, + .data = .{ .pl_node = .{ + .src_node = gz.nodeIndexToRelative(args.src_node), + .payload_index = payload_index, } }, }); gz.instructions.appendAssumeCapacity(new_index); diff --git a/src/Module.zig b/src/Module.zig index 1119d73ab0..55ec1fdd2c 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1532,10 +1532,10 @@ pub const Fn = struct { switch (zir_tags[func.zir_body_inst]) { .func => return false, .func_inferred => return true, - .extended => { - const extended = zir.instructions.items(.data)[func.zir_body_inst].extended; - const small = @bitCast(Zir.Inst.ExtendedFunc.Small, extended.small); - return small.is_inferred_error; + .func_extended => { + const inst_data = zir.instructions.items(.data)[func.zir_body_inst].pl_node; + const extra = zir.extraData(Zir.Inst.ExtendedFunc, inst_data.payload_index); + return extra.data.bits.is_inferred_error; }, else => unreachable, } diff --git a/src/Sema.zig b/src/Sema.zig index 40b9698d49..ccfb11e639 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -745,6 +745,7 @@ fn analyzeBodyInner( .field_call_bind => try sema.zirFieldCallBind(block, inst), .func => try sema.zirFunc(block, inst, false), .func_inferred => try sema.zirFunc(block, inst, true), + .func_extended => try sema.zirFuncExtended(block, inst), .import => try sema.zirImport(block, inst), .indexable_ptr_len => try sema.zirIndexablePtrLen(block, inst), .int => try sema.zirInt(block, inst), @@ -911,7 +912,6 @@ fn analyzeBodyInner( const extended = datas[inst].extended; break :ext switch (extended.opcode) { // zig fmt: off - .func => try sema.zirFuncExtended( block, extended, inst), .variable => try sema.zirVarExtended( block, extended), .struct_decl => try sema.zirStructDecl( block, extended, inst), .enum_decl => try sema.zirEnumDecl( block, extended), @@ -16099,37 +16099,33 @@ fn zirVarExtended( return result; } -fn zirFuncExtended( - sema: *Sema, - block: *Block, - extended: Zir.Inst.Extended.InstData, - inst: Zir.Inst.Index, -) CompileError!Air.Inst.Ref { +fn zirFuncExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); - const extra = sema.code.extraData(Zir.Inst.ExtendedFunc, extended.operand); - const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; - const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = extra.data.src_node }; + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const extra = sema.code.extraData(Zir.Inst.ExtendedFunc, inst_data.payload_index); + + const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node }; const align_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at align - const small = @bitCast(Zir.Inst.ExtendedFunc.Small, extended.small); var extra_index: usize = extra.end; - const lib_name: ?[]const u8 = if (small.has_lib_name) blk: { + const lib_name: ?[]const u8 = if (extra.data.bits.has_lib_name) blk: { const lib_name = sema.code.nullTerminatedString(sema.code.extra[extra_index]); extra_index += 1; break :blk lib_name; } else null; - const cc: std.builtin.CallingConvention = if (small.has_cc) blk: { + const cc: std.builtin.CallingConvention = if (extra.data.bits.has_cc) blk: { const cc_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; const cc_tv = try sema.resolveInstConst(block, cc_src, cc_ref); break :blk cc_tv.val.toEnum(std.builtin.CallingConvention); } else .Unspecified; - const align_val: Value = if (small.has_align) blk: { + const align_val: Value = if (extra.data.bits.has_align) blk: { const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; const align_tv = try sema.resolveInstConst(block, align_src, align_ref); @@ -16146,13 +16142,13 @@ fn zirFuncExtended( src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; } - const is_var_args = small.is_var_args; - const is_inferred_error = small.is_inferred_error; - const is_extern = small.is_extern; + const is_var_args = extra.data.bits.is_var_args; + const is_inferred_error = extra.data.bits.is_inferred_error; + const is_extern = extra.data.bits.is_extern; return sema.funcCommon( block, - extra.data.src_node, + inst_data.src_node, inst, ret_ty_body, cc, diff --git a/src/Zir.zig b/src/Zir.zig index 29732d7907..9ef6d33b86 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -73,6 +73,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, en i32 => @bitCast(i32, code.extra[i]), Inst.Call.Flags => @bitCast(Inst.Call.Flags, code.extra[i]), Inst.SwitchBlock.Bits => @bitCast(Inst.SwitchBlock.Bits, code.extra[i]), + Inst.ExtendedFunc.Bits => @bitCast(Inst.ExtendedFunc.Bits, code.extra[i]), else => @compileError("bad field type"), }; i += 1; @@ -415,6 +416,10 @@ pub const Inst = struct { func, /// Same as `func` but has an inferred error set. func_inferred, + /// Represents a function declaration or function prototype, depending on + /// whether body_len is 0. + /// Uses the `pl_node` union field. `payload_index` points to a `ExtendedFunc`. + func_extended, /// Implements the `@import` builtin. /// Uses the `str_tok` field. import, @@ -1062,6 +1067,7 @@ pub const Inst = struct { .field_val_named, .func, .func_inferred, + .func_extended, .has_decl, .int, .int_big, @@ -1347,6 +1353,7 @@ pub const Inst = struct { .field_val_named, .func, .func_inferred, + .func_extended, .has_decl, .int, .int_big, @@ -1601,6 +1608,7 @@ pub const Inst = struct { .field_call_bind = .pl_node, .func = .pl_node, .func_inferred = .pl_node, + .func_extended = .pl_node, .import = .str_tok, .int = .int, .int_big = .str, @@ -1802,11 +1810,6 @@ pub const Inst = struct { /// Rarer instructions are here; ones that do not fit in the 8-bit `Tag` enum. /// `noreturn` instructions may not go here; they must be part of the main `Tag` enum. pub const Extended = enum(u16) { - /// Represents a function declaration or function prototype, depending on - /// whether body_len is 0. - /// `operand` is payload index to `ExtendedFunc`. - /// `small` is `ExtendedFunc.Small`. - func, /// Declares a global variable. /// `operand` is payload index to `ExtendedVar`. /// `small` is `ExtendedVar.Small`. @@ -2621,14 +2624,14 @@ pub const Inst = struct { /// 4. body: Index // for each body_len /// 5. src_locs: Func.SrcLocs // if body_len != 0 pub const ExtendedFunc = struct { - src_node: i32, /// If this is 0 it means a void return type. ret_body_len: u32, /// Points to the block that contains the param instructions for this function. param_block: Index, body_len: u32, + bits: Bits, - pub const Small = packed struct { + pub const Bits = packed struct { is_var_args: bool, is_inferred_error: bool, has_lib_name: bool, @@ -2636,7 +2639,7 @@ pub const Inst = struct { has_align: bool, is_test: bool, is_extern: bool, - _: u9 = undefined, + _: u25 = undefined, }; }; @@ -3460,14 +3463,11 @@ pub fn declIterator(zir: Zir, decl_inst: u32) DeclIterator { switch (tags[decl_inst]) { // Functions are allowed and yield no iterations. // There is one case matching this in the extended instruction set below. - .func, - .func_inferred, - => return declIteratorInner(zir, 0, 0), + .func, .func_inferred, .func_extended => return declIteratorInner(zir, 0, 0), .extended => { const extended = datas[decl_inst].extended; switch (extended.opcode) { - .func => return declIteratorInner(zir, 0, 0), .struct_decl => { const small = @bitCast(Inst.StructDecl.Small, extended.small); var extra_index: usize = extended.operand; @@ -3572,21 +3572,21 @@ fn findDeclsInner( const body = zir.extra[extra.end..][0..extra.data.body_len]; return zir.findDeclsBody(list, body); }, + .func_extended => { + try list.append(inst); + + const inst_data = datas[inst].pl_node; + const extra = zir.extraData(Inst.ExtendedFunc, inst_data.payload_index); + var extra_index: usize = extra.end; + extra_index += @boolToInt(extra.data.bits.has_lib_name); + extra_index += @boolToInt(extra.data.bits.has_cc); + extra_index += @boolToInt(extra.data.bits.has_align); + const body = zir.extra[extra_index..][0..extra.data.body_len]; + return zir.findDeclsBody(list, body); + }, .extended => { const extended = datas[inst].extended; switch (extended.opcode) { - .func => { - try list.append(inst); - - const extra = zir.extraData(Inst.ExtendedFunc, extended.operand); - const small = @bitCast(Inst.ExtendedFunc.Small, extended.small); - var extra_index: usize = extra.end; - extra_index += @boolToInt(small.has_lib_name); - extra_index += @boolToInt(small.has_cc); - extra_index += @boolToInt(small.has_align); - const body = zir.extra[extra_index..][0..extra.data.body_len]; - return zir.findDeclsBody(list, body); - }, // Decl instructions are interesting but have no body. // TODO yes they do have a body actually. recurse over them just like block instructions. @@ -3733,15 +3733,13 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { .body = body, }; }, - .extended => blk: { - const extended = datas[fn_inst].extended; - assert(extended.opcode == .func); - const extra = zir.extraData(Inst.ExtendedFunc, extended.operand); - const small = @bitCast(Inst.ExtendedFunc.Small, extended.small); + .func_extended => blk: { + const inst_data = datas[fn_inst].pl_node; + const extra = zir.extraData(Inst.ExtendedFunc, inst_data.payload_index); var extra_index: usize = extra.end; - extra_index += @boolToInt(small.has_lib_name); - extra_index += @boolToInt(small.has_cc); - extra_index += @boolToInt(small.has_align); + extra_index += @boolToInt(extra.data.bits.has_lib_name); + extra_index += @boolToInt(extra.data.bits.has_cc); + extra_index += @boolToInt(extra.data.bits.has_align); const ret_ty_body = zir.extra[extra_index..][0..extra.data.ret_body_len]; extra_index += ret_ty_body.len; const body = zir.extra[extra_index..][0..extra.data.body_len]; diff --git a/src/print_zir.zig b/src/print_zir.zig index a65361652d..ac90c26cf6 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -429,6 +429,7 @@ const Writer = struct { .func => try self.writeFunc(stream, inst, false), .func_inferred => try self.writeFunc(stream, inst, true), + .func_extended => try self.writeFuncExtended(stream, inst), .@"unreachable" => try self.writeUnreachable(stream, inst), @@ -469,7 +470,6 @@ const Writer = struct { }, .@"asm" => try self.writeAsm(stream, extended), - .func => try self.writeFuncExtended(stream, extended), .variable => try self.writeVarExtended(stream, extended), .alloc => try self.writeAllocExtended(stream, extended), @@ -1920,24 +1920,24 @@ const Writer = struct { ); } - fn writeFuncExtended(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { - const extra = self.code.extraData(Zir.Inst.ExtendedFunc, extended.operand); - const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; - const small = @bitCast(Zir.Inst.ExtendedFunc.Small, extended.small); + fn writeFuncExtended(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Zir.Inst.ExtendedFunc, inst_data.payload_index); + const src = inst_data.src(); var extra_index: usize = extra.end; - if (small.has_lib_name) { + if (extra.data.bits.has_lib_name) { const lib_name = self.code.nullTerminatedString(self.code.extra[extra_index]); extra_index += 1; try stream.print("lib_name=\"{}\", ", .{std.zig.fmtEscapes(lib_name)}); } - try self.writeFlag(stream, "test, ", small.is_test); - const cc: Zir.Inst.Ref = if (!small.has_cc) .none else blk: { + try self.writeFlag(stream, "test, ", extra.data.bits.is_test); + const cc: Zir.Inst.Ref = if (!extra.data.bits.has_cc) .none else blk: { const cc = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; break :blk cc; }; - const align_inst: Zir.Inst.Ref = if (!small.has_align) .none else blk: { + const align_inst: Zir.Inst.Ref = if (!extra.data.bits.has_align) .none else blk: { const align_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); extra_index += 1; break :blk align_inst; @@ -1956,9 +1956,9 @@ const Writer = struct { return self.writeFuncCommon( stream, ret_ty_body, - small.is_inferred_error, - small.is_var_args, - small.is_extern, + extra.data.bits.is_inferred_error, + extra.data.bits.is_var_args, + extra.data.bits.is_extern, cc, align_inst, body, From a0a2ce92ca129d28e22c63f7bace1672c43776b5 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Fri, 29 Apr 2022 17:07:51 +0200 Subject: [PATCH 1307/2031] std: Do not allocate the result for ChildProcess.init Instead, just return ChildProcess directly. This structure does not require a stable address, so we can put it on the stack just fine. If someone wants it on the heap they should do. const proc = try allocator.create(ChildProcess); proc.* = ChildProcess.init(args, allocator); --- lib/std/build.zig | 8 ++------ lib/std/build/RunStep.zig | 4 +--- lib/std/child_process.zig | 26 +++++++------------------- lib/std/testing.zig | 5 +++-- src/Compilation.zig | 4 +--- src/link/Coff.zig | 4 +--- src/link/Elf.zig | 4 +--- src/link/Wasm.zig | 4 +--- src/main.zig | 8 ++------ src/mingw.zig | 4 +--- test/tests.zig | 4 +--- 11 files changed, 21 insertions(+), 54 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 453f20c333..38744cea1e 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -971,9 +971,7 @@ pub const Builder = struct { if (!std.process.can_spawn) return error.ExecNotSupported; - const child = std.ChildProcess.init(argv, self.allocator) catch unreachable; - defer child.deinit(); - + var child = std.ChildProcess.init(argv, self.allocator); child.cwd = cwd; child.env_map = env_map; @@ -1187,9 +1185,7 @@ pub const Builder = struct { return error.ExecNotSupported; const max_output_size = 400 * 1024; - const child = try std.ChildProcess.init(argv, self.allocator); - defer child.deinit(); - + var child = std.ChildProcess.init(argv, self.allocator); child.stdin_behavior = .Ignore; child.stdout_behavior = .Pipe; child.stderr_behavior = stderr_behavior; diff --git a/lib/std/build/RunStep.zig b/lib/std/build/RunStep.zig index e8544921d9..e00fe3deb6 100644 --- a/lib/std/build/RunStep.zig +++ b/lib/std/build/RunStep.zig @@ -184,9 +184,7 @@ fn make(step: *Step) !void { return ExecError.ExecNotSupported; } - const child = std.ChildProcess.init(argv, self.builder.allocator) catch unreachable; - defer child.deinit(); - + var child = std.ChildProcess.init(argv, self.builder.allocator); child.cwd = cwd; child.env_map = self.env_map orelse self.builder.env_map; diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index f2b978ba9f..5f01ed01dd 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -98,10 +98,8 @@ pub const ChildProcess = struct { }; /// First argument in argv is the executable. - /// On success must call deinit. - pub fn init(argv: []const []const u8, allocator: mem.Allocator) !*ChildProcess { - const child = try allocator.create(ChildProcess); - child.* = ChildProcess{ + pub fn init(argv: []const []const u8, allocator: mem.Allocator) ChildProcess { + return .{ .allocator = allocator, .argv = argv, .pid = undefined, @@ -121,8 +119,6 @@ pub const ChildProcess = struct { .stderr_behavior = StdIo.Inherit, .expand_arg0 = .no_expand, }; - errdefer allocator.destroy(child); - return child; } pub fn setUserName(self: *ChildProcess, name: []const u8) !void { @@ -199,7 +195,7 @@ pub const ChildProcess = struct { }; fn collectOutputPosix( - child: *const ChildProcess, + child: ChildProcess, stdout: *std.ArrayList(u8), stderr: *std.ArrayList(u8), max_output_bytes: usize, @@ -298,7 +294,7 @@ pub const ChildProcess = struct { } } - fn collectOutputWindows(child: *const ChildProcess, outs: [2]*std.ArrayList(u8), max_output_bytes: usize) !void { + fn collectOutputWindows(child: ChildProcess, outs: [2]*std.ArrayList(u8), max_output_bytes: usize) !void { const bump_amt = 512; const handles = [_]windows.HANDLE{ child.stdout.?.handle, @@ -383,9 +379,7 @@ pub const ChildProcess = struct { max_output_bytes: usize = 50 * 1024, expand_arg0: Arg0Expand = .no_expand, }) !ExecResult { - const child = try ChildProcess.init(args.argv, args.allocator); - defer child.deinit(); - + var child = ChildProcess.init(args.argv, args.allocator); child.stdin_behavior = .Ignore; child.stdout_behavior = .Pipe; child.stderr_behavior = .Pipe; @@ -452,10 +446,6 @@ pub const ChildProcess = struct { return self.term.?; } - pub fn deinit(self: *ChildProcess) void { - self.allocator.destroy(self); - } - fn waitUnwrappedWindows(self: *ChildProcess) !void { const result = windows.WaitForSingleObjectEx(self.handle, windows.INFINITE, false); @@ -1374,8 +1364,7 @@ test "build and call child_process" { // spawn compiled file as child_process with argument 'hello world' + expect success const args = [_][]const u8{ child_path, "hello world" }; - var child_proc = try ChildProcess.init(&args, allocator); - defer child_proc.deinit(); + var child_proc = ChildProcess.init(&args, allocator); const ret_val = try child_proc.spawnAndWait(); try testing.expectEqual(ret_val, .{ .Exited = 0 }); } @@ -1385,11 +1374,10 @@ test "creating a child process with stdin and stdout behavior set to StdIo.Pipe" const testing = std.testing; const allocator = testing.allocator; - var child_process = try std.ChildProcess.init( + var child_process = std.ChildProcess.init( &[_][]const u8{ testing.zig_exe_path, "fmt", "--stdin" }, allocator, ); - defer child_process.deinit(); child_process.stdin_behavior = .Pipe; child_process.stdout_behavior = .Pipe; diff --git a/lib/std/testing.zig b/lib/std/testing.zig index cfdf300c04..174e898bca 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -442,10 +442,11 @@ pub fn buildExe(zigexec: []const u8, zigfile: []const u8, binfile: []const u8) ! const flag_emit = "-femit-bin="; const cmd_emit = try std.mem.concat(allocator, u8, &[_][]const u8{ flag_emit, binfile }); defer allocator.free(cmd_emit); + const args = [_][]const u8{ zigexec, "build-exe", zigfile, cmd_emit }; - var procCompileChild = try std.ChildProcess.init(&args, allocator); - defer procCompileChild.deinit(); + var procCompileChild = std.ChildProcess.init(&args, allocator); try procCompileChild.spawn(); + const ret_val = try procCompileChild.wait(); try expectEqual(ret_val, .{ .Exited = 0 }); } diff --git a/src/Compilation.zig b/src/Compilation.zig index b0c8f5f475..2ad8a9e030 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3611,9 +3611,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P } if (std.process.can_spawn) { - const child = try std.ChildProcess.init(argv.items, arena); - defer child.deinit(); - + var child = std.ChildProcess.init(argv.items, arena); if (comp.clang_passthrough_mode) { child.stdin_behavior = .Inherit; child.stdout_behavior = .Inherit; diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 246918515d..178a6ab6b4 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -1390,9 +1390,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) ! // If possible, we run LLD as a child process because it does not always // behave properly as a library, unfortunately. // https://github.com/ziglang/zig/issues/3825 - const child = try std.ChildProcess.init(argv.items, arena); - defer child.deinit(); - + var child = std.ChildProcess.init(argv.items, arena); if (comp.clang_passthrough_mode) { child.stdin_behavior = .Inherit; child.stdout_behavior = .Inherit; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 144ac24b9b..11a701fcf9 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1754,9 +1754,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // If possible, we run LLD as a child process because it does not always // behave properly as a library, unfortunately. // https://github.com/ziglang/zig/issues/3825 - const child = try std.ChildProcess.init(argv.items, arena); - defer child.deinit(); - + var child = std.ChildProcess.init(argv.items, arena); if (comp.clang_passthrough_mode) { child.stdin_behavior = .Inherit; child.stdout_behavior = .Inherit; diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 3c53e91587..4e898cee07 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2405,9 +2405,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! // If possible, we run LLD as a child process because it does not always // behave properly as a library, unfortunately. // https://github.com/ziglang/zig/issues/3825 - const child = try std.ChildProcess.init(argv.items, arena); - defer child.deinit(); - + var child = std.ChildProcess.init(argv.items, arena); if (comp.clang_passthrough_mode) { child.stdin_behavior = .Inherit; child.stdout_behavior = .Inherit; diff --git a/src/main.zig b/src/main.zig index e47ff0e272..1538e86225 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3012,9 +3012,7 @@ fn runOrTest( const cmd = try std.mem.join(arena, " ", argv.items); fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd }); } else if (std.process.can_spawn) { - const child = try std.ChildProcess.init(argv.items, gpa); - defer child.deinit(); - + var child = std.ChildProcess.init(argv.items, gpa); child.stdin_behavior = .Inherit; child.stdout_behavior = .Inherit; child.stderr_behavior = .Inherit; @@ -3700,9 +3698,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi }; if (std.process.can_spawn) { - const child = try std.ChildProcess.init(child_argv, gpa); - defer child.deinit(); - + var child = std.ChildProcess.init(child_argv, gpa); child.stdin_behavior = .Inherit; child.stdout_behavior = .Inherit; child.stderr_behavior = .Inherit; diff --git a/src/mingw.zig b/src/mingw.zig index 84ec0795f1..e99a1af8fc 100644 --- a/src/mingw.zig +++ b/src/mingw.zig @@ -369,9 +369,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { } if (std.process.can_spawn) { - const child = try std.ChildProcess.init(&args, arena); - defer child.deinit(); - + var child = std.ChildProcess.init(&args, arena); child.stdin_behavior = .Ignore; child.stdout_behavior = .Pipe; child.stderr_behavior = .Pipe; diff --git a/test/tests.zig b/test/tests.zig index 5b15da2bcb..ee4b922021 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -731,9 +731,7 @@ pub const StackTracesContext = struct { return ExecError.ExecNotSupported; } - const child = std.ChildProcess.init(args.items, b.allocator) catch unreachable; - defer child.deinit(); - + var child = std.ChildProcess.init(args.items, b.allocator); child.stdin_behavior = .Ignore; child.stdout_behavior = .Pipe; child.stderr_behavior = .Pipe; From 609896a6e8180ec32844d2b4656b52ef41fe7757 Mon Sep 17 00:00:00 2001 From: InKryption <59504965+InKryption@users.noreply.github.com> Date: Sat, 30 Apr 2022 04:51:53 +0200 Subject: [PATCH 1308/2031] std.MultiArrayList: add functions `addOne`, `pop`, and `popOrNull` (#11553) Update basic usage test to account for these, and by extension for addOneAssumeCapacity. --- lib/std/multi_array_list.zig | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/lib/std/multi_array_list.zig b/lib/std/multi_array_list.zig index ae81b6c5e5..48dddf636d 100644 --- a/lib/std/multi_array_list.zig +++ b/lib/std/multi_array_list.zig @@ -178,6 +178,14 @@ pub fn MultiArrayList(comptime S: type) type { self.set(self.len - 1, elem); } + /// Extend the list by 1 element, returning the newly reserved + /// index with uninitialized data. + /// Allocates more memory as necesasry. + pub fn addOne(self: *Self, allocator: Allocator) Allocator.Error!usize { + try self.ensureUnusedCapacity(allocator, 1); + return self.addOneAssumeCapacity(); + } + /// Extend the list by 1 element, asserting `self.capacity` /// is sufficient to hold an additional item. Returns the /// newly reserved index with uninitialized data. @@ -188,6 +196,23 @@ pub fn MultiArrayList(comptime S: type) type { return index; } + /// Remove and return the last element from the list. + /// Asserts the list has at least one item. + /// Invalidates pointers to fields of the removed element. + pub fn pop(self: *Self) S { + const val = self.get(self.len - 1); + self.len -= 1; + return val; + } + + /// Remove and return the last element from the list, or + /// return `null` if list is empty. + /// Invalidates pointers to fields of the removed element, if any. + pub fn popOrNull(self: *Self) ?S { + if (self.len == 0) return null; + return self.pop(); + } + /// Inserts an item into an ordered list. Shifts all elements /// after and including the specified index back by one and /// sets the given index to the specified element. May reallocate @@ -532,6 +557,17 @@ test "basic usage" { try testing.expectEqualStrings("foobar", list.items(.b)[0]); try testing.expectEqualStrings("zigzag", list.items(.b)[1]); try testing.expectEqualStrings("fizzbuzz", list.items(.b)[2]); + + list.set(try list.addOne(ally), .{ + .a = 4, + .b = "xnopyt", + .c = 'd', + }); + try testing.expectEqualStrings("xnopyt", list.pop().b); + try testing.expectEqual(@as(?u8, 'c'), if (list.popOrNull()) |elem| elem.c else null); + try testing.expectEqual(@as(u32, 2), list.pop().a); + try testing.expectEqual(@as(u8, 'a'), list.pop().c); + try testing.expectEqual(@as(?Foo, null), list.popOrNull()); } // This was observed to fail on aarch64 with LLVM 11, when the capacityInBytes From 032c722d2019a475362c0ae01241a80417bdd8a2 Mon Sep 17 00:00:00 2001 From: sin-ack <77421532+sin-ack@users.noreply.github.com> Date: Sat, 30 Apr 2022 05:53:06 +0300 Subject: [PATCH 1309/2031] Sema: Fix many-pointer array concatenation at comptime (#11512) * Sema: Correctly determine whether array_cat lhs and rhs are single ptrs Many-pointers are also not single-pointers and wouldn't be considered here. This commit makes the conditions use the appropriately-named isSinglePointer instead. * Sema: Correctly obtain ArrayInfo for many-pointer concatenation Many-pointers at comptime have a known size like slices and can be used in array concatenation. This fixes a stage1 regression. * test: Add comptime manyptr concatenation test Co-authored-by: sin-ack --- src/Sema.zig | 28 ++++++++++++++++------------ test/behavior/basic.zig | 25 +++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index ccfb11e639..b17ca031c2 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9106,8 +9106,8 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const rhs_len = try sema.usizeCast(block, lhs_src, rhs_info.len); const final_len = lhs_len + rhs_len; const final_len_including_sent = final_len + @boolToInt(res_sent != null); - const lhs_single_ptr = lhs_ty.zigTypeTag() == .Pointer and !lhs_ty.isSlice(); - const rhs_single_ptr = rhs_ty.zigTypeTag() == .Pointer and !rhs_ty.isSlice(); + const lhs_single_ptr = lhs_ty.isSinglePointer(); + const rhs_single_ptr = rhs_ty.isSinglePointer(); const lhs_sub_val = if (lhs_single_ptr) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val; const rhs_sub_val = if (rhs_single_ptr) (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).? else rhs_val; var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded); @@ -9160,17 +9160,21 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, inst: Air.Inst.R .Array => t.arrayInfo(), .Pointer => blk: { const ptrinfo = t.ptrInfo().data; - if (ptrinfo.size == .Slice) { - const val = try sema.resolveConstValue(block, src, inst); - return Type.ArrayInfo{ - .elem_type = t.childType(), - .sentinel = t.sentinel(), - .len = val.sliceLen(sema.mod), - }; + switch (ptrinfo.size) { + .Slice, .Many => { + const val = try sema.resolveConstValue(block, src, inst); + return Type.ArrayInfo{ + .elem_type = t.childType(), + .sentinel = t.sentinel(), + .len = val.sliceLen(sema.mod), + }; + }, + .One => { + if (ptrinfo.pointee_type.zigTypeTag() != .Array) return null; + break :blk ptrinfo.pointee_type.arrayInfo(); + }, + .C => return null, } - if (ptrinfo.pointee_type.zigTypeTag() != .Array) return null; - if (ptrinfo.size != .One) return null; - break :blk ptrinfo.pointee_type.arrayInfo(); }, else => null, }; diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index d608cba98b..12ead179f4 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -709,6 +709,31 @@ test "string concatenation" { try expect(b[len] == 0); } +fn manyptrConcat(comptime s: [*:0]const u8) [*:0]const u8 { + return "very " ++ s; +} + +test "comptime manyptr concatenation" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + const s = "epic"; + const actual = manyptrConcat(s); + const expected = "very epic"; + + const len = mem.len(actual); + const len_with_null = len + 1; + { + var i: u32 = 0; + while (i < len_with_null) : (i += 1) { + try expect(actual[i] == expected[i]); + } + } + try expect(actual[len] == 0); + try expect(expected[len] == 0); +} + test "thread local variable" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO From d127c1d59ebf19e5ffaedece99a39f69712683b0 Mon Sep 17 00:00:00 2001 From: Daniele Cocca Date: Sat, 30 Apr 2022 07:34:21 +0100 Subject: [PATCH 1310/2031] CBE: handle returning `undefined` for ErrorUnion Just like for Struct in 8238d4b33585a715c58ab559cd001dd3ea1db55b, in the case of ErrorUnion struct we need to return a compound literal "(T){...}" instead of just "{}", which is invalid code when used in e.g. a "return" expression. --- src/codegen/c.zig | 2 +- test/behavior/sizeof_and_typeof.zig | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 8f73daca46..2cd93d47fc 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -580,7 +580,7 @@ pub const DeclGen = struct { 64 => return writer.writeAll("(void *)0xaaaaaaaaaaaaaaaa"), else => unreachable, }, - .Struct => { + .Struct, .ErrorUnion => { try writer.writeByte('('); try dg.renderTypecast(writer, ty); return writer.writeAll("){0xaa}"); diff --git a/test/behavior/sizeof_and_typeof.zig b/test/behavior/sizeof_and_typeof.zig index 5db56243af..3dd786d1f7 100644 --- a/test/behavior/sizeof_and_typeof.zig +++ b/test/behavior/sizeof_and_typeof.zig @@ -187,7 +187,6 @@ test "@sizeOf(T) == 0 doesn't force resolving struct size" { test "@TypeOf() has no runtime side effects" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; const S = struct { fn foo(comptime T: type, ptr: *T) T { ptr.* += 1; @@ -203,7 +202,6 @@ test "@TypeOf() has no runtime side effects" { test "branching logic inside @TypeOf" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; const S = struct { var data: i32 = 0; fn foo() anyerror!i32 { From f65ca80bbeedc42d7c2d9ab7e9ad96b44d3c71f8 Mon Sep 17 00:00:00 2001 From: r00ster Date: Sun, 1 May 2022 17:05:57 +0200 Subject: [PATCH 1311/2031] std.heap: Fix typo --- lib/std/heap.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/heap.zig b/lib/std/heap.zig index aaf87367d8..65952f6c50 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -282,7 +282,7 @@ const PageAllocator = struct { return @ptrCast([*]u8, addr)[0..alignPageAllocLen(aligned_len, n, len_align)]; } - // If it wasn't, actually do an explicitely aligned allocation. + // If it wasn't, actually do an explicitly aligned allocation. w.VirtualFree(addr, 0, w.MEM_RELEASE); const alloc_size = n + alignment - mem.page_size; From 615a98351768598db65fcfc521d3c67eb443eed6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 1 May 2022 14:05:01 -0700 Subject: [PATCH 1312/2031] cmake: default install prefix to ./stage1 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aad902f953..d2b0c31054 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ if(NOT CMAKE_BUILD_TYPE) endif() if(NOT CMAKE_INSTALL_PREFIX) - set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}" CACHE STRING + set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/stage1" CACHE STRING "Directory to install zig to" FORCE) endif() From 60c2972c5d976e2dc8bf2432c8ab06edcf5c6d35 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 1 May 2022 15:02:06 -0700 Subject: [PATCH 1313/2031] stage2: fix comptime fixed-width float division --- lib/std/special/compiler_rt/fmod.zig | 49 +++++++++++++++++++--------- src/Sema.zig | 44 ++++++++++++++++--------- test/behavior/floatop.zig | 21 ++++++++++++ 3 files changed, 82 insertions(+), 32 deletions(-) diff --git a/lib/std/special/compiler_rt/fmod.zig b/lib/std/special/compiler_rt/fmod.zig index b9a5710b9c..28e0df3d6b 100644 --- a/lib/std/special/compiler_rt/fmod.zig +++ b/lib/std/special/compiler_rt/fmod.zig @@ -324,25 +324,42 @@ inline fn generic_fmod(comptime T: type, x: T, y: T) T { return @bitCast(T, ux); } -test "fmod, fmodf" { - inline for ([_]type{ f32, f64 }) |T| { - const nan_val = math.nan(T); - const inf_val = math.inf(T); +test "fmodf" { + const nan_val = math.nan(f32); + const inf_val = math.inf(f32); - try std.testing.expect(math.isNan(generic_fmod(T, nan_val, 1.0))); - try std.testing.expect(math.isNan(generic_fmod(T, 1.0, nan_val))); - try std.testing.expect(math.isNan(generic_fmod(T, inf_val, 1.0))); - try std.testing.expect(math.isNan(generic_fmod(T, 0.0, 0.0))); - try std.testing.expect(math.isNan(generic_fmod(T, 1.0, 0.0))); + try std.testing.expect(math.isNan(fmodf(nan_val, 1.0))); + try std.testing.expect(math.isNan(fmodf(1.0, nan_val))); + try std.testing.expect(math.isNan(fmodf(inf_val, 1.0))); + try std.testing.expect(math.isNan(fmodf(0.0, 0.0))); + try std.testing.expect(math.isNan(fmodf(1.0, 0.0))); - try std.testing.expectEqual(@as(T, 0.0), generic_fmod(T, 0.0, 2.0)); - try std.testing.expectEqual(@as(T, -0.0), generic_fmod(T, -0.0, 2.0)); + try std.testing.expectEqual(@as(f32, 0.0), fmodf(0.0, 2.0)); + try std.testing.expectEqual(@as(f32, -0.0), fmodf(-0.0, 2.0)); - try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, 10.0)); - try std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, -10.0)); - try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, 10.0)); - try std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, -10.0)); - } + try std.testing.expectEqual(@as(f32, -2.0), fmodf(-32.0, 10.0)); + try std.testing.expectEqual(@as(f32, -2.0), fmodf(-32.0, -10.0)); + try std.testing.expectEqual(@as(f32, 2.0), fmodf(32.0, 10.0)); + try std.testing.expectEqual(@as(f32, 2.0), fmodf(32.0, -10.0)); +} + +test "fmod" { + const nan_val = math.nan(f64); + const inf_val = math.inf(f64); + + try std.testing.expect(math.isNan(fmod(nan_val, 1.0))); + try std.testing.expect(math.isNan(fmod(1.0, nan_val))); + try std.testing.expect(math.isNan(fmod(inf_val, 1.0))); + try std.testing.expect(math.isNan(fmod(0.0, 0.0))); + try std.testing.expect(math.isNan(fmod(1.0, 0.0))); + + try std.testing.expectEqual(@as(f64, 0.0), fmod(0.0, 2.0)); + try std.testing.expectEqual(@as(f64, -0.0), fmod(-0.0, 2.0)); + + try std.testing.expectEqual(@as(f64, -2.0), fmod(-32.0, 10.0)); + try std.testing.expectEqual(@as(f64, -2.0), fmod(-32.0, -10.0)); + try std.testing.expectEqual(@as(f64, 2.0), fmod(32.0, 10.0)); + try std.testing.expectEqual(@as(f64, 2.0), fmod(32.0, -10.0)); } test { diff --git a/src/Sema.zig b/src/Sema.zig index b17ca031c2..012750e429 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9847,25 +9847,37 @@ fn analyzeArithmetic( // TODO: emit runtime safety for division by zero // // For floats: - // If the rhs is zero, compile error for division by zero. - // If the rhs is undefined, compile error because there is a possible - // value (zero) for which the division would be illegal behavior. + // If the rhs is zero: + // * comptime_float: compile error for division by zero. + // * other float type: + // * if the lhs is zero: QNaN + // * otherwise: +Inf or -Inf depending on lhs sign + // If the rhs is undefined: + // * comptime_float: compile error because there is a possible + // value (zero) for which the division would be illegal behavior. + // * other float type: result is undefined // If the lhs is undefined, result is undefined. - if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef()) { - if (lhs_val.compareWithZero(.eq)) { - return sema.addConstant(resolved_type, Value.zero); + switch (scalar_tag) { + .Int, .ComptimeInt, .ComptimeFloat => { + if (maybe_lhs_val) |lhs_val| { + if (!lhs_val.isUndef()) { + if (lhs_val.compareWithZero(.eq)) { + return sema.addConstant(resolved_type, Value.zero); + } + } } - } - } - if (maybe_rhs_val) |rhs_val| { - if (rhs_val.isUndef()) { - return sema.failWithUseOfUndef(block, rhs_src); - } - if (rhs_val.compareWithZero(.eq)) { - return sema.failWithDivideByZero(block, rhs_src); - } + if (maybe_rhs_val) |rhs_val| { + if (rhs_val.isUndef()) { + return sema.failWithUseOfUndef(block, rhs_src); + } + if (rhs_val.compareWithZero(.eq)) { + return sema.failWithDivideByZero(block, rhs_src); + } + } + }, + else => {}, } + if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index cc978f3b8d..ab87283627 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -687,3 +687,24 @@ test "f128 at compile time is lossy" { try expect(@as(f128, 10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); } + +test "comptime fixed-width float zero divided by zero produces NaN" { + inline for (.{ f16, f32, f64, f80, f128 }) |F| { + try expect(math.isNan(@as(F, 0) / @as(F, 0))); + } +} + +test "comptime fixed-width float non-zero divided by zero produces signed Inf" { + inline for (.{ f16, f32, f64, f80, f128 }) |F| { + const pos = @as(F, 1) / @as(F, 0); + const neg = @as(F, -1) / @as(F, 0); + try expect(math.isInf(pos)); + try expect(math.isInf(neg)); + try expect(pos > 0); + try expect(neg < 0); + } +} + +test "comptime_float zero divided by zero produces zero" { + try expect((0.0 / 0.0) == 0.0); +} From 82b96ca0de47efb677864b33c304565e6856da73 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 1 May 2022 15:35:35 -0700 Subject: [PATCH 1314/2031] disable failing behavior tests Oops, I forgot to run the non-LLVM backend tests on that last commit. --- test/behavior/floatop.zig | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index ab87283627..9ba61f6901 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -689,12 +689,24 @@ test "f128 at compile time is lossy" { } test "comptime fixed-width float zero divided by zero produces NaN" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + inline for (.{ f16, f32, f64, f80, f128 }) |F| { try expect(math.isNan(@as(F, 0) / @as(F, 0))); } } test "comptime fixed-width float non-zero divided by zero produces signed Inf" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + inline for (.{ f16, f32, f64, f80, f128 }) |F| { const pos = @as(F, 1) / @as(F, 0); const neg = @as(F, -1) / @as(F, 0); From 1387d2f5acf65ed809254f266788f65f8379a3a2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 1 May 2022 15:43:52 -0700 Subject: [PATCH 1315/2031] zig build: use CacheMode.whole for build_runner.zig Maybe after we have incremental compilation metadata serialization and non-LLVM backends, it will make sense to switch this back. For now, however, this makes successive `zig build` commands much faster. --- src/main.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.zig b/src/main.zig index 1538e86225..4d69bd51c7 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3677,6 +3677,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi .self_exe_path = self_exe_path, .thread_pool = &thread_pool, .use_stage1 = use_stage1, + .cache_mode = .whole, }) catch |err| { fatal("unable to create compilation: {s}", .{@errorName(err)}); }; From a3f56154d08f25dbe2b6d15de30deda8a2d9ef33 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 1 May 2022 22:11:57 -0700 Subject: [PATCH 1316/2031] stage1: disable new behavior tests Oops, I forgot to check if the new behavior tests are passing for stage1. --- test/behavior/floatop.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 9ba61f6901..c02c1c15c4 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -689,6 +689,7 @@ test "f128 at compile time is lossy" { } test "comptime fixed-width float zero divided by zero produces NaN" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -701,6 +702,7 @@ test "comptime fixed-width float zero divided by zero produces NaN" { } test "comptime fixed-width float non-zero divided by zero produces signed Inf" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -718,5 +720,7 @@ test "comptime fixed-width float non-zero divided by zero produces signed Inf" { } test "comptime_float zero divided by zero produces zero" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + try expect((0.0 / 0.0) == 0.0); } From 9af4cada73e43fb98e906a0ed79197963d1a05ac Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 May 2022 12:13:55 -0700 Subject: [PATCH 1317/2031] Sema: coerce comptime_float to fixed-width float Instead of doing heterogeneous comparison at comptime. This makes the following test pass (as it already does for stage1): ```zig test { const x: f64 = 12.34; expect(x == 12.34); } ``` There is already behavior test coverage for this, however, other bugs in `std.fmt.parseFloat` are masking the failures. From a language specification perspective, this makes sense because it makes comptime comparisons with comptime_float work the same way they work with runtime comparisons. --- src/Sema.zig | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 012750e429..87cb7cc9d2 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -20787,14 +20787,14 @@ fn cmpNumeric( sema: *Sema, block: *Block, src: LazySrcLoc, - lhs: Air.Inst.Ref, - rhs: Air.Inst.Ref, + uncasted_lhs: Air.Inst.Ref, + uncasted_rhs: Air.Inst.Ref, op: std.math.CompareOperator, lhs_src: LazySrcLoc, rhs_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { - const lhs_ty = sema.typeOf(lhs); - const rhs_ty = sema.typeOf(rhs); + const lhs_ty = sema.typeOf(uncasted_lhs); + const rhs_ty = sema.typeOf(uncasted_rhs); assert(lhs_ty.isNumeric()); assert(rhs_ty.isNumeric()); @@ -20803,6 +20803,19 @@ fn cmpNumeric( const rhs_ty_tag = rhs_ty.zigTypeTag(); const target = sema.mod.getTarget(); + // One exception to heterogeneous comparison: comptime_float needs to + // coerce to fixed-width float. + + const lhs = if (lhs_ty_tag == .ComptimeFloat and rhs_ty_tag == .Float) + try sema.coerce(block, rhs_ty, uncasted_lhs, lhs_src) + else + uncasted_lhs; + + const rhs = if (lhs_ty_tag == .Float and rhs_ty_tag == .ComptimeFloat) + try sema.coerce(block, lhs_ty, uncasted_rhs, rhs_src) + else + uncasted_rhs; + const runtime_src: LazySrcLoc = src: { if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { @@ -20845,8 +20858,10 @@ fn cmpNumeric( .Float, .ComptimeFloat => true, else => false, }; + if (lhs_is_float and rhs_is_float) { - // Implicit cast the smaller one to the larger one. + // Smaller fixed-width floats coerce to larger fixed-width floats. + // comptime_float coerces to fixed-width float. const dest_ty = x: { if (lhs_ty_tag == .ComptimeFloat) { break :x rhs_ty; From bb55276f063f2934795c1c2523dc7525cd4adce5 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Mon, 2 May 2022 09:26:08 +0200 Subject: [PATCH 1318/2031] Avoid some unnecessary underscores in constant names --- lib/std/elf.zig | 410 +++++++++++++++++++++---------------------- lib/std/os/linux.zig | 14 +- lib/std/target.zig | 112 ++++++------ 3 files changed, 268 insertions(+), 268 deletions(-) diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 7e59a04fc5..2ea928f891 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -943,577 +943,577 @@ pub const Half = switch (@sizeOf(usize)) { else => @compileError("expected pointer size of 32 or 64"), }; -/// Machine architectures +/// Machine architectures. +/// /// See current registered ELF machine architectures at: -/// http://www.sco.com/developers/gabi/latest/ch4.eheader.html -/// The underscore prefix is because many of these start with numbers. +/// http://www.sco.com/developers/gabi/latest/ch4.eheader.html pub const EM = enum(u16) { /// No machine - _NONE = 0, + NONE = 0, /// AT&T WE 32100 - _M32 = 1, + M32 = 1, /// SPARC - _SPARC = 2, + SPARC = 2, /// Intel 386 - _386 = 3, + @"386" = 3, /// Motorola 68000 - _68K = 4, + @"68K" = 4, /// Motorola 88000 - _88K = 5, + @"88K" = 5, /// Intel MCU - _IAMCU = 6, + IAMCU = 6, /// Intel 80860 - _860 = 7, + @"860" = 7, /// MIPS R3000 - _MIPS = 8, + MIPS = 8, /// IBM System/370 - _S370 = 9, + S370 = 9, /// MIPS RS3000 Little-endian - _MIPS_RS3_LE = 10, + MIPS_RS3_LE = 10, /// SPU Mark II - _SPU_2 = 13, + SPU_2 = 13, /// Hewlett-Packard PA-RISC - _PARISC = 15, + PARISC = 15, /// Fujitsu VPP500 - _VPP500 = 17, + VPP500 = 17, /// Enhanced instruction set SPARC - _SPARC32PLUS = 18, + SPARC32PLUS = 18, /// Intel 80960 - _960 = 19, + @"960" = 19, /// PowerPC - _PPC = 20, + PPC = 20, /// PowerPC64 - _PPC64 = 21, + PPC64 = 21, /// IBM System/390 - _S390 = 22, + S390 = 22, /// IBM SPU/SPC - _SPU = 23, + SPU = 23, /// NEC V800 - _V800 = 36, + V800 = 36, /// Fujitsu FR20 - _FR20 = 37, + FR20 = 37, /// TRW RH-32 - _RH32 = 38, + RH32 = 38, /// Motorola RCE - _RCE = 39, + RCE = 39, /// ARM - _ARM = 40, + ARM = 40, /// DEC Alpha - _ALPHA = 41, + ALPHA = 41, /// Hitachi SH - _SH = 42, + SH = 42, /// SPARC V9 - _SPARCV9 = 43, + SPARCV9 = 43, /// Siemens TriCore - _TRICORE = 44, + TRICORE = 44, /// Argonaut RISC Core - _ARC = 45, + ARC = 45, /// Hitachi H8/300 - _H8_300 = 46, + H8_300 = 46, /// Hitachi H8/300H - _H8_300H = 47, + H8_300H = 47, /// Hitachi H8S - _H8S = 48, + H8S = 48, /// Hitachi H8/500 - _H8_500 = 49, + H8_500 = 49, /// Intel IA-64 processor architecture - _IA_64 = 50, + IA_64 = 50, /// Stanford MIPS-X - _MIPS_X = 51, + MIPS_X = 51, /// Motorola ColdFire - _COLDFIRE = 52, + COLDFIRE = 52, /// Motorola M68HC12 - _68HC12 = 53, + @"68HC12" = 53, /// Fujitsu MMA Multimedia Accelerator - _MMA = 54, + MMA = 54, /// Siemens PCP - _PCP = 55, + PCP = 55, /// Sony nCPU embedded RISC processor - _NCPU = 56, + NCPU = 56, /// Denso NDR1 microprocessor - _NDR1 = 57, + NDR1 = 57, /// Motorola Star*Core processor - _STARCORE = 58, + STARCORE = 58, /// Toyota ME16 processor - _ME16 = 59, + ME16 = 59, /// STMicroelectronics ST100 processor - _ST100 = 60, + ST100 = 60, /// Advanced Logic Corp. TinyJ embedded processor family - _TINYJ = 61, + TINYJ = 61, /// AMD x86-64 architecture - _X86_64 = 62, + X86_64 = 62, /// Sony DSP Processor - _PDSP = 63, + PDSP = 63, /// Digital Equipment Corp. PDP-10 - _PDP10 = 64, + PDP10 = 64, /// Digital Equipment Corp. PDP-11 - _PDP11 = 65, + PDP11 = 65, /// Siemens FX66 microcontroller - _FX66 = 66, + FX66 = 66, /// STMicroelectronics ST9+ 8/16 bit microcontroller - _ST9PLUS = 67, + ST9PLUS = 67, /// STMicroelectronics ST7 8-bit microcontroller - _ST7 = 68, + ST7 = 68, /// Motorola MC68HC16 Microcontroller - _68HC16 = 69, + @"68HC16" = 69, /// Motorola MC68HC11 Microcontroller - _68HC11 = 70, + @"68HC11" = 70, /// Motorola MC68HC08 Microcontroller - _68HC08 = 71, + @"68HC08" = 71, /// Motorola MC68HC05 Microcontroller - _68HC05 = 72, + @"68HC05" = 72, /// Silicon Graphics SVx - _SVX = 73, + SVX = 73, /// STMicroelectronics ST19 8-bit microcontroller - _ST19 = 74, + ST19 = 74, /// Digital VAX - _VAX = 75, + VAX = 75, /// Axis Communications 32-bit embedded processor - _CRIS = 76, + CRIS = 76, /// Infineon Technologies 32-bit embedded processor - _JAVELIN = 77, + JAVELIN = 77, /// Element 14 64-bit DSP Processor - _FIREPATH = 78, + FIREPATH = 78, /// LSI Logic 16-bit DSP Processor - _ZSP = 79, + ZSP = 79, /// Donald Knuth's educational 64-bit processor - _MMIX = 80, + MMIX = 80, /// Harvard University machine-independent object files - _HUANY = 81, + HUANY = 81, /// SiTera Prism - _PRISM = 82, + PRISM = 82, /// Atmel AVR 8-bit microcontroller - _AVR = 83, + AVR = 83, /// Fujitsu FR30 - _FR30 = 84, + FR30 = 84, /// Mitsubishi D10V - _D10V = 85, + D10V = 85, /// Mitsubishi D30V - _D30V = 86, + D30V = 86, /// NEC v850 - _V850 = 87, + V850 = 87, /// Mitsubishi M32R - _M32R = 88, + M32R = 88, /// Matsushita MN10300 - _MN10300 = 89, + MN10300 = 89, /// Matsushita MN10200 - _MN10200 = 90, + MN10200 = 90, /// picoJava - _PJ = 91, + PJ = 91, /// OpenRISC 32-bit embedded processor - _OPENRISC = 92, + OPENRISC = 92, /// ARC International ARCompact processor (old spelling/synonym: EM_ARC_A5) - _ARC_COMPACT = 93, + ARC_COMPACT = 93, /// Tensilica Xtensa Architecture - _XTENSA = 94, + XTENSA = 94, /// Alphamosaic VideoCore processor - _VIDEOCORE = 95, + VIDEOCORE = 95, /// Thompson Multimedia General Purpose Processor - _TMM_GPP = 96, + TMM_GPP = 96, /// National Semiconductor 32000 series - _NS32K = 97, + NS32K = 97, /// Tenor Network TPC processor - _TPC = 98, + TPC = 98, /// Trebia SNP 1000 processor - _SNP1K = 99, + SNP1K = 99, /// STMicroelectronics (www.st.com) ST200 - _ST200 = 100, + ST200 = 100, /// Ubicom IP2xxx microcontroller family - _IP2K = 101, + IP2K = 101, /// MAX Processor - _MAX = 102, + MAX = 102, /// National Semiconductor CompactRISC microprocessor - _CR = 103, + CR = 103, /// Fujitsu F2MC16 - _F2MC16 = 104, + F2MC16 = 104, /// Texas Instruments embedded microcontroller msp430 - _MSP430 = 105, + MSP430 = 105, /// Analog Devices Blackfin (DSP) processor - _BLACKFIN = 106, + BLACKFIN = 106, /// S1C33 Family of Seiko Epson processors - _SE_C33 = 107, + SE_C33 = 107, /// Sharp embedded microprocessor - _SEP = 108, + SEP = 108, /// Arca RISC Microprocessor - _ARCA = 109, + ARCA = 109, /// Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University - _UNICORE = 110, + UNICORE = 110, /// eXcess: 16/32/64-bit configurable embedded CPU - _EXCESS = 111, + EXCESS = 111, /// Icera Semiconductor Inc. Deep Execution Processor - _DXP = 112, + DXP = 112, /// Altera Nios II soft-core processor - _ALTERA_NIOS2 = 113, + ALTERA_NIOS2 = 113, /// National Semiconductor CompactRISC CRX - _CRX = 114, + CRX = 114, /// Motorola XGATE embedded processor - _XGATE = 115, + XGATE = 115, /// Infineon C16x/XC16x processor - _C166 = 116, + C166 = 116, /// Renesas M16C series microprocessors - _M16C = 117, + M16C = 117, /// Microchip Technology dsPIC30F Digital Signal Controller - _DSPIC30F = 118, + DSPIC30F = 118, /// Freescale Communication Engine RISC core - _CE = 119, + CE = 119, /// Renesas M32C series microprocessors - _M32C = 120, + M32C = 120, /// Altium TSK3000 core - _TSK3000 = 131, + TSK3000 = 131, /// Freescale RS08 embedded processor - _RS08 = 132, + RS08 = 132, /// Analog Devices SHARC family of 32-bit DSP processors - _SHARC = 133, + SHARC = 133, /// Cyan Technology eCOG2 microprocessor - _ECOG2 = 134, + ECOG2 = 134, /// Sunplus S+core7 RISC processor - _SCORE7 = 135, + SCORE7 = 135, /// New Japan Radio (NJR) 24-bit DSP Processor - _DSP24 = 136, + DSP24 = 136, /// Broadcom VideoCore III processor - _VIDEOCORE3 = 137, + VIDEOCORE3 = 137, /// RISC processor for Lattice FPGA architecture - _LATTICEMICO32 = 138, + LATTICEMICO32 = 138, /// Seiko Epson C17 family - _SE_C17 = 139, + SE_C17 = 139, /// The Texas Instruments TMS320C6000 DSP family - _TI_C6000 = 140, + TI_C6000 = 140, /// The Texas Instruments TMS320C2000 DSP family - _TI_C2000 = 141, + TI_C2000 = 141, /// The Texas Instruments TMS320C55x DSP family - _TI_C5500 = 142, + TI_C5500 = 142, /// STMicroelectronics 64bit VLIW Data Signal Processor - _MMDSP_PLUS = 160, + MMDSP_PLUS = 160, /// Cypress M8C microprocessor - _CYPRESS_M8C = 161, + CYPRESS_M8C = 161, /// Renesas R32C series microprocessors - _R32C = 162, + R32C = 162, /// NXP Semiconductors TriMedia architecture family - _TRIMEDIA = 163, + TRIMEDIA = 163, /// Qualcomm Hexagon processor - _HEXAGON = 164, + HEXAGON = 164, /// Intel 8051 and variants - _8051 = 165, + @"8051" = 165, /// STMicroelectronics STxP7x family of configurable and extensible RISC processors - _STXP7X = 166, + STXP7X = 166, /// Andes Technology compact code size embedded RISC processor family - _NDS32 = 167, + NDS32 = 167, /// Cyan Technology eCOG1X family - _ECOG1X = 168, + ECOG1X = 168, /// Dallas Semiconductor MAXQ30 Core Micro-controllers - _MAXQ30 = 169, + MAXQ30 = 169, /// New Japan Radio (NJR) 16-bit DSP Processor - _XIMO16 = 170, + XIMO16 = 170, /// M2000 Reconfigurable RISC Microprocessor - _MANIK = 171, + MANIK = 171, /// Cray Inc. NV2 vector architecture - _CRAYNV2 = 172, + CRAYNV2 = 172, /// Renesas RX family - _RX = 173, + RX = 173, /// Imagination Technologies META processor architecture - _METAG = 174, + METAG = 174, /// MCST Elbrus general purpose hardware architecture - _MCST_ELBRUS = 175, + MCST_ELBRUS = 175, /// Cyan Technology eCOG16 family - _ECOG16 = 176, + ECOG16 = 176, /// National Semiconductor CompactRISC CR16 16-bit microprocessor - _CR16 = 177, + CR16 = 177, /// Freescale Extended Time Processing Unit - _ETPU = 178, + ETPU = 178, /// Infineon Technologies SLE9X core - _SLE9X = 179, + SLE9X = 179, /// Intel L10M - _L10M = 180, + L10M = 180, /// Intel K10M - _K10M = 181, + K10M = 181, /// ARM AArch64 - _AARCH64 = 183, + AARCH64 = 183, /// Atmel Corporation 32-bit microprocessor family - _AVR32 = 185, + AVR32 = 185, /// STMicroeletronics STM8 8-bit microcontroller - _STM8 = 186, + STM8 = 186, /// Tilera TILE64 multicore architecture family - _TILE64 = 187, + TILE64 = 187, /// Tilera TILEPro multicore architecture family - _TILEPRO = 188, + TILEPRO = 188, /// NVIDIA CUDA architecture - _CUDA = 190, + CUDA = 190, /// Tilera TILE-Gx multicore architecture family - _TILEGX = 191, + TILEGX = 191, /// CloudShield architecture family - _CLOUDSHIELD = 192, + CLOUDSHIELD = 192, /// KIPO-KAIST Core-A 1st generation processor family - _COREA_1ST = 193, + COREA_1ST = 193, /// KIPO-KAIST Core-A 2nd generation processor family - _COREA_2ND = 194, + COREA_2ND = 194, /// Synopsys ARCompact V2 - _ARC_COMPACT2 = 195, + ARC_COMPACT2 = 195, /// Open8 8-bit RISC soft processor core - _OPEN8 = 196, + OPEN8 = 196, /// Renesas RL78 family - _RL78 = 197, + RL78 = 197, /// Broadcom VideoCore V processor - _VIDEOCORE5 = 198, + VIDEOCORE5 = 198, /// Renesas 78KOR family - _78KOR = 199, + @"78KOR" = 199, /// Freescale 56800EX Digital Signal Controller (DSC) - _56800EX = 200, + @"56800EX" = 200, /// Beyond BA1 CPU architecture - _BA1 = 201, + BA1 = 201, /// Beyond BA2 CPU architecture - _BA2 = 202, + BA2 = 202, /// XMOS xCORE processor family - _XCORE = 203, + XCORE = 203, /// Microchip 8-bit PIC(r) family - _MCHP_PIC = 204, + MCHP_PIC = 204, /// Reserved by Intel - _INTEL205 = 205, + INTEL205 = 205, /// Reserved by Intel - _INTEL206 = 206, + INTEL206 = 206, /// Reserved by Intel - _INTEL207 = 207, + INTEL207 = 207, /// Reserved by Intel - _INTEL208 = 208, + INTEL208 = 208, /// Reserved by Intel - _INTEL209 = 209, + INTEL209 = 209, /// KM211 KM32 32-bit processor - _KM32 = 210, + KM32 = 210, /// KM211 KMX32 32-bit processor - _KMX32 = 211, + KMX32 = 211, /// KM211 KMX16 16-bit processor - _KMX16 = 212, + KMX16 = 212, /// KM211 KMX8 8-bit processor - _KMX8 = 213, + KMX8 = 213, /// KM211 KVARC processor - _KVARC = 214, + KVARC = 214, /// Paneve CDP architecture family - _CDP = 215, + CDP = 215, /// Cognitive Smart Memory Processor - _COGE = 216, + COGE = 216, /// iCelero CoolEngine - _COOL = 217, + COOL = 217, /// Nanoradio Optimized RISC - _NORC = 218, + NORC = 218, /// CSR Kalimba architecture family - _CSR_KALIMBA = 219, + CSR_KALIMBA = 219, /// AMD GPU architecture - _AMDGPU = 224, + AMDGPU = 224, /// RISC-V - _RISCV = 243, + RISCV = 243, /// Lanai 32-bit processor - _LANAI = 244, + LANAI = 244, /// Linux kernel bpf virtual machine - _BPF = 247, + BPF = 247, /// C-SKY - _CSKY = 252, + CSKY = 252, /// Fujitsu FR-V - _FRV = 0x5441, + FRV = 0x5441, _, pub fn toTargetCpuArch(em: EM) ?std.Target.Cpu.Arch { return switch (em) { - ._AVR => .avr, - ._MSP430 => .msp430, - ._ARC => .arc, - ._ARM => .arm, - ._HEXAGON => .hexagon, - ._68K => .m68k, - ._MIPS => .mips, - ._MIPS_RS3_LE => .mipsel, - ._PPC => .powerpc, - ._SPARC => .sparc, - ._386 => .i386, - ._XCORE => .xcore, - ._CSR_KALIMBA => .kalimba, - ._LANAI => .lanai, - ._AARCH64 => .aarch64, - ._PPC64 => .powerpc64, - ._RISCV => .riscv64, - ._X86_64 => .x86_64, - ._BPF => .bpfel, - ._SPARCV9 => .sparcv9, - ._S390 => .s390x, - ._SPU_2 => .spu_2, + .AVR => .avr, + .MSP430 => .msp430, + .ARC => .arc, + .ARM => .arm, + .HEXAGON => .hexagon, + .@"68K" => .m68k, + .MIPS => .mips, + .MIPS_RS3_LE => .mipsel, + .PPC => .powerpc, + .SPARC => .sparc, + .@"386" => .i386, + .XCORE => .xcore, + .CSR_KALIMBA => .kalimba, + .LANAI => .lanai, + .AARCH64 => .aarch64, + .PPC64 => .powerpc64, + .RISCV => .riscv64, + .X86_64 => .x86_64, + .BPF => .bpfel, + .SPARCV9 => .sparcv9, + .S390 => .s390x, + .SPU_2 => .spu_2, // there's many cases we don't (yet) handle, or will never have a // zig target cpu arch equivalent (such as null). else => null, diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index de79091f53..739b924b2c 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -5418,8 +5418,8 @@ pub const PERF = struct { // TODO: Add the rest of the AUDIT defines? pub const AUDIT = struct { pub const ARCH = enum(u32) { - const _64BIT = 0x80000000; - const _LE = 0x40000000; + const @"64BIT" = 0x80000000; + const LE = 0x40000000; pub const current: AUDIT.ARCH = switch (native_arch) { .i386 => .I386, @@ -5440,13 +5440,13 @@ pub const AUDIT = struct { ARM = toAudit(.arm), ARMEB = toAudit(.armeb), CSKY = toAudit(.csky), - HEXAGON = @enumToInt(std.elf.EM._HEXAGON), + HEXAGON = @enumToInt(std.elf.EM.HEXAGON), I386 = toAudit(.i386), M68K = toAudit(.m68k), MIPS = toAudit(.mips), - MIPSEL = toAudit(.mips) | _LE, + MIPSEL = toAudit(.mips) | LE, MIPS64 = toAudit(.mips64), - MIPSEL64 = toAudit(.mips64) | _LE, + MIPSEL64 = toAudit(.mips64) | LE, PPC = toAudit(.powerpc), PPC64 = toAudit(.powerpc64), PPC64LE = toAudit(.powerpc64le), @@ -5459,8 +5459,8 @@ pub const AUDIT = struct { fn toAudit(arch: std.Target.Cpu.Arch) u32 { var res: u32 = @enumToInt(arch.toElfMachine()); - if (arch.endian() == .Little) res |= _LE; - if (arch.ptrBitWidth() == 64) res |= _64BIT; + if (arch.endian() == .Little) res |= LE; + if (arch.ptrBitWidth() == 64) res |= @"64BIT"; return res; } diff --git a/lib/std/target.zig b/lib/std/target.zig index a849e223d2..c23a42c963 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -914,62 +914,62 @@ pub const Target = struct { pub fn toElfMachine(arch: Arch) std.elf.EM { return switch (arch) { - .avr => ._AVR, - .msp430 => ._MSP430, - .arc => ._ARC, - .arm => ._ARM, - .armeb => ._ARM, - .hexagon => ._HEXAGON, - .m68k => ._68K, - .le32 => ._NONE, - .mips => ._MIPS, - .mipsel => ._MIPS_RS3_LE, - .powerpc, .powerpcle => ._PPC, - .r600 => ._NONE, - .riscv32 => ._RISCV, - .sparc => ._SPARC, - .sparcel => ._SPARC, - .tce => ._NONE, - .tcele => ._NONE, - .thumb => ._ARM, - .thumbeb => ._ARM, - .i386 => ._386, - .xcore => ._XCORE, - .nvptx => ._NONE, - .amdil => ._NONE, - .hsail => ._NONE, - .spir => ._NONE, - .kalimba => ._CSR_KALIMBA, - .shave => ._NONE, - .lanai => ._LANAI, - .wasm32 => ._NONE, - .renderscript32 => ._NONE, - .aarch64_32 => ._AARCH64, - .aarch64 => ._AARCH64, - .aarch64_be => ._AARCH64, - .mips64 => ._MIPS, - .mips64el => ._MIPS_RS3_LE, - .powerpc64 => ._PPC64, - .powerpc64le => ._PPC64, - .riscv64 => ._RISCV, - .x86_64 => ._X86_64, - .nvptx64 => ._NONE, - .le64 => ._NONE, - .amdil64 => ._NONE, - .hsail64 => ._NONE, - .spir64 => ._NONE, - .wasm64 => ._NONE, - .renderscript64 => ._NONE, - .amdgcn => ._NONE, - .bpfel => ._BPF, - .bpfeb => ._BPF, - .csky => ._CSKY, - .sparcv9 => ._SPARCV9, - .s390x => ._S390, - .ve => ._NONE, - .spu_2 => ._SPU_2, - .spirv32 => ._NONE, - .spirv64 => ._NONE, + .avr => .AVR, + .msp430 => .MSP430, + .arc => .ARC, + .arm => .ARM, + .armeb => .ARM, + .hexagon => .HEXAGON, + .m68k => .@"68K", + .le32 => .NONE, + .mips => .MIPS, + .mipsel => .MIPS_RS3_LE, + .powerpc, .powerpcle => .PPC, + .r600 => .NONE, + .riscv32 => .RISCV, + .sparc => .SPARC, + .sparcel => .SPARC, + .tce => .NONE, + .tcele => .NONE, + .thumb => .ARM, + .thumbeb => .ARM, + .i386 => .@"386", + .xcore => .XCORE, + .nvptx => .NONE, + .amdil => .NONE, + .hsail => .NONE, + .spir => .NONE, + .kalimba => .CSR_KALIMBA, + .shave => .NONE, + .lanai => .LANAI, + .wasm32 => .NONE, + .renderscript32 => .NONE, + .aarch64_32 => .AARCH64, + .aarch64 => .AARCH64, + .aarch64_be => .AARCH64, + .mips64 => .MIPS, + .mips64el => .MIPS_RS3_LE, + .powerpc64 => .PPC64, + .powerpc64le => .PPC64, + .riscv64 => .RISCV, + .x86_64 => .X86_64, + .nvptx64 => .NONE, + .le64 => .NONE, + .amdil64 => .NONE, + .hsail64 => .NONE, + .spir64 => .NONE, + .wasm64 => .NONE, + .renderscript64 => .NONE, + .amdgcn => .NONE, + .bpfel => .BPF, + .bpfeb => .BPF, + .csky => .CSKY, + .sparcv9 => .SPARCV9, + .s390x => .S390, + .ve => .NONE, + .spu_2 => .SPU_2, + .spirv32 => .NONE, + .spirv64 => .NONE, }; } From 3679d737f88fc18da8b8f1d6d6d2a6e8690ebd1d Mon Sep 17 00:00:00 2001 From: Lee Cannon Date: Sun, 1 May 2022 20:44:48 +0100 Subject: [PATCH 1319/2031] Allocator: correct `PanicFree` function name --- lib/std/mem/Allocator.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/mem/Allocator.zig b/lib/std/mem/Allocator.zig index 2c6d53849d..2edfde04a6 100644 --- a/lib/std/mem/Allocator.zig +++ b/lib/std/mem/Allocator.zig @@ -144,10 +144,10 @@ pub fn NoOpFree(comptime AllocatorType: type) type { }; } -/// Set freeFn to `PanicFree(AllocatorType).noOpFree` if free is not a supported operation. +/// Set freeFn to `PanicFree(AllocatorType).panicFree` if free is not a supported operation. pub fn PanicFree(comptime AllocatorType: type) type { return struct { - pub fn noOpFree( + pub fn panicFree( self: *AllocatorType, buf: []u8, buf_align: u29, From f648a1b0439aaf22be26b29d290e53493f4db16b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 2 May 2022 21:38:05 +0200 Subject: [PATCH 1320/2031] test: regression fix: skip stage1 if not enabled --- src/test.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test.zig b/src/test.zig index 758347ac18..bdc02ff2e0 100644 --- a/src/test.zig +++ b/src/test.zig @@ -1053,6 +1053,8 @@ pub const TestContext = struct { // Cross-product to get all possible test combinations for (backends) |backend| { + if (backend == .stage1 and skip_stage1) continue; + for (targets) |target| { const name = try std.fmt.allocPrint(ctx.arena, "{s} ({s}, {s})", .{ name_prefix, From 098bee0e5657bb6dcd92b2b2fa8056ffce893ffc Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Mon, 2 May 2022 20:28:34 -0700 Subject: [PATCH 1321/2031] edwards25519 fixes (#11568) * edwards25519: fix X coordinate of the base point Reported by @OfekShochat -- Thanks! * edwards25519: reduce public scalar when the top bit is set, not cleared This is an optimization for the unexpected case of a scalar larger than the field size. Fixes #11563 * edwards25519: add a test implicit reduction of invalid scalars --- lib/std/crypto/25519/edwards25519.zig | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/std/crypto/25519/edwards25519.zig b/lib/std/crypto/25519/edwards25519.zig index fcfc6ee258..b106304a3a 100644 --- a/lib/std/crypto/25519/edwards25519.zig +++ b/lib/std/crypto/25519/edwards25519.zig @@ -62,7 +62,7 @@ pub const Edwards25519 = struct { /// The edwards25519 base point. pub const basePoint = Edwards25519{ - .x = Fe{ .limbs = .{ 3990542415680775, 3398198340507945, 4322667446711068, 2814063955482877, 2839572215813860 } }, + .x = Fe{ .limbs = .{ 1738742601995546, 1146398526822698, 2070867633025821, 562264141797630, 587772402128613 } }, .y = Fe{ .limbs = .{ 1801439850948184, 1351079888211148, 450359962737049, 900719925474099, 1801439850948198 } }, .z = Fe.one, .t = Fe{ .limbs = .{ 1841354044333475, 16398895984059, 755974180946558, 900171276175154, 1821297809914039 } }, @@ -147,7 +147,7 @@ pub const Edwards25519 = struct { } fn slide(s: [32]u8) [2 * 32]i8 { - const reduced = if ((s[s.len - 1] & 0x80) != 0) s else scalar.reduce(s); + const reduced = if ((s[s.len - 1] & 0x80) == 0) s else scalar.reduce(s); var e: [2 * 32]i8 = undefined; for (reduced) |x, i| { e[i * 2 + 0] = @as(i8, @truncate(u4, x)); @@ -549,3 +549,17 @@ test "edwards25519 hash-to-curve operation" { p = Edwards25519.fromString(false, "QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_NU_", "abc"); try htest.assertEqual("42fa27c8f5a1ae0aa38bb59d5938e5145622ba5dedd11d11736fa2f9502d7367", p.toBytes()[0..]); } + +test "edwards25519 implicit reduction of invalid scalars" { + const s = [_]u8{0} ** 31 ++ [_]u8{255}; + const p1 = try Edwards25519.basePoint.mulPublic(s); + const p2 = try Edwards25519.basePoint.mul(s); + const p3 = try p1.mulPublic(s); + const p4 = try p1.mul(s); + + try std.testing.expectEqualSlices(u8, p1.toBytes()[0..], p2.toBytes()[0..]); + try std.testing.expectEqualSlices(u8, p3.toBytes()[0..], p4.toBytes()[0..]); + + try htest.assertEqual("339f189ecc5fbebe9895345c72dc07bda6e615f8a40e768441b6f529cd6c671a", p1.toBytes()[0..]); + try htest.assertEqual("a501e4c595a3686d8bee7058c7e6af7fd237f945c47546910e37e0e79b1bafb0", p3.toBytes()[0..]); +} From 2085a4af5654a74f2a5d8ac1fa7934a3663bf3a0 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Mon, 2 May 2022 22:45:06 +1200 Subject: [PATCH 1322/2031] add new float-parser based on eisel-lemire algorithm The previous float-parsing method was lacking in a lot of areas. This commit introduces a state-of-the art implementation that is both accurate and fast to std. Code is derived from working repo https://github.com/tiehuis/zig-parsefloat. This includes more test-cases and performance numbers that are present in this commit. * Accuracy The primary testing regime has been using test-data found at https://github.com/tiehuis/parse-number-fxx-test-data. This is a fork of upstream with support for f128 test-cases added. This data has been verified against other independent implementations and represents accurate round-to-even IEEE-754 floating point semantics. * Performance Compared to the existing parseFloat implementation there is ~5-10x performance improvement using the above corpus. (f128 parsing excluded in below measurements). ** Old $ time ./test_all_fxx_data 3520298/5296694 succeeded (1776396 fail) ________________________________________________________ Executed in 28.68 secs fish external usr time 28.48 secs 0.00 micros 28.48 secs sys time 0.08 secs 694.00 micros 0.08 secs ** This Implementation $ time ./test_all_fxx_data 5296693/5296694 succeeded (1 fail) ________________________________________________________ Executed in 4.54 secs fish external usr time 4.37 secs 515.00 micros 4.37 secs sys time 0.10 secs 171.00 micros 0.10 secs Further performance numbers can be seen using the https://github.com/tiehuis/simple_fastfloat_benchmark/ repository, which compares against some other well-known string-to-float conversion functions. A breakdown can be found here: https://github.com/tiehuis/zig-parsefloat/blob/0d9f020f1a37ca88bf889703b397c1c41779f090/PERFORMANCE.md#commit-b15406a0d2e18b50a4b62fceb5a6a3bb60ca5706 In summary, we are within 20% of the C++ reference implementation and have about ~600-700MB/s throughput on a Intel I5-6500 3.5Ghz. * F128 Support Finally, f128 is now completely supported with full accuracy. This does use a slower path which is possible to improve in future. * Behavioural Changes There are a few behavioural changes to note. - `parseHexFloat` is now redundant and these are now supported directly in `parseFloat`. - We implement round-to-even in all parsing routines. This is as specified by IEEE-754. Previous code used different rounding mechanisms (standard was round-to-zero, hex-parsing looked to use round-up) so there may be subtle differences. Closes #2207. Fixes #11169. --- lib/std/fmt.zig | 2 +- lib/std/fmt/parse_float.zig | 499 +++-------- lib/std/fmt/parse_float/FloatInfo.zig | 131 +++ lib/std/fmt/parse_float/FloatStream.zig | 137 +++ lib/std/fmt/parse_float/common.zig | 91 ++ .../fmt/parse_float/convert_eisel_lemire.zig | 843 ++++++++++++++++++ lib/std/fmt/parse_float/convert_fast.zig | 130 +++ lib/std/fmt/parse_float/convert_hex.zig | 89 ++ lib/std/fmt/parse_float/convert_slow.zig | 114 +++ lib/std/fmt/parse_float/decimal.zig | 493 ++++++++++ lib/std/fmt/parse_float/parse.zig | 293 ++++++ lib/std/fmt/parse_float/parse_float.zig | 64 ++ lib/std/fmt/parse_hex_float.zig | 347 ------- 13 files changed, 2497 insertions(+), 736 deletions(-) create mode 100644 lib/std/fmt/parse_float/FloatInfo.zig create mode 100644 lib/std/fmt/parse_float/FloatStream.zig create mode 100644 lib/std/fmt/parse_float/common.zig create mode 100644 lib/std/fmt/parse_float/convert_eisel_lemire.zig create mode 100644 lib/std/fmt/parse_float/convert_fast.zig create mode 100644 lib/std/fmt/parse_float/convert_hex.zig create mode 100644 lib/std/fmt/parse_float/convert_slow.zig create mode 100644 lib/std/fmt/parse_float/decimal.zig create mode 100644 lib/std/fmt/parse_float/parse.zig create mode 100644 lib/std/fmt/parse_float/parse_float.zig delete mode 100644 lib/std/fmt/parse_hex_float.zig diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 6fa8cfdfa3..96ed842154 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -1837,8 +1837,8 @@ test "parseUnsigned" { } pub const parseFloat = @import("fmt/parse_float.zig").parseFloat; +pub const parseHexFloat = @compileError("deprecated; use `parseFloat`"); pub const ParseFloatError = @import("fmt/parse_float.zig").ParseFloatError; -pub const parseHexFloat = @import("fmt/parse_hex_float.zig").parseHexFloat; test { _ = parseFloat; diff --git a/lib/std/fmt/parse_float.zig b/lib/std/fmt/parse_float.zig index 585c23ed54..44b6fb2fc0 100644 --- a/lib/std/fmt/parse_float.zig +++ b/lib/std/fmt/parse_float.zig @@ -1,386 +1,18 @@ -// Adapted from https://github.com/grzegorz-kraszewski/stringtofloat. - -// MIT License -// -// Copyright (c) 2016 Grzegorz Kraszewski -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// - -// Be aware that this implementation has the following limitations: -// -// - Is not round-trip accurate for all values -// - Only supports round-to-zero -// - Does not handle denormals +pub const parseFloat = @import("parse_float/parse_float.zig").parseFloat; +pub const ParseFloatError = @import("parse_float/parse_float.zig").ParseFloatError; const std = @import("std"); -const ascii = std.ascii; +const math = std.math; +const testing = std.testing; +const expect = testing.expect; +const expectEqual = testing.expectEqual; +const expectError = testing.expectError; +const approxEqAbs = std.math.approxEqAbs; +const epsilon = 1e-7; -// The mantissa field in FloatRepr is 64bit wide and holds only 19 digits -// without overflowing -const max_digits = 19; - -const f64_plus_zero: u64 = 0x0000000000000000; -const f64_minus_zero: u64 = 0x8000000000000000; -const f64_plus_infinity: u64 = 0x7FF0000000000000; -const f64_minus_infinity: u64 = 0xFFF0000000000000; - -const Z96 = struct { - d0: u32, - d1: u32, - d2: u32, - - // d = s >> 1 - inline fn shiftRight1(d: *Z96, s: Z96) void { - d.d0 = (s.d0 >> 1) | ((s.d1 & 1) << 31); - d.d1 = (s.d1 >> 1) | ((s.d2 & 1) << 31); - d.d2 = s.d2 >> 1; - } - - // d = s << 1 - inline fn shiftLeft1(d: *Z96, s: Z96) void { - d.d2 = (s.d2 << 1) | ((s.d1 & (1 << 31)) >> 31); - d.d1 = (s.d1 << 1) | ((s.d0 & (1 << 31)) >> 31); - d.d0 = s.d0 << 1; - } - - // d += s - inline fn add(d: *Z96, s: Z96) void { - var w = @as(u64, d.d0) + @as(u64, s.d0); - d.d0 = @truncate(u32, w); - - w >>= 32; - w += @as(u64, d.d1) + @as(u64, s.d1); - d.d1 = @truncate(u32, w); - - w >>= 32; - w += @as(u64, d.d2) + @as(u64, s.d2); - d.d2 = @truncate(u32, w); - } - - // d -= s - inline fn sub(d: *Z96, s: Z96) void { - var w = @as(u64, d.d0) -% @as(u64, s.d0); - d.d0 = @truncate(u32, w); - - w >>= 32; - w += @as(u64, d.d1) -% @as(u64, s.d1); - d.d1 = @truncate(u32, w); - - w >>= 32; - w += @as(u64, d.d2) -% @as(u64, s.d2); - d.d2 = @truncate(u32, w); - } -}; - -const FloatRepr = struct { - negative: bool, - exponent: i32, - mantissa: u64, -}; - -fn convertRepr(comptime T: type, n: FloatRepr) T { - const mask28: u32 = 0xf << 28; - - var s: Z96 = undefined; - var q: Z96 = undefined; - var r: Z96 = undefined; - - s.d0 = @truncate(u32, n.mantissa); - s.d1 = @truncate(u32, n.mantissa >> 32); - s.d2 = 0; - - var binary_exponent: i32 = 92; - var exp = n.exponent; - - while (exp > 0) : (exp -= 1) { - q.shiftLeft1(s); // q = p << 1 - r.shiftLeft1(q); // r = p << 2 - s.shiftLeft1(r); // p = p << 3 - s.add(q); // p = (p << 3) + (p << 1) - - while (s.d2 & mask28 != 0) { - q.shiftRight1(s); - binary_exponent += 1; - s = q; - } - } - - while (exp < 0) { - while (s.d2 & (1 << 31) == 0) { - q.shiftLeft1(s); - binary_exponent -= 1; - s = q; - } - - q.d2 = s.d2 / 10; - r.d1 = s.d2 % 10; - r.d2 = (s.d1 >> 8) | (r.d1 << 24); - q.d1 = r.d2 / 10; - r.d1 = r.d2 % 10; - r.d2 = ((s.d1 & 0xff) << 16) | (s.d0 >> 16) | (r.d1 << 24); - r.d0 = r.d2 / 10; - r.d1 = r.d2 % 10; - q.d1 = (q.d1 << 8) | ((r.d0 & 0x00ff0000) >> 16); - q.d0 = r.d0 << 16; - r.d2 = (s.d0 *% 0xffff) | (r.d1 << 16); - q.d0 |= r.d2 / 10; - s = q; - - exp += 1; - } - - if (s.d0 != 0 or s.d1 != 0 or s.d2 != 0) { - while (s.d2 & mask28 == 0) { - q.shiftLeft1(s); - binary_exponent -= 1; - s = q; - } - } - - binary_exponent += 1023; - - const repr: u64 = blk: { - if (binary_exponent > 2046) { - break :blk if (n.negative) f64_minus_infinity else f64_plus_infinity; - } else if (binary_exponent < 1) { - break :blk if (n.negative) f64_minus_zero else f64_plus_zero; - } else if (s.d2 != 0) { - const binexs2 = @intCast(u64, binary_exponent) << 52; - const rr = (@as(u64, s.d2 & ~mask28) << 24) | ((@as(u64, s.d1) + 128) >> 8) | binexs2; - break :blk if (n.negative) rr | (1 << 63) else rr; - } else { - break :blk 0; - } - }; - - const f = @bitCast(f64, repr); - return @floatCast(T, f); -} - -const State = enum { - MaybeSign, - LeadingMantissaZeros, - LeadingFractionalZeros, - MantissaIntegral, - MantissaFractional, - ExponentSign, - LeadingExponentZeros, - Exponent, -}; - -const ParseResult = enum { - Ok, - PlusZero, - MinusZero, - PlusInf, - MinusInf, -}; - -fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult { - var digit_index: usize = 0; - var negative_exp = false; - var exponent: i32 = 0; - - var state = State.MaybeSign; - - var i: usize = 0; - while (i < s.len) { - const c = s[i]; - - switch (state) { - .MaybeSign => { - state = .LeadingMantissaZeros; - - if (c == '+') { - i += 1; - } else if (c == '-') { - n.negative = true; - i += 1; - } else if (ascii.isDigit(c) or c == '.') { - // continue - } else { - return error.InvalidCharacter; - } - }, - .LeadingMantissaZeros => { - if (c == '0') { - i += 1; - } else if (c == '.') { - i += 1; - state = .LeadingFractionalZeros; - } else if (c == '_') { - i += 1; - } else { - state = .MantissaIntegral; - } - }, - .LeadingFractionalZeros => { - if (c == '0') { - i += 1; - if (n.exponent > std.math.minInt(i32)) { - n.exponent -= 1; - } - } else { - state = .MantissaFractional; - } - }, - .MantissaIntegral => { - if (ascii.isDigit(c)) { - if (digit_index < max_digits) { - n.mantissa *%= 10; - n.mantissa += c - '0'; - digit_index += 1; - } else if (n.exponent < std.math.maxInt(i32)) { - n.exponent += 1; - } - - i += 1; - } else if (c == '.') { - i += 1; - state = .MantissaFractional; - } else if (c == '_') { - i += 1; - } else { - state = .MantissaFractional; - } - }, - .MantissaFractional => { - if (ascii.isDigit(c)) { - if (digit_index < max_digits) { - n.mantissa *%= 10; - n.mantissa += c - '0'; - n.exponent -%= 1; - digit_index += 1; - } - - i += 1; - } else if (c == 'e' or c == 'E') { - i += 1; - state = .ExponentSign; - } else if (c == '_') { - i += 1; - } else { - state = .ExponentSign; - } - }, - .ExponentSign => { - if (c == '+') { - i += 1; - } else if (c == '_') { - return error.InvalidCharacter; - } else if (c == '-') { - negative_exp = true; - i += 1; - } - - state = .LeadingExponentZeros; - }, - .LeadingExponentZeros => { - if (c == '0') { - i += 1; - } else if (c == '_') { - i += 1; - } else { - state = .Exponent; - } - }, - .Exponent => { - if (ascii.isDigit(c)) { - if (exponent < std.math.maxInt(i32) / 10) { - exponent *= 10; - exponent += @intCast(i32, c - '0'); - } - - i += 1; - } else if (c == '_') { - i += 1; - } else { - return error.InvalidCharacter; - } - }, - } - } - - if (negative_exp) exponent = -exponent; - n.exponent += exponent; - - if (n.mantissa == 0) { - return if (n.negative) .MinusZero else .PlusZero; - } else if (n.exponent > 309) { - return if (n.negative) .MinusInf else .PlusInf; - } else if (n.exponent < -328) { - return if (n.negative) .MinusZero else .PlusZero; - } - - return .Ok; -} - -fn caseInEql(a: []const u8, b: []const u8) bool { - if (a.len != b.len) return false; - - for (a) |_, i| { - if (ascii.toUpper(a[i]) != ascii.toUpper(b[i])) { - return false; - } - } - - return true; -} - -pub const ParseFloatError = error{InvalidCharacter}; - -pub fn parseFloat(comptime T: type, s: []const u8) ParseFloatError!T { - if (s.len == 0 or (s.len == 1 and (s[0] == '+' or s[0] == '-'))) { - return error.InvalidCharacter; - } - - if (caseInEql(s, "nan")) { - return std.math.nan(T); - } else if (caseInEql(s, "inf") or caseInEql(s, "+inf")) { - return std.math.inf(T); - } else if (caseInEql(s, "-inf")) { - return -std.math.inf(T); - } - - var r = FloatRepr{ - .negative = false, - .exponent = 0, - .mantissa = 0, - }; - - return switch (try parseRepr(s, &r)) { - .Ok => convertRepr(T, r), - .PlusZero => 0.0, - .MinusZero => -@as(T, 0.0), - .PlusInf => std.math.inf(T), - .MinusInf => -std.math.inf(T), - }; -} +// See https://github.com/tiehuis/parse-number-fxx-test-data for a wider-selection of test-data. test "fmt.parseFloat" { - const testing = std.testing; - const expect = testing.expect; - const expectEqual = testing.expectEqual; - const approxEqAbs = std.math.approxEqAbs; - const epsilon = 1e-7; - inline for ([_]type{ f16, f32, f64, f128 }) |T| { const Z = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); @@ -405,8 +37,8 @@ test "fmt.parseFloat" { try expect(approxEqAbs(T, try parseFloat(T, "3.141"), 3.141, epsilon)); try expect(approxEqAbs(T, try parseFloat(T, "-3.141"), -3.141, epsilon)); - try expectEqual(try parseFloat(T, "1e-700"), 0); - try expectEqual(try parseFloat(T, "1e+700"), std.math.inf(T)); + try expectEqual(try parseFloat(T, "1e-5000"), 0); + try expectEqual(try parseFloat(T, "1e+5000"), std.math.inf(T)); try expectEqual(@bitCast(Z, try parseFloat(T, "nAn")), @bitCast(Z, std.math.nan(T))); try expectEqual(try parseFloat(T, "inF"), std.math.inf(T)); @@ -415,14 +47,105 @@ test "fmt.parseFloat" { try expectEqual(try parseFloat(T, "0.4e0066999999999999999999999999999999999999999999999999999"), std.math.inf(T)); try expect(approxEqAbs(T, try parseFloat(T, "0_1_2_3_4_5_6.7_8_9_0_0_0e0_0_1_0"), @as(T, 123456.789000e10), epsilon)); - if (T != f16) { - try expect(approxEqAbs(T, try parseFloat(T, "1e-2"), 0.01, epsilon)); - try expect(approxEqAbs(T, try parseFloat(T, "1234e-2"), 12.34, epsilon)); + // underscore rule is simple and reduces to "can only occur between two digits" and multiple are not supported. + try expectError(error.InvalidCharacter, parseFloat(T, "0123456.789000e_0010")); // cannot occur immediately after exponent + try expectError(error.InvalidCharacter, parseFloat(T, "_0123456.789000e0010")); // cannot occur before any digits + try expectError(error.InvalidCharacter, parseFloat(T, "0__123456.789000e_0010")); // cannot occur twice in a row + try expectError(error.InvalidCharacter, parseFloat(T, "0123456_.789000e0010")); // cannot occur before decimal point + try expectError(error.InvalidCharacter, parseFloat(T, "0123456.789000e0010_")); // cannot occur at end of number - try expect(approxEqAbs(T, try parseFloat(T, "123142.1"), 123142.1, epsilon)); - try expect(approxEqAbs(T, try parseFloat(T, "-123142.1124"), @as(T, -123142.1124), epsilon)); - try expect(approxEqAbs(T, try parseFloat(T, "0.7062146892655368"), @as(T, 0.7062146892655368), epsilon)); - try expect(approxEqAbs(T, try parseFloat(T, "2.71828182845904523536"), @as(T, 2.718281828459045), epsilon)); - } + try expect(approxEqAbs(T, try parseFloat(T, "1e-2"), 0.01, epsilon)); + try expect(approxEqAbs(T, try parseFloat(T, "1234e-2"), 12.34, epsilon)); + + try expect(approxEqAbs(T, try parseFloat(T, "123142.1"), 123142.1, epsilon)); + try expect(approxEqAbs(T, try parseFloat(T, "-123142.1124"), @as(T, -123142.1124), epsilon)); + try expect(approxEqAbs(T, try parseFloat(T, "0.7062146892655368"), @as(T, 0.7062146892655368), epsilon)); + try expect(approxEqAbs(T, try parseFloat(T, "2.71828182845904523536"), @as(T, 2.718281828459045), epsilon)); } } + +test "fmt.parseFloat #11169" { + try expectEqual(try parseFloat(f128, "9007199254740993.0"), 9007199254740993.0); +} + +test "fmt.parseFloat hex.special" { + try testing.expect(math.isNan(try parseFloat(f32, "nAn"))); + try testing.expect(math.isPositiveInf(try parseFloat(f32, "iNf"))); + try testing.expect(math.isPositiveInf(try parseFloat(f32, "+Inf"))); + try testing.expect(math.isNegativeInf(try parseFloat(f32, "-iNf"))); +} +test "fmt.parseFloat hex.zero" { + try testing.expectEqual(@as(f32, 0.0), try parseFloat(f32, "0x0")); + try testing.expectEqual(@as(f32, 0.0), try parseFloat(f32, "-0x0")); + try testing.expectEqual(@as(f32, 0.0), try parseFloat(f32, "0x0p42")); + try testing.expectEqual(@as(f32, 0.0), try parseFloat(f32, "-0x0.00000p42")); + try testing.expectEqual(@as(f32, 0.0), try parseFloat(f32, "0x0.00000p666")); +} + +test "fmt.parseFloat hex.f16" { + try testing.expectEqual(try parseFloat(f16, "0x1p0"), 1.0); + try testing.expectEqual(try parseFloat(f16, "-0x1p-1"), -0.5); + try testing.expectEqual(try parseFloat(f16, "0x10p+10"), 16384.0); + try testing.expectEqual(try parseFloat(f16, "0x10p-10"), 0.015625); + // Max normalized value. + try testing.expectEqual(try parseFloat(f16, "0x1.ffcp+15"), math.floatMax(f16)); + try testing.expectEqual(try parseFloat(f16, "-0x1.ffcp+15"), -math.floatMax(f16)); + // Min normalized value. + try testing.expectEqual(try parseFloat(f16, "0x1p-14"), math.floatMin(f16)); + try testing.expectEqual(try parseFloat(f16, "-0x1p-14"), -math.floatMin(f16)); + // Min denormal value. + try testing.expectEqual(try parseFloat(f16, "0x1p-24"), math.floatTrueMin(f16)); + try testing.expectEqual(try parseFloat(f16, "-0x1p-24"), -math.floatTrueMin(f16)); +} + +test "fmt.parseFloat hex.f32" { + try testing.expectEqual(try parseFloat(f32, "0x1p0"), 1.0); + try testing.expectEqual(try parseFloat(f32, "-0x1p-1"), -0.5); + try testing.expectEqual(try parseFloat(f32, "0x10p+10"), 16384.0); + try testing.expectEqual(try parseFloat(f32, "0x10p-10"), 0.015625); + try testing.expectEqual(try parseFloat(f32, "0x0.ffffffp128"), 0x0.ffffffp128); + try testing.expectEqual(try parseFloat(f32, "0x0.1234570p-125"), 0x0.1234570p-125); + // Max normalized value. + try testing.expectEqual(try parseFloat(f32, "0x1.fffffeP+127"), math.floatMax(f32)); + try testing.expectEqual(try parseFloat(f32, "-0x1.fffffeP+127"), -math.floatMax(f32)); + // Min normalized value. + try testing.expectEqual(try parseFloat(f32, "0x1p-126"), math.floatMin(f32)); + try testing.expectEqual(try parseFloat(f32, "-0x1p-126"), -math.floatMin(f32)); + // Min denormal value. + try testing.expectEqual(try parseFloat(f32, "0x1P-149"), math.floatTrueMin(f32)); + try testing.expectEqual(try parseFloat(f32, "-0x1P-149"), -math.floatTrueMin(f32)); +} + +test "fmt.parseFloat hex.f64" { + try testing.expectEqual(try parseFloat(f64, "0x1p0"), 1.0); + try testing.expectEqual(try parseFloat(f64, "-0x1p-1"), -0.5); + try testing.expectEqual(try parseFloat(f64, "0x10p+10"), 16384.0); + try testing.expectEqual(try parseFloat(f64, "0x10p-10"), 0.015625); + // Max normalized value. + try testing.expectEqual(try parseFloat(f64, "0x1.fffffffffffffp+1023"), math.floatMax(f64)); + try testing.expectEqual(try parseFloat(f64, "-0x1.fffffffffffffp1023"), -math.floatMax(f64)); + // Min normalized value. + try testing.expectEqual(try parseFloat(f64, "0x1p-1022"), math.floatMin(f64)); + try testing.expectEqual(try parseFloat(f64, "-0x1p-1022"), -math.floatMin(f64)); + // Min denormalized value. + //try testing.expectEqual(try parseFloat(f64, "0x1p-1074"), math.floatTrueMin(f64)); + try testing.expectEqual(try parseFloat(f64, "-0x1p-1074"), -math.floatTrueMin(f64)); +} +test "fmt.parseFloat hex.f128" { + try testing.expectEqual(try parseFloat(f128, "0x1p0"), 1.0); + try testing.expectEqual(try parseFloat(f128, "-0x1p-1"), -0.5); + try testing.expectEqual(try parseFloat(f128, "0x10p+10"), 16384.0); + try testing.expectEqual(try parseFloat(f128, "0x10p-10"), 0.015625); + // Max normalized value. + try testing.expectEqual(try parseFloat(f128, "0xf.fffffffffffffffffffffffffff8p+16380"), math.floatMax(f128)); + try testing.expectEqual(try parseFloat(f128, "-0xf.fffffffffffffffffffffffffff8p+16380"), -math.floatMax(f128)); + // Min normalized value. + try testing.expectEqual(try parseFloat(f128, "0x1p-16382"), math.floatMin(f128)); + try testing.expectEqual(try parseFloat(f128, "-0x1p-16382"), -math.floatMin(f128)); + // // Min denormalized value. + try testing.expectEqual(try parseFloat(f128, "0x1p-16494"), math.floatTrueMin(f128)); + try testing.expectEqual(try parseFloat(f128, "-0x1p-16494"), -math.floatTrueMin(f128)); + + // NOTE: We are performing round-to-even. Previous behavior was round-up. + // try testing.expectEqual(try parseFloat(f128, "0x1.edcb34a235253948765432134674fp-1"), 0x1.edcb34a235253948765432134674fp-1); +} diff --git a/lib/std/fmt/parse_float/FloatInfo.zig b/lib/std/fmt/parse_float/FloatInfo.zig new file mode 100644 index 0000000000..8fd95963b1 --- /dev/null +++ b/lib/std/fmt/parse_float/FloatInfo.zig @@ -0,0 +1,131 @@ +const std = @import("std"); +const Self = @This(); + +// Minimum exponent that for a fast path case, or `-⌊(MANTISSA_EXPLICIT_BITS+1)/log2(5)⌋` +min_exponent_fast_path: comptime_int, + +// Maximum exponent that for a fast path case, or `⌊(MANTISSA_EXPLICIT_BITS+1)/log2(5)⌋` +max_exponent_fast_path: comptime_int, + +// Maximum exponent that can be represented for a disguised-fast path case. +// This is `MAX_EXPONENT_FAST_PATH + ⌊(MANTISSA_EXPLICIT_BITS+1)/log2(10)⌋` +max_exponent_fast_path_disguised: comptime_int, + +// Maximum mantissa for the fast-path (`1 << 53` for f64). +max_mantissa_fast_path: comptime_int, + +// Smallest decimal exponent for a non-zero value. Including subnormals. +smallest_power_of_ten: comptime_int, + +// Largest decimal exponent for a non-infinite value. +largest_power_of_ten: comptime_int, + +// The number of bits in the significand, *excluding* the hidden bit. +mantissa_explicit_bits: comptime_int, + +// Minimum exponent value `-(1 << (EXP_BITS - 1)) + 1`. +minimum_exponent: comptime_int, + +// Round-to-even only happens for negative values of q +// when q ≥ −4 in the 64-bit case and when q ≥ −17 in +// the 32-bitcase. +// +// When q ≥ 0,we have that 5^q ≤ 2m+1. In the 64-bit case,we +// have 5^q ≤ 2m+1 ≤ 2^54 or q ≤ 23. In the 32-bit case,we have +// 5^q ≤ 2m+1 ≤ 2^25 or q ≤ 10. +// +// When q < 0, we have w ≥ (2m+1)×5^−q. We must have that w < 2^64 +// so (2m+1)×5^−q < 2^64. We have that 2m+1 > 2^53 (64-bit case) +// or 2m+1 > 2^24 (32-bit case). Hence,we must have 2^53×5^−q < 2^64 +// (64-bit) and 2^24×5^−q < 2^64 (32-bit). Hence we have 5^−q < 2^11 +// or q ≥ −4 (64-bit case) and 5^−q < 2^40 or q ≥ −17 (32-bitcase). +// +// Thus we have that we only need to round ties to even when +// we have that q ∈ [−4,23](in the 64-bit case) or q∈[−17,10] +// (in the 32-bit case). In both cases,the power of five(5^|q|) +// fits in a 64-bit word. +min_exponent_round_to_even: comptime_int, +max_exponent_round_to_even: comptime_int, + +// Largest exponent value `(1 << EXP_BITS) - 1`. +infinite_power: comptime_int, + +// Following should compute based on derived calculations where possible. +pub fn from(comptime T: type) Self { + return switch (T) { + f16 => .{ + // Fast-Path + .min_exponent_fast_path = -4, + .max_exponent_fast_path = 4, + .max_exponent_fast_path_disguised = 7, + .max_mantissa_fast_path = 2 << std.math.floatMantissaBits(T), + // Slow + Eisel-Lemire + .mantissa_explicit_bits = std.math.floatMantissaBits(T), + .infinite_power = 0x1f, + // Eisel-Lemire + .smallest_power_of_ten = -26, // TODO: refine, fails one test + .largest_power_of_ten = 4, + .minimum_exponent = -15, + // w >= (2m+1) * 5^-q and w < 2^64 + // => 2m+1 > 2^11 + // => 2^11*5^-q < 2^64 + // => 5^-q < 2^53 + // => q >= -23 + .min_exponent_round_to_even = -22, + .max_exponent_round_to_even = 5, + }, + f32 => .{ + // Fast-Path + .min_exponent_fast_path = -10, + .max_exponent_fast_path = 10, + .max_exponent_fast_path_disguised = 17, + .max_mantissa_fast_path = 2 << std.math.floatMantissaBits(T), + // Slow + Eisel-Lemire + .mantissa_explicit_bits = std.math.floatMantissaBits(T), + .infinite_power = 0xff, + // Eisel-Lemire + .smallest_power_of_ten = -65, + .largest_power_of_ten = 38, + .minimum_exponent = -127, + .min_exponent_round_to_even = -17, + .max_exponent_round_to_even = 10, + }, + f64 => .{ + // Fast-Path + .min_exponent_fast_path = -22, + .max_exponent_fast_path = 22, + .max_exponent_fast_path_disguised = 37, + .max_mantissa_fast_path = 2 << std.math.floatMantissaBits(T), + // Slow + Eisel-Lemire + .mantissa_explicit_bits = std.math.floatMantissaBits(T), + .infinite_power = 0x7ff, + // Eisel-Lemire + .smallest_power_of_ten = -342, + .largest_power_of_ten = 308, + .minimum_exponent = -1023, + .min_exponent_round_to_even = -4, + .max_exponent_round_to_even = 23, + }, + f128 => .{ + // Fast-Path + .min_exponent_fast_path = -48, + .max_exponent_fast_path = 48, + .max_exponent_fast_path_disguised = 82, + .max_mantissa_fast_path = 2 << std.math.floatMantissaBits(T), + // Slow + Eisel-Lemire + .mantissa_explicit_bits = std.math.floatMantissaBits(T), + .infinite_power = 0x7fff, + // Eisel-Lemire. + // NOTE: Not yet tested (no f128 eisel-lemire implementation) + .smallest_power_of_ten = -4966, + .largest_power_of_ten = 4932, + .minimum_exponent = -16382, + // 2^113 * 5^-q < 2^128 + // 5^-q < 2^15 + // => q >= -6 + .min_exponent_round_to_even = -6, + .max_exponent_round_to_even = 49, + }, + else => unreachable, + }; +} diff --git a/lib/std/fmt/parse_float/FloatStream.zig b/lib/std/fmt/parse_float/FloatStream.zig new file mode 100644 index 0000000000..803ac65e6d --- /dev/null +++ b/lib/std/fmt/parse_float/FloatStream.zig @@ -0,0 +1,137 @@ +//! A wrapper over a byte-slice, providing useful methods for parsing string floating point values. + +const std = @import("std"); +const FloatStream = @This(); +const common = @import("common.zig"); + +slice: []const u8, +offset: usize, +underscore_count: usize, + +pub fn init(s: []const u8) FloatStream { + return .{ .slice = s, .offset = 0, .underscore_count = 0 }; +} + +// Returns the offset from the start *excluding* any underscores that were found. +pub fn offsetTrue(self: FloatStream) usize { + return self.offset - self.underscore_count; +} + +pub fn reset(self: *FloatStream) void { + self.offset = 0; + self.underscore_count = 0; +} + +pub fn len(self: FloatStream) usize { + if (self.offset > self.slice.len) { + return 0; + } + return self.slice.len - self.offset; +} + +pub fn hasLen(self: FloatStream, n: usize) bool { + return self.offset + n <= self.slice.len; +} + +pub fn firstUnchecked(self: FloatStream) u8 { + return self.slice[self.offset]; +} + +pub fn first(self: FloatStream) ?u8 { + return if (self.hasLen(1)) + return self.firstUnchecked() + else + null; +} + +pub fn isEmpty(self: FloatStream) bool { + return !self.hasLen(1); +} + +pub fn firstIs(self: FloatStream, c: u8) bool { + if (self.first()) |ok| { + return ok == c; + } + return false; +} + +pub fn firstIsLower(self: FloatStream, c: u8) bool { + if (self.first()) |ok| { + return ok | 0x20 == c; + } + return false; +} + +pub fn firstIs2(self: FloatStream, c1: u8, c2: u8) bool { + if (self.first()) |ok| { + return ok == c1 or ok == c2; + } + return false; +} + +pub fn firstIs3(self: FloatStream, c1: u8, c2: u8, c3: u8) bool { + if (self.first()) |ok| { + return ok == c1 or ok == c2 or ok == c3; + } + return false; +} + +pub fn firstIsDigit(self: FloatStream, comptime base: u8) bool { + comptime std.debug.assert(base == 10 or base == 16); + + if (self.first()) |ok| { + return common.isDigit(ok, base); + } + return false; +} + +pub fn advance(self: *FloatStream, n: usize) void { + self.offset += n; +} + +pub fn skipChars(self: *FloatStream, c: u8) void { + while (self.firstIs(c)) : (self.advance(1)) {} +} + +pub fn skipChars2(self: *FloatStream, c1: u8, c2: u8) void { + while (self.firstIs2(c1, c2)) : (self.advance(1)) {} +} + +pub fn readU64Unchecked(self: FloatStream) u64 { + return std.mem.readIntSliceLittle(u64, self.slice[self.offset..]); +} + +pub fn readU64(self: FloatStream) ?u64 { + if (self.hasLen(8)) { + return self.readU64Unchecked(); + } + return null; +} + +pub fn atUnchecked(self: *FloatStream, i: usize) u8 { + return self.slice[self.offset + i]; +} + +pub fn scanDigit(self: *FloatStream, comptime base: u8) ?u8 { + comptime std.debug.assert(base == 10 or base == 16); + + retry: while (true) { + if (self.first()) |ok| { + if ('0' <= ok and ok <= '9') { + self.advance(1); + return ok - '0'; + } else if (base == 16 and 'a' <= ok and ok <= 'f') { + self.advance(1); + return ok - 'a' + 10; + } else if (base == 16 and 'A' <= ok and ok <= 'F') { + self.advance(1); + return ok - 'A' + 10; + } else if (ok == '_') { + self.advance(1); + self.underscore_count += 1; + continue :retry; + } + } + return null; + } +} diff --git a/lib/std/fmt/parse_float/common.zig b/lib/std/fmt/parse_float/common.zig new file mode 100644 index 0000000000..c1b34a081b --- /dev/null +++ b/lib/std/fmt/parse_float/common.zig @@ -0,0 +1,91 @@ +const std = @import("std"); + +/// A custom N-bit floating point type, representing `f * 2^e`. +/// e is biased, so it be directly shifted into the exponent bits. +/// Negative exponent indicates an invalid result. +pub fn BiasedFp(comptime T: type) type { + const MantissaT = mantissaType(T); + + return struct { + const Self = @This(); + + /// The significant digits. + f: MantissaT, + /// The biased, binary exponent. + e: i32, + + pub fn zero() Self { + return .{ .f = 0, .e = 0 }; + } + + pub fn zeroPow2(e: i32) Self { + return .{ .f = 0, .e = e }; + } + + pub fn inf(comptime FloatT: type) Self { + return .{ .f = 0, .e = (1 << std.math.floatExponentBits(FloatT)) - 1 }; + } + + pub fn eql(self: Self, other: Self) bool { + return self.f == other.f and self.e == other.e; + } + + pub fn toFloat(self: Self, comptime FloatT: type, negative: bool) FloatT { + var word = self.f; + word |= @intCast(MantissaT, self.e) << std.math.floatMantissaBits(FloatT); + var f = floatFromUnsigned(FloatT, MantissaT, word); + if (negative) f = -f; + return f; + } + }; +} + +pub fn floatFromUnsigned(comptime T: type, comptime MantissaT: type, v: MantissaT) T { + return switch (T) { + f16 => @bitCast(f16, @truncate(u16, v)), + f32 => @bitCast(f32, @truncate(u32, v)), + f64 => @bitCast(f64, @truncate(u64, v)), + f128 => @bitCast(f128, v), + else => unreachable, + }; +} + +/// Represents a parsed floating point value as its components. +pub fn Number(comptime T: type) type { + return struct { + exponent: i64, + mantissa: mantissaType(T), + negative: bool, + /// More than max_mantissa digits were found during parse + many_digits: bool, + /// The number was a hex-float (e.g. 0x1.234p567) + hex: bool, + }; +} + +/// Determine if 8 bytes are all decimal digits. +/// This does not care about the order in which the bytes were loaded. +pub fn isEightDigits(v: u64) bool { + const a = v +% 0x4646_4646_4646_4646; + const b = v -% 0x3030_3030_3030_3030; + return ((a | b) & 0x8080_8080_8080_8080) == 0; +} + +pub fn isDigit(c: u8, comptime base: u8) bool { + std.debug.assert(base == 10 or base == 16); + + return if (base == 10) + '0' <= c and c <= '9' + else + '0' <= c and c <= '9' or 'a' <= c and c <= 'f' or 'A' <= c and c <= 'F'; +} + +/// Returns the underlying storage type used for the mantissa of floating-point type. +/// The output unsigned type must have at least as many bits as the input floating-point type. +pub fn mantissaType(comptime T: type) type { + return switch (T) { + f16, f32, f64 => u64, + f128 => u128, + else => unreachable, + }; +} diff --git a/lib/std/fmt/parse_float/convert_eisel_lemire.zig b/lib/std/fmt/parse_float/convert_eisel_lemire.zig new file mode 100644 index 0000000000..724be45181 --- /dev/null +++ b/lib/std/fmt/parse_float/convert_eisel_lemire.zig @@ -0,0 +1,843 @@ +const std = @import("std"); +const math = std.math; +const common = @import("common.zig"); +const FloatInfo = @import("FloatInfo.zig"); +const BiasedFp = common.BiasedFp; +const Number = common.Number; + +/// Compute a float using an extended-precision representation. +/// +/// Fast conversion of a the significant digits and decimal exponent +/// a float to an extended representation with a binary float. This +/// algorithm will accurately parse the vast majority of cases, +/// and uses a 128-bit representation (with a fallback 192-bit +/// representation). +/// +/// This algorithm scales the exponent by the decimal exponent +/// using pre-computed powers-of-5, and calculates if the +/// representation can be unambiguously rounded to the nearest +/// machine float. Near-halfway cases are not handled here, +/// and are represented by a negative, biased binary exponent. +/// +/// The algorithm is described in detail in "Daniel Lemire, Number Parsing +/// at a Gigabyte per Second" in section 5, "Fast Algorithm", and +/// section 6, "Exact Numbers And Ties", available online: +/// . +pub fn convertEiselLemire(comptime T: type, q: i64, w_: u64) ?BiasedFp(f64) { + std.debug.assert(T == f16 or T == f32 or T == f64); + var w = w_; + const float_info = FloatInfo.from(T); + + // Short-circuit if the value can only be a literal 0 or infinity. + if (w == 0 or q < float_info.smallest_power_of_ten) { + return BiasedFp(f64).zero(); + } else if (q > float_info.largest_power_of_ten) { + return BiasedFp(f64).inf(T); + } + + // Normalize our significant digits, so the most-significant bit is set. + const lz = @clz(u64, @bitCast(u64, w)); + w = math.shl(u64, w, lz); + + const r = computeProductApprox(q, w, float_info.mantissa_explicit_bits + 3); + if (r.lo == 0xffff_ffff_ffff_ffff) { + // If we have failed to approximate w x 5^-q with our 128-bit value. + // Since the addition of 1 could lead to an overflow which could then + // round up over the half-way point, this can lead to improper rounding + // of a float. + // + // However, this can only occur if q ∈ [-27, 55]. The upper bound of q + // is 55 because 5^55 < 2^128, however, this can only happen if 5^q > 2^64, + // since otherwise the product can be represented in 64-bits, producing + // an exact result. For negative exponents, rounding-to-even can + // only occur if 5^-q < 2^64. + // + // For detailed explanations of rounding for negative exponents, see + // . For detailed + // explanations of rounding for positive exponents, see + // . + const inside_safe_exponent = q >= -27 and q <= 55; + if (!inside_safe_exponent) { + return null; + } + } + + const upper_bit = @intCast(i32, r.hi >> 63); + var mantissa = math.shr(u64, r.hi, upper_bit + 64 - @intCast(i32, float_info.mantissa_explicit_bits) - 3); + var power2 = power(@intCast(i32, q)) + upper_bit - @intCast(i32, lz) - float_info.minimum_exponent; + if (power2 <= 0) { + if (-power2 + 1 >= 64) { + // Have more than 64 bits below the minimum exponent, must be 0. + return BiasedFp(f64).zero(); + } + // Have a subnormal value. + mantissa = math.shr(u64, mantissa, -power2 + 1); + mantissa += mantissa & 1; + mantissa >>= 1; + power2 = @boolToInt(mantissa >= (1 << float_info.mantissa_explicit_bits)); + return BiasedFp(f64){ .f = mantissa, .e = power2 }; + } + + // Need to handle rounding ties. Normally, we need to round up, + // but if we fall right in between and and we have an even basis, we + // need to round down. + // + // This will only occur if: + // 1. The lower 64 bits of the 128-bit representation is 0. + // IE, 5^q fits in single 64-bit word. + // 2. The least-significant bit prior to truncated mantissa is odd. + // 3. All the bits truncated when shifting to mantissa bits + 1 are 0. + // + // Or, we may fall between two floats: we are exactly halfway. + if (r.lo <= 1 and + q >= float_info.min_exponent_round_to_even and + q <= float_info.max_exponent_round_to_even and + mantissa & 3 == 1 and + math.shl(u64, mantissa, (upper_bit + 64 - @intCast(i32, float_info.mantissa_explicit_bits) - 3)) == r.hi) + { + // Zero the lowest bit, so we don't round up. + mantissa &= ~@as(u64, 1); + } + + // Round-to-even, then shift the significant digits into place. + mantissa += mantissa & 1; + mantissa >>= 1; + if (mantissa >= 2 << float_info.mantissa_explicit_bits) { + // Rounding up overflowed, so the carry bit is set. Set the + // mantissa to 1 (only the implicit, hidden bit is set) and + // increase the exponent. + mantissa = 1 << float_info.mantissa_explicit_bits; + power2 += 1; + } + + // Zero out the hidden bit + mantissa &= ~(@as(u64, 1) << float_info.mantissa_explicit_bits); + if (power2 >= float_info.infinite_power) { + // Exponent is above largest normal value, must be infinite + return BiasedFp(f64).inf(T); + } + + return BiasedFp(f64){ .f = mantissa, .e = power2 }; +} + +/// Calculate a base 2 exponent from a decimal exponent. +/// This uses a pre-computed integer approximation for +/// log2(10), where 217706 / 2^16 is accurate for the +/// entire range of non-finite decimal exponents. +fn power(q: i32) i32 { + return ((q *% (152170 + 65536)) >> 16) + 63; +} + +const U128 = struct { + lo: u64, + hi: u64, + + pub fn new(lo: u64, hi: u64) U128 { + return .{ .lo = lo, .hi = hi }; + } + + pub fn mul(a: u64, b: u64) U128 { + const x = @as(u128, a) * b; + return .{ + .hi = @truncate(u64, x >> 64), + .lo = @truncate(u64, x), + }; + } +}; + +// This will compute or rather approximate w * 5**q and return a pair of 64-bit words +// approximating the result, with the "high" part corresponding to the most significant +// bits and the low part corresponding to the least significant bits. +fn computeProductApprox(q: i64, w: u64, comptime precision: usize) U128 { + std.debug.assert(q >= eisel_lemire_smallest_power_of_five); + std.debug.assert(q <= eisel_lemire_largest_power_of_five); + std.debug.assert(precision <= 64); + + const mask = if (precision < 64) + 0xffff_ffff_ffff_ffff >> precision + else + 0xffff_ffff_ffff_ffff; + + // 5^q < 2^64, then the multiplication always provides an exact value. + // That means whenever we need to round ties to even, we always have + // an exact value. + const index = @intCast(usize, q - @intCast(i64, eisel_lemire_smallest_power_of_five)); + const pow5 = eisel_lemire_table_powers_of_five_128[index]; + + // Only need one multiplication as long as there is 1 zero but + // in the explicit mantissa bits, +1 for the hidden bit, +1 to + // determine the rounding direction, +1 for if the computed + // product has a leading zero. + var first = U128.mul(w, pow5.lo); + if (first.hi & mask == mask) { + // Need to do a second multiplication to get better precision + // for the lower product. This will always be exact + // where q is < 55, since 5^55 < 2^128. If this wraps, + // then we need to need to round up the hi product. + const second = U128.mul(w, pow5.hi); + + first.lo +%= second.hi; + if (second.hi > first.lo) { + first.hi += 1; + } + } + + return .{ .lo = first.lo, .hi = first.hi }; +} + +// Eisel-Lemire tables ~10Kb +const eisel_lemire_smallest_power_of_five = -342; +const eisel_lemire_largest_power_of_five = 308; +const eisel_lemire_table_powers_of_five_128 = [_]U128{ + U128.new(0xeef453d6923bd65a, 0x113faa2906a13b3f), // 5^-342 + U128.new(0x9558b4661b6565f8, 0x4ac7ca59a424c507), // 5^-341 + U128.new(0xbaaee17fa23ebf76, 0x5d79bcf00d2df649), // 5^-340 + U128.new(0xe95a99df8ace6f53, 0xf4d82c2c107973dc), // 5^-339 + U128.new(0x91d8a02bb6c10594, 0x79071b9b8a4be869), // 5^-338 + U128.new(0xb64ec836a47146f9, 0x9748e2826cdee284), // 5^-337 + U128.new(0xe3e27a444d8d98b7, 0xfd1b1b2308169b25), // 5^-336 + U128.new(0x8e6d8c6ab0787f72, 0xfe30f0f5e50e20f7), // 5^-335 + U128.new(0xb208ef855c969f4f, 0xbdbd2d335e51a935), // 5^-334 + U128.new(0xde8b2b66b3bc4723, 0xad2c788035e61382), // 5^-333 + U128.new(0x8b16fb203055ac76, 0x4c3bcb5021afcc31), // 5^-332 + U128.new(0xaddcb9e83c6b1793, 0xdf4abe242a1bbf3d), // 5^-331 + U128.new(0xd953e8624b85dd78, 0xd71d6dad34a2af0d), // 5^-330 + U128.new(0x87d4713d6f33aa6b, 0x8672648c40e5ad68), // 5^-329 + U128.new(0xa9c98d8ccb009506, 0x680efdaf511f18c2), // 5^-328 + U128.new(0xd43bf0effdc0ba48, 0x212bd1b2566def2), // 5^-327 + U128.new(0x84a57695fe98746d, 0x14bb630f7604b57), // 5^-326 + U128.new(0xa5ced43b7e3e9188, 0x419ea3bd35385e2d), // 5^-325 + U128.new(0xcf42894a5dce35ea, 0x52064cac828675b9), // 5^-324 + U128.new(0x818995ce7aa0e1b2, 0x7343efebd1940993), // 5^-323 + U128.new(0xa1ebfb4219491a1f, 0x1014ebe6c5f90bf8), // 5^-322 + U128.new(0xca66fa129f9b60a6, 0xd41a26e077774ef6), // 5^-321 + U128.new(0xfd00b897478238d0, 0x8920b098955522b4), // 5^-320 + U128.new(0x9e20735e8cb16382, 0x55b46e5f5d5535b0), // 5^-319 + U128.new(0xc5a890362fddbc62, 0xeb2189f734aa831d), // 5^-318 + U128.new(0xf712b443bbd52b7b, 0xa5e9ec7501d523e4), // 5^-317 + U128.new(0x9a6bb0aa55653b2d, 0x47b233c92125366e), // 5^-316 + U128.new(0xc1069cd4eabe89f8, 0x999ec0bb696e840a), // 5^-315 + U128.new(0xf148440a256e2c76, 0xc00670ea43ca250d), // 5^-314 + U128.new(0x96cd2a865764dbca, 0x380406926a5e5728), // 5^-313 + U128.new(0xbc807527ed3e12bc, 0xc605083704f5ecf2), // 5^-312 + U128.new(0xeba09271e88d976b, 0xf7864a44c633682e), // 5^-311 + U128.new(0x93445b8731587ea3, 0x7ab3ee6afbe0211d), // 5^-310 + U128.new(0xb8157268fdae9e4c, 0x5960ea05bad82964), // 5^-309 + U128.new(0xe61acf033d1a45df, 0x6fb92487298e33bd), // 5^-308 + U128.new(0x8fd0c16206306bab, 0xa5d3b6d479f8e056), // 5^-307 + U128.new(0xb3c4f1ba87bc8696, 0x8f48a4899877186c), // 5^-306 + U128.new(0xe0b62e2929aba83c, 0x331acdabfe94de87), // 5^-305 + U128.new(0x8c71dcd9ba0b4925, 0x9ff0c08b7f1d0b14), // 5^-304 + U128.new(0xaf8e5410288e1b6f, 0x7ecf0ae5ee44dd9), // 5^-303 + U128.new(0xdb71e91432b1a24a, 0xc9e82cd9f69d6150), // 5^-302 + U128.new(0x892731ac9faf056e, 0xbe311c083a225cd2), // 5^-301 + U128.new(0xab70fe17c79ac6ca, 0x6dbd630a48aaf406), // 5^-300 + U128.new(0xd64d3d9db981787d, 0x92cbbccdad5b108), // 5^-299 + U128.new(0x85f0468293f0eb4e, 0x25bbf56008c58ea5), // 5^-298 + U128.new(0xa76c582338ed2621, 0xaf2af2b80af6f24e), // 5^-297 + U128.new(0xd1476e2c07286faa, 0x1af5af660db4aee1), // 5^-296 + U128.new(0x82cca4db847945ca, 0x50d98d9fc890ed4d), // 5^-295 + U128.new(0xa37fce126597973c, 0xe50ff107bab528a0), // 5^-294 + U128.new(0xcc5fc196fefd7d0c, 0x1e53ed49a96272c8), // 5^-293 + U128.new(0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7a), // 5^-292 + U128.new(0x9faacf3df73609b1, 0x77b191618c54e9ac), // 5^-291 + U128.new(0xc795830d75038c1d, 0xd59df5b9ef6a2417), // 5^-290 + U128.new(0xf97ae3d0d2446f25, 0x4b0573286b44ad1d), // 5^-289 + U128.new(0x9becce62836ac577, 0x4ee367f9430aec32), // 5^-288 + U128.new(0xc2e801fb244576d5, 0x229c41f793cda73f), // 5^-287 + U128.new(0xf3a20279ed56d48a, 0x6b43527578c1110f), // 5^-286 + U128.new(0x9845418c345644d6, 0x830a13896b78aaa9), // 5^-285 + U128.new(0xbe5691ef416bd60c, 0x23cc986bc656d553), // 5^-284 + U128.new(0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa8), // 5^-283 + U128.new(0x94b3a202eb1c3f39, 0x7bf7d71432f3d6a9), // 5^-282 + U128.new(0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc53), // 5^-281 + U128.new(0xe858ad248f5c22c9, 0xd1b3400f8f9cff68), // 5^-280 + U128.new(0x91376c36d99995be, 0x23100809b9c21fa1), // 5^-279 + U128.new(0xb58547448ffffb2d, 0xabd40a0c2832a78a), // 5^-278 + U128.new(0xe2e69915b3fff9f9, 0x16c90c8f323f516c), // 5^-277 + U128.new(0x8dd01fad907ffc3b, 0xae3da7d97f6792e3), // 5^-276 + U128.new(0xb1442798f49ffb4a, 0x99cd11cfdf41779c), // 5^-275 + U128.new(0xdd95317f31c7fa1d, 0x40405643d711d583), // 5^-274 + U128.new(0x8a7d3eef7f1cfc52, 0x482835ea666b2572), // 5^-273 + U128.new(0xad1c8eab5ee43b66, 0xda3243650005eecf), // 5^-272 + U128.new(0xd863b256369d4a40, 0x90bed43e40076a82), // 5^-271 + U128.new(0x873e4f75e2224e68, 0x5a7744a6e804a291), // 5^-270 + U128.new(0xa90de3535aaae202, 0x711515d0a205cb36), // 5^-269 + U128.new(0xd3515c2831559a83, 0xd5a5b44ca873e03), // 5^-268 + U128.new(0x8412d9991ed58091, 0xe858790afe9486c2), // 5^-267 + U128.new(0xa5178fff668ae0b6, 0x626e974dbe39a872), // 5^-266 + U128.new(0xce5d73ff402d98e3, 0xfb0a3d212dc8128f), // 5^-265 + U128.new(0x80fa687f881c7f8e, 0x7ce66634bc9d0b99), // 5^-264 + U128.new(0xa139029f6a239f72, 0x1c1fffc1ebc44e80), // 5^-263 + U128.new(0xc987434744ac874e, 0xa327ffb266b56220), // 5^-262 + U128.new(0xfbe9141915d7a922, 0x4bf1ff9f0062baa8), // 5^-261 + U128.new(0x9d71ac8fada6c9b5, 0x6f773fc3603db4a9), // 5^-260 + U128.new(0xc4ce17b399107c22, 0xcb550fb4384d21d3), // 5^-259 + U128.new(0xf6019da07f549b2b, 0x7e2a53a146606a48), // 5^-258 + U128.new(0x99c102844f94e0fb, 0x2eda7444cbfc426d), // 5^-257 + U128.new(0xc0314325637a1939, 0xfa911155fefb5308), // 5^-256 + U128.new(0xf03d93eebc589f88, 0x793555ab7eba27ca), // 5^-255 + U128.new(0x96267c7535b763b5, 0x4bc1558b2f3458de), // 5^-254 + U128.new(0xbbb01b9283253ca2, 0x9eb1aaedfb016f16), // 5^-253 + U128.new(0xea9c227723ee8bcb, 0x465e15a979c1cadc), // 5^-252 + U128.new(0x92a1958a7675175f, 0xbfacd89ec191ec9), // 5^-251 + U128.new(0xb749faed14125d36, 0xcef980ec671f667b), // 5^-250 + U128.new(0xe51c79a85916f484, 0x82b7e12780e7401a), // 5^-249 + U128.new(0x8f31cc0937ae58d2, 0xd1b2ecb8b0908810), // 5^-248 + U128.new(0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa15), // 5^-247 + U128.new(0xdfbdcece67006ac9, 0x67a791e093e1d49a), // 5^-246 + U128.new(0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e0), // 5^-245 + U128.new(0xaecc49914078536d, 0x58fae9f773886e18), // 5^-244 + U128.new(0xda7f5bf590966848, 0xaf39a475506a899e), // 5^-243 + U128.new(0x888f99797a5e012d, 0x6d8406c952429603), // 5^-242 + U128.new(0xaab37fd7d8f58178, 0xc8e5087ba6d33b83), // 5^-241 + U128.new(0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a64), // 5^-240 + U128.new(0x855c3be0a17fcd26, 0x5cf2eea09a55067f), // 5^-239 + U128.new(0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481e), // 5^-238 + U128.new(0xd0601d8efc57b08b, 0xf13b94daf124da26), // 5^-237 + U128.new(0x823c12795db6ce57, 0x76c53d08d6b70858), // 5^-236 + U128.new(0xa2cb1717b52481ed, 0x54768c4b0c64ca6e), // 5^-235 + U128.new(0xcb7ddcdda26da268, 0xa9942f5dcf7dfd09), // 5^-234 + U128.new(0xfe5d54150b090b02, 0xd3f93b35435d7c4c), // 5^-233 + U128.new(0x9efa548d26e5a6e1, 0xc47bc5014a1a6daf), // 5^-232 + U128.new(0xc6b8e9b0709f109a, 0x359ab6419ca1091b), // 5^-231 + U128.new(0xf867241c8cc6d4c0, 0xc30163d203c94b62), // 5^-230 + U128.new(0x9b407691d7fc44f8, 0x79e0de63425dcf1d), // 5^-229 + U128.new(0xc21094364dfb5636, 0x985915fc12f542e4), // 5^-228 + U128.new(0xf294b943e17a2bc4, 0x3e6f5b7b17b2939d), // 5^-227 + U128.new(0x979cf3ca6cec5b5a, 0xa705992ceecf9c42), // 5^-226 + U128.new(0xbd8430bd08277231, 0x50c6ff782a838353), // 5^-225 + U128.new(0xece53cec4a314ebd, 0xa4f8bf5635246428), // 5^-224 + U128.new(0x940f4613ae5ed136, 0x871b7795e136be99), // 5^-223 + U128.new(0xb913179899f68584, 0x28e2557b59846e3f), // 5^-222 + U128.new(0xe757dd7ec07426e5, 0x331aeada2fe589cf), // 5^-221 + U128.new(0x9096ea6f3848984f, 0x3ff0d2c85def7621), // 5^-220 + U128.new(0xb4bca50b065abe63, 0xfed077a756b53a9), // 5^-219 + U128.new(0xe1ebce4dc7f16dfb, 0xd3e8495912c62894), // 5^-218 + U128.new(0x8d3360f09cf6e4bd, 0x64712dd7abbbd95c), // 5^-217 + U128.new(0xb080392cc4349dec, 0xbd8d794d96aacfb3), // 5^-216 + U128.new(0xdca04777f541c567, 0xecf0d7a0fc5583a0), // 5^-215 + U128.new(0x89e42caaf9491b60, 0xf41686c49db57244), // 5^-214 + U128.new(0xac5d37d5b79b6239, 0x311c2875c522ced5), // 5^-213 + U128.new(0xd77485cb25823ac7, 0x7d633293366b828b), // 5^-212 + U128.new(0x86a8d39ef77164bc, 0xae5dff9c02033197), // 5^-211 + U128.new(0xa8530886b54dbdeb, 0xd9f57f830283fdfc), // 5^-210 + U128.new(0xd267caa862a12d66, 0xd072df63c324fd7b), // 5^-209 + U128.new(0x8380dea93da4bc60, 0x4247cb9e59f71e6d), // 5^-208 + U128.new(0xa46116538d0deb78, 0x52d9be85f074e608), // 5^-207 + U128.new(0xcd795be870516656, 0x67902e276c921f8b), // 5^-206 + U128.new(0x806bd9714632dff6, 0xba1cd8a3db53b6), // 5^-205 + U128.new(0xa086cfcd97bf97f3, 0x80e8a40eccd228a4), // 5^-204 + U128.new(0xc8a883c0fdaf7df0, 0x6122cd128006b2cd), // 5^-203 + U128.new(0xfad2a4b13d1b5d6c, 0x796b805720085f81), // 5^-202 + U128.new(0x9cc3a6eec6311a63, 0xcbe3303674053bb0), // 5^-201 + U128.new(0xc3f490aa77bd60fc, 0xbedbfc4411068a9c), // 5^-200 + U128.new(0xf4f1b4d515acb93b, 0xee92fb5515482d44), // 5^-199 + U128.new(0x991711052d8bf3c5, 0x751bdd152d4d1c4a), // 5^-198 + U128.new(0xbf5cd54678eef0b6, 0xd262d45a78a0635d), // 5^-197 + U128.new(0xef340a98172aace4, 0x86fb897116c87c34), // 5^-196 + U128.new(0x9580869f0e7aac0e, 0xd45d35e6ae3d4da0), // 5^-195 + U128.new(0xbae0a846d2195712, 0x8974836059cca109), // 5^-194 + U128.new(0xe998d258869facd7, 0x2bd1a438703fc94b), // 5^-193 + U128.new(0x91ff83775423cc06, 0x7b6306a34627ddcf), // 5^-192 + U128.new(0xb67f6455292cbf08, 0x1a3bc84c17b1d542), // 5^-191 + U128.new(0xe41f3d6a7377eeca, 0x20caba5f1d9e4a93), // 5^-190 + U128.new(0x8e938662882af53e, 0x547eb47b7282ee9c), // 5^-189 + U128.new(0xb23867fb2a35b28d, 0xe99e619a4f23aa43), // 5^-188 + U128.new(0xdec681f9f4c31f31, 0x6405fa00e2ec94d4), // 5^-187 + U128.new(0x8b3c113c38f9f37e, 0xde83bc408dd3dd04), // 5^-186 + U128.new(0xae0b158b4738705e, 0x9624ab50b148d445), // 5^-185 + U128.new(0xd98ddaee19068c76, 0x3badd624dd9b0957), // 5^-184 + U128.new(0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d6), // 5^-183 + U128.new(0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4c), // 5^-182 + U128.new(0xd47487cc8470652b, 0x7647c3200069671f), // 5^-181 + U128.new(0x84c8d4dfd2c63f3b, 0x29ecd9f40041e073), // 5^-180 + U128.new(0xa5fb0a17c777cf09, 0xf468107100525890), // 5^-179 + U128.new(0xcf79cc9db955c2cc, 0x7182148d4066eeb4), // 5^-178 + U128.new(0x81ac1fe293d599bf, 0xc6f14cd848405530), // 5^-177 + U128.new(0xa21727db38cb002f, 0xb8ada00e5a506a7c), // 5^-176 + U128.new(0xca9cf1d206fdc03b, 0xa6d90811f0e4851c), // 5^-175 + U128.new(0xfd442e4688bd304a, 0x908f4a166d1da663), // 5^-174 + U128.new(0x9e4a9cec15763e2e, 0x9a598e4e043287fe), // 5^-173 + U128.new(0xc5dd44271ad3cdba, 0x40eff1e1853f29fd), // 5^-172 + U128.new(0xf7549530e188c128, 0xd12bee59e68ef47c), // 5^-171 + U128.new(0x9a94dd3e8cf578b9, 0x82bb74f8301958ce), // 5^-170 + U128.new(0xc13a148e3032d6e7, 0xe36a52363c1faf01), // 5^-169 + U128.new(0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac1), // 5^-168 + U128.new(0x96f5600f15a7b7e5, 0x29ab103a5ef8c0b9), // 5^-167 + U128.new(0xbcb2b812db11a5de, 0x7415d448f6b6f0e7), // 5^-166 + U128.new(0xebdf661791d60f56, 0x111b495b3464ad21), // 5^-165 + U128.new(0x936b9fcebb25c995, 0xcab10dd900beec34), // 5^-164 + U128.new(0xb84687c269ef3bfb, 0x3d5d514f40eea742), // 5^-163 + U128.new(0xe65829b3046b0afa, 0xcb4a5a3112a5112), // 5^-162 + U128.new(0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ab), // 5^-161 + U128.new(0xb3f4e093db73a093, 0x59ed216765690f56), // 5^-160 + U128.new(0xe0f218b8d25088b8, 0x306869c13ec3532c), // 5^-159 + U128.new(0x8c974f7383725573, 0x1e414218c73a13fb), // 5^-158 + U128.new(0xafbd2350644eeacf, 0xe5d1929ef90898fa), // 5^-157 + U128.new(0xdbac6c247d62a583, 0xdf45f746b74abf39), // 5^-156 + U128.new(0x894bc396ce5da772, 0x6b8bba8c328eb783), // 5^-155 + U128.new(0xab9eb47c81f5114f, 0x66ea92f3f326564), // 5^-154 + U128.new(0xd686619ba27255a2, 0xc80a537b0efefebd), // 5^-153 + U128.new(0x8613fd0145877585, 0xbd06742ce95f5f36), // 5^-152 + U128.new(0xa798fc4196e952e7, 0x2c48113823b73704), // 5^-151 + U128.new(0xd17f3b51fca3a7a0, 0xf75a15862ca504c5), // 5^-150 + U128.new(0x82ef85133de648c4, 0x9a984d73dbe722fb), // 5^-149 + U128.new(0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebba), // 5^-148 + U128.new(0xcc963fee10b7d1b3, 0x318df905079926a8), // 5^-147 + U128.new(0xffbbcfe994e5c61f, 0xfdf17746497f7052), // 5^-146 + U128.new(0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa633), // 5^-145 + U128.new(0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc0), // 5^-144 + U128.new(0xf9bd690a1b68637b, 0x3dfdce7aa3c673b0), // 5^-143 + U128.new(0x9c1661a651213e2d, 0x6bea10ca65c084e), // 5^-142 + U128.new(0xc31bfa0fe5698db8, 0x486e494fcff30a62), // 5^-141 + U128.new(0xf3e2f893dec3f126, 0x5a89dba3c3efccfa), // 5^-140 + U128.new(0x986ddb5c6b3a76b7, 0xf89629465a75e01c), // 5^-139 + U128.new(0xbe89523386091465, 0xf6bbb397f1135823), // 5^-138 + U128.new(0xee2ba6c0678b597f, 0x746aa07ded582e2c), // 5^-137 + U128.new(0x94db483840b717ef, 0xa8c2a44eb4571cdc), // 5^-136 + U128.new(0xba121a4650e4ddeb, 0x92f34d62616ce413), // 5^-135 + U128.new(0xe896a0d7e51e1566, 0x77b020baf9c81d17), // 5^-134 + U128.new(0x915e2486ef32cd60, 0xace1474dc1d122e), // 5^-133 + U128.new(0xb5b5ada8aaff80b8, 0xd819992132456ba), // 5^-132 + U128.new(0xe3231912d5bf60e6, 0x10e1fff697ed6c69), // 5^-131 + U128.new(0x8df5efabc5979c8f, 0xca8d3ffa1ef463c1), // 5^-130 + U128.new(0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb2), // 5^-129 + U128.new(0xddd0467c64bce4a0, 0xac7cb3f6d05ddbde), // 5^-128 + U128.new(0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96b), // 5^-127 + U128.new(0xad4ab7112eb3929d, 0x86c16c98d2c953c6), // 5^-126 + U128.new(0xd89d64d57a607744, 0xe871c7bf077ba8b7), // 5^-125 + U128.new(0x87625f056c7c4a8b, 0x11471cd764ad4972), // 5^-124 + U128.new(0xa93af6c6c79b5d2d, 0xd598e40d3dd89bcf), // 5^-123 + U128.new(0xd389b47879823479, 0x4aff1d108d4ec2c3), // 5^-122 + U128.new(0x843610cb4bf160cb, 0xcedf722a585139ba), // 5^-121 + U128.new(0xa54394fe1eedb8fe, 0xc2974eb4ee658828), // 5^-120 + U128.new(0xce947a3da6a9273e, 0x733d226229feea32), // 5^-119 + U128.new(0x811ccc668829b887, 0x806357d5a3f525f), // 5^-118 + U128.new(0xa163ff802a3426a8, 0xca07c2dcb0cf26f7), // 5^-117 + U128.new(0xc9bcff6034c13052, 0xfc89b393dd02f0b5), // 5^-116 + U128.new(0xfc2c3f3841f17c67, 0xbbac2078d443ace2), // 5^-115 + U128.new(0x9d9ba7832936edc0, 0xd54b944b84aa4c0d), // 5^-114 + U128.new(0xc5029163f384a931, 0xa9e795e65d4df11), // 5^-113 + U128.new(0xf64335bcf065d37d, 0x4d4617b5ff4a16d5), // 5^-112 + U128.new(0x99ea0196163fa42e, 0x504bced1bf8e4e45), // 5^-111 + U128.new(0xc06481fb9bcf8d39, 0xe45ec2862f71e1d6), // 5^-110 + U128.new(0xf07da27a82c37088, 0x5d767327bb4e5a4c), // 5^-109 + U128.new(0x964e858c91ba2655, 0x3a6a07f8d510f86f), // 5^-108 + U128.new(0xbbe226efb628afea, 0x890489f70a55368b), // 5^-107 + U128.new(0xeadab0aba3b2dbe5, 0x2b45ac74ccea842e), // 5^-106 + U128.new(0x92c8ae6b464fc96f, 0x3b0b8bc90012929d), // 5^-105 + U128.new(0xb77ada0617e3bbcb, 0x9ce6ebb40173744), // 5^-104 + U128.new(0xe55990879ddcaabd, 0xcc420a6a101d0515), // 5^-103 + U128.new(0x8f57fa54c2a9eab6, 0x9fa946824a12232d), // 5^-102 + U128.new(0xb32df8e9f3546564, 0x47939822dc96abf9), // 5^-101 + U128.new(0xdff9772470297ebd, 0x59787e2b93bc56f7), // 5^-100 + U128.new(0x8bfbea76c619ef36, 0x57eb4edb3c55b65a), // 5^-99 + U128.new(0xaefae51477a06b03, 0xede622920b6b23f1), // 5^-98 + U128.new(0xdab99e59958885c4, 0xe95fab368e45eced), // 5^-97 + U128.new(0x88b402f7fd75539b, 0x11dbcb0218ebb414), // 5^-96 + U128.new(0xaae103b5fcd2a881, 0xd652bdc29f26a119), // 5^-95 + U128.new(0xd59944a37c0752a2, 0x4be76d3346f0495f), // 5^-94 + U128.new(0x857fcae62d8493a5, 0x6f70a4400c562ddb), // 5^-93 + U128.new(0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb952), // 5^-92 + U128.new(0xd097ad07a71f26b2, 0x7e2000a41346a7a7), // 5^-91 + U128.new(0x825ecc24c873782f, 0x8ed400668c0c28c8), // 5^-90 + U128.new(0xa2f67f2dfa90563b, 0x728900802f0f32fa), // 5^-89 + U128.new(0xcbb41ef979346bca, 0x4f2b40a03ad2ffb9), // 5^-88 + U128.new(0xfea126b7d78186bc, 0xe2f610c84987bfa8), // 5^-87 + U128.new(0x9f24b832e6b0f436, 0xdd9ca7d2df4d7c9), // 5^-86 + U128.new(0xc6ede63fa05d3143, 0x91503d1c79720dbb), // 5^-85 + U128.new(0xf8a95fcf88747d94, 0x75a44c6397ce912a), // 5^-84 + U128.new(0x9b69dbe1b548ce7c, 0xc986afbe3ee11aba), // 5^-83 + U128.new(0xc24452da229b021b, 0xfbe85badce996168), // 5^-82 + U128.new(0xf2d56790ab41c2a2, 0xfae27299423fb9c3), // 5^-81 + U128.new(0x97c560ba6b0919a5, 0xdccd879fc967d41a), // 5^-80 + U128.new(0xbdb6b8e905cb600f, 0x5400e987bbc1c920), // 5^-79 + U128.new(0xed246723473e3813, 0x290123e9aab23b68), // 5^-78 + U128.new(0x9436c0760c86e30b, 0xf9a0b6720aaf6521), // 5^-77 + U128.new(0xb94470938fa89bce, 0xf808e40e8d5b3e69), // 5^-76 + U128.new(0xe7958cb87392c2c2, 0xb60b1d1230b20e04), // 5^-75 + U128.new(0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c2), // 5^-74 + U128.new(0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af3), // 5^-73 + U128.new(0xe2280b6c20dd5232, 0x25c6da63c38de1b0), // 5^-72 + U128.new(0x8d590723948a535f, 0x579c487e5a38ad0e), // 5^-71 + U128.new(0xb0af48ec79ace837, 0x2d835a9df0c6d851), // 5^-70 + U128.new(0xdcdb1b2798182244, 0xf8e431456cf88e65), // 5^-69 + U128.new(0x8a08f0f8bf0f156b, 0x1b8e9ecb641b58ff), // 5^-68 + U128.new(0xac8b2d36eed2dac5, 0xe272467e3d222f3f), // 5^-67 + U128.new(0xd7adf884aa879177, 0x5b0ed81dcc6abb0f), // 5^-66 + U128.new(0x86ccbb52ea94baea, 0x98e947129fc2b4e9), // 5^-65 + U128.new(0xa87fea27a539e9a5, 0x3f2398d747b36224), // 5^-64 + U128.new(0xd29fe4b18e88640e, 0x8eec7f0d19a03aad), // 5^-63 + U128.new(0x83a3eeeef9153e89, 0x1953cf68300424ac), // 5^-62 + U128.new(0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd7), // 5^-61 + U128.new(0xcdb02555653131b6, 0x3792f412cb06794d), // 5^-60 + U128.new(0x808e17555f3ebf11, 0xe2bbd88bbee40bd0), // 5^-59 + U128.new(0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec4), // 5^-58 + U128.new(0xc8de047564d20a8b, 0xf245825a5a445275), // 5^-57 + U128.new(0xfb158592be068d2e, 0xeed6e2f0f0d56712), // 5^-56 + U128.new(0x9ced737bb6c4183d, 0x55464dd69685606b), // 5^-55 + U128.new(0xc428d05aa4751e4c, 0xaa97e14c3c26b886), // 5^-54 + U128.new(0xf53304714d9265df, 0xd53dd99f4b3066a8), // 5^-53 + U128.new(0x993fe2c6d07b7fab, 0xe546a8038efe4029), // 5^-52 + U128.new(0xbf8fdb78849a5f96, 0xde98520472bdd033), // 5^-51 + U128.new(0xef73d256a5c0f77c, 0x963e66858f6d4440), // 5^-50 + U128.new(0x95a8637627989aad, 0xdde7001379a44aa8), // 5^-49 + U128.new(0xbb127c53b17ec159, 0x5560c018580d5d52), // 5^-48 + U128.new(0xe9d71b689dde71af, 0xaab8f01e6e10b4a6), // 5^-47 + U128.new(0x9226712162ab070d, 0xcab3961304ca70e8), // 5^-46 + U128.new(0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d22), // 5^-45 + U128.new(0xe45c10c42a2b3b05, 0x8cb89a7db77c506a), // 5^-44 + U128.new(0x8eb98a7a9a5b04e3, 0x77f3608e92adb242), // 5^-43 + U128.new(0xb267ed1940f1c61c, 0x55f038b237591ed3), // 5^-42 + U128.new(0xdf01e85f912e37a3, 0x6b6c46dec52f6688), // 5^-41 + U128.new(0x8b61313bbabce2c6, 0x2323ac4b3b3da015), // 5^-40 + U128.new(0xae397d8aa96c1b77, 0xabec975e0a0d081a), // 5^-39 + U128.new(0xd9c7dced53c72255, 0x96e7bd358c904a21), // 5^-38 + U128.new(0x881cea14545c7575, 0x7e50d64177da2e54), // 5^-37 + U128.new(0xaa242499697392d2, 0xdde50bd1d5d0b9e9), // 5^-36 + U128.new(0xd4ad2dbfc3d07787, 0x955e4ec64b44e864), // 5^-35 + U128.new(0x84ec3c97da624ab4, 0xbd5af13bef0b113e), // 5^-34 + U128.new(0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58e), // 5^-33 + U128.new(0xcfb11ead453994ba, 0x67de18eda5814af2), // 5^-32 + U128.new(0x81ceb32c4b43fcf4, 0x80eacf948770ced7), // 5^-31 + U128.new(0xa2425ff75e14fc31, 0xa1258379a94d028d), // 5^-30 + U128.new(0xcad2f7f5359a3b3e, 0x96ee45813a04330), // 5^-29 + U128.new(0xfd87b5f28300ca0d, 0x8bca9d6e188853fc), // 5^-28 + U128.new(0x9e74d1b791e07e48, 0x775ea264cf55347e), // 5^-27 + U128.new(0xc612062576589dda, 0x95364afe032a819e), // 5^-26 + U128.new(0xf79687aed3eec551, 0x3a83ddbd83f52205), // 5^-25 + U128.new(0x9abe14cd44753b52, 0xc4926a9672793543), // 5^-24 + U128.new(0xc16d9a0095928a27, 0x75b7053c0f178294), // 5^-23 + U128.new(0xf1c90080baf72cb1, 0x5324c68b12dd6339), // 5^-22 + U128.new(0x971da05074da7bee, 0xd3f6fc16ebca5e04), // 5^-21 + U128.new(0xbce5086492111aea, 0x88f4bb1ca6bcf585), // 5^-20 + U128.new(0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6), // 5^-19 + U128.new(0x9392ee8e921d5d07, 0x3aff322e62439fd0), // 5^-18 + U128.new(0xb877aa3236a4b449, 0x9befeb9fad487c3), // 5^-17 + U128.new(0xe69594bec44de15b, 0x4c2ebe687989a9b4), // 5^-16 + U128.new(0x901d7cf73ab0acd9, 0xf9d37014bf60a11), // 5^-15 + U128.new(0xb424dc35095cd80f, 0x538484c19ef38c95), // 5^-14 + U128.new(0xe12e13424bb40e13, 0x2865a5f206b06fba), // 5^-13 + U128.new(0x8cbccc096f5088cb, 0xf93f87b7442e45d4), // 5^-12 + U128.new(0xafebff0bcb24aafe, 0xf78f69a51539d749), // 5^-11 + U128.new(0xdbe6fecebdedd5be, 0xb573440e5a884d1c), // 5^-10 + U128.new(0x89705f4136b4a597, 0x31680a88f8953031), // 5^-9 + U128.new(0xabcc77118461cefc, 0xfdc20d2b36ba7c3e), // 5^-8 + U128.new(0xd6bf94d5e57a42bc, 0x3d32907604691b4d), // 5^-7 + U128.new(0x8637bd05af6c69b5, 0xa63f9a49c2c1b110), // 5^-6 + U128.new(0xa7c5ac471b478423, 0xfcf80dc33721d54), // 5^-5 + U128.new(0xd1b71758e219652b, 0xd3c36113404ea4a9), // 5^-4 + U128.new(0x83126e978d4fdf3b, 0x645a1cac083126ea), // 5^-3 + U128.new(0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4), // 5^-2 + U128.new(0xcccccccccccccccc, 0xcccccccccccccccd), // 5^-1 + U128.new(0x8000000000000000, 0x0), // 5^0 + U128.new(0xa000000000000000, 0x0), // 5^1 + U128.new(0xc800000000000000, 0x0), // 5^2 + U128.new(0xfa00000000000000, 0x0), // 5^3 + U128.new(0x9c40000000000000, 0x0), // 5^4 + U128.new(0xc350000000000000, 0x0), // 5^5 + U128.new(0xf424000000000000, 0x0), // 5^6 + U128.new(0x9896800000000000, 0x0), // 5^7 + U128.new(0xbebc200000000000, 0x0), // 5^8 + U128.new(0xee6b280000000000, 0x0), // 5^9 + U128.new(0x9502f90000000000, 0x0), // 5^10 + U128.new(0xba43b74000000000, 0x0), // 5^11 + U128.new(0xe8d4a51000000000, 0x0), // 5^12 + U128.new(0x9184e72a00000000, 0x0), // 5^13 + U128.new(0xb5e620f480000000, 0x0), // 5^14 + U128.new(0xe35fa931a0000000, 0x0), // 5^15 + U128.new(0x8e1bc9bf04000000, 0x0), // 5^16 + U128.new(0xb1a2bc2ec5000000, 0x0), // 5^17 + U128.new(0xde0b6b3a76400000, 0x0), // 5^18 + U128.new(0x8ac7230489e80000, 0x0), // 5^19 + U128.new(0xad78ebc5ac620000, 0x0), // 5^20 + U128.new(0xd8d726b7177a8000, 0x0), // 5^21 + U128.new(0x878678326eac9000, 0x0), // 5^22 + U128.new(0xa968163f0a57b400, 0x0), // 5^23 + U128.new(0xd3c21bcecceda100, 0x0), // 5^24 + U128.new(0x84595161401484a0, 0x0), // 5^25 + U128.new(0xa56fa5b99019a5c8, 0x0), // 5^26 + U128.new(0xcecb8f27f4200f3a, 0x0), // 5^27 + U128.new(0x813f3978f8940984, 0x4000000000000000), // 5^28 + U128.new(0xa18f07d736b90be5, 0x5000000000000000), // 5^29 + U128.new(0xc9f2c9cd04674ede, 0xa400000000000000), // 5^30 + U128.new(0xfc6f7c4045812296, 0x4d00000000000000), // 5^31 + U128.new(0x9dc5ada82b70b59d, 0xf020000000000000), // 5^32 + U128.new(0xc5371912364ce305, 0x6c28000000000000), // 5^33 + U128.new(0xf684df56c3e01bc6, 0xc732000000000000), // 5^34 + U128.new(0x9a130b963a6c115c, 0x3c7f400000000000), // 5^35 + U128.new(0xc097ce7bc90715b3, 0x4b9f100000000000), // 5^36 + U128.new(0xf0bdc21abb48db20, 0x1e86d40000000000), // 5^37 + U128.new(0x96769950b50d88f4, 0x1314448000000000), // 5^38 + U128.new(0xbc143fa4e250eb31, 0x17d955a000000000), // 5^39 + U128.new(0xeb194f8e1ae525fd, 0x5dcfab0800000000), // 5^40 + U128.new(0x92efd1b8d0cf37be, 0x5aa1cae500000000), // 5^41 + U128.new(0xb7abc627050305ad, 0xf14a3d9e40000000), // 5^42 + U128.new(0xe596b7b0c643c719, 0x6d9ccd05d0000000), // 5^43 + U128.new(0x8f7e32ce7bea5c6f, 0xe4820023a2000000), // 5^44 + U128.new(0xb35dbf821ae4f38b, 0xdda2802c8a800000), // 5^45 + U128.new(0xe0352f62a19e306e, 0xd50b2037ad200000), // 5^46 + U128.new(0x8c213d9da502de45, 0x4526f422cc340000), // 5^47 + U128.new(0xaf298d050e4395d6, 0x9670b12b7f410000), // 5^48 + U128.new(0xdaf3f04651d47b4c, 0x3c0cdd765f114000), // 5^49 + U128.new(0x88d8762bf324cd0f, 0xa5880a69fb6ac800), // 5^50 + U128.new(0xab0e93b6efee0053, 0x8eea0d047a457a00), // 5^51 + U128.new(0xd5d238a4abe98068, 0x72a4904598d6d880), // 5^52 + U128.new(0x85a36366eb71f041, 0x47a6da2b7f864750), // 5^53 + U128.new(0xa70c3c40a64e6c51, 0x999090b65f67d924), // 5^54 + U128.new(0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d), // 5^55 + U128.new(0x82818f1281ed449f, 0xbff8f10e7a8921a4), // 5^56 + U128.new(0xa321f2d7226895c7, 0xaff72d52192b6a0d), // 5^57 + U128.new(0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490), // 5^58 + U128.new(0xfee50b7025c36a08, 0x2f236d04753d5b4), // 5^59 + U128.new(0x9f4f2726179a2245, 0x1d762422c946590), // 5^60 + U128.new(0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5), // 5^61 + U128.new(0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2), // 5^62 + U128.new(0x9b934c3b330c8577, 0x63cc55f49f88eb2f), // 5^63 + U128.new(0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb), // 5^64 + U128.new(0xf316271c7fc3908a, 0x8bef464e3945ef7a), // 5^65 + U128.new(0x97edd871cfda3a56, 0x97758bf0e3cbb5ac), // 5^66 + U128.new(0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317), // 5^67 + U128.new(0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd), // 5^68 + U128.new(0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a), // 5^69 + U128.new(0xb975d6b6ee39e436, 0xb3e2fd538e122b44), // 5^70 + U128.new(0xe7d34c64a9c85d44, 0x60dbbca87196b616), // 5^71 + U128.new(0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd), // 5^72 + U128.new(0xb51d13aea4a488dd, 0x6babab6398bdbe41), // 5^73 + U128.new(0xe264589a4dcdab14, 0xc696963c7eed2dd1), // 5^74 + U128.new(0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2), // 5^75 + U128.new(0xb0de65388cc8ada8, 0x3b25a55f43294bcb), // 5^76 + U128.new(0xdd15fe86affad912, 0x49ef0eb713f39ebe), // 5^77 + U128.new(0x8a2dbf142dfcc7ab, 0x6e3569326c784337), // 5^78 + U128.new(0xacb92ed9397bf996, 0x49c2c37f07965404), // 5^79 + U128.new(0xd7e77a8f87daf7fb, 0xdc33745ec97be906), // 5^80 + U128.new(0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3), // 5^81 + U128.new(0xa8acd7c0222311bc, 0xc40832ea0d68ce0c), // 5^82 + U128.new(0xd2d80db02aabd62b, 0xf50a3fa490c30190), // 5^83 + U128.new(0x83c7088e1aab65db, 0x792667c6da79e0fa), // 5^84 + U128.new(0xa4b8cab1a1563f52, 0x577001b891185938), // 5^85 + U128.new(0xcde6fd5e09abcf26, 0xed4c0226b55e6f86), // 5^86 + U128.new(0x80b05e5ac60b6178, 0x544f8158315b05b4), // 5^87 + U128.new(0xa0dc75f1778e39d6, 0x696361ae3db1c721), // 5^88 + U128.new(0xc913936dd571c84c, 0x3bc3a19cd1e38e9), // 5^89 + U128.new(0xfb5878494ace3a5f, 0x4ab48a04065c723), // 5^90 + U128.new(0x9d174b2dcec0e47b, 0x62eb0d64283f9c76), // 5^91 + U128.new(0xc45d1df942711d9a, 0x3ba5d0bd324f8394), // 5^92 + U128.new(0xf5746577930d6500, 0xca8f44ec7ee36479), // 5^93 + U128.new(0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb), // 5^94 + U128.new(0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e), // 5^95 + U128.new(0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e), // 5^96 + U128.new(0x95d04aee3b80ece5, 0xbba1f1d158724a12), // 5^97 + U128.new(0xbb445da9ca61281f, 0x2a8a6e45ae8edc97), // 5^98 + U128.new(0xea1575143cf97226, 0xf52d09d71a3293bd), // 5^99 + U128.new(0x924d692ca61be758, 0x593c2626705f9c56), // 5^100 + U128.new(0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c), // 5^101 + U128.new(0xe498f455c38b997a, 0xb6dfb9c0f956447), // 5^102 + U128.new(0x8edf98b59a373fec, 0x4724bd4189bd5eac), // 5^103 + U128.new(0xb2977ee300c50fe7, 0x58edec91ec2cb657), // 5^104 + U128.new(0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed), // 5^105 + U128.new(0x8b865b215899f46c, 0xbd79e0d20082ee74), // 5^106 + U128.new(0xae67f1e9aec07187, 0xecd8590680a3aa11), // 5^107 + U128.new(0xda01ee641a708de9, 0xe80e6f4820cc9495), // 5^108 + U128.new(0x884134fe908658b2, 0x3109058d147fdcdd), // 5^109 + U128.new(0xaa51823e34a7eede, 0xbd4b46f0599fd415), // 5^110 + U128.new(0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a), // 5^111 + U128.new(0x850fadc09923329e, 0x3e2cf6bc604ddb0), // 5^112 + U128.new(0xa6539930bf6bff45, 0x84db8346b786151c), // 5^113 + U128.new(0xcfe87f7cef46ff16, 0xe612641865679a63), // 5^114 + U128.new(0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e), // 5^115 + U128.new(0xa26da3999aef7749, 0xe3be5e330f38f09d), // 5^116 + U128.new(0xcb090c8001ab551c, 0x5cadf5bfd3072cc5), // 5^117 + U128.new(0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6), // 5^118 + U128.new(0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa), // 5^119 + U128.new(0xc646d63501a1511d, 0xb281e1fd541501b8), // 5^120 + U128.new(0xf7d88bc24209a565, 0x1f225a7ca91a4226), // 5^121 + U128.new(0x9ae757596946075f, 0x3375788de9b06958), // 5^122 + U128.new(0xc1a12d2fc3978937, 0x52d6b1641c83ae), // 5^123 + U128.new(0xf209787bb47d6b84, 0xc0678c5dbd23a49a), // 5^124 + U128.new(0x9745eb4d50ce6332, 0xf840b7ba963646e0), // 5^125 + U128.new(0xbd176620a501fbff, 0xb650e5a93bc3d898), // 5^126 + U128.new(0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe), // 5^127 + U128.new(0x93ba47c980e98cdf, 0xc66f336c36b10137), // 5^128 + U128.new(0xb8a8d9bbe123f017, 0xb80b0047445d4184), // 5^129 + U128.new(0xe6d3102ad96cec1d, 0xa60dc059157491e5), // 5^130 + U128.new(0x9043ea1ac7e41392, 0x87c89837ad68db2f), // 5^131 + U128.new(0xb454e4a179dd1877, 0x29babe4598c311fb), // 5^132 + U128.new(0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a), // 5^133 + U128.new(0x8ce2529e2734bb1d, 0x1899e4a65f58660c), // 5^134 + U128.new(0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f), // 5^135 + U128.new(0xdc21a1171d42645d, 0x76707543f4fa1f73), // 5^136 + U128.new(0x899504ae72497eba, 0x6a06494a791c53a8), // 5^137 + U128.new(0xabfa45da0edbde69, 0x487db9d17636892), // 5^138 + U128.new(0xd6f8d7509292d603, 0x45a9d2845d3c42b6), // 5^139 + U128.new(0x865b86925b9bc5c2, 0xb8a2392ba45a9b2), // 5^140 + U128.new(0xa7f26836f282b732, 0x8e6cac7768d7141e), // 5^141 + U128.new(0xd1ef0244af2364ff, 0x3207d795430cd926), // 5^142 + U128.new(0x8335616aed761f1f, 0x7f44e6bd49e807b8), // 5^143 + U128.new(0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6), // 5^144 + U128.new(0xcd036837130890a1, 0x36dba887c37a8c0f), // 5^145 + U128.new(0x802221226be55a64, 0xc2494954da2c9789), // 5^146 + U128.new(0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c), // 5^147 + U128.new(0xc83553c5c8965d3d, 0x6f92829494e5acc7), // 5^148 + U128.new(0xfa42a8b73abbf48c, 0xcb772339ba1f17f9), // 5^149 + U128.new(0x9c69a97284b578d7, 0xff2a760414536efb), // 5^150 + U128.new(0xc38413cf25e2d70d, 0xfef5138519684aba), // 5^151 + U128.new(0xf46518c2ef5b8cd1, 0x7eb258665fc25d69), // 5^152 + U128.new(0x98bf2f79d5993802, 0xef2f773ffbd97a61), // 5^153 + U128.new(0xbeeefb584aff8603, 0xaafb550ffacfd8fa), // 5^154 + U128.new(0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38), // 5^155 + U128.new(0x952ab45cfa97a0b2, 0xdd945a747bf26183), // 5^156 + U128.new(0xba756174393d88df, 0x94f971119aeef9e4), // 5^157 + U128.new(0xe912b9d1478ceb17, 0x7a37cd5601aab85d), // 5^158 + U128.new(0x91abb422ccb812ee, 0xac62e055c10ab33a), // 5^159 + U128.new(0xb616a12b7fe617aa, 0x577b986b314d6009), // 5^160 + U128.new(0xe39c49765fdf9d94, 0xed5a7e85fda0b80b), // 5^161 + U128.new(0x8e41ade9fbebc27d, 0x14588f13be847307), // 5^162 + U128.new(0xb1d219647ae6b31c, 0x596eb2d8ae258fc8), // 5^163 + U128.new(0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb), // 5^164 + U128.new(0x8aec23d680043bee, 0x25de7bb9480d5854), // 5^165 + U128.new(0xada72ccc20054ae9, 0xaf561aa79a10ae6a), // 5^166 + U128.new(0xd910f7ff28069da4, 0x1b2ba1518094da04), // 5^167 + U128.new(0x87aa9aff79042286, 0x90fb44d2f05d0842), // 5^168 + U128.new(0xa99541bf57452b28, 0x353a1607ac744a53), // 5^169 + U128.new(0xd3fa922f2d1675f2, 0x42889b8997915ce8), // 5^170 + U128.new(0x847c9b5d7c2e09b7, 0x69956135febada11), // 5^171 + U128.new(0xa59bc234db398c25, 0x43fab9837e699095), // 5^172 + U128.new(0xcf02b2c21207ef2e, 0x94f967e45e03f4bb), // 5^173 + U128.new(0x8161afb94b44f57d, 0x1d1be0eebac278f5), // 5^174 + U128.new(0xa1ba1ba79e1632dc, 0x6462d92a69731732), // 5^175 + U128.new(0xca28a291859bbf93, 0x7d7b8f7503cfdcfe), // 5^176 + U128.new(0xfcb2cb35e702af78, 0x5cda735244c3d43e), // 5^177 + U128.new(0x9defbf01b061adab, 0x3a0888136afa64a7), // 5^178 + U128.new(0xc56baec21c7a1916, 0x88aaa1845b8fdd0), // 5^179 + U128.new(0xf6c69a72a3989f5b, 0x8aad549e57273d45), // 5^180 + U128.new(0x9a3c2087a63f6399, 0x36ac54e2f678864b), // 5^181 + U128.new(0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd), // 5^182 + U128.new(0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5), // 5^183 + U128.new(0x969eb7c47859e743, 0x9f644ae5a4b1b325), // 5^184 + U128.new(0xbc4665b596706114, 0x873d5d9f0dde1fee), // 5^185 + U128.new(0xeb57ff22fc0c7959, 0xa90cb506d155a7ea), // 5^186 + U128.new(0x9316ff75dd87cbd8, 0x9a7f12442d588f2), // 5^187 + U128.new(0xb7dcbf5354e9bece, 0xc11ed6d538aeb2f), // 5^188 + U128.new(0xe5d3ef282a242e81, 0x8f1668c8a86da5fa), // 5^189 + U128.new(0x8fa475791a569d10, 0xf96e017d694487bc), // 5^190 + U128.new(0xb38d92d760ec4455, 0x37c981dcc395a9ac), // 5^191 + U128.new(0xe070f78d3927556a, 0x85bbe253f47b1417), // 5^192 + U128.new(0x8c469ab843b89562, 0x93956d7478ccec8e), // 5^193 + U128.new(0xaf58416654a6babb, 0x387ac8d1970027b2), // 5^194 + U128.new(0xdb2e51bfe9d0696a, 0x6997b05fcc0319e), // 5^195 + U128.new(0x88fcf317f22241e2, 0x441fece3bdf81f03), // 5^196 + U128.new(0xab3c2fddeeaad25a, 0xd527e81cad7626c3), // 5^197 + U128.new(0xd60b3bd56a5586f1, 0x8a71e223d8d3b074), // 5^198 + U128.new(0x85c7056562757456, 0xf6872d5667844e49), // 5^199 + U128.new(0xa738c6bebb12d16c, 0xb428f8ac016561db), // 5^200 + U128.new(0xd106f86e69d785c7, 0xe13336d701beba52), // 5^201 + U128.new(0x82a45b450226b39c, 0xecc0024661173473), // 5^202 + U128.new(0xa34d721642b06084, 0x27f002d7f95d0190), // 5^203 + U128.new(0xcc20ce9bd35c78a5, 0x31ec038df7b441f4), // 5^204 + U128.new(0xff290242c83396ce, 0x7e67047175a15271), // 5^205 + U128.new(0x9f79a169bd203e41, 0xf0062c6e984d386), // 5^206 + U128.new(0xc75809c42c684dd1, 0x52c07b78a3e60868), // 5^207 + U128.new(0xf92e0c3537826145, 0xa7709a56ccdf8a82), // 5^208 + U128.new(0x9bbcc7a142b17ccb, 0x88a66076400bb691), // 5^209 + U128.new(0xc2abf989935ddbfe, 0x6acff893d00ea435), // 5^210 + U128.new(0xf356f7ebf83552fe, 0x583f6b8c4124d43), // 5^211 + U128.new(0x98165af37b2153de, 0xc3727a337a8b704a), // 5^212 + U128.new(0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c), // 5^213 + U128.new(0xeda2ee1c7064130c, 0x1162def06f79df73), // 5^214 + U128.new(0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8), // 5^215 + U128.new(0xb9a74a0637ce2ee1, 0x6d953e2bd7173692), // 5^216 + U128.new(0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437), // 5^217 + U128.new(0x910ab1d4db9914a0, 0x1d9c9892400a22a2), // 5^218 + U128.new(0xb54d5e4a127f59c8, 0x2503beb6d00cab4b), // 5^219 + U128.new(0xe2a0b5dc971f303a, 0x2e44ae64840fd61d), // 5^220 + U128.new(0x8da471a9de737e24, 0x5ceaecfed289e5d2), // 5^221 + U128.new(0xb10d8e1456105dad, 0x7425a83e872c5f47), // 5^222 + U128.new(0xdd50f1996b947518, 0xd12f124e28f77719), // 5^223 + U128.new(0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f), // 5^224 + U128.new(0xace73cbfdc0bfb7b, 0x636cc64d1001550b), // 5^225 + U128.new(0xd8210befd30efa5a, 0x3c47f7e05401aa4e), // 5^226 + U128.new(0x8714a775e3e95c78, 0x65acfaec34810a71), // 5^227 + U128.new(0xa8d9d1535ce3b396, 0x7f1839a741a14d0d), // 5^228 + U128.new(0xd31045a8341ca07c, 0x1ede48111209a050), // 5^229 + U128.new(0x83ea2b892091e44d, 0x934aed0aab460432), // 5^230 + U128.new(0xa4e4b66b68b65d60, 0xf81da84d5617853f), // 5^231 + U128.new(0xce1de40642e3f4b9, 0x36251260ab9d668e), // 5^232 + U128.new(0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019), // 5^233 + U128.new(0xa1075a24e4421730, 0xb24cf65b8612f81f), // 5^234 + U128.new(0xc94930ae1d529cfc, 0xdee033f26797b627), // 5^235 + U128.new(0xfb9b7cd9a4a7443c, 0x169840ef017da3b1), // 5^236 + U128.new(0x9d412e0806e88aa5, 0x8e1f289560ee864e), // 5^237 + U128.new(0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2), // 5^238 + U128.new(0xf5b5d7ec8acb58a2, 0xae10af696774b1db), // 5^239 + U128.new(0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29), // 5^240 + U128.new(0xbff610b0cc6edd3f, 0x17fd090a58d32af3), // 5^241 + U128.new(0xeff394dcff8a948e, 0xddfc4b4cef07f5b0), // 5^242 + U128.new(0x95f83d0a1fb69cd9, 0x4abdaf101564f98e), // 5^243 + U128.new(0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1), // 5^244 + U128.new(0xea53df5fd18d5513, 0x84c86189216dc5ed), // 5^245 + U128.new(0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4), // 5^246 + U128.new(0xb7118682dbb66a77, 0x3fbc8c33221dc2a1), // 5^247 + U128.new(0xe4d5e82392a40515, 0xfabaf3feaa5334a), // 5^248 + U128.new(0x8f05b1163ba6832d, 0x29cb4d87f2a7400e), // 5^249 + U128.new(0xb2c71d5bca9023f8, 0x743e20e9ef511012), // 5^250 + U128.new(0xdf78e4b2bd342cf6, 0x914da9246b255416), // 5^251 + U128.new(0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e), // 5^252 + U128.new(0xae9672aba3d0c320, 0xa184ac2473b529b1), // 5^253 + U128.new(0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e), // 5^254 + U128.new(0x8865899617fb1871, 0x7e2fa67c7a658892), // 5^255 + U128.new(0xaa7eebfb9df9de8d, 0xddbb901b98feeab7), // 5^256 + U128.new(0xd51ea6fa85785631, 0x552a74227f3ea565), // 5^257 + U128.new(0x8533285c936b35de, 0xd53a88958f87275f), // 5^258 + U128.new(0xa67ff273b8460356, 0x8a892abaf368f137), // 5^259 + U128.new(0xd01fef10a657842c, 0x2d2b7569b0432d85), // 5^260 + U128.new(0x8213f56a67f6b29b, 0x9c3b29620e29fc73), // 5^261 + U128.new(0xa298f2c501f45f42, 0x8349f3ba91b47b8f), // 5^262 + U128.new(0xcb3f2f7642717713, 0x241c70a936219a73), // 5^263 + U128.new(0xfe0efb53d30dd4d7, 0xed238cd383aa0110), // 5^264 + U128.new(0x9ec95d1463e8a506, 0xf4363804324a40aa), // 5^265 + U128.new(0xc67bb4597ce2ce48, 0xb143c6053edcd0d5), // 5^266 + U128.new(0xf81aa16fdc1b81da, 0xdd94b7868e94050a), // 5^267 + U128.new(0x9b10a4e5e9913128, 0xca7cf2b4191c8326), // 5^268 + U128.new(0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0), // 5^269 + U128.new(0xf24a01a73cf2dccf, 0xbc633b39673c8cec), // 5^270 + U128.new(0x976e41088617ca01, 0xd5be0503e085d813), // 5^271 + U128.new(0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18), // 5^272 + U128.new(0xec9c459d51852ba2, 0xddf8e7d60ed1219e), // 5^273 + U128.new(0x93e1ab8252f33b45, 0xcabb90e5c942b503), // 5^274 + U128.new(0xb8da1662e7b00a17, 0x3d6a751f3b936243), // 5^275 + U128.new(0xe7109bfba19c0c9d, 0xcc512670a783ad4), // 5^276 + U128.new(0x906a617d450187e2, 0x27fb2b80668b24c5), // 5^277 + U128.new(0xb484f9dc9641e9da, 0xb1f9f660802dedf6), // 5^278 + U128.new(0xe1a63853bbd26451, 0x5e7873f8a0396973), // 5^279 + U128.new(0x8d07e33455637eb2, 0xdb0b487b6423e1e8), // 5^280 + U128.new(0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62), // 5^281 + U128.new(0xdc5c5301c56b75f7, 0x7641a140cc7810fb), // 5^282 + U128.new(0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d), // 5^283 + U128.new(0xac2820d9623bf429, 0x546345fa9fbdcd44), // 5^284 + U128.new(0xd732290fbacaf133, 0xa97c177947ad4095), // 5^285 + U128.new(0x867f59a9d4bed6c0, 0x49ed8eabcccc485d), // 5^286 + U128.new(0xa81f301449ee8c70, 0x5c68f256bfff5a74), // 5^287 + U128.new(0xd226fc195c6a2f8c, 0x73832eec6fff3111), // 5^288 + U128.new(0x83585d8fd9c25db7, 0xc831fd53c5ff7eab), // 5^289 + U128.new(0xa42e74f3d032f525, 0xba3e7ca8b77f5e55), // 5^290 + U128.new(0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb), // 5^291 + U128.new(0x80444b5e7aa7cf85, 0x7980d163cf5b81b3), // 5^292 + U128.new(0xa0555e361951c366, 0xd7e105bcc332621f), // 5^293 + U128.new(0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7), // 5^294 + U128.new(0xfa856334878fc150, 0xb14f98f6f0feb951), // 5^295 + U128.new(0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3), // 5^296 + U128.new(0xc3b8358109e84f07, 0xa862f80ec4700c8), // 5^297 + U128.new(0xf4a642e14c6262c8, 0xcd27bb612758c0fa), // 5^298 + U128.new(0x98e7e9cccfbd7dbd, 0x8038d51cb897789c), // 5^299 + U128.new(0xbf21e44003acdd2c, 0xe0470a63e6bd56c3), // 5^300 + U128.new(0xeeea5d5004981478, 0x1858ccfce06cac74), // 5^301 + U128.new(0x95527a5202df0ccb, 0xf37801e0c43ebc8), // 5^302 + U128.new(0xbaa718e68396cffd, 0xd30560258f54e6ba), // 5^303 + U128.new(0xe950df20247c83fd, 0x47c6b82ef32a2069), // 5^304 + U128.new(0x91d28b7416cdd27e, 0x4cdc331d57fa5441), // 5^305 + U128.new(0xb6472e511c81471d, 0xe0133fe4adf8e952), // 5^306 + U128.new(0xe3d8f9e563a198e5, 0x58180fddd97723a6), // 5^307 + U128.new(0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648), // 5^308 +}; diff --git a/lib/std/fmt/parse_float/convert_fast.zig b/lib/std/fmt/parse_float/convert_fast.zig new file mode 100644 index 0000000000..75875156fd --- /dev/null +++ b/lib/std/fmt/parse_float/convert_fast.zig @@ -0,0 +1,130 @@ +//! Representation of a float as the signficant digits and exponent. +//! The fast path algorithm using machine-sized integers and floats. +//! +//! This only works if both the mantissa and the exponent can be exactly +//! represented as a machine float, since IEE-754 guarantees no rounding +//! will occur. +//! +//! There is an exception: disguised fast-path cases, where we can shift +//! powers-of-10 from the exponent to the significant digits. + +const std = @import("std"); +const math = std.math; +const common = @import("common.zig"); +const FloatInfo = @import("FloatInfo.zig"); +const Number = common.Number; +const floatFromU64 = common.floatFromU64; + +fn isFastPath(comptime T: type, n: Number(T)) bool { + const info = FloatInfo.from(T); + + return info.min_exponent_fast_path <= n.exponent and + n.exponent <= info.max_exponent_fast_path_disguised and + n.mantissa <= info.max_mantissa_fast_path and + !n.many_digits; +} + +// upper bound for tables is floor(mantissaDigits(T) / log2(5)) +// for f64 this is floor(53 / log2(5)) = 22. +// +// Must have max_disguised_fast_path - max_exponent_fast_path entries. (82 - 48 = 34 for f128) +fn fastPow10(comptime T: type, i: usize) T { + return switch (T) { + f16 => ([8]f16{ + 1e0, 1e1, 1e2, 1e3, 1e4, 0, 0, 0, + })[i & 7], + + f32 => ([16]f32{ + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, + 1e8, 1e9, 1e10, 0, 0, 0, 0, 0, + })[i & 15], + + f64 => ([32]f64{ + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, + 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, + 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + })[i & 31], + + f128 => ([64]f128{ + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, + 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, + 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 1e23, + 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, 1e30, 1e31, + 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39, + 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, + 1e48, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + })[i & 63], + + else => unreachable, + }; +} + +fn fastIntPow10(comptime T: type, i: usize) T { + return switch (T) { + u64 => ([16]u64{ + 1, 10, 100, 1000, + 10000, 100000, 1000000, 10000000, + 100000000, 1000000000, 10000000000, 100000000000, + 1000000000000, 10000000000000, 100000000000000, 1000000000000000, + })[i], + + u128 => ([35]u128{ + 1, 10, + 100, 1000, + 10000, 100000, + 1000000, 10000000, + 100000000, 1000000000, + 10000000000, 100000000000, + 1000000000000, 10000000000000, + 100000000000000, 1000000000000000, + 10000000000000000, 100000000000000000, + 1000000000000000000, 10000000000000000000, + 100000000000000000000, 1000000000000000000000, + 10000000000000000000000, 100000000000000000000000, + 1000000000000000000000000, 10000000000000000000000000, + 100000000000000000000000000, 1000000000000000000000000000, + 10000000000000000000000000000, 100000000000000000000000000000, + 1000000000000000000000000000000, 10000000000000000000000000000000, + 100000000000000000000000000000000, 1000000000000000000000000000000000, + 10000000000000000000000000000000000, + })[i], + + else => unreachable, + }; +} + +pub fn convertFast(comptime T: type, n: Number(T)) ?T { + const MantissaT = common.mantissaType(T); + + if (!isFastPath(T, n)) { + return null; + } + + // TODO: x86 (no SSE/SSE2) requires x87 FPU to be setup correctly with fldcw + const info = FloatInfo.from(T); + + var value: T = 0; + if (n.exponent <= info.max_exponent_fast_path) { + // normal fast path + value = @intToFloat(T, n.mantissa); + value = if (n.exponent < 0) + value / fastPow10(T, @intCast(usize, -n.exponent)) + else + value * fastPow10(T, @intCast(usize, n.exponent)); + } else { + // disguised fast path + const shift = n.exponent - info.max_exponent_fast_path; + const mantissa = math.mul(MantissaT, n.mantissa, fastIntPow10(MantissaT, @intCast(usize, shift))) catch return null; + if (mantissa > info.max_mantissa_fast_path) { + return null; + } + value = @intToFloat(T, mantissa) * fastPow10(T, info.max_exponent_fast_path); + } + + if (n.negative) { + value = -value; + } + return value; +} diff --git a/lib/std/fmt/parse_float/convert_hex.zig b/lib/std/fmt/parse_float/convert_hex.zig new file mode 100644 index 0000000000..3b3f797216 --- /dev/null +++ b/lib/std/fmt/parse_float/convert_hex.zig @@ -0,0 +1,89 @@ +//! Conversion of hex-float representation into an accurate value. +// +// Derived from golang strconv/atof.go. + +const std = @import("std"); +const math = std.math; +const common = @import("common.zig"); +const Number = common.Number; +const floatFromUnsigned = common.floatFromUnsigned; + +// converts the form 0xMMM.NNNpEEE. +// +// MMM.NNN = mantissa +// EEE = exponent +// +// MMM.NNN is stored as an integer, the exponent is offset. +pub fn convertHex(comptime T: type, n_: Number(T)) T { + const MantissaT = common.mantissaType(T); + var n = n_; + + if (n.mantissa == 0) { + return if (n.negative) -0.0 else 0.0; + } + + const max_exp = math.floatExponentMax(T); + const min_exp = math.floatExponentMin(T); + const mantissa_bits = math.floatMantissaBits(T); + const exp_bits = math.floatExponentBits(T); + const exp_bias = min_exp - 1; + + // mantissa now implicitly divided by 2^mantissa_bits + n.exponent += mantissa_bits; + + // Shift mantissa and exponent to bring representation into float range. + // Eventually we want a mantissa with a leading 1-bit followed by mantbits other bits. + // For rounding, we need two more, where the bottom bit represents + // whether that bit or any later bit was non-zero. + // (If the mantissa has already lost non-zero bits, trunc is true, + // and we OR in a 1 below after shifting left appropriately.) + while (n.mantissa != 0 and n.mantissa >> (mantissa_bits + 2) == 0) { + n.mantissa <<= 1; + n.exponent -= 1; + } + if (n.many_digits) { + n.mantissa |= 1; + } + while (n.mantissa >> (1 + mantissa_bits + 2) != 0) { + n.mantissa = (n.mantissa >> 1) | (n.mantissa & 1); + n.exponent += 1; + } + + // If exponent is too negative, + // denormalize in hopes of making it representable. + // (The -2 is for the rounding bits.) + while (n.mantissa > 1 and n.exponent < min_exp - 2) { + n.mantissa = (n.mantissa >> 1) | (n.mantissa & 1); + n.exponent += 1; + } + + // Round using two bottom bits. + var round = n.mantissa & 3; + n.mantissa >>= 2; + round |= n.mantissa & 1; // round to even (round up if mantissa is odd) + n.exponent += 2; + if (round == 3) { + n.mantissa += 1; + if (n.mantissa == 1 << (1 + mantissa_bits)) { + n.mantissa >>= 1; + n.exponent += 1; + } + } + + // Denormal or zero + if (n.mantissa >> mantissa_bits == 0) { + n.exponent = exp_bias; + } + + // Infinity and range error + if (n.exponent > max_exp) { + return math.inf(T); + } + + var bits = n.mantissa & ((1 << mantissa_bits) - 1); + bits |= @intCast(MantissaT, (n.exponent - exp_bias) & ((1 << exp_bits) - 1)) << mantissa_bits; + if (n.negative) { + bits |= 1 << (mantissa_bits + exp_bits); + } + return floatFromUnsigned(T, MantissaT, bits); +} diff --git a/lib/std/fmt/parse_float/convert_slow.zig b/lib/std/fmt/parse_float/convert_slow.zig new file mode 100644 index 0000000000..225a1e208c --- /dev/null +++ b/lib/std/fmt/parse_float/convert_slow.zig @@ -0,0 +1,114 @@ +const std = @import("std"); +const math = std.math; +const common = @import("common.zig"); +const BiasedFp = common.BiasedFp; +const Decimal = @import("decimal.zig").Decimal; +const mantissaType = common.mantissaType; + +const max_shift = 60; +const num_powers = 19; +const powers = [_]u8{ 0, 3, 6, 9, 13, 16, 19, 23, 26, 29, 33, 36, 39, 43, 46, 49, 53, 56, 59 }; + +pub fn getShift(n: usize) usize { + return if (n < num_powers) powers[n] else max_shift; +} + +/// Parse the significant digits and biased, binary exponent of a float. +/// +/// This is a fallback algorithm that uses a big-integer representation +/// of the float, and therefore is considerably slower than faster +/// approximations. However, it will always determine how to round +/// the significant digits to the nearest machine float, allowing +/// use to handle near half-way cases. +/// +/// Near half-way cases are halfway between two consecutive machine floats. +/// For example, the float `16777217.0` has a bitwise representation of +/// `100000000000000000000000 1`. Rounding to a single-precision float, +/// the trailing `1` is truncated. Using round-nearest, tie-even, any +/// value above `16777217.0` must be rounded up to `16777218.0`, while +/// any value before or equal to `16777217.0` must be rounded down +/// to `16777216.0`. These near-halfway conversions therefore may require +/// a large number of digits to unambiguously determine how to round. +/// +/// The algorithms described here are based on "Processing Long Numbers Quickly", +/// available here: . +pub fn convertSlow(comptime T: type, s: []const u8) BiasedFp(T) { + const MantissaT = mantissaType(T); + const min_exponent = -(1 << (math.floatExponentBits(T) - 1)) + 1; + const infinite_power = (1 << math.floatExponentBits(T)) - 1; + const mantissa_explicit_bits = math.floatMantissaBits(T); + + var d = Decimal(T).parse(s); // no need to recheck underscores + if (d.num_digits == 0 or d.decimal_point < Decimal(T).min_exponent) { + return BiasedFp(T).zero(); + } else if (d.decimal_point >= Decimal(T).max_exponent) { + return BiasedFp(T).inf(T); + } + + var exp2: i32 = 0; + // Shift right toward (1/2 .. 1] + while (d.decimal_point > 0) { + const n = @intCast(usize, d.decimal_point); + const shift = getShift(n); + d.rightShift(shift); + if (d.decimal_point < -Decimal(T).decimal_point_range) { + return BiasedFp(T).zero(); + } + exp2 += @intCast(i32, shift); + } + // Shift left toward (1/2 .. 1] + while (d.decimal_point <= 0) { + const shift = blk: { + if (d.decimal_point == 0) { + break :blk switch (d.digits[0]) { + 5...9 => break, + 0, 1 => @as(usize, 2), + else => 1, + }; + } else { + const n = @intCast(usize, -d.decimal_point); + break :blk getShift(n); + } + }; + d.leftShift(shift); + if (d.decimal_point > Decimal(T).decimal_point_range) { + return BiasedFp(T).inf(T); + } + exp2 -= @intCast(i32, shift); + } + // We are now in the range [1/2 .. 1] but the binary format uses [1 .. 2] + exp2 -= 1; + while (min_exponent + 1 > exp2) { + var n = @intCast(usize, (min_exponent + 1) - exp2); + if (n > max_shift) { + n = max_shift; + } + d.rightShift(n); + exp2 += @intCast(i32, n); + } + if (exp2 - min_exponent >= infinite_power) { + return BiasedFp(T).inf(T); + } + + // Shift the decimal to the hidden bit, and then round the value + // to get the high mantissa+1 bits. + d.leftShift(mantissa_explicit_bits + 1); + var mantissa = d.round(); + if (mantissa >= (@as(MantissaT, 1) << (mantissa_explicit_bits + 1))) { + // Rounding up overflowed to the carry bit, need to + // shift back to the hidden bit. + d.rightShift(1); + exp2 += 1; + mantissa = d.round(); + if ((exp2 - min_exponent) >= infinite_power) { + return BiasedFp(T).inf(T); + } + } + var power2 = exp2 - min_exponent; + if (mantissa < (@as(MantissaT, 1) << mantissa_explicit_bits)) { + power2 -= 1; + } + // Zero out all the bits above the explicit mantissa bits. + mantissa &= (@as(MantissaT, 1) << mantissa_explicit_bits) - 1; + return .{ .f = mantissa, .e = power2 }; +} diff --git a/lib/std/fmt/parse_float/decimal.zig b/lib/std/fmt/parse_float/decimal.zig new file mode 100644 index 0000000000..1a4c7ebb0d --- /dev/null +++ b/lib/std/fmt/parse_float/decimal.zig @@ -0,0 +1,493 @@ +const std = @import("std"); +const math = std.math; +const common = @import("common.zig"); +const FloatStream = @import("FloatStream.zig"); +const isEightDigits = @import("common.zig").isEightDigits; +const mantissaType = common.mantissaType; + +// Arbitrary-precision decimal class for fallback algorithms. +// +// This is only used if the fast-path (native floats) and +// the Eisel-Lemire algorithm are unable to unambiguously +// determine the float. +// +// The technique used is "Simple Decimal Conversion", developed +// by Nigel Tao and Ken Thompson. A detailed description of the +// algorithm can be found in "ParseNumberF64 by Simple Decimal Conversion", +// available online: . +// +// Big-decimal implementation. We do not use the big.Int routines since we only require a maximum +// fixed region of memory. Further, we require only a small subset of operations. +// +// This accepts a floating point parameter and will generate a Decimal which can correctly parse +// the input with sufficient accuracy. Internally this means either a u64 mantissa (f16, f32 or f64) +// or a u128 mantissa (f128). +pub fn Decimal(comptime T: type) type { + const MantissaT = mantissaType(T); + std.debug.assert(MantissaT == u64 or MantissaT == u128); + + return struct { + const Self = @This(); + + /// The maximum number of digits required to unambiguously round a float. + /// + /// For a double-precision IEEE-754 float, this required 767 digits, + /// so we store the max digits + 1. + /// + /// We can exactly represent a float in radix `b` from radix 2 if + /// `b` is divisible by 2. This function calculates the exact number of + /// digits required to exactly represent that float. + /// + /// According to the "Handbook of Floating Point Arithmetic", + /// for IEEE754, with emin being the min exponent, p2 being the + /// precision, and b being the radix, the number of digits follows as: + /// + /// `−emin + p2 + ⌊(emin + 1) log(2, b) − log(1 − 2^(−p2), b)⌋` + /// + /// For f32, this follows as: + /// emin = -126 + /// p2 = 24 + /// + /// For f64, this follows as: + /// emin = -1022 + /// p2 = 53 + /// + /// For f128, this follows as: + /// emin = -16383 + /// p2 = 112 + /// + /// In Python: + /// `-emin + p2 + math.floor((emin+ 1)*math.log(2, b)-math.log(1-2**(-p2), b))` + pub const max_digits = if (MantissaT == u64) 768 else 11564; + /// The max digits that can be exactly represented in a 64-bit integer. + pub const max_digits_without_overflow = if (MantissaT == u64) 19 else 38; + pub const decimal_point_range = if (MantissaT == u64) 2047 else 32767; + pub const min_exponent = if (MantissaT == u64) -324 else -4966; + pub const max_exponent = if (MantissaT == u64) 310 else 4933; + pub const max_decimal_digits = if (MantissaT == u64) 18 else 37; + + /// The number of significant digits in the decimal. + num_digits: usize, + /// The offset of the decimal point in the significant digits. + decimal_point: i32, + /// If the number of significant digits stored in the decimal is truncated. + truncated: bool, + /// buffer of the raw digits, in the range [0, 9]. + digits: [max_digits]u8, + + pub fn new() Self { + return .{ + .num_digits = 0, + .decimal_point = 0, + .truncated = false, + .digits = [_]u8{0} ** max_digits, + }; + } + + /// Append a digit to the buffer + pub fn tryAddDigit(self: *Self, digit: u8) void { + if (self.num_digits < max_digits) { + self.digits[self.num_digits] = digit; + } + self.num_digits += 1; + } + + /// Trim trailing zeroes from the buffer + pub fn trim(self: *Self) void { + // All of the following calls to `Self::trim` can't panic because: + // + // 1. `parse_decimal` sets `num_digits` to a max of `max_digits`. + // 2. `right_shift` sets `num_digits` to `write_index`, which is bounded by `num_digits`. + // 3. `left_shift` `num_digits` to a max of `max_digits`. + // + // Trim is only called in `right_shift` and `left_shift`. + std.debug.assert(self.num_digits <= max_digits); + while (self.num_digits != 0 and self.digits[self.num_digits - 1] == 0) { + self.num_digits -= 1; + } + } + + pub fn round(self: *Self) MantissaT { + if (self.num_digits == 0 or self.decimal_point < 0) { + return 0; + } else if (self.decimal_point > max_decimal_digits) { + return math.maxInt(MantissaT); + } + + const dp = @intCast(usize, self.decimal_point); + var n: MantissaT = 0; + + var i: usize = 0; + while (i < dp) : (i += 1) { + n *= 10; + if (i < self.num_digits) { + n += @as(MantissaT, self.digits[i]); + } + } + + var round_up = false; + if (dp < self.num_digits) { + round_up = self.digits[dp] >= 5; + if (self.digits[dp] == 5 and dp + 1 == self.num_digits) { + round_up = self.truncated or ((dp != 0) and (1 & self.digits[dp - 1] != 0)); + } + } + if (round_up) { + n += 1; + } + return n; + } + + /// Computes decimal * 2^shift. + pub fn leftShift(self: *Self, shift: usize) void { + if (self.num_digits == 0) { + return; + } + const num_new_digits = self.numberOfDigitsLeftShift(shift); + var read_index = self.num_digits; + var write_index = self.num_digits + num_new_digits; + var n: MantissaT = 0; + while (read_index != 0) { + read_index -= 1; + write_index -= 1; + n += math.shl(MantissaT, self.digits[read_index], shift); + + const quotient = n / 10; + const remainder = n - (10 * quotient); + if (write_index < max_digits) { + self.digits[write_index] = @intCast(u8, remainder); + } else if (remainder > 0) { + self.truncated = true; + } + n = quotient; + } + while (n > 0) { + write_index -= 1; + + const quotient = n / 10; + const remainder = n - (10 * quotient); + if (write_index < max_digits) { + self.digits[write_index] = @intCast(u8, remainder); + } else if (remainder > 0) { + self.truncated = true; + } + n = quotient; + } + + self.num_digits += num_new_digits; + if (self.num_digits > max_digits) { + self.num_digits = max_digits; + } + self.decimal_point += @intCast(i32, num_new_digits); + self.trim(); + } + + /// Computes decimal * 2^-shift. + pub fn rightShift(self: *Self, shift: usize) void { + var read_index: usize = 0; + var write_index: usize = 0; + var n: MantissaT = 0; + while (math.shr(MantissaT, n, shift) == 0) { + if (read_index < self.num_digits) { + n = (10 * n) + self.digits[read_index]; + read_index += 1; + } else if (n == 0) { + return; + } else { + while (math.shr(MantissaT, n, shift) == 0) { + n *= 10; + read_index += 1; + } + break; + } + } + + self.decimal_point -= @intCast(i32, read_index) - 1; + if (self.decimal_point < -decimal_point_range) { + self.num_digits = 0; + self.decimal_point = 0; + self.truncated = false; + return; + } + + const mask = math.shl(MantissaT, 1, shift) - 1; + while (read_index < self.num_digits) { + const new_digit = @intCast(u8, math.shr(MantissaT, n, shift)); + n = (10 * (n & mask)) + self.digits[read_index]; + read_index += 1; + self.digits[write_index] = new_digit; + write_index += 1; + } + while (n > 0) { + const new_digit = @intCast(u8, math.shr(MantissaT, n, shift)); + n = 10 * (n & mask); + if (write_index < max_digits) { + self.digits[write_index] = new_digit; + write_index += 1; + } else if (new_digit > 0) { + self.truncated = true; + } + } + self.num_digits = write_index; + self.trim(); + } + + /// Parse a bit integer representation of the float as a decimal. + // We do not verify underscores in this path since these will have been verified + // via parse.parseNumber so can assume the number is well-formed. + // This code-path does not have to handle hex-floats since these will always be handled via another + // function prior to this. + pub fn parse(s: []const u8) Self { + var d = Self.new(); + var stream = FloatStream.init(s); + + stream.skipChars2('0', '_'); + while (stream.scanDigit(10)) |digit| { + d.tryAddDigit(digit); + } + + if (stream.firstIs('.')) { + stream.advance(1); + const marker = stream.offsetTrue(); + + // Skip leading zeroes + if (d.num_digits == 0) { + stream.skipChars('0'); + } + + while (stream.hasLen(8) and d.num_digits + 8 < max_digits) { + const v = stream.readU64Unchecked(); + if (!isEightDigits(v)) { + break; + } + std.mem.writeIntSliceLittle(u64, d.digits[d.num_digits..], v - 0x3030_3030_3030_3030); + d.num_digits += 8; + stream.advance(8); + } + + while (stream.scanDigit(10)) |digit| { + d.tryAddDigit(digit); + } + d.decimal_point = @intCast(i32, marker) - @intCast(i32, stream.offsetTrue()); + } + if (d.num_digits != 0) { + // Ignore trailing zeros if any + var n_trailing_zeros: usize = 0; + var i = stream.offsetTrue() - 1; + while (true) { + if (s[i] == '0') { + n_trailing_zeros += 1; + } else if (s[i] != '.') { + break; + } + + i -= 1; + if (i == 0) break; + } + d.decimal_point += @intCast(i32, n_trailing_zeros); + d.num_digits -= n_trailing_zeros; + d.decimal_point += @intCast(i32, d.num_digits); + if (d.num_digits > max_digits) { + d.truncated = true; + d.num_digits = max_digits; + } + } + if (stream.firstIsLower('e')) { + stream.advance(1); + var neg_exp = false; + if (stream.firstIs('-')) { + neg_exp = true; + stream.advance(1); + } else if (stream.firstIs('+')) { + stream.advance(1); + } + var exp_num: i32 = 0; + while (stream.scanDigit(10)) |digit| { + if (exp_num < 0x10000) { + exp_num = 10 * exp_num + digit; + } + } + d.decimal_point += if (neg_exp) -exp_num else exp_num; + } + + var i = d.num_digits; + while (i < max_digits_without_overflow) : (i += 1) { + d.digits[i] = 0; + } + + return d; + } + + // Compute the number decimal digits introduced by a base-2 shift. This is performed + // by storing the leading digits of 1/2^i = 5^i and using these along with the cut-off + // value to quickly determine the decimal shift from binary. + // + // See also https://github.com/golang/go/blob/go1.15.3/src/strconv/decimal.go#L163 for + // another description of the method. + pub fn numberOfDigitsLeftShift(self: *Self, shift: usize) usize { + const ShiftCutoff = struct { + delta: u8, + cutoff: []const u8, + }; + + // Leading digits of 1/2^i = 5^i. + // + // ``` + // import math + // + // bits = 128 + // for i in range(bits): + // log2 = math.log(2)/math.log(10) + // print(f'.{{ .delta = {int(log2*i+1)}, .cutoff = "{5**i}" }}, // {2**i}') + // ``` + const pow2_to_pow5_table = [_]ShiftCutoff{ + .{ .delta = 0, .cutoff = "" }, + .{ .delta = 1, .cutoff = "5" }, // 2 + .{ .delta = 1, .cutoff = "25" }, // 4 + .{ .delta = 1, .cutoff = "125" }, // 8 + .{ .delta = 2, .cutoff = "625" }, // 16 + .{ .delta = 2, .cutoff = "3125" }, // 32 + .{ .delta = 2, .cutoff = "15625" }, // 64 + .{ .delta = 3, .cutoff = "78125" }, // 128 + .{ .delta = 3, .cutoff = "390625" }, // 256 + .{ .delta = 3, .cutoff = "1953125" }, // 512 + .{ .delta = 4, .cutoff = "9765625" }, // 1024 + .{ .delta = 4, .cutoff = "48828125" }, // 2048 + .{ .delta = 4, .cutoff = "244140625" }, // 4096 + .{ .delta = 4, .cutoff = "1220703125" }, // 8192 + .{ .delta = 5, .cutoff = "6103515625" }, // 16384 + .{ .delta = 5, .cutoff = "30517578125" }, // 32768 + .{ .delta = 5, .cutoff = "152587890625" }, // 65536 + .{ .delta = 6, .cutoff = "762939453125" }, // 131072 + .{ .delta = 6, .cutoff = "3814697265625" }, // 262144 + .{ .delta = 6, .cutoff = "19073486328125" }, // 524288 + .{ .delta = 7, .cutoff = "95367431640625" }, // 1048576 + .{ .delta = 7, .cutoff = "476837158203125" }, // 2097152 + .{ .delta = 7, .cutoff = "2384185791015625" }, // 4194304 + .{ .delta = 7, .cutoff = "11920928955078125" }, // 8388608 + .{ .delta = 8, .cutoff = "59604644775390625" }, // 16777216 + .{ .delta = 8, .cutoff = "298023223876953125" }, // 33554432 + .{ .delta = 8, .cutoff = "1490116119384765625" }, // 67108864 + .{ .delta = 9, .cutoff = "7450580596923828125" }, // 134217728 + .{ .delta = 9, .cutoff = "37252902984619140625" }, // 268435456 + .{ .delta = 9, .cutoff = "186264514923095703125" }, // 536870912 + .{ .delta = 10, .cutoff = "931322574615478515625" }, // 1073741824 + .{ .delta = 10, .cutoff = "4656612873077392578125" }, // 2147483648 + .{ .delta = 10, .cutoff = "23283064365386962890625" }, // 4294967296 + .{ .delta = 10, .cutoff = "116415321826934814453125" }, // 8589934592 + .{ .delta = 11, .cutoff = "582076609134674072265625" }, // 17179869184 + .{ .delta = 11, .cutoff = "2910383045673370361328125" }, // 34359738368 + .{ .delta = 11, .cutoff = "14551915228366851806640625" }, // 68719476736 + .{ .delta = 12, .cutoff = "72759576141834259033203125" }, // 137438953472 + .{ .delta = 12, .cutoff = "363797880709171295166015625" }, // 274877906944 + .{ .delta = 12, .cutoff = "1818989403545856475830078125" }, // 549755813888 + .{ .delta = 13, .cutoff = "9094947017729282379150390625" }, // 1099511627776 + .{ .delta = 13, .cutoff = "45474735088646411895751953125" }, // 2199023255552 + .{ .delta = 13, .cutoff = "227373675443232059478759765625" }, // 4398046511104 + .{ .delta = 13, .cutoff = "1136868377216160297393798828125" }, // 8796093022208 + .{ .delta = 14, .cutoff = "5684341886080801486968994140625" }, // 17592186044416 + .{ .delta = 14, .cutoff = "28421709430404007434844970703125" }, // 35184372088832 + .{ .delta = 14, .cutoff = "142108547152020037174224853515625" }, // 70368744177664 + .{ .delta = 15, .cutoff = "710542735760100185871124267578125" }, // 140737488355328 + .{ .delta = 15, .cutoff = "3552713678800500929355621337890625" }, // 281474976710656 + .{ .delta = 15, .cutoff = "17763568394002504646778106689453125" }, // 562949953421312 + .{ .delta = 16, .cutoff = "88817841970012523233890533447265625" }, // 1125899906842624 + .{ .delta = 16, .cutoff = "444089209850062616169452667236328125" }, // 2251799813685248 + .{ .delta = 16, .cutoff = "2220446049250313080847263336181640625" }, // 4503599627370496 + .{ .delta = 16, .cutoff = "11102230246251565404236316680908203125" }, // 9007199254740992 + .{ .delta = 17, .cutoff = "55511151231257827021181583404541015625" }, // 18014398509481984 + .{ .delta = 17, .cutoff = "277555756156289135105907917022705078125" }, // 36028797018963968 + .{ .delta = 17, .cutoff = "1387778780781445675529539585113525390625" }, // 72057594037927936 + .{ .delta = 18, .cutoff = "6938893903907228377647697925567626953125" }, // 144115188075855872 + .{ .delta = 18, .cutoff = "34694469519536141888238489627838134765625" }, // 288230376151711744 + .{ .delta = 18, .cutoff = "173472347597680709441192448139190673828125" }, // 576460752303423488 + .{ .delta = 19, .cutoff = "867361737988403547205962240695953369140625" }, // 1152921504606846976 + .{ .delta = 19, .cutoff = "4336808689942017736029811203479766845703125" }, // 2305843009213693952 + .{ .delta = 19, .cutoff = "21684043449710088680149056017398834228515625" }, // 4611686018427387904 + .{ .delta = 19, .cutoff = "108420217248550443400745280086994171142578125" }, // 9223372036854775808 + .{ .delta = 20, .cutoff = "542101086242752217003726400434970855712890625" }, // 18446744073709551616 + .{ .delta = 20, .cutoff = "2710505431213761085018632002174854278564453125" }, // 36893488147419103232 + .{ .delta = 20, .cutoff = "13552527156068805425093160010874271392822265625" }, // 73786976294838206464 + .{ .delta = 21, .cutoff = "67762635780344027125465800054371356964111328125" }, // 147573952589676412928 + .{ .delta = 21, .cutoff = "338813178901720135627329000271856784820556640625" }, // 295147905179352825856 + .{ .delta = 21, .cutoff = "1694065894508600678136645001359283924102783203125" }, // 590295810358705651712 + .{ .delta = 22, .cutoff = "8470329472543003390683225006796419620513916015625" }, // 1180591620717411303424 + .{ .delta = 22, .cutoff = "42351647362715016953416125033982098102569580078125" }, // 2361183241434822606848 + .{ .delta = 22, .cutoff = "211758236813575084767080625169910490512847900390625" }, // 4722366482869645213696 + .{ .delta = 22, .cutoff = "1058791184067875423835403125849552452564239501953125" }, // 9444732965739290427392 + .{ .delta = 23, .cutoff = "5293955920339377119177015629247762262821197509765625" }, // 18889465931478580854784 + .{ .delta = 23, .cutoff = "26469779601696885595885078146238811314105987548828125" }, // 37778931862957161709568 + .{ .delta = 23, .cutoff = "132348898008484427979425390731194056570529937744140625" }, // 75557863725914323419136 + .{ .delta = 24, .cutoff = "661744490042422139897126953655970282852649688720703125" }, // 151115727451828646838272 + .{ .delta = 24, .cutoff = "3308722450212110699485634768279851414263248443603515625" }, // 302231454903657293676544 + .{ .delta = 24, .cutoff = "16543612251060553497428173841399257071316242218017578125" }, // 604462909807314587353088 + .{ .delta = 25, .cutoff = "82718061255302767487140869206996285356581211090087890625" }, // 1208925819614629174706176 + .{ .delta = 25, .cutoff = "413590306276513837435704346034981426782906055450439453125" }, // 2417851639229258349412352 + .{ .delta = 25, .cutoff = "2067951531382569187178521730174907133914530277252197265625" }, // 4835703278458516698824704 + .{ .delta = 25, .cutoff = "10339757656912845935892608650874535669572651386260986328125" }, // 9671406556917033397649408 + .{ .delta = 26, .cutoff = "51698788284564229679463043254372678347863256931304931640625" }, // 19342813113834066795298816 + .{ .delta = 26, .cutoff = "258493941422821148397315216271863391739316284656524658203125" }, // 38685626227668133590597632 + .{ .delta = 26, .cutoff = "1292469707114105741986576081359316958696581423282623291015625" }, // 77371252455336267181195264 + .{ .delta = 27, .cutoff = "6462348535570528709932880406796584793482907116413116455078125" }, // 154742504910672534362390528 + .{ .delta = 27, .cutoff = "32311742677852643549664402033982923967414535582065582275390625" }, // 309485009821345068724781056 + .{ .delta = 27, .cutoff = "161558713389263217748322010169914619837072677910327911376953125" }, // 618970019642690137449562112 + .{ .delta = 28, .cutoff = "807793566946316088741610050849573099185363389551639556884765625" }, // 1237940039285380274899124224 + .{ .delta = 28, .cutoff = "4038967834731580443708050254247865495926816947758197784423828125" }, // 2475880078570760549798248448 + .{ .delta = 28, .cutoff = "20194839173657902218540251271239327479634084738790988922119140625" }, // 4951760157141521099596496896 + .{ .delta = 28, .cutoff = "100974195868289511092701256356196637398170423693954944610595703125" }, // 9903520314283042199192993792 + .{ .delta = 29, .cutoff = "504870979341447555463506281780983186990852118469774723052978515625" }, // 19807040628566084398385987584 + .{ .delta = 29, .cutoff = "2524354896707237777317531408904915934954260592348873615264892578125" }, // 39614081257132168796771975168 + .{ .delta = 29, .cutoff = "12621774483536188886587657044524579674771302961744368076324462890625" }, // 79228162514264337593543950336 + .{ .delta = 30, .cutoff = "63108872417680944432938285222622898373856514808721840381622314453125" }, // 158456325028528675187087900672 + .{ .delta = 30, .cutoff = "315544362088404722164691426113114491869282574043609201908111572265625" }, // 316912650057057350374175801344 + .{ .delta = 30, .cutoff = "1577721810442023610823457130565572459346412870218046009540557861328125" }, // 633825300114114700748351602688 + .{ .delta = 31, .cutoff = "7888609052210118054117285652827862296732064351090230047702789306640625" }, // 1267650600228229401496703205376 + .{ .delta = 31, .cutoff = "39443045261050590270586428264139311483660321755451150238513946533203125" }, // 2535301200456458802993406410752 + .{ .delta = 31, .cutoff = "197215226305252951352932141320696557418301608777255751192569732666015625" }, // 5070602400912917605986812821504 + .{ .delta = 32, .cutoff = "986076131526264756764660706603482787091508043886278755962848663330078125" }, // 10141204801825835211973625643008 + .{ .delta = 32, .cutoff = "4930380657631323783823303533017413935457540219431393779814243316650390625" }, // 20282409603651670423947251286016 + .{ .delta = 32, .cutoff = "24651903288156618919116517665087069677287701097156968899071216583251953125" }, // 40564819207303340847894502572032 + .{ .delta = 32, .cutoff = "123259516440783094595582588325435348386438505485784844495356082916259765625" }, // 81129638414606681695789005144064 + .{ .delta = 33, .cutoff = "616297582203915472977912941627176741932192527428924222476780414581298828125" }, // 162259276829213363391578010288128 + .{ .delta = 33, .cutoff = "3081487911019577364889564708135883709660962637144621112383902072906494140625" }, // 324518553658426726783156020576256 + .{ .delta = 33, .cutoff = "15407439555097886824447823540679418548304813185723105561919510364532470703125" }, // 649037107316853453566312041152512 + .{ .delta = 34, .cutoff = "77037197775489434122239117703397092741524065928615527809597551822662353515625" }, // 1298074214633706907132624082305024 + .{ .delta = 34, .cutoff = "385185988877447170611195588516985463707620329643077639047987759113311767578125" }, // 2596148429267413814265248164610048 + .{ .delta = 34, .cutoff = "1925929944387235853055977942584927318538101648215388195239938795566558837890625" }, // 5192296858534827628530496329220096 + .{ .delta = 35, .cutoff = "9629649721936179265279889712924636592690508241076940976199693977832794189453125" }, // 10384593717069655257060992658440192 + .{ .delta = 35, .cutoff = "48148248609680896326399448564623182963452541205384704880998469889163970947265625" }, // 20769187434139310514121985316880384 + .{ .delta = 35, .cutoff = "240741243048404481631997242823115914817262706026923524404992349445819854736328125" }, // 41538374868278621028243970633760768 + .{ .delta = 35, .cutoff = "1203706215242022408159986214115579574086313530134617622024961747229099273681640625" }, // 83076749736557242056487941267521536 + .{ .delta = 36, .cutoff = "6018531076210112040799931070577897870431567650673088110124808736145496368408203125" }, // 166153499473114484112975882535043072 + .{ .delta = 36, .cutoff = "30092655381050560203999655352889489352157838253365440550624043680727481842041015625" }, // 332306998946228968225951765070086144 + .{ .delta = 36, .cutoff = "150463276905252801019998276764447446760789191266827202753120218403637409210205078125" }, // 664613997892457936451903530140172288 + .{ .delta = 37, .cutoff = "752316384526264005099991383822237233803945956334136013765601092018187046051025390625" }, // 1329227995784915872903807060280344576 + .{ .delta = 37, .cutoff = "3761581922631320025499956919111186169019729781670680068828005460090935230255126953125" }, // 2658455991569831745807614120560689152 + .{ .delta = 37, .cutoff = "18807909613156600127499784595555930845098648908353400344140027300454676151275634765625" }, // 5316911983139663491615228241121378304 + .{ .delta = 38, .cutoff = "94039548065783000637498922977779654225493244541767001720700136502273380756378173828125" }, // 10633823966279326983230456482242756608 + .{ .delta = 38, .cutoff = "470197740328915003187494614888898271127466222708835008603500682511366903781890869140625" }, // 21267647932558653966460912964485513216 + .{ .delta = 38, .cutoff = "2350988701644575015937473074444491355637331113544175043017503412556834518909454345703125" }, // 42535295865117307932921825928971026432 + .{ .delta = 38, .cutoff = "11754943508222875079687365372222456778186655567720875215087517062784172594547271728515625" }, // 85070591730234615865843651857942052864 + .{ .delta = 39, .cutoff = "58774717541114375398436826861112283890933277838604376075437585313920862972736358642578125" }, // 170141183460469231731687303715884105728 + }; + + std.debug.assert(shift < pow2_to_pow5_table.len); + const x = pow2_to_pow5_table[shift]; + + // Compare leading digits of current to check if lexicographically less than cutoff. + for (x.cutoff) |p5, i| { + if (i >= self.num_digits) { + return x.delta - 1; + } else if (self.digits[i] == p5 - '0') { // digits are stored as integers + continue; + } else if (self.digits[i] < p5 - '0') { + return x.delta - 1; + } else { + return x.delta; + } + return x.delta; + } + return x.delta; + } + }; +} diff --git a/lib/std/fmt/parse_float/parse.zig b/lib/std/fmt/parse_float/parse.zig new file mode 100644 index 0000000000..104052c3b3 --- /dev/null +++ b/lib/std/fmt/parse_float/parse.zig @@ -0,0 +1,293 @@ +const std = @import("std"); +const common = @import("common.zig"); +const FloatStream = @import("FloatStream.zig"); +const isEightDigits = common.isEightDigits; +const Number = common.Number; + +/// Parse 8 digits, loaded as bytes in little-endian order. +/// +/// This uses the trick where every digit is in [0x030, 0x39], +/// and therefore can be parsed in 3 multiplications, much +/// faster than the normal 8. +/// +/// This is based off the algorithm described in "Fast numeric string to +/// int", available here: . +fn parse8Digits(v_: u64) u64 { + var v = v_; + const mask = 0x0000_00ff_0000_00ff; + const mul1 = 0x000f_4240_0000_0064; + const mul2 = 0x0000_2710_0000_0001; + v -= 0x3030_3030_3030_3030; + v = (v * 10) + (v >> 8); // will not overflow, fits in 63 bits + const v1 = (v & mask) *% mul1; + const v2 = ((v >> 16) & mask) *% mul2; + return @as(u64, @truncate(u32, (v1 +% v2) >> 32)); +} + +/// Parse digits until a non-digit character is found. +fn tryParseDigits(comptime T: type, stream: *FloatStream, x: *T, comptime base: u8) void { + // Try to parse 8 digits at a time, using an optimized algorithm. + // This only supports decimal digits. + if (base == 10) { + while (stream.hasLen(8)) { + const v = stream.readU64Unchecked(); + if (!isEightDigits(v)) { + break; + } + + x.* = x.* *% 1_0000_0000 +% parse8Digits(v); + stream.advance(8); + } + } + + while (stream.scanDigit(base)) |digit| { + x.* *%= base; + x.* +%= digit; + } +} + +fn min_n_digit_int(comptime T: type, digit_count: usize) T { + var n: T = 1; + var i: usize = 1; + while (i < digit_count) : (i += 1) n *= 10; + return n; +} + +/// Parse up to N digits +fn tryParseNDigits(comptime T: type, stream: *FloatStream, x: *T, comptime base: u8, comptime n: usize) void { + while (x.* < min_n_digit_int(T, n)) { + if (stream.scanDigit(base)) |digit| { + x.* *%= base; + x.* +%= digit; + } else { + break; + } + } +} + +/// Parse the scientific notation component of a float. +fn parseScientific(stream: *FloatStream) ?i64 { + var exponent: i64 = 0; + var negative = false; + + if (stream.first()) |c| { + negative = c == '-'; + if (c == '-' or c == '+') { + stream.advance(1); + } + } + if (stream.firstIsDigit(10)) { + while (stream.scanDigit(10)) |digit| { + // no overflows here, saturate well before overflow + if (exponent < 0x1000_0000) { + exponent = 10 * exponent + digit; + } + } + + return if (negative) -exponent else exponent; + } + + return null; +} + +const ParseInfo = struct { + // 10 or 16 + base: u8, + // 10^19 fits in u64, 16^16 fits in u64 + max_mantissa_digits: usize, + // e.g. e or p (E and P also checked) + exp_char_lower: u8, +}; + +fn parsePartialNumberBase(comptime T: type, stream: *FloatStream, negative: bool, n: *usize, comptime info: ParseInfo) ?Number(T) { + const MantissaT = common.mantissaType(T); + + // parse initial digits before dot + var mantissa: MantissaT = 0; + tryParseDigits(MantissaT, stream, &mantissa, info.base); + var int_end = stream.offsetTrue(); + var n_digits = @intCast(isize, stream.offsetTrue()); + + // handle dot with the following digits + var exponent: i64 = 0; + if (stream.firstIs('.')) { + stream.advance(1); + const marker = stream.offsetTrue(); + tryParseDigits(MantissaT, stream, &mantissa, info.base); + const n_after_dot = stream.offsetTrue() - marker; + exponent = -@intCast(i64, n_after_dot); + n_digits += @intCast(isize, n_after_dot); + } + + // adjust required shift to offset mantissa for base-16 (2^4) + if (info.base == 16) { + exponent *= 4; + } + + if (n_digits == 0) { + return null; + } + + // handle scientific format + var exp_number: i64 = 0; + if (stream.firstIsLower(info.exp_char_lower)) { + stream.advance(1); + exp_number = parseScientific(stream) orelse return null; + exponent += exp_number; + } + + const len = stream.offset; // length must be complete parsed length + n.* = len; + + if (stream.underscore_count > 0 and !validUnderscores(stream.slice, info.base)) { + return null; + } + + // common case with not many digits + if (n_digits <= info.max_mantissa_digits) { + return Number(T){ + .exponent = exponent, + .mantissa = mantissa, + .negative = negative, + .many_digits = false, + .hex = info.base == 16, + }; + } + + n_digits -= info.max_mantissa_digits; + var many_digits = false; + stream.reset(); // re-parse from beginning + while (stream.firstIs3('0', '.', '_')) { + // '0' = '.' + 2 + const next = stream.firstUnchecked(); + if (next != '_') { + n_digits -= @intCast(isize, next -| ('0' - 1)); + } else { + stream.underscore_count += 1; + } + stream.advance(1); + } + if (n_digits > 0) { + // at this point we have more than max_mantissa_digits significant digits, let's try again + many_digits = true; + mantissa = 0; + stream.reset(); + tryParseNDigits(MantissaT, stream, &mantissa, info.base, info.max_mantissa_digits); + + exponent = blk: { + if (mantissa >= min_n_digit_int(MantissaT, info.max_mantissa_digits)) { + // big int + break :blk @intCast(i64, int_end) - @intCast(i64, stream.offsetTrue()); + } else { + // the next byte must be present and be '.' + // We know this is true because we had more than 19 + // digits previously, so we overflowed a 64-bit integer, + // but parsing only the integral digits produced less + // than 19 digits. That means we must have a decimal + // point, and at least 1 fractional digit. + stream.advance(1); + var marker = stream.offsetTrue(); + tryParseNDigits(MantissaT, stream, &mantissa, info.base, info.max_mantissa_digits); + break :blk @intCast(i64, marker) - @intCast(i64, stream.offsetTrue()); + } + }; + // add back the explicit part + exponent += exp_number; + } + + return Number(T){ + .exponent = exponent, + .mantissa = mantissa, + .negative = negative, + .many_digits = many_digits, + .hex = info.base == 16, + }; +} + +/// Parse a partial, non-special floating point number. +/// +/// This creates a representation of the float as the +/// significant digits and the decimal exponent. +fn parsePartialNumber(comptime T: type, s: []const u8, negative: bool, n: *usize) ?Number(T) { + std.debug.assert(s.len != 0); + var stream = FloatStream.init(s); + const MantissaT = common.mantissaType(T); + + if (stream.hasLen(2) and stream.atUnchecked(0) == '0' and std.ascii.toLower(stream.atUnchecked(1)) == 'x') { + stream.advance(2); + return parsePartialNumberBase(T, &stream, negative, n, .{ + .base = 16, + .max_mantissa_digits = if (MantissaT == u64) 16 else 32, + .exp_char_lower = 'p', + }); + } else { + return parsePartialNumberBase(T, &stream, negative, n, .{ + .base = 10, + .max_mantissa_digits = if (MantissaT == u64) 19 else 38, + .exp_char_lower = 'e', + }); + } +} + +pub fn parseNumber(comptime T: type, s: []const u8, negative: bool) ?Number(T) { + var consumed: usize = 0; + if (parsePartialNumber(T, s, negative, &consumed)) |number| { + // must consume entire float (no trailing data) + if (s.len == consumed) { + return number; + } + } + return null; +} + +fn parsePartialInfOrNan(comptime T: type, s: []const u8, n: *usize) ?T { + // inf/infinity; infxxx should only consume inf. + if (std.ascii.startsWithIgnoreCase(s, "inf")) { + n.* = 3; + if (std.ascii.startsWithIgnoreCase(s[3..], "inity")) { + n.* = 8; + } + return std.math.inf(T); + } + + if (std.ascii.startsWithIgnoreCase(s, "nan")) { + n.* = 3; + return std.math.nan(T); + } + + return null; +} + +pub fn parseInfOrNan(comptime T: type, s: []const u8, negative: bool) ?T { + var consumed: usize = 0; + if (parsePartialInfOrNan(T, s, &consumed)) |special| { + if (s.len == consumed) { + if (negative) { + return -1 * special; + } + return special; + } + } + return null; +} + +pub fn validUnderscores(s: []const u8, comptime base: u8) bool { + var i: usize = 0; + while (i < s.len) : (i += 1) { + if (s[i] == '_') { + // underscore at start of end + if (i == 0 or i + 1 == s.len) { + return false; + } + // consecutive underscores + if (!common.isDigit(s[i - 1], base) or !common.isDigit(s[i + 1], base)) { + return false; + } + + // next is guaranteed a digit, skip an extra + i += 1; + } + } + + return true; +} diff --git a/lib/std/fmt/parse_float/parse_float.zig b/lib/std/fmt/parse_float/parse_float.zig new file mode 100644 index 0000000000..d781b11495 --- /dev/null +++ b/lib/std/fmt/parse_float/parse_float.zig @@ -0,0 +1,64 @@ +const std = @import("std"); +const parse = @import("parse.zig"); +const parseNumber = parse.parseNumber; +const parseInfOrNan = parse.parseInfOrNan; +const convertFast = @import("convert_fast.zig").convertFast; +const convertEiselLemire = @import("convert_eisel_lemire.zig").convertEiselLemire; +const convertSlow = @import("convert_slow.zig").convertSlow; +const convertHex = @import("convert_hex.zig").convertHex; + +const optimize = true; + +pub const ParseFloatError = error{ + InvalidCharacter, +}; + +pub fn parseFloat(comptime T: type, s: []const u8) ParseFloatError!T { + if (s.len == 0) { + return error.InvalidCharacter; + } + + var i: usize = 0; + const negative = s[i] == '-'; + if (s[i] == '-' or s[i] == '+') { + i += 1; + } + if (s.len == i) { + return error.InvalidCharacter; + } + + const n = parse.parseNumber(T, s[i..], negative) orelse { + return parse.parseInfOrNan(T, s[i..], negative) orelse error.InvalidCharacter; + }; + + if (n.hex) { + return convertHex(T, n); + } + + if (optimize) { + if (convertFast(T, n)) |f| { + return f; + } + + if (T == f16 or T == f32 or T == f64) { + // If significant digits were truncated, then we can have rounding error + // only if `mantissa + 1` produces a different result. We also avoid + // redundantly using the Eisel-Lemire algorithm if it was unable to + // correctly round on the first pass. + if (convertEiselLemire(T, n.exponent, n.mantissa)) |bf| { + if (!n.many_digits) { + return bf.toFloat(T, n.negative); + } + if (convertEiselLemire(T, n.exponent, n.mantissa + 1)) |bf2| { + if (bf.eql(bf2)) { + return bf.toFloat(T, n.negative); + } + } + } + } + } + + // Unable to correctly round the float using the Eisel-Lemire algorithm. + // Fallback to a slower, but always correct algorithm. + return convertSlow(T, s[i..]).toFloat(T, negative); +} diff --git a/lib/std/fmt/parse_hex_float.zig b/lib/std/fmt/parse_hex_float.zig deleted file mode 100644 index 3e8bc5c5d9..0000000000 --- a/lib/std/fmt/parse_hex_float.zig +++ /dev/null @@ -1,347 +0,0 @@ -// The rounding logic is inspired by LLVM's APFloat and Go's atofHex -// implementation. - -const std = @import("std"); -const ascii = std.ascii; -const fmt = std.fmt; -const math = std.math; -const testing = std.testing; - -const assert = std.debug.assert; - -pub fn parseHexFloat(comptime T: type, s: []const u8) !T { - assert(@typeInfo(T) == .Float); - - const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); - - const mantissa_bits = math.floatMantissaBits(T); - const exponent_bits = math.floatExponentBits(T); - const exponent_min = math.floatExponentMin(T); - const exponent_max = math.floatExponentMax(T); - - const exponent_bias = exponent_max; - const sign_shift = mantissa_bits + exponent_bits; - - if (s.len == 0) - return error.InvalidCharacter; - - if (ascii.eqlIgnoreCase(s, "nan")) { - return math.nan(T); - } else if (ascii.eqlIgnoreCase(s, "inf") or ascii.eqlIgnoreCase(s, "+inf")) { - return math.inf(T); - } else if (ascii.eqlIgnoreCase(s, "-inf")) { - return -math.inf(T); - } - - var negative: bool = false; - var exp_negative: bool = false; - - var mantissa: u128 = 0; - var exponent: i16 = 0; - var frac_scale: i16 = 0; - - const State = enum { - MaybeSign, - Prefix, - LeadingIntegerDigit, - IntegerDigit, - MaybeDot, - LeadingFractionDigit, - FractionDigit, - ExpPrefix, - MaybeExpSign, - ExpDigit, - }; - - var state = State.MaybeSign; - - var i: usize = 0; - while (i < s.len) { - const c = s[i]; - - switch (state) { - .MaybeSign => { - state = .Prefix; - - if (c == '+') { - i += 1; - } else if (c == '-') { - negative = true; - i += 1; - } - }, - .Prefix => { - state = .LeadingIntegerDigit; - - // Match both 0x and 0X. - if (i + 2 > s.len or s[i] != '0' or s[i + 1] | 32 != 'x') - return error.InvalidCharacter; - i += 2; - }, - .LeadingIntegerDigit => { - if (c == '0') { - // Skip leading zeros. - i += 1; - } else if (c == '_') { - return error.InvalidCharacter; - } else { - state = .IntegerDigit; - } - }, - .IntegerDigit => { - if (ascii.isXDigit(c)) { - if (mantissa >= math.maxInt(u128) / 16) - return error.Overflow; - mantissa *%= 16; - mantissa += try fmt.charToDigit(c, 16); - i += 1; - } else if (c == '_') { - i += 1; - } else { - state = .MaybeDot; - } - }, - .MaybeDot => { - if (c == '.') { - state = .LeadingFractionDigit; - i += 1; - } else state = .ExpPrefix; - }, - .LeadingFractionDigit => { - if (c == '_') { - return error.InvalidCharacter; - } else state = .FractionDigit; - }, - .FractionDigit => { - if (ascii.isXDigit(c)) { - if (mantissa < math.maxInt(u128) / 16) { - mantissa *%= 16; - mantissa +%= try fmt.charToDigit(c, 16); - frac_scale += 1; - } else if (c != '0') { - return error.Overflow; - } - i += 1; - } else if (c == '_') { - i += 1; - } else { - state = .ExpPrefix; - } - }, - .ExpPrefix => { - state = .MaybeExpSign; - // Match both p and P. - if (c | 32 != 'p') - return error.InvalidCharacter; - i += 1; - }, - .MaybeExpSign => { - state = .ExpDigit; - - if (c == '+') { - i += 1; - } else if (c == '-') { - exp_negative = true; - i += 1; - } - }, - .ExpDigit => { - if (ascii.isXDigit(c)) { - if (exponent >= math.maxInt(i16) / 10) - return error.Overflow; - exponent *%= 10; - exponent +%= try fmt.charToDigit(c, 10); - i += 1; - } else if (c == '_') { - i += 1; - } else { - return error.InvalidCharacter; - } - }, - } - } - - if (exp_negative) - exponent *= -1; - - // Bring the decimal part to the left side of the decimal dot. - exponent -= frac_scale * 4; - - if (mantissa == 0) { - // Signed zero. - return if (negative) -0.0 else 0.0; - } - - // Divide by 2^mantissa_bits to right-align the mantissa in the fractional - // part. - exponent += mantissa_bits; - - // Keep around two extra bits to correctly round any value that doesn't fit - // the available mantissa bits. The result LSB serves as Guard bit, the - // following one is the Round bit and the last one is the Sticky bit, - // computed by OR-ing all the dropped bits. - - // Normalize by aligning the implicit one bit. - while (mantissa >> (mantissa_bits + 2) == 0) { - mantissa <<= 1; - exponent -= 1; - } - - // Normalize again by dropping the excess precision. - // Note that the discarded bits are folded into the Sticky bit. - while (mantissa >> (mantissa_bits + 2 + 1) != 0) { - mantissa = mantissa >> 1 | (mantissa & 1); - exponent += 1; - } - - // Very small numbers can be possibly represented as denormals, reduce the - // exponent as much as possible. - while (mantissa != 0 and exponent < exponent_min - 2) { - mantissa = mantissa >> 1 | (mantissa & 1); - exponent += 1; - } - - // Whenever the guard bit is one (G=1) and: - // - we've truncated more than 0.5ULP (R=S=1) - // - we've truncated exactly 0.5ULP (R=1 S=0) - // Were are going to increase the mantissa (round up) - const guard_bit_and_half_or_more = (mantissa & 0b110) == 0b110; - mantissa >>= 2; - exponent += 2; - - if (guard_bit_and_half_or_more) { - mantissa += 1; - } - - if (mantissa == (1 << (mantissa_bits + 1))) { - // Renormalize, if the exponent overflows we'll catch that below. - mantissa >>= 1; - exponent += 1; - } - - if (mantissa >> mantissa_bits == 0) { - // This is a denormal number, the biased exponent is zero. - exponent = -exponent_bias; - } - - if (exponent > exponent_max) { - // Overflow, return +inf. - return math.inf(T); - } - - // Remove the implicit bit. - mantissa &= @as(u128, (1 << mantissa_bits) - 1); - - const raw: TBits = - (if (negative) @as(TBits, 1) << sign_shift else 0) | - @as(TBits, @bitCast(u16, exponent + exponent_bias)) << mantissa_bits | - @truncate(TBits, mantissa); - - return @bitCast(T, raw); -} - -test "special" { - try testing.expect(math.isNan(try parseHexFloat(f32, "nAn"))); - try testing.expect(math.isPositiveInf(try parseHexFloat(f32, "iNf"))); - try testing.expect(math.isPositiveInf(try parseHexFloat(f32, "+Inf"))); - try testing.expect(math.isNegativeInf(try parseHexFloat(f32, "-iNf"))); -} -test "zero" { - try testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "0x0")); - try testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "-0x0")); - try testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "0x0p42")); - try testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "-0x0.00000p42")); - try testing.expectEqual(@as(f32, 0.0), try parseHexFloat(f32, "0x0.00000p666")); -} - -test "f16" { - const Case = struct { s: []const u8, v: f16 }; - const cases: []const Case = &[_]Case{ - .{ .s = "0x1p0", .v = 1.0 }, - .{ .s = "-0x1p-1", .v = -0.5 }, - .{ .s = "0x10p+10", .v = 16384.0 }, - .{ .s = "0x10p-10", .v = 0.015625 }, - // Max normalized value. - .{ .s = "0x1.ffcp+15", .v = math.floatMax(f16) }, - .{ .s = "-0x1.ffcp+15", .v = -math.floatMax(f16) }, - // Min normalized value. - .{ .s = "0x1p-14", .v = math.floatMin(f16) }, - .{ .s = "-0x1p-14", .v = -math.floatMin(f16) }, - // Min denormal value. - .{ .s = "0x1p-24", .v = math.floatTrueMin(f16) }, - .{ .s = "-0x1p-24", .v = -math.floatTrueMin(f16) }, - }; - - for (cases) |case| { - try testing.expectEqual(case.v, try parseHexFloat(f16, case.s)); - } -} -test "f32" { - const Case = struct { s: []const u8, v: f32 }; - const cases: []const Case = &[_]Case{ - .{ .s = "0x1p0", .v = 1.0 }, - .{ .s = "-0x1p-1", .v = -0.5 }, - .{ .s = "0x10p+10", .v = 16384.0 }, - .{ .s = "0x10p-10", .v = 0.015625 }, - .{ .s = "0x0.ffffffp128", .v = 0x0.ffffffp128 }, - .{ .s = "0x0.1234570p-125", .v = 0x0.1234570p-125 }, - // Max normalized value. - .{ .s = "0x1.fffffeP+127", .v = math.floatMax(f32) }, - .{ .s = "-0x1.fffffeP+127", .v = -math.floatMax(f32) }, - // Min normalized value. - .{ .s = "0x1p-126", .v = math.floatMin(f32) }, - .{ .s = "-0x1p-126", .v = -math.floatMin(f32) }, - // Min denormal value. - .{ .s = "0x1P-149", .v = math.floatTrueMin(f32) }, - .{ .s = "-0x1P-149", .v = -math.floatTrueMin(f32) }, - }; - - for (cases) |case| { - try testing.expectEqual(case.v, try parseHexFloat(f32, case.s)); - } -} -test "f64" { - const Case = struct { s: []const u8, v: f64 }; - const cases: []const Case = &[_]Case{ - .{ .s = "0x1p0", .v = 1.0 }, - .{ .s = "-0x1p-1", .v = -0.5 }, - .{ .s = "0x10p+10", .v = 16384.0 }, - .{ .s = "0x10p-10", .v = 0.015625 }, - // Max normalized value. - .{ .s = "0x1.fffffffffffffp+1023", .v = math.floatMax(f64) }, - .{ .s = "-0x1.fffffffffffffp1023", .v = -math.floatMax(f64) }, - // Min normalized value. - .{ .s = "0x1p-1022", .v = math.floatMin(f64) }, - .{ .s = "-0x1p-1022", .v = -math.floatMin(f64) }, - // Min denormalized value. - .{ .s = "0x1p-1074", .v = math.floatTrueMin(f64) }, - .{ .s = "-0x1p-1074", .v = -math.floatTrueMin(f64) }, - }; - - for (cases) |case| { - try testing.expectEqual(case.v, try parseHexFloat(f64, case.s)); - } -} -test "f128" { - const Case = struct { s: []const u8, v: f128 }; - const cases: []const Case = &[_]Case{ - .{ .s = "0x1p0", .v = 1.0 }, - .{ .s = "-0x1p-1", .v = -0.5 }, - .{ .s = "0x10p+10", .v = 16384.0 }, - .{ .s = "0x10p-10", .v = 0.015625 }, - // Max normalized value. - .{ .s = "0xf.fffffffffffffffffffffffffff8p+16380", .v = math.floatMax(f128) }, - .{ .s = "-0xf.fffffffffffffffffffffffffff8p+16380", .v = -math.floatMax(f128) }, - // Min normalized value. - .{ .s = "0x1p-16382", .v = math.floatMin(f128) }, - .{ .s = "-0x1p-16382", .v = -math.floatMin(f128) }, - // // Min denormalized value. - .{ .s = "0x1p-16494", .v = math.floatTrueMin(f128) }, - .{ .s = "-0x1p-16494", .v = -math.floatTrueMin(f128) }, - .{ .s = "0x1.edcb34a235253948765432134674fp-1", .v = 0x1.edcb34a235253948765432134674fp-1 }, - }; - - for (cases) |case| { - try testing.expectEqual(@bitCast(u128, case.v), @bitCast(u128, try parseHexFloat(f128, case.s))); - } -} From bbfe2234c87ee67012e0117248ba2c4f3781a1c5 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Mon, 2 May 2022 23:39:38 +1200 Subject: [PATCH 1323/2031] update AstGen float literal parsing --- src/AstGen.zig | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index f74a2b2d39..1da1cf187e 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -6715,13 +6715,7 @@ fn floatLiteral(gz: *GenZir, rl: ResultLoc, node: Ast.Node.Index, sign: Sign) In const main_token = main_tokens[node]; const bytes = tree.tokenSlice(main_token); - const unsigned_float_number: f128 = if (bytes.len > 2 and bytes[1] == 'x') hex: { - assert(bytes[0] == '0'); // validated by tokenizer - break :hex std.fmt.parseHexFloat(f128, bytes) catch |err| switch (err) { - error.InvalidCharacter => unreachable, // validated by tokenizer - error.Overflow => return astgen.failNode(node, "number literal cannot be represented in a 128-bit floating point", .{}), - }; - } else std.fmt.parseFloat(f128, bytes) catch |err| switch (err) { + const unsigned_float_number = std.fmt.parseFloat(f128, bytes) catch |err| switch (err) { error.InvalidCharacter => unreachable, // validated by tokenizer }; const float_number = switch (sign) { From 1a1f62a0ce0565a7f6da9b4b8654407f6c514cb0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 May 2022 14:59:03 -0700 Subject: [PATCH 1324/2031] std: enable real start code always for LLVM backend --- lib/std/start.zig | 4 +--- test/incremental/aarch64-macos/hello_world_with_updates.0.zig | 2 +- test/incremental/x86_64-linux/hello_world_with_updates.0.zig | 2 +- test/incremental/x86_64-macos/hello_world_with_updates.0.zig | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/std/start.zig b/lib/std/start.zig index 5690337317..81c7942bc1 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -29,9 +29,7 @@ comptime { builtin.zig_backend == .stage2_aarch64 or builtin.zig_backend == .stage2_arm or builtin.zig_backend == .stage2_riscv64 or - builtin.zig_backend == .stage2_sparcv9 or - (builtin.zig_backend == .stage2_llvm and native_os != .linux and native_os != .macos) or - (builtin.zig_backend == .stage2_llvm and native_arch != .x86_64 and native_arch != .aarch64)) + builtin.zig_backend == .stage2_sparcv9) { if (builtin.output_mode == .Exe) { if ((builtin.link_libc or builtin.object_format == .c) and @hasDecl(root, "main")) { diff --git a/test/incremental/aarch64-macos/hello_world_with_updates.0.zig b/test/incremental/aarch64-macos/hello_world_with_updates.0.zig index 11a379a9a9..300baac1a4 100644 --- a/test/incremental/aarch64-macos/hello_world_with_updates.0.zig +++ b/test/incremental/aarch64-macos/hello_world_with_updates.0.zig @@ -2,4 +2,4 @@ // output_mode=Exe // target=aarch64-macos // -// :109:9: error: struct 'tmp.tmp' has no member named 'main' +// :107:9: error: struct 'tmp.tmp' has no member named 'main' diff --git a/test/incremental/x86_64-linux/hello_world_with_updates.0.zig b/test/incremental/x86_64-linux/hello_world_with_updates.0.zig index 960fae5e64..47b5e9db7a 100644 --- a/test/incremental/x86_64-linux/hello_world_with_updates.0.zig +++ b/test/incremental/x86_64-linux/hello_world_with_updates.0.zig @@ -2,4 +2,4 @@ // output_mode=Exe // target=x86_64-linux // -// :109:9: error: struct 'tmp.tmp' has no member named 'main' +// :107:9: error: struct 'tmp.tmp' has no member named 'main' diff --git a/test/incremental/x86_64-macos/hello_world_with_updates.0.zig b/test/incremental/x86_64-macos/hello_world_with_updates.0.zig index 34440c4603..dd43748e6e 100644 --- a/test/incremental/x86_64-macos/hello_world_with_updates.0.zig +++ b/test/incremental/x86_64-macos/hello_world_with_updates.0.zig @@ -2,4 +2,4 @@ // output_mode=Exe // target=x86_64-macos // -// :109:9: error: struct 'tmp.tmp' has no member named 'main' +// :107:9: error: struct 'tmp.tmp' has no member named 'main' From d98869da430b0ab7054600dd0cb80a6d45ce0639 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 May 2022 14:59:31 -0700 Subject: [PATCH 1325/2031] behavior tests: fix wrong packed struct test case Packed structs are defined to have the same alignment and size as their backing integer. --- test/behavior/struct.zig | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 89f6e20aa0..b05126db62 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -421,21 +421,10 @@ test "packed struct 24bits" { if (builtin.cpu.arch == .arm) return error.SkipZigTest; // TODO comptime { - // TODO Remove if and leave only the else branch when it is also fixed in stage2 - if (builtin.zig_backend == .stage2_llvm or builtin.zig_backend == .stage2_x86 or - builtin.zig_backend == .stage2_riscv64) - { - // Stage 2 still expects the wrong values - try expect(@sizeOf(Foo24Bits) == 4); - if (@sizeOf(usize) == 4) { - try expect(@sizeOf(Foo96Bits) == 12); - } else { - try expect(@sizeOf(Foo96Bits) == 16); - } - } else { - // Stage1 is now fixed and is expected to return right values - try expectEqual(@sizeOf(Foo24Bits), 3); - try expectEqual(@sizeOf(Foo96Bits), 12); + // stage1 gets the wrong answer for sizeof + if (builtin.zig_backend != .stage1) { + std.debug.assert(@sizeOf(Foo24Bits) == @sizeOf(u24)); + std.debug.assert(@sizeOf(Foo96Bits) == @sizeOf(u96)); } } From b95942744c7ded279f5695ed20fdbbc806323cba Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 May 2022 18:50:49 -0700 Subject: [PATCH 1326/2031] std.pdb: fix incorrect use of packed struct --- lib/std/pdb.zig | 50 ++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/std/pdb.zig b/lib/std/pdb.zig index 46078d6252..a44296c920 100644 --- a/lib/std/pdb.zig +++ b/lib/std/pdb.zig @@ -14,7 +14,7 @@ const ArrayList = std.ArrayList; // documentation and/or contributors. // https://llvm.org/docs/PDB/DbiStream.html#stream-header -pub const DbiStreamHeader = packed struct { +pub const DbiStreamHeader = extern struct { VersionSignature: i32, VersionHeader: u32, Age: u32, @@ -37,7 +37,7 @@ pub const DbiStreamHeader = packed struct { Padding: u32, }; -pub const SectionContribEntry = packed struct { +pub const SectionContribEntry = extern struct { /// COFF Section index, 1-based Section: u16, Padding1: [2]u8, @@ -50,7 +50,7 @@ pub const SectionContribEntry = packed struct { RelocCrc: u32, }; -pub const ModInfo = packed struct { +pub const ModInfo = extern struct { Unused1: u32, SectionContr: SectionContribEntry, Flags: u16, @@ -68,7 +68,7 @@ pub const ModInfo = packed struct { //ObjFileName: char[], }; -pub const SectionMapHeader = packed struct { +pub const SectionMapHeader = extern struct { /// Number of segment descriptors Count: u16, @@ -76,7 +76,7 @@ pub const SectionMapHeader = packed struct { LogCount: u16, }; -pub const SectionMapEntry = packed struct { +pub const SectionMapEntry = extern struct { /// See the SectionMapEntryFlags enum below. Flags: u16, @@ -310,7 +310,7 @@ pub const SymbolKind = enum(u16) { pub const TypeIndex = u32; -pub const ProcSym = packed struct { +pub const ProcSym = extern struct { Parent: u32, End: u32, Next: u32, @@ -342,7 +342,7 @@ pub const SectionContrSubstreamVersion = enum(u32) { _, }; -pub const RecordPrefix = packed struct { +pub const RecordPrefix = extern struct { /// Record length, starting from &RecordKind. RecordLen: u16, @@ -354,7 +354,7 @@ pub const RecordPrefix = packed struct { /// The structure definition follows. /// LineBlockFragmentHeader Blocks[] /// Each `LineBlockFragmentHeader` as specified below. -pub const LineFragmentHeader = packed struct { +pub const LineFragmentHeader = extern struct { /// Code offset of line contribution. RelocOffset: u32, @@ -376,7 +376,7 @@ pub const LineFlags = packed struct { /// header. The structure definitions follow. /// LineNumberEntry Lines[NumLines]; /// ColumnNumberEntry Columns[NumLines]; -pub const LineBlockFragmentHeader = packed struct { +pub const LineBlockFragmentHeader = extern struct { /// Offset of FileChecksum entry in File /// checksums buffer. The checksum entry then /// contains another offset into the string @@ -388,7 +388,7 @@ pub const LineBlockFragmentHeader = packed struct { BlockSize: u32, }; -pub const LineNumberEntry = packed struct { +pub const LineNumberEntry = extern struct { /// Offset to start of code bytes for line number Offset: u32, Flags: u32, @@ -404,13 +404,13 @@ pub const LineNumberEntry = packed struct { }; }; -pub const ColumnNumberEntry = packed struct { +pub const ColumnNumberEntry = extern struct { StartColumn: u16, EndColumn: u16, }; /// Checksum bytes follow. -pub const FileChecksumEntryHeader = packed struct { +pub const FileChecksumEntryHeader = extern struct { /// Byte offset of filename in global string table. FileNameOffset: u32, @@ -441,7 +441,7 @@ pub const DebugSubsectionKind = enum(u32) { CoffSymbolRVA = 0xfd, }; -pub const DebugSubsectionHeader = packed struct { +pub const DebugSubsectionHeader = extern struct { /// codeview::DebugSubsectionKind enum Kind: DebugSubsectionKind, @@ -449,7 +449,7 @@ pub const DebugSubsectionHeader = packed struct { Length: u32, }; -pub const PDBStringTableHeader = packed struct { +pub const PDBStringTableHeader = extern struct { /// PDBStringTableSignature Signature: u32, @@ -686,12 +686,12 @@ pub const Pdb = struct { var symbol_i: usize = 0; while (symbol_i != module.symbols.len) { - const prefix = @ptrCast(*RecordPrefix, &module.symbols[symbol_i]); + const prefix = @ptrCast(*align(1) RecordPrefix, &module.symbols[symbol_i]); if (prefix.RecordLen < 2) return null; switch (prefix.RecordKind) { .S_LPROC32, .S_GPROC32 => { - const proc_sym = @ptrCast(*ProcSym, &module.symbols[symbol_i + @sizeOf(RecordPrefix)]); + const proc_sym = @ptrCast(*align(1) ProcSym, &module.symbols[symbol_i + @sizeOf(RecordPrefix)]); if (address >= proc_sym.CodeOffset and address < proc_sym.CodeOffset + proc_sym.CodeSize) { return mem.sliceTo(@ptrCast([*:0]u8, proc_sym) + @sizeOf(ProcSym), 0); } @@ -712,7 +712,7 @@ pub const Pdb = struct { var skip_len: usize = undefined; const checksum_offset = module.checksum_offset orelse return error.MissingDebugInfo; while (sect_offset != subsect_info.len) : (sect_offset += skip_len) { - const subsect_hdr = @ptrCast(*DebugSubsectionHeader, &subsect_info[sect_offset]); + const subsect_hdr = @ptrCast(*align(1) DebugSubsectionHeader, &subsect_info[sect_offset]); skip_len = subsect_hdr.Length; sect_offset += @sizeOf(DebugSubsectionHeader); @@ -720,7 +720,7 @@ pub const Pdb = struct { .Lines => { var line_index = sect_offset; - const line_hdr = @ptrCast(*LineFragmentHeader, &subsect_info[line_index]); + const line_hdr = @ptrCast(*align(1) LineFragmentHeader, &subsect_info[line_index]); if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo; line_index += @sizeOf(LineFragmentHeader); @@ -734,7 +734,7 @@ pub const Pdb = struct { const subsection_end_index = sect_offset + subsect_hdr.Length; while (line_index < subsection_end_index) { - const block_hdr = @ptrCast(*LineBlockFragmentHeader, &subsect_info[line_index]); + const block_hdr = @ptrCast(*align(1) LineBlockFragmentHeader, &subsect_info[line_index]); line_index += @sizeOf(LineBlockFragmentHeader); const start_line_index = line_index; @@ -746,7 +746,7 @@ pub const Pdb = struct { // This is done with a simple linear search. var line_i: u32 = 0; while (line_i < block_hdr.NumLines) : (line_i += 1) { - const line_num_entry = @ptrCast(*LineNumberEntry, &subsect_info[line_index]); + const line_num_entry = @ptrCast(*align(1) LineNumberEntry, &subsect_info[line_index]); line_index += @sizeOf(LineNumberEntry); const vaddr_start = frag_vaddr_start + line_num_entry.Offset; @@ -758,7 +758,7 @@ pub const Pdb = struct { // line_i == 0 would mean that no matching LineNumberEntry was found. if (line_i > 0) { const subsect_index = checksum_offset + block_hdr.NameIndex; - const chksum_hdr = @ptrCast(*FileChecksumEntryHeader, &module.subsect_info[subsect_index]); + const chksum_hdr = @ptrCast(*align(1) FileChecksumEntryHeader, &module.subsect_info[subsect_index]); const strtab_offset = @sizeOf(PDBStringTableHeader) + chksum_hdr.FileNameOffset; try self.string_table.?.seekTo(strtab_offset); const source_file_name = try self.string_table.?.reader().readUntilDelimiterAlloc(self.allocator, 0, 1024); @@ -768,12 +768,12 @@ pub const Pdb = struct { const column = if (has_column) blk: { const start_col_index = start_line_index + @sizeOf(LineNumberEntry) * block_hdr.NumLines; const col_index = start_col_index + @sizeOf(ColumnNumberEntry) * line_entry_idx; - const col_num_entry = @ptrCast(*ColumnNumberEntry, &subsect_info[col_index]); + const col_num_entry = @ptrCast(*align(1) ColumnNumberEntry, &subsect_info[col_index]); break :blk col_num_entry.StartColumn; } else 0; const found_line_index = start_line_index + line_entry_idx * @sizeOf(LineNumberEntry); - const line_num_entry = @ptrCast(*LineNumberEntry, &subsect_info[found_line_index]); + const line_num_entry = @ptrCast(*align(1) LineNumberEntry, &subsect_info[found_line_index]); const flags = @ptrCast(*LineNumberEntry.Flags, &line_num_entry.Flags); return debug.LineInfo{ @@ -833,7 +833,7 @@ pub const Pdb = struct { var sect_offset: usize = 0; var skip_len: usize = undefined; while (sect_offset != mod.subsect_info.len) : (sect_offset += skip_len) { - const subsect_hdr = @ptrCast(*DebugSubsectionHeader, &mod.subsect_info[sect_offset]); + const subsect_hdr = @ptrCast(*align(1) DebugSubsectionHeader, &mod.subsect_info[sect_offset]); skip_len = subsect_hdr.Length; sect_offset += @sizeOf(DebugSubsectionHeader); @@ -969,7 +969,7 @@ fn blockCountFromSize(size: u32, block_size: u32) u32 { } // https://llvm.org/docs/PDB/MsfFile.html#the-superblock -const SuperBlock = packed struct { +const SuperBlock = extern struct { /// The LLVM docs list a space between C / C++ but empirically this is not the case. const file_magic = "Microsoft C/C++ MSF 7.00\r\n\x1a\x44\x53\x00\x00\x00"; From 5fbda2c579f584c770eadc0e236ed0933be739a8 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Tue, 3 May 2022 17:14:01 +1200 Subject: [PATCH 1327/2031] temporary fix for stage2/stage1 f128 rounding discrepency This is only to get tests running again. The root issue should be fixed in stage1 so rounding is consistent between stages. --- lib/std/fmt.zig | 1 - test/behavior/math.zig | 10 ++++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 96ed842154..64351088f5 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -1842,7 +1842,6 @@ pub const ParseFloatError = @import("fmt/parse_float.zig").ParseFloatError; test { _ = parseFloat; - _ = parseHexFloat; } pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 0479015eee..3997f2db04 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -778,8 +778,14 @@ test "quad hex float literal parsing accurate" { try expect(@bitCast(u128, f) == 0x40042eab345678439abcdefea5678234); } { - var f: f128 = 0x1.edcb34a235253948765432134674fp-1; - try expect(@bitCast(u128, f) == 0x3ffeedcb34a235253948765432134674); + // TODO: modify stage1/parse_f128.c to use round-to-even + if (builtin.zig_backend == .stage1) { + var f: f128 = 0x1.edcb34a235253948765432134674fp-1; + try expect(@bitCast(u128, f) == 0x3ffeedcb34a235253948765432134674); // round-down + } else { + var f: f128 = 0x1.edcb34a235253948765432134674fp-1; + try expect(@bitCast(u128, f) == 0x3ffeedcb34a235253948765432134675); // round-to-even + } } { var f: f128 = 0x1.353e45674d89abacc3a2ebf3ff4ffp-50; From 65389dc280b97365605bc3f7f4038c1972534b9a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 May 2022 20:19:31 -0700 Subject: [PATCH 1328/2031] stage2: improve inline asm stage1 compatibility * outputs can have names and be referenced with template replacements the same as inputs. * fix print_air.zig not decoding correctly. * LLVM backend: use a table for template names for simplicity --- src/Air.zig | 2 ++ src/Sema.zig | 18 +++++++++++++----- src/arch/aarch64/CodeGen.zig | 8 +++++--- src/arch/arm/CodeGen.zig | 8 +++++--- src/arch/riscv64/CodeGen.zig | 8 +++++--- src/arch/sparcv9/CodeGen.zig | 8 +++++--- src/arch/x86_64/CodeGen.zig | 8 +++++--- src/codegen/c.zig | 10 +++++++--- src/codegen/llvm.zig | 37 +++++++++++++++++------------------- src/print_air.zig | 28 +++++++++++++++++---------- 10 files changed, 82 insertions(+), 53 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 0968d95180..2dae8454cf 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -815,6 +815,8 @@ pub const VectorCmp = struct { /// 1. `Inst.Ref` for every inputs_len /// 2. for every outputs_len /// - constraint: memory at this position is reinterpreted as a null +/// terminated string. +/// - name: memory at this position is reinterpreted as a null /// terminated string. pad to the next u32 after the null byte. /// 3. for every inputs_len /// - constraint: memory at this position is reinterpreted as a null diff --git a/src/Sema.zig b/src/Sema.zig index 87cb7cc9d2..9750111662 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -10535,7 +10535,11 @@ fn zirAsm( var output_type_bits = extra.data.output_type_bits; var needed_capacity: usize = @typeInfo(Air.Asm).Struct.fields.len + outputs_len + inputs_len; - const Output = struct { constraint: []const u8, ty: Type }; + const Output = struct { + constraint: []const u8, + name: []const u8, + ty: Type, + }; const output: ?Output = if (outputs_len == 0) null else blk: { const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i); extra_i = output.end; @@ -10548,10 +10552,12 @@ fn zirAsm( } const constraint = sema.code.nullTerminatedString(output.data.constraint); - needed_capacity += constraint.len / 4 + 1; + const name = sema.code.nullTerminatedString(output.data.name); + needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; break :blk Output{ .constraint = constraint, + .name = name, .ty = try sema.resolveType(block, ret_ty_src, output.data.operand), }; }; @@ -10573,7 +10579,7 @@ fn zirAsm( const constraint = sema.code.nullTerminatedString(input.data.constraint); const name = sema.code.nullTerminatedString(input.data.name); - needed_capacity += (constraint.len + name.len + 1) / 4 + 1; + needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; inputs[arg_i] = .{ .c = constraint, .n = name }; } @@ -10611,7 +10617,9 @@ fn zirAsm( const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); mem.copy(u8, buffer, o.constraint); buffer[o.constraint.len] = 0; - sema.air_extra.items.len += o.constraint.len / 4 + 1; + mem.copy(u8, buffer[o.constraint.len + 1 ..], o.name); + buffer[o.constraint.len + 1 + o.name.len] = 0; + sema.air_extra.items.len += (o.constraint.len + o.name.len + (2 + 3)) / 4; } for (inputs) |input| { const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); @@ -10619,7 +10627,7 @@ fn zirAsm( buffer[input.c.len] = 0; mem.copy(u8, buffer[input.c.len + 1 ..], input.n); buffer[input.c.len + 1 + input.n.len] = 0; - sema.air_extra.items.len += (input.c.len + input.n.len + 1) / 4 + 1; + sema.air_extra.items.len += (input.c.len + input.n.len + (2 + 3)) / 4; } for (clobbers) |clobber| { const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 5ed7b63db3..3b27633f69 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -3272,10 +3272,12 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { if (output != .none) { return self.fail("TODO implement codegen for non-expr asm", .{}); } + const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += constraint.len / 4 + 1; + extra_i += (constraint.len + name.len + (2 + 3)) / 4; break constraint; } else null; @@ -3283,10 +3285,10 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { for (inputs) |input| { const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); const constraint = std.mem.sliceTo(input_bytes, 0); - const input_name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); + const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + input_name.len + 1) / 4 + 1; + extra_i += (constraint.len + name.len + (2 + 3)) / 4; if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 73f51f6481..87d51b0276 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -4078,10 +4078,12 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { if (output != .none) { return self.fail("TODO implement codegen for non-expr asm", .{}); } + const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += constraint.len / 4 + 1; + extra_i += (constraint.len + name.len + (2 + 3)) / 4; break constraint; } else null; @@ -4089,10 +4091,10 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { for (inputs) |input| { const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); const constraint = std.mem.sliceTo(input_bytes, 0); - const input_name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); + const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + input_name.len + 1) / 4 + 1; + extra_i += (constraint.len + name.len + (2 + 3)) / 4; if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 61fddee207..96d30c31ce 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -2098,10 +2098,12 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { if (output != .none) { return self.fail("TODO implement codegen for non-expr asm", .{}); } + const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += constraint.len / 4 + 1; + extra_i += (constraint.len + name.len + (2 + 3)) / 4; break constraint; } else null; @@ -2109,10 +2111,10 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { for (inputs) |input| { const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); const constraint = std.mem.sliceTo(input_bytes, 0); - const input_name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); + const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + input_name.len + 1) / 4 + 1; + extra_i += (constraint.len + name.len + (2 + 3)) / 4; if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index bcd8cf8eeb..7d93916fc1 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -642,10 +642,12 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { if (output != .none) { return self.fail("TODO implement codegen for non-expr asm", .{}); } + const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += constraint.len / 4 + 1; + extra_i += (constraint.len + name.len + (2 + 3)) / 4; break constraint; } else null; @@ -653,10 +655,10 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { for (inputs) |input| { const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); const constraint = std.mem.sliceTo(input_bytes, 0); - const input_name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); + const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + input_name.len + 1) / 4 + 1; + extra_i += (constraint.len + name.len + (2 + 3)) / 4; if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 0103f5382f..a4f9c862d9 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -4739,10 +4739,12 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { if (output != .none) { return self.fail("TODO implement codegen for non-expr asm", .{}); } + const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += constraint.len / 4 + 1; + extra_i += (constraint.len + name.len + (2 + 3)) / 4; break constraint; } else null; @@ -4750,10 +4752,10 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { for (inputs) |input| { const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); const constraint = std.mem.sliceTo(input_bytes, 0); - const input_name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); + const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + input_name.len + 1) / 4 + 1; + extra_i += (constraint.len + name.len + (2 + 3)) / 4; if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 2cd93d47fc..44b616c493 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -3018,10 +3018,12 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { if (output != .none) { return f.fail("TODO implement codegen for non-expr asm", .{}); } + const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += constraint.len / 4 + 1; + extra_i += (constraint.len + name.len + (2 + 3)) / 4; break constraint; } else null; @@ -3031,10 +3033,12 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const inputs_extra_begin = extra_i; for (inputs) |input, i| { - const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); + const input_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(input_bytes, 0); + const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += constraint.len / 4 + 1; + extra_i += (constraint.len + name.len + (2 + 3)) / 4; if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') { const reg = constraint[1 .. constraint.len - 1]; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index c9ea5bebac..63e7eece99 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4638,14 +4638,19 @@ pub const FuncGen = struct { var llvm_param_i: usize = 0; var total_i: usize = 0; + var name_map: std.StringArrayHashMapUnmanaged(void) = .{}; + try name_map.ensureUnusedCapacity(arena, outputs.len + inputs.len); + for (outputs) |output| { if (output != .none) { return self.todo("implement inline asm with non-returned output", .{}); } + const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += constraint.len / 4 + 1; + extra_i += (constraint.len + name.len + (2 + 3)) / 4; try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1); if (total_i != 0) { @@ -4654,17 +4659,17 @@ pub const FuncGen = struct { llvm_constraints.appendAssumeCapacity('='); llvm_constraints.appendSliceAssumeCapacity(constraint[1..]); + name_map.putAssumeCapacityNoClobber(name, {}); total_i += 1; } - const input_start_extra_i = extra_i; for (inputs) |input| { - const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); - const constraint = std.mem.sliceTo(input_bytes, 0); - const input_name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); + const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(extra_bytes, 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. - extra_i += (constraint.len + input_name.len + 1) / 4 + 1; + extra_i += (constraint.len + name.len + (2 + 3)) / 4; const arg_llvm_value = try self.resolveInst(input); @@ -4677,6 +4682,7 @@ pub const FuncGen = struct { } llvm_constraints.appendSliceAssumeCapacity(constraint); + name_map.putAssumeCapacityNoClobber(name, {}); llvm_param_i += 1; total_i += 1; } @@ -4739,20 +4745,11 @@ pub const FuncGen = struct { const name = asm_source[name_start..i]; state = .start; - extra_i = input_start_extra_i; - for (inputs) |_, input_i| { - const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); - const constraint = std.mem.sliceTo(input_bytes, 0); - const input_name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); - extra_i += (constraint.len + input_name.len + 1) / 4 + 1; - - if (std.mem.eql(u8, name, input_name)) { - try rendered_template.writer().print("{d}", .{input_i}); - break; - } - } else { - return self.todo("TODO validate asm in Sema", .{}); - } + const index = name_map.getIndex(name) orelse { + // we should validate the assembly in Sema; by now it is too late + return self.todo("unknown input or output name: '{s}'", .{name}); + }; + try rendered_template.writer().print("{d}", .{index}); }, else => {}, }, diff --git a/src/print_air.zig b/src/print_air.zig index 6e336e138b..c01d96ed7f 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -542,15 +542,19 @@ const Writer = struct { extra_i += inputs.len; for (outputs) |output| { - const constraint = w.air.nullTerminatedString(extra_i); + const extra_bytes = std.mem.sliceAsBytes(w.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(extra_bytes, 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); + // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += constraint.len / 4 + 1; + // for the strings and their null terminators, we still use the next u32 + // for the null terminator. + extra_i += (constraint.len + name.len + (2 + 3)) / 4; if (output == .none) { - try s.print(", -> {s}", .{constraint}); + try s.print(", [{s}] -> {s}", .{ name, constraint }); } else { - try s.print(", out {s} = (", .{constraint}); + try s.print(", [{s}] out {s} = (", .{ name, constraint }); try w.writeOperand(s, inst, op_index, output); op_index += 1; try s.writeByte(')'); @@ -558,12 +562,15 @@ const Writer = struct { } for (inputs) |input| { - const constraint = w.air.nullTerminatedString(extra_i); + const extra_bytes = std.mem.sliceAsBytes(w.air.extra[extra_i..]); + const constraint = std.mem.sliceTo(extra_bytes, 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += constraint.len / 4 + 1; + // for the strings and their null terminators, we still use the next u32 + // for the null terminator. + extra_i += (constraint.len + name.len + 1) / 4 + 1; - try s.print(", in {s} = (", .{constraint}); + try s.print(", [{s}] in {s} = (", .{ name, constraint }); try w.writeOperand(s, inst, op_index, input); op_index += 1; try s.writeByte(')'); @@ -572,7 +579,8 @@ const Writer = struct { { var clobber_i: u32 = 0; while (clobber_i < clobbers_len) : (clobber_i += 1) { - const clobber = w.air.nullTerminatedString(extra_i); + const extra_bytes = std.mem.sliceAsBytes(w.air.extra[extra_i..]); + const clobber = std.mem.sliceTo(extra_bytes, 0); // This equation accounts for the fact that even if we have exactly 4 bytes // for the string, we still use the next u32 for the null terminator. extra_i += clobber.len / 4 + 1; From 48d2f226cbba4f06806382fcc097b30a8d60387f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 May 2022 20:34:53 -0700 Subject: [PATCH 1329/2031] Sema: allow exporting optionals and pointers --- src/Sema.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 9750111662..d9c2d1cc2a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4323,7 +4323,7 @@ pub fn analyzeExport( const exported_decl = mod.declPtr(exported_decl_index); // TODO run the same checks as we do for C ABI struct fields switch (exported_decl.ty.zigTypeTag()) { - .Fn, .Int, .Enum, .Struct, .Union, .Array, .Float => {}, + .Fn, .Int, .Enum, .Struct, .Union, .Array, .Float, .Pointer, .Optional => {}, else => return sema.fail(block, src, "unable to export type '{}'", .{ exported_decl.ty.fmt(sema.mod), }), From 76b7f5672511be454a14b909446ed6464244c1e9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 May 2022 20:35:06 -0700 Subject: [PATCH 1330/2031] std.os.windows: upgrade to new function pointer semantics --- lib/std/os/windows.zig | 50 +++++++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 800a16f660..b949f92feb 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -2693,7 +2693,10 @@ pub const MEM_RESERVE_PLACEHOLDERS = 0x2; pub const MEM_DECOMMIT = 0x4000; pub const MEM_RELEASE = 0x8000; -pub const PTHREAD_START_ROUTINE = fn (LPVOID) callconv(.C) DWORD; +pub const PTHREAD_START_ROUTINE = switch (builtin.zig_backend) { + .stage1 => fn (LPVOID) callconv(.C) DWORD, + else => *const fn (LPVOID) callconv(.C) DWORD, +}; pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; pub const WIN32_FIND_DATAW = extern struct { @@ -2866,7 +2869,10 @@ pub const IMAGE_TLS_DIRECTORY = extern struct { pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY; pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY; -pub const PIMAGE_TLS_CALLBACK = ?fn (PVOID, DWORD, PVOID) callconv(.C) void; +pub const PIMAGE_TLS_CALLBACK = switch (builtin.zig_backend) { + .stage1 => ?fn (PVOID, DWORD, PVOID) callconv(.C) void, + else => ?*const fn (PVOID, DWORD, PVOID) callconv(.C) void, +}; pub const PROV_RSA_FULL = 1; @@ -2892,7 +2898,10 @@ pub const FILE_ACTION_MODIFIED = 0x00000003; pub const FILE_ACTION_RENAMED_OLD_NAME = 0x00000004; pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005; -pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?fn (DWORD, DWORD, *OVERLAPPED) callconv(.C) void; +pub const LPOVERLAPPED_COMPLETION_ROUTINE = switch (builtin.zig_backend) { + .stage1 => ?fn (DWORD, DWORD, *OVERLAPPED) callconv(.C) void, + else => ?*const fn (DWORD, DWORD, *OVERLAPPED) callconv(.C) void, +}; pub const FILE_NOTIFY_CHANGE_CREATION = 64; pub const FILE_NOTIFY_CHANGE_SIZE = 8; @@ -2945,7 +2954,10 @@ pub const RTL_CRITICAL_SECTION = extern struct { pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION; pub const INIT_ONCE = RTL_RUN_ONCE; pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT; -pub const INIT_ONCE_FN = fn (InitOnce: *INIT_ONCE, Parameter: ?*anyopaque, Context: ?*anyopaque) callconv(.C) BOOL; +pub const INIT_ONCE_FN = switch (builtin.zig_backend) { + .stage1 => fn (InitOnce: *INIT_ONCE, Parameter: ?*anyopaque, Context: ?*anyopaque) callconv(.C) BOOL, + else => *const fn (InitOnce: *INIT_ONCE, Parameter: ?*anyopaque, Context: ?*anyopaque) callconv(.C) BOOL, +}; pub const RTL_RUN_ONCE = extern struct { Ptr: ?*anyopaque, @@ -3230,7 +3242,10 @@ pub const EXCEPTION_POINTERS = extern struct { ContextRecord: *std.os.windows.CONTEXT, }; -pub const VECTORED_EXCEPTION_HANDLER = fn (ExceptionInfo: *EXCEPTION_POINTERS) callconv(WINAPI) c_long; +pub const VECTORED_EXCEPTION_HANDLER = switch (builtin.zig_backend) { + .stage1 => fn (ExceptionInfo: *EXCEPTION_POINTERS) callconv(WINAPI) c_long, + else => *const fn (ExceptionInfo: *EXCEPTION_POINTERS) callconv(WINAPI) c_long, +}; pub const OBJECT_ATTRIBUTES = extern struct { Length: ULONG, @@ -3506,7 +3521,10 @@ pub const RTL_DRIVE_LETTER_CURDIR = extern struct { DosPath: UNICODE_STRING, }; -pub const PPS_POST_PROCESS_INIT_ROUTINE = ?fn () callconv(.C) void; +pub const PPS_POST_PROCESS_INIT_ROUTINE = switch (builtin.zig_backend) { + .stage1 => ?fn () callconv(.C) void, + else => ?*const fn () callconv(.C) void, +}; pub const FILE_BOTH_DIR_INFORMATION = extern struct { NextEntryOffset: ULONG, @@ -3526,7 +3544,10 @@ pub const FILE_BOTH_DIR_INFORMATION = extern struct { }; pub const FILE_BOTH_DIRECTORY_INFORMATION = FILE_BOTH_DIR_INFORMATION; -pub const IO_APC_ROUTINE = fn (PVOID, *IO_STATUS_BLOCK, ULONG) callconv(.C) void; +pub const IO_APC_ROUTINE = switch (builtin.zig_backend) { + .stage1 => fn (PVOID, *IO_STATUS_BLOCK, ULONG) callconv(.C) void, + else => *const fn (PVOID, *IO_STATUS_BLOCK, ULONG) callconv(.C) void, +}; pub const CURDIR = extern struct { DosPath: UNICODE_STRING, @@ -3598,8 +3619,14 @@ pub const ENUM_PAGE_FILE_INFORMATION = extern struct { PeakUsage: SIZE_T, }; -pub const PENUM_PAGE_FILE_CALLBACKW = ?fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCWSTR) callconv(.C) BOOL; -pub const PENUM_PAGE_FILE_CALLBACKA = ?fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCSTR) callconv(.C) BOOL; +pub const PENUM_PAGE_FILE_CALLBACKW = switch (builtin.zig_backend) { + .stage1 => ?fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCWSTR) callconv(.C) BOOL, + else => ?*const fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCWSTR) callconv(.C) BOOL, +}; +pub const PENUM_PAGE_FILE_CALLBACKA = switch (builtin.zig_backend) { + .stage1 => ?fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCSTR) callconv(.C) BOOL, + else => ?*const fn (?LPVOID, *ENUM_PAGE_FILE_INFORMATION, LPCSTR) callconv(.C) BOOL, +}; pub const PSAPI_WS_WATCH_INFORMATION_EX = extern struct { BasicInfo: PSAPI_WS_WATCH_INFORMATION, @@ -3699,4 +3726,7 @@ pub const CTRL_CLOSE_EVENT: DWORD = 2; pub const CTRL_LOGOFF_EVENT: DWORD = 5; pub const CTRL_SHUTDOWN_EVENT: DWORD = 6; -pub const HANDLER_ROUTINE = fn (dwCtrlType: DWORD) callconv(.C) BOOL; +pub const HANDLER_ROUTINE = switch (builtin.zig_backend) { + .stage1 => fn (dwCtrlType: DWORD) callconv(.C) BOOL, + else => *const fn (dwCtrlType: DWORD) callconv(.C) BOOL, +}; From defda6202a5c9fd50f465b3a6a9728ba5d636a38 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 May 2022 21:16:18 -0700 Subject: [PATCH 1331/2031] LLVM: insert workaround for aarch64-windows f16 CodeView crash --- src/codegen/llvm.zig | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 63e7eece99..d11a359bb6 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4577,6 +4577,10 @@ pub const FuncGen = struct { const operand_ty = self.air.typeOf(pl_op.operand); const name = self.air.nullTerminatedString(pl_op.payload); + if (needDbgVarWorkaround(self.dg, operand_ty)) { + return null; + } + const di_local_var = dib.createAutoVariable( self.di_scope.?, name.ptr, @@ -6147,6 +6151,10 @@ pub const FuncGen = struct { const inst_ty = self.air.typeOfIndex(inst); if (self.dg.object.di_builder) |dib| { + if (needDbgVarWorkaround(self.dg, inst_ty)) { + return arg_val; + } + const src_index = self.getSrcArgIndex(self.arg_index - 1); const func = self.dg.decl.getFunction().?; const lbrace_line = self.dg.module.declPtr(func.owner_decl).src_line + func.lbrace_line + 1; @@ -8248,3 +8256,15 @@ const AnnotatedDITypePtr = enum(usize) { }; const lt_errors_fn_name = "__zig_lt_errors_len"; + +/// Without this workaround, LLVM crashes with "unknown codeview register H1" +/// TODO use llvm-reduce and file upstream LLVM bug for this. +fn needDbgVarWorkaround(dg: *DeclGen, ty: Type) bool { + if (ty.tag() == .f16) { + const target = dg.module.getTarget(); + if (target.os.tag == .windows and target.cpu.arch == .aarch64) { + return true; + } + } + return false; +} From ea7142c43f6a7539f6deb1e77ae5af168fde613e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 May 2022 21:35:03 -0700 Subject: [PATCH 1332/2031] LLVM: set module PIC, PIE, and CodeModel Some simple code from stage1 ported over to stage2. --- src/codegen/llvm.zig | 4 ++++ src/codegen/llvm/bindings.zig | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index d11a359bb6..6c74fc8223 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -340,6 +340,10 @@ pub const Object = struct { llvm_module.setModuleDataLayout(target_data); + if (options.pic) llvm_module.setModulePICLevel(); + if (options.pie) llvm_module.setModulePIELevel(); + if (code_model != .Default) llvm_module.setModuleCodeModel(code_model); + return Object{ .gpa = gpa, .module = options.module.?, diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index b8dc3e1830..560b9544dd 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -310,6 +310,15 @@ pub const Module = opaque { pub const setModuleDataLayout = LLVMSetModuleDataLayout; extern fn LLVMSetModuleDataLayout(*const Module, *const TargetData) void; + pub const setModulePICLevel = ZigLLVMSetModulePICLevel; + extern fn ZigLLVMSetModulePICLevel(module: *const Module) void; + + pub const setModulePIELevel = ZigLLVMSetModulePIELevel; + extern fn ZigLLVMSetModulePIELevel(module: *const Module) void; + + pub const setModuleCodeModel = ZigLLVMSetModuleCodeModel; + extern fn ZigLLVMSetModuleCodeModel(module: *const Module, code_model: CodeModel) void; + pub const addFunction = LLVMAddFunction; extern fn LLVMAddFunction(*const Module, Name: [*:0]const u8, FunctionTy: *const Type) *const Value; From aa3c3f66da630cddc12931e7845f4e71acbc4860 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 May 2022 21:35:37 -0700 Subject: [PATCH 1333/2031] test runner: enable arg processing for stage2 builds --- lib/std/special/test_runner.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/special/test_runner.zig b/lib/std/special/test_runner.zig index d71f2b9274..4b6c80ac66 100644 --- a/lib/std/special/test_runner.zig +++ b/lib/std/special/test_runner.zig @@ -28,7 +28,7 @@ pub fn main() void { { return main2() catch @panic("test failure"); } - if (builtin.zig_backend == .stage1) processArgs(); + processArgs(); const test_fn_list = builtin.test_functions; var ok_count: usize = 0; var skip_count: usize = 0; From 02209d8a5f7b26d503ca5c3a73219fa8839243f6 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Tue, 3 May 2022 19:04:16 +1200 Subject: [PATCH 1334/2031] fix aarch64 f16 nan parse test failure --- lib/std/fmt/parse_float/parse.zig | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/std/fmt/parse_float/parse.zig b/lib/std/fmt/parse_float/parse.zig index 104052c3b3..3b757c7c41 100644 --- a/lib/std/fmt/parse_float/parse.zig +++ b/lib/std/fmt/parse_float/parse.zig @@ -240,14 +240,15 @@ pub fn parseNumber(comptime T: type, s: []const u8, negative: bool) ?Number(T) { return null; } -fn parsePartialInfOrNan(comptime T: type, s: []const u8, n: *usize) ?T { +fn parsePartialInfOrNan(comptime T: type, s: []const u8, negative: bool, n: *usize) ?T { // inf/infinity; infxxx should only consume inf. if (std.ascii.startsWithIgnoreCase(s, "inf")) { n.* = 3; if (std.ascii.startsWithIgnoreCase(s[3..], "inity")) { n.* = 8; } - return std.math.inf(T); + + return if (!negative) std.math.inf(T) else -std.math.inf(T); } if (std.ascii.startsWithIgnoreCase(s, "nan")) { @@ -260,11 +261,8 @@ fn parsePartialInfOrNan(comptime T: type, s: []const u8, n: *usize) ?T { pub fn parseInfOrNan(comptime T: type, s: []const u8, negative: bool) ?T { var consumed: usize = 0; - if (parsePartialInfOrNan(T, s, &consumed)) |special| { + if (parsePartialInfOrNan(T, s, negative, &consumed)) |special| { if (s.len == consumed) { - if (negative) { - return -1 * special; - } return special; } } From 2947a2faab2e287926b20d5d8984ad69bba5bd2c Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Tue, 3 May 2022 20:12:29 +1200 Subject: [PATCH 1335/2031] export __floatuntikf for PowerPC PPC __floatuntikf is equivalent to X86 __floatuntitf. --- lib/std/special/compiler_rt.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index f509e584f5..638bbcc695 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -813,6 +813,7 @@ comptime { @export(__floatditf, .{ .name = "__floatdikf", .linkage = linkage }); @export(__floatunditf, .{ .name = "__floatundikf", .linkage = linkage }); @export(__floatunsitf, .{ .name = "__floatunsikf", .linkage = linkage }); + @export(__floatuntitf, .{ .name = "__floatuntikf", .linkage = linkage }); @export(__letf2, .{ .name = "__eqkf2", .linkage = linkage }); @export(__letf2, .{ .name = "__nekf2", .linkage = linkage }); From d48789467d95ddf0582c855d01f32bef48726e29 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 3 May 2022 11:48:30 +0200 Subject: [PATCH 1336/2031] x86_64: use math.zig to check isPowerOfTwo and calc log2_int --- src/arch/x86_64/CodeGen.zig | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 0103f5382f..ed85874413 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1085,8 +1085,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { // when truncating a `u16` to `u5`, for example, those top 3 bits in the result // have to be removed. this only happens if the dst if not a power-of-two size. const dst_bit_size = dst_ty.bitSize(self.target.*); - const is_power_of_two = (dst_bit_size & (dst_bit_size - 1)) == 0; - if (!is_power_of_two or dst_bit_size < 8) { + if (!math.isPowerOfTwo(dst_bit_size) or dst_bit_size < 8) { const max_reg_bit_width = Register.rax.size(); const shift = @intCast(u6, max_reg_bit_width - dst_ty.bitSize(self.target.*)); const mask = (~@as(u64, 0)) >> shift; @@ -5125,8 +5124,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl } const base_reg = opts.dest_stack_base orelse .rbp; - const is_power_of_two = (abi_size % 2) == 0; - if (!is_power_of_two) { + if (!math.isPowerOfTwo(abi_size)) { self.register_manager.freezeRegs(&.{reg}); defer self.register_manager.unfreezeRegs(&.{reg}); @@ -5135,31 +5133,31 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl var next_offset = stack_offset; var remainder = abi_size; while (remainder > 0) { - const closest_power_of_two = @as(u6, 1) << @intCast(u3, math.log2(remainder)); + const nearest_power_of_two = @as(u6, 1) << math.log2_int(u3, @intCast(u3, remainder)); _ = try self.addInst(.{ .tag = .mov, .ops = (Mir.Ops{ .reg1 = base_reg, - .reg2 = registerAlias(tmp_reg, closest_power_of_two), + .reg2 = registerAlias(tmp_reg, nearest_power_of_two), .flags = 0b10, }).encode(), .data = .{ .imm = @bitCast(u32, -next_offset) }, }); - if (closest_power_of_two > 1) { + if (nearest_power_of_two > 1) { _ = try self.addInst(.{ .tag = .shr, .ops = (Mir.Ops{ .reg1 = tmp_reg, .flags = 0b10, }).encode(), - .data = .{ .imm = closest_power_of_two * 8 }, + .data = .{ .imm = nearest_power_of_two * 8 }, }); } - remainder -= closest_power_of_two; - next_offset -= closest_power_of_two; + remainder -= nearest_power_of_two; + next_offset -= nearest_power_of_two; } } else { _ = try self.addInst(.{ From b6930825b0e59bc355370eb68f5b74868d8aa1d7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 3 May 2022 14:04:15 -0700 Subject: [PATCH 1337/2031] github issue templates enhancements Make the honey traps links instead of actual templates --- .github/ISSUE_TEMPLATE/config.yml | 7 +++++++ .github/ISSUE_TEMPLATE/proposal.yml | 16 ---------------- .github/ISSUE_TEMPLATE/question.yml | 20 -------------------- 3 files changed, 7 insertions(+), 36 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/proposal.yml delete mode 100644 .github/ISSUE_TEMPLATE/question.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..fc5f33e21f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,7 @@ +contact_links: + - name: Language Proposal + about: Propose to improve the Zig language + url: https://github.com/ziglang/zig/wiki/Language-Proposals + - name: Question + about: Please use one of the community spaces for questions or general discussions. + url: https://github.com/ziglang/zig/wiki/Community diff --git a/.github/ISSUE_TEMPLATE/proposal.yml b/.github/ISSUE_TEMPLATE/proposal.yml deleted file mode 100644 index 83e30d2ef4..0000000000 --- a/.github/ISSUE_TEMPLATE/proposal.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Language Proposal -description: Propose to improve the Zig language -labels: ["proposal"] -body: - - type: markdown - attributes: - value: | - Thank you for your interest in improving the Zig language. However, we are - not accepting new proposals to change the language at this time. - - type: checkboxes - id: trash - attributes: - label: Please do not file a proposal to change the language - options: - - label: "I understand, thank you. I will not submit a new proposal at this time" - required: true diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml deleted file mode 100644 index 1e57fc76d4..0000000000 --- a/.github/ISSUE_TEMPLATE/question.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Question -description: Ask a Zig-related question -labels: ["question"] -body: - - type: markdown - attributes: - value: | - Welcome! There are a bunch of great places to ask Zig-related questions. - Please take a look at - [The Community Wiki Page](https://github.com/ziglang/zig/wiki/Community) and - find a comfy place to ask questions. You will find plenty of helpful people in - these spaces. However, this issue tracker is not for questions. It is for - more actionable items such as bug reports and enhancements. - - type: checkboxes - id: trash - attributes: - label: Please do not open a question issue on the bug tracker - options: - - label: "I understand, thank you. I will take my question to one of the community spaces instead" - required: true From f4131cf8a645d7c857fb1c0401000cac0b50080b Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Tue, 3 May 2022 21:45:20 +0200 Subject: [PATCH 1338/2031] musl: update to 1.2.3 This was a bit trickier than it should be due to symbol conflicts with zig's compiler-rt implementation. We attempt to use weak linkage in our compiler-rt, but this does not seem to be working in all cases. I manually disabled export of the problematic compiler-rt math functions in order to cross compile musl's libc.so for all targets as input to `tools/gen_stubs.zig`. Other than that, this update went fairly smoothly. Quite a few additional symbols were added to the blacklist in `tools/gen_stubs.zig` due to recent reorganization of zig's compiler-rt. --- .../include/aarch64-linux-musl/bits/hwcap.h | 4 +- .../include/aarch64-linux-musl/bits/mman.h | 2 + .../include/aarch64-linux-musl/bits/syscall.h | 14 +- .../include/arm-linux-musl/bits/syscall.h | 14 +- lib/libc/include/generic-musl/ctype.h | 2 + lib/libc/include/generic-musl/elf.h | 2 + lib/libc/include/generic-musl/locale.h | 4 +- .../include/generic-musl/netinet/if_ether.h | 1 + lib/libc/include/generic-musl/netinet/in.h | 1 + lib/libc/include/generic-musl/netinet/tcp.h | 11 + lib/libc/include/generic-musl/pthread.h | 1 + lib/libc/include/generic-musl/setjmp.h | 14 +- lib/libc/include/generic-musl/signal.h | 8 + lib/libc/include/generic-musl/stdc-predef.h | 3 + lib/libc/include/generic-musl/stddef.h | 4 +- lib/libc/include/generic-musl/stdio.h | 4 +- lib/libc/include/generic-musl/stdlib.h | 5 +- lib/libc/include/generic-musl/string.h | 4 +- .../include/generic-musl/sys/membarrier.h | 4 + lib/libc/include/generic-musl/sys/mman.h | 1 + lib/libc/include/generic-musl/sys/mount.h | 1 + lib/libc/include/generic-musl/sys/prctl.h | 16 + lib/libc/include/generic-musl/sys/ptrace.h | 9 + lib/libc/include/generic-musl/sys/socket.h | 2 + lib/libc/include/generic-musl/time.h | 4 +- lib/libc/include/generic-musl/unistd.h | 6 +- lib/libc/include/generic-musl/wchar.h | 4 +- .../include/i386-linux-musl/bits/syscall.h | 14 +- .../include/m68k-linux-musl/bits/syscall.h | 14 +- .../include/mips-linux-musl/bits/syscall.h | 14 +- .../include/mips64-linux-musl/bits/syscall.h | 14 +- .../include/powerpc-linux-musl/bits/fenv.h | 2 +- .../include/powerpc-linux-musl/bits/shm.h | 2 +- .../include/powerpc-linux-musl/bits/syscall.h | 14 +- .../powerpc64-linux-musl/bits/syscall.h | 14 +- .../include/riscv64-linux-musl/bits/syscall.h | 16 +- .../include/riscv64-linux-musl/bits/user.h | 1 + .../include/s390x-linux-musl/bits/ptrace.h | 3 + .../include/s390x-linux-musl/bits/syscall.h | 14 +- .../include/x86_64-linux-musl/bits/syscall.h | 14 +- lib/libc/musl/COPYRIGHT | 5 +- lib/libc/musl/arch/aarch64/bits/hwcap.h | 2 + lib/libc/musl/arch/aarch64/bits/mman.h | 2 + lib/libc/musl/arch/aarch64/bits/syscall.h.in | 6 + lib/libc/musl/arch/arm/bits/syscall.h.in | 6 + lib/libc/musl/arch/i386/arch.mak | 1 + lib/libc/musl/arch/i386/bits/syscall.h.in | 6 + lib/libc/musl/arch/m68k/arch.mak | 1 + lib/libc/musl/arch/m68k/atomic_arch.h | 8 + lib/libc/musl/arch/m68k/bits/alltypes.h.in | 25 + lib/libc/musl/arch/m68k/bits/fcntl.h | 40 ++ lib/libc/musl/arch/m68k/bits/fenv.h | 29 + lib/libc/musl/arch/m68k/bits/float.h | 39 ++ lib/libc/musl/arch/m68k/bits/ipcstat.h | 1 + lib/libc/musl/arch/m68k/bits/msg.h | 18 + lib/libc/musl/arch/m68k/bits/posix.h | 2 + lib/libc/musl/arch/m68k/bits/ptrace.h | 2 + lib/libc/musl/arch/m68k/bits/reg.h | 45 ++ lib/libc/musl/arch/m68k/bits/sem.h | 13 + lib/libc/musl/arch/m68k/bits/setjmp.h | 1 + lib/libc/musl/arch/m68k/bits/shm.h | 31 + lib/libc/musl/arch/m68k/bits/signal.h | 140 ++++ lib/libc/musl/arch/m68k/bits/stat.h | 25 + lib/libc/musl/arch/m68k/bits/stdint.h | 20 + lib/libc/musl/arch/m68k/bits/syscall.h.in | 418 +++++++++++ lib/libc/musl/arch/m68k/bits/user.h | 38 + lib/libc/musl/arch/m68k/crt_arch.h | 14 + lib/libc/musl/arch/m68k/kstat.h | 21 + lib/libc/musl/arch/m68k/pthread_arch.h | 12 + lib/libc/musl/arch/m68k/reloc.h | 30 + lib/libc/musl/arch/m68k/syscall_arch.h | 90 +++ lib/libc/musl/arch/mips/arch.mak | 1 + lib/libc/musl/arch/mips/bits/syscall.h.in | 6 + lib/libc/musl/arch/mips/pthread_arch.h | 3 +- lib/libc/musl/arch/mips64/bits/syscall.h.in | 6 + lib/libc/musl/arch/powerpc/arch.mak | 1 + lib/libc/musl/arch/powerpc/bits/fenv.h | 2 +- lib/libc/musl/arch/powerpc/bits/shm.h | 2 +- lib/libc/musl/arch/powerpc/bits/syscall.h.in | 6 + lib/libc/musl/arch/powerpc/reloc.h | 2 +- .../musl/arch/powerpc64/bits/syscall.h.in | 6 + lib/libc/musl/arch/riscv64/bits/syscall.h.in | 8 +- lib/libc/musl/arch/riscv64/bits/user.h | 1 + lib/libc/musl/arch/s390x/bits/ptrace.h | 3 + lib/libc/musl/arch/s390x/bits/syscall.h.in | 6 + lib/libc/musl/arch/x86_64/bits/syscall.h.in | 6 + lib/libc/musl/include/ctype.h | 2 + lib/libc/musl/include/elf.h | 2 + lib/libc/musl/include/locale.h | 4 +- lib/libc/musl/include/netinet/if_ether.h | 1 + lib/libc/musl/include/netinet/in.h | 1 + lib/libc/musl/include/netinet/tcp.h | 11 + lib/libc/musl/include/pthread.h | 1 + lib/libc/musl/include/setjmp.h | 14 +- lib/libc/musl/include/signal.h | 8 + lib/libc/musl/include/stdc-predef.h | 3 + lib/libc/musl/include/stddef.h | 4 +- lib/libc/musl/include/stdio.h | 4 +- lib/libc/musl/include/stdlib.h | 5 +- lib/libc/musl/include/string.h | 4 +- lib/libc/musl/include/sys/membarrier.h | 4 + lib/libc/musl/include/sys/mman.h | 1 + lib/libc/musl/include/sys/mount.h | 1 + lib/libc/musl/include/sys/prctl.h | 16 + lib/libc/musl/include/sys/ptrace.h | 9 + lib/libc/musl/include/sys/socket.h | 2 + lib/libc/musl/include/time.h | 4 +- lib/libc/musl/include/unistd.h | 6 +- lib/libc/musl/include/wchar.h | 4 +- lib/libc/musl/libc.S | 650 +++++++++--------- lib/libc/musl/src/complex/cacosf.c | 4 +- lib/libc/musl/src/complex/catanf.c | 4 +- lib/libc/musl/src/complex/cproj.c | 2 +- lib/libc/musl/src/complex/cprojf.c | 2 +- lib/libc/musl/src/complex/cprojl.c | 2 +- lib/libc/musl/src/ctype/nonspacing.h | 122 ++-- lib/libc/musl/src/env/__libc_start_main.c | 3 +- lib/libc/musl/src/env/__stack_chk_fail.c | 9 + lib/libc/musl/src/errno/__strerror.h | 4 + lib/libc/musl/src/fenv/powerpc/fenv-sf.c | 2 +- lib/libc/musl/src/fenv/powerpc/fenv.S | 2 +- lib/libc/musl/src/include/stdlib.h | 1 + lib/libc/musl/src/internal/version.h | 2 +- lib/libc/musl/src/ldso/dl_iterate_phdr.c | 3 +- lib/libc/musl/src/legacy/cuserid.c | 14 +- lib/libc/musl/src/linux/epoll.c | 4 +- lib/libc/musl/src/locale/dcngettext.c | 3 + lib/libc/musl/src/locale/duplocale.c | 5 + lib/libc/musl/src/locale/strtod_l.c | 22 + lib/libc/musl/src/malloc/free.c | 2 +- .../musl/src/malloc/mallocng/aligned_alloc.c | 3 + lib/libc/musl/src/malloc/mallocng/free.c | 12 +- lib/libc/musl/src/malloc/oldmalloc/malloc.c | 6 +- lib/libc/musl/src/math/acoshf.c | 8 +- lib/libc/musl/src/math/expm1f.c | 3 +- lib/libc/musl/src/math/fmaf.c | 21 +- lib/libc/musl/src/math/powerpc/fabs.c | 2 +- lib/libc/musl/src/math/powerpc/fabsf.c | 2 +- lib/libc/musl/src/math/powerpc/fma.c | 2 +- lib/libc/musl/src/math/powerpc/fmaf.c | 2 +- lib/libc/musl/src/misc/ioctl.c | 9 +- lib/libc/musl/src/passwd/nscd_query.c | 10 +- lib/libc/musl/src/process/fdop.h | 5 + .../posix_spawn_file_actions_addclose.c | 1 + .../posix_spawn_file_actions_adddup2.c | 1 + .../posix_spawn_file_actions_addfchdir.c | 1 + .../posix_spawn_file_actions_addopen.c | 1 + lib/libc/musl/src/setjmp/powerpc/longjmp.S | 32 +- lib/libc/musl/src/setjmp/powerpc/setjmp.S | 32 +- lib/libc/musl/src/signal/block.c | 4 +- lib/libc/musl/src/stdio/fgetws.c | 7 +- lib/libc/musl/src/stdio/fseek.c | 7 + lib/libc/musl/src/stdio/getdelim.c | 8 +- lib/libc/musl/src/stdio/popen.c | 24 +- lib/libc/musl/src/stdlib/qsort.c | 37 +- lib/libc/musl/src/stdlib/qsort_nr.c | 14 + lib/libc/musl/src/stdlib/strtod.c | 7 - lib/libc/musl/src/thread/pthread_getname_np.c | 25 + lib/libc/musl/src/thread/pthread_setname_np.c | 2 +- lib/libc/musl/src/time/__tz.c | 40 +- lib/libc/musl/src/unistd/nice.c | 9 +- src/musl.zig | 6 +- tools/gen_stubs.zig | 112 +++ 163 files changed, 2317 insertions(+), 542 deletions(-) create mode 100644 lib/libc/include/aarch64-linux-musl/bits/mman.h create mode 100644 lib/libc/musl/arch/aarch64/bits/mman.h create mode 100644 lib/libc/musl/arch/i386/arch.mak create mode 100644 lib/libc/musl/arch/m68k/arch.mak create mode 100644 lib/libc/musl/arch/m68k/atomic_arch.h create mode 100644 lib/libc/musl/arch/m68k/bits/alltypes.h.in create mode 100644 lib/libc/musl/arch/m68k/bits/fcntl.h create mode 100644 lib/libc/musl/arch/m68k/bits/fenv.h create mode 100644 lib/libc/musl/arch/m68k/bits/float.h create mode 100644 lib/libc/musl/arch/m68k/bits/ipcstat.h create mode 100644 lib/libc/musl/arch/m68k/bits/msg.h create mode 100644 lib/libc/musl/arch/m68k/bits/posix.h create mode 100644 lib/libc/musl/arch/m68k/bits/ptrace.h create mode 100644 lib/libc/musl/arch/m68k/bits/reg.h create mode 100644 lib/libc/musl/arch/m68k/bits/sem.h create mode 100644 lib/libc/musl/arch/m68k/bits/setjmp.h create mode 100644 lib/libc/musl/arch/m68k/bits/shm.h create mode 100644 lib/libc/musl/arch/m68k/bits/signal.h create mode 100644 lib/libc/musl/arch/m68k/bits/stat.h create mode 100644 lib/libc/musl/arch/m68k/bits/stdint.h create mode 100644 lib/libc/musl/arch/m68k/bits/syscall.h.in create mode 100644 lib/libc/musl/arch/m68k/bits/user.h create mode 100644 lib/libc/musl/arch/m68k/crt_arch.h create mode 100644 lib/libc/musl/arch/m68k/kstat.h create mode 100644 lib/libc/musl/arch/m68k/pthread_arch.h create mode 100644 lib/libc/musl/arch/m68k/reloc.h create mode 100644 lib/libc/musl/arch/m68k/syscall_arch.h create mode 100644 lib/libc/musl/arch/mips/arch.mak create mode 100644 lib/libc/musl/arch/powerpc/arch.mak create mode 100644 lib/libc/musl/src/locale/strtod_l.c create mode 100644 lib/libc/musl/src/stdlib/qsort_nr.c create mode 100644 lib/libc/musl/src/thread/pthread_getname_np.c diff --git a/lib/libc/include/aarch64-linux-musl/bits/hwcap.h b/lib/libc/include/aarch64-linux-musl/bits/hwcap.h index 80a088ef35..4628bc7823 100644 --- a/lib/libc/include/aarch64-linux-musl/bits/hwcap.h +++ b/lib/libc/include/aarch64-linux-musl/bits/hwcap.h @@ -47,4 +47,6 @@ #define HWCAP2_I8MM (1 << 13) #define HWCAP2_BF16 (1 << 14) #define HWCAP2_DGH (1 << 15) -#define HWCAP2_RNG (1 << 16) \ No newline at end of file +#define HWCAP2_RNG (1 << 16) +#define HWCAP2_BTI (1 << 17) +#define HWCAP2_MTE (1 << 18) \ No newline at end of file diff --git a/lib/libc/include/aarch64-linux-musl/bits/mman.h b/lib/libc/include/aarch64-linux-musl/bits/mman.h new file mode 100644 index 0000000000..acfc36f243 --- /dev/null +++ b/lib/libc/include/aarch64-linux-musl/bits/mman.h @@ -0,0 +1,2 @@ +#define PROT_BTI 0x10 +#define PROT_MTE 0x20 \ No newline at end of file diff --git a/lib/libc/include/aarch64-linux-musl/bits/syscall.h b/lib/libc/include/aarch64-linux-musl/bits/syscall.h index b15eb8bfd0..d2a4e40aa8 100644 --- a/lib/libc/include/aarch64-linux-musl/bits/syscall.h +++ b/lib/libc/include/aarch64-linux-musl/bits/syscall.h @@ -293,6 +293,12 @@ #define __NR_openat2 437 #define __NR_pidfd_getfd 438 #define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 #define SYS_io_setup 0 #define SYS_io_destroy 1 @@ -588,4 +594,10 @@ #define SYS_close_range 436 #define SYS_openat2 437 #define SYS_pidfd_getfd 438 -#define SYS_faccessat2 439 \ No newline at end of file +#define SYS_faccessat2 439 +#define SYS_process_madvise 440 +#define SYS_epoll_pwait2 441 +#define SYS_mount_setattr 442 +#define SYS_landlock_create_ruleset 444 +#define SYS_landlock_add_rule 445 +#define SYS_landlock_restrict_self 446 \ No newline at end of file diff --git a/lib/libc/include/arm-linux-musl/bits/syscall.h b/lib/libc/include/arm-linux-musl/bits/syscall.h index 479dd8a379..179b6df9f3 100644 --- a/lib/libc/include/arm-linux-musl/bits/syscall.h +++ b/lib/libc/include/arm-linux-musl/bits/syscall.h @@ -393,6 +393,12 @@ #define __NR_openat2 437 #define __NR_pidfd_getfd 438 #define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 #define __ARM_NR_breakpoint 0x0f0001 #define __ARM_NR_cacheflush 0x0f0002 @@ -795,4 +801,10 @@ #define SYS_close_range 436 #define SYS_openat2 437 #define SYS_pidfd_getfd 438 -#define SYS_faccessat2 439 \ No newline at end of file +#define SYS_faccessat2 439 +#define SYS_process_madvise 440 +#define SYS_epoll_pwait2 441 +#define SYS_mount_setattr 442 +#define SYS_landlock_create_ruleset 444 +#define SYS_landlock_add_rule 445 +#define SYS_landlock_restrict_self 446 \ No newline at end of file diff --git a/lib/libc/include/generic-musl/ctype.h b/lib/libc/include/generic-musl/ctype.h index 7005220cea..a991158a6d 100644 --- a/lib/libc/include/generic-musl/ctype.h +++ b/lib/libc/include/generic-musl/ctype.h @@ -64,7 +64,9 @@ int isascii(int); int toascii(int); #define _tolower(a) ((a)|0x20) #define _toupper(a) ((a)&0x5f) +#ifndef __cplusplus #define isascii(a) (0 ? isascii(a) : (unsigned)(a) < 128) +#endif #endif diff --git a/lib/libc/include/generic-musl/elf.h b/lib/libc/include/generic-musl/elf.h index ff7214ed48..6b2c83b6a8 100644 --- a/lib/libc/include/generic-musl/elf.h +++ b/lib/libc/include/generic-musl/elf.h @@ -686,6 +686,8 @@ typedef struct { #define NT_ARM_PAC_MASK 0x406 #define NT_ARM_PACA_KEYS 0x407 #define NT_ARM_PACG_KEYS 0x408 +#define NT_ARM_TAGGED_ADDR_CTRL 0x409 +#define NT_ARM_PAC_ENABLED_KEYS 0x40a #define NT_METAG_CBUF 0x500 #define NT_METAG_RPIPE 0x501 #define NT_METAG_TLS 0x502 diff --git a/lib/libc/include/generic-musl/locale.h b/lib/libc/include/generic-musl/locale.h index 61ffafb458..6328b5f058 100644 --- a/lib/libc/include/generic-musl/locale.h +++ b/lib/libc/include/generic-musl/locale.h @@ -7,7 +7,9 @@ extern "C" { #include -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/include/generic-musl/netinet/if_ether.h b/lib/libc/include/generic-musl/netinet/if_ether.h index d1c0a03570..f9169287f2 100644 --- a/lib/libc/include/generic-musl/netinet/if_ether.h +++ b/lib/libc/include/generic-musl/netinet/if_ether.h @@ -66,6 +66,7 @@ #define ETH_P_1588 0x88F7 #define ETH_P_NCSI 0x88F8 #define ETH_P_PRP 0x88FB +#define ETH_P_CFM 0x8902 #define ETH_P_FCOE 0x8906 #define ETH_P_TDLS 0x890D #define ETH_P_FIP 0x8914 diff --git a/lib/libc/include/generic-musl/netinet/in.h b/lib/libc/include/generic-musl/netinet/in.h index a57f3a6c7c..98202653c1 100644 --- a/lib/libc/include/generic-musl/netinet/in.h +++ b/lib/libc/include/generic-musl/netinet/in.h @@ -48,6 +48,7 @@ struct ipv6_mreq { #define INADDR_BROADCAST ((in_addr_t) 0xffffffff) #define INADDR_NONE ((in_addr_t) 0xffffffff) #define INADDR_LOOPBACK ((in_addr_t) 0x7f000001) +#define INADDR_DUMMY ((in_addr_t) 0xc0000008) #define INADDR_UNSPEC_GROUP ((in_addr_t) 0xe0000000) #define INADDR_ALLHOSTS_GROUP ((in_addr_t) 0xe0000001) diff --git a/lib/libc/include/generic-musl/netinet/tcp.h b/lib/libc/include/generic-musl/netinet/tcp.h index 9f514e33af..3eb212c61a 100644 --- a/lib/libc/include/generic-musl/netinet/tcp.h +++ b/lib/libc/include/generic-musl/netinet/tcp.h @@ -80,6 +80,8 @@ enum { TCP_NLA_SRTT, TCP_NLA_TIMEOUT_REHASH, TCP_NLA_BYTES_NOTSENT, + TCP_NLA_EDT, + TCP_NLA_TTL, }; #if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) @@ -281,12 +283,21 @@ struct tcp_repair_window { uint32_t rcv_wup; }; +#define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1 + struct tcp_zerocopy_receive { uint64_t address; uint32_t length; uint32_t recv_skip_hint; uint32_t inq; int32_t err; + uint64_t copybuf_address; + int32_t copybuf_len; + uint32_t flags; + uint64_t msg_control; + uint64_t msg_controllen; + uint32_t msg_flags; + uint32_t reserved; }; #endif diff --git a/lib/libc/include/generic-musl/pthread.h b/lib/libc/include/generic-musl/pthread.h index c31099ec7f..9552460b4c 100644 --- a/lib/libc/include/generic-musl/pthread.h +++ b/lib/libc/include/generic-musl/pthread.h @@ -221,6 +221,7 @@ int pthread_getaffinity_np(pthread_t, size_t, struct cpu_set_t *); int pthread_setaffinity_np(pthread_t, size_t, const struct cpu_set_t *); int pthread_getattr_np(pthread_t, pthread_attr_t *); int pthread_setname_np(pthread_t, const char *); +int pthread_getname_np(pthread_t, char *, size_t); int pthread_getattr_default_np(pthread_attr_t *); int pthread_setattr_default_np(const pthread_attr_t *); int pthread_tryjoin_np(pthread_t, void **); diff --git a/lib/libc/include/generic-musl/setjmp.h b/lib/libc/include/generic-musl/setjmp.h index e956f67302..2ccc2ac822 100644 --- a/lib/libc/include/generic-musl/setjmp.h +++ b/lib/libc/include/generic-musl/setjmp.h @@ -15,25 +15,33 @@ typedef struct __jmp_buf_tag { unsigned long __ss[128/sizeof(long)]; } jmp_buf[1]; +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) +#define __setjmp_attr __attribute__((__returns_twice__)) +#else +#define __setjmp_attr +#endif + #if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ || defined(_BSD_SOURCE) typedef jmp_buf sigjmp_buf; -int sigsetjmp (sigjmp_buf, int); +int sigsetjmp (sigjmp_buf, int) __setjmp_attr; _Noreturn void siglongjmp (sigjmp_buf, int); #endif #if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ || defined(_BSD_SOURCE) -int _setjmp (jmp_buf); +int _setjmp (jmp_buf) __setjmp_attr; _Noreturn void _longjmp (jmp_buf, int); #endif -int setjmp (jmp_buf); +int setjmp (jmp_buf) __setjmp_attr; _Noreturn void longjmp (jmp_buf, int); #define setjmp setjmp +#undef __setjmp_attr + #ifdef __cplusplus } #endif diff --git a/lib/libc/include/generic-musl/signal.h b/lib/libc/include/generic-musl/signal.h index 779f4f0609..1fafc54e6f 100644 --- a/lib/libc/include/generic-musl/signal.h +++ b/lib/libc/include/generic-musl/signal.h @@ -75,6 +75,8 @@ typedef struct sigaltstack stack_t; #define SEGV_ACCERR 2 #define SEGV_BNDERR 3 #define SEGV_PKUERR 4 +#define SEGV_MTEAERR 8 +#define SEGV_MTESERR 9 #define BUS_ADRALN 1 #define BUS_ADRERR 2 @@ -176,6 +178,9 @@ struct sigaction { #define sa_handler __sa_handler.sa_handler #define sa_sigaction __sa_handler.sa_sigaction +#define SA_UNSUPPORTED 0x00000400 +#define SA_EXPOSE_TAGBITS 0x00000800 + struct sigevent { union sigval sigev_value; int sigev_signo; @@ -259,6 +264,9 @@ void (*sigset(int, void (*)(int)))(int); #if defined(_BSD_SOURCE) || defined(_GNU_SOURCE) #define NSIG _NSIG typedef void (*sig_t)(int); + +#define SYS_SECCOMP 1 +#define SYS_USER_DISPATCH 2 #endif #ifdef _GNU_SOURCE diff --git a/lib/libc/include/generic-musl/stdc-predef.h b/lib/libc/include/generic-musl/stdc-predef.h index 2ed5e520c9..7f0f90dc81 100644 --- a/lib/libc/include/generic-musl/stdc-predef.h +++ b/lib/libc/include/generic-musl/stdc-predef.h @@ -7,4 +7,7 @@ #define __STDC_IEC_559__ 1 #endif +#define __STDC_UTF_16__ 1 +#define __STDC_UTF_32__ 1 + #endif \ No newline at end of file diff --git a/lib/libc/include/generic-musl/stddef.h b/lib/libc/include/generic-musl/stddef.h index 758cb66c99..08cd52271d 100644 --- a/lib/libc/include/generic-musl/stddef.h +++ b/lib/libc/include/generic-musl/stddef.h @@ -1,7 +1,9 @@ #ifndef _STDDEF_H #define _STDDEF_H -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/include/generic-musl/stdio.h b/lib/libc/include/generic-musl/stdio.h index 92f52bd27e..3b82c24fd3 100644 --- a/lib/libc/include/generic-musl/stdio.h +++ b/lib/libc/include/generic-musl/stdio.h @@ -25,7 +25,9 @@ extern "C" { #include -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/include/generic-musl/stdlib.h b/lib/libc/include/generic-musl/stdlib.h index ee777fc915..eb6daf49e8 100644 --- a/lib/libc/include/generic-musl/stdlib.h +++ b/lib/libc/include/generic-musl/stdlib.h @@ -7,7 +7,9 @@ extern "C" { #include -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) @@ -146,6 +148,7 @@ int clearenv(void); #define WCOREDUMP(s) ((s) & 0x80) #define WIFCONTINUED(s) ((s) == 0xffff) void *reallocarray (void *, size_t, size_t); +void qsort_r (void *, size_t, size_t, int (*)(const void *, const void *, void *), void *); #endif #ifdef _GNU_SOURCE diff --git a/lib/libc/include/generic-musl/string.h b/lib/libc/include/generic-musl/string.h index 3670e6e7a6..fdd4eb782d 100644 --- a/lib/libc/include/generic-musl/string.h +++ b/lib/libc/include/generic-musl/string.h @@ -7,7 +7,9 @@ extern "C" { #include -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/include/generic-musl/sys/membarrier.h b/lib/libc/include/generic-musl/sys/membarrier.h index b4559ee15e..73e8b2a19b 100644 --- a/lib/libc/include/generic-musl/sys/membarrier.h +++ b/lib/libc/include/generic-musl/sys/membarrier.h @@ -9,9 +9,13 @@ #define MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED 16 #define MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE 32 #define MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE 64 +#define MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ 128 +#define MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ 256 #define MEMBARRIER_CMD_SHARED MEMBARRIER_CMD_GLOBAL +#define MEMBARRIER_CMD_FLAG_CPU 1 + int membarrier(int, int); #endif \ No newline at end of file diff --git a/lib/libc/include/generic-musl/sys/mman.h b/lib/libc/include/generic-musl/sys/mman.h index a620fffea1..4f82dd1ecd 100644 --- a/lib/libc/include/generic-musl/sys/mman.h +++ b/lib/libc/include/generic-musl/sys/mman.h @@ -40,6 +40,7 @@ extern "C" { #define MAP_HUGE_SHIFT 26 #define MAP_HUGE_MASK 0x3f +#define MAP_HUGE_16KB (14 << 26) #define MAP_HUGE_64KB (16 << 26) #define MAP_HUGE_512KB (19 << 26) #define MAP_HUGE_1MB (20 << 26) diff --git a/lib/libc/include/generic-musl/sys/mount.h b/lib/libc/include/generic-musl/sys/mount.h index a3836f2ff2..fc2b8333a4 100644 --- a/lib/libc/include/generic-musl/sys/mount.h +++ b/lib/libc/include/generic-musl/sys/mount.h @@ -31,6 +31,7 @@ extern "C" { #define MS_REMOUNT 32 #define MS_MANDLOCK 64 #define MS_DIRSYNC 128 +#define MS_NOSYMFOLLOW 256 #define MS_NOATIME 1024 #define MS_NODIRATIME 2048 #define MS_BIND 4096 diff --git a/lib/libc/include/generic-musl/sys/prctl.h b/lib/libc/include/generic-musl/sys/prctl.h index bceebd8bcf..76c0658671 100644 --- a/lib/libc/include/generic-musl/sys/prctl.h +++ b/lib/libc/include/generic-musl/sys/prctl.h @@ -157,10 +157,26 @@ struct prctl_mm_map { #define PR_SET_TAGGED_ADDR_CTRL 55 #define PR_GET_TAGGED_ADDR_CTRL 56 #define PR_TAGGED_ADDR_ENABLE (1UL << 0) +#define PR_MTE_TCF_SHIFT 1 +#define PR_MTE_TCF_NONE (0UL << 1) +#define PR_MTE_TCF_SYNC (1UL << 1) +#define PR_MTE_TCF_ASYNC (2UL << 1) +#define PR_MTE_TCF_MASK (3UL << 1) +#define PR_MTE_TAG_SHIFT 3 +#define PR_MTE_TAG_MASK (0xffffUL << 3) #define PR_SET_IO_FLUSHER 57 #define PR_GET_IO_FLUSHER 58 +#define PR_SET_SYSCALL_USER_DISPATCH 59 +#define PR_SYS_DISPATCH_OFF 0 +#define PR_SYS_DISPATCH_ON 1 +#define SYSCALL_DISPATCH_FILTER_ALLOW 0 +#define SYSCALL_DISPATCH_FILTER_BLOCK 1 + +#define PR_PAC_SET_ENABLED_KEYS 60 +#define PR_PAC_GET_ENABLED_KEYS 61 + int prctl (int, ...); #ifdef __cplusplus diff --git a/lib/libc/include/generic-musl/sys/ptrace.h b/lib/libc/include/generic-musl/sys/ptrace.h index 1f0c6890fe..09f65cbcae 100644 --- a/lib/libc/include/generic-musl/sys/ptrace.h +++ b/lib/libc/include/generic-musl/sys/ptrace.h @@ -42,6 +42,7 @@ extern "C" { #define PTRACE_SECCOMP_GET_FILTER 0x420c #define PTRACE_SECCOMP_GET_METADATA 0x420d #define PTRACE_GET_SYSCALL_INFO 0x420e +#define PTRACE_GET_RSEQ_CONFIGURATION 0x420f #define PT_READ_I PTRACE_PEEKTEXT #define PT_READ_D PTRACE_PEEKDATA @@ -130,6 +131,14 @@ struct __ptrace_syscall_info { }; }; +struct __ptrace_rseq_configuration { + uint64_t rseq_abi_pointer; + uint32_t rseq_abi_size; + uint32_t signature; + uint32_t flags; + uint32_t pad; +}; + long ptrace(int, ...); #ifdef __cplusplus diff --git a/lib/libc/include/generic-musl/sys/socket.h b/lib/libc/include/generic-musl/sys/socket.h index 957d3da9f0..8308d578fc 100644 --- a/lib/libc/include/generic-musl/sys/socket.h +++ b/lib/libc/include/generic-musl/sys/socket.h @@ -289,6 +289,8 @@ struct linger { #define SCM_TXTIME SO_TXTIME #define SO_BINDTOIFINDEX 62 #define SO_DETACH_REUSEPORT_BPF 68 +#define SO_PREFER_BUSY_POLL 69 +#define SO_BUSY_POLL_BUDGET 70 #ifndef SOL_SOCKET #define SOL_SOCKET 1 diff --git a/lib/libc/include/generic-musl/time.h b/lib/libc/include/generic-musl/time.h index 5a9f61eb4d..a5bb2d2b8a 100644 --- a/lib/libc/include/generic-musl/time.h +++ b/lib/libc/include/generic-musl/time.h @@ -7,7 +7,9 @@ extern "C" { #include -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/include/generic-musl/unistd.h b/lib/libc/include/generic-musl/unistd.h index c6f5698b4e..cb9b96959d 100644 --- a/lib/libc/include/generic-musl/unistd.h +++ b/lib/libc/include/generic-musl/unistd.h @@ -14,8 +14,12 @@ extern "C" { #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 +#define SEEK_DATA 3 +#define SEEK_HOLE 4 -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/include/generic-musl/wchar.h b/lib/libc/include/generic-musl/wchar.h index 9c11e0c77f..2557778921 100644 --- a/lib/libc/include/generic-musl/wchar.h +++ b/lib/libc/include/generic-musl/wchar.h @@ -38,7 +38,9 @@ extern "C" { #define WCHAR_MIN (-1-0x7fffffff+L'\0') #endif -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/include/i386-linux-musl/bits/syscall.h b/lib/libc/include/i386-linux-musl/bits/syscall.h index 4a727513c1..37130cf3f0 100644 --- a/lib/libc/include/i386-linux-musl/bits/syscall.h +++ b/lib/libc/include/i386-linux-musl/bits/syscall.h @@ -430,6 +430,12 @@ #define __NR_openat2 437 #define __NR_pidfd_getfd 438 #define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 #define SYS_restart_syscall 0 #define SYS_exit 1 @@ -860,4 +866,10 @@ #define SYS_close_range 436 #define SYS_openat2 437 #define SYS_pidfd_getfd 438 -#define SYS_faccessat2 439 \ No newline at end of file +#define SYS_faccessat2 439 +#define SYS_process_madvise 440 +#define SYS_epoll_pwait2 441 +#define SYS_mount_setattr 442 +#define SYS_landlock_create_ruleset 444 +#define SYS_landlock_add_rule 445 +#define SYS_landlock_restrict_self 446 \ No newline at end of file diff --git a/lib/libc/include/m68k-linux-musl/bits/syscall.h b/lib/libc/include/m68k-linux-musl/bits/syscall.h index 01c5178ed8..b6b94a1fe5 100644 --- a/lib/libc/include/m68k-linux-musl/bits/syscall.h +++ b/lib/libc/include/m68k-linux-musl/bits/syscall.h @@ -410,6 +410,12 @@ #define __NR_openat2 437 #define __NR_pidfd_getfd 438 #define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 #define SYS_restart_syscall 0 #define SYS_exit 1 #define SYS_fork 2 @@ -821,4 +827,10 @@ #define SYS_close_range 436 #define SYS_openat2 437 #define SYS_pidfd_getfd 438 -#define SYS_faccessat2 439 \ No newline at end of file +#define SYS_faccessat2 439 +#define SYS_process_madvise 440 +#define SYS_epoll_pwait2 441 +#define SYS_mount_setattr 442 +#define SYS_landlock_create_ruleset 444 +#define SYS_landlock_add_rule 445 +#define SYS_landlock_restrict_self 446 \ No newline at end of file diff --git a/lib/libc/include/mips-linux-musl/bits/syscall.h b/lib/libc/include/mips-linux-musl/bits/syscall.h index 982dcca465..fffb6b9ce3 100644 --- a/lib/libc/include/mips-linux-musl/bits/syscall.h +++ b/lib/libc/include/mips-linux-musl/bits/syscall.h @@ -412,6 +412,12 @@ #define __NR_openat2 4437 #define __NR_pidfd_getfd 4438 #define __NR_faccessat2 4439 +#define __NR_process_madvise 4440 +#define __NR_epoll_pwait2 4441 +#define __NR_mount_setattr 4442 +#define __NR_landlock_create_ruleset 4444 +#define __NR_landlock_add_rule 4445 +#define __NR_landlock_restrict_self 4446 #define SYS_syscall 4000 #define SYS_exit 4001 @@ -826,4 +832,10 @@ #define SYS_close_range 4436 #define SYS_openat2 4437 #define SYS_pidfd_getfd 4438 -#define SYS_faccessat2 4439 \ No newline at end of file +#define SYS_faccessat2 4439 +#define SYS_process_madvise 4440 +#define SYS_epoll_pwait2 4441 +#define SYS_mount_setattr 4442 +#define SYS_landlock_create_ruleset 4444 +#define SYS_landlock_add_rule 4445 +#define SYS_landlock_restrict_self 4446 \ No newline at end of file diff --git a/lib/libc/include/mips64-linux-musl/bits/syscall.h b/lib/libc/include/mips64-linux-musl/bits/syscall.h index 27a8226b71..30803a8e15 100644 --- a/lib/libc/include/mips64-linux-musl/bits/syscall.h +++ b/lib/libc/include/mips64-linux-musl/bits/syscall.h @@ -342,6 +342,12 @@ #define __NR_openat2 5437 #define __NR_pidfd_getfd 5438 #define __NR_faccessat2 5439 +#define __NR_process_madvise 5440 +#define __NR_epoll_pwait2 5441 +#define __NR_mount_setattr 5442 +#define __NR_landlock_create_ruleset 5444 +#define __NR_landlock_add_rule 5445 +#define __NR_landlock_restrict_self 5446 #define SYS_read 5000 #define SYS_write 5001 @@ -686,4 +692,10 @@ #define SYS_close_range 5436 #define SYS_openat2 5437 #define SYS_pidfd_getfd 5438 -#define SYS_faccessat2 5439 \ No newline at end of file +#define SYS_faccessat2 5439 +#define SYS_process_madvise 5440 +#define SYS_epoll_pwait2 5441 +#define SYS_mount_setattr 5442 +#define SYS_landlock_create_ruleset 5444 +#define SYS_landlock_add_rule 5445 +#define SYS_landlock_restrict_self 5446 \ No newline at end of file diff --git a/lib/libc/include/powerpc-linux-musl/bits/fenv.h b/lib/libc/include/powerpc-linux-musl/bits/fenv.h index f5f4597255..b3e0ba5135 100644 --- a/lib/libc/include/powerpc-linux-musl/bits/fenv.h +++ b/lib/libc/include/powerpc-linux-musl/bits/fenv.h @@ -1,4 +1,4 @@ -#ifdef _SOFT_FLOAT +#if defined(_SOFT_FLOAT) || defined(__NO_FPRS__) #define FE_ALL_EXCEPT 0 #define FE_TONEAREST 0 #else diff --git a/lib/libc/include/powerpc-linux-musl/bits/shm.h b/lib/libc/include/powerpc-linux-musl/bits/shm.h index e45c2ab19a..dae173193e 100644 --- a/lib/libc/include/powerpc-linux-musl/bits/shm.h +++ b/lib/libc/include/powerpc-linux-musl/bits/shm.h @@ -8,11 +8,11 @@ struct shmid_ds { unsigned long __shm_dtime_lo; unsigned long __shm_ctime_hi; unsigned long __shm_ctime_lo; + unsigned long __pad1; size_t shm_segsz; pid_t shm_cpid; pid_t shm_lpid; unsigned long shm_nattch; - unsigned long __pad1; unsigned long __pad2; time_t shm_atime; time_t shm_dtime; diff --git a/lib/libc/include/powerpc-linux-musl/bits/syscall.h b/lib/libc/include/powerpc-linux-musl/bits/syscall.h index 3c935d00da..a309456d00 100644 --- a/lib/libc/include/powerpc-linux-musl/bits/syscall.h +++ b/lib/libc/include/powerpc-linux-musl/bits/syscall.h @@ -419,6 +419,12 @@ #define __NR_openat2 437 #define __NR_pidfd_getfd 438 #define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 #define SYS_restart_syscall 0 #define SYS_exit 1 @@ -840,4 +846,10 @@ #define SYS_close_range 436 #define SYS_openat2 437 #define SYS_pidfd_getfd 438 -#define SYS_faccessat2 439 \ No newline at end of file +#define SYS_faccessat2 439 +#define SYS_process_madvise 440 +#define SYS_epoll_pwait2 441 +#define SYS_mount_setattr 442 +#define SYS_landlock_create_ruleset 444 +#define SYS_landlock_add_rule 445 +#define SYS_landlock_restrict_self 446 \ No newline at end of file diff --git a/lib/libc/include/powerpc64-linux-musl/bits/syscall.h b/lib/libc/include/powerpc64-linux-musl/bits/syscall.h index de64c3ef30..4e7c41165b 100644 --- a/lib/libc/include/powerpc64-linux-musl/bits/syscall.h +++ b/lib/libc/include/powerpc64-linux-musl/bits/syscall.h @@ -391,6 +391,12 @@ #define __NR_openat2 437 #define __NR_pidfd_getfd 438 #define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 #define SYS_restart_syscall 0 #define SYS_exit 1 @@ -784,4 +790,10 @@ #define SYS_close_range 436 #define SYS_openat2 437 #define SYS_pidfd_getfd 438 -#define SYS_faccessat2 439 \ No newline at end of file +#define SYS_faccessat2 439 +#define SYS_process_madvise 440 +#define SYS_epoll_pwait2 441 +#define SYS_mount_setattr 442 +#define SYS_landlock_create_ruleset 444 +#define SYS_landlock_add_rule 445 +#define SYS_landlock_restrict_self 446 \ No newline at end of file diff --git a/lib/libc/include/riscv64-linux-musl/bits/syscall.h b/lib/libc/include/riscv64-linux-musl/bits/syscall.h index 27e10f5768..a19c2a5806 100644 --- a/lib/libc/include/riscv64-linux-musl/bits/syscall.h +++ b/lib/libc/include/riscv64-linux-musl/bits/syscall.h @@ -76,7 +76,7 @@ #define __NR_splice 76 #define __NR_tee 77 #define __NR_readlinkat 78 -#define __NR_fstatat 79 +#define __NR_newfstatat 79 #define __NR_fstat 80 #define __NR_sync 81 #define __NR_fsync 82 @@ -293,6 +293,12 @@ #define __NR_openat2 437 #define __NR_pidfd_getfd 438 #define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 #define __NR_sysriscv __NR_arch_specific_syscall #define __NR_riscv_flush_icache (__NR_sysriscv + 15) @@ -374,7 +380,7 @@ #define SYS_splice 76 #define SYS_tee 77 #define SYS_readlinkat 78 -#define SYS_fstatat 79 +#define SYS_newfstatat 79 #define SYS_fstat 80 #define SYS_sync 81 #define SYS_fsync 82 @@ -591,5 +597,11 @@ #define SYS_openat2 437 #define SYS_pidfd_getfd 438 #define SYS_faccessat2 439 +#define SYS_process_madvise 440 +#define SYS_epoll_pwait2 441 +#define SYS_mount_setattr 442 +#define SYS_landlock_create_ruleset 444 +#define SYS_landlock_add_rule 445 +#define SYS_landlock_restrict_self 446 #define SYS_sysriscv __NR_arch_specific_syscall #define SYS_riscv_flush_icache (__NR_sysriscv + 15) \ No newline at end of file diff --git a/lib/libc/include/riscv64-linux-musl/bits/user.h b/lib/libc/include/riscv64-linux-musl/bits/user.h index ffeab50d0d..93ddf49b50 100644 --- a/lib/libc/include/riscv64-linux-musl/bits/user.h +++ b/lib/libc/include/riscv64-linux-musl/bits/user.h @@ -1,5 +1,6 @@ #include #define ELF_NGREG 32 +#define ELF_NFPREG 33 typedef unsigned long elf_greg_t, elf_gregset_t[ELF_NGREG]; typedef union __riscv_mc_fp_state elf_fpregset_t; \ No newline at end of file diff --git a/lib/libc/include/s390x-linux-musl/bits/ptrace.h b/lib/libc/include/s390x-linux-musl/bits/ptrace.h index c30556e102..a47ef428f6 100644 --- a/lib/libc/include/s390x-linux-musl/bits/ptrace.h +++ b/lib/libc/include/s390x-linux-musl/bits/ptrace.h @@ -1,4 +1,7 @@ #define PTRACE_SINGLEBLOCK 12 +#define PTRACE_OLDSETOPTIONS 21 +#define PTRACE_SYSEMU 31 +#define PTRACE_SYSEMU_SINGLESTEP 32 #define PTRACE_PEEKUSR_AREA 0x5000 #define PTRACE_POKEUSR_AREA 0x5001 #define PTRACE_GET_LAST_BREAK 0x5006 diff --git a/lib/libc/include/s390x-linux-musl/bits/syscall.h b/lib/libc/include/s390x-linux-musl/bits/syscall.h index 2874c40203..90e978a74b 100644 --- a/lib/libc/include/s390x-linux-musl/bits/syscall.h +++ b/lib/libc/include/s390x-linux-musl/bits/syscall.h @@ -356,6 +356,12 @@ #define __NR_openat2 437 #define __NR_pidfd_getfd 438 #define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 #define SYS_exit 1 #define SYS_fork 2 @@ -714,4 +720,10 @@ #define SYS_close_range 436 #define SYS_openat2 437 #define SYS_pidfd_getfd 438 -#define SYS_faccessat2 439 \ No newline at end of file +#define SYS_faccessat2 439 +#define SYS_process_madvise 440 +#define SYS_epoll_pwait2 441 +#define SYS_mount_setattr 442 +#define SYS_landlock_create_ruleset 444 +#define SYS_landlock_add_rule 445 +#define SYS_landlock_restrict_self 446 \ No newline at end of file diff --git a/lib/libc/include/x86_64-linux-musl/bits/syscall.h b/lib/libc/include/x86_64-linux-musl/bits/syscall.h index d91f553b77..f9dbc02026 100644 --- a/lib/libc/include/x86_64-linux-musl/bits/syscall.h +++ b/lib/libc/include/x86_64-linux-musl/bits/syscall.h @@ -349,6 +349,12 @@ #define __NR_openat2 437 #define __NR_pidfd_getfd 438 #define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 #define SYS_read 0 #define SYS_write 1 @@ -700,4 +706,10 @@ #define SYS_close_range 436 #define SYS_openat2 437 #define SYS_pidfd_getfd 438 -#define SYS_faccessat2 439 \ No newline at end of file +#define SYS_faccessat2 439 +#define SYS_process_madvise 440 +#define SYS_epoll_pwait2 441 +#define SYS_mount_setattr 442 +#define SYS_landlock_create_ruleset 444 +#define SYS_landlock_add_rule 445 +#define SYS_landlock_restrict_self 446 \ No newline at end of file diff --git a/lib/libc/musl/COPYRIGHT b/lib/libc/musl/COPYRIGHT index e647237146..c1628e9ac8 100644 --- a/lib/libc/musl/COPYRIGHT +++ b/lib/libc/musl/COPYRIGHT @@ -127,10 +127,13 @@ Copyright © 2017-2018 Arm Limited and labelled as such in comments in the individual source files. All have been licensed under extremely permissive terms. -The ARM memcpy code (src/string/arm/memcpy_el.S) is Copyright © 2008 +The ARM memcpy code (src/string/arm/memcpy.S) is Copyright © 2008 The Android Open Source Project and is licensed under a two-clause BSD license. It was taken from Bionic libc, used on Android. +The AArch64 memcpy and memset code (src/string/aarch64/*) are +Copyright © 1999-2019, Arm Limited. + The implementation of DES for crypt (src/crypt/crypt_des.c) is Copyright © 1994 David Burren. It is licensed under a BSD license. diff --git a/lib/libc/musl/arch/aarch64/bits/hwcap.h b/lib/libc/musl/arch/aarch64/bits/hwcap.h index 7ab73f99b6..424cc4d4fc 100644 --- a/lib/libc/musl/arch/aarch64/bits/hwcap.h +++ b/lib/libc/musl/arch/aarch64/bits/hwcap.h @@ -48,3 +48,5 @@ #define HWCAP2_BF16 (1 << 14) #define HWCAP2_DGH (1 << 15) #define HWCAP2_RNG (1 << 16) +#define HWCAP2_BTI (1 << 17) +#define HWCAP2_MTE (1 << 18) diff --git a/lib/libc/musl/arch/aarch64/bits/mman.h b/lib/libc/musl/arch/aarch64/bits/mman.h new file mode 100644 index 0000000000..8fad5ceb0f --- /dev/null +++ b/lib/libc/musl/arch/aarch64/bits/mman.h @@ -0,0 +1,2 @@ +#define PROT_BTI 0x10 +#define PROT_MTE 0x20 diff --git a/lib/libc/musl/arch/aarch64/bits/syscall.h.in b/lib/libc/musl/arch/aarch64/bits/syscall.h.in index f9457c184a..5f420e6176 100644 --- a/lib/libc/musl/arch/aarch64/bits/syscall.h.in +++ b/lib/libc/musl/arch/aarch64/bits/syscall.h.in @@ -293,4 +293,10 @@ #define __NR_openat2 437 #define __NR_pidfd_getfd 438 #define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 diff --git a/lib/libc/musl/arch/arm/bits/syscall.h.in b/lib/libc/musl/arch/arm/bits/syscall.h.in index 7e2fc26697..048fdea797 100644 --- a/lib/libc/musl/arch/arm/bits/syscall.h.in +++ b/lib/libc/musl/arch/arm/bits/syscall.h.in @@ -393,6 +393,12 @@ #define __NR_openat2 437 #define __NR_pidfd_getfd 438 #define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 #define __ARM_NR_breakpoint 0x0f0001 #define __ARM_NR_cacheflush 0x0f0002 diff --git a/lib/libc/musl/arch/i386/arch.mak b/lib/libc/musl/arch/i386/arch.mak new file mode 100644 index 0000000000..aa4d05ceff --- /dev/null +++ b/lib/libc/musl/arch/i386/arch.mak @@ -0,0 +1 @@ +COMPAT_SRC_DIRS = compat/time32 diff --git a/lib/libc/musl/arch/i386/bits/syscall.h.in b/lib/libc/musl/arch/i386/bits/syscall.h.in index abdb210d39..46ffe1d99a 100644 --- a/lib/libc/musl/arch/i386/bits/syscall.h.in +++ b/lib/libc/musl/arch/i386/bits/syscall.h.in @@ -430,4 +430,10 @@ #define __NR_openat2 437 #define __NR_pidfd_getfd 438 #define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 diff --git a/lib/libc/musl/arch/m68k/arch.mak b/lib/libc/musl/arch/m68k/arch.mak new file mode 100644 index 0000000000..aa4d05ceff --- /dev/null +++ b/lib/libc/musl/arch/m68k/arch.mak @@ -0,0 +1 @@ +COMPAT_SRC_DIRS = compat/time32 diff --git a/lib/libc/musl/arch/m68k/atomic_arch.h b/lib/libc/musl/arch/m68k/atomic_arch.h new file mode 100644 index 0000000000..b369649a1a --- /dev/null +++ b/lib/libc/musl/arch/m68k/atomic_arch.h @@ -0,0 +1,8 @@ +#define a_cas a_cas +static inline int a_cas(volatile int *p, int t, int s) +{ + __asm__ __volatile__ ( + "cas.l %0, %2, (%1)" + : "+d"(t) : "a"(p), "d"(s) : "memory", "cc"); + return t; +} diff --git a/lib/libc/musl/arch/m68k/bits/alltypes.h.in b/lib/libc/musl/arch/m68k/bits/alltypes.h.in new file mode 100644 index 0000000000..f564690983 --- /dev/null +++ b/lib/libc/musl/arch/m68k/bits/alltypes.h.in @@ -0,0 +1,25 @@ +#define _REDIR_TIME64 1 +#define _Addr int +#define _Int64 long long +#define _Reg int + +#define __BYTE_ORDER 4321 +#define __LONG_MAX 0x7fffffffL + +#ifndef __cplusplus +#ifdef __WCHAR_TYPE__ +TYPEDEF __WCHAR_TYPE__ wchar_t; +#else +TYPEDEF long wchar_t; +#endif +#endif + +#if __mcffpu__ +TYPEDEF float float_t; +TYPEDEF double double_t; +#else +TYPEDEF long double float_t; +TYPEDEF long double double_t; +#endif + +TYPEDEF struct { long long __ll; long double __ld; } max_align_t; diff --git a/lib/libc/musl/arch/m68k/bits/fcntl.h b/lib/libc/musl/arch/m68k/bits/fcntl.h new file mode 100644 index 0000000000..f1c8400ffc --- /dev/null +++ b/lib/libc/musl/arch/m68k/bits/fcntl.h @@ -0,0 +1,40 @@ +#define O_CREAT 0100 +#define O_EXCL 0200 +#define O_NOCTTY 0400 +#define O_TRUNC 01000 +#define O_APPEND 02000 +#define O_NONBLOCK 04000 +#define O_DSYNC 010000 +#define O_SYNC 04010000 +#define O_RSYNC 04010000 +#define O_DIRECTORY 040000 +#define O_NOFOLLOW 0100000 +#define O_CLOEXEC 02000000 + +#define O_ASYNC 020000 +#define O_DIRECT 0200000 +#define O_LARGEFILE 0400000 +#define O_NOATIME 01000000 +#define O_PATH 010000000 +#define O_TMPFILE 020200000 +#define O_NDELAY O_NONBLOCK + +#define F_DUPFD 0 +#define F_GETFD 1 +#define F_SETFD 2 +#define F_GETFL 3 +#define F_SETFL 4 + +#define F_SETOWN 8 +#define F_GETOWN 9 +#define F_SETSIG 10 +#define F_GETSIG 11 + +#define F_GETLK 12 +#define F_SETLK 13 +#define F_SETLKW 14 + +#define F_SETOWN_EX 15 +#define F_GETOWN_EX 16 + +#define F_GETOWNER_UIDS 17 diff --git a/lib/libc/musl/arch/m68k/bits/fenv.h b/lib/libc/musl/arch/m68k/bits/fenv.h new file mode 100644 index 0000000000..c90a4a58af --- /dev/null +++ b/lib/libc/musl/arch/m68k/bits/fenv.h @@ -0,0 +1,29 @@ +#if __HAVE_68881__ || __mcffpu__ + +#define FE_INEXACT 8 +#define FE_DIVBYZERO 16 +#define FE_UNDERFLOW 32 +#define FE_OVERFLOW 64 +#define FE_INVALID 128 + +#define FE_ALL_EXCEPT 0xf8 + +#define FE_TONEAREST 0 +#define FE_TOWARDZERO 16 +#define FE_DOWNWARD 32 +#define FE_UPWARD 48 + +#else + +#define FE_ALL_EXCEPT 0 +#define FE_TONEAREST 0 + +#endif + +typedef unsigned fexcept_t; + +typedef struct { + unsigned __control_register, __status_register, __instruction_address; +} fenv_t; + +#define FE_DFL_ENV ((const fenv_t *) -1) diff --git a/lib/libc/musl/arch/m68k/bits/float.h b/lib/libc/musl/arch/m68k/bits/float.h new file mode 100644 index 0000000000..0e6899d583 --- /dev/null +++ b/lib/libc/musl/arch/m68k/bits/float.h @@ -0,0 +1,39 @@ +#if !__mcffpu__ + +#define FLT_EVAL_METHOD 2 + +#define LDBL_TRUE_MIN 3.6451995318824746025e-4951L +#define LDBL_MIN 1.68105157155604675313e-4932L +#define LDBL_MAX 1.1897314953572317650e+4932L +#define LDBL_EPSILON 1.0842021724855044340e-19L + +#define LDBL_MANT_DIG 64 +#define LDBL_MIN_EXP (-16382) +#define LDBL_MAX_EXP 16384 + +#define LDBL_DIG 18 +#define LDBL_MIN_10_EXP (-4931) +#define LDBL_MAX_10_EXP 4932 + +#define DECIMAL_DIG 21 + +#else + +#define FLT_EVAL_METHOD 0 + +#define LDBL_TRUE_MIN 4.94065645841246544177e-324L +#define LDBL_MIN 2.22507385850720138309e-308L +#define LDBL_MAX 1.79769313486231570815e+308L +#define LDBL_EPSILON 2.22044604925031308085e-16L + +#define LDBL_MANT_DIG 53 +#define LDBL_MIN_EXP (-1021) +#define LDBL_MAX_EXP 1024 + +#define LDBL_DIG 15 +#define LDBL_MIN_10_EXP (-307) +#define LDBL_MAX_10_EXP 308 + +#define DECIMAL_DIG 17 + +#endif diff --git a/lib/libc/musl/arch/m68k/bits/ipcstat.h b/lib/libc/musl/arch/m68k/bits/ipcstat.h new file mode 100644 index 0000000000..4f4fcb0c5b --- /dev/null +++ b/lib/libc/musl/arch/m68k/bits/ipcstat.h @@ -0,0 +1 @@ +#define IPC_STAT 0x102 diff --git a/lib/libc/musl/arch/m68k/bits/msg.h b/lib/libc/musl/arch/m68k/bits/msg.h new file mode 100644 index 0000000000..7bbbb2bf43 --- /dev/null +++ b/lib/libc/musl/arch/m68k/bits/msg.h @@ -0,0 +1,18 @@ +struct msqid_ds { + struct ipc_perm msg_perm; + unsigned long __msg_stime_lo; + unsigned long __msg_stime_hi; + unsigned long __msg_rtime_lo; + unsigned long __msg_rtime_hi; + unsigned long __msg_ctime_lo; + unsigned long __msg_ctime_hi; + unsigned long msg_cbytes; + msgqnum_t msg_qnum; + msglen_t msg_qbytes; + pid_t msg_lspid; + pid_t msg_lrpid; + unsigned long __unused[2]; + time_t msg_stime; + time_t msg_rtime; + time_t msg_ctime; +}; diff --git a/lib/libc/musl/arch/m68k/bits/posix.h b/lib/libc/musl/arch/m68k/bits/posix.h new file mode 100644 index 0000000000..30a38714f3 --- /dev/null +++ b/lib/libc/musl/arch/m68k/bits/posix.h @@ -0,0 +1,2 @@ +#define _POSIX_V6_ILP32_OFFBIG 1 +#define _POSIX_V7_ILP32_OFFBIG 1 diff --git a/lib/libc/musl/arch/m68k/bits/ptrace.h b/lib/libc/musl/arch/m68k/bits/ptrace.h new file mode 100644 index 0000000000..da93e7a729 --- /dev/null +++ b/lib/libc/musl/arch/m68k/bits/ptrace.h @@ -0,0 +1,2 @@ +#define PTRACE_GET_THREAD_AREA 25 +#define PTRACE_SINGLEBLOCK 33 diff --git a/lib/libc/musl/arch/m68k/bits/reg.h b/lib/libc/musl/arch/m68k/bits/reg.h new file mode 100644 index 0000000000..99201f7094 --- /dev/null +++ b/lib/libc/musl/arch/m68k/bits/reg.h @@ -0,0 +1,45 @@ +#undef __WORDSIZE +#define __WORDSIZE 32 +#define PT_D1 0 +#define PT_D2 1 +#define PT_D3 2 +#define PT_D4 3 +#define PT_D5 4 +#define PT_D6 5 +#define PT_D7 6 +#define PT_A0 7 +#define PT_A1 8 +#define PT_A2 9 +#define PT_A3 10 +#define PT_A4 11 +#define PT_A5 12 +#define PT_A6 13 +#define PT_D0 14 +#define PT_USP 15 +#define PT_ORIG_D0 16 +#define PT_SR 17 +#define PT_PC 18 + +#if __mcffpu__ +#define PT_FP0 21 +#define PT_FP1 23 +#define PT_FP2 25 +#define PT_FP3 27 +#define PT_FP4 29 +#define PT_FP5 31 +#define PT_FP6 33 +#define PT_FP7 35 +#else +#define PT_FP0 21 +#define PT_FP1 24 +#define PT_FP2 27 +#define PT_FP3 30 +#define PT_FP4 33 +#define PT_FP5 36 +#define PT_FP6 39 +#define PT_FP7 42 +#endif + +#define PT_FPCR 45 +#define PT_FPSR 46 +#define PT_FPIAR 47 diff --git a/lib/libc/musl/arch/m68k/bits/sem.h b/lib/libc/musl/arch/m68k/bits/sem.h new file mode 100644 index 0000000000..d88338e695 --- /dev/null +++ b/lib/libc/musl/arch/m68k/bits/sem.h @@ -0,0 +1,13 @@ +struct semid_ds { + struct ipc_perm sem_perm; + unsigned long __sem_otime_lo; + unsigned long __sem_otime_hi; + unsigned long __sem_ctime_lo; + unsigned long __sem_ctime_hi; + char __sem_nsems_pad[sizeof(long)-sizeof(short)]; + unsigned short sem_nsems; + long __unused3; + long __unused4; + time_t sem_otime; + time_t sem_ctime; +}; diff --git a/lib/libc/musl/arch/m68k/bits/setjmp.h b/lib/libc/musl/arch/m68k/bits/setjmp.h new file mode 100644 index 0000000000..5e091fb49b --- /dev/null +++ b/lib/libc/musl/arch/m68k/bits/setjmp.h @@ -0,0 +1 @@ +typedef unsigned long __jmp_buf[39]; diff --git a/lib/libc/musl/arch/m68k/bits/shm.h b/lib/libc/musl/arch/m68k/bits/shm.h new file mode 100644 index 0000000000..725fb46968 --- /dev/null +++ b/lib/libc/musl/arch/m68k/bits/shm.h @@ -0,0 +1,31 @@ +#define SHMLBA 4096 + +struct shmid_ds { + struct ipc_perm shm_perm; + size_t shm_segsz; + unsigned long __shm_atime_lo; + unsigned long __shm_atime_hi; + unsigned long __shm_dtime_lo; + unsigned long __shm_dtime_hi; + unsigned long __shm_ctime_lo; + unsigned long __shm_ctime_hi; + pid_t shm_cpid; + pid_t shm_lpid; + unsigned long shm_nattch; + unsigned long __pad1; + unsigned long __pad2; + unsigned long __pad3; + time_t shm_atime; + time_t shm_dtime; + time_t shm_ctime; +}; + +struct shminfo { + unsigned long shmmax, shmmin, shmmni, shmseg, shmall, __unused[4]; +}; + +struct shm_info { + int __used_ids; + unsigned long shm_tot, shm_rss, shm_swp; + unsigned long __swap_attempts, __swap_successes; +}; diff --git a/lib/libc/musl/arch/m68k/bits/signal.h b/lib/libc/musl/arch/m68k/bits/signal.h new file mode 100644 index 0000000000..2c369ca35c --- /dev/null +++ b/lib/libc/musl/arch/m68k/bits/signal.h @@ -0,0 +1,140 @@ +#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ + || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) + +#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE) +#define MINSIGSTKSZ 2048 +#define SIGSTKSZ 8192 +#endif + +#ifdef _GNU_SOURCE +enum { R_D0 = 0 }; +#define R_D0 R_D0 +enum { R_D1 = 1 }; +#define R_D1 R_D1 +enum { R_D2 = 2 }; +#define R_D2 R_D2 +enum { R_D3 = 3 }; +#define R_D3 R_D3 +enum { R_D4 = 4 }; +#define R_D4 R_D4 +enum { R_D5 = 5 }; +#define R_D5 R_D5 +enum { R_D6 = 6 }; +#define R_D6 R_D6 +enum { R_D7 = 7 }; +#define R_D7 R_D7 +enum { R_A0 = 8 }; +#define R_A0 R_A0 +enum { R_A1 = 9 }; +#define R_A1 R_A1 +enum { R_A2 = 10 }; +#define R_A2 R_A2 +enum { R_A3 = 11 }; +#define R_A3 R_A3 +enum { R_A4 = 12 }; +#define R_A4 R_A4 +enum { R_A5 = 13 }; +#define R_A5 R_A5 +enum { R_A6 = 14 }; +#define R_A6 R_A6 +enum { R_A7 = 15 }; +#define R_A7 R_A7 +enum { R_SP = 15 }; +#define R_SP R_SP +enum { R_PC = 16 }; +#define R_PC R_PC +enum { R_PS = 17 }; +#define R_PS R_PS +#endif + +#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) + +struct sigcontext { + unsigned long sc_mask, sc_usp, sc_d0, sc_d1, sc_a0, sc_a1; + unsigned short sc_sr; + unsigned long sc_pc; + unsigned short sc_formatvec; + unsigned long sc_fpregs[6], sc_fpcntl[3]; + unsigned char sc_fpstate[216]; +}; + +typedef int greg_t, gregset_t[18]; +typedef struct { + int f_pcr, f_psr, f_fpiaddr, f_fpregs[8][3]; +} fpregset_t; + +typedef struct { + int version; + gregset_t gregs; + fpregset_t fpregs; +} mcontext_t; +#else +typedef struct { + int __version; + int __gregs[18]; + int __fpregs[27]; +} mcontext_t; +#endif + +struct sigaltstack { + void *ss_sp; + int ss_flags; + size_t ss_size; +}; + +typedef struct __ucontext { + unsigned long uc_flags; + struct __ucontext *uc_link; + stack_t uc_stack; + mcontext_t uc_mcontext; + long __reserved[80]; + sigset_t uc_sigmask; +} ucontext_t; + +#define SA_NOCLDSTOP 1 +#define SA_NOCLDWAIT 2 +#define SA_SIGINFO 4 +#define SA_ONSTACK 0x08000000 +#define SA_RESTART 0x10000000 +#define SA_NODEFER 0x40000000 +#define SA_RESETHAND 0x80000000 +#define SA_RESTORER 0x04000000 + +#endif + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT SIGABRT +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGURG 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGPROF 27 +#define SIGWINCH 28 +#define SIGIO 29 +#define SIGPOLL 29 +#define SIGPWR 30 +#define SIGSYS 31 +#define SIGUNUSED SIGSYS + +#define _NSIG 65 diff --git a/lib/libc/musl/arch/m68k/bits/stat.h b/lib/libc/musl/arch/m68k/bits/stat.h new file mode 100644 index 0000000000..f87681471a --- /dev/null +++ b/lib/libc/musl/arch/m68k/bits/stat.h @@ -0,0 +1,25 @@ +/* copied from kernel definition, but with padding replaced + * by the corresponding correctly-sized userspace types. */ + +struct stat { + dev_t st_dev; + short __st_dev_padding; + long __st_ino_truncated; + mode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + dev_t st_rdev; + short __st_rdev_padding; + off_t st_size; + blksize_t st_blksize; + blkcnt_t st_blocks; + struct { + long tv_sec; + long tv_nsec; + } __st_atim32, __st_mtim32, __st_ctim32; + ino_t st_ino; + struct timespec st_atim; + struct timespec st_mtim; + struct timespec st_ctim; +}; diff --git a/lib/libc/musl/arch/m68k/bits/stdint.h b/lib/libc/musl/arch/m68k/bits/stdint.h new file mode 100644 index 0000000000..d1b2712199 --- /dev/null +++ b/lib/libc/musl/arch/m68k/bits/stdint.h @@ -0,0 +1,20 @@ +typedef int32_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef uint32_t uint_fast16_t; +typedef uint32_t uint_fast32_t; + +#define INT_FAST16_MIN INT32_MIN +#define INT_FAST32_MIN INT32_MIN + +#define INT_FAST16_MAX INT32_MAX +#define INT_FAST32_MAX INT32_MAX + +#define UINT_FAST16_MAX UINT32_MAX +#define UINT_FAST32_MAX UINT32_MAX + +#define INTPTR_MIN INT32_MIN +#define INTPTR_MAX INT32_MAX +#define UINTPTR_MAX UINT32_MAX +#define PTRDIFF_MIN INT32_MIN +#define PTRDIFF_MAX INT32_MAX +#define SIZE_MAX UINT32_MAX diff --git a/lib/libc/musl/arch/m68k/bits/syscall.h.in b/lib/libc/musl/arch/m68k/bits/syscall.h.in new file mode 100644 index 0000000000..a0c6332330 --- /dev/null +++ b/lib/libc/musl/arch/m68k/bits/syscall.h.in @@ -0,0 +1,418 @@ +#define __NR_restart_syscall 0 +#define __NR_exit 1 +#define __NR_fork 2 +#define __NR_read 3 +#define __NR_write 4 +#define __NR_open 5 +#define __NR_close 6 +#define __NR_waitpid 7 +#define __NR_creat 8 +#define __NR_link 9 +#define __NR_unlink 10 +#define __NR_execve 11 +#define __NR_chdir 12 +#define __NR_time 13 +#define __NR_mknod 14 +#define __NR_chmod 15 +#define __NR_chown 16 +#define __NR_oldstat 18 +#define __NR_lseek 19 +#define __NR_getpid 20 +#define __NR_mount 21 +#define __NR_umount 22 +#define __NR_setuid 23 +#define __NR_getuid 24 +#define __NR_stime 25 +#define __NR_ptrace 26 +#define __NR_alarm 27 +#define __NR_oldfstat 28 +#define __NR_pause 29 +#define __NR_utime 30 +#define __NR_access 33 +#define __NR_nice 34 +#define __NR_sync 36 +#define __NR_kill 37 +#define __NR_rename 38 +#define __NR_mkdir 39 +#define __NR_rmdir 40 +#define __NR_dup 41 +#define __NR_pipe 42 +#define __NR_times 43 +#define __NR_brk 45 +#define __NR_setgid 46 +#define __NR_getgid 47 +#define __NR_signal 48 +#define __NR_geteuid 49 +#define __NR_getegid 50 +#define __NR_acct 51 +#define __NR_umount2 52 +#define __NR_ioctl 54 +#define __NR_fcntl 55 +#define __NR_setpgid 57 +#define __NR_umask 60 +#define __NR_chroot 61 +#define __NR_ustat 62 +#define __NR_dup2 63 +#define __NR_getppid 64 +#define __NR_getpgrp 65 +#define __NR_setsid 66 +#define __NR_sigaction 67 +#define __NR_sgetmask 68 +#define __NR_ssetmask 69 +#define __NR_setreuid 70 +#define __NR_setregid 71 +#define __NR_sigsuspend 72 +#define __NR_sigpending 73 +#define __NR_sethostname 74 +#define __NR_setrlimit 75 +#define __NR_getrlimit 76 +#define __NR_getrusage 77 +#define __NR_gettimeofday_time32 78 +#define __NR_settimeofday_time32 79 +#define __NR_getgroups 80 +#define __NR_setgroups 81 +#define __NR_select 82 +#define __NR_symlink 83 +#define __NR_oldlstat 84 +#define __NR_readlink 85 +#define __NR_uselib 86 +#define __NR_swapon 87 +#define __NR_reboot 88 +#define __NR_readdir 89 +#define __NR_mmap 90 +#define __NR_munmap 91 +#define __NR_truncate 92 +#define __NR_ftruncate 93 +#define __NR_fchmod 94 +#define __NR_fchown 95 +#define __NR_getpriority 96 +#define __NR_setpriority 97 +#define __NR_statfs 99 +#define __NR_fstatfs 100 +#define __NR_socketcall 102 +#define __NR_syslog 103 +#define __NR_setitimer 104 +#define __NR_getitimer 105 +#define __NR_stat 106 +#define __NR_lstat 107 +#define __NR_fstat 108 +#define __NR_vhangup 111 +#define __NR_wait4 114 +#define __NR_swapoff 115 +#define __NR_sysinfo 116 +#define __NR_ipc 117 +#define __NR_fsync 118 +#define __NR_sigreturn 119 +#define __NR_clone 120 +#define __NR_setdomainname 121 +#define __NR_uname 122 +#define __NR_cacheflush 123 +#define __NR_adjtimex 124 +#define __NR_mprotect 125 +#define __NR_sigprocmask 126 +#define __NR_create_module 127 +#define __NR_init_module 128 +#define __NR_delete_module 129 +#define __NR_get_kernel_syms 130 +#define __NR_quotactl 131 +#define __NR_getpgid 132 +#define __NR_fchdir 133 +#define __NR_bdflush 134 +#define __NR_sysfs 135 +#define __NR_personality 136 +#define __NR_setfsuid 138 +#define __NR_setfsgid 139 +#define __NR__llseek 140 +#define __NR_getdents 141 +#define __NR__newselect 142 +#define __NR_flock 143 +#define __NR_msync 144 +#define __NR_readv 145 +#define __NR_writev 146 +#define __NR_getsid 147 +#define __NR_fdatasync 148 +#define __NR__sysctl 149 +#define __NR_mlock 150 +#define __NR_munlock 151 +#define __NR_mlockall 152 +#define __NR_munlockall 153 +#define __NR_sched_setparam 154 +#define __NR_sched_getparam 155 +#define __NR_sched_setscheduler 156 +#define __NR_sched_getscheduler 157 +#define __NR_sched_yield 158 +#define __NR_sched_get_priority_max 159 +#define __NR_sched_get_priority_min 160 +#define __NR_sched_rr_get_interval 161 +#define __NR_nanosleep 162 +#define __NR_mremap 163 +#define __NR_setresuid 164 +#define __NR_getresuid 165 +#define __NR_getpagesize 166 +#define __NR_query_module 167 +#define __NR_poll 168 +#define __NR_nfsservctl 169 +#define __NR_setresgid 170 +#define __NR_getresgid 171 +#define __NR_prctl 172 +#define __NR_rt_sigreturn 173 +#define __NR_rt_sigaction 174 +#define __NR_rt_sigprocmask 175 +#define __NR_rt_sigpending 176 +#define __NR_rt_sigtimedwait 177 +#define __NR_rt_sigqueueinfo 178 +#define __NR_rt_sigsuspend 179 +#define __NR_pread64 180 +#define __NR_pwrite64 181 +#define __NR_lchown 182 +#define __NR_getcwd 183 +#define __NR_capget 184 +#define __NR_capset 185 +#define __NR_sigaltstack 186 +#define __NR_sendfile 187 +#define __NR_getpmsg 188 +#define __NR_putpmsg 189 +#define __NR_vfork 190 +#define __NR_ugetrlimit 191 +#define __NR_mmap2 192 +#define __NR_truncate64 193 +#define __NR_ftruncate64 194 +#define __NR_stat64 195 +#define __NR_lstat64 196 +#define __NR_fstat64 197 +#define __NR_chown32 198 +#define __NR_getuid32 199 +#define __NR_getgid32 200 +#define __NR_geteuid32 201 +#define __NR_getegid32 202 +#define __NR_setreuid32 203 +#define __NR_setregid32 204 +#define __NR_getgroups32 205 +#define __NR_setgroups32 206 +#define __NR_fchown32 207 +#define __NR_setresuid32 208 +#define __NR_getresuid32 209 +#define __NR_setresgid32 210 +#define __NR_getresgid32 211 +#define __NR_lchown32 212 +#define __NR_setuid32 213 +#define __NR_setgid32 214 +#define __NR_setfsuid32 215 +#define __NR_setfsgid32 216 +#define __NR_pivot_root 217 +#define __NR_getdents64 220 +#define __NR_gettid 221 +#define __NR_tkill 222 +#define __NR_setxattr 223 +#define __NR_lsetxattr 224 +#define __NR_fsetxattr 225 +#define __NR_getxattr 226 +#define __NR_lgetxattr 227 +#define __NR_fgetxattr 228 +#define __NR_listxattr 229 +#define __NR_llistxattr 230 +#define __NR_flistxattr 231 +#define __NR_removexattr 232 +#define __NR_lremovexattr 233 +#define __NR_fremovexattr 234 +#define __NR_futex 235 +#define __NR_sendfile64 236 +#define __NR_mincore 237 +#define __NR_madvise 238 +#define __NR_fcntl64 239 +#define __NR_readahead 240 +#define __NR_io_setup 241 +#define __NR_io_destroy 242 +#define __NR_io_getevents 243 +#define __NR_io_submit 244 +#define __NR_io_cancel 245 +#define __NR_fadvise64 246 +#define __NR_exit_group 247 +#define __NR_lookup_dcookie 248 +#define __NR_epoll_create 249 +#define __NR_epoll_ctl 250 +#define __NR_epoll_wait 251 +#define __NR_remap_file_pages 252 +#define __NR_set_tid_address 253 +#define __NR_timer_create 254 +#define __NR_timer_settime32 255 +#define __NR_timer_gettime32 256 +#define __NR_timer_getoverrun 257 +#define __NR_timer_delete 258 +#define __NR_clock_settime32 259 +#define __NR_clock_gettime32 260 +#define __NR_clock_getres_time32 261 +#define __NR_clock_nanosleep_time32 262 +#define __NR_statfs64 263 +#define __NR_fstatfs64 264 +#define __NR_tgkill 265 +#define __NR_utimes 266 +#define __NR_fadvise64_64 267 +#define __NR_mbind 268 +#define __NR_get_mempolicy 269 +#define __NR_set_mempolicy 270 +#define __NR_mq_open 271 +#define __NR_mq_unlink 272 +#define __NR_mq_timedsend 273 +#define __NR_mq_timedreceive 274 +#define __NR_mq_notify 275 +#define __NR_mq_getsetattr 276 +#define __NR_waitid 277 +#define __NR_add_key 279 +#define __NR_request_key 280 +#define __NR_keyctl 281 +#define __NR_ioprio_set 282 +#define __NR_ioprio_get 283 +#define __NR_inotify_init 284 +#define __NR_inotify_add_watch 285 +#define __NR_inotify_rm_watch 286 +#define __NR_migrate_pages 287 +#define __NR_openat 288 +#define __NR_mkdirat 289 +#define __NR_mknodat 290 +#define __NR_fchownat 291 +#define __NR_futimesat 292 +#define __NR_fstatat64 293 +#define __NR_unlinkat 294 +#define __NR_renameat 295 +#define __NR_linkat 296 +#define __NR_symlinkat 297 +#define __NR_readlinkat 298 +#define __NR_fchmodat 299 +#define __NR_faccessat 300 +#define __NR_pselect6 301 +#define __NR_ppoll 302 +#define __NR_unshare 303 +#define __NR_set_robust_list 304 +#define __NR_get_robust_list 305 +#define __NR_splice 306 +#define __NR_sync_file_range 307 +#define __NR_tee 308 +#define __NR_vmsplice 309 +#define __NR_move_pages 310 +#define __NR_sched_setaffinity 311 +#define __NR_sched_getaffinity 312 +#define __NR_kexec_load 313 +#define __NR_getcpu 314 +#define __NR_epoll_pwait 315 +#define __NR_utimensat 316 +#define __NR_signalfd 317 +#define __NR_timerfd_create 318 +#define __NR_eventfd 319 +#define __NR_fallocate 320 +#define __NR_timerfd_settime32 321 +#define __NR_timerfd_gettime32 322 +#define __NR_signalfd4 323 +#define __NR_eventfd2 324 +#define __NR_epoll_create1 325 +#define __NR_dup3 326 +#define __NR_pipe2 327 +#define __NR_inotify_init1 328 +#define __NR_preadv 329 +#define __NR_pwritev 330 +#define __NR_rt_tgsigqueueinfo 331 +#define __NR_perf_event_open 332 +#define __NR_get_thread_area 333 +#define __NR_set_thread_area 334 +#define __NR_atomic_cmpxchg_32 335 +#define __NR_atomic_barrier 336 +#define __NR_fanotify_init 337 +#define __NR_fanotify_mark 338 +#define __NR_prlimit64 339 +#define __NR_name_to_handle_at 340 +#define __NR_open_by_handle_at 341 +#define __NR_clock_adjtime 342 +#define __NR_syncfs 343 +#define __NR_setns 344 +#define __NR_process_vm_readv 345 +#define __NR_process_vm_writev 346 +#define __NR_kcmp 347 +#define __NR_finit_module 348 +#define __NR_sched_setattr 349 +#define __NR_sched_getattr 350 +#define __NR_renameat2 351 +#define __NR_getrandom 352 +#define __NR_memfd_create 353 +#define __NR_bpf 354 +#define __NR_execveat 355 +#define __NR_socket 356 +#define __NR_socketpair 357 +#define __NR_bind 358 +#define __NR_connect 359 +#define __NR_listen 360 +#define __NR_accept4 361 +#define __NR_getsockopt 362 +#define __NR_setsockopt 363 +#define __NR_getsockname 364 +#define __NR_getpeername 365 +#define __NR_sendto 366 +#define __NR_sendmsg 367 +#define __NR_recvfrom 368 +#define __NR_recvmsg 369 +#define __NR_shutdown 370 +#define __NR_recvmmsg 371 +#define __NR_sendmmsg 372 +#define __NR_userfaultfd 373 +#define __NR_membarrier 374 +#define __NR_mlock2 375 +#define __NR_copy_file_range 376 +#define __NR_preadv2 377 +#define __NR_pwritev2 378 +#define __NR_statx 379 +#define __NR_seccomp 380 +#define __NR_pkey_mprotect 381 +#define __NR_pkey_alloc 382 +#define __NR_pkey_free 383 +#define __NR_rseq 384 +#define __NR_semget 393 +#define __NR_semctl 394 +#define __NR_shmget 395 +#define __NR_shmctl 396 +#define __NR_shmat 397 +#define __NR_shmdt 398 +#define __NR_msgget 399 +#define __NR_msgsnd 400 +#define __NR_msgrcv 401 +#define __NR_msgctl 402 +#define __NR_clock_gettime64 403 +#define __NR_clock_settime64 404 +#define __NR_clock_adjtime64 405 +#define __NR_clock_getres_time64 406 +#define __NR_clock_nanosleep_time64 407 +#define __NR_timer_gettime64 408 +#define __NR_timer_settime64 409 +#define __NR_timerfd_gettime64 410 +#define __NR_timerfd_settime64 411 +#define __NR_utimensat_time64 412 +#define __NR_pselect6_time64 413 +#define __NR_ppoll_time64 414 +#define __NR_io_pgetevents_time64 416 +#define __NR_recvmmsg_time64 417 +#define __NR_mq_timedsend_time64 418 +#define __NR_mq_timedreceive_time64 419 +#define __NR_semtimedop_time64 420 +#define __NR_rt_sigtimedwait_time64 421 +#define __NR_futex_time64 422 +#define __NR_sched_rr_get_interval_time64 423 +#define __NR_pidfd_send_signal 424 +#define __NR_io_uring_setup 425 +#define __NR_io_uring_enter 426 +#define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 +#define __NR_pidfd_open 434 +#define __NR_clone3 435 +#define __NR_close_range 436 +#define __NR_openat2 437 +#define __NR_pidfd_getfd 438 +#define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 diff --git a/lib/libc/musl/arch/m68k/bits/user.h b/lib/libc/musl/arch/m68k/bits/user.h new file mode 100644 index 0000000000..6a4439196b --- /dev/null +++ b/lib/libc/musl/arch/m68k/bits/user.h @@ -0,0 +1,38 @@ +#undef __WORDSIZE +#define __WORDSIZE 32 + +struct user_m68kfp_struct { + unsigned long fpregs[24], fpcntl[3]; +}; + +struct user_regs_struct { + long d1, d2, d3, d4, d5, d6, d7; + long a0, a1, a2, a3, a4, a5, a6; + long d0, usp, orig_d0; + short stkadj, sr; + long pc; + short fmtvec, __pad; +}; + +struct user { + struct user_regs_struct regs; + int u_fpvalid; + struct user_m68kfp_struct m68kfp; + unsigned long u_tsize, u_dsize, u_ssize, start_code, start_stack; + long signal; + int reserved; + unsigned long u_ar0; + struct user_m68kfp_struct *u_fpstate; + unsigned long magic; + char u_comm[32]; +}; + +#define ELF_NGREG 20 +typedef unsigned long elf_greg_t; +typedef elf_greg_t elf_gregset_t[ELF_NGREG]; +typedef struct user_m68kfp_struct elf_fpregset_t; + +#define NBPG 4096 +#define UPAGES 1 +#define HOST_TEXT_START_ADDR (u.start_code) +#define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG) diff --git a/lib/libc/musl/arch/m68k/crt_arch.h b/lib/libc/musl/arch/m68k/crt_arch.h new file mode 100644 index 0000000000..48a42f29e2 --- /dev/null +++ b/lib/libc/musl/arch/m68k/crt_arch.h @@ -0,0 +1,14 @@ +__asm__( +".text\n" +".weak _DYNAMIC \n" +".hidden _DYNAMIC \n" +".global " START "\n" +START ":\n" +" suba.l %fp,%fp \n" +" movea.l %sp,%a0 \n" +" lea _DYNAMIC-.-8,%a1 \n" +" pea (%pc,%a1) \n" +" pea (%a0) \n" +" lea " START "_c-.-8,%a1 \n" +" jsr (%pc,%a1) \n" +); diff --git a/lib/libc/musl/arch/m68k/kstat.h b/lib/libc/musl/arch/m68k/kstat.h new file mode 100644 index 0000000000..ac13e272ce --- /dev/null +++ b/lib/libc/musl/arch/m68k/kstat.h @@ -0,0 +1,21 @@ +struct kstat { + dev_t st_dev; + short __st_dev_padding; + long __st_ino_truncated; + mode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + dev_t st_rdev; + short __st_rdev_padding; + off_t st_size; + blksize_t st_blksize; + blkcnt_t st_blocks; + long st_atime_sec; + long st_atime_nsec; + long st_mtime_sec; + long st_mtime_nsec; + long st_ctime_sec; + long st_ctime_nsec; + ino_t st_ino; +}; diff --git a/lib/libc/musl/arch/m68k/pthread_arch.h b/lib/libc/musl/arch/m68k/pthread_arch.h new file mode 100644 index 0000000000..5bea4e1adc --- /dev/null +++ b/lib/libc/musl/arch/m68k/pthread_arch.h @@ -0,0 +1,12 @@ +static inline uintptr_t __get_tp() +{ + return __syscall(SYS_get_thread_area); +} + +#define TLS_ABOVE_TP +#define GAP_ABOVE_TP 0 + +#define TP_OFFSET 0x7000 +#define DTP_OFFSET 0x8000 + +#define MC_PC gregs[R_PC] diff --git a/lib/libc/musl/arch/m68k/reloc.h b/lib/libc/musl/arch/m68k/reloc.h new file mode 100644 index 0000000000..f920b39e03 --- /dev/null +++ b/lib/libc/musl/arch/m68k/reloc.h @@ -0,0 +1,30 @@ +#if __HAVE_68881__ +#define FP_SUFFIX "" +#elif __mcffpu__ +#define FP_SUFFIX "-fp64" +#else +#define FP_SUFFIX "-sf" +#endif + +#define LDSO_ARCH "m68k" FP_SUFFIX + +#define TPOFF_K (-0x7000) + +#define REL_SYMBOLIC R_68K_32 +#define REL_OFFSET R_68K_PC32 +#define REL_GOT R_68K_GLOB_DAT +#define REL_PLT R_68K_JMP_SLOT +#define REL_RELATIVE R_68K_RELATIVE +#define REL_COPY R_68K_COPY +#define REL_DTPMOD R_68K_TLS_DTPMOD32 +#define REL_DTPOFF R_68K_TLS_DTPREL32 +#define REL_TPOFF R_68K_TLS_TPREL32 + +#define CRTJMP(pc,sp) __asm__ __volatile__( \ + "move.l %1,%%sp ; jmp (%0)" : : "r"(pc), "r"(sp) : "memory" ) + +#define GETFUNCSYM(fp, sym, got) __asm__ ( \ + ".hidden " #sym "\n" \ + "lea " #sym "-.-8,%0 \n" \ + "lea (%%pc,%0),%0 \n" \ + : "=a"(*fp) : : "memory" ) diff --git a/lib/libc/musl/arch/m68k/syscall_arch.h b/lib/libc/musl/arch/m68k/syscall_arch.h new file mode 100644 index 0000000000..6a9d0ae8e4 --- /dev/null +++ b/lib/libc/musl/arch/m68k/syscall_arch.h @@ -0,0 +1,90 @@ +#define __SYSCALL_LL_E(x) \ +((union { long long ll; long l[2]; }){ .ll = x }).l[0], \ +((union { long long ll; long l[2]; }){ .ll = x }).l[1] +#define __SYSCALL_LL_O(x) __SYSCALL_LL_E((x)) + +static __inline long __syscall0(long n) +{ + register unsigned long d0 __asm__("d0") = n; + __asm__ __volatile__ ("trap #0" : "+r"(d0) + : + : "memory"); + return d0; +} + +static inline long __syscall1(long n, long a) +{ + register unsigned long d0 __asm__("d0") = n; + register unsigned long d1 __asm__("d1") = a; + __asm__ __volatile__ ("trap #0" : "+r"(d0) + : "r"(d1) + : "memory"); + return d0; +} + +static inline long __syscall2(long n, long a, long b) +{ + register unsigned long d0 __asm__("d0") = n; + register unsigned long d1 __asm__("d1") = a; + register unsigned long d2 __asm__("d2") = b; + __asm__ __volatile__ ("trap #0" : "+r"(d0) + : "r"(d1), "r"(d2) + : "memory"); + return d0; +} + +static inline long __syscall3(long n, long a, long b, long c) +{ + register unsigned long d0 __asm__("d0") = n; + register unsigned long d1 __asm__("d1") = a; + register unsigned long d2 __asm__("d2") = b; + register unsigned long d3 __asm__("d3") = c; + __asm__ __volatile__ ("trap #0" : "+r"(d0) + : "r"(d1), "r"(d2), "r"(d3) + : "memory"); + return d0; +} + +static inline long __syscall4(long n, long a, long b, long c, long d) +{ + register unsigned long d0 __asm__("d0") = n; + register unsigned long d1 __asm__("d1") = a; + register unsigned long d2 __asm__("d2") = b; + register unsigned long d3 __asm__("d3") = c; + register unsigned long d4 __asm__("d4") = d; + __asm__ __volatile__ ("trap #0" : "+r"(d0) + : "r"(d1), "r"(d2), "r"(d3), "r"(d4) + : "memory"); + return d0; +} + +static inline long __syscall5(long n, long a, long b, long c, long d, long e) +{ + register unsigned long d0 __asm__("d0") = n; + register unsigned long d1 __asm__("d1") = a; + register unsigned long d2 __asm__("d2") = b; + register unsigned long d3 __asm__("d3") = c; + register unsigned long d4 __asm__("d4") = d; + register unsigned long d5 __asm__("d5") = e; + __asm__ __volatile__ ("trap #0" : "+r"(d0) + : "r"(d1), "r"(d2), "r"(d3), "r"(d4), "r"(d5) + : "memory"); + return d0; +} + +static inline long __syscall6(long n, long a, long b, long c, long d, long e, long f) +{ + register unsigned long d0 __asm__("d0") = n; + register unsigned long d1 __asm__("d1") = a; + register unsigned long d2 __asm__("d2") = b; + register unsigned long d3 __asm__("d3") = c; + register unsigned long d4 __asm__("d4") = d; + register unsigned long d5 __asm__("d5") = e; + register unsigned long a0 __asm__("a0") = f; + __asm__ __volatile__ ("trap #0" : "+r"(d0) + : "r"(d1), "r"(d2), "r"(d3), "r"(d4), "r"(d5), "r"(a0) + : "memory"); + return d0; +} + +#define SYSCALL_IPC_BROKEN_MODE diff --git a/lib/libc/musl/arch/mips/arch.mak b/lib/libc/musl/arch/mips/arch.mak new file mode 100644 index 0000000000..aa4d05ceff --- /dev/null +++ b/lib/libc/musl/arch/mips/arch.mak @@ -0,0 +1 @@ +COMPAT_SRC_DIRS = compat/time32 diff --git a/lib/libc/musl/arch/mips/bits/syscall.h.in b/lib/libc/musl/arch/mips/bits/syscall.h.in index 2bb03f067a..63e3503a6b 100644 --- a/lib/libc/musl/arch/mips/bits/syscall.h.in +++ b/lib/libc/musl/arch/mips/bits/syscall.h.in @@ -412,4 +412,10 @@ #define __NR_openat2 4437 #define __NR_pidfd_getfd 4438 #define __NR_faccessat2 4439 +#define __NR_process_madvise 4440 +#define __NR_epoll_pwait2 4441 +#define __NR_mount_setattr 4442 +#define __NR_landlock_create_ruleset 4444 +#define __NR_landlock_add_rule 4445 +#define __NR_landlock_restrict_self 4446 diff --git a/lib/libc/musl/arch/mips/pthread_arch.h b/lib/libc/musl/arch/mips/pthread_arch.h index c45347ab92..376b77415a 100644 --- a/lib/libc/musl/arch/mips/pthread_arch.h +++ b/lib/libc/musl/arch/mips/pthread_arch.h @@ -1,10 +1,9 @@ static inline uintptr_t __get_tp() { -#if __mips_isa_rev < 2 register uintptr_t tp __asm__("$3"); +#if __mips_isa_rev < 2 __asm__ (".word 0x7c03e83b" : "=r" (tp) ); #else - uintptr_t tp; __asm__ ("rdhwr %0, $29" : "=r" (tp) ); #endif return tp; diff --git a/lib/libc/musl/arch/mips64/bits/syscall.h.in b/lib/libc/musl/arch/mips64/bits/syscall.h.in index 045e8238ad..b89965d11a 100644 --- a/lib/libc/musl/arch/mips64/bits/syscall.h.in +++ b/lib/libc/musl/arch/mips64/bits/syscall.h.in @@ -342,4 +342,10 @@ #define __NR_openat2 5437 #define __NR_pidfd_getfd 5438 #define __NR_faccessat2 5439 +#define __NR_process_madvise 5440 +#define __NR_epoll_pwait2 5441 +#define __NR_mount_setattr 5442 +#define __NR_landlock_create_ruleset 5444 +#define __NR_landlock_add_rule 5445 +#define __NR_landlock_restrict_self 5446 diff --git a/lib/libc/musl/arch/powerpc/arch.mak b/lib/libc/musl/arch/powerpc/arch.mak new file mode 100644 index 0000000000..aa4d05ceff --- /dev/null +++ b/lib/libc/musl/arch/powerpc/arch.mak @@ -0,0 +1 @@ +COMPAT_SRC_DIRS = compat/time32 diff --git a/lib/libc/musl/arch/powerpc/bits/fenv.h b/lib/libc/musl/arch/powerpc/bits/fenv.h index c5a3e5c5c7..5b15c69a3a 100644 --- a/lib/libc/musl/arch/powerpc/bits/fenv.h +++ b/lib/libc/musl/arch/powerpc/bits/fenv.h @@ -1,4 +1,4 @@ -#ifdef _SOFT_FLOAT +#if defined(_SOFT_FLOAT) || defined(__NO_FPRS__) #define FE_ALL_EXCEPT 0 #define FE_TONEAREST 0 #else diff --git a/lib/libc/musl/arch/powerpc/bits/shm.h b/lib/libc/musl/arch/powerpc/bits/shm.h index fb1d4020f6..7f1ca17ebf 100644 --- a/lib/libc/musl/arch/powerpc/bits/shm.h +++ b/lib/libc/musl/arch/powerpc/bits/shm.h @@ -8,11 +8,11 @@ struct shmid_ds { unsigned long __shm_dtime_lo; unsigned long __shm_ctime_hi; unsigned long __shm_ctime_lo; + unsigned long __pad1; size_t shm_segsz; pid_t shm_cpid; pid_t shm_lpid; unsigned long shm_nattch; - unsigned long __pad1; unsigned long __pad2; time_t shm_atime; time_t shm_dtime; diff --git a/lib/libc/musl/arch/powerpc/bits/syscall.h.in b/lib/libc/musl/arch/powerpc/bits/syscall.h.in index 5c6fae3e58..b1605a58f1 100644 --- a/lib/libc/musl/arch/powerpc/bits/syscall.h.in +++ b/lib/libc/musl/arch/powerpc/bits/syscall.h.in @@ -419,4 +419,10 @@ #define __NR_openat2 437 #define __NR_pidfd_getfd 438 #define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 diff --git a/lib/libc/musl/arch/powerpc/reloc.h b/lib/libc/musl/arch/powerpc/reloc.h index 527b6b7cdc..fdfbf827e8 100644 --- a/lib/libc/musl/arch/powerpc/reloc.h +++ b/lib/libc/musl/arch/powerpc/reloc.h @@ -1,4 +1,4 @@ -#ifdef _SOFT_FLOAT +#if defined(_SOFT_FLOAT) || defined(__NO_FPRS__) #define FP_SUFFIX "-sf" #else #define FP_SUFFIX "" diff --git a/lib/libc/musl/arch/powerpc64/bits/syscall.h.in b/lib/libc/musl/arch/powerpc64/bits/syscall.h.in index edf73d3d6b..b3a8fba092 100644 --- a/lib/libc/musl/arch/powerpc64/bits/syscall.h.in +++ b/lib/libc/musl/arch/powerpc64/bits/syscall.h.in @@ -391,4 +391,10 @@ #define __NR_openat2 437 #define __NR_pidfd_getfd 438 #define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 diff --git a/lib/libc/musl/arch/riscv64/bits/syscall.h.in b/lib/libc/musl/arch/riscv64/bits/syscall.h.in index 5def016b12..b534afe81c 100644 --- a/lib/libc/musl/arch/riscv64/bits/syscall.h.in +++ b/lib/libc/musl/arch/riscv64/bits/syscall.h.in @@ -76,7 +76,7 @@ #define __NR_splice 76 #define __NR_tee 77 #define __NR_readlinkat 78 -#define __NR_fstatat 79 +#define __NR_newfstatat 79 #define __NR_fstat 80 #define __NR_sync 81 #define __NR_fsync 82 @@ -293,6 +293,12 @@ #define __NR_openat2 437 #define __NR_pidfd_getfd 438 #define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 #define __NR_sysriscv __NR_arch_specific_syscall #define __NR_riscv_flush_icache (__NR_sysriscv + 15) diff --git a/lib/libc/musl/arch/riscv64/bits/user.h b/lib/libc/musl/arch/riscv64/bits/user.h index 2da743eaf8..0d37de0b04 100644 --- a/lib/libc/musl/arch/riscv64/bits/user.h +++ b/lib/libc/musl/arch/riscv64/bits/user.h @@ -1,5 +1,6 @@ #include #define ELF_NGREG 32 +#define ELF_NFPREG 33 typedef unsigned long elf_greg_t, elf_gregset_t[ELF_NGREG]; typedef union __riscv_mc_fp_state elf_fpregset_t; diff --git a/lib/libc/musl/arch/s390x/bits/ptrace.h b/lib/libc/musl/arch/s390x/bits/ptrace.h index d50e326211..a06cb0778f 100644 --- a/lib/libc/musl/arch/s390x/bits/ptrace.h +++ b/lib/libc/musl/arch/s390x/bits/ptrace.h @@ -1,4 +1,7 @@ #define PTRACE_SINGLEBLOCK 12 +#define PTRACE_OLDSETOPTIONS 21 +#define PTRACE_SYSEMU 31 +#define PTRACE_SYSEMU_SINGLESTEP 32 #define PTRACE_PEEKUSR_AREA 0x5000 #define PTRACE_POKEUSR_AREA 0x5001 #define PTRACE_GET_LAST_BREAK 0x5006 diff --git a/lib/libc/musl/arch/s390x/bits/syscall.h.in b/lib/libc/musl/arch/s390x/bits/syscall.h.in index fb2e60e30b..dfc384792a 100644 --- a/lib/libc/musl/arch/s390x/bits/syscall.h.in +++ b/lib/libc/musl/arch/s390x/bits/syscall.h.in @@ -356,4 +356,10 @@ #define __NR_openat2 437 #define __NR_pidfd_getfd 438 #define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 diff --git a/lib/libc/musl/arch/x86_64/bits/syscall.h.in b/lib/libc/musl/arch/x86_64/bits/syscall.h.in index a611795104..c3882de7e7 100644 --- a/lib/libc/musl/arch/x86_64/bits/syscall.h.in +++ b/lib/libc/musl/arch/x86_64/bits/syscall.h.in @@ -349,4 +349,10 @@ #define __NR_openat2 437 #define __NR_pidfd_getfd 438 #define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 diff --git a/lib/libc/musl/include/ctype.h b/lib/libc/musl/include/ctype.h index 7936536f57..32bcef4dab 100644 --- a/lib/libc/musl/include/ctype.h +++ b/lib/libc/musl/include/ctype.h @@ -64,7 +64,9 @@ int isascii(int); int toascii(int); #define _tolower(a) ((a)|0x20) #define _toupper(a) ((a)&0x5f) +#ifndef __cplusplus #define isascii(a) (0 ? isascii(a) : (unsigned)(a) < 128) +#endif #endif diff --git a/lib/libc/musl/include/elf.h b/lib/libc/musl/include/elf.h index b5e7befb02..86e2f0bb7d 100644 --- a/lib/libc/musl/include/elf.h +++ b/lib/libc/musl/include/elf.h @@ -686,6 +686,8 @@ typedef struct { #define NT_ARM_PAC_MASK 0x406 #define NT_ARM_PACA_KEYS 0x407 #define NT_ARM_PACG_KEYS 0x408 +#define NT_ARM_TAGGED_ADDR_CTRL 0x409 +#define NT_ARM_PAC_ENABLED_KEYS 0x40a #define NT_METAG_CBUF 0x500 #define NT_METAG_RPIPE 0x501 #define NT_METAG_TLS 0x502 diff --git a/lib/libc/musl/include/locale.h b/lib/libc/musl/include/locale.h index ce384381c6..11106fea87 100644 --- a/lib/libc/musl/include/locale.h +++ b/lib/libc/musl/include/locale.h @@ -7,7 +7,9 @@ extern "C" { #include -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/musl/include/netinet/if_ether.h b/lib/libc/musl/include/netinet/if_ether.h index 55a2ff1b17..3479f511bf 100644 --- a/lib/libc/musl/include/netinet/if_ether.h +++ b/lib/libc/musl/include/netinet/if_ether.h @@ -66,6 +66,7 @@ #define ETH_P_1588 0x88F7 #define ETH_P_NCSI 0x88F8 #define ETH_P_PRP 0x88FB +#define ETH_P_CFM 0x8902 #define ETH_P_FCOE 0x8906 #define ETH_P_TDLS 0x890D #define ETH_P_FIP 0x8914 diff --git a/lib/libc/musl/include/netinet/in.h b/lib/libc/musl/include/netinet/in.h index f9594339f0..fb628b61a9 100644 --- a/lib/libc/musl/include/netinet/in.h +++ b/lib/libc/musl/include/netinet/in.h @@ -48,6 +48,7 @@ struct ipv6_mreq { #define INADDR_BROADCAST ((in_addr_t) 0xffffffff) #define INADDR_NONE ((in_addr_t) 0xffffffff) #define INADDR_LOOPBACK ((in_addr_t) 0x7f000001) +#define INADDR_DUMMY ((in_addr_t) 0xc0000008) #define INADDR_UNSPEC_GROUP ((in_addr_t) 0xe0000000) #define INADDR_ALLHOSTS_GROUP ((in_addr_t) 0xe0000001) diff --git a/lib/libc/musl/include/netinet/tcp.h b/lib/libc/musl/include/netinet/tcp.h index b7b997f5fd..fad1d84494 100644 --- a/lib/libc/musl/include/netinet/tcp.h +++ b/lib/libc/musl/include/netinet/tcp.h @@ -80,6 +80,8 @@ enum { TCP_NLA_SRTT, TCP_NLA_TIMEOUT_REHASH, TCP_NLA_BYTES_NOTSENT, + TCP_NLA_EDT, + TCP_NLA_TTL, }; #if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) @@ -281,12 +283,21 @@ struct tcp_repair_window { uint32_t rcv_wup; }; +#define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1 + struct tcp_zerocopy_receive { uint64_t address; uint32_t length; uint32_t recv_skip_hint; uint32_t inq; int32_t err; + uint64_t copybuf_address; + int32_t copybuf_len; + uint32_t flags; + uint64_t msg_control; + uint64_t msg_controllen; + uint32_t msg_flags; + uint32_t reserved; }; #endif diff --git a/lib/libc/musl/include/pthread.h b/lib/libc/musl/include/pthread.h index 0492f26a6b..89fd9ff7c1 100644 --- a/lib/libc/musl/include/pthread.h +++ b/lib/libc/musl/include/pthread.h @@ -221,6 +221,7 @@ int pthread_getaffinity_np(pthread_t, size_t, struct cpu_set_t *); int pthread_setaffinity_np(pthread_t, size_t, const struct cpu_set_t *); int pthread_getattr_np(pthread_t, pthread_attr_t *); int pthread_setname_np(pthread_t, const char *); +int pthread_getname_np(pthread_t, char *, size_t); int pthread_getattr_default_np(pthread_attr_t *); int pthread_setattr_default_np(const pthread_attr_t *); int pthread_tryjoin_np(pthread_t, void **); diff --git a/lib/libc/musl/include/setjmp.h b/lib/libc/musl/include/setjmp.h index 2d43abf84f..1976af231b 100644 --- a/lib/libc/musl/include/setjmp.h +++ b/lib/libc/musl/include/setjmp.h @@ -15,25 +15,33 @@ typedef struct __jmp_buf_tag { unsigned long __ss[128/sizeof(long)]; } jmp_buf[1]; +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) +#define __setjmp_attr __attribute__((__returns_twice__)) +#else +#define __setjmp_attr +#endif + #if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ || defined(_BSD_SOURCE) typedef jmp_buf sigjmp_buf; -int sigsetjmp (sigjmp_buf, int); +int sigsetjmp (sigjmp_buf, int) __setjmp_attr; _Noreturn void siglongjmp (sigjmp_buf, int); #endif #if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ || defined(_BSD_SOURCE) -int _setjmp (jmp_buf); +int _setjmp (jmp_buf) __setjmp_attr; _Noreturn void _longjmp (jmp_buf, int); #endif -int setjmp (jmp_buf); +int setjmp (jmp_buf) __setjmp_attr; _Noreturn void longjmp (jmp_buf, int); #define setjmp setjmp +#undef __setjmp_attr + #ifdef __cplusplus } #endif diff --git a/lib/libc/musl/include/signal.h b/lib/libc/musl/include/signal.h index 9ed929e4f2..c347f8610a 100644 --- a/lib/libc/musl/include/signal.h +++ b/lib/libc/musl/include/signal.h @@ -75,6 +75,8 @@ typedef struct sigaltstack stack_t; #define SEGV_ACCERR 2 #define SEGV_BNDERR 3 #define SEGV_PKUERR 4 +#define SEGV_MTEAERR 8 +#define SEGV_MTESERR 9 #define BUS_ADRALN 1 #define BUS_ADRERR 2 @@ -176,6 +178,9 @@ struct sigaction { #define sa_handler __sa_handler.sa_handler #define sa_sigaction __sa_handler.sa_sigaction +#define SA_UNSUPPORTED 0x00000400 +#define SA_EXPOSE_TAGBITS 0x00000800 + struct sigevent { union sigval sigev_value; int sigev_signo; @@ -259,6 +264,9 @@ void (*sigset(int, void (*)(int)))(int); #if defined(_BSD_SOURCE) || defined(_GNU_SOURCE) #define NSIG _NSIG typedef void (*sig_t)(int); + +#define SYS_SECCOMP 1 +#define SYS_USER_DISPATCH 2 #endif #ifdef _GNU_SOURCE diff --git a/lib/libc/musl/include/stdc-predef.h b/lib/libc/musl/include/stdc-predef.h index f8cd4b8911..af1a27998f 100644 --- a/lib/libc/musl/include/stdc-predef.h +++ b/lib/libc/musl/include/stdc-predef.h @@ -7,4 +7,7 @@ #define __STDC_IEC_559__ 1 #endif +#define __STDC_UTF_16__ 1 +#define __STDC_UTF_32__ 1 + #endif diff --git a/lib/libc/musl/include/stddef.h b/lib/libc/musl/include/stddef.h index bd75385350..f25b86396e 100644 --- a/lib/libc/musl/include/stddef.h +++ b/lib/libc/musl/include/stddef.h @@ -1,7 +1,9 @@ #ifndef _STDDEF_H #define _STDDEF_H -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/musl/include/stdio.h b/lib/libc/musl/include/stdio.h index 3604198c3e..d1ed01f03f 100644 --- a/lib/libc/musl/include/stdio.h +++ b/lib/libc/musl/include/stdio.h @@ -25,7 +25,9 @@ extern "C" { #include -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/musl/include/stdlib.h b/lib/libc/musl/include/stdlib.h index b54a051fe9..b507ca33b4 100644 --- a/lib/libc/musl/include/stdlib.h +++ b/lib/libc/musl/include/stdlib.h @@ -7,7 +7,9 @@ extern "C" { #include -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) @@ -146,6 +148,7 @@ int clearenv(void); #define WCOREDUMP(s) ((s) & 0x80) #define WIFCONTINUED(s) ((s) == 0xffff) void *reallocarray (void *, size_t, size_t); +void qsort_r (void *, size_t, size_t, int (*)(const void *, const void *, void *), void *); #endif #ifdef _GNU_SOURCE diff --git a/lib/libc/musl/include/string.h b/lib/libc/musl/include/string.h index 795a2abcd9..43ad0942ed 100644 --- a/lib/libc/musl/include/string.h +++ b/lib/libc/musl/include/string.h @@ -7,7 +7,9 @@ extern "C" { #include -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/musl/include/sys/membarrier.h b/lib/libc/musl/include/sys/membarrier.h index 10cb31083c..11193eda15 100644 --- a/lib/libc/musl/include/sys/membarrier.h +++ b/lib/libc/musl/include/sys/membarrier.h @@ -9,9 +9,13 @@ #define MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED 16 #define MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE 32 #define MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE 64 +#define MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ 128 +#define MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ 256 #define MEMBARRIER_CMD_SHARED MEMBARRIER_CMD_GLOBAL +#define MEMBARRIER_CMD_FLAG_CPU 1 + int membarrier(int, int); #endif diff --git a/lib/libc/musl/include/sys/mman.h b/lib/libc/musl/include/sys/mman.h index 4d603e9104..80a3baae23 100644 --- a/lib/libc/musl/include/sys/mman.h +++ b/lib/libc/musl/include/sys/mman.h @@ -40,6 +40,7 @@ extern "C" { #define MAP_HUGE_SHIFT 26 #define MAP_HUGE_MASK 0x3f +#define MAP_HUGE_16KB (14 << 26) #define MAP_HUGE_64KB (16 << 26) #define MAP_HUGE_512KB (19 << 26) #define MAP_HUGE_1MB (20 << 26) diff --git a/lib/libc/musl/include/sys/mount.h b/lib/libc/musl/include/sys/mount.h index 57a89c09ec..09bd6e9dfe 100644 --- a/lib/libc/musl/include/sys/mount.h +++ b/lib/libc/musl/include/sys/mount.h @@ -31,6 +31,7 @@ extern "C" { #define MS_REMOUNT 32 #define MS_MANDLOCK 64 #define MS_DIRSYNC 128 +#define MS_NOSYMFOLLOW 256 #define MS_NOATIME 1024 #define MS_NODIRATIME 2048 #define MS_BIND 4096 diff --git a/lib/libc/musl/include/sys/prctl.h b/lib/libc/musl/include/sys/prctl.h index 4b9fcc0508..087a75c9da 100644 --- a/lib/libc/musl/include/sys/prctl.h +++ b/lib/libc/musl/include/sys/prctl.h @@ -157,10 +157,26 @@ struct prctl_mm_map { #define PR_SET_TAGGED_ADDR_CTRL 55 #define PR_GET_TAGGED_ADDR_CTRL 56 #define PR_TAGGED_ADDR_ENABLE (1UL << 0) +#define PR_MTE_TCF_SHIFT 1 +#define PR_MTE_TCF_NONE (0UL << 1) +#define PR_MTE_TCF_SYNC (1UL << 1) +#define PR_MTE_TCF_ASYNC (2UL << 1) +#define PR_MTE_TCF_MASK (3UL << 1) +#define PR_MTE_TAG_SHIFT 3 +#define PR_MTE_TAG_MASK (0xffffUL << 3) #define PR_SET_IO_FLUSHER 57 #define PR_GET_IO_FLUSHER 58 +#define PR_SET_SYSCALL_USER_DISPATCH 59 +#define PR_SYS_DISPATCH_OFF 0 +#define PR_SYS_DISPATCH_ON 1 +#define SYSCALL_DISPATCH_FILTER_ALLOW 0 +#define SYSCALL_DISPATCH_FILTER_BLOCK 1 + +#define PR_PAC_SET_ENABLED_KEYS 60 +#define PR_PAC_GET_ENABLED_KEYS 61 + int prctl (int, ...); #ifdef __cplusplus diff --git a/lib/libc/musl/include/sys/ptrace.h b/lib/libc/musl/include/sys/ptrace.h index 5d62a9859a..c72e3c061c 100644 --- a/lib/libc/musl/include/sys/ptrace.h +++ b/lib/libc/musl/include/sys/ptrace.h @@ -42,6 +42,7 @@ extern "C" { #define PTRACE_SECCOMP_GET_FILTER 0x420c #define PTRACE_SECCOMP_GET_METADATA 0x420d #define PTRACE_GET_SYSCALL_INFO 0x420e +#define PTRACE_GET_RSEQ_CONFIGURATION 0x420f #define PT_READ_I PTRACE_PEEKTEXT #define PT_READ_D PTRACE_PEEKDATA @@ -130,6 +131,14 @@ struct __ptrace_syscall_info { }; }; +struct __ptrace_rseq_configuration { + uint64_t rseq_abi_pointer; + uint32_t rseq_abi_size; + uint32_t signature; + uint32_t flags; + uint32_t pad; +}; + long ptrace(int, ...); #ifdef __cplusplus diff --git a/lib/libc/musl/include/sys/socket.h b/lib/libc/musl/include/sys/socket.h index 38f5bb17b3..6dc1e40adf 100644 --- a/lib/libc/musl/include/sys/socket.h +++ b/lib/libc/musl/include/sys/socket.h @@ -289,6 +289,8 @@ struct linger { #define SCM_TXTIME SO_TXTIME #define SO_BINDTOIFINDEX 62 #define SO_DETACH_REUSEPORT_BPF 68 +#define SO_PREFER_BUSY_POLL 69 +#define SO_BUSY_POLL_BUDGET 70 #ifndef SOL_SOCKET #define SOL_SOCKET 1 diff --git a/lib/libc/musl/include/time.h b/lib/libc/musl/include/time.h index 5494df1836..3d94837205 100644 --- a/lib/libc/musl/include/time.h +++ b/lib/libc/musl/include/time.h @@ -7,7 +7,9 @@ extern "C" { #include -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/musl/include/unistd.h b/lib/libc/musl/include/unistd.h index 1306402603..212263a7e8 100644 --- a/lib/libc/musl/include/unistd.h +++ b/lib/libc/musl/include/unistd.h @@ -14,8 +14,12 @@ extern "C" { #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 +#define SEEK_DATA 3 +#define SEEK_HOLE 4 -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/musl/include/wchar.h b/lib/libc/musl/include/wchar.h index 88eb55b18c..ed5d774dfa 100644 --- a/lib/libc/musl/include/wchar.h +++ b/lib/libc/musl/include/wchar.h @@ -38,7 +38,9 @@ extern "C" { #define WCHAR_MIN (-1-0x7fffffff+L'\0') #endif -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/musl/libc.S b/lib/libc/musl/libc.S index 14970ca0ea..e24603b6e1 100644 --- a/lib/libc/musl/libc.S +++ b/lib/libc/musl/libc.S @@ -27,48 +27,48 @@ aio_read64: .globl aio_read .type aio_read, %function; aio_read: -.globl aio_write -.type aio_write, %function; -aio_write: .weak aio_write64 .type aio_write64, %function; aio_write64: -.globl aio_fsync -.type aio_fsync, %function; -aio_fsync: +.globl aio_write +.type aio_write, %function; +aio_write: .weak aio_fsync64 .type aio_fsync64, %function; aio_fsync64: -.weak aio_return64 -.type aio_return64, %function; -aio_return64: +.globl aio_fsync +.type aio_fsync, %function; +aio_fsync: .globl aio_return .type aio_return, %function; aio_return: -.globl aio_error -.type aio_error, %function; -aio_error: +.weak aio_return64 +.type aio_return64, %function; +aio_return64: .weak aio_error64 .type aio_error64, %function; aio_error64: -.globl aio_cancel -.type aio_cancel, %function; -aio_cancel: +.globl aio_error +.type aio_error, %function; +aio_error: .weak aio_cancel64 .type aio_cancel64, %function; aio_cancel64: -.globl aio_suspend -.type aio_suspend, %function; -aio_suspend: +.globl aio_cancel +.type aio_cancel, %function; +aio_cancel: .weak aio_suspend64 .type aio_suspend64, %function; aio_suspend64: -.globl lio_listio -.type lio_listio, %function; -lio_listio: +.globl aio_suspend +.type aio_suspend, %function; +aio_suspend: .weak lio_listio64 .type lio_listio64, %function; lio_listio64: +.globl lio_listio +.type lio_listio, %function; +lio_listio: .globl cabs .type cabs, %function; cabs: @@ -327,12 +327,12 @@ isalnum_l: .globl isalpha .type isalpha, %function; isalpha: -.weak isalpha_l -.type isalpha_l, %function; -isalpha_l: .globl __isalpha_l .type __isalpha_l, %function; __isalpha_l: +.weak isalpha_l +.type isalpha_l, %function; +isalpha_l: .globl isascii .type isascii, %function; isascii: @@ -348,21 +348,21 @@ isblank_l: .globl iscntrl .type iscntrl, %function; iscntrl: -.weak iscntrl_l -.type iscntrl_l, %function; -iscntrl_l: .globl __iscntrl_l .type __iscntrl_l, %function; __iscntrl_l: +.weak iscntrl_l +.type iscntrl_l, %function; +iscntrl_l: .globl isdigit .type isdigit, %function; isdigit: -.weak isdigit_l -.type isdigit_l, %function; -isdigit_l: .globl __isdigit_l .type __isdigit_l, %function; __isdigit_l: +.weak isdigit_l +.type isdigit_l, %function; +isdigit_l: .globl isgraph .type isgraph, %function; isgraph: @@ -375,96 +375,96 @@ isgraph_l: .globl islower .type islower, %function; islower: -.weak islower_l -.type islower_l, %function; -islower_l: .globl __islower_l .type __islower_l, %function; __islower_l: +.weak islower_l +.type islower_l, %function; +islower_l: .globl isprint .type isprint, %function; isprint: -.weak isprint_l -.type isprint_l, %function; -isprint_l: .globl __isprint_l .type __isprint_l, %function; __isprint_l: +.weak isprint_l +.type isprint_l, %function; +isprint_l: .globl ispunct .type ispunct, %function; ispunct: -.weak ispunct_l -.type ispunct_l, %function; -ispunct_l: .globl __ispunct_l .type __ispunct_l, %function; __ispunct_l: +.weak ispunct_l +.type ispunct_l, %function; +ispunct_l: .globl isspace .type isspace, %function; isspace: -.weak isspace_l -.type isspace_l, %function; -isspace_l: .globl __isspace_l .type __isspace_l, %function; __isspace_l: +.weak isspace_l +.type isspace_l, %function; +isspace_l: .globl isupper .type isupper, %function; isupper: -.weak isupper_l -.type isupper_l, %function; -isupper_l: .globl __isupper_l .type __isupper_l, %function; __isupper_l: +.weak isupper_l +.type isupper_l, %function; +isupper_l: .globl iswalnum .type iswalnum, %function; iswalnum: -.globl __iswalnum_l -.type __iswalnum_l, %function; -__iswalnum_l: .weak iswalnum_l .type iswalnum_l, %function; iswalnum_l: +.globl __iswalnum_l +.type __iswalnum_l, %function; +__iswalnum_l: .globl iswalpha .type iswalpha, %function; iswalpha: -.globl __iswalpha_l -.type __iswalpha_l, %function; -__iswalpha_l: .weak iswalpha_l .type iswalpha_l, %function; iswalpha_l: +.globl __iswalpha_l +.type __iswalpha_l, %function; +__iswalpha_l: .globl iswblank .type iswblank, %function; iswblank: -.globl __iswblank_l -.type __iswblank_l, %function; -__iswblank_l: .weak iswblank_l .type iswblank_l, %function; iswblank_l: +.globl __iswblank_l +.type __iswblank_l, %function; +__iswblank_l: .globl iswcntrl .type iswcntrl, %function; iswcntrl: -.globl __iswcntrl_l -.type __iswcntrl_l, %function; -__iswcntrl_l: .weak iswcntrl_l .type iswcntrl_l, %function; iswcntrl_l: +.globl __iswcntrl_l +.type __iswcntrl_l, %function; +__iswcntrl_l: .globl iswctype .type iswctype, %function; iswctype: .globl wctype .type wctype, %function; wctype: -.globl __iswctype_l -.type __iswctype_l, %function; -__iswctype_l: .weak iswctype_l .type iswctype_l, %function; iswctype_l: +.globl __iswctype_l +.type __iswctype_l, %function; +__iswctype_l: .globl __wctype_l .type __wctype_l, %function; __wctype_l: @@ -474,21 +474,21 @@ wctype_l: .globl iswdigit .type iswdigit, %function; iswdigit: -.globl __iswdigit_l -.type __iswdigit_l, %function; -__iswdigit_l: .weak iswdigit_l .type iswdigit_l, %function; iswdigit_l: +.globl __iswdigit_l +.type __iswdigit_l, %function; +__iswdigit_l: .globl iswgraph .type iswgraph, %function; iswgraph: -.globl __iswgraph_l -.type __iswgraph_l, %function; -__iswgraph_l: .weak iswgraph_l .type iswgraph_l, %function; iswgraph_l: +.globl __iswgraph_l +.type __iswgraph_l, %function; +__iswgraph_l: .globl iswlower .type iswlower, %function; iswlower: @@ -501,57 +501,57 @@ __iswlower_l: .globl iswprint .type iswprint, %function; iswprint: -.globl __iswprint_l -.type __iswprint_l, %function; -__iswprint_l: .weak iswprint_l .type iswprint_l, %function; iswprint_l: +.globl __iswprint_l +.type __iswprint_l, %function; +__iswprint_l: .globl iswpunct .type iswpunct, %function; iswpunct: -.globl __iswpunct_l -.type __iswpunct_l, %function; -__iswpunct_l: .weak iswpunct_l .type iswpunct_l, %function; iswpunct_l: +.globl __iswpunct_l +.type __iswpunct_l, %function; +__iswpunct_l: .globl iswspace .type iswspace, %function; iswspace: -.globl __iswspace_l -.type __iswspace_l, %function; -__iswspace_l: .weak iswspace_l .type iswspace_l, %function; iswspace_l: +.globl __iswspace_l +.type __iswspace_l, %function; +__iswspace_l: .globl iswupper .type iswupper, %function; iswupper: -.globl __iswupper_l -.type __iswupper_l, %function; -__iswupper_l: .weak iswupper_l .type iswupper_l, %function; iswupper_l: +.globl __iswupper_l +.type __iswupper_l, %function; +__iswupper_l: .globl iswxdigit .type iswxdigit, %function; iswxdigit: -.globl __iswxdigit_l -.type __iswxdigit_l, %function; -__iswxdigit_l: .weak iswxdigit_l .type iswxdigit_l, %function; iswxdigit_l: +.globl __iswxdigit_l +.type __iswxdigit_l, %function; +__iswxdigit_l: .globl isxdigit .type isxdigit, %function; isxdigit: -.globl __isxdigit_l -.type __isxdigit_l, %function; -__isxdigit_l: .weak isxdigit_l .type isxdigit_l, %function; isxdigit_l: +.globl __isxdigit_l +.type __isxdigit_l, %function; +__isxdigit_l: .globl toascii .type toascii, %function; toascii: @@ -567,24 +567,24 @@ tolower_l: .globl toupper .type toupper, %function; toupper: -.weak toupper_l -.type toupper_l, %function; -toupper_l: .globl __toupper_l .type __toupper_l, %function; __toupper_l: +.weak toupper_l +.type toupper_l, %function; +toupper_l: .globl towlower .type towlower, %function; towlower: .globl towupper .type towupper, %function; towupper: -.globl __towupper_l -.type __towupper_l, %function; -__towupper_l: .weak towupper_l .type towupper_l, %function; towupper_l: +.globl __towupper_l +.type __towupper_l, %function; +__towupper_l: .globl __towlower_l .type __towlower_l, %function; __towlower_l: @@ -660,12 +660,12 @@ seekdir: .globl telldir .type telldir, %function; telldir: -.globl versionsort -.type versionsort, %function; -versionsort: .weak versionsort64 .type versionsort64, %function; versionsort64: +.globl versionsort +.type versionsort, %function; +versionsort: .weak _init .type _init, %function; _init: @@ -735,12 +735,12 @@ exit: .globl quick_exit .type quick_exit, %function; quick_exit: -.globl creat -.type creat, %function; -creat: .weak creat64 .type creat64, %function; creat64: +.globl creat +.type creat, %function; +creat: .globl fcntl .type fcntl, %function; fcntl: @@ -750,24 +750,24 @@ open64: .globl open .type open, %function; open: -.weak openat64 -.type openat64, %function; -openat64: .globl openat .type openat, %function; openat: -.weak posix_fadvise64 -.type posix_fadvise64, %function; -posix_fadvise64: +.weak openat64 +.type openat64, %function; +openat64: .globl posix_fadvise .type posix_fadvise, %function; posix_fadvise: -.weak posix_fallocate64 -.type posix_fallocate64, %function; -posix_fallocate64: +.weak posix_fadvise64 +.type posix_fadvise64, %function; +posix_fadvise64: .globl posix_fallocate .type posix_fallocate, %function; posix_fallocate: +.weak posix_fallocate64 +.type posix_fallocate64, %function; +posix_fallocate64: .globl __flt_rounds .type __flt_rounds, %function; __flt_rounds: @@ -891,12 +891,12 @@ euidaccess: .weak eaccess .type eaccess, %function; eaccess: -.weak ftw64 -.type ftw64, %function; -ftw64: .globl ftw .type ftw, %function; ftw: +.weak ftw64 +.type ftw64, %function; +ftw64: .globl futimes .type futimes, %function; futimes: @@ -930,48 +930,48 @@ lutimes: .globl ulimit .type ulimit, %function; ulimit: -.weak endutent -.type endutent, %function; -endutent: .globl endutxent .type endutxent, %function; endutxent: -.weak setutent -.type setutent, %function; -setutent: +.weak endutent +.type endutent, %function; +endutent: .globl setutxent .type setutxent, %function; setutxent: +.weak setutent +.type setutent, %function; +setutent: .globl getutxent .type getutxent, %function; getutxent: .weak getutent .type getutent, %function; getutent: -.weak getutid -.type getutid, %function; -getutid: .globl getutxid .type getutxid, %function; getutxid: +.weak getutid +.type getutid, %function; +getutid: .weak getutline .type getutline, %function; getutline: .globl getutxline .type getutxline, %function; getutxline: -.weak pututline -.type pututline, %function; -pututline: .globl pututxline .type pututxline, %function; pututxline: -.weak updwtmp -.type updwtmp, %function; -updwtmp: +.weak pututline +.type pututline, %function; +pututline: .globl updwtmpx .type updwtmpx, %function; updwtmpx: +.weak updwtmp +.type updwtmp, %function; +updwtmp: .weak utmpxname .type utmpxname, %function; utmpxname: @@ -1042,12 +1042,12 @@ eventfd_read: .globl eventfd_write .type eventfd_write, %function; eventfd_write: -.weak fallocate64 -.type fallocate64, %function; -fallocate64: .globl fallocate .type fallocate, %function; fallocate: +.weak fallocate64 +.type fallocate64, %function; +fallocate64: .globl fanotify_init .type fanotify_init, %function; fanotify_init: @@ -1156,12 +1156,12 @@ remap_file_pages: .globl sbrk .type sbrk, %function; sbrk: -.globl sendfile -.type sendfile, %function; -sendfile: .weak sendfile64 .type sendfile64, %function; sendfile64: +.globl sendfile +.type sendfile, %function; +sendfile: .globl setfsgid .type setfsgid, %function; setfsgid: @@ -1297,18 +1297,18 @@ dngettext: .globl dgettext .type dgettext, %function; dgettext: -.weak duplocale -.type duplocale, %function; -duplocale: .globl __duplocale .type __duplocale, %function; __duplocale: -.weak __freelocale -.type __freelocale, %function; -__freelocale: +.weak duplocale +.type duplocale, %function; +duplocale: .globl freelocale .type freelocale, %function; freelocale: +.weak __freelocale +.type __freelocale, %function; +__freelocale: .globl iconv_open .type iconv_open, %function; iconv_open: @@ -1318,18 +1318,18 @@ iconv: .globl iconv_close .type iconv_close, %function; iconv_close: -.weak nl_langinfo_l -.type nl_langinfo_l, %function; -nl_langinfo_l: .globl __nl_langinfo_l .type __nl_langinfo_l, %function; __nl_langinfo_l: -.globl __nl_langinfo -.type __nl_langinfo, %function; -__nl_langinfo: +.weak nl_langinfo_l +.type nl_langinfo_l, %function; +nl_langinfo_l: .weak nl_langinfo .type nl_langinfo, %function; nl_langinfo: +.globl __nl_langinfo +.type __nl_langinfo, %function; +__nl_langinfo: .globl localeconv .type localeconv, %function; localeconv: @@ -1357,6 +1357,24 @@ strfmon_l: .globl strfmon .type strfmon, %function; strfmon: +.weak __strtof_l +.type __strtof_l, %function; +__strtof_l: +.globl strtof_l +.type strtof_l, %function; +strtof_l: +.globl strtod_l +.type strtod_l, %function; +strtod_l: +.weak __strtod_l +.type __strtod_l, %function; +__strtod_l: +.globl strtold_l +.type strtold_l, %function; +strtold_l: +.weak __strtold_l +.type __strtold_l, %function; +__strtold_l: .globl __strxfrm_l .type __strxfrm_l, %function; __strxfrm_l: @@ -1390,12 +1408,12 @@ wcscoll_l: .globl wcscoll .type wcscoll, %function; wcscoll: -.globl __wcsxfrm_l -.type __wcsxfrm_l, %function; -__wcsxfrm_l: .weak wcsxfrm_l .type wcsxfrm_l, %function; wcsxfrm_l: +.globl __wcsxfrm_l +.type __wcsxfrm_l, %function; +__wcsxfrm_l: .globl wcsxfrm .type wcsxfrm, %function; wcsxfrm: @@ -1567,12 +1585,12 @@ erfcl: .globl exp .type exp, %function; exp: -.weak pow10 -.type pow10, %function; -pow10: .globl exp10 .type exp10, %function; exp10: +.weak pow10 +.type pow10, %function; +pow10: .weak pow10f .type pow10f, %function; pow10f: @@ -1738,12 +1756,12 @@ lgammaf: .weak lgammaf_r .type lgammaf_r, %function; lgammaf_r: -.weak lgammal_r -.type lgammal_r, %function; -lgammal_r: .globl __lgammal_r .type __lgammal_r, %function; __lgammal_r: +.weak lgammal_r +.type lgammal_r, %function; +lgammal_r: .globl lgammal .type lgammal, %function; lgammal: @@ -1888,12 +1906,12 @@ remainder: .weak drem .type drem, %function; drem: -.weak dremf -.type dremf, %function; -dremf: .globl remainderf .type remainderf, %function; remainderf: +.weak dremf +.type dremf, %function; +dremf: .globl remainderl .type remainderl, %function; remainderl: @@ -2389,24 +2407,24 @@ dn_expand: .globl dn_skipname .type dn_skipname, %function; dn_skipname: -.weak setnetent -.type setnetent, %function; -setnetent: .globl sethostent .type sethostent, %function; sethostent: +.weak setnetent +.type setnetent, %function; +setnetent: .globl gethostent .type gethostent, %function; gethostent: .globl getnetent .type getnetent, %function; getnetent: -.globl endhostent -.type endhostent, %function; -endhostent: .weak endnetent .type endnetent, %function; endnetent: +.globl endhostent +.type endhostent, %function; +endhostent: .globl ether_aton_r .type ether_aton_r, %function; ether_aton_r: @@ -2701,12 +2719,12 @@ getpwnam_r: .globl getpwuid_r .type getpwuid_r, %function; getpwuid_r: -.weak endpwent -.type endpwent, %function; -endpwent: .globl setpwent .type setpwent, %function; setpwent: +.weak endpwent +.type endpwent, %function; +endpwent: .globl getpwent .type getpwent, %function; getpwent: @@ -2911,12 +2929,12 @@ waitpid: .globl fnmatch .type fnmatch, %function; fnmatch: -.globl glob -.type glob, %function; -glob: .weak glob64 .type glob64, %function; glob64: +.globl glob +.type glob, %function; +glob: .globl globfree .type globfree, %function; globfree: @@ -3031,9 +3049,6 @@ pselect: .globl select .type select, %function; select: -.globl _longjmp -.type _longjmp, %function; -_longjmp: #if !defined(ARCH_mips) && !defined(ARCH_i386) && !defined(ARCH_x86_64) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) .globl __longjmp .type __longjmp, %function; @@ -3042,15 +3057,18 @@ __longjmp: .globl longjmp .type longjmp, %function; longjmp: +.globl _longjmp +.type _longjmp, %function; +_longjmp: .globl _setjmp .type _setjmp, %function; _setjmp: -.globl setjmp -.type setjmp, %function; -setjmp: .globl __setjmp .type __setjmp, %function; __setjmp: +.globl setjmp +.type setjmp, %function; +setjmp: .globl getitimer .type getitimer, %function; getitimer: @@ -3165,22 +3183,22 @@ sigwait: .globl sigwaitinfo .type sigwaitinfo, %function; sigwaitinfo: -WEAK64 __fxstat64 -.type __fxstat64, %function; -__fxstat64: #if !defined(ARCH_mips) && !defined(ARCH_i386) && !defined(ARCH_powerpc) .globl __fxstat .type __fxstat, %function; __fxstat: #endif +WEAK64 __fxstat64 +.type __fxstat64, %function; +__fxstat64: +WEAK64 __fxstatat64 +.type __fxstatat64, %function; +__fxstatat64: #if !defined(ARCH_mips) && !defined(ARCH_i386) && !defined(ARCH_powerpc) .globl __fxstatat .type __fxstatat, %function; __fxstatat: #endif -WEAK64 __fxstatat64 -.type __fxstatat64, %function; -__fxstatat64: WEAK64 __lxstat64 .type __lxstat64, %function; __lxstat64: @@ -3218,12 +3236,12 @@ fstat64: .globl fstat .type fstat, %function; fstat: -.globl fstatat -.type fstatat, %function; -fstatat: .weak fstatat64 .type fstatat64, %function; fstatat64: +.globl fstatat +.type fstatat, %function; +fstatat: .globl futimens .type futimens, %function; futimens: @@ -3233,12 +3251,12 @@ futimesat: .globl lchmod .type lchmod, %function; lchmod: -.weak lstat64 -.type lstat64, %function; -lstat64: .globl lstat .type lstat, %function; lstat: +.weak lstat64 +.type lstat64, %function; +lstat64: .globl mkdir .type mkdir, %function; mkdir: @@ -3263,30 +3281,30 @@ stat: .weak stat64 .type stat64, %function; stat64: -.weak statfs -.type statfs, %function; -statfs: .weak statfs64 .type statfs64, %function; statfs64: -.weak fstatfs -.type fstatfs, %function; -fstatfs: +.weak statfs +.type statfs, %function; +statfs: .weak fstatfs64 .type fstatfs64, %function; fstatfs64: +.weak fstatfs +.type fstatfs, %function; +fstatfs: .weak statvfs64 .type statvfs64, %function; statvfs64: .globl statvfs .type statvfs, %function; statvfs: -.weak fstatvfs64 -.type fstatvfs64, %function; -fstatvfs64: .globl fstatvfs .type fstatvfs, %function; fstatvfs: +.weak fstatvfs64 +.type fstatvfs64, %function; +fstatvfs64: .globl umask .type umask, %function; umask: @@ -3307,12 +3325,12 @@ __uflow: .globl asprintf .type asprintf, %function; asprintf: -.weak clearerr_unlocked -.type clearerr_unlocked, %function; -clearerr_unlocked: .globl clearerr .type clearerr, %function; clearerr: +.weak clearerr_unlocked +.type clearerr_unlocked, %function; +clearerr_unlocked: .globl dprintf .type dprintf, %function; dprintf: @@ -3367,27 +3385,27 @@ fclose: .weak _IO_feof_unlocked .type _IO_feof_unlocked, %function; _IO_feof_unlocked: -.globl feof -.type feof, %function; -feof: .weak feof_unlocked .type feof_unlocked, %function; feof_unlocked: -.globl ferror -.type ferror, %function; -ferror: +.globl feof +.type feof, %function; +feof: .weak ferror_unlocked .type ferror_unlocked, %function; ferror_unlocked: .weak _IO_ferror_unlocked .type _IO_ferror_unlocked, %function; _IO_ferror_unlocked: -.weak fflush_unlocked -.type fflush_unlocked, %function; -fflush_unlocked: +.globl ferror +.type ferror, %function; +ferror: .globl fflush .type fflush, %function; fflush: +.weak fflush_unlocked +.type fflush_unlocked, %function; +fflush_unlocked: .globl fgetc .type fgetc, %function; fgetc: @@ -3400,21 +3418,21 @@ fgetpos: .weak fgetpos64 .type fgetpos64, %function; fgetpos64: -.weak fgets_unlocked -.type fgets_unlocked, %function; -fgets_unlocked: .globl fgets .type fgets, %function; fgets: -.weak getwc_unlocked -.type getwc_unlocked, %function; -getwc_unlocked: -.weak fgetwc_unlocked -.type fgetwc_unlocked, %function; -fgetwc_unlocked: +.weak fgets_unlocked +.type fgets_unlocked, %function; +fgets_unlocked: .globl __fgetwc_unlocked .type __fgetwc_unlocked, %function; __fgetwc_unlocked: +.weak fgetwc_unlocked +.type fgetwc_unlocked, %function; +fgetwc_unlocked: +.weak getwc_unlocked +.type getwc_unlocked, %function; +getwc_unlocked: .globl fgetwc .type fgetwc, %function; fgetwc: @@ -3424,12 +3442,12 @@ fgetws_unlocked: .globl fgetws .type fgetws, %function; fgetws: -.weak fileno_unlocked -.type fileno_unlocked, %function; -fileno_unlocked: .globl fileno .type fileno, %function; fileno: +.weak fileno_unlocked +.type fileno_unlocked, %function; +fileno_unlocked: .globl flockfile .type flockfile, %function; flockfile: @@ -3451,30 +3469,30 @@ fprintf: .globl fputc .type fputc, %function; fputc: -.weak fputs_unlocked -.type fputs_unlocked, %function; -fputs_unlocked: .globl fputs .type fputs, %function; fputs: -.weak putwc_unlocked -.type putwc_unlocked, %function; -putwc_unlocked: -.weak fputwc_unlocked -.type fputwc_unlocked, %function; -fputwc_unlocked: +.weak fputs_unlocked +.type fputs_unlocked, %function; +fputs_unlocked: .globl __fputwc_unlocked .type __fputwc_unlocked, %function; __fputwc_unlocked: +.weak fputwc_unlocked +.type fputwc_unlocked, %function; +fputwc_unlocked: +.weak putwc_unlocked +.type putwc_unlocked, %function; +putwc_unlocked: .globl fputwc .type fputwc, %function; fputwc: -.globl fputws -.type fputws, %function; -fputws: .weak fputws_unlocked .type fputws_unlocked, %function; fputws_unlocked: +.globl fputws +.type fputws, %function; +fputws: .globl fread .type fread, %function; fread: @@ -3493,12 +3511,12 @@ __isoc99_fscanf: .globl fscanf .type fscanf, %function; fscanf: -.weak fseeko -.type fseeko, %function; -fseeko: .weak fseeko64 .type fseeko64, %function; fseeko64: +.weak fseeko +.type fseeko, %function; +fseeko: .globl fseek .type fseek, %function; fseek: @@ -3529,33 +3547,33 @@ fwide: .globl fwprintf .type fwprintf, %function; fwprintf: -.weak fwrite_unlocked -.type fwrite_unlocked, %function; -fwrite_unlocked: .globl fwrite .type fwrite, %function; fwrite: -.globl fwscanf -.type fwscanf, %function; -fwscanf: +.weak fwrite_unlocked +.type fwrite_unlocked, %function; +fwrite_unlocked: .weak __isoc99_fwscanf .type __isoc99_fwscanf, %function; __isoc99_fwscanf: +.globl fwscanf +.type fwscanf, %function; +fwscanf: .globl getc .type getc, %function; getc: .weak _IO_getc .type _IO_getc, %function; _IO_getc: -.weak _IO_getc_unlocked -.type _IO_getc_unlocked, %function; -_IO_getc_unlocked: -.globl getc_unlocked -.type getc_unlocked, %function; -getc_unlocked: .weak fgetc_unlocked .type fgetc_unlocked, %function; fgetc_unlocked: +.globl getc_unlocked +.type getc_unlocked, %function; +getc_unlocked: +.weak _IO_getc_unlocked +.type _IO_getc_unlocked, %function; +_IO_getc_unlocked: .globl getchar .type getchar, %function; getchar: @@ -3580,12 +3598,12 @@ getw: .globl getwc .type getwc, %function; getwc: -.globl getwchar -.type getwchar, %function; -getwchar: .weak getwchar_unlocked .type getwchar_unlocked, %function; getwchar_unlocked: +.globl getwchar +.type getwchar, %function; +getwchar: .globl open_memstream .type open_memstream, %function; open_memstream: @@ -3610,15 +3628,15 @@ putc: .weak _IO_putc .type _IO_putc, %function; _IO_putc: -.weak _IO_putc_unlocked -.type _IO_putc_unlocked, %function; -_IO_putc_unlocked: -.globl putc_unlocked -.type putc_unlocked, %function; -putc_unlocked: .weak fputc_unlocked .type fputc_unlocked, %function; fputc_unlocked: +.globl putc_unlocked +.type putc_unlocked, %function; +putc_unlocked: +.weak _IO_putc_unlocked +.type _IO_putc_unlocked, %function; +_IO_putc_unlocked: .globl putchar .type putchar, %function; putchar: @@ -3673,12 +3691,12 @@ snprintf: .globl sprintf .type sprintf, %function; sprintf: -.weak __isoc99_sscanf -.type __isoc99_sscanf, %function; -__isoc99_sscanf: .globl sscanf .type sscanf, %function; sscanf: +.weak __isoc99_sscanf +.type __isoc99_sscanf, %function; +__isoc99_sscanf: .globl swprintf .type swprintf, %function; swprintf: @@ -3691,12 +3709,12 @@ __isoc99_swscanf: .globl tempnam .type tempnam, %function; tempnam: -.globl tmpfile -.type tmpfile, %function; -tmpfile: .weak tmpfile64 .type tmpfile64, %function; tmpfile64: +.globl tmpfile +.type tmpfile, %function; +tmpfile: .globl tmpnam .type tmpnam, %function; tmpnam: @@ -3715,30 +3733,30 @@ vdprintf: .globl vfprintf .type vfprintf, %function; vfprintf: -.weak __isoc99_vfscanf -.type __isoc99_vfscanf, %function; -__isoc99_vfscanf: .globl vfscanf .type vfscanf, %function; vfscanf: +.weak __isoc99_vfscanf +.type __isoc99_vfscanf, %function; +__isoc99_vfscanf: .globl vfwprintf .type vfwprintf, %function; vfwprintf: -.globl vfwscanf -.type vfwscanf, %function; -vfwscanf: .weak __isoc99_vfwscanf .type __isoc99_vfwscanf, %function; __isoc99_vfwscanf: +.globl vfwscanf +.type vfwscanf, %function; +vfwscanf: .globl vprintf .type vprintf, %function; vprintf: -.weak __isoc99_vscanf -.type __isoc99_vscanf, %function; -__isoc99_vscanf: .globl vscanf .type vscanf, %function; vscanf: +.weak __isoc99_vscanf +.type __isoc99_vscanf, %function; +__isoc99_vscanf: .globl vsnprintf .type vsnprintf, %function; vsnprintf: @@ -3772,12 +3790,12 @@ __isoc99_vwscanf: .globl wprintf .type wprintf, %function; wprintf: -.weak __isoc99_wscanf -.type __isoc99_wscanf, %function; -__isoc99_wscanf: .globl wscanf .type wscanf, %function; wscanf: +.weak __isoc99_wscanf +.type __isoc99_wscanf, %function; +__isoc99_wscanf: .globl abs .type abs, %function; abs: @@ -3826,36 +3844,21 @@ llabs: .globl lldiv .type lldiv, %function; lldiv: +.weak qsort_r +.type qsort_r, %function; +qsort_r: .globl qsort .type qsort, %function; qsort: -.weak __strtof_l -.type __strtof_l, %function; -__strtof_l: -.weak strtof_l -.type strtof_l, %function; -strtof_l: .globl strtof .type strtof, %function; strtof: .globl strtod .type strtod, %function; strtod: -.weak __strtod_l -.type __strtod_l, %function; -__strtod_l: -.weak strtod_l -.type strtod_l, %function; -strtod_l: -.weak strtold_l -.type strtold_l, %function; -strtold_l: .globl strtold .type strtold, %function; strtold: -.weak __strtold_l -.type __strtold_l, %function; -__strtold_l: .globl strtoull .type strtoull, %function; strtoull: @@ -3868,12 +3871,12 @@ strtoll: .weak __strtoll_internal .type __strtoll_internal, %function; __strtoll_internal: -.globl strtoul -.type strtoul, %function; -strtoul: .weak __strtoul_internal .type __strtoul_internal, %function; __strtoul_internal: +.globl strtoul +.type strtoul, %function; +strtoul: .weak __strtol_internal .type __strtol_internal, %function; __strtol_internal: @@ -3973,12 +3976,12 @@ stpncpy: .globl strcasecmp .type strcasecmp, %function; strcasecmp: -.weak strcasecmp_l -.type strcasecmp_l, %function; -strcasecmp_l: .globl __strcasecmp_l .type __strcasecmp_l, %function; __strcasecmp_l: +.weak strcasecmp_l +.type strcasecmp_l, %function; +strcasecmp_l: .globl strcasestr .type strcasestr, %function; strcasestr: @@ -4003,12 +4006,12 @@ strcspn: .globl strdup .type strdup, %function; strdup: -.globl strerror_r -.type strerror_r, %function; -strerror_r: .weak __xpg_strerror_r .type __xpg_strerror_r, %function; __xpg_strerror_r: +.globl strerror_r +.type strerror_r, %function; +strerror_r: .globl strlcat .type strlcat, %function; strlcat: @@ -4165,24 +4168,24 @@ mkostemp: .weak mkostemp64 .type mkostemp64, %function; mkostemp64: -.weak mkostemps -.type mkostemps, %function; -mkostemps: .weak mkostemps64 .type mkostemps64, %function; mkostemps64: +.weak mkostemps +.type mkostemps, %function; +mkostemps: .globl mkstemp .type mkstemp, %function; mkstemp: .weak mkstemp64 .type mkstemp64, %function; mkstemp64: -.globl mkstemps -.type mkstemps, %function; -mkstemps: .weak mkstemps64 .type mkstemps64, %function; mkstemps64: +.globl mkstemps +.type mkstemps, %function; +mkstemps: .globl mktemp .type mktemp, %function; mktemp: @@ -4195,12 +4198,12 @@ cfgetispeed: .globl cfmakeraw .type cfmakeraw, %function; cfmakeraw: -.globl cfsetospeed -.type cfsetospeed, %function; -cfsetospeed: .weak cfsetspeed .type cfsetspeed, %function; cfsetspeed: +.globl cfsetospeed +.type cfsetospeed, %function; +cfsetospeed: .globl cfsetispeed .type cfsetispeed, %function; cfsetispeed: @@ -4438,15 +4441,18 @@ pthread_getconcurrency: .globl pthread_getcpuclockid .type pthread_getcpuclockid, %function; pthread_getcpuclockid: +.globl pthread_getname_np +.type pthread_getname_np, %function; +pthread_getname_np: .globl pthread_getschedparam .type pthread_getschedparam, %function; pthread_getschedparam: -.weak tss_get -.type tss_get, %function; -tss_get: .weak pthread_getspecific .type pthread_getspecific, %function; pthread_getspecific: +.weak tss_get +.type tss_get, %function; +tss_get: .weak pthread_join .type pthread_join, %function; pthread_join: @@ -4549,12 +4555,12 @@ pthread_rwlockattr_init: .globl pthread_rwlockattr_setpshared .type pthread_rwlockattr_setpshared, %function; pthread_rwlockattr_setpshared: -.weak pthread_self -.type pthread_self, %function; -pthread_self: .weak thrd_current .type thrd_current, %function; thrd_current: +.weak pthread_self +.type pthread_self, %function; +pthread_self: .globl pthread_setattr_default_np .type pthread_setattr_default_np, %function; pthread_setattr_default_np: @@ -4819,12 +4825,12 @@ fdatasync: .globl fsync .type fsync, %function; fsync: -.weak ftruncate64 -.type ftruncate64, %function; -ftruncate64: .globl ftruncate .type ftruncate, %function; ftruncate: +.weak ftruncate64 +.type ftruncate64, %function; +ftruncate64: .globl getcwd .type getcwd, %function; getcwd: @@ -4918,12 +4924,12 @@ pwrite: .weak pwrite64 .type pwrite64, %function; pwrite64: -.weak pwritev64 -.type pwritev64, %function; -pwritev64: .globl pwritev .type pwritev, %function; pwritev: +.weak pwritev64 +.type pwritev64, %function; +pwritev64: .globl read .type read, %function; read: @@ -5075,10 +5081,6 @@ _dl_debug_addr: .type ___environ, %object; .size ___environ, PTR_SIZE_BYTES ___environ: -.weak _environ -.type _environ, %object; -.size _environ, PTR_SIZE_BYTES -_environ: .globl __environ .type __environ, %object; .size __environ, PTR_SIZE_BYTES @@ -5087,6 +5089,10 @@ __environ: .type environ, %object; .size environ, PTR_SIZE_BYTES environ: +.weak _environ +.type _environ, %object; +.size _environ, PTR_SIZE_BYTES +_environ: .globl __stack_chk_guard .type __stack_chk_guard, %object; .size __stack_chk_guard, PTR_SIZE_BYTES diff --git a/lib/libc/musl/src/complex/cacosf.c b/lib/libc/musl/src/complex/cacosf.c index 2e048540fa..ed8acf0f5a 100644 --- a/lib/libc/musl/src/complex/cacosf.c +++ b/lib/libc/musl/src/complex/cacosf.c @@ -2,8 +2,10 @@ // FIXME +static const float float_pi_2 = M_PI_2; + float complex cacosf(float complex z) { z = casinf(z); - return CMPLXF((float)M_PI_2 - crealf(z), -cimagf(z)); + return CMPLXF(float_pi_2 - crealf(z), -cimagf(z)); } diff --git a/lib/libc/musl/src/complex/catanf.c b/lib/libc/musl/src/complex/catanf.c index ef3907a506..1d569f2dac 100644 --- a/lib/libc/musl/src/complex/catanf.c +++ b/lib/libc/musl/src/complex/catanf.c @@ -61,13 +61,15 @@ static const double DP1 = 3.140625; static const double DP2 = 9.67502593994140625E-4; static const double DP3 = 1.509957990978376432E-7; +static const float float_pi = M_PI; + static float _redupif(float xx) { float x, t; long i; x = xx; - t = x/(float)M_PI; + t = x/float_pi; if (t >= 0.0f) t += 0.5f; else diff --git a/lib/libc/musl/src/complex/cproj.c b/lib/libc/musl/src/complex/cproj.c index 9ae1e17c0d..d2b8f5a972 100644 --- a/lib/libc/musl/src/complex/cproj.c +++ b/lib/libc/musl/src/complex/cproj.c @@ -3,6 +3,6 @@ double complex cproj(double complex z) { if (isinf(creal(z)) || isinf(cimag(z))) - return CMPLX(INFINITY, copysign(0.0, creal(z))); + return CMPLX(INFINITY, copysign(0.0, cimag(z))); return z; } diff --git a/lib/libc/musl/src/complex/cprojf.c b/lib/libc/musl/src/complex/cprojf.c index 03fab339d9..15a874bb2f 100644 --- a/lib/libc/musl/src/complex/cprojf.c +++ b/lib/libc/musl/src/complex/cprojf.c @@ -3,6 +3,6 @@ float complex cprojf(float complex z) { if (isinf(crealf(z)) || isinf(cimagf(z))) - return CMPLXF(INFINITY, copysignf(0.0, crealf(z))); + return CMPLXF(INFINITY, copysignf(0.0, cimagf(z))); return z; } diff --git a/lib/libc/musl/src/complex/cprojl.c b/lib/libc/musl/src/complex/cprojl.c index 38a494c5c4..531ffa1c5e 100644 --- a/lib/libc/musl/src/complex/cprojl.c +++ b/lib/libc/musl/src/complex/cprojl.c @@ -9,7 +9,7 @@ long double complex cprojl(long double complex z) long double complex cprojl(long double complex z) { if (isinf(creall(z)) || isinf(cimagl(z))) - return CMPLXL(INFINITY, copysignl(0.0, creall(z))); + return CMPLXL(INFINITY, copysignl(0.0, cimagl(z))); return z; } #endif diff --git a/lib/libc/musl/src/ctype/nonspacing.h b/lib/libc/musl/src/ctype/nonspacing.h index 5d05a3d1a0..7746f3b603 100644 --- a/lib/libc/musl/src/ctype/nonspacing.h +++ b/lib/libc/musl/src/ctype/nonspacing.h @@ -1,23 +1,23 @@ -16,16,16,18,19,20,21,22,23,24,25,26,27,28,29,30,31,16,16,32,16,16,16,33,34,35, -36,37,38,39,16,16,40,16,16,16,16,16,16,16,16,16,16,16,41,42,16,16,43,16,16,16, +16,16,16,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,16,33,16,16,16,34,35,36, +37,38,39,40,16,16,41,16,16,16,16,16,16,16,16,16,16,16,42,43,16,16,44,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,44,16,45,46,47,48,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,45,16,46,47,48,49,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,50,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,51,16,16,52, +53,16,54,55,56,16,16,16,16,16,16,57,16,16,58,16,59,60,61,62,63,64,65,66,67,68, +69,70,16,71,72,73,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,74,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,75,76,16,16,16,77,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,49,16,16,50, -51,16,52,53,54,16,16,16,16,16,16,55,16,16,56,16,57,58,59,60,61,62,63,64,65,66, -67,68,16,69,70,71,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,72,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,73,74,16,16,16,75,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,76,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,77,78,16,16,16,16,16,16,16,79,16,16,16,16,16,80,81,82,16,16,16,16,16,83, -84,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,78,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,79,80,16,16,16,16,16,16,16,81,16,16,16,16,16,82,83,84,16,16,16,16,16,85, +86,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, @@ -35,55 +35,57 @@ 242,7,128,127,0,0,0,0,0,0,0,0,0,0,0,0,242,31,0,63,0,0,0,0,0,0,0,0,0,3,0,0,160, 2,0,0,0,0,0,0,254,127,223,224,255,254,255,255,255,31,64,0,0,0,0,0,0,0,0,0,0,0, 0,224,253,102,0,0,0,195,1,0,30,0,100,32,0,32,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,0, -0,0,28,0,0,0,12,0,0,0,12,0,0,0,0,0,0,0,176,63,64,254,15,32,0,0,0,0,0,120,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,135,1,4,14,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,9,0,0,0,0,0,0,64,127, -229,31,248,159,0,0,0,0,0,0,255,127,0,0,0,0,0,0,0,0,15,0,0,0,0,0,208,23,4,0,0, -0,0,248,15,0,3,0,0,0,60,59,0,0,0,0,0,0,64,163,3,0,0,0,0,0,0,240,207,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,247,255,253,33,16,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255, +0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,224,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,28,0,0,0,28,0,0,0,12,0,0,0,12,0,0,0,0,0,0,0,176,63,64,254, +15,32,0,0,0,0,0,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,0,0,2,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,135,1,4,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +128,9,0,0,0,0,0,0,64,127,229,31,248,159,0,0,0,0,0,0,255,127,0,0,0,0,0,0,0,0, +15,0,0,0,0,0,208,23,4,0,0,0,0,248,15,0,3,0,0,0,60,59,0,0,0,0,0,0,64,163,3,0,0, +0,0,0,0,240,207,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,247,255,253,33,16, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255, 251,0,248,0,0,0,124,0,0,0,0,0,0,223,255,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255, 255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,3,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0, 0,60,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,128,247,63,0,0,0,192,0,0,0,0,0,0,0,0,0,0,3,0,68,8,0,0,96,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,48,0,0,0,255,255,3,128,0,0,0,0,192,63,0,0,128,255,3,0, -0,0,0,0,7,0,0,0,0,0,200,51,0,0,0,0,32,0,0,0,0,0,0,0,0,126,102,0,8,16,0,0,0,0, -0,16,0,0,0,0,0,0,157,193,2,0,0,0,0,48,64, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,33,0,0,0,0,0,64, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,0,0,255,255,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,110,240,0,0,0,0,0,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0, -0,0,0,0,0,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,192,255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,255, -127,0,0,0,0,0,0,128,3,0,0,0,0,0,120,38,0,32,0,0,0,0,0,0,7,0,0,0,128,239,31,0, -0,0,0,0,0,0,8,0,3,0,0,0,0,0,192,127,0,30,0,0,0,0,0,0,0,0,0,0,0,128,211,64,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,248,7,0,0,3,0,0,0,0,0,0,24,1,0,0,0,192, -31,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,92,0,0,64,0,0,0,0,0, -0,0,0,0,0,248,133,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,60,176,1,0,0,48,0,0,0, -0,0,0,0,0,0,0,248,167,1,0,0,0,0,0,0,0,0,0,0,0,0,40,191,0,0,0,0,0,0,0,0,0,0,0, -0,224,188,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -128,255,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,12,1,0,0,0,254,7,0,0,0,0,248,121,128,0, -126,14,0,0,0,0,0,252,127,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,191,0,0,0, -0,0,0,0,0,0,0,252,255,255,252,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,126,180,191,0, -0,0,0,0,0,0,0,0,163,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,24, -0,0,0,0,0,0,0,255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,0,0,0,0,0,127,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0, -0,128,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,15, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,3,248,255,231,15,0,0,0,60,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,255,255,255,255,255,255,127,248,255,255,255,255,255,31,32,0,16,0,0,248, -254,255,0,0,0,0,0,0,0,0,0, -0,127,255,255,249,219,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,240,7,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,7,0,0,0,0,0,200,51,0,0,0,0,32,0,0, +0,0,0,0,0,0,126,102,0,8,16,0,0,0,0,0,16,0,0,0,0,0,0,157,193,2,0,0,0,0,48,64,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,33,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,0,0,0, +64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,0,0,255, +255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,110,240,0, +0,0,0,0,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,0,0,0,0,0,240,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,255,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,255,127,0,0,0,0,0,0,128, +3,0,0,0,0,0,120,38,0,32,0,0,0,0,0,0,7,0,0,0,128,239,31,0,0,0,0,0,0,0,8,0,3,0, +0,0,0,0,192,127,0,30,0,0,0,0,0,0,0,0,0,0,0,128,211,64,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,128,248,7,0,0,3,0,0,0,0,0,0,24,1,0,0,0,192,31,31,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,255,92,0,0,64,0,0,0,0,0,0,0,0,0,0,248,133,13,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,176,1,0,0,48,0,0,0,0,0,0,0,0,0,0, +248,167,1,0,0,0,0,0,0,0,0,0,0,0,0,40,191,0,0,0,0,0,0,0,0,0,0,0,0,224,188,15,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,6,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,240,12,1,0,0,0,254,7,0,0,0,0,248,121,128,0,126,14,0,0,0,0,0,252, +127,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,191,0,0,0,0,0,0,0,0,0,0,252,255, +255,252,109,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,126,180,191,0,0,0,0,0,0,0,0,0,163,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,0,0,0,0,0,0,0,255, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,128,7,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,15,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,3,248,255,231,15,0,0,0,60,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255, +255,255,255,255,127,248,255,255,255,255,255,31,32,0,16,0,0,248,254,255,0,0,0, +0,0,0,0,0,0,0,127,255,255,249,219,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,240,7,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, diff --git a/lib/libc/musl/src/env/__libc_start_main.c b/lib/libc/musl/src/env/__libc_start_main.c index 8fbe526271..c5b277bdcf 100644 --- a/lib/libc/musl/src/env/__libc_start_main.c +++ b/lib/libc/musl/src/env/__libc_start_main.c @@ -69,7 +69,8 @@ weak_alias(libc_start_init, __libc_start_init); typedef int lsm2_fn(int (*)(int,char **,char **), int, char **); static lsm2_fn libc_start_main_stage2; -int __libc_start_main(int (*main)(int,char **,char **), int argc, char **argv) +int __libc_start_main(int (*main)(int,char **,char **), int argc, char **argv, + void (*init_dummy)(), void(*fini_dummy)(), void(*ldso_dummy)()) { char **envp = argv+argc+1; diff --git a/lib/libc/musl/src/env/__stack_chk_fail.c b/lib/libc/musl/src/env/__stack_chk_fail.c index bf5a280ad9..e53526020f 100644 --- a/lib/libc/musl/src/env/__stack_chk_fail.c +++ b/lib/libc/musl/src/env/__stack_chk_fail.c @@ -9,6 +9,15 @@ void __init_ssp(void *entropy) if (entropy) memcpy(&__stack_chk_guard, entropy, sizeof(uintptr_t)); else __stack_chk_guard = (uintptr_t)&__stack_chk_guard * 1103515245; +#if UINTPTR_MAX >= 0xffffffffffffffff + /* Sacrifice 8 bits of entropy on 64bit to prevent leaking/ + * overwriting the canary via string-manipulation functions. + * The NULL byte is on the second byte so that off-by-ones can + * still be detected. Endianness is taken care of + * automatically. */ + ((char *)&__stack_chk_guard)[1] = 0; +#endif + __pthread_self()->canary = __stack_chk_guard; } diff --git a/lib/libc/musl/src/errno/__strerror.h b/lib/libc/musl/src/errno/__strerror.h index 2d992da554..14925907b5 100644 --- a/lib/libc/musl/src/errno/__strerror.h +++ b/lib/libc/musl/src/errno/__strerror.h @@ -102,3 +102,7 @@ E(EDQUOT, "Quota exceeded") E(ENOMEDIUM, "No medium found") E(EMEDIUMTYPE, "Wrong medium type") E(EMULTIHOP, "Multihop attempted") +E(ENOKEY, "Required key not available") +E(EKEYEXPIRED, "Key has expired") +E(EKEYREVOKED, "Key has been revoked") +E(EKEYREJECTED, "Key was rejected by service") diff --git a/lib/libc/musl/src/fenv/powerpc/fenv-sf.c b/lib/libc/musl/src/fenv/powerpc/fenv-sf.c index 85bef40f10..d4248f26f7 100644 --- a/lib/libc/musl/src/fenv/powerpc/fenv-sf.c +++ b/lib/libc/musl/src/fenv/powerpc/fenv-sf.c @@ -1,3 +1,3 @@ -#ifdef _SOFT_FLOAT +#if defined(_SOFT_FLOAT) || defined(__NO_FPRS__) #include "../fenv.c" #endif diff --git a/lib/libc/musl/src/fenv/powerpc/fenv.S b/lib/libc/musl/src/fenv/powerpc/fenv.S index 22cea216a0..55055d0b3a 100644 --- a/lib/libc/musl/src/fenv/powerpc/fenv.S +++ b/lib/libc/musl/src/fenv/powerpc/fenv.S @@ -1,4 +1,4 @@ -#ifndef _SOFT_FLOAT +#if !defined(_SOFT_FLOAT) && !defined(__NO_FPRS__) .global feclearexcept .type feclearexcept,@function feclearexcept: diff --git a/lib/libc/musl/src/include/stdlib.h b/lib/libc/musl/src/include/stdlib.h index e9da20158c..812b04de2f 100644 --- a/lib/libc/musl/src/include/stdlib.h +++ b/lib/libc/musl/src/include/stdlib.h @@ -8,6 +8,7 @@ hidden void __env_rm_add(char *, char *); hidden int __mkostemps(char *, int, int); hidden int __ptsname_r(int, char *, size_t); hidden char *__randname(char *); +hidden void __qsort_r (void *, size_t, size_t, int (*)(const void *, const void *, void *), void *); hidden void *__libc_malloc(size_t); hidden void *__libc_malloc_impl(size_t); diff --git a/lib/libc/musl/src/internal/version.h b/lib/libc/musl/src/internal/version.h index b23e8e2963..a9327dd446 100644 --- a/lib/libc/musl/src/internal/version.h +++ b/lib/libc/musl/src/internal/version.h @@ -1 +1 @@ -#define VERSION "1.2.2" +#define VERSION "1.2.3" diff --git a/lib/libc/musl/src/ldso/dl_iterate_phdr.c b/lib/libc/musl/src/ldso/dl_iterate_phdr.c index 86c87ef835..9546dd3609 100644 --- a/lib/libc/musl/src/ldso/dl_iterate_phdr.c +++ b/lib/libc/musl/src/ldso/dl_iterate_phdr.c @@ -1,5 +1,6 @@ #include #include +#include "pthread_impl.h" #include "libc.h" #define AUX_CNT 38 @@ -35,7 +36,7 @@ static int static_dl_iterate_phdr(int(*callback)(struct dl_phdr_info *info, size info.dlpi_subs = 0; if (tls_phdr) { info.dlpi_tls_modid = 1; - info.dlpi_tls_data = (void *)(base + tls_phdr->p_vaddr); + info.dlpi_tls_data = __tls_get_addr((tls_mod_off_t[]){1,0}); } else { info.dlpi_tls_modid = 0; info.dlpi_tls_data = 0; diff --git a/lib/libc/musl/src/legacy/cuserid.c b/lib/libc/musl/src/legacy/cuserid.c index 4e78798ded..dcaf73d4e6 100644 --- a/lib/libc/musl/src/legacy/cuserid.c +++ b/lib/libc/musl/src/legacy/cuserid.c @@ -2,13 +2,21 @@ #include #include #include +#include char *cuserid(char *buf) { + static char usridbuf[L_cuserid]; struct passwd pw, *ppw; long pwb[256]; - if (getpwuid_r(geteuid(), &pw, (void *)pwb, sizeof pwb, &ppw)) - return 0; - snprintf(buf, L_cuserid, "%s", pw.pw_name); + if (buf) *buf = 0; + getpwuid_r(geteuid(), &pw, (void *)pwb, sizeof pwb, &ppw); + if (!ppw) + return buf; + size_t len = strnlen(pw.pw_name, L_cuserid); + if (len == L_cuserid) + return buf; + if (!buf) buf = usridbuf; + memcpy(buf, pw.pw_name, len+1); return buf; } diff --git a/lib/libc/musl/src/linux/epoll.c b/lib/libc/musl/src/linux/epoll.c index deff5b101a..93baa8147e 100644 --- a/lib/libc/musl/src/linux/epoll.c +++ b/lib/libc/musl/src/linux/epoll.c @@ -24,9 +24,9 @@ int epoll_ctl(int fd, int op, int fd2, struct epoll_event *ev) int epoll_pwait(int fd, struct epoll_event *ev, int cnt, int to, const sigset_t *sigs) { - int r = __syscall(SYS_epoll_pwait, fd, ev, cnt, to, sigs, _NSIG/8); + int r = __syscall_cp(SYS_epoll_pwait, fd, ev, cnt, to, sigs, _NSIG/8); #ifdef SYS_epoll_wait - if (r==-ENOSYS && !sigs) r = __syscall(SYS_epoll_wait, fd, ev, cnt, to); + if (r==-ENOSYS && !sigs) r = __syscall_cp(SYS_epoll_wait, fd, ev, cnt, to); #endif return __syscall_ret(r); } diff --git a/lib/libc/musl/src/locale/dcngettext.c b/lib/libc/musl/src/locale/dcngettext.c index d1e6c6d13a..0b53286db7 100644 --- a/lib/libc/musl/src/locale/dcngettext.c +++ b/lib/libc/musl/src/locale/dcngettext.c @@ -132,6 +132,9 @@ char *dcngettext(const char *domainname, const char *msgid1, const char *msgid2, struct binding *q; int old_errno = errno; + /* match gnu gettext behaviour */ + if (!msgid1) goto notrans; + if ((unsigned)category >= LC_ALL) goto notrans; if (!domainname) domainname = __gettextdomain(); diff --git a/lib/libc/musl/src/locale/duplocale.c b/lib/libc/musl/src/locale/duplocale.c index 030b64cb0e..5ce33ae6de 100644 --- a/lib/libc/musl/src/locale/duplocale.c +++ b/lib/libc/musl/src/locale/duplocale.c @@ -3,6 +3,11 @@ #include "locale_impl.h" #include "libc.h" +#define malloc __libc_malloc +#define calloc undef +#define realloc undef +#define free undef + locale_t __duplocale(locale_t old) { locale_t new = malloc(sizeof *new); diff --git a/lib/libc/musl/src/locale/strtod_l.c b/lib/libc/musl/src/locale/strtod_l.c new file mode 100644 index 0000000000..574ba148e0 --- /dev/null +++ b/lib/libc/musl/src/locale/strtod_l.c @@ -0,0 +1,22 @@ +#define _GNU_SOURCE +#include +#include + +float strtof_l(const char *restrict s, char **restrict p, locale_t l) +{ + return strtof(s, p); +} + +double strtod_l(const char *restrict s, char **restrict p, locale_t l) +{ + return strtod(s, p); +} + +long double strtold_l(const char *restrict s, char **restrict p, locale_t l) +{ + return strtold(s, p); +} + +weak_alias(strtof_l, __strtof_l); +weak_alias(strtod_l, __strtod_l); +weak_alias(strtold_l, __strtold_l); diff --git a/lib/libc/musl/src/malloc/free.c b/lib/libc/musl/src/malloc/free.c index f17a952cb4..3944f7b28f 100644 --- a/lib/libc/musl/src/malloc/free.c +++ b/lib/libc/musl/src/malloc/free.c @@ -2,5 +2,5 @@ void free(void *p) { - return __libc_free(p); + __libc_free(p); } diff --git a/lib/libc/musl/src/malloc/mallocng/aligned_alloc.c b/lib/libc/musl/src/malloc/mallocng/aligned_alloc.c index 3411689600..e0862a83ae 100644 --- a/lib/libc/musl/src/malloc/mallocng/aligned_alloc.c +++ b/lib/libc/musl/src/malloc/mallocng/aligned_alloc.c @@ -22,6 +22,9 @@ void *aligned_alloc(size_t align, size_t len) if (align <= UNIT) align = UNIT; unsigned char *p = malloc(len + align - UNIT); + if (!p) + return 0; + struct meta *g = get_meta(p); int idx = get_slot_index(p); size_t stride = get_stride(g); diff --git a/lib/libc/musl/src/malloc/mallocng/free.c b/lib/libc/musl/src/malloc/mallocng/free.c index 40745f97da..418a085c18 100644 --- a/lib/libc/musl/src/malloc/mallocng/free.c +++ b/lib/libc/musl/src/malloc/mallocng/free.c @@ -119,7 +119,11 @@ void free(void *p) if (((uintptr_t)(start-1) ^ (uintptr_t)end) >= 2*PGSZ && g->last_idx) { unsigned char *base = start + (-(uintptr_t)start & (PGSZ-1)); size_t len = (end-base) & -PGSZ; - if (len) madvise(base, len, MADV_FREE); + if (len) { + int e = errno; + madvise(base, len, MADV_FREE); + errno = e; + } } // atomic free without locking if this is neither first or last slot @@ -139,5 +143,9 @@ void free(void *p) wrlock(); struct mapinfo mi = nontrivial_free(g, idx); unlock(); - if (mi.len) munmap(mi.base, mi.len); + if (mi.len) { + int e = errno; + munmap(mi.base, mi.len); + errno = e; + } } diff --git a/lib/libc/musl/src/malloc/oldmalloc/malloc.c b/lib/libc/musl/src/malloc/oldmalloc/malloc.c index 53f5f959ec..25d00d44de 100644 --- a/lib/libc/musl/src/malloc/oldmalloc/malloc.c +++ b/lib/libc/musl/src/malloc/oldmalloc/malloc.c @@ -11,7 +11,7 @@ #include "malloc_impl.h" #include "fork_impl.h" -#define malloc __libc_malloc +#define malloc __libc_malloc_impl #define realloc __libc_realloc #define free __libc_free @@ -481,12 +481,14 @@ void __bin_chunk(struct chunk *self) if (size > RECLAIM && (size^(size-osize)) > size-osize) { uintptr_t a = (uintptr_t)self + SIZE_ALIGN+PAGE_SIZE-1 & -PAGE_SIZE; uintptr_t b = (uintptr_t)next - SIZE_ALIGN & -PAGE_SIZE; + int e = errno; #if 1 __madvise((void *)a, b-a, MADV_DONTNEED); #else __mmap((void *)a, b-a, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0); #endif + errno = e; } unlock_bin(i); @@ -499,7 +501,9 @@ static void unmap_chunk(struct chunk *self) size_t len = CHUNK_SIZE(self) + extra; /* Crash on double free */ if (extra & 1) a_crash(); + int e = errno; __munmap(base, len); + errno = e; } void free(void *p) diff --git a/lib/libc/musl/src/math/acoshf.c b/lib/libc/musl/src/math/acoshf.c index 8a4ec4d57e..b773d48e2b 100644 --- a/lib/libc/musl/src/math/acoshf.c +++ b/lib/libc/musl/src/math/acoshf.c @@ -15,12 +15,12 @@ float acoshf(float x) uint32_t a = u.i & 0x7fffffff; if (a < 0x3f800000+(1<<23)) - /* |x| < 2, invalid if x < 1 or nan */ + /* |x| < 2, invalid if x < 1 */ /* up to 2ulp error in [1,1.125] */ return log1pf(x-1 + sqrtf((x-1)*(x-1)+2*(x-1))); - if (a < 0x3f800000+(12<<23)) - /* |x| < 0x1p12 */ + if (u.i < 0x3f800000+(12<<23)) + /* 2 <= x < 0x1p12 */ return logf(2*x - 1/(x+sqrtf(x*x-1))); - /* x >= 0x1p12 */ + /* x >= 0x1p12 or x <= -2 or nan */ return logf(x) + 0.693147180559945309417232121458176568f; } diff --git a/lib/libc/musl/src/math/expm1f.c b/lib/libc/musl/src/math/expm1f.c index 297e0b44a2..09a41afe7d 100644 --- a/lib/libc/musl/src/math/expm1f.c +++ b/lib/libc/musl/src/math/expm1f.c @@ -16,7 +16,6 @@ #include "libm.h" static const float -o_threshold = 8.8721679688e+01, /* 0x42b17180 */ ln2_hi = 6.9313812256e-01, /* 0x3f317180 */ ln2_lo = 9.0580006145e-06, /* 0x3717f7d1 */ invln2 = 1.4426950216e+00, /* 0x3fb8aa3b */ @@ -41,7 +40,7 @@ float expm1f(float x) return x; if (sign) return -1; - if (x > o_threshold) { + if (hx > 0x42b17217) { /* x > log(FLT_MAX) */ x *= 0x1p127f; return x; } diff --git a/lib/libc/musl/src/math/fmaf.c b/lib/libc/musl/src/math/fmaf.c index 80f5cd8a33..7c65acf1fc 100644 --- a/lib/libc/musl/src/math/fmaf.c +++ b/lib/libc/musl/src/math/fmaf.c @@ -77,17 +77,16 @@ float fmaf(float x, float y, float z) * If result is inexact, and exactly halfway between two float values, * we need to adjust the low-order bit in the direction of the error. */ -#ifdef FE_TOWARDZERO - fesetround(FE_TOWARDZERO); -#endif - volatile double vxy = xy; /* XXX work around gcc CSE bug */ - double adjusted_result = vxy + z; - fesetround(FE_TONEAREST); - if (result == adjusted_result) { - u.f = adjusted_result; + double err; + int neg = u.i >> 63; + if (neg == (z > xy)) + err = xy - result + z; + else + err = z - result + xy; + if (neg == (err < 0)) u.i++; - adjusted_result = u.f; - } - z = adjusted_result; + else + u.i--; + z = u.f; return z; } diff --git a/lib/libc/musl/src/math/powerpc/fabs.c b/lib/libc/musl/src/math/powerpc/fabs.c index 0efc21ef83..9453a3aa98 100644 --- a/lib/libc/musl/src/math/powerpc/fabs.c +++ b/lib/libc/musl/src/math/powerpc/fabs.c @@ -1,6 +1,6 @@ #include -#if defined(_SOFT_FLOAT) || defined(BROKEN_PPC_D_ASM) +#if defined(_SOFT_FLOAT) || defined(__NO_FPRS__) || defined(BROKEN_PPC_D_ASM) #include "../fabs.c" diff --git a/lib/libc/musl/src/math/powerpc/fabsf.c b/lib/libc/musl/src/math/powerpc/fabsf.c index d88b5911c0..2e9da588dd 100644 --- a/lib/libc/musl/src/math/powerpc/fabsf.c +++ b/lib/libc/musl/src/math/powerpc/fabsf.c @@ -1,6 +1,6 @@ #include -#ifdef _SOFT_FLOAT +#if defined(_SOFT_FLOAT) || defined(__NO_FPRS__) #include "../fabsf.c" diff --git a/lib/libc/musl/src/math/powerpc/fma.c b/lib/libc/musl/src/math/powerpc/fma.c index 135c990357..0eb2ba1ef5 100644 --- a/lib/libc/musl/src/math/powerpc/fma.c +++ b/lib/libc/musl/src/math/powerpc/fma.c @@ -1,6 +1,6 @@ #include -#if defined(_SOFT_FLOAT) || defined(BROKEN_PPC_D_ASM) +#if defined(_SOFT_FLOAT) || defined(__NO_FPRS__) || defined(BROKEN_PPC_D_ASM) #include "../fma.c" diff --git a/lib/libc/musl/src/math/powerpc/fmaf.c b/lib/libc/musl/src/math/powerpc/fmaf.c index a99a2a3ba1..dc1a749d98 100644 --- a/lib/libc/musl/src/math/powerpc/fmaf.c +++ b/lib/libc/musl/src/math/powerpc/fmaf.c @@ -1,6 +1,6 @@ #include -#ifdef _SOFT_FLOAT +#if defined(_SOFT_FLOAT) || defined(__NO_FPRS__) #include "../fmaf.c" diff --git a/lib/libc/musl/src/misc/ioctl.c b/lib/libc/musl/src/misc/ioctl.c index 492828119a..35804f026e 100644 --- a/lib/libc/musl/src/misc/ioctl.c +++ b/lib/libc/musl/src/misc/ioctl.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "syscall.h" #define alignof(t) offsetof(struct { char c; t x; }, x) @@ -53,7 +54,7 @@ static const struct ioctl_compat_map compat_map[] = { { _IOWR('A', 0x23, char[136]), _IOWR('A', 0x23, char[132]), 0, WR, 1, 0 }, { 0, 0, 4, WR, 1, 0 }, /* snd_pcm_sync_ptr (flags only) */ { 0, 0, 32, WR, 1, OFFS(8,12,16,24,28) }, /* snd_pcm_mmap_status */ - { 0, 0, 8, WR, 1, OFFS(0,4) }, /* snd_pcm_mmap_control */ + { 0, 0, 4, WR, 1, 0 }, /* snd_pcm_mmap_control (each member) */ /* VIDIOC_QUERYBUF, VIDIOC_QBUF, VIDIOC_DQBUF, VIDIOC_PREPARE_BUF */ { _IOWR('V', 9, new_misaligned(68)), _IOWR('V', 9, char[68]), 68, WR, 1, OFFS(20, 24) }, @@ -90,7 +91,11 @@ static void convert_ioctl_struct(const struct ioctl_compat_map *map, char *old, * if another exception appears this needs changing. */ convert_ioctl_struct(map+1, old, new, dir); convert_ioctl_struct(map+2, old+4, new+8, dir); - convert_ioctl_struct(map+3, old+68, new+72, dir); + /* snd_pcm_mmap_control, special-cased due to kernel + * type definition having been botched. */ + int adj = BYTE_ORDER==BIG_ENDIAN ? 4 : 0; + convert_ioctl_struct(map+3, old+68, new+72+adj, dir); + convert_ioctl_struct(map+3, old+72, new+76+3*adj, dir); return; } for (int i=0; i < map->noffs; i++) { diff --git a/lib/libc/musl/src/passwd/nscd_query.c b/lib/libc/musl/src/passwd/nscd_query.c index d38e371bcd..dc3406b851 100644 --- a/lib/libc/musl/src/passwd/nscd_query.c +++ b/lib/libc/musl/src/passwd/nscd_query.c @@ -40,7 +40,15 @@ retry: buf[0] = NSCDVERSION; fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - if (fd < 0) return NULL; + if (fd < 0) { + if (errno == EAFNOSUPPORT) { + f = fopen("/dev/null", "re"); + if (f) + errno = errno_save; + return f; + } + return 0; + } if(!(f = fdopen(fd, "r"))) { close(fd); diff --git a/lib/libc/musl/src/process/fdop.h b/lib/libc/musl/src/process/fdop.h index 5adf144387..7cf733b21d 100644 --- a/lib/libc/musl/src/process/fdop.h +++ b/lib/libc/musl/src/process/fdop.h @@ -10,3 +10,8 @@ struct fdop { mode_t mode; char path[]; }; + +#define malloc __libc_malloc +#define calloc __libc_calloc +#define realloc undef +#define free __libc_free diff --git a/lib/libc/musl/src/process/posix_spawn_file_actions_addclose.c b/lib/libc/musl/src/process/posix_spawn_file_actions_addclose.c index cdda597991..0c2ef8fa37 100644 --- a/lib/libc/musl/src/process/posix_spawn_file_actions_addclose.c +++ b/lib/libc/musl/src/process/posix_spawn_file_actions_addclose.c @@ -5,6 +5,7 @@ int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *fa, int fd) { + if (fd < 0) return EBADF; struct fdop *op = malloc(sizeof *op); if (!op) return ENOMEM; op->cmd = FDOP_CLOSE; diff --git a/lib/libc/musl/src/process/posix_spawn_file_actions_adddup2.c b/lib/libc/musl/src/process/posix_spawn_file_actions_adddup2.c index 0367498fd4..addca4d4f0 100644 --- a/lib/libc/musl/src/process/posix_spawn_file_actions_adddup2.c +++ b/lib/libc/musl/src/process/posix_spawn_file_actions_adddup2.c @@ -5,6 +5,7 @@ int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa, int srcfd, int fd) { + if (srcfd < 0 || fd < 0) return EBADF; struct fdop *op = malloc(sizeof *op); if (!op) return ENOMEM; op->cmd = FDOP_DUP2; diff --git a/lib/libc/musl/src/process/posix_spawn_file_actions_addfchdir.c b/lib/libc/musl/src/process/posix_spawn_file_actions_addfchdir.c index 436c683d25..e89ede8c3c 100644 --- a/lib/libc/musl/src/process/posix_spawn_file_actions_addfchdir.c +++ b/lib/libc/musl/src/process/posix_spawn_file_actions_addfchdir.c @@ -6,6 +6,7 @@ int posix_spawn_file_actions_addfchdir_np(posix_spawn_file_actions_t *fa, int fd) { + if (fd < 0) return EBADF; struct fdop *op = malloc(sizeof *op); if (!op) return ENOMEM; op->cmd = FDOP_FCHDIR; diff --git a/lib/libc/musl/src/process/posix_spawn_file_actions_addopen.c b/lib/libc/musl/src/process/posix_spawn_file_actions_addopen.c index 368922c76b..82bbcec9eb 100644 --- a/lib/libc/musl/src/process/posix_spawn_file_actions_addopen.c +++ b/lib/libc/musl/src/process/posix_spawn_file_actions_addopen.c @@ -6,6 +6,7 @@ int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *restrict fa, int fd, const char *restrict path, int flags, mode_t mode) { + if (fd < 0) return EBADF; struct fdop *op = malloc(sizeof *op + strlen(path) + 1); if (!op) return ENOMEM; op->cmd = FDOP_OPEN; diff --git a/lib/libc/musl/src/setjmp/powerpc/longjmp.S b/lib/libc/musl/src/setjmp/powerpc/longjmp.S index e598bd056e..611389fed9 100644 --- a/lib/libc/musl/src/setjmp/powerpc/longjmp.S +++ b/lib/libc/musl/src/setjmp/powerpc/longjmp.S @@ -37,7 +37,37 @@ longjmp: lwz 29, 72(3) lwz 30, 76(3) lwz 31, 80(3) -#ifndef _SOFT_FLOAT +#if defined(_SOFT_FLOAT) || defined(__NO_FPRS__) + mflr 0 + bl 1f + .hidden __hwcap + .long __hwcap-. +1: mflr 4 + lwz 5, 0(4) + lwzx 4, 4, 5 + andis. 4, 4, 0x80 + beq 1f + .long 0x11c35b01 /* evldd 14,88(3) */ + .long 0x11e36301 /* ... */ + .long 0x12036b01 + .long 0x12237301 + .long 0x12437b01 + .long 0x12638301 + .long 0x12838b01 + .long 0x12a39301 + .long 0x12c39b01 + .long 0x12e3a301 + .long 0x1303ab01 + .long 0x1323b301 + .long 0x1343bb01 + .long 0x1363c301 + .long 0x1383cb01 + .long 0x13a3d301 + .long 0x13c3db01 + .long 0x13e3e301 /* evldd 31,224(3) */ + .long 0x11a3eb01 /* evldd 13,232(3) */ +1: mtlr 0 +#else lfd 14,88(3) lfd 15,96(3) lfd 16,104(3) diff --git a/lib/libc/musl/src/setjmp/powerpc/setjmp.S b/lib/libc/musl/src/setjmp/powerpc/setjmp.S index cd91a207f5..f1fcce339e 100644 --- a/lib/libc/musl/src/setjmp/powerpc/setjmp.S +++ b/lib/libc/musl/src/setjmp/powerpc/setjmp.S @@ -37,7 +37,37 @@ setjmp: stw 29, 72(3) stw 30, 76(3) stw 31, 80(3) -#ifndef _SOFT_FLOAT +#if defined(_SOFT_FLOAT) || defined(__NO_FPRS__) + mflr 0 + bl 1f + .hidden __hwcap + .long __hwcap-. +1: mflr 4 + lwz 5, 0(4) + lwzx 4, 4, 5 + andis. 4, 4, 0x80 + beq 1f + .long 0x11c35b21 /* evstdd 14,88(3) */ + .long 0x11e36321 /* ... */ + .long 0x12036b21 + .long 0x12237321 + .long 0x12437b21 + .long 0x12638321 + .long 0x12838b21 + .long 0x12a39321 + .long 0x12c39b21 + .long 0x12e3a321 + .long 0x1303ab21 + .long 0x1323b321 + .long 0x1343bb21 + .long 0x1363c321 + .long 0x1383cb21 + .long 0x13a3d321 + .long 0x13c3db21 + .long 0x13e3e321 /* evstdd 31,224(3) */ + .long 0x11a3eb21 /* evstdd 13,232(3) */ +1: mtlr 0 +#else stfd 14,88(3) stfd 15,96(3) stfd 16,104(3) diff --git a/lib/libc/musl/src/signal/block.c b/lib/libc/musl/src/signal/block.c index d7f6100134..cc8698f0bb 100644 --- a/lib/libc/musl/src/signal/block.c +++ b/lib/libc/musl/src/signal/block.c @@ -3,9 +3,9 @@ #include static const unsigned long all_mask[] = { -#if ULONG_MAX == 0xffffffff && _NSIG == 129 +#if ULONG_MAX == 0xffffffff && _NSIG > 65 -1UL, -1UL, -1UL, -1UL -#elif ULONG_MAX == 0xffffffff +#elif ULONG_MAX == 0xffffffff || _NSIG > 65 -1UL, -1UL #else -1UL diff --git a/lib/libc/musl/src/stdio/fgetws.c b/lib/libc/musl/src/stdio/fgetws.c index b08b30491a..195cb4355a 100644 --- a/lib/libc/musl/src/stdio/fgetws.c +++ b/lib/libc/musl/src/stdio/fgetws.c @@ -1,6 +1,5 @@ #include "stdio_impl.h" #include -#include wint_t __fgetwc_unlocked(FILE *); @@ -12,10 +11,6 @@ wchar_t *fgetws(wchar_t *restrict s, int n, FILE *restrict f) FLOCK(f); - /* Setup a dummy errno so we can detect EILSEQ. This is - * the only way to catch encoding errors in the form of a - * partial character just before EOF. */ - errno = EAGAIN; for (; n; n--) { wint_t c = __fgetwc_unlocked(f); if (c == WEOF) break; @@ -23,7 +18,7 @@ wchar_t *fgetws(wchar_t *restrict s, int n, FILE *restrict f) if (c == '\n') break; } *p = 0; - if (ferror(f) || errno==EILSEQ) p = s; + if (ferror(f)) p = s; FUNLOCK(f); diff --git a/lib/libc/musl/src/stdio/fseek.c b/lib/libc/musl/src/stdio/fseek.c index 439308f757..c07f7e9526 100644 --- a/lib/libc/musl/src/stdio/fseek.c +++ b/lib/libc/musl/src/stdio/fseek.c @@ -1,7 +1,14 @@ #include "stdio_impl.h" +#include int __fseeko_unlocked(FILE *f, off_t off, int whence) { + /* Fail immediately for invalid whence argument. */ + if (whence != SEEK_CUR && whence != SEEK_SET && whence != SEEK_END) { + errno = EINVAL; + return -1; + } + /* Adjust relative offset for unread data in buffer, if any. */ if (whence == SEEK_CUR && f->rend) off -= f->rend - f->rpos; diff --git a/lib/libc/musl/src/stdio/getdelim.c b/lib/libc/musl/src/stdio/getdelim.c index d2f5b15ab1..df114441c7 100644 --- a/lib/libc/musl/src/stdio/getdelim.c +++ b/lib/libc/musl/src/stdio/getdelim.c @@ -55,9 +55,11 @@ ssize_t getdelim(char **restrict s, size_t *restrict n, int delim, FILE *restric *s = tmp; *n = m; } - memcpy(*s+i, f->rpos, k); - f->rpos += k; - i += k; + if (k) { + memcpy(*s+i, f->rpos, k); + f->rpos += k; + i += k; + } if (z) break; if ((c = getc_unlocked(f)) == EOF) { if (!i || !feof(f)) { diff --git a/lib/libc/musl/src/stdio/popen.c b/lib/libc/musl/src/stdio/popen.c index 92cb57ee93..3ec833941c 100644 --- a/lib/libc/musl/src/stdio/popen.c +++ b/lib/libc/musl/src/stdio/popen.c @@ -31,25 +31,12 @@ FILE *popen(const char *cmd, const char *mode) __syscall(SYS_close, p[1]); return NULL; } - FLOCK(f); - - /* If the child's end of the pipe happens to already be on the final - * fd number to which it will be assigned (either 0 or 1), it must - * be moved to a different fd. Otherwise, there is no safe way to - * remove the close-on-exec flag in the child without also creating - * a file descriptor leak race condition in the parent. */ - if (p[1-op] == 1-op) { - int tmp = fcntl(1-op, F_DUPFD_CLOEXEC, 0); - if (tmp < 0) { - e = errno; - goto fail; - } - __syscall(SYS_close, p[1-op]); - p[1-op] = tmp; - } e = ENOMEM; if (!posix_spawn_file_actions_init(&fa)) { + for (FILE *l = *__ofl_lock(); l; l=l->next) + if (l->pipe_pid && posix_spawn_file_actions_addclose(&fa, l->fd)) + goto fail; if (!posix_spawn_file_actions_adddup2(&fa, p[1-op], 1-op)) { if (!(e = posix_spawn(&pid, "/bin/sh", &fa, 0, (char *[]){ "sh", "-c", (char *)cmd, 0 }, __environ))) { @@ -58,13 +45,14 @@ FILE *popen(const char *cmd, const char *mode) if (!strchr(mode, 'e')) fcntl(p[op], F_SETFD, 0); __syscall(SYS_close, p[1-op]); - FUNLOCK(f); + __ofl_unlock(); return f; } } +fail: + __ofl_unlock(); posix_spawn_file_actions_destroy(&fa); } -fail: fclose(f); __syscall(SYS_close, p[1-op]); diff --git a/lib/libc/musl/src/stdlib/qsort.c b/lib/libc/musl/src/stdlib/qsort.c index da58fd3177..314ddc29da 100644 --- a/lib/libc/musl/src/stdlib/qsort.c +++ b/lib/libc/musl/src/stdlib/qsort.c @@ -24,6 +24,7 @@ /* Smoothsort, an adaptive variant of Heapsort. Memory usage: O(1). Run time: Worst case O(n log n), close to O(n) in the mostly-sorted case. */ +#define _BSD_SOURCE #include #include #include @@ -31,7 +32,7 @@ #include "atomic.h" #define ntz(x) a_ctz_l((x)) -typedef int (*cmpfun)(const void *, const void *); +typedef int (*cmpfun)(const void *, const void *, void *); static inline int pntz(size_t p[2]) { int r = ntz(p[0] - 1); @@ -88,7 +89,7 @@ static inline void shr(size_t p[2], int n) p[1] >>= n; } -static void sift(unsigned char *head, size_t width, cmpfun cmp, int pshift, size_t lp[]) +static void sift(unsigned char *head, size_t width, cmpfun cmp, void *arg, int pshift, size_t lp[]) { unsigned char *rt, *lf; unsigned char *ar[14 * sizeof(size_t) + 1]; @@ -99,10 +100,10 @@ static void sift(unsigned char *head, size_t width, cmpfun cmp, int pshift, size rt = head - width; lf = head - width - lp[pshift - 2]; - if((*cmp)(ar[0], lf) >= 0 && (*cmp)(ar[0], rt) >= 0) { + if(cmp(ar[0], lf, arg) >= 0 && cmp(ar[0], rt, arg) >= 0) { break; } - if((*cmp)(lf, rt) >= 0) { + if(cmp(lf, rt, arg) >= 0) { ar[i++] = lf; head = lf; pshift -= 1; @@ -115,7 +116,7 @@ static void sift(unsigned char *head, size_t width, cmpfun cmp, int pshift, size cycle(width, ar, i); } -static void trinkle(unsigned char *head, size_t width, cmpfun cmp, size_t pp[2], int pshift, int trusty, size_t lp[]) +static void trinkle(unsigned char *head, size_t width, cmpfun cmp, void *arg, size_t pp[2], int pshift, int trusty, size_t lp[]) { unsigned char *stepson, *rt, *lf; @@ -130,13 +131,13 @@ static void trinkle(unsigned char *head, size_t width, cmpfun cmp, size_t pp[2], ar[0] = head; while(p[0] != 1 || p[1] != 0) { stepson = head - lp[pshift]; - if((*cmp)(stepson, ar[0]) <= 0) { + if(cmp(stepson, ar[0], arg) <= 0) { break; } if(!trusty && pshift > 1) { rt = head - width; lf = head - width - lp[pshift - 2]; - if((*cmp)(rt, stepson) >= 0 || (*cmp)(lf, stepson) >= 0) { + if(cmp(rt, stepson, arg) >= 0 || cmp(lf, stepson, arg) >= 0) { break; } } @@ -150,11 +151,11 @@ static void trinkle(unsigned char *head, size_t width, cmpfun cmp, size_t pp[2], } if(!trusty) { cycle(width, ar, i); - sift(head, width, cmp, pshift, lp); + sift(head, width, cmp, arg, pshift, lp); } } -void qsort(void *base, size_t nel, size_t width, cmpfun cmp) +void __qsort_r(void *base, size_t nel, size_t width, cmpfun cmp, void *arg) { size_t lp[12*sizeof(size_t)]; size_t i, size = width * nel; @@ -173,16 +174,16 @@ void qsort(void *base, size_t nel, size_t width, cmpfun cmp) while(head < high) { if((p[0] & 3) == 3) { - sift(head, width, cmp, pshift, lp); + sift(head, width, cmp, arg, pshift, lp); shr(p, 2); pshift += 2; } else { if(lp[pshift - 1] >= high - head) { - trinkle(head, width, cmp, p, pshift, 0, lp); + trinkle(head, width, cmp, arg, p, pshift, 0, lp); } else { - sift(head, width, cmp, pshift, lp); + sift(head, width, cmp, arg, pshift, lp); } - + if(pshift == 1) { shl(p, 1); pshift = 0; @@ -191,12 +192,12 @@ void qsort(void *base, size_t nel, size_t width, cmpfun cmp) pshift = 1; } } - + p[0] |= 1; head += width; } - trinkle(head, width, cmp, p, pshift, 0, lp); + trinkle(head, width, cmp, arg, p, pshift, 0, lp); while(pshift != 1 || p[0] != 1 || p[1] != 0) { if(pshift <= 1) { @@ -208,11 +209,13 @@ void qsort(void *base, size_t nel, size_t width, cmpfun cmp) pshift -= 2; p[0] ^= 7; shr(p, 1); - trinkle(head - lp[pshift] - width, width, cmp, p, pshift + 1, 1, lp); + trinkle(head - lp[pshift] - width, width, cmp, arg, p, pshift + 1, 1, lp); shl(p, 1); p[0] |= 1; - trinkle(head - width, width, cmp, p, pshift, 1, lp); + trinkle(head - width, width, cmp, arg, p, pshift, 1, lp); } head -= width; } } + +weak_alias(__qsort_r, qsort_r); diff --git a/lib/libc/musl/src/stdlib/qsort_nr.c b/lib/libc/musl/src/stdlib/qsort_nr.c new file mode 100644 index 0000000000..efe7ccecd1 --- /dev/null +++ b/lib/libc/musl/src/stdlib/qsort_nr.c @@ -0,0 +1,14 @@ +#define _BSD_SOURCE +#include + +typedef int (*cmpfun)(const void *, const void *); + +static int wrapper_cmp(const void *v1, const void *v2, void *cmp) +{ + return ((cmpfun)cmp)(v1, v2); +} + +void qsort(void *base, size_t nel, size_t width, cmpfun cmp) +{ + __qsort_r(base, nel, width, wrapper_cmp, cmp); +} diff --git a/lib/libc/musl/src/stdlib/strtod.c b/lib/libc/musl/src/stdlib/strtod.c index a5d0118a2d..39b9daada8 100644 --- a/lib/libc/musl/src/stdlib/strtod.c +++ b/lib/libc/musl/src/stdlib/strtod.c @@ -28,10 +28,3 @@ long double strtold(const char *restrict s, char **restrict p) { return strtox(s, p, 2); } - -weak_alias(strtof, strtof_l); -weak_alias(strtod, strtod_l); -weak_alias(strtold, strtold_l); -weak_alias(strtof, __strtof_l); -weak_alias(strtod, __strtod_l); -weak_alias(strtold, __strtold_l); diff --git a/lib/libc/musl/src/thread/pthread_getname_np.c b/lib/libc/musl/src/thread/pthread_getname_np.c new file mode 100644 index 0000000000..85504e45dc --- /dev/null +++ b/lib/libc/musl/src/thread/pthread_getname_np.c @@ -0,0 +1,25 @@ +#define _GNU_SOURCE +#include +#include +#include + +#include "pthread_impl.h" + +int pthread_getname_np(pthread_t thread, char *name, size_t len) +{ + int fd, cs, status = 0; + char f[sizeof "/proc/self/task//comm" + 3*sizeof(int)]; + + if (len < 16) return ERANGE; + + if (thread == pthread_self()) + return prctl(PR_GET_NAME, (unsigned long)name, 0UL, 0UL, 0UL) ? errno : 0; + + snprintf(f, sizeof f, "/proc/self/task/%d/comm", thread->tid); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); + if ((fd = open(f, O_RDONLY|O_CLOEXEC)) < 0 || (len = read(fd, name, len)) == -1) status = errno; + else name[len-1] = 0; /* remove trailing new line only if successful */ + if (fd >= 0) close(fd); + pthread_setcancelstate(cs, 0); + return status; +} diff --git a/lib/libc/musl/src/thread/pthread_setname_np.c b/lib/libc/musl/src/thread/pthread_setname_np.c index 82d35e17ed..fc2d230618 100644 --- a/lib/libc/musl/src/thread/pthread_setname_np.c +++ b/lib/libc/musl/src/thread/pthread_setname_np.c @@ -19,7 +19,7 @@ int pthread_setname_np(pthread_t thread, const char *name) snprintf(f, sizeof f, "/proc/self/task/%d/comm", thread->tid); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); - if ((fd = open(f, O_WRONLY)) < 0 || write(fd, name, len) < 0) status = errno; + if ((fd = open(f, O_WRONLY|O_CLOEXEC)) < 0 || write(fd, name, len) < 0) status = errno; if (fd >= 0) close(fd); pthread_setcancelstate(cs, 0); return status; diff --git a/lib/libc/musl/src/time/__tz.c b/lib/libc/musl/src/time/__tz.c index 09a6317e6b..c34b3eb755 100644 --- a/lib/libc/musl/src/time/__tz.c +++ b/lib/libc/musl/src/time/__tz.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "libc.h" #include "lock.h" #include "fork_impl.h" @@ -154,10 +155,21 @@ static void do_tzset() } if (old_tz) memcpy(old_tz, s, i+1); + int posix_form = 0; + if (*s != ':') { + p = s; + char dummy_name[TZNAME_MAX+1]; + getname(dummy_name, &p); + if (p!=s && (*p == '+' || *p == '-' || isdigit(*p) + || !strcmp(dummy_name, "UTC") + || !strcmp(dummy_name, "GMT"))) + posix_form = 1; + } + /* Non-suid can use an absolute tzfile pathname or a relative * pathame beginning with "."; in secure mode, only the * standard path will be searched. */ - if (*s == ':' || ((p=strchr(s, '/')) && !memchr(s, ',', p-s))) { + if (!posix_form) { if (*s == ':') s++; if (*s == '/' || *s == '.') { if (!libc.secure || !strcmp(s, "/etc/localtime")) @@ -281,22 +293,20 @@ static size_t scan_trans(long long t, int local, size_t *alt) n = (index-trans)>>scale; if (a == n-1) return -1; if (a == 0) { - x = zi_read32(trans + (a< +#include #include #include #include "syscall.h" @@ -12,5 +13,11 @@ int nice(int inc) prio += getpriority(PRIO_PROCESS, 0); if (prio > NZERO-1) prio = NZERO-1; if (prio < -NZERO) prio = -NZERO; - return setpriority(PRIO_PROCESS, 0, prio) ? -1 : prio; + if (setpriority(PRIO_PROCESS, 0, prio)) { + if (errno == EACCES) + errno = EPERM; + return -1; + } else { + return prio; + } } diff --git a/src/musl.zig b/src/musl.zig index c576dbec84..d061addc9a 100644 --- a/src/musl.zig +++ b/src/musl.zig @@ -118,7 +118,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { try addSrcFile(arena, &source_table, src_file); } - const time32_compat_arch_list = [_][]const u8{ "arm", "i386", "mips", "powerpc" }; + const time32_compat_arch_list = [_][]const u8{ "arm", "i386", "mips", "powerpc", "m68k" }; for (time32_compat_arch_list) |time32_compat_arch| { if (mem.eql(u8, arch_name, time32_compat_arch)) { for (compat_time32_files) |compat_time32_file| { @@ -766,6 +766,7 @@ const src_files = [_][]const u8{ "musl/src/locale/setlocale.c", "musl/src/locale/strcoll.c", "musl/src/locale/strfmon.c", + "musl/src/locale/strtod_l.c", "musl/src/locale/strxfrm.c", "musl/src/locale/textdomain.c", "musl/src/locale/uselocale.c", @@ -1780,6 +1781,7 @@ const src_files = [_][]const u8{ "musl/src/stdlib/llabs.c", "musl/src/stdlib/lldiv.c", "musl/src/stdlib/qsort.c", + "musl/src/stdlib/qsort_nr.c", "musl/src/stdlib/strtod.c", "musl/src/stdlib/strtol.c", "musl/src/stdlib/wcstod.c", @@ -1990,6 +1992,7 @@ const src_files = [_][]const u8{ "musl/src/thread/pthread_getattr_np.c", "musl/src/thread/pthread_getconcurrency.c", "musl/src/thread/pthread_getcpuclockid.c", + "musl/src/thread/pthread_getname_np.c", "musl/src/thread/pthread_getschedparam.c", "musl/src/thread/pthread_getspecific.c", "musl/src/thread/pthread_join.c", @@ -2208,6 +2211,7 @@ const src_files = [_][]const u8{ "musl/src/unistd/writev.c", "musl/src/unistd/x32/lseek.c", }; + const compat_time32_files = [_][]const u8{ "musl/compat/time32/__xstat.c", "musl/compat/time32/adjtime32.c", diff --git a/tools/gen_stubs.zig b/tools/gen_stubs.zig index 64889a1e72..c77818fdb4 100644 --- a/tools/gen_stubs.zig +++ b/tools/gen_stubs.zig @@ -550,10 +550,17 @@ fn fatal(comptime format: []const u8, args: anytype) noreturn { } const blacklisted_symbols = [_][]const u8{ + "__absvdi2", + "__absvsi2", + "__absvti2", "__adddf3", "__addkf3", + "__addodi4", + "__addosi4", + "__addoti4", "__addsf3", "__addtf3", + "__addxf3", "__ashldi3", "__ashlti3", "__ashrdi3", @@ -602,13 +609,23 @@ const blacklisted_symbols = [_][]const u8{ "__atomic_store_2", "__atomic_store_4", "__atomic_store_8", + "__bswapdi2", + "__bswapsi2", + "__bswapti2", + "__ceilh", + "__ceilx", "__clear_cache", "__clzdi2", "__clzsi2", "__clzti2", "__cmpdf2", + "__cmpdi2", "__cmpsf2", + "__cmpsi2", "__cmptf2", + "__cmpti2", + "__cosh", + "__cosx", "__ctzdi2", "__ctzsi2", "__ctzti2", @@ -621,18 +638,30 @@ const blacklisted_symbols = [_][]const u8{ "__divsi3", "__divtf3", "__divti3", + "__divxf3", "__dlstart", "__eqdf2", "__eqkf2", "__eqsf2", "__eqtf2", + "__eqxf2", + "__exp2h", + "__exp2x", + "__exph", + "__expx", "__extenddfkf2", "__extenddftf2", + "__extenddfxf2", "__extendhfsf2", "__extendhftf2", + "__extendhfxf2", "__extendsfdf2", "__extendsfkf2", "__extendsftf2", + "__extendsfxf2", + "__extendxftf2", + "__fabsh", + "__fabsx", "__ffsdi2", "__ffssi2", "__ffsti2", @@ -658,48 +687,81 @@ const blacklisted_symbols = [_][]const u8{ "__fixunstfdi", "__fixunstfsi", "__fixunstfti", + "__fixunsxfdi", + "__fixunsxfsi", + "__fixunsxfti", + "__fixxfdi", + "__fixxfsi", + "__fixxfti", "__floatdidf", "__floatdikf", "__floatdisf", "__floatditf", + "__floatdixf", "__floatsidf", "__floatsikf", "__floatsisf", "__floatsitf", + "__floatsixf", "__floattidf", "__floattisf", "__floattitf", + "__floattixf", "__floatundidf", "__floatundikf", "__floatundisf", "__floatunditf", + "__floatundixf", "__floatunsidf", "__floatunsikf", "__floatunsisf", "__floatunsitf", + "__floatunsixf", "__floatuntidf", + "__floatuntikf", "__floatuntisf", "__floatuntitf", + "__floatuntixf", + "__floorh", + "__floorx", + "__fmah", + "__fmax", + "__fmaxh", + "__fmaxx", + "__fminh", + "__fminx", + "__fmodh", + "__fmodx", "__gedf2", "__gekf2", "__gesf2", "__getf2", + "__gexf2", "__gnu_f2h_ieee", "__gnu_h2f_ieee", "__gtdf2", "__gtkf2", "__gtsf2", "__gttf2", + "__gtxf2", "__ledf2", "__lekf2", "__lesf2", "__letf2", + "__lexf2", + "__log10h", + "__log10x", + "__log2h", + "__log2x", + "__logh", + "__logx", "__lshrdi3", "__lshrti3", "__ltdf2", "__ltkf2", "__ltsf2", "__lttf2", + "__ltxf2", "__moddi3", "__modsi3", "__modti3", @@ -708,6 +770,7 @@ const blacklisted_symbols = [_][]const u8{ "__muldi3", "__mulkf3", "__mulodi4", + "__mulosi4", "__muloti4", "__mulsc3", "__mulsf3", @@ -716,30 +779,61 @@ const blacklisted_symbols = [_][]const u8{ "__multf3", "__multi3", "__mulxc3", + "__mulxf3", "__nedf2", "__negdf2", + "__negdi2", "__negsf2", + "__negsi2", + "__negti2", + "__negvdi2", + "__negvsi2", + "__negvti2", "__nekf2", "__nesf2", "__netf2", + "__nexf2", "__paritydi2", "__paritysi2", "__parityti2", "__popcountdi2", "__popcountsi2", "__popcountti2", + "__roundh", + "__roundx", + "__sincosh", + "__sincosx", + "__sinh", + "__sinx", + "__sqrth", + "__sqrtx", "__subdf3", "__subkf3", + "__subodi4", + "__subosi4", + "__suboti4", "__subsf3", "__subtf3", + "__subxf3", + "__tanh", + "__tanx", "__truncdfhf2", "__truncdfsf2", + "__trunch", "__trunckfdf2", "__trunckfsf2", "__truncsfhf2", "__trunctfdf2", "__trunctfhf2", "__trunctfsf2", + "__trunctfxf2", + "__truncx", + "__truncxfdf2", + "__truncxfhf2", + "__truncxfsf2", + "__ucmpdi2", + "__ucmpsi2", + "__ucmpti2", "__udivdi3", "__udivmoddi4", "__udivmodsi4", @@ -754,5 +848,23 @@ const blacklisted_symbols = [_][]const u8{ "__unordsf2", "__unordtf2", "__zig_probe_stack", + "ceilf128", + "cosf128", + "exp2f128", + "expf128", + "fabsf128", + "floorf128", + "fmaf128", "fmaq", + "fmaxf128", + "fminf128", + "fmodf128", + "log10f128", + "log2f128", + "logf128", + "roundf128", + "sincosf128", + "sinf128", + "sqrtf128", + "truncf128", }; From 3ed9ef3e6bed3fef6d6cad07920d08b28e20ec3e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 3 May 2022 21:50:00 -0700 Subject: [PATCH 1339/2031] Sema: fix bigIntToFloat The implementation had the `@mulAdd` parameters mixed up. --- src/Sema.zig | 4 ++-- src/value.zig | 2 +- test/behavior/floatop.zig | 13 ------------- 3 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index d9c2d1cc2a..3ad9550175 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -19730,8 +19730,8 @@ fn bitCast( try sema.resolveTypeLayout(block, inst_src, old_ty); const target = sema.mod.getTarget(); - var dest_bits = dest_ty.bitSize(target); - var old_bits = old_ty.bitSize(target); + const dest_bits = dest_ty.bitSize(target); + const old_bits = old_ty.bitSize(target); if (old_bits != dest_bits) { return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{}' has {d} bits but source type '{}' has {d} bits", .{ diff --git a/src/value.zig b/src/value.zig index adfe4600f8..e8ccada8ed 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1476,7 +1476,7 @@ pub const Value = extern union { while (i != 0) { i -= 1; const limb: f128 = @intToFloat(f128, limbs[i]); - result = @mulAdd(f128, base, limb, result); + result = @mulAdd(f128, base, result, limb); } if (positive) { return result; diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index c02c1c15c4..536c365655 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -667,24 +667,11 @@ fn fnWithFloatMode() f32 { } test "float literal at compile time not lossy" { - if (builtin.zig_backend != .stage1) { - // https://github.com/ziglang/zig/issues/11169 - return error.SkipZigTest; - } - try expect(16777216.0 + 1.0 == 16777217.0); try expect(9007199254740992.0 + 1.0 == 9007199254740993.0); } test "f128 at compile time is lossy" { - if (builtin.zig_backend != .stage1) { - // this one is happening because we represent comptime-known f128 integers with - // Value.Tag.bigint and only convert to f128 representation if it stops being an - // integer. Is this something we want? need to have a lang spec discussion on this - // topic. - return error.SkipZigTest; // TODO - } - try expect(@as(f128, 10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); } From 28ef79ea808fdb8e00b6d88d1565314a9e412768 Mon Sep 17 00:00:00 2001 From: Jay Petacat Date: Wed, 4 May 2022 10:43:27 -0400 Subject: [PATCH 1340/2031] std: Single-threaded `panicImpl` sleep is unreachable (#11569) Adding `unreachable` prevents the futex code from being inspected during a single-threaded build. Without futex, first draft BYOS packages don't need to implement `nanosleep` to get a single-threaded "hello world" program working. Use of `assert()` did not achieve the desired effect of avoiding futex in a single-threaded build. --- lib/std/debug.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 347a9c433d..1a34d67f9d 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -340,6 +340,7 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize if (panicking.fetchSub(1, .SeqCst) != 1) { // Another thread is panicking, wait for the last one to finish // and call abort() + if (builtin.single_threaded) unreachable; // Sleep forever without hammering the CPU var futex = std.atomic.Atomic(u32).init(0); From 2477a95d29caa3d24177094a991f1b1162915348 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 4 May 2022 18:38:45 +0200 Subject: [PATCH 1341/2031] tools/gen_stubs: sort output by section/symbol name This will make future diffs smaller and easier to review. --- lib/libc/musl/libc.S | 8841 +++++++++++++++++++++--------------------- tools/gen_stubs.zig | 30 + 2 files changed, 4463 insertions(+), 4408 deletions(-) diff --git a/lib/libc/musl/libc.S b/lib/libc/musl/libc.S index e24603b6e1..7ee1f1a4ed 100644 --- a/lib/libc/musl/libc.S +++ b/lib/libc/musl/libc.S @@ -7,7 +7,134 @@ #define PTR_SIZE_BYTES 4 #define PTR2_SIZE_BYTES 8 #endif +.bss +.weak ___environ +.type ___environ, %object; +.size ___environ, PTR_SIZE_BYTES +___environ: +.globl __daylight +.type __daylight, %object; +.size __daylight, 4 +__daylight: +.globl __environ +.type __environ, %object; +.size __environ, PTR_SIZE_BYTES +__environ: +.globl __optpos +.type __optpos, %object; +.size __optpos, 4 +__optpos: +.globl __optreset +.type __optreset, %object; +.size __optreset, 4 +__optreset: +.globl __progname +.type __progname, %object; +.size __progname, PTR_SIZE_BYTES +__progname: +.globl __progname_full +.type __progname_full, %object; +.size __progname_full, PTR_SIZE_BYTES +__progname_full: +.globl __signgam +.type __signgam, %object; +.size __signgam, 4 +__signgam: +.globl __stack_chk_guard +.type __stack_chk_guard, %object; +.size __stack_chk_guard, PTR_SIZE_BYTES +__stack_chk_guard: +.globl __timezone +.type __timezone, %object; +.size __timezone, PTR_SIZE_BYTES +__timezone: +.globl __tzname +.type __tzname, %object; +.size __tzname, PTR2_SIZE_BYTES +__tzname: +.weak _environ +.type _environ, %object; +.size _environ, PTR_SIZE_BYTES +_environ: +.weak daylight +.type daylight, %object; +.size daylight, 4 +daylight: +.weak environ +.type environ, %object; +.size environ, PTR_SIZE_BYTES +environ: +.globl getdate_err +.type getdate_err, %object; +.size getdate_err, 4 +getdate_err: +.globl h_errno +.type h_errno, %object; +.size h_errno, 4 +h_errno: +.globl optarg +.type optarg, %object; +.size optarg, PTR_SIZE_BYTES +optarg: +.globl optopt +.type optopt, %object; +.size optopt, 4 +optopt: +.weak optreset +.type optreset, %object; +.size optreset, 4 +optreset: +.weak program_invocation_name +.type program_invocation_name, %object; +.size program_invocation_name, PTR_SIZE_BYTES +program_invocation_name: +.weak program_invocation_short_name +.type program_invocation_short_name, %object; +.size program_invocation_short_name, PTR_SIZE_BYTES +program_invocation_short_name: +.weak signgam +.type signgam, %object; +.size signgam, 4 +signgam: +.weak timezone +.type timezone, %object; +.size timezone, PTR_SIZE_BYTES +timezone: +.weak tzname +.type tzname, %object; +.size tzname, PTR2_SIZE_BYTES +tzname: +.data +.globl _dl_debug_addr +.type _dl_debug_addr, %object; +.size _dl_debug_addr, PTR_SIZE_BYTES +_dl_debug_addr: +.globl opterr +.type opterr, %object; +.size opterr, 4 +opterr: +.globl optind +.type optind, %object; +.size optind, 4 +optind: +.data.rel.ro +.globl stderr +.type stderr, %object; +.size stderr, PTR_SIZE_BYTES +stderr: +.globl stdin +.type stdin, %object; +.size stdin, PTR_SIZE_BYTES +stdin: +.globl stdout +.type stdout, %object; +.size stdout, PTR_SIZE_BYTES +stdout: .rodata +.globl _ns_flagdata +.type _ns_flagdata, %object; +.size _ns_flagdata, 128 +_ns_flagdata: .globl in6addr_any .type in6addr_any, %object; .size in6addr_any, 16 @@ -16,59 +143,916 @@ in6addr_any: .type in6addr_loopback, %object; .size in6addr_loopback, 16 in6addr_loopback: -.globl _ns_flagdata -.type _ns_flagdata, %object; -.size _ns_flagdata, 128 -_ns_flagdata: .text -.weak aio_read64 -.type aio_read64, %function; -aio_read64: -.globl aio_read -.type aio_read, %function; -aio_read: -.weak aio_write64 -.type aio_write64, %function; -aio_write64: -.globl aio_write -.type aio_write, %function; -aio_write: -.weak aio_fsync64 -.type aio_fsync64, %function; -aio_fsync64: +.globl _Exit +.type _Exit, %function; +_Exit: +.globl _Fork +.type _Fork, %function; +_Fork: +.weak _IO_feof_unlocked +.type _IO_feof_unlocked, %function; +_IO_feof_unlocked: +.weak _IO_ferror_unlocked +.type _IO_ferror_unlocked, %function; +_IO_ferror_unlocked: +.weak _IO_getc +.type _IO_getc, %function; +_IO_getc: +.weak _IO_getc_unlocked +.type _IO_getc_unlocked, %function; +_IO_getc_unlocked: +.weak _IO_putc +.type _IO_putc, %function; +_IO_putc: +.weak _IO_putc_unlocked +.type _IO_putc_unlocked, %function; +_IO_putc_unlocked: +#if !defined(ARCH_riscv64) && !defined(ARCH_mips) && !defined(ARCH_x86_64) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) +.globl ___tls_get_addr +.type ___tls_get_addr, %function; +___tls_get_addr: +#endif +#ifdef PTR32 +.globl __adjtime64 +.type __adjtime64, %function; +__adjtime64: +.globl __adjtimex_time64 +.type __adjtimex_time64, %function; +__adjtimex_time64: +.globl __aio_suspend_time64 +.type __aio_suspend_time64, %function; +__aio_suspend_time64: +#endif +.globl __assert_fail +.type __assert_fail, %function; +__assert_fail: +#if !defined(ARCH_riscv64) && !defined(ARCH_i386) && !defined(ARCH_x86_64) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) +.globl __cachectl +.type __cachectl, %function; +__cachectl: +#endif +#ifdef PTR32 +.globl __clock_adjtime64 +.type __clock_adjtime64, %function; +__clock_adjtime64: +.globl __clock_getres_time64 +.type __clock_getres_time64, %function; +__clock_getres_time64: +.weak __clock_gettime64 +.type __clock_gettime64, %function; +__clock_gettime64: +.weak __clock_nanosleep_time64 +.type __clock_nanosleep_time64, %function; +__clock_nanosleep_time64: +.globl __clock_settime64 +.type __clock_settime64, %function; +__clock_settime64: +.globl __cnd_timedwait_time64 +.type __cnd_timedwait_time64, %function; +__cnd_timedwait_time64: +.globl __ctime64 +.type __ctime64, %function; +__ctime64: +.globl __ctime64_r +.type __ctime64_r, %function; +__ctime64_r: +#endif +.globl __ctype_b_loc +.type __ctype_b_loc, %function; +__ctype_b_loc: +.globl __ctype_get_mb_cur_max +.type __ctype_get_mb_cur_max, %function; +__ctype_get_mb_cur_max: +.globl __ctype_tolower_loc +.type __ctype_tolower_loc, %function; +__ctype_tolower_loc: +.globl __ctype_toupper_loc +.type __ctype_toupper_loc, %function; +__ctype_toupper_loc: +.globl __cxa_atexit +.type __cxa_atexit, %function; +__cxa_atexit: +.globl __cxa_finalize +.type __cxa_finalize, %function; +__cxa_finalize: +#ifdef PTR32 +.globl __difftime64 +.type __difftime64, %function; +__difftime64: +#endif +.globl __dls2b +.type __dls2b, %function; +__dls2b: +.globl __dls3 +.type __dls3, %function; +__dls3: +#ifdef PTR32 +.globl __dlsym_time64 +.type __dlsym_time64, %function; +__dlsym_time64: +#endif +.globl __duplocale +.type __duplocale, %function; +__duplocale: +.globl __errno_location +.type __errno_location, %function; +__errno_location: +.globl __fbufsize +.type __fbufsize, %function; +__fbufsize: +.globl __fgetwc_unlocked +.type __fgetwc_unlocked, %function; +__fgetwc_unlocked: +.globl __flbf +.type __flbf, %function; +__flbf: +.globl __flt_rounds +.type __flt_rounds, %function; +__flt_rounds: +.globl __fpclassify +.type __fpclassify, %function; +__fpclassify: +.globl __fpclassifyf +.type __fpclassifyf, %function; +__fpclassifyf: +.globl __fpclassifyl +.type __fpclassifyl, %function; +__fpclassifyl: +.globl __fpending +.type __fpending, %function; +__fpending: +.globl __fpurge +.type __fpurge, %function; +__fpurge: +.globl __fputwc_unlocked +.type __fputwc_unlocked, %function; +__fputwc_unlocked: +.globl __freadable +.type __freadable, %function; +__freadable: +.globl __freadahead +.type __freadahead, %function; +__freadahead: +.globl __freading +.type __freading, %function; +__freading: +.globl __freadptr +.type __freadptr, %function; +__freadptr: +.globl __freadptrinc +.type __freadptrinc, %function; +__freadptrinc: +.weak __freelocale +.type __freelocale, %function; +__freelocale: +.globl __fseterr +.type __fseterr, %function; +__fseterr: +.globl __fsetlocking +.type __fsetlocking, %function; +__fsetlocking: +#ifdef PTR32 +.globl __fstat_time64 +.type __fstat_time64, %function; +__fstat_time64: +.globl __fstatat_time64 +.type __fstatat_time64, %function; +__fstatat_time64: +.globl __ftime64 +.type __ftime64, %function; +__ftime64: +.globl __futimens_time64 +.type __futimens_time64, %function; +__futimens_time64: +.globl __futimes_time64 +.type __futimes_time64, %function; +__futimes_time64: +.weak __futimesat_time64 +.type __futimesat_time64, %function; +__futimesat_time64: +#endif +.globl __fwritable +.type __fwritable, %function; +__fwritable: +.globl __fwriting +.type __fwriting, %function; +__fwriting: +#if !defined(ARCH_mips) && !defined(ARCH_i386) && !defined(ARCH_powerpc) +.globl __fxstat +.type __fxstat, %function; +__fxstat: +#endif +WEAK64 __fxstat64 +.type __fxstat64, %function; +__fxstat64: +#if !defined(ARCH_mips) && !defined(ARCH_i386) && !defined(ARCH_powerpc) +.globl __fxstatat +.type __fxstatat, %function; +__fxstatat: +#endif +WEAK64 __fxstatat64 +.type __fxstatat64, %function; +__fxstatat64: +.weak __getdelim +.type __getdelim, %function; +__getdelim: +#ifdef PTR32 +.globl __getitimer_time64 +.type __getitimer_time64, %function; +__getitimer_time64: +.globl __getrusage_time64 +.type __getrusage_time64, %function; +__getrusage_time64: +.globl __gettimeofday_time64 +.type __gettimeofday_time64, %function; +__gettimeofday_time64: +.globl __gmtime64 +.type __gmtime64, %function; +__gmtime64: +.weak __gmtime64_r +.type __gmtime64_r, %function; +__gmtime64_r: +#endif +.globl __h_errno_location +.type __h_errno_location, %function; +__h_errno_location: +.globl __isalnum_l +.type __isalnum_l, %function; +__isalnum_l: +.globl __isalpha_l +.type __isalpha_l, %function; +__isalpha_l: +.globl __isblank_l +.type __isblank_l, %function; +__isblank_l: +.globl __iscntrl_l +.type __iscntrl_l, %function; +__iscntrl_l: +.globl __isdigit_l +.type __isdigit_l, %function; +__isdigit_l: +.globl __isgraph_l +.type __isgraph_l, %function; +__isgraph_l: +.globl __islower_l +.type __islower_l, %function; +__islower_l: +.weak __isoc99_fscanf +.type __isoc99_fscanf, %function; +__isoc99_fscanf: +.weak __isoc99_fwscanf +.type __isoc99_fwscanf, %function; +__isoc99_fwscanf: +.weak __isoc99_scanf +.type __isoc99_scanf, %function; +__isoc99_scanf: +.weak __isoc99_sscanf +.type __isoc99_sscanf, %function; +__isoc99_sscanf: +.weak __isoc99_swscanf +.type __isoc99_swscanf, %function; +__isoc99_swscanf: +.weak __isoc99_vfscanf +.type __isoc99_vfscanf, %function; +__isoc99_vfscanf: +.weak __isoc99_vfwscanf +.type __isoc99_vfwscanf, %function; +__isoc99_vfwscanf: +.weak __isoc99_vscanf +.type __isoc99_vscanf, %function; +__isoc99_vscanf: +.weak __isoc99_vsscanf +.type __isoc99_vsscanf, %function; +__isoc99_vsscanf: +.weak __isoc99_vswscanf +.type __isoc99_vswscanf, %function; +__isoc99_vswscanf: +.weak __isoc99_vwscanf +.type __isoc99_vwscanf, %function; +__isoc99_vwscanf: +.weak __isoc99_wscanf +.type __isoc99_wscanf, %function; +__isoc99_wscanf: +.globl __isprint_l +.type __isprint_l, %function; +__isprint_l: +.globl __ispunct_l +.type __ispunct_l, %function; +__ispunct_l: +.globl __isspace_l +.type __isspace_l, %function; +__isspace_l: +.globl __isupper_l +.type __isupper_l, %function; +__isupper_l: +.globl __iswalnum_l +.type __iswalnum_l, %function; +__iswalnum_l: +.globl __iswalpha_l +.type __iswalpha_l, %function; +__iswalpha_l: +.globl __iswblank_l +.type __iswblank_l, %function; +__iswblank_l: +.globl __iswcntrl_l +.type __iswcntrl_l, %function; +__iswcntrl_l: +.globl __iswctype_l +.type __iswctype_l, %function; +__iswctype_l: +.globl __iswdigit_l +.type __iswdigit_l, %function; +__iswdigit_l: +.globl __iswgraph_l +.type __iswgraph_l, %function; +__iswgraph_l: +.globl __iswlower_l +.type __iswlower_l, %function; +__iswlower_l: +.globl __iswprint_l +.type __iswprint_l, %function; +__iswprint_l: +.globl __iswpunct_l +.type __iswpunct_l, %function; +__iswpunct_l: +.globl __iswspace_l +.type __iswspace_l, %function; +__iswspace_l: +.globl __iswupper_l +.type __iswupper_l, %function; +__iswupper_l: +.globl __iswxdigit_l +.type __iswxdigit_l, %function; +__iswxdigit_l: +.globl __isxdigit_l +.type __isxdigit_l, %function; +__isxdigit_l: +.globl __lgammal_r +.type __lgammal_r, %function; +__lgammal_r: +.globl __libc_current_sigrtmax +.type __libc_current_sigrtmax, %function; +__libc_current_sigrtmax: +.globl __libc_current_sigrtmin +.type __libc_current_sigrtmin, %function; +__libc_current_sigrtmin: +.globl __libc_start_main +.type __libc_start_main, %function; +__libc_start_main: +#ifdef PTR32 +.globl __localtime64 +.type __localtime64, %function; +__localtime64: +.weak __localtime64_r +.type __localtime64_r, %function; +__localtime64_r: +#endif +#if !defined(ARCH_mips) && !defined(ARCH_i386) && !defined(ARCH_x86_64) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) +.globl __longjmp +.type __longjmp, %function; +__longjmp: +#endif +#ifdef PTR32 +.globl __lstat_time64 +.type __lstat_time64, %function; +__lstat_time64: +.globl __lutimes_time64 +.type __lutimes_time64, %function; +__lutimes_time64: +#endif +#if !defined(ARCH_mips) && !defined(ARCH_i386) && !defined(ARCH_powerpc) +.globl __lxstat +.type __lxstat, %function; +__lxstat: +#endif +WEAK64 __lxstat64 +.type __lxstat64, %function; +__lxstat64: +#ifdef PTR32 +.globl __mktime64 +.type __mktime64, %function; +__mktime64: +.globl __mq_timedreceive_time64 +.type __mq_timedreceive_time64, %function; +__mq_timedreceive_time64: +.globl __mq_timedsend_time64 +.type __mq_timedsend_time64, %function; +__mq_timedsend_time64: +.globl __mtx_timedlock_time64 +.type __mtx_timedlock_time64, %function; +__mtx_timedlock_time64: +.globl __nanosleep_time64 +.type __nanosleep_time64, %function; +__nanosleep_time64: +#endif +.globl __newlocale +.type __newlocale, %function; +__newlocale: +.globl __nl_langinfo +.type __nl_langinfo, %function; +__nl_langinfo: +.globl __nl_langinfo_l +.type __nl_langinfo_l, %function; +__nl_langinfo_l: +.globl __overflow +.type __overflow, %function; +.protected __overflow +__overflow: +.weak __posix_getopt +.type __posix_getopt, %function; +__posix_getopt: +#ifdef PTR32 +.globl __ppoll_time64 +.type __ppoll_time64, %function; +__ppoll_time64: +.globl __pselect_time64 +.type __pselect_time64, %function; +__pselect_time64: +.weak __pthread_cond_timedwait_time64 +.type __pthread_cond_timedwait_time64, %function; +__pthread_cond_timedwait_time64: +.weak __pthread_mutex_timedlock_time64 +.type __pthread_mutex_timedlock_time64, %function; +__pthread_mutex_timedlock_time64: +.weak __pthread_rwlock_timedrdlock_time64 +.type __pthread_rwlock_timedrdlock_time64, %function; +__pthread_rwlock_timedrdlock_time64: +.weak __pthread_rwlock_timedwrlock_time64 +.type __pthread_rwlock_timedwrlock_time64, %function; +__pthread_rwlock_timedwrlock_time64: +.weak __pthread_timedjoin_np_time64 +.type __pthread_timedjoin_np_time64, %function; +__pthread_timedjoin_np_time64: +.globl __recvmmsg_time64 +.type __recvmmsg_time64, %function; +__recvmmsg_time64: +#endif +.globl __res_state +.type __res_state, %function; +__res_state: +#if !defined(ARCH_mips) && !defined(ARCH_i386) && !defined(ARCH_x86_64) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) +.globl __riscv_flush_icache +.type __riscv_flush_icache, %function; +__riscv_flush_icache: +#endif +.globl __sched_cpucount +.type __sched_cpucount, %function; +__sched_cpucount: +#ifdef PTR32 +.globl __sched_rr_get_interval_time64 +.type __sched_rr_get_interval_time64, %function; +__sched_rr_get_interval_time64: +.globl __select_time64 +.type __select_time64, %function; +__select_time64: +.globl __sem_timedwait_time64 +.type __sem_timedwait_time64, %function; +__sem_timedwait_time64: +.globl __semtimedop_time64 +.type __semtimedop_time64, %function; +__semtimedop_time64: +.globl __setitimer_time64 +.type __setitimer_time64, %function; +__setitimer_time64: +#endif +.globl __setjmp +.type __setjmp, %function; +__setjmp: +#ifdef PTR32 +.globl __settimeofday_time64 +.type __settimeofday_time64, %function; +__settimeofday_time64: +#endif +.globl __signbit +.type __signbit, %function; +__signbit: +.globl __signbitf +.type __signbitf, %function; +__signbitf: +.globl __signbitl +.type __signbitl, %function; +__signbitl: +.globl __sigsetjmp +.type __sigsetjmp, %function; +__sigsetjmp: +#ifdef PTR32 +.globl __sigtimedwait_time64 +.type __sigtimedwait_time64, %function; +__sigtimedwait_time64: +#endif +.globl __stack_chk_fail +.type __stack_chk_fail, %function; +__stack_chk_fail: +#ifdef PTR32 +.globl __stat_time64 +.type __stat_time64, %function; +__stat_time64: +.globl __stime64 +.type __stime64, %function; +__stime64: +#endif +.globl __strcasecmp_l +.type __strcasecmp_l, %function; +__strcasecmp_l: +.globl __strcoll_l +.type __strcoll_l, %function; +__strcoll_l: +.globl __strerror_l +.type __strerror_l, %function; +__strerror_l: +.globl __strncasecmp_l +.type __strncasecmp_l, %function; +__strncasecmp_l: +.weak __strtod_l +.type __strtod_l, %function; +__strtod_l: +.weak __strtof_l +.type __strtof_l, %function; +__strtof_l: +.weak __strtoimax_internal +.type __strtoimax_internal, %function; +__strtoimax_internal: +.weak __strtol_internal +.type __strtol_internal, %function; +__strtol_internal: +.weak __strtold_l +.type __strtold_l, %function; +__strtold_l: +.weak __strtoll_internal +.type __strtoll_internal, %function; +__strtoll_internal: +.weak __strtoul_internal +.type __strtoul_internal, %function; +__strtoul_internal: +.weak __strtoull_internal +.type __strtoull_internal, %function; +__strtoull_internal: +.weak __strtoumax_internal +.type __strtoumax_internal, %function; +__strtoumax_internal: +.globl __strxfrm_l +.type __strxfrm_l, %function; +__strxfrm_l: +.weak __sysv_signal +.type __sysv_signal, %function; +__sysv_signal: +#ifdef PTR32 +.globl __thrd_sleep_time64 +.type __thrd_sleep_time64, %function; +__thrd_sleep_time64: +.globl __time64 +.type __time64, %function; +__time64: +.globl __timegm_time64 +.type __timegm_time64, %function; +__timegm_time64: +.globl __timer_gettime64 +.type __timer_gettime64, %function; +__timer_gettime64: +.globl __timer_settime64 +.type __timer_settime64, %function; +__timer_settime64: +.globl __timerfd_gettime64 +.type __timerfd_gettime64, %function; +__timerfd_gettime64: +.globl __timerfd_settime64 +.type __timerfd_settime64, %function; +__timerfd_settime64: +.globl __timespec_get_time64 +.type __timespec_get_time64, %function; +__timespec_get_time64: +#endif +.globl __tls_get_addr +.type __tls_get_addr, %function; +__tls_get_addr: +.globl __tolower_l +.type __tolower_l, %function; +__tolower_l: +.globl __toupper_l +.type __toupper_l, %function; +__toupper_l: +.globl __towctrans_l +.type __towctrans_l, %function; +__towctrans_l: +.globl __towlower_l +.type __towlower_l, %function; +__towlower_l: +.globl __towupper_l +.type __towupper_l, %function; +__towupper_l: +.globl __uflow +.type __uflow, %function; +.protected __uflow +__uflow: +.globl __uselocale +.type __uselocale, %function; +__uselocale: +#ifdef PTR32 +.globl __utime64 +.type __utime64, %function; +__utime64: +.globl __utimensat_time64 +.type __utimensat_time64, %function; +__utimensat_time64: +.globl __utimes_time64 +.type __utimes_time64, %function; +__utimes_time64: +.globl __wait3_time64 +.type __wait3_time64, %function; +__wait3_time64: +.globl __wait4_time64 +.type __wait4_time64, %function; +__wait4_time64: +#endif +.globl __wcscoll_l +.type __wcscoll_l, %function; +__wcscoll_l: +.globl __wcsftime_l +.type __wcsftime_l, %function; +__wcsftime_l: +.globl __wcsxfrm_l +.type __wcsxfrm_l, %function; +__wcsxfrm_l: +.globl __wctrans_l +.type __wctrans_l, %function; +__wctrans_l: +.globl __wctype_l +.type __wctype_l, %function; +__wctype_l: +.globl __xmknod +.type __xmknod, %function; +__xmknod: +.globl __xmknodat +.type __xmknodat, %function; +__xmknodat: +.weak __xpg_basename +.type __xpg_basename, %function; +__xpg_basename: +.weak __xpg_strerror_r +.type __xpg_strerror_r, %function; +__xpg_strerror_r: +#if !defined(ARCH_mips) && !defined(ARCH_i386) && !defined(ARCH_powerpc) +.globl __xstat +.type __xstat, %function; +__xstat: +#endif +WEAK64 __xstat64 +.type __xstat64, %function; +__xstat64: +.weak _dl_debug_state +.type _dl_debug_state, %function; +_dl_debug_state: +.globl _dlstart +.type _dlstart, %function; +_dlstart: +.globl _exit +.type _exit, %function; +_exit: +.weak _fini +.type _fini, %function; +_fini: +#if !defined(ARCH_riscv64) && !defined(ARCH_i386) && !defined(ARCH_x86_64) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) +.globl _flush_cache +.type _flush_cache, %function; +_flush_cache: +#endif +.globl _flushlbf +.type _flushlbf, %function; +_flushlbf: +.weak _init +.type _init, %function; +_init: +.globl _longjmp +.type _longjmp, %function; +_longjmp: +.globl _pthread_cleanup_pop +.type _pthread_cleanup_pop, %function; +_pthread_cleanup_pop: +.globl _pthread_cleanup_push +.type _pthread_cleanup_push, %function; +_pthread_cleanup_push: +.globl _setjmp +.type _setjmp, %function; +_setjmp: +.globl a64l +.type a64l, %function; +a64l: +.globl abort +.type abort, %function; +abort: +.globl abs +.type abs, %function; +abs: +.globl accept +.type accept, %function; +accept: +.globl accept4 +.type accept4, %function; +accept4: +.globl access +.type access, %function; +access: +.globl acct +.type acct, %function; +acct: +.globl acos +.type acos, %function; +acos: +.globl acosf +.type acosf, %function; +acosf: +.globl acosh +.type acosh, %function; +acosh: +.globl acoshf +.type acoshf, %function; +acoshf: +.globl acoshl +.type acoshl, %function; +acoshl: +.globl acosl +.type acosl, %function; +acosl: +.globl addmntent +.type addmntent, %function; +addmntent: +.globl adjtime +.type adjtime, %function; +adjtime: +.globl adjtimex +.type adjtimex, %function; +adjtimex: +.globl aio_cancel +.type aio_cancel, %function; +aio_cancel: +.weak aio_cancel64 +.type aio_cancel64, %function; +aio_cancel64: +.globl aio_error +.type aio_error, %function; +aio_error: +.weak aio_error64 +.type aio_error64, %function; +aio_error64: .globl aio_fsync .type aio_fsync, %function; aio_fsync: +.weak aio_fsync64 +.type aio_fsync64, %function; +aio_fsync64: +.globl aio_read +.type aio_read, %function; +aio_read: +.weak aio_read64 +.type aio_read64, %function; +aio_read64: .globl aio_return .type aio_return, %function; aio_return: .weak aio_return64 .type aio_return64, %function; aio_return64: -.weak aio_error64 -.type aio_error64, %function; -aio_error64: -.globl aio_error -.type aio_error, %function; -aio_error: -.weak aio_cancel64 -.type aio_cancel64, %function; -aio_cancel64: -.globl aio_cancel -.type aio_cancel, %function; -aio_cancel: -.weak aio_suspend64 -.type aio_suspend64, %function; -aio_suspend64: .globl aio_suspend .type aio_suspend, %function; aio_suspend: -.weak lio_listio64 -.type lio_listio64, %function; -lio_listio64: -.globl lio_listio -.type lio_listio, %function; -lio_listio: +.weak aio_suspend64 +.type aio_suspend64, %function; +aio_suspend64: +.globl aio_write +.type aio_write, %function; +aio_write: +.weak aio_write64 +.type aio_write64, %function; +aio_write64: +.globl alarm +.type alarm, %function; +alarm: +.globl aligned_alloc +.type aligned_alloc, %function; +aligned_alloc: +.globl alphasort +.type alphasort, %function; +alphasort: +.weak alphasort64 +.type alphasort64, %function; +alphasort64: +#if !defined(ARCH_riscv64) && !defined(ARCH_mips) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) +.globl arch_prctl +.type arch_prctl, %function; +arch_prctl: +#endif +.globl asctime +.type asctime, %function; +asctime: +.weak asctime_r +.type asctime_r, %function; +asctime_r: +.globl asin +.type asin, %function; +asin: +.globl asinf +.type asinf, %function; +asinf: +.globl asinh +.type asinh, %function; +asinh: +.globl asinhf +.type asinhf, %function; +asinhf: +.globl asinhl +.type asinhl, %function; +asinhl: +.globl asinl +.type asinl, %function; +asinl: +.globl asprintf +.type asprintf, %function; +asprintf: +.globl at_quick_exit +.type at_quick_exit, %function; +at_quick_exit: +.globl atan +.type atan, %function; +atan: +.globl atan2 +.type atan2, %function; +atan2: +.globl atan2f +.type atan2f, %function; +atan2f: +.globl atan2l +.type atan2l, %function; +atan2l: +.globl atanf +.type atanf, %function; +atanf: +.globl atanh +.type atanh, %function; +atanh: +.globl atanhf +.type atanhf, %function; +atanhf: +.globl atanhl +.type atanhl, %function; +atanhl: +.globl atanl +.type atanl, %function; +atanl: +.globl atexit +.type atexit, %function; +atexit: +.globl atof +.type atof, %function; +atof: +.globl atoi +.type atoi, %function; +atoi: +.globl atol +.type atol, %function; +atol: +.globl atoll +.type atoll, %function; +atoll: +.globl basename +.type basename, %function; +basename: +.globl bcmp +.type bcmp, %function; +bcmp: +.globl bcopy +.type bcopy, %function; +bcopy: +.globl bind +.type bind, %function; +bind: +.globl bind_textdomain_codeset +.type bind_textdomain_codeset, %function; +bind_textdomain_codeset: +.globl bindtextdomain +.type bindtextdomain, %function; +bindtextdomain: +.globl brk +.type brk, %function; +brk: +.weak bsd_signal +.type bsd_signal, %function; +bsd_signal: +.globl bsearch +.type bsearch, %function; +bsearch: +.globl btowc +.type btowc, %function; +btowc: +.globl bzero +.type bzero, %function; +bzero: +.globl c16rtomb +.type c16rtomb, %function; +c16rtomb: +.globl c32rtomb +.type c32rtomb, %function; +c32rtomb: .globl cabs .type cabs, %function; cabs: @@ -78,6 +1062,16 @@ cabsf: .globl cabsl .type cabsl, %function; cabsl: +#if !defined(ARCH_riscv64) && !defined(ARCH_i386) && !defined(ARCH_x86_64) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) +.weak cachectl +.type cachectl, %function; +cachectl: +#endif +#if !defined(ARCH_riscv64) && !defined(ARCH_i386) && !defined(ARCH_x86_64) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) +.weak cacheflush +.type cacheflush, %function; +cacheflush: +#endif .globl cacos .type cacos, %function; cacos: @@ -96,6 +1090,18 @@ cacoshl: .globl cacosl .type cacosl, %function; cacosl: +.globl call_once +.type call_once, %function; +call_once: +.globl calloc +.type calloc, %function; +calloc: +.globl capget +.type capget, %function; +capget: +.globl capset +.type capset, %function; +capset: .globl carg .type carg, %function; carg: @@ -141,6 +1147,24 @@ catanhl: .globl catanl .type catanl, %function; catanl: +.globl catclose +.type catclose, %function; +catclose: +.globl catgets +.type catgets, %function; +catgets: +.globl catopen +.type catopen, %function; +catopen: +.globl cbrt +.type cbrt, %function; +cbrt: +.globl cbrtf +.type cbrtf, %function; +cbrtf: +.globl cbrtl +.type cbrtl, %function; +cbrtl: .globl ccos .type ccos, %function; ccos: @@ -159,6 +1183,15 @@ ccoshl: .globl ccosl .type ccosl, %function; ccosl: +.globl ceil +.type ceil, %function; +ceil: +.globl ceilf +.type ceilf, %function; +ceilf: +.globl ceill +.type ceill, %function; +ceill: .globl cexp .type cexp, %function; cexp: @@ -168,6 +1201,36 @@ cexpf: .globl cexpl .type cexpl, %function; cexpl: +.globl cfgetispeed +.type cfgetispeed, %function; +cfgetispeed: +.globl cfgetospeed +.type cfgetospeed, %function; +cfgetospeed: +.globl cfmakeraw +.type cfmakeraw, %function; +cfmakeraw: +.globl cfsetispeed +.type cfsetispeed, %function; +cfsetispeed: +.globl cfsetospeed +.type cfsetospeed, %function; +cfsetospeed: +.weak cfsetspeed +.type cfsetspeed, %function; +cfsetspeed: +.globl chdir +.type chdir, %function; +chdir: +.globl chmod +.type chmod, %function; +chmod: +.globl chown +.type chown, %function; +chown: +.globl chroot +.type chroot, %function; +chroot: .globl cimag .type cimag, %function; cimag: @@ -177,6 +1240,36 @@ cimagf: .globl cimagl .type cimagl, %function; cimagl: +.globl clearenv +.type clearenv, %function; +clearenv: +.globl clearerr +.type clearerr, %function; +clearerr: +.weak clearerr_unlocked +.type clearerr_unlocked, %function; +clearerr_unlocked: +.globl clock +.type clock, %function; +clock: +.globl clock_adjtime +.type clock_adjtime, %function; +clock_adjtime: +.globl clock_getcpuclockid +.type clock_getcpuclockid, %function; +clock_getcpuclockid: +.globl clock_getres +.type clock_getres, %function; +clock_getres: +WEAK64 clock_gettime +.type clock_gettime, %function; +clock_gettime: +WEAK64 clock_nanosleep +.type clock_nanosleep, %function; +clock_nanosleep: +.globl clock_settime +.type clock_settime, %function; +clock_settime: .globl clog .type clog, %function; clog: @@ -186,6 +1279,39 @@ clogf: .globl clogl .type clogl, %function; clogl: +.globl clone +.type clone, %function; +clone: +.globl close +.type close, %function; +close: +.globl closedir +.type closedir, %function; +closedir: +.globl closelog +.type closelog, %function; +closelog: +.globl cnd_broadcast +.type cnd_broadcast, %function; +cnd_broadcast: +.globl cnd_destroy +.type cnd_destroy, %function; +cnd_destroy: +.globl cnd_init +.type cnd_init, %function; +cnd_init: +.globl cnd_signal +.type cnd_signal, %function; +cnd_signal: +.globl cnd_timedwait +.type cnd_timedwait, %function; +cnd_timedwait: +.globl cnd_wait +.type cnd_wait, %function; +cnd_wait: +.globl confstr +.type confstr, %function; +confstr: .globl conj .type conj, %function; conj: @@ -195,6 +1321,39 @@ conjf: .globl conjl .type conjl, %function; conjl: +.globl connect +.type connect, %function; +connect: +.globl copy_file_range +.type copy_file_range, %function; +copy_file_range: +.globl copysign +.type copysign, %function; +copysign: +.globl copysignf +.type copysignf, %function; +copysignf: +.globl copysignl +.type copysignl, %function; +copysignl: +.globl cos +.type cos, %function; +cos: +.globl cosf +.type cosf, %function; +cosf: +.globl cosh +.type cosh, %function; +cosh: +.globl coshf +.type coshf, %function; +coshf: +.globl coshl +.type coshl, %function; +coshl: +.globl cosl +.type cosl, %function; +cosl: .globl cpow .type cpow, %function; cpow: @@ -222,6 +1381,18 @@ crealf: .globl creall .type creall, %function; creall: +.globl creat +.type creat, %function; +creat: +.weak creat64 +.type creat64, %function; +creat64: +.globl crypt +.type crypt, %function; +crypt: +.weak crypt_r +.type crypt_r, %function; +crypt_r: .globl csin .type csin, %function; csin: @@ -267,582 +1438,51 @@ ctanhl: .globl ctanl .type ctanl, %function; ctanl: -.globl confstr -.type confstr, %function; -confstr: -.globl fpathconf -.type fpathconf, %function; -fpathconf: -.globl get_nprocs_conf -.type get_nprocs_conf, %function; -get_nprocs_conf: -.globl get_nprocs -.type get_nprocs, %function; -get_nprocs: -.globl get_phys_pages -.type get_phys_pages, %function; -get_phys_pages: -.globl get_avphys_pages -.type get_avphys_pages, %function; -get_avphys_pages: -.globl pathconf -.type pathconf, %function; -pathconf: -.globl sysconf -.type sysconf, %function; -sysconf: -.globl crypt -.type crypt, %function; -crypt: -.weak crypt_r -.type crypt_r, %function; -crypt_r: -.globl setkey -.type setkey, %function; -setkey: -.globl encrypt -.type encrypt, %function; -encrypt: -.globl __ctype_b_loc -.type __ctype_b_loc, %function; -__ctype_b_loc: -.globl __ctype_get_mb_cur_max -.type __ctype_get_mb_cur_max, %function; -__ctype_get_mb_cur_max: -.globl __ctype_tolower_loc -.type __ctype_tolower_loc, %function; -__ctype_tolower_loc: -.globl __ctype_toupper_loc -.type __ctype_toupper_loc, %function; -__ctype_toupper_loc: -.globl isalnum -.type isalnum, %function; -isalnum: -.globl __isalnum_l -.type __isalnum_l, %function; -__isalnum_l: -.weak isalnum_l -.type isalnum_l, %function; -isalnum_l: -.globl isalpha -.type isalpha, %function; -isalpha: -.globl __isalpha_l -.type __isalpha_l, %function; -__isalpha_l: -.weak isalpha_l -.type isalpha_l, %function; -isalpha_l: -.globl isascii -.type isascii, %function; -isascii: -.globl isblank -.type isblank, %function; -isblank: -.globl __isblank_l -.type __isblank_l, %function; -__isblank_l: -.weak isblank_l -.type isblank_l, %function; -isblank_l: -.globl iscntrl -.type iscntrl, %function; -iscntrl: -.globl __iscntrl_l -.type __iscntrl_l, %function; -__iscntrl_l: -.weak iscntrl_l -.type iscntrl_l, %function; -iscntrl_l: -.globl isdigit -.type isdigit, %function; -isdigit: -.globl __isdigit_l -.type __isdigit_l, %function; -__isdigit_l: -.weak isdigit_l -.type isdigit_l, %function; -isdigit_l: -.globl isgraph -.type isgraph, %function; -isgraph: -.globl __isgraph_l -.type __isgraph_l, %function; -__isgraph_l: -.weak isgraph_l -.type isgraph_l, %function; -isgraph_l: -.globl islower -.type islower, %function; -islower: -.globl __islower_l -.type __islower_l, %function; -__islower_l: -.weak islower_l -.type islower_l, %function; -islower_l: -.globl isprint -.type isprint, %function; -isprint: -.globl __isprint_l -.type __isprint_l, %function; -__isprint_l: -.weak isprint_l -.type isprint_l, %function; -isprint_l: -.globl ispunct -.type ispunct, %function; -ispunct: -.globl __ispunct_l -.type __ispunct_l, %function; -__ispunct_l: -.weak ispunct_l -.type ispunct_l, %function; -ispunct_l: -.globl isspace -.type isspace, %function; -isspace: -.globl __isspace_l -.type __isspace_l, %function; -__isspace_l: -.weak isspace_l -.type isspace_l, %function; -isspace_l: -.globl isupper -.type isupper, %function; -isupper: -.globl __isupper_l -.type __isupper_l, %function; -__isupper_l: -.weak isupper_l -.type isupper_l, %function; -isupper_l: -.globl iswalnum -.type iswalnum, %function; -iswalnum: -.weak iswalnum_l -.type iswalnum_l, %function; -iswalnum_l: -.globl __iswalnum_l -.type __iswalnum_l, %function; -__iswalnum_l: -.globl iswalpha -.type iswalpha, %function; -iswalpha: -.weak iswalpha_l -.type iswalpha_l, %function; -iswalpha_l: -.globl __iswalpha_l -.type __iswalpha_l, %function; -__iswalpha_l: -.globl iswblank -.type iswblank, %function; -iswblank: -.weak iswblank_l -.type iswblank_l, %function; -iswblank_l: -.globl __iswblank_l -.type __iswblank_l, %function; -__iswblank_l: -.globl iswcntrl -.type iswcntrl, %function; -iswcntrl: -.weak iswcntrl_l -.type iswcntrl_l, %function; -iswcntrl_l: -.globl __iswcntrl_l -.type __iswcntrl_l, %function; -__iswcntrl_l: -.globl iswctype -.type iswctype, %function; -iswctype: -.globl wctype -.type wctype, %function; -wctype: -.weak iswctype_l -.type iswctype_l, %function; -iswctype_l: -.globl __iswctype_l -.type __iswctype_l, %function; -__iswctype_l: -.globl __wctype_l -.type __wctype_l, %function; -__wctype_l: -.weak wctype_l -.type wctype_l, %function; -wctype_l: -.globl iswdigit -.type iswdigit, %function; -iswdigit: -.weak iswdigit_l -.type iswdigit_l, %function; -iswdigit_l: -.globl __iswdigit_l -.type __iswdigit_l, %function; -__iswdigit_l: -.globl iswgraph -.type iswgraph, %function; -iswgraph: -.weak iswgraph_l -.type iswgraph_l, %function; -iswgraph_l: -.globl __iswgraph_l -.type __iswgraph_l, %function; -__iswgraph_l: -.globl iswlower -.type iswlower, %function; -iswlower: -.weak iswlower_l -.type iswlower_l, %function; -iswlower_l: -.globl __iswlower_l -.type __iswlower_l, %function; -__iswlower_l: -.globl iswprint -.type iswprint, %function; -iswprint: -.weak iswprint_l -.type iswprint_l, %function; -iswprint_l: -.globl __iswprint_l -.type __iswprint_l, %function; -__iswprint_l: -.globl iswpunct -.type iswpunct, %function; -iswpunct: -.weak iswpunct_l -.type iswpunct_l, %function; -iswpunct_l: -.globl __iswpunct_l -.type __iswpunct_l, %function; -__iswpunct_l: -.globl iswspace -.type iswspace, %function; -iswspace: -.weak iswspace_l -.type iswspace_l, %function; -iswspace_l: -.globl __iswspace_l -.type __iswspace_l, %function; -__iswspace_l: -.globl iswupper -.type iswupper, %function; -iswupper: -.weak iswupper_l -.type iswupper_l, %function; -iswupper_l: -.globl __iswupper_l -.type __iswupper_l, %function; -__iswupper_l: -.globl iswxdigit -.type iswxdigit, %function; -iswxdigit: -.weak iswxdigit_l -.type iswxdigit_l, %function; -iswxdigit_l: -.globl __iswxdigit_l -.type __iswxdigit_l, %function; -__iswxdigit_l: -.globl isxdigit -.type isxdigit, %function; -isxdigit: -.weak isxdigit_l -.type isxdigit_l, %function; -isxdigit_l: -.globl __isxdigit_l -.type __isxdigit_l, %function; -__isxdigit_l: -.globl toascii -.type toascii, %function; -toascii: -.globl tolower -.type tolower, %function; -tolower: -.globl __tolower_l -.type __tolower_l, %function; -__tolower_l: -.weak tolower_l -.type tolower_l, %function; -tolower_l: -.globl toupper -.type toupper, %function; -toupper: -.globl __toupper_l -.type __toupper_l, %function; -__toupper_l: -.weak toupper_l -.type toupper_l, %function; -toupper_l: -.globl towlower -.type towlower, %function; -towlower: -.globl towupper -.type towupper, %function; -towupper: -.weak towupper_l -.type towupper_l, %function; -towupper_l: -.globl __towupper_l -.type __towupper_l, %function; -__towupper_l: -.globl __towlower_l -.type __towlower_l, %function; -__towlower_l: -.weak towlower_l -.type towlower_l, %function; -towlower_l: -.globl wcswidth -.type wcswidth, %function; -wcswidth: -.globl wctrans -.type wctrans, %function; -wctrans: -.globl towctrans -.type towctrans, %function; -towctrans: -.globl __wctrans_l -.type __wctrans_l, %function; -__wctrans_l: -.weak wctrans_l -.type wctrans_l, %function; -wctrans_l: -.weak towctrans_l -.type towctrans_l, %function; -towctrans_l: -.globl __towctrans_l -.type __towctrans_l, %function; -__towctrans_l: -.globl wcwidth -.type wcwidth, %function; -wcwidth: -.globl alphasort -.type alphasort, %function; -alphasort: -.weak alphasort64 -.type alphasort64, %function; -alphasort64: -.globl closedir -.type closedir, %function; -closedir: +.globl ctermid +.type ctermid, %function; +ctermid: +.globl ctime +.type ctime, %function; +ctime: +.globl ctime_r +.type ctime_r, %function; +ctime_r: +.globl cuserid +.type cuserid, %function; +cuserid: +.globl daemon +.type daemon, %function; +daemon: +.globl dcgettext +.type dcgettext, %function; +dcgettext: +.globl dcngettext +.type dcngettext, %function; +dcngettext: +.globl delete_module +.type delete_module, %function; +delete_module: +.globl dgettext +.type dgettext, %function; +dgettext: +.globl difftime +.type difftime, %function; +difftime: .globl dirfd .type dirfd, %function; dirfd: -.globl fdopendir -.type fdopendir, %function; -fdopendir: -.globl opendir -.type opendir, %function; -opendir: -.globl readdir -.type readdir, %function; -readdir: -.weak readdir64 -.type readdir64, %function; -readdir64: -.weak readdir64_r -.type readdir64_r, %function; -readdir64_r: -.globl readdir_r -.type readdir_r, %function; -readdir_r: -.globl rewinddir -.type rewinddir, %function; -rewinddir: -.weak scandir64 -.type scandir64, %function; -scandir64: -.globl scandir -.type scandir, %function; -scandir: -.globl seekdir -.type seekdir, %function; -seekdir: -.globl telldir -.type telldir, %function; -telldir: -.weak versionsort64 -.type versionsort64, %function; -versionsort64: -.globl versionsort -.type versionsort, %function; -versionsort: -.weak _init -.type _init, %function; -_init: -.globl __libc_start_main -.type __libc_start_main, %function; -__libc_start_main: -.globl __stack_chk_fail -.type __stack_chk_fail, %function; -__stack_chk_fail: -.globl clearenv -.type clearenv, %function; -clearenv: -.globl getenv -.type getenv, %function; -getenv: -.globl putenv -.type putenv, %function; -putenv: -.globl secure_getenv -.type secure_getenv, %function; -secure_getenv: -.globl setenv -.type setenv, %function; -setenv: -.globl unsetenv -.type unsetenv, %function; -unsetenv: -.globl __errno_location -.type __errno_location, %function; -__errno_location: -.weak strerror_l -.type strerror_l, %function; -strerror_l: -.globl __strerror_l -.type __strerror_l, %function; -__strerror_l: -.globl strerror -.type strerror, %function; -strerror: -.globl _Exit -.type _Exit, %function; -_Exit: -.globl abort -.type abort, %function; -abort: -.globl __assert_fail -.type __assert_fail, %function; -__assert_fail: -.globl at_quick_exit -.type at_quick_exit, %function; -at_quick_exit: -.globl __cxa_finalize -.type __cxa_finalize, %function; -__cxa_finalize: -.globl __cxa_atexit -.type __cxa_atexit, %function; -__cxa_atexit: -.globl atexit -.type atexit, %function; -atexit: -.weak _fini -.type _fini, %function; -_fini: -.globl exit -.type exit, %function; -exit: -.globl quick_exit -.type quick_exit, %function; -quick_exit: -.weak creat64 -.type creat64, %function; -creat64: -.globl creat -.type creat, %function; -creat: -.globl fcntl -.type fcntl, %function; -fcntl: -.weak open64 -.type open64, %function; -open64: -.globl open -.type open, %function; -open: -.globl openat -.type openat, %function; -openat: -.weak openat64 -.type openat64, %function; -openat64: -.globl posix_fadvise -.type posix_fadvise, %function; -posix_fadvise: -.weak posix_fadvise64 -.type posix_fadvise64, %function; -posix_fadvise64: -.globl posix_fallocate -.type posix_fallocate, %function; -posix_fallocate: -.weak posix_fallocate64 -.type posix_fallocate64, %function; -posix_fallocate64: -.globl __flt_rounds -.type __flt_rounds, %function; -__flt_rounds: -.globl fegetexceptflag -.type fegetexceptflag, %function; -fegetexceptflag: -.globl feholdexcept -.type feholdexcept, %function; -feholdexcept: -.globl fesetexceptflag -.type fesetexceptflag, %function; -fesetexceptflag: -.globl fesetround -.type fesetround, %function; -fesetround: -.globl feupdateenv -.type feupdateenv, %function; -feupdateenv: -.globl feclearexcept -.type feclearexcept, %function; -feclearexcept: -.globl feraiseexcept -.type feraiseexcept, %function; -feraiseexcept: -.globl fetestexcept -.type fetestexcept, %function; -fetestexcept: -.globl fegetround -.type fegetround, %function; -fegetround: -.globl fegetenv -.type fegetenv, %function; -fegetenv: -.globl fesetenv -.type fesetenv, %function; -fesetenv: -.globl ftok -.type ftok, %function; -ftok: -.globl msgctl -.type msgctl, %function; -msgctl: -.globl msgget -.type msgget, %function; -msgget: -.globl msgrcv -.type msgrcv, %function; -msgrcv: -.globl msgsnd -.type msgsnd, %function; -msgsnd: -.globl semctl -.type semctl, %function; -semctl: -.globl semget -.type semget, %function; -semget: -.globl semop -.type semop, %function; -semop: -.globl semtimedop -.type semtimedop, %function; -semtimedop: -.globl shmat -.type shmat, %function; -shmat: -.globl shmctl -.type shmctl, %function; -shmctl: -.globl shmdt -.type shmdt, %function; -shmdt: -.globl shmget -.type shmget, %function; -shmget: +.globl dirname +.type dirname, %function; +dirname: +.globl div +.type div, %function; +div: +.globl dl_iterate_phdr +.type dl_iterate_phdr, %function; +dl_iterate_phdr: +.globl dladdr +.type dladdr, %function; +dladdr: .globl dlclose .type dlclose, %function; dlclose: @@ -852,172 +1492,90 @@ dlerror: .globl dlinfo .type dlinfo, %function; dlinfo: +.globl dlopen +.type dlopen, %function; +dlopen: .globl dlsym .type dlsym, %function; dlsym: -.globl cuserid -.type cuserid, %function; -cuserid: -.globl daemon -.type daemon, %function; -daemon: -.globl vwarn -.type vwarn, %function; -vwarn: -.globl vwarnx -.type vwarnx, %function; -vwarnx: -.globl verr -.type verr, %function; -verr: -.globl verrx -.type verrx, %function; -verrx: -.globl warn -.type warn, %function; -warn: -.globl warnx -.type warnx, %function; -warnx: -.globl err -.type err, %function; -err: -.globl errx -.type errx, %function; -errx: -.globl euidaccess -.type euidaccess, %function; -euidaccess: +.globl dn_comp +.type dn_comp, %function; +dn_comp: +.weak dn_expand +.type dn_expand, %function; +dn_expand: +.globl dn_skipname +.type dn_skipname, %function; +dn_skipname: +.globl dngettext +.type dngettext, %function; +dngettext: +.globl dprintf +.type dprintf, %function; +dprintf: +.globl drand48 +.type drand48, %function; +drand48: +.weak drem +.type drem, %function; +drem: +.weak dremf +.type dremf, %function; +dremf: +.globl dup +.type dup, %function; +dup: +.globl dup2 +.type dup2, %function; +dup2: +.weak dup3 +.type dup3, %function; +dup3: +.weak duplocale +.type duplocale, %function; +duplocale: .weak eaccess .type eaccess, %function; eaccess: -.globl ftw -.type ftw, %function; -ftw: -.weak ftw64 -.type ftw64, %function; -ftw64: -.globl futimes -.type futimes, %function; -futimes: -.globl getdtablesize -.type getdtablesize, %function; -getdtablesize: -.globl getloadavg -.type getloadavg, %function; -getloadavg: -.globl getpagesize -.type getpagesize, %function; -getpagesize: -.globl getpass -.type getpass, %function; -getpass: +.globl ecvt +.type ecvt, %function; +ecvt: +.globl encrypt +.type encrypt, %function; +encrypt: +.weak endgrent +.type endgrent, %function; +endgrent: +.globl endhostent +.type endhostent, %function; +endhostent: +.globl endmntent +.type endmntent, %function; +endmntent: +.weak endnetent +.type endnetent, %function; +endnetent: +.globl endprotoent +.type endprotoent, %function; +endprotoent: +.weak endpwent +.type endpwent, %function; +endpwent: +.globl endservent +.type endservent, %function; +endservent: +.globl endspent +.type endspent, %function; +endspent: .globl endusershell .type endusershell, %function; endusershell: -.globl setusershell -.type setusershell, %function; -setusershell: -.globl getusershell -.type getusershell, %function; -getusershell: -.globl isastream -.type isastream, %function; -isastream: -.globl lutimes -.type lutimes, %function; -lutimes: -.globl ulimit -.type ulimit, %function; -ulimit: -.globl endutxent -.type endutxent, %function; -endutxent: .weak endutent .type endutent, %function; endutent: -.globl setutxent -.type setutxent, %function; -setutxent: -.weak setutent -.type setutent, %function; -setutent: -.globl getutxent -.type getutxent, %function; -getutxent: -.weak getutent -.type getutent, %function; -getutent: -.globl getutxid -.type getutxid, %function; -getutxid: -.weak getutid -.type getutid, %function; -getutid: -.weak getutline -.type getutline, %function; -getutline: -.globl getutxline -.type getutxline, %function; -getutxline: -.globl pututxline -.type pututxline, %function; -pututxline: -.weak pututline -.type pututline, %function; -pututline: -.globl updwtmpx -.type updwtmpx, %function; -updwtmpx: -.weak updwtmp -.type updwtmp, %function; -updwtmp: -.weak utmpxname -.type utmpxname, %function; -utmpxname: -.weak utmpname -.type utmpname, %function; -utmpname: -.globl valloc -.type valloc, %function; -valloc: -.globl adjtime -.type adjtime, %function; -adjtime: -.globl adjtimex -.type adjtimex, %function; -adjtimex: -.globl brk -.type brk, %function; -brk: -#if !defined(ARCH_mips) && !defined(ARCH_i386) && !defined(ARCH_x86_64) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) -.weak riscv_flush_icache -.type riscv_flush_icache, %function; -riscv_flush_icache: -#endif -#if !defined(ARCH_mips) && !defined(ARCH_i386) && !defined(ARCH_x86_64) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) -.globl __riscv_flush_icache -.type __riscv_flush_icache, %function; -__riscv_flush_icache: -#endif -.globl capset -.type capset, %function; -capset: -.globl capget -.type capget, %function; -capget: -.globl chroot -.type chroot, %function; -chroot: -.globl clock_adjtime -.type clock_adjtime, %function; -clock_adjtime: -.globl clone -.type clone, %function; -clone: -.globl copy_file_range -.type copy_file_range, %function; -copy_file_range: +.globl endutxent +.type endutxent, %function; +endutxent: .globl epoll_create .type epoll_create, %function; epoll_create: @@ -1033,6 +1591,57 @@ epoll_pwait: .globl epoll_wait .type epoll_wait, %function; epoll_wait: +.globl erand48 +.type erand48, %function; +erand48: +.globl erf +.type erf, %function; +erf: +.globl erfc +.type erfc, %function; +erfc: +.globl erfcf +.type erfcf, %function; +erfcf: +.globl erfcl +.type erfcl, %function; +erfcl: +.globl erff +.type erff, %function; +erff: +.globl erfl +.type erfl, %function; +erfl: +.globl err +.type err, %function; +err: +.globl errx +.type errx, %function; +errx: +.globl ether_aton +.type ether_aton, %function; +ether_aton: +.globl ether_aton_r +.type ether_aton_r, %function; +ether_aton_r: +.globl ether_hostton +.type ether_hostton, %function; +ether_hostton: +.globl ether_line +.type ether_line, %function; +ether_line: +.globl ether_ntoa +.type ether_ntoa, %function; +ether_ntoa: +.globl ether_ntoa_r +.type ether_ntoa_r, %function; +ether_ntoa_r: +.globl ether_ntohost +.type ether_ntohost, %function; +ether_ntohost: +.globl euidaccess +.type euidaccess, %function; +euidaccess: .globl eventfd .type eventfd, %function; eventfd: @@ -1042,564 +1651,39 @@ eventfd_read: .globl eventfd_write .type eventfd_write, %function; eventfd_write: -.globl fallocate -.type fallocate, %function; -fallocate: -.weak fallocate64 -.type fallocate64, %function; -fallocate64: -.globl fanotify_init -.type fanotify_init, %function; -fanotify_init: -.globl fanotify_mark -.type fanotify_mark, %function; -fanotify_mark: -.globl flock -.type flock, %function; -flock: -.globl getdents -.type getdents, %function; -getdents: -.weak getdents64 -.type getdents64, %function; -getdents64: -.globl getrandom -.type getrandom, %function; -getrandom: -.globl gettid -.type gettid, %function; -gettid: -.globl inotify_init -.type inotify_init, %function; -inotify_init: -.globl inotify_init1 -.type inotify_init1, %function; -inotify_init1: -.globl inotify_add_watch -.type inotify_add_watch, %function; -inotify_add_watch: -.globl inotify_rm_watch -.type inotify_rm_watch, %function; -inotify_rm_watch: -.globl klogctl -.type klogctl, %function; -klogctl: -.weak membarrier -.type membarrier, %function; -membarrier: -.globl memfd_create -.type memfd_create, %function; -memfd_create: -.globl mlock2 -.type mlock2, %function; -mlock2: -.globl init_module -.type init_module, %function; -init_module: -.globl delete_module -.type delete_module, %function; -delete_module: -.globl mount -.type mount, %function; -mount: -.globl umount -.type umount, %function; -umount: -.globl umount2 -.type umount2, %function; -umount2: -.globl name_to_handle_at -.type name_to_handle_at, %function; -name_to_handle_at: -.globl open_by_handle_at -.type open_by_handle_at, %function; -open_by_handle_at: -.globl personality -.type personality, %function; -personality: -.globl pivot_root -.type pivot_root, %function; -pivot_root: -.globl ppoll -.type ppoll, %function; -ppoll: -.globl prctl -.type prctl, %function; -prctl: -.weak prlimit64 -.type prlimit64, %function; -prlimit64: -.globl prlimit -.type prlimit, %function; -prlimit: -.globl process_vm_writev -.type process_vm_writev, %function; -process_vm_writev: -.globl process_vm_readv -.type process_vm_readv, %function; -process_vm_readv: -.globl ptrace -.type ptrace, %function; -ptrace: -.globl quotactl -.type quotactl, %function; -quotactl: -.globl readahead -.type readahead, %function; -readahead: -.globl reboot -.type reboot, %function; -reboot: -.globl remap_file_pages -.type remap_file_pages, %function; -remap_file_pages: -.globl sbrk -.type sbrk, %function; -sbrk: -.weak sendfile64 -.type sendfile64, %function; -sendfile64: -.globl sendfile -.type sendfile, %function; -sendfile: -.globl setfsgid -.type setfsgid, %function; -setfsgid: -.globl setfsuid -.type setfsuid, %function; -setfsuid: -.globl setgroups -.type setgroups, %function; -setgroups: -.globl sethostname -.type sethostname, %function; -sethostname: -.globl setns -.type setns, %function; -setns: -.globl settimeofday -.type settimeofday, %function; -settimeofday: -.globl signalfd -.type signalfd, %function; -signalfd: -.globl splice -.type splice, %function; -splice: -.globl stime -.type stime, %function; -stime: -.globl swapon -.type swapon, %function; -swapon: -.globl swapoff -.type swapoff, %function; -swapoff: -.globl sync_file_range -.type sync_file_range, %function; -sync_file_range: -.globl syncfs -.type syncfs, %function; -syncfs: -.weak sysinfo -.type sysinfo, %function; -sysinfo: -.globl tee -.type tee, %function; -tee: -.globl timerfd_create -.type timerfd_create, %function; -timerfd_create: -.globl timerfd_settime -.type timerfd_settime, %function; -timerfd_settime: -.globl timerfd_gettime -.type timerfd_gettime, %function; -timerfd_gettime: -.globl unshare -.type unshare, %function; -unshare: -.globl utimes -.type utimes, %function; -utimes: -.globl vhangup -.type vhangup, %function; -vhangup: -.globl vmsplice -.type vmsplice, %function; -vmsplice: -.globl wait3 -.type wait3, %function; -wait3: -.globl wait4 -.type wait4, %function; -wait4: -.globl getxattr -.type getxattr, %function; -getxattr: -.globl lgetxattr -.type lgetxattr, %function; -lgetxattr: -.globl fgetxattr -.type fgetxattr, %function; -fgetxattr: -.globl listxattr -.type listxattr, %function; -listxattr: -.globl llistxattr -.type llistxattr, %function; -llistxattr: -.globl flistxattr -.type flistxattr, %function; -flistxattr: -.globl setxattr -.type setxattr, %function; -setxattr: -.globl lsetxattr -.type lsetxattr, %function; -lsetxattr: -.globl fsetxattr -.type fsetxattr, %function; -fsetxattr: -.globl removexattr -.type removexattr, %function; -removexattr: -.globl lremovexattr -.type lremovexattr, %function; -lremovexattr: -.globl fremovexattr -.type fremovexattr, %function; -fremovexattr: -.globl bind_textdomain_codeset -.type bind_textdomain_codeset, %function; -bind_textdomain_codeset: -.globl catclose -.type catclose, %function; -catclose: -.globl catgets -.type catgets, %function; -catgets: -.globl catopen -.type catopen, %function; -catopen: -.globl bindtextdomain -.type bindtextdomain, %function; -bindtextdomain: -.globl dcngettext -.type dcngettext, %function; -dcngettext: -.globl dcgettext -.type dcgettext, %function; -dcgettext: -.globl dngettext -.type dngettext, %function; -dngettext: -.globl dgettext -.type dgettext, %function; -dgettext: -.globl __duplocale -.type __duplocale, %function; -__duplocale: -.weak duplocale -.type duplocale, %function; -duplocale: -.globl freelocale -.type freelocale, %function; -freelocale: -.weak __freelocale -.type __freelocale, %function; -__freelocale: -.globl iconv_open -.type iconv_open, %function; -iconv_open: -.globl iconv -.type iconv, %function; -iconv: -.globl iconv_close -.type iconv_close, %function; -iconv_close: -.globl __nl_langinfo_l -.type __nl_langinfo_l, %function; -__nl_langinfo_l: -.weak nl_langinfo_l -.type nl_langinfo_l, %function; -nl_langinfo_l: -.weak nl_langinfo -.type nl_langinfo, %function; -nl_langinfo: -.globl __nl_langinfo -.type __nl_langinfo, %function; -__nl_langinfo: -.globl localeconv -.type localeconv, %function; -localeconv: -.weak newlocale -.type newlocale, %function; -newlocale: -.globl __newlocale -.type __newlocale, %function; -__newlocale: -.globl setlocale -.type setlocale, %function; -setlocale: -.globl __strcoll_l -.type __strcoll_l, %function; -__strcoll_l: -.weak strcoll_l -.type strcoll_l, %function; -strcoll_l: -.globl strcoll -.type strcoll, %function; -strcoll: -.globl strfmon_l -.type strfmon_l, %function; -strfmon_l: -.globl strfmon -.type strfmon, %function; -strfmon: -.weak __strtof_l -.type __strtof_l, %function; -__strtof_l: -.globl strtof_l -.type strtof_l, %function; -strtof_l: -.globl strtod_l -.type strtod_l, %function; -strtod_l: -.weak __strtod_l -.type __strtod_l, %function; -__strtod_l: -.globl strtold_l -.type strtold_l, %function; -strtold_l: -.weak __strtold_l -.type __strtold_l, %function; -__strtold_l: -.globl __strxfrm_l -.type __strxfrm_l, %function; -__strxfrm_l: -.weak strxfrm_l -.type strxfrm_l, %function; -strxfrm_l: -.globl strxfrm -.type strxfrm, %function; -strxfrm: -.globl textdomain -.type textdomain, %function; -textdomain: -.globl gettext -.type gettext, %function; -gettext: -.globl ngettext -.type ngettext, %function; -ngettext: -.globl __uselocale -.type __uselocale, %function; -__uselocale: -.weak uselocale -.type uselocale, %function; -uselocale: -.globl __wcscoll_l -.type __wcscoll_l, %function; -__wcscoll_l: -.weak wcscoll_l -.type wcscoll_l, %function; -wcscoll_l: -.globl wcscoll -.type wcscoll, %function; -wcscoll: -.weak wcsxfrm_l -.type wcsxfrm_l, %function; -wcsxfrm_l: -.globl __wcsxfrm_l -.type __wcsxfrm_l, %function; -__wcsxfrm_l: -.globl wcsxfrm -.type wcsxfrm, %function; -wcsxfrm: -.globl calloc -.type calloc, %function; -calloc: -.globl free -.type free, %function; -free: -.weak malloc -.type malloc, %function; -malloc: -.globl aligned_alloc -.type aligned_alloc, %function; -aligned_alloc: -.globl malloc_usable_size -.type malloc_usable_size, %function; -malloc_usable_size: -.globl memalign -.type memalign, %function; -memalign: -.globl posix_memalign -.type posix_memalign, %function; -posix_memalign: -.globl realloc -.type realloc, %function; -realloc: -.globl reallocarray -.type reallocarray, %function; -reallocarray: -.globl __fpclassify -.type __fpclassify, %function; -__fpclassify: -.globl __fpclassifyf -.type __fpclassifyf, %function; -__fpclassifyf: -.globl __fpclassifyl -.type __fpclassifyl, %function; -__fpclassifyl: -.globl __signbit -.type __signbit, %function; -__signbit: -.globl __signbitf -.type __signbitf, %function; -__signbitf: -.globl __signbitl -.type __signbitl, %function; -__signbitl: -.globl acos -.type acos, %function; -acos: -.globl acosf -.type acosf, %function; -acosf: -.globl acosh -.type acosh, %function; -acosh: -.globl acoshf -.type acoshf, %function; -acoshf: -.globl acoshl -.type acoshl, %function; -acoshl: -.globl acosl -.type acosl, %function; -acosl: -.globl asin -.type asin, %function; -asin: -.globl asinf -.type asinf, %function; -asinf: -.globl asinh -.type asinh, %function; -asinh: -.globl asinhf -.type asinhf, %function; -asinhf: -.globl asinhl -.type asinhl, %function; -asinhl: -.globl asinl -.type asinl, %function; -asinl: -.globl atan -.type atan, %function; -atan: -.globl atan2 -.type atan2, %function; -atan2: -.globl atan2f -.type atan2f, %function; -atan2f: -.globl atan2l -.type atan2l, %function; -atan2l: -.globl atanf -.type atanf, %function; -atanf: -.globl atanh -.type atanh, %function; -atanh: -.globl atanhf -.type atanhf, %function; -atanhf: -.globl atanhl -.type atanhl, %function; -atanhl: -.globl atanl -.type atanl, %function; -atanl: -.globl cbrt -.type cbrt, %function; -cbrt: -.globl cbrtf -.type cbrtf, %function; -cbrtf: -.globl cbrtl -.type cbrtl, %function; -cbrtl: -.globl ceil -.type ceil, %function; -ceil: -.globl ceilf -.type ceilf, %function; -ceilf: -.globl ceill -.type ceill, %function; -ceill: -.globl copysignl -.type copysignl, %function; -copysignl: -.globl cos -.type cos, %function; -cos: -.globl cosf -.type cosf, %function; -cosf: -.globl cosh -.type cosh, %function; -cosh: -.globl coshf -.type coshf, %function; -coshf: -.globl coshl -.type coshl, %function; -coshl: -.globl cosl -.type cosl, %function; -cosl: -.globl erf -.type erf, %function; -erf: -.globl erfc -.type erfc, %function; -erfc: -.globl erff -.type erff, %function; -erff: -.globl erfcf -.type erfcf, %function; -erfcf: -.globl erfl -.type erfl, %function; -erfl: -.globl erfcl -.type erfcl, %function; -erfcl: +.globl execl +.type execl, %function; +execl: +.globl execle +.type execle, %function; +execle: +.globl execlp +.type execlp, %function; +execlp: +.globl execv +.type execv, %function; +execv: +.globl execve +.type execve, %function; +execve: +.globl execvp +.type execvp, %function; +execvp: +.weak execvpe +.type execvpe, %function; +execvpe: +.globl exit +.type exit, %function; +exit: .globl exp .type exp, %function; exp: .globl exp10 .type exp10, %function; exp10: -.weak pow10 -.type pow10, %function; -pow10: -.weak pow10f -.type pow10f, %function; -pow10f: .globl exp10f .type exp10f, %function; exp10f: -.weak pow10l -.type pow10l, %function; -pow10l: .globl exp10l .type exp10l, %function; exp10l: @@ -1618,6 +1702,9 @@ expf: .globl expl .type expl, %function; expl: +.globl explicit_bzero +.type explicit_bzero, %function; +explicit_bzero: .globl expm1 .type expm1, %function; expm1: @@ -1627,9 +1714,57 @@ expm1f: .globl expm1l .type expm1l, %function; expm1l: +.globl fabs +.type fabs, %function; +fabs: +.globl fabsf +.type fabsf, %function; +fabsf: .globl fabsl .type fabsl, %function; fabsl: +.globl faccessat +.type faccessat, %function; +faccessat: +.globl fallocate +.type fallocate, %function; +fallocate: +.weak fallocate64 +.type fallocate64, %function; +fallocate64: +.globl fanotify_init +.type fanotify_init, %function; +fanotify_init: +.globl fanotify_mark +.type fanotify_mark, %function; +fanotify_mark: +.globl fchdir +.type fchdir, %function; +fchdir: +.globl fchmod +.type fchmod, %function; +fchmod: +.globl fchmodat +.type fchmodat, %function; +fchmodat: +.globl fchown +.type fchown, %function; +fchown: +.globl fchownat +.type fchownat, %function; +fchownat: +.globl fclose +.type fclose, %function; +fclose: +.globl fcntl +.type fcntl, %function; +fcntl: +.globl fcvt +.type fcvt, %function; +fcvt: +.globl fdatasync +.type fdatasync, %function; +fdatasync: .globl fdim .type fdim, %function; fdim: @@ -1639,12 +1774,141 @@ fdimf: .globl fdiml .type fdiml, %function; fdiml: +.weak fdopen +.type fdopen, %function; +fdopen: +.globl fdopendir +.type fdopendir, %function; +fdopendir: +.globl feclearexcept +.type feclearexcept, %function; +feclearexcept: +.globl fegetenv +.type fegetenv, %function; +fegetenv: +.globl fegetexceptflag +.type fegetexceptflag, %function; +fegetexceptflag: +.globl fegetround +.type fegetround, %function; +fegetround: +.globl feholdexcept +.type feholdexcept, %function; +feholdexcept: +.globl feof +.type feof, %function; +feof: +.weak feof_unlocked +.type feof_unlocked, %function; +feof_unlocked: +.globl feraiseexcept +.type feraiseexcept, %function; +feraiseexcept: +.globl ferror +.type ferror, %function; +ferror: +.weak ferror_unlocked +.type ferror_unlocked, %function; +ferror_unlocked: +.globl fesetenv +.type fesetenv, %function; +fesetenv: +.globl fesetexceptflag +.type fesetexceptflag, %function; +fesetexceptflag: +.globl fesetround +.type fesetround, %function; +fesetround: +.globl fetestexcept +.type fetestexcept, %function; +fetestexcept: +.globl feupdateenv +.type feupdateenv, %function; +feupdateenv: +.globl fexecve +.type fexecve, %function; +fexecve: +.globl fflush +.type fflush, %function; +fflush: +.weak fflush_unlocked +.type fflush_unlocked, %function; +fflush_unlocked: +.globl ffs +.type ffs, %function; +ffs: +.globl ffsl +.type ffsl, %function; +ffsl: +.globl ffsll +.type ffsll, %function; +ffsll: +.globl fgetc +.type fgetc, %function; +fgetc: +.weak fgetc_unlocked +.type fgetc_unlocked, %function; +fgetc_unlocked: +.globl fgetgrent +.type fgetgrent, %function; +fgetgrent: +.globl fgetln +.type fgetln, %function; +fgetln: +.globl fgetpos +.type fgetpos, %function; +fgetpos: +.weak fgetpos64 +.type fgetpos64, %function; +fgetpos64: +.globl fgetpwent +.type fgetpwent, %function; +fgetpwent: +.globl fgets +.type fgets, %function; +fgets: +.weak fgets_unlocked +.type fgets_unlocked, %function; +fgets_unlocked: +.globl fgetspent +.type fgetspent, %function; +fgetspent: +.globl fgetwc +.type fgetwc, %function; +fgetwc: +.weak fgetwc_unlocked +.type fgetwc_unlocked, %function; +fgetwc_unlocked: +.globl fgetws +.type fgetws, %function; +fgetws: +.weak fgetws_unlocked +.type fgetws_unlocked, %function; +fgetws_unlocked: +.globl fgetxattr +.type fgetxattr, %function; +fgetxattr: +.globl fileno +.type fileno, %function; +fileno: +.weak fileno_unlocked +.type fileno_unlocked, %function; +fileno_unlocked: .globl finite .type finite, %function; finite: .globl finitef .type finitef, %function; finitef: +.globl flistxattr +.type flistxattr, %function; +flistxattr: +.globl flock +.type flock, %function; +flock: +.globl flockfile +.type flockfile, %function; +flockfile: .globl floor .type floor, %function; floor: @@ -1654,12 +1918,33 @@ floorf: .globl floorl .type floorl, %function; floorl: +.globl fma +.type fma, %function; +fma: +.globl fmaf +.type fmaf, %function; +fmaf: .globl fmal .type fmal, %function; fmal: +.globl fmax +.type fmax, %function; +fmax: +.globl fmaxf +.type fmaxf, %function; +fmaxf: .globl fmaxl .type fmaxl, %function; fmaxl: +.globl fmemopen +.type fmemopen, %function; +fmemopen: +.globl fmin +.type fmin, %function; +fmin: +.globl fminf +.type fminf, %function; +fminf: .globl fminl .type fminl, %function; fminl: @@ -1672,6 +1957,87 @@ fmodf: .globl fmodl .type fmodl, %function; fmodl: +.globl fmtmsg +.type fmtmsg, %function; +fmtmsg: +.globl fnmatch +.type fnmatch, %function; +fnmatch: +.globl fopen +.type fopen, %function; +fopen: +.weak fopen64 +.type fopen64, %function; +fopen64: +.globl fopencookie +.type fopencookie, %function; +fopencookie: +.globl fork +.type fork, %function; +fork: +.globl forkpty +.type forkpty, %function; +forkpty: +.globl fpathconf +.type fpathconf, %function; +fpathconf: +.globl fprintf +.type fprintf, %function; +fprintf: +.weak fpurge +.type fpurge, %function; +fpurge: +.globl fputc +.type fputc, %function; +fputc: +.weak fputc_unlocked +.type fputc_unlocked, %function; +fputc_unlocked: +.globl fputs +.type fputs, %function; +fputs: +.weak fputs_unlocked +.type fputs_unlocked, %function; +fputs_unlocked: +.globl fputwc +.type fputwc, %function; +fputwc: +.weak fputwc_unlocked +.type fputwc_unlocked, %function; +fputwc_unlocked: +.globl fputws +.type fputws, %function; +fputws: +.weak fputws_unlocked +.type fputws_unlocked, %function; +fputws_unlocked: +.globl fread +.type fread, %function; +fread: +.weak fread_unlocked +.type fread_unlocked, %function; +fread_unlocked: +.globl free +.type free, %function; +free: +.globl freeaddrinfo +.type freeaddrinfo, %function; +freeaddrinfo: +.globl freeifaddrs +.type freeifaddrs, %function; +freeifaddrs: +.globl freelocale +.type freelocale, %function; +freelocale: +.globl fremovexattr +.type fremovexattr, %function; +fremovexattr: +.globl freopen +.type freopen, %function; +freopen: +.weak freopen64 +.type freopen64, %function; +freopen64: .globl frexp .type frexp, %function; frexp: @@ -1681,6 +2047,489 @@ frexpf: .globl frexpl .type frexpl, %function; frexpl: +.globl fscanf +.type fscanf, %function; +fscanf: +.globl fseek +.type fseek, %function; +fseek: +.weak fseeko +.type fseeko, %function; +fseeko: +.weak fseeko64 +.type fseeko64, %function; +fseeko64: +.globl fsetpos +.type fsetpos, %function; +fsetpos: +.weak fsetpos64 +.type fsetpos64, %function; +fsetpos64: +.globl fsetxattr +.type fsetxattr, %function; +fsetxattr: +.globl fstat +.type fstat, %function; +fstat: +.weak fstat64 +.type fstat64, %function; +fstat64: +.globl fstatat +.type fstatat, %function; +fstatat: +.weak fstatat64 +.type fstatat64, %function; +fstatat64: +.weak fstatfs +.type fstatfs, %function; +fstatfs: +.weak fstatfs64 +.type fstatfs64, %function; +fstatfs64: +.globl fstatvfs +.type fstatvfs, %function; +fstatvfs: +.weak fstatvfs64 +.type fstatvfs64, %function; +fstatvfs64: +.globl fsync +.type fsync, %function; +fsync: +.globl ftell +.type ftell, %function; +ftell: +.weak ftello +.type ftello, %function; +ftello: +.weak ftello64 +.type ftello64, %function; +ftello64: +.globl ftime +.type ftime, %function; +ftime: +.globl ftok +.type ftok, %function; +ftok: +.globl ftruncate +.type ftruncate, %function; +ftruncate: +.weak ftruncate64 +.type ftruncate64, %function; +ftruncate64: +.globl ftrylockfile +.type ftrylockfile, %function; +ftrylockfile: +.globl ftw +.type ftw, %function; +ftw: +.weak ftw64 +.type ftw64, %function; +ftw64: +.globl funlockfile +.type funlockfile, %function; +funlockfile: +.globl futimens +.type futimens, %function; +futimens: +.globl futimes +.type futimes, %function; +futimes: +WEAK64 futimesat +.type futimesat, %function; +futimesat: +.globl fwide +.type fwide, %function; +fwide: +.globl fwprintf +.type fwprintf, %function; +fwprintf: +.globl fwrite +.type fwrite, %function; +fwrite: +.weak fwrite_unlocked +.type fwrite_unlocked, %function; +fwrite_unlocked: +.globl fwscanf +.type fwscanf, %function; +fwscanf: +.globl gai_strerror +.type gai_strerror, %function; +gai_strerror: +.globl gcvt +.type gcvt, %function; +gcvt: +.globl get_avphys_pages +.type get_avphys_pages, %function; +get_avphys_pages: +.globl get_current_dir_name +.type get_current_dir_name, %function; +get_current_dir_name: +.globl get_nprocs +.type get_nprocs, %function; +get_nprocs: +.globl get_nprocs_conf +.type get_nprocs_conf, %function; +get_nprocs_conf: +.globl get_phys_pages +.type get_phys_pages, %function; +get_phys_pages: +.globl getaddrinfo +.type getaddrinfo, %function; +getaddrinfo: +.weak getauxval +.type getauxval, %function; +getauxval: +.globl getc +.type getc, %function; +getc: +.globl getc_unlocked +.type getc_unlocked, %function; +getc_unlocked: +.globl getchar +.type getchar, %function; +getchar: +.globl getchar_unlocked +.type getchar_unlocked, %function; +getchar_unlocked: +.globl getcwd +.type getcwd, %function; +getcwd: +.globl getdate +.type getdate, %function; +getdate: +.globl getdelim +.type getdelim, %function; +getdelim: +.globl getdents +.type getdents, %function; +getdents: +.weak getdents64 +.type getdents64, %function; +getdents64: +.globl getdomainname +.type getdomainname, %function; +getdomainname: +.globl getdtablesize +.type getdtablesize, %function; +getdtablesize: +.globl getegid +.type getegid, %function; +getegid: +.globl getentropy +.type getentropy, %function; +getentropy: +.globl getenv +.type getenv, %function; +getenv: +.globl geteuid +.type geteuid, %function; +geteuid: +.globl getgid +.type getgid, %function; +getgid: +.globl getgrent +.type getgrent, %function; +getgrent: +.globl getgrgid +.type getgrgid, %function; +getgrgid: +.globl getgrgid_r +.type getgrgid_r, %function; +getgrgid_r: +.globl getgrnam +.type getgrnam, %function; +getgrnam: +.globl getgrnam_r +.type getgrnam_r, %function; +getgrnam_r: +.globl getgrouplist +.type getgrouplist, %function; +getgrouplist: +.globl getgroups +.type getgroups, %function; +getgroups: +.globl gethostbyaddr +.type gethostbyaddr, %function; +gethostbyaddr: +.globl gethostbyaddr_r +.type gethostbyaddr_r, %function; +gethostbyaddr_r: +.globl gethostbyname +.type gethostbyname, %function; +gethostbyname: +.globl gethostbyname2 +.type gethostbyname2, %function; +gethostbyname2: +.globl gethostbyname2_r +.type gethostbyname2_r, %function; +gethostbyname2_r: +.globl gethostbyname_r +.type gethostbyname_r, %function; +gethostbyname_r: +.globl gethostent +.type gethostent, %function; +gethostent: +.globl gethostid +.type gethostid, %function; +gethostid: +.globl gethostname +.type gethostname, %function; +gethostname: +.globl getifaddrs +.type getifaddrs, %function; +getifaddrs: +.globl getitimer +.type getitimer, %function; +getitimer: +.globl getline +.type getline, %function; +getline: +.globl getloadavg +.type getloadavg, %function; +getloadavg: +.globl getlogin +.type getlogin, %function; +getlogin: +.globl getlogin_r +.type getlogin_r, %function; +getlogin_r: +.globl getmntent +.type getmntent, %function; +getmntent: +.globl getmntent_r +.type getmntent_r, %function; +getmntent_r: +.globl getnameinfo +.type getnameinfo, %function; +getnameinfo: +.globl getnetbyaddr +.type getnetbyaddr, %function; +getnetbyaddr: +.globl getnetbyname +.type getnetbyname, %function; +getnetbyname: +.globl getnetent +.type getnetent, %function; +getnetent: +.globl getopt +.type getopt, %function; +getopt: +.globl getopt_long +.type getopt_long, %function; +getopt_long: +.globl getopt_long_only +.type getopt_long_only, %function; +getopt_long_only: +.globl getpagesize +.type getpagesize, %function; +getpagesize: +.globl getpass +.type getpass, %function; +getpass: +.globl getpeername +.type getpeername, %function; +getpeername: +.globl getpgid +.type getpgid, %function; +getpgid: +.globl getpgrp +.type getpgrp, %function; +getpgrp: +.globl getpid +.type getpid, %function; +getpid: +.globl getppid +.type getppid, %function; +getppid: +.globl getpriority +.type getpriority, %function; +getpriority: +.globl getprotobyname +.type getprotobyname, %function; +getprotobyname: +.globl getprotobynumber +.type getprotobynumber, %function; +getprotobynumber: +.globl getprotoent +.type getprotoent, %function; +getprotoent: +.globl getpwent +.type getpwent, %function; +getpwent: +.globl getpwnam +.type getpwnam, %function; +getpwnam: +.globl getpwnam_r +.type getpwnam_r, %function; +getpwnam_r: +.globl getpwuid +.type getpwuid, %function; +getpwuid: +.globl getpwuid_r +.type getpwuid_r, %function; +getpwuid_r: +.globl getrandom +.type getrandom, %function; +getrandom: +.globl getresgid +.type getresgid, %function; +getresgid: +.globl getresuid +.type getresuid, %function; +getresuid: +.globl getrlimit +.type getrlimit, %function; +getrlimit: +.weak getrlimit64 +.type getrlimit64, %function; +getrlimit64: +.globl getrusage +.type getrusage, %function; +getrusage: +.globl gets +.type gets, %function; +gets: +.globl getservbyname +.type getservbyname, %function; +getservbyname: +.globl getservbyname_r +.type getservbyname_r, %function; +getservbyname_r: +.globl getservbyport +.type getservbyport, %function; +getservbyport: +.globl getservbyport_r +.type getservbyport_r, %function; +getservbyport_r: +.globl getservent +.type getservent, %function; +getservent: +.globl getsid +.type getsid, %function; +getsid: +.globl getsockname +.type getsockname, %function; +getsockname: +.globl getsockopt +.type getsockopt, %function; +getsockopt: +.globl getspent +.type getspent, %function; +getspent: +.globl getspnam +.type getspnam, %function; +getspnam: +.globl getspnam_r +.type getspnam_r, %function; +getspnam_r: +.globl getsubopt +.type getsubopt, %function; +getsubopt: +.globl gettext +.type gettext, %function; +gettext: +.globl gettid +.type gettid, %function; +gettid: +.globl gettimeofday +.type gettimeofday, %function; +gettimeofday: +.globl getuid +.type getuid, %function; +getuid: +.globl getusershell +.type getusershell, %function; +getusershell: +.weak getutent +.type getutent, %function; +getutent: +.weak getutid +.type getutid, %function; +getutid: +.weak getutline +.type getutline, %function; +getutline: +.globl getutxent +.type getutxent, %function; +getutxent: +.globl getutxid +.type getutxid, %function; +getutxid: +.globl getutxline +.type getutxline, %function; +getutxline: +.globl getw +.type getw, %function; +getw: +.globl getwc +.type getwc, %function; +getwc: +.weak getwc_unlocked +.type getwc_unlocked, %function; +getwc_unlocked: +.globl getwchar +.type getwchar, %function; +getwchar: +.weak getwchar_unlocked +.type getwchar_unlocked, %function; +getwchar_unlocked: +.globl getxattr +.type getxattr, %function; +getxattr: +.globl glob +.type glob, %function; +glob: +.weak glob64 +.type glob64, %function; +glob64: +.globl globfree +.type globfree, %function; +globfree: +.weak globfree64 +.type globfree64, %function; +globfree64: +.globl gmtime +.type gmtime, %function; +gmtime: +WEAK64 gmtime_r +.type gmtime_r, %function; +gmtime_r: +.globl grantpt +.type grantpt, %function; +grantpt: +.globl hasmntopt +.type hasmntopt, %function; +hasmntopt: +.globl hcreate +.type hcreate, %function; +hcreate: +.weak hcreate_r +.type hcreate_r, %function; +hcreate_r: +.globl hdestroy +.type hdestroy, %function; +hdestroy: +.weak hdestroy_r +.type hdestroy_r, %function; +hdestroy_r: +.globl herror +.type herror, %function; +herror: +.globl hsearch +.type hsearch, %function; +hsearch: +.weak hsearch_r +.type hsearch_r, %function; +hsearch_r: +.globl hstrerror +.type hstrerror, %function; +hstrerror: +.globl htonl +.type htonl, %function; +htonl: +.globl htons +.type htons, %function; +htons: .globl hypot .type hypot, %function; hypot: @@ -1690,6 +2539,27 @@ hypotf: .globl hypotl .type hypotl, %function; hypotl: +.globl iconv +.type iconv, %function; +iconv: +.globl iconv_close +.type iconv_close, %function; +iconv_close: +.globl iconv_open +.type iconv_open, %function; +iconv_open: +.globl if_freenameindex +.type if_freenameindex, %function; +if_freenameindex: +.globl if_indextoname +.type if_indextoname, %function; +if_indextoname: +.globl if_nameindex +.type if_nameindex, %function; +if_nameindex: +.globl if_nametoindex +.type if_nametoindex, %function; +if_nametoindex: .globl ilogb .type ilogb, %function; ilogb: @@ -1699,42 +2569,289 @@ ilogbf: .globl ilogbl .type ilogbl, %function; ilogbl: +.globl imaxabs +.type imaxabs, %function; +imaxabs: +.globl imaxdiv +.type imaxdiv, %function; +imaxdiv: +.globl index +.type index, %function; +index: +.globl inet_addr +.type inet_addr, %function; +inet_addr: +.weak inet_aton +.type inet_aton, %function; +inet_aton: +.globl inet_lnaof +.type inet_lnaof, %function; +inet_lnaof: +.globl inet_makeaddr +.type inet_makeaddr, %function; +inet_makeaddr: +.globl inet_netof +.type inet_netof, %function; +inet_netof: +.globl inet_network +.type inet_network, %function; +inet_network: +.globl inet_ntoa +.type inet_ntoa, %function; +inet_ntoa: +.globl inet_ntop +.type inet_ntop, %function; +inet_ntop: +.globl inet_pton +.type inet_pton, %function; +inet_pton: +.globl init_module +.type init_module, %function; +init_module: +.globl initgroups +.type initgroups, %function; +initgroups: +.globl initstate +.type initstate, %function; +initstate: +.globl inotify_add_watch +.type inotify_add_watch, %function; +inotify_add_watch: +.globl inotify_init +.type inotify_init, %function; +inotify_init: +.globl inotify_init1 +.type inotify_init1, %function; +inotify_init1: +.globl inotify_rm_watch +.type inotify_rm_watch, %function; +inotify_rm_watch: +.globl insque +.type insque, %function; +insque: +.globl ioctl +.type ioctl, %function; +ioctl: +#if !defined(ARCH_riscv64) && !defined(ARCH_aarch64) +.globl ioperm +.type ioperm, %function; +ioperm: +#endif +#if !defined(ARCH_riscv64) && !defined(ARCH_aarch64) +.globl iopl +.type iopl, %function; +iopl: +#endif +.globl isalnum +.type isalnum, %function; +isalnum: +.weak isalnum_l +.type isalnum_l, %function; +isalnum_l: +.globl isalpha +.type isalpha, %function; +isalpha: +.weak isalpha_l +.type isalpha_l, %function; +isalpha_l: +.globl isascii +.type isascii, %function; +isascii: +.globl isastream +.type isastream, %function; +isastream: +.globl isatty +.type isatty, %function; +isatty: +.globl isblank +.type isblank, %function; +isblank: +.weak isblank_l +.type isblank_l, %function; +isblank_l: +.globl iscntrl +.type iscntrl, %function; +iscntrl: +.weak iscntrl_l +.type iscntrl_l, %function; +iscntrl_l: +.globl isdigit +.type isdigit, %function; +isdigit: +.weak isdigit_l +.type isdigit_l, %function; +isdigit_l: +.globl isgraph +.type isgraph, %function; +isgraph: +.weak isgraph_l +.type isgraph_l, %function; +isgraph_l: +.globl islower +.type islower, %function; +islower: +.weak islower_l +.type islower_l, %function; +islower_l: +.globl isprint +.type isprint, %function; +isprint: +.weak isprint_l +.type isprint_l, %function; +isprint_l: +.globl ispunct +.type ispunct, %function; +ispunct: +.weak ispunct_l +.type ispunct_l, %function; +ispunct_l: +.globl issetugid +.type issetugid, %function; +issetugid: +.globl isspace +.type isspace, %function; +isspace: +.weak isspace_l +.type isspace_l, %function; +isspace_l: +.globl isupper +.type isupper, %function; +isupper: +.weak isupper_l +.type isupper_l, %function; +isupper_l: +.globl iswalnum +.type iswalnum, %function; +iswalnum: +.weak iswalnum_l +.type iswalnum_l, %function; +iswalnum_l: +.globl iswalpha +.type iswalpha, %function; +iswalpha: +.weak iswalpha_l +.type iswalpha_l, %function; +iswalpha_l: +.globl iswblank +.type iswblank, %function; +iswblank: +.weak iswblank_l +.type iswblank_l, %function; +iswblank_l: +.globl iswcntrl +.type iswcntrl, %function; +iswcntrl: +.weak iswcntrl_l +.type iswcntrl_l, %function; +iswcntrl_l: +.globl iswctype +.type iswctype, %function; +iswctype: +.weak iswctype_l +.type iswctype_l, %function; +iswctype_l: +.globl iswdigit +.type iswdigit, %function; +iswdigit: +.weak iswdigit_l +.type iswdigit_l, %function; +iswdigit_l: +.globl iswgraph +.type iswgraph, %function; +iswgraph: +.weak iswgraph_l +.type iswgraph_l, %function; +iswgraph_l: +.globl iswlower +.type iswlower, %function; +iswlower: +.weak iswlower_l +.type iswlower_l, %function; +iswlower_l: +.globl iswprint +.type iswprint, %function; +iswprint: +.weak iswprint_l +.type iswprint_l, %function; +iswprint_l: +.globl iswpunct +.type iswpunct, %function; +iswpunct: +.weak iswpunct_l +.type iswpunct_l, %function; +iswpunct_l: +.globl iswspace +.type iswspace, %function; +iswspace: +.weak iswspace_l +.type iswspace_l, %function; +iswspace_l: +.globl iswupper +.type iswupper, %function; +iswupper: +.weak iswupper_l +.type iswupper_l, %function; +iswupper_l: +.globl iswxdigit +.type iswxdigit, %function; +iswxdigit: +.weak iswxdigit_l +.type iswxdigit_l, %function; +iswxdigit_l: +.globl isxdigit +.type isxdigit, %function; +isxdigit: +.weak isxdigit_l +.type isxdigit_l, %function; +isxdigit_l: .globl j0 .type j0, %function; j0: -.globl y0 -.type y0, %function; -y0: .globl j0f .type j0f, %function; j0f: -.globl y0f -.type y0f, %function; -y0f: .globl j1 .type j1, %function; j1: -.globl y1 -.type y1, %function; -y1: .globl j1f .type j1f, %function; j1f: -.globl y1f -.type y1f, %function; -y1f: .globl jn .type jn, %function; jn: -.globl yn -.type yn, %function; -yn: .globl jnf .type jnf, %function; jnf: -.globl ynf -.type ynf, %function; -ynf: +.globl jrand48 +.type jrand48, %function; +jrand48: +.globl kill +.type kill, %function; +kill: +.globl killpg +.type killpg, %function; +killpg: +.globl klogctl +.type klogctl, %function; +klogctl: +.globl l64a +.type l64a, %function; +l64a: +.globl labs +.type labs, %function; +labs: +.globl lchmod +.type lchmod, %function; +lchmod: +.globl lchown +.type lchown, %function; +lchown: +.globl lckpwdf +.type lckpwdf, %function; +lckpwdf: +.globl lcong48 +.type lcong48, %function; +lcong48: .globl ldexp .type ldexp, %function; ldexp: @@ -1744,6 +2861,12 @@ ldexpf: .globl ldexpl .type ldexpl, %function; ldexpl: +.globl ldiv +.type ldiv, %function; +ldiv: +.globl lfind +.type lfind, %function; +lfind: .globl lgamma .type lgamma, %function; lgamma: @@ -1756,15 +2879,42 @@ lgammaf: .weak lgammaf_r .type lgammaf_r, %function; lgammaf_r: -.globl __lgammal_r -.type __lgammal_r, %function; -__lgammal_r: -.weak lgammal_r -.type lgammal_r, %function; -lgammal_r: .globl lgammal .type lgammal, %function; lgammal: +.weak lgammal_r +.type lgammal_r, %function; +lgammal_r: +.globl lgetxattr +.type lgetxattr, %function; +lgetxattr: +.globl link +.type link, %function; +link: +.globl linkat +.type linkat, %function; +linkat: +.globl lio_listio +.type lio_listio, %function; +lio_listio: +.weak lio_listio64 +.type lio_listio64, %function; +lio_listio64: +.globl listen +.type listen, %function; +listen: +.globl listxattr +.type listxattr, %function; +listxattr: +.globl llabs +.type llabs, %function; +llabs: +.globl lldiv +.type lldiv, %function; +lldiv: +.globl llistxattr +.type llistxattr, %function; +llistxattr: .globl llrint .type llrint, %function; llrint: @@ -1783,6 +2933,21 @@ llroundf: .globl llroundl .type llroundl, %function; llroundl: +.globl localeconv +.type localeconv, %function; +localeconv: +.globl localtime +.type localtime, %function; +localtime: +WEAK64 localtime_r +.type localtime_r, %function; +localtime_r: +.globl lockf +.type lockf, %function; +lockf: +.weak lockf64 +.type lockf64, %function; +lockf64: .globl log .type log, %function; log: @@ -1825,9 +2990,21 @@ logbl: .globl logf .type logf, %function; logf: +.globl login_tty +.type login_tty, %function; +login_tty: .globl logl .type logl, %function; logl: +.globl longjmp +.type longjmp, %function; +longjmp: +.globl lrand48 +.type lrand48, %function; +lrand48: +.globl lremovexattr +.type lremovexattr, %function; +lremovexattr: .globl lrint .type lrint, %function; lrint: @@ -1846,498 +3023,36 @@ lroundf: .globl lroundl .type lroundl, %function; lroundl: -.globl modf -.type modf, %function; -modf: -.globl modff -.type modff, %function; -modff: -.globl modfl -.type modfl, %function; -modfl: -.globl nan -.type nan, %function; -nan: -.globl nanf -.type nanf, %function; -nanf: -.globl nanl -.type nanl, %function; -nanl: -.globl nearbyint -.type nearbyint, %function; -nearbyint: -.globl nearbyintf -.type nearbyintf, %function; -nearbyintf: -.globl nearbyintl -.type nearbyintl, %function; -nearbyintl: -.globl nextafter -.type nextafter, %function; -nextafter: -.globl nextafterf -.type nextafterf, %function; -nextafterf: -.globl nextafterl -.type nextafterl, %function; -nextafterl: -.globl nexttoward -.type nexttoward, %function; -nexttoward: -.globl nexttowardf -.type nexttowardf, %function; -nexttowardf: -.globl nexttowardl -.type nexttowardl, %function; -nexttowardl: -.globl pow -.type pow, %function; -pow: -.globl powf -.type powf, %function; -powf: -.globl powl -.type powl, %function; -powl: -.globl remainder -.type remainder, %function; -remainder: -.weak drem -.type drem, %function; -drem: -.globl remainderf -.type remainderf, %function; -remainderf: -.weak dremf -.type dremf, %function; -dremf: -.globl remainderl -.type remainderl, %function; -remainderl: -.globl remquo -.type remquo, %function; -remquo: -.globl remquof -.type remquof, %function; -remquof: -.globl remquol -.type remquol, %function; -remquol: -.globl rint -.type rint, %function; -rint: -.globl rintf -.type rintf, %function; -rintf: -.globl rintl -.type rintl, %function; -rintl: -.globl copysign -.type copysign, %function; -copysign: -.globl copysignf -.type copysignf, %function; -copysignf: -.globl fabs -.type fabs, %function; -fabs: -.globl fabsf -.type fabsf, %function; -fabsf: -.globl fma -.type fma, %function; -fma: -.globl fmaf -.type fmaf, %function; -fmaf: -.globl fmax -.type fmax, %function; -fmax: -.globl fmaxf -.type fmaxf, %function; -fmaxf: -.globl fmin -.type fmin, %function; -fmin: -.globl fminf -.type fminf, %function; -fminf: -.globl sqrt -.type sqrt, %function; -sqrt: -.globl sqrtf -.type sqrtf, %function; -sqrtf: -.globl round -.type round, %function; -round: -.globl roundf -.type roundf, %function; -roundf: -.globl roundl -.type roundl, %function; -roundl: -.globl scalb -.type scalb, %function; -scalb: -.globl scalbf -.type scalbf, %function; -scalbf: -.globl scalbln -.type scalbln, %function; -scalbln: -.globl scalblnf -.type scalblnf, %function; -scalblnf: -.globl scalblnl -.type scalblnl, %function; -scalblnl: -.globl scalbn -.type scalbn, %function; -scalbn: -.globl scalbnf -.type scalbnf, %function; -scalbnf: -.globl scalbnl -.type scalbnl, %function; -scalbnl: -.globl significand -.type significand, %function; -significand: -.globl significandf -.type significandf, %function; -significandf: -.globl sin -.type sin, %function; -sin: -.globl sincos -.type sincos, %function; -sincos: -.globl sincosf -.type sincosf, %function; -sincosf: -.globl sincosl -.type sincosl, %function; -sincosl: -.globl sinf -.type sinf, %function; -sinf: -.globl sinh -.type sinh, %function; -sinh: -.globl sinhf -.type sinhf, %function; -sinhf: -.globl sinhl -.type sinhl, %function; -sinhl: -.globl sinl -.type sinl, %function; -sinl: -.globl sqrtl -.type sqrtl, %function; -sqrtl: -.globl tan -.type tan, %function; -tan: -.globl tanf -.type tanf, %function; -tanf: -.globl tanh -.type tanh, %function; -tanh: -.globl tanhf -.type tanhf, %function; -tanhf: -.globl tanhl -.type tanhl, %function; -tanhl: -.globl tanl -.type tanl, %function; -tanl: -.globl tgamma -.type tgamma, %function; -tgamma: -.globl tgammaf -.type tgammaf, %function; -tgammaf: -.globl tgammal -.type tgammal, %function; -tgammal: -.globl trunc -.type trunc, %function; -trunc: -.globl truncf -.type truncf, %function; -truncf: -.globl truncl -.type truncl, %function; -truncl: -.globl a64l -.type a64l, %function; -a64l: -.globl l64a -.type l64a, %function; -l64a: -.weak __xpg_basename -.type __xpg_basename, %function; -__xpg_basename: -.globl basename -.type basename, %function; -basename: -.globl dirname -.type dirname, %function; -dirname: -.globl ffs -.type ffs, %function; -ffs: -.globl ffsl -.type ffsl, %function; -ffsl: -.globl ffsll -.type ffsll, %function; -ffsll: -.globl fmtmsg -.type fmtmsg, %function; -fmtmsg: -.globl forkpty -.type forkpty, %function; -forkpty: -.globl get_current_dir_name -.type get_current_dir_name, %function; -get_current_dir_name: -.weak getauxval -.type getauxval, %function; -getauxval: -.globl getdomainname -.type getdomainname, %function; -getdomainname: -.globl getentropy -.type getentropy, %function; -getentropy: -.globl gethostid -.type gethostid, %function; -gethostid: -.weak __posix_getopt -.type __posix_getopt, %function; -__posix_getopt: -.globl getopt -.type getopt, %function; -getopt: -.globl getopt_long -.type getopt_long, %function; -getopt_long: -.globl getopt_long_only -.type getopt_long_only, %function; -getopt_long_only: -.globl getpriority -.type getpriority, %function; -getpriority: -.globl getresgid -.type getresgid, %function; -getresgid: -.globl getresuid -.type getresuid, %function; -getresuid: -.weak getrlimit64 -.type getrlimit64, %function; -getrlimit64: -.globl getrlimit -.type getrlimit, %function; -getrlimit: -.globl getrusage -.type getrusage, %function; -getrusage: -.globl getsubopt -.type getsubopt, %function; -getsubopt: -.globl initgroups -.type initgroups, %function; -initgroups: -.globl ioctl -.type ioctl, %function; -ioctl: -.globl issetugid -.type issetugid, %function; -issetugid: -.weak lockf64 -.type lockf64, %function; -lockf64: -.globl lockf -.type lockf, %function; -lockf: -.globl login_tty -.type login_tty, %function; -login_tty: -.globl setmntent -.type setmntent, %function; -setmntent: -.globl endmntent -.type endmntent, %function; -endmntent: -.globl getmntent_r -.type getmntent_r, %function; -getmntent_r: -.globl getmntent -.type getmntent, %function; -getmntent: -.globl addmntent -.type addmntent, %function; -addmntent: -.globl hasmntopt -.type hasmntopt, %function; -hasmntopt: -.weak nftw64 -.type nftw64, %function; -nftw64: -.globl nftw -.type nftw, %function; -nftw: -.globl openpty -.type openpty, %function; -openpty: -.globl ptsname -.type ptsname, %function; -ptsname: -.globl posix_openpt -.type posix_openpt, %function; -posix_openpt: -.globl grantpt -.type grantpt, %function; -grantpt: -.globl unlockpt -.type unlockpt, %function; -unlockpt: -.weak ptsname_r -.type ptsname_r, %function; -ptsname_r: -.globl realpath -.type realpath, %function; -realpath: -.globl setdomainname -.type setdomainname, %function; -setdomainname: -.globl setpriority -.type setpriority, %function; -setpriority: -.globl setrlimit -.type setrlimit, %function; -setrlimit: -.weak setrlimit64 -.type setrlimit64, %function; -setrlimit64: -.globl syscall -.type syscall, %function; -syscall: -.globl setlogmask -.type setlogmask, %function; -setlogmask: -.globl closelog -.type closelog, %function; -closelog: -.globl openlog -.type openlog, %function; -openlog: -.globl syslog -.type syslog, %function; -syslog: -.weak vsyslog -.type vsyslog, %function; -vsyslog: -.globl uname -.type uname, %function; -uname: -.globl wordexp -.type wordexp, %function; -wordexp: -.globl wordfree -.type wordfree, %function; -wordfree: +.globl lsearch +.type lsearch, %function; +lsearch: +.weak lseek +.type lseek, %function; +lseek: +.weak lseek64 +.type lseek64, %function; +lseek64: +.globl lsetxattr +.type lsetxattr, %function; +lsetxattr: +.globl lstat +.type lstat, %function; +lstat: +.weak lstat64 +.type lstat64, %function; +lstat64: +.globl lutimes +.type lutimes, %function; +lutimes: .weak madvise .type madvise, %function; madvise: -.globl mincore -.type mincore, %function; -mincore: -.globl mlock -.type mlock, %function; -mlock: -.globl mlockall -.type mlockall, %function; -mlockall: -.weak mmap -.type mmap, %function; -mmap: -.weak mmap64 -.type mmap64, %function; -mmap64: -.weak mprotect -.type mprotect, %function; -mprotect: -.weak mremap -.type mremap, %function; -mremap: -.globl msync -.type msync, %function; -msync: -.globl munlock -.type munlock, %function; -munlock: -.globl munlockall -.type munlockall, %function; -munlockall: -.weak munmap -.type munmap, %function; -munmap: -.globl posix_madvise -.type posix_madvise, %function; -posix_madvise: -.globl shm_open -.type shm_open, %function; -shm_open: -.globl shm_unlink -.type shm_unlink, %function; -shm_unlink: -.globl mq_close -.type mq_close, %function; -mq_close: -.globl mq_getattr -.type mq_getattr, %function; -mq_getattr: -.globl mq_notify -.type mq_notify, %function; -mq_notify: -.globl mq_open -.type mq_open, %function; -mq_open: -.globl mq_receive -.type mq_receive, %function; -mq_receive: -.globl mq_send -.type mq_send, %function; -mq_send: -.globl mq_setattr -.type mq_setattr, %function; -mq_setattr: -.globl mq_timedreceive -.type mq_timedreceive, %function; -mq_timedreceive: -.globl mq_timedsend -.type mq_timedsend, %function; -mq_timedsend: -.globl mq_unlink -.type mq_unlink, %function; -mq_unlink: -.globl btowc -.type btowc, %function; -btowc: -.globl c16rtomb -.type c16rtomb, %function; -c16rtomb: -.globl c32rtomb -.type c32rtomb, %function; -c32rtomb: +.weak malloc +.type malloc, %function; +malloc: +.globl malloc_usable_size +.type malloc_usable_size, %function; +malloc_usable_size: .globl mblen .type mblen, %function; mblen: @@ -2368,480 +3083,384 @@ mbstowcs: .globl mbtowc .type mbtowc, %function; mbtowc: -.globl wcrtomb -.type wcrtomb, %function; -wcrtomb: -.globl wcsnrtombs -.type wcsnrtombs, %function; -wcsnrtombs: -.globl wcsrtombs -.type wcsrtombs, %function; -wcsrtombs: -.globl wcstombs -.type wcstombs, %function; -wcstombs: -.globl wctob -.type wctob, %function; -wctob: -.globl wctomb -.type wctomb, %function; -wctomb: -.globl accept -.type accept, %function; -accept: -.globl accept4 -.type accept4, %function; -accept4: -.globl bind -.type bind, %function; -bind: -.globl connect -.type connect, %function; -connect: -.globl dn_comp -.type dn_comp, %function; -dn_comp: -.weak dn_expand -.type dn_expand, %function; -dn_expand: -.globl dn_skipname -.type dn_skipname, %function; -dn_skipname: -.globl sethostent -.type sethostent, %function; -sethostent: -.weak setnetent -.type setnetent, %function; -setnetent: -.globl gethostent -.type gethostent, %function; -gethostent: -.globl getnetent -.type getnetent, %function; -getnetent: -.weak endnetent -.type endnetent, %function; -endnetent: -.globl endhostent -.type endhostent, %function; -endhostent: -.globl ether_aton_r -.type ether_aton_r, %function; -ether_aton_r: -.globl ether_aton -.type ether_aton, %function; -ether_aton: -.globl ether_ntoa_r -.type ether_ntoa_r, %function; -ether_ntoa_r: -.globl ether_ntoa -.type ether_ntoa, %function; -ether_ntoa: -.globl ether_line -.type ether_line, %function; -ether_line: -.globl ether_ntohost -.type ether_ntohost, %function; -ether_ntohost: -.globl ether_hostton -.type ether_hostton, %function; -ether_hostton: -.globl freeaddrinfo -.type freeaddrinfo, %function; -freeaddrinfo: -.globl gai_strerror -.type gai_strerror, %function; -gai_strerror: -.globl getaddrinfo -.type getaddrinfo, %function; -getaddrinfo: -.globl gethostbyaddr -.type gethostbyaddr, %function; -gethostbyaddr: -.globl gethostbyaddr_r -.type gethostbyaddr_r, %function; -gethostbyaddr_r: -.globl gethostbyname -.type gethostbyname, %function; -gethostbyname: -.globl gethostbyname2 -.type gethostbyname2, %function; -gethostbyname2: -.globl gethostbyname2_r -.type gethostbyname2_r, %function; -gethostbyname2_r: -.globl gethostbyname_r -.type gethostbyname_r, %function; -gethostbyname_r: -.globl freeifaddrs -.type freeifaddrs, %function; -freeifaddrs: -.globl getifaddrs -.type getifaddrs, %function; -getifaddrs: -.globl getnameinfo -.type getnameinfo, %function; -getnameinfo: -.globl getpeername -.type getpeername, %function; -getpeername: -.globl getservbyname -.type getservbyname, %function; -getservbyname: -.globl getservbyname_r -.type getservbyname_r, %function; -getservbyname_r: -.globl getservbyport -.type getservbyport, %function; -getservbyport: -.globl getservbyport_r -.type getservbyport_r, %function; -getservbyport_r: -.globl getsockname -.type getsockname, %function; -getsockname: -.globl getsockopt -.type getsockopt, %function; -getsockopt: -.globl __h_errno_location -.type __h_errno_location, %function; -__h_errno_location: -.globl herror -.type herror, %function; -herror: -.globl hstrerror -.type hstrerror, %function; -hstrerror: -.globl htonl -.type htonl, %function; -htonl: -.globl htons -.type htons, %function; -htons: -.globl if_freenameindex -.type if_freenameindex, %function; -if_freenameindex: -.globl if_indextoname -.type if_indextoname, %function; -if_indextoname: -.globl if_nameindex -.type if_nameindex, %function; -if_nameindex: -.globl if_nametoindex -.type if_nametoindex, %function; -if_nametoindex: -.globl inet_addr -.type inet_addr, %function; -inet_addr: -.weak inet_aton -.type inet_aton, %function; -inet_aton: -.globl inet_network -.type inet_network, %function; -inet_network: -.globl inet_makeaddr -.type inet_makeaddr, %function; -inet_makeaddr: -.globl inet_lnaof -.type inet_lnaof, %function; -inet_lnaof: -.globl inet_netof -.type inet_netof, %function; -inet_netof: -.globl inet_ntoa -.type inet_ntoa, %function; -inet_ntoa: -.globl inet_ntop -.type inet_ntop, %function; -inet_ntop: -.globl inet_pton -.type inet_pton, %function; -inet_pton: -.globl listen -.type listen, %function; -listen: -.globl getnetbyaddr -.type getnetbyaddr, %function; -getnetbyaddr: -.globl getnetbyname -.type getnetbyname, %function; -getnetbyname: +.globl memalign +.type memalign, %function; +memalign: +.weak membarrier +.type membarrier, %function; +membarrier: +.globl memccpy +.type memccpy, %function; +memccpy: +.globl memchr +.type memchr, %function; +memchr: +.globl memcmp +.type memcmp, %function; +memcmp: +.globl memcpy +.type memcpy, %function; +memcpy: +.globl memfd_create +.type memfd_create, %function; +memfd_create: +.globl memmem +.type memmem, %function; +memmem: +.globl memmove +.type memmove, %function; +memmove: +.globl mempcpy +.type mempcpy, %function; +mempcpy: +.weak memrchr +.type memrchr, %function; +memrchr: +.globl memset +.type memset, %function; +memset: +.globl mincore +.type mincore, %function; +mincore: +.globl mkdir +.type mkdir, %function; +mkdir: +.globl mkdirat +.type mkdirat, %function; +mkdirat: +.globl mkdtemp +.type mkdtemp, %function; +mkdtemp: +.globl mkfifo +.type mkfifo, %function; +mkfifo: +.globl mkfifoat +.type mkfifoat, %function; +mkfifoat: +.globl mknod +.type mknod, %function; +mknod: +.globl mknodat +.type mknodat, %function; +mknodat: +.globl mkostemp +.type mkostemp, %function; +mkostemp: +.weak mkostemp64 +.type mkostemp64, %function; +mkostemp64: +.weak mkostemps +.type mkostemps, %function; +mkostemps: +.weak mkostemps64 +.type mkostemps64, %function; +mkostemps64: +.globl mkstemp +.type mkstemp, %function; +mkstemp: +.weak mkstemp64 +.type mkstemp64, %function; +mkstemp64: +.globl mkstemps +.type mkstemps, %function; +mkstemps: +.weak mkstemps64 +.type mkstemps64, %function; +mkstemps64: +.globl mktemp +.type mktemp, %function; +mktemp: +.globl mktime +.type mktime, %function; +mktime: +.globl mlock +.type mlock, %function; +mlock: +.globl mlock2 +.type mlock2, %function; +mlock2: +.globl mlockall +.type mlockall, %function; +mlockall: +.weak mmap +.type mmap, %function; +mmap: +.weak mmap64 +.type mmap64, %function; +mmap64: +.globl modf +.type modf, %function; +modf: +.globl modff +.type modff, %function; +modff: +.globl modfl +.type modfl, %function; +modfl: +.globl mount +.type mount, %function; +mount: +.weak mprotect +.type mprotect, %function; +mprotect: +.globl mq_close +.type mq_close, %function; +mq_close: +.globl mq_getattr +.type mq_getattr, %function; +mq_getattr: +.globl mq_notify +.type mq_notify, %function; +mq_notify: +.globl mq_open +.type mq_open, %function; +mq_open: +.globl mq_receive +.type mq_receive, %function; +mq_receive: +.globl mq_send +.type mq_send, %function; +mq_send: +.globl mq_setattr +.type mq_setattr, %function; +mq_setattr: +.globl mq_timedreceive +.type mq_timedreceive, %function; +mq_timedreceive: +.globl mq_timedsend +.type mq_timedsend, %function; +mq_timedsend: +.globl mq_unlink +.type mq_unlink, %function; +mq_unlink: +.globl mrand48 +.type mrand48, %function; +mrand48: +.weak mremap +.type mremap, %function; +mremap: +.globl msgctl +.type msgctl, %function; +msgctl: +.globl msgget +.type msgget, %function; +msgget: +.globl msgrcv +.type msgrcv, %function; +msgrcv: +.globl msgsnd +.type msgsnd, %function; +msgsnd: +.globl msync +.type msync, %function; +msync: +.globl mtx_destroy +.type mtx_destroy, %function; +mtx_destroy: +.globl mtx_init +.type mtx_init, %function; +mtx_init: +.globl mtx_lock +.type mtx_lock, %function; +mtx_lock: +.globl mtx_timedlock +.type mtx_timedlock, %function; +mtx_timedlock: +.globl mtx_trylock +.type mtx_trylock, %function; +mtx_trylock: +.globl mtx_unlock +.type mtx_unlock, %function; +mtx_unlock: +.globl munlock +.type munlock, %function; +munlock: +.globl munlockall +.type munlockall, %function; +munlockall: +.weak munmap +.type munmap, %function; +munmap: +.globl name_to_handle_at +.type name_to_handle_at, %function; +name_to_handle_at: +.globl nan +.type nan, %function; +nan: +.globl nanf +.type nanf, %function; +nanf: +.globl nanl +.type nanl, %function; +nanl: +.globl nanosleep +.type nanosleep, %function; +nanosleep: +.globl nearbyint +.type nearbyint, %function; +nearbyint: +.globl nearbyintf +.type nearbyintf, %function; +nearbyintf: +.globl nearbyintl +.type nearbyintl, %function; +nearbyintl: +.weak newlocale +.type newlocale, %function; +newlocale: +.globl nextafter +.type nextafter, %function; +nextafter: +.globl nextafterf +.type nextafterf, %function; +nextafterf: +.globl nextafterl +.type nextafterl, %function; +nextafterl: +.globl nexttoward +.type nexttoward, %function; +nexttoward: +.globl nexttowardf +.type nexttowardf, %function; +nexttowardf: +.globl nexttowardl +.type nexttowardl, %function; +nexttowardl: +.globl nftw +.type nftw, %function; +nftw: +.weak nftw64 +.type nftw64, %function; +nftw64: +.globl ngettext +.type ngettext, %function; +ngettext: +.globl nice +.type nice, %function; +nice: +.weak nl_langinfo +.type nl_langinfo, %function; +nl_langinfo: +.weak nl_langinfo_l +.type nl_langinfo_l, %function; +nl_langinfo_l: +.globl nrand48 +.type nrand48, %function; +nrand48: .globl ns_get16 .type ns_get16, %function; ns_get16: .globl ns_get32 .type ns_get32, %function; ns_get32: +.globl ns_initparse +.type ns_initparse, %function; +ns_initparse: +.globl ns_name_uncompress +.type ns_name_uncompress, %function; +ns_name_uncompress: +.globl ns_parserr +.type ns_parserr, %function; +ns_parserr: .globl ns_put16 .type ns_put16, %function; ns_put16: .globl ns_put32 .type ns_put32, %function; ns_put32: -.globl ns_initparse -.type ns_initparse, %function; -ns_initparse: .globl ns_skiprr .type ns_skiprr, %function; ns_skiprr: -.globl ns_parserr -.type ns_parserr, %function; -ns_parserr: -.globl ns_name_uncompress -.type ns_name_uncompress, %function; -ns_name_uncompress: .globl ntohl .type ntohl, %function; ntohl: .globl ntohs .type ntohs, %function; ntohs: -.globl endprotoent -.type endprotoent, %function; -endprotoent: -.globl setprotoent -.type setprotoent, %function; -setprotoent: -.globl getprotoent -.type getprotoent, %function; -getprotoent: -.globl getprotobyname -.type getprotobyname, %function; -getprotobyname: -.globl getprotobynumber -.type getprotobynumber, %function; -getprotobynumber: -.globl recv -.type recv, %function; -recv: -.globl recvfrom -.type recvfrom, %function; -recvfrom: -.globl recvmmsg -.type recvmmsg, %function; -recvmmsg: -.globl recvmsg -.type recvmsg, %function; -recvmsg: -.globl res_init -.type res_init, %function; -res_init: -.weak res_mkquery -.type res_mkquery, %function; -res_mkquery: -.globl res_query -.type res_query, %function; -res_query: -.weak res_search -.type res_search, %function; -res_search: -.globl res_querydomain -.type res_querydomain, %function; -res_querydomain: -.weak res_send -.type res_send, %function; -res_send: -.globl __res_state -.type __res_state, %function; -__res_state: -.globl send -.type send, %function; -send: -.globl sendmmsg -.type sendmmsg, %function; -sendmmsg: -.globl sendmsg -.type sendmsg, %function; -sendmsg: -.globl sendto -.type sendto, %function; -sendto: -.globl endservent -.type endservent, %function; -endservent: -.globl setservent -.type setservent, %function; -setservent: -.globl getservent -.type getservent, %function; -getservent: -.globl setsockopt -.type setsockopt, %function; -setsockopt: -.globl shutdown -.type shutdown, %function; -shutdown: -.globl sockatmark -.type sockatmark, %function; -sockatmark: -.globl socket -.type socket, %function; -socket: -.globl socketpair -.type socketpair, %function; -socketpair: -.globl fgetgrent -.type fgetgrent, %function; -fgetgrent: -.globl fgetpwent -.type fgetpwent, %function; -fgetpwent: -.globl fgetspent -.type fgetspent, %function; -fgetspent: -.globl getgrnam_r -.type getgrnam_r, %function; -getgrnam_r: -.globl getgrgid_r -.type getgrgid_r, %function; -getgrgid_r: -.weak endgrent -.type endgrent, %function; -endgrent: -.globl setgrent -.type setgrent, %function; -setgrent: -.globl getgrent -.type getgrent, %function; -getgrent: -.globl getgrgid -.type getgrgid, %function; -getgrgid: -.globl getgrnam -.type getgrnam, %function; -getgrnam: -.globl getgrouplist -.type getgrouplist, %function; -getgrouplist: -.globl getpwnam_r -.type getpwnam_r, %function; -getpwnam_r: -.globl getpwuid_r -.type getpwuid_r, %function; -getpwuid_r: -.globl setpwent -.type setpwent, %function; -setpwent: -.weak endpwent -.type endpwent, %function; -endpwent: -.globl getpwent -.type getpwent, %function; -getpwent: -.globl getpwuid -.type getpwuid, %function; -getpwuid: -.globl getpwnam -.type getpwnam, %function; -getpwnam: -.globl setspent -.type setspent, %function; -setspent: -.globl endspent -.type endspent, %function; -endspent: -.globl getspent -.type getspent, %function; -getspent: -.globl getspnam -.type getspnam, %function; -getspnam: -.globl getspnam_r -.type getspnam_r, %function; -getspnam_r: -.globl lckpwdf -.type lckpwdf, %function; -lckpwdf: -.globl ulckpwdf -.type ulckpwdf, %function; -ulckpwdf: -.globl putgrent -.type putgrent, %function; -putgrent: -.globl putpwent -.type putpwent, %function; -putpwent: -.globl putspent -.type putspent, %function; -putspent: -.globl erand48 -.type erand48, %function; -erand48: -.globl drand48 -.type drand48, %function; -drand48: -.globl lcong48 -.type lcong48, %function; -lcong48: -.globl nrand48 -.type nrand48, %function; -nrand48: -.globl lrand48 -.type lrand48, %function; -lrand48: -.globl jrand48 -.type jrand48, %function; -jrand48: -.globl mrand48 -.type mrand48, %function; -mrand48: -.globl srand -.type srand, %function; -srand: -.globl rand -.type rand, %function; -rand: -.globl rand_r -.type rand_r, %function; -rand_r: -.globl srandom -.type srandom, %function; -srandom: -.globl initstate -.type initstate, %function; -initstate: -.globl setstate -.type setstate, %function; -setstate: -.globl random -.type random, %function; -random: -.globl seed48 -.type seed48, %function; -seed48: -.globl srand48 -.type srand48, %function; -srand48: -.globl _Fork -.type _Fork, %function; -_Fork: -.globl execl -.type execl, %function; -execl: -.globl execle -.type execle, %function; -execle: -.globl execlp -.type execlp, %function; -execlp: -.globl execv -.type execv, %function; -execv: -.globl execve -.type execve, %function; -execve: -.weak execvpe -.type execvpe, %function; -execvpe: -.globl execvp -.type execvp, %function; -execvp: -.globl fexecve -.type fexecve, %function; -fexecve: -.globl fork -.type fork, %function; -fork: +.globl open +.type open, %function; +open: +.weak open64 +.type open64, %function; +open64: +.globl open_by_handle_at +.type open_by_handle_at, %function; +open_by_handle_at: +.globl open_memstream +.type open_memstream, %function; +open_memstream: +.globl open_wmemstream +.type open_wmemstream, %function; +open_wmemstream: +.globl openat +.type openat, %function; +openat: +.weak openat64 +.type openat64, %function; +openat64: +.globl opendir +.type opendir, %function; +opendir: +.globl openlog +.type openlog, %function; +openlog: +.globl openpty +.type openpty, %function; +openpty: +.globl pathconf +.type pathconf, %function; +pathconf: +.globl pause +.type pause, %function; +pause: +.globl pclose +.type pclose, %function; +pclose: +.globl perror +.type perror, %function; +perror: +.globl personality +.type personality, %function; +personality: +.globl pipe +.type pipe, %function; +pipe: +.globl pipe2 +.type pipe2, %function; +pipe2: +.globl pivot_root +.type pivot_root, %function; +pivot_root: +.globl poll +.type poll, %function; +poll: +.globl popen +.type popen, %function; +popen: +.globl posix_close +.type posix_close, %function; +posix_close: +.globl posix_fadvise +.type posix_fadvise, %function; +posix_fadvise: +.weak posix_fadvise64 +.type posix_fadvise64, %function; +posix_fadvise64: +.globl posix_fallocate +.type posix_fallocate, %function; +posix_fallocate: +.weak posix_fallocate64 +.type posix_fallocate64, %function; +posix_fallocate64: +.globl posix_madvise +.type posix_madvise, %function; +posix_madvise: +.globl posix_memalign +.type posix_memalign, %function; +posix_memalign: +.globl posix_openpt +.type posix_openpt, %function; +posix_openpt: .globl posix_spawn .type posix_spawn, %function; posix_spawn: @@ -2875,6 +3494,12 @@ posix_spawnattr_getflags: .globl posix_spawnattr_getpgroup .type posix_spawnattr_getpgroup, %function; posix_spawnattr_getpgroup: +.globl posix_spawnattr_getschedparam +.type posix_spawnattr_getschedparam, %function; +posix_spawnattr_getschedparam: +.globl posix_spawnattr_getschedpolicy +.type posix_spawnattr_getschedpolicy, %function; +posix_spawnattr_getschedpolicy: .globl posix_spawnattr_getsigdefault .type posix_spawnattr_getsigdefault, %function; posix_spawnattr_getsigdefault: @@ -2884,24 +3509,18 @@ posix_spawnattr_getsigmask: .globl posix_spawnattr_init .type posix_spawnattr_init, %function; posix_spawnattr_init: -.globl posix_spawnattr_getschedparam -.type posix_spawnattr_getschedparam, %function; -posix_spawnattr_getschedparam: -.globl posix_spawnattr_setschedparam -.type posix_spawnattr_setschedparam, %function; -posix_spawnattr_setschedparam: -.globl posix_spawnattr_getschedpolicy -.type posix_spawnattr_getschedpolicy, %function; -posix_spawnattr_getschedpolicy: -.globl posix_spawnattr_setschedpolicy -.type posix_spawnattr_setschedpolicy, %function; -posix_spawnattr_setschedpolicy: .globl posix_spawnattr_setflags .type posix_spawnattr_setflags, %function; posix_spawnattr_setflags: .globl posix_spawnattr_setpgroup .type posix_spawnattr_setpgroup, %function; posix_spawnattr_setpgroup: +.globl posix_spawnattr_setschedparam +.type posix_spawnattr_setschedparam, %function; +posix_spawnattr_setschedparam: +.globl posix_spawnattr_setschedpolicy +.type posix_spawnattr_setschedpolicy, %function; +posix_spawnattr_setschedpolicy: .globl posix_spawnattr_setsigdefault .type posix_spawnattr_setsigdefault, %function; posix_spawnattr_setsigdefault: @@ -2911,1371 +3530,66 @@ posix_spawnattr_setsigmask: .globl posix_spawnp .type posix_spawnp, %function; posix_spawnp: -.globl system -.type system, %function; -system: -.globl vfork -.type vfork, %function; -vfork: -.globl wait -.type wait, %function; -wait: -.globl waitid -.type waitid, %function; -waitid: -.globl waitpid -.type waitpid, %function; -waitpid: -.globl fnmatch -.type fnmatch, %function; -fnmatch: -.weak glob64 -.type glob64, %function; -glob64: -.globl glob -.type glob, %function; -glob: -.globl globfree -.type globfree, %function; -globfree: -.weak globfree64 -.type globfree64, %function; -globfree64: -.globl regcomp -.type regcomp, %function; -regcomp: -.globl regfree -.type regfree, %function; -regfree: -.globl regerror -.type regerror, %function; -regerror: -.globl regexec -.type regexec, %function; -regexec: -.globl sched_setaffinity -.type sched_setaffinity, %function; -sched_setaffinity: -.globl pthread_setaffinity_np -.type pthread_setaffinity_np, %function; -pthread_setaffinity_np: -.globl sched_getaffinity -.type sched_getaffinity, %function; -sched_getaffinity: -.globl pthread_getaffinity_np -.type pthread_getaffinity_np, %function; -pthread_getaffinity_np: -.globl __sched_cpucount -.type __sched_cpucount, %function; -__sched_cpucount: -.globl sched_get_priority_max -.type sched_get_priority_max, %function; -sched_get_priority_max: -.globl sched_get_priority_min -.type sched_get_priority_min, %function; -sched_get_priority_min: -.globl sched_getcpu -.type sched_getcpu, %function; -sched_getcpu: -.globl sched_getparam -.type sched_getparam, %function; -sched_getparam: -.globl sched_getscheduler -.type sched_getscheduler, %function; -sched_getscheduler: -.globl sched_rr_get_interval -.type sched_rr_get_interval, %function; -sched_rr_get_interval: -.globl sched_setparam -.type sched_setparam, %function; -sched_setparam: -.globl sched_setscheduler -.type sched_setscheduler, %function; -sched_setscheduler: -.globl sched_yield -.type sched_yield, %function; -sched_yield: -.globl hcreate -.type hcreate, %function; -hcreate: -.weak hcreate_r -.type hcreate_r, %function; -hcreate_r: -.globl hdestroy -.type hdestroy, %function; -hdestroy: -.weak hdestroy_r -.type hdestroy_r, %function; -hdestroy_r: -.globl hsearch -.type hsearch, %function; -hsearch: -.weak hsearch_r -.type hsearch_r, %function; -hsearch_r: -.globl insque -.type insque, %function; -insque: -.globl remque -.type remque, %function; -remque: -.globl lsearch -.type lsearch, %function; -lsearch: -.globl lfind -.type lfind, %function; -lfind: -.globl tdelete -.type tdelete, %function; -tdelete: -.globl tdestroy -.type tdestroy, %function; -tdestroy: -.globl tfind -.type tfind, %function; -tfind: -.globl tsearch -.type tsearch, %function; -tsearch: -.globl twalk -.type twalk, %function; -twalk: -.globl poll -.type poll, %function; -poll: +.globl pow +.type pow, %function; +pow: +.weak pow10 +.type pow10, %function; +pow10: +.weak pow10f +.type pow10f, %function; +pow10f: +.weak pow10l +.type pow10l, %function; +pow10l: +.globl powf +.type powf, %function; +powf: +.globl powl +.type powl, %function; +powl: +.globl ppoll +.type ppoll, %function; +ppoll: +.globl prctl +.type prctl, %function; +prctl: +.globl pread +.type pread, %function; +pread: +.weak pread64 +.type pread64, %function; +pread64: +.globl preadv +.type preadv, %function; +preadv: +.weak preadv64 +.type preadv64, %function; +preadv64: +.globl printf +.type printf, %function; +printf: +.globl prlimit +.type prlimit, %function; +prlimit: +.weak prlimit64 +.type prlimit64, %function; +prlimit64: +.globl process_vm_readv +.type process_vm_readv, %function; +process_vm_readv: +.globl process_vm_writev +.type process_vm_writev, %function; +process_vm_writev: .globl pselect .type pselect, %function; pselect: -.globl select -.type select, %function; -select: -#if !defined(ARCH_mips) && !defined(ARCH_i386) && !defined(ARCH_x86_64) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) -.globl __longjmp -.type __longjmp, %function; -__longjmp: -#endif -.globl longjmp -.type longjmp, %function; -longjmp: -.globl _longjmp -.type _longjmp, %function; -_longjmp: -.globl _setjmp -.type _setjmp, %function; -_setjmp: -.globl __setjmp -.type __setjmp, %function; -__setjmp: -.globl setjmp -.type setjmp, %function; -setjmp: -.globl getitimer -.type getitimer, %function; -getitimer: -.globl kill -.type kill, %function; -kill: -.globl killpg -.type killpg, %function; -killpg: .globl psiginfo .type psiginfo, %function; psiginfo: .globl psignal .type psignal, %function; psignal: -.globl raise -.type raise, %function; -raise: -.globl __sigsetjmp -.type __sigsetjmp, %function; -__sigsetjmp: -.globl sigsetjmp -.type sigsetjmp, %function; -sigsetjmp: -.globl setitimer -.type setitimer, %function; -setitimer: -.weak sigaction -.type sigaction, %function; -sigaction: -.globl sigaddset -.type sigaddset, %function; -sigaddset: -.globl sigaltstack -.type sigaltstack, %function; -sigaltstack: -.globl sigandset -.type sigandset, %function; -sigandset: -.globl sigdelset -.type sigdelset, %function; -sigdelset: -.globl sigemptyset -.type sigemptyset, %function; -sigemptyset: -.globl sigfillset -.type sigfillset, %function; -sigfillset: -.globl sighold -.type sighold, %function; -sighold: -.globl sigignore -.type sigignore, %function; -sigignore: -.globl siginterrupt -.type siginterrupt, %function; -siginterrupt: -.globl sigisemptyset -.type sigisemptyset, %function; -sigisemptyset: -.globl sigismember -.type sigismember, %function; -sigismember: -.globl siglongjmp -.type siglongjmp, %function; -siglongjmp: -.globl signal -.type signal, %function; -signal: -.weak __sysv_signal -.type __sysv_signal, %function; -__sysv_signal: -.weak bsd_signal -.type bsd_signal, %function; -bsd_signal: -.globl sigorset -.type sigorset, %function; -sigorset: -.globl sigpause -.type sigpause, %function; -sigpause: -.globl sigpending -.type sigpending, %function; -sigpending: -.globl sigprocmask -.type sigprocmask, %function; -sigprocmask: -.globl sigqueue -.type sigqueue, %function; -sigqueue: -.globl sigrelse -.type sigrelse, %function; -sigrelse: -.globl __libc_current_sigrtmax -.type __libc_current_sigrtmax, %function; -__libc_current_sigrtmax: -.globl __libc_current_sigrtmin -.type __libc_current_sigrtmin, %function; -__libc_current_sigrtmin: -.globl sigset -.type sigset, %function; -sigset: -.globl sigsuspend -.type sigsuspend, %function; -sigsuspend: -.globl sigtimedwait -.type sigtimedwait, %function; -sigtimedwait: -.globl sigwait -.type sigwait, %function; -sigwait: -.globl sigwaitinfo -.type sigwaitinfo, %function; -sigwaitinfo: -#if !defined(ARCH_mips) && !defined(ARCH_i386) && !defined(ARCH_powerpc) -.globl __fxstat -.type __fxstat, %function; -__fxstat: -#endif -WEAK64 __fxstat64 -.type __fxstat64, %function; -__fxstat64: -WEAK64 __fxstatat64 -.type __fxstatat64, %function; -__fxstatat64: -#if !defined(ARCH_mips) && !defined(ARCH_i386) && !defined(ARCH_powerpc) -.globl __fxstatat -.type __fxstatat, %function; -__fxstatat: -#endif -WEAK64 __lxstat64 -.type __lxstat64, %function; -__lxstat64: -#if !defined(ARCH_mips) && !defined(ARCH_i386) && !defined(ARCH_powerpc) -.globl __lxstat -.type __lxstat, %function; -__lxstat: -#endif -#if !defined(ARCH_mips) && !defined(ARCH_i386) && !defined(ARCH_powerpc) -.globl __xstat -.type __xstat, %function; -__xstat: -#endif -WEAK64 __xstat64 -.type __xstat64, %function; -__xstat64: -.globl __xmknod -.type __xmknod, %function; -__xmknod: -.globl __xmknodat -.type __xmknodat, %function; -__xmknodat: -.globl chmod -.type chmod, %function; -chmod: -.globl fchmod -.type fchmod, %function; -fchmod: -.globl fchmodat -.type fchmodat, %function; -fchmodat: -.weak fstat64 -.type fstat64, %function; -fstat64: -.globl fstat -.type fstat, %function; -fstat: -.weak fstatat64 -.type fstatat64, %function; -fstatat64: -.globl fstatat -.type fstatat, %function; -fstatat: -.globl futimens -.type futimens, %function; -futimens: -WEAK64 futimesat -.type futimesat, %function; -futimesat: -.globl lchmod -.type lchmod, %function; -lchmod: -.globl lstat -.type lstat, %function; -lstat: -.weak lstat64 -.type lstat64, %function; -lstat64: -.globl mkdir -.type mkdir, %function; -mkdir: -.globl mkdirat -.type mkdirat, %function; -mkdirat: -.globl mkfifo -.type mkfifo, %function; -mkfifo: -.globl mkfifoat -.type mkfifoat, %function; -mkfifoat: -.globl mknod -.type mknod, %function; -mknod: -.globl mknodat -.type mknodat, %function; -mknodat: -.globl stat -.type stat, %function; -stat: -.weak stat64 -.type stat64, %function; -stat64: -.weak statfs64 -.type statfs64, %function; -statfs64: -.weak statfs -.type statfs, %function; -statfs: -.weak fstatfs64 -.type fstatfs64, %function; -fstatfs64: -.weak fstatfs -.type fstatfs, %function; -fstatfs: -.weak statvfs64 -.type statvfs64, %function; -statvfs64: -.globl statvfs -.type statvfs, %function; -statvfs: -.globl fstatvfs -.type fstatvfs, %function; -fstatvfs: -.weak fstatvfs64 -.type fstatvfs64, %function; -fstatvfs64: -.globl umask -.type umask, %function; -umask: -.globl utimensat -.type utimensat, %function; -utimensat: -.weak fdopen -.type fdopen, %function; -fdopen: -.globl __overflow -.type __overflow, %function; -.protected __overflow -__overflow: -.globl __uflow -.type __uflow, %function; -.protected __uflow -__uflow: -.globl asprintf -.type asprintf, %function; -asprintf: -.globl clearerr -.type clearerr, %function; -clearerr: -.weak clearerr_unlocked -.type clearerr_unlocked, %function; -clearerr_unlocked: -.globl dprintf -.type dprintf, %function; -dprintf: -.globl _flushlbf -.type _flushlbf, %function; -_flushlbf: -.globl __fsetlocking -.type __fsetlocking, %function; -__fsetlocking: -.globl __fwriting -.type __fwriting, %function; -__fwriting: -.globl __freading -.type __freading, %function; -__freading: -.globl __freadable -.type __freadable, %function; -__freadable: -.globl __fwritable -.type __fwritable, %function; -__fwritable: -.globl __flbf -.type __flbf, %function; -__flbf: -.globl __fbufsize -.type __fbufsize, %function; -__fbufsize: -.globl __fpending -.type __fpending, %function; -__fpending: -.globl __fpurge -.type __fpurge, %function; -__fpurge: -.weak fpurge -.type fpurge, %function; -fpurge: -.globl __freadahead -.type __freadahead, %function; -__freadahead: -.globl __freadptr -.type __freadptr, %function; -__freadptr: -.globl __freadptrinc -.type __freadptrinc, %function; -__freadptrinc: -.globl __fseterr -.type __fseterr, %function; -__fseterr: -.globl fclose -.type fclose, %function; -fclose: -.weak _IO_feof_unlocked -.type _IO_feof_unlocked, %function; -_IO_feof_unlocked: -.weak feof_unlocked -.type feof_unlocked, %function; -feof_unlocked: -.globl feof -.type feof, %function; -feof: -.weak ferror_unlocked -.type ferror_unlocked, %function; -ferror_unlocked: -.weak _IO_ferror_unlocked -.type _IO_ferror_unlocked, %function; -_IO_ferror_unlocked: -.globl ferror -.type ferror, %function; -ferror: -.globl fflush -.type fflush, %function; -fflush: -.weak fflush_unlocked -.type fflush_unlocked, %function; -fflush_unlocked: -.globl fgetc -.type fgetc, %function; -fgetc: -.globl fgetln -.type fgetln, %function; -fgetln: -.globl fgetpos -.type fgetpos, %function; -fgetpos: -.weak fgetpos64 -.type fgetpos64, %function; -fgetpos64: -.globl fgets -.type fgets, %function; -fgets: -.weak fgets_unlocked -.type fgets_unlocked, %function; -fgets_unlocked: -.globl __fgetwc_unlocked -.type __fgetwc_unlocked, %function; -__fgetwc_unlocked: -.weak fgetwc_unlocked -.type fgetwc_unlocked, %function; -fgetwc_unlocked: -.weak getwc_unlocked -.type getwc_unlocked, %function; -getwc_unlocked: -.globl fgetwc -.type fgetwc, %function; -fgetwc: -.weak fgetws_unlocked -.type fgetws_unlocked, %function; -fgetws_unlocked: -.globl fgetws -.type fgetws, %function; -fgetws: -.globl fileno -.type fileno, %function; -fileno: -.weak fileno_unlocked -.type fileno_unlocked, %function; -fileno_unlocked: -.globl flockfile -.type flockfile, %function; -flockfile: -.globl fmemopen -.type fmemopen, %function; -fmemopen: -.weak fopen64 -.type fopen64, %function; -fopen64: -.globl fopen -.type fopen, %function; -fopen: -.globl fopencookie -.type fopencookie, %function; -fopencookie: -.globl fprintf -.type fprintf, %function; -fprintf: -.globl fputc -.type fputc, %function; -fputc: -.globl fputs -.type fputs, %function; -fputs: -.weak fputs_unlocked -.type fputs_unlocked, %function; -fputs_unlocked: -.globl __fputwc_unlocked -.type __fputwc_unlocked, %function; -__fputwc_unlocked: -.weak fputwc_unlocked -.type fputwc_unlocked, %function; -fputwc_unlocked: -.weak putwc_unlocked -.type putwc_unlocked, %function; -putwc_unlocked: -.globl fputwc -.type fputwc, %function; -fputwc: -.weak fputws_unlocked -.type fputws_unlocked, %function; -fputws_unlocked: -.globl fputws -.type fputws, %function; -fputws: -.globl fread -.type fread, %function; -fread: -.weak fread_unlocked -.type fread_unlocked, %function; -fread_unlocked: -.weak freopen64 -.type freopen64, %function; -freopen64: -.globl freopen -.type freopen, %function; -freopen: -.weak __isoc99_fscanf -.type __isoc99_fscanf, %function; -__isoc99_fscanf: -.globl fscanf -.type fscanf, %function; -fscanf: -.weak fseeko64 -.type fseeko64, %function; -fseeko64: -.weak fseeko -.type fseeko, %function; -fseeko: -.globl fseek -.type fseek, %function; -fseek: -.globl fsetpos -.type fsetpos, %function; -fsetpos: -.weak fsetpos64 -.type fsetpos64, %function; -fsetpos64: -.weak ftello -.type ftello, %function; -ftello: -.weak ftello64 -.type ftello64, %function; -ftello64: -.globl ftell -.type ftell, %function; -ftell: -.globl ftrylockfile -.type ftrylockfile, %function; -ftrylockfile: -.globl funlockfile -.type funlockfile, %function; -funlockfile: -.globl fwide -.type fwide, %function; -fwide: -.globl fwprintf -.type fwprintf, %function; -fwprintf: -.globl fwrite -.type fwrite, %function; -fwrite: -.weak fwrite_unlocked -.type fwrite_unlocked, %function; -fwrite_unlocked: -.weak __isoc99_fwscanf -.type __isoc99_fwscanf, %function; -__isoc99_fwscanf: -.globl fwscanf -.type fwscanf, %function; -fwscanf: -.globl getc -.type getc, %function; -getc: -.weak _IO_getc -.type _IO_getc, %function; -_IO_getc: -.weak fgetc_unlocked -.type fgetc_unlocked, %function; -fgetc_unlocked: -.globl getc_unlocked -.type getc_unlocked, %function; -getc_unlocked: -.weak _IO_getc_unlocked -.type _IO_getc_unlocked, %function; -_IO_getc_unlocked: -.globl getchar -.type getchar, %function; -getchar: -.globl getchar_unlocked -.type getchar_unlocked, %function; -getchar_unlocked: -.weak __getdelim -.type __getdelim, %function; -__getdelim: -.globl getdelim -.type getdelim, %function; -getdelim: -.globl getline -.type getline, %function; -getline: -.globl gets -.type gets, %function; -gets: -.globl getw -.type getw, %function; -getw: -.globl getwc -.type getwc, %function; -getwc: -.weak getwchar_unlocked -.type getwchar_unlocked, %function; -getwchar_unlocked: -.globl getwchar -.type getwchar, %function; -getwchar: -.globl open_memstream -.type open_memstream, %function; -open_memstream: -.globl open_wmemstream -.type open_wmemstream, %function; -open_wmemstream: -.globl pclose -.type pclose, %function; -pclose: -.globl perror -.type perror, %function; -perror: -.globl popen -.type popen, %function; -popen: -.globl printf -.type printf, %function; -printf: -.globl putc -.type putc, %function; -putc: -.weak _IO_putc -.type _IO_putc, %function; -_IO_putc: -.weak fputc_unlocked -.type fputc_unlocked, %function; -fputc_unlocked: -.globl putc_unlocked -.type putc_unlocked, %function; -putc_unlocked: -.weak _IO_putc_unlocked -.type _IO_putc_unlocked, %function; -_IO_putc_unlocked: -.globl putchar -.type putchar, %function; -putchar: -.globl putchar_unlocked -.type putchar_unlocked, %function; -putchar_unlocked: -.globl puts -.type puts, %function; -puts: -.globl putw -.type putw, %function; -putw: -.globl putwc -.type putwc, %function; -putwc: -.globl putwchar -.type putwchar, %function; -putwchar: -.weak putwchar_unlocked -.type putwchar_unlocked, %function; -putwchar_unlocked: -.globl remove -.type remove, %function; -remove: -.globl rename -.type rename, %function; -rename: -.globl rewind -.type rewind, %function; -rewind: -.weak __isoc99_scanf -.type __isoc99_scanf, %function; -__isoc99_scanf: -.globl scanf -.type scanf, %function; -scanf: -.globl setbuf -.type setbuf, %function; -setbuf: -.globl setbuffer -.type setbuffer, %function; -setbuffer: -.globl setlinebuf -.type setlinebuf, %function; -setlinebuf: -.globl setvbuf -.type setvbuf, %function; -setvbuf: -.globl snprintf -.type snprintf, %function; -snprintf: -.globl sprintf -.type sprintf, %function; -sprintf: -.globl sscanf -.type sscanf, %function; -sscanf: -.weak __isoc99_sscanf -.type __isoc99_sscanf, %function; -__isoc99_sscanf: -.globl swprintf -.type swprintf, %function; -swprintf: -.globl swscanf -.type swscanf, %function; -swscanf: -.weak __isoc99_swscanf -.type __isoc99_swscanf, %function; -__isoc99_swscanf: -.globl tempnam -.type tempnam, %function; -tempnam: -.weak tmpfile64 -.type tmpfile64, %function; -tmpfile64: -.globl tmpfile -.type tmpfile, %function; -tmpfile: -.globl tmpnam -.type tmpnam, %function; -tmpnam: -.globl ungetc -.type ungetc, %function; -ungetc: -.globl ungetwc -.type ungetwc, %function; -ungetwc: -.globl vasprintf -.type vasprintf, %function; -vasprintf: -.globl vdprintf -.type vdprintf, %function; -vdprintf: -.globl vfprintf -.type vfprintf, %function; -vfprintf: -.globl vfscanf -.type vfscanf, %function; -vfscanf: -.weak __isoc99_vfscanf -.type __isoc99_vfscanf, %function; -__isoc99_vfscanf: -.globl vfwprintf -.type vfwprintf, %function; -vfwprintf: -.weak __isoc99_vfwscanf -.type __isoc99_vfwscanf, %function; -__isoc99_vfwscanf: -.globl vfwscanf -.type vfwscanf, %function; -vfwscanf: -.globl vprintf -.type vprintf, %function; -vprintf: -.globl vscanf -.type vscanf, %function; -vscanf: -.weak __isoc99_vscanf -.type __isoc99_vscanf, %function; -__isoc99_vscanf: -.globl vsnprintf -.type vsnprintf, %function; -vsnprintf: -.globl vsprintf -.type vsprintf, %function; -vsprintf: -.globl vsscanf -.type vsscanf, %function; -vsscanf: -.weak __isoc99_vsscanf -.type __isoc99_vsscanf, %function; -__isoc99_vsscanf: -.globl vswprintf -.type vswprintf, %function; -vswprintf: -.weak __isoc99_vswscanf -.type __isoc99_vswscanf, %function; -__isoc99_vswscanf: -.globl vswscanf -.type vswscanf, %function; -vswscanf: -.globl vwprintf -.type vwprintf, %function; -vwprintf: -.globl vwscanf -.type vwscanf, %function; -vwscanf: -.weak __isoc99_vwscanf -.type __isoc99_vwscanf, %function; -__isoc99_vwscanf: -.globl wprintf -.type wprintf, %function; -wprintf: -.globl wscanf -.type wscanf, %function; -wscanf: -.weak __isoc99_wscanf -.type __isoc99_wscanf, %function; -__isoc99_wscanf: -.globl abs -.type abs, %function; -abs: -.globl atof -.type atof, %function; -atof: -.globl atoi -.type atoi, %function; -atoi: -.globl atol -.type atol, %function; -atol: -.globl atoll -.type atoll, %function; -atoll: -.globl bsearch -.type bsearch, %function; -bsearch: -.globl div -.type div, %function; -div: -.globl ecvt -.type ecvt, %function; -ecvt: -.globl fcvt -.type fcvt, %function; -fcvt: -.globl gcvt -.type gcvt, %function; -gcvt: -.globl imaxabs -.type imaxabs, %function; -imaxabs: -.globl imaxdiv -.type imaxdiv, %function; -imaxdiv: -.globl labs -.type labs, %function; -labs: -.globl ldiv -.type ldiv, %function; -ldiv: -.globl llabs -.type llabs, %function; -llabs: -.globl lldiv -.type lldiv, %function; -lldiv: -.weak qsort_r -.type qsort_r, %function; -qsort_r: -.globl qsort -.type qsort, %function; -qsort: -.globl strtof -.type strtof, %function; -strtof: -.globl strtod -.type strtod, %function; -strtod: -.globl strtold -.type strtold, %function; -strtold: -.globl strtoull -.type strtoull, %function; -strtoull: -.weak __strtoull_internal -.type __strtoull_internal, %function; -__strtoull_internal: -.globl strtoll -.type strtoll, %function; -strtoll: -.weak __strtoll_internal -.type __strtoll_internal, %function; -__strtoll_internal: -.weak __strtoul_internal -.type __strtoul_internal, %function; -__strtoul_internal: -.globl strtoul -.type strtoul, %function; -strtoul: -.weak __strtol_internal -.type __strtol_internal, %function; -__strtol_internal: -.globl strtol -.type strtol, %function; -strtol: -.globl strtoimax -.type strtoimax, %function; -strtoimax: -.weak __strtoimax_internal -.type __strtoimax_internal, %function; -__strtoimax_internal: -.globl strtoumax -.type strtoumax, %function; -strtoumax: -.weak __strtoumax_internal -.type __strtoumax_internal, %function; -__strtoumax_internal: -.globl wcstof -.type wcstof, %function; -wcstof: -.globl wcstod -.type wcstod, %function; -wcstod: -.globl wcstold -.type wcstold, %function; -wcstold: -.globl wcstoull -.type wcstoull, %function; -wcstoull: -.globl wcstoll -.type wcstoll, %function; -wcstoll: -.globl wcstoul -.type wcstoul, %function; -wcstoul: -.globl wcstol -.type wcstol, %function; -wcstol: -.globl wcstoimax -.type wcstoimax, %function; -wcstoimax: -.globl wcstoumax -.type wcstoumax, %function; -wcstoumax: -.globl bcmp -.type bcmp, %function; -bcmp: -.globl bcopy -.type bcopy, %function; -bcopy: -.globl bzero -.type bzero, %function; -bzero: -.globl explicit_bzero -.type explicit_bzero, %function; -explicit_bzero: -.globl index -.type index, %function; -index: -.globl memccpy -.type memccpy, %function; -memccpy: -.globl memchr -.type memchr, %function; -memchr: -.globl memcmp -.type memcmp, %function; -memcmp: -.globl memcpy -.type memcpy, %function; -memcpy: -.globl memmem -.type memmem, %function; -memmem: -.globl memmove -.type memmove, %function; -memmove: -.globl mempcpy -.type mempcpy, %function; -mempcpy: -.weak memrchr -.type memrchr, %function; -memrchr: -.globl memset -.type memset, %function; -memset: -.globl rindex -.type rindex, %function; -rindex: -.weak stpcpy -.type stpcpy, %function; -stpcpy: -.weak stpncpy -.type stpncpy, %function; -stpncpy: -.globl strcasecmp -.type strcasecmp, %function; -strcasecmp: -.globl __strcasecmp_l -.type __strcasecmp_l, %function; -__strcasecmp_l: -.weak strcasecmp_l -.type strcasecmp_l, %function; -strcasecmp_l: -.globl strcasestr -.type strcasestr, %function; -strcasestr: -.globl strcat -.type strcat, %function; -strcat: -.globl strchr -.type strchr, %function; -strchr: -.weak strchrnul -.type strchrnul, %function; -strchrnul: -.globl strcmp -.type strcmp, %function; -strcmp: -.globl strcpy -.type strcpy, %function; -strcpy: -.globl strcspn -.type strcspn, %function; -strcspn: -.globl strdup -.type strdup, %function; -strdup: -.weak __xpg_strerror_r -.type __xpg_strerror_r, %function; -__xpg_strerror_r: -.globl strerror_r -.type strerror_r, %function; -strerror_r: -.globl strlcat -.type strlcat, %function; -strlcat: -.globl strlcpy -.type strlcpy, %function; -strlcpy: -.globl strlen -.type strlen, %function; -strlen: -.globl strncasecmp -.type strncasecmp, %function; -strncasecmp: -.globl __strncasecmp_l -.type __strncasecmp_l, %function; -__strncasecmp_l: -.weak strncasecmp_l -.type strncasecmp_l, %function; -strncasecmp_l: -.globl strncat -.type strncat, %function; -strncat: -.globl strncmp -.type strncmp, %function; -strncmp: -.globl strncpy -.type strncpy, %function; -strncpy: -.globl strndup -.type strndup, %function; -strndup: -.globl strnlen -.type strnlen, %function; -strnlen: -.globl strpbrk -.type strpbrk, %function; -strpbrk: -.globl strrchr -.type strrchr, %function; -strrchr: -.globl strsep -.type strsep, %function; -strsep: -.globl strsignal -.type strsignal, %function; -strsignal: -.globl strspn -.type strspn, %function; -strspn: -.globl strstr -.type strstr, %function; -strstr: -.globl strtok -.type strtok, %function; -strtok: -.globl strtok_r -.type strtok_r, %function; -strtok_r: -.globl strverscmp -.type strverscmp, %function; -strverscmp: -.globl swab -.type swab, %function; -swab: -.globl wcpcpy -.type wcpcpy, %function; -wcpcpy: -.globl wcpncpy -.type wcpncpy, %function; -wcpncpy: -.globl wcscasecmp -.type wcscasecmp, %function; -wcscasecmp: -.globl wcscasecmp_l -.type wcscasecmp_l, %function; -wcscasecmp_l: -.globl wcscat -.type wcscat, %function; -wcscat: -.globl wcschr -.type wcschr, %function; -wcschr: -.globl wcscmp -.type wcscmp, %function; -wcscmp: -.globl wcscpy -.type wcscpy, %function; -wcscpy: -.globl wcscspn -.type wcscspn, %function; -wcscspn: -.globl wcsdup -.type wcsdup, %function; -wcsdup: -.globl wcslen -.type wcslen, %function; -wcslen: -.globl wcsncasecmp -.type wcsncasecmp, %function; -wcsncasecmp: -.globl wcsncasecmp_l -.type wcsncasecmp_l, %function; -wcsncasecmp_l: -.globl wcsncat -.type wcsncat, %function; -wcsncat: -.globl wcsncmp -.type wcsncmp, %function; -wcsncmp: -.globl wcsncpy -.type wcsncpy, %function; -wcsncpy: -.globl wcsnlen -.type wcsnlen, %function; -wcsnlen: -.globl wcspbrk -.type wcspbrk, %function; -wcspbrk: -.globl wcsrchr -.type wcsrchr, %function; -wcsrchr: -.globl wcsspn -.type wcsspn, %function; -wcsspn: -.globl wcsstr -.type wcsstr, %function; -wcsstr: -.globl wcstok -.type wcstok, %function; -wcstok: -.globl wcswcs -.type wcswcs, %function; -wcswcs: -.globl wmemchr -.type wmemchr, %function; -wmemchr: -.globl wmemcmp -.type wmemcmp, %function; -wmemcmp: -.globl wmemcpy -.type wmemcpy, %function; -wmemcpy: -.globl wmemmove -.type wmemmove, %function; -wmemmove: -.globl wmemset -.type wmemset, %function; -wmemset: -.globl mkdtemp -.type mkdtemp, %function; -mkdtemp: -.globl mkostemp -.type mkostemp, %function; -mkostemp: -.weak mkostemp64 -.type mkostemp64, %function; -mkostemp64: -.weak mkostemps64 -.type mkostemps64, %function; -mkostemps64: -.weak mkostemps -.type mkostemps, %function; -mkostemps: -.globl mkstemp -.type mkstemp, %function; -mkstemp: -.weak mkstemp64 -.type mkstemp64, %function; -mkstemp64: -.weak mkstemps64 -.type mkstemps64, %function; -mkstemps64: -.globl mkstemps -.type mkstemps, %function; -mkstemps: -.globl mktemp -.type mktemp, %function; -mktemp: -.globl cfgetospeed -.type cfgetospeed, %function; -cfgetospeed: -.globl cfgetispeed -.type cfgetispeed, %function; -cfgetispeed: -.globl cfmakeraw -.type cfmakeraw, %function; -cfmakeraw: -.weak cfsetspeed -.type cfsetspeed, %function; -cfsetspeed: -.globl cfsetospeed -.type cfsetospeed, %function; -cfsetospeed: -.globl cfsetispeed -.type cfsetispeed, %function; -cfsetispeed: -.globl tcdrain -.type tcdrain, %function; -tcdrain: -.globl tcflow -.type tcflow, %function; -tcflow: -.globl tcflush -.type tcflush, %function; -tcflush: -.globl tcgetattr -.type tcgetattr, %function; -tcgetattr: -.globl tcgetsid -.type tcgetsid, %function; -tcgetsid: -.globl tcgetwinsize -.type tcgetwinsize, %function; -tcgetwinsize: -.globl tcsendbreak -.type tcsendbreak, %function; -tcsendbreak: -.globl tcsetattr -.type tcsetattr, %function; -tcsetattr: -.globl tcsetwinsize -.type tcsetwinsize, %function; -tcsetwinsize: -.globl __tls_get_addr -.type __tls_get_addr, %function; -__tls_get_addr: -.globl call_once -.type call_once, %function; -call_once: -.globl cnd_broadcast -.type cnd_broadcast, %function; -cnd_broadcast: -.globl cnd_destroy -.type cnd_destroy, %function; -cnd_destroy: -.globl cnd_init -.type cnd_init, %function; -cnd_init: -.globl cnd_signal -.type cnd_signal, %function; -cnd_signal: -.globl cnd_timedwait -.type cnd_timedwait, %function; -cnd_timedwait: -.globl cnd_wait -.type cnd_wait, %function; -cnd_wait: -.globl mtx_destroy -.type mtx_destroy, %function; -mtx_destroy: -.globl mtx_init -.type mtx_init, %function; -mtx_init: -.globl mtx_lock -.type mtx_lock, %function; -mtx_lock: -.globl mtx_timedlock -.type mtx_timedlock, %function; -mtx_timedlock: -.globl mtx_trylock -.type mtx_trylock, %function; -mtx_trylock: -.globl mtx_unlock -.type mtx_unlock, %function; -mtx_unlock: .globl pthread_atfork .type pthread_atfork, %function; pthread_atfork: @@ -4306,30 +3620,6 @@ pthread_attr_getstack: .globl pthread_attr_getstacksize .type pthread_attr_getstacksize, %function; pthread_attr_getstacksize: -.globl pthread_barrierattr_getpshared -.type pthread_barrierattr_getpshared, %function; -pthread_barrierattr_getpshared: -.globl pthread_condattr_getclock -.type pthread_condattr_getclock, %function; -pthread_condattr_getclock: -.globl pthread_condattr_getpshared -.type pthread_condattr_getpshared, %function; -pthread_condattr_getpshared: -.globl pthread_mutexattr_getprotocol -.type pthread_mutexattr_getprotocol, %function; -pthread_mutexattr_getprotocol: -.globl pthread_mutexattr_getpshared -.type pthread_mutexattr_getpshared, %function; -pthread_mutexattr_getpshared: -.globl pthread_mutexattr_getrobust -.type pthread_mutexattr_getrobust, %function; -pthread_mutexattr_getrobust: -.globl pthread_mutexattr_gettype -.type pthread_mutexattr_gettype, %function; -pthread_mutexattr_gettype: -.globl pthread_rwlockattr_getpshared -.type pthread_rwlockattr_getpshared, %function; -pthread_rwlockattr_getpshared: .globl pthread_attr_init .type pthread_attr_init, %function; pthread_attr_init: @@ -4369,6 +3659,9 @@ pthread_barrier_wait: .globl pthread_barrierattr_destroy .type pthread_barrierattr_destroy, %function; pthread_barrierattr_destroy: +.globl pthread_barrierattr_getpshared +.type pthread_barrierattr_getpshared, %function; +pthread_barrierattr_getpshared: .globl pthread_barrierattr_init .type pthread_barrierattr_init, %function; pthread_barrierattr_init: @@ -4378,12 +3671,6 @@ pthread_barrierattr_setpshared: .globl pthread_cancel .type pthread_cancel, %function; pthread_cancel: -.globl _pthread_cleanup_push -.type _pthread_cleanup_push, %function; -_pthread_cleanup_push: -.globl _pthread_cleanup_pop -.type _pthread_cleanup_pop, %function; -_pthread_cleanup_pop: .globl pthread_cond_broadcast .type pthread_cond_broadcast, %function; pthread_cond_broadcast: @@ -4405,6 +3692,12 @@ pthread_cond_wait: .globl pthread_condattr_destroy .type pthread_condattr_destroy, %function; pthread_condattr_destroy: +.globl pthread_condattr_getclock +.type pthread_condattr_getclock, %function; +pthread_condattr_getclock: +.globl pthread_condattr_getpshared +.type pthread_condattr_getpshared, %function; +pthread_condattr_getpshared: .globl pthread_condattr_init .type pthread_condattr_init, %function; pthread_condattr_init: @@ -4414,24 +3707,24 @@ pthread_condattr_setclock: .globl pthread_condattr_setpshared .type pthread_condattr_setpshared, %function; pthread_condattr_setpshared: -.weak pthread_exit -.type pthread_exit, %function; -pthread_exit: .weak pthread_create .type pthread_create, %function; pthread_create: .weak pthread_detach .type pthread_detach, %function; pthread_detach: -.weak thrd_detach -.type thrd_detach, %function; -thrd_detach: .weak pthread_equal .type pthread_equal, %function; pthread_equal: -.weak thrd_equal -.type thrd_equal, %function; -thrd_equal: +.weak pthread_exit +.type pthread_exit, %function; +pthread_exit: +.globl pthread_getaffinity_np +.type pthread_getaffinity_np, %function; +pthread_getaffinity_np: +.globl pthread_getattr_default_np +.type pthread_getattr_default_np, %function; +pthread_getattr_default_np: .globl pthread_getattr_np .type pthread_getattr_np, %function; pthread_getattr_np: @@ -4450,18 +3743,9 @@ pthread_getschedparam: .weak pthread_getspecific .type pthread_getspecific, %function; pthread_getspecific: -.weak tss_get -.type tss_get, %function; -tss_get: .weak pthread_join .type pthread_join, %function; pthread_join: -WEAK64 pthread_timedjoin_np -.type pthread_timedjoin_np, %function; -pthread_timedjoin_np: -.weak pthread_tryjoin_np -.type pthread_tryjoin_np, %function; -pthread_tryjoin_np: .weak pthread_key_create .type pthread_key_create, %function; pthread_key_create: @@ -4501,6 +3785,18 @@ pthread_mutex_unlock: .globl pthread_mutexattr_destroy .type pthread_mutexattr_destroy, %function; pthread_mutexattr_destroy: +.globl pthread_mutexattr_getprotocol +.type pthread_mutexattr_getprotocol, %function; +pthread_mutexattr_getprotocol: +.globl pthread_mutexattr_getpshared +.type pthread_mutexattr_getpshared, %function; +pthread_mutexattr_getpshared: +.globl pthread_mutexattr_getrobust +.type pthread_mutexattr_getrobust, %function; +pthread_mutexattr_getrobust: +.globl pthread_mutexattr_gettype +.type pthread_mutexattr_gettype, %function; +pthread_mutexattr_gettype: .globl pthread_mutexattr_init .type pthread_mutexattr_init, %function; pthread_mutexattr_init: @@ -4549,24 +3845,24 @@ pthread_rwlock_wrlock: .globl pthread_rwlockattr_destroy .type pthread_rwlockattr_destroy, %function; pthread_rwlockattr_destroy: +.globl pthread_rwlockattr_getpshared +.type pthread_rwlockattr_getpshared, %function; +pthread_rwlockattr_getpshared: .globl pthread_rwlockattr_init .type pthread_rwlockattr_init, %function; pthread_rwlockattr_init: .globl pthread_rwlockattr_setpshared .type pthread_rwlockattr_setpshared, %function; pthread_rwlockattr_setpshared: -.weak thrd_current -.type thrd_current, %function; -thrd_current: .weak pthread_self .type pthread_self, %function; pthread_self: +.globl pthread_setaffinity_np +.type pthread_setaffinity_np, %function; +pthread_setaffinity_np: .globl pthread_setattr_default_np .type pthread_setattr_default_np, %function; pthread_setattr_default_np: -.globl pthread_getattr_default_np -.type pthread_getattr_default_np, %function; -pthread_getattr_default_np: .weak pthread_setcancelstate .type pthread_setcancelstate, %function; pthread_setcancelstate: @@ -4609,6 +3905,341 @@ pthread_spin_unlock: .weak pthread_testcancel .type pthread_testcancel, %function; pthread_testcancel: +WEAK64 pthread_timedjoin_np +.type pthread_timedjoin_np, %function; +pthread_timedjoin_np: +.weak pthread_tryjoin_np +.type pthread_tryjoin_np, %function; +pthread_tryjoin_np: +.globl ptrace +.type ptrace, %function; +ptrace: +.globl ptsname +.type ptsname, %function; +ptsname: +.weak ptsname_r +.type ptsname_r, %function; +ptsname_r: +.globl putc +.type putc, %function; +putc: +.globl putc_unlocked +.type putc_unlocked, %function; +putc_unlocked: +.globl putchar +.type putchar, %function; +putchar: +.globl putchar_unlocked +.type putchar_unlocked, %function; +putchar_unlocked: +.globl putenv +.type putenv, %function; +putenv: +.globl putgrent +.type putgrent, %function; +putgrent: +.globl putpwent +.type putpwent, %function; +putpwent: +.globl puts +.type puts, %function; +puts: +.globl putspent +.type putspent, %function; +putspent: +.weak pututline +.type pututline, %function; +pututline: +.globl pututxline +.type pututxline, %function; +pututxline: +.globl putw +.type putw, %function; +putw: +.globl putwc +.type putwc, %function; +putwc: +.weak putwc_unlocked +.type putwc_unlocked, %function; +putwc_unlocked: +.globl putwchar +.type putwchar, %function; +putwchar: +.weak putwchar_unlocked +.type putwchar_unlocked, %function; +putwchar_unlocked: +.globl pwrite +.type pwrite, %function; +pwrite: +.weak pwrite64 +.type pwrite64, %function; +pwrite64: +.globl pwritev +.type pwritev, %function; +pwritev: +.weak pwritev64 +.type pwritev64, %function; +pwritev64: +.globl qsort +.type qsort, %function; +qsort: +.weak qsort_r +.type qsort_r, %function; +qsort_r: +.globl quick_exit +.type quick_exit, %function; +quick_exit: +.globl quotactl +.type quotactl, %function; +quotactl: +.globl raise +.type raise, %function; +raise: +.globl rand +.type rand, %function; +rand: +.globl rand_r +.type rand_r, %function; +rand_r: +.globl random +.type random, %function; +random: +.globl read +.type read, %function; +read: +.globl readahead +.type readahead, %function; +readahead: +.globl readdir +.type readdir, %function; +readdir: +.weak readdir64 +.type readdir64, %function; +readdir64: +.weak readdir64_r +.type readdir64_r, %function; +readdir64_r: +.globl readdir_r +.type readdir_r, %function; +readdir_r: +.globl readlink +.type readlink, %function; +readlink: +.globl readlinkat +.type readlinkat, %function; +readlinkat: +.globl readv +.type readv, %function; +readv: +.globl realloc +.type realloc, %function; +realloc: +.globl reallocarray +.type reallocarray, %function; +reallocarray: +.globl realpath +.type realpath, %function; +realpath: +.globl reboot +.type reboot, %function; +reboot: +.globl recv +.type recv, %function; +recv: +.globl recvfrom +.type recvfrom, %function; +recvfrom: +.globl recvmmsg +.type recvmmsg, %function; +recvmmsg: +.globl recvmsg +.type recvmsg, %function; +recvmsg: +.globl regcomp +.type regcomp, %function; +regcomp: +.globl regerror +.type regerror, %function; +regerror: +.globl regexec +.type regexec, %function; +regexec: +.globl regfree +.type regfree, %function; +regfree: +.globl remainder +.type remainder, %function; +remainder: +.globl remainderf +.type remainderf, %function; +remainderf: +.globl remainderl +.type remainderl, %function; +remainderl: +.globl remap_file_pages +.type remap_file_pages, %function; +remap_file_pages: +.globl remove +.type remove, %function; +remove: +.globl removexattr +.type removexattr, %function; +removexattr: +.globl remque +.type remque, %function; +remque: +.globl remquo +.type remquo, %function; +remquo: +.globl remquof +.type remquof, %function; +remquof: +.globl remquol +.type remquol, %function; +remquol: +.globl rename +.type rename, %function; +rename: +.globl renameat +.type renameat, %function; +renameat: +.globl res_init +.type res_init, %function; +res_init: +.weak res_mkquery +.type res_mkquery, %function; +res_mkquery: +.globl res_query +.type res_query, %function; +res_query: +.globl res_querydomain +.type res_querydomain, %function; +res_querydomain: +.weak res_search +.type res_search, %function; +res_search: +.weak res_send +.type res_send, %function; +res_send: +.globl rewind +.type rewind, %function; +rewind: +.globl rewinddir +.type rewinddir, %function; +rewinddir: +.globl rindex +.type rindex, %function; +rindex: +.globl rint +.type rint, %function; +rint: +.globl rintf +.type rintf, %function; +rintf: +.globl rintl +.type rintl, %function; +rintl: +#if !defined(ARCH_mips) && !defined(ARCH_i386) && !defined(ARCH_x86_64) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) +.weak riscv_flush_icache +.type riscv_flush_icache, %function; +riscv_flush_icache: +#endif +.globl rmdir +.type rmdir, %function; +rmdir: +.globl round +.type round, %function; +round: +.globl roundf +.type roundf, %function; +roundf: +.globl roundl +.type roundl, %function; +roundl: +.globl sbrk +.type sbrk, %function; +sbrk: +.globl scalb +.type scalb, %function; +scalb: +.globl scalbf +.type scalbf, %function; +scalbf: +.globl scalbln +.type scalbln, %function; +scalbln: +.globl scalblnf +.type scalblnf, %function; +scalblnf: +.globl scalblnl +.type scalblnl, %function; +scalblnl: +.globl scalbn +.type scalbn, %function; +scalbn: +.globl scalbnf +.type scalbnf, %function; +scalbnf: +.globl scalbnl +.type scalbnl, %function; +scalbnl: +.globl scandir +.type scandir, %function; +scandir: +.weak scandir64 +.type scandir64, %function; +scandir64: +.globl scanf +.type scanf, %function; +scanf: +.globl sched_get_priority_max +.type sched_get_priority_max, %function; +sched_get_priority_max: +.globl sched_get_priority_min +.type sched_get_priority_min, %function; +sched_get_priority_min: +.globl sched_getaffinity +.type sched_getaffinity, %function; +sched_getaffinity: +.globl sched_getcpu +.type sched_getcpu, %function; +sched_getcpu: +.globl sched_getparam +.type sched_getparam, %function; +sched_getparam: +.globl sched_getscheduler +.type sched_getscheduler, %function; +sched_getscheduler: +.globl sched_rr_get_interval +.type sched_rr_get_interval, %function; +sched_rr_get_interval: +.globl sched_setaffinity +.type sched_setaffinity, %function; +sched_setaffinity: +.globl sched_setparam +.type sched_setparam, %function; +sched_setparam: +.globl sched_setscheduler +.type sched_setscheduler, %function; +sched_setscheduler: +.globl sched_yield +.type sched_yield, %function; +sched_yield: +.globl secure_getenv +.type secure_getenv, %function; +secure_getenv: +.globl seed48 +.type seed48, %function; +seed48: +.globl seekdir +.type seekdir, %function; +seekdir: +.globl select +.type select, %function; +select: +.globl sem_close +.type sem_close, %function; +sem_close: .globl sem_destroy .type sem_destroy, %function; sem_destroy: @@ -4621,9 +4252,6 @@ sem_init: .globl sem_open .type sem_open, %function; sem_open: -.globl sem_close -.type sem_close, %function; -sem_close: .globl sem_post .type sem_post, %function; sem_post: @@ -4639,9 +4267,672 @@ sem_unlink: .globl sem_wait .type sem_wait, %function; sem_wait: +.globl semctl +.type semctl, %function; +semctl: +.globl semget +.type semget, %function; +semget: +.globl semop +.type semop, %function; +semop: +.globl semtimedop +.type semtimedop, %function; +semtimedop: +.globl send +.type send, %function; +send: +.globl sendfile +.type sendfile, %function; +sendfile: +.weak sendfile64 +.type sendfile64, %function; +sendfile64: +.globl sendmmsg +.type sendmmsg, %function; +sendmmsg: +.globl sendmsg +.type sendmsg, %function; +sendmsg: +.globl sendto +.type sendto, %function; +sendto: +.globl setbuf +.type setbuf, %function; +setbuf: +.globl setbuffer +.type setbuffer, %function; +setbuffer: +.globl setdomainname +.type setdomainname, %function; +setdomainname: +.globl setegid +.type setegid, %function; +setegid: +.globl setenv +.type setenv, %function; +setenv: +.globl seteuid +.type seteuid, %function; +seteuid: +.globl setfsgid +.type setfsgid, %function; +setfsgid: +.globl setfsuid +.type setfsuid, %function; +setfsuid: +.globl setgid +.type setgid, %function; +setgid: +.globl setgrent +.type setgrent, %function; +setgrent: +.globl setgroups +.type setgroups, %function; +setgroups: +.globl sethostent +.type sethostent, %function; +sethostent: +.globl sethostname +.type sethostname, %function; +sethostname: +.globl setitimer +.type setitimer, %function; +setitimer: +.globl setjmp +.type setjmp, %function; +setjmp: +.globl setkey +.type setkey, %function; +setkey: +.globl setlinebuf +.type setlinebuf, %function; +setlinebuf: +.globl setlocale +.type setlocale, %function; +setlocale: +.globl setlogmask +.type setlogmask, %function; +setlogmask: +.globl setmntent +.type setmntent, %function; +setmntent: +.weak setnetent +.type setnetent, %function; +setnetent: +.globl setns +.type setns, %function; +setns: +.globl setpgid +.type setpgid, %function; +setpgid: +.globl setpgrp +.type setpgrp, %function; +setpgrp: +.globl setpriority +.type setpriority, %function; +setpriority: +.globl setprotoent +.type setprotoent, %function; +setprotoent: +.globl setpwent +.type setpwent, %function; +setpwent: +.globl setregid +.type setregid, %function; +setregid: +.globl setresgid +.type setresgid, %function; +setresgid: +.globl setresuid +.type setresuid, %function; +setresuid: +.globl setreuid +.type setreuid, %function; +setreuid: +.globl setrlimit +.type setrlimit, %function; +setrlimit: +.weak setrlimit64 +.type setrlimit64, %function; +setrlimit64: +.globl setservent +.type setservent, %function; +setservent: +.globl setsid +.type setsid, %function; +setsid: +.globl setsockopt +.type setsockopt, %function; +setsockopt: +.globl setspent +.type setspent, %function; +setspent: +.globl setstate +.type setstate, %function; +setstate: +.globl settimeofday +.type settimeofday, %function; +settimeofday: +.globl setuid +.type setuid, %function; +setuid: +.globl setusershell +.type setusershell, %function; +setusershell: +.weak setutent +.type setutent, %function; +setutent: +.globl setutxent +.type setutxent, %function; +setutxent: +.globl setvbuf +.type setvbuf, %function; +setvbuf: +.globl setxattr +.type setxattr, %function; +setxattr: +.globl shm_open +.type shm_open, %function; +shm_open: +.globl shm_unlink +.type shm_unlink, %function; +shm_unlink: +.globl shmat +.type shmat, %function; +shmat: +.globl shmctl +.type shmctl, %function; +shmctl: +.globl shmdt +.type shmdt, %function; +shmdt: +.globl shmget +.type shmget, %function; +shmget: +.globl shutdown +.type shutdown, %function; +shutdown: +.weak sigaction +.type sigaction, %function; +sigaction: +.globl sigaddset +.type sigaddset, %function; +sigaddset: +.globl sigaltstack +.type sigaltstack, %function; +sigaltstack: +.globl sigandset +.type sigandset, %function; +sigandset: +.globl sigdelset +.type sigdelset, %function; +sigdelset: +.globl sigemptyset +.type sigemptyset, %function; +sigemptyset: +.globl sigfillset +.type sigfillset, %function; +sigfillset: +.globl sighold +.type sighold, %function; +sighold: +.globl sigignore +.type sigignore, %function; +sigignore: +.globl siginterrupt +.type siginterrupt, %function; +siginterrupt: +.globl sigisemptyset +.type sigisemptyset, %function; +sigisemptyset: +.globl sigismember +.type sigismember, %function; +sigismember: +.globl siglongjmp +.type siglongjmp, %function; +siglongjmp: +.globl signal +.type signal, %function; +signal: +.globl signalfd +.type signalfd, %function; +signalfd: +.globl significand +.type significand, %function; +significand: +.globl significandf +.type significandf, %function; +significandf: +.globl sigorset +.type sigorset, %function; +sigorset: +.globl sigpause +.type sigpause, %function; +sigpause: +.globl sigpending +.type sigpending, %function; +sigpending: +.globl sigprocmask +.type sigprocmask, %function; +sigprocmask: +.globl sigqueue +.type sigqueue, %function; +sigqueue: +.globl sigrelse +.type sigrelse, %function; +sigrelse: +.globl sigset +.type sigset, %function; +sigset: +.globl sigsetjmp +.type sigsetjmp, %function; +sigsetjmp: +.globl sigsuspend +.type sigsuspend, %function; +sigsuspend: +.globl sigtimedwait +.type sigtimedwait, %function; +sigtimedwait: +.globl sigwait +.type sigwait, %function; +sigwait: +.globl sigwaitinfo +.type sigwaitinfo, %function; +sigwaitinfo: +.globl sin +.type sin, %function; +sin: +.globl sincos +.type sincos, %function; +sincos: +.globl sincosf +.type sincosf, %function; +sincosf: +.globl sincosl +.type sincosl, %function; +sincosl: +.globl sinf +.type sinf, %function; +sinf: +.globl sinh +.type sinh, %function; +sinh: +.globl sinhf +.type sinhf, %function; +sinhf: +.globl sinhl +.type sinhl, %function; +sinhl: +.globl sinl +.type sinl, %function; +sinl: +.globl sleep +.type sleep, %function; +sleep: +.globl snprintf +.type snprintf, %function; +snprintf: +.globl sockatmark +.type sockatmark, %function; +sockatmark: +.globl socket +.type socket, %function; +socket: +.globl socketpair +.type socketpair, %function; +socketpair: +.globl splice +.type splice, %function; +splice: +.globl sprintf +.type sprintf, %function; +sprintf: +.globl sqrt +.type sqrt, %function; +sqrt: +.globl sqrtf +.type sqrtf, %function; +sqrtf: +.globl sqrtl +.type sqrtl, %function; +sqrtl: +.globl srand +.type srand, %function; +srand: +.globl srand48 +.type srand48, %function; +srand48: +.globl srandom +.type srandom, %function; +srandom: +.globl sscanf +.type sscanf, %function; +sscanf: +.globl stat +.type stat, %function; +stat: +.weak stat64 +.type stat64, %function; +stat64: +.weak statfs +.type statfs, %function; +statfs: +.weak statfs64 +.type statfs64, %function; +statfs64: +.globl statvfs +.type statvfs, %function; +statvfs: +.weak statvfs64 +.type statvfs64, %function; +statvfs64: +.globl stime +.type stime, %function; +stime: +.weak stpcpy +.type stpcpy, %function; +stpcpy: +.weak stpncpy +.type stpncpy, %function; +stpncpy: +.globl strcasecmp +.type strcasecmp, %function; +strcasecmp: +.weak strcasecmp_l +.type strcasecmp_l, %function; +strcasecmp_l: +.globl strcasestr +.type strcasestr, %function; +strcasestr: +.globl strcat +.type strcat, %function; +strcat: +.globl strchr +.type strchr, %function; +strchr: +.weak strchrnul +.type strchrnul, %function; +strchrnul: +.globl strcmp +.type strcmp, %function; +strcmp: +.globl strcoll +.type strcoll, %function; +strcoll: +.weak strcoll_l +.type strcoll_l, %function; +strcoll_l: +.globl strcpy +.type strcpy, %function; +strcpy: +.globl strcspn +.type strcspn, %function; +strcspn: +.globl strdup +.type strdup, %function; +strdup: +.globl strerror +.type strerror, %function; +strerror: +.weak strerror_l +.type strerror_l, %function; +strerror_l: +.globl strerror_r +.type strerror_r, %function; +strerror_r: +.globl strfmon +.type strfmon, %function; +strfmon: +.globl strfmon_l +.type strfmon_l, %function; +strfmon_l: +.globl strftime +.type strftime, %function; +strftime: +.weak strftime_l +.type strftime_l, %function; +strftime_l: +.globl strlcat +.type strlcat, %function; +strlcat: +.globl strlcpy +.type strlcpy, %function; +strlcpy: +.globl strlen +.type strlen, %function; +strlen: +.globl strncasecmp +.type strncasecmp, %function; +strncasecmp: +.weak strncasecmp_l +.type strncasecmp_l, %function; +strncasecmp_l: +.globl strncat +.type strncat, %function; +strncat: +.globl strncmp +.type strncmp, %function; +strncmp: +.globl strncpy +.type strncpy, %function; +strncpy: +.globl strndup +.type strndup, %function; +strndup: +.globl strnlen +.type strnlen, %function; +strnlen: +.globl strpbrk +.type strpbrk, %function; +strpbrk: +.globl strptime +.type strptime, %function; +strptime: +.globl strrchr +.type strrchr, %function; +strrchr: +.globl strsep +.type strsep, %function; +strsep: +.globl strsignal +.type strsignal, %function; +strsignal: +.globl strspn +.type strspn, %function; +strspn: +.globl strstr +.type strstr, %function; +strstr: +.globl strtod +.type strtod, %function; +strtod: +.globl strtod_l +.type strtod_l, %function; +strtod_l: +.globl strtof +.type strtof, %function; +strtof: +.globl strtof_l +.type strtof_l, %function; +strtof_l: +.globl strtoimax +.type strtoimax, %function; +strtoimax: +.globl strtok +.type strtok, %function; +strtok: +.globl strtok_r +.type strtok_r, %function; +strtok_r: +.globl strtol +.type strtol, %function; +strtol: +.globl strtold +.type strtold, %function; +strtold: +.globl strtold_l +.type strtold_l, %function; +strtold_l: +.globl strtoll +.type strtoll, %function; +strtoll: +.globl strtoul +.type strtoul, %function; +strtoul: +.globl strtoull +.type strtoull, %function; +strtoull: +.globl strtoumax +.type strtoumax, %function; +strtoumax: +.globl strverscmp +.type strverscmp, %function; +strverscmp: +.globl strxfrm +.type strxfrm, %function; +strxfrm: +.weak strxfrm_l +.type strxfrm_l, %function; +strxfrm_l: +.globl swab +.type swab, %function; +swab: +.globl swapoff +.type swapoff, %function; +swapoff: +.globl swapon +.type swapon, %function; +swapon: +.globl swprintf +.type swprintf, %function; +swprintf: +.globl swscanf +.type swscanf, %function; +swscanf: +.globl symlink +.type symlink, %function; +symlink: +.globl symlinkat +.type symlinkat, %function; +symlinkat: +.globl sync +.type sync, %function; +sync: +.globl sync_file_range +.type sync_file_range, %function; +sync_file_range: +.globl syncfs +.type syncfs, %function; +syncfs: +.globl syscall +.type syscall, %function; +syscall: +.globl sysconf +.type sysconf, %function; +sysconf: +.weak sysinfo +.type sysinfo, %function; +sysinfo: +.globl syslog +.type syslog, %function; +syslog: +.globl system +.type system, %function; +system: +.globl tan +.type tan, %function; +tan: +.globl tanf +.type tanf, %function; +tanf: +.globl tanh +.type tanh, %function; +tanh: +.globl tanhf +.type tanhf, %function; +tanhf: +.globl tanhl +.type tanhl, %function; +tanhl: +.globl tanl +.type tanl, %function; +tanl: +.globl tcdrain +.type tcdrain, %function; +tcdrain: +.globl tcflow +.type tcflow, %function; +tcflow: +.globl tcflush +.type tcflush, %function; +tcflush: +.globl tcgetattr +.type tcgetattr, %function; +tcgetattr: +.globl tcgetpgrp +.type tcgetpgrp, %function; +tcgetpgrp: +.globl tcgetsid +.type tcgetsid, %function; +tcgetsid: +.globl tcgetwinsize +.type tcgetwinsize, %function; +tcgetwinsize: +.globl tcsendbreak +.type tcsendbreak, %function; +tcsendbreak: +.globl tcsetattr +.type tcsetattr, %function; +tcsetattr: +.globl tcsetpgrp +.type tcsetpgrp, %function; +tcsetpgrp: +.globl tcsetwinsize +.type tcsetwinsize, %function; +tcsetwinsize: +.globl tdelete +.type tdelete, %function; +tdelete: +.globl tdestroy +.type tdestroy, %function; +tdestroy: +.globl tee +.type tee, %function; +tee: +.globl telldir +.type telldir, %function; +telldir: +.globl tempnam +.type tempnam, %function; +tempnam: +.globl textdomain +.type textdomain, %function; +textdomain: +.globl tfind +.type tfind, %function; +tfind: +.globl tgamma +.type tgamma, %function; +tgamma: +.globl tgammaf +.type tgammaf, %function; +tgammaf: +.globl tgammal +.type tgammal, %function; +tgammal: .globl thrd_create .type thrd_create, %function; thrd_create: +.weak thrd_current +.type thrd_current, %function; +thrd_current: +.weak thrd_detach +.type thrd_detach, %function; +thrd_detach: +.weak thrd_equal +.type thrd_equal, %function; +thrd_equal: .globl thrd_exit .type thrd_exit, %function; thrd_exit: @@ -4654,87 +4945,6 @@ thrd_sleep: .globl thrd_yield .type thrd_yield, %function; thrd_yield: -.globl tss_create -.type tss_create, %function; -tss_create: -.globl tss_delete -.type tss_delete, %function; -tss_delete: -.globl tss_set -.type tss_set, %function; -tss_set: -.weak tzset -.type tzset, %function; -tzset: -.globl asctime -.type asctime, %function; -asctime: -.weak asctime_r -.type asctime_r, %function; -asctime_r: -.globl clock -.type clock, %function; -clock: -.globl clock_getcpuclockid -.type clock_getcpuclockid, %function; -clock_getcpuclockid: -.globl clock_getres -.type clock_getres, %function; -clock_getres: -WEAK64 clock_gettime -.type clock_gettime, %function; -clock_gettime: -WEAK64 clock_nanosleep -.type clock_nanosleep, %function; -clock_nanosleep: -.globl clock_settime -.type clock_settime, %function; -clock_settime: -.globl ctime -.type ctime, %function; -ctime: -.globl ctime_r -.type ctime_r, %function; -ctime_r: -.globl difftime -.type difftime, %function; -difftime: -.globl ftime -.type ftime, %function; -ftime: -.globl getdate -.type getdate, %function; -getdate: -.globl gettimeofday -.type gettimeofday, %function; -gettimeofday: -.globl gmtime -.type gmtime, %function; -gmtime: -WEAK64 gmtime_r -.type gmtime_r, %function; -gmtime_r: -.globl localtime -.type localtime, %function; -localtime: -WEAK64 localtime_r -.type localtime_r, %function; -localtime_r: -.globl mktime -.type mktime, %function; -mktime: -.globl nanosleep -.type nanosleep, %function; -nanosleep: -.weak strftime_l -.type strftime_l, %function; -strftime_l: -.globl strftime -.type strftime, %function; -strftime: -.globl strptime -.type strptime, %function; -strptime: .globl time .type time, %function; time: @@ -4756,656 +4966,471 @@ timer_gettime: .globl timer_settime .type timer_settime, %function; timer_settime: +.globl timerfd_create +.type timerfd_create, %function; +timerfd_create: +.globl timerfd_gettime +.type timerfd_gettime, %function; +timerfd_gettime: +.globl timerfd_settime +.type timerfd_settime, %function; +timerfd_settime: .globl times .type times, %function; times: .globl timespec_get .type timespec_get, %function; timespec_get: -.globl utime -.type utime, %function; -utime: -.globl __wcsftime_l -.type __wcsftime_l, %function; -__wcsftime_l: -.weak wcsftime_l -.type wcsftime_l, %function; -wcsftime_l: -.globl wcsftime -.type wcsftime, %function; -wcsftime: -.globl _exit -.type _exit, %function; -_exit: -.globl access -.type access, %function; -access: -.globl acct -.type acct, %function; -acct: -.globl alarm -.type alarm, %function; -alarm: -.globl chdir -.type chdir, %function; -chdir: -.globl chown -.type chown, %function; -chown: -.globl close -.type close, %function; -close: -.globl ctermid -.type ctermid, %function; -ctermid: -.globl dup -.type dup, %function; -dup: -.globl dup2 -.type dup2, %function; -dup2: -.weak dup3 -.type dup3, %function; -dup3: -.globl faccessat -.type faccessat, %function; -faccessat: -.globl fchdir -.type fchdir, %function; -fchdir: -.globl fchown -.type fchown, %function; -fchown: -.globl fchownat -.type fchownat, %function; -fchownat: -.globl fdatasync -.type fdatasync, %function; -fdatasync: -.globl fsync -.type fsync, %function; -fsync: -.globl ftruncate -.type ftruncate, %function; -ftruncate: -.weak ftruncate64 -.type ftruncate64, %function; -ftruncate64: -.globl getcwd -.type getcwd, %function; -getcwd: -.globl getegid -.type getegid, %function; -getegid: -.globl geteuid -.type geteuid, %function; -geteuid: -.globl getgid -.type getgid, %function; -getgid: -.globl getgroups -.type getgroups, %function; -getgroups: -.globl gethostname -.type gethostname, %function; -gethostname: -.globl getlogin -.type getlogin, %function; -getlogin: -.globl getlogin_r -.type getlogin_r, %function; -getlogin_r: -.globl getpgid -.type getpgid, %function; -getpgid: -.globl getpgrp -.type getpgrp, %function; -getpgrp: -.globl getpid -.type getpid, %function; -getpid: -.globl getppid -.type getppid, %function; -getppid: -.globl getsid -.type getsid, %function; -getsid: -.globl getuid -.type getuid, %function; -getuid: -.globl isatty -.type isatty, %function; -isatty: -.globl lchown -.type lchown, %function; -lchown: -.globl link -.type link, %function; -link: -.globl linkat -.type linkat, %function; -linkat: -.weak lseek -.type lseek, %function; -lseek: -.weak lseek64 -.type lseek64, %function; -lseek64: -.globl nice -.type nice, %function; -nice: -.globl pause -.type pause, %function; -pause: -.globl pipe -.type pipe, %function; -pipe: -.globl pipe2 -.type pipe2, %function; -pipe2: -.globl posix_close -.type posix_close, %function; -posix_close: -.weak pread64 -.type pread64, %function; -pread64: -.globl pread -.type pread, %function; -pread: -.globl preadv -.type preadv, %function; -preadv: -.weak preadv64 -.type preadv64, %function; -preadv64: -.globl pwrite -.type pwrite, %function; -pwrite: -.weak pwrite64 -.type pwrite64, %function; -pwrite64: -.globl pwritev -.type pwritev, %function; -pwritev: -.weak pwritev64 -.type pwritev64, %function; -pwritev64: -.globl read -.type read, %function; -read: -.globl readlink -.type readlink, %function; -readlink: -.globl readlinkat -.type readlinkat, %function; -readlinkat: -.globl readv -.type readv, %function; -readv: -.globl renameat -.type renameat, %function; -renameat: -.globl rmdir -.type rmdir, %function; -rmdir: -.globl setegid -.type setegid, %function; -setegid: -.globl seteuid -.type seteuid, %function; -seteuid: -.globl setgid -.type setgid, %function; -setgid: -.globl setpgid -.type setpgid, %function; -setpgid: -.globl setpgrp -.type setpgrp, %function; -setpgrp: -.globl setregid -.type setregid, %function; -setregid: -.globl setresgid -.type setresgid, %function; -setresgid: -.globl setresuid -.type setresuid, %function; -setresuid: -.globl setreuid -.type setreuid, %function; -setreuid: -.globl setsid -.type setsid, %function; -setsid: -.globl setuid -.type setuid, %function; -setuid: -.globl sleep -.type sleep, %function; -sleep: -.globl symlink -.type symlink, %function; -symlink: -.globl symlinkat -.type symlinkat, %function; -symlinkat: -.globl sync -.type sync, %function; -sync: -.globl tcgetpgrp -.type tcgetpgrp, %function; -tcgetpgrp: -.globl tcsetpgrp -.type tcsetpgrp, %function; -tcsetpgrp: -.weak truncate64 -.type truncate64, %function; -truncate64: +.globl tmpfile +.type tmpfile, %function; +tmpfile: +.weak tmpfile64 +.type tmpfile64, %function; +tmpfile64: +.globl tmpnam +.type tmpnam, %function; +tmpnam: +.globl toascii +.type toascii, %function; +toascii: +.globl tolower +.type tolower, %function; +tolower: +.weak tolower_l +.type tolower_l, %function; +tolower_l: +.globl toupper +.type toupper, %function; +toupper: +.weak toupper_l +.type toupper_l, %function; +toupper_l: +.globl towctrans +.type towctrans, %function; +towctrans: +.weak towctrans_l +.type towctrans_l, %function; +towctrans_l: +.globl towlower +.type towlower, %function; +towlower: +.weak towlower_l +.type towlower_l, %function; +towlower_l: +.globl towupper +.type towupper, %function; +towupper: +.weak towupper_l +.type towupper_l, %function; +towupper_l: +.globl trunc +.type trunc, %function; +trunc: .globl truncate .type truncate, %function; truncate: +.weak truncate64 +.type truncate64, %function; +truncate64: +.globl truncf +.type truncf, %function; +truncf: +.globl truncl +.type truncl, %function; +truncl: +.globl tsearch +.type tsearch, %function; +tsearch: +.globl tss_create +.type tss_create, %function; +tss_create: +.globl tss_delete +.type tss_delete, %function; +tss_delete: +.weak tss_get +.type tss_get, %function; +tss_get: +.globl tss_set +.type tss_set, %function; +tss_set: .globl ttyname .type ttyname, %function; ttyname: .globl ttyname_r .type ttyname_r, %function; ttyname_r: +.globl twalk +.type twalk, %function; +twalk: +.weak tzset +.type tzset, %function; +tzset: .globl ualarm .type ualarm, %function; ualarm: +.globl ulckpwdf +.type ulckpwdf, %function; +ulckpwdf: +.globl ulimit +.type ulimit, %function; +ulimit: +.globl umask +.type umask, %function; +umask: +.globl umount +.type umount, %function; +umount: +.globl umount2 +.type umount2, %function; +umount2: +.globl uname +.type uname, %function; +uname: +.globl ungetc +.type ungetc, %function; +ungetc: +.globl ungetwc +.type ungetwc, %function; +ungetwc: .globl unlink .type unlink, %function; unlink: .globl unlinkat .type unlinkat, %function; unlinkat: +.globl unlockpt +.type unlockpt, %function; +unlockpt: +.globl unsetenv +.type unsetenv, %function; +unsetenv: +.globl unshare +.type unshare, %function; +unshare: +.weak updwtmp +.type updwtmp, %function; +updwtmp: +.globl updwtmpx +.type updwtmpx, %function; +updwtmpx: +.weak uselocale +.type uselocale, %function; +uselocale: .globl usleep .type usleep, %function; usleep: +.globl utime +.type utime, %function; +utime: +.globl utimensat +.type utimensat, %function; +utimensat: +.globl utimes +.type utimes, %function; +utimes: +.weak utmpname +.type utmpname, %function; +utmpname: +.weak utmpxname +.type utmpxname, %function; +utmpxname: +.globl valloc +.type valloc, %function; +valloc: +.globl vasprintf +.type vasprintf, %function; +vasprintf: +.globl vdprintf +.type vdprintf, %function; +vdprintf: +.globl verr +.type verr, %function; +verr: +.globl verrx +.type verrx, %function; +verrx: +.globl versionsort +.type versionsort, %function; +versionsort: +.weak versionsort64 +.type versionsort64, %function; +versionsort64: +.globl vfork +.type vfork, %function; +vfork: +.globl vfprintf +.type vfprintf, %function; +vfprintf: +.globl vfscanf +.type vfscanf, %function; +vfscanf: +.globl vfwprintf +.type vfwprintf, %function; +vfwprintf: +.globl vfwscanf +.type vfwscanf, %function; +vfwscanf: +.globl vhangup +.type vhangup, %function; +vhangup: +.globl vmsplice +.type vmsplice, %function; +vmsplice: +.globl vprintf +.type vprintf, %function; +vprintf: +.globl vscanf +.type vscanf, %function; +vscanf: +.globl vsnprintf +.type vsnprintf, %function; +vsnprintf: +.globl vsprintf +.type vsprintf, %function; +vsprintf: +.globl vsscanf +.type vsscanf, %function; +vsscanf: +.globl vswprintf +.type vswprintf, %function; +vswprintf: +.globl vswscanf +.type vswscanf, %function; +vswscanf: +.weak vsyslog +.type vsyslog, %function; +vsyslog: +.globl vwarn +.type vwarn, %function; +vwarn: +.globl vwarnx +.type vwarnx, %function; +vwarnx: +.globl vwprintf +.type vwprintf, %function; +vwprintf: +.globl vwscanf +.type vwscanf, %function; +vwscanf: +.globl wait +.type wait, %function; +wait: +.globl wait3 +.type wait3, %function; +wait3: +.globl wait4 +.type wait4, %function; +wait4: +.globl waitid +.type waitid, %function; +waitid: +.globl waitpid +.type waitpid, %function; +waitpid: +.globl warn +.type warn, %function; +warn: +.globl warnx +.type warnx, %function; +warnx: +.globl wcpcpy +.type wcpcpy, %function; +wcpcpy: +.globl wcpncpy +.type wcpncpy, %function; +wcpncpy: +.globl wcrtomb +.type wcrtomb, %function; +wcrtomb: +.globl wcscasecmp +.type wcscasecmp, %function; +wcscasecmp: +.globl wcscasecmp_l +.type wcscasecmp_l, %function; +wcscasecmp_l: +.globl wcscat +.type wcscat, %function; +wcscat: +.globl wcschr +.type wcschr, %function; +wcschr: +.globl wcscmp +.type wcscmp, %function; +wcscmp: +.globl wcscoll +.type wcscoll, %function; +wcscoll: +.weak wcscoll_l +.type wcscoll_l, %function; +wcscoll_l: +.globl wcscpy +.type wcscpy, %function; +wcscpy: +.globl wcscspn +.type wcscspn, %function; +wcscspn: +.globl wcsdup +.type wcsdup, %function; +wcsdup: +.globl wcsftime +.type wcsftime, %function; +wcsftime: +.weak wcsftime_l +.type wcsftime_l, %function; +wcsftime_l: +.globl wcslen +.type wcslen, %function; +wcslen: +.globl wcsncasecmp +.type wcsncasecmp, %function; +wcsncasecmp: +.globl wcsncasecmp_l +.type wcsncasecmp_l, %function; +wcsncasecmp_l: +.globl wcsncat +.type wcsncat, %function; +wcsncat: +.globl wcsncmp +.type wcsncmp, %function; +wcsncmp: +.globl wcsncpy +.type wcsncpy, %function; +wcsncpy: +.globl wcsnlen +.type wcsnlen, %function; +wcsnlen: +.globl wcsnrtombs +.type wcsnrtombs, %function; +wcsnrtombs: +.globl wcspbrk +.type wcspbrk, %function; +wcspbrk: +.globl wcsrchr +.type wcsrchr, %function; +wcsrchr: +.globl wcsrtombs +.type wcsrtombs, %function; +wcsrtombs: +.globl wcsspn +.type wcsspn, %function; +wcsspn: +.globl wcsstr +.type wcsstr, %function; +wcsstr: +.globl wcstod +.type wcstod, %function; +wcstod: +.globl wcstof +.type wcstof, %function; +wcstof: +.globl wcstoimax +.type wcstoimax, %function; +wcstoimax: +.globl wcstok +.type wcstok, %function; +wcstok: +.globl wcstol +.type wcstol, %function; +wcstol: +.globl wcstold +.type wcstold, %function; +wcstold: +.globl wcstoll +.type wcstoll, %function; +wcstoll: +.globl wcstombs +.type wcstombs, %function; +wcstombs: +.globl wcstoul +.type wcstoul, %function; +wcstoul: +.globl wcstoull +.type wcstoull, %function; +wcstoull: +.globl wcstoumax +.type wcstoumax, %function; +wcstoumax: +.globl wcswcs +.type wcswcs, %function; +wcswcs: +.globl wcswidth +.type wcswidth, %function; +wcswidth: +.globl wcsxfrm +.type wcsxfrm, %function; +wcsxfrm: +.weak wcsxfrm_l +.type wcsxfrm_l, %function; +wcsxfrm_l: +.globl wctob +.type wctob, %function; +wctob: +.globl wctomb +.type wctomb, %function; +wctomb: +.globl wctrans +.type wctrans, %function; +wctrans: +.weak wctrans_l +.type wctrans_l, %function; +wctrans_l: +.globl wctype +.type wctype, %function; +wctype: +.weak wctype_l +.type wctype_l, %function; +wctype_l: +.globl wcwidth +.type wcwidth, %function; +wcwidth: +.globl wmemchr +.type wmemchr, %function; +wmemchr: +.globl wmemcmp +.type wmemcmp, %function; +wmemcmp: +.globl wmemcpy +.type wmemcpy, %function; +wmemcpy: +.globl wmemmove +.type wmemmove, %function; +wmemmove: +.globl wmemset +.type wmemset, %function; +wmemset: +.globl wordexp +.type wordexp, %function; +wordexp: +.globl wordfree +.type wordfree, %function; +wordfree: +.globl wprintf +.type wprintf, %function; +wprintf: .globl write .type write, %function; write: .globl writev .type writev, %function; writev: -.globl _dlstart -.type _dlstart, %function; -_dlstart: -.weak _dl_debug_state -.type _dl_debug_state, %function; -_dl_debug_state: -.globl __dls2b -.type __dls2b, %function; -__dls2b: -.globl __dls3 -.type __dls3, %function; -__dls3: -.globl dlopen -.type dlopen, %function; -dlopen: -.globl dladdr -.type dladdr, %function; -dladdr: -.globl dl_iterate_phdr -.type dl_iterate_phdr, %function; -dl_iterate_phdr: -.data.rel.ro -.globl stderr -.type stderr, %object; -.size stderr, PTR_SIZE_BYTES -stderr: -.globl stdin -.type stdin, %object; -.size stdin, PTR_SIZE_BYTES -stdin: -.globl stdout -.type stdout, %object; -.size stdout, PTR_SIZE_BYTES -stdout: -.data -.globl optind -.type optind, %object; -.size optind, 4 -optind: -.globl opterr -.type opterr, %object; -.size opterr, 4 -opterr: -.globl _dl_debug_addr -.type _dl_debug_addr, %object; -.size _dl_debug_addr, PTR_SIZE_BYTES -_dl_debug_addr: -.bss -.weak ___environ -.type ___environ, %object; -.size ___environ, PTR_SIZE_BYTES -___environ: -.globl __environ -.type __environ, %object; -.size __environ, PTR_SIZE_BYTES -__environ: -.weak environ -.type environ, %object; -.size environ, PTR_SIZE_BYTES -environ: -.weak _environ -.type _environ, %object; -.size _environ, PTR_SIZE_BYTES -_environ: -.globl __stack_chk_guard -.type __stack_chk_guard, %object; -.size __stack_chk_guard, PTR_SIZE_BYTES -__stack_chk_guard: -.weak program_invocation_short_name -.type program_invocation_short_name, %object; -.size program_invocation_short_name, PTR_SIZE_BYTES -program_invocation_short_name: -.globl __progname -.type __progname, %object; -.size __progname, PTR_SIZE_BYTES -__progname: -.weak program_invocation_name -.type program_invocation_name, %object; -.size program_invocation_name, PTR_SIZE_BYTES -program_invocation_name: -.globl __progname_full -.type __progname_full, %object; -.size __progname_full, PTR_SIZE_BYTES -__progname_full: -.globl __signgam -.type __signgam, %object; -.size __signgam, 4 -__signgam: -.weak signgam -.type signgam, %object; -.size signgam, 4 -signgam: -.globl __optreset -.type __optreset, %object; -.size __optreset, 4 -__optreset: -.weak optreset -.type optreset, %object; -.size optreset, 4 -optreset: -.globl __optpos -.type __optpos, %object; -.size __optpos, 4 -__optpos: -.globl optarg -.type optarg, %object; -.size optarg, PTR_SIZE_BYTES -optarg: -.globl optopt -.type optopt, %object; -.size optopt, 4 -optopt: -.globl h_errno -.type h_errno, %object; -.size h_errno, 4 -h_errno: -.globl __timezone -.type __timezone, %object; -.size __timezone, PTR_SIZE_BYTES -__timezone: -.weak timezone -.type timezone, %object; -.size timezone, PTR_SIZE_BYTES -timezone: -.globl __daylight -.type __daylight, %object; -.size __daylight, 4 -__daylight: -.weak daylight -.type daylight, %object; -.size daylight, 4 -daylight: -.globl __tzname -.type __tzname, %object; -.size __tzname, PTR2_SIZE_BYTES -__tzname: -.weak tzname -.type tzname, %object; -.size tzname, PTR2_SIZE_BYTES -tzname: -.globl getdate_err -.type getdate_err, %object; -.size getdate_err, 4 -getdate_err: -.text -#ifdef PTR32 -.globl __aio_suspend_time64 -.type __aio_suspend_time64, %function; -__aio_suspend_time64: -.globl __semtimedop_time64 -.type __semtimedop_time64, %function; -__semtimedop_time64: -.globl __dlsym_time64 -.type __dlsym_time64, %function; -__dlsym_time64: -.globl __futimes_time64 -.type __futimes_time64, %function; -__futimes_time64: -.globl __lutimes_time64 -.type __lutimes_time64, %function; -__lutimes_time64: -.globl __adjtime64 -.type __adjtime64, %function; -__adjtime64: -.globl __adjtimex_time64 -.type __adjtimex_time64, %function; -__adjtimex_time64: -#endif -#if !defined(ARCH_riscv64) && !defined(ARCH_i386) && !defined(ARCH_x86_64) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) -.globl _flush_cache -.type _flush_cache, %function; -_flush_cache: -#endif -#if !defined(ARCH_riscv64) && !defined(ARCH_i386) && !defined(ARCH_x86_64) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) -.weak cacheflush -.type cacheflush, %function; -cacheflush: -#endif -#if !defined(ARCH_riscv64) && !defined(ARCH_i386) && !defined(ARCH_x86_64) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) -.globl __cachectl -.type __cachectl, %function; -__cachectl: -#endif -#if !defined(ARCH_riscv64) && !defined(ARCH_i386) && !defined(ARCH_x86_64) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) -.weak cachectl -.type cachectl, %function; -cachectl: -#endif -#ifdef PTR32 -.globl __clock_adjtime64 -.type __clock_adjtime64, %function; -__clock_adjtime64: -#endif -#if !defined(ARCH_riscv64) && !defined(ARCH_aarch64) -.globl ioperm -.type ioperm, %function; -ioperm: -#endif -#if !defined(ARCH_riscv64) && !defined(ARCH_aarch64) -.globl iopl -.type iopl, %function; -iopl: -#endif -#ifdef PTR32 -.globl __ppoll_time64 -.type __ppoll_time64, %function; -__ppoll_time64: -.globl __settimeofday_time64 -.type __settimeofday_time64, %function; -__settimeofday_time64: -.globl __stime64 -.type __stime64, %function; -__stime64: -.globl __timerfd_settime64 -.type __timerfd_settime64, %function; -__timerfd_settime64: -.globl __timerfd_gettime64 -.type __timerfd_gettime64, %function; -__timerfd_gettime64: -.globl __utimes_time64 -.type __utimes_time64, %function; -__utimes_time64: -.globl __wait3_time64 -.type __wait3_time64, %function; -__wait3_time64: -.globl __wait4_time64 -.type __wait4_time64, %function; -__wait4_time64: -.globl __getrusage_time64 -.type __getrusage_time64, %function; -__getrusage_time64: -.globl __mq_timedreceive_time64 -.type __mq_timedreceive_time64, %function; -__mq_timedreceive_time64: -.globl __mq_timedsend_time64 -.type __mq_timedsend_time64, %function; -__mq_timedsend_time64: -.globl __recvmmsg_time64 -.type __recvmmsg_time64, %function; -__recvmmsg_time64: -.globl __sched_rr_get_interval_time64 -.type __sched_rr_get_interval_time64, %function; -__sched_rr_get_interval_time64: -.globl __pselect_time64 -.type __pselect_time64, %function; -__pselect_time64: -.globl __select_time64 -.type __select_time64, %function; -__select_time64: -.globl __getitimer_time64 -.type __getitimer_time64, %function; -__getitimer_time64: -.globl __setitimer_time64 -.type __setitimer_time64, %function; -__setitimer_time64: -.globl __sigtimedwait_time64 -.type __sigtimedwait_time64, %function; -__sigtimedwait_time64: -.globl __fstat_time64 -.type __fstat_time64, %function; -__fstat_time64: -.globl __fstatat_time64 -.type __fstatat_time64, %function; -__fstatat_time64: -.globl __futimens_time64 -.type __futimens_time64, %function; -__futimens_time64: -.weak __futimesat_time64 -.type __futimesat_time64, %function; -__futimesat_time64: -.globl __lstat_time64 -.type __lstat_time64, %function; -__lstat_time64: -.globl __stat_time64 -.type __stat_time64, %function; -__stat_time64: -.globl __utimensat_time64 -.type __utimensat_time64, %function; -__utimensat_time64: -.globl __cnd_timedwait_time64 -.type __cnd_timedwait_time64, %function; -__cnd_timedwait_time64: -.globl __mtx_timedlock_time64 -.type __mtx_timedlock_time64, %function; -__mtx_timedlock_time64: -.weak __pthread_cond_timedwait_time64 -.type __pthread_cond_timedwait_time64, %function; -__pthread_cond_timedwait_time64: -.weak __pthread_timedjoin_np_time64 -.type __pthread_timedjoin_np_time64, %function; -__pthread_timedjoin_np_time64: -.weak __pthread_mutex_timedlock_time64 -.type __pthread_mutex_timedlock_time64, %function; -__pthread_mutex_timedlock_time64: -.weak __pthread_rwlock_timedrdlock_time64 -.type __pthread_rwlock_timedrdlock_time64, %function; -__pthread_rwlock_timedrdlock_time64: -.weak __pthread_rwlock_timedwrlock_time64 -.type __pthread_rwlock_timedwrlock_time64, %function; -__pthread_rwlock_timedwrlock_time64: -.globl __sem_timedwait_time64 -.type __sem_timedwait_time64, %function; -__sem_timedwait_time64: -.globl __thrd_sleep_time64 -.type __thrd_sleep_time64, %function; -__thrd_sleep_time64: -.globl __clock_getres_time64 -.type __clock_getres_time64, %function; -__clock_getres_time64: -.weak __clock_gettime64 -.type __clock_gettime64, %function; -__clock_gettime64: -.weak __clock_nanosleep_time64 -.type __clock_nanosleep_time64, %function; -__clock_nanosleep_time64: -.globl __clock_settime64 -.type __clock_settime64, %function; -__clock_settime64: -.globl __ctime64 -.type __ctime64, %function; -__ctime64: -.globl __ctime64_r -.type __ctime64_r, %function; -__ctime64_r: -.globl __difftime64 -.type __difftime64, %function; -__difftime64: -.globl __ftime64 -.type __ftime64, %function; -__ftime64: -.globl __gettimeofday_time64 -.type __gettimeofday_time64, %function; -__gettimeofday_time64: -.globl __gmtime64 -.type __gmtime64, %function; -__gmtime64: -.weak __gmtime64_r -.type __gmtime64_r, %function; -__gmtime64_r: -.globl __localtime64 -.type __localtime64, %function; -__localtime64: -.weak __localtime64_r -.type __localtime64_r, %function; -__localtime64_r: -.globl __mktime64 -.type __mktime64, %function; -__mktime64: -.globl __nanosleep_time64 -.type __nanosleep_time64, %function; -__nanosleep_time64: -.globl __time64 -.type __time64, %function; -__time64: -.globl __timegm_time64 -.type __timegm_time64, %function; -__timegm_time64: -.globl __timer_gettime64 -.type __timer_gettime64, %function; -__timer_gettime64: -.globl __timer_settime64 -.type __timer_settime64, %function; -__timer_settime64: -.globl __timespec_get_time64 -.type __timespec_get_time64, %function; -__timespec_get_time64: -.globl __utime64 -.type __utime64, %function; -__utime64: -#endif -#if !defined(ARCH_riscv64) && !defined(ARCH_mips) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) -.globl arch_prctl -.type arch_prctl, %function; -arch_prctl: -#endif -#if !defined(ARCH_riscv64) && !defined(ARCH_mips) && !defined(ARCH_x86_64) && !defined(ARCH_powerpc) && !defined(ARCH_powerpc64) && !defined(ARCH_aarch64) -.globl ___tls_get_addr -.type ___tls_get_addr, %function; -___tls_get_addr: -#endif +.globl wscanf +.type wscanf, %function; +wscanf: +.globl y0 +.type y0, %function; +y0: +.globl y0f +.type y0f, %function; +y0f: +.globl y1 +.type y1, %function; +y1: +.globl y1f +.type y1f, %function; +y1f: +.globl yn +.type yn, %function; +yn: +.globl ynf +.type ynf, %function; +ynf: diff --git a/tools/gen_stubs.zig b/tools/gen_stubs.zig index c77818fdb4..a153d0f2eb 100644 --- a/tools/gen_stubs.zig +++ b/tools/gen_stubs.zig @@ -236,6 +236,36 @@ pub fn main() !void { \\ ); + // Sort the symbols for deterministic output and cleaner vcs diffs. + const SymTableSort = struct { + sections: *const std.StringArrayHashMap(void), + sym_table: *const std.StringArrayHashMap(MultiSym), + + /// Sort first by section name, then by symbol name + pub fn lessThan(ctx: @This(), index_a: usize, index_b: usize) bool { + const multi_sym_a = ctx.sym_table.values()[index_a]; + const multi_sym_b = ctx.sym_table.values()[index_b]; + + const section_a = ctx.sections.keys()[multi_sym_a.section]; + const section_b = ctx.sections.keys()[multi_sym_b.section]; + + switch (mem.order(u8, section_a, section_b)) { + .lt => return true, + .gt => return false, + .eq => {}, + } + + const symbol_a = ctx.sym_table.keys()[index_a]; + const symbol_b = ctx.sym_table.keys()[index_b]; + + switch (mem.order(u8, symbol_a, symbol_b)) { + .lt => return true, + .gt, .eq => return false, + } + } + }; + sym_table.sort(SymTableSort{ .sym_table = &sym_table, .sections = §ions }); + var prev_section: u16 = std.math.maxInt(u16); var prev_pp_state: enum { none, ptr32, special } = .none; for (sym_table.values()) |multi_sym, sym_index| { From 8b8e57c670132d615f2b5bbf69acf1658c083b12 Mon Sep 17 00:00:00 2001 From: Erik Arvstedt Date: Wed, 4 May 2022 20:01:48 +0200 Subject: [PATCH 1342/2031] std.meta.TrailerFlags: include in std tests Previously, TrailerFlags was unreferenced in std, so its tests were never run. Also, fix the use of `default_value` whose type was changed in f4a249325e8e3741a6294462ae37a79cb9089c56 (#10766). --- lib/std/meta.zig | 4 ++++ lib/std/meta/trailer_flags.zig | 5 +---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/std/meta.zig b/lib/std/meta.zig index e6ce746f19..0de51bfa68 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -10,6 +10,10 @@ pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags; const Type = std.builtin.Type; +test "std.meta.TrailerFlags" { + _ = TrailerFlags; +} + pub fn tagName(v: anytype) []const u8 { const T = @TypeOf(v); switch (@typeInfo(T)) { diff --git a/lib/std/meta/trailer_flags.zig b/lib/std/meta/trailer_flags.zig index a128564708..e4288692b1 100644 --- a/lib/std/meta/trailer_flags.zig +++ b/lib/std/meta/trailer_flags.zig @@ -24,10 +24,7 @@ pub fn TrailerFlags(comptime Fields: type) type { fields[i] = Type.StructField{ .name = struct_field.name, .field_type = ?struct_field.field_type, - .default_value = @as( - ??struct_field.field_type, - @as(?struct_field.field_type, null), - ), + .default_value = &@as(?struct_field.field_type, null), .is_comptime = false, .alignment = @alignOf(?struct_field.field_type), }; From e5b07d8187c8e36c7fad4d190d9eac604018ce3b Mon Sep 17 00:00:00 2001 From: Erik Arvstedt Date: Wed, 4 May 2022 20:01:49 +0200 Subject: [PATCH 1343/2031] std.meta.TrailerFlags: improve init type --- lib/std/meta/trailer_flags.zig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/std/meta/trailer_flags.zig b/lib/std/meta/trailer_flags.zig index e4288692b1..212a47bc29 100644 --- a/lib/std/meta/trailer_flags.zig +++ b/lib/std/meta/trailer_flags.zig @@ -18,7 +18,8 @@ pub fn TrailerFlags(comptime Fields: type) type { pub const FieldEnum = std.meta.FieldEnum(Fields); - pub const InitStruct = blk: { + pub const ActiveFields = std.enums.EnumFieldStruct(FieldEnum, bool, false); + pub const FieldValues = blk: { comptime var fields: [bit_count]Type.StructField = undefined; inline for (@typeInfo(Fields).Struct.fields) |struct_field, i| { fields[i] = Type.StructField{ @@ -57,19 +58,18 @@ pub fn TrailerFlags(comptime Fields: type) type { self.bits |= 1 << field_index; } - /// `fields` is a struct with each field set to an optional value. - /// Only the non-null bits are observed and are used to set the flag bits. - pub fn init(fields: InitStruct) Self { + /// `fields` is a boolean struct where each active field is set to `true` + pub fn init(fields: ActiveFields) Self { var self: Self = .{ .bits = 0 }; inline for (@typeInfo(Fields).Struct.fields) |field, i| { - if (@field(fields, field.name)) |_| + if (@field(fields, field.name)) self.bits |= 1 << i; } return self; } - /// `fields` is a struct with each field set to an optional value (same as `init`). - pub fn setMany(self: Self, p: [*]align(@alignOf(Fields)) u8, fields: InitStruct) void { + /// `fields` is a struct with each field set to an optional value + pub fn setMany(self: Self, p: [*]align(@alignOf(Fields)) u8, fields: FieldValues) void { inline for (@typeInfo(Fields).Struct.fields) |field, i| { if (@field(fields, field.name)) |value| self.set(p, @intToEnum(FieldEnum, i), value); @@ -142,7 +142,7 @@ test "TrailerFlags" { var flags = Flags.init(.{ .b = true, - .c = 1234, + .c = true, }); const slice = try testing.allocator.allocAdvanced(u8, 8, flags.sizeInBytes(), .exact); defer testing.allocator.free(slice); From 9d79b740bc981da9f336d5c72d1abe7d81e6ad9f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 3 May 2022 10:29:12 +0200 Subject: [PATCH 1344/2031] test: improve test batch/sequence iterator With this improved iterator, type of test is now inferred from the filename, enabling us to put all cases in one common parent directory, and iterate over that, thus automating a lot of tasks. --- src/test.zig | 370 ++++++++++++++++++++++++++++----------------------- 1 file changed, 205 insertions(+), 165 deletions(-) diff --git a/src/test.zig b/src/test.zig index bdc02ff2e0..1359f22ff0 100644 --- a/src/test.zig +++ b/src/test.zig @@ -44,7 +44,7 @@ test { // TODO make this incremental once the bug is solved that it triggers // See: https://github.com/ziglang/zig/issues/11344 - ctx.addTestCasesFromDir(dir, .independent); + ctx.addTestCasesFromDir(dir); } { @@ -55,7 +55,7 @@ test { var dir = try std.fs.cwd().openDir(dir_path, .{ .iterate = true }); defer dir.close(); - ctx.addTestCasesFromDir(dir, .incremental); + ctx.addTestCasesFromDir(dir); } try @import("test_cases").addCases(&ctx); @@ -395,6 +395,134 @@ const TestManifest = struct { } }; +const TestStrategy = enum { + /// Execute tests as independent compilations, unless they are explicitly + /// incremental ("foo.0.zig", "foo.1.zig", etc.) + independent, + /// Execute all tests as incremental updates to a single compilation. Explicitly + /// incremental tests ("foo.0.zig", "foo.1.zig", etc.) still execute in order + incremental, +}; + +/// Iterates a set of filenames extracting batches that are either incremental +/// ("foo.0.zig", "foo.1.zig", etc.) or independent ("foo.zig", "bar.zig", etc.). +/// Assumes filenames are sorted. +const TestIterator = struct { + start: usize = 0, + end: usize = 0, + filenames: []const []const u8, + + const Error = error{InvalidIncrementalTestIndex}; + + fn next(it: *TestIterator) Error!?[]const []const u8 { + try it.nextInner(); + if (it.start == it.end) return null; + return it.filenames[it.start..it.end]; + } + + fn nextInner(it: *TestIterator) Error!void { + it.start = it.end; + if (it.end == it.filenames.len) return; + if (it.end + 1 == it.filenames.len) { + it.end += 1; + return; + } + + const remaining = it.filenames[it.end..]; + var i: usize = 0; + while (i < remaining.len - 1) : (i += 1) { + // First, check if this file is part of an incremental update sequence + // Split filename into ".." + const prev_parts = getTestFileNameParts(remaining[i]); + const new_parts = getTestFileNameParts(remaining[i + 1]); + + // If base_name and file_ext match, these files are in the same test sequence + // and the new one should be the incremented version of the previous test + if (std.mem.eql(u8, prev_parts.base_name, new_parts.base_name) and + std.mem.eql(u8, prev_parts.file_ext, new_parts.file_ext)) + { + // This is "foo.X.zig" followed by "foo.Y.zig". Make sure that X = Y + 1 + if (prev_parts.test_index == null) + return error.InvalidIncrementalTestIndex; + if (new_parts.test_index == null) + return error.InvalidIncrementalTestIndex; + if (new_parts.test_index.? != prev_parts.test_index.? + 1) + return error.InvalidIncrementalTestIndex; + } else { + // This is not the same test sequence, so the new file must be the first file + // in a new sequence ("*.0.zig") or an independent test file ("*.zig") + if (new_parts.test_index != null and new_parts.test_index.? != 0) + return error.InvalidIncrementalTestIndex; + + it.end += i + 1; + break; + } + } else { + it.end += remaining.len; + } + } +}; + +/// For a filename in the format ".X." or ".", returns +/// "", "" and X parsed as a decimal number. If X is not present, or +/// cannot be parsed as a decimal number, it is treated as part of +fn getTestFileNameParts(name: []const u8) struct { + base_name: []const u8, + file_ext: []const u8, + test_index: ?usize, +} { + const file_ext = std.fs.path.extension(name); + const trimmed = name[0 .. name.len - file_ext.len]; // Trim off "." + const maybe_index = std.fs.path.extension(trimmed); // Extract ".X" + + // Attempt to parse index + const index: ?usize = if (maybe_index.len > 0) + std.fmt.parseInt(usize, maybe_index[1..], 10) catch null + else + null; + + // Adjust "" extent based on parsing success + const base_name_end = trimmed.len - if (index != null) maybe_index.len else 0; + return .{ + .base_name = name[0..base_name_end], + .file_ext = if (file_ext.len > 0) file_ext[1..] else file_ext, + .test_index = index, + }; +} + +/// Sort test filenames in-place, so that incremental test cases ("foo.0.zig", +/// "foo.1.zig", etc.) are contiguous and appear in numerical order. +fn sortTestFilenames( + filenames: [][]const u8, +) void { + const Context = struct { + pub fn lessThan(_: @This(), a: []const u8, b: []const u8) bool { + const a_parts = getTestFileNameParts(a); + const b_parts = getTestFileNameParts(b); + + // Sort ".X." based on "" and "" first + return switch (std.mem.order(u8, a_parts.base_name, b_parts.base_name)) { + .lt => true, + .gt => false, + .eq => switch (std.mem.order(u8, a_parts.file_ext, b_parts.file_ext)) { + .lt => true, + .gt => false, + .eq => b: { // a and b differ only in their ".X" part + + // Sort "." before any ".X." + if (a_parts.test_index == null) break :b true; + if (b_parts.test_index == null) break :b false; + + // Make sure that incremental tests appear in linear order + return a_parts.test_index.? < b_parts.test_index.?; + }, + }, + }; + } + }; + std.sort.sort([]const u8, filenames, Context{}, Context.lessThan); +} + pub const TestContext = struct { arena: Allocator, cases: std.ArrayList(Case), @@ -895,100 +1023,29 @@ pub const TestContext = struct { case.compiles(fixed_src); } - const Strategy = enum { - /// Execute tests as independent compilations, unless they are explicitly - /// incremental ("foo.0.zig", "foo.1.zig", etc.) - independent, - /// Execute all tests as incremental updates to a single compilation. Explicitly - /// incremental tests ("foo.0.zig", "foo.1.zig", etc.) still execute in order - incremental, - }; - - /// Adds a test for each file in the provided directory, using the selected strategy. + /// Adds a test for each file in the provided directory. + /// Testing strategy (TestStrategy) is inferred automatically from filenames. /// Recurses nested directories. /// /// Each file should include a test manifest as a contiguous block of comments at /// the end of the file. The first line should be the test type, followed by a set of /// key-value config values, followed by a blank line, then the expected output. - pub fn addTestCasesFromDir(ctx: *TestContext, dir: std.fs.Dir, strategy: Strategy) void { + pub fn addTestCasesFromDir(ctx: *TestContext, dir: std.fs.Dir) void { var current_file: []const u8 = "none"; - ctx.addTestCasesFromDirInner(dir, strategy, ¤t_file) catch |err| { + ctx.addTestCasesFromDirInner(dir, ¤t_file) catch |err| { std.debug.panic("test harness failed to process file '{s}': {s}\n", .{ current_file, @errorName(err), }); }; } - /// For a filename in the format ".X." or ".", returns - /// "", "" and X parsed as a decimal number. If X is not present, or - /// cannot be parsed as a decimal number, it is treated as part of - fn getTestFileNameParts(name: []const u8) struct { - base_name: []const u8, - file_ext: []const u8, - test_index: ?usize, - } { - const file_ext = std.fs.path.extension(name); - const trimmed = name[0 .. name.len - file_ext.len]; // Trim off "." - const maybe_index = std.fs.path.extension(trimmed); // Extract ".X" - - // Attempt to parse index - const index: ?usize = if (maybe_index.len > 0) - std.fmt.parseInt(usize, maybe_index[1..], 10) catch null - else - null; - - // Adjust "" extent based on parsing success - const base_name_end = trimmed.len - if (index != null) maybe_index.len else 0; - return .{ - .base_name = name[0..base_name_end], - .file_ext = if (file_ext.len > 0) file_ext[1..] else file_ext, - .test_index = index, - }; - } - - /// Sort test filenames in-place, so that incremental test cases ("foo.0.zig", - /// "foo.1.zig", etc.) are contiguous and appear in numerical order. - fn sortTestFilenames( - filenames: [][]const u8, - ) void { - const Context = struct { - pub fn lessThan(_: @This(), a: []const u8, b: []const u8) bool { - const a_parts = getTestFileNameParts(a); - const b_parts = getTestFileNameParts(b); - - // Sort ".X." based on "" and "" first - return switch (std.mem.order(u8, a_parts.base_name, b_parts.base_name)) { - .lt => true, - .gt => false, - .eq => switch (std.mem.order(u8, a_parts.file_ext, b_parts.file_ext)) { - .lt => true, - .gt => false, - .eq => b: { // a and b differ only in their ".X" part - - // Sort "." before any ".X." - if (a_parts.test_index == null) break :b true; - if (b_parts.test_index == null) break :b false; - - // Make sure that incremental tests appear in linear order - return a_parts.test_index.? < b_parts.test_index.?; - }, - }, - }; - } - }; - std.sort.sort([]const u8, filenames, Context{}, Context.lessThan); - } - fn addTestCasesFromDirInner( ctx: *TestContext, dir: std.fs.Dir, - strategy: Strategy, /// This is kept up to date with the currently being processed file so /// that if any errors occur the caller knows it happened during this file. current_file: *[]const u8, ) !void { - var cases = std.ArrayList(usize).init(ctx.arena); - var it = try dir.walk(ctx.arena); var filenames = std.ArrayList([]const u8).init(ctx.arena); @@ -1006,104 +1063,87 @@ pub const TestContext = struct { // Sort filenames, so that incremental tests are contiguous and in-order sortTestFilenames(filenames.items); - var prev_filename: []const u8 = ""; - for (filenames.items) |filename| { - current_file.* = filename; + var test_it = TestIterator{ .filenames = filenames.items }; + while (try test_it.next()) |batch| { + const strategy: TestStrategy = if (batch.len > 1) .incremental else .independent; + var cases = std.ArrayList(usize).init(ctx.arena); - // First, check if this file is part of an incremental update sequence - // Split filename into ".." - const prev_parts = getTestFileNameParts(prev_filename); - const new_parts = getTestFileNameParts(filename); + for (batch) |filename| { + current_file.* = filename; - // If base_name and file_ext match, these files are in the same test sequence - // and the new one should be the incremented version of the previous test - if (std.mem.eql(u8, prev_parts.base_name, new_parts.base_name) and - std.mem.eql(u8, prev_parts.file_ext, new_parts.file_ext)) - { - // This is "foo.X.zig" followed by "foo.Y.zig". Make sure that X = Y + 1 - if (prev_parts.test_index == null) return error.InvalidIncrementalTestIndex; - if (new_parts.test_index == null) return error.InvalidIncrementalTestIndex; - if (new_parts.test_index.? != prev_parts.test_index.? + 1) return error.InvalidIncrementalTestIndex; - } else { - // This is not the same test sequence, so the new file must be the first file - // in a new sequence ("*.0.zig") or an independent test file ("*.zig") - if (new_parts.test_index != null and new_parts.test_index.? != 0) return error.InvalidIncrementalTestIndex; - cases.clearRetainingCapacity(); - } - prev_filename = filename; + const max_file_size = 10 * 1024 * 1024; + const src = try dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0); - const max_file_size = 10 * 1024 * 1024; - const src = try dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0); + // Parse the manifest + var manifest = try TestManifest.parse(ctx.arena, src); - // Parse the manifest - var manifest = try TestManifest.parse(ctx.arena, src); + if (cases.items.len == 0) { + const backends = try manifest.getConfigForKeyAlloc(ctx.arena, "backend", Backend); + const targets = try manifest.getConfigForKeyAlloc(ctx.arena, "target", CrossTarget); + const is_test = manifest.getConfigForKeyAssertSingle("is_test", bool); + const output_mode = manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode); - if (cases.items.len == 0) { - const backends = try manifest.getConfigForKeyAlloc(ctx.arena, "backend", Backend); - const targets = try manifest.getConfigForKeyAlloc(ctx.arena, "target", CrossTarget); - const is_test = manifest.getConfigForKeyAssertSingle("is_test", bool); - const output_mode = manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode); + const name_prefix = blk: { + const ext_index = std.mem.lastIndexOfScalar(u8, current_file.*, '.') orelse + return error.InvalidFilename; + const index = std.mem.lastIndexOfScalar(u8, current_file.*[0..ext_index], '.') orelse ext_index; + break :blk current_file.*[0..index]; + }; - const name_prefix = blk: { - const ext_index = std.mem.lastIndexOfScalar(u8, current_file.*, '.') orelse - return error.InvalidFilename; - const index = std.mem.lastIndexOfScalar(u8, current_file.*[0..ext_index], '.') orelse ext_index; - break :blk current_file.*[0..index]; - }; + // Cross-product to get all possible test combinations + for (backends) |backend| { + if (backend == .stage1 and skip_stage1) continue; - // Cross-product to get all possible test combinations - for (backends) |backend| { - if (backend == .stage1 and skip_stage1) continue; - - for (targets) |target| { - const name = try std.fmt.allocPrint(ctx.arena, "{s} ({s}, {s})", .{ - name_prefix, - @tagName(backend), - try target.zigTriple(ctx.arena), - }); - const next = ctx.cases.items.len; - try ctx.cases.append(.{ - .name = name, - .target = target, - .backend = backend, - .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), - .is_test = is_test, - .output_mode = output_mode, - .link_libc = backend == .llvm, - .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), - }); - try cases.append(next); + for (targets) |target| { + const name = try std.fmt.allocPrint(ctx.arena, "{s} ({s}, {s})", .{ + name_prefix, + @tagName(backend), + try target.zigTriple(ctx.arena), + }); + const next = ctx.cases.items.len; + try ctx.cases.append(.{ + .name = name, + .target = target, + .backend = backend, + .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), + .is_test = is_test, + .output_mode = output_mode, + .link_libc = backend == .llvm, + .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), + }); + try cases.append(next); + } } } - } - for (cases.items) |case_index| { - const case = &ctx.cases.items[case_index]; - switch (manifest.@"type") { - .@"error" => { - const errors = try manifest.trailingAlloc(ctx.arena); - switch (strategy) { - .independent => { - case.addError(src, errors); - }, - .incremental => { - case.addErrorNamed("update", src, errors); - }, - } - }, - .run => { - var output = std.ArrayList(u8).init(ctx.arena); - var trailing_it = manifest.trailing(); - while (trailing_it.next()) |line| { - try output.appendSlice(line); - try output.append('\n'); - } - if (output.items.len > 0) { - try output.resize(output.items.len - 1); - } - case.addCompareOutput(src, output.toOwnedSlice()); - }, - .cli => @panic("TODO cli tests"), + for (cases.items) |case_index| { + const case = &ctx.cases.items[case_index]; + switch (manifest.@"type") { + .@"error" => { + const errors = try manifest.trailingAlloc(ctx.arena); + switch (strategy) { + .independent => { + case.addError(src, errors); + }, + .incremental => { + case.addErrorNamed("update", src, errors); + }, + } + }, + .run => { + var output = std.ArrayList(u8).init(ctx.arena); + var trailing_it = manifest.trailing(); + while (trailing_it.next()) |line| { + try output.appendSlice(line); + try output.append('\n'); + } + if (output.items.len > 0) { + try output.resize(output.items.len - 1); + } + case.addCompareOutput(src, output.toOwnedSlice()); + }, + .cli => @panic("TODO cli tests"), + } } } } From 3624e1ef485c3575cb0c57a312386f49daf7ad37 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 4 May 2022 23:51:16 +0200 Subject: [PATCH 1345/2031] test: move compile errors and incremental tests into common dir --- ci/zinc/linux_test.sh | 2 +- src/test.zig | 15 +-------------- .../aarch64-linux/conditional_branches.0.zig | 0 .../aarch64-linux/conditional_branches.1.zig | 0 .../aarch64-linux/hello_world_with_updates.0.zig | 0 .../aarch64-linux/hello_world_with_updates.1.zig | 0 .../aarch64-linux/hello_world_with_updates.2.zig | 0 .../aarch64-macos/hello_world_with_updates.0.zig | 0 .../aarch64-macos/hello_world_with_updates.1.zig | 0 .../aarch64-macos/hello_world_with_updates.2.zig | 0 .../aarch64-macos/hello_world_with_updates.3.zig | 0 .../aarch64-macos/hello_world_with_updates.4.zig | 0 .../aarch64-macos/hello_world_with_updates.5.zig | 0 .../aarch64-macos/hello_world_with_updates.6.zig | 0 .../adding_numbers_at_runtime_and_comptime.0.zig | 0 .../adding_numbers_at_runtime_and_comptime.1.zig | 0 .../adding_numbers_at_runtime_and_comptime.2.zig | 0 .../ambiguous_reference.zig | 0 .../arm-linux/arithmetic_operations.0.zig | 0 .../arm-linux/arithmetic_operations.1.zig | 0 .../arm-linux/arithmetic_operations.2.zig | 0 .../arm-linux/arithmetic_operations.3.zig | 0 .../arm-linux/arithmetic_operations.4.zig | 0 .../arm-linux/arithmetic_operations.5.zig | 0 .../arm-linux/arithmetic_operations.6.zig | 0 .../{incremental => cases}/arm-linux/errors.0.zig | 0 .../{incremental => cases}/arm-linux/errors.1.zig | 0 .../{incremental => cases}/arm-linux/errors.2.zig | 0 .../{incremental => cases}/arm-linux/errors.3.zig | 0 .../arm-linux/function_pointers.zig | 0 .../arm-linux/hello_world_with_updates.0.zig | 0 .../arm-linux/hello_world_with_updates.1.zig | 0 .../arm-linux/hello_world_with_updates.2.zig | 0 .../arm-linux/parameters_and_return_values.0.zig | 0 .../arm-linux/parameters_and_return_values.1.zig | 0 .../arm-linux/print_u32s.zig | 0 .../arm-linux/spilling_registers.0.zig | 0 .../arm-linux/spilling_registers.1.zig | 0 .../bad_inferred_variable_type.zig | 0 test/{incremental => cases}/binary_operands.0.zig | 0 test/{incremental => cases}/binary_operands.1.zig | 0 .../{incremental => cases}/binary_operands.10.zig | 0 .../{incremental => cases}/binary_operands.11.zig | 0 .../{incremental => cases}/binary_operands.12.zig | 0 .../{incremental => cases}/binary_operands.13.zig | 0 .../{incremental => cases}/binary_operands.14.zig | 0 .../{incremental => cases}/binary_operands.15.zig | 0 .../{incremental => cases}/binary_operands.16.zig | 0 .../{incremental => cases}/binary_operands.17.zig | 0 .../{incremental => cases}/binary_operands.18.zig | 0 .../{incremental => cases}/binary_operands.19.zig | 0 test/{incremental => cases}/binary_operands.2.zig | 0 .../{incremental => cases}/binary_operands.20.zig | 0 .../{incremental => cases}/binary_operands.21.zig | 0 .../{incremental => cases}/binary_operands.22.zig | 0 .../{incremental => cases}/binary_operands.23.zig | 0 .../{incremental => cases}/binary_operands.24.zig | 0 .../{incremental => cases}/binary_operands.25.zig | 0 test/{incremental => cases}/binary_operands.3.zig | 0 test/{incremental => cases}/binary_operands.4.zig | 0 test/{incremental => cases}/binary_operands.5.zig | 0 test/{incremental => cases}/binary_operands.6.zig | 0 test/{incremental => cases}/binary_operands.7.zig | 0 test/{incremental => cases}/binary_operands.8.zig | 0 test/{incremental => cases}/binary_operands.9.zig | 0 test/{incremental => cases}/break_continue.0.zig | 0 test/{incremental => cases}/break_continue.1.zig | 0 test/{incremental => cases}/break_continue.2.zig | 0 test/{incremental => cases}/break_continue.3.zig | 0 .../catch_at_comptime.0.zig | 0 .../catch_at_comptime.1.zig | 0 .../catch_at_comptime.2.zig | 0 .../catch_at_comptime.3.zig | 0 .../catch_at_comptime.4.zig | 0 test/{incremental => cases}/compile_error.zig | 0 .../compile_error_in_inline_fn_call_fixed.0.zig | 0 .../compile_error_in_inline_fn_call_fixed.1.zig | 0 .../stage1/exe/main_missing_name.zig | 0 .../stage1/exe/missing_main_fn_in_executable.zig | 0 .../compile_errors/stage1/exe/private_main_fn.zig | 0 ...on_C_ABI_compatible_type_or_has_align_attr.zig | 0 .../stage1/obj/C_pointer_to_anyopaque.zig | 0 .../stage1/obj/Frame_of_generic_function.zig | 0 ...y_minus_for_unsigned_types_a_compile_error.zig | 0 ...Issue_6823_dont_allow_._to_be_followed_by_.zig | 0 ..._9165_windows_tcp_server_compilation_error.zig | 0 .../access_non-existent_member_of_error_set.zig | 0 ...sing_runtime_parameter_from_outer_function.zig | 0 .../stage1/obj/add_assign_on_undefined_value.zig | 0 .../stage1/obj/add_on_undefined_value.zig | 0 .../obj/add_overflow_in_function_evaluation.zig | 0 .../obj/add_wrap_assign_on_undefined_value.zig | 0 .../stage1/obj/add_wrap_on_undefined_value.zig | 0 .../stage1/obj/addition_with_non_numbers.zig | 0 .../stage1/obj/address_of_number_literal.zig | 0 .../obj/alignCast_expects_pointer_or_slice.zig | 0 ..._expr_function_pointers_is_a_compile_error.zig | 0 .../obj/aligned_variable_of_zero-bit_type.zig | 0 .../obj/alignment_of_enum_field_specified.zig | 0 .../stage1/obj/ambiguous_decl_reference.zig | 0 .../stage1/obj/and_on_undefined_value.zig | 0 .../stage1/obj/array_access_of_non_array.zig | 0 .../stage1/obj/array_access_of_type.zig | 0 .../obj/array_access_of_undeclared_identifier.zig | 0 .../obj/array_access_with_non_integer_index.zig | 0 .../obj/array_concatenation_with_wrong_type.zig | 0 .../stage1/obj/array_in_c_exported_function.zig | 0 .../stage1/obj/asm_at_compile_time.zig | 0 .../obj/assign_inline_fn_to_non-comptime_var.zig | 0 .../obj/assign_null_to_non-optional_pointer.zig | 0 .../obj/assign_through_constant_pointer.zig | 0 .../stage1/obj/assign_through_constant_slice.zig | 0 .../stage1/obj/assign_to_constant_field.zig | 0 .../stage1/obj/assign_to_constant_variable.zig | 0 .../stage1/obj/assign_to_invalid_dereference.zig | 0 .../stage1/obj/assign_too_big_number_to_u16.zig | 0 .../stage1/obj/assign_unreachable.zig | 0 ...s_with_a_function_that_returns_an_optional.zig | 0 .../async_function_depends_on_its_own_frame.zig | 0 ...nction_indirectly_depends_on_its_own_frame.zig | 0 ...orderings_of_atomicStore_Acquire_or_AcqRel.zig | 0 ...s_of_cmpxchg-failure_stricter_than_success.zig | 0 ...s_of_cmpxchg-success_Monotonic_or_stricter.zig | 0 ...mic_orderings_of_fence_Acquire_or_stricter.zig | 0 .../obj/atomicrmw_with_bool_op_not_.Xchg.zig | 0 .../obj/atomicrmw_with_enum_op_not_.Xchg.zig | 0 ...icrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig | 0 .../obj/attempt_to_cast_enum_literal_to_error.zig | 0 ...se_over_comptime_variable_from_outer_scope.zig | 0 .../obj/attempt_to_create_17_bit_float_type.zig | 0 ...a_non-integer_non-float_or_non-vector_type.zig | 0 .../attempt_to_use_0_bit_type_in_extern_fn.zig | 0 .../stage1/obj/attempted_double_ampersand.zig | 0 .../attempted_double_pipe_on_boolean_values.zig | 0 ...pted_implicit_cast_from_T_to_slice_const_T.zig | 0 ...mplicit_cast_from_const_T_to_array_len_1_T.zig | 0 ...mpted_implicit_cast_from_const_T_to_sliceT.zig | 0 .../stage1/obj/bad_alignCast_at_comptime.zig | 0 .../stage1/obj/bad_alignment_in_asynccall.zig | 0 ..._implicit_cast_from_array_pointer_to_slice.zig | 0 .../stage1/obj/bad_alignment_type.zig | 0 ...side_function_which_references_local_const.zig | 0 .../compile_errors/stage1/obj/bad_import.zig | 0 .../stage1/obj/bad_usage_of_call.zig | 0 .../obj/bin_and_assign_on_undefined_value.zig | 0 .../stage1/obj/bin_and_on_undefined_value.zig | 0 .../stage1/obj/bin_not_on_undefined_value.zig | 0 .../obj/bin_or_assign_on_undefined_value.zig | 0 .../stage1/obj/bin_or_on_undefined_value.zig | 0 .../obj/bin_xor_assign_on_undefined_value.zig | 0 .../stage1/obj/bin_xor_on_undefined_value.zig | 0 .../stage1/obj/binary_not_on_number_literal.zig | 0 .../bitCast_same_size_but_bit_count_mismatch.zig | 0 .../stage1/obj/bitCast_to_enum_type.zig | 0 ..._with_different_sizes_inside_an_expression.zig | 0 .../bit_shifting_only_works_on_integer_types.zig | 0 .../stage1/obj/bogus_compile_var.zig | 0 .../stage1/obj/bogus_method_call_on_slice.zig | 0 .../stage1/obj/bool_not_on_undefined_value.zig | 0 .../stage1/obj/branch_on_undefined_value.zig | 0 .../stage1/obj/cImport_with_bogus_include.zig | 0 .../stage1/obj/call_assigned_to_constant.zig | 0 .../call_with_new_stack_on_unsupported_target.zig | 0 ...pcs_aapcs_aapcsvfp_on_unsupported_platform.zig | 0 ...callconv_interrupt_on_unsupported_platform.zig | 0 .../callconv_signal_on_unsupported_platform.zig | 0 ...astcall_thiscall_on_unsupported_platform-0.zig | 0 ...astcall_thiscall_on_unsupported_platform-1.zig | 0 ...allconv_vectorcall_on_unsupported_platform.zig | 0 ...g_a_generic_function_only_known_at_runtime.zig | 0 ...ing_function_with_naked_calling_convention.zig | 0 ..._function_passing_array_instead_of_pointer.zig | 0 .../obj/cannot_break_out_of_defer_expression.zig | 0 .../cannot_continue_out_of_defer_expression.zig | 0 ...itch_prong_with_incompatible_payload_types.zig | 0 ...t_enum_literal_to_enum_but_it_doesnt_match.zig | 0 ...or_set_to_error_union_of_smaller_error_set.zig | 0 .../obj/cast_global_error_set_to_error_set.zig | 0 .../cast_negative_integer_literal_to_usize.zig | 0 .../cast_negative_value_to_unsigned_integer.zig | 0 .../stage1/obj/cast_unreachable.zig | 0 ...ting_bit_offset_pointer_to_regular_pointer.zig | 0 .../stage1/obj/catch_on_undefined_value.zig | 0 .../stage1/obj/chained_comparison_operators.zig | 0 .../stage1/obj/cmpxchg_with_float.zig | 0 .../obj/colliding_invalid_top_level_functions.zig | 0 ...ptional_to_non-optional_with_invalid_types.zig | 0 ...paring_a_non-optional_pointer_against_null.zig | 0 ...against_undefined_produces_undefined_value.zig | 0 .../comparison_operators_with_undefined_value.zig | 0 ...omparison_with_error_union_and_error_value.zig | 0 .../stage1/obj/compile-time_division_by_zero.zig | 0 .../compile-time_remainder_division_by_zero.zig | 0 ...ows_traceback_of_references_that_caused_it.zig | 0 ...g_of_tagged_enum_doesnt_crash_the_compiler.zig | 0 .../compile_error_in_struct_init_expression.zig | 0 ...aluating_return_type_of_inferred_error_set.zig | 0 .../compile_errors/stage1/obj/compile_log.zig | 0 .../compile_log_a_pointer_to_an_opaque_value.zig | 0 ..._function_which_must_be_comptime_evaluated.zig | 0 ...tement_warning_deduplication_in_generic_fn.zig | 0 .../stage1/obj/compile_time_division_by_zero.zig | 0 ...e_cast_enum_to_union_but_field_has_payload.zig | 0 .../comptime_continue_inside_runtime_catch.zig | 0 .../comptime_continue_inside_runtime_if_bool.zig | 0 .../comptime_continue_inside_runtime_if_error.zig | 0 ...mptime_continue_inside_runtime_if_optional.zig | 0 .../comptime_continue_inside_runtime_switch.zig | 0 ...omptime_continue_inside_runtime_while_bool.zig | 0 ...mptime_continue_inside_runtime_while_error.zig | 0 ...ime_continue_inside_runtime_while_optional.zig | 0 .../stage1/obj/comptime_float_in_asm_input.zig | 0 .../obj/comptime_implicit_cast_f64_to_f32.zig | 0 .../stage1/obj/comptime_int_in_asm_input.zig | 0 .../obj/comptime_ptrcast_of_zero-sized_type.zig | 0 ...ot_match_memory_at_target_index_terminated.zig | 0 ..._match_memory_at_target_index_unterminated.zig | 0 ...ce-sentinel_does_not_match_target-sentinel.zig | 0 ...slice-sentinel_is_out_of_bounds_terminated.zig | 0 ...ice-sentinel_is_out_of_bounds_unterminated.zig | 0 .../obj/comptime_slice_of_an_undefined_slice.zig | 0 ...me_slice_of_undefined_pointer_non-zero_len.zig | 0 .../obj/comptime_struct_field_no_init_value.zig | 0 .../stage1/obj/const_frame_cast_to_anyframe.zig | 0 .../const_is_a_statement_not_an_expression.zig | 0 .../stage1/obj/container_init_with_non-type.zig | 0 .../control_flow_uses_comptime_var_at_runtime.zig | 0 .../control_reaches_end_of_non-void_function.zig | 0 .../stage1/obj/declaration_between_fields.zig | 0 ..._name_as_primitive_must_use_special_syntax.zig | 0 .../obj/deduplicate_undeclared_identifier.zig | 0 .../stage1/obj/deref_on_undefined_value.zig | 0 .../stage1/obj/deref_slice_and_get_len_field.zig | 0 .../stage1/obj/dereference_an_array.zig | 0 .../obj/dereference_unknown_length_pointer.zig | 0 .../stage1/obj/direct_struct_loop.zig | 0 ..._embedding_opaque_type_in_struct_and_union.zig | 0 ...minated_pointer_to_null-terminated_pointer.zig | 0 .../stage1/obj/discarding_error_value.zig | 0 .../stage1/obj/div_assign_on_undefined_value.zig | 0 .../stage1/obj/div_on_undefined_value.zig | 0 .../stage1/obj/division_by_zero.zig | 0 ..._implicit_cast_double_pointer_to_anyopaque.zig | 0 .../obj/double_optional_on_main_return_value.zig | 0 .../stage1/obj/duplicate_boolean_switch_value.zig | 0 .../stage1/obj/duplicate_enum_field.zig | 0 .../stage1/obj/duplicate_error_in_switch.zig | 0 .../obj/duplicate_error_value_in_error_set.zig | 0 ...duplicate_field_in_struct_value_expression.zig | 0 .../stage1/obj/duplicate_struct_field.zig | 0 .../stage1/obj/duplicate_union_field.zig | 0 .../stage1/obj/embedFile_with_bogus_file.zig | 0 .../stage1/obj/empty_for_loop_body.zig | 0 .../compile_errors/stage1/obj/empty_if_body.zig | 0 .../stage1/obj/empty_switch_on_an_integer.zig | 0 .../stage1/obj/empty_while_loop_body.zig | 0 .../obj/endless_loop_in_function_evaluation.zig | 0 .../obj/enum_field_value_references_enum.zig | 0 ..._in_field_count_range_but_not_matching_tag.zig | 0 .../stage1/obj/enum_value_already_taken.zig | 0 .../stage1/obj/enum_with_0_fields.zig | 0 ...th_declarations_unavailable_for_reify_type.zig | 0 ...r_equality_but_sets_have_no_common_members.zig | 0 .../stage1/obj/error_not_handled_in_switch.zig | 0 ...ote_for_function_parameter_incompatibility.zig | 0 ...rror_union_operator_with_non_error_set_LHS.zig | 0 .../obj/error_when_evaluating_return_type.zig | 0 .../obj/exceeded_maximum_bit_width_of_integer.zig | 0 ...integer_when_there_is_a_fraction_component.zig | 0 ...cast_known_at_comptime_violates_error_sets.zig | 0 .../explicitly_casting_non_tag_type_to_enum.zig | 0 .../export_function_with_comptime_parameter.zig | 0 .../stage1/obj/export_generic_function.zig | 0 .../stage1/obj/exported_async_function.zig | 0 ...ted_enum_without_explicit_integer_tag_type.zig | 0 .../obj/extern_function_pointer_mismatch.zig | 0 .../extern_function_with_comptime_parameter.zig | 0 ...n-compatible_but_inferred_integer_tag_type.zig | 0 ...ith_non-extern-compatible_integer_tag_type.zig | 0 .../obj/extern_union_field_missing_type.zig | 0 .../obj/extern_union_given_enum_tag_type.zig | 0 .../stage1/obj/extern_variable_has_no_type.zig | 0 .../stage1/obj/fieldParentPtr-bad_field_name.zig | 0 ...Ptr-comptime_field_ptr_not_based_on_struct.zig | 0 .../fieldParentPtr-comptime_wrong_field_index.zig | 0 ...ieldParentPtr-field_pointer_is_not_pointer.zig | 0 .../stage1/obj/fieldParentPtr-non_struct.zig | 0 .../stage1/obj/field_access_of_opaque_type.zig | 0 .../stage1/obj/field_access_of_slices.zig | 0 .../field_access_of_unknown_length_pointer.zig | 0 .../stage1/obj/field_type_supplied_in_an_enum.zig | 0 .../stage1/obj/floatToInt_comptime_safety.zig | 0 .../stage1/obj/float_literal_too_large_error.zig | 0 .../float_literal_too_small_error_denormal.zig | 0 .../obj/for_loop_body_expression_ignored.zig | 0 ...rame_called_outside_of_function_definition.zig | 0 .../obj/frame_causes_function_to_be_async.zig | 0 .../obj/function_alignment_non_power_of_2.zig | 0 .../function_call_assigned_to_incorrect_type.zig | 0 .../stage1/obj/function_parameter_is_opaque.zig | 0 .../obj/function_prototype_with_no_body.zig | 0 .../stage1/obj/function_returning_opaque_type.zig | 0 ...with_ccc_indirectly_calling_async_function.zig | 0 .../obj/function_with_invalid_return_type.zig | 0 ..._with_non-extern_non-packed_enum_parameter.zig | 0 ...ith_non-extern_non-packed_struct_parameter.zig | 0 ...with_non-extern_non-packed_union_parameter.zig | 0 ...c_fn_as_parameter_without_comptime_keyword.zig | 0 ...c_function_call_assigned_to_incorrect_type.zig | 0 ...tion_instance_with_non-constant_expression.zig | 0 .../generic_function_returning_opaque_type.zig | 0 ...ction_where_return_type_is_self-referenced.zig | 0 .../global_variable_alignment_non_power_of_2.zig | 0 ...le_initializer_must_be_constant_expression.zig | 0 .../stage1/obj/hasDecl_with_non-container.zig | 0 .../stage1/obj/if_condition_is_bool_not_int.zig | 0 .../obj/ignored_assert-err-ok_return_value.zig | 0 .../obj/ignored_comptime_statement_value.zig | 0 .../stage1/obj/ignored_comptime_value.zig | 0 .../stage1/obj/ignored_deferred_function_call.zig | 0 .../obj/ignored_deferred_statement_value.zig | 0 .../ignored_expression_in_while_continuation.zig | 0 .../stage1/obj/ignored_return_value.zig | 0 .../stage1/obj/ignored_statement_value.zig | 0 .../stage1/obj/illegal_comparison_of_types.zig | 0 ...nter_and_Zig_pointer-bad_const-align-child.zig | 0 ...implicit_cast_const_array_to_mutable_slice.zig | 0 .../implicit_cast_from_array_to_mutable_slice.zig | 0 .../stage1/obj/implicit_cast_from_f64_to_f32.zig | 0 .../implicit_cast_of_error_set_not_a_subset.zig | 0 ...ointers_which_would_mess_up_null_semantics.zig | 0 ...icit_casting_null_c_pointer_to_zig_pointer.zig | 0 ...cit_casting_too_big_integers_to_C_pointers.zig | 0 ...casting_undefined_c_pointer_to_zig_pointer.zig | 0 .../stage1/obj/implicit_dependency_on_libc.zig | 0 .../stage1/obj/implicit_semicolon-block_expr.zig | 0 .../obj/implicit_semicolon-block_statement.zig | 0 .../implicit_semicolon-comptime_expression.zig | 0 .../obj/implicit_semicolon-comptime_statement.zig | 0 .../stage1/obj/implicit_semicolon-defer.zig | 0 .../obj/implicit_semicolon-for_expression.zig | 0 .../obj/implicit_semicolon-for_statement.zig | 0 ...licit_semicolon-if-else-if-else_expression.zig | 0 ...plicit_semicolon-if-else-if-else_statement.zig | 0 .../implicit_semicolon-if-else-if_expression.zig | 0 .../implicit_semicolon-if-else-if_statement.zig | 0 .../obj/implicit_semicolon-if-else_expression.zig | 0 .../obj/implicit_semicolon-if-else_statement.zig | 0 .../obj/implicit_semicolon-if_expression.zig | 0 .../obj/implicit_semicolon-if_statement.zig | 0 .../obj/implicit_semicolon-test_expression.zig | 0 .../obj/implicit_semicolon-test_statement.zig | 0 ...plicit_semicolon-while-continue_expression.zig | 0 ...mplicit_semicolon-while-continue_statement.zig | 0 .../obj/implicit_semicolon-while_expression.zig | 0 .../obj/implicit_semicolon-while_statement.zig | 0 .../obj/implicitly_casting_enum_to_tag_type.zig | 0 .../implicitly_increasing_pointer_alignment.zig | 0 .../obj/implicitly_increasing_slice_alignment.zig | 0 .../stage1/obj/import_outside_package_path.zig | 0 .../stage1/obj/incompatible_sentinels.zig | 0 .../stage1/obj/incorrect_return_type.zig | 0 .../obj/increase_pointer_alignment_in_ptrCast.zig | 0 .../indexing_a_undefined_slice_at_comptime.zig | 0 .../stage1/obj/indexing_an_array_of_size_zero.zig | 0 ...g_an_array_of_size_zero_with_runtime_index.zig | 0 .../stage1/obj/indexing_single-item_pointer.zig | 0 ...rect_recursion_of_async_functions_detected.zig | 0 .../stage1/obj/indirect_struct_loop.zig | 0 .../obj/inferred_array_size_invalid_here.zig | 0 .../inferring_error_set_of_function_pointer.zig | 0 .../obj/initializing_array_with_struct_syntax.zig | 0 ...for_an_invalid_struct_that_contains_itself.zig | 0 .../obj/intToPtr_with_misaligned_address.zig | 0 .../obj/int_to_err_global_invalid_number.zig | 0 .../obj/int_to_err_non_global_invalid_number.zig | 0 .../stage1/obj/int_to_ptr_of_0_bits.zig | 0 .../stage1/obj/integer_cast_truncates_bits.zig | 0 .../stage1/obj/integer_overflow_error.zig | 0 .../stage1/obj/integer_underflow_error.zig | 0 .../stage1/obj/invalid_break_expression.zig | 0 .../stage1/obj/invalid_builtin_fn.zig | 0 .../invalid_cast_from_integral_type_to_enum.zig | 0 .../invalid_comparison_for_function_pointers.zig | 0 .../stage1/obj/invalid_continue_expression.zig | 0 .../stage1/obj/invalid_deref_on_switch_target.zig | 0 .../stage1/obj/invalid_empty_unicode_escape.zig | 0 .../obj/invalid_exponent_in_float_literal-1.zig | 0 .../obj/invalid_exponent_in_float_literal-2.zig | 0 .../obj/invalid_field_access_in_comptime.zig | 0 .../invalid_field_in_struct_value_expression.zig | 0 .../stage1/obj/invalid_float_literal.zig | 0 .../stage1/obj/invalid_legacy_unicode_escape.zig | 0 .../stage1/obj/invalid_maybe_type.zig | 0 .../stage1/obj/invalid_member_of_builtin_enum.zig | 0 .../stage1/obj/invalid_multiple_dereferences.zig | 0 .../invalid_optional_type_in_extern_struct.zig | 0 .../stage1/obj/invalid_pointer_for_var_type.zig | 0 .../stage1/obj/invalid_pointer_syntax.zig | 0 .../stage1/obj/invalid_shift_amount_error.zig | 0 .../stage1/obj/invalid_struct_field.zig | 0 .../obj/invalid_suspend_in_exported_function.zig | 0 .../compile_errors/stage1/obj/invalid_type.zig | 0 .../obj/invalid_type_used_in_array_type.zig | 0 ...id_underscore_placement_in_float_literal-1.zig | 0 ...d_underscore_placement_in_float_literal-10.zig | 0 ...d_underscore_placement_in_float_literal-11.zig | 0 ...d_underscore_placement_in_float_literal-12.zig | 0 ...d_underscore_placement_in_float_literal-13.zig | 0 ...d_underscore_placement_in_float_literal-14.zig | 0 ...id_underscore_placement_in_float_literal-2.zig | 0 ...id_underscore_placement_in_float_literal-3.zig | 0 ...id_underscore_placement_in_float_literal-4.zig | 0 ...id_underscore_placement_in_float_literal-5.zig | 0 ...id_underscore_placement_in_float_literal-6.zig | 0 ...id_underscore_placement_in_float_literal-7.zig | 0 ...id_underscore_placement_in_float_literal-9.zig | 0 ...alid_underscore_placement_in_int_literal-1.zig | 0 ...alid_underscore_placement_in_int_literal-2.zig | 0 ...alid_underscore_placement_in_int_literal-3.zig | 0 ...alid_underscore_placement_in_int_literal-4.zig | 0 .../invalid_union_field_access_in_comptime.zig | 0 ..._diagnostic_string_for_top_level_decl_type.zig | 0 ...erce_from_undefined_array_pointer_to_slice.zig | 0 ...ssue_3818_bitcast_from_parray-slice_to_u16.zig | 0 ...non-terminated-slice_to_terminated-pointer.zig | 0 ...enced_by_typeInfo_and_passed_into_function.zig | 0 ..._optional_anyopaque_to_anyopaque_must_fail.zig | 0 ...comptime_slice-len_increment_beyond_bounds.zig | 0 ...ssue_9346_return_outside_of_function_scope.zig | 0 .../stage1/obj/labeled_break_not_found.zig | 0 .../stage1/obj/labeled_continue_not_found.zig | 0 .../lazy_pointer_with_undefined_element_type.zig | 0 .../stage1/obj/libc_headers_note.zig | 0 ..._bytes_from_comptime_reinterpreted_pointer.zig | 0 ..._vector_pointer_with_unknown_runtime_index.zig | 0 .../local_shadows_global_that_occurs_later.zig | 0 .../stage1/obj/local_variable_redeclaration.zig | 0 .../obj/local_variable_redeclares_parameter.zig | 0 .../obj/local_variable_shadowing_global.zig | 0 .../obj/locally_shadowing_a_primitive_type.zig | 0 .../obj/main_function_with_bogus_args_type.zig | 0 .../method_call_with_first_arg_type_primitive.zig | 0 ...d_call_with_first_arg_type_wrong_container.zig | 0 .../stage1/obj/missing_boolean_switch_value.zig | 0 ...sing_const_in_slice_with_nested_array_type.zig | 0 .../stage1/obj/missing_else_clause.zig | 0 .../missing_field_in_struct_value_expression.zig | 0 .../stage1/obj/missing_function_call_param.zig | 0 .../stage1/obj/missing_function_name.zig | 0 .../stage1/obj/missing_param_name.zig | 0 ...missing_parameter_name_of_generic_function.zig | 0 .../obj/missing_result_type_for_phi_node.zig | 0 ...isspelled_type_with_pointer_only_reference.zig | 0 .../stage1/obj/mod_assign_on_undefined_value.zig | 0 .../stage1/obj/mod_on_undefined_value.zig | 0 .../obj/mul_overflow_in_function_evaluation.zig | 0 .../stage1/obj/mult_assign_on_undefined_value.zig | 0 .../stage1/obj/mult_on_undefined_value.zig | 0 .../obj/mult_wrap_assign_on_undefined_value.zig | 0 .../stage1/obj/mult_wrap_on_undefined_value.zig | 0 .../stage1/obj/multiple_function_definitions.zig | 0 .../stage1/obj/negate_on_undefined_value.zig | 0 .../stage1/obj/negate_wrap_on_undefined_value.zig | 0 .../negation_overflow_in_function_evaluation.zig | 0 .../stage1/obj/nested_error_set_mismatch.zig | 0 ...o_else_prong_on_switch_on_global_error_set.zig | 0 .../stage1/obj/noalias_on_non_pointer_param.zig | 0 ...ter_eventually_is_inferred_to_become_async.zig | 0 ..._with_struct_return_value_outside_function.zig | 0 ...ression_in_struct_literal_outside_function.zig | 0 .../obj/non-const_switch_number_literal.zig | 0 ...les_of_things_that_require_const_variables.zig | 0 .../obj/non-enum_tag_type_passed_to_union.zig | 0 .../obj/non-extern_function_with_var_args.zig | 0 ..._for_loop_on_a_type_that_requires_comptime.zig | 0 ...n-integer_tag_type_to_automatic_union_enum.zig | 0 .../stage1/obj/non-pure_function_returns_type.zig | 0 ...async_function_pointer_passed_to_asyncCall.zig | 0 .../obj/non_compile_time_array_concatenation.zig | 0 .../obj/non_constant_expression_in_array_size.zig | 0 ...ror_sets_used_in_merge_error_sets_operator.zig | 0 .../stage1/obj/non_float_passed_to_floatToInt.zig | 0 .../stage1/obj/non_int_passed_to_intToFloat.zig | 0 .../stage1/obj/non_pointer_given_to_ptrToInt.zig | 0 .../stage1/obj/normal_string_with_newline.zig | 0 .../stage1/obj/offsetOf-bad_field_name.zig | 0 .../stage1/obj/offsetOf-non_struct.zig | 0 ...ity_binary_operator_allowed_for_error_sets.zig | 0 .../stage1/obj/opaque_type_with_field.zig | 0 .../optional_pointer_to_void_in_extern_struct.zig | 0 .../stage1/obj/or_on_undefined_value.zig | 0 .../stage1/obj/orelse_on_undefined_value.zig | 0 ...of_range_comptime_int_passed_to_floatToInt.zig | 0 .../obj/overflow_in_enum_value_allocation.zig | 0 .../obj/packed_union_given_enum_tag_type.zig | 0 .../packed_union_with_automatic_layout_field.zig | 0 .../stage1/obj/panic_called_at_compile_time.zig | 0 .../stage1/obj/parameter_redeclaration.zig | 0 .../stage1/obj/parameter_shadowing_global.zig | 0 .../obj/pass_const_ptr_to_mutable_ptr_fn.zig | 0 ...ng_a_not-aligned-enough_pointer_to_cmpxchg.zig | 0 .../passing_an_under-aligned_function_pointer.zig | 0 ...it_cast_const_pointer_to_mutable_C_pointer.zig | 0 .../pointer_arithmetic_on_pointer-to-array.zig | 0 ...cked_when_coercing_pointer_to_anon_literal.zig | 0 .../stage1/obj/pointer_to_noreturn.zig | 0 .../stage1/obj/popCount-non-integer.zig | 0 ...ent_bad_implicit_casting_of_anyframe_types.zig | 0 ...imitives_take_precedence_over_declarations.zig | 0 .../ptrCast_a_0_bit_type_to_a_non-_0_bit_type.zig | 0 .../obj/ptrCast_discards_const_qualifier.zig | 0 .../obj/ptrToInt_0_to_non_optional_pointer.zig | 0 .../stage1/obj/ptrToInt_on_void.zig | 0 .../stage1/obj/ptrcast_to_non-pointer.zig | 0 ...range_operator_in_switch_used_on_error_set.zig | 0 .../reading_past_end_of_pointer_casted_array.zig | 0 .../stage1/obj/recursive_inferred_error_set.zig | 0 .../stage1/obj/redefinition_of_enums.zig | 0 .../obj/redefinition_of_global_variables.zig | 0 .../stage1/obj/redefinition_of_struct.zig | 0 .../refer_to_the_type_of_a_generic_function.zig | 0 .../obj/referring_to_a_struct_that_is_invalid.zig | 0 ...rly_when_assigning_a_value_within_a_struct.zig | 0 .../obj/reify_type.Fn_with_is_generic_true.zig | 0 ...n_with_is_var_args_true_and_non-C_callconv.zig | 0 .../obj/reify_type.Fn_with_return_type_null.zig | 0 ...fy_type.Pointer_with_invalid_address_space.zig | 0 ..._exhaustive_enum_with_non-integer_tag_type.zig | 0 ...or_exhaustive_enum_with_undefined_tag_type.zig | 0 ..._type_for_exhaustive_enum_with_zero_fields.zig | 0 ...ype_for_tagged_union_with_extra_enum_field.zig | 0 ...pe_for_tagged_union_with_extra_union_field.zig | 0 .../reify_type_for_union_with_opaque_field.zig | 0 .../obj/reify_type_for_union_with_zero_fields.zig | 0 .../obj/reify_type_union_payload_is_undefined.zig | 0 .../stage1/obj/reify_type_with_Type.Int.zig | 0 .../reify_type_with_non-constant_expression.zig | 0 .../stage1/obj/reify_type_with_undefined.zig | 0 ..._incompatibility_mismatching_handle_is_ptr.zig | 0 ...ity_mismatching_handle_is_ptr_generic_call.zig | 0 .../stage1/obj/return_from_defer_expression.zig | 0 .../returning_error_from_void_async_function.zig | 0 .../obj/runtime-known_async_function_called.zig | 0 ...e-known_function_called_with_async_keyword.zig | 0 ...runtime_assignment_to_comptime_struct_type.zig | 0 .../runtime_assignment_to_comptime_union_type.zig | 0 ...me_cast_to_union_which_has_non-void_fields.zig | 0 .../runtime_index_into_comptime_type_slice.zig | 0 ...aturating_arithmetic_does_not_allow_floats.zig | 0 ...gn_does_not_allow_negative_rhs_at_comptime.zig | 0 ...hl_does_not_allow_negative_rhs_at_comptime.zig | 0 .../obj/setAlignStack_in_inline_function.zig | 0 .../obj/setAlignStack_in_naked_function.zig | 0 .../stage1/obj/setAlignStack_outside_function.zig | 0 .../stage1/obj/setAlignStack_set_twice.zig | 0 .../stage1/obj/setAlignStack_too_big.zig | 0 .../obj/setFloatMode_twice_for_same_scope.zig | 0 .../obj/setRuntimeSafety_twice_for_same_scope.zig | 0 .../obj/setting_a_section_on_a_local_variable.zig | 0 .../shift_amount_has_to_be_an_integer_type.zig | 0 .../obj/shift_by_negative_comptime_integer.zig | 0 .../obj/shift_left_assign_on_undefined_value.zig | 0 .../stage1/obj/shift_left_on_undefined_value.zig | 0 .../obj/shift_right_assign_on_undefined_value.zig | 0 .../stage1/obj/shift_right_on_undefined_value.zig | 0 .../shifting_RHS_is_log2_of_LHS_int_bit_width.zig | 0 ...hifting_without_int_type_or_comptime_known.zig | 0 .../stage1/obj/shlExact_shifts_out_1_bits.zig | 0 .../stage1/obj/shrExact_shifts_out_1_bits.zig | 0 .../stage1/obj/signed_integer_division.zig | 0 .../obj/signed_integer_remainder_division.zig | 0 .../compile_errors/stage1/obj/sizeOf_bad_type.zig | 0 .../slice_cannot_have_its_bytes_reinterpreted.zig | 0 .../obj/slice_passed_as_array_init_type.zig | 0 ...slice_passed_as_array_init_type_with_elems.zig | 0 .../stage1/obj/slice_sentinel_mismatch-1.zig | 0 .../stage1/obj/slice_sentinel_mismatch-2.zig | 0 .../obj/slicing_of_global_undefined_pointer.zig | 0 .../stage1/obj/slicing_single-item_pointer.zig | 0 .../specify_enum_tag_type_that_is_too_small.zig | 0 .../obj/specify_non-integer_enum_tag_type.zig | 0 .../stage1/obj/src_outside_function.zig | 0 .../obj/std.fmt_error_for_unused_arguments.zig | 0 ..._vector_pointer_with_unknown_runtime_index.zig | 0 ...lue_in_compile_time_variable_then_using_it.zig | 0 ...truct_depends_on_itself_via_optional_field.zig | 0 .../stage1/obj/struct_field_missing_type.zig | 0 .../stage1/obj/struct_init_syntax_for_array.zig | 0 ...th_declarations_unavailable_for_reify_type.zig | 0 .../stage1/obj/struct_with_invalid_field.zig | 0 .../stage1/obj/sub_assign_on_undefined_value.zig | 0 .../stage1/obj/sub_on_undefined_value.zig | 0 .../obj/sub_overflow_in_function_evaluation.zig | 0 .../obj/sub_wrap_assign_on_undefined_value.zig | 0 .../stage1/obj/sub_wrap_on_undefined_value.zig | 0 .../stage1/obj/suspend_inside_suspend_block.zig | 0 ...tch_expression-duplicate_enumeration_prong.zig | 0 ...licate_enumeration_prong_when_else_present.zig | 0 ...ion-duplicate_or_overlapping_integer_value.zig | 0 .../obj/switch_expression-duplicate_type.zig | 0 ...tch_expression-duplicate_type_struct_alias.zig | 0 ...witch_expression-missing_enumeration_prong.zig | 0 .../switch_expression-multiple_else_prongs.zig | 0 ...h_expression-non_exhaustive_integer_prongs.zig | 0 ...ession-switch_on_pointer_type_with_no_else.zig | 0 ...tch_expression-unreachable_else_prong_bool.zig | 0 ...tch_expression-unreachable_else_prong_enum.zig | 0 ...expression-unreachable_else_prong_range_i8.zig | 0 ...expression-unreachable_else_prong_range_u8.zig | 0 ...witch_expression-unreachable_else_prong_u1.zig | 0 ...witch_expression-unreachable_else_prong_u2.zig | 0 ...switch_on_enum_with_1_field_with_no_prongs.zig | 0 .../obj/switch_on_union_with_no_attached_enum.zig | 0 .../switch_with_invalid_expression_parameter.zig | 0 .../obj/switch_with_overlapping_case_ranges.zig | 0 ..._used_on_union_with_no_associated_enum_tag.zig | 0 .../obj/take_slice_of_invalid_dereference.zig | 0 .../taking_bit_offset_of_void_field_in_struct.zig | 0 ...taking_byte_offset_of_void_field_in_struct.zig | 0 .../stage1/obj/threadlocal_qualifier_on_const.zig | 0 .../stage1/obj/top_level_decl_dependency_loop.zig | 0 .../stage1/obj/truncate_sign_mismatch.zig | 0 .../stage1/obj/truncate_undefined_value.zig | 0 ...try_in_function_with_non_error_return_type.zig | 0 .../obj/type_checking_function_pointers.zig | 0 .../obj/type_variables_must_be_constant.zig | 0 .../stage1/obj/undeclared_identifier.zig | 0 ..._identifier_error_should_mark_fn_as_impure.zig | 0 ...undeclared_identifier_in_unanalyzed_branch.zig | 0 .../obj/undefined_as_field_type_is_rejected.zig | 0 .../stage1/obj/undefined_function_call.zig | 0 .../obj/underscore_is_not_a_declarable_symbol.zig | 0 ...underscore_should_not_be_usable_inside_for.zig | 0 ...derscore_should_not_be_usable_inside_while.zig | 0 ...ore_should_not_be_usable_inside_while_else.zig | 0 .../obj/union_auto-enum_value_already_taken.zig | 0 .../obj/union_enum_field_does_not_match_enum.zig | 0 .../obj/union_fields_with_value_assignments.zig | 0 .../stage1/obj/union_with_0_fields.zig | 0 .../obj/union_with_specified_enum_omits_field.zig | 0 ...on_with_too_small_explicit_signed_tag_type.zig | 0 ..._with_too_small_explicit_unsigned_tag_type.zig | 0 .../obj/unknown_length_pointer_to_opaque.zig | 0 .../stage1/obj/unreachable_code-double_break.zig | 0 .../obj/unreachable_code-nested_returns.zig | 0 .../stage1/obj/unreachable_code.zig | 0 .../obj/unreachable_executed_at_comptime.zig | 0 .../stage1/obj/unreachable_parameter.zig | 0 .../stage1/obj/unreachable_variable.zig | 0 .../stage1/obj/unreachable_with_return.zig | 0 ...modifier_at_start_of_asm_output_constraint.zig | 0 .../obj/unused_variable_error_on_errdefer.zig | 0 .../use_anyopaque_as_return_type_of_fn_ptr.zig | 0 ...sts_to_assign_null_to_non-nullable_pointer.zig | 0 .../use_invalid_number_literal_as_array_index.zig | 0 ...of_comptime-known_undefined_function_value.zig | 0 .../stage1/obj/use_of_undeclared_identifier.zig | 0 ...g_an_unknown_len_ptr_type_instead_of_array.zig | 0 ...lid_types_in_function_call_raises_an_error.zig | 0 .../stage1/obj/usingnamespace_with_wrong_type.zig | 0 .../stage1/obj/variable_has_wrong_type.zig | 0 ...n_inline_assembly_template_cannot_be_found.zig | 0 ...itialization_compile_error_then_referenced.zig | 0 .../stage1/obj/variable_with_type_noreturn.zig | 0 .../stage1/obj/vector_index_out_of_bounds.zig | 0 .../stage1/obj/volatile_on_global_assembly.zig | 0 ...row_is_a_compile_error_in_non-Wasm_targets.zig | 0 ...ize_is_a_compile_error_in_non-Wasm_targets.zig | 0 .../obj/while_expected_bool_got_error_union.zig | 0 .../obj/while_expected_bool_got_optional.zig | 0 .../obj/while_expected_error_union_got_bool.zig | 0 .../while_expected_error_union_got_optional.zig | 0 .../obj/while_expected_optional_got_bool.zig | 0 .../while_expected_optional_got_error_union.zig | 0 .../obj/while_loop_body_expression_ignored.zig | 0 .../stage1/obj/write_to_const_global_variable.zig | 0 .../obj/wrong_frame_type_used_for_async_call.zig | 0 .../stage1/obj/wrong_function_type.zig | 0 ...initializer_for_union_payload_of_type_type.zig | 0 .../stage1/obj/wrong_number_of_arguments.zig | 0 ...ong_number_of_arguments_for_method_fn_call.zig | 0 .../wrong_panic_signature_generic_function.zig | 0 .../wrong_panic_signature_runtime_function.zig | 0 ...ng_pointer_coerced_to_pointer_to_opaque_{}.zig | 0 .../stage1/obj/wrong_return_type_for_main.zig | 0 .../stage1/obj/wrong_size_to_an_array_literal.zig | 0 ...wrong_type_for_argument_tuple_to_asyncCall.zig | 0 .../stage1/obj/wrong_type_for_reify_type.zig | 0 .../wrong_type_for_result_ptr_to_asyncCall.zig | 0 .../stage1/obj/wrong_type_passed_to_panic.zig | 0 .../stage1/obj/wrong_type_to_hasField.zig | 0 ...ypes_given_to_atomic_order_args_in_cmpxchg.zig | 0 .../stage1/obj/wrong_types_given_to_export.zig | 0 .../stage1/test/access_invalid_typeInfo_decl.zig | 0 .../stage1/test/alignCast_of_zero_sized_types.zig | 0 .../compile_errors/stage1/test/bad_splat_type.zig | 0 .../test/binary_OR_operator_on_error_sets.zig | 0 ...ejects_non_comptime-known_fn-always_inline.zig | 0 ...rejects_non_comptime-known_fn-compile_time.zig | 0 ...etween_optional_T_where_T_is_not_a_pointer.zig | 0 .../test/combination_of_nosuspend_and_async.zig | 0 ...rison_of_non-tagged_union_and_enum_literal.zig | 0 .../comptime_vector_overflow_shows_the_index.zig | 0 ...uplicate_field_in_anonymous_struct_literal.zig | 0 ...ruct_initializer_doesnt_crash_the_compiler.zig | 0 .../errors_in_for_loop_bodies_are_propagated.zig | 0 .../stage1/test/export_with_empty_name_string.zig | 0 .../test/helpful_return_type_error_message.zig | 0 ...int-float_conversion_to_comptime_int-float.zig | 0 .../stage1/test/invalid_assignments.zig | 0 .../stage1/test/invalid_float_casts.zig | 0 .../stage1/test/invalid_int_casts.zig | 0 .../test/invalid_non-exhaustive_enum_to_union.zig | 0 .../test/invalid_pointer_with_reify_type.zig | 0 .../compile_errors/stage1/test/nested_vectors.zig | 0 ...on-exhaustive_enum_marker_assigned_a_value.zig | 0 .../stage1/test/non-exhaustive_enums.zig | 0 .../stage1/test/not_an_enum_type.zig | 0 ...ed_struct_with_fields_of_not_allowed_types.zig | 0 .../ptrToInt_with_pointer_to_zero-sized_type.zig | 0 .../stage1/test/reassign_to_array_parameter.zig | 0 .../stage1/test/reassign_to_slice_parameter.zig | 0 .../stage1/test/reassign_to_struct_parameter.zig | 0 .../stage1/test/reference_to_const_data.zig | 0 .../reify_typeOf_with_incompatible_arguments.zig | 0 .../test/reify_typeOf_with_no_arguments.zig | 0 ...ject_extern_function_definitions_with_body.zig | 0 .../reject_extern_variables_with_initializers.zig | 0 ...ction_returning_type_crashes_compiler_2655.zig | 0 .../stage1/test/return_invalid_type_from_test.zig | 0 .../shift_on_type_with_non-power-of-two_size.zig | 0 ...th_selected_index_past_first_vector_length.zig | 0 .../switch_ranges_endpoints_are_validated.zig | 0 ...witching_with_exhaustive_enum_has___prong_.zig | 0 .../test/switching_with_non-exhaustive_enums.zig | 0 ...me_on_invalid_value_of_non-exhaustive_enum.zig | 0 .../type_mismatch_in_C_prototype_with_varargs.zig | 0 .../type_mismatch_with_tuple_concatenation.zig | 0 .../stage2/comptime_unreachable.zig | 0 ...inside_comptime_function_has_compile_error.zig | 0 .../stage2/duplicate-unused_labels.zig | 0 .../stage2/embed_outside_package.zig | 0 .../stage2/import_outside_package.zig | 0 .../compile_errors/stage2/out_of_bounds_index.zig | 0 .../stage2/slice_of_null_pointer.zig | 0 .../stage2/struct_duplicate_field_name.zig | 0 .../stage2/union_access_of_inactive_field.zig | 0 .../stage2/union_duplicate_enum_field.zig | 0 .../stage2/union_duplicate_field_definition.zig | 0 .../stage2/union_enum_field_missing.zig | 0 .../compile_errors/stage2/union_extra_field.zig | 0 .../stage2/union_runtime_coercion_from_enum.zig | 0 test/{incremental => cases}/compile_log.0.zig | 0 test/{incremental => cases}/compile_log.1.zig | 0 .../{incremental => cases}/double_ampersand.0.zig | 0 .../{incremental => cases}/double_ampersand.1.zig | 0 .../{incremental => cases}/double_ampersand.2.zig | 0 test/{incremental => cases}/enum_values.0.zig | 0 test/{incremental => cases}/enum_values.1.zig | 0 .../extern_variable_has_no_type.0.zig | 0 .../extern_variable_has_no_type.1.zig | 0 test/{incremental => cases}/function_calls.0.zig | 0 test/{incremental => cases}/function_calls.1.zig | 0 test/{incremental => cases}/function_calls.2.zig | 0 test/{incremental => cases}/function_calls.3.zig | 0 .../function_redeclaration.zig | 0 .../global_variable_redeclaration.zig | 0 .../inner_func_accessing_outer_var.zig | 0 test/{incremental => cases}/int_to_ptr.0.zig | 0 test/{incremental => cases}/int_to_ptr.1.zig | 0 .../{incremental => cases}/large_add_function.zig | 0 ..._access_chaining_pointer_to_optional_array.zig | 0 ...aces_pointer_access_chaining_array_pointer.zig | 0 ...ess_spaces_pointer_access_chaining_complex.zig | 0 ...ces_pointer_access_chaining_struct_pointer.zig | 0 .../llvm/any_typed_null_to_any_typed_optional.zig | 0 test/{incremental => cases}/llvm/blocks.zig | 0 ...ough_multiple_pointers_with_address_spaces.zig | 0 ..._segment_address_space_reading_and_writing.zig | 0 test/{incremental => cases}/llvm/for_loop.zig | 0 test/{incremental => cases}/llvm/hello_world.zig | 0 .../llvm/invalid_address_space_coercion.zig | 0 ...s_space_when_taking_address_of_dereference.zig | 0 .../{incremental => cases}/llvm/nested_blocks.zig | 0 test/{incremental => cases}/llvm/optionals.zig | 0 .../llvm/pointer_keeps_address_space.zig | 0 ...s_space_when_taking_address_of_dereference.zig | 0 ..._address_space_coerces_to_implicit_pointer.zig | 0 .../pointer_with_different_address_spaces.zig | 0 .../pointers_with_different_address_spaces.zig | 0 test/{incremental => cases}/llvm/rem.zig | 0 .../llvm/shift_right_plus_left.0.zig | 0 .../llvm/shift_right_plus_left.1.zig | 0 .../llvm/simple_addition_and_subtraction.zig | 0 .../llvm/simple_if_statement.zig | 0 test/{incremental => cases}/llvm/while_loops.zig | 0 .../lower_unnamed_consts_structs.0.zig | 0 .../lower_unnamed_consts_structs.1.zig | 0 .../lower_unnamed_consts_structs.2.zig | 0 .../{incremental => cases}/merge_error_sets.0.zig | 0 .../{incremental => cases}/merge_error_sets.1.zig | 0 ...tiplying_numbers_at_runtime_and_comptime.0.zig | 0 ...tiplying_numbers_at_runtime_and_comptime.1.zig | 0 ...tiplying_numbers_at_runtime_and_comptime.2.zig | 0 .../{incremental => cases}/non_leaf_functions.zig | 0 .../{incremental => cases}/optional_payload.0.zig | 0 .../{incremental => cases}/optional_payload.1.zig | 0 .../{incremental => cases}/optional_payload.2.zig | 0 .../{incremental => cases}/optional_payload.3.zig | 0 .../orelse_at_comptime.0.zig | 0 .../orelse_at_comptime.1.zig | 0 .../passing_u0_to_function.zig | 0 test/{incremental => cases}/plan9/exit.zig | 0 .../plan9/hello_world_with_updates.0.zig | 0 .../plan9/hello_world_with_updates.1.zig | 0 .../recursive_fibonacci.zig | 0 .../recursive_inline_function.0.zig | 0 .../recursive_inline_function.1.zig | 0 .../redundant_comptime.0.zig | 0 .../redundant_comptime.1.zig | 0 test/{incremental => cases}/returns_in_try.zig | 0 .../riscv64-linux/hello_world_with_updates.0.zig | 0 .../riscv64-linux/hello_world_with_updates.1.zig | 0 .../runtime_bitwise_and.zig | 0 .../{incremental => cases}/runtime_bitwise_or.zig | 0 ...return_values_in_callee_preserved_register.zig | 0 ...tting_an_address_space_on_a_local_variable.zig | 0 .../sparcv9-linux/hello_world.zig | 0 .../try_in_comptime_in_struct_in_test.zig | 0 test/{incremental => cases}/type_of.0.zig | 0 test/{incremental => cases}/type_of.1.zig | 0 test/{incremental => cases}/type_of.2.zig | 0 test/{incremental => cases}/unused_labels.0.zig | 0 test/{incremental => cases}/unused_labels.1.zig | 0 test/{incremental => cases}/unused_labels.2.zig | 0 test/{incremental => cases}/unused_labels.3.zig | 0 test/{incremental => cases}/unused_vars.zig | 0 .../variable_shadowing.0.zig | 0 .../variable_shadowing.1.zig | 0 .../variable_shadowing.2.zig | 0 .../variable_shadowing.3.zig | 0 .../variable_shadowing.4.zig | 0 .../variable_shadowing.5.zig | 0 .../variable_shadowing.6.zig | 0 .../variable_shadowing.7.zig | 0 .../variable_shadowing.8.zig | 0 .../variable_shadowing.9.zig | 0 .../wasm-wasi/conditions.0.zig | 0 .../wasm-wasi/conditions.1.zig | 0 .../wasm-wasi/conditions.2.zig | 0 .../wasm-wasi/conditions.3.zig | 0 .../wasm-wasi/conditions.4.zig | 0 .../wasm-wasi/conditions.5.zig | 0 .../wasm-wasi/error_unions.0.zig | 0 .../wasm-wasi/error_unions.1.zig | 0 .../wasm-wasi/error_unions.2.zig | 0 .../wasm-wasi/error_unions.3.zig | 0 .../wasm-wasi/error_unions.4.zig | 0 .../wasm-wasi/error_unions.5.zig | 0 .../{incremental => cases}/wasm-wasi/locals.0.zig | 0 .../{incremental => cases}/wasm-wasi/locals.1.zig | 0 .../wasm-wasi/optionals.0.zig | 0 .../wasm-wasi/optionals.1.zig | 0 .../wasm-wasi/optionals.2.zig | 0 .../wasm-wasi/optionals.3.zig | 0 .../wasm-wasi/optionals.4.zig | 0 .../wasm-wasi/pointers.0.zig | 0 .../wasm-wasi/pointers.1.zig | 0 .../wasm-wasi/structs.0.zig | 0 .../wasm-wasi/structs.1.zig | 0 .../wasm-wasi/structs.2.zig | 0 .../wasm-wasi/structs.3.zig | 0 .../wasm-wasi/structs.4.zig | 0 .../{incremental => cases}/wasm-wasi/switch.0.zig | 0 .../{incremental => cases}/wasm-wasi/switch.1.zig | 0 .../{incremental => cases}/wasm-wasi/switch.2.zig | 0 .../{incremental => cases}/wasm-wasi/switch.3.zig | 0 .../wasm-wasi/while_loops.0.zig | 0 .../wasm-wasi/while_loops.1.zig | 0 .../wasm-wasi/while_loops.2.zig | 0 .../x86_64-linux/assert_function.0.zig | 0 .../x86_64-linux/assert_function.1.zig | 0 .../x86_64-linux/assert_function.10.zig | 0 .../x86_64-linux/assert_function.11.zig | 0 .../x86_64-linux/assert_function.12.zig | 0 .../x86_64-linux/assert_function.13.zig | 0 .../x86_64-linux/assert_function.14.zig | 0 .../x86_64-linux/assert_function.15.zig | 0 .../x86_64-linux/assert_function.16.zig | 0 .../x86_64-linux/assert_function.17.zig | 0 .../x86_64-linux/assert_function.18.zig | 0 .../x86_64-linux/assert_function.2.zig | 0 .../x86_64-linux/assert_function.3.zig | 0 .../x86_64-linux/assert_function.4.zig | 0 .../x86_64-linux/assert_function.5.zig | 0 .../x86_64-linux/assert_function.6.zig | 0 .../x86_64-linux/assert_function.7.zig | 0 .../x86_64-linux/assert_function.8.zig | 0 .../x86_64-linux/assert_function.9.zig | 0 .../x86_64-linux/comptime_var.0.zig | 0 .../x86_64-linux/comptime_var.1.zig | 0 .../x86_64-linux/comptime_var.2.zig | 0 .../x86_64-linux/comptime_var.3.zig | 0 .../x86_64-linux/comptime_var.4.zig | 0 .../x86_64-linux/comptime_var.5.zig | 0 .../x86_64-linux/comptime_var.6.zig | 0 .../x86_64-linux/hello_world_with_updates.0.zig | 0 .../x86_64-linux/hello_world_with_updates.1.zig | 0 .../x86_64-linux/hello_world_with_updates.2.zig | 0 .../x86_64-linux/hello_world_with_updates.3.zig | 0 .../x86_64-linux/hello_world_with_updates.4.zig | 0 .../x86_64-linux/hello_world_with_updates.5.zig | 0 .../x86_64-linux/inline_assembly.0.zig | 0 .../x86_64-linux/inline_assembly.1.zig | 0 .../x86_64-linux/inline_assembly.2.zig | 0 .../x86_64-linux/inline_assembly.3.zig | 0 .../only_1_function_and_it_gets_updated.0.zig | 0 .../only_1_function_and_it_gets_updated.1.zig | 0 .../x86_64-macos/assert_function.0.zig | 0 .../x86_64-macos/assert_function.1.zig | 0 .../x86_64-macos/assert_function.10.zig | 0 .../x86_64-macos/assert_function.11.zig | 0 .../x86_64-macos/assert_function.12.zig | 0 .../x86_64-macos/assert_function.13.zig | 0 .../x86_64-macos/assert_function.14.zig | 0 .../x86_64-macos/assert_function.15.zig | 0 .../x86_64-macos/assert_function.16.zig | 0 .../x86_64-macos/assert_function.17.zig | 0 .../x86_64-macos/assert_function.18.zig | 0 .../x86_64-macos/assert_function.2.zig | 0 .../x86_64-macos/assert_function.3.zig | 0 .../x86_64-macos/assert_function.4.zig | 0 .../x86_64-macos/assert_function.5.zig | 0 .../x86_64-macos/assert_function.6.zig | 0 .../x86_64-macos/assert_function.7.zig | 0 .../x86_64-macos/assert_function.8.zig | 0 .../x86_64-macos/assert_function.9.zig | 0 .../x86_64-macos/comptime_var.0.zig | 0 .../x86_64-macos/comptime_var.1.zig | 0 .../x86_64-macos/comptime_var.2.zig | 0 .../x86_64-macos/comptime_var.3.zig | 0 .../x86_64-macos/comptime_var.4.zig | 0 .../x86_64-macos/comptime_var.5.zig | 0 .../x86_64-macos/comptime_var.6.zig | 0 .../x86_64-macos/hello_world_with_updates.0.zig | 0 .../x86_64-macos/hello_world_with_updates.1.zig | 0 .../x86_64-macos/hello_world_with_updates.2.zig | 0 .../x86_64-macos/hello_world_with_updates.3.zig | 0 .../x86_64-macos/hello_world_with_updates.4.zig | 0 .../x86_64-macos/hello_world_with_updates.5.zig | 0 .../x86_64-macos/hello_world_with_updates.6.zig | 0 952 files changed, 2 insertions(+), 15 deletions(-) rename test/{incremental => cases}/aarch64-linux/conditional_branches.0.zig (100%) rename test/{incremental => cases}/aarch64-linux/conditional_branches.1.zig (100%) rename test/{incremental => cases}/aarch64-linux/hello_world_with_updates.0.zig (100%) rename test/{incremental => cases}/aarch64-linux/hello_world_with_updates.1.zig (100%) rename test/{incremental => cases}/aarch64-linux/hello_world_with_updates.2.zig (100%) rename test/{incremental => cases}/aarch64-macos/hello_world_with_updates.0.zig (100%) rename test/{incremental => cases}/aarch64-macos/hello_world_with_updates.1.zig (100%) rename test/{incremental => cases}/aarch64-macos/hello_world_with_updates.2.zig (100%) rename test/{incremental => cases}/aarch64-macos/hello_world_with_updates.3.zig (100%) rename test/{incremental => cases}/aarch64-macos/hello_world_with_updates.4.zig (100%) rename test/{incremental => cases}/aarch64-macos/hello_world_with_updates.5.zig (100%) rename test/{incremental => cases}/aarch64-macos/hello_world_with_updates.6.zig (100%) rename test/{incremental => cases}/adding_numbers_at_runtime_and_comptime.0.zig (100%) rename test/{incremental => cases}/adding_numbers_at_runtime_and_comptime.1.zig (100%) rename test/{incremental => cases}/adding_numbers_at_runtime_and_comptime.2.zig (100%) rename test/{incremental => cases}/ambiguous_reference.zig (100%) rename test/{incremental => cases}/arm-linux/arithmetic_operations.0.zig (100%) rename test/{incremental => cases}/arm-linux/arithmetic_operations.1.zig (100%) rename test/{incremental => cases}/arm-linux/arithmetic_operations.2.zig (100%) rename test/{incremental => cases}/arm-linux/arithmetic_operations.3.zig (100%) rename test/{incremental => cases}/arm-linux/arithmetic_operations.4.zig (100%) rename test/{incremental => cases}/arm-linux/arithmetic_operations.5.zig (100%) rename test/{incremental => cases}/arm-linux/arithmetic_operations.6.zig (100%) rename test/{incremental => cases}/arm-linux/errors.0.zig (100%) rename test/{incremental => cases}/arm-linux/errors.1.zig (100%) rename test/{incremental => cases}/arm-linux/errors.2.zig (100%) rename test/{incremental => cases}/arm-linux/errors.3.zig (100%) rename test/{incremental => cases}/arm-linux/function_pointers.zig (100%) rename test/{incremental => cases}/arm-linux/hello_world_with_updates.0.zig (100%) rename test/{incremental => cases}/arm-linux/hello_world_with_updates.1.zig (100%) rename test/{incremental => cases}/arm-linux/hello_world_with_updates.2.zig (100%) rename test/{incremental => cases}/arm-linux/parameters_and_return_values.0.zig (100%) rename test/{incremental => cases}/arm-linux/parameters_and_return_values.1.zig (100%) rename test/{incremental => cases}/arm-linux/print_u32s.zig (100%) rename test/{incremental => cases}/arm-linux/spilling_registers.0.zig (100%) rename test/{incremental => cases}/arm-linux/spilling_registers.1.zig (100%) rename test/{incremental => cases}/bad_inferred_variable_type.zig (100%) rename test/{incremental => cases}/binary_operands.0.zig (100%) rename test/{incremental => cases}/binary_operands.1.zig (100%) rename test/{incremental => cases}/binary_operands.10.zig (100%) rename test/{incremental => cases}/binary_operands.11.zig (100%) rename test/{incremental => cases}/binary_operands.12.zig (100%) rename test/{incremental => cases}/binary_operands.13.zig (100%) rename test/{incremental => cases}/binary_operands.14.zig (100%) rename test/{incremental => cases}/binary_operands.15.zig (100%) rename test/{incremental => cases}/binary_operands.16.zig (100%) rename test/{incremental => cases}/binary_operands.17.zig (100%) rename test/{incremental => cases}/binary_operands.18.zig (100%) rename test/{incremental => cases}/binary_operands.19.zig (100%) rename test/{incremental => cases}/binary_operands.2.zig (100%) rename test/{incremental => cases}/binary_operands.20.zig (100%) rename test/{incremental => cases}/binary_operands.21.zig (100%) rename test/{incremental => cases}/binary_operands.22.zig (100%) rename test/{incremental => cases}/binary_operands.23.zig (100%) rename test/{incremental => cases}/binary_operands.24.zig (100%) rename test/{incremental => cases}/binary_operands.25.zig (100%) rename test/{incremental => cases}/binary_operands.3.zig (100%) rename test/{incremental => cases}/binary_operands.4.zig (100%) rename test/{incremental => cases}/binary_operands.5.zig (100%) rename test/{incremental => cases}/binary_operands.6.zig (100%) rename test/{incremental => cases}/binary_operands.7.zig (100%) rename test/{incremental => cases}/binary_operands.8.zig (100%) rename test/{incremental => cases}/binary_operands.9.zig (100%) rename test/{incremental => cases}/break_continue.0.zig (100%) rename test/{incremental => cases}/break_continue.1.zig (100%) rename test/{incremental => cases}/break_continue.2.zig (100%) rename test/{incremental => cases}/break_continue.3.zig (100%) rename test/{incremental => cases}/catch_at_comptime.0.zig (100%) rename test/{incremental => cases}/catch_at_comptime.1.zig (100%) rename test/{incremental => cases}/catch_at_comptime.2.zig (100%) rename test/{incremental => cases}/catch_at_comptime.3.zig (100%) rename test/{incremental => cases}/catch_at_comptime.4.zig (100%) rename test/{incremental => cases}/compile_error.zig (100%) rename test/{incremental => cases}/compile_error_in_inline_fn_call_fixed.0.zig (100%) rename test/{incremental => cases}/compile_error_in_inline_fn_call_fixed.1.zig (100%) rename test/{ => cases}/compile_errors/stage1/exe/main_missing_name.zig (100%) rename test/{ => cases}/compile_errors/stage1/exe/missing_main_fn_in_executable.zig (100%) rename test/{ => cases}/compile_errors/stage1/exe/private_main_fn.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/C_pointer_to_anyopaque.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/Frame_of_generic_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/Issue_6823_dont_allow_._to_be_followed_by_.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/Issue_9165_windows_tcp_server_compilation_error.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/access_non-existent_member_of_error_set.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/accessing_runtime_parameter_from_outer_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/add_assign_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/add_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/add_overflow_in_function_evaluation.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/add_wrap_assign_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/add_wrap_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/addition_with_non_numbers.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/address_of_number_literal.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/alignCast_expects_pointer_or_slice.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/align_n_expr_function_pointers_is_a_compile_error.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/aligned_variable_of_zero-bit_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/alignment_of_enum_field_specified.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/ambiguous_decl_reference.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/and_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/array_access_of_non_array.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/array_access_of_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/array_access_of_undeclared_identifier.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/array_access_with_non_integer_index.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/array_concatenation_with_wrong_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/array_in_c_exported_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/asm_at_compile_time.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/assign_inline_fn_to_non-comptime_var.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/assign_null_to_non-optional_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/assign_through_constant_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/assign_through_constant_slice.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/assign_to_constant_field.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/assign_to_constant_variable.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/assign_to_invalid_dereference.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/assign_too_big_number_to_u16.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/assign_unreachable.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/async_function_depends_on_its_own_frame.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/async_function_indirectly_depends_on_its_own_frame.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/atomic_orderings_of_fence_Acquire_or_stricter.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/atomicrmw_with_bool_op_not_.Xchg.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/atomicrmw_with_enum_op_not_.Xchg.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/attempt_to_cast_enum_literal_to_error.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/attempt_to_close_over_comptime_variable_from_outer_scope.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/attempt_to_create_17_bit_float_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/attempt_to_use_0_bit_type_in_extern_fn.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/attempted_double_ampersand.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/attempted_double_pipe_on_boolean_values.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/attempted_implicit_cast_from_T_to_slice_const_T.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_array_len_1_T.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_sliceT.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bad_alignCast_at_comptime.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bad_alignment_in_asynccall.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bad_alignment_in_implicit_cast_from_array_pointer_to_slice.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bad_alignment_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bad_import.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bad_usage_of_call.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bin_and_assign_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bin_and_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bin_not_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bin_or_assign_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bin_or_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bin_xor_assign_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bin_xor_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/binary_not_on_number_literal.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bitCast_same_size_but_bit_count_mismatch.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bitCast_to_enum_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bitCast_with_different_sizes_inside_an_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bit_shifting_only_works_on_integer_types.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bogus_compile_var.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bogus_method_call_on_slice.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/bool_not_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/branch_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/cImport_with_bogus_include.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/call_assigned_to_constant.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/call_with_new_stack_on_unsupported_target.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/callconv_apcs_aapcs_aapcsvfp_on_unsupported_platform.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/callconv_interrupt_on_unsupported_platform.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/callconv_signal_on_unsupported_platform.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-0.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-1.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/callconv_vectorcall_on_unsupported_platform.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/calling_a_generic_function_only_known_at_runtime.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/calling_function_with_naked_calling_convention.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/calling_var_args_extern_function_passing_array_instead_of_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/cannot_break_out_of_defer_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/cannot_continue_out_of_defer_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/capture_group_on_switch_prong_with_incompatible_payload_types.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/cast_enum_literal_to_enum_but_it_doesnt_match.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/cast_error_union_of_global_error_set_to_error_union_of_smaller_error_set.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/cast_global_error_set_to_error_set.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/cast_negative_integer_literal_to_usize.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/cast_negative_value_to_unsigned_integer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/cast_unreachable.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/casting_bit_offset_pointer_to_regular_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/catch_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/chained_comparison_operators.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/cmpxchg_with_float.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/colliding_invalid_top_level_functions.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/compare_optional_to_non-optional_with_invalid_types.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comparing_a_non-optional_pointer_against_null.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comparing_against_undefined_produces_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comparison_operators_with_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comparison_with_error_union_and_error_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/compile-time_division_by_zero.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/compile-time_remainder_division_by_zero.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/compileError_shows_traceback_of_references_that_caused_it.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/compile_error_in_struct_init_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/compile_error_when_evaluating_return_type_of_inferred_error_set.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/compile_log.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/compile_log_a_pointer_to_an_opaque_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/compile_log_statement_warning_deduplication_in_generic_fn.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/compile_time_division_by_zero.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_cast_enum_to_union_but_field_has_payload.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_float_in_asm_input.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_implicit_cast_f64_to_f32.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_int_in_asm_input.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_ptrcast_of_zero-sized_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_target-sentinel.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_terminated.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_slice_of_an_undefined_slice.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_slice_of_undefined_pointer_non-zero_len.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/comptime_struct_field_no_init_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/const_frame_cast_to_anyframe.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/const_is_a_statement_not_an_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/container_init_with_non-type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/control_flow_uses_comptime_var_at_runtime.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/control_reaches_end_of_non-void_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/declaration_between_fields.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/declaration_with_same_name_as_primitive_must_use_special_syntax.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/deduplicate_undeclared_identifier.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/deref_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/dereference_an_array.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/direct_struct_loop.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/directly_embedding_opaque_type_in_struct_and_union.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/discarding_error_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/div_assign_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/div_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/division_by_zero.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/dont_implicit_cast_double_pointer_to_anyopaque.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/double_optional_on_main_return_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/duplicate_boolean_switch_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/duplicate_enum_field.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/duplicate_error_in_switch.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/duplicate_error_value_in_error_set.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/duplicate_field_in_struct_value_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/duplicate_struct_field.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/duplicate_union_field.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/embedFile_with_bogus_file.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/empty_for_loop_body.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/empty_if_body.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/empty_switch_on_an_integer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/empty_while_loop_body.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/enum_field_value_references_enum.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/enum_in_field_count_range_but_not_matching_tag.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/enum_value_already_taken.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/enum_with_0_fields.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/enum_with_declarations_unavailable_for_reify_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/error_equality_but_sets_have_no_common_members.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/error_not_handled_in_switch.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/error_note_for_function_parameter_incompatibility.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/error_union_operator_with_non_error_set_LHS.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/error_when_evaluating_return_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/exceeded_maximum_bit_width_of_integer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/explicitly_casting_non_tag_type_to_enum.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/export_generic_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/exported_async_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/exported_enum_without_explicit_integer_tag_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/extern_function_pointer_mismatch.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/extern_struct_with_non-extern-compatible_integer_tag_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/extern_union_field_missing_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/extern_union_given_enum_tag_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/extern_variable_has_no_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/fieldParentPtr-bad_field_name.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/fieldParentPtr-comptime_wrong_field_index.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/fieldParentPtr-field_pointer_is_not_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/fieldParentPtr-non_struct.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/field_access_of_opaque_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/field_access_of_slices.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/field_access_of_unknown_length_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/field_type_supplied_in_an_enum.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/floatToInt_comptime_safety.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/float_literal_too_large_error.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/float_literal_too_small_error_denormal.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/for_loop_body_expression_ignored.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/frame_called_outside_of_function_definition.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/frame_causes_function_to_be_async.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/function_alignment_non_power_of_2.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/function_call_assigned_to_incorrect_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/function_parameter_is_opaque.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/function_prototype_with_no_body.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/function_returning_opaque_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/function_with_ccc_indirectly_calling_async_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/function_with_invalid_return_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/generic_fn_as_parameter_without_comptime_keyword.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/generic_function_call_assigned_to_incorrect_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/generic_function_instance_with_non-constant_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/generic_function_returning_opaque_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/generic_function_where_return_type_is_self-referenced.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/global_variable_alignment_non_power_of_2.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/global_variable_initializer_must_be_constant_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/hasDecl_with_non-container.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/if_condition_is_bool_not_int.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/ignored_assert-err-ok_return_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/ignored_comptime_statement_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/ignored_comptime_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/ignored_deferred_function_call.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/ignored_deferred_statement_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/ignored_expression_in_while_continuation.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/ignored_return_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/ignored_statement_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/illegal_comparison_of_types.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_cast_const_array_to_mutable_slice.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_cast_from_array_to_mutable_slice.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_cast_from_f64_to_f32.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_cast_of_error_set_not_a_subset.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_casting_C_pointers_which_would_mess_up_null_semantics.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_casting_null_c_pointer_to_zig_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_casting_too_big_integers_to_C_pointers.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_casting_undefined_c_pointer_to_zig_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_dependency_on_libc.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-block_expr.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-block_statement.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-comptime_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-comptime_statement.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-defer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-for_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-for_statement.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_statement.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-if-else-if_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-if-else-if_statement.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-if-else_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-if-else_statement.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-if_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-if_statement.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-test_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-test_statement.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-while-continue_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-while-continue_statement.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-while_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicit_semicolon-while_statement.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicitly_casting_enum_to_tag_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicitly_increasing_pointer_alignment.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/implicitly_increasing_slice_alignment.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/import_outside_package_path.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/incompatible_sentinels.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/incorrect_return_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/increase_pointer_alignment_in_ptrCast.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/indexing_a_undefined_slice_at_comptime.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/indexing_an_array_of_size_zero.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/indexing_an_array_of_size_zero_with_runtime_index.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/indexing_single-item_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/indirect_recursion_of_async_functions_detected.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/indirect_struct_loop.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/inferred_array_size_invalid_here.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/inferring_error_set_of_function_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/initializing_array_with_struct_syntax.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/int_to_err_global_invalid_number.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/int_to_err_non_global_invalid_number.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/int_to_ptr_of_0_bits.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/integer_cast_truncates_bits.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/integer_overflow_error.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/integer_underflow_error.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_break_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_builtin_fn.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_cast_from_integral_type_to_enum.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_comparison_for_function_pointers.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_continue_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_empty_unicode_escape.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_exponent_in_float_literal-1.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_exponent_in_float_literal-2.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_field_access_in_comptime.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_field_in_struct_value_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_float_literal.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_legacy_unicode_escape.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_maybe_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_member_of_builtin_enum.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_multiple_dereferences.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_optional_type_in_extern_struct.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_pointer_for_var_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_pointer_syntax.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_shift_amount_error.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_struct_field.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_suspend_in_exported_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_type_used_in_array_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-1.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-10.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-11.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-12.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-13.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-14.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-2.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-3.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-4.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-5.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-6.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-7.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-9.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-1.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-2.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-3.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-4.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/invalid_union_field_access_in_comptime.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/issue_2032_compile_diagnostic_string_for_top_level_decl_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/issue_2687_coerce_from_undefined_array_pointer_to_slice.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/issue_4207_coerce_from_non-terminated-slice_to_terminated-pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/issue_5618_coercion_of_optional_anyopaque_to_anyopaque_must_fail.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/issue_7810-comptime_slice-len_increment_beyond_bounds.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/issue_9346_return_outside_of_function_scope.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/labeled_break_not_found.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/labeled_continue_not_found.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/lazy_pointer_with_undefined_element_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/libc_headers_note.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/load_vector_pointer_with_unknown_runtime_index.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/local_shadows_global_that_occurs_later.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/local_variable_redeclaration.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/local_variable_redeclares_parameter.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/local_variable_shadowing_global.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/locally_shadowing_a_primitive_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/main_function_with_bogus_args_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/method_call_with_first_arg_type_primitive.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/method_call_with_first_arg_type_wrong_container.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/missing_boolean_switch_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/missing_const_in_slice_with_nested_array_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/missing_else_clause.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/missing_field_in_struct_value_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/missing_function_call_param.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/missing_function_name.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/missing_param_name.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/missing_parameter_name_of_generic_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/missing_result_type_for_phi_node.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/misspelled_type_with_pointer_only_reference.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/mod_assign_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/mod_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/mul_overflow_in_function_evaluation.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/mult_assign_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/mult_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/mult_wrap_assign_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/mult_wrap_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/multiple_function_definitions.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/negate_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/negate_wrap_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/negation_overflow_in_function_evaluation.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/nested_error_set_mismatch.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/no_else_prong_on_switch_on_global_error_set.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/noalias_on_non_pointer_param.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/non-async_function_pointer_eventually_is_inferred_to_become_async.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/non-const_expression_function_call_with_struct_return_value_outside_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/non-const_expression_in_struct_literal_outside_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/non-const_switch_number_literal.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/non-const_variables_of_things_that_require_const_variables.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/non-enum_tag_type_passed_to_union.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/non-extern_function_with_var_args.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/non-inline_for_loop_on_a_type_that_requires_comptime.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/non-integer_tag_type_to_automatic_union_enum.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/non-pure_function_returns_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/non_async_function_pointer_passed_to_asyncCall.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/non_compile_time_array_concatenation.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/non_constant_expression_in_array_size.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/non_error_sets_used_in_merge_error_sets_operator.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/non_float_passed_to_floatToInt.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/non_int_passed_to_intToFloat.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/non_pointer_given_to_ptrToInt.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/normal_string_with_newline.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/offsetOf-bad_field_name.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/offsetOf-non_struct.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/only_equality_binary_operator_allowed_for_error_sets.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/opaque_type_with_field.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/optional_pointer_to_void_in_extern_struct.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/or_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/orelse_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/out_of_range_comptime_int_passed_to_floatToInt.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/packed_union_given_enum_tag_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/packed_union_with_automatic_layout_field.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/panic_called_at_compile_time.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/parameter_redeclaration.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/parameter_shadowing_global.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/pass_const_ptr_to_mutable_ptr_fn.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/passing_an_under-aligned_function_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/pointer_arithmetic_on_pointer-to-array.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/pointer_to_noreturn.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/popCount-non-integer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/prevent_bad_implicit_casting_of_anyframe_types.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/primitives_take_precedence_over_declarations.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/ptrCast_a_0_bit_type_to_a_non-_0_bit_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/ptrCast_discards_const_qualifier.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/ptrToInt_0_to_non_optional_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/ptrToInt_on_void.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/ptrcast_to_non-pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/range_operator_in_switch_used_on_error_set.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/reading_past_end_of_pointer_casted_array.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/recursive_inferred_error_set.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/redefinition_of_enums.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/redefinition_of_global_variables.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/redefinition_of_struct.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/refer_to_the_type_of_a_generic_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/referring_to_a_struct_that_is_invalid.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/reify_type.Fn_with_is_generic_true.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/reify_type.Fn_with_return_type_null.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/reify_type.Pointer_with_invalid_address_space.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_zero_fields.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_enum_field.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_union_field.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/reify_type_for_union_with_opaque_field.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/reify_type_for_union_with_zero_fields.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/reify_type_union_payload_is_undefined.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/reify_type_with_Type.Int.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/reify_type_with_non-constant_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/reify_type_with_undefined.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/return_from_defer_expression.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/returning_error_from_void_async_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/runtime-known_async_function_called.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/runtime-known_function_called_with_async_keyword.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/runtime_assignment_to_comptime_struct_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/runtime_assignment_to_comptime_union_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/runtime_cast_to_union_which_has_non-void_fields.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/runtime_index_into_comptime_type_slice.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/saturating_arithmetic_does_not_allow_floats.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/saturating_shl_assign_does_not_allow_negative_rhs_at_comptime.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/saturating_shl_does_not_allow_negative_rhs_at_comptime.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/setAlignStack_in_inline_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/setAlignStack_in_naked_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/setAlignStack_outside_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/setAlignStack_set_twice.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/setAlignStack_too_big.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/setFloatMode_twice_for_same_scope.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/setRuntimeSafety_twice_for_same_scope.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/setting_a_section_on_a_local_variable.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/shift_amount_has_to_be_an_integer_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/shift_by_negative_comptime_integer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/shift_left_assign_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/shift_left_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/shift_right_assign_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/shift_right_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/shifting_RHS_is_log2_of_LHS_int_bit_width.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/shifting_without_int_type_or_comptime_known.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/shlExact_shifts_out_1_bits.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/shrExact_shifts_out_1_bits.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/signed_integer_division.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/signed_integer_remainder_division.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/sizeOf_bad_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/slice_cannot_have_its_bytes_reinterpreted.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/slice_passed_as_array_init_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/slice_passed_as_array_init_type_with_elems.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/slice_sentinel_mismatch-1.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/slice_sentinel_mismatch-2.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/slicing_of_global_undefined_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/slicing_single-item_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/specify_non-integer_enum_tag_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/src_outside_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/std.fmt_error_for_unused_arguments.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/store_vector_pointer_with_unknown_runtime_index.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/storing_runtime_value_in_compile_time_variable_then_using_it.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/struct_depends_on_itself_via_optional_field.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/struct_field_missing_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/struct_init_syntax_for_array.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/struct_with_declarations_unavailable_for_reify_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/struct_with_invalid_field.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/sub_assign_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/sub_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/sub_overflow_in_function_evaluation.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/sub_wrap_assign_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/sub_wrap_on_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/suspend_inside_suspend_block.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong_when_else_present.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_expression-duplicate_or_overlapping_integer_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_expression-duplicate_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_expression-duplicate_type_struct_alias.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_expression-missing_enumeration_prong.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_expression-multiple_else_prongs.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_expression-non_exhaustive_integer_prongs.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_expression-switch_on_pointer_type_with_no_else.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_bool.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_enum.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_i8.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_u8.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u1.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u2.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_on_enum_with_1_field_with_no_prongs.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_on_union_with_no_attached_enum.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_with_invalid_expression_parameter.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/switch_with_overlapping_case_ranges.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/tagName_used_on_union_with_no_associated_enum_tag.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/taking_bit_offset_of_void_field_in_struct.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/taking_byte_offset_of_void_field_in_struct.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/threadlocal_qualifier_on_const.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/top_level_decl_dependency_loop.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/truncate_sign_mismatch.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/truncate_undefined_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/try_in_function_with_non_error_return_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/type_checking_function_pointers.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/type_variables_must_be_constant.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/undeclared_identifier.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/undeclared_identifier_error_should_mark_fn_as_impure.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/undeclared_identifier_in_unanalyzed_branch.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/undefined_as_field_type_is_rejected.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/undefined_function_call.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/underscore_is_not_a_declarable_symbol.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_for.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while_else.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/union_auto-enum_value_already_taken.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/union_enum_field_does_not_match_enum.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/union_fields_with_value_assignments.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/union_with_0_fields.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/union_with_specified_enum_omits_field.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/union_with_too_small_explicit_signed_tag_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/union_with_too_small_explicit_unsigned_tag_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/unknown_length_pointer_to_opaque.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/unreachable_code-double_break.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/unreachable_code-nested_returns.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/unreachable_code.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/unreachable_executed_at_comptime.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/unreachable_parameter.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/unreachable_variable.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/unreachable_with_return.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/unsupported_modifier_at_start_of_asm_output_constraint.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/unused_variable_error_on_errdefer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/use_anyopaque_as_return_type_of_fn_ptr.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/use_invalid_number_literal_as_array_index.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/use_of_comptime-known_undefined_function_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/use_of_undeclared_identifier.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/using_an_unknown_len_ptr_type_instead_of_array.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/using_invalid_types_in_function_call_raises_an_error.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/usingnamespace_with_wrong_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/variable_has_wrong_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/variable_in_inline_assembly_template_cannot_be_found.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/variable_initialization_compile_error_then_referenced.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/variable_with_type_noreturn.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/vector_index_out_of_bounds.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/volatile_on_global_assembly.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wasmMemoryGrow_is_a_compile_error_in_non-Wasm_targets.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wasmMemorySize_is_a_compile_error_in_non-Wasm_targets.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/while_expected_bool_got_error_union.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/while_expected_bool_got_optional.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/while_expected_error_union_got_bool.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/while_expected_error_union_got_optional.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/while_expected_optional_got_bool.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/while_expected_optional_got_error_union.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/while_loop_body_expression_ignored.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/write_to_const_global_variable.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wrong_frame_type_used_for_async_call.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wrong_function_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wrong_initializer_for_union_payload_of_type_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wrong_number_of_arguments.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wrong_number_of_arguments_for_method_fn_call.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wrong_panic_signature_generic_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wrong_panic_signature_runtime_function.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wrong_return_type_for_main.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wrong_size_to_an_array_literal.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wrong_type_for_argument_tuple_to_asyncCall.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wrong_type_for_reify_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wrong_type_for_result_ptr_to_asyncCall.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wrong_type_passed_to_panic.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wrong_type_to_hasField.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wrong_types_given_to_atomic_order_args_in_cmpxchg.zig (100%) rename test/{ => cases}/compile_errors/stage1/obj/wrong_types_given_to_export.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/access_invalid_typeInfo_decl.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/alignCast_of_zero_sized_types.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/bad_splat_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/binary_OR_operator_on_error_sets.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-always_inline.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-compile_time.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/cast_between_optional_T_where_T_is_not_a_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/combination_of_nosuspend_and_async.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/comparison_of_non-tagged_union_and_enum_literal.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/comptime_vector_overflow_shows_the_index.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/duplicate_field_in_anonymous_struct_literal.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/error_in_struct_initializer_doesnt_crash_the_compiler.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/errors_in_for_loop_bodies_are_propagated.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/export_with_empty_name_string.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/helpful_return_type_error_message.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/int-float_conversion_to_comptime_int-float.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/invalid_assignments.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/invalid_float_casts.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/invalid_int_casts.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/invalid_non-exhaustive_enum_to_union.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/invalid_pointer_with_reify_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/nested_vectors.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/non-exhaustive_enum_marker_assigned_a_value.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/non-exhaustive_enums.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/not_an_enum_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/packed_struct_with_fields_of_not_allowed_types.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/ptrToInt_with_pointer_to_zero-sized_type.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/reassign_to_array_parameter.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/reassign_to_slice_parameter.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/reassign_to_struct_parameter.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/reference_to_const_data.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/reify_typeOf_with_incompatible_arguments.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/reify_typeOf_with_no_arguments.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/reject_extern_function_definitions_with_body.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/reject_extern_variables_with_initializers.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/return_invalid_type_from_test.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/shift_on_type_with_non-power-of-two_size.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/shuffle_with_selected_index_past_first_vector_length.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/switching_with_exhaustive_enum_has___prong_.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/switching_with_non-exhaustive_enums.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/tagName_on_invalid_value_of_non-exhaustive_enum.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/type_mismatch_in_C_prototype_with_varargs.zig (100%) rename test/{ => cases}/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig (100%) rename test/{ => cases}/compile_errors/stage2/comptime_unreachable.zig (100%) rename test/{ => cases}/compile_errors/stage2/constant_inside_comptime_function_has_compile_error.zig (100%) rename test/{ => cases}/compile_errors/stage2/duplicate-unused_labels.zig (100%) rename test/{ => cases}/compile_errors/stage2/embed_outside_package.zig (100%) rename test/{ => cases}/compile_errors/stage2/import_outside_package.zig (100%) rename test/{ => cases}/compile_errors/stage2/out_of_bounds_index.zig (100%) rename test/{ => cases}/compile_errors/stage2/slice_of_null_pointer.zig (100%) rename test/{ => cases}/compile_errors/stage2/struct_duplicate_field_name.zig (100%) rename test/{ => cases}/compile_errors/stage2/union_access_of_inactive_field.zig (100%) rename test/{ => cases}/compile_errors/stage2/union_duplicate_enum_field.zig (100%) rename test/{ => cases}/compile_errors/stage2/union_duplicate_field_definition.zig (100%) rename test/{ => cases}/compile_errors/stage2/union_enum_field_missing.zig (100%) rename test/{ => cases}/compile_errors/stage2/union_extra_field.zig (100%) rename test/{ => cases}/compile_errors/stage2/union_runtime_coercion_from_enum.zig (100%) rename test/{incremental => cases}/compile_log.0.zig (100%) rename test/{incremental => cases}/compile_log.1.zig (100%) rename test/{incremental => cases}/double_ampersand.0.zig (100%) rename test/{incremental => cases}/double_ampersand.1.zig (100%) rename test/{incremental => cases}/double_ampersand.2.zig (100%) rename test/{incremental => cases}/enum_values.0.zig (100%) rename test/{incremental => cases}/enum_values.1.zig (100%) rename test/{incremental => cases}/extern_variable_has_no_type.0.zig (100%) rename test/{incremental => cases}/extern_variable_has_no_type.1.zig (100%) rename test/{incremental => cases}/function_calls.0.zig (100%) rename test/{incremental => cases}/function_calls.1.zig (100%) rename test/{incremental => cases}/function_calls.2.zig (100%) rename test/{incremental => cases}/function_calls.3.zig (100%) rename test/{incremental => cases}/function_redeclaration.zig (100%) rename test/{incremental => cases}/global_variable_redeclaration.zig (100%) rename test/{incremental => cases}/inner_func_accessing_outer_var.zig (100%) rename test/{incremental => cases}/int_to_ptr.0.zig (100%) rename test/{incremental => cases}/int_to_ptr.1.zig (100%) rename test/{incremental => cases}/large_add_function.zig (100%) rename test/{incremental => cases}/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig (100%) rename test/{incremental => cases}/llvm/address_spaces_pointer_access_chaining_array_pointer.zig (100%) rename test/{incremental => cases}/llvm/address_spaces_pointer_access_chaining_complex.zig (100%) rename test/{incremental => cases}/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig (100%) rename test/{incremental => cases}/llvm/any_typed_null_to_any_typed_optional.zig (100%) rename test/{incremental => cases}/llvm/blocks.zig (100%) rename test/{incremental => cases}/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig (100%) rename test/{incremental => cases}/llvm/f_segment_address_space_reading_and_writing.zig (100%) rename test/{incremental => cases}/llvm/for_loop.zig (100%) rename test/{incremental => cases}/llvm/hello_world.zig (100%) rename test/{incremental => cases}/llvm/invalid_address_space_coercion.zig (100%) rename test/{incremental => cases}/llvm/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig (100%) rename test/{incremental => cases}/llvm/nested_blocks.zig (100%) rename test/{incremental => cases}/llvm/optionals.zig (100%) rename test/{incremental => cases}/llvm/pointer_keeps_address_space.zig (100%) rename test/{incremental => cases}/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig (100%) rename test/{incremental => cases}/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig (100%) rename test/{incremental => cases}/llvm/pointer_with_different_address_spaces.zig (100%) rename test/{incremental => cases}/llvm/pointers_with_different_address_spaces.zig (100%) rename test/{incremental => cases}/llvm/rem.zig (100%) rename test/{incremental => cases}/llvm/shift_right_plus_left.0.zig (100%) rename test/{incremental => cases}/llvm/shift_right_plus_left.1.zig (100%) rename test/{incremental => cases}/llvm/simple_addition_and_subtraction.zig (100%) rename test/{incremental => cases}/llvm/simple_if_statement.zig (100%) rename test/{incremental => cases}/llvm/while_loops.zig (100%) rename test/{incremental => cases}/lower_unnamed_consts_structs.0.zig (100%) rename test/{incremental => cases}/lower_unnamed_consts_structs.1.zig (100%) rename test/{incremental => cases}/lower_unnamed_consts_structs.2.zig (100%) rename test/{incremental => cases}/merge_error_sets.0.zig (100%) rename test/{incremental => cases}/merge_error_sets.1.zig (100%) rename test/{incremental => cases}/multiplying_numbers_at_runtime_and_comptime.0.zig (100%) rename test/{incremental => cases}/multiplying_numbers_at_runtime_and_comptime.1.zig (100%) rename test/{incremental => cases}/multiplying_numbers_at_runtime_and_comptime.2.zig (100%) rename test/{incremental => cases}/non_leaf_functions.zig (100%) rename test/{incremental => cases}/optional_payload.0.zig (100%) rename test/{incremental => cases}/optional_payload.1.zig (100%) rename test/{incremental => cases}/optional_payload.2.zig (100%) rename test/{incremental => cases}/optional_payload.3.zig (100%) rename test/{incremental => cases}/orelse_at_comptime.0.zig (100%) rename test/{incremental => cases}/orelse_at_comptime.1.zig (100%) rename test/{incremental => cases}/passing_u0_to_function.zig (100%) rename test/{incremental => cases}/plan9/exit.zig (100%) rename test/{incremental => cases}/plan9/hello_world_with_updates.0.zig (100%) rename test/{incremental => cases}/plan9/hello_world_with_updates.1.zig (100%) rename test/{incremental => cases}/recursive_fibonacci.zig (100%) rename test/{incremental => cases}/recursive_inline_function.0.zig (100%) rename test/{incremental => cases}/recursive_inline_function.1.zig (100%) rename test/{incremental => cases}/redundant_comptime.0.zig (100%) rename test/{incremental => cases}/redundant_comptime.1.zig (100%) rename test/{incremental => cases}/returns_in_try.zig (100%) rename test/{incremental => cases}/riscv64-linux/hello_world_with_updates.0.zig (100%) rename test/{incremental => cases}/riscv64-linux/hello_world_with_updates.1.zig (100%) rename test/{incremental => cases}/runtime_bitwise_and.zig (100%) rename test/{incremental => cases}/runtime_bitwise_or.zig (100%) rename test/{incremental => cases}/save_function_return_values_in_callee_preserved_register.zig (100%) rename test/{incremental => cases}/setting_an_address_space_on_a_local_variable.zig (100%) rename test/{incremental => cases}/sparcv9-linux/hello_world.zig (100%) rename test/{incremental => cases}/try_in_comptime_in_struct_in_test.zig (100%) rename test/{incremental => cases}/type_of.0.zig (100%) rename test/{incremental => cases}/type_of.1.zig (100%) rename test/{incremental => cases}/type_of.2.zig (100%) rename test/{incremental => cases}/unused_labels.0.zig (100%) rename test/{incremental => cases}/unused_labels.1.zig (100%) rename test/{incremental => cases}/unused_labels.2.zig (100%) rename test/{incremental => cases}/unused_labels.3.zig (100%) rename test/{incremental => cases}/unused_vars.zig (100%) rename test/{incremental => cases}/variable_shadowing.0.zig (100%) rename test/{incremental => cases}/variable_shadowing.1.zig (100%) rename test/{incremental => cases}/variable_shadowing.2.zig (100%) rename test/{incremental => cases}/variable_shadowing.3.zig (100%) rename test/{incremental => cases}/variable_shadowing.4.zig (100%) rename test/{incremental => cases}/variable_shadowing.5.zig (100%) rename test/{incremental => cases}/variable_shadowing.6.zig (100%) rename test/{incremental => cases}/variable_shadowing.7.zig (100%) rename test/{incremental => cases}/variable_shadowing.8.zig (100%) rename test/{incremental => cases}/variable_shadowing.9.zig (100%) rename test/{incremental => cases}/wasm-wasi/conditions.0.zig (100%) rename test/{incremental => cases}/wasm-wasi/conditions.1.zig (100%) rename test/{incremental => cases}/wasm-wasi/conditions.2.zig (100%) rename test/{incremental => cases}/wasm-wasi/conditions.3.zig (100%) rename test/{incremental => cases}/wasm-wasi/conditions.4.zig (100%) rename test/{incremental => cases}/wasm-wasi/conditions.5.zig (100%) rename test/{incremental => cases}/wasm-wasi/error_unions.0.zig (100%) rename test/{incremental => cases}/wasm-wasi/error_unions.1.zig (100%) rename test/{incremental => cases}/wasm-wasi/error_unions.2.zig (100%) rename test/{incremental => cases}/wasm-wasi/error_unions.3.zig (100%) rename test/{incremental => cases}/wasm-wasi/error_unions.4.zig (100%) rename test/{incremental => cases}/wasm-wasi/error_unions.5.zig (100%) rename test/{incremental => cases}/wasm-wasi/locals.0.zig (100%) rename test/{incremental => cases}/wasm-wasi/locals.1.zig (100%) rename test/{incremental => cases}/wasm-wasi/optionals.0.zig (100%) rename test/{incremental => cases}/wasm-wasi/optionals.1.zig (100%) rename test/{incremental => cases}/wasm-wasi/optionals.2.zig (100%) rename test/{incremental => cases}/wasm-wasi/optionals.3.zig (100%) rename test/{incremental => cases}/wasm-wasi/optionals.4.zig (100%) rename test/{incremental => cases}/wasm-wasi/pointers.0.zig (100%) rename test/{incremental => cases}/wasm-wasi/pointers.1.zig (100%) rename test/{incremental => cases}/wasm-wasi/structs.0.zig (100%) rename test/{incremental => cases}/wasm-wasi/structs.1.zig (100%) rename test/{incremental => cases}/wasm-wasi/structs.2.zig (100%) rename test/{incremental => cases}/wasm-wasi/structs.3.zig (100%) rename test/{incremental => cases}/wasm-wasi/structs.4.zig (100%) rename test/{incremental => cases}/wasm-wasi/switch.0.zig (100%) rename test/{incremental => cases}/wasm-wasi/switch.1.zig (100%) rename test/{incremental => cases}/wasm-wasi/switch.2.zig (100%) rename test/{incremental => cases}/wasm-wasi/switch.3.zig (100%) rename test/{incremental => cases}/wasm-wasi/while_loops.0.zig (100%) rename test/{incremental => cases}/wasm-wasi/while_loops.1.zig (100%) rename test/{incremental => cases}/wasm-wasi/while_loops.2.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.0.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.1.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.10.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.11.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.12.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.13.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.14.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.15.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.16.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.17.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.18.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.2.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.3.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.4.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.5.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.6.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.7.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.8.zig (100%) rename test/{incremental => cases}/x86_64-linux/assert_function.9.zig (100%) rename test/{incremental => cases}/x86_64-linux/comptime_var.0.zig (100%) rename test/{incremental => cases}/x86_64-linux/comptime_var.1.zig (100%) rename test/{incremental => cases}/x86_64-linux/comptime_var.2.zig (100%) rename test/{incremental => cases}/x86_64-linux/comptime_var.3.zig (100%) rename test/{incremental => cases}/x86_64-linux/comptime_var.4.zig (100%) rename test/{incremental => cases}/x86_64-linux/comptime_var.5.zig (100%) rename test/{incremental => cases}/x86_64-linux/comptime_var.6.zig (100%) rename test/{incremental => cases}/x86_64-linux/hello_world_with_updates.0.zig (100%) rename test/{incremental => cases}/x86_64-linux/hello_world_with_updates.1.zig (100%) rename test/{incremental => cases}/x86_64-linux/hello_world_with_updates.2.zig (100%) rename test/{incremental => cases}/x86_64-linux/hello_world_with_updates.3.zig (100%) rename test/{incremental => cases}/x86_64-linux/hello_world_with_updates.4.zig (100%) rename test/{incremental => cases}/x86_64-linux/hello_world_with_updates.5.zig (100%) rename test/{incremental => cases}/x86_64-linux/inline_assembly.0.zig (100%) rename test/{incremental => cases}/x86_64-linux/inline_assembly.1.zig (100%) rename test/{incremental => cases}/x86_64-linux/inline_assembly.2.zig (100%) rename test/{incremental => cases}/x86_64-linux/inline_assembly.3.zig (100%) rename test/{incremental => cases}/x86_64-linux/only_1_function_and_it_gets_updated.0.zig (100%) rename test/{incremental => cases}/x86_64-linux/only_1_function_and_it_gets_updated.1.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.0.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.1.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.10.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.11.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.12.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.13.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.14.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.15.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.16.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.17.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.18.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.2.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.3.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.4.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.5.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.6.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.7.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.8.zig (100%) rename test/{incremental => cases}/x86_64-macos/assert_function.9.zig (100%) rename test/{incremental => cases}/x86_64-macos/comptime_var.0.zig (100%) rename test/{incremental => cases}/x86_64-macos/comptime_var.1.zig (100%) rename test/{incremental => cases}/x86_64-macos/comptime_var.2.zig (100%) rename test/{incremental => cases}/x86_64-macos/comptime_var.3.zig (100%) rename test/{incremental => cases}/x86_64-macos/comptime_var.4.zig (100%) rename test/{incremental => cases}/x86_64-macos/comptime_var.5.zig (100%) rename test/{incremental => cases}/x86_64-macos/comptime_var.6.zig (100%) rename test/{incremental => cases}/x86_64-macos/hello_world_with_updates.0.zig (100%) rename test/{incremental => cases}/x86_64-macos/hello_world_with_updates.1.zig (100%) rename test/{incremental => cases}/x86_64-macos/hello_world_with_updates.2.zig (100%) rename test/{incremental => cases}/x86_64-macos/hello_world_with_updates.3.zig (100%) rename test/{incremental => cases}/x86_64-macos/hello_world_with_updates.4.zig (100%) rename test/{incremental => cases}/x86_64-macos/hello_world_with_updates.5.zig (100%) rename test/{incremental => cases}/x86_64-macos/hello_world_with_updates.6.zig (100%) diff --git a/ci/zinc/linux_test.sh b/ci/zinc/linux_test.sh index ec999a483a..180589cc8e 100755 --- a/ci/zinc/linux_test.sh +++ b/ci/zinc/linux_test.sh @@ -45,7 +45,7 @@ cd $WORKSPACE # Look for non-conforming code formatting. # Formatting errors can be fixed by running `zig fmt` on the files printed here. -$ZIG fmt --check . --exclude test/compile_errors/ --exclude test/incremental/ +$ZIG fmt --check . --exclude test/cases/ # Build stage2 standalone so that we can test stage2 against stage2 compiler-rt. $ZIG build -p stage2 -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL" diff --git a/src/test.zig b/src/test.zig index 1359f22ff0..c5dda0757f 100644 --- a/src/test.zig +++ b/src/test.zig @@ -36,20 +36,7 @@ test { { const dir_path = try std.fs.path.join(arena, &.{ - std.fs.path.dirname(@src().file).?, "..", "test", "compile_errors", - }); - - var dir = try std.fs.cwd().openDir(dir_path, .{ .iterate = true }); - defer dir.close(); - - // TODO make this incremental once the bug is solved that it triggers - // See: https://github.com/ziglang/zig/issues/11344 - ctx.addTestCasesFromDir(dir); - } - - { - const dir_path = try std.fs.path.join(arena, &.{ - std.fs.path.dirname(@src().file).?, "..", "test", "incremental", + std.fs.path.dirname(@src().file).?, "..", "test", "cases", }); var dir = try std.fs.cwd().openDir(dir_path, .{ .iterate = true }); diff --git a/test/incremental/aarch64-linux/conditional_branches.0.zig b/test/cases/aarch64-linux/conditional_branches.0.zig similarity index 100% rename from test/incremental/aarch64-linux/conditional_branches.0.zig rename to test/cases/aarch64-linux/conditional_branches.0.zig diff --git a/test/incremental/aarch64-linux/conditional_branches.1.zig b/test/cases/aarch64-linux/conditional_branches.1.zig similarity index 100% rename from test/incremental/aarch64-linux/conditional_branches.1.zig rename to test/cases/aarch64-linux/conditional_branches.1.zig diff --git a/test/incremental/aarch64-linux/hello_world_with_updates.0.zig b/test/cases/aarch64-linux/hello_world_with_updates.0.zig similarity index 100% rename from test/incremental/aarch64-linux/hello_world_with_updates.0.zig rename to test/cases/aarch64-linux/hello_world_with_updates.0.zig diff --git a/test/incremental/aarch64-linux/hello_world_with_updates.1.zig b/test/cases/aarch64-linux/hello_world_with_updates.1.zig similarity index 100% rename from test/incremental/aarch64-linux/hello_world_with_updates.1.zig rename to test/cases/aarch64-linux/hello_world_with_updates.1.zig diff --git a/test/incremental/aarch64-linux/hello_world_with_updates.2.zig b/test/cases/aarch64-linux/hello_world_with_updates.2.zig similarity index 100% rename from test/incremental/aarch64-linux/hello_world_with_updates.2.zig rename to test/cases/aarch64-linux/hello_world_with_updates.2.zig diff --git a/test/incremental/aarch64-macos/hello_world_with_updates.0.zig b/test/cases/aarch64-macos/hello_world_with_updates.0.zig similarity index 100% rename from test/incremental/aarch64-macos/hello_world_with_updates.0.zig rename to test/cases/aarch64-macos/hello_world_with_updates.0.zig diff --git a/test/incremental/aarch64-macos/hello_world_with_updates.1.zig b/test/cases/aarch64-macos/hello_world_with_updates.1.zig similarity index 100% rename from test/incremental/aarch64-macos/hello_world_with_updates.1.zig rename to test/cases/aarch64-macos/hello_world_with_updates.1.zig diff --git a/test/incremental/aarch64-macos/hello_world_with_updates.2.zig b/test/cases/aarch64-macos/hello_world_with_updates.2.zig similarity index 100% rename from test/incremental/aarch64-macos/hello_world_with_updates.2.zig rename to test/cases/aarch64-macos/hello_world_with_updates.2.zig diff --git a/test/incremental/aarch64-macos/hello_world_with_updates.3.zig b/test/cases/aarch64-macos/hello_world_with_updates.3.zig similarity index 100% rename from test/incremental/aarch64-macos/hello_world_with_updates.3.zig rename to test/cases/aarch64-macos/hello_world_with_updates.3.zig diff --git a/test/incremental/aarch64-macos/hello_world_with_updates.4.zig b/test/cases/aarch64-macos/hello_world_with_updates.4.zig similarity index 100% rename from test/incremental/aarch64-macos/hello_world_with_updates.4.zig rename to test/cases/aarch64-macos/hello_world_with_updates.4.zig diff --git a/test/incremental/aarch64-macos/hello_world_with_updates.5.zig b/test/cases/aarch64-macos/hello_world_with_updates.5.zig similarity index 100% rename from test/incremental/aarch64-macos/hello_world_with_updates.5.zig rename to test/cases/aarch64-macos/hello_world_with_updates.5.zig diff --git a/test/incremental/aarch64-macos/hello_world_with_updates.6.zig b/test/cases/aarch64-macos/hello_world_with_updates.6.zig similarity index 100% rename from test/incremental/aarch64-macos/hello_world_with_updates.6.zig rename to test/cases/aarch64-macos/hello_world_with_updates.6.zig diff --git a/test/incremental/adding_numbers_at_runtime_and_comptime.0.zig b/test/cases/adding_numbers_at_runtime_and_comptime.0.zig similarity index 100% rename from test/incremental/adding_numbers_at_runtime_and_comptime.0.zig rename to test/cases/adding_numbers_at_runtime_and_comptime.0.zig diff --git a/test/incremental/adding_numbers_at_runtime_and_comptime.1.zig b/test/cases/adding_numbers_at_runtime_and_comptime.1.zig similarity index 100% rename from test/incremental/adding_numbers_at_runtime_and_comptime.1.zig rename to test/cases/adding_numbers_at_runtime_and_comptime.1.zig diff --git a/test/incremental/adding_numbers_at_runtime_and_comptime.2.zig b/test/cases/adding_numbers_at_runtime_and_comptime.2.zig similarity index 100% rename from test/incremental/adding_numbers_at_runtime_and_comptime.2.zig rename to test/cases/adding_numbers_at_runtime_and_comptime.2.zig diff --git a/test/incremental/ambiguous_reference.zig b/test/cases/ambiguous_reference.zig similarity index 100% rename from test/incremental/ambiguous_reference.zig rename to test/cases/ambiguous_reference.zig diff --git a/test/incremental/arm-linux/arithmetic_operations.0.zig b/test/cases/arm-linux/arithmetic_operations.0.zig similarity index 100% rename from test/incremental/arm-linux/arithmetic_operations.0.zig rename to test/cases/arm-linux/arithmetic_operations.0.zig diff --git a/test/incremental/arm-linux/arithmetic_operations.1.zig b/test/cases/arm-linux/arithmetic_operations.1.zig similarity index 100% rename from test/incremental/arm-linux/arithmetic_operations.1.zig rename to test/cases/arm-linux/arithmetic_operations.1.zig diff --git a/test/incremental/arm-linux/arithmetic_operations.2.zig b/test/cases/arm-linux/arithmetic_operations.2.zig similarity index 100% rename from test/incremental/arm-linux/arithmetic_operations.2.zig rename to test/cases/arm-linux/arithmetic_operations.2.zig diff --git a/test/incremental/arm-linux/arithmetic_operations.3.zig b/test/cases/arm-linux/arithmetic_operations.3.zig similarity index 100% rename from test/incremental/arm-linux/arithmetic_operations.3.zig rename to test/cases/arm-linux/arithmetic_operations.3.zig diff --git a/test/incremental/arm-linux/arithmetic_operations.4.zig b/test/cases/arm-linux/arithmetic_operations.4.zig similarity index 100% rename from test/incremental/arm-linux/arithmetic_operations.4.zig rename to test/cases/arm-linux/arithmetic_operations.4.zig diff --git a/test/incremental/arm-linux/arithmetic_operations.5.zig b/test/cases/arm-linux/arithmetic_operations.5.zig similarity index 100% rename from test/incremental/arm-linux/arithmetic_operations.5.zig rename to test/cases/arm-linux/arithmetic_operations.5.zig diff --git a/test/incremental/arm-linux/arithmetic_operations.6.zig b/test/cases/arm-linux/arithmetic_operations.6.zig similarity index 100% rename from test/incremental/arm-linux/arithmetic_operations.6.zig rename to test/cases/arm-linux/arithmetic_operations.6.zig diff --git a/test/incremental/arm-linux/errors.0.zig b/test/cases/arm-linux/errors.0.zig similarity index 100% rename from test/incremental/arm-linux/errors.0.zig rename to test/cases/arm-linux/errors.0.zig diff --git a/test/incremental/arm-linux/errors.1.zig b/test/cases/arm-linux/errors.1.zig similarity index 100% rename from test/incremental/arm-linux/errors.1.zig rename to test/cases/arm-linux/errors.1.zig diff --git a/test/incremental/arm-linux/errors.2.zig b/test/cases/arm-linux/errors.2.zig similarity index 100% rename from test/incremental/arm-linux/errors.2.zig rename to test/cases/arm-linux/errors.2.zig diff --git a/test/incremental/arm-linux/errors.3.zig b/test/cases/arm-linux/errors.3.zig similarity index 100% rename from test/incremental/arm-linux/errors.3.zig rename to test/cases/arm-linux/errors.3.zig diff --git a/test/incremental/arm-linux/function_pointers.zig b/test/cases/arm-linux/function_pointers.zig similarity index 100% rename from test/incremental/arm-linux/function_pointers.zig rename to test/cases/arm-linux/function_pointers.zig diff --git a/test/incremental/arm-linux/hello_world_with_updates.0.zig b/test/cases/arm-linux/hello_world_with_updates.0.zig similarity index 100% rename from test/incremental/arm-linux/hello_world_with_updates.0.zig rename to test/cases/arm-linux/hello_world_with_updates.0.zig diff --git a/test/incremental/arm-linux/hello_world_with_updates.1.zig b/test/cases/arm-linux/hello_world_with_updates.1.zig similarity index 100% rename from test/incremental/arm-linux/hello_world_with_updates.1.zig rename to test/cases/arm-linux/hello_world_with_updates.1.zig diff --git a/test/incremental/arm-linux/hello_world_with_updates.2.zig b/test/cases/arm-linux/hello_world_with_updates.2.zig similarity index 100% rename from test/incremental/arm-linux/hello_world_with_updates.2.zig rename to test/cases/arm-linux/hello_world_with_updates.2.zig diff --git a/test/incremental/arm-linux/parameters_and_return_values.0.zig b/test/cases/arm-linux/parameters_and_return_values.0.zig similarity index 100% rename from test/incremental/arm-linux/parameters_and_return_values.0.zig rename to test/cases/arm-linux/parameters_and_return_values.0.zig diff --git a/test/incremental/arm-linux/parameters_and_return_values.1.zig b/test/cases/arm-linux/parameters_and_return_values.1.zig similarity index 100% rename from test/incremental/arm-linux/parameters_and_return_values.1.zig rename to test/cases/arm-linux/parameters_and_return_values.1.zig diff --git a/test/incremental/arm-linux/print_u32s.zig b/test/cases/arm-linux/print_u32s.zig similarity index 100% rename from test/incremental/arm-linux/print_u32s.zig rename to test/cases/arm-linux/print_u32s.zig diff --git a/test/incremental/arm-linux/spilling_registers.0.zig b/test/cases/arm-linux/spilling_registers.0.zig similarity index 100% rename from test/incremental/arm-linux/spilling_registers.0.zig rename to test/cases/arm-linux/spilling_registers.0.zig diff --git a/test/incremental/arm-linux/spilling_registers.1.zig b/test/cases/arm-linux/spilling_registers.1.zig similarity index 100% rename from test/incremental/arm-linux/spilling_registers.1.zig rename to test/cases/arm-linux/spilling_registers.1.zig diff --git a/test/incremental/bad_inferred_variable_type.zig b/test/cases/bad_inferred_variable_type.zig similarity index 100% rename from test/incremental/bad_inferred_variable_type.zig rename to test/cases/bad_inferred_variable_type.zig diff --git a/test/incremental/binary_operands.0.zig b/test/cases/binary_operands.0.zig similarity index 100% rename from test/incremental/binary_operands.0.zig rename to test/cases/binary_operands.0.zig diff --git a/test/incremental/binary_operands.1.zig b/test/cases/binary_operands.1.zig similarity index 100% rename from test/incremental/binary_operands.1.zig rename to test/cases/binary_operands.1.zig diff --git a/test/incremental/binary_operands.10.zig b/test/cases/binary_operands.10.zig similarity index 100% rename from test/incremental/binary_operands.10.zig rename to test/cases/binary_operands.10.zig diff --git a/test/incremental/binary_operands.11.zig b/test/cases/binary_operands.11.zig similarity index 100% rename from test/incremental/binary_operands.11.zig rename to test/cases/binary_operands.11.zig diff --git a/test/incremental/binary_operands.12.zig b/test/cases/binary_operands.12.zig similarity index 100% rename from test/incremental/binary_operands.12.zig rename to test/cases/binary_operands.12.zig diff --git a/test/incremental/binary_operands.13.zig b/test/cases/binary_operands.13.zig similarity index 100% rename from test/incremental/binary_operands.13.zig rename to test/cases/binary_operands.13.zig diff --git a/test/incremental/binary_operands.14.zig b/test/cases/binary_operands.14.zig similarity index 100% rename from test/incremental/binary_operands.14.zig rename to test/cases/binary_operands.14.zig diff --git a/test/incremental/binary_operands.15.zig b/test/cases/binary_operands.15.zig similarity index 100% rename from test/incremental/binary_operands.15.zig rename to test/cases/binary_operands.15.zig diff --git a/test/incremental/binary_operands.16.zig b/test/cases/binary_operands.16.zig similarity index 100% rename from test/incremental/binary_operands.16.zig rename to test/cases/binary_operands.16.zig diff --git a/test/incremental/binary_operands.17.zig b/test/cases/binary_operands.17.zig similarity index 100% rename from test/incremental/binary_operands.17.zig rename to test/cases/binary_operands.17.zig diff --git a/test/incremental/binary_operands.18.zig b/test/cases/binary_operands.18.zig similarity index 100% rename from test/incremental/binary_operands.18.zig rename to test/cases/binary_operands.18.zig diff --git a/test/incremental/binary_operands.19.zig b/test/cases/binary_operands.19.zig similarity index 100% rename from test/incremental/binary_operands.19.zig rename to test/cases/binary_operands.19.zig diff --git a/test/incremental/binary_operands.2.zig b/test/cases/binary_operands.2.zig similarity index 100% rename from test/incremental/binary_operands.2.zig rename to test/cases/binary_operands.2.zig diff --git a/test/incremental/binary_operands.20.zig b/test/cases/binary_operands.20.zig similarity index 100% rename from test/incremental/binary_operands.20.zig rename to test/cases/binary_operands.20.zig diff --git a/test/incremental/binary_operands.21.zig b/test/cases/binary_operands.21.zig similarity index 100% rename from test/incremental/binary_operands.21.zig rename to test/cases/binary_operands.21.zig diff --git a/test/incremental/binary_operands.22.zig b/test/cases/binary_operands.22.zig similarity index 100% rename from test/incremental/binary_operands.22.zig rename to test/cases/binary_operands.22.zig diff --git a/test/incremental/binary_operands.23.zig b/test/cases/binary_operands.23.zig similarity index 100% rename from test/incremental/binary_operands.23.zig rename to test/cases/binary_operands.23.zig diff --git a/test/incremental/binary_operands.24.zig b/test/cases/binary_operands.24.zig similarity index 100% rename from test/incremental/binary_operands.24.zig rename to test/cases/binary_operands.24.zig diff --git a/test/incremental/binary_operands.25.zig b/test/cases/binary_operands.25.zig similarity index 100% rename from test/incremental/binary_operands.25.zig rename to test/cases/binary_operands.25.zig diff --git a/test/incremental/binary_operands.3.zig b/test/cases/binary_operands.3.zig similarity index 100% rename from test/incremental/binary_operands.3.zig rename to test/cases/binary_operands.3.zig diff --git a/test/incremental/binary_operands.4.zig b/test/cases/binary_operands.4.zig similarity index 100% rename from test/incremental/binary_operands.4.zig rename to test/cases/binary_operands.4.zig diff --git a/test/incremental/binary_operands.5.zig b/test/cases/binary_operands.5.zig similarity index 100% rename from test/incremental/binary_operands.5.zig rename to test/cases/binary_operands.5.zig diff --git a/test/incremental/binary_operands.6.zig b/test/cases/binary_operands.6.zig similarity index 100% rename from test/incremental/binary_operands.6.zig rename to test/cases/binary_operands.6.zig diff --git a/test/incremental/binary_operands.7.zig b/test/cases/binary_operands.7.zig similarity index 100% rename from test/incremental/binary_operands.7.zig rename to test/cases/binary_operands.7.zig diff --git a/test/incremental/binary_operands.8.zig b/test/cases/binary_operands.8.zig similarity index 100% rename from test/incremental/binary_operands.8.zig rename to test/cases/binary_operands.8.zig diff --git a/test/incremental/binary_operands.9.zig b/test/cases/binary_operands.9.zig similarity index 100% rename from test/incremental/binary_operands.9.zig rename to test/cases/binary_operands.9.zig diff --git a/test/incremental/break_continue.0.zig b/test/cases/break_continue.0.zig similarity index 100% rename from test/incremental/break_continue.0.zig rename to test/cases/break_continue.0.zig diff --git a/test/incremental/break_continue.1.zig b/test/cases/break_continue.1.zig similarity index 100% rename from test/incremental/break_continue.1.zig rename to test/cases/break_continue.1.zig diff --git a/test/incremental/break_continue.2.zig b/test/cases/break_continue.2.zig similarity index 100% rename from test/incremental/break_continue.2.zig rename to test/cases/break_continue.2.zig diff --git a/test/incremental/break_continue.3.zig b/test/cases/break_continue.3.zig similarity index 100% rename from test/incremental/break_continue.3.zig rename to test/cases/break_continue.3.zig diff --git a/test/incremental/catch_at_comptime.0.zig b/test/cases/catch_at_comptime.0.zig similarity index 100% rename from test/incremental/catch_at_comptime.0.zig rename to test/cases/catch_at_comptime.0.zig diff --git a/test/incremental/catch_at_comptime.1.zig b/test/cases/catch_at_comptime.1.zig similarity index 100% rename from test/incremental/catch_at_comptime.1.zig rename to test/cases/catch_at_comptime.1.zig diff --git a/test/incremental/catch_at_comptime.2.zig b/test/cases/catch_at_comptime.2.zig similarity index 100% rename from test/incremental/catch_at_comptime.2.zig rename to test/cases/catch_at_comptime.2.zig diff --git a/test/incremental/catch_at_comptime.3.zig b/test/cases/catch_at_comptime.3.zig similarity index 100% rename from test/incremental/catch_at_comptime.3.zig rename to test/cases/catch_at_comptime.3.zig diff --git a/test/incremental/catch_at_comptime.4.zig b/test/cases/catch_at_comptime.4.zig similarity index 100% rename from test/incremental/catch_at_comptime.4.zig rename to test/cases/catch_at_comptime.4.zig diff --git a/test/incremental/compile_error.zig b/test/cases/compile_error.zig similarity index 100% rename from test/incremental/compile_error.zig rename to test/cases/compile_error.zig diff --git a/test/incremental/compile_error_in_inline_fn_call_fixed.0.zig b/test/cases/compile_error_in_inline_fn_call_fixed.0.zig similarity index 100% rename from test/incremental/compile_error_in_inline_fn_call_fixed.0.zig rename to test/cases/compile_error_in_inline_fn_call_fixed.0.zig diff --git a/test/incremental/compile_error_in_inline_fn_call_fixed.1.zig b/test/cases/compile_error_in_inline_fn_call_fixed.1.zig similarity index 100% rename from test/incremental/compile_error_in_inline_fn_call_fixed.1.zig rename to test/cases/compile_error_in_inline_fn_call_fixed.1.zig diff --git a/test/compile_errors/stage1/exe/main_missing_name.zig b/test/cases/compile_errors/stage1/exe/main_missing_name.zig similarity index 100% rename from test/compile_errors/stage1/exe/main_missing_name.zig rename to test/cases/compile_errors/stage1/exe/main_missing_name.zig diff --git a/test/compile_errors/stage1/exe/missing_main_fn_in_executable.zig b/test/cases/compile_errors/stage1/exe/missing_main_fn_in_executable.zig similarity index 100% rename from test/compile_errors/stage1/exe/missing_main_fn_in_executable.zig rename to test/cases/compile_errors/stage1/exe/missing_main_fn_in_executable.zig diff --git a/test/compile_errors/stage1/exe/private_main_fn.zig b/test/cases/compile_errors/stage1/exe/private_main_fn.zig similarity index 100% rename from test/compile_errors/stage1/exe/private_main_fn.zig rename to test/cases/compile_errors/stage1/exe/private_main_fn.zig diff --git a/test/compile_errors/stage1/obj/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig b/test/cases/compile_errors/stage1/obj/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig similarity index 100% rename from test/compile_errors/stage1/obj/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig rename to test/cases/compile_errors/stage1/obj/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig diff --git a/test/compile_errors/stage1/obj/C_pointer_to_anyopaque.zig b/test/cases/compile_errors/stage1/obj/C_pointer_to_anyopaque.zig similarity index 100% rename from test/compile_errors/stage1/obj/C_pointer_to_anyopaque.zig rename to test/cases/compile_errors/stage1/obj/C_pointer_to_anyopaque.zig diff --git a/test/compile_errors/stage1/obj/Frame_of_generic_function.zig b/test/cases/compile_errors/stage1/obj/Frame_of_generic_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/Frame_of_generic_function.zig rename to test/cases/compile_errors/stage1/obj/Frame_of_generic_function.zig diff --git a/test/compile_errors/stage1/obj/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig b/test/cases/compile_errors/stage1/obj/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig similarity index 100% rename from test/compile_errors/stage1/obj/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig rename to test/cases/compile_errors/stage1/obj/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig diff --git a/test/compile_errors/stage1/obj/Issue_6823_dont_allow_._to_be_followed_by_.zig b/test/cases/compile_errors/stage1/obj/Issue_6823_dont_allow_._to_be_followed_by_.zig similarity index 100% rename from test/compile_errors/stage1/obj/Issue_6823_dont_allow_._to_be_followed_by_.zig rename to test/cases/compile_errors/stage1/obj/Issue_6823_dont_allow_._to_be_followed_by_.zig diff --git a/test/compile_errors/stage1/obj/Issue_9165_windows_tcp_server_compilation_error.zig b/test/cases/compile_errors/stage1/obj/Issue_9165_windows_tcp_server_compilation_error.zig similarity index 100% rename from test/compile_errors/stage1/obj/Issue_9165_windows_tcp_server_compilation_error.zig rename to test/cases/compile_errors/stage1/obj/Issue_9165_windows_tcp_server_compilation_error.zig diff --git a/test/compile_errors/stage1/obj/access_non-existent_member_of_error_set.zig b/test/cases/compile_errors/stage1/obj/access_non-existent_member_of_error_set.zig similarity index 100% rename from test/compile_errors/stage1/obj/access_non-existent_member_of_error_set.zig rename to test/cases/compile_errors/stage1/obj/access_non-existent_member_of_error_set.zig diff --git a/test/compile_errors/stage1/obj/accessing_runtime_parameter_from_outer_function.zig b/test/cases/compile_errors/stage1/obj/accessing_runtime_parameter_from_outer_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/accessing_runtime_parameter_from_outer_function.zig rename to test/cases/compile_errors/stage1/obj/accessing_runtime_parameter_from_outer_function.zig diff --git a/test/compile_errors/stage1/obj/add_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/add_assign_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/add_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/add_assign_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/add_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/add_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/add_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/add_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/add_overflow_in_function_evaluation.zig b/test/cases/compile_errors/stage1/obj/add_overflow_in_function_evaluation.zig similarity index 100% rename from test/compile_errors/stage1/obj/add_overflow_in_function_evaluation.zig rename to test/cases/compile_errors/stage1/obj/add_overflow_in_function_evaluation.zig diff --git a/test/compile_errors/stage1/obj/add_wrap_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/add_wrap_assign_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/add_wrap_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/add_wrap_assign_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/add_wrap_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/add_wrap_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/add_wrap_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/add_wrap_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/addition_with_non_numbers.zig b/test/cases/compile_errors/stage1/obj/addition_with_non_numbers.zig similarity index 100% rename from test/compile_errors/stage1/obj/addition_with_non_numbers.zig rename to test/cases/compile_errors/stage1/obj/addition_with_non_numbers.zig diff --git a/test/compile_errors/stage1/obj/address_of_number_literal.zig b/test/cases/compile_errors/stage1/obj/address_of_number_literal.zig similarity index 100% rename from test/compile_errors/stage1/obj/address_of_number_literal.zig rename to test/cases/compile_errors/stage1/obj/address_of_number_literal.zig diff --git a/test/compile_errors/stage1/obj/alignCast_expects_pointer_or_slice.zig b/test/cases/compile_errors/stage1/obj/alignCast_expects_pointer_or_slice.zig similarity index 100% rename from test/compile_errors/stage1/obj/alignCast_expects_pointer_or_slice.zig rename to test/cases/compile_errors/stage1/obj/alignCast_expects_pointer_or_slice.zig diff --git a/test/compile_errors/stage1/obj/align_n_expr_function_pointers_is_a_compile_error.zig b/test/cases/compile_errors/stage1/obj/align_n_expr_function_pointers_is_a_compile_error.zig similarity index 100% rename from test/compile_errors/stage1/obj/align_n_expr_function_pointers_is_a_compile_error.zig rename to test/cases/compile_errors/stage1/obj/align_n_expr_function_pointers_is_a_compile_error.zig diff --git a/test/compile_errors/stage1/obj/aligned_variable_of_zero-bit_type.zig b/test/cases/compile_errors/stage1/obj/aligned_variable_of_zero-bit_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/aligned_variable_of_zero-bit_type.zig rename to test/cases/compile_errors/stage1/obj/aligned_variable_of_zero-bit_type.zig diff --git a/test/compile_errors/stage1/obj/alignment_of_enum_field_specified.zig b/test/cases/compile_errors/stage1/obj/alignment_of_enum_field_specified.zig similarity index 100% rename from test/compile_errors/stage1/obj/alignment_of_enum_field_specified.zig rename to test/cases/compile_errors/stage1/obj/alignment_of_enum_field_specified.zig diff --git a/test/compile_errors/stage1/obj/ambiguous_decl_reference.zig b/test/cases/compile_errors/stage1/obj/ambiguous_decl_reference.zig similarity index 100% rename from test/compile_errors/stage1/obj/ambiguous_decl_reference.zig rename to test/cases/compile_errors/stage1/obj/ambiguous_decl_reference.zig diff --git a/test/compile_errors/stage1/obj/and_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/and_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/and_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/and_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/array_access_of_non_array.zig b/test/cases/compile_errors/stage1/obj/array_access_of_non_array.zig similarity index 100% rename from test/compile_errors/stage1/obj/array_access_of_non_array.zig rename to test/cases/compile_errors/stage1/obj/array_access_of_non_array.zig diff --git a/test/compile_errors/stage1/obj/array_access_of_type.zig b/test/cases/compile_errors/stage1/obj/array_access_of_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/array_access_of_type.zig rename to test/cases/compile_errors/stage1/obj/array_access_of_type.zig diff --git a/test/compile_errors/stage1/obj/array_access_of_undeclared_identifier.zig b/test/cases/compile_errors/stage1/obj/array_access_of_undeclared_identifier.zig similarity index 100% rename from test/compile_errors/stage1/obj/array_access_of_undeclared_identifier.zig rename to test/cases/compile_errors/stage1/obj/array_access_of_undeclared_identifier.zig diff --git a/test/compile_errors/stage1/obj/array_access_with_non_integer_index.zig b/test/cases/compile_errors/stage1/obj/array_access_with_non_integer_index.zig similarity index 100% rename from test/compile_errors/stage1/obj/array_access_with_non_integer_index.zig rename to test/cases/compile_errors/stage1/obj/array_access_with_non_integer_index.zig diff --git a/test/compile_errors/stage1/obj/array_concatenation_with_wrong_type.zig b/test/cases/compile_errors/stage1/obj/array_concatenation_with_wrong_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/array_concatenation_with_wrong_type.zig rename to test/cases/compile_errors/stage1/obj/array_concatenation_with_wrong_type.zig diff --git a/test/compile_errors/stage1/obj/array_in_c_exported_function.zig b/test/cases/compile_errors/stage1/obj/array_in_c_exported_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/array_in_c_exported_function.zig rename to test/cases/compile_errors/stage1/obj/array_in_c_exported_function.zig diff --git a/test/compile_errors/stage1/obj/asm_at_compile_time.zig b/test/cases/compile_errors/stage1/obj/asm_at_compile_time.zig similarity index 100% rename from test/compile_errors/stage1/obj/asm_at_compile_time.zig rename to test/cases/compile_errors/stage1/obj/asm_at_compile_time.zig diff --git a/test/compile_errors/stage1/obj/assign_inline_fn_to_non-comptime_var.zig b/test/cases/compile_errors/stage1/obj/assign_inline_fn_to_non-comptime_var.zig similarity index 100% rename from test/compile_errors/stage1/obj/assign_inline_fn_to_non-comptime_var.zig rename to test/cases/compile_errors/stage1/obj/assign_inline_fn_to_non-comptime_var.zig diff --git a/test/compile_errors/stage1/obj/assign_null_to_non-optional_pointer.zig b/test/cases/compile_errors/stage1/obj/assign_null_to_non-optional_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/assign_null_to_non-optional_pointer.zig rename to test/cases/compile_errors/stage1/obj/assign_null_to_non-optional_pointer.zig diff --git a/test/compile_errors/stage1/obj/assign_through_constant_pointer.zig b/test/cases/compile_errors/stage1/obj/assign_through_constant_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/assign_through_constant_pointer.zig rename to test/cases/compile_errors/stage1/obj/assign_through_constant_pointer.zig diff --git a/test/compile_errors/stage1/obj/assign_through_constant_slice.zig b/test/cases/compile_errors/stage1/obj/assign_through_constant_slice.zig similarity index 100% rename from test/compile_errors/stage1/obj/assign_through_constant_slice.zig rename to test/cases/compile_errors/stage1/obj/assign_through_constant_slice.zig diff --git a/test/compile_errors/stage1/obj/assign_to_constant_field.zig b/test/cases/compile_errors/stage1/obj/assign_to_constant_field.zig similarity index 100% rename from test/compile_errors/stage1/obj/assign_to_constant_field.zig rename to test/cases/compile_errors/stage1/obj/assign_to_constant_field.zig diff --git a/test/compile_errors/stage1/obj/assign_to_constant_variable.zig b/test/cases/compile_errors/stage1/obj/assign_to_constant_variable.zig similarity index 100% rename from test/compile_errors/stage1/obj/assign_to_constant_variable.zig rename to test/cases/compile_errors/stage1/obj/assign_to_constant_variable.zig diff --git a/test/compile_errors/stage1/obj/assign_to_invalid_dereference.zig b/test/cases/compile_errors/stage1/obj/assign_to_invalid_dereference.zig similarity index 100% rename from test/compile_errors/stage1/obj/assign_to_invalid_dereference.zig rename to test/cases/compile_errors/stage1/obj/assign_to_invalid_dereference.zig diff --git a/test/compile_errors/stage1/obj/assign_too_big_number_to_u16.zig b/test/cases/compile_errors/stage1/obj/assign_too_big_number_to_u16.zig similarity index 100% rename from test/compile_errors/stage1/obj/assign_too_big_number_to_u16.zig rename to test/cases/compile_errors/stage1/obj/assign_too_big_number_to_u16.zig diff --git a/test/compile_errors/stage1/obj/assign_unreachable.zig b/test/cases/compile_errors/stage1/obj/assign_unreachable.zig similarity index 100% rename from test/compile_errors/stage1/obj/assign_unreachable.zig rename to test/cases/compile_errors/stage1/obj/assign_unreachable.zig diff --git a/test/compile_errors/stage1/obj/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig b/test/cases/compile_errors/stage1/obj/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig similarity index 100% rename from test/compile_errors/stage1/obj/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig rename to test/cases/compile_errors/stage1/obj/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig diff --git a/test/compile_errors/stage1/obj/async_function_depends_on_its_own_frame.zig b/test/cases/compile_errors/stage1/obj/async_function_depends_on_its_own_frame.zig similarity index 100% rename from test/compile_errors/stage1/obj/async_function_depends_on_its_own_frame.zig rename to test/cases/compile_errors/stage1/obj/async_function_depends_on_its_own_frame.zig diff --git a/test/compile_errors/stage1/obj/async_function_indirectly_depends_on_its_own_frame.zig b/test/cases/compile_errors/stage1/obj/async_function_indirectly_depends_on_its_own_frame.zig similarity index 100% rename from test/compile_errors/stage1/obj/async_function_indirectly_depends_on_its_own_frame.zig rename to test/cases/compile_errors/stage1/obj/async_function_indirectly_depends_on_its_own_frame.zig diff --git a/test/compile_errors/stage1/obj/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig b/test/cases/compile_errors/stage1/obj/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig similarity index 100% rename from test/compile_errors/stage1/obj/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig rename to test/cases/compile_errors/stage1/obj/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig diff --git a/test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig b/test/cases/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig similarity index 100% rename from test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig rename to test/cases/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig diff --git a/test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig b/test/cases/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig similarity index 100% rename from test/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig rename to test/cases/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig diff --git a/test/compile_errors/stage1/obj/atomic_orderings_of_fence_Acquire_or_stricter.zig b/test/cases/compile_errors/stage1/obj/atomic_orderings_of_fence_Acquire_or_stricter.zig similarity index 100% rename from test/compile_errors/stage1/obj/atomic_orderings_of_fence_Acquire_or_stricter.zig rename to test/cases/compile_errors/stage1/obj/atomic_orderings_of_fence_Acquire_or_stricter.zig diff --git a/test/compile_errors/stage1/obj/atomicrmw_with_bool_op_not_.Xchg.zig b/test/cases/compile_errors/stage1/obj/atomicrmw_with_bool_op_not_.Xchg.zig similarity index 100% rename from test/compile_errors/stage1/obj/atomicrmw_with_bool_op_not_.Xchg.zig rename to test/cases/compile_errors/stage1/obj/atomicrmw_with_bool_op_not_.Xchg.zig diff --git a/test/compile_errors/stage1/obj/atomicrmw_with_enum_op_not_.Xchg.zig b/test/cases/compile_errors/stage1/obj/atomicrmw_with_enum_op_not_.Xchg.zig similarity index 100% rename from test/compile_errors/stage1/obj/atomicrmw_with_enum_op_not_.Xchg.zig rename to test/cases/compile_errors/stage1/obj/atomicrmw_with_enum_op_not_.Xchg.zig diff --git a/test/compile_errors/stage1/obj/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig b/test/cases/compile_errors/stage1/obj/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig similarity index 100% rename from test/compile_errors/stage1/obj/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig rename to test/cases/compile_errors/stage1/obj/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig diff --git a/test/compile_errors/stage1/obj/attempt_to_cast_enum_literal_to_error.zig b/test/cases/compile_errors/stage1/obj/attempt_to_cast_enum_literal_to_error.zig similarity index 100% rename from test/compile_errors/stage1/obj/attempt_to_cast_enum_literal_to_error.zig rename to test/cases/compile_errors/stage1/obj/attempt_to_cast_enum_literal_to_error.zig diff --git a/test/compile_errors/stage1/obj/attempt_to_close_over_comptime_variable_from_outer_scope.zig b/test/cases/compile_errors/stage1/obj/attempt_to_close_over_comptime_variable_from_outer_scope.zig similarity index 100% rename from test/compile_errors/stage1/obj/attempt_to_close_over_comptime_variable_from_outer_scope.zig rename to test/cases/compile_errors/stage1/obj/attempt_to_close_over_comptime_variable_from_outer_scope.zig diff --git a/test/compile_errors/stage1/obj/attempt_to_create_17_bit_float_type.zig b/test/cases/compile_errors/stage1/obj/attempt_to_create_17_bit_float_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/attempt_to_create_17_bit_float_type.zig rename to test/cases/compile_errors/stage1/obj/attempt_to_create_17_bit_float_type.zig diff --git a/test/compile_errors/stage1/obj/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig b/test/cases/compile_errors/stage1/obj/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig rename to test/cases/compile_errors/stage1/obj/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig diff --git a/test/compile_errors/stage1/obj/attempt_to_use_0_bit_type_in_extern_fn.zig b/test/cases/compile_errors/stage1/obj/attempt_to_use_0_bit_type_in_extern_fn.zig similarity index 100% rename from test/compile_errors/stage1/obj/attempt_to_use_0_bit_type_in_extern_fn.zig rename to test/cases/compile_errors/stage1/obj/attempt_to_use_0_bit_type_in_extern_fn.zig diff --git a/test/compile_errors/stage1/obj/attempted_double_ampersand.zig b/test/cases/compile_errors/stage1/obj/attempted_double_ampersand.zig similarity index 100% rename from test/compile_errors/stage1/obj/attempted_double_ampersand.zig rename to test/cases/compile_errors/stage1/obj/attempted_double_ampersand.zig diff --git a/test/compile_errors/stage1/obj/attempted_double_pipe_on_boolean_values.zig b/test/cases/compile_errors/stage1/obj/attempted_double_pipe_on_boolean_values.zig similarity index 100% rename from test/compile_errors/stage1/obj/attempted_double_pipe_on_boolean_values.zig rename to test/cases/compile_errors/stage1/obj/attempted_double_pipe_on_boolean_values.zig diff --git a/test/compile_errors/stage1/obj/attempted_implicit_cast_from_T_to_slice_const_T.zig b/test/cases/compile_errors/stage1/obj/attempted_implicit_cast_from_T_to_slice_const_T.zig similarity index 100% rename from test/compile_errors/stage1/obj/attempted_implicit_cast_from_T_to_slice_const_T.zig rename to test/cases/compile_errors/stage1/obj/attempted_implicit_cast_from_T_to_slice_const_T.zig diff --git a/test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_array_len_1_T.zig b/test/cases/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_array_len_1_T.zig similarity index 100% rename from test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_array_len_1_T.zig rename to test/cases/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_array_len_1_T.zig diff --git a/test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_sliceT.zig b/test/cases/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_sliceT.zig similarity index 100% rename from test/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_sliceT.zig rename to test/cases/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_sliceT.zig diff --git a/test/compile_errors/stage1/obj/bad_alignCast_at_comptime.zig b/test/cases/compile_errors/stage1/obj/bad_alignCast_at_comptime.zig similarity index 100% rename from test/compile_errors/stage1/obj/bad_alignCast_at_comptime.zig rename to test/cases/compile_errors/stage1/obj/bad_alignCast_at_comptime.zig diff --git a/test/compile_errors/stage1/obj/bad_alignment_in_asynccall.zig b/test/cases/compile_errors/stage1/obj/bad_alignment_in_asynccall.zig similarity index 100% rename from test/compile_errors/stage1/obj/bad_alignment_in_asynccall.zig rename to test/cases/compile_errors/stage1/obj/bad_alignment_in_asynccall.zig diff --git a/test/compile_errors/stage1/obj/bad_alignment_in_implicit_cast_from_array_pointer_to_slice.zig b/test/cases/compile_errors/stage1/obj/bad_alignment_in_implicit_cast_from_array_pointer_to_slice.zig similarity index 100% rename from test/compile_errors/stage1/obj/bad_alignment_in_implicit_cast_from_array_pointer_to_slice.zig rename to test/cases/compile_errors/stage1/obj/bad_alignment_in_implicit_cast_from_array_pointer_to_slice.zig diff --git a/test/compile_errors/stage1/obj/bad_alignment_type.zig b/test/cases/compile_errors/stage1/obj/bad_alignment_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/bad_alignment_type.zig rename to test/cases/compile_errors/stage1/obj/bad_alignment_type.zig diff --git a/test/compile_errors/stage1/obj/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig b/test/cases/compile_errors/stage1/obj/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig similarity index 100% rename from test/compile_errors/stage1/obj/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig rename to test/cases/compile_errors/stage1/obj/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig diff --git a/test/compile_errors/stage1/obj/bad_import.zig b/test/cases/compile_errors/stage1/obj/bad_import.zig similarity index 100% rename from test/compile_errors/stage1/obj/bad_import.zig rename to test/cases/compile_errors/stage1/obj/bad_import.zig diff --git a/test/compile_errors/stage1/obj/bad_usage_of_call.zig b/test/cases/compile_errors/stage1/obj/bad_usage_of_call.zig similarity index 100% rename from test/compile_errors/stage1/obj/bad_usage_of_call.zig rename to test/cases/compile_errors/stage1/obj/bad_usage_of_call.zig diff --git a/test/compile_errors/stage1/obj/bin_and_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/bin_and_assign_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/bin_and_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/bin_and_assign_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/bin_and_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/bin_and_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/bin_and_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/bin_and_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/bin_not_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/bin_not_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/bin_not_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/bin_not_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/bin_or_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/bin_or_assign_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/bin_or_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/bin_or_assign_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/bin_or_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/bin_or_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/bin_or_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/bin_or_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/bin_xor_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/bin_xor_assign_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/bin_xor_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/bin_xor_assign_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/bin_xor_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/bin_xor_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/bin_xor_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/bin_xor_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/binary_not_on_number_literal.zig b/test/cases/compile_errors/stage1/obj/binary_not_on_number_literal.zig similarity index 100% rename from test/compile_errors/stage1/obj/binary_not_on_number_literal.zig rename to test/cases/compile_errors/stage1/obj/binary_not_on_number_literal.zig diff --git a/test/compile_errors/stage1/obj/bitCast_same_size_but_bit_count_mismatch.zig b/test/cases/compile_errors/stage1/obj/bitCast_same_size_but_bit_count_mismatch.zig similarity index 100% rename from test/compile_errors/stage1/obj/bitCast_same_size_but_bit_count_mismatch.zig rename to test/cases/compile_errors/stage1/obj/bitCast_same_size_but_bit_count_mismatch.zig diff --git a/test/compile_errors/stage1/obj/bitCast_to_enum_type.zig b/test/cases/compile_errors/stage1/obj/bitCast_to_enum_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/bitCast_to_enum_type.zig rename to test/cases/compile_errors/stage1/obj/bitCast_to_enum_type.zig diff --git a/test/compile_errors/stage1/obj/bitCast_with_different_sizes_inside_an_expression.zig b/test/cases/compile_errors/stage1/obj/bitCast_with_different_sizes_inside_an_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/bitCast_with_different_sizes_inside_an_expression.zig rename to test/cases/compile_errors/stage1/obj/bitCast_with_different_sizes_inside_an_expression.zig diff --git a/test/compile_errors/stage1/obj/bit_shifting_only_works_on_integer_types.zig b/test/cases/compile_errors/stage1/obj/bit_shifting_only_works_on_integer_types.zig similarity index 100% rename from test/compile_errors/stage1/obj/bit_shifting_only_works_on_integer_types.zig rename to test/cases/compile_errors/stage1/obj/bit_shifting_only_works_on_integer_types.zig diff --git a/test/compile_errors/stage1/obj/bogus_compile_var.zig b/test/cases/compile_errors/stage1/obj/bogus_compile_var.zig similarity index 100% rename from test/compile_errors/stage1/obj/bogus_compile_var.zig rename to test/cases/compile_errors/stage1/obj/bogus_compile_var.zig diff --git a/test/compile_errors/stage1/obj/bogus_method_call_on_slice.zig b/test/cases/compile_errors/stage1/obj/bogus_method_call_on_slice.zig similarity index 100% rename from test/compile_errors/stage1/obj/bogus_method_call_on_slice.zig rename to test/cases/compile_errors/stage1/obj/bogus_method_call_on_slice.zig diff --git a/test/compile_errors/stage1/obj/bool_not_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/bool_not_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/bool_not_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/bool_not_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/branch_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/branch_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/branch_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/branch_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/cImport_with_bogus_include.zig b/test/cases/compile_errors/stage1/obj/cImport_with_bogus_include.zig similarity index 100% rename from test/compile_errors/stage1/obj/cImport_with_bogus_include.zig rename to test/cases/compile_errors/stage1/obj/cImport_with_bogus_include.zig diff --git a/test/compile_errors/stage1/obj/call_assigned_to_constant.zig b/test/cases/compile_errors/stage1/obj/call_assigned_to_constant.zig similarity index 100% rename from test/compile_errors/stage1/obj/call_assigned_to_constant.zig rename to test/cases/compile_errors/stage1/obj/call_assigned_to_constant.zig diff --git a/test/compile_errors/stage1/obj/call_with_new_stack_on_unsupported_target.zig b/test/cases/compile_errors/stage1/obj/call_with_new_stack_on_unsupported_target.zig similarity index 100% rename from test/compile_errors/stage1/obj/call_with_new_stack_on_unsupported_target.zig rename to test/cases/compile_errors/stage1/obj/call_with_new_stack_on_unsupported_target.zig diff --git a/test/compile_errors/stage1/obj/callconv_apcs_aapcs_aapcsvfp_on_unsupported_platform.zig b/test/cases/compile_errors/stage1/obj/callconv_apcs_aapcs_aapcsvfp_on_unsupported_platform.zig similarity index 100% rename from test/compile_errors/stage1/obj/callconv_apcs_aapcs_aapcsvfp_on_unsupported_platform.zig rename to test/cases/compile_errors/stage1/obj/callconv_apcs_aapcs_aapcsvfp_on_unsupported_platform.zig diff --git a/test/compile_errors/stage1/obj/callconv_interrupt_on_unsupported_platform.zig b/test/cases/compile_errors/stage1/obj/callconv_interrupt_on_unsupported_platform.zig similarity index 100% rename from test/compile_errors/stage1/obj/callconv_interrupt_on_unsupported_platform.zig rename to test/cases/compile_errors/stage1/obj/callconv_interrupt_on_unsupported_platform.zig diff --git a/test/compile_errors/stage1/obj/callconv_signal_on_unsupported_platform.zig b/test/cases/compile_errors/stage1/obj/callconv_signal_on_unsupported_platform.zig similarity index 100% rename from test/compile_errors/stage1/obj/callconv_signal_on_unsupported_platform.zig rename to test/cases/compile_errors/stage1/obj/callconv_signal_on_unsupported_platform.zig diff --git a/test/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-0.zig b/test/cases/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-0.zig similarity index 100% rename from test/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-0.zig rename to test/cases/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-0.zig diff --git a/test/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-1.zig b/test/cases/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-1.zig similarity index 100% rename from test/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-1.zig rename to test/cases/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-1.zig diff --git a/test/compile_errors/stage1/obj/callconv_vectorcall_on_unsupported_platform.zig b/test/cases/compile_errors/stage1/obj/callconv_vectorcall_on_unsupported_platform.zig similarity index 100% rename from test/compile_errors/stage1/obj/callconv_vectorcall_on_unsupported_platform.zig rename to test/cases/compile_errors/stage1/obj/callconv_vectorcall_on_unsupported_platform.zig diff --git a/test/compile_errors/stage1/obj/calling_a_generic_function_only_known_at_runtime.zig b/test/cases/compile_errors/stage1/obj/calling_a_generic_function_only_known_at_runtime.zig similarity index 100% rename from test/compile_errors/stage1/obj/calling_a_generic_function_only_known_at_runtime.zig rename to test/cases/compile_errors/stage1/obj/calling_a_generic_function_only_known_at_runtime.zig diff --git a/test/compile_errors/stage1/obj/calling_function_with_naked_calling_convention.zig b/test/cases/compile_errors/stage1/obj/calling_function_with_naked_calling_convention.zig similarity index 100% rename from test/compile_errors/stage1/obj/calling_function_with_naked_calling_convention.zig rename to test/cases/compile_errors/stage1/obj/calling_function_with_naked_calling_convention.zig diff --git a/test/compile_errors/stage1/obj/calling_var_args_extern_function_passing_array_instead_of_pointer.zig b/test/cases/compile_errors/stage1/obj/calling_var_args_extern_function_passing_array_instead_of_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/calling_var_args_extern_function_passing_array_instead_of_pointer.zig rename to test/cases/compile_errors/stage1/obj/calling_var_args_extern_function_passing_array_instead_of_pointer.zig diff --git a/test/compile_errors/stage1/obj/cannot_break_out_of_defer_expression.zig b/test/cases/compile_errors/stage1/obj/cannot_break_out_of_defer_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/cannot_break_out_of_defer_expression.zig rename to test/cases/compile_errors/stage1/obj/cannot_break_out_of_defer_expression.zig diff --git a/test/compile_errors/stage1/obj/cannot_continue_out_of_defer_expression.zig b/test/cases/compile_errors/stage1/obj/cannot_continue_out_of_defer_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/cannot_continue_out_of_defer_expression.zig rename to test/cases/compile_errors/stage1/obj/cannot_continue_out_of_defer_expression.zig diff --git a/test/compile_errors/stage1/obj/capture_group_on_switch_prong_with_incompatible_payload_types.zig b/test/cases/compile_errors/stage1/obj/capture_group_on_switch_prong_with_incompatible_payload_types.zig similarity index 100% rename from test/compile_errors/stage1/obj/capture_group_on_switch_prong_with_incompatible_payload_types.zig rename to test/cases/compile_errors/stage1/obj/capture_group_on_switch_prong_with_incompatible_payload_types.zig diff --git a/test/compile_errors/stage1/obj/cast_enum_literal_to_enum_but_it_doesnt_match.zig b/test/cases/compile_errors/stage1/obj/cast_enum_literal_to_enum_but_it_doesnt_match.zig similarity index 100% rename from test/compile_errors/stage1/obj/cast_enum_literal_to_enum_but_it_doesnt_match.zig rename to test/cases/compile_errors/stage1/obj/cast_enum_literal_to_enum_but_it_doesnt_match.zig diff --git a/test/compile_errors/stage1/obj/cast_error_union_of_global_error_set_to_error_union_of_smaller_error_set.zig b/test/cases/compile_errors/stage1/obj/cast_error_union_of_global_error_set_to_error_union_of_smaller_error_set.zig similarity index 100% rename from test/compile_errors/stage1/obj/cast_error_union_of_global_error_set_to_error_union_of_smaller_error_set.zig rename to test/cases/compile_errors/stage1/obj/cast_error_union_of_global_error_set_to_error_union_of_smaller_error_set.zig diff --git a/test/compile_errors/stage1/obj/cast_global_error_set_to_error_set.zig b/test/cases/compile_errors/stage1/obj/cast_global_error_set_to_error_set.zig similarity index 100% rename from test/compile_errors/stage1/obj/cast_global_error_set_to_error_set.zig rename to test/cases/compile_errors/stage1/obj/cast_global_error_set_to_error_set.zig diff --git a/test/compile_errors/stage1/obj/cast_negative_integer_literal_to_usize.zig b/test/cases/compile_errors/stage1/obj/cast_negative_integer_literal_to_usize.zig similarity index 100% rename from test/compile_errors/stage1/obj/cast_negative_integer_literal_to_usize.zig rename to test/cases/compile_errors/stage1/obj/cast_negative_integer_literal_to_usize.zig diff --git a/test/compile_errors/stage1/obj/cast_negative_value_to_unsigned_integer.zig b/test/cases/compile_errors/stage1/obj/cast_negative_value_to_unsigned_integer.zig similarity index 100% rename from test/compile_errors/stage1/obj/cast_negative_value_to_unsigned_integer.zig rename to test/cases/compile_errors/stage1/obj/cast_negative_value_to_unsigned_integer.zig diff --git a/test/compile_errors/stage1/obj/cast_unreachable.zig b/test/cases/compile_errors/stage1/obj/cast_unreachable.zig similarity index 100% rename from test/compile_errors/stage1/obj/cast_unreachable.zig rename to test/cases/compile_errors/stage1/obj/cast_unreachable.zig diff --git a/test/compile_errors/stage1/obj/casting_bit_offset_pointer_to_regular_pointer.zig b/test/cases/compile_errors/stage1/obj/casting_bit_offset_pointer_to_regular_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/casting_bit_offset_pointer_to_regular_pointer.zig rename to test/cases/compile_errors/stage1/obj/casting_bit_offset_pointer_to_regular_pointer.zig diff --git a/test/compile_errors/stage1/obj/catch_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/catch_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/catch_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/catch_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/chained_comparison_operators.zig b/test/cases/compile_errors/stage1/obj/chained_comparison_operators.zig similarity index 100% rename from test/compile_errors/stage1/obj/chained_comparison_operators.zig rename to test/cases/compile_errors/stage1/obj/chained_comparison_operators.zig diff --git a/test/compile_errors/stage1/obj/cmpxchg_with_float.zig b/test/cases/compile_errors/stage1/obj/cmpxchg_with_float.zig similarity index 100% rename from test/compile_errors/stage1/obj/cmpxchg_with_float.zig rename to test/cases/compile_errors/stage1/obj/cmpxchg_with_float.zig diff --git a/test/compile_errors/stage1/obj/colliding_invalid_top_level_functions.zig b/test/cases/compile_errors/stage1/obj/colliding_invalid_top_level_functions.zig similarity index 100% rename from test/compile_errors/stage1/obj/colliding_invalid_top_level_functions.zig rename to test/cases/compile_errors/stage1/obj/colliding_invalid_top_level_functions.zig diff --git a/test/compile_errors/stage1/obj/compare_optional_to_non-optional_with_invalid_types.zig b/test/cases/compile_errors/stage1/obj/compare_optional_to_non-optional_with_invalid_types.zig similarity index 100% rename from test/compile_errors/stage1/obj/compare_optional_to_non-optional_with_invalid_types.zig rename to test/cases/compile_errors/stage1/obj/compare_optional_to_non-optional_with_invalid_types.zig diff --git a/test/compile_errors/stage1/obj/comparing_a_non-optional_pointer_against_null.zig b/test/cases/compile_errors/stage1/obj/comparing_a_non-optional_pointer_against_null.zig similarity index 100% rename from test/compile_errors/stage1/obj/comparing_a_non-optional_pointer_against_null.zig rename to test/cases/compile_errors/stage1/obj/comparing_a_non-optional_pointer_against_null.zig diff --git a/test/compile_errors/stage1/obj/comparing_against_undefined_produces_undefined_value.zig b/test/cases/compile_errors/stage1/obj/comparing_against_undefined_produces_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/comparing_against_undefined_produces_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/comparing_against_undefined_produces_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/comparison_operators_with_undefined_value.zig b/test/cases/compile_errors/stage1/obj/comparison_operators_with_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/comparison_operators_with_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/comparison_operators_with_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/comparison_with_error_union_and_error_value.zig b/test/cases/compile_errors/stage1/obj/comparison_with_error_union_and_error_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/comparison_with_error_union_and_error_value.zig rename to test/cases/compile_errors/stage1/obj/comparison_with_error_union_and_error_value.zig diff --git a/test/compile_errors/stage1/obj/compile-time_division_by_zero.zig b/test/cases/compile_errors/stage1/obj/compile-time_division_by_zero.zig similarity index 100% rename from test/compile_errors/stage1/obj/compile-time_division_by_zero.zig rename to test/cases/compile_errors/stage1/obj/compile-time_division_by_zero.zig diff --git a/test/compile_errors/stage1/obj/compile-time_remainder_division_by_zero.zig b/test/cases/compile_errors/stage1/obj/compile-time_remainder_division_by_zero.zig similarity index 100% rename from test/compile_errors/stage1/obj/compile-time_remainder_division_by_zero.zig rename to test/cases/compile_errors/stage1/obj/compile-time_remainder_division_by_zero.zig diff --git a/test/compile_errors/stage1/obj/compileError_shows_traceback_of_references_that_caused_it.zig b/test/cases/compile_errors/stage1/obj/compileError_shows_traceback_of_references_that_caused_it.zig similarity index 100% rename from test/compile_errors/stage1/obj/compileError_shows_traceback_of_references_that_caused_it.zig rename to test/cases/compile_errors/stage1/obj/compileError_shows_traceback_of_references_that_caused_it.zig diff --git a/test/compile_errors/stage1/obj/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig b/test/cases/compile_errors/stage1/obj/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig similarity index 100% rename from test/compile_errors/stage1/obj/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig rename to test/cases/compile_errors/stage1/obj/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig diff --git a/test/compile_errors/stage1/obj/compile_error_in_struct_init_expression.zig b/test/cases/compile_errors/stage1/obj/compile_error_in_struct_init_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/compile_error_in_struct_init_expression.zig rename to test/cases/compile_errors/stage1/obj/compile_error_in_struct_init_expression.zig diff --git a/test/compile_errors/stage1/obj/compile_error_when_evaluating_return_type_of_inferred_error_set.zig b/test/cases/compile_errors/stage1/obj/compile_error_when_evaluating_return_type_of_inferred_error_set.zig similarity index 100% rename from test/compile_errors/stage1/obj/compile_error_when_evaluating_return_type_of_inferred_error_set.zig rename to test/cases/compile_errors/stage1/obj/compile_error_when_evaluating_return_type_of_inferred_error_set.zig diff --git a/test/compile_errors/stage1/obj/compile_log.zig b/test/cases/compile_errors/stage1/obj/compile_log.zig similarity index 100% rename from test/compile_errors/stage1/obj/compile_log.zig rename to test/cases/compile_errors/stage1/obj/compile_log.zig diff --git a/test/compile_errors/stage1/obj/compile_log_a_pointer_to_an_opaque_value.zig b/test/cases/compile_errors/stage1/obj/compile_log_a_pointer_to_an_opaque_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/compile_log_a_pointer_to_an_opaque_value.zig rename to test/cases/compile_errors/stage1/obj/compile_log_a_pointer_to_an_opaque_value.zig diff --git a/test/compile_errors/stage1/obj/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig b/test/cases/compile_errors/stage1/obj/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig similarity index 100% rename from test/compile_errors/stage1/obj/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig rename to test/cases/compile_errors/stage1/obj/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig diff --git a/test/compile_errors/stage1/obj/compile_log_statement_warning_deduplication_in_generic_fn.zig b/test/cases/compile_errors/stage1/obj/compile_log_statement_warning_deduplication_in_generic_fn.zig similarity index 100% rename from test/compile_errors/stage1/obj/compile_log_statement_warning_deduplication_in_generic_fn.zig rename to test/cases/compile_errors/stage1/obj/compile_log_statement_warning_deduplication_in_generic_fn.zig diff --git a/test/compile_errors/stage1/obj/compile_time_division_by_zero.zig b/test/cases/compile_errors/stage1/obj/compile_time_division_by_zero.zig similarity index 100% rename from test/compile_errors/stage1/obj/compile_time_division_by_zero.zig rename to test/cases/compile_errors/stage1/obj/compile_time_division_by_zero.zig diff --git a/test/compile_errors/stage1/obj/comptime_cast_enum_to_union_but_field_has_payload.zig b/test/cases/compile_errors/stage1/obj/comptime_cast_enum_to_union_but_field_has_payload.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_cast_enum_to_union_but_field_has_payload.zig rename to test/cases/compile_errors/stage1/obj/comptime_cast_enum_to_union_but_field_has_payload.zig diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig b/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig rename to test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig b/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig rename to test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig b/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig rename to test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig b/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig rename to test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig b/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig rename to test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig b/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig rename to test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig b/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig rename to test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig diff --git a/test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig b/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig rename to test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig diff --git a/test/compile_errors/stage1/obj/comptime_float_in_asm_input.zig b/test/cases/compile_errors/stage1/obj/comptime_float_in_asm_input.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_float_in_asm_input.zig rename to test/cases/compile_errors/stage1/obj/comptime_float_in_asm_input.zig diff --git a/test/compile_errors/stage1/obj/comptime_implicit_cast_f64_to_f32.zig b/test/cases/compile_errors/stage1/obj/comptime_implicit_cast_f64_to_f32.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_implicit_cast_f64_to_f32.zig rename to test/cases/compile_errors/stage1/obj/comptime_implicit_cast_f64_to_f32.zig diff --git a/test/compile_errors/stage1/obj/comptime_int_in_asm_input.zig b/test/cases/compile_errors/stage1/obj/comptime_int_in_asm_input.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_int_in_asm_input.zig rename to test/cases/compile_errors/stage1/obj/comptime_int_in_asm_input.zig diff --git a/test/compile_errors/stage1/obj/comptime_ptrcast_of_zero-sized_type.zig b/test/cases/compile_errors/stage1/obj/comptime_ptrcast_of_zero-sized_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_ptrcast_of_zero-sized_type.zig rename to test/cases/compile_errors/stage1/obj/comptime_ptrcast_of_zero-sized_type.zig diff --git a/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig b/test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig rename to test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig diff --git a/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig b/test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig rename to test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig diff --git a/test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_target-sentinel.zig b/test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_target-sentinel.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_target-sentinel.zig rename to test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_target-sentinel.zig diff --git a/test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_terminated.zig b/test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_terminated.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_terminated.zig rename to test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_terminated.zig diff --git a/test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig b/test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig rename to test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig diff --git a/test/compile_errors/stage1/obj/comptime_slice_of_an_undefined_slice.zig b/test/cases/compile_errors/stage1/obj/comptime_slice_of_an_undefined_slice.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_slice_of_an_undefined_slice.zig rename to test/cases/compile_errors/stage1/obj/comptime_slice_of_an_undefined_slice.zig diff --git a/test/compile_errors/stage1/obj/comptime_slice_of_undefined_pointer_non-zero_len.zig b/test/cases/compile_errors/stage1/obj/comptime_slice_of_undefined_pointer_non-zero_len.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_slice_of_undefined_pointer_non-zero_len.zig rename to test/cases/compile_errors/stage1/obj/comptime_slice_of_undefined_pointer_non-zero_len.zig diff --git a/test/compile_errors/stage1/obj/comptime_struct_field_no_init_value.zig b/test/cases/compile_errors/stage1/obj/comptime_struct_field_no_init_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/comptime_struct_field_no_init_value.zig rename to test/cases/compile_errors/stage1/obj/comptime_struct_field_no_init_value.zig diff --git a/test/compile_errors/stage1/obj/const_frame_cast_to_anyframe.zig b/test/cases/compile_errors/stage1/obj/const_frame_cast_to_anyframe.zig similarity index 100% rename from test/compile_errors/stage1/obj/const_frame_cast_to_anyframe.zig rename to test/cases/compile_errors/stage1/obj/const_frame_cast_to_anyframe.zig diff --git a/test/compile_errors/stage1/obj/const_is_a_statement_not_an_expression.zig b/test/cases/compile_errors/stage1/obj/const_is_a_statement_not_an_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/const_is_a_statement_not_an_expression.zig rename to test/cases/compile_errors/stage1/obj/const_is_a_statement_not_an_expression.zig diff --git a/test/compile_errors/stage1/obj/container_init_with_non-type.zig b/test/cases/compile_errors/stage1/obj/container_init_with_non-type.zig similarity index 100% rename from test/compile_errors/stage1/obj/container_init_with_non-type.zig rename to test/cases/compile_errors/stage1/obj/container_init_with_non-type.zig diff --git a/test/compile_errors/stage1/obj/control_flow_uses_comptime_var_at_runtime.zig b/test/cases/compile_errors/stage1/obj/control_flow_uses_comptime_var_at_runtime.zig similarity index 100% rename from test/compile_errors/stage1/obj/control_flow_uses_comptime_var_at_runtime.zig rename to test/cases/compile_errors/stage1/obj/control_flow_uses_comptime_var_at_runtime.zig diff --git a/test/compile_errors/stage1/obj/control_reaches_end_of_non-void_function.zig b/test/cases/compile_errors/stage1/obj/control_reaches_end_of_non-void_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/control_reaches_end_of_non-void_function.zig rename to test/cases/compile_errors/stage1/obj/control_reaches_end_of_non-void_function.zig diff --git a/test/compile_errors/stage1/obj/declaration_between_fields.zig b/test/cases/compile_errors/stage1/obj/declaration_between_fields.zig similarity index 100% rename from test/compile_errors/stage1/obj/declaration_between_fields.zig rename to test/cases/compile_errors/stage1/obj/declaration_between_fields.zig diff --git a/test/compile_errors/stage1/obj/declaration_with_same_name_as_primitive_must_use_special_syntax.zig b/test/cases/compile_errors/stage1/obj/declaration_with_same_name_as_primitive_must_use_special_syntax.zig similarity index 100% rename from test/compile_errors/stage1/obj/declaration_with_same_name_as_primitive_must_use_special_syntax.zig rename to test/cases/compile_errors/stage1/obj/declaration_with_same_name_as_primitive_must_use_special_syntax.zig diff --git a/test/compile_errors/stage1/obj/deduplicate_undeclared_identifier.zig b/test/cases/compile_errors/stage1/obj/deduplicate_undeclared_identifier.zig similarity index 100% rename from test/compile_errors/stage1/obj/deduplicate_undeclared_identifier.zig rename to test/cases/compile_errors/stage1/obj/deduplicate_undeclared_identifier.zig diff --git a/test/compile_errors/stage1/obj/deref_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/deref_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/deref_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/deref_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig b/test/cases/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig similarity index 100% rename from test/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig rename to test/cases/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig diff --git a/test/compile_errors/stage1/obj/dereference_an_array.zig b/test/cases/compile_errors/stage1/obj/dereference_an_array.zig similarity index 100% rename from test/compile_errors/stage1/obj/dereference_an_array.zig rename to test/cases/compile_errors/stage1/obj/dereference_an_array.zig diff --git a/test/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig b/test/cases/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig rename to test/cases/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig diff --git a/test/compile_errors/stage1/obj/direct_struct_loop.zig b/test/cases/compile_errors/stage1/obj/direct_struct_loop.zig similarity index 100% rename from test/compile_errors/stage1/obj/direct_struct_loop.zig rename to test/cases/compile_errors/stage1/obj/direct_struct_loop.zig diff --git a/test/compile_errors/stage1/obj/directly_embedding_opaque_type_in_struct_and_union.zig b/test/cases/compile_errors/stage1/obj/directly_embedding_opaque_type_in_struct_and_union.zig similarity index 100% rename from test/compile_errors/stage1/obj/directly_embedding_opaque_type_in_struct_and_union.zig rename to test/cases/compile_errors/stage1/obj/directly_embedding_opaque_type_in_struct_and_union.zig diff --git a/test/compile_errors/stage1/obj/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig b/test/cases/compile_errors/stage1/obj/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig rename to test/cases/compile_errors/stage1/obj/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig diff --git a/test/compile_errors/stage1/obj/discarding_error_value.zig b/test/cases/compile_errors/stage1/obj/discarding_error_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/discarding_error_value.zig rename to test/cases/compile_errors/stage1/obj/discarding_error_value.zig diff --git a/test/compile_errors/stage1/obj/div_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/div_assign_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/div_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/div_assign_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/div_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/div_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/div_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/div_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/division_by_zero.zig b/test/cases/compile_errors/stage1/obj/division_by_zero.zig similarity index 100% rename from test/compile_errors/stage1/obj/division_by_zero.zig rename to test/cases/compile_errors/stage1/obj/division_by_zero.zig diff --git a/test/compile_errors/stage1/obj/dont_implicit_cast_double_pointer_to_anyopaque.zig b/test/cases/compile_errors/stage1/obj/dont_implicit_cast_double_pointer_to_anyopaque.zig similarity index 100% rename from test/compile_errors/stage1/obj/dont_implicit_cast_double_pointer_to_anyopaque.zig rename to test/cases/compile_errors/stage1/obj/dont_implicit_cast_double_pointer_to_anyopaque.zig diff --git a/test/compile_errors/stage1/obj/double_optional_on_main_return_value.zig b/test/cases/compile_errors/stage1/obj/double_optional_on_main_return_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/double_optional_on_main_return_value.zig rename to test/cases/compile_errors/stage1/obj/double_optional_on_main_return_value.zig diff --git a/test/compile_errors/stage1/obj/duplicate_boolean_switch_value.zig b/test/cases/compile_errors/stage1/obj/duplicate_boolean_switch_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/duplicate_boolean_switch_value.zig rename to test/cases/compile_errors/stage1/obj/duplicate_boolean_switch_value.zig diff --git a/test/compile_errors/stage1/obj/duplicate_enum_field.zig b/test/cases/compile_errors/stage1/obj/duplicate_enum_field.zig similarity index 100% rename from test/compile_errors/stage1/obj/duplicate_enum_field.zig rename to test/cases/compile_errors/stage1/obj/duplicate_enum_field.zig diff --git a/test/compile_errors/stage1/obj/duplicate_error_in_switch.zig b/test/cases/compile_errors/stage1/obj/duplicate_error_in_switch.zig similarity index 100% rename from test/compile_errors/stage1/obj/duplicate_error_in_switch.zig rename to test/cases/compile_errors/stage1/obj/duplicate_error_in_switch.zig diff --git a/test/compile_errors/stage1/obj/duplicate_error_value_in_error_set.zig b/test/cases/compile_errors/stage1/obj/duplicate_error_value_in_error_set.zig similarity index 100% rename from test/compile_errors/stage1/obj/duplicate_error_value_in_error_set.zig rename to test/cases/compile_errors/stage1/obj/duplicate_error_value_in_error_set.zig diff --git a/test/compile_errors/stage1/obj/duplicate_field_in_struct_value_expression.zig b/test/cases/compile_errors/stage1/obj/duplicate_field_in_struct_value_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/duplicate_field_in_struct_value_expression.zig rename to test/cases/compile_errors/stage1/obj/duplicate_field_in_struct_value_expression.zig diff --git a/test/compile_errors/stage1/obj/duplicate_struct_field.zig b/test/cases/compile_errors/stage1/obj/duplicate_struct_field.zig similarity index 100% rename from test/compile_errors/stage1/obj/duplicate_struct_field.zig rename to test/cases/compile_errors/stage1/obj/duplicate_struct_field.zig diff --git a/test/compile_errors/stage1/obj/duplicate_union_field.zig b/test/cases/compile_errors/stage1/obj/duplicate_union_field.zig similarity index 100% rename from test/compile_errors/stage1/obj/duplicate_union_field.zig rename to test/cases/compile_errors/stage1/obj/duplicate_union_field.zig diff --git a/test/compile_errors/stage1/obj/embedFile_with_bogus_file.zig b/test/cases/compile_errors/stage1/obj/embedFile_with_bogus_file.zig similarity index 100% rename from test/compile_errors/stage1/obj/embedFile_with_bogus_file.zig rename to test/cases/compile_errors/stage1/obj/embedFile_with_bogus_file.zig diff --git a/test/compile_errors/stage1/obj/empty_for_loop_body.zig b/test/cases/compile_errors/stage1/obj/empty_for_loop_body.zig similarity index 100% rename from test/compile_errors/stage1/obj/empty_for_loop_body.zig rename to test/cases/compile_errors/stage1/obj/empty_for_loop_body.zig diff --git a/test/compile_errors/stage1/obj/empty_if_body.zig b/test/cases/compile_errors/stage1/obj/empty_if_body.zig similarity index 100% rename from test/compile_errors/stage1/obj/empty_if_body.zig rename to test/cases/compile_errors/stage1/obj/empty_if_body.zig diff --git a/test/compile_errors/stage1/obj/empty_switch_on_an_integer.zig b/test/cases/compile_errors/stage1/obj/empty_switch_on_an_integer.zig similarity index 100% rename from test/compile_errors/stage1/obj/empty_switch_on_an_integer.zig rename to test/cases/compile_errors/stage1/obj/empty_switch_on_an_integer.zig diff --git a/test/compile_errors/stage1/obj/empty_while_loop_body.zig b/test/cases/compile_errors/stage1/obj/empty_while_loop_body.zig similarity index 100% rename from test/compile_errors/stage1/obj/empty_while_loop_body.zig rename to test/cases/compile_errors/stage1/obj/empty_while_loop_body.zig diff --git a/test/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig b/test/cases/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig similarity index 100% rename from test/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig rename to test/cases/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig diff --git a/test/compile_errors/stage1/obj/enum_field_value_references_enum.zig b/test/cases/compile_errors/stage1/obj/enum_field_value_references_enum.zig similarity index 100% rename from test/compile_errors/stage1/obj/enum_field_value_references_enum.zig rename to test/cases/compile_errors/stage1/obj/enum_field_value_references_enum.zig diff --git a/test/compile_errors/stage1/obj/enum_in_field_count_range_but_not_matching_tag.zig b/test/cases/compile_errors/stage1/obj/enum_in_field_count_range_but_not_matching_tag.zig similarity index 100% rename from test/compile_errors/stage1/obj/enum_in_field_count_range_but_not_matching_tag.zig rename to test/cases/compile_errors/stage1/obj/enum_in_field_count_range_but_not_matching_tag.zig diff --git a/test/compile_errors/stage1/obj/enum_value_already_taken.zig b/test/cases/compile_errors/stage1/obj/enum_value_already_taken.zig similarity index 100% rename from test/compile_errors/stage1/obj/enum_value_already_taken.zig rename to test/cases/compile_errors/stage1/obj/enum_value_already_taken.zig diff --git a/test/compile_errors/stage1/obj/enum_with_0_fields.zig b/test/cases/compile_errors/stage1/obj/enum_with_0_fields.zig similarity index 100% rename from test/compile_errors/stage1/obj/enum_with_0_fields.zig rename to test/cases/compile_errors/stage1/obj/enum_with_0_fields.zig diff --git a/test/compile_errors/stage1/obj/enum_with_declarations_unavailable_for_reify_type.zig b/test/cases/compile_errors/stage1/obj/enum_with_declarations_unavailable_for_reify_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/enum_with_declarations_unavailable_for_reify_type.zig rename to test/cases/compile_errors/stage1/obj/enum_with_declarations_unavailable_for_reify_type.zig diff --git a/test/compile_errors/stage1/obj/error_equality_but_sets_have_no_common_members.zig b/test/cases/compile_errors/stage1/obj/error_equality_but_sets_have_no_common_members.zig similarity index 100% rename from test/compile_errors/stage1/obj/error_equality_but_sets_have_no_common_members.zig rename to test/cases/compile_errors/stage1/obj/error_equality_but_sets_have_no_common_members.zig diff --git a/test/compile_errors/stage1/obj/error_not_handled_in_switch.zig b/test/cases/compile_errors/stage1/obj/error_not_handled_in_switch.zig similarity index 100% rename from test/compile_errors/stage1/obj/error_not_handled_in_switch.zig rename to test/cases/compile_errors/stage1/obj/error_not_handled_in_switch.zig diff --git a/test/compile_errors/stage1/obj/error_note_for_function_parameter_incompatibility.zig b/test/cases/compile_errors/stage1/obj/error_note_for_function_parameter_incompatibility.zig similarity index 100% rename from test/compile_errors/stage1/obj/error_note_for_function_parameter_incompatibility.zig rename to test/cases/compile_errors/stage1/obj/error_note_for_function_parameter_incompatibility.zig diff --git a/test/compile_errors/stage1/obj/error_union_operator_with_non_error_set_LHS.zig b/test/cases/compile_errors/stage1/obj/error_union_operator_with_non_error_set_LHS.zig similarity index 100% rename from test/compile_errors/stage1/obj/error_union_operator_with_non_error_set_LHS.zig rename to test/cases/compile_errors/stage1/obj/error_union_operator_with_non_error_set_LHS.zig diff --git a/test/compile_errors/stage1/obj/error_when_evaluating_return_type.zig b/test/cases/compile_errors/stage1/obj/error_when_evaluating_return_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/error_when_evaluating_return_type.zig rename to test/cases/compile_errors/stage1/obj/error_when_evaluating_return_type.zig diff --git a/test/compile_errors/stage1/obj/exceeded_maximum_bit_width_of_integer.zig b/test/cases/compile_errors/stage1/obj/exceeded_maximum_bit_width_of_integer.zig similarity index 100% rename from test/compile_errors/stage1/obj/exceeded_maximum_bit_width_of_integer.zig rename to test/cases/compile_errors/stage1/obj/exceeded_maximum_bit_width_of_integer.zig diff --git a/test/compile_errors/stage1/obj/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig b/test/cases/compile_errors/stage1/obj/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig similarity index 100% rename from test/compile_errors/stage1/obj/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig rename to test/cases/compile_errors/stage1/obj/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig diff --git a/test/compile_errors/stage1/obj/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig b/test/cases/compile_errors/stage1/obj/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig similarity index 100% rename from test/compile_errors/stage1/obj/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig rename to test/cases/compile_errors/stage1/obj/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig diff --git a/test/compile_errors/stage1/obj/explicitly_casting_non_tag_type_to_enum.zig b/test/cases/compile_errors/stage1/obj/explicitly_casting_non_tag_type_to_enum.zig similarity index 100% rename from test/compile_errors/stage1/obj/explicitly_casting_non_tag_type_to_enum.zig rename to test/cases/compile_errors/stage1/obj/explicitly_casting_non_tag_type_to_enum.zig diff --git a/test/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig b/test/cases/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig similarity index 100% rename from test/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig rename to test/cases/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig diff --git a/test/compile_errors/stage1/obj/export_generic_function.zig b/test/cases/compile_errors/stage1/obj/export_generic_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/export_generic_function.zig rename to test/cases/compile_errors/stage1/obj/export_generic_function.zig diff --git a/test/compile_errors/stage1/obj/exported_async_function.zig b/test/cases/compile_errors/stage1/obj/exported_async_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/exported_async_function.zig rename to test/cases/compile_errors/stage1/obj/exported_async_function.zig diff --git a/test/compile_errors/stage1/obj/exported_enum_without_explicit_integer_tag_type.zig b/test/cases/compile_errors/stage1/obj/exported_enum_without_explicit_integer_tag_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/exported_enum_without_explicit_integer_tag_type.zig rename to test/cases/compile_errors/stage1/obj/exported_enum_without_explicit_integer_tag_type.zig diff --git a/test/compile_errors/stage1/obj/extern_function_pointer_mismatch.zig b/test/cases/compile_errors/stage1/obj/extern_function_pointer_mismatch.zig similarity index 100% rename from test/compile_errors/stage1/obj/extern_function_pointer_mismatch.zig rename to test/cases/compile_errors/stage1/obj/extern_function_pointer_mismatch.zig diff --git a/test/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig b/test/cases/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig similarity index 100% rename from test/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig rename to test/cases/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig diff --git a/test/compile_errors/stage1/obj/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig b/test/cases/compile_errors/stage1/obj/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig rename to test/cases/compile_errors/stage1/obj/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig diff --git a/test/compile_errors/stage1/obj/extern_struct_with_non-extern-compatible_integer_tag_type.zig b/test/cases/compile_errors/stage1/obj/extern_struct_with_non-extern-compatible_integer_tag_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/extern_struct_with_non-extern-compatible_integer_tag_type.zig rename to test/cases/compile_errors/stage1/obj/extern_struct_with_non-extern-compatible_integer_tag_type.zig diff --git a/test/compile_errors/stage1/obj/extern_union_field_missing_type.zig b/test/cases/compile_errors/stage1/obj/extern_union_field_missing_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/extern_union_field_missing_type.zig rename to test/cases/compile_errors/stage1/obj/extern_union_field_missing_type.zig diff --git a/test/compile_errors/stage1/obj/extern_union_given_enum_tag_type.zig b/test/cases/compile_errors/stage1/obj/extern_union_given_enum_tag_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/extern_union_given_enum_tag_type.zig rename to test/cases/compile_errors/stage1/obj/extern_union_given_enum_tag_type.zig diff --git a/test/compile_errors/stage1/obj/extern_variable_has_no_type.zig b/test/cases/compile_errors/stage1/obj/extern_variable_has_no_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/extern_variable_has_no_type.zig rename to test/cases/compile_errors/stage1/obj/extern_variable_has_no_type.zig diff --git a/test/compile_errors/stage1/obj/fieldParentPtr-bad_field_name.zig b/test/cases/compile_errors/stage1/obj/fieldParentPtr-bad_field_name.zig similarity index 100% rename from test/compile_errors/stage1/obj/fieldParentPtr-bad_field_name.zig rename to test/cases/compile_errors/stage1/obj/fieldParentPtr-bad_field_name.zig diff --git a/test/compile_errors/stage1/obj/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig b/test/cases/compile_errors/stage1/obj/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig similarity index 100% rename from test/compile_errors/stage1/obj/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig rename to test/cases/compile_errors/stage1/obj/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig diff --git a/test/compile_errors/stage1/obj/fieldParentPtr-comptime_wrong_field_index.zig b/test/cases/compile_errors/stage1/obj/fieldParentPtr-comptime_wrong_field_index.zig similarity index 100% rename from test/compile_errors/stage1/obj/fieldParentPtr-comptime_wrong_field_index.zig rename to test/cases/compile_errors/stage1/obj/fieldParentPtr-comptime_wrong_field_index.zig diff --git a/test/compile_errors/stage1/obj/fieldParentPtr-field_pointer_is_not_pointer.zig b/test/cases/compile_errors/stage1/obj/fieldParentPtr-field_pointer_is_not_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/fieldParentPtr-field_pointer_is_not_pointer.zig rename to test/cases/compile_errors/stage1/obj/fieldParentPtr-field_pointer_is_not_pointer.zig diff --git a/test/compile_errors/stage1/obj/fieldParentPtr-non_struct.zig b/test/cases/compile_errors/stage1/obj/fieldParentPtr-non_struct.zig similarity index 100% rename from test/compile_errors/stage1/obj/fieldParentPtr-non_struct.zig rename to test/cases/compile_errors/stage1/obj/fieldParentPtr-non_struct.zig diff --git a/test/compile_errors/stage1/obj/field_access_of_opaque_type.zig b/test/cases/compile_errors/stage1/obj/field_access_of_opaque_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/field_access_of_opaque_type.zig rename to test/cases/compile_errors/stage1/obj/field_access_of_opaque_type.zig diff --git a/test/compile_errors/stage1/obj/field_access_of_slices.zig b/test/cases/compile_errors/stage1/obj/field_access_of_slices.zig similarity index 100% rename from test/compile_errors/stage1/obj/field_access_of_slices.zig rename to test/cases/compile_errors/stage1/obj/field_access_of_slices.zig diff --git a/test/compile_errors/stage1/obj/field_access_of_unknown_length_pointer.zig b/test/cases/compile_errors/stage1/obj/field_access_of_unknown_length_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/field_access_of_unknown_length_pointer.zig rename to test/cases/compile_errors/stage1/obj/field_access_of_unknown_length_pointer.zig diff --git a/test/compile_errors/stage1/obj/field_type_supplied_in_an_enum.zig b/test/cases/compile_errors/stage1/obj/field_type_supplied_in_an_enum.zig similarity index 100% rename from test/compile_errors/stage1/obj/field_type_supplied_in_an_enum.zig rename to test/cases/compile_errors/stage1/obj/field_type_supplied_in_an_enum.zig diff --git a/test/compile_errors/stage1/obj/floatToInt_comptime_safety.zig b/test/cases/compile_errors/stage1/obj/floatToInt_comptime_safety.zig similarity index 100% rename from test/compile_errors/stage1/obj/floatToInt_comptime_safety.zig rename to test/cases/compile_errors/stage1/obj/floatToInt_comptime_safety.zig diff --git a/test/compile_errors/stage1/obj/float_literal_too_large_error.zig b/test/cases/compile_errors/stage1/obj/float_literal_too_large_error.zig similarity index 100% rename from test/compile_errors/stage1/obj/float_literal_too_large_error.zig rename to test/cases/compile_errors/stage1/obj/float_literal_too_large_error.zig diff --git a/test/compile_errors/stage1/obj/float_literal_too_small_error_denormal.zig b/test/cases/compile_errors/stage1/obj/float_literal_too_small_error_denormal.zig similarity index 100% rename from test/compile_errors/stage1/obj/float_literal_too_small_error_denormal.zig rename to test/cases/compile_errors/stage1/obj/float_literal_too_small_error_denormal.zig diff --git a/test/compile_errors/stage1/obj/for_loop_body_expression_ignored.zig b/test/cases/compile_errors/stage1/obj/for_loop_body_expression_ignored.zig similarity index 100% rename from test/compile_errors/stage1/obj/for_loop_body_expression_ignored.zig rename to test/cases/compile_errors/stage1/obj/for_loop_body_expression_ignored.zig diff --git a/test/compile_errors/stage1/obj/frame_called_outside_of_function_definition.zig b/test/cases/compile_errors/stage1/obj/frame_called_outside_of_function_definition.zig similarity index 100% rename from test/compile_errors/stage1/obj/frame_called_outside_of_function_definition.zig rename to test/cases/compile_errors/stage1/obj/frame_called_outside_of_function_definition.zig diff --git a/test/compile_errors/stage1/obj/frame_causes_function_to_be_async.zig b/test/cases/compile_errors/stage1/obj/frame_causes_function_to_be_async.zig similarity index 100% rename from test/compile_errors/stage1/obj/frame_causes_function_to_be_async.zig rename to test/cases/compile_errors/stage1/obj/frame_causes_function_to_be_async.zig diff --git a/test/compile_errors/stage1/obj/function_alignment_non_power_of_2.zig b/test/cases/compile_errors/stage1/obj/function_alignment_non_power_of_2.zig similarity index 100% rename from test/compile_errors/stage1/obj/function_alignment_non_power_of_2.zig rename to test/cases/compile_errors/stage1/obj/function_alignment_non_power_of_2.zig diff --git a/test/compile_errors/stage1/obj/function_call_assigned_to_incorrect_type.zig b/test/cases/compile_errors/stage1/obj/function_call_assigned_to_incorrect_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/function_call_assigned_to_incorrect_type.zig rename to test/cases/compile_errors/stage1/obj/function_call_assigned_to_incorrect_type.zig diff --git a/test/compile_errors/stage1/obj/function_parameter_is_opaque.zig b/test/cases/compile_errors/stage1/obj/function_parameter_is_opaque.zig similarity index 100% rename from test/compile_errors/stage1/obj/function_parameter_is_opaque.zig rename to test/cases/compile_errors/stage1/obj/function_parameter_is_opaque.zig diff --git a/test/compile_errors/stage1/obj/function_prototype_with_no_body.zig b/test/cases/compile_errors/stage1/obj/function_prototype_with_no_body.zig similarity index 100% rename from test/compile_errors/stage1/obj/function_prototype_with_no_body.zig rename to test/cases/compile_errors/stage1/obj/function_prototype_with_no_body.zig diff --git a/test/compile_errors/stage1/obj/function_returning_opaque_type.zig b/test/cases/compile_errors/stage1/obj/function_returning_opaque_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/function_returning_opaque_type.zig rename to test/cases/compile_errors/stage1/obj/function_returning_opaque_type.zig diff --git a/test/compile_errors/stage1/obj/function_with_ccc_indirectly_calling_async_function.zig b/test/cases/compile_errors/stage1/obj/function_with_ccc_indirectly_calling_async_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/function_with_ccc_indirectly_calling_async_function.zig rename to test/cases/compile_errors/stage1/obj/function_with_ccc_indirectly_calling_async_function.zig diff --git a/test/compile_errors/stage1/obj/function_with_invalid_return_type.zig b/test/cases/compile_errors/stage1/obj/function_with_invalid_return_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/function_with_invalid_return_type.zig rename to test/cases/compile_errors/stage1/obj/function_with_invalid_return_type.zig diff --git a/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig b/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig similarity index 100% rename from test/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig rename to test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig diff --git a/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig b/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig similarity index 100% rename from test/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig rename to test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig diff --git a/test/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig b/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig similarity index 100% rename from test/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig rename to test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig diff --git a/test/compile_errors/stage1/obj/generic_fn_as_parameter_without_comptime_keyword.zig b/test/cases/compile_errors/stage1/obj/generic_fn_as_parameter_without_comptime_keyword.zig similarity index 100% rename from test/compile_errors/stage1/obj/generic_fn_as_parameter_without_comptime_keyword.zig rename to test/cases/compile_errors/stage1/obj/generic_fn_as_parameter_without_comptime_keyword.zig diff --git a/test/compile_errors/stage1/obj/generic_function_call_assigned_to_incorrect_type.zig b/test/cases/compile_errors/stage1/obj/generic_function_call_assigned_to_incorrect_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/generic_function_call_assigned_to_incorrect_type.zig rename to test/cases/compile_errors/stage1/obj/generic_function_call_assigned_to_incorrect_type.zig diff --git a/test/compile_errors/stage1/obj/generic_function_instance_with_non-constant_expression.zig b/test/cases/compile_errors/stage1/obj/generic_function_instance_with_non-constant_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/generic_function_instance_with_non-constant_expression.zig rename to test/cases/compile_errors/stage1/obj/generic_function_instance_with_non-constant_expression.zig diff --git a/test/compile_errors/stage1/obj/generic_function_returning_opaque_type.zig b/test/cases/compile_errors/stage1/obj/generic_function_returning_opaque_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/generic_function_returning_opaque_type.zig rename to test/cases/compile_errors/stage1/obj/generic_function_returning_opaque_type.zig diff --git a/test/compile_errors/stage1/obj/generic_function_where_return_type_is_self-referenced.zig b/test/cases/compile_errors/stage1/obj/generic_function_where_return_type_is_self-referenced.zig similarity index 100% rename from test/compile_errors/stage1/obj/generic_function_where_return_type_is_self-referenced.zig rename to test/cases/compile_errors/stage1/obj/generic_function_where_return_type_is_self-referenced.zig diff --git a/test/compile_errors/stage1/obj/global_variable_alignment_non_power_of_2.zig b/test/cases/compile_errors/stage1/obj/global_variable_alignment_non_power_of_2.zig similarity index 100% rename from test/compile_errors/stage1/obj/global_variable_alignment_non_power_of_2.zig rename to test/cases/compile_errors/stage1/obj/global_variable_alignment_non_power_of_2.zig diff --git a/test/compile_errors/stage1/obj/global_variable_initializer_must_be_constant_expression.zig b/test/cases/compile_errors/stage1/obj/global_variable_initializer_must_be_constant_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/global_variable_initializer_must_be_constant_expression.zig rename to test/cases/compile_errors/stage1/obj/global_variable_initializer_must_be_constant_expression.zig diff --git a/test/compile_errors/stage1/obj/hasDecl_with_non-container.zig b/test/cases/compile_errors/stage1/obj/hasDecl_with_non-container.zig similarity index 100% rename from test/compile_errors/stage1/obj/hasDecl_with_non-container.zig rename to test/cases/compile_errors/stage1/obj/hasDecl_with_non-container.zig diff --git a/test/compile_errors/stage1/obj/if_condition_is_bool_not_int.zig b/test/cases/compile_errors/stage1/obj/if_condition_is_bool_not_int.zig similarity index 100% rename from test/compile_errors/stage1/obj/if_condition_is_bool_not_int.zig rename to test/cases/compile_errors/stage1/obj/if_condition_is_bool_not_int.zig diff --git a/test/compile_errors/stage1/obj/ignored_assert-err-ok_return_value.zig b/test/cases/compile_errors/stage1/obj/ignored_assert-err-ok_return_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/ignored_assert-err-ok_return_value.zig rename to test/cases/compile_errors/stage1/obj/ignored_assert-err-ok_return_value.zig diff --git a/test/compile_errors/stage1/obj/ignored_comptime_statement_value.zig b/test/cases/compile_errors/stage1/obj/ignored_comptime_statement_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/ignored_comptime_statement_value.zig rename to test/cases/compile_errors/stage1/obj/ignored_comptime_statement_value.zig diff --git a/test/compile_errors/stage1/obj/ignored_comptime_value.zig b/test/cases/compile_errors/stage1/obj/ignored_comptime_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/ignored_comptime_value.zig rename to test/cases/compile_errors/stage1/obj/ignored_comptime_value.zig diff --git a/test/compile_errors/stage1/obj/ignored_deferred_function_call.zig b/test/cases/compile_errors/stage1/obj/ignored_deferred_function_call.zig similarity index 100% rename from test/compile_errors/stage1/obj/ignored_deferred_function_call.zig rename to test/cases/compile_errors/stage1/obj/ignored_deferred_function_call.zig diff --git a/test/compile_errors/stage1/obj/ignored_deferred_statement_value.zig b/test/cases/compile_errors/stage1/obj/ignored_deferred_statement_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/ignored_deferred_statement_value.zig rename to test/cases/compile_errors/stage1/obj/ignored_deferred_statement_value.zig diff --git a/test/compile_errors/stage1/obj/ignored_expression_in_while_continuation.zig b/test/cases/compile_errors/stage1/obj/ignored_expression_in_while_continuation.zig similarity index 100% rename from test/compile_errors/stage1/obj/ignored_expression_in_while_continuation.zig rename to test/cases/compile_errors/stage1/obj/ignored_expression_in_while_continuation.zig diff --git a/test/compile_errors/stage1/obj/ignored_return_value.zig b/test/cases/compile_errors/stage1/obj/ignored_return_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/ignored_return_value.zig rename to test/cases/compile_errors/stage1/obj/ignored_return_value.zig diff --git a/test/compile_errors/stage1/obj/ignored_statement_value.zig b/test/cases/compile_errors/stage1/obj/ignored_statement_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/ignored_statement_value.zig rename to test/cases/compile_errors/stage1/obj/ignored_statement_value.zig diff --git a/test/compile_errors/stage1/obj/illegal_comparison_of_types.zig b/test/cases/compile_errors/stage1/obj/illegal_comparison_of_types.zig similarity index 100% rename from test/compile_errors/stage1/obj/illegal_comparison_of_types.zig rename to test/cases/compile_errors/stage1/obj/illegal_comparison_of_types.zig diff --git a/test/compile_errors/stage1/obj/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig b/test/cases/compile_errors/stage1/obj/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig rename to test/cases/compile_errors/stage1/obj/implicit_cast_between_C_pointer_and_Zig_pointer-bad_const-align-child.zig diff --git a/test/compile_errors/stage1/obj/implicit_cast_const_array_to_mutable_slice.zig b/test/cases/compile_errors/stage1/obj/implicit_cast_const_array_to_mutable_slice.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_cast_const_array_to_mutable_slice.zig rename to test/cases/compile_errors/stage1/obj/implicit_cast_const_array_to_mutable_slice.zig diff --git a/test/compile_errors/stage1/obj/implicit_cast_from_array_to_mutable_slice.zig b/test/cases/compile_errors/stage1/obj/implicit_cast_from_array_to_mutable_slice.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_cast_from_array_to_mutable_slice.zig rename to test/cases/compile_errors/stage1/obj/implicit_cast_from_array_to_mutable_slice.zig diff --git a/test/compile_errors/stage1/obj/implicit_cast_from_f64_to_f32.zig b/test/cases/compile_errors/stage1/obj/implicit_cast_from_f64_to_f32.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_cast_from_f64_to_f32.zig rename to test/cases/compile_errors/stage1/obj/implicit_cast_from_f64_to_f32.zig diff --git a/test/compile_errors/stage1/obj/implicit_cast_of_error_set_not_a_subset.zig b/test/cases/compile_errors/stage1/obj/implicit_cast_of_error_set_not_a_subset.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_cast_of_error_set_not_a_subset.zig rename to test/cases/compile_errors/stage1/obj/implicit_cast_of_error_set_not_a_subset.zig diff --git a/test/compile_errors/stage1/obj/implicit_casting_C_pointers_which_would_mess_up_null_semantics.zig b/test/cases/compile_errors/stage1/obj/implicit_casting_C_pointers_which_would_mess_up_null_semantics.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_casting_C_pointers_which_would_mess_up_null_semantics.zig rename to test/cases/compile_errors/stage1/obj/implicit_casting_C_pointers_which_would_mess_up_null_semantics.zig diff --git a/test/compile_errors/stage1/obj/implicit_casting_null_c_pointer_to_zig_pointer.zig b/test/cases/compile_errors/stage1/obj/implicit_casting_null_c_pointer_to_zig_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_casting_null_c_pointer_to_zig_pointer.zig rename to test/cases/compile_errors/stage1/obj/implicit_casting_null_c_pointer_to_zig_pointer.zig diff --git a/test/compile_errors/stage1/obj/implicit_casting_too_big_integers_to_C_pointers.zig b/test/cases/compile_errors/stage1/obj/implicit_casting_too_big_integers_to_C_pointers.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_casting_too_big_integers_to_C_pointers.zig rename to test/cases/compile_errors/stage1/obj/implicit_casting_too_big_integers_to_C_pointers.zig diff --git a/test/compile_errors/stage1/obj/implicit_casting_undefined_c_pointer_to_zig_pointer.zig b/test/cases/compile_errors/stage1/obj/implicit_casting_undefined_c_pointer_to_zig_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_casting_undefined_c_pointer_to_zig_pointer.zig rename to test/cases/compile_errors/stage1/obj/implicit_casting_undefined_c_pointer_to_zig_pointer.zig diff --git a/test/compile_errors/stage1/obj/implicit_dependency_on_libc.zig b/test/cases/compile_errors/stage1/obj/implicit_dependency_on_libc.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_dependency_on_libc.zig rename to test/cases/compile_errors/stage1/obj/implicit_dependency_on_libc.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-block_expr.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-block_expr.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-block_expr.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-block_expr.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-block_statement.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-block_statement.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-block_statement.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-block_statement.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-comptime_expression.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-comptime_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-comptime_expression.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-comptime_expression.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-comptime_statement.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-comptime_statement.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-comptime_statement.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-comptime_statement.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-defer.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-defer.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-defer.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-defer.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-for_expression.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-for_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-for_expression.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-for_expression.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-for_statement.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-for_statement.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-for_statement.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-for_statement.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_expression.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_expression.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_expression.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_statement.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_statement.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_statement.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_statement.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_expression.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_expression.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if_expression.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_statement.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if_statement.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-if-else-if_statement.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if_statement.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if-else_expression.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-if-else_expression.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else_expression.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if-else_statement.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else_statement.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-if-else_statement.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else_statement.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if_expression.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-if_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-if_expression.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-if_expression.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-if_statement.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-if_statement.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-if_statement.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-if_statement.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-test_expression.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-test_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-test_expression.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-test_expression.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-test_statement.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-test_statement.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-test_statement.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-test_statement.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-while-continue_expression.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-while-continue_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-while-continue_expression.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-while-continue_expression.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-while-continue_statement.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-while-continue_statement.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-while-continue_statement.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-while-continue_statement.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-while_expression.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-while_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-while_expression.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-while_expression.zig diff --git a/test/compile_errors/stage1/obj/implicit_semicolon-while_statement.zig b/test/cases/compile_errors/stage1/obj/implicit_semicolon-while_statement.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicit_semicolon-while_statement.zig rename to test/cases/compile_errors/stage1/obj/implicit_semicolon-while_statement.zig diff --git a/test/compile_errors/stage1/obj/implicitly_casting_enum_to_tag_type.zig b/test/cases/compile_errors/stage1/obj/implicitly_casting_enum_to_tag_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicitly_casting_enum_to_tag_type.zig rename to test/cases/compile_errors/stage1/obj/implicitly_casting_enum_to_tag_type.zig diff --git a/test/compile_errors/stage1/obj/implicitly_increasing_pointer_alignment.zig b/test/cases/compile_errors/stage1/obj/implicitly_increasing_pointer_alignment.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicitly_increasing_pointer_alignment.zig rename to test/cases/compile_errors/stage1/obj/implicitly_increasing_pointer_alignment.zig diff --git a/test/compile_errors/stage1/obj/implicitly_increasing_slice_alignment.zig b/test/cases/compile_errors/stage1/obj/implicitly_increasing_slice_alignment.zig similarity index 100% rename from test/compile_errors/stage1/obj/implicitly_increasing_slice_alignment.zig rename to test/cases/compile_errors/stage1/obj/implicitly_increasing_slice_alignment.zig diff --git a/test/compile_errors/stage1/obj/import_outside_package_path.zig b/test/cases/compile_errors/stage1/obj/import_outside_package_path.zig similarity index 100% rename from test/compile_errors/stage1/obj/import_outside_package_path.zig rename to test/cases/compile_errors/stage1/obj/import_outside_package_path.zig diff --git a/test/compile_errors/stage1/obj/incompatible_sentinels.zig b/test/cases/compile_errors/stage1/obj/incompatible_sentinels.zig similarity index 100% rename from test/compile_errors/stage1/obj/incompatible_sentinels.zig rename to test/cases/compile_errors/stage1/obj/incompatible_sentinels.zig diff --git a/test/compile_errors/stage1/obj/incorrect_return_type.zig b/test/cases/compile_errors/stage1/obj/incorrect_return_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/incorrect_return_type.zig rename to test/cases/compile_errors/stage1/obj/incorrect_return_type.zig diff --git a/test/compile_errors/stage1/obj/increase_pointer_alignment_in_ptrCast.zig b/test/cases/compile_errors/stage1/obj/increase_pointer_alignment_in_ptrCast.zig similarity index 100% rename from test/compile_errors/stage1/obj/increase_pointer_alignment_in_ptrCast.zig rename to test/cases/compile_errors/stage1/obj/increase_pointer_alignment_in_ptrCast.zig diff --git a/test/compile_errors/stage1/obj/indexing_a_undefined_slice_at_comptime.zig b/test/cases/compile_errors/stage1/obj/indexing_a_undefined_slice_at_comptime.zig similarity index 100% rename from test/compile_errors/stage1/obj/indexing_a_undefined_slice_at_comptime.zig rename to test/cases/compile_errors/stage1/obj/indexing_a_undefined_slice_at_comptime.zig diff --git a/test/compile_errors/stage1/obj/indexing_an_array_of_size_zero.zig b/test/cases/compile_errors/stage1/obj/indexing_an_array_of_size_zero.zig similarity index 100% rename from test/compile_errors/stage1/obj/indexing_an_array_of_size_zero.zig rename to test/cases/compile_errors/stage1/obj/indexing_an_array_of_size_zero.zig diff --git a/test/compile_errors/stage1/obj/indexing_an_array_of_size_zero_with_runtime_index.zig b/test/cases/compile_errors/stage1/obj/indexing_an_array_of_size_zero_with_runtime_index.zig similarity index 100% rename from test/compile_errors/stage1/obj/indexing_an_array_of_size_zero_with_runtime_index.zig rename to test/cases/compile_errors/stage1/obj/indexing_an_array_of_size_zero_with_runtime_index.zig diff --git a/test/compile_errors/stage1/obj/indexing_single-item_pointer.zig b/test/cases/compile_errors/stage1/obj/indexing_single-item_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/indexing_single-item_pointer.zig rename to test/cases/compile_errors/stage1/obj/indexing_single-item_pointer.zig diff --git a/test/compile_errors/stage1/obj/indirect_recursion_of_async_functions_detected.zig b/test/cases/compile_errors/stage1/obj/indirect_recursion_of_async_functions_detected.zig similarity index 100% rename from test/compile_errors/stage1/obj/indirect_recursion_of_async_functions_detected.zig rename to test/cases/compile_errors/stage1/obj/indirect_recursion_of_async_functions_detected.zig diff --git a/test/compile_errors/stage1/obj/indirect_struct_loop.zig b/test/cases/compile_errors/stage1/obj/indirect_struct_loop.zig similarity index 100% rename from test/compile_errors/stage1/obj/indirect_struct_loop.zig rename to test/cases/compile_errors/stage1/obj/indirect_struct_loop.zig diff --git a/test/compile_errors/stage1/obj/inferred_array_size_invalid_here.zig b/test/cases/compile_errors/stage1/obj/inferred_array_size_invalid_here.zig similarity index 100% rename from test/compile_errors/stage1/obj/inferred_array_size_invalid_here.zig rename to test/cases/compile_errors/stage1/obj/inferred_array_size_invalid_here.zig diff --git a/test/compile_errors/stage1/obj/inferring_error_set_of_function_pointer.zig b/test/cases/compile_errors/stage1/obj/inferring_error_set_of_function_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/inferring_error_set_of_function_pointer.zig rename to test/cases/compile_errors/stage1/obj/inferring_error_set_of_function_pointer.zig diff --git a/test/compile_errors/stage1/obj/initializing_array_with_struct_syntax.zig b/test/cases/compile_errors/stage1/obj/initializing_array_with_struct_syntax.zig similarity index 100% rename from test/compile_errors/stage1/obj/initializing_array_with_struct_syntax.zig rename to test/cases/compile_errors/stage1/obj/initializing_array_with_struct_syntax.zig diff --git a/test/compile_errors/stage1/obj/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig b/test/cases/compile_errors/stage1/obj/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig similarity index 100% rename from test/compile_errors/stage1/obj/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig rename to test/cases/compile_errors/stage1/obj/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig diff --git a/test/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig b/test/cases/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig similarity index 100% rename from test/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig rename to test/cases/compile_errors/stage1/obj/intToPtr_with_misaligned_address.zig diff --git a/test/compile_errors/stage1/obj/int_to_err_global_invalid_number.zig b/test/cases/compile_errors/stage1/obj/int_to_err_global_invalid_number.zig similarity index 100% rename from test/compile_errors/stage1/obj/int_to_err_global_invalid_number.zig rename to test/cases/compile_errors/stage1/obj/int_to_err_global_invalid_number.zig diff --git a/test/compile_errors/stage1/obj/int_to_err_non_global_invalid_number.zig b/test/cases/compile_errors/stage1/obj/int_to_err_non_global_invalid_number.zig similarity index 100% rename from test/compile_errors/stage1/obj/int_to_err_non_global_invalid_number.zig rename to test/cases/compile_errors/stage1/obj/int_to_err_non_global_invalid_number.zig diff --git a/test/compile_errors/stage1/obj/int_to_ptr_of_0_bits.zig b/test/cases/compile_errors/stage1/obj/int_to_ptr_of_0_bits.zig similarity index 100% rename from test/compile_errors/stage1/obj/int_to_ptr_of_0_bits.zig rename to test/cases/compile_errors/stage1/obj/int_to_ptr_of_0_bits.zig diff --git a/test/compile_errors/stage1/obj/integer_cast_truncates_bits.zig b/test/cases/compile_errors/stage1/obj/integer_cast_truncates_bits.zig similarity index 100% rename from test/compile_errors/stage1/obj/integer_cast_truncates_bits.zig rename to test/cases/compile_errors/stage1/obj/integer_cast_truncates_bits.zig diff --git a/test/compile_errors/stage1/obj/integer_overflow_error.zig b/test/cases/compile_errors/stage1/obj/integer_overflow_error.zig similarity index 100% rename from test/compile_errors/stage1/obj/integer_overflow_error.zig rename to test/cases/compile_errors/stage1/obj/integer_overflow_error.zig diff --git a/test/compile_errors/stage1/obj/integer_underflow_error.zig b/test/cases/compile_errors/stage1/obj/integer_underflow_error.zig similarity index 100% rename from test/compile_errors/stage1/obj/integer_underflow_error.zig rename to test/cases/compile_errors/stage1/obj/integer_underflow_error.zig diff --git a/test/compile_errors/stage1/obj/invalid_break_expression.zig b/test/cases/compile_errors/stage1/obj/invalid_break_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_break_expression.zig rename to test/cases/compile_errors/stage1/obj/invalid_break_expression.zig diff --git a/test/compile_errors/stage1/obj/invalid_builtin_fn.zig b/test/cases/compile_errors/stage1/obj/invalid_builtin_fn.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_builtin_fn.zig rename to test/cases/compile_errors/stage1/obj/invalid_builtin_fn.zig diff --git a/test/compile_errors/stage1/obj/invalid_cast_from_integral_type_to_enum.zig b/test/cases/compile_errors/stage1/obj/invalid_cast_from_integral_type_to_enum.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_cast_from_integral_type_to_enum.zig rename to test/cases/compile_errors/stage1/obj/invalid_cast_from_integral_type_to_enum.zig diff --git a/test/compile_errors/stage1/obj/invalid_comparison_for_function_pointers.zig b/test/cases/compile_errors/stage1/obj/invalid_comparison_for_function_pointers.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_comparison_for_function_pointers.zig rename to test/cases/compile_errors/stage1/obj/invalid_comparison_for_function_pointers.zig diff --git a/test/compile_errors/stage1/obj/invalid_continue_expression.zig b/test/cases/compile_errors/stage1/obj/invalid_continue_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_continue_expression.zig rename to test/cases/compile_errors/stage1/obj/invalid_continue_expression.zig diff --git a/test/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig b/test/cases/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig rename to test/cases/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig diff --git a/test/compile_errors/stage1/obj/invalid_empty_unicode_escape.zig b/test/cases/compile_errors/stage1/obj/invalid_empty_unicode_escape.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_empty_unicode_escape.zig rename to test/cases/compile_errors/stage1/obj/invalid_empty_unicode_escape.zig diff --git a/test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-1.zig b/test/cases/compile_errors/stage1/obj/invalid_exponent_in_float_literal-1.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-1.zig rename to test/cases/compile_errors/stage1/obj/invalid_exponent_in_float_literal-1.zig diff --git a/test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-2.zig b/test/cases/compile_errors/stage1/obj/invalid_exponent_in_float_literal-2.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_exponent_in_float_literal-2.zig rename to test/cases/compile_errors/stage1/obj/invalid_exponent_in_float_literal-2.zig diff --git a/test/compile_errors/stage1/obj/invalid_field_access_in_comptime.zig b/test/cases/compile_errors/stage1/obj/invalid_field_access_in_comptime.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_field_access_in_comptime.zig rename to test/cases/compile_errors/stage1/obj/invalid_field_access_in_comptime.zig diff --git a/test/compile_errors/stage1/obj/invalid_field_in_struct_value_expression.zig b/test/cases/compile_errors/stage1/obj/invalid_field_in_struct_value_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_field_in_struct_value_expression.zig rename to test/cases/compile_errors/stage1/obj/invalid_field_in_struct_value_expression.zig diff --git a/test/compile_errors/stage1/obj/invalid_float_literal.zig b/test/cases/compile_errors/stage1/obj/invalid_float_literal.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_float_literal.zig rename to test/cases/compile_errors/stage1/obj/invalid_float_literal.zig diff --git a/test/compile_errors/stage1/obj/invalid_legacy_unicode_escape.zig b/test/cases/compile_errors/stage1/obj/invalid_legacy_unicode_escape.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_legacy_unicode_escape.zig rename to test/cases/compile_errors/stage1/obj/invalid_legacy_unicode_escape.zig diff --git a/test/compile_errors/stage1/obj/invalid_maybe_type.zig b/test/cases/compile_errors/stage1/obj/invalid_maybe_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_maybe_type.zig rename to test/cases/compile_errors/stage1/obj/invalid_maybe_type.zig diff --git a/test/compile_errors/stage1/obj/invalid_member_of_builtin_enum.zig b/test/cases/compile_errors/stage1/obj/invalid_member_of_builtin_enum.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_member_of_builtin_enum.zig rename to test/cases/compile_errors/stage1/obj/invalid_member_of_builtin_enum.zig diff --git a/test/compile_errors/stage1/obj/invalid_multiple_dereferences.zig b/test/cases/compile_errors/stage1/obj/invalid_multiple_dereferences.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_multiple_dereferences.zig rename to test/cases/compile_errors/stage1/obj/invalid_multiple_dereferences.zig diff --git a/test/compile_errors/stage1/obj/invalid_optional_type_in_extern_struct.zig b/test/cases/compile_errors/stage1/obj/invalid_optional_type_in_extern_struct.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_optional_type_in_extern_struct.zig rename to test/cases/compile_errors/stage1/obj/invalid_optional_type_in_extern_struct.zig diff --git a/test/compile_errors/stage1/obj/invalid_pointer_for_var_type.zig b/test/cases/compile_errors/stage1/obj/invalid_pointer_for_var_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_pointer_for_var_type.zig rename to test/cases/compile_errors/stage1/obj/invalid_pointer_for_var_type.zig diff --git a/test/compile_errors/stage1/obj/invalid_pointer_syntax.zig b/test/cases/compile_errors/stage1/obj/invalid_pointer_syntax.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_pointer_syntax.zig rename to test/cases/compile_errors/stage1/obj/invalid_pointer_syntax.zig diff --git a/test/compile_errors/stage1/obj/invalid_shift_amount_error.zig b/test/cases/compile_errors/stage1/obj/invalid_shift_amount_error.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_shift_amount_error.zig rename to test/cases/compile_errors/stage1/obj/invalid_shift_amount_error.zig diff --git a/test/compile_errors/stage1/obj/invalid_struct_field.zig b/test/cases/compile_errors/stage1/obj/invalid_struct_field.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_struct_field.zig rename to test/cases/compile_errors/stage1/obj/invalid_struct_field.zig diff --git a/test/compile_errors/stage1/obj/invalid_suspend_in_exported_function.zig b/test/cases/compile_errors/stage1/obj/invalid_suspend_in_exported_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_suspend_in_exported_function.zig rename to test/cases/compile_errors/stage1/obj/invalid_suspend_in_exported_function.zig diff --git a/test/compile_errors/stage1/obj/invalid_type.zig b/test/cases/compile_errors/stage1/obj/invalid_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_type.zig rename to test/cases/compile_errors/stage1/obj/invalid_type.zig diff --git a/test/compile_errors/stage1/obj/invalid_type_used_in_array_type.zig b/test/cases/compile_errors/stage1/obj/invalid_type_used_in_array_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_type_used_in_array_type.zig rename to test/cases/compile_errors/stage1/obj/invalid_type_used_in_array_type.zig diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-1.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-1.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-1.zig rename to test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-1.zig diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-10.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-10.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-10.zig rename to test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-10.zig diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-11.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-11.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-11.zig rename to test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-11.zig diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-12.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-12.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-12.zig rename to test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-12.zig diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-13.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-13.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-13.zig rename to test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-13.zig diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-14.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-14.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-14.zig rename to test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-14.zig diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-2.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-2.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-2.zig rename to test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-2.zig diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-3.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-3.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-3.zig rename to test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-3.zig diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-4.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-4.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-4.zig rename to test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-4.zig diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-5.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-5.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-5.zig rename to test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-5.zig diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-6.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-6.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-6.zig rename to test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-6.zig diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-7.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-7.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-7.zig rename to test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-7.zig diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-9.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-9.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-9.zig rename to test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-9.zig diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-1.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-1.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-1.zig rename to test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-1.zig diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-2.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-2.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-2.zig rename to test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-2.zig diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-3.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-3.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-3.zig rename to test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-3.zig diff --git a/test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-4.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-4.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-4.zig rename to test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-4.zig diff --git a/test/compile_errors/stage1/obj/invalid_union_field_access_in_comptime.zig b/test/cases/compile_errors/stage1/obj/invalid_union_field_access_in_comptime.zig similarity index 100% rename from test/compile_errors/stage1/obj/invalid_union_field_access_in_comptime.zig rename to test/cases/compile_errors/stage1/obj/invalid_union_field_access_in_comptime.zig diff --git a/test/compile_errors/stage1/obj/issue_2032_compile_diagnostic_string_for_top_level_decl_type.zig b/test/cases/compile_errors/stage1/obj/issue_2032_compile_diagnostic_string_for_top_level_decl_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/issue_2032_compile_diagnostic_string_for_top_level_decl_type.zig rename to test/cases/compile_errors/stage1/obj/issue_2032_compile_diagnostic_string_for_top_level_decl_type.zig diff --git a/test/compile_errors/stage1/obj/issue_2687_coerce_from_undefined_array_pointer_to_slice.zig b/test/cases/compile_errors/stage1/obj/issue_2687_coerce_from_undefined_array_pointer_to_slice.zig similarity index 100% rename from test/compile_errors/stage1/obj/issue_2687_coerce_from_undefined_array_pointer_to_slice.zig rename to test/cases/compile_errors/stage1/obj/issue_2687_coerce_from_undefined_array_pointer_to_slice.zig diff --git a/test/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig b/test/cases/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig similarity index 100% rename from test/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig rename to test/cases/compile_errors/stage1/obj/issue_3818_bitcast_from_parray-slice_to_u16.zig diff --git a/test/compile_errors/stage1/obj/issue_4207_coerce_from_non-terminated-slice_to_terminated-pointer.zig b/test/cases/compile_errors/stage1/obj/issue_4207_coerce_from_non-terminated-slice_to_terminated-pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/issue_4207_coerce_from_non-terminated-slice_to_terminated-pointer.zig rename to test/cases/compile_errors/stage1/obj/issue_4207_coerce_from_non-terminated-slice_to_terminated-pointer.zig diff --git a/test/compile_errors/stage1/obj/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig b/test/cases/compile_errors/stage1/obj/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig rename to test/cases/compile_errors/stage1/obj/issue_5221_invalid_struct_init_type_referenced_by_typeInfo_and_passed_into_function.zig diff --git a/test/compile_errors/stage1/obj/issue_5618_coercion_of_optional_anyopaque_to_anyopaque_must_fail.zig b/test/cases/compile_errors/stage1/obj/issue_5618_coercion_of_optional_anyopaque_to_anyopaque_must_fail.zig similarity index 100% rename from test/compile_errors/stage1/obj/issue_5618_coercion_of_optional_anyopaque_to_anyopaque_must_fail.zig rename to test/cases/compile_errors/stage1/obj/issue_5618_coercion_of_optional_anyopaque_to_anyopaque_must_fail.zig diff --git a/test/compile_errors/stage1/obj/issue_7810-comptime_slice-len_increment_beyond_bounds.zig b/test/cases/compile_errors/stage1/obj/issue_7810-comptime_slice-len_increment_beyond_bounds.zig similarity index 100% rename from test/compile_errors/stage1/obj/issue_7810-comptime_slice-len_increment_beyond_bounds.zig rename to test/cases/compile_errors/stage1/obj/issue_7810-comptime_slice-len_increment_beyond_bounds.zig diff --git a/test/compile_errors/stage1/obj/issue_9346_return_outside_of_function_scope.zig b/test/cases/compile_errors/stage1/obj/issue_9346_return_outside_of_function_scope.zig similarity index 100% rename from test/compile_errors/stage1/obj/issue_9346_return_outside_of_function_scope.zig rename to test/cases/compile_errors/stage1/obj/issue_9346_return_outside_of_function_scope.zig diff --git a/test/compile_errors/stage1/obj/labeled_break_not_found.zig b/test/cases/compile_errors/stage1/obj/labeled_break_not_found.zig similarity index 100% rename from test/compile_errors/stage1/obj/labeled_break_not_found.zig rename to test/cases/compile_errors/stage1/obj/labeled_break_not_found.zig diff --git a/test/compile_errors/stage1/obj/labeled_continue_not_found.zig b/test/cases/compile_errors/stage1/obj/labeled_continue_not_found.zig similarity index 100% rename from test/compile_errors/stage1/obj/labeled_continue_not_found.zig rename to test/cases/compile_errors/stage1/obj/labeled_continue_not_found.zig diff --git a/test/compile_errors/stage1/obj/lazy_pointer_with_undefined_element_type.zig b/test/cases/compile_errors/stage1/obj/lazy_pointer_with_undefined_element_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/lazy_pointer_with_undefined_element_type.zig rename to test/cases/compile_errors/stage1/obj/lazy_pointer_with_undefined_element_type.zig diff --git a/test/compile_errors/stage1/obj/libc_headers_note.zig b/test/cases/compile_errors/stage1/obj/libc_headers_note.zig similarity index 100% rename from test/compile_errors/stage1/obj/libc_headers_note.zig rename to test/cases/compile_errors/stage1/obj/libc_headers_note.zig diff --git a/test/compile_errors/stage1/obj/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig b/test/cases/compile_errors/stage1/obj/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig rename to test/cases/compile_errors/stage1/obj/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig diff --git a/test/compile_errors/stage1/obj/load_vector_pointer_with_unknown_runtime_index.zig b/test/cases/compile_errors/stage1/obj/load_vector_pointer_with_unknown_runtime_index.zig similarity index 100% rename from test/compile_errors/stage1/obj/load_vector_pointer_with_unknown_runtime_index.zig rename to test/cases/compile_errors/stage1/obj/load_vector_pointer_with_unknown_runtime_index.zig diff --git a/test/compile_errors/stage1/obj/local_shadows_global_that_occurs_later.zig b/test/cases/compile_errors/stage1/obj/local_shadows_global_that_occurs_later.zig similarity index 100% rename from test/compile_errors/stage1/obj/local_shadows_global_that_occurs_later.zig rename to test/cases/compile_errors/stage1/obj/local_shadows_global_that_occurs_later.zig diff --git a/test/compile_errors/stage1/obj/local_variable_redeclaration.zig b/test/cases/compile_errors/stage1/obj/local_variable_redeclaration.zig similarity index 100% rename from test/compile_errors/stage1/obj/local_variable_redeclaration.zig rename to test/cases/compile_errors/stage1/obj/local_variable_redeclaration.zig diff --git a/test/compile_errors/stage1/obj/local_variable_redeclares_parameter.zig b/test/cases/compile_errors/stage1/obj/local_variable_redeclares_parameter.zig similarity index 100% rename from test/compile_errors/stage1/obj/local_variable_redeclares_parameter.zig rename to test/cases/compile_errors/stage1/obj/local_variable_redeclares_parameter.zig diff --git a/test/compile_errors/stage1/obj/local_variable_shadowing_global.zig b/test/cases/compile_errors/stage1/obj/local_variable_shadowing_global.zig similarity index 100% rename from test/compile_errors/stage1/obj/local_variable_shadowing_global.zig rename to test/cases/compile_errors/stage1/obj/local_variable_shadowing_global.zig diff --git a/test/compile_errors/stage1/obj/locally_shadowing_a_primitive_type.zig b/test/cases/compile_errors/stage1/obj/locally_shadowing_a_primitive_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/locally_shadowing_a_primitive_type.zig rename to test/cases/compile_errors/stage1/obj/locally_shadowing_a_primitive_type.zig diff --git a/test/compile_errors/stage1/obj/main_function_with_bogus_args_type.zig b/test/cases/compile_errors/stage1/obj/main_function_with_bogus_args_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/main_function_with_bogus_args_type.zig rename to test/cases/compile_errors/stage1/obj/main_function_with_bogus_args_type.zig diff --git a/test/compile_errors/stage1/obj/method_call_with_first_arg_type_primitive.zig b/test/cases/compile_errors/stage1/obj/method_call_with_first_arg_type_primitive.zig similarity index 100% rename from test/compile_errors/stage1/obj/method_call_with_first_arg_type_primitive.zig rename to test/cases/compile_errors/stage1/obj/method_call_with_first_arg_type_primitive.zig diff --git a/test/compile_errors/stage1/obj/method_call_with_first_arg_type_wrong_container.zig b/test/cases/compile_errors/stage1/obj/method_call_with_first_arg_type_wrong_container.zig similarity index 100% rename from test/compile_errors/stage1/obj/method_call_with_first_arg_type_wrong_container.zig rename to test/cases/compile_errors/stage1/obj/method_call_with_first_arg_type_wrong_container.zig diff --git a/test/compile_errors/stage1/obj/missing_boolean_switch_value.zig b/test/cases/compile_errors/stage1/obj/missing_boolean_switch_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/missing_boolean_switch_value.zig rename to test/cases/compile_errors/stage1/obj/missing_boolean_switch_value.zig diff --git a/test/compile_errors/stage1/obj/missing_const_in_slice_with_nested_array_type.zig b/test/cases/compile_errors/stage1/obj/missing_const_in_slice_with_nested_array_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/missing_const_in_slice_with_nested_array_type.zig rename to test/cases/compile_errors/stage1/obj/missing_const_in_slice_with_nested_array_type.zig diff --git a/test/compile_errors/stage1/obj/missing_else_clause.zig b/test/cases/compile_errors/stage1/obj/missing_else_clause.zig similarity index 100% rename from test/compile_errors/stage1/obj/missing_else_clause.zig rename to test/cases/compile_errors/stage1/obj/missing_else_clause.zig diff --git a/test/compile_errors/stage1/obj/missing_field_in_struct_value_expression.zig b/test/cases/compile_errors/stage1/obj/missing_field_in_struct_value_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/missing_field_in_struct_value_expression.zig rename to test/cases/compile_errors/stage1/obj/missing_field_in_struct_value_expression.zig diff --git a/test/compile_errors/stage1/obj/missing_function_call_param.zig b/test/cases/compile_errors/stage1/obj/missing_function_call_param.zig similarity index 100% rename from test/compile_errors/stage1/obj/missing_function_call_param.zig rename to test/cases/compile_errors/stage1/obj/missing_function_call_param.zig diff --git a/test/compile_errors/stage1/obj/missing_function_name.zig b/test/cases/compile_errors/stage1/obj/missing_function_name.zig similarity index 100% rename from test/compile_errors/stage1/obj/missing_function_name.zig rename to test/cases/compile_errors/stage1/obj/missing_function_name.zig diff --git a/test/compile_errors/stage1/obj/missing_param_name.zig b/test/cases/compile_errors/stage1/obj/missing_param_name.zig similarity index 100% rename from test/compile_errors/stage1/obj/missing_param_name.zig rename to test/cases/compile_errors/stage1/obj/missing_param_name.zig diff --git a/test/compile_errors/stage1/obj/missing_parameter_name_of_generic_function.zig b/test/cases/compile_errors/stage1/obj/missing_parameter_name_of_generic_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/missing_parameter_name_of_generic_function.zig rename to test/cases/compile_errors/stage1/obj/missing_parameter_name_of_generic_function.zig diff --git a/test/compile_errors/stage1/obj/missing_result_type_for_phi_node.zig b/test/cases/compile_errors/stage1/obj/missing_result_type_for_phi_node.zig similarity index 100% rename from test/compile_errors/stage1/obj/missing_result_type_for_phi_node.zig rename to test/cases/compile_errors/stage1/obj/missing_result_type_for_phi_node.zig diff --git a/test/compile_errors/stage1/obj/misspelled_type_with_pointer_only_reference.zig b/test/cases/compile_errors/stage1/obj/misspelled_type_with_pointer_only_reference.zig similarity index 100% rename from test/compile_errors/stage1/obj/misspelled_type_with_pointer_only_reference.zig rename to test/cases/compile_errors/stage1/obj/misspelled_type_with_pointer_only_reference.zig diff --git a/test/compile_errors/stage1/obj/mod_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/mod_assign_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/mod_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/mod_assign_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/mod_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/mod_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/mod_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/mod_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/mul_overflow_in_function_evaluation.zig b/test/cases/compile_errors/stage1/obj/mul_overflow_in_function_evaluation.zig similarity index 100% rename from test/compile_errors/stage1/obj/mul_overflow_in_function_evaluation.zig rename to test/cases/compile_errors/stage1/obj/mul_overflow_in_function_evaluation.zig diff --git a/test/compile_errors/stage1/obj/mult_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/mult_assign_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/mult_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/mult_assign_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/mult_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/mult_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/mult_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/mult_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/mult_wrap_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/mult_wrap_assign_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/mult_wrap_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/mult_wrap_assign_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/mult_wrap_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/mult_wrap_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/mult_wrap_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/mult_wrap_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/multiple_function_definitions.zig b/test/cases/compile_errors/stage1/obj/multiple_function_definitions.zig similarity index 100% rename from test/compile_errors/stage1/obj/multiple_function_definitions.zig rename to test/cases/compile_errors/stage1/obj/multiple_function_definitions.zig diff --git a/test/compile_errors/stage1/obj/negate_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/negate_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/negate_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/negate_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/negate_wrap_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/negate_wrap_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/negate_wrap_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/negate_wrap_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/negation_overflow_in_function_evaluation.zig b/test/cases/compile_errors/stage1/obj/negation_overflow_in_function_evaluation.zig similarity index 100% rename from test/compile_errors/stage1/obj/negation_overflow_in_function_evaluation.zig rename to test/cases/compile_errors/stage1/obj/negation_overflow_in_function_evaluation.zig diff --git a/test/compile_errors/stage1/obj/nested_error_set_mismatch.zig b/test/cases/compile_errors/stage1/obj/nested_error_set_mismatch.zig similarity index 100% rename from test/compile_errors/stage1/obj/nested_error_set_mismatch.zig rename to test/cases/compile_errors/stage1/obj/nested_error_set_mismatch.zig diff --git a/test/compile_errors/stage1/obj/no_else_prong_on_switch_on_global_error_set.zig b/test/cases/compile_errors/stage1/obj/no_else_prong_on_switch_on_global_error_set.zig similarity index 100% rename from test/compile_errors/stage1/obj/no_else_prong_on_switch_on_global_error_set.zig rename to test/cases/compile_errors/stage1/obj/no_else_prong_on_switch_on_global_error_set.zig diff --git a/test/compile_errors/stage1/obj/noalias_on_non_pointer_param.zig b/test/cases/compile_errors/stage1/obj/noalias_on_non_pointer_param.zig similarity index 100% rename from test/compile_errors/stage1/obj/noalias_on_non_pointer_param.zig rename to test/cases/compile_errors/stage1/obj/noalias_on_non_pointer_param.zig diff --git a/test/compile_errors/stage1/obj/non-async_function_pointer_eventually_is_inferred_to_become_async.zig b/test/cases/compile_errors/stage1/obj/non-async_function_pointer_eventually_is_inferred_to_become_async.zig similarity index 100% rename from test/compile_errors/stage1/obj/non-async_function_pointer_eventually_is_inferred_to_become_async.zig rename to test/cases/compile_errors/stage1/obj/non-async_function_pointer_eventually_is_inferred_to_become_async.zig diff --git a/test/compile_errors/stage1/obj/non-const_expression_function_call_with_struct_return_value_outside_function.zig b/test/cases/compile_errors/stage1/obj/non-const_expression_function_call_with_struct_return_value_outside_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/non-const_expression_function_call_with_struct_return_value_outside_function.zig rename to test/cases/compile_errors/stage1/obj/non-const_expression_function_call_with_struct_return_value_outside_function.zig diff --git a/test/compile_errors/stage1/obj/non-const_expression_in_struct_literal_outside_function.zig b/test/cases/compile_errors/stage1/obj/non-const_expression_in_struct_literal_outside_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/non-const_expression_in_struct_literal_outside_function.zig rename to test/cases/compile_errors/stage1/obj/non-const_expression_in_struct_literal_outside_function.zig diff --git a/test/compile_errors/stage1/obj/non-const_switch_number_literal.zig b/test/cases/compile_errors/stage1/obj/non-const_switch_number_literal.zig similarity index 100% rename from test/compile_errors/stage1/obj/non-const_switch_number_literal.zig rename to test/cases/compile_errors/stage1/obj/non-const_switch_number_literal.zig diff --git a/test/compile_errors/stage1/obj/non-const_variables_of_things_that_require_const_variables.zig b/test/cases/compile_errors/stage1/obj/non-const_variables_of_things_that_require_const_variables.zig similarity index 100% rename from test/compile_errors/stage1/obj/non-const_variables_of_things_that_require_const_variables.zig rename to test/cases/compile_errors/stage1/obj/non-const_variables_of_things_that_require_const_variables.zig diff --git a/test/compile_errors/stage1/obj/non-enum_tag_type_passed_to_union.zig b/test/cases/compile_errors/stage1/obj/non-enum_tag_type_passed_to_union.zig similarity index 100% rename from test/compile_errors/stage1/obj/non-enum_tag_type_passed_to_union.zig rename to test/cases/compile_errors/stage1/obj/non-enum_tag_type_passed_to_union.zig diff --git a/test/compile_errors/stage1/obj/non-extern_function_with_var_args.zig b/test/cases/compile_errors/stage1/obj/non-extern_function_with_var_args.zig similarity index 100% rename from test/compile_errors/stage1/obj/non-extern_function_with_var_args.zig rename to test/cases/compile_errors/stage1/obj/non-extern_function_with_var_args.zig diff --git a/test/compile_errors/stage1/obj/non-inline_for_loop_on_a_type_that_requires_comptime.zig b/test/cases/compile_errors/stage1/obj/non-inline_for_loop_on_a_type_that_requires_comptime.zig similarity index 100% rename from test/compile_errors/stage1/obj/non-inline_for_loop_on_a_type_that_requires_comptime.zig rename to test/cases/compile_errors/stage1/obj/non-inline_for_loop_on_a_type_that_requires_comptime.zig diff --git a/test/compile_errors/stage1/obj/non-integer_tag_type_to_automatic_union_enum.zig b/test/cases/compile_errors/stage1/obj/non-integer_tag_type_to_automatic_union_enum.zig similarity index 100% rename from test/compile_errors/stage1/obj/non-integer_tag_type_to_automatic_union_enum.zig rename to test/cases/compile_errors/stage1/obj/non-integer_tag_type_to_automatic_union_enum.zig diff --git a/test/compile_errors/stage1/obj/non-pure_function_returns_type.zig b/test/cases/compile_errors/stage1/obj/non-pure_function_returns_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/non-pure_function_returns_type.zig rename to test/cases/compile_errors/stage1/obj/non-pure_function_returns_type.zig diff --git a/test/compile_errors/stage1/obj/non_async_function_pointer_passed_to_asyncCall.zig b/test/cases/compile_errors/stage1/obj/non_async_function_pointer_passed_to_asyncCall.zig similarity index 100% rename from test/compile_errors/stage1/obj/non_async_function_pointer_passed_to_asyncCall.zig rename to test/cases/compile_errors/stage1/obj/non_async_function_pointer_passed_to_asyncCall.zig diff --git a/test/compile_errors/stage1/obj/non_compile_time_array_concatenation.zig b/test/cases/compile_errors/stage1/obj/non_compile_time_array_concatenation.zig similarity index 100% rename from test/compile_errors/stage1/obj/non_compile_time_array_concatenation.zig rename to test/cases/compile_errors/stage1/obj/non_compile_time_array_concatenation.zig diff --git a/test/compile_errors/stage1/obj/non_constant_expression_in_array_size.zig b/test/cases/compile_errors/stage1/obj/non_constant_expression_in_array_size.zig similarity index 100% rename from test/compile_errors/stage1/obj/non_constant_expression_in_array_size.zig rename to test/cases/compile_errors/stage1/obj/non_constant_expression_in_array_size.zig diff --git a/test/compile_errors/stage1/obj/non_error_sets_used_in_merge_error_sets_operator.zig b/test/cases/compile_errors/stage1/obj/non_error_sets_used_in_merge_error_sets_operator.zig similarity index 100% rename from test/compile_errors/stage1/obj/non_error_sets_used_in_merge_error_sets_operator.zig rename to test/cases/compile_errors/stage1/obj/non_error_sets_used_in_merge_error_sets_operator.zig diff --git a/test/compile_errors/stage1/obj/non_float_passed_to_floatToInt.zig b/test/cases/compile_errors/stage1/obj/non_float_passed_to_floatToInt.zig similarity index 100% rename from test/compile_errors/stage1/obj/non_float_passed_to_floatToInt.zig rename to test/cases/compile_errors/stage1/obj/non_float_passed_to_floatToInt.zig diff --git a/test/compile_errors/stage1/obj/non_int_passed_to_intToFloat.zig b/test/cases/compile_errors/stage1/obj/non_int_passed_to_intToFloat.zig similarity index 100% rename from test/compile_errors/stage1/obj/non_int_passed_to_intToFloat.zig rename to test/cases/compile_errors/stage1/obj/non_int_passed_to_intToFloat.zig diff --git a/test/compile_errors/stage1/obj/non_pointer_given_to_ptrToInt.zig b/test/cases/compile_errors/stage1/obj/non_pointer_given_to_ptrToInt.zig similarity index 100% rename from test/compile_errors/stage1/obj/non_pointer_given_to_ptrToInt.zig rename to test/cases/compile_errors/stage1/obj/non_pointer_given_to_ptrToInt.zig diff --git a/test/compile_errors/stage1/obj/normal_string_with_newline.zig b/test/cases/compile_errors/stage1/obj/normal_string_with_newline.zig similarity index 100% rename from test/compile_errors/stage1/obj/normal_string_with_newline.zig rename to test/cases/compile_errors/stage1/obj/normal_string_with_newline.zig diff --git a/test/compile_errors/stage1/obj/offsetOf-bad_field_name.zig b/test/cases/compile_errors/stage1/obj/offsetOf-bad_field_name.zig similarity index 100% rename from test/compile_errors/stage1/obj/offsetOf-bad_field_name.zig rename to test/cases/compile_errors/stage1/obj/offsetOf-bad_field_name.zig diff --git a/test/compile_errors/stage1/obj/offsetOf-non_struct.zig b/test/cases/compile_errors/stage1/obj/offsetOf-non_struct.zig similarity index 100% rename from test/compile_errors/stage1/obj/offsetOf-non_struct.zig rename to test/cases/compile_errors/stage1/obj/offsetOf-non_struct.zig diff --git a/test/compile_errors/stage1/obj/only_equality_binary_operator_allowed_for_error_sets.zig b/test/cases/compile_errors/stage1/obj/only_equality_binary_operator_allowed_for_error_sets.zig similarity index 100% rename from test/compile_errors/stage1/obj/only_equality_binary_operator_allowed_for_error_sets.zig rename to test/cases/compile_errors/stage1/obj/only_equality_binary_operator_allowed_for_error_sets.zig diff --git a/test/compile_errors/stage1/obj/opaque_type_with_field.zig b/test/cases/compile_errors/stage1/obj/opaque_type_with_field.zig similarity index 100% rename from test/compile_errors/stage1/obj/opaque_type_with_field.zig rename to test/cases/compile_errors/stage1/obj/opaque_type_with_field.zig diff --git a/test/compile_errors/stage1/obj/optional_pointer_to_void_in_extern_struct.zig b/test/cases/compile_errors/stage1/obj/optional_pointer_to_void_in_extern_struct.zig similarity index 100% rename from test/compile_errors/stage1/obj/optional_pointer_to_void_in_extern_struct.zig rename to test/cases/compile_errors/stage1/obj/optional_pointer_to_void_in_extern_struct.zig diff --git a/test/compile_errors/stage1/obj/or_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/or_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/or_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/or_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/orelse_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/orelse_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/orelse_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/orelse_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/out_of_range_comptime_int_passed_to_floatToInt.zig b/test/cases/compile_errors/stage1/obj/out_of_range_comptime_int_passed_to_floatToInt.zig similarity index 100% rename from test/compile_errors/stage1/obj/out_of_range_comptime_int_passed_to_floatToInt.zig rename to test/cases/compile_errors/stage1/obj/out_of_range_comptime_int_passed_to_floatToInt.zig diff --git a/test/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig b/test/cases/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig similarity index 100% rename from test/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig rename to test/cases/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig diff --git a/test/compile_errors/stage1/obj/packed_union_given_enum_tag_type.zig b/test/cases/compile_errors/stage1/obj/packed_union_given_enum_tag_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/packed_union_given_enum_tag_type.zig rename to test/cases/compile_errors/stage1/obj/packed_union_given_enum_tag_type.zig diff --git a/test/compile_errors/stage1/obj/packed_union_with_automatic_layout_field.zig b/test/cases/compile_errors/stage1/obj/packed_union_with_automatic_layout_field.zig similarity index 100% rename from test/compile_errors/stage1/obj/packed_union_with_automatic_layout_field.zig rename to test/cases/compile_errors/stage1/obj/packed_union_with_automatic_layout_field.zig diff --git a/test/compile_errors/stage1/obj/panic_called_at_compile_time.zig b/test/cases/compile_errors/stage1/obj/panic_called_at_compile_time.zig similarity index 100% rename from test/compile_errors/stage1/obj/panic_called_at_compile_time.zig rename to test/cases/compile_errors/stage1/obj/panic_called_at_compile_time.zig diff --git a/test/compile_errors/stage1/obj/parameter_redeclaration.zig b/test/cases/compile_errors/stage1/obj/parameter_redeclaration.zig similarity index 100% rename from test/compile_errors/stage1/obj/parameter_redeclaration.zig rename to test/cases/compile_errors/stage1/obj/parameter_redeclaration.zig diff --git a/test/compile_errors/stage1/obj/parameter_shadowing_global.zig b/test/cases/compile_errors/stage1/obj/parameter_shadowing_global.zig similarity index 100% rename from test/compile_errors/stage1/obj/parameter_shadowing_global.zig rename to test/cases/compile_errors/stage1/obj/parameter_shadowing_global.zig diff --git a/test/compile_errors/stage1/obj/pass_const_ptr_to_mutable_ptr_fn.zig b/test/cases/compile_errors/stage1/obj/pass_const_ptr_to_mutable_ptr_fn.zig similarity index 100% rename from test/compile_errors/stage1/obj/pass_const_ptr_to_mutable_ptr_fn.zig rename to test/cases/compile_errors/stage1/obj/pass_const_ptr_to_mutable_ptr_fn.zig diff --git a/test/compile_errors/stage1/obj/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig b/test/cases/compile_errors/stage1/obj/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig similarity index 100% rename from test/compile_errors/stage1/obj/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig rename to test/cases/compile_errors/stage1/obj/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig diff --git a/test/compile_errors/stage1/obj/passing_an_under-aligned_function_pointer.zig b/test/cases/compile_errors/stage1/obj/passing_an_under-aligned_function_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/passing_an_under-aligned_function_pointer.zig rename to test/cases/compile_errors/stage1/obj/passing_an_under-aligned_function_pointer.zig diff --git a/test/compile_errors/stage1/obj/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig b/test/cases/compile_errors/stage1/obj/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig rename to test/cases/compile_errors/stage1/obj/peer_cast_then_implicit_cast_const_pointer_to_mutable_C_pointer.zig diff --git a/test/compile_errors/stage1/obj/pointer_arithmetic_on_pointer-to-array.zig b/test/cases/compile_errors/stage1/obj/pointer_arithmetic_on_pointer-to-array.zig similarity index 100% rename from test/compile_errors/stage1/obj/pointer_arithmetic_on_pointer-to-array.zig rename to test/cases/compile_errors/stage1/obj/pointer_arithmetic_on_pointer-to-array.zig diff --git a/test/compile_errors/stage1/obj/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig b/test/cases/compile_errors/stage1/obj/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig similarity index 100% rename from test/compile_errors/stage1/obj/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig rename to test/cases/compile_errors/stage1/obj/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig diff --git a/test/compile_errors/stage1/obj/pointer_to_noreturn.zig b/test/cases/compile_errors/stage1/obj/pointer_to_noreturn.zig similarity index 100% rename from test/compile_errors/stage1/obj/pointer_to_noreturn.zig rename to test/cases/compile_errors/stage1/obj/pointer_to_noreturn.zig diff --git a/test/compile_errors/stage1/obj/popCount-non-integer.zig b/test/cases/compile_errors/stage1/obj/popCount-non-integer.zig similarity index 100% rename from test/compile_errors/stage1/obj/popCount-non-integer.zig rename to test/cases/compile_errors/stage1/obj/popCount-non-integer.zig diff --git a/test/compile_errors/stage1/obj/prevent_bad_implicit_casting_of_anyframe_types.zig b/test/cases/compile_errors/stage1/obj/prevent_bad_implicit_casting_of_anyframe_types.zig similarity index 100% rename from test/compile_errors/stage1/obj/prevent_bad_implicit_casting_of_anyframe_types.zig rename to test/cases/compile_errors/stage1/obj/prevent_bad_implicit_casting_of_anyframe_types.zig diff --git a/test/compile_errors/stage1/obj/primitives_take_precedence_over_declarations.zig b/test/cases/compile_errors/stage1/obj/primitives_take_precedence_over_declarations.zig similarity index 100% rename from test/compile_errors/stage1/obj/primitives_take_precedence_over_declarations.zig rename to test/cases/compile_errors/stage1/obj/primitives_take_precedence_over_declarations.zig diff --git a/test/compile_errors/stage1/obj/ptrCast_a_0_bit_type_to_a_non-_0_bit_type.zig b/test/cases/compile_errors/stage1/obj/ptrCast_a_0_bit_type_to_a_non-_0_bit_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/ptrCast_a_0_bit_type_to_a_non-_0_bit_type.zig rename to test/cases/compile_errors/stage1/obj/ptrCast_a_0_bit_type_to_a_non-_0_bit_type.zig diff --git a/test/compile_errors/stage1/obj/ptrCast_discards_const_qualifier.zig b/test/cases/compile_errors/stage1/obj/ptrCast_discards_const_qualifier.zig similarity index 100% rename from test/compile_errors/stage1/obj/ptrCast_discards_const_qualifier.zig rename to test/cases/compile_errors/stage1/obj/ptrCast_discards_const_qualifier.zig diff --git a/test/compile_errors/stage1/obj/ptrToInt_0_to_non_optional_pointer.zig b/test/cases/compile_errors/stage1/obj/ptrToInt_0_to_non_optional_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/ptrToInt_0_to_non_optional_pointer.zig rename to test/cases/compile_errors/stage1/obj/ptrToInt_0_to_non_optional_pointer.zig diff --git a/test/compile_errors/stage1/obj/ptrToInt_on_void.zig b/test/cases/compile_errors/stage1/obj/ptrToInt_on_void.zig similarity index 100% rename from test/compile_errors/stage1/obj/ptrToInt_on_void.zig rename to test/cases/compile_errors/stage1/obj/ptrToInt_on_void.zig diff --git a/test/compile_errors/stage1/obj/ptrcast_to_non-pointer.zig b/test/cases/compile_errors/stage1/obj/ptrcast_to_non-pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/ptrcast_to_non-pointer.zig rename to test/cases/compile_errors/stage1/obj/ptrcast_to_non-pointer.zig diff --git a/test/compile_errors/stage1/obj/range_operator_in_switch_used_on_error_set.zig b/test/cases/compile_errors/stage1/obj/range_operator_in_switch_used_on_error_set.zig similarity index 100% rename from test/compile_errors/stage1/obj/range_operator_in_switch_used_on_error_set.zig rename to test/cases/compile_errors/stage1/obj/range_operator_in_switch_used_on_error_set.zig diff --git a/test/compile_errors/stage1/obj/reading_past_end_of_pointer_casted_array.zig b/test/cases/compile_errors/stage1/obj/reading_past_end_of_pointer_casted_array.zig similarity index 100% rename from test/compile_errors/stage1/obj/reading_past_end_of_pointer_casted_array.zig rename to test/cases/compile_errors/stage1/obj/reading_past_end_of_pointer_casted_array.zig diff --git a/test/compile_errors/stage1/obj/recursive_inferred_error_set.zig b/test/cases/compile_errors/stage1/obj/recursive_inferred_error_set.zig similarity index 100% rename from test/compile_errors/stage1/obj/recursive_inferred_error_set.zig rename to test/cases/compile_errors/stage1/obj/recursive_inferred_error_set.zig diff --git a/test/compile_errors/stage1/obj/redefinition_of_enums.zig b/test/cases/compile_errors/stage1/obj/redefinition_of_enums.zig similarity index 100% rename from test/compile_errors/stage1/obj/redefinition_of_enums.zig rename to test/cases/compile_errors/stage1/obj/redefinition_of_enums.zig diff --git a/test/compile_errors/stage1/obj/redefinition_of_global_variables.zig b/test/cases/compile_errors/stage1/obj/redefinition_of_global_variables.zig similarity index 100% rename from test/compile_errors/stage1/obj/redefinition_of_global_variables.zig rename to test/cases/compile_errors/stage1/obj/redefinition_of_global_variables.zig diff --git a/test/compile_errors/stage1/obj/redefinition_of_struct.zig b/test/cases/compile_errors/stage1/obj/redefinition_of_struct.zig similarity index 100% rename from test/compile_errors/stage1/obj/redefinition_of_struct.zig rename to test/cases/compile_errors/stage1/obj/redefinition_of_struct.zig diff --git a/test/compile_errors/stage1/obj/refer_to_the_type_of_a_generic_function.zig b/test/cases/compile_errors/stage1/obj/refer_to_the_type_of_a_generic_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/refer_to_the_type_of_a_generic_function.zig rename to test/cases/compile_errors/stage1/obj/refer_to_the_type_of_a_generic_function.zig diff --git a/test/compile_errors/stage1/obj/referring_to_a_struct_that_is_invalid.zig b/test/cases/compile_errors/stage1/obj/referring_to_a_struct_that_is_invalid.zig similarity index 100% rename from test/compile_errors/stage1/obj/referring_to_a_struct_that_is_invalid.zig rename to test/cases/compile_errors/stage1/obj/referring_to_a_struct_that_is_invalid.zig diff --git a/test/compile_errors/stage1/obj/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig b/test/cases/compile_errors/stage1/obj/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig similarity index 100% rename from test/compile_errors/stage1/obj/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig rename to test/cases/compile_errors/stage1/obj/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig diff --git a/test/compile_errors/stage1/obj/reify_type.Fn_with_is_generic_true.zig b/test/cases/compile_errors/stage1/obj/reify_type.Fn_with_is_generic_true.zig similarity index 100% rename from test/compile_errors/stage1/obj/reify_type.Fn_with_is_generic_true.zig rename to test/cases/compile_errors/stage1/obj/reify_type.Fn_with_is_generic_true.zig diff --git a/test/compile_errors/stage1/obj/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig b/test/cases/compile_errors/stage1/obj/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig similarity index 100% rename from test/compile_errors/stage1/obj/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig rename to test/cases/compile_errors/stage1/obj/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig diff --git a/test/compile_errors/stage1/obj/reify_type.Fn_with_return_type_null.zig b/test/cases/compile_errors/stage1/obj/reify_type.Fn_with_return_type_null.zig similarity index 100% rename from test/compile_errors/stage1/obj/reify_type.Fn_with_return_type_null.zig rename to test/cases/compile_errors/stage1/obj/reify_type.Fn_with_return_type_null.zig diff --git a/test/compile_errors/stage1/obj/reify_type.Pointer_with_invalid_address_space.zig b/test/cases/compile_errors/stage1/obj/reify_type.Pointer_with_invalid_address_space.zig similarity index 100% rename from test/compile_errors/stage1/obj/reify_type.Pointer_with_invalid_address_space.zig rename to test/cases/compile_errors/stage1/obj/reify_type.Pointer_with_invalid_address_space.zig diff --git a/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig b/test/cases/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig rename to test/cases/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig diff --git a/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig b/test/cases/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig rename to test/cases/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig diff --git a/test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_zero_fields.zig b/test/cases/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_zero_fields.zig similarity index 100% rename from test/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_zero_fields.zig rename to test/cases/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_zero_fields.zig diff --git a/test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_enum_field.zig b/test/cases/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_enum_field.zig similarity index 100% rename from test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_enum_field.zig rename to test/cases/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_enum_field.zig diff --git a/test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_union_field.zig b/test/cases/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_union_field.zig similarity index 100% rename from test/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_union_field.zig rename to test/cases/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_union_field.zig diff --git a/test/compile_errors/stage1/obj/reify_type_for_union_with_opaque_field.zig b/test/cases/compile_errors/stage1/obj/reify_type_for_union_with_opaque_field.zig similarity index 100% rename from test/compile_errors/stage1/obj/reify_type_for_union_with_opaque_field.zig rename to test/cases/compile_errors/stage1/obj/reify_type_for_union_with_opaque_field.zig diff --git a/test/compile_errors/stage1/obj/reify_type_for_union_with_zero_fields.zig b/test/cases/compile_errors/stage1/obj/reify_type_for_union_with_zero_fields.zig similarity index 100% rename from test/compile_errors/stage1/obj/reify_type_for_union_with_zero_fields.zig rename to test/cases/compile_errors/stage1/obj/reify_type_for_union_with_zero_fields.zig diff --git a/test/compile_errors/stage1/obj/reify_type_union_payload_is_undefined.zig b/test/cases/compile_errors/stage1/obj/reify_type_union_payload_is_undefined.zig similarity index 100% rename from test/compile_errors/stage1/obj/reify_type_union_payload_is_undefined.zig rename to test/cases/compile_errors/stage1/obj/reify_type_union_payload_is_undefined.zig diff --git a/test/compile_errors/stage1/obj/reify_type_with_Type.Int.zig b/test/cases/compile_errors/stage1/obj/reify_type_with_Type.Int.zig similarity index 100% rename from test/compile_errors/stage1/obj/reify_type_with_Type.Int.zig rename to test/cases/compile_errors/stage1/obj/reify_type_with_Type.Int.zig diff --git a/test/compile_errors/stage1/obj/reify_type_with_non-constant_expression.zig b/test/cases/compile_errors/stage1/obj/reify_type_with_non-constant_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/reify_type_with_non-constant_expression.zig rename to test/cases/compile_errors/stage1/obj/reify_type_with_non-constant_expression.zig diff --git a/test/compile_errors/stage1/obj/reify_type_with_undefined.zig b/test/cases/compile_errors/stage1/obj/reify_type_with_undefined.zig similarity index 100% rename from test/compile_errors/stage1/obj/reify_type_with_undefined.zig rename to test/cases/compile_errors/stage1/obj/reify_type_with_undefined.zig diff --git a/test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr.zig b/test/cases/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr.zig similarity index 100% rename from test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr.zig rename to test/cases/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr.zig diff --git a/test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig b/test/cases/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig similarity index 100% rename from test/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig rename to test/cases/compile_errors/stage1/obj/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig diff --git a/test/compile_errors/stage1/obj/return_from_defer_expression.zig b/test/cases/compile_errors/stage1/obj/return_from_defer_expression.zig similarity index 100% rename from test/compile_errors/stage1/obj/return_from_defer_expression.zig rename to test/cases/compile_errors/stage1/obj/return_from_defer_expression.zig diff --git a/test/compile_errors/stage1/obj/returning_error_from_void_async_function.zig b/test/cases/compile_errors/stage1/obj/returning_error_from_void_async_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/returning_error_from_void_async_function.zig rename to test/cases/compile_errors/stage1/obj/returning_error_from_void_async_function.zig diff --git a/test/compile_errors/stage1/obj/runtime-known_async_function_called.zig b/test/cases/compile_errors/stage1/obj/runtime-known_async_function_called.zig similarity index 100% rename from test/compile_errors/stage1/obj/runtime-known_async_function_called.zig rename to test/cases/compile_errors/stage1/obj/runtime-known_async_function_called.zig diff --git a/test/compile_errors/stage1/obj/runtime-known_function_called_with_async_keyword.zig b/test/cases/compile_errors/stage1/obj/runtime-known_function_called_with_async_keyword.zig similarity index 100% rename from test/compile_errors/stage1/obj/runtime-known_function_called_with_async_keyword.zig rename to test/cases/compile_errors/stage1/obj/runtime-known_function_called_with_async_keyword.zig diff --git a/test/compile_errors/stage1/obj/runtime_assignment_to_comptime_struct_type.zig b/test/cases/compile_errors/stage1/obj/runtime_assignment_to_comptime_struct_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/runtime_assignment_to_comptime_struct_type.zig rename to test/cases/compile_errors/stage1/obj/runtime_assignment_to_comptime_struct_type.zig diff --git a/test/compile_errors/stage1/obj/runtime_assignment_to_comptime_union_type.zig b/test/cases/compile_errors/stage1/obj/runtime_assignment_to_comptime_union_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/runtime_assignment_to_comptime_union_type.zig rename to test/cases/compile_errors/stage1/obj/runtime_assignment_to_comptime_union_type.zig diff --git a/test/compile_errors/stage1/obj/runtime_cast_to_union_which_has_non-void_fields.zig b/test/cases/compile_errors/stage1/obj/runtime_cast_to_union_which_has_non-void_fields.zig similarity index 100% rename from test/compile_errors/stage1/obj/runtime_cast_to_union_which_has_non-void_fields.zig rename to test/cases/compile_errors/stage1/obj/runtime_cast_to_union_which_has_non-void_fields.zig diff --git a/test/compile_errors/stage1/obj/runtime_index_into_comptime_type_slice.zig b/test/cases/compile_errors/stage1/obj/runtime_index_into_comptime_type_slice.zig similarity index 100% rename from test/compile_errors/stage1/obj/runtime_index_into_comptime_type_slice.zig rename to test/cases/compile_errors/stage1/obj/runtime_index_into_comptime_type_slice.zig diff --git a/test/compile_errors/stage1/obj/saturating_arithmetic_does_not_allow_floats.zig b/test/cases/compile_errors/stage1/obj/saturating_arithmetic_does_not_allow_floats.zig similarity index 100% rename from test/compile_errors/stage1/obj/saturating_arithmetic_does_not_allow_floats.zig rename to test/cases/compile_errors/stage1/obj/saturating_arithmetic_does_not_allow_floats.zig diff --git a/test/compile_errors/stage1/obj/saturating_shl_assign_does_not_allow_negative_rhs_at_comptime.zig b/test/cases/compile_errors/stage1/obj/saturating_shl_assign_does_not_allow_negative_rhs_at_comptime.zig similarity index 100% rename from test/compile_errors/stage1/obj/saturating_shl_assign_does_not_allow_negative_rhs_at_comptime.zig rename to test/cases/compile_errors/stage1/obj/saturating_shl_assign_does_not_allow_negative_rhs_at_comptime.zig diff --git a/test/compile_errors/stage1/obj/saturating_shl_does_not_allow_negative_rhs_at_comptime.zig b/test/cases/compile_errors/stage1/obj/saturating_shl_does_not_allow_negative_rhs_at_comptime.zig similarity index 100% rename from test/compile_errors/stage1/obj/saturating_shl_does_not_allow_negative_rhs_at_comptime.zig rename to test/cases/compile_errors/stage1/obj/saturating_shl_does_not_allow_negative_rhs_at_comptime.zig diff --git a/test/compile_errors/stage1/obj/setAlignStack_in_inline_function.zig b/test/cases/compile_errors/stage1/obj/setAlignStack_in_inline_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/setAlignStack_in_inline_function.zig rename to test/cases/compile_errors/stage1/obj/setAlignStack_in_inline_function.zig diff --git a/test/compile_errors/stage1/obj/setAlignStack_in_naked_function.zig b/test/cases/compile_errors/stage1/obj/setAlignStack_in_naked_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/setAlignStack_in_naked_function.zig rename to test/cases/compile_errors/stage1/obj/setAlignStack_in_naked_function.zig diff --git a/test/compile_errors/stage1/obj/setAlignStack_outside_function.zig b/test/cases/compile_errors/stage1/obj/setAlignStack_outside_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/setAlignStack_outside_function.zig rename to test/cases/compile_errors/stage1/obj/setAlignStack_outside_function.zig diff --git a/test/compile_errors/stage1/obj/setAlignStack_set_twice.zig b/test/cases/compile_errors/stage1/obj/setAlignStack_set_twice.zig similarity index 100% rename from test/compile_errors/stage1/obj/setAlignStack_set_twice.zig rename to test/cases/compile_errors/stage1/obj/setAlignStack_set_twice.zig diff --git a/test/compile_errors/stage1/obj/setAlignStack_too_big.zig b/test/cases/compile_errors/stage1/obj/setAlignStack_too_big.zig similarity index 100% rename from test/compile_errors/stage1/obj/setAlignStack_too_big.zig rename to test/cases/compile_errors/stage1/obj/setAlignStack_too_big.zig diff --git a/test/compile_errors/stage1/obj/setFloatMode_twice_for_same_scope.zig b/test/cases/compile_errors/stage1/obj/setFloatMode_twice_for_same_scope.zig similarity index 100% rename from test/compile_errors/stage1/obj/setFloatMode_twice_for_same_scope.zig rename to test/cases/compile_errors/stage1/obj/setFloatMode_twice_for_same_scope.zig diff --git a/test/compile_errors/stage1/obj/setRuntimeSafety_twice_for_same_scope.zig b/test/cases/compile_errors/stage1/obj/setRuntimeSafety_twice_for_same_scope.zig similarity index 100% rename from test/compile_errors/stage1/obj/setRuntimeSafety_twice_for_same_scope.zig rename to test/cases/compile_errors/stage1/obj/setRuntimeSafety_twice_for_same_scope.zig diff --git a/test/compile_errors/stage1/obj/setting_a_section_on_a_local_variable.zig b/test/cases/compile_errors/stage1/obj/setting_a_section_on_a_local_variable.zig similarity index 100% rename from test/compile_errors/stage1/obj/setting_a_section_on_a_local_variable.zig rename to test/cases/compile_errors/stage1/obj/setting_a_section_on_a_local_variable.zig diff --git a/test/compile_errors/stage1/obj/shift_amount_has_to_be_an_integer_type.zig b/test/cases/compile_errors/stage1/obj/shift_amount_has_to_be_an_integer_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/shift_amount_has_to_be_an_integer_type.zig rename to test/cases/compile_errors/stage1/obj/shift_amount_has_to_be_an_integer_type.zig diff --git a/test/compile_errors/stage1/obj/shift_by_negative_comptime_integer.zig b/test/cases/compile_errors/stage1/obj/shift_by_negative_comptime_integer.zig similarity index 100% rename from test/compile_errors/stage1/obj/shift_by_negative_comptime_integer.zig rename to test/cases/compile_errors/stage1/obj/shift_by_negative_comptime_integer.zig diff --git a/test/compile_errors/stage1/obj/shift_left_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/shift_left_assign_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/shift_left_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/shift_left_assign_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/shift_left_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/shift_left_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/shift_left_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/shift_left_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/shift_right_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/shift_right_assign_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/shift_right_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/shift_right_assign_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/shift_right_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/shift_right_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/shift_right_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/shift_right_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/shifting_RHS_is_log2_of_LHS_int_bit_width.zig b/test/cases/compile_errors/stage1/obj/shifting_RHS_is_log2_of_LHS_int_bit_width.zig similarity index 100% rename from test/compile_errors/stage1/obj/shifting_RHS_is_log2_of_LHS_int_bit_width.zig rename to test/cases/compile_errors/stage1/obj/shifting_RHS_is_log2_of_LHS_int_bit_width.zig diff --git a/test/compile_errors/stage1/obj/shifting_without_int_type_or_comptime_known.zig b/test/cases/compile_errors/stage1/obj/shifting_without_int_type_or_comptime_known.zig similarity index 100% rename from test/compile_errors/stage1/obj/shifting_without_int_type_or_comptime_known.zig rename to test/cases/compile_errors/stage1/obj/shifting_without_int_type_or_comptime_known.zig diff --git a/test/compile_errors/stage1/obj/shlExact_shifts_out_1_bits.zig b/test/cases/compile_errors/stage1/obj/shlExact_shifts_out_1_bits.zig similarity index 100% rename from test/compile_errors/stage1/obj/shlExact_shifts_out_1_bits.zig rename to test/cases/compile_errors/stage1/obj/shlExact_shifts_out_1_bits.zig diff --git a/test/compile_errors/stage1/obj/shrExact_shifts_out_1_bits.zig b/test/cases/compile_errors/stage1/obj/shrExact_shifts_out_1_bits.zig similarity index 100% rename from test/compile_errors/stage1/obj/shrExact_shifts_out_1_bits.zig rename to test/cases/compile_errors/stage1/obj/shrExact_shifts_out_1_bits.zig diff --git a/test/compile_errors/stage1/obj/signed_integer_division.zig b/test/cases/compile_errors/stage1/obj/signed_integer_division.zig similarity index 100% rename from test/compile_errors/stage1/obj/signed_integer_division.zig rename to test/cases/compile_errors/stage1/obj/signed_integer_division.zig diff --git a/test/compile_errors/stage1/obj/signed_integer_remainder_division.zig b/test/cases/compile_errors/stage1/obj/signed_integer_remainder_division.zig similarity index 100% rename from test/compile_errors/stage1/obj/signed_integer_remainder_division.zig rename to test/cases/compile_errors/stage1/obj/signed_integer_remainder_division.zig diff --git a/test/compile_errors/stage1/obj/sizeOf_bad_type.zig b/test/cases/compile_errors/stage1/obj/sizeOf_bad_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/sizeOf_bad_type.zig rename to test/cases/compile_errors/stage1/obj/sizeOf_bad_type.zig diff --git a/test/compile_errors/stage1/obj/slice_cannot_have_its_bytes_reinterpreted.zig b/test/cases/compile_errors/stage1/obj/slice_cannot_have_its_bytes_reinterpreted.zig similarity index 100% rename from test/compile_errors/stage1/obj/slice_cannot_have_its_bytes_reinterpreted.zig rename to test/cases/compile_errors/stage1/obj/slice_cannot_have_its_bytes_reinterpreted.zig diff --git a/test/compile_errors/stage1/obj/slice_passed_as_array_init_type.zig b/test/cases/compile_errors/stage1/obj/slice_passed_as_array_init_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/slice_passed_as_array_init_type.zig rename to test/cases/compile_errors/stage1/obj/slice_passed_as_array_init_type.zig diff --git a/test/compile_errors/stage1/obj/slice_passed_as_array_init_type_with_elems.zig b/test/cases/compile_errors/stage1/obj/slice_passed_as_array_init_type_with_elems.zig similarity index 100% rename from test/compile_errors/stage1/obj/slice_passed_as_array_init_type_with_elems.zig rename to test/cases/compile_errors/stage1/obj/slice_passed_as_array_init_type_with_elems.zig diff --git a/test/compile_errors/stage1/obj/slice_sentinel_mismatch-1.zig b/test/cases/compile_errors/stage1/obj/slice_sentinel_mismatch-1.zig similarity index 100% rename from test/compile_errors/stage1/obj/slice_sentinel_mismatch-1.zig rename to test/cases/compile_errors/stage1/obj/slice_sentinel_mismatch-1.zig diff --git a/test/compile_errors/stage1/obj/slice_sentinel_mismatch-2.zig b/test/cases/compile_errors/stage1/obj/slice_sentinel_mismatch-2.zig similarity index 100% rename from test/compile_errors/stage1/obj/slice_sentinel_mismatch-2.zig rename to test/cases/compile_errors/stage1/obj/slice_sentinel_mismatch-2.zig diff --git a/test/compile_errors/stage1/obj/slicing_of_global_undefined_pointer.zig b/test/cases/compile_errors/stage1/obj/slicing_of_global_undefined_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/slicing_of_global_undefined_pointer.zig rename to test/cases/compile_errors/stage1/obj/slicing_of_global_undefined_pointer.zig diff --git a/test/compile_errors/stage1/obj/slicing_single-item_pointer.zig b/test/cases/compile_errors/stage1/obj/slicing_single-item_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/slicing_single-item_pointer.zig rename to test/cases/compile_errors/stage1/obj/slicing_single-item_pointer.zig diff --git a/test/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig b/test/cases/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig similarity index 100% rename from test/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig rename to test/cases/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig diff --git a/test/compile_errors/stage1/obj/specify_non-integer_enum_tag_type.zig b/test/cases/compile_errors/stage1/obj/specify_non-integer_enum_tag_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/specify_non-integer_enum_tag_type.zig rename to test/cases/compile_errors/stage1/obj/specify_non-integer_enum_tag_type.zig diff --git a/test/compile_errors/stage1/obj/src_outside_function.zig b/test/cases/compile_errors/stage1/obj/src_outside_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/src_outside_function.zig rename to test/cases/compile_errors/stage1/obj/src_outside_function.zig diff --git a/test/compile_errors/stage1/obj/std.fmt_error_for_unused_arguments.zig b/test/cases/compile_errors/stage1/obj/std.fmt_error_for_unused_arguments.zig similarity index 100% rename from test/compile_errors/stage1/obj/std.fmt_error_for_unused_arguments.zig rename to test/cases/compile_errors/stage1/obj/std.fmt_error_for_unused_arguments.zig diff --git a/test/compile_errors/stage1/obj/store_vector_pointer_with_unknown_runtime_index.zig b/test/cases/compile_errors/stage1/obj/store_vector_pointer_with_unknown_runtime_index.zig similarity index 100% rename from test/compile_errors/stage1/obj/store_vector_pointer_with_unknown_runtime_index.zig rename to test/cases/compile_errors/stage1/obj/store_vector_pointer_with_unknown_runtime_index.zig diff --git a/test/compile_errors/stage1/obj/storing_runtime_value_in_compile_time_variable_then_using_it.zig b/test/cases/compile_errors/stage1/obj/storing_runtime_value_in_compile_time_variable_then_using_it.zig similarity index 100% rename from test/compile_errors/stage1/obj/storing_runtime_value_in_compile_time_variable_then_using_it.zig rename to test/cases/compile_errors/stage1/obj/storing_runtime_value_in_compile_time_variable_then_using_it.zig diff --git a/test/compile_errors/stage1/obj/struct_depends_on_itself_via_optional_field.zig b/test/cases/compile_errors/stage1/obj/struct_depends_on_itself_via_optional_field.zig similarity index 100% rename from test/compile_errors/stage1/obj/struct_depends_on_itself_via_optional_field.zig rename to test/cases/compile_errors/stage1/obj/struct_depends_on_itself_via_optional_field.zig diff --git a/test/compile_errors/stage1/obj/struct_field_missing_type.zig b/test/cases/compile_errors/stage1/obj/struct_field_missing_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/struct_field_missing_type.zig rename to test/cases/compile_errors/stage1/obj/struct_field_missing_type.zig diff --git a/test/compile_errors/stage1/obj/struct_init_syntax_for_array.zig b/test/cases/compile_errors/stage1/obj/struct_init_syntax_for_array.zig similarity index 100% rename from test/compile_errors/stage1/obj/struct_init_syntax_for_array.zig rename to test/cases/compile_errors/stage1/obj/struct_init_syntax_for_array.zig diff --git a/test/compile_errors/stage1/obj/struct_with_declarations_unavailable_for_reify_type.zig b/test/cases/compile_errors/stage1/obj/struct_with_declarations_unavailable_for_reify_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/struct_with_declarations_unavailable_for_reify_type.zig rename to test/cases/compile_errors/stage1/obj/struct_with_declarations_unavailable_for_reify_type.zig diff --git a/test/compile_errors/stage1/obj/struct_with_invalid_field.zig b/test/cases/compile_errors/stage1/obj/struct_with_invalid_field.zig similarity index 100% rename from test/compile_errors/stage1/obj/struct_with_invalid_field.zig rename to test/cases/compile_errors/stage1/obj/struct_with_invalid_field.zig diff --git a/test/compile_errors/stage1/obj/sub_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/sub_assign_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/sub_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/sub_assign_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/sub_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/sub_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/sub_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/sub_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/sub_overflow_in_function_evaluation.zig b/test/cases/compile_errors/stage1/obj/sub_overflow_in_function_evaluation.zig similarity index 100% rename from test/compile_errors/stage1/obj/sub_overflow_in_function_evaluation.zig rename to test/cases/compile_errors/stage1/obj/sub_overflow_in_function_evaluation.zig diff --git a/test/compile_errors/stage1/obj/sub_wrap_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/sub_wrap_assign_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/sub_wrap_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/sub_wrap_assign_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/sub_wrap_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/sub_wrap_on_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/sub_wrap_on_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/sub_wrap_on_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/suspend_inside_suspend_block.zig b/test/cases/compile_errors/stage1/obj/suspend_inside_suspend_block.zig similarity index 100% rename from test/compile_errors/stage1/obj/suspend_inside_suspend_block.zig rename to test/cases/compile_errors/stage1/obj/suspend_inside_suspend_block.zig diff --git a/test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong.zig b/test/cases/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong.zig rename to test/cases/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong.zig diff --git a/test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong_when_else_present.zig b/test/cases/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong_when_else_present.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong_when_else_present.zig rename to test/cases/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong_when_else_present.zig diff --git a/test/compile_errors/stage1/obj/switch_expression-duplicate_or_overlapping_integer_value.zig b/test/cases/compile_errors/stage1/obj/switch_expression-duplicate_or_overlapping_integer_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_expression-duplicate_or_overlapping_integer_value.zig rename to test/cases/compile_errors/stage1/obj/switch_expression-duplicate_or_overlapping_integer_value.zig diff --git a/test/compile_errors/stage1/obj/switch_expression-duplicate_type.zig b/test/cases/compile_errors/stage1/obj/switch_expression-duplicate_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_expression-duplicate_type.zig rename to test/cases/compile_errors/stage1/obj/switch_expression-duplicate_type.zig diff --git a/test/compile_errors/stage1/obj/switch_expression-duplicate_type_struct_alias.zig b/test/cases/compile_errors/stage1/obj/switch_expression-duplicate_type_struct_alias.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_expression-duplicate_type_struct_alias.zig rename to test/cases/compile_errors/stage1/obj/switch_expression-duplicate_type_struct_alias.zig diff --git a/test/compile_errors/stage1/obj/switch_expression-missing_enumeration_prong.zig b/test/cases/compile_errors/stage1/obj/switch_expression-missing_enumeration_prong.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_expression-missing_enumeration_prong.zig rename to test/cases/compile_errors/stage1/obj/switch_expression-missing_enumeration_prong.zig diff --git a/test/compile_errors/stage1/obj/switch_expression-multiple_else_prongs.zig b/test/cases/compile_errors/stage1/obj/switch_expression-multiple_else_prongs.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_expression-multiple_else_prongs.zig rename to test/cases/compile_errors/stage1/obj/switch_expression-multiple_else_prongs.zig diff --git a/test/compile_errors/stage1/obj/switch_expression-non_exhaustive_integer_prongs.zig b/test/cases/compile_errors/stage1/obj/switch_expression-non_exhaustive_integer_prongs.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_expression-non_exhaustive_integer_prongs.zig rename to test/cases/compile_errors/stage1/obj/switch_expression-non_exhaustive_integer_prongs.zig diff --git a/test/compile_errors/stage1/obj/switch_expression-switch_on_pointer_type_with_no_else.zig b/test/cases/compile_errors/stage1/obj/switch_expression-switch_on_pointer_type_with_no_else.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_expression-switch_on_pointer_type_with_no_else.zig rename to test/cases/compile_errors/stage1/obj/switch_expression-switch_on_pointer_type_with_no_else.zig diff --git a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_bool.zig b/test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_bool.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_bool.zig rename to test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_bool.zig diff --git a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_enum.zig b/test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_enum.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_enum.zig rename to test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_enum.zig diff --git a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_i8.zig b/test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_i8.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_i8.zig rename to test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_i8.zig diff --git a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_u8.zig b/test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_u8.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_u8.zig rename to test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_u8.zig diff --git a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u1.zig b/test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u1.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u1.zig rename to test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u1.zig diff --git a/test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u2.zig b/test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u2.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u2.zig rename to test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u2.zig diff --git a/test/compile_errors/stage1/obj/switch_on_enum_with_1_field_with_no_prongs.zig b/test/cases/compile_errors/stage1/obj/switch_on_enum_with_1_field_with_no_prongs.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_on_enum_with_1_field_with_no_prongs.zig rename to test/cases/compile_errors/stage1/obj/switch_on_enum_with_1_field_with_no_prongs.zig diff --git a/test/compile_errors/stage1/obj/switch_on_union_with_no_attached_enum.zig b/test/cases/compile_errors/stage1/obj/switch_on_union_with_no_attached_enum.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_on_union_with_no_attached_enum.zig rename to test/cases/compile_errors/stage1/obj/switch_on_union_with_no_attached_enum.zig diff --git a/test/compile_errors/stage1/obj/switch_with_invalid_expression_parameter.zig b/test/cases/compile_errors/stage1/obj/switch_with_invalid_expression_parameter.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_with_invalid_expression_parameter.zig rename to test/cases/compile_errors/stage1/obj/switch_with_invalid_expression_parameter.zig diff --git a/test/compile_errors/stage1/obj/switch_with_overlapping_case_ranges.zig b/test/cases/compile_errors/stage1/obj/switch_with_overlapping_case_ranges.zig similarity index 100% rename from test/compile_errors/stage1/obj/switch_with_overlapping_case_ranges.zig rename to test/cases/compile_errors/stage1/obj/switch_with_overlapping_case_ranges.zig diff --git a/test/compile_errors/stage1/obj/tagName_used_on_union_with_no_associated_enum_tag.zig b/test/cases/compile_errors/stage1/obj/tagName_used_on_union_with_no_associated_enum_tag.zig similarity index 100% rename from test/compile_errors/stage1/obj/tagName_used_on_union_with_no_associated_enum_tag.zig rename to test/cases/compile_errors/stage1/obj/tagName_used_on_union_with_no_associated_enum_tag.zig diff --git a/test/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig b/test/cases/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig similarity index 100% rename from test/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig rename to test/cases/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig diff --git a/test/compile_errors/stage1/obj/taking_bit_offset_of_void_field_in_struct.zig b/test/cases/compile_errors/stage1/obj/taking_bit_offset_of_void_field_in_struct.zig similarity index 100% rename from test/compile_errors/stage1/obj/taking_bit_offset_of_void_field_in_struct.zig rename to test/cases/compile_errors/stage1/obj/taking_bit_offset_of_void_field_in_struct.zig diff --git a/test/compile_errors/stage1/obj/taking_byte_offset_of_void_field_in_struct.zig b/test/cases/compile_errors/stage1/obj/taking_byte_offset_of_void_field_in_struct.zig similarity index 100% rename from test/compile_errors/stage1/obj/taking_byte_offset_of_void_field_in_struct.zig rename to test/cases/compile_errors/stage1/obj/taking_byte_offset_of_void_field_in_struct.zig diff --git a/test/compile_errors/stage1/obj/threadlocal_qualifier_on_const.zig b/test/cases/compile_errors/stage1/obj/threadlocal_qualifier_on_const.zig similarity index 100% rename from test/compile_errors/stage1/obj/threadlocal_qualifier_on_const.zig rename to test/cases/compile_errors/stage1/obj/threadlocal_qualifier_on_const.zig diff --git a/test/compile_errors/stage1/obj/top_level_decl_dependency_loop.zig b/test/cases/compile_errors/stage1/obj/top_level_decl_dependency_loop.zig similarity index 100% rename from test/compile_errors/stage1/obj/top_level_decl_dependency_loop.zig rename to test/cases/compile_errors/stage1/obj/top_level_decl_dependency_loop.zig diff --git a/test/compile_errors/stage1/obj/truncate_sign_mismatch.zig b/test/cases/compile_errors/stage1/obj/truncate_sign_mismatch.zig similarity index 100% rename from test/compile_errors/stage1/obj/truncate_sign_mismatch.zig rename to test/cases/compile_errors/stage1/obj/truncate_sign_mismatch.zig diff --git a/test/compile_errors/stage1/obj/truncate_undefined_value.zig b/test/cases/compile_errors/stage1/obj/truncate_undefined_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/truncate_undefined_value.zig rename to test/cases/compile_errors/stage1/obj/truncate_undefined_value.zig diff --git a/test/compile_errors/stage1/obj/try_in_function_with_non_error_return_type.zig b/test/cases/compile_errors/stage1/obj/try_in_function_with_non_error_return_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/try_in_function_with_non_error_return_type.zig rename to test/cases/compile_errors/stage1/obj/try_in_function_with_non_error_return_type.zig diff --git a/test/compile_errors/stage1/obj/type_checking_function_pointers.zig b/test/cases/compile_errors/stage1/obj/type_checking_function_pointers.zig similarity index 100% rename from test/compile_errors/stage1/obj/type_checking_function_pointers.zig rename to test/cases/compile_errors/stage1/obj/type_checking_function_pointers.zig diff --git a/test/compile_errors/stage1/obj/type_variables_must_be_constant.zig b/test/cases/compile_errors/stage1/obj/type_variables_must_be_constant.zig similarity index 100% rename from test/compile_errors/stage1/obj/type_variables_must_be_constant.zig rename to test/cases/compile_errors/stage1/obj/type_variables_must_be_constant.zig diff --git a/test/compile_errors/stage1/obj/undeclared_identifier.zig b/test/cases/compile_errors/stage1/obj/undeclared_identifier.zig similarity index 100% rename from test/compile_errors/stage1/obj/undeclared_identifier.zig rename to test/cases/compile_errors/stage1/obj/undeclared_identifier.zig diff --git a/test/compile_errors/stage1/obj/undeclared_identifier_error_should_mark_fn_as_impure.zig b/test/cases/compile_errors/stage1/obj/undeclared_identifier_error_should_mark_fn_as_impure.zig similarity index 100% rename from test/compile_errors/stage1/obj/undeclared_identifier_error_should_mark_fn_as_impure.zig rename to test/cases/compile_errors/stage1/obj/undeclared_identifier_error_should_mark_fn_as_impure.zig diff --git a/test/compile_errors/stage1/obj/undeclared_identifier_in_unanalyzed_branch.zig b/test/cases/compile_errors/stage1/obj/undeclared_identifier_in_unanalyzed_branch.zig similarity index 100% rename from test/compile_errors/stage1/obj/undeclared_identifier_in_unanalyzed_branch.zig rename to test/cases/compile_errors/stage1/obj/undeclared_identifier_in_unanalyzed_branch.zig diff --git a/test/compile_errors/stage1/obj/undefined_as_field_type_is_rejected.zig b/test/cases/compile_errors/stage1/obj/undefined_as_field_type_is_rejected.zig similarity index 100% rename from test/compile_errors/stage1/obj/undefined_as_field_type_is_rejected.zig rename to test/cases/compile_errors/stage1/obj/undefined_as_field_type_is_rejected.zig diff --git a/test/compile_errors/stage1/obj/undefined_function_call.zig b/test/cases/compile_errors/stage1/obj/undefined_function_call.zig similarity index 100% rename from test/compile_errors/stage1/obj/undefined_function_call.zig rename to test/cases/compile_errors/stage1/obj/undefined_function_call.zig diff --git a/test/compile_errors/stage1/obj/underscore_is_not_a_declarable_symbol.zig b/test/cases/compile_errors/stage1/obj/underscore_is_not_a_declarable_symbol.zig similarity index 100% rename from test/compile_errors/stage1/obj/underscore_is_not_a_declarable_symbol.zig rename to test/cases/compile_errors/stage1/obj/underscore_is_not_a_declarable_symbol.zig diff --git a/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_for.zig b/test/cases/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_for.zig similarity index 100% rename from test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_for.zig rename to test/cases/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_for.zig diff --git a/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while.zig b/test/cases/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while.zig similarity index 100% rename from test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while.zig rename to test/cases/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while.zig diff --git a/test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while_else.zig b/test/cases/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while_else.zig similarity index 100% rename from test/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while_else.zig rename to test/cases/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while_else.zig diff --git a/test/compile_errors/stage1/obj/union_auto-enum_value_already_taken.zig b/test/cases/compile_errors/stage1/obj/union_auto-enum_value_already_taken.zig similarity index 100% rename from test/compile_errors/stage1/obj/union_auto-enum_value_already_taken.zig rename to test/cases/compile_errors/stage1/obj/union_auto-enum_value_already_taken.zig diff --git a/test/compile_errors/stage1/obj/union_enum_field_does_not_match_enum.zig b/test/cases/compile_errors/stage1/obj/union_enum_field_does_not_match_enum.zig similarity index 100% rename from test/compile_errors/stage1/obj/union_enum_field_does_not_match_enum.zig rename to test/cases/compile_errors/stage1/obj/union_enum_field_does_not_match_enum.zig diff --git a/test/compile_errors/stage1/obj/union_fields_with_value_assignments.zig b/test/cases/compile_errors/stage1/obj/union_fields_with_value_assignments.zig similarity index 100% rename from test/compile_errors/stage1/obj/union_fields_with_value_assignments.zig rename to test/cases/compile_errors/stage1/obj/union_fields_with_value_assignments.zig diff --git a/test/compile_errors/stage1/obj/union_with_0_fields.zig b/test/cases/compile_errors/stage1/obj/union_with_0_fields.zig similarity index 100% rename from test/compile_errors/stage1/obj/union_with_0_fields.zig rename to test/cases/compile_errors/stage1/obj/union_with_0_fields.zig diff --git a/test/compile_errors/stage1/obj/union_with_specified_enum_omits_field.zig b/test/cases/compile_errors/stage1/obj/union_with_specified_enum_omits_field.zig similarity index 100% rename from test/compile_errors/stage1/obj/union_with_specified_enum_omits_field.zig rename to test/cases/compile_errors/stage1/obj/union_with_specified_enum_omits_field.zig diff --git a/test/compile_errors/stage1/obj/union_with_too_small_explicit_signed_tag_type.zig b/test/cases/compile_errors/stage1/obj/union_with_too_small_explicit_signed_tag_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/union_with_too_small_explicit_signed_tag_type.zig rename to test/cases/compile_errors/stage1/obj/union_with_too_small_explicit_signed_tag_type.zig diff --git a/test/compile_errors/stage1/obj/union_with_too_small_explicit_unsigned_tag_type.zig b/test/cases/compile_errors/stage1/obj/union_with_too_small_explicit_unsigned_tag_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/union_with_too_small_explicit_unsigned_tag_type.zig rename to test/cases/compile_errors/stage1/obj/union_with_too_small_explicit_unsigned_tag_type.zig diff --git a/test/compile_errors/stage1/obj/unknown_length_pointer_to_opaque.zig b/test/cases/compile_errors/stage1/obj/unknown_length_pointer_to_opaque.zig similarity index 100% rename from test/compile_errors/stage1/obj/unknown_length_pointer_to_opaque.zig rename to test/cases/compile_errors/stage1/obj/unknown_length_pointer_to_opaque.zig diff --git a/test/compile_errors/stage1/obj/unreachable_code-double_break.zig b/test/cases/compile_errors/stage1/obj/unreachable_code-double_break.zig similarity index 100% rename from test/compile_errors/stage1/obj/unreachable_code-double_break.zig rename to test/cases/compile_errors/stage1/obj/unreachable_code-double_break.zig diff --git a/test/compile_errors/stage1/obj/unreachable_code-nested_returns.zig b/test/cases/compile_errors/stage1/obj/unreachable_code-nested_returns.zig similarity index 100% rename from test/compile_errors/stage1/obj/unreachable_code-nested_returns.zig rename to test/cases/compile_errors/stage1/obj/unreachable_code-nested_returns.zig diff --git a/test/compile_errors/stage1/obj/unreachable_code.zig b/test/cases/compile_errors/stage1/obj/unreachable_code.zig similarity index 100% rename from test/compile_errors/stage1/obj/unreachable_code.zig rename to test/cases/compile_errors/stage1/obj/unreachable_code.zig diff --git a/test/compile_errors/stage1/obj/unreachable_executed_at_comptime.zig b/test/cases/compile_errors/stage1/obj/unreachable_executed_at_comptime.zig similarity index 100% rename from test/compile_errors/stage1/obj/unreachable_executed_at_comptime.zig rename to test/cases/compile_errors/stage1/obj/unreachable_executed_at_comptime.zig diff --git a/test/compile_errors/stage1/obj/unreachable_parameter.zig b/test/cases/compile_errors/stage1/obj/unreachable_parameter.zig similarity index 100% rename from test/compile_errors/stage1/obj/unreachable_parameter.zig rename to test/cases/compile_errors/stage1/obj/unreachable_parameter.zig diff --git a/test/compile_errors/stage1/obj/unreachable_variable.zig b/test/cases/compile_errors/stage1/obj/unreachable_variable.zig similarity index 100% rename from test/compile_errors/stage1/obj/unreachable_variable.zig rename to test/cases/compile_errors/stage1/obj/unreachable_variable.zig diff --git a/test/compile_errors/stage1/obj/unreachable_with_return.zig b/test/cases/compile_errors/stage1/obj/unreachable_with_return.zig similarity index 100% rename from test/compile_errors/stage1/obj/unreachable_with_return.zig rename to test/cases/compile_errors/stage1/obj/unreachable_with_return.zig diff --git a/test/compile_errors/stage1/obj/unsupported_modifier_at_start_of_asm_output_constraint.zig b/test/cases/compile_errors/stage1/obj/unsupported_modifier_at_start_of_asm_output_constraint.zig similarity index 100% rename from test/compile_errors/stage1/obj/unsupported_modifier_at_start_of_asm_output_constraint.zig rename to test/cases/compile_errors/stage1/obj/unsupported_modifier_at_start_of_asm_output_constraint.zig diff --git a/test/compile_errors/stage1/obj/unused_variable_error_on_errdefer.zig b/test/cases/compile_errors/stage1/obj/unused_variable_error_on_errdefer.zig similarity index 100% rename from test/compile_errors/stage1/obj/unused_variable_error_on_errdefer.zig rename to test/cases/compile_errors/stage1/obj/unused_variable_error_on_errdefer.zig diff --git a/test/compile_errors/stage1/obj/use_anyopaque_as_return_type_of_fn_ptr.zig b/test/cases/compile_errors/stage1/obj/use_anyopaque_as_return_type_of_fn_ptr.zig similarity index 100% rename from test/compile_errors/stage1/obj/use_anyopaque_as_return_type_of_fn_ptr.zig rename to test/cases/compile_errors/stage1/obj/use_anyopaque_as_return_type_of_fn_ptr.zig diff --git a/test/compile_errors/stage1/obj/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig b/test/cases/compile_errors/stage1/obj/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig similarity index 100% rename from test/compile_errors/stage1/obj/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig rename to test/cases/compile_errors/stage1/obj/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig diff --git a/test/compile_errors/stage1/obj/use_invalid_number_literal_as_array_index.zig b/test/cases/compile_errors/stage1/obj/use_invalid_number_literal_as_array_index.zig similarity index 100% rename from test/compile_errors/stage1/obj/use_invalid_number_literal_as_array_index.zig rename to test/cases/compile_errors/stage1/obj/use_invalid_number_literal_as_array_index.zig diff --git a/test/compile_errors/stage1/obj/use_of_comptime-known_undefined_function_value.zig b/test/cases/compile_errors/stage1/obj/use_of_comptime-known_undefined_function_value.zig similarity index 100% rename from test/compile_errors/stage1/obj/use_of_comptime-known_undefined_function_value.zig rename to test/cases/compile_errors/stage1/obj/use_of_comptime-known_undefined_function_value.zig diff --git a/test/compile_errors/stage1/obj/use_of_undeclared_identifier.zig b/test/cases/compile_errors/stage1/obj/use_of_undeclared_identifier.zig similarity index 100% rename from test/compile_errors/stage1/obj/use_of_undeclared_identifier.zig rename to test/cases/compile_errors/stage1/obj/use_of_undeclared_identifier.zig diff --git a/test/compile_errors/stage1/obj/using_an_unknown_len_ptr_type_instead_of_array.zig b/test/cases/compile_errors/stage1/obj/using_an_unknown_len_ptr_type_instead_of_array.zig similarity index 100% rename from test/compile_errors/stage1/obj/using_an_unknown_len_ptr_type_instead_of_array.zig rename to test/cases/compile_errors/stage1/obj/using_an_unknown_len_ptr_type_instead_of_array.zig diff --git a/test/compile_errors/stage1/obj/using_invalid_types_in_function_call_raises_an_error.zig b/test/cases/compile_errors/stage1/obj/using_invalid_types_in_function_call_raises_an_error.zig similarity index 100% rename from test/compile_errors/stage1/obj/using_invalid_types_in_function_call_raises_an_error.zig rename to test/cases/compile_errors/stage1/obj/using_invalid_types_in_function_call_raises_an_error.zig diff --git a/test/compile_errors/stage1/obj/usingnamespace_with_wrong_type.zig b/test/cases/compile_errors/stage1/obj/usingnamespace_with_wrong_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/usingnamespace_with_wrong_type.zig rename to test/cases/compile_errors/stage1/obj/usingnamespace_with_wrong_type.zig diff --git a/test/compile_errors/stage1/obj/variable_has_wrong_type.zig b/test/cases/compile_errors/stage1/obj/variable_has_wrong_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/variable_has_wrong_type.zig rename to test/cases/compile_errors/stage1/obj/variable_has_wrong_type.zig diff --git a/test/compile_errors/stage1/obj/variable_in_inline_assembly_template_cannot_be_found.zig b/test/cases/compile_errors/stage1/obj/variable_in_inline_assembly_template_cannot_be_found.zig similarity index 100% rename from test/compile_errors/stage1/obj/variable_in_inline_assembly_template_cannot_be_found.zig rename to test/cases/compile_errors/stage1/obj/variable_in_inline_assembly_template_cannot_be_found.zig diff --git a/test/compile_errors/stage1/obj/variable_initialization_compile_error_then_referenced.zig b/test/cases/compile_errors/stage1/obj/variable_initialization_compile_error_then_referenced.zig similarity index 100% rename from test/compile_errors/stage1/obj/variable_initialization_compile_error_then_referenced.zig rename to test/cases/compile_errors/stage1/obj/variable_initialization_compile_error_then_referenced.zig diff --git a/test/compile_errors/stage1/obj/variable_with_type_noreturn.zig b/test/cases/compile_errors/stage1/obj/variable_with_type_noreturn.zig similarity index 100% rename from test/compile_errors/stage1/obj/variable_with_type_noreturn.zig rename to test/cases/compile_errors/stage1/obj/variable_with_type_noreturn.zig diff --git a/test/compile_errors/stage1/obj/vector_index_out_of_bounds.zig b/test/cases/compile_errors/stage1/obj/vector_index_out_of_bounds.zig similarity index 100% rename from test/compile_errors/stage1/obj/vector_index_out_of_bounds.zig rename to test/cases/compile_errors/stage1/obj/vector_index_out_of_bounds.zig diff --git a/test/compile_errors/stage1/obj/volatile_on_global_assembly.zig b/test/cases/compile_errors/stage1/obj/volatile_on_global_assembly.zig similarity index 100% rename from test/compile_errors/stage1/obj/volatile_on_global_assembly.zig rename to test/cases/compile_errors/stage1/obj/volatile_on_global_assembly.zig diff --git a/test/compile_errors/stage1/obj/wasmMemoryGrow_is_a_compile_error_in_non-Wasm_targets.zig b/test/cases/compile_errors/stage1/obj/wasmMemoryGrow_is_a_compile_error_in_non-Wasm_targets.zig similarity index 100% rename from test/compile_errors/stage1/obj/wasmMemoryGrow_is_a_compile_error_in_non-Wasm_targets.zig rename to test/cases/compile_errors/stage1/obj/wasmMemoryGrow_is_a_compile_error_in_non-Wasm_targets.zig diff --git a/test/compile_errors/stage1/obj/wasmMemorySize_is_a_compile_error_in_non-Wasm_targets.zig b/test/cases/compile_errors/stage1/obj/wasmMemorySize_is_a_compile_error_in_non-Wasm_targets.zig similarity index 100% rename from test/compile_errors/stage1/obj/wasmMemorySize_is_a_compile_error_in_non-Wasm_targets.zig rename to test/cases/compile_errors/stage1/obj/wasmMemorySize_is_a_compile_error_in_non-Wasm_targets.zig diff --git a/test/compile_errors/stage1/obj/while_expected_bool_got_error_union.zig b/test/cases/compile_errors/stage1/obj/while_expected_bool_got_error_union.zig similarity index 100% rename from test/compile_errors/stage1/obj/while_expected_bool_got_error_union.zig rename to test/cases/compile_errors/stage1/obj/while_expected_bool_got_error_union.zig diff --git a/test/compile_errors/stage1/obj/while_expected_bool_got_optional.zig b/test/cases/compile_errors/stage1/obj/while_expected_bool_got_optional.zig similarity index 100% rename from test/compile_errors/stage1/obj/while_expected_bool_got_optional.zig rename to test/cases/compile_errors/stage1/obj/while_expected_bool_got_optional.zig diff --git a/test/compile_errors/stage1/obj/while_expected_error_union_got_bool.zig b/test/cases/compile_errors/stage1/obj/while_expected_error_union_got_bool.zig similarity index 100% rename from test/compile_errors/stage1/obj/while_expected_error_union_got_bool.zig rename to test/cases/compile_errors/stage1/obj/while_expected_error_union_got_bool.zig diff --git a/test/compile_errors/stage1/obj/while_expected_error_union_got_optional.zig b/test/cases/compile_errors/stage1/obj/while_expected_error_union_got_optional.zig similarity index 100% rename from test/compile_errors/stage1/obj/while_expected_error_union_got_optional.zig rename to test/cases/compile_errors/stage1/obj/while_expected_error_union_got_optional.zig diff --git a/test/compile_errors/stage1/obj/while_expected_optional_got_bool.zig b/test/cases/compile_errors/stage1/obj/while_expected_optional_got_bool.zig similarity index 100% rename from test/compile_errors/stage1/obj/while_expected_optional_got_bool.zig rename to test/cases/compile_errors/stage1/obj/while_expected_optional_got_bool.zig diff --git a/test/compile_errors/stage1/obj/while_expected_optional_got_error_union.zig b/test/cases/compile_errors/stage1/obj/while_expected_optional_got_error_union.zig similarity index 100% rename from test/compile_errors/stage1/obj/while_expected_optional_got_error_union.zig rename to test/cases/compile_errors/stage1/obj/while_expected_optional_got_error_union.zig diff --git a/test/compile_errors/stage1/obj/while_loop_body_expression_ignored.zig b/test/cases/compile_errors/stage1/obj/while_loop_body_expression_ignored.zig similarity index 100% rename from test/compile_errors/stage1/obj/while_loop_body_expression_ignored.zig rename to test/cases/compile_errors/stage1/obj/while_loop_body_expression_ignored.zig diff --git a/test/compile_errors/stage1/obj/write_to_const_global_variable.zig b/test/cases/compile_errors/stage1/obj/write_to_const_global_variable.zig similarity index 100% rename from test/compile_errors/stage1/obj/write_to_const_global_variable.zig rename to test/cases/compile_errors/stage1/obj/write_to_const_global_variable.zig diff --git a/test/compile_errors/stage1/obj/wrong_frame_type_used_for_async_call.zig b/test/cases/compile_errors/stage1/obj/wrong_frame_type_used_for_async_call.zig similarity index 100% rename from test/compile_errors/stage1/obj/wrong_frame_type_used_for_async_call.zig rename to test/cases/compile_errors/stage1/obj/wrong_frame_type_used_for_async_call.zig diff --git a/test/compile_errors/stage1/obj/wrong_function_type.zig b/test/cases/compile_errors/stage1/obj/wrong_function_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/wrong_function_type.zig rename to test/cases/compile_errors/stage1/obj/wrong_function_type.zig diff --git a/test/compile_errors/stage1/obj/wrong_initializer_for_union_payload_of_type_type.zig b/test/cases/compile_errors/stage1/obj/wrong_initializer_for_union_payload_of_type_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/wrong_initializer_for_union_payload_of_type_type.zig rename to test/cases/compile_errors/stage1/obj/wrong_initializer_for_union_payload_of_type_type.zig diff --git a/test/compile_errors/stage1/obj/wrong_number_of_arguments.zig b/test/cases/compile_errors/stage1/obj/wrong_number_of_arguments.zig similarity index 100% rename from test/compile_errors/stage1/obj/wrong_number_of_arguments.zig rename to test/cases/compile_errors/stage1/obj/wrong_number_of_arguments.zig diff --git a/test/compile_errors/stage1/obj/wrong_number_of_arguments_for_method_fn_call.zig b/test/cases/compile_errors/stage1/obj/wrong_number_of_arguments_for_method_fn_call.zig similarity index 100% rename from test/compile_errors/stage1/obj/wrong_number_of_arguments_for_method_fn_call.zig rename to test/cases/compile_errors/stage1/obj/wrong_number_of_arguments_for_method_fn_call.zig diff --git a/test/compile_errors/stage1/obj/wrong_panic_signature_generic_function.zig b/test/cases/compile_errors/stage1/obj/wrong_panic_signature_generic_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/wrong_panic_signature_generic_function.zig rename to test/cases/compile_errors/stage1/obj/wrong_panic_signature_generic_function.zig diff --git a/test/compile_errors/stage1/obj/wrong_panic_signature_runtime_function.zig b/test/cases/compile_errors/stage1/obj/wrong_panic_signature_runtime_function.zig similarity index 100% rename from test/compile_errors/stage1/obj/wrong_panic_signature_runtime_function.zig rename to test/cases/compile_errors/stage1/obj/wrong_panic_signature_runtime_function.zig diff --git a/test/compile_errors/stage1/obj/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig b/test/cases/compile_errors/stage1/obj/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig similarity index 100% rename from test/compile_errors/stage1/obj/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig rename to test/cases/compile_errors/stage1/obj/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig diff --git a/test/compile_errors/stage1/obj/wrong_return_type_for_main.zig b/test/cases/compile_errors/stage1/obj/wrong_return_type_for_main.zig similarity index 100% rename from test/compile_errors/stage1/obj/wrong_return_type_for_main.zig rename to test/cases/compile_errors/stage1/obj/wrong_return_type_for_main.zig diff --git a/test/compile_errors/stage1/obj/wrong_size_to_an_array_literal.zig b/test/cases/compile_errors/stage1/obj/wrong_size_to_an_array_literal.zig similarity index 100% rename from test/compile_errors/stage1/obj/wrong_size_to_an_array_literal.zig rename to test/cases/compile_errors/stage1/obj/wrong_size_to_an_array_literal.zig diff --git a/test/compile_errors/stage1/obj/wrong_type_for_argument_tuple_to_asyncCall.zig b/test/cases/compile_errors/stage1/obj/wrong_type_for_argument_tuple_to_asyncCall.zig similarity index 100% rename from test/compile_errors/stage1/obj/wrong_type_for_argument_tuple_to_asyncCall.zig rename to test/cases/compile_errors/stage1/obj/wrong_type_for_argument_tuple_to_asyncCall.zig diff --git a/test/compile_errors/stage1/obj/wrong_type_for_reify_type.zig b/test/cases/compile_errors/stage1/obj/wrong_type_for_reify_type.zig similarity index 100% rename from test/compile_errors/stage1/obj/wrong_type_for_reify_type.zig rename to test/cases/compile_errors/stage1/obj/wrong_type_for_reify_type.zig diff --git a/test/compile_errors/stage1/obj/wrong_type_for_result_ptr_to_asyncCall.zig b/test/cases/compile_errors/stage1/obj/wrong_type_for_result_ptr_to_asyncCall.zig similarity index 100% rename from test/compile_errors/stage1/obj/wrong_type_for_result_ptr_to_asyncCall.zig rename to test/cases/compile_errors/stage1/obj/wrong_type_for_result_ptr_to_asyncCall.zig diff --git a/test/compile_errors/stage1/obj/wrong_type_passed_to_panic.zig b/test/cases/compile_errors/stage1/obj/wrong_type_passed_to_panic.zig similarity index 100% rename from test/compile_errors/stage1/obj/wrong_type_passed_to_panic.zig rename to test/cases/compile_errors/stage1/obj/wrong_type_passed_to_panic.zig diff --git a/test/compile_errors/stage1/obj/wrong_type_to_hasField.zig b/test/cases/compile_errors/stage1/obj/wrong_type_to_hasField.zig similarity index 100% rename from test/compile_errors/stage1/obj/wrong_type_to_hasField.zig rename to test/cases/compile_errors/stage1/obj/wrong_type_to_hasField.zig diff --git a/test/compile_errors/stage1/obj/wrong_types_given_to_atomic_order_args_in_cmpxchg.zig b/test/cases/compile_errors/stage1/obj/wrong_types_given_to_atomic_order_args_in_cmpxchg.zig similarity index 100% rename from test/compile_errors/stage1/obj/wrong_types_given_to_atomic_order_args_in_cmpxchg.zig rename to test/cases/compile_errors/stage1/obj/wrong_types_given_to_atomic_order_args_in_cmpxchg.zig diff --git a/test/compile_errors/stage1/obj/wrong_types_given_to_export.zig b/test/cases/compile_errors/stage1/obj/wrong_types_given_to_export.zig similarity index 100% rename from test/compile_errors/stage1/obj/wrong_types_given_to_export.zig rename to test/cases/compile_errors/stage1/obj/wrong_types_given_to_export.zig diff --git a/test/compile_errors/stage1/test/access_invalid_typeInfo_decl.zig b/test/cases/compile_errors/stage1/test/access_invalid_typeInfo_decl.zig similarity index 100% rename from test/compile_errors/stage1/test/access_invalid_typeInfo_decl.zig rename to test/cases/compile_errors/stage1/test/access_invalid_typeInfo_decl.zig diff --git a/test/compile_errors/stage1/test/alignCast_of_zero_sized_types.zig b/test/cases/compile_errors/stage1/test/alignCast_of_zero_sized_types.zig similarity index 100% rename from test/compile_errors/stage1/test/alignCast_of_zero_sized_types.zig rename to test/cases/compile_errors/stage1/test/alignCast_of_zero_sized_types.zig diff --git a/test/compile_errors/stage1/test/bad_splat_type.zig b/test/cases/compile_errors/stage1/test/bad_splat_type.zig similarity index 100% rename from test/compile_errors/stage1/test/bad_splat_type.zig rename to test/cases/compile_errors/stage1/test/bad_splat_type.zig diff --git a/test/compile_errors/stage1/test/binary_OR_operator_on_error_sets.zig b/test/cases/compile_errors/stage1/test/binary_OR_operator_on_error_sets.zig similarity index 100% rename from test/compile_errors/stage1/test/binary_OR_operator_on_error_sets.zig rename to test/cases/compile_errors/stage1/test/binary_OR_operator_on_error_sets.zig diff --git a/test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-always_inline.zig b/test/cases/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-always_inline.zig similarity index 100% rename from test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-always_inline.zig rename to test/cases/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-always_inline.zig diff --git a/test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-compile_time.zig b/test/cases/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-compile_time.zig similarity index 100% rename from test/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-compile_time.zig rename to test/cases/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-compile_time.zig diff --git a/test/compile_errors/stage1/test/cast_between_optional_T_where_T_is_not_a_pointer.zig b/test/cases/compile_errors/stage1/test/cast_between_optional_T_where_T_is_not_a_pointer.zig similarity index 100% rename from test/compile_errors/stage1/test/cast_between_optional_T_where_T_is_not_a_pointer.zig rename to test/cases/compile_errors/stage1/test/cast_between_optional_T_where_T_is_not_a_pointer.zig diff --git a/test/compile_errors/stage1/test/combination_of_nosuspend_and_async.zig b/test/cases/compile_errors/stage1/test/combination_of_nosuspend_and_async.zig similarity index 100% rename from test/compile_errors/stage1/test/combination_of_nosuspend_and_async.zig rename to test/cases/compile_errors/stage1/test/combination_of_nosuspend_and_async.zig diff --git a/test/compile_errors/stage1/test/comparison_of_non-tagged_union_and_enum_literal.zig b/test/cases/compile_errors/stage1/test/comparison_of_non-tagged_union_and_enum_literal.zig similarity index 100% rename from test/compile_errors/stage1/test/comparison_of_non-tagged_union_and_enum_literal.zig rename to test/cases/compile_errors/stage1/test/comparison_of_non-tagged_union_and_enum_literal.zig diff --git a/test/compile_errors/stage1/test/comptime_vector_overflow_shows_the_index.zig b/test/cases/compile_errors/stage1/test/comptime_vector_overflow_shows_the_index.zig similarity index 100% rename from test/compile_errors/stage1/test/comptime_vector_overflow_shows_the_index.zig rename to test/cases/compile_errors/stage1/test/comptime_vector_overflow_shows_the_index.zig diff --git a/test/compile_errors/stage1/test/duplicate_field_in_anonymous_struct_literal.zig b/test/cases/compile_errors/stage1/test/duplicate_field_in_anonymous_struct_literal.zig similarity index 100% rename from test/compile_errors/stage1/test/duplicate_field_in_anonymous_struct_literal.zig rename to test/cases/compile_errors/stage1/test/duplicate_field_in_anonymous_struct_literal.zig diff --git a/test/compile_errors/stage1/test/error_in_struct_initializer_doesnt_crash_the_compiler.zig b/test/cases/compile_errors/stage1/test/error_in_struct_initializer_doesnt_crash_the_compiler.zig similarity index 100% rename from test/compile_errors/stage1/test/error_in_struct_initializer_doesnt_crash_the_compiler.zig rename to test/cases/compile_errors/stage1/test/error_in_struct_initializer_doesnt_crash_the_compiler.zig diff --git a/test/compile_errors/stage1/test/errors_in_for_loop_bodies_are_propagated.zig b/test/cases/compile_errors/stage1/test/errors_in_for_loop_bodies_are_propagated.zig similarity index 100% rename from test/compile_errors/stage1/test/errors_in_for_loop_bodies_are_propagated.zig rename to test/cases/compile_errors/stage1/test/errors_in_for_loop_bodies_are_propagated.zig diff --git a/test/compile_errors/stage1/test/export_with_empty_name_string.zig b/test/cases/compile_errors/stage1/test/export_with_empty_name_string.zig similarity index 100% rename from test/compile_errors/stage1/test/export_with_empty_name_string.zig rename to test/cases/compile_errors/stage1/test/export_with_empty_name_string.zig diff --git a/test/compile_errors/stage1/test/helpful_return_type_error_message.zig b/test/cases/compile_errors/stage1/test/helpful_return_type_error_message.zig similarity index 100% rename from test/compile_errors/stage1/test/helpful_return_type_error_message.zig rename to test/cases/compile_errors/stage1/test/helpful_return_type_error_message.zig diff --git a/test/compile_errors/stage1/test/int-float_conversion_to_comptime_int-float.zig b/test/cases/compile_errors/stage1/test/int-float_conversion_to_comptime_int-float.zig similarity index 100% rename from test/compile_errors/stage1/test/int-float_conversion_to_comptime_int-float.zig rename to test/cases/compile_errors/stage1/test/int-float_conversion_to_comptime_int-float.zig diff --git a/test/compile_errors/stage1/test/invalid_assignments.zig b/test/cases/compile_errors/stage1/test/invalid_assignments.zig similarity index 100% rename from test/compile_errors/stage1/test/invalid_assignments.zig rename to test/cases/compile_errors/stage1/test/invalid_assignments.zig diff --git a/test/compile_errors/stage1/test/invalid_float_casts.zig b/test/cases/compile_errors/stage1/test/invalid_float_casts.zig similarity index 100% rename from test/compile_errors/stage1/test/invalid_float_casts.zig rename to test/cases/compile_errors/stage1/test/invalid_float_casts.zig diff --git a/test/compile_errors/stage1/test/invalid_int_casts.zig b/test/cases/compile_errors/stage1/test/invalid_int_casts.zig similarity index 100% rename from test/compile_errors/stage1/test/invalid_int_casts.zig rename to test/cases/compile_errors/stage1/test/invalid_int_casts.zig diff --git a/test/compile_errors/stage1/test/invalid_non-exhaustive_enum_to_union.zig b/test/cases/compile_errors/stage1/test/invalid_non-exhaustive_enum_to_union.zig similarity index 100% rename from test/compile_errors/stage1/test/invalid_non-exhaustive_enum_to_union.zig rename to test/cases/compile_errors/stage1/test/invalid_non-exhaustive_enum_to_union.zig diff --git a/test/compile_errors/stage1/test/invalid_pointer_with_reify_type.zig b/test/cases/compile_errors/stage1/test/invalid_pointer_with_reify_type.zig similarity index 100% rename from test/compile_errors/stage1/test/invalid_pointer_with_reify_type.zig rename to test/cases/compile_errors/stage1/test/invalid_pointer_with_reify_type.zig diff --git a/test/compile_errors/stage1/test/nested_vectors.zig b/test/cases/compile_errors/stage1/test/nested_vectors.zig similarity index 100% rename from test/compile_errors/stage1/test/nested_vectors.zig rename to test/cases/compile_errors/stage1/test/nested_vectors.zig diff --git a/test/compile_errors/stage1/test/non-exhaustive_enum_marker_assigned_a_value.zig b/test/cases/compile_errors/stage1/test/non-exhaustive_enum_marker_assigned_a_value.zig similarity index 100% rename from test/compile_errors/stage1/test/non-exhaustive_enum_marker_assigned_a_value.zig rename to test/cases/compile_errors/stage1/test/non-exhaustive_enum_marker_assigned_a_value.zig diff --git a/test/compile_errors/stage1/test/non-exhaustive_enums.zig b/test/cases/compile_errors/stage1/test/non-exhaustive_enums.zig similarity index 100% rename from test/compile_errors/stage1/test/non-exhaustive_enums.zig rename to test/cases/compile_errors/stage1/test/non-exhaustive_enums.zig diff --git a/test/compile_errors/stage1/test/not_an_enum_type.zig b/test/cases/compile_errors/stage1/test/not_an_enum_type.zig similarity index 100% rename from test/compile_errors/stage1/test/not_an_enum_type.zig rename to test/cases/compile_errors/stage1/test/not_an_enum_type.zig diff --git a/test/compile_errors/stage1/test/packed_struct_with_fields_of_not_allowed_types.zig b/test/cases/compile_errors/stage1/test/packed_struct_with_fields_of_not_allowed_types.zig similarity index 100% rename from test/compile_errors/stage1/test/packed_struct_with_fields_of_not_allowed_types.zig rename to test/cases/compile_errors/stage1/test/packed_struct_with_fields_of_not_allowed_types.zig diff --git a/test/compile_errors/stage1/test/ptrToInt_with_pointer_to_zero-sized_type.zig b/test/cases/compile_errors/stage1/test/ptrToInt_with_pointer_to_zero-sized_type.zig similarity index 100% rename from test/compile_errors/stage1/test/ptrToInt_with_pointer_to_zero-sized_type.zig rename to test/cases/compile_errors/stage1/test/ptrToInt_with_pointer_to_zero-sized_type.zig diff --git a/test/compile_errors/stage1/test/reassign_to_array_parameter.zig b/test/cases/compile_errors/stage1/test/reassign_to_array_parameter.zig similarity index 100% rename from test/compile_errors/stage1/test/reassign_to_array_parameter.zig rename to test/cases/compile_errors/stage1/test/reassign_to_array_parameter.zig diff --git a/test/compile_errors/stage1/test/reassign_to_slice_parameter.zig b/test/cases/compile_errors/stage1/test/reassign_to_slice_parameter.zig similarity index 100% rename from test/compile_errors/stage1/test/reassign_to_slice_parameter.zig rename to test/cases/compile_errors/stage1/test/reassign_to_slice_parameter.zig diff --git a/test/compile_errors/stage1/test/reassign_to_struct_parameter.zig b/test/cases/compile_errors/stage1/test/reassign_to_struct_parameter.zig similarity index 100% rename from test/compile_errors/stage1/test/reassign_to_struct_parameter.zig rename to test/cases/compile_errors/stage1/test/reassign_to_struct_parameter.zig diff --git a/test/compile_errors/stage1/test/reference_to_const_data.zig b/test/cases/compile_errors/stage1/test/reference_to_const_data.zig similarity index 100% rename from test/compile_errors/stage1/test/reference_to_const_data.zig rename to test/cases/compile_errors/stage1/test/reference_to_const_data.zig diff --git a/test/compile_errors/stage1/test/reify_typeOf_with_incompatible_arguments.zig b/test/cases/compile_errors/stage1/test/reify_typeOf_with_incompatible_arguments.zig similarity index 100% rename from test/compile_errors/stage1/test/reify_typeOf_with_incompatible_arguments.zig rename to test/cases/compile_errors/stage1/test/reify_typeOf_with_incompatible_arguments.zig diff --git a/test/compile_errors/stage1/test/reify_typeOf_with_no_arguments.zig b/test/cases/compile_errors/stage1/test/reify_typeOf_with_no_arguments.zig similarity index 100% rename from test/compile_errors/stage1/test/reify_typeOf_with_no_arguments.zig rename to test/cases/compile_errors/stage1/test/reify_typeOf_with_no_arguments.zig diff --git a/test/compile_errors/stage1/test/reject_extern_function_definitions_with_body.zig b/test/cases/compile_errors/stage1/test/reject_extern_function_definitions_with_body.zig similarity index 100% rename from test/compile_errors/stage1/test/reject_extern_function_definitions_with_body.zig rename to test/cases/compile_errors/stage1/test/reject_extern_function_definitions_with_body.zig diff --git a/test/compile_errors/stage1/test/reject_extern_variables_with_initializers.zig b/test/cases/compile_errors/stage1/test/reject_extern_variables_with_initializers.zig similarity index 100% rename from test/compile_errors/stage1/test/reject_extern_variables_with_initializers.zig rename to test/cases/compile_errors/stage1/test/reject_extern_variables_with_initializers.zig diff --git a/test/compile_errors/stage1/test/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig b/test/cases/compile_errors/stage1/test/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig similarity index 100% rename from test/compile_errors/stage1/test/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig rename to test/cases/compile_errors/stage1/test/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig diff --git a/test/compile_errors/stage1/test/return_invalid_type_from_test.zig b/test/cases/compile_errors/stage1/test/return_invalid_type_from_test.zig similarity index 100% rename from test/compile_errors/stage1/test/return_invalid_type_from_test.zig rename to test/cases/compile_errors/stage1/test/return_invalid_type_from_test.zig diff --git a/test/compile_errors/stage1/test/shift_on_type_with_non-power-of-two_size.zig b/test/cases/compile_errors/stage1/test/shift_on_type_with_non-power-of-two_size.zig similarity index 100% rename from test/compile_errors/stage1/test/shift_on_type_with_non-power-of-two_size.zig rename to test/cases/compile_errors/stage1/test/shift_on_type_with_non-power-of-two_size.zig diff --git a/test/compile_errors/stage1/test/shuffle_with_selected_index_past_first_vector_length.zig b/test/cases/compile_errors/stage1/test/shuffle_with_selected_index_past_first_vector_length.zig similarity index 100% rename from test/compile_errors/stage1/test/shuffle_with_selected_index_past_first_vector_length.zig rename to test/cases/compile_errors/stage1/test/shuffle_with_selected_index_past_first_vector_length.zig diff --git a/test/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig b/test/cases/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig similarity index 100% rename from test/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig rename to test/cases/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig diff --git a/test/compile_errors/stage1/test/switching_with_exhaustive_enum_has___prong_.zig b/test/cases/compile_errors/stage1/test/switching_with_exhaustive_enum_has___prong_.zig similarity index 100% rename from test/compile_errors/stage1/test/switching_with_exhaustive_enum_has___prong_.zig rename to test/cases/compile_errors/stage1/test/switching_with_exhaustive_enum_has___prong_.zig diff --git a/test/compile_errors/stage1/test/switching_with_non-exhaustive_enums.zig b/test/cases/compile_errors/stage1/test/switching_with_non-exhaustive_enums.zig similarity index 100% rename from test/compile_errors/stage1/test/switching_with_non-exhaustive_enums.zig rename to test/cases/compile_errors/stage1/test/switching_with_non-exhaustive_enums.zig diff --git a/test/compile_errors/stage1/test/tagName_on_invalid_value_of_non-exhaustive_enum.zig b/test/cases/compile_errors/stage1/test/tagName_on_invalid_value_of_non-exhaustive_enum.zig similarity index 100% rename from test/compile_errors/stage1/test/tagName_on_invalid_value_of_non-exhaustive_enum.zig rename to test/cases/compile_errors/stage1/test/tagName_on_invalid_value_of_non-exhaustive_enum.zig diff --git a/test/compile_errors/stage1/test/type_mismatch_in_C_prototype_with_varargs.zig b/test/cases/compile_errors/stage1/test/type_mismatch_in_C_prototype_with_varargs.zig similarity index 100% rename from test/compile_errors/stage1/test/type_mismatch_in_C_prototype_with_varargs.zig rename to test/cases/compile_errors/stage1/test/type_mismatch_in_C_prototype_with_varargs.zig diff --git a/test/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig b/test/cases/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig similarity index 100% rename from test/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig rename to test/cases/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig diff --git a/test/compile_errors/stage2/comptime_unreachable.zig b/test/cases/compile_errors/stage2/comptime_unreachable.zig similarity index 100% rename from test/compile_errors/stage2/comptime_unreachable.zig rename to test/cases/compile_errors/stage2/comptime_unreachable.zig diff --git a/test/compile_errors/stage2/constant_inside_comptime_function_has_compile_error.zig b/test/cases/compile_errors/stage2/constant_inside_comptime_function_has_compile_error.zig similarity index 100% rename from test/compile_errors/stage2/constant_inside_comptime_function_has_compile_error.zig rename to test/cases/compile_errors/stage2/constant_inside_comptime_function_has_compile_error.zig diff --git a/test/compile_errors/stage2/duplicate-unused_labels.zig b/test/cases/compile_errors/stage2/duplicate-unused_labels.zig similarity index 100% rename from test/compile_errors/stage2/duplicate-unused_labels.zig rename to test/cases/compile_errors/stage2/duplicate-unused_labels.zig diff --git a/test/compile_errors/stage2/embed_outside_package.zig b/test/cases/compile_errors/stage2/embed_outside_package.zig similarity index 100% rename from test/compile_errors/stage2/embed_outside_package.zig rename to test/cases/compile_errors/stage2/embed_outside_package.zig diff --git a/test/compile_errors/stage2/import_outside_package.zig b/test/cases/compile_errors/stage2/import_outside_package.zig similarity index 100% rename from test/compile_errors/stage2/import_outside_package.zig rename to test/cases/compile_errors/stage2/import_outside_package.zig diff --git a/test/compile_errors/stage2/out_of_bounds_index.zig b/test/cases/compile_errors/stage2/out_of_bounds_index.zig similarity index 100% rename from test/compile_errors/stage2/out_of_bounds_index.zig rename to test/cases/compile_errors/stage2/out_of_bounds_index.zig diff --git a/test/compile_errors/stage2/slice_of_null_pointer.zig b/test/cases/compile_errors/stage2/slice_of_null_pointer.zig similarity index 100% rename from test/compile_errors/stage2/slice_of_null_pointer.zig rename to test/cases/compile_errors/stage2/slice_of_null_pointer.zig diff --git a/test/compile_errors/stage2/struct_duplicate_field_name.zig b/test/cases/compile_errors/stage2/struct_duplicate_field_name.zig similarity index 100% rename from test/compile_errors/stage2/struct_duplicate_field_name.zig rename to test/cases/compile_errors/stage2/struct_duplicate_field_name.zig diff --git a/test/compile_errors/stage2/union_access_of_inactive_field.zig b/test/cases/compile_errors/stage2/union_access_of_inactive_field.zig similarity index 100% rename from test/compile_errors/stage2/union_access_of_inactive_field.zig rename to test/cases/compile_errors/stage2/union_access_of_inactive_field.zig diff --git a/test/compile_errors/stage2/union_duplicate_enum_field.zig b/test/cases/compile_errors/stage2/union_duplicate_enum_field.zig similarity index 100% rename from test/compile_errors/stage2/union_duplicate_enum_field.zig rename to test/cases/compile_errors/stage2/union_duplicate_enum_field.zig diff --git a/test/compile_errors/stage2/union_duplicate_field_definition.zig b/test/cases/compile_errors/stage2/union_duplicate_field_definition.zig similarity index 100% rename from test/compile_errors/stage2/union_duplicate_field_definition.zig rename to test/cases/compile_errors/stage2/union_duplicate_field_definition.zig diff --git a/test/compile_errors/stage2/union_enum_field_missing.zig b/test/cases/compile_errors/stage2/union_enum_field_missing.zig similarity index 100% rename from test/compile_errors/stage2/union_enum_field_missing.zig rename to test/cases/compile_errors/stage2/union_enum_field_missing.zig diff --git a/test/compile_errors/stage2/union_extra_field.zig b/test/cases/compile_errors/stage2/union_extra_field.zig similarity index 100% rename from test/compile_errors/stage2/union_extra_field.zig rename to test/cases/compile_errors/stage2/union_extra_field.zig diff --git a/test/compile_errors/stage2/union_runtime_coercion_from_enum.zig b/test/cases/compile_errors/stage2/union_runtime_coercion_from_enum.zig similarity index 100% rename from test/compile_errors/stage2/union_runtime_coercion_from_enum.zig rename to test/cases/compile_errors/stage2/union_runtime_coercion_from_enum.zig diff --git a/test/incremental/compile_log.0.zig b/test/cases/compile_log.0.zig similarity index 100% rename from test/incremental/compile_log.0.zig rename to test/cases/compile_log.0.zig diff --git a/test/incremental/compile_log.1.zig b/test/cases/compile_log.1.zig similarity index 100% rename from test/incremental/compile_log.1.zig rename to test/cases/compile_log.1.zig diff --git a/test/incremental/double_ampersand.0.zig b/test/cases/double_ampersand.0.zig similarity index 100% rename from test/incremental/double_ampersand.0.zig rename to test/cases/double_ampersand.0.zig diff --git a/test/incremental/double_ampersand.1.zig b/test/cases/double_ampersand.1.zig similarity index 100% rename from test/incremental/double_ampersand.1.zig rename to test/cases/double_ampersand.1.zig diff --git a/test/incremental/double_ampersand.2.zig b/test/cases/double_ampersand.2.zig similarity index 100% rename from test/incremental/double_ampersand.2.zig rename to test/cases/double_ampersand.2.zig diff --git a/test/incremental/enum_values.0.zig b/test/cases/enum_values.0.zig similarity index 100% rename from test/incremental/enum_values.0.zig rename to test/cases/enum_values.0.zig diff --git a/test/incremental/enum_values.1.zig b/test/cases/enum_values.1.zig similarity index 100% rename from test/incremental/enum_values.1.zig rename to test/cases/enum_values.1.zig diff --git a/test/incremental/extern_variable_has_no_type.0.zig b/test/cases/extern_variable_has_no_type.0.zig similarity index 100% rename from test/incremental/extern_variable_has_no_type.0.zig rename to test/cases/extern_variable_has_no_type.0.zig diff --git a/test/incremental/extern_variable_has_no_type.1.zig b/test/cases/extern_variable_has_no_type.1.zig similarity index 100% rename from test/incremental/extern_variable_has_no_type.1.zig rename to test/cases/extern_variable_has_no_type.1.zig diff --git a/test/incremental/function_calls.0.zig b/test/cases/function_calls.0.zig similarity index 100% rename from test/incremental/function_calls.0.zig rename to test/cases/function_calls.0.zig diff --git a/test/incremental/function_calls.1.zig b/test/cases/function_calls.1.zig similarity index 100% rename from test/incremental/function_calls.1.zig rename to test/cases/function_calls.1.zig diff --git a/test/incremental/function_calls.2.zig b/test/cases/function_calls.2.zig similarity index 100% rename from test/incremental/function_calls.2.zig rename to test/cases/function_calls.2.zig diff --git a/test/incremental/function_calls.3.zig b/test/cases/function_calls.3.zig similarity index 100% rename from test/incremental/function_calls.3.zig rename to test/cases/function_calls.3.zig diff --git a/test/incremental/function_redeclaration.zig b/test/cases/function_redeclaration.zig similarity index 100% rename from test/incremental/function_redeclaration.zig rename to test/cases/function_redeclaration.zig diff --git a/test/incremental/global_variable_redeclaration.zig b/test/cases/global_variable_redeclaration.zig similarity index 100% rename from test/incremental/global_variable_redeclaration.zig rename to test/cases/global_variable_redeclaration.zig diff --git a/test/incremental/inner_func_accessing_outer_var.zig b/test/cases/inner_func_accessing_outer_var.zig similarity index 100% rename from test/incremental/inner_func_accessing_outer_var.zig rename to test/cases/inner_func_accessing_outer_var.zig diff --git a/test/incremental/int_to_ptr.0.zig b/test/cases/int_to_ptr.0.zig similarity index 100% rename from test/incremental/int_to_ptr.0.zig rename to test/cases/int_to_ptr.0.zig diff --git a/test/incremental/int_to_ptr.1.zig b/test/cases/int_to_ptr.1.zig similarity index 100% rename from test/incremental/int_to_ptr.1.zig rename to test/cases/int_to_ptr.1.zig diff --git a/test/incremental/large_add_function.zig b/test/cases/large_add_function.zig similarity index 100% rename from test/incremental/large_add_function.zig rename to test/cases/large_add_function.zig diff --git a/test/incremental/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig b/test/cases/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig similarity index 100% rename from test/incremental/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig rename to test/cases/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig diff --git a/test/incremental/llvm/address_spaces_pointer_access_chaining_array_pointer.zig b/test/cases/llvm/address_spaces_pointer_access_chaining_array_pointer.zig similarity index 100% rename from test/incremental/llvm/address_spaces_pointer_access_chaining_array_pointer.zig rename to test/cases/llvm/address_spaces_pointer_access_chaining_array_pointer.zig diff --git a/test/incremental/llvm/address_spaces_pointer_access_chaining_complex.zig b/test/cases/llvm/address_spaces_pointer_access_chaining_complex.zig similarity index 100% rename from test/incremental/llvm/address_spaces_pointer_access_chaining_complex.zig rename to test/cases/llvm/address_spaces_pointer_access_chaining_complex.zig diff --git a/test/incremental/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig b/test/cases/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig similarity index 100% rename from test/incremental/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig rename to test/cases/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig diff --git a/test/incremental/llvm/any_typed_null_to_any_typed_optional.zig b/test/cases/llvm/any_typed_null_to_any_typed_optional.zig similarity index 100% rename from test/incremental/llvm/any_typed_null_to_any_typed_optional.zig rename to test/cases/llvm/any_typed_null_to_any_typed_optional.zig diff --git a/test/incremental/llvm/blocks.zig b/test/cases/llvm/blocks.zig similarity index 100% rename from test/incremental/llvm/blocks.zig rename to test/cases/llvm/blocks.zig diff --git a/test/incremental/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig b/test/cases/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig similarity index 100% rename from test/incremental/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig rename to test/cases/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig diff --git a/test/incremental/llvm/f_segment_address_space_reading_and_writing.zig b/test/cases/llvm/f_segment_address_space_reading_and_writing.zig similarity index 100% rename from test/incremental/llvm/f_segment_address_space_reading_and_writing.zig rename to test/cases/llvm/f_segment_address_space_reading_and_writing.zig diff --git a/test/incremental/llvm/for_loop.zig b/test/cases/llvm/for_loop.zig similarity index 100% rename from test/incremental/llvm/for_loop.zig rename to test/cases/llvm/for_loop.zig diff --git a/test/incremental/llvm/hello_world.zig b/test/cases/llvm/hello_world.zig similarity index 100% rename from test/incremental/llvm/hello_world.zig rename to test/cases/llvm/hello_world.zig diff --git a/test/incremental/llvm/invalid_address_space_coercion.zig b/test/cases/llvm/invalid_address_space_coercion.zig similarity index 100% rename from test/incremental/llvm/invalid_address_space_coercion.zig rename to test/cases/llvm/invalid_address_space_coercion.zig diff --git a/test/incremental/llvm/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig b/test/cases/llvm/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig similarity index 100% rename from test/incremental/llvm/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig rename to test/cases/llvm/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig diff --git a/test/incremental/llvm/nested_blocks.zig b/test/cases/llvm/nested_blocks.zig similarity index 100% rename from test/incremental/llvm/nested_blocks.zig rename to test/cases/llvm/nested_blocks.zig diff --git a/test/incremental/llvm/optionals.zig b/test/cases/llvm/optionals.zig similarity index 100% rename from test/incremental/llvm/optionals.zig rename to test/cases/llvm/optionals.zig diff --git a/test/incremental/llvm/pointer_keeps_address_space.zig b/test/cases/llvm/pointer_keeps_address_space.zig similarity index 100% rename from test/incremental/llvm/pointer_keeps_address_space.zig rename to test/cases/llvm/pointer_keeps_address_space.zig diff --git a/test/incremental/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig b/test/cases/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig similarity index 100% rename from test/incremental/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig rename to test/cases/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig diff --git a/test/incremental/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig b/test/cases/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig similarity index 100% rename from test/incremental/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig rename to test/cases/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig diff --git a/test/incremental/llvm/pointer_with_different_address_spaces.zig b/test/cases/llvm/pointer_with_different_address_spaces.zig similarity index 100% rename from test/incremental/llvm/pointer_with_different_address_spaces.zig rename to test/cases/llvm/pointer_with_different_address_spaces.zig diff --git a/test/incremental/llvm/pointers_with_different_address_spaces.zig b/test/cases/llvm/pointers_with_different_address_spaces.zig similarity index 100% rename from test/incremental/llvm/pointers_with_different_address_spaces.zig rename to test/cases/llvm/pointers_with_different_address_spaces.zig diff --git a/test/incremental/llvm/rem.zig b/test/cases/llvm/rem.zig similarity index 100% rename from test/incremental/llvm/rem.zig rename to test/cases/llvm/rem.zig diff --git a/test/incremental/llvm/shift_right_plus_left.0.zig b/test/cases/llvm/shift_right_plus_left.0.zig similarity index 100% rename from test/incremental/llvm/shift_right_plus_left.0.zig rename to test/cases/llvm/shift_right_plus_left.0.zig diff --git a/test/incremental/llvm/shift_right_plus_left.1.zig b/test/cases/llvm/shift_right_plus_left.1.zig similarity index 100% rename from test/incremental/llvm/shift_right_plus_left.1.zig rename to test/cases/llvm/shift_right_plus_left.1.zig diff --git a/test/incremental/llvm/simple_addition_and_subtraction.zig b/test/cases/llvm/simple_addition_and_subtraction.zig similarity index 100% rename from test/incremental/llvm/simple_addition_and_subtraction.zig rename to test/cases/llvm/simple_addition_and_subtraction.zig diff --git a/test/incremental/llvm/simple_if_statement.zig b/test/cases/llvm/simple_if_statement.zig similarity index 100% rename from test/incremental/llvm/simple_if_statement.zig rename to test/cases/llvm/simple_if_statement.zig diff --git a/test/incremental/llvm/while_loops.zig b/test/cases/llvm/while_loops.zig similarity index 100% rename from test/incremental/llvm/while_loops.zig rename to test/cases/llvm/while_loops.zig diff --git a/test/incremental/lower_unnamed_consts_structs.0.zig b/test/cases/lower_unnamed_consts_structs.0.zig similarity index 100% rename from test/incremental/lower_unnamed_consts_structs.0.zig rename to test/cases/lower_unnamed_consts_structs.0.zig diff --git a/test/incremental/lower_unnamed_consts_structs.1.zig b/test/cases/lower_unnamed_consts_structs.1.zig similarity index 100% rename from test/incremental/lower_unnamed_consts_structs.1.zig rename to test/cases/lower_unnamed_consts_structs.1.zig diff --git a/test/incremental/lower_unnamed_consts_structs.2.zig b/test/cases/lower_unnamed_consts_structs.2.zig similarity index 100% rename from test/incremental/lower_unnamed_consts_structs.2.zig rename to test/cases/lower_unnamed_consts_structs.2.zig diff --git a/test/incremental/merge_error_sets.0.zig b/test/cases/merge_error_sets.0.zig similarity index 100% rename from test/incremental/merge_error_sets.0.zig rename to test/cases/merge_error_sets.0.zig diff --git a/test/incremental/merge_error_sets.1.zig b/test/cases/merge_error_sets.1.zig similarity index 100% rename from test/incremental/merge_error_sets.1.zig rename to test/cases/merge_error_sets.1.zig diff --git a/test/incremental/multiplying_numbers_at_runtime_and_comptime.0.zig b/test/cases/multiplying_numbers_at_runtime_and_comptime.0.zig similarity index 100% rename from test/incremental/multiplying_numbers_at_runtime_and_comptime.0.zig rename to test/cases/multiplying_numbers_at_runtime_and_comptime.0.zig diff --git a/test/incremental/multiplying_numbers_at_runtime_and_comptime.1.zig b/test/cases/multiplying_numbers_at_runtime_and_comptime.1.zig similarity index 100% rename from test/incremental/multiplying_numbers_at_runtime_and_comptime.1.zig rename to test/cases/multiplying_numbers_at_runtime_and_comptime.1.zig diff --git a/test/incremental/multiplying_numbers_at_runtime_and_comptime.2.zig b/test/cases/multiplying_numbers_at_runtime_and_comptime.2.zig similarity index 100% rename from test/incremental/multiplying_numbers_at_runtime_and_comptime.2.zig rename to test/cases/multiplying_numbers_at_runtime_and_comptime.2.zig diff --git a/test/incremental/non_leaf_functions.zig b/test/cases/non_leaf_functions.zig similarity index 100% rename from test/incremental/non_leaf_functions.zig rename to test/cases/non_leaf_functions.zig diff --git a/test/incremental/optional_payload.0.zig b/test/cases/optional_payload.0.zig similarity index 100% rename from test/incremental/optional_payload.0.zig rename to test/cases/optional_payload.0.zig diff --git a/test/incremental/optional_payload.1.zig b/test/cases/optional_payload.1.zig similarity index 100% rename from test/incremental/optional_payload.1.zig rename to test/cases/optional_payload.1.zig diff --git a/test/incremental/optional_payload.2.zig b/test/cases/optional_payload.2.zig similarity index 100% rename from test/incremental/optional_payload.2.zig rename to test/cases/optional_payload.2.zig diff --git a/test/incremental/optional_payload.3.zig b/test/cases/optional_payload.3.zig similarity index 100% rename from test/incremental/optional_payload.3.zig rename to test/cases/optional_payload.3.zig diff --git a/test/incremental/orelse_at_comptime.0.zig b/test/cases/orelse_at_comptime.0.zig similarity index 100% rename from test/incremental/orelse_at_comptime.0.zig rename to test/cases/orelse_at_comptime.0.zig diff --git a/test/incremental/orelse_at_comptime.1.zig b/test/cases/orelse_at_comptime.1.zig similarity index 100% rename from test/incremental/orelse_at_comptime.1.zig rename to test/cases/orelse_at_comptime.1.zig diff --git a/test/incremental/passing_u0_to_function.zig b/test/cases/passing_u0_to_function.zig similarity index 100% rename from test/incremental/passing_u0_to_function.zig rename to test/cases/passing_u0_to_function.zig diff --git a/test/incremental/plan9/exit.zig b/test/cases/plan9/exit.zig similarity index 100% rename from test/incremental/plan9/exit.zig rename to test/cases/plan9/exit.zig diff --git a/test/incremental/plan9/hello_world_with_updates.0.zig b/test/cases/plan9/hello_world_with_updates.0.zig similarity index 100% rename from test/incremental/plan9/hello_world_with_updates.0.zig rename to test/cases/plan9/hello_world_with_updates.0.zig diff --git a/test/incremental/plan9/hello_world_with_updates.1.zig b/test/cases/plan9/hello_world_with_updates.1.zig similarity index 100% rename from test/incremental/plan9/hello_world_with_updates.1.zig rename to test/cases/plan9/hello_world_with_updates.1.zig diff --git a/test/incremental/recursive_fibonacci.zig b/test/cases/recursive_fibonacci.zig similarity index 100% rename from test/incremental/recursive_fibonacci.zig rename to test/cases/recursive_fibonacci.zig diff --git a/test/incremental/recursive_inline_function.0.zig b/test/cases/recursive_inline_function.0.zig similarity index 100% rename from test/incremental/recursive_inline_function.0.zig rename to test/cases/recursive_inline_function.0.zig diff --git a/test/incremental/recursive_inline_function.1.zig b/test/cases/recursive_inline_function.1.zig similarity index 100% rename from test/incremental/recursive_inline_function.1.zig rename to test/cases/recursive_inline_function.1.zig diff --git a/test/incremental/redundant_comptime.0.zig b/test/cases/redundant_comptime.0.zig similarity index 100% rename from test/incremental/redundant_comptime.0.zig rename to test/cases/redundant_comptime.0.zig diff --git a/test/incremental/redundant_comptime.1.zig b/test/cases/redundant_comptime.1.zig similarity index 100% rename from test/incremental/redundant_comptime.1.zig rename to test/cases/redundant_comptime.1.zig diff --git a/test/incremental/returns_in_try.zig b/test/cases/returns_in_try.zig similarity index 100% rename from test/incremental/returns_in_try.zig rename to test/cases/returns_in_try.zig diff --git a/test/incremental/riscv64-linux/hello_world_with_updates.0.zig b/test/cases/riscv64-linux/hello_world_with_updates.0.zig similarity index 100% rename from test/incremental/riscv64-linux/hello_world_with_updates.0.zig rename to test/cases/riscv64-linux/hello_world_with_updates.0.zig diff --git a/test/incremental/riscv64-linux/hello_world_with_updates.1.zig b/test/cases/riscv64-linux/hello_world_with_updates.1.zig similarity index 100% rename from test/incremental/riscv64-linux/hello_world_with_updates.1.zig rename to test/cases/riscv64-linux/hello_world_with_updates.1.zig diff --git a/test/incremental/runtime_bitwise_and.zig b/test/cases/runtime_bitwise_and.zig similarity index 100% rename from test/incremental/runtime_bitwise_and.zig rename to test/cases/runtime_bitwise_and.zig diff --git a/test/incremental/runtime_bitwise_or.zig b/test/cases/runtime_bitwise_or.zig similarity index 100% rename from test/incremental/runtime_bitwise_or.zig rename to test/cases/runtime_bitwise_or.zig diff --git a/test/incremental/save_function_return_values_in_callee_preserved_register.zig b/test/cases/save_function_return_values_in_callee_preserved_register.zig similarity index 100% rename from test/incremental/save_function_return_values_in_callee_preserved_register.zig rename to test/cases/save_function_return_values_in_callee_preserved_register.zig diff --git a/test/incremental/setting_an_address_space_on_a_local_variable.zig b/test/cases/setting_an_address_space_on_a_local_variable.zig similarity index 100% rename from test/incremental/setting_an_address_space_on_a_local_variable.zig rename to test/cases/setting_an_address_space_on_a_local_variable.zig diff --git a/test/incremental/sparcv9-linux/hello_world.zig b/test/cases/sparcv9-linux/hello_world.zig similarity index 100% rename from test/incremental/sparcv9-linux/hello_world.zig rename to test/cases/sparcv9-linux/hello_world.zig diff --git a/test/incremental/try_in_comptime_in_struct_in_test.zig b/test/cases/try_in_comptime_in_struct_in_test.zig similarity index 100% rename from test/incremental/try_in_comptime_in_struct_in_test.zig rename to test/cases/try_in_comptime_in_struct_in_test.zig diff --git a/test/incremental/type_of.0.zig b/test/cases/type_of.0.zig similarity index 100% rename from test/incremental/type_of.0.zig rename to test/cases/type_of.0.zig diff --git a/test/incremental/type_of.1.zig b/test/cases/type_of.1.zig similarity index 100% rename from test/incremental/type_of.1.zig rename to test/cases/type_of.1.zig diff --git a/test/incremental/type_of.2.zig b/test/cases/type_of.2.zig similarity index 100% rename from test/incremental/type_of.2.zig rename to test/cases/type_of.2.zig diff --git a/test/incremental/unused_labels.0.zig b/test/cases/unused_labels.0.zig similarity index 100% rename from test/incremental/unused_labels.0.zig rename to test/cases/unused_labels.0.zig diff --git a/test/incremental/unused_labels.1.zig b/test/cases/unused_labels.1.zig similarity index 100% rename from test/incremental/unused_labels.1.zig rename to test/cases/unused_labels.1.zig diff --git a/test/incremental/unused_labels.2.zig b/test/cases/unused_labels.2.zig similarity index 100% rename from test/incremental/unused_labels.2.zig rename to test/cases/unused_labels.2.zig diff --git a/test/incremental/unused_labels.3.zig b/test/cases/unused_labels.3.zig similarity index 100% rename from test/incremental/unused_labels.3.zig rename to test/cases/unused_labels.3.zig diff --git a/test/incremental/unused_vars.zig b/test/cases/unused_vars.zig similarity index 100% rename from test/incremental/unused_vars.zig rename to test/cases/unused_vars.zig diff --git a/test/incremental/variable_shadowing.0.zig b/test/cases/variable_shadowing.0.zig similarity index 100% rename from test/incremental/variable_shadowing.0.zig rename to test/cases/variable_shadowing.0.zig diff --git a/test/incremental/variable_shadowing.1.zig b/test/cases/variable_shadowing.1.zig similarity index 100% rename from test/incremental/variable_shadowing.1.zig rename to test/cases/variable_shadowing.1.zig diff --git a/test/incremental/variable_shadowing.2.zig b/test/cases/variable_shadowing.2.zig similarity index 100% rename from test/incremental/variable_shadowing.2.zig rename to test/cases/variable_shadowing.2.zig diff --git a/test/incremental/variable_shadowing.3.zig b/test/cases/variable_shadowing.3.zig similarity index 100% rename from test/incremental/variable_shadowing.3.zig rename to test/cases/variable_shadowing.3.zig diff --git a/test/incremental/variable_shadowing.4.zig b/test/cases/variable_shadowing.4.zig similarity index 100% rename from test/incremental/variable_shadowing.4.zig rename to test/cases/variable_shadowing.4.zig diff --git a/test/incremental/variable_shadowing.5.zig b/test/cases/variable_shadowing.5.zig similarity index 100% rename from test/incremental/variable_shadowing.5.zig rename to test/cases/variable_shadowing.5.zig diff --git a/test/incremental/variable_shadowing.6.zig b/test/cases/variable_shadowing.6.zig similarity index 100% rename from test/incremental/variable_shadowing.6.zig rename to test/cases/variable_shadowing.6.zig diff --git a/test/incremental/variable_shadowing.7.zig b/test/cases/variable_shadowing.7.zig similarity index 100% rename from test/incremental/variable_shadowing.7.zig rename to test/cases/variable_shadowing.7.zig diff --git a/test/incremental/variable_shadowing.8.zig b/test/cases/variable_shadowing.8.zig similarity index 100% rename from test/incremental/variable_shadowing.8.zig rename to test/cases/variable_shadowing.8.zig diff --git a/test/incremental/variable_shadowing.9.zig b/test/cases/variable_shadowing.9.zig similarity index 100% rename from test/incremental/variable_shadowing.9.zig rename to test/cases/variable_shadowing.9.zig diff --git a/test/incremental/wasm-wasi/conditions.0.zig b/test/cases/wasm-wasi/conditions.0.zig similarity index 100% rename from test/incremental/wasm-wasi/conditions.0.zig rename to test/cases/wasm-wasi/conditions.0.zig diff --git a/test/incremental/wasm-wasi/conditions.1.zig b/test/cases/wasm-wasi/conditions.1.zig similarity index 100% rename from test/incremental/wasm-wasi/conditions.1.zig rename to test/cases/wasm-wasi/conditions.1.zig diff --git a/test/incremental/wasm-wasi/conditions.2.zig b/test/cases/wasm-wasi/conditions.2.zig similarity index 100% rename from test/incremental/wasm-wasi/conditions.2.zig rename to test/cases/wasm-wasi/conditions.2.zig diff --git a/test/incremental/wasm-wasi/conditions.3.zig b/test/cases/wasm-wasi/conditions.3.zig similarity index 100% rename from test/incremental/wasm-wasi/conditions.3.zig rename to test/cases/wasm-wasi/conditions.3.zig diff --git a/test/incremental/wasm-wasi/conditions.4.zig b/test/cases/wasm-wasi/conditions.4.zig similarity index 100% rename from test/incremental/wasm-wasi/conditions.4.zig rename to test/cases/wasm-wasi/conditions.4.zig diff --git a/test/incremental/wasm-wasi/conditions.5.zig b/test/cases/wasm-wasi/conditions.5.zig similarity index 100% rename from test/incremental/wasm-wasi/conditions.5.zig rename to test/cases/wasm-wasi/conditions.5.zig diff --git a/test/incremental/wasm-wasi/error_unions.0.zig b/test/cases/wasm-wasi/error_unions.0.zig similarity index 100% rename from test/incremental/wasm-wasi/error_unions.0.zig rename to test/cases/wasm-wasi/error_unions.0.zig diff --git a/test/incremental/wasm-wasi/error_unions.1.zig b/test/cases/wasm-wasi/error_unions.1.zig similarity index 100% rename from test/incremental/wasm-wasi/error_unions.1.zig rename to test/cases/wasm-wasi/error_unions.1.zig diff --git a/test/incremental/wasm-wasi/error_unions.2.zig b/test/cases/wasm-wasi/error_unions.2.zig similarity index 100% rename from test/incremental/wasm-wasi/error_unions.2.zig rename to test/cases/wasm-wasi/error_unions.2.zig diff --git a/test/incremental/wasm-wasi/error_unions.3.zig b/test/cases/wasm-wasi/error_unions.3.zig similarity index 100% rename from test/incremental/wasm-wasi/error_unions.3.zig rename to test/cases/wasm-wasi/error_unions.3.zig diff --git a/test/incremental/wasm-wasi/error_unions.4.zig b/test/cases/wasm-wasi/error_unions.4.zig similarity index 100% rename from test/incremental/wasm-wasi/error_unions.4.zig rename to test/cases/wasm-wasi/error_unions.4.zig diff --git a/test/incremental/wasm-wasi/error_unions.5.zig b/test/cases/wasm-wasi/error_unions.5.zig similarity index 100% rename from test/incremental/wasm-wasi/error_unions.5.zig rename to test/cases/wasm-wasi/error_unions.5.zig diff --git a/test/incremental/wasm-wasi/locals.0.zig b/test/cases/wasm-wasi/locals.0.zig similarity index 100% rename from test/incremental/wasm-wasi/locals.0.zig rename to test/cases/wasm-wasi/locals.0.zig diff --git a/test/incremental/wasm-wasi/locals.1.zig b/test/cases/wasm-wasi/locals.1.zig similarity index 100% rename from test/incremental/wasm-wasi/locals.1.zig rename to test/cases/wasm-wasi/locals.1.zig diff --git a/test/incremental/wasm-wasi/optionals.0.zig b/test/cases/wasm-wasi/optionals.0.zig similarity index 100% rename from test/incremental/wasm-wasi/optionals.0.zig rename to test/cases/wasm-wasi/optionals.0.zig diff --git a/test/incremental/wasm-wasi/optionals.1.zig b/test/cases/wasm-wasi/optionals.1.zig similarity index 100% rename from test/incremental/wasm-wasi/optionals.1.zig rename to test/cases/wasm-wasi/optionals.1.zig diff --git a/test/incremental/wasm-wasi/optionals.2.zig b/test/cases/wasm-wasi/optionals.2.zig similarity index 100% rename from test/incremental/wasm-wasi/optionals.2.zig rename to test/cases/wasm-wasi/optionals.2.zig diff --git a/test/incremental/wasm-wasi/optionals.3.zig b/test/cases/wasm-wasi/optionals.3.zig similarity index 100% rename from test/incremental/wasm-wasi/optionals.3.zig rename to test/cases/wasm-wasi/optionals.3.zig diff --git a/test/incremental/wasm-wasi/optionals.4.zig b/test/cases/wasm-wasi/optionals.4.zig similarity index 100% rename from test/incremental/wasm-wasi/optionals.4.zig rename to test/cases/wasm-wasi/optionals.4.zig diff --git a/test/incremental/wasm-wasi/pointers.0.zig b/test/cases/wasm-wasi/pointers.0.zig similarity index 100% rename from test/incremental/wasm-wasi/pointers.0.zig rename to test/cases/wasm-wasi/pointers.0.zig diff --git a/test/incremental/wasm-wasi/pointers.1.zig b/test/cases/wasm-wasi/pointers.1.zig similarity index 100% rename from test/incremental/wasm-wasi/pointers.1.zig rename to test/cases/wasm-wasi/pointers.1.zig diff --git a/test/incremental/wasm-wasi/structs.0.zig b/test/cases/wasm-wasi/structs.0.zig similarity index 100% rename from test/incremental/wasm-wasi/structs.0.zig rename to test/cases/wasm-wasi/structs.0.zig diff --git a/test/incremental/wasm-wasi/structs.1.zig b/test/cases/wasm-wasi/structs.1.zig similarity index 100% rename from test/incremental/wasm-wasi/structs.1.zig rename to test/cases/wasm-wasi/structs.1.zig diff --git a/test/incremental/wasm-wasi/structs.2.zig b/test/cases/wasm-wasi/structs.2.zig similarity index 100% rename from test/incremental/wasm-wasi/structs.2.zig rename to test/cases/wasm-wasi/structs.2.zig diff --git a/test/incremental/wasm-wasi/structs.3.zig b/test/cases/wasm-wasi/structs.3.zig similarity index 100% rename from test/incremental/wasm-wasi/structs.3.zig rename to test/cases/wasm-wasi/structs.3.zig diff --git a/test/incremental/wasm-wasi/structs.4.zig b/test/cases/wasm-wasi/structs.4.zig similarity index 100% rename from test/incremental/wasm-wasi/structs.4.zig rename to test/cases/wasm-wasi/structs.4.zig diff --git a/test/incremental/wasm-wasi/switch.0.zig b/test/cases/wasm-wasi/switch.0.zig similarity index 100% rename from test/incremental/wasm-wasi/switch.0.zig rename to test/cases/wasm-wasi/switch.0.zig diff --git a/test/incremental/wasm-wasi/switch.1.zig b/test/cases/wasm-wasi/switch.1.zig similarity index 100% rename from test/incremental/wasm-wasi/switch.1.zig rename to test/cases/wasm-wasi/switch.1.zig diff --git a/test/incremental/wasm-wasi/switch.2.zig b/test/cases/wasm-wasi/switch.2.zig similarity index 100% rename from test/incremental/wasm-wasi/switch.2.zig rename to test/cases/wasm-wasi/switch.2.zig diff --git a/test/incremental/wasm-wasi/switch.3.zig b/test/cases/wasm-wasi/switch.3.zig similarity index 100% rename from test/incremental/wasm-wasi/switch.3.zig rename to test/cases/wasm-wasi/switch.3.zig diff --git a/test/incremental/wasm-wasi/while_loops.0.zig b/test/cases/wasm-wasi/while_loops.0.zig similarity index 100% rename from test/incremental/wasm-wasi/while_loops.0.zig rename to test/cases/wasm-wasi/while_loops.0.zig diff --git a/test/incremental/wasm-wasi/while_loops.1.zig b/test/cases/wasm-wasi/while_loops.1.zig similarity index 100% rename from test/incremental/wasm-wasi/while_loops.1.zig rename to test/cases/wasm-wasi/while_loops.1.zig diff --git a/test/incremental/wasm-wasi/while_loops.2.zig b/test/cases/wasm-wasi/while_loops.2.zig similarity index 100% rename from test/incremental/wasm-wasi/while_loops.2.zig rename to test/cases/wasm-wasi/while_loops.2.zig diff --git a/test/incremental/x86_64-linux/assert_function.0.zig b/test/cases/x86_64-linux/assert_function.0.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.0.zig rename to test/cases/x86_64-linux/assert_function.0.zig diff --git a/test/incremental/x86_64-linux/assert_function.1.zig b/test/cases/x86_64-linux/assert_function.1.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.1.zig rename to test/cases/x86_64-linux/assert_function.1.zig diff --git a/test/incremental/x86_64-linux/assert_function.10.zig b/test/cases/x86_64-linux/assert_function.10.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.10.zig rename to test/cases/x86_64-linux/assert_function.10.zig diff --git a/test/incremental/x86_64-linux/assert_function.11.zig b/test/cases/x86_64-linux/assert_function.11.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.11.zig rename to test/cases/x86_64-linux/assert_function.11.zig diff --git a/test/incremental/x86_64-linux/assert_function.12.zig b/test/cases/x86_64-linux/assert_function.12.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.12.zig rename to test/cases/x86_64-linux/assert_function.12.zig diff --git a/test/incremental/x86_64-linux/assert_function.13.zig b/test/cases/x86_64-linux/assert_function.13.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.13.zig rename to test/cases/x86_64-linux/assert_function.13.zig diff --git a/test/incremental/x86_64-linux/assert_function.14.zig b/test/cases/x86_64-linux/assert_function.14.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.14.zig rename to test/cases/x86_64-linux/assert_function.14.zig diff --git a/test/incremental/x86_64-linux/assert_function.15.zig b/test/cases/x86_64-linux/assert_function.15.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.15.zig rename to test/cases/x86_64-linux/assert_function.15.zig diff --git a/test/incremental/x86_64-linux/assert_function.16.zig b/test/cases/x86_64-linux/assert_function.16.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.16.zig rename to test/cases/x86_64-linux/assert_function.16.zig diff --git a/test/incremental/x86_64-linux/assert_function.17.zig b/test/cases/x86_64-linux/assert_function.17.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.17.zig rename to test/cases/x86_64-linux/assert_function.17.zig diff --git a/test/incremental/x86_64-linux/assert_function.18.zig b/test/cases/x86_64-linux/assert_function.18.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.18.zig rename to test/cases/x86_64-linux/assert_function.18.zig diff --git a/test/incremental/x86_64-linux/assert_function.2.zig b/test/cases/x86_64-linux/assert_function.2.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.2.zig rename to test/cases/x86_64-linux/assert_function.2.zig diff --git a/test/incremental/x86_64-linux/assert_function.3.zig b/test/cases/x86_64-linux/assert_function.3.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.3.zig rename to test/cases/x86_64-linux/assert_function.3.zig diff --git a/test/incremental/x86_64-linux/assert_function.4.zig b/test/cases/x86_64-linux/assert_function.4.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.4.zig rename to test/cases/x86_64-linux/assert_function.4.zig diff --git a/test/incremental/x86_64-linux/assert_function.5.zig b/test/cases/x86_64-linux/assert_function.5.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.5.zig rename to test/cases/x86_64-linux/assert_function.5.zig diff --git a/test/incremental/x86_64-linux/assert_function.6.zig b/test/cases/x86_64-linux/assert_function.6.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.6.zig rename to test/cases/x86_64-linux/assert_function.6.zig diff --git a/test/incremental/x86_64-linux/assert_function.7.zig b/test/cases/x86_64-linux/assert_function.7.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.7.zig rename to test/cases/x86_64-linux/assert_function.7.zig diff --git a/test/incremental/x86_64-linux/assert_function.8.zig b/test/cases/x86_64-linux/assert_function.8.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.8.zig rename to test/cases/x86_64-linux/assert_function.8.zig diff --git a/test/incremental/x86_64-linux/assert_function.9.zig b/test/cases/x86_64-linux/assert_function.9.zig similarity index 100% rename from test/incremental/x86_64-linux/assert_function.9.zig rename to test/cases/x86_64-linux/assert_function.9.zig diff --git a/test/incremental/x86_64-linux/comptime_var.0.zig b/test/cases/x86_64-linux/comptime_var.0.zig similarity index 100% rename from test/incremental/x86_64-linux/comptime_var.0.zig rename to test/cases/x86_64-linux/comptime_var.0.zig diff --git a/test/incremental/x86_64-linux/comptime_var.1.zig b/test/cases/x86_64-linux/comptime_var.1.zig similarity index 100% rename from test/incremental/x86_64-linux/comptime_var.1.zig rename to test/cases/x86_64-linux/comptime_var.1.zig diff --git a/test/incremental/x86_64-linux/comptime_var.2.zig b/test/cases/x86_64-linux/comptime_var.2.zig similarity index 100% rename from test/incremental/x86_64-linux/comptime_var.2.zig rename to test/cases/x86_64-linux/comptime_var.2.zig diff --git a/test/incremental/x86_64-linux/comptime_var.3.zig b/test/cases/x86_64-linux/comptime_var.3.zig similarity index 100% rename from test/incremental/x86_64-linux/comptime_var.3.zig rename to test/cases/x86_64-linux/comptime_var.3.zig diff --git a/test/incremental/x86_64-linux/comptime_var.4.zig b/test/cases/x86_64-linux/comptime_var.4.zig similarity index 100% rename from test/incremental/x86_64-linux/comptime_var.4.zig rename to test/cases/x86_64-linux/comptime_var.4.zig diff --git a/test/incremental/x86_64-linux/comptime_var.5.zig b/test/cases/x86_64-linux/comptime_var.5.zig similarity index 100% rename from test/incremental/x86_64-linux/comptime_var.5.zig rename to test/cases/x86_64-linux/comptime_var.5.zig diff --git a/test/incremental/x86_64-linux/comptime_var.6.zig b/test/cases/x86_64-linux/comptime_var.6.zig similarity index 100% rename from test/incremental/x86_64-linux/comptime_var.6.zig rename to test/cases/x86_64-linux/comptime_var.6.zig diff --git a/test/incremental/x86_64-linux/hello_world_with_updates.0.zig b/test/cases/x86_64-linux/hello_world_with_updates.0.zig similarity index 100% rename from test/incremental/x86_64-linux/hello_world_with_updates.0.zig rename to test/cases/x86_64-linux/hello_world_with_updates.0.zig diff --git a/test/incremental/x86_64-linux/hello_world_with_updates.1.zig b/test/cases/x86_64-linux/hello_world_with_updates.1.zig similarity index 100% rename from test/incremental/x86_64-linux/hello_world_with_updates.1.zig rename to test/cases/x86_64-linux/hello_world_with_updates.1.zig diff --git a/test/incremental/x86_64-linux/hello_world_with_updates.2.zig b/test/cases/x86_64-linux/hello_world_with_updates.2.zig similarity index 100% rename from test/incremental/x86_64-linux/hello_world_with_updates.2.zig rename to test/cases/x86_64-linux/hello_world_with_updates.2.zig diff --git a/test/incremental/x86_64-linux/hello_world_with_updates.3.zig b/test/cases/x86_64-linux/hello_world_with_updates.3.zig similarity index 100% rename from test/incremental/x86_64-linux/hello_world_with_updates.3.zig rename to test/cases/x86_64-linux/hello_world_with_updates.3.zig diff --git a/test/incremental/x86_64-linux/hello_world_with_updates.4.zig b/test/cases/x86_64-linux/hello_world_with_updates.4.zig similarity index 100% rename from test/incremental/x86_64-linux/hello_world_with_updates.4.zig rename to test/cases/x86_64-linux/hello_world_with_updates.4.zig diff --git a/test/incremental/x86_64-linux/hello_world_with_updates.5.zig b/test/cases/x86_64-linux/hello_world_with_updates.5.zig similarity index 100% rename from test/incremental/x86_64-linux/hello_world_with_updates.5.zig rename to test/cases/x86_64-linux/hello_world_with_updates.5.zig diff --git a/test/incremental/x86_64-linux/inline_assembly.0.zig b/test/cases/x86_64-linux/inline_assembly.0.zig similarity index 100% rename from test/incremental/x86_64-linux/inline_assembly.0.zig rename to test/cases/x86_64-linux/inline_assembly.0.zig diff --git a/test/incremental/x86_64-linux/inline_assembly.1.zig b/test/cases/x86_64-linux/inline_assembly.1.zig similarity index 100% rename from test/incremental/x86_64-linux/inline_assembly.1.zig rename to test/cases/x86_64-linux/inline_assembly.1.zig diff --git a/test/incremental/x86_64-linux/inline_assembly.2.zig b/test/cases/x86_64-linux/inline_assembly.2.zig similarity index 100% rename from test/incremental/x86_64-linux/inline_assembly.2.zig rename to test/cases/x86_64-linux/inline_assembly.2.zig diff --git a/test/incremental/x86_64-linux/inline_assembly.3.zig b/test/cases/x86_64-linux/inline_assembly.3.zig similarity index 100% rename from test/incremental/x86_64-linux/inline_assembly.3.zig rename to test/cases/x86_64-linux/inline_assembly.3.zig diff --git a/test/incremental/x86_64-linux/only_1_function_and_it_gets_updated.0.zig b/test/cases/x86_64-linux/only_1_function_and_it_gets_updated.0.zig similarity index 100% rename from test/incremental/x86_64-linux/only_1_function_and_it_gets_updated.0.zig rename to test/cases/x86_64-linux/only_1_function_and_it_gets_updated.0.zig diff --git a/test/incremental/x86_64-linux/only_1_function_and_it_gets_updated.1.zig b/test/cases/x86_64-linux/only_1_function_and_it_gets_updated.1.zig similarity index 100% rename from test/incremental/x86_64-linux/only_1_function_and_it_gets_updated.1.zig rename to test/cases/x86_64-linux/only_1_function_and_it_gets_updated.1.zig diff --git a/test/incremental/x86_64-macos/assert_function.0.zig b/test/cases/x86_64-macos/assert_function.0.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.0.zig rename to test/cases/x86_64-macos/assert_function.0.zig diff --git a/test/incremental/x86_64-macos/assert_function.1.zig b/test/cases/x86_64-macos/assert_function.1.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.1.zig rename to test/cases/x86_64-macos/assert_function.1.zig diff --git a/test/incremental/x86_64-macos/assert_function.10.zig b/test/cases/x86_64-macos/assert_function.10.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.10.zig rename to test/cases/x86_64-macos/assert_function.10.zig diff --git a/test/incremental/x86_64-macos/assert_function.11.zig b/test/cases/x86_64-macos/assert_function.11.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.11.zig rename to test/cases/x86_64-macos/assert_function.11.zig diff --git a/test/incremental/x86_64-macos/assert_function.12.zig b/test/cases/x86_64-macos/assert_function.12.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.12.zig rename to test/cases/x86_64-macos/assert_function.12.zig diff --git a/test/incremental/x86_64-macos/assert_function.13.zig b/test/cases/x86_64-macos/assert_function.13.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.13.zig rename to test/cases/x86_64-macos/assert_function.13.zig diff --git a/test/incremental/x86_64-macos/assert_function.14.zig b/test/cases/x86_64-macos/assert_function.14.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.14.zig rename to test/cases/x86_64-macos/assert_function.14.zig diff --git a/test/incremental/x86_64-macos/assert_function.15.zig b/test/cases/x86_64-macos/assert_function.15.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.15.zig rename to test/cases/x86_64-macos/assert_function.15.zig diff --git a/test/incremental/x86_64-macos/assert_function.16.zig b/test/cases/x86_64-macos/assert_function.16.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.16.zig rename to test/cases/x86_64-macos/assert_function.16.zig diff --git a/test/incremental/x86_64-macos/assert_function.17.zig b/test/cases/x86_64-macos/assert_function.17.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.17.zig rename to test/cases/x86_64-macos/assert_function.17.zig diff --git a/test/incremental/x86_64-macos/assert_function.18.zig b/test/cases/x86_64-macos/assert_function.18.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.18.zig rename to test/cases/x86_64-macos/assert_function.18.zig diff --git a/test/incremental/x86_64-macos/assert_function.2.zig b/test/cases/x86_64-macos/assert_function.2.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.2.zig rename to test/cases/x86_64-macos/assert_function.2.zig diff --git a/test/incremental/x86_64-macos/assert_function.3.zig b/test/cases/x86_64-macos/assert_function.3.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.3.zig rename to test/cases/x86_64-macos/assert_function.3.zig diff --git a/test/incremental/x86_64-macos/assert_function.4.zig b/test/cases/x86_64-macos/assert_function.4.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.4.zig rename to test/cases/x86_64-macos/assert_function.4.zig diff --git a/test/incremental/x86_64-macos/assert_function.5.zig b/test/cases/x86_64-macos/assert_function.5.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.5.zig rename to test/cases/x86_64-macos/assert_function.5.zig diff --git a/test/incremental/x86_64-macos/assert_function.6.zig b/test/cases/x86_64-macos/assert_function.6.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.6.zig rename to test/cases/x86_64-macos/assert_function.6.zig diff --git a/test/incremental/x86_64-macos/assert_function.7.zig b/test/cases/x86_64-macos/assert_function.7.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.7.zig rename to test/cases/x86_64-macos/assert_function.7.zig diff --git a/test/incremental/x86_64-macos/assert_function.8.zig b/test/cases/x86_64-macos/assert_function.8.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.8.zig rename to test/cases/x86_64-macos/assert_function.8.zig diff --git a/test/incremental/x86_64-macos/assert_function.9.zig b/test/cases/x86_64-macos/assert_function.9.zig similarity index 100% rename from test/incremental/x86_64-macos/assert_function.9.zig rename to test/cases/x86_64-macos/assert_function.9.zig diff --git a/test/incremental/x86_64-macos/comptime_var.0.zig b/test/cases/x86_64-macos/comptime_var.0.zig similarity index 100% rename from test/incremental/x86_64-macos/comptime_var.0.zig rename to test/cases/x86_64-macos/comptime_var.0.zig diff --git a/test/incremental/x86_64-macos/comptime_var.1.zig b/test/cases/x86_64-macos/comptime_var.1.zig similarity index 100% rename from test/incremental/x86_64-macos/comptime_var.1.zig rename to test/cases/x86_64-macos/comptime_var.1.zig diff --git a/test/incremental/x86_64-macos/comptime_var.2.zig b/test/cases/x86_64-macos/comptime_var.2.zig similarity index 100% rename from test/incremental/x86_64-macos/comptime_var.2.zig rename to test/cases/x86_64-macos/comptime_var.2.zig diff --git a/test/incremental/x86_64-macos/comptime_var.3.zig b/test/cases/x86_64-macos/comptime_var.3.zig similarity index 100% rename from test/incremental/x86_64-macos/comptime_var.3.zig rename to test/cases/x86_64-macos/comptime_var.3.zig diff --git a/test/incremental/x86_64-macos/comptime_var.4.zig b/test/cases/x86_64-macos/comptime_var.4.zig similarity index 100% rename from test/incremental/x86_64-macos/comptime_var.4.zig rename to test/cases/x86_64-macos/comptime_var.4.zig diff --git a/test/incremental/x86_64-macos/comptime_var.5.zig b/test/cases/x86_64-macos/comptime_var.5.zig similarity index 100% rename from test/incremental/x86_64-macos/comptime_var.5.zig rename to test/cases/x86_64-macos/comptime_var.5.zig diff --git a/test/incremental/x86_64-macos/comptime_var.6.zig b/test/cases/x86_64-macos/comptime_var.6.zig similarity index 100% rename from test/incremental/x86_64-macos/comptime_var.6.zig rename to test/cases/x86_64-macos/comptime_var.6.zig diff --git a/test/incremental/x86_64-macos/hello_world_with_updates.0.zig b/test/cases/x86_64-macos/hello_world_with_updates.0.zig similarity index 100% rename from test/incremental/x86_64-macos/hello_world_with_updates.0.zig rename to test/cases/x86_64-macos/hello_world_with_updates.0.zig diff --git a/test/incremental/x86_64-macos/hello_world_with_updates.1.zig b/test/cases/x86_64-macos/hello_world_with_updates.1.zig similarity index 100% rename from test/incremental/x86_64-macos/hello_world_with_updates.1.zig rename to test/cases/x86_64-macos/hello_world_with_updates.1.zig diff --git a/test/incremental/x86_64-macos/hello_world_with_updates.2.zig b/test/cases/x86_64-macos/hello_world_with_updates.2.zig similarity index 100% rename from test/incremental/x86_64-macos/hello_world_with_updates.2.zig rename to test/cases/x86_64-macos/hello_world_with_updates.2.zig diff --git a/test/incremental/x86_64-macos/hello_world_with_updates.3.zig b/test/cases/x86_64-macos/hello_world_with_updates.3.zig similarity index 100% rename from test/incremental/x86_64-macos/hello_world_with_updates.3.zig rename to test/cases/x86_64-macos/hello_world_with_updates.3.zig diff --git a/test/incremental/x86_64-macos/hello_world_with_updates.4.zig b/test/cases/x86_64-macos/hello_world_with_updates.4.zig similarity index 100% rename from test/incremental/x86_64-macos/hello_world_with_updates.4.zig rename to test/cases/x86_64-macos/hello_world_with_updates.4.zig diff --git a/test/incremental/x86_64-macos/hello_world_with_updates.5.zig b/test/cases/x86_64-macos/hello_world_with_updates.5.zig similarity index 100% rename from test/incremental/x86_64-macos/hello_world_with_updates.5.zig rename to test/cases/x86_64-macos/hello_world_with_updates.5.zig diff --git a/test/incremental/x86_64-macos/hello_world_with_updates.6.zig b/test/cases/x86_64-macos/hello_world_with_updates.6.zig similarity index 100% rename from test/incremental/x86_64-macos/hello_world_with_updates.6.zig rename to test/cases/x86_64-macos/hello_world_with_updates.6.zig From 259f784241fb44e0a1b570daaf31ba2b9f164106 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 3 May 2022 23:44:32 -0700 Subject: [PATCH 1346/2031] stage2: improve `@sizeOf` and `@alignOf` integers Prior to this commit, the logic for ABI size and ABI alignment for integers was naive and incorrect. This results in wasted hardware as well as undefined behavior in the LLVM backend when we memset an incorrect number of bytes to 0xaa due to disagreeing with LLVM about the ABI size of integers. This commit introduces a "max int align" value which is different per Target. This value is used to derive the ABI size and alignment of all integers. This commit makes an interesting change from stage1, which treats 128-bit integers as 16-bytes aligned for x86_64-linux. stage1 is incorrect. The maximum integer alignment on this system is only 8 bytes. This change breaks the behavior test called "128-bit cmpxchg" because on that target, 128-bit cmpxchg does require a 16-bytes aligned pointer to a 128 bit integer. However, this alignment property does not belong on *all* 128 bit integers - only on the pointer type in the `@cmpxchg` builtin function prototype. The user can then use an alignment override annotation on a 128-bit integer variable or struct field to obtain such a pointer. --- lib/std/target.zig | 77 ++++++++++++++++++++++ src/codegen/llvm.zig | 2 +- src/type.zig | 38 ++++++----- test/behavior/align.zig | 130 ++++++++++++++++++++++++++++++-------- test/behavior/bitcast.zig | 15 +---- test/behavior/struct.zig | 9 +-- 6 files changed, 211 insertions(+), 60 deletions(-) diff --git a/lib/std/target.zig b/lib/std/target.zig index c23a42c963..777b0f0ec0 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1773,6 +1773,83 @@ pub const Target = struct { else => false, }; } + + pub inline fn maxIntAlignment(target: Target) u16 { + return switch (target.cpu.arch) { + .avr => 1, + .msp430 => 2, + .xcore => 4, + + .arm, + .armeb, + .thumb, + .thumbeb, + .x86_64, + .hexagon, + .mips, + .mipsel, + .mips64, + .mips64el, + .powerpc, + .powerpcle, + .powerpc64, + .powerpc64le, + .r600, + .amdgcn, + .riscv32, + .riscv64, + .sparc, + .sparcv9, + .sparcel, + .s390x, + .lanai, + .wasm32, + .wasm64, + => 8, + + .i386 => return switch (target.os.tag) { + .windows => 8, + else => 4, + }, + .aarch64, + .aarch64_be, + .aarch64_32, + .bpfel, + .bpfeb, + .nvptx, + .nvptx64, + => 16, + + // Below this comment are unverified and I have chosen a number + // based on ptrBitWidth. + + .spu_2 => 2, + + .csky, + .arc, + .m68k, + .tce, + .tcele, + .le32, + .amdil, + .hsail, + .spir, + .kalimba, + .renderscript32, + .spirv32, + .shave, + => 4, + + .le64, + .amdil64, + .hsail64, + .spir64, + .renderscript64, + .ve, + .spirv64, + => 8, + }; + } }; test { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 6c74fc8223..068067d765 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -7583,7 +7583,7 @@ pub const FuncGen = struct { const size_bytes = elem_ty.abiSize(target); _ = self.builder.buildMemCpy( self.builder.buildBitCast(ptr, llvm_ptr_u8, ""), - ptr_ty.ptrAlignment(target), + ptr_alignment, self.builder.buildBitCast(elem, llvm_ptr_u8, ""), elem_ty.abiAlignment(target), self.context.intType(Type.usize.intInfo(target).bits).constInt(size_bytes, .False), diff --git a/src/type.zig b/src/type.zig index c96c512d9a..44432b95f6 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2788,11 +2788,6 @@ pub const Type = extern union { return AbiAlignmentAdvanced{ .scalar = target_util.defaultFunctionAlignment(target) }; }, - .i16, .u16 => return AbiAlignmentAdvanced{ .scalar = 2 }, - .i32, .u32 => return AbiAlignmentAdvanced{ .scalar = 4 }, - .i64, .u64 => return AbiAlignmentAdvanced{ .scalar = 8 }, - .u128, .i128 => return AbiAlignmentAdvanced{ .scalar = 16 }, - .isize, .usize, .single_const_pointer_to_comptime_int, @@ -2865,14 +2860,15 @@ pub const Type = extern union { // ABI alignment of vectors? .vector => return AbiAlignmentAdvanced{ .scalar = 16 }, + .i16, .u16 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(16, target) }, + .i32, .u32 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(32, target) }, + .i64, .u64 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(64, target) }, + .u128, .i128 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(128, target) }, + .int_signed, .int_unsigned => { const bits: u16 = ty.cast(Payload.Bits).?.data; if (bits == 0) return AbiAlignmentAdvanced{ .scalar = 0 }; - if (bits <= 8) return AbiAlignmentAdvanced{ .scalar = 1 }; - if (bits <= 16) return AbiAlignmentAdvanced{ .scalar = 2 }; - if (bits <= 32) return AbiAlignmentAdvanced{ .scalar = 4 }; - if (bits <= 64) return AbiAlignmentAdvanced{ .scalar = 8 }; - return AbiAlignmentAdvanced{ .scalar = 16 }; + return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(bits, target) }; }, .optional => { @@ -3113,10 +3109,6 @@ pub const Type = extern union { assert(elem_size >= payload.elem_type.abiAlignment(target)); return (payload.len + 1) * elem_size; }, - .i16, .u16 => return 2, - .i32, .u32 => return 4, - .i64, .u64 => return 8, - .u128, .i128 => return 16, .isize, .usize, @@ -3189,10 +3181,14 @@ pub const Type = extern union { .error_set_merged, => return 2, // TODO revisit this when we have the concept of the error tag type + .i16, .u16 => return intAbiSize(16, target), + .i32, .u32 => return intAbiSize(32, target), + .i64, .u64 => return intAbiSize(64, target), + .u128, .i128 => return intAbiSize(128, target), .int_signed, .int_unsigned => { const bits: u16 = self.cast(Payload.Bits).?.data; if (bits == 0) return 0; - return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8); + return intAbiSize(bits, target); }, .optional => { @@ -3234,6 +3230,18 @@ pub const Type = extern union { }; } + fn intAbiSize(bits: u16, target: Target) u64 { + const alignment = intAbiAlignment(bits, target); + return std.mem.alignForwardGeneric(u64, (bits + 7) / 8, alignment); + } + + fn intAbiAlignment(bits: u16, target: Target) u32 { + return @minimum( + std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8), + target.maxIntAlignment(), + ); + } + /// Asserts the type has the bit size already resolved. pub fn bitSize(ty: Type, target: Target) u64 { return switch (ty.tag()) { diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 3a35d1fcca..9d7ca9958a 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -47,41 +47,121 @@ fn expects4(x: *align(4) u32) void { x.* += 1; } -test "alignment of structs" { +test "alignment of struct with pointer has same alignment as usize" { try expect(@alignOf(struct { a: i32, b: *i32, }) == @alignOf(usize)); } -test "alignment of >= 128-bit integer type" { - try expect(@alignOf(u128) == 16); - try expect(@alignOf(u129) == 16); -} - -test "alignment of struct with 128-bit field" { - try expect(@alignOf(struct { +test "alignment and size of structs with 128-bit fields" { + const A = struct { x: u128, - }) == 16); - - comptime { - try expect(@alignOf(struct { - x: u128, - }) == 16); - } -} - -test "size of extern struct with 128-bit field" { - try expect(@sizeOf(extern struct { + }; + const B = extern struct { x: u128, y: u8, - }) == 32); + }; + const expected = switch (builtin.cpu.arch) { + .arm, + .armeb, + .thumb, + .thumbeb, + .x86_64, + .hexagon, + .mips, + .mipsel, + .mips64, + .mips64el, + .powerpc, + .powerpcle, + .powerpc64, + .powerpc64le, + .r600, + .amdgcn, + .riscv32, + .riscv64, + .sparc, + .sparcv9, + .sparcel, + .s390x, + .lanai, + .wasm32, + .wasm64, + => .{ + .a_align = 8, + .a_size = 16, + .b_align = 8, + .b_size = 24, + + .u128_align = 8, + .u128_size = 16, + .u129_align = 8, + .u129_size = 24, + }, + + .i386 => switch (builtin.os.tag) { + .windows => .{ + .a_align = 8, + .a_size = 16, + + .b_align = 8, + .b_size = 24, + + .u128_align = 8, + .u128_size = 16, + .u129_align = 8, + .u129_size = 24, + }, + else => .{ + .a_align = 4, + .a_size = 16, + + .b_align = 4, + .b_size = 20, + + .u128_align = 4, + .u128_size = 16, + .u129_align = 4, + .u129_size = 20, + }, + }, + + .aarch64, + .aarch64_be, + .aarch64_32, + .bpfel, + .bpfeb, + .nvptx, + .nvptx64, + => .{ + .a_align = 16, + .a_size = 16, + + .b_align = 16, + .b_size = 32, + + .u128_align = 16, + .u128_size = 16, + .u129_align = 16, + .u129_size = 32, + }, + + else => return error.SkipZigTest, + }; comptime { - try expect(@sizeOf(extern struct { - x: u128, - y: u8, - }) == 32); + std.debug.assert(@alignOf(A) == expected.a_align); + std.debug.assert(@sizeOf(A) == expected.a_size); + + std.debug.assert(@alignOf(B) == expected.b_align); + std.debug.assert(@sizeOf(B) == expected.b_size); + + std.debug.assert(@alignOf(u128) == expected.u128_align); + std.debug.assert(@sizeOf(u128) == expected.u128_size); + + std.debug.assert(@alignOf(u129) == expected.u129_align); + std.debug.assert(@sizeOf(u129) == expected.u129_size); } } @@ -328,7 +408,6 @@ test "read 128-bit field from default aligned struct in stack memory" { .nevermind = 1, .badguy = 12, }; - try expect((@ptrToInt(&default_aligned.badguy) % 16) == 0); try expect(12 == default_aligned.badguy); } @@ -345,7 +424,6 @@ test "read 128-bit field from default aligned struct in global memory" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - try expect((@ptrToInt(&default_aligned_global.badguy) % 16) == 0); try expect(12 == default_aligned_global.badguy); } diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index 6be3cc15c4..e74a4c44f4 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -138,18 +138,9 @@ test "@bitCast packed structs at runtime and comptime" { fn doTheTest() !void { var full = Full{ .number = 0x1234 }; var two_halves = @bitCast(Divided, full); - switch (native_endian) { - .Big => { - try expect(two_halves.half1 == 0x12); - try expect(two_halves.quarter3 == 0x3); - try expect(two_halves.quarter4 == 0x4); - }, - .Little => { - try expect(two_halves.half1 == 0x34); - try expect(two_halves.quarter3 == 0x2); - try expect(two_halves.quarter4 == 0x1); - }, - } + try expect(two_halves.half1 == 0x34); + try expect(two_halves.quarter3 == 0x2); + try expect(two_halves.quarter4 == 0x1); } }; try S.doTheTest(); diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index b05126db62..ed75268f7d 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -499,17 +499,14 @@ const Bitfields = packed struct { f7: u8, }; -test "native bit field understands endianness" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; +test "packed struct fields are ordered from LSB to MSB" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - var all: u64 = if (native_endian != .Little) - 0x1111222233445677 - else - 0x7765443322221111; + var all: u64 = 0x7765443322221111; var bytes: [8]u8 = undefined; @memcpy(&bytes, @ptrCast([*]u8, &all), 8); var bitfields = @ptrCast(*Bitfields, &bytes).*; From 5b1c0d922c1061706ae1673333fcfb1d8fdd4602 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 4 May 2022 14:06:54 -0700 Subject: [PATCH 1347/2031] stage2: improve semantics of atomic operations ZIR instructions updated: atomic_load, atomic_rmw, atomic_store, cmpxchg These no longer construct a pointer type as the result location. This solves a TODO that was preventing the pointer from possibly being volatile, as well as properly handling allowzero and addrspace. It also allows the pointer to be over-aligned, which may be needed depending on the target. As a consequence, the element type needs to be communicated in the ZIR. This is done by strategically making one of the operands be ResultLoc.ty instead of ResultLoc.coerced_ty if possible, or otherwise explicitly adding elem_type into the ZIR encoding, such as in the case of atomic_load. The pointer type of atomic operations is now checked in Sema by coercing it to an expected pointer type, that maybe over-aligned according to target requirements. Together with the previous commit, Zig now has smaller alignment for large integers, depending on the target, and yet still has type safety for atomic operations that specially require higher alignment. --- src/AstGen.zig | 60 +++----------- src/Sema.zig | 162 ++++++++++++++++++++------------------ src/Zir.zig | 8 +- src/print_zir.zig | 15 +++- src/target.zig | 68 +++++++++++++++- test/behavior/atomics.zig | 2 +- 6 files changed, 183 insertions(+), 132 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 1da1cf187e..d4895aa2e4 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -7423,42 +7423,22 @@ fn builtinCall( }, .atomic_load => { - const int_type = try typeExpr(gz, scope, params[0]); - // TODO allow this pointer type to be volatile - const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{ - .ptr_type_simple = .{ - .is_allowzero = false, - .is_mutable = false, - .is_volatile = false, - .size = .One, - .elem_type = int_type, - }, - } }); - const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.Bin{ + const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.AtomicLoad{ // zig fmt: off - .lhs = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]), - .rhs = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[2]), + .elem_type = try typeExpr(gz, scope, params[0]), + .ptr = try expr (gz, scope, .none, params[1]), + .ordering = try expr (gz, scope, .{ .coerced_ty = .atomic_order_type }, params[2]), // zig fmt: on }); return rvalue(gz, rl, result, node); }, .atomic_rmw => { const int_type = try typeExpr(gz, scope, params[0]); - // TODO allow this pointer type to be volatile - const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{ - .ptr_type_simple = .{ - .is_allowzero = false, - .is_mutable = true, - .is_volatile = false, - .size = .One, - .elem_type = int_type, - }, - } }); const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{ // zig fmt: off - .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]), + .ptr = try expr(gz, scope, .none, params[1]), .operation = try expr(gz, scope, .{ .coerced_ty = .atomic_rmw_op_type }, params[2]), - .operand = try expr(gz, scope, .{ .coerced_ty = int_type }, params[3]), + .operand = try expr(gz, scope, .{ .ty = int_type }, params[3]), .ordering = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[4]), // zig fmt: on }); @@ -7466,20 +7446,10 @@ fn builtinCall( }, .atomic_store => { const int_type = try typeExpr(gz, scope, params[0]); - // TODO allow this pointer type to be volatile - const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{ - .ptr_type_simple = .{ - .is_allowzero = false, - .is_mutable = true, - .is_volatile = false, - .size = .One, - .elem_type = int_type, - }, - } }); const result = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{ // zig fmt: off - .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]), - .operand = try expr(gz, scope, .{ .coerced_ty = int_type }, params[2]), + .ptr = try expr(gz, scope, .none, params[1]), + .operand = try expr(gz, scope, .{ .ty = int_type }, params[2]), .ordering = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[3]), // zig fmt: on }); @@ -7684,20 +7654,10 @@ fn cmpxchg( tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { const int_type = try typeExpr(gz, scope, params[0]); - // TODO: allow this to be volatile - const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{ - .ptr_type_simple = .{ - .is_allowzero = false, - .is_mutable = true, - .is_volatile = false, - .size = .One, - .elem_type = int_type, - }, - } }); const result = try gz.addPlNode(tag, node, Zir.Inst.Cmpxchg{ // zig fmt: off - .ptr = try expr(gz, scope, .{ .coerced_ty = ptr_type }, params[1]), - .expected_value = try expr(gz, scope, .{ .coerced_ty = int_type }, params[2]), + .ptr = try expr(gz, scope, .none, params[1]), + .expected_value = try expr(gz, scope, .{ .ty = int_type }, params[2]), .new_value = try expr(gz, scope, .{ .coerced_ty = int_type }, params[3]), .success_order = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[4]), .failure_order = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[5]), diff --git a/src/Sema.zig b/src/Sema.zig index 3ad9550175..8125fc0cc1 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14715,51 +14715,64 @@ fn checkNumericType( } } -fn checkAtomicOperandType( +/// Returns the casted pointer. +fn checkAtomicPtrOperand( sema: *Sema, block: *Block, - ty_src: LazySrcLoc, - ty: Type, -) CompileError!void { - var buffer: Type.Payload.Bits = undefined; + elem_ty: Type, + elem_ty_src: LazySrcLoc, + ptr: Air.Inst.Ref, + ptr_src: LazySrcLoc, + ptr_const: bool, +) CompileError!Air.Inst.Ref { const target = sema.mod.getTarget(); - const max_atomic_bits = target_util.largestAtomicBits(target); - const int_ty = switch (ty.zigTypeTag()) { - .Int => ty, - .Enum => ty.intTagType(&buffer), - .Float => { - const bit_count = ty.floatBits(target); - if (bit_count > max_atomic_bits) { - return sema.fail( - block, - ty_src, - "expected {d}-bit float type or smaller; found {d}-bit float type", - .{ max_atomic_bits, bit_count }, - ); - } - return; - }, - .Bool => return, // Will be treated as `u8`. - else => { - if (ty.isPtrAtRuntime()) return; + var diag: target_util.AtomicPtrAlignmentDiagnostics = .{}; + const alignment = target_util.atomicPtrAlignment(target, elem_ty, &diag) catch |err| switch (err) { + error.FloatTooBig => return sema.fail( + block, + elem_ty_src, + "expected {d}-bit float type or smaller; found {d}-bit float type", + .{ diag.max_bits, diag.bits }, + ), + error.IntTooBig => return sema.fail( + block, + elem_ty_src, + "expected {d}-bit integer type or smaller; found {d}-bit integer type", + .{ diag.max_bits, diag.bits }, + ), + error.BadType => return sema.fail( + block, + elem_ty_src, + "expected bool, integer, float, enum, or pointer type; found {}", + .{elem_ty.fmt(sema.mod)}, + ), + }; - return sema.fail( - block, - ty_src, - "expected bool, integer, float, enum, or pointer type; found {}", - .{ty.fmt(sema.mod)}, - ); + var wanted_ptr_data: Type.Payload.Pointer.Data = .{ + .pointee_type = elem_ty, + .@"align" = alignment, + .@"addrspace" = .generic, + .mutable = !ptr_const, + }; + + const ptr_ty = sema.typeOf(ptr); + const ptr_data = switch (try ptr_ty.zigTypeTagOrPoison()) { + .Pointer => ptr_ty.ptrInfo().data, + else => { + const wanted_ptr_ty = try Type.ptr(sema.arena, sema.mod, wanted_ptr_data); + _ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); + unreachable; }, }; - const bit_count = int_ty.intInfo(target).bits; - if (bit_count > max_atomic_bits) { - return sema.fail( - block, - ty_src, - "expected {d}-bit integer type or smaller; found {d}-bit integer type", - .{ max_atomic_bits, bit_count }, - ); - } + + wanted_ptr_data.@"addrspace" = ptr_data.@"addrspace"; + wanted_ptr_data.@"allowzero" = ptr_data.@"allowzero"; + wanted_ptr_data.@"volatile" = ptr_data.@"volatile"; + + const wanted_ptr_ty = try Type.ptr(sema.arena, sema.mod, wanted_ptr_data); + const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); + + return casted_ptr; } fn checkPtrIsNotComptimeMutable( @@ -15036,10 +15049,8 @@ fn zirCmpxchg( const success_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node }; const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = inst_data.src_node }; // zig fmt: on - const ptr = sema.resolveInst(extra.ptr); - const ptr_ty = sema.typeOf(ptr); - const elem_ty = ptr_ty.elemType(); - try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty); + const expected_value = sema.resolveInst(extra.expected_value); + const elem_ty = sema.typeOf(expected_value); if (elem_ty.zigTypeTag() == .Float) { return sema.fail( block, @@ -15048,7 +15059,8 @@ fn zirCmpxchg( .{elem_ty.fmt(sema.mod)}, ); } - const expected_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.expected_value), expected_src); + const uncasted_ptr = sema.resolveInst(extra.ptr); + const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); const new_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.new_value), new_value_src); const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order); const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order); @@ -15081,6 +15093,7 @@ fn zirCmpxchg( // to become undef as well return sema.addConstUndef(result_ty); } + const ptr_ty = sema.typeOf(ptr); const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; const result_val = if (stored_val.eql(expected_val, elem_ty, sema.mod)) blk: { try sema.storePtr(block, src, ptr, new_value); @@ -15487,17 +15500,16 @@ fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; + const extra = sema.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data; // zig fmt: off const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; // zig fmt: on - const ptr = sema.resolveInst(extra.lhs); - const ptr_ty = sema.typeOf(ptr); - const elem_ty = ptr_ty.elemType(); - try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty); - const order = try sema.resolveAtomicOrder(block, order_src, extra.rhs); + const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); + const uncasted_ptr = sema.resolveInst(extra.ptr); + const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true); + const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); switch (order) { .Release, .AcqRel => { @@ -15516,7 +15528,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! } if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { - if (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) |elem_val| { + if (try sema.pointerDeref(block, ptr_src, ptr_val, sema.typeOf(ptr))) |elem_val| { return sema.addConstant(elem_ty, elem_val); } } @@ -15536,19 +15548,19 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data; const src = inst_data.src(); // zig fmt: off - const operand_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; + const elem_ty_src : LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const op_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node }; // zig fmt: on - const ptr = sema.resolveInst(extra.ptr); - const ptr_ty = sema.typeOf(ptr); - const operand_ty = ptr_ty.elemType(); - try sema.checkAtomicOperandType(block, operand_ty_src, operand_ty); + const operand = sema.resolveInst(extra.operand); + const elem_ty = sema.typeOf(operand); + const uncasted_ptr = sema.resolveInst(extra.ptr); + const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation); - switch (operand_ty.zigTypeTag()) { + switch (elem_ty.zigTypeTag()) { .Enum => if (op != .Xchg) { return sema.fail(block, op_src, "@atomicRmw with enum only allowed with .Xchg", .{}); }, @@ -15561,7 +15573,6 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A }, else => {}, } - const operand = try sema.coerce(block, operand_ty, sema.resolveInst(extra.operand), operand_src); const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); if (order == .Unordered) { @@ -15569,8 +15580,8 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } // special case zero bit types - if (try sema.typeHasOnePossibleValue(block, operand_ty_src, operand_ty)) |val| { - return sema.addConstant(operand_ty, val); + if (try sema.typeHasOnePossibleValue(block, elem_ty_src, elem_ty)) |val| { + return sema.addConstant(elem_ty, val); } const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { @@ -15581,22 +15592,23 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A }; if (ptr_val.isComptimeMutablePtr()) { const target = sema.mod.getTarget(); + const ptr_ty = sema.typeOf(ptr); const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; const new_val = switch (op) { // zig fmt: off .Xchg => operand_val, - .Add => try stored_val.numberAddWrap(operand_val, operand_ty, sema.arena, target), - .Sub => try stored_val.numberSubWrap(operand_val, operand_ty, sema.arena, target), - .And => try stored_val.bitwiseAnd (operand_val, operand_ty, sema.arena, target), - .Nand => try stored_val.bitwiseNand (operand_val, operand_ty, sema.arena, target), - .Or => try stored_val.bitwiseOr (operand_val, operand_ty, sema.arena, target), - .Xor => try stored_val.bitwiseXor (operand_val, operand_ty, sema.arena, target), - .Max => stored_val.numberMax (operand_val, target), - .Min => stored_val.numberMin (operand_val, target), + .Add => try stored_val.numberAddWrap(operand_val, elem_ty, sema.arena, target), + .Sub => try stored_val.numberSubWrap(operand_val, elem_ty, sema.arena, target), + .And => try stored_val.bitwiseAnd (operand_val, elem_ty, sema.arena, target), + .Nand => try stored_val.bitwiseNand (operand_val, elem_ty, sema.arena, target), + .Or => try stored_val.bitwiseOr (operand_val, elem_ty, sema.arena, target), + .Xor => try stored_val.bitwiseXor (operand_val, elem_ty, sema.arena, target), + .Max => stored_val.numberMax (operand_val, target), + .Min => stored_val.numberMin (operand_val, target), // zig fmt: on }; - try sema.storePtrVal(block, src, ptr_val, new_val, operand_ty); - return sema.addConstant(operand_ty, stored_val); + try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty); + return sema.addConstant(elem_ty, stored_val); } else break :rs ptr_src; } else ptr_src; @@ -15620,15 +15632,15 @@ fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data; const src = inst_data.src(); // zig fmt: off - const operand_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; + const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; // zig fmt: on - const ptr = sema.resolveInst(extra.ptr); - const operand_ty = sema.typeOf(ptr).elemType(); - try sema.checkAtomicOperandType(block, operand_ty_src, operand_ty); - const operand = try sema.coerce(block, operand_ty, sema.resolveInst(extra.operand), operand_src); + const operand = sema.resolveInst(extra.operand); + const elem_ty = sema.typeOf(operand); + const uncasted_ptr = sema.resolveInst(extra.ptr); + const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); const air_tag: Air.Inst.Tag = switch (order) { diff --git a/src/Zir.zig b/src/Zir.zig index 9ef6d33b86..30edd6d0e1 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -903,7 +903,7 @@ pub const Inst = struct { /// Uses the `pl_node` union field with payload `Select`. select, /// Implements the `@atomicLoad` builtin. - /// Uses the `pl_node` union field with payload `Bin`. + /// Uses the `pl_node` union field with payload `AtomicLoad`. atomic_load, /// Implements the `@atomicRmw` builtin. /// Uses the `pl_node` union field with payload `AtomicRmw`. @@ -3293,6 +3293,12 @@ pub const Inst = struct { ordering: Ref, }; + pub const AtomicLoad = struct { + elem_type: Ref, + ptr: Ref, + ordering: Ref, + }; + pub const MulAdd = struct { mulend1: Ref, mulend2: Ref, diff --git a/src/print_zir.zig b/src/print_zir.zig index ac90c26cf6..a9d0f4690f 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -283,6 +283,7 @@ const Writer = struct { => try self.writeStructInit(stream, inst), .cmpxchg_strong, .cmpxchg_weak => try self.writeCmpxchg(stream, inst), + .atomic_load => try self.writeAtomicLoad(stream, inst), .atomic_store => try self.writeAtomicStore(stream, inst), .atomic_rmw => try self.writeAtomicRmw(stream, inst), .memcpy => try self.writeMemcpy(stream, inst), @@ -351,7 +352,6 @@ const Writer = struct { .offset_of, .splat, .reduce, - .atomic_load, .bitcast, .vector_type, .maximum, @@ -929,6 +929,19 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } + fn writeAtomicLoad(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data; + + try self.writeInstRef(stream, extra.elem_type); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.ptr); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.ordering); + try stream.writeAll(") "); + try self.writeSrc(stream, inst_data.src()); + } + fn writeAtomicStore(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data; diff --git a/src/target.zig b/src/target.zig index 27ed1118db..7818d496eb 100644 --- a/src/target.zig +++ b/src/target.zig @@ -1,5 +1,6 @@ const std = @import("std"); const llvm = @import("codegen/llvm/bindings.zig"); +const Type = @import("type.zig").Type; pub const ArchOsAbi = struct { arch: std.Target.Cpu.Arch, @@ -543,10 +544,28 @@ pub fn needUnwindTables(target: std.Target) bool { return target.os.tag == .windows; } -/// TODO this was ported from stage1 but it does not take into account CPU features, -/// which can affect this value. Audit this! -pub fn largestAtomicBits(target: std.Target) u32 { - return switch (target.cpu.arch) { +pub const AtomicPtrAlignmentError = error{ + FloatTooBig, + IntTooBig, + BadType, +}; + +pub const AtomicPtrAlignmentDiagnostics = struct { + bits: u16 = undefined, + max_bits: u16 = undefined, +}; + +/// If ABI alignment of `ty` is OK for atomic operations, returs 0. +/// Otherwise returns the alignment required on a pointer for the target +/// to perform atomic operations. +pub fn atomicPtrAlignment( + target: std.Target, + ty: Type, + diags: *AtomicPtrAlignmentDiagnostics, +) AtomicPtrAlignmentError!u32 { + // TODO this was ported from stage1 but it does not take into account CPU features, + // which can affect this value. Audit this! + const max_atomic_bits: u16 = switch (target.cpu.arch) { .avr, .msp430, .spu_2, @@ -611,6 +630,47 @@ pub fn largestAtomicBits(target: std.Target) u32 { .x86_64 => 128, }; + + var buffer: Type.Payload.Bits = undefined; + + const int_ty = switch (ty.zigTypeTag()) { + .Int => ty, + .Enum => ty.intTagType(&buffer), + .Float => { + const bit_count = ty.floatBits(target); + if (bit_count > max_atomic_bits) { + diags.* = .{ + .bits = bit_count, + .max_bits = max_atomic_bits, + }; + return error.FloatTooBig; + } + if (target.cpu.arch == .x86_64 and bit_count > 64) { + return 16; + } + return 0; + }, + .Bool => return 0, + else => { + if (ty.isPtrAtRuntime()) return 0; + return error.BadType; + }, + }; + + const bit_count = int_ty.intInfo(target).bits; + if (bit_count > max_atomic_bits) { + diags.* = .{ + .bits = bit_count, + .max_bits = max_atomic_bits, + }; + return error.IntTooBig; + } + + if (target.cpu.arch == .x86_64 and bit_count > 64) { + return 16; + } + + return 0; } pub fn defaultAddressSpace( diff --git a/test/behavior/atomics.zig b/test/behavior/atomics.zig index 71e17d9b4c..62471c5ea0 100644 --- a/test/behavior/atomics.zig +++ b/test/behavior/atomics.zig @@ -127,7 +127,7 @@ test "128-bit cmpxchg" { } fn test_u128_cmpxchg() !void { - var x: u128 = 1234; + var x: u128 align(16) = 1234; if (@cmpxchgWeak(u128, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| { try expect(x1 == 1234); } else { From f21c11a7f7fb84db4289a6f735dea012387606d6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 4 May 2022 15:22:41 -0700 Subject: [PATCH 1348/2031] stage2: change x86_64 max int alignment from 8 to 16 For x86_64, LLVMABIAlignmentOfType(i128) reports 8. However I think 16 is a better number for two reasons: 1. Better machine code when loading into SIMD register. 2. The C ABI wants 16 for extern structs. --- lib/std/target.zig | 7 ++++++- src/target.zig | 9 +-------- test/behavior/align.zig | 5 ++++- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/std/target.zig b/lib/std/target.zig index 777b0f0ec0..aab9d5accc 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1784,7 +1784,6 @@ pub const Target = struct { .armeb, .thumb, .thumbeb, - .x86_64, .hexagon, .mips, .mipsel, @@ -1811,6 +1810,12 @@ pub const Target = struct { .windows => 8, else => 4, }, + + // For x86_64, LLVMABIAlignmentOfType(i128) reports 8. However I think 16 + // is a better number because of two reasons: + // 1. Better machine code when loading into SIMD register. + // 2. The C ABI wants 16 for extern structs. + .x86_64, .aarch64, .aarch64_be, .aarch64_32, diff --git a/src/target.zig b/src/target.zig index 7818d496eb..9249ed1b60 100644 --- a/src/target.zig +++ b/src/target.zig @@ -555,7 +555,7 @@ pub const AtomicPtrAlignmentDiagnostics = struct { max_bits: u16 = undefined, }; -/// If ABI alignment of `ty` is OK for atomic operations, returs 0. +/// If ABI alignment of `ty` is OK for atomic operations, returns 0. /// Otherwise returns the alignment required on a pointer for the target /// to perform atomic operations. pub fn atomicPtrAlignment( @@ -645,9 +645,6 @@ pub fn atomicPtrAlignment( }; return error.FloatTooBig; } - if (target.cpu.arch == .x86_64 and bit_count > 64) { - return 16; - } return 0; }, .Bool => return 0, @@ -666,10 +663,6 @@ pub fn atomicPtrAlignment( return error.IntTooBig; } - if (target.cpu.arch == .x86_64 and bit_count > 64) { - return 16; - } - return 0; } diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 9d7ca9958a..393908d5bd 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -55,6 +55,9 @@ test "alignment of struct with pointer has same alignment as usize" { } test "alignment and size of structs with 128-bit fields" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + const A = struct { x: u128, }; @@ -67,7 +70,6 @@ test "alignment and size of structs with 128-bit fields" { .armeb, .thumb, .thumbeb, - .x86_64, .hexagon, .mips, .mipsel, @@ -128,6 +130,7 @@ test "alignment and size of structs with 128-bit fields" { }, }, + .x86_64, .aarch64, .aarch64_be, .aarch64_32, From 2f6a01d0c39542e7d88c9af14e869b820fd156cc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 4 May 2022 17:31:12 -0700 Subject: [PATCH 1349/2031] stage1: fix `@sizeOf` for 128-bit integer types --- src/stage1/analyze.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp index aef4966ee7..34dff556e0 100644 --- a/src/stage1/analyze.cpp +++ b/src/stage1/analyze.cpp @@ -7686,6 +7686,7 @@ ZigType *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) { // However for some targets, LLVM incorrectly reports this as 8. // See: https://github.com/ziglang/zig/issues/2987 entry->abi_align = 16; + entry->abi_size = align_forward(entry->abi_size, entry->abi_align); } } From af7e945a7dc00a2a5055d9770b9ecda253d64a8e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 4 May 2022 18:45:59 -0700 Subject: [PATCH 1350/2031] stage2: fix `@sizeOf` for structs with comptime fields --- src/type.zig | 2 +- test/behavior/align.zig | 4 ++++ test/behavior/bitcast.zig | 4 ++++ test/behavior/struct.zig | 6 ++++++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/type.zig b/src/type.zig index 44432b95f6..ddeec596e1 100644 --- a/src/type.zig +++ b/src/type.zig @@ -5177,7 +5177,7 @@ pub const Type = extern union { const field = it.struct_obj.fields.values()[it.field]; defer it.field += 1; - if (!field.ty.hasRuntimeBits()) + if (!field.ty.hasRuntimeBits() or field.is_comptime) return FieldOffset{ .field = it.field, .offset = it.offset }; const field_align = field.normalAlignment(it.target); diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 393908d5bd..5a3a76beb2 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -55,6 +55,10 @@ test "alignment of struct with pointer has same alignment as usize" { } test "alignment and size of structs with 128-bit fields" { + if (builtin.zig_backend == .stage1) { + // stage1 gets the wrong answer for a lot of targets + return error.SkipZigTest; + } if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index e74a4c44f4..4922b9473e 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -120,6 +120,10 @@ test "bitcast generates a temporary value" { } test "@bitCast packed structs at runtime and comptime" { + if (builtin.zig_backend == .stage1) { + // stage1 gets the wrong answer for a lot of targets + return error.SkipZigTest; + } if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index ed75268f7d..24365d49b7 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -500,6 +500,10 @@ const Bitfields = packed struct { }; test "packed struct fields are ordered from LSB to MSB" { + if (builtin.zig_backend == .stage1) { + // stage1 gets the wrong answer for a lot of targets + return error.SkipZigTest; + } if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO @@ -971,6 +975,8 @@ test "comptime struct field" { comptime b: i32 = 1234, }; + comptime std.debug.assert(@sizeOf(T) == 4); + var foo: T = undefined; comptime try expect(foo.b == 1234); } From 0bebb688fbc4c6ffb88a2b0aefcf536143bb5f2e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 4 May 2022 19:11:02 -0700 Subject: [PATCH 1351/2031] stage2: change max int align from 8 to 16 for more ISAs These targets now have a similar disagreement with LLVM about the alignment of 128-bit integers as x86_64: * riscv64 * powerpc64 * powerpc64le * mips64 * mips64el * sparcv9 See #2987 --- lib/std/target.zig | 30 +++++++++++++++--------------- test/behavior/align.zig | 12 ++++++------ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/std/target.zig b/lib/std/target.zig index aab9d5accc..2db1415cd3 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1787,18 +1787,12 @@ pub const Target = struct { .hexagon, .mips, .mipsel, - .mips64, - .mips64el, .powerpc, .powerpcle, - .powerpc64, - .powerpc64le, .r600, .amdgcn, .riscv32, - .riscv64, .sparc, - .sparcv9, .sparcel, .s390x, .lanai, @@ -1812,10 +1806,20 @@ pub const Target = struct { }, // For x86_64, LLVMABIAlignmentOfType(i128) reports 8. However I think 16 - // is a better number because of two reasons: + // is a better number for two reasons: // 1. Better machine code when loading into SIMD register. // 2. The C ABI wants 16 for extern structs. + // 3. 16-byte cmpxchg needs 16-byte alignment. + // Same logic for riscv64, powerpc64, mips64, sparcv9. .x86_64, + .riscv64, + .powerpc64, + .powerpc64le, + .mips64, + .mips64el, + .sparcv9, + + // Even LLVMABIAlignmentOfType(i128) agrees on these targets. .aarch64, .aarch64_be, .aarch64_32, @@ -1825,11 +1829,9 @@ pub const Target = struct { .nvptx64, => 16, - // Below this comment are unverified and I have chosen a number - // based on ptrBitWidth. - - .spu_2 => 2, - + // Below this comment are unverified but based on the fact that C requires + // int128_t to be 16 bytes aligned, it's a safe default. + .spu_2, .csky, .arc, .m68k, @@ -1843,8 +1845,6 @@ pub const Target = struct { .renderscript32, .spirv32, .shave, - => 4, - .le64, .amdil64, .hsail64, @@ -1852,7 +1852,7 @@ pub const Target = struct { .renderscript64, .ve, .spirv64, - => 8, + => 16, }; } }; diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 5a3a76beb2..18a55f6653 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -77,18 +77,12 @@ test "alignment and size of structs with 128-bit fields" { .hexagon, .mips, .mipsel, - .mips64, - .mips64el, .powerpc, .powerpcle, - .powerpc64, - .powerpc64le, .r600, .amdgcn, .riscv32, - .riscv64, .sparc, - .sparcv9, .sparcel, .s390x, .lanai, @@ -134,6 +128,12 @@ test "alignment and size of structs with 128-bit fields" { }, }, + .mips64, + .mips64el, + .powerpc64, + .powerpc64le, + .riscv64, + .sparcv9, .x86_64, .aarch64, .aarch64_be, From ba127058d1fcf3ca042794244eee052915998b1a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 4 May 2022 19:15:31 -0700 Subject: [PATCH 1352/2031] CLI: detect MinGW-flavored static libraries Ideally on Windows, static libraries look like "foo.lib". However, CMake and other build systems will unfortunately produce static libraries that instead look like "libfoo.a". This patch makes Zig's CLI resolve "-lfoo" arguments into static libraries that match this other pattern. This patch fixes an issue with zig-bootstrap where it won't find the LLVM, Clang, and LLD libraries. --- src/main.zig | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main.zig b/src/main.zig index 4d69bd51c7..9487f2e962 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2118,6 +2118,25 @@ fn buildOutputType( continue :syslib; } + // Unfortunately, in the case of MinGW we also need to look for `libfoo.a`. + if (target_info.target.isMinGW()) { + for (lib_dirs.items) |lib_dir_path| { + test_path.clearRetainingCapacity(); + try test_path.writer().print("{s}" ++ sep ++ "lib{s}.a", .{ + lib_dir_path, lib_name, + }); + fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { + error.FileNotFound => continue, + else => |e| fatal("unable to search for static library '{s}': {s}", .{ + test_path.items, @errorName(e), + }), + }; + try link_objects.append(.{ .path = try arena.dupe(u8, test_path.items) }); + system_libs.orderedRemoveAt(i); + continue :syslib; + } + } + std.log.scoped(.cli).debug("depending on system for -l{s}", .{lib_name}); i += 1; From 1b432b557608bd047afaad71ffb7bd2ddf23c444 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 4 May 2022 20:38:53 -0700 Subject: [PATCH 1353/2031] stage2: implement global assembly So far it's supported by the LLVM backend only. I recommend for the other backends to wait for the resolution of #10761 before adding support for this feature. --- src/Module.zig | 15 +++++++++++++++ src/Sema.zig | 28 +++++++++++++++++++++++----- src/codegen/llvm.zig | 14 ++++++++++++++ src/codegen/llvm/bindings.zig | 3 +++ test/behavior/asm.zig | 1 - 5 files changed, 55 insertions(+), 6 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 55ec1fdd2c..864663ada2 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -151,6 +151,8 @@ allocated_decls: std.SegmentedList(Decl, 0) = .{}, /// When a Decl object is freed from `allocated_decls`, it is pushed into this stack. decls_free_list: std.ArrayListUnmanaged(Decl.Index) = .{}, +global_assembly: std.AutoHashMapUnmanaged(Decl.Index, []u8) = .{}, + const MonomorphedFuncsSet = std.HashMapUnmanaged( *Fn, void, @@ -2831,6 +2833,7 @@ pub fn deinit(mod: *Module) void { mod.decls_free_list.deinit(gpa); mod.allocated_decls.deinit(gpa); + mod.global_assembly.deinit(gpa); } pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void { @@ -2842,6 +2845,9 @@ pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void { if (decl.deletion_flag) { assert(mod.deletion_set.swapRemove(decl_index)); } + if (mod.global_assembly.fetchRemove(decl_index)) |kv| { + gpa.free(kv.value); + } if (decl.has_tv) { if (decl.getInnerNamespace()) |namespace| { namespace.destroyDecls(mod); @@ -5714,3 +5720,12 @@ pub fn markDeclAlive(mod: *Module, decl: *Decl) void { fn markDeclIndexAlive(mod: *Module, decl_index: Decl.Index) void { return mod.markDeclAlive(mod.declPtr(decl_index)); } + +pub fn addGlobalAssembly(mod: *Module, decl_index: Decl.Index, source: []const u8) !void { + try mod.global_assembly.ensureUnusedCapacity(mod.gpa, 1); + + const duped_source = try mod.gpa.dupe(u8, source); + errdefer mod.gpa.free(duped_source); + + mod.global_assembly.putAssumeCapacityNoClobber(decl_index, duped_source); +} diff --git a/src/Sema.zig b/src/Sema.zig index 8125fc0cc1..9b4977c616 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -10517,16 +10517,35 @@ fn zirAsm( const is_volatile = @truncate(u1, extended.small >> 15) != 0; const is_global_assembly = sema.func == null; - if (block.is_comptime and !is_global_assembly) { - try sema.requireRuntimeBlock(block, src); - } - if (extra.data.asm_source == 0) { // This can move to become an AstGen error after inline assembly improvements land // and stage1 code matches stage2 code. return sema.fail(block, src, "assembly code must use string literal syntax", .{}); } + const asm_source = sema.code.nullTerminatedString(extra.data.asm_source); + + if (is_global_assembly) { + if (outputs_len != 0) { + return sema.fail(block, src, "module-level assembly does not support outputs", .{}); + } + if (inputs_len != 0) { + return sema.fail(block, src, "module-level assembly does not support inputs", .{}); + } + if (clobbers_len != 0) { + return sema.fail(block, src, "module-level assembly does not support clobbers", .{}); + } + if (is_volatile) { + return sema.fail(block, src, "volatile keyword is redundant on module-level assembly", .{}); + } + try sema.mod.addGlobalAssembly(sema.owner_decl_index, asm_source); + return Air.Inst.Ref.void_value; + } + + if (block.is_comptime) { + try sema.requireRuntimeBlock(block, src); + } + if (outputs_len > 1) { return sema.fail(block, src, "TODO implement Sema for asm with more than 1 output", .{}); } @@ -10591,7 +10610,6 @@ fn zirAsm( needed_capacity += name.*.len / 4 + 1; } - const asm_source = sema.code.nullTerminatedString(extra.data.asm_source); needed_capacity += (asm_source.len + 3) / 4; const gpa = sema.gpa; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 068067d765..65289b1a8c 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -476,6 +476,19 @@ pub const Object = struct { _ = builder.buildRet(is_lt); } + fn genModuleLevelAssembly(object: *Object, comp: *Compilation) !void { + const mod = comp.bin_file.options.module.?; + if (mod.global_assembly.count() == 0) return; + var buffer = std.ArrayList(u8).init(comp.gpa); + defer buffer.deinit(); + var it = mod.global_assembly.iterator(); + while (it.next()) |kv| { + try buffer.appendSlice(kv.value_ptr.*); + try buffer.append('\n'); + } + object.llvm_module.setModuleInlineAsm2(buffer.items.ptr, buffer.items.len - 1); + } + pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void { var sub_prog_node = prog_node.start("LLVM Emit Object", 0); sub_prog_node.activate(); @@ -484,6 +497,7 @@ pub const Object = struct { try self.genErrorNameTable(comp); try self.genCmpLtErrorsLenFunction(comp); + try self.genModuleLevelAssembly(comp); if (self.di_builder) |dib| { // When lowering debug info for pointers, we emitted the element types as diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 560b9544dd..4732e0dd49 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -381,6 +381,9 @@ pub const Module = opaque { pub const createDIBuilder = ZigLLVMCreateDIBuilder; extern fn ZigLLVMCreateDIBuilder(module: *const Module, allow_unresolved: bool) *DIBuilder; + + pub const setModuleInlineAsm2 = LLVMSetModuleInlineAsm2; + extern fn LLVMSetModuleInlineAsm2(M: *const Module, Asm: [*]const u8, Len: usize) void; }; pub const lookupIntrinsicID = LLVMLookupIntrinsicID; diff --git a/test/behavior/asm.zig b/test/behavior/asm.zig index dab6f12127..9aa95a3a0b 100644 --- a/test/behavior/asm.zig +++ b/test/behavior/asm.zig @@ -23,7 +23,6 @@ test "module level assembly" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO if (is_x86_64_linux) { try expect(this_is_my_alias() == 1234); From 17fc44dd1287163c25832c40f7e81cd5532e52bd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 4 May 2022 21:10:03 -0700 Subject: [PATCH 1354/2031] LLVM: fix x86_64 sysv C ABI for extern structs with sub-64 bit integers --- src/codegen/llvm.zig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 65289b1a8c..c0c576cdf1 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -8030,7 +8030,8 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. } } if (classes[0] == .integer and classes[1] == .none) { - return llvm_types_buffer[0]; + const abi_size = fn_info.return_type.abiSize(target); + return dg.context.intType(@intCast(c_uint, abi_size * 8)); } return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False); }, @@ -8124,7 +8125,8 @@ fn lowerFnParamTy(dg: *DeclGen, cc: std.builtin.CallingConvention, ty: Type) !*c } } if (classes[0] == .integer and classes[1] == .none) { - return llvm_types_buffer[0]; + const abi_size = ty.abiSize(target); + return dg.context.intType(@intCast(c_uint, abi_size * 8)); } return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False); }, From 44252f4d352d53afd86d678c0b0a40b3f681c7eb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 4 May 2022 22:57:57 -0700 Subject: [PATCH 1355/2031] LLVM: fix C ABI for windows * sret logic needed a check for hasRuntimeBits() * lower f128 on windows targets with the "sse" class rather than "memory". For reference, clang emits a compile error when __float128 is used with the MSVC ABI, saying that this type is not supported. The docs for the x64 calling convention have both of these sentences: - "Any argument that doesn't fit in 8 bytes, or isn't 1, 2, 4, or 8 bytes, must be passed by reference." - "All floating point operations are done using the 16 XMM registers." * For i128, however, it is clear that the Windows calling convention wants such an object to be passed by reference. I fixed the LLVM lowering for function parameters to make this work. --- src/arch/x86_64/abi.zig | 17 +++++++++-------- src/codegen/llvm.zig | 27 ++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index f388c5c93a..da2e3da394 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -12,13 +12,10 @@ pub fn classifyWindows(ty: Type, target: Target) Class { // and the registers used for those arguments. Any argument that doesn't fit in 8 // bytes, or isn't 1, 2, 4, or 8 bytes, must be passed by reference. A single argument // is never spread across multiple registers." + // "All floating point operations are done using the 16 XMM registers." // "Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed // as if they were integers of the same size." - switch (ty.abiSize(target)) { - 1, 2, 4, 8 => {}, - else => return .memory, - } - return switch (ty.zigTypeTag()) { + switch (ty.zigTypeTag()) { .Pointer, .Int, .Bool, @@ -33,9 +30,13 @@ pub fn classifyWindows(ty: Type, target: Target) Class { .ErrorUnion, .AnyFrame, .Frame, - => .integer, + => switch (ty.abiSize(target)) { + 0 => unreachable, + 1, 2, 4, 8 => return .integer, + else => return .memory, + }, - .Float, .Vector => .sse, + .Float, .Vector => return .sse, .Type, .ComptimeFloat, @@ -47,7 +48,7 @@ pub fn classifyWindows(ty: Type, target: Target) Class { .Opaque, .EnumLiteral, => unreachable, - }; + } } /// There are a maximum of 8 possible return slots. Returned values are in diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index c0c576cdf1..426ef7c378 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -644,7 +644,17 @@ pub const Object = struct { if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; const llvm_arg_i = @intCast(c_uint, args.items.len) + param_offset; - try args.append(llvm_func.getParam(llvm_arg_i)); + const param = llvm_func.getParam(llvm_arg_i); + // It is possible for the calling convention to make the argument's by-reference nature + // disagree with our canonical value for it, in which case we must dereference here. + const need_deref = !param_ty.isPtrAtRuntime() and !isByRef(param_ty) and + (param.typeOf().getTypeKind() == .Pointer); + const loaded_param = if (!need_deref) param else l: { + const load_inst = builder.buildLoad(param, ""); + load_inst.setAlignment(param_ty.abiAlignment(target)); + break :l load_inst; + }; + try args.append(loaded_param); } var di_file: ?*llvm.DIFile = null; @@ -3743,6 +3753,19 @@ pub const FuncGen = struct { arg_ptr.setAlignment(alignment); const store_inst = self.builder.buildStore(llvm_arg, arg_ptr); store_inst.setAlignment(alignment); + + if (abi_llvm_ty.getTypeKind() == .Pointer) { + // In this case, the calling convention wants a pointer, but + // we have a value. + if (arg_ptr.typeOf() == abi_llvm_ty) { + try llvm_args.append(arg_ptr); + continue; + } + const casted_ptr = self.builder.buildBitCast(arg_ptr, abi_llvm_ty, ""); + try llvm_args.append(casted_ptr); + continue; + } + break :p self.builder.buildBitCast(arg_ptr, ptr_abi_ty, ""); }; @@ -7931,6 +7954,8 @@ fn llvmFieldIndex( } fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool { + if (!fn_info.return_type.hasRuntimeBitsIgnoreComptime()) return false; + switch (fn_info.cc) { .Unspecified, .Inline => return isByRef(fn_info.return_type), .C => switch (target.cpu.arch) { From 49a7ceb5bcd87d8aeb7b25f8b2972ad4ec0b9e24 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 4 May 2022 19:26:33 +0200 Subject: [PATCH 1356/2031] cmake: add /usr/lib/llvm13 to searched paths Alpine linux installs llvm to this path and currently patches zig's cmake file in order to build zig from source. https://git.alpinelinux.org/aports/tree/testing/zig/llvm-include.patch?id=0c3f7850bef38fb4c63fc6af5c14724e5311b0cc --- cmake/Findllvm.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/Findllvm.cmake b/cmake/Findllvm.cmake index 257a406b66..62c31f282c 100644 --- a/cmake/Findllvm.cmake +++ b/cmake/Findllvm.cmake @@ -12,6 +12,7 @@ find_path(LLVM_INCLUDE_DIRS NAMES llvm/IR/IRBuilder.h /usr/lib/llvm/13/include /usr/lib/llvm-13/include /usr/lib/llvm-13.0/include + /usr/lib/llvm13/include /usr/local/llvm13/include /usr/local/llvm130/include /usr/local/opt/llvm@13/include @@ -31,6 +32,7 @@ if(ZIG_PREFER_CLANG_CPP_DYLIB) /usr/lib/llvm/13/lib /usr/lib/llvm/13/lib64 /usr/lib/llvm-13/lib + /usr/lib/llvm13/lib /usr/local/llvm13/lib /usr/local/llvm130/lib /usr/local/opt/llvm@13/lib From b6d7f63f34bbdb7c67abecbcd47389d2a3d28743 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 20 Apr 2022 21:16:17 +0700 Subject: [PATCH 1357/2031] stage2: sparcv9: Implement jmpl lowering --- src/arch/sparcv9/CodeGen.zig | 20 ++++++++++++++++++-- src/arch/sparcv9/Emit.zig | 5 +++-- src/arch/sparcv9/Mir.zig | 12 +----------- src/arch/sparcv9/bits.zig | 16 +++++++++++++--- 4 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 7d93916fc1..ffd1dfde86 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -1,5 +1,7 @@ //! SPARCv9 codegen. //! This lowers AIR into MIR. +//! For now this only implements medium/low code model with absolute addressing. +//! TODO add support for other code models. const std = @import("std"); const assert = std.debug.assert; const log = std.log.scoped(.codegen); @@ -884,7 +886,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. _ = try self.addInst(.{ .tag = .jmpl, - .data = .{ .branch_link_indirect = .{ .reg = .o7 } }, + .data = .{ + .arithmetic_3op = .{ + .is_imm = false, + .rd = .o7, + .rs1 = .o7, + .rs2_or_imm = .{ .rs2 = .g0 }, + }, + }, }); } else if (func_value.castTag(.extern_fn)) |_| { return self.fail("TODO implement calling extern functions", .{}); @@ -899,7 +908,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. _ = try self.addInst(.{ .tag = .jmpl, - .data = .{ .branch_link_indirect = .{ .reg = .o7 } }, + .data = .{ + .arithmetic_3op = .{ + .is_imm = false, + .rd = .o7, + .rs1 = .o7, + .rs2_or_imm = .{ .rs2 = .g0 }, + }, + }, }); } diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index b811a3567f..a136c622e7 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -56,8 +56,7 @@ pub fn emitMir( .call => @panic("TODO implement sparcv9 call"), - .jmpl => @panic("TODO implement sparcv9 jmpl"), - .jmpl_i => @panic("TODO implement sparcv9 jmpl to reg"), + .jmpl => try emit.mirArithmetic3Op(inst), .ldub => try emit.mirArithmetic3Op(inst), .lduh => try emit.mirArithmetic3Op(inst), @@ -163,6 +162,7 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { const imm = data.rs2_or_imm.imm; switch (tag) { .add => try emit.writeInstruction(Instruction.add(i13, rs1, imm, rd)), + .jmpl => try emit.writeInstruction(Instruction.jmpl(i13, rs1, imm, rd)), .ldub => try emit.writeInstruction(Instruction.ldub(i13, rs1, imm, rd)), .lduh => try emit.writeInstruction(Instruction.lduh(i13, rs1, imm, rd)), .lduw => try emit.writeInstruction(Instruction.lduw(i13, rs1, imm, rd)), @@ -177,6 +177,7 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { const rs2 = data.rs2_or_imm.rs2; switch (tag) { .add => try emit.writeInstruction(Instruction.add(Register, rs1, rs2, rd)), + .jmpl => try emit.writeInstruction(Instruction.jmpl(Register, rs1, rs2, rd)), .ldub => try emit.writeInstruction(Instruction.ldub(Register, rs1, rs2, rd)), .lduh => try emit.writeInstruction(Instruction.lduh(Register, rs1, rs2, rd)), .lduw => try emit.writeInstruction(Instruction.lduw(Register, rs1, rs2, rd)), diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index c79ebdcac1..21f59322cd 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -54,11 +54,8 @@ pub const Inst = struct { call, /// A.24 Jump and Link - /// jmpl (far direct jump) uses the branch_link field, - /// while jmpl_i (indirect jump) uses the branch_link_indirect field. - /// Those two MIR instructions will be lowered into SPARCv9 jmpl instruction. + /// It uses the arithmetic_3op field. jmpl, - jmpl_i, /// A.27 Load Integer /// Those uses the arithmetic_3op field. @@ -167,13 +164,6 @@ pub const Inst = struct { link: Register = .o7, }, - /// Indirect branch and link (always unconditional). - /// Used by e.g. jmpl_i - branch_link_indirect: struct { - reg: Register, - link: Register = .o7, - }, - /// Branch with prediction. /// Used by e.g. bpcc branch_predict: struct { diff --git a/src/arch/sparcv9/bits.zig b/src/arch/sparcv9/bits.zig index bc8b8822b7..2b50e68d32 100644 --- a/src/arch/sparcv9/bits.zig +++ b/src/arch/sparcv9/bits.zig @@ -271,6 +271,8 @@ pub const Instruction = union(enum) { rd: u5, op3: u6, rs1: u5, + // See Errata 58 of SPARCv9 specification + // https://sparc.org/errata-for-v9/#58 i: u1 = 0b1, reserved: u8 = 0b00000000, rs2: u5, @@ -977,10 +979,10 @@ pub const Instruction = union(enum) { }; } - pub fn @"or"(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + pub fn jmpl(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { return switch (s2) { - Register => format3a(0b10, 0b00_0010, rs1, rs2, rd), - i13 => format3b(0b10, 0b00_0010, rs1, rs2, rd), + Register => format3a(0b10, 0b11_1000, rs1, rs2, rd), + i13 => format3b(0b10, 0b11_1000, rs1, rs2, rd), else => unreachable, }; } @@ -1017,6 +1019,14 @@ pub const Instruction = union(enum) { }; } + pub fn @"or"(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b10, 0b00_0010, rs1, rs2, rd), + i13 => format3b(0b10, 0b00_0010, rs1, rs2, rd), + else => unreachable, + }; + } + pub fn nop() Instruction { return sethi(0, .g0); } From a00d69ea4a8c6e21ca99daa10d3b324609f3ef24 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 21 Apr 2022 04:52:50 +0700 Subject: [PATCH 1358/2031] stage2: sparcv9: Implement basic stack load/stores --- src/arch/sparcv9/CodeGen.zig | 74 +++++++++++++++++++++++++++++++++++- src/arch/sparcv9/Emit.zig | 13 +++++++ src/arch/sparcv9/Mir.zig | 10 +++++ src/arch/sparcv9/abi.zig | 5 +++ src/arch/sparcv9/bits.zig | 32 ++++++++++++++++ 5 files changed, 132 insertions(+), 2 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index ffd1dfde86..ca31297888 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -1351,7 +1351,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void try self.genLoad(reg, reg, i13, 0, ty.abiSize(self.target.*)); }, .stack_offset => |off| { - const simm13 = math.cast(u12, off) catch + const biased_offset = off + abi.stack_bias; + const simm13 = math.cast(i13, biased_offset) catch return self.fail("TODO larger stack offsets", .{}); try self.genLoad(reg, .sp, i13, simm13, ty.abiSize(self.target.*)); }, @@ -1381,11 +1382,80 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); }, - .register => return self.fail("TODO implement storing types abi_size={}", .{abi_size}), + .register => |reg| { + const biased_offset = stack_offset + abi.stack_bias; + const simm13 = math.cast(i13, biased_offset) catch + return self.fail("TODO larger stack offsets", .{}); + return self.genStore(reg, .sp, i13, simm13, abi_size); + }, .memory, .stack_offset => return self.fail("TODO implement memcpy", .{}), } } +fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_type: type, off: off_type, abi_size: u64) !void { + assert(off_type == Register or off_type == i13); + + const is_imm = (off_type == i13); + const rs2_or_imm = if (is_imm) .{ .imm = off } else .{ .rs2 = off }; + + switch (abi_size) { + 1 => { + _ = try self.addInst(.{ + .tag = .stb, + .data = .{ + .arithmetic_3op = .{ + .is_imm = is_imm, + .rd = value_reg, + .rs1 = addr_reg, + .rs2_or_imm = rs2_or_imm, + }, + }, + }); + }, + 2 => { + _ = try self.addInst(.{ + .tag = .sth, + .data = .{ + .arithmetic_3op = .{ + .is_imm = is_imm, + .rd = value_reg, + .rs1 = addr_reg, + .rs2_or_imm = rs2_or_imm, + }, + }, + }); + }, + 4 => { + _ = try self.addInst(.{ + .tag = .stw, + .data = .{ + .arithmetic_3op = .{ + .is_imm = is_imm, + .rd = value_reg, + .rs1 = addr_reg, + .rs2_or_imm = rs2_or_imm, + }, + }, + }); + }, + 8 => { + _ = try self.addInst(.{ + .tag = .stx, + .data = .{ + .arithmetic_3op = .{ + .is_imm = is_imm, + .rd = value_reg, + .rs1 = addr_reg, + .rs2_or_imm = rs2_or_imm, + }, + }, + }); + }, + 3, 5, 6, 7 => return self.fail("TODO: genLoad for more abi_sizes", .{}), + else => unreachable, + } +} + fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { if (typed_value.val.isUndef()) return MCValue{ .undef = {} }; diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index a136c622e7..91dcb4fd5f 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -76,6 +76,11 @@ pub fn emitMir( .sllx => @panic("TODO implement sparcv9 sllx"), + .stb => try emit.mirArithmetic3Op(inst), + .sth => try emit.mirArithmetic3Op(inst), + .stw => try emit.mirArithmetic3Op(inst), + .stx => try emit.mirArithmetic3Op(inst), + .sub => try emit.mirArithmetic3Op(inst), .tcc => try emit.mirTrap(inst), @@ -170,6 +175,10 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { .@"or" => try emit.writeInstruction(Instruction.@"or"(i13, rs1, imm, rd)), .save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)), .restore => try emit.writeInstruction(Instruction.restore(i13, rs1, imm, rd)), + .stb => try emit.writeInstruction(Instruction.stb(i13, rs1, imm, rd)), + .sth => try emit.writeInstruction(Instruction.sth(i13, rs1, imm, rd)), + .stw => try emit.writeInstruction(Instruction.stw(i13, rs1, imm, rd)), + .stx => try emit.writeInstruction(Instruction.stx(i13, rs1, imm, rd)), .sub => try emit.writeInstruction(Instruction.sub(i13, rs1, imm, rd)), else => unreachable, } @@ -185,6 +194,10 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { .@"or" => try emit.writeInstruction(Instruction.@"or"(Register, rs1, rs2, rd)), .save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)), .restore => try emit.writeInstruction(Instruction.restore(Register, rs1, rs2, rd)), + .stb => try emit.writeInstruction(Instruction.stb(Register, rs1, rs2, rd)), + .sth => try emit.writeInstruction(Instruction.sth(Register, rs1, rs2, rd)), + .stw => try emit.writeInstruction(Instruction.stw(Register, rs1, rs2, rd)), + .stx => try emit.writeInstruction(Instruction.stx(Register, rs1, rs2, rd)), .sub => try emit.writeInstruction(Instruction.sub(Register, rs1, rs2, rd)), else => unreachable, } diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index 21f59322cd..54f147f415 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -94,6 +94,16 @@ pub const Inst = struct { // TODO add other operations. sllx, + /// A.54 Store Integer + /// This uses the arithmetic_3op field. + /// Note that the std variant of this instruction is deprecated, so do not emit + /// it unless specifically requested (e.g. by inline assembly). + // TODO add other operations. + stb, + sth, + stw, + stx, + /// A.56 Subtract /// Those uses the arithmetic_3op field. // TODO add other operations. diff --git a/src/arch/sparcv9/abi.zig b/src/arch/sparcv9/abi.zig index 4cb10a99ea..ae72f270be 100644 --- a/src/arch/sparcv9/abi.zig +++ b/src/arch/sparcv9/abi.zig @@ -1,6 +1,11 @@ const bits = @import("bits.zig"); const Register = bits.Register; +// On SPARCv9, %sp points to top of stack + stack bias, +// and %fp points to top of previous frame + stack bias. +// See: Registers and the Stack Frame, page 3P-8, SCD 2.4.1. +pub const stack_bias = 2047; + // There are no callee-preserved registers since the windowing // mechanism already takes care of them. // We still need to preserve %o0-%o5, %g1, %g4, and %g5 before calling diff --git a/src/arch/sparcv9/bits.zig b/src/arch/sparcv9/bits.zig index 2b50e68d32..e66b24f617 100644 --- a/src/arch/sparcv9/bits.zig +++ b/src/arch/sparcv9/bits.zig @@ -1059,6 +1059,38 @@ pub const Instruction = union(enum) { return format2a(0b100, imm, rd); } + pub fn stb(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b11, 0b00_0101, rs1, rs2, rd), + i13 => format3b(0b11, 0b00_0101, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn sth(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b11, 0b00_0110, rs1, rs2, rd), + i13 => format3b(0b11, 0b00_0110, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn stw(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b11, 0b00_0100, rs1, rs2, rd), + i13 => format3b(0b11, 0b00_0100, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn stx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b11, 0b00_1110, rs1, rs2, rd), + i13 => format3b(0b11, 0b00_1110, rs1, rs2, rd), + else => unreachable, + }; + } + pub fn sub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { return switch (s2) { Register => format3a(0b10, 0b00_0100, rs1, rs2, rd), From e03ec51b4b47c6730462bed3f8d703767a7b33d9 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 21 Apr 2022 05:29:15 +0700 Subject: [PATCH 1359/2031] stage2: sparcv9: Pad branch delay slots with nops --- src/arch/sparcv9/CodeGen.zig | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index ca31297888..240b7e3a55 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -394,7 +394,11 @@ fn gen(self: *Self) !void { }, }); - // TODO Find a way to fill this slot + // Branches in SPARC have a delay slot, that is, the instruction + // following it will unconditionally be executed. + // See: Section 3.2.3 Control Transfer in SPARCv9 manual. + // See also: https://arcb.csc.ncsu.edu/~mueller/codeopt/codeopt00/notes/delaybra.html + // TODO Find a way to fill this delay slot // nop _ = try self.addInst(.{ .tag = .nop, @@ -895,6 +899,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. }, }, }); + + // TODO Find a way to fill this delay slot + _ = try self.addInst(.{ + .tag = .nop, + .data = .{ .nop = {} }, + }); } else if (func_value.castTag(.extern_fn)) |_| { return self.fail("TODO implement calling extern functions", .{}); } else { @@ -917,6 +927,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. }, }, }); + + // TODO Find a way to fill this delay slot + _ = try self.addInst(.{ + .tag = .nop, + .data = .{ .nop = {} }, + }); } const result = info.return_value; From f6bf3dd78c7bbb139e64ddfa05995f3896366d77 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 21 Apr 2022 06:02:53 +0700 Subject: [PATCH 1360/2031] stage2: sparcv9: Fix stack space accounting --- src/arch/sparcv9/CodeGen.zig | 37 ++++++++++++++++++++++++++++-------- src/arch/sparcv9/abi.zig | 9 ++++++++- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 240b7e3a55..3715c56558 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -340,16 +340,15 @@ fn gen(self: *Self) !void { if (cc != .Naked) { // TODO Finish function prologue and epilogue for sparcv9. - // TODO Backpatch stack offset - // save %sp, -176, %sp - _ = try self.addInst(.{ + // save %sp, stack_save_area, %sp + const save_inst = try self.addInst(.{ .tag = .save, .data = .{ .arithmetic_3op = .{ .is_imm = true, .rd = .sp, .rs1 = .sp, - .rs2_or_imm = .{ .imm = -176 }, + .rs2_or_imm = .{ .imm = -abi.stack_save_area }, }, }, }); @@ -382,6 +381,28 @@ fn gen(self: *Self) !void { return self.fail("TODO add branches in sparcv9", .{}); } + // Backpatch stack offset + const total_stack_size = self.max_end_stack + abi.stack_save_area; // TODO + self.saved_regs_stack_space; + const stack_size = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align); + if (math.cast(i13, stack_size)) |size| { + self.mir_instructions.set(save_inst, .{ + .tag = .save, + .data = .{ + .arithmetic_3op = .{ + .is_imm = true, + .rd = .sp, + .rs1 = .sp, + .rs2_or_imm = .{ .imm = -size }, + }, + }, + }); + } else |_| { + // TODO for large stacks, replace the prologue with: + // setx stack_size, %g1 + // save %sp, %g1, %sp + return self.fail("TODO SPARCv9: allow larger stacks", .{}); + } + // return %i7 + 8 _ = try self.addInst(.{ .tag = .@"return", @@ -1367,8 +1388,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void try self.genLoad(reg, reg, i13, 0, ty.abiSize(self.target.*)); }, .stack_offset => |off| { - const biased_offset = off + abi.stack_bias; - const simm13 = math.cast(i13, biased_offset) catch + const real_offset = off + abi.stack_bias + abi.stack_save_area; + const simm13 = math.cast(i13, real_offset) catch return self.fail("TODO larger stack offsets", .{}); try self.genLoad(reg, .sp, i13, simm13, ty.abiSize(self.target.*)); }, @@ -1399,8 +1420,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); }, .register => |reg| { - const biased_offset = stack_offset + abi.stack_bias; - const simm13 = math.cast(i13, biased_offset) catch + const real_offset = stack_offset + abi.stack_bias + abi.stack_save_area; + const simm13 = math.cast(i13, real_offset) catch return self.fail("TODO larger stack offsets", .{}); return self.genStore(reg, .sp, i13, simm13, abi_size); }, diff --git a/src/arch/sparcv9/abi.zig b/src/arch/sparcv9/abi.zig index ae72f270be..a9001c7dc7 100644 --- a/src/arch/sparcv9/abi.zig +++ b/src/arch/sparcv9/abi.zig @@ -1,11 +1,18 @@ const bits = @import("bits.zig"); const Register = bits.Register; +// SPARCv9 stack constants. +// See: Registers and the Stack Frame, page 3P-8, SCD 2.4.1. + // On SPARCv9, %sp points to top of stack + stack bias, // and %fp points to top of previous frame + stack bias. -// See: Registers and the Stack Frame, page 3P-8, SCD 2.4.1. pub const stack_bias = 2047; +// The first 176 bytes of the stack is reserved for register saving purposes. +// SPARCv9 requires to reserve space in the stack for the first six arguments, +// even though they are usually passed in registers. +pub const stack_save_area = 176; + // There are no callee-preserved registers since the windowing // mechanism already takes care of them. // We still need to preserve %o0-%o5, %g1, %g4, and %g5 before calling From ae201807f52077709b46ab7d2adfe5178ce138af Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 21 Apr 2022 06:06:58 +0700 Subject: [PATCH 1361/2031] stage2: sparcv9: Simplify genLoad/genStore --- src/arch/sparcv9/CodeGen.zig | 102 +++++++---------------------------- 1 file changed, 20 insertions(+), 82 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 3715c56558..b435b1885c 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -1185,48 +1185,17 @@ fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_ty const rs2_or_imm = if (is_imm) .{ .imm = off } else .{ .rs2 = off }; switch (abi_size) { - 1 => { + 1, 2, 4, 8 => { + const tag: Mir.Inst.Tag = switch (abi_size) { + 1 => .ldub, + 2 => .lduh, + 4 => .lduw, + 8 => .ldx, + else => unreachable, // unexpected abi size + }; + _ = try self.addInst(.{ - .tag = .ldub, - .data = .{ - .arithmetic_3op = .{ - .is_imm = is_imm, - .rd = value_reg, - .rs1 = addr_reg, - .rs2_or_imm = rs2_or_imm, - }, - }, - }); - }, - 2 => { - _ = try self.addInst(.{ - .tag = .lduh, - .data = .{ - .arithmetic_3op = .{ - .is_imm = is_imm, - .rd = value_reg, - .rs1 = addr_reg, - .rs2_or_imm = rs2_or_imm, - }, - }, - }); - }, - 4 => { - _ = try self.addInst(.{ - .tag = .lduw, - .data = .{ - .arithmetic_3op = .{ - .is_imm = is_imm, - .rd = value_reg, - .rs1 = addr_reg, - .rs2_or_imm = rs2_or_imm, - }, - }, - }); - }, - 8 => { - _ = try self.addInst(.{ - .tag = .ldx, + .tag = tag, .data = .{ .arithmetic_3op = .{ .is_imm = is_imm, @@ -1436,48 +1405,17 @@ fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_t const rs2_or_imm = if (is_imm) .{ .imm = off } else .{ .rs2 = off }; switch (abi_size) { - 1 => { + 1, 2, 4, 8 => { + const tag: Mir.Inst.Tag = switch (abi_size) { + 1 => .stb, + 2 => .sth, + 4 => .stw, + 8 => .stx, + else => unreachable, // unexpected abi size + }; + _ = try self.addInst(.{ - .tag = .stb, - .data = .{ - .arithmetic_3op = .{ - .is_imm = is_imm, - .rd = value_reg, - .rs1 = addr_reg, - .rs2_or_imm = rs2_or_imm, - }, - }, - }); - }, - 2 => { - _ = try self.addInst(.{ - .tag = .sth, - .data = .{ - .arithmetic_3op = .{ - .is_imm = is_imm, - .rd = value_reg, - .rs1 = addr_reg, - .rs2_or_imm = rs2_or_imm, - }, - }, - }); - }, - 4 => { - _ = try self.addInst(.{ - .tag = .stw, - .data = .{ - .arithmetic_3op = .{ - .is_imm = is_imm, - .rd = value_reg, - .rs1 = addr_reg, - .rs2_or_imm = rs2_or_imm, - }, - }, - }); - }, - 8 => { - _ = try self.addInst(.{ - .tag = .stx, + .tag = tag, .data = .{ .arithmetic_3op = .{ .is_imm = is_imm, From e76d52c74dbb7566af75baf7184b04f131d26d5f Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 21 Apr 2022 20:02:27 +0700 Subject: [PATCH 1362/2031] stage2: sparcv9: Remove dbg_arg instruction --- src/arch/sparcv9/CodeGen.zig | 12 ------------ src/arch/sparcv9/Emit.zig | 12 ------------ src/arch/sparcv9/Mir.zig | 15 +-------------- 3 files changed, 1 insertion(+), 38 deletions(-) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index b435b1885c..6176be73a3 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -780,16 +780,6 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { // TODO Copy registers to the stack const mcv = result; - _ = try self.addInst(.{ - .tag = .dbg_arg, - .data = .{ - .dbg_arg_info = .{ - .air_inst = inst, - .arg_index = arg_index, - }, - }, - }); - if (self.liveness.isUnused(inst)) return self.finishAirBookkeeping(); @@ -1050,9 +1040,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { const gpa = self.gpa; - try self.mir_instructions.ensureUnusedCapacity(gpa, 1); - const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len); self.mir_instructions.appendAssumeCapacity(inst); return result_index; diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index 91dcb4fd5f..aea25f2ffc 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -45,7 +45,6 @@ pub fn emitMir( for (mir_tags) |tag, index| { const inst = @intCast(u32, index); switch (tag) { - .dbg_arg => try emit.mirDbgArg(inst), .dbg_line => try emit.mirDbgLine(inst), .dbg_prologue_end => try emit.mirDebugPrologueEnd(), .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(), @@ -92,17 +91,6 @@ pub fn deinit(emit: *Emit) void { emit.* = undefined; } -fn mirDbgArg(emit: *Emit, inst: Mir.Inst.Index) !void { - const tag = emit.mir.instructions.items(.tag)[inst]; - const dbg_arg_info = emit.mir.instructions.items(.data)[inst].dbg_arg_info; - _ = dbg_arg_info; - - switch (tag) { - .dbg_arg => {}, // TODO try emit.genArgDbgInfo(dbg_arg_info.air_inst, dbg_arg_info.arg_index), - else => unreachable, - } -} - fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column; diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index 54f147f415..8916eafe80 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -28,8 +28,6 @@ pub const Inst = struct { data: Data, pub const Tag = enum(u16) { - /// Pseudo-instruction: Argument - dbg_arg, /// Pseudo-instruction: End of prologue dbg_prologue_end, /// Pseudo-instruction: Beginning of epilogue @@ -122,14 +120,6 @@ pub const Inst = struct { /// how to interpret the data within. // TODO this is a quick-n-dirty solution that needs to be cleaned up. pub const Data = union { - /// Debug info: argument - /// - /// Used by e.g. dbg_arg - dbg_arg_info: struct { - air_inst: Air.Inst.Index, - arg_index: usize, - }, - /// Debug info: line and column /// /// Used by e.g. dbg_line @@ -234,10 +224,7 @@ pub const Inst = struct { // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks. comptime { if (builtin.mode != .Debug) { - // TODO clean up the definition of Data before enabling this. - // I'll do that after the PoC backend can produce usable binaries. - - // assert(@sizeOf(Data) == 8); + assert(@sizeOf(Data) == 8); } } }; From c73eb00727e70e6d5224b371b5d0dc4ac6af4dd3 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 21 Apr 2022 20:17:41 +0700 Subject: [PATCH 1363/2031] stage2: sparcv9: Add debug info generation for args --- src/arch/sparcv9/CodeGen.zig | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig index 6176be73a3..35d9a78dcd 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparcv9/CodeGen.zig @@ -780,6 +780,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { // TODO Copy registers to the stack const mcv = result; + try self.genArgDbgInfo(inst, mcv, @intCast(u32, arg_index)); + if (self.liveness.isUnused(inst)) return self.finishAirBookkeeping(); @@ -1038,6 +1040,26 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { // Common helper functions +/// Adds a Type to the .debug_info at the current position. The bytes will be populated later, +/// after codegen for this symbol is done. +fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { + switch (self.debug_output) { + .dwarf => |dw| { + assert(ty.hasRuntimeBits()); + const dbg_info = &dw.dbg_info; + const index = dbg_info.items.len; + try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 + const mod = self.bin_file.options.module.?; + const atom = switch (self.bin_file.tag) { + .elf => &mod.declPtr(self.mod_fn.owner_decl).link.elf.dbg_info_atom, + else => unreachable, + }; + try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); + }, + else => {}, + } +} + fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { const gpa = self.gpa; try self.mir_instructions.ensureUnusedCapacity(gpa, 1); @@ -1166,6 +1188,40 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live self.finishAirBookkeeping(); } +fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32) !void { + const ty = self.air.instructions.items(.data)[inst].ty; + const name = self.mod_fn.getParamName(arg_index); + const name_with_null = name.ptr[0 .. name.len + 1]; + + switch (mcv) { + .register => |reg| { + switch (self.debug_output) { + .dwarf => |dw| { + const dbg_info = &dw.dbg_info; + try dbg_info.ensureUnusedCapacity(3); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1, // ULEB128 dwarf expression length + reg.dwarfLocOp(), + }); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + }, + else => {}, + } + }, + .stack_offset => |offset| { + _ = offset; + switch (self.debug_output) { + .dwarf => {}, + else => {}, + } + }, + else => {}, + } +} + fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_type: type, off: off_type, abi_size: u64) !void { assert(off_type == Register or off_type == i13); From e963d5be0b3dbe87f68014920cd1b93109318c32 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 21 Apr 2022 21:08:50 +0700 Subject: [PATCH 1364/2031] stage2: sparcv9: Simplify debug info emission, remove unused formats --- src/arch/sparcv9/Emit.zig | 56 +++++++++++---------------------------- 1 file changed, 15 insertions(+), 41 deletions(-) diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig index aea25f2ffc..4da2215c46 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparcv9/Emit.zig @@ -101,22 +101,22 @@ fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { } } -fn mirDebugPrologueEnd(self: *Emit) !void { - switch (self.debug_output) { +fn mirDebugPrologueEnd(emit: *Emit) !void { + switch (emit.debug_output) { .dwarf => |dbg_out| { try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); - try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); + try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, .plan9 => {}, .none => {}, } } -fn mirDebugEpilogueBegin(self: *Emit) !void { - switch (self.debug_output) { +fn mirDebugEpilogueBegin(emit: *Emit) !void { + switch (emit.debug_output) { .dwarf => |dbg_out| { try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); - try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); + try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); }, .plan9 => {}, .none => {}, @@ -232,10 +232,10 @@ fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void { // Common helper functions -fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { - const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line); - const delta_pc: usize = self.code.items.len - self.prev_di_pc; - switch (self.debug_output) { +fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void { + const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line); + const delta_pc: usize = emit.code.items.len - emit.prev_di_pc; + switch (emit.debug_output) { .dwarf => |dbg_out| { // TODO Look into using the DWARF special opcodes to compress this data. // It lets you emit single-byte opcodes that add different numbers to @@ -248,38 +248,12 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; } dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); - self.prev_di_pc = self.code.items.len; - self.prev_di_line = line; - self.prev_di_column = column; - self.prev_di_pc = self.code.items.len; + emit.prev_di_pc = emit.code.items.len; + emit.prev_di_line = line; + emit.prev_di_column = column; + emit.prev_di_pc = emit.code.items.len; }, - .plan9 => |dbg_out| { - if (delta_pc <= 0) return; // only do this when the pc changes - // we have already checked the target in the linker to make sure it is compatable - const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable; - - // increasing the line number - try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line); - // increasing the pc - const d_pc_p9 = @intCast(i64, delta_pc) - quant; - if (d_pc_p9 > 0) { - // minus one because if its the last one, we want to leave space to change the line which is one quanta - try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant); - if (dbg_out.pcop_change_index.*) |pci| - dbg_out.dbg_line.items[pci] += 1; - dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1); - } else if (d_pc_p9 == 0) { - // we don't need to do anything, because adding the quant does it for us - } else unreachable; - if (dbg_out.start_line.* == null) - dbg_out.start_line.* = self.prev_di_line; - dbg_out.end_line.* = line; - // only do this if the pc changed - self.prev_di_line = line; - self.prev_di_column = column; - self.prev_di_pc = self.code.items.len; - }, - .none => {}, + else => {}, } } From bc5b5df2c72d9b345b5cd242915aba2d2b975747 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 21 Apr 2022 21:16:24 +0700 Subject: [PATCH 1365/2031] stage2: sparcv9: Update test case --- test/cases/sparcv9-linux/hello_world.zig | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/test/cases/sparcv9-linux/hello_world.zig b/test/cases/sparcv9-linux/hello_world.zig index 327a8c56ad..1aea9da7a3 100644 --- a/test/cases/sparcv9-linux/hello_world.zig +++ b/test/cases/sparcv9-linux/hello_world.zig @@ -1,23 +1,18 @@ const msg = "Hello, World!\n"; -pub export fn _start() noreturn { +fn length() usize { + return msg.len; +} + +pub fn main() void { asm volatile ("ta 0x6d" : : [number] "{g1}" (4), [arg1] "{o0}" (1), [arg2] "{o1}" (@ptrToInt(msg)), - [arg3] "{o2}" (msg.len), + [arg3] "{o2}" (length()), : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory" ); - - asm volatile ("ta 0x6d" - : - : [number] "{g1}" (1), - [arg1] "{o0}" (0), - : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory" - ); - - unreachable; } // run From 5a6f0d2e51d3d7fb3f88099611f0347d0d27e889 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 22 Apr 2022 21:30:56 +0700 Subject: [PATCH 1366/2031] stage2: sparcv9: Update Mir tag doc comments --- src/arch/sparcv9/Mir.zig | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index 8916eafe80..d2772116ee 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -39,24 +39,24 @@ pub const Inst = struct { // in The SPARC Architecture Manual, Version 9. /// A.2 Add - /// Those uses the arithmetic_3op field. + /// This uses the arithmetic_3op field. // TODO add other operations. add, /// A.7 Branch on Integer Condition Codes with Prediction (BPcc) - /// It uses the branch_predict field. + /// This uses the branch_predict field. bpcc, /// A.8 Call and Link - /// It uses the branch_link field. + /// This uses the branch_link field. call, /// A.24 Jump and Link - /// It uses the arithmetic_3op field. + /// This uses the arithmetic_3op field. jmpl, /// A.27 Load Integer - /// Those uses the arithmetic_3op field. + /// This uses the arithmetic_3op field. /// Note that the ldd variant of this instruction is deprecated, so do not emit /// it unless specifically requested (e.g. by inline assembly). // TODO add other operations. @@ -66,29 +66,29 @@ pub const Inst = struct { ldx, /// A.31 Logical Operations - /// Those uses the arithmetic_3op field. + /// This uses the arithmetic_3op field. // TODO add other operations. @"or", /// A.40 No Operation - /// It uses the nop field. + /// This uses the nop field. nop, /// A.45 RETURN - /// It uses the arithmetic_2op field. + /// This Thisuses the arithmetic_2op field. @"return", /// A.46 SAVE and RESTORE - /// Those uses the arithmetic_3op field. + /// This uses the arithmetic_3op field. save, restore, /// A.48 SETHI - /// It uses the sethi field. + /// This uses the sethi field. sethi, /// A.49 Shift - /// Those uses the shift field. + /// This uses the shift field. // TODO add other operations. sllx, @@ -103,12 +103,12 @@ pub const Inst = struct { stx, /// A.56 Subtract - /// Those uses the arithmetic_3op field. + /// This uses the arithmetic_3op field. // TODO add other operations. sub, /// A.61 Trap on Integer Condition Codes (Tcc) - /// It uses the trap field. + /// This uses the trap field. tcc, }; From 64927aa782bb2105012da508975c5b3d0d10f477 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 5 May 2022 19:38:55 +0200 Subject: [PATCH 1367/2031] sparcv9: fix typo in def comment --- src/arch/sparcv9/Mir.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig index d2772116ee..ef0be93f4c 100644 --- a/src/arch/sparcv9/Mir.zig +++ b/src/arch/sparcv9/Mir.zig @@ -75,7 +75,7 @@ pub const Inst = struct { nop, /// A.45 RETURN - /// This Thisuses the arithmetic_2op field. + /// This uses the arithmetic_2op field. @"return", /// A.46 SAVE and RESTORE From 9985699943edb4bf4f3dae9b57e0e2017c23c4bf Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 5 May 2022 20:00:13 +0200 Subject: [PATCH 1368/2031] libstd: map sparcv9 to qemu-sparc64 for test-runner --- lib/std/zig/system/NativeTargetInfo.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/std/zig/system/NativeTargetInfo.zig b/lib/std/zig/system/NativeTargetInfo.zig index f917ee8e34..9055d1c215 100644 --- a/lib/std/zig/system/NativeTargetInfo.zig +++ b/lib/std/zig/system/NativeTargetInfo.zig @@ -931,6 +931,7 @@ pub fn getExternalExecutor( .riscv64 => Executor{ .qemu = "qemu-riscv64" }, .s390x => Executor{ .qemu = "qemu-s390x" }, .sparc => Executor{ .qemu = "qemu-sparc" }, + .sparcv9 => Executor{ .qemu = "qemu-sparc64" }, .x86_64 => Executor{ .qemu = "qemu-x86_64" }, else => return bad_result, }; From c2d2307d09017e97a9e9ce59d29754ff2becdd54 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 23 Apr 2022 20:25:59 +0200 Subject: [PATCH 1369/2031] stage2 AArch64: initial implementation of {add,sub}_with_overflow --- src/arch/aarch64/CodeGen.zig | 128 ++++++++++++++++++++++++++++------- src/arch/arm/CodeGen.zig | 2 +- test/behavior/math.zig | 1 - 3 files changed, 105 insertions(+), 26 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 3b27633f69..98d0fbc978 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -546,8 +546,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .trunc_float => try self.airUnaryMath(inst), - .add_with_overflow => try self.airAddWithOverflow(inst), - .sub_with_overflow => try self.airSubWithOverflow(inst), + .add_with_overflow => try self.airOverflow(inst), + .sub_with_overflow => try self.airOverflow(inst), .mul_with_overflow => try self.airMulWithOverflow(inst), .shl_with_overflow => try self.airShlWithOverflow(inst), @@ -1245,18 +1245,24 @@ fn binOpRegister( }; defer self.register_manager.unfreezeRegs(&.{rhs_reg}); - const dest_reg = if (maybe_inst) |inst| blk: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const dest_reg = switch (mir_tag) { + .cmp_shifted_register => undefined, // cmp has no destination register + else => if (maybe_inst) |inst| blk: { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; - if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { - break :blk lhs_reg; - } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { - break :blk rhs_reg; - } else { - const raw_reg = try self.register_manager.allocReg(inst); + if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { + break :blk lhs_reg; + } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { + break :blk rhs_reg; + } else { + const raw_reg = try self.register_manager.allocReg(inst); + break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); + } + } else blk: { + const raw_reg = try self.register_manager.allocReg(null); break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); - } - } else try self.register_manager.allocReg(null); + }, + }; if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); @@ -1368,7 +1374,10 @@ fn binOpImmediate( const raw_reg = try self.register_manager.allocReg(inst); break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); } - } else try self.register_manager.allocReg(null), + } else blk: { + const raw_reg = try self.register_manager.allocReg(null); + break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); + }, }; if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); @@ -1711,14 +1720,68 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch}); -} +fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { + const tag = self.air.instructions.items(.tag)[inst]; + 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 result: { + const lhs = try self.resolveInst(extra.lhs); + const rhs = try self.resolveInst(extra.rhs); + const lhs_ty = self.air.typeOf(extra.lhs); + const rhs_ty = self.air.typeOf(extra.rhs); -fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch}); + const tuple_ty = self.air.typeOfIndex(inst); + const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); + const tuple_align = tuple_ty.abiAlignment(self.target.*); + const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*)); + + switch (lhs_ty.zigTypeTag()) { + .Vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}), + .Int => { + const mod = self.bin_file.options.module.?; + assert(lhs_ty.eql(rhs_ty, mod)); + const int_info = lhs_ty.intInfo(self.target.*); + switch (int_info.bits) { + 1...31, 33...63 => { + const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); + + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = null; + + const base_tag: Air.Inst.Tag = switch (tag) { + .add_with_overflow => .add, + .sub_with_overflow => .sub, + else => unreachable, + }; + const dest = try self.binOp(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); + const dest_reg = dest.register; + self.register_manager.freezeRegs(&.{dest_reg}); + defer self.register_manager.unfreezeRegs(&.{dest_reg}); + + const raw_truncated_reg = try self.register_manager.allocReg(null); + const truncated_reg = registerAlias(raw_truncated_reg, lhs_ty.abiSize(self.target.*)); + self.register_manager.freezeRegs(&.{truncated_reg}); + defer self.register_manager.unfreezeRegs(&.{truncated_reg}); + + // sbfx/ubfx truncated, dest, #0, #bits + try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); + + // cmp dest, truncated + _ = try self.binOp(.cmp_eq, null, dest, .{ .register = truncated_reg }, Type.usize, Type.usize); + + try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); + try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); + + break :result MCValue{ .stack_offset = stack_offset }; + }, + 32, 64 => return self.fail("TODO overflow operations on integers u32/i32 and u64/i64", .{}), + else => return self.fail("TODO overflow operations on integers > u32/i32", .{}), + } + }, + else => unreachable, + } + }; + return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); } fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { @@ -1957,7 +2020,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { switch (elem_size) { else => { const dest = try self.allocRegOrMem(inst, true); - const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ty, Type.usize); + const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ptr_field_type, Type.usize); try self.load(dest, addr, slice_ptr_field_type); break :result dest; @@ -2409,9 +2472,26 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; - _ = extra; - return self.fail("TODO implement codegen struct_field_val", .{}); - //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); + const operand = extra.struct_operand; + const index = extra.field_index; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const mcv = try self.resolveInst(operand); + const struct_ty = self.air.typeOf(operand); + const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); + + switch (mcv) { + .dead, .unreach => unreachable, + .stack_offset => |off| { + break :result MCValue{ .stack_offset = off - struct_field_offset }; + }, + .memory => |addr| { + break :result MCValue{ .memory = addr + struct_field_offset }; + }, + else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), + } + }; + + return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); } fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 87d51b0276..d463ba9928 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1989,7 +1989,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { }, else => { const dest = try self.allocRegOrMem(inst, true); - const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ty, Type.usize); + const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ptr_field_type, Type.usize); try self.load(dest, addr, slice_ptr_field_type); break :result dest; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 3997f2db04..39489990d9 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -624,7 +624,6 @@ test "128-bit multiplication" { test "@addWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO var result: u8 = undefined; try expect(@addWithOverflow(u8, 250, 100, &result)); From f267e7a8b45d343eb1c3cf5f0662746912af8c99 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Mon, 25 Apr 2022 21:04:39 +0200 Subject: [PATCH 1370/2031] stage2 AArch64: implement {add,sub}_with_overflow for all ints < 64 --- src/arch/aarch64/CodeGen.zig | 211 ++++++++++++++++++++++++++++------- src/arch/aarch64/Emit.zig | 14 ++- src/arch/aarch64/Mir.zig | 8 ++ 3 files changed, 193 insertions(+), 40 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 98d0fbc978..bad514bcd9 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -102,9 +102,12 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; const MCValue = union(enum) { - /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. - /// TODO Look into deleting this tag and using `dead` instead, since every use - /// of MCValue.none should be instead looking at the type and noticing it is 0 bits. + /// No runtime bits. `void` types, empty structs, u0, enums with 1 + /// tag, etc. + /// + /// TODO Look into deleting this tag and using `dead` instead, + /// since every use of MCValue.none should be instead looking at + /// the type and noticing it is 0 bits. none, /// Control flow will not allow this value to be observed. unreach, @@ -113,28 +116,56 @@ const MCValue = union(enum) { /// The value is undefined. undef, /// A pointer-sized integer that fits in a register. - /// If the type is a pointer, this is the pointer address in virtual address space. + /// + /// If the type is a pointer, this is the pointer address in + /// virtual address space. immediate: u64, /// The value is in a target-specific register. register: Register, + /// The value is a tuple { wrapped: u32, overflow: u1 } where + /// wrapped is stored in the register and the overflow bit is + /// stored in the C flag of the CPSR. + /// + /// This MCValue is only generated by a add_with_overflow or + /// sub_with_overflow instruction operating on u32. + register_c_flag: Register, + /// The value is a tuple { wrapped: i32, overflow: u1 } where + /// wrapped is stored in the register and the overflow bit is + /// stored in the V flag of the CPSR. + /// + /// This MCValue is only generated by a add_with_overflow or + /// sub_with_overflow instruction operating on i32. + register_v_flag: Register, /// The value is in memory at a hard-coded address. - /// If the type is a pointer, it means the pointer address is at this memory location. + /// + /// If the type is a pointer, it means the pointer address is at + /// this memory location. memory: u64, - /// The value is in memory referenced indirectly via a GOT entry index. - /// If the type is a pointer, it means the pointer is referenced indirectly via GOT. - /// When lowered, linker will emit relocations of type ARM64_RELOC_GOT_LOAD_PAGE21 and ARM64_RELOC_GOT_LOAD_PAGEOFF12. + /// The value is in memory referenced indirectly via a GOT entry + /// index. + /// + /// If the type is a pointer, it means the pointer is referenced + /// indirectly via GOT. When lowered, linker will emit + /// relocations of type ARM64_RELOC_GOT_LOAD_PAGE21 and + /// ARM64_RELOC_GOT_LOAD_PAGEOFF12. got_load: u32, /// The value is in memory referenced directly via symbol index. - /// If the type is a pointer, it means the pointer is referenced directly via symbol index. - /// When lowered, linker will emit a relocation of type ARM64_RELOC_PAGE21 and ARM64_RELOC_PAGEOFF12. + /// + /// If the type is a pointer, it means the pointer is referenced + /// directly via symbol index. When lowered, linker will emit a + /// relocation of type ARM64_RELOC_PAGE21 and + /// ARM64_RELOC_PAGEOFF12. direct_load: u32, /// The value is one of the stack variables. - /// If the type is a pointer, it means the pointer address is in the stack at this offset. + /// + /// If the type is a pointer, it means the pointer address is in + /// the stack at this offset. stack_offset: u32, - /// The value is a pointer to one of the stack variables (payload is stack offset). + /// The value is a pointer to one of the stack variables (payload + /// is stack offset). ptr_stack_offset: u32, - /// The value is in the compare flags assuming an unsigned operation, - /// with this operator applied on top of it. + /// The value is in the compare flags assuming an unsigned + /// operation, with this operator applied on top of it. compare_flags_unsigned: math.CompareOperator, /// The value is in the compare flags assuming a signed operation, /// with this operator applied on top of it. @@ -716,8 +747,13 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { branch.inst_table.putAssumeCapacity(inst, .dead); switch (prev_value) { .register => |reg| { - const canon_reg = toCanonicalReg(reg); - self.register_manager.freeReg(canon_reg); + self.register_manager.freeReg(reg); + }, + .register_c_flag, + .register_v_flag, + => |reg| { + self.register_manager.freeReg(reg); + self.compare_flags_inst = null; }, .compare_flags_signed, .compare_flags_unsigned => { self.compare_flags_inst = null; @@ -857,7 +893,13 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void const stack_mcv = try self.allocRegOrMem(inst, false); log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); const reg_mcv = self.getResolvedInstValue(inst); - assert(reg == toCanonicalReg(reg_mcv.register)); + switch (reg_mcv) { + .register, + .register_c_flag, + .register_v_flag, + => |r| assert(reg.id() == r.id()), + else => unreachable, // not a register + } const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; try branch.inst_table.put(self.gpa, inst, stack_mcv); try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); @@ -868,7 +910,14 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void fn spillCompareFlagsIfOccupied(self: *Self) !void { if (self.compare_flags_inst) |inst_to_save| { const mcv = self.getResolvedInstValue(inst_to_save); - assert(mcv == .compare_flags_signed or mcv == .compare_flags_unsigned); + switch (mcv) { + .compare_flags_signed, + .compare_flags_unsigned, + .register_c_flag, + .register_v_flag, + => {}, + else => unreachable, // mcv doesn't occupy the compare flags + } const new_mcv = try self.allocRegOrMem(inst_to_save, true); try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); @@ -1269,7 +1318,9 @@ fn binOpRegister( const mir_data: Mir.Inst.Data = switch (mir_tag) { .add_shifted_register, + .adds_shifted_register, .sub_shifted_register, + .subs_shifted_register, => .{ .rrr_imm6_shift = .{ .rd = dest_reg, .rn = lhs_reg, @@ -1384,7 +1435,9 @@ fn binOpImmediate( const mir_data: Mir.Inst.Data = switch (mir_tag) { .add_immediate, + .adds_immediate, .sub_immediate, + .subs_immediate, => .{ .rr_imm12_sh = .{ .rd = dest_reg, .rn = lhs_reg, @@ -1774,7 +1827,52 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { break :result MCValue{ .stack_offset = stack_offset }; }, - 32, 64 => return self.fail("TODO overflow operations on integers u32/i32 and u64/i64", .{}), + 32, 64 => { + // Only say yes if the operation is + // commutative, i.e. we can swap both of the + // operands + const lhs_immediate_ok = switch (tag) { + .add_with_overflow => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), + .sub_with_overflow => false, + else => unreachable, + }; + const rhs_immediate_ok = switch (tag) { + .add_with_overflow, + .sub_with_overflow, + => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), + else => unreachable, + }; + + const mir_tag_register: Mir.Inst.Tag = switch (tag) { + .add_with_overflow => .adds_shifted_register, + .sub_with_overflow => .subs_shifted_register, + else => unreachable, + }; + const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { + .add_with_overflow => .adds_immediate, + .sub_with_overflow => .subs_immediate, + else => unreachable, + }; + + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = inst; + + const dest = blk: { + if (rhs_immediate_ok) { + break :blk try self.binOpImmediate(mir_tag_immediate, null, lhs, rhs, lhs_ty, false); + } else if (lhs_immediate_ok) { + // swap lhs and rhs + break :blk try self.binOpImmediate(mir_tag_immediate, null, rhs, lhs, rhs_ty, true); + } else { + break :blk try self.binOpRegister(mir_tag_register, null, lhs, rhs, lhs_ty, rhs_ty); + } + }; + + switch (int_info.signedness) { + .unsigned => break :result MCValue{ .register_c_flag = dest.register }, + .signed => break :result MCValue{ .register_v_flag = dest.register }, + } + }, else => return self.fail("TODO overflow operations on integers > u32/i32", .{}), } }, @@ -2148,8 +2246,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .undef => unreachable, .unreach => unreachable, .dead => unreachable, - .compare_flags_unsigned => unreachable, - .compare_flags_signed => unreachable, + .compare_flags_unsigned, + .compare_flags_signed, + .register_c_flag, + .register_v_flag, + => unreachable, // cannot hold an address .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), .register => |addr_reg| { @@ -2366,8 +2467,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .undef => unreachable, .unreach => unreachable, .dead => unreachable, - .compare_flags_unsigned => unreachable, - .compare_flags_signed => unreachable, + .compare_flags_unsigned, + .compare_flags_signed, + .register_c_flag, + .register_v_flag, + => unreachable, // cannot hold an address .immediate => |imm| { try self.setRegOrMem(value_ty, .{ .memory = imm }, value); }, @@ -2487,6 +2591,40 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { .memory => |addr| { break :result MCValue{ .memory = addr + struct_field_offset }; }, + .register_c_flag, + .register_v_flag, + => |reg| { + switch (index) { + 0 => { + // get wrapped value: return register + break :result MCValue{ .register = reg }; + }, + 1 => { + // TODO return special MCValue condition flags + // get overflow bit: set register to C flag + // resp. V flag + const raw_dest_reg = try self.register_manager.allocReg(null); + const dest_reg = raw_dest_reg.to32(); + + // C flag: cset reg, cs + // V flag: cset reg, vs + _ = try self.addInst(.{ + .tag = .cset, + .data = .{ .r_cond = .{ + .rd = dest_reg, + .cond = switch (mcv) { + .register_c_flag => .cs, + .register_v_flag => .vs, + else => unreachable, + }, + } }, + }); + + break :result MCValue{ .register = dest_reg }; + }, + else => unreachable, + } + }, else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), } }; @@ -2531,7 +2669,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { switch (mcv) { .register => |reg| { - self.register_manager.getRegAssumeFree(toCanonicalReg(reg), inst); + self.register_manager.getRegAssumeFree(reg, inst); }, else => {}, } @@ -2596,15 +2734,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. switch (mc_arg) { .none => continue, - .undef => unreachable, - .immediate => unreachable, - .unreach => unreachable, - .dead => unreachable, - .memory => unreachable, - .compare_flags_signed => unreachable, - .compare_flags_unsigned => unreachable, - .got_load => unreachable, - .direct_load => unreachable, .register => |reg| { try self.register_manager.getReg(reg, null); try self.genSetReg(arg_ty, reg, arg_mcv); @@ -2615,6 +2744,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .ptr_stack_offset => { return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); }, + else => unreachable, } } @@ -3518,6 +3648,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), } }, + .register_c_flag, + .register_v_flag, + => { + return self.fail("TODO implement genSetStack {}", .{mcv}); + }, .got_load, .direct_load, .memory, @@ -3635,7 +3770,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .tag = .cset, .data = .{ .r_cond = .{ .rd = reg, - .cond = condition.negate(), + .cond = condition, } }, }); }, @@ -3678,6 +3813,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .data = .{ .rr = .{ .rd = reg, .rn = src_reg } }, }); }, + .register_c_flag, + .register_v_flag, + => unreachable, // doesn't fit into a register .got_load, .direct_load, => |sym_index| { @@ -4279,8 +4417,3 @@ fn registerAlias(reg: Register, size_bytes: u64) Register { unreachable; // TODO handle floating-point registers } } - -/// Resolves any aliased registers to the 64-bit wide ones. -fn toCanonicalReg(reg: Register) Register { - return reg.to64(); -} diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index d9dfbc6fac..85389f445e 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -77,8 +77,10 @@ pub fn emitMir( const inst = @intCast(u32, index); switch (tag) { .add_immediate => try emit.mirAddSubtractImmediate(inst), + .adds_immediate => try emit.mirAddSubtractImmediate(inst), .cmp_immediate => try emit.mirAddSubtractImmediate(inst), .sub_immediate => try emit.mirAddSubtractImmediate(inst), + .subs_immediate => try emit.mirAddSubtractImmediate(inst), .asr_register => try emit.mirShiftRegister(inst), .lsl_register => try emit.mirShiftRegister(inst), @@ -106,8 +108,10 @@ pub fn emitMir( .eor_immediate => try emit.mirLogicalImmediate(inst), .add_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), + .adds_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), .cmp_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), .sub_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), + .subs_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), .cset => try emit.mirConditionalSelect(inst), @@ -454,7 +458,9 @@ fn mirAddSubtractImmediate(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; switch (tag) { .add_immediate, + .adds_immediate, .sub_immediate, + .subs_immediate, => { const rr_imm12_sh = emit.mir.instructions.items(.data)[inst].rr_imm12_sh; const rd = rr_imm12_sh.rd; @@ -464,7 +470,9 @@ fn mirAddSubtractImmediate(emit: *Emit, inst: Mir.Inst.Index) !void { switch (tag) { .add_immediate => try emit.writeInstruction(Instruction.add(rd, rn, imm12, sh)), + .adds_immediate => try emit.writeInstruction(Instruction.adds(rd, rn, imm12, sh)), .sub_immediate => try emit.writeInstruction(Instruction.sub(rd, rn, imm12, sh)), + .subs_immediate => try emit.writeInstruction(Instruction.subs(rd, rn, imm12, sh)), else => unreachable, } }, @@ -674,7 +682,9 @@ fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; switch (tag) { .add_shifted_register, + .adds_shifted_register, .sub_shifted_register, + .subs_shifted_register, => { const rrr_imm6_shift = emit.mir.instructions.items(.data)[inst].rrr_imm6_shift; const rd = rrr_imm6_shift.rd; @@ -685,7 +695,9 @@ fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void { switch (tag) { .add_shifted_register => try emit.writeInstruction(Instruction.addShiftedRegister(rd, rn, rm, shift, imm6)), + .adds_shifted_register => try emit.writeInstruction(Instruction.addsShiftedRegister(rd, rn, rm, shift, imm6)), .sub_shifted_register => try emit.writeInstruction(Instruction.subShiftedRegister(rd, rn, rm, shift, imm6)), + .subs_shifted_register => try emit.writeInstruction(Instruction.subsShiftedRegister(rd, rn, rm, shift, imm6)), else => unreachable, } }, @@ -717,7 +729,7 @@ fn mirConditionalSelect(emit: *Emit, inst: Mir.Inst.Index) !void { 64 => .xzr, else => unreachable, }; - try emit.writeInstruction(Instruction.csinc(r_cond.rd, zr, zr, r_cond.cond)); + try emit.writeInstruction(Instruction.csinc(r_cond.rd, zr, zr, r_cond.cond.negate())); }, else => unreachable, } diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index 516ccca984..8c5b635649 100644 --- a/src/arch/aarch64/Mir.zig +++ b/src/arch/aarch64/Mir.zig @@ -26,8 +26,12 @@ pub const Inst = struct { pub const Tag = enum(u16) { /// Add (immediate) add_immediate, + /// Add, update condition flags (immediate) + adds_immediate, /// Add (shifted register) add_shifted_register, + /// Add, update condition flags (shifted register) + adds_shifted_register, /// Bitwise AND (shifted register) and_shifted_register, /// Arithmetic Shift Right (immediate) @@ -170,8 +174,12 @@ pub const Inst = struct { strh_register, /// Subtract (immediate) sub_immediate, + /// Subtract, update condition flags (immediate) + subs_immediate, /// Subtract (shifted register) sub_shifted_register, + /// Subtract, update condition flags (shifted register) + subs_shifted_register, /// Supervisor Call svc, /// Unsigned bitfield extract From aaacda4df97c03cfcea444c1d77c06f46575049d Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Tue, 26 Apr 2022 22:42:38 +0200 Subject: [PATCH 1371/2031] stage2 AArch64: implement shl_with_overflow --- src/arch/aarch64/CodeGen.zig | 50 ++++++++++++++++++++++++++++++++++-- test/behavior/math.zig | 2 -- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index bad514bcd9..9b66c91925 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -1888,8 +1888,54 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { } fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch}); + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; + if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); + const result: MCValue = result: { + const lhs = try self.resolveInst(extra.lhs); + const rhs = try self.resolveInst(extra.rhs); + const lhs_ty = self.air.typeOf(extra.lhs); + const rhs_ty = self.air.typeOf(extra.rhs); + + const tuple_ty = self.air.typeOfIndex(inst); + const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); + const tuple_align = tuple_ty.abiAlignment(self.target.*); + const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*)); + + switch (lhs_ty.zigTypeTag()) { + .Vector => return self.fail("TODO implement shl_with_overflow for vectors", .{}), + .Int => { + const int_info = lhs_ty.intInfo(self.target.*); + if (int_info.bits <= 64) { + const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); + + if (lhs == .register) self.register_manager.freezeRegs(&.{lhs.register}); + defer if (lhs == .register) self.register_manager.unfreezeRegs(&.{lhs.register}); + + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = null; + + // lsl dest, lhs, rhs + const dest = try self.binOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty); + + // asr/lsr reconstructed, dest, rhs + const reconstructed = try self.binOp(.shr, null, dest, rhs, lhs_ty, rhs_ty); + + // cmp lhs, reconstructed + _ = try self.binOp(.cmp_eq, null, lhs, reconstructed, lhs_ty, lhs_ty); + + try self.genSetStack(lhs_ty, stack_offset, dest); + try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); + + break :result MCValue{ .stack_offset = stack_offset }; + } else { + return self.fail("TODO overflow operations on integers > u64/i64", .{}); + } + }, + else => unreachable, + } + }; + return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); } fn airDiv(self: *Self, inst: Air.Inst.Index) !void { diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 39489990d9..c758272728 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -686,7 +686,6 @@ test "@mulWithOverflow" { test "@subWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO var result: u8 = undefined; try expect(@subWithOverflow(u8, 1, 2, &result)); @@ -706,7 +705,6 @@ test "@subWithOverflow" { test "@shlWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO var result: u16 = undefined; try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); From 8715b01005c49ff99327a87264ffaa28fb3807a0 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 3 May 2022 12:20:27 +0200 Subject: [PATCH 1372/2031] aarch64: implement mul_with_overflow for <= 32bit ints Add emitters for `smull`, `umull` and `tst (immediate)` instructions. --- src/arch/aarch64/CodeGen.zig | 72 +++++++++++++++++++++++++++++++++++- src/arch/aarch64/Emit.zig | 6 +++ src/arch/aarch64/Mir.zig | 6 +++ src/arch/aarch64/bits.zig | 33 +++++++++++++++++ test/behavior/math.zig | 1 - 5 files changed, 115 insertions(+), 3 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 9b66c91925..3122501dac 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -1296,6 +1296,11 @@ fn binOpRegister( const dest_reg = switch (mir_tag) { .cmp_shifted_register => undefined, // cmp has no destination register + .smull, .umull => blk: { + // TODO can we reuse anything for smull and umull? + const raw_reg = try self.register_manager.allocReg(null); + break :blk raw_reg.to64(); + }, else => if (maybe_inst) |inst| blk: { const bin_op = self.air.instructions.items(.data)[inst].bin_op; @@ -1335,6 +1340,8 @@ fn binOpRegister( .shift = .lsl, } }, .mul, + .smull, + .umull, .lsl_register, .asr_register, .lsr_register, @@ -1883,8 +1890,69 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { } fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch}); + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; + if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); + const result: MCValue = result: { + const lhs = try self.resolveInst(extra.lhs); + const rhs = try self.resolveInst(extra.rhs); + const lhs_ty = self.air.typeOf(extra.lhs); + const rhs_ty = self.air.typeOf(extra.rhs); + + const tuple_ty = self.air.typeOfIndex(inst); + const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); + const tuple_align = tuple_ty.abiAlignment(self.target.*); + const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*)); + + switch (lhs_ty.zigTypeTag()) { + .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}), + .Int => { + const int_info = lhs_ty.intInfo(self.target.*); + + if (int_info.bits <= 32) { + const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); + + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = null; + + const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { + .signed => .smull, + .unsigned => .umull, + }; + + const dest = try self.binOpRegister(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); + const dest_reg = dest.register; + self.register_manager.freezeRegs(&.{dest_reg}); + defer self.register_manager.unfreezeRegs(&.{dest_reg}); + + const truncated_reg = try self.register_manager.allocReg(null); + self.register_manager.freezeRegs(&.{truncated_reg}); + defer self.register_manager.unfreezeRegs(&.{truncated_reg}); + + try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); + _ = try self.binOp( + .cmp_eq, + null, + dest, + .{ .register = truncated_reg }, + Type.usize, + Type.usize, + ); + + try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); + try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ + .compare_flags_unsigned = .neq, + }); + + break :result MCValue{ .stack_offset = stack_offset }; + } else if (int_info.bits <= 64) { + return self.fail("TODO implement mul_with_overflow for ints", .{}); + } else return self.fail("TODO implmenet mul_with_overflow for integers > u64/i64", .{}); + }, + else => unreachable, + } + }; + return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); } fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 85389f445e..5c4e221586 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -106,6 +106,7 @@ pub fn emitMir( .call_extern => try emit.mirCallExtern(inst), .eor_immediate => try emit.mirLogicalImmediate(inst), + .tst_immediate => try emit.mirLogicalImmediate(inst), .add_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), .adds_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), @@ -166,6 +167,8 @@ pub fn emitMir( .movz => try emit.mirMoveWideImmediate(inst), .mul => try emit.mirDataProcessing3Source(inst), + .smull => try emit.mirDataProcessing3Source(inst), + .umull => try emit.mirDataProcessing3Source(inst), .nop => try emit.mirNop(), @@ -674,6 +677,7 @@ fn mirLogicalImmediate(emit: *Emit, inst: Mir.Inst.Index) !void { switch (tag) { .eor_immediate => try emit.writeInstruction(Instruction.eorImmediate(rd, rn, imms, immr, n)), + .tst_immediate => try emit.writeInstruction(Instruction.tstImmediate(rn, imms, immr, n)), else => unreachable, } } @@ -1000,6 +1004,8 @@ fn mirDataProcessing3Source(emit: *Emit, inst: Mir.Inst.Index) !void { switch (tag) { .mul => try emit.writeInstruction(Instruction.mul(rrr.rd, rrr.rn, rrr.rm)), + .smull => try emit.writeInstruction(Instruction.smull(rrr.rd, rrr.rn, rrr.rm)), + .umull => try emit.writeInstruction(Instruction.umull(rrr.rd, rrr.rn, rrr.rm)), else => unreachable, } } diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index 8c5b635649..49ec895290 100644 --- a/src/arch/aarch64/Mir.zig +++ b/src/arch/aarch64/Mir.zig @@ -146,6 +146,8 @@ pub const Inst = struct { ret, /// Signed bitfield extract sbfx, + /// Signed multiply long + smull, /// Signed extend byte sxtb, /// Signed extend halfword @@ -182,8 +184,12 @@ pub const Inst = struct { subs_shifted_register, /// Supervisor Call svc, + /// Test bits (immediate) + tst_immediate, /// Unsigned bitfield extract ubfx, + /// Unsigned multiply long + umull, /// Unsigned extend byte uxtb, /// Unsigned extend halfword diff --git a/src/arch/aarch64/bits.zig b/src/arch/aarch64/bits.zig index 0775ca1f7b..b72891af30 100644 --- a/src/arch/aarch64/bits.zig +++ b/src/arch/aarch64/bits.zig @@ -1409,6 +1409,10 @@ pub const Instruction = union(enum) { return logicalImmediate(0b11, rd, rn, imms, immr, n); } + pub fn tstImmediate(rn: Register, imms: u6, immr: u6, n: u1) Instruction { + return andsImmediate(.xzr, rn, imms, immr, n); + } + // Bitfield pub fn sbfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction { @@ -1564,6 +1568,15 @@ pub const Instruction = union(enum) { return dataProcessing3Source(0b00, 0b000, 0b0, rd, rn, rm, ra); } + pub fn smaddl(rd: Register, rn: Register, rm: Register, ra: Register) Instruction { + return dataProcessing3Source(0b00, 0b001, 0b0, rd, rn, rm, ra); + } + + pub fn umaddl(rd: Register, rn: Register, rm: Register, ra: Register) Instruction { + assert(rd.size() == 64); + return dataProcessing3Source(0b00, 0b101, 0b0, rd, rn, rm, ra); + } + pub fn msub(rd: Register, rn: Register, rm: Register, ra: Register) Instruction { return dataProcessing3Source(0b00, 0b000, 0b1, rd, rn, rm, ra); } @@ -1572,6 +1585,14 @@ pub const Instruction = union(enum) { return madd(rd, rn, rm, .xzr); } + pub fn smull(rd: Register, rn: Register, rm: Register) Instruction { + return smaddl(rd, rn, rm, .xzr); + } + + pub fn umull(rd: Register, rn: Register, rm: Register) Instruction { + return umaddl(rd, rn, rm, .xzr); + } + pub fn mneg(rd: Register, rn: Register, rm: Register) Instruction { return msub(rd, rn, rm, .xzr); } @@ -1790,6 +1811,18 @@ test "serialize instructions" { .inst = Instruction.lsrImmediate(.x4, .x2, 63), .expected = 0b1_10_100110_1_111111_111111_00010_00100, }, + .{ // umull x0, w0, w1 + .inst = Instruction.umull(.x0, .w0, .w1), + .expected = 0b1_00_11011_1_01_00001_0_11111_00000_00000, + }, + .{ // smull x0, w0, w1 + .inst = Instruction.smull(.x0, .w0, .w1), + .expected = 0b1_00_11011_0_01_00001_0_11111_00000_00000, + }, + .{ // tst x0, #0xffffffff00000000 + .inst = Instruction.tstImmediate(.x0, 0b011111, 0b100000, 0b1), + .expected = 0b1_11_100100_1_100000_011111_00000_11111, + }, }; for (testcases) |case| { diff --git a/test/behavior/math.zig b/test/behavior/math.zig index c758272728..7a60ec8417 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -666,7 +666,6 @@ test "small int addition" { test "@mulWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO var result: u8 = undefined; try expect(@mulWithOverflow(u8, 86, 3, &result)); From f4421c01e8a8793a7c02fce870bacbb710bb1e12 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 4 May 2022 21:20:31 +0200 Subject: [PATCH 1373/2031] aarch64: implement mul_with_overflow for ints in range 33-64 bits incl --- src/arch/aarch64/CodeGen.zig | 216 +++++++++++++++++++++++++++++++---- src/arch/aarch64/Emit.zig | 13 ++- src/arch/aarch64/Mir.zig | 4 + src/arch/aarch64/bits.zig | 24 +++- 4 files changed, 229 insertions(+), 28 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 3122501dac..fdf7eadb73 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -1294,28 +1294,29 @@ fn binOpRegister( }; defer self.register_manager.unfreezeRegs(&.{rhs_reg}); - const dest_reg = switch (mir_tag) { - .cmp_shifted_register => undefined, // cmp has no destination register - .smull, .umull => blk: { - // TODO can we reuse anything for smull and umull? - const raw_reg = try self.register_manager.allocReg(null); - break :blk raw_reg.to64(); - }, - else => if (maybe_inst) |inst| blk: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const dest_reg: Register = reg: { + const dest_reg = switch (mir_tag) { + .cmp_shifted_register => undefined, // cmp has no destination register + else => if (maybe_inst) |inst| blk: { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; - if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { - break :blk lhs_reg; - } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { - break :blk rhs_reg; - } else { - const raw_reg = try self.register_manager.allocReg(inst); + if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { + break :blk lhs_reg; + } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { + break :blk rhs_reg; + } else { + const raw_reg = try self.register_manager.allocReg(inst); + break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); + } + } else blk: { + const raw_reg = try self.register_manager.allocReg(null); break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); - } - } else blk: { - const raw_reg = try self.register_manager.allocReg(null); - break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); - }, + }, + }; + break :reg switch (mir_tag) { + .smull, .umull => dest_reg.to64(), + else => dest_reg, + }; }; if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); @@ -1340,7 +1341,9 @@ fn binOpRegister( .shift = .lsl, } }, .mul, + .smulh, .smull, + .umulh, .umull, .lsl_register, .asr_register, @@ -1946,8 +1949,177 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { break :result MCValue{ .stack_offset = stack_offset }; } else if (int_info.bits <= 64) { - return self.fail("TODO implement mul_with_overflow for ints", .{}); - } else return self.fail("TODO implmenet mul_with_overflow for integers > u64/i64", .{}); + const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); + + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = null; + + // TODO this should really be put in a helper similar to `binOpRegister` + const lhs_is_register = lhs == .register; + const rhs_is_register = rhs == .register; + + if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); + if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); + + const lhs_reg = if (lhs_is_register) lhs.register else blk: { + const raw_reg = try self.register_manager.allocReg(null); + const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); + self.register_manager.freezeRegs(&.{reg}); + break :blk reg; + }; + defer self.register_manager.unfreezeRegs(&.{lhs_reg}); + + const rhs_reg = if (rhs_is_register) rhs.register else blk: { + const raw_reg = try self.register_manager.allocReg(null); + const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*)); + self.register_manager.freezeRegs(&.{reg}); + break :blk reg; + }; + defer self.register_manager.unfreezeRegs(&.{rhs_reg}); + + if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); + if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); + + // TODO reuse operands + const dest_reg = blk: { + const raw_reg = try self.register_manager.allocReg(null); + const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); + self.register_manager.freezeRegs(&.{reg}); + break :blk reg; + }; + defer self.register_manager.unfreezeRegs(&.{dest_reg}); + + switch (int_info.signedness) { + .signed => { + // mul dest, lhs, rhs + _ = try self.addInst(.{ + .tag = .mul, + .data = .{ .rrr = .{ + .rd = dest_reg, + .rn = lhs_reg, + .rm = rhs_reg, + } }, + }); + + const dest_high_reg = try self.register_manager.allocReg(null); + self.register_manager.freezeRegs(&.{dest_high_reg}); + defer self.register_manager.unfreezeRegs(&.{dest_high_reg}); + + // smulh dest_high, lhs, rhs + _ = try self.addInst(.{ + .tag = .smulh, + .data = .{ .rrr = .{ + .rd = dest_high_reg, + .rn = lhs_reg, + .rm = rhs_reg, + } }, + }); + + // cmp dest_high, dest, asr #63 + _ = try self.addInst(.{ + .tag = .cmp_shifted_register, + .data = .{ .rr_imm6_shift = .{ + .rn = dest_high_reg, + .rm = dest_reg, + .imm6 = 63, + .shift = .asr, + } }, + }); + + const shift: u6 = @intCast(u6, @as(u7, 64) - @intCast(u7, int_info.bits)); + if (shift > 0) { + // lsl dest_high, dest, #shift + _ = try self.addInst(.{ + .tag = .lsl_immediate, + .data = .{ .rr_shift = .{ + .rd = dest_high_reg, + .rn = dest_reg, + .shift = shift, + } }, + }); + + // cmp dest, dest_high, #shift + _ = try self.addInst(.{ + .tag = .cmp_shifted_register, + .data = .{ .rr_imm6_shift = .{ + .rn = dest_reg, + .rm = dest_high_reg, + .imm6 = shift, + .shift = .asr, + } }, + }); + } + }, + .unsigned => { + const dest_high_reg = try self.register_manager.allocReg(null); + self.register_manager.freezeRegs(&.{dest_high_reg}); + defer self.register_manager.unfreezeRegs(&.{dest_high_reg}); + + // umulh dest_high, lhs, rhs + _ = try self.addInst(.{ + .tag = .umulh, + .data = .{ .rrr = .{ + .rd = dest_high_reg, + .rn = lhs_reg, + .rm = rhs_reg, + } }, + }); + + // mul dest, lhs, rhs + _ = try self.addInst(.{ + .tag = .mul, + .data = .{ .rrr = .{ + .rd = dest_reg, + .rn = lhs_reg, + .rm = rhs_reg, + } }, + }); + + _ = try self.binOp( + .cmp_eq, + null, + .{ .register = dest_high_reg }, + .{ .immediate = 0 }, + Type.usize, + Type.usize, + ); + + if (int_info.bits < 64) { + // lsr dest_high, dest, #shift + _ = try self.addInst(.{ + .tag = .lsr_immediate, + .data = .{ .rr_shift = .{ + .rd = dest_high_reg, + .rn = dest_reg, + .shift = @intCast(u6, int_info.bits), + } }, + }); + + _ = try self.binOp( + .cmp_eq, + null, + .{ .register = dest_high_reg }, + .{ .immediate = 0 }, + Type.usize, + Type.usize, + ); + } + }, + } + + const truncated_reg = try self.register_manager.allocReg(null); + self.register_manager.freezeRegs(&.{truncated_reg}); + defer self.register_manager.unfreezeRegs(&.{truncated_reg}); + + try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); + + try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); + try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ + .compare_flags_unsigned = .neq, + }); + + break :result MCValue{ .stack_offset = stack_offset }; + } else return self.fail("TODO implement mul_with_overflow for integers > u64/i64", .{}); }, else => unreachable, } diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 5c4e221586..1393533a7f 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -167,7 +167,9 @@ pub fn emitMir( .movz => try emit.mirMoveWideImmediate(inst), .mul => try emit.mirDataProcessing3Source(inst), + .smulh => try emit.mirDataProcessing3Source(inst), .smull => try emit.mirDataProcessing3Source(inst), + .umulh => try emit.mirDataProcessing3Source(inst), .umull => try emit.mirDataProcessing3Source(inst), .nop => try emit.mirNop(), @@ -677,7 +679,14 @@ fn mirLogicalImmediate(emit: *Emit, inst: Mir.Inst.Index) !void { switch (tag) { .eor_immediate => try emit.writeInstruction(Instruction.eorImmediate(rd, rn, imms, immr, n)), - .tst_immediate => try emit.writeInstruction(Instruction.tstImmediate(rn, imms, immr, n)), + .tst_immediate => { + const zr: Register = switch (rd.size()) { + 32 => .wzr, + 64 => .xzr, + else => unreachable, + }; + try emit.writeInstruction(Instruction.andsImmediate(zr, rn, imms, immr, n)); + }, else => unreachable, } } @@ -1004,7 +1013,9 @@ fn mirDataProcessing3Source(emit: *Emit, inst: Mir.Inst.Index) !void { switch (tag) { .mul => try emit.writeInstruction(Instruction.mul(rrr.rd, rrr.rn, rrr.rm)), + .smulh => try emit.writeInstruction(Instruction.smulh(rrr.rd, rrr.rn, rrr.rm)), .smull => try emit.writeInstruction(Instruction.smull(rrr.rd, rrr.rn, rrr.rm)), + .umulh => try emit.writeInstruction(Instruction.umulh(rrr.rd, rrr.rn, rrr.rm)), .umull => try emit.writeInstruction(Instruction.umull(rrr.rd, rrr.rn, rrr.rm)), else => unreachable, } diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index 49ec895290..1b27303419 100644 --- a/src/arch/aarch64/Mir.zig +++ b/src/arch/aarch64/Mir.zig @@ -146,6 +146,8 @@ pub const Inst = struct { ret, /// Signed bitfield extract sbfx, + /// Signed multiply high + smulh, /// Signed multiply long smull, /// Signed extend byte @@ -188,6 +190,8 @@ pub const Inst = struct { tst_immediate, /// Unsigned bitfield extract ubfx, + /// Unsigned multiply high + umulh, /// Unsigned multiply long umull, /// Unsigned extend byte diff --git a/src/arch/aarch64/bits.zig b/src/arch/aarch64/bits.zig index b72891af30..d8cb868d66 100644 --- a/src/arch/aarch64/bits.zig +++ b/src/arch/aarch64/bits.zig @@ -1409,10 +1409,6 @@ pub const Instruction = union(enum) { return logicalImmediate(0b11, rd, rn, imms, immr, n); } - pub fn tstImmediate(rn: Register, imms: u6, immr: u6, n: u1) Instruction { - return andsImmediate(.xzr, rn, imms, immr, n); - } - // Bitfield pub fn sbfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction { @@ -1589,10 +1585,20 @@ pub const Instruction = union(enum) { return smaddl(rd, rn, rm, .xzr); } + pub fn smulh(rd: Register, rn: Register, rm: Register) Instruction { + assert(rd.size() == 64); + return dataProcessing3Source(0b00, 0b010, 0b0, rd, rn, rm, .xzr); + } + pub fn umull(rd: Register, rn: Register, rm: Register) Instruction { return umaddl(rd, rn, rm, .xzr); } + pub fn umulh(rd: Register, rn: Register, rm: Register) Instruction { + assert(rd.size() == 64); + return dataProcessing3Source(0b00, 0b110, 0b0, rd, rn, rm, .xzr); + } + pub fn mneg(rd: Register, rn: Register, rm: Register) Instruction { return msub(rd, rn, rm, .xzr); } @@ -1820,9 +1826,17 @@ test "serialize instructions" { .expected = 0b1_00_11011_0_01_00001_0_11111_00000_00000, }, .{ // tst x0, #0xffffffff00000000 - .inst = Instruction.tstImmediate(.x0, 0b011111, 0b100000, 0b1), + .inst = Instruction.andsImmediate(.xzr, .x0, 0b011111, 0b100000, 0b1), .expected = 0b1_11_100100_1_100000_011111_00000_11111, }, + .{ // umulh x0, x1, x2 + .inst = Instruction.umulh(.x0, .x1, .x2), + .expected = 0b1_00_11011_1_10_00010_0_11111_00001_00000, + }, + .{ // smulh x0, x1, x2 + .inst = Instruction.smulh(.x0, .x1, .x2), + .expected = 0b1_00_11011_0_10_00010_0_11111_00001_00000, + }, }; for (testcases) |case| { From d112cd52f36cbb00e18009417044ab1e4496dd80 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 4 May 2022 23:00:41 +0200 Subject: [PATCH 1374/2031] aarch64: fix mul_with_overflow for ints <= 32bits --- src/arch/aarch64/CodeGen.zig | 76 ++++++++++++++++----------- src/arch/aarch64/Emit.zig | 55 ++++++++++++++++++-- src/arch/aarch64/Mir.zig | 29 +++++++++++ src/arch/aarch64/bits.zig | 99 +++++++++++++++++++++++++++++++++++- 4 files changed, 223 insertions(+), 36 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index fdf7eadb73..d146724188 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -1294,29 +1294,23 @@ fn binOpRegister( }; defer self.register_manager.unfreezeRegs(&.{rhs_reg}); - const dest_reg: Register = reg: { - const dest_reg = switch (mir_tag) { - .cmp_shifted_register => undefined, // cmp has no destination register - else => if (maybe_inst) |inst| blk: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const dest_reg = switch (mir_tag) { + .cmp_shifted_register => undefined, // cmp has no destination register + else => if (maybe_inst) |inst| blk: { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; - if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { - break :blk lhs_reg; - } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { - break :blk rhs_reg; - } else { - const raw_reg = try self.register_manager.allocReg(inst); - break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); - } - } else blk: { - const raw_reg = try self.register_manager.allocReg(null); + if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { + break :blk lhs_reg; + } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { + break :blk rhs_reg; + } else { + const raw_reg = try self.register_manager.allocReg(inst); break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); - }, - }; - break :reg switch (mir_tag) { - .smull, .umull => dest_reg.to64(), - else => dest_reg, - }; + } + } else blk: { + const raw_reg = try self.register_manager.allocReg(null); + break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); + }, }; if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); @@ -1341,9 +1335,7 @@ fn binOpRegister( .shift = .lsl, } }, .mul, - .smulh, .smull, - .umulh, .umull, .lsl_register, .asr_register, @@ -1932,16 +1924,38 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { self.register_manager.freezeRegs(&.{truncated_reg}); defer self.register_manager.unfreezeRegs(&.{truncated_reg}); - try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); - _ = try self.binOp( - .cmp_eq, - null, - dest, - .{ .register = truncated_reg }, - Type.usize, - Type.usize, + try self.truncRegister( + dest_reg.to32(), + truncated_reg.to32(), + int_info.signedness, + int_info.bits, ); + switch (int_info.signedness) { + .signed => { + _ = try self.addInst(.{ + .tag = .cmp_extended_register, + .data = .{ .rr_extend_shift = .{ + .rn = dest_reg.to64(), + .rm = truncated_reg.to32(), + .ext_type = .sxtw, + .imm3 = 0, + } }, + }); + }, + .unsigned => { + _ = try self.addInst(.{ + .tag = .cmp_extended_register, + .data = .{ .rr_extend_shift = .{ + .rn = dest_reg.to64(), + .rm = truncated_reg.to32(), + .ext_type = .uxtw, + .imm3 = 0, + } }, + }); + }, + } + try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq, diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 1393533a7f..959ca4037c 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -114,6 +114,12 @@ pub fn emitMir( .sub_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), .subs_shifted_register => try emit.mirAddSubtractShiftedRegister(inst), + .add_extended_register => try emit.mirAddSubtractExtendedRegister(inst), + .adds_extended_register => try emit.mirAddSubtractExtendedRegister(inst), + .sub_extended_register => try emit.mirAddSubtractExtendedRegister(inst), + .subs_extended_register => try emit.mirAddSubtractExtendedRegister(inst), + .cmp_extended_register => try emit.mirAddSubtractExtendedRegister(inst), + .cset => try emit.mirConditionalSelect(inst), .dbg_line => try emit.mirDbgLine(inst), @@ -732,6 +738,47 @@ fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void { } } +fn mirAddSubtractExtendedRegister(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + switch (tag) { + .add_extended_register, + .adds_extended_register, + .sub_extended_register, + .subs_extended_register, + => { + const rrr_extend_shift = emit.mir.instructions.items(.data)[inst].rrr_extend_shift; + const rd = rrr_extend_shift.rd; + const rn = rrr_extend_shift.rn; + const rm = rrr_extend_shift.rm; + const ext_type = rrr_extend_shift.ext_type; + const imm3 = rrr_extend_shift.imm3; + + switch (tag) { + .add_extended_register => try emit.writeInstruction(Instruction.addExtendedRegister(rd, rn, rm, ext_type, imm3)), + .adds_extended_register => try emit.writeInstruction(Instruction.addsExtendedRegister(rd, rn, rm, ext_type, imm3)), + .sub_extended_register => try emit.writeInstruction(Instruction.subExtendedRegister(rd, rn, rm, ext_type, imm3)), + .subs_extended_register => try emit.writeInstruction(Instruction.subsExtendedRegister(rd, rn, rm, ext_type, imm3)), + else => unreachable, + } + }, + .cmp_extended_register => { + const rr_extend_shift = emit.mir.instructions.items(.data)[inst].rr_extend_shift; + const rn = rr_extend_shift.rn; + const rm = rr_extend_shift.rm; + const ext_type = rr_extend_shift.ext_type; + const imm3 = rr_extend_shift.imm3; + const zr: Register = switch (rn.size()) { + 32 => .wzr, + 64 => .xzr, + else => unreachable, + }; + + try emit.writeInstruction(Instruction.subsExtendedRegister(zr, rn, rm, ext_type, imm3)); + }, + else => unreachable, + } +} + fn mirConditionalSelect(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; switch (tag) { @@ -1013,10 +1060,10 @@ fn mirDataProcessing3Source(emit: *Emit, inst: Mir.Inst.Index) !void { switch (tag) { .mul => try emit.writeInstruction(Instruction.mul(rrr.rd, rrr.rn, rrr.rm)), - .smulh => try emit.writeInstruction(Instruction.smulh(rrr.rd, rrr.rn, rrr.rm)), - .smull => try emit.writeInstruction(Instruction.smull(rrr.rd, rrr.rn, rrr.rm)), - .umulh => try emit.writeInstruction(Instruction.umulh(rrr.rd, rrr.rn, rrr.rm)), - .umull => try emit.writeInstruction(Instruction.umull(rrr.rd, rrr.rn, rrr.rm)), + .smulh => try emit.writeInstruction(Instruction.smulh(rrr.rd.to64(), rrr.rn.to64(), rrr.rm.to64())), + .smull => try emit.writeInstruction(Instruction.smull(rrr.rd.to64(), rrr.rn.to32(), rrr.rm.to32())), + .umulh => try emit.writeInstruction(Instruction.umulh(rrr.rd.to64(), rrr.rn.to64(), rrr.rm.to64())), + .umull => try emit.writeInstruction(Instruction.umull(rrr.rd.to64(), rrr.rn.to32(), rrr.rm.to32())), else => unreachable, } } diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index 1b27303419..1d66a69c8e 100644 --- a/src/arch/aarch64/Mir.zig +++ b/src/arch/aarch64/Mir.zig @@ -32,6 +32,10 @@ pub const Inst = struct { add_shifted_register, /// Add, update condition flags (shifted register) adds_shifted_register, + /// Add (extended register) + add_extended_register, + /// Add, update condition flags (extended register) + adds_extended_register, /// Bitwise AND (shifted register) and_shifted_register, /// Arithmetic Shift Right (immediate) @@ -56,6 +60,8 @@ pub const Inst = struct { cmp_immediate, /// Compare (shifted register) cmp_shifted_register, + /// Compare (extended register) + cmp_extended_register, /// Conditional set cset, /// Pseudo-instruction: End of prologue @@ -184,6 +190,10 @@ pub const Inst = struct { sub_shifted_register, /// Subtract, update condition flags (shifted register) subs_shifted_register, + /// Subtract (extended register) + sub_extended_register, + /// Subtract, update condition flags (extended register) + subs_extended_register, /// Supervisor Call svc, /// Test bits (immediate) @@ -300,6 +310,15 @@ pub const Inst = struct { imm6: u6, shift: bits.Instruction.AddSubtractShiftedRegisterShift, }, + /// Two registers with sign-extension (extension type and 3-bit shift amount) + /// + /// Used by e.g. cmp_extended_register + rr_extend_shift: struct { + rn: Register, + rm: Register, + ext_type: bits.Instruction.AddSubtractExtendedRegisterOption, + imm3: u3, + }, /// Two registers and a shift (logical instruction version) /// (shift type and 6-bit amount) /// @@ -356,6 +375,16 @@ pub const Inst = struct { imm6: u6, shift: bits.Instruction.AddSubtractShiftedRegisterShift, }, + /// Three registers with sign-extension (extension type and 3-bit shift amount) + /// + /// Used by e.g. add_extended_register + rrr_extend_shift: struct { + rd: Register, + rn: Register, + rm: Register, + ext_type: bits.Instruction.AddSubtractExtendedRegisterOption, + imm3: u3, + }, /// Three registers and a shift (logical instruction version) /// (shift type and 6-bit amount) /// diff --git a/src/arch/aarch64/bits.zig b/src/arch/aarch64/bits.zig index d8cb868d66..a3f5fbac51 100644 --- a/src/arch/aarch64/bits.zig +++ b/src/arch/aarch64/bits.zig @@ -330,6 +330,17 @@ pub const Instruction = union(enum) { op: u1, sf: u1, }, + add_subtract_extended_register: packed struct { + rd: u5, + rn: u5, + imm3: u3, + option: u3, + rm: u5, + fixed: u8 = 0b01011_00_1, + s: u1, + op: u1, + sf: u1, + }, conditional_branch: struct { cond: u4, o0: u1, @@ -495,6 +506,7 @@ pub const Instruction = union(enum) { .logical_immediate => |v| @bitCast(u32, v), .bitfield => |v| @bitCast(u32, v), .add_subtract_shifted_register => |v| @bitCast(u32, v), + .add_subtract_extended_register => |v| @bitCast(u32, v), // TODO once packed structs work, this can be refactored .conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25), .compare_and_branch => |v| @as(u32, v.rt) | (@as(u32, v.imm19) << 5) | (@as(u32, v.op) << 24) | (@as(u32, v.fixed) << 25) | (@as(u32, v.sf) << 31), @@ -1006,6 +1018,44 @@ pub const Instruction = union(enum) { }; } + pub const AddSubtractExtendedRegisterOption = enum(u3) { + uxtb, + uxth, + uxtw, + uxtx, // serves also as lsl + sxtb, + sxth, + sxtw, + sxtx, + }; + + fn addSubtractExtendedRegister( + op: u1, + s: u1, + rd: Register, + rn: Register, + rm: Register, + extend: AddSubtractExtendedRegisterOption, + imm3: u3, + ) Instruction { + return Instruction{ + .add_subtract_extended_register = .{ + .rd = rd.enc(), + .rn = rn.enc(), + .imm3 = imm3, + .option = @enumToInt(extend), + .rm = rm.enc(), + .s = s, + .op = op, + .sf = switch (rd.size()) { + 32 => 0b0, + 64 => 0b1, + else => unreachable, // unexpected register size + }, + }, + }; + } + fn conditionalBranch( o0: u1, o1: u1, @@ -1524,6 +1574,48 @@ pub const Instruction = union(enum) { return addSubtractShiftedRegister(0b1, 0b1, shift, rd, rn, rm, imm6); } + // Add/subtract (extended register) + + pub fn addExtendedRegister( + rd: Register, + rn: Register, + rm: Register, + extend: AddSubtractExtendedRegisterOption, + imm3: u3, + ) Instruction { + return addSubtractExtendedRegister(0b0, 0b0, rd, rn, rm, extend, imm3); + } + + pub fn addsExtendedRegister( + rd: Register, + rn: Register, + rm: Register, + extend: AddSubtractExtendedRegisterOption, + imm3: u3, + ) Instruction { + return addSubtractExtendedRegister(0b0, 0b1, rd, rn, rm, extend, imm3); + } + + pub fn subExtendedRegister( + rd: Register, + rn: Register, + rm: Register, + extend: AddSubtractExtendedRegisterOption, + imm3: u3, + ) Instruction { + return addSubtractExtendedRegister(0b1, 0b0, rd, rn, rm, extend, imm3); + } + + pub fn subsExtendedRegister( + rd: Register, + rn: Register, + rm: Register, + extend: AddSubtractExtendedRegisterOption, + imm3: u3, + ) Instruction { + return addSubtractExtendedRegister(0b1, 0b1, rd, rn, rm, extend, imm3); + } + // Conditional branch pub fn bCond(cond: Condition, offset: i21) Instruction { @@ -1565,11 +1657,12 @@ pub const Instruction = union(enum) { } pub fn smaddl(rd: Register, rn: Register, rm: Register, ra: Register) Instruction { + assert(rd.size() == 64 and rn.size() == 32 and rm.size() == 32 and ra.size() == 64); return dataProcessing3Source(0b00, 0b001, 0b0, rd, rn, rm, ra); } pub fn umaddl(rd: Register, rn: Register, rm: Register, ra: Register) Instruction { - assert(rd.size() == 64); + assert(rd.size() == 64 and rn.size() == 32 and rm.size() == 32 and ra.size() == 64); return dataProcessing3Source(0b00, 0b101, 0b0, rd, rn, rm, ra); } @@ -1837,6 +1930,10 @@ test "serialize instructions" { .inst = Instruction.smulh(.x0, .x1, .x2), .expected = 0b1_00_11011_0_10_00010_0_11111_00001_00000, }, + .{ // adds x0, x1, x2, sxtx + .inst = Instruction.addsExtendedRegister(.x0, .x1, .x2, .sxtx, 0), + .expected = 0b1_0_1_01011_00_1_00010_111_000_00001_00000, + }, }; for (testcases) |case| { From 3cef23129a37676cfd1e86efbc20d6089d7812e5 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 4 May 2022 23:12:59 +0200 Subject: [PATCH 1375/2031] test: test more int sizes for @mulWithOverflow builtin --- test/behavior/math.zig | 155 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 154 insertions(+), 1 deletion(-) diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 7a60ec8417..fdf8eb6a3f 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -664,7 +664,7 @@ test "small int addition" { try expect(result == 0); } -test "@mulWithOverflow" { +test "basic @mulWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var result: u8 = undefined; @@ -683,6 +683,159 @@ test "@mulWithOverflow" { try expect(result == 236); } +// TODO migrate to this for all backends once they handle more cases +test "extensive @mulWithOverflow" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + { + var a: u8 = 3; + var b: u8 = 85; + var res: u8 = undefined; + + try expect(!@mulWithOverflow(u8, a, b, &res)); + try expect(res == 255); + + b = 86; + try expect(@mulWithOverflow(u8, a, b, &res)); + try expect(res == 2); + } + + { + var a: i8 = 3; + var b: i8 = -42; + var res: i8 = undefined; + try expect(!@mulWithOverflow(i8, a, b, &res)); + try expect(res == -126); + + b = -43; + try expect(@mulWithOverflow(i8, a, b, &res)); + try expect(res == 127); + } + + { + var a: u16 = 3; + var b: u16 = 0x5555; + var res: u16 = undefined; + try expect(!@mulWithOverflow(u16, a, b, &res)); + try expect(res == 0xffff); + + b = 0x5556; + try expect(@mulWithOverflow(u16, a, b, &res)); + try expect(res == 2); + } + + { + var a: i16 = 3; + var b: i16 = -0x2aaa; + var res: i16 = undefined; + try expect(!@mulWithOverflow(i16, a, b, &res)); + try expect(res == -0x7ffe); + + b = -0x2aab; + try expect(@mulWithOverflow(i16, a, b, &res)); + try expect(res == 0x7fff); + } + + { + var a: u30 = 3; + var b: u30 = 0x15555555; + var res: u30 = undefined; + try expect(!@mulWithOverflow(u30, a, b, &res)); + try expect(res == 0x3fffffff); + + b = 0x15555556; + try expect(@mulWithOverflow(u30, a, b, &res)); + try expect(res == 2); + } + + { + var a: i30 = 3; + var b: i30 = -0xaaaaaaa; + var res: i30 = undefined; + try expect(!@mulWithOverflow(i30, a, b, &res)); + try expect(res == -0x1ffffffe); + + b = -0xaaaaaab; + try expect(@mulWithOverflow(i30, a, b, &res)); + try expect(res == 0x1fffffff); + } + + { + var a: u32 = 3; + var b: u32 = 0x55555555; + var res: u32 = undefined; + try expect(!@mulWithOverflow(u32, a, b, &res)); + try expect(res == 0xffffffff); + + b = 0x55555556; + try expect(@mulWithOverflow(u32, a, b, &res)); + try expect(res == 2); + } + + { + var a: i32 = 3; + var b: i32 = -0x2aaaaaaa; + var res: i32 = undefined; + try expect(!@mulWithOverflow(i32, a, b, &res)); + try expect(res == -0x7ffffffe); + + b = -0x2aaaaaab; + try expect(@mulWithOverflow(i32, a, b, &res)); + try expect(res == 0x7fffffff); + } + + { + var a: u62 = 3; + var b: u62 = 0x1555555555555555; + var res: u62 = undefined; + try expect(!@mulWithOverflow(u62, a, b, &res)); + try expect(res == 0x3fffffffffffffff); + + b = 0x1555555555555556; + try expect(@mulWithOverflow(u62, a, b, &res)); + try expect(res == 2); + } + + { + var a: i62 = 3; + var b: i62 = -0xaaaaaaaaaaaaaaa; + var res: i62 = undefined; + try expect(!@mulWithOverflow(i62, a, b, &res)); + try expect(res == -0x1ffffffffffffffe); + + b = -0xaaaaaaaaaaaaaab; + try expect(@mulWithOverflow(i62, a, b, &res)); + try expect(res == 0x1fffffffffffffff); + } + + { + var a: u64 = 3; + var b: u64 = 0x5555555555555555; + var res: u64 = undefined; + try expect(!@mulWithOverflow(u64, a, b, &res)); + try expect(res == 0xffffffffffffffff); + + b = 0x5555555555555556; + try expect(@mulWithOverflow(u64, a, b, &res)); + try expect(res == 2); + } + + { + var a: i64 = 3; + var b: i64 = -0x2aaaaaaaaaaaaaaa; + var res: i64 = undefined; + try expect(!@mulWithOverflow(i64, a, b, &res)); + try expect(res == -0x7ffffffffffffffe); + + b = -0x2aaaaaaaaaaaaaab; + try expect(@mulWithOverflow(i64, a, b, &res)); + try expect(res == 0x7fffffffffffffff); + } +} + test "@subWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO From eab5a1bd5a6b6fb6f968cb99895a862ad5639f15 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 5 May 2022 21:43:31 +0200 Subject: [PATCH 1376/2031] test: test bitwidths between 1...8 and 8...16 for mul_with_overflow --- test/behavior/math.zig | 48 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/test/behavior/math.zig b/test/behavior/math.zig index fdf8eb6a3f..1458fbc5ed 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -690,6 +690,30 @@ test "extensive @mulWithOverflow" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + { + var a: u5 = 3; + var b: u5 = 10; + var res: u5 = undefined; + try expect(!@mulWithOverflow(u5, a, b, &res)); + try expect(res == 30); + + b = 11; + try expect(@mulWithOverflow(u5, a, b, &res)); + try expect(res == 1); + } + + { + var a: i5 = 3; + var b: i5 = -5; + var res: i5 = undefined; + try expect(!@mulWithOverflow(i5, a, b, &res)); + try expect(res == -15); + + b = -6; + try expect(@mulWithOverflow(i5, a, b, &res)); + try expect(res == 14); + } + { var a: u8 = 3; var b: u8 = 85; @@ -715,6 +739,30 @@ test "extensive @mulWithOverflow" { try expect(res == 127); } + { + var a: u14 = 3; + var b: u14 = 0x1555; + var res: u14 = undefined; + try expect(!@mulWithOverflow(u14, a, b, &res)); + try expect(res == 0x3fff); + + b = 0x1556; + try expect(@mulWithOverflow(u14, a, b, &res)); + try expect(res == 2); + } + + { + var a: i14 = 3; + var b: i14 = -0xaaa; + var res: i14 = undefined; + try expect(!@mulWithOverflow(i14, a, b, &res)); + try expect(res == -0x1ffe); + + b = -0xaab; + try expect(@mulWithOverflow(i14, a, b, &res)); + try expect(res == 0x1fff); + } + { var a: u16 = 3; var b: u16 = 0x5555; From 90a8817f558bb5c5b4292c666b2ed61b4a415f8d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 5 May 2022 22:33:03 +0200 Subject: [PATCH 1377/2031] aarch64: ensure we set correct operand size at codegen stage --- src/arch/aarch64/CodeGen.zig | 9 +++++++-- src/arch/aarch64/Emit.zig | 8 ++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index d146724188..825bf51b1f 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -1335,8 +1335,6 @@ fn binOpRegister( .shift = .lsl, } }, .mul, - .smull, - .umull, .lsl_register, .asr_register, .lsr_register, @@ -1345,6 +1343,13 @@ fn binOpRegister( .rn = lhs_reg, .rm = rhs_reg, } }, + .smull, + .umull, + => .{ .rrr = .{ + .rd = dest_reg.to64(), + .rn = lhs_reg, + .rm = rhs_reg, + } }, .and_shifted_register, .orr_shifted_register, .eor_shifted_register, diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 959ca4037c..9e4993b0d9 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -1060,10 +1060,10 @@ fn mirDataProcessing3Source(emit: *Emit, inst: Mir.Inst.Index) !void { switch (tag) { .mul => try emit.writeInstruction(Instruction.mul(rrr.rd, rrr.rn, rrr.rm)), - .smulh => try emit.writeInstruction(Instruction.smulh(rrr.rd.to64(), rrr.rn.to64(), rrr.rm.to64())), - .smull => try emit.writeInstruction(Instruction.smull(rrr.rd.to64(), rrr.rn.to32(), rrr.rm.to32())), - .umulh => try emit.writeInstruction(Instruction.umulh(rrr.rd.to64(), rrr.rn.to64(), rrr.rm.to64())), - .umull => try emit.writeInstruction(Instruction.umull(rrr.rd.to64(), rrr.rn.to32(), rrr.rm.to32())), + .smulh => try emit.writeInstruction(Instruction.smulh(rrr.rd, rrr.rn, rrr.rm)), + .smull => try emit.writeInstruction(Instruction.smull(rrr.rd, rrr.rn, rrr.rm)), + .umulh => try emit.writeInstruction(Instruction.umulh(rrr.rd, rrr.rn, rrr.rm)), + .umull => try emit.writeInstruction(Instruction.umull(rrr.rd, rrr.rn, rrr.rm)), else => unreachable, } } From ebfc2825abeb839acb01ebf787156865e95c6bde Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 5 May 2022 10:02:33 +0200 Subject: [PATCH 1378/2031] x64: explicitly handle Vector vs Int types for overflow arith --- src/arch/x86_64/CodeGen.zig | 111 ++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 48 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index c8dab1c500..a6c812c75d 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1414,23 +1414,27 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + const result = if (self.liveness.isUnused(inst)) .dead else result: { + const ty = self.air.typeOf(bin_op.lhs); - if (self.liveness.isUnused(inst)) { - return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); - } + switch (ty.zigTypeTag()) { + .Vector => return self.fail("TODO implement add_with_overflow for Vector type", .{}), + .Int => { + const int_info = ty.intInfo(self.target.*); - const ty = self.air.typeOf(bin_op.lhs); - const signedness: std.builtin.Signedness = blk: { - if (ty.zigTypeTag() != .Int) { - return self.fail("TODO implement airAddWithOverflow for type {}", .{ty.fmtDebug()}); + if (int_info.bits > 64) { + return self.fail("TODO implement add_with_overflow for Ints larger than 64bits", .{}); + } + + const partial = try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); + const result: MCValue = switch (int_info.signedness) { + .signed => .{ .register_overflow_signed = partial.register }, + .unsigned => .{ .register_overflow_unsigned = partial.register }, + }; + break :result result; + }, + else => unreachable, } - break :blk ty.intInfo(self.target.*).signedness; - }; - - const partial = try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); - const result: MCValue = switch (signedness) { - .signed => .{ .register_overflow_signed = partial.register }, - .unsigned => .{ .register_overflow_unsigned = partial.register }, }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); @@ -1439,23 +1443,27 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + const result = if (self.liveness.isUnused(inst)) .dead else result: { + const ty = self.air.typeOf(bin_op.lhs); - if (self.liveness.isUnused(inst)) { - return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); - } + switch (ty.zigTypeTag()) { + .Vector => return self.fail("TODO implement sub_with_overflow for Vector type", .{}), + .Int => { + const int_info = ty.intInfo(self.target.*); - const ty = self.air.typeOf(bin_op.lhs); - const signedness: std.builtin.Signedness = blk: { - if (ty.zigTypeTag() != .Int) { - return self.fail("TODO implement airSubWithOverflow for type {}", .{ty.fmtDebug()}); + if (int_info.bits > 64) { + return self.fail("TODO implement sub_with_overflow for Ints larger than 64bits", .{}); + } + + const partial = try self.genSubOp(inst, bin_op.lhs, bin_op.rhs); + const result: MCValue = switch (int_info.signedness) { + .signed => .{ .register_overflow_signed = partial.register }, + .unsigned => .{ .register_overflow_unsigned = partial.register }, + }; + break :result result; + }, + else => unreachable, } - break :blk ty.intInfo(self.target.*).signedness; - }; - - const partial = try self.genSubOp(inst, bin_op.lhs, bin_op.rhs); - const result: MCValue = switch (signedness) { - .signed => .{ .register_overflow_signed = partial.register }, - .unsigned => .{ .register_overflow_unsigned = partial.register }, }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); @@ -1466,30 +1474,37 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const result = if (self.liveness.isUnused(inst)) .dead else result: { const ty = self.air.typeOf(bin_op.lhs); - const signedness: std.builtin.Signedness = blk: { - if (ty.zigTypeTag() != .Int) { - return self.fail("TODO implement airMulWithOverflow for type {}", .{ty.fmtDebug()}); - } - break :blk ty.intInfo(self.target.*).signedness; - }; - // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. - try self.register_manager.getReg(.rax, inst); - try self.register_manager.getReg(.rdx, null); - self.register_manager.freezeRegs(&.{ .rax, .rdx }); - defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); + switch (ty.zigTypeTag()) { + .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}), + .Int => { + const int_info = ty.intInfo(self.target.*); - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); + if (int_info.bits > 64) { + return self.fail("TODO implement mul_with_overflow for Ints larger than 64bits", .{}); + } - try self.genIntMulDivOpMir(switch (signedness) { - .signed => .imul, - .unsigned => .mul, - }, ty, signedness, lhs, rhs); + // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. + try self.register_manager.getReg(.rax, inst); + try self.register_manager.getReg(.rdx, null); + self.register_manager.freezeRegs(&.{ .rax, .rdx }); + defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); - switch (signedness) { - .signed => break :result MCValue{ .register_overflow_signed = .rax }, - .unsigned => break :result MCValue{ .register_overflow_unsigned = .rax }, + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + try self.genIntMulDivOpMir(switch (int_info.signedness) { + .signed => .imul, + .unsigned => .mul, + }, ty, int_info.signedness, lhs, rhs); + + const result: MCValue = switch (int_info.signedness) { + .signed => .{ .register_overflow_signed = .rax }, + .unsigned => .{ .register_overflow_unsigned = .rax }, + }; + break :result result; + }, + else => unreachable, } }; From 0fc1e8b54f2022081a642c13eb74dfdda5aad867 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 5 May 2022 18:59:57 +0200 Subject: [PATCH 1379/2031] x64: handle signed mul_with_overflow for non-pow-2 ints --- src/arch/x86_64/CodeGen.zig | 183 +++++++++++++++++++++++++++--------- src/arch/x86_64/Emit.zig | 2 +- 2 files changed, 142 insertions(+), 43 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index a6c812c75d..bcbb1bd56e 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1086,29 +1086,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { // have to be removed. this only happens if the dst if not a power-of-two size. const dst_bit_size = dst_ty.bitSize(self.target.*); if (!math.isPowerOfTwo(dst_bit_size) or dst_bit_size < 8) { - const max_reg_bit_width = Register.rax.size(); - const shift = @intCast(u6, max_reg_bit_width - dst_ty.bitSize(self.target.*)); - const mask = (~@as(u64, 0)) >> shift; - try self.genBinMathOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask }); - - if (src_ty.intInfo(self.target.*).signedness == .signed) { - _ = try self.addInst(.{ - .tag = .sal, - .ops = (Mir.Ops{ - .reg1 = reg, - .flags = 0b10, - }).encode(), - .data = .{ .imm = shift }, - }); - _ = try self.addInst(.{ - .tag = .sar, - .ops = (Mir.Ops{ - .reg1 = reg, - .flags = 0b10, - }).encode(), - .data = .{ .imm = shift }, - }); - } + try self.truncateRegister(dst_ty, reg); } return self.finishAir(inst, .{ .register = reg }, .{ ty_op.operand, .none, .none }); @@ -1478,31 +1456,119 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { switch (ty.zigTypeTag()) { .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}), .Int => { + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = null; + const int_info = ty.intInfo(self.target.*); if (int_info.bits > 64) { return self.fail("TODO implement mul_with_overflow for Ints larger than 64bits", .{}); } - // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. - try self.register_manager.getReg(.rax, inst); - try self.register_manager.getReg(.rdx, null); - self.register_manager.freezeRegs(&.{ .rax, .rdx }); - defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); + if (math.isPowerOfTwo(int_info.bits)) { + // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. + try self.register_manager.getReg(.rax, inst); + try self.register_manager.getReg(.rdx, null); + self.register_manager.freezeRegs(&.{ .rax, .rdx }); + defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); + + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + try self.genIntMulDivOpMir(switch (int_info.signedness) { + .signed => .imul, + .unsigned => .mul, + }, ty, int_info.signedness, lhs, rhs); + + const result: MCValue = switch (int_info.signedness) { + .signed => .{ .register_overflow_signed = .rax }, + .unsigned => .{ .register_overflow_unsigned = .rax }, + }; + break :result result; + } const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - try self.genIntMulDivOpMir(switch (int_info.signedness) { - .signed => .imul, - .unsigned => .mul, - }, ty, int_info.signedness, lhs, rhs); + rhs.freezeIfRegister(&self.register_manager); + defer rhs.unfreezeIfRegister(&self.register_manager); - const result: MCValue = switch (int_info.signedness) { - .signed => .{ .register_overflow_signed = .rax }, - .unsigned => .{ .register_overflow_unsigned = .rax }, + // TODO check if we could reuse rhs instead, and swap the values out. + const dst_mcv = blk: { + if (self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { + if (lhs.isRegister()) break :blk lhs; + } + break :blk MCValue{ .register = try self.copyToTmpRegister(ty, lhs) }; }; - break :result result; + dst_mcv.freezeIfRegister(&self.register_manager); + defer dst_mcv.unfreezeIfRegister(&self.register_manager); + + const rhs_mcv = blk: { + if (rhs.isRegister() or rhs.isMemory()) break :blk rhs; + break :blk MCValue{ .register = try self.copyToTmpRegister(ty, rhs) }; + }; + rhs_mcv.freezeIfRegister(&self.register_manager); + defer rhs_mcv.unfreezeIfRegister(&self.register_manager); + + const tuple_ty = self.air.typeOfIndex(inst); + const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); + const tuple_align = tuple_ty.abiAlignment(self.target.*); + const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)); + + const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align)); + const extended_ty = switch (int_info.signedness) { + .signed => Type.isize, + .unsigned => ty, + }; + + try self.genIntMulComplexOpMir(extended_ty, dst_mcv, rhs_mcv); + + const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }); + self.register_manager.freezeRegs(&temp_regs); + defer self.register_manager.unfreezeRegs(&temp_regs); + + const overflow_reg = temp_regs[0]; + const flags: u2 = switch (int_info.signedness) { + .signed => 0b00, + .unsigned => 0b10, + }; + _ = try self.addInst(.{ + .tag = .cond_set_byte_overflow, + .ops = (Mir.Ops{ + .reg1 = overflow_reg.to8(), + .flags = flags, + }).encode(), + .data = undefined, + }); + + const scratch_reg = temp_regs[1]; + try self.genSetReg(extended_ty, scratch_reg, dst_mcv); + try self.truncateRegister(ty, scratch_reg); + try self.genBinMathOpMir(.cmp, extended_ty, dst_mcv, .{ .register = scratch_reg }); + + const eq_reg = temp_regs[2]; + _ = try self.addInst(.{ + .tag = .cond_set_byte_eq_ne, + .ops = (Mir.Ops{ + .reg1 = eq_reg.to8(), + .flags = 0b00, + }).encode(), + .data = undefined, + }); + + try self.genBinMathOpMir( + .@"or", + Type.u8, + .{ .register = overflow_reg }, + .{ .register = eq_reg }, + ); + + try self.genSetStack(ty, stack_offset, .{ .register = scratch_reg }, .{}); + try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ + .register = overflow_reg.to8(), + }, .{}); + + break :result MCValue{ .stack_offset = stack_offset }; }, else => unreachable, } @@ -1648,7 +1714,7 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa }).encode(), .data = undefined, }); - try self.genBinMathOpMir(.add, Type.isize, .{ .register = divisor.to64() }, .{ .register = .rax }); + try self.genBinMathOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax }); return MCValue{ .register = divisor }; } @@ -2222,8 +2288,8 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { } // TODO we could allocate register here, but need to expect addr register and potentially // offset register. - try self.genBinMathOpMir(.add, slice_ptr_field_type, .{ .register = addr_reg.to64() }, .{ - .register = offset_reg.to64(), + try self.genBinMathOpMir(.add, slice_ptr_field_type, .{ .register = addr_reg }, .{ + .register = offset_reg, }); return MCValue{ .register = addr_reg.to64() }; } @@ -2315,7 +2381,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { // TODO we could allocate register here, but need to expect addr register and potentially // offset register. const dst_mcv = try self.allocRegOrMem(inst, false); - try self.genBinMathOpMir(.add, array_ty, .{ .register = addr_reg.to64() }, .{ .register = offset_reg.to64() }); + try self.genBinMathOpMir(.add, Type.usize, .{ .register = addr_reg }, .{ .register = offset_reg }); try self.load(dst_mcv, .{ .register = addr_reg.to64() }, array_ty); break :result dst_mcv; }; @@ -3178,8 +3244,8 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC _ = try self.addInst(.{ .tag = mir_tag, .ops = (Mir.Ops{ - .reg1 = registerAlias(dst_reg, @divExact(src_reg.size(), 8)), - .reg2 = src_reg, + .reg1 = registerAlias(dst_reg, abi_size), + .reg2 = registerAlias(src_reg, abi_size), }).encode(), .data = undefined, }); @@ -6531,3 +6597,36 @@ fn shiftRegister(self: *Self, reg: Register, shift: u8) !void { }); } } + +/// Truncates the value in the register in place. +/// Clobbers any remaining bits. +fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { + const int_info = ty.intInfo(self.target.*); + const max_reg_bit_width = Register.rax.size(); + switch (int_info.signedness) { + .signed => { + const shift = @intCast(u6, max_reg_bit_width - int_info.bits); + _ = try self.addInst(.{ + .tag = .sal, + .ops = (Mir.Ops{ + .reg1 = reg.to64(), + .flags = 0b10, + }).encode(), + .data = .{ .imm = shift }, + }); + _ = try self.addInst(.{ + .tag = .sar, + .ops = (Mir.Ops{ + .reg1 = reg.to64(), + .flags = 0b10, + }).encode(), + .data = .{ .imm = shift }, + }); + }, + .unsigned => { + const shift = @intCast(u6, max_reg_bit_width - int_info.bits); + const mask = (~@as(u64, 0)) >> shift; + try self.genBinMathOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask }); + }, + } +} diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 6af2c07974..f83557f5ad 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -1383,7 +1383,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .sub => OpCode.oneByte(if (is_one_byte) 0x2a else 0x2b), .xor => OpCode.oneByte(if (is_one_byte) 0x32 else 0x33), .@"and" => OpCode.oneByte(if (is_one_byte) 0x22 else 0x23), - .@"or" => OpCode.oneByte(if (is_one_byte) 0x0b else 0x0b), + .@"or" => OpCode.oneByte(if (is_one_byte) 0x0a else 0x0b), .sbb => OpCode.oneByte(if (is_one_byte) 0x1a else 0x1b), .cmp => OpCode.oneByte(if (is_one_byte) 0x3a else 0x3b), .mov => OpCode.oneByte(if (is_one_byte) 0x8a else 0x8b), From 0728847ce74aace8fe8e2a8f61da09002cd4ad7e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 5 May 2022 19:29:24 +0200 Subject: [PATCH 1380/2031] x64: handle unsigned mul_with_overflow for non-pow-2 ints --- src/arch/x86_64/CodeGen.zig | 78 ++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index bcbb1bd56e..6a6e6ad6cb 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1487,28 +1487,52 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { break :result result; } - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); + const dst_reg: Register = dst_reg: { + switch (int_info.signedness) { + .signed => { + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); - rhs.freezeIfRegister(&self.register_manager); - defer rhs.unfreezeIfRegister(&self.register_manager); + rhs.freezeIfRegister(&self.register_manager); + defer rhs.unfreezeIfRegister(&self.register_manager); - // TODO check if we could reuse rhs instead, and swap the values out. - const dst_mcv = blk: { - if (self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { - if (lhs.isRegister()) break :blk lhs; + // TODO check if we could reuse rhs instead, and swap the values out. + const dst_reg: Register = blk: { + if (self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { + if (lhs.isRegister()) break :blk lhs.register; + } + break :blk try self.copyToTmpRegister(ty, lhs); + }; + self.register_manager.freezeRegs(&.{dst_reg}); + + const rhs_mcv = blk: { + if (rhs.isRegister() or rhs.isMemory()) break :blk rhs; + break :blk MCValue{ .register = try self.copyToTmpRegister(ty, rhs) }; + }; + rhs_mcv.freezeIfRegister(&self.register_manager); + defer rhs_mcv.unfreezeIfRegister(&self.register_manager); + + try self.genIntMulComplexOpMir(Type.isize, .{ .register = dst_reg }, rhs_mcv); + + break :dst_reg dst_reg; + }, + .unsigned => { + // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. + try self.register_manager.getReg(.rax, null); + try self.register_manager.getReg(.rdx, null); + self.register_manager.freezeRegs(&.{ .rax, .rdx }); + defer self.register_manager.unfreezeRegs(&.{.rdx}); + + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + try self.genIntMulDivOpMir(.mul, ty, .unsigned, lhs, rhs); + + break :dst_reg registerAlias(.rax, @intCast(u32, ty.abiSize(self.target.*))); + }, } - break :blk MCValue{ .register = try self.copyToTmpRegister(ty, lhs) }; }; - dst_mcv.freezeIfRegister(&self.register_manager); - defer dst_mcv.unfreezeIfRegister(&self.register_manager); - - const rhs_mcv = blk: { - if (rhs.isRegister() or rhs.isMemory()) break :blk rhs; - break :blk MCValue{ .register = try self.copyToTmpRegister(ty, rhs) }; - }; - rhs_mcv.freezeIfRegister(&self.register_manager); - defer rhs_mcv.unfreezeIfRegister(&self.register_manager); + defer self.register_manager.unfreezeRegs(&.{dst_reg}); const tuple_ty = self.air.typeOfIndex(inst); const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); @@ -1521,8 +1545,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { .unsigned => ty, }; - try self.genIntMulComplexOpMir(extended_ty, dst_mcv, rhs_mcv); - const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }); self.register_manager.freezeRegs(&temp_regs); defer self.register_manager.unfreezeRegs(&temp_regs); @@ -1542,9 +1564,14 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { }); const scratch_reg = temp_regs[1]; - try self.genSetReg(extended_ty, scratch_reg, dst_mcv); + try self.genSetReg(extended_ty, scratch_reg, .{ .register = dst_reg }); try self.truncateRegister(ty, scratch_reg); - try self.genBinMathOpMir(.cmp, extended_ty, dst_mcv, .{ .register = scratch_reg }); + try self.genBinMathOpMir( + .cmp, + extended_ty, + .{ .register = dst_reg }, + .{ .register = scratch_reg }, + ); const eq_reg = temp_regs[2]; _ = try self.addInst(.{ @@ -6626,7 +6653,12 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { .unsigned => { const shift = @intCast(u6, max_reg_bit_width - int_info.bits); const mask = (~@as(u64, 0)) >> shift; - try self.genBinMathOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask }); + if (int_info.bits <= 32) { + try self.genBinMathOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask }); + } else { + const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask }); + try self.genBinMathOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .register = tmp_reg }); + } }, } } From e3160ec573882108e2b51cefcf3abc5b346f70c0 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 5 May 2022 21:35:59 +0200 Subject: [PATCH 1381/2031] x64: mul_with_overflow: cannot reuse operand if not the result --- src/arch/x86_64/CodeGen.zig | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 6a6e6ad6cb..767a67bf92 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1496,11 +1496,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { rhs.freezeIfRegister(&self.register_manager); defer rhs.unfreezeIfRegister(&self.register_manager); - // TODO check if we could reuse rhs instead, and swap the values out. const dst_reg: Register = blk: { - if (self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { - if (lhs.isRegister()) break :blk lhs.register; - } + if (lhs.isRegister()) break :blk lhs.register; break :blk try self.copyToTmpRegister(ty, lhs); }; self.register_manager.freezeRegs(&.{dst_reg}); From c592f0ca21e8274681fc1dc3c4597ce96ab2096e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 5 May 2022 22:53:11 +0200 Subject: [PATCH 1382/2031] test: pass extended mul_with_overflow tests on x64 --- test/behavior/math.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 1458fbc5ed..760dece01e 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -686,7 +686,6 @@ test "basic @mulWithOverflow" { // TODO migrate to this for all backends once they handle more cases test "extensive @mulWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO From 496eb69273d363f21ca3923f00714b229882e42a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Apr 2022 20:28:36 -0700 Subject: [PATCH 1383/2031] CI: add non-LLVM backends to the test matrix We can't yet run the behavior tests with stage3, but at least we can run them with stage2, and we can use the proper test matrix. This commit also adds use_llvm and ofmt to the zig build system. --- build.zig | 12 +++ ci/zinc/linux_test.sh | 26 +++--- lib/std/build.zig | 14 ++++ test/tests.zig | 179 +++++++++++++++++++++++++++++++----------- 4 files changed, 168 insertions(+), 63 deletions(-) diff --git a/build.zig b/build.zig index 4d3cf492bd..e9f0b69400 100644 --- a/build.zig +++ b/build.zig @@ -445,6 +445,9 @@ pub fn build(b: *Builder) !void { false, // skip_single_threaded skip_non_native, skip_libc, + skip_stage1, + omit_stage2, + is_stage1, )); toolchain_step.dependOn(tests.addPkgTests( @@ -457,6 +460,9 @@ pub fn build(b: *Builder) !void { true, // skip_single_threaded skip_non_native, true, // skip_libc + skip_stage1, + omit_stage2 or true, // TODO get these all passing + is_stage1, )); toolchain_step.dependOn(tests.addPkgTests( @@ -469,6 +475,9 @@ pub fn build(b: *Builder) !void { true, // skip_single_threaded skip_non_native, true, // skip_libc + skip_stage1, + omit_stage2 or true, // TODO get these all passing + is_stage1, )); toolchain_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); @@ -494,6 +503,9 @@ pub fn build(b: *Builder) !void { false, skip_non_native, skip_libc, + skip_stage1, + omit_stage2 or true, // TODO get these all passing + is_stage1, ); const test_step = b.step("test", "Run all the tests"); diff --git a/ci/zinc/linux_test.sh b/ci/zinc/linux_test.sh index 180589cc8e..01aee9a502 100755 --- a/ci/zinc/linux_test.sh +++ b/ci/zinc/linux_test.sh @@ -48,25 +48,19 @@ cd $WORKSPACE $ZIG fmt --check . --exclude test/cases/ # Build stage2 standalone so that we can test stage2 against stage2 compiler-rt. -$ZIG build -p stage2 -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL" +$ZIG build -p stage2 -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL" # Ensure that stage2 can build itself. -./stage2/bin/zig build -p stage3 -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL" +stage2/bin/zig build -p stage3 -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL" +stage2/bin/zig build # test building self-hosted without LLVM +stage2/bin/zig build -Dtarget=arm-linux-musleabihf # test building self-hosted for 32-bit arm -stage2/bin/zig test test/behavior.zig -I test -fLLVM -stage2/bin/zig test test/behavior.zig -I test -fno-LLVM -stage2/bin/zig test test/behavior.zig -I test -fLLVM -target aarch64-linux --test-cmd qemu-aarch64 --test-cmd-bin -stage2/bin/zig test test/behavior.zig -I test -fno-LLVM -target aarch64-linux --test-cmd qemu-aarch64 --test-cmd-bin -stage2/bin/zig test test/behavior.zig -I test -ofmt=c -stage2/bin/zig test test/behavior.zig -I test -fno-LLVM -target wasm32-wasi --test-cmd wasmtime --test-cmd-bin -stage2/bin/zig test test/behavior.zig -I test -fLLVM -target wasm32-wasi --test-cmd wasmtime --test-cmd-bin -stage2/bin/zig test test/behavior.zig -I test -fno-LLVM -target arm-linux --test-cmd qemu-arm --test-cmd-bin -stage2/bin/zig test test/behavior.zig -I test -fLLVM -target aarch64-macos --test-no-exec -stage2/bin/zig test test/behavior.zig -I test -fno-LLVM -target aarch64-macos --test-no-exec -stage2/bin/zig test test/behavior.zig -I test -fLLVM -target x86_64-macos --test-no-exec -stage2/bin/zig test test/behavior.zig -I test -fno-LLVM -target x86_64-macos --test-no-exec +# Here we use stage2 instead of stage3 because of two bugs remaining: +# * https://github.com/ziglang/zig/issues/11367 (and corresponding workaround in compiler source) +# * https://github.com/ziglang/zig/pull/11492#issuecomment-1112871321 +stage2/bin/zig build test-behavior -fqemu -fwasmtime -$ZIG build test-behavior -fqemu -fwasmtime +$ZIG build test-behavior -fqemu -fwasmtime -Domit-stage2 $ZIG build test-compiler-rt -fqemu -fwasmtime $ZIG build test-std -fqemu -fwasmtime $ZIG build test-minilibc -fqemu -fwasmtime @@ -79,8 +73,6 @@ $ZIG build test-runtime-safety -fqemu -fwasmtime $ZIG build test-translate-c -fqemu -fwasmtime $ZIG build test-run-translated-c -fqemu -fwasmtime $ZIG build docs -fqemu -fwasmtime -$ZIG build # test building self-hosted without LLVM -$ZIG build -Dtarget=arm-linux-musleabihf # test building self-hosted for 32-bit arm $ZIG build test-fmt -fqemu -fwasmtime $ZIG build test-stage2 -fqemu -fwasmtime diff --git a/lib/std/build.zig b/lib/std/build.zig index 38744cea1e..b50678a0e5 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1590,6 +1590,8 @@ pub const LibExeObjStep = struct { want_lto: ?bool = null, use_stage1: ?bool = null, + use_llvm: ?bool = null, + ofmt: ?std.Target.ObjectFormat = null, output_path_source: GeneratedFile, output_lib_path_source: GeneratedFile, @@ -2351,6 +2353,18 @@ pub const LibExeObjStep = struct { } } + if (self.use_llvm) |use_llvm| { + if (use_llvm) { + try zig_args.append("-fLLVM"); + } else { + try zig_args.append("-fno-LLVM"); + } + } + + if (self.ofmt) |ofmt| { + try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)})); + } + if (self.entry_symbol_name) |entry| { try zig_args.append("--entry"); try zig_args.append(entry); diff --git a/test/tests.zig b/test/tests.zig index ee4b922021..62c437e7f7 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -34,6 +34,7 @@ const TestTarget = struct { link_libc: bool = false, single_threaded: bool = false, disable_native: bool = false, + backend: ?std.builtin.CompilerBackend = null, }; const test_targets = blk: { @@ -42,15 +43,73 @@ const test_targets = blk: { // lot of branches) @setEvalBranchQuota(50000); break :blk [_]TestTarget{ - TestTarget{}, - TestTarget{ + .{}, + .{ .link_libc = true, }, - TestTarget{ + .{ .single_threaded = true, }, - TestTarget{ + .{ + .link_libc = true, + .backend = .stage2_c, + }, + .{ + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .none, + }, + .backend = .stage2_x86_64, + }, + .{ + .target = .{ + .cpu_arch = .aarch64, + .os_tag = .linux, + }, + .backend = .stage2_aarch64, + }, + .{ + .target = .{ + .cpu_arch = .wasm32, + .os_tag = .wasi, + }, + .single_threaded = true, + .backend = .stage2_wasm, + }, + .{ + .target = .{ + .cpu_arch = .arm, + .os_tag = .linux, + }, + .backend = .stage2_wasm, + }, + .{ + .target = CrossTarget.parse(.{ + .arch_os_abi = "arm-linux-none", + .cpu_features = "generic+v8a", + }) catch unreachable, + .backend = .stage2_arm, + }, + .{ + .target = .{ + .cpu_arch = .aarch64, + .os_tag = .macos, + .abi = .gnu, + }, + .backend = .stage2_aarch64, + }, + .{ + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .macos, + .abi = .gnu, + }, + .backend = .stage2_x86_64, + }, + + .{ .target = .{ .cpu_arch = .wasm32, .os_tag = .wasi, @@ -58,7 +117,7 @@ const test_targets = blk: { .link_libc = false, .single_threaded = true, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .wasm32, .os_tag = .wasi, @@ -67,14 +126,14 @@ const test_targets = blk: { .single_threaded = true, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .none, }, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .x86_64, .os_tag = .linux, @@ -82,7 +141,7 @@ const test_targets = blk: { }, .link_libc = true, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .x86_64, .os_tag = .linux, @@ -91,14 +150,14 @@ const test_targets = blk: { .link_libc = true, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .i386, .os_tag = .linux, .abi = .none, }, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .i386, .os_tag = .linux, @@ -106,7 +165,7 @@ const test_targets = blk: { }, .link_libc = true, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .i386, .os_tag = .linux, @@ -115,14 +174,14 @@ const test_targets = blk: { .link_libc = true, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .none, }, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .aarch64, .os_tag = .linux, @@ -130,7 +189,7 @@ const test_targets = blk: { }, .link_libc = true, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .aarch64, .os_tag = .linux, @@ -138,7 +197,7 @@ const test_targets = blk: { }, .link_libc = true, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .aarch64, .os_tag = .windows, @@ -147,13 +206,13 @@ const test_targets = blk: { .link_libc = true, }, - TestTarget{ + .{ .target = CrossTarget.parse(.{ .arch_os_abi = "arm-linux-none", .cpu_features = "generic+v8a", }) catch unreachable, }, - TestTarget{ + .{ .target = CrossTarget.parse(.{ .arch_os_abi = "arm-linux-musleabihf", .cpu_features = "generic+v8a", @@ -161,7 +220,7 @@ const test_targets = blk: { .link_libc = true, }, // https://github.com/ziglang/zig/issues/3287 - //TestTarget{ + //.{ // .target = CrossTarget.parse(.{ // .arch_os_abi = "arm-linux-gnueabihf", // .cpu_features = "generic+v8a", @@ -169,7 +228,7 @@ const test_targets = blk: { // .link_libc = true, //}, - TestTarget{ + .{ .target = .{ .cpu_arch = .mips, .os_tag = .linux, @@ -177,7 +236,7 @@ const test_targets = blk: { }, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .mips, .os_tag = .linux, @@ -187,7 +246,7 @@ const test_targets = blk: { }, // https://github.com/ziglang/zig/issues/4927 - //TestTarget{ + //.{ // .target = .{ // .cpu_arch = .mips, // .os_tag = .linux, @@ -196,7 +255,7 @@ const test_targets = blk: { // .link_libc = true, //}, - TestTarget{ + .{ .target = .{ .cpu_arch = .mipsel, .os_tag = .linux, @@ -204,7 +263,7 @@ const test_targets = blk: { }, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .mipsel, .os_tag = .linux, @@ -214,7 +273,7 @@ const test_targets = blk: { }, // https://github.com/ziglang/zig/issues/4927 - //TestTarget{ + //.{ // .target = .{ // .cpu_arch = .mipsel, // .os_tag = .linux, @@ -223,14 +282,14 @@ const test_targets = blk: { // .link_libc = true, //}, - TestTarget{ + .{ .target = .{ .cpu_arch = .powerpc, .os_tag = .linux, .abi = .none, }, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .powerpc, .os_tag = .linux, @@ -239,7 +298,7 @@ const test_targets = blk: { .link_libc = true, }, // https://github.com/ziglang/zig/issues/2256 - //TestTarget{ + //.{ // .target = .{ // .cpu_arch = .powerpc, // .os_tag = .linux, @@ -248,7 +307,7 @@ const test_targets = blk: { // .link_libc = true, //}, - TestTarget{ + .{ .target = .{ .cpu_arch = .riscv64, .os_tag = .linux, @@ -256,7 +315,7 @@ const test_targets = blk: { }, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .riscv64, .os_tag = .linux, @@ -266,7 +325,7 @@ const test_targets = blk: { }, // https://github.com/ziglang/zig/issues/3340 - //TestTarget{ + //.{ // .target = .{ // .cpu_arch = .riscv64, // .os = .linux, @@ -275,7 +334,7 @@ const test_targets = blk: { // .link_libc = true, //}, - TestTarget{ + .{ .target = .{ .cpu_arch = .x86_64, .os_tag = .macos, @@ -283,7 +342,7 @@ const test_targets = blk: { }, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .aarch64, .os_tag = .macos, @@ -291,7 +350,7 @@ const test_targets = blk: { }, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .i386, .os_tag = .windows, @@ -299,7 +358,7 @@ const test_targets = blk: { }, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .x86_64, .os_tag = .windows, @@ -307,7 +366,7 @@ const test_targets = blk: { }, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .i386, .os_tag = .windows, @@ -316,7 +375,7 @@ const test_targets = blk: { .link_libc = true, }, - TestTarget{ + .{ .target = .{ .cpu_arch = .x86_64, .os_tag = .windows, @@ -326,38 +385,38 @@ const test_targets = blk: { }, // Do the release tests last because they take a long time - TestTarget{ + .{ .mode = .ReleaseFast, }, - TestTarget{ + .{ .link_libc = true, .mode = .ReleaseFast, }, - TestTarget{ + .{ .mode = .ReleaseFast, .single_threaded = true, }, - TestTarget{ + .{ .mode = .ReleaseSafe, }, - TestTarget{ + .{ .link_libc = true, .mode = .ReleaseSafe, }, - TestTarget{ + .{ .mode = .ReleaseSafe, .single_threaded = true, }, - TestTarget{ + .{ .mode = .ReleaseSmall, }, - TestTarget{ + .{ .link_libc = true, .mode = .ReleaseSmall, }, - TestTarget{ + .{ .mode = .ReleaseSmall, .single_threaded = true, }, @@ -524,6 +583,9 @@ pub fn addPkgTests( skip_single_threaded: bool, skip_non_native: bool, skip_libc: bool, + skip_stage1: bool, + skip_stage2: bool, + is_stage1: bool, ) *build.Step { const step = b.step(b.fmt("test-{s}", .{name}), desc); @@ -549,6 +611,11 @@ pub fn addPkgTests( continue; } + if (test_target.backend) |backend| switch (backend) { + .stage1 => if (skip_stage1) continue, + else => if (skip_stage2) continue, + } else if (is_stage1 and skip_stage1) continue; + const want_this_mode = for (modes) |m| { if (m == test_target.mode) break true; } else false; @@ -565,12 +632,14 @@ pub fn addPkgTests( const these_tests = b.addTest(root_src); const single_threaded_txt = if (test_target.single_threaded) "single" else "multi"; - these_tests.setNamePrefix(b.fmt("{s}-{s}-{s}-{s}-{s} ", .{ + const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default"; + these_tests.setNamePrefix(b.fmt("{s}-{s}-{s}-{s}-{s}-{s} ", .{ name, triple_prefix, @tagName(test_target.mode), libc_prefix, single_threaded_txt, + backend_txt, })); these_tests.single_threaded = test_target.single_threaded; these_tests.setFilter(test_filter); @@ -581,6 +650,24 @@ pub fn addPkgTests( } these_tests.overrideZigLibDir("lib"); these_tests.addIncludePath("test"); + if (test_target.backend) |backend| switch (backend) { + .stage1 => { + these_tests.use_stage1 = true; + }, + .stage2_llvm => { + these_tests.use_stage1 = false; + these_tests.use_llvm = true; + }, + .stage2_c => { + these_tests.use_stage1 = false; + these_tests.use_llvm = false; + these_tests.ofmt = .c; + }, + else => { + these_tests.use_stage1 = false; + these_tests.use_llvm = false; + }, + }; step.dependOn(&these_tests.step); } From 413b789e06bfc98bd285f0a340bd537b1d2c9dec Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 5 May 2022 11:32:53 -0700 Subject: [PATCH 1384/2031] std.os.linux.arm-eabi: upgrade to new fn ptr semantics --- lib/std/os/linux/arm-eabi.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/std/os/linux/arm-eabi.zig b/lib/std/os/linux/arm-eabi.zig index 613957fa25..7f4a7385f2 100644 --- a/lib/std/os/linux/arm-eabi.zig +++ b/lib/std/os/linux/arm-eabi.zig @@ -99,7 +99,10 @@ pub fn syscall6( /// This matches the libc clone function. pub extern fn clone( - func: fn (arg: usize) callconv(.C) u8, + func: switch (@import("builtin").zig_backend) { + .stage1 => fn (arg: usize) callconv(.C) u8, + else => *const fn (arg: usize) callconv(.C) u8, + }, stack: usize, flags: u32, arg: usize, From edb3adaa33f943d0c64071fbf9d43aadadaa1e95 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 29 Apr 2022 11:24:30 +0200 Subject: [PATCH 1385/2031] stage2,llvm: handle softfloats in @intToFloat and @floatToInt If the hw doesn't have support for exotic floating-point types such as `f80`, we lower the call to a compiler-rt function call instead. I've added a behavior test specifically targeting this use case which now passes on `aarch64-macos`. Additionally, this commit makes it possible to successfully build stage3 on `aarch64-macos`. We can print the compiler's help message, however, building with it needs a little bit more love still. --- src/codegen/llvm.zig | 163 +++++++++++++++++++++++++++++++++++++---- test/behavior/cast.zig | 36 +++++++++ 2 files changed, 185 insertions(+), 14 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 426ef7c378..374ae9b38e 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4229,41 +4229,166 @@ pub const FuncGen = struct { return self.builder.buildInsertValue(partial, len, 1, ""); } + inline fn isPowerOfTwo(bits: u64) bool { + return bits != 0 and ((bits & (~bits + 1)) == bits); + } + + fn intTypeFromBitsAndSignRounded(self: *FuncGen, bits: u16, signed: bool) error{OutOfMemory}!Type { + const next_pow_two = math.log2_int_ceil(u16, bits); + const rounded_bits = @as(u32, 1) << next_pow_two; + return switch (rounded_bits) { + 8, 16, 32 => if (signed) Type.initTag(.i32) else Type.initTag(.u32), + 64 => if (signed) Type.initTag(.i64) else Type.initTag(.u64), + 128 => if (signed) Type.initTag(.i128) else Type.initTag(.u128), + else => |big| if (signed) + Type.Tag.int_signed.create( + self.dg.object.type_map_arena.allocator(), + @intCast(u16, big), + ) + else + Type.Tag.int_unsigned.create( + self.dg.object.type_map_arena.allocator(), + @intCast(u16, big), + ), + }; + } + fn airIntToFloat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; + const target = self.dg.module.getTarget(); const ty_op = self.air.instructions.items(.data)[inst].ty_op; - const operand = try self.resolveInst(ty_op.operand); - const operand_ty = self.air.typeOf(ty_op.operand); - const operand_scalar_ty = operand_ty.scalarType(); + + var operand = try self.resolveInst(ty_op.operand); + var operand_ty = self.air.typeOf(ty_op.operand); + var operand_scalar_ty = operand_ty.scalarType(); + + { + const operand_bits = @intCast(u16, operand_scalar_ty.bitSize(target)); + const is_signed = operand_scalar_ty.isSignedInt(); + + if (!isPowerOfTwo(operand_bits) or operand_bits < 32) { + const wider_ty = try self.intTypeFromBitsAndSignRounded(operand_bits, is_signed); + const wider_llvm_ty = try self.dg.llvmType(wider_ty); + if (is_signed) { + operand = self.builder.buildSExt(operand, wider_llvm_ty, ""); + } else { + operand = self.builder.buildZExt(operand, wider_llvm_ty, ""); + } + operand_ty = wider_ty; + operand_scalar_ty = operand_ty.scalarType(); + } + } + const dest_ty = self.air.typeOfIndex(inst); + const dest_scalar_ty = dest_ty.scalarType(); const dest_llvm_ty = try self.dg.llvmType(dest_ty); - if (operand_scalar_ty.isSignedInt()) { - return self.builder.buildSIToFP(operand, dest_llvm_ty, ""); - } else { - return self.builder.buildUIToFP(operand, dest_llvm_ty, ""); + if (intrinsicsAllowed(dest_scalar_ty, target)) { + if (operand_scalar_ty.isSignedInt()) { + return self.builder.buildSIToFP(operand, dest_llvm_ty, ""); + } else { + return self.builder.buildUIToFP(operand, dest_llvm_ty, ""); + } } + + const operand_bits = @intCast(u16, operand_scalar_ty.bitSize(target)); + const compiler_rt_operand_abbrev = compilerRtIntAbbrev(operand_bits); + + const dest_bits = dest_scalar_ty.floatBits(target); + const compiler_rt_dest_abbrev = compilerRtFloatAbbrev(dest_bits); + + var fn_name_buf: [64]u8 = undefined; + const fn_name = if (operand_scalar_ty.isSignedInt()) + std.fmt.bufPrintZ(&fn_name_buf, "__float{s}i{s}f", .{ + compiler_rt_operand_abbrev, + compiler_rt_dest_abbrev, + }) catch unreachable + else + std.fmt.bufPrintZ(&fn_name_buf, "__floatun{s}i{s}f", .{ + compiler_rt_operand_abbrev, + compiler_rt_dest_abbrev, + }) catch unreachable; + + const operand_llvm_ty = try self.dg.llvmType(operand_ty); + const param_types = [1]*const llvm.Type{operand_llvm_ty}; + const libc_fn = self.getLibcFunction(fn_name, ¶m_types, dest_llvm_ty); + const params = [1]*const llvm.Value{operand}; + + return self.builder.buildCall(libc_fn, ¶ms, params.len, .C, .Auto, ""); } fn airFloatToInt(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; + const target = self.dg.module.getTarget(); const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const operand = try self.resolveInst(ty_op.operand); - const dest_ty = self.air.typeOfIndex(inst); - const dest_scalar_ty = dest_ty.scalarType(); + const operand_ty = self.air.typeOf(ty_op.operand); + const operand_scalar_ty = operand_ty.scalarType(); + + var dest_ty = self.air.typeOfIndex(inst); + var dest_scalar_ty = dest_ty.scalarType(); + + if (intrinsicsAllowed(operand_scalar_ty, target)) { + // TODO set fast math flag + const dest_llvm_ty = try self.dg.llvmType(dest_ty); + if (dest_scalar_ty.isSignedInt()) { + return self.builder.buildFPToSI(operand, dest_llvm_ty, ""); + } else { + return self.builder.buildFPToUI(operand, dest_llvm_ty, ""); + } + } + + const needs_truncating = blk: { + const dest_bits = @intCast(u16, dest_scalar_ty.bitSize(target)); + + if (!isPowerOfTwo(dest_bits) or dest_bits < 32) { + dest_ty = try self.intTypeFromBitsAndSignRounded(dest_bits, dest_scalar_ty.isSignedInt()); + dest_scalar_ty = dest_ty.scalarType(); + break :blk true; + } + + break :blk false; + }; + const dest_llvm_ty = try self.dg.llvmType(dest_ty); - // TODO set fast math flag + const operand_bits = operand_scalar_ty.floatBits(target); + const compiler_rt_operand_abbrev = compilerRtFloatAbbrev(operand_bits); - if (dest_scalar_ty.isSignedInt()) { - return self.builder.buildFPToSI(operand, dest_llvm_ty, ""); - } else { - return self.builder.buildFPToUI(operand, dest_llvm_ty, ""); + const dest_bits = @intCast(u16, dest_scalar_ty.bitSize(target)); + const compiler_rt_dest_abbrev = compilerRtIntAbbrev(dest_bits); + + var fn_name_buf: [64]u8 = undefined; + const fn_name = if (dest_scalar_ty.isSignedInt()) + std.fmt.bufPrintZ(&fn_name_buf, "__fix{s}f{s}i", .{ + compiler_rt_operand_abbrev, + compiler_rt_dest_abbrev, + }) catch unreachable + else + std.fmt.bufPrintZ(&fn_name_buf, "__fixun{s}f{s}i", .{ + compiler_rt_operand_abbrev, + compiler_rt_dest_abbrev, + }) catch unreachable; + + const operand_llvm_ty = try self.dg.llvmType(operand_ty); + const param_types = [1]*const llvm.Type{operand_llvm_ty}; + const libc_fn = self.getLibcFunction(fn_name, ¶m_types, dest_llvm_ty); + const params = [1]*const llvm.Value{operand}; + + const result = self.builder.buildCall(libc_fn, ¶ms, params.len, .C, .Auto, ""); + + if (needs_truncating) { + const requested_ty = self.air.typeOfIndex(inst); + const requested_llvm_ty = try self.dg.llvmType(requested_ty); + return self.builder.buildTrunc(result, requested_llvm_ty, ""); } + + return result; } fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value { @@ -5615,6 +5740,16 @@ pub const FuncGen = struct { }; } + fn compilerRtIntAbbrev(bits: u16) []const u8 { + return switch (bits) { + 16 => "h", + 32 => "s", + 64 => "d", + 128 => "t", + else => "o", // Non-standard + }; + } + /// Creates a floating point comparison by lowering to the appropriate /// hardware instruction or softfloat routine for the target fn buildFloatCmp( diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index a1bd41e7b0..416c7914e7 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -112,6 +112,42 @@ test "@intToFloat" { comptime try S.doTheTest(); } +test "@intToFloat(f80)" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest(comptime Int: type) !void { + try testIntToFloat(Int, -2); + } + + fn testIntToFloat(comptime Int: type, k: Int) !void { + const f = @intToFloat(f80, k); + const i = @floatToInt(Int, f); + try expect(i == k); + } + }; + try S.doTheTest(i31); + try S.doTheTest(i32); + try S.doTheTest(i45); + try S.doTheTest(i64); + try S.doTheTest(i80); + try S.doTheTest(i128); + // try S.doTheTest(i256); // TODO missing compiler_rt symbols + comptime try S.doTheTest(i31); + comptime try S.doTheTest(i32); + comptime try S.doTheTest(i45); + comptime try S.doTheTest(i64); + comptime try S.doTheTest(i80); + comptime try S.doTheTest(i128); + comptime try S.doTheTest(i256); +} + test "@floatToInt" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From ac1aaec9c38eb44b93099ff18579a9401f107100 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 6 May 2022 09:03:08 +0200 Subject: [PATCH 1386/2031] x64: handle CF flags spilling in overflow calls Handle spilling of CF flags set with an overflow call. Add saving stack offset to memory. --- src/arch/x86_64/CodeGen.zig | 40 +++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 767a67bf92..2fdce1149c 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -966,14 +966,16 @@ pub fn spillCompareFlagsIfOccupied(self: *Self) !void { const mcv = self.getResolvedInstValue(inst_to_save); assert(mcv.usesCompareFlags()); - const new_mcv = try self.allocRegOrMem(inst_to_save, true); + const new_mcv = try self.allocRegOrMem(inst_to_save, !mcv.isRegister()); try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); self.compare_flags_inst = null; + // TODO consolidate with register manager and spillInstruction + // this call should really belong in the register manager! + if (mcv.isRegister()) self.register_manager.freeReg(mcv.asRegister().?); } } @@ -1404,6 +1406,9 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement add_with_overflow for Ints larger than 64bits", .{}); } + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = inst; + const partial = try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); const result: MCValue = switch (int_info.signedness) { .signed => .{ .register_overflow_signed = partial.register }, @@ -1433,6 +1438,9 @@ fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement sub_with_overflow for Ints larger than 64bits", .{}); } + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = inst; + const partial = try self.genSubOp(inst, bin_op.lhs, bin_op.rhs); const result: MCValue = switch (int_info.signedness) { .signed => .{ .register_overflow_signed = partial.register }, @@ -1456,9 +1464,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { switch (ty.zigTypeTag()) { .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}), .Int => { - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = null; - const int_info = ty.intInfo(self.target.*); if (int_info.bits > 64) { @@ -1466,6 +1471,9 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { } if (math.isPowerOfTwo(int_info.bits)) { + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = inst; + // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. try self.register_manager.getReg(.rax, inst); try self.register_manager.getReg(.rdx, null); @@ -1487,6 +1495,9 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { break :result result; } + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = null; + const dst_reg: Register = dst_reg: { switch (int_info.signedness) { .signed => { @@ -2783,7 +2794,6 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue } fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { - _ = ptr_ty; const abi_size = value_ty.abiSize(self.target.*); switch (ptr) { .none => unreachable, @@ -3000,6 +3010,24 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type try self.genInlineMemcpy(.{ .register = addr_reg.to64() }, value, .{ .immediate = abi_size }, .{}); }, + .stack_offset => { + if (abi_size <= 8) { + // TODO this should really be a recursive call + const tmp_reg = try self.copyToTmpRegister(value_ty, value); + _ = try self.addInst(.{ + .tag = .mov, + .ops = (Mir.Ops{ + .reg1 = addr_reg.to64(), + .reg2 = tmp_reg, + .flags = 0b10, + }).encode(), + .data = .{ .imm = 0 }, + }); + return; + } + + try self.genInlineMemcpy(.{ .register = addr_reg.to64() }, value, .{ .immediate = abi_size }, .{}); + }, else => return self.fail("TODO implement storing {} to MCValue.memory", .{value}), } }, From 83d837813999b73771a0e9623ed1c6c5339e4f4c Mon Sep 17 00:00:00 2001 From: aiotter Date: Fri, 6 May 2022 21:58:20 +0900 Subject: [PATCH 1387/2031] std.c: Add C APIs to read directories --- lib/std/c.zig | 15 +++++++++++++++ lib/std/c/darwin.zig | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/lib/std/c.zig b/lib/std/c.zig index d98dbf0cde..0590c3554a 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -59,6 +59,20 @@ pub usingnamespace switch (builtin.os.tag) { pub const whence_t = if (builtin.os.tag == .wasi) std.os.wasi.whence_t else c_int; +// Unix-like systems +pub usingnamespace switch (builtin.os.tag) { + .netbsd, .windows => struct {}, + else => struct { + pub const DIR = opaque {}; + pub extern "c" fn opendir(pathname: [*:0]const u8) ?*DIR; + pub extern "c" fn fdopendir(fd: c_int) ?*DIR; + pub extern "c" fn rewinddir(dp: *DIR) void; + pub extern "c" fn closedir(dp: *DIR) c_int; + pub extern "c" fn telldir(dp: *DIR) c_long; + pub extern "c" fn seekdir(dp: *DIR, loc: c_long) void; + }, +}; + pub usingnamespace switch (builtin.os.tag) { .netbsd, .macos, .ios, .watchos, .tvos, .windows => struct {}, else => struct { @@ -76,6 +90,7 @@ pub usingnamespace switch (builtin.os.tag) { pub extern "c" fn sigfillset(set: ?*c.sigset_t) void; pub extern "c" fn alarm(seconds: c_uint) c_uint; pub extern "c" fn sigwait(set: ?*c.sigset_t, sig: ?*c_int) c_int; + pub extern "c" fn readdir(dp: *c.DIR) ?*c.dirent; }, }; diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index b536471a30..d40701f04e 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -62,9 +62,13 @@ const private = struct { /// force 64bit version. /// Note that this is fixed on aarch64 and no longer necessary. extern "c" fn @"fstatat$INODE64"(dirfd: fd_t, path_name: [*:0]const u8, buf: *Stat, flags: u32) c_int; + + extern "c" fn readdir(dir: *std.c.DIR) ?*dirent; + extern "c" fn @"readdir$INODE64"(dir: *std.c.DIR) ?*dirent; }; pub const fstat = if (native_arch == .aarch64) private.fstat else private.@"fstat$INODE64"; pub const fstatat = if (native_arch == .aarch64) private.fstatat else private.@"fstatat$INODE64"; +pub const readdir = if (native_arch == .aarch64) private.readdir else private.@"readdir$INODE64"; pub extern "c" fn mach_absolute_time() u64; pub extern "c" fn mach_continuous_time() u64; From ad7f725dbacb8144bdff7f0ce3bcfaf47725a4bc Mon Sep 17 00:00:00 2001 From: aiotter Date: Fri, 6 May 2022 23:52:21 +0900 Subject: [PATCH 1388/2031] std.c: Move Darwin-unspecific functions from std/c/darwin.zig to std/c.zig --- lib/std/c.zig | 33 ++++++++++++++++++++------------- lib/std/c/darwin.zig | 13 ------------- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 0590c3554a..c33d7b35ab 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -70,26 +70,33 @@ pub usingnamespace switch (builtin.os.tag) { pub extern "c" fn closedir(dp: *DIR) c_int; pub extern "c" fn telldir(dp: *DIR) c_long; pub extern "c" fn seekdir(dp: *DIR, loc: c_long) void; + + pub extern "c" fn clock_gettime(clk_id: c_int, tp: *c.timespec) c_int; + pub extern "c" fn clock_getres(clk_id: c_int, tp: *c.timespec) c_int; + pub extern "c" fn gettimeofday(noalias tv: ?*c.timeval, noalias tz: ?*c.timezone) c_int; + pub extern "c" fn nanosleep(rqtp: *const c.timespec, rmtp: ?*c.timespec) c_int; + + pub extern "c" fn getrusage(who: c_int, usage: *c.rusage) c_int; + + pub extern "c" fn sched_yield() c_int; + + pub extern "c" fn sigaction(sig: c_int, noalias act: ?*const c.Sigaction, noalias oact: ?*c.Sigaction) c_int; + pub extern "c" fn sigprocmask(how: c_int, noalias set: ?*const c.sigset_t, noalias oset: ?*c.sigset_t) c_int; + pub extern "c" fn sigfillset(set: ?*c.sigset_t) void; + pub extern "c" fn sigwait(set: ?*c.sigset_t, sig: ?*c_int) c_int; + + pub extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int; + + pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *c.Stat) c_int; + + pub extern "c" fn alarm(seconds: c_uint) c_uint; }, }; pub usingnamespace switch (builtin.os.tag) { .netbsd, .macos, .ios, .watchos, .tvos, .windows => struct {}, else => struct { - pub extern "c" fn clock_getres(clk_id: c_int, tp: *c.timespec) c_int; - pub extern "c" fn clock_gettime(clk_id: c_int, tp: *c.timespec) c_int; pub extern "c" fn fstat(fd: c.fd_t, buf: *c.Stat) c_int; - pub extern "c" fn getrusage(who: c_int, usage: *c.rusage) c_int; - pub extern "c" fn gettimeofday(noalias tv: ?*c.timeval, noalias tz: ?*c.timezone) c_int; - pub extern "c" fn nanosleep(rqtp: *const c.timespec, rmtp: ?*c.timespec) c_int; - pub extern "c" fn sched_yield() c_int; - pub extern "c" fn sigaction(sig: c_int, noalias act: ?*const c.Sigaction, noalias oact: ?*c.Sigaction) c_int; - pub extern "c" fn sigprocmask(how: c_int, noalias set: ?*const c.sigset_t, noalias oset: ?*c.sigset_t) c_int; - pub extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int; - pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *c.Stat) c_int; - pub extern "c" fn sigfillset(set: ?*c.sigset_t) void; - pub extern "c" fn alarm(seconds: c_uint) c_uint; - pub extern "c" fn sigwait(set: ?*c.sigset_t, sig: ?*c_int) c_int; pub extern "c" fn readdir(dp: *c.DIR) ?*c.dirent; }, }; diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index d40701f04e..e44da7e1f7 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -700,19 +700,6 @@ pub extern "c" fn os_unfair_lock_assert_not_owner(o: os_unfair_lock_t) void; // XXX: close -> close$NOCANCEL // XXX: getdirentries -> _getdirentries64 -pub extern "c" fn clock_getres(clk_id: c_int, tp: *timespec) c_int; -pub extern "c" fn clock_gettime(clk_id: c_int, tp: *timespec) c_int; -pub extern "c" fn getrusage(who: c_int, usage: *rusage) c_int; -pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int; -pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; -pub extern "c" fn sched_yield() c_int; -pub extern "c" fn sigaction(sig: c_int, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) c_int; -pub extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int; -pub extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int; -pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *Stat) c_int; -pub extern "c" fn sigfillset(set: ?*sigset_t) void; -pub extern "c" fn alarm(seconds: c_uint) c_uint; -pub extern "c" fn sigwait(set: ?*sigset_t, sig: ?*c_int) c_int; // See: https://opensource.apple.com/source/xnu/xnu-6153.141.1/bsd/sys/_types.h.auto.html // TODO: audit mode_t/pid_t, should likely be u16/i32 From d136cd3202711bf6ad11a6f62115cfce0d6e88f1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 5 May 2022 23:13:14 -0700 Subject: [PATCH 1389/2031] LLVM: rework the previous commit Idiomatic Zig, use const instead of var, simplify the logic. --- src/codegen/llvm.zig | 143 +++++++++++----------------------- src/codegen/llvm/bindings.zig | 8 ++ 2 files changed, 55 insertions(+), 96 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 374ae9b38e..800786891e 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4229,61 +4229,20 @@ pub const FuncGen = struct { return self.builder.buildInsertValue(partial, len, 1, ""); } - inline fn isPowerOfTwo(bits: u64) bool { - return bits != 0 and ((bits & (~bits + 1)) == bits); - } - - fn intTypeFromBitsAndSignRounded(self: *FuncGen, bits: u16, signed: bool) error{OutOfMemory}!Type { - const next_pow_two = math.log2_int_ceil(u16, bits); - const rounded_bits = @as(u32, 1) << next_pow_two; - return switch (rounded_bits) { - 8, 16, 32 => if (signed) Type.initTag(.i32) else Type.initTag(.u32), - 64 => if (signed) Type.initTag(.i64) else Type.initTag(.u64), - 128 => if (signed) Type.initTag(.i128) else Type.initTag(.u128), - else => |big| if (signed) - Type.Tag.int_signed.create( - self.dg.object.type_map_arena.allocator(), - @intCast(u16, big), - ) - else - Type.Tag.int_unsigned.create( - self.dg.object.type_map_arena.allocator(), - @intCast(u16, big), - ), - }; - } - fn airIntToFloat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; - const target = self.dg.module.getTarget(); const ty_op = self.air.instructions.items(.data)[inst].ty_op; - var operand = try self.resolveInst(ty_op.operand); - var operand_ty = self.air.typeOf(ty_op.operand); - var operand_scalar_ty = operand_ty.scalarType(); - - { - const operand_bits = @intCast(u16, operand_scalar_ty.bitSize(target)); - const is_signed = operand_scalar_ty.isSignedInt(); - - if (!isPowerOfTwo(operand_bits) or operand_bits < 32) { - const wider_ty = try self.intTypeFromBitsAndSignRounded(operand_bits, is_signed); - const wider_llvm_ty = try self.dg.llvmType(wider_ty); - if (is_signed) { - operand = self.builder.buildSExt(operand, wider_llvm_ty, ""); - } else { - operand = self.builder.buildZExt(operand, wider_llvm_ty, ""); - } - operand_ty = wider_ty; - operand_scalar_ty = operand_ty.scalarType(); - } - } + const operand = try self.resolveInst(ty_op.operand); + const operand_ty = self.air.typeOf(ty_op.operand); + const operand_scalar_ty = operand_ty.scalarType(); const dest_ty = self.air.typeOfIndex(inst); const dest_scalar_ty = dest_ty.scalarType(); const dest_llvm_ty = try self.dg.llvmType(dest_ty); + const target = self.dg.module.getTarget(); if (intrinsicsAllowed(dest_scalar_ty, target)) { if (operand_scalar_ty.isSignedInt()) { @@ -4294,27 +4253,28 @@ pub const FuncGen = struct { } const operand_bits = @intCast(u16, operand_scalar_ty.bitSize(target)); - const compiler_rt_operand_abbrev = compilerRtIntAbbrev(operand_bits); - + const rt_int_bits = compilerRtIntBits(operand_bits); + const rt_int_ty = self.context.intType(rt_int_bits); + const extended = e: { + if (operand_scalar_ty.isSignedInt()) { + break :e self.builder.buildSExtOrBitCast(operand, rt_int_ty, ""); + } else { + break :e self.builder.buildZExtOrBitCast(operand, rt_int_ty, ""); + } + }; const dest_bits = dest_scalar_ty.floatBits(target); + const compiler_rt_operand_abbrev = compilerRtIntAbbrev(rt_int_bits); const compiler_rt_dest_abbrev = compilerRtFloatAbbrev(dest_bits); - + const sign_prefix = if (operand_scalar_ty.isSignedInt()) "" else "un"; var fn_name_buf: [64]u8 = undefined; - const fn_name = if (operand_scalar_ty.isSignedInt()) - std.fmt.bufPrintZ(&fn_name_buf, "__float{s}i{s}f", .{ - compiler_rt_operand_abbrev, - compiler_rt_dest_abbrev, - }) catch unreachable - else - std.fmt.bufPrintZ(&fn_name_buf, "__floatun{s}i{s}f", .{ - compiler_rt_operand_abbrev, - compiler_rt_dest_abbrev, - }) catch unreachable; - - const operand_llvm_ty = try self.dg.llvmType(operand_ty); - const param_types = [1]*const llvm.Type{operand_llvm_ty}; + const fn_name = std.fmt.bufPrintZ(&fn_name_buf, "__float{s}{s}i{s}f", .{ + sign_prefix, + compiler_rt_operand_abbrev, + compiler_rt_dest_abbrev, + }) catch unreachable; + const param_types = [1]*const llvm.Type{rt_int_ty}; const libc_fn = self.getLibcFunction(fn_name, ¶m_types, dest_llvm_ty); - const params = [1]*const llvm.Value{operand}; + const params = [1]*const llvm.Value{extended}; return self.builder.buildCall(libc_fn, ¶ms, params.len, .C, .Auto, ""); } @@ -4330,12 +4290,12 @@ pub const FuncGen = struct { const operand_ty = self.air.typeOf(ty_op.operand); const operand_scalar_ty = operand_ty.scalarType(); - var dest_ty = self.air.typeOfIndex(inst); - var dest_scalar_ty = dest_ty.scalarType(); + const dest_ty = self.air.typeOfIndex(inst); + const dest_scalar_ty = dest_ty.scalarType(); + const dest_llvm_ty = try self.dg.llvmType(dest_ty); if (intrinsicsAllowed(operand_scalar_ty, target)) { // TODO set fast math flag - const dest_llvm_ty = try self.dg.llvmType(dest_ty); if (dest_scalar_ty.isSignedInt()) { return self.builder.buildFPToSI(operand, dest_llvm_ty, ""); } else { @@ -4343,52 +4303,34 @@ pub const FuncGen = struct { } } - const needs_truncating = blk: { - const dest_bits = @intCast(u16, dest_scalar_ty.bitSize(target)); - - if (!isPowerOfTwo(dest_bits) or dest_bits < 32) { - dest_ty = try self.intTypeFromBitsAndSignRounded(dest_bits, dest_scalar_ty.isSignedInt()); - dest_scalar_ty = dest_ty.scalarType(); - break :blk true; - } - - break :blk false; - }; - - const dest_llvm_ty = try self.dg.llvmType(dest_ty); + const rt_int_bits = compilerRtIntBits(@intCast(u16, dest_scalar_ty.bitSize(target))); + const libc_ret_ty = self.context.intType(rt_int_bits); const operand_bits = operand_scalar_ty.floatBits(target); const compiler_rt_operand_abbrev = compilerRtFloatAbbrev(operand_bits); - const dest_bits = @intCast(u16, dest_scalar_ty.bitSize(target)); - const compiler_rt_dest_abbrev = compilerRtIntAbbrev(dest_bits); + const compiler_rt_dest_abbrev = compilerRtIntAbbrev(rt_int_bits); + const sign_prefix = if (dest_scalar_ty.isSignedInt()) "" else "un"; var fn_name_buf: [64]u8 = undefined; - const fn_name = if (dest_scalar_ty.isSignedInt()) - std.fmt.bufPrintZ(&fn_name_buf, "__fix{s}f{s}i", .{ - compiler_rt_operand_abbrev, - compiler_rt_dest_abbrev, - }) catch unreachable - else - std.fmt.bufPrintZ(&fn_name_buf, "__fixun{s}f{s}i", .{ - compiler_rt_operand_abbrev, - compiler_rt_dest_abbrev, - }) catch unreachable; + const fn_name = std.fmt.bufPrintZ(&fn_name_buf, "__fix{s}{s}f{s}i", .{ + sign_prefix, + compiler_rt_operand_abbrev, + compiler_rt_dest_abbrev, + }) catch unreachable; const operand_llvm_ty = try self.dg.llvmType(operand_ty); const param_types = [1]*const llvm.Type{operand_llvm_ty}; - const libc_fn = self.getLibcFunction(fn_name, ¶m_types, dest_llvm_ty); + const libc_fn = self.getLibcFunction(fn_name, ¶m_types, libc_ret_ty); const params = [1]*const llvm.Value{operand}; const result = self.builder.buildCall(libc_fn, ¶ms, params.len, .C, .Auto, ""); - if (needs_truncating) { - const requested_ty = self.air.typeOfIndex(inst); - const requested_llvm_ty = try self.dg.llvmType(requested_ty); - return self.builder.buildTrunc(result, requested_llvm_ty, ""); + if (libc_ret_ty == dest_llvm_ty) { + return result; } - return result; + return self.builder.buildTrunc(result, dest_llvm_ty, ""); } fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value { @@ -8448,3 +8390,12 @@ fn needDbgVarWorkaround(dg: *DeclGen, ty: Type) bool { } return false; } + +fn compilerRtIntBits(bits: u16) u16 { + inline for (.{ 8, 16, 32, 64, 128 }) |b| { + if (bits <= b) { + return b; + } + } + return bits; +} diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 4732e0dd49..c70a7d1553 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -476,6 +476,14 @@ pub const Builder = opaque { Name: [*:0]const u8, ) *const Value; + pub const buildSExtOrBitCast = LLVMBuildSExtOrBitCast; + extern fn LLVMBuildSExtOrBitCast( + *const Builder, + Val: *const Value, + DestTy: *const Type, + Name: [*:0]const u8, + ) *const Value; + pub const buildCall = ZigLLVMBuildCall; extern fn ZigLLVMBuildCall( *const Builder, From 4df65fc26485fe32976561990393a35eb52ddfc6 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 6 May 2022 21:58:25 +0200 Subject: [PATCH 1390/2031] wasm: Store signed ints as two's complement When a signed integer is negative, the integer will be stored as a two's complement, rather than its signed value. Instead, we verify the signed bits during arithmetic operations. This fixes signed cases of `@mulWithOverflow`. --- src/arch/wasm/CodeGen.zig | 108 +++++++++++++++++++++++++------------- 1 file changed, 71 insertions(+), 37 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 5171dfb460..c192cba8d9 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1818,7 +1818,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro try self.emitWValue(rhs); } const valtype = typeToValtype(ty, self.target); - const abi_size = @intCast(u8, ty.abiSize(self.target)); + const abi_size = @intCast(u8, ty.bitSize(self.target)); const opcode = buildOpcode(.{ .valtype1 = valtype, @@ -1852,21 +1852,13 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { // load local's value from memory by its stack position try self.emitWValue(operand); - // Build the opcode with the right bitsize - const signedness: std.builtin.Signedness = if (ty.isUnsignedInt() or - ty.zigTypeTag() == .ErrorSet or - ty.zigTypeTag() == .Bool) - .unsigned - else - .signed; - - const abi_size = @intCast(u8, ty.abiSize(self.target)); + const abi_size = @intCast(u8, ty.bitSize(self.target)); const opcode = buildOpcode(.{ .valtype1 = typeToValtype(ty, self.target), .width = abi_size * 8, // use bitsize instead of byte size .op = .load, - .signedness = signedness, + .signedness = .unsigned, }); try self.addMemArg( @@ -1948,6 +1940,7 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError .signedness = if (ty.isSignedInt()) .signed else .unsigned, }); try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); + const bin_local = try self.allocLocal(ty); const int_info = ty.intInfo(self.target); const bitsize = int_info.bits; @@ -1963,25 +1956,37 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError } else if (is_signed and bitsize == 16) { try self.addTag(.i32_extend16_s); } else { - const result = (@as(u64, 1) << @intCast(u6, bitsize - @boolToInt(is_signed))) - 1; - if (bitsize < 32) { - try self.addImm32(@bitCast(i32, @intCast(u32, result))); - try self.addTag(.i32_and); - } else { - try self.addImm64(result); - try self.addTag(.i64_and); - } + try self.addLabel(.local_set, bin_local.local); + return self.wrapOperand(bin_local, ty); } } else if (int_info.bits > 64) { return self.fail("TODO wasm: Integer wrapping for bitsizes larger than 64", .{}); } // save the result in a temporary - const bin_local = try self.allocLocal(ty); try self.addLabel(.local_set, bin_local.local); return bin_local; } +/// Wraps an operand based on a given type's bitsize. +/// Asserts `Type` is <= 64bits. +fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue { + assert(ty.abiSize(self.target) <= 8); + const result_local = try self.allocLocal(ty); + const bitsize = ty.intInfo(self.target).bits; + const result = @intCast(u64, (@as(u65, 1) << @intCast(u7, bitsize)) - 1); + try self.emitWValue(operand); + if (bitsize <= 32) { + try self.addImm32(@bitCast(i32, @intCast(u32, result))); + try self.addTag(.i32_and); + } else { + try self.addImm64(result); + try self.addTag(.i64_and); + } + try self.addLabel(.local_set, result_local.local); + return result_local; +} + fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WValue { switch (ptr_val.tag()) { .decl_ref_mut => { @@ -2098,6 +2103,22 @@ fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) } else return WValue{ .memory = target_sym_index }; } +/// Converts a signed integer to its 2's complement form and returns +/// an unsigned integer instead. +/// Asserts bitsize <= 64 +fn convertTo2Complement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo(@TypeOf(value)).Int.bits) { + const T = @TypeOf(value); + comptime assert(@typeInfo(T) == .Int); + comptime assert(@typeInfo(T).Int.signedness == .signed); + assert(bits <= 64); + const WantedT = std.meta.Int(.unsigned, @typeInfo(T).Int.bits); + if (value >= 0) return @bitCast(WantedT, value); + const max_value = @intCast(u64, (@as(u65, 1) << bits) - 1); + const flipped = (~-value) + 1; + const result = @bitCast(WantedT, flipped) & max_value; + return @intCast(WantedT, result); +} + fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { if (val.isUndefDeep()) return self.emitUndefined(ty); if (val.castTag(.decl_ref)) |decl_ref| { @@ -2114,10 +2135,12 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { switch (ty.zigTypeTag()) { .Int => { const int_info = ty.intInfo(self.target); - // write constant switch (int_info.signedness) { .signed => switch (int_info.bits) { - 0...32 => return WValue{ .imm32 = @bitCast(u32, @intCast(i32, val.toSignedInt())) }, + 0...32 => return WValue{ .imm32 = @intCast(u32, convertTo2Complement( + val.toSignedInt(), + @intCast(u6, int_info.bits), + )) }, 33...64 => return WValue{ .imm64 = @bitCast(u64, val.toSignedInt()) }, else => unreachable, }, @@ -4009,22 +4032,33 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue try self.addLabel(.local_set, tmp_val.local); break :blk tmp_val; } else if (op == .mul) blk: { - const bin_op = try self.wrapBinOp(lhs, rhs, lhs_ty, op); - try self.startBlock(.block, wasm.block_empty); - // check if 0. true => Break out of block as cannot over -or underflow. - try self.emitWValue(lhs); - switch (wasm_bits) { - 32 => try self.addTag(.i32_eqz), - 64 => try self.addTag(.i64_eqz), - else => unreachable, + if (int_info.signedness == .signed) { + const shift_val = convertTo2Complement(-@intCast(i17, int_info.bits), @intCast(u7, int_info.bits)); + const shift_imm = if (wasm_bits == 32) WValue{ .imm32 = shift_val } else WValue{ .imm64 = shift_val }; + + const lhs_shl = try self.binOp(lhs, shift_imm, lhs_ty, .shl); + const lhs_shr = try self.binOp(lhs_shl, shift_imm, lhs_ty, .shr); + const rhs_shl = try self.binOp(rhs, shift_imm, lhs_ty, .shl); + const rhs_shr = try self.binOp(rhs_shl, shift_imm, lhs_ty, .shr); + + const bin_op = try self.binOp(lhs_shr, rhs_shr, lhs_ty, op); + const shl = try self.binOp(bin_op, shift_imm, lhs_ty, .shl); + const shr = try self.binOp(shl, shift_imm, lhs_ty, .shr); + + const cmp_op = try self.cmp(shr, bin_op, lhs_ty, .neq); + try self.emitWValue(cmp_op); + try self.addLabel(.local_set, overflow_bit.local); + break :blk try self.wrapOperand(bin_op, lhs_ty); + } else { + const bin_op = try self.binOp(lhs, rhs, lhs_ty, op); + const shift_imm = if (wasm_bits == 32) WValue{ .imm32 = int_info.bits } else WValue{ .imm64 = int_info.bits }; + // const zero = if (wasm_bits == 32) WValue{ .imm32 = 0 } else WValue{ .imm64 = 0 }; + const shr = try self.binOp(bin_op, shift_imm, lhs_ty, .shr); + const cmp_op = try self.cmp(shr, zero, lhs_ty, .neq); + try self.emitWValue(cmp_op); + try self.addLabel(.local_set, overflow_bit.local); + break :blk try self.wrapOperand(bin_op, lhs_ty); } - try self.addLabel(.br_if, 0); - const div = try self.binOp(bin_op, lhs, lhs_ty, .div); - const cmp_res = try self.cmp(div, rhs, lhs_ty, .neq); - try self.emitWValue(cmp_res); - try self.addLabel(.local_set, overflow_bit.local); - try self.endBlock(); - break :blk bin_op; } else try self.wrapBinOp(lhs, rhs, lhs_ty, op); const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); From 6d605ca6908e53a883eeb0aa9800e89136f83013 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 6 May 2022 15:42:52 -0700 Subject: [PATCH 1391/2031] CI: macos: stage2 zig build test-behavior --- ci/azure/macos_script | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/ci/azure/macos_script b/ci/azure/macos_script index b163afe3b3..1df1b10dc1 100755 --- a/ci/azure/macos_script +++ b/ci/azure/macos_script @@ -58,12 +58,23 @@ make $JOBS install # Build stage2 standalone so that we can test stage2 against stage2 compiler-rt. release/bin/zig build -p stage2 -Denable-llvm -stage2/bin/zig test ../test/behavior.zig -I../test -fLLVM -stage2/bin/zig test ../test/behavior.zig -I../test -fno-LLVM +stage2/bin/zig build test-behavior -release/bin/zig build test-toolchain -Denable-macos-sdk -release/bin/zig build test-std -release/bin/zig build docs +release/bin/zig build test-behavior -Denable-macos-sdk -Domit-stage2 +release/bin/zig build test-compiler-rt -Denable-macos-sdk +release/bin/zig build test-std -Denable-macos-sdk +release/bin/zig build test-minilibc -Denable-macos-sdk +release/bin/zig build test-compare-output -Denable-macos-sdk +release/bin/zig build test-standalone -Denable-macos-sdk +release/bin/zig build test-stack-traces -Denable-macos-sdk +release/bin/zig build test-cli -Denable-macos-sdk +release/bin/zig build test-asm-link -Denable-macos-sdk +release/bin/zig build test-runtime-safety -Denable-macos-sdk +release/bin/zig build test-translate-c -Denable-macos-sdk +release/bin/zig build test-run-translated-c -Denable-macos-sdk +release/bin/zig build docs -Denable-macos-sdk +release/bin/zig build test-fmt -Denable-macos-sdk +release/bin/zig build test-stage2 -Denable-macos-sdk if [ "${BUILD_REASON}" != "PullRequest" ]; then mv ../LICENSE release/ From ac954eb539ec242af568fe3565094cea12c4beea Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 7 May 2022 00:57:55 +0200 Subject: [PATCH 1392/2031] regalloc: ensure we only freeze/unfreeze at the outermost scope This prevents a nasty type of bugs where we accidentally unfreeze a register that was frozen purposely in the outer scope, risking accidental realloc of a taken register. Fix CF flags spilling on aarch64 backend. --- src/arch/aarch64/CodeGen.zig | 219 ++++++++---- src/arch/arm/CodeGen.zig | 174 +++++---- src/arch/riscv64/CodeGen.zig | 20 +- src/arch/x86_64/CodeGen.zig | 663 ++++++++++++++++++++++------------- src/register_manager.zig | 71 ++-- test/behavior/align.zig | 1 + 6 files changed, 735 insertions(+), 413 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 825bf51b1f..fca4327d2a 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -23,6 +23,7 @@ const log = std.log.scoped(.codegen); const build_options = @import("build_options"); const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs); +const RegisterLock = RegisterManager.RegisterLock; const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; const FnResult = @import("../../codegen.zig").FnResult; @@ -910,16 +911,16 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void fn spillCompareFlagsIfOccupied(self: *Self) !void { if (self.compare_flags_inst) |inst_to_save| { const mcv = self.getResolvedInstValue(inst_to_save); - switch (mcv) { + const new_mcv = switch (mcv) { .compare_flags_signed, .compare_flags_unsigned, + => try self.allocRegOrMem(inst_to_save, true), .register_c_flag, .register_v_flag, - => {}, + => try self.allocRegOrMem(inst_to_save, false), else => unreachable, // mcv doesn't occupy the compare flags - } + }; - const new_mcv = try self.allocRegOrMem(inst_to_save, true); try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); @@ -927,6 +928,15 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); self.compare_flags_inst = null; + + // TODO consolidate with register manager and spillInstruction + // this call should really belong in the register manager! + switch (mcv) { + .register_c_flag, + .register_v_flag, + => |reg| self.register_manager.freeReg(reg), + else => {}, + } } } @@ -1048,8 +1058,8 @@ fn trunc( } }, }; - self.register_manager.freezeRegs(&.{operand_reg}); - defer self.register_manager.unfreezeRegs(&.{operand_reg}); + const lock = self.register_manager.freezeReg(operand_reg); + defer if (lock) |reg| self.register_manager.unfreezeReg(reg); const dest_reg = if (maybe_inst) |inst| blk: { const ty_op = self.air.instructions.items(.data)[inst].ty_op; @@ -1135,8 +1145,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { .register => |r| r, else => try self.copyToTmpRegister(operand_ty, operand), }; - self.register_manager.freezeRegs(&.{op_reg}); - defer self.register_manager.unfreezeRegs(&.{op_reg}); + const reg_lock = self.register_manager.freezeRegAssumeUnused(op_reg); + defer self.register_manager.unfreezeReg(reg_lock); const dest_reg = blk: { if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { @@ -1168,8 +1178,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { .register => |r| r, else => try self.copyToTmpRegister(operand_ty, operand), }; - self.register_manager.freezeRegs(&.{op_reg}); - defer self.register_manager.unfreezeRegs(&.{op_reg}); + const reg_lock = self.register_manager.freezeRegAssumeUnused(op_reg); + defer self.register_manager.unfreezeReg(reg_lock); const dest_reg = blk: { if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { @@ -1257,8 +1267,17 @@ fn binOpRegister( const lhs_is_register = lhs == .register; const rhs_is_register = rhs == .register; - if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); - if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); + const lhs_lock: ?RegisterLock = if (lhs_is_register) + self.register_manager.freezeReg(lhs.register) + else + null; + defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + + const rhs_lock: ?RegisterLock = if (rhs_is_register) + self.register_manager.freezeReg(rhs.register) + else + null; + defer if (rhs_lock) |reg| self.register_manager.unfreezeReg(reg); const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; @@ -1270,13 +1289,13 @@ fn binOpRegister( const raw_reg = try self.register_manager.allocReg(track_inst); const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); - self.register_manager.freezeRegs(&.{reg}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); break :blk reg; }; - defer self.register_manager.unfreezeRegs(&.{lhs_reg}); + const new_lhs_lock = self.register_manager.freezeReg(lhs_reg); + defer if (new_lhs_lock) |reg| self.register_manager.unfreezeReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else blk: { const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { @@ -1286,13 +1305,13 @@ fn binOpRegister( const raw_reg = try self.register_manager.allocReg(track_inst); const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*)); - self.register_manager.freezeRegs(&.{reg}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); break :blk reg; }; - defer self.register_manager.unfreezeRegs(&.{rhs_reg}); + const new_rhs_lock = self.register_manager.freezeReg(rhs_reg); + defer if (new_rhs_lock) |reg| self.register_manager.unfreezeReg(reg); const dest_reg = switch (mir_tag) { .cmp_shifted_register => undefined, // cmp has no destination register @@ -1394,7 +1413,11 @@ fn binOpImmediate( ) !MCValue { const lhs_is_register = lhs == .register; - if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); + const lhs_lock: ?RegisterLock = if (lhs_is_register) + self.register_manager.freezeReg(lhs.register) + else + null; + defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; @@ -1408,13 +1431,13 @@ fn binOpImmediate( const raw_reg = try self.register_manager.allocReg(track_inst); const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); - self.register_manager.freezeRegs(&.{reg}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); break :blk reg; }; - defer self.register_manager.unfreezeRegs(&.{lhs_reg}); + const new_lhs_lock = self.register_manager.freezeReg(lhs_reg); + defer if (new_lhs_lock) |reg| self.register_manager.unfreezeReg(reg); const dest_reg = switch (mir_tag) { .cmp_immediate => undefined, // cmp has no destination register @@ -1758,7 +1781,10 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { const lhs_ty = self.air.typeOf(bin_op.lhs); const rhs_ty = self.air.typeOf(bin_op.rhs); - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1815,13 +1841,13 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { }; const dest = try self.binOp(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); const dest_reg = dest.register; - self.register_manager.freezeRegs(&.{dest_reg}); - defer self.register_manager.unfreezeRegs(&.{dest_reg}); + const dest_reg_lock = self.register_manager.freezeRegAssumeUnused(dest_reg); + defer self.register_manager.unfreezeReg(dest_reg_lock); const raw_truncated_reg = try self.register_manager.allocReg(null); const truncated_reg = registerAlias(raw_truncated_reg, lhs_ty.abiSize(self.target.*)); - self.register_manager.freezeRegs(&.{truncated_reg}); - defer self.register_manager.unfreezeRegs(&.{truncated_reg}); + const truncated_reg_lock = self.register_manager.freezeRegAssumeUnused(truncated_reg); + defer self.register_manager.unfreezeReg(truncated_reg_lock); // sbfx/ubfx truncated, dest, #0, #bits try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); @@ -1922,12 +1948,12 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const dest = try self.binOpRegister(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); const dest_reg = dest.register; - self.register_manager.freezeRegs(&.{dest_reg}); - defer self.register_manager.unfreezeRegs(&.{dest_reg}); + const dest_reg_lock = self.register_manager.freezeRegAssumeUnused(dest_reg); + defer self.register_manager.unfreezeReg(dest_reg_lock); const truncated_reg = try self.register_manager.allocReg(null); - self.register_manager.freezeRegs(&.{truncated_reg}); - defer self.register_manager.unfreezeRegs(&.{truncated_reg}); + const truncated_reg_lock = self.register_manager.freezeRegAssumeUnused(truncated_reg); + defer self.register_manager.unfreezeReg(truncated_reg_lock); try self.truncRegister( dest_reg.to32(), @@ -1977,36 +2003,44 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const lhs_is_register = lhs == .register; const rhs_is_register = rhs == .register; - if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); - if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); + const lhs_lock: ?RegisterLock = if (lhs_is_register) + self.register_manager.freezeRegAssumeUnused(lhs.register) + else + null; + defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + + const rhs_lock: ?RegisterLock = if (rhs_is_register) + self.register_manager.freezeRegAssumeUnused(rhs.register) + else + null; + defer if (rhs_lock) |reg| self.register_manager.unfreezeReg(reg); const lhs_reg = if (lhs_is_register) lhs.register else blk: { const raw_reg = try self.register_manager.allocReg(null); const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); - self.register_manager.freezeRegs(&.{reg}); break :blk reg; }; - defer self.register_manager.unfreezeRegs(&.{lhs_reg}); + const new_lhs_lock = self.register_manager.freezeReg(lhs_reg); + defer if (new_lhs_lock) |reg| self.register_manager.unfreezeReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else blk: { const raw_reg = try self.register_manager.allocReg(null); const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*)); - self.register_manager.freezeRegs(&.{reg}); break :blk reg; }; - defer self.register_manager.unfreezeRegs(&.{rhs_reg}); + const new_rhs_lock = self.register_manager.freezeReg(rhs_reg); + defer if (new_rhs_lock) |reg| self.register_manager.unfreezeReg(reg); if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); - // TODO reuse operands const dest_reg = blk: { const raw_reg = try self.register_manager.allocReg(null); const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); - self.register_manager.freezeRegs(&.{reg}); break :blk reg; }; - defer self.register_manager.unfreezeRegs(&.{dest_reg}); + const dest_reg_lock = self.register_manager.freezeRegAssumeUnused(dest_reg); + defer self.register_manager.unfreezeReg(dest_reg_lock); switch (int_info.signedness) { .signed => { @@ -2021,8 +2055,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { }); const dest_high_reg = try self.register_manager.allocReg(null); - self.register_manager.freezeRegs(&.{dest_high_reg}); - defer self.register_manager.unfreezeRegs(&.{dest_high_reg}); + const dest_high_reg_lock = self.register_manager.freezeRegAssumeUnused(dest_high_reg); + defer self.register_manager.unfreezeReg(dest_high_reg_lock); // smulh dest_high, lhs, rhs _ = try self.addInst(.{ @@ -2071,8 +2105,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { }, .unsigned => { const dest_high_reg = try self.register_manager.allocReg(null); - self.register_manager.freezeRegs(&.{dest_high_reg}); - defer self.register_manager.unfreezeRegs(&.{dest_high_reg}); + const dest_high_reg_lock = self.register_manager.freezeRegAssumeUnused(dest_high_reg); + defer self.register_manager.unfreezeReg(dest_high_reg_lock); // umulh dest_high, lhs, rhs _ = try self.addInst(.{ @@ -2127,8 +2161,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { } const truncated_reg = try self.register_manager.allocReg(null); - self.register_manager.freezeRegs(&.{truncated_reg}); - defer self.register_manager.unfreezeRegs(&.{truncated_reg}); + const truncated_reg_lock = self.register_manager.freezeRegAssumeUnused(truncated_reg); + defer self.register_manager.unfreezeReg(truncated_reg_lock); try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); @@ -2168,14 +2202,20 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { if (int_info.bits <= 64) { const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); - if (lhs == .register) self.register_manager.freezeRegs(&.{lhs.register}); - defer if (lhs == .register) self.register_manager.unfreezeRegs(&.{lhs.register}); + const lhs_lock: ?RegisterLock = if (lhs == .register) + self.register_manager.freezeRegAssumeUnused(lhs.register) + else + null; + defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = null; // lsl dest, lhs, rhs const dest = try self.binOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty); + const dest_reg = dest.register; + const dest_reg_lock = self.register_manager.freezeRegAssumeUnused(dest_reg); + defer self.register_manager.unfreezeReg(dest_reg_lock); // asr/lsr reconstructed, dest, rhs const reconstructed = try self.binOp(.shr, null, dest, rhs, lhs_ty, rhs_ty); @@ -2184,7 +2224,9 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { _ = try self.binOp(.cmp_eq, null, lhs, reconstructed, lhs_ty, lhs_ty); try self.genSetStack(lhs_ty, stack_offset, dest); - try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); + try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ + .compare_flags_unsigned = .neq, + }); break :result MCValue{ .stack_offset = stack_offset }; } else { @@ -2411,14 +2453,18 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { var buf: Type.SlicePtrFieldTypeBuffer = undefined; const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); - if (index_is_register) self.register_manager.freezeRegs(&.{index_mcv.register}); - defer if (index_is_register) self.register_manager.unfreezeRegs(&.{index_mcv.register}); + const index_lock: ?RegisterLock = if (index_is_register) + self.register_manager.freezeRegAssumeUnused(index_mcv.register) + else + null; + defer if (index_lock) |reg| self.register_manager.unfreezeReg(reg); const base_mcv: MCValue = switch (slice_mcv) { .stack_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_offset = off }) }, else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}), }; - self.register_manager.freezeRegs(&.{base_mcv.register}); + const base_lock = self.register_manager.freezeRegAssumeUnused(base_mcv.register); + defer self.register_manager.unfreezeReg(base_lock); switch (elem_size) { else => { @@ -2559,8 +2605,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), .register => |addr_reg| { - self.register_manager.freezeRegs(&.{addr_reg}); - defer self.register_manager.unfreezeRegs(&.{addr_reg}); + const addr_reg_lock = self.register_manager.freezeReg(addr_reg); + defer if (addr_reg_lock) |reg| self.register_manager.unfreezeReg(reg); switch (dst_mcv) { .dead => unreachable, @@ -2573,16 +2619,19 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo if (elem_size <= 8) { const raw_tmp_reg = try self.register_manager.allocReg(null); const tmp_reg = registerAlias(raw_tmp_reg, elem_size); - self.register_manager.freezeRegs(&.{tmp_reg}); - defer self.register_manager.unfreezeRegs(&.{tmp_reg}); + const tmp_reg_lock = self.register_manager.freezeRegAssumeUnused(tmp_reg); + defer self.register_manager.unfreezeReg(tmp_reg_lock); try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); } else { // TODO optimize the register allocation const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }); - self.register_manager.freezeRegs(®s); - defer self.register_manager.unfreezeRegs(®s); + var regs_locks: [4]RegisterLock = undefined; + self.register_manager.freezeRegsAssumeUnused(4, regs, ®s_locks); + defer for (regs_locks) |reg| { + self.register_manager.unfreezeReg(reg); + }; const src_reg = addr_reg; const dst_reg = regs[0]; @@ -2784,8 +2833,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type try self.genSetStack(value_ty, off, value); }, .register => |addr_reg| { - self.register_manager.freezeRegs(&.{addr_reg}); - defer self.register_manager.unfreezeRegs(&.{addr_reg}); + const addr_reg_lock = self.register_manager.freezeReg(addr_reg); + defer if (addr_reg_lock) |reg| self.register_manager.unfreezeReg(reg); switch (value) { .register => |value_reg| { @@ -2795,8 +2844,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type if (abi_size <= 8) { const raw_tmp_reg = try self.register_manager.allocReg(null); const tmp_reg = registerAlias(raw_tmp_reg, abi_size); - self.register_manager.freezeRegs(&.{tmp_reg}); - defer self.register_manager.unfreezeRegs(&.{tmp_reg}); + const tmp_reg_lock = self.register_manager.freezeRegAssumeUnused(tmp_reg); + defer self.register_manager.unfreezeReg(tmp_reg_lock); try self.genSetReg(value_ty, tmp_reg, value); try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); @@ -2856,12 +2905,12 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = struct_field_offset, }); - self.register_manager.freezeRegs(&.{offset_reg}); - defer self.register_manager.unfreezeRegs(&.{offset_reg}); + const offset_reg_lock = self.register_manager.freezeRegAssumeUnused(offset_reg); + defer self.register_manager.unfreezeReg(offset_reg_lock); const addr_reg = try self.copyToTmpRegister(ptr_ty, mcv); - self.register_manager.freezeRegs(&.{addr_reg}); - defer self.register_manager.unfreezeRegs(&.{addr_reg}); + const addr_reg_lock = self.register_manager.freezeRegAssumeUnused(addr_reg); + defer self.register_manager.unfreezeReg(addr_reg_lock); const dest = try self.binOp( .add, @@ -3369,6 +3418,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { const parent_compare_flags_inst = self.compare_flags_inst; try self.branch_stack.append(.{}); + errdefer { + _ = self.branch_stack.pop(); + } try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len); for (liveness_condbr.then_deaths) |operand| { @@ -3955,8 +4007,38 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro }, .register_c_flag, .register_v_flag, - => { - return self.fail("TODO implement genSetStack {}", .{mcv}); + => |reg| { + const reg_lock = self.register_manager.freezeReg(reg); + defer if (reg_lock) |locked_reg| self.register_manager.unfreezeReg(locked_reg); + + const wrapped_ty = ty.structFieldType(0); + try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg }); + + const overflow_bit_ty = ty.structFieldType(1); + const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*)); + const raw_cond_reg = try self.register_manager.allocReg(null); + const cond_reg = registerAlias( + raw_cond_reg, + @intCast(u32, overflow_bit_ty.abiSize(self.target.*)), + ); + + // C flag: cset reg, cs + // V flag: cset reg, vs + _ = try self.addInst(.{ + .tag = .cset, + .data = .{ .r_cond = .{ + .rd = cond_reg, + .cond = switch (mcv) { + .register_c_flag => .cs, + .register_v_flag => .vs, + else => unreachable, + }, + } }, + }); + + try self.genSetStack(overflow_bit_ty, stack_offset - overflow_bit_offset, .{ + .register = cond_reg, + }); }, .got_load, .direct_load, @@ -3983,8 +4065,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro // TODO call extern memcpy const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }); - self.register_manager.freezeRegs(®s); - defer self.register_manager.unfreezeRegs(®s); + var regs_locks: [5]RegisterLock = undefined; + self.register_manager.freezeRegsAssumeUnused(5, regs, ®s_locks); + defer for (regs_locks) |reg| { + self.register_manager.unfreezeReg(reg); + }; const src_reg = regs[0]; const dst_reg = regs[1]; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index d463ba9928..eb2654bf2e 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -23,6 +23,7 @@ const log = std.log.scoped(.codegen); const build_options = @import("build_options"); const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers); +const RegisterLock = RegisterManager.RegisterLock; const FnResult = @import("../../codegen.zig").FnResult; const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; @@ -1038,8 +1039,8 @@ fn trunc( } }, }; - self.register_manager.freezeRegs(&.{operand_reg}); - defer self.register_manager.unfreezeRegs(&.{operand_reg}); + const operand_reg_lock = self.register_manager.freezeReg(operand_reg); + defer if (operand_reg_lock) |reg| self.register_manager.unfreezeReg(reg); const dest_reg = if (maybe_inst) |inst| blk: { const ty_op = self.air.instructions.items(.data)[inst].ty_op; @@ -1127,8 +1128,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { .register => |r| r, else => try self.copyToTmpRegister(operand_ty, operand), }; - self.register_manager.freezeRegs(&.{op_reg}); - defer self.register_manager.unfreezeRegs(&.{op_reg}); + const op_reg_lock = self.register_manager.freezeRegAssumeUnused(op_reg); + defer self.register_manager.unfreezeReg(op_reg_lock); const dest_reg = blk: { if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { @@ -1157,8 +1158,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { .register => |r| r, else => try self.copyToTmpRegister(operand_ty, operand), }; - self.register_manager.freezeRegs(&.{op_reg}); - defer self.register_manager.unfreezeRegs(&.{op_reg}); + const op_reg_lock = self.register_manager.freezeRegAssumeUnused(op_reg); + defer self.register_manager.unfreezeReg(op_reg_lock); const dest_reg = blk: { if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { @@ -1218,15 +1219,15 @@ fn minMax( .register => |r| r, else => try self.copyToTmpRegister(lhs_ty, lhs), }; - self.register_manager.freezeRegs(&.{lhs_reg}); - defer self.register_manager.unfreezeRegs(&.{lhs_reg}); + const lhs_reg_lock = self.register_manager.freezeReg(lhs_reg); + defer if (lhs_reg_lock) |reg| self.register_manager.unfreezeReg(reg); const rhs_reg = switch (rhs) { .register => |r| r, else => try self.copyToTmpRegister(rhs_ty, rhs), }; - self.register_manager.freezeRegs(&.{rhs_reg}); - defer self.register_manager.unfreezeRegs(&.{rhs_reg}); + const rhs_reg_lock = self.register_manager.freezeReg(rhs_reg); + defer if (rhs_reg_lock) |reg| self.register_manager.unfreezeReg(reg); const dest_reg = if (maybe_inst) |inst| blk: { const bin_op = self.air.instructions.items(.data)[inst].bin_op; @@ -1392,12 +1393,12 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { }; const dest = try self.binOp(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); const dest_reg = dest.register; - self.register_manager.freezeRegs(&.{dest_reg}); - defer self.register_manager.unfreezeRegs(&.{dest_reg}); + const dest_reg_lock = self.register_manager.freezeRegAssumeUnused(dest_reg); + defer self.register_manager.unfreezeReg(dest_reg_lock); const truncated_reg = try self.register_manager.allocReg(null); - self.register_manager.freezeRegs(&.{truncated_reg}); - defer self.register_manager.unfreezeRegs(&.{truncated_reg}); + const truncated_reg_lock = self.register_manager.freezeRegAssumeUnused(truncated_reg); + defer self.register_manager.unfreezeReg(truncated_reg_lock); // sbfx/ubfx truncated, dest, #0, #bits try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); @@ -1493,12 +1494,12 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const dest = try self.binOpRegister(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); const dest_reg = dest.register; - self.register_manager.freezeRegs(&.{dest_reg}); - defer self.register_manager.unfreezeRegs(&.{dest_reg}); + const dest_reg_lock = self.register_manager.freezeRegAssumeUnused(dest_reg); + defer self.register_manager.unfreezeReg(dest_reg_lock); const truncated_reg = try self.register_manager.allocReg(null); - self.register_manager.freezeRegs(&.{truncated_reg}); - defer self.register_manager.unfreezeRegs(&.{truncated_reg}); + const truncated_reg_lock = self.register_manager.freezeRegAssumeUnused(truncated_reg); + defer self.register_manager.unfreezeReg(truncated_reg_lock); // sbfx/ubfx truncated, dest, #0, #bits try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); @@ -1526,28 +1527,32 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const lhs_is_register = lhs == .register; const rhs_is_register = rhs == .register; - if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); - if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); + const lhs_lock: ?RegisterLock = if (lhs_is_register) + self.register_manager.freezeReg(lhs.register) + else + null; + defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); - const lhs_reg = if (lhs_is_register) lhs.register else blk: { - const reg = try self.register_manager.allocReg(null); - self.register_manager.freezeRegs(&.{reg}); + const lhs_reg = if (lhs_is_register) + lhs.register + else + try self.register_manager.allocReg(null); + const new_lhs_lock = self.register_manager.freezeReg(lhs_reg); + defer if (new_lhs_lock) |reg| self.register_manager.unfreezeReg(reg); - break :blk reg; - }; - defer self.register_manager.unfreezeRegs(&.{lhs_reg}); - - const rhs_reg = if (rhs_is_register) rhs.register else blk: { - const reg = try self.register_manager.allocReg(null); - self.register_manager.freezeRegs(&.{reg}); - - break :blk reg; - }; - defer self.register_manager.unfreezeRegs(&.{rhs_reg}); + const rhs_reg = if (rhs_is_register) + rhs.register + else + try self.register_manager.allocReg(null); + const new_rhs_lock = self.register_manager.freezeReg(rhs_reg); + defer if (new_rhs_lock) |reg| self.register_manager.unfreezeReg(reg); const dest_regs = try self.register_manager.allocRegs(2, .{ null, null }); - self.register_manager.freezeRegs(&dest_regs); - defer self.register_manager.unfreezeRegs(&dest_regs); + var dest_regs_locks: [2]RegisterLock = undefined; + self.register_manager.freezeRegsAssumeUnused(2, dest_regs, &dest_regs_locks); + defer for (dest_regs_locks) |reg| { + self.register_manager.unfreezeReg(reg); + }; const rdlo = dest_regs[0]; const rdhi = dest_regs[1]; @@ -1555,8 +1560,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); const truncated_reg = try self.register_manager.allocReg(null); - self.register_manager.freezeRegs(&.{truncated_reg}); - defer self.register_manager.unfreezeRegs(&.{truncated_reg}); + const truncated_reg_lock = self.register_manager.freezeRegAssumeUnused(truncated_reg); + defer self.register_manager.unfreezeReg(truncated_reg_lock); _ = try self.addInst(.{ .tag = base_tag, @@ -1648,8 +1653,11 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { if (int_info.bits <= 32) { const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); - if (lhs == .register) self.register_manager.freezeRegs(&.{lhs.register}); - defer if (lhs == .register) self.register_manager.unfreezeRegs(&.{lhs.register}); + const lhs_lock: ?RegisterLock = if (lhs == .register) + self.register_manager.freezeRegAssumeUnused(lhs.register) + else + null; + defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = null; @@ -1939,8 +1947,11 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { var buf: Type.SlicePtrFieldTypeBuffer = undefined; const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); - if (index_is_register) self.register_manager.freezeRegs(&.{index_mcv.register}); - defer if (index_is_register) self.register_manager.unfreezeRegs(&.{index_mcv.register}); + const index_lock: ?RegisterLock = if (index_is_register) + self.register_manager.freezeRegAssumeUnused(index_mcv.register) + else + null; + defer if (index_lock) |reg| self.register_manager.unfreezeReg(reg); const base_mcv = slicePtr(slice_mcv); @@ -1950,20 +1961,20 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { .register => |r| r, else => try self.copyToTmpRegister(slice_ptr_field_type, base_mcv), }; - self.register_manager.freezeRegs(&.{base_reg}); - defer self.register_manager.unfreezeRegs(&.{base_reg}); + const base_reg_lock = self.register_manager.freezeRegAssumeUnused(base_reg); + defer self.register_manager.unfreezeReg(base_reg_lock); const dst_reg = try self.register_manager.allocReg(inst); const dst_mcv = MCValue{ .register = dst_reg }; - self.register_manager.freezeRegs(&.{dst_reg}); - defer self.register_manager.unfreezeRegs(&.{dst_reg}); + const dst_reg_lock = self.register_manager.freezeRegAssumeUnused(dst_reg); + defer self.register_manager.unfreezeReg(dst_reg_lock); const index_reg: Register = switch (index_mcv) { .register => |reg| reg, else => try self.copyToTmpRegister(Type.usize, index_mcv), }; - self.register_manager.freezeRegs(&.{index_reg}); - defer self.register_manager.unfreezeRegs(&.{index_reg}); + const index_reg_lock = self.register_manager.freezeRegAssumeUnused(index_reg); + defer self.register_manager.unfreezeReg(index_reg_lock); const tag: Mir.Inst.Tag = switch (elem_size) { 1 => .ldrb, @@ -2149,8 +2160,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), .register => |reg| { - self.register_manager.freezeRegs(&.{reg}); - defer self.register_manager.unfreezeRegs(&.{reg}); + const reg_lock = self.register_manager.freezeReg(reg); + defer if (reg_lock) |reg_locked| self.register_manager.unfreezeReg(reg_locked); switch (dst_mcv) { .dead => unreachable, @@ -2162,16 +2173,19 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .stack_offset => |off| { if (elem_size <= 4) { const tmp_reg = try self.register_manager.allocReg(null); - self.register_manager.freezeRegs(&.{tmp_reg}); - defer self.register_manager.unfreezeRegs(&.{tmp_reg}); + const tmp_reg_lock = self.register_manager.freezeRegAssumeUnused(tmp_reg); + defer self.register_manager.unfreezeReg(tmp_reg_lock); try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); } else { // TODO optimize the register allocation const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }); - self.register_manager.freezeRegs(®s); - defer self.register_manager.unfreezeRegs(®s); + var regs_locks: [4]RegisterLock = undefined; + self.register_manager.freezeRegsAssumeUnused(4, regs, ®s_locks); + defer for (regs_locks) |reg_locked| { + self.register_manager.unfreezeReg(reg_locked); + }; const src_reg = reg; const dst_reg = regs[0]; @@ -2197,8 +2211,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .stack_argument_offset, => { const reg = try self.register_manager.allocReg(null); - self.register_manager.freezeRegs(&.{reg}); - defer self.register_manager.unfreezeRegs(&.{reg}); + const reg_lock = self.register_manager.freezeRegAssumeUnused(reg); + defer self.register_manager.unfreezeReg(reg_lock); try self.genSetReg(ptr_ty, reg, ptr); try self.load(dst_mcv, .{ .register = reg }, ptr_ty); @@ -2252,8 +2266,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type try self.genSetStack(value_ty, off, value); }, .register => |addr_reg| { - self.register_manager.freezeRegs(&.{addr_reg}); - defer self.register_manager.unfreezeRegs(&.{addr_reg}); + const addr_reg_lock = self.register_manager.freezeReg(addr_reg); + defer if (addr_reg_lock) |reg| self.register_manager.unfreezeReg(reg); switch (value) { .dead => unreachable, @@ -2264,15 +2278,18 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type else => { if (elem_size <= 4) { const tmp_reg = try self.register_manager.allocReg(null); - self.register_manager.freezeRegs(&.{tmp_reg}); - defer self.register_manager.unfreezeRegs(&.{tmp_reg}); + const tmp_reg_lock = self.register_manager.freezeRegAssumeUnused(tmp_reg); + defer self.register_manager.unfreezeReg(tmp_reg_lock); try self.genSetReg(value_ty, tmp_reg, value); try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); } else { const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }); - self.register_manager.freezeRegs(®s); - defer self.register_manager.unfreezeRegs(®s); + var regs_locks: [4]RegisterLock = undefined; + self.register_manager.freezeRegsAssumeUnused(4, regs, ®s_locks); + defer for (regs_locks) |reg| { + self.register_manager.unfreezeReg(reg); + }; const src_reg = regs[0]; const dst_reg = addr_reg; @@ -2356,12 +2373,12 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = struct_field_offset, }); - self.register_manager.freezeRegs(&.{offset_reg}); - defer self.register_manager.unfreezeRegs(&.{offset_reg}); + const offset_reg_lock = self.register_manager.freezeRegAssumeUnused(offset_reg); + defer self.register_manager.unfreezeReg(offset_reg_lock); const addr_reg = try self.copyToTmpRegister(ptr_ty, mcv); - self.register_manager.freezeRegs(&.{addr_reg}); - defer self.register_manager.unfreezeRegs(&.{addr_reg}); + const addr_reg_lock = self.register_manager.freezeRegAssumeUnused(addr_reg); + defer self.register_manager.unfreezeReg(addr_reg_lock); const dest = try self.binOp( .add, @@ -2477,8 +2494,11 @@ fn binOpRegister( const lhs_is_register = lhs == .register; const rhs_is_register = rhs == .register; - if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); - if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); + const lhs_lock: ?RegisterLock = if (lhs_is_register) + self.register_manager.freezeReg(lhs.register) + else + null; + defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; @@ -2489,13 +2509,13 @@ fn binOpRegister( } else null; const reg = try self.register_manager.allocReg(track_inst); - self.register_manager.freezeRegs(&.{reg}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); break :blk reg; }; - defer self.register_manager.unfreezeRegs(&.{lhs_reg}); + const new_lhs_lock = self.register_manager.freezeReg(lhs_reg); + defer if (new_lhs_lock) |reg| self.register_manager.unfreezeReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else blk: { const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { @@ -2504,13 +2524,13 @@ fn binOpRegister( } else null; const reg = try self.register_manager.allocReg(track_inst); - self.register_manager.freezeRegs(&.{reg}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); break :blk reg; }; - defer self.register_manager.unfreezeRegs(&.{rhs_reg}); + const new_rhs_lock = self.register_manager.freezeReg(rhs_reg); + defer if (new_rhs_lock) |reg| self.register_manager.unfreezeReg(reg); const dest_reg = switch (mir_tag) { .cmp => .r0, // cmp has no destination regardless @@ -2593,7 +2613,11 @@ fn binOpImmediate( ) !MCValue { const lhs_is_register = lhs == .register; - if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); + const lhs_lock: ?RegisterLock = if (lhs_is_register) + self.register_manager.freezeReg(lhs.register) + else + null; + defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; @@ -2606,13 +2630,13 @@ fn binOpImmediate( } else null; const reg = try self.register_manager.allocReg(track_inst); - self.register_manager.freezeRegs(&.{reg}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); break :blk reg; }; - defer self.register_manager.unfreezeRegs(&.{lhs_reg}); + const new_lhs_lock = self.register_manager.freezeReg(lhs_reg); + defer if (new_lhs_lock) |reg| self.register_manager.unfreezeReg(reg); const dest_reg = switch (mir_tag) { .cmp => .r0, // cmp has no destination reg diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 96d30c31ce..da036379e5 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -23,6 +23,7 @@ const log = std.log.scoped(.codegen); const build_options = @import("build_options"); const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs); +const RegisterLock = RegisterManager.RegisterLock; const FnResult = @import("../../codegen.zig").FnResult; const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; @@ -937,8 +938,11 @@ fn binOpRegister( const lhs_is_register = lhs == .register; const rhs_is_register = rhs == .register; - if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); - if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); + const lhs_lock: ?RegisterLock = if (lhs_is_register) + self.register_manager.freezeReg(lhs.register) + else + null; + defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; @@ -949,13 +953,13 @@ fn binOpRegister( } else null; const reg = try self.register_manager.allocReg(track_inst); - self.register_manager.freezeRegs(&.{reg}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); break :blk reg; }; - defer self.register_manager.unfreezeRegs(&.{lhs_reg}); + const new_lhs_lock = self.register_manager.freezeReg(lhs_reg); + defer if (new_lhs_lock) |reg| self.register_manager.unfreezeReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else blk: { const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { @@ -964,13 +968,13 @@ fn binOpRegister( } else null; const reg = try self.register_manager.allocReg(track_inst); - self.register_manager.freezeRegs(&.{reg}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); break :blk reg; }; - defer self.register_manager.unfreezeRegs(&.{rhs_reg}); + const new_rhs_lock = self.register_manager.freezeReg(rhs_reg); + defer if (new_rhs_lock) |reg| self.register_manager.unfreezeReg(reg); const dest_reg = if (maybe_inst) |inst| blk: { const bin_op = self.air.instructions.items(.data)[inst].bin_op; @@ -1448,8 +1452,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .stack_offset, => { const reg = try self.register_manager.allocReg(null); - self.register_manager.freezeRegs(&.{reg}); - defer self.register_manager.unfreezeRegs(&.{reg}); + const reg_lock = self.register_manager.freezeRegAssumeUnused(reg); + defer self.register_manager.unfreezeReg(reg_lock); try self.genSetReg(ptr_ty, reg, ptr); try self.load(dst_mcv, .{ .register = reg }, ptr_ty); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 2fdce1149c..8bb7111142 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -22,6 +22,8 @@ const Liveness = @import("../../Liveness.zig"); const Mir = @import("Mir.zig"); const Module = @import("../../Module.zig"); const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; +const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers); +const RegisterLock = RegisterManager.RegisterLock; const Target = std.Target; const Type = @import("../../type.zig").Type; const TypedValue = @import("../../TypedValue.zig"); @@ -42,8 +44,6 @@ const InnerError = error{ OutOfRegisters, }; -const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers); - gpa: Allocator, air: Air, liveness: Liveness, @@ -211,40 +211,6 @@ pub const MCValue = union(enum) { else => false, }; } - - fn freezeIfRegister(mcv: MCValue, mgr: *RegisterManager) void { - switch (mcv) { - .register, - .register_overflow_signed, - .register_overflow_unsigned, - => |reg| { - mgr.freezeRegs(&.{reg}); - }, - else => {}, - } - } - - fn unfreezeIfRegister(mcv: MCValue, mgr: *RegisterManager) void { - switch (mcv) { - .register, - .register_overflow_signed, - .register_overflow_unsigned, - => |reg| { - mgr.unfreezeRegs(&.{reg}); - }, - else => {}, - } - } - - fn asRegister(mcv: MCValue) ?Register { - return switch (mcv) { - .register, - .register_overflow_signed, - .register_overflow_unsigned, - => |reg| reg, - else => null, - }; - } }; const Branch = struct { @@ -876,15 +842,20 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; branch.inst_table.putAssumeCapacityNoClobber(inst, result); - if (result.asRegister()) |reg| { - // In some cases (such as bitcast), an operand - // may be the same MCValue as the result. If - // that operand died and was a register, it - // was freed by processDeath. We have to - // "re-allocate" the register. - if (self.register_manager.isRegFree(reg)) { - self.register_manager.getRegAssumeFree(reg, inst); - } + switch (result) { + .register, + .register_overflow_signed, + .register_overflow_unsigned, + => |reg| { + // In some cases (such as bitcast), an operand + // may be the same MCValue as the result. If + // that operand died and was a register, it + // was freed by processDeath. We have to + // "re-allocate" the register. + if (self.register_manager.isRegFree(reg)) { + self.register_manager.getRegAssumeFree(reg, inst); + } + }, } } self.finishAirBookkeeping(); @@ -955,7 +926,15 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void const stack_mcv = try self.allocRegOrMem(inst, false); log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); const reg_mcv = self.getResolvedInstValue(inst); - assert(reg.to64() == reg_mcv.asRegister().?.to64()); + switch (reg_mcv) { + .register, + .register_overflow_unsigned, + .register_overflow_signed, + => |other| { + assert(reg.to64() == other.to64()); + }, + else => {}, + } const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; try branch.inst_table.put(self.gpa, inst, stack_mcv); try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{}); @@ -1043,8 +1022,11 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement intCast for abi sizes larger than 8", .{}); } - operand.freezeIfRegister(&self.register_manager); - defer operand.unfreezeIfRegister(&self.register_manager); + const operand_lock: ?RegisterLock = switch (operand) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (operand_lock) |reg| self.register_manager.unfreezeReg(reg); const reg = try self.register_manager.allocReg(inst); try self.genSetReg(dest_ty, reg, .{ .immediate = 0 }); @@ -1071,8 +1053,11 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement trunc for abi sizes larger than 8", .{}); } - operand.freezeIfRegister(&self.register_manager); - defer operand.unfreezeIfRegister(&self.register_manager); + const operand_lock: ?RegisterLock = switch (operand) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (operand_lock) |reg| self.register_manager.unfreezeReg(reg); const reg: Register = blk: { if (operand.isRegister()) { @@ -1156,16 +1141,22 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void { // TODO improve by checking if any operand can be reused. // TODO audit register allocation const lhs = try self.resolveInst(bin_op.lhs); - lhs.freezeIfRegister(&self.register_manager); - defer lhs.unfreezeIfRegister(&self.register_manager); + const lhs_lock: ?RegisterLock = switch (lhs) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); const lhs_reg = try self.copyToTmpRegister(ty, lhs); - self.register_manager.freezeRegs(&.{lhs_reg}); - defer self.register_manager.unfreezeRegs(&.{lhs_reg}); + const lhs_reg_lock = self.register_manager.freezeRegAssumeUnused(lhs_reg); + defer self.register_manager.unfreezeReg(lhs_reg_lock); const rhs_mcv = try self.limitImmediateType(bin_op.rhs, i32); - rhs_mcv.freezeIfRegister(&self.register_manager); - defer rhs_mcv.unfreezeIfRegister(&self.register_manager); + const rhs_lock: ?RegisterLock = switch (rhs_mcv) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (rhs_lock) |reg| self.register_manager.unfreezeReg(reg); try self.genBinMathOpMir(.cmp, ty, .{ .register = lhs_reg }, rhs_mcv); @@ -1200,8 +1191,11 @@ fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_r const offset = try self.resolveInst(op_rhs); const offset_ty = self.air.typeOf(op_rhs); - offset.freezeIfRegister(&self.register_manager); - defer offset.unfreezeIfRegister(&self.register_manager); + const offset_lock: ?RegisterLock = switch (offset) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (offset_lock) |reg| self.register_manager.unfreezeReg(reg); const dst_mcv = blk: { if (self.reuseOperand(inst, op_lhs, 0, ptr)) { @@ -1210,8 +1204,11 @@ fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_r break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, ptr) }; }; - dst_mcv.freezeIfRegister(&self.register_manager); - defer dst_mcv.unfreezeIfRegister(&self.register_manager); + const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (dst_mcv_lock) |reg| self.register_manager.unfreezeReg(reg); const offset_mcv = blk: { if (self.reuseOperand(inst, op_rhs, 1, offset)) { @@ -1220,8 +1217,11 @@ fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_r break :blk MCValue{ .register = try self.copyToTmpRegister(offset_ty, offset) }; }; - offset_mcv.freezeIfRegister(&self.register_manager); - defer offset_mcv.unfreezeIfRegister(&self.register_manager); + const offset_mcv_lock: ?RegisterLock = switch (offset_mcv) { + .register => |reg| self.register_manager.freezeReg(reg), + else => null, + }; + defer if (offset_mcv_lock) |reg| self.register_manager.unfreezeReg(reg); try self.genIntMulComplexOpMir(offset_ty, offset_mcv, .{ .immediate = elem_size }); @@ -1306,12 +1306,18 @@ fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air const dst_ty = self.air.typeOf(op_lhs); const lhs = try self.resolveInst(op_lhs); - lhs.freezeIfRegister(&self.register_manager); - defer lhs.unfreezeIfRegister(&self.register_manager); + const lhs_lock: ?RegisterLock = switch (lhs) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); const rhs = try self.resolveInst(op_rhs); - rhs.freezeIfRegister(&self.register_manager); - defer rhs.unfreezeIfRegister(&self.register_manager); + const rhs_lock: ?RegisterLock = switch (rhs) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (rhs_lock) |reg| self.register_manager.unfreezeReg(reg); const dst_mcv = blk: { if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) { @@ -1319,17 +1325,21 @@ fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air } break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); }; - - dst_mcv.freezeIfRegister(&self.register_manager); - defer dst_mcv.unfreezeIfRegister(&self.register_manager); + const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { + .register => |reg| self.register_manager.freezeReg(reg), + else => null, + }; + defer if (dst_mcv_lock) |reg| self.register_manager.unfreezeReg(reg); const rhs_mcv = blk: { if (rhs.isMemory() or rhs.isRegister()) break :blk rhs; break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, rhs) }; }; - - rhs_mcv.freezeIfRegister(&self.register_manager); - defer rhs_mcv.unfreezeIfRegister(&self.register_manager); + const rhs_mcv_lock: ?RegisterLock = switch (rhs_mcv) { + .register => |reg| self.register_manager.freezeReg(reg), + else => null, + }; + defer if (rhs_mcv_lock) |reg| self.register_manager.unfreezeReg(reg); try self.genBinMathOpMir(.sub, dst_ty, dst_mcv, rhs_mcv); @@ -1366,8 +1376,11 @@ fn airMul(self: *Self, inst: Air.Inst.Index) !void { // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. try self.register_manager.getReg(.rax, inst); try self.register_manager.getReg(.rdx, null); - self.register_manager.freezeRegs(&.{ .rax, .rdx }); - defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); + var reg_locks: [2]RegisterLock = undefined; + self.register_manager.freezeRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + defer for (reg_locks) |reg| { + self.register_manager.unfreezeReg(reg); + }; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); @@ -1477,8 +1490,11 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. try self.register_manager.getReg(.rax, inst); try self.register_manager.getReg(.rdx, null); - self.register_manager.freezeRegs(&.{ .rax, .rdx }); - defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); + var reg_locks: [2]RegisterLock = undefined; + self.register_manager.freezeRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + defer for (reg_locks) |reg| { + self.register_manager.unfreezeReg(reg); + }; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); @@ -1504,21 +1520,28 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - rhs.freezeIfRegister(&self.register_manager); - defer rhs.unfreezeIfRegister(&self.register_manager); + const rhs_lock: ?RegisterLock = switch (rhs) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (rhs_lock) |reg| self.register_manager.unfreezeReg(reg); const dst_reg: Register = blk: { if (lhs.isRegister()) break :blk lhs.register; break :blk try self.copyToTmpRegister(ty, lhs); }; - self.register_manager.freezeRegs(&.{dst_reg}); + const dst_reg_lock = self.register_manager.freezeRegAssumeUnused(dst_reg); + defer self.register_manager.unfreezeReg(dst_reg_lock); const rhs_mcv = blk: { if (rhs.isRegister() or rhs.isMemory()) break :blk rhs; break :blk MCValue{ .register = try self.copyToTmpRegister(ty, rhs) }; }; - rhs_mcv.freezeIfRegister(&self.register_manager); - defer rhs_mcv.unfreezeIfRegister(&self.register_manager); + const rhs_mcv_lock: ?RegisterLock = switch (rhs_mcv) { + .register => |reg| self.register_manager.freezeReg(reg), + else => null, + }; + defer if (rhs_mcv_lock) |reg| self.register_manager.unfreezeReg(reg); try self.genIntMulComplexOpMir(Type.isize, .{ .register = dst_reg }, rhs_mcv); @@ -1528,8 +1551,11 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. try self.register_manager.getReg(.rax, null); try self.register_manager.getReg(.rdx, null); - self.register_manager.freezeRegs(&.{ .rax, .rdx }); - defer self.register_manager.unfreezeRegs(&.{.rdx}); + var reg_locks: [2]RegisterLock = undefined; + self.register_manager.freezeRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + defer for (reg_locks) |reg| { + self.register_manager.unfreezeReg(reg); + }; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); @@ -1540,7 +1566,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { }, } }; - defer self.register_manager.unfreezeRegs(&.{dst_reg}); + const dst_reg_lock = self.register_manager.freezeRegAssumeUnused(dst_reg); + defer self.register_manager.unfreezeReg(dst_reg_lock); const tuple_ty = self.air.typeOfIndex(inst); const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); @@ -1554,8 +1581,11 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { }; const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }); - self.register_manager.freezeRegs(&temp_regs); - defer self.register_manager.unfreezeRegs(&temp_regs); + var temp_regs_locks: [3]RegisterLock = undefined; + self.register_manager.freezeRegsAssumeUnused(3, temp_regs, &temp_regs_locks); + defer for (temp_regs_locks) |reg| { + self.register_manager.unfreezeReg(reg); + }; const overflow_reg = temp_regs[0]; const flags: u2 = switch (int_info.signedness) { @@ -1703,14 +1733,15 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa .register => |reg| reg, else => try self.copyToTmpRegister(ty, lhs), }; - self.register_manager.freezeRegs(&.{dividend}); + const dividend_lock = self.register_manager.freezeReg(dividend); + defer if (dividend_lock) |reg| self.register_manager.unfreezeReg(reg); const divisor = switch (rhs) { .register => |reg| reg, else => try self.copyToTmpRegister(ty, rhs), }; - self.register_manager.freezeRegs(&.{divisor}); - defer self.register_manager.unfreezeRegs(&.{ dividend, divisor }); + const divisor_lock = self.register_manager.freezeReg(divisor); + defer if (divisor_lock) |reg| self.register_manager.unfreezeReg(reg); try self.genIntMulDivOpMir(switch (signedness) { .signed => .idiv, @@ -1779,20 +1810,30 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { }; try self.register_manager.getReg(.rax, track_rax); try self.register_manager.getReg(.rdx, null); - self.register_manager.freezeRegs(&.{ .rax, .rdx }); - defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); + var reg_locks: [2]RegisterLock = undefined; + self.register_manager.freezeRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + defer for (reg_locks) |reg| { + self.register_manager.unfreezeReg(reg); + }; const lhs = try self.resolveInst(bin_op.lhs); - lhs.freezeIfRegister(&self.register_manager); - defer lhs.unfreezeIfRegister(&self.register_manager); + const lhs_lock: ?RegisterLock = switch (lhs) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); const rhs = blk: { const rhs = try self.resolveInst(bin_op.rhs); if (signedness == .signed) { switch (tag) { .div_floor => { - rhs.freezeIfRegister(&self.register_manager); - defer rhs.unfreezeIfRegister(&self.register_manager); + const rhs_lock: ?RegisterLock = switch (rhs) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (rhs_lock) |reg| self.register_manager.unfreezeReg(reg); + break :blk try self.copyToRegisterWithInstTracking(inst, ty, rhs); }, else => {}, @@ -1800,8 +1841,11 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { } break :blk rhs; }; - rhs.freezeIfRegister(&self.register_manager); - defer rhs.unfreezeIfRegister(&self.register_manager); + const rhs_lock: ?RegisterLock = switch (rhs) { + .register => |reg| self.register_manager.freezeReg(reg), + else => null, + }; + defer if (rhs_lock) |reg| self.register_manager.unfreezeReg(reg); if (signedness == .unsigned) { try self.genIntMulDivOpMir(.div, ty, signedness, lhs, rhs); @@ -1835,8 +1879,11 @@ fn airRem(self: *Self, inst: Air.Inst.Index) !void { // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. try self.register_manager.getReg(.rax, null); try self.register_manager.getReg(.rdx, inst); - self.register_manager.freezeRegs(&.{ .rax, .rdx }); - defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); + var reg_locks: [2]RegisterLock = undefined; + self.register_manager.freezeRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + defer for (reg_locks) |reg| { + self.register_manager.unfreezeReg(reg); + }; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); @@ -1863,8 +1910,11 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void { // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. try self.register_manager.getReg(.rax, null); try self.register_manager.getReg(.rdx, if (signedness == .unsigned) inst else null); - self.register_manager.freezeRegs(&.{ .rax, .rdx }); - defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx }); + var reg_locks: [2]RegisterLock = undefined; + self.register_manager.freezeRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + defer for (reg_locks) |reg| { + self.register_manager.unfreezeReg(reg); + }; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); @@ -1954,12 +2004,15 @@ fn airShl(self: *Self, inst: Air.Inst.Index) !void { try self.register_manager.getReg(.rcx, null); try self.genSetReg(shift_ty, .rcx, shift); } - self.register_manager.freezeRegs(&.{.rcx}); - defer self.register_manager.unfreezeRegs(&.{.rcx}); + const rcx_lock = self.register_manager.freezeRegAssumeUnused(.rcx); + defer self.register_manager.unfreezeReg(rcx_lock); const value = try self.resolveInst(bin_op.lhs); - value.freezeIfRegister(&self.register_manager); - defer value.unfreezeIfRegister(&self.register_manager); + const value_lock: ?RegisterLock = switch (value) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (value_lock) |reg| self.register_manager.unfreezeReg(reg); const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, value); _ = try self.addInst(.{ @@ -2055,8 +2108,11 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { const err_ty = err_union_ty.errorUnionSet(); const payload_ty = err_union_ty.errorUnionPayload(); const operand = try self.resolveInst(ty_op.operand); - operand.freezeIfRegister(&self.register_manager); - defer operand.unfreezeIfRegister(&self.register_manager); + const operand_lock: ?RegisterLock = switch (operand) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (operand_lock) |reg| self.register_manager.unfreezeReg(reg); const result: MCValue = result: { if (!payload_ty.hasRuntimeBits()) break :result operand; @@ -2085,8 +2141,11 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { if (!payload_ty.hasRuntimeBits()) break :result MCValue.none; const operand = try self.resolveInst(ty_op.operand); - operand.freezeIfRegister(&self.register_manager); - defer operand.unfreezeIfRegister(&self.register_manager); + const operand_lock: ?RegisterLock = switch (operand) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (operand_lock) |reg| self.register_manager.unfreezeReg(reg); const abi_align = err_union_ty.abiAlignment(self.target.*); const err_ty = err_union_ty.errorUnionSet(); @@ -2154,8 +2213,11 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { const optional_ty = self.air.typeOfIndex(inst); const operand = try self.resolveInst(ty_op.operand); - operand.freezeIfRegister(&self.register_manager); - defer operand.unfreezeIfRegister(&self.register_manager); + const operand_lock: ?RegisterLock = switch (operand) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (operand_lock) |reg| self.register_manager.unfreezeReg(reg); if (optional_ty.isPtrLikeOptional()) { // TODO should we check if we can reuse the operand? @@ -2288,8 +2350,11 @@ fn elemOffset(self: *Self, index_ty: Type, index: MCValue, elem_size: u64) !Regi fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { const slice_ty = self.air.typeOf(lhs); const slice_mcv = try self.resolveInst(lhs); - slice_mcv.freezeIfRegister(&self.register_manager); - defer slice_mcv.unfreezeIfRegister(&self.register_manager); + const slice_mcv_lock: ?RegisterLock = switch (slice_mcv) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (slice_mcv_lock) |reg| self.register_manager.unfreezeReg(reg); const elem_ty = slice_ty.childType(); const elem_size = elem_ty.abiSize(self.target.*); @@ -2298,12 +2363,15 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { const index_ty = self.air.typeOf(rhs); const index_mcv = try self.resolveInst(rhs); - index_mcv.freezeIfRegister(&self.register_manager); - defer index_mcv.unfreezeIfRegister(&self.register_manager); + const index_mcv_lock: ?RegisterLock = switch (index_mcv) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (index_mcv_lock) |reg| self.register_manager.unfreezeReg(reg); const offset_reg = try self.elemOffset(index_ty, index_mcv, elem_size); - self.register_manager.freezeRegs(&.{offset_reg}); - defer self.register_manager.unfreezeRegs(&.{offset_reg}); + const offset_reg_lock = self.register_manager.freezeRegAssumeUnused(offset_reg); + defer self.register_manager.unfreezeReg(offset_reg_lock); const addr_reg = try self.register_manager.allocReg(null); switch (slice_mcv) { @@ -2359,20 +2427,26 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const array_ty = self.air.typeOf(bin_op.lhs); const array = try self.resolveInst(bin_op.lhs); - array.freezeIfRegister(&self.register_manager); - defer array.unfreezeIfRegister(&self.register_manager); + const array_lock: ?RegisterLock = switch (array) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (array_lock) |reg| self.register_manager.unfreezeReg(reg); const elem_ty = array_ty.childType(); const elem_abi_size = elem_ty.abiSize(self.target.*); const index_ty = self.air.typeOf(bin_op.rhs); const index = try self.resolveInst(bin_op.rhs); - index.freezeIfRegister(&self.register_manager); - defer index.unfreezeIfRegister(&self.register_manager); + const index_lock: ?RegisterLock = switch (index) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (index_lock) |reg| self.register_manager.unfreezeReg(reg); const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); - self.register_manager.freezeRegs(&.{offset_reg}); - defer self.register_manager.unfreezeRegs(&.{offset_reg}); + const offset_reg_lock = self.register_manager.freezeRegAssumeUnused(offset_reg); + defer self.register_manager.unfreezeReg(offset_reg_lock); const addr_reg = try self.register_manager.allocReg(null); switch (array) { @@ -2432,19 +2506,25 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { const ptr_ty = self.air.typeOf(bin_op.lhs); const ptr = try self.resolveInst(bin_op.lhs); - ptr.freezeIfRegister(&self.register_manager); - defer ptr.unfreezeIfRegister(&self.register_manager); + const ptr_lock: ?RegisterLock = switch (ptr) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (ptr_lock) |reg| self.register_manager.unfreezeReg(reg); const elem_ty = ptr_ty.elemType2(); const elem_abi_size = elem_ty.abiSize(self.target.*); const index_ty = self.air.typeOf(bin_op.rhs); const index = try self.resolveInst(bin_op.rhs); - index.freezeIfRegister(&self.register_manager); - defer index.unfreezeIfRegister(&self.register_manager); + const index_lock: ?RegisterLock = switch (index) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (index_lock) |reg| self.register_manager.unfreezeReg(reg); const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); - self.register_manager.freezeRegs(&.{offset_reg}); - defer self.register_manager.unfreezeRegs(&.{offset_reg}); + const offset_reg_lock = self.register_manager.freezeRegAssumeUnused(offset_reg); + defer self.register_manager.unfreezeReg(offset_reg_lock); const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); @@ -2473,19 +2553,25 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const ptr_ty = self.air.typeOf(extra.lhs); const ptr = try self.resolveInst(extra.lhs); - ptr.freezeIfRegister(&self.register_manager); - defer ptr.unfreezeIfRegister(&self.register_manager); + const ptr_lock: ?RegisterLock = switch (ptr) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (ptr_lock) |reg| self.register_manager.unfreezeReg(reg); const elem_ty = ptr_ty.elemType2(); const elem_abi_size = elem_ty.abiSize(self.target.*); const index_ty = self.air.typeOf(extra.rhs); const index = try self.resolveInst(extra.rhs); - index.freezeIfRegister(&self.register_manager); - defer index.unfreezeIfRegister(&self.register_manager); + const index_lock: ?RegisterLock = switch (index) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (index_lock) |reg| self.register_manager.unfreezeReg(reg); const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); - self.register_manager.freezeRegs(&.{offset_reg}); - defer self.register_manager.unfreezeRegs(&.{offset_reg}); + const offset_reg_lock = self.register_manager.freezeRegAssumeUnused(offset_reg); + defer self.register_manager.unfreezeReg(offset_reg_lock); const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); @@ -2506,12 +2592,18 @@ fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { } const ptr = try self.resolveInst(bin_op.lhs); - ptr.freezeIfRegister(&self.register_manager); - defer ptr.unfreezeIfRegister(&self.register_manager); + const ptr_lock: ?RegisterLock = switch (ptr) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (ptr_lock) |reg| self.register_manager.unfreezeReg(reg); const tag = try self.resolveInst(bin_op.rhs); - tag.freezeIfRegister(&self.register_manager); - defer tag.unfreezeIfRegister(&self.register_manager); + const tag_lock: ?RegisterLock = switch (tag) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (tag_lock) |reg| self.register_manager.unfreezeReg(reg); const adjusted_ptr: MCValue = if (layout.payload_size > 0 and layout.tag_align < layout.payload_align) blk: { // TODO reusing the operand @@ -2541,8 +2633,11 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { // TODO reusing the operand const operand = try self.resolveInst(ty_op.operand); - operand.freezeIfRegister(&self.register_manager); - defer operand.unfreezeIfRegister(&self.register_manager); + const operand_lock: ?RegisterLock = switch (operand) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (operand_lock) |reg| self.register_manager.unfreezeReg(reg); const tag_abi_size = tag_ty.abiSize(self.target.*); const dst_mcv: MCValue = blk: { @@ -2689,8 +2784,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }); }, .register => |reg| { - self.register_manager.freezeRegs(&.{reg}); - defer self.register_manager.unfreezeRegs(&.{reg}); + const reg_lock = self.register_manager.freezeReg(reg); + defer if (reg_lock) |locked_reg| self.register_manager.unfreezeReg(locked_reg); switch (dst_mcv) { .dead => unreachable, @@ -2815,8 +2910,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type try self.genSetStack(value_ty, off, value, .{}); }, .register => |reg| { - self.register_manager.freezeRegs(&.{reg}); - defer self.register_manager.unfreezeRegs(&.{reg}); + const reg_lock = self.register_manager.freezeReg(reg); + defer if (reg_lock) |locked_reg| self.register_manager.unfreezeReg(locked_reg); switch (value) { .none => unreachable, @@ -2906,12 +3001,15 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .direct_load, .memory, => { - value.freezeIfRegister(&self.register_manager); - defer value.unfreezeIfRegister(&self.register_manager); + const value_lock: ?RegisterLock = switch (value) { + .register => |reg| self.register_manager.freezeReg(reg), + else => null, + }; + defer if (value_lock) |reg| self.register_manager.unfreezeReg(reg); const addr_reg = try self.register_manager.allocReg(null); - self.register_manager.freezeRegs(&.{addr_reg}); - defer self.register_manager.unfreezeRegs(&.{addr_reg}); + const addr_reg_lock = self.register_manager.freezeRegAssumeUnused(addr_reg); + defer self.register_manager.unfreezeReg(addr_reg_lock); try self.loadMemPtrIntoRegister(addr_reg, ptr_ty, ptr); @@ -2982,8 +3080,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type => { if (abi_size <= 8) { const tmp_reg = try self.register_manager.allocReg(null); - self.register_manager.freezeRegs(&.{tmp_reg}); - defer self.register_manager.unfreezeRegs(&.{tmp_reg}); + const tmp_reg_lock = self.register_manager.freezeRegAssumeUnused(tmp_reg); + defer self.register_manager.unfreezeReg(tmp_reg_lock); try self.loadMemPtrIntoRegister(tmp_reg, value_ty, value); @@ -3073,8 +3171,8 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = struct_field_offset, }); - self.register_manager.freezeRegs(&.{offset_reg}); - defer self.register_manager.unfreezeRegs(&.{offset_reg}); + const offset_reg_lock = self.register_manager.freezeRegAssumeUnused(offset_reg); + defer self.register_manager.unfreezeReg(offset_reg_lock); const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, mcv); try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); @@ -3085,24 +3183,27 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde break :result MCValue{ .ptr_stack_offset = ptr_stack_offset }; }, .register => |reg| { + const reg_lock = self.register_manager.freezeRegAssumeUnused(reg); + defer self.register_manager.unfreezeReg(reg_lock); + const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = struct_field_offset, }); - self.register_manager.freezeRegs(&.{offset_reg}); - defer self.register_manager.unfreezeRegs(&.{offset_reg}); + const offset_reg_lock = self.register_manager.freezeRegAssumeUnused(offset_reg); + defer self.register_manager.unfreezeReg(offset_reg_lock); const can_reuse_operand = self.reuseOperand(inst, operand, 0, mcv); const result_reg = blk: { if (can_reuse_operand) { break :blk reg; } else { - self.register_manager.freezeRegs(&.{reg}); const result_reg = try self.register_manager.allocReg(inst); try self.genSetReg(ptr_ty, result_reg, mcv); break :blk result_reg; } }; - defer if (!can_reuse_operand) self.register_manager.unfreezeRegs(&.{reg}); + const result_reg_lock = self.register_manager.freezeReg(result_reg); + defer if (result_reg_lock) |reg_locked| self.register_manager.unfreezeReg(reg_locked); try self.genBinMathOpMir(.add, ptr_ty, .{ .register = result_reg }, .{ .register = offset_reg }); break :result MCValue{ .register = result_reg }; @@ -3130,8 +3231,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { break :result MCValue{ .stack_offset = stack_offset }; }, .register => |reg| { - self.register_manager.freezeRegs(&.{reg}); - defer self.register_manager.unfreezeRegs(&.{reg}); + const reg_lock = self.register_manager.freezeRegAssumeUnused(reg); + defer self.register_manager.unfreezeReg(reg_lock); const dst_mcv = blk: { if (self.reuseOperand(inst, operand, 0, mcv)) { @@ -3143,8 +3244,11 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { break :blk dst_mcv; } }; - dst_mcv.freezeIfRegister(&self.register_manager); - defer dst_mcv.unfreezeIfRegister(&self.register_manager); + const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { + .register => |reg| self.register_manager.freezeReg(reg), + else => null, + }; + defer if (dst_mcv_lock) |reg_locked| self.register_manager.unfreezeReg(reg_locked); // Shift by struct_field_offset. const shift = @intCast(u8, struct_field_offset * @sizeOf(usize)); @@ -3186,8 +3290,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { }, 1 => { // Get overflow bit. - mcv.freezeIfRegister(&self.register_manager); - defer mcv.unfreezeIfRegister(&self.register_manager); + const reg_lock = self.register_manager.freezeRegAssumeUnused(reg); + defer self.register_manager.unfreezeReg(reg_lock); const dst_reg = try self.register_manager.allocReg(inst); const flags: u2 = switch (mcv) { @@ -3229,12 +3333,18 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: const dst_ty = self.air.typeOf(op_lhs); const lhs = try self.resolveInst(op_lhs); - lhs.freezeIfRegister(&self.register_manager); - defer lhs.unfreezeIfRegister(&self.register_manager); + const lhs_lock: ?RegisterLock = switch (lhs) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); const rhs = try self.resolveInst(op_rhs); - rhs.freezeIfRegister(&self.register_manager); - defer rhs.unfreezeIfRegister(&self.register_manager); + const rhs_lock: ?RegisterLock = switch (rhs) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (rhs_lock) |reg| self.register_manager.unfreezeReg(reg); var flipped: bool = false; const dst_mcv = blk: { @@ -3247,16 +3357,22 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: } break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); }; - dst_mcv.freezeIfRegister(&self.register_manager); - defer dst_mcv.unfreezeIfRegister(&self.register_manager); + const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { + .register => |reg| self.register_manager.freezeReg(reg), + else => null, + }; + defer if (dst_mcv_lock) |reg| self.register_manager.unfreezeReg(reg); const src_mcv = blk: { const mcv = if (flipped) lhs else rhs; if (mcv.isRegister() or mcv.isMemory()) break :blk mcv; break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, mcv) }; }; - src_mcv.freezeIfRegister(&self.register_manager); - defer src_mcv.unfreezeIfRegister(&self.register_manager); + const src_mcv_lock: ?RegisterLock = switch (src_mcv) { + .register => |reg| self.register_manager.freezeReg(reg), + else => null, + }; + defer if (src_mcv_lock) |reg| self.register_manager.unfreezeReg(reg); const tag = self.air.instructions.items(.tag)[inst]; switch (tag) { @@ -3287,8 +3403,9 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .register_overflow_unsigned => unreachable, .register_overflow_signed => unreachable, .ptr_stack_offset => { - self.register_manager.freezeRegs(&.{dst_reg}); - defer self.register_manager.unfreezeRegs(&.{dst_reg}); + const dst_reg_lock = self.register_manager.freezeReg(dst_reg); + defer if (dst_reg_lock) |reg_locked| self.register_manager.unfreezeReg(reg_locked); + const reg = try self.copyToTmpRegister(dst_ty, src_mcv); return self.genBinMathOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); }, @@ -3318,8 +3435,9 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .compare_flags_unsigned, => { assert(abi_size <= 8); - self.register_manager.freezeRegs(&.{dst_reg}); - defer self.register_manager.unfreezeRegs(&.{dst_reg}); + const dst_reg_lock = self.register_manager.freezeReg(dst_reg); + defer if (dst_reg_lock) |reg_locked| self.register_manager.unfreezeReg(reg_locked); + const reg = try self.copyToTmpRegister(dst_ty, src_mcv); return self.genBinMathOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); }, @@ -3659,7 +3777,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. try self.register_manager.getReg(reg, null); } - if (info.return_value == .stack_offset) { + const rdi_lock: ?RegisterLock = if (info.return_value == .stack_offset) blk: { const ret_ty = fn_ty.fnReturnType(); const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*)); @@ -3668,11 +3786,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. try self.register_manager.getReg(.rdi, null); try self.genSetReg(Type.usize, .rdi, .{ .ptr_stack_offset = stack_offset }); - self.register_manager.freezeRegs(&.{.rdi}); + const rdi_lock = self.register_manager.freezeRegAssumeUnused(.rdi); info.return_value.stack_offset = stack_offset; - } - defer if (info.return_value == .stack_offset) self.register_manager.unfreezeRegs(&.{.rdi}); + + break :blk rdi_lock; + } else null; + defer if (rdi_lock) |reg| self.register_manager.unfreezeReg(reg); for (args) |arg, arg_i| { const mc_arg = info.args[arg_i]; @@ -3891,11 +4011,16 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { const ret_ty = self.fn_type.fnReturnType(); switch (self.ret_mcv) { .stack_offset => { - self.register_manager.freezeRegs(&.{ .rax, .rcx }); - defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx }); + var reg_locks: [2]RegisterLock = undefined; + self.register_manager.freezeRegsAssumeUnused(2, .{ .rax, .rcx }, ®_locks); + defer for (reg_locks) |reg| { + self.register_manager.unfreezeReg(reg); + }; + const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv); - self.register_manager.freezeRegs(&.{reg}); - defer self.register_manager.unfreezeRegs(&.{reg}); + const reg_lock = self.register_manager.freezeRegAssumeUnused(reg); + defer self.register_manager.unfreezeReg(reg_lock); + try self.genSetStack(ret_ty, 0, operand, .{ .source_stack_base = .rbp, .dest_stack_base = reg, @@ -3926,11 +4051,16 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { const elem_ty = ptr_ty.elemType(); switch (self.ret_mcv) { .stack_offset => { - self.register_manager.freezeRegs(&.{ .rax, .rcx }); - defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx }); + var reg_locks: [2]RegisterLock = undefined; + self.register_manager.freezeRegsAssumeUnused(2, .{ .rax, .rcx }, ®_locks); + defer for (reg_locks) |reg| { + self.register_manager.unfreezeReg(reg); + }; + const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv); - self.register_manager.freezeRegs(&.{reg}); - defer self.register_manager.unfreezeRegs(&.{reg}); + const reg_lock = self.register_manager.freezeRegAssumeUnused(reg); + defer self.register_manager.unfreezeReg(reg_lock); + try self.genInlineMemcpy(.{ .stack_offset = 0 }, ptr, .{ .immediate = elem_ty.abiSize(self.target.*) }, .{ .source_stack_base = .rbp, .dest_stack_base = reg, @@ -3980,12 +4110,15 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { // Source operand can be an immediate, 8 bits or 32 bits. // TODO look into reusing the operand const lhs = try self.resolveInst(bin_op.lhs); - lhs.freezeIfRegister(&self.register_manager); - defer lhs.unfreezeIfRegister(&self.register_manager); + const lhs_lock: ?RegisterLock = switch (lhs) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); const dst_reg = try self.copyToTmpRegister(ty, lhs); - self.register_manager.freezeRegs(&.{dst_reg}); - defer self.register_manager.unfreezeRegs(&.{dst_reg}); + const dst_reg_lock = self.register_manager.freezeRegAssumeUnused(dst_reg); + defer self.register_manager.unfreezeReg(dst_reg_lock); const dst_mcv = MCValue{ .register = dst_reg }; @@ -4448,8 +4581,13 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand_ptr = try self.resolveInst(un_op); - operand_ptr.freezeIfRegister(&self.register_manager); - defer operand_ptr.unfreezeIfRegister(&self.register_manager); + + const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (operand_ptr_lock) |reg| self.register_manager.unfreezeReg(reg); + const operand: MCValue = blk: { if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { // The MCValue that holds the pointer can be re-used as the value. @@ -4479,8 +4617,13 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand_ptr = try self.resolveInst(un_op); - operand_ptr.freezeIfRegister(&self.register_manager); - defer operand_ptr.unfreezeIfRegister(&self.register_manager); + + const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (operand_ptr_lock) |reg| self.register_manager.unfreezeReg(reg); + const operand: MCValue = blk: { if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { // The MCValue that holds the pointer can be re-used as the value. @@ -4510,8 +4653,13 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand_ptr = try self.resolveInst(un_op); - operand_ptr.freezeIfRegister(&self.register_manager); - defer operand_ptr.unfreezeIfRegister(&self.register_manager); + + const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (operand_ptr_lock) |reg| self.register_manager.unfreezeReg(reg); + const operand: MCValue = blk: { if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { // The MCValue that holds the pointer can be re-used as the value. @@ -4541,8 +4689,13 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand_ptr = try self.resolveInst(un_op); - operand_ptr.freezeIfRegister(&self.register_manager); - defer operand_ptr.unfreezeIfRegister(&self.register_manager); + + const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (operand_ptr_lock) |reg| self.register_manager.unfreezeReg(reg); + const operand: MCValue = blk: { if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { // The MCValue that holds the pointer can be re-used as the value. @@ -4610,8 +4763,8 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u .register => |cond_reg| { try self.spillCompareFlagsIfOccupied(); - self.register_manager.freezeRegs(&.{cond_reg}); - defer self.register_manager.unfreezeRegs(&.{cond_reg}); + const cond_reg_lock = self.register_manager.freezeReg(cond_reg); + defer if (cond_reg_lock) |reg| self.register_manager.unfreezeReg(reg); switch (case) { .none => unreachable, @@ -4670,8 +4823,8 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u if (abi_size <= 8) { const reg = try self.copyToTmpRegister(ty, condition); - self.register_manager.freezeRegs(&.{reg}); - defer self.register_manager.unfreezeRegs(&.{reg}); + const reg_lock = self.register_manager.freezeRegAssumeUnused(reg); + defer self.register_manager.unfreezeReg(reg_lock); return self.genCondSwitchMir(ty, .{ .register = reg }, case); } @@ -5158,8 +5311,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl .register_overflow_unsigned, .register_overflow_signed, => |reg| { - self.register_manager.freezeRegs(&.{reg}); - defer self.register_manager.unfreezeRegs(&.{reg}); + const reg_lock = self.register_manager.freezeReg(reg); + defer if (reg_lock) |reg_locked| self.register_manager.unfreezeReg(reg_locked); const wrapped_ty = ty.structFieldType(0); try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg }, .{}); @@ -5260,8 +5413,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl const base_reg = opts.dest_stack_base orelse .rbp; if (!math.isPowerOfTwo(abi_size)) { - self.register_manager.freezeRegs(&.{reg}); - defer self.register_manager.unfreezeRegs(&.{reg}); + const reg_lock = self.register_manager.freezeReg(reg); + defer if (reg_lock) |reg_locked| self.register_manager.unfreezeReg(reg_locked); const tmp_reg = try self.copyToTmpRegister(ty, mcv); @@ -5350,13 +5503,26 @@ fn genInlineMemcpy( len: MCValue, opts: InlineMemcpyOpts, ) InnerError!void { - self.register_manager.freezeRegs(&.{ .rax, .rcx }); + try self.register_manager.getReg(.rax, null); + try self.register_manager.getReg(.rcx, null); - if (opts.source_stack_base) |reg| self.register_manager.freezeRegs(&.{reg}); - defer if (opts.source_stack_base) |reg| self.register_manager.unfreezeRegs(&.{reg}); + var reg_locks: [2]RegisterLock = undefined; + self.register_manager.freezeRegsAssumeUnused(2, .{ .rax, .rcx }, ®_locks); + defer for (reg_locks) |reg| { + self.register_manager.unfreezeReg(reg); + }; - if (opts.dest_stack_base) |reg| self.register_manager.freezeRegs(&.{reg}); - defer if (opts.dest_stack_base) |reg| self.register_manager.unfreezeRegs(&.{reg}); + const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg| + self.register_manager.freezeReg(reg) + else + null; + defer if (ssbase_lock) |reg| self.register_manager.unfreezeReg(reg); + + const dsbase_lock: ?RegisterLock = if (opts.dest_stack_base) |reg| + self.register_manager.freezeReg(reg) + else + null; + defer if (dsbase_lock) |reg| self.register_manager.unfreezeReg(reg); const dst_addr_reg = try self.register_manager.allocReg(null); switch (dst_ptr) { @@ -5390,8 +5556,8 @@ fn genInlineMemcpy( return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); }, } - self.register_manager.freezeRegs(&.{dst_addr_reg}); - defer self.register_manager.unfreezeRegs(&.{dst_addr_reg}); + const dst_addr_reg_lock = self.register_manager.freezeRegAssumeUnused(dst_addr_reg); + defer self.register_manager.unfreezeReg(dst_addr_reg_lock); const src_addr_reg = try self.register_manager.allocReg(null); switch (src_ptr) { @@ -5425,18 +5591,13 @@ fn genInlineMemcpy( return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr}); }, } - self.register_manager.freezeRegs(&.{src_addr_reg}); - defer self.register_manager.unfreezeRegs(&.{src_addr_reg}); + const src_addr_reg_lock = self.register_manager.freezeRegAssumeUnused(src_addr_reg); + defer self.register_manager.unfreezeReg(src_addr_reg_lock); const regs = try self.register_manager.allocRegs(2, .{ null, null }); const count_reg = regs[0].to64(); const tmp_reg = regs[1].to8(); - self.register_manager.unfreezeRegs(&.{ .rax, .rcx }); - - try self.register_manager.getReg(.rax, null); - try self.register_manager.getReg(.rcx, null); - try self.genSetReg(Type.usize, count_reg, len); // mov rcx, 0 @@ -5540,7 +5701,9 @@ fn genInlineMemset( len: MCValue, opts: InlineMemcpyOpts, ) InnerError!void { - self.register_manager.freezeRegs(&.{.rax}); + try self.register_manager.getReg(.rax, null); + const rax_lock = self.register_manager.freezeRegAssumeUnused(.rax); + defer self.register_manager.unfreezeReg(rax_lock); const addr_reg = try self.register_manager.allocReg(null); switch (dst_ptr) { @@ -5574,11 +5737,8 @@ fn genInlineMemset( return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); }, } - self.register_manager.freezeRegs(&.{addr_reg}); - defer self.register_manager.unfreezeRegs(&.{addr_reg}); - - self.register_manager.unfreezeRegs(&.{.rax}); - try self.register_manager.getReg(.rax, null); + const addr_reg_lock = self.register_manager.freezeRegAssumeUnused(addr_reg); + defer self.register_manager.unfreezeReg(addr_reg_lock); try self.genSetReg(Type.usize, .rax, len); try self.genBinMathOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 }); @@ -6017,16 +6177,25 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) !void { const extra = self.air.extraData(Air.Bin, pl_op.payload).data; const dst_ptr = try self.resolveInst(pl_op.operand); - dst_ptr.freezeIfRegister(&self.register_manager); - defer dst_ptr.unfreezeIfRegister(&self.register_manager); + const dst_ptr_lock: ?RegisterLock = switch (dst_ptr) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (dst_ptr_lock) |reg| self.register_manager.unfreezeReg(reg); const src_val = try self.resolveInst(extra.lhs); - src_val.freezeIfRegister(&self.register_manager); - defer src_val.unfreezeIfRegister(&self.register_manager); + const src_val_lock: ?RegisterLock = switch (src_val) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (src_val_lock) |reg| self.register_manager.unfreezeReg(reg); const len = try self.resolveInst(extra.rhs); - len.freezeIfRegister(&self.register_manager); - defer len.unfreezeIfRegister(&self.register_manager); + const len_lock: ?RegisterLock = switch (len) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (len_lock) |reg| self.register_manager.unfreezeReg(reg); try self.genInlineMemset(dst_ptr, src_val, len, .{}); @@ -6038,17 +6207,26 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { const extra = self.air.extraData(Air.Bin, pl_op.payload).data; const dst_ptr = try self.resolveInst(pl_op.operand); - dst_ptr.freezeIfRegister(&self.register_manager); - defer dst_ptr.unfreezeIfRegister(&self.register_manager); + const dst_ptr_lock: ?RegisterLock = switch (dst_ptr) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (dst_ptr_lock) |reg| self.register_manager.unfreezeReg(reg); const src_ty = self.air.typeOf(extra.lhs); const src_ptr = try self.resolveInst(extra.lhs); - src_ptr.freezeIfRegister(&self.register_manager); - defer src_ptr.unfreezeIfRegister(&self.register_manager); + const src_ptr_lock: ?RegisterLock = switch (src_ptr) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (src_ptr_lock) |reg| self.register_manager.unfreezeReg(reg); const len = try self.resolveInst(extra.rhs); - len.freezeIfRegister(&self.register_manager); - defer len.unfreezeIfRegister(&self.register_manager); + const len_lock: ?RegisterLock = switch (len) { + .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + else => null, + }; + defer if (len_lock) |reg| self.register_manager.unfreezeReg(reg); // TODO Is this the only condition for pointer dereference for memcpy? const src: MCValue = blk: { @@ -6070,8 +6248,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { else => break :blk src_ptr, } }; - src.freezeIfRegister(&self.register_manager); - defer src.unfreezeIfRegister(&self.register_manager); + const src_lock: ?RegisterLock = switch (src) { + .register => |reg| self.register_manager.freezeReg(reg), + else => null, + }; + defer if (src_lock) |reg| self.register_manager.unfreezeReg(reg); try self.genInlineMemcpy(dst_ptr, src, len, .{}); diff --git a/src/register_manager.zig b/src/register_manager.zig index 08ac50377b..7e96d87af2 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -116,21 +116,50 @@ pub fn RegisterManager( return self.frozen_registers & mask != 0; } - /// Prevents the registers from being allocated until they are - /// unfrozen again - pub fn freezeRegs(self: *Self, regs: []const Register) void { - for (regs) |reg| { - const mask = getRegisterMask(reg) orelse continue; - self.frozen_registers |= mask; + pub const RegisterLock = struct { + register: Register, + }; + + /// Prevents the register from being allocated until they are + /// unfrozen again. + /// Returns `RegisterLock` if the register was not already + /// frozen, or `null` otherwise. + /// Only the owner of the `RegisterLock` can unfreeze the + /// register later. + pub fn freezeReg(self: *Self, reg: Register) ?RegisterLock { + if (self.isRegFrozen(reg)) return null; + const mask = getRegisterMask(reg) orelse return null; + self.frozen_registers |= mask; + return RegisterLock{ .register = reg }; + } + + /// Like `freezeReg` but asserts the register was unused always + /// returning a valid lock. + pub fn freezeRegAssumeUnused(self: *Self, reg: Register) RegisterLock { + assert(!self.isRegFrozen(reg)); + const mask = getRegisterMask(reg) orelse unreachable; + self.frozen_registers |= mask; + return RegisterLock{ .register = reg }; + } + + /// Like `freezeRegAssumeUnused` but locks multiple registers. + pub fn freezeRegsAssumeUnused( + self: *Self, + comptime count: comptime_int, + regs: [count]Register, + buf: *[count]RegisterLock, + ) void { + for (®s) |reg, i| { + buf[i] = self.freezeRegAssumeUnused(reg); } } - /// Enables the allocation of the registers - pub fn unfreezeRegs(self: *Self, regs: []const Register) void { - for (regs) |reg| { - const mask = getRegisterMask(reg) orelse continue; - self.frozen_registers &= ~mask; - } + /// Unfreezes the register allowing its re-allocation and re-use. + /// Requires `RegisterLock` to unfreeze a register. + /// Call `freezeReg` to obtain the lock first. + pub fn unfreezeReg(self: *Self, lock: RegisterLock) void { + const mask = getRegisterMask(lock.register) orelse return; + self.frozen_registers &= ~mask; } /// Returns true when at least one register is frozen @@ -419,8 +448,8 @@ test "allocReg: spilling" { // Frozen registers function.register_manager.freeReg(.r3); { - function.register_manager.freezeRegs(&.{.r2}); - defer function.register_manager.unfreezeRegs(&.{.r2}); + const lock = function.register_manager.freezeReg(.r2); + defer if (lock) |reg| function.register_manager.unfreezeReg(reg); try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); } @@ -447,8 +476,8 @@ test "tryAllocRegs" { function.register_manager.freeReg(.r2); function.register_manager.freeReg(.r3); { - function.register_manager.freezeRegs(&.{.r1}); - defer function.register_manager.unfreezeRegs(&.{.r1}); + const lock = function.register_manager.freezeReg(.r1); + defer if (lock) |reg| function.register_manager.unfreezeReg(reg); try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?); } @@ -486,8 +515,8 @@ test "allocRegs: normal usage" { // contain any valuable data anymore and can be reused. For an // example of that, see `selectively reducing register // pressure`. - function.register_manager.freezeRegs(&.{result_reg}); - defer function.register_manager.unfreezeRegs(&.{result_reg}); + const lock = function.register_manager.freezeReg(result_reg); + defer if (lock) |reg| function.register_manager.unfreezeReg(reg); const regs = try function.register_manager.allocRegs(2, .{ null, null }); try function.genAdd(result_reg, regs[0], regs[1]); @@ -507,16 +536,14 @@ test "allocRegs: selectively reducing register pressure" { { const result_reg: MockRegister2 = .r1; - function.register_manager.freezeRegs(&.{result_reg}); - defer function.register_manager.unfreezeRegs(&.{result_reg}); + const lock = function.register_manager.freezeReg(result_reg); // Here, we don't defer unfreeze because we manually unfreeze // after genAdd const regs = try function.register_manager.allocRegs(2, .{ null, null }); - function.register_manager.freezeRegs(&.{result_reg}); try function.genAdd(result_reg, regs[0], regs[1]); - function.register_manager.unfreezeRegs(®s); + function.register_manager.unfreezeReg(lock.?); const extra_summand_reg = try function.register_manager.allocReg(null); try function.genAdd(result_reg, result_reg, extra_summand_reg); diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 18a55f6653..563f937822 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -7,6 +7,7 @@ var foo: u8 align(4) = 100; test "global variable alignment" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO comptime try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4); comptime try expect(@TypeOf(&foo) == *align(4) u8); From a2b8a9756f867b75e9e9dfd0ecea9de162ad5eb1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 6 May 2022 16:26:04 -0700 Subject: [PATCH 1393/2031] CI: macos: disable stage2 test-behavior stage2 on macOS is not yet capable of passing these tests. Such improvements will be done in a follow-up change. --- ci/azure/macos_script | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ci/azure/macos_script b/ci/azure/macos_script index 1df1b10dc1..d44f4dae4b 100755 --- a/ci/azure/macos_script +++ b/ci/azure/macos_script @@ -58,8 +58,11 @@ make $JOBS install # Build stage2 standalone so that we can test stage2 against stage2 compiler-rt. release/bin/zig build -p stage2 -Denable-llvm -stage2/bin/zig build test-behavior +# TODO: enable this +#stage2/bin/zig build test-behavior +# TODO: upgrade these to test stage2 instead of stage1 +# TODO: upgrade these to test stage3 instead of stage2 release/bin/zig build test-behavior -Denable-macos-sdk -Domit-stage2 release/bin/zig build test-compiler-rt -Denable-macos-sdk release/bin/zig build test-std -Denable-macos-sdk From a2dbe6589ee6f33bb796ea3fd88b0bda5915e58e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 7 May 2022 01:44:26 +0200 Subject: [PATCH 1394/2031] macho: share traditional codepaths with stage2+llvm backend --- ci/azure/macos_script | 3 +-- src/link/MachO.zig | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/ci/azure/macos_script b/ci/azure/macos_script index d44f4dae4b..e8c7c69a33 100755 --- a/ci/azure/macos_script +++ b/ci/azure/macos_script @@ -58,8 +58,7 @@ make $JOBS install # Build stage2 standalone so that we can test stage2 against stage2 compiler-rt. release/bin/zig build -p stage2 -Denable-llvm -# TODO: enable this -#stage2/bin/zig build test-behavior +stage2/bin/zig build test-behavior # TODO: upgrade these to test stage2 instead of stage1 # TODO: upgrade these to test stage3 instead of stage2 diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 7caccb1fb8..4b804e6fcb 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -392,8 +392,9 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO { // Adhoc code signature is required when targeting aarch64-macos either directly or indirectly via the simulator // ABI such as aarch64-ios-simulator, etc. const requires_adhoc_codesig = cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator); + const use_llvm = build_options.have_llvm and options.use_llvm; const use_stage1 = build_options.is_stage1 and options.use_stage1; - const needs_prealloc = !(use_stage1 or options.cache_mode == .whole); + const needs_prealloc = !(use_stage1 or use_llvm or options.cache_mode == .whole); const self = try gpa.create(MachO); errdefer gpa.destroy(self); @@ -410,7 +411,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO { .needs_prealloc = needs_prealloc, }; - const use_llvm = build_options.have_llvm and options.use_llvm; if (use_llvm and !use_stage1) { self.llvm_object = try LlvmObject.create(gpa, options); } @@ -571,7 +571,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No if (mem.eql(u8, prev_digest, &digest)) { // Hot diggity dog! The output binary is already there. - if (use_stage1) { + const use_llvm = build_options.have_llvm and self.base.options.use_llvm; + if (use_llvm or use_stage1) { log.debug("MachO Zld digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); self.base.lock = man.toOwnedLock(); return; @@ -1025,7 +1026,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try self.createTentativeDefAtoms(); try self.parseObjectsIntoAtoms(); - if (use_stage1) { + const use_llvm = build_options.have_llvm and self.base.options.use_llvm; + if (use_llvm or use_stage1) { try self.sortSections(); try self.allocateTextSegment(); try self.allocateDataConstSegment(); @@ -1041,7 +1043,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No self.logSectionOrdinals(); } - if (use_stage1) { + if (use_llvm or use_stage1) { try self.writeAllAtoms(); } else { try self.writeAtoms(); @@ -4930,12 +4932,13 @@ fn allocateSegment(self: *MachO, index: u16, offset: u64) !void { var start: u64 = offset; for (seg.sections.items) |*sect, sect_id| { const is_zerofill = sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL; + const use_llvm = build_options.have_llvm and self.base.options.use_llvm; const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; const alignment = try math.powi(u32, 2, sect.@"align"); const start_aligned = mem.alignForwardGeneric(u64, start, alignment); // TODO handle zerofill sections in stage2 - sect.offset = if (is_zerofill and use_stage1) 0 else @intCast(u32, seg.inner.fileoff + start_aligned); + sect.offset = if (is_zerofill and (use_stage1 or use_llvm)) 0 else @intCast(u32, seg.inner.fileoff + start_aligned); sect.addr = seg.inner.vmaddr + start_aligned; // Recalculate section size given the allocated start address @@ -4963,7 +4966,7 @@ fn allocateSegment(self: *MachO, index: u16, offset: u64) !void { start = start_aligned + sect.size; - if (!(is_zerofill and use_stage1)) { + if (!(is_zerofill and (use_stage1 or use_llvm))) { seg.inner.filesize = start; } seg.inner.vmsize = start; @@ -5012,10 +5015,11 @@ fn initSection( sect.addr = seg.inner.vmaddr + off - seg.inner.fileoff; const is_zerofill = opts.flags == macho.S_ZEROFILL or opts.flags == macho.S_THREAD_LOCAL_ZEROFILL; + const use_llvm = build_options.have_llvm and self.base.options.use_llvm; const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; // TODO handle zerofill in stage2 - if (!(is_zerofill and use_stage1)) { + if (!(is_zerofill and (use_stage1 or use_llvm))) { sect.offset = @intCast(u32, off); } } From efeb031b7917f552ea73ee59c086e3d75c87cbaf Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 7 May 2022 01:52:00 +0200 Subject: [PATCH 1395/2031] macho: skip cache if cache_mode is .whole --- src/link/MachO.zig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 4b804e6fcb..fa110f28cc 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -519,7 +519,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No var needs_full_relink = true; cache: { - if (use_stage1 and self.base.options.disable_lld_caching) break :cache; + if ((use_stage1 and self.base.options.disable_lld_caching) or self.base.options.cache_mode == .whole) + break :cache; man = comp.cache_parent.obtain(); @@ -1099,7 +1100,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No } cache: { - if (use_stage1 and self.base.options.disable_lld_caching) break :cache; + if ((use_stage1 and self.base.options.disable_lld_caching) or self.base.options.cache_mode == .whole) + break :cache; // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. Cache.writeSmallFile(cache_dir_handle, id_symlink_basename, &digest) catch |err| { From 9afc4fe0e2114ae0ed53d48d98f5e12b6a269339 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 5 May 2022 14:27:20 -0700 Subject: [PATCH 1396/2031] Sema: solve a false positive "depends on itself" This improves the ABI alignment resolution code. This commit fully enables the MachO linker code in stage3. Note, however, that there are still miscompilations in stage3. --- lib/std/array_hash_map.zig | 4 -- src/Sema.zig | 1 + src/link/MachO.zig | 3 - src/link/MachO/Dylib.zig | 8 +-- src/link/tapi/yaml.zig | 2 +- src/type.zig | 129 ++++++++++++++++++++++++++----------- test/behavior/eval.zig | 111 +++++++++++++++++++++++++++++++ 7 files changed, 208 insertions(+), 50 deletions(-) diff --git a/lib/std/array_hash_map.zig b/lib/std/array_hash_map.zig index 304c98a2a9..d3c3677747 100644 --- a/lib/std/array_hash_map.zig +++ b/lib/std/array_hash_map.zig @@ -82,10 +82,6 @@ pub fn ArrayHashMap( allocator: Allocator, ctx: Context, - comptime { - std.hash_map.verifyContext(Context, K, K, u32, true); - } - /// The ArrayHashMapUnmanaged type using the same settings as this managed map. pub const Unmanaged = ArrayHashMapUnmanaged(K, V, Context, store_hash); diff --git a/src/Sema.zig b/src/Sema.zig index 9b4977c616..76edfbf2cd 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -18073,6 +18073,7 @@ fn elemValSlice( const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst); try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds); } + try sema.queueFullTypeResolution(sema.typeOf(slice)); return block.addBinOp(.slice_elem_val, slice, elem_index); } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 7caccb1fb8..b6fed43172 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1322,9 +1322,6 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy error.EndOfStream, error.NotDylib => { try file.seekTo(0); - // TODO https://github.com/ziglang/zig/issues/11367 - if (@import("builtin").zig_backend != .stage1) return error.Unexpected; - var lib_stub = LibStub.loadFromFile(self.base.allocator, file) catch { dylib.deinit(self.base.allocator); return false; diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 26bac50144..833c976bf9 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -242,20 +242,20 @@ fn addObjCClassSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) for (expanded) |sym| { if (self.symbols.contains(sym)) continue; - try self.symbols.putNoClobber(allocator, sym, .{}); + try self.symbols.putNoClobber(allocator, sym, {}); } } fn addObjCIVarSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void { const expanded = try std.fmt.allocPrint(allocator, "_OBJC_IVAR_$_{s}", .{sym_name}); if (self.symbols.contains(expanded)) return; - try self.symbols.putNoClobber(allocator, expanded, .{}); + try self.symbols.putNoClobber(allocator, expanded, {}); } fn addObjCEhTypeSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void { const expanded = try std.fmt.allocPrint(allocator, "_OBJC_EHTYPE_$_{s}", .{sym_name}); if (self.symbols.contains(expanded)) return; - try self.symbols.putNoClobber(allocator, expanded, .{}); + try self.symbols.putNoClobber(allocator, expanded, {}); } fn addSymbol(self: *Dylib, allocator: Allocator, sym_name: []const u8) !void { @@ -373,7 +373,7 @@ pub fn parseFromStub( // TODO I thought that we could switch on presence of `parent-umbrella` map; // however, turns out `libsystem_notify.dylib` is fully reexported by `libSystem.dylib` // BUT does not feature a `parent-umbrella` map as the only sublib. Apple's bug perhaps? - try umbrella_libs.put(elem.installName(), .{}); + try umbrella_libs.put(elem.installName(), {}); } switch (elem) { diff --git a/src/link/tapi/yaml.zig b/src/link/tapi/yaml.zig index 7c1997604d..e31aa41985 100644 --- a/src/link/tapi/yaml.zig +++ b/src/link/tapi/yaml.zig @@ -153,7 +153,7 @@ pub const Value = union(ValueType) { if (node.cast(Node.Doc)) |doc| { const inner = doc.value orelse { // empty doc - return Value{ .empty = .{} }; + return Value{ .empty = {} }; }; return Value.fromNode(arena, tree, inner, null); } else if (node.cast(Node.Map)) |map| { diff --git a/src/type.zig b/src/type.zig index ddeec596e1..049f7ec856 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2394,11 +2394,15 @@ pub const Type = extern union { _ = try sk.sema.typeRequiresComptime(sk.block, sk.src, ty); } switch (struct_obj.requires_comptime) { - .wip => unreachable, .yes => return false, - .no => if (struct_obj.known_non_opv) return true, + .wip, .no => if (struct_obj.known_non_opv) return true, .unknown => {}, } + if (struct_obj.status == .field_types_wip) { + // In this case, we guess that hasRuntimeBits() for this type is true, + // and then later if our guess was incorrect, we emit a compile error. + return true; + } if (sema_kit) |sk| { _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty); } @@ -2735,6 +2739,12 @@ pub const Type = extern union { val: Value, }; + const AbiAlignmentAdvancedStrat = union(enum) { + eager, + lazy: Allocator, + sema_kit: Module.WipAnalysis, + }; + /// If you pass `eager` you will get back `scalar` and assert the type is resolved. /// In this case there will be no error, guaranteed. /// If you pass `lazy` you may get back `scalar` or `val`. @@ -2744,11 +2754,7 @@ pub const Type = extern union { pub fn abiAlignmentAdvanced( ty: Type, target: Target, - strat: union(enum) { - eager, - lazy: Allocator, - sema_kit: Module.WipAnalysis, - }, + strat: AbiAlignmentAdvancedStrat, ) Module.CompileError!AbiAlignmentAdvanced { const sema_kit = switch (strat) { .sema_kit => |sk| sk, @@ -2928,21 +2934,24 @@ pub const Type = extern union { }, .@"struct" => { + const struct_obj = ty.castTag(.@"struct").?.data; if (sema_kit) |sk| { - try sk.sema.resolveTypeLayout(sk.block, sk.src, ty); - } - if (ty.castTag(.@"struct")) |payload| { - const struct_obj = payload.data; - if (!struct_obj.haveLayout()) switch (strat) { - .eager => unreachable, // struct layout not resolved - .sema_kit => unreachable, // handled above - .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }, - }; - if (struct_obj.layout == .Packed) { - var buf: Type.Payload.Bits = undefined; - const int_ty = struct_obj.packedIntegerType(target, &buf); - return AbiAlignmentAdvanced{ .scalar = int_ty.abiAlignment(target) }; + if (struct_obj.status == .field_types_wip) { + // We'll guess "pointer-aligned" and if we guess wrong, emit + // a compile error later. + return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }; } + _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty); + } + if (!struct_obj.haveFieldTypes()) switch (strat) { + .eager => unreachable, // struct layout not resolved + .sema_kit => unreachable, // handled above + .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }, + }; + if (struct_obj.layout == .Packed) { + var buf: Type.Payload.Bits = undefined; + const int_ty = struct_obj.packedIntegerType(target, &buf); + return AbiAlignmentAdvanced{ .scalar = int_ty.abiAlignment(target) }; } const fields = ty.structFields(); @@ -2950,7 +2959,16 @@ pub const Type = extern union { for (fields.values()) |field| { if (!(try field.ty.hasRuntimeBitsAdvanced(false, sema_kit))) continue; - const field_align = field.normalAlignment(target); + const field_align = if (field.abi_align != 0) + field.abi_align + else switch (try field.ty.abiAlignmentAdvanced(target, strat)) { + .scalar => |a| a, + .val => switch (strat) { + .eager => unreachable, // struct layout not resolved + .sema_kit => unreachable, // handled above + .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }, + }, + }; big_align = @maximum(big_align, field_align); } return AbiAlignmentAdvanced{ .scalar = big_align }; @@ -2980,24 +2998,14 @@ pub const Type = extern union { const int_tag_ty = ty.intTagType(&buffer); return AbiAlignmentAdvanced{ .scalar = int_tag_ty.abiAlignment(target) }; }, - .@"union" => switch (strat) { - .eager, .sema_kit => { - if (sema_kit) |sk| { - try sk.sema.resolveTypeLayout(sk.block, sk.src, ty); - } - // TODO pass `true` for have_tag when unions have a safety tag - return AbiAlignmentAdvanced{ .scalar = ty.castTag(.@"union").?.data.abiAlignment(target, false) }; - }, - .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }, + .@"union" => { + const union_obj = ty.castTag(.@"union").?.data; + // TODO pass `true` for have_tag when unions have a safety tag + return abiAlignmentAdvancedUnion(ty, target, strat, union_obj, false); }, - .union_tagged => switch (strat) { - .eager, .sema_kit => { - if (sema_kit) |sk| { - try sk.sema.resolveTypeLayout(sk.block, sk.src, ty); - } - return AbiAlignmentAdvanced{ .scalar = ty.castTag(.union_tagged).?.data.abiAlignment(target, true) }; - }, - .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }, + .union_tagged => { + const union_obj = ty.castTag(.union_tagged).?.data; + return abiAlignmentAdvancedUnion(ty, target, strat, union_obj, true); }, .empty_struct, @@ -3023,6 +3031,51 @@ pub const Type = extern union { }; } + pub fn abiAlignmentAdvancedUnion( + ty: Type, + target: Target, + strat: AbiAlignmentAdvancedStrat, + union_obj: *Module.Union, + have_tag: bool, + ) Module.CompileError!AbiAlignmentAdvanced { + const sema_kit = switch (strat) { + .sema_kit => |sk| sk, + else => null, + }; + if (sema_kit) |sk| { + if (union_obj.status == .field_types_wip) { + // We'll guess "pointer-aligned" and if we guess wrong, emit + // a compile error later. + return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }; + } + _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty); + } + if (!union_obj.haveFieldTypes()) switch (strat) { + .eager => unreachable, // union layout not resolved + .sema_kit => unreachable, // handled above + .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }, + }; + + var max_align: u32 = 0; + if (have_tag) max_align = union_obj.tag_ty.abiAlignment(target); + for (union_obj.fields.values()) |field| { + if (!(try field.ty.hasRuntimeBitsAdvanced(false, sema_kit))) continue; + + const field_align = if (field.abi_align != 0) + field.abi_align + else switch (try field.ty.abiAlignmentAdvanced(target, strat)) { + .scalar => |a| a, + .val => switch (strat) { + .eager => unreachable, // struct layout not resolved + .sema_kit => unreachable, // handled above + .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }, + }, + }; + max_align = @maximum(max_align, field_align); + } + return AbiAlignmentAdvanced{ .scalar = max_align }; + } + /// Asserts the type has the ABI size already resolved. /// Types that return false for hasRuntimeBits() return 0. pub fn abiSize(self: Type, target: Target) u64 { diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 3895b4b7b2..007ea48d87 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -999,3 +999,114 @@ test "comptime break operand passing through runtime switch converted to runtime try S.doTheTest('b'); comptime try S.doTheTest('b'); } + +test "no dependency loop for alignment of self struct" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var a: namespace.A = undefined; + a.d = .{ .g = &buf }; + a.d.g[3] = 42; + a.d.g[3] += 1; + try expect(a.d.g[3] == 43); + } + + var buf: [10]u8 align(@alignOf([*]u8)) = undefined; + + const namespace = struct { + const B = struct { a: A }; + const A = C(B); + }; + + pub fn C(comptime B: type) type { + return struct { + d: D(F) = .{}, + + const F = struct { b: B }; + }; + } + + pub fn D(comptime F: type) type { + return struct { + g: [*]align(@alignOf(F)) u8 = undefined, + }; + } + }; + try S.doTheTest(); +} + +test "no dependency loop for alignment of self bare union" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var a: namespace.A = undefined; + a.d = .{ .g = &buf }; + a.d.g[3] = 42; + a.d.g[3] += 1; + try expect(a.d.g[3] == 43); + } + + var buf: [10]u8 align(@alignOf([*]u8)) = undefined; + + const namespace = struct { + const B = union { a: A, b: void }; + const A = C(B); + }; + + pub fn C(comptime B: type) type { + return struct { + d: D(F) = .{}, + + const F = struct { b: B }; + }; + } + + pub fn D(comptime F: type) type { + return struct { + g: [*]align(@alignOf(F)) u8 = undefined, + }; + } + }; + try S.doTheTest(); +} + +test "no dependency loop for alignment of self tagged union" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var a: namespace.A = undefined; + a.d = .{ .g = &buf }; + a.d.g[3] = 42; + a.d.g[3] += 1; + try expect(a.d.g[3] == 43); + } + + var buf: [10]u8 align(@alignOf([*]u8)) = undefined; + + const namespace = struct { + const B = union(enum) { a: A, b: void }; + const A = C(B); + }; + + pub fn C(comptime B: type) type { + return struct { + d: D(F) = .{}, + + const F = struct { b: B }; + }; + } + + pub fn D(comptime F: type) type { + return struct { + g: [*]align(@alignOf(F)) u8 = undefined, + }; + } + }; + try S.doTheTest(); +} From f034cef262ed050551bc5c36d710263950e5eb27 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 5 May 2022 22:16:38 -0700 Subject: [PATCH 1397/2031] link/MachO: use const instead of var and limit scope of vars --- src/link/MachO/Dylib.zig | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 833c976bf9..57c8238827 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -214,12 +214,12 @@ fn parseSymbols(self: *Dylib, allocator: Allocator) !void { const index = self.symtab_cmd_index orelse return; const symtab_cmd = self.load_commands.items[index].symtab; - var symtab = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms); + const symtab = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms); defer allocator.free(symtab); _ = try self.file.preadAll(symtab, symtab_cmd.symoff + self.library_offset); const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab)); - var strtab = try allocator.alloc(u8, symtab_cmd.strsize); + const strtab = try allocator.alloc(u8, symtab_cmd.strsize); defer allocator.free(strtab); _ = try self.file.preadAll(strtab, symtab_cmd.stroff + self.library_offset); @@ -345,14 +345,16 @@ pub fn parseFromStub( const umbrella_lib = lib_stub.inner[0]; - var id = try Id.default(allocator, umbrella_lib.installName()); - if (umbrella_lib.currentVersion()) |version| { - try id.parseCurrentVersion(version); + { + var id = try Id.default(allocator, umbrella_lib.installName()); + if (umbrella_lib.currentVersion()) |version| { + try id.parseCurrentVersion(version); + } + if (umbrella_lib.compatibilityVersion()) |version| { + try id.parseCompatibilityVersion(version); + } + self.id = id; } - if (umbrella_lib.compatibilityVersion()) |version| { - try id.parseCompatibilityVersion(version); - } - self.id = id; var umbrella_libs = std.StringHashMap(void).init(allocator); defer umbrella_libs.deinit(); From 3b60ab4872355f0b9a9c7d0794ca8b548ab99412 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 5 May 2022 22:17:21 -0700 Subject: [PATCH 1398/2031] stage2: fix std lib tests always filtering out all tests --- src/Compilation.zig | 1 + src/Module.zig | 16 ++++++++++++++-- src/Package.zig | 8 ++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 2ad8a9e030..3f4d8d6976 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1418,6 +1418,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .gpa = gpa, .comp = comp, .main_pkg = main_pkg, + .main_pkg_is_std = try main_pkg.isInsideOf(std_pkg.*), .root_pkg = root_pkg, .zig_cache_artifact_directory = zig_cache_artifact_directory, .global_zir_cache = global_zir_cache, diff --git a/src/Module.zig b/src/Module.zig index 864663ada2..0966c8ffcf 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -130,6 +130,10 @@ stage1_flags: packed struct { } = .{}, job_queued_update_builtin_zig: bool = true, +/// This makes it so that we can run `zig test` on the standard library. +/// Otherwise, the logic for scanning test decls skips all of them because +/// `main_pkg != std_pkg`. +main_pkg_is_std: bool, compile_log_text: ArrayListUnmanaged(u8) = .{}, @@ -4528,14 +4532,22 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi // test decl with no name. Skip the part where we check against // the test name filter. if (!mod.comp.bin_file.options.is_test) break :blk false; - if (decl_pkg != mod.main_pkg) break :blk false; + if (decl_pkg != mod.main_pkg) { + if (!mod.main_pkg_is_std) break :blk false; + const std_pkg = mod.main_pkg.table.get("std").?; + if (std_pkg != decl_pkg) break :blk false; + } try mod.test_functions.put(gpa, new_decl_index, {}); break :blk true; }, else => blk: { if (!is_named_test) break :blk false; if (!mod.comp.bin_file.options.is_test) break :blk false; - if (decl_pkg != mod.main_pkg) break :blk false; + if (decl_pkg != mod.main_pkg) { + if (!mod.main_pkg_is_std) break :blk false; + const std_pkg = mod.main_pkg.table.get("std").?; + if (std_pkg != decl_pkg) break :blk false; + } // TODO check the name against --test-filter try mod.test_functions.put(gpa, new_decl_index, {}); break :blk true; diff --git a/src/Package.zig b/src/Package.zig index df894280a9..9ac1e2e5b1 100644 --- a/src/Package.zig +++ b/src/Package.zig @@ -124,3 +124,11 @@ pub fn addAndAdopt(parent: *Package, gpa: Allocator, name: []const u8, child: *P child.parent = parent; return parent.add(gpa, name, child); } + +pub fn isInsideOf(pkg: Package, another: Package) !bool { + var pkg_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; + var another_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; + const pkg_path = try pkg.root_src_directory.handle.realpath(pkg.root_src_path, &pkg_buffer); + const another_path = try another.root_src_directory.handle.realpath(".", &another_buffer); + return mem.startsWith(u8, pkg_path, another_path); +} From ec95e00e28cb23f37dc097f71afd7090e947a1cd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 6 May 2022 19:22:40 -0700 Subject: [PATCH 1399/2031] flatten lib/std/special and improve "pkg inside another" logic stage2: change logic for detecting whether the main package is inside the std package. Previously it relied on realpath() which is not portable. This uses resolve() which is how imports already work. * stage2: fix cleanup bug when creating Module * flatten lib/std/special/* to lib/* - this was motivated by making main_pkg_is_inside_std false for compiler_rt & friends. * rename "mini libc" to "universal libc" --- CMakeLists.txt | 138 +++++++++--------- build.zig | 8 +- ci/drone/linux_script_test | 2 +- ci/zinc/linux_test.sh | 2 +- doc/langref.html.in | 2 +- lib/{std/special => }/build_runner.zig | 0 lib/{std/special => }/c.zig | 0 lib/{std/special => }/compiler_rt.zig | 0 lib/{std/special => }/compiler_rt/README.md | 0 lib/{std/special => }/compiler_rt/absv.zig | 0 .../special => }/compiler_rt/absvdi2_test.zig | 0 .../special => }/compiler_rt/absvsi2_test.zig | 0 .../special => }/compiler_rt/absvti2_test.zig | 0 lib/{std/special => }/compiler_rt/addXf3.zig | 0 .../special => }/compiler_rt/addXf3_test.zig | 0 lib/{std/special => }/compiler_rt/addo.zig | 0 .../special => }/compiler_rt/addodi4_test.zig | 0 .../special => }/compiler_rt/addosi4_test.zig | 0 .../special => }/compiler_rt/addoti4_test.zig | 0 lib/{std/special => }/compiler_rt/arm.zig | 0 .../special => }/compiler_rt/ashldi3_test.zig | 0 .../special => }/compiler_rt/ashlti3_test.zig | 0 .../special => }/compiler_rt/ashrdi3_test.zig | 0 .../special => }/compiler_rt/ashrti3_test.zig | 0 lib/{std/special => }/compiler_rt/atomics.zig | 0 lib/{std/special => }/compiler_rt/aulldiv.zig | 0 lib/{std/special => }/compiler_rt/aullrem.zig | 0 lib/{std/special => }/compiler_rt/bswap.zig | 0 .../compiler_rt/bswapdi2_test.zig | 0 .../compiler_rt/bswapsi2_test.zig | 0 .../compiler_rt/bswapti2_test.zig | 0 lib/{std/special => }/compiler_rt/ceil.zig | 0 .../special => }/compiler_rt/clear_cache.zig | 0 .../special => }/compiler_rt/clzdi2_test.zig | 0 .../special => }/compiler_rt/clzsi2_test.zig | 0 .../special => }/compiler_rt/clzti2_test.zig | 0 lib/{std/special => }/compiler_rt/cmp.zig | 0 .../special => }/compiler_rt/cmpdi2_test.zig | 0 .../special => }/compiler_rt/cmpsi2_test.zig | 0 .../special => }/compiler_rt/cmpti2_test.zig | 0 .../special => }/compiler_rt/compareXf2.zig | 0 .../compiler_rt/comparedf2_test.zig | 0 .../compiler_rt/comparesf2_test.zig | 0 lib/{std/special => }/compiler_rt/cos.zig | 0 .../special => }/compiler_rt/count0bits.zig | 0 .../special => }/compiler_rt/ctzdi2_test.zig | 0 .../special => }/compiler_rt/ctzsi2_test.zig | 0 .../special => }/compiler_rt/ctzti2_test.zig | 0 lib/{std/special => }/compiler_rt/divdf3.zig | 0 .../special => }/compiler_rt/divdf3_test.zig | 0 lib/{std/special => }/compiler_rt/divsf3.zig | 0 .../special => }/compiler_rt/divsf3_test.zig | 0 lib/{std/special => }/compiler_rt/divtf3.zig | 0 .../special => }/compiler_rt/divtf3_test.zig | 0 lib/{std/special => }/compiler_rt/divti3.zig | 0 .../special => }/compiler_rt/divti3_test.zig | 0 lib/{std/special => }/compiler_rt/divxf3.zig | 0 .../special => }/compiler_rt/divxf3_test.zig | 0 lib/{std/special => }/compiler_rt/emutls.zig | 0 lib/{std/special => }/compiler_rt/exp.zig | 0 lib/{std/special => }/compiler_rt/exp2.zig | 0 .../special => }/compiler_rt/extendXfYf2.zig | 0 .../compiler_rt/extendXfYf2_test.zig | 0 .../special => }/compiler_rt/extend_f80.zig | 0 lib/{std/special => }/compiler_rt/fabs.zig | 0 .../special => }/compiler_rt/ffsdi2_test.zig | 0 .../special => }/compiler_rt/ffssi2_test.zig | 0 .../special => }/compiler_rt/ffsti2_test.zig | 0 lib/{std/special => }/compiler_rt/fixXfYi.zig | 0 .../special => }/compiler_rt/fixXfYi_test.zig | 0 .../special => }/compiler_rt/fixint_test.zig | 0 .../special => }/compiler_rt/floatXiYf.zig | 0 .../compiler_rt/floatXiYf_test.zig | 0 lib/{std/special => }/compiler_rt/floor.zig | 0 lib/{std/special => }/compiler_rt/fma.zig | 0 lib/{std/special => }/compiler_rt/fmax.zig | 0 lib/{std/special => }/compiler_rt/fmin.zig | 0 lib/{std/special => }/compiler_rt/fmod.zig | 0 .../special => }/compiler_rt/fmodq_test.zig | 0 .../special => }/compiler_rt/fmodx_test.zig | 0 lib/{std/special => }/compiler_rt/int.zig | 0 lib/{std/special => }/compiler_rt/log.zig | 0 lib/{std/special => }/compiler_rt/log10.zig | 0 lib/{std/special => }/compiler_rt/log2.zig | 0 .../special => }/compiler_rt/lshrdi3_test.zig | 0 .../special => }/compiler_rt/lshrti3_test.zig | 0 lib/{std/special => }/compiler_rt/modti3.zig | 0 .../special => }/compiler_rt/modti3_test.zig | 0 lib/{std/special => }/compiler_rt/mulXf3.zig | 0 .../special => }/compiler_rt/mulXf3_test.zig | 0 lib/{std/special => }/compiler_rt/muldi3.zig | 0 .../special => }/compiler_rt/muldi3_test.zig | 0 lib/{std/special => }/compiler_rt/mulo.zig | 0 .../special => }/compiler_rt/mulodi4_test.zig | 0 .../special => }/compiler_rt/mulosi4_test.zig | 0 .../special => }/compiler_rt/muloti4_test.zig | 0 lib/{std/special => }/compiler_rt/multi3.zig | 0 .../special => }/compiler_rt/multi3_test.zig | 0 lib/{std/special => }/compiler_rt/negXf2.zig | 0 lib/{std/special => }/compiler_rt/negXi2.zig | 0 .../special => }/compiler_rt/negdi2_test.zig | 0 .../special => }/compiler_rt/negsi2_test.zig | 0 .../special => }/compiler_rt/negti2_test.zig | 0 lib/{std/special => }/compiler_rt/negv.zig | 0 .../special => }/compiler_rt/negvdi2_test.zig | 0 .../special => }/compiler_rt/negvsi2_test.zig | 0 .../special => }/compiler_rt/negvti2_test.zig | 0 .../compiler_rt/os_version_check.zig | 0 lib/{std/special => }/compiler_rt/parity.zig | 0 .../compiler_rt/paritydi2_test.zig | 0 .../compiler_rt/paritysi2_test.zig | 0 .../compiler_rt/parityti2_test.zig | 0 .../special => }/compiler_rt/popcount.zig | 0 .../compiler_rt/popcountdi2_test.zig | 0 .../compiler_rt/popcountsi2_test.zig | 0 .../compiler_rt/popcountti2_test.zig | 0 .../special => }/compiler_rt/rem_pio2.zig | 0 .../compiler_rt/rem_pio2_large.zig | 0 .../special => }/compiler_rt/rem_pio2f.zig | 0 lib/{std/special => }/compiler_rt/round.zig | 0 lib/{std/special => }/compiler_rt/shift.zig | 0 lib/{std/special => }/compiler_rt/sin.zig | 0 lib/{std/special => }/compiler_rt/sincos.zig | 0 lib/{std/special => }/compiler_rt/sparc.zig | 0 lib/{std/special => }/compiler_rt/sqrt.zig | 0 .../special => }/compiler_rt/stack_probe.zig | 0 lib/{std/special => }/compiler_rt/subo.zig | 0 .../special => }/compiler_rt/subodi4_test.zig | 0 .../special => }/compiler_rt/subosi4_test.zig | 0 .../special => }/compiler_rt/suboti4_test.zig | 0 lib/{std/special => }/compiler_rt/tan.zig | 0 lib/{std/special => }/compiler_rt/trig.zig | 0 lib/{std/special => }/compiler_rt/trunc.zig | 0 .../special => }/compiler_rt/truncXfYf2.zig | 0 .../compiler_rt/truncXfYf2_test.zig | 0 .../special => }/compiler_rt/trunc_f80.zig | 0 .../special => }/compiler_rt/ucmpdi2_test.zig | 0 .../special => }/compiler_rt/ucmpsi2_test.zig | 0 .../special => }/compiler_rt/ucmpti2_test.zig | 0 lib/{std/special => }/compiler_rt/udivmod.zig | 0 .../compiler_rt/udivmoddi4_test.zig | 0 .../special => }/compiler_rt/udivmodti4.zig | 0 .../compiler_rt/udivmodti4_test.zig | 0 lib/{std/special => }/compiler_rt/udivti3.zig | 0 lib/{std/special => }/compiler_rt/umodti3.zig | 0 lib/{std/special => }/docs/index.html | 0 lib/{std/special => }/docs/main.js | 0 lib/{std/special => }/init-exe/build.zig | 0 lib/{std/special => }/init-exe/src/main.zig | 0 lib/{std/special => }/init-lib/build.zig | 0 lib/{std/special => }/init-lib/src/main.zig | 0 lib/{std/special => }/ssp.zig | 0 lib/std/build.zig | 2 +- lib/{std/special => }/test_runner.zig | 0 src/Compilation.zig | 33 +++-- src/Module.zig | 8 +- src/Package.zig | 8 - src/main.zig | 15 +- src/stage1/all_types.hpp | 1 - src/stage1/codegen.cpp | 22 +-- 160 files changed, 115 insertions(+), 126 deletions(-) rename lib/{std/special => }/build_runner.zig (100%) rename lib/{std/special => }/c.zig (100%) rename lib/{std/special => }/compiler_rt.zig (100%) rename lib/{std/special => }/compiler_rt/README.md (100%) rename lib/{std/special => }/compiler_rt/absv.zig (100%) rename lib/{std/special => }/compiler_rt/absvdi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/absvsi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/absvti2_test.zig (100%) rename lib/{std/special => }/compiler_rt/addXf3.zig (100%) rename lib/{std/special => }/compiler_rt/addXf3_test.zig (100%) rename lib/{std/special => }/compiler_rt/addo.zig (100%) rename lib/{std/special => }/compiler_rt/addodi4_test.zig (100%) rename lib/{std/special => }/compiler_rt/addosi4_test.zig (100%) rename lib/{std/special => }/compiler_rt/addoti4_test.zig (100%) rename lib/{std/special => }/compiler_rt/arm.zig (100%) rename lib/{std/special => }/compiler_rt/ashldi3_test.zig (100%) rename lib/{std/special => }/compiler_rt/ashlti3_test.zig (100%) rename lib/{std/special => }/compiler_rt/ashrdi3_test.zig (100%) rename lib/{std/special => }/compiler_rt/ashrti3_test.zig (100%) rename lib/{std/special => }/compiler_rt/atomics.zig (100%) rename lib/{std/special => }/compiler_rt/aulldiv.zig (100%) rename lib/{std/special => }/compiler_rt/aullrem.zig (100%) rename lib/{std/special => }/compiler_rt/bswap.zig (100%) rename lib/{std/special => }/compiler_rt/bswapdi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/bswapsi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/bswapti2_test.zig (100%) rename lib/{std/special => }/compiler_rt/ceil.zig (100%) rename lib/{std/special => }/compiler_rt/clear_cache.zig (100%) rename lib/{std/special => }/compiler_rt/clzdi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/clzsi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/clzti2_test.zig (100%) rename lib/{std/special => }/compiler_rt/cmp.zig (100%) rename lib/{std/special => }/compiler_rt/cmpdi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/cmpsi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/cmpti2_test.zig (100%) rename lib/{std/special => }/compiler_rt/compareXf2.zig (100%) rename lib/{std/special => }/compiler_rt/comparedf2_test.zig (100%) rename lib/{std/special => }/compiler_rt/comparesf2_test.zig (100%) rename lib/{std/special => }/compiler_rt/cos.zig (100%) rename lib/{std/special => }/compiler_rt/count0bits.zig (100%) rename lib/{std/special => }/compiler_rt/ctzdi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/ctzsi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/ctzti2_test.zig (100%) rename lib/{std/special => }/compiler_rt/divdf3.zig (100%) rename lib/{std/special => }/compiler_rt/divdf3_test.zig (100%) rename lib/{std/special => }/compiler_rt/divsf3.zig (100%) rename lib/{std/special => }/compiler_rt/divsf3_test.zig (100%) rename lib/{std/special => }/compiler_rt/divtf3.zig (100%) rename lib/{std/special => }/compiler_rt/divtf3_test.zig (100%) rename lib/{std/special => }/compiler_rt/divti3.zig (100%) rename lib/{std/special => }/compiler_rt/divti3_test.zig (100%) rename lib/{std/special => }/compiler_rt/divxf3.zig (100%) rename lib/{std/special => }/compiler_rt/divxf3_test.zig (100%) rename lib/{std/special => }/compiler_rt/emutls.zig (100%) rename lib/{std/special => }/compiler_rt/exp.zig (100%) rename lib/{std/special => }/compiler_rt/exp2.zig (100%) rename lib/{std/special => }/compiler_rt/extendXfYf2.zig (100%) rename lib/{std/special => }/compiler_rt/extendXfYf2_test.zig (100%) rename lib/{std/special => }/compiler_rt/extend_f80.zig (100%) rename lib/{std/special => }/compiler_rt/fabs.zig (100%) rename lib/{std/special => }/compiler_rt/ffsdi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/ffssi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/ffsti2_test.zig (100%) rename lib/{std/special => }/compiler_rt/fixXfYi.zig (100%) rename lib/{std/special => }/compiler_rt/fixXfYi_test.zig (100%) rename lib/{std/special => }/compiler_rt/fixint_test.zig (100%) rename lib/{std/special => }/compiler_rt/floatXiYf.zig (100%) rename lib/{std/special => }/compiler_rt/floatXiYf_test.zig (100%) rename lib/{std/special => }/compiler_rt/floor.zig (100%) rename lib/{std/special => }/compiler_rt/fma.zig (100%) rename lib/{std/special => }/compiler_rt/fmax.zig (100%) rename lib/{std/special => }/compiler_rt/fmin.zig (100%) rename lib/{std/special => }/compiler_rt/fmod.zig (100%) rename lib/{std/special => }/compiler_rt/fmodq_test.zig (100%) rename lib/{std/special => }/compiler_rt/fmodx_test.zig (100%) rename lib/{std/special => }/compiler_rt/int.zig (100%) rename lib/{std/special => }/compiler_rt/log.zig (100%) rename lib/{std/special => }/compiler_rt/log10.zig (100%) rename lib/{std/special => }/compiler_rt/log2.zig (100%) rename lib/{std/special => }/compiler_rt/lshrdi3_test.zig (100%) rename lib/{std/special => }/compiler_rt/lshrti3_test.zig (100%) rename lib/{std/special => }/compiler_rt/modti3.zig (100%) rename lib/{std/special => }/compiler_rt/modti3_test.zig (100%) rename lib/{std/special => }/compiler_rt/mulXf3.zig (100%) rename lib/{std/special => }/compiler_rt/mulXf3_test.zig (100%) rename lib/{std/special => }/compiler_rt/muldi3.zig (100%) rename lib/{std/special => }/compiler_rt/muldi3_test.zig (100%) rename lib/{std/special => }/compiler_rt/mulo.zig (100%) rename lib/{std/special => }/compiler_rt/mulodi4_test.zig (100%) rename lib/{std/special => }/compiler_rt/mulosi4_test.zig (100%) rename lib/{std/special => }/compiler_rt/muloti4_test.zig (100%) rename lib/{std/special => }/compiler_rt/multi3.zig (100%) rename lib/{std/special => }/compiler_rt/multi3_test.zig (100%) rename lib/{std/special => }/compiler_rt/negXf2.zig (100%) rename lib/{std/special => }/compiler_rt/negXi2.zig (100%) rename lib/{std/special => }/compiler_rt/negdi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/negsi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/negti2_test.zig (100%) rename lib/{std/special => }/compiler_rt/negv.zig (100%) rename lib/{std/special => }/compiler_rt/negvdi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/negvsi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/negvti2_test.zig (100%) rename lib/{std/special => }/compiler_rt/os_version_check.zig (100%) rename lib/{std/special => }/compiler_rt/parity.zig (100%) rename lib/{std/special => }/compiler_rt/paritydi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/paritysi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/parityti2_test.zig (100%) rename lib/{std/special => }/compiler_rt/popcount.zig (100%) rename lib/{std/special => }/compiler_rt/popcountdi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/popcountsi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/popcountti2_test.zig (100%) rename lib/{std/special => }/compiler_rt/rem_pio2.zig (100%) rename lib/{std/special => }/compiler_rt/rem_pio2_large.zig (100%) rename lib/{std/special => }/compiler_rt/rem_pio2f.zig (100%) rename lib/{std/special => }/compiler_rt/round.zig (100%) rename lib/{std/special => }/compiler_rt/shift.zig (100%) rename lib/{std/special => }/compiler_rt/sin.zig (100%) rename lib/{std/special => }/compiler_rt/sincos.zig (100%) rename lib/{std/special => }/compiler_rt/sparc.zig (100%) rename lib/{std/special => }/compiler_rt/sqrt.zig (100%) rename lib/{std/special => }/compiler_rt/stack_probe.zig (100%) rename lib/{std/special => }/compiler_rt/subo.zig (100%) rename lib/{std/special => }/compiler_rt/subodi4_test.zig (100%) rename lib/{std/special => }/compiler_rt/subosi4_test.zig (100%) rename lib/{std/special => }/compiler_rt/suboti4_test.zig (100%) rename lib/{std/special => }/compiler_rt/tan.zig (100%) rename lib/{std/special => }/compiler_rt/trig.zig (100%) rename lib/{std/special => }/compiler_rt/trunc.zig (100%) rename lib/{std/special => }/compiler_rt/truncXfYf2.zig (100%) rename lib/{std/special => }/compiler_rt/truncXfYf2_test.zig (100%) rename lib/{std/special => }/compiler_rt/trunc_f80.zig (100%) rename lib/{std/special => }/compiler_rt/ucmpdi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/ucmpsi2_test.zig (100%) rename lib/{std/special => }/compiler_rt/ucmpti2_test.zig (100%) rename lib/{std/special => }/compiler_rt/udivmod.zig (100%) rename lib/{std/special => }/compiler_rt/udivmoddi4_test.zig (100%) rename lib/{std/special => }/compiler_rt/udivmodti4.zig (100%) rename lib/{std/special => }/compiler_rt/udivmodti4_test.zig (100%) rename lib/{std/special => }/compiler_rt/udivti3.zig (100%) rename lib/{std/special => }/compiler_rt/umodti3.zig (100%) rename lib/{std/special => }/docs/index.html (100%) rename lib/{std/special => }/docs/main.js (100%) rename lib/{std/special => }/init-exe/build.zig (100%) rename lib/{std/special => }/init-exe/src/main.zig (100%) rename lib/{std/special => }/init-lib/build.zig (100%) rename lib/{std/special => }/init-lib/src/main.zig (100%) rename lib/{std/special => }/ssp.zig (100%) rename lib/{std/special => }/test_runner.zig (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index d2b0c31054..dd57918073 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -478,74 +478,74 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/process.zig" "${CMAKE_SOURCE_DIR}/lib/std/rand.zig" "${CMAKE_SOURCE_DIR}/lib/std/sort.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/absv.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/addXf3.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/addo.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/arm.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/atomics.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/aulldiv.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/aullrem.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/bswap.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/ceil.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/clear_cache.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/cmp.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/compareXf2.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/cos.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/count0bits.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divdf3.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divsf3.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divtf3.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divti3.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/divxf3.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/emutls.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/exp.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/exp2.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/extendXfYf2.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/extend_f80.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fabs.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fixXfYi.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floatXiYf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/floor.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fma.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fmax.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fmin.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/fmod.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/int.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/log.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/log10.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/log2.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/modti3.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/mulXf3.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/muldi3.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/mulo.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/multi3.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/negXf2.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/negXi2.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/negv.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/os_version_check.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/parity.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/popcount.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/rem_pio2.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/rem_pio2_large.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/rem_pio2f.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/round.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/shift.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/sin.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/sincos.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/sparc.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/sqrt.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/stack_probe.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/subo.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/tan.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/trig.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/trunc.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/truncXfYf2.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/trunc_f80.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivmod.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivmodti4.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/udivti3.zig" - "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt/umodti3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/absv.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/addXf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/addo.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/arm.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/atomics.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/aulldiv.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/aullrem.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/bswap.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/ceil.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/clear_cache.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/cmp.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/compareXf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/cos.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/count0bits.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/divdf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/divsf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/divtf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/divti3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/divxf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/emutls.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/exp.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/exp2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extendXfYf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extend_f80.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fabs.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixXfYi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatXiYf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floor.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fma.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fmax.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fmin.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fmod.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/int.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/log.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/log10.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/log2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/modti3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulXf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/muldi3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulo.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/multi3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negXf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negXi2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negv.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/os_version_check.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/parity.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/popcount.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/rem_pio2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/rem_pio2_large.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/rem_pio2f.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/round.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/shift.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/sin.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/sincos.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/sparc.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/sqrt.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/stack_probe.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subo.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/tan.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/trig.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/trunc.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/truncXfYf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/trunc_f80.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/udivmod.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/udivmodti4.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/udivti3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/umodti3.zig" "${CMAKE_SOURCE_DIR}/lib/std/start.zig" "${CMAKE_SOURCE_DIR}/lib/std/std.zig" "${CMAKE_SOURCE_DIR}/lib/std/target.zig" @@ -869,7 +869,7 @@ set(BUILD_ZIG1_ARGS -lc --pkg-begin build_options "${ZIG_CONFIG_ZIG_OUT}" --pkg-end - --pkg-begin compiler_rt "${CMAKE_SOURCE_DIR}/lib/std/special/compiler_rt.zig" + --pkg-begin compiler_rt "${CMAKE_SOURCE_DIR}/lib/compiler_rt.zig" --pkg-end ) diff --git a/build.zig b/build.zig index 4d3cf492bd..a70623ce8e 100644 --- a/build.zig +++ b/build.zig @@ -450,7 +450,7 @@ pub fn build(b: *Builder) !void { toolchain_step.dependOn(tests.addPkgTests( b, test_filter, - "lib/std/special/compiler_rt.zig", + "lib/compiler_rt.zig", "compiler-rt", "Run the compiler_rt tests", modes, @@ -462,9 +462,9 @@ pub fn build(b: *Builder) !void { toolchain_step.dependOn(tests.addPkgTests( b, test_filter, - "lib/std/special/c.zig", - "minilibc", - "Run the mini libc tests", + "lib/c.zig", + "universal-libc", + "Run the universal libc tests", modes, true, // skip_single_threaded skip_non_native, diff --git a/ci/drone/linux_script_test b/ci/drone/linux_script_test index 14f6fd85df..12f9439102 100755 --- a/ci/drone/linux_script_test +++ b/ci/drone/linux_script_test @@ -28,7 +28,7 @@ case "$1" in ./build/zig build $BUILD_FLAGS test-std -Dskip-debug -Dskip-release-safe -Dskip-release-fast ;; 6) - ./build/zig build $BUILD_FLAGS test-minilibc + ./build/zig build $BUILD_FLAGS test-universal-libc ./build/zig build $BUILD_FLAGS test-compare-output ./build/zig build $BUILD_FLAGS test-standalone -Dskip-release-safe ./build/zig build $BUILD_FLAGS test-stack-traces diff --git a/ci/zinc/linux_test.sh b/ci/zinc/linux_test.sh index 180589cc8e..c15a4e15c8 100755 --- a/ci/zinc/linux_test.sh +++ b/ci/zinc/linux_test.sh @@ -69,7 +69,7 @@ stage2/bin/zig test test/behavior.zig -I test -fno-LLVM -target x86_64-macos -- $ZIG build test-behavior -fqemu -fwasmtime $ZIG build test-compiler-rt -fqemu -fwasmtime $ZIG build test-std -fqemu -fwasmtime -$ZIG build test-minilibc -fqemu -fwasmtime +$ZIG build test-universal-libc -fqemu -fwasmtime $ZIG build test-compare-output -fqemu -fwasmtime $ZIG build test-standalone -fqemu -fwasmtime $ZIG build test-stack-traces -fqemu -fwasmtime diff --git a/doc/langref.html.in b/doc/langref.html.in index 3c5de6c8d2..d81661ecd3 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1040,7 +1040,7 @@ fn addOne(number: i32) i32 {

The shell output shown above displays two lines after the zig test command. These lines are diff --git a/lib/std/special/build_runner.zig b/lib/build_runner.zig similarity index 100% rename from lib/std/special/build_runner.zig rename to lib/build_runner.zig diff --git a/lib/std/special/c.zig b/lib/c.zig similarity index 100% rename from lib/std/special/c.zig rename to lib/c.zig diff --git a/lib/std/special/compiler_rt.zig b/lib/compiler_rt.zig similarity index 100% rename from lib/std/special/compiler_rt.zig rename to lib/compiler_rt.zig diff --git a/lib/std/special/compiler_rt/README.md b/lib/compiler_rt/README.md similarity index 100% rename from lib/std/special/compiler_rt/README.md rename to lib/compiler_rt/README.md diff --git a/lib/std/special/compiler_rt/absv.zig b/lib/compiler_rt/absv.zig similarity index 100% rename from lib/std/special/compiler_rt/absv.zig rename to lib/compiler_rt/absv.zig diff --git a/lib/std/special/compiler_rt/absvdi2_test.zig b/lib/compiler_rt/absvdi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/absvdi2_test.zig rename to lib/compiler_rt/absvdi2_test.zig diff --git a/lib/std/special/compiler_rt/absvsi2_test.zig b/lib/compiler_rt/absvsi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/absvsi2_test.zig rename to lib/compiler_rt/absvsi2_test.zig diff --git a/lib/std/special/compiler_rt/absvti2_test.zig b/lib/compiler_rt/absvti2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/absvti2_test.zig rename to lib/compiler_rt/absvti2_test.zig diff --git a/lib/std/special/compiler_rt/addXf3.zig b/lib/compiler_rt/addXf3.zig similarity index 100% rename from lib/std/special/compiler_rt/addXf3.zig rename to lib/compiler_rt/addXf3.zig diff --git a/lib/std/special/compiler_rt/addXf3_test.zig b/lib/compiler_rt/addXf3_test.zig similarity index 100% rename from lib/std/special/compiler_rt/addXf3_test.zig rename to lib/compiler_rt/addXf3_test.zig diff --git a/lib/std/special/compiler_rt/addo.zig b/lib/compiler_rt/addo.zig similarity index 100% rename from lib/std/special/compiler_rt/addo.zig rename to lib/compiler_rt/addo.zig diff --git a/lib/std/special/compiler_rt/addodi4_test.zig b/lib/compiler_rt/addodi4_test.zig similarity index 100% rename from lib/std/special/compiler_rt/addodi4_test.zig rename to lib/compiler_rt/addodi4_test.zig diff --git a/lib/std/special/compiler_rt/addosi4_test.zig b/lib/compiler_rt/addosi4_test.zig similarity index 100% rename from lib/std/special/compiler_rt/addosi4_test.zig rename to lib/compiler_rt/addosi4_test.zig diff --git a/lib/std/special/compiler_rt/addoti4_test.zig b/lib/compiler_rt/addoti4_test.zig similarity index 100% rename from lib/std/special/compiler_rt/addoti4_test.zig rename to lib/compiler_rt/addoti4_test.zig diff --git a/lib/std/special/compiler_rt/arm.zig b/lib/compiler_rt/arm.zig similarity index 100% rename from lib/std/special/compiler_rt/arm.zig rename to lib/compiler_rt/arm.zig diff --git a/lib/std/special/compiler_rt/ashldi3_test.zig b/lib/compiler_rt/ashldi3_test.zig similarity index 100% rename from lib/std/special/compiler_rt/ashldi3_test.zig rename to lib/compiler_rt/ashldi3_test.zig diff --git a/lib/std/special/compiler_rt/ashlti3_test.zig b/lib/compiler_rt/ashlti3_test.zig similarity index 100% rename from lib/std/special/compiler_rt/ashlti3_test.zig rename to lib/compiler_rt/ashlti3_test.zig diff --git a/lib/std/special/compiler_rt/ashrdi3_test.zig b/lib/compiler_rt/ashrdi3_test.zig similarity index 100% rename from lib/std/special/compiler_rt/ashrdi3_test.zig rename to lib/compiler_rt/ashrdi3_test.zig diff --git a/lib/std/special/compiler_rt/ashrti3_test.zig b/lib/compiler_rt/ashrti3_test.zig similarity index 100% rename from lib/std/special/compiler_rt/ashrti3_test.zig rename to lib/compiler_rt/ashrti3_test.zig diff --git a/lib/std/special/compiler_rt/atomics.zig b/lib/compiler_rt/atomics.zig similarity index 100% rename from lib/std/special/compiler_rt/atomics.zig rename to lib/compiler_rt/atomics.zig diff --git a/lib/std/special/compiler_rt/aulldiv.zig b/lib/compiler_rt/aulldiv.zig similarity index 100% rename from lib/std/special/compiler_rt/aulldiv.zig rename to lib/compiler_rt/aulldiv.zig diff --git a/lib/std/special/compiler_rt/aullrem.zig b/lib/compiler_rt/aullrem.zig similarity index 100% rename from lib/std/special/compiler_rt/aullrem.zig rename to lib/compiler_rt/aullrem.zig diff --git a/lib/std/special/compiler_rt/bswap.zig b/lib/compiler_rt/bswap.zig similarity index 100% rename from lib/std/special/compiler_rt/bswap.zig rename to lib/compiler_rt/bswap.zig diff --git a/lib/std/special/compiler_rt/bswapdi2_test.zig b/lib/compiler_rt/bswapdi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/bswapdi2_test.zig rename to lib/compiler_rt/bswapdi2_test.zig diff --git a/lib/std/special/compiler_rt/bswapsi2_test.zig b/lib/compiler_rt/bswapsi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/bswapsi2_test.zig rename to lib/compiler_rt/bswapsi2_test.zig diff --git a/lib/std/special/compiler_rt/bswapti2_test.zig b/lib/compiler_rt/bswapti2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/bswapti2_test.zig rename to lib/compiler_rt/bswapti2_test.zig diff --git a/lib/std/special/compiler_rt/ceil.zig b/lib/compiler_rt/ceil.zig similarity index 100% rename from lib/std/special/compiler_rt/ceil.zig rename to lib/compiler_rt/ceil.zig diff --git a/lib/std/special/compiler_rt/clear_cache.zig b/lib/compiler_rt/clear_cache.zig similarity index 100% rename from lib/std/special/compiler_rt/clear_cache.zig rename to lib/compiler_rt/clear_cache.zig diff --git a/lib/std/special/compiler_rt/clzdi2_test.zig b/lib/compiler_rt/clzdi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/clzdi2_test.zig rename to lib/compiler_rt/clzdi2_test.zig diff --git a/lib/std/special/compiler_rt/clzsi2_test.zig b/lib/compiler_rt/clzsi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/clzsi2_test.zig rename to lib/compiler_rt/clzsi2_test.zig diff --git a/lib/std/special/compiler_rt/clzti2_test.zig b/lib/compiler_rt/clzti2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/clzti2_test.zig rename to lib/compiler_rt/clzti2_test.zig diff --git a/lib/std/special/compiler_rt/cmp.zig b/lib/compiler_rt/cmp.zig similarity index 100% rename from lib/std/special/compiler_rt/cmp.zig rename to lib/compiler_rt/cmp.zig diff --git a/lib/std/special/compiler_rt/cmpdi2_test.zig b/lib/compiler_rt/cmpdi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/cmpdi2_test.zig rename to lib/compiler_rt/cmpdi2_test.zig diff --git a/lib/std/special/compiler_rt/cmpsi2_test.zig b/lib/compiler_rt/cmpsi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/cmpsi2_test.zig rename to lib/compiler_rt/cmpsi2_test.zig diff --git a/lib/std/special/compiler_rt/cmpti2_test.zig b/lib/compiler_rt/cmpti2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/cmpti2_test.zig rename to lib/compiler_rt/cmpti2_test.zig diff --git a/lib/std/special/compiler_rt/compareXf2.zig b/lib/compiler_rt/compareXf2.zig similarity index 100% rename from lib/std/special/compiler_rt/compareXf2.zig rename to lib/compiler_rt/compareXf2.zig diff --git a/lib/std/special/compiler_rt/comparedf2_test.zig b/lib/compiler_rt/comparedf2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/comparedf2_test.zig rename to lib/compiler_rt/comparedf2_test.zig diff --git a/lib/std/special/compiler_rt/comparesf2_test.zig b/lib/compiler_rt/comparesf2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/comparesf2_test.zig rename to lib/compiler_rt/comparesf2_test.zig diff --git a/lib/std/special/compiler_rt/cos.zig b/lib/compiler_rt/cos.zig similarity index 100% rename from lib/std/special/compiler_rt/cos.zig rename to lib/compiler_rt/cos.zig diff --git a/lib/std/special/compiler_rt/count0bits.zig b/lib/compiler_rt/count0bits.zig similarity index 100% rename from lib/std/special/compiler_rt/count0bits.zig rename to lib/compiler_rt/count0bits.zig diff --git a/lib/std/special/compiler_rt/ctzdi2_test.zig b/lib/compiler_rt/ctzdi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/ctzdi2_test.zig rename to lib/compiler_rt/ctzdi2_test.zig diff --git a/lib/std/special/compiler_rt/ctzsi2_test.zig b/lib/compiler_rt/ctzsi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/ctzsi2_test.zig rename to lib/compiler_rt/ctzsi2_test.zig diff --git a/lib/std/special/compiler_rt/ctzti2_test.zig b/lib/compiler_rt/ctzti2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/ctzti2_test.zig rename to lib/compiler_rt/ctzti2_test.zig diff --git a/lib/std/special/compiler_rt/divdf3.zig b/lib/compiler_rt/divdf3.zig similarity index 100% rename from lib/std/special/compiler_rt/divdf3.zig rename to lib/compiler_rt/divdf3.zig diff --git a/lib/std/special/compiler_rt/divdf3_test.zig b/lib/compiler_rt/divdf3_test.zig similarity index 100% rename from lib/std/special/compiler_rt/divdf3_test.zig rename to lib/compiler_rt/divdf3_test.zig diff --git a/lib/std/special/compiler_rt/divsf3.zig b/lib/compiler_rt/divsf3.zig similarity index 100% rename from lib/std/special/compiler_rt/divsf3.zig rename to lib/compiler_rt/divsf3.zig diff --git a/lib/std/special/compiler_rt/divsf3_test.zig b/lib/compiler_rt/divsf3_test.zig similarity index 100% rename from lib/std/special/compiler_rt/divsf3_test.zig rename to lib/compiler_rt/divsf3_test.zig diff --git a/lib/std/special/compiler_rt/divtf3.zig b/lib/compiler_rt/divtf3.zig similarity index 100% rename from lib/std/special/compiler_rt/divtf3.zig rename to lib/compiler_rt/divtf3.zig diff --git a/lib/std/special/compiler_rt/divtf3_test.zig b/lib/compiler_rt/divtf3_test.zig similarity index 100% rename from lib/std/special/compiler_rt/divtf3_test.zig rename to lib/compiler_rt/divtf3_test.zig diff --git a/lib/std/special/compiler_rt/divti3.zig b/lib/compiler_rt/divti3.zig similarity index 100% rename from lib/std/special/compiler_rt/divti3.zig rename to lib/compiler_rt/divti3.zig diff --git a/lib/std/special/compiler_rt/divti3_test.zig b/lib/compiler_rt/divti3_test.zig similarity index 100% rename from lib/std/special/compiler_rt/divti3_test.zig rename to lib/compiler_rt/divti3_test.zig diff --git a/lib/std/special/compiler_rt/divxf3.zig b/lib/compiler_rt/divxf3.zig similarity index 100% rename from lib/std/special/compiler_rt/divxf3.zig rename to lib/compiler_rt/divxf3.zig diff --git a/lib/std/special/compiler_rt/divxf3_test.zig b/lib/compiler_rt/divxf3_test.zig similarity index 100% rename from lib/std/special/compiler_rt/divxf3_test.zig rename to lib/compiler_rt/divxf3_test.zig diff --git a/lib/std/special/compiler_rt/emutls.zig b/lib/compiler_rt/emutls.zig similarity index 100% rename from lib/std/special/compiler_rt/emutls.zig rename to lib/compiler_rt/emutls.zig diff --git a/lib/std/special/compiler_rt/exp.zig b/lib/compiler_rt/exp.zig similarity index 100% rename from lib/std/special/compiler_rt/exp.zig rename to lib/compiler_rt/exp.zig diff --git a/lib/std/special/compiler_rt/exp2.zig b/lib/compiler_rt/exp2.zig similarity index 100% rename from lib/std/special/compiler_rt/exp2.zig rename to lib/compiler_rt/exp2.zig diff --git a/lib/std/special/compiler_rt/extendXfYf2.zig b/lib/compiler_rt/extendXfYf2.zig similarity index 100% rename from lib/std/special/compiler_rt/extendXfYf2.zig rename to lib/compiler_rt/extendXfYf2.zig diff --git a/lib/std/special/compiler_rt/extendXfYf2_test.zig b/lib/compiler_rt/extendXfYf2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/extendXfYf2_test.zig rename to lib/compiler_rt/extendXfYf2_test.zig diff --git a/lib/std/special/compiler_rt/extend_f80.zig b/lib/compiler_rt/extend_f80.zig similarity index 100% rename from lib/std/special/compiler_rt/extend_f80.zig rename to lib/compiler_rt/extend_f80.zig diff --git a/lib/std/special/compiler_rt/fabs.zig b/lib/compiler_rt/fabs.zig similarity index 100% rename from lib/std/special/compiler_rt/fabs.zig rename to lib/compiler_rt/fabs.zig diff --git a/lib/std/special/compiler_rt/ffsdi2_test.zig b/lib/compiler_rt/ffsdi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/ffsdi2_test.zig rename to lib/compiler_rt/ffsdi2_test.zig diff --git a/lib/std/special/compiler_rt/ffssi2_test.zig b/lib/compiler_rt/ffssi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/ffssi2_test.zig rename to lib/compiler_rt/ffssi2_test.zig diff --git a/lib/std/special/compiler_rt/ffsti2_test.zig b/lib/compiler_rt/ffsti2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/ffsti2_test.zig rename to lib/compiler_rt/ffsti2_test.zig diff --git a/lib/std/special/compiler_rt/fixXfYi.zig b/lib/compiler_rt/fixXfYi.zig similarity index 100% rename from lib/std/special/compiler_rt/fixXfYi.zig rename to lib/compiler_rt/fixXfYi.zig diff --git a/lib/std/special/compiler_rt/fixXfYi_test.zig b/lib/compiler_rt/fixXfYi_test.zig similarity index 100% rename from lib/std/special/compiler_rt/fixXfYi_test.zig rename to lib/compiler_rt/fixXfYi_test.zig diff --git a/lib/std/special/compiler_rt/fixint_test.zig b/lib/compiler_rt/fixint_test.zig similarity index 100% rename from lib/std/special/compiler_rt/fixint_test.zig rename to lib/compiler_rt/fixint_test.zig diff --git a/lib/std/special/compiler_rt/floatXiYf.zig b/lib/compiler_rt/floatXiYf.zig similarity index 100% rename from lib/std/special/compiler_rt/floatXiYf.zig rename to lib/compiler_rt/floatXiYf.zig diff --git a/lib/std/special/compiler_rt/floatXiYf_test.zig b/lib/compiler_rt/floatXiYf_test.zig similarity index 100% rename from lib/std/special/compiler_rt/floatXiYf_test.zig rename to lib/compiler_rt/floatXiYf_test.zig diff --git a/lib/std/special/compiler_rt/floor.zig b/lib/compiler_rt/floor.zig similarity index 100% rename from lib/std/special/compiler_rt/floor.zig rename to lib/compiler_rt/floor.zig diff --git a/lib/std/special/compiler_rt/fma.zig b/lib/compiler_rt/fma.zig similarity index 100% rename from lib/std/special/compiler_rt/fma.zig rename to lib/compiler_rt/fma.zig diff --git a/lib/std/special/compiler_rt/fmax.zig b/lib/compiler_rt/fmax.zig similarity index 100% rename from lib/std/special/compiler_rt/fmax.zig rename to lib/compiler_rt/fmax.zig diff --git a/lib/std/special/compiler_rt/fmin.zig b/lib/compiler_rt/fmin.zig similarity index 100% rename from lib/std/special/compiler_rt/fmin.zig rename to lib/compiler_rt/fmin.zig diff --git a/lib/std/special/compiler_rt/fmod.zig b/lib/compiler_rt/fmod.zig similarity index 100% rename from lib/std/special/compiler_rt/fmod.zig rename to lib/compiler_rt/fmod.zig diff --git a/lib/std/special/compiler_rt/fmodq_test.zig b/lib/compiler_rt/fmodq_test.zig similarity index 100% rename from lib/std/special/compiler_rt/fmodq_test.zig rename to lib/compiler_rt/fmodq_test.zig diff --git a/lib/std/special/compiler_rt/fmodx_test.zig b/lib/compiler_rt/fmodx_test.zig similarity index 100% rename from lib/std/special/compiler_rt/fmodx_test.zig rename to lib/compiler_rt/fmodx_test.zig diff --git a/lib/std/special/compiler_rt/int.zig b/lib/compiler_rt/int.zig similarity index 100% rename from lib/std/special/compiler_rt/int.zig rename to lib/compiler_rt/int.zig diff --git a/lib/std/special/compiler_rt/log.zig b/lib/compiler_rt/log.zig similarity index 100% rename from lib/std/special/compiler_rt/log.zig rename to lib/compiler_rt/log.zig diff --git a/lib/std/special/compiler_rt/log10.zig b/lib/compiler_rt/log10.zig similarity index 100% rename from lib/std/special/compiler_rt/log10.zig rename to lib/compiler_rt/log10.zig diff --git a/lib/std/special/compiler_rt/log2.zig b/lib/compiler_rt/log2.zig similarity index 100% rename from lib/std/special/compiler_rt/log2.zig rename to lib/compiler_rt/log2.zig diff --git a/lib/std/special/compiler_rt/lshrdi3_test.zig b/lib/compiler_rt/lshrdi3_test.zig similarity index 100% rename from lib/std/special/compiler_rt/lshrdi3_test.zig rename to lib/compiler_rt/lshrdi3_test.zig diff --git a/lib/std/special/compiler_rt/lshrti3_test.zig b/lib/compiler_rt/lshrti3_test.zig similarity index 100% rename from lib/std/special/compiler_rt/lshrti3_test.zig rename to lib/compiler_rt/lshrti3_test.zig diff --git a/lib/std/special/compiler_rt/modti3.zig b/lib/compiler_rt/modti3.zig similarity index 100% rename from lib/std/special/compiler_rt/modti3.zig rename to lib/compiler_rt/modti3.zig diff --git a/lib/std/special/compiler_rt/modti3_test.zig b/lib/compiler_rt/modti3_test.zig similarity index 100% rename from lib/std/special/compiler_rt/modti3_test.zig rename to lib/compiler_rt/modti3_test.zig diff --git a/lib/std/special/compiler_rt/mulXf3.zig b/lib/compiler_rt/mulXf3.zig similarity index 100% rename from lib/std/special/compiler_rt/mulXf3.zig rename to lib/compiler_rt/mulXf3.zig diff --git a/lib/std/special/compiler_rt/mulXf3_test.zig b/lib/compiler_rt/mulXf3_test.zig similarity index 100% rename from lib/std/special/compiler_rt/mulXf3_test.zig rename to lib/compiler_rt/mulXf3_test.zig diff --git a/lib/std/special/compiler_rt/muldi3.zig b/lib/compiler_rt/muldi3.zig similarity index 100% rename from lib/std/special/compiler_rt/muldi3.zig rename to lib/compiler_rt/muldi3.zig diff --git a/lib/std/special/compiler_rt/muldi3_test.zig b/lib/compiler_rt/muldi3_test.zig similarity index 100% rename from lib/std/special/compiler_rt/muldi3_test.zig rename to lib/compiler_rt/muldi3_test.zig diff --git a/lib/std/special/compiler_rt/mulo.zig b/lib/compiler_rt/mulo.zig similarity index 100% rename from lib/std/special/compiler_rt/mulo.zig rename to lib/compiler_rt/mulo.zig diff --git a/lib/std/special/compiler_rt/mulodi4_test.zig b/lib/compiler_rt/mulodi4_test.zig similarity index 100% rename from lib/std/special/compiler_rt/mulodi4_test.zig rename to lib/compiler_rt/mulodi4_test.zig diff --git a/lib/std/special/compiler_rt/mulosi4_test.zig b/lib/compiler_rt/mulosi4_test.zig similarity index 100% rename from lib/std/special/compiler_rt/mulosi4_test.zig rename to lib/compiler_rt/mulosi4_test.zig diff --git a/lib/std/special/compiler_rt/muloti4_test.zig b/lib/compiler_rt/muloti4_test.zig similarity index 100% rename from lib/std/special/compiler_rt/muloti4_test.zig rename to lib/compiler_rt/muloti4_test.zig diff --git a/lib/std/special/compiler_rt/multi3.zig b/lib/compiler_rt/multi3.zig similarity index 100% rename from lib/std/special/compiler_rt/multi3.zig rename to lib/compiler_rt/multi3.zig diff --git a/lib/std/special/compiler_rt/multi3_test.zig b/lib/compiler_rt/multi3_test.zig similarity index 100% rename from lib/std/special/compiler_rt/multi3_test.zig rename to lib/compiler_rt/multi3_test.zig diff --git a/lib/std/special/compiler_rt/negXf2.zig b/lib/compiler_rt/negXf2.zig similarity index 100% rename from lib/std/special/compiler_rt/negXf2.zig rename to lib/compiler_rt/negXf2.zig diff --git a/lib/std/special/compiler_rt/negXi2.zig b/lib/compiler_rt/negXi2.zig similarity index 100% rename from lib/std/special/compiler_rt/negXi2.zig rename to lib/compiler_rt/negXi2.zig diff --git a/lib/std/special/compiler_rt/negdi2_test.zig b/lib/compiler_rt/negdi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/negdi2_test.zig rename to lib/compiler_rt/negdi2_test.zig diff --git a/lib/std/special/compiler_rt/negsi2_test.zig b/lib/compiler_rt/negsi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/negsi2_test.zig rename to lib/compiler_rt/negsi2_test.zig diff --git a/lib/std/special/compiler_rt/negti2_test.zig b/lib/compiler_rt/negti2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/negti2_test.zig rename to lib/compiler_rt/negti2_test.zig diff --git a/lib/std/special/compiler_rt/negv.zig b/lib/compiler_rt/negv.zig similarity index 100% rename from lib/std/special/compiler_rt/negv.zig rename to lib/compiler_rt/negv.zig diff --git a/lib/std/special/compiler_rt/negvdi2_test.zig b/lib/compiler_rt/negvdi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/negvdi2_test.zig rename to lib/compiler_rt/negvdi2_test.zig diff --git a/lib/std/special/compiler_rt/negvsi2_test.zig b/lib/compiler_rt/negvsi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/negvsi2_test.zig rename to lib/compiler_rt/negvsi2_test.zig diff --git a/lib/std/special/compiler_rt/negvti2_test.zig b/lib/compiler_rt/negvti2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/negvti2_test.zig rename to lib/compiler_rt/negvti2_test.zig diff --git a/lib/std/special/compiler_rt/os_version_check.zig b/lib/compiler_rt/os_version_check.zig similarity index 100% rename from lib/std/special/compiler_rt/os_version_check.zig rename to lib/compiler_rt/os_version_check.zig diff --git a/lib/std/special/compiler_rt/parity.zig b/lib/compiler_rt/parity.zig similarity index 100% rename from lib/std/special/compiler_rt/parity.zig rename to lib/compiler_rt/parity.zig diff --git a/lib/std/special/compiler_rt/paritydi2_test.zig b/lib/compiler_rt/paritydi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/paritydi2_test.zig rename to lib/compiler_rt/paritydi2_test.zig diff --git a/lib/std/special/compiler_rt/paritysi2_test.zig b/lib/compiler_rt/paritysi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/paritysi2_test.zig rename to lib/compiler_rt/paritysi2_test.zig diff --git a/lib/std/special/compiler_rt/parityti2_test.zig b/lib/compiler_rt/parityti2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/parityti2_test.zig rename to lib/compiler_rt/parityti2_test.zig diff --git a/lib/std/special/compiler_rt/popcount.zig b/lib/compiler_rt/popcount.zig similarity index 100% rename from lib/std/special/compiler_rt/popcount.zig rename to lib/compiler_rt/popcount.zig diff --git a/lib/std/special/compiler_rt/popcountdi2_test.zig b/lib/compiler_rt/popcountdi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/popcountdi2_test.zig rename to lib/compiler_rt/popcountdi2_test.zig diff --git a/lib/std/special/compiler_rt/popcountsi2_test.zig b/lib/compiler_rt/popcountsi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/popcountsi2_test.zig rename to lib/compiler_rt/popcountsi2_test.zig diff --git a/lib/std/special/compiler_rt/popcountti2_test.zig b/lib/compiler_rt/popcountti2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/popcountti2_test.zig rename to lib/compiler_rt/popcountti2_test.zig diff --git a/lib/std/special/compiler_rt/rem_pio2.zig b/lib/compiler_rt/rem_pio2.zig similarity index 100% rename from lib/std/special/compiler_rt/rem_pio2.zig rename to lib/compiler_rt/rem_pio2.zig diff --git a/lib/std/special/compiler_rt/rem_pio2_large.zig b/lib/compiler_rt/rem_pio2_large.zig similarity index 100% rename from lib/std/special/compiler_rt/rem_pio2_large.zig rename to lib/compiler_rt/rem_pio2_large.zig diff --git a/lib/std/special/compiler_rt/rem_pio2f.zig b/lib/compiler_rt/rem_pio2f.zig similarity index 100% rename from lib/std/special/compiler_rt/rem_pio2f.zig rename to lib/compiler_rt/rem_pio2f.zig diff --git a/lib/std/special/compiler_rt/round.zig b/lib/compiler_rt/round.zig similarity index 100% rename from lib/std/special/compiler_rt/round.zig rename to lib/compiler_rt/round.zig diff --git a/lib/std/special/compiler_rt/shift.zig b/lib/compiler_rt/shift.zig similarity index 100% rename from lib/std/special/compiler_rt/shift.zig rename to lib/compiler_rt/shift.zig diff --git a/lib/std/special/compiler_rt/sin.zig b/lib/compiler_rt/sin.zig similarity index 100% rename from lib/std/special/compiler_rt/sin.zig rename to lib/compiler_rt/sin.zig diff --git a/lib/std/special/compiler_rt/sincos.zig b/lib/compiler_rt/sincos.zig similarity index 100% rename from lib/std/special/compiler_rt/sincos.zig rename to lib/compiler_rt/sincos.zig diff --git a/lib/std/special/compiler_rt/sparc.zig b/lib/compiler_rt/sparc.zig similarity index 100% rename from lib/std/special/compiler_rt/sparc.zig rename to lib/compiler_rt/sparc.zig diff --git a/lib/std/special/compiler_rt/sqrt.zig b/lib/compiler_rt/sqrt.zig similarity index 100% rename from lib/std/special/compiler_rt/sqrt.zig rename to lib/compiler_rt/sqrt.zig diff --git a/lib/std/special/compiler_rt/stack_probe.zig b/lib/compiler_rt/stack_probe.zig similarity index 100% rename from lib/std/special/compiler_rt/stack_probe.zig rename to lib/compiler_rt/stack_probe.zig diff --git a/lib/std/special/compiler_rt/subo.zig b/lib/compiler_rt/subo.zig similarity index 100% rename from lib/std/special/compiler_rt/subo.zig rename to lib/compiler_rt/subo.zig diff --git a/lib/std/special/compiler_rt/subodi4_test.zig b/lib/compiler_rt/subodi4_test.zig similarity index 100% rename from lib/std/special/compiler_rt/subodi4_test.zig rename to lib/compiler_rt/subodi4_test.zig diff --git a/lib/std/special/compiler_rt/subosi4_test.zig b/lib/compiler_rt/subosi4_test.zig similarity index 100% rename from lib/std/special/compiler_rt/subosi4_test.zig rename to lib/compiler_rt/subosi4_test.zig diff --git a/lib/std/special/compiler_rt/suboti4_test.zig b/lib/compiler_rt/suboti4_test.zig similarity index 100% rename from lib/std/special/compiler_rt/suboti4_test.zig rename to lib/compiler_rt/suboti4_test.zig diff --git a/lib/std/special/compiler_rt/tan.zig b/lib/compiler_rt/tan.zig similarity index 100% rename from lib/std/special/compiler_rt/tan.zig rename to lib/compiler_rt/tan.zig diff --git a/lib/std/special/compiler_rt/trig.zig b/lib/compiler_rt/trig.zig similarity index 100% rename from lib/std/special/compiler_rt/trig.zig rename to lib/compiler_rt/trig.zig diff --git a/lib/std/special/compiler_rt/trunc.zig b/lib/compiler_rt/trunc.zig similarity index 100% rename from lib/std/special/compiler_rt/trunc.zig rename to lib/compiler_rt/trunc.zig diff --git a/lib/std/special/compiler_rt/truncXfYf2.zig b/lib/compiler_rt/truncXfYf2.zig similarity index 100% rename from lib/std/special/compiler_rt/truncXfYf2.zig rename to lib/compiler_rt/truncXfYf2.zig diff --git a/lib/std/special/compiler_rt/truncXfYf2_test.zig b/lib/compiler_rt/truncXfYf2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/truncXfYf2_test.zig rename to lib/compiler_rt/truncXfYf2_test.zig diff --git a/lib/std/special/compiler_rt/trunc_f80.zig b/lib/compiler_rt/trunc_f80.zig similarity index 100% rename from lib/std/special/compiler_rt/trunc_f80.zig rename to lib/compiler_rt/trunc_f80.zig diff --git a/lib/std/special/compiler_rt/ucmpdi2_test.zig b/lib/compiler_rt/ucmpdi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/ucmpdi2_test.zig rename to lib/compiler_rt/ucmpdi2_test.zig diff --git a/lib/std/special/compiler_rt/ucmpsi2_test.zig b/lib/compiler_rt/ucmpsi2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/ucmpsi2_test.zig rename to lib/compiler_rt/ucmpsi2_test.zig diff --git a/lib/std/special/compiler_rt/ucmpti2_test.zig b/lib/compiler_rt/ucmpti2_test.zig similarity index 100% rename from lib/std/special/compiler_rt/ucmpti2_test.zig rename to lib/compiler_rt/ucmpti2_test.zig diff --git a/lib/std/special/compiler_rt/udivmod.zig b/lib/compiler_rt/udivmod.zig similarity index 100% rename from lib/std/special/compiler_rt/udivmod.zig rename to lib/compiler_rt/udivmod.zig diff --git a/lib/std/special/compiler_rt/udivmoddi4_test.zig b/lib/compiler_rt/udivmoddi4_test.zig similarity index 100% rename from lib/std/special/compiler_rt/udivmoddi4_test.zig rename to lib/compiler_rt/udivmoddi4_test.zig diff --git a/lib/std/special/compiler_rt/udivmodti4.zig b/lib/compiler_rt/udivmodti4.zig similarity index 100% rename from lib/std/special/compiler_rt/udivmodti4.zig rename to lib/compiler_rt/udivmodti4.zig diff --git a/lib/std/special/compiler_rt/udivmodti4_test.zig b/lib/compiler_rt/udivmodti4_test.zig similarity index 100% rename from lib/std/special/compiler_rt/udivmodti4_test.zig rename to lib/compiler_rt/udivmodti4_test.zig diff --git a/lib/std/special/compiler_rt/udivti3.zig b/lib/compiler_rt/udivti3.zig similarity index 100% rename from lib/std/special/compiler_rt/udivti3.zig rename to lib/compiler_rt/udivti3.zig diff --git a/lib/std/special/compiler_rt/umodti3.zig b/lib/compiler_rt/umodti3.zig similarity index 100% rename from lib/std/special/compiler_rt/umodti3.zig rename to lib/compiler_rt/umodti3.zig diff --git a/lib/std/special/docs/index.html b/lib/docs/index.html similarity index 100% rename from lib/std/special/docs/index.html rename to lib/docs/index.html diff --git a/lib/std/special/docs/main.js b/lib/docs/main.js similarity index 100% rename from lib/std/special/docs/main.js rename to lib/docs/main.js diff --git a/lib/std/special/init-exe/build.zig b/lib/init-exe/build.zig similarity index 100% rename from lib/std/special/init-exe/build.zig rename to lib/init-exe/build.zig diff --git a/lib/std/special/init-exe/src/main.zig b/lib/init-exe/src/main.zig similarity index 100% rename from lib/std/special/init-exe/src/main.zig rename to lib/init-exe/src/main.zig diff --git a/lib/std/special/init-lib/build.zig b/lib/init-lib/build.zig similarity index 100% rename from lib/std/special/init-lib/build.zig rename to lib/init-lib/build.zig diff --git a/lib/std/special/init-lib/src/main.zig b/lib/init-lib/src/main.zig similarity index 100% rename from lib/std/special/init-lib/src/main.zig rename to lib/init-lib/src/main.zig diff --git a/lib/std/special/ssp.zig b/lib/ssp.zig similarity index 100% rename from lib/std/special/ssp.zig rename to lib/ssp.zig diff --git a/lib/std/build.zig b/lib/std/build.zig index 38744cea1e..fcc349a62b 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -229,7 +229,7 @@ pub const Builder = struct { self.allocator.destroy(self); } - /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file. + /// This function is intended to be called by lib/build_runner.zig, not a build.zig file. pub fn resolveInstallPrefix(self: *Builder, install_prefix: ?[]const u8, dir_list: DirList) void { if (self.dest_dir) |dest_dir| { self.install_prefix = install_prefix orelse "/usr"; diff --git a/lib/std/special/test_runner.zig b/lib/test_runner.zig similarity index 100% rename from lib/std/special/test_runner.zig rename to lib/test_runner.zig diff --git a/src/Compilation.zig b/src/Compilation.zig index 3f4d8d6976..9126289804 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1352,7 +1352,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { const test_pkg = try Package.createWithDir( gpa, options.zig_lib_directory, - "std" ++ std.fs.path.sep_str ++ "special", + null, "test_runner.zig", ); errdefer test_pkg.destroy(gpa); @@ -1382,6 +1382,20 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { try builtin_pkg.add(gpa, "std", std_pkg); try builtin_pkg.add(gpa, "builtin", builtin_pkg); + const main_pkg_in_std = m: { + const std_path = try std.fs.path.resolve(arena, &[_][]const u8{ + std_pkg.root_src_directory.path orelse ".", + std.fs.path.dirname(std_pkg.root_src_path) orelse ".", + }); + defer arena.free(std_path); + const main_path = try std.fs.path.resolve(arena, &[_][]const u8{ + main_pkg.root_src_directory.path orelse ".", + main_pkg.root_src_path, + }); + defer arena.free(main_path); + break :m mem.startsWith(u8, main_path, std_path); + }; + // Pre-open the directory handles for cached ZIR code so that it does not need // to redundantly happen for each AstGen operation. const zir_sub_dir = "z"; @@ -1418,15 +1432,15 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .gpa = gpa, .comp = comp, .main_pkg = main_pkg, - .main_pkg_is_std = try main_pkg.isInsideOf(std_pkg.*), + .main_pkg_in_std = main_pkg_in_std, .root_pkg = root_pkg, .zig_cache_artifact_directory = zig_cache_artifact_directory, .global_zir_cache = global_zir_cache, .local_zir_cache = local_zir_cache, .emit_h = emit_h, - .error_name_list = try std.ArrayListUnmanaged([]const u8).initCapacity(gpa, 1), + .error_name_list = .{}, }; - module.error_name_list.appendAssumeCapacity("(no error)"); + try module.error_name_list.append(gpa, "(no error)"); break :blk module; } else blk: { @@ -4780,18 +4794,9 @@ fn buildOutputFromZig( defer tracy_trace.end(); std.debug.assert(output_mode != .Exe); - const special_sub = "std" ++ std.fs.path.sep_str ++ "special"; - const special_path = try comp.zig_lib_directory.join(comp.gpa, &[_][]const u8{special_sub}); - defer comp.gpa.free(special_path); - - var special_dir = try comp.zig_lib_directory.handle.openDir(special_sub, .{}); - defer special_dir.close(); var main_pkg: Package = .{ - .root_src_directory = .{ - .path = special_path, - .handle = special_dir, - }, + .root_src_directory = comp.zig_lib_directory, .root_src_path = src_basename, }; defer main_pkg.deinitTable(comp.gpa); diff --git a/src/Module.zig b/src/Module.zig index 0966c8ffcf..de29753b13 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -112,7 +112,7 @@ global_error_set: std.StringHashMapUnmanaged(ErrorInt) = .{}, /// ErrorInt -> []const u8 for fast lookups for @intToError at comptime /// Corresponds with `global_error_set`. -error_name_list: ArrayListUnmanaged([]const u8) = .{}, +error_name_list: ArrayListUnmanaged([]const u8), /// Incrementing integer used to compare against the corresponding Decl /// field to determine whether a Decl's status applies to an ongoing update, or a @@ -133,7 +133,7 @@ job_queued_update_builtin_zig: bool = true, /// This makes it so that we can run `zig test` on the standard library. /// Otherwise, the logic for scanning test decls skips all of them because /// `main_pkg != std_pkg`. -main_pkg_is_std: bool, +main_pkg_in_std: bool, compile_log_text: ArrayListUnmanaged(u8) = .{}, @@ -4533,7 +4533,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi // the test name filter. if (!mod.comp.bin_file.options.is_test) break :blk false; if (decl_pkg != mod.main_pkg) { - if (!mod.main_pkg_is_std) break :blk false; + if (!mod.main_pkg_in_std) break :blk false; const std_pkg = mod.main_pkg.table.get("std").?; if (std_pkg != decl_pkg) break :blk false; } @@ -4544,7 +4544,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi if (!is_named_test) break :blk false; if (!mod.comp.bin_file.options.is_test) break :blk false; if (decl_pkg != mod.main_pkg) { - if (!mod.main_pkg_is_std) break :blk false; + if (!mod.main_pkg_in_std) break :blk false; const std_pkg = mod.main_pkg.table.get("std").?; if (std_pkg != decl_pkg) break :blk false; } diff --git a/src/Package.zig b/src/Package.zig index 9ac1e2e5b1..df894280a9 100644 --- a/src/Package.zig +++ b/src/Package.zig @@ -124,11 +124,3 @@ pub fn addAndAdopt(parent: *Package, gpa: Allocator, name: []const u8, child: *P child.parent = parent; return parent.add(gpa, name, child); } - -pub fn isInsideOf(pkg: Package, another: Package) !bool { - var pkg_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; - var another_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; - const pkg_path = try pkg.root_src_directory.handle.realpath(pkg.root_src_path, &pkg_buffer); - const another_path = try another.root_src_directory.handle.realpath(".", &another_buffer); - return mem.startsWith(u8, pkg_path, another_path); -} diff --git a/src/main.zig b/src/main.zig index 9487f2e962..58c614c76e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3421,8 +3421,8 @@ pub fn cmdInit( const s = fs.path.sep_str; const template_sub_path = switch (output_mode) { .Obj => unreachable, - .Lib => "std" ++ s ++ "special" ++ s ++ "init-lib", - .Exe => "std" ++ s ++ "special" ++ s ++ "init-exe", + .Lib => "init-lib", + .Exe => "init-exe", }; var template_dir = zig_lib_directory.handle.openDir(template_sub_path, .{}) catch |err| { fatal("unable to open zig project template directory '{s}{s}{s}': {s}", .{ zig_lib_directory.path, s, template_sub_path, @errorName(err) }); @@ -3571,19 +3571,10 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi }; defer zig_lib_directory.handle.close(); - const std_special = "std" ++ fs.path.sep_str ++ "special"; - const special_dir_path = try zig_lib_directory.join(arena, &[_][]const u8{std_special}); - var main_pkg: Package = .{ - .root_src_directory = .{ - .path = special_dir_path, - .handle = zig_lib_directory.handle.openDir(std_special, .{}) catch |err| { - fatal("unable to open directory '{s}{s}{s}': {s}", .{ override_lib_dir, fs.path.sep_str, std_special, @errorName(err) }); - }, - }, + .root_src_directory = zig_lib_directory, .root_src_path = "build_runner.zig", }; - defer main_pkg.root_src_directory.handle.close(); var cleanup_build_dir: ?fs.Dir = null; defer if (cleanup_build_dir) |*dir| dir.close(); diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp index 398693e6d8..fbf0793d68 100644 --- a/src/stage1/all_types.hpp +++ b/src/stage1/all_types.hpp @@ -2144,7 +2144,6 @@ struct CodeGen { Buf docs_output_path; Buf *builtin_zig_path; - Buf *zig_std_special_dir; // Cannot be overridden; derived from zig_lib_dir. Stage1ZirInst *invalid_inst_src; Stage1AirInst *invalid_inst_gen; diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 9d46a660bc..03bd22b42d 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -10088,7 +10088,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { } static ZigPackage *create_test_runner_pkg(CodeGen *g) { - return codegen_create_package(g, buf_ptr(g->zig_std_special_dir), "test_runner.zig", "std.special"); + return codegen_create_package(g, buf_ptr(g->zig_lib_dir), "test_runner.zig", ""); } static Error define_builtin_compile_vars(CodeGen *g) { @@ -10141,12 +10141,17 @@ static Error define_builtin_compile_vars(CodeGen *g) { } else { g->root_pkg = g->main_pkg; } + + ZigPackage *compiler_rt_pkg = codegen_create_package(g, buf_ptr(g->zig_lib_dir), + "compiler_rt.zig", "compiler_rt"); + g->compile_var_package->package_table.put(buf_create_from_str("std"), g->std_package); g->main_pkg->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); g->main_pkg->package_table.put(buf_create_from_str("root"), g->root_pkg); g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); g->std_package->package_table.put(buf_create_from_str("std"), g->std_package); g->std_package->package_table.put(buf_create_from_str("root"), g->root_pkg); + g->std_package->package_table.put(buf_create_from_str("compiler_rt"), compiler_rt_pkg); g->compile_var_import = add_source_file(g, g->compile_var_package, g->builtin_zig_path, contents, SourceKindPkgMain); @@ -10454,11 +10459,11 @@ static void gen_root_source(CodeGen *g) { Buf *import_target_path; Buf full_path = BUF_INIT; ZigType *compiler_rt_import; - if ((err = analyze_import(g, std_import, buf_create_from_str("./special/compiler_rt.zig"), + if ((err = analyze_import(g, std_import, buf_create_from_str("compiler_rt"), &compiler_rt_import, &import_target_path, &full_path))) { if (err == ErrorFileNotFound) { - fprintf(stderr, "unable to find '%s'", buf_ptr(import_target_path)); + fprintf(stderr, "unable to find '%s'\n", buf_ptr(import_target_path)); } else { fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(&full_path), err_str(err)); } @@ -10538,11 +10543,11 @@ void codegen_build_object(CodeGen *g) { fprintf(stderr, "Unable to create directory %s: %s\n", buf_ptr(doc_dir_path), err_str(err)); exit(1); } - Buf *index_html_src_path = buf_sprintf("%s" OS_SEP "special" OS_SEP "docs" OS_SEP "index.html", - buf_ptr(g->zig_std_dir)); + Buf *index_html_src_path = buf_sprintf("%s" OS_SEP "docs" OS_SEP "index.html", + buf_ptr(g->zig_lib_dir)); Buf *index_html_dest_path = buf_sprintf("%s" OS_SEP "index.html", buf_ptr(doc_dir_path)); - Buf *main_js_src_path = buf_sprintf("%s" OS_SEP "special" OS_SEP "docs" OS_SEP "main.js", - buf_ptr(g->zig_std_dir)); + Buf *main_js_src_path = buf_sprintf("%s" OS_SEP "docs" OS_SEP "main.js", + buf_ptr(g->zig_lib_dir)); Buf *main_js_dest_path = buf_sprintf("%s" OS_SEP "main.js", buf_ptr(doc_dir_path)); if ((err = os_copy_file(index_html_src_path, index_html_dest_path))) { @@ -10702,9 +10707,6 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget g->main_pkg = new_package(".", "", ""); } - g->zig_std_special_dir = buf_alloc(); - os_path_join(g->zig_std_dir, buf_sprintf("special"), g->zig_std_special_dir); - target_triple_llvm(&g->llvm_triple_str, g->zig_target); g->pointer_size_bytes = target_arch_pointer_bit_width(g->zig_target->arch) / 8; From 43a627927f989034ae64846abea73bc8bdb545be Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 7 May 2022 10:31:08 +0200 Subject: [PATCH 1400/2031] x64: fix misused register locks --- src/arch/x86_64/CodeGen.zig | 45 ++++++++++++++++--------------------- src/register_manager.zig | 8 ++++++- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 8bb7111142..7df315d7e1 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -211,6 +211,16 @@ pub const MCValue = union(enum) { else => false, }; } + + fn asRegister(mcv: MCValue) ?Register { + return switch (mcv) { + .register, + .register_overflow_unsigned, + .register_overflow_signed, + => |reg| reg, + else => null, + }; + } }; const Branch = struct { @@ -842,20 +852,15 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; branch.inst_table.putAssumeCapacityNoClobber(inst, result); - switch (result) { - .register, - .register_overflow_signed, - .register_overflow_unsigned, - => |reg| { - // In some cases (such as bitcast), an operand - // may be the same MCValue as the result. If - // that operand died and was a register, it - // was freed by processDeath. We have to - // "re-allocate" the register. - if (self.register_manager.isRegFree(reg)) { - self.register_manager.getRegAssumeFree(reg, inst); - } - }, + if (result.asRegister()) |reg| { + // In some cases (such as bitcast), an operand + // may be the same MCValue as the result. If + // that operand died and was a register, it + // was freed by processDeath. We have to + // "re-allocate" the register. + if (self.register_manager.isRegFree(reg)) { + self.register_manager.getRegAssumeFree(reg, inst); + } } } self.finishAirBookkeeping(); @@ -4011,12 +4016,6 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { const ret_ty = self.fn_type.fnReturnType(); switch (self.ret_mcv) { .stack_offset => { - var reg_locks: [2]RegisterLock = undefined; - self.register_manager.freezeRegsAssumeUnused(2, .{ .rax, .rcx }, ®_locks); - defer for (reg_locks) |reg| { - self.register_manager.unfreezeReg(reg); - }; - const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv); const reg_lock = self.register_manager.freezeRegAssumeUnused(reg); defer self.register_manager.unfreezeReg(reg_lock); @@ -4051,12 +4050,6 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { const elem_ty = ptr_ty.elemType(); switch (self.ret_mcv) { .stack_offset => { - var reg_locks: [2]RegisterLock = undefined; - self.register_manager.freezeRegsAssumeUnused(2, .{ .rax, .rcx }, ®_locks); - defer for (reg_locks) |reg| { - self.register_manager.unfreezeReg(reg); - }; - const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv); const reg_lock = self.register_manager.freezeRegAssumeUnused(reg); defer self.register_manager.unfreezeReg(reg_lock); diff --git a/src/register_manager.zig b/src/register_manager.zig index 7e96d87af2..6e73181f2a 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -127,7 +127,11 @@ pub fn RegisterManager( /// Only the owner of the `RegisterLock` can unfreeze the /// register later. pub fn freezeReg(self: *Self, reg: Register) ?RegisterLock { - if (self.isRegFrozen(reg)) return null; + log.debug("freezing {}", .{reg}); + if (self.isRegFrozen(reg)) { + log.debug(" register already locked", .{}); + return null; + } const mask = getRegisterMask(reg) orelse return null; self.frozen_registers |= mask; return RegisterLock{ .register = reg }; @@ -136,6 +140,7 @@ pub fn RegisterManager( /// Like `freezeReg` but asserts the register was unused always /// returning a valid lock. pub fn freezeRegAssumeUnused(self: *Self, reg: Register) RegisterLock { + log.debug("freezing asserting free {}", .{reg}); assert(!self.isRegFrozen(reg)); const mask = getRegisterMask(reg) orelse unreachable; self.frozen_registers |= mask; @@ -158,6 +163,7 @@ pub fn RegisterManager( /// Requires `RegisterLock` to unfreeze a register. /// Call `freezeReg` to obtain the lock first. pub fn unfreezeReg(self: *Self, lock: RegisterLock) void { + log.debug("unfreezing {}", .{lock.register}); const mask = getRegisterMask(lock.register) orelse return; self.frozen_registers &= ~mask; } From 197c2a465f51b61bd3d2e58c5e6fecadacb20894 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 7 May 2022 10:46:05 +0200 Subject: [PATCH 1401/2031] regalloc: rename freeze/unfreeze to lock/unlock registers --- src/arch/aarch64/CodeGen.zig | 138 ++++++------- src/arch/arm/CodeGen.zig | 134 ++++++------- src/arch/riscv64/CodeGen.zig | 16 +- src/arch/x86_64/CodeGen.zig | 374 +++++++++++++++++------------------ src/register_manager.zig | 107 +++++----- 5 files changed, 384 insertions(+), 385 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index fca4327d2a..a56d9beabe 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -728,7 +728,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { // zig fmt: on } - assert(!self.register_manager.frozenRegsExist()); + assert(!self.register_manager.lockedRegsExist()); if (std.debug.runtime_safety) { if (self.air_bookkeeping < old_air_bookkeeping + 1) { @@ -1058,8 +1058,8 @@ fn trunc( } }, }; - const lock = self.register_manager.freezeReg(operand_reg); - defer if (lock) |reg| self.register_manager.unfreezeReg(reg); + const lock = self.register_manager.lockReg(operand_reg); + defer if (lock) |reg| self.register_manager.unlockReg(reg); const dest_reg = if (maybe_inst) |inst| blk: { const ty_op = self.air.instructions.items(.data)[inst].ty_op; @@ -1145,8 +1145,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { .register => |r| r, else => try self.copyToTmpRegister(operand_ty, operand), }; - const reg_lock = self.register_manager.freezeRegAssumeUnused(op_reg); - defer self.register_manager.unfreezeReg(reg_lock); + const reg_lock = self.register_manager.lockRegAssumeUnused(op_reg); + defer self.register_manager.unlockReg(reg_lock); const dest_reg = blk: { if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { @@ -1178,8 +1178,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { .register => |r| r, else => try self.copyToTmpRegister(operand_ty, operand), }; - const reg_lock = self.register_manager.freezeRegAssumeUnused(op_reg); - defer self.register_manager.unfreezeReg(reg_lock); + const reg_lock = self.register_manager.lockRegAssumeUnused(op_reg); + defer self.register_manager.unlockReg(reg_lock); const dest_reg = blk: { if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { @@ -1268,16 +1268,16 @@ fn binOpRegister( const rhs_is_register = rhs == .register; const lhs_lock: ?RegisterLock = if (lhs_is_register) - self.register_manager.freezeReg(lhs.register) + self.register_manager.lockReg(lhs.register) else null; - defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs_lock: ?RegisterLock = if (rhs_is_register) - self.register_manager.freezeReg(rhs.register) + self.register_manager.lockReg(rhs.register) else null; - defer if (rhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; @@ -1294,8 +1294,8 @@ fn binOpRegister( break :blk reg; }; - const new_lhs_lock = self.register_manager.freezeReg(lhs_reg); - defer if (new_lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + const new_lhs_lock = self.register_manager.lockReg(lhs_reg); + defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else blk: { const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { @@ -1310,8 +1310,8 @@ fn binOpRegister( break :blk reg; }; - const new_rhs_lock = self.register_manager.freezeReg(rhs_reg); - defer if (new_rhs_lock) |reg| self.register_manager.unfreezeReg(reg); + const new_rhs_lock = self.register_manager.lockReg(rhs_reg); + defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); const dest_reg = switch (mir_tag) { .cmp_shifted_register => undefined, // cmp has no destination register @@ -1414,10 +1414,10 @@ fn binOpImmediate( const lhs_is_register = lhs == .register; const lhs_lock: ?RegisterLock = if (lhs_is_register) - self.register_manager.freezeReg(lhs.register) + self.register_manager.lockReg(lhs.register) else null; - defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; @@ -1436,8 +1436,8 @@ fn binOpImmediate( break :blk reg; }; - const new_lhs_lock = self.register_manager.freezeReg(lhs_reg); - defer if (new_lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + const new_lhs_lock = self.register_manager.lockReg(lhs_reg); + defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const dest_reg = switch (mir_tag) { .cmp_immediate => undefined, // cmp has no destination register @@ -1841,13 +1841,13 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { }; const dest = try self.binOp(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); const dest_reg = dest.register; - const dest_reg_lock = self.register_manager.freezeRegAssumeUnused(dest_reg); - defer self.register_manager.unfreezeReg(dest_reg_lock); + const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); + defer self.register_manager.unlockReg(dest_reg_lock); const raw_truncated_reg = try self.register_manager.allocReg(null); const truncated_reg = registerAlias(raw_truncated_reg, lhs_ty.abiSize(self.target.*)); - const truncated_reg_lock = self.register_manager.freezeRegAssumeUnused(truncated_reg); - defer self.register_manager.unfreezeReg(truncated_reg_lock); + const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); + defer self.register_manager.unlockReg(truncated_reg_lock); // sbfx/ubfx truncated, dest, #0, #bits try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); @@ -1948,12 +1948,12 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const dest = try self.binOpRegister(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); const dest_reg = dest.register; - const dest_reg_lock = self.register_manager.freezeRegAssumeUnused(dest_reg); - defer self.register_manager.unfreezeReg(dest_reg_lock); + const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); + defer self.register_manager.unlockReg(dest_reg_lock); const truncated_reg = try self.register_manager.allocReg(null); - const truncated_reg_lock = self.register_manager.freezeRegAssumeUnused(truncated_reg); - defer self.register_manager.unfreezeReg(truncated_reg_lock); + const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); + defer self.register_manager.unlockReg(truncated_reg_lock); try self.truncRegister( dest_reg.to32(), @@ -2004,32 +2004,32 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const rhs_is_register = rhs == .register; const lhs_lock: ?RegisterLock = if (lhs_is_register) - self.register_manager.freezeRegAssumeUnused(lhs.register) + self.register_manager.lockRegAssumeUnused(lhs.register) else null; - defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs_lock: ?RegisterLock = if (rhs_is_register) - self.register_manager.freezeRegAssumeUnused(rhs.register) + self.register_manager.lockRegAssumeUnused(rhs.register) else null; - defer if (rhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); const lhs_reg = if (lhs_is_register) lhs.register else blk: { const raw_reg = try self.register_manager.allocReg(null); const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); break :blk reg; }; - const new_lhs_lock = self.register_manager.freezeReg(lhs_reg); - defer if (new_lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + const new_lhs_lock = self.register_manager.lockReg(lhs_reg); + defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else blk: { const raw_reg = try self.register_manager.allocReg(null); const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*)); break :blk reg; }; - const new_rhs_lock = self.register_manager.freezeReg(rhs_reg); - defer if (new_rhs_lock) |reg| self.register_manager.unfreezeReg(reg); + const new_rhs_lock = self.register_manager.lockReg(rhs_reg); + defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); @@ -2039,8 +2039,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); break :blk reg; }; - const dest_reg_lock = self.register_manager.freezeRegAssumeUnused(dest_reg); - defer self.register_manager.unfreezeReg(dest_reg_lock); + const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); + defer self.register_manager.unlockReg(dest_reg_lock); switch (int_info.signedness) { .signed => { @@ -2055,8 +2055,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { }); const dest_high_reg = try self.register_manager.allocReg(null); - const dest_high_reg_lock = self.register_manager.freezeRegAssumeUnused(dest_high_reg); - defer self.register_manager.unfreezeReg(dest_high_reg_lock); + const dest_high_reg_lock = self.register_manager.lockRegAssumeUnused(dest_high_reg); + defer self.register_manager.unlockReg(dest_high_reg_lock); // smulh dest_high, lhs, rhs _ = try self.addInst(.{ @@ -2105,8 +2105,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { }, .unsigned => { const dest_high_reg = try self.register_manager.allocReg(null); - const dest_high_reg_lock = self.register_manager.freezeRegAssumeUnused(dest_high_reg); - defer self.register_manager.unfreezeReg(dest_high_reg_lock); + const dest_high_reg_lock = self.register_manager.lockRegAssumeUnused(dest_high_reg); + defer self.register_manager.unlockReg(dest_high_reg_lock); // umulh dest_high, lhs, rhs _ = try self.addInst(.{ @@ -2161,8 +2161,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { } const truncated_reg = try self.register_manager.allocReg(null); - const truncated_reg_lock = self.register_manager.freezeRegAssumeUnused(truncated_reg); - defer self.register_manager.unfreezeReg(truncated_reg_lock); + const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); + defer self.register_manager.unlockReg(truncated_reg_lock); try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); @@ -2203,10 +2203,10 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); const lhs_lock: ?RegisterLock = if (lhs == .register) - self.register_manager.freezeRegAssumeUnused(lhs.register) + self.register_manager.lockRegAssumeUnused(lhs.register) else null; - defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = null; @@ -2214,8 +2214,8 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { // lsl dest, lhs, rhs const dest = try self.binOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty); const dest_reg = dest.register; - const dest_reg_lock = self.register_manager.freezeRegAssumeUnused(dest_reg); - defer self.register_manager.unfreezeReg(dest_reg_lock); + const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); + defer self.register_manager.unlockReg(dest_reg_lock); // asr/lsr reconstructed, dest, rhs const reconstructed = try self.binOp(.shr, null, dest, rhs, lhs_ty, rhs_ty); @@ -2454,17 +2454,17 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); const index_lock: ?RegisterLock = if (index_is_register) - self.register_manager.freezeRegAssumeUnused(index_mcv.register) + self.register_manager.lockRegAssumeUnused(index_mcv.register) else null; - defer if (index_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (index_lock) |reg| self.register_manager.unlockReg(reg); const base_mcv: MCValue = switch (slice_mcv) { .stack_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_offset = off }) }, else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}), }; - const base_lock = self.register_manager.freezeRegAssumeUnused(base_mcv.register); - defer self.register_manager.unfreezeReg(base_lock); + const base_lock = self.register_manager.lockRegAssumeUnused(base_mcv.register); + defer self.register_manager.unlockReg(base_lock); switch (elem_size) { else => { @@ -2605,8 +2605,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), .register => |addr_reg| { - const addr_reg_lock = self.register_manager.freezeReg(addr_reg); - defer if (addr_reg_lock) |reg| self.register_manager.unfreezeReg(reg); + const addr_reg_lock = self.register_manager.lockReg(addr_reg); + defer if (addr_reg_lock) |reg| self.register_manager.unlockReg(reg); switch (dst_mcv) { .dead => unreachable, @@ -2619,8 +2619,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo if (elem_size <= 8) { const raw_tmp_reg = try self.register_manager.allocReg(null); const tmp_reg = registerAlias(raw_tmp_reg, elem_size); - const tmp_reg_lock = self.register_manager.freezeRegAssumeUnused(tmp_reg); - defer self.register_manager.unfreezeReg(tmp_reg_lock); + const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); + defer self.register_manager.unlockReg(tmp_reg_lock); try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); @@ -2628,9 +2628,9 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo // TODO optimize the register allocation const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }); var regs_locks: [4]RegisterLock = undefined; - self.register_manager.freezeRegsAssumeUnused(4, regs, ®s_locks); + self.register_manager.lockRegsAssumeUnused(4, regs, ®s_locks); defer for (regs_locks) |reg| { - self.register_manager.unfreezeReg(reg); + self.register_manager.unlockReg(reg); }; const src_reg = addr_reg; @@ -2833,8 +2833,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type try self.genSetStack(value_ty, off, value); }, .register => |addr_reg| { - const addr_reg_lock = self.register_manager.freezeReg(addr_reg); - defer if (addr_reg_lock) |reg| self.register_manager.unfreezeReg(reg); + const addr_reg_lock = self.register_manager.lockReg(addr_reg); + defer if (addr_reg_lock) |reg| self.register_manager.unlockReg(reg); switch (value) { .register => |value_reg| { @@ -2844,8 +2844,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type if (abi_size <= 8) { const raw_tmp_reg = try self.register_manager.allocReg(null); const tmp_reg = registerAlias(raw_tmp_reg, abi_size); - const tmp_reg_lock = self.register_manager.freezeRegAssumeUnused(tmp_reg); - defer self.register_manager.unfreezeReg(tmp_reg_lock); + const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); + defer self.register_manager.unlockReg(tmp_reg_lock); try self.genSetReg(value_ty, tmp_reg, value); try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); @@ -2905,12 +2905,12 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = struct_field_offset, }); - const offset_reg_lock = self.register_manager.freezeRegAssumeUnused(offset_reg); - defer self.register_manager.unfreezeReg(offset_reg_lock); + const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); + defer self.register_manager.unlockReg(offset_reg_lock); const addr_reg = try self.copyToTmpRegister(ptr_ty, mcv); - const addr_reg_lock = self.register_manager.freezeRegAssumeUnused(addr_reg); - defer self.register_manager.unfreezeReg(addr_reg_lock); + const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); + defer self.register_manager.unlockReg(addr_reg_lock); const dest = try self.binOp( .add, @@ -4008,8 +4008,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro .register_c_flag, .register_v_flag, => |reg| { - const reg_lock = self.register_manager.freezeReg(reg); - defer if (reg_lock) |locked_reg| self.register_manager.unfreezeReg(locked_reg); + const reg_lock = self.register_manager.lockReg(reg); + defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg); const wrapped_ty = ty.structFieldType(0); try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg }); @@ -4066,9 +4066,9 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro // TODO call extern memcpy const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }); var regs_locks: [5]RegisterLock = undefined; - self.register_manager.freezeRegsAssumeUnused(5, regs, ®s_locks); + self.register_manager.lockRegsAssumeUnused(5, regs, ®s_locks); defer for (regs_locks) |reg| { - self.register_manager.unfreezeReg(reg); + self.register_manager.unlockReg(reg); }; const src_reg = regs[0]; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index eb2654bf2e..cad7cedbb4 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -735,7 +735,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { // zig fmt: on } - assert(!self.register_manager.frozenRegsExist()); + assert(!self.register_manager.lockedRegsExist()); if (std.debug.runtime_safety) { if (self.air_bookkeeping < old_air_bookkeeping + 1) { @@ -1039,8 +1039,8 @@ fn trunc( } }, }; - const operand_reg_lock = self.register_manager.freezeReg(operand_reg); - defer if (operand_reg_lock) |reg| self.register_manager.unfreezeReg(reg); + const operand_reg_lock = self.register_manager.lockReg(operand_reg); + defer if (operand_reg_lock) |reg| self.register_manager.unlockReg(reg); const dest_reg = if (maybe_inst) |inst| blk: { const ty_op = self.air.instructions.items(.data)[inst].ty_op; @@ -1128,8 +1128,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { .register => |r| r, else => try self.copyToTmpRegister(operand_ty, operand), }; - const op_reg_lock = self.register_manager.freezeRegAssumeUnused(op_reg); - defer self.register_manager.unfreezeReg(op_reg_lock); + const op_reg_lock = self.register_manager.lockRegAssumeUnused(op_reg); + defer self.register_manager.unlockReg(op_reg_lock); const dest_reg = blk: { if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { @@ -1158,8 +1158,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { .register => |r| r, else => try self.copyToTmpRegister(operand_ty, operand), }; - const op_reg_lock = self.register_manager.freezeRegAssumeUnused(op_reg); - defer self.register_manager.unfreezeReg(op_reg_lock); + const op_reg_lock = self.register_manager.lockRegAssumeUnused(op_reg); + defer self.register_manager.unlockReg(op_reg_lock); const dest_reg = blk: { if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { @@ -1219,15 +1219,15 @@ fn minMax( .register => |r| r, else => try self.copyToTmpRegister(lhs_ty, lhs), }; - const lhs_reg_lock = self.register_manager.freezeReg(lhs_reg); - defer if (lhs_reg_lock) |reg| self.register_manager.unfreezeReg(reg); + const lhs_reg_lock = self.register_manager.lockReg(lhs_reg); + defer if (lhs_reg_lock) |reg| self.register_manager.unlockReg(reg); const rhs_reg = switch (rhs) { .register => |r| r, else => try self.copyToTmpRegister(rhs_ty, rhs), }; - const rhs_reg_lock = self.register_manager.freezeReg(rhs_reg); - defer if (rhs_reg_lock) |reg| self.register_manager.unfreezeReg(reg); + const rhs_reg_lock = self.register_manager.lockReg(rhs_reg); + defer if (rhs_reg_lock) |reg| self.register_manager.unlockReg(reg); const dest_reg = if (maybe_inst) |inst| blk: { const bin_op = self.air.instructions.items(.data)[inst].bin_op; @@ -1393,12 +1393,12 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { }; const dest = try self.binOp(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); const dest_reg = dest.register; - const dest_reg_lock = self.register_manager.freezeRegAssumeUnused(dest_reg); - defer self.register_manager.unfreezeReg(dest_reg_lock); + const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); + defer self.register_manager.unlockReg(dest_reg_lock); const truncated_reg = try self.register_manager.allocReg(null); - const truncated_reg_lock = self.register_manager.freezeRegAssumeUnused(truncated_reg); - defer self.register_manager.unfreezeReg(truncated_reg_lock); + const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); + defer self.register_manager.unlockReg(truncated_reg_lock); // sbfx/ubfx truncated, dest, #0, #bits try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); @@ -1494,12 +1494,12 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const dest = try self.binOpRegister(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); const dest_reg = dest.register; - const dest_reg_lock = self.register_manager.freezeRegAssumeUnused(dest_reg); - defer self.register_manager.unfreezeReg(dest_reg_lock); + const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); + defer self.register_manager.unlockReg(dest_reg_lock); const truncated_reg = try self.register_manager.allocReg(null); - const truncated_reg_lock = self.register_manager.freezeRegAssumeUnused(truncated_reg); - defer self.register_manager.unfreezeReg(truncated_reg_lock); + const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); + defer self.register_manager.unlockReg(truncated_reg_lock); // sbfx/ubfx truncated, dest, #0, #bits try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); @@ -1528,30 +1528,30 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const rhs_is_register = rhs == .register; const lhs_lock: ?RegisterLock = if (lhs_is_register) - self.register_manager.freezeReg(lhs.register) + self.register_manager.lockReg(lhs.register) else null; - defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); const lhs_reg = if (lhs_is_register) lhs.register else try self.register_manager.allocReg(null); - const new_lhs_lock = self.register_manager.freezeReg(lhs_reg); - defer if (new_lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + const new_lhs_lock = self.register_manager.lockReg(lhs_reg); + defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else try self.register_manager.allocReg(null); - const new_rhs_lock = self.register_manager.freezeReg(rhs_reg); - defer if (new_rhs_lock) |reg| self.register_manager.unfreezeReg(reg); + const new_rhs_lock = self.register_manager.lockReg(rhs_reg); + defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); const dest_regs = try self.register_manager.allocRegs(2, .{ null, null }); var dest_regs_locks: [2]RegisterLock = undefined; - self.register_manager.freezeRegsAssumeUnused(2, dest_regs, &dest_regs_locks); + self.register_manager.lockRegsAssumeUnused(2, dest_regs, &dest_regs_locks); defer for (dest_regs_locks) |reg| { - self.register_manager.unfreezeReg(reg); + self.register_manager.unlockReg(reg); }; const rdlo = dest_regs[0]; const rdhi = dest_regs[1]; @@ -1560,8 +1560,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); const truncated_reg = try self.register_manager.allocReg(null); - const truncated_reg_lock = self.register_manager.freezeRegAssumeUnused(truncated_reg); - defer self.register_manager.unfreezeReg(truncated_reg_lock); + const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); + defer self.register_manager.unlockReg(truncated_reg_lock); _ = try self.addInst(.{ .tag = base_tag, @@ -1654,10 +1654,10 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); const lhs_lock: ?RegisterLock = if (lhs == .register) - self.register_manager.freezeRegAssumeUnused(lhs.register) + self.register_manager.lockRegAssumeUnused(lhs.register) else null; - defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = null; @@ -1948,10 +1948,10 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); const index_lock: ?RegisterLock = if (index_is_register) - self.register_manager.freezeRegAssumeUnused(index_mcv.register) + self.register_manager.lockRegAssumeUnused(index_mcv.register) else null; - defer if (index_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (index_lock) |reg| self.register_manager.unlockReg(reg); const base_mcv = slicePtr(slice_mcv); @@ -1961,20 +1961,20 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { .register => |r| r, else => try self.copyToTmpRegister(slice_ptr_field_type, base_mcv), }; - const base_reg_lock = self.register_manager.freezeRegAssumeUnused(base_reg); - defer self.register_manager.unfreezeReg(base_reg_lock); + const base_reg_lock = self.register_manager.lockRegAssumeUnused(base_reg); + defer self.register_manager.unlockReg(base_reg_lock); const dst_reg = try self.register_manager.allocReg(inst); const dst_mcv = MCValue{ .register = dst_reg }; - const dst_reg_lock = self.register_manager.freezeRegAssumeUnused(dst_reg); - defer self.register_manager.unfreezeReg(dst_reg_lock); + const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); + defer self.register_manager.unlockReg(dst_reg_lock); const index_reg: Register = switch (index_mcv) { .register => |reg| reg, else => try self.copyToTmpRegister(Type.usize, index_mcv), }; - const index_reg_lock = self.register_manager.freezeRegAssumeUnused(index_reg); - defer self.register_manager.unfreezeReg(index_reg_lock); + const index_reg_lock = self.register_manager.lockRegAssumeUnused(index_reg); + defer self.register_manager.unlockReg(index_reg_lock); const tag: Mir.Inst.Tag = switch (elem_size) { 1 => .ldrb, @@ -2160,8 +2160,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), .register => |reg| { - const reg_lock = self.register_manager.freezeReg(reg); - defer if (reg_lock) |reg_locked| self.register_manager.unfreezeReg(reg_locked); + const reg_lock = self.register_manager.lockReg(reg); + defer if (reg_lock) |reg_locked| self.register_manager.unlockReg(reg_locked); switch (dst_mcv) { .dead => unreachable, @@ -2173,8 +2173,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .stack_offset => |off| { if (elem_size <= 4) { const tmp_reg = try self.register_manager.allocReg(null); - const tmp_reg_lock = self.register_manager.freezeRegAssumeUnused(tmp_reg); - defer self.register_manager.unfreezeReg(tmp_reg_lock); + const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); + defer self.register_manager.unlockReg(tmp_reg_lock); try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); @@ -2182,9 +2182,9 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo // TODO optimize the register allocation const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }); var regs_locks: [4]RegisterLock = undefined; - self.register_manager.freezeRegsAssumeUnused(4, regs, ®s_locks); + self.register_manager.lockRegsAssumeUnused(4, regs, ®s_locks); defer for (regs_locks) |reg_locked| { - self.register_manager.unfreezeReg(reg_locked); + self.register_manager.unlockReg(reg_locked); }; const src_reg = reg; @@ -2211,8 +2211,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .stack_argument_offset, => { const reg = try self.register_manager.allocReg(null); - const reg_lock = self.register_manager.freezeRegAssumeUnused(reg); - defer self.register_manager.unfreezeReg(reg_lock); + const reg_lock = self.register_manager.lockRegAssumeUnused(reg); + defer self.register_manager.unlockReg(reg_lock); try self.genSetReg(ptr_ty, reg, ptr); try self.load(dst_mcv, .{ .register = reg }, ptr_ty); @@ -2266,8 +2266,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type try self.genSetStack(value_ty, off, value); }, .register => |addr_reg| { - const addr_reg_lock = self.register_manager.freezeReg(addr_reg); - defer if (addr_reg_lock) |reg| self.register_manager.unfreezeReg(reg); + const addr_reg_lock = self.register_manager.lockReg(addr_reg); + defer if (addr_reg_lock) |reg| self.register_manager.unlockReg(reg); switch (value) { .dead => unreachable, @@ -2278,17 +2278,17 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type else => { if (elem_size <= 4) { const tmp_reg = try self.register_manager.allocReg(null); - const tmp_reg_lock = self.register_manager.freezeRegAssumeUnused(tmp_reg); - defer self.register_manager.unfreezeReg(tmp_reg_lock); + const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); + defer self.register_manager.unlockReg(tmp_reg_lock); try self.genSetReg(value_ty, tmp_reg, value); try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); } else { const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }); var regs_locks: [4]RegisterLock = undefined; - self.register_manager.freezeRegsAssumeUnused(4, regs, ®s_locks); + self.register_manager.lockRegsAssumeUnused(4, regs, ®s_locks); defer for (regs_locks) |reg| { - self.register_manager.unfreezeReg(reg); + self.register_manager.unlockReg(reg); }; const src_reg = regs[0]; @@ -2373,12 +2373,12 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = struct_field_offset, }); - const offset_reg_lock = self.register_manager.freezeRegAssumeUnused(offset_reg); - defer self.register_manager.unfreezeReg(offset_reg_lock); + const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); + defer self.register_manager.unlockReg(offset_reg_lock); const addr_reg = try self.copyToTmpRegister(ptr_ty, mcv); - const addr_reg_lock = self.register_manager.freezeRegAssumeUnused(addr_reg); - defer self.register_manager.unfreezeReg(addr_reg_lock); + const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); + defer self.register_manager.unlockReg(addr_reg_lock); const dest = try self.binOp( .add, @@ -2495,10 +2495,10 @@ fn binOpRegister( const rhs_is_register = rhs == .register; const lhs_lock: ?RegisterLock = if (lhs_is_register) - self.register_manager.freezeReg(lhs.register) + self.register_manager.lockReg(lhs.register) else null; - defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; @@ -2514,8 +2514,8 @@ fn binOpRegister( break :blk reg; }; - const new_lhs_lock = self.register_manager.freezeReg(lhs_reg); - defer if (new_lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + const new_lhs_lock = self.register_manager.lockReg(lhs_reg); + defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else blk: { const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { @@ -2529,8 +2529,8 @@ fn binOpRegister( break :blk reg; }; - const new_rhs_lock = self.register_manager.freezeReg(rhs_reg); - defer if (new_rhs_lock) |reg| self.register_manager.unfreezeReg(reg); + const new_rhs_lock = self.register_manager.lockReg(rhs_reg); + defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); const dest_reg = switch (mir_tag) { .cmp => .r0, // cmp has no destination regardless @@ -2614,10 +2614,10 @@ fn binOpImmediate( const lhs_is_register = lhs == .register; const lhs_lock: ?RegisterLock = if (lhs_is_register) - self.register_manager.freezeReg(lhs.register) + self.register_manager.lockReg(lhs.register) else null; - defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; @@ -2635,8 +2635,8 @@ fn binOpImmediate( break :blk reg; }; - const new_lhs_lock = self.register_manager.freezeReg(lhs_reg); - defer if (new_lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + const new_lhs_lock = self.register_manager.lockReg(lhs_reg); + defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const dest_reg = switch (mir_tag) { .cmp => .r0, // cmp has no destination reg diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index da036379e5..07ce2a9f89 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -939,10 +939,10 @@ fn binOpRegister( const rhs_is_register = rhs == .register; const lhs_lock: ?RegisterLock = if (lhs_is_register) - self.register_manager.freezeReg(lhs.register) + self.register_manager.lockReg(lhs.register) else null; - defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; @@ -958,8 +958,8 @@ fn binOpRegister( break :blk reg; }; - const new_lhs_lock = self.register_manager.freezeReg(lhs_reg); - defer if (new_lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + const new_lhs_lock = self.register_manager.lockReg(lhs_reg); + defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else blk: { const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { @@ -973,8 +973,8 @@ fn binOpRegister( break :blk reg; }; - const new_rhs_lock = self.register_manager.freezeReg(rhs_reg); - defer if (new_rhs_lock) |reg| self.register_manager.unfreezeReg(reg); + const new_rhs_lock = self.register_manager.lockReg(rhs_reg); + defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); const dest_reg = if (maybe_inst) |inst| blk: { const bin_op = self.air.instructions.items(.data)[inst].bin_op; @@ -1452,8 +1452,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .stack_offset, => { const reg = try self.register_manager.allocReg(null); - const reg_lock = self.register_manager.freezeRegAssumeUnused(reg); - defer self.register_manager.unfreezeReg(reg_lock); + const reg_lock = self.register_manager.lockRegAssumeUnused(reg); + defer self.register_manager.unlockReg(reg_lock); try self.genSetReg(ptr_ty, reg, ptr); try self.load(dst_mcv, .{ .register = reg }, ptr_ty); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 7df315d7e1..8d140c4da9 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -795,7 +795,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { // zig fmt: on } - assert(!self.register_manager.frozenRegsExist()); + assert(!self.register_manager.lockedRegsExist()); if (std.debug.runtime_safety) { if (self.air_bookkeeping < old_air_bookkeeping + 1) { @@ -1028,10 +1028,10 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { } const operand_lock: ?RegisterLock = switch (operand) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (operand_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (operand_lock) |reg| self.register_manager.unlockReg(reg); const reg = try self.register_manager.allocReg(inst); try self.genSetReg(dest_ty, reg, .{ .immediate = 0 }); @@ -1059,10 +1059,10 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { } const operand_lock: ?RegisterLock = switch (operand) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (operand_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (operand_lock) |reg| self.register_manager.unlockReg(reg); const reg: Register = blk: { if (operand.isRegister()) { @@ -1147,21 +1147,21 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void { // TODO audit register allocation const lhs = try self.resolveInst(bin_op.lhs); const lhs_lock: ?RegisterLock = switch (lhs) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); const lhs_reg = try self.copyToTmpRegister(ty, lhs); - const lhs_reg_lock = self.register_manager.freezeRegAssumeUnused(lhs_reg); - defer self.register_manager.unfreezeReg(lhs_reg_lock); + const lhs_reg_lock = self.register_manager.lockRegAssumeUnused(lhs_reg); + defer self.register_manager.unlockReg(lhs_reg_lock); const rhs_mcv = try self.limitImmediateType(bin_op.rhs, i32); const rhs_lock: ?RegisterLock = switch (rhs_mcv) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (rhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); try self.genBinMathOpMir(.cmp, ty, .{ .register = lhs_reg }, rhs_mcv); @@ -1197,10 +1197,10 @@ fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_r const offset_ty = self.air.typeOf(op_rhs); const offset_lock: ?RegisterLock = switch (offset) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (offset_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (offset_lock) |reg| self.register_manager.unlockReg(reg); const dst_mcv = blk: { if (self.reuseOperand(inst, op_lhs, 0, ptr)) { @@ -1210,10 +1210,10 @@ fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_r }; const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (dst_mcv_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (dst_mcv_lock) |reg| self.register_manager.unlockReg(reg); const offset_mcv = blk: { if (self.reuseOperand(inst, op_rhs, 1, offset)) { @@ -1223,10 +1223,10 @@ fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_r }; const offset_mcv_lock: ?RegisterLock = switch (offset_mcv) { - .register => |reg| self.register_manager.freezeReg(reg), + .register => |reg| self.register_manager.lockReg(reg), else => null, }; - defer if (offset_mcv_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (offset_mcv_lock) |reg| self.register_manager.unlockReg(reg); try self.genIntMulComplexOpMir(offset_ty, offset_mcv, .{ .immediate = elem_size }); @@ -1312,17 +1312,17 @@ fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air const lhs = try self.resolveInst(op_lhs); const lhs_lock: ?RegisterLock = switch (lhs) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs = try self.resolveInst(op_rhs); const rhs_lock: ?RegisterLock = switch (rhs) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (rhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); const dst_mcv = blk: { if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) { @@ -1331,20 +1331,20 @@ fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); }; const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { - .register => |reg| self.register_manager.freezeReg(reg), + .register => |reg| self.register_manager.lockReg(reg), else => null, }; - defer if (dst_mcv_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (dst_mcv_lock) |reg| self.register_manager.unlockReg(reg); const rhs_mcv = blk: { if (rhs.isMemory() or rhs.isRegister()) break :blk rhs; break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, rhs) }; }; const rhs_mcv_lock: ?RegisterLock = switch (rhs_mcv) { - .register => |reg| self.register_manager.freezeReg(reg), + .register => |reg| self.register_manager.lockReg(reg), else => null, }; - defer if (rhs_mcv_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (rhs_mcv_lock) |reg| self.register_manager.unlockReg(reg); try self.genBinMathOpMir(.sub, dst_ty, dst_mcv, rhs_mcv); @@ -1382,9 +1382,9 @@ fn airMul(self: *Self, inst: Air.Inst.Index) !void { try self.register_manager.getReg(.rax, inst); try self.register_manager.getReg(.rdx, null); var reg_locks: [2]RegisterLock = undefined; - self.register_manager.freezeRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); defer for (reg_locks) |reg| { - self.register_manager.unfreezeReg(reg); + self.register_manager.unlockReg(reg); }; const lhs = try self.resolveInst(bin_op.lhs); @@ -1496,9 +1496,9 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.register_manager.getReg(.rax, inst); try self.register_manager.getReg(.rdx, null); var reg_locks: [2]RegisterLock = undefined; - self.register_manager.freezeRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); defer for (reg_locks) |reg| { - self.register_manager.unfreezeReg(reg); + self.register_manager.unlockReg(reg); }; const lhs = try self.resolveInst(bin_op.lhs); @@ -1526,27 +1526,27 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const rhs = try self.resolveInst(bin_op.rhs); const rhs_lock: ?RegisterLock = switch (rhs) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (rhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); const dst_reg: Register = blk: { if (lhs.isRegister()) break :blk lhs.register; break :blk try self.copyToTmpRegister(ty, lhs); }; - const dst_reg_lock = self.register_manager.freezeRegAssumeUnused(dst_reg); - defer self.register_manager.unfreezeReg(dst_reg_lock); + const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); + defer self.register_manager.unlockReg(dst_reg_lock); const rhs_mcv = blk: { if (rhs.isRegister() or rhs.isMemory()) break :blk rhs; break :blk MCValue{ .register = try self.copyToTmpRegister(ty, rhs) }; }; const rhs_mcv_lock: ?RegisterLock = switch (rhs_mcv) { - .register => |reg| self.register_manager.freezeReg(reg), + .register => |reg| self.register_manager.lockReg(reg), else => null, }; - defer if (rhs_mcv_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (rhs_mcv_lock) |reg| self.register_manager.unlockReg(reg); try self.genIntMulComplexOpMir(Type.isize, .{ .register = dst_reg }, rhs_mcv); @@ -1557,9 +1557,9 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.register_manager.getReg(.rax, null); try self.register_manager.getReg(.rdx, null); var reg_locks: [2]RegisterLock = undefined; - self.register_manager.freezeRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); defer for (reg_locks) |reg| { - self.register_manager.unfreezeReg(reg); + self.register_manager.unlockReg(reg); }; const lhs = try self.resolveInst(bin_op.lhs); @@ -1571,8 +1571,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { }, } }; - const dst_reg_lock = self.register_manager.freezeRegAssumeUnused(dst_reg); - defer self.register_manager.unfreezeReg(dst_reg_lock); + const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); + defer self.register_manager.unlockReg(dst_reg_lock); const tuple_ty = self.air.typeOfIndex(inst); const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); @@ -1587,9 +1587,9 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }); var temp_regs_locks: [3]RegisterLock = undefined; - self.register_manager.freezeRegsAssumeUnused(3, temp_regs, &temp_regs_locks); + self.register_manager.lockRegsAssumeUnused(3, temp_regs, &temp_regs_locks); defer for (temp_regs_locks) |reg| { - self.register_manager.unfreezeReg(reg); + self.register_manager.unlockReg(reg); }; const overflow_reg = temp_regs[0]; @@ -1738,15 +1738,15 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa .register => |reg| reg, else => try self.copyToTmpRegister(ty, lhs), }; - const dividend_lock = self.register_manager.freezeReg(dividend); - defer if (dividend_lock) |reg| self.register_manager.unfreezeReg(reg); + const dividend_lock = self.register_manager.lockReg(dividend); + defer if (dividend_lock) |reg| self.register_manager.unlockReg(reg); const divisor = switch (rhs) { .register => |reg| reg, else => try self.copyToTmpRegister(ty, rhs), }; - const divisor_lock = self.register_manager.freezeReg(divisor); - defer if (divisor_lock) |reg| self.register_manager.unfreezeReg(reg); + const divisor_lock = self.register_manager.lockReg(divisor); + defer if (divisor_lock) |reg| self.register_manager.unlockReg(reg); try self.genIntMulDivOpMir(switch (signedness) { .signed => .idiv, @@ -1816,17 +1816,17 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { try self.register_manager.getReg(.rax, track_rax); try self.register_manager.getReg(.rdx, null); var reg_locks: [2]RegisterLock = undefined; - self.register_manager.freezeRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); defer for (reg_locks) |reg| { - self.register_manager.unfreezeReg(reg); + self.register_manager.unlockReg(reg); }; const lhs = try self.resolveInst(bin_op.lhs); const lhs_lock: ?RegisterLock = switch (lhs) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs = blk: { const rhs = try self.resolveInst(bin_op.rhs); @@ -1834,10 +1834,10 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { switch (tag) { .div_floor => { const rhs_lock: ?RegisterLock = switch (rhs) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (rhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); break :blk try self.copyToRegisterWithInstTracking(inst, ty, rhs); }, @@ -1847,10 +1847,10 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { break :blk rhs; }; const rhs_lock: ?RegisterLock = switch (rhs) { - .register => |reg| self.register_manager.freezeReg(reg), + .register => |reg| self.register_manager.lockReg(reg), else => null, }; - defer if (rhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); if (signedness == .unsigned) { try self.genIntMulDivOpMir(.div, ty, signedness, lhs, rhs); @@ -1885,9 +1885,9 @@ fn airRem(self: *Self, inst: Air.Inst.Index) !void { try self.register_manager.getReg(.rax, null); try self.register_manager.getReg(.rdx, inst); var reg_locks: [2]RegisterLock = undefined; - self.register_manager.freezeRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); defer for (reg_locks) |reg| { - self.register_manager.unfreezeReg(reg); + self.register_manager.unlockReg(reg); }; const lhs = try self.resolveInst(bin_op.lhs); @@ -1916,9 +1916,9 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void { try self.register_manager.getReg(.rax, null); try self.register_manager.getReg(.rdx, if (signedness == .unsigned) inst else null); var reg_locks: [2]RegisterLock = undefined; - self.register_manager.freezeRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); defer for (reg_locks) |reg| { - self.register_manager.unfreezeReg(reg); + self.register_manager.unlockReg(reg); }; const lhs = try self.resolveInst(bin_op.lhs); @@ -2009,15 +2009,15 @@ fn airShl(self: *Self, inst: Air.Inst.Index) !void { try self.register_manager.getReg(.rcx, null); try self.genSetReg(shift_ty, .rcx, shift); } - const rcx_lock = self.register_manager.freezeRegAssumeUnused(.rcx); - defer self.register_manager.unfreezeReg(rcx_lock); + const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx); + defer self.register_manager.unlockReg(rcx_lock); const value = try self.resolveInst(bin_op.lhs); const value_lock: ?RegisterLock = switch (value) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (value_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (value_lock) |reg| self.register_manager.unlockReg(reg); const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, value); _ = try self.addInst(.{ @@ -2114,10 +2114,10 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { const payload_ty = err_union_ty.errorUnionPayload(); const operand = try self.resolveInst(ty_op.operand); const operand_lock: ?RegisterLock = switch (operand) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (operand_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (operand_lock) |reg| self.register_manager.unlockReg(reg); const result: MCValue = result: { if (!payload_ty.hasRuntimeBits()) break :result operand; @@ -2147,10 +2147,10 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { const operand = try self.resolveInst(ty_op.operand); const operand_lock: ?RegisterLock = switch (operand) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (operand_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (operand_lock) |reg| self.register_manager.unlockReg(reg); const abi_align = err_union_ty.abiAlignment(self.target.*); const err_ty = err_union_ty.errorUnionSet(); @@ -2219,10 +2219,10 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { const optional_ty = self.air.typeOfIndex(inst); const operand = try self.resolveInst(ty_op.operand); const operand_lock: ?RegisterLock = switch (operand) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (operand_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (operand_lock) |reg| self.register_manager.unlockReg(reg); if (optional_ty.isPtrLikeOptional()) { // TODO should we check if we can reuse the operand? @@ -2356,10 +2356,10 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { const slice_ty = self.air.typeOf(lhs); const slice_mcv = try self.resolveInst(lhs); const slice_mcv_lock: ?RegisterLock = switch (slice_mcv) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (slice_mcv_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (slice_mcv_lock) |reg| self.register_manager.unlockReg(reg); const elem_ty = slice_ty.childType(); const elem_size = elem_ty.abiSize(self.target.*); @@ -2369,14 +2369,14 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { const index_ty = self.air.typeOf(rhs); const index_mcv = try self.resolveInst(rhs); const index_mcv_lock: ?RegisterLock = switch (index_mcv) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (index_mcv_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (index_mcv_lock) |reg| self.register_manager.unlockReg(reg); const offset_reg = try self.elemOffset(index_ty, index_mcv, elem_size); - const offset_reg_lock = self.register_manager.freezeRegAssumeUnused(offset_reg); - defer self.register_manager.unfreezeReg(offset_reg_lock); + const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); + defer self.register_manager.unlockReg(offset_reg_lock); const addr_reg = try self.register_manager.allocReg(null); switch (slice_mcv) { @@ -2433,10 +2433,10 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { const array_ty = self.air.typeOf(bin_op.lhs); const array = try self.resolveInst(bin_op.lhs); const array_lock: ?RegisterLock = switch (array) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (array_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (array_lock) |reg| self.register_manager.unlockReg(reg); const elem_ty = array_ty.childType(); const elem_abi_size = elem_ty.abiSize(self.target.*); @@ -2444,14 +2444,14 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { const index_ty = self.air.typeOf(bin_op.rhs); const index = try self.resolveInst(bin_op.rhs); const index_lock: ?RegisterLock = switch (index) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (index_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (index_lock) |reg| self.register_manager.unlockReg(reg); const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); - const offset_reg_lock = self.register_manager.freezeRegAssumeUnused(offset_reg); - defer self.register_manager.unfreezeReg(offset_reg_lock); + const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); + defer self.register_manager.unlockReg(offset_reg_lock); const addr_reg = try self.register_manager.allocReg(null); switch (array) { @@ -2512,24 +2512,24 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { const ptr_ty = self.air.typeOf(bin_op.lhs); const ptr = try self.resolveInst(bin_op.lhs); const ptr_lock: ?RegisterLock = switch (ptr) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (ptr_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (ptr_lock) |reg| self.register_manager.unlockReg(reg); const elem_ty = ptr_ty.elemType2(); const elem_abi_size = elem_ty.abiSize(self.target.*); const index_ty = self.air.typeOf(bin_op.rhs); const index = try self.resolveInst(bin_op.rhs); const index_lock: ?RegisterLock = switch (index) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (index_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (index_lock) |reg| self.register_manager.unlockReg(reg); const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); - const offset_reg_lock = self.register_manager.freezeRegAssumeUnused(offset_reg); - defer self.register_manager.unfreezeReg(offset_reg_lock); + const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); + defer self.register_manager.unlockReg(offset_reg_lock); const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); @@ -2559,24 +2559,24 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { const ptr_ty = self.air.typeOf(extra.lhs); const ptr = try self.resolveInst(extra.lhs); const ptr_lock: ?RegisterLock = switch (ptr) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (ptr_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (ptr_lock) |reg| self.register_manager.unlockReg(reg); const elem_ty = ptr_ty.elemType2(); const elem_abi_size = elem_ty.abiSize(self.target.*); const index_ty = self.air.typeOf(extra.rhs); const index = try self.resolveInst(extra.rhs); const index_lock: ?RegisterLock = switch (index) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (index_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (index_lock) |reg| self.register_manager.unlockReg(reg); const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); - const offset_reg_lock = self.register_manager.freezeRegAssumeUnused(offset_reg); - defer self.register_manager.unfreezeReg(offset_reg_lock); + const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); + defer self.register_manager.unlockReg(offset_reg_lock); const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); @@ -2598,17 +2598,17 @@ fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { const ptr = try self.resolveInst(bin_op.lhs); const ptr_lock: ?RegisterLock = switch (ptr) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (ptr_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (ptr_lock) |reg| self.register_manager.unlockReg(reg); const tag = try self.resolveInst(bin_op.rhs); const tag_lock: ?RegisterLock = switch (tag) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (tag_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (tag_lock) |reg| self.register_manager.unlockReg(reg); const adjusted_ptr: MCValue = if (layout.payload_size > 0 and layout.tag_align < layout.payload_align) blk: { // TODO reusing the operand @@ -2639,10 +2639,10 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { // TODO reusing the operand const operand = try self.resolveInst(ty_op.operand); const operand_lock: ?RegisterLock = switch (operand) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (operand_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (operand_lock) |reg| self.register_manager.unlockReg(reg); const tag_abi_size = tag_ty.abiSize(self.target.*); const dst_mcv: MCValue = blk: { @@ -2789,8 +2789,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }); }, .register => |reg| { - const reg_lock = self.register_manager.freezeReg(reg); - defer if (reg_lock) |locked_reg| self.register_manager.unfreezeReg(locked_reg); + const reg_lock = self.register_manager.lockReg(reg); + defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg); switch (dst_mcv) { .dead => unreachable, @@ -2915,8 +2915,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type try self.genSetStack(value_ty, off, value, .{}); }, .register => |reg| { - const reg_lock = self.register_manager.freezeReg(reg); - defer if (reg_lock) |locked_reg| self.register_manager.unfreezeReg(locked_reg); + const reg_lock = self.register_manager.lockReg(reg); + defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg); switch (value) { .none => unreachable, @@ -3007,14 +3007,14 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .memory, => { const value_lock: ?RegisterLock = switch (value) { - .register => |reg| self.register_manager.freezeReg(reg), + .register => |reg| self.register_manager.lockReg(reg), else => null, }; - defer if (value_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (value_lock) |reg| self.register_manager.unlockReg(reg); const addr_reg = try self.register_manager.allocReg(null); - const addr_reg_lock = self.register_manager.freezeRegAssumeUnused(addr_reg); - defer self.register_manager.unfreezeReg(addr_reg_lock); + const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); + defer self.register_manager.unlockReg(addr_reg_lock); try self.loadMemPtrIntoRegister(addr_reg, ptr_ty, ptr); @@ -3085,8 +3085,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type => { if (abi_size <= 8) { const tmp_reg = try self.register_manager.allocReg(null); - const tmp_reg_lock = self.register_manager.freezeRegAssumeUnused(tmp_reg); - defer self.register_manager.unfreezeReg(tmp_reg_lock); + const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); + defer self.register_manager.unlockReg(tmp_reg_lock); try self.loadMemPtrIntoRegister(tmp_reg, value_ty, value); @@ -3176,8 +3176,8 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = struct_field_offset, }); - const offset_reg_lock = self.register_manager.freezeRegAssumeUnused(offset_reg); - defer self.register_manager.unfreezeReg(offset_reg_lock); + const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); + defer self.register_manager.unlockReg(offset_reg_lock); const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, mcv); try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); @@ -3188,14 +3188,14 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde break :result MCValue{ .ptr_stack_offset = ptr_stack_offset }; }, .register => |reg| { - const reg_lock = self.register_manager.freezeRegAssumeUnused(reg); - defer self.register_manager.unfreezeReg(reg_lock); + const reg_lock = self.register_manager.lockRegAssumeUnused(reg); + defer self.register_manager.unlockReg(reg_lock); const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = struct_field_offset, }); - const offset_reg_lock = self.register_manager.freezeRegAssumeUnused(offset_reg); - defer self.register_manager.unfreezeReg(offset_reg_lock); + const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); + defer self.register_manager.unlockReg(offset_reg_lock); const can_reuse_operand = self.reuseOperand(inst, operand, 0, mcv); const result_reg = blk: { @@ -3207,8 +3207,8 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde break :blk result_reg; } }; - const result_reg_lock = self.register_manager.freezeReg(result_reg); - defer if (result_reg_lock) |reg_locked| self.register_manager.unfreezeReg(reg_locked); + const result_reg_lock = self.register_manager.lockReg(result_reg); + defer if (result_reg_lock) |reg_locked| self.register_manager.unlockReg(reg_locked); try self.genBinMathOpMir(.add, ptr_ty, .{ .register = result_reg }, .{ .register = offset_reg }); break :result MCValue{ .register = result_reg }; @@ -3236,8 +3236,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { break :result MCValue{ .stack_offset = stack_offset }; }, .register => |reg| { - const reg_lock = self.register_manager.freezeRegAssumeUnused(reg); - defer self.register_manager.unfreezeReg(reg_lock); + const reg_lock = self.register_manager.lockRegAssumeUnused(reg); + defer self.register_manager.unlockReg(reg_lock); const dst_mcv = blk: { if (self.reuseOperand(inst, operand, 0, mcv)) { @@ -3250,10 +3250,10 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { } }; const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { - .register => |reg| self.register_manager.freezeReg(reg), + .register => |reg| self.register_manager.lockReg(reg), else => null, }; - defer if (dst_mcv_lock) |reg_locked| self.register_manager.unfreezeReg(reg_locked); + defer if (dst_mcv_lock) |reg_locked| self.register_manager.unlockReg(reg_locked); // Shift by struct_field_offset. const shift = @intCast(u8, struct_field_offset * @sizeOf(usize)); @@ -3295,8 +3295,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { }, 1 => { // Get overflow bit. - const reg_lock = self.register_manager.freezeRegAssumeUnused(reg); - defer self.register_manager.unfreezeReg(reg_lock); + const reg_lock = self.register_manager.lockRegAssumeUnused(reg); + defer self.register_manager.unlockReg(reg_lock); const dst_reg = try self.register_manager.allocReg(inst); const flags: u2 = switch (mcv) { @@ -3339,17 +3339,17 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: const lhs = try self.resolveInst(op_lhs); const lhs_lock: ?RegisterLock = switch (lhs) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs = try self.resolveInst(op_rhs); const rhs_lock: ?RegisterLock = switch (rhs) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (rhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); var flipped: bool = false; const dst_mcv = blk: { @@ -3363,10 +3363,10 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); }; const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { - .register => |reg| self.register_manager.freezeReg(reg), + .register => |reg| self.register_manager.lockReg(reg), else => null, }; - defer if (dst_mcv_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (dst_mcv_lock) |reg| self.register_manager.unlockReg(reg); const src_mcv = blk: { const mcv = if (flipped) lhs else rhs; @@ -3374,10 +3374,10 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, mcv) }; }; const src_mcv_lock: ?RegisterLock = switch (src_mcv) { - .register => |reg| self.register_manager.freezeReg(reg), + .register => |reg| self.register_manager.lockReg(reg), else => null, }; - defer if (src_mcv_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (src_mcv_lock) |reg| self.register_manager.unlockReg(reg); const tag = self.air.instructions.items(.tag)[inst]; switch (tag) { @@ -3408,8 +3408,8 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .register_overflow_unsigned => unreachable, .register_overflow_signed => unreachable, .ptr_stack_offset => { - const dst_reg_lock = self.register_manager.freezeReg(dst_reg); - defer if (dst_reg_lock) |reg_locked| self.register_manager.unfreezeReg(reg_locked); + const dst_reg_lock = self.register_manager.lockReg(dst_reg); + defer if (dst_reg_lock) |reg_locked| self.register_manager.unlockReg(reg_locked); const reg = try self.copyToTmpRegister(dst_ty, src_mcv); return self.genBinMathOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); @@ -3440,8 +3440,8 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .compare_flags_unsigned, => { assert(abi_size <= 8); - const dst_reg_lock = self.register_manager.freezeReg(dst_reg); - defer if (dst_reg_lock) |reg_locked| self.register_manager.unfreezeReg(reg_locked); + const dst_reg_lock = self.register_manager.lockReg(dst_reg); + defer if (dst_reg_lock) |reg_locked| self.register_manager.unlockReg(reg_locked); const reg = try self.copyToTmpRegister(dst_ty, src_mcv); return self.genBinMathOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); @@ -3791,13 +3791,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. try self.register_manager.getReg(.rdi, null); try self.genSetReg(Type.usize, .rdi, .{ .ptr_stack_offset = stack_offset }); - const rdi_lock = self.register_manager.freezeRegAssumeUnused(.rdi); + const rdi_lock = self.register_manager.lockRegAssumeUnused(.rdi); info.return_value.stack_offset = stack_offset; break :blk rdi_lock; } else null; - defer if (rdi_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (rdi_lock) |reg| self.register_manager.unlockReg(reg); for (args) |arg, arg_i| { const mc_arg = info.args[arg_i]; @@ -4017,8 +4017,8 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { switch (self.ret_mcv) { .stack_offset => { const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv); - const reg_lock = self.register_manager.freezeRegAssumeUnused(reg); - defer self.register_manager.unfreezeReg(reg_lock); + const reg_lock = self.register_manager.lockRegAssumeUnused(reg); + defer self.register_manager.unlockReg(reg_lock); try self.genSetStack(ret_ty, 0, operand, .{ .source_stack_base = .rbp, @@ -4051,8 +4051,8 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { switch (self.ret_mcv) { .stack_offset => { const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv); - const reg_lock = self.register_manager.freezeRegAssumeUnused(reg); - defer self.register_manager.unfreezeReg(reg_lock); + const reg_lock = self.register_manager.lockRegAssumeUnused(reg); + defer self.register_manager.unlockReg(reg_lock); try self.genInlineMemcpy(.{ .stack_offset = 0 }, ptr, .{ .immediate = elem_ty.abiSize(self.target.*) }, .{ .source_stack_base = .rbp, @@ -4104,14 +4104,14 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { // TODO look into reusing the operand const lhs = try self.resolveInst(bin_op.lhs); const lhs_lock: ?RegisterLock = switch (lhs) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (lhs_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); const dst_reg = try self.copyToTmpRegister(ty, lhs); - const dst_reg_lock = self.register_manager.freezeRegAssumeUnused(dst_reg); - defer self.register_manager.unfreezeReg(dst_reg_lock); + const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); + defer self.register_manager.unlockReg(dst_reg_lock); const dst_mcv = MCValue{ .register = dst_reg }; @@ -4576,10 +4576,10 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { const operand_ptr = try self.resolveInst(un_op); const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (operand_ptr_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (operand_ptr_lock) |reg| self.register_manager.unlockReg(reg); const operand: MCValue = blk: { if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { @@ -4612,10 +4612,10 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { const operand_ptr = try self.resolveInst(un_op); const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (operand_ptr_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (operand_ptr_lock) |reg| self.register_manager.unlockReg(reg); const operand: MCValue = blk: { if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { @@ -4648,10 +4648,10 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { const operand_ptr = try self.resolveInst(un_op); const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (operand_ptr_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (operand_ptr_lock) |reg| self.register_manager.unlockReg(reg); const operand: MCValue = blk: { if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { @@ -4684,10 +4684,10 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { const operand_ptr = try self.resolveInst(un_op); const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (operand_ptr_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (operand_ptr_lock) |reg| self.register_manager.unlockReg(reg); const operand: MCValue = blk: { if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { @@ -4756,8 +4756,8 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u .register => |cond_reg| { try self.spillCompareFlagsIfOccupied(); - const cond_reg_lock = self.register_manager.freezeReg(cond_reg); - defer if (cond_reg_lock) |reg| self.register_manager.unfreezeReg(reg); + const cond_reg_lock = self.register_manager.lockReg(cond_reg); + defer if (cond_reg_lock) |reg| self.register_manager.unlockReg(reg); switch (case) { .none => unreachable, @@ -4816,8 +4816,8 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u if (abi_size <= 8) { const reg = try self.copyToTmpRegister(ty, condition); - const reg_lock = self.register_manager.freezeRegAssumeUnused(reg); - defer self.register_manager.unfreezeReg(reg_lock); + const reg_lock = self.register_manager.lockRegAssumeUnused(reg); + defer self.register_manager.unlockReg(reg_lock); return self.genCondSwitchMir(ty, .{ .register = reg }, case); } @@ -5304,8 +5304,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl .register_overflow_unsigned, .register_overflow_signed, => |reg| { - const reg_lock = self.register_manager.freezeReg(reg); - defer if (reg_lock) |reg_locked| self.register_manager.unfreezeReg(reg_locked); + const reg_lock = self.register_manager.lockReg(reg); + defer if (reg_lock) |reg_locked| self.register_manager.unlockReg(reg_locked); const wrapped_ty = ty.structFieldType(0); try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg }, .{}); @@ -5406,8 +5406,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl const base_reg = opts.dest_stack_base orelse .rbp; if (!math.isPowerOfTwo(abi_size)) { - const reg_lock = self.register_manager.freezeReg(reg); - defer if (reg_lock) |reg_locked| self.register_manager.unfreezeReg(reg_locked); + const reg_lock = self.register_manager.lockReg(reg); + defer if (reg_lock) |reg_locked| self.register_manager.unlockReg(reg_locked); const tmp_reg = try self.copyToTmpRegister(ty, mcv); @@ -5500,22 +5500,22 @@ fn genInlineMemcpy( try self.register_manager.getReg(.rcx, null); var reg_locks: [2]RegisterLock = undefined; - self.register_manager.freezeRegsAssumeUnused(2, .{ .rax, .rcx }, ®_locks); + self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rcx }, ®_locks); defer for (reg_locks) |reg| { - self.register_manager.unfreezeReg(reg); + self.register_manager.unlockReg(reg); }; const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg| - self.register_manager.freezeReg(reg) + self.register_manager.lockReg(reg) else null; - defer if (ssbase_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (ssbase_lock) |reg| self.register_manager.unlockReg(reg); const dsbase_lock: ?RegisterLock = if (opts.dest_stack_base) |reg| - self.register_manager.freezeReg(reg) + self.register_manager.lockReg(reg) else null; - defer if (dsbase_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (dsbase_lock) |reg| self.register_manager.unlockReg(reg); const dst_addr_reg = try self.register_manager.allocReg(null); switch (dst_ptr) { @@ -5549,8 +5549,8 @@ fn genInlineMemcpy( return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); }, } - const dst_addr_reg_lock = self.register_manager.freezeRegAssumeUnused(dst_addr_reg); - defer self.register_manager.unfreezeReg(dst_addr_reg_lock); + const dst_addr_reg_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg); + defer self.register_manager.unlockReg(dst_addr_reg_lock); const src_addr_reg = try self.register_manager.allocReg(null); switch (src_ptr) { @@ -5584,8 +5584,8 @@ fn genInlineMemcpy( return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr}); }, } - const src_addr_reg_lock = self.register_manager.freezeRegAssumeUnused(src_addr_reg); - defer self.register_manager.unfreezeReg(src_addr_reg_lock); + const src_addr_reg_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg); + defer self.register_manager.unlockReg(src_addr_reg_lock); const regs = try self.register_manager.allocRegs(2, .{ null, null }); const count_reg = regs[0].to64(); @@ -5695,8 +5695,8 @@ fn genInlineMemset( opts: InlineMemcpyOpts, ) InnerError!void { try self.register_manager.getReg(.rax, null); - const rax_lock = self.register_manager.freezeRegAssumeUnused(.rax); - defer self.register_manager.unfreezeReg(rax_lock); + const rax_lock = self.register_manager.lockRegAssumeUnused(.rax); + defer self.register_manager.unlockReg(rax_lock); const addr_reg = try self.register_manager.allocReg(null); switch (dst_ptr) { @@ -5730,8 +5730,8 @@ fn genInlineMemset( return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr}); }, } - const addr_reg_lock = self.register_manager.freezeRegAssumeUnused(addr_reg); - defer self.register_manager.unfreezeReg(addr_reg_lock); + const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); + defer self.register_manager.unlockReg(addr_reg_lock); try self.genSetReg(Type.usize, .rax, len); try self.genBinMathOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 }); @@ -6171,24 +6171,24 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) !void { const dst_ptr = try self.resolveInst(pl_op.operand); const dst_ptr_lock: ?RegisterLock = switch (dst_ptr) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (dst_ptr_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (dst_ptr_lock) |reg| self.register_manager.unlockReg(reg); const src_val = try self.resolveInst(extra.lhs); const src_val_lock: ?RegisterLock = switch (src_val) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (src_val_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (src_val_lock) |reg| self.register_manager.unlockReg(reg); const len = try self.resolveInst(extra.rhs); const len_lock: ?RegisterLock = switch (len) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (len_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (len_lock) |reg| self.register_manager.unlockReg(reg); try self.genInlineMemset(dst_ptr, src_val, len, .{}); @@ -6201,25 +6201,25 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { const dst_ptr = try self.resolveInst(pl_op.operand); const dst_ptr_lock: ?RegisterLock = switch (dst_ptr) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (dst_ptr_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (dst_ptr_lock) |reg| self.register_manager.unlockReg(reg); const src_ty = self.air.typeOf(extra.lhs); const src_ptr = try self.resolveInst(extra.lhs); const src_ptr_lock: ?RegisterLock = switch (src_ptr) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (src_ptr_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (src_ptr_lock) |reg| self.register_manager.unlockReg(reg); const len = try self.resolveInst(extra.rhs); const len_lock: ?RegisterLock = switch (len) { - .register => |reg| self.register_manager.freezeRegAssumeUnused(reg), + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (len_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (len_lock) |reg| self.register_manager.unlockReg(reg); // TODO Is this the only condition for pointer dereference for memcpy? const src: MCValue = blk: { @@ -6242,10 +6242,10 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { } }; const src_lock: ?RegisterLock = switch (src) { - .register => |reg| self.register_manager.freezeReg(reg), + .register => |reg| self.register_manager.lockReg(reg), else => null, }; - defer if (src_lock) |reg| self.register_manager.unfreezeReg(reg); + defer if (src_lock) |reg| self.register_manager.unlockReg(reg); try self.genInlineMemcpy(dst_ptr, src, len, .{}); diff --git a/src/register_manager.zig b/src/register_manager.zig index 6e73181f2a..61f5e173ee 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -45,9 +45,8 @@ pub fn RegisterManager( /// Tracks all registers allocated in the course of this /// function allocated_registers: FreeRegInt = 0, - /// Tracks registers which are temporarily blocked from being - /// allocated - frozen_registers: FreeRegInt = 0, + /// Tracks registers which are locked from being allocated + locked_registers: FreeRegInt = 0, const Self = @This(); @@ -108,12 +107,12 @@ pub fn RegisterManager( return self.allocated_registers & mask != 0; } - /// Returns whether this register is frozen + /// Returns whether this register is locked /// /// Returns false when this register is not tracked - pub fn isRegFrozen(self: Self, reg: Register) bool { + pub fn isRegLocked(self: Self, reg: Register) bool { const mask = getRegisterMask(reg) orelse return false; - return self.frozen_registers & mask != 0; + return self.locked_registers & mask != 0; } pub const RegisterLock = struct { @@ -121,56 +120,56 @@ pub fn RegisterManager( }; /// Prevents the register from being allocated until they are - /// unfrozen again. + /// unlocked again. /// Returns `RegisterLock` if the register was not already - /// frozen, or `null` otherwise. - /// Only the owner of the `RegisterLock` can unfreeze the + /// locked, or `null` otherwise. + /// Only the owner of the `RegisterLock` can unlock the /// register later. - pub fn freezeReg(self: *Self, reg: Register) ?RegisterLock { - log.debug("freezing {}", .{reg}); - if (self.isRegFrozen(reg)) { + pub fn lockReg(self: *Self, reg: Register) ?RegisterLock { + log.debug("locking {}", .{reg}); + if (self.isRegLocked(reg)) { log.debug(" register already locked", .{}); return null; } const mask = getRegisterMask(reg) orelse return null; - self.frozen_registers |= mask; + self.locked_registers |= mask; return RegisterLock{ .register = reg }; } - /// Like `freezeReg` but asserts the register was unused always + /// Like `lockReg` but asserts the register was unused always /// returning a valid lock. - pub fn freezeRegAssumeUnused(self: *Self, reg: Register) RegisterLock { - log.debug("freezing asserting free {}", .{reg}); - assert(!self.isRegFrozen(reg)); + pub fn lockRegAssumeUnused(self: *Self, reg: Register) RegisterLock { + log.debug("locking asserting free {}", .{reg}); + assert(!self.isRegLocked(reg)); const mask = getRegisterMask(reg) orelse unreachable; - self.frozen_registers |= mask; + self.locked_registers |= mask; return RegisterLock{ .register = reg }; } - /// Like `freezeRegAssumeUnused` but locks multiple registers. - pub fn freezeRegsAssumeUnused( + /// Like `lockRegAssumeUnused` but locks multiple registers. + pub fn lockRegsAssumeUnused( self: *Self, comptime count: comptime_int, regs: [count]Register, buf: *[count]RegisterLock, ) void { for (®s) |reg, i| { - buf[i] = self.freezeRegAssumeUnused(reg); + buf[i] = self.lockRegAssumeUnused(reg); } } - /// Unfreezes the register allowing its re-allocation and re-use. - /// Requires `RegisterLock` to unfreeze a register. - /// Call `freezeReg` to obtain the lock first. - pub fn unfreezeReg(self: *Self, lock: RegisterLock) void { - log.debug("unfreezing {}", .{lock.register}); + /// Unlocks the register allowing its re-allocation and re-use. + /// Requires `RegisterLock` to unlock a register. + /// Call `lockReg` to obtain the lock first. + pub fn unlockReg(self: *Self, lock: RegisterLock) void { + log.debug("unlocking {}", .{lock.register}); const mask = getRegisterMask(lock.register) orelse return; - self.frozen_registers &= ~mask; + self.locked_registers &= ~mask; } - /// Returns true when at least one register is frozen - pub fn frozenRegsExist(self: Self) bool { - return self.frozen_registers != 0; + /// Returns true when at least one register is locked + pub fn lockedRegsExist(self: Self) bool { + return self.locked_registers != 0; } /// Allocates a specified number of registers, optionally @@ -183,15 +182,15 @@ pub fn RegisterManager( ) ?[count]Register { comptime assert(count > 0 and count <= tracked_registers.len); - const free_and_not_frozen_registers = self.free_registers & ~self.frozen_registers; - const free_and_not_frozen_registers_count = @popCount(FreeRegInt, free_and_not_frozen_registers); - if (free_and_not_frozen_registers_count < count) return null; + const free_and_not_locked_registers = self.free_registers & ~self.locked_registers; + const free_and_not_locked_registers_count = @popCount(FreeRegInt, free_and_not_locked_registers); + if (free_and_not_locked_registers_count < count) return null; var regs: [count]Register = undefined; var i: usize = 0; for (tracked_registers) |reg| { if (i >= count) break; - if (self.isRegFrozen(reg)) continue; + if (self.isRegLocked(reg)) continue; if (!self.isRegFree(reg)) continue; regs[i] = reg; @@ -229,8 +228,8 @@ pub fn RegisterManager( insts: [count]?Air.Inst.Index, ) AllocateRegistersError![count]Register { comptime assert(count > 0 and count <= tracked_registers.len); - const frozen_registers_count = @popCount(FreeRegInt, self.frozen_registers); - if (count > tracked_registers.len - frozen_registers_count) return error.OutOfRegisters; + const locked_registers_count = @popCount(FreeRegInt, self.locked_registers); + if (count > tracked_registers.len - locked_registers_count) return error.OutOfRegisters; const result = self.tryAllocRegs(count, insts) orelse blk: { // We'll take over the first count registers. Spill @@ -240,7 +239,7 @@ pub fn RegisterManager( var i: usize = 0; for (tracked_registers) |reg| { if (i >= count) break; - if (self.isRegFrozen(reg)) continue; + if (self.isRegLocked(reg)) continue; regs[i] = reg; self.markRegAllocated(reg); @@ -451,15 +450,15 @@ test "allocReg: spilling" { try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); - // Frozen registers + // Locked registers function.register_manager.freeReg(.r3); { - const lock = function.register_manager.freezeReg(.r2); - defer if (lock) |reg| function.register_manager.unfreezeReg(reg); + const lock = function.register_manager.lockReg(.r2); + defer if (lock) |reg| function.register_manager.unlockReg(reg); try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); } - try expect(!function.register_manager.frozenRegsExist()); + try expect(!function.register_manager.lockedRegsExist()); } test "tryAllocRegs" { @@ -477,17 +476,17 @@ test "tryAllocRegs" { try expect(function.register_manager.isRegAllocated(.r2)); try expect(!function.register_manager.isRegAllocated(.r3)); - // Frozen registers + // Locked registers function.register_manager.freeReg(.r0); function.register_manager.freeReg(.r2); function.register_manager.freeReg(.r3); { - const lock = function.register_manager.freezeReg(.r1); - defer if (lock) |reg| function.register_manager.unfreezeReg(reg); + const lock = function.register_manager.lockReg(.r1); + defer if (lock) |reg| function.register_manager.unlockReg(reg); try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?); } - try expect(!function.register_manager.frozenRegsExist()); + try expect(!function.register_manager.lockedRegsExist()); try expect(function.register_manager.isRegAllocated(.r0)); try expect(function.register_manager.isRegAllocated(.r1)); @@ -510,19 +509,19 @@ test "allocRegs: normal usage" { // The result register is known and fixed at this point, we // don't want to accidentally allocate lhs or rhs to the - // result register, this is why we freeze it. + // result register, this is why we lock it. // - // Using defer unfreeze right after freeze is a good idea in - // most cases as you probably are using the frozen registers + // Using defer unlock right after lock is a good idea in + // most cases as you probably are using the locked registers // in the remainder of this scope and don't need to use it // after the end of this scope. However, in some situations, - // it may make sense to manually unfreeze registers before the + // it may make sense to manually unlock registers before the // end of the scope when you are certain that they don't // contain any valuable data anymore and can be reused. For an // example of that, see `selectively reducing register // pressure`. - const lock = function.register_manager.freezeReg(result_reg); - defer if (lock) |reg| function.register_manager.unfreezeReg(reg); + const lock = function.register_manager.lockReg(result_reg); + defer if (lock) |reg| function.register_manager.unlockReg(reg); const regs = try function.register_manager.allocRegs(2, .{ null, null }); try function.genAdd(result_reg, regs[0], regs[1]); @@ -542,14 +541,14 @@ test "allocRegs: selectively reducing register pressure" { { const result_reg: MockRegister2 = .r1; - const lock = function.register_manager.freezeReg(result_reg); + const lock = function.register_manager.lockReg(result_reg); - // Here, we don't defer unfreeze because we manually unfreeze + // Here, we don't defer unlock because we manually unlock // after genAdd const regs = try function.register_manager.allocRegs(2, .{ null, null }); try function.genAdd(result_reg, regs[0], regs[1]); - function.register_manager.unfreezeReg(lock.?); + function.register_manager.unlockReg(lock.?); const extra_summand_reg = try function.register_manager.allocReg(null); try function.genAdd(result_reg, result_reg, extra_summand_reg); From bf11cdc9d880555aee087a9bc0ecd1424428b63b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 7 May 2022 11:28:57 +0200 Subject: [PATCH 1402/2031] x64: refactor code to avoid stage1 sema limitations --- src/arch/x86_64/CodeGen.zig | 851 +++++++++++++++++++----------------- src/register_manager.zig | 2 +- 2 files changed, 453 insertions(+), 400 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 8d140c4da9..5cdc4c9889 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -191,36 +191,12 @@ pub const MCValue = union(enum) { }; } - fn usesCompareFlags(mcv: MCValue) bool { - return switch (mcv) { - .compare_flags_unsigned, - .compare_flags_signed, - .register_overflow_unsigned, - .register_overflow_signed, - => true, - else => false, - }; - } - fn isRegister(mcv: MCValue) bool { return switch (mcv) { - .register, - .register_overflow_unsigned, - .register_overflow_signed, - => true, + .register => true, else => false, }; } - - fn asRegister(mcv: MCValue) ?Register { - return switch (mcv) { - .register, - .register_overflow_unsigned, - .register_overflow_signed, - => |reg| reg, - else => null, - }; - } }; const Branch = struct { @@ -852,15 +828,21 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; branch.inst_table.putAssumeCapacityNoClobber(inst, result); - if (result.asRegister()) |reg| { - // In some cases (such as bitcast), an operand - // may be the same MCValue as the result. If - // that operand died and was a register, it - // was freed by processDeath. We have to - // "re-allocate" the register. - if (self.register_manager.isRegFree(reg)) { - self.register_manager.getRegAssumeFree(reg, inst); - } + switch (result) { + .register, + .register_overflow_signed, + .register_overflow_unsigned, + => |reg| { + // In some cases (such as bitcast), an operand + // may be the same MCValue as the result. If + // that operand died and was a register, it + // was freed by processDeath. We have to + // "re-allocate" the register. + if (self.register_manager.isRegFree(reg)) { + self.register_manager.getRegAssumeFree(reg, inst); + } + }, + else => {}, } } self.finishAirBookkeeping(); @@ -948,18 +930,32 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void pub fn spillCompareFlagsIfOccupied(self: *Self) !void { if (self.compare_flags_inst) |inst_to_save| { const mcv = self.getResolvedInstValue(inst_to_save); - assert(mcv.usesCompareFlags()); + const new_mcv = switch (mcv) { + .register_overflow_signed, + .register_overflow_unsigned, + => try self.allocRegOrMem(inst_to_save, false), + .compare_flags_signed, + .compare_flags_unsigned, + => try self.allocRegOrMem(inst_to_save, true), + else => unreachable, + }; - const new_mcv = try self.allocRegOrMem(inst_to_save, !mcv.isRegister()); try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); self.compare_flags_inst = null; + // TODO consolidate with register manager and spillInstruction // this call should really belong in the register manager! - if (mcv.isRegister()) self.register_manager.freeReg(mcv.asRegister().?); + switch (mcv) { + .register_overflow_signed, + .register_overflow_unsigned, + => |reg| self.register_manager.freeReg(reg), + else => {}, + } } } @@ -1031,7 +1027,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (operand_lock) |reg| self.register_manager.unlockReg(reg); + defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); const reg = try self.register_manager.allocReg(inst); try self.genSetReg(dest_ty, reg, .{ .immediate = 0 }); @@ -1062,7 +1058,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (operand_lock) |reg| self.register_manager.unlockReg(reg); + defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); const reg: Register = blk: { if (operand.isRegister()) { @@ -1150,7 +1146,7 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); + defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); const lhs_reg = try self.copyToTmpRegister(ty, lhs); const lhs_reg_lock = self.register_manager.lockRegAssumeUnused(lhs_reg); @@ -1161,7 +1157,7 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); + defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); try self.genBinMathOpMir(.cmp, ty, .{ .register = lhs_reg }, rhs_mcv); @@ -1200,9 +1196,9 @@ fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_r .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (offset_lock) |reg| self.register_manager.unlockReg(reg); + defer if (offset_lock) |lock| self.register_manager.unlockReg(lock); - const dst_mcv = blk: { + const dst_mcv: MCValue = blk: { if (self.reuseOperand(inst, op_lhs, 0, ptr)) { if (ptr.isMemory() or ptr.isRegister()) break :blk ptr; } @@ -1213,9 +1209,9 @@ fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_r .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (dst_mcv_lock) |reg| self.register_manager.unlockReg(reg); + defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); - const offset_mcv = blk: { + const offset_mcv: MCValue = blk: { if (self.reuseOperand(inst, op_rhs, 1, offset)) { if (offset.isRegister()) break :blk offset; } @@ -1226,7 +1222,7 @@ fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_r .register => |reg| self.register_manager.lockReg(reg), else => null, }; - defer if (offset_mcv_lock) |reg| self.register_manager.unlockReg(reg); + defer if (offset_mcv_lock) |lock| self.register_manager.unlockReg(lock); try self.genIntMulComplexOpMir(offset_ty, offset_mcv, .{ .immediate = elem_size }); @@ -1315,16 +1311,16 @@ fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); + defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); const rhs = try self.resolveInst(op_rhs); const rhs_lock: ?RegisterLock = switch (rhs) { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); + defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); - const dst_mcv = blk: { + const dst_mcv: MCValue = blk: { if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) { break :blk lhs; } @@ -1334,9 +1330,9 @@ fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air .register => |reg| self.register_manager.lockReg(reg), else => null, }; - defer if (dst_mcv_lock) |reg| self.register_manager.unlockReg(reg); + defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); - const rhs_mcv = blk: { + const rhs_mcv: MCValue = blk: { if (rhs.isMemory() or rhs.isRegister()) break :blk rhs; break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, rhs) }; }; @@ -1344,7 +1340,7 @@ fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air .register => |reg| self.register_manager.lockReg(reg), else => null, }; - defer if (rhs_mcv_lock) |reg| self.register_manager.unlockReg(reg); + defer if (rhs_mcv_lock) |lock| self.register_manager.unlockReg(lock); try self.genBinMathOpMir(.sub, dst_ty, dst_mcv, rhs_mcv); @@ -1476,9 +1472,13 @@ fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; - const result = if (self.liveness.isUnused(inst)) .dead else result: { - const ty = self.air.typeOf(bin_op.lhs); + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + } + + const ty = self.air.typeOf(bin_op.lhs); + const result: MCValue = result: { switch (ty.zigTypeTag()) { .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}), .Int => { @@ -1529,7 +1529,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); + defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); const dst_reg: Register = blk: { if (lhs.isRegister()) break :blk lhs.register; @@ -1538,7 +1538,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); defer self.register_manager.unlockReg(dst_reg_lock); - const rhs_mcv = blk: { + const rhs_mcv: MCValue = blk: { if (rhs.isRegister() or rhs.isMemory()) break :blk rhs; break :blk MCValue{ .register = try self.copyToTmpRegister(ty, rhs) }; }; @@ -1546,7 +1546,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { .register => |reg| self.register_manager.lockReg(reg), else => null, }; - defer if (rhs_mcv_lock) |reg| self.register_manager.unlockReg(reg); + defer if (rhs_mcv_lock) |lock| self.register_manager.unlockReg(lock); try self.genIntMulComplexOpMir(Type.isize, .{ .register = dst_reg }, rhs_mcv); @@ -1734,19 +1734,19 @@ fn genIntMulDivOpMir( /// Clobbers .rax and .rdx registers. fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCValue { const signedness = ty.intInfo(self.target.*).signedness; - const dividend = switch (lhs) { + const dividend: Register = switch (lhs) { .register => |reg| reg, else => try self.copyToTmpRegister(ty, lhs), }; const dividend_lock = self.register_manager.lockReg(dividend); - defer if (dividend_lock) |reg| self.register_manager.unlockReg(reg); + defer if (dividend_lock) |lock| self.register_manager.unlockReg(lock); - const divisor = switch (rhs) { + const divisor: Register = switch (rhs) { .register => |reg| reg, else => try self.copyToTmpRegister(ty, rhs), }; const divisor_lock = self.register_manager.lockReg(divisor); - defer if (divisor_lock) |reg| self.register_manager.unlockReg(reg); + defer if (divisor_lock) |lock| self.register_manager.unlockReg(lock); try self.genIntMulDivOpMir(switch (signedness) { .signed => .idiv, @@ -1791,67 +1791,72 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa fn airDiv(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 result: { - const tag = self.air.instructions.items(.tag)[inst]; - const ty = self.air.typeOfIndex(inst); - if (ty.zigTypeTag() != .Int) { - return self.fail("TODO implement {} for operands of dst type {}", .{ tag, ty.zigTypeTag() }); + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + } + + const tag = self.air.instructions.items(.tag)[inst]; + const ty = self.air.typeOfIndex(inst); + + if (ty.zigTypeTag() != .Int) { + return self.fail("TODO implement {} for operands of dst type {}", .{ tag, ty.zigTypeTag() }); + } + + if (tag == .div_float) { + return self.fail("TODO implement {}", .{tag}); + } + + const signedness = ty.intInfo(self.target.*).signedness; + + // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. + const track_rax: ?Air.Inst.Index = blk: { + if (signedness == .unsigned) break :blk inst; + switch (tag) { + .div_exact, .div_trunc => break :blk inst, + else => break :blk null, } + }; + try self.register_manager.getReg(.rax, track_rax); + try self.register_manager.getReg(.rdx, null); + var reg_locks: [2]RegisterLock = undefined; + self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + defer for (reg_locks) |reg| { + self.register_manager.unlockReg(reg); + }; - if (tag == .div_float) { - return self.fail("TODO implement {}", .{tag}); - } + const lhs = try self.resolveInst(bin_op.lhs); + const lhs_lock: ?RegisterLock = switch (lhs) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); - const signedness = ty.intInfo(self.target.*).signedness; - - // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. - const track_rax: ?Air.Inst.Index = blk: { - if (signedness == .unsigned) break :blk inst; + const rhs: MCValue = blk: { + const rhs = try self.resolveInst(bin_op.rhs); + if (signedness == .signed) { switch (tag) { - .div_exact, .div_trunc => break :blk inst, - else => break :blk null, + .div_floor => { + const rhs_lock: ?RegisterLock = switch (rhs) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); + + break :blk try self.copyToRegisterWithInstTracking(inst, ty, rhs); + }, + else => {}, } - }; - try self.register_manager.getReg(.rax, track_rax); - try self.register_manager.getReg(.rdx, null); - var reg_locks: [2]RegisterLock = undefined; - self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); - defer for (reg_locks) |reg| { - self.register_manager.unlockReg(reg); - }; - - const lhs = try self.resolveInst(bin_op.lhs); - const lhs_lock: ?RegisterLock = switch (lhs) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); - - const rhs = blk: { - const rhs = try self.resolveInst(bin_op.rhs); - if (signedness == .signed) { - switch (tag) { - .div_floor => { - const rhs_lock: ?RegisterLock = switch (rhs) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); - - break :blk try self.copyToRegisterWithInstTracking(inst, ty, rhs); - }, - else => {}, - } - } - break :blk rhs; - }; - const rhs_lock: ?RegisterLock = switch (rhs) { - .register => |reg| self.register_manager.lockReg(reg), - else => null, - }; - defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); + } + break :blk rhs; + }; + const rhs_lock: ?RegisterLock = switch (rhs) { + .register => |reg| self.register_manager.lockReg(reg), + else => null, + }; + defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); + const result: MCValue = result: { if (signedness == .unsigned) { try self.genIntMulDivOpMir(.div, ty, signedness, lhs, rhs); break :result MCValue{ .register = .rax }; @@ -1871,59 +1876,69 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { else => unreachable, } }; + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } fn airRem(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 result: { - const ty = self.air.typeOfIndex(inst); - if (ty.zigTypeTag() != .Int) { - return self.fail("TODO implement .rem for operands of dst type {}", .{ty.zigTypeTag()}); - } - // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. - try self.register_manager.getReg(.rax, null); - try self.register_manager.getReg(.rdx, inst); - var reg_locks: [2]RegisterLock = undefined; - self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); - defer for (reg_locks) |reg| { - self.register_manager.unlockReg(reg); - }; - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - - const signedness = ty.intInfo(self.target.*).signedness; - try self.genIntMulDivOpMir(switch (signedness) { - .signed => .idiv, - .unsigned => .div, - }, ty, signedness, lhs, rhs); - break :result MCValue{ .register = .rdx }; + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + } + const ty = self.air.typeOfIndex(inst); + if (ty.zigTypeTag() != .Int) { + return self.fail("TODO implement .rem for operands of dst type {}", .{ty.zigTypeTag()}); + } + // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. + try self.register_manager.getReg(.rax, null); + try self.register_manager.getReg(.rdx, inst); + var reg_locks: [2]RegisterLock = undefined; + self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + defer for (reg_locks) |reg| { + self.register_manager.unlockReg(reg); }; + + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + const signedness = ty.intInfo(self.target.*).signedness; + try self.genIntMulDivOpMir(switch (signedness) { + .signed => .idiv, + .unsigned => .div, + }, ty, signedness, lhs, rhs); + + const result: MCValue = .{ .register = .rdx }; + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } fn airMod(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 result: { - const ty = self.air.typeOfIndex(inst); - if (ty.zigTypeTag() != .Int) { - return self.fail("TODO implement .mod for operands of dst type {}", .{ty.zigTypeTag()}); - } - const signedness = ty.intInfo(self.target.*).signedness; - // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. - try self.register_manager.getReg(.rax, null); - try self.register_manager.getReg(.rdx, if (signedness == .unsigned) inst else null); - var reg_locks: [2]RegisterLock = undefined; - self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); - defer for (reg_locks) |reg| { - self.register_manager.unlockReg(reg); - }; + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + } - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); + const ty = self.air.typeOfIndex(inst); + if (ty.zigTypeTag() != .Int) { + return self.fail("TODO implement .mod for operands of dst type {}", .{ty.zigTypeTag()}); + } + const signedness = ty.intInfo(self.target.*).signedness; + // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. + try self.register_manager.getReg(.rax, null); + try self.register_manager.getReg(.rdx, if (signedness == .unsigned) inst else null); + var reg_locks: [2]RegisterLock = undefined; + self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + defer for (reg_locks) |reg| { + self.register_manager.unlockReg(reg); + }; + + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + const result: MCValue = result: { switch (signedness) { .unsigned => { try self.genIntMulDivOpMir(switch (signedness) { @@ -1943,6 +1958,7 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void { }, } }; + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -2017,7 +2033,7 @@ fn airShl(self: *Self, inst: Air.Inst.Index) !void { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (value_lock) |reg| self.register_manager.unlockReg(reg); + defer if (value_lock) |lock| self.register_manager.unlockReg(lock); const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, value); _ = try self.addInst(.{ @@ -2117,7 +2133,7 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (operand_lock) |reg| self.register_manager.unlockReg(reg); + defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); const result: MCValue = result: { if (!payload_ty.hasRuntimeBits()) break :result operand; @@ -2150,7 +2166,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (operand_lock) |reg| self.register_manager.unlockReg(reg); + defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); const abi_align = err_union_ty.abiAlignment(self.target.*); const err_ty = err_union_ty.errorUnionSet(); @@ -2222,7 +2238,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (operand_lock) |reg| self.register_manager.unlockReg(reg); + defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); if (optional_ty.isPtrLikeOptional()) { // TODO should we check if we can reuse the operand? @@ -2359,7 +2375,7 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (slice_mcv_lock) |reg| self.register_manager.unlockReg(reg); + defer if (slice_mcv_lock) |lock| self.register_manager.unlockReg(lock); const elem_ty = slice_ty.childType(); const elem_size = elem_ty.abiSize(self.target.*); @@ -2372,7 +2388,7 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (index_mcv_lock) |reg| self.register_manager.unlockReg(reg); + defer if (index_mcv_lock) |lock| self.register_manager.unlockReg(lock); const offset_reg = try self.elemOffset(index_ty, index_mcv, elem_size); const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); @@ -2429,110 +2445,119 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { 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 result: { - const array_ty = self.air.typeOf(bin_op.lhs); - const array = try self.resolveInst(bin_op.lhs); - const array_lock: ?RegisterLock = switch (array) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (array_lock) |reg| self.register_manager.unlockReg(reg); - const elem_ty = array_ty.childType(); - const elem_abi_size = elem_ty.abiSize(self.target.*); + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + } - const index_ty = self.air.typeOf(bin_op.rhs); - const index = try self.resolveInst(bin_op.rhs); - const index_lock: ?RegisterLock = switch (index) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (index_lock) |reg| self.register_manager.unlockReg(reg); - - const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); - const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); - defer self.register_manager.unlockReg(offset_reg_lock); - - const addr_reg = try self.register_manager.allocReg(null); - switch (array) { - .register => { - const off = @intCast(i32, try self.allocMem( - inst, - @intCast(u32, array_ty.abiSize(self.target.*)), - array_ty.abiAlignment(self.target.*), - )); - try self.genSetStack(array_ty, off, array, .{}); - // lea reg, [rbp] - _ = try self.addInst(.{ - .tag = .lea, - .ops = (Mir.Ops{ - .reg1 = addr_reg.to64(), - .reg2 = .rbp, - }).encode(), - .data = .{ .imm = @bitCast(u32, -off) }, - }); - }, - .stack_offset => |off| { - // lea reg, [rbp] - _ = try self.addInst(.{ - .tag = .lea, - .ops = (Mir.Ops{ - .reg1 = addr_reg.to64(), - .reg2 = .rbp, - }).encode(), - .data = .{ .imm = @bitCast(u32, -off) }, - }); - }, - .memory, - .got_load, - .direct_load, - => { - try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array); - }, - else => return self.fail("TODO implement array_elem_val when array is {}", .{array}), - } - - // TODO we could allocate register here, but need to expect addr register and potentially - // offset register. - const dst_mcv = try self.allocRegOrMem(inst, false); - try self.genBinMathOpMir(.add, Type.usize, .{ .register = addr_reg }, .{ .register = offset_reg }); - try self.load(dst_mcv, .{ .register = addr_reg.to64() }, array_ty); - break :result dst_mcv; + const array_ty = self.air.typeOf(bin_op.lhs); + const array = try self.resolveInst(bin_op.lhs); + const array_lock: ?RegisterLock = switch (array) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); + defer if (array_lock) |lock| self.register_manager.unlockReg(lock); + + const elem_ty = array_ty.childType(); + const elem_abi_size = elem_ty.abiSize(self.target.*); + + const index_ty = self.air.typeOf(bin_op.rhs); + const index = try self.resolveInst(bin_op.rhs); + const index_lock: ?RegisterLock = switch (index) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (index_lock) |lock| self.register_manager.unlockReg(lock); + + const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); + const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); + defer self.register_manager.unlockReg(offset_reg_lock); + + const addr_reg = try self.register_manager.allocReg(null); + switch (array) { + .register => { + const off = @intCast(i32, try self.allocMem( + inst, + @intCast(u32, array_ty.abiSize(self.target.*)), + array_ty.abiAlignment(self.target.*), + )); + try self.genSetStack(array_ty, off, array, .{}); + // lea reg, [rbp] + _ = try self.addInst(.{ + .tag = .lea, + .ops = (Mir.Ops{ + .reg1 = addr_reg.to64(), + .reg2 = .rbp, + }).encode(), + .data = .{ .imm = @bitCast(u32, -off) }, + }); + }, + .stack_offset => |off| { + // lea reg, [rbp] + _ = try self.addInst(.{ + .tag = .lea, + .ops = (Mir.Ops{ + .reg1 = addr_reg.to64(), + .reg2 = .rbp, + }).encode(), + .data = .{ .imm = @bitCast(u32, -off) }, + }); + }, + .memory, + .got_load, + .direct_load, + => { + try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array); + }, + else => return self.fail("TODO implement array_elem_val when array is {}", .{array}), + } + + // TODO we could allocate register here, but need to expect addr register and potentially + // offset register. + const dst_mcv = try self.allocRegOrMem(inst, false); + try self.genBinMathOpMir(.add, Type.usize, .{ .register = addr_reg }, .{ .register = offset_reg }); + try self.load(dst_mcv, .{ .register = addr_reg.to64() }, array_ty); + + return self.finishAir(inst, dst_mcv, .{ 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; - const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else result: { - // this is identical to the `airPtrElemPtr` codegen expect here an - // additional `mov` is needed at the end to get the actual value - const ptr_ty = self.air.typeOf(bin_op.lhs); - const ptr = try self.resolveInst(bin_op.lhs); - const ptr_lock: ?RegisterLock = switch (ptr) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (ptr_lock) |reg| self.register_manager.unlockReg(reg); + if (!is_volatile and self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + } - const elem_ty = ptr_ty.elemType2(); - const elem_abi_size = elem_ty.abiSize(self.target.*); - const index_ty = self.air.typeOf(bin_op.rhs); - const index = try self.resolveInst(bin_op.rhs); - const index_lock: ?RegisterLock = switch (index) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (index_lock) |reg| self.register_manager.unlockReg(reg); + // this is identical to the `airPtrElemPtr` codegen expect here an + // additional `mov` is needed at the end to get the actual value - const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); - const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); - defer self.register_manager.unlockReg(offset_reg_lock); + const ptr_ty = self.air.typeOf(bin_op.lhs); + const ptr = try self.resolveInst(bin_op.lhs); + const ptr_lock: ?RegisterLock = switch (ptr) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); - const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); - try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); + const elem_ty = ptr_ty.elemType2(); + const elem_abi_size = elem_ty.abiSize(self.target.*); + const index_ty = self.air.typeOf(bin_op.rhs); + const index = try self.resolveInst(bin_op.rhs); + const index_lock: ?RegisterLock = switch (index) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (index_lock) |lock| self.register_manager.unlockReg(lock); + + const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); + const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); + defer self.register_manager.unlockReg(offset_reg_lock); + + const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); + try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); + + const result: MCValue = result: { if (elem_abi_size > 8) { return self.fail("TODO copy value with size {} from pointer", .{elem_abi_size}); } else { @@ -2549,40 +2574,44 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { break :result .{ .register = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)) }; } }; + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } fn airPtrElemPtr(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 result: { - const ptr_ty = self.air.typeOf(extra.lhs); - const ptr = try self.resolveInst(extra.lhs); - const ptr_lock: ?RegisterLock = switch (ptr) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (ptr_lock) |reg| self.register_manager.unlockReg(reg); - const elem_ty = ptr_ty.elemType2(); - const elem_abi_size = elem_ty.abiSize(self.target.*); - const index_ty = self.air.typeOf(extra.rhs); - const index = try self.resolveInst(extra.rhs); - const index_lock: ?RegisterLock = switch (index) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (index_lock) |reg| self.register_manager.unlockReg(reg); + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); + } - const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); - const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); - defer self.register_manager.unlockReg(offset_reg_lock); - - const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); - try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); - break :result dst_mcv; + const ptr_ty = self.air.typeOf(extra.lhs); + const ptr = try self.resolveInst(extra.lhs); + const ptr_lock: ?RegisterLock = switch (ptr) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, }; - return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); + defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); + + const elem_ty = ptr_ty.elemType2(); + const elem_abi_size = elem_ty.abiSize(self.target.*); + const index_ty = self.air.typeOf(extra.rhs); + const index = try self.resolveInst(extra.rhs); + const index_lock: ?RegisterLock = switch (index) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (index_lock) |lock| self.register_manager.unlockReg(lock); + + const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); + const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); + defer self.register_manager.unlockReg(offset_reg_lock); + + const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); + try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); + + return self.finishAir(inst, dst_mcv, .{ extra.lhs, extra.rhs, .none }); } fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { @@ -2601,14 +2630,14 @@ fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (ptr_lock) |reg| self.register_manager.unlockReg(reg); + defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); const tag = try self.resolveInst(bin_op.rhs); const tag_lock: ?RegisterLock = switch (tag) { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (tag_lock) |reg| self.register_manager.unlockReg(reg); + defer if (tag_lock) |lock| self.register_manager.unlockReg(lock); const adjusted_ptr: MCValue = if (layout.payload_size > 0 and layout.tag_align < layout.payload_align) blk: { // TODO reusing the operand @@ -2642,7 +2671,7 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (operand_lock) |reg| self.register_manager.unlockReg(reg); + defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); const tag_abi_size = tag_ty.abiSize(self.target.*); const dst_mcv: MCValue = blk: { @@ -2790,7 +2819,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo }, .register => |reg| { const reg_lock = self.register_manager.lockReg(reg); - defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg); + defer if (reg_lock) |lock| self.register_manager.unlockReg(lock); switch (dst_mcv) { .dead => unreachable, @@ -2916,7 +2945,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type }, .register => |reg| { const reg_lock = self.register_manager.lockReg(reg); - defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg); + defer if (reg_lock) |lock| self.register_manager.unlockReg(lock); switch (value) { .none => unreachable, @@ -3010,7 +3039,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .register => |reg| self.register_manager.lockReg(reg), else => null, }; - defer if (value_lock) |reg| self.register_manager.unlockReg(reg); + defer if (value_lock) |lock| self.register_manager.unlockReg(lock); const addr_reg = try self.register_manager.allocReg(null); const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); @@ -3198,7 +3227,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde defer self.register_manager.unlockReg(offset_reg_lock); const can_reuse_operand = self.reuseOperand(inst, operand, 0, mcv); - const result_reg = blk: { + const result_reg: Register = blk: { if (can_reuse_operand) { break :blk reg; } else { @@ -3208,7 +3237,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde } }; const result_reg_lock = self.register_manager.lockReg(result_reg); - defer if (result_reg_lock) |reg_locked| self.register_manager.unlockReg(reg_locked); + defer if (result_reg_lock) |lock| self.register_manager.unlockReg(lock); try self.genBinMathOpMir(.add, ptr_ty, .{ .register = result_reg }, .{ .register = offset_reg }); break :result MCValue{ .register = result_reg }; @@ -3224,12 +3253,17 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; const operand = extra.struct_operand; const index = extra.field_index; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const mcv = try self.resolveInst(operand); - const struct_ty = self.air.typeOf(operand); - const struct_field_offset = struct_ty.structFieldOffset(index, self.target.*); - const struct_field_ty = struct_ty.structFieldType(index); + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ extra.struct_operand, .none, .none }); + } + + const mcv = try self.resolveInst(operand); + const struct_ty = self.air.typeOf(operand); + const struct_field_offset = struct_ty.structFieldOffset(index, self.target.*); + const struct_field_ty = struct_ty.structFieldType(index); + + const result: MCValue = result: { switch (mcv) { .stack_offset => |off| { const stack_offset = off - @intCast(i32, struct_field_offset); @@ -3239,7 +3273,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const reg_lock = self.register_manager.lockRegAssumeUnused(reg); defer self.register_manager.unlockReg(reg_lock); - const dst_mcv = blk: { + const dst_mcv: MCValue = blk: { if (self.reuseOperand(inst, operand, 0, mcv)) { break :blk mcv; } else { @@ -3250,10 +3284,10 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { } }; const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { - .register => |reg| self.register_manager.lockReg(reg), + .register => |a_reg| self.register_manager.lockReg(a_reg), else => null, }; - defer if (dst_mcv_lock) |reg_locked| self.register_manager.unlockReg(reg_locked); + defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); // Shift by struct_field_offset. const shift = @intCast(u8, struct_field_offset * @sizeOf(usize)); @@ -3342,17 +3376,17 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); + defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); const rhs = try self.resolveInst(op_rhs); const rhs_lock: ?RegisterLock = switch (rhs) { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); + defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); var flipped: bool = false; - const dst_mcv = blk: { + const dst_mcv: MCValue = blk: { if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) { break :blk lhs; } @@ -3366,9 +3400,9 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: .register => |reg| self.register_manager.lockReg(reg), else => null, }; - defer if (dst_mcv_lock) |reg| self.register_manager.unlockReg(reg); + defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); - const src_mcv = blk: { + const src_mcv: MCValue = blk: { const mcv = if (flipped) lhs else rhs; if (mcv.isRegister() or mcv.isMemory()) break :blk mcv; break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, mcv) }; @@ -3377,7 +3411,7 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: .register => |reg| self.register_manager.lockReg(reg), else => null, }; - defer if (src_mcv_lock) |reg| self.register_manager.unlockReg(reg); + defer if (src_mcv_lock) |lock| self.register_manager.unlockReg(lock); const tag = self.air.instructions.items(.tag)[inst]; switch (tag) { @@ -3409,7 +3443,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC .register_overflow_signed => unreachable, .ptr_stack_offset => { const dst_reg_lock = self.register_manager.lockReg(dst_reg); - defer if (dst_reg_lock) |reg_locked| self.register_manager.unlockReg(reg_locked); + defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); const reg = try self.copyToTmpRegister(dst_ty, src_mcv); return self.genBinMathOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); @@ -3441,7 +3475,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC => { assert(abi_size <= 8); const dst_reg_lock = self.register_manager.lockReg(dst_reg); - defer if (dst_reg_lock) |reg_locked| self.register_manager.unlockReg(reg_locked); + defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); const reg = try self.copyToTmpRegister(dst_ty, src_mcv); return self.genBinMathOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); @@ -3782,22 +3816,25 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. try self.register_manager.getReg(reg, null); } - const rdi_lock: ?RegisterLock = if (info.return_value == .stack_offset) blk: { - const ret_ty = fn_ty.fnReturnType(); - const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); - const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*)); - const stack_offset = @intCast(i32, try self.allocMem(inst, ret_abi_size, ret_abi_align)); - log.debug("airCall: return value on stack at offset {}", .{stack_offset}); + const rdi_lock: ?RegisterLock = blk: { + if (info.return_value == .stack_offset) { + const ret_ty = fn_ty.fnReturnType(); + const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); + const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*)); + const stack_offset = @intCast(i32, try self.allocMem(inst, ret_abi_size, ret_abi_align)); + log.debug("airCall: return value on stack at offset {}", .{stack_offset}); - try self.register_manager.getReg(.rdi, null); - try self.genSetReg(Type.usize, .rdi, .{ .ptr_stack_offset = stack_offset }); - const rdi_lock = self.register_manager.lockRegAssumeUnused(.rdi); + try self.register_manager.getReg(.rdi, null); + try self.genSetReg(Type.usize, .rdi, .{ .ptr_stack_offset = stack_offset }); + const rdi_lock = self.register_manager.lockRegAssumeUnused(.rdi); - info.return_value.stack_offset = stack_offset; + info.return_value.stack_offset = stack_offset; - break :blk rdi_lock; - } else null; - defer if (rdi_lock) |reg| self.register_manager.unlockReg(reg); + break :blk rdi_lock; + } + break :blk null; + }; + defer if (rdi_lock) |lock| self.register_manager.unlockReg(lock); for (args) |arg, arg_i| { const mc_arg = info.args[arg_i]; @@ -4107,7 +4144,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); + defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); const dst_reg = try self.copyToTmpRegister(ty, lhs); const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); @@ -4572,27 +4609,31 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand_ptr = try self.resolveInst(un_op); - const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (operand_ptr_lock) |reg| self.register_manager.unlockReg(reg); + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ un_op, .none, .none }); + } - const operand: MCValue = blk: { - if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { - // The MCValue that holds the pointer can be re-used as the value. - break :blk operand_ptr; - } else { - break :blk try self.allocRegOrMem(inst, true); - } - }; - const ptr_ty = self.air.typeOf(un_op); - try self.load(operand, operand_ptr, ptr_ty); - break :result try self.isNull(inst, ptr_ty.elemType(), operand); + const operand_ptr = try self.resolveInst(un_op); + const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, }; + defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock); + + const operand: MCValue = blk: { + if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { + // The MCValue that holds the pointer can be re-used as the value. + break :blk operand_ptr; + } else { + break :blk try self.allocRegOrMem(inst, true); + } + }; + const ptr_ty = self.air.typeOf(un_op); + try self.load(operand, operand_ptr, ptr_ty); + + const result = try self.isNull(inst, ptr_ty.elemType(), operand); + return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -4608,27 +4649,31 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand_ptr = try self.resolveInst(un_op); - const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (operand_ptr_lock) |reg| self.register_manager.unlockReg(reg); + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ un_op, .none, .none }); + } - const operand: MCValue = blk: { - if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { - // The MCValue that holds the pointer can be re-used as the value. - break :blk operand_ptr; - } else { - break :blk try self.allocRegOrMem(inst, true); - } - }; - const ptr_ty = self.air.typeOf(un_op); - try self.load(operand, operand_ptr, ptr_ty); - break :result try self.isNonNull(inst, ptr_ty.elemType(), operand); + const operand_ptr = try self.resolveInst(un_op); + const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, }; + defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock); + + const operand: MCValue = blk: { + if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { + // The MCValue that holds the pointer can be re-used as the value. + break :blk operand_ptr; + } else { + break :blk try self.allocRegOrMem(inst, true); + } + }; + const ptr_ty = self.air.typeOf(un_op); + try self.load(operand, operand_ptr, ptr_ty); + + const result = try self.isNonNull(inst, ptr_ty.elemType(), operand); + return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -4644,27 +4689,31 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand_ptr = try self.resolveInst(un_op); - const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (operand_ptr_lock) |reg| self.register_manager.unlockReg(reg); + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ un_op, .none, .none }); + } - const operand: MCValue = blk: { - if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { - // The MCValue that holds the pointer can be re-used as the value. - break :blk operand_ptr; - } else { - break :blk try self.allocRegOrMem(inst, true); - } - }; - const ptr_ty = self.air.typeOf(un_op); - try self.load(operand, operand_ptr, ptr_ty); - break :result try self.isErr(inst, ptr_ty.elemType(), operand); + const operand_ptr = try self.resolveInst(un_op); + const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, }; + defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock); + + const operand: MCValue = blk: { + if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { + // The MCValue that holds the pointer can be re-used as the value. + break :blk operand_ptr; + } else { + break :blk try self.allocRegOrMem(inst, true); + } + }; + const ptr_ty = self.air.typeOf(un_op); + try self.load(operand, operand_ptr, ptr_ty); + + const result = try self.isErr(inst, ptr_ty.elemType(), operand); + return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -4680,27 +4729,31 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand_ptr = try self.resolveInst(un_op); - const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (operand_ptr_lock) |reg| self.register_manager.unlockReg(reg); + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ un_op, .none, .none }); + } - const operand: MCValue = blk: { - if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { - // The MCValue that holds the pointer can be re-used as the value. - break :blk operand_ptr; - } else { - break :blk try self.allocRegOrMem(inst, true); - } - }; - const ptr_ty = self.air.typeOf(un_op); - try self.load(operand, operand_ptr, ptr_ty); - break :result try self.isNonErr(inst, ptr_ty.elemType(), operand); + const operand_ptr = try self.resolveInst(un_op); + const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, }; + defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock); + + const operand: MCValue = blk: { + if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { + // The MCValue that holds the pointer can be re-used as the value. + break :blk operand_ptr; + } else { + break :blk try self.allocRegOrMem(inst, true); + } + }; + const ptr_ty = self.air.typeOf(un_op); + try self.load(operand, operand_ptr, ptr_ty); + + const result = try self.isNonErr(inst, ptr_ty.elemType(), operand); + return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -4757,7 +4810,7 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u try self.spillCompareFlagsIfOccupied(); const cond_reg_lock = self.register_manager.lockReg(cond_reg); - defer if (cond_reg_lock) |reg| self.register_manager.unlockReg(reg); + defer if (cond_reg_lock) |lock| self.register_manager.unlockReg(lock); switch (case) { .none => unreachable, @@ -5305,7 +5358,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl .register_overflow_signed, => |reg| { const reg_lock = self.register_manager.lockReg(reg); - defer if (reg_lock) |reg_locked| self.register_manager.unlockReg(reg_locked); + defer if (reg_lock) |lock| self.register_manager.unlockReg(lock); const wrapped_ty = ty.structFieldType(0); try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg }, .{}); @@ -5407,7 +5460,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl const base_reg = opts.dest_stack_base orelse .rbp; if (!math.isPowerOfTwo(abi_size)) { const reg_lock = self.register_manager.lockReg(reg); - defer if (reg_lock) |reg_locked| self.register_manager.unlockReg(reg_locked); + defer if (reg_lock) |lock| self.register_manager.unlockReg(lock); const tmp_reg = try self.copyToTmpRegister(ty, mcv); @@ -5501,8 +5554,8 @@ fn genInlineMemcpy( var reg_locks: [2]RegisterLock = undefined; self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rcx }, ®_locks); - defer for (reg_locks) |reg| { - self.register_manager.unlockReg(reg); + defer for (reg_locks) |lock| { + self.register_manager.unlockReg(lock); }; const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg| @@ -5515,7 +5568,7 @@ fn genInlineMemcpy( self.register_manager.lockReg(reg) else null; - defer if (dsbase_lock) |reg| self.register_manager.unlockReg(reg); + defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock); const dst_addr_reg = try self.register_manager.allocReg(null); switch (dst_ptr) { @@ -6174,21 +6227,21 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) !void { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (dst_ptr_lock) |reg| self.register_manager.unlockReg(reg); + defer if (dst_ptr_lock) |lock| self.register_manager.unlockReg(lock); const src_val = try self.resolveInst(extra.lhs); const src_val_lock: ?RegisterLock = switch (src_val) { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (src_val_lock) |reg| self.register_manager.unlockReg(reg); + defer if (src_val_lock) |lock| self.register_manager.unlockReg(lock); const len = try self.resolveInst(extra.rhs); const len_lock: ?RegisterLock = switch (len) { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (len_lock) |reg| self.register_manager.unlockReg(reg); + defer if (len_lock) |lock| self.register_manager.unlockReg(lock); try self.genInlineMemset(dst_ptr, src_val, len, .{}); @@ -6204,7 +6257,7 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (dst_ptr_lock) |reg| self.register_manager.unlockReg(reg); + defer if (dst_ptr_lock) |lock| self.register_manager.unlockReg(lock); const src_ty = self.air.typeOf(extra.lhs); const src_ptr = try self.resolveInst(extra.lhs); @@ -6212,14 +6265,14 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (src_ptr_lock) |reg| self.register_manager.unlockReg(reg); + defer if (src_ptr_lock) |lock| self.register_manager.unlockReg(lock); const len = try self.resolveInst(extra.rhs); const len_lock: ?RegisterLock = switch (len) { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; - defer if (len_lock) |reg| self.register_manager.unlockReg(reg); + defer if (len_lock) |lock| self.register_manager.unlockReg(lock); // TODO Is this the only condition for pointer dereference for memcpy? const src: MCValue = blk: { @@ -6245,7 +6298,7 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { .register => |reg| self.register_manager.lockReg(reg), else => null, }; - defer if (src_lock) |reg| self.register_manager.unlockReg(reg); + defer if (src_lock) |lock| self.register_manager.unlockReg(lock); try self.genInlineMemcpy(dst_ptr, src, len, .{}); diff --git a/src/register_manager.zig b/src/register_manager.zig index 61f5e173ee..44480cc8a4 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -153,7 +153,7 @@ pub fn RegisterManager( regs: [count]Register, buf: *[count]RegisterLock, ) void { - for (®s) |reg, i| { + for (regs) |reg, i| { buf[i] = self.lockRegAssumeUnused(reg); } } From f57b059e58253af3718c5b17fefc40c47b33e63c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 7 May 2022 13:27:11 +0200 Subject: [PATCH 1403/2031] regalloc: refactor locking multiple registers at once --- src/arch/aarch64/CodeGen.zig | 6 ++---- src/arch/arm/CodeGen.zig | 9 +++------ src/arch/x86_64/CodeGen.zig | 24 ++++++++---------------- src/register_manager.zig | 5 +++-- 4 files changed, 16 insertions(+), 28 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index a56d9beabe..e43cbca1c7 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -2627,8 +2627,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo } else { // TODO optimize the register allocation const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }); - var regs_locks: [4]RegisterLock = undefined; - self.register_manager.lockRegsAssumeUnused(4, regs, ®s_locks); + const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs); defer for (regs_locks) |reg| { self.register_manager.unlockReg(reg); }; @@ -4065,8 +4064,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro // TODO call extern memcpy const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }); - var regs_locks: [5]RegisterLock = undefined; - self.register_manager.lockRegsAssumeUnused(5, regs, ®s_locks); + const regs_locks = self.register_manager.lockRegsAssumeUnused(5, regs); defer for (regs_locks) |reg| { self.register_manager.unlockReg(reg); }; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index cad7cedbb4..8486b0451c 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1548,8 +1548,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); const dest_regs = try self.register_manager.allocRegs(2, .{ null, null }); - var dest_regs_locks: [2]RegisterLock = undefined; - self.register_manager.lockRegsAssumeUnused(2, dest_regs, &dest_regs_locks); + const dest_regs_locks = self.register_manager.lockRegsAssumeUnused(2, dest_regs); defer for (dest_regs_locks) |reg| { self.register_manager.unlockReg(reg); }; @@ -2181,8 +2180,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo } else { // TODO optimize the register allocation const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }); - var regs_locks: [4]RegisterLock = undefined; - self.register_manager.lockRegsAssumeUnused(4, regs, ®s_locks); + const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs); defer for (regs_locks) |reg_locked| { self.register_manager.unlockReg(reg_locked); }; @@ -2285,8 +2283,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); } else { const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }); - var regs_locks: [4]RegisterLock = undefined; - self.register_manager.lockRegsAssumeUnused(4, regs, ®s_locks); + const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs); defer for (regs_locks) |reg| { self.register_manager.unlockReg(reg); }; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5cdc4c9889..ee472eeac8 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1377,8 +1377,7 @@ fn airMul(self: *Self, inst: Air.Inst.Index) !void { // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. try self.register_manager.getReg(.rax, inst); try self.register_manager.getReg(.rdx, null); - var reg_locks: [2]RegisterLock = undefined; - self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); defer for (reg_locks) |reg| { self.register_manager.unlockReg(reg); }; @@ -1495,8 +1494,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. try self.register_manager.getReg(.rax, inst); try self.register_manager.getReg(.rdx, null); - var reg_locks: [2]RegisterLock = undefined; - self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); defer for (reg_locks) |reg| { self.register_manager.unlockReg(reg); }; @@ -1556,8 +1554,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. try self.register_manager.getReg(.rax, null); try self.register_manager.getReg(.rdx, null); - var reg_locks: [2]RegisterLock = undefined; - self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); defer for (reg_locks) |reg| { self.register_manager.unlockReg(reg); }; @@ -1586,8 +1583,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { }; const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }); - var temp_regs_locks: [3]RegisterLock = undefined; - self.register_manager.lockRegsAssumeUnused(3, temp_regs, &temp_regs_locks); + const temp_regs_locks = self.register_manager.lockRegsAssumeUnused(3, temp_regs); defer for (temp_regs_locks) |reg| { self.register_manager.unlockReg(reg); }; @@ -1819,8 +1815,7 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { }; try self.register_manager.getReg(.rax, track_rax); try self.register_manager.getReg(.rdx, null); - var reg_locks: [2]RegisterLock = undefined; - self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); defer for (reg_locks) |reg| { self.register_manager.unlockReg(reg); }; @@ -1893,8 +1888,7 @@ fn airRem(self: *Self, inst: Air.Inst.Index) !void { // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. try self.register_manager.getReg(.rax, null); try self.register_manager.getReg(.rdx, inst); - var reg_locks: [2]RegisterLock = undefined; - self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); defer for (reg_locks) |reg| { self.register_manager.unlockReg(reg); }; @@ -1929,8 +1923,7 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void { // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. try self.register_manager.getReg(.rax, null); try self.register_manager.getReg(.rdx, if (signedness == .unsigned) inst else null); - var reg_locks: [2]RegisterLock = undefined; - self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }, ®_locks); + const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); defer for (reg_locks) |reg| { self.register_manager.unlockReg(reg); }; @@ -5552,8 +5545,7 @@ fn genInlineMemcpy( try self.register_manager.getReg(.rax, null); try self.register_manager.getReg(.rcx, null); - var reg_locks: [2]RegisterLock = undefined; - self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rcx }, ®_locks); + const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rcx }); defer for (reg_locks) |lock| { self.register_manager.unlockReg(lock); }; diff --git a/src/register_manager.zig b/src/register_manager.zig index 44480cc8a4..2c0502e867 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -151,11 +151,12 @@ pub fn RegisterManager( self: *Self, comptime count: comptime_int, regs: [count]Register, - buf: *[count]RegisterLock, - ) void { + ) [count]RegisterLock { + var buf: [count]RegisterLock = undefined; for (regs) |reg, i| { buf[i] = self.lockRegAssumeUnused(reg); } + return buf; } /// Unlocks the register allowing its re-allocation and re-use. From 0c51e703f19d04edbfa26b7243b9bc125b5489a6 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 7 May 2022 14:24:18 +0200 Subject: [PATCH 1404/2031] wasm: `@addWithOverflow` for bitsize 32 --- src/arch/wasm/CodeGen.zig | 82 ++++++++++++++++++++++++++++----------- src/type.zig | 1 + 2 files changed, 60 insertions(+), 23 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index c192cba8d9..306c27a4a0 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1818,11 +1818,11 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro try self.emitWValue(rhs); } const valtype = typeToValtype(ty, self.target); - const abi_size = @intCast(u8, ty.bitSize(self.target)); + const abi_size = @intCast(u8, ty.abiSize(self.target)); const opcode = buildOpcode(.{ .valtype1 = valtype, - .width = abi_size * 8, // use bitsize instead of byte size + .width = abi_size * 8, .op = .store, }); @@ -1853,10 +1853,10 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { // load local's value from memory by its stack position try self.emitWValue(operand); - const abi_size = @intCast(u8, ty.bitSize(self.target)); + const abi_size = @intCast(u8, ty.abiSize(self.target)); const opcode = buildOpcode(.{ .valtype1 = typeToValtype(ty, self.target), - .width = abi_size * 8, // use bitsize instead of byte size + .width = abi_size * 8, .op = .load, .signedness = .unsigned, }); @@ -2106,7 +2106,7 @@ fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) /// Converts a signed integer to its 2's complement form and returns /// an unsigned integer instead. /// Asserts bitsize <= 64 -fn convertTo2Complement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo(@TypeOf(value)).Int.bits) { +fn toTwosComplement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo(@TypeOf(value)).Int.bits) { const T = @TypeOf(value); comptime assert(@typeInfo(T) == .Int); comptime assert(@typeInfo(T).Int.signedness == .signed); @@ -2137,7 +2137,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { const int_info = ty.intInfo(self.target); switch (int_info.signedness) { .signed => switch (int_info.bits) { - 0...32 => return WValue{ .imm32 = @intCast(u32, convertTo2Complement( + 0...32 => return WValue{ .imm32 = @intCast(u32, toTwosComplement( val.toSignedInt(), @intCast(u6, int_info.bits), )) }, @@ -2856,29 +2856,36 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty = self.air.getRefType(ty_op.ty); const operand = try self.resolveInst(ty_op.operand); const ref_ty = self.air.typeOf(ty_op.operand); - const ref_info = ref_ty.intInfo(self.target); - const wanted_info = ty.intInfo(self.target); + if (ty.abiSize(self.target) > 8 or ref_ty.abiSize(self.target) > 8) { + return self.fail("todo Wasm intcast for bitsize > 64", .{}); + } + return self.intcast(operand, ty, ref_ty); +} - const op_bits = toWasmBits(ref_info.bits) orelse - return self.fail("TODO: Wasm intcast integer types of bitsize: {d}", .{ref_info.bits}); - const wanted_bits = toWasmBits(wanted_info.bits) orelse - return self.fail("TODO: Wasm intcast integer types of bitsize: {d}", .{wanted_info.bits}); +/// Upcasts or downcasts an integer based on the given and wanted types, +/// and stores the result in a new operand. +/// Asserts type's bitsize <= 64 +fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue { + const given_info = given.intInfo(self.target); + const wanted_info = wanted.intInfo(self.target); + assert(given_info.bits <= 64); + assert(wanted_info.bits <= 64); - // hot path + const op_bits = toWasmBits(given_info.bits).?; + const wanted_bits = toWasmBits(wanted_info.bits).?; if (op_bits == wanted_bits) return operand; + try self.emitWValue(operand); if (op_bits > 32 and wanted_bits == 32) { - try self.emitWValue(operand); try self.addTag(.i32_wrap_i64); } else if (op_bits == 32 and wanted_bits > 32) { - try self.emitWValue(operand); - try self.addTag(switch (ref_info.signedness) { + try self.addTag(switch (wanted_info.signedness) { .signed => .i64_extend_i32_s, .unsigned => .i64_extend_i32_u, }); } else unreachable; - const result = try self.allocLocal(ty); + const result = try self.allocLocal(wanted); try self.addLabel(.local_set, result.local); return result; } @@ -3983,7 +3990,7 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue const cmp_res = try self.cmp(lhs, rhs, lhs_ty, .lt); try self.emitWValue(cmp_res); try self.addLabel(.local_set, overflow_bit.local); - } else if (int_info.signedness == .signed and op != .shl) { + } else if (int_info.signedness == .signed and op != .shl and op != .mul) { // for overflow, we first check if lhs is > 0 (or lhs < 0 in case of subtraction). If not, we will not overflow. // We first create an outer block, where we handle overflow. // Then we create an inner block, where underflow is handled. @@ -4032,9 +4039,36 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue try self.addLabel(.local_set, tmp_val.local); break :blk tmp_val; } else if (op == .mul) blk: { - if (int_info.signedness == .signed) { - const shift_val = convertTo2Complement(-@intCast(i17, int_info.bits), @intCast(u7, int_info.bits)); - const shift_imm = if (wasm_bits == 32) WValue{ .imm32 = shift_val } else WValue{ .imm64 = shift_val }; + // for 32 & 64 bitsize we calculate overflow + // differently. + if (int_info.bits == 32) { + const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64; + const lhs_upcast = try self.intcast(lhs, lhs_ty, new_ty); + const rhs_upcast = try self.intcast(rhs, lhs_ty, new_ty); + const bin_op = try self.binOp(lhs_upcast, rhs_upcast, new_ty, op); + if (int_info.signedness == .unsigned) { + const shr = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); + const wrap = try self.intcast(shr, new_ty, lhs_ty); + const cmp_res = try self.cmp(wrap, zero, lhs_ty, .neq); + try self.emitWValue(cmp_res); + try self.addLabel(.local_set, overflow_bit.local); + break :blk try self.intcast(bin_op, new_ty, lhs_ty); + } else { + const down_cast = try self.intcast(bin_op, new_ty, lhs_ty); + const shr = try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr); + + const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); + const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty); + const cmp_res = try self.cmp(down_shr_res, shr, lhs_ty, .neq); + try self.emitWValue(cmp_res); + try self.addLabel(.local_set, overflow_bit.local); + break :blk down_cast; + } + } else if (int_info.signedness == .signed) { + const shift_imm = if (wasm_bits == 32) + WValue{ .imm32 = wasm_bits - int_info.bits } + else + WValue{ .imm64 = wasm_bits - int_info.bits }; const lhs_shl = try self.binOp(lhs, shift_imm, lhs_ty, .shl); const lhs_shr = try self.binOp(lhs_shl, shift_imm, lhs_ty, .shr); @@ -4051,8 +4085,10 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue break :blk try self.wrapOperand(bin_op, lhs_ty); } else { const bin_op = try self.binOp(lhs, rhs, lhs_ty, op); - const shift_imm = if (wasm_bits == 32) WValue{ .imm32 = int_info.bits } else WValue{ .imm64 = int_info.bits }; - // const zero = if (wasm_bits == 32) WValue{ .imm32 = 0 } else WValue{ .imm64 = 0 }; + const shift_imm = if (wasm_bits == 32) + WValue{ .imm32 = int_info.bits } + else + WValue{ .imm64 = int_info.bits }; const shr = try self.binOp(bin_op, shift_imm, lhs_ty, .shr); const cmp_op = try self.cmp(shr, zero, lhs_ty, .neq); try self.emitWValue(cmp_op); diff --git a/src/type.zig b/src/type.zig index ddeec596e1..71b04acebb 100644 --- a/src/type.zig +++ b/src/type.zig @@ -5952,6 +5952,7 @@ pub const Type = extern union { pub const @"u64" = initTag(.u64); pub const @"i32" = initTag(.i32); + pub const @"i64" = initTag(.i64); pub const @"f16" = initTag(.f16); pub const @"f32" = initTag(.f32); From ad4f0dda8b8c270594ed24a27c808f8bd43924bf Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 7 May 2022 17:04:19 +0200 Subject: [PATCH 1405/2031] wasm: Fix `@floatToInt` and split overflow ops As we now store negative signed integers as two's complement, we must also ensure that when truncating a float, its value is wrapped around the integer's size. This also splits `@mulWithOverflow` into its own function to make the code more maintainable and reduce branching. --- src/arch/wasm/CodeGen.zig | 260 ++++++++++++++++++-------------------- 1 file changed, 120 insertions(+), 140 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 306c27a4a0..9318b4ecca 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1424,7 +1424,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .add_with_overflow => self.airBinOpOverflow(inst, .add), .sub_with_overflow => self.airBinOpOverflow(inst, .sub), .shl_with_overflow => self.airBinOpOverflow(inst, .shl), - .mul_with_overflow => self.airBinOpOverflow(inst, .mul), + .mul_with_overflow => self.airMulWithOverflow(inst), .clz => self.airClz(inst), .ctz => self.airCtz(inst), @@ -1927,7 +1927,14 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - return self.wrapBinOp(lhs, rhs, self.air.typeOf(bin_op.lhs), op); + const ty = self.air.typeOf(bin_op.lhs); + if (ty.zigTypeTag() == .Vector) { + return self.fail("TODO: Implement wrapping arithmetic for vectors", .{}); + } else if (ty.abiSize(self.target) > 8) { + return self.fail("TODO: Implement wrapping arithmetic for bitsize > 64", .{}); + } + + return self.wrapBinOp(lhs, rhs, ty, op); } fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { @@ -1941,31 +1948,8 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError }); try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); const bin_local = try self.allocLocal(ty); - - const int_info = ty.intInfo(self.target); - const bitsize = int_info.bits; - const is_signed = int_info.signedness == .signed; - // if target type bitsize is x < 32 and 32 > x < 64, we perform - // result & ((1< 64) { - return self.fail("TODO wasm: Integer wrapping for bitsizes larger than 64", .{}); - } - - // save the result in a temporary try self.addLabel(.local_set, bin_local.local); - return bin_local; + return self.wrapOperand(bin_local, ty); } /// Wraps an operand based on a given type's bitsize. @@ -2855,11 +2839,12 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const ty = self.air.getRefType(ty_op.ty); const operand = try self.resolveInst(ty_op.operand); - const ref_ty = self.air.typeOf(ty_op.operand); - if (ty.abiSize(self.target) > 8 or ref_ty.abiSize(self.target) > 8) { + const operand_ty = self.air.typeOf(ty_op.operand); + if (ty.abiSize(self.target) > 8 or operand_ty.abiSize(self.target) > 8) { return self.fail("todo Wasm intcast for bitsize > 64", .{}); } - return self.intcast(operand, ty, ref_ty); + + return self.intcast(operand, operand_ty, ty); } /// Upcasts or downcasts an integer based on the given and wanted types, @@ -3102,63 +3087,17 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { - if (self.liveness.isUnused(inst)) return WValue.none; + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); - const op_ty = self.air.typeOf(ty_op.operand); - const int_info = self.air.getRefType(ty_op.ty).intInfo(self.target); + const wanted_ty = self.air.getRefType(ty_op.ty); + const int_info = wanted_ty.intInfo(self.target); const wanted_bits = int_info.bits; - const result = try self.allocLocal(self.air.getRefType(ty_op.ty)); - const op_bits = op_ty.intInfo(self.target).bits; - const wasm_bits = toWasmBits(wanted_bits) orelse + _ = toWasmBits(wanted_bits) orelse { return self.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{wanted_bits}); - - // Use wasm's instruction to wrap from 64bit to 32bit integer when possible - if (op_bits == 64 and wanted_bits == 32) { - try self.emitWValue(operand); - try self.addTag(.i32_wrap_i64); - try self.addLabel(.local_set, result.local); - return result; - } - - // Any other truncation must be done manually - if (int_info.signedness == .unsigned) { - const mask = (@as(u65, 1) << @intCast(u7, wanted_bits)) - 1; - try self.emitWValue(operand); - switch (wasm_bits) { - 32 => { - try self.addImm32(@bitCast(i32, @intCast(u32, mask))); - try self.addTag(.i32_and); - }, - 64 => { - try self.addImm64(@intCast(u64, mask)); - try self.addTag(.i64_and); - }, - else => unreachable, - } - } else { - const shift_bits = wasm_bits - wanted_bits; - try self.emitWValue(operand); - switch (wasm_bits) { - 32 => { - try self.addImm32(@bitCast(i16, shift_bits)); - try self.addTag(.i32_shl); - try self.addImm32(@bitCast(i16, shift_bits)); - try self.addTag(.i32_shr_s); - }, - 64 => { - try self.addImm64(shift_bits); - try self.addTag(.i64_shl); - try self.addImm64(shift_bits); - try self.addTag(.i64_shr_s); - }, - else => unreachable, - } - } - - try self.addLabel(.local_set, result.local); - return result; + }; + return self.wrapOperand(operand, wanted_ty); } fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -3448,7 +3387,8 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const result = try self.allocLocal(dest_ty); try self.addLabel(.local_set, result.local); - return result; + + return self.wrapOperand(result, dest_ty); } fn airIntToFloat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -3952,6 +3892,10 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue const rhs = try self.resolveInst(extra.rhs); const lhs_ty = self.air.typeOf(extra.lhs); + if (lhs_ty.zigTypeTag() == .Vector) { + return self.fail("TODO: Implement overflow arithmetic for vectors", .{}); + } + // We store the bit if it's overflowed or not in this. As it's zero-initialized // we only need to update it if an overflow (or underflow) occured. const overflow_bit = try self.allocLocal(Type.initTag(.u1)); @@ -3990,7 +3934,7 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue const cmp_res = try self.cmp(lhs, rhs, lhs_ty, .lt); try self.emitWValue(cmp_res); try self.addLabel(.local_set, overflow_bit.local); - } else if (int_info.signedness == .signed and op != .shl and op != .mul) { + } else if (int_info.signedness == .signed and op != .shl) { // for overflow, we first check if lhs is > 0 (or lhs < 0 in case of subtraction). If not, we will not overflow. // We first create an outer block, where we handle overflow. // Then we create an inner block, where underflow is handled. @@ -4038,63 +3982,6 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue } try self.addLabel(.local_set, tmp_val.local); break :blk tmp_val; - } else if (op == .mul) blk: { - // for 32 & 64 bitsize we calculate overflow - // differently. - if (int_info.bits == 32) { - const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64; - const lhs_upcast = try self.intcast(lhs, lhs_ty, new_ty); - const rhs_upcast = try self.intcast(rhs, lhs_ty, new_ty); - const bin_op = try self.binOp(lhs_upcast, rhs_upcast, new_ty, op); - if (int_info.signedness == .unsigned) { - const shr = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); - const wrap = try self.intcast(shr, new_ty, lhs_ty); - const cmp_res = try self.cmp(wrap, zero, lhs_ty, .neq); - try self.emitWValue(cmp_res); - try self.addLabel(.local_set, overflow_bit.local); - break :blk try self.intcast(bin_op, new_ty, lhs_ty); - } else { - const down_cast = try self.intcast(bin_op, new_ty, lhs_ty); - const shr = try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr); - - const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); - const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty); - const cmp_res = try self.cmp(down_shr_res, shr, lhs_ty, .neq); - try self.emitWValue(cmp_res); - try self.addLabel(.local_set, overflow_bit.local); - break :blk down_cast; - } - } else if (int_info.signedness == .signed) { - const shift_imm = if (wasm_bits == 32) - WValue{ .imm32 = wasm_bits - int_info.bits } - else - WValue{ .imm64 = wasm_bits - int_info.bits }; - - const lhs_shl = try self.binOp(lhs, shift_imm, lhs_ty, .shl); - const lhs_shr = try self.binOp(lhs_shl, shift_imm, lhs_ty, .shr); - const rhs_shl = try self.binOp(rhs, shift_imm, lhs_ty, .shl); - const rhs_shr = try self.binOp(rhs_shl, shift_imm, lhs_ty, .shr); - - const bin_op = try self.binOp(lhs_shr, rhs_shr, lhs_ty, op); - const shl = try self.binOp(bin_op, shift_imm, lhs_ty, .shl); - const shr = try self.binOp(shl, shift_imm, lhs_ty, .shr); - - const cmp_op = try self.cmp(shr, bin_op, lhs_ty, .neq); - try self.emitWValue(cmp_op); - try self.addLabel(.local_set, overflow_bit.local); - break :blk try self.wrapOperand(bin_op, lhs_ty); - } else { - const bin_op = try self.binOp(lhs, rhs, lhs_ty, op); - const shift_imm = if (wasm_bits == 32) - WValue{ .imm32 = int_info.bits } - else - WValue{ .imm64 = int_info.bits }; - const shr = try self.binOp(bin_op, shift_imm, lhs_ty, .shr); - const cmp_op = try self.cmp(shr, zero, lhs_ty, .neq); - try self.emitWValue(cmp_op); - try self.addLabel(.local_set, overflow_bit.local); - break :blk try self.wrapOperand(bin_op, lhs_ty); - } } else try self.wrapBinOp(lhs, rhs, lhs_ty, op); const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); @@ -4105,6 +3992,99 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue return result_ptr; } +fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; + const lhs = try self.resolveInst(extra.lhs); + const rhs = try self.resolveInst(extra.rhs); + const lhs_ty = self.air.typeOf(extra.lhs); + + if (lhs_ty.zigTypeTag() == .Vector) { + return self.fail("TODO: Implement overflow arithmetic for vectors", .{}); + } + + // We store the bit if it's overflowed or not in this. As it's zero-initialized + // we only need to update it if an overflow (or underflow) occured. + const overflow_bit = try self.allocLocal(Type.initTag(.u1)); + const int_info = lhs_ty.intInfo(self.target); + const wasm_bits = toWasmBits(int_info.bits) orelse { + return self.fail("TODO: Implement overflow arithmetic for integer bitsize: {d}", .{int_info.bits}); + }; + + if (wasm_bits == 64) { + return self.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits}); + } + + const zero = switch (wasm_bits) { + 32 => WValue{ .imm32 = 0 }, + 64 => WValue{ .imm64 = 0 }, + else => unreachable, + }; + + // for 32 bit integers we upcast it to a 64bit integer + const bin_op = if (int_info.bits == 32) blk: { + const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64; + const lhs_upcast = try self.intcast(lhs, lhs_ty, new_ty); + const rhs_upcast = try self.intcast(rhs, lhs_ty, new_ty); + const bin_op = try self.binOp(lhs_upcast, rhs_upcast, new_ty, .mul); + if (int_info.signedness == .unsigned) { + const shr = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); + const wrap = try self.intcast(shr, new_ty, lhs_ty); + const cmp_res = try self.cmp(wrap, zero, lhs_ty, .neq); + try self.emitWValue(cmp_res); + try self.addLabel(.local_set, overflow_bit.local); + break :blk try self.intcast(bin_op, new_ty, lhs_ty); + } else { + const down_cast = try self.intcast(bin_op, new_ty, lhs_ty); + const shr = try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr); + + const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); + const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty); + const cmp_res = try self.cmp(down_shr_res, shr, lhs_ty, .neq); + try self.emitWValue(cmp_res); + try self.addLabel(.local_set, overflow_bit.local); + break :blk down_cast; + } + } else if (int_info.signedness == .signed) blk: { + const shift_imm = if (wasm_bits == 32) + WValue{ .imm32 = wasm_bits - int_info.bits } + else + WValue{ .imm64 = wasm_bits - int_info.bits }; + + const lhs_shl = try self.binOp(lhs, shift_imm, lhs_ty, .shl); + const lhs_shr = try self.binOp(lhs_shl, shift_imm, lhs_ty, .shr); + const rhs_shl = try self.binOp(rhs, shift_imm, lhs_ty, .shl); + const rhs_shr = try self.binOp(rhs_shl, shift_imm, lhs_ty, .shr); + + const bin_op = try self.binOp(lhs_shr, rhs_shr, lhs_ty, .mul); + const shl = try self.binOp(bin_op, shift_imm, lhs_ty, .shl); + const shr = try self.binOp(shl, shift_imm, lhs_ty, .shr); + + const cmp_op = try self.cmp(shr, bin_op, lhs_ty, .neq); + try self.emitWValue(cmp_op); + try self.addLabel(.local_set, overflow_bit.local); + break :blk try self.wrapOperand(bin_op, lhs_ty); + } else blk: { + const bin_op = try self.binOp(lhs, rhs, lhs_ty, .mul); + const shift_imm = if (wasm_bits == 32) + WValue{ .imm32 = int_info.bits } + else + WValue{ .imm64 = int_info.bits }; + const shr = try self.binOp(bin_op, shift_imm, lhs_ty, .shr); + const cmp_op = try self.cmp(shr, zero, lhs_ty, .neq); + try self.emitWValue(cmp_op); + try self.addLabel(.local_set, overflow_bit.local); + break :blk try self.wrapOperand(bin_op, lhs_ty); + }; + + const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); + try self.store(result_ptr, bin_op, lhs_ty, 0); + const offset = @intCast(u32, lhs_ty.abiSize(self.target)); + try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset); + + return result_ptr; +} + fn airMaxMin(self: *Self, inst: Air.Inst.Index, op: enum { max, min }) InnerError!WValue { if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; const bin_op = self.air.instructions.items(.data)[inst].bin_op; From a11097958271562fe7e64716356c07d9996fad5f Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 7 May 2022 17:09:09 +0200 Subject: [PATCH 1406/2031] stage2: Split `@mulWithOverflow` tests --- test/behavior/math.zig | 7 ++++++- test/cases/binary_operands.13.zig | 2 +- test/cases/binary_operands.2.zig | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 760dece01e..42f2635afd 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -687,7 +687,6 @@ test "basic @mulWithOverflow" { test "extensive @mulWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO { var a: u5 = 3; @@ -833,6 +832,12 @@ test "extensive @mulWithOverflow" { try expect(@mulWithOverflow(i32, a, b, &res)); try expect(res == 0x7fffffff); } +} + +test "@mulWithOverflow bitsize > 32" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO { var a: u62 = 3; diff --git a/test/cases/binary_operands.13.zig b/test/cases/binary_operands.13.zig index e62107b47e..d626059e10 100644 --- a/test/cases/binary_operands.13.zig +++ b/test/cases/binary_operands.13.zig @@ -1,6 +1,6 @@ pub fn main() void { var i: i4 = 3; - if (i *% 3 != 1) unreachable; + if (i *% 3 != -7) unreachable; return; } diff --git a/test/cases/binary_operands.2.zig b/test/cases/binary_operands.2.zig index 7407ed7d95..78c10f56b7 100644 --- a/test/cases/binary_operands.2.zig +++ b/test/cases/binary_operands.2.zig @@ -1,6 +1,6 @@ pub fn main() void { var i: i4 = 7; - if (i +% 1 != 0) unreachable; + if (i +% 1 != -8) unreachable; return; } From 756ddf092562acb8b552c31ad458b9ae1754e77c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 7 May 2022 22:35:36 +0200 Subject: [PATCH 1407/2031] arm: fix CF flags spilling and implement genSetStack for reg with overflow flags --- src/arch/arm/CodeGen.zig | 56 ++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 8486b0451c..51f287f3d0 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -898,16 +898,16 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void fn spillCompareFlagsIfOccupied(self: *Self) !void { if (self.compare_flags_inst) |inst_to_save| { const mcv = self.getResolvedInstValue(inst_to_save); - switch (mcv) { + const new_mcv = switch (mcv) { .compare_flags_signed, .compare_flags_unsigned, + => try self.allocRegOrMem(inst_to_save, true), .register_c_flag, .register_v_flag, - => {}, + => try self.allocRegOrMem(inst_to_save, false), else => unreachable, // mcv doesn't occupy the compare flags - } + }; - const new_mcv = try self.allocRegOrMem(inst_to_save, true); try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); @@ -915,6 +915,15 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); self.compare_flags_inst = null; + + // TODO consolidate with register manager and spillInstruction + // this call should really belong in the register manager! + switch (mcv) { + .register_c_flag, + .register_v_flag, + => |reg| self.register_manager.freeReg(reg), + else => {}, + } } } @@ -1972,8 +1981,8 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { .register => |reg| reg, else => try self.copyToTmpRegister(Type.usize, index_mcv), }; - const index_reg_lock = self.register_manager.lockRegAssumeUnused(index_reg); - defer self.register_manager.unlockReg(index_reg_lock); + const index_reg_lock = self.register_manager.lockReg(index_reg); + defer if (index_reg_lock) |lock| self.register_manager.unlockReg(lock); const tag: Mir.Inst.Tag = switch (elem_size) { 1 => .ldrb, @@ -3677,6 +3686,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { const parent_compare_flags_inst = self.compare_flags_inst; try self.branch_stack.append(.{}); + errdefer { + _ = self.branch_stack.pop(); + } try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len); for (liveness_condbr.then_deaths) |operand| { @@ -4285,8 +4297,36 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro }, .register_c_flag, .register_v_flag, - => { - return self.fail("TODO implement genSetStack {}", .{mcv}); + => |reg| { + const reg_lock = self.register_manager.lockReg(reg); + defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg); + + const wrapped_ty = ty.structFieldType(0); + try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg }); + + const overflow_bit_ty = ty.structFieldType(1); + const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*)); + const cond_reg = try self.register_manager.allocReg(null); + + // C flag: movcs reg, #1 + // V flag: movvs reg, #1 + _ = try self.addInst(.{ + .tag = .mov, + .cond = switch (mcv) { + .register_c_flag => .cs, + .register_v_flag => .vs, + else => unreachable, + }, + .data = .{ .rr_op = .{ + .rd = cond_reg, + .rn = .r0, + .op = Instruction.Operand.fromU32(1).?, + } }, + }); + + try self.genSetStack(overflow_bit_ty, stack_offset - overflow_bit_offset, .{ + .register = cond_reg, + }); }, .memory, .stack_argument_offset, From 6bf67eada47b7c6f4819a759268503658c97e9ec Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 7 May 2022 22:52:11 +0200 Subject: [PATCH 1408/2031] arm: lock dest register in shl_overflow so that we do not spill it Nerf two tests - they will require further investigation, but arm now passes all tests with the safety PR. --- src/arch/arm/CodeGen.zig | 3 +++ test/behavior/align.zig | 1 + test/behavior/byval_arg_var.zig | 1 + 3 files changed, 5 insertions(+) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 51f287f3d0..02ca66f297 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1672,6 +1672,9 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { // lsl dest, lhs, rhs const dest = try self.binOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty); + const dest_reg = dest.register; + const dest_lock = self.register_manager.lockRegAssumeUnused(dest_reg); + defer self.register_manager.unlockReg(dest_lock); // asr/lsr reconstructed, dest, rhs const reconstructed = try self.binOp(.shr, null, dest, rhs, lhs_ty, rhs_ty); diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 563f937822..6c1122323a 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -8,6 +8,7 @@ var foo: u8 align(4) = 100; test "global variable alignment" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO comptime try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4); comptime try expect(@TypeOf(&foo) == *align(4) u8); diff --git a/test/behavior/byval_arg_var.zig b/test/behavior/byval_arg_var.zig index b6b972d2d3..d2e8ecb638 100644 --- a/test/behavior/byval_arg_var.zig +++ b/test/behavior/byval_arg_var.zig @@ -6,6 +6,7 @@ var result: []const u8 = "wrong"; test "pass string literal byvalue to a generic var param" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; start(); blowUpStack(10); From aa05cd48097b42d0c52ff5e0f3dd1ece68d1cdcd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 7 May 2022 17:43:40 -0700 Subject: [PATCH 1409/2031] CI: fix universal libc step name This was a merge conflict that went undetected. --- ci/azure/macos_script | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/azure/macos_script b/ci/azure/macos_script index e8c7c69a33..36c034d871 100755 --- a/ci/azure/macos_script +++ b/ci/azure/macos_script @@ -65,7 +65,7 @@ stage2/bin/zig build test-behavior release/bin/zig build test-behavior -Denable-macos-sdk -Domit-stage2 release/bin/zig build test-compiler-rt -Denable-macos-sdk release/bin/zig build test-std -Denable-macos-sdk -release/bin/zig build test-minilibc -Denable-macos-sdk +release/bin/zig build test-universal-libc -Denable-macos-sdk release/bin/zig build test-compare-output -Denable-macos-sdk release/bin/zig build test-standalone -Denable-macos-sdk release/bin/zig build test-stack-traces -Denable-macos-sdk From 663b67783e3e140fcaf06747f4aa28b57259023c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 7 May 2022 17:41:20 -0700 Subject: [PATCH 1410/2031] compiler-rt: avoid symbol collisions with Windows libc closes #11600 --- lib/compiler_rt.zig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig index 638bbcc695..2ed0bdc520 100644 --- a/lib/compiler_rt.zig +++ b/lib/compiler_rt.zig @@ -724,24 +724,24 @@ comptime { @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage }); } - mathExport("ceil", @import("./compiler_rt/ceil.zig"), true); + mathExport("ceil", @import("./compiler_rt/ceil.zig"), false); mathExport("cos", @import("./compiler_rt/cos.zig"), true); mathExport("exp", @import("./compiler_rt/exp.zig"), true); mathExport("exp2", @import("./compiler_rt/exp2.zig"), true); mathExport("fabs", @import("./compiler_rt/fabs.zig"), true); - mathExport("floor", @import("./compiler_rt/floor.zig"), true); + mathExport("floor", @import("./compiler_rt/floor.zig"), false); mathExport("fma", @import("./compiler_rt/fma.zig"), true); mathExport("fmax", @import("./compiler_rt/fmax.zig"), true); mathExport("fmin", @import("./compiler_rt/fmin.zig"), true); mathExport("fmod", @import("./compiler_rt/fmod.zig"), true); mathExport("log", @import("./compiler_rt/log.zig"), true); - mathExport("log10", @import("./compiler_rt/log10.zig"), true); + mathExport("log10", @import("./compiler_rt/log10.zig"), false); mathExport("log2", @import("./compiler_rt/log2.zig"), true); mathExport("round", @import("./compiler_rt/round.zig"), true); mathExport("sin", @import("./compiler_rt/sin.zig"), true); mathExport("sincos", @import("./compiler_rt/sincos.zig"), true); mathExport("sqrt", @import("./compiler_rt/sqrt.zig"), true); - mathExport("tan", @import("./compiler_rt/tan.zig"), false); + mathExport("tan", @import("./compiler_rt/tan.zig"), true); mathExport("trunc", @import("./compiler_rt/trunc.zig"), true); if (arch.isSPARC()) { @@ -825,7 +825,7 @@ comptime { } } -inline fn mathExport(double_name: []const u8, comptime import: type, is_standard: bool) void { +inline fn mathExport(double_name: []const u8, comptime import: type, win_libc_has_it: bool) void { const half_name = "__" ++ double_name ++ "h"; const half_fn = @field(import, half_name); const float_name = double_name ++ "f"; @@ -855,7 +855,7 @@ inline fn mathExport(double_name: []const u8, comptime import: type, is_standard // Weak aliases don't work on Windows, so we avoid exporting the `l` alias // on this platform for functions we know will collide. - if (builtin.os.tag != .windows or !builtin.link_libc or !is_standard) { + if (builtin.os.tag != .windows or !builtin.link_libc or !win_libc_has_it) { inline for (pairs) |pair| { const F = pair[0]; const func = pair[1]; @@ -865,7 +865,7 @@ inline fn mathExport(double_name: []const u8, comptime import: type, is_standard } } - if (is_ppc and is_standard) { + if (is_ppc) { // LLVM PPC backend lowers f128 ops with the suffix `f128` instead of `l`. @export(quad_fn, .{ .name = double_name ++ "f128", .linkage = linkage }); } From 6fde2fcd519cacdc44871fc1035b05afee072f44 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 8 May 2022 13:05:16 -0700 Subject: [PATCH 1411/2031] allow in-memory coercion of differently-named floats with same bits For example, this allows passing a `*c_longdouble` where a `*f80` is expected, provided that `c_longdouble` maps to `f80` for this target. --- src/Sema.zig | 9 +++++++++ src/stage1/ir.cpp | 6 ++++++ test/behavior/cast.zig | 20 ++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index 76edfbf2cd..2e7e5c9293 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -18673,6 +18673,15 @@ fn coerceInMemoryAllowed( } } + // Differently-named floats with the same number of bits. + if (dest_ty.zigTypeTag() == .Float and src_ty.zigTypeTag() == .Float) { + const dest_bits = dest_ty.floatBits(target); + const src_bits = src_ty.floatBits(target); + if (dest_bits == src_bits) { + return .ok; + } + } + // Pointers / Pointer-like Optionals var dest_buf: Type.Payload.ElemType = undefined; var src_buf: Type.Payload.ElemType = undefined; diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index f7ab5e12fa..ee195680cd 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -4480,6 +4480,12 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted return result; } + if (wanted_type->id == ZigTypeIdFloat && actual_type->id == ZigTypeIdFloat) { + if (wanted_type->data.floating.bit_count == actual_type->data.floating.bit_count) { + return result; + } + } + if (wanted_type->id == ZigTypeIdVector && actual_type->id == ZigTypeIdVector) { if (actual_type->data.vector.len != wanted_type->data.vector.len) { result.id = ConstCastResultIdVectorLength; diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 416c7914e7..231fa25b2d 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1426,3 +1426,23 @@ test "pointer to empty struct literal to mutable slice" { var x: []i32 = &.{}; try expect(x.len == 0); } + +test "coerce between pointers of compatible differently-named floats" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const F = switch (@typeInfo(c_longdouble).Float.bits) { + 16 => f16, + 32 => f32, + 64 => f64, + 80 => f80, + 128 => f128, + else => @compileError("unreachable"), + }; + var f1: F = 12.34; + var f2: *c_longdouble = &f1; + f2.* += 1; + try expect(f1 == @as(F, 12.34) + 1); +} From cd019ee502d6792c1615b0fab10827db419beab4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 8 May 2022 13:06:21 -0700 Subject: [PATCH 1412/2031] compiler_rt: avoid weak aliases on Windows When exporting math functions for Windows, we provide weak exports of 'l' variants rather than weak aliases. We still use aliases on other operating systems so that the 'l' variants have one less jump instruction in this case. --- lib/compiler_rt.zig | 49 ++++++++++++++++++++------------------ lib/compiler_rt/ceil.zig | 11 +++++++++ lib/compiler_rt/cos.zig | 11 +++++++++ lib/compiler_rt/exp.zig | 11 +++++++++ lib/compiler_rt/exp2.zig | 11 +++++++++ lib/compiler_rt/fabs.zig | 11 +++++++++ lib/compiler_rt/floor.zig | 11 +++++++++ lib/compiler_rt/fma.zig | 11 +++++++++ lib/compiler_rt/fmax.zig | 11 +++++++++ lib/compiler_rt/fmin.zig | 11 +++++++++ lib/compiler_rt/fmod.zig | 11 +++++++++ lib/compiler_rt/log.zig | 11 +++++++++ lib/compiler_rt/log10.zig | 11 +++++++++ lib/compiler_rt/log2.zig | 11 +++++++++ lib/compiler_rt/round.zig | 11 +++++++++ lib/compiler_rt/sin.zig | 11 +++++++++ lib/compiler_rt/sincos.zig | 11 +++++++++ lib/compiler_rt/sqrt.zig | 11 +++++++++ lib/compiler_rt/tan.zig | 11 +++++++++ lib/compiler_rt/trunc.zig | 11 +++++++++ 20 files changed, 235 insertions(+), 23 deletions(-) diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig index 2ed0bdc520..fdf5940702 100644 --- a/lib/compiler_rt.zig +++ b/lib/compiler_rt.zig @@ -724,25 +724,25 @@ comptime { @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage }); } - mathExport("ceil", @import("./compiler_rt/ceil.zig"), false); - mathExport("cos", @import("./compiler_rt/cos.zig"), true); - mathExport("exp", @import("./compiler_rt/exp.zig"), true); - mathExport("exp2", @import("./compiler_rt/exp2.zig"), true); - mathExport("fabs", @import("./compiler_rt/fabs.zig"), true); - mathExport("floor", @import("./compiler_rt/floor.zig"), false); - mathExport("fma", @import("./compiler_rt/fma.zig"), true); - mathExport("fmax", @import("./compiler_rt/fmax.zig"), true); - mathExport("fmin", @import("./compiler_rt/fmin.zig"), true); - mathExport("fmod", @import("./compiler_rt/fmod.zig"), true); - mathExport("log", @import("./compiler_rt/log.zig"), true); - mathExport("log10", @import("./compiler_rt/log10.zig"), false); - mathExport("log2", @import("./compiler_rt/log2.zig"), true); - mathExport("round", @import("./compiler_rt/round.zig"), true); - mathExport("sin", @import("./compiler_rt/sin.zig"), true); - mathExport("sincos", @import("./compiler_rt/sincos.zig"), true); - mathExport("sqrt", @import("./compiler_rt/sqrt.zig"), true); - mathExport("tan", @import("./compiler_rt/tan.zig"), true); - mathExport("trunc", @import("./compiler_rt/trunc.zig"), true); + mathExport("ceil", @import("./compiler_rt/ceil.zig")); + mathExport("cos", @import("./compiler_rt/cos.zig")); + mathExport("exp", @import("./compiler_rt/exp.zig")); + mathExport("exp2", @import("./compiler_rt/exp2.zig")); + mathExport("fabs", @import("./compiler_rt/fabs.zig")); + mathExport("floor", @import("./compiler_rt/floor.zig")); + mathExport("fma", @import("./compiler_rt/fma.zig")); + mathExport("fmax", @import("./compiler_rt/fmax.zig")); + mathExport("fmin", @import("./compiler_rt/fmin.zig")); + mathExport("fmod", @import("./compiler_rt/fmod.zig")); + mathExport("log", @import("./compiler_rt/log.zig")); + mathExport("log10", @import("./compiler_rt/log10.zig")); + mathExport("log2", @import("./compiler_rt/log2.zig")); + mathExport("round", @import("./compiler_rt/round.zig")); + mathExport("sin", @import("./compiler_rt/sin.zig")); + mathExport("sincos", @import("./compiler_rt/sincos.zig")); + mathExport("sqrt", @import("./compiler_rt/sqrt.zig")); + mathExport("tan", @import("./compiler_rt/tan.zig")); + mathExport("trunc", @import("./compiler_rt/trunc.zig")); if (arch.isSPARC()) { // SPARC systems use a different naming scheme @@ -825,7 +825,7 @@ comptime { } } -inline fn mathExport(double_name: []const u8, comptime import: type, win_libc_has_it: bool) void { +inline fn mathExport(double_name: []const u8, comptime import: type) void { const half_name = "__" ++ double_name ++ "h"; const half_fn = @field(import, half_name); const float_name = double_name ++ "f"; @@ -853,9 +853,12 @@ inline fn mathExport(double_name: []const u8, comptime import: type, win_libc_ha .{ f128, quad_fn }, }; - // Weak aliases don't work on Windows, so we avoid exporting the `l` alias - // on this platform for functions we know will collide. - if (builtin.os.tag != .windows or !builtin.link_libc or !win_libc_has_it) { + if (builtin.os.tag == .windows) { + // Weak aliases don't work on Windows, so we have to provide the 'l' variants + // as additional function definitions that jump to the real definition. + const long_double_fn = @field(import, long_double_name); + @export(long_double_fn, .{ .name = long_double_name, .linkage = linkage }); + } else { inline for (pairs) |pair| { const F = pair[0]; const func = pair[1]; diff --git a/lib/compiler_rt/ceil.zig b/lib/compiler_rt/ceil.zig index c7087a2c3a..06020ea8f8 100644 --- a/lib/compiler_rt/ceil.zig +++ b/lib/compiler_rt/ceil.zig @@ -111,6 +111,17 @@ pub fn ceilq(x: f128) callconv(.C) f128 { } } +pub fn ceill(x: c_longdouble) callconv(.C) c_longdouble { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __ceilh(x), + 32 => return ceilf(x), + 64 => return ceil(x), + 80 => return __ceilx(x), + 128 => return ceilq(x), + else => @compileError("unreachable"), + } +} + test "ceil32" { try expect(ceilf(1.3) == 2.0); try expect(ceilf(-1.3) == -1.0); diff --git a/lib/compiler_rt/cos.zig b/lib/compiler_rt/cos.zig index 957e5f9c91..e01f458243 100644 --- a/lib/compiler_rt/cos.zig +++ b/lib/compiler_rt/cos.zig @@ -107,6 +107,17 @@ pub fn cosq(a: f128) callconv(.C) f128 { return cos(@floatCast(f64, a)); } +pub fn cosl(x: c_longdouble) callconv(.C) c_longdouble { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __cosh(x), + 32 => return cosf(x), + 64 => return cos(x), + 80 => return __cosx(x), + 128 => return cosq(x), + else => @compileError("unreachable"), + } +} + test "cos32" { const epsilon = 0.00001; diff --git a/lib/compiler_rt/exp.zig b/lib/compiler_rt/exp.zig index 0f129dfd4c..a2c5d0e550 100644 --- a/lib/compiler_rt/exp.zig +++ b/lib/compiler_rt/exp.zig @@ -182,6 +182,17 @@ pub fn expq(a: f128) callconv(.C) f128 { return exp(@floatCast(f64, a)); } +pub fn expl(x: c_longdouble) callconv(.C) c_longdouble { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __exph(x), + 32 => return expf(x), + 64 => return exp(x), + 80 => return __expx(x), + 128 => return expq(x), + else => @compileError("unreachable"), + } +} + test "exp32" { const epsilon = 0.000001; diff --git a/lib/compiler_rt/exp2.zig b/lib/compiler_rt/exp2.zig index 53432a831d..cbcb53c99f 100644 --- a/lib/compiler_rt/exp2.zig +++ b/lib/compiler_rt/exp2.zig @@ -149,6 +149,17 @@ pub fn exp2q(x: f128) callconv(.C) f128 { return exp2(@floatCast(f64, x)); } +pub fn exp2l(x: c_longdouble) callconv(.C) c_longdouble { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __exp2h(x), + 32 => return exp2f(x), + 64 => return exp2(x), + 80 => return __exp2x(x), + 128 => return exp2q(x), + else => @compileError("unreachable"), + } +} + const exp2ft = [_]f64{ 0x1.6a09e667f3bcdp-1, 0x1.7a11473eb0187p-1, diff --git a/lib/compiler_rt/fabs.zig b/lib/compiler_rt/fabs.zig index fbef81fc9a..396fdd46b7 100644 --- a/lib/compiler_rt/fabs.zig +++ b/lib/compiler_rt/fabs.zig @@ -20,6 +20,17 @@ pub fn fabsq(a: f128) callconv(.C) f128 { return generic_fabs(a); } +pub fn fabsl(x: c_longdouble) callconv(.C) c_longdouble { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __fabsh(x), + 32 => return fabsf(x), + 64 => return fabs(x), + 80 => return __fabsx(x), + 128 => return fabsq(x), + else => @compileError("unreachable"), + } +} + inline fn generic_fabs(x: anytype) @TypeOf(x) { const T = @TypeOf(x); const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); diff --git a/lib/compiler_rt/floor.zig b/lib/compiler_rt/floor.zig index f6df164b58..783898fca7 100644 --- a/lib/compiler_rt/floor.zig +++ b/lib/compiler_rt/floor.zig @@ -141,6 +141,17 @@ pub fn floorq(x: f128) callconv(.C) f128 { } } +pub fn floorl(x: c_longdouble) callconv(.C) c_longdouble { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __floorh(x), + 32 => return floorf(x), + 64 => return floor(x), + 80 => return __floorx(x), + 128 => return floorq(x), + else => @compileError("unreachable"), + } +} + test "floor16" { try expect(__floorh(1.3) == 1.0); try expect(__floorh(-1.3) == -2.0); diff --git a/lib/compiler_rt/fma.zig b/lib/compiler_rt/fma.zig index 4c603bf095..7a39a4c9a0 100644 --- a/lib/compiler_rt/fma.zig +++ b/lib/compiler_rt/fma.zig @@ -135,6 +135,17 @@ pub fn fmaq(x: f128, y: f128, z: f128) callconv(.C) f128 { } } +pub fn fmal(x: c_longdouble, y: c_longdouble, z: c_longdouble) callconv(.C) c_longdouble { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __fmah(x, y, z), + 32 => return fmaf(x, y, z), + 64 => return fma(x, y, z), + 80 => return __fmax(x, y, z), + 128 => return fmaq(x, y, z), + else => @compileError("unreachable"), + } +} + const dd = struct { hi: f64, lo: f64, diff --git a/lib/compiler_rt/fmax.zig b/lib/compiler_rt/fmax.zig index a5bd68cd74..defc935afc 100644 --- a/lib/compiler_rt/fmax.zig +++ b/lib/compiler_rt/fmax.zig @@ -21,6 +21,17 @@ pub fn fmaxq(x: f128, y: f128) callconv(.C) f128 { return generic_fmax(f128, x, y); } +pub fn fmaxl(x: c_longdouble, y: c_longdouble) callconv(.C) c_longdouble { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __fmaxh(x, y), + 32 => return fmaxf(x, y), + 64 => return fmax(x, y), + 80 => return __fmaxx(x, y), + 128 => return fmaxq(x, y), + else => @compileError("unreachable"), + } +} + inline fn generic_fmax(comptime T: type, x: T, y: T) T { if (math.isNan(x)) return y; diff --git a/lib/compiler_rt/fmin.zig b/lib/compiler_rt/fmin.zig index cc4dbf082b..e93300bd4b 100644 --- a/lib/compiler_rt/fmin.zig +++ b/lib/compiler_rt/fmin.zig @@ -21,6 +21,17 @@ pub fn fminq(x: f128, y: f128) callconv(.C) f128 { return generic_fmin(f128, x, y); } +pub fn fminl(x: c_longdouble, y: c_longdouble) callconv(.C) c_longdouble { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __fminh(x, y), + 32 => return fminf(x, y), + 64 => return fmin(x, y), + 80 => return __fminx(x, y), + 128 => return fminq(x, y), + else => @compileError("unreachable"), + } +} + inline fn generic_fmin(comptime T: type, x: T, y: T) T { if (math.isNan(x)) return y; diff --git a/lib/compiler_rt/fmod.zig b/lib/compiler_rt/fmod.zig index 28e0df3d6b..5d413ca37d 100644 --- a/lib/compiler_rt/fmod.zig +++ b/lib/compiler_rt/fmod.zig @@ -237,6 +237,17 @@ pub fn fmodq(a: f128, b: f128) callconv(.C) f128 { return amod; } +pub fn fmodl(a: c_longdouble, b: c_longdouble) callconv(.C) c_longdouble { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __fmodh(a, b), + 32 => return fmodf(a, b), + 64 => return fmod(a, b), + 80 => return __fmodx(a, b), + 128 => return fmodq(a, b), + else => @compileError("unreachable"), + } +} + inline fn generic_fmod(comptime T: type, x: T, y: T) T { @setRuntimeSafety(false); diff --git a/lib/compiler_rt/log.zig b/lib/compiler_rt/log.zig index 8b09baac2e..6e705dae60 100644 --- a/lib/compiler_rt/log.zig +++ b/lib/compiler_rt/log.zig @@ -131,6 +131,17 @@ pub fn logq(a: f128) callconv(.C) f128 { return log(@floatCast(f64, a)); } +pub fn logl(x: c_longdouble) callconv(.C) c_longdouble { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __logh(x), + 32 => return logf(x), + 64 => return log(x), + 80 => return __logx(x), + 128 => return logq(x), + else => @compileError("unreachable"), + } +} + test "ln32" { const epsilon = 0.000001; diff --git a/lib/compiler_rt/log10.zig b/lib/compiler_rt/log10.zig index ce06d8c649..47499d2739 100644 --- a/lib/compiler_rt/log10.zig +++ b/lib/compiler_rt/log10.zig @@ -159,6 +159,17 @@ pub fn log10q(a: f128) callconv(.C) f128 { return log10(@floatCast(f64, a)); } +pub fn log10l(x: c_longdouble) callconv(.C) c_longdouble { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __log10h(x), + 32 => return log10f(x), + 64 => return log10(x), + 80 => return __log10x(x), + 128 => return log10q(x), + else => @compileError("unreachable"), + } +} + test "log10_32" { const epsilon = 0.000001; diff --git a/lib/compiler_rt/log2.zig b/lib/compiler_rt/log2.zig index 2c2d620c3d..53f35c9a80 100644 --- a/lib/compiler_rt/log2.zig +++ b/lib/compiler_rt/log2.zig @@ -150,6 +150,17 @@ pub fn log2q(a: f128) callconv(.C) f128 { return math.log2(a); } +pub fn log2l(x: c_longdouble) callconv(.C) c_longdouble { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __log2h(x), + 32 => return log2f(x), + 64 => return log2(x), + 80 => return __log2x(x), + 128 => return log2q(x), + else => @compileError("unreachable"), + } +} + test "log2_32" { const epsilon = 0.000001; diff --git a/lib/compiler_rt/round.zig b/lib/compiler_rt/round.zig index 99201efcf8..4f3266e00c 100644 --- a/lib/compiler_rt/round.zig +++ b/lib/compiler_rt/round.zig @@ -123,6 +123,17 @@ pub fn roundq(x_: f128) callconv(.C) f128 { } } +pub fn roundl(x: c_longdouble) callconv(.C) c_longdouble { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __roundh(x), + 32 => return roundf(x), + 64 => return round(x), + 80 => return __roundx(x), + 128 => return roundq(x), + else => @compileError("unreachable"), + } +} + test "round32" { try expect(roundf(1.3) == 1.0); try expect(roundf(-1.3) == -1.0); diff --git a/lib/compiler_rt/sin.zig b/lib/compiler_rt/sin.zig index 3d5572a59f..20259bc309 100644 --- a/lib/compiler_rt/sin.zig +++ b/lib/compiler_rt/sin.zig @@ -111,6 +111,17 @@ pub fn sinq(x: f128) callconv(.C) f128 { return sin(@floatCast(f64, x)); } +pub fn sinl(x: c_longdouble) callconv(.C) c_longdouble { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __sinh(x), + 32 => return sinf(x), + 64 => return sin(x), + 80 => return __sinx(x), + 128 => return sinq(x), + else => @compileError("unreachable"), + } +} + test "sin32" { const epsilon = 0.00001; diff --git a/lib/compiler_rt/sincos.zig b/lib/compiler_rt/sincos.zig index 31ebd0d1d0..8bc5b83ee5 100644 --- a/lib/compiler_rt/sincos.zig +++ b/lib/compiler_rt/sincos.zig @@ -181,6 +181,17 @@ pub fn sincosq(x: f128, r_sin: *f128, r_cos: *f128) callconv(.C) void { r_cos.* = small_cos; } +pub fn sincosl(x: c_longdouble, r_sin: *c_longdouble, r_cos: *c_longdouble) callconv(.C) void { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __sincosh(x, r_sin, r_cos), + 32 => return sincosf(x, r_sin, r_cos), + 64 => return sincos(x, r_sin, r_cos), + 80 => return __sincosx(x, r_sin, r_cos), + 128 => return sincosq(x, r_sin, r_cos), + else => @compileError("unreachable"), + } +} + const rem_pio2_generic = @compileError("TODO"); /// Ported from musl sincosl.c. Needs the following dependencies to be complete: diff --git a/lib/compiler_rt/sqrt.zig b/lib/compiler_rt/sqrt.zig index ba07beb86e..8d43949f99 100644 --- a/lib/compiler_rt/sqrt.zig +++ b/lib/compiler_rt/sqrt.zig @@ -225,6 +225,17 @@ pub fn sqrtq(x: f128) callconv(.C) f128 { return sqrt(@floatCast(f64, x)); } +pub fn sqrtl(x: c_longdouble) callconv(.C) c_longdouble { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __sqrth(x), + 32 => return sqrtf(x), + 64 => return sqrt(x), + 80 => return __sqrtx(x), + 128 => return sqrtq(x), + else => @compileError("unreachable"), + } +} + test "sqrtf" { const V = [_]f32{ 0.0, diff --git a/lib/compiler_rt/tan.zig b/lib/compiler_rt/tan.zig index d99f00b99e..d37022d918 100644 --- a/lib/compiler_rt/tan.zig +++ b/lib/compiler_rt/tan.zig @@ -96,6 +96,17 @@ pub fn tanq(x: f128) callconv(.C) f128 { return tan(@floatCast(f64, x)); } +pub fn tanl(x: c_longdouble) callconv(.C) c_longdouble { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __tanh(x), + 32 => return tanf(x), + 64 => return tan(x), + 80 => return __tanx(x), + 128 => return tanq(x), + else => @compileError("unreachable"), + } +} + test "tan" { try expect(tan(@as(f32, 0.0)) == tanf(0.0)); try expect(tan(@as(f64, 0.0)) == tan(0.0)); diff --git a/lib/compiler_rt/trunc.zig b/lib/compiler_rt/trunc.zig index 5406f9a02d..d00df60d99 100644 --- a/lib/compiler_rt/trunc.zig +++ b/lib/compiler_rt/trunc.zig @@ -81,6 +81,17 @@ pub fn truncq(x: f128) callconv(.C) f128 { } } +pub fn truncl(x: c_longdouble) callconv(.C) c_longdouble { + switch (@typeInfo(c_longdouble).Float.bits) { + 16 => return __trunch(x), + 32 => return truncf(x), + 64 => return trunc(x), + 80 => return __truncx(x), + 128 => return truncq(x), + else => @compileError("unreachable"), + } +} + test "trunc32" { try expect(truncf(1.3) == 1.0); try expect(truncf(-1.3) == -1.0); From ea3f5905f0635d0c0cb3983ba5ca6c92859e9d81 Mon Sep 17 00:00:00 2001 From: Hannes Bredberg Date: Fri, 6 May 2022 21:32:22 +0200 Subject: [PATCH 1413/2031] Add Win64 calling convention Closes ziglang/zig#11585 --- lib/std/builtin.zig | 1 + src/codegen/llvm.zig | 1 + src/stage1/all_types.hpp | 1 + src/stage1/analyze.cpp | 4 ++++ src/stage1/codegen.cpp | 5 +++++ src/stage1/ir.cpp | 1 + 6 files changed, 13 insertions(+) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index f38fc4e155..8c1e38ea09 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -147,6 +147,7 @@ pub const CallingConvention = enum { AAPCS, AAPCSVFP, SysV, + Win64, PtxKernel, }; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 800786891e..57bcbe9338 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -7940,6 +7940,7 @@ fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) llvm.Ca }, .Signal => .AVR_SIGNAL, .SysV => .X86_64_SysV, + .Win64 => .Win64, .PtxKernel => return switch (target.cpu.arch) { .nvptx, .nvptx64 => .PTX_Kernel, else => unreachable, diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp index fbf0793d68..ba5df49c59 100644 --- a/src/stage1/all_types.hpp +++ b/src/stage1/all_types.hpp @@ -84,6 +84,7 @@ enum CallingConvention { CallingConventionAAPCS, CallingConventionAAPCSVFP, CallingConventionSysV, + CallingConventionWin64, CallingConventionPtxKernel }; diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp index 34dff556e0..8faf3c0fc9 100644 --- a/src/stage1/analyze.cpp +++ b/src/stage1/analyze.cpp @@ -991,6 +991,7 @@ const char *calling_convention_name(CallingConvention cc) { case CallingConventionAAPCSVFP: return "AAPCSVFP"; case CallingConventionInline: return "Inline"; case CallingConventionSysV: return "SysV"; + case CallingConventionWin64: return "Win64"; case CallingConventionPtxKernel: return "PtxKernel"; } zig_unreachable(); @@ -1015,6 +1016,7 @@ bool calling_convention_allows_zig_types(CallingConvention cc) { case CallingConventionAAPCS: case CallingConventionAAPCSVFP: case CallingConventionSysV: + case CallingConventionWin64: return false; } zig_unreachable(); @@ -2006,6 +2008,7 @@ Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_ allowed_platforms = "ARM"; break; case CallingConventionSysV: + case CallingConventionWin64: if (g->zig_target->arch != ZigLLVM_x86_64) allowed_platforms = "x86_64"; break; @@ -3846,6 +3849,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { case CallingConventionAAPCS: case CallingConventionAAPCSVFP: case CallingConventionSysV: + case CallingConventionWin64: case CallingConventionPtxKernel: add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name), GlobalLinkageIdStrong, fn_cc); diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 03bd22b42d..d101030c33 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -209,6 +209,9 @@ static ZigLLVM_CallingConv get_llvm_cc(CodeGen *g, CallingConvention cc) { case CallingConventionSysV: assert(g->zig_target->arch == ZigLLVM_x86_64); return ZigLLVM_X86_64_SysV; + case CallingConventionWin64: + assert(g->zig_target->arch == ZigLLVM_x86_64); + return ZigLLVM_Win64; case CallingConventionPtxKernel: assert(g->zig_target->arch == ZigLLVM_nvptx || g->zig_target->arch == ZigLLVM_nvptx64); @@ -359,6 +362,7 @@ static bool cc_want_sret_attr(CallingConvention cc) { case CallingConventionAAPCS: case CallingConventionAAPCSVFP: case CallingConventionSysV: + case CallingConventionWin64: case CallingConventionPtxKernel: return true; case CallingConventionAsync: @@ -10033,6 +10037,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { static_assert(CallingConventionAAPCS == 12, ""); static_assert(CallingConventionAAPCSVFP == 13, ""); static_assert(CallingConventionSysV == 14, ""); + static_assert(CallingConventionWin64 == 15, ""); static_assert(BuiltinPtrSizeOne == 0, ""); static_assert(BuiltinPtrSizeMany == 1, ""); diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index f7ab5e12fa..2f4e0aa0e7 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -11743,6 +11743,7 @@ static Stage1AirInst *ir_analyze_instruction_export(IrAnalyze *ira, Stage1ZirIns case CallingConventionAAPCS: case CallingConventionAAPCSVFP: case CallingConventionSysV: + case CallingConventionWin64: case CallingConventionPtxKernel: add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc); fn_entry->section_name = section_name; From 9c3d24ea0bffa53fb08a73493d72a6a0866f6432 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 9 May 2022 17:39:19 +0200 Subject: [PATCH 1414/2031] x64: add naive impl of shr --- src/arch/x86_64/CodeGen.zig | 75 ++++++++++++++++++++++++++++++++++--- test/behavior/math.zig | 2 - 2 files changed, 70 insertions(+), 7 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index ee472eeac8..28de88ff86 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2052,11 +2052,76 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { fn airShr(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 shr for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); + + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + } + + const ty = self.air.typeOfIndex(inst); + const tag = self.air.instructions.items(.tag)[inst]; + switch (tag) { + .shr_exact => return self.fail("TODO implement shr_exact for type {}", .{ty.fmtDebug()}), + .shr => {}, + else => unreachable, + } + + if (ty.zigTypeTag() != .Int) { + return self.fail("TODO implement shr for type {}", .{ty.fmtDebug()}); + } + if (ty.abiSize(self.target.*) > 8) { + return self.fail("TODO implement shr for integers larger than 8 bytes", .{}); + } + + // TODO look into reusing the operands + // TODO audit register allocation mechanics + const shift = try self.resolveInst(bin_op.rhs); + const shift_ty = self.air.typeOf(bin_op.rhs); + + blk: { + switch (shift) { + .register => |reg| { + if (reg.to64() == .rcx) break :blk; + }, + else => {}, + } + try self.register_manager.getReg(.rcx, null); + try self.genSetReg(shift_ty, .rcx, shift); + } + const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx); + defer self.register_manager.unlockReg(rcx_lock); + + const value = try self.resolveInst(bin_op.lhs); + const value_lock: ?RegisterLock = switch (value) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (value_lock) |lock| self.register_manager.unlockReg(lock); + + const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, value); + switch (ty.intInfo(self.target.*).signedness) { + .signed => { + _ = try self.addInst(.{ + .tag = .sar, + .ops = (Mir.Ops{ + .reg1 = dst_mcv.register, + .flags = 0b01, + }).encode(), + .data = undefined, + }); + }, + .unsigned => { + _ = try self.addInst(.{ + .tag = .shr, + .ops = (Mir.Ops{ + .reg1 = dst_mcv.register, + .flags = 0b01, + }).encode(), + .data = undefined, + }); + }, + } + + return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none }); } fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 42f2635afd..dd609c9b08 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -570,8 +570,6 @@ test "bit shift a u1" { } test "truncating shift right" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - try testShrTrunc(maxInt(u16)); comptime try testShrTrunc(maxInt(u16)); } From 94b9bcd034960f0467ca789217064b17f2ca7a49 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 6 May 2022 10:14:31 -0700 Subject: [PATCH 1415/2031] stdlib: escape backslashes and double quotes in Builder response file Fixes #11595 --- lib/std/build.zig | 27 +++++++++++++- test/standalone.zig | 1 + test/standalone/issue_11595/build.zig | 52 +++++++++++++++++++++++++++ test/standalone/issue_11595/main.zig | 5 +++ test/standalone/issue_11595/test.c | 10 ++++++ 5 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 test/standalone/issue_11595/build.zig create mode 100644 test/standalone/issue_11595/main.zig create mode 100644 test/standalone/issue_11595/test.c diff --git a/lib/std/build.zig b/lib/std/build.zig index b57a1d37e7..b0200a928b 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -3009,6 +3009,7 @@ pub const LibExeObjStep = struct { // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and // pass that to zig, e.g. via 'zig build-lib @args.rsp' + // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html var args_length: usize = 0; for (zig_args.items) |arg| { args_length += arg.len + 1; // +1 to account for null terminator @@ -3020,9 +3021,33 @@ pub const LibExeObjStep = struct { ); try std.fs.cwd().makePath(args_dir); + var args_arena = std.heap.ArenaAllocator.init(builder.allocator); + defer args_arena.deinit(); + + const args_to_escape = zig_args.items[2..]; + var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len); + + arg_blk: for (args_to_escape) |arg| { + for (arg) |c, arg_idx| { + if (c == '\\' or c == '"') { + // Slow path for arguments that need to be escaped. We'll need to allocate and copy + var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1); + const writer = escaped.writer(); + writer.writeAll(arg[0..arg_idx]) catch unreachable; + for (arg[arg_idx..]) |to_escape| { + if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\'); + try writer.writeByte(to_escape); + } + escaped_args.appendAssumeCapacity(escaped.items); + continue :arg_blk; + } + } + escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument + } + // Write the args to zig-cache/args/ to avoid conflicts with // other zig build commands running in parallel. - const partially_quoted = try std.mem.join(builder.allocator, "\" \"", zig_args.items[2..]); + const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items); const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" }); var args_hash: [Sha256.digest_length]u8 = undefined; diff --git a/test/standalone.zig b/test/standalone.zig index 6df3387018..856a58df84 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -49,6 +49,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { cases.addBuildFile("test/standalone/issue_7030/build.zig", .{}); cases.addBuildFile("test/standalone/install_raw_hex/build.zig", .{}); cases.addBuildFile("test/standalone/issue_9812/build.zig", .{}); + cases.addBuildFile("test/standalone/issue_11595/build.zig", .{}); if (builtin.os.tag != .wasi) { cases.addBuildFile("test/standalone/load_dynamic_library/build.zig", .{}); } diff --git a/test/standalone/issue_11595/build.zig b/test/standalone/issue_11595/build.zig new file mode 100644 index 0000000000..d636f63ebc --- /dev/null +++ b/test/standalone/issue_11595/build.zig @@ -0,0 +1,52 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const Builder = std.build.Builder; +const CrossTarget = std.zig.CrossTarget; + +// TODO integrate this with the std.build executor API +fn isRunnableTarget(t: CrossTarget) bool { + if (t.isNative()) return true; + + return (t.getOsTag() == builtin.os.tag and + t.getCpuArch() == builtin.cpu.arch); +} + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + const target = b.standardTargetOptions(.{}); + + const exe = b.addExecutable("zigtest", "main.zig"); + exe.setBuildMode(mode); + exe.install(); + + const c_sources = [_][]const u8{ + "test.c", + }; + + exe.addCSourceFiles(&c_sources, &.{}); + exe.linkLibC(); + + var i: i32 = 0; + while (i < 1000) : (i += 1) { + exe.defineCMacro("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + } + + exe.defineCMacro("FOO", "42"); + exe.defineCMacro("BAR", "\"BAR\""); + exe.defineCMacro("BAZ", + \\"\"BAZ\"" + ); + exe.defineCMacro("QUX", "\"Q\" \"UX\""); + exe.defineCMacro("QUUX", "\"QU\\\"UX\""); + + exe.setTarget(target); + b.default_step.dependOn(&exe.step); + + const test_step = b.step("test", "Test the program"); + if (isRunnableTarget(target)) { + const run_cmd = exe.run(); + test_step.dependOn(&run_cmd.step); + } else { + test_step.dependOn(&exe.step); + } +} diff --git a/test/standalone/issue_11595/main.zig b/test/standalone/issue_11595/main.zig new file mode 100644 index 0000000000..b91f54cb9c --- /dev/null +++ b/test/standalone/issue_11595/main.zig @@ -0,0 +1,5 @@ +extern fn check() c_int; + +pub fn main() u8 { + return @intCast(u8, check()); +} diff --git a/test/standalone/issue_11595/test.c b/test/standalone/issue_11595/test.c new file mode 100644 index 0000000000..5bfaa1a351 --- /dev/null +++ b/test/standalone/issue_11595/test.c @@ -0,0 +1,10 @@ + #include + +int check(void) { + if (FOO != 42) return 1; + if (strcmp(BAR, "BAR")) return 2; + if (strcmp(BAZ, "\"BAZ\"")) return 3; + if (strcmp(QUX, "QUX")) return 4; + if (strcmp(QUUX, "QU\"UX")) return 5; + return 0; +} From 941b6830b1831c4df5ba369088ff473a012a3b54 Mon Sep 17 00:00:00 2001 From: Helio Machado <0x2b3bfa0+git@googlemail.com> Date: Mon, 9 May 2022 18:50:01 +0200 Subject: [PATCH 1416/2031] std.crypto: generate AES constants at compile time (#11612) * std/crypto: generate AES constants at compile time * Apply suggestions from code review Co-authored-by: Frank Denis <124872+jedisct1@users.noreply.github.com> * Update lib/std/crypto/aes/soft.zig * Separate encryption and decryption tables * Run `zig fmt` * Increase branch quota and remove redundant align * Update lib/std/crypto/aes/soft.zig Co-authored-by: Frank Denis <124872+jedisct1@users.noreply.github.com> * Rename identifiers and simplify dataflow * Increase branch quota (again) and fix comment Co-authored-by: Frank Denis <124872+jedisct1@users.noreply.github.com> --- lib/std/crypto/aes/soft.zig | 457 +++++++++--------------------------- 1 file changed, 113 insertions(+), 344 deletions(-) diff --git a/lib/std/crypto/aes/soft.zig b/lib/std/crypto/aes/soft.zig index bc9a994b95..7d676eb8ce 100644 --- a/lib/std/crypto/aes/soft.zig +++ b/lib/std/crypto/aes/soft.zig @@ -1,6 +1,7 @@ // Based on Go stdlib implementation const std = @import("../../std.zig"); +const math = std.math; const mem = std.mem; const BlockVec = [4]u32; @@ -49,10 +50,10 @@ pub const Block = struct { const s2 = block.repr[2]; const s3 = block.repr[3]; - const t0 = round_key.repr[0] ^ te0[@truncate(u8, s0 >> 24)] ^ te1[@truncate(u8, s1 >> 16)] ^ te2[@truncate(u8, s2 >> 8)] ^ te3[@truncate(u8, s3)]; - const t1 = round_key.repr[1] ^ te0[@truncate(u8, s1 >> 24)] ^ te1[@truncate(u8, s2 >> 16)] ^ te2[@truncate(u8, s3 >> 8)] ^ te3[@truncate(u8, s0)]; - const t2 = round_key.repr[2] ^ te0[@truncate(u8, s2 >> 24)] ^ te1[@truncate(u8, s3 >> 16)] ^ te2[@truncate(u8, s0 >> 8)] ^ te3[@truncate(u8, s1)]; - const t3 = round_key.repr[3] ^ te0[@truncate(u8, s3 >> 24)] ^ te1[@truncate(u8, s0 >> 16)] ^ te2[@truncate(u8, s1 >> 8)] ^ te3[@truncate(u8, s2)]; + const t0 = round_key.repr[0] ^ table_encrypt[0][@truncate(u8, s0 >> 24)] ^ table_encrypt[1][@truncate(u8, s1 >> 16)] ^ table_encrypt[2][@truncate(u8, s2 >> 8)] ^ table_encrypt[3][@truncate(u8, s3)]; + const t1 = round_key.repr[1] ^ table_encrypt[0][@truncate(u8, s1 >> 24)] ^ table_encrypt[1][@truncate(u8, s2 >> 16)] ^ table_encrypt[2][@truncate(u8, s3 >> 8)] ^ table_encrypt[3][@truncate(u8, s0)]; + const t2 = round_key.repr[2] ^ table_encrypt[0][@truncate(u8, s2 >> 24)] ^ table_encrypt[1][@truncate(u8, s3 >> 16)] ^ table_encrypt[2][@truncate(u8, s0 >> 8)] ^ table_encrypt[3][@truncate(u8, s1)]; + const t3 = round_key.repr[3] ^ table_encrypt[0][@truncate(u8, s3 >> 24)] ^ table_encrypt[1][@truncate(u8, s0 >> 16)] ^ table_encrypt[2][@truncate(u8, s1 >> 8)] ^ table_encrypt[3][@truncate(u8, s2)]; return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; } @@ -65,10 +66,10 @@ pub const Block = struct { const t3 = block.repr[3]; // Last round uses s-box directly and XORs to produce output. - var s0 = @as(u32, sbox0[t0 >> 24]) << 24 | @as(u32, sbox0[t1 >> 16 & 0xff]) << 16 | @as(u32, sbox0[t2 >> 8 & 0xff]) << 8 | @as(u32, sbox0[t3 & 0xff]); - var s1 = @as(u32, sbox0[t1 >> 24]) << 24 | @as(u32, sbox0[t2 >> 16 & 0xff]) << 16 | @as(u32, sbox0[t3 >> 8 & 0xff]) << 8 | @as(u32, sbox0[t0 & 0xff]); - var s2 = @as(u32, sbox0[t2 >> 24]) << 24 | @as(u32, sbox0[t3 >> 16 & 0xff]) << 16 | @as(u32, sbox0[t0 >> 8 & 0xff]) << 8 | @as(u32, sbox0[t1 & 0xff]); - var s3 = @as(u32, sbox0[t3 >> 24]) << 24 | @as(u32, sbox0[t0 >> 16 & 0xff]) << 16 | @as(u32, sbox0[t1 >> 8 & 0xff]) << 8 | @as(u32, sbox0[t2 & 0xff]); + var s0 = @as(u32, sbox_encrypt[t0 >> 24]) << 24 | @as(u32, sbox_encrypt[t1 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t2 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t3 & 0xff]); + var s1 = @as(u32, sbox_encrypt[t1 >> 24]) << 24 | @as(u32, sbox_encrypt[t2 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t3 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t0 & 0xff]); + var s2 = @as(u32, sbox_encrypt[t2 >> 24]) << 24 | @as(u32, sbox_encrypt[t3 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t0 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t1 & 0xff]); + var s3 = @as(u32, sbox_encrypt[t3 >> 24]) << 24 | @as(u32, sbox_encrypt[t0 >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[t1 >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[t2 & 0xff]); s0 ^= round_key.repr[0]; s1 ^= round_key.repr[1]; s2 ^= round_key.repr[2]; @@ -84,10 +85,10 @@ pub const Block = struct { const s2 = block.repr[2]; const s3 = block.repr[3]; - const t0 = round_key.repr[0] ^ td0[@truncate(u8, s0 >> 24)] ^ td1[@truncate(u8, s3 >> 16)] ^ td2[@truncate(u8, s2 >> 8)] ^ td3[@truncate(u8, s1)]; - const t1 = round_key.repr[1] ^ td0[@truncate(u8, s1 >> 24)] ^ td1[@truncate(u8, s0 >> 16)] ^ td2[@truncate(u8, s3 >> 8)] ^ td3[@truncate(u8, s2)]; - const t2 = round_key.repr[2] ^ td0[@truncate(u8, s2 >> 24)] ^ td1[@truncate(u8, s1 >> 16)] ^ td2[@truncate(u8, s0 >> 8)] ^ td3[@truncate(u8, s3)]; - const t3 = round_key.repr[3] ^ td0[@truncate(u8, s3 >> 24)] ^ td1[@truncate(u8, s2 >> 16)] ^ td2[@truncate(u8, s1 >> 8)] ^ td3[@truncate(u8, s0)]; + const t0 = round_key.repr[0] ^ table_decrypt[0][@truncate(u8, s0 >> 24)] ^ table_decrypt[1][@truncate(u8, s3 >> 16)] ^ table_decrypt[2][@truncate(u8, s2 >> 8)] ^ table_decrypt[3][@truncate(u8, s1)]; + const t1 = round_key.repr[1] ^ table_decrypt[0][@truncate(u8, s1 >> 24)] ^ table_decrypt[1][@truncate(u8, s0 >> 16)] ^ table_decrypt[2][@truncate(u8, s3 >> 8)] ^ table_decrypt[3][@truncate(u8, s2)]; + const t2 = round_key.repr[2] ^ table_decrypt[0][@truncate(u8, s2 >> 24)] ^ table_decrypt[1][@truncate(u8, s1 >> 16)] ^ table_decrypt[2][@truncate(u8, s0 >> 8)] ^ table_decrypt[3][@truncate(u8, s3)]; + const t3 = round_key.repr[3] ^ table_decrypt[0][@truncate(u8, s3 >> 24)] ^ table_decrypt[1][@truncate(u8, s2 >> 16)] ^ table_decrypt[2][@truncate(u8, s1 >> 8)] ^ table_decrypt[3][@truncate(u8, s0)]; return Block{ .repr = BlockVec{ t0, t1, t2, t3 } }; } @@ -100,10 +101,10 @@ pub const Block = struct { const t3 = block.repr[3]; // Last round uses s-box directly and XORs to produce output. - var s0 = @as(u32, sbox1[t0 >> 24]) << 24 | @as(u32, sbox1[t3 >> 16 & 0xff]) << 16 | @as(u32, sbox1[t2 >> 8 & 0xff]) << 8 | @as(u32, sbox1[t1 & 0xff]); - var s1 = @as(u32, sbox1[t1 >> 24]) << 24 | @as(u32, sbox1[t0 >> 16 & 0xff]) << 16 | @as(u32, sbox1[t3 >> 8 & 0xff]) << 8 | @as(u32, sbox1[t2 & 0xff]); - var s2 = @as(u32, sbox1[t2 >> 24]) << 24 | @as(u32, sbox1[t1 >> 16 & 0xff]) << 16 | @as(u32, sbox1[t0 >> 8 & 0xff]) << 8 | @as(u32, sbox1[t3 & 0xff]); - var s3 = @as(u32, sbox1[t3 >> 24]) << 24 | @as(u32, sbox1[t2 >> 16 & 0xff]) << 16 | @as(u32, sbox1[t1 >> 8 & 0xff]) << 8 | @as(u32, sbox1[t0 & 0xff]); + var s0 = @as(u32, sbox_decrypt[t0 >> 24]) << 24 | @as(u32, sbox_decrypt[t3 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t2 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t1 & 0xff]); + var s1 = @as(u32, sbox_decrypt[t1 >> 24]) << 24 | @as(u32, sbox_decrypt[t0 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t3 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t2 & 0xff]); + var s2 = @as(u32, sbox_decrypt[t2 >> 24]) << 24 | @as(u32, sbox_decrypt[t1 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t0 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t3 & 0xff]); + var s3 = @as(u32, sbox_decrypt[t3 >> 24]) << 24 | @as(u32, sbox_decrypt[t2 >> 16 & 0xff]) << 16 | @as(u32, sbox_decrypt[t1 >> 8 & 0xff]) << 8 | @as(u32, sbox_decrypt[t0 & 0xff]); s0 ^= round_key.repr[0]; s1 ^= round_key.repr[1]; s2 ^= round_key.repr[2]; @@ -223,9 +224,9 @@ fn KeySchedule(comptime Aes: type) type { // Key expansion algorithm. See FIPS-197, Figure 11. fn expandKey(key: [key_length]u8) Self { const subw = struct { - // Apply sbox0 to each byte in w. + // Apply sbox_encrypt to each byte in w. fn func(w: u32) u32 { - return @as(u32, sbox0[w >> 24]) << 24 | @as(u32, sbox0[w >> 16 & 0xff]) << 16 | @as(u32, sbox0[w >> 8 & 0xff]) << 8 | @as(u32, sbox0[w & 0xff]); + return @as(u32, sbox_encrypt[w >> 24]) << 24 | @as(u32, sbox_encrypt[w >> 16 & 0xff]) << 16 | @as(u32, sbox_encrypt[w >> 8 & 0xff]) << 8 | @as(u32, sbox_encrypt[w & 0xff]); } }.func; @@ -258,7 +259,7 @@ fn KeySchedule(comptime Aes: type) type { inline while (j < 4) : (j += 1) { var x = round_keys[(ei + j) / 4].repr[(ei + j) % 4]; if (i > 0 and i + 4 < total_words) { - x = td0[sbox0[x >> 24]] ^ td1[sbox0[x >> 16 & 0xff]] ^ td2[sbox0[x >> 8 & 0xff]] ^ td3[sbox0[x & 0xff]]; + x = table_decrypt[0][sbox_encrypt[x >> 24]] ^ table_decrypt[1][sbox_encrypt[x >> 16 & 0xff]] ^ table_decrypt[2][sbox_encrypt[x >> 8 & 0xff]] ^ table_decrypt[3][sbox_encrypt[x & 0xff]]; } inv_round_keys[(i + j) / 4].repr[(i + j) % 4] = x; } @@ -410,333 +411,101 @@ pub const Aes256 = struct { }; // constants -const powx = [16]u8{ - 0x01, - 0x02, - 0x04, - 0x08, - 0x10, - 0x20, - 0x40, - 0x80, - 0x1b, - 0x36, - 0x6c, - 0xd8, - 0xab, - 0x4d, - 0x9a, - 0x2f, + +// Rijndael's irreducible polynomial. +const poly: u9 = 1 << 8 | 1 << 4 | 1 << 3 | 1 << 1 | 1 << 0; // x⁸ + x⁴ + x³ + x + 1 + +// Powers of x mod poly in GF(2). +const powx = init: { + var array: [16]u8 = undefined; + + var value = 1; + for (array) |*power| { + power.* = value; + value = mul(value, 2); + } + + break :init array; }; -const sbox0 align(64) = [256]u8{ - 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, - 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, - 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, - 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, - 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, - 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, - 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, - 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, - 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, - 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, - 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, - 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, - 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, - 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, - 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, - 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, -}; +const sbox_encrypt align(64) = generateSbox(false); +const sbox_decrypt align(64) = generateSbox(true); +const table_encrypt align(64) = generateTable(false); +const table_decrypt align(64) = generateTable(true); -const sbox1 align(64) = [256]u8{ - 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, - 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, - 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, - 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, - 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, - 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, - 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, - 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, - 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, - 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, - 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, - 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, - 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, - 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, - 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, - 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d, -}; +// Generate S-box substitution values. +fn generateSbox(invert: bool) [256]u8 { + @setEvalBranchQuota(10000); -const te0 align(64) = [256]u32{ - 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, - 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, - 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, - 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, - 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, - 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, - 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, - 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, - 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, - 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, - 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, - 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, - 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, - 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, - 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, - 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, - 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, - 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, - 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, - 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, - 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, - 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, - 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, - 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, - 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, - 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, - 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, - 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, - 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, - 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, - 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, - 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a, -}; -const te1 align(64) = [256]u32{ - 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, - 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, - 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, - 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, - 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, - 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, - 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, - 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, - 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, - 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, - 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, - 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, - 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, - 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, - 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, - 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, - 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, - 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, - 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, - 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, - 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, - 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, - 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, - 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, - 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, - 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, - 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, - 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, - 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, - 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, - 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, - 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616, -}; -const te2 align(64) = [256]u32{ - 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, - 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, - 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, - 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, - 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, - 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, - 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, - 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, - 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, - 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, - 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, - 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, - 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, - 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, - 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, - 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, - 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, - 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, - 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, - 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, - 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, - 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, - 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, - 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, - 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, - 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, - 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, - 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, - 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, - 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, - 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, - 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16, -}; -const te3 align(64) = [256]u32{ - 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, - 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, - 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, - 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, - 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, - 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, - 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, - 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, - 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, - 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, - 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, - 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, - 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, - 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, - 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, - 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, - 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, - 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, - 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, - 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, - 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, - 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, - 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, - 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, - 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, - 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, - 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, - 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, - 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, - 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, - 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, - 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c, -}; + var sbox: [256]u8 = undefined; -const td0 align(64) = [256]u32{ - 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, - 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, - 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, - 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, - 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, - 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, - 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, - 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, - 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, - 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, - 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, - 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, - 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, - 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, - 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, - 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, - 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, - 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, - 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, - 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, - 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, - 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, - 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, - 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, - 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, - 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, - 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, - 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, - 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, - 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, - 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, - 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742, -}; -const td1 align(64) = [256]u32{ - 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, - 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, - 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, - 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, - 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, - 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, - 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, - 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, - 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, - 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, - 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, - 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, - 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, - 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, - 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, - 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, - 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, - 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, - 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, - 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, - 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, - 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, - 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, - 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, - 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, - 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, - 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, - 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, - 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, - 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, - 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, - 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857, -}; -const td2 align(64) = [256]u32{ - 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, - 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, - 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, - 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, - 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, - 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, - 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, - 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, - 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, - 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, - 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, - 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, - 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, - 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, - 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, - 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, - 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, - 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, - 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, - 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, - 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, - 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, - 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, - 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, - 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, - 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, - 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, - 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, - 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, - 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, - 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, - 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8, -}; -const td3 align(64) = [256]u32{ - 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, - 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, - 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, - 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, - 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, - 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, - 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, - 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, - 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, - 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, - 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, - 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, - 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, - 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, - 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, - 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, - 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, - 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, - 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, - 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, - 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, - 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, - 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, - 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, - 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, - 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, - 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, - 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, - 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, - 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, - 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, - 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0, -}; + var p: u8 = 1; + var q: u8 = 1; + for (sbox) |_| { + p = mul(p, 3); + q = mul(q, 0xf6); // divide by 3 + + var value: u8 = q ^ 0x63; + value ^= math.rotl(u8, q, 1); + value ^= math.rotl(u8, q, 2); + value ^= math.rotl(u8, q, 3); + value ^= math.rotl(u8, q, 4); + + if (invert) { + sbox[value] = p; + } else { + sbox[p] = value; + } + } + + if (invert) { + sbox[0x63] = 0x00; + } else { + sbox[0x00] = 0x63; + } + + return sbox; +} + +// Generate lookup tables. +fn generateTable(invert: bool) [4][256]u32 { + var table: [4][256]u32 = undefined; + + for (generateSbox(invert)) |value, index| { + table[0][index] = mul(value, if (invert) 0xb else 0x3); + table[0][index] |= math.shl(u32, mul(value, if (invert) 0xd else 0x1), 8); + table[0][index] |= math.shl(u32, mul(value, if (invert) 0x9 else 0x1), 16); + table[0][index] |= math.shl(u32, mul(value, if (invert) 0xe else 0x2), 24); + + table[1][index] = math.rotr(u32, table[0][index], 8); + table[2][index] = math.rotr(u32, table[0][index], 16); + table[3][index] = math.rotr(u32, table[0][index], 24); + } + + return table; +} + +// Multiply a and b as GF(2) polynomials modulo poly. +fn mul(a: u8, b: u8) u8 { + @setEvalBranchQuota(30000); + + var i: u9 = a; + var j: u8 = b; + var k: u8 = 1; + var s: u9 = 0; + + while (k < 0x100 and j != 0) : (k <<= 1) { + if (j & k != 0) { + s ^= i; + j ^= k; + } + + i <<= 1; + + if (i & 0x100 != 0) { + i ^= poly; + } + } + + return @truncate(u8, s); +} From 8e1c220be257236565fb28d84dc56045f15be697 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 30 Apr 2022 15:42:21 +0200 Subject: [PATCH 1417/2031] wasm: Add basic debug info references --- src/link.zig | 3 ++- src/link/Dwarf.zig | 6 ++++++ src/link/Wasm.zig | 49 +++++++++++++++++++++++++++++++++++++++++- src/link/Wasm/Atom.zig | 5 +++++ 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/link.zig b/src/link.zig index 08c8446219..ebcec92ac1 100644 --- a/src/link.zig +++ b/src/link.zig @@ -485,8 +485,9 @@ pub const File = struct { .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl), .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl), .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl), + .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl), .plan9 => @panic("TODO: implement updateDeclLineNumber for plan9"), - .wasm, .spirv, .nvptx => {}, + .spirv, .nvptx => {}, } } diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 97fc090b9a..bf02c22f7b 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -735,6 +735,7 @@ pub fn initDeclState(self: *Dwarf, mod: *Module, decl: *Module.Decl) !DeclState const atom = switch (self.tag) { .elf => &decl.link.elf.dbg_info_atom, .macho => &decl.link.macho.dbg_info_atom, + .wasm => &decl.link.wasm.dbg_info_atom, else => unreachable, }; try decl_state.addTypeReloc( @@ -1250,6 +1251,10 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl) const file_pos = sect.offset + decl.fn_link.macho.off + self.getRelocDbgLineOff(); try d_sym.file.pwriteAll(&data, file_pos); }, + .wasm => { + const wasm_file = file.cast(File.Wasm).?; + _ = wasm_file; // TODO, update .debug_line + }, else => unreachable, } } @@ -1285,6 +1290,7 @@ pub fn freeDecl(self: *Dwarf, decl: *Module.Decl) void { const fn_link = switch (self.tag) { .elf => &decl.fn_link.elf, .macho => &decl.fn_link.macho, + .wasm => &decl.fn_link.wasm.src_fn, else => unreachable, }; _ = self.dbg_line_fn_free_list.remove(fn_link); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 4e898cee07..f4bd962016 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -11,6 +11,7 @@ const log = std.log.scoped(.link); const wasm = std.wasm; const Atom = @import("Wasm/Atom.zig"); +const Dwarf = @import("Dwarf.zig"); const Module = @import("../Module.zig"); const Compilation = @import("../Compilation.zig"); const CodeGen = @import("../arch/wasm/CodeGen.zig"); @@ -82,6 +83,8 @@ data_segments: std.StringArrayHashMapUnmanaged(u32) = .{}, segment_info: std.ArrayListUnmanaged(types.Segment) = .{}, /// Deduplicated string table for strings used by symbols, imports and exports. string_table: StringTable = .{}, +/// Debug information for wasm +dwarf: ?Dwarf = null, // Output sections /// Output type section @@ -137,10 +140,15 @@ pub const Segment = struct { }; pub const FnData = struct { + /// Reference to the wasm type that represents this function. type_index: u32, + /// Contains debug information related to this function. + /// For Wasm, the offset is relative to the code-section. + src_fn: Dwarf.SrcFn, pub const empty: FnData = .{ .type_index = undefined, + .src_fn = Dwarf.SrcFn.empty, }; }; @@ -318,6 +326,11 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { }, .name = undefined, }; + + if (!options.strip and options.module != null) { + self.dwarf = Dwarf.init(gpa, .wasm, options.target); + } + const use_llvm = build_options.have_llvm and options.use_llvm; const use_stage1 = build_options.is_stage1 and options.use_stage1; if (use_llvm and !use_stage1) { @@ -479,6 +492,10 @@ pub fn deinit(self: *Wasm) void { self.exports.deinit(gpa); self.string_table.deinit(gpa); + + if (self.dwarf) |*dwarf| { + dwarf.deinit(); + } } pub fn allocateDeclIndexes(self: *Wasm, decl_index: Module.Decl.Index) !void { @@ -515,12 +532,19 @@ pub fn updateFunc(self: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes if (build_options.have_llvm) { if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func, air, liveness); } + + const tracy = trace(@src()); + defer tracy.end(); + const decl_index = func.owner_decl; const decl = mod.declPtr(decl_index); assert(decl.link.wasm.sym_index != 0); // Must call allocateDeclIndexes() decl.link.wasm.clear(); + var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dwarf| try dwarf.initDeclState(mod, decl) else null; + defer if (decl_state) |*ds| ds.deinit(); + var code_writer = std.ArrayList(u8).init(self.base.allocator); defer code_writer.deinit(); const result = try codegen.generateFunction( @@ -530,7 +554,7 @@ pub fn updateFunc(self: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes air, liveness, &code_writer, - .none, + if (decl_state) |*ds| .{ .dwarf = ds } else .none, ); const code = switch (result) { @@ -555,6 +579,9 @@ pub fn updateDecl(self: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index); } + const tracy = trace(@src()); + defer tracy.end(); + const decl = mod.declPtr(decl_index); assert(decl.link.wasm.sym_index != 0); // Must call allocateDeclIndexes() @@ -596,6 +623,20 @@ pub fn updateDecl(self: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi return self.finishUpdateDecl(decl, code); } +pub fn updateDeclLineNumber(self: *Wasm, mod: *Module, decl: *const Module.Decl) !void { + if (self.llvm_object) |_| return; + if (self.dwarf) |*dw| { + const tracy = trace(@src()); + defer tracy.end(); + + const decl_name = try decl.getFullyQualifiedName(mod); + defer self.base.allocator.free(decl_name); + + log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl }); + try dw.updateDeclLineNumber(&self.base, decl); + } +} + fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void { if (code.len == 0) return; const mod = self.base.options.module.?; @@ -852,6 +893,12 @@ pub fn freeDecl(self: *Wasm, decl_index: Module.Decl.Index) void { } _ = self.resolved_symbols.swapRemove(atom.symbolLoc()); _ = self.symbol_atom.remove(atom.symbolLoc()); + + if (self.dwarf) |*dwarf| { + dwarf.freeDecl(decl); + dwarf.freeAtom(&atom.dbg_info_atom); + } + atom.deinit(self.base.allocator); } diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index fc45648d9a..f56a0995bf 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -4,6 +4,7 @@ const std = @import("std"); const types = @import("types.zig"); const Wasm = @import("../Wasm.zig"); const Symbol = @import("Symbol.zig"); +const Dwarf = @import("../Dwarf.zig"); const leb = std.leb; const log = std.log.scoped(.link); @@ -38,6 +39,9 @@ prev: ?*Atom, /// When the parent atom is being freed, it will also do so for all local atoms. locals: std.ArrayListUnmanaged(Atom) = .{}, +/// Represents the debug Atom that holds all debug information of this Atom. +dbg_info_atom: Dwarf.Atom, + /// Represents a default empty wasm `Atom` pub const empty: Atom = .{ .alignment = 0, @@ -47,6 +51,7 @@ pub const empty: Atom = .{ .prev = null, .size = 0, .sym_index = 0, + .dbg_info_atom = undefined, }; /// Frees all resources owned by this `Atom`. From 33b2f4f382234c95e8e10cf1b17d1621bb402bf1 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 30 Apr 2022 20:52:13 +0200 Subject: [PATCH 1418/2031] wasm: Implement debug info for parameters --- lib/std/dwarf/OP.zig | 6 +++++ src/arch/wasm/CodeGen.zig | 50 ++++++++++++++++++++++++++++++++++++++- src/link/Wasm.zig | 2 +- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/lib/std/dwarf/OP.zig b/lib/std/dwarf/OP.zig index 57fb7b7532..678ef39396 100644 --- a/lib/std/dwarf/OP.zig +++ b/lib/std/dwarf/OP.zig @@ -205,3 +205,9 @@ pub const HP_unmod_range = 0xe5; pub const HP_tls = 0xe6; // PGI (STMicroelectronics) extensions. pub const PGI_omp_thread_num = 0xf8; +// Wasm extensions. +pub const WASM_location = 0xed; +pub const WASM_local = 0x00; +pub const WASM_global = 0x01; +pub const WASM_global_u32 = 0x03; +pub const WASM_operand_stack = 0x02; diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 9318b4ecca..1288721e07 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -546,6 +546,8 @@ block_depth: u32 = 0, air: Air, liveness: Liveness, gpa: mem.Allocator, +debug_output: codegen.DebugInfoOutput, +mod_fn: *const Module.Fn, /// Table to save `WValue`'s generated by an `Air.Inst` values: ValueTable, /// Mapping from Air.Inst.Index to block ids @@ -856,6 +858,8 @@ pub fn generate( .locals = .{}, .target = bin_file.options.target, .bin_file = bin_file.cast(link.File.Wasm).?, + .debug_output = debug_output, + .mod_fn = func, }; defer code_gen.deinit(); @@ -1022,6 +1026,23 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool } } +/// For a given `Type`, add debug information to .debug_info at the current position. +/// The actual bytes will be written to the position after relocation. +fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { + switch (self.debug_output) { + .dwarf => |dwarf| { + assert(ty.hasRuntimeBitsIgnoreComptime()); + const dbg_info = &dwarf.dbg_info; + const index = dbg_info.items.len; + try dbg_info.resize(index + 4); + const atom = &self.decl.link.wasm.dbg_info_atom; + try dwarf.addTypeReloc(atom, ty, @intCast(u32, index), null); + }, + .plan9 => unreachable, + .none => {}, + } +} + /// Lowers a Zig type and its value based on a given calling convention to ensure /// it matches the ABI. fn lowerArg(self: *Self, cc: std.builtin.CallingConvention, ty: Type, value: WValue) !void { @@ -1873,7 +1894,8 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { } fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { - const arg = self.args[self.arg_index]; + const arg_index = self.arg_index; + const arg = self.args[arg_index]; const cc = self.decl.ty.fnInfo().cc; if (cc == .C) { const ty = self.air.typeOfIndex(inst); @@ -1886,6 +1908,32 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } else { self.arg_index += 1; } + + switch (self.debug_output) { + .dwarf => |dwarf| { + // TODO: Get the original arg index rather than wasm arg index + const name = self.mod_fn.getParamName(arg_index); + const leb_size = link.File.Wasm.getULEB128Size(arg.local); + const dbg_info = &dwarf.dbg_info; + try dbg_info.ensureUnusedCapacity(3 + leb_size + 5 + name.len + 1); + // wasm locations are encoded as follow: + // DW_OP_WASM_location wasm-op + // where wasm-op is defined as + // wasm-op := wasm-local | wasm-global | wasm-operand_stack + // where each argument is encoded as + // i:uleb128 + dbg_info.appendSliceAssumeCapacity(&.{ + @enumToInt(link.File.Dwarf.AbbrevKind.parameter), + std.dwarf.OP.WASM_location, + std.dwarf.OP.WASM_local, + }); + leb.writeULEB128(dbg_info.writer(), arg.local) catch unreachable; + try self.addDbgInfoTypeReloc(self.air.typeOfIndex(inst)); + dbg_info.appendSliceAssumeCapacity(name); + dbg_info.appendAssumeCapacity(0); + }, + else => {}, + } return arg; } diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index f4bd962016..f9c40fd767 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2668,7 +2668,7 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { try file.writevAll(&iovecs); } -fn getULEB128Size(uint_value: anytype) u32 { +pub fn getULEB128Size(uint_value: anytype) u32 { const T = @TypeOf(uint_value); const U = if (@typeInfo(T).Int.bits < 8) u8 else T; var value = @intCast(U, uint_value); From d46cdb539620a2f3647507ffed519908a1c31647 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 1 May 2022 16:32:48 +0200 Subject: [PATCH 1419/2031] wasm: Debug information for locals Implements very basic debug information for locals. For now it only implements debug info when the variable is stored within a Wasm local. The goal is to support those that live in the data section (virtual stack). --- src/arch/wasm/CodeGen.zig | 62 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 1288721e07..9c90aa0afa 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1478,15 +1478,17 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .get_union_tag => self.airGetUnionTag(inst), // TODO - .dbg_stmt, .dbg_inline_begin, .dbg_inline_end, .dbg_block_begin, .dbg_block_end, - .dbg_var_ptr, - .dbg_var_val, => WValue.none, + .dbg_var_ptr => self.airDbgVar(inst, true), + .dbg_var_val => self.airDbgVar(inst, false), + + .dbg_stmt => self.airDbgStmt(inst), + .call => self.airCall(inst, .auto), .call_always_tail => self.airCall(inst, .always_tail), .call_never_tail => self.airCall(inst, .never_tail), @@ -4277,3 +4279,57 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addLabel(.local_set, result.local); return result; } + +fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !WValue { + if (self.debug_output != .dwarf) return WValue{ .none = {} }; + + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const ty = self.air.typeOf(pl_op.operand); + const operand = try self.resolveInst(pl_op.operand); + const op_ty = if (is_ptr) ty.childType() else ty; + + log.debug("airDbgVar: %{d}: {}, {}", .{ inst, op_ty.fmtDebug(), operand }); + + const name = self.air.nullTerminatedString(pl_op.payload); + log.debug(" var name = ({s})", .{name}); + + const dbg_info = &self.debug_output.dwarf.dbg_info; + try dbg_info.append(@enumToInt(link.File.Dwarf.AbbrevKind.variable)); + switch (operand) { + .local => |local| { + const leb_size = link.File.Wasm.getULEB128Size(local); + try dbg_info.ensureUnusedCapacity(2 + leb_size); + // wasm locals are encoded as follow: + // DW_OP_WASM_location wasm-op + // where wasm-op is defined as + // wasm-op := wasm-local | wasm-global | wasm-operand_stack + // where wasm-local is encoded as + // wasm-local := 0x00 i:uleb128 + dbg_info.appendSliceAssumeCapacity(&.{ + std.dwarf.OP.WASM_location, + std.dwarf.OP.WASM_local, + }); + leb.writeULEB128(dbg_info.writer(), local) catch unreachable; + }, + else => {}, // TODO + } + + try dbg_info.ensureUnusedCapacity(5 + name.len + 1); + try self.addDbgInfoTypeReloc(op_ty); + dbg_info.appendSliceAssumeCapacity(name); + dbg_info.appendAssumeCapacity(0); + try return WValue{ .none = {} }; +} + +fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !WValue { + if (self.debug_output != .dwarf) return WValue{ .none = {} }; + + const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; + try self.addInst(.{ .tag = .dbg_line, .data = .{ + .payload = try self.addExtra(Mir.DbgLineColumn{ + .line = dbg_stmt.line, + .column = dbg_stmt.column, + }), + } }); + return WValue{ .none = {} }; +} From f76027220042d965278184088f1f04b6c8430bcb Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 1 May 2022 16:34:05 +0200 Subject: [PATCH 1420/2031] wasm: Debug info for lines + pro/epilogue Maps lines and columns between wasm bytecode and Zig source code. While this supports prologue and epilogue information, we need to add support for performing relocations as the offsets are relative to the code section, which means we must relocate it according to the atom offset's offset while keeping function count in mind as well (due to leb128 encoding). --- src/arch/wasm/CodeGen.zig | 8 ++++++ src/arch/wasm/Emit.zig | 55 +++++++++++++++++++++++++++++++++++++++ src/arch/wasm/Mir.zig | 19 ++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 9c90aa0afa..cb2578bee9 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -882,6 +882,8 @@ fn genFunc(self: *Self) InnerError!void { self.args = cc_result.args; self.return_value = cc_result.return_value; + try self.addTag(.dbg_prologue_end); + // Generate MIR for function body try self.genBody(self.air.getMainBody()); // In case we have a return value, but the last instruction is a noreturn (such as a while loop) @@ -896,6 +898,8 @@ fn genFunc(self: *Self) InnerError!void { // End of function body try self.addTag(.end); + try self.addTag(.dbg_epilogue_begin); + // check if we have to initialize and allocate anything into the stack frame. // If so, create enough stack space and insert the instructions at the front of the list. if (self.stack_size > 0) { @@ -942,6 +946,10 @@ fn genFunc(self: *Self) InnerError!void { .code = self.code, .locals = self.locals.items, .decl = self.decl, + .dbg_output = self.debug_output, + .prev_di_line = 0, + .prev_di_column = 0, + .prev_di_offset = 0, }; emit.emitMir() catch |err| switch (err) { diff --git a/src/arch/wasm/Emit.zig b/src/arch/wasm/Emit.zig index bcbff8d195..0081dc4d5d 100644 --- a/src/arch/wasm/Emit.zig +++ b/src/arch/wasm/Emit.zig @@ -6,6 +6,7 @@ const std = @import("std"); const Mir = @import("Mir.zig"); const link = @import("../../link.zig"); const Module = @import("../../Module.zig"); +const codegen = @import("../../codegen.zig"); const leb128 = std.leb; /// Contains our list of instructions @@ -22,6 +23,16 @@ locals: []const u8, /// The declaration that code is being generated for. decl: *Module.Decl, +// Debug information +/// Holds the debug information for this emission +dbg_output: codegen.DebugInfoOutput, +/// Previous debug info line +prev_di_line: u32, +/// Previous debug info column +prev_di_column: u32, +/// Previous offset relative to code section +prev_di_offset: u32, + const InnerError = error{ OutOfMemory, EmitFail, @@ -40,6 +51,10 @@ pub fn emitMir(emit: *Emit) InnerError!void { .block => try emit.emitBlock(tag, inst), .loop => try emit.emitBlock(tag, inst), + .dbg_line => try emit.emitDbgLine(inst), + .dbg_epilogue_begin => try emit.emitDbgEpilogueBegin(), + .dbg_prologue_end => try emit.emitDbgPrologueEnd(), + // branch instructions .br_if => try emit.emitLabel(tag, inst), .br_table => try emit.emitBrTable(inst), @@ -419,3 +434,43 @@ fn emitMemFill(emit: *Emit) !void { // For now we will always emit index 0. try leb128.writeULEB128(emit.code.writer(), @as(u32, 0)); } + +fn emitDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { + const extra_index = emit.mir.instructions.items(.data)[inst].payload; + const dbg_line = emit.mir.extraData(Mir.DbgLineColumn, extra_index).data; + try emit.dbgAdvancePCAndLine(dbg_line.line, dbg_line.column); +} + +fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void { + if (emit.dbg_output != .dwarf) return; + + const dbg_line = &emit.dbg_output.dwarf.dbg_line; + try dbg_line.ensureUnusedCapacity(11); + dbg_line.appendAssumeCapacity(std.dwarf.LNS.advance_pc); + // TODO: This must emit a relocation to calculate the offset relative + // to the code section start. + leb128.writeULEB128(dbg_line.writer(), emit.offset() - emit.prev_di_offset) catch unreachable; + const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line); + if (delta_line != 0) { + dbg_line.appendAssumeCapacity(std.dwarf.LNS.advance_line); + leb128.writeILEB128(dbg_line.writer(), delta_line) catch unreachable; + } + dbg_line.appendAssumeCapacity(std.dwarf.LNS.copy); + emit.prev_di_line = line; + emit.prev_di_column = column; + emit.prev_di_offset = emit.offset(); +} + +fn emitDbgPrologueEnd(emit: *Emit) !void { + if (emit.dbg_output != .dwarf) return; + + try emit.dbg_output.dwarf.dbg_line.append(std.dwarf.LNS.set_prologue_end); + try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); +} + +fn emitDbgEpilogueBegin(emit: *Emit) !void { + if (emit.dbg_output != .dwarf) return; + + try emit.dbg_output.dwarf.dbg_line.append(std.dwarf.LNS.set_epilogue_begin); + try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); +} diff --git a/src/arch/wasm/Mir.zig b/src/arch/wasm/Mir.zig index 87e64ce9e0..6cf43c1e03 100644 --- a/src/arch/wasm/Mir.zig +++ b/src/arch/wasm/Mir.zig @@ -47,6 +47,19 @@ pub const Inst = struct { /// /// Type of the loop is given in data `block_type` loop = 0x03, + /// Inserts debug information about the current line and column + /// of the source code + /// + /// Uses `payload` of which the payload type is `DbgLineColumn` + dbg_line = 0x06, + /// Emits epilogue begin debug information + /// + /// Uses `nop` + dbg_epilogue_begin = 0x07, + /// Emits prologue end debug information + /// + /// Uses `nop` + dbg_prologue_end = 0x08, /// Represents the end of a function body or an initialization expression /// /// Payload is `nop` @@ -645,3 +658,9 @@ pub const Memory = struct { pointer: u32, offset: u32, }; + +/// Maps a source line with wasm bytecode +pub const DbgLineColumn = struct { + line: u32, + column: u32, +}; From 9b6b7034c20a0089b88e58b5142c91b2d26bcef8 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Mon, 2 May 2022 21:41:47 +0200 Subject: [PATCH 1421/2031] wasm: Flush debug information + commit decl This implements parts to commit a decl's debug information into a linear memory buffer. The goal is to write this buffer at once after we finished linking. --- src/link/Dwarf.zig | 29 ++++++++++++++++++++++++++- src/link/Wasm.zig | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index bf02c22f7b..bf054c524c 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -818,6 +818,7 @@ pub fn commitDeclState( const src_fn = switch (self.tag) { .elf => &decl.fn_link.elf, .macho => &decl.fn_link.macho, + .wasm => &decl.fn_link.wasm.src_fn, else => unreachable, // TODO }; src_fn.len = @intCast(u32, dbg_line_buffer.items.len); @@ -958,6 +959,26 @@ pub fn commitDeclState( next_padding_size, ); }, + .wasm => { + const wasm_file = file.cast(File.Wasm).?; + const segment_index = try wasm_file.getDebugLineIndex(); + const segment = &wasm_file.segments.items[segment_index]; + const debug_atom = wasm_file.atoms.get(segment_index).?; + if (needed_size != segment.size) { + log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); + if (needed_size > segment.size) { + log.debug(" allocating {d} bytes for debug line information", .{needed_size - segment.size}); + try debug_atom.code.resize(self.allocator, needed_size); + std.mem.set(u8, debug_atom.code.items[segment.size..], 0); + } + debug_atom.size = needed_size; + segment.size = needed_size; + } + // since we can tighly pack the debug lines, wasm does not require + // us to pad with Nops. + const offset = segment.offset + src_fn.off; + std.mem.copy(u8, debug_atom.code.items[offset..], dbg_line_buffer.items); + }, else => unreachable, } @@ -973,6 +994,7 @@ pub fn commitDeclState( const atom = switch (self.tag) { .elf => &decl.link.elf.dbg_info_atom, .macho => &decl.link.macho.dbg_info_atom, + .wasm => &decl.link.wasm.dbg_info_atom, else => unreachable, }; @@ -1094,6 +1116,7 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3 const file_pos = debug_info_sect.offset + atom.off; try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false); }, + .wasm => {}, else => unreachable, } // TODO Look at the free list before appending at the end. @@ -1253,7 +1276,11 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl) }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - _ = wasm_file; // TODO, update .debug_line + const segment_index = wasm_file.getDebugLineIndex() catch unreachable; + const segment = wasm_file.segments.items[segment_index]; + const offset = segment.offset + decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff(); + const debug_atom = wasm_file.atoms.get(segment_index).?; + std.mem.copy(u8, debug_atom.code.items[offset..], &data); }, else => unreachable, } diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index f9c40fd767..d2ef331a3d 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -62,6 +62,10 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, /// Represents the index into `segments` where the 'code' section /// lives. code_section_index: ?u32 = null, +/// The index of the segment representing the custom '.debug_info' section. +debug_info_index: ?u32 = null, +/// The index of the segment representing the custom '.debug_line' section. +debug_line_index: ?u32 = null, /// The count of imported functions. This number will be appended /// to the function indexes as their index starts at the lowest non-extern function. imported_functions_count: u32 = 0, @@ -311,6 +315,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option .init = .{ .i32_const = 0 }, }; } + return wasm_bin; } @@ -566,6 +571,17 @@ pub fn updateFunc(self: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes }, }; + if (self.dwarf) |*dwarf| { + try dwarf.commitDeclState( + &self.base, + mod, + decl, + // Actual value will be written after relocation + 0, + code.len, + &decl_state.?, + ); + } return self.finishUpdateDecl(decl, code); } @@ -1517,6 +1533,35 @@ fn populateErrorNameTable(self: *Wasm) !void { try self.parseAtom(names_atom, .data); } +pub fn getDebugInfoIndex(self: *Wasm) !u32 { + assert(self.dwarf != null); + return self.debug_info_index orelse { + self.debug_info_index = @intCast(u32, self.segments.items.len); + const segment = try self.segments.addOne(self.base.allocator); + segment.* = .{ + .size = 0, + .offset = 0, + // debug sections always have alignment '1' + .alignment = 1, + }; + return self.debug_info_index.?; + }; +} + +pub fn getDebugLineIndex(self: *Wasm) !u32 { + assert(self.dwarf != null); + return self.debug_line_index orelse { + self.debug_line_index = @intCast(u32, self.segments.items.len); + const segment = try self.segments.addOne(self.base.allocator); + segment.* = .{ + .size = 0, + .offset = 0, + .alignment = 1, + }; + return self.debug_line_index.?; + }; +} + fn resetState(self: *Wasm) void { for (self.segment_info.items) |*segment_info| { self.base.allocator.free(segment_info.name); @@ -1542,6 +1587,7 @@ fn resetState(self: *Wasm) void { self.atoms.clearRetainingCapacity(); self.symbol_atom.clearRetainingCapacity(); self.code_section_index = null; + self.debug_info_index = null; } pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { @@ -1636,6 +1682,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try self.objects.items[object_index].parseIntoAtoms(self.base.allocator, object_index, self); } + if (self.dwarf) |*dwarf| { + try dwarf.flushModule(&self.base, self.base.options.module.?); + } try self.allocateAtoms(); try self.setupMemory(); self.mapFunctionTable(); From 2ae2ac33d9ddd1fb181e08a811d97b1bf238bced Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 4 May 2022 21:25:33 +0200 Subject: [PATCH 1422/2031] wasm: Emit debug sections This commit adds the ability to emit the following debug sections: .debug_info .debug_abbrev .debug_line .debug_str Line information and files are now being loaded correctly by browser debuggers. --- src/link/Dwarf.zig | 56 ++++++++++++++++++++++++++++++-------- src/link/Wasm.zig | 61 +++++++++++++++++++++++++++++++++++++----- src/link/Wasm/Atom.zig | 9 ------- 3 files changed, 99 insertions(+), 27 deletions(-) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index bf054c524c..a7d2744491 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -963,21 +963,18 @@ pub fn commitDeclState( const wasm_file = file.cast(File.Wasm).?; const segment_index = try wasm_file.getDebugLineIndex(); const segment = &wasm_file.segments.items[segment_index]; - const debug_atom = wasm_file.atoms.get(segment_index).?; + const debug_line = &wasm_file.debug_line; if (needed_size != segment.size) { log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); if (needed_size > segment.size) { - log.debug(" allocating {d} bytes for debug line information", .{needed_size - segment.size}); - try debug_atom.code.resize(self.allocator, needed_size); - std.mem.set(u8, debug_atom.code.items[segment.size..], 0); + log.debug(" allocating {d} bytes for 'debug line' information", .{needed_size - segment.size}); + try debug_line.resize(self.allocator, needed_size); + mem.set(u8, debug_line.items[segment.size..], 0); } - debug_atom.size = needed_size; segment.size = needed_size; } - // since we can tighly pack the debug lines, wasm does not require - // us to pad with Nops. const offset = segment.offset + src_fn.off; - std.mem.copy(u8, debug_atom.code.items[offset..], dbg_line_buffer.items); + mem.copy(u8, debug_line.items[offset..], dbg_line_buffer.items); }, else => unreachable, } @@ -1116,7 +1113,9 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3 const file_pos = debug_info_sect.offset + atom.off; try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false); }, - .wasm => {}, + .wasm => { + log.debug(" todo: updateDeclDebugInfoAllocation for Wasm: {d}", .{atom.len}); + }, else => unreachable, } // TODO Look at the free list before appending at the end. @@ -1241,6 +1240,23 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co trailing_zero, ); }, + .wasm => { + const wasm_file = file.cast(File.Wasm).?; + const segment_index = try wasm_file.getDebugInfoIndex(); + const segment = &wasm_file.segments.items[segment_index]; + const debug_info = &wasm_file.debug_info; + if (needed_size != segment.size) { + log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); + if (needed_size > segment.size) { + log.debug(" allocating {d} bytes for 'debug info' information", .{needed_size - segment.size}); + try debug_info.resize(self.allocator, needed_size); + mem.set(u8, debug_info.items[segment.size..], 0); + } + segment.size = needed_size; + } + const offset = segment.offset + atom.off; + mem.copy(u8, debug_info.items[offset..], dbg_info_buf); + }, else => unreachable, } } @@ -1279,8 +1295,7 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl) const segment_index = wasm_file.getDebugLineIndex() catch unreachable; const segment = wasm_file.segments.items[segment_index]; const offset = segment.offset + decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff(); - const debug_atom = wasm_file.atoms.get(segment_index).?; - std.mem.copy(u8, debug_atom.code.items[offset..], &data); + mem.copy(u8, wasm_file.debug_line.items[offset..], &data); }, else => unreachable, } @@ -1514,6 +1529,11 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { const file_pos = debug_abbrev_sect.offset + abbrev_offset; try d_sym.file.pwriteAll(&abbrev_buf, file_pos); }, + .wasm => { + const wasm_file = file.cast(File.Wasm).?; + try wasm_file.debug_abbrev.resize(wasm_file.base.allocator, needed_size); + mem.copy(u8, wasm_file.debug_abbrev.items, &abbrev_buf); + }, else => unreachable, } } @@ -1621,6 +1641,10 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6 const file_pos = debug_info_sect.offset; try pwriteDbgInfoNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt, false); }, + .wasm => { + const wasm_file = file.cast(File.Wasm).?; + mem.copy(u8, wasm_file.debug_info.items, di_buf.items); + }, else => unreachable, } } @@ -2004,6 +2028,10 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void { const file_pos = debug_line_sect.offset; try pwriteDbgLineNops(d_sym.file, file_pos, 0, di_buf.items, jmp_amt); }, + .wasm => { + const wasm_file = file.cast(File.Wasm).?; + mem.copy(u8, wasm_file.debug_line.items, di_buf.items); + }, else => unreachable, } } @@ -2127,6 +2155,8 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void { const debug_info_sect = &dwarf_segment.sections.items[d_sym.debug_info_section_index.?]; break :blk debug_info_sect.offset; }, + // for wasm, the offset is always 0 as we write to memory first + .wasm => break :blk @as(u32, 0), else => unreachable, } }; @@ -2145,6 +2175,10 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void { const d_sym = &macho_file.d_sym.?; try d_sym.file.pwriteAll(&buf, file_pos + reloc.atom.off + reloc.offset); }, + .wasm => { + const wasm_file = file.cast(File.Wasm).?; + mem.copy(u8, wasm_file.debug_info.items[reloc.atom.off + reloc.offset ..], &buf); + }, else => unreachable, } } diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index d2ef331a3d..4cc21260c3 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -90,6 +90,14 @@ string_table: StringTable = .{}, /// Debug information for wasm dwarf: ?Dwarf = null, +// *debug information* // +/// Contains all bytes for the '.debug_info' section +debug_info: std.ArrayListUnmanaged(u8) = .{}, +/// Contains all bytes for the '.debug_line' section +debug_line: std.ArrayListUnmanaged(u8) = .{}, +/// Contains all bytes for the '.debug_abbrev' section +debug_abbrev: std.ArrayListUnmanaged(u8) = .{}, + // Output sections /// Output type section func_types: std.ArrayListUnmanaged(wasm.Type) = .{}, @@ -501,6 +509,10 @@ pub fn deinit(self: *Wasm) void { if (self.dwarf) |*dwarf| { dwarf.deinit(); } + + self.debug_info.deinit(gpa); + self.debug_line.deinit(gpa); + self.debug_abbrev.deinit(gpa); } pub fn allocateDeclIndexes(self: *Wasm, decl_index: Module.Decl.Index) !void { @@ -576,7 +588,9 @@ pub fn updateFunc(self: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes &self.base, mod, decl, - // Actual value will be written after relocation + // Actual value will be written after relocation. + // For Wasm, this is the offset relative to the code section + // which isn't known until flush(). 0, code.len, &decl_state.?, @@ -1016,10 +1030,12 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { // segment indexes can be off by 1 due to also containing a segment // for the code section, so we must check if the existing segment // is larger than that of the code section, and substract the index by 1 in such case. - const info_add = if (self.code_section_index) |idx| blk: { + var info_add = if (self.code_section_index) |idx| blk: { if (idx < index) break :blk @as(u32, 1); break :blk 0; } else @as(u32, 0); + if (self.debug_info_index != null) info_add += 1; + if (self.debug_line_index != null) info_add += 1; symbol.index = index - info_add; // segment info already exists, so free its memory self.base.allocator.free(segment_name); @@ -1320,11 +1336,8 @@ fn setupMemory(self: *Wasm) !void { } var offset: u32 = @intCast(u32, memory_ptr); - for (self.segments.items) |*segment, i| { - // skip 'code' segments - if (self.code_section_index) |index| { - if (index == i) continue; - } + for (self.data_segments.values()) |segment_index| { + const segment = &self.segments.items[segment_index]; memory_ptr = std.mem.alignForwardGeneric(u64, memory_ptr, segment.alignment); memory_ptr += segment.size; segment.offset = offset; @@ -1588,6 +1601,7 @@ fn resetState(self: *Wasm) void { self.symbol_atom.clearRetainingCapacity(); self.code_section_index = null; self.debug_info_index = null; + self.debug_line_index = null; } pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { @@ -2016,10 +2030,43 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try self.emitDataRelocations(file, arena, data_index, symbol_table); } } else if (!self.base.options.strip) { + if (self.dwarf) |*dwarf| { + if (self.debug_info_index != null) { + _ = dwarf; + try dwarf.writeDbgAbbrev(&self.base); + try dwarf.writeDbgInfoHeader(&self.base, mod, 0, 0); + try dwarf.writeDbgLineHeader(&self.base, mod); + + try emitDebugSection(file, self.debug_info.items, ".debug_info"); + try emitDebugSection(file, self.debug_abbrev.items, ".debug_abbrev"); // TODO + try emitDebugSection(file, self.debug_line.items, ".debug_line"); + try emitDebugSection(file, dwarf.strtab.items, ".debug_str"); + } + } try self.emitNameSection(file, arena); } } +fn emitDebugSection(file: fs.File, data: []const u8, name: []const u8) !void { + const header_offset = try reserveCustomSectionHeader(file); + const writer = file.writer(); + try leb.writeULEB128(writer, @intCast(u32, name.len)); + try writer.writeAll(name); + + try file.writevAll(&[_]std.os.iovec_const{.{ + .iov_base = data.ptr, + .iov_len = data.len, + }}); + const start = header_offset + 6 + name.len + getULEB128Size(@intCast(u32, name.len)); + log.debug("Emit debug section: '{s}' start=0x{x:0>8} end=0x{x:0>8}", .{ name, start, start + data.len }); + + try writeCustomSectionHeader( + file, + header_offset, + @intCast(u32, (try file.getPos()) - header_offset - 6), + ); +} + fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { const Name = struct { index: u32, diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index f56a0995bf..6f65b1b83a 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -90,15 +90,6 @@ pub fn getFirst(self: *Atom) *Atom { return tmp; } -/// Returns the atom for the given `symbol_index`. -/// This can be either the `Atom` itself, or one of its locals. -pub fn symbolAtom(self: *Atom, symbol_index: u32) *Atom { - if (self.sym_index == symbol_index) return self; - return for (self.locals.items) |*local_atom| { - if (local_atom.sym_index == symbol_index) break local_atom; - } else unreachable; // Used a symbol index not present in this atom or its children. -} - /// Returns the location of the symbol that represents this `Atom` pub fn symbolLoc(self: Atom) Wasm.SymbolLoc { return .{ .file = self.file, .index = self.sym_index }; From 62453496bac17f13e1e129de5cf08accddc02302 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 8 May 2022 17:16:58 +0200 Subject: [PATCH 1423/2031] wasm: Write nops for padding debug info --- src/arch/wasm/CodeGen.zig | 6 +-- src/link/Dwarf.zig | 106 ++++++++++++++++++++++++++++++++++++-- src/link/Wasm.zig | 17 ++++-- test/behavior/ptrcast.zig | 2 + 4 files changed, 119 insertions(+), 12 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index cb2578bee9..947174aaed 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1362,7 +1362,6 @@ fn isByRef(ty: Type, target: std.Target) bool { .NoReturn, .Void, .Bool, - .Float, .ErrorSet, .Fn, .Enum, @@ -1375,7 +1374,8 @@ fn isByRef(ty: Type, target: std.Target) bool { .Frame, .Union, => return ty.hasRuntimeBitsIgnoreComptime(), - .Int => return if (ty.intInfo(target).bits > 64) true else false, + .Int => return ty.intInfo(target).bits > 64, + .Float => return ty.floatBits(target) > 64, .ErrorUnion => { const has_tag = ty.errorUnionSet().hasRuntimeBitsIgnoreComptime(); const has_pl = ty.errorUnionPayload().hasRuntimeBitsIgnoreComptime(); @@ -4326,7 +4326,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !WValue { try self.addDbgInfoTypeReloc(op_ty); dbg_info.appendSliceAssumeCapacity(name); dbg_info.appendAssumeCapacity(0); - try return WValue{ .none = {} }; + return WValue{ .none = {} }; } fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !WValue { diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index a7d2744491..a204dd91ae 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -851,6 +851,10 @@ pub fn commitDeclState( const file_pos = debug_line_sect.offset + src_fn.off; try pwriteDbgLineNops(d_sym.file, file_pos, 0, &[0]u8{}, src_fn.len); }, + .wasm => { + const wasm_file = file.cast(File.Wasm).?; + writeDbgLineNopsBuffered(wasm_file.debug_line.items, src_fn.off, 0, &.{}, src_fn.len); + }, else => unreachable, } // TODO Look at the free list before appending at the end. @@ -972,9 +976,16 @@ pub fn commitDeclState( mem.set(u8, debug_line.items[segment.size..], 0); } segment.size = needed_size; + debug_line.items.len = needed_size; } const offset = segment.offset + src_fn.off; - mem.copy(u8, debug_line.items[offset..], dbg_line_buffer.items); + writeDbgLineNopsBuffered( + debug_line.items, + offset, + prev_padding_size, + dbg_line_buffer.items, + next_padding_size, + ); }, else => unreachable, } @@ -1114,7 +1125,8 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3 try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false); }, .wasm => { - log.debug(" todo: updateDeclDebugInfoAllocation for Wasm: {d}", .{atom.len}); + const wasm_file = file.cast(File.Wasm).?; + writeDbgInfoNopsBuffered(wasm_file.debug_info.items, atom.off, 0, &.{0}, atom.len, false); }, else => unreachable, } @@ -1253,9 +1265,17 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co mem.set(u8, debug_info.items[segment.size..], 0); } segment.size = needed_size; + debug_info.items.len = needed_size; } const offset = segment.offset + atom.off; - mem.copy(u8, debug_info.items[offset..], dbg_info_buf); + writeDbgInfoNopsBuffered( + debug_info.items, + offset, + prev_padding_size, + dbg_info_buf, + next_padding_size, + trailing_zero, + ); }, else => unreachable, } @@ -1643,7 +1663,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6 }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - mem.copy(u8, wasm_file.debug_info.items, di_buf.items); + writeDbgInfoNopsBuffered(wasm_file.debug_info.items, 0, 0, di_buf.items, jmp_amt, false); }, else => unreachable, } @@ -1738,6 +1758,45 @@ fn pwriteDbgLineNops( try file.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); } +fn writeDbgLineNopsBuffered( + buf: []u8, + offset: u32, + prev_padding_size: usize, + content: []const u8, + next_padding_size: usize, +) void { + assert(buf.len >= content.len + prev_padding_size + next_padding_size); + const tracy = trace(@src()); + defer tracy.end(); + + const three_byte_nop = [3]u8{ DW.LNS.advance_pc, 0b1000_0000, 0 }; + { + var padding_left = prev_padding_size; + if (padding_left % 2 != 0) { + buf[offset - padding_left ..][0..3].* = three_byte_nop; + padding_left -= 3; + } + + while (padding_left > 0) : (padding_left -= 1) { + buf[offset - padding_left] = DW.LNS.negate_stmt; + } + } + + mem.copy(u8, buf[offset..], content); + + { + var padding_left = next_padding_size; + if (padding_left % 2 != 0) { + buf[offset + content.len + padding_left ..][0..3].* = three_byte_nop; + padding_left -= 3; + } + + while (padding_left > 0) : (padding_left -= 1) { + buf[offset + content.len + padding_left] = DW.LNS.negate_stmt; + } + } +} + /// Writes to the file a buffer, prefixed and suffixed by the specified number of /// bytes of padding. fn pwriteDbgInfoNops( @@ -1810,6 +1869,38 @@ fn pwriteDbgInfoNops( try file.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); } +fn writeDbgInfoNopsBuffered( + buf: []u8, + offset: u32, + prev_padding_size: usize, + content: []const u8, + next_padding_size: usize, + trailing_zero: bool, +) void { + assert(buf.len >= content.len + prev_padding_size + next_padding_size + @boolToInt(trailing_zero)); + const tracy = trace(@src()); + defer tracy.end(); + + { + var padding_left = prev_padding_size; + while (padding_left > 0) : (padding_left -= 1) { + buf[offset - padding_left] = @enumToInt(AbbrevKind.pad1); + } + } + + mem.copy(u8, buf[offset..], content); + { + var padding_left = next_padding_size; + while (padding_left > 0) : (padding_left -= 1) { + buf[offset + content.len + padding_left] = @enumToInt(AbbrevKind.pad1); + } + } + + if (trailing_zero) { + buf[offset + content.len + next_padding_size] = 0; + } +} + pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void { const target_endian = self.target.cpu.arch.endian(); const init_len_size: usize = if (self.tag == .macho) @@ -1909,6 +2000,11 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void { const file_pos = debug_aranges_sect.offset; try d_sym.file.pwriteAll(di_buf.items, file_pos); }, + .wasm => { + const wasm_file = file.cast(File.Wasm).?; + try wasm_file.debug_aranges.resize(wasm_file.base.allocator, needed_size); + mem.copy(u8, wasm_file.debug_aranges.items, di_buf.items); + }, else => unreachable, } } @@ -2030,7 +2126,7 @@ pub fn writeDbgLineHeader(self: *Dwarf, file: *File, module: *Module) !void { }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - mem.copy(u8, wasm_file.debug_line.items, di_buf.items); + writeDbgLineNopsBuffered(wasm_file.debug_line.items, 0, 0, di_buf.items, jmp_amt); }, else => unreachable, } diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 4cc21260c3..d8c87703a6 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -97,6 +97,8 @@ debug_info: std.ArrayListUnmanaged(u8) = .{}, debug_line: std.ArrayListUnmanaged(u8) = .{}, /// Contains all bytes for the '.debug_abbrev' section debug_abbrev: std.ArrayListUnmanaged(u8) = .{}, +/// Contains all bytes for the '.debug_ranges' section +debug_aranges: std.ArrayListUnmanaged(u8) = .{}, // Output sections /// Output type section @@ -513,6 +515,7 @@ pub fn deinit(self: *Wasm) void { self.debug_info.deinit(gpa); self.debug_line.deinit(gpa); self.debug_abbrev.deinit(gpa); + self.debug_aranges.deinit(gpa); } pub fn allocateDeclIndexes(self: *Wasm, decl_index: Module.Decl.Index) !void { @@ -1928,6 +1931,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } // Code section + var code_section_size: u32 = 0; if (self.code_section_index) |code_index| { const header_offset = try reserveVecSectionHeader(file); const writer = file.writer(); @@ -1940,11 +1944,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try writer.writeAll(atom.code.items); atom = atom.next orelse break; } + + code_section_size = @intCast(u32, (try file.getPos()) - header_offset - header_size); try writeVecSectionHeader( file, header_offset, .code, - @intCast(u32, (try file.getPos()) - header_offset - header_size), + code_section_size, @intCast(u32, self.functions.items.len), ); code_section_index = section_count; @@ -2032,13 +2038,16 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } else if (!self.base.options.strip) { if (self.dwarf) |*dwarf| { if (self.debug_info_index != null) { - _ = dwarf; try dwarf.writeDbgAbbrev(&self.base); - try dwarf.writeDbgInfoHeader(&self.base, mod, 0, 0); + // for debug info and ranges, the address is always 0, + // as locations are always offsets relative to 'code' section. + try dwarf.writeDbgInfoHeader(&self.base, mod, 0, code_section_size); + try dwarf.writeDbgAranges(&self.base, 0, code_section_size); try dwarf.writeDbgLineHeader(&self.base, mod); try emitDebugSection(file, self.debug_info.items, ".debug_info"); - try emitDebugSection(file, self.debug_abbrev.items, ".debug_abbrev"); // TODO + try emitDebugSection(file, self.debug_aranges.items, ".debug_ranges"); + try emitDebugSection(file, self.debug_abbrev.items, ".debug_abbrev"); try emitDebugSection(file, self.debug_line.items, ".debug_line"); try emitDebugSection(file, dwarf.strtab.items, ".debug_str"); } diff --git a/test/behavior/ptrcast.zig b/test/behavior/ptrcast.zig index 10138cff01..c827cb6ef7 100644 --- a/test/behavior/ptrcast.zig +++ b/test/behavior/ptrcast.zig @@ -134,6 +134,7 @@ test "lower reinterpreted comptime field ptr (with under-aligned fields)" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO: CBE does not yet support under-aligned fields // Test lowering a field ptr @@ -158,6 +159,7 @@ test "lower reinterpreted comptime field ptr" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO // Test lowering a field ptr comptime var bytes align(4) = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; From 20e7f1218b997e3da5d10cb5d038ed782d772716 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 9 May 2022 22:31:36 +0200 Subject: [PATCH 1424/2031] x64: make one entry point for binary ops * rename `genBinMathOp` into `genBinOp` and handle commutativity * rename `genBinMathOpMir` into `genBinOpMir` --- src/arch/x86_64/CodeGen.zig | 323 +++++++++++++----------------------- 1 file changed, 113 insertions(+), 210 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 28de88ff86..8dbb6aef7a 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -586,11 +586,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off - .add => try self.airAdd(inst), - .addwrap => try self.airAdd(inst), + .add => try self.airBinOp(inst), + .addwrap => try self.airBinOp(inst), .add_sat => try self.airAddSat(inst), - .sub => try self.airSub(inst), - .subwrap => try self.airSub(inst), + .sub => try self.airBinOp(inst), + .subwrap => try self.airBinOp(inst), .sub_sat => try self.airSubSat(inst), .mul => try self.airMul(inst), .mulwrap => try self.airMul(inst), @@ -601,8 +601,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .shl_sat => try self.airShlSat(inst), .min => try self.airMin(inst), .max => try self.airMax(inst), - .ptr_add => try self.airPtrAdd(inst), - .ptr_sub => try self.airPtrSub(inst), + .ptr_add => try self.airBinOp(inst), + .ptr_sub => try self.airBinOp(inst), .slice => try self.airSlice(inst), .sqrt, @@ -638,11 +638,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_vector => try self.airCmpVector(inst), .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), - .bool_and => try self.airBoolOp(inst), - .bool_or => try self.airBoolOp(inst), - .bit_and => try self.airBitAnd(inst), - .bit_or => try self.airBitOr(inst), - .xor => try self.airXor(inst), + .bool_and => try self.airBinOp(inst), + .bool_or => try self.airBinOp(inst), + .bit_and => try self.airBinOp(inst), + .bit_or => try self.airBinOp(inst), + .xor => try self.airBinOp(inst), .shr, .shr_exact => try self.airShr(inst), .alloc => try self.airAlloc(inst), @@ -1122,7 +1122,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { }, else => {}, } - break :result try self.genBinMathOp(inst, ty_op.operand, .bool_true); + break :result try self.genBinOp(inst, ty_op.operand, .bool_true); }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -1159,7 +1159,7 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void { }; defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); - try self.genBinMathOpMir(.cmp, ty, .{ .register = lhs_reg }, rhs_mcv); + try self.genBinOpMir(.cmp, ty, .{ .register = lhs_reg }, rhs_mcv); const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, rhs_mcv); _ = try self.addInst(.{ @@ -1185,63 +1185,12 @@ fn airMax(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue { - const dst_ty = self.air.typeOfIndex(inst); - const elem_size = dst_ty.elemType2().abiSize(self.target.*); - const ptr = try self.resolveInst(op_lhs); - const offset = try self.resolveInst(op_rhs); - const offset_ty = self.air.typeOf(op_rhs); - - const offset_lock: ?RegisterLock = switch (offset) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (offset_lock) |lock| self.register_manager.unlockReg(lock); - - const dst_mcv: MCValue = blk: { - if (self.reuseOperand(inst, op_lhs, 0, ptr)) { - if (ptr.isMemory() or ptr.isRegister()) break :blk ptr; - } - break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, ptr) }; - }; - - const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); - - const offset_mcv: MCValue = blk: { - if (self.reuseOperand(inst, op_rhs, 1, offset)) { - if (offset.isRegister()) break :blk offset; - } - break :blk MCValue{ .register = try self.copyToTmpRegister(offset_ty, offset) }; - }; - - const offset_mcv_lock: ?RegisterLock = switch (offset_mcv) { - .register => |reg| self.register_manager.lockReg(reg), - else => null, - }; - defer if (offset_mcv_lock) |lock| self.register_manager.unlockReg(lock); - - try self.genIntMulComplexOpMir(offset_ty, offset_mcv, .{ .immediate = elem_size }); - - const tag = self.air.instructions.items(.tag)[inst]; - switch (tag) { - .ptr_add => try self.genBinMathOpMir(.add, dst_ty, dst_mcv, offset_mcv), - .ptr_sub => try self.genBinMathOpMir(.sub, dst_ty, dst_mcv, offset_mcv), - else => unreachable, - } - - return dst_mcv; -} - fn airPtrAdd(self: *Self, inst: Air.Inst.Index) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const result = if (self.liveness.isUnused(inst)) .dead else - try self.genPtrBinMathOp(inst, bin_op.lhs, bin_op.rhs); + try self.genBinOp(inst, bin_op.lhs, bin_op.rhs); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1250,7 +1199,7 @@ fn airPtrSub(self: *Self, inst: Air.Inst.Index) !void { const result = if (self.liveness.isUnused(inst)) .dead else - try self.genPtrBinMathOp(inst, bin_op.lhs, bin_op.rhs); + try self.genBinOp(inst, bin_op.lhs, bin_op.rhs); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1275,21 +1224,12 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airAdd(self: *Self, inst: Air.Inst.Index) !void { +fn airBinOp(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 - try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airAddWrap(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 - try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); + try self.genBinOp(inst, bin_op.lhs, bin_op.rhs); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1302,60 +1242,6 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -/// Result is always a register. -fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue { - const dst_ty = self.air.typeOf(op_lhs); - - const lhs = try self.resolveInst(op_lhs); - const lhs_lock: ?RegisterLock = switch (lhs) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); - - const rhs = try self.resolveInst(op_rhs); - const rhs_lock: ?RegisterLock = switch (rhs) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); - - const dst_mcv: MCValue = blk: { - if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) { - break :blk lhs; - } - break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); - }; - const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { - .register => |reg| self.register_manager.lockReg(reg), - else => null, - }; - defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); - - const rhs_mcv: MCValue = blk: { - if (rhs.isMemory() or rhs.isRegister()) break :blk rhs; - break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, rhs) }; - }; - const rhs_mcv_lock: ?RegisterLock = switch (rhs_mcv) { - .register => |reg| self.register_manager.lockReg(reg), - else => null, - }; - defer if (rhs_mcv_lock) |lock| self.register_manager.unlockReg(lock); - - try self.genBinMathOpMir(.sub, dst_ty, dst_mcv, rhs_mcv); - - return dst_mcv; -} - -fn airSub(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 - try self.genSubOp(inst, bin_op.lhs, bin_op.rhs); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airSubSat(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)) @@ -1422,7 +1308,7 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; - const partial = try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); + const partial = try self.genBinOp(inst, bin_op.lhs, bin_op.rhs); const result: MCValue = switch (int_info.signedness) { .signed => .{ .register_overflow_signed = partial.register }, .unsigned => .{ .register_overflow_unsigned = partial.register }, @@ -1454,7 +1340,7 @@ fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; - const partial = try self.genSubOp(inst, bin_op.lhs, bin_op.rhs); + const partial = try self.genBinOp(inst, bin_op.lhs, bin_op.rhs); const result: MCValue = switch (int_info.signedness) { .signed => .{ .register_overflow_signed = partial.register }, .unsigned => .{ .register_overflow_unsigned = partial.register }, @@ -1605,7 +1491,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const scratch_reg = temp_regs[1]; try self.genSetReg(extended_ty, scratch_reg, .{ .register = dst_reg }); try self.truncateRegister(ty, scratch_reg); - try self.genBinMathOpMir( + try self.genBinOpMir( .cmp, extended_ty, .{ .register = dst_reg }, @@ -1622,7 +1508,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { .data = undefined, }); - try self.genBinMathOpMir( + try self.genBinOpMir( .@"or", Type.u8, .{ .register = overflow_reg }, @@ -1781,7 +1667,7 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa }).encode(), .data = undefined, }); - try self.genBinMathOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax }); + try self.genBinOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax }); return MCValue{ .register = divisor }; } @@ -1945,7 +1831,7 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void { try self.genIntMulComplexOpMir(ty, div_floor, rhs); const result = try self.copyToRegisterWithInstTracking(inst, ty, lhs); - try self.genBinMathOpMir(.sub, ty, result, div_floor); + try self.genBinOpMir(.sub, ty, result, div_floor); break :result result; }, @@ -1955,33 +1841,6 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airBitAnd(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 - try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airBitOr(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 - try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airXor(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 - try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airShl(self: *Self, inst: Air.Inst.Index) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; if (self.liveness.isUnused(inst)) { @@ -1989,6 +1848,7 @@ fn airShl(self: *Self, inst: Air.Inst.Index) !void { } const ty = self.air.typeOfIndex(inst); + const tag = self.air.instructions.items(.tag)[inst]; switch (tag) { .shl_exact => return self.fail("TODO implement {} for type {}", .{ tag, ty.fmtDebug() }), @@ -2470,7 +2330,7 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { } // TODO we could allocate register here, but need to expect addr register and potentially // offset register. - try self.genBinMathOpMir(.add, slice_ptr_field_type, .{ .register = addr_reg }, .{ + try self.genBinOpMir(.add, slice_ptr_field_type, .{ .register = addr_reg }, .{ .register = offset_reg, }); return MCValue{ .register = addr_reg.to64() }; @@ -2573,7 +2433,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { // TODO we could allocate register here, but need to expect addr register and potentially // offset register. const dst_mcv = try self.allocRegOrMem(inst, false); - try self.genBinMathOpMir(.add, Type.usize, .{ .register = addr_reg }, .{ .register = offset_reg }); + try self.genBinOpMir(.add, Type.usize, .{ .register = addr_reg }, .{ .register = offset_reg }); try self.load(dst_mcv, .{ .register = addr_reg.to64() }, array_ty); return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none }); @@ -2613,7 +2473,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { defer self.register_manager.unlockReg(offset_reg_lock); const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); - try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); + try self.genBinOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); const result: MCValue = result: { if (elem_abi_size > 8) { @@ -2667,7 +2527,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { defer self.register_manager.unlockReg(offset_reg_lock); const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); - try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); + try self.genBinOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); return self.finishAir(inst, dst_mcv, .{ extra.lhs, extra.rhs, .none }); } @@ -2700,7 +2560,7 @@ fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { const adjusted_ptr: MCValue = if (layout.payload_size > 0 and layout.tag_align < layout.payload_align) blk: { // TODO reusing the operand const reg = try self.copyToTmpRegister(ptr_ty, ptr); - try self.genBinMathOpMir(.add, ptr_ty, .{ .register = reg }, .{ .immediate = layout.payload_size }); + try self.genBinOpMir(.add, ptr_ty, .{ .register = reg }, .{ .immediate = layout.payload_size }); break :blk MCValue{ .register = reg }; } else ptr; @@ -3267,7 +3127,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde defer self.register_manager.unlockReg(offset_reg_lock); const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, mcv); - try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); + try self.genBinOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); break :result dst_mcv; }, .ptr_stack_offset => |off| { @@ -3297,7 +3157,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde const result_reg_lock = self.register_manager.lockReg(result_reg); defer if (result_reg_lock) |lock| self.register_manager.unlockReg(lock); - try self.genBinMathOpMir(.add, ptr_ty, .{ .register = result_reg }, .{ .register = offset_reg }); + try self.genBinOpMir(.add, ptr_ty, .{ .register = result_reg }, .{ .register = offset_reg }); break :result MCValue{ .register = result_reg }; }, else => return self.fail("TODO implement codegen struct_field_ptr for {}", .{mcv}), @@ -3357,7 +3217,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const mask = (~@as(u64, 0)) >> mask_shift; const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask }); - try self.genBinMathOpMir(.@"and", Type.usize, dst_mcv, .{ .register = tmp_reg }); + try self.genBinOpMir(.@"and", Type.usize, dst_mcv, .{ .register = tmp_reg }); const signedness: std.builtin.Signedness = blk: { if (struct_field_ty.zigTypeTag() != .Int) break :blk .unsigned; @@ -3426,8 +3286,39 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { } /// Result is always a register. -fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue { +fn genBinOp( + self: *Self, + inst: Air.Inst.Index, + op_lhs: Air.Inst.Ref, + op_rhs: Air.Inst.Ref, +) !MCValue { + const tag = self.air.instructions.items(.tag)[inst]; + const is_commutative: bool = switch (tag) { + .add, + .addwrap, + .add_with_overflow, + .bool_or, + .bit_or, + .bool_and, + .bit_and, + .xor, + .not, + => true, + + .sub, + .subwrap, + .sub_with_overflow, + .mul, + .shl, + .shr, + .ptr_add, + .ptr_sub, + => false, + + else => unreachable, + }; const dst_ty = self.air.typeOf(op_lhs); + const src_ty = self.air.typeOf(op_rhs); const lhs = try self.resolveInst(op_lhs); const lhs_lock: ?RegisterLock = switch (lhs) { @@ -3448,7 +3339,7 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) { break :blk lhs; } - if (self.reuseOperand(inst, op_rhs, 1, rhs) and rhs.isRegister()) { + if (is_commutative and self.reuseOperand(inst, op_rhs, 1, rhs) and rhs.isRegister()) { flipped = true; break :blk rhs; } @@ -3463,7 +3354,7 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: const src_mcv: MCValue = blk: { const mcv = if (flipped) lhs else rhs; if (mcv.isRegister() or mcv.isMemory()) break :blk mcv; - break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, mcv) }; + break :blk MCValue{ .register = try self.copyToTmpRegister(src_ty, mcv) }; }; const src_mcv_lock: ?RegisterLock = switch (src_mcv) { .register => |reg| self.register_manager.lockReg(reg), @@ -3471,18 +3362,39 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: }; defer if (src_mcv_lock) |lock| self.register_manager.unlockReg(lock); - const tag = self.air.instructions.items(.tag)[inst]; switch (tag) { - .add, .addwrap, .add_with_overflow => try self.genBinMathOpMir(.add, dst_ty, dst_mcv, src_mcv), - .bool_or, .bit_or => try self.genBinMathOpMir(.@"or", dst_ty, dst_mcv, src_mcv), - .bool_and, .bit_and => try self.genBinMathOpMir(.@"and", dst_ty, dst_mcv, src_mcv), - .xor, .not => try self.genBinMathOpMir(.xor, dst_ty, dst_mcv, src_mcv), + .add, + .addwrap, + .add_with_overflow, + => try self.genBinOpMir(.add, dst_ty, dst_mcv, src_mcv), + + .sub, + .subwrap, + .sub_with_overflow, + => try self.genBinOpMir(.sub, dst_ty, dst_mcv, src_mcv), + + .ptr_add, + .ptr_sub, + => { + const mir_tag: Mir.Inst.Tag = switch (tag) { + .ptr_add => .add, + .ptr_sub => .sub, + else => unreachable, + }; + const elem_size = dst_ty.elemType2().abiSize(self.target.*); + try self.genIntMulComplexOpMir(src_ty, src_mcv, .{ .immediate = elem_size }); + try self.genBinOpMir(mir_tag, dst_ty, dst_mcv, src_mcv); + }, + + .bool_or, .bit_or => try self.genBinOpMir(.@"or", dst_ty, dst_mcv, src_mcv), + .bool_and, .bit_and => try self.genBinOpMir(.@"and", dst_ty, dst_mcv, src_mcv), + .xor, .not => try self.genBinOpMir(.xor, dst_ty, dst_mcv, src_mcv), else => unreachable, } return dst_mcv; } -fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { +fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); switch (dst_mcv) { .none => unreachable, @@ -3504,7 +3416,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); const reg = try self.copyToTmpRegister(dst_ty, src_mcv); - return self.genBinMathOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); + return self.genBinOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); }, .register => |src_reg| { _ = try self.addInst(.{ @@ -3536,7 +3448,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); const reg = try self.copyToTmpRegister(dst_ty, src_mcv); - return self.genBinMathOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); + return self.genBinOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); }, .stack_offset => |off| { if (off > math.maxInt(i32)) { @@ -3637,7 +3549,7 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC /// Performs multi-operand integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv. /// Does not support byte-size operands. -fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { +fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError!void { const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); switch (dst_mcv) { .none => unreachable, @@ -3733,11 +3645,17 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .data = undefined, }); // copy dst_reg back out - return self.genSetStack(dst_ty, off, MCValue{ .register = dst_reg }, .{}); + return self.genSetStack(dst_ty, off, .{ .register = dst_reg }, .{}); }, - .immediate => |imm| { - _ = imm; - return self.fail("TODO implement x86 multiply source immediate", .{}); + .immediate => { + // copy dst to a register + const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); + const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); + defer self.register_manager.unlockReg(dst_reg_lock); + + try self.genIntMulComplexOpMir(dst_ty, .{ .register = dst_reg }, src_mcv); + + return self.genSetStack(dst_ty, off, .{ .register = dst_reg }, .{}); }, .memory, .stack_offset => { return self.fail("TODO implement x86 multiply source memory", .{}); @@ -4213,7 +4131,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { // This instruction supports only signed 32-bit immediates at most. const src_mcv = try self.limitImmediateType(bin_op.rhs, i32); - try self.genBinMathOpMir(.cmp, ty, dst_mcv, src_mcv); + try self.genBinOpMir(.cmp, ty, dst_mcv, src_mcv); break :result switch (signedness) { .signed => MCValue{ .compare_flags_signed = op }, .unsigned => MCValue{ .compare_flags_unsigned = op }, @@ -4606,7 +4524,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValu break :blk if (payload_ty.hasRuntimeBits()) Type.bool else ty; } else ty; - try self.genBinMathOpMir(.cmp, cmp_ty, operand, MCValue{ .immediate = 0 }); + try self.genBinOpMir(.cmp, cmp_ty, operand, MCValue{ .immediate = 0 }); return MCValue{ .compare_flags_unsigned = .eq }; } @@ -4629,13 +4547,13 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue if (!payload_type.hasRuntimeBits()) { if (err_type.abiSize(self.target.*) <= 8) { - try self.genBinMathOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 }); + try self.genBinOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 }); return MCValue{ .compare_flags_unsigned = .gt }; } else { return self.fail("TODO isErr for errors with size larger than register size", .{}); } } else { - try self.genBinMathOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 }); + try self.genBinOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 }); return MCValue{ .compare_flags_unsigned = .gt }; } } @@ -5053,21 +4971,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); } -fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - const air_tags = self.air.instructions.items(.tag); - const result: MCValue = if (self.liveness.isUnused(inst)) - .dead - else switch (air_tags[inst]) { - // lhs AND rhs - .bool_and => try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs), - // lhs OR rhs - .bool_or => try self.genBinMathOp(inst, bin_op.lhs, bin_op.rhs), - else => unreachable, // Not a boolean operation - }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { const block_data = self.blocks.getPtr(block).?; @@ -5844,7 +5747,7 @@ fn genInlineMemset( defer self.register_manager.unlockReg(addr_reg_lock); try self.genSetReg(Type.usize, .rax, len); - try self.genBinMathOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 }); + try self.genBinOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 }); // loop: // cmp rax, -1 @@ -6963,10 +6866,10 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { const shift = @intCast(u6, max_reg_bit_width - int_info.bits); const mask = (~@as(u64, 0)) >> shift; if (int_info.bits <= 32) { - try self.genBinMathOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask }); + try self.genBinOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask }); } else { const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask }); - try self.genBinMathOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .register = tmp_reg }); + try self.genBinOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .register = tmp_reg }); } }, } From 7b9f8bfbd80aa37afc160c39b341c59fdd3cbad8 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 9 May 2022 23:50:01 +0200 Subject: [PATCH 1425/2031] x64: migrate mul to new genBinOp helper --- src/arch/x86_64/CodeGen.zig | 151 ++++++++++++++---------------------- 1 file changed, 59 insertions(+), 92 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 8dbb6aef7a..726476f6c7 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -592,8 +592,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .sub => try self.airBinOp(inst), .subwrap => try self.airBinOp(inst), .sub_sat => try self.airSubSat(inst), - .mul => try self.airMul(inst), - .mulwrap => try self.airMul(inst), + .mul => try self.airBinOp(inst), + .mulwrap => try self.airBinOp(inst), .mul_sat => try self.airMulSat(inst), .rem => try self.airRem(inst), .mod => try self.airMod(inst), @@ -1122,7 +1122,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { }, else => {}, } - break :result try self.genBinOp(inst, ty_op.operand, .bool_true); + break :result try self.genBinOp(inst, ty_op.operand, .bool_true, true); }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -1185,24 +1185,6 @@ fn airMax(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airPtrAdd(self: *Self, inst: Air.Inst.Index) !void { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - const result = if (self.liveness.isUnused(inst)) - .dead - else - try self.genBinOp(inst, bin_op.lhs, bin_op.rhs); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airPtrSub(self: *Self, inst: Air.Inst.Index) !void { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - const result = if (self.liveness.isUnused(inst)) - .dead - else - try self.genBinOp(inst, bin_op.lhs, bin_op.rhs); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airSlice(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; @@ -1229,7 +1211,7 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { const result: MCValue = if (self.liveness.isUnused(inst)) .dead else - try self.genBinOp(inst, bin_op.lhs, bin_op.rhs); + try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, true); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1251,36 +1233,6 @@ fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airMul(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 result: { - const ty = self.air.typeOfIndex(inst); - - if (ty.zigTypeTag() != .Int) { - return self.fail("TODO implement 'mul' for operands of dst type {}", .{ty.zigTypeTag()}); - } - - // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. - try self.register_manager.getReg(.rax, inst); - try self.register_manager.getReg(.rdx, null); - const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); - defer for (reg_locks) |reg| { - self.register_manager.unlockReg(reg); - }; - - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - - const signedness = ty.intInfo(self.target.*).signedness; - try self.genIntMulDivOpMir(switch (signedness) { - .signed => .imul, - .unsigned => .mul, - }, ty, signedness, lhs, rhs); - break :result MCValue{ .register = .rax }; - }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airMulSat(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)) @@ -1308,7 +1260,7 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; - const partial = try self.genBinOp(inst, bin_op.lhs, bin_op.rhs); + const partial = try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, true); const result: MCValue = switch (int_info.signedness) { .signed => .{ .register_overflow_signed = partial.register }, .unsigned => .{ .register_overflow_unsigned = partial.register }, @@ -1340,7 +1292,7 @@ fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; - const partial = try self.genBinOp(inst, bin_op.lhs, bin_op.rhs); + const partial = try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, true); const result: MCValue = switch (int_info.signedness) { .signed => .{ .register_overflow_signed = partial.register }, .unsigned => .{ .register_overflow_unsigned = partial.register }, @@ -1373,34 +1325,17 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement mul_with_overflow for Ints larger than 64bits", .{}); } + try self.spillCompareFlagsIfOccupied(); + if (math.isPowerOfTwo(int_info.bits)) { - try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; - - // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. - try self.register_manager.getReg(.rax, inst); - try self.register_manager.getReg(.rdx, null); - const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); - defer for (reg_locks) |reg| { - self.register_manager.unlockReg(reg); + const partial = try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, true); + break :result switch (int_info.signedness) { + .signed => MCValue{ .register_overflow_signed = partial.register }, + .unsigned => MCValue{ .register_overflow_unsigned = partial.register }, }; - - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - - try self.genIntMulDivOpMir(switch (int_info.signedness) { - .signed => .imul, - .unsigned => .mul, - }, ty, int_info.signedness, lhs, rhs); - - const result: MCValue = switch (int_info.signedness) { - .signed => .{ .register_overflow_signed = .rax }, - .unsigned => .{ .register_overflow_unsigned = .rax }, - }; - break :result result; } - try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = null; const dst_reg: Register = dst_reg: { @@ -1437,20 +1372,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { break :dst_reg dst_reg; }, .unsigned => { - // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. - try self.register_manager.getReg(.rax, null); - try self.register_manager.getReg(.rdx, null); - const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); - defer for (reg_locks) |reg| { - self.register_manager.unlockReg(reg); - }; - - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - - try self.genIntMulDivOpMir(.mul, ty, .unsigned, lhs, rhs); - - break :dst_reg registerAlias(.rax, @intCast(u32, ty.abiSize(self.target.*))); + const dst_mcv = try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, false); + break :dst_reg dst_mcv.register; }, } }; @@ -3291,6 +3214,7 @@ fn genBinOp( inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, + track: bool, ) !MCValue { const tag = self.air.instructions.items(.tag)[inst]; const is_commutative: bool = switch (tag) { @@ -3309,6 +3233,8 @@ fn genBinOp( .subwrap, .sub_with_overflow, .mul, + .mulwrap, + .mul_with_overflow, .shl, .shr, .ptr_add, @@ -3320,6 +3246,43 @@ fn genBinOp( const dst_ty = self.air.typeOf(op_lhs); const src_ty = self.air.typeOf(op_rhs); + if (dst_ty.zigTypeTag() == .Vector or dst_ty.zigTypeTag() == .Float) { + return self.fail("TODO implement genBinOp for {}", .{dst_ty.fmtDebug()}); + } + if (dst_ty.abiSize(self.target.*) > 8) { + return self.fail("TODO implement genBinOp for {}", .{dst_ty.fmtDebug()}); + } + + switch (tag) { + .mul, + .mulwrap, + .mul_with_overflow, + => { + // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. + try self.register_manager.getReg(.rax, if (track) inst else null); + try self.register_manager.getReg(.rdx, null); + const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); + defer for (reg_locks) |reg| { + self.register_manager.unlockReg(reg); + }; + + const lhs = try self.resolveInst(op_lhs); + const rhs = try self.resolveInst(op_rhs); + + const int_info = dst_ty.intInfo(self.target.*); + try self.genIntMulDivOpMir(switch (int_info.signedness) { + .signed => .imul, + .unsigned => .mul, + }, dst_ty, int_info.signedness, lhs, rhs); + + return switch (int_info.signedness) { + .signed => MCValue{ .register = .rax }, + .unsigned => MCValue{ .register = registerAlias(.rax, @intCast(u32, dst_ty.abiSize(self.target.*))) }, + }; + }, + else => {}, + } + const lhs = try self.resolveInst(op_lhs); const lhs_lock: ?RegisterLock = switch (lhs) { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), @@ -3343,7 +3306,11 @@ fn genBinOp( flipped = true; break :blk rhs; } - break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); + if (track) { + break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); + } else { + break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, lhs) }; + } }; const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { .register => |reg| self.register_manager.lockReg(reg), From c3b7a5cc26d11a0349ff1d7812b00687cfa41c2e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 9 May 2022 23:58:46 +0200 Subject: [PATCH 1426/2031] x64: pass tag and maybe_inst explictly to genBinOp --- src/arch/x86_64/CodeGen.zig | 68 ++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 726476f6c7..94f1195a3c 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1122,7 +1122,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { }, else => {}, } - break :result try self.genBinOp(inst, ty_op.operand, .bool_true, true); + break :result try self.genBinOp(.not, inst, ty_op.operand, .bool_true); }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -1208,10 +1208,13 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { fn airBinOp(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 - try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, true); + + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + } + + const tag = self.air.instructions.items(.tag)[inst]; + const result = try self.genBinOp(tag, inst, bin_op.lhs, bin_op.rhs); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1260,7 +1263,7 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; - const partial = try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, true); + const partial = try self.genBinOp(.add, inst, bin_op.lhs, bin_op.rhs); const result: MCValue = switch (int_info.signedness) { .signed => .{ .register_overflow_signed = partial.register }, .unsigned => .{ .register_overflow_unsigned = partial.register }, @@ -1292,7 +1295,7 @@ fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; - const partial = try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, true); + const partial = try self.genBinOp(.sub, inst, bin_op.lhs, bin_op.rhs); const result: MCValue = switch (int_info.signedness) { .signed => .{ .register_overflow_signed = partial.register }, .unsigned => .{ .register_overflow_unsigned = partial.register }, @@ -1329,7 +1332,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { if (math.isPowerOfTwo(int_info.bits)) { self.compare_flags_inst = inst; - const partial = try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, true); + const partial = try self.genBinOp(.mul, inst, bin_op.lhs, bin_op.rhs); break :result switch (int_info.signedness) { .signed => MCValue{ .register_overflow_signed = partial.register }, .unsigned => MCValue{ .register_overflow_unsigned = partial.register }, @@ -1372,7 +1375,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { break :dst_reg dst_reg; }, .unsigned => { - const dst_mcv = try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, false); + const dst_mcv = try self.genBinOp(.mul, null, bin_op.lhs, bin_op.rhs); break :dst_reg dst_mcv.register; }, } @@ -3211,16 +3214,14 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { /// Result is always a register. fn genBinOp( self: *Self, - inst: Air.Inst.Index, + tag: Air.Inst.Tag, + maybe_inst: ?Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, - track: bool, ) !MCValue { - const tag = self.air.instructions.items(.tag)[inst]; const is_commutative: bool = switch (tag) { .add, .addwrap, - .add_with_overflow, .bool_or, .bit_or, .bool_and, @@ -3231,10 +3232,8 @@ fn genBinOp( .sub, .subwrap, - .sub_with_overflow, .mul, .mulwrap, - .mul_with_overflow, .shl, .shr, .ptr_add, @@ -3256,10 +3255,9 @@ fn genBinOp( switch (tag) { .mul, .mulwrap, - .mul_with_overflow, => { // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. - try self.register_manager.getReg(.rax, if (track) inst else null); + try self.register_manager.getReg(.rax, maybe_inst); try self.register_manager.getReg(.rdx, null); const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); defer for (reg_locks) |reg| { @@ -3299,18 +3297,17 @@ fn genBinOp( var flipped: bool = false; const dst_mcv: MCValue = blk: { - if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) { - break :blk lhs; - } - if (is_commutative and self.reuseOperand(inst, op_rhs, 1, rhs) and rhs.isRegister()) { - flipped = true; - break :blk rhs; - } - if (track) { + if (maybe_inst) |inst| { + if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) { + break :blk lhs; + } + if (is_commutative and self.reuseOperand(inst, op_rhs, 1, rhs) and rhs.isRegister()) { + flipped = true; + break :blk rhs; + } break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); - } else { - break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, lhs) }; } + break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, lhs) }; }; const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { .register => |reg| self.register_manager.lockReg(reg), @@ -3332,12 +3329,10 @@ fn genBinOp( switch (tag) { .add, .addwrap, - .add_with_overflow, => try self.genBinOpMir(.add, dst_ty, dst_mcv, src_mcv), .sub, .subwrap, - .sub_with_overflow, => try self.genBinOpMir(.sub, dst_ty, dst_mcv, src_mcv), .ptr_add, @@ -3353,9 +3348,18 @@ fn genBinOp( try self.genBinOpMir(mir_tag, dst_ty, dst_mcv, src_mcv); }, - .bool_or, .bit_or => try self.genBinOpMir(.@"or", dst_ty, dst_mcv, src_mcv), - .bool_and, .bit_and => try self.genBinOpMir(.@"and", dst_ty, dst_mcv, src_mcv), - .xor, .not => try self.genBinOpMir(.xor, dst_ty, dst_mcv, src_mcv), + .bool_or, + .bit_or, + => try self.genBinOpMir(.@"or", dst_ty, dst_mcv, src_mcv), + + .bool_and, + .bit_and, + => try self.genBinOpMir(.@"and", dst_ty, dst_mcv, src_mcv), + + .xor, + .not, + => try self.genBinOpMir(.xor, dst_ty, dst_mcv, src_mcv), + else => unreachable, } return dst_mcv; From 252c5a2339820d15bc7a063cdd68cc65b49ed413 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 10 May 2022 01:13:13 +0200 Subject: [PATCH 1427/2031] x64: migrate mod and rem into genBinOp --- src/arch/x86_64/CodeGen.zig | 142 +++++++++++++++--------------------- 1 file changed, 60 insertions(+), 82 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 94f1195a3c..65293e0f46 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -595,8 +595,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .mul => try self.airBinOp(inst), .mulwrap => try self.airBinOp(inst), .mul_sat => try self.airMulSat(inst), - .rem => try self.airRem(inst), - .mod => try self.airMod(inst), + .rem => try self.airBinOp(inst), + .mod => try self.airBinOp(inst), .shl, .shl_exact => try self.airShl(inst), .shl_sat => try self.airShlSat(inst), .min => try self.airMin(inst), @@ -1539,6 +1539,7 @@ fn genIntMulDivOpMir( } } +/// Always returns a register. /// Clobbers .rax and .rdx registers. fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCValue { const signedness = ty.intInfo(self.target.*).signedness; @@ -1687,86 +1688,6 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airRem(self: *Self, inst: Air.Inst.Index) !void { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - - if (self.liveness.isUnused(inst)) { - return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); - } - const ty = self.air.typeOfIndex(inst); - if (ty.zigTypeTag() != .Int) { - return self.fail("TODO implement .rem for operands of dst type {}", .{ty.zigTypeTag()}); - } - // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. - try self.register_manager.getReg(.rax, null); - try self.register_manager.getReg(.rdx, inst); - const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); - defer for (reg_locks) |reg| { - self.register_manager.unlockReg(reg); - }; - - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - - const signedness = ty.intInfo(self.target.*).signedness; - try self.genIntMulDivOpMir(switch (signedness) { - .signed => .idiv, - .unsigned => .div, - }, ty, signedness, lhs, rhs); - - const result: MCValue = .{ .register = .rdx }; - - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airMod(self: *Self, inst: Air.Inst.Index) !void { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - - if (self.liveness.isUnused(inst)) { - return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); - } - - const ty = self.air.typeOfIndex(inst); - if (ty.zigTypeTag() != .Int) { - return self.fail("TODO implement .mod for operands of dst type {}", .{ty.zigTypeTag()}); - } - const signedness = ty.intInfo(self.target.*).signedness; - - // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. - try self.register_manager.getReg(.rax, null); - try self.register_manager.getReg(.rdx, if (signedness == .unsigned) inst else null); - const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); - defer for (reg_locks) |reg| { - self.register_manager.unlockReg(reg); - }; - - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - - const result: MCValue = result: { - switch (signedness) { - .unsigned => { - try self.genIntMulDivOpMir(switch (signedness) { - .signed => .idiv, - .unsigned => .div, - }, ty, signedness, lhs, rhs); - break :result MCValue{ .register = .rdx }; - }, - .signed => { - const div_floor = try self.genInlineIntDivFloor(ty, lhs, rhs); - try self.genIntMulComplexOpMir(ty, div_floor, rhs); - - const result = try self.copyToRegisterWithInstTracking(inst, ty, lhs); - try self.genBinOpMir(.sub, ty, result, div_floor); - - break :result result; - }, - } - }; - - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airShl(self: *Self, inst: Air.Inst.Index) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; if (self.liveness.isUnused(inst)) { @@ -3234,12 +3155,18 @@ fn genBinOp( .subwrap, .mul, .mulwrap, + .div_exact, + .div_trunc, + .rem, + .mod, .shl, .shr, .ptr_add, .ptr_sub, => false, + .div_float => return self.fail("TODO implement genBinOp for {}", .{tag}), + else => unreachable, }; const dst_ty = self.air.typeOf(op_lhs); @@ -3278,6 +3205,57 @@ fn genBinOp( .unsigned => MCValue{ .register = registerAlias(.rax, @intCast(u32, dst_ty.abiSize(self.target.*))) }, }; }, + .mod, + .rem, + => { + const int_info = dst_ty.intInfo(self.target.*); + const track_inst_rdx: ?Air.Inst.Index = switch (tag) { + .mod => if (int_info.signedness == .unsigned) maybe_inst else null, + .rem => maybe_inst, + else => unreachable, + }; + + // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. + try self.register_manager.getReg(.rax, null); + try self.register_manager.getReg(.rdx, track_inst_rdx); + const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); + defer for (reg_locks) |reg| { + self.register_manager.unlockReg(reg); + }; + + const lhs = try self.resolveInst(op_lhs); + const rhs = try self.resolveInst(op_rhs); + + switch (int_info.signedness) { + .signed => { + switch (tag) { + .rem => { + try self.genIntMulDivOpMir(.idiv, dst_ty, .signed, lhs, rhs); + return MCValue{ .register = .rdx }; + }, + .mod => { + const div_floor = try self.genInlineIntDivFloor(dst_ty, lhs, rhs); + try self.genIntMulComplexOpMir(dst_ty, div_floor, rhs); + const div_floor_lock = self.register_manager.lockReg(div_floor.register); + defer if (div_floor_lock) |lock| self.register_manager.unlockReg(lock); + + const result: MCValue = if (maybe_inst) |inst| + try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs) + else + MCValue{ .register = try self.copyToTmpRegister(dst_ty, lhs) }; + try self.genBinOpMir(.sub, dst_ty, result, div_floor); + + return result; + }, + else => unreachable, + } + }, + .unsigned => { + try self.genIntMulDivOpMir(.div, dst_ty, .unsigned, lhs, rhs); + return MCValue{ .register = .rdx }; + }, + } + }, else => {}, } From af3ecd04b4377db7104a2d37d462f0f4b3e3db84 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 10 May 2022 09:04:39 +0200 Subject: [PATCH 1428/2031] x64: make genBinOp operate on MCValues directly --- src/arch/x86_64/CodeGen.zig | 138 ++++++++++++++++++++++-------------- 1 file changed, 84 insertions(+), 54 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 65293e0f46..5e27026b7c 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1089,8 +1089,15 @@ fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void { fn airNot(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 result: { - const operand = try self.resolveInst(ty_op.operand); + + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); + } + + const operand_ty = self.air.typeOf(ty_op.operand); + const operand = try self.resolveInst(ty_op.operand); + + const result: MCValue = result: { switch (operand) { .dead => unreachable, .unreach => unreachable, @@ -1122,7 +1129,28 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { }, else => {}, } - break :result try self.genBinOp(.not, inst, ty_op.operand, .bool_true); + + const operand_lock: ?RegisterLock = switch (operand) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); + + const dst_mcv: MCValue = blk: { + if (self.reuseOperand(inst, ty_op.operand, 0, operand) and operand.isRegister()) { + break :blk operand; + } + break :blk try self.copyToRegisterWithInstTracking(inst, operand_ty, operand); + }; + const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { + .register => |reg| self.register_manager.lockReg(reg), + else => null, + }; + defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); + + try self.genBinOpMir(.xor, operand_ty, dst_mcv, .{ .immediate = 1 }); + + break :result dst_mcv; }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -1214,7 +1242,13 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { } const tag = self.air.instructions.items(.tag)[inst]; - const result = try self.genBinOp(tag, inst, bin_op.lhs, bin_op.rhs); + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + + const result = try self.genBinOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1263,7 +1297,10 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; - const partial = try self.genBinOp(.add, inst, bin_op.lhs, bin_op.rhs); + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + const partial = try self.genBinOp(.add, null, lhs, rhs, ty, ty); const result: MCValue = switch (int_info.signedness) { .signed => .{ .register_overflow_signed = partial.register }, .unsigned => .{ .register_overflow_unsigned = partial.register }, @@ -1295,7 +1332,10 @@ fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; - const partial = try self.genBinOp(.sub, inst, bin_op.lhs, bin_op.rhs); + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + const partial = try self.genBinOp(.sub, null, lhs, rhs, ty, ty); const result: MCValue = switch (int_info.signedness) { .signed => .{ .register_overflow_signed = partial.register }, .unsigned => .{ .register_overflow_unsigned = partial.register }, @@ -1330,9 +1370,12 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.spillCompareFlagsIfOccupied(); + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + if (math.isPowerOfTwo(int_info.bits)) { self.compare_flags_inst = inst; - const partial = try self.genBinOp(.mul, inst, bin_op.lhs, bin_op.rhs); + const partial = try self.genBinOp(.mul, null, lhs, rhs, ty, ty); break :result switch (int_info.signedness) { .signed => MCValue{ .register_overflow_signed = partial.register }, .unsigned => MCValue{ .register_overflow_unsigned = partial.register }, @@ -1344,9 +1387,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const dst_reg: Register = dst_reg: { switch (int_info.signedness) { .signed => { - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - const rhs_lock: ?RegisterLock = switch (rhs) { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, @@ -1375,7 +1415,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { break :dst_reg dst_reg; }, .unsigned => { - const dst_mcv = try self.genBinOp(.mul, null, bin_op.lhs, bin_op.rhs); + const dst_mcv = try self.genBinOp(.mul, null, lhs, rhs, ty, ty); break :dst_reg dst_mcv.register; }, } @@ -3137,8 +3177,10 @@ fn genBinOp( self: *Self, tag: Air.Inst.Tag, maybe_inst: ?Air.Inst.Index, - op_lhs: Air.Inst.Ref, - op_rhs: Air.Inst.Ref, + lhs: MCValue, + rhs: MCValue, + lhs_ty: Type, + rhs_ty: Type, ) !MCValue { const is_commutative: bool = switch (tag) { .add, @@ -3148,7 +3190,6 @@ fn genBinOp( .bool_and, .bit_and, .xor, - .not, => true, .sub, @@ -3169,14 +3210,12 @@ fn genBinOp( else => unreachable, }; - const dst_ty = self.air.typeOf(op_lhs); - const src_ty = self.air.typeOf(op_rhs); - if (dst_ty.zigTypeTag() == .Vector or dst_ty.zigTypeTag() == .Float) { - return self.fail("TODO implement genBinOp for {}", .{dst_ty.fmtDebug()}); + if (lhs_ty.zigTypeTag() == .Vector or lhs_ty.zigTypeTag() == .Float) { + return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()}); } - if (dst_ty.abiSize(self.target.*) > 8) { - return self.fail("TODO implement genBinOp for {}", .{dst_ty.fmtDebug()}); + if (lhs_ty.abiSize(self.target.*) > 8) { + return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()}); } switch (tag) { @@ -3191,24 +3230,21 @@ fn genBinOp( self.register_manager.unlockReg(reg); }; - const lhs = try self.resolveInst(op_lhs); - const rhs = try self.resolveInst(op_rhs); - - const int_info = dst_ty.intInfo(self.target.*); + const int_info = lhs_ty.intInfo(self.target.*); try self.genIntMulDivOpMir(switch (int_info.signedness) { .signed => .imul, .unsigned => .mul, - }, dst_ty, int_info.signedness, lhs, rhs); + }, lhs_ty, int_info.signedness, lhs, rhs); return switch (int_info.signedness) { .signed => MCValue{ .register = .rax }, - .unsigned => MCValue{ .register = registerAlias(.rax, @intCast(u32, dst_ty.abiSize(self.target.*))) }, + .unsigned => MCValue{ .register = registerAlias(.rax, @intCast(u32, lhs_ty.abiSize(self.target.*))) }, }; }, .mod, .rem, => { - const int_info = dst_ty.intInfo(self.target.*); + const int_info = lhs_ty.intInfo(self.target.*); const track_inst_rdx: ?Air.Inst.Index = switch (tag) { .mod => if (int_info.signedness == .unsigned) maybe_inst else null, .rem => maybe_inst, @@ -3223,27 +3259,24 @@ fn genBinOp( self.register_manager.unlockReg(reg); }; - const lhs = try self.resolveInst(op_lhs); - const rhs = try self.resolveInst(op_rhs); - switch (int_info.signedness) { .signed => { switch (tag) { .rem => { - try self.genIntMulDivOpMir(.idiv, dst_ty, .signed, lhs, rhs); + try self.genIntMulDivOpMir(.idiv, lhs_ty, .signed, lhs, rhs); return MCValue{ .register = .rdx }; }, .mod => { - const div_floor = try self.genInlineIntDivFloor(dst_ty, lhs, rhs); - try self.genIntMulComplexOpMir(dst_ty, div_floor, rhs); + const div_floor = try self.genInlineIntDivFloor(lhs_ty, lhs, rhs); + try self.genIntMulComplexOpMir(lhs_ty, div_floor, rhs); const div_floor_lock = self.register_manager.lockReg(div_floor.register); defer if (div_floor_lock) |lock| self.register_manager.unlockReg(lock); const result: MCValue = if (maybe_inst) |inst| - try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs) + try self.copyToRegisterWithInstTracking(inst, lhs_ty, lhs) else - MCValue{ .register = try self.copyToTmpRegister(dst_ty, lhs) }; - try self.genBinOpMir(.sub, dst_ty, result, div_floor); + MCValue{ .register = try self.copyToTmpRegister(lhs_ty, lhs) }; + try self.genBinOpMir(.sub, lhs_ty, result, div_floor); return result; }, @@ -3251,7 +3284,7 @@ fn genBinOp( } }, .unsigned => { - try self.genIntMulDivOpMir(.div, dst_ty, .unsigned, lhs, rhs); + try self.genIntMulDivOpMir(.div, lhs_ty, .unsigned, lhs, rhs); return MCValue{ .register = .rdx }; }, } @@ -3259,14 +3292,12 @@ fn genBinOp( else => {}, } - const lhs = try self.resolveInst(op_lhs); const lhs_lock: ?RegisterLock = switch (lhs) { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, }; defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); - const rhs = try self.resolveInst(op_rhs); const rhs_lock: ?RegisterLock = switch (rhs) { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, @@ -3276,16 +3307,17 @@ fn genBinOp( var flipped: bool = false; const dst_mcv: MCValue = blk: { if (maybe_inst) |inst| { - if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + if (self.reuseOperand(inst, bin_op.lhs, 0, lhs) and lhs.isRegister()) { break :blk lhs; } - if (is_commutative and self.reuseOperand(inst, op_rhs, 1, rhs) and rhs.isRegister()) { + if (is_commutative and self.reuseOperand(inst, bin_op.rhs, 1, rhs) and rhs.isRegister()) { flipped = true; break :blk rhs; } - break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs); + break :blk try self.copyToRegisterWithInstTracking(inst, lhs_ty, lhs); } - break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, lhs) }; + break :blk MCValue{ .register = try self.copyToTmpRegister(lhs_ty, lhs) }; }; const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { .register => |reg| self.register_manager.lockReg(reg), @@ -3296,7 +3328,7 @@ fn genBinOp( const src_mcv: MCValue = blk: { const mcv = if (flipped) lhs else rhs; if (mcv.isRegister() or mcv.isMemory()) break :blk mcv; - break :blk MCValue{ .register = try self.copyToTmpRegister(src_ty, mcv) }; + break :blk MCValue{ .register = try self.copyToTmpRegister(rhs_ty, mcv) }; }; const src_mcv_lock: ?RegisterLock = switch (src_mcv) { .register => |reg| self.register_manager.lockReg(reg), @@ -3307,11 +3339,11 @@ fn genBinOp( switch (tag) { .add, .addwrap, - => try self.genBinOpMir(.add, dst_ty, dst_mcv, src_mcv), + => try self.genBinOpMir(.add, lhs_ty, dst_mcv, src_mcv), .sub, .subwrap, - => try self.genBinOpMir(.sub, dst_ty, dst_mcv, src_mcv), + => try self.genBinOpMir(.sub, lhs_ty, dst_mcv, src_mcv), .ptr_add, .ptr_sub, @@ -3321,22 +3353,20 @@ fn genBinOp( .ptr_sub => .sub, else => unreachable, }; - const elem_size = dst_ty.elemType2().abiSize(self.target.*); - try self.genIntMulComplexOpMir(src_ty, src_mcv, .{ .immediate = elem_size }); - try self.genBinOpMir(mir_tag, dst_ty, dst_mcv, src_mcv); + const elem_size = lhs_ty.elemType2().abiSize(self.target.*); + try self.genIntMulComplexOpMir(rhs_ty, src_mcv, .{ .immediate = elem_size }); + try self.genBinOpMir(mir_tag, lhs_ty, dst_mcv, src_mcv); }, .bool_or, .bit_or, - => try self.genBinOpMir(.@"or", dst_ty, dst_mcv, src_mcv), + => try self.genBinOpMir(.@"or", lhs_ty, dst_mcv, src_mcv), .bool_and, .bit_and, - => try self.genBinOpMir(.@"and", dst_ty, dst_mcv, src_mcv), + => try self.genBinOpMir(.@"and", lhs_ty, dst_mcv, src_mcv), - .xor, - .not, - => try self.genBinOpMir(.xor, dst_ty, dst_mcv, src_mcv), + .xor => try self.genBinOpMir(.xor, lhs_ty, dst_mcv, src_mcv), else => unreachable, } From 85ca14e35a6be6591faaac42a4c95cf183ef4c3b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 10 May 2022 09:08:33 +0200 Subject: [PATCH 1429/2031] x64: converge add_with_overflow and sub_with_overflow --- src/arch/x86_64/CodeGen.zig | 47 +++++++------------------------------ 1 file changed, 9 insertions(+), 38 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5e27026b7c..0586514a62 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -621,8 +621,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .trunc_float, => try self.airUnaryMath(inst), - .add_with_overflow => try self.airAddWithOverflow(inst), - .sub_with_overflow => try self.airSubWithOverflow(inst), + .add_with_overflow => try self.airAddSubWithOverflow(inst), + .sub_with_overflow => try self.airAddSubWithOverflow(inst), .mul_with_overflow => try self.airMulWithOverflow(inst), .shl_with_overflow => try self.airShlWithOverflow(inst), @@ -1279,8 +1279,9 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { +fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const tag = self.air.instructions.items(.tag)[inst]; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const result = if (self.liveness.isUnused(inst)) .dead else result: { const ty = self.air.typeOf(bin_op.lhs); @@ -1300,42 +1301,12 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const partial = try self.genBinOp(.add, null, lhs, rhs, ty, ty); - const result: MCValue = switch (int_info.signedness) { - .signed => .{ .register_overflow_signed = partial.register }, - .unsigned => .{ .register_overflow_unsigned = partial.register }, + const base_tag: Air.Inst.Tag = switch (tag) { + .add_with_overflow => .add, + .sub_with_overflow => .sub, + else => unreachable, }; - break :result result; - }, - else => unreachable, - } - }; - - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - -fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; - const result = if (self.liveness.isUnused(inst)) .dead else result: { - const ty = self.air.typeOf(bin_op.lhs); - - switch (ty.zigTypeTag()) { - .Vector => return self.fail("TODO implement sub_with_overflow for Vector type", .{}), - .Int => { - const int_info = ty.intInfo(self.target.*); - - if (int_info.bits > 64) { - return self.fail("TODO implement sub_with_overflow for Ints larger than 64bits", .{}); - } - - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; - - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - - const partial = try self.genBinOp(.sub, null, lhs, rhs, ty, ty); + const partial = try self.genBinOp(base_tag, null, lhs, rhs, ty, ty); const result: MCValue = switch (int_info.signedness) { .signed => .{ .register_overflow_signed = partial.register }, .unsigned => .{ .register_overflow_unsigned = partial.register }, From b57a356bb638be402ccc169c1076c53792d2ebe4 Mon Sep 17 00:00:00 2001 From: OfekShochat <50481015+OfekShochat@users.noreply.github.com> Date: Tue, 19 Apr 2022 08:15:30 +0300 Subject: [PATCH 1430/2031] std.json add stringify struct with string as array --- lib/std/json.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/std/json.zig b/lib/std/json.zig index b670e488b2..25bc2654db 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -3268,6 +3268,11 @@ test "stringify struct" { }{ .foo = 42 }, StringifyOptions{}); } +test "stringify struct with string as array" { + try teststringify("{\"foo\":\"bar\"}", .{ .foo = "bar" }, StringifyOptions{}); + try teststringify("{\"foo\":[98,97,114]}", .{ .foo = "bar" }, StringifyOptions{ .string = .Array }); +} + test "stringify struct with indentation" { try teststringify( \\{ From 10a671ad091bbb9453bc07e67086f68b07bff928 Mon Sep 17 00:00:00 2001 From: Andreas Reischuck Date: Tue, 10 May 2022 00:06:00 +0200 Subject: [PATCH 1431/2031] std.json stringify fix object keys are always is strings * extracted outputJsonString to avoid code duplication --- lib/std/json.zig | 82 +++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/lib/std/json.zig b/lib/std/json.zig index 25bc2654db..2650b98822 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -2963,6 +2963,47 @@ fn outputUnicodeEscape( } } +fn outputJsonString(value: []const u8, options: StringifyOptions, out_stream: anytype) !void { + try out_stream.writeByte('\"'); + var i: usize = 0; + while (i < value.len) : (i += 1) { + switch (value[i]) { + // normal ascii character + 0x20...0x21, 0x23...0x2E, 0x30...0x5B, 0x5D...0x7F => |c| try out_stream.writeByte(c), + // only 2 characters that *must* be escaped + '\\' => try out_stream.writeAll("\\\\"), + '\"' => try out_stream.writeAll("\\\""), + // solidus is optional to escape + '/' => { + if (options.string.String.escape_solidus) { + try out_stream.writeAll("\\/"); + } else { + try out_stream.writeByte('/'); + } + }, + // control characters with short escapes + // TODO: option to switch between unicode and 'short' forms? + 0x8 => try out_stream.writeAll("\\b"), + 0xC => try out_stream.writeAll("\\f"), + '\n' => try out_stream.writeAll("\\n"), + '\r' => try out_stream.writeAll("\\r"), + '\t' => try out_stream.writeAll("\\t"), + else => { + const ulen = std.unicode.utf8ByteSequenceLength(value[i]) catch unreachable; + // control characters (only things left with 1 byte length) should always be printed as unicode escapes + if (ulen == 1 or options.string.String.escape_unicode) { + const codepoint = std.unicode.utf8Decode(value[i .. i + ulen]) catch unreachable; + try outputUnicodeEscape(codepoint, out_stream); + } else { + try out_stream.writeAll(value[i .. i + ulen]); + } + i += ulen - 1; + }, + } + } + try out_stream.writeByte('\"'); +} + pub fn stringify( value: anytype, options: StringifyOptions, @@ -3048,7 +3089,7 @@ pub fn stringify( try out_stream.writeByte('\n'); try child_whitespace.outputIndent(out_stream); } - try stringify(Field.name, options, out_stream); + try outputJsonString(Field.name, options, out_stream); try out_stream.writeByte(':'); if (child_options.whitespace) |child_whitespace| { if (child_whitespace.separator) { @@ -3082,44 +3123,7 @@ pub fn stringify( // TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972) .Slice => { if (ptr_info.child == u8 and options.string == .String and std.unicode.utf8ValidateSlice(value)) { - try out_stream.writeByte('\"'); - var i: usize = 0; - while (i < value.len) : (i += 1) { - switch (value[i]) { - // normal ascii character - 0x20...0x21, 0x23...0x2E, 0x30...0x5B, 0x5D...0x7F => |c| try out_stream.writeByte(c), - // only 2 characters that *must* be escaped - '\\' => try out_stream.writeAll("\\\\"), - '\"' => try out_stream.writeAll("\\\""), - // solidus is optional to escape - '/' => { - if (options.string.String.escape_solidus) { - try out_stream.writeAll("\\/"); - } else { - try out_stream.writeByte('/'); - } - }, - // control characters with short escapes - // TODO: option to switch between unicode and 'short' forms? - 0x8 => try out_stream.writeAll("\\b"), - 0xC => try out_stream.writeAll("\\f"), - '\n' => try out_stream.writeAll("\\n"), - '\r' => try out_stream.writeAll("\\r"), - '\t' => try out_stream.writeAll("\\t"), - else => { - const ulen = std.unicode.utf8ByteSequenceLength(value[i]) catch unreachable; - // control characters (only things left with 1 byte length) should always be printed as unicode escapes - if (ulen == 1 or options.string.String.escape_unicode) { - const codepoint = std.unicode.utf8Decode(value[i .. i + ulen]) catch unreachable; - try outputUnicodeEscape(codepoint, out_stream); - } else { - try out_stream.writeAll(value[i .. i + ulen]); - } - i += ulen - 1; - }, - } - } - try out_stream.writeByte('\"'); + try outputJsonString(value, options, out_stream); return; } From 52651ae7a0deaffff1ecfd47b503ebb1c95aee32 Mon Sep 17 00:00:00 2001 From: Brian Gold Date: Mon, 9 May 2022 20:52:09 -0700 Subject: [PATCH 1432/2031] io_uring cancel test must tolerate CQ reordering Fixes #11382. Tested on ArchLinux 5.17.5-arch1-1. --- lib/std/os/linux/io_uring.zig | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index b67b616bca..be8b442611 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -2400,15 +2400,25 @@ test "accept/connect/recv/cancel" { try testing.expectEqual(@as(u64, 0x99999999), sqe_cancel.user_data); try testing.expectEqual(@as(u32, 1), try ring.submit()); - const cqe_recv = try ring.copy_cqe(); + var cqe_recv = try ring.copy_cqe(); if (cqe_recv.err() == .INVAL) return error.SkipZigTest; + var cqe_cancel = try ring.copy_cqe(); + if (cqe_cancel.err() == .INVAL) return error.SkipZigTest; + + // The recv/cancel CQEs may arrive in any order, the recv CQE will sometimes come first: + if (cqe_recv.user_data == 0x99999999 and cqe_cancel.user_data == 0xffffffff) { + const a = cqe_recv; + const b = cqe_cancel; + cqe_recv = b; + cqe_cancel = a; + } + try testing.expectEqual(linux.io_uring_cqe{ .user_data = 0xffffffff, .res = -@as(i32, @enumToInt(linux.E.CANCELED)), .flags = 0, }, cqe_recv); - const cqe_cancel = try ring.copy_cqe(); try testing.expectEqual(linux.io_uring_cqe{ .user_data = 0x99999999, .res = 0, From aaf4011c2caaf445fbf8cd2e27d6ce80f2bc115d Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 10 May 2022 15:57:46 +0200 Subject: [PATCH 1433/2031] Typo --- lib/std/crypto/25519/field.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/crypto/25519/field.zig b/lib/std/crypto/25519/field.zig index 277fb98d5c..ce021ffb2a 100644 --- a/lib/std/crypto/25519/field.zig +++ b/lib/std/crypto/25519/field.zig @@ -172,7 +172,7 @@ pub const Fe = struct { return fe; } - /// Substract a field elememnt + /// Substract a field element pub inline fn sub(a: Fe, b: Fe) Fe { var fe = b; comptime var i = 0; From ef9e3fb2b6cabb711b3421250b033f1696f961ae Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 10 May 2022 09:27:14 +0200 Subject: [PATCH 1434/2031] x64: migrate div to genMulDivBinOp --- src/arch/x86_64/CodeGen.zig | 431 +++++++++++++++++++----------------- 1 file changed, 230 insertions(+), 201 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 0586514a62..79a3605e23 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -592,11 +592,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .sub => try self.airBinOp(inst), .subwrap => try self.airBinOp(inst), .sub_sat => try self.airSubSat(inst), - .mul => try self.airBinOp(inst), - .mulwrap => try self.airBinOp(inst), + .mul => try self.airMulDivBinOp(inst), + .mulwrap => try self.airMulDivBinOp(inst), .mul_sat => try self.airMulSat(inst), - .rem => try self.airBinOp(inst), - .mod => try self.airBinOp(inst), + .rem => try self.airMulDivBinOp(inst), + .mod => try self.airMulDivBinOp(inst), .shl, .shl_exact => try self.airShl(inst), .shl_sat => try self.airShlSat(inst), .min => try self.airMin(inst), @@ -626,7 +626,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .mul_with_overflow => try self.airMulWithOverflow(inst), .shl_with_overflow => try self.airShlWithOverflow(inst), - .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), + .div_float, .div_trunc, .div_floor, .div_exact => try self.airMulDivBinOp(inst), .cmp_lt => try self.airCmp(inst, .lt), .cmp_lte => try self.airCmp(inst, .lte), @@ -959,6 +959,12 @@ pub fn spillCompareFlagsIfOccupied(self: *Self) !void { } } +pub fn spillRegisters(self: *Self, comptime count: comptime_int, registers: [count]Register) !void { + for (registers) |reg| { + try self.register_manager.getReg(reg, null); + } +} + /// Copies a value to a register without tracking the register. The register is not considered /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. @@ -1252,6 +1258,26 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + } + + const tag = self.air.instructions.items(.tag)[inst]; + const ty = self.air.typeOfIndex(inst); + + try self.spillRegisters(2, .{ .rax, .rdx }); + + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + const result = try self.genMulDivBinOp(tag, inst, ty, lhs, rhs); + + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +} + fn airAddSat(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)) @@ -1339,25 +1365,31 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement mul_with_overflow for Ints larger than 64bits", .{}); } - try self.spillCompareFlagsIfOccupied(); - - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - if (math.isPowerOfTwo(int_info.bits)) { + try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; - const partial = try self.genBinOp(.mul, null, lhs, rhs, ty, ty); + + try self.spillRegisters(2, .{ .rax, .rdx }); + + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + const partial = try self.genMulDivBinOp(.mul, null, ty, lhs, rhs); break :result switch (int_info.signedness) { .signed => MCValue{ .register_overflow_signed = partial.register }, .unsigned => MCValue{ .register_overflow_unsigned = partial.register }, }; } + try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = null; const dst_reg: Register = dst_reg: { switch (int_info.signedness) { .signed => { + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const rhs_lock: ?RegisterLock = switch (rhs) { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, @@ -1386,7 +1418,12 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { break :dst_reg dst_reg; }, .unsigned => { - const dst_mcv = try self.genBinOp(.mul, null, lhs, rhs, ty, ty); + try self.spillRegisters(2, .{ .rax, .rdx }); + + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + const dst_mcv = try self.genMulDivBinOp(.mul, null, ty, lhs, rhs); break :dst_reg dst_mcv.register; }, } @@ -1487,7 +1524,13 @@ fn genIntMulDivOpMir( return self.fail("TODO implement genIntMulDivOpMir for ABI size larger than 8", .{}); } - try self.genSetReg(ty, .rax, lhs); + lhs: { + switch (lhs) { + .register => |reg| if (reg.to64() == .rax) break :lhs, + else => {}, + } + try self.genSetReg(ty, .rax, lhs); + } switch (signedness) { .signed => { @@ -1609,96 +1652,6 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa return MCValue{ .register = divisor }; } -fn airDiv(self: *Self, inst: Air.Inst.Index) !void { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - - if (self.liveness.isUnused(inst)) { - return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); - } - - const tag = self.air.instructions.items(.tag)[inst]; - const ty = self.air.typeOfIndex(inst); - - if (ty.zigTypeTag() != .Int) { - return self.fail("TODO implement {} for operands of dst type {}", .{ tag, ty.zigTypeTag() }); - } - - if (tag == .div_float) { - return self.fail("TODO implement {}", .{tag}); - } - - const signedness = ty.intInfo(self.target.*).signedness; - - // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. - const track_rax: ?Air.Inst.Index = blk: { - if (signedness == .unsigned) break :blk inst; - switch (tag) { - .div_exact, .div_trunc => break :blk inst, - else => break :blk null, - } - }; - try self.register_manager.getReg(.rax, track_rax); - try self.register_manager.getReg(.rdx, null); - const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); - defer for (reg_locks) |reg| { - self.register_manager.unlockReg(reg); - }; - - const lhs = try self.resolveInst(bin_op.lhs); - const lhs_lock: ?RegisterLock = switch (lhs) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); - - const rhs: MCValue = blk: { - const rhs = try self.resolveInst(bin_op.rhs); - if (signedness == .signed) { - switch (tag) { - .div_floor => { - const rhs_lock: ?RegisterLock = switch (rhs) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); - - break :blk try self.copyToRegisterWithInstTracking(inst, ty, rhs); - }, - else => {}, - } - } - break :blk rhs; - }; - const rhs_lock: ?RegisterLock = switch (rhs) { - .register => |reg| self.register_manager.lockReg(reg), - else => null, - }; - defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); - - const result: MCValue = result: { - if (signedness == .unsigned) { - try self.genIntMulDivOpMir(.div, ty, signedness, lhs, rhs); - break :result MCValue{ .register = .rax }; - } - - switch (tag) { - .div_exact, .div_trunc => { - try self.genIntMulDivOpMir(switch (signedness) { - .signed => .idiv, - .unsigned => .div, - }, ty, signedness, lhs, rhs); - break :result MCValue{ .register = .rax }; - }, - .div_floor => { - break :result try self.genInlineIntDivFloor(ty, lhs, rhs); - }, - else => unreachable, - } - }; - - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airShl(self: *Self, inst: Air.Inst.Index) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; if (self.liveness.isUnused(inst)) { @@ -3143,6 +3096,170 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +/// Result is always a register. +/// Clobbers .rax and .rdx therefore care needs to be taken to spill operands upfront. +/// Asserts .rax and .rdx are free. +fn genMulDivBinOp( + self: *Self, + tag: Air.Inst.Tag, + maybe_inst: ?Air.Inst.Index, + ty: Type, + lhs: MCValue, + rhs: MCValue, +) !MCValue { + if (ty.zigTypeTag() == .Vector or ty.zigTypeTag() == .Float) { + return self.fail("TODO implement genBinOp for {}", .{ty.fmtDebug()}); + } + if (ty.abiSize(self.target.*) > 8) { + return self.fail("TODO implement genBinOp for {}", .{ty.fmtDebug()}); + } + if (tag == .div_float) { + return self.fail("TODO implement genMulDivBinOp for div_float", .{}); + } + + assert(self.register_manager.isRegFree(.rax)); + assert(self.register_manager.isRegFree(.rdx)); + + const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); + defer for (reg_locks) |reg| { + self.register_manager.unlockReg(reg); + }; + + const int_info = ty.intInfo(self.target.*); + const signedness = int_info.signedness; + + switch (tag) { + .mul, + .mulwrap, + => { + try self.register_manager.getReg(.rax, maybe_inst); + try self.register_manager.getReg(.rdx, null); + + try self.genIntMulDivOpMir(switch (signedness) { + .signed => .imul, + .unsigned => .mul, + }, ty, signedness, lhs, rhs); + + return switch (signedness) { + .signed => MCValue{ .register = .rax }, + .unsigned => MCValue{ .register = registerAlias(.rax, @intCast(u32, ty.abiSize(self.target.*))) }, + }; + }, + .mod, + .rem, + => { + const track_inst_rdx: ?Air.Inst.Index = switch (tag) { + .mod => if (signedness == .unsigned) maybe_inst else null, + .rem => maybe_inst, + else => unreachable, + }; + + try self.register_manager.getReg(.rax, null); + try self.register_manager.getReg(.rdx, track_inst_rdx); + + switch (signedness) { + .signed => { + switch (tag) { + .rem => { + try self.genIntMulDivOpMir(.idiv, ty, .signed, lhs, rhs); + return MCValue{ .register = .rdx }; + }, + .mod => { + const div_floor = try self.genInlineIntDivFloor(ty, lhs, rhs); + try self.genIntMulComplexOpMir(ty, div_floor, rhs); + const div_floor_lock = self.register_manager.lockReg(div_floor.register); + defer if (div_floor_lock) |lock| self.register_manager.unlockReg(lock); + + const result: MCValue = if (maybe_inst) |inst| + try self.copyToRegisterWithInstTracking(inst, ty, lhs) + else + MCValue{ .register = try self.copyToTmpRegister(ty, lhs) }; + try self.genBinOpMir(.sub, ty, result, div_floor); + + return result; + }, + else => unreachable, + } + }, + .unsigned => { + try self.genIntMulDivOpMir(.div, ty, .unsigned, lhs, rhs); + return MCValue{ .register = .rdx }; + }, + } + }, + .div_trunc, + .div_floor, + .div_exact, + => { + const track_inst_rax: ?Air.Inst.Index = blk: { + if (signedness == .unsigned) break :blk maybe_inst; + switch (tag) { + .div_exact, .div_trunc => break :blk maybe_inst, + else => break :blk null, + } + }; + + try self.register_manager.getReg(.rax, track_inst_rax); + try self.register_manager.getReg(.rdx, null); + + const lhs_lock: ?RegisterLock = switch (lhs) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); + + const actual_rhs: MCValue = blk: { + if (signedness == .signed) { + switch (tag) { + .div_floor => { + const rhs_lock: ?RegisterLock = switch (rhs) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); + + if (maybe_inst) |inst| { + break :blk try self.copyToRegisterWithInstTracking(inst, ty, rhs); + } + break :blk MCValue{ .register = try self.copyToTmpRegister(ty, rhs) }; + }, + else => {}, + } + } + break :blk rhs; + }; + const rhs_lock: ?RegisterLock = switch (actual_rhs) { + .register => |reg| self.register_manager.lockReg(reg), + else => null, + }; + defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); + + const result: MCValue = result: { + if (signedness == .unsigned) { + try self.genIntMulDivOpMir(.div, ty, signedness, lhs, actual_rhs); + break :result MCValue{ .register = .rax }; + } + + switch (tag) { + .div_exact, .div_trunc => { + try self.genIntMulDivOpMir(switch (signedness) { + .signed => .idiv, + .unsigned => .div, + }, ty, signedness, lhs, actual_rhs); + break :result MCValue{ .register = .rax }; + }, + .div_floor => { + break :result try self.genInlineIntDivFloor(ty, lhs, actual_rhs); + }, + else => unreachable, + } + }; + return result; + }, + else => unreachable, + } +} + /// Result is always a register. fn genBinOp( self: *Self, @@ -3153,6 +3270,13 @@ fn genBinOp( lhs_ty: Type, rhs_ty: Type, ) !MCValue { + if (lhs_ty.zigTypeTag() == .Vector or lhs_ty.zigTypeTag() == .Float) { + return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()}); + } + if (lhs_ty.abiSize(self.target.*) > 8) { + return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()}); + } + const is_commutative: bool = switch (tag) { .add, .addwrap, @@ -3163,106 +3287,9 @@ fn genBinOp( .xor, => true, - .sub, - .subwrap, - .mul, - .mulwrap, - .div_exact, - .div_trunc, - .rem, - .mod, - .shl, - .shr, - .ptr_add, - .ptr_sub, - => false, - - .div_float => return self.fail("TODO implement genBinOp for {}", .{tag}), - - else => unreachable, + else => false, }; - if (lhs_ty.zigTypeTag() == .Vector or lhs_ty.zigTypeTag() == .Float) { - return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()}); - } - if (lhs_ty.abiSize(self.target.*) > 8) { - return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()}); - } - - switch (tag) { - .mul, - .mulwrap, - => { - // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. - try self.register_manager.getReg(.rax, maybe_inst); - try self.register_manager.getReg(.rdx, null); - const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); - defer for (reg_locks) |reg| { - self.register_manager.unlockReg(reg); - }; - - const int_info = lhs_ty.intInfo(self.target.*); - try self.genIntMulDivOpMir(switch (int_info.signedness) { - .signed => .imul, - .unsigned => .mul, - }, lhs_ty, int_info.signedness, lhs, rhs); - - return switch (int_info.signedness) { - .signed => MCValue{ .register = .rax }, - .unsigned => MCValue{ .register = registerAlias(.rax, @intCast(u32, lhs_ty.abiSize(self.target.*))) }, - }; - }, - .mod, - .rem, - => { - const int_info = lhs_ty.intInfo(self.target.*); - const track_inst_rdx: ?Air.Inst.Index = switch (tag) { - .mod => if (int_info.signedness == .unsigned) maybe_inst else null, - .rem => maybe_inst, - else => unreachable, - }; - - // Spill .rax and .rdx upfront to ensure we don't spill the operands too late. - try self.register_manager.getReg(.rax, null); - try self.register_manager.getReg(.rdx, track_inst_rdx); - const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); - defer for (reg_locks) |reg| { - self.register_manager.unlockReg(reg); - }; - - switch (int_info.signedness) { - .signed => { - switch (tag) { - .rem => { - try self.genIntMulDivOpMir(.idiv, lhs_ty, .signed, lhs, rhs); - return MCValue{ .register = .rdx }; - }, - .mod => { - const div_floor = try self.genInlineIntDivFloor(lhs_ty, lhs, rhs); - try self.genIntMulComplexOpMir(lhs_ty, div_floor, rhs); - const div_floor_lock = self.register_manager.lockReg(div_floor.register); - defer if (div_floor_lock) |lock| self.register_manager.unlockReg(lock); - - const result: MCValue = if (maybe_inst) |inst| - try self.copyToRegisterWithInstTracking(inst, lhs_ty, lhs) - else - MCValue{ .register = try self.copyToTmpRegister(lhs_ty, lhs) }; - try self.genBinOpMir(.sub, lhs_ty, result, div_floor); - - return result; - }, - else => unreachable, - } - }, - .unsigned => { - try self.genIntMulDivOpMir(.div, lhs_ty, .unsigned, lhs, rhs); - return MCValue{ .register = .rdx }; - }, - } - }, - else => {}, - } - const lhs_lock: ?RegisterLock = switch (lhs) { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), else => null, @@ -5460,6 +5487,7 @@ fn genInlineMemcpy( len: MCValue, opts: InlineMemcpyOpts, ) InnerError!void { + // TODO preserve contents of .rax and .rcx if not free, and then restore try self.register_manager.getReg(.rax, null); try self.register_manager.getReg(.rcx, null); @@ -5657,6 +5685,7 @@ fn genInlineMemset( len: MCValue, opts: InlineMemcpyOpts, ) InnerError!void { + // TODO preserve contents of .rax and then restore try self.register_manager.getReg(.rax, null); const rax_lock = self.register_manager.lockRegAssumeUnused(.rax); defer self.register_manager.unlockReg(rax_lock); From aef3c149e64746dac01f4d48a2835abd4204e625 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 10 May 2022 17:25:49 +0200 Subject: [PATCH 1435/2031] x64: refactor genMulDivBinOp helper --- src/arch/x86_64/CodeGen.zig | 178 ++++++++++++++++++------------------ 1 file changed, 87 insertions(+), 91 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 79a3605e23..82c7593a85 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -3131,75 +3131,82 @@ fn genMulDivBinOp( switch (tag) { .mul, .mulwrap, - => { - try self.register_manager.getReg(.rax, maybe_inst); - try self.register_manager.getReg(.rdx, null); - - try self.genIntMulDivOpMir(switch (signedness) { - .signed => .imul, - .unsigned => .mul, - }, ty, signedness, lhs, rhs); - - return switch (signedness) { - .signed => MCValue{ .register = .rax }, - .unsigned => MCValue{ .register = registerAlias(.rax, @intCast(u32, ty.abiSize(self.target.*))) }, - }; - }, - .mod, .rem, + .div_trunc, + .div_exact, => { - const track_inst_rdx: ?Air.Inst.Index = switch (tag) { - .mod => if (signedness == .unsigned) maybe_inst else null, - .rem => maybe_inst, - else => unreachable, + const track_inst_rax: ?Air.Inst.Index = switch (tag) { + .mul, .mulwrap, .div_exact, .div_trunc => maybe_inst, + else => null, }; - - try self.register_manager.getReg(.rax, null); + const track_inst_rdx: ?Air.Inst.Index = switch (tag) { + .rem => maybe_inst, + else => null, + }; + try self.register_manager.getReg(.rax, track_inst_rax); try self.register_manager.getReg(.rdx, track_inst_rdx); - switch (signedness) { - .signed => { - switch (tag) { - .rem => { - try self.genIntMulDivOpMir(.idiv, ty, .signed, lhs, rhs); - return MCValue{ .register = .rdx }; - }, - .mod => { - const div_floor = try self.genInlineIntDivFloor(ty, lhs, rhs); - try self.genIntMulComplexOpMir(ty, div_floor, rhs); - const div_floor_lock = self.register_manager.lockReg(div_floor.register); - defer if (div_floor_lock) |lock| self.register_manager.unlockReg(lock); - - const result: MCValue = if (maybe_inst) |inst| - try self.copyToRegisterWithInstTracking(inst, ty, lhs) - else - MCValue{ .register = try self.copyToTmpRegister(ty, lhs) }; - try self.genBinOpMir(.sub, ty, result, div_floor); - - return result; - }, - else => unreachable, - } + const mir_tag: Mir.Inst.Tag = switch (signedness) { + .signed => switch (tag) { + .mul, .mulwrap => Mir.Inst.Tag.imul, + .div_trunc, .div_exact, .rem => Mir.Inst.Tag.idiv, + else => unreachable, }, - .unsigned => { - try self.genIntMulDivOpMir(.div, ty, .unsigned, lhs, rhs); - return MCValue{ .register = .rdx }; + .unsigned => switch (tag) { + .mul, .mulwrap => Mir.Inst.Tag.mul, + .div_trunc, .div_exact, .rem => Mir.Inst.Tag.div, + else => unreachable, + }, + }; + + try self.genIntMulDivOpMir(mir_tag, ty, .signed, lhs, rhs); + + switch (signedness) { + .signed => switch (tag) { + .mul, .mulwrap, .div_trunc, .div_exact => return MCValue{ .register = .rax }, + .rem => return MCValue{ .register = .rdx }, + else => unreachable, + }, + .unsigned => switch (tag) { + .mul, .mulwrap, .div_trunc, .div_exact => return MCValue{ + .register = registerAlias(.rax, @intCast(u32, ty.abiSize(self.target.*))), + }, + .rem => return MCValue{ + .register = registerAlias(.rdx, @intCast(u32, ty.abiSize(self.target.*))), + }, + else => unreachable, }, } }, - .div_trunc, - .div_floor, - .div_exact, - => { - const track_inst_rax: ?Air.Inst.Index = blk: { - if (signedness == .unsigned) break :blk maybe_inst; - switch (tag) { - .div_exact, .div_trunc => break :blk maybe_inst, - else => break :blk null, - } - }; - try self.register_manager.getReg(.rax, track_inst_rax); + .mod => { + try self.register_manager.getReg(.rax, null); + try self.register_manager.getReg(.rdx, if (signedness == .unsigned) maybe_inst else null); + + switch (signedness) { + .signed => { + const div_floor = try self.genInlineIntDivFloor(ty, lhs, rhs); + try self.genIntMulComplexOpMir(ty, div_floor, rhs); + const div_floor_lock = self.register_manager.lockReg(div_floor.register); + defer if (div_floor_lock) |lock| self.register_manager.unlockReg(lock); + + const result: MCValue = if (maybe_inst) |inst| + try self.copyToRegisterWithInstTracking(inst, ty, lhs) + else + MCValue{ .register = try self.copyToTmpRegister(ty, lhs) }; + try self.genBinOpMir(.sub, ty, result, div_floor); + + return result; + }, + .unsigned => { + try self.genIntMulDivOpMir(.div, ty, .unsigned, lhs, rhs); + return MCValue{ .register = registerAlias(.rdx, @intCast(u32, ty.abiSize(self.target.*))) }; + }, + } + }, + + .div_floor => { + try self.register_manager.getReg(.rax, if (signedness == .unsigned) maybe_inst else null); try self.register_manager.getReg(.rdx, null); const lhs_lock: ?RegisterLock = switch (lhs) { @@ -3209,24 +3216,21 @@ fn genMulDivBinOp( defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); const actual_rhs: MCValue = blk: { - if (signedness == .signed) { - switch (tag) { - .div_floor => { - const rhs_lock: ?RegisterLock = switch (rhs) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); + switch (signedness) { + .signed => { + const rhs_lock: ?RegisterLock = switch (rhs) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); - if (maybe_inst) |inst| { - break :blk try self.copyToRegisterWithInstTracking(inst, ty, rhs); - } - break :blk MCValue{ .register = try self.copyToTmpRegister(ty, rhs) }; - }, - else => {}, - } + if (maybe_inst) |inst| { + break :blk try self.copyToRegisterWithInstTracking(inst, ty, rhs); + } + break :blk MCValue{ .register = try self.copyToTmpRegister(ty, rhs) }; + }, + .unsigned => break :blk rhs, } - break :blk rhs; }; const rhs_lock: ?RegisterLock = switch (actual_rhs) { .register => |reg| self.register_manager.lockReg(reg), @@ -3235,27 +3239,19 @@ fn genMulDivBinOp( defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); const result: MCValue = result: { - if (signedness == .unsigned) { - try self.genIntMulDivOpMir(.div, ty, signedness, lhs, actual_rhs); - break :result MCValue{ .register = .rax }; - } - - switch (tag) { - .div_exact, .div_trunc => { - try self.genIntMulDivOpMir(switch (signedness) { - .signed => .idiv, - .unsigned => .div, - }, ty, signedness, lhs, actual_rhs); - break :result MCValue{ .register = .rax }; + switch (signedness) { + .signed => break :result try self.genInlineIntDivFloor(ty, lhs, actual_rhs), + .unsigned => { + try self.genIntMulDivOpMir(.div, ty, .unsigned, lhs, actual_rhs); + break :result MCValue{ + .register = registerAlias(.rax, @intCast(u32, ty.abiSize(self.target.*))), + }; }, - .div_floor => { - break :result try self.genInlineIntDivFloor(ty, lhs, actual_rhs); - }, - else => unreachable, } }; return result; }, + else => unreachable, } } From 6a4e445f5aaadf8e87ec08499c84f1d5f279e8b3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 10 May 2022 17:53:53 +0200 Subject: [PATCH 1436/2031] x64: pull shl and shr into one helper fn --- src/arch/x86_64/CodeGen.zig | 253 +++++++++++++++++------------------- 1 file changed, 121 insertions(+), 132 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 82c7593a85..d26ec613fd 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -597,7 +597,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .mul_sat => try self.airMulSat(inst), .rem => try self.airMulDivBinOp(inst), .mod => try self.airMulDivBinOp(inst), - .shl, .shl_exact => try self.airShl(inst), + .shl, .shl_exact => try self.airShlShrBinOp(inst), .shl_sat => try self.airShlSat(inst), .min => try self.airMin(inst), .max => try self.airMax(inst), @@ -643,7 +643,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .bit_and => try self.airBinOp(inst), .bit_or => try self.airBinOp(inst), .xor => try self.airBinOp(inst), - .shr, .shr_exact => try self.airShr(inst), + .shr, .shr_exact => try self.airShlShrBinOp(inst), .alloc => try self.airAlloc(inst), .ret_ptr => try self.airRetPtr(inst), @@ -1652,64 +1652,22 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa return MCValue{ .register = divisor }; } -fn airShl(self: *Self, inst: Air.Inst.Index) !void { +fn airShlShrBinOp(self: *Self, inst: Air.Inst.Index) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; + if (self.liveness.isUnused(inst)) { return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); } - const ty = self.air.typeOfIndex(inst); - const tag = self.air.instructions.items(.tag)[inst]; - switch (tag) { - .shl_exact => return self.fail("TODO implement {} for type {}", .{ tag, ty.fmtDebug() }), - .shl => {}, - else => unreachable, - } + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); - if (ty.zigTypeTag() != .Int) { - return self.fail("TODO implement .shl for type {}", .{ty.fmtDebug()}); - } - if (ty.abiSize(self.target.*) > 8) { - return self.fail("TODO implement .shl for integers larger than 8 bytes", .{}); - } + const result = try self.genShiftBinOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); - // TODO look into reusing the operands - // TODO audit register allocation mechanics - const shift = try self.resolveInst(bin_op.rhs); - const shift_ty = self.air.typeOf(bin_op.rhs); - - blk: { - switch (shift) { - .register => |reg| { - if (reg.to64() == .rcx) break :blk; - }, - else => {}, - } - try self.register_manager.getReg(.rcx, null); - try self.genSetReg(shift_ty, .rcx, shift); - } - const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx); - defer self.register_manager.unlockReg(rcx_lock); - - const value = try self.resolveInst(bin_op.lhs); - const value_lock: ?RegisterLock = switch (value) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (value_lock) |lock| self.register_manager.unlockReg(lock); - - const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, value); - _ = try self.addInst(.{ - .tag = .sal, - .ops = (Mir.Ops{ - .reg1 = dst_mcv.register, - .flags = 0b01, - }).encode(), - .data = undefined, - }); - - return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none }); + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { @@ -1721,80 +1679,6 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airShr(self: *Self, inst: Air.Inst.Index) !void { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - - if (self.liveness.isUnused(inst)) { - return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); - } - - const ty = self.air.typeOfIndex(inst); - const tag = self.air.instructions.items(.tag)[inst]; - switch (tag) { - .shr_exact => return self.fail("TODO implement shr_exact for type {}", .{ty.fmtDebug()}), - .shr => {}, - else => unreachable, - } - - if (ty.zigTypeTag() != .Int) { - return self.fail("TODO implement shr for type {}", .{ty.fmtDebug()}); - } - if (ty.abiSize(self.target.*) > 8) { - return self.fail("TODO implement shr for integers larger than 8 bytes", .{}); - } - - // TODO look into reusing the operands - // TODO audit register allocation mechanics - const shift = try self.resolveInst(bin_op.rhs); - const shift_ty = self.air.typeOf(bin_op.rhs); - - blk: { - switch (shift) { - .register => |reg| { - if (reg.to64() == .rcx) break :blk; - }, - else => {}, - } - try self.register_manager.getReg(.rcx, null); - try self.genSetReg(shift_ty, .rcx, shift); - } - const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx); - defer self.register_manager.unlockReg(rcx_lock); - - const value = try self.resolveInst(bin_op.lhs); - const value_lock: ?RegisterLock = switch (value) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (value_lock) |lock| self.register_manager.unlockReg(lock); - - const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, value); - switch (ty.intInfo(self.target.*).signedness) { - .signed => { - _ = try self.addInst(.{ - .tag = .sar, - .ops = (Mir.Ops{ - .reg1 = dst_mcv.register, - .flags = 0b01, - }).encode(), - .data = undefined, - }); - }, - .unsigned => { - _ = try self.addInst(.{ - .tag = .shr, - .ops = (Mir.Ops{ - .reg1 = dst_mcv.register, - .flags = 0b01, - }).encode(), - .data = undefined, - }); - }, - } - - return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none }); -} - fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; if (self.liveness.isUnused(inst)) { @@ -1822,7 +1706,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { // TODO reuse the operand const result = try self.copyToRegisterWithInstTracking(inst, optional_ty, operand); const shift = @intCast(u8, offset * @sizeOf(usize)); - try self.shiftRegister(result.register, @intCast(u8, shift)); + try self.shiftRegisterRightUnsigned(result.register, @intCast(u8, shift)); break :result result; }, else => return self.fail("TODO implement optional_payload when operand is {}", .{operand}), @@ -1909,7 +1793,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { // TODO reuse operand const shift = @intCast(u6, err_abi_size * @sizeOf(usize)); const result = try self.copyToRegisterWithInstTracking(inst, err_union_ty, operand); - try self.shiftRegister(result.register.to64(), shift); + try self.shiftRegisterRightUnsigned(result.register.to64(), shift); break :result MCValue{ .register = registerAlias(result.register, @intCast(u32, payload_ty.abiSize(self.target.*))), }; @@ -2421,7 +2305,7 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { else 0; const result = try self.copyToRegisterWithInstTracking(inst, union_ty, operand); - try self.shiftRegister(result.register.to64(), shift); + try self.shiftRegisterRightUnsigned(result.register.to64(), shift); break :blk MCValue{ .register = registerAlias(result.register, @intCast(u32, layout.tag_size)), }; @@ -3020,7 +2904,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { // Shift by struct_field_offset. const shift = @intCast(u8, struct_field_offset * @sizeOf(usize)); - try self.shiftRegister(dst_mcv.register, shift); + try self.shiftRegisterRightUnsigned(dst_mcv.register, shift); // Mask with reg.size() - struct_field_size const max_reg_bit_width = Register.rax.size(); @@ -3097,7 +2981,111 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { } /// Result is always a register. -/// Clobbers .rax and .rdx therefore care needs to be taken to spill operands upfront. +/// Clobbers .rcx therefore care is needed to spill .rcx upfront. +/// Asserts .rcx is free. +fn genShiftBinOp( + self: *Self, + tag: Air.Inst.Tag, + maybe_inst: ?Air.Inst.Index, + lhs: MCValue, + rhs: MCValue, + lhs_ty: Type, + rhs_ty: Type, +) !MCValue { + if (lhs_ty.zigTypeTag() == .Vector or lhs_ty.zigTypeTag() == .Float) { + return self.fail("TODO implement genShiftBinOp for {}", .{lhs_ty.fmtDebug()}); + } + if (lhs_ty.abiSize(self.target.*) > 8) { + return self.fail("TODO implement genShiftBinOp for {}", .{lhs_ty.fmtDebug()}); + } + + assert(self.register_manager.isRegFree(.rcx)); + + try self.register_manager.getReg(.rcx, null); + try self.genSetReg(rhs_ty, .rcx, rhs); + const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx); + defer self.register_manager.unlockReg(rcx_lock); + + const int_info = lhs_ty.intInfo(self.target.*); + const signedness = int_info.signedness; + + const lhs_lock: ?RegisterLock = switch (lhs) { + .register => |reg| self.register_manager.lockReg(reg), + else => null, + }; + defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); + + const rhs_lock: ?RegisterLock = switch (rhs) { + .register => |reg| self.register_manager.lockReg(reg), + else => null, + }; + defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); + + const dst: MCValue = blk: { + if (maybe_inst) |inst| { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + // TODO dst can also be a memory location + if (self.reuseOperand(inst, bin_op.lhs, 0, lhs) and lhs.isRegister()) { + break :blk lhs; + } + break :blk try self.copyToRegisterWithInstTracking(inst, lhs_ty, lhs); + } + break :blk MCValue{ .register = try self.copyToTmpRegister(lhs_ty, lhs) }; + }; + + switch (tag) { + .shl => switch (signedness) { + .signed => { + _ = try self.addInst(.{ + .tag = .sal, + .ops = (Mir.Ops{ + .reg1 = dst.register, + .flags = 0b01, + }).encode(), + .data = undefined, + }); + }, + .unsigned => { + _ = try self.addInst(.{ + .tag = .shl, + .ops = (Mir.Ops{ + .reg1 = dst.register, + .flags = 0b01, + }).encode(), + .data = undefined, + }); + }, + }, + .shr => switch (signedness) { + .signed => { + _ = try self.addInst(.{ + .tag = .sar, + .ops = (Mir.Ops{ + .reg1 = dst.register, + .flags = 0b01, + }).encode(), + .data = undefined, + }); + }, + .unsigned => { + _ = try self.addInst(.{ + .tag = .shr, + .ops = (Mir.Ops{ + .reg1 = dst.register, + .flags = 0b01, + }).encode(), + .data = undefined, + }); + }, + }, + else => unreachable, + } + + return dst; +} + +/// Result is always a register. +/// Clobbers .rax and .rdx therefore care is needed to spill .rax and .rdx upfront. /// Asserts .rax and .rdx are free. fn genMulDivBinOp( self: *Self, @@ -6790,7 +6778,8 @@ fn registerAlias(reg: Register, size_bytes: u32) Register { } } -fn shiftRegister(self: *Self, reg: Register, shift: u8) !void { +/// Shifts register right without sign-extension. +fn shiftRegisterRightUnsigned(self: *Self, reg: Register, shift: u8) !void { if (shift == 0) return; if (shift == 1) { _ = try self.addInst(.{ From a9514ae1732df1fdc142b602e62ab0d12f693afa Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 10 May 2022 18:16:14 +0200 Subject: [PATCH 1437/2031] x64: handle immediate as RHS of shift bin ops --- src/arch/x86_64/CodeGen.zig | 50 ++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index d26ec613fd..d129fc493b 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1659,6 +1659,8 @@ fn airShlShrBinOp(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); } + try self.spillRegisters(1, .{.rcx}); + const tag = self.air.instructions.items(.tag)[inst]; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); @@ -2981,7 +2983,7 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { } /// Result is always a register. -/// Clobbers .rcx therefore care is needed to spill .rcx upfront. +/// Clobbers .rcx for non-immediate rhs, therefore care is needed to spill .rcx upfront. /// Asserts .rcx is free. fn genShiftBinOp( self: *Self, @@ -2999,12 +3001,7 @@ fn genShiftBinOp( return self.fail("TODO implement genShiftBinOp for {}", .{lhs_ty.fmtDebug()}); } - assert(self.register_manager.isRegFree(.rcx)); - - try self.register_manager.getReg(.rcx, null); - try self.genSetReg(rhs_ty, .rcx, rhs); - const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx); - defer self.register_manager.unlockReg(rcx_lock); + assert(rhs_ty.abiSize(self.target.*) == 1); const int_info = lhs_ty.intInfo(self.target.*); const signedness = int_info.signedness; @@ -3021,6 +3018,29 @@ fn genShiftBinOp( }; defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); + const flags: u2 = blk: { + if (rhs.isImmediate()) { + const flags: u2 = switch (rhs.immediate) { + 0 => unreachable, // TODO is this valid? + 1 => 0b00, + else => 0b10, + }; + break :blk flags; + } + + assert(self.register_manager.isRegFree(.rcx)); + + try self.register_manager.getReg(.rcx, null); + try self.genSetReg(rhs_ty, .rcx, rhs); + const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx); + defer self.register_manager.unlockReg(rcx_lock); + + break :blk 0b01; + }; + const data: Mir.Inst.Data = if (rhs.isImmediate()) .{ + .imm = @intCast(u8, rhs.immediate), + } else undefined; + const dst: MCValue = blk: { if (maybe_inst) |inst| { const bin_op = self.air.instructions.items(.data)[inst].bin_op; @@ -3040,9 +3060,9 @@ fn genShiftBinOp( .tag = .sal, .ops = (Mir.Ops{ .reg1 = dst.register, - .flags = 0b01, + .flags = flags, }).encode(), - .data = undefined, + .data = data, }); }, .unsigned => { @@ -3050,9 +3070,9 @@ fn genShiftBinOp( .tag = .shl, .ops = (Mir.Ops{ .reg1 = dst.register, - .flags = 0b01, + .flags = flags, }).encode(), - .data = undefined, + .data = data, }); }, }, @@ -3062,9 +3082,9 @@ fn genShiftBinOp( .tag = .sar, .ops = (Mir.Ops{ .reg1 = dst.register, - .flags = 0b01, + .flags = flags, }).encode(), - .data = undefined, + .data = data, }); }, .unsigned => { @@ -3072,9 +3092,9 @@ fn genShiftBinOp( .tag = .shr, .ops = (Mir.Ops{ .reg1 = dst.register, - .flags = 0b01, + .flags = flags, }).encode(), - .data = undefined, + .data = data, }); }, }, From 9725205859517ce81e201b79b55f7a7c2cb87f20 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 10 May 2022 18:52:49 +0200 Subject: [PATCH 1438/2031] x64: consolidate shifts into single MIR helper fn --- src/arch/x86_64/CodeGen.zig | 201 +++++++++++++++--------------------- 1 file changed, 81 insertions(+), 120 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index d129fc493b..72c65e84de 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1708,7 +1708,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { // TODO reuse the operand const result = try self.copyToRegisterWithInstTracking(inst, optional_ty, operand); const shift = @intCast(u8, offset * @sizeOf(usize)); - try self.shiftRegisterRightUnsigned(result.register, @intCast(u8, shift)); + try self.genShiftBinOpMir(optional_ty, result.register, .{ .immediate = @intCast(u8, shift) }, .right); break :result result; }, else => return self.fail("TODO implement optional_payload when operand is {}", .{operand}), @@ -1795,7 +1795,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { // TODO reuse operand const shift = @intCast(u6, err_abi_size * @sizeOf(usize)); const result = try self.copyToRegisterWithInstTracking(inst, err_union_ty, operand); - try self.shiftRegisterRightUnsigned(result.register.to64(), shift); + try self.genShiftBinOpMir(Type.usize, result.register, .{ .immediate = shift }, .right); break :result MCValue{ .register = registerAlias(result.register, @intCast(u32, payload_ty.abiSize(self.target.*))), }; @@ -2307,7 +2307,7 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { else 0; const result = try self.copyToRegisterWithInstTracking(inst, union_ty, operand); - try self.shiftRegisterRightUnsigned(result.register.to64(), shift); + try self.genShiftBinOpMir(Type.usize, result.register, .{ .immediate = shift }, .right); break :blk MCValue{ .register = registerAlias(result.register, @intCast(u32, layout.tag_size)), }; @@ -2906,7 +2906,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { // Shift by struct_field_offset. const shift = @intCast(u8, struct_field_offset * @sizeOf(usize)); - try self.shiftRegisterRightUnsigned(dst_mcv.register, shift); + try self.genShiftBinOpMir(Type.usize, dst_mcv.register, .{ .immediate = shift }, .right); // Mask with reg.size() - struct_field_size const max_reg_bit_width = Register.rax.size(); @@ -2982,6 +2982,74 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +/// Clobbers .rcx for non-immediate shift value. +fn genShiftBinOpMir(self: *Self, ty: Type, reg: Register, shift: MCValue, direction: enum { left, right }) !void { + assert(reg.to64() != .rcx); + + const abi_size = @intCast(u32, ty.abiSize(self.target.*)); + const signedness: std.builtin.Signedness = blk: { + if (ty.zigTypeTag() != .Int) break :blk .unsigned; + break :blk ty.intInfo(self.target.*).signedness; + }; + + const tag: Mir.Inst.Tag = switch (signedness) { + .signed => switch (direction) { + .left => Mir.Inst.Tag.sal, + .right => Mir.Inst.Tag.sar, + }, + .unsigned => switch (direction) { + .left => Mir.Inst.Tag.shl, + .right => Mir.Inst.Tag.shr, + }, + }; + + blk: { + switch (shift) { + .immediate => |imm| switch (imm) { + 0 => return, + 1 => { + _ = try self.addInst(.{ + .tag = tag, + .ops = (Mir.Ops{ + .reg1 = registerAlias(reg, abi_size), + .flags = 0b00, + }).encode(), + .data = undefined, + }); + return; + }, + else => { + _ = try self.addInst(.{ + .tag = tag, + .ops = (Mir.Ops{ + .reg1 = registerAlias(reg, abi_size), + .flags = 0b10, + }).encode(), + .data = .{ .imm = @intCast(u8, imm) }, + }); + return; + }, + }, + .register => |shift_reg| { + if (shift_reg == .rcx) break :blk; + }, + else => {}, + } + assert(self.register_manager.isRegFree(.rcx)); + try self.register_manager.getReg(.rcx, null); + try self.genSetReg(Type.u8, .rcx, shift); + } + + _ = try self.addInst(.{ + .tag = tag, + .ops = (Mir.Ops{ + .reg1 = registerAlias(reg, abi_size), + .flags = 0b01, + }).encode(), + .data = undefined, + }); +} + /// Result is always a register. /// Clobbers .rcx for non-immediate rhs, therefore care is needed to spill .rcx upfront. /// Asserts .rcx is free. @@ -3003,9 +3071,6 @@ fn genShiftBinOp( assert(rhs_ty.abiSize(self.target.*) == 1); - const int_info = lhs_ty.intInfo(self.target.*); - const signedness = int_info.signedness; - const lhs_lock: ?RegisterLock = switch (lhs) { .register => |reg| self.register_manager.lockReg(reg), else => null, @@ -3018,28 +3083,10 @@ fn genShiftBinOp( }; defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); - const flags: u2 = blk: { - if (rhs.isImmediate()) { - const flags: u2 = switch (rhs.immediate) { - 0 => unreachable, // TODO is this valid? - 1 => 0b00, - else => 0b10, - }; - break :blk flags; - } - - assert(self.register_manager.isRegFree(.rcx)); - - try self.register_manager.getReg(.rcx, null); - try self.genSetReg(rhs_ty, .rcx, rhs); - const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx); - defer self.register_manager.unlockReg(rcx_lock); - - break :blk 0b01; - }; - const data: Mir.Inst.Data = if (rhs.isImmediate()) .{ - .imm = @intCast(u8, rhs.immediate), - } else undefined; + assert(self.register_manager.isRegFree(.rcx)); + try self.register_manager.getReg(.rcx, null); + const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx); + defer self.register_manager.unlockReg(rcx_lock); const dst: MCValue = blk: { if (maybe_inst) |inst| { @@ -3054,50 +3101,8 @@ fn genShiftBinOp( }; switch (tag) { - .shl => switch (signedness) { - .signed => { - _ = try self.addInst(.{ - .tag = .sal, - .ops = (Mir.Ops{ - .reg1 = dst.register, - .flags = flags, - }).encode(), - .data = data, - }); - }, - .unsigned => { - _ = try self.addInst(.{ - .tag = .shl, - .ops = (Mir.Ops{ - .reg1 = dst.register, - .flags = flags, - }).encode(), - .data = data, - }); - }, - }, - .shr => switch (signedness) { - .signed => { - _ = try self.addInst(.{ - .tag = .sar, - .ops = (Mir.Ops{ - .reg1 = dst.register, - .flags = flags, - }).encode(), - .data = data, - }); - }, - .unsigned => { - _ = try self.addInst(.{ - .tag = .shr, - .ops = (Mir.Ops{ - .reg1 = dst.register, - .flags = flags, - }).encode(), - .data = data, - }); - }, - }, + .shl => try self.genShiftBinOpMir(lhs_ty, dst.register, rhs, .left), + .shr => try self.genShiftBinOpMir(lhs_ty, dst.register, rhs, .right), else => unreachable, } @@ -5422,14 +5427,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl }); if (nearest_power_of_two > 1) { - _ = try self.addInst(.{ - .tag = .shr, - .ops = (Mir.Ops{ - .reg1 = tmp_reg, - .flags = 0b10, - }).encode(), - .data = .{ .imm = nearest_power_of_two * 8 }, - }); + try self.genShiftBinOpMir(ty, tmp_reg, .{ .immediate = nearest_power_of_two * 8 }, .right); } remainder -= nearest_power_of_two; @@ -6798,29 +6796,6 @@ fn registerAlias(reg: Register, size_bytes: u32) Register { } } -/// Shifts register right without sign-extension. -fn shiftRegisterRightUnsigned(self: *Self, reg: Register, shift: u8) !void { - if (shift == 0) return; - if (shift == 1) { - _ = try self.addInst(.{ - .tag = .shr, - .ops = (Mir.Ops{ - .reg1 = reg, - }).encode(), - .data = undefined, - }); - } else { - _ = try self.addInst(.{ - .tag = .shr, - .ops = (Mir.Ops{ - .reg1 = reg, - .flags = 0b10, - }).encode(), - .data = .{ .imm = shift }, - }); - } -} - /// Truncates the value in the register in place. /// Clobbers any remaining bits. fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { @@ -6829,22 +6804,8 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { switch (int_info.signedness) { .signed => { const shift = @intCast(u6, max_reg_bit_width - int_info.bits); - _ = try self.addInst(.{ - .tag = .sal, - .ops = (Mir.Ops{ - .reg1 = reg.to64(), - .flags = 0b10, - }).encode(), - .data = .{ .imm = shift }, - }); - _ = try self.addInst(.{ - .tag = .sar, - .ops = (Mir.Ops{ - .reg1 = reg.to64(), - .flags = 0b10, - }).encode(), - .data = .{ .imm = shift }, - }); + try self.genShiftBinOpMir(Type.isize, reg, .{ .immediate = shift }, .left); + try self.genShiftBinOpMir(Type.isize, reg, .{ .immediate = shift }, .right); }, .unsigned => { const shift = @intCast(u6, max_reg_bit_width - int_info.bits); From f131e41db9f8b1b1bbd678b52ef377821d83bddc Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 10 May 2022 19:34:20 +0200 Subject: [PATCH 1439/2031] x64: implement shl_exact and shr_exact --- src/arch/x86_64/CodeGen.zig | 54 +++++++++++++++++++------------------ test/behavior/math.zig | 4 --- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 72c65e84de..2ed61006f2 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1708,7 +1708,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { // TODO reuse the operand const result = try self.copyToRegisterWithInstTracking(inst, optional_ty, operand); const shift = @intCast(u8, offset * @sizeOf(usize)); - try self.genShiftBinOpMir(optional_ty, result.register, .{ .immediate = @intCast(u8, shift) }, .right); + try self.genShiftBinOpMir(.shr, optional_ty, result.register, .{ .immediate = @intCast(u8, shift) }); break :result result; }, else => return self.fail("TODO implement optional_payload when operand is {}", .{operand}), @@ -1795,7 +1795,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { // TODO reuse operand const shift = @intCast(u6, err_abi_size * @sizeOf(usize)); const result = try self.copyToRegisterWithInstTracking(inst, err_union_ty, operand); - try self.genShiftBinOpMir(Type.usize, result.register, .{ .immediate = shift }, .right); + try self.genShiftBinOpMir(.shr, Type.usize, result.register, .{ .immediate = shift }); break :result MCValue{ .register = registerAlias(result.register, @intCast(u32, payload_ty.abiSize(self.target.*))), }; @@ -2307,7 +2307,7 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { else 0; const result = try self.copyToRegisterWithInstTracking(inst, union_ty, operand); - try self.genShiftBinOpMir(Type.usize, result.register, .{ .immediate = shift }, .right); + try self.genShiftBinOpMir(.shr, Type.usize, result.register, .{ .immediate = shift }); break :blk MCValue{ .register = registerAlias(result.register, @intCast(u32, layout.tag_size)), }; @@ -2906,7 +2906,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { // Shift by struct_field_offset. const shift = @intCast(u8, struct_field_offset * @sizeOf(usize)); - try self.genShiftBinOpMir(Type.usize, dst_mcv.register, .{ .immediate = shift }, .right); + try self.genShiftBinOpMir(.shr, Type.usize, dst_mcv.register, .{ .immediate = shift }); // Mask with reg.size() - struct_field_size const max_reg_bit_width = Register.rax.size(); @@ -2983,26 +2983,15 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { } /// Clobbers .rcx for non-immediate shift value. -fn genShiftBinOpMir(self: *Self, ty: Type, reg: Register, shift: MCValue, direction: enum { left, right }) !void { +fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shift: MCValue) !void { assert(reg.to64() != .rcx); + switch (tag) { + .sal, .sar, .shl, .shr => {}, + else => unreachable, + } + const abi_size = @intCast(u32, ty.abiSize(self.target.*)); - const signedness: std.builtin.Signedness = blk: { - if (ty.zigTypeTag() != .Int) break :blk .unsigned; - break :blk ty.intInfo(self.target.*).signedness; - }; - - const tag: Mir.Inst.Tag = switch (signedness) { - .signed => switch (direction) { - .left => Mir.Inst.Tag.sal, - .right => Mir.Inst.Tag.sar, - }, - .unsigned => switch (direction) { - .left => Mir.Inst.Tag.shl, - .right => Mir.Inst.Tag.shr, - }, - }; - blk: { switch (shift) { .immediate => |imm| switch (imm) { @@ -3100,9 +3089,22 @@ fn genShiftBinOp( break :blk MCValue{ .register = try self.copyToTmpRegister(lhs_ty, lhs) }; }; + const signedness = lhs_ty.intInfo(self.target.*).signedness; switch (tag) { - .shl => try self.genShiftBinOpMir(lhs_ty, dst.register, rhs, .left), - .shr => try self.genShiftBinOpMir(lhs_ty, dst.register, rhs, .right), + .shl => try self.genShiftBinOpMir(switch (signedness) { + .signed => .sal, + .unsigned => .shl, + }, lhs_ty, dst.register, rhs), + + .shl_exact => try self.genShiftBinOpMir(.shl, lhs_ty, dst.register, rhs), + + .shr, + .shr_exact, + => try self.genShiftBinOpMir(switch (signedness) { + .signed => .sar, + .unsigned => .shr, + }, lhs_ty, dst.register, rhs), + else => unreachable, } @@ -5427,7 +5429,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl }); if (nearest_power_of_two > 1) { - try self.genShiftBinOpMir(ty, tmp_reg, .{ .immediate = nearest_power_of_two * 8 }, .right); + try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{ .immediate = nearest_power_of_two * 8 }); } remainder -= nearest_power_of_two; @@ -6804,8 +6806,8 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { switch (int_info.signedness) { .signed => { const shift = @intCast(u6, max_reg_bit_width - int_info.bits); - try self.genShiftBinOpMir(Type.isize, reg, .{ .immediate = shift }, .left); - try self.genShiftBinOpMir(Type.isize, reg, .{ .immediate = shift }, .right); + try self.genShiftBinOpMir(.sal, Type.isize, reg, .{ .immediate = shift }); + try self.genShiftBinOpMir(.sar, Type.isize, reg, .{ .immediate = shift }); }, .unsigned => { const shift = @intCast(u6, max_reg_bit_width - int_info.bits); diff --git a/test/behavior/math.zig b/test/behavior/math.zig index dd609c9b08..53b11d1d9f 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1065,8 +1065,6 @@ fn testShlTrunc(x: u16) !void { } test "exact shift left" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - try testShlExact(0b00110101); comptime try testShlExact(0b00110101); } @@ -1076,8 +1074,6 @@ fn testShlExact(x: u8) !void { } test "exact shift right" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - try testShrExact(0b10110100); comptime try testShrExact(0b10110100); } From 2a738599a0180b6f12439d33722a52182b11c21e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 10 May 2022 20:45:57 +0200 Subject: [PATCH 1440/2031] x64: implement missing bits in add_with_overflow and sub_with_overflow --- src/arch/x86_64/CodeGen.zig | 173 +++++++++++++++++++++--------------- test/behavior/math.zig | 1 - 2 files changed, 101 insertions(+), 73 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 2ed61006f2..15610f1bd2 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1311,18 +1311,15 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const result = if (self.liveness.isUnused(inst)) .dead else result: { const ty = self.air.typeOf(bin_op.lhs); - + const abi_size = ty.abiSize(self.target.*); switch (ty.zigTypeTag()) { .Vector => return self.fail("TODO implement add_with_overflow for Vector type", .{}), .Int => { - const int_info = ty.intInfo(self.target.*); - - if (int_info.bits > 64) { + if (abi_size > 8) { return self.fail("TODO implement add_with_overflow for Ints larger than 64bits", .{}); } try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); @@ -1333,11 +1330,30 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { else => unreachable, }; const partial = try self.genBinOp(base_tag, null, lhs, rhs, ty, ty); - const result: MCValue = switch (int_info.signedness) { - .signed => .{ .register_overflow_signed = partial.register }, - .unsigned => .{ .register_overflow_unsigned = partial.register }, - }; - break :result result; + + const int_info = ty.intInfo(self.target.*); + + if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) { + self.compare_flags_inst = inst; + + const result: MCValue = switch (int_info.signedness) { + .signed => .{ .register_overflow_signed = partial.register }, + .unsigned => .{ .register_overflow_unsigned = partial.register }, + }; + break :result result; + } + + self.compare_flags_inst = null; + + const tuple_ty = self.air.typeOfIndex(inst); + const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); + const tuple_align = tuple_ty.abiAlignment(self.target.*); + const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)); + const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align)); + + try self.genSetStackTruncatedOverflowCompare(ty, stack_offset, overflow_bit_offset, partial.register); + + break :result MCValue{ .stack_offset = stack_offset }; }, else => unreachable, } @@ -1346,6 +1362,75 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn genSetStackTruncatedOverflowCompare( + self: *Self, + ty: Type, + stack_offset: i32, + overflow_bit_offset: i32, + reg: Register, +) !void { + const reg_lock = self.register_manager.lockReg(reg); + defer if (reg_lock) |lock| self.register_manager.unlockReg(lock); + + const int_info = ty.intInfo(self.target.*); + const extended_ty = switch (int_info.signedness) { + .signed => Type.isize, + .unsigned => ty, + }; + + const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }); + const temp_regs_locks = self.register_manager.lockRegsAssumeUnused(3, temp_regs); + defer for (temp_regs_locks) |rreg| { + self.register_manager.unlockReg(rreg); + }; + + const overflow_reg = temp_regs[0]; + const flags: u2 = switch (int_info.signedness) { + .signed => 0b00, + .unsigned => 0b10, + }; + _ = try self.addInst(.{ + .tag = .cond_set_byte_overflow, + .ops = (Mir.Ops{ + .reg1 = overflow_reg.to8(), + .flags = flags, + }).encode(), + .data = undefined, + }); + + const scratch_reg = temp_regs[1]; + try self.genSetReg(extended_ty, scratch_reg, .{ .register = reg }); + try self.truncateRegister(ty, scratch_reg); + try self.genBinOpMir( + .cmp, + extended_ty, + .{ .register = reg }, + .{ .register = scratch_reg }, + ); + + const eq_reg = temp_regs[2]; + _ = try self.addInst(.{ + .tag = .cond_set_byte_eq_ne, + .ops = (Mir.Ops{ + .reg1 = eq_reg.to8(), + .flags = 0b00, + }).encode(), + .data = undefined, + }); + + try self.genBinOpMir( + .@"or", + Type.u8, + .{ .register = overflow_reg }, + .{ .register = eq_reg }, + ); + + try self.genSetStack(ty, stack_offset, .{ .register = scratch_reg }, .{}); + try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ + .register = overflow_reg.to8(), + }, .{}); +} + fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; @@ -1355,17 +1440,18 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { } const ty = self.air.typeOf(bin_op.lhs); + const abi_size = ty.abiSize(self.target.*); const result: MCValue = result: { switch (ty.zigTypeTag()) { .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}), .Int => { - const int_info = ty.intInfo(self.target.*); - - if (int_info.bits > 64) { + if (abi_size > 8) { return self.fail("TODO implement mul_with_overflow for Ints larger than 64bits", .{}); } - if (math.isPowerOfTwo(int_info.bits)) { + const int_info = ty.intInfo(self.target.*); + + if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) { try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; @@ -1428,71 +1514,14 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { }, } }; - const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); - defer self.register_manager.unlockReg(dst_reg_lock); const tuple_ty = self.air.typeOfIndex(inst); const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); const tuple_align = tuple_ty.abiAlignment(self.target.*); const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)); - const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align)); - const extended_ty = switch (int_info.signedness) { - .signed => Type.isize, - .unsigned => ty, - }; - const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }); - const temp_regs_locks = self.register_manager.lockRegsAssumeUnused(3, temp_regs); - defer for (temp_regs_locks) |reg| { - self.register_manager.unlockReg(reg); - }; - - const overflow_reg = temp_regs[0]; - const flags: u2 = switch (int_info.signedness) { - .signed => 0b00, - .unsigned => 0b10, - }; - _ = try self.addInst(.{ - .tag = .cond_set_byte_overflow, - .ops = (Mir.Ops{ - .reg1 = overflow_reg.to8(), - .flags = flags, - }).encode(), - .data = undefined, - }); - - const scratch_reg = temp_regs[1]; - try self.genSetReg(extended_ty, scratch_reg, .{ .register = dst_reg }); - try self.truncateRegister(ty, scratch_reg); - try self.genBinOpMir( - .cmp, - extended_ty, - .{ .register = dst_reg }, - .{ .register = scratch_reg }, - ); - - const eq_reg = temp_regs[2]; - _ = try self.addInst(.{ - .tag = .cond_set_byte_eq_ne, - .ops = (Mir.Ops{ - .reg1 = eq_reg.to8(), - .flags = 0b00, - }).encode(), - .data = undefined, - }); - - try self.genBinOpMir( - .@"or", - Type.u8, - .{ .register = overflow_reg }, - .{ .register = eq_reg }, - ); - - try self.genSetStack(ty, stack_offset, .{ .register = scratch_reg }, .{}); - try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ - .register = overflow_reg.to8(), - }, .{}); + try self.genSetStackTruncatedOverflowCompare(ty, stack_offset, overflow_bit_offset, dst_reg); break :result MCValue{ .stack_offset = stack_offset }; }, diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 53b11d1d9f..201fccb8ae 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -640,7 +640,6 @@ test "@addWithOverflow" { test "small int addition" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From d31875f7abcad00dc95211e7395d80baa879cb93 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 10 May 2022 20:53:44 +0200 Subject: [PATCH 1441/2031] x64: implement shl_with_overflow for powers of two --- src/arch/x86_64/CodeGen.zig | 104 +++++++++++++++++++++++++++++++++++- test/behavior/math.zig | 1 - 2 files changed, 102 insertions(+), 3 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 15610f1bd2..8353f9b85e 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1533,8 +1533,108 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { } fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - _ = inst; - return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch}); + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + } + + const lhs_ty = self.air.typeOf(bin_op.lhs); + const abi_size = lhs_ty.abiSize(self.target.*); + const rhs_ty = self.air.typeOf(bin_op.rhs); + + const result: MCValue = result: { + switch (lhs_ty.zigTypeTag()) { + .Vector => return self.fail("TODO implement shl_with_overflow for Vector type", .{}), + .Int => { + if (abi_size > 8) { + return self.fail("TODO implement shl_with_overflow for Ints larger than 64bits", .{}); + } + + const int_info = lhs_ty.intInfo(self.target.*); + + if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) { + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = inst; + + try self.spillRegisters(1, .{.rcx}); + + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + const partial = try self.genShiftBinOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty); + break :result switch (int_info.signedness) { + .signed => MCValue{ .register_overflow_signed = partial.register }, + .unsigned => MCValue{ .register_overflow_unsigned = partial.register }, + }; + } + + return self.fail("TODO shl_with_overflow non-power-of-two", .{}); + + // try self.spillCompareFlagsIfOccupied(); + // self.compare_flags_inst = null; + + // const dst_reg: Register = dst_reg: { + // switch (int_info.signedness) { + // .signed => { + // const lhs = try self.resolveInst(bin_op.lhs); + // const rhs = try self.resolveInst(bin_op.rhs); + + // const rhs_lock: ?RegisterLock = switch (rhs) { + // .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + // else => null, + // }; + // defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); + + // const dst_reg: Register = blk: { + // if (lhs.isRegister()) break :blk lhs.register; + // break :blk try self.copyToTmpRegister(ty, lhs); + // }; + // const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); + // defer self.register_manager.unlockReg(dst_reg_lock); + + // const rhs_mcv: MCValue = blk: { + // if (rhs.isRegister() or rhs.isMemory()) break :blk rhs; + // break :blk MCValue{ .register = try self.copyToTmpRegister(ty, rhs) }; + // }; + // const rhs_mcv_lock: ?RegisterLock = switch (rhs_mcv) { + // .register => |reg| self.register_manager.lockReg(reg), + // else => null, + // }; + // defer if (rhs_mcv_lock) |lock| self.register_manager.unlockReg(lock); + + // try self.genIntMulComplexOpMir(Type.isize, .{ .register = dst_reg }, rhs_mcv); + + // break :dst_reg dst_reg; + // }, + // .unsigned => { + // try self.spillRegisters(2, .{ .rax, .rdx }); + + // const lhs = try self.resolveInst(bin_op.lhs); + // const rhs = try self.resolveInst(bin_op.rhs); + + // const dst_mcv = try self.genMulDivBinOp(.mul, null, ty, lhs, rhs); + // break :dst_reg dst_mcv.register; + // }, + // } + // }; + + // const tuple_ty = self.air.typeOfIndex(inst); + // const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); + // const tuple_align = tuple_ty.abiAlignment(self.target.*); + // const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)); + // const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align)); + + // try self.genSetStackTruncatedOverflowCompare(ty, stack_offset, overflow_bit_offset, dst_reg); + + // break :result MCValue{ .stack_offset = stack_offset }; + }, + else => unreachable, + } + }; + + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } /// Generates signed or unsigned integer multiplication/division. diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 201fccb8ae..91ce4b34cb 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -905,7 +905,6 @@ test "@subWithOverflow" { test "@shlWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO var result: u16 = undefined; try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); From 1d3b714125e98e4c135dbb9d2718e58984ab92eb Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 10 May 2022 21:19:05 +0200 Subject: [PATCH 1442/2031] x64: implement shl with overflow for non-pow-2 --- src/arch/x86_64/CodeGen.zig | 132 +++++------------------------------- test/behavior/math.zig | 51 ++++++++++---- 2 files changed, 56 insertions(+), 127 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 8353f9b85e..087f7e3d62 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -621,10 +621,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .trunc_float, => try self.airUnaryMath(inst), - .add_with_overflow => try self.airAddSubWithOverflow(inst), - .sub_with_overflow => try self.airAddSubWithOverflow(inst), + .add_with_overflow => try self.airAddSubShlWithOverflow(inst), + .sub_with_overflow => try self.airAddSubShlWithOverflow(inst), .mul_with_overflow => try self.airMulWithOverflow(inst), - .shl_with_overflow => try self.airShlWithOverflow(inst), + .shl_with_overflow => try self.airAddSubShlWithOverflow(inst), .div_float, .div_trunc, .div_floor, .div_exact => try self.airMulDivBinOp(inst), @@ -1305,7 +1305,7 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { +fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const tag = self.air.instructions.items(.tag)[inst]; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; @@ -1313,23 +1313,30 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const ty = self.air.typeOf(bin_op.lhs); const abi_size = ty.abiSize(self.target.*); switch (ty.zigTypeTag()) { - .Vector => return self.fail("TODO implement add_with_overflow for Vector type", .{}), + .Vector => return self.fail("TODO implement add/sub/shl with overflow for Vector type", .{}), .Int => { if (abi_size > 8) { - return self.fail("TODO implement add_with_overflow for Ints larger than 64bits", .{}); + return self.fail("TODO implement add/sub/shl with overflow for Ints larger than 64bits", .{}); } try self.spillCompareFlagsIfOccupied(); + if (tag == .shl_with_overflow) { + try self.spillRegisters(1, .{.rcx}); + } + const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const base_tag: Air.Inst.Tag = switch (tag) { - .add_with_overflow => .add, - .sub_with_overflow => .sub, + const partial: MCValue = switch (tag) { + .add_with_overflow => try self.genBinOp(.add, null, lhs, rhs, ty, ty), + .sub_with_overflow => try self.genBinOp(.sub, null, lhs, rhs, ty, ty), + .shl_with_overflow => blk: { + const shift_ty = self.air.typeOf(bin_op.rhs); + break :blk try self.genShiftBinOp(.shl, null, lhs, rhs, ty, shift_ty); + }, else => unreachable, }; - const partial = try self.genBinOp(base_tag, null, lhs, rhs, ty, ty); const int_info = ty.intInfo(self.target.*); @@ -1532,111 +1539,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; - - if (self.liveness.isUnused(inst)) { - return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); - } - - const lhs_ty = self.air.typeOf(bin_op.lhs); - const abi_size = lhs_ty.abiSize(self.target.*); - const rhs_ty = self.air.typeOf(bin_op.rhs); - - const result: MCValue = result: { - switch (lhs_ty.zigTypeTag()) { - .Vector => return self.fail("TODO implement shl_with_overflow for Vector type", .{}), - .Int => { - if (abi_size > 8) { - return self.fail("TODO implement shl_with_overflow for Ints larger than 64bits", .{}); - } - - const int_info = lhs_ty.intInfo(self.target.*); - - if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) { - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; - - try self.spillRegisters(1, .{.rcx}); - - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - - const partial = try self.genShiftBinOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty); - break :result switch (int_info.signedness) { - .signed => MCValue{ .register_overflow_signed = partial.register }, - .unsigned => MCValue{ .register_overflow_unsigned = partial.register }, - }; - } - - return self.fail("TODO shl_with_overflow non-power-of-two", .{}); - - // try self.spillCompareFlagsIfOccupied(); - // self.compare_flags_inst = null; - - // const dst_reg: Register = dst_reg: { - // switch (int_info.signedness) { - // .signed => { - // const lhs = try self.resolveInst(bin_op.lhs); - // const rhs = try self.resolveInst(bin_op.rhs); - - // const rhs_lock: ?RegisterLock = switch (rhs) { - // .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - // else => null, - // }; - // defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); - - // const dst_reg: Register = blk: { - // if (lhs.isRegister()) break :blk lhs.register; - // break :blk try self.copyToTmpRegister(ty, lhs); - // }; - // const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); - // defer self.register_manager.unlockReg(dst_reg_lock); - - // const rhs_mcv: MCValue = blk: { - // if (rhs.isRegister() or rhs.isMemory()) break :blk rhs; - // break :blk MCValue{ .register = try self.copyToTmpRegister(ty, rhs) }; - // }; - // const rhs_mcv_lock: ?RegisterLock = switch (rhs_mcv) { - // .register => |reg| self.register_manager.lockReg(reg), - // else => null, - // }; - // defer if (rhs_mcv_lock) |lock| self.register_manager.unlockReg(lock); - - // try self.genIntMulComplexOpMir(Type.isize, .{ .register = dst_reg }, rhs_mcv); - - // break :dst_reg dst_reg; - // }, - // .unsigned => { - // try self.spillRegisters(2, .{ .rax, .rdx }); - - // const lhs = try self.resolveInst(bin_op.lhs); - // const rhs = try self.resolveInst(bin_op.rhs); - - // const dst_mcv = try self.genMulDivBinOp(.mul, null, ty, lhs, rhs); - // break :dst_reg dst_mcv.register; - // }, - // } - // }; - - // const tuple_ty = self.air.typeOfIndex(inst); - // const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); - // const tuple_align = tuple_ty.abiAlignment(self.target.*); - // const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)); - // const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align)); - - // try self.genSetStackTruncatedOverflowCompare(ty, stack_offset, overflow_bit_offset, dst_reg); - - // break :result MCValue{ .stack_offset = stack_offset }; - }, - else => unreachable, - } - }; - - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); -} - /// Generates signed or unsigned integer multiplication/division. /// Clobbers .rax and .rdx registers. /// Quotient is saved in .rax and remainder in .rdx. diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 91ce4b34cb..8dc21c0598 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -905,20 +905,47 @@ test "@subWithOverflow" { test "@shlWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - var result: u16 = undefined; - try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); - try expect(result == 0b0111111111111000); - try expect(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); - try expect(result == 0b1011111111111100); + { + var result: u4 = undefined; + var a: u4 = 2; + var b: u2 = 1; + try expect(!@shlWithOverflow(u4, a, b, &result)); + try expect(result == 4); - var a: u16 = 0b0000_0000_0000_0011; - var b: u4 = 15; - try expect(@shlWithOverflow(u16, a, b, &result)); - try expect(result == 0b1000_0000_0000_0000); - b = 14; - try expect(!@shlWithOverflow(u16, a, b, &result)); - try expect(result == 0b1100_0000_0000_0000); + b = 3; + try expect(@shlWithOverflow(u4, a, b, &result)); + try expect(result == 0); + } + + { + var result: i9 = undefined; + var a: i9 = 127; + var b: u4 = 1; + try expect(!@shlWithOverflow(i9, a, b, &result)); + try expect(result == 254); + + b = 2; + try expect(@shlWithOverflow(i9, a, b, &result)); + try expect(result == -4); + } + + { + var result: u16 = undefined; + try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); + try expect(result == 0b0111111111111000); + try expect(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); + try expect(result == 0b1011111111111100); + + var a: u16 = 0b0000_0000_0000_0011; + var b: u4 = 15; + try expect(@shlWithOverflow(u16, a, b, &result)); + try expect(result == 0b1000_0000_0000_0000); + b = 14; + try expect(!@shlWithOverflow(u16, a, b, &result)); + try expect(result == 0b1100_0000_0000_0000); + } } test "overflow arithmetic with u0 values" { From f6f98a621f70a10078fe3de688fe4f8c2e027a30 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 10 May 2022 21:21:09 +0200 Subject: [PATCH 1443/2031] x64: enable additional math test --- test/behavior/math.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 8dc21c0598..85b62e0275 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -9,7 +9,6 @@ const mem = std.mem; const math = std.math; test "assignment operators" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From 3c69810fe6660fa3115d345bb60b3f811ac7c8c0 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 10 May 2022 21:30:39 +0200 Subject: [PATCH 1444/2031] x64: fix binary not implementation --- src/arch/x86_64/CodeGen.zig | 3 ++- test/behavior/math.zig | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 087f7e3d62..be053f310c 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1154,7 +1154,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { }; defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); - try self.genBinOpMir(.xor, operand_ty, dst_mcv, .{ .immediate = 1 }); + const mask = ~@as(u64, 0); + try self.genBinOpMir(.xor, operand_ty, dst_mcv, .{ .immediate = mask }); break :result dst_mcv; }; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 85b62e0275..53a3f61d14 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -362,7 +362,6 @@ fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int test "binary not" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO try expect(comptime x: { break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101; From 67c4b16d6e27f1d81e2e2f837bd53d958b9baa33 Mon Sep 17 00:00:00 2001 From: Kirk Scheibelhut Date: Tue, 10 May 2022 12:15:31 -0700 Subject: [PATCH 1445/2031] docs: T.bit_count -> @typeInfo(T).Int.bits bit_count was removed in #6246 --- doc/langref.html.in | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index d81661ecd3..f5d3c0b7c1 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -9220,8 +9220,8 @@ test "@setRuntimeSafety" { any bits that disagree with the resultant sign bit are shifted out.

- The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits. - This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior. + The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(@typeInfo(T).Int.bits){#endsyntax#} bits. + This is because {#syntax#}shift_amt >= @typeInfo(T).Int.bits{#endsyntax#} is undefined behavior.

{#see_also|@shrExact|@shlWithOverflow#} {#header_close#} @@ -9234,8 +9234,8 @@ test "@setRuntimeSafety" { If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.

- The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits. - This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior. + The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(@typeInfo(T).Int.bits){#endsyntax#} bits. + This is because {#syntax#}shift_amt >= @typeInfo(T).Int.bits{#endsyntax#} is undefined behavior.

{#see_also|@shlExact|@shrExact#} {#header_close#} @@ -9247,8 +9247,8 @@ test "@setRuntimeSafety" { that the shift will not shift any 1 bits out.

- The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits. - This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior. + The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(@typeInfo(T).Int.bits){#endsyntax#} bits. + This is because {#syntax#}shift_amt >= @typeInfo(T).Int.bits{#endsyntax#} is undefined behavior.

{#see_also|@shlExact|@shlWithOverflow#} {#header_close#} From 9654a54d4a2729200d38dbb6eec827cb03dc0f90 Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Fri, 24 Sep 2021 22:14:53 +0900 Subject: [PATCH 1446/2031] Add Visibility field to ExportOptions. Signed-off-by: Takeshi Yoneda --- lib/std/builtin.zig | 9 +++++++++ src/Sema.zig | 5 +++++ src/codegen/llvm.zig | 5 +++++ src/codegen/llvm/bindings.zig | 9 +++++++++ src/stage1/all_types.hpp | 7 +++++++ src/stage1/analyze.cpp | 12 +++++++----- src/stage1/analyze.hpp | 4 ++-- src/stage1/codegen.cpp | 20 ++++++++++++++++++++ src/stage1/ir.cpp | 33 +++++++++++++++++++++++++++++++-- 9 files changed, 95 insertions(+), 9 deletions(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 8c1e38ea09..3b0a24d3f5 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -68,6 +68,14 @@ pub const GlobalLinkage = enum { LinkOnce, }; +/// This data structure is used by the Zig language code generation and +/// therefore must be kept in sync with the compiler implementation. +pub const GlobalVisibility = enum { + default, + hidden, + protected, +}; + /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. pub const AtomicOrder = enum { @@ -655,6 +663,7 @@ pub const ExportOptions = struct { name: []const u8, linkage: GlobalLinkage = .Strong, section: ?[]const u8 = null, + visibility: GlobalVisibility = .default, }; /// This data structure is used by the Zig language code generation and diff --git a/src/Sema.zig b/src/Sema.zig index 2e7e5c9293..6de4e3704c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4348,6 +4348,7 @@ pub fn analyzeExport( .name = symbol_name, .linkage = borrowed_options.linkage, .section = section, + .visibility = borrowed_options.visibility, }, .src = src, .link = switch (mod.comp.bin_file.tag) { @@ -15007,6 +15008,9 @@ fn resolveExportOptions( const section = try sema.fieldVal(block, src, options, "section", src); const section_val = try sema.resolveConstValue(block, src, section); + const visibility = try sema.fieldVal(block, src, options, "visibility", src); + const visibility_val = try sema.resolveConstValue(block, src, visibility); + if (!section_val.isNull()) { return sema.fail(block, src, "TODO: implement exporting with linksection", .{}); } @@ -15015,6 +15019,7 @@ fn resolveExportOptions( .name = try name_val.toAllocatedBytes(name_ty, sema.arena, sema.mod), .linkage = linkage_val.toEnum(std.builtin.GlobalLinkage), .section = null, // TODO + .visibility = visibility_val.toEnum(std.builtin.GlobalVisibility), }; } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 57bcbe9338..5e70710046 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -808,6 +808,11 @@ pub const Object = struct { .Weak => llvm_global.setLinkage(.WeakODR), .LinkOnce => llvm_global.setLinkage(.LinkOnceODR), } + switch (exports[0].options.visibility) { + .default => llvm_global.setVisibility(.Default), + .hidden => llvm_global.setVisibility(.Hidden), + .protected => llvm_global.setVisibility(.Protected), + } if (decl.val.castTag(.variable)) |variable| { if (variable.data.is_threadlocal) { llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel); diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index c70a7d1553..f3987a8665 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -117,6 +117,9 @@ pub const Value = opaque { pub const setLinkage = LLVMSetLinkage; extern fn LLVMSetLinkage(Global: *const Value, Linkage: Linkage) void; + pub const setVisibility = LLVMSetVisibility; + extern fn LLVMSetVisibility(Global: *const Value, Linkage: Visibility) void; + pub const setUnnamedAddr = LLVMSetUnnamedAddr; extern fn LLVMSetUnnamedAddr(Global: *const Value, HasUnnamedAddr: Bool) void; @@ -1324,6 +1327,12 @@ pub const Linkage = enum(c_uint) { LinkerPrivateWeak, }; +pub const Visibility = enum(c_uint) { + Default, + Hidden, + Protected, +}; + pub const ThreadLocalMode = enum(c_uint) { NotThreadLocal, GeneralDynamicTLSModel, diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp index ba5df49c59..f2395efdcb 100644 --- a/src/stage1/all_types.hpp +++ b/src/stage1/all_types.hpp @@ -571,6 +571,12 @@ enum GlobalLinkageId { GlobalLinkageIdLinkOnce, }; +enum GlobalVisibilityId { + GlobalVisibilityIdDefault, + GlobalVisibilityIdHidden, + GlobalVisibilityIdProtected, +}; + enum TldId { TldIdVar, TldIdFn, @@ -1654,6 +1660,7 @@ enum FnAnalState { struct GlobalExport { Buf name; GlobalLinkageId linkage; + GlobalVisibilityId visibility; }; struct ZigFn { diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp index 8faf3c0fc9..0a5468cbc3 100644 --- a/src/stage1/analyze.cpp +++ b/src/stage1/analyze.cpp @@ -3717,14 +3717,15 @@ ZigType *get_test_fn_type(CodeGen *g) { return g->test_fn_type; } -void add_var_export(CodeGen *g, ZigVar *var, const char *symbol_name, GlobalLinkageId linkage) { +void add_var_export(CodeGen *g, ZigVar *var, const char *symbol_name, GlobalLinkageId linkage, GlobalVisibilityId visibility) { GlobalExport *global_export = var->export_list.add_one(); memset(global_export, 0, sizeof(GlobalExport)); buf_init_from_str(&global_export->name, symbol_name); global_export->linkage = linkage; + global_export->visibility = visibility; } -void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, CallingConvention cc) { +void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, GlobalVisibilityId visibility, CallingConvention cc) { CallingConvention winapi_cc = g->zig_target->arch == ZigLLVM_x86 ? CallingConventionStdcall : CallingConventionC; @@ -3749,6 +3750,7 @@ void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, G memset(fn_export, 0, sizeof(GlobalExport)); buf_init_from_str(&fn_export->name, symbol_name); fn_export->linkage = linkage; + fn_export->visibility = visibility; } static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { @@ -3852,13 +3854,13 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { case CallingConventionWin64: case CallingConventionPtxKernel: add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name), - GlobalLinkageIdStrong, fn_cc); + GlobalLinkageIdStrong, GlobalVisibilityIdDefault, fn_cc); break; case CallingConventionUnspecified: // An exported function without a specific calling // convention defaults to C add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name), - GlobalLinkageIdStrong, CallingConventionC); + GlobalLinkageIdStrong, GlobalVisibilityIdDefault, CallingConventionC); break; } } @@ -4321,7 +4323,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) { if (is_export) { validate_export_var_type(g, type, source_node); - add_var_export(g, tld_var->var, tld_var->var->name, GlobalLinkageIdStrong); + add_var_export(g, tld_var->var, tld_var->var->name, GlobalLinkageIdStrong, GlobalVisibilityIdDefault); } if (is_extern) { diff --git a/src/stage1/analyze.hpp b/src/stage1/analyze.hpp index 64e0e199f8..d4985286d5 100644 --- a/src/stage1/analyze.hpp +++ b/src/stage1/analyze.hpp @@ -221,8 +221,8 @@ ZigType *get_align_amt_type(CodeGen *g); ZigPackage *new_anonymous_package(void); Buf *const_value_to_buffer(ZigValue *const_val); -void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, CallingConvention cc); -void add_var_export(CodeGen *g, ZigVar *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage); +void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, GlobalVisibilityId visibility, CallingConvention cc); +void add_var_export(CodeGen *g, ZigVar *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, GlobalVisibilityId visibility); ZigValue *get_builtin_value(CodeGen *codegen, const char *name); diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index d101030c33..dece8e7348 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -242,6 +242,18 @@ static LLVMLinkage to_llvm_linkage(GlobalLinkageId id, bool is_extern) { zig_unreachable(); } +static LLVMVisibility to_llvm_visibility(GlobalVisibilityId id) { + switch (id) { + case GlobalVisibilityIdDefault: + return LLVMDefaultVisibility; + case GlobalVisibilityIdHidden: + return LLVMHiddenVisibility; + case GlobalVisibilityIdProtected: + return LLVMProtectedVisibility; + } + zig_unreachable(); +} + struct CalcLLVMFieldIndex { uint32_t offset; uint32_t field_index; @@ -400,6 +412,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { const char *unmangled_name = buf_ptr(&fn->symbol_name); const char *symbol_name; GlobalLinkageId linkage; + GlobalVisibilityId visibility = GlobalVisibilityIdDefault; if (fn->body_node == nullptr) { symbol_name = unmangled_name; linkage = GlobalLinkageIdStrong; @@ -410,6 +423,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { GlobalExport *fn_export = &fn->export_list.items[0]; symbol_name = buf_ptr(&fn_export->name); linkage = fn_export->linkage; + visibility = fn_export->visibility; } CallingConvention cc = fn->type_entry->data.fn.fn_type_id.cc; @@ -532,6 +546,8 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { LLVMSetUnnamedAddr(llvm_fn, true); } + LLVMSetVisibility(llvm_fn, to_llvm_visibility(visibility)); + ZigType *return_type = fn_type->data.fn.fn_type_id.return_type; if (return_type->id == ZigTypeIdUnreachable) { addLLVMFnAttr(llvm_fn, "noreturn"); @@ -8951,6 +8967,7 @@ static void do_code_gen(CodeGen *g) { assert(var->decl_node); GlobalLinkageId linkage; + GlobalVisibilityId visibility = GlobalVisibilityIdDefault; const char *unmangled_name = var->name; const char *symbol_name; if (var->export_list.length == 0) { @@ -8965,6 +8982,7 @@ static void do_code_gen(CodeGen *g) { GlobalExport *global_export = &var->export_list.items[0]; symbol_name = buf_ptr(&global_export->name); linkage = global_export->linkage; + visibility = global_export->visibility; } LLVMValueRef global_value; @@ -9010,6 +9028,8 @@ static void do_code_gen(CodeGen *g) { set_global_tls(g, var, global_value); } + LLVMSetVisibility(global_value, to_llvm_visibility(visibility)); + var->value_ref = global_value; for (size_t export_i = 1; export_i < var->export_list.length; export_i += 1) { diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 62834e564d..02b9dbd48b 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -8635,6 +8635,25 @@ static bool ir_resolve_global_linkage(IrAnalyze *ira, Stage1AirInst *value, Glob return true; } +static bool ir_resolve_global_visibility(IrAnalyze *ira, Stage1AirInst *value, GlobalVisibilityId *out) { + if (type_is_invalid(value->value->type)) + return false; + + ZigType *global_visibility_type = get_builtin_type(ira->codegen, "GlobalVisibility"); + + Stage1AirInst *casted_value = ir_implicit_cast(ira, value, global_visibility_type); + if (type_is_invalid(casted_value->value->type)) + return false; + + ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + if (!const_val) + return false; + + *out = (GlobalVisibilityId)bigint_as_u32(&const_val->data.x_enum_tag); + return true; +} + + static bool ir_resolve_float_mode(IrAnalyze *ira, Stage1AirInst *value, FloatMode *out) { if (type_is_invalid(value->value->type)) return false; @@ -11661,6 +11680,12 @@ static Stage1AirInst *ir_analyze_instruction_export(IrAnalyze *ira, Stage1ZirIns if (type_is_invalid(section_inst->value->type)) return ira->codegen->invalid_inst_gen; + TypeStructField *visibility_field = find_struct_type_field(options_type, buf_create_from_str("visibility")); + src_assert(visibility_field != nullptr, instruction->base.source_node); + Stage1AirInst *visibility_inst = ir_analyze_struct_value_field_value(ira, instruction->base.scope, instruction->base.source_node, options, visibility_field); + if (type_is_invalid(visibility_inst->value->type)) + return ira->codegen->invalid_inst_gen; + // The `section` field is optional, we have to unwrap it first Stage1AirInst *non_null_check = ir_analyze_test_non_null(ira, instruction->base.scope, instruction->base.source_node, section_inst); bool is_non_null; @@ -11689,6 +11714,10 @@ static Stage1AirInst *ir_analyze_instruction_export(IrAnalyze *ira, Stage1ZirIns if (!ir_resolve_global_linkage(ira, linkage_inst, &global_linkage_id)) return ira->codegen->invalid_inst_gen; + GlobalVisibilityId global_visibility_id; + if (!ir_resolve_global_visibility(ira, visibility_inst, &global_visibility_id)) + return ira->codegen->invalid_inst_gen; + Buf *section_name = nullptr; if (section_str_inst != nullptr && !(section_name = ir_resolve_str(ira, section_str_inst))) return ira->codegen->invalid_inst_gen; @@ -11751,7 +11780,7 @@ static Stage1AirInst *ir_analyze_instruction_export(IrAnalyze *ira, Stage1ZirIns case CallingConventionSysV: case CallingConventionWin64: case CallingConventionPtxKernel: - add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc); + add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, global_visibility_id, cc); fn_entry->section_name = section_name; break; } @@ -11898,7 +11927,7 @@ static Stage1AirInst *ir_analyze_instruction_export(IrAnalyze *ira, Stage1ZirIns if (load_ptr->ptr->id == Stage1AirInstIdVarPtr) { Stage1AirInstVarPtr *var_ptr = reinterpret_cast(load_ptr->ptr); ZigVar *var = var_ptr->var; - add_var_export(ira->codegen, var, buf_ptr(symbol_name), global_linkage_id); + add_var_export(ira->codegen, var, buf_ptr(symbol_name), global_linkage_id, global_visibility_id); var->section_name = section_name; } } From c4c5020f0267758c7eb127689177cf1a70fb6d97 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 10 May 2022 15:44:40 -0700 Subject: [PATCH 1447/2031] fixups to the previous commit * Rename std.builtin.GlobalVisibility to std.builtin.SymbolVisibility * Add missing compile error. From the LLVM language reference: "A symbol with internal or private linkage must have default visibility." --- lib/std/builtin.zig | 4 ++-- src/Sema.zig | 30 ++++++++++++++++++++---------- src/stage1/all_types.hpp | 10 +++++----- src/stage1/analyze.cpp | 10 +++++----- src/stage1/analyze.hpp | 4 ++-- src/stage1/codegen.cpp | 12 ++++++------ src/stage1/ir.cpp | 8 ++++---- 7 files changed, 44 insertions(+), 34 deletions(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 3b0a24d3f5..bf9a2d5682 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -70,7 +70,7 @@ pub const GlobalLinkage = enum { /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. -pub const GlobalVisibility = enum { +pub const SymbolVisibility = enum { default, hidden, protected, @@ -663,7 +663,7 @@ pub const ExportOptions = struct { name: []const u8, linkage: GlobalLinkage = .Strong, section: ?[]const u8 = null, - visibility: GlobalVisibility = .default, + visibility: SymbolVisibility = .default, }; /// This data structure is used by the Zig language code generation and diff --git a/src/Sema.zig b/src/Sema.zig index 6de4e3704c..fbf25be288 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14999,27 +14999,37 @@ fn resolveExportOptions( const air_ref = sema.resolveInst(zir_ref); const options = try sema.coerce(block, export_options_ty, air_ref, src); - const name = try sema.fieldVal(block, src, options, "name", src); - const name_val = try sema.resolveConstValue(block, src, name); + const name_operand = try sema.fieldVal(block, src, options, "name", src); + const name_val = try sema.resolveConstValue(block, src, name_operand); + const name_ty = Type.initTag(.const_slice_u8); + const name = try name_val.toAllocatedBytes(name_ty, sema.arena, sema.mod); - const linkage = try sema.fieldVal(block, src, options, "linkage", src); - const linkage_val = try sema.resolveConstValue(block, src, linkage); + const linkage_operand = try sema.fieldVal(block, src, options, "linkage", src); + const linkage_val = try sema.resolveConstValue(block, src, linkage_operand); + const linkage = linkage_val.toEnum(std.builtin.GlobalLinkage); const section = try sema.fieldVal(block, src, options, "section", src); const section_val = try sema.resolveConstValue(block, src, section); - const visibility = try sema.fieldVal(block, src, options, "visibility", src); - const visibility_val = try sema.resolveConstValue(block, src, visibility); + const visibility_operand = try sema.fieldVal(block, src, options, "visibility", src); + const visibility_val = try sema.resolveConstValue(block, src, visibility_operand); + const visibility = visibility_val.toEnum(std.builtin.SymbolVisibility); + + if (visibility != .default and linkage == .Internal) { + return sema.fail(block, src, "symbol '{s}' exported with internal linkage has non-default visibility {s}", .{ + name, @tagName(visibility), + }); + } if (!section_val.isNull()) { return sema.fail(block, src, "TODO: implement exporting with linksection", .{}); } - const name_ty = Type.initTag(.const_slice_u8); + return std.builtin.ExportOptions{ - .name = try name_val.toAllocatedBytes(name_ty, sema.arena, sema.mod), - .linkage = linkage_val.toEnum(std.builtin.GlobalLinkage), + .name = name, + .linkage = linkage, .section = null, // TODO - .visibility = visibility_val.toEnum(std.builtin.GlobalVisibility), + .visibility = visibility, }; } diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp index f2395efdcb..5ee07fcaf8 100644 --- a/src/stage1/all_types.hpp +++ b/src/stage1/all_types.hpp @@ -571,10 +571,10 @@ enum GlobalLinkageId { GlobalLinkageIdLinkOnce, }; -enum GlobalVisibilityId { - GlobalVisibilityIdDefault, - GlobalVisibilityIdHidden, - GlobalVisibilityIdProtected, +enum SymbolVisibilityId { + SymbolVisibilityIdDefault, + SymbolVisibilityIdHidden, + SymbolVisibilityIdProtected, }; enum TldId { @@ -1660,7 +1660,7 @@ enum FnAnalState { struct GlobalExport { Buf name; GlobalLinkageId linkage; - GlobalVisibilityId visibility; + SymbolVisibilityId visibility; }; struct ZigFn { diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp index 0a5468cbc3..62b343b35c 100644 --- a/src/stage1/analyze.cpp +++ b/src/stage1/analyze.cpp @@ -3717,7 +3717,7 @@ ZigType *get_test_fn_type(CodeGen *g) { return g->test_fn_type; } -void add_var_export(CodeGen *g, ZigVar *var, const char *symbol_name, GlobalLinkageId linkage, GlobalVisibilityId visibility) { +void add_var_export(CodeGen *g, ZigVar *var, const char *symbol_name, GlobalLinkageId linkage, SymbolVisibilityId visibility) { GlobalExport *global_export = var->export_list.add_one(); memset(global_export, 0, sizeof(GlobalExport)); buf_init_from_str(&global_export->name, symbol_name); @@ -3725,7 +3725,7 @@ void add_var_export(CodeGen *g, ZigVar *var, const char *symbol_name, GlobalLink global_export->visibility = visibility; } -void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, GlobalVisibilityId visibility, CallingConvention cc) { +void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, SymbolVisibilityId visibility, CallingConvention cc) { CallingConvention winapi_cc = g->zig_target->arch == ZigLLVM_x86 ? CallingConventionStdcall : CallingConventionC; @@ -3854,13 +3854,13 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { case CallingConventionWin64: case CallingConventionPtxKernel: add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name), - GlobalLinkageIdStrong, GlobalVisibilityIdDefault, fn_cc); + GlobalLinkageIdStrong, SymbolVisibilityIdDefault, fn_cc); break; case CallingConventionUnspecified: // An exported function without a specific calling // convention defaults to C add_fn_export(g, fn_table_entry, buf_ptr(&fn_table_entry->symbol_name), - GlobalLinkageIdStrong, GlobalVisibilityIdDefault, CallingConventionC); + GlobalLinkageIdStrong, SymbolVisibilityIdDefault, CallingConventionC); break; } } @@ -4323,7 +4323,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) { if (is_export) { validate_export_var_type(g, type, source_node); - add_var_export(g, tld_var->var, tld_var->var->name, GlobalLinkageIdStrong, GlobalVisibilityIdDefault); + add_var_export(g, tld_var->var, tld_var->var->name, GlobalLinkageIdStrong, SymbolVisibilityIdDefault); } if (is_extern) { diff --git a/src/stage1/analyze.hpp b/src/stage1/analyze.hpp index d4985286d5..b2669eb637 100644 --- a/src/stage1/analyze.hpp +++ b/src/stage1/analyze.hpp @@ -221,8 +221,8 @@ ZigType *get_align_amt_type(CodeGen *g); ZigPackage *new_anonymous_package(void); Buf *const_value_to_buffer(ZigValue *const_val); -void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, GlobalVisibilityId visibility, CallingConvention cc); -void add_var_export(CodeGen *g, ZigVar *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, GlobalVisibilityId visibility); +void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, SymbolVisibilityId visibility, CallingConvention cc); +void add_var_export(CodeGen *g, ZigVar *fn_table_entry, const char *symbol_name, GlobalLinkageId linkage, SymbolVisibilityId visibility); ZigValue *get_builtin_value(CodeGen *codegen, const char *name); diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index dece8e7348..e057a8cc80 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -242,13 +242,13 @@ static LLVMLinkage to_llvm_linkage(GlobalLinkageId id, bool is_extern) { zig_unreachable(); } -static LLVMVisibility to_llvm_visibility(GlobalVisibilityId id) { +static LLVMVisibility to_llvm_visibility(SymbolVisibilityId id) { switch (id) { - case GlobalVisibilityIdDefault: + case SymbolVisibilityIdDefault: return LLVMDefaultVisibility; - case GlobalVisibilityIdHidden: + case SymbolVisibilityIdHidden: return LLVMHiddenVisibility; - case GlobalVisibilityIdProtected: + case SymbolVisibilityIdProtected: return LLVMProtectedVisibility; } zig_unreachable(); @@ -412,7 +412,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { const char *unmangled_name = buf_ptr(&fn->symbol_name); const char *symbol_name; GlobalLinkageId linkage; - GlobalVisibilityId visibility = GlobalVisibilityIdDefault; + SymbolVisibilityId visibility = SymbolVisibilityIdDefault; if (fn->body_node == nullptr) { symbol_name = unmangled_name; linkage = GlobalLinkageIdStrong; @@ -8967,7 +8967,7 @@ static void do_code_gen(CodeGen *g) { assert(var->decl_node); GlobalLinkageId linkage; - GlobalVisibilityId visibility = GlobalVisibilityIdDefault; + SymbolVisibilityId visibility = SymbolVisibilityIdDefault; const char *unmangled_name = var->name; const char *symbol_name; if (var->export_list.length == 0) { diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 02b9dbd48b..4449985574 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -8635,11 +8635,11 @@ static bool ir_resolve_global_linkage(IrAnalyze *ira, Stage1AirInst *value, Glob return true; } -static bool ir_resolve_global_visibility(IrAnalyze *ira, Stage1AirInst *value, GlobalVisibilityId *out) { +static bool ir_resolve_global_visibility(IrAnalyze *ira, Stage1AirInst *value, SymbolVisibilityId *out) { if (type_is_invalid(value->value->type)) return false; - ZigType *global_visibility_type = get_builtin_type(ira->codegen, "GlobalVisibility"); + ZigType *global_visibility_type = get_builtin_type(ira->codegen, "SymbolVisibility"); Stage1AirInst *casted_value = ir_implicit_cast(ira, value, global_visibility_type); if (type_is_invalid(casted_value->value->type)) @@ -8649,7 +8649,7 @@ static bool ir_resolve_global_visibility(IrAnalyze *ira, Stage1AirInst *value, G if (!const_val) return false; - *out = (GlobalVisibilityId)bigint_as_u32(&const_val->data.x_enum_tag); + *out = (SymbolVisibilityId)bigint_as_u32(&const_val->data.x_enum_tag); return true; } @@ -11714,7 +11714,7 @@ static Stage1AirInst *ir_analyze_instruction_export(IrAnalyze *ira, Stage1ZirIns if (!ir_resolve_global_linkage(ira, linkage_inst, &global_linkage_id)) return ira->codegen->invalid_inst_gen; - GlobalVisibilityId global_visibility_id; + SymbolVisibilityId global_visibility_id; if (!ir_resolve_global_visibility(ira, visibility_inst, &global_visibility_id)) return ira->codegen->invalid_inst_gen; From 3997828a6176203b25b541c6e450f5e4bda82ce4 Mon Sep 17 00:00:00 2001 From: Alexander Slesarev Date: Sun, 31 Oct 2021 22:36:30 -0600 Subject: [PATCH 1448/2031] Added _LIBCPP_HAS_NO_THREADS for single_threaded binaries linked with libcxx. Fixed single-threaded mode for Windows. --- lib/std/build/RunStep.zig | 72 ++++++++++++++++++++++++++++ src/Compilation.zig | 13 +++++ src/libcxx.zig | 24 +++++++--- test/standalone/c_compiler/build.zig | 42 ++++++++++------ test/standalone/c_compiler/test.c | 23 +++++---- test/standalone/c_compiler/test.cpp | 60 +++++++++++++++++------ 6 files changed, 189 insertions(+), 45 deletions(-) diff --git a/lib/std/build/RunStep.zig b/lib/std/build/RunStep.zig index e00fe3deb6..8cfbc3e89a 100644 --- a/lib/std/build/RunStep.zig +++ b/lib/std/build/RunStep.zig @@ -1,6 +1,7 @@ const std = @import("../std.zig"); const builtin = @import("builtin"); const build = std.build; +const CrossTarget = std.zig.CrossTarget; const Step = build.Step; const Builder = build.Builder; const LibExeObjStep = build.LibExeObjStep; @@ -142,6 +143,23 @@ pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) }; } +/// Returns true if the step could be run, otherwise false +pub fn isRunnable( + self: *RunStep, +) bool { + for (self.argv.items) |arg| { + switch (arg) { + .artifact => |artifact| { + _ = self.getExternalExecutor(artifact) catch { + return false; + }; + }, + else => {}, + } + } + return true; +} + pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void { self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) }; } @@ -154,6 +172,57 @@ fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo { }; } +fn getExternalExecutor(self: *RunStep, artifact: *LibExeObjStep) !?[]const u8 { + const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc; + const executor = self.builder.host.getExternalExecutor(artifact.target_info, .{ + .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null, + .link_libc = artifact.is_linking_libc, + }); + switch (executor) { + .bad_dl, .bad_os_or_cpu => { + return error.NoExecutable; + }, + .native => { + return null; + }, + .rosetta => { + if (self.builder.enable_rosetta) { + return null; + } else { + return error.RosettaNotEnabled; + } + }, + .qemu => |bin_name| { + if (self.builder.enable_qemu) { + return bin_name; + } else { + return error.QemuNotEnabled; + } + }, + .wine => |bin_name| { + if (self.builder.enable_wine) { + return bin_name; + } else { + return error.WineNotEnabled; + } + }, + .wasmtime => |bin_name| { + if (self.builder.enable_wasmtime) { + return bin_name; + } else { + return error.WasmtimeNotEnabled; + } + }, + .darling => |bin_name| { + if (self.builder.enable_darling) { + return bin_name; + } else { + return error.DarlingNotEnabled; + } + }, + } +} + fn make(step: *Step) !void { const self = @fieldParentPtr(RunStep, "step", step); @@ -169,6 +238,9 @@ fn make(step: *Step) !void { // On Windows we don't have rpaths so we have to add .dll search paths to PATH self.addPathForDynLibs(artifact); } + if (try self.getExternalExecutor(artifact)) |executor| { + try argv_list.append(executor); + } const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder); try argv_list.append(executable_path); }, diff --git a/src/Compilation.zig b/src/Compilation.zig index 9126289804..3eb527b612 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1180,6 +1180,15 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { if (must_single_thread and !single_threaded) { return error.TargetRequiresSingleThreaded; } + if (!single_threaded and options.link_libcpp) { + if (options.target.cpu.arch.isARM()) { + log.warn( + \\libc++ does not work on multi-threaded ARM yet. + \\For more details: https://github.com/ziglang/zig/issues/6573 + , .{}); + return error.TargetRequiresSingleThreaded; + } + } const llvm_cpu_features: ?[*:0]const u8 = if (build_options.have_llvm and use_llvm) blk: { var buf = std.ArrayList(u8).init(arena); @@ -3803,6 +3812,10 @@ pub fn addCCArgs( try argv.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); try argv.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); try argv.append("-D_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS"); + + if (comp.bin_file.options.single_threaded) { + try argv.append("-D_LIBCPP_HAS_NO_THREADS"); + } else {} } if (comp.bin_file.options.link_libunwind) { diff --git a/src/libcxx.zig b/src/libcxx.zig index b2974e24c9..a5ab242339 100644 --- a/src/libcxx.zig +++ b/src/libcxx.zig @@ -128,6 +128,12 @@ pub fn buildLibCXX(comp: *Compilation) !void { continue; if (std.mem.startsWith(u8, cxx_src, "src/support/ibm/") and target.os.tag != .zos) continue; + if (comp.bin_file.options.single_threaded) { + if (std.mem.startsWith(u8, cxx_src, "src/support/win32/thread_win32.cpp")) { + continue; + } + try cflags.append("-D_LIBCPP_HAS_NO_THREADS"); + } try cflags.append("-DNDEBUG"); try cflags.append("-D_LIBCPP_BUILDING_LIBRARY"); @@ -145,8 +151,7 @@ pub fn buildLibCXX(comp: *Compilation) !void { } if (target.os.tag == .wasi) { - // WASI doesn't support thread and exception yet. - try cflags.append("-D_LIBCPP_HAS_NO_THREADS"); + // WASI doesn't support exceptions yet. try cflags.append("-fno-exceptions"); } @@ -264,13 +269,20 @@ pub fn buildLibCXXABI(comp: *Compilation) !void { var cflags = std.ArrayList([]const u8).init(arena); if (target.os.tag == .wasi) { - // WASI doesn't support thread and exception yet. - if (std.mem.startsWith(u8, cxxabi_src, "src/cxa_thread_atexit.cpp") or - std.mem.startsWith(u8, cxxabi_src, "src/cxa_exception.cpp") or + // WASI doesn't support exceptions yet. + if (std.mem.startsWith(u8, cxxabi_src, "src/cxa_exception.cpp") or std.mem.startsWith(u8, cxxabi_src, "src/cxa_personality.cpp")) continue; - try cflags.append("-D_LIBCXXABI_HAS_NO_THREADS"); try cflags.append("-fno-exceptions"); + } + + // WASM targets are single threaded. + if (comp.bin_file.options.single_threaded) { + if (std.mem.startsWith(u8, cxxabi_src, "src/cxa_thread_atexit.cpp")) { + continue; + } + try cflags.append("-D_LIBCXXABI_HAS_NO_THREADS"); + try cflags.append("-D_LIBCPP_HAS_NO_THREADS"); } else { try cflags.append("-DHAVE___CXA_THREAD_ATEXIT_IMPL"); } diff --git a/test/standalone/c_compiler/build.zig b/test/standalone/c_compiler/build.zig index ad500c3eb3..5fb39d5a94 100644 --- a/test/standalone/c_compiler/build.zig +++ b/test/standalone/c_compiler/build.zig @@ -1,20 +1,21 @@ const std = @import("std"); -const builtin = @import("builtin"); const Builder = std.build.Builder; const CrossTarget = std.zig.CrossTarget; -// TODO integrate this with the std.build executor API -fn isRunnableTarget(t: CrossTarget) bool { - if (t.isNative()) return true; - - return (t.getOsTag() == builtin.os.tag and - t.getCpuArch() == builtin.cpu.arch); -} - pub fn build(b: *Builder) void { const mode = b.standardReleaseOptions(); const target = b.standardTargetOptions(.{}); + const is_wine_enabled = b.option(bool, "enable-wine", "Use Wine to run cross compiled Windows tests") orelse false; + const is_qemu_enabled = b.option(bool, "enable-qemu", "Use QEMU to run cross compiled foreign architecture tests") orelse false; + const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false; + const is_darling_enabled = b.option(bool, "enable-darling", "[Experimental] Use Darling to run cross compiled macOS tests") orelse false; + const single_threaded = b.option(bool, "single-threaded", "Test single threaded mode") orelse false; + b.enable_wine = is_wine_enabled; + b.enable_qemu = is_qemu_enabled; + b.enable_wasmtime = is_wasmtime_enabled; + b.enable_darling = is_darling_enabled; + const test_step = b.step("test", "Test the program"); const exe_c = b.addExecutable("test_c", null); @@ -29,9 +30,16 @@ pub fn build(b: *Builder) void { exe_cpp.addCSourceFile("test.cpp", &[0][]const u8{}); exe_cpp.setBuildMode(mode); exe_cpp.setTarget(target); - exe_cpp.linkSystemLibrary("c++"); + exe_cpp.linkLibCpp(); + exe_cpp.single_threaded = single_threaded; + const os_tag = target.getOsTag(); + // macos C++ exceptions could be compiled, but not being catched, + // additional support is required, possibly unwind + DWARF CFI + if (target.getCpuArch().isWasm() or os_tag == .macos) { + exe_cpp.defineCMacro("_LIBCPP_NO_EXCEPTIONS", null); + } - switch (target.getOsTag()) { + switch (os_tag) { .windows => { // https://github.com/ziglang/zig/issues/8531 exe_cpp.want_lto = false; @@ -44,13 +52,17 @@ pub fn build(b: *Builder) void { else => {}, } - if (isRunnableTarget(target)) { - const run_c_cmd = exe_c.run(); + const run_c_cmd = exe_c.run(); + if (run_c_cmd.isRunnable()) { test_step.dependOn(&run_c_cmd.step); - const run_cpp_cmd = exe_cpp.run(); - test_step.dependOn(&run_cpp_cmd.step); } else { test_step.dependOn(&exe_c.step); + } + + const run_cpp_cmd = exe_cpp.run(); + if (run_cpp_cmd.isRunnable()) { + test_step.dependOn(&run_cpp_cmd.step); + } else { test_step.dependOn(&exe_cpp.step); } } diff --git a/test/standalone/c_compiler/test.c b/test/standalone/c_compiler/test.c index 842a63fc67..da0a137798 100644 --- a/test/standalone/c_compiler/test.c +++ b/test/standalone/c_compiler/test.c @@ -1,25 +1,28 @@ #include #include +#include -typedef struct { - int val; +typedef struct { + int val; } STest; int getVal(STest* data) { return data->val; } int main (int argc, char *argv[]) { - STest* data = (STest*)malloc(sizeof(STest)); - data->val = 123; + STest* data = (STest*)malloc(sizeof(STest)); + data->val = 123; - assert(getVal(data) != 456); - int ok = (getVal(data) == 123); + assert(getVal(data) != 456); + int ok = (getVal(data) == 123); - if (argc>1) fprintf(stdout, "val=%d\n", data->val); + if (argc > 1) { + fprintf(stdout, "val=%d\n", data->val); + } - free(data); + free(data); - if (!ok) abort(); + if (!ok) abort(); - return 0; + return EXIT_SUCCESS; } diff --git a/test/standalone/c_compiler/test.cpp b/test/standalone/c_compiler/test.cpp index 8c533d02cc..71b0c05996 100644 --- a/test/standalone/c_compiler/test.cpp +++ b/test/standalone/c_compiler/test.cpp @@ -1,15 +1,33 @@ -#include #include +#include + +#ifndef _LIBCPP_HAS_NO_THREADS +#include +#endif + +thread_local unsigned int tls_counter = 1; + +// a non-optimized way of checking for prime numbers: +bool is_prime(int x) { + for (int i = 2; i decl_node); GlobalLinkageId linkage; - SymbolVisibilityId visibility = SymbolVisibilityIdDefault; const char *unmangled_name = var->name; const char *symbol_name; if (var->export_list.length == 0) { @@ -8982,7 +8965,6 @@ static void do_code_gen(CodeGen *g) { GlobalExport *global_export = &var->export_list.items[0]; symbol_name = buf_ptr(&global_export->name); linkage = global_export->linkage; - visibility = global_export->visibility; } LLVMValueRef global_value; @@ -9028,8 +9010,6 @@ static void do_code_gen(CodeGen *g) { set_global_tls(g, var, global_value); } - LLVMSetVisibility(global_value, to_llvm_visibility(visibility)); - var->value_ref = global_value; for (size_t export_i = 1; export_i < var->export_list.length; export_i += 1) { diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 4449985574..62834e564d 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -8635,25 +8635,6 @@ static bool ir_resolve_global_linkage(IrAnalyze *ira, Stage1AirInst *value, Glob return true; } -static bool ir_resolve_global_visibility(IrAnalyze *ira, Stage1AirInst *value, SymbolVisibilityId *out) { - if (type_is_invalid(value->value->type)) - return false; - - ZigType *global_visibility_type = get_builtin_type(ira->codegen, "SymbolVisibility"); - - Stage1AirInst *casted_value = ir_implicit_cast(ira, value, global_visibility_type); - if (type_is_invalid(casted_value->value->type)) - return false; - - ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); - if (!const_val) - return false; - - *out = (SymbolVisibilityId)bigint_as_u32(&const_val->data.x_enum_tag); - return true; -} - - static bool ir_resolve_float_mode(IrAnalyze *ira, Stage1AirInst *value, FloatMode *out) { if (type_is_invalid(value->value->type)) return false; @@ -11680,12 +11661,6 @@ static Stage1AirInst *ir_analyze_instruction_export(IrAnalyze *ira, Stage1ZirIns if (type_is_invalid(section_inst->value->type)) return ira->codegen->invalid_inst_gen; - TypeStructField *visibility_field = find_struct_type_field(options_type, buf_create_from_str("visibility")); - src_assert(visibility_field != nullptr, instruction->base.source_node); - Stage1AirInst *visibility_inst = ir_analyze_struct_value_field_value(ira, instruction->base.scope, instruction->base.source_node, options, visibility_field); - if (type_is_invalid(visibility_inst->value->type)) - return ira->codegen->invalid_inst_gen; - // The `section` field is optional, we have to unwrap it first Stage1AirInst *non_null_check = ir_analyze_test_non_null(ira, instruction->base.scope, instruction->base.source_node, section_inst); bool is_non_null; @@ -11714,10 +11689,6 @@ static Stage1AirInst *ir_analyze_instruction_export(IrAnalyze *ira, Stage1ZirIns if (!ir_resolve_global_linkage(ira, linkage_inst, &global_linkage_id)) return ira->codegen->invalid_inst_gen; - SymbolVisibilityId global_visibility_id; - if (!ir_resolve_global_visibility(ira, visibility_inst, &global_visibility_id)) - return ira->codegen->invalid_inst_gen; - Buf *section_name = nullptr; if (section_str_inst != nullptr && !(section_name = ir_resolve_str(ira, section_str_inst))) return ira->codegen->invalid_inst_gen; @@ -11780,7 +11751,7 @@ static Stage1AirInst *ir_analyze_instruction_export(IrAnalyze *ira, Stage1ZirIns case CallingConventionSysV: case CallingConventionWin64: case CallingConventionPtxKernel: - add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, global_visibility_id, cc); + add_fn_export(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc); fn_entry->section_name = section_name; break; } @@ -11927,7 +11898,7 @@ static Stage1AirInst *ir_analyze_instruction_export(IrAnalyze *ira, Stage1ZirIns if (load_ptr->ptr->id == Stage1AirInstIdVarPtr) { Stage1AirInstVarPtr *var_ptr = reinterpret_cast(load_ptr->ptr); ZigVar *var = var_ptr->var; - add_var_export(ira->codegen, var, buf_ptr(symbol_name), global_linkage_id, global_visibility_id); + add_var_export(ira->codegen, var, buf_ptr(symbol_name), global_linkage_id); var->section_name = section_name; } } From 45415093c655d30ec226172695248fbf28941667 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 10 May 2022 16:38:37 -0700 Subject: [PATCH 1450/2031] reduce the scope of this branch * back out the changes to RunStep * move the disabled test to the .cpp code and avoid a confusing name-collision with the _LIBCPP macro prefix * fix merge conflict with the edits to the same test that ensure global initializers are called. Now this branch is only concerned with single-threaded targets and passing the correct macro defines to libc++. --- lib/std/build/RunStep.zig | 72 ---------------------------- src/Compilation.zig | 2 +- test/standalone/c_compiler/build.zig | 40 ++++++---------- test/standalone/c_compiler/test.cpp | 18 ++++++- 4 files changed, 31 insertions(+), 101 deletions(-) diff --git a/lib/std/build/RunStep.zig b/lib/std/build/RunStep.zig index 8cfbc3e89a..e00fe3deb6 100644 --- a/lib/std/build/RunStep.zig +++ b/lib/std/build/RunStep.zig @@ -1,7 +1,6 @@ const std = @import("../std.zig"); const builtin = @import("builtin"); const build = std.build; -const CrossTarget = std.zig.CrossTarget; const Step = build.Step; const Builder = build.Builder; const LibExeObjStep = build.LibExeObjStep; @@ -143,23 +142,6 @@ pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) }; } -/// Returns true if the step could be run, otherwise false -pub fn isRunnable( - self: *RunStep, -) bool { - for (self.argv.items) |arg| { - switch (arg) { - .artifact => |artifact| { - _ = self.getExternalExecutor(artifact) catch { - return false; - }; - }, - else => {}, - } - } - return true; -} - pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void { self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) }; } @@ -172,57 +154,6 @@ fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo { }; } -fn getExternalExecutor(self: *RunStep, artifact: *LibExeObjStep) !?[]const u8 { - const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc; - const executor = self.builder.host.getExternalExecutor(artifact.target_info, .{ - .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null, - .link_libc = artifact.is_linking_libc, - }); - switch (executor) { - .bad_dl, .bad_os_or_cpu => { - return error.NoExecutable; - }, - .native => { - return null; - }, - .rosetta => { - if (self.builder.enable_rosetta) { - return null; - } else { - return error.RosettaNotEnabled; - } - }, - .qemu => |bin_name| { - if (self.builder.enable_qemu) { - return bin_name; - } else { - return error.QemuNotEnabled; - } - }, - .wine => |bin_name| { - if (self.builder.enable_wine) { - return bin_name; - } else { - return error.WineNotEnabled; - } - }, - .wasmtime => |bin_name| { - if (self.builder.enable_wasmtime) { - return bin_name; - } else { - return error.WasmtimeNotEnabled; - } - }, - .darling => |bin_name| { - if (self.builder.enable_darling) { - return bin_name; - } else { - return error.DarlingNotEnabled; - } - }, - } -} - fn make(step: *Step) !void { const self = @fieldParentPtr(RunStep, "step", step); @@ -238,9 +169,6 @@ fn make(step: *Step) !void { // On Windows we don't have rpaths so we have to add .dll search paths to PATH self.addPathForDynLibs(artifact); } - if (try self.getExternalExecutor(artifact)) |executor| { - try argv_list.append(executor); - } const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder); try argv_list.append(executable_path); }, diff --git a/src/Compilation.zig b/src/Compilation.zig index 3eb527b612..5e4ab0ec12 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3815,7 +3815,7 @@ pub fn addCCArgs( if (comp.bin_file.options.single_threaded) { try argv.append("-D_LIBCPP_HAS_NO_THREADS"); - } else {} + } } if (comp.bin_file.options.link_libunwind) { diff --git a/test/standalone/c_compiler/build.zig b/test/standalone/c_compiler/build.zig index 5fb39d5a94..240d535182 100644 --- a/test/standalone/c_compiler/build.zig +++ b/test/standalone/c_compiler/build.zig @@ -1,21 +1,20 @@ const std = @import("std"); +const builtin = @import("builtin"); const Builder = std.build.Builder; const CrossTarget = std.zig.CrossTarget; +// TODO integrate this with the std.build executor API +fn isRunnableTarget(t: CrossTarget) bool { + if (t.isNative()) return true; + + return (t.getOsTag() == builtin.os.tag and + t.getCpuArch() == builtin.cpu.arch); +} + pub fn build(b: *Builder) void { const mode = b.standardReleaseOptions(); const target = b.standardTargetOptions(.{}); - const is_wine_enabled = b.option(bool, "enable-wine", "Use Wine to run cross compiled Windows tests") orelse false; - const is_qemu_enabled = b.option(bool, "enable-qemu", "Use QEMU to run cross compiled foreign architecture tests") orelse false; - const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false; - const is_darling_enabled = b.option(bool, "enable-darling", "[Experimental] Use Darling to run cross compiled macOS tests") orelse false; - const single_threaded = b.option(bool, "single-threaded", "Test single threaded mode") orelse false; - b.enable_wine = is_wine_enabled; - b.enable_qemu = is_qemu_enabled; - b.enable_wasmtime = is_wasmtime_enabled; - b.enable_darling = is_darling_enabled; - const test_step = b.step("test", "Test the program"); const exe_c = b.addExecutable("test_c", null); @@ -31,15 +30,8 @@ pub fn build(b: *Builder) void { exe_cpp.setBuildMode(mode); exe_cpp.setTarget(target); exe_cpp.linkLibCpp(); - exe_cpp.single_threaded = single_threaded; - const os_tag = target.getOsTag(); - // macos C++ exceptions could be compiled, but not being catched, - // additional support is required, possibly unwind + DWARF CFI - if (target.getCpuArch().isWasm() or os_tag == .macos) { - exe_cpp.defineCMacro("_LIBCPP_NO_EXCEPTIONS", null); - } - switch (os_tag) { + switch (target.getOsTag()) { .windows => { // https://github.com/ziglang/zig/issues/8531 exe_cpp.want_lto = false; @@ -52,17 +44,13 @@ pub fn build(b: *Builder) void { else => {}, } - const run_c_cmd = exe_c.run(); - if (run_c_cmd.isRunnable()) { + if (isRunnableTarget(target)) { + const run_c_cmd = exe_c.run(); test_step.dependOn(&run_c_cmd.step); - } else { - test_step.dependOn(&exe_c.step); - } - - const run_cpp_cmd = exe_cpp.run(); - if (run_cpp_cmd.isRunnable()) { + const run_cpp_cmd = exe_cpp.run(); test_step.dependOn(&run_cpp_cmd.step); } else { + test_step.dependOn(&exe_c.step); test_step.dependOn(&exe_cpp.step); } } diff --git a/test/standalone/c_compiler/test.cpp b/test/standalone/c_compiler/test.cpp index 71b0c05996..1f3fe173d2 100644 --- a/test/standalone/c_compiler/test.cpp +++ b/test/standalone/c_compiler/test.cpp @@ -30,13 +30,25 @@ private: int m_val; }; +class GlobalConstructorTest { +public: + GlobalConstructorTest(int val) : m_val(val) {}; + virtual ~GlobalConstructorTest() {} + + virtual int getVal() const { return m_val; } + virtual void printVal() { std::cout << "val=" << m_val << std::endl; } +private: + int m_val; +}; + volatile int runtime_val = 456; -CTest global(runtime_val); // test if global initializers are called. +GlobalConstructorTest global(runtime_val); // test if global initializers are called. int main (int argc, char *argv[]) { assert(global.getVal() == 456); + auto t = std::make_unique(123); assert(t->getVal() != 456); assert(tls_counter == 2); @@ -53,7 +65,9 @@ int main (int argc, char *argv[]) assert(ret); #endif -#ifndef _LIBCPP_NO_EXCEPTIONS +#if !defined(__wasm__) && !defined(__APPLE__) + // WASM and macOS are not passing this yet. + // TODO file an issue for this and link it here. try { throw 20; } catch (int e) { From e0a514df41cdf40b0e99ba48b86143bfc03dd3aa Mon Sep 17 00:00:00 2001 From: Silver Date: Fri, 24 Dec 2021 05:38:14 +0000 Subject: [PATCH 1451/2031] std: make IntegerBitSet and ArrayBitSet have defined memory layout This is useful for wrapping C libraries and native interfaces that make use of bit sets --- lib/std/bit_set.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/bit_set.zig b/lib/std/bit_set.zig index b9c8fd6200..8006a623b5 100644 --- a/lib/std/bit_set.zig +++ b/lib/std/bit_set.zig @@ -50,7 +50,7 @@ pub fn StaticBitSet(comptime size: usize) type { /// This set is good for sets with a small size, but may generate /// inefficient code for larger sets, especially in debug mode. pub fn IntegerBitSet(comptime size: u16) type { - return struct { + return packed struct { const Self = @This(); // TODO: Make this a comptime field once those are fixed @@ -287,7 +287,7 @@ pub fn ArrayBitSet(comptime MaskIntType: type, comptime size: usize) type { ", which contains padding bits. Please round this up to an unpadded integer size (i.e. " ++ @typeName(FixedMaskType) ++ ")."); } - return struct { + return extern struct { const Self = @This(); // TODO: Make this a comptime field once those are fixed From 550888e2ac0dca25b5d736676bfe3ee9d4c8a8ec Mon Sep 17 00:00:00 2001 From: Mahdi Rakhshandehroo <24380301+mrakh@users.noreply.github.com> Date: Mon, 27 Dec 2021 19:13:15 -0500 Subject: [PATCH 1452/2031] std: improve random float generation --- lib/std/rand.zig | 128 ++++++++++++++++++++++++++++++++++++--- lib/std/rand/Dilbert.zig | 52 ++++++++++++++++ 2 files changed, 171 insertions(+), 9 deletions(-) create mode 100644 lib/std/rand/Dilbert.zig diff --git a/lib/std/rand.zig b/lib/std/rand.zig index 0c3a0fd2ba..b2c1f6734a 100644 --- a/lib/std/rand.zig +++ b/lib/std/rand.zig @@ -16,6 +16,8 @@ const math = std.math; const ziggurat = @import("rand/ziggurat.zig"); const maxInt = std.math.maxInt; +const Dilbert = @import("rand/Dilbert.zig"); + /// Fast unbiased random numbers. pub const DefaultPrng = Xoshiro256; @@ -249,18 +251,51 @@ pub const Random = struct { /// Return a floating point value evenly distributed in the range [0, 1). pub fn float(r: Random, comptime T: type) T { - // Generate a uniform value between [1, 2) and scale down to [0, 1). - // Note: The lowest mantissa bit is always set to 0 so we only use half the available range. + // Generate a uniformly random value between for the mantissa. + // Then generate an exponentially biased random value for the exponent. + // Over the previous method, this has the advantage of being able to + // represent every possible value in the available range. switch (T) { f32 => { - const s = r.int(u32); - const repr = (0x7f << 23) | (s >> 9); - return @bitCast(f32, repr) - 1.0; + // Use 23 random bits for the mantissa, and the rest for the exponent. + // If all 41 bits are zero, generate additional random bits, until a + // set bit is found, or 126 bits have been generated. + const rand = r.int(u64); + var rand_lz = @clz(u64, rand | 0x7FFFFF); + if (rand_lz == 41) { + rand_lz += @clz(u64, r.int(u64)); + if (rand_lz == 41 + 64) { + // It is astronomically unlikely to reach this point. + rand_lz += @clz(u32, r.int(u32) | 0x7FF); + } + } + const mantissa = @truncate(u23, rand); + const exponent = @as(u32, 126 - rand_lz) << 23; + return @bitCast(f32, exponent | mantissa); }, f64 => { - const s = r.int(u64); - const repr = (0x3ff << 52) | (s >> 12); - return @bitCast(f64, repr) - 1.0; + // Use 52 random bits for the mantissa, and the rest for the exponent. + // If all 12 bits are zero, generate additional random bits, until a + // set bit is found, or 1022 bits have been generated. + const rand = r.int(u64); + var rand_lz: u64 = @clz(u64, rand | 0xFFFFFFFFFFFFF); + if (rand_lz == 12) { + while (true) { + // It is astronomically unlikely for this loop to execute more than once. + const addl_rand_lz = @clz(u64, r.int(u64)); + rand_lz += addl_rand_lz; + if (addl_rand_lz != 64) { + break; + } + if (rand_lz >= 1022) { + rand_lz = 1022; + break; + } + } + } + const mantissa = rand & 0xFFFFFFFFFFFFF; + const exponent = (1022 - rand_lz) << 52; + return @bitCast(f64, exponent | mantissa); }, else => @compileError("unknown floating point type"), } @@ -573,7 +608,7 @@ test "splitmix64 sequence" { } // Actual Random helper function tests, pcg engine is assumed correct. -test "Random float" { +test "Random float correctness" { var prng = DefaultPrng.init(0); const random = prng.random(); @@ -589,6 +624,81 @@ test "Random float" { } } +// Check the "astronomically unlikely" code paths. +test "Random float coverage" { + var prng = try Dilbert.init(&[_]u8{0}); + const random = prng.random(); + + const rand_f64 = random.float(f64); + const rand_f32 = random.float(f32); + + try expect(rand_f32 == 0.0); + try expect(rand_f64 == 0.0); +} + +test "Random float chi-square goodness of fit" { + const num_numbers = 100000; + const num_buckets = 1000; + + var f32_hist = std.AutoHashMap(u32, u32).init(std.testing.allocator); + defer f32_hist.deinit(); + var f64_hist = std.AutoHashMap(u64, u32).init(std.testing.allocator); + defer f64_hist.deinit(); + + var prng = DefaultPrng.init(0); + const random = prng.random(); + + var i: usize = 0; + while (i < num_numbers) : (i += 1) { + const rand_f32 = random.float(f32); + const rand_f64 = random.float(f64); + var f32_put = try f32_hist.getOrPut(@floatToInt(u32, rand_f32 * @intToFloat(f32, num_buckets))); + if (f32_put.found_existing) { + f32_put.value_ptr.* += 1; + } else { + f32_put.value_ptr.* = 0; + } + var f64_put = try f64_hist.getOrPut(@floatToInt(u32, rand_f64 * @intToFloat(f64, num_buckets))); + if (f64_put.found_existing) { + f64_put.value_ptr.* += 1; + } else { + f64_put.value_ptr.* = 0; + } + } + + var f32_total_variance: f64 = 0; + var f64_total_variance: f64 = 0; + + { + var j: u32 = 0; + while (j < num_buckets) : (j += 1) { + const count = @intToFloat(f64, (if (f32_hist.get(j)) |v| v else 0)); + const expected = @intToFloat(f64, num_numbers) / @intToFloat(f64, num_buckets); + const delta = count - expected; + const variance = (delta * delta) / expected; + f32_total_variance += variance; + } + } + + { + var j: u64 = 0; + while (j < num_buckets) : (j += 1) { + const count = @intToFloat(f64, (if (f64_hist.get(j)) |v| v else 0)); + const expected = @intToFloat(f64, num_numbers) / @intToFloat(f64, num_buckets); + const delta = count - expected; + const variance = (delta * delta) / expected; + f64_total_variance += variance; + } + } + + // Corresponds to a p-value > 0.05. + // Critical value is calculated by opening a Python interpreter and running: + // scipy.stats.chi2.isf(0.05, num_buckets - 1) + const critical_value = 1073.6426506574246; + try expect(f32_total_variance < critical_value); + try expect(f64_total_variance < critical_value); +} + test "Random shuffle" { var prng = DefaultPrng.init(0); const random = prng.random(); diff --git a/lib/std/rand/Dilbert.zig b/lib/std/rand/Dilbert.zig new file mode 100644 index 0000000000..dd07a4ceb3 --- /dev/null +++ b/lib/std/rand/Dilbert.zig @@ -0,0 +1,52 @@ +//! Dilbert PRNG +//! Do not use this PRNG! It is meant to be predictable, for the purposes of test reproducibility and coverage. +//! Its output is just a repeat of a user-specified byte pattern. +//! Name is a reference to this comic: https://dilbert.com/strip/2001-10-25 + +const std = @import("std"); +const Random = std.rand.Random; +const math = std.math; +const Dilbert = @This(); + +pattern: []const u8 = undefined, +curr_idx: usize = 0, + +pub fn init(pattern: []const u8) !Dilbert { + if (pattern.len == 0) + return error.EmptyPattern; + var self = Dilbert{}; + self.pattern = pattern; + self.curr_idx = 0; + return self; +} + +pub fn random(self: *Dilbert) Random { + return Random.init(self, fill); +} + +pub fn fill(self: *Dilbert, buf: []u8) void { + for (buf) |*byte| { + byte.* = self.pattern[self.curr_idx]; + self.curr_idx = (self.curr_idx + 1) % self.pattern.len; + } +} + +test "Dilbert fill" { + var r = try Dilbert.init("9nine"); + + const seq = [_]u64{ + 0x396E696E65396E69, + 0x6E65396E696E6539, + 0x6E696E65396E696E, + 0x65396E696E65396E, + 0x696E65396E696E65, + }; + + for (seq) |s| { + var buf0: [8]u8 = undefined; + var buf1: [8]u8 = undefined; + std.mem.writeIntBig(u64, &buf0, s); + r.fill(&buf1); + try std.testing.expect(std.mem.eql(u8, buf0[0..], buf1[0..])); + } +} From 7bedeb9659af84daab33a4804c54b4c8811c0c5d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 10 May 2022 19:02:03 -0700 Subject: [PATCH 1453/2031] std.rand: move tests to a separate test file --- lib/std/rand.zig | 396 +--------------------------------- lib/std/rand/Dilbert.zig | 52 ----- lib/std/rand/test.zig | 447 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 448 insertions(+), 447 deletions(-) delete mode 100644 lib/std/rand/Dilbert.zig create mode 100644 lib/std/rand/test.zig diff --git a/lib/std/rand.zig b/lib/std/rand.zig index b2c1f6734a..01397085f7 100644 --- a/lib/std/rand.zig +++ b/lib/std/rand.zig @@ -9,15 +9,11 @@ const std = @import("std.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; -const expect = std.testing.expect; -const expectEqual = std.testing.expectEqual; const mem = std.mem; const math = std.math; const ziggurat = @import("rand/ziggurat.zig"); const maxInt = std.math.maxInt; -const Dilbert = @import("rand/Dilbert.zig"); - /// Fast unbiased random numbers. pub const DefaultPrng = Xoshiro256; @@ -354,221 +350,6 @@ pub fn limitRangeBiased(comptime T: type, random_int: T, less_than: T) T { return @intCast(T, m >> bits); } -const SequentialPrng = struct { - const Self = @This(); - next_value: u8, - - pub fn init() Self { - return Self{ - .next_value = 0, - }; - } - - pub fn random(self: *Self) Random { - return Random.init(self, fill); - } - - pub fn fill(self: *Self, buf: []u8) void { - for (buf) |*b| { - b.* = self.next_value; - } - self.next_value +%= 1; - } -}; - -test "Random int" { - try testRandomInt(); - comptime try testRandomInt(); -} -fn testRandomInt() !void { - var rng = SequentialPrng.init(); - const random = rng.random(); - - try expect(random.int(u0) == 0); - - rng.next_value = 0; - try expect(random.int(u1) == 0); - try expect(random.int(u1) == 1); - try expect(random.int(u2) == 2); - try expect(random.int(u2) == 3); - try expect(random.int(u2) == 0); - - rng.next_value = 0xff; - try expect(random.int(u8) == 0xff); - rng.next_value = 0x11; - try expect(random.int(u8) == 0x11); - - rng.next_value = 0xff; - try expect(random.int(u32) == 0xffffffff); - rng.next_value = 0x11; - try expect(random.int(u32) == 0x11111111); - - rng.next_value = 0xff; - try expect(random.int(i32) == -1); - rng.next_value = 0x11; - try expect(random.int(i32) == 0x11111111); - - rng.next_value = 0xff; - try expect(random.int(i8) == -1); - rng.next_value = 0x11; - try expect(random.int(i8) == 0x11); - - rng.next_value = 0xff; - try expect(random.int(u33) == 0x1ffffffff); - rng.next_value = 0xff; - try expect(random.int(i1) == -1); - rng.next_value = 0xff; - try expect(random.int(i2) == -1); - rng.next_value = 0xff; - try expect(random.int(i33) == -1); -} - -test "Random boolean" { - try testRandomBoolean(); - comptime try testRandomBoolean(); -} -fn testRandomBoolean() !void { - var rng = SequentialPrng.init(); - const random = rng.random(); - - try expect(random.boolean() == false); - try expect(random.boolean() == true); - try expect(random.boolean() == false); - try expect(random.boolean() == true); -} - -test "Random enum" { - try testRandomEnumValue(); - comptime try testRandomEnumValue(); -} -fn testRandomEnumValue() !void { - const TestEnum = enum { - First, - Second, - Third, - }; - var rng = SequentialPrng.init(); - const random = rng.random(); - rng.next_value = 0; - try expect(random.enumValue(TestEnum) == TestEnum.First); - try expect(random.enumValue(TestEnum) == TestEnum.First); - try expect(random.enumValue(TestEnum) == TestEnum.First); -} - -test "Random intLessThan" { - @setEvalBranchQuota(10000); - try testRandomIntLessThan(); - comptime try testRandomIntLessThan(); -} -fn testRandomIntLessThan() !void { - var rng = SequentialPrng.init(); - const random = rng.random(); - - rng.next_value = 0xff; - try expect(random.uintLessThan(u8, 4) == 3); - try expect(rng.next_value == 0); - try expect(random.uintLessThan(u8, 4) == 0); - try expect(rng.next_value == 1); - - rng.next_value = 0; - try expect(random.uintLessThan(u64, 32) == 0); - - // trigger the bias rejection code path - rng.next_value = 0; - try expect(random.uintLessThan(u8, 3) == 0); - // verify we incremented twice - try expect(rng.next_value == 2); - - rng.next_value = 0xff; - try expect(random.intRangeLessThan(u8, 0, 0x80) == 0x7f); - rng.next_value = 0xff; - try expect(random.intRangeLessThan(u8, 0x7f, 0xff) == 0xfe); - - rng.next_value = 0xff; - try expect(random.intRangeLessThan(i8, 0, 0x40) == 0x3f); - rng.next_value = 0xff; - try expect(random.intRangeLessThan(i8, -0x40, 0x40) == 0x3f); - rng.next_value = 0xff; - try expect(random.intRangeLessThan(i8, -0x80, 0) == -1); - - rng.next_value = 0xff; - try expect(random.intRangeLessThan(i3, -4, 0) == -1); - rng.next_value = 0xff; - try expect(random.intRangeLessThan(i3, -2, 2) == 1); -} - -test "Random intAtMost" { - @setEvalBranchQuota(10000); - try testRandomIntAtMost(); - comptime try testRandomIntAtMost(); -} -fn testRandomIntAtMost() !void { - var rng = SequentialPrng.init(); - const random = rng.random(); - - rng.next_value = 0xff; - try expect(random.uintAtMost(u8, 3) == 3); - try expect(rng.next_value == 0); - try expect(random.uintAtMost(u8, 3) == 0); - - // trigger the bias rejection code path - rng.next_value = 0; - try expect(random.uintAtMost(u8, 2) == 0); - // verify we incremented twice - try expect(rng.next_value == 2); - - rng.next_value = 0xff; - try expect(random.intRangeAtMost(u8, 0, 0x7f) == 0x7f); - rng.next_value = 0xff; - try expect(random.intRangeAtMost(u8, 0x7f, 0xfe) == 0xfe); - - rng.next_value = 0xff; - try expect(random.intRangeAtMost(i8, 0, 0x3f) == 0x3f); - rng.next_value = 0xff; - try expect(random.intRangeAtMost(i8, -0x40, 0x3f) == 0x3f); - rng.next_value = 0xff; - try expect(random.intRangeAtMost(i8, -0x80, -1) == -1); - - rng.next_value = 0xff; - try expect(random.intRangeAtMost(i3, -4, -1) == -1); - rng.next_value = 0xff; - try expect(random.intRangeAtMost(i3, -2, 1) == 1); - - try expect(random.uintAtMost(u0, 0) == 0); -} - -test "Random Biased" { - var prng = DefaultPrng.init(0); - const random = prng.random(); - // Not thoroughly checking the logic here. - // Just want to execute all the paths with different types. - - try expect(random.uintLessThanBiased(u1, 1) == 0); - try expect(random.uintLessThanBiased(u32, 10) < 10); - try expect(random.uintLessThanBiased(u64, 20) < 20); - - try expect(random.uintAtMostBiased(u0, 0) == 0); - try expect(random.uintAtMostBiased(u1, 0) <= 0); - try expect(random.uintAtMostBiased(u32, 10) <= 10); - try expect(random.uintAtMostBiased(u64, 20) <= 20); - - try expect(random.intRangeLessThanBiased(u1, 0, 1) == 0); - try expect(random.intRangeLessThanBiased(i1, -1, 0) == -1); - try expect(random.intRangeLessThanBiased(u32, 10, 20) >= 10); - try expect(random.intRangeLessThanBiased(i32, 10, 20) >= 10); - try expect(random.intRangeLessThanBiased(u64, 20, 40) >= 20); - try expect(random.intRangeLessThanBiased(i64, 20, 40) >= 20); - - // uncomment for broken module error: - //expect(random.intRangeAtMostBiased(u0, 0, 0) == 0); - try expect(random.intRangeAtMostBiased(u1, 0, 1) >= 0); - try expect(random.intRangeAtMostBiased(i1, -1, 0) >= -1); - try expect(random.intRangeAtMostBiased(u32, 10, 20) >= 10); - try expect(random.intRangeAtMostBiased(i32, 10, 20) >= 10); - try expect(random.intRangeAtMostBiased(u64, 20, 40) >= 20); - try expect(random.intRangeAtMostBiased(i64, 20, 40) >= 20); -} - // Generator to extend 64-bit seed values into longer sequences. // // The number of cycles is thus limited to 64-bits regardless of the engine, but this @@ -590,182 +371,7 @@ pub const SplitMix64 = struct { } }; -test "splitmix64 sequence" { - var r = SplitMix64.init(0xaeecf86f7878dd75); - - const seq = [_]u64{ - 0x5dbd39db0178eb44, - 0xa9900fb66b397da3, - 0x5c1a28b1aeebcf5c, - 0x64a963238f776912, - 0xc6d4177b21d1c0ab, - 0xb2cbdbdb5ea35394, - }; - - for (seq) |s| { - try expect(s == r.next()); - } -} - -// Actual Random helper function tests, pcg engine is assumed correct. -test "Random float correctness" { - var prng = DefaultPrng.init(0); - const random = prng.random(); - - var i: usize = 0; - while (i < 1000) : (i += 1) { - const val1 = random.float(f32); - try expect(val1 >= 0.0); - try expect(val1 < 1.0); - - const val2 = random.float(f64); - try expect(val2 >= 0.0); - try expect(val2 < 1.0); - } -} - -// Check the "astronomically unlikely" code paths. -test "Random float coverage" { - var prng = try Dilbert.init(&[_]u8{0}); - const random = prng.random(); - - const rand_f64 = random.float(f64); - const rand_f32 = random.float(f32); - - try expect(rand_f32 == 0.0); - try expect(rand_f64 == 0.0); -} - -test "Random float chi-square goodness of fit" { - const num_numbers = 100000; - const num_buckets = 1000; - - var f32_hist = std.AutoHashMap(u32, u32).init(std.testing.allocator); - defer f32_hist.deinit(); - var f64_hist = std.AutoHashMap(u64, u32).init(std.testing.allocator); - defer f64_hist.deinit(); - - var prng = DefaultPrng.init(0); - const random = prng.random(); - - var i: usize = 0; - while (i < num_numbers) : (i += 1) { - const rand_f32 = random.float(f32); - const rand_f64 = random.float(f64); - var f32_put = try f32_hist.getOrPut(@floatToInt(u32, rand_f32 * @intToFloat(f32, num_buckets))); - if (f32_put.found_existing) { - f32_put.value_ptr.* += 1; - } else { - f32_put.value_ptr.* = 0; - } - var f64_put = try f64_hist.getOrPut(@floatToInt(u32, rand_f64 * @intToFloat(f64, num_buckets))); - if (f64_put.found_existing) { - f64_put.value_ptr.* += 1; - } else { - f64_put.value_ptr.* = 0; - } - } - - var f32_total_variance: f64 = 0; - var f64_total_variance: f64 = 0; - - { - var j: u32 = 0; - while (j < num_buckets) : (j += 1) { - const count = @intToFloat(f64, (if (f32_hist.get(j)) |v| v else 0)); - const expected = @intToFloat(f64, num_numbers) / @intToFloat(f64, num_buckets); - const delta = count - expected; - const variance = (delta * delta) / expected; - f32_total_variance += variance; - } - } - - { - var j: u64 = 0; - while (j < num_buckets) : (j += 1) { - const count = @intToFloat(f64, (if (f64_hist.get(j)) |v| v else 0)); - const expected = @intToFloat(f64, num_numbers) / @intToFloat(f64, num_buckets); - const delta = count - expected; - const variance = (delta * delta) / expected; - f64_total_variance += variance; - } - } - - // Corresponds to a p-value > 0.05. - // Critical value is calculated by opening a Python interpreter and running: - // scipy.stats.chi2.isf(0.05, num_buckets - 1) - const critical_value = 1073.6426506574246; - try expect(f32_total_variance < critical_value); - try expect(f64_total_variance < critical_value); -} - -test "Random shuffle" { - var prng = DefaultPrng.init(0); - const random = prng.random(); - - var seq = [_]u8{ 0, 1, 2, 3, 4 }; - var seen = [_]bool{false} ** 5; - - var i: usize = 0; - while (i < 1000) : (i += 1) { - random.shuffle(u8, seq[0..]); - seen[seq[0]] = true; - try expect(sumArray(seq[0..]) == 10); - } - - // we should see every entry at the head at least once - for (seen) |e| { - try expect(e == true); - } -} - -fn sumArray(s: []const u8) u32 { - var r: u32 = 0; - for (s) |e| - r += e; - return r; -} - -test "Random range" { - var prng = DefaultPrng.init(0); - const random = prng.random(); - - try testRange(random, -4, 3); - try testRange(random, -4, -1); - try testRange(random, 10, 14); - try testRange(random, -0x80, 0x7f); -} - -fn testRange(r: Random, start: i8, end: i8) !void { - try testRangeBias(r, start, end, true); - try testRangeBias(r, start, end, false); -} -fn testRangeBias(r: Random, start: i8, end: i8, biased: bool) !void { - const count = @intCast(usize, @as(i32, end) - @as(i32, start)); - var values_buffer = [_]bool{false} ** 0x100; - const values = values_buffer[0..count]; - var i: usize = 0; - while (i < count) { - const value: i32 = if (biased) r.intRangeLessThanBiased(i8, start, end) else r.intRangeLessThan(i8, start, end); - const index = @intCast(usize, value - start); - if (!values[index]) { - i += 1; - values[index] = true; - } - } -} - -test "CSPRNG" { - var secret_seed: [DefaultCsprng.secret_seed_length]u8 = undefined; - std.crypto.random.bytes(&secret_seed); - var csprng = DefaultCsprng.init(secret_seed); - const random = csprng.random(); - const a = random.int(u64); - const b = random.int(u64); - const c = random.int(u64); - try expect(a ^ b ^ c != 0); -} - test { std.testing.refAllDecls(@This()); + _ = @import("rand/test.zig"); } diff --git a/lib/std/rand/Dilbert.zig b/lib/std/rand/Dilbert.zig deleted file mode 100644 index dd07a4ceb3..0000000000 --- a/lib/std/rand/Dilbert.zig +++ /dev/null @@ -1,52 +0,0 @@ -//! Dilbert PRNG -//! Do not use this PRNG! It is meant to be predictable, for the purposes of test reproducibility and coverage. -//! Its output is just a repeat of a user-specified byte pattern. -//! Name is a reference to this comic: https://dilbert.com/strip/2001-10-25 - -const std = @import("std"); -const Random = std.rand.Random; -const math = std.math; -const Dilbert = @This(); - -pattern: []const u8 = undefined, -curr_idx: usize = 0, - -pub fn init(pattern: []const u8) !Dilbert { - if (pattern.len == 0) - return error.EmptyPattern; - var self = Dilbert{}; - self.pattern = pattern; - self.curr_idx = 0; - return self; -} - -pub fn random(self: *Dilbert) Random { - return Random.init(self, fill); -} - -pub fn fill(self: *Dilbert, buf: []u8) void { - for (buf) |*byte| { - byte.* = self.pattern[self.curr_idx]; - self.curr_idx = (self.curr_idx + 1) % self.pattern.len; - } -} - -test "Dilbert fill" { - var r = try Dilbert.init("9nine"); - - const seq = [_]u64{ - 0x396E696E65396E69, - 0x6E65396E696E6539, - 0x6E696E65396E696E, - 0x65396E696E65396E, - 0x696E65396E696E65, - }; - - for (seq) |s| { - var buf0: [8]u8 = undefined; - var buf1: [8]u8 = undefined; - std.mem.writeIntBig(u64, &buf0, s); - r.fill(&buf1); - try std.testing.expect(std.mem.eql(u8, buf0[0..], buf1[0..])); - } -} diff --git a/lib/std/rand/test.zig b/lib/std/rand/test.zig new file mode 100644 index 0000000000..6915e028f2 --- /dev/null +++ b/lib/std/rand/test.zig @@ -0,0 +1,447 @@ +const std = @import("../std.zig"); +const math = std.math; +const DefaultPrng = std.rand.DefaultPrng; +const Random = std.rand.Random; +const SplitMix64 = std.rand.SplitMix64; +const DefaultCsprng = std.rand.DefaultCsprng; +const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; + +const SequentialPrng = struct { + const Self = @This(); + next_value: u8, + + pub fn init() Self { + return Self{ + .next_value = 0, + }; + } + + pub fn random(self: *Self) Random { + return Random.init(self, fill); + } + + pub fn fill(self: *Self, buf: []u8) void { + for (buf) |*b| { + b.* = self.next_value; + } + self.next_value +%= 1; + } +}; + +/// Do not use this PRNG! It is meant to be predictable, for the purposes of test reproducibility and coverage. +/// Its output is just a repeat of a user-specified byte pattern. +/// Name is a reference to this comic: https://dilbert.com/strip/2001-10-25 +const Dilbert = struct { + pattern: []const u8 = undefined, + curr_idx: usize = 0, + + pub fn init(pattern: []const u8) !Dilbert { + if (pattern.len == 0) + return error.EmptyPattern; + var self = Dilbert{}; + self.pattern = pattern; + self.curr_idx = 0; + return self; + } + + pub fn random(self: *Dilbert) Random { + return Random.init(self, fill); + } + + pub fn fill(self: *Dilbert, buf: []u8) void { + for (buf) |*byte| { + byte.* = self.pattern[self.curr_idx]; + self.curr_idx = (self.curr_idx + 1) % self.pattern.len; + } + } + + test "Dilbert fill" { + var r = try Dilbert.init("9nine"); + + const seq = [_]u64{ + 0x396E696E65396E69, + 0x6E65396E696E6539, + 0x6E696E65396E696E, + 0x65396E696E65396E, + 0x696E65396E696E65, + }; + + for (seq) |s| { + var buf0: [8]u8 = undefined; + var buf1: [8]u8 = undefined; + std.mem.writeIntBig(u64, &buf0, s); + r.fill(&buf1); + try std.testing.expect(std.mem.eql(u8, buf0[0..], buf1[0..])); + } + } +}; + +test "Random int" { + try testRandomInt(); + comptime try testRandomInt(); +} +fn testRandomInt() !void { + var rng = SequentialPrng.init(); + const random = rng.random(); + + try expect(random.int(u0) == 0); + + rng.next_value = 0; + try expect(random.int(u1) == 0); + try expect(random.int(u1) == 1); + try expect(random.int(u2) == 2); + try expect(random.int(u2) == 3); + try expect(random.int(u2) == 0); + + rng.next_value = 0xff; + try expect(random.int(u8) == 0xff); + rng.next_value = 0x11; + try expect(random.int(u8) == 0x11); + + rng.next_value = 0xff; + try expect(random.int(u32) == 0xffffffff); + rng.next_value = 0x11; + try expect(random.int(u32) == 0x11111111); + + rng.next_value = 0xff; + try expect(random.int(i32) == -1); + rng.next_value = 0x11; + try expect(random.int(i32) == 0x11111111); + + rng.next_value = 0xff; + try expect(random.int(i8) == -1); + rng.next_value = 0x11; + try expect(random.int(i8) == 0x11); + + rng.next_value = 0xff; + try expect(random.int(u33) == 0x1ffffffff); + rng.next_value = 0xff; + try expect(random.int(i1) == -1); + rng.next_value = 0xff; + try expect(random.int(i2) == -1); + rng.next_value = 0xff; + try expect(random.int(i33) == -1); +} + +test "Random boolean" { + try testRandomBoolean(); + comptime try testRandomBoolean(); +} +fn testRandomBoolean() !void { + var rng = SequentialPrng.init(); + const random = rng.random(); + + try expect(random.boolean() == false); + try expect(random.boolean() == true); + try expect(random.boolean() == false); + try expect(random.boolean() == true); +} + +test "Random enum" { + try testRandomEnumValue(); + comptime try testRandomEnumValue(); +} +fn testRandomEnumValue() !void { + const TestEnum = enum { + First, + Second, + Third, + }; + var rng = SequentialPrng.init(); + const random = rng.random(); + rng.next_value = 0; + try expect(random.enumValue(TestEnum) == TestEnum.First); + try expect(random.enumValue(TestEnum) == TestEnum.First); + try expect(random.enumValue(TestEnum) == TestEnum.First); +} + +test "Random intLessThan" { + @setEvalBranchQuota(10000); + try testRandomIntLessThan(); + comptime try testRandomIntLessThan(); +} +fn testRandomIntLessThan() !void { + var rng = SequentialPrng.init(); + const random = rng.random(); + + rng.next_value = 0xff; + try expect(random.uintLessThan(u8, 4) == 3); + try expect(rng.next_value == 0); + try expect(random.uintLessThan(u8, 4) == 0); + try expect(rng.next_value == 1); + + rng.next_value = 0; + try expect(random.uintLessThan(u64, 32) == 0); + + // trigger the bias rejection code path + rng.next_value = 0; + try expect(random.uintLessThan(u8, 3) == 0); + // verify we incremented twice + try expect(rng.next_value == 2); + + rng.next_value = 0xff; + try expect(random.intRangeLessThan(u8, 0, 0x80) == 0x7f); + rng.next_value = 0xff; + try expect(random.intRangeLessThan(u8, 0x7f, 0xff) == 0xfe); + + rng.next_value = 0xff; + try expect(random.intRangeLessThan(i8, 0, 0x40) == 0x3f); + rng.next_value = 0xff; + try expect(random.intRangeLessThan(i8, -0x40, 0x40) == 0x3f); + rng.next_value = 0xff; + try expect(random.intRangeLessThan(i8, -0x80, 0) == -1); + + rng.next_value = 0xff; + try expect(random.intRangeLessThan(i3, -4, 0) == -1); + rng.next_value = 0xff; + try expect(random.intRangeLessThan(i3, -2, 2) == 1); +} + +test "Random intAtMost" { + @setEvalBranchQuota(10000); + try testRandomIntAtMost(); + comptime try testRandomIntAtMost(); +} +fn testRandomIntAtMost() !void { + var rng = SequentialPrng.init(); + const random = rng.random(); + + rng.next_value = 0xff; + try expect(random.uintAtMost(u8, 3) == 3); + try expect(rng.next_value == 0); + try expect(random.uintAtMost(u8, 3) == 0); + + // trigger the bias rejection code path + rng.next_value = 0; + try expect(random.uintAtMost(u8, 2) == 0); + // verify we incremented twice + try expect(rng.next_value == 2); + + rng.next_value = 0xff; + try expect(random.intRangeAtMost(u8, 0, 0x7f) == 0x7f); + rng.next_value = 0xff; + try expect(random.intRangeAtMost(u8, 0x7f, 0xfe) == 0xfe); + + rng.next_value = 0xff; + try expect(random.intRangeAtMost(i8, 0, 0x3f) == 0x3f); + rng.next_value = 0xff; + try expect(random.intRangeAtMost(i8, -0x40, 0x3f) == 0x3f); + rng.next_value = 0xff; + try expect(random.intRangeAtMost(i8, -0x80, -1) == -1); + + rng.next_value = 0xff; + try expect(random.intRangeAtMost(i3, -4, -1) == -1); + rng.next_value = 0xff; + try expect(random.intRangeAtMost(i3, -2, 1) == 1); + + try expect(random.uintAtMost(u0, 0) == 0); +} + +test "Random Biased" { + var prng = DefaultPrng.init(0); + const random = prng.random(); + // Not thoroughly checking the logic here. + // Just want to execute all the paths with different types. + + try expect(random.uintLessThanBiased(u1, 1) == 0); + try expect(random.uintLessThanBiased(u32, 10) < 10); + try expect(random.uintLessThanBiased(u64, 20) < 20); + + try expect(random.uintAtMostBiased(u0, 0) == 0); + try expect(random.uintAtMostBiased(u1, 0) <= 0); + try expect(random.uintAtMostBiased(u32, 10) <= 10); + try expect(random.uintAtMostBiased(u64, 20) <= 20); + + try expect(random.intRangeLessThanBiased(u1, 0, 1) == 0); + try expect(random.intRangeLessThanBiased(i1, -1, 0) == -1); + try expect(random.intRangeLessThanBiased(u32, 10, 20) >= 10); + try expect(random.intRangeLessThanBiased(i32, 10, 20) >= 10); + try expect(random.intRangeLessThanBiased(u64, 20, 40) >= 20); + try expect(random.intRangeLessThanBiased(i64, 20, 40) >= 20); + + // uncomment for broken module error: + //expect(random.intRangeAtMostBiased(u0, 0, 0) == 0); + try expect(random.intRangeAtMostBiased(u1, 0, 1) >= 0); + try expect(random.intRangeAtMostBiased(i1, -1, 0) >= -1); + try expect(random.intRangeAtMostBiased(u32, 10, 20) >= 10); + try expect(random.intRangeAtMostBiased(i32, 10, 20) >= 10); + try expect(random.intRangeAtMostBiased(u64, 20, 40) >= 20); + try expect(random.intRangeAtMostBiased(i64, 20, 40) >= 20); +} + +test "splitmix64 sequence" { + var r = SplitMix64.init(0xaeecf86f7878dd75); + + const seq = [_]u64{ + 0x5dbd39db0178eb44, + 0xa9900fb66b397da3, + 0x5c1a28b1aeebcf5c, + 0x64a963238f776912, + 0xc6d4177b21d1c0ab, + 0xb2cbdbdb5ea35394, + }; + + for (seq) |s| { + try expect(s == r.next()); + } +} + +// Actual Random helper function tests, pcg engine is assumed correct. +test "Random float correctness" { + var prng = DefaultPrng.init(0); + const random = prng.random(); + + var i: usize = 0; + while (i < 1000) : (i += 1) { + const val1 = random.float(f32); + try expect(val1 >= 0.0); + try expect(val1 < 1.0); + + const val2 = random.float(f64); + try expect(val2 >= 0.0); + try expect(val2 < 1.0); + } +} + +// Check the "astronomically unlikely" code paths. +test "Random float coverage" { + var prng = try Dilbert.init(&[_]u8{0}); + const random = prng.random(); + + const rand_f64 = random.float(f64); + const rand_f32 = random.float(f32); + + try expect(rand_f32 == 0.0); + try expect(rand_f64 == 0.0); +} + +test "Random float chi-square goodness of fit" { + const num_numbers = 100000; + const num_buckets = 1000; + + var f32_hist = std.AutoHashMap(u32, u32).init(std.testing.allocator); + defer f32_hist.deinit(); + var f64_hist = std.AutoHashMap(u64, u32).init(std.testing.allocator); + defer f64_hist.deinit(); + + var prng = DefaultPrng.init(0); + const random = prng.random(); + + var i: usize = 0; + while (i < num_numbers) : (i += 1) { + const rand_f32 = random.float(f32); + const rand_f64 = random.float(f64); + var f32_put = try f32_hist.getOrPut(@floatToInt(u32, rand_f32 * @intToFloat(f32, num_buckets))); + if (f32_put.found_existing) { + f32_put.value_ptr.* += 1; + } else { + f32_put.value_ptr.* = 0; + } + var f64_put = try f64_hist.getOrPut(@floatToInt(u32, rand_f64 * @intToFloat(f64, num_buckets))); + if (f64_put.found_existing) { + f64_put.value_ptr.* += 1; + } else { + f64_put.value_ptr.* = 0; + } + } + + var f32_total_variance: f64 = 0; + var f64_total_variance: f64 = 0; + + { + var j: u32 = 0; + while (j < num_buckets) : (j += 1) { + const count = @intToFloat(f64, (if (f32_hist.get(j)) |v| v else 0)); + const expected = @intToFloat(f64, num_numbers) / @intToFloat(f64, num_buckets); + const delta = count - expected; + const variance = (delta * delta) / expected; + f32_total_variance += variance; + } + } + + { + var j: u64 = 0; + while (j < num_buckets) : (j += 1) { + const count = @intToFloat(f64, (if (f64_hist.get(j)) |v| v else 0)); + const expected = @intToFloat(f64, num_numbers) / @intToFloat(f64, num_buckets); + const delta = count - expected; + const variance = (delta * delta) / expected; + f64_total_variance += variance; + } + } + + // Corresponds to a p-value > 0.05. + // Critical value is calculated by opening a Python interpreter and running: + // scipy.stats.chi2.isf(0.05, num_buckets - 1) + const critical_value = 1073.6426506574246; + try expect(f32_total_variance < critical_value); + try expect(f64_total_variance < critical_value); +} + +test "Random shuffle" { + var prng = DefaultPrng.init(0); + const random = prng.random(); + + var seq = [_]u8{ 0, 1, 2, 3, 4 }; + var seen = [_]bool{false} ** 5; + + var i: usize = 0; + while (i < 1000) : (i += 1) { + random.shuffle(u8, seq[0..]); + seen[seq[0]] = true; + try expect(sumArray(seq[0..]) == 10); + } + + // we should see every entry at the head at least once + for (seen) |e| { + try expect(e == true); + } +} + +fn sumArray(s: []const u8) u32 { + var r: u32 = 0; + for (s) |e| + r += e; + return r; +} + +test "Random range" { + var prng = DefaultPrng.init(0); + const random = prng.random(); + + try testRange(random, -4, 3); + try testRange(random, -4, -1); + try testRange(random, 10, 14); + try testRange(random, -0x80, 0x7f); +} + +fn testRange(r: Random, start: i8, end: i8) !void { + try testRangeBias(r, start, end, true); + try testRangeBias(r, start, end, false); +} +fn testRangeBias(r: Random, start: i8, end: i8, biased: bool) !void { + const count = @intCast(usize, @as(i32, end) - @as(i32, start)); + var values_buffer = [_]bool{false} ** 0x100; + const values = values_buffer[0..count]; + var i: usize = 0; + while (i < count) { + const value: i32 = if (biased) r.intRangeLessThanBiased(i8, start, end) else r.intRangeLessThan(i8, start, end); + const index = @intCast(usize, value - start); + if (!values[index]) { + i += 1; + values[index] = true; + } + } +} + +test "CSPRNG" { + var secret_seed: [DefaultCsprng.secret_seed_length]u8 = undefined; + std.crypto.random.bytes(&secret_seed); + var csprng = DefaultCsprng.init(secret_seed); + const random = csprng.random(); + const a = random.int(u64); + const b = random.int(u64); + const c = random.int(u64); + try expect(a ^ b ^ c != 0); +} From b33c8b0b06dd6a24d89efb278eeb833db93b2f9b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 10 May 2022 21:46:24 -0700 Subject: [PATCH 1454/2031] Sema: comptime float negation supports negative zero When handling the `negate` ZIR instruction, Zig now checks for a comptime operand and handles it as a special case rather than lowering it as `0 - x` so that the expression `-x` where `x` is a floating point value known at compile-time, will get the negative zero bitwise representation. --- src/Sema.zig | 44 +++++++++++++++++++++++++++++------------- src/value.zig | 32 ++++++++++++++++++++++++++++++ test/behavior/math.zig | 29 ++++++++++++++++++++-------- 3 files changed, 84 insertions(+), 21 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index fbf25be288..2cc9b82410 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -758,8 +758,8 @@ fn analyzeBodyInner( .is_non_null => try sema.zirIsNonNull(block, inst), .is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst), .merge_error_sets => try sema.zirMergeErrorSets(block, inst), - .negate => try sema.zirNegate(block, inst, .sub), - .negate_wrap => try sema.zirNegate(block, inst, .subwrap), + .negate => try sema.zirNegate(block, inst), + .negate_wrap => try sema.zirNegateWrap(block, inst), .optional_payload_safe => try sema.zirOptionalPayload(block, inst, true), .optional_payload_safe_ptr => try sema.zirOptionalPayloadPtr(block, inst, true), .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false), @@ -9328,15 +9328,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return sema.fail(block, lhs_src, "TODO runtime array_mul", .{}); } -fn zirNegate( - sema: *Sema, - block: *Block, - inst: Zir.Inst.Index, - tag_override: Zir.Inst.Tag, -) CompileError!Air.Inst.Ref { - const tracy = trace(@src()); - defer tracy.end(); - +fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const lhs_src = src; @@ -9346,16 +9338,42 @@ fn zirNegate( const rhs_ty = sema.typeOf(rhs); const rhs_scalar_ty = rhs_ty.scalarType(); - if (tag_override == .sub and rhs_scalar_ty.isUnsignedInt()) { + if (rhs_scalar_ty.isUnsignedInt()) { return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(sema.mod)}); } + if (rhs_scalar_ty.isAnyFloat()) { + // We handle comptime negation here to ensure negative zero is represented in the bits. + if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { + if (rhs_val.isUndef()) return sema.addConstUndef(rhs_ty); + const target = sema.mod.getTarget(); + return sema.addConstant(rhs_ty, try rhs_val.floatNeg(rhs_ty, sema.arena, target)); + } + } + const lhs = if (rhs_ty.zigTypeTag() == .Vector) try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, Value.zero)) else sema.resolveInst(.zero); - return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src); + return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src); +} + +fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const inst_data = sema.code.instructions.items(.data)[inst].un_node; + const src = inst_data.src(); + const lhs_src = src; + const rhs_src = src; // TODO better source location + + const rhs = sema.resolveInst(inst_data.operand); + const rhs_ty = sema.typeOf(rhs); + + const lhs = if (rhs_ty.zigTypeTag() == .Vector) + try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, Value.zero)) + else + sema.resolveInst(.zero); + + return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src); } fn zirArithmetic( diff --git a/src/value.zig b/src/value.zig index e8ccada8ed..1e70ad0c54 100644 --- a/src/value.zig +++ b/src/value.zig @@ -4155,6 +4155,38 @@ pub const Value = extern union { } } + pub fn floatNeg( + val: Value, + float_type: Type, + arena: Allocator, + target: Target, + ) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatNegScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatNegScalar(val, float_type, arena, target); + } + + pub fn floatNegScalar( + val: Value, + float_type: Type, + arena: Allocator, + target: Target, + ) !Value { + switch (float_type.floatBits(target)) { + 16 => return Value.Tag.float_16.create(arena, -val.toFloat(f16)), + 32 => return Value.Tag.float_32.create(arena, -val.toFloat(f32)), + 64 => return Value.Tag.float_64.create(arena, -val.toFloat(f64)), + 80 => return Value.Tag.float_80.create(arena, -val.toFloat(f80)), + 128 => return Value.Tag.float_128.create(arena, -val.toFloat(f128)), + else => unreachable, + } + } + pub fn floatDiv( lhs: Value, rhs: Value, diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 42f2635afd..eb07ffd7a5 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1593,17 +1593,30 @@ test "compare undefined literal with comptime_int" { } test "signed zeros are represented properly" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { - inline for ([_]type{ f16, f32, f64, f128 }) |T| { - const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); - var as_fp_val = -@as(T, 0.0); - var as_uint_val = @bitCast(ST, as_fp_val); - // Ensure the sign bit is set. - try expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1); - } + try testOne(f16); + try testOne(f32); + try testOne(f64); + // TODO enable this + //try testOne(f80); + try testOne(f128); + // TODO enable this + //try testOne(c_longdouble); + } + + fn testOne(comptime T: type) !void { + const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); + var as_fp_val = -@as(T, 0.0); + var as_uint_val = @bitCast(ST, as_fp_val); + // Ensure the sign bit is set. + try expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1); } }; From bd32a0f3db2d03749e7aef22cbc2aa6f85b689d1 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Wed, 11 May 2022 03:34:44 -0700 Subject: [PATCH 1455/2031] Sema: add "declared here" note to `zirErrSetCast` --- src/Sema.zig | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 2cc9b82410..d12b85e097 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14215,12 +14215,18 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat if (!dest_ty.isAnyError()) { const error_name = val.castTag(.@"error").?.data.name; if (!dest_ty.errorSetHasField(error_name)) { - return sema.fail( - block, - src, - "error.{s} not a member of error set '{}'", - .{ error_name, dest_ty.fmt(sema.mod) }, - ); + const msg = msg: { + const msg = try sema.errMsg( + block, + src, + "error.{s} not a member of error set '{}'", + .{ error_name, dest_ty.fmt(sema.mod) }, + ); + errdefer msg.destroy(sema.gpa); + try sema.addDeclaredHereNote(msg, dest_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); } } From 363cdb7d5fc376dfb6fc0f67d983922fc4208aa1 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Wed, 11 May 2022 03:36:58 -0700 Subject: [PATCH 1456/2031] Sema: add error for disjoint error sets in `zirErrSetCast` --- src/Sema.zig | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index d12b85e097..5f584fe40c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14209,9 +14209,46 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat try sema.checkErrorSetType(block, dest_ty_src, dest_ty); try sema.checkErrorSetType(block, operand_src, operand_ty); - if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { - try sema.resolveInferredErrorSetTy(block, src, dest_ty); + // operand must be defined since it can be an invalid error value + const maybe_operand_val = try sema.resolveDefinedValue(block, operand_src, operand); + if (disjoint: { + // Try avoiding resolving inferred error sets if we can + if (!dest_ty.isAnyError() and dest_ty.errorSetNames().len == 0) break :disjoint true; + if (!operand_ty.isAnyError() and operand_ty.errorSetNames().len == 0) break :disjoint true; + if (dest_ty.isAnyError()) break :disjoint false; + if (operand_ty.isAnyError()) break :disjoint false; + for (dest_ty.errorSetNames()) |dest_err_name| + if (operand_ty.errorSetHasField(dest_err_name)) + break :disjoint false; + + if (dest_ty.tag() != .error_set_inferred and operand_ty.tag() != .error_set_inferred) + break :disjoint true; + + try sema.resolveInferredErrorSetTy(block, dest_ty_src, dest_ty); + try sema.resolveInferredErrorSetTy(block, operand_src, operand_ty); + for (dest_ty.errorSetNames()) |dest_err_name| + if (operand_ty.errorSetHasField(dest_err_name)) + break :disjoint false; + + break :disjoint true; + }) { + const msg = msg: { + const msg = try sema.errMsg( + block, + src, + "error sets '{}' and '{}' have no common errors", + .{ operand_ty.fmt(sema.mod), dest_ty.fmt(sema.mod) }, + ); + errdefer msg.destroy(sema.gpa); + try sema.addDeclaredHereNote(msg, operand_ty); + try sema.addDeclaredHereNote(msg, dest_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + + if (maybe_operand_val) |val| { if (!dest_ty.isAnyError()) { const error_name = val.castTag(.@"error").?.data.name; if (!dest_ty.errorSetHasField(error_name)) { From 4a6aee9dbe41f640b5244536f4bad9a1ab04eb60 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Wed, 11 May 2022 03:42:32 -0700 Subject: [PATCH 1457/2031] Sema: implement runtime safety checks for `zirErrSetCast` --- src/Sema.zig | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 5f584fe40c..157fc1ea0e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14271,10 +14271,18 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat } try sema.requireRuntimeBlock(block, src); - if (block.wantSafety()) { - // TODO + if (block.wantSafety() and !dest_ty.isAnyError()) { + const err_int_inst = try block.addBitCast(Type.u16, operand); + // TODO: Output a switch instead of chained OR's. + var found_match: Air.Inst.Ref = undefined; + for (dest_ty.errorSetNames()) |dest_err_name, i| { + const dest_err_int = (try sema.mod.getErrorValue(dest_err_name)).value; + const dest_err_int_inst = try sema.addIntUnsigned(Type.u16, dest_err_int); + const next_match = try block.addBinOp(.cmp_eq, dest_err_int_inst, err_int_inst); + found_match = if (i == 0) next_match else try block.addBinOp(.bool_or, found_match, next_match); + } + try sema.addSafetyCheck(block, found_match, .invalid_error_code); } - return block.addBitCast(dest_ty, operand); } From 77d732a85f863d5bdafb060151429b18459a2021 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Wed, 11 May 2022 03:44:58 -0700 Subject: [PATCH 1458/2031] aarch64,arm: disable broken `@errSetCast` test --- test/behavior/error.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 3c19471705..dc86967175 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -202,6 +202,8 @@ fn testErrorSetType() !void { test "explicit error set cast" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testExplicitErrorSetCast(Set1.A); comptime try testExplicitErrorSetCast(Set1.A); From 3a64693db30d0a9d72dcc34d3ae053f25927f5ec Mon Sep 17 00:00:00 2001 From: Meghan Date: Wed, 11 May 2022 12:43:18 -0700 Subject: [PATCH 1459/2031] std: add http definitions for Method and Status (#10661) --- lib/std/http.zig | 8 ++ lib/std/http/method.zig | 65 ++++++++++++++ lib/std/http/status.zig | 182 ++++++++++++++++++++++++++++++++++++++++ lib/std/std.zig | 1 + 4 files changed, 256 insertions(+) create mode 100644 lib/std/http.zig create mode 100644 lib/std/http/method.zig create mode 100644 lib/std/http/status.zig diff --git a/lib/std/http.zig b/lib/std/http.zig new file mode 100644 index 0000000000..8da6968403 --- /dev/null +++ b/lib/std/http.zig @@ -0,0 +1,8 @@ +const std = @import("std.zig"); + +pub const Method = @import("http/method.zig").Method; +pub const Status = @import("http/status.zig").Status; + +test { + std.testing.refAllDecls(@This()); +} diff --git a/lib/std/http/method.zig b/lib/std/http/method.zig new file mode 100644 index 0000000000..c118ca9a47 --- /dev/null +++ b/lib/std/http/method.zig @@ -0,0 +1,65 @@ +//! HTTP Methods +//! https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods + +// Style guide is violated here so that @tagName can be used effectively +/// https://datatracker.ietf.org/doc/html/rfc7231#section-4 Initial definiton +/// https://datatracker.ietf.org/doc/html/rfc5789#section-2 PATCH +pub const Method = enum { + GET, + HEAD, + POST, + PUT, + DELETE, + CONNECT, + OPTIONS, + TRACE, + PATCH, + + /// Returns true if a request of this method is allowed to have a body + /// Actual behavior from servers may vary and should still be checked + pub fn requestHasBody(self: Method) bool { + return switch (self) { + .POST, .PUT, .PATCH => true, + .GET, .HEAD, .DELETE, .CONNECT, .OPTIONS, .TRACE => false, + }; + } + + /// Returns true if a response to this method is allowed to have a body + /// Actual behavior from clients may vary and should still be checked + pub fn responseHasBody(self: Method) bool { + return switch (self) { + .GET, .POST, .DELETE, .CONNECT, .OPTIONS, .PATCH => true, + .HEAD, .PUT, .TRACE => false, + }; + } + + /// An HTTP method is safe if it doesn't alter the state of the server. + /// https://developer.mozilla.org/en-US/docs/Glossary/Safe/HTTP + /// https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.1 + pub fn safe(self: Method) bool { + return switch (self) { + .GET, .HEAD, .OPTIONS, .TRACE => true, + .POST, .PUT, .DELETE, .CONNECT, .PATCH => false, + }; + } + + /// An HTTP method is idempotent if an identical request can be made once or several times in a row with the same effect while leaving the server in the same state. + /// https://developer.mozilla.org/en-US/docs/Glossary/Idempotent + /// https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.2 + pub fn idempotent(self: Method) bool { + return switch (self) { + .GET, .HEAD, .PUT, .DELETE, .OPTIONS, .TRACE => true, + .CONNECT, .POST, .PATCH => false, + }; + } + + /// A cacheable response is an HTTP response that can be cached, that is stored to be retrieved and used later, saving a new request to the server. + /// https://developer.mozilla.org/en-US/docs/Glossary/cacheable + /// https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.3 + pub fn cacheable(self: Method) bool { + return switch (self) { + .GET, .HEAD => true, + .POST, .PUT, .DELETE, .CONNECT, .OPTIONS, .TRACE, .PATCH => false, + }; + } +}; diff --git a/lib/std/http/status.zig b/lib/std/http/status.zig new file mode 100644 index 0000000000..bf96b71c0a --- /dev/null +++ b/lib/std/http/status.zig @@ -0,0 +1,182 @@ +//! HTTP Status +//! https://developer.mozilla.org/en-US/docs/Web/HTTP/Status + +const std = @import("../std.zig"); + +pub const Status = enum(u10) { + @"continue" = 100, // RFC7231, Section 6.2.1 + switching_protcols = 101, // RFC7231, Section 6.2.2 + processing = 102, // RFC2518 + early_hints = 103, // RFC8297 + + ok = 200, // RFC7231, Section 6.3.1 + created = 201, // RFC7231, Section 6.3.2 + accepted = 202, // RFC7231, Section 6.3.3 + non_authoritative_info = 203, // RFC7231, Section 6.3.4 + no_content = 204, // RFC7231, Section 6.3.5 + reset_content = 205, // RFC7231, Section 6.3.6 + partial_content = 206, // RFC7233, Section 4.1 + multi_status = 207, // RFC4918 + already_reported = 208, // RFC5842 + im_used = 226, // RFC3229 + + multiple_choice = 300, // RFC7231, Section 6.4.1 + moved_permanently = 301, // RFC7231, Section 6.4.2 + found = 302, // RFC7231, Section 6.4.3 + see_other = 303, // RFC7231, Section 6.4.4 + not_modified = 304, // RFC7232, Section 4.1 + use_proxy = 305, // RFC7231, Section 6.4.5 + temporary_redirect = 307, // RFC7231, Section 6.4.7 + permanent_redirect = 308, // RFC7538 + + bad_request = 400, // RFC7231, Section 6.5.1 + unauthorized = 401, // RFC7235, Section 3.1 + payment_required = 402, // RFC7231, Section 6.5.2 + forbidden = 403, // RFC7231, Section 6.5.3 + not_found = 404, // RFC7231, Section 6.5.4 + method_not_allowed = 405, // RFC7231, Section 6.5.5 + not_acceptable = 406, // RFC7231, Section 6.5.6 + proxy_auth_required = 407, // RFC7235, Section 3.2 + request_timeout = 408, // RFC7231, Section 6.5.7 + conflict = 409, // RFC7231, Section 6.5.8 + gone = 410, // RFC7231, Section 6.5.9 + length_required = 411, // RFC7231, Section 6.5.10 + precondition_failed = 412, // RFC7232, Section 4.2][RFC8144, Section 3.2 + payload_too_large = 413, // RFC7231, Section 6.5.11 + uri_too_long = 414, // RFC7231, Section 6.5.12 + unsupported_media_type = 415, // RFC7231, Section 6.5.13][RFC7694, Section 3 + range_not_satisfiable = 416, // RFC7233, Section 4.4 + expectation_failed = 417, // RFC7231, Section 6.5.14 + teapot = 418, // RFC 7168, 2.3.3 + misdirected_request = 421, // RFC7540, Section 9.1.2 + unprocessable_entity = 422, // RFC4918 + locked = 423, // RFC4918 + failed_dependency = 424, // RFC4918 + too_early = 425, // RFC8470 + upgrade_required = 426, // RFC7231, Section 6.5.15 + precondition_required = 428, // RFC6585 + too_many_requests = 429, // RFC6585 + header_fields_too_large = 431, // RFC6585 + unavailable_for_legal_reasons = 451, // RFC7725 + + internal_server_error = 500, // RFC7231, Section 6.6.1 + not_implemented = 501, // RFC7231, Section 6.6.2 + bad_gateway = 502, // RFC7231, Section 6.6.3 + service_unavailable = 503, // RFC7231, Section 6.6.4 + gateway_timeout = 504, // RFC7231, Section 6.6.5 + http_version_not_supported = 505, // RFC7231, Section 6.6.6 + variant_also_negotiates = 506, // RFC2295 + insufficient_storage = 507, // RFC4918 + loop_detected = 508, // RFC5842 + not_extended = 510, // RFC2774 + network_authentication_required = 511, // RFC6585 + + _, + + pub fn phrase(self: Status) ?[]const u8 { + return switch (self) { + // 1xx statuses + .@"continue" => "Continue", + .switching_protcols => "Switching Protocols", + .processing => "Processing", + .early_hints => "Early Hints", + + // 2xx statuses + .ok => "OK", + .created => "Created", + .accepted => "Accepted", + .non_authoritative_info => "Non-Authoritative Information", + .no_content => "No Content", + .reset_content => "Reset Content", + .partial_content => "Partial Content", + .multi_status => "Multi-Status", + .already_reported => "Already Reported", + .im_used => "IM Used", + + // 3xx statuses + .multiple_choice => "Multiple Choice", + .moved_permanently => "Moved Permanently", + .found => "Found", + .see_other => "See Other", + .not_modified => "Not Modified", + .use_proxy => "Use Proxy", + .temporary_redirect => "Temporary Redirect", + .permanent_redirect => "Permanent Redirect", + + // 4xx statuses + .bad_request => "Bad Request", + .unauthorized => "Unauthorized", + .payment_required => "Payment Required", + .forbidden => "Forbidden", + .not_found => "Not Found", + .method_not_allowed => "Method Not Allowed", + .not_acceptable => "Not Acceptable", + .proxy_auth_required => "Proxy Authentication Required", + .request_timeout => "Request Timeout", + .conflict => "Conflict", + .gone => "Gone", + .length_required => "Length Required", + .precondition_failed => "Precondition Failed", + .payload_too_large => "Payload Too Large", + .uri_too_long => "URI Too Long", + .unsupported_media_type => "Unsupported Media Type", + .range_not_satisfiable => "Range Not Satisfiable", + .expectation_failed => "Expectation Failed", + .teapot => "I'm a teapot", + .misdirected_request => "Misdirected Request", + .unprocessable_entity => "Unprocessable Entity", + .locked => "Locked", + .failed_dependency => "Failed Dependency", + .too_early => "Too Early", + .upgrade_required => "Upgrade Required", + .precondition_required => "Precondition Required", + .too_many_requests => "Too Many Requests", + .header_fields_too_large => "Request Header Fields Too Large", + .unavailable_for_legal_reasons => "Unavailable For Legal Reasons", + + // 5xx statuses + .internal_server_error => "Internal Server Error", + .not_implemented => "Not Implemented", + .bad_gateway => "Bad Gateway", + .service_unavailable => "Service Unavailable", + .gateway_timeout => "Gateway Timeout", + .http_version_not_supported => "HTTP Version Not Supported", + .variant_also_negotiates => "Variant Also Negotiates", + .insufficient_storage => "Insufficient Storage", + .loop_detected => "Loop Detected", + .not_extended => "Not Extended", + .network_authentication_required => "Network Authentication Required", + + else => return null, + }; + } + + pub const Class = enum { + informational, + success, + redirect, + client_error, + server_error, + }; + + pub fn class(self: Status) ?Class { + return switch (@enumToInt(self)) { + 100...199 => .informational, + 200...299 => .success, + 300...399 => .redirect, + 400...499 => .client_error, + 500...599 => .server_error, + else => null, + }; + } +}; + +test { + try std.testing.expectEqualStrings("OK", Status.ok.phrase().?); + try std.testing.expectEqualStrings("Not Found", Status.not_found.phrase().?); +} + +test { + try std.testing.expectEqual(@as(?Status.Class, Status.Class.success), Status.ok.class()); + try std.testing.expectEqual(@as(?Status.Class, Status.Class.client_error), Status.not_found.class()); +} diff --git a/lib/std/std.zig b/lib/std/std.zig index 32e16b40c2..95bdd4c043 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -65,6 +65,7 @@ pub const fs = @import("fs.zig"); pub const hash = @import("hash.zig"); pub const hash_map = @import("hash_map.zig"); pub const heap = @import("heap.zig"); +pub const http = @import("http.zig"); pub const io = @import("io.zig"); pub const json = @import("json.zig"); pub const leb = @import("leb128.zig"); From d1f1f5124f8edb9245394642d89f80c5118a6833 Mon Sep 17 00:00:00 2001 From: Francesco Alemanno <50984334+francescoalemanno@users.noreply.github.com> Date: Tue, 1 Feb 2022 09:54:02 +0100 Subject: [PATCH 1460/2031] add std.rand.RomuTrio --- lib/std/rand.zig | 1 + lib/std/rand/RomuTrio.zig | 132 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 lib/std/rand/RomuTrio.zig diff --git a/lib/std/rand.zig b/lib/std/rand.zig index 01397085f7..826ffe6d4a 100644 --- a/lib/std/rand.zig +++ b/lib/std/rand.zig @@ -26,6 +26,7 @@ pub const Pcg = @import("rand/Pcg.zig"); pub const Xoroshiro128 = @import("rand/Xoroshiro128.zig"); pub const Xoshiro256 = @import("rand/Xoshiro256.zig"); pub const Sfc64 = @import("rand/Sfc64.zig"); +pub const RomuTrio = @import("rand/RomuTrio.zig"); pub const Random = struct { ptr: *anyopaque, diff --git a/lib/std/rand/RomuTrio.zig b/lib/std/rand/RomuTrio.zig new file mode 100644 index 0000000000..fe23be054f --- /dev/null +++ b/lib/std/rand/RomuTrio.zig @@ -0,0 +1,132 @@ +// Website: romu-random.org +// Reference paper: http://arxiv.org/abs/2002.11331 +// Beware: this PRNG is trivially predictable. While fast, it should *never* be used for cryptographic purposes. + +const std = @import("std"); +const Random = std.rand.Random; +const math = std.math; +const RomuTrio = @This(); + +x_state: u64, +y_state: u64, +z_state: u64, // set to nonzero seed + +pub fn init(init_s: u64) RomuTrio { + var x = RomuTrio{ .x_state = undefined, .y_state = undefined, .z_state = undefined }; + x.seed(init_s); + return x; +} + +pub fn random(self: *RomuTrio) Random { + return Random.init(self, fill); +} + +fn next(self: *RomuTrio) u64 { + const xp = self.x_state; + const yp = self.y_state; + const zp = self.z_state; + self.x_state = 15241094284759029579 *% zp; + self.y_state = yp -% xp; + self.y_state = std.math.rotl(u64, self.y_state, 12); + self.z_state = zp -% yp; + self.z_state = std.math.rotl(u64, self.z_state, 44); + return xp; +} + +pub fn seedWithBuf(self: *RomuTrio, buf: [24]u8) void { + const seed_buf = @bitCast([3]u64, buf); + self.x_state = seed_buf[0]; + self.y_state = seed_buf[1]; + self.z_state = seed_buf[2]; +} + +pub fn seed(self: *RomuTrio, init_s: u64) void { + // RomuTrio requires 192-bits of seed. + var gen = std.rand.SplitMix64.init(init_s); + + self.x_state = gen.next(); + self.y_state = gen.next(); + self.z_state = gen.next(); +} + +pub fn fill(self: *RomuTrio, buf: []u8) void { + var i: usize = 0; + const aligned_len = buf.len - (buf.len & 7); + + // Complete 8 byte segments. + while (i < aligned_len) : (i += 8) { + var n = self.next(); + comptime var j: usize = 0; + inline while (j < 8) : (j += 1) { + buf[i + j] = @truncate(u8, n); + n >>= 8; + } + } + + // Remaining. (cuts the stream) + if (i != buf.len) { + var n = self.next(); + while (i < buf.len) : (i += 1) { + buf[i] = @truncate(u8, n); + n >>= 8; + } + } +} + +test "RomuTrio sequence" { + // Unfortunately there does not seem to be an official test sequence. + var r = RomuTrio.init(0); + + const seq = [_]u64{ + 16294208416658607535, + 13964609475759908645, + 4703697494102998476, + 3425221541186733346, + 2285772463536419399, + 9454187757529463048, + 13695907680080547496, + 8328236714879408626, + 12323357569716880909, + 12375466223337721820, + }; + + for (seq) |s| { + try std.testing.expectEqual(s, r.next()); + } +} + +test "RomuTrio fill" { + // Unfortunately there does not seem to be an official test sequence. + var r = RomuTrio.init(0); + + const seq = [_]u64{ + 16294208416658607535, + 13964609475759908645, + 4703697494102998476, + 3425221541186733346, + 2285772463536419399, + 9454187757529463048, + 13695907680080547496, + 8328236714879408626, + 12323357569716880909, + 12375466223337721820, + }; + + for (seq) |s| { + var buf0: [8]u8 = undefined; + var buf1: [7]u8 = undefined; + std.mem.writeIntLittle(u64, &buf0, s); + r.fill(&buf1); + try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); + } +} + +test "RomuTrio buf seeding test" { + const buf0 = [24]u8{ 175, 205, 29, 123, 57, 168, 32, 226, 37, 39, 185, 157, 84, 66, 204, 193, 204, 125, 231, 26, 243, 227, 70, 65 }; + const resulting_state = .{ .x = 16294208416658607535, .y = 13964609475759908645, .z = 4703697494102998476 }; + var r = RomuTrio.init(0); + r.seedWithBuf(buf0); + try std.testing.expect(r.x_state == resulting_state.x); + try std.testing.expect(r.y_state == resulting_state.y); + try std.testing.expect(r.z_state == resulting_state.z); +} From d383b940c2e9c9d0f2e8ef7607b38b7a74021b47 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 11 May 2022 13:50:14 -0700 Subject: [PATCH 1461/2031] Revert "add std.rand.RomuTrio" This reverts commit d1f1f5124f8edb9245394642d89f80c5118a6833. The unit tests did not pass on `-target mips-linux`. --- lib/std/rand.zig | 1 - lib/std/rand/RomuTrio.zig | 132 -------------------------------------- 2 files changed, 133 deletions(-) delete mode 100644 lib/std/rand/RomuTrio.zig diff --git a/lib/std/rand.zig b/lib/std/rand.zig index 826ffe6d4a..01397085f7 100644 --- a/lib/std/rand.zig +++ b/lib/std/rand.zig @@ -26,7 +26,6 @@ pub const Pcg = @import("rand/Pcg.zig"); pub const Xoroshiro128 = @import("rand/Xoroshiro128.zig"); pub const Xoshiro256 = @import("rand/Xoshiro256.zig"); pub const Sfc64 = @import("rand/Sfc64.zig"); -pub const RomuTrio = @import("rand/RomuTrio.zig"); pub const Random = struct { ptr: *anyopaque, diff --git a/lib/std/rand/RomuTrio.zig b/lib/std/rand/RomuTrio.zig deleted file mode 100644 index fe23be054f..0000000000 --- a/lib/std/rand/RomuTrio.zig +++ /dev/null @@ -1,132 +0,0 @@ -// Website: romu-random.org -// Reference paper: http://arxiv.org/abs/2002.11331 -// Beware: this PRNG is trivially predictable. While fast, it should *never* be used for cryptographic purposes. - -const std = @import("std"); -const Random = std.rand.Random; -const math = std.math; -const RomuTrio = @This(); - -x_state: u64, -y_state: u64, -z_state: u64, // set to nonzero seed - -pub fn init(init_s: u64) RomuTrio { - var x = RomuTrio{ .x_state = undefined, .y_state = undefined, .z_state = undefined }; - x.seed(init_s); - return x; -} - -pub fn random(self: *RomuTrio) Random { - return Random.init(self, fill); -} - -fn next(self: *RomuTrio) u64 { - const xp = self.x_state; - const yp = self.y_state; - const zp = self.z_state; - self.x_state = 15241094284759029579 *% zp; - self.y_state = yp -% xp; - self.y_state = std.math.rotl(u64, self.y_state, 12); - self.z_state = zp -% yp; - self.z_state = std.math.rotl(u64, self.z_state, 44); - return xp; -} - -pub fn seedWithBuf(self: *RomuTrio, buf: [24]u8) void { - const seed_buf = @bitCast([3]u64, buf); - self.x_state = seed_buf[0]; - self.y_state = seed_buf[1]; - self.z_state = seed_buf[2]; -} - -pub fn seed(self: *RomuTrio, init_s: u64) void { - // RomuTrio requires 192-bits of seed. - var gen = std.rand.SplitMix64.init(init_s); - - self.x_state = gen.next(); - self.y_state = gen.next(); - self.z_state = gen.next(); -} - -pub fn fill(self: *RomuTrio, buf: []u8) void { - var i: usize = 0; - const aligned_len = buf.len - (buf.len & 7); - - // Complete 8 byte segments. - while (i < aligned_len) : (i += 8) { - var n = self.next(); - comptime var j: usize = 0; - inline while (j < 8) : (j += 1) { - buf[i + j] = @truncate(u8, n); - n >>= 8; - } - } - - // Remaining. (cuts the stream) - if (i != buf.len) { - var n = self.next(); - while (i < buf.len) : (i += 1) { - buf[i] = @truncate(u8, n); - n >>= 8; - } - } -} - -test "RomuTrio sequence" { - // Unfortunately there does not seem to be an official test sequence. - var r = RomuTrio.init(0); - - const seq = [_]u64{ - 16294208416658607535, - 13964609475759908645, - 4703697494102998476, - 3425221541186733346, - 2285772463536419399, - 9454187757529463048, - 13695907680080547496, - 8328236714879408626, - 12323357569716880909, - 12375466223337721820, - }; - - for (seq) |s| { - try std.testing.expectEqual(s, r.next()); - } -} - -test "RomuTrio fill" { - // Unfortunately there does not seem to be an official test sequence. - var r = RomuTrio.init(0); - - const seq = [_]u64{ - 16294208416658607535, - 13964609475759908645, - 4703697494102998476, - 3425221541186733346, - 2285772463536419399, - 9454187757529463048, - 13695907680080547496, - 8328236714879408626, - 12323357569716880909, - 12375466223337721820, - }; - - for (seq) |s| { - var buf0: [8]u8 = undefined; - var buf1: [7]u8 = undefined; - std.mem.writeIntLittle(u64, &buf0, s); - r.fill(&buf1); - try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); - } -} - -test "RomuTrio buf seeding test" { - const buf0 = [24]u8{ 175, 205, 29, 123, 57, 168, 32, 226, 37, 39, 185, 157, 84, 66, 204, 193, 204, 125, 231, 26, 243, 227, 70, 65 }; - const resulting_state = .{ .x = 16294208416658607535, .y = 13964609475759908645, .z = 4703697494102998476 }; - var r = RomuTrio.init(0); - r.seedWithBuf(buf0); - try std.testing.expect(r.x_state == resulting_state.x); - try std.testing.expect(r.y_state == resulting_state.y); - try std.testing.expect(r.z_state == resulting_state.z); -} From 15d5988e692c182892a118115fd7025048e06c29 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Sun, 16 Jan 2022 20:11:08 -0800 Subject: [PATCH 1462/2031] Add `process.EnvMap`, a platform-independent environment variable map EnvMap provides the same API as the previously used BufMap (besides `putMove` and `getPtr`), so usage sites of `getEnvMap` can usually remain unchanged. For non-Windows, EnvMap is a wrapper around BufMap. On Windows, it uses a new EnvMapWindows to handle some Windows-specific behavior: - Lookups use Unicode-aware case insensitivity (but `get` cannot return an error because EnvMapWindows has an internal buffer to use for lookup conversions) - Canonical names are returned when iterating the EnvMap Fixes #10561, closes #4603 --- lib/std/buf_map.zig | 2 +- lib/std/os/windows/ntdll.zig | 6 + lib/std/process.zig | 403 +++++++++++++++++++++++++++++++++-- lib/std/unicode.zig | 23 ++ 4 files changed, 421 insertions(+), 13 deletions(-) diff --git a/lib/std/buf_map.zig b/lib/std/buf_map.zig index 5b26ae9684..bd59d6da45 100644 --- a/lib/std/buf_map.zig +++ b/lib/std/buf_map.zig @@ -9,7 +9,7 @@ const testing = std.testing; pub const BufMap = struct { hash_map: BufMapHashMap, - const BufMapHashMap = StringHashMap([]const u8); + pub const BufMapHashMap = StringHashMap([]const u8); /// Create a BufMap backed by a specific allocator. /// That allocator will be used for both backing allocations diff --git a/lib/std/os/windows/ntdll.zig b/lib/std/os/windows/ntdll.zig index e3e590b094..2444d5f487 100644 --- a/lib/std/os/windows/ntdll.zig +++ b/lib/std/os/windows/ntdll.zig @@ -229,6 +229,12 @@ pub extern "ntdll" fn RtlEqualUnicodeString( CaseInSensitive: BOOLEAN, ) callconv(WINAPI) BOOLEAN; +pub extern "NtDll" fn RtlUpcaseUnicodeString( + DestinationString: *UNICODE_STRING, + SourceString: *const UNICODE_STRING, + AllocateDestinationString: BOOLEAN, +) callconv(WINAPI) NTSTATUS; + pub extern "ntdll" fn NtLockFile( FileHandle: HANDLE, Event: ?HANDLE, diff --git a/lib/std/process.zig b/lib/std/process.zig index c0f11b22ce..f5d14cf6da 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -2,7 +2,6 @@ const std = @import("std.zig"); const builtin = @import("builtin"); const os = std.os; const fs = std.fs; -const BufMap = std.BufMap; const mem = std.mem; const math = std.math; const Allocator = mem.Allocator; @@ -53,9 +52,385 @@ test "getCwdAlloc" { testing.allocator.free(cwd); } -/// Caller owns resulting `BufMap`. -pub fn getEnvMap(allocator: Allocator) !BufMap { - var result = BufMap.init(allocator); +/// EnvMap for Windows that handles Unicode-aware case insensitivity for lookups, while also +/// providing the canonical environment variable names when iterating. +/// +/// Allows for zero-allocation lookups (even though it needs to do UTF-8 -> UTF-16 -> uppercase +/// conversions) by allocating a buffer large enough to fit the largest environment variable +/// name, and using that when doing lookups (i.e. anything that overflows the buffer can be treated +/// as the environment variable not being found). +pub const EnvMapWindows = struct { + allocator: Allocator, + /// Keys are UTF-16le stored as []const u8 + uppercased_map: std.StringHashMapUnmanaged(EnvValue), + /// Buffer for converting to uppercased UTF-16 on key lookups + /// Must call `reallocUppercaseBuf` before doing any lookups after a `put` call. + uppercase_buf_utf16: []u16 = &[_]u16{}, + max_name_utf16_length: usize = 0, + + pub const EnvValue = struct { + value: []const u8, + canonical_name: []const u8, + }; + + const Self = @This(); + + /// Deinitialize with `deinit`. + pub fn init(allocator: Allocator) Self { + return .{ + .allocator = allocator, + .uppercased_map = std.StringHashMapUnmanaged(EnvValue){}, + }; + } + + pub fn deinit(self: *Self) void { + var it = self.uppercased_map.iterator(); + while (it.next()) |entry| { + self.allocator.free(entry.key_ptr.*); + self.allocator.free(entry.value_ptr.value); + self.allocator.free(entry.value_ptr.canonical_name); + } + self.uppercased_map.deinit(self.allocator); + self.allocator.free(self.uppercase_buf_utf16); + } + + /// Increases the size of the uppercase buffer if the maximum name size has increased. + /// Must be called before any `get` calls after any number of `put` calls. + pub fn reallocUppercaseBuf(self: *Self) !void { + if (self.max_name_utf16_length > self.uppercase_buf_utf16.len) { + self.uppercase_buf_utf16 = try self.allocator.realloc(self.uppercase_buf_utf16, self.max_name_utf16_length); + } + } + + /// Converts `src` to uppercase using `RtlUpcaseUnicodeString` and puts the result in `dest`. + /// Returns the length of the converted UTF-16 string. `dest.len` must be >= `src.len`. + /// + /// Note: As of now, RtlUpcaseUnicodeString does not seem to handle codepoints above 0x10000 + /// (i.e. those that require a surrogate pair), so this function will always return a length + /// equal to `src.len`. However, if RtlUpcaseUnicodeString is updated to handle codepoints above + /// 0x10000, this property would still hold unless there are lowercase <-> uppercase conversions + /// that cross over the boundary between codepoints >= 0x10000 and < 0x10000. + /// TODO: Is it feasible that Unicode lowercase <-> uppercase conversions could cross that boundary? + fn uppercaseName(dest: []u16, src: []const u16) u16 { + assert(dest.len >= src.len); + + const dest_bytes = @intCast(u16, dest.len * 2); + var dest_string = os.windows.UNICODE_STRING{ + .Length = dest_bytes, + .MaximumLength = dest_bytes, + .Buffer = @intToPtr([*]u16, @ptrToInt(dest.ptr)), + }; + const src_bytes = @intCast(u16, src.len * 2); + const src_string = os.windows.UNICODE_STRING{ + .Length = src_bytes, + .MaximumLength = src_bytes, + .Buffer = @intToPtr([*]u16, @ptrToInt(src.ptr)), + }; + const rc = os.windows.ntdll.RtlUpcaseUnicodeString(&dest_string, &src_string, os.windows.FALSE); + switch (rc) { + .SUCCESS => return dest_string.Length / 2, + else => unreachable, // we are not allocating, so no errors should be possible + } + } + + /// Note: Does not realloc the uppercase buf to allow for calling put for many variables and + /// only allocating the uppercase buf afterwards. + pub fn putUtf8(self: *Self, name: []const u8, value: []const u8) !void { + const uppercased_len = len: { + const name_uppercased_utf16 = uppercased: { + var name_utf16_buf = try std.ArrayListAligned(u8, @alignOf(u16)).initCapacity(self.allocator, name.len); + errdefer name_utf16_buf.deinit(); + + var uppercased_len = try std.unicode.utf8ToUtf16LeWriter(name_utf16_buf.writer(), name); + assert(uppercased_len == name_utf16_buf.items.len); + + break :uppercased name_utf16_buf.toOwnedSlice(); + }; + errdefer self.allocator.free(name_uppercased_utf16); + + const name_canonical = try self.allocator.dupe(u8, name); + errdefer self.allocator.free(name_canonical); + + const value_dupe = try self.allocator.dupe(u8, value); + errdefer self.allocator.free(value_dupe); + + const get_or_put = try self.uppercased_map.getOrPut(self.allocator, name_uppercased_utf16); + if (get_or_put.found_existing) { + // note: this is only safe from UAF because the errdefer that frees this value above + // no longer has a possibility of being triggered after this point + self.allocator.free(name_uppercased_utf16); + self.allocator.free(get_or_put.value_ptr.value); + self.allocator.free(get_or_put.value_ptr.canonical_name); + } else { + get_or_put.key_ptr.* = name_uppercased_utf16; + } + get_or_put.value_ptr.value = value_dupe; + get_or_put.value_ptr.canonical_name = name_canonical; + + break :len name_uppercased_utf16.len; + }; + + // The buffer for case conversion for key lookups will need to be as big as the largest + // key stored in the hash map. + self.max_name_utf16_length = @maximum(self.max_name_utf16_length, uppercased_len); + } + + /// Asserts that the name does not already exist in the map. + /// Note: Does not realloc the uppercase buf to allow for calling put for many variables and + /// only allocating the uppercase buf afterwards. + pub fn putUtf16NoClobber(self: *Self, name_utf16: []const u16, value_utf16: []const u16) !void { + const uppercased_len = len: { + const name_canonical = try std.unicode.utf16leToUtf8Alloc(self.allocator, name_utf16); + errdefer self.allocator.free(name_canonical); + + const value = try std.unicode.utf16leToUtf8Alloc(self.allocator, value_utf16); + errdefer self.allocator.free(value); + + const name_uppercased_utf16 = try self.allocator.alloc(u16, name_utf16.len); + errdefer self.allocator.free(name_uppercased_utf16); + + const uppercased_len = uppercaseName(name_uppercased_utf16, name_utf16); + assert(uppercased_len == name_uppercased_utf16.len); + + try self.uppercased_map.putNoClobber(self.allocator, std.mem.sliceAsBytes(name_uppercased_utf16), EnvValue{ + .value = value, + .canonical_name = name_canonical, + }); + break :len name_uppercased_utf16.len; + }; + + // The buffer for case conversion for key lookups will need to be as big as the largest + // key stored in the hash map. + self.max_name_utf16_length = @maximum(self.max_name_utf16_length, uppercased_len); + } + + /// Attempts to convert a UTF-8 name into a uppercased UTF-16le name for a lookup. If the + /// name cannot be converted, this function will return `null`. + fn utf8ToUppercasedUtf16(self: Self, name: []const u8) ?[]u16 { + const name_utf16: []u16 = to_utf16: { + var utf16_buf_stream = std.io.fixedBufferStream(std.mem.sliceAsBytes(self.uppercase_buf_utf16)); + _ = std.unicode.utf8ToUtf16LeWriter(utf16_buf_stream.writer(), name) catch |err| switch (err) { + // If the buffer isn't large enough, we can treat that as 'env var not found', as we + // know anything too large for the buffer can't be found in the map. + error.NoSpaceLeft => return null, + // Anything with invalid UTF-8 will also not be found in the map, so treat that as + // 'env var not found' too + error.InvalidUtf8 => return null, + }; + break :to_utf16 std.mem.bytesAsSlice(u16, utf16_buf_stream.getWritten()); + }; + + // uppercase in place + const uppercased_len = uppercaseName(name_utf16, name_utf16); + assert(uppercased_len == name_utf16.len); + + return name_utf16; + } + + /// Returns true if an entry was found and deleted, false otherwise. + pub fn remove(self: *Self, name: []const u8) bool { + const name_utf16 = self.utf8ToUppercasedUtf16(name) orelse return false; + const kv = self.uppercased_map.fetchRemove(std.mem.sliceAsBytes(name_utf16)) orelse return false; + self.allocator.free(kv.key); + self.allocator.free(kv.value.value); + self.allocator.free(kv.value.canonical_name); + return true; + } + + pub fn get(self: Self, name: []const u8) ?EnvValue { + const name_utf16 = self.utf8ToUppercasedUtf16(name) orelse return null; + return self.uppercased_map.get(std.mem.sliceAsBytes(name_utf16)); + } + + pub fn count(self: Self) EnvMap.Size { + return self.uppercased_map.count(); + } + + pub fn iterator(self: *const Self) Iterator { + return .{ + .env_map = self, + .uppercased_map_iterator = self.uppercased_map.iterator(), + }; + } + + pub const Iterator = struct { + env_map: *const Self, + uppercased_map_iterator: std.StringHashMapUnmanaged(EnvValue).Iterator, + + pub fn next(it: *Iterator) ?EnvMap.Entry { + if (it.uppercased_map_iterator.next()) |uppercased_entry| { + return EnvMap.Entry{ + .name = uppercased_entry.value_ptr.canonical_name, + .value = uppercased_entry.value_ptr.value, + }; + } else { + return null; + } + } + }; +}; + +test "EnvMapWindows" { + if (builtin.os.tag != .windows) return error.SkipZigTest; + + var env_map = EnvMapWindows.init(testing.allocator); + defer env_map.deinit(); + + // both put methods + try env_map.putUtf16NoClobber(std.unicode.utf8ToUtf16LeStringLiteral("Path"), std.unicode.utf8ToUtf16LeStringLiteral("something")); + try env_map.putUtf8("КИРИЛЛИЦА", "something else"); + try env_map.reallocUppercaseBuf(); + + try testing.expectEqual(@as(EnvMap.Size, 2), env_map.count()); + + // unicode-aware case-insensitive lookups + try testing.expectEqualStrings("something", env_map.get("PATH").?.value); + try testing.expectEqualStrings("something else", env_map.get("кириллица").?.value); + try testing.expect(env_map.get("missing") == null); + + // canonical names when iterating + var it = env_map.iterator(); + var count: EnvMap.Size = 0; + while (it.next()) |entry| { + const is_an_expected_name = std.mem.eql(u8, "Path", entry.name) or std.mem.eql(u8, "КИРИЛЛИЦА", entry.name); + try testing.expect(is_an_expected_name); + count += 1; + } + try testing.expectEqual(@as(EnvMap.Size, 2), count); +} + +pub const EnvMap = struct { + storage: StorageType, + + pub const StorageType = switch (builtin.os.tag) { + .windows => EnvMapWindows, + else => std.BufMap, + }; + + /// Matches what BufMap uses for its internal HashMap Size + pub const Size = u32; + + const Self = @This(); + + /// Deinitialize with `deinit`. + pub fn init(allocator: Allocator) Self { + return Self{ .storage = StorageType.init(allocator) }; + } + + pub fn deinit(self: *Self) void { + self.storage.deinit(); + } + + pub fn get(self: Self, name: []const u8) ?[]const u8 { + switch (builtin.os.tag) { + .windows => { + if (self.storage.get(name)) |entry| { + return entry.value; + } else { + return null; + } + }, + else => return self.storage.get(name), + } + } + + pub fn count(self: Self) Size { + return self.storage.count(); + } + + pub fn iterator(self: *const Self) Iterator { + return .{ .storage_iterator = self.storage.iterator() }; + } + + pub fn put(self: *Self, name: []const u8, value: []const u8) !void { + switch (builtin.os.tag) { + .windows => { + try self.storage.putUtf8(name, value); + try self.storage.reallocUppercaseBuf(); + }, + else => return self.storage.put(name, value), + } + } + + pub fn remove(self: *Self, name: []const u8) void { + _ = self.storage.remove(name); + } + + pub const Entry = struct { + name: []const u8, + value: []const u8, + }; + + pub const Iterator = struct { + storage_iterator: switch (builtin.os.tag) { + .windows => EnvMapWindows.Iterator, + else => std.BufMap.BufMapHashMap.Iterator, + }, + + pub fn next(it: *Iterator) ?Entry { + switch (builtin.os.tag) { + .windows => return it.storage_iterator.next(), + else => { + if (it.storage_iterator.next()) |entry| { + return Entry{ + .name = entry.key_ptr.*, + .value = entry.value_ptr.*, + }; + } else { + return null; + } + }, + } + } + }; +}; + +test "EnvMap" { + var env = EnvMap.init(testing.allocator); + defer env.deinit(); + + try env.put("SOMETHING_NEW", "hello"); + try testing.expectEqualStrings("hello", env.get("SOMETHING_NEW").?); + try testing.expectEqual(@as(EnvMap.Size, 1), env.count()); + + // overwrite + try env.put("SOMETHING_NEW", "something"); + try testing.expectEqualStrings("something", env.get("SOMETHING_NEW").?); + try testing.expectEqual(@as(EnvMap.Size, 1), env.count()); + + // a new longer name to test the Windows-specific conversion buffer + try env.put("SOMETHING_NEW_AND_LONGER", "1"); + try testing.expectEqualStrings("1", env.get("SOMETHING_NEW_AND_LONGER").?); + try testing.expectEqual(@as(EnvMap.Size, 2), env.count()); + + // case insensitivity on Windows only + if (builtin.os.tag == .windows) { + try testing.expectEqualStrings("1", env.get("something_New_aNd_LONGER").?); + } else { + try testing.expect(null == env.get("something_New_aNd_LONGER")); + } + + var it = env.iterator(); + var count: EnvMap.Size = 0; + while (it.next()) |entry| { + const is_an_expected_name = std.mem.eql(u8, "SOMETHING_NEW", entry.name) or std.mem.eql(u8, "SOMETHING_NEW_AND_LONGER", entry.name); + try testing.expect(is_an_expected_name); + count += 1; + } + try testing.expectEqual(@as(EnvMap.Size, 2), count); + + env.remove("SOMETHING_NEW"); + try testing.expect(env.get("SOMETHING_NEW") == null); + + try testing.expectEqual(@as(EnvMap.Size, 1), env.count()); +} + +/// Returns a snapshot of the environment variables of the current process. +/// Any modifications to the resulting EnvMap will not be not reflected in the environment, and +/// likewise, any future modifications to the environment will not be reflected in the EnvMap. +/// Caller owns resulting `EnvMap` and should call its `deinit` fn when done. +pub fn getEnvMap(allocator: Allocator) !EnvMap { + var result = EnvMap.init(allocator); errdefer result.deinit(); if (builtin.os.tag == .windows) { @@ -65,23 +440,27 @@ pub fn getEnvMap(allocator: Allocator) !BufMap { while (ptr[i] != 0) { const key_start = i; + // There are some special environment variables that start with =, + // so we need a special case to not treat = as a key/value separator + // if it's the first character. + // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133 + if (ptr[key_start] == '=') i += 1; + while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} const key_w = ptr[key_start..i]; - const key = try std.unicode.utf16leToUtf8Alloc(allocator, key_w); - errdefer allocator.free(key); if (ptr[i] == '=') i += 1; const value_start = i; while (ptr[i] != 0) : (i += 1) {} const value_w = ptr[value_start..i]; - const value = try std.unicode.utf16leToUtf8Alloc(allocator, value_w); - errdefer allocator.free(value); + + try result.storage.putUtf16NoClobber(key_w, value_w); i += 1; // skip over null byte - - try result.putMove(key, value); } + + try result.storage.reallocUppercaseBuf(); return result; } else if (builtin.os.tag == .wasi and !builtin.link_libc) { var environ_count: usize = undefined; @@ -140,8 +519,8 @@ pub fn getEnvMap(allocator: Allocator) !BufMap { } } -test "os.getEnvMap" { - var env = try getEnvMap(std.testing.allocator); +test "getEnvMap" { + var env = try getEnvMap(testing.allocator); defer env.deinit(); } diff --git a/lib/std/unicode.zig b/lib/std/unicode.zig index 81a7ed838f..706b12105a 100644 --- a/lib/std/unicode.zig +++ b/lib/std/unicode.zig @@ -710,6 +710,29 @@ pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) !usize { return dest_i; } +pub fn utf8ToUtf16LeWriter(writer: anytype, utf8: []const u8) !usize { + var src_i: usize = 0; + var bytes_written: usize = 0; + while (src_i < utf8.len) { + const n = utf8ByteSequenceLength(utf8[src_i]) catch return error.InvalidUtf8; + const next_src_i = src_i + n; + const codepoint = utf8Decode(utf8[src_i..next_src_i]) catch return error.InvalidUtf8; + if (codepoint < 0x10000) { + const short = @intCast(u16, codepoint); + try writer.writeIntLittle(u16, short); + bytes_written += 2; + } else { + const high = @intCast(u16, (codepoint - 0x10000) >> 10) + 0xD800; + const low = @intCast(u16, codepoint & 0x3FF) + 0xDC00; + try writer.writeIntLittle(u16, high); + try writer.writeIntLittle(u16, low); + bytes_written += 4; + } + src_i = next_src_i; + } + return bytes_written; +} + test "utf8ToUtf16Le" { var utf16le: [2]u16 = [_]u16{0} ** 2; { From 9e89000ffc92fd881ccb59d2571debe003a2f7b1 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sun, 6 Feb 2022 23:52:08 -0700 Subject: [PATCH 1463/2031] Update usages of `process.getEnvMap` and change BufMap -> EnvMap where applicable # Conflicts: # lib/std/build/RunStep.zig --- doc/docgen.zig | 4 ++-- lib/std/build.zig | 8 ++++---- lib/std/build/RunStep.zig | 25 +++++++------------------ lib/std/child_process.zig | 26 +++++++++++++------------- lib/std/process.zig | 2 +- 5 files changed, 27 insertions(+), 38 deletions(-) diff --git a/doc/docgen.zig b/doc/docgen.zig index 3dd58a012d..a101b96be7 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -1708,7 +1708,7 @@ fn genHtml( } } -fn exec(allocator: Allocator, env_map: *std.BufMap, args: []const []const u8) !ChildProcess.ExecResult { +fn exec(allocator: Allocator, env_map: *process.EnvMap, args: []const []const u8) !ChildProcess.ExecResult { const result = try ChildProcess.exec(.{ .allocator = allocator, .argv = args, @@ -1732,7 +1732,7 @@ fn exec(allocator: Allocator, env_map: *std.BufMap, args: []const []const u8) !C return result; } -fn getBuiltinCode(allocator: Allocator, env_map: *std.BufMap, zig_exe: []const u8) ![]const u8 { +fn getBuiltinCode(allocator: Allocator, env_map: *process.EnvMap, zig_exe: []const u8) ![]const u8 { const result = try exec(allocator, env_map, &[_][]const u8{ zig_exe, "build-obj", "--show-builtin" }); return result.stdout; } diff --git a/lib/std/build.zig b/lib/std/build.zig index b0200a928b..0f196cc9e9 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -12,7 +12,7 @@ const StringHashMap = std.StringHashMap; const Allocator = mem.Allocator; const process = std.process; const BufSet = std.BufSet; -const BufMap = std.BufMap; +const EnvMap = std.process.EnvMap; const fmt_lib = std.fmt; const File = std.fs.File; const CrossTarget = std.zig.CrossTarget; @@ -48,7 +48,7 @@ pub const Builder = struct { invalid_user_input: bool, zig_exe: []const u8, default_step: *Step, - env_map: *BufMap, + env_map: *EnvMap, top_level_steps: ArrayList(*TopLevelStep), install_prefix: []const u8, dest_dir: ?[]const u8, @@ -167,7 +167,7 @@ pub const Builder = struct { cache_root: []const u8, global_cache_root: []const u8, ) !*Builder { - const env_map = try allocator.create(BufMap); + const env_map = try allocator.create(EnvMap); env_map.* = try process.getEnvMap(allocator); const host = try NativeTargetInfo.detect(allocator, .{}); @@ -963,7 +963,7 @@ pub const Builder = struct { warn("\n", .{}); } - pub fn spawnChildEnvMap(self: *Builder, cwd: ?[]const u8, env_map: *const BufMap, argv: []const []const u8) !void { + pub fn spawnChildEnvMap(self: *Builder, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void { if (self.verbose) { printCmd(cwd, argv); } diff --git a/lib/std/build/RunStep.zig b/lib/std/build/RunStep.zig index e00fe3deb6..bc6ccd7b69 100644 --- a/lib/std/build/RunStep.zig +++ b/lib/std/build/RunStep.zig @@ -9,7 +9,7 @@ const fs = std.fs; const mem = std.mem; const process = std.process; const ArrayList = std.ArrayList; -const BufMap = std.BufMap; +const EnvMap = process.EnvMap; const Allocator = mem.Allocator; const ExecError = build.Builder.ExecError; @@ -29,7 +29,7 @@ argv: ArrayList(Arg), cwd: ?[]const u8, /// Override this field to modify the environment, or use setEnvironmentVariable -env_map: ?*BufMap, +env_map: ?*EnvMap, stdout_action: StdIoAction = .inherit, stderr_action: StdIoAction = .inherit, @@ -91,8 +91,8 @@ pub fn addArgs(self: *RunStep, args: []const []const u8) void { } pub fn clearEnvironment(self: *RunStep) void { - const new_env_map = self.builder.allocator.create(BufMap) catch unreachable; - new_env_map.* = BufMap.init(self.builder.allocator); + const new_env_map = self.builder.allocator.create(EnvMap) catch unreachable; + new_env_map.* = EnvMap.init(self.builder.allocator); self.env_map = new_env_map; } @@ -100,18 +100,7 @@ pub fn addPathDir(self: *RunStep, search_path: []const u8) void { const env_map = self.getEnvMap(); var key: []const u8 = undefined; - var prev_path: ?[]const u8 = undefined; - if (builtin.os.tag == .windows) { - key = "Path"; - prev_path = env_map.get(key); - if (prev_path == null) { - key = "PATH"; - prev_path = env_map.get(key); - } - } else { - key = "PATH"; - prev_path = env_map.get(key); - } + var prev_path = env_map.get("PATH"); if (prev_path) |pp| { const new_path = self.builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path }); @@ -121,9 +110,9 @@ pub fn addPathDir(self: *RunStep, search_path: []const u8) void { } } -pub fn getEnvMap(self: *RunStep) *BufMap { +pub fn getEnvMap(self: *RunStep) *EnvMap { return self.env_map orelse { - const env_map = self.builder.allocator.create(BufMap) catch unreachable; + const env_map = self.builder.allocator.create(EnvMap) catch unreachable; env_map.* = process.getEnvMap(self.builder.allocator) catch unreachable; self.env_map = env_map; return env_map; diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 5f01ed01dd..48f0776465 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -12,7 +12,7 @@ const linux = os.linux; const mem = std.mem; const math = std.math; const debug = std.debug; -const BufMap = std.BufMap; +const EnvMap = process.EnvMap; const Os = std.builtin.Os; const TailQueue = std.TailQueue; const maxInt = std.math.maxInt; @@ -34,7 +34,7 @@ pub const ChildProcess = struct { argv: []const []const u8, /// Leave as null to use the current env map using the supplied allocator. - env_map: ?*const BufMap, + env_map: ?*const EnvMap, stdin_behavior: StdIo, stdout_behavior: StdIo, @@ -375,7 +375,7 @@ pub const ChildProcess = struct { argv: []const []const u8, cwd: ?[]const u8 = null, cwd_dir: ?fs.Dir = null, - env_map: ?*const BufMap = null, + env_map: ?*const EnvMap = null, max_output_bytes: usize = 50 * 1024, expand_arg0: Arg0Expand = .no_expand, }) !ExecResult { @@ -1237,7 +1237,7 @@ fn readIntFd(fd: i32) !ErrInt { } /// Caller must free result. -pub fn createWindowsEnvBlock(allocator: mem.Allocator, env_map: *const BufMap) ![]u16 { +pub fn createWindowsEnvBlock(allocator: mem.Allocator, env_map: *const EnvMap) ![]u16 { // count bytes needed const max_chars_needed = x: { var max_chars_needed: usize = 4; // 4 for the final 4 null bytes @@ -1245,7 +1245,7 @@ pub fn createWindowsEnvBlock(allocator: mem.Allocator, env_map: *const BufMap) ! while (it.next()) |pair| { // +1 for '=' // +1 for null byte - max_chars_needed += pair.key_ptr.len + pair.value_ptr.len + 2; + max_chars_needed += pair.name.len + pair.value.len + 2; } break :x max_chars_needed; }; @@ -1255,10 +1255,10 @@ pub fn createWindowsEnvBlock(allocator: mem.Allocator, env_map: *const BufMap) ! var it = env_map.iterator(); var i: usize = 0; while (it.next()) |pair| { - i += try unicode.utf8ToUtf16Le(result[i..], pair.key_ptr.*); + i += try unicode.utf8ToUtf16Le(result[i..], pair.name); result[i] = '='; i += 1; - i += try unicode.utf8ToUtf16Le(result[i..], pair.value_ptr.*); + i += try unicode.utf8ToUtf16Le(result[i..], pair.value); result[i] = 0; i += 1; } @@ -1273,17 +1273,17 @@ pub fn createWindowsEnvBlock(allocator: mem.Allocator, env_map: *const BufMap) ! return allocator.shrink(result, i); } -pub fn createNullDelimitedEnvMap(arena: mem.Allocator, env_map: *const std.BufMap) ![:null]?[*:0]u8 { +pub fn createNullDelimitedEnvMap(arena: mem.Allocator, env_map: *const EnvMap) ![:null]?[*:0]u8 { const envp_count = env_map.count(); const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null); { var it = env_map.iterator(); var i: usize = 0; while (it.next()) |pair| : (i += 1) { - const env_buf = try arena.allocSentinel(u8, pair.key_ptr.len + pair.value_ptr.len + 1, 0); - mem.copy(u8, env_buf, pair.key_ptr.*); - env_buf[pair.key_ptr.len] = '='; - mem.copy(u8, env_buf[pair.key_ptr.len + 1 ..], pair.value_ptr.*); + const env_buf = try arena.allocSentinel(u8, pair.name.len + pair.value.len + 1, 0); + mem.copy(u8, env_buf, pair.name); + env_buf[pair.name.len] = '='; + mem.copy(u8, env_buf[pair.name.len + 1 ..], pair.value); envp_buf[i] = env_buf.ptr; } assert(i == envp_count); @@ -1294,7 +1294,7 @@ pub fn createNullDelimitedEnvMap(arena: mem.Allocator, env_map: *const std.BufMa test "createNullDelimitedEnvMap" { const testing = std.testing; const allocator = testing.allocator; - var envmap = BufMap.init(allocator); + var envmap = EnvMap.init(allocator); defer envmap.deinit(); try envmap.put("HOME", "/home/ifreund"); diff --git a/lib/std/process.zig b/lib/std/process.zig index f5d14cf6da..66a82c4b1d 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -1364,7 +1364,7 @@ pub fn execv(allocator: mem.Allocator, argv: []const []const u8) ExecvError { pub fn execve( allocator: mem.Allocator, argv: []const []const u8, - env_map: ?*const std.BufMap, + env_map: ?*const EnvMap, ) ExecvError { if (!can_execv) @compileError("The target OS does not support execv"); From a65be05a6efdd89657215da1b661e01f09eef4e8 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Thu, 20 Jan 2022 02:21:52 -0800 Subject: [PATCH 1464/2031] Make the BufMap.count return value match its underlying HashMap's Size Fixes a process.EnvMap compile error on 32-bit architectures --- lib/std/buf_map.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/buf_map.zig b/lib/std/buf_map.zig index bd59d6da45..5d155747d2 100644 --- a/lib/std/buf_map.zig +++ b/lib/std/buf_map.zig @@ -82,7 +82,7 @@ pub const BufMap = struct { } /// Returns the number of KV pairs stored in the map. - pub fn count(self: BufMap) usize { + pub fn count(self: BufMap) BufMapHashMap.Size { return self.hash_map.count(); } From a2069612a5ee838fd88fdeb93f44c4d4fb1fcb8b Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Thu, 20 Jan 2022 07:17:16 -0800 Subject: [PATCH 1465/2031] Fix regression in RunStep.addPathDir `key` was never being given a value, caused by b83cea12f31feaa0aff9489d5f7bd6159d284567 --- lib/std/build/RunStep.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/build/RunStep.zig b/lib/std/build/RunStep.zig index bc6ccd7b69..1e4cca5167 100644 --- a/lib/std/build/RunStep.zig +++ b/lib/std/build/RunStep.zig @@ -99,8 +99,8 @@ pub fn clearEnvironment(self: *RunStep) void { pub fn addPathDir(self: *RunStep, search_path: []const u8) void { const env_map = self.getEnvMap(); - var key: []const u8 = undefined; - var prev_path = env_map.get("PATH"); + var key: []const u8 = "PATH"; + var prev_path = env_map.get(key); if (prev_path) |pp| { const new_path = self.builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path }); From b2b48fbf2cf84744e5d154b7d4f62b298f725f2b Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Thu, 20 Jan 2022 07:19:16 -0800 Subject: [PATCH 1466/2031] Set EnvMap.Size to BufMap.BufMapHashMap.Size Now that BufMap.BufMapHashMap is pub, we can just get Size directly --- lib/std/process.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/std/process.zig b/lib/std/process.zig index 66a82c4b1d..d7f6570e8b 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -307,8 +307,7 @@ pub const EnvMap = struct { else => std.BufMap, }; - /// Matches what BufMap uses for its internal HashMap Size - pub const Size = u32; + pub const Size = std.BufMap.BufMapHashMap.Size; const Self = @This(); From e70cb04f89005d08d837b062e8c10a3a1b406a6c Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Thu, 3 Feb 2022 22:56:06 -0800 Subject: [PATCH 1467/2031] EnvMapWindows: Fix putUtf8 not uppercasing keys --- lib/std/process.zig | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/std/process.zig b/lib/std/process.zig index d7f6570e8b..712c8e9317 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -141,8 +141,13 @@ pub const EnvMapWindows = struct { var name_utf16_buf = try std.ArrayListAligned(u8, @alignOf(u16)).initCapacity(self.allocator, name.len); errdefer name_utf16_buf.deinit(); - var uppercased_len = try std.unicode.utf8ToUtf16LeWriter(name_utf16_buf.writer(), name); - assert(uppercased_len == name_utf16_buf.items.len); + const bytes_written = try std.unicode.utf8ToUtf16LeWriter(name_utf16_buf.writer(), name); + var name_utf16 = name_utf16_buf.items[0..bytes_written]; + + // uppercase in place + var name_uppercased_utf16 = std.mem.bytesAsSlice(u16, name_utf16); + const uppercased_len = uppercaseName(name_uppercased_utf16, name_uppercased_utf16); + assert(uppercased_len == name_uppercased_utf16.len); break :uppercased name_utf16_buf.toOwnedSlice(); }; @@ -278,7 +283,7 @@ test "EnvMapWindows" { // both put methods try env_map.putUtf16NoClobber(std.unicode.utf8ToUtf16LeStringLiteral("Path"), std.unicode.utf8ToUtf16LeStringLiteral("something")); - try env_map.putUtf8("КИРИЛЛИЦА", "something else"); + try env_map.putUtf8("КИРиллИЦА", "something else"); try env_map.reallocUppercaseBuf(); try testing.expectEqual(@as(EnvMap.Size, 2), env_map.count()); @@ -292,7 +297,7 @@ test "EnvMapWindows" { var it = env_map.iterator(); var count: EnvMap.Size = 0; while (it.next()) |entry| { - const is_an_expected_name = std.mem.eql(u8, "Path", entry.name) or std.mem.eql(u8, "КИРИЛЛИЦА", entry.name); + const is_an_expected_name = std.mem.eql(u8, "Path", entry.name) or std.mem.eql(u8, "КИРиллИЦА", entry.name); try testing.expect(is_an_expected_name); count += 1; } From 69f0a5587d0db07546e91968b21135fdef856136 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Fri, 4 Feb 2022 12:08:38 -0700 Subject: [PATCH 1468/2031] remove extra storage from EnvMap on windows --- lib/std/child_process.zig | 14 +- lib/std/process.zig | 443 ++++++++++---------------------------- 2 files changed, 122 insertions(+), 335 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 48f0776465..0bb737decb 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -1245,7 +1245,7 @@ pub fn createWindowsEnvBlock(allocator: mem.Allocator, env_map: *const EnvMap) ! while (it.next()) |pair| { // +1 for '=' // +1 for null byte - max_chars_needed += pair.name.len + pair.value.len + 2; + max_chars_needed += pair.key_ptr.len + pair.value_ptr.len + 2; } break :x max_chars_needed; }; @@ -1255,10 +1255,10 @@ pub fn createWindowsEnvBlock(allocator: mem.Allocator, env_map: *const EnvMap) ! var it = env_map.iterator(); var i: usize = 0; while (it.next()) |pair| { - i += try unicode.utf8ToUtf16Le(result[i..], pair.name); + i += try unicode.utf8ToUtf16Le(result[i..], pair.key_ptr.*); result[i] = '='; i += 1; - i += try unicode.utf8ToUtf16Le(result[i..], pair.value); + i += try unicode.utf8ToUtf16Le(result[i..], pair.value_ptr.*); result[i] = 0; i += 1; } @@ -1280,10 +1280,10 @@ pub fn createNullDelimitedEnvMap(arena: mem.Allocator, env_map: *const EnvMap) ! var it = env_map.iterator(); var i: usize = 0; while (it.next()) |pair| : (i += 1) { - const env_buf = try arena.allocSentinel(u8, pair.name.len + pair.value.len + 1, 0); - mem.copy(u8, env_buf, pair.name); - env_buf[pair.name.len] = '='; - mem.copy(u8, env_buf[pair.name.len + 1 ..], pair.value); + const env_buf = try arena.allocSentinel(u8, pair.key_ptr.len + pair.value_ptr.len + 1, 0); + mem.copy(u8, env_buf, pair.key_ptr.*); + env_buf[pair.key_ptr.len] = '='; + mem.copy(u8, env_buf[pair.key_ptr.len + 1 ..], pair.value_ptr.*); envp_buf[i] = env_buf.ptr; } assert(i == envp_count); diff --git a/lib/std/process.zig b/lib/std/process.zig index 712c8e9317..0b891bbdf5 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -52,341 +52,128 @@ test "getCwdAlloc" { testing.allocator.free(cwd); } -/// EnvMap for Windows that handles Unicode-aware case insensitivity for lookups, while also -/// providing the canonical environment variable names when iterating. -/// -/// Allows for zero-allocation lookups (even though it needs to do UTF-8 -> UTF-16 -> uppercase -/// conversions) by allocating a buffer large enough to fit the largest environment variable -/// name, and using that when doing lookups (i.e. anything that overflows the buffer can be treated -/// as the environment variable not being found). -pub const EnvMapWindows = struct { - allocator: Allocator, - /// Keys are UTF-16le stored as []const u8 - uppercased_map: std.StringHashMapUnmanaged(EnvValue), - /// Buffer for converting to uppercased UTF-16 on key lookups - /// Must call `reallocUppercaseBuf` before doing any lookups after a `put` call. - uppercase_buf_utf16: []u16 = &[_]u16{}, - max_name_utf16_length: usize = 0, - - pub const EnvValue = struct { - value: []const u8, - canonical_name: []const u8, - }; - - const Self = @This(); - - /// Deinitialize with `deinit`. - pub fn init(allocator: Allocator) Self { - return .{ - .allocator = allocator, - .uppercased_map = std.StringHashMapUnmanaged(EnvValue){}, - }; - } - - pub fn deinit(self: *Self) void { - var it = self.uppercased_map.iterator(); - while (it.next()) |entry| { - self.allocator.free(entry.key_ptr.*); - self.allocator.free(entry.value_ptr.value); - self.allocator.free(entry.value_ptr.canonical_name); - } - self.uppercased_map.deinit(self.allocator); - self.allocator.free(self.uppercase_buf_utf16); - } - - /// Increases the size of the uppercase buffer if the maximum name size has increased. - /// Must be called before any `get` calls after any number of `put` calls. - pub fn reallocUppercaseBuf(self: *Self) !void { - if (self.max_name_utf16_length > self.uppercase_buf_utf16.len) { - self.uppercase_buf_utf16 = try self.allocator.realloc(self.uppercase_buf_utf16, self.max_name_utf16_length); - } - } - - /// Converts `src` to uppercase using `RtlUpcaseUnicodeString` and puts the result in `dest`. - /// Returns the length of the converted UTF-16 string. `dest.len` must be >= `src.len`. - /// - /// Note: As of now, RtlUpcaseUnicodeString does not seem to handle codepoints above 0x10000 - /// (i.e. those that require a surrogate pair), so this function will always return a length - /// equal to `src.len`. However, if RtlUpcaseUnicodeString is updated to handle codepoints above - /// 0x10000, this property would still hold unless there are lowercase <-> uppercase conversions - /// that cross over the boundary between codepoints >= 0x10000 and < 0x10000. - /// TODO: Is it feasible that Unicode lowercase <-> uppercase conversions could cross that boundary? - fn uppercaseName(dest: []u16, src: []const u16) u16 { - assert(dest.len >= src.len); - - const dest_bytes = @intCast(u16, dest.len * 2); - var dest_string = os.windows.UNICODE_STRING{ - .Length = dest_bytes, - .MaximumLength = dest_bytes, - .Buffer = @intToPtr([*]u16, @ptrToInt(dest.ptr)), - }; - const src_bytes = @intCast(u16, src.len * 2); - const src_string = os.windows.UNICODE_STRING{ - .Length = src_bytes, - .MaximumLength = src_bytes, - .Buffer = @intToPtr([*]u16, @ptrToInt(src.ptr)), - }; - const rc = os.windows.ntdll.RtlUpcaseUnicodeString(&dest_string, &src_string, os.windows.FALSE); - switch (rc) { - .SUCCESS => return dest_string.Length / 2, - else => unreachable, // we are not allocating, so no errors should be possible - } - } - - /// Note: Does not realloc the uppercase buf to allow for calling put for many variables and - /// only allocating the uppercase buf afterwards. - pub fn putUtf8(self: *Self, name: []const u8, value: []const u8) !void { - const uppercased_len = len: { - const name_uppercased_utf16 = uppercased: { - var name_utf16_buf = try std.ArrayListAligned(u8, @alignOf(u16)).initCapacity(self.allocator, name.len); - errdefer name_utf16_buf.deinit(); - - const bytes_written = try std.unicode.utf8ToUtf16LeWriter(name_utf16_buf.writer(), name); - var name_utf16 = name_utf16_buf.items[0..bytes_written]; - - // uppercase in place - var name_uppercased_utf16 = std.mem.bytesAsSlice(u16, name_utf16); - const uppercased_len = uppercaseName(name_uppercased_utf16, name_uppercased_utf16); - assert(uppercased_len == name_uppercased_utf16.len); - - break :uppercased name_utf16_buf.toOwnedSlice(); - }; - errdefer self.allocator.free(name_uppercased_utf16); - - const name_canonical = try self.allocator.dupe(u8, name); - errdefer self.allocator.free(name_canonical); - - const value_dupe = try self.allocator.dupe(u8, value); - errdefer self.allocator.free(value_dupe); - - const get_or_put = try self.uppercased_map.getOrPut(self.allocator, name_uppercased_utf16); - if (get_or_put.found_existing) { - // note: this is only safe from UAF because the errdefer that frees this value above - // no longer has a possibility of being triggered after this point - self.allocator.free(name_uppercased_utf16); - self.allocator.free(get_or_put.value_ptr.value); - self.allocator.free(get_or_put.value_ptr.canonical_name); - } else { - get_or_put.key_ptr.* = name_uppercased_utf16; - } - get_or_put.value_ptr.value = value_dupe; - get_or_put.value_ptr.canonical_name = name_canonical; - - break :len name_uppercased_utf16.len; - }; - - // The buffer for case conversion for key lookups will need to be as big as the largest - // key stored in the hash map. - self.max_name_utf16_length = @maximum(self.max_name_utf16_length, uppercased_len); - } - - /// Asserts that the name does not already exist in the map. - /// Note: Does not realloc the uppercase buf to allow for calling put for many variables and - /// only allocating the uppercase buf afterwards. - pub fn putUtf16NoClobber(self: *Self, name_utf16: []const u16, value_utf16: []const u16) !void { - const uppercased_len = len: { - const name_canonical = try std.unicode.utf16leToUtf8Alloc(self.allocator, name_utf16); - errdefer self.allocator.free(name_canonical); - - const value = try std.unicode.utf16leToUtf8Alloc(self.allocator, value_utf16); - errdefer self.allocator.free(value); - - const name_uppercased_utf16 = try self.allocator.alloc(u16, name_utf16.len); - errdefer self.allocator.free(name_uppercased_utf16); - - const uppercased_len = uppercaseName(name_uppercased_utf16, name_utf16); - assert(uppercased_len == name_uppercased_utf16.len); - - try self.uppercased_map.putNoClobber(self.allocator, std.mem.sliceAsBytes(name_uppercased_utf16), EnvValue{ - .value = value, - .canonical_name = name_canonical, - }); - break :len name_uppercased_utf16.len; - }; - - // The buffer for case conversion for key lookups will need to be as big as the largest - // key stored in the hash map. - self.max_name_utf16_length = @maximum(self.max_name_utf16_length, uppercased_len); - } - - /// Attempts to convert a UTF-8 name into a uppercased UTF-16le name for a lookup. If the - /// name cannot be converted, this function will return `null`. - fn utf8ToUppercasedUtf16(self: Self, name: []const u8) ?[]u16 { - const name_utf16: []u16 = to_utf16: { - var utf16_buf_stream = std.io.fixedBufferStream(std.mem.sliceAsBytes(self.uppercase_buf_utf16)); - _ = std.unicode.utf8ToUtf16LeWriter(utf16_buf_stream.writer(), name) catch |err| switch (err) { - // If the buffer isn't large enough, we can treat that as 'env var not found', as we - // know anything too large for the buffer can't be found in the map. - error.NoSpaceLeft => return null, - // Anything with invalid UTF-8 will also not be found in the map, so treat that as - // 'env var not found' too - error.InvalidUtf8 => return null, - }; - break :to_utf16 std.mem.bytesAsSlice(u16, utf16_buf_stream.getWritten()); - }; - - // uppercase in place - const uppercased_len = uppercaseName(name_utf16, name_utf16); - assert(uppercased_len == name_utf16.len); - - return name_utf16; - } - - /// Returns true if an entry was found and deleted, false otherwise. - pub fn remove(self: *Self, name: []const u8) bool { - const name_utf16 = self.utf8ToUppercasedUtf16(name) orelse return false; - const kv = self.uppercased_map.fetchRemove(std.mem.sliceAsBytes(name_utf16)) orelse return false; - self.allocator.free(kv.key); - self.allocator.free(kv.value.value); - self.allocator.free(kv.value.canonical_name); - return true; - } - - pub fn get(self: Self, name: []const u8) ?EnvValue { - const name_utf16 = self.utf8ToUppercasedUtf16(name) orelse return null; - return self.uppercased_map.get(std.mem.sliceAsBytes(name_utf16)); - } - - pub fn count(self: Self) EnvMap.Size { - return self.uppercased_map.count(); - } - - pub fn iterator(self: *const Self) Iterator { - return .{ - .env_map = self, - .uppercased_map_iterator = self.uppercased_map.iterator(), - }; - } - - pub const Iterator = struct { - env_map: *const Self, - uppercased_map_iterator: std.StringHashMapUnmanaged(EnvValue).Iterator, - - pub fn next(it: *Iterator) ?EnvMap.Entry { - if (it.uppercased_map_iterator.next()) |uppercased_entry| { - return EnvMap.Entry{ - .name = uppercased_entry.value_ptr.canonical_name, - .value = uppercased_entry.value_ptr.value, - }; - } else { - return null; - } - } - }; -}; - -test "EnvMapWindows" { - if (builtin.os.tag != .windows) return error.SkipZigTest; - - var env_map = EnvMapWindows.init(testing.allocator); - defer env_map.deinit(); - - // both put methods - try env_map.putUtf16NoClobber(std.unicode.utf8ToUtf16LeStringLiteral("Path"), std.unicode.utf8ToUtf16LeStringLiteral("something")); - try env_map.putUtf8("КИРиллИЦА", "something else"); - try env_map.reallocUppercaseBuf(); - - try testing.expectEqual(@as(EnvMap.Size, 2), env_map.count()); - - // unicode-aware case-insensitive lookups - try testing.expectEqualStrings("something", env_map.get("PATH").?.value); - try testing.expectEqualStrings("something else", env_map.get("кириллица").?.value); - try testing.expect(env_map.get("missing") == null); - - // canonical names when iterating - var it = env_map.iterator(); - var count: EnvMap.Size = 0; - while (it.next()) |entry| { - const is_an_expected_name = std.mem.eql(u8, "Path", entry.name) or std.mem.eql(u8, "КИРиллИЦА", entry.name); - try testing.expect(is_an_expected_name); - count += 1; - } - try testing.expectEqual(@as(EnvMap.Size, 2), count); -} - pub const EnvMap = struct { - storage: StorageType, + hash_map: HashMap, - pub const StorageType = switch (builtin.os.tag) { - .windows => EnvMapWindows, - else => std.BufMap, - }; + const HashMap = std.HashMap( + []const u8, + []const u8, + EnvNameHashContext, + std.hash_map.default_max_load_percentage, + ); - pub const Size = std.BufMap.BufMapHashMap.Size; - - const Self = @This(); - - /// Deinitialize with `deinit`. - pub fn init(allocator: Allocator) Self { - return Self{ .storage = StorageType.init(allocator) }; - } - - pub fn deinit(self: *Self) void { - self.storage.deinit(); - } - - pub fn get(self: Self, name: []const u8) ?[]const u8 { - switch (builtin.os.tag) { - .windows => { - if (self.storage.get(name)) |entry| { - return entry.value; - } else { - return null; + pub const EnvNameHashContext = struct { + pub fn hash(self: @This(), s: []const u8) u64 { + _ = self; + if (builtin.os.tag == .windows) { + const h = std.hash.Wyhash.init(0); + // TODO: improve this, instead of iterating over ascii, + // iterate over with unicode + for (s) |c| { + var s_upper = [_]u8 { std.ascii.toLower(c) }; + h.update(s_upper); } - }, - else => return self.storage.get(name), - } - } - - pub fn count(self: Self) Size { - return self.storage.count(); - } - - pub fn iterator(self: *const Self) Iterator { - return .{ .storage_iterator = self.storage.iterator() }; - } - - pub fn put(self: *Self, name: []const u8, value: []const u8) !void { - switch (builtin.os.tag) { - .windows => { - try self.storage.putUtf8(name, value); - try self.storage.reallocUppercaseBuf(); - }, - else => return self.storage.put(name, value), - } - } - - pub fn remove(self: *Self, name: []const u8) void { - _ = self.storage.remove(name); - } - - pub const Entry = struct { - name: []const u8, - value: []const u8, - }; - - pub const Iterator = struct { - storage_iterator: switch (builtin.os.tag) { - .windows => EnvMapWindows.Iterator, - else => std.BufMap.BufMapHashMap.Iterator, - }, - - pub fn next(it: *Iterator) ?Entry { - switch (builtin.os.tag) { - .windows => return it.storage_iterator.next(), - else => { - if (it.storage_iterator.next()) |entry| { - return Entry{ - .name = entry.key_ptr.*, - .value = entry.value_ptr.*, - }; - } else { - return null; - } - }, + return h.final(); } + return std.hash_map.hashString(s); + } + pub fn eql(self: @This(), a: []const u8, b: []const u8) bool { + _ = self; + if (builtin.os.tag == .windows) { + // TODO: improve this, instead of comparing ascii + // compare with unicode + return std.ascii.eqlIgnoreCase(a, b); + } + return std.hash_map.eqlString(a, b); } }; + + /// Create a EnvMap backed by a specific allocator. + /// That allocator will be used for both backing allocations + /// and string deduplication. + pub fn init(allocator: Allocator) EnvMap { + return EnvMap{ .hash_map = HashMap.init(allocator) }; + } + + /// Free the backing storage of the map, as well as all + /// of the stored keys and values. + pub fn deinit(self: *EnvMap) void { + var it = self.hash_map.iterator(); + while (it.next()) |entry| { + self.free(entry.key_ptr.*); + self.free(entry.value_ptr.*); + } + + self.hash_map.deinit(); + } + + /// Same as `put` but the key and value become owned by the EnvMap rather + /// than being copied. + /// If `putMove` fails, the ownership of key and value does not transfer. + pub fn putMove(self: *EnvMap, key: []u8, value: []u8) !void { + const get_or_put = try self.hash_map.getOrPut(key); + if (get_or_put.found_existing) { + self.free(get_or_put.key_ptr.*); + self.free(get_or_put.value_ptr.*); + get_or_put.key_ptr.* = key; + } + get_or_put.value_ptr.* = value; + } + + /// `key` and `value` are copied into the EnvMap. + pub fn put(self: *EnvMap, key: []const u8, value: []const u8) !void { + const value_copy = try self.copy(value); + errdefer self.free(value_copy); + const get_or_put = try self.hash_map.getOrPut(key); + if (get_or_put.found_existing) { + self.free(get_or_put.value_ptr.*); + } else { + get_or_put.key_ptr.* = self.copy(key) catch |err| { + _ = self.hash_map.remove(key); + return err; + }; + } + get_or_put.value_ptr.* = value_copy; + } + + /// Find the address of the value associated with a key. + /// The returned pointer is invalidated if the map resizes. + pub fn getPtr(self: EnvMap, key: []const u8) ?*[]const u8 { + return self.hash_map.getPtr(key); + } + + /// Return the map's copy of the value associated with + /// a key. The returned string is invalidated if this + /// key is removed from the map. + pub fn get(self: EnvMap, key: []const u8) ?[]const u8 { + return self.hash_map.get(key); + } + + /// Removes the item from the map and frees its value. + /// This invalidates the value returned by get() for this key. + pub fn remove(self: *EnvMap, key: []const u8) void { + const kv = self.hash_map.fetchRemove(key) orelse return; + self.free(kv.key); + self.free(kv.value); + } + + /// Returns the number of KV pairs stored in the map. + pub fn count(self: EnvMap) HashMap.Size { + return self.hash_map.count(); + } + + /// Returns an iterator over entries in the map. + pub fn iterator(self: *const EnvMap) HashMap.Iterator { + return self.hash_map.iterator(); + } + + fn free(self: EnvMap, value: []const u8) void { + self.hash_map.allocator.free(value); + } + + fn copy(self: EnvMap, value: []const u8) ![]u8 { + return self.hash_map.allocator.dupe(u8, value); + } }; test "EnvMap" { From e65d8f82c5f98b20236f571fe4a4e40924ea8dc2 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Fri, 4 Feb 2022 22:36:24 -0700 Subject: [PATCH 1469/2031] add unicode support --- lib/std/os/windows/ntdll.zig | 4 ++++ lib/std/process.zig | 32 ++++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/lib/std/os/windows/ntdll.zig b/lib/std/os/windows/ntdll.zig index 2444d5f487..5817e482e7 100644 --- a/lib/std/os/windows/ntdll.zig +++ b/lib/std/os/windows/ntdll.zig @@ -235,6 +235,10 @@ pub extern "NtDll" fn RtlUpcaseUnicodeString( AllocateDestinationString: BOOLEAN, ) callconv(WINAPI) NTSTATUS; +pub extern "NtDll" fn RtlUpcaseUnicodeChar( + SourceCharacter: u16, +) callconv(WINAPI) u16; + pub extern "ntdll" fn NtLockFile( FileHandle: HANDLE, Event: ?HANDLE, diff --git a/lib/std/process.zig b/lib/std/process.zig index 0b891bbdf5..8892f1cc88 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -63,26 +63,42 @@ pub const EnvMap = struct { ); pub const EnvNameHashContext = struct { + fn upcase(c: u21) u21 { + if (c <= std.math.maxInt(u16)) + return std.os.windows.ntdll.RtlUpcaseUnicodeChar(c); + return c; + } + pub fn hash(self: @This(), s: []const u8) u64 { _ = self; if (builtin.os.tag == .windows) { const h = std.hash.Wyhash.init(0); - // TODO: improve this, instead of iterating over ascii, - // iterate over with unicode - for (s) |c| { - var s_upper = [_]u8 { std.ascii.toLower(c) }; - h.update(s_upper); + var it = std.unicode.Utf8View(s).iterator(); + while (it.nextCodepoint()) |cp| { + const cp_upper = upcase(cp); + h.update(&[_]u8{ + @intCast(u8, (cp_upper >> 16) & 0xff), + @intCast(u8, (cp_upper >> 8) & 0xff), + @intCast(u8, (cp_upper >> 0) & 0xff), + }); } return h.final(); } return std.hash_map.hashString(s); } + pub fn eql(self: @This(), a: []const u8, b: []const u8) bool { _ = self; if (builtin.os.tag == .windows) { - // TODO: improve this, instead of comparing ascii - // compare with unicode - return std.ascii.eqlIgnoreCase(a, b); + var it_a = std.unicode.Utf8View(a).iterator(); + var it_b = std.unicode.Utf8View(b).iterator(); + while (true) { + const c_a = it_a.nextCodepoint() orelse break; + const c_b = it_b.nextCodepoint() orelse return false; + if (upcase(c_a) != upcase(c_b)) + return false; + } + if (it_b.nextCodepoint()) return false; } return std.hash_map.eqlString(a, b); } From 1c874a871fe4795f71edf6e9739b72d071eb453a Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Fri, 4 Feb 2022 23:35:22 -0700 Subject: [PATCH 1470/2031] reverse some of the now unneeded changes from squeek --- lib/std/buf_map.zig | 2 +- lib/std/process.zig | 10 ++++++---- lib/std/unicode.zig | 23 ----------------------- 3 files changed, 7 insertions(+), 28 deletions(-) diff --git a/lib/std/buf_map.zig b/lib/std/buf_map.zig index 5d155747d2..2a6239c490 100644 --- a/lib/std/buf_map.zig +++ b/lib/std/buf_map.zig @@ -9,7 +9,7 @@ const testing = std.testing; pub const BufMap = struct { hash_map: BufMapHashMap, - pub const BufMapHashMap = StringHashMap([]const u8); + const BufMapHashMap = StringHashMap([]const u8); /// Create a BufMap backed by a specific allocator. /// That allocator will be used for both backing allocations diff --git a/lib/std/process.zig b/lib/std/process.zig index 8892f1cc88..26cde9db51 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -255,19 +255,21 @@ pub fn getEnvMap(allocator: Allocator) !EnvMap { while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} const key_w = ptr[key_start..i]; + const key = try std.unicode.utf16leToUtf8Alloc(allocator, key_w); + errdefer allocator.free(key); if (ptr[i] == '=') i += 1; const value_start = i; while (ptr[i] != 0) : (i += 1) {} const value_w = ptr[value_start..i]; - - try result.storage.putUtf16NoClobber(key_w, value_w); + const value = try std.unicode.utf16leToUtf8Alloc(allocator, value_w); + errdefer allocator.free(value); i += 1; // skip over null byte - } - try result.storage.reallocUppercaseBuf(); + try result.putMove(key, value); + } return result; } else if (builtin.os.tag == .wasi and !builtin.link_libc) { var environ_count: usize = undefined; diff --git a/lib/std/unicode.zig b/lib/std/unicode.zig index 706b12105a..81a7ed838f 100644 --- a/lib/std/unicode.zig +++ b/lib/std/unicode.zig @@ -710,29 +710,6 @@ pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) !usize { return dest_i; } -pub fn utf8ToUtf16LeWriter(writer: anytype, utf8: []const u8) !usize { - var src_i: usize = 0; - var bytes_written: usize = 0; - while (src_i < utf8.len) { - const n = utf8ByteSequenceLength(utf8[src_i]) catch return error.InvalidUtf8; - const next_src_i = src_i + n; - const codepoint = utf8Decode(utf8[src_i..next_src_i]) catch return error.InvalidUtf8; - if (codepoint < 0x10000) { - const short = @intCast(u16, codepoint); - try writer.writeIntLittle(u16, short); - bytes_written += 2; - } else { - const high = @intCast(u16, (codepoint - 0x10000) >> 10) + 0xD800; - const low = @intCast(u16, codepoint & 0x3FF) + 0xDC00; - try writer.writeIntLittle(u16, high); - try writer.writeIntLittle(u16, low); - bytes_written += 4; - } - src_i = next_src_i; - } - return bytes_written; -} - test "utf8ToUtf16Le" { var utf16le: [2]u16 = [_]u16{0} ** 2; { From 71f69190ef7a79650647cf5422633813756fbe48 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Fri, 4 Feb 2022 23:42:10 -0700 Subject: [PATCH 1471/2031] some fixes to the EnvMap HashContext --- lib/std/build/RunStep.zig | 2 +- lib/std/process.zig | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/std/build/RunStep.zig b/lib/std/build/RunStep.zig index 1e4cca5167..e8cf87a441 100644 --- a/lib/std/build/RunStep.zig +++ b/lib/std/build/RunStep.zig @@ -99,7 +99,7 @@ pub fn clearEnvironment(self: *RunStep) void { pub fn addPathDir(self: *RunStep, search_path: []const u8) void { const env_map = self.getEnvMap(); - var key: []const u8 = "PATH"; + const key = "PATH"; var prev_path = env_map.get(key); if (prev_path) |pp| { diff --git a/lib/std/process.zig b/lib/std/process.zig index 26cde9db51..b460264a8c 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -62,18 +62,20 @@ pub const EnvMap = struct { std.hash_map.default_max_load_percentage, ); + pub const Size = HashMap.Size; + pub const EnvNameHashContext = struct { fn upcase(c: u21) u21 { if (c <= std.math.maxInt(u16)) - return std.os.windows.ntdll.RtlUpcaseUnicodeChar(c); + return std.os.windows.ntdll.RtlUpcaseUnicodeChar(@intCast(u16, c)); return c; } pub fn hash(self: @This(), s: []const u8) u64 { _ = self; if (builtin.os.tag == .windows) { - const h = std.hash.Wyhash.init(0); - var it = std.unicode.Utf8View(s).iterator(); + var h = std.hash.Wyhash.init(0); + var it = std.unicode.Utf8View.initUnchecked(s).iterator(); while (it.nextCodepoint()) |cp| { const cp_upper = upcase(cp); h.update(&[_]u8{ @@ -90,15 +92,15 @@ pub const EnvMap = struct { pub fn eql(self: @This(), a: []const u8, b: []const u8) bool { _ = self; if (builtin.os.tag == .windows) { - var it_a = std.unicode.Utf8View(a).iterator(); - var it_b = std.unicode.Utf8View(b).iterator(); + var it_a = std.unicode.Utf8View.initUnchecked(a).iterator(); + var it_b = std.unicode.Utf8View.initUnchecked(b).iterator(); while (true) { const c_a = it_a.nextCodepoint() orelse break; const c_b = it_b.nextCodepoint() orelse return false; if (upcase(c_a) != upcase(c_b)) return false; } - if (it_b.nextCodepoint()) return false; + if (it_b.nextCodepoint()) |_| return false; } return std.hash_map.eqlString(a, b); } @@ -220,7 +222,7 @@ test "EnvMap" { var it = env.iterator(); var count: EnvMap.Size = 0; while (it.next()) |entry| { - const is_an_expected_name = std.mem.eql(u8, "SOMETHING_NEW", entry.name) or std.mem.eql(u8, "SOMETHING_NEW_AND_LONGER", entry.name); + const is_an_expected_name = std.mem.eql(u8, "SOMETHING_NEW", entry.key_ptr.*) or std.mem.eql(u8, "SOMETHING_NEW_AND_LONGER", entry.key_ptr.*); try testing.expect(is_an_expected_name); count += 1; } From 8492ced0755924b3c4c0d67fb306d65bffd0933a Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sun, 6 Feb 2022 23:30:06 -0700 Subject: [PATCH 1472/2031] incorporate review changes from squeek --- lib/std/process.zig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/std/process.zig b/lib/std/process.zig index b460264a8c..84d41972b5 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -100,7 +100,7 @@ pub const EnvMap = struct { if (upcase(c_a) != upcase(c_b)) return false; } - if (it_b.nextCodepoint()) |_| return false; + return if (it_b.nextCodepoint()) |_| false else true; } return std.hash_map.eqlString(a, b); } @@ -232,6 +232,12 @@ test "EnvMap" { try testing.expect(env.get("SOMETHING_NEW") == null); try testing.expectEqual(@as(EnvMap.Size, 1), env.count()); + + // test Unicode case-insensitivity on Windows + if (builtin.os.tag == .windows) { + try env.put("КИРиллИЦА", "something else"); + try testing.expectEqualStrings("something else", env.get("кириллица").?); + } } /// Returns a snapshot of the environment variables of the current process. From a38e6a64d32974bebb1c338dac5e56a35c8bcac4 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Fri, 18 Feb 2022 14:58:13 -0700 Subject: [PATCH 1473/2031] document that on Windows, all key arguments in EnvMap must be valid utf8 --- lib/std/process.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/std/process.zig b/lib/std/process.zig index 84d41972b5..0b64b5910d 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -128,6 +128,7 @@ pub const EnvMap = struct { /// Same as `put` but the key and value become owned by the EnvMap rather /// than being copied. /// If `putMove` fails, the ownership of key and value does not transfer. + /// On Windows `key` must be a valid UTF-8 string. pub fn putMove(self: *EnvMap, key: []u8, value: []u8) !void { const get_or_put = try self.hash_map.getOrPut(key); if (get_or_put.found_existing) { @@ -139,6 +140,7 @@ pub const EnvMap = struct { } /// `key` and `value` are copied into the EnvMap. + /// On Windows `key` must be a valid UTF-8 string. pub fn put(self: *EnvMap, key: []const u8, value: []const u8) !void { const value_copy = try self.copy(value); errdefer self.free(value_copy); @@ -156,6 +158,7 @@ pub const EnvMap = struct { /// Find the address of the value associated with a key. /// The returned pointer is invalidated if the map resizes. + /// On Windows `key` must be a valid UTF-8 string. pub fn getPtr(self: EnvMap, key: []const u8) ?*[]const u8 { return self.hash_map.getPtr(key); } @@ -163,12 +166,14 @@ pub const EnvMap = struct { /// Return the map's copy of the value associated with /// a key. The returned string is invalidated if this /// key is removed from the map. + /// On Windows `key` must be a valid UTF-8 string. pub fn get(self: EnvMap, key: []const u8) ?[]const u8 { return self.hash_map.get(key); } /// Removes the item from the map and frees its value. /// This invalidates the value returned by get() for this key. + /// On Windows `key` must be a valid UTF-8 string. pub fn remove(self: *EnvMap, key: []const u8) void { const kv = self.hash_map.fetchRemove(key) orelse return; self.free(kv.key); From 2ddfe16e1e21ee9bae5adcbfe4b6f4cdcf61a12b Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sat, 19 Feb 2022 12:33:43 -0700 Subject: [PATCH 1474/2031] fix ntdll extern casing Co-authored-by: Veikka Tuominen --- lib/std/os/windows/ntdll.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/os/windows/ntdll.zig b/lib/std/os/windows/ntdll.zig index 5817e482e7..ca033cc367 100644 --- a/lib/std/os/windows/ntdll.zig +++ b/lib/std/os/windows/ntdll.zig @@ -229,13 +229,13 @@ pub extern "ntdll" fn RtlEqualUnicodeString( CaseInSensitive: BOOLEAN, ) callconv(WINAPI) BOOLEAN; -pub extern "NtDll" fn RtlUpcaseUnicodeString( +pub extern "ntdll" fn RtlUpcaseUnicodeString( DestinationString: *UNICODE_STRING, SourceString: *const UNICODE_STRING, AllocateDestinationString: BOOLEAN, ) callconv(WINAPI) NTSTATUS; -pub extern "NtDll" fn RtlUpcaseUnicodeChar( +pub extern "ntdll" fn RtlUpcaseUnicodeChar( SourceCharacter: u16, ) callconv(WINAPI) u16; From aef642fc0731f18514e5ffd6f743274789774f21 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Wed, 11 May 2022 18:43:41 -0600 Subject: [PATCH 1475/2031] remove RtlUpcaseUnicodeString, no longer needed --- lib/std/os/windows/ntdll.zig | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/std/os/windows/ntdll.zig b/lib/std/os/windows/ntdll.zig index ca033cc367..bf9dc9bd2f 100644 --- a/lib/std/os/windows/ntdll.zig +++ b/lib/std/os/windows/ntdll.zig @@ -229,12 +229,6 @@ pub extern "ntdll" fn RtlEqualUnicodeString( CaseInSensitive: BOOLEAN, ) callconv(WINAPI) BOOLEAN; -pub extern "ntdll" fn RtlUpcaseUnicodeString( - DestinationString: *UNICODE_STRING, - SourceString: *const UNICODE_STRING, - AllocateDestinationString: BOOLEAN, -) callconv(WINAPI) NTSTATUS; - pub extern "ntdll" fn RtlUpcaseUnicodeChar( SourceCharacter: u16, ) callconv(WINAPI) u16; From 1d5ea10bee39c6378c5d41597ed477075c3c667f Mon Sep 17 00:00:00 2001 From: Erik Arvstedt Date: Thu, 12 May 2022 14:13:20 +0200 Subject: [PATCH 1476/2031] std.rand: fixup 'improve random float generation' - Test: Fix bucket counting. Previously, the first hit was not counted. This off-by-one error slightly increased the mean of `*_total_variance`, which decreased the acceptance rate for a particular random seed from 95% to 92.6%. (Irrelevant for test failure because the seed is fixed.) - Improve comments --- lib/std/rand.zig | 8 +++++--- lib/std/rand/test.zig | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/std/rand.zig b/lib/std/rand.zig index 01397085f7..e3c97bf385 100644 --- a/lib/std/rand.zig +++ b/lib/std/rand.zig @@ -247,10 +247,9 @@ pub const Random = struct { /// Return a floating point value evenly distributed in the range [0, 1). pub fn float(r: Random, comptime T: type) T { - // Generate a uniformly random value between for the mantissa. + // Generate a uniformly random value for the mantissa. // Then generate an exponentially biased random value for the exponent. - // Over the previous method, this has the advantage of being able to - // represent every possible value in the available range. + // This covers every possible value in the range. switch (T) { f32 => { // Use 23 random bits for the mantissa, and the rest for the exponent. @@ -259,6 +258,9 @@ pub const Random = struct { const rand = r.int(u64); var rand_lz = @clz(u64, rand | 0x7FFFFF); if (rand_lz == 41) { + // TODO: when #5177 or #489 is implemented, + // tell the compiler it is unlikely (1/2^41) to reach this point. + // (Same for the if branch and the f64 calculations below.) rand_lz += @clz(u64, r.int(u64)); if (rand_lz == 41 + 64) { // It is astronomically unlikely to reach this point. diff --git a/lib/std/rand/test.zig b/lib/std/rand/test.zig index 6915e028f2..7c2016901f 100644 --- a/lib/std/rand/test.zig +++ b/lib/std/rand/test.zig @@ -336,13 +336,13 @@ test "Random float chi-square goodness of fit" { if (f32_put.found_existing) { f32_put.value_ptr.* += 1; } else { - f32_put.value_ptr.* = 0; + f32_put.value_ptr.* = 1; } var f64_put = try f64_hist.getOrPut(@floatToInt(u32, rand_f64 * @intToFloat(f64, num_buckets))); if (f64_put.found_existing) { f64_put.value_ptr.* += 1; } else { - f64_put.value_ptr.* = 0; + f64_put.value_ptr.* = 1; } } @@ -371,7 +371,7 @@ test "Random float chi-square goodness of fit" { } } - // Corresponds to a p-value > 0.05. + // Accept p-values >= 0.05. // Critical value is calculated by opening a Python interpreter and running: // scipy.stats.chi2.isf(0.05, num_buckets - 1) const critical_value = 1073.6426506574246; From 23ef7a80601d553bc9cce24500c9d079883867a4 Mon Sep 17 00:00:00 2001 From: Erik Arvstedt Date: Thu, 12 May 2022 16:00:35 +0200 Subject: [PATCH 1477/2031] std.rand.float: simplify leading zero calculations This saves a `bitwise or` operation in the common case and removes the (slightly magic) mask constants. --- lib/std/rand.zig | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/std/rand.zig b/lib/std/rand.zig index e3c97bf385..980722cdff 100644 --- a/lib/std/rand.zig +++ b/lib/std/rand.zig @@ -256,12 +256,12 @@ pub const Random = struct { // If all 41 bits are zero, generate additional random bits, until a // set bit is found, or 126 bits have been generated. const rand = r.int(u64); - var rand_lz = @clz(u64, rand | 0x7FFFFF); - if (rand_lz == 41) { + var rand_lz = @clz(u64, rand); + if (rand_lz >= 41) { // TODO: when #5177 or #489 is implemented, // tell the compiler it is unlikely (1/2^41) to reach this point. // (Same for the if branch and the f64 calculations below.) - rand_lz += @clz(u64, r.int(u64)); + rand_lz = 41 + @clz(u64, r.int(u64)); if (rand_lz == 41 + 64) { // It is astronomically unlikely to reach this point. rand_lz += @clz(u32, r.int(u32) | 0x7FF); @@ -276,8 +276,9 @@ pub const Random = struct { // If all 12 bits are zero, generate additional random bits, until a // set bit is found, or 1022 bits have been generated. const rand = r.int(u64); - var rand_lz: u64 = @clz(u64, rand | 0xFFFFFFFFFFFFF); - if (rand_lz == 12) { + var rand_lz: u64 = @clz(u64, rand); + if (rand_lz >= 12) { + rand_lz = 12; while (true) { // It is astronomically unlikely for this loop to execute more than once. const addl_rand_lz = @clz(u64, r.int(u64)); From 974af5f29101ce8d000359b749f35a2b07c90a32 Mon Sep 17 00:00:00 2001 From: Travis Staloch Date: Wed, 11 May 2022 16:33:55 -0700 Subject: [PATCH 1478/2031] add std.math.sign --- lib/std/math.zig | 115 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 3 deletions(-) diff --git a/lib/std/math.zig b/lib/std/math.zig index 214ade39ce..97868a4f7e 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -611,15 +611,15 @@ pub fn IntFittingRange(comptime from: comptime_int, comptime to: comptime_int) t if (from == 0 and to == 0) { return u0; } - const sign: std.builtin.Signedness = if (from < 0) .signed else .unsigned; + const signedness: std.builtin.Signedness = if (from < 0) .signed else .unsigned; const largest_positive_integer = max(if (from < 0) (-from) - 1 else from, to); // two's complement const base = log2(largest_positive_integer); const upper = (1 << base) - 1; var magnitude_bits = if (upper >= largest_positive_integer) base else base + 1; - if (sign == .signed) { + if (signedness == .signed) { magnitude_bits += 1; } - return std.meta.Int(sign, magnitude_bits); + return std.meta.Int(signedness, magnitude_bits); } test "IntFittingRange" { @@ -1447,3 +1447,112 @@ pub fn break_f80(x: f80) F80 { .exp = @truncate(u16, int >> 64), }; } + +/// Returns -1, 0, or 1. +/// Supports integer types, vectors of integer types, and float types. +/// Unsigned integer types will always return 0 or 1. +/// TODO: support vectors of floats +/// Branchless. +pub inline fn sign(i: anytype) @TypeOf(i) { + const T = @TypeOf(i); + return switch (@typeInfo(T)) { + .Int, .ComptimeInt => @as(T, @boolToInt(i > 0)) - @boolToInt(i < 0), + .Float, .ComptimeFloat => @intToFloat(T, @boolToInt(i > 0)) - @intToFloat(T, @boolToInt(i < 0)), + .Vector => |vinfo| blk: { + const u1xN = std.meta.Vector(vinfo.len, u1); + break :blk switch (@typeInfo(vinfo.child)) { + .Int => @as(T, @bitCast(u1xN, i > @splat(vinfo.len, @as(vinfo.child, 0)))) - + @as(T, @bitCast(u1xN, i < @splat(vinfo.len, @as(vinfo.child, 0)))), + .Float => @compileError("TODO: add support for vectors of floats once @intToFloat accepts vector types"), + // break :blk @intToFloat(T, @bitCast(u1xN, i > @splat(vinfo.len, @as(vinfo.child, 0)))) - + // @intToFloat(T, @bitCast(u1xN, i < @splat(vinfo.len, @as(vinfo.child, 0)))), + else => @compileError("Expected vector of ints or floats, found " ++ @typeName(T)), + }; + }, + else => @compileError("Expected an int, float or vector of one, found " ++ @typeName(T)), + }; +} + +fn testSign() !void { + // each of the following blocks checks the inputs + // 2, -2, 0, { 2, -2, 0 } provide expected output + // 1, -1, 0, { 1, -1, 0 } for the given T + // (negative values omitted for unsigned types) + { + const T = i8; + try std.testing.expectEqual(@as(T, 1), sign(@as(T, 2))); + try std.testing.expectEqual(@as(T, -1), sign(@as(T, -2))); + try std.testing.expectEqual(@as(T, 0), sign(@as(T, 0))); + try std.testing.expectEqual(@Vector(3, T){ 1, -1, 0 }, sign(@Vector(3, T){ 2, -2, 0 })); + } + { + const T = i32; + try std.testing.expectEqual(@as(T, 1), sign(@as(T, 2))); + try std.testing.expectEqual(@as(T, -1), sign(@as(T, -2))); + try std.testing.expectEqual(@as(T, 0), sign(@as(T, 0))); + try std.testing.expectEqual(@Vector(3, T){ 1, -1, 0 }, sign(@Vector(3, T){ 2, -2, 0 })); + } + { + const T = i64; + try std.testing.expectEqual(@as(T, 1), sign(@as(T, 2))); + try std.testing.expectEqual(@as(T, -1), sign(@as(T, -2))); + try std.testing.expectEqual(@as(T, 0), sign(@as(T, 0))); + try std.testing.expectEqual(@Vector(3, T){ 1, -1, 0 }, sign(@Vector(3, T){ 2, -2, 0 })); + } + { + const T = u8; + try std.testing.expectEqual(@as(T, 1), sign(@as(T, 2))); + try std.testing.expectEqual(@as(T, 0), sign(@as(T, 0))); + try std.testing.expectEqual(@Vector(2, T){ 1, 0 }, sign(@Vector(2, T){ 2, 0 })); + } + { + const T = u32; + try std.testing.expectEqual(@as(T, 1), sign(@as(T, 2))); + try std.testing.expectEqual(@as(T, 0), sign(@as(T, 0))); + try std.testing.expectEqual(@Vector(2, T){ 1, 0 }, sign(@Vector(2, T){ 2, 0 })); + } + { + const T = u64; + try std.testing.expectEqual(@as(T, 1), sign(@as(T, 2))); + try std.testing.expectEqual(@as(T, 0), sign(@as(T, 0))); + try std.testing.expectEqual(@Vector(2, T){ 1, 0 }, sign(@Vector(2, T){ 2, 0 })); + } + { + const T = f16; + try std.testing.expectEqual(@as(T, 1), sign(@as(T, 2))); + try std.testing.expectEqual(@as(T, -1), sign(@as(T, -2))); + try std.testing.expectEqual(@as(T, 0), sign(@as(T, 0))); + // TODO - uncomment once @intToFloat supports vectors + // try std.testing.expectEqual(@Vector(3, T){ 1, -1, 0 }, sign(@Vector(3, T){ 2, -2, 0 })); + } + { + const T = f32; + try std.testing.expectEqual(@as(T, 1), sign(@as(T, 2))); + try std.testing.expectEqual(@as(T, -1), sign(@as(T, -2))); + try std.testing.expectEqual(@as(T, 0), sign(@as(T, 0))); + // TODO - uncomment once @intToFloat supports vectors + // try std.testing.expectEqual(@Vector(3, T){ 1, -1, 0 }, sign(@Vector(3, T){ 2, -2, 0 })); + } + { + const T = f64; + try std.testing.expectEqual(@as(T, 1), sign(@as(T, 2))); + try std.testing.expectEqual(@as(T, -1), sign(@as(T, -2))); + try std.testing.expectEqual(@as(T, 0), sign(@as(T, 0))); + // TODO - uncomment once @intToFloat supports vectors + // try std.testing.expectEqual(@Vector(3, T){ 1, -1, 0 }, sign(@Vector(3, T){ 2, -2, 0 })); + } + + // comptime_int + try std.testing.expectEqual(-1, sign(-10)); + try std.testing.expectEqual(1, sign(10)); + try std.testing.expectEqual(0, sign(0)); + // comptime_float + try std.testing.expectEqual(-1.0, sign(-10.0)); + try std.testing.expectEqual(1.0, sign(10.0)); + try std.testing.expectEqual(0.0, sign(0.0)); +} + +test "sign" { + try testSign(); + comptime try testSign(); +} From 537f9052167a683c922c9815eb9fdce5b0778dc2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 12 May 2022 21:10:01 -0700 Subject: [PATCH 1479/2031] fix bad runtime safety test cases The "slicing operator with sentinel" runtime safety test cases should all have been compile errors in their current forms. In this commit I adjust them to use runtime-known slices before triggering the runtime safety. Furthermore the test cases did not actually have unique names. --- test/runtime_safety.zig | 56 +++++------------------------------------ 1 file changed, 6 insertions(+), 50 deletions(-) diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 0a1e7e00f3..f73226159a 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -95,67 +95,23 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} ; - cases.addRuntimeSafety("slicing operator with sentinel", + cases.addRuntimeSafety("slice with sentinel out of bounds", \\const std = @import("std"); ++ check_panic_msg ++ \\pub fn main() void { \\ var buf = [4]u8{'a','b','c',0}; - \\ const slice = buf[0..4 :0]; + \\ const input: []u8 = &buf; + \\ const slice = input[0..4 :0]; \\ _ = slice; \\} ); - cases.addRuntimeSafety("slicing operator with sentinel", - \\const std = @import("std"); - ++ check_panic_msg ++ - \\pub fn main() void { - \\ var buf = [4]u8{'a','b','c',0}; - \\ const slice = buf[0..:0]; - \\ _ = slice; - \\} - ); - cases.addRuntimeSafety("slicing operator with sentinel", + cases.addRuntimeSafety("empty slice with sentinel out of bounds", \\const std = @import("std"); ++ check_panic_msg ++ \\pub fn main() void { \\ var buf_zero = [0]u8{}; - \\ const slice = buf_zero[0..0 :0]; - \\ _ = slice; - \\} - ); - cases.addRuntimeSafety("slicing operator with sentinel", - \\const std = @import("std"); - ++ check_panic_msg ++ - \\pub fn main() void { - \\ var buf_zero = [0]u8{}; - \\ const slice = buf_zero[0..:0]; - \\ _ = slice; - \\} - ); - cases.addRuntimeSafety("slicing operator with sentinel", - \\const std = @import("std"); - ++ check_panic_msg ++ - \\pub fn main() void { - \\ var buf_sentinel = [2:0]u8{'a','b'}; - \\ @ptrCast(*[3]u8, &buf_sentinel)[2] = 0; - \\ const slice = buf_sentinel[0..3 :0]; - \\ _ = slice; - \\} - ); - cases.addRuntimeSafety("slicing operator with sentinel", - \\const std = @import("std"); - ++ check_panic_msg ++ - \\pub fn main() void { - \\ var buf_slice: []const u8 = &[3]u8{ 'a', 'b', 0 }; - \\ const slice = buf_slice[0..3 :0]; - \\ _ = slice; - \\} - ); - cases.addRuntimeSafety("slicing operator with sentinel", - \\const std = @import("std"); - ++ check_panic_msg ++ - \\pub fn main() void { - \\ var buf_slice: []const u8 = &[3]u8{ 'a', 'b', 0 }; - \\ const slice = buf_slice[0.. :0]; + \\ const input: []u8 = &buf_zero; + \\ const slice = input[0..0 :0]; \\ _ = slice; \\} ); From aceb7e18bd1eb25affe8118e01e066c9e6bb0c04 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Fri, 13 May 2022 08:52:06 -0600 Subject: [PATCH 1480/2031] std.os.linux: fix signature of setgroups the list parameter should be a multi-item pointer rather than a single-item pointer. see: https://linux.die.net/man/2/setgroups > setgroups() sets the supplementary group IDs for the calling process... > the size argument specifies the number of supplementary group IDs in the buffer pointed to by list. --- lib/std/os/linux.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 739b924b2c..94e2dd47b9 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1051,7 +1051,7 @@ pub fn getgroups(size: usize, list: *gid_t) usize { } } -pub fn setgroups(size: usize, list: *const gid_t) usize { +pub fn setgroups(size: usize, list: [*]const gid_t) usize { if (@hasField(SYS, "setgroups32")) { return syscall2(.setgroups32, size, @ptrToInt(list)); } else { From fb0692334ed64a995690c21525fc3e729e63f424 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 13 May 2022 22:59:06 +0700 Subject: [PATCH 1481/2031] target: Rename sparcv9 -> sparc64 Rename all references of sparcv9 to sparc64, to make Zig align more with other projects. Also, added new function to convert glibc arch name to Zig arch name, since it refers to the architecture as sparcv9. This is based on the suggestion by @kubkon in PR 11847. (https://github.com/ziglang/zig/pull/11487#pullrequestreview-963761757) --- CMakeLists.txt | 10 ++-- doc/langref.html.in | 4 +- lib/c.zig | 2 +- lib/compiler_rt/clear_cache.zig | 2 +- lib/std/Thread.zig | 2 +- lib/std/builtin.zig | 6 +- lib/std/c/linux.zig | 4 +- lib/std/c/minix.zig | 2 +- lib/std/c/netbsd.zig | 6 +- lib/std/c/openbsd.zig | 2 +- lib/std/debug.zig | 2 +- lib/std/elf.zig | 2 +- lib/std/mem.zig | 2 +- lib/std/os/linux.zig | 12 ++-- lib/std/os/linux/ioctl.zig | 2 +- lib/std/os/linux/tls.zig | 4 +- lib/std/start.zig | 6 +- lib/std/target.zig | 30 +++++----- lib/std/zig/system/NativeTargetInfo.zig | 2 +- lib/std/zig/system/linux.zig | 4 +- src/Compilation.zig | 2 +- src/arch/{sparcv9 => sparc64}/CodeGen.zig | 8 +-- src/arch/{sparcv9 => sparc64}/Emit.zig | 6 +- src/arch/{sparcv9 => sparc64}/Mir.zig | 0 src/arch/{sparcv9 => sparc64}/abi.zig | 0 src/arch/{sparcv9 => sparc64}/bits.zig | 0 src/codegen.zig | 2 +- src/codegen/llvm.zig | 4 +- src/glibc.zig | 55 +++++++++++++++++-- src/link/Elf.zig | 4 +- src/link/Plan9/aout.zig | 2 +- src/stage1/codegen.cpp | 9 ++- src/target.zig | 12 ++-- src/type.zig | 4 +- test/behavior/align.zig | 2 +- test/behavior/vector.zig | 2 +- .../hello_world.zig | 2 +- tools/process_headers.zig | 4 +- 38 files changed, 139 insertions(+), 85 deletions(-) rename src/arch/{sparcv9 => sparc64}/CodeGen.zig (99%) rename src/arch/{sparcv9 => sparc64}/Emit.zig (98%) rename src/arch/{sparcv9 => sparc64}/Mir.zig (100%) rename src/arch/{sparcv9 => sparc64}/abi.zig (100%) rename src/arch/{sparcv9 => sparc64}/bits.zig (100%) rename test/cases/{sparcv9-linux => sparc64-linux}/hello_world.zig (94%) diff --git a/CMakeLists.txt b/CMakeLists.txt index dd57918073..cc962fcff1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -612,11 +612,11 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/arch/riscv64/Mir.zig" "${CMAKE_SOURCE_DIR}/src/arch/riscv64/bits.zig" "${CMAKE_SOURCE_DIR}/src/arch/riscv64/abi.zig" - "${CMAKE_SOURCE_DIR}/src/arch/sparcv9/CodeGen.zig" - "${CMAKE_SOURCE_DIR}/src/arch/sparcv9/Emit.zig" - "${CMAKE_SOURCE_DIR}/src/arch/sparcv9/Mir.zig" - "${CMAKE_SOURCE_DIR}/src/arch/sparcv9/bits.zig" - "${CMAKE_SOURCE_DIR}/src/arch/sparcv9/abi.zig" + "${CMAKE_SOURCE_DIR}/src/arch/sparc64/CodeGen.zig" + "${CMAKE_SOURCE_DIR}/src/arch/sparc64/Emit.zig" + "${CMAKE_SOURCE_DIR}/src/arch/sparc64/Mir.zig" + "${CMAKE_SOURCE_DIR}/src/arch/sparc64/bits.zig" + "${CMAKE_SOURCE_DIR}/src/arch/sparc64/abi.zig" "${CMAKE_SOURCE_DIR}/src/arch/wasm/CodeGen.zig" "${CMAKE_SOURCE_DIR}/src/arch/wasm/Emit.zig" "${CMAKE_SOURCE_DIR}/src/arch/wasm/Mir.zig" diff --git a/doc/langref.html.in b/doc/langref.html.in index f5d3c0b7c1..32320d5113 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -11392,7 +11392,7 @@ Architectures: riscv32 riscv64 sparc - sparcv9 + sparc64 sparcel s390x thumb @@ -11543,7 +11543,7 @@ Available libcs: s390x-linux-gnu s390x-linux-musl sparc-linux-gnu - sparcv9-linux-gnu + sparc64-linux-gnu wasm32-freestanding-musl wasm32-wasi-musl x86_64-linux-gnu diff --git a/lib/c.zig b/lib/c.zig index 525bdd267d..30c6e0cd76 100644 --- a/lib/c.zig +++ b/lib/c.zig @@ -625,7 +625,7 @@ fn clone() callconv(.Naked) void { \\ sc ); }, - .sparcv9 => { + .sparc64 => { // __clone(func, stack, flags, arg, ptid, tls, ctid) // i0, i1, i2, i3, i4, i5, sp // syscall(SYS_clone, flags, stack, ptid, tls, ctid) diff --git a/lib/compiler_rt/clear_cache.zig b/lib/compiler_rt/clear_cache.zig index d6ce02249e..0765a23811 100644 --- a/lib/compiler_rt/clear_cache.zig +++ b/lib/compiler_rt/clear_cache.zig @@ -36,7 +36,7 @@ pub fn clear_cache(start: usize, end: usize) callconv(.C) void { else => false, }; const sparc = switch (arch) { - .sparc, .sparcv9, .sparcel => true, + .sparc, .sparc64, .sparcel => true, else => false, }; const apple = switch (os) { diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 45d2ac0040..9e29c82672 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -859,7 +859,7 @@ const LinuxThreadImpl = struct { [len] "r" (self.mapped.len), : "memory" ), - .sparcv9 => asm volatile ( + .sparc64 => asm volatile ( \\ # SPARCs really don't like it when active stack frames \\ # is unmapped (it will result in a segfault), so we \\ # force-deactivate it by running `restore` until diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index bf9a2d5682..53786c8661 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -727,8 +727,8 @@ pub const CompilerBackend = enum(u64) { /// riscv64 backend. stage2_riscv64 = 9, /// The reference implementation self-hosted compiler of Zig, using the - /// sparcv9 backend. - stage2_sparcv9 = 10, + /// sparc64 backend. + stage2_sparc64 = 10, _, }; @@ -775,7 +775,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_x86 or builtin.zig_backend == .stage2_riscv64 or - builtin.zig_backend == .stage2_sparcv9) + builtin.zig_backend == .stage2_sparc64) { while (true) { @breakpoint(); diff --git a/lib/std/c/linux.zig b/lib/std/c/linux.zig index 5f96fe3fe0..62a7d3e964 100644 --- a/lib/std/c/linux.zig +++ b/lib/std/c/linux.zig @@ -115,7 +115,7 @@ pub const _errno = switch (native_abi) { }; pub const Stat = switch (native_arch) { - .sparcv9 => extern struct { + .sparc64 => extern struct { dev: u64, __pad1: u16, ino: ino_t, @@ -345,7 +345,7 @@ const __SIZEOF_PTHREAD_MUTEX_T = switch (native_abi) { .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (native_arch) { .aarch64 => 48, .x86_64 => if (native_abi == .gnux32) 40 else 32, - .mips64, .powerpc64, .powerpc64le, .sparcv9 => 40, + .mips64, .powerpc64, .powerpc64le, .sparc64 => 40, else => if (@sizeOf(usize) == 8) 40 else 24, }, .android => if (@sizeOf(usize) == 8) 40 else 4, diff --git a/lib/std/c/minix.zig b/lib/std/c/minix.zig index 9c644a7986..62cefc14fb 100644 --- a/lib/std/c/minix.zig +++ b/lib/std/c/minix.zig @@ -11,7 +11,7 @@ const __SIZEOF_PTHREAD_MUTEX_T = switch (builtin.abi) { .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (builtin.cpu.arch) { .aarch64 => 48, .x86_64 => if (builtin.abi == .gnux32) 40 else 32, - .mips64, .powerpc64, .powerpc64le, .sparcv9 => 40, + .mips64, .powerpc64, .powerpc64le, .sparc64 => 40, else => if (@sizeOf(usize) == 8) 40 else 24, }, else => unreachable, diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig index b1f8e8846e..e481de0996 100644 --- a/lib/std/c/netbsd.zig +++ b/lib/std/c/netbsd.zig @@ -99,14 +99,14 @@ const pthread_spin_t = switch (builtin.cpu.arch) { .powerpc, .powerpc64, .powerpc64le => i32, .i386, .x86_64 => u8, .arm, .armeb, .thumb, .thumbeb => i32, - .sparc, .sparcel, .sparcv9 => u8, + .sparc, .sparcel, .sparc64 => u8, .riscv32, .riscv64 => u32, else => @compileError("undefined pthread_spin_t for this arch"), }; const padded_pthread_spin_t = switch (builtin.cpu.arch) { .i386, .x86_64 => u32, - .sparc, .sparcel, .sparcv9 => u32, + .sparc, .sparcel, .sparc64 => u32, else => pthread_spin_t, }; @@ -1070,7 +1070,7 @@ pub const ucontext_t = extern struct { .i386 => 4, .mips, .mipsel, .mips64, .mips64el => 14, .arm, .armeb, .thumb, .thumbeb => 1, - .sparc, .sparcel, .sparcv9 => if (@sizeOf(usize) == 4) 43 else 8, + .sparc, .sparcel, .sparc64 => if (@sizeOf(usize) == 4) 43 else 8, else => 0, } ]u32, diff --git a/lib/std/c/openbsd.zig b/lib/std/c/openbsd.zig index 0aa90c741a..71c9d21ce3 100644 --- a/lib/std/c/openbsd.zig +++ b/lib/std/c/openbsd.zig @@ -1263,7 +1263,7 @@ pub const E = enum(u16) { const _MAX_PAGE_SHIFT = switch (builtin.cpu.arch) { .i386 => 12, - .sparcv9 => 13, + .sparc64 => 13, }; pub const MINSIGSTKSZ = 1 << _MAX_PAGE_SHIFT; pub const SIGSTKSZ = MINSIGSTKSZ + (1 << _MAX_PAGE_SHIFT) * 4; diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 1a34d67f9d..83667c758b 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -401,7 +401,7 @@ pub const StackIterator = struct { fp: usize, pub fn init(first_address: ?usize, fp: ?usize) StackIterator { - if (native_arch == .sparcv9) { + if (native_arch == .sparc64) { // Flush all the register windows on stack. asm volatile ( \\ flushw diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 2ea928f891..d80ae2f6a0 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -1511,7 +1511,7 @@ pub const EM = enum(u16) { .RISCV => .riscv64, .X86_64 => .x86_64, .BPF => .bpfel, - .SPARCV9 => .sparcv9, + .SPARCV9 => .sparc64, .S390 => .s390x, .SPU_2 => .spu_2, // there's many cases we don't (yet) handle, or will never have a diff --git a/lib/std/mem.zig b/lib/std/mem.zig index eec9d4d61f..73cc24c45c 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -18,7 +18,7 @@ pub const page_size = switch (builtin.cpu.arch) { .macos, .ios, .watchos, .tvos => 16 * 1024, else => 4 * 1024, }, - .sparcv9 => 8 * 1024, + .sparc64 => 8 * 1024, else => 4 * 1024, }; diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 94e2dd47b9..fec9f55ad1 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -38,7 +38,7 @@ const arch_bits = switch (native_arch) { .aarch64 => @import("linux/arm64.zig"), .arm, .thumb => @import("linux/arm-eabi.zig"), .riscv64 => @import("linux/riscv64.zig"), - .sparcv9 => @import("linux/sparc64.zig"), + .sparc64 => @import("linux/sparc64.zig"), .mips, .mipsel => @import("linux/mips.zig"), .powerpc => @import("linux/powerpc.zig"), .powerpc64, .powerpc64le => @import("linux/powerpc64.zig"), @@ -1098,7 +1098,7 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact const result = switch (native_arch) { // The sparc version of rt_sigaction needs the restorer function to be passed as an argument too. - .sparc, .sparcv9 => syscall5(.rt_sigaction, sig, ksa_arg, oldksa_arg, @ptrToInt(ksa.restorer), mask_size), + .sparc, .sparc64 => syscall5(.rt_sigaction, sig, ksa_arg, oldksa_arg, @ptrToInt(ksa.restorer), mask_size), else => syscall4(.rt_sigaction, sig, ksa_arg, oldksa_arg, mask_size), }; if (getErrno(result) != .SUCCESS) return result; @@ -1698,7 +1698,7 @@ pub fn seccomp(operation: u32, flags: u32, args: ?*const anyopaque) usize { pub const E = switch (native_arch) { .mips, .mipsel => @import("linux/errno/mips.zig").E, - .sparc, .sparcel, .sparcv9 => @import("linux/errno/sparc.zig").E, + .sparc, .sparcel, .sparc64 => @import("linux/errno/sparc.zig").E, else => @import("linux/errno/generic.zig").E, }; @@ -4090,7 +4090,7 @@ pub const V = switch (native_arch) { pub const LNEXT = 15; pub const DISCARD = 16; }, - .sparc, .sparcv9 => struct { + .sparc, .sparc64 => struct { pub const INTR = 0; pub const QUIT = 1; pub const ERASE = 2; @@ -5427,7 +5427,7 @@ pub const AUDIT = struct { .aarch64 => .AARCH64, .arm, .thumb => .ARM, .riscv64 => .RISCV64, - .sparcv9 => .SPARC64, + .sparc64 => .SPARC64, .mips => .MIPS, .mipsel => .MIPSEL, .powerpc => .PPC, @@ -5454,7 +5454,7 @@ pub const AUDIT = struct { RISCV64 = toAudit(.riscv64), S390X = toAudit(.s390x), SPARC = toAudit(.sparc), - SPARC64 = toAudit(.sparcv9), + SPARC64 = toAudit(.sparc64), X86_64 = toAudit(.x86_64), fn toAudit(arch: std.Target.Cpu.Arch) u32 { diff --git a/lib/std/os/linux/ioctl.zig b/lib/std/os/linux/ioctl.zig index 35ff1bfc32..96ec96c306 100644 --- a/lib/std/os/linux/ioctl.zig +++ b/lib/std/os/linux/ioctl.zig @@ -10,7 +10,7 @@ const bits = switch (@import("builtin").cpu.arch) { .powerpc64, .powerpc64le, .sparc, - .sparcv9, + .sparc64, .sparcel, => .{ .size = 13, .dir = 3, .none = 1, .read = 2, .write = 4 }, else => .{ .size = 14, .dir = 2, .none = 0, .read = 2, .write = 1 }, diff --git a/lib/std/os/linux/tls.zig b/lib/std/os/linux/tls.zig index 770ab9b92c..d91f7d7a9e 100644 --- a/lib/std/os/linux/tls.zig +++ b/lib/std/os/linux/tls.zig @@ -49,7 +49,7 @@ const TLSVariant = enum { const tls_variant = switch (native_arch) { .arm, .armeb, .thumb, .aarch64, .aarch64_be, .riscv32, .riscv64, .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => TLSVariant.VariantI, - .x86_64, .i386, .sparcv9 => TLSVariant.VariantII, + .x86_64, .i386, .sparc64 => TLSVariant.VariantII, else => @compileError("undefined tls_variant for this architecture"), }; @@ -174,7 +174,7 @@ pub fn setThreadPointer(addr: usize) void { : [addr] "r" (addr), ); }, - .sparcv9 => { + .sparc64 => { asm volatile ( \\ mov %[addr], %%g7 : diff --git a/lib/std/start.zig b/lib/std/start.zig index 81c7942bc1..516d6363d7 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -29,7 +29,7 @@ comptime { builtin.zig_backend == .stage2_aarch64 or builtin.zig_backend == .stage2_arm or builtin.zig_backend == .stage2_riscv64 or - builtin.zig_backend == .stage2_sparcv9) + builtin.zig_backend == .stage2_sparc64) { if (builtin.output_mode == .Exe) { if ((builtin.link_libc or builtin.object_format == .c) and @hasDecl(root, "main")) { @@ -164,7 +164,7 @@ fn exit2(code: usize) noreturn { : "rcx", "r11", "memory" ); }, - .sparcv9 => { + .sparc64 => { asm volatile ("ta 0x6d" : : [number] "{g1}" (1), @@ -323,7 +323,7 @@ fn _start() callconv(.Naked) noreturn { : "r0" ); }, - .sparcv9 => { + .sparc64 => { // argc is stored after a register window (16 registers) plus stack bias argc_argv_ptr = asm ( \\ mov %%g0, %%i6 diff --git a/lib/std/target.zig b/lib/std/target.zig index 2db1415cd3..b11408cfce 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -785,7 +785,7 @@ pub const Target = struct { riscv32, riscv64, sparc, - sparcv9, + sparc64, sparcel, s390x, tce, @@ -884,7 +884,7 @@ pub const Target = struct { pub fn isSPARC(arch: Arch) bool { return switch (arch) { - .sparc, .sparcel, .sparcv9 => true, + .sparc, .sparcel, .sparc64 => true, else => false, }; } @@ -964,7 +964,7 @@ pub const Target = struct { .bpfel => .BPF, .bpfeb => .BPF, .csky => .CSKY, - .sparcv9 => .SPARCV9, + .sparc64 => .SPARCV9, .s390x => .S390, .ve => .NONE, .spu_2 => .SPU_2, @@ -1025,7 +1025,7 @@ pub const Target = struct { .bpfel => .Unknown, .bpfeb => .Unknown, .csky => .Unknown, - .sparcv9 => .Unknown, + .sparc64 => .Unknown, .s390x => .Unknown, .ve => .Unknown, .spu_2 => .Unknown, @@ -1092,7 +1092,7 @@ pub const Target = struct { .powerpc64, .thumbeb, .sparc, - .sparcv9, + .sparc64, .tce, .lanai, .s390x, @@ -1159,7 +1159,7 @@ pub const Target = struct { .amdgcn, .bpfel, .bpfeb, - .sparcv9, + .sparc64, .s390x, .ve, .spirv64, @@ -1177,7 +1177,7 @@ pub const Target = struct { .powerpc, .powerpcle, .powerpc64, .powerpc64le => "powerpc", .amdgcn => "amdgpu", .riscv32, .riscv64 => "riscv", - .sparc, .sparcv9, .sparcel => "sparc", + .sparc, .sparc64, .sparcel => "sparc", .s390x => "systemz", .i386, .x86_64 => "x86", .nvptx, .nvptx64 => "nvptx", @@ -1200,7 +1200,7 @@ pub const Target = struct { .powerpc, .powerpcle, .powerpc64, .powerpc64le => &powerpc.all_features, .amdgcn => &amdgpu.all_features, .riscv32, .riscv64 => &riscv.all_features, - .sparc, .sparcv9, .sparcel => &sparc.all_features, + .sparc, .sparc64, .sparcel => &sparc.all_features, .spirv32, .spirv64 => &spirv.all_features, .s390x => &systemz.all_features, .i386, .x86_64 => &x86.all_features, @@ -1225,7 +1225,7 @@ pub const Target = struct { .powerpc, .powerpcle, .powerpc64, .powerpc64le => comptime allCpusFromDecls(powerpc.cpu), .amdgcn => comptime allCpusFromDecls(amdgpu.cpu), .riscv32, .riscv64 => comptime allCpusFromDecls(riscv.cpu), - .sparc, .sparcv9, .sparcel => comptime allCpusFromDecls(sparc.cpu), + .sparc, .sparc64, .sparcel => comptime allCpusFromDecls(sparc.cpu), .s390x => comptime allCpusFromDecls(systemz.cpu), .i386, .x86_64 => comptime allCpusFromDecls(x86.cpu), .nvptx, .nvptx64 => comptime allCpusFromDecls(nvptx.cpu), @@ -1286,7 +1286,7 @@ pub const Target = struct { .riscv32 => &riscv.cpu.generic_rv32, .riscv64 => &riscv.cpu.generic_rv64, .sparc, .sparcel => &sparc.cpu.generic, - .sparcv9 => &sparc.cpu.v9, + .sparc64 => &sparc.cpu.v9, // 64-bit SPARC needs v9 as the baseline .s390x => &systemz.cpu.generic, .i386 => &x86.cpu._i386, .x86_64 => &x86.cpu.x86_64, @@ -1587,7 +1587,7 @@ pub const Target = struct { .powerpc, .powerpcle => return copy(&result, "/lib/ld.so.1"), .powerpc64, .powerpc64le => return copy(&result, "/lib64/ld64.so.2"), .s390x => return copy(&result, "/lib64/ld64.so.1"), - .sparcv9 => return copy(&result, "/lib64/ld-linux.so.2"), + .sparc64 => return copy(&result, "/lib64/ld-linux.so.2"), .x86_64 => return copy(&result, switch (self.abi) { .gnux32 => "/libx32/ld-linux-x32.so.2", else => "/lib64/ld-linux-x86-64.so.2", @@ -1735,7 +1735,7 @@ pub const Target = struct { .mips64, .mips64el, .sparc, - .sparcv9, + .sparc64, .sparcel, .powerpc, .powerpcle, @@ -1760,7 +1760,7 @@ pub const Target = struct { .mips64, .mips64el, .sparc, - .sparcv9, + .sparc64, .sparcel, .powerpc, .powerpcle, @@ -1810,14 +1810,14 @@ pub const Target = struct { // 1. Better machine code when loading into SIMD register. // 2. The C ABI wants 16 for extern structs. // 3. 16-byte cmpxchg needs 16-byte alignment. - // Same logic for riscv64, powerpc64, mips64, sparcv9. + // Same logic for riscv64, powerpc64, mips64, sparc64. .x86_64, .riscv64, .powerpc64, .powerpc64le, .mips64, .mips64el, - .sparcv9, + .sparc64, // Even LLVMABIAlignmentOfType(i128) agrees on these targets. .aarch64, diff --git a/lib/std/zig/system/NativeTargetInfo.zig b/lib/std/zig/system/NativeTargetInfo.zig index 9055d1c215..0956e469af 100644 --- a/lib/std/zig/system/NativeTargetInfo.zig +++ b/lib/std/zig/system/NativeTargetInfo.zig @@ -931,7 +931,7 @@ pub fn getExternalExecutor( .riscv64 => Executor{ .qemu = "qemu-riscv64" }, .s390x => Executor{ .qemu = "qemu-s390x" }, .sparc => Executor{ .qemu = "qemu-sparc" }, - .sparcv9 => Executor{ .qemu = "qemu-sparc64" }, + .sparc64 => Executor{ .qemu = "qemu-sparc64" }, .x86_64 => Executor{ .qemu = "qemu-x86_64" }, else => return bad_result, }; diff --git a/lib/std/zig/system/linux.zig b/lib/std/zig/system/linux.zig index 0594ff1e2f..ee1e987464 100644 --- a/lib/std/zig/system/linux.zig +++ b/lib/std/zig/system/linux.zig @@ -66,7 +66,7 @@ const SparcCpuinfoImpl = struct { const SparcCpuinfoParser = CpuinfoParser(SparcCpuinfoImpl); test "cpuinfo: SPARC" { - try testParser(SparcCpuinfoParser, .sparcv9, &Target.sparc.cpu.niagara2, + try testParser(SparcCpuinfoParser, .sparc64, &Target.sparc.cpu.niagara2, \\cpu : UltraSparc T2 (Niagara2) \\fpu : UltraSparc T2 integrated FPU \\pmu : niagara2 @@ -456,7 +456,7 @@ pub fn detectNativeCpuAndFeatures() ?Target.Cpu { .arm, .armeb, .thumb, .thumbeb, .aarch64, .aarch64_be, .aarch64_32 => { return ArmCpuinfoParser.parse(current_arch, f.reader()) catch null; }, - .sparcv9 => { + .sparc64 => { return SparcCpuinfoParser.parse(current_arch, f.reader()) catch null; }, .powerpc, .powerpcle, .powerpc64, .powerpc64le => { diff --git a/src/Compilation.zig b/src/Compilation.zig index 5e4ab0ec12..2091502da2 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -4566,7 +4566,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca .i386 => .stage2_x86, .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64, .riscv64 => .stage2_riscv64, - .sparcv9 => .stage2_sparcv9, + .sparc64 => .stage2_sparc64, else => .other, }; }; diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparc64/CodeGen.zig similarity index 99% rename from src/arch/sparcv9/CodeGen.zig rename to src/arch/sparc64/CodeGen.zig index 35d9a78dcd..0745cd46c9 100644 --- a/src/arch/sparcv9/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -338,7 +338,7 @@ pub fn generate( fn gen(self: *Self) !void { const cc = self.fn_type.fnCallingConvention(); if (cc != .Naked) { - // TODO Finish function prologue and epilogue for sparcv9. + // TODO Finish function prologue and epilogue for sparc64. // save %sp, stack_save_area, %sp const save_inst = try self.addInst(.{ @@ -378,7 +378,7 @@ fn gen(self: *Self) !void { for (self.exitlude_jump_relocs.items) |jmp_reloc| { _ = jmp_reloc; - return self.fail("TODO add branches in sparcv9", .{}); + return self.fail("TODO add branches in sparc64", .{}); } // Backpatch stack offset @@ -1668,11 +1668,11 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type, role: RegisterView) .callee => .{ .register = abi.c_abi_int_return_regs_callee_view[0] }, }; } else { - return self.fail("TODO support more return values for sparcv9", .{}); + return self.fail("TODO support more return values for sparc64", .{}); } } }, - else => return self.fail("TODO implement function parameters for {} on sparcv9", .{cc}), + else => return self.fail("TODO implement function parameters for {} on sparc64", .{cc}), } return result; diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparc64/Emit.zig similarity index 98% rename from src/arch/sparcv9/Emit.zig rename to src/arch/sparc64/Emit.zig index 4da2215c46..81ae062c61 100644 --- a/src/arch/sparcv9/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -51,9 +51,9 @@ pub fn emitMir( .add => try emit.mirArithmetic3Op(inst), - .bpcc => @panic("TODO implement sparcv9 bpcc"), + .bpcc => @panic("TODO implement sparc64 bpcc"), - .call => @panic("TODO implement sparcv9 call"), + .call => @panic("TODO implement sparc64 call"), .jmpl => try emit.mirArithmetic3Op(inst), @@ -73,7 +73,7 @@ pub fn emitMir( .sethi => try emit.mirSethi(inst), - .sllx => @panic("TODO implement sparcv9 sllx"), + .sllx => @panic("TODO implement sparc64 sllx"), .stb => try emit.mirArithmetic3Op(inst), .sth => try emit.mirArithmetic3Op(inst), diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparc64/Mir.zig similarity index 100% rename from src/arch/sparcv9/Mir.zig rename to src/arch/sparc64/Mir.zig diff --git a/src/arch/sparcv9/abi.zig b/src/arch/sparc64/abi.zig similarity index 100% rename from src/arch/sparcv9/abi.zig rename to src/arch/sparc64/abi.zig diff --git a/src/arch/sparcv9/bits.zig b/src/arch/sparc64/bits.zig similarity index 100% rename from src/arch/sparcv9/bits.zig rename to src/arch/sparc64/bits.zig diff --git a/src/codegen.zig b/src/codegen.zig index debd7b5e9d..def69d952f 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -103,7 +103,7 @@ pub fn generateFunction( //.riscv32 => return Function(.riscv32).generate(bin_file, src_loc, func, air, liveness, code, debug_output), .riscv64 => return @import("arch/riscv64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.sparc => return Function(.sparc).generate(bin_file, src_loc, func, air, liveness, code, debug_output), - .sparcv9 => return @import("arch/sparcv9/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), + .sparc64 => return @import("arch/sparc64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.sparcel => return Function(.sparcel).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.s390x => return Function(.s390x).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.tce => return Function(.tce).generate(bin_file, src_loc, func, air, liveness, code, debug_output), diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 5e70710046..5a8516b363 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -53,7 +53,7 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![:0]u8 { .riscv32 => "riscv32", .riscv64 => "riscv64", .sparc => "sparc", - .sparcv9 => "sparcv9", + .sparc64 => "sparc64", .sparcel => "sparcel", .s390x => "s390x", .tce => "tce", @@ -7797,7 +7797,7 @@ fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void { llvm.LLVMInitializeRISCVAsmPrinter(); llvm.LLVMInitializeRISCVAsmParser(); }, - .sparc, .sparcv9, .sparcel => { + .sparc, .sparc64, .sparcel => { llvm.LLVMInitializeSparcTarget(); llvm.LLVMInitializeSparcTargetInfo(); llvm.LLVMInitializeSparcTargetMC(); diff --git a/src/glibc.zig b/src/glibc.zig index dee4e50e37..4deac5275f 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -43,6 +43,29 @@ pub const libs = [_]Lib{ .{ .name = "util", .sover = 1 }, }; +// glibc's naming of Zig architectures +const Arch = enum(c_int) { + arm, + armeb, + aarch64, + aarch64_be, + mips, + mipsel, + mips64, + mips64el, + powerpc, + powerpc64, + powerpc64le, + riscv32, + riscv64, + sparc, + sparcv9, + sparcel, + s390x, + i386, + x86_64, +}; + pub const LoadMetaDataError = error{ /// The files that ship with the Zig compiler were unable to be read, or otherwise had malformed data. ZigInstallationCorrupt, @@ -134,7 +157,7 @@ pub fn loadMetaData(gpa: Allocator, zig_lib_dir: fs.Dir) LoadMetaDataError!*ABI log.err("abilists: expected ABI name", .{}); return error.ZigInstallationCorrupt; }; - const arch_tag = std.meta.stringToEnum(std.Target.Cpu.Arch, arch_name) orelse { + const arch_tag = std.meta.stringToEnum(Arch, arch_name) orelse { log.err("abilists: unrecognized arch: '{s}'", .{arch_name}); return error.ZigInstallationCorrupt; }; @@ -148,7 +171,7 @@ pub fn loadMetaData(gpa: Allocator, zig_lib_dir: fs.Dir) LoadMetaDataError!*ABI }; targets[i] = .{ - .arch = arch_tag, + .arch = glibcToZigArch(arch_tag), .os = .linux, .abi = abi_tag, }; @@ -381,7 +404,7 @@ fn start_asm_path(comp: *Compilation, arena: Allocator, basename: []const u8) ![ const arch = comp.getTarget().cpu.arch; const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le; const is_aarch64 = arch == .aarch64 or arch == .aarch64_be; - const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparcv9; + const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparc64; const is_64 = arch.ptrBitWidth() == 64; const s = path.sep_str; @@ -519,7 +542,7 @@ fn add_include_dirs_arch( const is_x86 = arch == .i386 or arch == .x86_64; const is_aarch64 = arch == .aarch64 or arch == .aarch64_be; const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le; - const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparcv9; + const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparc64; const is_64 = arch.ptrBitWidth() == 64; const s = path.sep_str; @@ -1115,6 +1138,30 @@ fn buildSharedLib( try sub_compilation.updateSubCompilation(); } +fn glibcToZigArch(arch_tag: Arch) std.Target.Cpu.Arch { + return switch (arch_tag) { + .arm => .arm, + .armeb => .armeb, + .aarch64 => .aarch64, + .aarch64_be => .aarch64_be, + .mips => .mips, + .mipsel => .mipsel, + .mips64 => .mips64, + .mips64el => .mips64el, + .powerpc => .powerpc, + .powerpc64 => .powerpc64, + .powerpc64le => .powerpc64le, + .riscv32 => .riscv32, + .riscv64 => .riscv64, + .sparc => .sparc, + .sparcv9 => .sparc64, // In glibc, sparc64 is called sparcv9. + .sparcel => .sparcel, + .s390x => .s390x, + .i386 => .i386, + .x86_64 => .x86_64, + }; +} + // Return true if glibc has crti/crtn sources for that architecture. pub fn needsCrtiCrtn(target: std.Target) bool { return switch (target.cpu.arch) { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 11a701fcf9..be366076b4 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -307,7 +307,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { const page_size: u32 = switch (options.target.cpu.arch) { .powerpc64le => 0x10000, - .sparcv9 => 0x2000, + .sparc64 => 0x2000, else => 0x1000, }; @@ -2940,7 +2940,7 @@ fn getLDMOption(target: std.Target) ?[]const u8 { .powerpc64 => return "elf64ppc", .powerpc64le => return "elf64lppc", .sparc, .sparcel => return "elf32_sparc", - .sparcv9 => return "elf64_sparc", + .sparc64 => return "elf64_sparc", .mips => return "elf32btsmip", .mipsel => return "elf32ltsmip", .mips64 => { diff --git a/src/link/Plan9/aout.zig b/src/link/Plan9/aout.zig index 39994516fa..09f39d8990 100644 --- a/src/link/Plan9/aout.zig +++ b/src/link/Plan9/aout.zig @@ -110,7 +110,7 @@ pub const R_MAGIC = _MAGIC(HDR_MAGIC, 28); // arm64 pub fn magicFromArch(arch: std.Target.Cpu.Arch) !u32 { return switch (arch) { .i386 => I_MAGIC, - .sparc => K_MAGIC, // TODO should sparcv9 and sparcel go here? + .sparc => K_MAGIC, // TODO should sparc64 and sparcel go here? .mips => V_MAGIC, .arm => E_MAGIC, .aarch64 => R_MAGIC, diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index d101030c33..d6fb6d4753 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -21,6 +21,7 @@ #include "zigendian.h" #include +#include #include #include @@ -3596,7 +3597,7 @@ static LLVMValueRef gen_soft_float_bin_op(CodeGen *g, LLVMValueRef op1_value, LL result = LLVMBuildLoad(g->builder, result, ""); } - // Some operations are implemented as compound ops and require us to perform some + // Some operations are implemented as compound ops and require us to perform some // more operations before we obtain the final result switch (op_id) { case IrBinOpDivTrunc: @@ -9983,6 +9984,12 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { cur_arch = arch_name; } } + + // Workaround to LLVM/Zig naming mismatch. + // LLVM calls it sparcv9, while Zig calls it sparc64. + if (!strcmp(cur_arch, "sparcv9")) { + cur_arch = "sparc64"; + } } assert(cur_arch != nullptr); diff --git a/src/target.zig b/src/target.zig index 9249ed1b60..c794ea52b0 100644 --- a/src/target.zig +++ b/src/target.zig @@ -63,7 +63,7 @@ pub const available_libcs = [_]ArchOsAbi{ .{ .arch = .s390x, .os = .linux, .abi = .gnu }, .{ .arch = .s390x, .os = .linux, .abi = .musl }, .{ .arch = .sparc, .os = .linux, .abi = .gnu }, - .{ .arch = .sparcv9, .os = .linux, .abi = .gnu }, + .{ .arch = .sparc64, .os = .linux, .abi = .gnu }, .{ .arch = .wasm32, .os = .freestanding, .abi = .musl }, .{ .arch = .wasm32, .os = .wasi, .abi = .musl }, .{ .arch = .x86_64, .os = .linux, .abi = .gnu }, @@ -118,7 +118,7 @@ pub fn osArchName(target: std.Target) [:0]const u8 { .mips, .mipsel, .mips64, .mips64el => "mips", .powerpc, .powerpcle, .powerpc64, .powerpc64le => "powerpc", .riscv32, .riscv64 => "riscv", - .sparc, .sparcel, .sparcv9 => "sparc", + .sparc, .sparcel, .sparc64 => "sparc", .i386, .x86_64 => "x86", else => @tagName(target.cpu.arch), }, @@ -232,7 +232,7 @@ pub fn hasLlvmSupport(target: std.Target) bool { .riscv32, .riscv64, .sparc, - .sparcv9, + .sparc64, .sparcel, .s390x, .tce, @@ -351,7 +351,7 @@ pub fn archToLLVM(arch_tag: std.Target.Cpu.Arch) llvm.ArchType { .riscv32 => .riscv32, .riscv64 => .riscv64, .sparc => .sparc, - .sparcv9 => .sparcv9, + .sparc64 => .sparcv9, // In LLVM, sparc64 == sparcv9. .sparcel => .sparcel, .s390x => .systemz, .tce => .tce, @@ -617,7 +617,7 @@ pub fn atomicPtrAlignment( .powerpc64, .powerpc64le, .riscv64, - .sparcv9, + .sparc64, .s390x, .amdil64, .hsail64, @@ -723,7 +723,7 @@ pub fn defaultFunctionAlignment(target: std.Target) u32 { return switch (target.cpu.arch) { .arm, .armeb => 4, .aarch64, .aarch64_32, .aarch64_be => 4, - .sparc, .sparcel, .sparcv9 => 4, + .sparc, .sparcel, .sparc64 => 4, .riscv64 => 2, else => 1, }; diff --git a/src/type.zig b/src/type.zig index 8742d9b61c..2c3ce0d900 100644 --- a/src/type.zig +++ b/src/type.zig @@ -6229,7 +6229,7 @@ pub const CType = enum { .mips64, .mips64el, .sparc, - .sparcv9, + .sparc64, .sparcel, .powerpc, .powerpcle, @@ -6267,7 +6267,7 @@ pub const CType = enum { .mips64, .mips64el, .sparc, - .sparcv9, + .sparc64, .sparcel, .powerpc, .powerpcle, diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 6c1122323a..b89cd5ee69 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -135,7 +135,7 @@ test "alignment and size of structs with 128-bit fields" { .powerpc64, .powerpc64le, .riscv64, - .sparcv9, + .sparc64, .x86_64, .aarch64, .aarch64_be, diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index aaf61745b0..9847054692 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -604,7 +604,7 @@ test "vector shift operators" { .mips64, .mips64el, .riscv64, - .sparcv9, + .sparc64, => { // LLVM miscompiles on this architecture // https://github.com/ziglang/zig/issues/4951 diff --git a/test/cases/sparcv9-linux/hello_world.zig b/test/cases/sparc64-linux/hello_world.zig similarity index 94% rename from test/cases/sparcv9-linux/hello_world.zig rename to test/cases/sparc64-linux/hello_world.zig index 1aea9da7a3..b5a2562c44 100644 --- a/test/cases/sparcv9-linux/hello_world.zig +++ b/test/cases/sparc64-linux/hello_world.zig @@ -16,7 +16,7 @@ pub fn main() void { } // run -// target=sparcv9-linux +// target=sparc64-linux // // Hello, World! // diff --git a/tools/process_headers.zig b/tools/process_headers.zig index 579667a5a6..39fbe08a4a 100644 --- a/tools/process_headers.zig +++ b/tools/process_headers.zig @@ -170,13 +170,13 @@ const glibc_targets = [_]LibCTarget{ .abi = MultiAbi{ .specific = Abi.gnu }, }, LibCTarget{ - .name = "sparc64-linux-gnu", + .name = "sparc-linux-gnu", .arch = MultiArch{ .specific = Arch.sparc }, .abi = MultiAbi{ .specific = Abi.gnu }, }, LibCTarget{ .name = "sparcv9-linux-gnu", - .arch = MultiArch{ .specific = Arch.sparcv9 }, + .arch = MultiArch{ .specific = Arch.sparc64 }, .abi = MultiAbi{ .specific = Abi.gnu }, }, LibCTarget{ From 24633b561369b5847e33d54ac02b77f23c9f8519 Mon Sep 17 00:00:00 2001 From: Kirk Scheibelhut Date: Thu, 12 May 2022 12:05:07 -0700 Subject: [PATCH 1482/2031] zig fmt: make --exclude ignore missing dirs --- src/main.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main.zig b/src/main.zig index 58c614c76e..246eacfe7a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3960,7 +3960,10 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void // Mark any excluded files/directories as already seen, // so that they are skipped later during actual processing for (excluded_files.items) |file_path| { - var dir = try fs.cwd().openDir(file_path, .{}); + var dir = fs.cwd().openDir(file_path, .{}) catch |err| switch (err) { + error.FileNotFound => continue, + else => |e| return e, + }; defer dir.close(); const stat = try dir.stat(); From 66f3efb63b4b352b1dbbaa4216fb3b0dabac3f3e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 May 2022 00:00:20 -0700 Subject: [PATCH 1483/2031] migrate runtime safety tests to the new test harness * migrate runtime safety tests to the new test harness - this required adding compare output / execution support for stage1 to the test harness. * rename `zig build test-stage2` to `zig build test-cases` since it now does quite a bit of stage1 testing actually. I named it this way since the main directory in the source tree associated with these tests is "test/cases/". * add some documentation for the test manifest format. --- build.zig | 27 +- ci/azure/macos_script | 3 +- ci/drone/linux_script_test | 3 +- ci/zinc/linux_test.sh | 3 +- src/test.zig | 17 +- test/cases/README.md | 61 + test/cases/safety/@alignCast misaligned.zig | 21 + .../@asyncCall with too small a frame.zig | 19 + ...tCast error not present in destination.zig | 18 + ...Int cannot fit - negative out of range.zig | 17 + ...oInt cannot fit - negative to unsigned.zig | 17 + ...Int cannot fit - positive out of range.zig | 17 + test/cases/safety/@intCast to u0.zig | 19 + .../@intToEnum - no matching tag value.zig | 22 + ...o to non-optional byte-aligned pointer.zig | 15 + ...r address zero to non-optional pointer.zig | 15 + .../@tagName on corrupted enum value.zig | 24 + .../@tagName on corrupted union value.zig | 25 + .../safety/array slice sentinel mismatch.zig | 18 + test/cases/safety/awaiting twice.zig | 28 + test/cases/safety/bad union field access.zig | 24 + test/cases/safety/calling panic.zig | 15 + ...ast []u8 to bigger slice of wrong size.zig | 18 + ...er to global error and no code matches.zig | 16 + ...mpty slice with sentinel out of bounds.zig | 20 + ...ror return trace across suspend points.zig | 38 + .../exact division failure - vectors.zig | 20 + test/cases/safety/exact division failure.zig | 18 + .../intToPtr with misaligned address.zig | 17 + .../safety/integer addition overflow.zig | 22 + .../integer division by zero - vectors.zig | 19 + .../cases/safety/integer division by zero.zig | 17 + .../integer multiplication overflow.zig | 18 + .../safety/integer negation overflow.zig | 18 + .../safety/integer subtraction overflow.zig | 18 + .../invalid resume of async function.zig | 18 + ...suspend function call, callee suspends.zig | 19 + .../optional unwrap operator on C pointer.zig | 15 + ...tional unwrap operator on null pointer.zig | 15 + .../safety/out of bounds slice access.zig | 18 + ...r casting null to non-optional pointer.zig | 15 + .../pointer slice sentinel mismatch.zig | 20 + ...ng a function which is awaiting a call.zig | 20 + ...g a function which is awaiting a frame.zig | 21 + ...n which has been suspended and resumed.zig | 31 + ...ed function which never been suspended.zig | 26 + .../safety/shift left by huge amount.zig | 20 + .../safety/shift right by huge amount.zig | 20 + ...ed integer division overflow - vectors.zig | 20 + .../signed integer division overflow.zig | 18 + ...in cast to unsigned integer - widening.zig | 15 + ...ot fitting in cast to unsigned integer.zig | 18 + .../safety/signed shift left overflow.zig | 18 + .../safety/signed shift right overflow.zig | 18 + .../safety/signed-unsigned vector cast.zig | 19 + .../slice sentinel mismatch - floats.zig | 19 + ... sentinel mismatch - optional pointers.zig | 19 + .../safety/slice slice sentinel mismatch.zig | 18 + .../slice with sentinel out of bounds.zig | 20 + test/cases/safety/slicing null C pointer.zig | 16 + .../safety/switch on corrupted enum value.zig | 25 + .../switch on corrupted union value.zig | 25 + test/cases/safety/truncating vector cast.zig | 19 + ...ast to signed integer - same bit count.zig | 15 + .../safety/unsigned shift left overflow.zig | 18 + .../safety/unsigned shift right overflow.zig | 18 + .../safety/unsigned-signed vector cast.zig | 19 + test/cases/safety/unwrap error.zig | 18 + ...e does not fit in shortening cast - u0.zig | 18 + .../value does not fit in shortening cast.zig | 18 + .../vector integer addition overflow.zig | 19 + ...vector integer multiplication overflow.zig | 19 + .../vector integer negation overflow.zig | 18 + .../vector integer subtraction overflow.zig | 19 + test/runtime_safety.zig | 1206 ----------------- test/tests.zig | 16 - 76 files changed, 1410 insertions(+), 1243 deletions(-) create mode 100644 test/cases/README.md create mode 100644 test/cases/safety/@alignCast misaligned.zig create mode 100644 test/cases/safety/@asyncCall with too small a frame.zig create mode 100644 test/cases/safety/@errSetCast error not present in destination.zig create mode 100644 test/cases/safety/@floatToInt cannot fit - negative out of range.zig create mode 100644 test/cases/safety/@floatToInt cannot fit - negative to unsigned.zig create mode 100644 test/cases/safety/@floatToInt cannot fit - positive out of range.zig create mode 100644 test/cases/safety/@intCast to u0.zig create mode 100644 test/cases/safety/@intToEnum - no matching tag value.zig create mode 100644 test/cases/safety/@intToPtr address zero to non-optional byte-aligned pointer.zig create mode 100644 test/cases/safety/@intToPtr address zero to non-optional pointer.zig create mode 100644 test/cases/safety/@tagName on corrupted enum value.zig create mode 100644 test/cases/safety/@tagName on corrupted union value.zig create mode 100644 test/cases/safety/array slice sentinel mismatch.zig create mode 100644 test/cases/safety/awaiting twice.zig create mode 100644 test/cases/safety/bad union field access.zig create mode 100644 test/cases/safety/calling panic.zig create mode 100644 test/cases/safety/cast []u8 to bigger slice of wrong size.zig create mode 100644 test/cases/safety/cast integer to global error and no code matches.zig create mode 100644 test/cases/safety/empty slice with sentinel out of bounds.zig create mode 100644 test/cases/safety/error return trace across suspend points.zig create mode 100644 test/cases/safety/exact division failure - vectors.zig create mode 100644 test/cases/safety/exact division failure.zig create mode 100644 test/cases/safety/intToPtr with misaligned address.zig create mode 100644 test/cases/safety/integer addition overflow.zig create mode 100644 test/cases/safety/integer division by zero - vectors.zig create mode 100644 test/cases/safety/integer division by zero.zig create mode 100644 test/cases/safety/integer multiplication overflow.zig create mode 100644 test/cases/safety/integer negation overflow.zig create mode 100644 test/cases/safety/integer subtraction overflow.zig create mode 100644 test/cases/safety/invalid resume of async function.zig create mode 100644 test/cases/safety/nosuspend function call, callee suspends.zig create mode 100644 test/cases/safety/optional unwrap operator on C pointer.zig create mode 100644 test/cases/safety/optional unwrap operator on null pointer.zig create mode 100644 test/cases/safety/out of bounds slice access.zig create mode 100644 test/cases/safety/pointer casting null to non-optional pointer.zig create mode 100644 test/cases/safety/pointer slice sentinel mismatch.zig create mode 100644 test/cases/safety/resuming a function which is awaiting a call.zig create mode 100644 test/cases/safety/resuming a function which is awaiting a frame.zig create mode 100644 test/cases/safety/resuming a non-suspended function which has been suspended and resumed.zig create mode 100644 test/cases/safety/resuming a non-suspended function which never been suspended.zig create mode 100644 test/cases/safety/shift left by huge amount.zig create mode 100644 test/cases/safety/shift right by huge amount.zig create mode 100644 test/cases/safety/signed integer division overflow - vectors.zig create mode 100644 test/cases/safety/signed integer division overflow.zig create mode 100644 test/cases/safety/signed integer not fitting in cast to unsigned integer - widening.zig create mode 100644 test/cases/safety/signed integer not fitting in cast to unsigned integer.zig create mode 100644 test/cases/safety/signed shift left overflow.zig create mode 100644 test/cases/safety/signed shift right overflow.zig create mode 100644 test/cases/safety/signed-unsigned vector cast.zig create mode 100644 test/cases/safety/slice sentinel mismatch - floats.zig create mode 100644 test/cases/safety/slice sentinel mismatch - optional pointers.zig create mode 100644 test/cases/safety/slice slice sentinel mismatch.zig create mode 100644 test/cases/safety/slice with sentinel out of bounds.zig create mode 100644 test/cases/safety/slicing null C pointer.zig create mode 100644 test/cases/safety/switch on corrupted enum value.zig create mode 100644 test/cases/safety/switch on corrupted union value.zig create mode 100644 test/cases/safety/truncating vector cast.zig create mode 100644 test/cases/safety/unsigned integer not fitting in cast to signed integer - same bit count.zig create mode 100644 test/cases/safety/unsigned shift left overflow.zig create mode 100644 test/cases/safety/unsigned shift right overflow.zig create mode 100644 test/cases/safety/unsigned-signed vector cast.zig create mode 100644 test/cases/safety/unwrap error.zig create mode 100644 test/cases/safety/value does not fit in shortening cast - u0.zig create mode 100644 test/cases/safety/value does not fit in shortening cast.zig create mode 100644 test/cases/safety/vector integer addition overflow.zig create mode 100644 test/cases/safety/vector integer multiplication overflow.zig create mode 100644 test/cases/safety/vector integer negation overflow.zig create mode 100644 test/cases/safety/vector integer subtraction overflow.zig delete mode 100644 test/runtime_safety.zig diff --git a/build.zig b/build.zig index 29b7d309d4..300ceed5c0 100644 --- a/build.zig +++ b/build.zig @@ -40,10 +40,10 @@ pub fn build(b: *Builder) !void { const toolchain_step = b.step("test-toolchain", "Run the tests for the toolchain"); - var test_stage2 = b.addTest("src/test.zig"); - test_stage2.setBuildMode(mode); - test_stage2.addPackagePath("test_cases", "test/cases.zig"); - test_stage2.single_threaded = single_threaded; + var test_cases = b.addTest("src/test.zig"); + test_cases.setBuildMode(mode); + test_cases.addPackagePath("test_cases", "test/cases.zig"); + test_cases.single_threaded = single_threaded; const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"}); @@ -158,7 +158,7 @@ pub fn build(b: *Builder) !void { if (target.isWindows() and target.getAbi() == .gnu) { // LTO is currently broken on mingw, this can be removed when it's fixed. exe.want_lto = false; - test_stage2.want_lto = false; + test_cases.want_lto = false; } const exe_options = b.addOptions(); @@ -175,7 +175,7 @@ pub fn build(b: *Builder) !void { if (link_libc) { exe.linkLibC(); - test_stage2.linkLibC(); + test_cases.linkLibC(); } const is_debug = mode == .Debug; @@ -258,7 +258,7 @@ pub fn build(b: *Builder) !void { zig0.defineCMacro("ZIG_VERSION_PATCH", b.fmt("{d}", .{zig_version.patch})); zig0.defineCMacro("ZIG_VERSION_STRING", b.fmt("\"{s}\"", .{version})); - for ([_]*std.build.LibExeObjStep{ zig0, exe, test_stage2 }) |artifact| { + for ([_]*std.build.LibExeObjStep{ zig0, exe, test_cases }) |artifact| { artifact.addIncludePath("src"); artifact.addIncludePath("deps/SoftFloat-3e/source/include"); artifact.addIncludePath("deps/SoftFloat-3e-prebuilt"); @@ -335,11 +335,11 @@ pub fn build(b: *Builder) !void { } try addCmakeCfgOptionsToExe(b, cfg, exe, use_zig_libcxx); - try addCmakeCfgOptionsToExe(b, cfg, test_stage2, use_zig_libcxx); + try addCmakeCfgOptionsToExe(b, cfg, test_cases, use_zig_libcxx); } else { // Here we are -Denable-llvm but no cmake integration. try addStaticLlvmOptionsToExe(exe); - try addStaticLlvmOptionsToExe(test_stage2); + try addStaticLlvmOptionsToExe(test_cases); } } @@ -381,7 +381,7 @@ pub fn build(b: *Builder) !void { const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); const test_stage2_options = b.addOptions(); - test_stage2.addOptions("build_options", test_stage2_options); + test_cases.addOptions("build_options", test_stage2_options); test_stage2_options.addOption(bool, "enable_logging", enable_logging); test_stage2_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots); @@ -404,10 +404,10 @@ pub fn build(b: *Builder) !void { test_stage2_options.addOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version)); test_stage2_options.addOption(std.SemanticVersion, "semver", semver); - const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests"); - test_stage2_step.dependOn(&test_stage2.step); + const test_cases_step = b.step("test-cases", "Run the main compiler test cases"); + test_cases_step.dependOn(&test_cases.step); if (!skip_stage2_tests) { - toolchain_step.dependOn(test_stage2_step); + toolchain_step.dependOn(test_cases_step); } var chosen_modes: [4]builtin.Mode = undefined; @@ -485,7 +485,6 @@ pub fn build(b: *Builder) !void { toolchain_step.dependOn(tests.addStackTraceTests(b, test_filter, modes)); toolchain_step.dependOn(tests.addCliTests(b, test_filter, modes)); toolchain_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); - toolchain_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes)); toolchain_step.dependOn(tests.addTranslateCTests(b, test_filter)); if (!skip_run_translated_c) { toolchain_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target)); diff --git a/ci/azure/macos_script b/ci/azure/macos_script index 36c034d871..28a53b4501 100755 --- a/ci/azure/macos_script +++ b/ci/azure/macos_script @@ -71,12 +71,11 @@ release/bin/zig build test-standalone -Denable-macos-sdk release/bin/zig build test-stack-traces -Denable-macos-sdk release/bin/zig build test-cli -Denable-macos-sdk release/bin/zig build test-asm-link -Denable-macos-sdk -release/bin/zig build test-runtime-safety -Denable-macos-sdk release/bin/zig build test-translate-c -Denable-macos-sdk release/bin/zig build test-run-translated-c -Denable-macos-sdk release/bin/zig build docs -Denable-macos-sdk release/bin/zig build test-fmt -Denable-macos-sdk -release/bin/zig build test-stage2 -Denable-macos-sdk +release/bin/zig build test-cases -Denable-macos-sdk if [ "${BUILD_REASON}" != "PullRequest" ]; then mv ../LICENSE release/ diff --git a/ci/drone/linux_script_test b/ci/drone/linux_script_test index 12f9439102..15a12b59ee 100755 --- a/ci/drone/linux_script_test +++ b/ci/drone/linux_script_test @@ -34,12 +34,11 @@ case "$1" in ./build/zig build $BUILD_FLAGS test-stack-traces ./build/zig build $BUILD_FLAGS test-cli ./build/zig build $BUILD_FLAGS test-asm-link - ./build/zig build $BUILD_FLAGS test-runtime-safety ./build/zig build $BUILD_FLAGS test-translate-c ;; 7) ./build/zig build $BUILD_FLAGS # test building self-hosted without LLVM - ./build/zig build $BUILD_FLAGS test-stage2 + ./build/zig build $BUILD_FLAGS test-cases ;; '') echo "error: expecting test group argument" diff --git a/ci/zinc/linux_test.sh b/ci/zinc/linux_test.sh index 9dd5e35478..1c82992b25 100755 --- a/ci/zinc/linux_test.sh +++ b/ci/zinc/linux_test.sh @@ -69,12 +69,11 @@ $ZIG build test-standalone -fqemu -fwasmtime $ZIG build test-stack-traces -fqemu -fwasmtime $ZIG build test-cli -fqemu -fwasmtime $ZIG build test-asm-link -fqemu -fwasmtime -$ZIG build test-runtime-safety -fqemu -fwasmtime $ZIG build test-translate-c -fqemu -fwasmtime $ZIG build test-run-translated-c -fqemu -fwasmtime $ZIG build docs -fqemu -fwasmtime $ZIG build test-fmt -fqemu -fwasmtime -$ZIG build test-stage2 -fqemu -fwasmtime +$ZIG build test-cases -fqemu -fwasmtime # Produce the experimental std lib documentation. mkdir -p "$RELEASE_STAGING/docs/std" diff --git a/src/test.zig b/src/test.zig index c5dda0757f..abadedad91 100644 --- a/src/test.zig +++ b/src/test.zig @@ -1394,7 +1394,22 @@ pub const TestContext = struct { } }, .CompareObjectFile => @panic("TODO implement in the test harness"), - .Execution => @panic("TODO implement in the test harness"), + .Execution => |expected_stdout| { + switch (result.term) { + .Exited => |code| { + if (code != 0) { + dumpArgs(zig_args.items); + return error.CompilationFailed; + } + }, + else => { + dumpArgs(zig_args.items); + return error.CompilationCrashed; + }, + } + try std.testing.expectEqualStrings("", result.stderr); + try std.testing.expectEqualStrings(expected_stdout, result.stdout); + }, .Header => @panic("TODO implement in the test harness"), } return; diff --git a/test/cases/README.md b/test/cases/README.md new file mode 100644 index 0000000000..4c9f401ab6 --- /dev/null +++ b/test/cases/README.md @@ -0,0 +1,61 @@ +# Test Case Quick Reference + +Use comments at the **end of the file** to indicate metadata about the test +case. Here are examples of different kinds of tests: + +## Compile Error Test + +If you want it to be run with `zig test` and match expected error messages: + +```zig +// error +// is_test=1 +// +// :4:13: error: 'try' outside function scope +``` + +## Execution + +This will do `zig run` on the code and expect exit code 0. + +```zig +// run +``` + +## Incremental Compilation + +Make multiple files that have ".", and then an integer, before the ".zig" +extension, like this: + +``` +hello.0.zig +hello.1.zig +hello.2.zig +``` + +Each file can be a different kind of test, such as expecting compile errors, +or expecting to be run and exit(0). The test harness will use these to simulate +incremental compilation. + +At the time of writing there is no way to specify multiple files being changed +as part of an update. + +## Subdirectories + +Subdirectories do not have any semantic meaning but they can be used for +organization since the test harness will recurse into them. The full directory +path will be prepended as a prefix on the test case name. + +## Limiting which Backends and Targets are Tested + +```zig +// run +// backend=stage2,llvm +// target=x86_64-linux,x86_64-macos +``` + +Possible backends are: + + * `stage1`: equivalent to `-fstage1`. + * `stage2`: equivalent to passing `-fno-stage1 -fno-LLVM`. + * `llvm`: equivalent to `-fLLVM -fno-stage1`. diff --git a/test/cases/safety/@alignCast misaligned.zig b/test/cases/safety/@alignCast misaligned.zig new file mode 100644 index 0000000000..3948cea443 --- /dev/null +++ b/test/cases/safety/@alignCast misaligned.zig @@ -0,0 +1,21 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +pub fn main() !void { + var array align(4) = [_]u32{0x11111111, 0x11111111}; + const bytes = std.mem.sliceAsBytes(array[0..]); + if (foo(bytes) != 0x11111111) return error.Wrong; + return error.TestFailed; +} +fn foo(bytes: []u8) u32 { + const slice4 = bytes[1..5]; + const int_slice = std.mem.bytesAsSlice(u32, @alignCast(4, slice4)); + return int_slice[0]; +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/@asyncCall with too small a frame.zig b/test/cases/safety/@asyncCall with too small a frame.zig new file mode 100644 index 0000000000..8ae4d4ee3f --- /dev/null +++ b/test/cases/safety/@asyncCall with too small a frame.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + var bytes: [1]u8 align(16) = undefined; + var ptr = other; + var frame = @asyncCall(&bytes, {}, ptr, .{}); + _ = frame; + return error.TestFailed; +} +fn other() callconv(.Async) void { + suspend {} +} +// run +// backend=stage1 diff --git a/test/cases/safety/@errSetCast error not present in destination.zig b/test/cases/safety/@errSetCast error not present in destination.zig new file mode 100644 index 0000000000..3934307e9e --- /dev/null +++ b/test/cases/safety/@errSetCast error not present in destination.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +const Set1 = error{A, B}; +const Set2 = error{A, C}; +pub fn main() !void { + foo(Set1.B) catch {}; + return error.TestFailed; +} +fn foo(set1: Set1) Set2 { + return @errSetCast(Set2, set1); +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/@floatToInt cannot fit - negative out of range.zig b/test/cases/safety/@floatToInt cannot fit - negative out of range.zig new file mode 100644 index 0000000000..1e7016830e --- /dev/null +++ b/test/cases/safety/@floatToInt cannot fit - negative out of range.zig @@ -0,0 +1,17 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + baz(bar(-129.1)); + return error.TestFailed; +} +fn bar(a: f32) i8 { + return @floatToInt(i8, a); +} +fn baz(_: i8) void { } +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/@floatToInt cannot fit - negative to unsigned.zig b/test/cases/safety/@floatToInt cannot fit - negative to unsigned.zig new file mode 100644 index 0000000000..fbcc7fc11d --- /dev/null +++ b/test/cases/safety/@floatToInt cannot fit - negative to unsigned.zig @@ -0,0 +1,17 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + baz(bar(-1.1)); + return error.TestFailed; +} +fn bar(a: f32) u8 { + return @floatToInt(u8, a); +} +fn baz(_: u8) void { } +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/@floatToInt cannot fit - positive out of range.zig b/test/cases/safety/@floatToInt cannot fit - positive out of range.zig new file mode 100644 index 0000000000..1ab83edafa --- /dev/null +++ b/test/cases/safety/@floatToInt cannot fit - positive out of range.zig @@ -0,0 +1,17 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + baz(bar(256.2)); + return error.TestFailed; +} +fn bar(a: f32) u8 { + return @floatToInt(u8, a); +} +fn baz(_: u8) void { } +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/@intCast to u0.zig b/test/cases/safety/@intCast to u0.zig new file mode 100644 index 0000000000..10c3d22213 --- /dev/null +++ b/test/cases/safety/@intCast to u0.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +pub fn main() !void { + bar(1, 1); + return error.TestFailed; +} + +fn bar(one: u1, not_zero: i32) void { + var x = one << @intCast(u0, not_zero); + _ = x; +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/@intToEnum - no matching tag value.zig b/test/cases/safety/@intToEnum - no matching tag value.zig new file mode 100644 index 0000000000..7f66dc050d --- /dev/null +++ b/test/cases/safety/@intToEnum - no matching tag value.zig @@ -0,0 +1,22 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +const Foo = enum { + A, + B, + C, +}; +pub fn main() !void { + baz(bar(3)); + return error.TestFailed; +} +fn bar(a: u2) Foo { + return @intToEnum(Foo, a); +} +fn baz(_: Foo) void {} +// run +// backend=stage1 diff --git a/test/cases/safety/@intToPtr address zero to non-optional byte-aligned pointer.zig b/test/cases/safety/@intToPtr address zero to non-optional byte-aligned pointer.zig new file mode 100644 index 0000000000..6c44505af9 --- /dev/null +++ b/test/cases/safety/@intToPtr address zero to non-optional byte-aligned pointer.zig @@ -0,0 +1,15 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + var zero: usize = 0; + var b = @intToPtr(*u8, zero); + _ = b; + return error.TestFailed; +} +// run +// backend=stage1 diff --git a/test/cases/safety/@intToPtr address zero to non-optional pointer.zig b/test/cases/safety/@intToPtr address zero to non-optional pointer.zig new file mode 100644 index 0000000000..9f61766884 --- /dev/null +++ b/test/cases/safety/@intToPtr address zero to non-optional pointer.zig @@ -0,0 +1,15 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + var zero: usize = 0; + var b = @intToPtr(*i32, zero); + _ = b; + return error.TestFailed; +} +// run +// backend=stage1 diff --git a/test/cases/safety/@tagName on corrupted enum value.zig b/test/cases/safety/@tagName on corrupted enum value.zig new file mode 100644 index 0000000000..577ba183d5 --- /dev/null +++ b/test/cases/safety/@tagName on corrupted enum value.zig @@ -0,0 +1,24 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "invalid enum value")) { + std.process.exit(0); + } + std.process.exit(1); +} + +const E = enum(u32) { + X = 1, +}; + +pub fn main() !void { + var e: E = undefined; + @memset(@ptrCast([*]u8, &e), 0x55, @sizeOf(E)); + var n = @tagName(e); + _ = n; + return error.TestFailed; +} + +// run +// backend=stage1 diff --git a/test/cases/safety/@tagName on corrupted union value.zig b/test/cases/safety/@tagName on corrupted union value.zig new file mode 100644 index 0000000000..6012e86833 --- /dev/null +++ b/test/cases/safety/@tagName on corrupted union value.zig @@ -0,0 +1,25 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "invalid enum value")) { + std.process.exit(0); + } + std.process.exit(1); +} + +const U = union(enum(u32)) { + X: u8, +}; + +pub fn main() !void { + var u: U = undefined; + @memset(@ptrCast([*]u8, &u), 0x55, @sizeOf(U)); + var t: @typeInfo(U).Union.tag_type.? = u; + var n = @tagName(t); + _ = n; + return error.TestFailed; +} + +// run +// backend=stage1 diff --git a/test/cases/safety/array slice sentinel mismatch.zig b/test/cases/safety/array slice sentinel mismatch.zig new file mode 100644 index 0000000000..83e8b1f787 --- /dev/null +++ b/test/cases/safety/array slice sentinel mismatch.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "sentinel mismatch")) { + std.process.exit(0); + } + std.process.exit(1); +} +pub fn main() !void { + var buf: [4]u8 = undefined; + const slice = buf[0..3 :0]; + _ = slice; + return error.TestFailed; +} +// run +// backend=stage1 + diff --git a/test/cases/safety/awaiting twice.zig b/test/cases/safety/awaiting twice.zig new file mode 100644 index 0000000000..2a64320c07 --- /dev/null +++ b/test/cases/safety/awaiting twice.zig @@ -0,0 +1,28 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +var frame: anyframe = undefined; + +pub fn main() !void { + _ = async amain(); + resume frame; + return error.TestFailed; +} + +fn amain() void { + var f = async func(); + await f; + await f; +} + +fn func() void { + suspend { + frame = @frame(); + } +} +// run +// backend=stage1 diff --git a/test/cases/safety/bad union field access.zig b/test/cases/safety/bad union field access.zig new file mode 100644 index 0000000000..cc476f57c5 --- /dev/null +++ b/test/cases/safety/bad union field access.zig @@ -0,0 +1,24 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +const Foo = union { + float: f32, + int: u32, +}; + +pub fn main() !void { + var f = Foo { .int = 42 }; + bar(&f); + return error.TestFailed; +} + +fn bar(f: *Foo) void { + f.float = 12.34; +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/calling panic.zig b/test/cases/safety/calling panic.zig new file mode 100644 index 0000000000..e6ff577e7f --- /dev/null +++ b/test/cases/safety/calling panic.zig @@ -0,0 +1,15 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "oh no")) { + std.process.exit(0); + } + std.process.exit(1); +} +pub fn main() !void { + if (true) @panic("oh no"); + return error.TestFailed; +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/cast []u8 to bigger slice of wrong size.zig b/test/cases/safety/cast []u8 to bigger slice of wrong size.zig new file mode 100644 index 0000000000..5bdee09e52 --- /dev/null +++ b/test/cases/safety/cast []u8 to bigger slice of wrong size.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +pub fn main() !void { + const x = widenSlice(&[_]u8{1, 2, 3, 4, 5}); + if (x.len == 0) return error.Whatever; + return error.TestFailed; +} +fn widenSlice(slice: []align(1) const u8) []align(1) const i32 { + return std.mem.bytesAsSlice(i32, slice); +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/cast integer to global error and no code matches.zig b/test/cases/safety/cast integer to global error and no code matches.zig new file mode 100644 index 0000000000..57b72aab5d --- /dev/null +++ b/test/cases/safety/cast integer to global error and no code matches.zig @@ -0,0 +1,16 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + bar(9999) catch {}; + return error.TestFailed; +} +fn bar(x: u16) anyerror { + return @intToError(x); +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/empty slice with sentinel out of bounds.zig b/test/cases/safety/empty slice with sentinel out of bounds.zig new file mode 100644 index 0000000000..2ea22ed41d --- /dev/null +++ b/test/cases/safety/empty slice with sentinel out of bounds.zig @@ -0,0 +1,20 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "index out of bounds")) { + std.process.exit(0); + } + std.process.exit(1); +} + +pub fn main() !void { + var buf_zero = [0]u8{}; + const input: []u8 = &buf_zero; + const slice = input[0..0 :0]; + _ = slice; + return error.TestFailed; +} + +// run +// backend=stage1 diff --git a/test/cases/safety/error return trace across suspend points.zig b/test/cases/safety/error return trace across suspend points.zig new file mode 100644 index 0000000000..b27bc770fb --- /dev/null +++ b/test/cases/safety/error return trace across suspend points.zig @@ -0,0 +1,38 @@ +const std = @import("std"); + + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +var failing_frame: @Frame(failing) = undefined; + +pub fn main() !void { + const p = nonFailing(); + resume p; + const p2 = async printTrace(p); + _ = p2; + return error.TestFailed; +} + +fn nonFailing() anyframe->anyerror!void { + failing_frame = async failing(); + return &failing_frame; +} + +fn failing() anyerror!void { + suspend {} + return second(); +} + +fn second() callconv(.Async) anyerror!void { + return error.Fail; +} + +fn printTrace(p: anyframe->anyerror!void) void { + (await p) catch unreachable; +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/exact division failure - vectors.zig b/test/cases/safety/exact division failure - vectors.zig new file mode 100644 index 0000000000..77dd427683 --- /dev/null +++ b/test/cases/safety/exact division failure - vectors.zig @@ -0,0 +1,20 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +pub fn main() !void { + var a: @Vector(4, i32) = [4]i32{111, 222, 333, 444}; + var b: @Vector(4, i32) = [4]i32{111, 222, 333, 441}; + const x = divExact(a, b); + _ = x; + return error.TestFailed; +} +fn divExact(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) { + return @divExact(a, b); +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/exact division failure.zig b/test/cases/safety/exact division failure.zig new file mode 100644 index 0000000000..c363df94ab --- /dev/null +++ b/test/cases/safety/exact division failure.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +pub fn main() !void { + const x = divExact(10, 3); + if (x == 0) return error.Whatever; + return error.TestFailed; +} +fn divExact(a: i32, b: i32) i32 { + return @divExact(a, b); +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/intToPtr with misaligned address.zig b/test/cases/safety/intToPtr with misaligned address.zig new file mode 100644 index 0000000000..eaca2cb32d --- /dev/null +++ b/test/cases/safety/intToPtr with misaligned address.zig @@ -0,0 +1,17 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "incorrect alignment")) { + std.process.exit(0); + } + std.process.exit(1); +} +pub fn main() !void { + var x: usize = 5; + var y = @intToPtr([*]align(4) u8, x); + _ = y; + return error.TestFailed; +} +// run +// backend=stage1 diff --git a/test/cases/safety/integer addition overflow.zig b/test/cases/safety/integer addition overflow.zig new file mode 100644 index 0000000000..74fce53890 --- /dev/null +++ b/test/cases/safety/integer addition overflow.zig @@ -0,0 +1,22 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "integer overflow")) { + std.process.exit(0); + } + std.process.exit(1); +} + +pub fn main() !void { + const x = add(65530, 10); + if (x == 0) return error.Whatever; + return error.TestFailed; +} + +fn add(a: u16, b: u16) u16 { + return a + b; +} + +// run +// backend=stage1 diff --git a/test/cases/safety/integer division by zero - vectors.zig b/test/cases/safety/integer division by zero - vectors.zig new file mode 100644 index 0000000000..b3365bfcae --- /dev/null +++ b/test/cases/safety/integer division by zero - vectors.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + var a: @Vector(4, i32) = [4]i32{111, 222, 333, 444}; + var b: @Vector(4, i32) = [4]i32{111, 0, 333, 444}; + const x = div0(a, b); + _ = x; + return error.TestFailed; +} +fn div0(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) { + return @divTrunc(a, b); +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/integer division by zero.zig b/test/cases/safety/integer division by zero.zig new file mode 100644 index 0000000000..b209a69d35 --- /dev/null +++ b/test/cases/safety/integer division by zero.zig @@ -0,0 +1,17 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + const x = div0(999, 0); + _ = x; + return error.TestFailed; +} +fn div0(a: i32, b: i32) i32 { + return @divTrunc(a, b); +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/integer multiplication overflow.zig b/test/cases/safety/integer multiplication overflow.zig new file mode 100644 index 0000000000..0eb68fabe0 --- /dev/null +++ b/test/cases/safety/integer multiplication overflow.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +pub fn main() !void { + const x = mul(300, 6000); + if (x == 0) return error.Whatever; + return error.TestFailed; +} +fn mul(a: u16, b: u16) u16 { + return a * b; +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/integer negation overflow.zig b/test/cases/safety/integer negation overflow.zig new file mode 100644 index 0000000000..c9d7e4f919 --- /dev/null +++ b/test/cases/safety/integer negation overflow.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +pub fn main() !void { + const x = neg(-32768); + if (x == 32767) return error.Whatever; + return error.TestFailed; +} +fn neg(a: i16) i16 { + return -a; +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/integer subtraction overflow.zig b/test/cases/safety/integer subtraction overflow.zig new file mode 100644 index 0000000000..f1b17020a8 --- /dev/null +++ b/test/cases/safety/integer subtraction overflow.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +pub fn main() !void { + const x = sub(10, 20); + if (x == 0) return error.Whatever; + return error.TestFailed; +} +fn sub(a: u16, b: u16) u16 { + return a - b; +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/invalid resume of async function.zig b/test/cases/safety/invalid resume of async function.zig new file mode 100644 index 0000000000..acde5eed92 --- /dev/null +++ b/test/cases/safety/invalid resume of async function.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + var p = async suspendOnce(); + resume p; //ok + resume p; //bad + return error.TestFailed; +} +fn suspendOnce() void { + suspend {} +} +// run +// backend=stage1 diff --git a/test/cases/safety/nosuspend function call, callee suspends.zig b/test/cases/safety/nosuspend function call, callee suspends.zig new file mode 100644 index 0000000000..fc99174bd0 --- /dev/null +++ b/test/cases/safety/nosuspend function call, callee suspends.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + _ = nosuspend add(101, 100); + return error.TestFailed; +} +fn add(a: i32, b: i32) i32 { + if (a > 100) { + suspend {} + } + return a + b; +} +// run +// backend=stage1 diff --git a/test/cases/safety/optional unwrap operator on C pointer.zig b/test/cases/safety/optional unwrap operator on C pointer.zig new file mode 100644 index 0000000000..4d5fa54d3d --- /dev/null +++ b/test/cases/safety/optional unwrap operator on C pointer.zig @@ -0,0 +1,15 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + var ptr: [*c]i32 = null; + var b = ptr.?; + _ = b; + return error.TestFailed; +} +// run +// backend=stage1 diff --git a/test/cases/safety/optional unwrap operator on null pointer.zig b/test/cases/safety/optional unwrap operator on null pointer.zig new file mode 100644 index 0000000000..eb791be268 --- /dev/null +++ b/test/cases/safety/optional unwrap operator on null pointer.zig @@ -0,0 +1,15 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + var ptr: ?*i32 = null; + var b = ptr.?; + _ = b; + return error.TestFailed; +} +// run +// backend=stage1 diff --git a/test/cases/safety/out of bounds slice access.zig b/test/cases/safety/out of bounds slice access.zig new file mode 100644 index 0000000000..e429328b1e --- /dev/null +++ b/test/cases/safety/out of bounds slice access.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + const a = [_]i32{1, 2, 3, 4}; + baz(bar(&a)); + return error.TestFailed; +} +fn bar(a: []const i32) i32 { + return a[4]; +} +fn baz(_: i32) void { } +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/pointer casting null to non-optional pointer.zig b/test/cases/safety/pointer casting null to non-optional pointer.zig new file mode 100644 index 0000000000..a61ba2fa9b --- /dev/null +++ b/test/cases/safety/pointer casting null to non-optional pointer.zig @@ -0,0 +1,15 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + var c_ptr: [*c]u8 = 0; + var zig_ptr: *u8 = c_ptr; + _ = zig_ptr; + return error.TestFailed; +} +// run +// backend=stage1 diff --git a/test/cases/safety/pointer slice sentinel mismatch.zig b/test/cases/safety/pointer slice sentinel mismatch.zig new file mode 100644 index 0000000000..f796534783 --- /dev/null +++ b/test/cases/safety/pointer slice sentinel mismatch.zig @@ -0,0 +1,20 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "sentinel mismatch")) { + std.process.exit(0); + } + std.process.exit(1); +} + +pub fn main() !void { + var buf: [4]u8 = undefined; + const ptr: [*]u8 = &buf; + const slice = ptr[0..3 :0]; + _ = slice; + return error.TestFailed; +} + +// run +// backend=stage1 diff --git a/test/cases/safety/resuming a function which is awaiting a call.zig b/test/cases/safety/resuming a function which is awaiting a call.zig new file mode 100644 index 0000000000..63c4e1ef50 --- /dev/null +++ b/test/cases/safety/resuming a function which is awaiting a call.zig @@ -0,0 +1,20 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + var frame = async first(); + resume frame; + return error.TestFailed; +} +fn first() void { + other(); +} +fn other() void { + suspend {} +} +// run +// backend=stage1 diff --git a/test/cases/safety/resuming a function which is awaiting a frame.zig b/test/cases/safety/resuming a function which is awaiting a frame.zig new file mode 100644 index 0000000000..0f68be4db8 --- /dev/null +++ b/test/cases/safety/resuming a function which is awaiting a frame.zig @@ -0,0 +1,21 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + var frame = async first(); + resume frame; + return error.TestFailed; +} +fn first() void { + var frame = async other(); + await frame; +} +fn other() void { + suspend {} +} +// run +// backend=stage1 diff --git a/test/cases/safety/resuming a non-suspended function which has been suspended and resumed.zig b/test/cases/safety/resuming a non-suspended function which has been suspended and resumed.zig new file mode 100644 index 0000000000..3f3a62fe4e --- /dev/null +++ b/test/cases/safety/resuming a non-suspended function which has been suspended and resumed.zig @@ -0,0 +1,31 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +fn foo() void { + suspend { + global_frame = @frame(); + } + var f = async bar(@frame()); + _ = f; + std.os.exit(1); +} + +fn bar(frame: anyframe) void { + suspend { + resume frame; + } + std.os.exit(1); +} + +var global_frame: anyframe = undefined; +pub fn main() !void { + _ = async foo(); + resume global_frame; + std.os.exit(1); +} +// run +// backend=stage1 diff --git a/test/cases/safety/resuming a non-suspended function which never been suspended.zig b/test/cases/safety/resuming a non-suspended function which never been suspended.zig new file mode 100644 index 0000000000..b0dfbc1911 --- /dev/null +++ b/test/cases/safety/resuming a non-suspended function which never been suspended.zig @@ -0,0 +1,26 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +fn foo() void { + var f = async bar(@frame()); + _ = f; + std.os.exit(1); +} + +fn bar(frame: anyframe) void { + suspend { + resume frame; + } + std.os.exit(1); +} + +pub fn main() !void { + _ = async foo(); + return error.TestFailed; +} +// run +// backend=stage1 diff --git a/test/cases/safety/shift left by huge amount.zig b/test/cases/safety/shift left by huge amount.zig new file mode 100644 index 0000000000..8cf5ec3752 --- /dev/null +++ b/test/cases/safety/shift left by huge amount.zig @@ -0,0 +1,20 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "shift amount is greater than the type size")) { + std.process.exit(0); + } + std.process.exit(1); +} + +pub fn main() !void { + var x: u24 = 42; + var y: u5 = 24; + var z = x >> y; + _ = z; + return error.TestFailed; +} + +// run +// backend=stage1 diff --git a/test/cases/safety/shift right by huge amount.zig b/test/cases/safety/shift right by huge amount.zig new file mode 100644 index 0000000000..90a620ddc4 --- /dev/null +++ b/test/cases/safety/shift right by huge amount.zig @@ -0,0 +1,20 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "shift amount is greater than the type size")) { + std.process.exit(0); + } + std.process.exit(1); +} + +pub fn main() !void { + var x: u24 = 42; + var y: u5 = 24; + var z = x << y; + _ = z; + return error.TestFailed; +} + +// run +// backend=stage1 diff --git a/test/cases/safety/signed integer division overflow - vectors.zig b/test/cases/safety/signed integer division overflow - vectors.zig new file mode 100644 index 0000000000..5ce8fd740e --- /dev/null +++ b/test/cases/safety/signed integer division overflow - vectors.zig @@ -0,0 +1,20 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +pub fn main() !void { + var a: @Vector(4, i16) = [_]i16{ 1, 2, -32768, 4 }; + var b: @Vector(4, i16) = [_]i16{ 1, 2, -1, 4 }; + const x = div(a, b); + if (x[2] == 32767) return error.Whatever; + return error.TestFailed; +} +fn div(a: @Vector(4, i16), b: @Vector(4, i16)) @Vector(4, i16) { + return @divTrunc(a, b); +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/signed integer division overflow.zig b/test/cases/safety/signed integer division overflow.zig new file mode 100644 index 0000000000..64e1827b45 --- /dev/null +++ b/test/cases/safety/signed integer division overflow.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +pub fn main() !void { + const x = div(-32768, -1); + if (x == 32767) return error.Whatever; + return error.TestFailed; +} +fn div(a: i16, b: i16) i16 { + return @divTrunc(a, b); +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/signed integer not fitting in cast to unsigned integer - widening.zig b/test/cases/safety/signed integer not fitting in cast to unsigned integer - widening.zig new file mode 100644 index 0000000000..7c08fcddb2 --- /dev/null +++ b/test/cases/safety/signed integer not fitting in cast to unsigned integer - widening.zig @@ -0,0 +1,15 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + var value: c_short = -1; + var casted = @intCast(u32, value); + _ = casted; + return error.TestFailed; +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/signed integer not fitting in cast to unsigned integer.zig b/test/cases/safety/signed integer not fitting in cast to unsigned integer.zig new file mode 100644 index 0000000000..6ed4246403 --- /dev/null +++ b/test/cases/safety/signed integer not fitting in cast to unsigned integer.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +pub fn main() !void { + const x = unsigned_cast(-10); + if (x == 0) return error.Whatever; + return error.TestFailed; +} +fn unsigned_cast(x: i32) u32 { + return @intCast(u32, x); +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/signed shift left overflow.zig b/test/cases/safety/signed shift left overflow.zig new file mode 100644 index 0000000000..814d52b1a9 --- /dev/null +++ b/test/cases/safety/signed shift left overflow.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +pub fn main() !void { + const x = shl(-16385, 1); + if (x == 0) return error.Whatever; + return error.TestFailed; +} +fn shl(a: i16, b: u4) i16 { + return @shlExact(a, b); +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/signed shift right overflow.zig b/test/cases/safety/signed shift right overflow.zig new file mode 100644 index 0000000000..fc2e00cee3 --- /dev/null +++ b/test/cases/safety/signed shift right overflow.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +pub fn main() !void { + const x = shr(-16385, 1); + if (x == 0) return error.Whatever; + return error.TestFailed; +} +fn shr(a: i16, b: u4) i16 { + return @shrExact(a, b); +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/signed-unsigned vector cast.zig b/test/cases/safety/signed-unsigned vector cast.zig new file mode 100644 index 0000000000..4de78e969d --- /dev/null +++ b/test/cases/safety/signed-unsigned vector cast.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "attempt to cast negative value to unsigned integer")) { + std.process.exit(0); + } + std.process.exit(1); +} + +pub fn main() !void { + var x = @splat(4, @as(i32, -2147483647)); + var y = @intCast(@Vector(4, u32), x); + _ = y; + return error.TestFailed; +} + +// run +// backend=stage1 diff --git a/test/cases/safety/slice sentinel mismatch - floats.zig b/test/cases/safety/slice sentinel mismatch - floats.zig new file mode 100644 index 0000000000..df5edc1fdf --- /dev/null +++ b/test/cases/safety/slice sentinel mismatch - floats.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "sentinel mismatch")) { + std.process.exit(0); + } + std.process.exit(1); +} + +pub fn main() !void { + var buf: [4]f32 = undefined; + const slice = buf[0..3 :1.2]; + _ = slice; + return error.TestFailed; +} + +// run +// backend=stage1 diff --git a/test/cases/safety/slice sentinel mismatch - optional pointers.zig b/test/cases/safety/slice sentinel mismatch - optional pointers.zig new file mode 100644 index 0000000000..8199f3280e --- /dev/null +++ b/test/cases/safety/slice sentinel mismatch - optional pointers.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "sentinel mismatch")) { + std.process.exit(0); + } + std.process.exit(1); +} + +pub fn main() !void { + var buf: [4]?*i32 = undefined; + const slice = buf[0..3 :null]; + _ = slice; + return error.TestFailed; +} + +// run +// backend=stage1 diff --git a/test/cases/safety/slice slice sentinel mismatch.zig b/test/cases/safety/slice slice sentinel mismatch.zig new file mode 100644 index 0000000000..8303b1a288 --- /dev/null +++ b/test/cases/safety/slice slice sentinel mismatch.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "sentinel mismatch")) { + std.process.exit(0); + } + std.process.exit(1); +} +pub fn main() !void { + var buf: [4]u8 = undefined; + const slice = buf[0..]; + const slice2 = slice[0..3 :0]; + _ = slice2; + return error.TestFailed; +} +// run +// backend=stage1 diff --git a/test/cases/safety/slice with sentinel out of bounds.zig b/test/cases/safety/slice with sentinel out of bounds.zig new file mode 100644 index 0000000000..7fc890e144 --- /dev/null +++ b/test/cases/safety/slice with sentinel out of bounds.zig @@ -0,0 +1,20 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "index out of bounds")) { + std.process.exit(0); + } + std.process.exit(1); +} + +pub fn main() !void { + var buf = [4]u8{ 'a', 'b', 'c', 0 }; + const input: []u8 = &buf; + const slice = input[0..4 :0]; + _ = slice; + return error.TestFailed; +} + +// run +// backend=stage1 diff --git a/test/cases/safety/slicing null C pointer.zig b/test/cases/safety/slicing null C pointer.zig new file mode 100644 index 0000000000..2b678ad9cf --- /dev/null +++ b/test/cases/safety/slicing null C pointer.zig @@ -0,0 +1,16 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +pub fn main() !void { + var ptr: [*c]const u32 = null; + var slice = ptr[0..3]; + _ = slice; + return error.TestFailed; +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/switch on corrupted enum value.zig b/test/cases/safety/switch on corrupted enum value.zig new file mode 100644 index 0000000000..b574444341 --- /dev/null +++ b/test/cases/safety/switch on corrupted enum value.zig @@ -0,0 +1,25 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "reached unreachable code")) { + std.process.exit(0); + } + std.process.exit(1); +} + +const E = enum(u32) { + X = 1, +}; + +pub fn main() !void { + var e: E = undefined; + @memset(@ptrCast([*]u8, &e), 0x55, @sizeOf(E)); + switch (e) { + .X => @breakpoint(), + } + return error.TestFailed; +} + +// run +// backend=stage1 diff --git a/test/cases/safety/switch on corrupted union value.zig b/test/cases/safety/switch on corrupted union value.zig new file mode 100644 index 0000000000..8ec4fece1b --- /dev/null +++ b/test/cases/safety/switch on corrupted union value.zig @@ -0,0 +1,25 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "reached unreachable code")) { + std.process.exit(0); + } + std.process.exit(1); +} + +const U = union(enum(u32)) { + X: u8, +}; + +pub fn main() !void { + var u: U = undefined; + @memset(@ptrCast([*]u8, &u), 0x55, @sizeOf(U)); + switch (u) { + .X => @breakpoint(), + } + return error.TestFailed; +} + +// run +// backend=stage1 diff --git a/test/cases/safety/truncating vector cast.zig b/test/cases/safety/truncating vector cast.zig new file mode 100644 index 0000000000..f2795d9c66 --- /dev/null +++ b/test/cases/safety/truncating vector cast.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "integer cast truncated bits")) { + std.process.exit(0); + } + std.process.exit(1); +} + +pub fn main() !void { + var x = @splat(4, @as(u32, 0xdeadbeef)); + var y = @intCast(@Vector(4, u16), x); + _ = y; + return error.TestFailed; +} + +// run +// backend=stage1 diff --git a/test/cases/safety/unsigned integer not fitting in cast to signed integer - same bit count.zig b/test/cases/safety/unsigned integer not fitting in cast to signed integer - same bit count.zig new file mode 100644 index 0000000000..6118d5a5ea --- /dev/null +++ b/test/cases/safety/unsigned integer not fitting in cast to signed integer - same bit count.zig @@ -0,0 +1,15 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + var value: u8 = 245; + var casted = @intCast(i8, value); + _ = casted; + return error.TestFailed; +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/unsigned shift left overflow.zig b/test/cases/safety/unsigned shift left overflow.zig new file mode 100644 index 0000000000..3fa2658c3f --- /dev/null +++ b/test/cases/safety/unsigned shift left overflow.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +pub fn main() !void { + const x = shl(0b0010111111111111, 3); + if (x == 0) return error.Whatever; + return error.TestFailed; +} +fn shl(a: u16, b: u4) u16 { + return @shlExact(a, b); +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/unsigned shift right overflow.zig b/test/cases/safety/unsigned shift right overflow.zig new file mode 100644 index 0000000000..0953229a67 --- /dev/null +++ b/test/cases/safety/unsigned shift right overflow.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +pub fn main() !void { + const x = shr(0b0010111111111111, 3); + if (x == 0) return error.Whatever; + return error.TestFailed; +} +fn shr(a: u16, b: u4) u16 { + return @shrExact(a, b); +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/unsigned-signed vector cast.zig b/test/cases/safety/unsigned-signed vector cast.zig new file mode 100644 index 0000000000..9c157d8f40 --- /dev/null +++ b/test/cases/safety/unsigned-signed vector cast.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "integer cast truncated bits")) { + std.process.exit(0); + } + std.process.exit(1); +} + +pub fn main() !void { + var x = @splat(4, @as(u32, 0x80000000)); + var y = @intCast(@Vector(4, i32), x); + _ = y; + return error.TestFailed; +} + +// run +// backend=stage1 diff --git a/test/cases/safety/unwrap error.zig b/test/cases/safety/unwrap error.zig new file mode 100644 index 0000000000..451b9b3891 --- /dev/null +++ b/test/cases/safety/unwrap error.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "attempt to unwrap error: Whatever")) { + std.process.exit(0); + } + std.process.exit(1); +} +pub fn main() !void { + bar() catch unreachable; + return error.TestFailed; +} +fn bar() !void { + return error.Whatever; +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/value does not fit in shortening cast - u0.zig b/test/cases/safety/value does not fit in shortening cast - u0.zig new file mode 100644 index 0000000000..3bbcaf972f --- /dev/null +++ b/test/cases/safety/value does not fit in shortening cast - u0.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +pub fn main() !void { + const x = shorten_cast(1); + if (x == 0) return error.Whatever; + return error.TestFailed; +} +fn shorten_cast(x: u8) u0 { + return @intCast(u0, x); +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/value does not fit in shortening cast.zig b/test/cases/safety/value does not fit in shortening cast.zig new file mode 100644 index 0000000000..2c7409d225 --- /dev/null +++ b/test/cases/safety/value does not fit in shortening cast.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} + +pub fn main() !void { + const x = shorten_cast(200); + if (x == 0) return error.Whatever; + return error.TestFailed; +} +fn shorten_cast(x: i32) i8 { + return @intCast(i8, x); +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/vector integer addition overflow.zig b/test/cases/safety/vector integer addition overflow.zig new file mode 100644 index 0000000000..32b045cd56 --- /dev/null +++ b/test/cases/safety/vector integer addition overflow.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + var a: @Vector(4, i32) = [_]i32{ 1, 2, 2147483643, 4 }; + var b: @Vector(4, i32) = [_]i32{ 5, 6, 7, 8 }; + const x = add(a, b); + _ = x; + return error.TestFailed; +} +fn add(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) { + return a + b; +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/vector integer multiplication overflow.zig b/test/cases/safety/vector integer multiplication overflow.zig new file mode 100644 index 0000000000..2c9d48e1fd --- /dev/null +++ b/test/cases/safety/vector integer multiplication overflow.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + var a: @Vector(4, u8) = [_]u8{ 1, 2, 200, 4 }; + var b: @Vector(4, u8) = [_]u8{ 5, 6, 2, 8 }; + const x = mul(b, a); + _ = x; + return error.TestFailed; +} +fn mul(a: @Vector(4, u8), b: @Vector(4, u8)) @Vector(4, u8) { + return a * b; +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/vector integer negation overflow.zig b/test/cases/safety/vector integer negation overflow.zig new file mode 100644 index 0000000000..81506da41c --- /dev/null +++ b/test/cases/safety/vector integer negation overflow.zig @@ -0,0 +1,18 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + var a: @Vector(4, i16) = [_]i16{ 1, -32768, 200, 4 }; + const x = neg(a); + _ = x; + return error.TestFailed; +} +fn neg(a: @Vector(4, i16)) @Vector(4, i16) { + return -a; +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/cases/safety/vector integer subtraction overflow.zig b/test/cases/safety/vector integer subtraction overflow.zig new file mode 100644 index 0000000000..c3a21e971c --- /dev/null +++ b/test/cases/safety/vector integer subtraction overflow.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { + _ = message; + _ = stack_trace; + std.process.exit(0); +} +pub fn main() !void { + var a: @Vector(4, u32) = [_]u32{ 1, 2, 8, 4 }; + var b: @Vector(4, u32) = [_]u32{ 5, 6, 7, 8 }; + const x = sub(b, a); + _ = x; + return error.TestFailed; +} +fn sub(a: @Vector(4, u32), b: @Vector(4, u32)) @Vector(4, u32) { + return a - b; +} +// run +// backend=stage1 \ No newline at end of file diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig deleted file mode 100644 index f73226159a..0000000000 --- a/test/runtime_safety.zig +++ /dev/null @@ -1,1206 +0,0 @@ -const tests = @import("tests.zig"); - -pub fn addCases(cases: *tests.CompareOutputContext) void { - { - const check_panic_msg = - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = stack_trace; - \\ if (std.mem.eql(u8, message, "reached unreachable code")) { - \\ std.process.exit(126); // good - \\ } - \\ std.process.exit(0); // test failed - \\} - ; - - cases.addRuntimeSafety("switch on corrupted enum value", - \\const std = @import("std"); - ++ check_panic_msg ++ - \\const E = enum(u32) { - \\ X = 1, - \\}; - \\pub fn main() void { - \\ var e: E = undefined; - \\ @memset(@ptrCast([*]u8, &e), 0x55, @sizeOf(E)); - \\ switch (e) { - \\ .X => @breakpoint(), - \\ } - \\} - ); - - cases.addRuntimeSafety("switch on corrupted union value", - \\const std = @import("std"); - ++ check_panic_msg ++ - \\const U = union(enum(u32)) { - \\ X: u8, - \\}; - \\pub fn main() void { - \\ var u: U = undefined; - \\ @memset(@ptrCast([*]u8, &u), 0x55, @sizeOf(U)); - \\ switch (u) { - \\ .X => @breakpoint(), - \\ } - \\} - ); - } - - { - const check_panic_msg = - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = stack_trace; - \\ if (std.mem.eql(u8, message, "invalid enum value")) { - \\ std.process.exit(126); // good - \\ } - \\ std.process.exit(0); // test failed - \\} - ; - - cases.addRuntimeSafety("@tagName on corrupted enum value", - \\const std = @import("std"); - ++ check_panic_msg ++ - \\const E = enum(u32) { - \\ X = 1, - \\}; - \\pub fn main() void { - \\ var e: E = undefined; - \\ @memset(@ptrCast([*]u8, &e), 0x55, @sizeOf(E)); - \\ var n = @tagName(e); - \\ _ = n; - \\} - ); - - cases.addRuntimeSafety("@tagName on corrupted union value", - \\const std = @import("std"); - ++ check_panic_msg ++ - \\const U = union(enum(u32)) { - \\ X: u8, - \\}; - \\pub fn main() void { - \\ var u: U = undefined; - \\ @memset(@ptrCast([*]u8, &u), 0x55, @sizeOf(U)); - \\ var t: @typeInfo(U).Union.tag_type.? = u; - \\ var n = @tagName(t); - \\ _ = n; - \\} - ); - } - - { - const check_panic_msg = - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = stack_trace; - \\ if (std.mem.eql(u8, message, "index out of bounds")) { - \\ std.process.exit(126); // good - \\ } - \\ std.process.exit(0); // test failed - \\} - ; - - cases.addRuntimeSafety("slice with sentinel out of bounds", - \\const std = @import("std"); - ++ check_panic_msg ++ - \\pub fn main() void { - \\ var buf = [4]u8{'a','b','c',0}; - \\ const input: []u8 = &buf; - \\ const slice = input[0..4 :0]; - \\ _ = slice; - \\} - ); - cases.addRuntimeSafety("empty slice with sentinel out of bounds", - \\const std = @import("std"); - ++ check_panic_msg ++ - \\pub fn main() void { - \\ var buf_zero = [0]u8{}; - \\ const input: []u8 = &buf_zero; - \\ const slice = input[0..0 :0]; - \\ _ = slice; - \\} - ); - } - - cases.addRuntimeSafety("truncating vector cast", - \\const std = @import("std"); - \\const V = @import("std").meta.Vector; - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = stack_trace; - \\ if (std.mem.eql(u8, message, "integer cast truncated bits")) { - \\ std.process.exit(126); // good - \\ } - \\ std.process.exit(0); // test failed - \\} - \\pub fn main() void { - \\ var x = @splat(4, @as(u32, 0xdeadbeef)); - \\ var y = @intCast(V(4, u16), x); - \\ _ = y; - \\} - ); - - cases.addRuntimeSafety("unsigned-signed vector cast", - \\const std = @import("std"); - \\const V = @import("std").meta.Vector; - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = stack_trace; - \\ if (std.mem.eql(u8, message, "integer cast truncated bits")) { - \\ std.process.exit(126); // good - \\ } - \\ std.process.exit(0); // test failed - \\} - \\pub fn main() void { - \\ var x = @splat(4, @as(u32, 0x80000000)); - \\ var y = @intCast(V(4, i32), x); - \\ _ = y; - \\} - ); - - cases.addRuntimeSafety("signed-unsigned vector cast", - \\const std = @import("std"); - \\const V = @import("std").meta.Vector; - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = stack_trace; - \\ if (std.mem.eql(u8, message, "attempt to cast negative value to unsigned integer")) { - \\ std.process.exit(126); // good - \\ } - \\ std.process.exit(0); // test failed - \\} - \\pub fn main() void { - \\ var x = @splat(4, @as(i32, -2147483647)); - \\ var y = @intCast(V(4, u32), x); - \\ _ = y; - \\} - ); - - cases.addRuntimeSafety("shift left by huge amount", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = stack_trace; - \\ if (std.mem.eql(u8, message, "shift amount is greater than the type size")) { - \\ std.process.exit(126); // good - \\ } - \\ std.process.exit(0); // test failed - \\} - \\pub fn main() void { - \\ var x: u24 = 42; - \\ var y: u5 = 24; - \\ var z = x >> y; - \\ _ = z; - \\} - ); - - cases.addRuntimeSafety("shift right by huge amount", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = stack_trace; - \\ if (std.mem.eql(u8, message, "shift amount is greater than the type size")) { - \\ std.process.exit(126); // good - \\ } - \\ std.process.exit(0); // test failed - \\} - \\pub fn main() void { - \\ var x: u24 = 42; - \\ var y: u5 = 24; - \\ var z = x << y; - \\ _ = z; - \\} - ); - - cases.addRuntimeSafety("slice sentinel mismatch - optional pointers", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = stack_trace; - \\ if (std.mem.eql(u8, message, "sentinel mismatch")) { - \\ std.process.exit(126); // good - \\ } - \\ std.process.exit(0); // test failed - \\} - \\pub fn main() void { - \\ var buf: [4]?*i32 = undefined; - \\ const slice = buf[0..3 :null]; - \\ _ = slice; - \\} - ); - - cases.addRuntimeSafety("slice sentinel mismatch - floats", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = stack_trace; - \\ if (std.mem.eql(u8, message, "sentinel mismatch")) { - \\ std.process.exit(126); // good - \\ } - \\ std.process.exit(0); // test failed - \\} - \\pub fn main() void { - \\ var buf: [4]f32 = undefined; - \\ const slice = buf[0..3 :1.2]; - \\ _ = slice; - \\} - ); - - cases.addRuntimeSafety("pointer slice sentinel mismatch", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = stack_trace; - \\ if (std.mem.eql(u8, message, "sentinel mismatch")) { - \\ std.process.exit(126); // good - \\ } - \\ std.process.exit(0); // test failed - \\} - \\pub fn main() void { - \\ var buf: [4]u8 = undefined; - \\ const ptr: [*]u8 = &buf; - \\ const slice = ptr[0..3 :0]; - \\ _ = slice; - \\} - ); - - cases.addRuntimeSafety("slice slice sentinel mismatch", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = stack_trace; - \\ if (std.mem.eql(u8, message, "sentinel mismatch")) { - \\ std.process.exit(126); // good - \\ } - \\ std.process.exit(0); // test failed - \\} - \\pub fn main() void { - \\ var buf: [4]u8 = undefined; - \\ const slice = buf[0..]; - \\ const slice2 = slice[0..3 :0]; - \\ _ = slice2; - \\} - ); - - cases.addRuntimeSafety("array slice sentinel mismatch", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = stack_trace; - \\ if (std.mem.eql(u8, message, "sentinel mismatch")) { - \\ std.process.exit(126); // good - \\ } - \\ std.process.exit(0); // test failed - \\} - \\pub fn main() void { - \\ var buf: [4]u8 = undefined; - \\ const slice = buf[0..3 :0]; - \\ _ = slice; - \\} - ); - - cases.addRuntimeSafety("intToPtr with misaligned address", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = stack_trace; - \\ if (std.mem.eql(u8, message, "incorrect alignment")) { - \\ std.os.exit(126); // good - \\ } - \\ std.os.exit(0); // test failed - \\} - \\pub fn main() void { - \\ var x: usize = 5; - \\ var y = @intToPtr([*]align(4) u8, x); - \\ _ = y; - \\} - ); - - cases.addRuntimeSafety("resuming a non-suspended function which never been suspended", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\fn foo() void { - \\ var f = async bar(@frame()); - \\ _ = f; - \\ std.os.exit(0); - \\} - \\ - \\fn bar(frame: anyframe) void { - \\ suspend { - \\ resume frame; - \\ } - \\ std.os.exit(0); - \\} - \\ - \\pub fn main() void { - \\ _ = async foo(); - \\} - ); - - cases.addRuntimeSafety("resuming a non-suspended function which has been suspended and resumed", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\fn foo() void { - \\ suspend { - \\ global_frame = @frame(); - \\ } - \\ var f = async bar(@frame()); - \\ _ = f; - \\ std.os.exit(0); - \\} - \\ - \\fn bar(frame: anyframe) void { - \\ suspend { - \\ resume frame; - \\ } - \\ std.os.exit(0); - \\} - \\ - \\var global_frame: anyframe = undefined; - \\pub fn main() void { - \\ _ = async foo(); - \\ resume global_frame; - \\ std.os.exit(0); - \\} - ); - - cases.addRuntimeSafety("nosuspend function call, callee suspends", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ _ = nosuspend add(101, 100); - \\} - \\fn add(a: i32, b: i32) i32 { - \\ if (a > 100) { - \\ suspend {} - \\ } - \\ return a + b; - \\} - ); - - cases.addRuntimeSafety("awaiting twice", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\var frame: anyframe = undefined; - \\ - \\pub fn main() void { - \\ _ = async amain(); - \\ resume frame; - \\} - \\ - \\fn amain() void { - \\ var f = async func(); - \\ await f; - \\ await f; - \\} - \\ - \\fn func() void { - \\ suspend { - \\ frame = @frame(); - \\ } - \\} - ); - - cases.addRuntimeSafety("@asyncCall with too small a frame", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ var bytes: [1]u8 align(16) = undefined; - \\ var ptr = other; - \\ var frame = @asyncCall(&bytes, {}, ptr, .{}); - \\ _ = frame; - \\} - \\fn other() callconv(.Async) void { - \\ suspend {} - \\} - ); - - cases.addRuntimeSafety("resuming a function which is awaiting a frame", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ var frame = async first(); - \\ resume frame; - \\} - \\fn first() void { - \\ var frame = async other(); - \\ await frame; - \\} - \\fn other() void { - \\ suspend {} - \\} - ); - - cases.addRuntimeSafety("resuming a function which is awaiting a call", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ var frame = async first(); - \\ resume frame; - \\} - \\fn first() void { - \\ other(); - \\} - \\fn other() void { - \\ suspend {} - \\} - ); - - cases.addRuntimeSafety("invalid resume of async function", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ var p = async suspendOnce(); - \\ resume p; //ok - \\ resume p; //bad - \\} - \\fn suspendOnce() void { - \\ suspend {} - \\} - ); - - cases.addRuntimeSafety(".? operator on null pointer", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ var ptr: ?*i32 = null; - \\ var b = ptr.?; - \\ _ = b; - \\} - ); - - cases.addRuntimeSafety(".? operator on C pointer", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ var ptr: [*c]i32 = null; - \\ var b = ptr.?; - \\ _ = b; - \\} - ); - - cases.addRuntimeSafety("@intToPtr address zero to non-optional pointer", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ var zero: usize = 0; - \\ var b = @intToPtr(*i32, zero); - \\ _ = b; - \\} - ); - - cases.addRuntimeSafety("@intToPtr address zero to non-optional byte-aligned pointer", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ var zero: usize = 0; - \\ var b = @intToPtr(*u8, zero); - \\ _ = b; - \\} - ); - - cases.addRuntimeSafety("pointer casting null to non-optional pointer", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ var c_ptr: [*c]u8 = 0; - \\ var zig_ptr: *u8 = c_ptr; - \\ _ = zig_ptr; - \\} - ); - - cases.addRuntimeSafety("@intToEnum - no matching tag value", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\const Foo = enum { - \\ A, - \\ B, - \\ C, - \\}; - \\pub fn main() void { - \\ baz(bar(3)); - \\} - \\fn bar(a: u2) Foo { - \\ return @intToEnum(Foo, a); - \\} - \\fn baz(_: Foo) void {} - ); - - cases.addRuntimeSafety("@floatToInt cannot fit - negative to unsigned", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ baz(bar(-1.1)); - \\} - \\fn bar(a: f32) u8 { - \\ return @floatToInt(u8, a); - \\} - \\fn baz(_: u8) void { } - ); - - cases.addRuntimeSafety("@floatToInt cannot fit - negative out of range", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ baz(bar(-129.1)); - \\} - \\fn bar(a: f32) i8 { - \\ return @floatToInt(i8, a); - \\} - \\fn baz(_: i8) void { } - ); - - cases.addRuntimeSafety("@floatToInt cannot fit - positive out of range", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ baz(bar(256.2)); - \\} - \\fn bar(a: f32) u8 { - \\ return @floatToInt(u8, a); - \\} - \\fn baz(_: u8) void { } - ); - - cases.addRuntimeSafety("calling panic", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ @panic("oh no"); - \\} - ); - - cases.addRuntimeSafety("out of bounds slice access", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ const a = [_]i32{1, 2, 3, 4}; - \\ baz(bar(&a)); - \\} - \\fn bar(a: []const i32) i32 { - \\ return a[4]; - \\} - \\fn baz(_: i32) void { } - ); - - cases.addRuntimeSafety("integer addition overflow", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() !void { - \\ const x = add(65530, 10); - \\ if (x == 0) return error.Whatever; - \\} - \\fn add(a: u16, b: u16) u16 { - \\ return a + b; - \\} - ); - - cases.addRuntimeSafety("vector integer addition overflow", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ var a: @Vector(4, i32) = [_]i32{ 1, 2, 2147483643, 4 }; - \\ var b: @Vector(4, i32) = [_]i32{ 5, 6, 7, 8 }; - \\ const x = add(a, b); - \\ _ = x; - \\} - \\fn add(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) { - \\ return a + b; - \\} - ); - - cases.addRuntimeSafety("vector integer subtraction overflow", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ var a: @Vector(4, u32) = [_]u32{ 1, 2, 8, 4 }; - \\ var b: @Vector(4, u32) = [_]u32{ 5, 6, 7, 8 }; - \\ const x = sub(b, a); - \\ _ = x; - \\} - \\fn sub(a: @Vector(4, u32), b: @Vector(4, u32)) @Vector(4, u32) { - \\ return a - b; - \\} - ); - - cases.addRuntimeSafety("vector integer multiplication overflow", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ var a: @Vector(4, u8) = [_]u8{ 1, 2, 200, 4 }; - \\ var b: @Vector(4, u8) = [_]u8{ 5, 6, 2, 8 }; - \\ const x = mul(b, a); - \\ _ = x; - \\} - \\fn mul(a: @Vector(4, u8), b: @Vector(4, u8)) @Vector(4, u8) { - \\ return a * b; - \\} - ); - - cases.addRuntimeSafety("vector integer negation overflow", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ var a: @Vector(4, i16) = [_]i16{ 1, -32768, 200, 4 }; - \\ const x = neg(a); - \\ _ = x; - \\} - \\fn neg(a: @Vector(4, i16)) @Vector(4, i16) { - \\ return -a; - \\} - ); - - cases.addRuntimeSafety("integer subtraction overflow", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() !void { - \\ const x = sub(10, 20); - \\ if (x == 0) return error.Whatever; - \\} - \\fn sub(a: u16, b: u16) u16 { - \\ return a - b; - \\} - ); - - cases.addRuntimeSafety("integer multiplication overflow", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() !void { - \\ const x = mul(300, 6000); - \\ if (x == 0) return error.Whatever; - \\} - \\fn mul(a: u16, b: u16) u16 { - \\ return a * b; - \\} - ); - - cases.addRuntimeSafety("integer negation overflow", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() !void { - \\ const x = neg(-32768); - \\ if (x == 32767) return error.Whatever; - \\} - \\fn neg(a: i16) i16 { - \\ return -a; - \\} - ); - - cases.addRuntimeSafety("signed integer division overflow", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() !void { - \\ const x = div(-32768, -1); - \\ if (x == 32767) return error.Whatever; - \\} - \\fn div(a: i16, b: i16) i16 { - \\ return @divTrunc(a, b); - \\} - ); - - cases.addRuntimeSafety("signed integer division overflow - vectors", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() !void { - \\ var a: @Vector(4, i16) = [_]i16{ 1, 2, -32768, 4 }; - \\ var b: @Vector(4, i16) = [_]i16{ 1, 2, -1, 4 }; - \\ const x = div(a, b); - \\ if (x[2] == 32767) return error.Whatever; - \\} - \\fn div(a: @Vector(4, i16), b: @Vector(4, i16)) @Vector(4, i16) { - \\ return @divTrunc(a, b); - \\} - ); - - cases.addRuntimeSafety("signed shift left overflow", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() !void { - \\ const x = shl(-16385, 1); - \\ if (x == 0) return error.Whatever; - \\} - \\fn shl(a: i16, b: u4) i16 { - \\ return @shlExact(a, b); - \\} - ); - - cases.addRuntimeSafety("unsigned shift left overflow", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() !void { - \\ const x = shl(0b0010111111111111, 3); - \\ if (x == 0) return error.Whatever; - \\} - \\fn shl(a: u16, b: u4) u16 { - \\ return @shlExact(a, b); - \\} - ); - - cases.addRuntimeSafety("signed shift right overflow", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() !void { - \\ const x = shr(-16385, 1); - \\ if (x == 0) return error.Whatever; - \\} - \\fn shr(a: i16, b: u4) i16 { - \\ return @shrExact(a, b); - \\} - ); - - cases.addRuntimeSafety("unsigned shift right overflow", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() !void { - \\ const x = shr(0b0010111111111111, 3); - \\ if (x == 0) return error.Whatever; - \\} - \\fn shr(a: u16, b: u4) u16 { - \\ return @shrExact(a, b); - \\} - ); - - cases.addRuntimeSafety("integer division by zero", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ const x = div0(999, 0); - \\ _ = x; - \\} - \\fn div0(a: i32, b: i32) i32 { - \\ return @divTrunc(a, b); - \\} - ); - - cases.addRuntimeSafety("integer division by zero - vectors", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ var a: @Vector(4, i32) = [4]i32{111, 222, 333, 444}; - \\ var b: @Vector(4, i32) = [4]i32{111, 0, 333, 444}; - \\ const x = div0(a, b); - \\ _ = x; - \\} - \\fn div0(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) { - \\ return @divTrunc(a, b); - \\} - ); - - cases.addRuntimeSafety("exact division failure", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() !void { - \\ const x = divExact(10, 3); - \\ if (x == 0) return error.Whatever; - \\} - \\fn divExact(a: i32, b: i32) i32 { - \\ return @divExact(a, b); - \\} - ); - - cases.addRuntimeSafety("exact division failure - vectors", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() !void { - \\ var a: @Vector(4, i32) = [4]i32{111, 222, 333, 444}; - \\ var b: @Vector(4, i32) = [4]i32{111, 222, 333, 441}; - \\ const x = divExact(a, b); - \\ _ = x; - \\} - \\fn divExact(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) { - \\ return @divExact(a, b); - \\} - ); - - cases.addRuntimeSafety("cast []u8 to bigger slice of wrong size", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() !void { - \\ const x = widenSlice(&[_]u8{1, 2, 3, 4, 5}); - \\ if (x.len == 0) return error.Whatever; - \\} - \\fn widenSlice(slice: []align(1) const u8) []align(1) const i32 { - \\ return std.mem.bytesAsSlice(i32, slice); - \\} - ); - - cases.addRuntimeSafety("value does not fit in shortening cast", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() !void { - \\ const x = shorten_cast(200); - \\ if (x == 0) return error.Whatever; - \\} - \\fn shorten_cast(x: i32) i8 { - \\ return @intCast(i8, x); - \\} - ); - - cases.addRuntimeSafety("value does not fit in shortening cast - u0", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() !void { - \\ const x = shorten_cast(1); - \\ if (x == 0) return error.Whatever; - \\} - \\fn shorten_cast(x: u8) u0 { - \\ return @intCast(u0, x); - \\} - ); - - cases.addRuntimeSafety("signed integer not fitting in cast to unsigned integer", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() !void { - \\ const x = unsigned_cast(-10); - \\ if (x == 0) return error.Whatever; - \\} - \\fn unsigned_cast(x: i32) u32 { - \\ return @intCast(u32, x); - \\} - ); - - cases.addRuntimeSafety("signed integer not fitting in cast to unsigned integer - widening", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ var value: c_short = -1; - \\ var casted = @intCast(u32, value); - \\ _ = casted; - \\} - ); - - cases.addRuntimeSafety("unsigned integer not fitting in cast to signed integer - same bit count", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ var value: u8 = 245; - \\ var casted = @intCast(i8, value); - \\ _ = casted; - \\} - ); - - cases.addRuntimeSafety("unwrap error", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = stack_trace; - \\ if (std.mem.eql(u8, message, "attempt to unwrap error: Whatever")) { - \\ std.os.exit(126); // good - \\ } - \\ std.os.exit(0); // test failed - \\} - \\pub fn main() void { - \\ bar() catch unreachable; - \\} - \\fn bar() !void { - \\ return error.Whatever; - \\} - ); - - cases.addRuntimeSafety("cast integer to global error and no code matches", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() void { - \\ bar(9999) catch {}; - \\} - \\fn bar(x: u16) anyerror { - \\ return @intToError(x); - \\} - ); - - cases.addRuntimeSafety("@errSetCast error not present in destination", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\const Set1 = error{A, B}; - \\const Set2 = error{A, C}; - \\pub fn main() void { - \\ foo(Set1.B) catch {}; - \\} - \\fn foo(set1: Set1) Set2 { - \\ return @errSetCast(Set2, set1); - \\} - ); - - cases.addRuntimeSafety("@alignCast misaligned", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\pub fn main() !void { - \\ var array align(4) = [_]u32{0x11111111, 0x11111111}; - \\ const bytes = std.mem.sliceAsBytes(array[0..]); - \\ if (foo(bytes) != 0x11111111) return error.Wrong; - \\} - \\fn foo(bytes: []u8) u32 { - \\ const slice4 = bytes[1..5]; - \\ const int_slice = std.mem.bytesAsSlice(u32, @alignCast(4, slice4)); - \\ return int_slice[0]; - \\} - ); - - cases.addRuntimeSafety("bad union field access", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\ - \\const Foo = union { - \\ float: f32, - \\ int: u32, - \\}; - \\ - \\pub fn main() void { - \\ var f = Foo { .int = 42 }; - \\ bar(&f); - \\} - \\ - \\fn bar(f: *Foo) void { - \\ f.float = 12.34; - \\} - ); - - // @intCast a runtime integer to u0 actually results in a comptime-known value, - // but we still emit a safety check to ensure the integer was 0 and thus - // did not truncate information. - cases.addRuntimeSafety("@intCast to u0", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\ - \\pub fn main() void { - \\ bar(1, 1); - \\} - \\ - \\fn bar(one: u1, not_zero: i32) void { - \\ var x = one << @intCast(u0, not_zero); - \\ _ = x; - \\} - ); - - // This case makes sure that the code compiles and runs. There is not actually a special - // runtime safety check having to do specifically with error return traces across suspend points. - cases.addRuntimeSafety("error return trace across suspend points", - \\const std = @import("std"); - \\ - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\ - \\var failing_frame: @Frame(failing) = undefined; - \\ - \\pub fn main() void { - \\ const p = nonFailing(); - \\ resume p; - \\ const p2 = async printTrace(p); - \\ _ = p2; - \\} - \\ - \\fn nonFailing() anyframe->anyerror!void { - \\ failing_frame = async failing(); - \\ return &failing_frame; - \\} - \\ - \\fn failing() anyerror!void { - \\ suspend {} - \\ return second(); - \\} - \\ - \\fn second() callconv(.Async) anyerror!void { - \\ return error.Fail; - \\} - \\ - \\fn printTrace(p: anyframe->anyerror!void) void { - \\ (await p) catch unreachable; - \\} - ); - - // Slicing a C pointer returns a non-allowzero slice, thus we need to emit - // a safety check to ensure the pointer is not null. - cases.addRuntimeSafety("slicing null C pointer", - \\const std = @import("std"); - \\pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { - \\ _ = message; - \\ _ = stack_trace; - \\ std.os.exit(126); - \\} - \\ - \\pub fn main() void { - \\ var ptr: [*c]const u32 = null; - \\ var slice = ptr[0..3]; - \\ _ = slice; - \\} - ); -} diff --git a/test/tests.zig b/test/tests.zig index 62c437e7f7..e44b190bb4 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -18,7 +18,6 @@ const compare_output = @import("compare_output.zig"); const standalone = @import("standalone.zig"); const stack_traces = @import("stack_traces.zig"); const assemble_and_link = @import("assemble_and_link.zig"); -const runtime_safety = @import("runtime_safety.zig"); const translate_c = @import("translate_c.zig"); const run_translated_c = @import("run_translated_c.zig"); const gen_h = @import("gen_h.zig"); @@ -455,21 +454,6 @@ pub fn addStackTraceTests(b: *build.Builder, test_filter: ?[]const u8, modes: [] return cases.step; } -pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { - const cases = b.allocator.create(CompareOutputContext) catch unreachable; - cases.* = CompareOutputContext{ - .b = b, - .step = b.step("test-runtime-safety", "Run the runtime safety tests"), - .test_index = 0, - .test_filter = test_filter, - .modes = modes, - }; - - runtime_safety.addCases(cases); - - return cases.step; -} - pub fn addStandaloneTests( b: *build.Builder, test_filter: ?[]const u8, From 7755f7863a30892da3044fb014b1382879162918 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 May 2022 00:16:38 -0700 Subject: [PATCH 1484/2031] disable a runtime safety test that is failing on WASI --- test/cases/safety/@asyncCall with too small a frame.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/cases/safety/@asyncCall with too small a frame.zig b/test/cases/safety/@asyncCall with too small a frame.zig index 8ae4d4ee3f..8315643538 100644 --- a/test/cases/safety/@asyncCall with too small a frame.zig +++ b/test/cases/safety/@asyncCall with too small a frame.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { _ = message; @@ -6,6 +7,10 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noretur std.process.exit(0); } pub fn main() !void { + if (builtin.zig_backend == .stage1 and builtin.os.tag == .wasi) { + // TODO file a bug for this failure + std.process.exit(0); // skip the test + } var bytes: [1]u8 align(16) = undefined; var ptr = other; var frame = @asyncCall(&bytes, {}, ptr, .{}); From 915032715f337fc75a71d305cbda1b72da745f06 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 May 2022 14:02:53 -0700 Subject: [PATCH 1485/2031] test harness: dump stderr when compiler crashes --- src/test.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test.zig b/src/test.zig index abadedad91..317e020dc2 100644 --- a/src/test.zig +++ b/src/test.zig @@ -1330,6 +1330,7 @@ pub const TestContext = struct { } }, else => { + std.debug.print("{s}", .{result.stderr}); dumpArgs(zig_args.items); return error.CompilationCrashed; }, @@ -1403,6 +1404,7 @@ pub const TestContext = struct { } }, else => { + std.debug.print("{s}", .{result.stderr}); dumpArgs(zig_args.items); return error.CompilationCrashed; }, From f6e9b6620d5597edc773540cf46fea30b4f79000 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 May 2022 14:30:43 -0700 Subject: [PATCH 1486/2031] build.zig: rename a local variable --- build.zig | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/build.zig b/build.zig index 300ceed5c0..24d874b924 100644 --- a/build.zig +++ b/build.zig @@ -380,29 +380,30 @@ pub fn build(b: *Builder) !void { const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); - const test_stage2_options = b.addOptions(); - test_cases.addOptions("build_options", test_stage2_options); + const test_cases_options = b.addOptions(); + test_cases.addOptions("build_options", test_cases_options); - test_stage2_options.addOption(bool, "enable_logging", enable_logging); - test_stage2_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots); - test_stage2_options.addOption(bool, "skip_non_native", skip_non_native); - test_stage2_options.addOption(bool, "skip_stage1", skip_stage1); - test_stage2_options.addOption(bool, "is_stage1", is_stage1); - test_stage2_options.addOption(bool, "omit_stage2", omit_stage2); - test_stage2_options.addOption(bool, "have_llvm", enable_llvm); - test_stage2_options.addOption(bool, "llvm_has_m68k", llvm_has_m68k); - test_stage2_options.addOption(bool, "llvm_has_csky", llvm_has_csky); - test_stage2_options.addOption(bool, "llvm_has_ve", llvm_has_ve); - test_stage2_options.addOption(bool, "llvm_has_arc", llvm_has_arc); - test_stage2_options.addOption(bool, "enable_qemu", b.enable_qemu); - test_stage2_options.addOption(bool, "enable_wine", b.enable_wine); - test_stage2_options.addOption(bool, "enable_wasmtime", b.enable_wasmtime); - test_stage2_options.addOption(bool, "enable_rosetta", b.enable_rosetta); - test_stage2_options.addOption(bool, "enable_darling", b.enable_darling); - test_stage2_options.addOption(u32, "mem_leak_frames", mem_leak_frames * 2); - test_stage2_options.addOption(?[]const u8, "glibc_runtimes_dir", b.glibc_runtimes_dir); - test_stage2_options.addOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version)); - test_stage2_options.addOption(std.SemanticVersion, "semver", semver); + test_cases_options.addOption(bool, "enable_logging", enable_logging); + test_cases_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots); + test_cases_options.addOption(bool, "skip_non_native", skip_non_native); + test_cases_options.addOption(bool, "skip_stage1", skip_stage1); + test_cases_options.addOption(bool, "is_stage1", is_stage1); + test_cases_options.addOption(bool, "omit_stage2", omit_stage2); + test_cases_options.addOption(bool, "have_llvm", enable_llvm); + test_cases_options.addOption(bool, "llvm_has_m68k", llvm_has_m68k); + test_cases_options.addOption(bool, "llvm_has_csky", llvm_has_csky); + test_cases_options.addOption(bool, "llvm_has_ve", llvm_has_ve); + test_cases_options.addOption(bool, "llvm_has_arc", llvm_has_arc); + test_cases_options.addOption(bool, "enable_qemu", b.enable_qemu); + test_cases_options.addOption(bool, "enable_wine", b.enable_wine); + test_cases_options.addOption(bool, "enable_wasmtime", b.enable_wasmtime); + test_cases_options.addOption(bool, "enable_rosetta", b.enable_rosetta); + test_cases_options.addOption(bool, "enable_darling", b.enable_darling); + test_cases_options.addOption(u32, "mem_leak_frames", mem_leak_frames * 2); + test_cases_options.addOption(?[]const u8, "glibc_runtimes_dir", b.glibc_runtimes_dir); + test_cases_options.addOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version)); + test_cases_options.addOption(std.SemanticVersion, "semver", semver); + test_cases_options.addOption(?[]const u8, "test_filter", test_filter); const test_cases_step = b.step("test-cases", "Run the main compiler test cases"); test_cases_step.dependOn(&test_cases.step); From b986fcfc996ec54e2f755b1161df1a6ceac009d4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 May 2022 14:31:02 -0700 Subject: [PATCH 1487/2031] test-cases: honor -Dtest-filter argument from zig build --- src/test.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test.zig b/src/test.zig index 317e020dc2..492fa4ff53 100644 --- a/src/test.zig +++ b/src/test.zig @@ -1201,6 +1201,9 @@ pub const TestContext = struct { if (!build_options.have_llvm and case.backend == .llvm) continue; + if (build_options.test_filter) |test_filter| { + if (std.mem.indexOf(u8, case.name, test_filter) == null) continue; + } var prg_node = root_node.start(case.name, case.updates.items.len); prg_node.activate(); defer prg_node.end(); From c30edd78f9ed65c5659f86a63a9b59a88838c618 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 May 2022 14:31:19 -0700 Subject: [PATCH 1488/2031] std.Progress: activate() calls maybeRefresh() This makes the progress bar display the ongoing operation in the case that the API user calls activate(). --- lib/std/Progress.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 07f9077844..925cefcb74 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -93,7 +93,9 @@ pub const Node = struct { /// This is the same as calling `start` and then `end` on the returned `Node`. Thread-safe. pub fn completeOne(self: *Node) void { - self.activate(); + if (self.parent) |parent| { + @atomicStore(?*Node, &parent.recently_updated_child, self, .Release); + } _ = @atomicRmw(usize, &self.unprotected_completed_items, .Add, 1, .Monotonic); self.context.maybeRefresh(); } @@ -120,6 +122,7 @@ pub const Node = struct { pub fn activate(self: *Node) void { if (self.parent) |parent| { @atomicStore(?*Node, &parent.recently_updated_child, self, .Release); + self.context.maybeRefresh(); } } From 7d8b90b9050a3e957e4ab936b149f3b45b6b6000 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 May 2022 17:32:23 -0700 Subject: [PATCH 1489/2031] test harness: actually run the stage1 "run" tests --- src/test.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test.zig b/src/test.zig index 492fa4ff53..585c6d62e3 100644 --- a/src/test.zig +++ b/src/test.zig @@ -1294,6 +1294,8 @@ pub const TestContext = struct { if (case.is_test) { try zig_args.append("test"); + } else if (update.case == .Execution) { + try zig_args.append("run"); } else switch (case.output_mode) { .Obj => try zig_args.append("build-obj"), .Exe => try zig_args.append("build-exe"), @@ -1402,6 +1404,7 @@ pub const TestContext = struct { switch (result.term) { .Exited => |code| { if (code != 0) { + std.debug.print("{s}", .{result.stderr}); dumpArgs(zig_args.items); return error.CompilationFailed; } From 0cd43b0f8686075cf9bb8b8655ca828bd329d60f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 May 2022 17:58:32 -0700 Subject: [PATCH 1490/2031] runtime safety tests only on the native target This matches master branch. We can look into adding more target coverage as we switch to stage2. As it stands, this works around having to duplicate the "Executor" logic to figure out when to not run the tests due to them being non-native. --- test/cases/safety/@alignCast misaligned.zig | 3 ++- test/cases/safety/@asyncCall with too small a frame.zig | 1 + .../safety/@errSetCast error not present in destination.zig | 3 ++- .../safety/@floatToInt cannot fit - negative out of range.zig | 3 ++- .../safety/@floatToInt cannot fit - negative to unsigned.zig | 3 ++- .../safety/@floatToInt cannot fit - positive out of range.zig | 3 ++- test/cases/safety/@intCast to u0.zig | 3 ++- test/cases/safety/@intToEnum - no matching tag value.zig | 1 + ...ToPtr address zero to non-optional byte-aligned pointer.zig | 1 + .../safety/@intToPtr address zero to non-optional pointer.zig | 1 + test/cases/safety/@tagName on corrupted enum value.zig | 1 + test/cases/safety/@tagName on corrupted union value.zig | 1 + test/cases/safety/array slice sentinel mismatch.zig | 2 +- test/cases/safety/awaiting twice.zig | 1 + test/cases/safety/bad union field access.zig | 3 ++- test/cases/safety/calling panic.zig | 3 ++- test/cases/safety/cast []u8 to bigger slice of wrong size.zig | 3 ++- .../cast integer to global error and no code matches.zig | 3 ++- test/cases/safety/empty slice with sentinel out of bounds.zig | 1 + test/cases/safety/error return trace across suspend points.zig | 3 ++- test/cases/safety/exact division failure - vectors.zig | 3 ++- test/cases/safety/exact division failure.zig | 3 ++- test/cases/safety/intToPtr with misaligned address.zig | 1 + test/cases/safety/integer addition overflow.zig | 1 + test/cases/safety/integer division by zero - vectors.zig | 3 ++- test/cases/safety/integer division by zero.zig | 3 ++- test/cases/safety/integer multiplication overflow.zig | 3 ++- test/cases/safety/integer negation overflow.zig | 3 ++- test/cases/safety/integer subtraction overflow.zig | 3 ++- test/cases/safety/invalid resume of async function.zig | 1 + test/cases/safety/nosuspend function call, callee suspends.zig | 1 + test/cases/safety/optional unwrap operator on C pointer.zig | 1 + test/cases/safety/optional unwrap operator on null pointer.zig | 1 + test/cases/safety/out of bounds slice access.zig | 3 ++- .../safety/pointer casting null to non-optional pointer.zig | 1 + test/cases/safety/pointer slice sentinel mismatch.zig | 1 + .../safety/resuming a function which is awaiting a call.zig | 1 + .../safety/resuming a function which is awaiting a frame.zig | 1 + ...suspended function which has been suspended and resumed.zig | 1 + ...ing a non-suspended function which never been suspended.zig | 1 + test/cases/safety/shift left by huge amount.zig | 1 + test/cases/safety/shift right by huge amount.zig | 1 + .../safety/signed integer division overflow - vectors.zig | 3 ++- test/cases/safety/signed integer division overflow.zig | 3 ++- ...eger not fitting in cast to unsigned integer - widening.zig | 3 ++- .../signed integer not fitting in cast to unsigned integer.zig | 3 ++- test/cases/safety/signed shift left overflow.zig | 3 ++- test/cases/safety/signed shift right overflow.zig | 3 ++- test/cases/safety/signed-unsigned vector cast.zig | 1 + test/cases/safety/slice sentinel mismatch - floats.zig | 1 + .../safety/slice sentinel mismatch - optional pointers.zig | 1 + test/cases/safety/slice slice sentinel mismatch.zig | 1 + test/cases/safety/slice with sentinel out of bounds.zig | 1 + test/cases/safety/slicing null C pointer.zig | 3 ++- test/cases/safety/switch on corrupted enum value.zig | 1 + test/cases/safety/switch on corrupted union value.zig | 1 + test/cases/safety/truncating vector cast.zig | 1 + ... not fitting in cast to signed integer - same bit count.zig | 3 ++- test/cases/safety/unsigned shift left overflow.zig | 3 ++- test/cases/safety/unsigned shift right overflow.zig | 3 ++- test/cases/safety/unsigned-signed vector cast.zig | 1 + test/cases/safety/unwrap error.zig | 3 ++- .../safety/value does not fit in shortening cast - u0.zig | 3 ++- test/cases/safety/value does not fit in shortening cast.zig | 3 ++- test/cases/safety/vector integer addition overflow.zig | 3 ++- test/cases/safety/vector integer multiplication overflow.zig | 3 ++- test/cases/safety/vector integer negation overflow.zig | 3 ++- test/cases/safety/vector integer subtraction overflow.zig | 3 ++- 68 files changed, 104 insertions(+), 37 deletions(-) diff --git a/test/cases/safety/@alignCast misaligned.zig b/test/cases/safety/@alignCast misaligned.zig index 3948cea443..2708d63e6f 100644 --- a/test/cases/safety/@alignCast misaligned.zig +++ b/test/cases/safety/@alignCast misaligned.zig @@ -18,4 +18,5 @@ fn foo(bytes: []u8) u32 { return int_slice[0]; } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/@asyncCall with too small a frame.zig b/test/cases/safety/@asyncCall with too small a frame.zig index 8315643538..e245e9bb47 100644 --- a/test/cases/safety/@asyncCall with too small a frame.zig +++ b/test/cases/safety/@asyncCall with too small a frame.zig @@ -22,3 +22,4 @@ fn other() callconv(.Async) void { } // run // backend=stage1 +// target=native diff --git a/test/cases/safety/@errSetCast error not present in destination.zig b/test/cases/safety/@errSetCast error not present in destination.zig index 3934307e9e..5bb92f4f28 100644 --- a/test/cases/safety/@errSetCast error not present in destination.zig +++ b/test/cases/safety/@errSetCast error not present in destination.zig @@ -15,4 +15,5 @@ fn foo(set1: Set1) Set2 { return @errSetCast(Set2, set1); } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/@floatToInt cannot fit - negative out of range.zig b/test/cases/safety/@floatToInt cannot fit - negative out of range.zig index 1e7016830e..f6b2d632f2 100644 --- a/test/cases/safety/@floatToInt cannot fit - negative out of range.zig +++ b/test/cases/safety/@floatToInt cannot fit - negative out of range.zig @@ -14,4 +14,5 @@ fn bar(a: f32) i8 { } fn baz(_: i8) void { } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/@floatToInt cannot fit - negative to unsigned.zig b/test/cases/safety/@floatToInt cannot fit - negative to unsigned.zig index fbcc7fc11d..22e9def050 100644 --- a/test/cases/safety/@floatToInt cannot fit - negative to unsigned.zig +++ b/test/cases/safety/@floatToInt cannot fit - negative to unsigned.zig @@ -14,4 +14,5 @@ fn bar(a: f32) u8 { } fn baz(_: u8) void { } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/@floatToInt cannot fit - positive out of range.zig b/test/cases/safety/@floatToInt cannot fit - positive out of range.zig index 1ab83edafa..67fecde115 100644 --- a/test/cases/safety/@floatToInt cannot fit - positive out of range.zig +++ b/test/cases/safety/@floatToInt cannot fit - positive out of range.zig @@ -14,4 +14,5 @@ fn bar(a: f32) u8 { } fn baz(_: u8) void { } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/@intCast to u0.zig b/test/cases/safety/@intCast to u0.zig index 10c3d22213..7d51fdec25 100644 --- a/test/cases/safety/@intCast to u0.zig +++ b/test/cases/safety/@intCast to u0.zig @@ -16,4 +16,5 @@ fn bar(one: u1, not_zero: i32) void { _ = x; } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/@intToEnum - no matching tag value.zig b/test/cases/safety/@intToEnum - no matching tag value.zig index 7f66dc050d..79fcf33bc6 100644 --- a/test/cases/safety/@intToEnum - no matching tag value.zig +++ b/test/cases/safety/@intToEnum - no matching tag value.zig @@ -20,3 +20,4 @@ fn bar(a: u2) Foo { fn baz(_: Foo) void {} // run // backend=stage1 +// target=native diff --git a/test/cases/safety/@intToPtr address zero to non-optional byte-aligned pointer.zig b/test/cases/safety/@intToPtr address zero to non-optional byte-aligned pointer.zig index 6c44505af9..ac781dc53f 100644 --- a/test/cases/safety/@intToPtr address zero to non-optional byte-aligned pointer.zig +++ b/test/cases/safety/@intToPtr address zero to non-optional byte-aligned pointer.zig @@ -13,3 +13,4 @@ pub fn main() !void { } // run // backend=stage1 +// target=native diff --git a/test/cases/safety/@intToPtr address zero to non-optional pointer.zig b/test/cases/safety/@intToPtr address zero to non-optional pointer.zig index 9f61766884..f8fd53eb97 100644 --- a/test/cases/safety/@intToPtr address zero to non-optional pointer.zig +++ b/test/cases/safety/@intToPtr address zero to non-optional pointer.zig @@ -13,3 +13,4 @@ pub fn main() !void { } // run // backend=stage1 +// target=native diff --git a/test/cases/safety/@tagName on corrupted enum value.zig b/test/cases/safety/@tagName on corrupted enum value.zig index 577ba183d5..507157911e 100644 --- a/test/cases/safety/@tagName on corrupted enum value.zig +++ b/test/cases/safety/@tagName on corrupted enum value.zig @@ -22,3 +22,4 @@ pub fn main() !void { // run // backend=stage1 +// target=native diff --git a/test/cases/safety/@tagName on corrupted union value.zig b/test/cases/safety/@tagName on corrupted union value.zig index 6012e86833..0c35b5ef3d 100644 --- a/test/cases/safety/@tagName on corrupted union value.zig +++ b/test/cases/safety/@tagName on corrupted union value.zig @@ -23,3 +23,4 @@ pub fn main() !void { // run // backend=stage1 +// target=native diff --git a/test/cases/safety/array slice sentinel mismatch.zig b/test/cases/safety/array slice sentinel mismatch.zig index 83e8b1f787..3aca5b9610 100644 --- a/test/cases/safety/array slice sentinel mismatch.zig +++ b/test/cases/safety/array slice sentinel mismatch.zig @@ -15,4 +15,4 @@ pub fn main() !void { } // run // backend=stage1 - +// target=native diff --git a/test/cases/safety/awaiting twice.zig b/test/cases/safety/awaiting twice.zig index 2a64320c07..867263de6e 100644 --- a/test/cases/safety/awaiting twice.zig +++ b/test/cases/safety/awaiting twice.zig @@ -26,3 +26,4 @@ fn func() void { } // run // backend=stage1 +// target=native diff --git a/test/cases/safety/bad union field access.zig b/test/cases/safety/bad union field access.zig index cc476f57c5..7adf48abf2 100644 --- a/test/cases/safety/bad union field access.zig +++ b/test/cases/safety/bad union field access.zig @@ -21,4 +21,5 @@ fn bar(f: *Foo) void { f.float = 12.34; } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/calling panic.zig b/test/cases/safety/calling panic.zig index e6ff577e7f..5befea3e3c 100644 --- a/test/cases/safety/calling panic.zig +++ b/test/cases/safety/calling panic.zig @@ -12,4 +12,5 @@ pub fn main() !void { return error.TestFailed; } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/cast []u8 to bigger slice of wrong size.zig b/test/cases/safety/cast []u8 to bigger slice of wrong size.zig index 5bdee09e52..588801b27e 100644 --- a/test/cases/safety/cast []u8 to bigger slice of wrong size.zig +++ b/test/cases/safety/cast []u8 to bigger slice of wrong size.zig @@ -15,4 +15,5 @@ fn widenSlice(slice: []align(1) const u8) []align(1) const i32 { return std.mem.bytesAsSlice(i32, slice); } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/cast integer to global error and no code matches.zig b/test/cases/safety/cast integer to global error and no code matches.zig index 57b72aab5d..3a8dc2374f 100644 --- a/test/cases/safety/cast integer to global error and no code matches.zig +++ b/test/cases/safety/cast integer to global error and no code matches.zig @@ -13,4 +13,5 @@ fn bar(x: u16) anyerror { return @intToError(x); } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/empty slice with sentinel out of bounds.zig b/test/cases/safety/empty slice with sentinel out of bounds.zig index 2ea22ed41d..ad8010868a 100644 --- a/test/cases/safety/empty slice with sentinel out of bounds.zig +++ b/test/cases/safety/empty slice with sentinel out of bounds.zig @@ -18,3 +18,4 @@ pub fn main() !void { // run // backend=stage1 +// target=native diff --git a/test/cases/safety/error return trace across suspend points.zig b/test/cases/safety/error return trace across suspend points.zig index b27bc770fb..b8cb90505a 100644 --- a/test/cases/safety/error return trace across suspend points.zig +++ b/test/cases/safety/error return trace across suspend points.zig @@ -35,4 +35,5 @@ fn printTrace(p: anyframe->anyerror!void) void { (await p) catch unreachable; } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/exact division failure - vectors.zig b/test/cases/safety/exact division failure - vectors.zig index 77dd427683..a514213f58 100644 --- a/test/cases/safety/exact division failure - vectors.zig +++ b/test/cases/safety/exact division failure - vectors.zig @@ -17,4 +17,5 @@ fn divExact(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) { return @divExact(a, b); } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/exact division failure.zig b/test/cases/safety/exact division failure.zig index c363df94ab..5e30f14b06 100644 --- a/test/cases/safety/exact division failure.zig +++ b/test/cases/safety/exact division failure.zig @@ -15,4 +15,5 @@ fn divExact(a: i32, b: i32) i32 { return @divExact(a, b); } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/intToPtr with misaligned address.zig b/test/cases/safety/intToPtr with misaligned address.zig index eaca2cb32d..5b480eccca 100644 --- a/test/cases/safety/intToPtr with misaligned address.zig +++ b/test/cases/safety/intToPtr with misaligned address.zig @@ -15,3 +15,4 @@ pub fn main() !void { } // run // backend=stage1 +// target=native diff --git a/test/cases/safety/integer addition overflow.zig b/test/cases/safety/integer addition overflow.zig index 74fce53890..cd23b66f36 100644 --- a/test/cases/safety/integer addition overflow.zig +++ b/test/cases/safety/integer addition overflow.zig @@ -20,3 +20,4 @@ fn add(a: u16, b: u16) u16 { // run // backend=stage1 +// target=native diff --git a/test/cases/safety/integer division by zero - vectors.zig b/test/cases/safety/integer division by zero - vectors.zig index b3365bfcae..136f179935 100644 --- a/test/cases/safety/integer division by zero - vectors.zig +++ b/test/cases/safety/integer division by zero - vectors.zig @@ -16,4 +16,5 @@ fn div0(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) { return @divTrunc(a, b); } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/integer division by zero.zig b/test/cases/safety/integer division by zero.zig index b209a69d35..8d80a7c848 100644 --- a/test/cases/safety/integer division by zero.zig +++ b/test/cases/safety/integer division by zero.zig @@ -14,4 +14,5 @@ fn div0(a: i32, b: i32) i32 { return @divTrunc(a, b); } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/integer multiplication overflow.zig b/test/cases/safety/integer multiplication overflow.zig index 0eb68fabe0..c9894217a7 100644 --- a/test/cases/safety/integer multiplication overflow.zig +++ b/test/cases/safety/integer multiplication overflow.zig @@ -15,4 +15,5 @@ fn mul(a: u16, b: u16) u16 { return a * b; } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/integer negation overflow.zig b/test/cases/safety/integer negation overflow.zig index c9d7e4f919..f3eb1feffe 100644 --- a/test/cases/safety/integer negation overflow.zig +++ b/test/cases/safety/integer negation overflow.zig @@ -15,4 +15,5 @@ fn neg(a: i16) i16 { return -a; } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/integer subtraction overflow.zig b/test/cases/safety/integer subtraction overflow.zig index f1b17020a8..ce1526cf60 100644 --- a/test/cases/safety/integer subtraction overflow.zig +++ b/test/cases/safety/integer subtraction overflow.zig @@ -15,4 +15,5 @@ fn sub(a: u16, b: u16) u16 { return a - b; } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/invalid resume of async function.zig b/test/cases/safety/invalid resume of async function.zig index acde5eed92..2ed5704c74 100644 --- a/test/cases/safety/invalid resume of async function.zig +++ b/test/cases/safety/invalid resume of async function.zig @@ -16,3 +16,4 @@ fn suspendOnce() void { } // run // backend=stage1 +// target=native diff --git a/test/cases/safety/nosuspend function call, callee suspends.zig b/test/cases/safety/nosuspend function call, callee suspends.zig index fc99174bd0..42daa4a9bd 100644 --- a/test/cases/safety/nosuspend function call, callee suspends.zig +++ b/test/cases/safety/nosuspend function call, callee suspends.zig @@ -17,3 +17,4 @@ fn add(a: i32, b: i32) i32 { } // run // backend=stage1 +// target=native diff --git a/test/cases/safety/optional unwrap operator on C pointer.zig b/test/cases/safety/optional unwrap operator on C pointer.zig index 4d5fa54d3d..05614b4ca2 100644 --- a/test/cases/safety/optional unwrap operator on C pointer.zig +++ b/test/cases/safety/optional unwrap operator on C pointer.zig @@ -13,3 +13,4 @@ pub fn main() !void { } // run // backend=stage1 +// target=native diff --git a/test/cases/safety/optional unwrap operator on null pointer.zig b/test/cases/safety/optional unwrap operator on null pointer.zig index eb791be268..1db44ba22a 100644 --- a/test/cases/safety/optional unwrap operator on null pointer.zig +++ b/test/cases/safety/optional unwrap operator on null pointer.zig @@ -13,3 +13,4 @@ pub fn main() !void { } // run // backend=stage1 +// target=native diff --git a/test/cases/safety/out of bounds slice access.zig b/test/cases/safety/out of bounds slice access.zig index e429328b1e..8c95978d4a 100644 --- a/test/cases/safety/out of bounds slice access.zig +++ b/test/cases/safety/out of bounds slice access.zig @@ -15,4 +15,5 @@ fn bar(a: []const i32) i32 { } fn baz(_: i32) void { } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/pointer casting null to non-optional pointer.zig b/test/cases/safety/pointer casting null to non-optional pointer.zig index a61ba2fa9b..0254e002ad 100644 --- a/test/cases/safety/pointer casting null to non-optional pointer.zig +++ b/test/cases/safety/pointer casting null to non-optional pointer.zig @@ -13,3 +13,4 @@ pub fn main() !void { } // run // backend=stage1 +// target=native diff --git a/test/cases/safety/pointer slice sentinel mismatch.zig b/test/cases/safety/pointer slice sentinel mismatch.zig index f796534783..f79e2a860c 100644 --- a/test/cases/safety/pointer slice sentinel mismatch.zig +++ b/test/cases/safety/pointer slice sentinel mismatch.zig @@ -18,3 +18,4 @@ pub fn main() !void { // run // backend=stage1 +// target=native diff --git a/test/cases/safety/resuming a function which is awaiting a call.zig b/test/cases/safety/resuming a function which is awaiting a call.zig index 63c4e1ef50..b344441507 100644 --- a/test/cases/safety/resuming a function which is awaiting a call.zig +++ b/test/cases/safety/resuming a function which is awaiting a call.zig @@ -18,3 +18,4 @@ fn other() void { } // run // backend=stage1 +// target=native diff --git a/test/cases/safety/resuming a function which is awaiting a frame.zig b/test/cases/safety/resuming a function which is awaiting a frame.zig index 0f68be4db8..e63b49183e 100644 --- a/test/cases/safety/resuming a function which is awaiting a frame.zig +++ b/test/cases/safety/resuming a function which is awaiting a frame.zig @@ -19,3 +19,4 @@ fn other() void { } // run // backend=stage1 +// target=native diff --git a/test/cases/safety/resuming a non-suspended function which has been suspended and resumed.zig b/test/cases/safety/resuming a non-suspended function which has been suspended and resumed.zig index 3f3a62fe4e..54de0b9ebd 100644 --- a/test/cases/safety/resuming a non-suspended function which has been suspended and resumed.zig +++ b/test/cases/safety/resuming a non-suspended function which has been suspended and resumed.zig @@ -29,3 +29,4 @@ pub fn main() !void { } // run // backend=stage1 +// target=native diff --git a/test/cases/safety/resuming a non-suspended function which never been suspended.zig b/test/cases/safety/resuming a non-suspended function which never been suspended.zig index b0dfbc1911..8c35753d6d 100644 --- a/test/cases/safety/resuming a non-suspended function which never been suspended.zig +++ b/test/cases/safety/resuming a non-suspended function which never been suspended.zig @@ -24,3 +24,4 @@ pub fn main() !void { } // run // backend=stage1 +// target=native diff --git a/test/cases/safety/shift left by huge amount.zig b/test/cases/safety/shift left by huge amount.zig index 8cf5ec3752..b1159b7d75 100644 --- a/test/cases/safety/shift left by huge amount.zig +++ b/test/cases/safety/shift left by huge amount.zig @@ -18,3 +18,4 @@ pub fn main() !void { // run // backend=stage1 +// target=native diff --git a/test/cases/safety/shift right by huge amount.zig b/test/cases/safety/shift right by huge amount.zig index 90a620ddc4..2c39011240 100644 --- a/test/cases/safety/shift right by huge amount.zig +++ b/test/cases/safety/shift right by huge amount.zig @@ -18,3 +18,4 @@ pub fn main() !void { // run // backend=stage1 +// target=native diff --git a/test/cases/safety/signed integer division overflow - vectors.zig b/test/cases/safety/signed integer division overflow - vectors.zig index 5ce8fd740e..d59adeb698 100644 --- a/test/cases/safety/signed integer division overflow - vectors.zig +++ b/test/cases/safety/signed integer division overflow - vectors.zig @@ -17,4 +17,5 @@ fn div(a: @Vector(4, i16), b: @Vector(4, i16)) @Vector(4, i16) { return @divTrunc(a, b); } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/signed integer division overflow.zig b/test/cases/safety/signed integer division overflow.zig index 64e1827b45..a46f175487 100644 --- a/test/cases/safety/signed integer division overflow.zig +++ b/test/cases/safety/signed integer division overflow.zig @@ -15,4 +15,5 @@ fn div(a: i16, b: i16) i16 { return @divTrunc(a, b); } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/signed integer not fitting in cast to unsigned integer - widening.zig b/test/cases/safety/signed integer not fitting in cast to unsigned integer - widening.zig index 7c08fcddb2..7abd085364 100644 --- a/test/cases/safety/signed integer not fitting in cast to unsigned integer - widening.zig +++ b/test/cases/safety/signed integer not fitting in cast to unsigned integer - widening.zig @@ -12,4 +12,5 @@ pub fn main() !void { return error.TestFailed; } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/signed integer not fitting in cast to unsigned integer.zig b/test/cases/safety/signed integer not fitting in cast to unsigned integer.zig index 6ed4246403..4dea06fc82 100644 --- a/test/cases/safety/signed integer not fitting in cast to unsigned integer.zig +++ b/test/cases/safety/signed integer not fitting in cast to unsigned integer.zig @@ -15,4 +15,5 @@ fn unsigned_cast(x: i32) u32 { return @intCast(u32, x); } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/signed shift left overflow.zig b/test/cases/safety/signed shift left overflow.zig index 814d52b1a9..88adbe5835 100644 --- a/test/cases/safety/signed shift left overflow.zig +++ b/test/cases/safety/signed shift left overflow.zig @@ -15,4 +15,5 @@ fn shl(a: i16, b: u4) i16 { return @shlExact(a, b); } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/signed shift right overflow.zig b/test/cases/safety/signed shift right overflow.zig index fc2e00cee3..9d5545ed3a 100644 --- a/test/cases/safety/signed shift right overflow.zig +++ b/test/cases/safety/signed shift right overflow.zig @@ -15,4 +15,5 @@ fn shr(a: i16, b: u4) i16 { return @shrExact(a, b); } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/signed-unsigned vector cast.zig b/test/cases/safety/signed-unsigned vector cast.zig index 4de78e969d..15d120350e 100644 --- a/test/cases/safety/signed-unsigned vector cast.zig +++ b/test/cases/safety/signed-unsigned vector cast.zig @@ -17,3 +17,4 @@ pub fn main() !void { // run // backend=stage1 +// target=native diff --git a/test/cases/safety/slice sentinel mismatch - floats.zig b/test/cases/safety/slice sentinel mismatch - floats.zig index df5edc1fdf..3295c20db3 100644 --- a/test/cases/safety/slice sentinel mismatch - floats.zig +++ b/test/cases/safety/slice sentinel mismatch - floats.zig @@ -17,3 +17,4 @@ pub fn main() !void { // run // backend=stage1 +// target=native diff --git a/test/cases/safety/slice sentinel mismatch - optional pointers.zig b/test/cases/safety/slice sentinel mismatch - optional pointers.zig index 8199f3280e..ecb82c61d4 100644 --- a/test/cases/safety/slice sentinel mismatch - optional pointers.zig +++ b/test/cases/safety/slice sentinel mismatch - optional pointers.zig @@ -17,3 +17,4 @@ pub fn main() !void { // run // backend=stage1 +// target=native diff --git a/test/cases/safety/slice slice sentinel mismatch.zig b/test/cases/safety/slice slice sentinel mismatch.zig index 8303b1a288..13b331a0f4 100644 --- a/test/cases/safety/slice slice sentinel mismatch.zig +++ b/test/cases/safety/slice slice sentinel mismatch.zig @@ -16,3 +16,4 @@ pub fn main() !void { } // run // backend=stage1 +// target=native diff --git a/test/cases/safety/slice with sentinel out of bounds.zig b/test/cases/safety/slice with sentinel out of bounds.zig index 7fc890e144..1ca83ea481 100644 --- a/test/cases/safety/slice with sentinel out of bounds.zig +++ b/test/cases/safety/slice with sentinel out of bounds.zig @@ -18,3 +18,4 @@ pub fn main() !void { // run // backend=stage1 +// target=native diff --git a/test/cases/safety/slicing null C pointer.zig b/test/cases/safety/slicing null C pointer.zig index 2b678ad9cf..db8d235c45 100644 --- a/test/cases/safety/slicing null C pointer.zig +++ b/test/cases/safety/slicing null C pointer.zig @@ -13,4 +13,5 @@ pub fn main() !void { return error.TestFailed; } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/switch on corrupted enum value.zig b/test/cases/safety/switch on corrupted enum value.zig index b574444341..dc7b9b3abf 100644 --- a/test/cases/safety/switch on corrupted enum value.zig +++ b/test/cases/safety/switch on corrupted enum value.zig @@ -23,3 +23,4 @@ pub fn main() !void { // run // backend=stage1 +// target=native diff --git a/test/cases/safety/switch on corrupted union value.zig b/test/cases/safety/switch on corrupted union value.zig index 8ec4fece1b..0fadad3c7e 100644 --- a/test/cases/safety/switch on corrupted union value.zig +++ b/test/cases/safety/switch on corrupted union value.zig @@ -23,3 +23,4 @@ pub fn main() !void { // run // backend=stage1 +// target=native diff --git a/test/cases/safety/truncating vector cast.zig b/test/cases/safety/truncating vector cast.zig index f2795d9c66..b0a3c4e25b 100644 --- a/test/cases/safety/truncating vector cast.zig +++ b/test/cases/safety/truncating vector cast.zig @@ -17,3 +17,4 @@ pub fn main() !void { // run // backend=stage1 +// target=native diff --git a/test/cases/safety/unsigned integer not fitting in cast to signed integer - same bit count.zig b/test/cases/safety/unsigned integer not fitting in cast to signed integer - same bit count.zig index 6118d5a5ea..9bf36b07c9 100644 --- a/test/cases/safety/unsigned integer not fitting in cast to signed integer - same bit count.zig +++ b/test/cases/safety/unsigned integer not fitting in cast to signed integer - same bit count.zig @@ -12,4 +12,5 @@ pub fn main() !void { return error.TestFailed; } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/unsigned shift left overflow.zig b/test/cases/safety/unsigned shift left overflow.zig index 3fa2658c3f..73c4292cdf 100644 --- a/test/cases/safety/unsigned shift left overflow.zig +++ b/test/cases/safety/unsigned shift left overflow.zig @@ -15,4 +15,5 @@ fn shl(a: u16, b: u4) u16 { return @shlExact(a, b); } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/unsigned shift right overflow.zig b/test/cases/safety/unsigned shift right overflow.zig index 0953229a67..6a3829f675 100644 --- a/test/cases/safety/unsigned shift right overflow.zig +++ b/test/cases/safety/unsigned shift right overflow.zig @@ -15,4 +15,5 @@ fn shr(a: u16, b: u4) u16 { return @shrExact(a, b); } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/unsigned-signed vector cast.zig b/test/cases/safety/unsigned-signed vector cast.zig index 9c157d8f40..c78ec2c73f 100644 --- a/test/cases/safety/unsigned-signed vector cast.zig +++ b/test/cases/safety/unsigned-signed vector cast.zig @@ -17,3 +17,4 @@ pub fn main() !void { // run // backend=stage1 +// target=native diff --git a/test/cases/safety/unwrap error.zig b/test/cases/safety/unwrap error.zig index 451b9b3891..ee9a502ebd 100644 --- a/test/cases/safety/unwrap error.zig +++ b/test/cases/safety/unwrap error.zig @@ -15,4 +15,5 @@ fn bar() !void { return error.Whatever; } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/value does not fit in shortening cast - u0.zig b/test/cases/safety/value does not fit in shortening cast - u0.zig index 3bbcaf972f..072be45731 100644 --- a/test/cases/safety/value does not fit in shortening cast - u0.zig +++ b/test/cases/safety/value does not fit in shortening cast - u0.zig @@ -15,4 +15,5 @@ fn shorten_cast(x: u8) u0 { return @intCast(u0, x); } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/value does not fit in shortening cast.zig b/test/cases/safety/value does not fit in shortening cast.zig index 2c7409d225..7188c9a846 100644 --- a/test/cases/safety/value does not fit in shortening cast.zig +++ b/test/cases/safety/value does not fit in shortening cast.zig @@ -15,4 +15,5 @@ fn shorten_cast(x: i32) i8 { return @intCast(i8, x); } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/vector integer addition overflow.zig b/test/cases/safety/vector integer addition overflow.zig index 32b045cd56..5eedc869e7 100644 --- a/test/cases/safety/vector integer addition overflow.zig +++ b/test/cases/safety/vector integer addition overflow.zig @@ -16,4 +16,5 @@ fn add(a: @Vector(4, i32), b: @Vector(4, i32)) @Vector(4, i32) { return a + b; } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/vector integer multiplication overflow.zig b/test/cases/safety/vector integer multiplication overflow.zig index 2c9d48e1fd..5d247cf545 100644 --- a/test/cases/safety/vector integer multiplication overflow.zig +++ b/test/cases/safety/vector integer multiplication overflow.zig @@ -16,4 +16,5 @@ fn mul(a: @Vector(4, u8), b: @Vector(4, u8)) @Vector(4, u8) { return a * b; } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/vector integer negation overflow.zig b/test/cases/safety/vector integer negation overflow.zig index 81506da41c..03d846aab0 100644 --- a/test/cases/safety/vector integer negation overflow.zig +++ b/test/cases/safety/vector integer negation overflow.zig @@ -15,4 +15,5 @@ fn neg(a: @Vector(4, i16)) @Vector(4, i16) { return -a; } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file diff --git a/test/cases/safety/vector integer subtraction overflow.zig b/test/cases/safety/vector integer subtraction overflow.zig index c3a21e971c..72287ffd07 100644 --- a/test/cases/safety/vector integer subtraction overflow.zig +++ b/test/cases/safety/vector integer subtraction overflow.zig @@ -16,4 +16,5 @@ fn sub(a: @Vector(4, u32), b: @Vector(4, u32)) @Vector(4, u32) { return a - b; } // run -// backend=stage1 \ No newline at end of file +// backend=stage1 +// target=native \ No newline at end of file From 1bdcbd18ae1f312748c9909db98532a8dfd006eb Mon Sep 17 00:00:00 2001 From: Robin Date: Sat, 14 May 2022 12:56:58 +0200 Subject: [PATCH 1491/2031] init-exe: add note about log_level in ReleaseSmall and ReleaseFast build mode (#11626) As suggested in https://github.com/ziglang/zig/issues/9945#issuecomment-950114977 by @wizzard0. Fixes #9945. --- lib/init-exe/src/main.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/init-exe/src/main.zig b/lib/init-exe/src/main.zig index a7a7c9546a..c2f93f4771 100644 --- a/lib/init-exe/src/main.zig +++ b/lib/init-exe/src/main.zig @@ -1,6 +1,8 @@ const std = @import("std"); pub fn main() anyerror!void { + // Note that info level log messages are by default printed only in Debug + // and ReleaseSafe build modes. std.log.info("All your codebase are belong to us.", .{}); } From 5138856a72fc009ec799cb935d9117e3bd72f16a Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 14 May 2022 21:39:09 +0200 Subject: [PATCH 1492/2031] test harness: Set filename on error return While calling `next` an error can occur while parsing the file. However, we don't set the filename that is currently being processed, until `next` completed successfully. This means that for invalid test names, the wrong filename was being displayed in the panic message. The fix is to retrieve the correct filename when an error occurs and then setting the filename appropriately. --- src/test.zig | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/test.zig b/src/test.zig index 585c6d62e3..a6537e77c7 100644 --- a/src/test.zig +++ b/src/test.zig @@ -398,6 +398,8 @@ const TestIterator = struct { start: usize = 0, end: usize = 0, filenames: []const []const u8, + /// reset on each call to `next` + index: usize = 0, const Error = error{InvalidIncrementalTestIndex}; @@ -416,12 +418,12 @@ const TestIterator = struct { } const remaining = it.filenames[it.end..]; - var i: usize = 0; - while (i < remaining.len - 1) : (i += 1) { + it.index = 0; + while (it.index < remaining.len - 1) : (it.index += 1) { // First, check if this file is part of an incremental update sequence // Split filename into ".." - const prev_parts = getTestFileNameParts(remaining[i]); - const new_parts = getTestFileNameParts(remaining[i + 1]); + const prev_parts = getTestFileNameParts(remaining[it.index]); + const new_parts = getTestFileNameParts(remaining[it.index + 1]); // If base_name and file_ext match, these files are in the same test sequence // and the new one should be the incremented version of the previous test @@ -441,13 +443,22 @@ const TestIterator = struct { if (new_parts.test_index != null and new_parts.test_index.? != 0) return error.InvalidIncrementalTestIndex; - it.end += i + 1; + it.end += it.index + 1; break; } } else { it.end += remaining.len; } } + + /// In the event of an `error.InvalidIncrementalTestIndex`, this function can + /// be used to find the current filename that was being processed. + /// Asserts the iterator hasn't reached the end. + fn currentFilename(it: TestIterator) []const u8 { + assert(it.end != it.filenames.len); + const remaining = it.filenames[it.end..]; + return remaining[it.index + 1]; + } }; /// For a filename in the format ".X." or ".", returns @@ -1051,7 +1062,8 @@ pub const TestContext = struct { sortTestFilenames(filenames.items); var test_it = TestIterator{ .filenames = filenames.items }; - while (try test_it.next()) |batch| { + while (test_it.next()) |maybe_batch| { + const batch = maybe_batch orelse break; const strategy: TestStrategy = if (batch.len > 1) .incremental else .independent; var cases = std.ArrayList(usize).init(ctx.arena); @@ -1133,6 +1145,10 @@ pub const TestContext = struct { } } } + } else |err| { + // make sure the current file is set to the file that produced an error + current_file.* = test_it.currentFilename(); + return err; } } From 5f97652da8881773b823a0730e5791816668528a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 15 May 2022 09:44:05 +0200 Subject: [PATCH 1493/2031] x64: remove verbose_mir functionality Originally I thought interleaving AIR with MIR will be useful, however as it stands, I have used it very sporadically, and recently, not at all, and I do not think anyone else is actually using it. If there is a simple error such as a wrong instruction emitted, `objdump` is perfectly capable of narrowing it down, while if there's something more subtle happening, regardless of having `--verbose-mir` functionality or not, you still gotta go via the debugger which offers a better view at interleaved source program with the emitted machine code. Finally, I believe `-femit-asm` when we add it will offer a more generic substitute. --- src/Compilation.zig | 3 - src/arch/x86_64/CodeGen.zig | 12 - src/arch/x86_64/PrintMir.zig | 478 ----------------------------------- src/main.zig | 4 - 4 files changed, 497 deletions(-) delete mode 100644 src/arch/x86_64/PrintMir.zig diff --git a/src/Compilation.zig b/src/Compilation.zig index 2091502da2..407c753193 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -82,7 +82,6 @@ clang_preprocessor_mode: ClangPreprocessorMode, /// Whether to print clang argvs to stdout. verbose_cc: bool, verbose_air: bool, -verbose_mir: bool, verbose_llvm_ir: bool, verbose_cimport: bool, verbose_llvm_cpu_features: bool, @@ -775,7 +774,6 @@ pub const InitOptions = struct { verbose_cc: bool = false, verbose_link: bool = false, verbose_air: bool = false, - verbose_mir: bool = false, verbose_llvm_ir: bool = false, verbose_cimport: bool = false, verbose_llvm_cpu_features: bool = false, @@ -1683,7 +1681,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .clang_preprocessor_mode = options.clang_preprocessor_mode, .verbose_cc = options.verbose_cc, .verbose_air = options.verbose_air, - .verbose_mir = options.verbose_mir, .verbose_llvm_ir = options.verbose_llvm_ir, .verbose_cimport = options.verbose_cimport, .verbose_llvm_cpu_features = options.verbose_llvm_cpu_features, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index be053f310c..80a9bbbd78 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -348,18 +348,6 @@ pub fn generate( else => |e| return e, }; - if (builtin.mode == .Debug and bin_file.options.module.?.comp.verbose_mir) { - const w = std.io.getStdErr().writer(); - w.print("# Begin Function MIR: {s}:\n", .{fn_owner_decl.name}) catch {}; - const PrintMir = @import("PrintMir.zig"); - const print = PrintMir{ - .mir = mir, - .bin_file = bin_file, - }; - print.printMir(w, function.mir_to_air_map, air) catch {}; // we don't care if the debug printing fails - w.print("# End Function MIR: {s}\n\n", .{fn_owner_decl.name}) catch {}; - } - if (function.err_msg) |em| { return FnResult{ .fail = em }; } else { diff --git a/src/arch/x86_64/PrintMir.zig b/src/arch/x86_64/PrintMir.zig deleted file mode 100644 index e457d859ea..0000000000 --- a/src/arch/x86_64/PrintMir.zig +++ /dev/null @@ -1,478 +0,0 @@ -//! This file contains the functionality for print x86_64 MIR in a debug way, interleaved with AIR - -const Print = @This(); - -const std = @import("std"); -const assert = std.debug.assert; -const bits = @import("bits.zig"); -const abi = @import("abi.zig"); -const leb128 = std.leb; -const link = @import("../../link.zig"); -const log = std.log.scoped(.codegen); -const math = std.math; -const mem = std.mem; - -const Air = @import("../../Air.zig"); -const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; -const DW = std.dwarf; -const Encoder = bits.Encoder; -const ErrorMsg = Module.ErrorMsg; -const MCValue = @import("CodeGen.zig").MCValue; -const Mir = @import("Mir.zig"); -const Module = @import("../../Module.zig"); -const Instruction = bits.Instruction; -const Register = bits.Register; -const Type = @import("../../type.zig").Type; -const fmtIntSizeBin = std.fmt.fmtIntSizeBin; - -mir: Mir, -bin_file: *link.File, - -pub fn printMir(print: *const Print, w: anytype, mir_to_air_map: std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index), air: Air) !void { - const instruction_bytes = print.mir.instructions.len * - // Here we don't use @sizeOf(Mir.Inst.Data) because it would include - // the debug safety tag but we want to measure release size. - (@sizeOf(Mir.Inst.Tag) + 2 + 8); - const extra_bytes = print.mir.extra.len * @sizeOf(u32); - const total_bytes = @sizeOf(Mir) + instruction_bytes + extra_bytes; - - // zig fmt: off - std.debug.print( - \\# Total MIR bytes: {} - \\# MIR Instructions: {d} ({}) - \\# MIR Extra Data: {d} ({}) - \\ - , .{ - fmtIntSizeBin(total_bytes), - print.mir.instructions.len, fmtIntSizeBin(instruction_bytes), - print.mir.extra.len, fmtIntSizeBin(extra_bytes), - }); - // zig fmt: on - const mir_tags = print.mir.instructions.items(.tag); - - for (mir_tags) |tag, index| { - const inst = @intCast(u32, index); - if (mir_to_air_map.get(inst)) |air_index| { - try w.print("air index %{} ({}) for following mir inst(s)\n", .{ air_index, air.instructions.items(.tag)[air_index] }); - } - try w.writeAll(" "); - switch (tag) { - .adc => try print.mirArith(.adc, inst, w), - .add => try print.mirArith(.add, inst, w), - .sub => try print.mirArith(.sub, inst, w), - .xor => try print.mirArith(.xor, inst, w), - .@"and" => try print.mirArith(.@"and", inst, w), - .@"or" => try print.mirArith(.@"or", inst, w), - .sbb => try print.mirArith(.sbb, inst, w), - .cmp => try print.mirArith(.cmp, inst, w), - .mov => try print.mirArith(.mov, inst, w), - - .adc_mem_imm => try print.mirArithMemImm(.adc, inst, w), - .add_mem_imm => try print.mirArithMemImm(.add, inst, w), - .sub_mem_imm => try print.mirArithMemImm(.sub, inst, w), - .xor_mem_imm => try print.mirArithMemImm(.xor, inst, w), - .and_mem_imm => try print.mirArithMemImm(.@"and", inst, w), - .or_mem_imm => try print.mirArithMemImm(.@"or", inst, w), - .sbb_mem_imm => try print.mirArithMemImm(.sbb, inst, w), - .cmp_mem_imm => try print.mirArithMemImm(.cmp, inst, w), - .mov_mem_imm => try print.mirArithMemImm(.mov, inst, w), - - .adc_scale_src => try print.mirArithScaleSrc(.adc, inst, w), - .add_scale_src => try print.mirArithScaleSrc(.add, inst, w), - .sub_scale_src => try print.mirArithScaleSrc(.sub, inst, w), - .xor_scale_src => try print.mirArithScaleSrc(.xor, inst, w), - .and_scale_src => try print.mirArithScaleSrc(.@"and", inst, w), - .or_scale_src => try print.mirArithScaleSrc(.@"or", inst, w), - .sbb_scale_src => try print.mirArithScaleSrc(.sbb, inst, w), - .cmp_scale_src => try print.mirArithScaleSrc(.cmp, inst, w), - .mov_scale_src => try print.mirArithScaleSrc(.mov, inst, w), - - .adc_scale_dst => try print.mirArithScaleDst(.adc, inst, w), - .add_scale_dst => try print.mirArithScaleDst(.add, inst, w), - .sub_scale_dst => try print.mirArithScaleDst(.sub, inst, w), - .xor_scale_dst => try print.mirArithScaleDst(.xor, inst, w), - .and_scale_dst => try print.mirArithScaleDst(.@"and", inst, w), - .or_scale_dst => try print.mirArithScaleDst(.@"or", inst, w), - .sbb_scale_dst => try print.mirArithScaleDst(.sbb, inst, w), - .cmp_scale_dst => try print.mirArithScaleDst(.cmp, inst, w), - .mov_scale_dst => try print.mirArithScaleDst(.mov, inst, w), - - .adc_scale_imm => try print.mirArithScaleImm(.adc, inst, w), - .add_scale_imm => try print.mirArithScaleImm(.add, inst, w), - .sub_scale_imm => try print.mirArithScaleImm(.sub, inst, w), - .xor_scale_imm => try print.mirArithScaleImm(.xor, inst, w), - .and_scale_imm => try print.mirArithScaleImm(.@"and", inst, w), - .or_scale_imm => try print.mirArithScaleImm(.@"or", inst, w), - .sbb_scale_imm => try print.mirArithScaleImm(.sbb, inst, w), - .cmp_scale_imm => try print.mirArithScaleImm(.cmp, inst, w), - .mov_scale_imm => try print.mirArithScaleImm(.mov, inst, w), - - .adc_mem_index_imm => try print.mirArithMemIndexImm(.adc, inst, w), - .add_mem_index_imm => try print.mirArithMemIndexImm(.add, inst, w), - .sub_mem_index_imm => try print.mirArithMemIndexImm(.sub, inst, w), - .xor_mem_index_imm => try print.mirArithMemIndexImm(.xor, inst, w), - .and_mem_index_imm => try print.mirArithMemIndexImm(.@"and", inst, w), - .or_mem_index_imm => try print.mirArithMemIndexImm(.@"or", inst, w), - .sbb_mem_index_imm => try print.mirArithMemIndexImm(.sbb, inst, w), - .cmp_mem_index_imm => try print.mirArithMemIndexImm(.cmp, inst, w), - .mov_mem_index_imm => try print.mirArithMemIndexImm(.mov, inst, w), - - .movabs => try print.mirMovabs(inst, w), - - .lea => try print.mirLea(inst, w), - .lea_pie => try print.mirLeaPie(inst, w), - - .imul_complex => try print.mirIMulComplex(inst, w), - - .push => try print.mirPushPop(.push, inst, w), - .pop => try print.mirPushPop(.pop, inst, w), - - .jmp => try print.mirJmpCall(.jmp, inst, w), - .call => try print.mirJmpCall(.call, inst, w), - - .cond_jmp_greater_less => try print.mirCondJmp(.cond_jmp_greater_less, inst, w), - .cond_jmp_above_below => try print.mirCondJmp(.cond_jmp_above_below, inst, w), - .cond_jmp_eq_ne => try print.mirCondJmp(.cond_jmp_eq_ne, inst, w), - - .cond_set_byte_greater_less => try print.mirCondSetByte(.cond_set_byte_greater_less, inst, w), - .cond_set_byte_above_below => try print.mirCondSetByte(.cond_set_byte_above_below, inst, w), - .cond_set_byte_eq_ne => try print.mirCondSetByte(.cond_set_byte_eq_ne, inst, w), - - .@"test" => try print.mirTest(inst, w), - - .brk => try w.writeAll("brk\n"), - .ret => try w.writeAll("ret\n"), - .nop => try w.writeAll("nop\n"), - .syscall => try w.writeAll("syscall\n"), - - .call_extern => try print.mirCallExtern(inst, w), - - .dbg_line, .dbg_prologue_end, .dbg_epilogue_begin => try w.print("{s}\n", .{@tagName(tag)}), - - .push_regs_from_callee_preserved_regs => try print.mirPushPopRegsFromCalleePreservedRegs(.push, inst, w), - .pop_regs_from_callee_preserved_regs => try print.mirPushPopRegsFromCalleePreservedRegs(.pop, inst, w), - - else => { - try w.print("TODO emit asm for {s}\n", .{@tagName(tag)}); - }, - } - } -} - -fn mirPushPop(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void { - const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); - switch (ops.flags) { - 0b00 => { - // PUSH/POP reg - try w.print("{s} {s}", .{ @tagName(tag), @tagName(ops.reg1) }); - }, - 0b01 => { - // PUSH/POP r/m64 - const imm = print.mir.instructions.items(.data)[inst].imm; - try w.print("{s} [{s} + {d}]", .{ @tagName(tag), @tagName(ops.reg1), imm }); - }, - 0b10 => { - const imm = print.mir.instructions.items(.data)[inst].imm; - // PUSH imm32 - assert(tag == .push); - try w.print("{s} {d}", .{ @tagName(tag), imm }); - }, - 0b11 => unreachable, - } - try w.writeByte('\n'); -} -fn mirPushPopRegsFromCalleePreservedRegs(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void { - const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); - const payload = print.mir.instructions.items(.data)[inst].payload; - const data = print.mir.extraData(Mir.RegsToPushOrPop, payload).data; - const regs = data.regs; - var disp: u32 = data.disp + 8; - if (regs == 0) return w.writeAll("no regs from callee_preserved_regs\n"); - var printed_first_reg = false; - for (abi.callee_preserved_regs) |reg, i| { - if ((regs >> @intCast(u5, i)) & 1 == 0) continue; - if (printed_first_reg) try w.writeAll(" "); - printed_first_reg = true; - if (tag == .push) { - try w.print("mov qword ptr [{s} + {d}], {s}", .{ - @tagName(ops.reg1), - @bitCast(u32, -@intCast(i32, disp)), - @tagName(reg.to64()), - }); - } else { - try w.print("mov {s}, qword ptr [{s} + {d}]", .{ - @tagName(reg.to64()), - @tagName(ops.reg1), - @bitCast(u32, -@intCast(i32, disp)), - }); - } - disp += 8; - try w.writeByte('\n'); - } -} - -fn mirJmpCall(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void { - try w.print("{s} ", .{@tagName(tag)}); - const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); - const flag = @truncate(u1, ops.flags); - if (flag == 0) { - return w.writeAll("TODO target\n"); - } - if (ops.reg1 == .none) { - // JMP/CALL [imm] - const imm = print.mir.instructions.items(.data)[inst].imm; - try w.print("[{x}]\n", .{imm}); - return; - } - // JMP/CALL reg - try w.print("{s}\n", .{@tagName(ops.reg1)}); -} - -fn mirCondJmp(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void { - _ = print; - _ = tag; - _ = inst; - try w.writeAll("TODO print mirCondJmp\n"); -} - -fn mirCondSetByte(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void { - _ = tag; - _ = inst; - _ = print; - try w.writeAll("TODO print mirCondSetByte\n"); -} - -fn mirTest(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void { - _ = print; - _ = inst; - try w.writeAll("TODO print mirTest\n"); -} - -fn mirArith(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void { - const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); - try w.writeAll(@tagName(tag)); - try w.writeByte(' '); - switch (ops.flags) { - 0b00 => { - if (ops.reg2 == .none) { - const imm = print.mir.instructions.items(.data)[inst].imm; - try w.print("{s}, {d}", .{ @tagName(ops.reg1), imm }); - } else try w.print("{s}, {s}", .{ @tagName(ops.reg1), @tagName(ops.reg2) }); - }, - 0b01 => { - const imm = print.mir.instructions.items(.data)[inst].imm; - if (ops.reg2 == .none) { - try w.print("{s}, ", .{@tagName(ops.reg1)}); - switch (ops.reg1.size()) { - 8 => try w.print("byte ptr ", .{}), - 16 => try w.print("word ptr ", .{}), - 32 => try w.print("dword ptr ", .{}), - 64 => try w.print("qword ptr ", .{}), - else => unreachable, - } - try w.print("[ds:{d}]", .{imm}); - } else { - try w.print("{s}, ", .{@tagName(ops.reg1)}); - switch (ops.reg1.size()) { - 8 => try w.print("byte ptr ", .{}), - 16 => try w.print("word ptr ", .{}), - 32 => try w.print("dword ptr ", .{}), - 64 => try w.print("qword ptr ", .{}), - else => unreachable, - } - try w.print("[{s} + {d}]", .{ @tagName(ops.reg2), imm }); - } - }, - 0b10 => { - const imm = print.mir.instructions.items(.data)[inst].imm; - if (ops.reg2 == .none) { - try w.writeAll("unused variant"); - } else { - switch (ops.reg2.size()) { - 8 => try w.print("byte ptr ", .{}), - 16 => try w.print("word ptr ", .{}), - 32 => try w.print("dword ptr ", .{}), - 64 => try w.print("qword ptr ", .{}), - else => unreachable, - } - try w.print("[{s} + {d}], {s}", .{ @tagName(ops.reg1), imm, @tagName(ops.reg2) }); - } - }, - 0b11 => { - try w.writeAll("unused variant"); - }, - } - try w.writeByte('\n'); -} - -fn mirArithMemImm(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void { - const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); - const payload = print.mir.instructions.items(.data)[inst].payload; - const imm_pair = print.mir.extraData(Mir.ImmPair, payload).data; - try w.print("{s} ", .{@tagName(tag)}); - switch (ops.flags) { - 0b00 => try w.print("byte ptr ", .{}), - 0b01 => try w.print("word ptr ", .{}), - 0b10 => try w.print("dword ptr ", .{}), - 0b11 => try w.print("qword ptr ", .{}), - } - try w.print("[{s} + {d}], {d}\n", .{ @tagName(ops.reg1), imm_pair.dest_off, imm_pair.operand }); -} - -fn mirArithScaleSrc(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void { - const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); - const scale = ops.flags; - // OP reg1, [reg2 + scale*rcx + imm32] - const imm = print.mir.instructions.items(.data)[inst].imm; - try w.print("{s} {s}, [{s} + {d}*rcx + {d}]\n", .{ @tagName(tag), @tagName(ops.reg1), @tagName(ops.reg2), scale, imm }); -} - -fn mirArithScaleDst(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void { - const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); - const scale = ops.flags; - const imm = print.mir.instructions.items(.data)[inst].imm; - - if (ops.reg2 == .none) { - // OP [reg1 + scale*rax + 0], imm32 - try w.print("{s} [{s} + {d}*rax + 0], {d}\n", .{ @tagName(tag), @tagName(ops.reg1), scale, imm }); - } - - // OP [reg1 + scale*rax + imm32], reg2 - try w.print("{s} [{s} + {d}*rax + {d}], {s}\n", .{ @tagName(tag), @tagName(ops.reg1), scale, imm, @tagName(ops.reg2) }); -} - -fn mirArithScaleImm(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void { - const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); - const scale = ops.flags; - const payload = print.mir.instructions.items(.data)[inst].payload; - const imm_pair = print.mir.extraData(Mir.ImmPair, payload).data; - try w.print("{s} [{s} + {d}*rax + {d}], {d}\n", .{ @tagName(tag), @tagName(ops.reg1), scale, imm_pair.dest_off, imm_pair.operand }); -} - -fn mirArithMemIndexImm(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void { - const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); - const payload = print.mir.instructions.items(.data)[inst].payload; - const imm_pair = print.mir.extraData(Mir.ImmPair, payload).data; - try w.print("{s} ", .{@tagName(tag)}); - switch (ops.flags) { - 0b00 => try w.print("byte ptr ", .{}), - 0b01 => try w.print("word ptr ", .{}), - 0b10 => try w.print("dword ptr ", .{}), - 0b11 => try w.print("qword ptr ", .{}), - } - try w.print("[{s} + 1*rax + {d}], {d}\n", .{ @tagName(ops.reg1), imm_pair.dest_off, imm_pair.operand }); -} - -fn mirMovabs(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void { - const tag = print.mir.instructions.items(.tag)[inst]; - assert(tag == .movabs); - const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); - - const is_64 = ops.reg1.size() == 64; - const imm: i128 = if (is_64) blk: { - const payload = print.mir.instructions.items(.data)[inst].payload; - const imm64 = print.mir.extraData(Mir.Imm64, payload).data; - break :blk imm64.decode(); - } else print.mir.instructions.items(.data)[inst].imm; - if (ops.flags == 0b00) { - // movabs reg, imm64 - try w.print("movabs {s}, {d}\n", .{ @tagName(ops.reg1), imm }); - } - if (ops.reg1 == .none) { - try w.writeAll("movabs moffs64, rax\n"); - } else { - // movabs rax, moffs64 - try w.writeAll("movabs rax, moffs64\n"); - } -} - -fn mirIMulComplex(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void { - const tag = print.mir.instructions.items(.tag)[inst]; - assert(tag == .imul_complex); - const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); - switch (ops.flags) { - 0b00 => { - try w.print("imul {s}, {s}\n", .{ @tagName(ops.reg1), @tagName(ops.reg2) }); - }, - 0b10 => { - const imm = print.mir.instructions.items(.data)[inst].imm; - try w.print("imul {s}, {s}, {d}\n", .{ @tagName(ops.reg1), @tagName(ops.reg2), imm }); - }, - else => return w.writeAll("TODO implement imul\n"), - } -} - -fn mirLea(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void { - const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); - try w.writeAll("lea "); - switch (ops.flags) { - 0b00 => { - const imm = print.mir.instructions.items(.data)[inst].imm; - try w.print("{s} [", .{@tagName(ops.reg1)}); - if (ops.reg2 != .none) { - try w.print("{s} + ", .{@tagName(ops.reg2)}); - } else { - try w.print("ds:", .{}); - } - try w.print("{d}]", .{imm}); - }, - 0b01 => { - try w.print("{s}, ", .{@tagName(ops.reg1)}); - switch (ops.reg1.size()) { - 8 => try w.print("byte ptr ", .{}), - 16 => try w.print("word ptr ", .{}), - 32 => try w.print("dword ptr ", .{}), - 64 => try w.print("qword ptr ", .{}), - else => unreachable, - } - try w.print("[rip + 0x0] ", .{}); - const payload = print.mir.instructions.items(.data)[inst].payload; - const imm = print.mir.extraData(Mir.Imm64, payload).data.decode(); - try w.print("target@{x}", .{imm}); - }, - 0b10 => { - const imm = print.mir.instructions.items(.data)[inst].imm; - try w.print("{s}, ", .{@tagName(ops.reg1)}); - switch (ops.reg1.size()) { - 8 => try w.print("byte ptr ", .{}), - 16 => try w.print("word ptr ", .{}), - 32 => try w.print("dword ptr ", .{}), - 64 => try w.print("qword ptr ", .{}), - else => unreachable, - } - try w.print("[rbp + rcx + {d}]", .{imm}); - }, - 0b11 => { - try w.writeAll("unused variant"); - }, - } - try w.writeAll("\n"); -} - -fn mirLeaPie(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void { - const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); - const load_reloc = print.mir.instructions.items(.data)[inst].load_reloc; - try w.print("lea {s}, ", .{@tagName(ops.reg1)}); - switch (ops.reg1.size()) { - 8 => try w.print("byte ptr ", .{}), - 16 => try w.print("word ptr ", .{}), - 32 => try w.print("dword ptr ", .{}), - 64 => try w.print("qword ptr ", .{}), - else => unreachable, - } - try w.print("[rip + 0x0] ", .{}); - if (print.bin_file.cast(link.File.MachO)) |macho_file| { - const target = macho_file.locals.items[load_reloc.sym_index]; - const target_name = macho_file.getString(target.n_strx); - try w.print("target@{s}", .{target_name}); - } else { - try w.print("TODO lea PIE for other backends", .{}); - } - return w.writeByte('\n'); -} - -fn mirCallExtern(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void { - _ = print; - _ = inst; - return w.writeAll("TODO call_extern"); -} diff --git a/src/main.zig b/src/main.zig index 246eacfe7a..bd923ed85f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -594,7 +594,6 @@ fn buildOutputType( var verbose_link = std.process.hasEnvVarConstant("ZIG_VERBOSE_LINK"); var verbose_cc = std.process.hasEnvVarConstant("ZIG_VERBOSE_CC"); var verbose_air = false; - var verbose_mir = false; var verbose_llvm_ir = false; var verbose_cimport = false; var verbose_llvm_cpu_features = false; @@ -1233,8 +1232,6 @@ fn buildOutputType( verbose_cc = true; } else if (mem.eql(u8, arg, "--verbose-air")) { verbose_air = true; - } else if (mem.eql(u8, arg, "--verbose-mir")) { - verbose_mir = true; } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) { verbose_llvm_ir = true; } else if (mem.eql(u8, arg, "--verbose-cimport")) { @@ -2720,7 +2717,6 @@ fn buildOutputType( .verbose_cc = verbose_cc, .verbose_link = verbose_link, .verbose_air = verbose_air, - .verbose_mir = verbose_mir, .verbose_llvm_ir = verbose_llvm_ir, .verbose_cimport = verbose_cimport, .verbose_llvm_cpu_features = verbose_llvm_cpu_features, From 5b03d55c5ecf697086db7c454e0f3982fec42408 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 15 May 2022 10:03:38 +0200 Subject: [PATCH 1494/2031] x64: rename brk to int3, and MIR to interrupt --- src/arch/x86_64/CodeGen.zig | 6 ++++-- src/arch/x86_64/Emit.zig | 16 +++++++++++----- src/arch/x86_64/Mir.zig | 5 +++-- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 80a9bbbd78..5ba28a3751 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -3755,8 +3755,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { fn airBreakpoint(self: *Self) !void { _ = try self.addInst(.{ - .tag = .brk, - .ops = undefined, + .tag = .interrupt, + .ops = (Mir.Ops{ + .flags = 0b00, + }).encode(), .data = undefined, }); return self.finishAirBookkeeping(); diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index f83557f5ad..518635b806 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -178,7 +178,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .@"test" => try emit.mirTest(inst), - .brk => try emit.mirBrk(), + .interrupt => try emit.mirInterrupt(inst), .nop => try emit.mirNop(), .call_extern => try emit.mirCallExtern(inst), @@ -225,8 +225,14 @@ fn fixupRelocs(emit: *Emit) InnerError!void { } } -fn mirBrk(emit: *Emit) InnerError!void { - return lowerToZoEnc(.brk, emit.code); +fn mirInterrupt(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; + assert(tag == .interrupt); + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + switch (ops.flags) { + 0b00 => return lowerToZoEnc(.int3, emit.code), + else => return emit.fail("TODO handle variant 0b{b} of interrupt instruction", .{ops.flags}), + } } fn mirNop(emit: *Emit) InnerError!void { @@ -1074,7 +1080,7 @@ const Tag = enum { push, pop, @"test", - brk, + int3, nop, imul, mul, @@ -1278,7 +1284,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .zo => return switch (tag) { .ret_near => OpCode.oneByte(0xc3), .ret_far => OpCode.oneByte(0xcb), - .brk => OpCode.oneByte(0xcc), + .int3 => OpCode.oneByte(0xcc), .nop => OpCode.oneByte(0x90), .syscall => OpCode.twoByte(0x0f, 0x05), .cbw => OpCode.oneByte(0x98), diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 50f28d7f19..30f4351cb0 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -342,8 +342,9 @@ pub const Inst = struct { /// TODO handle more cases @"test", - /// Breakpoint - brk, + /// Breakpoint form: + /// 0b00 int3 + interrupt, /// Nop nop, From 662a61fcc3824deacba510e5b036be6feb941d2b Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sat, 30 Apr 2022 20:49:57 +0700 Subject: [PATCH 1495/2031] stage2: sparc64: Implement airIsErr and airIsNonErr --- src/arch/sparc64/CodeGen.zig | 84 +++++++++++++++++++++++++++++++++++- src/arch/sparc64/Emit.zig | 1 + src/arch/sparc64/Mir.zig | 1 + 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 0745cd46c9..5b4ab0d564 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -125,6 +125,12 @@ const MCValue = union(enum) { stack_offset: u32, /// The value is a pointer to one of the stack variables (payload is stack offset). ptr_stack_offset: u32, + /// The value is in the compare flags assuming an unsigned operation, + /// with this operator applied on top of it. + compare_flags_unsigned: math.CompareOperator, + /// The value is in the compare flags assuming a signed operation, + /// with this operator applied on top of it. + compare_flags_signed: math.CompareOperator, fn isMemory(mcv: MCValue) bool { return switch (mcv) { @@ -536,9 +542,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .is_non_null_ptr => @panic("TODO try self.airIsNonNullPtr(inst)"), .is_null => @panic("TODO try self.airIsNull(inst)"), .is_null_ptr => @panic("TODO try self.airIsNullPtr(inst)"), - .is_non_err => @panic("TODO try self.airIsNonErr(inst)"), + .is_non_err => try self.airIsNonErr(inst), .is_non_err_ptr => @panic("TODO try self.airIsNonErrPtr(inst)"), - .is_err => @panic("TODO try self.airIsErr(inst)"), + .is_err => try self.airIsErr(inst), .is_err_ptr => @panic("TODO try self.airIsErrPtr(inst)"), .load => @panic("TODO try self.airLoad(inst)"), .loop => @panic("TODO try self.airLoop(inst)"), @@ -872,6 +878,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .unreach => unreachable, .dead => unreachable, .memory => unreachable, + .compare_flags_signed => unreachable, + .compare_flags_unsigned => unreachable, .register => |reg| { try self.register_manager.getReg(reg, null); try self.genSetReg(arg_ty, reg, arg_mcv); @@ -1004,6 +1012,26 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const operand = try self.resolveInst(un_op); + const ty = self.air.typeOf(un_op); + break :result try self.isErr(ty, operand); + }; + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + +fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const operand = try self.resolveInst(un_op); + const ty = self.air.typeOf(un_op); + break :result try self.isNonErr(ty, operand); + }; + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn airRet(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); @@ -1259,6 +1287,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void switch (mcv) { .dead => unreachable, .unreach, .none => return, // Nothing to do. + .compare_flags_signed => return self.fail("TODO: genSetReg for compare_flags_signed", .{}), + .compare_flags_unsigned => return self.fail("TODO: genSetReg for compare_flags_unsigned", .{}), .undef => { if (!self.wantSafety()) return; // The already existing value will do just fine. @@ -1426,6 +1456,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro else => return self.fail("TODO implement memset", .{}), } }, + .compare_flags_unsigned, + .compare_flags_signed, .immediate, .ptr_stack_offset, => { @@ -1522,6 +1554,54 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { } } +fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { + const error_type = ty.errorUnionSet(); + const payload_type = ty.errorUnionPayload(); + + if (!error_type.hasRuntimeBits()) { + return MCValue{ .immediate = 0 }; // always false + } else if (!payload_type.hasRuntimeBits()) { + if (error_type.abiSize(self.target.*) <= 8) { + const reg_mcv: MCValue = switch (operand) { + .register => operand, + else => .{ .register = try self.copyToTmpRegister(error_type, operand) }, + }; + + _ = try self.addInst(.{ + .tag = .subcc, + .data = .{ .arithmetic_3op = .{ + .is_imm = true, + .rs1 = reg_mcv.register, + .rs2_or_imm = .{ .imm = 0 }, + .rd = .g0, + } }, + }); + + return MCValue{ .compare_flags_unsigned = .gt }; + } else { + return self.fail("TODO isErr for errors with size > 8", .{}); + } + } else { + return self.fail("TODO isErr for non-empty payloads", .{}); + } +} + +fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { + // Call isErr, then negate the result. + const is_err_result = try self.isErr(ty, operand); + switch (is_err_result) { + .compare_flags_unsigned => |op| { + assert(op == .gt); + return MCValue{ .compare_flags_unsigned = .lte }; + }, + .immediate => |imm| { + assert(imm == 0); + return MCValue{ .immediate = 1 }; + }, + else => unreachable, + } +} + fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb { try self.ensureProcessDeathCapacity(operand_count + 1); return BigTomb{ diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 81ae062c61..29ae8572a5 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -81,6 +81,7 @@ pub fn emitMir( .stx => try emit.mirArithmetic3Op(inst), .sub => try emit.mirArithmetic3Op(inst), + .subcc => @panic("TODO implement sparcv9 subcc"), .tcc => try emit.mirTrap(inst), } diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index ef0be93f4c..bdf25814a9 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -106,6 +106,7 @@ pub const Inst = struct { /// This uses the arithmetic_3op field. // TODO add other operations. sub, + subcc, /// A.61 Trap on Integer Condition Codes (Tcc) /// This uses the trap field. From fd781195de54b58a5f103f86c5f0784454a53439 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 2 May 2022 19:55:51 +0700 Subject: [PATCH 1496/2031] stage2: sparc64: Split the conditionals between integer and FP ones On SPARCv9 the integer and FP conditional branch codes doesn't align with each other at all, so the two need to be treated separately. --- src/arch/sparc64/CodeGen.zig | 6 +- src/arch/sparc64/Mir.zig | 10 +- src/arch/sparc64/bits.zig | 196 ++++++++++++++++++++++++++++++++--- 3 files changed, 187 insertions(+), 25 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 5b4ab0d564..9871023867 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -725,7 +725,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { .data = .{ .trap = .{ .is_imm = true, - .cond = 0b1000, // TODO need to look into changing this into an enum + .cond = .al, .rs2_or_imm = .{ .imm = 0x6d }, }, }, @@ -842,7 +842,7 @@ fn airBreakpoint(self: *Self) !void { .data = .{ .trap = .{ .is_imm = true, - .cond = 0b1000, // TODO need to look into changing this into an enum + .cond = .al, .rs2_or_imm = .{ .imm = 0x01 }, }, }, @@ -1648,7 +1648,7 @@ fn parseRegName(name: []const u8) ?Register { fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { const tag = self.mir_instructions.items(.tag)[inst]; switch (tag) { - .bpcc => self.mir_instructions.items(.data)[inst].branch_predict.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len), + .bpcc => self.mir_instructions.items(.data)[inst].branch_predict_int.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len), else => unreachable, } } diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index bdf25814a9..f61f76bcba 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -44,7 +44,7 @@ pub const Inst = struct { add, /// A.7 Branch on Integer Condition Codes with Prediction (BPcc) - /// This uses the branch_predict field. + /// This uses the branch_predict_int field. bpcc, /// A.8 Call and Link @@ -165,13 +165,13 @@ pub const Inst = struct { link: Register = .o7, }, - /// Branch with prediction. + /// Branch with prediction, checking the integer status code /// Used by e.g. bpcc - branch_predict: struct { + branch_predict_int: struct { annul: bool = false, pt: bool = true, ccr: Instruction.CCR, - cond: Instruction.Condition, + cond: Instruction.ICondition, inst: Index, }, @@ -211,7 +211,7 @@ pub const Inst = struct { /// Used by e.g. tcc trap: struct { is_imm: bool = true, - cond: Instruction.Condition, + cond: Instruction.ICondition, ccr: Instruction.CCR = .icc, rs1: Register = .g0, rs2_or_imm: union { diff --git a/src/arch/sparc64/bits.zig b/src/arch/sparc64/bits.zig index e66b24f617..ac45f1876b 100644 --- a/src/arch/sparc64/bits.zig +++ b/src/arch/sparc64/bits.zig @@ -512,10 +512,172 @@ pub const Instruction = union(enum) { lookaside: bool = false, }; - // TODO: Need to define an enum for `cond` values - // This is kinda challenging since the cond values have different meanings - // depending on whether it's operating on integer or FP CCR. - pub const Condition = u4; + // In SPARCv9, FP and integer comparison operations + // are encoded differently. + + pub const FCondition = enum(u4) { + /// Branch Never + nv, + /// Branch on Not Equal + ne, + /// Branch on Less or Greater + lg, + /// Branch on Unordered or Less + ul, + /// Branch on Less + lt, + /// Branch on Unordered or Greater + ug, + /// Branch on Greater + gt, + /// Branch on Unordered + un, + /// Branch Always + al, + /// Branch on Equal + eq, + /// Branch on Unordered or Equal + ue, + /// Branch on Greater or Equal + ge, + /// Branch on Unordered or Greater or Equal + uge, + /// Branch on Less or Equal + le, + /// Branch on Unordered or Less or Equal + ule, + /// Branch on Ordered + ord, + + /// Converts a std.math.CompareOperator into a condition flag, + /// i.e. returns the condition that is true iff the result of the + /// comparison is true. + pub fn fromCompareOperator(op: std.math.CompareOperator) FCondition { + return switch (op) { + .gte => .ge, + .gt => .gt, + .neq => .ne, + .lt => .lt, + .lte => .le, + .eq => .eq, + }; + } + + /// Returns the condition which is true iff the given condition is + /// false (if such a condition exists). + pub fn negate(cond: FCondition) FCondition { + return switch (cond) { + .eq => .ne, + .ne => .eq, + .ge => .ul, + .ul => .ge, + .le => .ug, + .ug => .le, + .lt => .uge, + .uge => .lt, + .gt => .ule, + .ule => .gt, + .ue => .lg, + .lg => .ue, + .ord => .un, + .un => .ord, + .al => unreachable, + .nv => unreachable, + }; + } + }; + + pub const ICondition = enum(u4) { + /// Branch Never + nv, + /// Branch on Equal + eq, + /// Branch on Less or Equal + le, + /// Branch on Less + lt, + /// Branch on Less or Equal Unsigned + leu, + /// Branch on Carry Set (Less than, Unsigned) + cs, + /// Branch on Negative + neg, + /// Branch on Overflow Set + vs, + /// Branch Always + al, + /// Branch on Not Equal + ne, + /// Branch on Greater + gt, + /// Branch on Greater or Equal + ge, + /// Branch on Greater Unsigned + gu, + /// Branch on Carry Clear (Greater Than or Equal, Unsigned) + cc, + /// Branch on Positive + pos, + /// Branch on Overflow Clear + vc, + + /// Converts a std.math.CompareOperator into a condition flag, + /// i.e. returns the condition that is true iff the result of the + /// comparison is true. Assumes signed comparison. + pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) ICondition { + return switch (op) { + .gte => .ge, + .gt => .gt, + .neq => .ne, + .lt => .lt, + .lte => .le, + .eq => .eq, + }; + } + + /// Converts a std.math.CompareOperator into a condition flag, + /// i.e. returns the condition that is true iff the result of the + /// comparison is true. Assumes unsigned comparison. + pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) ICondition { + return switch (op) { + .gte => .cc, + .gt => .gu, + .neq => .ne, + .lt => .cs, + .lte => .le, + .eq => .eq, + }; + } + + /// Returns the condition which is true iff the given condition is + /// false (if such a condition exists). + pub fn negate(cond: ICondition) ICondition { + return switch (cond) { + .eq => .ne, + .ne => .eq, + .cs => .cc, + .cc => .cs, + .neg => .pos, + .pos => .neg, + .vs => .vc, + .vc => .vs, + .gu => .leu, + .leu => .gu, + .ge => .lt, + .lt => .ge, + .gt => .le, + .le => .gt, + .al => unreachable, + .nv => unreachable, + }; + } + }; + + pub const Condition = packed union { + fcond: FCondition, + icond: ICondition, + encoded: u4, + }; pub fn toU32(self: Instruction) u32 { // TODO: Remove this once packed structs work. @@ -593,7 +755,7 @@ pub const Instruction = union(enum) { return Instruction{ .format_2b = .{ .a = @boolToInt(annul), - .cond = cond, + .cond = cond.encoded, .op2 = op2, .disp22 = udisp_truncated, }, @@ -614,7 +776,7 @@ pub const Instruction = union(enum) { return Instruction{ .format_2c = .{ .a = @boolToInt(annul), - .cond = cond, + .cond = cond.encoded, .op2 = op2, .cc1 = ccr_cc1, .cc0 = ccr_cc0, @@ -895,7 +1057,7 @@ pub const Instruction = union(enum) { .rd = rd.enc(), .op3 = op3, .cc2 = ccr_cc2, - .cond = cond, + .cond = cond.encoded, .cc1 = ccr_cc1, .cc0 = ccr_cc0, .rs2 = rs2.enc(), @@ -912,7 +1074,7 @@ pub const Instruction = union(enum) { .rd = rd.enc(), .op3 = op3, .cc2 = ccr_cc2, - .cond = cond, + .cond = cond.encoded, .cc1 = ccr_cc1, .cc0 = ccr_cc0, .simm11 = @bitCast(u11, imm), @@ -960,7 +1122,7 @@ pub const Instruction = union(enum) { .format_4g = .{ .rd = rd.enc(), .op3 = op3, - .cond = cond, + .cond = cond.encoded, .opf_cc = opf_cc, .opf_low = opf_low, .rs2 = rs2.enc(), @@ -1099,11 +1261,11 @@ pub const Instruction = union(enum) { }; } - pub fn trap(comptime s2: type, cond: Condition, ccr: CCR, rs1: Register, rs2: s2) Instruction { + pub fn trap(comptime s2: type, cond: ICondition, ccr: CCR, rs1: Register, rs2: s2) Instruction { // Tcc instructions abuse the rd field to store the conditionals. return switch (s2) { - Register => format4a(0b11_1010, ccr, rs1, rs2, @intToEnum(Register, cond)), - u7 => format4e(0b11_1010, ccr, rs1, @intToEnum(Register, cond), rs2), + Register => format4a(0b11_1010, ccr, rs1, rs2, @intToEnum(Register, @enumToInt(cond))), + u7 => format4e(0b11_1010, ccr, rs1, @intToEnum(Register, @enumToInt(cond)), rs2), else => unreachable, }; } @@ -1128,11 +1290,11 @@ test "Serialize formats" { .expected = 0b00_00000_100_0000000000000000000000, }, .{ - .inst = Instruction.format2b(6, 3, true, -4), + .inst = Instruction.format2b(6, .{ .icond = .lt }, true, -4), .expected = 0b00_1_0011_110_1111111111111111111111, }, .{ - .inst = Instruction.format2c(3, 0, false, true, .xcc, 8), + .inst = Instruction.format2c(3, .{ .icond = .nv }, false, true, .xcc, 8), .expected = 0b00_0_0000_011_1_0_1_0000000000000000010, }, .{ @@ -1224,11 +1386,11 @@ test "Serialize formats" { .expected = 0b10_10010_001000_00000_1_1_0_11111111111, }, .{ - .inst = Instruction.format4c(8, 0, .xcc, .g0, .o1), + .inst = Instruction.format4c(8, .{ .icond = .nv }, .xcc, .g0, .o1), .expected = 0b10_01001_001000_1_0000_0_1_0_000000_00000, }, .{ - .inst = Instruction.format4d(8, 0, .xcc, 0, .l2), + .inst = Instruction.format4d(8, .{ .icond = .nv }, .xcc, 0, .l2), .expected = 0b10_10010_001000_1_0000_1_1_0_00000000000, }, .{ @@ -1240,7 +1402,7 @@ test "Serialize formats" { .expected = 0b10_10010_001000_00000_0_001_00100_01001, }, .{ - .inst = Instruction.format4g(8, 4, 2, 0, .o1, .l2), + .inst = Instruction.format4g(8, 4, 2, .{ .icond = .nv }, .o1, .l2), .expected = 0b10_10010_001000_0_0000_010_000100_01001, }, }; From 2dc2ab091e423fe087a6bc12ea6ada0214fd106b Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 2 May 2022 20:33:55 +0700 Subject: [PATCH 1497/2031] stage2: sparc64: Implement airCondBr from flags register --- src/arch/sparc64/CodeGen.zig | 220 +++++++++++++++++++++++++++++++++-- 1 file changed, 213 insertions(+), 7 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 9871023867..93f147740e 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -28,6 +28,7 @@ const build_options = @import("build_options"); const bits = @import("bits.zig"); const abi = @import("abi.zig"); +const Instruction = bits.Instruction; const Register = bits.Register; const Self = @This(); @@ -90,6 +91,9 @@ register_manager: RegisterManager = .{}, /// Maps offset to what is stored there. stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, +/// Tracks the current instruction allocated to the compare flags +compare_flags_inst: ?Air.Inst.Index = null, + /// Offset from the stack base, representing the end of the stack frame. max_end_stack: u32 = 0, /// Represents the current end stack offset. If there is no existing slot @@ -373,18 +377,31 @@ fn gen(self: *Self) !void { // exitlude jumps if (self.exitlude_jump_relocs.items.len > 0 and - self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2) + self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 3) { // If the last Mir instruction (apart from the // dbg_epilogue_begin) is the last exitlude jump - // relocation (which would just jump one instruction + // relocation (which would just jump two instructions // further), it can be safely removed - self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop()); + const index = self.exitlude_jump_relocs.pop(); + + // First, remove the delay slot, then remove + // the branch instruction itself. + self.mir_instructions.orderedRemove(index + 1); + self.mir_instructions.orderedRemove(index); } for (self.exitlude_jump_relocs.items) |jmp_reloc| { - _ = jmp_reloc; - return self.fail("TODO add branches in sparc64", .{}); + self.mir_instructions.set(jmp_reloc, .{ + .tag = .bpcc, + .data = .{ + .branch_predict_int = .{ + .ccr = .xcc, + .cond = .al, + .inst = @intCast(u32, self.mir_instructions.len), + }, + }, + }); } // Backpatch stack offset @@ -531,7 +548,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .ret_addr => @panic("TODO try self.airRetAddr(inst)"), .frame_addr => @panic("TODO try self.airFrameAddress(inst)"), .fence => @panic("TODO try self.airFence()"), - .cond_br => @panic("TODO try self.airCondBr(inst)"), + .cond_br => try self.airCondBr(inst), .dbg_stmt => try self.airDbgStmt(inst), .fptrunc => @panic("TODO try self.airFptrunc(inst)"), .fpext => @panic("TODO try self.airFpext(inst)"), @@ -968,6 +985,188 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. @panic("TODO handle return value with BigTomb"); } +fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const cond = try self.resolveInst(pl_op.operand); + const extra = self.air.extraData(Air.CondBr, pl_op.payload); + const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len]; + const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; + const liveness_condbr = self.liveness.getCondBr(inst); + + // Here we either emit a BPcc for branching on CCR content, + // or emit a BPr to branch on register content. + const reloc: Mir.Inst.Index = switch (cond) { + .compare_flags_signed, + .compare_flags_unsigned, + => try self.addInst(.{ + .tag = .bpcc, + .data = .{ + .branch_predict_int = .{ + .ccr = .xcc, + .cond = switch (cond) { + .compare_flags_signed => |cmp_op| blk: { + // Here we map to the opposite condition because the jump is to the false branch. + const condition = Instruction.ICondition.fromCompareOperatorSigned(cmp_op); + break :blk condition.negate(); + }, + .compare_flags_unsigned => |cmp_op| blk: { + // Here we map to the opposite condition because the jump is to the false branch. + const condition = Instruction.ICondition.fromCompareOperatorUnsigned(cmp_op); + break :blk condition.negate(); + }, + else => unreachable, + }, + .inst = undefined, // Will be filled by performReloc + }, + }, + }), + else => return self.fail("TODO branch on register content (BPr)", .{}), + }; + + // Regardless of the branch type that's emitted, we need to reserve + // a space for the delay slot. + // TODO Find a way to fill this delay slot + _ = try self.addInst(.{ + .tag = .nop, + .data = .{ .nop = {} }, + }); + + // If the condition dies here in this condbr instruction, process + // that death now instead of later as this has an effect on + // whether it needs to be spilled in the branches + if (self.liveness.operandDies(inst, 0)) { + const op_int = @enumToInt(pl_op.operand); + if (op_int >= Air.Inst.Ref.typed_value_map.len) { + const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); + self.processDeath(op_index); + } + } + + // Capture the state of register and stack allocation state so that we can revert to it. + const parent_next_stack_offset = self.next_stack_offset; + const parent_free_registers = self.register_manager.free_registers; + var parent_stack = try self.stack.clone(self.gpa); + defer parent_stack.deinit(self.gpa); + const parent_registers = self.register_manager.registers; + const parent_compare_flags_inst = self.compare_flags_inst; + + try self.branch_stack.append(.{}); + errdefer { + _ = self.branch_stack.pop(); + } + + try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len); + for (liveness_condbr.then_deaths) |operand| { + self.processDeath(operand); + } + try self.genBody(then_body); + + // Revert to the previous register and stack allocation state. + + var saved_then_branch = self.branch_stack.pop(); + defer saved_then_branch.deinit(self.gpa); + + self.register_manager.registers = parent_registers; + self.compare_flags_inst = parent_compare_flags_inst; + + self.stack.deinit(self.gpa); + self.stack = parent_stack; + parent_stack = .{}; + + self.next_stack_offset = parent_next_stack_offset; + self.register_manager.free_registers = parent_free_registers; + + try self.performReloc(reloc); + const else_branch = self.branch_stack.addOneAssumeCapacity(); + else_branch.* = .{}; + + try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len); + for (liveness_condbr.else_deaths) |operand| { + self.processDeath(operand); + } + try self.genBody(else_body); + + // At this point, each branch will possibly have conflicting values for where + // each instruction is stored. They agree, however, on which instructions are alive/dead. + // We use the first ("then") branch as canonical, and here emit + // instructions into the second ("else") branch to make it conform. + // We continue respect the data structure semantic guarantees of the else_branch so + // that we can use all the code emitting abstractions. This is why at the bottom we + // assert that parent_branch.free_registers equals the saved_then_branch.free_registers + // rather than assigning it. + const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2]; + try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count()); + + const else_slice = else_branch.inst_table.entries.slice(); + const else_keys = else_slice.items(.key); + const else_values = else_slice.items(.value); + for (else_keys) |else_key, else_idx| { + const else_value = else_values[else_idx]; + const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: { + // The instruction's MCValue is overridden in both branches. + parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value); + if (else_value == .dead) { + assert(then_entry.value == .dead); + continue; + } + break :blk then_entry.value; + } else blk: { + if (else_value == .dead) + continue; + // The instruction is only overridden in the else branch. + var i: usize = self.branch_stack.items.len - 2; + while (true) { + i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead? + if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| { + assert(mcv != .dead); + break :blk mcv; + } + } + }; + log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv }); + // TODO make sure the destination stack offset / register does not already have something + // going on there. + try self.setRegOrMem(self.air.typeOfIndex(else_key), canon_mcv, else_value); + // TODO track the new register / stack allocation + } + try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count()); + const then_slice = saved_then_branch.inst_table.entries.slice(); + const then_keys = then_slice.items(.key); + const then_values = then_slice.items(.value); + for (then_keys) |then_key, then_idx| { + const then_value = then_values[then_idx]; + // We already deleted the items from this table that matched the else_branch. + // So these are all instructions that are only overridden in the then branch. + parent_branch.inst_table.putAssumeCapacity(then_key, then_value); + if (then_value == .dead) + continue; + const parent_mcv = blk: { + var i: usize = self.branch_stack.items.len - 2; + while (true) { + i -= 1; + if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| { + assert(mcv != .dead); + break :blk mcv; + } + } + }; + log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value }); + // TODO make sure the destination stack offset / register does not already have something + // going on there. + try self.setRegOrMem(self.air.typeOfIndex(then_key), parent_mcv, then_value); + // TODO track the new register / stack allocation + } + + { + var item = self.branch_stack.pop(); + item.deinit(self.gpa); + } + + // We already took care of pl_op.operand earlier, so we're going + // to pass .none here + return self.finishAir(inst, .unreach, .{ .none, .none, .none }); +} + fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { // TODO emit debug info lexical block return self.finishAir(inst, .dead, .{ .none, .none, .none }); @@ -1798,11 +1997,18 @@ fn ret(self: *Self, mcv: MCValue) !void { const ret_ty = self.fn_type.fnReturnType(); try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); - // Just add space for an instruction, patch this later + // Just add space for a branch instruction, patch this later const index = try self.addInst(.{ .tag = .nop, .data = .{ .nop = {} }, }); + + // Reserve space for the delay slot too + // TODO find out a way to fill this + _ = try self.addInst(.{ + .tag = .nop, + .data = .{ .nop = {} }, + }); try self.exitlude_jump_relocs.append(self.gpa, index); } From b6de8d2565974318b888f4e93eab6235fcce6419 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 2 May 2022 20:45:14 +0700 Subject: [PATCH 1498/2031] stage2: sparc64: Implement airUnwrapErrPayload --- src/arch/sparc64/CodeGen.zig | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 93f147740e..979fc6c518 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -649,7 +649,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .optional_payload_ptr => @panic("TODO try self.airOptionalPayloadPtr(inst)"), .optional_payload_ptr_set => @panic("TODO try self.airOptionalPayloadPtrSet(inst)"), .unwrap_errunion_err => @panic("TODO try self.airUnwrapErrErr(inst)"), - .unwrap_errunion_payload => @panic("TODO try self.airUnwrapErrPayload(inst)"), + .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst), .unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"), .unwrap_errunion_payload_ptr=> @panic("TODO try self.airUnwrapErrPayloadPtr(inst)"), .errunion_payload_ptr_set => @panic("TODO try self.airErrUnionPayloadPtrSet(inst)"), @@ -1265,6 +1265,18 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement switch for {}", .{self.target.cpu.arch}); } +fn airUnwrapErrPayload(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 result: { + const error_union_ty = self.air.typeOf(ty_op.operand); + const payload_ty = error_union_ty.errorUnionPayload(); + if (!payload_ty.hasRuntimeBits()) break :result MCValue.none; + + return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{}); + }; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + // Common helper functions /// Adds a Type to the .debug_info at the current position. The bytes will be populated later, From 2770f9a03464dee1e11134054598a6e07c30a6e4 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 2 May 2022 21:55:57 +0700 Subject: [PATCH 1499/2031] stage2: sparc64: Implement airBr --- src/arch/sparc64/CodeGen.zig | 58 +++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 979fc6c518..f10baf0f85 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -543,7 +543,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .assembly => try self.airAsm(inst), .bitcast => @panic("TODO try self.airBitCast(inst)"), .block => try self.airBlock(inst), - .br => @panic("TODO try self.airBr(inst)"), + .br => try self.airBr(inst), .breakpoint => try self.airBreakpoint(), .ret_addr => @panic("TODO try self.airRetAddr(inst)"), .frame_addr => @panic("TODO try self.airFrameAddress(inst)"), @@ -852,6 +852,12 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ .none, .none, .none }); } +fn airBr(self: *Self, inst: Air.Inst.Index) !void { + const branch = self.air.instructions.items(.data)[inst].br; + try self.br(branch.block_inst, branch.operand); + return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); +} + fn airBreakpoint(self: *Self) !void { // ta 0x01 _ = try self.addInst(.{ @@ -1365,6 +1371,56 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { return MCValue{ .stack_offset = stack_offset }; } +fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { + const block_data = self.blocks.getPtr(block).?; + + if (self.air.typeOf(operand).hasRuntimeBits()) { + const operand_mcv = try self.resolveInst(operand); + const block_mcv = block_data.mcv; + if (block_mcv == .none) { + block_data.mcv = switch (operand_mcv) { + .none, .dead, .unreach => unreachable, + .register, .stack_offset, .memory => operand_mcv, + .immediate => blk: { + const new_mcv = try self.allocRegOrMem(block, true); + try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); + break :blk new_mcv; + }, + else => return self.fail("TODO implement block_data.mcv = operand_mcv for {}", .{operand_mcv}), + }; + } else { + try self.setRegOrMem(self.air.typeOfIndex(block), block_mcv, operand_mcv); + } + } + return self.brVoid(block); +} + +fn brVoid(self: *Self, block: Air.Inst.Index) !void { + const block_data = self.blocks.getPtr(block).?; + + // Emit a jump with a relocation. It will be patched up after the block ends. + try block_data.relocs.ensureUnusedCapacity(self.gpa, 1); + + const br_index = try self.addInst(.{ + .tag = .bpcc, + .data = .{ + .branch_predict_int = .{ + .ccr = .xcc, + .cond = .al, + .inst = undefined, // Will be filled by performReloc + }, + }, + }); + + // TODO Find a way to fill this delay slot + _ = try self.addInst(.{ + .tag = .nop, + .data = .{ .nop = {} }, + }); + + block_data.relocs.appendAssumeCapacity(br_index); +} + /// Copies a value to a register without tracking the register. The register is not considered /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. From 5d260eb573132194e802680803f225404a06b00e Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 2 May 2022 21:56:04 +0700 Subject: [PATCH 1500/2031] stage2: sparc64: Implement SPARCv9 subcc --- src/arch/sparc64/Emit.zig | 4 +++- src/arch/sparc64/bits.zig | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 29ae8572a5..30f22a0d0f 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -81,7 +81,7 @@ pub fn emitMir( .stx => try emit.mirArithmetic3Op(inst), .sub => try emit.mirArithmetic3Op(inst), - .subcc => @panic("TODO implement sparcv9 subcc"), + .subcc => try emit.mirArithmetic3Op(inst), .tcc => try emit.mirTrap(inst), } @@ -169,6 +169,7 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { .stw => try emit.writeInstruction(Instruction.stw(i13, rs1, imm, rd)), .stx => try emit.writeInstruction(Instruction.stx(i13, rs1, imm, rd)), .sub => try emit.writeInstruction(Instruction.sub(i13, rs1, imm, rd)), + .subcc => try emit.writeInstruction(Instruction.subcc(i13, rs1, imm, rd)), else => unreachable, } } else { @@ -188,6 +189,7 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { .stw => try emit.writeInstruction(Instruction.stw(Register, rs1, rs2, rd)), .stx => try emit.writeInstruction(Instruction.stx(Register, rs1, rs2, rd)), .sub => try emit.writeInstruction(Instruction.sub(Register, rs1, rs2, rd)), + .subcc => try emit.writeInstruction(Instruction.subcc(Register, rs1, rs2, rd)), else => unreachable, } } diff --git a/src/arch/sparc64/bits.zig b/src/arch/sparc64/bits.zig index ac45f1876b..d323288fac 100644 --- a/src/arch/sparc64/bits.zig +++ b/src/arch/sparc64/bits.zig @@ -1261,6 +1261,14 @@ pub const Instruction = union(enum) { }; } + pub fn subcc(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b10, 0b01_0100, rs1, rs2, rd), + i13 => format3b(0b10, 0b01_0100, rs1, rs2, rd), + else => unreachable, + }; + } + pub fn trap(comptime s2: type, cond: ICondition, ccr: CCR, rs1: Register, rs2: s2) Instruction { // Tcc instructions abuse the rd field to store the conditionals. return switch (s2) { From 339b0517b3a1e33f2b348ac9764687847054bea2 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 6 May 2022 21:59:47 +0700 Subject: [PATCH 1501/2031] stage2: sparc64: Implement SPARCv9 bpcc --- src/arch/sparc64/Emit.zig | 211 +++++++++++++++++++++++++++++++++++++- src/arch/sparc64/bits.zig | 4 + 2 files changed, 214 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 30f22a0d0f..c7c6ac4904 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -8,6 +8,7 @@ const link = @import("../../link.zig"); const Module = @import("../../Module.zig"); const ErrorMsg = Module.ErrorMsg; const Liveness = @import("../../Liveness.zig"); +const log = std.log.scoped(.sparcv9_emit); const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; const DW = std.dwarf; const leb128 = std.leb; @@ -31,16 +32,42 @@ prev_di_column: u32, /// Relative to the beginning of `code`. prev_di_pc: usize, +/// The branch type of every branch +branch_types: std.AutoHashMapUnmanaged(Mir.Inst.Index, BranchType) = .{}, +/// For every forward branch, maps the target instruction to a list of +/// branches which branch to this target instruction +branch_forward_origins: std.AutoHashMapUnmanaged(Mir.Inst.Index, std.ArrayListUnmanaged(Mir.Inst.Index)) = .{}, +/// For backward branches: stores the code offset of the target +/// instruction +/// +/// For forward branches: stores the code offset of the branch +/// instruction +code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .{}, + const InnerError = error{ OutOfMemory, EmitFail, }; +const BranchType = enum { + bpcc, + fn default(tag: Mir.Inst.Tag) BranchType { + return switch (tag) { + .bpcc => .bpcc, + else => unreachable, + }; + } +}; + pub fn emitMir( emit: *Emit, ) InnerError!void { const mir_tags = emit.mir.instructions.items(.tag); + // Convert absolute addresses into offsets and + // find smallest lowerings for branch instructions + try emit.lowerBranches(); + // Emit machine code for (mir_tags) |tag, index| { const inst = @intCast(u32, index); @@ -51,7 +78,7 @@ pub fn emitMir( .add => try emit.mirArithmetic3Op(inst), - .bpcc => @panic("TODO implement sparc64 bpcc"), + .bpcc => try emit.mirConditionalBranch(inst), .call => @panic("TODO implement sparc64 call"), @@ -89,6 +116,14 @@ pub fn emitMir( } pub fn deinit(emit: *Emit) void { + var iter = emit.branch_forward_origins.valueIterator(); + while (iter.next()) |origin_list| { + origin_list.deinit(emit.bin_file.allocator); + } + + emit.branch_types.deinit(emit.bin_file.allocator); + emit.branch_forward_origins.deinit(emit.bin_file.allocator); + emit.code_offset_mapping.deinit(emit.bin_file.allocator); emit.* = undefined; } @@ -195,6 +230,22 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { } } +fn mirConditionalBranch(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const branch_predict_int = emit.mir.instructions.items(.data)[inst].branch_predict_int; + + const offset = @intCast(i64, emit.code_offset_mapping.get(branch_predict_int.inst).?) - @intCast(i64, emit.code.items.len); + const branch_type = emit.branch_types.get(inst).?; + log.debug("mirConditionalBranchImmediate: {} offset={}", .{ inst, offset }); + + switch (branch_type) { + .bpcc => switch (tag) { + .bpcc => try emit.writeInstruction(Instruction.bpcc(branch_predict_int.cond, branch_predict_int.annul, branch_predict_int.pt, branch_predict_int.ccr, @intCast(i21, offset))), + else => unreachable, + }, + } +} + fn mirNop(emit: *Emit) !void { try emit.writeInstruction(Instruction.nop()); } @@ -235,6 +286,15 @@ fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void { // Common helper functions +fn branchTarget(emit: *Emit, inst: Mir.Inst.Index) Mir.Inst.Index { + const tag = emit.mir.instructions.items(.tag)[inst]; + + switch (tag) { + .bpcc => return emit.mir.instructions.items(.data)[inst].branch_predict_int.inst, + else => unreachable, + } +} + fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void { const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line); const delta_pc: usize = emit.code.items.len - emit.prev_di_pc; @@ -267,6 +327,155 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { return error.EmitFail; } +fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize { + const tag = emit.mir.instructions.items(.tag)[inst]; + + switch (tag) { + .dbg_line, + .dbg_epilogue_begin, + .dbg_prologue_end, + => return 0, + // Currently Mir instructions always map to single machine instruction. + else => return 4, + } +} + +fn isBranch(tag: Mir.Inst.Tag) bool { + return switch (tag) { + .bpcc => true, + else => false, + }; +} + +fn lowerBranches(emit: *Emit) !void { + const mir_tags = emit.mir.instructions.items(.tag); + const allocator = emit.bin_file.allocator; + + // First pass: Note down all branches and their target + // instructions, i.e. populate branch_types, + // branch_forward_origins, and code_offset_mapping + // + // TODO optimization opportunity: do this in codegen while + // generating MIR + for (mir_tags) |tag, index| { + const inst = @intCast(u32, index); + if (isBranch(tag)) { + const target_inst = emit.branchTarget(inst); + + // Remember this branch instruction + try emit.branch_types.put(allocator, inst, BranchType.default(tag)); + + // Forward branches require some extra stuff: We only + // know their offset once we arrive at the target + // instruction. Therefore, we need to be able to + // access the branch instruction when we visit the + // target instruction in order to manipulate its type + // etc. + if (target_inst > inst) { + // Remember the branch instruction index + try emit.code_offset_mapping.put(allocator, inst, 0); + + if (emit.branch_forward_origins.getPtr(target_inst)) |origin_list| { + try origin_list.append(allocator, inst); + } else { + var origin_list: std.ArrayListUnmanaged(Mir.Inst.Index) = .{}; + try origin_list.append(allocator, inst); + try emit.branch_forward_origins.put(allocator, target_inst, origin_list); + } + } + + // Remember the target instruction index so that we + // update the real code offset in all future passes + // + // putNoClobber may not be used as the put operation + // may clobber the entry when multiple branches branch + // to the same target instruction + try emit.code_offset_mapping.put(allocator, target_inst, 0); + } + } + + // Further passes: Until all branches are lowered, interate + // through all instructions and calculate new offsets and + // potentially new branch types + var all_branches_lowered = false; + while (!all_branches_lowered) { + all_branches_lowered = true; + var current_code_offset: usize = 0; + + for (mir_tags) |tag, index| { + const inst = @intCast(u32, index); + + // If this instruction contained in the code offset + // mapping (when it is a target of a branch or if it is a + // forward branch), update the code offset + if (emit.code_offset_mapping.getPtr(inst)) |offset| { + offset.* = current_code_offset; + } + + // If this instruction is a backward branch, calculate the + // offset, which may potentially update the branch type + if (isBranch(tag)) { + const target_inst = emit.branchTarget(inst); + if (target_inst < inst) { + const target_offset = emit.code_offset_mapping.get(target_inst).?; + const offset = @intCast(i64, target_offset) - @intCast(i64, current_code_offset); + const branch_type = emit.branch_types.getPtr(inst).?; + const optimal_branch_type = try emit.optimalBranchType(tag, offset); + if (branch_type.* != optimal_branch_type) { + branch_type.* = optimal_branch_type; + all_branches_lowered = false; + } + + log.debug("lowerBranches: branch {} has offset {}", .{ inst, offset }); + } + } + + // If this instruction is the target of one or more + // forward branches, calculate the offset, which may + // potentially update the branch type + if (emit.branch_forward_origins.get(inst)) |origin_list| { + for (origin_list.items) |forward_branch_inst| { + const branch_tag = emit.mir.instructions.items(.tag)[forward_branch_inst]; + const forward_branch_inst_offset = emit.code_offset_mapping.get(forward_branch_inst).?; + const offset = @intCast(i64, current_code_offset) - @intCast(i64, forward_branch_inst_offset); + const branch_type = emit.branch_types.getPtr(forward_branch_inst).?; + const optimal_branch_type = try emit.optimalBranchType(branch_tag, offset); + if (branch_type.* != optimal_branch_type) { + branch_type.* = optimal_branch_type; + all_branches_lowered = false; + } + + log.debug("lowerBranches: branch {} has offset {}", .{ forward_branch_inst, offset }); + } + } + + // Increment code offset + current_code_offset += emit.instructionSize(inst); + } + } +} + +fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType { + assert(offset & 0b11 == 0); + + switch (tag) { + .bpcc => { + if (std.math.cast(i21, offset)) |_| { + return BranchType.bpcc; + } else |_| { + // TODO use the following strategy to implement long branches: + // - Negate the conditional and target of the original BPcc; + // - In the space immediately after the branch, load + // the address of the original target, preferrably in + // a PC-relative way, into %o7; and + // - jmpl %o7 + %g0, %g0 + return emit.fail("TODO support BPcc branches larger than +-1 MiB", .{}); + } + }, + else => unreachable, + } +} + fn writeInstruction(emit: *Emit, instruction: Instruction) !void { // SPARCv9 instructions are always arranged in BE regardless of the // endianness mode the CPU is running in (Section 3.1 of the ISA specification). diff --git a/src/arch/sparc64/bits.zig b/src/arch/sparc64/bits.zig index d323288fac..b6d61900ce 100644 --- a/src/arch/sparc64/bits.zig +++ b/src/arch/sparc64/bits.zig @@ -1141,6 +1141,10 @@ pub const Instruction = union(enum) { }; } + pub fn bpcc(cond: ICondition, annul: bool, pt: bool, ccr: CCR, disp: i21) Instruction { + return format2c(0b001, .{ .icond = cond }, annul, pt, ccr, disp); + } + pub fn jmpl(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { return switch (s2) { Register => format3a(0b10, 0b11_1000, rs1, rs2, rd), From 3ab66343709147f17c6035fc298ffcc0088f0e1d Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 6 May 2022 22:10:41 +0700 Subject: [PATCH 1502/2031] stage2: sparc64: Implement airAlloc --- src/arch/sparc64/CodeGen.zig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index f10baf0f85..ff27ca003d 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -537,7 +537,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .xor => @panic("TODO try self.airXor(inst)"), .shr, .shr_exact => @panic("TODO try self.airShr(inst)"), - .alloc => @panic("TODO try self.airAlloc(inst)"), + .alloc => try self.airAlloc(inst), .ret_ptr => try self.airRetPtr(inst), .arg => try self.airArg(inst), .assembly => try self.airAsm(inst), @@ -671,6 +671,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { } } +fn airAlloc(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 airAsm(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const extra = self.air.extraData(Air.Asm, ty_pl.payload); From 0b54649cac53c5eebb573547bafbd4ec3a32443c Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 6 May 2022 22:14:32 +0700 Subject: [PATCH 1503/2031] stage2: sparc64: Implement error value generation --- src/arch/sparc64/CodeGen.zig | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index ff27ca003d..cd79969b25 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1808,6 +1808,34 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { return self.fail("TODO implement int genTypedValue of > 64 bits", .{}); } }, + .ErrorSet => { + const err_name = typed_value.val.castTag(.@"error").?.data.name; + const module = self.bin_file.options.module.?; + const global_error_set = module.global_error_set; + const error_index = global_error_set.get(err_name).?; + return MCValue{ .immediate = error_index }; + }, + .ErrorUnion => { + const error_type = typed_value.ty.errorUnionSet(); + const payload_type = typed_value.ty.errorUnionPayload(); + + if (typed_value.val.castTag(.eu_payload)) |pl| { + if (!payload_type.hasRuntimeBits()) { + // We use the error type directly as the type. + return MCValue{ .immediate = 0 }; + } + + _ = pl; + return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty.fmtDebug()}); + } else { + if (!payload_type.hasRuntimeBits()) { + // We use the error type directly as the type. + return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); + } + + return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty.fmtDebug()}); + } + }, .ComptimeInt => unreachable, // semantic analysis prevents this .ComptimeFloat => unreachable, // semantic analysis prevents this else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}), From 8f8853cd4ff48237770d9eeb4ea4f83979e77b5b Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 6 May 2022 22:58:21 +0700 Subject: [PATCH 1504/2031] stage2: sparc64: Implement airLoad/airStore --- src/arch/sparc64/CodeGen.zig | 212 ++++++++++++++++++++++++++++++++++- 1 file changed, 208 insertions(+), 4 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index cd79969b25..b9502f8935 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -563,7 +563,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .is_non_err_ptr => @panic("TODO try self.airIsNonErrPtr(inst)"), .is_err => try self.airIsErr(inst), .is_err_ptr => @panic("TODO try self.airIsErrPtr(inst)"), - .load => @panic("TODO try self.airLoad(inst)"), + .load => try self.airLoad(inst), .loop => @panic("TODO try self.airLoop(inst)"), .not => @panic("TODO try self.airNot(inst)"), .ptrtoint => @panic("TODO try self.airPtrToInt(inst)"), @@ -1242,6 +1242,36 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ un_op, .none, .none }); } +fn airLoad(self: *Self, inst: Air.Inst.Index) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const elem_ty = self.air.typeOfIndex(inst); + const elem_size = elem_ty.abiSize(self.target.*); + const result: MCValue = result: { + if (!elem_ty.hasRuntimeBits()) + break :result MCValue.none; + + const ptr = try self.resolveInst(ty_op.operand); + const is_volatile = self.air.typeOf(ty_op.operand).isVolatilePtr(); + if (self.liveness.isUnused(inst) and !is_volatile) + break :result MCValue.dead; + + const dst_mcv: MCValue = blk: { + if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) { + // The MCValue that holds the pointer can be re-used as the value. + break :blk switch (ptr) { + .register => |r| MCValue{ .register = r }, + else => ptr, + }; + } else { + break :blk try self.allocRegOrMem(inst, true); + } + }; + try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand)); + break :result dst_mcv; + }; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airRet(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); @@ -1263,10 +1293,15 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { } fn airStore(self: *Self, inst: Air.Inst.Index) !void { - _ = self; - _ = inst; + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const ptr = try self.resolveInst(bin_op.lhs); + const value = try self.resolveInst(bin_op.rhs); + const ptr_ty = self.air.typeOf(bin_op.lhs); + const value_ty = self.air.typeOf(bin_op.rhs); - return self.fail("TODO implement store for {}", .{self.target.cpu.arch}); + try self.store(ptr, value, ptr_ty, value_ty); + + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); } fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { @@ -1522,6 +1557,76 @@ fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32 } } +// TODO replace this to call to extern memcpy +fn genInlineMemcpy( + self: *Self, + src: Register, + dst: Register, + len: Register, + tmp: Register, +) !void { + // Here we assume that len > 0. + // Also we do the copy from end -> start address to save a register. + + // sub len, 1, len + _ = try self.addInst(.{ + .tag = .sub, + .data = .{ .arithmetic_3op = .{ + .is_imm = true, + .rs1 = len, + .rs2_or_imm = .{ .imm = 1 }, + .rd = len, + } }, + }); + + // loop: + // ldub [src + len], tmp + _ = try self.addInst(.{ + .tag = .ldub, + .data = .{ .arithmetic_3op = .{ + .is_imm = false, + .rs1 = src, + .rs2_or_imm = .{ .rs2 = len }, + .rd = tmp, + } }, + }); + + // stb tmp, [dst + len] + _ = try self.addInst(.{ + .tag = .stb, + .data = .{ .arithmetic_3op = .{ + .is_imm = false, + .rs1 = dst, + .rs2_or_imm = .{ .rs2 = len }, + .rd = tmp, + } }, + }); + + // brnz len, loop + _ = try self.addInst(.{ + .tag = .bpr, + .data = .{ .branch_predict_reg = .{ + .cond = .ne_zero, + .rs1 = len, + .inst = @intCast(u32, self.mir_instructions.len - 2), + } }, + }); + + // Delay slot: + // sub len, 1, len + _ = try self.addInst(.{ + .tag = .sub, + .data = .{ .arithmetic_3op = .{ + .is_imm = true, + .rs1 = len, + .rs2_or_imm = .{ .imm = 1 }, + .rd = len, + } }, + }); + + // end: +} + fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_type: type, off: off_type, abi_size: u64) !void { assert(off_type == Register or off_type == i13); @@ -1913,6 +2018,66 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT }; } +fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { + const elem_ty = ptr_ty.elemType(); + const elem_size = elem_ty.abiSize(self.target.*); + + switch (ptr) { + .none => unreachable, + .undef => unreachable, + .unreach => unreachable, + .dead => unreachable, + .compare_flags_unsigned, + .compare_flags_signed, + => unreachable, // cannot hold an address + .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), + .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), + .register => |addr_reg| { + self.register_manager.freezeRegs(&.{addr_reg}); + defer self.register_manager.unfreezeRegs(&.{addr_reg}); + + switch (dst_mcv) { + .dead => unreachable, + .undef => unreachable, + .compare_flags_signed, .compare_flags_unsigned => unreachable, + .register => |dst_reg| { + try self.genLoad(dst_reg, addr_reg, i13, 0, elem_size); + }, + .stack_offset => |off| { + if (elem_size <= 8) { + const tmp_reg = try self.register_manager.allocReg(null); + self.register_manager.freezeRegs(&.{tmp_reg}); + defer self.register_manager.unfreezeRegs(&.{tmp_reg}); + + try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); + try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); + } else { + const regs = try self.register_manager.allocRegs(3, .{ null, null, null }); + self.register_manager.freezeRegs(®s); + defer self.register_manager.unfreezeRegs(®s); + + const src_reg = addr_reg; + const dst_reg = regs[0]; + const len_reg = regs[1]; + const tmp_reg = regs[2]; + + try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = off }); + try self.genSetReg(Type.usize, len_reg, .{ .immediate = elem_size }); + try self.genInlineMemcpy(src_reg, dst_reg, len_reg, tmp_reg); + } + }, + else => return self.fail("TODO load from register into {}", .{dst_mcv}), + } + }, + .memory, + .stack_offset, + => { + const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr); + try self.load(dst_mcv, .{ .register = addr_reg }, ptr_ty); + }, + } +} + fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue { const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); @@ -2167,6 +2332,45 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); } +fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { + const abi_size = value_ty.abiSize(self.target.*); + + switch (ptr) { + .none => unreachable, + .undef => unreachable, + .unreach => unreachable, + .dead => unreachable, + .compare_flags_unsigned, + .compare_flags_signed, + => unreachable, // cannot hold an address + .immediate => |imm| { + try self.setRegOrMem(value_ty, .{ .memory = imm }, value); + }, + .ptr_stack_offset => |off| { + try self.genSetStack(value_ty, off, value); + }, + .register => |addr_reg| { + self.register_manager.freezeRegs(&.{addr_reg}); + defer self.register_manager.unfreezeRegs(&.{addr_reg}); + + switch (value) { + .register => |value_reg| { + try self.genStore(value_reg, addr_reg, i13, 0, abi_size); + }, + else => { + return self.fail("TODO implement copying of memory", .{}); + }, + } + }, + .memory, + .stack_offset, + => { + const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr); + try self.store(.{ .register = addr_reg }, value, ptr_ty, value_ty); + }, + } +} + /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`. fn wantSafety(self: *Self) bool { return switch (self.bin_file.options.optimize_mode) { From e057ff249648bdcd8cf2e9be62b40a2379de03e9 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 6 May 2022 23:16:07 +0700 Subject: [PATCH 1505/2031] stage2: sparc64: Implement SPARCv9 bpr --- src/arch/sparc64/Emit.zig | 63 ++++++++++++++++++++++++++++++++------- src/arch/sparc64/Mir.zig | 14 +++++++++ src/arch/sparc64/bits.zig | 4 +++ 3 files changed, 70 insertions(+), 11 deletions(-) diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index c7c6ac4904..738744fe90 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -51,9 +51,11 @@ const InnerError = error{ const BranchType = enum { bpcc, + bpr, fn default(tag: Mir.Inst.Tag) BranchType { return switch (tag) { .bpcc => .bpcc, + .bpr => .bpr, else => unreachable, }; } @@ -78,6 +80,7 @@ pub fn emitMir( .add => try emit.mirArithmetic3Op(inst), + .bpr => try emit.mirConditionalBranch(inst), .bpcc => try emit.mirConditionalBranch(inst), .call => @panic("TODO implement sparc64 call"), @@ -232,15 +235,43 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { fn mirConditionalBranch(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; - const branch_predict_int = emit.mir.instructions.items(.data)[inst].branch_predict_int; - - const offset = @intCast(i64, emit.code_offset_mapping.get(branch_predict_int.inst).?) - @intCast(i64, emit.code.items.len); const branch_type = emit.branch_types.get(inst).?; - log.debug("mirConditionalBranchImmediate: {} offset={}", .{ inst, offset }); switch (branch_type) { .bpcc => switch (tag) { - .bpcc => try emit.writeInstruction(Instruction.bpcc(branch_predict_int.cond, branch_predict_int.annul, branch_predict_int.pt, branch_predict_int.ccr, @intCast(i21, offset))), + .bpcc => { + const branch_predict_int = emit.mir.instructions.items(.data)[inst].branch_predict_int; + const offset = @intCast(i64, emit.code_offset_mapping.get(branch_predict_int.inst).?) - @intCast(i64, emit.code.items.len); + log.debug("mirConditionalBranch: {} offset={}", .{ inst, offset }); + + try emit.writeInstruction( + Instruction.bpcc( + branch_predict_int.cond, + branch_predict_int.annul, + branch_predict_int.pt, + branch_predict_int.ccr, + @intCast(i21, offset), + ), + ); + }, + else => unreachable, + }, + .bpr => switch (tag) { + .bpr => { + const branch_predict_reg = emit.mir.instructions.items(.data)[inst].branch_predict_reg; + const offset = @intCast(i64, emit.code_offset_mapping.get(branch_predict_reg.inst).?) - @intCast(i64, emit.code.items.len); + log.debug("mirConditionalBranch: {} offset={}", .{ inst, offset }); + + try emit.writeInstruction( + Instruction.bpr( + branch_predict_reg.cond, + branch_predict_reg.annul, + branch_predict_reg.pt, + branch_predict_reg.rs1, + @intCast(i18, offset), + ), + ); + }, else => unreachable, }, } @@ -291,6 +322,7 @@ fn branchTarget(emit: *Emit, inst: Mir.Inst.Index) Mir.Inst.Index { switch (tag) { .bpcc => return emit.mir.instructions.items(.data)[inst].branch_predict_int.inst, + .bpr => return emit.mir.instructions.items(.data)[inst].branch_predict_reg.inst, else => unreachable, } } @@ -343,6 +375,7 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize { fn isBranch(tag: Mir.Inst.Tag) bool { return switch (tag) { .bpcc => true, + .bpr => true, else => false, }; } @@ -459,19 +492,27 @@ fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType { assert(offset & 0b11 == 0); switch (tag) { + // TODO use the following strategy to implement long branches: + // - Negate the conditional and target of the original instruction; + // - In the space immediately after the branch, load + // the address of the original target, preferrably in + // a PC-relative way, into %o7; and + // - jmpl %o7 + %g0, %g0 + .bpcc => { if (std.math.cast(i21, offset)) |_| { return BranchType.bpcc; } else |_| { - // TODO use the following strategy to implement long branches: - // - Negate the conditional and target of the original BPcc; - // - In the space immediately after the branch, load - // the address of the original target, preferrably in - // a PC-relative way, into %o7; and - // - jmpl %o7 + %g0, %g0 return emit.fail("TODO support BPcc branches larger than +-1 MiB", .{}); } }, + .bpr => { + if (std.math.cast(i18, offset)) |_| { + return BranchType.bpr; + } else |_| { + return emit.fail("TODO support BPr branches larger than +-128 KiB", .{}); + } + }, else => unreachable, } } diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index f61f76bcba..d9309f1524 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -43,6 +43,10 @@ pub const Inst = struct { // TODO add other operations. add, + /// A.3 Branch on Integer Register with Prediction (BPr) + /// This uses the branch_predict_reg field. + bpr, + /// A.7 Branch on Integer Condition Codes with Prediction (BPcc) /// This uses the branch_predict_int field. bpcc, @@ -175,6 +179,16 @@ pub const Inst = struct { inst: Index, }, + /// Branch with prediction, comparing a register's content with zero + /// Used by e.g. bpr + branch_predict_reg: struct { + annul: bool = false, + pt: bool = true, + cond: Instruction.RCondition, + rs1: Register, + inst: Index, + }, + /// No additional data /// /// Used by e.g. flushw diff --git a/src/arch/sparc64/bits.zig b/src/arch/sparc64/bits.zig index b6d61900ce..cb7fe36810 100644 --- a/src/arch/sparc64/bits.zig +++ b/src/arch/sparc64/bits.zig @@ -1145,6 +1145,10 @@ pub const Instruction = union(enum) { return format2c(0b001, .{ .icond = cond }, annul, pt, ccr, disp); } + pub fn bpr(cond: RCondition, annul: bool, pt: bool, rs1: Register, disp: i18) Instruction { + return format2d(0b011, cond, annul, pt, rs1, disp); + } + pub fn jmpl(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { return switch (s2) { Register => format3a(0b10, 0b11_1000, rs1, rs2, rd), From ae2d6b7eea9c2dff7cfe6d38f5713d2f8cfed4e6 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 6 May 2022 23:20:02 +0700 Subject: [PATCH 1506/2031] stage2: sparc64: Add BPr support for airCondBr --- src/arch/sparc64/CodeGen.zig | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index b9502f8935..d09e65c3bf 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1031,7 +1031,23 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { }, }, }), - else => return self.fail("TODO branch on register content (BPr)", .{}), + else => blk: { + const reg = switch (cond) { + .register => |r| r, + else => try self.copyToTmpRegister(Type.bool, cond), + }; + + break :blk try self.addInst(.{ + .tag = .bpr, + .data = .{ + .branch_predict_reg = .{ + .cond = .eq_zero, + .rs1 = reg, + .inst = undefined, // populated later through performReloc + }, + }, + }); + }, }; // Regardless of the branch type that's emitted, we need to reserve From 0c8ce9ed9d916599e9ec518ecced941bf0b37eff Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 16 May 2022 23:00:49 +0700 Subject: [PATCH 1507/2031] stage2: sparc64: Implement airCmp --- src/arch/sparc64/CodeGen.zig | 229 ++++++++++++++++++++++++++++++++++- src/arch/sparc64/Mir.zig | 4 + 2 files changed, 227 insertions(+), 6 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index d09e65c3bf..b9aa20efdf 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -521,12 +521,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), - .cmp_lt => @panic("TODO try self.airCmp(inst, .lt)"), - .cmp_lte => @panic("TODO try self.airCmp(inst, .lte)"), - .cmp_eq => @panic("TODO try self.airCmp(inst, .eq)"), - .cmp_gte => @panic("TODO try self.airCmp(inst, .gte)"), - .cmp_gt => @panic("TODO try self.airCmp(inst, .gt)"), - .cmp_neq => @panic("TODO try self.airCmp(inst, .neq)"), + .cmp_lt => try self.airCmp(inst, .lt), + .cmp_lte => try self.airCmp(inst, .lte), + .cmp_eq => try self.airCmp(inst, .eq), + .cmp_gte => try self.airCmp(inst, .gte), + .cmp_gt => try self.airCmp(inst, .gt), + .cmp_neq => try self.airCmp(inst, .neq), .cmp_vector => @panic("TODO try self.airCmpVector(inst)"), .cmp_lt_errors_len => @panic("TODO try self.airCmpLtErrorsLen(inst)"), @@ -996,6 +996,54 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. @panic("TODO handle return value with BigTomb"); } +fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); + + var int_buffer: Type.Payload.Bits = undefined; + const int_ty = switch (lhs_ty.zigTypeTag()) { + .Vector => unreachable, // Should be handled by cmp_vector? + .Enum => lhs_ty.intTagType(&int_buffer), + .Int => lhs_ty, + .Bool => Type.initTag(.u1), + .Pointer => Type.usize, + .ErrorSet => Type.initTag(.u16), + .Optional => blk: { + var opt_buffer: Type.Payload.ElemType = undefined; + const payload_ty = lhs_ty.optionalChild(&opt_buffer); + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + break :blk Type.initTag(.u1); + } else if (lhs_ty.isPtrLikeOptional()) { + break :blk Type.usize; + } else { + return self.fail("TODO SPARCv9 cmp non-pointer optionals", .{}); + } + }, + .Float => return self.fail("TODO SPARCv9 cmp floats", .{}), + else => unreachable, + }; + + const int_info = int_ty.intInfo(self.target.*); + if (int_info.bits <= 64) { + _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty); + + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = inst; + + break :result switch (int_info.signedness) { + .signed => MCValue{ .compare_flags_signed = op }, + .unsigned => MCValue{ .compare_flags_unsigned = op }, + }; + } else { + return self.fail("TODO SPARCv9 cmp for ints > 64 bits", .{}); + } + }; + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +} + fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { const pl_op = self.air.instructions.items(.data)[inst].pl_op; const cond = try self.resolveInst(pl_op.operand); @@ -1427,6 +1475,149 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { return MCValue{ .stack_offset = stack_offset }; } +/// For all your binary operation needs, this function will generate +/// the corresponding Mir instruction(s). Returns the location of the +/// result. +/// +/// If the binary operation itself happens to be an Air instruction, +/// pass the corresponding index in the inst parameter. That helps +/// this function do stuff like reusing operands. +/// +/// This function does not do any lowering to Mir itself, but instead +/// looks at the lhs and rhs and determines which kind of lowering +/// would be best suitable and then delegates the lowering to other +/// functions. +fn binOp( + self: *Self, + tag: Air.Inst.Tag, + maybe_inst: ?Air.Inst.Index, + lhs: MCValue, + rhs: MCValue, + lhs_ty: Type, + rhs_ty: Type, +) InnerError!MCValue { + const mod = self.bin_file.options.module.?; + switch (tag) { + .cmp_eq => { + switch (lhs_ty.zigTypeTag()) { + .Float => return self.fail("TODO binary operations on floats", .{}), + .Vector => return self.fail("TODO binary operations on vectors", .{}), + .Int => { + assert(lhs_ty.eql(rhs_ty, mod)); + const int_info = lhs_ty.intInfo(self.target.*); + if (int_info.bits <= 64) { + // TODO optimize for small (i13) values by putting them inside immediates + + const mir_tag: Mir.Inst.Tag = switch (tag) { + .cmp_eq => .subcc, + else => unreachable, + }; + + return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + } else { + return self.fail("TODO binary operations on int with bits > 64", .{}); + } + }, + else => unreachable, + } + }, + + else => return self.fail("TODO implement {} binOp for SPARCv9", .{tag}), + } +} + +/// Don't call this function directly. Use binOp instead. +/// +/// Calling this function signals an intention to generate a Mir +/// instruction of the form +/// +/// op dest, lhs, rhs +/// +/// Asserts that generating an instruction of that form is possible. +fn binOpRegister( + self: *Self, + mir_tag: Mir.Inst.Tag, + maybe_inst: ?Air.Inst.Index, + lhs: MCValue, + rhs: MCValue, + lhs_ty: Type, + rhs_ty: Type, +) !MCValue { + const lhs_is_register = lhs == .register; + const rhs_is_register = rhs == .register; + + if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); + if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); + + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; + + const lhs_reg = if (lhs_is_register) lhs.register else blk: { + const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + break :inst Air.refToIndex(bin_op.lhs).?; + } else null; + + const reg = try self.register_manager.allocReg(track_inst); + self.register_manager.freezeRegs(&.{reg}); + + if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); + + break :blk reg; + }; + defer self.register_manager.unfreezeRegs(&.{lhs_reg}); + + const rhs_reg = if (rhs_is_register) rhs.register else blk: { + const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + break :inst Air.refToIndex(bin_op.rhs).?; + } else null; + + const reg = try self.register_manager.allocReg(track_inst); + self.register_manager.freezeRegs(&.{reg}); + + if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); + + break :blk reg; + }; + defer self.register_manager.unfreezeRegs(&.{rhs_reg}); + + const dest_reg = switch (mir_tag) { + else => if (maybe_inst) |inst| blk: { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + + if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { + break :blk lhs_reg; + } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { + break :blk rhs_reg; + } else { + break :blk try self.register_manager.allocReg(inst); + } + } else blk: { + break :blk try self.register_manager.allocReg(null); + }, + }; + + if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); + if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); + + const mir_data: Mir.Inst.Data = switch (mir_tag) { + .subcc => .{ .arithmetic_3op = .{ + .is_imm = false, + .rd = dest_reg, + .rs1 = lhs_reg, + .rs2_or_imm = .{ .rs2 = rhs_reg }, + } }, + else => unreachable, + }; + + _ = try self.addInst(.{ + .tag = mir_tag, + .data = mir_data, + }); + + return MCValue{ .register = dest_reg }; +} + fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { const block_data = self.blocks.getPtr(block).?; @@ -2146,6 +2337,9 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { .register => |reg| { self.register_manager.freeReg(reg); }, + .compare_flags_signed, .compare_flags_unsigned => { + self.compare_flags_inst = null; + }, else => {}, // TODO process stack allocation death } } @@ -2338,6 +2532,29 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { } } +/// Save the current instruction stored in the compare flags if +/// occupied +fn spillCompareFlagsIfOccupied(self: *Self) !void { + if (self.compare_flags_inst) |inst_to_save| { + const mcv = self.getResolvedInstValue(inst_to_save); + switch (mcv) { + .compare_flags_signed, + .compare_flags_unsigned, + => {}, + else => unreachable, // mcv doesn't occupy the compare flags + } + + const new_mcv = try self.allocRegOrMem(inst_to_save, true); + try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); + log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); + + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; + try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); + + self.compare_flags_inst = null; + } +} + pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { const stack_mcv = try self.allocRegOrMem(inst, false); log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index d9309f1524..683b09fa06 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -115,6 +115,10 @@ pub const Inst = struct { /// A.61 Trap on Integer Condition Codes (Tcc) /// This uses the trap field. tcc, + + // TODO add synthetic instructions + // TODO add cmp synthetic instruction to avoid wasting a register when + // comparing with subcc }; /// The position of an MIR instruction within the `Mir` instructions array. From e4a725c597ee4a461ac8135e952f4093183c6c80 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 16 May 2022 23:02:32 +0700 Subject: [PATCH 1508/2031] stage2: sparc64: Implement airBitCast --- src/arch/sparc64/CodeGen.zig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index b9aa20efdf..82a3679c8e 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -541,7 +541,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .ret_ptr => try self.airRetPtr(inst), .arg => try self.airArg(inst), .assembly => try self.airAsm(inst), - .bitcast => @panic("TODO try self.airBitCast(inst)"), + .bitcast => try self.airBitCast(inst), .block => try self.airBlock(inst), .br => try self.airBr(inst), .breakpoint => try self.airBreakpoint(), @@ -823,6 +823,12 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, mcv, .{ .none, .none, .none }); } +fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const result = try self.resolveInst(ty_op.operand); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airBlock(self: *Self, inst: Air.Inst.Index) !void { try self.blocks.putNoClobber(self.gpa, inst, .{ // A block is a setup to be able to jump to the end. From 8ea80fdf7a6ab963a9104b00c5f4364166734643 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 16 May 2022 23:03:50 +0700 Subject: [PATCH 1509/2031] stage2: sparc64: Implement airLoop --- src/arch/sparc64/CodeGen.zig | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 82a3679c8e..1087a6b732 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -564,7 +564,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .is_err => try self.airIsErr(inst), .is_err_ptr => @panic("TODO try self.airIsErrPtr(inst)"), .load => try self.airLoad(inst), - .loop => @panic("TODO try self.airLoop(inst)"), + .loop => try self.airLoop(inst), .not => @panic("TODO try self.airNot(inst)"), .ptrtoint => @panic("TODO try self.airPtrToInt(inst)"), .ret => try self.airRet(inst), @@ -1342,6 +1342,17 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airLoop(self: *Self, inst: Air.Inst.Index) !void { + // A loop is a setup to be able to jump back to the beginning. + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const loop = self.air.extraData(Air.Block, ty_pl.payload); + const body = self.air.extra[loop.end .. loop.end + loop.data.body_len]; + const start = @intCast(u32, self.mir_instructions.len); + try self.genBody(body); + try self.jump(start); + return self.finishAirBookkeeping(); +} + fn airRet(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); @@ -2231,6 +2242,26 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT }; } +/// Send control flow to `inst`. +fn jump(self: *Self, inst: Mir.Inst.Index) !void { + _ = try self.addInst(.{ + .tag = .bpcc, + .data = .{ + .branch_predict_int = .{ + .cond = .al, + .ccr = .xcc, + .inst = inst, + }, + }, + }); + + // TODO find out a way to fill this delay slot + _ = try self.addInst(.{ + .tag = .nop, + .data = .{ .nop = {} }, + }); +} + fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { const elem_ty = ptr_ty.elemType(); const elem_size = elem_ty.abiSize(self.target.*); From 26e3d36d74cb35b7de80f9b7e8499fd695dc6700 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 16 May 2022 23:05:10 +0700 Subject: [PATCH 1510/2031] stage2: sparc64: Implement airSliceLen --- src/arch/sparc64/CodeGen.zig | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 1087a6b732..63e9925e68 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -630,7 +630,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .switch_br => try self.airSwitch(inst), .slice_ptr => @panic("TODO try self.airSlicePtr(inst)"), - .slice_len => @panic("TODO try self.airSliceLen(inst)"), + .slice_len => try self.airSliceLen(inst), .ptr_slice_len_ptr => @panic("TODO try self.airPtrSliceLenPtr(inst)"), .ptr_slice_ptr_ptr => @panic("TODO try self.airPtrSlicePtrPtr(inst)"), @@ -1385,6 +1385,27 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airSliceLen(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 result: { + const ptr_bits = self.target.cpu.arch.ptrBitWidth(); + const ptr_bytes = @divExact(ptr_bits, 8); + const mcv = try self.resolveInst(ty_op.operand); + switch (mcv) { + .dead, .unreach, .none => unreachable, + .register => unreachable, // a slice doesn't fit in one register + .stack_offset => |off| { + break :result MCValue{ .stack_offset = off - ptr_bytes }; + }, + .memory => |addr| { + break :result MCValue{ .memory = addr + ptr_bytes }; + }, + else => return self.fail("TODO implement slice_len for {}", .{mcv}), + } + }; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { _ = self; _ = inst; From ccf438e4de75299a235f1f4f88a7de62779cc19a Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sat, 14 May 2022 13:17:44 +0700 Subject: [PATCH 1511/2031] stage2: sparc64: Replace freezeRegs with RegisterLock --- src/arch/sparc64/CodeGen.zig | 42 ++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 63e9925e68..cd445c5718 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -23,6 +23,7 @@ const FnResult = @import("../../codegen.zig").FnResult; const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; const RegisterManager = RegisterManagerFn(Self, Register, &abi.allocatable_regs); +const RegisterLock = RegisterManager.RegisterLock; const build_options = @import("build_options"); @@ -1584,8 +1585,17 @@ fn binOpRegister( const lhs_is_register = lhs == .register; const rhs_is_register = rhs == .register; - if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); - if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); + const lhs_lock: ?RegisterLock = if (lhs_is_register) + self.register_manager.lockReg(lhs.register) + else + null; + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); + + const rhs_lock: ?RegisterLock = if (rhs_is_register) + self.register_manager.lockReg(rhs.register) + else + null; + defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; @@ -1596,13 +1606,12 @@ fn binOpRegister( } else null; const reg = try self.register_manager.allocReg(track_inst); - self.register_manager.freezeRegs(&.{reg}); - if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); break :blk reg; }; - defer self.register_manager.unfreezeRegs(&.{lhs_reg}); + const new_lhs_lock = self.register_manager.lockReg(lhs_reg); + defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else blk: { const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { @@ -1611,13 +1620,12 @@ fn binOpRegister( } else null; const reg = try self.register_manager.allocReg(track_inst); - self.register_manager.freezeRegs(&.{reg}); - if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); break :blk reg; }; - defer self.register_manager.unfreezeRegs(&.{rhs_reg}); + const new_rhs_lock = self.register_manager.lockReg(rhs_reg); + defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); const dest_reg = switch (mir_tag) { else => if (maybe_inst) |inst| blk: { @@ -2298,8 +2306,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), .register => |addr_reg| { - self.register_manager.freezeRegs(&.{addr_reg}); - defer self.register_manager.unfreezeRegs(&.{addr_reg}); + const addr_reg_lock = self.register_manager.lockReg(addr_reg); + defer if (addr_reg_lock) |reg| self.register_manager.unlockReg(reg); switch (dst_mcv) { .dead => unreachable, @@ -2311,15 +2319,17 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .stack_offset => |off| { if (elem_size <= 8) { const tmp_reg = try self.register_manager.allocReg(null); - self.register_manager.freezeRegs(&.{tmp_reg}); - defer self.register_manager.unfreezeRegs(&.{tmp_reg}); + const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); + defer self.register_manager.unlockReg(tmp_reg_lock); try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); } else { const regs = try self.register_manager.allocRegs(3, .{ null, null, null }); - self.register_manager.freezeRegs(®s); - defer self.register_manager.unfreezeRegs(®s); + const regs_locks = self.register_manager.lockRegsAssumeUnused(3, regs); + defer for (regs_locks) |reg| { + self.register_manager.unlockReg(reg); + }; const src_reg = addr_reg; const dst_reg = regs[0]; @@ -2641,8 +2651,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type try self.genSetStack(value_ty, off, value); }, .register => |addr_reg| { - self.register_manager.freezeRegs(&.{addr_reg}); - defer self.register_manager.unfreezeRegs(&.{addr_reg}); + const addr_reg_lock = self.register_manager.lockReg(addr_reg); + defer if (addr_reg_lock) |reg| self.register_manager.unlockReg(reg); switch (value) { .register => |value_reg| { From 7822426ff2a7c23bf8090d6a1f77e54fca8d62c8 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sat, 14 May 2022 22:19:37 +0700 Subject: [PATCH 1512/2031] stage2: sparc64: Implement airSliceElemVal --- src/arch/sparc64/CodeGen.zig | 374 +++++++++++++++++++++++++++++++++-- src/arch/sparc64/Emit.zig | 7 + src/arch/sparc64/Mir.zig | 13 +- 3 files changed, 378 insertions(+), 16 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index cd445c5718..8780f4fbbe 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -30,6 +30,7 @@ const build_options = @import("build_options"); const bits = @import("bits.zig"); const abi = @import("abi.zig"); const Instruction = bits.Instruction; +const ShiftWidth = Instruction.ShiftWidth; const Register = bits.Register; const Self = @This(); @@ -637,7 +638,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .ptr_slice_ptr_ptr => @panic("TODO try self.airPtrSlicePtrPtr(inst)"), .array_elem_val => @panic("TODO try self.airArrayElemVal(inst)"), - .slice_elem_val => @panic("TODO try self.airSliceElemVal(inst)"), + .slice_elem_val => try self.airSliceElemVal(inst), .slice_elem_ptr => @panic("TODO try self.airSliceElemPtr(inst)"), .ptr_elem_val => @panic("TODO try self.airPtrElemVal(inst)"), .ptr_elem_ptr => @panic("TODO try self.airPtrElemPtr(inst)"), @@ -1374,16 +1375,48 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); } -fn airStore(self: *Self, inst: Air.Inst.Index) !void { +fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { + const is_volatile = false; // TODO const bin_op = self.air.instructions.items(.data)[inst].bin_op; - const ptr = try self.resolveInst(bin_op.lhs); - const value = try self.resolveInst(bin_op.rhs); - const ptr_ty = self.air.typeOf(bin_op.lhs); - const value_ty = self.air.typeOf(bin_op.rhs); - try self.store(ptr, value, ptr_ty, value_ty); + if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + const result: MCValue = result: { + const slice_mcv = try self.resolveInst(bin_op.lhs); + const index_mcv = try self.resolveInst(bin_op.rhs); - return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + const slice_ty = self.air.typeOf(bin_op.lhs); + const elem_ty = slice_ty.childType(); + const elem_size = elem_ty.abiSize(self.target.*); + + var buf: Type.SlicePtrFieldTypeBuffer = undefined; + const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); + + const index_lock: ?RegisterLock = if (index_mcv == .register) + self.register_manager.lockRegAssumeUnused(index_mcv.register) + else + null; + defer if (index_lock) |reg| self.register_manager.unlockReg(reg); + + const base_mcv: MCValue = switch (slice_mcv) { + .stack_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_offset = off }) }, + else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}), + }; + const base_lock = self.register_manager.lockRegAssumeUnused(base_mcv.register); + defer self.register_manager.unlockReg(base_lock); + + switch (elem_size) { + else => { + // TODO skip the ptr_add emission entirely and use native addressing modes + // i.e sllx/mulx then R+R or scale immediate then R+I + const dest = try self.allocRegOrMem(inst, true); + const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ptr_field_type, Type.usize); + try self.load(dest, addr, slice_ptr_field_type); + + break :result dest; + }, + } + }; + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { @@ -1407,6 +1440,18 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airStore(self: *Self, inst: Air.Inst.Index) !void { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const ptr = try self.resolveInst(bin_op.lhs); + const value = try self.resolveInst(bin_op.rhs); + const ptr_ty = self.air.typeOf(bin_op.lhs); + const value_ty = self.air.typeOf(bin_op.rhs); + + try self.store(ptr, value, ptr_ty, value_ty); + + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); +} + fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { _ = self; _ = inst; @@ -1561,10 +1606,226 @@ fn binOp( } }, + .mul => { + switch (lhs_ty.zigTypeTag()) { + .Vector => return self.fail("TODO binary operations on vectors", .{}), + .Int => { + assert(lhs_ty.eql(rhs_ty, mod)); + const int_info = lhs_ty.intInfo(self.target.*); + if (int_info.bits <= 64) { + // If LHS is immediate, then swap it with RHS. + const lhs_is_imm = lhs == .immediate; + const new_lhs = if (lhs_is_imm) rhs else lhs; + const new_rhs = if (lhs_is_imm) lhs else rhs; + const new_lhs_ty = if (lhs_is_imm) rhs_ty else lhs_ty; + const new_rhs_ty = if (lhs_is_imm) lhs_ty else rhs_ty; + + // At this point, RHS might be an immediate + // If it's a power of two immediate then we emit an shl instead + // TODO add similar checks for LHS + if (new_rhs == .immediate and math.isPowerOfTwo(new_rhs.immediate)) { + return try self.binOp(.shl, maybe_inst, new_lhs, .{ .immediate = math.log2(new_rhs.immediate) }, new_lhs_ty, Type.usize); + } + + return try self.binOpRegister(.mulx, maybe_inst, new_lhs, new_rhs, new_lhs_ty, new_rhs_ty); + } else { + return self.fail("TODO binary operations on int with bits > 64", .{}); + } + }, + else => unreachable, + } + }, + + .ptr_add => { + switch (lhs_ty.zigTypeTag()) { + .Pointer => { + const ptr_ty = lhs_ty; + const elem_ty = switch (ptr_ty.ptrSize()) { + .One => ptr_ty.childType().childType(), // ptr to array, so get array element type + else => ptr_ty.childType(), + }; + const elem_size = elem_ty.abiSize(self.target.*); + + if (elem_size == 1) { + const base_tag: Mir.Inst.Tag = switch (tag) { + .ptr_add => .add, + else => unreachable, + }; + + return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + } else { + // convert the offset into a byte offset by + // multiplying it with elem_size + + const offset = try self.binOp(.mul, null, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize); + const addr = try self.binOp(tag, null, lhs, offset, Type.initTag(.manyptr_u8), Type.usize); + return addr; + } + }, + else => unreachable, + } + }, + + .shl => { + const base_tag: Air.Inst.Tag = switch (tag) { + .shl => .shl_exact, + else => unreachable, + }; + + // Generate a shl_exact/shr_exact + const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + + // Truncate if necessary + switch (tag) { + .shl => switch (lhs_ty.zigTypeTag()) { + .Vector => return self.fail("TODO binary operations on vectors", .{}), + .Int => { + const int_info = lhs_ty.intInfo(self.target.*); + if (int_info.bits <= 64) { + const result_reg = result.register; + try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); + return result; + } else { + return self.fail("TODO binary operations on integers > u64/i64", .{}); + } + }, + else => unreachable, + }, + else => unreachable, + } + }, + + .shl_exact => { + switch (lhs_ty.zigTypeTag()) { + .Vector => return self.fail("TODO binary operations on vectors", .{}), + .Int => { + const int_info = lhs_ty.intInfo(self.target.*); + if (int_info.bits <= 64) { + const rhs_immediate_ok = rhs == .immediate; + + const mir_tag: Mir.Inst.Tag = switch (tag) { + .shl_exact => .sllx, + else => unreachable, + }; + + if (rhs_immediate_ok) { + return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); + } else { + return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + } + } else { + return self.fail("TODO binary operations on int with bits > 64", .{}); + } + }, + else => unreachable, + } + }, + else => return self.fail("TODO implement {} binOp for SPARCv9", .{tag}), } } +/// Don't call this function directly. Use binOp instead. +/// +/// Calling this function signals an intention to generate a Mir +/// instruction of the form +/// +/// op dest, lhs, #rhs_imm +/// +/// Set lhs_and_rhs_swapped to true iff inst.bin_op.lhs corresponds to +/// rhs and vice versa. This parameter is only used when maybe_inst != +/// null. +/// +/// Asserts that generating an instruction of that form is possible. +fn binOpImmediate( + self: *Self, + mir_tag: Mir.Inst.Tag, + maybe_inst: ?Air.Inst.Index, + lhs: MCValue, + rhs: MCValue, + lhs_ty: Type, + lhs_and_rhs_swapped: bool, +) !MCValue { + const lhs_is_register = lhs == .register; + + const lhs_lock: ?RegisterLock = if (lhs_is_register) + self.register_manager.lockReg(lhs.register) + else + null; + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); + + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; + + const lhs_reg = if (lhs_is_register) lhs.register else blk: { + const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + break :inst Air.refToIndex( + if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, + ).?; + } else null; + + const reg = try self.register_manager.allocReg(track_inst); + + if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); + + break :blk reg; + }; + const new_lhs_lock = self.register_manager.lockReg(lhs_reg); + defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); + + const dest_reg = switch (mir_tag) { + else => if (maybe_inst) |inst| blk: { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + + if (lhs_is_register and self.reuseOperand( + inst, + if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, + if (lhs_and_rhs_swapped) 1 else 0, + lhs, + )) { + break :blk lhs_reg; + } else { + break :blk try self.register_manager.allocReg(inst); + } + } else blk: { + break :blk try self.register_manager.allocReg(null); + }, + }; + + if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); + + const mir_data: Mir.Inst.Data = switch (mir_tag) { + .add, + .mulx, + .subcc, + => .{ + .arithmetic_3op = .{ + .is_imm = true, + .rd = dest_reg, + .rs1 = lhs_reg, + .rs2_or_imm = .{ .imm = @intCast(i13, rhs.immediate) }, + }, + }, + .sllx => .{ + .shift = .{ + .is_imm = true, + .width = ShiftWidth.shift64, + .rd = dest_reg, + .rs1 = lhs_reg, + .rs2_or_imm = .{ .imm = @intCast(u6, rhs.immediate) }, + }, + }, + else => unreachable, + }; + + _ = try self.addInst(.{ + .tag = mir_tag, + .data = mir_data, + }); + + return MCValue{ .register = dest_reg }; +} + /// Don't call this function directly. Use binOp instead. /// /// Calling this function signals an intention to generate a Mir @@ -1647,12 +1908,26 @@ fn binOpRegister( if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); const mir_data: Mir.Inst.Data = switch (mir_tag) { - .subcc => .{ .arithmetic_3op = .{ - .is_imm = false, - .rd = dest_reg, - .rs1 = lhs_reg, - .rs2_or_imm = .{ .rs2 = rhs_reg }, - } }, + .add, + .mulx, + .subcc, + => .{ + .arithmetic_3op = .{ + .is_imm = false, + .rd = dest_reg, + .rs1 = lhs_reg, + .rs2_or_imm = .{ .rs2 = rhs_reg }, + }, + }, + .sllx => .{ + .shift = .{ + .is_imm = false, + .width = ShiftWidth.shift64, + .rd = dest_reg, + .rs1 = lhs_reg, + .rs2_or_imm = .{ .rs2 = rhs_reg }, + }, + }, else => unreachable, }; @@ -2672,6 +2947,77 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type } } +fn truncRegister( + self: *Self, + operand_reg: Register, + dest_reg: Register, + int_signedness: std.builtin.Signedness, + int_bits: u16, +) !void { + switch (int_bits) { + 1...31, 33...63 => { + _ = try self.addInst(.{ + .tag = .sllx, + .data = .{ + .shift = .{ + .is_imm = true, + .width = ShiftWidth.shift64, + .rd = dest_reg, + .rs1 = operand_reg, + .rs2_or_imm = .{ .imm = @intCast(u6, 64 - int_bits) }, + }, + }, + }); + _ = try self.addInst(.{ + .tag = switch (int_signedness) { + .signed => .srax, + .unsigned => .srlx, + }, + .data = .{ + .shift = .{ + .is_imm = true, + .width = ShiftWidth.shift32, + .rd = dest_reg, + .rs1 = dest_reg, + .rs2_or_imm = .{ .imm = @intCast(u6, int_bits) }, + }, + }, + }); + }, + 32 => { + _ = try self.addInst(.{ + .tag = switch (int_signedness) { + .signed => .sra, + .unsigned => .srl, + }, + .data = .{ + .shift = .{ + .is_imm = true, + .width = ShiftWidth.shift32, + .rd = dest_reg, + .rs1 = operand_reg, + .rs2_or_imm = .{ .imm = 0 }, + }, + }, + }); + }, + 64 => { + _ = try self.addInst(.{ + .tag = .@"or", + .data = .{ + .arithmetic_3op = .{ + .is_imm = true, + .rd = dest_reg, + .rs1 = .g0, + .rs2_or_imm = .{ .rs2 = operand_reg }, + }, + }, + }); + }, + else => unreachable, + } +} + /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`. fn wantSafety(self: *Self) bool { return switch (self.bin_file.options.optimize_mode) { diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 738744fe90..6f6a6d2327 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -94,6 +94,8 @@ pub fn emitMir( .@"or" => try emit.mirArithmetic3Op(inst), + .mulx => @panic("TODO implement sparc64 mulx"), + .nop => try emit.mirNop(), .@"return" => try emit.mirArithmetic2Op(inst), @@ -103,7 +105,12 @@ pub fn emitMir( .sethi => try emit.mirSethi(inst), + .sll => @panic("TODO implement sparc64 sll"), + .srl => @panic("TODO implement sparc64 srl"), + .sra => @panic("TODO implement sparc64 sra"), .sllx => @panic("TODO implement sparc64 sllx"), + .srlx => @panic("TODO implement sparc64 srlx"), + .srax => @panic("TODO implement sparc64 srax"), .stb => try emit.mirArithmetic3Op(inst), .sth => try emit.mirArithmetic3Op(inst), diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index 683b09fa06..441e151cea 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -74,6 +74,11 @@ pub const Inst = struct { // TODO add other operations. @"or", + /// A.37 Multiply and Divide (64-bit) + /// This uses the arithmetic_3op field. + // TODO add other operations. + mulx, + /// A.40 No Operation /// This uses the nop field. nop, @@ -93,8 +98,12 @@ pub const Inst = struct { /// A.49 Shift /// This uses the shift field. - // TODO add other operations. + sll, + srl, + sra, sllx, + srlx, + srax, /// A.54 Store Integer /// This uses the arithmetic_3op field. @@ -210,7 +219,7 @@ pub const Inst = struct { /// if is_imm true then it uses the imm field of rs2_or_imm, /// otherwise it uses rs2 field. /// - /// Used by e.g. add, sub + /// Used by e.g. sllx shift: struct { is_imm: bool, width: Instruction.ShiftWidth, From 26116211eccddbe47afde4cbe0795c257cc882b8 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sat, 14 May 2022 22:43:46 +0700 Subject: [PATCH 1513/2031] stage2: sparc64: Implement inline memcpy for genSetStack --- src/arch/sparc64/CodeGen.zig | 42 +++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 8780f4fbbe..f301f7412a 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -2375,7 +2375,47 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro return self.fail("TODO larger stack offsets", .{}); return self.genStore(reg, .sp, i13, simm13, abi_size); }, - .memory, .stack_offset => return self.fail("TODO implement memcpy", .{}), + .memory, .stack_offset => { + switch (mcv) { + .stack_offset => |off| { + if (stack_offset == off) + return; // Copy stack variable to itself; nothing to do. + }, + else => {}, + } + + if (abi_size <= 8) { + const reg = try self.copyToTmpRegister(ty, mcv); + return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); + } else { + var ptr_ty_payload: Type.Payload.ElemType = .{ + .base = .{ .tag = .single_mut_pointer }, + .data = ty, + }; + const ptr_ty = Type.initPayload(&ptr_ty_payload.base); + + const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }); + const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs); + defer for (regs_locks) |reg| { + self.register_manager.unlockReg(reg); + }; + + const src_reg = regs[0]; + const dst_reg = regs[1]; + const len_reg = regs[2]; + const tmp_reg = regs[3]; + + switch (mcv) { + .stack_offset => |off| try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }), + .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = addr }), + else => unreachable, + } + + try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = stack_offset }); + try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size }); + try self.genInlineMemcpy(src_reg, dst_reg, len_reg, tmp_reg); + } + }, } } From 77eef33c044d08096d828a3178ff6166a1502c26 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 15 May 2022 10:20:35 +0700 Subject: [PATCH 1514/2031] stage2: sparc64: Implement airStructFieldPtrIndex --- src/arch/sparc64/CodeGen.zig | 50 +++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index f301f7412a..8ad135609b 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -623,10 +623,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .atomic_store_release => @panic("TODO try self.airAtomicStore(inst, .Release)"), .atomic_store_seq_cst => @panic("TODO try self.airAtomicStore(inst, .SeqCst)"), - .struct_field_ptr_index_0 => @panic("TODO try self.airStructFieldPtrIndex(inst, 0)"), - .struct_field_ptr_index_1 => @panic("TODO try self.airStructFieldPtrIndex(inst, 1)"), - .struct_field_ptr_index_2 => @panic("TODO try self.airStructFieldPtrIndex(inst, 2)"), - .struct_field_ptr_index_3 => @panic("TODO try self.airStructFieldPtrIndex(inst, 3)"), + .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), + .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), + .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), + .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), .field_parent_ptr => @panic("TODO try self.airFieldParentPtr(inst)"), @@ -1452,6 +1452,12 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const result = try self.structFieldPtr(inst, ty_op.operand, index); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { _ = self; _ = inst; @@ -2987,6 +2993,42 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type } } +fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue { + return if (self.liveness.isUnused(inst)) .dead else result: { + const mcv = try self.resolveInst(operand); + const ptr_ty = self.air.typeOf(operand); + const struct_ty = ptr_ty.childType(); + const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); + switch (mcv) { + .ptr_stack_offset => |off| { + break :result MCValue{ .ptr_stack_offset = off - struct_field_offset }; + }, + else => { + const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ + .immediate = struct_field_offset, + }); + const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); + defer self.register_manager.unlockReg(offset_reg_lock); + + const addr_reg = try self.copyToTmpRegister(ptr_ty, mcv); + const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); + defer self.register_manager.unlockReg(addr_reg_lock); + + const dest = try self.binOp( + .add, + null, + .{ .register = addr_reg }, + .{ .register = offset_reg }, + Type.usize, + Type.usize, + ); + + break :result dest; + }, + } + }; +} + fn truncRegister( self: *Self, operand_reg: Register, From 67a1fedf8443a16da6a8e0f815ea42c2b9acff0a Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 15 May 2022 10:28:22 +0700 Subject: [PATCH 1515/2031] stage2: sparc64: Implement airUnwrapErrErr --- src/arch/sparc64/CodeGen.zig | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 8ad135609b..79636a2824 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -650,7 +650,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .optional_payload => @panic("TODO try self.airOptionalPayload(inst)"), .optional_payload_ptr => @panic("TODO try self.airOptionalPayloadPtr(inst)"), .optional_payload_ptr_set => @panic("TODO try self.airOptionalPayloadPtrSet(inst)"), - .unwrap_errunion_err => @panic("TODO try self.airUnwrapErrErr(inst)"), + .unwrap_errunion_err => try self.airUnwrapErrErr(inst), .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst), .unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"), .unwrap_errunion_payload_ptr=> @panic("TODO try self.airUnwrapErrPayloadPtr(inst)"), @@ -1465,6 +1465,19 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement switch for {}", .{self.target.cpu.arch}); } +fn airUnwrapErrErr(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 result: { + const error_union_ty = self.air.typeOf(ty_op.operand); + const payload_ty = error_union_ty.errorUnionPayload(); + const mcv = try self.resolveInst(ty_op.operand); + if (!payload_ty.hasRuntimeBits()) break :result mcv; + + return self.fail("TODO implement unwrap error union error for non-empty payloads", .{}); + }; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airUnwrapErrPayload(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 result: { From 7245aad6895c39fee1f7897add9e18807e880213 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 15 May 2022 10:39:56 +0700 Subject: [PATCH 1516/2031] stage2: sparc64: Implement airBinOp for addition --- src/arch/sparc64/CodeGen.zig | 46 ++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 79636a2824..18583d1667 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -483,7 +483,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off - .add, .ptr_add => @panic("TODO try self.airBinOp(inst)"), + .add, .ptr_add => try self.airBinOp(inst), .addwrap => @panic("TODO try self.airAddWrap(inst)"), .add_sat => @panic("TODO try self.airAddSat(inst)"), .sub, .ptr_sub => @panic("TODO try self.airBinOp(inst)"), @@ -825,6 +825,21 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, mcv, .{ .none, .none, .none }); } +fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { + const tag = self.air.instructions.items(.tag)[inst]; + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +} + fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const result = try self.resolveInst(ty_op.operand); @@ -1601,7 +1616,7 @@ fn binOp( ) InnerError!MCValue { const mod = self.bin_file.options.module.?; switch (tag) { - .cmp_eq => { + .add, .cmp_eq => { switch (lhs_ty.zigTypeTag()) { .Float => return self.fail("TODO binary operations on floats", .{}), .Vector => return self.fail("TODO binary operations on vectors", .{}), @@ -1609,14 +1624,37 @@ fn binOp( assert(lhs_ty.eql(rhs_ty, mod)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 64) { - // TODO optimize for small (i13) values by putting them inside immediates + // Only say yes if the operation is + // commutative, i.e. we can swap both of the + // operands + const lhs_immediate_ok = switch (tag) { + .add => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), + .sub, .cmp_eq => false, + else => unreachable, + }; + const rhs_immediate_ok = switch (tag) { + .add, + .sub, + .cmp_eq, + => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), + else => unreachable, + }; const mir_tag: Mir.Inst.Tag = switch (tag) { + .add => .add, .cmp_eq => .subcc, else => unreachable, }; - return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + if (rhs_immediate_ok) { + return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); + } else if (lhs_immediate_ok) { + // swap lhs and rhs + return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true); + } else { + // TODO convert large immediates to register before adding + return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + } } else { return self.fail("TODO binary operations on int with bits > 64", .{}); } From b618dbdf6989235242ef35f9b676848794fc2229 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 15 May 2022 22:10:27 +0700 Subject: [PATCH 1517/2031] stage2: sparc64: Implement SPARCv9 mulx --- src/arch/sparc64/Emit.zig | 4 +++- src/arch/sparc64/bits.zig | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 6f6a6d2327..6f30f785c5 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -94,7 +94,7 @@ pub fn emitMir( .@"or" => try emit.mirArithmetic3Op(inst), - .mulx => @panic("TODO implement sparc64 mulx"), + .mulx => try emit.mirArithmetic3Op(inst), .nop => try emit.mirNop(), @@ -207,6 +207,7 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { .lduw => try emit.writeInstruction(Instruction.lduw(i13, rs1, imm, rd)), .ldx => try emit.writeInstruction(Instruction.ldx(i13, rs1, imm, rd)), .@"or" => try emit.writeInstruction(Instruction.@"or"(i13, rs1, imm, rd)), + .mulx => try emit.writeInstruction(Instruction.mulx(i13, rs1, imm, rd)), .save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)), .restore => try emit.writeInstruction(Instruction.restore(i13, rs1, imm, rd)), .stb => try emit.writeInstruction(Instruction.stb(i13, rs1, imm, rd)), @@ -227,6 +228,7 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { .lduw => try emit.writeInstruction(Instruction.lduw(Register, rs1, rs2, rd)), .ldx => try emit.writeInstruction(Instruction.ldx(Register, rs1, rs2, rd)), .@"or" => try emit.writeInstruction(Instruction.@"or"(Register, rs1, rs2, rd)), + .mulx => try emit.writeInstruction(Instruction.mulx(Register, rs1, rs2, rd)), .save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)), .restore => try emit.writeInstruction(Instruction.restore(Register, rs1, rs2, rd)), .stb => try emit.writeInstruction(Instruction.stb(Register, rs1, rs2, rd)), diff --git a/src/arch/sparc64/bits.zig b/src/arch/sparc64/bits.zig index cb7fe36810..f4226b49da 100644 --- a/src/arch/sparc64/bits.zig +++ b/src/arch/sparc64/bits.zig @@ -1197,6 +1197,14 @@ pub const Instruction = union(enum) { }; } + pub fn mulx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b10, 0b00_1001, rs1, rs2, rd), + i13 => format3b(0b10, 0b00_1001, rs1, rs2, rd), + else => unreachable, + }; + } + pub fn nop() Instruction { return sethi(0, .g0); } From c641fb8f05cfbca7484a09496f33bc9c2d95941e Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 26 Mar 2022 15:40:31 -0700 Subject: [PATCH 1518/2031] stage2: fix {add,sub,mul}_with_overflow vectorization in LLVM backend --- src/codegen/llvm.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 5a8516b363..3e15bd8d9a 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5604,8 +5604,9 @@ pub const FuncGen = struct { const rhs = try self.resolveInst(extra.rhs); const lhs_ty = self.air.typeOf(extra.lhs); + const scalar_ty = lhs_ty.scalarType(); - const intrinsic_name = if (lhs_ty.isSignedInt()) signed_intrinsic else unsigned_intrinsic; + const intrinsic_name = if (scalar_ty.isSignedInt()) signed_intrinsic else unsigned_intrinsic; const llvm_lhs_ty = try self.dg.llvmType(lhs_ty); From c2cb9b7cade597bc967620174b31db3895681f96 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 26 Mar 2022 15:41:44 -0700 Subject: [PATCH 1519/2031] stage2: vectorize shl_with_overflow in LLVM backend --- src/codegen/llvm.zig | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 3e15bd8d9a..3abdccfbe2 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5899,26 +5899,30 @@ pub const FuncGen = struct { const lhs_ty = self.air.typeOf(extra.lhs); const rhs_ty = self.air.typeOf(extra.rhs); + const lhs_scalar_ty = lhs_ty.scalarType(); + const rhs_scalar_ty = rhs_ty.scalarType(); + const dest_ty = self.air.typeOfIndex(inst); const llvm_dest_ty = try self.dg.llvmType(dest_ty); const tg = self.dg.module.getTarget(); - const casted_rhs = if (rhs_ty.bitSize(tg) < lhs_ty.bitSize(tg)) + const casted_rhs = if (rhs_scalar_ty.bitSize(tg) < lhs_scalar_ty.bitSize(tg)) self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_ty), "") else rhs; const result = self.builder.buildShl(lhs, casted_rhs, ""); - const reconstructed = if (lhs_ty.isSignedInt()) + const reconstructed = if (lhs_scalar_ty.isSignedInt()) self.builder.buildAShr(result, casted_rhs, "") else self.builder.buildLShr(result, casted_rhs, ""); const overflow_bit = self.builder.buildICmp(.NE, lhs, reconstructed, ""); - const partial = self.builder.buildInsertValue(llvm_dest_ty.getUndef(), result, 0, ""); - return self.builder.buildInsertValue(partial, overflow_bit, 1, ""); + var ty_buf: Type.Payload.Pointer = undefined; + const partial = self.builder.buildInsertValue(llvm_dest_ty.getUndef(), result, llvmFieldIndex(dest_ty, 0, tg, &ty_buf).?, ""); + return self.builder.buildInsertValue(partial, overflow_bit, llvmFieldIndex(dest_ty, 1, tg, &ty_buf).?, ""); } fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { From ca1ab38d3a037239fc1399c2f9d5b2967acb6757 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 26 Mar 2022 15:43:47 -0700 Subject: [PATCH 1520/2031] stage2: add global `Type` constant for `u1` --- src/type.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/type.zig b/src/type.zig index 2c3ce0d900..54b7d44a3d 100644 --- a/src/type.zig +++ b/src/type.zig @@ -5999,6 +5999,7 @@ pub const Type = extern union { }; }; + pub const @"u1" = initTag(.u1); pub const @"u8" = initTag(.u8); pub const @"u16" = initTag(.u16); pub const @"u32" = initTag(.u32); From 6b5c87957b37df89d2ea53f1b39a7e4be0bf1326 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 26 Mar 2022 15:45:30 -0700 Subject: [PATCH 1521/2031] stage2: handle vectors in `Value.intFitsInType` --- src/value.zig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/value.zig b/src/value.zig index 1e70ad0c54..2ebabe3a27 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1671,6 +1671,7 @@ pub const Value = extern union { } /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. + /// Vectors are also accepted. Vector results are reduced with AND. pub fn intFitsInType(self: Value, ty: Type, target: Target) bool { switch (self.tag()) { .zero, @@ -1767,6 +1768,16 @@ pub const Value = extern union { else => unreachable, }, + .aggregate => { + assert(ty.zigTypeTag() == .Vector); + for (self.castTag(.aggregate).?.data) |elem| { + if (!elem.intFitsInType(ty.scalarType(), target)) { + return false; + } + } + return true; + }, + else => unreachable, } } From e8117bab6f786348142a72b5279380a937c3a151 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 26 Mar 2022 15:47:35 -0700 Subject: [PATCH 1522/2031] stage2: clean up creation of boolean `Value`s --- src/value.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/value.zig b/src/value.zig index 2ebabe3a27..ffc15dfa19 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2026,7 +2026,7 @@ pub const Value = extern union { const result_data = try allocator.alloc(Value, ty.vectorLen()); for (result_data) |*scalar, i| { const res_bool = compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType(), mod); - scalar.* = if (res_bool) Value.@"true" else Value.@"false"; + scalar.* = makeBool(res_bool); } return Value.Tag.aggregate.create(allocator, result_data); } From 86a928ce61ae7df52d3d54fa5c653195a7a4cbef Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 26 Mar 2022 15:50:55 -0700 Subject: [PATCH 1523/2031] stage2: perform comptime vectorization of `*_with_overflow` in `Value` --- src/value.zig | 103 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 5 deletions(-) diff --git a/src/value.zig b/src/value.zig index ffc15dfa19..b7764327c0 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2961,7 +2961,8 @@ pub const Value = extern union { } pub const OverflowArithmeticResult = struct { - overflowed: bool, + /// TODO: Rename to `overflow_bit` and make of type `u1`. + overflowed: Value, wrapped_result: Value, }; @@ -2971,6 +2972,29 @@ pub const Value = extern union { ty: Type, arena: Allocator, target: Target, + ) !OverflowArithmeticResult { + if (ty.zigTypeTag() == .Vector) { + const overflowed_data = try arena.alloc(Value, ty.vectorLen()); + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + const of_math_result = try intAddWithOverflowScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + overflowed_data[i] = of_math_result.overflowed; + scalar.* = of_math_result.wrapped_result; + } + return OverflowArithmeticResult{ + .overflowed = try Value.Tag.aggregate.create(arena, overflowed_data), + .wrapped_result = try Value.Tag.aggregate.create(arena, result_data), + }; + } + return intAddWithOverflowScalar(lhs, rhs, ty, arena, target); + } + + pub fn intAddWithOverflowScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, ) !OverflowArithmeticResult { const info = ty.intInfo(target); @@ -2986,7 +3010,7 @@ pub const Value = extern union { const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); const result = try fromBigInt(arena, result_bigint.toConst()); return OverflowArithmeticResult{ - .overflowed = overflowed, + .overflowed = makeBool(overflowed), .wrapped_result = result, }; } @@ -3097,6 +3121,29 @@ pub const Value = extern union { ty: Type, arena: Allocator, target: Target, + ) !OverflowArithmeticResult { + if (ty.zigTypeTag() == .Vector) { + const overflowed_data = try arena.alloc(Value, ty.vectorLen()); + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + const of_math_result = try intSubWithOverflowScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + overflowed_data[i] = of_math_result.overflowed; + scalar.* = of_math_result.wrapped_result; + } + return OverflowArithmeticResult{ + .overflowed = try Value.Tag.aggregate.create(arena, overflowed_data), + .wrapped_result = try Value.Tag.aggregate.create(arena, result_data), + }; + } + return intSubWithOverflowScalar(lhs, rhs, ty, arena, target); + } + + pub fn intSubWithOverflowScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, ) !OverflowArithmeticResult { const info = ty.intInfo(target); @@ -3112,7 +3159,7 @@ pub const Value = extern union { const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); const wrapped_result = try fromBigInt(arena, result_bigint.toConst()); return OverflowArithmeticResult{ - .overflowed = overflowed, + .overflowed = makeBool(overflowed), .wrapped_result = wrapped_result, }; } @@ -3207,6 +3254,29 @@ pub const Value = extern union { ty: Type, arena: Allocator, target: Target, + ) !OverflowArithmeticResult { + if (ty.zigTypeTag() == .Vector) { + const overflowed_data = try arena.alloc(Value, ty.vectorLen()); + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + const of_math_result = try intMulWithOverflowScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + overflowed_data[i] = of_math_result.overflowed; + scalar.* = of_math_result.wrapped_result; + } + return OverflowArithmeticResult{ + .overflowed = try Value.Tag.aggregate.create(arena, overflowed_data), + .wrapped_result = try Value.Tag.aggregate.create(arena, result_data), + }; + } + return intMulWithOverflowScalar(lhs, rhs, ty, arena, target); + } + + pub fn intMulWithOverflowScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, ) !OverflowArithmeticResult { const info = ty.intInfo(target); @@ -3231,7 +3301,7 @@ pub const Value = extern union { } return OverflowArithmeticResult{ - .overflowed = overflowed, + .overflowed = makeBool(overflowed), .wrapped_result = try fromBigInt(arena, result_bigint.toConst()), }; } @@ -3921,6 +3991,29 @@ pub const Value = extern union { ty: Type, allocator: Allocator, target: Target, + ) !OverflowArithmeticResult { + if (ty.zigTypeTag() == .Vector) { + const overflowed_data = try allocator.alloc(Value, ty.vectorLen()); + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + const of_math_result = try shlWithOverflowScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), allocator, target); + overflowed_data[i] = of_math_result.overflowed; + scalar.* = of_math_result.wrapped_result; + } + return OverflowArithmeticResult{ + .overflowed = try Value.Tag.aggregate.create(allocator, overflowed_data), + .wrapped_result = try Value.Tag.aggregate.create(allocator, result_data), + }; + } + return shlWithOverflowScalar(lhs, rhs, ty, allocator, target); + } + + pub fn shlWithOverflowScalar( + lhs: Value, + rhs: Value, + ty: Type, + allocator: Allocator, + target: Target, ) !OverflowArithmeticResult { const info = ty.intInfo(target); var lhs_space: Value.BigIntSpace = undefined; @@ -3941,7 +4034,7 @@ pub const Value = extern union { result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits); } return OverflowArithmeticResult{ - .overflowed = overflowed, + .overflowed = makeBool(overflowed), .wrapped_result = try fromBigInt(allocator, result_bigint.toConst()), }; } From eb06c78a8ac472b3406075dcbddf7fce63e98597 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 26 Mar 2022 15:55:33 -0700 Subject: [PATCH 1524/2031] Sema: vectorize overflow arithmetic --- src/Sema.zig | 118 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 73 insertions(+), 45 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 2cc9b82410..5c37b1a0f4 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9417,32 +9417,29 @@ fn zirOverflowArithmetic( const ptr = sema.resolveInst(extra.ptr); const lhs_ty = sema.typeOf(lhs); + const rhs_ty = sema.typeOf(rhs); const mod = sema.mod; const target = mod.getTarget(); // Note, the types of lhs/rhs (also for shifting)/ptr are already correct as ensured by astgen. + try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); const dest_ty = lhs_ty; - if (dest_ty.zigTypeTag() != .Int) { - return sema.fail(block, src, "expected integer type, found '{}'", .{dest_ty.fmt(mod)}); + if (dest_ty.scalarType().zigTypeTag() != .Int) { + return sema.fail(block, src, "expected vector of integers or integer type, found '{}'", .{dest_ty.fmt(mod)}); } const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs); const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs); - const types = try sema.arena.alloc(Type, 2); - const values = try sema.arena.alloc(Value, 2); - const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{ - .types = types, - .values = values, - }); - - types[0] = dest_ty; - types[1] = Type.initTag(.u1); - values[0] = Value.initTag(.unreachable_value); - values[1] = Value.initTag(.unreachable_value); + const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty); + const ov_ty = tuple_ty.tupleFields().types[1]; + // TODO: Remove and use `ov_ty` instead. + // This is a temporary type used until overflow arithmetic properly returns `u1` instead of `bool`. + const overflowed_ty = if (dest_ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, dest_ty.vectorLen(), Type.@"bool") else Type.@"bool"; const result: struct { - overflowed: enum { yes, no, undef }, + /// TODO: Rename to `overflow_bit` and make of type `u1`. + overflowed: Air.Inst.Ref, wrapped: Air.Inst.Ref, } = result: { switch (zir_tag) { @@ -9452,23 +9449,24 @@ fn zirOverflowArithmetic( // Otherwise, if either of the argument is undefined, undefined is returned. if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { - break :result .{ .overflowed = .no, .wrapped = rhs }; + break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs }; } } if (maybe_rhs_val) |rhs_val| { if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) { - break :result .{ .overflowed = .no, .wrapped = lhs }; + break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; } } if (maybe_lhs_val) |lhs_val| { if (maybe_rhs_val) |rhs_val| { if (lhs_val.isUndef() or rhs_val.isUndef()) { - break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) }; + break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) }; } const result = try lhs_val.intAddWithOverflow(rhs_val, dest_ty, sema.arena, target); - const inst = try sema.addConstant(dest_ty, result.wrapped_result); - break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst }; + const overflowed = try sema.addConstant(overflowed_ty, result.overflowed); + const wrapped = try sema.addConstant(dest_ty, result.wrapped_result); + break :result .{ .overflowed = overflowed, .wrapped = wrapped }; } } }, @@ -9477,17 +9475,18 @@ fn zirOverflowArithmetic( // Otherwise, if either result is undefined, both results are undefined. if (maybe_rhs_val) |rhs_val| { if (rhs_val.isUndef()) { - break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) }; + break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) }; } else if (rhs_val.compareWithZero(.eq)) { - break :result .{ .overflowed = .no, .wrapped = lhs }; + break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; } else if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) }; + break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) }; } const result = try lhs_val.intSubWithOverflow(rhs_val, dest_ty, sema.arena, target); - const inst = try sema.addConstant(dest_ty, result.wrapped_result); - break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst }; + const overflowed = try sema.addConstant(overflowed_ty, result.overflowed); + const wrapped = try sema.addConstant(dest_ty, result.wrapped_result); + break :result .{ .overflowed = overflowed, .wrapped = wrapped }; } } }, @@ -9498,9 +9497,9 @@ fn zirOverflowArithmetic( if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { if (lhs_val.compareWithZero(.eq)) { - break :result .{ .overflowed = .no, .wrapped = lhs }; + break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; } else if (lhs_val.compare(.eq, Value.one, dest_ty, mod)) { - break :result .{ .overflowed = .no, .wrapped = rhs }; + break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs }; } } } @@ -9508,9 +9507,9 @@ fn zirOverflowArithmetic( if (maybe_rhs_val) |rhs_val| { if (!rhs_val.isUndef()) { if (rhs_val.compareWithZero(.eq)) { - break :result .{ .overflowed = .no, .wrapped = rhs }; + break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs }; } else if (rhs_val.compare(.eq, Value.one, dest_ty, mod)) { - break :result .{ .overflowed = .no, .wrapped = lhs }; + break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; } } } @@ -9518,12 +9517,13 @@ fn zirOverflowArithmetic( if (maybe_lhs_val) |lhs_val| { if (maybe_rhs_val) |rhs_val| { if (lhs_val.isUndef() or rhs_val.isUndef()) { - break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) }; + break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) }; } const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, target); - const inst = try sema.addConstant(dest_ty, result.wrapped_result); - break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst }; + const overflowed = try sema.addConstant(overflowed_ty, result.overflowed); + const wrapped = try sema.addConstant(dest_ty, result.wrapped_result); + break :result .{ .overflowed = overflowed, .wrapped = wrapped }; } } }, @@ -9533,23 +9533,24 @@ fn zirOverflowArithmetic( // Oterhwise if either of the arguments is undefined, both results are undefined. if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { - break :result .{ .overflowed = .no, .wrapped = lhs }; + break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; } } if (maybe_rhs_val) |rhs_val| { if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) { - break :result .{ .overflowed = .no, .wrapped = lhs }; + break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; } } if (maybe_lhs_val) |lhs_val| { if (maybe_rhs_val) |rhs_val| { if (lhs_val.isUndef() or rhs_val.isUndef()) { - break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) }; + break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) }; } const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, target); - const inst = try sema.addConstant(dest_ty, result.wrapped_result); - break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst }; + const overflowed = try sema.addConstant(overflowed_ty, result.overflowed); + const wrapped = try sema.addConstant(dest_ty, result.wrapped_result); + break :result .{ .overflowed = overflowed, .wrapped = wrapped }; } } }, @@ -9577,21 +9578,40 @@ fn zirOverflowArithmetic( } }, }); - const wrapped = try block.addStructFieldVal(tuple, 0, dest_ty); + const wrapped = try sema.tupleFieldValByIndex(block, src, tuple, 0, tuple_ty); try sema.storePtr2(block, src, ptr, ptr_src, wrapped, src, .store); - const overflow_bit = try block.addStructFieldVal(tuple, 1, Type.initTag(.u1)); - const zero_u1 = try sema.addConstant(Type.initTag(.u1), Value.zero); - return try block.addBinOp(.cmp_neq, overflow_bit, zero_u1); + const overflow_bit = try sema.tupleFieldValByIndex(block, src, tuple, 1, tuple_ty); + const zero_ov_val = if (dest_ty.zigTypeTag() == .Vector) try Value.Tag.repeated.create(sema.arena, Value.zero) else Value.zero; + const zero_ov = try sema.addConstant(ov_ty, zero_ov_val); + + const overflowed_inst = if (dest_ty.zigTypeTag() == .Vector) + block.addCmpVector(overflow_bit, .zero, .neq, try sema.addType(ov_ty)) + else + block.addBinOp(.cmp_neq, overflow_bit, zero_ov); + return overflowed_inst; }; try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store); + return result.overflowed; +} - return switch (result.overflowed) { - .yes => Air.Inst.Ref.bool_true, - .no => Air.Inst.Ref.bool_false, - .undef => try sema.addConstUndef(Type.bool), - }; +fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type { + const ov_ty = if (ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, ty.vectorLen(), Type.@"u1") else Type.@"u1"; + + const types = try sema.arena.alloc(Type, 2); + const values = try sema.arena.alloc(Value, 2); + const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{ + .types = types, + .values = values, + }); + + types[0] = ty; + types[1] = ov_ty; + values[0] = Value.initTag(.unreachable_value); + values[1] = Value.initTag(.unreachable_value); + + return tuple_ty; } fn analyzeArithmetic( @@ -23093,6 +23113,14 @@ fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref { return sema.addConstant(ty, try Value.Tag.int_u64.create(sema.arena, int)); } +fn addBool(sema: *Sema, ty: Type, boolean: bool) CompileError!Air.Inst.Ref { + return switch (ty.zigTypeTag()) { + .Vector => sema.addConstant(ty, try Value.Tag.repeated.create(sema.arena, Value.makeBool(boolean))), + .Bool => sema.resolveInst(if (boolean) .bool_true else .bool_false), + else => unreachable, + }; +} + fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref { return sema.addConstant(ty, Value.undef); } From c2980f332ed46e6ad7e8ac81b4dbef6d363447fb Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 26 Mar 2022 16:04:17 -0700 Subject: [PATCH 1525/2031] Sema: implement integer overflow safety for add, sub, mul --- src/Sema.zig | 74 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 12 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 5c37b1a0f4..a37fc2ef50 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1574,6 +1574,12 @@ fn failWithErrorSetCodeMissing( }); } +fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value) CompileError { + return sema.fail(block, src, "overflow of integer type '{}' with value '{}'", .{ + int_ty.fmt(sema.mod), val.fmtValue(Type.@"comptime_int", sema.mod), + }); +} + /// We don't return a pointer to the new error note because the pointer /// becomes invalid when you add another one. fn errNote( @@ -9711,10 +9717,11 @@ fn analyzeArithmetic( } if (maybe_rhs_val) |rhs_val| { if (is_int) { - return sema.addConstant( - resolved_type, - try lhs_val.intAdd(rhs_val, resolved_type, sema.arena, target), - ); + const sum = try lhs_val.intAdd(rhs_val, resolved_type, sema.arena, target); + if (!sum.intFitsInType(resolved_type, target)) { + return sema.failWithIntegerOverflow(block, src, resolved_type, sum); + } + return sema.addConstant(resolved_type, sum); } else { return sema.addConstant( resolved_type, @@ -9804,10 +9811,11 @@ fn analyzeArithmetic( } if (maybe_rhs_val) |rhs_val| { if (is_int) { - return sema.addConstant( - resolved_type, - try lhs_val.intSub(rhs_val, resolved_type, sema.arena, target), - ); + const diff = try lhs_val.intSub(rhs_val, resolved_type, sema.arena, target); + if (!diff.intFitsInType(resolved_type, target)) { + return sema.failWithIntegerOverflow(block, src, resolved_type, diff); + } + return sema.addConstant(resolved_type, diff); } else { return sema.addConstant( resolved_type, @@ -10177,10 +10185,11 @@ fn analyzeArithmetic( } } if (is_int) { - return sema.addConstant( - resolved_type, - try lhs_val.intMul(rhs_val, resolved_type, sema.arena, target), - ); + const product = try lhs_val.intMul(rhs_val, resolved_type, sema.arena, target); + if (!product.intFitsInType(resolved_type, target)) { + return sema.failWithIntegerOverflow(block, src, resolved_type, product); + } + return sema.addConstant(resolved_type, product); } else { return sema.addConstant( resolved_type, @@ -10468,6 +10477,45 @@ fn analyzeArithmetic( }; try sema.requireRuntimeBlock(block, rs.src); + if (block.wantSafety()) { + if (scalar_tag == .Int) { + const maybe_op_ov: ?Air.Inst.Tag = switch (rs.air_tag) { + .add => .add_with_overflow, + .sub => .sub_with_overflow, + .mul => .mul_with_overflow, + else => null, + }; + if (maybe_op_ov) |op_ov_tag| { + const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(resolved_type); + const op_ov = try block.addInst(.{ + .tag = op_ov_tag, + .data = .{ .ty_pl = .{ + .ty = try sema.addType(op_ov_tuple_ty), + .payload = try sema.addExtra(Air.Bin{ + .lhs = casted_lhs, + .rhs = casted_rhs, + }), + } }, + }); + const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty); + const any_ov_bit = if (resolved_type.zigTypeTag() == .Vector) + try block.addInst(.{ + .tag = .reduce, + .data = .{ .reduce = .{ + .operand = ov_bit, + .operation = .Or, + } }, + }) + else + ov_bit; + const zero_ov = try sema.addConstant(Type.@"u1", Value.zero); + const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov); + + try sema.addSafetyCheck(block, no_ov, .integer_overflow); + return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty); + } + } + } return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs); } @@ -16702,6 +16750,7 @@ pub const PanicId = enum { invalid_error_code, index_out_of_bounds, cast_truncated_data, + integer_overflow, }; fn addSafetyCheck( @@ -16825,6 +16874,7 @@ fn safetyPanic( .invalid_error_code => "invalid error code", .index_out_of_bounds => "attempt to index out of bounds", .cast_truncated_data => "integer cast truncated bits", + .integer_overflow => "integer overflow", }; const msg_inst = msg_inst: { From afc714d5e5ff4d10a2b5dcc8f4c2eac8245de35c Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 26 Mar 2022 16:06:59 -0700 Subject: [PATCH 1526/2031] stage2: implement runtime safety checks for shl_exact --- src/Sema.zig | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index a37fc2ef50..471639ba96 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -8826,8 +8826,6 @@ fn zirShl( return sema.addConstant(lhs_ty, val); } else lhs_src; - // TODO: insert runtime safety check for shl_exact - const new_rhs = if (air_tag == .shl_sat) rhs: { // Limit the RHS type for saturating shl to be an integer as small as the LHS. if (rhs_is_comptime_int or @@ -8845,6 +8843,41 @@ fn zirShl( } else rhs; try sema.requireRuntimeBlock(block, runtime_src); + if (block.wantSafety()) { + const maybe_op_ov: ?Air.Inst.Tag = switch (air_tag) { + .shl_exact => .shl_with_overflow, + else => null, + }; + if (maybe_op_ov) |op_ov_tag| { + const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(lhs_ty); + const op_ov = try block.addInst(.{ + .tag = op_ov_tag, + .data = .{ .ty_pl = .{ + .ty = try sema.addType(op_ov_tuple_ty), + .payload = try sema.addExtra(Air.Bin{ + .lhs = lhs, + .rhs = rhs, + }), + } }, + }); + const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty); + const any_ov_bit = if (lhs_ty.zigTypeTag() == .Vector) + try block.addInst(.{ + .tag = .reduce, + .data = .{ .reduce = .{ + .operand = ov_bit, + .operation = .Or, + } }, + }) + else + ov_bit; + const zero_ov = try sema.addConstant(Type.@"u1", Value.zero); + const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov); + + try sema.addSafetyCheck(block, no_ov, .shl_overflow); + return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty); + } + } return block.addBinOp(air_tag, lhs, new_rhs); } @@ -16751,6 +16784,7 @@ pub const PanicId = enum { index_out_of_bounds, cast_truncated_data, integer_overflow, + shl_overflow, }; fn addSafetyCheck( @@ -16875,6 +16909,7 @@ fn safetyPanic( .index_out_of_bounds => "attempt to index out of bounds", .cast_truncated_data => "integer cast truncated bits", .integer_overflow => "integer overflow", + .shl_overflow => "left shift overflowed bits", }; const msg_inst = msg_inst: { From 21be3d9166e8ea159ef334243b945bd20ed62652 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 26 Mar 2022 16:07:52 -0700 Subject: [PATCH 1527/2031] stage2: add vectorized overflow arithmetic behavior tests --- test/behavior/vector.zig | 92 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 9847054692..e8150dde7a 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -903,3 +903,95 @@ test "multiplication-assignment operator with an array operand" { try S.doTheTest(); comptime try S.doTheTest(); } + +test "@addWithOverflow" { + if (builtin.zig_backend == .stage1) { + // stage1 doesn't support vector args + return error.SkipZigTest; + } + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var result: @Vector(4, u8) = undefined; + var overflow = @addWithOverflow(@Vector(4, u8), @Vector(4, u8){ 250, 250, 250, 250 }, @Vector(4, u8){ 0, 5, 6, 10 }, &result); + var expected: @Vector(4, bool) = .{ false, false, true, true }; + try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "@subWithOverflow" { + if (builtin.zig_backend == .stage1) { + // stage1 doesn't support vector args + return error.SkipZigTest; + } + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var result: @Vector(4, i8) = undefined; + var overflow = @subWithOverflow(@Vector(4, i8), @Vector(4, i8){ -120, -120, 120, 120 }, @Vector(4, i8){ 8, 9, -7, -8 }, &result); + var expected: @Vector(4, bool) = .{ false, true, false, true }; + try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "@mulWithOverflow" { + if (builtin.zig_backend == .stage1) { + // stage1 doesn't support vector args + return error.SkipZigTest; + } + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var result: @Vector(4, u8) = undefined; + var overflow = @mulWithOverflow(@Vector(4, u8), @Vector(4, u8){ 10, 10, 10, 10 }, @Vector(4, u8){ 25, 26, 0, 30 }, &result); + var expected: @Vector(4, bool) = .{ false, true, false, true }; + try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "@shlWithOverflow" { + if (builtin.zig_backend == .stage1) { + // stage1 doesn't support vector args + return error.SkipZigTest; + } + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var result: @Vector(4, u8) = undefined; + var overflow = @shlWithOverflow(@Vector(4, u8), @Vector(4, u8){ 0, 1, 8, 255 }, @Vector(4, u3){ 7, 7, 7, 7 }, &result); + var expected: @Vector(4, bool) = .{ false, false, true, true }; + try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} From bb3532e775bb17fff1630103d4d29a650b7ad5c3 Mon Sep 17 00:00:00 2001 From: William Sengir Date: Mon, 28 Mar 2022 10:40:55 -0700 Subject: [PATCH 1528/2031] stage2: add more vector overflow tests --- test/behavior/vector.zig | 44 ++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index e8150dde7a..cbd8787701 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -917,10 +917,30 @@ test "@addWithOverflow" { const S = struct { fn doTheTest() !void { - var result: @Vector(4, u8) = undefined; - var overflow = @addWithOverflow(@Vector(4, u8), @Vector(4, u8){ 250, 250, 250, 250 }, @Vector(4, u8){ 0, 5, 6, 10 }, &result); - var expected: @Vector(4, bool) = .{ false, false, true, true }; - try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); + { + var result: @Vector(4, u8) = undefined; + var overflow = @addWithOverflow(@Vector(4, u8), @Vector(4, u8){ 250, 250, 250, 250 }, @Vector(4, u8){ 0, 5, 6, 10 }, &result); + var expected: @Vector(4, bool) = .{ false, false, true, true }; + try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); + } + { + var result: @Vector(4, i8) = undefined; + var overflow = @addWithOverflow(@Vector(4, i8), @Vector(4, i8){ -125, -125, 125, 125 }, @Vector(4, i8){ -3, -4, 2, 3 }, &result); + var expected: @Vector(4, bool) = .{ false, true, false, true }; + try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); + } + { + var result: @Vector(4, u1) = undefined; + var overflow = @addWithOverflow(@Vector(4, u1), @Vector(4, u1){ 0, 0, 1, 1 }, @Vector(4, u1){ 0, 1, 0, 1 }, &result); + var expected: @Vector(4, bool) = .{ false, false, false, true }; + try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); + } + { + var result: @Vector(4, u0) = undefined; + var overflow = @addWithOverflow(@Vector(4, u0), @Vector(4, u0){ 0, 0, 0, 0 }, @Vector(4, u0){ 0, 0, 0, 0 }, &result); + var expected: @Vector(4, bool) = .{ false, false, false, false }; + try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); + } } }; try S.doTheTest(); @@ -940,10 +960,18 @@ test "@subWithOverflow" { const S = struct { fn doTheTest() !void { - var result: @Vector(4, i8) = undefined; - var overflow = @subWithOverflow(@Vector(4, i8), @Vector(4, i8){ -120, -120, 120, 120 }, @Vector(4, i8){ 8, 9, -7, -8 }, &result); - var expected: @Vector(4, bool) = .{ false, true, false, true }; - try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); + { + var result: @Vector(2, u8) = undefined; + var overflow = @subWithOverflow(@Vector(2, u8), @Vector(2, u8){ 5, 5 }, @Vector(2, u8){ 5, 6 }, &result); + var expected: @Vector(2, bool) = .{ false, true }; + try expect(mem.eql(bool, &@as([2]bool, overflow), &@as([2]bool, expected))); + } + { + var result: @Vector(4, i8) = undefined; + var overflow = @subWithOverflow(@Vector(4, i8), @Vector(4, i8){ -120, -120, 120, 120 }, @Vector(4, i8){ 8, 9, -7, -8 }, &result); + var expected: @Vector(4, bool) = .{ false, true, false, true }; + try expect(mem.eql(bool, &@as([4]bool, overflow), &@as([4]bool, expected))); + } } }; try S.doTheTest(); From a5ea22d0693cf767fa47c9947399651ed3c35aaf Mon Sep 17 00:00:00 2001 From: William Sengir Date: Sat, 23 Apr 2022 02:54:52 -0700 Subject: [PATCH 1529/2031] LLVM: correctly pad result tuple of `airOverflow` --- src/codegen/llvm.zig | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 3abdccfbe2..7392b2068b 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5605,14 +5605,24 @@ pub const FuncGen = struct { const lhs_ty = self.air.typeOf(extra.lhs); const scalar_ty = lhs_ty.scalarType(); + const dest_ty = self.air.typeOfIndex(inst); const intrinsic_name = if (scalar_ty.isSignedInt()) signed_intrinsic else unsigned_intrinsic; const llvm_lhs_ty = try self.dg.llvmType(lhs_ty); + const llvm_dest_ty = try self.dg.llvmType(dest_ty); + + const tg = self.dg.module.getTarget(); const llvm_fn = self.getIntrinsic(intrinsic_name, &.{llvm_lhs_ty}); const result_struct = self.builder.buildCall(llvm_fn, &[_]*const llvm.Value{ lhs, rhs }, 2, .Fast, .Auto, ""); - return result_struct; + + const result = self.builder.buildExtractValue(result_struct, 0, ""); + const overflow_bit = self.builder.buildExtractValue(result_struct, 1, ""); + + var ty_buf: Type.Payload.Pointer = undefined; + const partial = self.builder.buildInsertValue(llvm_dest_ty.getUndef(), result, llvmFieldIndex(dest_ty, 0, tg, &ty_buf).?, ""); + return self.builder.buildInsertValue(partial, overflow_bit, llvmFieldIndex(dest_ty, 1, tg, &ty_buf).?, ""); } fn buildElementwiseCall( From 03ed0f0d2847a99823ee4ae1b1a0554b88c6544a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 12 May 2022 16:44:44 -0700 Subject: [PATCH 1530/2031] C backend: implement overflow arithmetic Most of the work here was additions to zig.h. The lowering code is mainly responsible for calling the correct function name depending on the operand type. Some of the compiler-rt calls here are not implemented yet and are non-standard symbols due to the C programming language not needing them. After this commit, the behavior tests with -ofmt=c are passing again. --- lib/std/builtin.zig | 3 +- src/codegen/c.zig | 200 ++++++----- src/link/C/zig.h | 821 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 940 insertions(+), 84 deletions(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 53786c8661..d352ac29dc 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -767,8 +767,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn // Until self-hosted catches up with stage1 language features, we have a simpler // default panic function: - if ((builtin.zig_backend == .stage2_llvm and builtin.link_libc) or - builtin.zig_backend == .stage2_c or + if (builtin.zig_backend == .stage2_c or builtin.zig_backend == .stage2_wasm or builtin.zig_backend == .stage2_arm or builtin.zig_backend == .stage2_aarch64 or diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 44b616c493..998271cd7f 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1766,10 +1766,10 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .mul_add => try airMulAdd(f, inst), - .add_with_overflow => try airAddWithOverflow(f, inst), - .sub_with_overflow => try airSubWithOverflow(f, inst), - .mul_with_overflow => try airMulWithOverflow(f, inst), - .shl_with_overflow => try airShlWithOverflow(f, inst), + .add_with_overflow => try airOverflow(f, inst, "addo_"), + .sub_with_overflow => try airOverflow(f, inst, "subo_"), + .mul_with_overflow => try airOverflow(f, inst, "mulo_"), + .shl_with_overflow => try airOverflow(f, inst, "shlo_"), .min => try airMinMax(f, inst, "<"), .max => try airMinMax(f, inst, ">"), @@ -2295,7 +2295,8 @@ fn airWrapOp( const bin_op = f.air.instructions.items(.data)[inst].bin_op; const inst_ty = f.air.typeOfIndex(inst); - const int_info = inst_ty.intInfo(f.object.dg.module.getTarget()); + const target = f.object.dg.module.getTarget(); + const int_info = inst_ty.intInfo(target); const bits = int_info.bits; // if it's an unsigned int with non-arbitrary bit size then we can just add @@ -2313,47 +2314,8 @@ fn airWrapOp( return f.fail("TODO: C backend: airWrapOp for large integers", .{}); } - var min_buf: [80]u8 = undefined; - const min = switch (int_info.signedness) { - .unsigned => "0", - else => switch (inst_ty.tag()) { - .c_short => "SHRT_MIN", - .c_int => "INT_MIN", - .c_long => "LONG_MIN", - .c_longlong => "LLONG_MIN", - .isize => "INTPTR_MIN", - else => blk: { - const val = -1 * std.math.pow(i64, 2, @intCast(i64, bits - 1)); - break :blk std.fmt.bufPrint(&min_buf, "{d}", .{val}) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, - }; - }, - }, - }; - var max_buf: [80]u8 = undefined; - const max = switch (inst_ty.tag()) { - .c_short => "SHRT_MAX", - .c_ushort => "USHRT_MAX", - .c_int => "INT_MAX", - .c_uint => "UINT_MAX", - .c_long => "LONG_MAX", - .c_ulong => "ULONG_MAX", - .c_longlong => "LLONG_MAX", - .c_ulonglong => "ULLONG_MAX", - .isize => "INTPTR_MAX", - .usize => "UINTPTR_MAX", - else => blk: { - const pow_bits = switch (int_info.signedness) { - .signed => bits - 1, - .unsigned => bits, - }; - const val = std.math.pow(u64, 2, pow_bits) - 1; - break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, - }; - }, - }; + const max = intMax(inst_ty, target, &max_buf); const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); @@ -2369,10 +2331,7 @@ fn airWrapOp( .c_long => try w.writeAll("long"), .c_longlong => try w.writeAll("longlong"), else => { - const prefix_byte: u8 = switch (int_info.signedness) { - .signed => 'i', - .unsigned => 'u', - }; + const prefix_byte: u8 = signAbbrev(int_info.signedness); for ([_]u8{ 8, 16, 32, 64 }) |nbits| { if (bits <= nbits) { try w.print("{c}{d}", .{ prefix_byte, nbits }); @@ -2390,6 +2349,9 @@ fn airWrapOp( try f.writeCValue(w, rhs); if (int_info.signedness == .signed) { + var min_buf: [80]u8 = undefined; + const min = intMin(inst_ty, target, &min_buf); + try w.print(", {s}", .{min}); } @@ -2475,10 +2437,7 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { .c_long => try w.writeAll("long"), .c_longlong => try w.writeAll("longlong"), else => { - const prefix_byte: u8 = switch (int_info.signedness) { - .signed => 'i', - .unsigned => 'u', - }; + const prefix_byte: u8 = signAbbrev(int_info.signedness); for ([_]u8{ 8, 16, 32, 64 }) |nbits| { if (bits <= nbits) { try w.print("{c}{d}", .{ prefix_byte, nbits }); @@ -2505,28 +2464,63 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { return ret; } -fn airAddWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue { - _ = f; - _ = inst; - return f.fail("TODO add with overflow", .{}); -} +fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8) !CValue { + if (f.liveness.isUnused(inst)) + return CValue.none; -fn airSubWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue { - _ = f; - _ = inst; - return f.fail("TODO sub with overflow", .{}); -} + const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; + const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; -fn airMulWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue { - _ = f; - _ = inst; - return f.fail("TODO mul with overflow", .{}); -} + const lhs = try f.resolveInst(bin_op.lhs); + const rhs = try f.resolveInst(bin_op.rhs); -fn airShlWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue { - _ = f; - _ = inst; - return f.fail("TODO shl with overflow", .{}); + const inst_ty = f.air.typeOfIndex(inst); + const scalar_ty = f.air.typeOf(bin_op.lhs).scalarType(); + const target = f.object.dg.module.getTarget(); + const int_info = scalar_ty.intInfo(target); + const w = f.object.writer(); + const c_bits = toCIntBits(int_info.bits) orelse + return f.fail("TODO: C backend: implement integer arithmetic larger than 128 bits", .{}); + + var max_buf: [80]u8 = undefined; + const max = intMax(scalar_ty, target, &max_buf); + + const ret = try f.allocLocal(inst_ty, .Mut); + try w.writeAll(";"); + try f.object.indent_writer.insertNewline(); + try f.writeCValue(w, ret); + + switch (int_info.signedness) { + .unsigned => { + try w.print(".field_1 = zig_{s}u{d}(", .{ + op_abbrev, c_bits, + }); + try f.writeCValue(w, lhs); + try w.writeAll(", "); + try f.writeCValue(w, rhs); + try w.writeAll(", &"); + try f.writeCValue(w, ret); + try w.print(".field_0, {s}", .{max}); + }, + .signed => { + var min_buf: [80]u8 = undefined; + const min = intMin(scalar_ty, target, &min_buf); + + try w.print(".field_1 = zig_{s}i{d}(", .{ + op_abbrev, c_bits, + }); + try f.writeCValue(w, lhs); + try w.writeAll(", "); + try f.writeCValue(w, rhs); + try w.writeAll(", &"); + try f.writeCValue(w, ret); + try w.print(".field_0, {s}, {s}", .{ min, max }); + }, + } + + try w.writeAll(");"); + try f.object.indent_writer.insertNewline(); + return ret; } fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { @@ -3571,11 +3565,7 @@ fn airBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !C return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); try writer.print(" = zig_{s}_", .{fn_name}); - const prefix_byte: u8 = switch (int_info.signedness) { - .signed => 'i', - .unsigned => 'u', - }; - try writer.print("{c}{d}(", .{ prefix_byte, c_bits }); + try writer.print("{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); try f.writeCValue(writer, try f.resolveInst(operand)); try writer.print(", {d});\n", .{int_info.bits}); return local; @@ -3596,11 +3586,7 @@ fn airBinOpBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u const int_info = lhs_ty.intInfo(target); const c_bits = toCIntBits(int_info.bits) orelse return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); - const prefix_byte: u8 = switch (int_info.signedness) { - .signed => 'i', - .unsigned => 'u', - }; - try writer.print(" = zig_{s}_{c}{d}", .{ fn_name, prefix_byte, c_bits }); + try writer.print(" = zig_{s}_{c}{d}", .{ fn_name, signAbbrev(int_info.signedness), c_bits }); } else if (lhs_ty.isRuntimeFloat()) { const c_bits = lhs_ty.floatBits(target); try writer.print(" = zig_{s}_f{d}", .{ fn_name, c_bits }); @@ -4085,3 +4071,53 @@ fn toCIntBits(zig_bits: u32) ?u32 { } return null; } + +fn signAbbrev(signedness: std.builtin.Signedness) u8 { + return switch (signedness) { + .signed => 'i', + .unsigned => 'u', + }; +} + +fn intMax(ty: Type, target: std.Target, buf: []u8) []const u8 { + switch (ty.tag()) { + .c_short => return "SHRT_MAX", + .c_ushort => return "USHRT_MAX", + .c_int => return "INT_MAX", + .c_uint => return "UINT_MAX", + .c_long => return "LONG_MAX", + .c_ulong => return "ULONG_MAX", + .c_longlong => return "LLONG_MAX", + .c_ulonglong => return "ULLONG_MAX", + else => { + const int_info = ty.intInfo(target); + const rhs = @intCast(u7, int_info.bits - @boolToInt(int_info.signedness == .signed)); + const val = (@as(u128, 1) << rhs) - 1; + // TODO make this integer literal have a suffix if necessary (such as "ull") + return std.fmt.bufPrint(buf, "{}", .{val}) catch |err| switch (err) { + error.NoSpaceLeft => unreachable, + }; + }, + } +} + +fn intMin(ty: Type, target: std.Target, buf: []u8) []const u8 { + switch (ty.tag()) { + .c_short => return "SHRT_MIN", + .c_int => return "INT_MIN", + .c_long => return "LONG_MIN", + .c_longlong => return "LLONG_MIN", + else => { + const int_info = ty.intInfo(target); + assert(int_info.signedness == .signed); + const val = v: { + if (int_info.bits == 0) break :v 0; + const rhs = @intCast(u7, (int_info.bits - 1)); + break :v -(@as(i128, 1) << rhs); + }; + return std.fmt.bufPrint(buf, "{d}", .{val}) catch |err| switch (err) { + error.NoSpaceLeft => unreachable, + }; + }, + } +} diff --git a/src/link/C/zig.h b/src/link/C/zig.h index 85c7856d2b..6bafee987b 100644 --- a/src/link/C/zig.h +++ b/src/link/C/zig.h @@ -165,8 +165,24 @@ #define int128_t __int128 #define uint128_t unsigned __int128 +#define UINT128_MAX ((uint128_t)(0xffffffffffffffffull) | 0xffffffffffffffffull) ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t); ZIG_EXTERN_C void *memset (void *, int, size_t); +ZIG_EXTERN_C int64_t __addodi4(int64_t lhs, int64_t rhs, int *overflow); +ZIG_EXTERN_C int128_t __addoti4(int128_t lhs, int128_t rhs, int *overflow); +ZIG_EXTERN_C uint64_t __uaddodi4(uint64_t lhs, uint64_t rhs, int *overflow); +ZIG_EXTERN_C uint128_t __uaddoti4(uint128_t lhs, uint128_t rhs, int *overflow); +ZIG_EXTERN_C int32_t __subosi4(int32_t lhs, int32_t rhs, int *overflow); +ZIG_EXTERN_C int64_t __subodi4(int64_t lhs, int64_t rhs, int *overflow); +ZIG_EXTERN_C int128_t __suboti4(int128_t lhs, int128_t rhs, int *overflow); +ZIG_EXTERN_C uint32_t __usubosi4(uint32_t lhs, uint32_t rhs, int *overflow); +ZIG_EXTERN_C uint64_t __usubodi4(uint64_t lhs, uint64_t rhs, int *overflow); +ZIG_EXTERN_C uint128_t __usuboti4(uint128_t lhs, uint128_t rhs, int *overflow); +ZIG_EXTERN_C int64_t __mulodi4(int64_t lhs, int64_t rhs, int *overflow); +ZIG_EXTERN_C int128_t __muloti4(int128_t lhs, int128_t rhs, int *overflow); +ZIG_EXTERN_C uint64_t __umulodi4(uint64_t lhs, uint64_t rhs, int *overflow); +ZIG_EXTERN_C uint128_t __umuloti4(uint128_t lhs, uint128_t rhs, int *overflow); + static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) { uint8_t thresh = max - rhs; @@ -396,6 +412,811 @@ static inline long long zig_subw_longlong(long long lhs, long long rhs, long lon return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs)); } +static inline bool zig_addo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { +#if defined(__GNUC__) && INT8_MAX == INT_MAX + if (min == INT8_MIN && max == INT8_MAX) { + return __builtin_sadd_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT8_MAX == LONG_MAX + if (min == INT8_MIN && max == INT8_MAX) { + return __builtin_saddl_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT8_MAX == LLONG_MAX + if (min == INT8_MIN && max == INT8_MAX) { + return __builtin_saddll_overflow(lhs, rhs, res); + } +#endif + int16_t big_result = (int16_t)lhs + (int16_t)rhs; + if (big_result > max) { + *res = big_result - ((int16_t)max - (int16_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int16_t)max - (int16_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_addo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t min, int16_t max) { +#if defined(__GNUC__) && INT16_MAX == INT_MAX + if (min == INT16_MIN && max == INT16_MAX) { + return __builtin_sadd_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT16_MAX == LONG_MAX + if (min == INT16_MIN && max == INT16_MAX) { + return __builtin_saddl_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT16_MAX == LLONG_MAX + if (min == INT16_MIN && max == INT16_MAX) { + return __builtin_saddll_overflow(lhs, rhs, res); + } +#endif + int32_t big_result = (int32_t)lhs + (int32_t)rhs; + if (big_result > max) { + *res = big_result - ((int32_t)max - (int32_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int32_t)max - (int32_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_addo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t min, int32_t max) { +#if defined(__GNUC__) && INT32_MAX == INT_MAX + if (min == INT32_MIN && max == INT32_MAX) { + return __builtin_sadd_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT32_MAX == LONG_MAX + if (min == INT32_MIN && max == INT32_MAX) { + return __builtin_saddl_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT32_MAX == LLONG_MAX + if (min == INT32_MIN && max == INT32_MAX) { + return __builtin_saddll_overflow(lhs, rhs, res); + } +#endif + int64_t big_result = (int64_t)lhs + (int64_t)rhs; + if (big_result > max) { + *res = big_result - ((int64_t)max - (int64_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int64_t)max - (int64_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_addo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t min, int64_t max) { + bool overflow; +#if defined(__GNUC__) && INT64_MAX == INT_MAX + overflow = __builtin_sadd_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT64_MAX == LONG_MAX + overflow = __builtin_saddl_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT64_MAX == LLONG_MAX + overflow = __builtin_saddll_overflow(lhs, rhs, res); +#else + int int_overflow; + *res = __addodi4(lhs, rhs, &int_overflow); + overflow = int_overflow != 0; +#endif + if (!overflow) { + if (*res > max) { + // TODO adjust the result to be the truncated bits + return true; + } else if (*res < min) { + // TODO adjust the result to be the truncated bits + return true; + } + } + return overflow; +} + +static inline bool zig_addo_i128(int128_t lhs, int128_t rhs, int128_t *res, int128_t min, int128_t max) { + bool overflow; +#if defined(__GNUC__) && INT128_MAX == INT_MAX + overflow = __builtin_sadd_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT128_MAX == LONG_MAX + overflow = __builtin_saddl_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT128_MAX == LLONG_MAX + overflow = __builtin_saddll_overflow(lhs, rhs, res); +#else + int int_overflow; + *res = __addoti4(lhs, rhs, &int_overflow); + overflow = int_overflow != 0; +#endif + if (!overflow) { + if (*res > max) { + // TODO adjust the result to be the truncated bits + return true; + } else if (*res < min) { + // TODO adjust the result to be the truncated bits + return true; + } + } + return overflow; +} + +static inline bool zig_addo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t max) { +#if defined(__GNUC__) && UINT8_MAX == UINT_MAX + if (max == UINT8_MAX) { + return __builtin_uadd_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT8_MAX == ULONG_MAX + if (max == UINT8_MAX) { + return __builtin_uaddl_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT8_MAX == ULLONG_MAX + if (max == UINT8_MAX) { + return __builtin_uaddll_overflow(lhs, rhs, res); + } +#endif + uint16_t big_result = (uint16_t)lhs + (uint16_t)rhs; + if (big_result > max) { + *res = big_result - max - 1; + return true; + } + *res = big_result; + return false; +} + +static inline uint16_t zig_addo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint16_t max) { +#if defined(__GNUC__) && UINT16_MAX == UINT_MAX + if (max == UINT16_MAX) { + return __builtin_uadd_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT16_MAX == ULONG_MAX + if (max == UINT16_MAX) { + return __builtin_uaddl_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT16_MAX == ULLONG_MAX + if (max == UINT16_MAX) { + return __builtin_uaddll_overflow(lhs, rhs, res); + } +#endif + uint32_t big_result = (uint32_t)lhs + (uint32_t)rhs; + if (big_result > max) { + *res = big_result - max - 1; + return true; + } + *res = big_result; + return false; +} + +static inline uint32_t zig_addo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint32_t max) { +#if defined(__GNUC__) && UINT32_MAX == UINT_MAX + if (max == UINT32_MAX) { + return __builtin_uadd_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT32_MAX == ULONG_MAX + if (max == UINT32_MAX) { + return __builtin_uaddl_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT32_MAX == ULLONG_MAX + if (max == UINT32_MAX) { + return __builtin_uaddll_overflow(lhs, rhs, res); + } +#endif + uint64_t big_result = (uint64_t)lhs + (uint64_t)rhs; + if (big_result > max) { + *res = big_result - max - 1; + return true; + } + *res = big_result; + return false; +} + +static inline uint64_t zig_addo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint64_t max) { + bool overflow; +#if defined(__GNUC__) && UINT64_MAX == UINT_MAX + overflow = __builtin_uadd_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT64_MAX == ULONG_MAX + overflow = __builtin_uaddl_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT64_MAX == ULLONG_MAX + overflow = __builtin_uaddll_overflow(lhs, rhs, res); +#else + int int_overflow; + *res = __uaddodi4(lhs, rhs, &int_overflow); + overflow = int_overflow != 0; +#endif + if (*res > max && !overflow) { + *res -= max - 1; + return true; + } + return overflow; +} + +static inline uint128_t zig_addo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint128_t max) { + bool overflow; + *res = __uaddoti4(lhs, rhs, &overflow); + if (*res > max && !overflow) { + *res -= max - 1; + return true; + } + return overflow; +} + +static inline bool zig_subo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { +#if defined(__GNUC__) && INT8_MAX == INT_MAX + if (min == INT8_MIN && max == INT8_MAX) { + return __builtin_ssub_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT8_MAX == LONG_MAX + if (min == INT8_MIN && max == INT8_MAX) { + return __builtin_ssubl_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT8_MAX == LLONG_MAX + if (min == INT8_MIN && max == INT8_MAX) { + return __builtin_ssubll_overflow(lhs, rhs, res); + } +#endif + int16_t big_result = (int16_t)lhs - (int16_t)rhs; + if (big_result > max) { + *res = big_result - ((int16_t)max - (int16_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int16_t)max - (int16_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_subo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t min, int16_t max) { +#if defined(__GNUC__) && INT16_MAX == INT_MAX + if (min == INT16_MIN && max == INT16_MAX) { + return __builtin_ssub_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT16_MAX == LONG_MAX + if (min == INT16_MIN && max == INT16_MAX) { + return __builtin_ssubl_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT16_MAX == LLONG_MAX + if (min == INT16_MIN && max == INT16_MAX) { + return __builtin_ssubll_overflow(lhs, rhs, res); + } +#endif + int32_t big_result = (int32_t)lhs - (int32_t)rhs; + if (big_result > max) { + *res = big_result - ((int32_t)max - (int32_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int32_t)max - (int32_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_subo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t min, int32_t max) { +#if defined(__GNUC__) && INT32_MAX == INT_MAX + if (min == INT32_MIN && max == INT32_MAX) { + return __builtin_ssub_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT32_MAX == LONG_MAX + if (min == INT32_MIN && max == INT32_MAX) { + return __builtin_ssubl_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT32_MAX == LLONG_MAX + if (min == INT32_MIN && max == INT32_MAX) { + return __builtin_ssubll_overflow(lhs, rhs, res); + } +#endif + int64_t big_result = (int64_t)lhs - (int64_t)rhs; + if (big_result > max) { + *res = big_result - ((int64_t)max - (int64_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int64_t)max - (int64_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_subo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t min, int64_t max) { + bool overflow; +#if defined(__GNUC__) && INT64_MAX == INT_MAX + overflow = __builtin_ssub_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT64_MAX == LONG_MAX + overflow = __builtin_ssubl_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT64_MAX == LLONG_MAX + overflow = __builtin_ssubll_overflow(lhs, rhs, res); +#else + int int_overflow; + *res = __subodi4(lhs, rhs, &int_overflow); + overflow = int_overflow != 0; +#endif + if (!overflow) { + if (*res > max) { + // TODO adjust the result to be the truncated bits + return true; + } else if (*res < min) { + // TODO adjust the result to be the truncated bits + return true; + } + } + return overflow; +} + +static inline bool zig_subo_i128(int128_t lhs, int128_t rhs, int128_t *res, int128_t min, int128_t max) { + bool overflow; +#if defined(__GNUC__) && INT128_MAX == INT_MAX + overflow = __builtin_ssub_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT128_MAX == LONG_MAX + overflow = __builtin_ssubl_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT128_MAX == LLONG_MAX + overflow = __builtin_ssubll_overflow(lhs, rhs, res); +#else + int int_overflow; + *res = __suboti4(lhs, rhs, &int_overflow); + overflow = int_overflow != 0; +#endif + if (!overflow) { + if (*res > max) { + // TODO adjust the result to be the truncated bits + return true; + } else if (*res < min) { + // TODO adjust the result to be the truncated bits + return true; + } + } + return overflow; +} + +static inline bool zig_subo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t max) { +#if defined(__GNUC__) && UINT8_MAX == UINT_MAX + return __builtin_usub_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT8_MAX == ULONG_MAX + return __builtin_usubl_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT8_MAX == ULLONG_MAX + return __builtin_usubll_overflow(lhs, rhs, res); +#endif + if (rhs > lhs) { + *res = max - (rhs - lhs - 1); + return true; + } + *res = lhs - rhs; + return false; +} + +static inline uint16_t zig_subo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint16_t max) { +#if defined(__GNUC__) && UINT16_MAX == UINT_MAX + return __builtin_usub_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT16_MAX == ULONG_MAX + return __builtin_usubl_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT16_MAX == ULLONG_MAX + return __builtin_usubll_overflow(lhs, rhs, res); +#endif + if (rhs > lhs) { + *res = max - (rhs - lhs - 1); + return true; + } + *res = lhs - rhs; + return false; +} + +static inline uint32_t zig_subo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint32_t max) { + if (max == UINT32_MAX) { +#if defined(__GNUC__) && UINT32_MAX == UINT_MAX + return __builtin_usub_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT32_MAX == ULONG_MAX + return __builtin_usubl_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT32_MAX == ULLONG_MAX + return __builtin_usubll_overflow(lhs, rhs, res); +#endif + int int_overflow; + *res = __usubosi4(lhs, rhs, &int_overflow); + return int_overflow != 0; + } else { + if (rhs > lhs) { + *res = max - (rhs - lhs - 1); + return true; + } + *res = lhs - rhs; + return false; + } +} + +static inline uint64_t zig_subo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint64_t max) { + if (max == UINT64_MAX) { +#if defined(__GNUC__) && UINT64_MAX == UINT_MAX + return __builtin_usub_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT64_MAX == ULONG_MAX + return __builtin_usubl_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT64_MAX == ULLONG_MAX + return __builtin_usubll_overflow(lhs, rhs, res); +#else + int int_overflow; + *res = __usubodi4(lhs, rhs, &int_overflow); + return int_overflow != 0; +#endif + } else { + if (rhs > lhs) { + *res = max - (rhs - lhs - 1); + return true; + } + *res = lhs - rhs; + return false; + } +} + +static inline uint128_t zig_subo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint128_t max) { + if (max == UINT128_MAX) { + int int_overflow; + *res = __usuboti4(lhs, rhs, &int_overflow); + return int_overflow != 0; + } else { + if (rhs > lhs) { + *res = max - (rhs - lhs - 1); + return true; + } + *res = lhs - rhs; + return false; + } +} + +static inline bool zig_mulo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { +#if defined(__GNUC__) && INT8_MAX == INT_MAX + if (min == INT8_MIN && max == INT8_MAX) { + return __builtin_smul_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT8_MAX == LONG_MAX + if (min == INT8_MIN && max == INT8_MAX) { + return __builtin_smull_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT8_MAX == LLONG_MAX + if (min == INT8_MIN && max == INT8_MAX) { + return __builtin_smulll_overflow(lhs, rhs, res); + } +#endif + int16_t big_result = (int16_t)lhs * (int16_t)rhs; + if (big_result > max) { + *res = big_result - ((int16_t)max - (int16_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int16_t)max - (int16_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_mulo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t min, int16_t max) { +#if defined(__GNUC__) && INT16_MAX == INT_MAX + if (min == INT16_MIN && max == INT16_MAX) { + return __builtin_smul_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT16_MAX == LONG_MAX + if (min == INT16_MIN && max == INT16_MAX) { + return __builtin_smull_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT16_MAX == LLONG_MAX + if (min == INT16_MIN && max == INT16_MAX) { + return __builtin_smulll_overflow(lhs, rhs, res); + } +#endif + int32_t big_result = (int32_t)lhs * (int32_t)rhs; + if (big_result > max) { + *res = big_result - ((int32_t)max - (int32_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int32_t)max - (int32_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_mulo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t min, int32_t max) { +#if defined(__GNUC__) && INT32_MAX == INT_MAX + if (min == INT32_MIN && max == INT32_MAX) { + return __builtin_smul_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT32_MAX == LONG_MAX + if (min == INT32_MIN && max == INT32_MAX) { + return __builtin_smull_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT32_MAX == LLONG_MAX + if (min == INT32_MIN && max == INT32_MAX) { + return __builtin_smulll_overflow(lhs, rhs, res); + } +#endif + int64_t big_result = (int64_t)lhs * (int64_t)rhs; + if (big_result > max) { + *res = big_result - ((int64_t)max - (int64_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int64_t)max - (int64_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_mulo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t min, int64_t max) { + bool overflow; +#if defined(__GNUC__) && INT64_MAX == INT_MAX + overflow = __builtin_smul_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT64_MAX == LONG_MAX + overflow = __builtin_smull_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT64_MAX == LLONG_MAX + overflow = __builtin_smulll_overflow(lhs, rhs, res); +#else + int int_overflow; + *res = __mulodi4(lhs, rhs, &int_overflow); + overflow = int_overflow != 0; +#endif + if (!overflow) { + if (*res > max) { + // TODO adjust the result to be the truncated bits + return true; + } else if (*res < min) { + // TODO adjust the result to be the truncated bits + return true; + } + } + return overflow; +} + +static inline bool zig_mulo_i128(int128_t lhs, int128_t rhs, int128_t *res, int128_t min, int128_t max) { + bool overflow; +#if defined(__GNUC__) && INT128_MAX == INT_MAX + overflow = __builtin_smul_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT128_MAX == LONG_MAX + overflow = __builtin_smull_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT128_MAX == LLONG_MAX + overflow = __builtin_smulll_overflow(lhs, rhs, res); +#else + int int_overflow; + *res = __muloti4(lhs, rhs, &int_overflow); + overflow = int_overflow != 0; +#endif + if (!overflow) { + if (*res > max) { + // TODO adjust the result to be the truncated bits + return true; + } else if (*res < min) { + // TODO adjust the result to be the truncated bits + return true; + } + } + return overflow; +} + +static inline bool zig_mulo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t max) { +#if defined(__GNUC__) && UINT8_MAX == UINT_MAX + if (max == UINT8_MAX) { + return __builtin_umul_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT8_MAX == ULONG_MAX + if (max == UINT8_MAX) { + return __builtin_umull_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT8_MAX == ULLONG_MAX + if (max == UINT8_MAX) { + return __builtin_umulll_overflow(lhs, rhs, res); + } +#endif + uint16_t big_result = (uint16_t)lhs * (uint16_t)rhs; + if (big_result > max) { + *res = big_result - max - 1; + return true; + } + *res = big_result; + return false; +} + +static inline uint16_t zig_mulo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint16_t max) { +#if defined(__GNUC__) && UINT16_MAX == UINT_MAX + if (max == UINT16_MAX) { + return __builtin_umul_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT16_MAX == ULONG_MAX + if (max == UINT16_MAX) { + return __builtin_umull_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT16_MAX == ULLONG_MAX + if (max == UINT16_MAX) { + return __builtin_umulll_overflow(lhs, rhs, res); + } +#endif + uint32_t big_result = (uint32_t)lhs * (uint32_t)rhs; + if (big_result > max) { + *res = big_result - max - 1; + return true; + } + *res = big_result; + return false; +} + +static inline uint32_t zig_mulo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint32_t max) { +#if defined(__GNUC__) && UINT32_MAX == UINT_MAX + if (max == UINT32_MAX) { + return __builtin_umul_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT32_MAX == ULONG_MAX + if (max == UINT32_MAX) { + return __builtin_umull_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT32_MAX == ULLONG_MAX + if (max == UINT32_MAX) { + return __builtin_umulll_overflow(lhs, rhs, res); + } +#endif + uint64_t big_result = (uint64_t)lhs * (uint64_t)rhs; + if (big_result > max) { + *res = big_result - max - 1; + return true; + } + *res = big_result; + return false; +} + +static inline uint64_t zig_mulo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint64_t max) { + bool overflow; +#if defined(__GNUC__) && UINT64_MAX == UINT_MAX + overflow = __builtin_umul_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT64_MAX == ULONG_MAX + overflow = __builtin_umull_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT64_MAX == ULLONG_MAX + overflow = __builtin_umulll_overflow(lhs, rhs, res); +#else + int int_overflow; + *res = __umulodi4(lhs, rhs, &int_overflow); + overflow = int_overflow != 0; +#endif + if (*res > max && !overflow) { + *res -= max - 1; + return true; + } + return overflow; +} + +static inline uint128_t zig_mulo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint128_t max) { + int overflow; + *res = __umuloti4(lhs, rhs, &overflow); + if (*res > max && overflow == 0) { + *res -= max - 1; + return true; + } + return overflow != 0; +} + +static inline bool zig_shlo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { + int16_t big_result = (int16_t)lhs << (int16_t)rhs; + if (big_result > max) { + *res = big_result - ((int16_t)max - (int16_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int16_t)max - (int16_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_shlo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t min, int16_t max) { + int32_t big_result = (int32_t)lhs << (int32_t)rhs; + if (big_result > max) { + *res = big_result - ((int32_t)max - (int32_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int32_t)max - (int32_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_shlo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t min, int32_t max) { + int64_t big_result = (int64_t)lhs << (int64_t)rhs; + if (big_result > max) { + *res = big_result - ((int64_t)max - (int64_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int64_t)max - (int64_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_shlo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t min, int64_t max) { + int overflow; + *res = __shlodi4(lhs, rhs, &overflow); + if (overflow == 0) { + if (*res > max) { + // TODO adjust the result to be the truncated bits + return true; + } else if (*res < min) { + // TODO adjust the result to be the truncated bits + return true; + } + } + return overflow != 0; +} + +static inline bool zig_shlo_i128(int128_t lhs, int128_t rhs, int128_t *res, int128_t min, int128_t max) { + int overflow; + *res = __shloti4(lhs, rhs, &overflow); + if (overflow == 0) { + if (*res > max) { + // TODO adjust the result to be the truncated bits + return true; + } else if (*res < min) { + // TODO adjust the result to be the truncated bits + return true; + } + } + return overflow != 0; +} + +static inline bool zig_shlo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t max) { + uint16_t big_result = (uint16_t)lhs << (uint16_t)rhs; + if (big_result > max) { + *res = big_result - max - 1; + return true; + } + *res = big_result; + return false; +} + +static inline uint16_t zig_shlo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint16_t max) { + uint32_t big_result = (uint32_t)lhs << (uint32_t)rhs; + if (big_result > max) { + *res = big_result - max - 1; + return true; + } + *res = big_result; + return false; +} + +static inline uint32_t zig_shlo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint32_t max) { + uint64_t big_result = (uint64_t)lhs << (uint64_t)rhs; + if (big_result > max) { + *res = big_result - max - 1; + return true; + } + *res = big_result; + return false; +} + +static inline uint64_t zig_shlo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint64_t max) { + int overflow; + *res = __ushlodi4(lhs, rhs, &overflow); + if (*res > max && overflow == 0) { + *res -= max - 1; + return true; + } + return overflow != 0; +} + +static inline uint128_t zig_shlo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint128_t max) { + int overflow; + *res = __ushloti4(lhs, rhs, &overflow); + if (*res > max && overflow == 0) { + *res -= max - 1; + return true; + } + return overflow != 0; +} + static inline float zig_bitcast_f32_u32(uint32_t arg) { float dest; memcpy(&dest, &arg, sizeof dest); From 316bf4fce5e069fa3ec1c2d9688e5b332eb4111c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 12 May 2022 19:34:09 -0700 Subject: [PATCH 1531/2031] disable 5 failing stage2_wasm tests --- test/behavior/union.zig | 2 ++ test/behavior/while.zig | 3 +++ 2 files changed, 5 insertions(+) diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 8315ea8a22..a8e280b258 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -212,6 +212,7 @@ test "union with specified enum tag" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; try doTest(); comptime try doTest(); @@ -221,6 +222,7 @@ test "packed union generates correctly aligned type" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage1) return error.SkipZigTest; const U = packed union { diff --git a/test/behavior/while.zig b/test/behavior/while.zig index 71f1d253e9..d447b876b7 100644 --- a/test/behavior/while.zig +++ b/test/behavior/while.zig @@ -146,6 +146,7 @@ test "while with optional as condition" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; numbers_left = 10; var sum: i32 = 0; @@ -159,6 +160,7 @@ test "while with optional as condition with else" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; numbers_left = 10; var sum: i32 = 0; @@ -177,6 +179,7 @@ test "while with error union condition" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; numbers_left = 10; var sum: i32 = 0; From b94d165b69f2743d779a04b1719382207e341596 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 13 May 2022 09:04:07 +0200 Subject: [PATCH 1532/2031] x64: fix capacity prealloc limit in lowerToMrEnc helper --- src/arch/x86_64/Emit.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 518635b806..57100abc0f 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -1896,7 +1896,7 @@ fn lowerToMrEnc( const opc = getOpCode(tag, .mr, reg.size() == 8 or reg_or_mem.size() == 8).?; switch (reg_or_mem) { .register => |dst_reg| { - const encoder = try Encoder.init(code, 3); + const encoder = try Encoder.init(code, 4); if (dst_reg.size() == 16) { encoder.prefix16BitMode(); } From 0a2d3d41556a3bbe836dafa5321439fa6da9b464 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 13 May 2022 19:51:14 +0200 Subject: [PATCH 1533/2031] wasm: Improve overflow add/sub for ints <= 64bits The implementation for add_with_overflow and sub_with_overflow is now a lot more robust and takes account for signed integers and arbitrary integer bitsizes. The final output is equal to that of the LLVM backend. --- src/arch/wasm/CodeGen.zig | 79 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 3 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 947174aaed..4bebf05e70 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1450,8 +1450,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .min => self.airMaxMin(inst, .min), .mul_add => self.airMulAdd(inst), - .add_with_overflow => self.airBinOpOverflow(inst, .add), - .sub_with_overflow => self.airBinOpOverflow(inst, .sub), + .add_with_overflow => self.airAddSubWithOverflow(inst, .add), + .sub_with_overflow => self.airAddSubWithOverflow(inst, .sub), .shl_with_overflow => self.airBinOpOverflow(inst, .shl), .mul_with_overflow => self.airMulWithOverflow(inst), @@ -3988,7 +3988,7 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue const cmp_res = try self.cmp(rhs, diff, lhs_ty, .gt); try self.emitWValue(cmp_res); try self.addLabel(.local_set, overflow_bit.local); - } else if (int_info.signedness == .unsigned and op == .sub) { + } else if (op == .sub) { const cmp_res = try self.cmp(lhs, rhs, lhs_ty, .lt); try self.emitWValue(cmp_res); try self.addLabel(.local_set, overflow_bit.local); @@ -4050,6 +4050,79 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue return result_ptr; } +fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { + assert(op == .add or op == .sub); + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; + const lhs_op = try self.resolveInst(extra.lhs); + const rhs_op = try self.resolveInst(extra.rhs); + const lhs_ty = self.air.typeOf(extra.lhs); + + if (lhs_ty.zigTypeTag() == .Vector) { + return self.fail("TODO: Implement overflow arithmetic for vectors", .{}); + } + + const int_info = lhs_ty.intInfo(self.target); + const is_signed = int_info.signedness == .signed; + const wasm_bits = toWasmBits(int_info.bits) orelse { + return self.fail("TODO: Implement sub_with_overflow for integer bitsize: {d}", .{int_info.bits}); + }; + + if (wasm_bits == 128) { + return self.fail("TODO: Implement sub_with_overflow for 128 bit integers", .{}); + } + + const zero = switch (wasm_bits) { + 32 => WValue{ .imm32 = 0 }, + 64 => WValue{ .imm64 = 0 }, + else => unreachable, + }; + const shift_amt = wasm_bits - int_info.bits; + const shift_val = switch (wasm_bits) { + 32 => WValue{ .imm32 = shift_amt }, + 64 => WValue{ .imm64 = shift_amt }, + else => unreachable, + }; + + // for signed integers, we first apply signed shifts by the difference in bits + // to get the signed value, as we store it internally as 2's complement. + const lhs = if (wasm_bits != int_info.bits and is_signed) blk: { + const shl = try self.binOp(lhs_op, shift_val, lhs_ty, .shl); + break :blk try self.binOp(shl, shift_val, lhs_ty, .shr); + } else lhs_op; + const rhs = if (wasm_bits != int_info.bits and is_signed) blk: { + const shl = try self.binOp(rhs_op, shift_val, lhs_ty, .shl); + break :blk try self.binOp(shl, shift_val, lhs_ty, .shr); + } else rhs_op; + + const bin_op = try self.binOp(lhs, rhs, lhs_ty, op); + const result = if (wasm_bits != int_info.bits) blk: { + break :blk try self.wrapOperand(bin_op, lhs_ty); + } else bin_op; + + const cmp_op: std.math.CompareOperator = if (op == .sub) .gt else .lt; + const overflow_bit: WValue = if (is_signed) blk: { + if (wasm_bits == int_info.bits) { + const cmp_zero = try self.cmp(rhs, zero, lhs_ty, cmp_op); + const lt = try self.cmp(bin_op, lhs, lhs_ty, .lt); + break :blk try self.binOp(cmp_zero, lt, Type.u32, .xor); // result of cmp_zero and lt is always 32bit + } + const shl = try self.binOp(bin_op, shift_val, lhs_ty, .shl); + const shr = try self.binOp(shl, shift_val, lhs_ty, .shr); + break :blk try self.cmp(shr, bin_op, lhs_ty, .neq); + } else if (wasm_bits == int_info.bits) + try self.cmp(bin_op, lhs, lhs_ty, cmp_op) + else + try self.cmp(bin_op, result, lhs_ty, .neq); + + const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); + try self.store(result_ptr, result, lhs_ty, 0); + const offset = @intCast(u32, lhs_ty.abiSize(self.target)); + try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset); + + return result_ptr; +} + fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; From 160aa4c11dcb0413796d08fd623ce7bbeabaf04b Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 13 May 2022 21:25:23 +0200 Subject: [PATCH 1534/2031] wasm: Improve shl_with_overflow This re-implements the shl_with_overflow operation from scratch, making it a lot more robust and outputs the equal code to the LLVM backend. --- src/arch/wasm/CodeGen.zig | 164 ++++++++++++-------------------------- test/behavior/union.zig | 2 - test/behavior/while.zig | 3 - 3 files changed, 49 insertions(+), 120 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 4bebf05e70..8e84b7d1fe 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1452,7 +1452,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .add_with_overflow => self.airAddSubWithOverflow(inst, .add), .sub_with_overflow => self.airAddSubWithOverflow(inst, .sub), - .shl_with_overflow => self.airBinOpOverflow(inst, .shl), + .shl_with_overflow => self.airShlWithOverflow(inst), .mul_with_overflow => self.airMulWithOverflow(inst), .clz => self.airClz(inst), @@ -3941,115 +3941,6 @@ fn airPtrSliceFieldPtr(self: *Self, inst: Air.Inst.Index, offset: u32) InnerErro return self.buildPointerOffset(slice_ptr, offset, .new); } -fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { - if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; - - const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - const lhs = try self.resolveInst(extra.lhs); - const rhs = try self.resolveInst(extra.rhs); - const lhs_ty = self.air.typeOf(extra.lhs); - - if (lhs_ty.zigTypeTag() == .Vector) { - return self.fail("TODO: Implement overflow arithmetic for vectors", .{}); - } - - // We store the bit if it's overflowed or not in this. As it's zero-initialized - // we only need to update it if an overflow (or underflow) occured. - const overflow_bit = try self.allocLocal(Type.initTag(.u1)); - const int_info = lhs_ty.intInfo(self.target); - const wasm_bits = toWasmBits(int_info.bits) orelse { - return self.fail("TODO: Implement overflow arithmetic for integer bitsize: {d}", .{int_info.bits}); - }; - - const zero = switch (wasm_bits) { - 32 => WValue{ .imm32 = 0 }, - 64 => WValue{ .imm64 = 0 }, - else => unreachable, - }; - const int_max = (@as(u65, 1) << @intCast(u7, int_info.bits - @boolToInt(int_info.signedness == .signed))) - 1; - const int_max_wvalue = switch (wasm_bits) { - 32 => WValue{ .imm32 = @intCast(u32, int_max) }, - 64 => WValue{ .imm64 = @intCast(u64, int_max) }, - else => unreachable, - }; - const int_min = if (int_info.signedness == .unsigned) - @as(i64, 0) - else - -@as(i64, 1) << @intCast(u6, int_info.bits - 1); - const int_min_wvalue = switch (wasm_bits) { - 32 => WValue{ .imm32 = @bitCast(u32, @intCast(i32, int_min)) }, - 64 => WValue{ .imm64 = @bitCast(u64, int_min) }, - else => unreachable, - }; - - if (int_info.signedness == .unsigned and op == .add) { - const diff = try self.binOp(int_max_wvalue, lhs, lhs_ty, .sub); - const cmp_res = try self.cmp(rhs, diff, lhs_ty, .gt); - try self.emitWValue(cmp_res); - try self.addLabel(.local_set, overflow_bit.local); - } else if (op == .sub) { - const cmp_res = try self.cmp(lhs, rhs, lhs_ty, .lt); - try self.emitWValue(cmp_res); - try self.addLabel(.local_set, overflow_bit.local); - } else if (int_info.signedness == .signed and op != .shl) { - // for overflow, we first check if lhs is > 0 (or lhs < 0 in case of subtraction). If not, we will not overflow. - // We first create an outer block, where we handle overflow. - // Then we create an inner block, where underflow is handled. - try self.startBlock(.block, wasm.block_empty); - try self.startBlock(.block, wasm.block_empty); - { - try self.emitWValue(lhs); - const cmp_result = try self.cmp(lhs, zero, lhs_ty, .lt); - try self.emitWValue(cmp_result); - } - try self.addLabel(.br_if, 0); // break to outer block, and handle underflow - - // handle overflow - { - const diff = try self.binOp(int_max_wvalue, lhs, lhs_ty, .sub); - const cmp_res = try self.cmp(rhs, diff, lhs_ty, if (op == .add) .gt else .lt); - try self.emitWValue(cmp_res); - try self.addLabel(.local_set, overflow_bit.local); - } - try self.addLabel(.br, 1); // break from blocks, and continue regular flow. - try self.endBlock(); - - // handle underflow - { - const diff = try self.binOp(int_min_wvalue, lhs, lhs_ty, .sub); - const cmp_res = try self.cmp(rhs, diff, lhs_ty, if (op == .add) .lt else .gt); - try self.emitWValue(cmp_res); - try self.addLabel(.local_set, overflow_bit.local); - } - try self.endBlock(); - } - - const bin_op = if (op == .shl) blk: { - const tmp_val = try self.binOp(lhs, rhs, lhs_ty, op); - const cmp_res = try self.cmp(tmp_val, int_max_wvalue, lhs_ty, .gt); - try self.emitWValue(cmp_res); - try self.addLabel(.local_set, overflow_bit.local); - - try self.emitWValue(tmp_val); - try self.emitWValue(int_max_wvalue); - switch (wasm_bits) { - 32 => try self.addTag(.i32_and), - 64 => try self.addTag(.i64_and), - else => unreachable, - } - try self.addLabel(.local_set, tmp_val.local); - break :blk tmp_val; - } else try self.wrapBinOp(lhs, rhs, lhs_ty, op); - - const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); - try self.store(result_ptr, bin_op, lhs_ty, 0); - const offset = @intCast(u32, lhs_ty.abiSize(self.target)); - try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset); - - return result_ptr; -} - fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { assert(op == .add or op == .sub); const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; @@ -4065,13 +3956,9 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W const int_info = lhs_ty.intInfo(self.target); const is_signed = int_info.signedness == .signed; const wasm_bits = toWasmBits(int_info.bits) orelse { - return self.fail("TODO: Implement sub_with_overflow for integer bitsize: {d}", .{int_info.bits}); + return self.fail("TODO: Implement {{add/sub}}_with_overflow for integer bitsize: {d}", .{int_info.bits}); }; - if (wasm_bits == 128) { - return self.fail("TODO: Implement sub_with_overflow for 128 bit integers", .{}); - } - const zero = switch (wasm_bits) { 32 => WValue{ .imm32 = 0 }, 64 => WValue{ .imm64 = 0 }, @@ -4123,6 +4010,53 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W return result_ptr; } +fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; + const lhs = try self.resolveInst(extra.lhs); + const rhs = try self.resolveInst(extra.rhs); + const lhs_ty = self.air.typeOf(extra.lhs); + + if (lhs_ty.zigTypeTag() == .Vector) { + return self.fail("TODO: Implement overflow arithmetic for vectors", .{}); + } + + const int_info = lhs_ty.intInfo(self.target); + const is_signed = int_info.signedness == .signed; + const wasm_bits = toWasmBits(int_info.bits) orelse { + return self.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits}); + }; + + const shl = try self.binOp(lhs, rhs, lhs_ty, .shl); + const result = if (wasm_bits != int_info.bits) blk: { + break :blk try self.wrapOperand(shl, lhs_ty); + } else shl; + + const overflow_bit = if (wasm_bits != int_info.bits and is_signed) blk: { + const shift_amt = wasm_bits - int_info.bits; + const shift_val = switch (wasm_bits) { + 32 => WValue{ .imm32 = shift_amt }, + 64 => WValue{ .imm64 = shift_amt }, + else => unreachable, + }; + + const secondary_shl = try self.binOp(shl, shift_val, lhs_ty, .shl); + const initial_shr = try self.binOp(secondary_shl, shift_val, lhs_ty, .shr); + const shr = try self.wrapBinOp(initial_shr, rhs, lhs_ty, .shr); + break :blk try self.cmp(lhs, shr, lhs_ty, .neq); + } else blk: { + const shr = try self.binOp(result, rhs, lhs_ty, .shr); + break :blk try self.cmp(lhs, shr, lhs_ty, .neq); + }; + + const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); + try self.store(result_ptr, result, lhs_ty, 0); + const offset = @intCast(u32, lhs_ty.abiSize(self.target)); + try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset); + + return result_ptr; +} + fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; diff --git a/test/behavior/union.zig b/test/behavior/union.zig index a8e280b258..8315ea8a22 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -212,7 +212,6 @@ test "union with specified enum tag" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; try doTest(); comptime try doTest(); @@ -222,7 +221,6 @@ test "packed union generates correctly aligned type" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage1) return error.SkipZigTest; const U = packed union { diff --git a/test/behavior/while.zig b/test/behavior/while.zig index d447b876b7..71f1d253e9 100644 --- a/test/behavior/while.zig +++ b/test/behavior/while.zig @@ -146,7 +146,6 @@ test "while with optional as condition" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; numbers_left = 10; var sum: i32 = 0; @@ -160,7 +159,6 @@ test "while with optional as condition with else" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; numbers_left = 10; var sum: i32 = 0; @@ -179,7 +177,6 @@ test "while with error union condition" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; numbers_left = 10; var sum: i32 = 0; From a84be7e988c91606bc42e1e1c8a34bbdcdb8a3f1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 May 2022 21:06:53 -0700 Subject: [PATCH 1535/2031] zig.h: improve overflow shl * zig_addo_u128: fix type-o * redo the shift-left overflow inline functions. no need to depend on compiler-rt. --- src/link/C/zig.h | 198 +++++++++++++++++------------------------------ 1 file changed, 73 insertions(+), 125 deletions(-) diff --git a/src/link/C/zig.h b/src/link/C/zig.h index 6bafee987b..e3a0447c05 100644 --- a/src/link/C/zig.h +++ b/src/link/C/zig.h @@ -633,13 +633,13 @@ static inline uint64_t zig_addo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, u } static inline uint128_t zig_addo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint128_t max) { - bool overflow; + int overflow; *res = __uaddoti4(lhs, rhs, &overflow); - if (*res > max && !overflow) { + if (*res > max && overflow == 0) { *res -= max - 1; return true; } - return overflow; + return overflow != 0; } static inline bool zig_subo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { @@ -1095,128 +1095,6 @@ static inline uint128_t zig_mulo_u128(uint128_t lhs, uint128_t rhs, uint128_t *r return overflow != 0; } -static inline bool zig_shlo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { - int16_t big_result = (int16_t)lhs << (int16_t)rhs; - if (big_result > max) { - *res = big_result - ((int16_t)max - (int16_t)min); - return true; - } - if (big_result < min) { - *res = big_result + ((int16_t)max - (int16_t)min); - return true; - } - *res = big_result; - return false; -} - -static inline bool zig_shlo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t min, int16_t max) { - int32_t big_result = (int32_t)lhs << (int32_t)rhs; - if (big_result > max) { - *res = big_result - ((int32_t)max - (int32_t)min); - return true; - } - if (big_result < min) { - *res = big_result + ((int32_t)max - (int32_t)min); - return true; - } - *res = big_result; - return false; -} - -static inline bool zig_shlo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t min, int32_t max) { - int64_t big_result = (int64_t)lhs << (int64_t)rhs; - if (big_result > max) { - *res = big_result - ((int64_t)max - (int64_t)min); - return true; - } - if (big_result < min) { - *res = big_result + ((int64_t)max - (int64_t)min); - return true; - } - *res = big_result; - return false; -} - -static inline bool zig_shlo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t min, int64_t max) { - int overflow; - *res = __shlodi4(lhs, rhs, &overflow); - if (overflow == 0) { - if (*res > max) { - // TODO adjust the result to be the truncated bits - return true; - } else if (*res < min) { - // TODO adjust the result to be the truncated bits - return true; - } - } - return overflow != 0; -} - -static inline bool zig_shlo_i128(int128_t lhs, int128_t rhs, int128_t *res, int128_t min, int128_t max) { - int overflow; - *res = __shloti4(lhs, rhs, &overflow); - if (overflow == 0) { - if (*res > max) { - // TODO adjust the result to be the truncated bits - return true; - } else if (*res < min) { - // TODO adjust the result to be the truncated bits - return true; - } - } - return overflow != 0; -} - -static inline bool zig_shlo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t max) { - uint16_t big_result = (uint16_t)lhs << (uint16_t)rhs; - if (big_result > max) { - *res = big_result - max - 1; - return true; - } - *res = big_result; - return false; -} - -static inline uint16_t zig_shlo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint16_t max) { - uint32_t big_result = (uint32_t)lhs << (uint32_t)rhs; - if (big_result > max) { - *res = big_result - max - 1; - return true; - } - *res = big_result; - return false; -} - -static inline uint32_t zig_shlo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint32_t max) { - uint64_t big_result = (uint64_t)lhs << (uint64_t)rhs; - if (big_result > max) { - *res = big_result - max - 1; - return true; - } - *res = big_result; - return false; -} - -static inline uint64_t zig_shlo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint64_t max) { - int overflow; - *res = __ushlodi4(lhs, rhs, &overflow); - if (*res > max && overflow == 0) { - *res -= max - 1; - return true; - } - return overflow != 0; -} - -static inline uint128_t zig_shlo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint128_t max) { - int overflow; - *res = __ushloti4(lhs, rhs, &overflow); - if (*res > max && overflow == 0) { - *res -= max - 1; - return true; - } - return overflow != 0; -} - static inline float zig_bitcast_f32_u32(uint32_t arg) { float dest; memcpy(&dest, &arg, sizeof dest); @@ -1429,6 +1307,76 @@ static inline int zig_popcount_u128(uint128_t value, uint8_t zig_type_bit_width) #define zig_popcount_i128 zig_popcount_u128 +static inline bool zig_shlo_i8(int8_t lhs, int8_t rhs, int8_t *res, uint8_t bits) { + *res = lhs << rhs; + if (zig_clz_i8(lhs, bits) >= rhs) return false; + *res &= UINT8_MAX >> (8 - bits); + return true; +} + +static inline bool zig_shlo_i16(int16_t lhs, int16_t rhs, int16_t *res, uint8_t bits) { + *res = lhs << rhs; + if (zig_clz_i16(lhs, bits) >= rhs) return false; + *res &= UINT16_MAX >> (16 - bits); + return true; +} + +static inline bool zig_shlo_i32(int32_t lhs, int32_t rhs, int32_t *res, uint8_t bits) { + *res = lhs << rhs; + if (zig_clz_i32(lhs, bits) >= rhs) return false; + *res &= UINT32_MAX >> (32 - bits); + return true; +} + +static inline bool zig_shlo_i64(int64_t lhs, int64_t rhs, int64_t *res, uint8_t bits) { + *res = lhs << rhs; + if (zig_clz_i64(lhs, bits) >= rhs) return false; + *res &= UINT64_MAX >> (64 - bits); + return true; +} + +static inline bool zig_shlo_i128(int128_t lhs, int128_t rhs, int128_t *res, uint8_t bits) { + *res = lhs << rhs; + if (zig_clz_i128(lhs, bits) >= rhs) return false; + *res &= UINT128_MAX >> (128 - bits); + return true; +} + +static inline bool zig_shlo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t bits) { + *res = lhs << rhs; + if (zig_clz_u8(lhs, bits) >= rhs) return false; + *res &= UINT8_MAX >> (8 - bits); + return true; +} + +static inline uint16_t zig_shlo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint8_t bits) { + *res = lhs << rhs; + if (zig_clz_u16(lhs, bits) >= rhs) return false; + *res &= UINT16_MAX >> (16 - bits); + return true; +} + +static inline uint32_t zig_shlo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint8_t bits) { + *res = lhs << rhs; + if (zig_clz_u32(lhs, bits) >= rhs) return false; + *res &= UINT32_MAX >> (32 - bits); + return true; +} + +static inline uint64_t zig_shlo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint8_t bits) { + *res = lhs << rhs; + if (zig_clz_u64(lhs, bits) >= rhs) return false; + *res &= UINT64_MAX >> (64 - bits); + return true; +} + +static inline uint128_t zig_shlo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint8_t bits) { + *res = lhs << rhs; + if (zig_clz_u128(lhs, bits) >= rhs) return false; + *res &= UINT128_MAX >> (128 - bits); + return true; +} + #define zig_sign_extend(T) \ static inline T zig_sign_extend_##T(T value, uint8_t zig_type_bit_width) { \ const T m = (T)1 << (T)(zig_type_bit_width - 1); \ From 852c82084163eec9911384b325dbd5713ee4df90 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 14 May 2022 21:24:48 +0200 Subject: [PATCH 1536/2031] aarch64: sub_with_overflow should always track V flag --- src/arch/aarch64/CodeGen.zig | 4 ++ test/behavior/math.zig | 85 +++++++++++++++++++++++++----------- 2 files changed, 64 insertions(+), 25 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index e43cbca1c7..10730c446f 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -1901,6 +1901,10 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { } }; + if (tag == .sub_with_overflow) { + break :result MCValue{ .register_v_flag = dest.register }; + } + switch (int_info.signedness) { .unsigned => break :result MCValue{ .register_c_flag = dest.register }, .signed => break :result MCValue{ .register_v_flag = dest.register }, diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 011c714935..2f8cf06ee7 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -621,24 +621,41 @@ test "128-bit multiplication" { test "@addWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - var result: u8 = undefined; - try expect(@addWithOverflow(u8, 250, 100, &result)); - try expect(result == 94); - try expect(!@addWithOverflow(u8, 100, 150, &result)); - try expect(result == 250); + { + var result: u8 = undefined; + try expect(@addWithOverflow(u8, 250, 100, &result)); + try expect(result == 94); + try expect(!@addWithOverflow(u8, 100, 150, &result)); + try expect(result == 250); - var a: u8 = 200; - var b: u8 = 99; - try expect(@addWithOverflow(u8, a, b, &result)); - try expect(result == 43); - b = 55; - try expect(!@addWithOverflow(u8, a, b, &result)); - try expect(result == 255); + var a: u8 = 200; + var b: u8 = 99; + try expect(@addWithOverflow(u8, a, b, &result)); + try expect(result == 43); + b = 55; + try expect(!@addWithOverflow(u8, a, b, &result)); + try expect(result == 255); + } + + { + var a: usize = 6; + var b: usize = 6; + var res: usize = undefined; + try expect(!@addWithOverflow(usize, a, b, &res)); + try expect(res == 12); + } + + { + var a: isize = -6; + var b: isize = -6; + var res: isize = undefined; + try expect(!@addWithOverflow(isize, a, b, &res)); + try expect(res == -12); + } } test "small int addition" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO var x: u2 = 0; @@ -886,19 +903,37 @@ test "@mulWithOverflow bitsize > 32" { test "@subWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - var result: u8 = undefined; - try expect(@subWithOverflow(u8, 1, 2, &result)); - try expect(result == 255); - try expect(!@subWithOverflow(u8, 1, 1, &result)); - try expect(result == 0); + { + var result: u8 = undefined; + try expect(@subWithOverflow(u8, 1, 2, &result)); + try expect(result == 255); + try expect(!@subWithOverflow(u8, 1, 1, &result)); + try expect(result == 0); - var a: u8 = 1; - var b: u8 = 2; - try expect(@subWithOverflow(u8, a, b, &result)); - try expect(result == 255); - b = 1; - try expect(!@subWithOverflow(u8, a, b, &result)); - try expect(result == 0); + var a: u8 = 1; + var b: u8 = 2; + try expect(@subWithOverflow(u8, a, b, &result)); + try expect(result == 255); + b = 1; + try expect(!@subWithOverflow(u8, a, b, &result)); + try expect(result == 0); + } + + { + var a: usize = 6; + var b: usize = 6; + var res: usize = undefined; + try expect(!@subWithOverflow(usize, a, b, &res)); + try expect(res == 0); + } + + { + var a: isize = -6; + var b: isize = -6; + var res: isize = undefined; + try expect(!@subWithOverflow(isize, a, b, &res)); + try expect(res == 0); + } } test "@shlWithOverflow" { From 7f96ca101aec59e0c5508939c9ccc783d3898c2a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 14 May 2022 22:07:24 +0200 Subject: [PATCH 1537/2031] arm: sub_with_overflow should always track V flag --- src/arch/arm/CodeGen.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 02ca66f297..93e6e95ba9 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1455,6 +1455,10 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { } }; + if (tag == .sub_with_overflow) { + break :result MCValue{ .register_v_flag = dest.register }; + } + switch (int_info.signedness) { .unsigned => break :result MCValue{ .register_c_flag = dest.register }, .signed => break :result MCValue{ .register_v_flag = dest.register }, From a0de0adb8e22222716d4d42b26490461ecd67de3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 14 May 2022 22:25:04 +0200 Subject: [PATCH 1538/2031] arm: disable recursive fibonacci --- test/cases/recursive_fibonacci.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/recursive_fibonacci.zig b/test/cases/recursive_fibonacci.zig index 4e284e3fc1..a2b8436dd7 100644 --- a/test/cases/recursive_fibonacci.zig +++ b/test/cases/recursive_fibonacci.zig @@ -20,5 +20,5 @@ fn assert(ok: bool) void { } // run -// target=arm-linux,x86_64-linux,x86_64-macos,wasm32-wasi +// target=x86_64-linux,x86_64-macos,wasm32-wasi // From f33b3fc3eae54b9d1159fc5a7a69a4b0e4aceca6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 May 2022 14:30:28 -0700 Subject: [PATCH 1539/2031] zig.h: add casts for overflow arithmetic operations This avoids the following error: ``` error: incompatible pointer types passing 'int64_t *' (aka 'long long *') to parameter of type 'long *' overflow = __builtin_saddl_overflow(lhs, rhs, res); ^~~ ``` My previous understanding was that this error would not occur because prior to this line we check that int64_t is equivalent to long, like this: ```c ``` However, it appears that this is still a warning in C if int64_t is primarily aliased to `long long`, even though `long` and `long long` are the same thing. --- src/link/C/zig.h | 162 +++++++++++++++++++++++------------------------ 1 file changed, 81 insertions(+), 81 deletions(-) diff --git a/src/link/C/zig.h b/src/link/C/zig.h index e3a0447c05..43d9913039 100644 --- a/src/link/C/zig.h +++ b/src/link/C/zig.h @@ -415,15 +415,15 @@ static inline long long zig_subw_longlong(long long lhs, long long rhs, long lon static inline bool zig_addo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { #if defined(__GNUC__) && INT8_MAX == INT_MAX if (min == INT8_MIN && max == INT8_MAX) { - return __builtin_sadd_overflow(lhs, rhs, res); + return __builtin_sadd_overflow(lhs, rhs, (int*)res); } #elif defined(__GNUC__) && INT8_MAX == LONG_MAX if (min == INT8_MIN && max == INT8_MAX) { - return __builtin_saddl_overflow(lhs, rhs, res); + return __builtin_saddl_overflow(lhs, rhs, (long*)res); } #elif defined(__GNUC__) && INT8_MAX == LLONG_MAX if (min == INT8_MIN && max == INT8_MAX) { - return __builtin_saddll_overflow(lhs, rhs, res); + return __builtin_saddll_overflow(lhs, rhs, (long long*)res); } #endif int16_t big_result = (int16_t)lhs + (int16_t)rhs; @@ -442,15 +442,15 @@ static inline bool zig_addo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, static inline bool zig_addo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t min, int16_t max) { #if defined(__GNUC__) && INT16_MAX == INT_MAX if (min == INT16_MIN && max == INT16_MAX) { - return __builtin_sadd_overflow(lhs, rhs, res); + return __builtin_sadd_overflow(lhs, rhs, (int*)res); } #elif defined(__GNUC__) && INT16_MAX == LONG_MAX if (min == INT16_MIN && max == INT16_MAX) { - return __builtin_saddl_overflow(lhs, rhs, res); + return __builtin_saddl_overflow(lhs, rhs, (long*)res); } #elif defined(__GNUC__) && INT16_MAX == LLONG_MAX if (min == INT16_MIN && max == INT16_MAX) { - return __builtin_saddll_overflow(lhs, rhs, res); + return __builtin_saddll_overflow(lhs, rhs, (long long*)res); } #endif int32_t big_result = (int32_t)lhs + (int32_t)rhs; @@ -469,15 +469,15 @@ static inline bool zig_addo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t static inline bool zig_addo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t min, int32_t max) { #if defined(__GNUC__) && INT32_MAX == INT_MAX if (min == INT32_MIN && max == INT32_MAX) { - return __builtin_sadd_overflow(lhs, rhs, res); + return __builtin_sadd_overflow(lhs, rhs, (int*)res); } #elif defined(__GNUC__) && INT32_MAX == LONG_MAX if (min == INT32_MIN && max == INT32_MAX) { - return __builtin_saddl_overflow(lhs, rhs, res); + return __builtin_saddl_overflow(lhs, rhs, (long*)res); } #elif defined(__GNUC__) && INT32_MAX == LLONG_MAX if (min == INT32_MIN && max == INT32_MAX) { - return __builtin_saddll_overflow(lhs, rhs, res); + return __builtin_saddll_overflow(lhs, rhs, (long long*)res); } #endif int64_t big_result = (int64_t)lhs + (int64_t)rhs; @@ -496,11 +496,11 @@ static inline bool zig_addo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t static inline bool zig_addo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t min, int64_t max) { bool overflow; #if defined(__GNUC__) && INT64_MAX == INT_MAX - overflow = __builtin_sadd_overflow(lhs, rhs, res); + overflow = __builtin_sadd_overflow(lhs, rhs, (int*)res); #elif defined(__GNUC__) && INT64_MAX == LONG_MAX - overflow = __builtin_saddl_overflow(lhs, rhs, res); + overflow = __builtin_saddl_overflow(lhs, rhs, (long*)res); #elif defined(__GNUC__) && INT64_MAX == LLONG_MAX - overflow = __builtin_saddll_overflow(lhs, rhs, res); + overflow = __builtin_saddll_overflow(lhs, rhs, (long long*)res); #else int int_overflow; *res = __addodi4(lhs, rhs, &int_overflow); @@ -521,11 +521,11 @@ static inline bool zig_addo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t static inline bool zig_addo_i128(int128_t lhs, int128_t rhs, int128_t *res, int128_t min, int128_t max) { bool overflow; #if defined(__GNUC__) && INT128_MAX == INT_MAX - overflow = __builtin_sadd_overflow(lhs, rhs, res); + overflow = __builtin_sadd_overflow(lhs, rhs, (int*)res); #elif defined(__GNUC__) && INT128_MAX == LONG_MAX - overflow = __builtin_saddl_overflow(lhs, rhs, res); + overflow = __builtin_saddl_overflow(lhs, rhs, (long*)res); #elif defined(__GNUC__) && INT128_MAX == LLONG_MAX - overflow = __builtin_saddll_overflow(lhs, rhs, res); + overflow = __builtin_saddll_overflow(lhs, rhs, (long long*)res); #else int int_overflow; *res = __addoti4(lhs, rhs, &int_overflow); @@ -546,15 +546,15 @@ static inline bool zig_addo_i128(int128_t lhs, int128_t rhs, int128_t *res, int1 static inline bool zig_addo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t max) { #if defined(__GNUC__) && UINT8_MAX == UINT_MAX if (max == UINT8_MAX) { - return __builtin_uadd_overflow(lhs, rhs, res); + return __builtin_uadd_overflow(lhs, rhs, (unsigned int*)res); } #elif defined(__GNUC__) && UINT8_MAX == ULONG_MAX if (max == UINT8_MAX) { - return __builtin_uaddl_overflow(lhs, rhs, res); + return __builtin_uaddl_overflow(lhs, rhs, (unsigned long*)res); } #elif defined(__GNUC__) && UINT8_MAX == ULLONG_MAX if (max == UINT8_MAX) { - return __builtin_uaddll_overflow(lhs, rhs, res); + return __builtin_uaddll_overflow(lhs, rhs, (unsigned long long*)res); } #endif uint16_t big_result = (uint16_t)lhs + (uint16_t)rhs; @@ -569,15 +569,15 @@ static inline bool zig_addo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t m static inline uint16_t zig_addo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint16_t max) { #if defined(__GNUC__) && UINT16_MAX == UINT_MAX if (max == UINT16_MAX) { - return __builtin_uadd_overflow(lhs, rhs, res); + return __builtin_uadd_overflow(lhs, rhs, (unsigned int*)res); } #elif defined(__GNUC__) && UINT16_MAX == ULONG_MAX if (max == UINT16_MAX) { - return __builtin_uaddl_overflow(lhs, rhs, res); + return __builtin_uaddl_overflow(lhs, rhs, (unsigned long*)res); } #elif defined(__GNUC__) && UINT16_MAX == ULLONG_MAX if (max == UINT16_MAX) { - return __builtin_uaddll_overflow(lhs, rhs, res); + return __builtin_uaddll_overflow(lhs, rhs, (unsigned long long*)res); } #endif uint32_t big_result = (uint32_t)lhs + (uint32_t)rhs; @@ -592,15 +592,15 @@ static inline uint16_t zig_addo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, u static inline uint32_t zig_addo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint32_t max) { #if defined(__GNUC__) && UINT32_MAX == UINT_MAX if (max == UINT32_MAX) { - return __builtin_uadd_overflow(lhs, rhs, res); + return __builtin_uadd_overflow(lhs, rhs, (unsigned int*)res); } #elif defined(__GNUC__) && UINT32_MAX == ULONG_MAX if (max == UINT32_MAX) { - return __builtin_uaddl_overflow(lhs, rhs, res); + return __builtin_uaddl_overflow(lhs, rhs, (unsigned long*)res); } #elif defined(__GNUC__) && UINT32_MAX == ULLONG_MAX if (max == UINT32_MAX) { - return __builtin_uaddll_overflow(lhs, rhs, res); + return __builtin_uaddll_overflow(lhs, rhs, (unsigned long long*)res); } #endif uint64_t big_result = (uint64_t)lhs + (uint64_t)rhs; @@ -615,11 +615,11 @@ static inline uint32_t zig_addo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, u static inline uint64_t zig_addo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint64_t max) { bool overflow; #if defined(__GNUC__) && UINT64_MAX == UINT_MAX - overflow = __builtin_uadd_overflow(lhs, rhs, res); + overflow = __builtin_uadd_overflow(lhs, rhs, (unsigned int*)res); #elif defined(__GNUC__) && UINT64_MAX == ULONG_MAX - overflow = __builtin_uaddl_overflow(lhs, rhs, res); + overflow = __builtin_uaddl_overflow(lhs, rhs, (unsigned long*)res); #elif defined(__GNUC__) && UINT64_MAX == ULLONG_MAX - overflow = __builtin_uaddll_overflow(lhs, rhs, res); + overflow = __builtin_uaddll_overflow(lhs, rhs, (unsigned long long*)res); #else int int_overflow; *res = __uaddodi4(lhs, rhs, &int_overflow); @@ -645,15 +645,15 @@ static inline uint128_t zig_addo_u128(uint128_t lhs, uint128_t rhs, uint128_t *r static inline bool zig_subo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { #if defined(__GNUC__) && INT8_MAX == INT_MAX if (min == INT8_MIN && max == INT8_MAX) { - return __builtin_ssub_overflow(lhs, rhs, res); + return __builtin_ssub_overflow(lhs, rhs, (int*)res); } #elif defined(__GNUC__) && INT8_MAX == LONG_MAX if (min == INT8_MIN && max == INT8_MAX) { - return __builtin_ssubl_overflow(lhs, rhs, res); + return __builtin_ssubl_overflow(lhs, rhs, (long*)res); } #elif defined(__GNUC__) && INT8_MAX == LLONG_MAX if (min == INT8_MIN && max == INT8_MAX) { - return __builtin_ssubll_overflow(lhs, rhs, res); + return __builtin_ssubll_overflow(lhs, rhs, (long long*)res); } #endif int16_t big_result = (int16_t)lhs - (int16_t)rhs; @@ -672,15 +672,15 @@ static inline bool zig_subo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, static inline bool zig_subo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t min, int16_t max) { #if defined(__GNUC__) && INT16_MAX == INT_MAX if (min == INT16_MIN && max == INT16_MAX) { - return __builtin_ssub_overflow(lhs, rhs, res); + return __builtin_ssub_overflow(lhs, rhs, (int*)res); } #elif defined(__GNUC__) && INT16_MAX == LONG_MAX if (min == INT16_MIN && max == INT16_MAX) { - return __builtin_ssubl_overflow(lhs, rhs, res); + return __builtin_ssubl_overflow(lhs, rhs, (long*)res); } #elif defined(__GNUC__) && INT16_MAX == LLONG_MAX if (min == INT16_MIN && max == INT16_MAX) { - return __builtin_ssubll_overflow(lhs, rhs, res); + return __builtin_ssubll_overflow(lhs, rhs, (long long*)res); } #endif int32_t big_result = (int32_t)lhs - (int32_t)rhs; @@ -699,15 +699,15 @@ static inline bool zig_subo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t static inline bool zig_subo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t min, int32_t max) { #if defined(__GNUC__) && INT32_MAX == INT_MAX if (min == INT32_MIN && max == INT32_MAX) { - return __builtin_ssub_overflow(lhs, rhs, res); + return __builtin_ssub_overflow(lhs, rhs, (int*)res); } #elif defined(__GNUC__) && INT32_MAX == LONG_MAX if (min == INT32_MIN && max == INT32_MAX) { - return __builtin_ssubl_overflow(lhs, rhs, res); + return __builtin_ssubl_overflow(lhs, rhs, (long*)res); } #elif defined(__GNUC__) && INT32_MAX == LLONG_MAX if (min == INT32_MIN && max == INT32_MAX) { - return __builtin_ssubll_overflow(lhs, rhs, res); + return __builtin_ssubll_overflow(lhs, rhs, (long long*)res); } #endif int64_t big_result = (int64_t)lhs - (int64_t)rhs; @@ -726,11 +726,11 @@ static inline bool zig_subo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t static inline bool zig_subo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t min, int64_t max) { bool overflow; #if defined(__GNUC__) && INT64_MAX == INT_MAX - overflow = __builtin_ssub_overflow(lhs, rhs, res); + overflow = __builtin_ssub_overflow(lhs, rhs, (int*)res); #elif defined(__GNUC__) && INT64_MAX == LONG_MAX - overflow = __builtin_ssubl_overflow(lhs, rhs, res); + overflow = __builtin_ssubl_overflow(lhs, rhs, (long*)res); #elif defined(__GNUC__) && INT64_MAX == LLONG_MAX - overflow = __builtin_ssubll_overflow(lhs, rhs, res); + overflow = __builtin_ssubll_overflow(lhs, rhs, (long long*)res); #else int int_overflow; *res = __subodi4(lhs, rhs, &int_overflow); @@ -751,11 +751,11 @@ static inline bool zig_subo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t static inline bool zig_subo_i128(int128_t lhs, int128_t rhs, int128_t *res, int128_t min, int128_t max) { bool overflow; #if defined(__GNUC__) && INT128_MAX == INT_MAX - overflow = __builtin_ssub_overflow(lhs, rhs, res); + overflow = __builtin_ssub_overflow(lhs, rhs, (int*)res); #elif defined(__GNUC__) && INT128_MAX == LONG_MAX - overflow = __builtin_ssubl_overflow(lhs, rhs, res); + overflow = __builtin_ssubl_overflow(lhs, rhs, (long*)res); #elif defined(__GNUC__) && INT128_MAX == LLONG_MAX - overflow = __builtin_ssubll_overflow(lhs, rhs, res); + overflow = __builtin_ssubll_overflow(lhs, rhs, (long long*)res); #else int int_overflow; *res = __suboti4(lhs, rhs, &int_overflow); @@ -775,11 +775,11 @@ static inline bool zig_subo_i128(int128_t lhs, int128_t rhs, int128_t *res, int1 static inline bool zig_subo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t max) { #if defined(__GNUC__) && UINT8_MAX == UINT_MAX - return __builtin_usub_overflow(lhs, rhs, res); + return __builtin_usub_overflow(lhs, rhs, (unsigned int*)res); #elif defined(__GNUC__) && UINT8_MAX == ULONG_MAX - return __builtin_usubl_overflow(lhs, rhs, res); + return __builtin_usubl_overflow(lhs, rhs, (unsigned long*)res); #elif defined(__GNUC__) && UINT8_MAX == ULLONG_MAX - return __builtin_usubll_overflow(lhs, rhs, res); + return __builtin_usubll_overflow(lhs, rhs, (unsigned long long*)res); #endif if (rhs > lhs) { *res = max - (rhs - lhs - 1); @@ -791,11 +791,11 @@ static inline bool zig_subo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t m static inline uint16_t zig_subo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint16_t max) { #if defined(__GNUC__) && UINT16_MAX == UINT_MAX - return __builtin_usub_overflow(lhs, rhs, res); + return __builtin_usub_overflow(lhs, rhs, (unsigned int*)res); #elif defined(__GNUC__) && UINT16_MAX == ULONG_MAX - return __builtin_usubl_overflow(lhs, rhs, res); + return __builtin_usubl_overflow(lhs, rhs, (unsigned long*)res); #elif defined(__GNUC__) && UINT16_MAX == ULLONG_MAX - return __builtin_usubll_overflow(lhs, rhs, res); + return __builtin_usubll_overflow(lhs, rhs, (unsigned long long*)res); #endif if (rhs > lhs) { *res = max - (rhs - lhs - 1); @@ -808,11 +808,11 @@ static inline uint16_t zig_subo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, u static inline uint32_t zig_subo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint32_t max) { if (max == UINT32_MAX) { #if defined(__GNUC__) && UINT32_MAX == UINT_MAX - return __builtin_usub_overflow(lhs, rhs, res); + return __builtin_usub_overflow(lhs, rhs, (unsigned int*)res); #elif defined(__GNUC__) && UINT32_MAX == ULONG_MAX - return __builtin_usubl_overflow(lhs, rhs, res); + return __builtin_usubl_overflow(lhs, rhs, (unsigned long*)res); #elif defined(__GNUC__) && UINT32_MAX == ULLONG_MAX - return __builtin_usubll_overflow(lhs, rhs, res); + return __builtin_usubll_overflow(lhs, rhs, (unsigned long long*)res); #endif int int_overflow; *res = __usubosi4(lhs, rhs, &int_overflow); @@ -830,11 +830,11 @@ static inline uint32_t zig_subo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, u static inline uint64_t zig_subo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint64_t max) { if (max == UINT64_MAX) { #if defined(__GNUC__) && UINT64_MAX == UINT_MAX - return __builtin_usub_overflow(lhs, rhs, res); + return __builtin_usub_overflow(lhs, rhs, (unsigned int*)res); #elif defined(__GNUC__) && UINT64_MAX == ULONG_MAX - return __builtin_usubl_overflow(lhs, rhs, res); + return __builtin_usubl_overflow(lhs, rhs, (unsigned long*)res); #elif defined(__GNUC__) && UINT64_MAX == ULLONG_MAX - return __builtin_usubll_overflow(lhs, rhs, res); + return __builtin_usubll_overflow(lhs, rhs, (unsigned long long*)res); #else int int_overflow; *res = __usubodi4(lhs, rhs, &int_overflow); @@ -868,15 +868,15 @@ static inline uint128_t zig_subo_u128(uint128_t lhs, uint128_t rhs, uint128_t *r static inline bool zig_mulo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { #if defined(__GNUC__) && INT8_MAX == INT_MAX if (min == INT8_MIN && max == INT8_MAX) { - return __builtin_smul_overflow(lhs, rhs, res); + return __builtin_smul_overflow(lhs, rhs, (int*)res); } #elif defined(__GNUC__) && INT8_MAX == LONG_MAX if (min == INT8_MIN && max == INT8_MAX) { - return __builtin_smull_overflow(lhs, rhs, res); + return __builtin_smull_overflow(lhs, rhs, (long*)res); } #elif defined(__GNUC__) && INT8_MAX == LLONG_MAX if (min == INT8_MIN && max == INT8_MAX) { - return __builtin_smulll_overflow(lhs, rhs, res); + return __builtin_smulll_overflow(lhs, rhs, (long long*)res); } #endif int16_t big_result = (int16_t)lhs * (int16_t)rhs; @@ -895,15 +895,15 @@ static inline bool zig_mulo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, static inline bool zig_mulo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t min, int16_t max) { #if defined(__GNUC__) && INT16_MAX == INT_MAX if (min == INT16_MIN && max == INT16_MAX) { - return __builtin_smul_overflow(lhs, rhs, res); + return __builtin_smul_overflow(lhs, rhs, (int*)res); } #elif defined(__GNUC__) && INT16_MAX == LONG_MAX if (min == INT16_MIN && max == INT16_MAX) { - return __builtin_smull_overflow(lhs, rhs, res); + return __builtin_smull_overflow(lhs, rhs, (long*)res); } #elif defined(__GNUC__) && INT16_MAX == LLONG_MAX if (min == INT16_MIN && max == INT16_MAX) { - return __builtin_smulll_overflow(lhs, rhs, res); + return __builtin_smulll_overflow(lhs, rhs, (long long*)res); } #endif int32_t big_result = (int32_t)lhs * (int32_t)rhs; @@ -922,15 +922,15 @@ static inline bool zig_mulo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t static inline bool zig_mulo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t min, int32_t max) { #if defined(__GNUC__) && INT32_MAX == INT_MAX if (min == INT32_MIN && max == INT32_MAX) { - return __builtin_smul_overflow(lhs, rhs, res); + return __builtin_smul_overflow(lhs, rhs, (int*)res); } #elif defined(__GNUC__) && INT32_MAX == LONG_MAX if (min == INT32_MIN && max == INT32_MAX) { - return __builtin_smull_overflow(lhs, rhs, res); + return __builtin_smull_overflow(lhs, rhs, (long*)res); } #elif defined(__GNUC__) && INT32_MAX == LLONG_MAX if (min == INT32_MIN && max == INT32_MAX) { - return __builtin_smulll_overflow(lhs, rhs, res); + return __builtin_smulll_overflow(lhs, rhs, (long long*)res); } #endif int64_t big_result = (int64_t)lhs * (int64_t)rhs; @@ -949,11 +949,11 @@ static inline bool zig_mulo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t static inline bool zig_mulo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t min, int64_t max) { bool overflow; #if defined(__GNUC__) && INT64_MAX == INT_MAX - overflow = __builtin_smul_overflow(lhs, rhs, res); + overflow = __builtin_smul_overflow(lhs, rhs, (int*)res); #elif defined(__GNUC__) && INT64_MAX == LONG_MAX - overflow = __builtin_smull_overflow(lhs, rhs, res); + overflow = __builtin_smull_overflow(lhs, rhs, (long*)res); #elif defined(__GNUC__) && INT64_MAX == LLONG_MAX - overflow = __builtin_smulll_overflow(lhs, rhs, res); + overflow = __builtin_smulll_overflow(lhs, rhs, (long long*)res); #else int int_overflow; *res = __mulodi4(lhs, rhs, &int_overflow); @@ -974,11 +974,11 @@ static inline bool zig_mulo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t static inline bool zig_mulo_i128(int128_t lhs, int128_t rhs, int128_t *res, int128_t min, int128_t max) { bool overflow; #if defined(__GNUC__) && INT128_MAX == INT_MAX - overflow = __builtin_smul_overflow(lhs, rhs, res); + overflow = __builtin_smul_overflow(lhs, rhs, (int*)res); #elif defined(__GNUC__) && INT128_MAX == LONG_MAX - overflow = __builtin_smull_overflow(lhs, rhs, res); + overflow = __builtin_smull_overflow(lhs, rhs, (long*)res); #elif defined(__GNUC__) && INT128_MAX == LLONG_MAX - overflow = __builtin_smulll_overflow(lhs, rhs, res); + overflow = __builtin_smulll_overflow(lhs, rhs, (long long*)res); #else int int_overflow; *res = __muloti4(lhs, rhs, &int_overflow); @@ -999,15 +999,15 @@ static inline bool zig_mulo_i128(int128_t lhs, int128_t rhs, int128_t *res, int1 static inline bool zig_mulo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t max) { #if defined(__GNUC__) && UINT8_MAX == UINT_MAX if (max == UINT8_MAX) { - return __builtin_umul_overflow(lhs, rhs, res); + return __builtin_umul_overflow(lhs, rhs, (unsigned int*)res); } #elif defined(__GNUC__) && UINT8_MAX == ULONG_MAX if (max == UINT8_MAX) { - return __builtin_umull_overflow(lhs, rhs, res); + return __builtin_umull_overflow(lhs, rhs, (unsigned long*)res); } #elif defined(__GNUC__) && UINT8_MAX == ULLONG_MAX if (max == UINT8_MAX) { - return __builtin_umulll_overflow(lhs, rhs, res); + return __builtin_umulll_overflow(lhs, rhs, (unsigned long long*)res); } #endif uint16_t big_result = (uint16_t)lhs * (uint16_t)rhs; @@ -1022,15 +1022,15 @@ static inline bool zig_mulo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t m static inline uint16_t zig_mulo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint16_t max) { #if defined(__GNUC__) && UINT16_MAX == UINT_MAX if (max == UINT16_MAX) { - return __builtin_umul_overflow(lhs, rhs, res); + return __builtin_umul_overflow(lhs, rhs, (unsigned int*)res); } #elif defined(__GNUC__) && UINT16_MAX == ULONG_MAX if (max == UINT16_MAX) { - return __builtin_umull_overflow(lhs, rhs, res); + return __builtin_umull_overflow(lhs, rhs, (unsigned long*)res); } #elif defined(__GNUC__) && UINT16_MAX == ULLONG_MAX if (max == UINT16_MAX) { - return __builtin_umulll_overflow(lhs, rhs, res); + return __builtin_umulll_overflow(lhs, rhs, (unsigned long long*)res); } #endif uint32_t big_result = (uint32_t)lhs * (uint32_t)rhs; @@ -1045,15 +1045,15 @@ static inline uint16_t zig_mulo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, u static inline uint32_t zig_mulo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint32_t max) { #if defined(__GNUC__) && UINT32_MAX == UINT_MAX if (max == UINT32_MAX) { - return __builtin_umul_overflow(lhs, rhs, res); + return __builtin_umul_overflow(lhs, rhs, (unsigned int*)res); } #elif defined(__GNUC__) && UINT32_MAX == ULONG_MAX if (max == UINT32_MAX) { - return __builtin_umull_overflow(lhs, rhs, res); + return __builtin_umull_overflow(lhs, rhs, (unsigned long*)res); } #elif defined(__GNUC__) && UINT32_MAX == ULLONG_MAX if (max == UINT32_MAX) { - return __builtin_umulll_overflow(lhs, rhs, res); + return __builtin_umulll_overflow(lhs, rhs, (unsigned long long*)res); } #endif uint64_t big_result = (uint64_t)lhs * (uint64_t)rhs; @@ -1068,11 +1068,11 @@ static inline uint32_t zig_mulo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, u static inline uint64_t zig_mulo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint64_t max) { bool overflow; #if defined(__GNUC__) && UINT64_MAX == UINT_MAX - overflow = __builtin_umul_overflow(lhs, rhs, res); + overflow = __builtin_umul_overflow(lhs, rhs, (unsigned int*)res); #elif defined(__GNUC__) && UINT64_MAX == ULONG_MAX - overflow = __builtin_umull_overflow(lhs, rhs, res); + overflow = __builtin_umull_overflow(lhs, rhs, (unsigned long*)res); #elif defined(__GNUC__) && UINT64_MAX == ULLONG_MAX - overflow = __builtin_umulll_overflow(lhs, rhs, res); + overflow = __builtin_umulll_overflow(lhs, rhs, (unsigned long long*)res); #else int int_overflow; *res = __umulodi4(lhs, rhs, &int_overflow); From 1392c24166f9edc1dd22c979689dcec456292ef4 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 14 May 2022 14:16:19 +0200 Subject: [PATCH 1540/2031] std.os: Add memfd_create for FreeBSD This is minorly breaking as e.g. std.os.linux.MFD_CLOEXEC is now std.os.linux.MFD.CLOEXEC. --- lib/std/c/freebsd.zig | 9 +++++++++ lib/std/os.zig | 46 ++++++++++++++++++++++++++++++------------- lib/std/os/linux.zig | 40 +++++++++++++++++++------------------ lib/std/os/test.zig | 5 +++-- 4 files changed, 65 insertions(+), 35 deletions(-) diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index e40d7acd8d..997542439d 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -1640,3 +1640,12 @@ pub const POLL = struct { pub const STANDARD = IN | PRI | OUT | RDNORM | RDBAND | WRBAND | ERR | HUP | NVAL; }; + +pub const NAME_MAX = 255; + +pub const MFD = struct { + pub const CLOEXEC = 0x0001; + pub const ALLOW_SEALING = 0x0002; +}; + +pub extern "c" fn memfd_create(name: [*:0]const u8, flags: c_uint) c_int; diff --git a/lib/std/os.zig b/lib/std/os.zig index 6859427b78..cd4500d60f 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -97,6 +97,7 @@ pub const MADV = system.MADV; pub const MAP = system.MAP; pub const MSF = system.MSF; pub const MAX_ADDR_LEN = system.MAX_ADDR_LEN; +pub const MFD = system.MFD; pub const MMAP2_UNIT = system.MMAP2_UNIT; pub const MSG = system.MSG; pub const NAME_MAX = system.NAME_MAX; @@ -6522,20 +6523,37 @@ pub const MemFdCreateError = error{ } || UnexpectedError; pub fn memfd_createZ(name: [*:0]const u8, flags: u32) MemFdCreateError!fd_t { - // memfd_create is available only in glibc versions starting with 2.27. - const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok; - const sys = if (use_c) std.c else linux; - const getErrno = if (use_c) std.c.getErrno else linux.getErrno; - const rc = sys.memfd_create(name, flags); - switch (getErrno(rc)) { - .SUCCESS => return @intCast(fd_t, rc), - .FAULT => unreachable, // name has invalid memory - .INVAL => unreachable, // name/flags are faulty - .NFILE => return error.SystemFdQuotaExceeded, - .MFILE => return error.ProcessFdQuotaExceeded, - .NOMEM => return error.OutOfMemory, - .NOSYS => return error.SystemOutdated, - else => |err| return unexpectedErrno(err), + switch (builtin.os.tag) { + .linux => { + // memfd_create is available only in glibc versions starting with 2.27. + const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok; + const sys = if (use_c) std.c else linux; + const getErrno = if (use_c) std.c.getErrno else linux.getErrno; + const rc = sys.memfd_create(name, flags); + switch (getErrno(rc)) { + .SUCCESS => return @intCast(fd_t, rc), + .FAULT => unreachable, // name has invalid memory + .INVAL => unreachable, // name/flags are faulty + .NFILE => return error.SystemFdQuotaExceeded, + .MFILE => return error.ProcessFdQuotaExceeded, + .NOMEM => return error.OutOfMemory, + .NOSYS => return error.SystemOutdated, + else => |err| return unexpectedErrno(err), + } + }, + .freebsd => { + const rc = system.memfd_create(name, flags); + switch (errno(rc)) { + .SUCCESS => return rc, + .BADF => unreachable, // name argument NULL + .INVAL => unreachable, // name too long or invalid/unsupported flags. + .MFILE => return error.ProcessFdQuotaExceeded, + .NFILE => return error.SystemFdQuotaExceeded, + .NOSYS => return error.SystemOutdated, + else => |err| return unexpectedErrno(err), + } + }, + else => @compileError("target OS does not support memfd_create()"), } } diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index fec9f55ad1..35d2e88c17 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -3972,11 +3972,6 @@ pub const POLL = struct { pub const RDBAND = 0x080; }; -pub const MFD_CLOEXEC = 0x0001; -pub const MFD_ALLOW_SEALING = 0x0002; -pub const MFD_HUGETLB = 0x0004; -pub const MFD_ALL_FLAGS = MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_HUGETLB; - pub const HUGETLB_FLAG_ENCODE_SHIFT = 26; pub const HUGETLB_FLAG_ENCODE_MASK = 0x3f; pub const HUGETLB_FLAG_ENCODE_64KB = 16 << HUGETLB_FLAG_ENCODE_SHIFT; @@ -3992,20 +3987,27 @@ pub const HUGETLB_FLAG_ENCODE_1GB = 30 << HUGETLB_FLAG_ENCODE_SHIFT; pub const HUGETLB_FLAG_ENCODE_2GB = 31 << HUGETLB_FLAG_ENCODE_SHIFT; pub const HUGETLB_FLAG_ENCODE_16GB = 34 << HUGETLB_FLAG_ENCODE_SHIFT; -pub const MFD_HUGE_SHIFT = HUGETLB_FLAG_ENCODE_SHIFT; -pub const MFD_HUGE_MASK = HUGETLB_FLAG_ENCODE_MASK; -pub const MFD_HUGE_64KB = HUGETLB_FLAG_ENCODE_64KB; -pub const MFD_HUGE_512KB = HUGETLB_FLAG_ENCODE_512KB; -pub const MFD_HUGE_1MB = HUGETLB_FLAG_ENCODE_1MB; -pub const MFD_HUGE_2MB = HUGETLB_FLAG_ENCODE_2MB; -pub const MFD_HUGE_8MB = HUGETLB_FLAG_ENCODE_8MB; -pub const MFD_HUGE_16MB = HUGETLB_FLAG_ENCODE_16MB; -pub const MFD_HUGE_32MB = HUGETLB_FLAG_ENCODE_32MB; -pub const MFD_HUGE_256MB = HUGETLB_FLAG_ENCODE_256MB; -pub const MFD_HUGE_512MB = HUGETLB_FLAG_ENCODE_512MB; -pub const MFD_HUGE_1GB = HUGETLB_FLAG_ENCODE_1GB; -pub const MFD_HUGE_2GB = HUGETLB_FLAG_ENCODE_2GB; -pub const MFD_HUGE_16GB = HUGETLB_FLAG_ENCODE_16GB; +pub const MFD = struct { + pub const CLOEXEC = 0x0001; + pub const ALLOW_SEALING = 0x0002; + pub const HUGETLB = 0x0004; + pub const ALL_FLAGS = CLOEXEC | ALLOW_SEALING | HUGETLB; + + pub const HUGE_SHIFT = HUGETLB_FLAG_ENCODE_SHIFT; + pub const HUGE_MASK = HUGETLB_FLAG_ENCODE_MASK; + pub const HUGE_64KB = HUGETLB_FLAG_ENCODE_64KB; + pub const HUGE_512KB = HUGETLB_FLAG_ENCODE_512KB; + pub const HUGE_1MB = HUGETLB_FLAG_ENCODE_1MB; + pub const HUGE_2MB = HUGETLB_FLAG_ENCODE_2MB; + pub const HUGE_8MB = HUGETLB_FLAG_ENCODE_8MB; + pub const HUGE_16MB = HUGETLB_FLAG_ENCODE_16MB; + pub const HUGE_32MB = HUGETLB_FLAG_ENCODE_32MB; + pub const HUGE_256MB = HUGETLB_FLAG_ENCODE_256MB; + pub const HUGE_512MB = HUGETLB_FLAG_ENCODE_512MB; + pub const HUGE_1GB = HUGETLB_FLAG_ENCODE_1GB; + pub const HUGE_2GB = HUGETLB_FLAG_ENCODE_2GB; + pub const HUGE_16GB = HUGETLB_FLAG_ENCODE_16GB; +}; pub const rusage = extern struct { utime: timeval, diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index ae0d14ef7e..dfe65f008d 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -495,8 +495,9 @@ test "argsAlloc" { } test "memfd_create" { - // memfd_create is linux specific. - if (native_os != .linux) return error.SkipZigTest; + // memfd_create is only supported by linux and freebsd. + if (native_os != .linux and native_os != .freebsd) return error.SkipZigTest; + const fd = std.os.memfd_create("test", 0) catch |err| switch (err) { // Related: https://github.com/ziglang/zig/issues/4019 error.SystemOutdated => return error.SkipZigTest, From 1de7b8d26c2d283b2e494dd6b4cecfa4d44c441d Mon Sep 17 00:00:00 2001 From: leesongun <12179851+leesongun@users.noreply.github.com> Date: Tue, 17 May 2022 07:28:20 +0900 Subject: [PATCH 1541/2031] `std.math.powi`: use standard definition of underflow/overflow, implement `u0`, `i0`, `i1` edge case (#11499) --- lib/std/math/powi.zig | 201 ++++++++++++++++++++++-------------------- 1 file changed, 105 insertions(+), 96 deletions(-) diff --git a/lib/std/math/powi.zig b/lib/std/math/powi.zig index 6c4b3a65f4..2e615de5d5 100644 --- a/lib/std/math/powi.zig +++ b/lib/std/math/powi.zig @@ -10,108 +10,94 @@ const testing = std.testing; /// Returns the power of x raised by the integer y (x^y). /// -/// Special Cases: -/// - powi(x, +-0) = 1 for any x -/// - powi(0, y) = 0 for any y -/// - powi(1, y) = 1 for any y -/// - powi(-1, y) = -1 for y an odd integer -/// - powi(-1, y) = 1 for y an even integer -/// - powi(x, y) = Overflow for y >= @sizeOf(x) - 1 or y > 0 -/// - powi(x, y) = Underflow for y > @sizeOf(x) - 1 or y < 0 +/// Errors: +/// - Overflow: Integer overflow or Infinity +/// - Underflow: Absolute value of result smaller than 1 +/// Edge case rules ordered by precedence: +/// - powi(T, x, 0) = 1 unless T is i1, i0, u0 +/// - powi(T, 0, x) = 0 when x > 0 +/// - powi(T, 0, x) = Overflow +/// - powi(T, 1, y) = 1 +/// - powi(T, -1, y) = -1 for y an odd integer +/// - powi(T, -1, y) = 1 unless T is i1, i0, u0 +/// - powi(T, -1, y) = Overflow +/// - powi(T, x, y) = Overflow when y >= @bitSizeOf(x) +/// - powi(T, x, y) = Underflow when y < 0 pub fn powi(comptime T: type, x: T, y: T) (error{ Overflow, Underflow, }!T) { - const info = @typeInfo(T); + const bit_size = @typeInfo(T).Int.bits; - comptime assert(@typeInfo(T) == .Int); + // `y & 1 == 0` won't compile when `does_one_overflow`. + const does_one_overflow = math.maxInt(T) < 1; + const is_y_even = !does_one_overflow and y & 1 == 0; - // powi(x, +-0) = 1 for any x - if (y == 0 or y == -0) { - return 1; + if (x == 1 or y == 0 or (x == -1 and is_y_even)) { + if (does_one_overflow) { + return error.Overflow; + } else { + return 1; + } } - switch (x) { - // powi(0, y) = 0 for any y - 0 => return 0, - - // powi(1, y) = 1 for any y - 1 => return 1, - - else => { - // powi(x, y) = Overflow for for y >= @sizeOf(x) - 1 y > 0 - // powi(x, y) = Underflow for for y > @sizeOf(x) - 1 y < 0 - const bit_size = @sizeOf(T) * 8; - if (info.Int.signedness == .signed) { - if (x == -1) { - // powi(-1, y) = -1 for for y an odd integer - // powi(-1, y) = 1 for for y an even integer - if (@mod(y, 2) == 0) { - return 1; - } else { - return -1; - } - } - - if (x > 0 and y >= bit_size - 1) { - return error.Overflow; - } else if (x < 0 and y > bit_size - 1) { - return error.Underflow; - } - } else { - if (y >= bit_size) { - return error.Overflow; - } - } - - var base = x; - var exp = y; - var acc: T = 1; - - while (exp > 1) { - if (exp & 1 == 1) { - if (@mulWithOverflow(T, acc, base, &acc)) { - if (x > 0) { - return error.Overflow; - } else { - return error.Underflow; - } - } - } - - exp >>= 1; - - if (@mulWithOverflow(T, base, base, &base)) { - if (x > 0) { - return error.Overflow; - } else { - return error.Underflow; - } - } - } - - if (exp == 1) { - if (@mulWithOverflow(T, acc, base, &acc)) { - if (x > 0) { - return error.Overflow; - } else { - return error.Underflow; - } - } - } - - return acc; - }, + if (x == -1) { + return -1; } + + if (x == 0) { + if (y > 0) { + return 0; + } else { + // Infinity/NaN, not overflow in strict sense + return error.Overflow; + } + } + // x >= 2 or x <= -2 from this point + if (y >= bit_size) { + return error.Overflow; + } + if (y < 0) { + return error.Underflow; + } + + // invariant : + // return value = powi(T, base, exp) * acc; + + var base = x; + var exp = y; + var acc: T = if (does_one_overflow) unreachable else 1; + + while (exp > 1) { + if (exp & 1 == 1) { + if (@mulWithOverflow(T, acc, base, &acc)) { + return error.Overflow; + } + } + + exp >>= 1; + + if (@mulWithOverflow(T, base, base, &base)) { + return error.Overflow; + } + } + + if (exp == 1) { + if (@mulWithOverflow(T, acc, base, &acc)) { + return error.Overflow; + } + } + + return acc; } test "math.powi" { - try testing.expectError(error.Underflow, powi(i8, -66, 6)); - try testing.expectError(error.Underflow, powi(i16, -13, 13)); - try testing.expectError(error.Underflow, powi(i32, -32, 21)); - try testing.expectError(error.Underflow, powi(i64, -24, 61)); - try testing.expectError(error.Underflow, powi(i17, -15, 15)); - try testing.expectError(error.Underflow, powi(i42, -6, 40)); + try testing.expectError(error.Overflow, powi(i8, -66, 6)); + try testing.expectError(error.Overflow, powi(i16, -13, 13)); + try testing.expectError(error.Overflow, powi(i32, -32, 21)); + try testing.expectError(error.Overflow, powi(i64, -24, 61)); + try testing.expectError(error.Overflow, powi(i17, -15, 15)); + try testing.expectError(error.Overflow, powi(i42, -6, 40)); try testing.expect((try powi(i8, -5, 3)) == -125); try testing.expect((try powi(i16, -16, 3)) == -4096); @@ -140,15 +126,29 @@ test "math.powi" { try testing.expectError(error.Overflow, powi(u64, 2342, 63)); try testing.expectError(error.Overflow, powi(u17, 2723, 16)); try testing.expectError(error.Overflow, powi(u42, 8234, 41)); + + const minInt = std.math.minInt; + try testing.expect((try powi(i8, -2, 7)) == minInt(i8)); + try testing.expect((try powi(i16, -2, 15)) == minInt(i16)); + try testing.expect((try powi(i32, -2, 31)) == minInt(i32)); + try testing.expect((try powi(i64, -2, 63)) == minInt(i64)); + + try testing.expectError(error.Underflow, powi(i8, 6, -2)); + try testing.expectError(error.Underflow, powi(i16, 5, -4)); + try testing.expectError(error.Underflow, powi(i32, 12, -6)); + try testing.expectError(error.Underflow, powi(i64, 34, -2)); + try testing.expectError(error.Underflow, powi(i17, 16, -3)); + try testing.expectError(error.Underflow, powi(i42, 34, -6)); } test "math.powi.special" { - try testing.expectError(error.Underflow, powi(i8, -2, 8)); - try testing.expectError(error.Underflow, powi(i16, -2, 16)); - try testing.expectError(error.Underflow, powi(i32, -2, 32)); - try testing.expectError(error.Underflow, powi(i64, -2, 64)); - try testing.expectError(error.Underflow, powi(i17, -2, 17)); - try testing.expectError(error.Underflow, powi(i42, -2, 42)); + try testing.expectError(error.Overflow, powi(i8, -2, 8)); + try testing.expectError(error.Overflow, powi(i16, -2, 16)); + try testing.expectError(error.Overflow, powi(i32, -2, 32)); + try testing.expectError(error.Overflow, powi(i64, -2, 64)); + try testing.expectError(error.Overflow, powi(i17, -2, 17)); + try testing.expectError(error.Overflow, powi(i17, -2, 16)); + try testing.expectError(error.Overflow, powi(i42, -2, 42)); try testing.expect((try powi(i8, -1, 3)) == -1); try testing.expect((try powi(i16, -1, 2)) == 1); @@ -185,3 +185,12 @@ test "math.powi.special" { try testing.expect((try powi(u17, 16, 0)) == 1); try testing.expect((try powi(u42, 34, 0)) == 1); } + +test "math.powi.narrow" { + try testing.expectError(error.Overflow, powi(u0, 0, 0)); + try testing.expectError(error.Overflow, powi(i0, 0, 0)); + try testing.expectError(error.Overflow, powi(i1, 0, 0)); + try testing.expectError(error.Overflow, powi(i1, -1, 0)); + try testing.expectError(error.Overflow, powi(i1, 0, -1)); + try testing.expect((try powi(i1, -1, -1)) == -1); +} From 7a4758ed7868096c12fec8dacba0bfd4a37fdd13 Mon Sep 17 00:00:00 2001 From: Thiago Teodoro Pereira Silva Date: Mon, 16 May 2022 19:56:33 -0300 Subject: [PATCH 1542/2031] std.os: add timerfd_create, timerfd_settime and timerfd_gettime --- lib/std/os.zig | 49 +++++++++++++++++++++++++++++++++++++++++++++ lib/std/os/test.zig | 20 ++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/lib/std/os.zig b/lib/std/os.zig index cd4500d60f..27c8bd099a 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -6926,3 +6926,52 @@ pub fn perf_event_open( else => |err| return unexpectedErrno(err), } } + +pub const TimerFdCreateError = error{ + AccessDenied, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + NoDevice, + SystemResources, +} || UnexpectedError; + +pub const TimerFdGetError = error{InvalidHandle} || UnexpectedError; +pub const TimerFdSetError = TimerFdGetError || error{Canceled}; + +pub fn timerfd_create(clokid: i32, flags: u32) TimerFdCreateError!fd_t { + var rc = linux.timerfd_create(clokid, flags); + return switch (errno(rc)) { + .SUCCESS => @intCast(fd_t, rc), + .INVAL => unreachable, + .MFILE => return error.ProcessFdQuotaExceeded, + .NFILE => return error.SystemFdQuotaExceeded, + .NODEV => return error.NoDevice, + .NOMEM => return error.SystemResources, + .PERM => return error.AccessDenied, + else => |err| return unexpectedErrno(err), + }; +} + +pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const linux.itimerspec, old_value: ?*linux.itimerspec) TimerFdSetError!void { + var rc = linux.timerfd_settime(fd, flags, new_value, old_value); + return switch (errno(rc)) { + .SUCCESS => {}, + .BADF => error.InvalidHandle, + .FAULT => unreachable, + .INVAL => unreachable, + .CANCELED => error.Canceled, + else => |err| return unexpectedErrno(err), + }; +} + +pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec { + var curr_value: linux.itimerspec = undefined; + var rc = linux.timerfd_gettime(fd, &curr_value); + return switch (errno(rc)) { + .SUCCESS => return curr_value, + .BADF => error.InvalidHandle, + .FAULT => unreachable, + .INVAL => unreachable, + else => |err| return unexpectedErrno(err), + }; +} diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index dfe65f008d..975d5a64eb 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -1000,3 +1000,23 @@ test "access smoke test" { file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); try os.access(file_path, os.F_OK); } + +test "timerfd" { + if (native_os != .linux) + return error.SkipZigTest; + + const linux = os.linux; + var tfd = try os.timerfd_create(linux.CLOCK.MONOTONIC, linux.TFD.CLOEXEC); + defer os.close(tfd); + + var sit: linux.itimerspec = .{ .it_interval = .{ .tv_sec = 0, .tv_nsec = 0 }, .it_value = .{ .tv_sec = 0, .tv_nsec = 10 * (1000 * 1000) } }; + try os.timerfd_settime(tfd, 0, &sit, null); + + var fds: [1]os.pollfd = .{.{ .fd = tfd, .events = os.linux.POLL.IN, .revents = 0 }}; + try expectEqual(try os.poll(&fds, -1), 1); + var git = try os.timerfd_gettime(tfd); + try expectEqual(git, .{ .it_interval = .{ .tv_sec = 0, .tv_nsec = 0 }, .it_value = .{ .tv_sec = 0, .tv_nsec = 0 } }); + + try os.timerfd_settime(tfd, 0, &sit, null); + try expectEqual(try os.poll(&fds, 5), 0); +} From eee8fffec70b1d3e2900970dbe836e346e499231 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 22 Apr 2022 21:30:54 +0300 Subject: [PATCH 1543/2031] stage2: implement error return traces --- lib/std/builtin.zig | 11 +++++ src/Air.zig | 8 ++++ src/Liveness.zig | 2 + src/Module.zig | 21 +++++++++ src/Sema.zig | 75 ++++++++++++++++++++++++++++---- src/arch/aarch64/CodeGen.zig | 20 +++++++++ src/arch/arm/CodeGen.zig | 20 +++++++++ src/arch/riscv64/CodeGen.zig | 20 +++++++++ src/arch/sparc64/CodeGen.zig | 2 + src/arch/wasm/CodeGen.zig | 2 + src/arch/x86_64/CodeGen.zig | 20 +++++++++ src/codegen/c.zig | 34 +++++++++++++++ src/codegen/llvm.zig | 84 +++++++++++++++++++++++++++++++++++- src/print_air.zig | 2 + src/type.zig | 7 +++ 15 files changed, 318 insertions(+), 10 deletions(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index d352ac29dc..a8069fa490 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -846,5 +846,16 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn } } +pub noinline fn returnError(maybe_st: ?*StackTrace) void { + @setCold(true); + const st = maybe_st orelse return; + addErrRetTraceAddr(st, @returnAddress()); +} + +pub inline fn addErrRetTraceAddr(st: *StackTrace, addr: usize) void { + st.instruction_addresses[st.index & (st.instruction_addresses.len - 1)] = addr; + st.index +%= 1; +} + const std = @import("std.zig"); const root = @import("root"); diff --git a/src/Air.zig b/src/Air.zig index 2dae8454cf..169449a066 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -649,6 +649,12 @@ pub const Inst = struct { /// flush(). cmp_lt_errors_len, + /// Returns pointer to current error return trace. + err_return_trace, + + /// Sets the operand as the current error return trace, + set_err_return_trace, + pub fn fromCmpOp(op: std.math.CompareOperator) Tag { return switch (op) { .lt => .cmp_lt, @@ -961,6 +967,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .alloc, .ret_ptr, .arg, + .err_return_trace, => return datas[inst].ty, .assembly, @@ -1048,6 +1055,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .memcpy, .set_union_tag, .prefetch, + .set_err_return_trace, => return Type.void, .ptrtoint, diff --git a/src/Liveness.zig b/src/Liveness.zig index e606c15b4b..c14c460d67 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -362,6 +362,7 @@ fn analyzeInst( .ret_addr, .frame_addr, .wasm_memory_size, + .err_return_trace, => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }), .not, @@ -434,6 +435,7 @@ fn analyzeInst( .round, .trunc_float, .cmp_lt_errors_len, + .set_err_return_trace, => { const operand = inst_datas[inst].un_op; return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none }); diff --git a/src/Module.zig b/src/Module.zig index de29753b13..cb38ddba45 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1427,6 +1427,7 @@ pub const Fn = struct { state: Analysis, is_cold: bool = false, is_noinline: bool = false, + calls_or_awaits_errorable_fn: bool = false, /// Any inferred error sets that this function owns, both its own inferred error set and /// inferred error sets of any inline/comptime functions called. Not to be confused @@ -4838,6 +4839,9 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air { }; defer sema.deinit(); + // reset in case case calls to errorable functions are removed. + func.calls_or_awaits_errorable_fn = false; + // First few indexes of extra are reserved and set at the end. const reserved_count = @typeInfo(Air.ExtraIndex).Enum.fields.len; try sema.air_extra.ensureTotalCapacity(gpa, reserved_count); @@ -4936,6 +4940,8 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air { func.state = .in_progress; log.debug("set {s} to in_progress", .{decl.name}); + const last_arg_index = inner_block.instructions.items.len; + sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) { // TODO make these unreachable instead of @panic error.NeededSourceLocation => @panic("zig compiler bug: NeededSourceLocation"), @@ -4944,6 +4950,21 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air { else => |e| return e, }; + // If we don't get an error return trace from a caller, create our own. + if (func.calls_or_awaits_errorable_fn and + mod.comp.bin_file.options.error_return_tracing and + !sema.fn_ret_ty.isError()) + { + sema.setupErrorReturnTrace(&inner_block, last_arg_index) catch |err| switch (err) { + // TODO make these unreachable instead of @panic + error.NeededSourceLocation => @panic("zig compiler bug: NeededSourceLocation"), + error.GenericPoison => @panic("zig compiler bug: GenericPoison"), + error.ComptimeReturn => @panic("zig compiler bug: ComptimeReturn"), + error.ComptimeBreak => @panic("zig compiler bug: ComptimeBreak"), + else => |e| return e, + }; + } + try wip_captures.finalize(); // Copy the block into place and mark that as the main block. diff --git a/src/Sema.zig b/src/Sema.zig index 471639ba96..ab2a59f44a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1411,6 +1411,38 @@ fn analyzeAsType( return ty.copy(sema.arena); } +pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void { + var err_trace_block = block.makeSubBlock(); + err_trace_block.is_comptime = false; + defer err_trace_block.instructions.deinit(sema.gpa); + + const src: LazySrcLoc = .unneeded; + + // var addrs: [err_return_trace_addr_count]usize = undefined; + const err_return_trace_addr_count = 32; + const addr_arr_ty = try Type.array(sema.arena, err_return_trace_addr_count, null, Type.usize, sema.mod); + const addrs_ptr = try err_trace_block.addTy(.alloc, try Type.Tag.single_mut_pointer.create(sema.arena, addr_arr_ty)); + + // var st: StackTrace = undefined; + const unresolved_stack_trace_ty = try sema.getBuiltinType(&err_trace_block, src, "StackTrace"); + const stack_trace_ty = try sema.resolveTypeFields(&err_trace_block, src, unresolved_stack_trace_ty); + const st_ptr = try err_trace_block.addTy(.alloc, try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty)); + + // st.instruction_addresses = &addrs; + const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "instruction_addresses", src); + try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store); + + // st.index = 0; + const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "index", src); + const zero = try sema.addConstant(Type.usize, Value.zero); + try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, zero, src, .store); + + // @errorReturnTrace() = &st; + _ = try err_trace_block.addUnOp(.set_err_return_trace, st_ptr); + + try block.instructions.insertSlice(sema.gpa, last_arg_index, err_trace_block.instructions.items); +} + /// May return Value Tags: `variable`, `undef`. /// See `resolveConstValue` for an alternative. /// Value Tag `generic_poison` causes `error.GenericPoison` to be returned. @@ -5236,6 +5268,13 @@ fn analyzeCall( } try sema.queueFullTypeResolution(func_ty_info.return_type); + if (sema.owner_func != null and func_ty_info.return_type.isError()) { + if (!sema.owner_func.?.calls_or_awaits_errorable_fn) { + // Ensure the type exists so that backends can assume that. + _ = try sema.getBuiltinType(block, call_src, "StackTrace"); + } + sema.owner_func.?.calls_or_awaits_errorable_fn = true; + } try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len + args.len); @@ -5645,6 +5684,15 @@ fn instantiateGenericCall( try sema.queueFullTypeResolution(new_fn_info.return_type); } + + if (sema.owner_func != null and new_fn_info.return_type.isError()) { + if (!sema.owner_func.?.calls_or_awaits_errorable_fn) { + // Ensure the type exists so that backends can assume that. + _ = try sema.getBuiltinType(block, call_src, "StackTrace"); + } + sema.owner_func.?.calls_or_awaits_errorable_fn = true; + } + try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len + runtime_args_len); const func_inst = try block.addInst(.{ @@ -12607,6 +12655,16 @@ fn analyzeRet( return always_noreturn; } + if (sema.fn_ret_ty.isError() and sema.mod.comp.bin_file.options.error_return_tracing) { + const return_err_fn = try sema.getBuiltin(block, src, "returnError"); + const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); + const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); + const ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty); + const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); + const args: [1]Air.Inst.Ref = .{err_return_trace}; + _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args); + } + try sema.resolveTypeLayout(block, src, sema.fn_ret_ty); _ = try block.addUnOp(.ret, operand); return always_noreturn; @@ -13338,9 +13396,14 @@ fn zirErrorReturnTrace( const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); - const opt_stack_trace_ty = try Type.optional(sema.arena, stack_trace_ty); - // https://github.com/ziglang/zig/issues/11259 - return sema.addConstant(opt_stack_trace_ty, Value.@"null"); + const opt_ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty); + if (sema.owner_func != null and + sema.owner_func.?.calls_or_awaits_errorable_fn and + sema.mod.comp.bin_file.options.error_return_tracing) + { + return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty); + } + return sema.addConstant(opt_ptr_stack_trace_ty, Value.@"null"); } fn zirFrame( @@ -21817,11 +21880,7 @@ fn resolvePeerTypes( info.data.sentinel = chosen_child_ty.sentinel(); info.data.size = .Slice; info.data.mutable = !(seen_const or chosen_child_ty.isConstPtr()); - info.data.pointee_type = switch (chosen_child_ty.tag()) { - .array => chosen_child_ty.elemType2(), - .array_u8, .array_u8_sentinel_0 => Type.initTag(.u8), - else => unreachable, - }; + info.data.pointee_type = chosen_child_ty.elemType2(); const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data); const opt_ptr_ty = if (any_are_null) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 10730c446f..56b7b87499 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -718,6 +718,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst), + .err_return_trace => try self.airErrReturnTrace(inst), + .set_err_return_trace => try self.airSetErrReturnTrace(inst), .wrap_optional => try self.airWrapOptional(inst), .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), @@ -2330,6 +2332,24 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airErrReturnTrace(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 + return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + +fn airSetErrReturnTrace(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 + return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airWrapOptional(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 result: { diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 93e6e95ba9..f09b37ee69 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -725,6 +725,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst), + .err_return_trace => try self.airErrReturnTrace(inst), + .set_err_return_trace => try self.airSetErrReturnTrace(inst), .wrap_optional => try self.airWrapOptional(inst), .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), @@ -1843,6 +1845,24 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airErrReturnTrace(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 + return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + +fn airSetErrReturnTrace(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 + return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + /// T to E!T fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 07ce2a9f89..6d813cfbbb 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -654,6 +654,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst), + .err_return_trace => try self.airErrReturnTrace(inst), + .set_err_return_trace => try self.airSetErrReturnTrace(inst), .wrap_optional => try self.airWrapOptional(inst), .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), @@ -1267,6 +1269,24 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airErrReturnTrace(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 + return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + +fn airSetErrReturnTrace(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 + return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airWrapOptional(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 result: { diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 0745cd46c9..a3f5f2b108 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -630,6 +630,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"), .unwrap_errunion_payload_ptr=> @panic("TODO try self.airUnwrapErrPayloadPtr(inst)"), .errunion_payload_ptr_set => @panic("TODO try self.airErrUnionPayloadPtrSet(inst)"), + .err_return_trace => @panic("TODO try self.airErrReturnTrace(inst)"), + .set_err_return_trace => @panic("TODO try self.airSetErrReturnTrace(inst)"), .wrap_optional => @panic("TODO try self.airWrapOptional(inst)"), .wrap_errunion_payload => @panic("TODO try self.airWrapErrUnionPayload(inst)"), diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 8e84b7d1fe..fd3ac0e93a 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1612,6 +1612,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .atomic_store_seq_cst, .atomic_rmw, .tag_name, + .err_return_trace, + .set_err_return_trace, => |tag| return self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), }; } diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5ba28a3751..31ecc58c66 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -749,6 +749,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst), + .err_return_trace => try self.airErrReturnTrace(inst), + .set_err_return_trace => try self.airSetErrReturnTrace(inst), .wrap_optional => try self.airWrapOptional(inst), .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), @@ -1855,6 +1857,24 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airErrReturnTrace(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 + return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + +fn airSetErrReturnTrace(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 + return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; if (self.liveness.isUnused(inst)) { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 998271cd7f..c3ca79dabe 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1911,6 +1911,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .wrap_errunion_payload => try airWrapErrUnionPay(f, inst), .wrap_errunion_err => try airWrapErrUnionErr(f, inst), .errunion_payload_ptr_set => try airErrUnionPayloadPtrSet(f, inst), + .err_return_trace => try airErrReturnTrace(f, inst), + .set_err_return_trace => try airSetErrReturnTrace(f, inst), .wasm_memory_size => try airWasmMemorySize(f, inst), .wasm_memory_grow => try airWasmMemoryGrow(f, inst), @@ -3447,6 +3449,38 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { return local; } +fn airErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue { + if (f.liveness.isUnused(inst)) return CValue.none; + + const un_op = f.air.instructions.items(.data)[inst].un_op; + const writer = f.object.writer(); + const inst_ty = f.air.typeOfIndex(inst); + const operand = try f.resolveInst(un_op); + const local = try f.allocLocal(inst_ty, .Const); + + try writer.writeAll(" = "); + + _ = operand; + _ = local; + return f.fail("TODO: C backend: implement airErrReturnTrace", .{}); +} + +fn airSetErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue { + if (f.liveness.isUnused(inst)) return CValue.none; + + const un_op = f.air.instructions.items(.data)[inst].un_op; + const writer = f.object.writer(); + const inst_ty = f.air.typeOfIndex(inst); + const operand = try f.resolveInst(un_op); + const local = try f.allocLocal(inst_ty, .Const); + + try writer.writeAll(" = "); + + _ = operand; + _ = local; + return f.fail("TODO: C backend: implement airSetErrReturnTrace", .{}); +} + fn airWrapErrUnionPay(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 7392b2068b..dfb0f8f03b 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -636,10 +636,18 @@ pub const Object = struct { const ret_ptr = if (sret) llvm_func.getParam(0) else null; const gpa = dg.gpa; + const err_return_tracing = fn_info.return_type.isError() and + dg.module.comp.bin_file.options.error_return_tracing; + + const err_ret_trace = if (err_return_tracing) + llvm_func.getParam(@boolToInt(ret_ptr != null)) + else + null; + var args = std.ArrayList(*const llvm.Value).init(gpa); defer args.deinit(); - const param_offset: c_uint = @boolToInt(ret_ptr != null); + const param_offset = @as(c_uint, @boolToInt(ret_ptr != null)) + @boolToInt(err_return_tracing); for (fn_info.param_types) |param_ty| { if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; @@ -711,6 +719,7 @@ pub const Object = struct { .base_line = dg.decl.src_line, .prev_dbg_line = 0, .prev_dbg_column = 0, + .err_ret_trace = err_ret_trace, }; defer fg.deinit(); @@ -1755,6 +1764,17 @@ pub const Object = struct { try param_di_types.append(try o.lowerDebugType(Type.void, .full)); } + if (fn_info.return_type.isError() and + o.module.comp.bin_file.options.error_return_tracing) + { + var ptr_ty_payload: Type.Payload.ElemType = .{ + .base = .{ .tag = .single_mut_pointer }, + .data = o.getStackTraceType(), + }; + const ptr_ty = Type.initPayload(&ptr_ty_payload.base); + try param_di_types.append(try o.lowerDebugType(ptr_ty, .full)); + } + for (fn_info.param_types) |param_ty| { if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; @@ -1824,6 +1844,27 @@ pub const Object = struct { "", // unique id ); } + + fn getStackTraceType(o: *Object) Type { + const mod = o.module; + + const std_pkg = mod.main_pkg.table.get("std").?; + const std_file = (mod.importPkg(std_pkg) catch unreachable).file; + + const builtin_str: []const u8 = "builtin"; + const std_namespace = mod.declPtr(std_file.root_decl.unwrap().?).src_namespace; + const builtin_decl = std_namespace.decls + .getKeyAdapted(builtin_str, Module.DeclAdapter{ .mod = mod }).?; + + const stack_trace_str: []const u8 = "StackTrace"; + // buffer is only used for int_type, `builtin` is a struct. + const builtin_ty = mod.declPtr(builtin_decl).val.toType(undefined); + const builtin_namespace = builtin_ty.getNamespace().?; + const stack_trace_decl = builtin_namespace.decls + .getKeyAdapted(stack_trace_str, Module.DeclAdapter{ .mod = mod }).?; + + return mod.declPtr(stack_trace_decl).val.toType(undefined); + } }; pub const DeclGen = struct { @@ -1976,8 +2017,15 @@ pub const DeclGen = struct { llvm_fn.addSretAttr(0, raw_llvm_ret_ty); } + const err_return_tracing = fn_info.return_type.isError() and + dg.module.comp.bin_file.options.error_return_tracing; + + if (err_return_tracing) { + dg.addArgAttr(llvm_fn, @boolToInt(sret), "nonnull"); + } + // Set parameter attributes. - var llvm_param_i: c_uint = @boolToInt(sret); + var llvm_param_i: c_uint = @as(c_uint, @boolToInt(sret)) + @boolToInt(err_return_tracing); for (fn_info.param_types) |param_ty| { if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; @@ -2435,6 +2483,17 @@ pub const DeclGen = struct { try llvm_params.append(llvm_sret_ty.pointerType(0)); } + if (fn_info.return_type.isError() and + dg.module.comp.bin_file.options.error_return_tracing) + { + var ptr_ty_payload: Type.Payload.ElemType = .{ + .base = .{ .tag = .single_mut_pointer }, + .data = dg.object.getStackTraceType(), + }; + const ptr_ty = Type.initPayload(&ptr_ty_payload.base); + try llvm_params.append(try lowerFnParamTy(dg, fn_info.cc, ptr_ty)); + } + for (fn_info.param_types) |param_ty| { if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; @@ -3449,6 +3508,8 @@ pub const FuncGen = struct { llvm_func: *const llvm.Value, + err_ret_trace: ?*const llvm.Value = null, + /// This data structure is used to implement breaking to blocks. blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, struct { parent_bb: *const llvm.BasicBlock, @@ -3678,6 +3739,8 @@ pub const FuncGen = struct { .unwrap_errunion_err => try self.airErrUnionErr(inst, false), .unwrap_errunion_err_ptr => try self.airErrUnionErr(inst, true), .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst), + .err_return_trace => try self.airErrReturnTrace(inst), + .set_err_return_trace => try self.airSetErrReturnTrace(inst), .wrap_optional => try self.airWrapOptional(inst), .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), @@ -3732,6 +3795,12 @@ pub const FuncGen = struct { break :blk ret_ptr; }; + if (fn_info.return_type.isError() and + self.dg.module.comp.bin_file.options.error_return_tracing) + { + try llvm_args.append(self.err_ret_trace.?); + } + for (args) |arg| { const param_ty = self.air.typeOf(arg); if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; @@ -5149,6 +5218,17 @@ pub const FuncGen = struct { return self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""); } + fn airErrReturnTrace(self: *FuncGen, _: Air.Inst.Index) !?*const llvm.Value { + return self.err_ret_trace.?; + } + + fn airSetErrReturnTrace(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + self.err_ret_trace = operand; + return null; + } + fn airWrapOptional(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 c01d96ed7f..0524356fa7 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -170,6 +170,7 @@ const Writer = struct { .round, .trunc_float, .cmp_lt_errors_len, + .set_err_return_trace, => try w.writeUnOp(s, inst), .breakpoint, @@ -182,6 +183,7 @@ const Writer = struct { .alloc, .ret_ptr, .arg, + .err_return_trace, => try w.writeTy(s, inst), .not, diff --git a/src/type.zig b/src/type.zig index 54b7d44a3d..cc00f712f0 100644 --- a/src/type.zig +++ b/src/type.zig @@ -4093,6 +4093,13 @@ pub const Type = extern union { }; } + pub fn isError(ty: Type) bool { + return switch (ty.zigTypeTag()) { + .ErrorUnion, .ErrorSet => true, + else => false, + }; + } + /// Returns whether ty, which must be an error set, includes an error `name`. /// Might return a false negative if `ty` is an inferred error set and not fully /// resolved yet. From 66c3988e5eebd423844d5dd20c762d6fefe20adf Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 22 Apr 2022 23:10:02 +0300 Subject: [PATCH 1544/2031] stage2: disable error return tracing on unsupported targets --- lib/test_runner.zig | 4 ++-- src/Sema.zig | 20 ++++++++++++++++++-- src/arch/aarch64/CodeGen.zig | 12 ++++-------- src/arch/arm/CodeGen.zig | 12 ++++-------- src/arch/riscv64/CodeGen.zig | 12 ++++-------- src/arch/x86_64/CodeGen.zig | 12 ++++-------- src/codegen/c.zig | 24 +----------------------- src/codegen/llvm.zig | 10 +++++----- 8 files changed, 42 insertions(+), 64 deletions(-) diff --git a/lib/test_runner.zig b/lib/test_runner.zig index 4b6c80ac66..5ae05464f2 100644 --- a/lib/test_runner.zig +++ b/lib/test_runner.zig @@ -92,9 +92,9 @@ pub fn main() void { fail_count += 1; progress.log("FAIL ({s})\n", .{@errorName(err)}); if (!have_tty) std.debug.print("FAIL ({s})\n", .{@errorName(err)}); - if (builtin.zig_backend != .stage2_llvm) if (@errorReturnTrace()) |trace| { + if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); - }; + } test_node.end(); }, } diff --git a/src/Sema.zig b/src/Sema.zig index ab2a59f44a..1801806084 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1412,6 +1412,12 @@ fn analyzeAsType( } pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void { + const backend_supports_error_return_tracing = false; + if (!backend_supports_error_return_tracing) { + // TODO implement this feature in all the backends and then delete this branch + return; + } + var err_trace_block = block.makeSubBlock(); err_trace_block.is_comptime = false; defer err_trace_block.instructions.deinit(sema.gpa); @@ -12655,7 +12661,12 @@ fn analyzeRet( return always_noreturn; } - if (sema.fn_ret_ty.isError() and sema.mod.comp.bin_file.options.error_return_tracing) { + // TODO implement this feature in all the backends and then delete this check. + const backend_supports_error_return_tracing = false; + + if (sema.fn_ret_ty.isError() and sema.mod.comp.bin_file.options.error_return_tracing and + backend_supports_error_return_tracing) + { const return_err_fn = try sema.getBuiltin(block, src, "returnError"); const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); @@ -13397,9 +13408,14 @@ fn zirErrorReturnTrace( const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); const opt_ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty); + + // TODO implement this feature in all the backends and then delete this check. + const backend_supports_error_return_tracing = false; + if (sema.owner_func != null and sema.owner_func.?.calls_or_awaits_errorable_fn and - sema.mod.comp.bin_file.options.error_return_tracing) + sema.mod.comp.bin_file.options.error_return_tracing and + backend_supports_error_return_tracing) { return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty); } diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 56b7b87499..95a29b840e 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -2333,21 +2333,17 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { } fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[inst].ty_op; + _ = inst; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + return self.finishAir(inst, result, .{ .none, .none, .none }); } fn airSetErrReturnTrace(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 - return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + _ = inst; + return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); } fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index f09b37ee69..0154548911 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1846,21 +1846,17 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { } fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[inst].ty_op; + _ = inst; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + return self.finishAir(inst, result, .{ .none, .none, .none }); } fn airSetErrReturnTrace(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 - return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + _ = inst; + return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); } /// T to E!T diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 6d813cfbbb..22c97b9aec 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1270,21 +1270,17 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { } fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[inst].ty_op; + _ = inst; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + return self.finishAir(inst, result, .{ .none, .none, .none }); } fn airSetErrReturnTrace(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 - return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + _ = inst; + return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); } fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 31ecc58c66..bb3ebec5db 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1858,21 +1858,17 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { } fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { - const ty_op = self.air.instructions.items(.data)[inst].ty_op; + _ = inst; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + return self.finishAir(inst, result, .{ .none, .none, .none }); } fn airSetErrReturnTrace(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 - return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + _ = inst; + return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); } fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index c3ca79dabe..92770168f4 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -3451,33 +3451,11 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { fn airErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; - - const un_op = f.air.instructions.items(.data)[inst].un_op; - const writer = f.object.writer(); - const inst_ty = f.air.typeOfIndex(inst); - const operand = try f.resolveInst(un_op); - const local = try f.allocLocal(inst_ty, .Const); - - try writer.writeAll(" = "); - - _ = operand; - _ = local; return f.fail("TODO: C backend: implement airErrReturnTrace", .{}); } fn airSetErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; - - const un_op = f.air.instructions.items(.data)[inst].un_op; - const writer = f.object.writer(); - const inst_ty = f.air.typeOfIndex(inst); - const operand = try f.resolveInst(un_op); - const local = try f.allocLocal(inst_ty, .Const); - - try writer.writeAll(" = "); - - _ = operand; - _ = local; + _ = inst; return f.fail("TODO: C backend: implement airSetErrReturnTrace", .{}); } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index dfb0f8f03b..f6bb989f37 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -637,7 +637,7 @@ pub const Object = struct { const gpa = dg.gpa; const err_return_tracing = fn_info.return_type.isError() and - dg.module.comp.bin_file.options.error_return_tracing; + dg.module.comp.bin_file.options.error_return_tracing and false; const err_ret_trace = if (err_return_tracing) llvm_func.getParam(@boolToInt(ret_ptr != null)) @@ -1765,7 +1765,7 @@ pub const Object = struct { } if (fn_info.return_type.isError() and - o.module.comp.bin_file.options.error_return_tracing) + o.module.comp.bin_file.options.error_return_tracing and false) { var ptr_ty_payload: Type.Payload.ElemType = .{ .base = .{ .tag = .single_mut_pointer }, @@ -2018,7 +2018,7 @@ pub const DeclGen = struct { } const err_return_tracing = fn_info.return_type.isError() and - dg.module.comp.bin_file.options.error_return_tracing; + dg.module.comp.bin_file.options.error_return_tracing and false; if (err_return_tracing) { dg.addArgAttr(llvm_fn, @boolToInt(sret), "nonnull"); @@ -2484,7 +2484,7 @@ pub const DeclGen = struct { } if (fn_info.return_type.isError() and - dg.module.comp.bin_file.options.error_return_tracing) + dg.module.comp.bin_file.options.error_return_tracing and false) { var ptr_ty_payload: Type.Payload.ElemType = .{ .base = .{ .tag = .single_mut_pointer }, @@ -3796,7 +3796,7 @@ pub const FuncGen = struct { }; if (fn_info.return_type.isError() and - self.dg.module.comp.bin_file.options.error_return_tracing) + self.dg.module.comp.bin_file.options.error_return_tracing and false) { try llvm_args.append(self.err_ret_trace.?); } From 53a5aee3b3684a03c91236702c9304dce21279e2 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 23 Apr 2022 09:38:38 +0300 Subject: [PATCH 1545/2031] stage2: enable error return tracing on llvm backend --- src/Compilation.zig | 3 ++- src/Sema.zig | 22 +++++++++++----------- src/codegen/llvm.zig | 13 ++++++++----- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 407c753193..687b0a6dc6 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1457,7 +1457,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { errdefer if (module) |zm| zm.deinit(); const error_return_tracing = !strip and switch (options.optimize_mode) { - .Debug, .ReleaseSafe => true, + .Debug, .ReleaseSafe => (!options.target.isWasm() or options.target.os.tag == .emscripten) and + !options.target.cpu.arch.isBpf(), .ReleaseFast, .ReleaseSmall => false, }; diff --git a/src/Sema.zig b/src/Sema.zig index 1801806084..86f06c3ad3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1412,7 +1412,8 @@ fn analyzeAsType( } pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void { - const backend_supports_error_return_tracing = false; + const backend_supports_error_return_tracing = + sema.mod.comp.bin_file.options.use_llvm; if (!backend_supports_error_return_tracing) { // TODO implement this feature in all the backends and then delete this branch return; @@ -5275,10 +5276,6 @@ fn analyzeCall( try sema.queueFullTypeResolution(func_ty_info.return_type); if (sema.owner_func != null and func_ty_info.return_type.isError()) { - if (!sema.owner_func.?.calls_or_awaits_errorable_fn) { - // Ensure the type exists so that backends can assume that. - _ = try sema.getBuiltinType(block, call_src, "StackTrace"); - } sema.owner_func.?.calls_or_awaits_errorable_fn = true; } @@ -5692,10 +5689,6 @@ fn instantiateGenericCall( } if (sema.owner_func != null and new_fn_info.return_type.isError()) { - if (!sema.owner_func.?.calls_or_awaits_errorable_fn) { - // Ensure the type exists so that backends can assume that. - _ = try sema.getBuiltinType(block, call_src, "StackTrace"); - } sema.owner_func.?.calls_or_awaits_errorable_fn = true; } @@ -12662,7 +12655,8 @@ fn analyzeRet( } // TODO implement this feature in all the backends and then delete this check. - const backend_supports_error_return_tracing = false; + const backend_supports_error_return_tracing = + sema.mod.comp.bin_file.options.use_llvm; if (sema.fn_ret_ty.isError() and sema.mod.comp.bin_file.options.error_return_tracing and backend_supports_error_return_tracing) @@ -13410,7 +13404,8 @@ fn zirErrorReturnTrace( const opt_ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty); // TODO implement this feature in all the backends and then delete this check. - const backend_supports_error_return_tracing = false; + const backend_supports_error_return_tracing = + sema.mod.comp.bin_file.options.use_llvm; if (sema.owner_func != null and sema.owner_func.?.calls_or_awaits_errorable_fn and @@ -21966,6 +21961,11 @@ pub fn resolveFnTypes( ) CompileError!void { try sema.resolveTypeFully(block, src, fn_info.return_type); + if (sema.mod.comp.bin_file.options.error_return_tracing and fn_info.return_type.isError()) { + // Ensure the type exists so that backends can assume that. + _ = try sema.getBuiltinType(block, src, "StackTrace"); + } + for (fn_info.param_types) |param_ty| { try sema.resolveTypeFully(block, src, param_ty); } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index f6bb989f37..9998f1d40f 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -637,7 +637,7 @@ pub const Object = struct { const gpa = dg.gpa; const err_return_tracing = fn_info.return_type.isError() and - dg.module.comp.bin_file.options.error_return_tracing and false; + dg.module.comp.bin_file.options.error_return_tracing; const err_ret_trace = if (err_return_tracing) llvm_func.getParam(@boolToInt(ret_ptr != null)) @@ -698,6 +698,9 @@ pub const Object = struct { const lexical_block = dib.createLexicalBlock(subprogram.toScope(), di_file.?, line_number, 1); di_scope = lexical_block.toScope(); + + // Setup a debug location in case there is a call to `returnError` before a `.dbg_stmt`. + builder.setCurrentDebugLocation(line_number + func.lbrace_line, func.lbrace_column, di_scope.?, null); } var fg: FuncGen = .{ @@ -1765,7 +1768,7 @@ pub const Object = struct { } if (fn_info.return_type.isError() and - o.module.comp.bin_file.options.error_return_tracing and false) + o.module.comp.bin_file.options.error_return_tracing) { var ptr_ty_payload: Type.Payload.ElemType = .{ .base = .{ .tag = .single_mut_pointer }, @@ -2018,7 +2021,7 @@ pub const DeclGen = struct { } const err_return_tracing = fn_info.return_type.isError() and - dg.module.comp.bin_file.options.error_return_tracing and false; + dg.module.comp.bin_file.options.error_return_tracing; if (err_return_tracing) { dg.addArgAttr(llvm_fn, @boolToInt(sret), "nonnull"); @@ -2484,7 +2487,7 @@ pub const DeclGen = struct { } if (fn_info.return_type.isError() and - dg.module.comp.bin_file.options.error_return_tracing and false) + dg.module.comp.bin_file.options.error_return_tracing) { var ptr_ty_payload: Type.Payload.ElemType = .{ .base = .{ .tag = .single_mut_pointer }, @@ -3796,7 +3799,7 @@ pub const FuncGen = struct { }; if (fn_info.return_type.isError() and - self.dg.module.comp.bin_file.options.error_return_tracing and false) + self.dg.module.comp.bin_file.options.error_return_tracing) { try llvm_args.append(self.err_ret_trace.?); } From e369752430a1b3a50e57e11b9f0682d026c62feb Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 23 Apr 2022 14:48:16 +0300 Subject: [PATCH 1546/2031] Sema: do not call `returnError` when returning payload of error union --- src/Module.zig | 2 +- src/Sema.zig | 3 ++- src/codegen/llvm.zig | 3 --- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index cb38ddba45..11549ccda6 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4839,7 +4839,7 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air { }; defer sema.deinit(); - // reset in case case calls to errorable functions are removed. + // reset in case calls to errorable functions are removed. func.calls_or_awaits_errorable_fn = false; // First few indexes of extra are reserved and set at the end. diff --git a/src/Sema.zig b/src/Sema.zig index 86f06c3ad3..a7dd905dd6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12658,7 +12658,8 @@ fn analyzeRet( const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm; - if (sema.fn_ret_ty.isError() and sema.mod.comp.bin_file.options.error_return_tracing and + if ((sema.fn_ret_ty.zigTypeTag() == .ErrorSet or sema.typeOf(uncasted_operand).zigTypeTag() == .ErrorUnion) and + sema.mod.comp.bin_file.options.error_return_tracing and backend_supports_error_return_tracing) { const return_err_fn = try sema.getBuiltin(block, src, "returnError"); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 9998f1d40f..dfb0f8f03b 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -698,9 +698,6 @@ pub const Object = struct { const lexical_block = dib.createLexicalBlock(subprogram.toScope(), di_file.?, line_number, 1); di_scope = lexical_block.toScope(); - - // Setup a debug location in case there is a call to `returnError` before a `.dbg_stmt`. - builder.setCurrentDebugLocation(line_number + func.lbrace_line, func.lbrace_column, di_scope.?, null); } var fg: FuncGen = .{ From ab4ec35b8bb3a19361afa315f77cce5f6054b109 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 23 Apr 2022 11:13:31 +0300 Subject: [PATCH 1547/2031] stage2: add runtime safety for unwrapping error --- lib/compiler_rt.zig | 24 +++++----- lib/std/builtin.zig | 4 ++ src/AstGen.zig | 27 +++++++++++ src/Sema.zig | 106 ++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 139 insertions(+), 22 deletions(-) diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig index fdf5940702..563d3d0820 100644 --- a/lib/compiler_rt.zig +++ b/lib/compiler_rt.zig @@ -198,19 +198,17 @@ comptime { const __trunctfxf2 = @import("compiler_rt/trunc_f80.zig").__trunctfxf2; @export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage }); - if (builtin.zig_backend == .stage1) { // TODO - switch (arch) { - .i386, - .x86_64, - => { - const zig_probe_stack = @import("compiler_rt/stack_probe.zig").zig_probe_stack; - @export(zig_probe_stack, .{ - .name = "__zig_probe_stack", - .linkage = linkage, - }); - }, - else => {}, - } + switch (arch) { + .i386, + .x86_64, + => { + const zig_probe_stack = @import("compiler_rt/stack_probe.zig").zig_probe_stack; + @export(zig_probe_stack, .{ + .name = "__zig_probe_stack", + .linkage = linkage, + }); + }, + else => {}, } const __unordsf2 = @import("compiler_rt/compareXf2.zig").__unordsf2; diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index a8069fa490..02aa7239f5 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -846,6 +846,10 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn } } +pub fn panicUnwrapError(st: ?*StackTrace, err: anyerror) noreturn { + std.debug.panicExtra(st, "attempt to unwrap error: {s}", .{@errorName(err)}); +} + pub noinline fn returnError(maybe_st: ?*StackTrace) void { @setCold(true); const st = maybe_st orelse return; diff --git a/src/AstGen.zig b/src/AstGen.zig index d4895aa2e4..3e502625db 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -856,6 +856,33 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr catch_token + 2 else null; + + var rhs = node_datas[node].rhs; + while (true) switch (node_tags[rhs]) { + .grouped_expression => rhs = node_datas[rhs].lhs, + .unreachable_literal => { + if (payload_token != null and mem.eql(u8, tree.tokenSlice(payload_token.?), "_")) { + return astgen.failTok(payload_token.?, "discard of error capture; omit it instead", .{}); + } else if (payload_token != null) { + return astgen.failTok(payload_token.?, "unused capture", .{}); + } + const lhs = node_datas[node].lhs; + + const operand = try reachableExpr(gz, scope, switch (rl) { + .ref => .ref, + else => .none, + }, lhs, lhs); + const result = try gz.addUnNode(switch (rl) { + .ref => .err_union_payload_safe_ptr, + else => .err_union_payload_safe, + }, operand, node); + switch (rl) { + .none, .coerced_ty, .discard, .ref => return result, + else => return rvalue(gz, rl, result, lhs), + } + }, + else => break, + }; switch (rl) { .ref => return orelseCatchExpr( gz, diff --git a/src/Sema.zig b/src/Sema.zig index a7dd905dd6..76888834d3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6248,8 +6248,7 @@ fn zirErrUnionPayload( } try sema.requireRuntimeBlock(block, src); if (safety_check and block.wantSafety()) { - const is_non_err = try block.addUnOp(.is_err, operand); - try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion); + try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err); } const result_ty = operand_ty.errorUnionPayload(); return block.addTyOp(.unwrap_errunion_payload, result_ty, operand); @@ -6330,8 +6329,7 @@ fn analyzeErrUnionPayloadPtr( try sema.requireRuntimeBlock(block, src); if (safety_check and block.wantSafety()) { - const is_non_err = try block.addUnOp(.is_err, operand); - try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion); + try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr); } const air_tag: Air.Inst.Tag = if (initializing) .errunion_payload_ptr_set @@ -13400,6 +13398,10 @@ fn zirErrorReturnTrace( extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; + return sema.getErrorReturnTrace(block, src); +} + +fn getErrorReturnTrace(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError!Air.Inst.Ref { const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); const opt_ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty); @@ -16852,7 +16854,6 @@ fn explainWhyTypeIsComptime( pub const PanicId = enum { unreach, unwrap_null, - unwrap_errunion, cast_to_null, incorrect_alignment, invalid_error_code, @@ -16962,12 +16963,100 @@ fn panicWithMsg( try Type.optional(arena, ptr_stack_trace_ty), Value.@"null", ); - const args = try arena.create([2]Air.Inst.Ref); - args.* = .{ msg_inst, null_stack_trace }; - _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, args); + const args: [2]Air.Inst.Ref = .{ msg_inst, null_stack_trace }; + _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args); return always_noreturn; } +fn panicUnwrapError( + sema: *Sema, + parent_block: *Block, + src: LazySrcLoc, + operand: Air.Inst.Ref, + unwrap_err_tag: Air.Inst.Tag, + is_non_err_tag: Air.Inst.Tag, +) !void { + const ok = try parent_block.addUnOp(is_non_err_tag, operand); + const gpa = sema.gpa; + + var fail_block: Block = .{ + .parent = parent_block, + .sema = sema, + .src_decl = parent_block.src_decl, + .namespace = parent_block.namespace, + .wip_capture_scope = parent_block.wip_capture_scope, + .instructions = .{}, + .inlining = parent_block.inlining, + .is_comptime = parent_block.is_comptime, + }; + + defer fail_block.instructions.deinit(gpa); + + { + const this_feature_is_implemented_in_the_backend = + sema.mod.comp.bin_file.options.object_format == .c or + sema.mod.comp.bin_file.options.use_llvm; + + if (!this_feature_is_implemented_in_the_backend) { + // TODO implement this feature in all the backends and then delete this branch + _ = try fail_block.addNoOp(.breakpoint); + _ = try fail_block.addNoOp(.unreach); + } else { + const panic_fn = try sema.getBuiltin(&fail_block, src, "panicUnwrapError"); + const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand); + const err_return_trace = try sema.getErrorReturnTrace(&fail_block, src); + const args: [2]Air.Inst.Ref = .{ err_return_trace, err }; + _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args); + } + } + + try parent_block.instructions.ensureUnusedCapacity(gpa, 1); + + try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + + 1 + // The main block only needs space for the cond_br. + @typeInfo(Air.CondBr).Struct.fields.len + + 1 + // The ok branch of the cond_br only needs space for the br. + fail_block.instructions.items.len); + + try sema.air_instructions.ensureUnusedCapacity(gpa, 3); + const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len); + const cond_br_inst = block_inst + 1; + const br_inst = cond_br_inst + 1; + sema.air_instructions.appendAssumeCapacity(.{ + .tag = .block, + .data = .{ .ty_pl = .{ + .ty = .void_type, + .payload = sema.addExtraAssumeCapacity(Air.Block{ + .body_len = 1, + }), + } }, + }); + sema.air_extra.appendAssumeCapacity(cond_br_inst); + + sema.air_instructions.appendAssumeCapacity(.{ + .tag = .cond_br, + .data = .{ .pl_op = .{ + .operand = ok, + .payload = sema.addExtraAssumeCapacity(Air.CondBr{ + .then_body_len = 1, + .else_body_len = @intCast(u32, fail_block.instructions.items.len), + }), + } }, + }); + sema.air_extra.appendAssumeCapacity(br_inst); + sema.air_extra.appendSliceAssumeCapacity(fail_block.instructions.items); + + sema.air_instructions.appendAssumeCapacity(.{ + .tag = .br, + .data = .{ .br = .{ + .block_inst = block_inst, + .operand = .void_value, + } }, + }); + + parent_block.instructions.appendAssumeCapacity(block_inst); +} + fn safetyPanic( sema: *Sema, block: *Block, @@ -16977,7 +17066,6 @@ fn safetyPanic( const msg = switch (panic_id) { .unreach => "reached unreachable code", .unwrap_null => "attempt to use null value", - .unwrap_errunion => "unreachable error occurred", .cast_to_null => "cast causes pointer to be null", .incorrect_alignment => "incorrect alignment", .invalid_error_code => "invalid error code", From 0a7f3be42e96361ab8a9a567a11782fb81ea17da Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 23 Apr 2022 11:25:06 +0300 Subject: [PATCH 1548/2031] Sema: improve index out of bounds panic message --- lib/std/builtin.zig | 6 +++ src/Sema.zig | 110 ++++++++++++++++++++++---------------------- 2 files changed, 60 insertions(+), 56 deletions(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 02aa7239f5..894103707c 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -847,9 +847,15 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn } pub fn panicUnwrapError(st: ?*StackTrace, err: anyerror) noreturn { + @setCold(true); std.debug.panicExtra(st, "attempt to unwrap error: {s}", .{@errorName(err)}); } +pub fn panicOutOfBounds(index: usize, len: usize) noreturn { + @setCold(true); + std.debug.panic("attempt to index out of bound: index {d}, len {d}", .{ index, len }); +} + pub noinline fn returnError(maybe_st: ?*StackTrace) void { @setCold(true); const st = maybe_st orelse return; diff --git a/src/Sema.zig b/src/Sema.zig index 76888834d3..c45079eaa7 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -16857,7 +16857,6 @@ pub const PanicId = enum { cast_to_null, incorrect_alignment, invalid_error_code, - index_out_of_bounds, cast_truncated_data, integer_overflow, shl_overflow, @@ -16886,6 +16885,17 @@ fn addSafetyCheck( _ = try sema.safetyPanic(&fail_block, .unneeded, panic_id); + try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); +} + +fn addSafetyCheckExtra( + sema: *Sema, + parent_block: *Block, + ok: Air.Inst.Ref, + fail_block: *Block, +) !void { + const gpa = sema.gpa; + try parent_block.instructions.ensureUnusedCapacity(gpa, 1); try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + @@ -16994,7 +17004,6 @@ fn panicUnwrapError( { const this_feature_is_implemented_in_the_backend = - sema.mod.comp.bin_file.options.object_format == .c or sema.mod.comp.bin_file.options.use_llvm; if (!this_feature_is_implemented_in_the_backend) { @@ -17009,52 +17018,48 @@ fn panicUnwrapError( _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args); } } + try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); +} - try parent_block.instructions.ensureUnusedCapacity(gpa, 1); +fn panicIndexOutOfBounds( + sema: *Sema, + parent_block: *Block, + src: LazySrcLoc, + index: Air.Inst.Ref, + len: Air.Inst.Ref, + cmp_op: Air.Inst.Tag, +) !void { + const ok = try parent_block.addBinOp(cmp_op, index, len); + const gpa = sema.gpa; - try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + - 1 + // The main block only needs space for the cond_br. - @typeInfo(Air.CondBr).Struct.fields.len + - 1 + // The ok branch of the cond_br only needs space for the br. - fail_block.instructions.items.len); + var fail_block: Block = .{ + .parent = parent_block, + .sema = sema, + .src_decl = parent_block.src_decl, + .namespace = parent_block.namespace, + .wip_capture_scope = parent_block.wip_capture_scope, + .instructions = .{}, + .inlining = parent_block.inlining, + .is_comptime = parent_block.is_comptime, + }; - try sema.air_instructions.ensureUnusedCapacity(gpa, 3); - const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len); - const cond_br_inst = block_inst + 1; - const br_inst = cond_br_inst + 1; - sema.air_instructions.appendAssumeCapacity(.{ - .tag = .block, - .data = .{ .ty_pl = .{ - .ty = .void_type, - .payload = sema.addExtraAssumeCapacity(Air.Block{ - .body_len = 1, - }), - } }, - }); - sema.air_extra.appendAssumeCapacity(cond_br_inst); + defer fail_block.instructions.deinit(gpa); - sema.air_instructions.appendAssumeCapacity(.{ - .tag = .cond_br, - .data = .{ .pl_op = .{ - .operand = ok, - .payload = sema.addExtraAssumeCapacity(Air.CondBr{ - .then_body_len = 1, - .else_body_len = @intCast(u32, fail_block.instructions.items.len), - }), - } }, - }); - sema.air_extra.appendAssumeCapacity(br_inst); - sema.air_extra.appendSliceAssumeCapacity(fail_block.instructions.items); + { + const this_feature_is_implemented_in_the_backend = + sema.mod.comp.bin_file.options.use_llvm; - sema.air_instructions.appendAssumeCapacity(.{ - .tag = .br, - .data = .{ .br = .{ - .block_inst = block_inst, - .operand = .void_value, - } }, - }); - - parent_block.instructions.appendAssumeCapacity(block_inst); + if (!this_feature_is_implemented_in_the_backend) { + // TODO implement this feature in all the backends and then delete this branch + _ = try fail_block.addNoOp(.breakpoint); + _ = try fail_block.addNoOp(.unreach); + } else { + const panic_fn = try sema.getBuiltin(&fail_block, src, "panicOutOfBounds"); + const args: [2]Air.Inst.Ref = .{ index, len }; + _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args); + } + } + try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); } fn safetyPanic( @@ -17069,7 +17074,6 @@ fn safetyPanic( .cast_to_null => "cast causes pointer to be null", .incorrect_alignment => "incorrect alignment", .invalid_error_code => "invalid error code", - .index_out_of_bounds => "attempt to index out of bounds", .cast_truncated_data => "integer cast truncated bits", .integer_overflow => "integer overflow", .shl_overflow => "left shift overflowed bits", @@ -18261,8 +18265,7 @@ fn elemValArray( if (maybe_index_val == null) { const len_inst = try sema.addIntUnsigned(Type.usize, array_len); const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt; - const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst); - try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds); + try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op); } } return block.addBinOp(.array_elem_val, array, elem_index); @@ -18317,8 +18320,7 @@ fn elemPtrArray( if (maybe_index_val == null) { const len_inst = try sema.addIntUnsigned(Type.usize, array_len); const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt; - const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst); - try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds); + try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op); } } return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty); @@ -18371,8 +18373,7 @@ fn elemValSlice( else try block.addTyOp(.slice_len, Type.usize, slice); const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; - const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst); - try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds); + try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op); } try sema.queueFullTypeResolution(sema.typeOf(slice)); return block.addBinOp(.slice_elem_val, slice, elem_index); @@ -18425,8 +18426,7 @@ fn elemPtrSlice( break :len try block.addTyOp(.slice_len, Type.usize, slice); }; const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; - const is_in_bounds = try block.addBinOp(cmp_op, elem_index, len_inst); - try sema.addSafetyCheck(block, is_in_bounds, .index_out_of_bounds); + try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op); } return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty); } @@ -21111,13 +21111,11 @@ fn analyzeSlice( break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src); } else null; if (opt_len_inst) |len_inst| { - const end_is_in_bounds = try block.addBinOp(.cmp_lte, end, len_inst); - try sema.addSafetyCheck(block, end_is_in_bounds, .index_out_of_bounds); + try sema.panicIndexOutOfBounds(block, src, end, len_inst, .cmp_lte); } // requirement: start <= end - const start_is_in_bounds = try block.addBinOp(.cmp_lte, start, end); - try sema.addSafetyCheck(block, start_is_in_bounds, .index_out_of_bounds); + try sema.panicIndexOutOfBounds(block, src, start, end, .cmp_lte); } return block.addInst(.{ .tag = .slice, From 6d27341b96240a4ae70c794eb05dec452fd31451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Thu, 5 May 2022 10:24:41 +0200 Subject: [PATCH 1549/2031] Fixes comptime 'error: cannot assign to constant' error in siphash. --- lib/std/crypto/siphash.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/std/crypto/siphash.zig b/lib/std/crypto/siphash.zig index 50706a0f35..dff8005b31 100644 --- a/lib/std/crypto/siphash.zig +++ b/lib/std/crypto/siphash.zig @@ -78,9 +78,12 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round pub fn update(self: *Self, b: []const u8) void { std.debug.assert(b.len % 8 == 0); + const inl = std.builtin.CallOptions{ .modifier = .always_inline }; + var off: usize = 0; while (off < b.len) : (off += 8) { - @call(.{ .modifier = .always_inline }, self.round, .{b[off..][0..8].*}); + const blob = b[off..][0..8].*; + @call(inl, round, .{ self, blob }); } self.msg_len +%= @truncate(u8, b.len); From a4369918b19e4920f51f40a2b05781dda45462f7 Mon Sep 17 00:00:00 2001 From: Stephen Gregoratto Date: Sat, 14 May 2022 19:42:31 +1000 Subject: [PATCH 1550/2031] Generate linux syscalls via. the linux source tree Previously, updating the `SYS` enum for each architecture required manually looking at the syscall tables and inserting any new additions. This commit adds a tool, `generate_linux_syscalls.zig`, that automates this process using the syscall tables in the Linux source tree. On architectures without a table, it runs `zig cc` as a pre-processor to extract the system-call numbers from the Linux headers. --- lib/std/os/linux.zig | 15 +- lib/std/os/linux/arm-eabi.zig | 412 +--- lib/std/os/linux/arm64.zig | 306 +-- lib/std/os/linux/i386.zig | 442 +--- lib/std/os/linux/mips.zig | 428 +--- lib/std/os/linux/powerpc.zig | 431 +--- lib/std/os/linux/powerpc64.zig | 405 +--- lib/std/os/linux/riscv64.zig | 311 +-- lib/std/os/linux/sparc64.zig | 385 +--- lib/std/os/linux/syscalls.zig | 3493 +++++++++++++++++++++++++++++ lib/std/os/linux/x86_64.zig | 364 +-- tools/generate_linux_syscalls.zig | 356 +++ 12 files changed, 3872 insertions(+), 3476 deletions(-) create mode 100644 lib/std/os/linux/syscalls.zig create mode 100644 tools/generate_linux_syscalls.zig diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 35d2e88c17..4c85e9c36d 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -67,7 +67,6 @@ pub const LOCK = arch_bits.LOCK; pub const MMAP2_UNIT = arch_bits.MMAP2_UNIT; pub const REG = arch_bits.REG; pub const SC = arch_bits.SC; -pub const SYS = arch_bits.SYS; pub const Stat = arch_bits.Stat; pub const VDSO = arch_bits.VDSO; pub const blkcnt_t = arch_bits.blkcnt_t; @@ -93,6 +92,20 @@ pub const BPF = @import("linux/bpf.zig"); pub const IOCTL = @import("linux/ioctl.zig"); pub const SECCOMP = @import("linux/seccomp.zig"); +pub const syscalls = @import("linux/syscalls.zig"); +pub const SYS = switch (@import("builtin").cpu.arch) { + .i386 => syscalls.X86, + .x86_64 => syscalls.X64, + .aarch64 => syscalls.Arm64, + .arm, .thumb => syscalls.Arm, + .riscv64 => syscalls.RiscV64, + .sparc64 => syscalls.Sparc64, + .mips, .mipsel => syscalls.Mips, + .powerpc => syscalls.PowerPC, + .powerpc64, .powerpc64le => syscalls.PowerPC64, + else => @compileError("The Zig Standard Library is missing syscall definitions for the target CPU architecture"), +}; + pub const MAP = struct { pub usingnamespace arch_bits.MAP; diff --git a/lib/std/os/linux/arm-eabi.zig b/lib/std/os/linux/arm-eabi.zig index 7f4a7385f2..c3a94312a0 100644 --- a/lib/std/os/linux/arm-eabi.zig +++ b/lib/std/os/linux/arm-eabi.zig @@ -1,6 +1,7 @@ const std = @import("../../std.zig"); const maxInt = std.math.maxInt; const linux = std.os.linux; +const SYS = linux.SYS; const iovec = std.os.iovec; const iovec_const = std.os.iovec_const; const socklen_t = linux.socklen_t; @@ -127,417 +128,6 @@ pub fn restore_rt() callconv(.Naked) void { ); } -pub const SYS = enum(usize) { - restart_syscall = 0, - exit = 1, - fork = 2, - read = 3, - write = 4, - open = 5, - close = 6, - creat = 8, - link = 9, - unlink = 10, - execve = 11, - chdir = 12, - mknod = 14, - chmod = 15, - lchown = 16, - lseek = 19, - getpid = 20, - mount = 21, - setuid = 23, - getuid = 24, - ptrace = 26, - pause = 29, - access = 33, - nice = 34, - sync = 36, - kill = 37, - rename = 38, - mkdir = 39, - rmdir = 40, - dup = 41, - pipe = 42, - times = 43, - brk = 45, - setgid = 46, - getgid = 47, - geteuid = 49, - getegid = 50, - acct = 51, - umount2 = 52, - ioctl = 54, - fcntl = 55, - setpgid = 57, - umask = 60, - chroot = 61, - ustat = 62, - dup2 = 63, - getppid = 64, - getpgrp = 65, - setsid = 66, - sigaction = 67, - setreuid = 70, - setregid = 71, - sigsuspend = 72, - sigpending = 73, - sethostname = 74, - setrlimit = 75, - getrusage = 77, - gettimeofday = 78, - settimeofday = 79, - getgroups = 80, - setgroups = 81, - symlink = 83, - readlink = 85, - uselib = 86, - swapon = 87, - reboot = 88, - munmap = 91, - truncate = 92, - ftruncate = 93, - fchmod = 94, - fchown = 95, - getpriority = 96, - setpriority = 97, - statfs = 99, - fstatfs = 100, - syslog = 103, - setitimer = 104, - getitimer = 105, - stat = 106, - lstat = 107, - fstat = 108, - vhangup = 111, - wait4 = 114, - swapoff = 115, - sysinfo = 116, - fsync = 118, - sigreturn = 119, - clone = 120, - setdomainname = 121, - uname = 122, - adjtimex = 124, - mprotect = 125, - sigprocmask = 126, - init_module = 128, - delete_module = 129, - quotactl = 131, - getpgid = 132, - fchdir = 133, - bdflush = 134, - sysfs = 135, - personality = 136, - setfsuid = 138, - setfsgid = 139, - _llseek = 140, - getdents = 141, - _newselect = 142, - flock = 143, - msync = 144, - readv = 145, - writev = 146, - getsid = 147, - fdatasync = 148, - _sysctl = 149, - mlock = 150, - munlock = 151, - mlockall = 152, - munlockall = 153, - sched_setparam = 154, - sched_getparam = 155, - sched_setscheduler = 156, - sched_getscheduler = 157, - sched_yield = 158, - sched_get_priority_max = 159, - sched_get_priority_min = 160, - sched_rr_get_interval = 161, - nanosleep = 162, - mremap = 163, - setresuid = 164, - getresuid = 165, - poll = 168, - nfsservctl = 169, - setresgid = 170, - getresgid = 171, - prctl = 172, - rt_sigreturn = 173, - rt_sigaction = 174, - rt_sigprocmask = 175, - rt_sigpending = 176, - rt_sigtimedwait = 177, - rt_sigqueueinfo = 178, - rt_sigsuspend = 179, - pread64 = 180, - pwrite64 = 181, - chown = 182, - getcwd = 183, - capget = 184, - capset = 185, - sigaltstack = 186, - sendfile = 187, - vfork = 190, - ugetrlimit = 191, - mmap2 = 192, - truncate64 = 193, - ftruncate64 = 194, - stat64 = 195, - lstat64 = 196, - fstat64 = 197, - lchown32 = 198, - getuid32 = 199, - getgid32 = 200, - geteuid32 = 201, - getegid32 = 202, - setreuid32 = 203, - setregid32 = 204, - getgroups32 = 205, - setgroups32 = 206, - fchown32 = 207, - setresuid32 = 208, - getresuid32 = 209, - setresgid32 = 210, - getresgid32 = 211, - chown32 = 212, - setuid32 = 213, - setgid32 = 214, - setfsuid32 = 215, - setfsgid32 = 216, - getdents64 = 217, - pivot_root = 218, - mincore = 219, - madvise = 220, - fcntl64 = 221, - gettid = 224, - readahead = 225, - setxattr = 226, - lsetxattr = 227, - fsetxattr = 228, - getxattr = 229, - lgetxattr = 230, - fgetxattr = 231, - listxattr = 232, - llistxattr = 233, - flistxattr = 234, - removexattr = 235, - lremovexattr = 236, - fremovexattr = 237, - tkill = 238, - sendfile64 = 239, - futex = 240, - sched_setaffinity = 241, - sched_getaffinity = 242, - io_setup = 243, - io_destroy = 244, - io_getevents = 245, - io_submit = 246, - io_cancel = 247, - exit_group = 248, - lookup_dcookie = 249, - epoll_create = 250, - epoll_ctl = 251, - epoll_wait = 252, - remap_file_pages = 253, - set_tid_address = 256, - timer_create = 257, - timer_settime = 258, - timer_gettime = 259, - timer_getoverrun = 260, - timer_delete = 261, - clock_settime = 262, - clock_gettime = 263, - clock_getres = 264, - clock_nanosleep = 265, - statfs64 = 266, - fstatfs64 = 267, - tgkill = 268, - utimes = 269, - fadvise64_64 = 270, - pciconfig_iobase = 271, - pciconfig_read = 272, - pciconfig_write = 273, - mq_open = 274, - mq_unlink = 275, - mq_timedsend = 276, - mq_timedreceive = 277, - mq_notify = 278, - mq_getsetattr = 279, - waitid = 280, - socket = 281, - bind = 282, - connect = 283, - listen = 284, - accept = 285, - getsockname = 286, - getpeername = 287, - socketpair = 288, - send = 289, - sendto = 290, - recv = 291, - recvfrom = 292, - shutdown = 293, - setsockopt = 294, - getsockopt = 295, - sendmsg = 296, - recvmsg = 297, - semop = 298, - semget = 299, - semctl = 300, - msgsnd = 301, - msgrcv = 302, - msgget = 303, - msgctl = 304, - shmat = 305, - shmdt = 306, - shmget = 307, - shmctl = 308, - add_key = 309, - request_key = 310, - keyctl = 311, - semtimedop = 312, - vserver = 313, - ioprio_set = 314, - ioprio_get = 315, - inotify_init = 316, - inotify_add_watch = 317, - inotify_rm_watch = 318, - mbind = 319, - get_mempolicy = 320, - set_mempolicy = 321, - openat = 322, - mkdirat = 323, - mknodat = 324, - fchownat = 325, - futimesat = 326, - fstatat64 = 327, - unlinkat = 328, - renameat = 329, - linkat = 330, - symlinkat = 331, - readlinkat = 332, - fchmodat = 333, - faccessat = 334, - pselect6 = 335, - ppoll = 336, - unshare = 337, - set_robust_list = 338, - get_robust_list = 339, - splice = 340, - sync_file_range = 341, - tee = 342, - vmsplice = 343, - move_pages = 344, - getcpu = 345, - epoll_pwait = 346, - kexec_load = 347, - utimensat = 348, - signalfd = 349, - timerfd_create = 350, - eventfd = 351, - fallocate = 352, - timerfd_settime = 353, - timerfd_gettime = 354, - signalfd4 = 355, - eventfd2 = 356, - epoll_create1 = 357, - dup3 = 358, - pipe2 = 359, - inotify_init1 = 360, - preadv = 361, - pwritev = 362, - rt_tgsigqueueinfo = 363, - perf_event_open = 364, - recvmmsg = 365, - accept4 = 366, - fanotify_init = 367, - fanotify_mark = 368, - prlimit64 = 369, - name_to_handle_at = 370, - open_by_handle_at = 371, - clock_adjtime = 372, - syncfs = 373, - sendmmsg = 374, - setns = 375, - process_vm_readv = 376, - process_vm_writev = 377, - kcmp = 378, - finit_module = 379, - sched_setattr = 380, - sched_getattr = 381, - renameat2 = 382, - seccomp = 383, - getrandom = 384, - memfd_create = 385, - bpf = 386, - execveat = 387, - userfaultfd = 388, - membarrier = 389, - mlock2 = 390, - copy_file_range = 391, - preadv2 = 392, - pwritev2 = 393, - pkey_mprotect = 394, - pkey_alloc = 395, - pkey_free = 396, - statx = 397, - rseq = 398, - io_pgetevents = 399, - migrate_pages = 400, - kexec_file_load = 401, - clock_gettime64 = 403, - clock_settime64 = 404, - clock_adjtime64 = 405, - clock_getres_time64 = 406, - clock_nanosleep_time64 = 407, - timer_gettime64 = 408, - timer_settime64 = 409, - timerfd_gettime64 = 410, - timerfd_settime64 = 411, - utimensat_time64 = 412, - pselect6_time64 = 413, - ppoll_time64 = 414, - io_pgetevents_time64 = 416, - recvmmsg_time64 = 417, - mq_timedsend_time64 = 418, - mq_timedreceive_time64 = 419, - semtimedop_time64 = 420, - rt_sigtimedwait_time64 = 421, - futex_time64 = 422, - sched_rr_get_interval_time64 = 423, - pidfd_send_signal = 424, - io_uring_setup = 425, - io_uring_enter = 426, - io_uring_register = 427, - open_tree = 428, - move_mount = 429, - fsopen = 430, - fsconfig = 431, - fsmount = 432, - fspick = 433, - pidfd_open = 434, - clone3 = 435, - close_range = 436, - openat2 = 437, - pidfd_getfd = 438, - faccessat2 = 439, - process_madvise = 440, - epoll_pwait2 = 441, - mount_setattr = 442, - landlock_create_ruleset = 444, - landlock_add_rule = 445, - landlock_restrict_self = 446, - - breakpoint = 0x0f0001, - cacheflush = 0x0f0002, - usr26 = 0x0f0003, - usr32 = 0x0f0004, - set_tls = 0x0f0005, - get_tls = 0x0f0006, - - _, -}; - pub const MMAP2_UNIT = 4096; pub const O = struct { diff --git a/lib/std/os/linux/arm64.zig b/lib/std/os/linux/arm64.zig index 92edc8a5e0..9bc0e32c19 100644 --- a/lib/std/os/linux/arm64.zig +++ b/lib/std/os/linux/arm64.zig @@ -1,6 +1,7 @@ const std = @import("../../std.zig"); const maxInt = std.math.maxInt; const linux = std.os.linux; +const SYS = linux.SYS; const socklen_t = linux.socklen_t; const sockaddr = linux.sockaddr; const iovec = std.os.iovec; @@ -110,311 +111,6 @@ pub fn restore_rt() callconv(.Naked) void { ); } -pub const SYS = enum(usize) { - io_setup = 0, - io_destroy = 1, - io_submit = 2, - io_cancel = 3, - io_getevents = 4, - setxattr = 5, - lsetxattr = 6, - fsetxattr = 7, - getxattr = 8, - lgetxattr = 9, - fgetxattr = 10, - listxattr = 11, - llistxattr = 12, - flistxattr = 13, - removexattr = 14, - lremovexattr = 15, - fremovexattr = 16, - getcwd = 17, - lookup_dcookie = 18, - eventfd2 = 19, - epoll_create1 = 20, - epoll_ctl = 21, - epoll_pwait = 22, - dup = 23, - dup3 = 24, - fcntl = 25, - inotify_init1 = 26, - inotify_add_watch = 27, - inotify_rm_watch = 28, - ioctl = 29, - ioprio_set = 30, - ioprio_get = 31, - flock = 32, - mknodat = 33, - mkdirat = 34, - unlinkat = 35, - symlinkat = 36, - linkat = 37, - renameat = 38, - umount2 = 39, - mount = 40, - pivot_root = 41, - nfsservctl = 42, - statfs = 43, - fstatfs = 44, - truncate = 45, - ftruncate = 46, - fallocate = 47, - faccessat = 48, - chdir = 49, - fchdir = 50, - chroot = 51, - fchmod = 52, - fchmodat = 53, - fchownat = 54, - fchown = 55, - openat = 56, - close = 57, - vhangup = 58, - pipe2 = 59, - quotactl = 60, - getdents64 = 61, - lseek = 62, - read = 63, - write = 64, - readv = 65, - writev = 66, - pread64 = 67, - pwrite64 = 68, - preadv = 69, - pwritev = 70, - sendfile = 71, - pselect6 = 72, - ppoll = 73, - signalfd4 = 74, - vmsplice = 75, - splice = 76, - tee = 77, - readlinkat = 78, - fstatat = 79, - fstat = 80, - sync = 81, - fsync = 82, - fdatasync = 83, - sync_file_range = 84, - timerfd_create = 85, - timerfd_settime = 86, - timerfd_gettime = 87, - utimensat = 88, - acct = 89, - capget = 90, - capset = 91, - personality = 92, - exit = 93, - exit_group = 94, - waitid = 95, - set_tid_address = 96, - unshare = 97, - futex = 98, - set_robust_list = 99, - get_robust_list = 100, - nanosleep = 101, - getitimer = 102, - setitimer = 103, - kexec_load = 104, - init_module = 105, - delete_module = 106, - timer_create = 107, - timer_gettime = 108, - timer_getoverrun = 109, - timer_settime = 110, - timer_delete = 111, - clock_settime = 112, - clock_gettime = 113, - clock_getres = 114, - clock_nanosleep = 115, - syslog = 116, - ptrace = 117, - sched_setparam = 118, - sched_setscheduler = 119, - sched_getscheduler = 120, - sched_getparam = 121, - sched_setaffinity = 122, - sched_getaffinity = 123, - sched_yield = 124, - sched_get_priority_max = 125, - sched_get_priority_min = 126, - sched_rr_get_interval = 127, - restart_syscall = 128, - kill = 129, - tkill = 130, - tgkill = 131, - sigaltstack = 132, - rt_sigsuspend = 133, - rt_sigaction = 134, - rt_sigprocmask = 135, - rt_sigpending = 136, - rt_sigtimedwait = 137, - rt_sigqueueinfo = 138, - rt_sigreturn = 139, - setpriority = 140, - getpriority = 141, - reboot = 142, - setregid = 143, - setgid = 144, - setreuid = 145, - setuid = 146, - setresuid = 147, - getresuid = 148, - setresgid = 149, - getresgid = 150, - setfsuid = 151, - setfsgid = 152, - times = 153, - setpgid = 154, - getpgid = 155, - getsid = 156, - setsid = 157, - getgroups = 158, - setgroups = 159, - uname = 160, - sethostname = 161, - setdomainname = 162, - getrlimit = 163, - setrlimit = 164, - getrusage = 165, - umask = 166, - prctl = 167, - getcpu = 168, - gettimeofday = 169, - settimeofday = 170, - adjtimex = 171, - getpid = 172, - getppid = 173, - getuid = 174, - geteuid = 175, - getgid = 176, - getegid = 177, - gettid = 178, - sysinfo = 179, - mq_open = 180, - mq_unlink = 181, - mq_timedsend = 182, - mq_timedreceive = 183, - mq_notify = 184, - mq_getsetattr = 185, - msgget = 186, - msgctl = 187, - msgrcv = 188, - msgsnd = 189, - semget = 190, - semctl = 191, - semtimedop = 192, - semop = 193, - shmget = 194, - shmctl = 195, - shmat = 196, - shmdt = 197, - socket = 198, - socketpair = 199, - bind = 200, - listen = 201, - accept = 202, - connect = 203, - getsockname = 204, - getpeername = 205, - sendto = 206, - recvfrom = 207, - setsockopt = 208, - getsockopt = 209, - shutdown = 210, - sendmsg = 211, - recvmsg = 212, - readahead = 213, - brk = 214, - munmap = 215, - mremap = 216, - add_key = 217, - request_key = 218, - keyctl = 219, - clone = 220, - execve = 221, - mmap = 222, - fadvise64 = 223, - swapon = 224, - swapoff = 225, - mprotect = 226, - msync = 227, - mlock = 228, - munlock = 229, - mlockall = 230, - munlockall = 231, - mincore = 232, - madvise = 233, - remap_file_pages = 234, - mbind = 235, - get_mempolicy = 236, - set_mempolicy = 237, - migrate_pages = 238, - move_pages = 239, - rt_tgsigqueueinfo = 240, - perf_event_open = 241, - accept4 = 242, - recvmmsg = 243, - arch_specific_syscall = 244, - wait4 = 260, - prlimit64 = 261, - fanotify_init = 262, - fanotify_mark = 263, - clock_adjtime = 266, - syncfs = 267, - setns = 268, - sendmmsg = 269, - process_vm_readv = 270, - process_vm_writev = 271, - kcmp = 272, - finit_module = 273, - sched_setattr = 274, - sched_getattr = 275, - renameat2 = 276, - seccomp = 277, - getrandom = 278, - memfd_create = 279, - bpf = 280, - execveat = 281, - userfaultfd = 282, - membarrier = 283, - mlock2 = 284, - copy_file_range = 285, - preadv2 = 286, - pwritev2 = 287, - pkey_mprotect = 288, - pkey_alloc = 289, - pkey_free = 290, - statx = 291, - io_pgetevents = 292, - rseq = 293, - kexec_file_load = 294, - pidfd_send_signal = 424, - io_uring_setup = 425, - io_uring_enter = 426, - io_uring_register = 427, - open_tree = 428, - move_mount = 429, - fsopen = 430, - fsconfig = 431, - fsmount = 432, - fspick = 433, - pidfd_open = 434, - clone3 = 435, - close_range = 436, - openat2 = 437, - pidfd_getfd = 438, - faccessat2 = 439, - process_madvise = 440, - epoll_pwait2 = 441, - mount_setattr = 442, - landlock_create_ruleset = 444, - landlock_add_rule = 445, - landlock_restrict_self = 446, - - _, -}; - pub const O = struct { pub const CREAT = 0o100; pub const EXCL = 0o200; diff --git a/lib/std/os/linux/i386.zig b/lib/std/os/linux/i386.zig index e683501ecb..6149997a04 100644 --- a/lib/std/os/linux/i386.zig +++ b/lib/std/os/linux/i386.zig @@ -1,6 +1,7 @@ const std = @import("../../std.zig"); const maxInt = std.math.maxInt; const linux = std.os.linux; +const SYS = linux.SYS; const socklen_t = linux.socklen_t; const iovec = std.os.iovec; const iovec_const = std.os.iovec_const; @@ -136,447 +137,6 @@ pub fn restore_rt() callconv(.Naked) void { ); } -pub const SYS = enum(usize) { - restart_syscall = 0, - exit = 1, - fork = 2, - read = 3, - write = 4, - open = 5, - close = 6, - waitpid = 7, - creat = 8, - link = 9, - unlink = 10, - execve = 11, - chdir = 12, - time = 13, - mknod = 14, - chmod = 15, - lchown = 16, - @"break" = 17, - oldstat = 18, - lseek = 19, - getpid = 20, - mount = 21, - umount = 22, - setuid = 23, - getuid = 24, - stime = 25, - ptrace = 26, - alarm = 27, - oldfstat = 28, - pause = 29, - utime = 30, - stty = 31, - gtty = 32, - access = 33, - nice = 34, - ftime = 35, - sync = 36, - kill = 37, - rename = 38, - mkdir = 39, - rmdir = 40, - dup = 41, - pipe = 42, - times = 43, - prof = 44, - brk = 45, - setgid = 46, - getgid = 47, - signal = 48, - geteuid = 49, - getegid = 50, - acct = 51, - umount2 = 52, - lock = 53, - ioctl = 54, - fcntl = 55, - mpx = 56, - setpgid = 57, - ulimit = 58, - oldolduname = 59, - umask = 60, - chroot = 61, - ustat = 62, - dup2 = 63, - getppid = 64, - getpgrp = 65, - setsid = 66, - sigaction = 67, - sgetmask = 68, - ssetmask = 69, - setreuid = 70, - setregid = 71, - sigsuspend = 72, - sigpending = 73, - sethostname = 74, - setrlimit = 75, - getrlimit = 76, - getrusage = 77, - gettimeofday = 78, - settimeofday = 79, - getgroups = 80, - setgroups = 81, - select = 82, - symlink = 83, - oldlstat = 84, - readlink = 85, - uselib = 86, - swapon = 87, - reboot = 88, - readdir = 89, - mmap = 90, - munmap = 91, - truncate = 92, - ftruncate = 93, - fchmod = 94, - fchown = 95, - getpriority = 96, - setpriority = 97, - profil = 98, - statfs = 99, - fstatfs = 100, - ioperm = 101, - socketcall = 102, - syslog = 103, - setitimer = 104, - getitimer = 105, - stat = 106, - lstat = 107, - fstat = 108, - olduname = 109, - iopl = 110, - vhangup = 111, - idle = 112, - vm86old = 113, - wait4 = 114, - swapoff = 115, - sysinfo = 116, - ipc = 117, - fsync = 118, - sigreturn = 119, - clone = 120, - setdomainname = 121, - uname = 122, - modify_ldt = 123, - adjtimex = 124, - mprotect = 125, - sigprocmask = 126, - create_module = 127, - init_module = 128, - delete_module = 129, - get_kernel_syms = 130, - quotactl = 131, - getpgid = 132, - fchdir = 133, - bdflush = 134, - sysfs = 135, - personality = 136, - afs_syscall = 137, - setfsuid = 138, - setfsgid = 139, - _llseek = 140, - getdents = 141, - _newselect = 142, - flock = 143, - msync = 144, - readv = 145, - writev = 146, - getsid = 147, - fdatasync = 148, - _sysctl = 149, - mlock = 150, - munlock = 151, - mlockall = 152, - munlockall = 153, - sched_setparam = 154, - sched_getparam = 155, - sched_setscheduler = 156, - sched_getscheduler = 157, - sched_yield = 158, - sched_get_priority_max = 159, - sched_get_priority_min = 160, - sched_rr_get_interval = 161, - nanosleep = 162, - mremap = 163, - setresuid = 164, - getresuid = 165, - vm86 = 166, - query_module = 167, - poll = 168, - nfsservctl = 169, - setresgid = 170, - getresgid = 171, - prctl = 172, - rt_sigreturn = 173, - rt_sigaction = 174, - rt_sigprocmask = 175, - rt_sigpending = 176, - rt_sigtimedwait = 177, - rt_sigqueueinfo = 178, - rt_sigsuspend = 179, - pread64 = 180, - pwrite64 = 181, - chown = 182, - getcwd = 183, - capget = 184, - capset = 185, - sigaltstack = 186, - sendfile = 187, - getpmsg = 188, - putpmsg = 189, - vfork = 190, - ugetrlimit = 191, - mmap2 = 192, - truncate64 = 193, - ftruncate64 = 194, - stat64 = 195, - lstat64 = 196, - fstat64 = 197, - lchown32 = 198, - getuid32 = 199, - getgid32 = 200, - geteuid32 = 201, - getegid32 = 202, - setreuid32 = 203, - setregid32 = 204, - getgroups32 = 205, - setgroups32 = 206, - fchown32 = 207, - setresuid32 = 208, - getresuid32 = 209, - setresgid32 = 210, - getresgid32 = 211, - chown32 = 212, - setuid32 = 213, - setgid32 = 214, - setfsuid32 = 215, - setfsgid32 = 216, - pivot_root = 217, - mincore = 218, - madvise = 219, - getdents64 = 220, - fcntl64 = 221, - gettid = 224, - readahead = 225, - setxattr = 226, - lsetxattr = 227, - fsetxattr = 228, - getxattr = 229, - lgetxattr = 230, - fgetxattr = 231, - listxattr = 232, - llistxattr = 233, - flistxattr = 234, - removexattr = 235, - lremovexattr = 236, - fremovexattr = 237, - tkill = 238, - sendfile64 = 239, - futex = 240, - sched_setaffinity = 241, - sched_getaffinity = 242, - set_thread_area = 243, - get_thread_area = 244, - io_setup = 245, - io_destroy = 246, - io_getevents = 247, - io_submit = 248, - io_cancel = 249, - fadvise64 = 250, - exit_group = 252, - lookup_dcookie = 253, - epoll_create = 254, - epoll_ctl = 255, - epoll_wait = 256, - remap_file_pages = 257, - set_tid_address = 258, - timer_create = 259, - timer_settime, // SYS_timer_create + 1 - timer_gettime, // SYS_timer_create + 2 - timer_getoverrun, // SYS_timer_create + 3 - timer_delete, // SYS_timer_create + 4 - clock_settime, // SYS_timer_create + 5 - clock_gettime, // SYS_timer_create + 6 - clock_getres, // SYS_timer_create + 7 - clock_nanosleep, // SYS_timer_create + 8 - statfs64 = 268, - fstatfs64 = 269, - tgkill = 270, - utimes = 271, - fadvise64_64 = 272, - vserver = 273, - mbind = 274, - get_mempolicy = 275, - set_mempolicy = 276, - mq_open = 277, - mq_unlink, // SYS_mq_open + 1 - mq_timedsend, // SYS_mq_open + 2 - mq_timedreceive, // SYS_mq_open + 3 - mq_notify, // SYS_mq_open + 4 - mq_getsetattr, // SYS_mq_open + 5 - kexec_load = 283, - waitid = 284, - add_key = 286, - request_key = 287, - keyctl = 288, - ioprio_set = 289, - ioprio_get = 290, - inotify_init = 291, - inotify_add_watch = 292, - inotify_rm_watch = 293, - migrate_pages = 294, - openat = 295, - mkdirat = 296, - mknodat = 297, - fchownat = 298, - futimesat = 299, - fstatat64 = 300, - unlinkat = 301, - renameat = 302, - linkat = 303, - symlinkat = 304, - readlinkat = 305, - fchmodat = 306, - faccessat = 307, - pselect6 = 308, - ppoll = 309, - unshare = 310, - set_robust_list = 311, - get_robust_list = 312, - splice = 313, - sync_file_range = 314, - tee = 315, - vmsplice = 316, - move_pages = 317, - getcpu = 318, - epoll_pwait = 319, - utimensat = 320, - signalfd = 321, - timerfd_create = 322, - eventfd = 323, - fallocate = 324, - timerfd_settime = 325, - timerfd_gettime = 326, - signalfd4 = 327, - eventfd2 = 328, - epoll_create1 = 329, - dup3 = 330, - pipe2 = 331, - inotify_init1 = 332, - preadv = 333, - pwritev = 334, - rt_tgsigqueueinfo = 335, - perf_event_open = 336, - recvmmsg = 337, - fanotify_init = 338, - fanotify_mark = 339, - prlimit64 = 340, - name_to_handle_at = 341, - open_by_handle_at = 342, - clock_adjtime = 343, - syncfs = 344, - sendmmsg = 345, - setns = 346, - process_vm_readv = 347, - process_vm_writev = 348, - kcmp = 349, - finit_module = 350, - sched_setattr = 351, - sched_getattr = 352, - renameat2 = 353, - seccomp = 354, - getrandom = 355, - memfd_create = 356, - bpf = 357, - execveat = 358, - socket = 359, - socketpair = 360, - bind = 361, - connect = 362, - listen = 363, - accept4 = 364, - getsockopt = 365, - setsockopt = 366, - getsockname = 367, - getpeername = 368, - sendto = 369, - sendmsg = 370, - recvfrom = 371, - recvmsg = 372, - shutdown = 373, - userfaultfd = 374, - membarrier = 375, - mlock2 = 376, - copy_file_range = 377, - preadv2 = 378, - pwritev2 = 379, - pkey_mprotect = 380, - pkey_alloc = 381, - pkey_free = 382, - statx = 383, - arch_prctl = 384, - io_pgetevents = 385, - rseq = 386, - semget = 393, - semctl = 394, - shmget = 395, - shmctl = 396, - shmat = 397, - shmdt = 398, - msgget = 399, - msgsnd = 400, - msgrcv = 401, - msgctl = 402, - clock_gettime64 = 403, - clock_settime64 = 404, - clock_adjtime64 = 405, - clock_getres_time64 = 406, - clock_nanosleep_time64 = 407, - timer_gettime64 = 408, - timer_settime64 = 409, - timerfd_gettime64 = 410, - timerfd_settime64 = 411, - utimensat_time64 = 412, - pselect6_time64 = 413, - ppoll_time64 = 414, - io_pgetevents_time64 = 416, - recvmmsg_time64 = 417, - mq_timedsend_time64 = 418, - mq_timedreceive_time64 = 419, - semtimedop_time64 = 420, - rt_sigtimedwait_time64 = 421, - futex_time64 = 422, - sched_rr_get_interval_time64 = 423, - pidfd_send_signal = 424, - io_uring_setup = 425, - io_uring_enter = 426, - io_uring_register = 427, - open_tree = 428, - move_mount = 429, - fsopen = 430, - fsconfig = 431, - fsmount = 432, - fspick = 433, - pidfd_open = 434, - clone3 = 435, - close_range = 436, - openat2 = 437, - pidfd_getfd = 438, - faccessat2 = 439, - process_madvise = 440, - epoll_pwait2 = 441, - mount_setattr = 442, - landlock_create_ruleset = 444, - landlock_add_rule = 445, - landlock_restrict_self = 446, - memfd_secret = 447, - - _, -}; - pub const O = struct { pub const CREAT = 0o100; pub const EXCL = 0o200; diff --git a/lib/std/os/linux/mips.zig b/lib/std/os/linux/mips.zig index f9e760babe..95b73396b0 100644 --- a/lib/std/os/linux/mips.zig +++ b/lib/std/os/linux/mips.zig @@ -1,6 +1,7 @@ const std = @import("../../std.zig"); const maxInt = std.math.maxInt; const linux = std.os.linux; +const SYS = linux.SYS; const socklen_t = linux.socklen_t; const iovec = std.os.iovec; const iovec_const = std.os.iovec_const; @@ -208,433 +209,6 @@ pub fn restore_rt() callconv(.Naked) void { ); } -pub const SYS = enum(usize) { - pub const Linux = 4000; - - syscall = Linux + 0, - exit = Linux + 1, - fork = Linux + 2, - read = Linux + 3, - write = Linux + 4, - open = Linux + 5, - close = Linux + 6, - waitpid = Linux + 7, - creat = Linux + 8, - link = Linux + 9, - unlink = Linux + 10, - execve = Linux + 11, - chdir = Linux + 12, - time = Linux + 13, - mknod = Linux + 14, - chmod = Linux + 15, - lchown = Linux + 16, - @"break" = Linux + 17, - unused18 = Linux + 18, - lseek = Linux + 19, - getpid = Linux + 20, - mount = Linux + 21, - umount = Linux + 22, - setuid = Linux + 23, - getuid = Linux + 24, - stime = Linux + 25, - ptrace = Linux + 26, - alarm = Linux + 27, - unused28 = Linux + 28, - pause = Linux + 29, - utime = Linux + 30, - stty = Linux + 31, - gtty = Linux + 32, - access = Linux + 33, - nice = Linux + 34, - ftime = Linux + 35, - sync = Linux + 36, - kill = Linux + 37, - rename = Linux + 38, - mkdir = Linux + 39, - rmdir = Linux + 40, - dup = Linux + 41, - pipe = Linux + 42, - times = Linux + 43, - prof = Linux + 44, - brk = Linux + 45, - setgid = Linux + 46, - getgid = Linux + 47, - signal = Linux + 48, - geteuid = Linux + 49, - getegid = Linux + 50, - acct = Linux + 51, - umount2 = Linux + 52, - lock = Linux + 53, - ioctl = Linux + 54, - fcntl = Linux + 55, - mpx = Linux + 56, - setpgid = Linux + 57, - ulimit = Linux + 58, - unused59 = Linux + 59, - umask = Linux + 60, - chroot = Linux + 61, - ustat = Linux + 62, - dup2 = Linux + 63, - getppid = Linux + 64, - getpgrp = Linux + 65, - setsid = Linux + 66, - sigaction = Linux + 67, - sgetmask = Linux + 68, - ssetmask = Linux + 69, - setreuid = Linux + 70, - setregid = Linux + 71, - sigsuspend = Linux + 72, - sigpending = Linux + 73, - sethostname = Linux + 74, - setrlimit = Linux + 75, - getrlimit = Linux + 76, - getrusage = Linux + 77, - gettimeofday = Linux + 78, - settimeofday = Linux + 79, - getgroups = Linux + 80, - setgroups = Linux + 81, - reserved82 = Linux + 82, - symlink = Linux + 83, - unused84 = Linux + 84, - readlink = Linux + 85, - uselib = Linux + 86, - swapon = Linux + 87, - reboot = Linux + 88, - readdir = Linux + 89, - mmap = Linux + 90, - munmap = Linux + 91, - truncate = Linux + 92, - ftruncate = Linux + 93, - fchmod = Linux + 94, - fchown = Linux + 95, - getpriority = Linux + 96, - setpriority = Linux + 97, - profil = Linux + 98, - statfs = Linux + 99, - fstatfs = Linux + 100, - ioperm = Linux + 101, - socketcall = Linux + 102, - syslog = Linux + 103, - setitimer = Linux + 104, - getitimer = Linux + 105, - stat = Linux + 106, - lstat = Linux + 107, - fstat = Linux + 108, - unused109 = Linux + 109, - iopl = Linux + 110, - vhangup = Linux + 111, - idle = Linux + 112, - vm86 = Linux + 113, - wait4 = Linux + 114, - swapoff = Linux + 115, - sysinfo = Linux + 116, - ipc = Linux + 117, - fsync = Linux + 118, - sigreturn = Linux + 119, - clone = Linux + 120, - setdomainname = Linux + 121, - uname = Linux + 122, - modify_ldt = Linux + 123, - adjtimex = Linux + 124, - mprotect = Linux + 125, - sigprocmask = Linux + 126, - create_module = Linux + 127, - init_module = Linux + 128, - delete_module = Linux + 129, - get_kernel_syms = Linux + 130, - quotactl = Linux + 131, - getpgid = Linux + 132, - fchdir = Linux + 133, - bdflush = Linux + 134, - sysfs = Linux + 135, - personality = Linux + 136, - afs_syscall = Linux + 137, - setfsuid = Linux + 138, - setfsgid = Linux + 139, - _llseek = Linux + 140, - getdents = Linux + 141, - _newselect = Linux + 142, - flock = Linux + 143, - msync = Linux + 144, - readv = Linux + 145, - writev = Linux + 146, - cacheflush = Linux + 147, - cachectl = Linux + 148, - sysmips = Linux + 149, - unused150 = Linux + 150, - getsid = Linux + 151, - fdatasync = Linux + 152, - _sysctl = Linux + 153, - mlock = Linux + 154, - munlock = Linux + 155, - mlockall = Linux + 156, - munlockall = Linux + 157, - sched_setparam = Linux + 158, - sched_getparam = Linux + 159, - sched_setscheduler = Linux + 160, - sched_getscheduler = Linux + 161, - sched_yield = Linux + 162, - sched_get_priority_max = Linux + 163, - sched_get_priority_min = Linux + 164, - sched_rr_get_interval = Linux + 165, - nanosleep = Linux + 166, - mremap = Linux + 167, - accept = Linux + 168, - bind = Linux + 169, - connect = Linux + 170, - getpeername = Linux + 171, - getsockname = Linux + 172, - getsockopt = Linux + 173, - listen = Linux + 174, - recv = Linux + 175, - recvfrom = Linux + 176, - recvmsg = Linux + 177, - send = Linux + 178, - sendmsg = Linux + 179, - sendto = Linux + 180, - setsockopt = Linux + 181, - shutdown = Linux + 182, - socket = Linux + 183, - socketpair = Linux + 184, - setresuid = Linux + 185, - getresuid = Linux + 186, - query_module = Linux + 187, - poll = Linux + 188, - nfsservctl = Linux + 189, - setresgid = Linux + 190, - getresgid = Linux + 191, - prctl = Linux + 192, - rt_sigreturn = Linux + 193, - rt_sigaction = Linux + 194, - rt_sigprocmask = Linux + 195, - rt_sigpending = Linux + 196, - rt_sigtimedwait = Linux + 197, - rt_sigqueueinfo = Linux + 198, - rt_sigsuspend = Linux + 199, - pread64 = Linux + 200, - pwrite64 = Linux + 201, - chown = Linux + 202, - getcwd = Linux + 203, - capget = Linux + 204, - capset = Linux + 205, - sigaltstack = Linux + 206, - sendfile = Linux + 207, - getpmsg = Linux + 208, - putpmsg = Linux + 209, - mmap2 = Linux + 210, - truncate64 = Linux + 211, - ftruncate64 = Linux + 212, - stat64 = Linux + 213, - lstat64 = Linux + 214, - fstat64 = Linux + 215, - pivot_root = Linux + 216, - mincore = Linux + 217, - madvise = Linux + 218, - getdents64 = Linux + 219, - fcntl64 = Linux + 220, - reserved221 = Linux + 221, - gettid = Linux + 222, - readahead = Linux + 223, - setxattr = Linux + 224, - lsetxattr = Linux + 225, - fsetxattr = Linux + 226, - getxattr = Linux + 227, - lgetxattr = Linux + 228, - fgetxattr = Linux + 229, - listxattr = Linux + 230, - llistxattr = Linux + 231, - flistxattr = Linux + 232, - removexattr = Linux + 233, - lremovexattr = Linux + 234, - fremovexattr = Linux + 235, - tkill = Linux + 236, - sendfile64 = Linux + 237, - futex = Linux + 238, - sched_setaffinity = Linux + 239, - sched_getaffinity = Linux + 240, - io_setup = Linux + 241, - io_destroy = Linux + 242, - io_getevents = Linux + 243, - io_submit = Linux + 244, - io_cancel = Linux + 245, - exit_group = Linux + 246, - lookup_dcookie = Linux + 247, - epoll_create = Linux + 248, - epoll_ctl = Linux + 249, - epoll_wait = Linux + 250, - remap_file_pages = Linux + 251, - set_tid_address = Linux + 252, - restart_syscall = Linux + 253, - fadvise64 = Linux + 254, - statfs64 = Linux + 255, - fstatfs64 = Linux + 256, - timer_create = Linux + 257, - timer_settime = Linux + 258, - timer_gettime = Linux + 259, - timer_getoverrun = Linux + 260, - timer_delete = Linux + 261, - clock_settime = Linux + 262, - clock_gettime = Linux + 263, - clock_getres = Linux + 264, - clock_nanosleep = Linux + 265, - tgkill = Linux + 266, - utimes = Linux + 267, - mbind = Linux + 268, - get_mempolicy = Linux + 269, - set_mempolicy = Linux + 270, - mq_open = Linux + 271, - mq_unlink = Linux + 272, - mq_timedsend = Linux + 273, - mq_timedreceive = Linux + 274, - mq_notify = Linux + 275, - mq_getsetattr = Linux + 276, - vserver = Linux + 277, - waitid = Linux + 278, - add_key = Linux + 280, - request_key = Linux + 281, - keyctl = Linux + 282, - set_thread_area = Linux + 283, - inotify_init = Linux + 284, - inotify_add_watch = Linux + 285, - inotify_rm_watch = Linux + 286, - migrate_pages = Linux + 287, - openat = Linux + 288, - mkdirat = Linux + 289, - mknodat = Linux + 290, - fchownat = Linux + 291, - futimesat = Linux + 292, - fstatat64 = Linux + 293, - unlinkat = Linux + 294, - renameat = Linux + 295, - linkat = Linux + 296, - symlinkat = Linux + 297, - readlinkat = Linux + 298, - fchmodat = Linux + 299, - faccessat = Linux + 300, - pselect6 = Linux + 301, - ppoll = Linux + 302, - unshare = Linux + 303, - splice = Linux + 304, - sync_file_range = Linux + 305, - tee = Linux + 306, - vmsplice = Linux + 307, - move_pages = Linux + 308, - set_robust_list = Linux + 309, - get_robust_list = Linux + 310, - kexec_load = Linux + 311, - getcpu = Linux + 312, - epoll_pwait = Linux + 313, - ioprio_set = Linux + 314, - ioprio_get = Linux + 315, - utimensat = Linux + 316, - signalfd = Linux + 317, - timerfd = Linux + 318, - eventfd = Linux + 319, - fallocate = Linux + 320, - timerfd_create = Linux + 321, - timerfd_gettime = Linux + 322, - timerfd_settime = Linux + 323, - signalfd4 = Linux + 324, - eventfd2 = Linux + 325, - epoll_create1 = Linux + 326, - dup3 = Linux + 327, - pipe2 = Linux + 328, - inotify_init1 = Linux + 329, - preadv = Linux + 330, - pwritev = Linux + 331, - rt_tgsigqueueinfo = Linux + 332, - perf_event_open = Linux + 333, - accept4 = Linux + 334, - recvmmsg = Linux + 335, - fanotify_init = Linux + 336, - fanotify_mark = Linux + 337, - prlimit64 = Linux + 338, - name_to_handle_at = Linux + 339, - open_by_handle_at = Linux + 340, - clock_adjtime = Linux + 341, - syncfs = Linux + 342, - sendmmsg = Linux + 343, - setns = Linux + 344, - process_vm_readv = Linux + 345, - process_vm_writev = Linux + 346, - kcmp = Linux + 347, - finit_module = Linux + 348, - sched_setattr = Linux + 349, - sched_getattr = Linux + 350, - renameat2 = Linux + 351, - seccomp = Linux + 352, - getrandom = Linux + 353, - memfd_create = Linux + 354, - bpf = Linux + 355, - execveat = Linux + 356, - userfaultfd = Linux + 357, - membarrier = Linux + 358, - mlock2 = Linux + 359, - copy_file_range = Linux + 360, - preadv2 = Linux + 361, - pwritev2 = Linux + 362, - pkey_mprotect = Linux + 363, - pkey_alloc = Linux + 364, - pkey_free = Linux + 365, - statx = Linux + 366, - rseq = Linux + 367, - io_pgetevents = Linux + 368, - semget = Linux + 393, - semctl = Linux + 394, - shmget = Linux + 395, - shmctl = Linux + 396, - shmat = Linux + 397, - shmdt = Linux + 398, - msgget = Linux + 399, - msgsnd = Linux + 400, - msgrcv = Linux + 401, - msgctl = Linux + 402, - clock_gettime64 = Linux + 403, - clock_settime64 = Linux + 404, - clock_adjtime64 = Linux + 405, - clock_getres_time64 = Linux + 406, - clock_nanosleep_time64 = Linux + 407, - timer_gettime64 = Linux + 408, - timer_settime64 = Linux + 409, - timerfd_gettime64 = Linux + 410, - timerfd_settime64 = Linux + 411, - utimensat_time64 = Linux + 412, - pselect6_time64 = Linux + 413, - ppoll_time64 = Linux + 414, - io_pgetevents_time64 = Linux + 416, - recvmmsg_time64 = Linux + 417, - mq_timedsend_time64 = Linux + 418, - mq_timedreceive_time64 = Linux + 419, - semtimedop_time64 = Linux + 420, - rt_sigtimedwait_time64 = Linux + 421, - futex_time64 = Linux + 422, - sched_rr_get_interval_time64 = Linux + 423, - pidfd_send_signal = Linux + 424, - io_uring_setup = Linux + 425, - io_uring_enter = Linux + 426, - io_uring_register = Linux + 427, - open_tree = Linux + 428, - move_mount = Linux + 429, - fsopen = Linux + 430, - fsconfig = Linux + 431, - fsmount = Linux + 432, - fspick = Linux + 433, - pidfd_open = Linux + 434, - clone3 = Linux + 435, - close_range = Linux + 436, - openat2 = Linux + 437, - pidfd_getfd = Linux + 438, - faccessat2 = Linux + 439, - process_madvise = Linux + 440, - epoll_pwait2 = Linux + 441, - mount_setattr = Linux + 442, - landlock_create_ruleset = Linux + 444, - landlock_add_rule = Linux + 445, - landlock_restrict_self = Linux + 446, - - _, -}; - pub const O = struct { pub const CREAT = 0o0400; pub const EXCL = 0o02000; diff --git a/lib/std/os/linux/powerpc.zig b/lib/std/os/linux/powerpc.zig index e45be74f77..bd4d172d44 100644 --- a/lib/std/os/linux/powerpc.zig +++ b/lib/std/os/linux/powerpc.zig @@ -1,6 +1,7 @@ const std = @import("../../std.zig"); const maxInt = std.math.maxInt; const linux = std.os.linux; +const SYS = linux.SYS; const socklen_t = linux.socklen_t; const iovec = std.os.iovec; const iovec_const = std.os.iovec_const; @@ -138,436 +139,6 @@ pub fn restore_rt() callconv(.Naked) void { ); } -pub const SYS = enum(usize) { - restart_syscall = 0, - exit = 1, - fork = 2, - read = 3, - write = 4, - open = 5, - close = 6, - waitpid = 7, - creat = 8, - link = 9, - unlink = 10, - execve = 11, - chdir = 12, - time = 13, - mknod = 14, - chmod = 15, - lchown = 16, - @"break" = 17, - oldstat = 18, - lseek = 19, - getpid = 20, - mount = 21, - umount = 22, - setuid = 23, - getuid = 24, - stime = 25, - ptrace = 26, - alarm = 27, - oldfstat = 28, - pause = 29, - utime = 30, - stty = 31, - gtty = 32, - access = 33, - nice = 34, - ftime = 35, - sync = 36, - kill = 37, - rename = 38, - mkdir = 39, - rmdir = 40, - dup = 41, - pipe = 42, - times = 43, - prof = 44, - brk = 45, - setgid = 46, - getgid = 47, - signal = 48, - geteuid = 49, - getegid = 50, - acct = 51, - umount2 = 52, - lock = 53, - ioctl = 54, - fcntl = 55, - mpx = 56, - setpgid = 57, - ulimit = 58, - oldolduname = 59, - umask = 60, - chroot = 61, - ustat = 62, - dup2 = 63, - getppid = 64, - getpgrp = 65, - setsid = 66, - sigaction = 67, - sgetmask = 68, - ssetmask = 69, - setreuid = 70, - setregid = 71, - sigsuspend = 72, - sigpending = 73, - sethostname = 74, - setrlimit = 75, - getrlimit = 76, - getrusage = 77, - gettimeofday = 78, - settimeofday = 79, - getgroups = 80, - setgroups = 81, - select = 82, - symlink = 83, - oldlstat = 84, - readlink = 85, - uselib = 86, - swapon = 87, - reboot = 88, - readdir = 89, - mmap = 90, - munmap = 91, - truncate = 92, - ftruncate = 93, - fchmod = 94, - fchown = 95, - getpriority = 96, - setpriority = 97, - profil = 98, - statfs = 99, - fstatfs = 100, - ioperm = 101, - socketcall = 102, - syslog = 103, - setitimer = 104, - getitimer = 105, - stat = 106, - lstat = 107, - fstat = 108, - olduname = 109, - iopl = 110, - vhangup = 111, - idle = 112, - vm86 = 113, - wait4 = 114, - swapoff = 115, - sysinfo = 116, - ipc = 117, - fsync = 118, - sigreturn = 119, - clone = 120, - setdomainname = 121, - uname = 122, - modify_ldt = 123, - adjtimex = 124, - mprotect = 125, - sigprocmask = 126, - create_module = 127, - init_module = 128, - delete_module = 129, - get_kernel_syms = 130, - quotactl = 131, - getpgid = 132, - fchdir = 133, - bdflush = 134, - sysfs = 135, - personality = 136, - afs_syscall = 137, - setfsuid = 138, - setfsgid = 139, - _llseek = 140, - getdents = 141, - _newselect = 142, - flock = 143, - msync = 144, - readv = 145, - writev = 146, - getsid = 147, - fdatasync = 148, - _sysctl = 149, - mlock = 150, - munlock = 151, - mlockall = 152, - munlockall = 153, - sched_setparam = 154, - sched_getparam = 155, - sched_setscheduler = 156, - sched_getscheduler = 157, - sched_yield = 158, - sched_get_priority_max = 159, - sched_get_priority_min = 160, - sched_rr_get_interval = 161, - nanosleep = 162, - mremap = 163, - setresuid = 164, - getresuid = 165, - query_module = 166, - poll = 167, - nfsservctl = 168, - setresgid = 169, - getresgid = 170, - prctl = 171, - rt_sigreturn = 172, - rt_sigaction = 173, - rt_sigprocmask = 174, - rt_sigpending = 175, - rt_sigtimedwait = 176, - rt_sigqueueinfo = 177, - rt_sigsuspend = 178, - pread64 = 179, - pwrite64 = 180, - chown = 181, - getcwd = 182, - capget = 183, - capset = 184, - sigaltstack = 185, - sendfile = 186, - getpmsg = 187, - putpmsg = 188, - vfork = 189, - ugetrlimit = 190, - readahead = 191, - mmap2 = 192, - truncate64 = 193, - ftruncate64 = 194, - stat64 = 195, - lstat64 = 196, - fstat64 = 197, - pciconfig_read = 198, - pciconfig_write = 199, - pciconfig_iobase = 200, - multiplexer = 201, - getdents64 = 202, - pivot_root = 203, - fcntl64 = 204, - madvise = 205, - mincore = 206, - gettid = 207, - tkill = 208, - setxattr = 209, - lsetxattr = 210, - fsetxattr = 211, - getxattr = 212, - lgetxattr = 213, - fgetxattr = 214, - listxattr = 215, - llistxattr = 216, - flistxattr = 217, - removexattr = 218, - lremovexattr = 219, - fremovexattr = 220, - futex = 221, - sched_setaffinity = 222, - sched_getaffinity = 223, - tuxcall = 225, - sendfile64 = 226, - io_setup = 227, - io_destroy = 228, - io_getevents = 229, - io_submit = 230, - io_cancel = 231, - set_tid_address = 232, - fadvise64 = 233, - exit_group = 234, - lookup_dcookie = 235, - epoll_create = 236, - epoll_ctl = 237, - epoll_wait = 238, - remap_file_pages = 239, - timer_create = 240, - timer_settime = 241, - timer_gettime = 242, - timer_getoverrun = 243, - timer_delete = 244, - clock_settime = 245, - clock_gettime = 246, - clock_getres = 247, - clock_nanosleep = 248, - swapcontext = 249, - tgkill = 250, - utimes = 251, - statfs64 = 252, - fstatfs64 = 253, - fadvise64_64 = 254, - rtas = 255, - sys_debug_setcontext = 256, - migrate_pages = 258, - mbind = 259, - get_mempolicy = 260, - set_mempolicy = 261, - mq_open = 262, - mq_unlink = 263, - mq_timedsend = 264, - mq_timedreceive = 265, - mq_notify = 266, - mq_getsetattr = 267, - kexec_load = 268, - add_key = 269, - request_key = 270, - keyctl = 271, - waitid = 272, - ioprio_set = 273, - ioprio_get = 274, - inotify_init = 275, - inotify_add_watch = 276, - inotify_rm_watch = 277, - spu_run = 278, - spu_create = 279, - pselect6 = 280, - ppoll = 281, - unshare = 282, - splice = 283, - tee = 284, - vmsplice = 285, - openat = 286, - mkdirat = 287, - mknodat = 288, - fchownat = 289, - futimesat = 290, - fstatat64 = 291, - unlinkat = 292, - renameat = 293, - linkat = 294, - symlinkat = 295, - readlinkat = 296, - fchmodat = 297, - faccessat = 298, - get_robust_list = 299, - set_robust_list = 300, - move_pages = 301, - getcpu = 302, - epoll_pwait = 303, - utimensat = 304, - signalfd = 305, - timerfd_create = 306, - eventfd = 307, - sync_file_range = 308, - fallocate = 309, - subpage_prot = 310, - timerfd_settime = 311, - timerfd_gettime = 312, - signalfd4 = 313, - eventfd2 = 314, - epoll_create1 = 315, - dup3 = 316, - pipe2 = 317, - inotify_init1 = 318, - perf_event_open = 319, - preadv = 320, - pwritev = 321, - rt_tgsigqueueinfo = 322, - fanotify_init = 323, - fanotify_mark = 324, - prlimit64 = 325, - socket = 326, - bind = 327, - connect = 328, - listen = 329, - accept = 330, - getsockname = 331, - getpeername = 332, - socketpair = 333, - send = 334, - sendto = 335, - recv = 336, - recvfrom = 337, - shutdown = 338, - setsockopt = 339, - getsockopt = 340, - sendmsg = 341, - recvmsg = 342, - recvmmsg = 343, - accept4 = 344, - name_to_handle_at = 345, - open_by_handle_at = 346, - clock_adjtime = 347, - syncfs = 348, - sendmmsg = 349, - setns = 350, - process_vm_readv = 351, - process_vm_writev = 352, - finit_module = 353, - kcmp = 354, - sched_setattr = 355, - sched_getattr = 356, - renameat2 = 357, - seccomp = 358, - getrandom = 359, - memfd_create = 360, - bpf = 361, - execveat = 362, - switch_endian = 363, - userfaultfd = 364, - membarrier = 365, - mlock2 = 378, - copy_file_range = 379, - preadv2 = 380, - pwritev2 = 381, - kexec_file_load = 382, - statx = 383, - pkey_alloc = 384, - pkey_free = 385, - pkey_mprotect = 386, - rseq = 387, - io_pgetevents = 388, - semget = 393, - semctl = 394, - shmget = 395, - shmctl = 396, - shmat = 397, - shmdt = 398, - msgget = 399, - msgsnd = 400, - msgrcv = 401, - msgctl = 402, - clock_gettime64 = 403, - clock_settime64 = 404, - clock_adjtime64 = 405, - clock_getres_time64 = 406, - clock_nanosleep_time64 = 407, - timer_gettime64 = 408, - timer_settime64 = 409, - timerfd_gettime64 = 410, - timerfd_settime64 = 411, - utimensat_time64 = 412, - pselect6_time64 = 413, - ppoll_time64 = 414, - io_pgetevents_time64 = 416, - recvmmsg_time64 = 417, - mq_timedsend_time64 = 418, - mq_timedreceive_time64 = 419, - semtimedop_time64 = 420, - rt_sigtimedwait_time64 = 421, - futex_time64 = 422, - sched_rr_get_interval_time64 = 423, - pidfd_send_signal = 424, - io_uring_setup = 425, - io_uring_enter = 426, - io_uring_register = 427, - open_tree = 428, - move_mount = 429, - fsopen = 430, - fsconfig = 431, - fsmount = 432, - fspick = 433, - pidfd_open = 434, - clone3 = 435, - close_range = 436, - openat2 = 437, - pidfd_getfd = 438, - faccessat2 = 439, - process_madvise = 440, - epoll_pwait2 = 441, - mount_setattr = 442, - landlock_create_ruleset = 444, - landlock_add_rule = 445, - landlock_restrict_self = 446, -}; - pub const O = struct { pub const CREAT = 0o100; pub const EXCL = 0o200; diff --git a/lib/std/os/linux/powerpc64.zig b/lib/std/os/linux/powerpc64.zig index 19d5f04bd3..4f838f6086 100644 --- a/lib/std/os/linux/powerpc64.zig +++ b/lib/std/os/linux/powerpc64.zig @@ -1,6 +1,7 @@ const std = @import("../../std.zig"); const maxInt = std.math.maxInt; const linux = std.os.linux; +const SYS = linux.SYS; const socklen_t = linux.socklen_t; const iovec = std.os.iovec; const iovec_const = std.os.iovec_const; @@ -138,410 +139,6 @@ pub fn restore_rt() callconv(.Naked) void { ); } -pub const SYS = enum(usize) { - restart_syscall = 0, - exit = 1, - fork = 2, - read = 3, - write = 4, - open = 5, - close = 6, - waitpid = 7, - creat = 8, - link = 9, - unlink = 10, - execve = 11, - chdir = 12, - time = 13, - mknod = 14, - chmod = 15, - lchown = 16, - @"break" = 17, - oldstat = 18, - lseek = 19, - getpid = 20, - mount = 21, - umount = 22, - setuid = 23, - getuid = 24, - stime = 25, - ptrace = 26, - alarm = 27, - oldfstat = 28, - pause = 29, - utime = 30, - stty = 31, - gtty = 32, - access = 33, - nice = 34, - ftime = 35, - sync = 36, - kill = 37, - rename = 38, - mkdir = 39, - rmdir = 40, - dup = 41, - pipe = 42, - times = 43, - prof = 44, - brk = 45, - setgid = 46, - getgid = 47, - signal = 48, - geteuid = 49, - getegid = 50, - acct = 51, - umount2 = 52, - lock = 53, - ioctl = 54, - fcntl = 55, - mpx = 56, - setpgid = 57, - ulimit = 58, - oldolduname = 59, - umask = 60, - chroot = 61, - ustat = 62, - dup2 = 63, - getppid = 64, - getpgrp = 65, - setsid = 66, - sigaction = 67, - sgetmask = 68, - ssetmask = 69, - setreuid = 70, - setregid = 71, - sigsuspend = 72, - sigpending = 73, - sethostname = 74, - setrlimit = 75, - getrlimit = 76, - getrusage = 77, - gettimeofday = 78, - settimeofday = 79, - getgroups = 80, - setgroups = 81, - select = 82, - symlink = 83, - oldlstat = 84, - readlink = 85, - uselib = 86, - swapon = 87, - reboot = 88, - readdir = 89, - mmap = 90, - munmap = 91, - truncate = 92, - ftruncate = 93, - fchmod = 94, - fchown = 95, - getpriority = 96, - setpriority = 97, - profil = 98, - statfs = 99, - fstatfs = 100, - ioperm = 101, - socketcall = 102, - syslog = 103, - setitimer = 104, - getitimer = 105, - stat = 106, - lstat = 107, - fstat = 108, - olduname = 109, - iopl = 110, - vhangup = 111, - idle = 112, - vm86 = 113, - wait4 = 114, - swapoff = 115, - sysinfo = 116, - ipc = 117, - fsync = 118, - sigreturn = 119, - clone = 120, - setdomainname = 121, - uname = 122, - modify_ldt = 123, - adjtimex = 124, - mprotect = 125, - sigprocmask = 126, - create_module = 127, - init_module = 128, - delete_module = 129, - get_kernel_syms = 130, - quotactl = 131, - getpgid = 132, - fchdir = 133, - bdflush = 134, - sysfs = 135, - personality = 136, - afs_syscall = 137, - setfsuid = 138, - setfsgid = 139, - _llseek = 140, - getdents = 141, - _newselect = 142, - flock = 143, - msync = 144, - readv = 145, - writev = 146, - getsid = 147, - fdatasync = 148, - _sysctl = 149, - mlock = 150, - munlock = 151, - mlockall = 152, - munlockall = 153, - sched_setparam = 154, - sched_getparam = 155, - sched_setscheduler = 156, - sched_getscheduler = 157, - sched_yield = 158, - sched_get_priority_max = 159, - sched_get_priority_min = 160, - sched_rr_get_interval = 161, - nanosleep = 162, - mremap = 163, - setresuid = 164, - getresuid = 165, - query_module = 166, - poll = 167, - nfsservctl = 168, - setresgid = 169, - getresgid = 170, - prctl = 171, - rt_sigreturn = 172, - rt_sigaction = 173, - rt_sigprocmask = 174, - rt_sigpending = 175, - rt_sigtimedwait = 176, - rt_sigqueueinfo = 177, - rt_sigsuspend = 178, - pread64 = 179, - pwrite64 = 180, - chown = 181, - getcwd = 182, - capget = 183, - capset = 184, - sigaltstack = 185, - sendfile = 186, - getpmsg = 187, - putpmsg = 188, - vfork = 189, - ugetrlimit = 190, - readahead = 191, - pciconfig_read = 198, - pciconfig_write = 199, - pciconfig_iobase = 200, - multiplexer = 201, - getdents64 = 202, - pivot_root = 203, - madvise = 205, - mincore = 206, - gettid = 207, - tkill = 208, - setxattr = 209, - lsetxattr = 210, - fsetxattr = 211, - getxattr = 212, - lgetxattr = 213, - fgetxattr = 214, - listxattr = 215, - llistxattr = 216, - flistxattr = 217, - removexattr = 218, - lremovexattr = 219, - fremovexattr = 220, - futex = 221, - sched_setaffinity = 222, - sched_getaffinity = 223, - tuxcall = 225, - io_setup = 227, - io_destroy = 228, - io_getevents = 229, - io_submit = 230, - io_cancel = 231, - set_tid_address = 232, - fadvise64 = 233, - exit_group = 234, - lookup_dcookie = 235, - epoll_create = 236, - epoll_ctl = 237, - epoll_wait = 238, - remap_file_pages = 239, - timer_create = 240, - timer_settime = 241, - timer_gettime = 242, - timer_getoverrun = 243, - timer_delete = 244, - clock_settime = 245, - clock_gettime = 246, - clock_getres = 247, - clock_nanosleep = 248, - swapcontext = 249, - tgkill = 250, - utimes = 251, - statfs64 = 252, - fstatfs64 = 253, - rtas = 255, - sys_debug_setcontext = 256, - migrate_pages = 258, - mbind = 259, - get_mempolicy = 260, - set_mempolicy = 261, - mq_open = 262, - mq_unlink = 263, - mq_timedsend = 264, - mq_timedreceive = 265, - mq_notify = 266, - mq_getsetattr = 267, - kexec_load = 268, - add_key = 269, - request_key = 270, - keyctl = 271, - waitid = 272, - ioprio_set = 273, - ioprio_get = 274, - inotify_init = 275, - inotify_add_watch = 276, - inotify_rm_watch = 277, - spu_run = 278, - spu_create = 279, - pselect6 = 280, - ppoll = 281, - unshare = 282, - splice = 283, - tee = 284, - vmsplice = 285, - openat = 286, - mkdirat = 287, - mknodat = 288, - fchownat = 289, - futimesat = 290, - fstatat = 291, - unlinkat = 292, - renameat = 293, - linkat = 294, - symlinkat = 295, - readlinkat = 296, - fchmodat = 297, - faccessat = 298, - get_robust_list = 299, - set_robust_list = 300, - move_pages = 301, - getcpu = 302, - epoll_pwait = 303, - utimensat = 304, - signalfd = 305, - timerfd_create = 306, - eventfd = 307, - sync_file_range = 308, - fallocate = 309, - subpage_prot = 310, - timerfd_settime = 311, - timerfd_gettime = 312, - signalfd4 = 313, - eventfd2 = 314, - epoll_create1 = 315, - dup3 = 316, - pipe2 = 317, - inotify_init1 = 318, - perf_event_open = 319, - preadv = 320, - pwritev = 321, - rt_tgsigqueueinfo = 322, - fanotify_init = 323, - fanotify_mark = 324, - prlimit64 = 325, - socket = 326, - bind = 327, - connect = 328, - listen = 329, - accept = 330, - getsockname = 331, - getpeername = 332, - socketpair = 333, - send = 334, - sendto = 335, - recv = 336, - recvfrom = 337, - shutdown = 338, - setsockopt = 339, - getsockopt = 340, - sendmsg = 341, - recvmsg = 342, - recvmmsg = 343, - accept4 = 344, - name_to_handle_at = 345, - open_by_handle_at = 346, - clock_adjtime = 347, - syncfs = 348, - sendmmsg = 349, - setns = 350, - process_vm_readv = 351, - process_vm_writev = 352, - finit_module = 353, - kcmp = 354, - sched_setattr = 355, - sched_getattr = 356, - renameat2 = 357, - seccomp = 358, - getrandom = 359, - memfd_create = 360, - bpf = 361, - execveat = 362, - switch_endian = 363, - userfaultfd = 364, - membarrier = 365, - mlock2 = 378, - copy_file_range = 379, - preadv2 = 380, - pwritev2 = 381, - kexec_file_load = 382, - statx = 383, - pkey_alloc = 384, - pkey_free = 385, - pkey_mprotect = 386, - rseq = 387, - io_pgetevents = 388, - semtimedop = 392, - semget = 393, - semctl = 394, - shmget = 395, - shmctl = 396, - shmat = 397, - shmdt = 398, - msgget = 399, - msgsnd = 400, - msgrcv = 401, - msgctl = 402, - pidfd_send_signal = 424, - io_uring_setup = 425, - io_uring_enter = 426, - io_uring_register = 427, - open_tree = 428, - move_mount = 429, - fsopen = 430, - fsconfig = 431, - fsmount = 432, - fspick = 433, - pidfd_open = 434, - clone3 = 435, - close_range = 436, - openat2 = 437, - pidfd_getfd = 438, - faccessat2 = 439, - process_madvise = 440, - epoll_pwait2 = 441, - mount_setattr = 442, - landlock_create_ruleset = 444, - landlock_add_rule = 445, - landlock_restrict_self = 446, - - _, -}; - pub const O = struct { pub const CREAT = 0o100; pub const EXCL = 0o200; diff --git a/lib/std/os/linux/riscv64.zig b/lib/std/os/linux/riscv64.zig index a2a50ddef4..30b725a85d 100644 --- a/lib/std/os/linux/riscv64.zig +++ b/lib/std/os/linux/riscv64.zig @@ -2,6 +2,7 @@ const std = @import("../../std.zig"); const iovec = std.os.iovec; const iovec_const = std.os.iovec_const; const linux = std.os.linux; +const SYS = linux.SYS; const uid_t = std.os.linux.uid_t; const gid_t = std.os.linux.gid_t; const pid_t = std.os.linux.pid_t; @@ -106,316 +107,6 @@ pub fn restore_rt() callconv(.Naked) void { ); } -pub const SYS = enum(usize) { - pub const arch_specific_syscall = 244; - - io_setup = 0, - io_destroy = 1, - io_submit = 2, - io_cancel = 3, - io_getevents = 4, - setxattr = 5, - lsetxattr = 6, - fsetxattr = 7, - getxattr = 8, - lgetxattr = 9, - fgetxattr = 10, - listxattr = 11, - llistxattr = 12, - flistxattr = 13, - removexattr = 14, - lremovexattr = 15, - fremovexattr = 16, - getcwd = 17, - lookup_dcookie = 18, - eventfd2 = 19, - epoll_create1 = 20, - epoll_ctl = 21, - epoll_pwait = 22, - dup = 23, - dup3 = 24, - fcntl = 25, - inotify_init1 = 26, - inotify_add_watch = 27, - inotify_rm_watch = 28, - ioctl = 29, - ioprio_set = 30, - ioprio_get = 31, - flock = 32, - mknodat = 33, - mkdirat = 34, - unlinkat = 35, - symlinkat = 36, - linkat = 37, - umount2 = 39, - mount = 40, - pivot_root = 41, - nfsservctl = 42, - statfs = 43, - fstatfs = 44, - truncate = 45, - ftruncate = 46, - fallocate = 47, - faccessat = 48, - chdir = 49, - fchdir = 50, - chroot = 51, - fchmod = 52, - fchmodat = 53, - fchownat = 54, - fchown = 55, - openat = 56, - close = 57, - vhangup = 58, - pipe2 = 59, - quotactl = 60, - getdents64 = 61, - lseek = 62, - read = 63, - write = 64, - readv = 65, - writev = 66, - pread64 = 67, - pwrite64 = 68, - preadv = 69, - pwritev = 70, - sendfile = 71, - pselect6 = 72, - ppoll = 73, - signalfd4 = 74, - vmsplice = 75, - splice = 76, - tee = 77, - readlinkat = 78, - fstatat = 79, - fstat = 80, - sync = 81, - fsync = 82, - fdatasync = 83, - sync_file_range = 84, - timerfd_create = 85, - timerfd_settime = 86, - timerfd_gettime = 87, - utimensat = 88, - acct = 89, - capget = 90, - capset = 91, - personality = 92, - exit = 93, - exit_group = 94, - waitid = 95, - set_tid_address = 96, - unshare = 97, - futex = 98, - set_robust_list = 99, - get_robust_list = 100, - nanosleep = 101, - getitimer = 102, - setitimer = 103, - kexec_load = 104, - init_module = 105, - delete_module = 106, - timer_create = 107, - timer_gettime = 108, - timer_getoverrun = 109, - timer_settime = 110, - timer_delete = 111, - clock_settime = 112, - clock_gettime = 113, - clock_getres = 114, - clock_nanosleep = 115, - syslog = 116, - ptrace = 117, - sched_setparam = 118, - sched_setscheduler = 119, - sched_getscheduler = 120, - sched_getparam = 121, - sched_setaffinity = 122, - sched_getaffinity = 123, - sched_yield = 124, - sched_get_priority_max = 125, - sched_get_priority_min = 126, - sched_rr_get_interval = 127, - restart_syscall = 128, - kill = 129, - tkill = 130, - tgkill = 131, - sigaltstack = 132, - rt_sigsuspend = 133, - rt_sigaction = 134, - rt_sigprocmask = 135, - rt_sigpending = 136, - rt_sigtimedwait = 137, - rt_sigqueueinfo = 138, - rt_sigreturn = 139, - setpriority = 140, - getpriority = 141, - reboot = 142, - setregid = 143, - setgid = 144, - setreuid = 145, - setuid = 146, - setresuid = 147, - getresuid = 148, - setresgid = 149, - getresgid = 150, - setfsuid = 151, - setfsgid = 152, - times = 153, - setpgid = 154, - getpgid = 155, - getsid = 156, - setsid = 157, - getgroups = 158, - setgroups = 159, - uname = 160, - sethostname = 161, - setdomainname = 162, - getrlimit = 163, - setrlimit = 164, - getrusage = 165, - umask = 166, - prctl = 167, - getcpu = 168, - gettimeofday = 169, - settimeofday = 170, - adjtimex = 171, - getpid = 172, - getppid = 173, - getuid = 174, - geteuid = 175, - getgid = 176, - getegid = 177, - gettid = 178, - sysinfo = 179, - mq_open = 180, - mq_unlink = 181, - mq_timedsend = 182, - mq_timedreceive = 183, - mq_notify = 184, - mq_getsetattr = 185, - msgget = 186, - msgctl = 187, - msgrcv = 188, - msgsnd = 189, - semget = 190, - semctl = 191, - semtimedop = 192, - semop = 193, - shmget = 194, - shmctl = 195, - shmat = 196, - shmdt = 197, - socket = 198, - socketpair = 199, - bind = 200, - listen = 201, - accept = 202, - connect = 203, - getsockname = 204, - getpeername = 205, - sendto = 206, - recvfrom = 207, - setsockopt = 208, - getsockopt = 209, - shutdown = 210, - sendmsg = 211, - recvmsg = 212, - readahead = 213, - brk = 214, - munmap = 215, - mremap = 216, - add_key = 217, - request_key = 218, - keyctl = 219, - clone = 220, - execve = 221, - mmap = 222, - fadvise64 = 223, - swapon = 224, - swapoff = 225, - mprotect = 226, - msync = 227, - mlock = 228, - munlock = 229, - mlockall = 230, - munlockall = 231, - mincore = 232, - madvise = 233, - remap_file_pages = 234, - mbind = 235, - get_mempolicy = 236, - set_mempolicy = 237, - migrate_pages = 238, - move_pages = 239, - rt_tgsigqueueinfo = 240, - perf_event_open = 241, - accept4 = 242, - recvmmsg = 243, - - riscv_flush_icache = arch_specific_syscall + 15, - - wait4 = 260, - prlimit64 = 261, - fanotify_init = 262, - fanotify_mark = 263, - name_to_handle_at = 264, - open_by_handle_at = 265, - clock_adjtime = 266, - syncfs = 267, - setns = 268, - sendmmsg = 269, - process_vm_readv = 270, - process_vm_writev = 271, - kcmp = 272, - finit_module = 273, - sched_setattr = 274, - sched_getattr = 275, - renameat2 = 276, - seccomp = 277, - getrandom = 278, - memfd_create = 279, - bpf = 280, - execveat = 281, - userfaultfd = 282, - membarrier = 283, - mlock2 = 284, - copy_file_range = 285, - preadv2 = 286, - pwritev2 = 287, - pkey_mprotect = 288, - pkey_alloc = 289, - pkey_free = 290, - statx = 291, - io_pgetevents = 292, - rseq = 293, - kexec_file_load = 294, - pidfd_send_signal = 424, - io_uring_setup = 425, - io_uring_enter = 426, - io_uring_register = 427, - open_tree = 428, - move_mount = 429, - fsopen = 430, - fsconfig = 431, - fsmount = 432, - fspick = 433, - pidfd_open = 434, - clone3 = 435, - close_range = 436, - openat2 = 437, - pidfd_getfd = 438, - faccessat2 = 439, - process_madvise = 440, - epoll_pwait2 = 441, - mount_setattr = 442, - landlock_create_ruleset = 444, - landlock_add_rule = 445, - landlock_restrict_self = 446, - - _, -}; - pub const O = struct { pub const CREAT = 0o100; pub const EXCL = 0o200; diff --git a/lib/std/os/linux/sparc64.zig b/lib/std/os/linux/sparc64.zig index 2f9efecae4..351dd1413b 100644 --- a/lib/std/os/linux/sparc64.zig +++ b/lib/std/os/linux/sparc64.zig @@ -7,6 +7,7 @@ const stack_t = linux.stack_t; const sigset_t = linux.sigset_t; const linux = std.os.linux; +const SYS = linux.SYS; const sockaddr = linux.sockaddr; const socklen_t = linux.socklen_t; const iovec = std.os.iovec; @@ -192,390 +193,6 @@ pub fn restore_rt() callconv(.C) void { ); } -pub const SYS = enum(usize) { - restart_syscall = 0, - exit = 1, - fork = 2, - read = 3, - write = 4, - open = 5, - close = 6, - wait4 = 7, - creat = 8, - link = 9, - unlink = 10, - execv = 11, - chdir = 12, - chown = 13, - mknod = 14, - chmod = 15, - lchown = 16, - brk = 17, - perfctr = 18, - lseek = 19, - getpid = 20, - capget = 21, - capset = 22, - setuid = 23, - getuid = 24, - vmsplice = 25, - ptrace = 26, - alarm = 27, - sigaltstack = 28, - pause = 29, - utime = 30, - access = 33, - nice = 34, - sync = 36, - kill = 37, - stat = 38, - sendfile = 39, - lstat = 40, - dup = 41, - pipe = 42, - times = 43, - umount2 = 45, - setgid = 46, - getgid = 47, - signal = 48, - geteuid = 49, - getegid = 50, - acct = 51, - memory_ordering = 52, - ioctl = 54, - reboot = 55, - symlink = 57, - readlink = 58, - execve = 59, - umask = 60, - chroot = 61, - fstat = 62, - fstat64 = 63, - getpagesize = 64, - msync = 65, - vfork = 66, - pread64 = 67, - pwrite64 = 68, - mmap = 71, - munmap = 73, - mprotect = 74, - madvise = 75, - vhangup = 76, - mincore = 78, - getgroups = 79, - setgroups = 80, - getpgrp = 81, - setitimer = 83, - swapon = 85, - getitimer = 86, - sethostname = 88, - dup2 = 90, - fcntl = 92, - select = 93, - fsync = 95, - setpriority = 96, - socket = 97, - connect = 98, - accept = 99, - getpriority = 100, - rt_sigreturn = 101, - rt_sigaction = 102, - rt_sigprocmask = 103, - rt_sigpending = 104, - rt_sigtimedwait = 105, - rt_sigqueueinfo = 106, - rt_sigsuspend = 107, - setresuid = 108, - getresuid = 109, - setresgid = 110, - getresgid = 111, - recvmsg = 113, - sendmsg = 114, - gettimeofday = 116, - getrusage = 117, - getsockopt = 118, - getcwd = 119, - readv = 120, - writev = 121, - settimeofday = 122, - fchown = 123, - fchmod = 124, - recvfrom = 125, - setreuid = 126, - setregid = 127, - rename = 128, - truncate = 129, - ftruncate = 130, - flock = 131, - lstat64 = 132, - sendto = 133, - shutdown = 134, - socketpair = 135, - mkdir = 136, - rmdir = 137, - utimes = 138, - stat64 = 139, - sendfile64 = 140, - getpeername = 141, - futex = 142, - gettid = 143, - getrlimit = 144, - setrlimit = 145, - pivot_root = 146, - prctl = 147, - pciconfig_read = 148, - pciconfig_write = 149, - getsockname = 150, - inotify_init = 151, - inotify_add_watch = 152, - poll = 153, - getdents64 = 154, - inotify_rm_watch = 156, - statfs = 157, - fstatfs = 158, - umount = 159, - sched_set_affinity = 160, - sched_get_affinity = 161, - getdomainname = 162, - setdomainname = 163, - utrap_install = 164, - quotactl = 165, - set_tid_address = 166, - mount = 167, - ustat = 168, - setxattr = 169, - lsetxattr = 170, - fsetxattr = 171, - getxattr = 172, - lgetxattr = 173, - getdents = 174, - setsid = 175, - fchdir = 176, - fgetxattr = 177, - listxattr = 178, - llistxattr = 179, - flistxattr = 180, - removexattr = 181, - lremovexattr = 182, - sigpending = 183, - query_module = 184, - setpgid = 185, - fremovexattr = 186, - tkill = 187, - exit_group = 188, - uname = 189, - init_module = 190, - personality = 191, - remap_file_pages = 192, - epoll_create = 193, - epoll_ctl = 194, - epoll_wait = 195, - ioprio_set = 196, - getppid = 197, - sigaction = 198, - sgetmask = 199, - ssetmask = 200, - sigsuspend = 201, - oldlstat = 202, - uselib = 203, - readdir = 204, - readahead = 205, - socketcall = 206, - syslog = 207, - lookup_dcookie = 208, - fadvise64 = 209, - fadvise64_64 = 210, - tgkill = 211, - waitpid = 212, - swapoff = 213, - sysinfo = 214, - ipc = 215, - sigreturn = 216, - clone = 217, - ioprio_get = 218, - adjtimex = 219, - sigprocmask = 220, - create_module = 221, - delete_module = 222, - get_kernel_syms = 223, - getpgid = 224, - bdflush = 225, - sysfs = 226, - afs_syscall = 227, - setfsuid = 228, - setfsgid = 229, - _newselect = 230, - splice = 232, - stime = 233, - statfs64 = 234, - fstatfs64 = 235, - _llseek = 236, - mlock = 237, - munlock = 238, - mlockall = 239, - munlockall = 240, - sched_setparam = 241, - sched_getparam = 242, - sched_setscheduler = 243, - sched_getscheduler = 244, - sched_yield = 245, - sched_get_priority_max = 246, - sched_get_priority_min = 247, - sched_rr_get_interval = 248, - nanosleep = 249, - mremap = 250, - _sysctl = 251, - getsid = 252, - fdatasync = 253, - nfsservctl = 254, - sync_file_range = 255, - clock_settime = 256, - clock_gettime = 257, - clock_getres = 258, - clock_nanosleep = 259, - sched_getaffinity = 260, - sched_setaffinity = 261, - timer_settime = 262, - timer_gettime = 263, - timer_getoverrun = 264, - timer_delete = 265, - timer_create = 266, - vserver = 267, - io_setup = 268, - io_destroy = 269, - io_submit = 270, - io_cancel = 271, - io_getevents = 272, - mq_open = 273, - mq_unlink = 274, - mq_timedsend = 275, - mq_timedreceive = 276, - mq_notify = 277, - mq_getsetattr = 278, - waitid = 279, - tee = 280, - add_key = 281, - request_key = 282, - keyctl = 283, - openat = 284, - mkdirat = 285, - mknodat = 286, - fchownat = 287, - futimesat = 288, - fstatat64 = 289, - unlinkat = 290, - renameat = 291, - linkat = 292, - symlinkat = 293, - readlinkat = 294, - fchmodat = 295, - faccessat = 296, - pselect6 = 297, - ppoll = 298, - unshare = 299, - set_robust_list = 300, - get_robust_list = 301, - migrate_pages = 302, - mbind = 303, - get_mempolicy = 304, - set_mempolicy = 305, - kexec_load = 306, - move_pages = 307, - getcpu = 308, - epoll_pwait = 309, - utimensat = 310, - signalfd = 311, - timerfd_create = 312, - eventfd = 313, - fallocate = 314, - timerfd_settime = 315, - timerfd_gettime = 316, - signalfd4 = 317, - eventfd2 = 318, - epoll_create1 = 319, - dup3 = 320, - pipe2 = 321, - inotify_init1 = 322, - accept4 = 323, - preadv = 324, - pwritev = 325, - rt_tgsigqueueinfo = 326, - perf_event_open = 327, - recvmmsg = 328, - fanotify_init = 329, - fanotify_mark = 330, - prlimit64 = 331, - name_to_handle_at = 332, - open_by_handle_at = 333, - clock_adjtime = 334, - syncfs = 335, - sendmmsg = 336, - setns = 337, - process_vm_readv = 338, - process_vm_writev = 339, - kern_features = 340, - kcmp = 341, - finit_module = 342, - sched_setattr = 343, - sched_getattr = 344, - renameat2 = 345, - seccomp = 346, - getrandom = 347, - memfd_create = 348, - bpf = 349, - execveat = 350, - membarrier = 351, - userfaultfd = 352, - bind = 353, - listen = 354, - setsockopt = 355, - mlock2 = 356, - copy_file_range = 357, - preadv2 = 358, - pwritev2 = 359, - statx = 360, - io_pgetevents = 361, - pkey_mprotect = 362, - pkey_alloc = 363, - pkey_free = 364, - rseq = 365, - semtimedop = 392, - semget = 393, - semctl = 394, - shmget = 395, - shmctl = 396, - shmat = 397, - shmdt = 398, - msgget = 399, - msgsnd = 400, - msgrcv = 401, - msgctl = 402, - pidfd_send_signal = 424, - io_uring_setup = 425, - io_uring_enter = 426, - io_uring_register = 427, - open_tree = 428, - move_mount = 429, - fsopen = 430, - fsconfig = 431, - fsmount = 432, - fspick = 433, - pidfd_open = 434, - clone3 = 435, - close_range = 436, - openat2 = 437, - pidfd_getfd = 438, - faccessat2 = 439, - process_madvise = 440, - epoll_pwait2 = 441, - mount_setattr = 442, - landlock_create_ruleset = 444, - landlock_add_rule = 445, - landlock_restrict_self = 446, - - _, -}; - pub const O = struct { pub const CREAT = 0x200; pub const EXCL = 0x800; diff --git a/lib/std/os/linux/syscalls.zig b/lib/std/os/linux/syscalls.zig new file mode 100644 index 0000000000..fb0993afe5 --- /dev/null +++ b/lib/std/os/linux/syscalls.zig @@ -0,0 +1,3493 @@ +// This file is automatically generated. +// See tools/generate_linux_syscalls.zig for more info. + +pub const X86 = enum(usize) { + restart_syscall = 0, + exit = 1, + fork = 2, + read = 3, + write = 4, + open = 5, + close = 6, + waitpid = 7, + creat = 8, + link = 9, + unlink = 10, + execve = 11, + chdir = 12, + time = 13, + mknod = 14, + chmod = 15, + lchown = 16, + @"break" = 17, + oldstat = 18, + lseek = 19, + getpid = 20, + mount = 21, + umount = 22, + setuid = 23, + getuid = 24, + stime = 25, + ptrace = 26, + alarm = 27, + oldfstat = 28, + pause = 29, + utime = 30, + stty = 31, + gtty = 32, + access = 33, + nice = 34, + ftime = 35, + sync = 36, + kill = 37, + rename = 38, + mkdir = 39, + rmdir = 40, + dup = 41, + pipe = 42, + times = 43, + prof = 44, + brk = 45, + setgid = 46, + getgid = 47, + signal = 48, + geteuid = 49, + getegid = 50, + acct = 51, + umount2 = 52, + lock = 53, + ioctl = 54, + fcntl = 55, + mpx = 56, + setpgid = 57, + ulimit = 58, + oldolduname = 59, + umask = 60, + chroot = 61, + ustat = 62, + dup2 = 63, + getppid = 64, + getpgrp = 65, + setsid = 66, + sigaction = 67, + sgetmask = 68, + ssetmask = 69, + setreuid = 70, + setregid = 71, + sigsuspend = 72, + sigpending = 73, + sethostname = 74, + setrlimit = 75, + getrlimit = 76, + getrusage = 77, + gettimeofday = 78, + settimeofday = 79, + getgroups = 80, + setgroups = 81, + select = 82, + symlink = 83, + oldlstat = 84, + readlink = 85, + uselib = 86, + swapon = 87, + reboot = 88, + readdir = 89, + mmap = 90, + munmap = 91, + truncate = 92, + ftruncate = 93, + fchmod = 94, + fchown = 95, + getpriority = 96, + setpriority = 97, + profil = 98, + statfs = 99, + fstatfs = 100, + ioperm = 101, + socketcall = 102, + syslog = 103, + setitimer = 104, + getitimer = 105, + stat = 106, + lstat = 107, + fstat = 108, + olduname = 109, + iopl = 110, + vhangup = 111, + idle = 112, + vm86old = 113, + wait4 = 114, + swapoff = 115, + sysinfo = 116, + ipc = 117, + fsync = 118, + sigreturn = 119, + clone = 120, + setdomainname = 121, + uname = 122, + modify_ldt = 123, + adjtimex = 124, + mprotect = 125, + sigprocmask = 126, + create_module = 127, + init_module = 128, + delete_module = 129, + get_kernel_syms = 130, + quotactl = 131, + getpgid = 132, + fchdir = 133, + bdflush = 134, + sysfs = 135, + personality = 136, + afs_syscall = 137, + setfsuid = 138, + setfsgid = 139, + _llseek = 140, + getdents = 141, + _newselect = 142, + flock = 143, + msync = 144, + readv = 145, + writev = 146, + getsid = 147, + fdatasync = 148, + _sysctl = 149, + mlock = 150, + munlock = 151, + mlockall = 152, + munlockall = 153, + sched_setparam = 154, + sched_getparam = 155, + sched_setscheduler = 156, + sched_getscheduler = 157, + sched_yield = 158, + sched_get_priority_max = 159, + sched_get_priority_min = 160, + sched_rr_get_interval = 161, + nanosleep = 162, + mremap = 163, + setresuid = 164, + getresuid = 165, + vm86 = 166, + query_module = 167, + poll = 168, + nfsservctl = 169, + setresgid = 170, + getresgid = 171, + prctl = 172, + rt_sigreturn = 173, + rt_sigaction = 174, + rt_sigprocmask = 175, + rt_sigpending = 176, + rt_sigtimedwait = 177, + rt_sigqueueinfo = 178, + rt_sigsuspend = 179, + pread64 = 180, + pwrite64 = 181, + chown = 182, + getcwd = 183, + capget = 184, + capset = 185, + sigaltstack = 186, + sendfile = 187, + getpmsg = 188, + putpmsg = 189, + vfork = 190, + ugetrlimit = 191, + mmap2 = 192, + truncate64 = 193, + ftruncate64 = 194, + stat64 = 195, + lstat64 = 196, + fstat64 = 197, + lchown32 = 198, + getuid32 = 199, + getgid32 = 200, + geteuid32 = 201, + getegid32 = 202, + setreuid32 = 203, + setregid32 = 204, + getgroups32 = 205, + setgroups32 = 206, + fchown32 = 207, + setresuid32 = 208, + getresuid32 = 209, + setresgid32 = 210, + getresgid32 = 211, + chown32 = 212, + setuid32 = 213, + setgid32 = 214, + setfsuid32 = 215, + setfsgid32 = 216, + pivot_root = 217, + mincore = 218, + madvise = 219, + getdents64 = 220, + fcntl64 = 221, + gettid = 224, + readahead = 225, + setxattr = 226, + lsetxattr = 227, + fsetxattr = 228, + getxattr = 229, + lgetxattr = 230, + fgetxattr = 231, + listxattr = 232, + llistxattr = 233, + flistxattr = 234, + removexattr = 235, + lremovexattr = 236, + fremovexattr = 237, + tkill = 238, + sendfile64 = 239, + futex = 240, + sched_setaffinity = 241, + sched_getaffinity = 242, + set_thread_area = 243, + get_thread_area = 244, + io_setup = 245, + io_destroy = 246, + io_getevents = 247, + io_submit = 248, + io_cancel = 249, + fadvise64 = 250, + exit_group = 252, + lookup_dcookie = 253, + epoll_create = 254, + epoll_ctl = 255, + epoll_wait = 256, + remap_file_pages = 257, + set_tid_address = 258, + timer_create = 259, + timer_settime = 260, + timer_gettime = 261, + timer_getoverrun = 262, + timer_delete = 263, + clock_settime = 264, + clock_gettime = 265, + clock_getres = 266, + clock_nanosleep = 267, + statfs64 = 268, + fstatfs64 = 269, + tgkill = 270, + utimes = 271, + fadvise64_64 = 272, + vserver = 273, + mbind = 274, + get_mempolicy = 275, + set_mempolicy = 276, + mq_open = 277, + mq_unlink = 278, + mq_timedsend = 279, + mq_timedreceive = 280, + mq_notify = 281, + mq_getsetattr = 282, + kexec_load = 283, + waitid = 284, + add_key = 286, + request_key = 287, + keyctl = 288, + ioprio_set = 289, + ioprio_get = 290, + inotify_init = 291, + inotify_add_watch = 292, + inotify_rm_watch = 293, + migrate_pages = 294, + openat = 295, + mkdirat = 296, + mknodat = 297, + fchownat = 298, + futimesat = 299, + fstatat64 = 300, + unlinkat = 301, + renameat = 302, + linkat = 303, + symlinkat = 304, + readlinkat = 305, + fchmodat = 306, + faccessat = 307, + pselect6 = 308, + ppoll = 309, + unshare = 310, + set_robust_list = 311, + get_robust_list = 312, + splice = 313, + sync_file_range = 314, + tee = 315, + vmsplice = 316, + move_pages = 317, + getcpu = 318, + epoll_pwait = 319, + utimensat = 320, + signalfd = 321, + timerfd_create = 322, + eventfd = 323, + fallocate = 324, + timerfd_settime = 325, + timerfd_gettime = 326, + signalfd4 = 327, + eventfd2 = 328, + epoll_create1 = 329, + dup3 = 330, + pipe2 = 331, + inotify_init1 = 332, + preadv = 333, + pwritev = 334, + rt_tgsigqueueinfo = 335, + perf_event_open = 336, + recvmmsg = 337, + fanotify_init = 338, + fanotify_mark = 339, + prlimit64 = 340, + name_to_handle_at = 341, + open_by_handle_at = 342, + clock_adjtime = 343, + syncfs = 344, + sendmmsg = 345, + setns = 346, + process_vm_readv = 347, + process_vm_writev = 348, + kcmp = 349, + finit_module = 350, + sched_setattr = 351, + sched_getattr = 352, + renameat2 = 353, + seccomp = 354, + getrandom = 355, + memfd_create = 356, + bpf = 357, + execveat = 358, + socket = 359, + socketpair = 360, + bind = 361, + connect = 362, + listen = 363, + accept4 = 364, + getsockopt = 365, + setsockopt = 366, + getsockname = 367, + getpeername = 368, + sendto = 369, + sendmsg = 370, + recvfrom = 371, + recvmsg = 372, + shutdown = 373, + userfaultfd = 374, + membarrier = 375, + mlock2 = 376, + copy_file_range = 377, + preadv2 = 378, + pwritev2 = 379, + pkey_mprotect = 380, + pkey_alloc = 381, + pkey_free = 382, + statx = 383, + arch_prctl = 384, + io_pgetevents = 385, + rseq = 386, + semget = 393, + semctl = 394, + shmget = 395, + shmctl = 396, + shmat = 397, + shmdt = 398, + msgget = 399, + msgsnd = 400, + msgrcv = 401, + msgctl = 402, + clock_gettime64 = 403, + clock_settime64 = 404, + clock_adjtime64 = 405, + clock_getres_time64 = 406, + clock_nanosleep_time64 = 407, + timer_gettime64 = 408, + timer_settime64 = 409, + timerfd_gettime64 = 410, + timerfd_settime64 = 411, + utimensat_time64 = 412, + pselect6_time64 = 413, + ppoll_time64 = 414, + io_pgetevents_time64 = 416, + recvmmsg_time64 = 417, + mq_timedsend_time64 = 418, + mq_timedreceive_time64 = 419, + semtimedop_time64 = 420, + rt_sigtimedwait_time64 = 421, + futex_time64 = 422, + sched_rr_get_interval_time64 = 423, + pidfd_send_signal = 424, + io_uring_setup = 425, + io_uring_enter = 426, + io_uring_register = 427, + open_tree = 428, + move_mount = 429, + fsopen = 430, + fsconfig = 431, + fsmount = 432, + fspick = 433, + pidfd_open = 434, + clone3 = 435, + close_range = 436, + openat2 = 437, + pidfd_getfd = 438, + faccessat2 = 439, + process_madvise = 440, + epoll_pwait2 = 441, + mount_setattr = 442, + quotactl_fd = 443, + landlock_create_ruleset = 444, + landlock_add_rule = 445, + landlock_restrict_self = 446, + memfd_secret = 447, + process_mrelease = 448, + futex_waitv = 449, + set_mempolicy_home_node = 450, +}; + +pub const X64 = enum(usize) { + read = 0, + write = 1, + open = 2, + close = 3, + stat = 4, + fstat = 5, + lstat = 6, + poll = 7, + lseek = 8, + mmap = 9, + mprotect = 10, + munmap = 11, + brk = 12, + rt_sigaction = 13, + rt_sigprocmask = 14, + rt_sigreturn = 15, + ioctl = 16, + pread64 = 17, + pwrite64 = 18, + readv = 19, + writev = 20, + access = 21, + pipe = 22, + select = 23, + sched_yield = 24, + mremap = 25, + msync = 26, + mincore = 27, + madvise = 28, + shmget = 29, + shmat = 30, + shmctl = 31, + dup = 32, + dup2 = 33, + pause = 34, + nanosleep = 35, + getitimer = 36, + alarm = 37, + setitimer = 38, + getpid = 39, + sendfile = 40, + socket = 41, + connect = 42, + accept = 43, + sendto = 44, + recvfrom = 45, + sendmsg = 46, + recvmsg = 47, + shutdown = 48, + bind = 49, + listen = 50, + getsockname = 51, + getpeername = 52, + socketpair = 53, + setsockopt = 54, + getsockopt = 55, + clone = 56, + fork = 57, + vfork = 58, + execve = 59, + exit = 60, + wait4 = 61, + kill = 62, + uname = 63, + semget = 64, + semop = 65, + semctl = 66, + shmdt = 67, + msgget = 68, + msgsnd = 69, + msgrcv = 70, + msgctl = 71, + fcntl = 72, + flock = 73, + fsync = 74, + fdatasync = 75, + truncate = 76, + ftruncate = 77, + getdents = 78, + getcwd = 79, + chdir = 80, + fchdir = 81, + rename = 82, + mkdir = 83, + rmdir = 84, + creat = 85, + link = 86, + unlink = 87, + symlink = 88, + readlink = 89, + chmod = 90, + fchmod = 91, + chown = 92, + fchown = 93, + lchown = 94, + umask = 95, + gettimeofday = 96, + getrlimit = 97, + getrusage = 98, + sysinfo = 99, + times = 100, + ptrace = 101, + getuid = 102, + syslog = 103, + getgid = 104, + setuid = 105, + setgid = 106, + geteuid = 107, + getegid = 108, + setpgid = 109, + getppid = 110, + getpgrp = 111, + setsid = 112, + setreuid = 113, + setregid = 114, + getgroups = 115, + setgroups = 116, + setresuid = 117, + getresuid = 118, + setresgid = 119, + getresgid = 120, + getpgid = 121, + setfsuid = 122, + setfsgid = 123, + getsid = 124, + capget = 125, + capset = 126, + rt_sigpending = 127, + rt_sigtimedwait = 128, + rt_sigqueueinfo = 129, + rt_sigsuspend = 130, + sigaltstack = 131, + utime = 132, + mknod = 133, + uselib = 134, + personality = 135, + ustat = 136, + statfs = 137, + fstatfs = 138, + sysfs = 139, + getpriority = 140, + setpriority = 141, + sched_setparam = 142, + sched_getparam = 143, + sched_setscheduler = 144, + sched_getscheduler = 145, + sched_get_priority_max = 146, + sched_get_priority_min = 147, + sched_rr_get_interval = 148, + mlock = 149, + munlock = 150, + mlockall = 151, + munlockall = 152, + vhangup = 153, + modify_ldt = 154, + pivot_root = 155, + _sysctl = 156, + prctl = 157, + arch_prctl = 158, + adjtimex = 159, + setrlimit = 160, + chroot = 161, + sync = 162, + acct = 163, + settimeofday = 164, + mount = 165, + umount2 = 166, + swapon = 167, + swapoff = 168, + reboot = 169, + sethostname = 170, + setdomainname = 171, + iopl = 172, + ioperm = 173, + create_module = 174, + init_module = 175, + delete_module = 176, + get_kernel_syms = 177, + query_module = 178, + quotactl = 179, + nfsservctl = 180, + getpmsg = 181, + putpmsg = 182, + afs_syscall = 183, + tuxcall = 184, + security = 185, + gettid = 186, + readahead = 187, + setxattr = 188, + lsetxattr = 189, + fsetxattr = 190, + getxattr = 191, + lgetxattr = 192, + fgetxattr = 193, + listxattr = 194, + llistxattr = 195, + flistxattr = 196, + removexattr = 197, + lremovexattr = 198, + fremovexattr = 199, + tkill = 200, + time = 201, + futex = 202, + sched_setaffinity = 203, + sched_getaffinity = 204, + set_thread_area = 205, + io_setup = 206, + io_destroy = 207, + io_getevents = 208, + io_submit = 209, + io_cancel = 210, + get_thread_area = 211, + lookup_dcookie = 212, + epoll_create = 213, + epoll_ctl_old = 214, + epoll_wait_old = 215, + remap_file_pages = 216, + getdents64 = 217, + set_tid_address = 218, + restart_syscall = 219, + semtimedop = 220, + fadvise64 = 221, + timer_create = 222, + timer_settime = 223, + timer_gettime = 224, + timer_getoverrun = 225, + timer_delete = 226, + clock_settime = 227, + clock_gettime = 228, + clock_getres = 229, + clock_nanosleep = 230, + exit_group = 231, + epoll_wait = 232, + epoll_ctl = 233, + tgkill = 234, + utimes = 235, + vserver = 236, + mbind = 237, + set_mempolicy = 238, + get_mempolicy = 239, + mq_open = 240, + mq_unlink = 241, + mq_timedsend = 242, + mq_timedreceive = 243, + mq_notify = 244, + mq_getsetattr = 245, + kexec_load = 246, + waitid = 247, + add_key = 248, + request_key = 249, + keyctl = 250, + ioprio_set = 251, + ioprio_get = 252, + inotify_init = 253, + inotify_add_watch = 254, + inotify_rm_watch = 255, + migrate_pages = 256, + openat = 257, + mkdirat = 258, + mknodat = 259, + fchownat = 260, + futimesat = 261, + fstatat64 = 262, + unlinkat = 263, + renameat = 264, + linkat = 265, + symlinkat = 266, + readlinkat = 267, + fchmodat = 268, + faccessat = 269, + pselect6 = 270, + ppoll = 271, + unshare = 272, + set_robust_list = 273, + get_robust_list = 274, + splice = 275, + tee = 276, + sync_file_range = 277, + vmsplice = 278, + move_pages = 279, + utimensat = 280, + epoll_pwait = 281, + signalfd = 282, + timerfd_create = 283, + eventfd = 284, + fallocate = 285, + timerfd_settime = 286, + timerfd_gettime = 287, + accept4 = 288, + signalfd4 = 289, + eventfd2 = 290, + epoll_create1 = 291, + dup3 = 292, + pipe2 = 293, + inotify_init1 = 294, + preadv = 295, + pwritev = 296, + rt_tgsigqueueinfo = 297, + perf_event_open = 298, + recvmmsg = 299, + fanotify_init = 300, + fanotify_mark = 301, + prlimit64 = 302, + name_to_handle_at = 303, + open_by_handle_at = 304, + clock_adjtime = 305, + syncfs = 306, + sendmmsg = 307, + setns = 308, + getcpu = 309, + process_vm_readv = 310, + process_vm_writev = 311, + kcmp = 312, + finit_module = 313, + sched_setattr = 314, + sched_getattr = 315, + renameat2 = 316, + seccomp = 317, + getrandom = 318, + memfd_create = 319, + kexec_file_load = 320, + bpf = 321, + execveat = 322, + userfaultfd = 323, + membarrier = 324, + mlock2 = 325, + copy_file_range = 326, + preadv2 = 327, + pwritev2 = 328, + pkey_mprotect = 329, + pkey_alloc = 330, + pkey_free = 331, + statx = 332, + io_pgetevents = 333, + rseq = 334, + pidfd_send_signal = 424, + io_uring_setup = 425, + io_uring_enter = 426, + io_uring_register = 427, + open_tree = 428, + move_mount = 429, + fsopen = 430, + fsconfig = 431, + fsmount = 432, + fspick = 433, + pidfd_open = 434, + clone3 = 435, + close_range = 436, + openat2 = 437, + pidfd_getfd = 438, + faccessat2 = 439, + process_madvise = 440, + epoll_pwait2 = 441, + mount_setattr = 442, + quotactl_fd = 443, + landlock_create_ruleset = 444, + landlock_add_rule = 445, + landlock_restrict_self = 446, + memfd_secret = 447, + process_mrelease = 448, + futex_waitv = 449, + set_mempolicy_home_node = 450, +}; + +pub const Arm = enum(usize) { + const arm_base = 0x0f0000; + + restart_syscall = 0, + exit = 1, + fork = 2, + read = 3, + write = 4, + open = 5, + close = 6, + creat = 8, + link = 9, + unlink = 10, + execve = 11, + chdir = 12, + mknod = 14, + chmod = 15, + lchown = 16, + lseek = 19, + getpid = 20, + mount = 21, + setuid = 23, + getuid = 24, + ptrace = 26, + pause = 29, + access = 33, + nice = 34, + sync = 36, + kill = 37, + rename = 38, + mkdir = 39, + rmdir = 40, + dup = 41, + pipe = 42, + times = 43, + brk = 45, + setgid = 46, + getgid = 47, + geteuid = 49, + getegid = 50, + acct = 51, + umount2 = 52, + ioctl = 54, + fcntl = 55, + setpgid = 57, + umask = 60, + chroot = 61, + ustat = 62, + dup2 = 63, + getppid = 64, + getpgrp = 65, + setsid = 66, + sigaction = 67, + setreuid = 70, + setregid = 71, + sigsuspend = 72, + sigpending = 73, + sethostname = 74, + setrlimit = 75, + getrusage = 77, + gettimeofday = 78, + settimeofday = 79, + getgroups = 80, + setgroups = 81, + symlink = 83, + readlink = 85, + uselib = 86, + swapon = 87, + reboot = 88, + munmap = 91, + truncate = 92, + ftruncate = 93, + fchmod = 94, + fchown = 95, + getpriority = 96, + setpriority = 97, + statfs = 99, + fstatfs = 100, + syslog = 103, + setitimer = 104, + getitimer = 105, + stat = 106, + lstat = 107, + fstat = 108, + vhangup = 111, + wait4 = 114, + swapoff = 115, + sysinfo = 116, + fsync = 118, + sigreturn = 119, + clone = 120, + setdomainname = 121, + uname = 122, + adjtimex = 124, + mprotect = 125, + sigprocmask = 126, + init_module = 128, + delete_module = 129, + quotactl = 131, + getpgid = 132, + fchdir = 133, + bdflush = 134, + sysfs = 135, + personality = 136, + setfsuid = 138, + setfsgid = 139, + _llseek = 140, + getdents = 141, + _newselect = 142, + flock = 143, + msync = 144, + readv = 145, + writev = 146, + getsid = 147, + fdatasync = 148, + _sysctl = 149, + mlock = 150, + munlock = 151, + mlockall = 152, + munlockall = 153, + sched_setparam = 154, + sched_getparam = 155, + sched_setscheduler = 156, + sched_getscheduler = 157, + sched_yield = 158, + sched_get_priority_max = 159, + sched_get_priority_min = 160, + sched_rr_get_interval = 161, + nanosleep = 162, + mremap = 163, + setresuid = 164, + getresuid = 165, + poll = 168, + nfsservctl = 169, + setresgid = 170, + getresgid = 171, + prctl = 172, + rt_sigreturn = 173, + rt_sigaction = 174, + rt_sigprocmask = 175, + rt_sigpending = 176, + rt_sigtimedwait = 177, + rt_sigqueueinfo = 178, + rt_sigsuspend = 179, + pread64 = 180, + pwrite64 = 181, + chown = 182, + getcwd = 183, + capget = 184, + capset = 185, + sigaltstack = 186, + sendfile = 187, + vfork = 190, + ugetrlimit = 191, + mmap2 = 192, + truncate64 = 193, + ftruncate64 = 194, + stat64 = 195, + lstat64 = 196, + fstat64 = 197, + lchown32 = 198, + getuid32 = 199, + getgid32 = 200, + geteuid32 = 201, + getegid32 = 202, + setreuid32 = 203, + setregid32 = 204, + getgroups32 = 205, + setgroups32 = 206, + fchown32 = 207, + setresuid32 = 208, + getresuid32 = 209, + setresgid32 = 210, + getresgid32 = 211, + chown32 = 212, + setuid32 = 213, + setgid32 = 214, + setfsuid32 = 215, + setfsgid32 = 216, + getdents64 = 217, + pivot_root = 218, + mincore = 219, + madvise = 220, + fcntl64 = 221, + gettid = 224, + readahead = 225, + setxattr = 226, + lsetxattr = 227, + fsetxattr = 228, + getxattr = 229, + lgetxattr = 230, + fgetxattr = 231, + listxattr = 232, + llistxattr = 233, + flistxattr = 234, + removexattr = 235, + lremovexattr = 236, + fremovexattr = 237, + tkill = 238, + sendfile64 = 239, + futex = 240, + sched_setaffinity = 241, + sched_getaffinity = 242, + io_setup = 243, + io_destroy = 244, + io_getevents = 245, + io_submit = 246, + io_cancel = 247, + exit_group = 248, + lookup_dcookie = 249, + epoll_create = 250, + epoll_ctl = 251, + epoll_wait = 252, + remap_file_pages = 253, + set_tid_address = 256, + timer_create = 257, + timer_settime = 258, + timer_gettime = 259, + timer_getoverrun = 260, + timer_delete = 261, + clock_settime = 262, + clock_gettime = 263, + clock_getres = 264, + clock_nanosleep = 265, + statfs64 = 266, + fstatfs64 = 267, + tgkill = 268, + utimes = 269, + fadvise64_64 = 270, + pciconfig_iobase = 271, + pciconfig_read = 272, + pciconfig_write = 273, + mq_open = 274, + mq_unlink = 275, + mq_timedsend = 276, + mq_timedreceive = 277, + mq_notify = 278, + mq_getsetattr = 279, + waitid = 280, + socket = 281, + bind = 282, + connect = 283, + listen = 284, + accept = 285, + getsockname = 286, + getpeername = 287, + socketpair = 288, + send = 289, + sendto = 290, + recv = 291, + recvfrom = 292, + shutdown = 293, + setsockopt = 294, + getsockopt = 295, + sendmsg = 296, + recvmsg = 297, + semop = 298, + semget = 299, + semctl = 300, + msgsnd = 301, + msgrcv = 302, + msgget = 303, + msgctl = 304, + shmat = 305, + shmdt = 306, + shmget = 307, + shmctl = 308, + add_key = 309, + request_key = 310, + keyctl = 311, + semtimedop = 312, + vserver = 313, + ioprio_set = 314, + ioprio_get = 315, + inotify_init = 316, + inotify_add_watch = 317, + inotify_rm_watch = 318, + mbind = 319, + get_mempolicy = 320, + set_mempolicy = 321, + openat = 322, + mkdirat = 323, + mknodat = 324, + fchownat = 325, + futimesat = 326, + fstatat64 = 327, + unlinkat = 328, + renameat = 329, + linkat = 330, + symlinkat = 331, + readlinkat = 332, + fchmodat = 333, + faccessat = 334, + pselect6 = 335, + ppoll = 336, + unshare = 337, + set_robust_list = 338, + get_robust_list = 339, + splice = 340, + sync_file_range = 341, + tee = 342, + vmsplice = 343, + move_pages = 344, + getcpu = 345, + epoll_pwait = 346, + kexec_load = 347, + utimensat = 348, + signalfd = 349, + timerfd_create = 350, + eventfd = 351, + fallocate = 352, + timerfd_settime = 353, + timerfd_gettime = 354, + signalfd4 = 355, + eventfd2 = 356, + epoll_create1 = 357, + dup3 = 358, + pipe2 = 359, + inotify_init1 = 360, + preadv = 361, + pwritev = 362, + rt_tgsigqueueinfo = 363, + perf_event_open = 364, + recvmmsg = 365, + accept4 = 366, + fanotify_init = 367, + fanotify_mark = 368, + prlimit64 = 369, + name_to_handle_at = 370, + open_by_handle_at = 371, + clock_adjtime = 372, + syncfs = 373, + sendmmsg = 374, + setns = 375, + process_vm_readv = 376, + process_vm_writev = 377, + kcmp = 378, + finit_module = 379, + sched_setattr = 380, + sched_getattr = 381, + renameat2 = 382, + seccomp = 383, + getrandom = 384, + memfd_create = 385, + bpf = 386, + execveat = 387, + userfaultfd = 388, + membarrier = 389, + mlock2 = 390, + copy_file_range = 391, + preadv2 = 392, + pwritev2 = 393, + pkey_mprotect = 394, + pkey_alloc = 395, + pkey_free = 396, + statx = 397, + rseq = 398, + io_pgetevents = 399, + migrate_pages = 400, + kexec_file_load = 401, + clock_gettime64 = 403, + clock_settime64 = 404, + clock_adjtime64 = 405, + clock_getres_time64 = 406, + clock_nanosleep_time64 = 407, + timer_gettime64 = 408, + timer_settime64 = 409, + timerfd_gettime64 = 410, + timerfd_settime64 = 411, + utimensat_time64 = 412, + pselect6_time64 = 413, + ppoll_time64 = 414, + io_pgetevents_time64 = 416, + recvmmsg_time64 = 417, + mq_timedsend_time64 = 418, + mq_timedreceive_time64 = 419, + semtimedop_time64 = 420, + rt_sigtimedwait_time64 = 421, + futex_time64 = 422, + sched_rr_get_interval_time64 = 423, + pidfd_send_signal = 424, + io_uring_setup = 425, + io_uring_enter = 426, + io_uring_register = 427, + open_tree = 428, + move_mount = 429, + fsopen = 430, + fsconfig = 431, + fsmount = 432, + fspick = 433, + pidfd_open = 434, + clone3 = 435, + close_range = 436, + openat2 = 437, + pidfd_getfd = 438, + faccessat2 = 439, + process_madvise = 440, + epoll_pwait2 = 441, + mount_setattr = 442, + quotactl_fd = 443, + landlock_create_ruleset = 444, + landlock_add_rule = 445, + landlock_restrict_self = 446, + process_mrelease = 448, + futex_waitv = 449, + set_mempolicy_home_node = 450, + + breakpoint = arm_base + 1, + cacheflush = arm_base + 2, + usr26 = arm_base + 3, + usr32 = arm_base + 4, + set_tls = arm_base + 5, + get_tls = arm_base + 6, +}; + +pub const Sparc64 = enum(usize) { + restart_syscall = 0, + exit = 1, + fork = 2, + read = 3, + write = 4, + open = 5, + close = 6, + wait4 = 7, + creat = 8, + link = 9, + unlink = 10, + execv = 11, + chdir = 12, + chown = 13, + mknod = 14, + chmod = 15, + lchown = 16, + brk = 17, + perfctr = 18, + lseek = 19, + getpid = 20, + capget = 21, + capset = 22, + setuid = 23, + getuid = 24, + vmsplice = 25, + ptrace = 26, + alarm = 27, + sigaltstack = 28, + pause = 29, + utime = 30, + access = 33, + nice = 34, + sync = 36, + kill = 37, + stat = 38, + sendfile = 39, + lstat = 40, + dup = 41, + pipe = 42, + times = 43, + umount2 = 45, + setgid = 46, + getgid = 47, + signal = 48, + geteuid = 49, + getegid = 50, + acct = 51, + memory_ordering = 52, + ioctl = 54, + reboot = 55, + symlink = 57, + readlink = 58, + execve = 59, + umask = 60, + chroot = 61, + fstat = 62, + fstat64 = 63, + getpagesize = 64, + msync = 65, + vfork = 66, + pread64 = 67, + pwrite64 = 68, + mmap = 71, + munmap = 73, + mprotect = 74, + madvise = 75, + vhangup = 76, + mincore = 78, + getgroups = 79, + setgroups = 80, + getpgrp = 81, + setitimer = 83, + swapon = 85, + getitimer = 86, + sethostname = 88, + dup2 = 90, + fcntl = 92, + select = 93, + fsync = 95, + setpriority = 96, + socket = 97, + connect = 98, + accept = 99, + getpriority = 100, + rt_sigreturn = 101, + rt_sigaction = 102, + rt_sigprocmask = 103, + rt_sigpending = 104, + rt_sigtimedwait = 105, + rt_sigqueueinfo = 106, + rt_sigsuspend = 107, + setresuid = 108, + getresuid = 109, + setresgid = 110, + getresgid = 111, + recvmsg = 113, + sendmsg = 114, + gettimeofday = 116, + getrusage = 117, + getsockopt = 118, + getcwd = 119, + readv = 120, + writev = 121, + settimeofday = 122, + fchown = 123, + fchmod = 124, + recvfrom = 125, + setreuid = 126, + setregid = 127, + rename = 128, + truncate = 129, + ftruncate = 130, + flock = 131, + lstat64 = 132, + sendto = 133, + shutdown = 134, + socketpair = 135, + mkdir = 136, + rmdir = 137, + utimes = 138, + stat64 = 139, + sendfile64 = 140, + getpeername = 141, + futex = 142, + gettid = 143, + getrlimit = 144, + setrlimit = 145, + pivot_root = 146, + prctl = 147, + pciconfig_read = 148, + pciconfig_write = 149, + getsockname = 150, + inotify_init = 151, + inotify_add_watch = 152, + poll = 153, + getdents64 = 154, + inotify_rm_watch = 156, + statfs = 157, + fstatfs = 158, + umount = 159, + sched_set_affinity = 160, + sched_get_affinity = 161, + getdomainname = 162, + setdomainname = 163, + utrap_install = 164, + quotactl = 165, + set_tid_address = 166, + mount = 167, + ustat = 168, + setxattr = 169, + lsetxattr = 170, + fsetxattr = 171, + getxattr = 172, + lgetxattr = 173, + getdents = 174, + setsid = 175, + fchdir = 176, + fgetxattr = 177, + listxattr = 178, + llistxattr = 179, + flistxattr = 180, + removexattr = 181, + lremovexattr = 182, + sigpending = 183, + query_module = 184, + setpgid = 185, + fremovexattr = 186, + tkill = 187, + exit_group = 188, + uname = 189, + init_module = 190, + personality = 191, + remap_file_pages = 192, + epoll_create = 193, + epoll_ctl = 194, + epoll_wait = 195, + ioprio_set = 196, + getppid = 197, + sigaction = 198, + sgetmask = 199, + ssetmask = 200, + sigsuspend = 201, + oldlstat = 202, + uselib = 203, + readdir = 204, + readahead = 205, + socketcall = 206, + syslog = 207, + lookup_dcookie = 208, + fadvise64 = 209, + fadvise64_64 = 210, + tgkill = 211, + waitpid = 212, + swapoff = 213, + sysinfo = 214, + ipc = 215, + sigreturn = 216, + clone = 217, + ioprio_get = 218, + adjtimex = 219, + sigprocmask = 220, + create_module = 221, + delete_module = 222, + get_kernel_syms = 223, + getpgid = 224, + bdflush = 225, + sysfs = 226, + afs_syscall = 227, + setfsuid = 228, + setfsgid = 229, + _newselect = 230, + splice = 232, + stime = 233, + statfs64 = 234, + fstatfs64 = 235, + _llseek = 236, + mlock = 237, + munlock = 238, + mlockall = 239, + munlockall = 240, + sched_setparam = 241, + sched_getparam = 242, + sched_setscheduler = 243, + sched_getscheduler = 244, + sched_yield = 245, + sched_get_priority_max = 246, + sched_get_priority_min = 247, + sched_rr_get_interval = 248, + nanosleep = 249, + mremap = 250, + _sysctl = 251, + getsid = 252, + fdatasync = 253, + nfsservctl = 254, + sync_file_range = 255, + clock_settime = 256, + clock_gettime = 257, + clock_getres = 258, + clock_nanosleep = 259, + sched_getaffinity = 260, + sched_setaffinity = 261, + timer_settime = 262, + timer_gettime = 263, + timer_getoverrun = 264, + timer_delete = 265, + timer_create = 266, + vserver = 267, + io_setup = 268, + io_destroy = 269, + io_submit = 270, + io_cancel = 271, + io_getevents = 272, + mq_open = 273, + mq_unlink = 274, + mq_timedsend = 275, + mq_timedreceive = 276, + mq_notify = 277, + mq_getsetattr = 278, + waitid = 279, + tee = 280, + add_key = 281, + request_key = 282, + keyctl = 283, + openat = 284, + mkdirat = 285, + mknodat = 286, + fchownat = 287, + futimesat = 288, + fstatat64 = 289, + unlinkat = 290, + renameat = 291, + linkat = 292, + symlinkat = 293, + readlinkat = 294, + fchmodat = 295, + faccessat = 296, + pselect6 = 297, + ppoll = 298, + unshare = 299, + set_robust_list = 300, + get_robust_list = 301, + migrate_pages = 302, + mbind = 303, + get_mempolicy = 304, + set_mempolicy = 305, + kexec_load = 306, + move_pages = 307, + getcpu = 308, + epoll_pwait = 309, + utimensat = 310, + signalfd = 311, + timerfd_create = 312, + eventfd = 313, + fallocate = 314, + timerfd_settime = 315, + timerfd_gettime = 316, + signalfd4 = 317, + eventfd2 = 318, + epoll_create1 = 319, + dup3 = 320, + pipe2 = 321, + inotify_init1 = 322, + accept4 = 323, + preadv = 324, + pwritev = 325, + rt_tgsigqueueinfo = 326, + perf_event_open = 327, + recvmmsg = 328, + fanotify_init = 329, + fanotify_mark = 330, + prlimit64 = 331, + name_to_handle_at = 332, + open_by_handle_at = 333, + clock_adjtime = 334, + syncfs = 335, + sendmmsg = 336, + setns = 337, + process_vm_readv = 338, + process_vm_writev = 339, + kern_features = 340, + kcmp = 341, + finit_module = 342, + sched_setattr = 343, + sched_getattr = 344, + renameat2 = 345, + seccomp = 346, + getrandom = 347, + memfd_create = 348, + bpf = 349, + execveat = 350, + membarrier = 351, + userfaultfd = 352, + bind = 353, + listen = 354, + setsockopt = 355, + mlock2 = 356, + copy_file_range = 357, + preadv2 = 358, + pwritev2 = 359, + statx = 360, + io_pgetevents = 361, + pkey_mprotect = 362, + pkey_alloc = 363, + pkey_free = 364, + rseq = 365, + semtimedop = 392, + semget = 393, + semctl = 394, + shmget = 395, + shmctl = 396, + shmat = 397, + shmdt = 398, + msgget = 399, + msgsnd = 400, + msgrcv = 401, + msgctl = 402, + pidfd_send_signal = 424, + io_uring_setup = 425, + io_uring_enter = 426, + io_uring_register = 427, + open_tree = 428, + move_mount = 429, + fsopen = 430, + fsconfig = 431, + fsmount = 432, + fspick = 433, + pidfd_open = 434, + close_range = 436, + openat2 = 437, + pidfd_getfd = 438, + faccessat2 = 439, + process_madvise = 440, + epoll_pwait2 = 441, + mount_setattr = 442, + quotactl_fd = 443, + landlock_create_ruleset = 444, + landlock_add_rule = 445, + landlock_restrict_self = 446, + process_mrelease = 448, + futex_waitv = 449, + set_mempolicy_home_node = 450, +}; + +pub const Mips = enum(usize) { + pub const Linux = 4000; + + syscall = Linux + 0, + exit = Linux + 1, + fork = Linux + 2, + read = Linux + 3, + write = Linux + 4, + open = Linux + 5, + close = Linux + 6, + waitpid = Linux + 7, + creat = Linux + 8, + link = Linux + 9, + unlink = Linux + 10, + execve = Linux + 11, + chdir = Linux + 12, + time = Linux + 13, + mknod = Linux + 14, + chmod = Linux + 15, + lchown = Linux + 16, + @"break" = Linux + 17, + lseek = Linux + 19, + getpid = Linux + 20, + mount = Linux + 21, + umount = Linux + 22, + setuid = Linux + 23, + getuid = Linux + 24, + stime = Linux + 25, + ptrace = Linux + 26, + alarm = Linux + 27, + pause = Linux + 29, + utime = Linux + 30, + stty = Linux + 31, + gtty = Linux + 32, + access = Linux + 33, + nice = Linux + 34, + ftime = Linux + 35, + sync = Linux + 36, + kill = Linux + 37, + rename = Linux + 38, + mkdir = Linux + 39, + rmdir = Linux + 40, + dup = Linux + 41, + pipe = Linux + 42, + times = Linux + 43, + prof = Linux + 44, + brk = Linux + 45, + setgid = Linux + 46, + getgid = Linux + 47, + signal = Linux + 48, + geteuid = Linux + 49, + getegid = Linux + 50, + acct = Linux + 51, + umount2 = Linux + 52, + lock = Linux + 53, + ioctl = Linux + 54, + fcntl = Linux + 55, + mpx = Linux + 56, + setpgid = Linux + 57, + ulimit = Linux + 58, + umask = Linux + 60, + chroot = Linux + 61, + ustat = Linux + 62, + dup2 = Linux + 63, + getppid = Linux + 64, + getpgrp = Linux + 65, + setsid = Linux + 66, + sigaction = Linux + 67, + sgetmask = Linux + 68, + ssetmask = Linux + 69, + setreuid = Linux + 70, + setregid = Linux + 71, + sigsuspend = Linux + 72, + sigpending = Linux + 73, + sethostname = Linux + 74, + setrlimit = Linux + 75, + getrlimit = Linux + 76, + getrusage = Linux + 77, + gettimeofday = Linux + 78, + settimeofday = Linux + 79, + getgroups = Linux + 80, + setgroups = Linux + 81, + reserved82 = Linux + 82, + symlink = Linux + 83, + readlink = Linux + 85, + uselib = Linux + 86, + swapon = Linux + 87, + reboot = Linux + 88, + readdir = Linux + 89, + mmap = Linux + 90, + munmap = Linux + 91, + truncate = Linux + 92, + ftruncate = Linux + 93, + fchmod = Linux + 94, + fchown = Linux + 95, + getpriority = Linux + 96, + setpriority = Linux + 97, + profil = Linux + 98, + statfs = Linux + 99, + fstatfs = Linux + 100, + ioperm = Linux + 101, + socketcall = Linux + 102, + syslog = Linux + 103, + setitimer = Linux + 104, + getitimer = Linux + 105, + stat = Linux + 106, + lstat = Linux + 107, + fstat = Linux + 108, + iopl = Linux + 110, + vhangup = Linux + 111, + idle = Linux + 112, + vm86 = Linux + 113, + wait4 = Linux + 114, + swapoff = Linux + 115, + sysinfo = Linux + 116, + ipc = Linux + 117, + fsync = Linux + 118, + sigreturn = Linux + 119, + clone = Linux + 120, + setdomainname = Linux + 121, + uname = Linux + 122, + modify_ldt = Linux + 123, + adjtimex = Linux + 124, + mprotect = Linux + 125, + sigprocmask = Linux + 126, + create_module = Linux + 127, + init_module = Linux + 128, + delete_module = Linux + 129, + get_kernel_syms = Linux + 130, + quotactl = Linux + 131, + getpgid = Linux + 132, + fchdir = Linux + 133, + bdflush = Linux + 134, + sysfs = Linux + 135, + personality = Linux + 136, + afs_syscall = Linux + 137, + setfsuid = Linux + 138, + setfsgid = Linux + 139, + _llseek = Linux + 140, + getdents = Linux + 141, + _newselect = Linux + 142, + flock = Linux + 143, + msync = Linux + 144, + readv = Linux + 145, + writev = Linux + 146, + cacheflush = Linux + 147, + cachectl = Linux + 148, + sysmips = Linux + 149, + getsid = Linux + 151, + fdatasync = Linux + 152, + _sysctl = Linux + 153, + mlock = Linux + 154, + munlock = Linux + 155, + mlockall = Linux + 156, + munlockall = Linux + 157, + sched_setparam = Linux + 158, + sched_getparam = Linux + 159, + sched_setscheduler = Linux + 160, + sched_getscheduler = Linux + 161, + sched_yield = Linux + 162, + sched_get_priority_max = Linux + 163, + sched_get_priority_min = Linux + 164, + sched_rr_get_interval = Linux + 165, + nanosleep = Linux + 166, + mremap = Linux + 167, + accept = Linux + 168, + bind = Linux + 169, + connect = Linux + 170, + getpeername = Linux + 171, + getsockname = Linux + 172, + getsockopt = Linux + 173, + listen = Linux + 174, + recv = Linux + 175, + recvfrom = Linux + 176, + recvmsg = Linux + 177, + send = Linux + 178, + sendmsg = Linux + 179, + sendto = Linux + 180, + setsockopt = Linux + 181, + shutdown = Linux + 182, + socket = Linux + 183, + socketpair = Linux + 184, + setresuid = Linux + 185, + getresuid = Linux + 186, + query_module = Linux + 187, + poll = Linux + 188, + nfsservctl = Linux + 189, + setresgid = Linux + 190, + getresgid = Linux + 191, + prctl = Linux + 192, + rt_sigreturn = Linux + 193, + rt_sigaction = Linux + 194, + rt_sigprocmask = Linux + 195, + rt_sigpending = Linux + 196, + rt_sigtimedwait = Linux + 197, + rt_sigqueueinfo = Linux + 198, + rt_sigsuspend = Linux + 199, + pread64 = Linux + 200, + pwrite64 = Linux + 201, + chown = Linux + 202, + getcwd = Linux + 203, + capget = Linux + 204, + capset = Linux + 205, + sigaltstack = Linux + 206, + sendfile = Linux + 207, + getpmsg = Linux + 208, + putpmsg = Linux + 209, + mmap2 = Linux + 210, + truncate64 = Linux + 211, + ftruncate64 = Linux + 212, + stat64 = Linux + 213, + lstat64 = Linux + 214, + fstat64 = Linux + 215, + pivot_root = Linux + 216, + mincore = Linux + 217, + madvise = Linux + 218, + getdents64 = Linux + 219, + fcntl64 = Linux + 220, + reserved221 = Linux + 221, + gettid = Linux + 222, + readahead = Linux + 223, + setxattr = Linux + 224, + lsetxattr = Linux + 225, + fsetxattr = Linux + 226, + getxattr = Linux + 227, + lgetxattr = Linux + 228, + fgetxattr = Linux + 229, + listxattr = Linux + 230, + llistxattr = Linux + 231, + flistxattr = Linux + 232, + removexattr = Linux + 233, + lremovexattr = Linux + 234, + fremovexattr = Linux + 235, + tkill = Linux + 236, + sendfile64 = Linux + 237, + futex = Linux + 238, + sched_setaffinity = Linux + 239, + sched_getaffinity = Linux + 240, + io_setup = Linux + 241, + io_destroy = Linux + 242, + io_getevents = Linux + 243, + io_submit = Linux + 244, + io_cancel = Linux + 245, + exit_group = Linux + 246, + lookup_dcookie = Linux + 247, + epoll_create = Linux + 248, + epoll_ctl = Linux + 249, + epoll_wait = Linux + 250, + remap_file_pages = Linux + 251, + set_tid_address = Linux + 252, + restart_syscall = Linux + 253, + fadvise64 = Linux + 254, + statfs64 = Linux + 255, + fstatfs64 = Linux + 256, + timer_create = Linux + 257, + timer_settime = Linux + 258, + timer_gettime = Linux + 259, + timer_getoverrun = Linux + 260, + timer_delete = Linux + 261, + clock_settime = Linux + 262, + clock_gettime = Linux + 263, + clock_getres = Linux + 264, + clock_nanosleep = Linux + 265, + tgkill = Linux + 266, + utimes = Linux + 267, + mbind = Linux + 268, + get_mempolicy = Linux + 269, + set_mempolicy = Linux + 270, + mq_open = Linux + 271, + mq_unlink = Linux + 272, + mq_timedsend = Linux + 273, + mq_timedreceive = Linux + 274, + mq_notify = Linux + 275, + mq_getsetattr = Linux + 276, + vserver = Linux + 277, + waitid = Linux + 278, + add_key = Linux + 280, + request_key = Linux + 281, + keyctl = Linux + 282, + set_thread_area = Linux + 283, + inotify_init = Linux + 284, + inotify_add_watch = Linux + 285, + inotify_rm_watch = Linux + 286, + migrate_pages = Linux + 287, + openat = Linux + 288, + mkdirat = Linux + 289, + mknodat = Linux + 290, + fchownat = Linux + 291, + futimesat = Linux + 292, + fstatat64 = Linux + 293, + unlinkat = Linux + 294, + renameat = Linux + 295, + linkat = Linux + 296, + symlinkat = Linux + 297, + readlinkat = Linux + 298, + fchmodat = Linux + 299, + faccessat = Linux + 300, + pselect6 = Linux + 301, + ppoll = Linux + 302, + unshare = Linux + 303, + splice = Linux + 304, + sync_file_range = Linux + 305, + tee = Linux + 306, + vmsplice = Linux + 307, + move_pages = Linux + 308, + set_robust_list = Linux + 309, + get_robust_list = Linux + 310, + kexec_load = Linux + 311, + getcpu = Linux + 312, + epoll_pwait = Linux + 313, + ioprio_set = Linux + 314, + ioprio_get = Linux + 315, + utimensat = Linux + 316, + signalfd = Linux + 317, + timerfd = Linux + 318, + eventfd = Linux + 319, + fallocate = Linux + 320, + timerfd_create = Linux + 321, + timerfd_gettime = Linux + 322, + timerfd_settime = Linux + 323, + signalfd4 = Linux + 324, + eventfd2 = Linux + 325, + epoll_create1 = Linux + 326, + dup3 = Linux + 327, + pipe2 = Linux + 328, + inotify_init1 = Linux + 329, + preadv = Linux + 330, + pwritev = Linux + 331, + rt_tgsigqueueinfo = Linux + 332, + perf_event_open = Linux + 333, + accept4 = Linux + 334, + recvmmsg = Linux + 335, + fanotify_init = Linux + 336, + fanotify_mark = Linux + 337, + prlimit64 = Linux + 338, + name_to_handle_at = Linux + 339, + open_by_handle_at = Linux + 340, + clock_adjtime = Linux + 341, + syncfs = Linux + 342, + sendmmsg = Linux + 343, + setns = Linux + 344, + process_vm_readv = Linux + 345, + process_vm_writev = Linux + 346, + kcmp = Linux + 347, + finit_module = Linux + 348, + sched_setattr = Linux + 349, + sched_getattr = Linux + 350, + renameat2 = Linux + 351, + seccomp = Linux + 352, + getrandom = Linux + 353, + memfd_create = Linux + 354, + bpf = Linux + 355, + execveat = Linux + 356, + userfaultfd = Linux + 357, + membarrier = Linux + 358, + mlock2 = Linux + 359, + copy_file_range = Linux + 360, + preadv2 = Linux + 361, + pwritev2 = Linux + 362, + pkey_mprotect = Linux + 363, + pkey_alloc = Linux + 364, + pkey_free = Linux + 365, + statx = Linux + 366, + rseq = Linux + 367, + io_pgetevents = Linux + 368, + semget = Linux + 393, + semctl = Linux + 394, + shmget = Linux + 395, + shmctl = Linux + 396, + shmat = Linux + 397, + shmdt = Linux + 398, + msgget = Linux + 399, + msgsnd = Linux + 400, + msgrcv = Linux + 401, + msgctl = Linux + 402, + clock_gettime64 = Linux + 403, + clock_settime64 = Linux + 404, + clock_adjtime64 = Linux + 405, + clock_getres_time64 = Linux + 406, + clock_nanosleep_time64 = Linux + 407, + timer_gettime64 = Linux + 408, + timer_settime64 = Linux + 409, + timerfd_gettime64 = Linux + 410, + timerfd_settime64 = Linux + 411, + utimensat_time64 = Linux + 412, + pselect6_time64 = Linux + 413, + ppoll_time64 = Linux + 414, + io_pgetevents_time64 = Linux + 416, + recvmmsg_time64 = Linux + 417, + mq_timedsend_time64 = Linux + 418, + mq_timedreceive_time64 = Linux + 419, + semtimedop_time64 = Linux + 420, + rt_sigtimedwait_time64 = Linux + 421, + futex_time64 = Linux + 422, + sched_rr_get_interval_time64 = Linux + 423, + pidfd_send_signal = Linux + 424, + io_uring_setup = Linux + 425, + io_uring_enter = Linux + 426, + io_uring_register = Linux + 427, + open_tree = Linux + 428, + move_mount = Linux + 429, + fsopen = Linux + 430, + fsconfig = Linux + 431, + fsmount = Linux + 432, + fspick = Linux + 433, + pidfd_open = Linux + 434, + clone3 = Linux + 435, + close_range = Linux + 436, + openat2 = Linux + 437, + pidfd_getfd = Linux + 438, + faccessat2 = Linux + 439, + process_madvise = Linux + 440, + epoll_pwait2 = Linux + 441, + mount_setattr = Linux + 442, + quotactl_fd = Linux + 443, + landlock_create_ruleset = Linux + 444, + landlock_add_rule = Linux + 445, + landlock_restrict_self = Linux + 446, + process_mrelease = Linux + 448, + futex_waitv = Linux + 449, + set_mempolicy_home_node = Linux + 450, +}; + +pub const PowerPC = enum(usize) { + restart_syscall = 0, + exit = 1, + fork = 2, + read = 3, + write = 4, + open = 5, + close = 6, + waitpid = 7, + creat = 8, + link = 9, + unlink = 10, + execve = 11, + chdir = 12, + time = 13, + mknod = 14, + chmod = 15, + lchown = 16, + @"break" = 17, + oldstat = 18, + lseek = 19, + getpid = 20, + mount = 21, + umount = 22, + setuid = 23, + getuid = 24, + stime = 25, + ptrace = 26, + alarm = 27, + oldfstat = 28, + pause = 29, + utime = 30, + stty = 31, + gtty = 32, + access = 33, + nice = 34, + ftime = 35, + sync = 36, + kill = 37, + rename = 38, + mkdir = 39, + rmdir = 40, + dup = 41, + pipe = 42, + times = 43, + prof = 44, + brk = 45, + setgid = 46, + getgid = 47, + signal = 48, + geteuid = 49, + getegid = 50, + acct = 51, + umount2 = 52, + lock = 53, + ioctl = 54, + fcntl = 55, + mpx = 56, + setpgid = 57, + ulimit = 58, + oldolduname = 59, + umask = 60, + chroot = 61, + ustat = 62, + dup2 = 63, + getppid = 64, + getpgrp = 65, + setsid = 66, + sigaction = 67, + sgetmask = 68, + ssetmask = 69, + setreuid = 70, + setregid = 71, + sigsuspend = 72, + sigpending = 73, + sethostname = 74, + setrlimit = 75, + getrlimit = 76, + getrusage = 77, + gettimeofday = 78, + settimeofday = 79, + getgroups = 80, + setgroups = 81, + select = 82, + symlink = 83, + oldlstat = 84, + readlink = 85, + uselib = 86, + swapon = 87, + reboot = 88, + readdir = 89, + mmap = 90, + munmap = 91, + truncate = 92, + ftruncate = 93, + fchmod = 94, + fchown = 95, + getpriority = 96, + setpriority = 97, + profil = 98, + statfs = 99, + fstatfs = 100, + ioperm = 101, + socketcall = 102, + syslog = 103, + setitimer = 104, + getitimer = 105, + stat = 106, + lstat = 107, + fstat = 108, + olduname = 109, + iopl = 110, + vhangup = 111, + idle = 112, + vm86 = 113, + wait4 = 114, + swapoff = 115, + sysinfo = 116, + ipc = 117, + fsync = 118, + sigreturn = 119, + clone = 120, + setdomainname = 121, + uname = 122, + modify_ldt = 123, + adjtimex = 124, + mprotect = 125, + sigprocmask = 126, + create_module = 127, + init_module = 128, + delete_module = 129, + get_kernel_syms = 130, + quotactl = 131, + getpgid = 132, + fchdir = 133, + bdflush = 134, + sysfs = 135, + personality = 136, + afs_syscall = 137, + setfsuid = 138, + setfsgid = 139, + _llseek = 140, + getdents = 141, + _newselect = 142, + flock = 143, + msync = 144, + readv = 145, + writev = 146, + getsid = 147, + fdatasync = 148, + _sysctl = 149, + mlock = 150, + munlock = 151, + mlockall = 152, + munlockall = 153, + sched_setparam = 154, + sched_getparam = 155, + sched_setscheduler = 156, + sched_getscheduler = 157, + sched_yield = 158, + sched_get_priority_max = 159, + sched_get_priority_min = 160, + sched_rr_get_interval = 161, + nanosleep = 162, + mremap = 163, + setresuid = 164, + getresuid = 165, + query_module = 166, + poll = 167, + nfsservctl = 168, + setresgid = 169, + getresgid = 170, + prctl = 171, + rt_sigreturn = 172, + rt_sigaction = 173, + rt_sigprocmask = 174, + rt_sigpending = 175, + rt_sigtimedwait = 176, + rt_sigqueueinfo = 177, + rt_sigsuspend = 178, + pread64 = 179, + pwrite64 = 180, + chown = 181, + getcwd = 182, + capget = 183, + capset = 184, + sigaltstack = 185, + sendfile = 186, + getpmsg = 187, + putpmsg = 188, + vfork = 189, + ugetrlimit = 190, + readahead = 191, + mmap2 = 192, + truncate64 = 193, + ftruncate64 = 194, + stat64 = 195, + lstat64 = 196, + fstat64 = 197, + pciconfig_read = 198, + pciconfig_write = 199, + pciconfig_iobase = 200, + multiplexer = 201, + getdents64 = 202, + pivot_root = 203, + fcntl64 = 204, + madvise = 205, + mincore = 206, + gettid = 207, + tkill = 208, + setxattr = 209, + lsetxattr = 210, + fsetxattr = 211, + getxattr = 212, + lgetxattr = 213, + fgetxattr = 214, + listxattr = 215, + llistxattr = 216, + flistxattr = 217, + removexattr = 218, + lremovexattr = 219, + fremovexattr = 220, + futex = 221, + sched_setaffinity = 222, + sched_getaffinity = 223, + tuxcall = 225, + sendfile64 = 226, + io_setup = 227, + io_destroy = 228, + io_getevents = 229, + io_submit = 230, + io_cancel = 231, + set_tid_address = 232, + fadvise64 = 233, + exit_group = 234, + lookup_dcookie = 235, + epoll_create = 236, + epoll_ctl = 237, + epoll_wait = 238, + remap_file_pages = 239, + timer_create = 240, + timer_settime = 241, + timer_gettime = 242, + timer_getoverrun = 243, + timer_delete = 244, + clock_settime = 245, + clock_gettime = 246, + clock_getres = 247, + clock_nanosleep = 248, + swapcontext = 249, + tgkill = 250, + utimes = 251, + statfs64 = 252, + fstatfs64 = 253, + fadvise64_64 = 254, + rtas = 255, + sys_debug_setcontext = 256, + migrate_pages = 258, + mbind = 259, + get_mempolicy = 260, + set_mempolicy = 261, + mq_open = 262, + mq_unlink = 263, + mq_timedsend = 264, + mq_timedreceive = 265, + mq_notify = 266, + mq_getsetattr = 267, + kexec_load = 268, + add_key = 269, + request_key = 270, + keyctl = 271, + waitid = 272, + ioprio_set = 273, + ioprio_get = 274, + inotify_init = 275, + inotify_add_watch = 276, + inotify_rm_watch = 277, + spu_run = 278, + spu_create = 279, + pselect6 = 280, + ppoll = 281, + unshare = 282, + splice = 283, + tee = 284, + vmsplice = 285, + openat = 286, + mkdirat = 287, + mknodat = 288, + fchownat = 289, + futimesat = 290, + fstatat64 = 291, + unlinkat = 292, + renameat = 293, + linkat = 294, + symlinkat = 295, + readlinkat = 296, + fchmodat = 297, + faccessat = 298, + get_robust_list = 299, + set_robust_list = 300, + move_pages = 301, + getcpu = 302, + epoll_pwait = 303, + utimensat = 304, + signalfd = 305, + timerfd_create = 306, + eventfd = 307, + sync_file_range = 308, + fallocate = 309, + subpage_prot = 310, + timerfd_settime = 311, + timerfd_gettime = 312, + signalfd4 = 313, + eventfd2 = 314, + epoll_create1 = 315, + dup3 = 316, + pipe2 = 317, + inotify_init1 = 318, + perf_event_open = 319, + preadv = 320, + pwritev = 321, + rt_tgsigqueueinfo = 322, + fanotify_init = 323, + fanotify_mark = 324, + prlimit64 = 325, + socket = 326, + bind = 327, + connect = 328, + listen = 329, + accept = 330, + getsockname = 331, + getpeername = 332, + socketpair = 333, + send = 334, + sendto = 335, + recv = 336, + recvfrom = 337, + shutdown = 338, + setsockopt = 339, + getsockopt = 340, + sendmsg = 341, + recvmsg = 342, + recvmmsg = 343, + accept4 = 344, + name_to_handle_at = 345, + open_by_handle_at = 346, + clock_adjtime = 347, + syncfs = 348, + sendmmsg = 349, + setns = 350, + process_vm_readv = 351, + process_vm_writev = 352, + finit_module = 353, + kcmp = 354, + sched_setattr = 355, + sched_getattr = 356, + renameat2 = 357, + seccomp = 358, + getrandom = 359, + memfd_create = 360, + bpf = 361, + execveat = 362, + switch_endian = 363, + userfaultfd = 364, + membarrier = 365, + mlock2 = 378, + copy_file_range = 379, + preadv2 = 380, + pwritev2 = 381, + kexec_file_load = 382, + statx = 383, + pkey_alloc = 384, + pkey_free = 385, + pkey_mprotect = 386, + rseq = 387, + io_pgetevents = 388, + semget = 393, + semctl = 394, + shmget = 395, + shmctl = 396, + shmat = 397, + shmdt = 398, + msgget = 399, + msgsnd = 400, + msgrcv = 401, + msgctl = 402, + clock_gettime64 = 403, + clock_settime64 = 404, + clock_adjtime64 = 405, + clock_getres_time64 = 406, + clock_nanosleep_time64 = 407, + timer_gettime64 = 408, + timer_settime64 = 409, + timerfd_gettime64 = 410, + timerfd_settime64 = 411, + utimensat_time64 = 412, + pselect6_time64 = 413, + ppoll_time64 = 414, + io_pgetevents_time64 = 416, + recvmmsg_time64 = 417, + mq_timedsend_time64 = 418, + mq_timedreceive_time64 = 419, + semtimedop_time64 = 420, + rt_sigtimedwait_time64 = 421, + futex_time64 = 422, + sched_rr_get_interval_time64 = 423, + pidfd_send_signal = 424, + io_uring_setup = 425, + io_uring_enter = 426, + io_uring_register = 427, + open_tree = 428, + move_mount = 429, + fsopen = 430, + fsconfig = 431, + fsmount = 432, + fspick = 433, + pidfd_open = 434, + clone3 = 435, + close_range = 436, + openat2 = 437, + pidfd_getfd = 438, + faccessat2 = 439, + process_madvise = 440, + epoll_pwait2 = 441, + mount_setattr = 442, + quotactl_fd = 443, + landlock_create_ruleset = 444, + landlock_add_rule = 445, + landlock_restrict_self = 446, + process_mrelease = 448, + futex_waitv = 449, + set_mempolicy_home_node = 450, +}; + +pub const PowerPC64 = enum(usize) { + restart_syscall = 0, + exit = 1, + fork = 2, + read = 3, + write = 4, + open = 5, + close = 6, + waitpid = 7, + creat = 8, + link = 9, + unlink = 10, + execve = 11, + chdir = 12, + time = 13, + mknod = 14, + chmod = 15, + lchown = 16, + @"break" = 17, + oldstat = 18, + lseek = 19, + getpid = 20, + mount = 21, + umount = 22, + setuid = 23, + getuid = 24, + stime = 25, + ptrace = 26, + alarm = 27, + oldfstat = 28, + pause = 29, + utime = 30, + stty = 31, + gtty = 32, + access = 33, + nice = 34, + ftime = 35, + sync = 36, + kill = 37, + rename = 38, + mkdir = 39, + rmdir = 40, + dup = 41, + pipe = 42, + times = 43, + prof = 44, + brk = 45, + setgid = 46, + getgid = 47, + signal = 48, + geteuid = 49, + getegid = 50, + acct = 51, + umount2 = 52, + lock = 53, + ioctl = 54, + fcntl = 55, + mpx = 56, + setpgid = 57, + ulimit = 58, + oldolduname = 59, + umask = 60, + chroot = 61, + ustat = 62, + dup2 = 63, + getppid = 64, + getpgrp = 65, + setsid = 66, + sigaction = 67, + sgetmask = 68, + ssetmask = 69, + setreuid = 70, + setregid = 71, + sigsuspend = 72, + sigpending = 73, + sethostname = 74, + setrlimit = 75, + getrlimit = 76, + getrusage = 77, + gettimeofday = 78, + settimeofday = 79, + getgroups = 80, + setgroups = 81, + select = 82, + symlink = 83, + oldlstat = 84, + readlink = 85, + uselib = 86, + swapon = 87, + reboot = 88, + readdir = 89, + mmap = 90, + munmap = 91, + truncate = 92, + ftruncate = 93, + fchmod = 94, + fchown = 95, + getpriority = 96, + setpriority = 97, + profil = 98, + statfs = 99, + fstatfs = 100, + ioperm = 101, + socketcall = 102, + syslog = 103, + setitimer = 104, + getitimer = 105, + stat = 106, + lstat = 107, + fstat = 108, + olduname = 109, + iopl = 110, + vhangup = 111, + idle = 112, + vm86 = 113, + wait4 = 114, + swapoff = 115, + sysinfo = 116, + ipc = 117, + fsync = 118, + sigreturn = 119, + clone = 120, + setdomainname = 121, + uname = 122, + modify_ldt = 123, + adjtimex = 124, + mprotect = 125, + sigprocmask = 126, + create_module = 127, + init_module = 128, + delete_module = 129, + get_kernel_syms = 130, + quotactl = 131, + getpgid = 132, + fchdir = 133, + bdflush = 134, + sysfs = 135, + personality = 136, + afs_syscall = 137, + setfsuid = 138, + setfsgid = 139, + _llseek = 140, + getdents = 141, + _newselect = 142, + flock = 143, + msync = 144, + readv = 145, + writev = 146, + getsid = 147, + fdatasync = 148, + _sysctl = 149, + mlock = 150, + munlock = 151, + mlockall = 152, + munlockall = 153, + sched_setparam = 154, + sched_getparam = 155, + sched_setscheduler = 156, + sched_getscheduler = 157, + sched_yield = 158, + sched_get_priority_max = 159, + sched_get_priority_min = 160, + sched_rr_get_interval = 161, + nanosleep = 162, + mremap = 163, + setresuid = 164, + getresuid = 165, + query_module = 166, + poll = 167, + nfsservctl = 168, + setresgid = 169, + getresgid = 170, + prctl = 171, + rt_sigreturn = 172, + rt_sigaction = 173, + rt_sigprocmask = 174, + rt_sigpending = 175, + rt_sigtimedwait = 176, + rt_sigqueueinfo = 177, + rt_sigsuspend = 178, + pread64 = 179, + pwrite64 = 180, + chown = 181, + getcwd = 182, + capget = 183, + capset = 184, + sigaltstack = 185, + sendfile = 186, + getpmsg = 187, + putpmsg = 188, + vfork = 189, + ugetrlimit = 190, + readahead = 191, + pciconfig_read = 198, + pciconfig_write = 199, + pciconfig_iobase = 200, + multiplexer = 201, + getdents64 = 202, + pivot_root = 203, + madvise = 205, + mincore = 206, + gettid = 207, + tkill = 208, + setxattr = 209, + lsetxattr = 210, + fsetxattr = 211, + getxattr = 212, + lgetxattr = 213, + fgetxattr = 214, + listxattr = 215, + llistxattr = 216, + flistxattr = 217, + removexattr = 218, + lremovexattr = 219, + fremovexattr = 220, + futex = 221, + sched_setaffinity = 222, + sched_getaffinity = 223, + tuxcall = 225, + io_setup = 227, + io_destroy = 228, + io_getevents = 229, + io_submit = 230, + io_cancel = 231, + set_tid_address = 232, + fadvise64 = 233, + exit_group = 234, + lookup_dcookie = 235, + epoll_create = 236, + epoll_ctl = 237, + epoll_wait = 238, + remap_file_pages = 239, + timer_create = 240, + timer_settime = 241, + timer_gettime = 242, + timer_getoverrun = 243, + timer_delete = 244, + clock_settime = 245, + clock_gettime = 246, + clock_getres = 247, + clock_nanosleep = 248, + swapcontext = 249, + tgkill = 250, + utimes = 251, + statfs64 = 252, + fstatfs64 = 253, + rtas = 255, + sys_debug_setcontext = 256, + migrate_pages = 258, + mbind = 259, + get_mempolicy = 260, + set_mempolicy = 261, + mq_open = 262, + mq_unlink = 263, + mq_timedsend = 264, + mq_timedreceive = 265, + mq_notify = 266, + mq_getsetattr = 267, + kexec_load = 268, + add_key = 269, + request_key = 270, + keyctl = 271, + waitid = 272, + ioprio_set = 273, + ioprio_get = 274, + inotify_init = 275, + inotify_add_watch = 276, + inotify_rm_watch = 277, + spu_run = 278, + spu_create = 279, + pselect6 = 280, + ppoll = 281, + unshare = 282, + splice = 283, + tee = 284, + vmsplice = 285, + openat = 286, + mkdirat = 287, + mknodat = 288, + fchownat = 289, + futimesat = 290, + fstatat64 = 291, + unlinkat = 292, + renameat = 293, + linkat = 294, + symlinkat = 295, + readlinkat = 296, + fchmodat = 297, + faccessat = 298, + get_robust_list = 299, + set_robust_list = 300, + move_pages = 301, + getcpu = 302, + epoll_pwait = 303, + utimensat = 304, + signalfd = 305, + timerfd_create = 306, + eventfd = 307, + sync_file_range = 308, + fallocate = 309, + subpage_prot = 310, + timerfd_settime = 311, + timerfd_gettime = 312, + signalfd4 = 313, + eventfd2 = 314, + epoll_create1 = 315, + dup3 = 316, + pipe2 = 317, + inotify_init1 = 318, + perf_event_open = 319, + preadv = 320, + pwritev = 321, + rt_tgsigqueueinfo = 322, + fanotify_init = 323, + fanotify_mark = 324, + prlimit64 = 325, + socket = 326, + bind = 327, + connect = 328, + listen = 329, + accept = 330, + getsockname = 331, + getpeername = 332, + socketpair = 333, + send = 334, + sendto = 335, + recv = 336, + recvfrom = 337, + shutdown = 338, + setsockopt = 339, + getsockopt = 340, + sendmsg = 341, + recvmsg = 342, + recvmmsg = 343, + accept4 = 344, + name_to_handle_at = 345, + open_by_handle_at = 346, + clock_adjtime = 347, + syncfs = 348, + sendmmsg = 349, + setns = 350, + process_vm_readv = 351, + process_vm_writev = 352, + finit_module = 353, + kcmp = 354, + sched_setattr = 355, + sched_getattr = 356, + renameat2 = 357, + seccomp = 358, + getrandom = 359, + memfd_create = 360, + bpf = 361, + execveat = 362, + switch_endian = 363, + userfaultfd = 364, + membarrier = 365, + mlock2 = 378, + copy_file_range = 379, + preadv2 = 380, + pwritev2 = 381, + kexec_file_load = 382, + statx = 383, + pkey_alloc = 384, + pkey_free = 385, + pkey_mprotect = 386, + rseq = 387, + io_pgetevents = 388, + semtimedop = 392, + semget = 393, + semctl = 394, + shmget = 395, + shmctl = 396, + shmat = 397, + shmdt = 398, + msgget = 399, + msgsnd = 400, + msgrcv = 401, + msgctl = 402, + pidfd_send_signal = 424, + io_uring_setup = 425, + io_uring_enter = 426, + io_uring_register = 427, + open_tree = 428, + move_mount = 429, + fsopen = 430, + fsconfig = 431, + fsmount = 432, + fspick = 433, + pidfd_open = 434, + clone3 = 435, + close_range = 436, + openat2 = 437, + pidfd_getfd = 438, + faccessat2 = 439, + process_madvise = 440, + epoll_pwait2 = 441, + mount_setattr = 442, + quotactl_fd = 443, + landlock_create_ruleset = 444, + landlock_add_rule = 445, + landlock_restrict_self = 446, + process_mrelease = 448, + futex_waitv = 449, + set_mempolicy_home_node = 450, +}; + +pub const Arm64 = enum(usize) { + io_setup = 0, + io_destroy = 1, + io_submit = 2, + io_cancel = 3, + io_getevents = 4, + setxattr = 5, + lsetxattr = 6, + fsetxattr = 7, + getxattr = 8, + lgetxattr = 9, + fgetxattr = 10, + listxattr = 11, + llistxattr = 12, + flistxattr = 13, + removexattr = 14, + lremovexattr = 15, + fremovexattr = 16, + getcwd = 17, + lookup_dcookie = 18, + eventfd2 = 19, + epoll_create1 = 20, + epoll_ctl = 21, + epoll_pwait = 22, + dup = 23, + dup3 = 24, + fcntl = 25, + inotify_init1 = 26, + inotify_add_watch = 27, + inotify_rm_watch = 28, + ioctl = 29, + ioprio_set = 30, + ioprio_get = 31, + flock = 32, + mknodat = 33, + mkdirat = 34, + unlinkat = 35, + symlinkat = 36, + linkat = 37, + renameat = 38, + umount2 = 39, + mount = 40, + pivot_root = 41, + nfsservctl = 42, + statfs = 43, + fstatfs = 44, + truncate = 45, + ftruncate = 46, + fallocate = 47, + faccessat = 48, + chdir = 49, + fchdir = 50, + chroot = 51, + fchmod = 52, + fchmodat = 53, + fchownat = 54, + fchown = 55, + openat = 56, + close = 57, + vhangup = 58, + pipe2 = 59, + quotactl = 60, + getdents64 = 61, + lseek = 62, + read = 63, + write = 64, + readv = 65, + writev = 66, + pread64 = 67, + pwrite64 = 68, + preadv = 69, + pwritev = 70, + sendfile = 71, + pselect6 = 72, + ppoll = 73, + signalfd4 = 74, + vmsplice = 75, + splice = 76, + tee = 77, + readlinkat = 78, + fstatat = 79, + fstat = 80, + sync = 81, + fsync = 82, + fdatasync = 83, + sync_file_range = 84, + timerfd_create = 85, + timerfd_settime = 86, + timerfd_gettime = 87, + utimensat = 88, + acct = 89, + capget = 90, + capset = 91, + personality = 92, + exit = 93, + exit_group = 94, + waitid = 95, + set_tid_address = 96, + unshare = 97, + futex = 98, + set_robust_list = 99, + get_robust_list = 100, + nanosleep = 101, + getitimer = 102, + setitimer = 103, + kexec_load = 104, + init_module = 105, + delete_module = 106, + timer_create = 107, + timer_gettime = 108, + timer_getoverrun = 109, + timer_settime = 110, + timer_delete = 111, + clock_settime = 112, + clock_gettime = 113, + clock_getres = 114, + clock_nanosleep = 115, + syslog = 116, + ptrace = 117, + sched_setparam = 118, + sched_setscheduler = 119, + sched_getscheduler = 120, + sched_getparam = 121, + sched_setaffinity = 122, + sched_getaffinity = 123, + sched_yield = 124, + sched_get_priority_max = 125, + sched_get_priority_min = 126, + sched_rr_get_interval = 127, + restart_syscall = 128, + kill = 129, + tkill = 130, + tgkill = 131, + sigaltstack = 132, + rt_sigsuspend = 133, + rt_sigaction = 134, + rt_sigprocmask = 135, + rt_sigpending = 136, + rt_sigtimedwait = 137, + rt_sigqueueinfo = 138, + rt_sigreturn = 139, + setpriority = 140, + getpriority = 141, + reboot = 142, + setregid = 143, + setgid = 144, + setreuid = 145, + setuid = 146, + setresuid = 147, + getresuid = 148, + setresgid = 149, + getresgid = 150, + setfsuid = 151, + setfsgid = 152, + times = 153, + setpgid = 154, + getpgid = 155, + getsid = 156, + setsid = 157, + getgroups = 158, + setgroups = 159, + uname = 160, + sethostname = 161, + setdomainname = 162, + getrlimit = 163, + setrlimit = 164, + getrusage = 165, + umask = 166, + prctl = 167, + getcpu = 168, + gettimeofday = 169, + settimeofday = 170, + adjtimex = 171, + getpid = 172, + getppid = 173, + getuid = 174, + geteuid = 175, + getgid = 176, + getegid = 177, + gettid = 178, + sysinfo = 179, + mq_open = 180, + mq_unlink = 181, + mq_timedsend = 182, + mq_timedreceive = 183, + mq_notify = 184, + mq_getsetattr = 185, + msgget = 186, + msgctl = 187, + msgrcv = 188, + msgsnd = 189, + semget = 190, + semctl = 191, + semtimedop = 192, + semop = 193, + shmget = 194, + shmctl = 195, + shmat = 196, + shmdt = 197, + socket = 198, + socketpair = 199, + bind = 200, + listen = 201, + accept = 202, + connect = 203, + getsockname = 204, + getpeername = 205, + sendto = 206, + recvfrom = 207, + setsockopt = 208, + getsockopt = 209, + shutdown = 210, + sendmsg = 211, + recvmsg = 212, + readahead = 213, + brk = 214, + munmap = 215, + mremap = 216, + add_key = 217, + request_key = 218, + keyctl = 219, + clone = 220, + execve = 221, + mmap = 222, + fadvise64 = 223, + swapon = 224, + swapoff = 225, + mprotect = 226, + msync = 227, + mlock = 228, + munlock = 229, + mlockall = 230, + munlockall = 231, + mincore = 232, + madvise = 233, + remap_file_pages = 234, + mbind = 235, + get_mempolicy = 236, + set_mempolicy = 237, + migrate_pages = 238, + move_pages = 239, + rt_tgsigqueueinfo = 240, + perf_event_open = 241, + accept4 = 242, + recvmmsg = 243, + wait4 = 260, + prlimit64 = 261, + fanotify_init = 262, + fanotify_mark = 263, + name_to_handle_at = 264, + open_by_handle_at = 265, + clock_adjtime = 266, + syncfs = 267, + setns = 268, + sendmmsg = 269, + process_vm_readv = 270, + process_vm_writev = 271, + kcmp = 272, + finit_module = 273, + sched_setattr = 274, + sched_getattr = 275, + renameat2 = 276, + seccomp = 277, + getrandom = 278, + memfd_create = 279, + bpf = 280, + execveat = 281, + userfaultfd = 282, + membarrier = 283, + mlock2 = 284, + copy_file_range = 285, + preadv2 = 286, + pwritev2 = 287, + pkey_mprotect = 288, + pkey_alloc = 289, + pkey_free = 290, + statx = 291, + io_pgetevents = 292, + rseq = 293, + kexec_file_load = 294, + pidfd_send_signal = 424, + io_uring_setup = 425, + io_uring_enter = 426, + io_uring_register = 427, + open_tree = 428, + move_mount = 429, + fsopen = 430, + fsconfig = 431, + fsmount = 432, + fspick = 433, + pidfd_open = 434, + clone3 = 435, + close_range = 436, + openat2 = 437, + pidfd_getfd = 438, + faccessat2 = 439, + process_madvise = 440, + epoll_pwait2 = 441, + mount_setattr = 442, + quotactl_fd = 443, + landlock_create_ruleset = 444, + landlock_add_rule = 445, + landlock_restrict_self = 446, + memfd_secret = 447, + process_mrelease = 448, + futex_waitv = 449, + set_mempolicy_home_node = 450, +}; + +pub const RiscV64 = enum(usize) { + pub const arch_specific_syscall = 244; + + io_setup = 0, + io_destroy = 1, + io_submit = 2, + io_cancel = 3, + io_getevents = 4, + setxattr = 5, + lsetxattr = 6, + fsetxattr = 7, + getxattr = 8, + lgetxattr = 9, + fgetxattr = 10, + listxattr = 11, + llistxattr = 12, + flistxattr = 13, + removexattr = 14, + lremovexattr = 15, + fremovexattr = 16, + getcwd = 17, + lookup_dcookie = 18, + eventfd2 = 19, + epoll_create1 = 20, + epoll_ctl = 21, + epoll_pwait = 22, + dup = 23, + dup3 = 24, + fcntl = 25, + inotify_init1 = 26, + inotify_add_watch = 27, + inotify_rm_watch = 28, + ioctl = 29, + ioprio_set = 30, + ioprio_get = 31, + flock = 32, + mknodat = 33, + mkdirat = 34, + unlinkat = 35, + symlinkat = 36, + linkat = 37, + umount2 = 39, + mount = 40, + pivot_root = 41, + nfsservctl = 42, + statfs = 43, + fstatfs = 44, + truncate = 45, + ftruncate = 46, + fallocate = 47, + faccessat = 48, + chdir = 49, + fchdir = 50, + chroot = 51, + fchmod = 52, + fchmodat = 53, + fchownat = 54, + fchown = 55, + openat = 56, + close = 57, + vhangup = 58, + pipe2 = 59, + quotactl = 60, + getdents64 = 61, + lseek = 62, + read = 63, + write = 64, + readv = 65, + writev = 66, + pread64 = 67, + pwrite64 = 68, + preadv = 69, + pwritev = 70, + sendfile = 71, + pselect6 = 72, + ppoll = 73, + signalfd4 = 74, + vmsplice = 75, + splice = 76, + tee = 77, + readlinkat = 78, + fstatat = 79, + fstat = 80, + sync = 81, + fsync = 82, + fdatasync = 83, + sync_file_range = 84, + timerfd_create = 85, + timerfd_settime = 86, + timerfd_gettime = 87, + utimensat = 88, + acct = 89, + capget = 90, + capset = 91, + personality = 92, + exit = 93, + exit_group = 94, + waitid = 95, + set_tid_address = 96, + unshare = 97, + futex = 98, + set_robust_list = 99, + get_robust_list = 100, + nanosleep = 101, + getitimer = 102, + setitimer = 103, + kexec_load = 104, + init_module = 105, + delete_module = 106, + timer_create = 107, + timer_gettime = 108, + timer_getoverrun = 109, + timer_settime = 110, + timer_delete = 111, + clock_settime = 112, + clock_gettime = 113, + clock_getres = 114, + clock_nanosleep = 115, + syslog = 116, + ptrace = 117, + sched_setparam = 118, + sched_setscheduler = 119, + sched_getscheduler = 120, + sched_getparam = 121, + sched_setaffinity = 122, + sched_getaffinity = 123, + sched_yield = 124, + sched_get_priority_max = 125, + sched_get_priority_min = 126, + sched_rr_get_interval = 127, + restart_syscall = 128, + kill = 129, + tkill = 130, + tgkill = 131, + sigaltstack = 132, + rt_sigsuspend = 133, + rt_sigaction = 134, + rt_sigprocmask = 135, + rt_sigpending = 136, + rt_sigtimedwait = 137, + rt_sigqueueinfo = 138, + rt_sigreturn = 139, + setpriority = 140, + getpriority = 141, + reboot = 142, + setregid = 143, + setgid = 144, + setreuid = 145, + setuid = 146, + setresuid = 147, + getresuid = 148, + setresgid = 149, + getresgid = 150, + setfsuid = 151, + setfsgid = 152, + times = 153, + setpgid = 154, + getpgid = 155, + getsid = 156, + setsid = 157, + getgroups = 158, + setgroups = 159, + uname = 160, + sethostname = 161, + setdomainname = 162, + getrlimit = 163, + setrlimit = 164, + getrusage = 165, + umask = 166, + prctl = 167, + getcpu = 168, + gettimeofday = 169, + settimeofday = 170, + adjtimex = 171, + getpid = 172, + getppid = 173, + getuid = 174, + geteuid = 175, + getgid = 176, + getegid = 177, + gettid = 178, + sysinfo = 179, + mq_open = 180, + mq_unlink = 181, + mq_timedsend = 182, + mq_timedreceive = 183, + mq_notify = 184, + mq_getsetattr = 185, + msgget = 186, + msgctl = 187, + msgrcv = 188, + msgsnd = 189, + semget = 190, + semctl = 191, + semtimedop = 192, + semop = 193, + shmget = 194, + shmctl = 195, + shmat = 196, + shmdt = 197, + socket = 198, + socketpair = 199, + bind = 200, + listen = 201, + accept = 202, + connect = 203, + getsockname = 204, + getpeername = 205, + sendto = 206, + recvfrom = 207, + setsockopt = 208, + getsockopt = 209, + shutdown = 210, + sendmsg = 211, + recvmsg = 212, + readahead = 213, + brk = 214, + munmap = 215, + mremap = 216, + add_key = 217, + request_key = 218, + keyctl = 219, + clone = 220, + execve = 221, + mmap = 222, + fadvise64 = 223, + swapon = 224, + swapoff = 225, + mprotect = 226, + msync = 227, + mlock = 228, + munlock = 229, + mlockall = 230, + munlockall = 231, + mincore = 232, + madvise = 233, + remap_file_pages = 234, + mbind = 235, + get_mempolicy = 236, + set_mempolicy = 237, + migrate_pages = 238, + move_pages = 239, + rt_tgsigqueueinfo = 240, + perf_event_open = 241, + accept4 = 242, + recvmmsg = 243, + wait4 = 260, + prlimit64 = 261, + fanotify_init = 262, + fanotify_mark = 263, + name_to_handle_at = 264, + open_by_handle_at = 265, + clock_adjtime = 266, + syncfs = 267, + setns = 268, + sendmmsg = 269, + process_vm_readv = 270, + process_vm_writev = 271, + kcmp = 272, + finit_module = 273, + sched_setattr = 274, + sched_getattr = 275, + renameat2 = 276, + seccomp = 277, + getrandom = 278, + memfd_create = 279, + bpf = 280, + execveat = 281, + userfaultfd = 282, + membarrier = 283, + mlock2 = 284, + copy_file_range = 285, + preadv2 = 286, + pwritev2 = 287, + pkey_mprotect = 288, + pkey_alloc = 289, + pkey_free = 290, + statx = 291, + io_pgetevents = 292, + rseq = 293, + kexec_file_load = 294, + pidfd_send_signal = 424, + io_uring_setup = 425, + io_uring_enter = 426, + io_uring_register = 427, + open_tree = 428, + move_mount = 429, + fsopen = 430, + fsconfig = 431, + fsmount = 432, + fspick = 433, + pidfd_open = 434, + clone3 = 435, + close_range = 436, + openat2 = 437, + pidfd_getfd = 438, + faccessat2 = 439, + process_madvise = 440, + epoll_pwait2 = 441, + mount_setattr = 442, + quotactl_fd = 443, + landlock_create_ruleset = 444, + landlock_add_rule = 445, + landlock_restrict_self = 446, + process_mrelease = 448, + futex_waitv = 449, + set_mempolicy_home_node = 450, + + riscv_flush_icache = arch_specific_syscall + 15, +}; diff --git a/lib/std/os/linux/x86_64.zig b/lib/std/os/linux/x86_64.zig index fa09919094..60fbb29557 100644 --- a/lib/std/os/linux/x86_64.zig +++ b/lib/std/os/linux/x86_64.zig @@ -1,6 +1,7 @@ const std = @import("../../std.zig"); const maxInt = std.math.maxInt; const linux = std.os.linux; +const SYS = linux.SYS; const iovec = std.os.iovec; const iovec_const = std.os.iovec_const; @@ -123,369 +124,6 @@ pub const nlink_t = usize; pub const blksize_t = isize; pub const blkcnt_t = isize; -pub const SYS = enum(usize) { - read = 0, - write = 1, - open = 2, - close = 3, - stat = 4, - fstat = 5, - lstat = 6, - poll = 7, - lseek = 8, - mmap = 9, - mprotect = 10, - munmap = 11, - brk = 12, - rt_sigaction = 13, - rt_sigprocmask = 14, - rt_sigreturn = 15, - ioctl = 16, - pread = 17, - pwrite = 18, - readv = 19, - writev = 20, - access = 21, - pipe = 22, - select = 23, - sched_yield = 24, - mremap = 25, - msync = 26, - mincore = 27, - madvise = 28, - shmget = 29, - shmat = 30, - shmctl = 31, - dup = 32, - dup2 = 33, - pause = 34, - nanosleep = 35, - getitimer = 36, - alarm = 37, - setitimer = 38, - getpid = 39, - sendfile = 40, - socket = 41, - connect = 42, - accept = 43, - sendto = 44, - recvfrom = 45, - sendmsg = 46, - recvmsg = 47, - shutdown = 48, - bind = 49, - listen = 50, - getsockname = 51, - getpeername = 52, - socketpair = 53, - setsockopt = 54, - getsockopt = 55, - clone = 56, - fork = 57, - vfork = 58, - execve = 59, - exit = 60, - wait4 = 61, - kill = 62, - uname = 63, - semget = 64, - semop = 65, - semctl = 66, - shmdt = 67, - msgget = 68, - msgsnd = 69, - msgrcv = 70, - msgctl = 71, - fcntl = 72, - flock = 73, - fsync = 74, - fdatasync = 75, - truncate = 76, - ftruncate = 77, - getdents = 78, - getcwd = 79, - chdir = 80, - fchdir = 81, - rename = 82, - mkdir = 83, - rmdir = 84, - creat = 85, - link = 86, - unlink = 87, - symlink = 88, - readlink = 89, - chmod = 90, - fchmod = 91, - chown = 92, - fchown = 93, - lchown = 94, - umask = 95, - gettimeofday = 96, - getrlimit = 97, - getrusage = 98, - sysinfo = 99, - times = 100, - ptrace = 101, - getuid = 102, - syslog = 103, - getgid = 104, - setuid = 105, - setgid = 106, - geteuid = 107, - getegid = 108, - setpgid = 109, - getppid = 110, - getpgrp = 111, - setsid = 112, - setreuid = 113, - setregid = 114, - getgroups = 115, - setgroups = 116, - setresuid = 117, - getresuid = 118, - setresgid = 119, - getresgid = 120, - getpgid = 121, - setfsuid = 122, - setfsgid = 123, - getsid = 124, - capget = 125, - capset = 126, - rt_sigpending = 127, - rt_sigtimedwait = 128, - rt_sigqueueinfo = 129, - rt_sigsuspend = 130, - sigaltstack = 131, - utime = 132, - mknod = 133, - uselib = 134, - personality = 135, - ustat = 136, - statfs = 137, - fstatfs = 138, - sysfs = 139, - getpriority = 140, - setpriority = 141, - sched_setparam = 142, - sched_getparam = 143, - sched_setscheduler = 144, - sched_getscheduler = 145, - sched_get_priority_max = 146, - sched_get_priority_min = 147, - sched_rr_get_interval = 148, - mlock = 149, - munlock = 150, - mlockall = 151, - munlockall = 152, - vhangup = 153, - modify_ldt = 154, - pivot_root = 155, - _sysctl = 156, - prctl = 157, - arch_prctl = 158, - adjtimex = 159, - setrlimit = 160, - chroot = 161, - sync = 162, - acct = 163, - settimeofday = 164, - mount = 165, - umount2 = 166, - swapon = 167, - swapoff = 168, - reboot = 169, - sethostname = 170, - setdomainname = 171, - iopl = 172, - ioperm = 173, - create_module = 174, - init_module = 175, - delete_module = 176, - get_kernel_syms = 177, - query_module = 178, - quotactl = 179, - nfsservctl = 180, - getpmsg = 181, - putpmsg = 182, - afs_syscall = 183, - tuxcall = 184, - security = 185, - gettid = 186, - readahead = 187, - setxattr = 188, - lsetxattr = 189, - fsetxattr = 190, - getxattr = 191, - lgetxattr = 192, - fgetxattr = 193, - listxattr = 194, - llistxattr = 195, - flistxattr = 196, - removexattr = 197, - lremovexattr = 198, - fremovexattr = 199, - tkill = 200, - time = 201, - futex = 202, - sched_setaffinity = 203, - sched_getaffinity = 204, - set_thread_area = 205, - io_setup = 206, - io_destroy = 207, - io_getevents = 208, - io_submit = 209, - io_cancel = 210, - get_thread_area = 211, - lookup_dcookie = 212, - epoll_create = 213, - epoll_ctl_old = 214, - epoll_wait_old = 215, - remap_file_pages = 216, - getdents64 = 217, - set_tid_address = 218, - restart_syscall = 219, - semtimedop = 220, - fadvise64 = 221, - timer_create = 222, - timer_settime = 223, - timer_gettime = 224, - timer_getoverrun = 225, - timer_delete = 226, - clock_settime = 227, - clock_gettime = 228, - clock_getres = 229, - clock_nanosleep = 230, - exit_group = 231, - epoll_wait = 232, - epoll_ctl = 233, - tgkill = 234, - utimes = 235, - vserver = 236, - mbind = 237, - set_mempolicy = 238, - get_mempolicy = 239, - mq_open = 240, - mq_unlink = 241, - mq_timedsend = 242, - mq_timedreceive = 243, - mq_notify = 244, - mq_getsetattr = 245, - kexec_load = 246, - waitid = 247, - add_key = 248, - request_key = 249, - keyctl = 250, - ioprio_set = 251, - ioprio_get = 252, - inotify_init = 253, - inotify_add_watch = 254, - inotify_rm_watch = 255, - migrate_pages = 256, - openat = 257, - mkdirat = 258, - mknodat = 259, - fchownat = 260, - futimesat = 261, - fstatat = 262, - unlinkat = 263, - renameat = 264, - linkat = 265, - symlinkat = 266, - readlinkat = 267, - fchmodat = 268, - faccessat = 269, - pselect6 = 270, - ppoll = 271, - unshare = 272, - set_robust_list = 273, - get_robust_list = 274, - splice = 275, - tee = 276, - sync_file_range = 277, - vmsplice = 278, - move_pages = 279, - utimensat = 280, - epoll_pwait = 281, - signalfd = 282, - timerfd_create = 283, - eventfd = 284, - fallocate = 285, - timerfd_settime = 286, - timerfd_gettime = 287, - accept4 = 288, - signalfd4 = 289, - eventfd2 = 290, - epoll_create1 = 291, - dup3 = 292, - pipe2 = 293, - inotify_init1 = 294, - preadv = 295, - pwritev = 296, - rt_tgsigqueueinfo = 297, - perf_event_open = 298, - recvmmsg = 299, - fanotify_init = 300, - fanotify_mark = 301, - prlimit64 = 302, - name_to_handle_at = 303, - open_by_handle_at = 304, - clock_adjtime = 305, - syncfs = 306, - sendmmsg = 307, - setns = 308, - getcpu = 309, - process_vm_readv = 310, - process_vm_writev = 311, - kcmp = 312, - finit_module = 313, - sched_setattr = 314, - sched_getattr = 315, - renameat2 = 316, - seccomp = 317, - getrandom = 318, - memfd_create = 319, - kexec_file_load = 320, - bpf = 321, - execveat = 322, - userfaultfd = 323, - membarrier = 324, - mlock2 = 325, - copy_file_range = 326, - preadv2 = 327, - pwritev2 = 328, - pkey_mprotect = 329, - pkey_alloc = 330, - pkey_free = 331, - statx = 332, - io_pgetevents = 333, - rseq = 334, - pidfd_send_signal = 424, - io_uring_setup = 425, - io_uring_enter = 426, - io_uring_register = 427, - open_tree = 428, - move_mount = 429, - fsopen = 430, - fsconfig = 431, - fsmount = 432, - fspick = 433, - pidfd_open = 434, - clone3 = 435, - close_range = 436, - openat2 = 437, - pidfd_getfd = 438, - faccessat2 = 439, - process_madvise = 440, - epoll_pwait2 = 441, - mount_setattr = 442, - landlock_create_ruleset = 444, - landlock_add_rule = 445, - landlock_restrict_self = 446, - memfd_secret = 447, - - _, -}; - pub const O = struct { pub const CREAT = 0o100; pub const EXCL = 0o200; diff --git a/tools/generate_linux_syscalls.zig b/tools/generate_linux_syscalls.zig new file mode 100644 index 0000000000..67f098ac4f --- /dev/null +++ b/tools/generate_linux_syscalls.zig @@ -0,0 +1,356 @@ +//! To get started, run this tool with no args and read the help message. +//! +//! This tool extracts the Linux syscall numbers from the Linux source tree +//! directly, and emits an enumerated list per supported Zig arch. + +const std = @import("std"); +const mem = std.mem; +const fmt = std.fmt; +const zig = std.zig; +const fs = std.fs; + +const stdlib_renames = std.ComptimeStringMap([]const u8, .{ + // Most 64-bit archs. + .{ "newfstatat", "fstatat64" }, + // POWER. + .{ "sync_file_range2", "sync_file_range" }, + // ARM EABI/Thumb. + .{ "arm_sync_file_range", "sync_file_range" }, + .{ "arm_fadvise64_64", "fadvise64_64" }, +}); + +pub fn main() !void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + + const args = try std.process.argsAlloc(allocator); + if (args.len < 3 or mem.eql(u8, args[1], "--help")) + usageAndExit(std.io.getStdErr(), args[0], 1); + const zig_exe = args[1]; + const linux_path = args[2]; + + var buf_out = std.io.bufferedWriter(std.io.getStdOut().writer()); + const writer = buf_out.writer(); + + // As of 5.17.1, the largest table is 23467 bytes. + // 32k should be enough for now. + var buf = try allocator.alloc(u8, 1 << 15); + const linux_dir = try std.fs.openDirAbsolute(linux_path, .{}); + + try writer.writeAll( + \\// This file is automatically generated. + \\// See tools/generate_linux_syscalls.zig for more info. + \\ + \\ + ); + + // These architectures have their syscall definitions generated from a TSV + // file, processed via scripts/syscallhdr.sh. + { + try writer.writeAll("pub const X86 = enum(usize) {\n"); + + const table = try linux_dir.readFile("arch/x86/entry/syscalls/syscall_32.tbl", buf); + var lines = mem.tokenize(u8, table, "\n"); + while (lines.next()) |line| { + if (line[0] == '#') continue; + + var fields = mem.tokenize(u8, line, " \t"); + const number = fields.next() orelse return error.Incomplete; + // abi is always i386 + _ = fields.next() orelse return error.Incomplete; + const name = fields.next() orelse return error.Incomplete; + + try writer.print(" {s} = {s},\n", .{ zig.fmtId(name), number }); + } + + try writer.writeAll("};\n\n"); + } + { + try writer.writeAll("pub const X64 = enum(usize) {\n"); + + const table = try linux_dir.readFile("arch/x86/entry/syscalls/syscall_64.tbl", buf); + var lines = mem.tokenize(u8, table, "\n"); + while (lines.next()) |line| { + if (line[0] == '#') continue; + + var fields = mem.tokenize(u8, line, " \t"); + const number = fields.next() orelse return error.Incomplete; + const abi = fields.next() orelse return error.Incomplete; + // The x32 abi syscalls are always at the end. + if (mem.eql(u8, abi, "x32")) break; + const name = fields.next() orelse return error.Incomplete; + + const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name; + try writer.print(" {s} = {s},\n", .{ zig.fmtId(fixed_name), number }); + } + + try writer.writeAll("};\n\n"); + } + { + try writer.writeAll( + \\pub const Arm = enum(usize) { + \\ const arm_base = 0x0f0000; + \\ + \\ + ); + + const table = try linux_dir.readFile("arch/arm/tools/syscall.tbl", buf); + var lines = mem.tokenize(u8, table, "\n"); + while (lines.next()) |line| { + if (line[0] == '#') continue; + + var fields = mem.tokenize(u8, line, " \t"); + const number = fields.next() orelse return error.Incomplete; + const abi = fields.next() orelse return error.Incomplete; + if (mem.eql(u8, abi, "oabi")) continue; + const name = fields.next() orelse return error.Incomplete; + + const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name; + try writer.print(" {s} = {s},\n", .{ zig.fmtId(fixed_name), number }); + } + + // TODO: maybe extract these from arch/arm/include/uapi/asm/unistd.h + try writer.writeAll( + \\ + \\ breakpoint = arm_base + 1, + \\ cacheflush = arm_base + 2, + \\ usr26 = arm_base + 3, + \\ usr32 = arm_base + 4, + \\ set_tls = arm_base + 5, + \\ get_tls = arm_base + 6, + \\}; + \\ + \\ + ); + } + { + try writer.writeAll("pub const Sparc64 = enum(usize) {\n"); + const table = try linux_dir.readFile("arch/sparc/kernel/syscalls/syscall.tbl", buf); + var lines = mem.tokenize(u8, table, "\n"); + while (lines.next()) |line| { + if (line[0] == '#') continue; + + var fields = mem.tokenize(u8, line, " \t"); + const number = fields.next() orelse return error.Incomplete; + const abi = fields.next() orelse return error.Incomplete; + if (mem.eql(u8, abi, "32")) continue; + const name = fields.next() orelse return error.Incomplete; + + try writer.print(" {s} = {s},\n", .{ zig.fmtId(name), number }); + } + + try writer.writeAll("};\n\n"); + } + { + try writer.writeAll( + \\pub const Mips = enum(usize) { + \\ pub const Linux = 4000; + \\ + \\ + ); + + const table = try linux_dir.readFile("arch/mips/kernel/syscalls/syscall_o32.tbl", buf); + var lines = mem.tokenize(u8, table, "\n"); + while (lines.next()) |line| { + if (line[0] == '#') continue; + + var fields = mem.tokenize(u8, line, " \t"); + const number = fields.next() orelse return error.Incomplete; + // abi is always o32 + _ = fields.next() orelse return error.Incomplete; + const name = fields.next() orelse return error.Incomplete; + if (mem.startsWith(u8, name, "unused")) continue; + + try writer.print(" {s} = Linux + {s},\n", .{ zig.fmtId(name), number }); + } + + try writer.writeAll("};\n\n"); + } + { + try writer.writeAll("pub const PowerPC = enum(usize) {\n"); + + const table = try linux_dir.readFile("arch/powerpc/kernel/syscalls/syscall.tbl", buf); + var list_64 = std.ArrayList(u8).init(allocator); + var lines = mem.tokenize(u8, table, "\n"); + while (lines.next()) |line| { + if (line[0] == '#') continue; + + var fields = mem.tokenize(u8, line, " \t"); + const number = fields.next() orelse return error.Incomplete; + const abi = fields.next() orelse return error.Incomplete; + const name = fields.next() orelse return error.Incomplete; + const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name; + + if (mem.eql(u8, abi, "spu")) { + continue; + } else if (mem.eql(u8, abi, "32")) { + try writer.print(" {s} = {s},\n", .{ zig.fmtId(fixed_name), number }); + } else if (mem.eql(u8, abi, "64")) { + try list_64.writer().print(" {s} = {s},\n", .{ zig.fmtId(fixed_name), number }); + } else { // common/nospu + try writer.print(" {s} = {s},\n", .{ zig.fmtId(fixed_name), number }); + try list_64.writer().print(" {s} = {s},\n", .{ zig.fmtId(fixed_name), number }); + } + } + + try writer.writeAll( + \\}; + \\ + \\pub const PowerPC64 = enum(usize) { + \\ + ); + try writer.writeAll(list_64.items); + try writer.writeAll("};\n\n"); + } + + // Newer architectures (starting with aarch64 c. 2012) now use the same C + // header file for their syscall numbers. Arch-specific headers are used to + // define pre-proc. vars that add additional (usually obsolete) syscalls. + // + // TODO: + // - It would be better to use libclang/translate-c directly to extract the definitions. + // - The `-dD` option only does minimal pre-processing and doesn't resolve addition, + // so arch specific syscalls are dealt with manually. + { + try writer.writeAll("pub const Arm64 = enum(usize) {\n"); + + const child_args = [_][]const u8{ + zig_exe, + "cc", + "-target", + "aarch64-linux-gnu", + "-E", + // -dM is cleaner, but -dD preserves iteration order. + "-dD", + // No need for line-markers. + "-P", + "-nostdinc", + // Using -I=[dir] includes the zig linux headers, which we don't want. + "-Iinclude", + "-Iinclude/uapi", + "arch/arm64/include/uapi/asm/unistd.h", + }; + + const child_result = try std.ChildProcess.exec(.{ + .allocator = allocator, + .argv = &child_args, + .cwd = linux_path, + .cwd_dir = linux_dir, + .max_output_bytes = 20 * 1024, + }); + if (child_result.stderr.len > 0) std.debug.print("{s}\n", .{child_result.stderr}); + + const defines = switch (child_result.term) { + .Exited => |code| if (code == 0) child_result.stdout else { + std.debug.print("zig cc exited with code {d}\n", .{code}); + std.process.exit(1); + }, + else => { + std.debug.print("zig cc crashed\n", .{}); + std.process.exit(1); + }, + }; + + var lines = mem.tokenize(u8, defines, "\n"); + loop: while (lines.next()) |line| { + var fields = mem.tokenize(u8, line, " \t"); + const cmd = fields.next() orelse return error.Incomplete; + if (!mem.eql(u8, cmd, "#define")) continue; + const define = fields.next() orelse return error.Incomplete; + const number = fields.next() orelse continue; + + if (!std.ascii.isDigit(number[0])) continue; + if (!mem.startsWith(u8, define, "__NR")) continue; + const name = mem.trimLeft(u8, mem.trimLeft(u8, define, "__NR3264_"), "__NR_"); + if (mem.eql(u8, name, "arch_specific_syscall")) continue; + if (mem.eql(u8, name, "syscalls")) break :loop; + + const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name; + try writer.print(" {s} = {s},\n", .{ zig.fmtId(fixed_name), number }); + } + + try writer.writeAll("};\n\n"); + } + { + try writer.writeAll( + \\pub const RiscV64 = enum(usize) { + \\ pub const arch_specific_syscall = 244; + \\ + \\ + ); + + const child_args = [_][]const u8{ + zig_exe, + "cc", + "-target", + "riscv64-linux-gnu", + "-E", + "-dD", + "-P", + "-nostdinc", + "-Iinclude", + "-Iinclude/uapi", + "arch/riscv/include/uapi/asm/unistd.h", + }; + + const child_result = try std.ChildProcess.exec(.{ + .allocator = allocator, + .argv = &child_args, + .cwd = linux_path, + .cwd_dir = linux_dir, + .max_output_bytes = 20 * 1024, + }); + if (child_result.stderr.len > 0) std.debug.print("{s}\n", .{child_result.stderr}); + + const defines = switch (child_result.term) { + .Exited => |code| if (code == 0) child_result.stdout else { + std.debug.print("zig cc exited with code {d}\n", .{code}); + std.process.exit(1); + }, + else => { + std.debug.print("zig cc crashed\n", .{}); + std.process.exit(1); + }, + }; + + var lines = mem.tokenize(u8, defines, "\n"); + loop: while (lines.next()) |line| { + var fields = mem.tokenize(u8, line, " \t"); + const cmd = fields.next() orelse return error.Incomplete; + if (!mem.eql(u8, cmd, "#define")) continue; + const define = fields.next() orelse return error.Incomplete; + const number = fields.next() orelse continue; + + if (!std.ascii.isDigit(number[0])) continue; + if (!mem.startsWith(u8, define, "__NR")) continue; + const name = mem.trimLeft(u8, mem.trimLeft(u8, define, "__NR3264_"), "__NR_"); + if (mem.eql(u8, name, "arch_specific_syscall")) continue; + if (mem.eql(u8, name, "syscalls")) break :loop; + + const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name; + try writer.print(" {s} = {s},\n", .{ zig.fmtId(fixed_name), number }); + } + + try writer.writeAll( + \\ + \\ riscv_flush_icache = arch_specific_syscall + 15, + \\}; + \\ + ); + } + + try buf_out.flush(); +} + +fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn { + file.writer().print( + \\Usage: {s} /path/to/zig /path/to/linux + \\Alternative Usage: zig run /path/to/git/zig/tools/generate_linux_syscalls.zig -- /path/to/zig /path/to/linux + \\ + \\Generates the list of Linux syscalls for each supported cpu arch, using the Linux development tree. + \\Prints to stdout Zig code which you can use to replace the file lib/std/os/linux/syscalls.zig. + \\ + , .{arg0}) catch std.process.exit(1); + std.process.exit(code); +} From 552ef5f2e4ecc4b788476be6836d262aaf3216d3 Mon Sep 17 00:00:00 2001 From: aiotter Date: Sat, 7 May 2022 10:10:21 +0900 Subject: [PATCH 1551/2031] std.c: Implement dirent on std/c/linux.zig --- lib/std/c/linux.zig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/std/c/linux.zig b/lib/std/c/linux.zig index 5f96fe3fe0..325ef3a894 100644 --- a/lib/std/c/linux.zig +++ b/lib/std/c/linux.zig @@ -364,3 +364,18 @@ pub const RTLD = struct { pub const GLOBAL = 256; pub const LOCAL = 0; }; + +pub const dirent = struct { + d_ino: c_uint, + d_off: c_uint, + d_reclen: c_ushort, + d_type: u8, + d_name: [256]u8, +}; +pub const dirent64 = struct { + d_ino: c_ulong, + d_off: c_ulong, + d_reclen: c_ushort, + d_type: u8, + d_name: [256]u8, +}; From f3517a1aa6058e9b09d57e81ecc81f27f086d163 Mon Sep 17 00:00:00 2001 From: YeonJiKun Date: Fri, 13 May 2022 12:23:05 +0900 Subject: [PATCH 1552/2031] zig test: Add proper detection for Windows console --- lib/test_runner.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/test_runner.zig b/lib/test_runner.zig index 5ae05464f2..dd88063fea 100644 --- a/lib/test_runner.zig +++ b/lib/test_runner.zig @@ -37,7 +37,8 @@ pub fn main() void { .dont_print_on_dumb = true, }; const root_node = progress.start("Test", test_fn_list.len); - const have_tty = progress.terminal != null and progress.supports_ansi_escape_codes; + const have_tty = progress.terminal != null and + (progress.supports_ansi_escape_codes or progress.is_windows_terminal); var async_frame_buffer: []align(std.Target.stack_align) u8 = undefined; // TODO this is on the next line (using `undefined` above) because otherwise zig incorrectly From ceeec8d19f36bf0a2cb132d35c0e119c4fec476d Mon Sep 17 00:00:00 2001 From: alice Date: Tue, 17 May 2022 21:41:57 +0100 Subject: [PATCH 1553/2031] Simplify `signbit` --- lib/std/math/signbit.zig | 69 +++++++--------------------------------- 1 file changed, 12 insertions(+), 57 deletions(-) diff --git a/lib/std/math/signbit.zig b/lib/std/math/signbit.zig index a72d6ab119..9aab487d37 100644 --- a/lib/std/math/signbit.zig +++ b/lib/std/math/signbit.zig @@ -5,64 +5,19 @@ const expect = std.testing.expect; /// Returns whether x is negative or negative 0. pub fn signbit(x: anytype) bool { const T = @TypeOf(x); - return switch (T) { - f16 => signbit16(x), - f32 => signbit32(x), - f64 => signbit64(x), - f80 => signbit80(x), - f128 => signbit128(x), - else => @compileError("signbit not implemented for " ++ @typeName(T)), - }; -} - -fn signbit16(x: f16) bool { - const bits = @bitCast(u16, x); - return bits >> 15 != 0; -} - -fn signbit32(x: f32) bool { - const bits = @bitCast(u32, x); - return bits >> 31 != 0; -} - -fn signbit64(x: f64) bool { - const bits = @bitCast(u64, x); - return bits >> 63 != 0; -} - -fn signbit80(x: f80) bool { - const bits = @bitCast(u80, x); - return bits >> 79 != 0; -} - -fn signbit128(x: f128) bool { - const bits = @bitCast(u128, x); - return bits >> 127 != 0; + const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); + return @bitCast(TBits, x) >> (@bitSizeOf(T) - 1) != 0; } test "math.signbit" { - try expect(signbit(@as(f16, 4.0)) == signbit16(4.0)); - try expect(signbit(@as(f32, 4.0)) == signbit32(4.0)); - try expect(signbit(@as(f64, 4.0)) == signbit64(4.0)); - try expect(signbit(@as(f128, 4.0)) == signbit128(4.0)); -} - -test "math.signbit16" { - try expect(!signbit16(4.0)); - try expect(signbit16(-3.0)); -} - -test "math.signbit32" { - try expect(!signbit32(4.0)); - try expect(signbit32(-3.0)); -} - -test "math.signbit64" { - try expect(!signbit64(4.0)); - try expect(signbit64(-3.0)); -} - -test "math.signbit128" { - try expect(!signbit128(4.0)); - try expect(signbit128(-3.0)); + inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { + try expect(!signbit(@as(T, 0.0))); + try expect(!signbit(@as(T, 1.0))); + try expect(signbit(@as(T, -2.0))); + try expect(signbit(@as(T, -0.0))); + try expect(!signbit(math.inf(T))); + try expect(signbit(-math.inf(T))); + try expect(!signbit(math.nan(T))); + try expect(signbit(-math.nan(T))); + } } From 70b6b98e91fb9d1d5575da4d59c5c523864a8f0e Mon Sep 17 00:00:00 2001 From: alice Date: Tue, 17 May 2022 21:55:22 +0100 Subject: [PATCH 1554/2031] Simplify `Copysign` --- lib/compiler_rt/fma.zig | 4 +- lib/std/math/atanh.zig | 4 +- lib/std/math/complex/cosh.zig | 20 +++---- lib/std/math/complex/proj.zig | 2 +- lib/std/math/complex/sinh.zig | 16 +++--- lib/std/math/complex/sqrt.zig | 12 ++-- lib/std/math/complex/tanh.zig | 8 +-- lib/std/math/copysign.zig | 101 ++++++---------------------------- lib/std/math/pow.zig | 2 +- 9 files changed, 51 insertions(+), 118 deletions(-) diff --git a/lib/compiler_rt/fma.zig b/lib/compiler_rt/fma.zig index 7a39a4c9a0..b121db212c 100644 --- a/lib/compiler_rt/fma.zig +++ b/lib/compiler_rt/fma.zig @@ -57,7 +57,7 @@ pub fn fma(x: f64, y: f64, z: f64) callconv(.C) f64 { if (spread <= 53 * 2) { zs = math.scalbn(zs, -spread); } else { - zs = math.copysign(f64, math.floatMin(f64), zs); + zs = math.copysign(math.floatMin(f64), zs); } const xy = dd_mul(xs, ys); @@ -116,7 +116,7 @@ pub fn fmaq(x: f128, y: f128, z: f128) callconv(.C) f128 { if (spread <= 113 * 2) { zs = math.scalbn(zs, -spread); } else { - zs = math.copysign(f128, math.floatMin(f128), zs); + zs = math.copysign(math.floatMin(f128), zs); } const xy = dd_mul128(xs, ys); diff --git a/lib/std/math/atanh.zig b/lib/std/math/atanh.zig index 2c27234cd2..aed5d8bca8 100644 --- a/lib/std/math/atanh.zig +++ b/lib/std/math/atanh.zig @@ -33,7 +33,7 @@ fn atanh_32(x: f32) f32 { var y = @bitCast(f32, i); // |x| if (y == 1.0) { - return math.copysign(f32, math.inf(f32), x); + return math.copysign(math.inf(f32), x); } if (u < 0x3F800000 - (1 << 23)) { @@ -62,7 +62,7 @@ fn atanh_64(x: f64) f64 { var y = @bitCast(f64, u & (maxInt(u64) >> 1)); // |x| if (y == 1.0) { - return math.copysign(f64, math.inf(f64), x); + return math.copysign(math.inf(f64), x); } if (e < 0x3FF - 1) { diff --git a/lib/std/math/complex/cosh.zig b/lib/std/math/complex/cosh.zig index 65cfc4a528..b3ffab5175 100644 --- a/lib/std/math/complex/cosh.zig +++ b/lib/std/math/complex/cosh.zig @@ -45,13 +45,13 @@ fn cosh32(z: Complex(f32)) Complex(f32) { if (ix < 0x42b17218) { // x < 88.7: exp(|x|) won't overflow const h = @exp(@fabs(x)) * 0.5; - return Complex(f32).init(math.copysign(f32, h, x) * @cos(y), h * @sin(y)); + return Complex(f32).init(math.copysign(h, x) * @cos(y), h * @sin(y)); } // x < 192.7: scale to avoid overflow else if (ix < 0x4340b1e7) { const v = Complex(f32).init(@fabs(x), y); const r = ldexp_cexp(v, -1); - return Complex(f32).init(r.re, r.im * math.copysign(f32, 1, x)); + return Complex(f32).init(r.re, r.im * math.copysign(@as(f32, 1.0), x)); } // x >= 192.7: result always overflows else { @@ -61,14 +61,14 @@ fn cosh32(z: Complex(f32)) Complex(f32) { } if (ix == 0 and iy >= 0x7f800000) { - return Complex(f32).init(y - y, math.copysign(f32, 0, x * (y - y))); + return Complex(f32).init(y - y, math.copysign(@as(f32, 0.0), x * (y - y))); } if (iy == 0 and ix >= 0x7f800000) { if (hx & 0x7fffff == 0) { - return Complex(f32).init(x * x, math.copysign(f32, 0, x) * y); + return Complex(f32).init(x * x, math.copysign(@as(f32, 0.0), x) * y); } - return Complex(f32).init(x, math.copysign(f32, 0, (x + x) * y)); + return Complex(f32).init(x, math.copysign(@as(f32, 0.0), (x + x) * y)); } if (ix < 0x7f800000 and iy >= 0x7f800000) { @@ -113,13 +113,13 @@ fn cosh64(z: Complex(f64)) Complex(f64) { if (ix < 0x40862e42) { // x < 710: exp(|x|) won't overflow const h = @exp(@fabs(x)) * 0.5; - return Complex(f64).init(h * @cos(y), math.copysign(f64, h, x) * @sin(y)); + return Complex(f64).init(h * @cos(y), math.copysign(h, x) * @sin(y)); } // x < 1455: scale to avoid overflow else if (ix < 0x4096bbaa) { const v = Complex(f64).init(@fabs(x), y); const r = ldexp_cexp(v, -1); - return Complex(f64).init(r.re, r.im * math.copysign(f64, 1, x)); + return Complex(f64).init(r.re, r.im * math.copysign(@as(f64, 1.0), x)); } // x >= 1455: result always overflows else { @@ -129,14 +129,14 @@ fn cosh64(z: Complex(f64)) Complex(f64) { } if (ix | lx == 0 and iy >= 0x7ff00000) { - return Complex(f64).init(y - y, math.copysign(f64, 0, x * (y - y))); + return Complex(f64).init(y - y, math.copysign(@as(f64, 0.0), x * (y - y))); } if (iy | ly == 0 and ix >= 0x7ff00000) { if ((hx & 0xfffff) | lx == 0) { - return Complex(f64).init(x * x, math.copysign(f64, 0, x) * y); + return Complex(f64).init(x * x, math.copysign(@as(f64, 0.0), x) * y); } - return Complex(f64).init(x * x, math.copysign(f64, 0, (x + x) * y)); + return Complex(f64).init(x * x, math.copysign(@as(f64, 0.0), (x + x) * y)); } if (ix < 0x7ff00000 and iy >= 0x7ff00000) { diff --git a/lib/std/math/complex/proj.zig b/lib/std/math/complex/proj.zig index 1e4b13f0df..0b4b7b7a17 100644 --- a/lib/std/math/complex/proj.zig +++ b/lib/std/math/complex/proj.zig @@ -9,7 +9,7 @@ pub fn proj(z: anytype) Complex(@TypeOf(z.re)) { const T = @TypeOf(z.re); if (math.isInf(z.re) or math.isInf(z.im)) { - return Complex(T).init(math.inf(T), math.copysign(T, 0, z.re)); + return Complex(T).init(math.inf(T), math.copysign(@as(T, 0.0), z.re)); } return Complex(T).init(z.re, z.im); diff --git a/lib/std/math/complex/sinh.zig b/lib/std/math/complex/sinh.zig index 1569565ecc..9afb7faf30 100644 --- a/lib/std/math/complex/sinh.zig +++ b/lib/std/math/complex/sinh.zig @@ -45,13 +45,13 @@ fn sinh32(z: Complex(f32)) Complex(f32) { if (ix < 0x42b17218) { // x < 88.7: exp(|x|) won't overflow const h = @exp(@fabs(x)) * 0.5; - return Complex(f32).init(math.copysign(f32, h, x) * @cos(y), h * @sin(y)); + return Complex(f32).init(math.copysign(h, x) * @cos(y), h * @sin(y)); } // x < 192.7: scale to avoid overflow else if (ix < 0x4340b1e7) { const v = Complex(f32).init(@fabs(x), y); const r = ldexp_cexp(v, -1); - return Complex(f32).init(r.re * math.copysign(f32, 1, x), r.im); + return Complex(f32).init(r.re * math.copysign(@as(f32, 1.0), x), r.im); } // x >= 192.7: result always overflows else { @@ -61,14 +61,14 @@ fn sinh32(z: Complex(f32)) Complex(f32) { } if (ix == 0 and iy >= 0x7f800000) { - return Complex(f32).init(math.copysign(f32, 0, x * (y - y)), y - y); + return Complex(f32).init(math.copysign(@as(f32, 0.0), x * (y - y)), y - y); } if (iy == 0 and ix >= 0x7f800000) { if (hx & 0x7fffff == 0) { return Complex(f32).init(x, y); } - return Complex(f32).init(x, math.copysign(f32, 0, y)); + return Complex(f32).init(x, math.copysign(@as(f32, 0.0), y)); } if (ix < 0x7f800000 and iy >= 0x7f800000) { @@ -112,13 +112,13 @@ fn sinh64(z: Complex(f64)) Complex(f64) { if (ix < 0x40862e42) { // x < 710: exp(|x|) won't overflow const h = @exp(@fabs(x)) * 0.5; - return Complex(f64).init(math.copysign(f64, h, x) * @cos(y), h * @sin(y)); + return Complex(f64).init(math.copysign(h, x) * @cos(y), h * @sin(y)); } // x < 1455: scale to avoid overflow else if (ix < 0x4096bbaa) { const v = Complex(f64).init(@fabs(x), y); const r = ldexp_cexp(v, -1); - return Complex(f64).init(r.re * math.copysign(f64, 1, x), r.im); + return Complex(f64).init(r.re * math.copysign(@as(f64, 1.0), x), r.im); } // x >= 1455: result always overflows else { @@ -128,14 +128,14 @@ fn sinh64(z: Complex(f64)) Complex(f64) { } if (ix | lx == 0 and iy >= 0x7ff00000) { - return Complex(f64).init(math.copysign(f64, 0, x * (y - y)), y - y); + return Complex(f64).init(math.copysign(@as(f64, 0.0), x * (y - y)), y - y); } if (iy | ly == 0 and ix >= 0x7ff00000) { if ((hx & 0xfffff) | lx == 0) { return Complex(f64).init(x, y); } - return Complex(f64).init(x, math.copysign(f64, 0, y)); + return Complex(f64).init(x, math.copysign(@as(f64, 0.0), y)); } if (ix < 0x7ff00000 and iy >= 0x7ff00000) { diff --git a/lib/std/math/complex/sqrt.zig b/lib/std/math/complex/sqrt.zig index ab24e2d60d..456d10aa85 100644 --- a/lib/std/math/complex/sqrt.zig +++ b/lib/std/math/complex/sqrt.zig @@ -43,9 +43,9 @@ fn sqrt32(z: Complex(f32)) Complex(f32) { // sqrt(-inf + i nan) = nan +- inf i // sqrt(-inf + iy) = 0 + inf i if (math.signbit(x)) { - return Complex(f32).init(@fabs(x - y), math.copysign(f32, x, y)); + return Complex(f32).init(@fabs(x - y), math.copysign(x, y)); } else { - return Complex(f32).init(x, math.copysign(f32, y - y, y)); + return Complex(f32).init(x, math.copysign(y - y, y)); } } @@ -65,7 +65,7 @@ fn sqrt32(z: Complex(f32)) Complex(f32) { const t = @sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5); return Complex(f32).init( @floatCast(f32, @fabs(y) / (2.0 * t)), - @floatCast(f32, math.copysign(f64, t, y)), + @floatCast(f32, math.copysign(t, y)), ); } } @@ -94,9 +94,9 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { // sqrt(-inf + i nan) = nan +- inf i // sqrt(-inf + iy) = 0 + inf i if (math.signbit(x)) { - return Complex(f64).init(@fabs(x - y), math.copysign(f64, x, y)); + return Complex(f64).init(@fabs(x - y), math.copysign(x, y)); } else { - return Complex(f64).init(x, math.copysign(f64, y - y, y)); + return Complex(f64).init(x, math.copysign(y - y, y)); } } @@ -116,7 +116,7 @@ fn sqrt64(z: Complex(f64)) Complex(f64) { result = Complex(f64).init(t, y / (2.0 * t)); } else { const t = @sqrt((-x + math.hypot(f64, x, y)) * 0.5); - result = Complex(f64).init(@fabs(y) / (2.0 * t), math.copysign(f64, t, y)); + result = Complex(f64).init(@fabs(y) / (2.0 * t), math.copysign(t, y)); } if (scale) { diff --git a/lib/std/math/complex/tanh.zig b/lib/std/math/complex/tanh.zig index 2ed2cb9609..92e197e308 100644 --- a/lib/std/math/complex/tanh.zig +++ b/lib/std/math/complex/tanh.zig @@ -34,7 +34,7 @@ fn tanh32(z: Complex(f32)) Complex(f32) { } const xx = @bitCast(f32, hx - 0x40000000); const r = if (math.isInf(y)) y else @sin(y) * @cos(y); - return Complex(f32).init(xx, math.copysign(f32, 0, r)); + return Complex(f32).init(xx, math.copysign(@as(f32, 0.0), r)); } if (!math.isFinite(y)) { @@ -45,7 +45,7 @@ fn tanh32(z: Complex(f32)) Complex(f32) { // x >= 11 if (ix >= 0x41300000) { const exp_mx = @exp(-@fabs(x)); - return Complex(f32).init(math.copysign(f32, 1, x), 4 * @sin(y) * @cos(y) * exp_mx * exp_mx); + return Complex(f32).init(math.copysign(@as(f32, 1.0), x), 4 * @sin(y) * @cos(y) * exp_mx * exp_mx); } // Kahan's algorithm @@ -77,7 +77,7 @@ fn tanh64(z: Complex(f64)) Complex(f64) { const xx = @bitCast(f64, (@as(u64, hx - 0x40000000) << 32) | lx); const r = if (math.isInf(y)) y else @sin(y) * @cos(y); - return Complex(f64).init(xx, math.copysign(f64, 0, r)); + return Complex(f64).init(xx, math.copysign(@as(f64, 0.0), r)); } if (!math.isFinite(y)) { @@ -88,7 +88,7 @@ fn tanh64(z: Complex(f64)) Complex(f64) { // x >= 22 if (ix >= 0x40360000) { const exp_mx = @exp(-@fabs(x)); - return Complex(f64).init(math.copysign(f64, 1, x), 4 * @sin(y) * @cos(y) * exp_mx * exp_mx); + return Complex(f64).init(math.copysign(@as(f64, 1.0), x), 4 * @sin(y) * @cos(y) * exp_mx * exp_mx); } // Kahan's algorithm diff --git a/lib/std/math/copysign.zig b/lib/std/math/copysign.zig index 72057c190d..b5fd6d4d9a 100644 --- a/lib/std/math/copysign.zig +++ b/lib/std/math/copysign.zig @@ -1,92 +1,25 @@ -// Ported from musl, which is licensed under the MIT license: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/copysignf.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/copysign.c - const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; -const maxInt = std.math.maxInt; -/// Returns a value with the magnitude of x and the sign of y. -pub fn copysign(comptime T: type, x: T, y: T) T { - return switch (T) { - f16 => copysign16(x, y), - f32 => copysign32(x, y), - f64 => copysign64(x, y), - f128 => copysign128(x, y), - else => @compileError("copysign not implemented for " ++ @typeName(T)), - }; -} - -fn copysign16(x: f16, y: f16) f16 { - const ux = @bitCast(u16, x); - const uy = @bitCast(u16, y); - - const h1 = ux & (maxInt(u16) / 2); - const h2 = uy & (@as(u16, 1) << 15); - return @bitCast(f16, h1 | h2); -} - -fn copysign32(x: f32, y: f32) f32 { - const ux = @bitCast(u32, x); - const uy = @bitCast(u32, y); - - const h1 = ux & (maxInt(u32) / 2); - const h2 = uy & (@as(u32, 1) << 31); - return @bitCast(f32, h1 | h2); -} - -fn copysign64(x: f64, y: f64) f64 { - const ux = @bitCast(u64, x); - const uy = @bitCast(u64, y); - - const h1 = ux & (maxInt(u64) / 2); - const h2 = uy & (@as(u64, 1) << 63); - return @bitCast(f64, h1 | h2); -} - -fn copysign128(x: f128, y: f128) f128 { - const ux = @bitCast(u128, x); - const uy = @bitCast(u128, y); - - const h1 = ux & (maxInt(u128) / 2); - const h2 = uy & (@as(u128, 1) << 127); - return @bitCast(f128, h1 | h2); +/// Returns a value with the magnitude of `magnitude` and the sign of `sign`. +pub fn copysign(magnitude: anytype, sign: @TypeOf(magnitude)) @TypeOf(magnitude) { + const T = @TypeOf(magnitude); + const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); + const sign_bit_mask = @as(TBits, 1) << (@bitSizeOf(T) - 1); + const mag = @bitCast(TBits, magnitude) & ~sign_bit_mask; + const sgn = @bitCast(TBits, sign) & sign_bit_mask; + return @bitCast(T, mag | sgn); } test "math.copysign" { - try expect(copysign(f16, 1.0, 1.0) == copysign16(1.0, 1.0)); - try expect(copysign(f32, 1.0, 1.0) == copysign32(1.0, 1.0)); - try expect(copysign(f64, 1.0, 1.0) == copysign64(1.0, 1.0)); - try expect(copysign(f128, 1.0, 1.0) == copysign128(1.0, 1.0)); -} - -test "math.copysign16" { - try expect(copysign16(5.0, 1.0) == 5.0); - try expect(copysign16(5.0, -1.0) == -5.0); - try expect(copysign16(-5.0, -1.0) == -5.0); - try expect(copysign16(-5.0, 1.0) == 5.0); -} - -test "math.copysign32" { - try expect(copysign32(5.0, 1.0) == 5.0); - try expect(copysign32(5.0, -1.0) == -5.0); - try expect(copysign32(-5.0, -1.0) == -5.0); - try expect(copysign32(-5.0, 1.0) == 5.0); -} - -test "math.copysign64" { - try expect(copysign64(5.0, 1.0) == 5.0); - try expect(copysign64(5.0, -1.0) == -5.0); - try expect(copysign64(-5.0, -1.0) == -5.0); - try expect(copysign64(-5.0, 1.0) == 5.0); -} - -test "math.copysign128" { - try expect(copysign128(5.0, 1.0) == 5.0); - try expect(copysign128(5.0, -1.0) == -5.0); - try expect(copysign128(-5.0, -1.0) == -5.0); - try expect(copysign128(-5.0, 1.0) == 5.0); + inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { + try expect(copysign(@as(T, 1.0), @as(T, 1.0)) == 1.0); + try expect(copysign(@as(T, 2.0), @as(T, -2.0)) == -2.0); + try expect(copysign(@as(T, -3.0), @as(T, 3.0)) == 3.0); + try expect(copysign(@as(T, -4.0), @as(T, -4.0)) == -4.0); + try expect(copysign(@as(T, 5.0), @as(T, -500.0)) == -5.0); + try expect(copysign(math.inf(T), @as(T, -0.0)) == -math.inf(T)); + try expect(copysign(@as(T, 6.0), -math.nan(T)) == -6.0); + } } diff --git a/lib/std/math/pow.zig b/lib/std/math/pow.zig index 48c6636926..9cedf50e13 100644 --- a/lib/std/math/pow.zig +++ b/lib/std/math/pow.zig @@ -60,7 +60,7 @@ pub fn pow(comptime T: type, x: T, y: T) T { if (y < 0) { // pow(+-0, y) = +- 0 for y an odd integer if (isOddInteger(y)) { - return math.copysign(T, math.inf(T), x); + return math.copysign(math.inf(T), x); } // pow(+-0, y) = +inf for y an even integer else { From 951ab802a38ede9f5329aeae1fed00161321d558 Mon Sep 17 00:00:00 2001 From: alice Date: Tue, 17 May 2022 22:04:12 +0100 Subject: [PATCH 1555/2031] std.math: simpler error handling --- lib/std/math/isfinite.zig | 5 +---- lib/std/math/isinf.zig | 5 +---- lib/std/math/isnormal.zig | 5 +---- lib/std/math/ldexp.zig | 5 +---- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/lib/std/math/isfinite.zig b/lib/std/math/isfinite.zig index 67a67a4610..556f8a2378 100644 --- a/lib/std/math/isfinite.zig +++ b/lib/std/math/isfinite.zig @@ -5,10 +5,7 @@ const expect = std.testing.expect; /// Returns whether x is a finite value. pub fn isFinite(x: anytype) bool { const T = @TypeOf(x); - const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); - if (@typeInfo(T) != .Float) { - @compileError("isFinite not implemented for " ++ @typeName(T)); - } + const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); const remove_sign = ~@as(TBits, 0) >> 1; return @bitCast(TBits, x) & remove_sign < @bitCast(TBits, math.inf(T)); } diff --git a/lib/std/math/isinf.zig b/lib/std/math/isinf.zig index 7275740fcc..a26332411f 100644 --- a/lib/std/math/isinf.zig +++ b/lib/std/math/isinf.zig @@ -5,10 +5,7 @@ const expect = std.testing.expect; /// Returns whether x is an infinity, ignoring sign. pub fn isInf(x: anytype) bool { const T = @TypeOf(x); - const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); - if (@typeInfo(T) != .Float) { - @compileError("isInf not implemented for " ++ @typeName(T)); - } + const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); const remove_sign = ~@as(TBits, 0) >> 1; return @bitCast(TBits, x) & remove_sign == @bitCast(TBits, math.inf(T)); } diff --git a/lib/std/math/isnormal.zig b/lib/std/math/isnormal.zig index e15d8a91cc..08f848f5df 100644 --- a/lib/std/math/isnormal.zig +++ b/lib/std/math/isnormal.zig @@ -5,10 +5,7 @@ const expect = std.testing.expect; /// Returns whether x is neither zero, subnormal, infinity, or NaN. pub fn isNormal(x: anytype) bool { const T = @TypeOf(x); - const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); - if (@typeInfo(T) != .Float) { - @compileError("isNormal not implemented for " ++ @typeName(T)); - } + const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); const increment_exp = 1 << math.floatMantissaBits(T); const remove_sign = ~@as(TBits, 0) >> 1; diff --git a/lib/std/math/ldexp.zig b/lib/std/math/ldexp.zig index 0934244c65..57d8896c9c 100644 --- a/lib/std/math/ldexp.zig +++ b/lib/std/math/ldexp.zig @@ -15,10 +15,7 @@ pub fn ldexp(x: anytype, n: i32) @TypeOf(x) { var shift = n; const T = @TypeOf(base); - const TBits = std.meta.Int(.unsigned, @bitSizeOf(T)); - if (@typeInfo(T) != .Float) { - @compileError("ldexp not implemented for " ++ @typeName(T)); - } + const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); const mantissa_bits = math.floatMantissaBits(T); const exponent_min = math.floatExponentMin(T); From c0ad0606df9a5bb657de70cd42229e20a91c51fc Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 10 May 2022 19:23:55 +0200 Subject: [PATCH 1556/2031] wasm: Support 128bit integer coercion The Wasm backend now correctly supports coercing a smaller integer into a 128bit integer. Regardless of signedness. --- src/arch/wasm/CodeGen.zig | 56 +++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index fd3ac0e93a..489adce66c 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1210,7 +1210,7 @@ fn allocStackPtr(self: *Self, inst: Air.Inst.Index) !WValue { /// From given zig bitsize, returns the wasm bitsize fn toWasmBits(bits: u16) ?u16 { - return for ([_]u16{ 32, 64 }) |wasm_bits| { + return for ([_]u16{ 32, 64, 128 }) |wasm_bits| { if (bits <= wasm_bits) return wasm_bits; } else null; } @@ -1837,8 +1837,11 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro } }, .Int => if (ty.intInfo(self.target).bits > 64) { - const len = @intCast(u32, ty.abiSize(self.target)); - return self.memcpy(lhs, rhs, .{ .imm32 = len }); + const lsb = try self.load(rhs, Type.u64, 0); + const msb = try self.load(rhs, Type.u64, 8); + try self.store(lhs, lsb, Type.u64, 0); + try self.store(lhs, msb, Type.u64, 8); + return; }, else => {}, } @@ -2900,8 +2903,11 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty = self.air.getRefType(ty_op.ty); const operand = try self.resolveInst(ty_op.operand); const operand_ty = self.air.typeOf(ty_op.operand); - if (ty.abiSize(self.target) > 8 or operand_ty.abiSize(self.target) > 8) { - return self.fail("todo Wasm intcast for bitsize > 64", .{}); + if (ty.zigTypeTag() == .Vector or operand_ty.zigTypeTag() == .Vector) { + return self.fail("todo Wasm intcast for vectors", .{}); + } + if (ty.abiSize(self.target) > 16 or operand_ty.abiSize(self.target) > 16) { + return self.fail("todo Wasm intcast for bitsize > 128", .{}); } return self.intcast(operand, operand_ty, ty); @@ -2909,25 +2915,53 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { /// Upcasts or downcasts an integer based on the given and wanted types, /// and stores the result in a new operand. -/// Asserts type's bitsize <= 64 +/// Asserts type's bitsize <= 128 fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue { const given_info = given.intInfo(self.target); const wanted_info = wanted.intInfo(self.target); - assert(given_info.bits <= 64); - assert(wanted_info.bits <= 64); + assert(given_info.bits <= 128); + assert(wanted_info.bits <= 128); const op_bits = toWasmBits(given_info.bits).?; const wanted_bits = toWasmBits(wanted_info.bits).?; if (op_bits == wanted_bits) return operand; - try self.emitWValue(operand); - if (op_bits > 32 and wanted_bits == 32) { + if (op_bits > 32 and op_bits <= 64 and wanted_bits == 32) { + try self.emitWValue(operand); try self.addTag(.i32_wrap_i64); - } else if (op_bits == 32 and wanted_bits > 32) { + } else if (op_bits == 32 and wanted_bits > 32 and wanted_bits <= 64) { + try self.emitWValue(operand); try self.addTag(switch (wanted_info.signedness) { .signed => .i64_extend_i32_s, .unsigned => .i64_extend_i32_u, }); + } else if (wanted_bits == 128) { + // for 128bit integers we store the integer in the virtual stack, rather than a local + const stack_ptr = try self.allocStack(wanted); + + // for 32 bit integers, we first coerce the value into a 64 bit integer before storing it + // meaning less store operations are required. + const lhs = if (op_bits == 32) blk: { + const tmp = try self.intcast( + operand, + given, + if (wanted.isSignedInt()) Type.i64 else Type.u64, + ); + break :blk tmp; + } else operand; + + // store msb first + try self.store(stack_ptr, lhs, Type.u64, 0); + + // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value + if (wanted.isSignedInt()) { + const shr = try self.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr); + try self.store(stack_ptr, shr, Type.u64, 8); + } else { + // Ensure memory of lsb is zero'd + try self.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8); + } + return stack_ptr; } else unreachable; const result = try self.allocLocal(wanted); From 59d3714b8d7008676902b51b891c4cc153447374 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 10 May 2022 20:23:03 +0200 Subject: [PATCH 1557/2031] wasm: 128bit integer cmp support This implements support for all compare operations on a 128bit integer, for both signed and unsigned integers. The new implementation is almost more efficient as it requires no control-flow, unlike the old implementation which used a block with breaks. --- src/arch/wasm/CodeGen.zig | 55 +++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 489adce66c..e217e6946e 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2962,7 +2962,7 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W try self.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8); } return stack_ptr; - } else unreachable; + } else return self.fail("todo Wasm @intCast to 128bit integers", .{}); const result = try self.allocLocal(wanted); try self.addLabel(.local_set, result.local); @@ -3710,35 +3710,44 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std } /// Compares big integers by checking both its high bits and low bits. -/// TODO: Lower this to compiler_rt call +/// TODO: Lower this to compiler_rt call when bitsize > 128 fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { if (operand_ty.intInfo(self.target).bits > 128) { return self.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(self.target).bits}); } - const result = try self.allocLocal(Type.initTag(.i32)); - { - try self.startBlock(.block, wasm.block_empty); - const lhs_high_bit = try self.load(lhs, Type.u64, 0); - const lhs_low_bit = try self.load(lhs, Type.u64, 8); - const rhs_high_bit = try self.load(rhs, Type.u64, 0); - const rhs_low_bit = try self.load(rhs, Type.u64, 8); - try self.emitWValue(lhs_high_bit); - try self.emitWValue(rhs_high_bit); - try self.addTag(.i64_ne); - try self.addLabel(.br_if, 0); - try self.emitWValue(lhs_low_bit); - try self.emitWValue(rhs_low_bit); - try self.addTag(.i64_ne); - try self.addLabel(.br_if, 0); - try self.addImm32(1); - try self.addLabel(.local_set, result.local); - try self.endBlock(); + const lhs_high_bit = try self.load(lhs, Type.u64, 0); + const lhs_low_bit = try self.load(lhs, Type.u64, 8); + const rhs_high_bit = try self.load(rhs, Type.u64, 0); + const rhs_low_bit = try self.load(rhs, Type.u64, 8); + + try self.emitWValue(or_result); + switch (op) { + .eq, .neq => { + const xor_high = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, .xor); + const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor); + const or_result = try self.binOp(xor_high, xor_low, Type.u64, .@"or"); + + switch (op) { + .eq => try self.addTag(.i64_eqz), + .neq => return self.cmp(or_result, .{ .imm32 = 0 }, Type.u32, .neq), + else => unreachable, + } + }, + else => { + const ty = if (operand_ty.isSignedInt()) Type.i64 else Type.u64; + const low_bit_eql = try self.cmp(lhs_low_bit, rhs_low_bit, ty, .eq); + const high_bit_cmp = try self.cmp(lhs_high_bit, rhs_high_bit, ty, op); + const low_bit_cmp = try self.cmp(lhs_low_bit, rhs_low_bit, ty, op); + + try self.emitWValue(low_bit_cmp); + try self.emitWValue(high_bit_cmp); + try self.emitWValue(low_bit_eql); + try self.addTag(.select); + }, } - try self.emitWValue(result); - try self.addImm32(0); - try self.addTag(if (op == .eq) .i32_ne else .i32_eq); + const result = try self.allocLocal(Type.initTag(.i32)); try self.addLabel(.local_set, result.local); return result; } From 167d3089ea11a8f0a45d6e083b1dacb107483b04 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 10 May 2022 22:25:14 +0200 Subject: [PATCH 1558/2031] wasm: Support 128bit add/sub wrapping operands --- src/arch/wasm/CodeGen.zig | 43 +++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index e217e6946e..c79a01f5e4 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1993,22 +1993,54 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { const ty = self.air.typeOf(bin_op.lhs); if (ty.zigTypeTag() == .Vector) { return self.fail("TODO: Implement wrapping arithmetic for vectors", .{}); - } else if (ty.abiSize(self.target) > 8) { - return self.fail("TODO: Implement wrapping arithmetic for bitsize > 64", .{}); + } else if (ty.abiSize(self.target) > 8 and op == .mul) { + return self.fail("TODO: Implement wrapping multiplication for bitsize > 64", .{}); + } else if (ty.abiSize(self.target) > 16) { + return self.fail("TODO: Implement wrapping arithmetic for bitsize > 128", .{}); } return self.wrapBinOp(lhs, rhs, ty, op); } fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { - try self.emitWValue(lhs); - try self.emitWValue(rhs); + const bit_size = ty.intInfo(self.target).bits; + var wasm_bits = toWasmBits(bit_size) orelse { + return self.fail("TODO: Implement wrapping arithmetic for integers with bitsize: {d}\n", .{bit_size}); + }; + if (wasm_bits == 128) { + if (op == .mul) return self.fail("TODO: Implement wrapping multiplication for 128bit integers", .{}); + if (bit_size != wasm_bits) return self.fail("TODO: Implement wrapping arithmetic for integers > 64 & < 128", .{}); + + const result = try self.allocStack(ty); + const lhs_high_bit = try self.load(lhs, Type.u64, 0); + const lhs_low_bit = try self.load(lhs, Type.u64, 8); + const rhs_high_bit = try self.load(rhs, Type.u64, 0); + const rhs_low_bit = try self.load(rhs, Type.u64, 8); + + const low_op_res = try self.binOp(rhs_low_bit, lhs_low_bit, Type.u64, op); + const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op); + + const lt = if (op == .add) blk: { + break :blk try self.binOp(high_op_res, rhs_high_bit, Type.u64, .lt); + } else if (op == .sub) blk: { + break :blk try self.binOp(rhs_high_bit, lhs_high_bit, Type.u64, .lt); + } else unreachable; + const tmp = try self.intcast(lt, Type.u32, Type.u64); + const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op); + + try self.store(result, high_op_res, Type.u64, 0); + try self.store(result, tmp_op, Type.u64, 8); + return result; + } const opcode: wasm.Opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, self.target), .signedness = if (ty.isSignedInt()) .signed else .unsigned, }); + + try self.emitWValue(lhs); + try self.emitWValue(rhs); try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); const bin_local = try self.allocLocal(ty); try self.addLabel(.local_set, bin_local.local); @@ -3721,7 +3753,6 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma const rhs_high_bit = try self.load(rhs, Type.u64, 0); const rhs_low_bit = try self.load(rhs, Type.u64, 8); - try self.emitWValue(or_result); switch (op) { .eq, .neq => { const xor_high = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, .xor); @@ -3729,7 +3760,7 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma const or_result = try self.binOp(xor_high, xor_low, Type.u64, .@"or"); switch (op) { - .eq => try self.addTag(.i64_eqz), + .eq => return self.cmp(or_result, .{ .imm32 = 0 }, Type.u32, .eq), .neq => return self.cmp(or_result, .{ .imm32 = 0 }, Type.u32, .neq), else => unreachable, } From 03a3ea2c1577208311d85482faadac6361442971 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 12 May 2022 21:53:57 +0200 Subject: [PATCH 1559/2031] wasm: 128 bit intcast and binary operations Also fixes some bugs in 128-bit binary comparisons where we checked if the lsb were equal, rather than msb. --- src/arch/wasm/CodeGen.zig | 94 +++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 39 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index c79a01f5e4..0b371a8cd2 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1958,25 +1958,31 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const operand_ty = self.air.typeOfIndex(inst); const ty = self.air.typeOf(bin_op.lhs); - if (isByRef(operand_ty, self.target)) { - return self.fail("TODO: Implement binary operation for type: {}", .{operand_ty.fmtDebug()}); - } - return self.binOp(lhs, rhs, ty, op); } fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { - try self.emitWValue(lhs); - try self.emitWValue(rhs); + if (isByRef(ty, self.target)) { + if (ty.zigTypeTag() == .Int) { + return self.binOpBigInt(lhs, rhs, ty, op); + } else { + return self.fail( + "TODO: Implement binary operation for type: {}", + .{ty.fmt(self.bin_file.base.options.module.?)}, + ); + } + } const opcode: wasm.Opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, self.target), .signedness = if (ty.isSignedInt()) .signed else .unsigned, }); + try self.emitWValue(lhs); + try self.emitWValue(rhs); + try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); // save the result in a temporary @@ -1985,6 +1991,37 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa return bin_local; } +fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { + if (ty.intInfo(self.target).bits != 128) { + return self.fail("TODO: Implement binary operation for big integer", .{}); + } + + if (op != .add and op != .sub) { + return self.fail("TODO: Implement binary operation for big integers", .{}); + } + + const result = try self.allocStack(ty); + const lhs_high_bit = try self.load(lhs, Type.u64, 0); + const lhs_low_bit = try self.load(lhs, Type.u64, 8); + const rhs_high_bit = try self.load(rhs, Type.u64, 0); + const rhs_low_bit = try self.load(rhs, Type.u64, 8); + + const low_op_res = try self.binOp(rhs_low_bit, lhs_low_bit, Type.u64, op); + const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op); + + const lt = if (op == .add) blk: { + break :blk try self.cmp(high_op_res, rhs_high_bit, Type.u64, .lt); + } else if (op == .sub) blk: { + break :blk try self.cmp(rhs_high_bit, lhs_high_bit, Type.u64, .lt); + } else unreachable; + const tmp = try self.intcast(lt, Type.u32, Type.u64); + const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op); + + try self.store(result, high_op_res, Type.u64, 0); + try self.store(result, tmp_op, Type.u64, 8); + return result; +} + fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs = try self.resolveInst(bin_op.lhs); @@ -1993,10 +2030,6 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { const ty = self.air.typeOf(bin_op.lhs); if (ty.zigTypeTag() == .Vector) { return self.fail("TODO: Implement wrapping arithmetic for vectors", .{}); - } else if (ty.abiSize(self.target) > 8 and op == .mul) { - return self.fail("TODO: Implement wrapping multiplication for bitsize > 64", .{}); - } else if (ty.abiSize(self.target) > 16) { - return self.fail("TODO: Implement wrapping arithmetic for bitsize > 128", .{}); } return self.wrapBinOp(lhs, rhs, ty, op); @@ -2007,30 +2040,9 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError var wasm_bits = toWasmBits(bit_size) orelse { return self.fail("TODO: Implement wrapping arithmetic for integers with bitsize: {d}\n", .{bit_size}); }; + if (wasm_bits == 128) { - if (op == .mul) return self.fail("TODO: Implement wrapping multiplication for 128bit integers", .{}); - if (bit_size != wasm_bits) return self.fail("TODO: Implement wrapping arithmetic for integers > 64 & < 128", .{}); - - const result = try self.allocStack(ty); - const lhs_high_bit = try self.load(lhs, Type.u64, 0); - const lhs_low_bit = try self.load(lhs, Type.u64, 8); - const rhs_high_bit = try self.load(rhs, Type.u64, 0); - const rhs_low_bit = try self.load(rhs, Type.u64, 8); - - const low_op_res = try self.binOp(rhs_low_bit, lhs_low_bit, Type.u64, op); - const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op); - - const lt = if (op == .add) blk: { - break :blk try self.binOp(high_op_res, rhs_high_bit, Type.u64, .lt); - } else if (op == .sub) blk: { - break :blk try self.binOp(rhs_high_bit, lhs_high_bit, Type.u64, .lt); - } else unreachable; - const tmp = try self.intcast(lt, Type.u32, Type.u64); - const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op); - - try self.store(result, high_op_res, Type.u64, 0); - try self.store(result, tmp_op, Type.u64, 8); - return result; + return self.binOp(lhs, rhs, ty, op); } const opcode: wasm.Opcode = buildOpcode(.{ @@ -2044,6 +2056,10 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); const bin_local = try self.allocLocal(ty); try self.addLabel(.local_set, bin_local.local); + if (wasm_bits == bit_size) { + return bin_local; + } + return self.wrapOperand(bin_local, ty); } @@ -2994,7 +3010,7 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W try self.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8); } return stack_ptr; - } else return self.fail("todo Wasm @intCast to 128bit integers", .{}); + } else return self.load(operand, wanted, 0); const result = try self.allocLocal(wanted); try self.addLabel(.local_set, result.local); @@ -3760,20 +3776,20 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma const or_result = try self.binOp(xor_high, xor_low, Type.u64, .@"or"); switch (op) { - .eq => return self.cmp(or_result, .{ .imm32 = 0 }, Type.u32, .eq), - .neq => return self.cmp(or_result, .{ .imm32 = 0 }, Type.u32, .neq), + .eq => return self.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .eq), + .neq => return self.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .neq), else => unreachable, } }, else => { const ty = if (operand_ty.isSignedInt()) Type.i64 else Type.u64; - const low_bit_eql = try self.cmp(lhs_low_bit, rhs_low_bit, ty, .eq); + const high_bit_eql = try self.cmp(lhs_high_bit, rhs_high_bit, ty, .eq); const high_bit_cmp = try self.cmp(lhs_high_bit, rhs_high_bit, ty, op); const low_bit_cmp = try self.cmp(lhs_low_bit, rhs_low_bit, ty, op); try self.emitWValue(low_bit_cmp); try self.emitWValue(high_bit_cmp); - try self.emitWValue(low_bit_eql); + try self.emitWValue(high_bit_eql); try self.addTag(.select); }, } From 502f5d824626675aecf9daaaf6af7548ef3e7f09 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 14 May 2022 19:23:31 +0200 Subject: [PATCH 1560/2031] wasm: Fix C-ABI for 128 bit integers We now pass the correct wasm type when the return type is a 128-bit integer. When a function accepts a 128-bit integer, we now allocate space on the virtual stack and store both arguments within that space as currently all following instructions assume the 128 bit integer doesn't live in a local, but the stack. --- src/arch/wasm/CodeGen.zig | 106 +++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 60 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 0b371a8cd2..001ffa8e7e 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -794,7 +794,7 @@ fn genFunctype(gpa: Allocator, fn_info: Type.Payload.Function.Data, target: std. defer returns.deinit(); if (firstParamSRet(fn_info, target)) { - try params.append(typeToValtype(fn_info.return_type, target)); + try params.append(.i32); // memory address is always a 32-bit handle } else if (fn_info.return_type.hasRuntimeBitsIgnoreComptime()) { if (fn_info.cc == .C) { const res_classes = abi.classifyType(fn_info.return_type, target); @@ -824,7 +824,10 @@ fn genFunctype(gpa: Allocator, fn_info: Type.Payload.Function.Data, target: std. } } }, - else => try params.append(typeToValtype(param_type, target)), + else => if (isByRef(param_type, target)) + try params.append(.i32) + else + try params.append(typeToValtype(param_type, target)), } } } @@ -844,7 +847,6 @@ pub fn generate( code: *std.ArrayList(u8), debug_output: codegen.DebugInfoOutput, ) codegen.GenerateSymbolError!codegen.FnResult { - _ = debug_output; // TODO _ = src_loc; var code_gen: Self = .{ .gpa = bin_file.allocator, @@ -1088,9 +1090,9 @@ fn lowerArg(self: *Self, cc: std.builtin.CallingConvention, ty: Type, value: WVa assert(ty.abiSize(self.target) == 16); // in this case we have an integer or float that must be lowered as 2 i64's. try self.emitWValue(value); - try self.addMemArg(.i64_load, .{ .offset = value.offset(), .alignment = 16 }); + try self.addMemArg(.i64_load, .{ .offset = value.offset(), .alignment = 8 }); try self.emitWValue(value); - try self.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 16 }); + try self.addMemArg(.i64_load, .{ .offset = value.offset() + 8, .alignment = 8 }); }, else => return self.lowerToStack(value), } @@ -1221,14 +1223,8 @@ fn memcpy(self: *Self, dst: WValue, src: WValue, len: WValue) !void { // When bulk_memory is enabled, we lower it to wasm's memcpy instruction. // If not, we lower it ourselves manually if (std.Target.wasm.featureSetHas(self.target.cpu.features, .bulk_memory)) { - switch (dst) { - .stack_offset => try self.emitWValue(try self.buildPointerOffset(dst, 0, .new)), - else => try self.emitWValue(dst), - } - switch (src) { - .stack_offset => try self.emitWValue(try self.buildPointerOffset(src, 0, .new)), - else => try self.emitWValue(src), - } + try self.lowerToStack(dst); + try self.lowerToStack(src); try self.emitWValue(len); try self.addExtended(.memory_copy); return; @@ -1792,7 +1788,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty = self.air.typeOf(bin_op.lhs).childType(); try self.store(lhs, rhs, ty, 0); - return .none; + return WValue{ .none = {} }; } fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void { @@ -1848,11 +1844,8 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro try self.emitWValue(lhs); // In this case we're actually interested in storing the stack position // into lhs, so we calculate that and emit that instead - if (rhs == .stack_offset) { - try self.emitWValue(try self.buildPointerOffset(rhs, 0, .new)); - } else { - try self.emitWValue(rhs); - } + try self.lowerToStack(rhs); + const valtype = typeToValtype(ty, self.target); const abi_size = @intCast(u8, ty.abiSize(self.target)); @@ -1912,14 +1905,29 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const arg_index = self.arg_index; const arg = self.args[arg_index]; const cc = self.decl.ty.fnInfo().cc; + const arg_ty = self.air.typeOfIndex(inst); if (cc == .C) { - const ty = self.air.typeOfIndex(inst); - const arg_classes = abi.classifyType(ty, self.target); + const arg_classes = abi.classifyType(arg_ty, self.target); for (arg_classes) |class| { if (class != .none) { self.arg_index += 1; } } + + // When we have an argument that's passed using more than a single parameter, + // we combine them into a single stack value + if (arg_classes[0] == .direct and arg_classes[1] == .direct) { + if (arg_ty.zigTypeTag() != .Int) { + return self.fail( + "TODO: Implement C-ABI argument for type '{}'", + .{arg_ty.fmt(self.bin_file.base.options.module.?)}, + ); + } + const result = try self.allocStack(arg_ty); + try self.store(result, arg, Type.u64, 0); + try self.store(result, self.args[arg_index + 1], Type.u64, 8); + return result; + } } else { self.arg_index += 1; } @@ -1943,7 +1951,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { std.dwarf.OP.WASM_local, }); leb.writeULEB128(dbg_info.writer(), arg.local) catch unreachable; - try self.addDbgInfoTypeReloc(self.air.typeOfIndex(inst)); + try self.addDbgInfoTypeReloc(arg_ty); dbg_info.appendSliceAssumeCapacity(name); dbg_info.appendAssumeCapacity(0); }, @@ -2503,14 +2511,8 @@ fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOper // ensure that when we compare pointers, we emit // the true pointer of a stack value, rather than the stack pointer. - switch (lhs) { - .stack_offset => try self.emitWValue(try self.buildPointerOffset(lhs, 0, .new)), - else => try self.emitWValue(lhs), - } - switch (rhs) { - .stack_offset => try self.emitWValue(try self.buildPointerOffset(rhs, 0, .new)), - else => try self.emitWValue(rhs), - } + try self.lowerToStack(lhs); + try self.lowerToStack(rhs); const signedness: std.builtin.Signedness = blk: { // by default we tell the operand type is unsigned (i.e. bools and enum values) @@ -2560,11 +2562,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { // if operand has codegen bits we should break with a value if (self.air.typeOf(br.operand).hasRuntimeBitsIgnoreComptime()) { const operand = try self.resolveInst(br.operand); - const op = switch (operand) { - .stack_offset => try self.buildPointerOffset(operand, 0, .new), - else => operand, - }; - try self.emitWValue(op); + try self.lowerToStack(operand); if (block.value != .none) { try self.addLabel(.local_set, block.value.local); @@ -3295,11 +3293,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ptr_local = try self.load(ptr, Type.usize, 0); try self.addLabel(.local_get, ptr_local.local); } else { - const pointer = switch (ptr) { - .stack_offset => try self.buildPointerOffset(ptr, 0, .new), - else => ptr, - }; - try self.emitWValue(pointer); + try self.lowerToStack(ptr); } // calculate index into slice @@ -3332,11 +3326,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ptr_local = try self.load(ptr, Type.usize, 0); try self.addLabel(.local_get, ptr_local.local); } else { - const pointer = switch (ptr) { - .stack_offset => try self.buildPointerOffset(ptr, 0, .new), - else => ptr, - }; - try self.emitWValue(pointer); + try self.lowerToStack(ptr); } // calculate index into ptr @@ -3365,11 +3355,7 @@ fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul }); const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op }); - const pointer = switch (ptr) { - .stack_offset => try self.buildPointerOffset(ptr, 0, .new), - else => ptr, - }; - try self.emitWValue(pointer); + try self.lowerToStack(ptr); try self.emitWValue(offset); try self.addImm32(@bitCast(i32, @intCast(u32, pointee_ty.abiSize(self.target)))); try self.addTag(Mir.Inst.Tag.fromOpcode(mul_opcode)); @@ -3400,10 +3386,7 @@ fn memset(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void // When bulk_memory is enabled, we lower it to wasm's memset instruction. // If not, we lower it ourselves if (std.Target.wasm.featureSetHas(self.target.cpu.features, .bulk_memory)) { - switch (ptr) { - .stack_offset => try self.emitWValue(try self.buildPointerOffset(ptr, 0, .new)), - else => try self.emitWValue(ptr), - } + try self.lowerToStack(ptr); try self.emitWValue(value); try self.emitWValue(len); try self.addExtended(.memory_fill); @@ -3490,12 +3473,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const elem_ty = array_ty.childType(); const elem_size = elem_ty.abiSize(self.target); - const array_ptr = switch (array) { - .stack_offset => try self.buildPointerOffset(array, 0, .new), - else => array, - }; - - try self.emitWValue(array_ptr); + try self.lowerToStack(array); try self.emitWValue(index); try self.addImm32(@bitCast(i32, @intCast(u32, elem_size))); try self.addTag(.i32_mul); @@ -3518,6 +3496,10 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const dest_ty = self.air.typeOfIndex(inst); const op_ty = self.air.typeOf(ty_op.operand); + if (op_ty.abiSize(self.target) > 8) { + return self.fail("TODO: floatToInt for integers/floats with bitsize larger than 64 bits", .{}); + } + try self.emitWValue(operand); const op = buildOpcode(.{ .op = .trunc, @@ -3541,6 +3523,10 @@ fn airIntToFloat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const dest_ty = self.air.typeOfIndex(inst); const op_ty = self.air.typeOf(ty_op.operand); + if (op_ty.abiSize(self.target) > 8) { + return self.fail("TODO: intToFloat for integers/floats with bitsize larger than 64 bits", .{}); + } + try self.emitWValue(operand); const op = buildOpcode(.{ .op = .convert, From ea073a6b767cc6597ff2b6ec3b5f14fde81dd6fc Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 15 May 2022 18:41:52 +0200 Subject: [PATCH 1561/2031] wasm: Support 128bit integers for max/min/ctz/clz `airMaxMin` was slightly updated to automatically support 128 bit integers, by using the `cmp` function, instead of doing it manually. This makes the function more maintanable as well. `ctz` and `clz` now support 128 bit integers, while updating the previous implementation also. --- src/arch/wasm/CodeGen.zig | 88 +++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 32 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 001ffa8e7e..3193361efe 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -4154,7 +4154,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return self.fail("TODO: Implement overflow arithmetic for integer bitsize: {d}", .{int_info.bits}); }; - if (wasm_bits == 64) { + if (wasm_bits > 32) { return self.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits}); } @@ -4236,32 +4236,26 @@ fn airMaxMin(self: *Self, inst: Air.Inst.Index, op: enum { max, min }) InnerErro return self.fail("TODO: `@maximum` and `@minimum` for vectors", .{}); } - if (ty.abiSize(self.target) > 8) { - return self.fail("TODO: `@maximum` and `@minimum` for types larger than 8 bytes", .{}); + if (ty.abiSize(self.target) > 16) { + return self.fail("TODO: `@maximum` and `@minimum` for types larger than 16 bytes", .{}); } const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - // operands to select from - try self.emitWValue(lhs); - try self.emitWValue(rhs); + const cmp_result = try self.cmp(lhs, rhs, ty, if (op == .max) .gt else .lt); - // operands to compare - try self.emitWValue(lhs); - try self.emitWValue(rhs); - const opcode = buildOpcode(.{ - .op = if (op == .max) .gt else .lt, - .signedness = if (ty.isSignedInt()) .signed else .unsigned, - .valtype1 = typeToValtype(ty, self.target), - }); - try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); + // operands to select from + try self.lowerToStack(lhs); + try self.lowerToStack(rhs); + try self.emitWValue(cmp_result); // based on the result from comparison, return operand 0 or 1. try self.addTag(.select); // store result in local - const result = try self.allocLocal(ty); + const result_ty = if (isByRef(ty, self.target)) Type.u32 else ty; + const result = try self.allocLocal(result_ty); try self.addLabel(.local_set, result.local); return result; } @@ -4302,31 +4296,39 @@ fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return self.fail("TODO: `@clz` for integers with bitsize '{d}'", .{int_info.bits}); }; - try self.emitWValue(operand); switch (wasm_bits) { 32 => { + try self.emitWValue(operand); try self.addTag(.i32_clz); - - if (wasm_bits != int_info.bits) { - const tmp = try self.allocLocal(ty); - try self.addLabel(.local_set, tmp.local); - const val: i32 = -@intCast(i32, wasm_bits - int_info.bits); - return self.wrapBinOp(tmp, .{ .imm32 = @bitCast(u32, val) }, ty, .add); - } }, 64 => { + try self.emitWValue(operand); try self.addTag(.i64_clz); + try self.addTag(.i32_wrap_i64); + }, + 128 => { + const msb = try self.load(operand, Type.u64, 0); + const lsb = try self.load(operand, Type.u64, 8); + const neq = try self.cmp(lsb, .{ .imm64 = 0 }, Type.u64, .neq); - if (wasm_bits != int_info.bits) { - const tmp = try self.allocLocal(ty); - try self.addLabel(.local_set, tmp.local); - const val: i64 = -@intCast(i64, wasm_bits - int_info.bits); - return self.wrapBinOp(tmp, .{ .imm64 = @bitCast(u64, val) }, ty, .add); - } + try self.emitWValue(lsb); + try self.addTag(.i64_clz); + try self.emitWValue(msb); + try self.addTag(.i64_clz); + try self.emitWValue(.{ .imm64 = 64 }); + try self.addTag(.i64_add); + try self.emitWValue(neq); + try self.addTag(.select); + try self.addTag(.i32_wrap_i64); }, else => unreachable, } + if (wasm_bits != int_info.bits) { + try self.emitWValue(.{ .imm32 = wasm_bits - int_info.bits }); + try self.addTag(.i32_sub); + } + const result = try self.allocLocal(result_ty); try self.addLabel(.local_set, result.local); return result; @@ -4365,12 +4367,34 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } else try self.emitWValue(operand); try self.addTag(.i64_ctz); }, + 128 => { + const msb = try self.load(operand, Type.u64, 0); + const lsb = try self.load(operand, Type.u64, 8); + const neq = try self.cmp(msb, .{ .imm64 = 0 }, Type.u64, .neq); + + try self.emitWValue(msb); + try self.addTag(.i64_ctz); + try self.emitWValue(lsb); + if (wasm_bits != int_info.bits) { + try self.addImm64(@as(u64, 1) << @intCast(u6, int_info.bits - 64)); + try self.addTag(.i64_or); + } + try self.addTag(.i64_ctz); + try self.addImm64(64); + if (wasm_bits != int_info.bits) { + try self.addTag(.i64_or); + } else { + try self.addTag(.i64_add); + } + try self.emitWValue(neq); + try self.addTag(.select); + }, else => unreachable, } - const result = try self.allocLocal(result_ty); + const result = try self.allocLocal(ty); try self.addLabel(.local_set, result.local); - return result; + return self.intcast(result, ty, result_ty); } fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !WValue { From 10fe24c043c95180f658c3eb4d7fbbfd0388c14e Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 15 May 2022 20:14:57 +0200 Subject: [PATCH 1562/2031] wasm: Implement trunc/wrap for 128 bit integers This also implments wrapping for arbitrary integer widths between 64 and 128. `@truncate` was fixed where the wasm types between operand and result differentiated. We solved this by first casting and then wrapping. --- src/arch/wasm/CodeGen.zig | 111 ++++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 41 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 3193361efe..06682e096f 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2000,7 +2000,7 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa } fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { - if (ty.intInfo(self.target).bits != 128) { + if (ty.intInfo(self.target).bits > 128) { return self.fail("TODO: Implement binary operation for big integer", .{}); } @@ -2050,7 +2050,8 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError }; if (wasm_bits == 128) { - return self.binOp(lhs, rhs, ty, op); + const bin_op = try self.binOpBigInt(lhs, rhs, ty, op); + return self.wrapOperand(bin_op, ty); } const opcode: wasm.Opcode = buildOpcode(.{ @@ -2064,28 +2065,46 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); const bin_local = try self.allocLocal(ty); try self.addLabel(.local_set, bin_local.local); - if (wasm_bits == bit_size) { - return bin_local; - } return self.wrapOperand(bin_local, ty); } /// Wraps an operand based on a given type's bitsize. -/// Asserts `Type` is <= 64bits. +/// Asserts `Type` is <= 128 bits. fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue { - assert(ty.abiSize(self.target) <= 8); + assert(ty.abiSize(self.target) <= 16); const result_local = try self.allocLocal(ty); const bitsize = ty.intInfo(self.target).bits; - const result = @intCast(u64, (@as(u65, 1) << @intCast(u7, bitsize)) - 1); + const wasm_bits = toWasmBits(bitsize) orelse { + return self.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{bitsize}); + }; + if (wasm_bits == bitsize) return operand; + + if (wasm_bits == 128) { + const msb = try self.load(operand, Type.u64, 0); + const lsb = try self.load(operand, Type.u64, 8); + + const result_ptr = try self.allocStack(ty); + try self.store(result_ptr, lsb, Type.u64, 8); + const result = (@as(u64, 1) << @intCast(u6, 64 - (wasm_bits - bitsize))) - 1; + try self.emitWValue(result_ptr); + try self.emitWValue(msb); + try self.addImm64(result); + try self.addTag(.i64_and); + try self.addMemArg(.i64_store, .{ .offset = result_ptr.offset(), .alignment = 8 }); + return result_ptr; + } + + const result = (@as(u64, 1) << @intCast(u6, bitsize)) - 1; try self.emitWValue(operand); if (bitsize <= 32) { try self.addImm32(@bitCast(i32, @intCast(u32, result))); try self.addTag(.i32_and); - } else { + } else if (bitsize <= 64) { try self.addImm64(result); try self.addTag(.i64_and); - } + } else unreachable; + try self.addLabel(.local_set, result_local.local); return result_local; } @@ -3231,13 +3250,20 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); const wanted_ty = self.air.getRefType(ty_op.ty); - const int_info = wanted_ty.intInfo(self.target); - const wanted_bits = int_info.bits; + const op_ty = self.air.typeOf(ty_op.operand); - _ = toWasmBits(wanted_bits) orelse { - return self.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{wanted_bits}); - }; - return self.wrapOperand(operand, wanted_ty); + const int_info = op_ty.intInfo(self.target); + if (toWasmBits(int_info.bits) == null) { + return self.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{int_info.bits}); + } + + const result = try self.intcast(operand, op_ty, wanted_ty); + const wanted_bits = wanted_ty.intInfo(self.target).bits; + const wasm_bits = toWasmBits(wanted_bits).?; + if (wasm_bits != wanted_bits) { + return self.wrapOperand(result, wanted_ty); + } + return result; } fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -3927,6 +3953,7 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); const op_ty = self.air.typeOf(ty_op.operand); + const result_ty = self.air.typeOfIndex(inst); if (op_ty.zigTypeTag() == .Vector) { return self.fail("TODO: Implement @popCount for vectors", .{}); @@ -3938,32 +3965,32 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return self.fail("TODO: Implement @popCount for integers with bitsize '{d}'", .{bits}); }; - try self.emitWValue(operand); - - // for signed integers we first mask the signedness bit - if (int_info.signedness == .signed and wasm_bits != bits) { - switch (wasm_bits) { - 32 => { - const mask = (@as(u32, 1) << @intCast(u5, bits)) - 1; - try self.addImm32(@bitCast(i32, mask)); - try self.addTag(.i32_and); - }, - 64 => { - const mask = (@as(u64, 1) << @intCast(u6, bits)) - 1; - try self.addImm64(mask); - try self.addTag(.i64_and); - }, - else => unreachable, - } - } - switch (wasm_bits) { - 32 => try self.addTag(.i32_popcnt), - 64 => try self.addTag(.i64_popcnt), - else => unreachable, + 128 => { + const msb = try self.load(operand, Type.u64, 0); + const lsb = try self.load(operand, Type.u64, 8); + + try self.emitWValue(msb); + try self.addTag(.i64_popcnt); + try self.emitWValue(lsb); + try self.addTag(.i64_popcnt); + try self.addTag(.i64_add); + try self.addTag(.i32_wrap_i64); + }, + else => { + try self.emitWValue(operand); + switch (wasm_bits) { + 32 => try self.addTag(.i32_popcnt), + 64 => { + try self.addTag(.i64_popcnt); + try self.addTag(.i32_wrap_i64); + }, + else => unreachable, + } + }, } - const result = try self.allocLocal(op_ty); + const result = try self.allocLocal(result_ty); try self.addLabel(.local_set, result.local); return result; } @@ -4366,6 +4393,7 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.emitWValue(bin_op); } else try self.emitWValue(operand); try self.addTag(.i64_ctz); + try self.addTag(.i32_wrap_i64); }, 128 => { const msb = try self.load(operand, Type.u64, 0); @@ -4388,13 +4416,14 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } try self.emitWValue(neq); try self.addTag(.select); + try self.addTag(.i32_wrap_i64); }, else => unreachable, } - const result = try self.allocLocal(ty); + const result = try self.allocLocal(result_ty); try self.addLabel(.local_set, result.local); - return self.intcast(result, ty, result_ty); + return result; } fn airDbgVar(self: *Self, inst: Air.Inst.Index, is_ptr: bool) !WValue { From fd081c74f10de71eaf00b0ccd7c4e4a0c0c5c3ad Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Mon, 16 May 2022 17:14:15 +0200 Subject: [PATCH 1563/2031] wasm: Support `not` instruction for 128 bit integers This also fixes the instruction for all other integer bitsizes, as it was previously assuming to always be a bool. 128 bit substraction was also fixed as it contained a bug where it swapped lhs with rhs. --- src/arch/wasm/CodeGen.zig | 61 +++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 06682e096f..feaa1cf145 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2014,13 +2014,13 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr const rhs_high_bit = try self.load(rhs, Type.u64, 0); const rhs_low_bit = try self.load(rhs, Type.u64, 8); - const low_op_res = try self.binOp(rhs_low_bit, lhs_low_bit, Type.u64, op); + const low_op_res = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op); const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op); const lt = if (op == .add) blk: { break :blk try self.cmp(high_op_res, rhs_high_bit, Type.u64, .lt); } else if (op == .sub) blk: { - break :blk try self.cmp(rhs_high_bit, lhs_high_bit, Type.u64, .lt); + break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt); } else unreachable; const tmp = try self.intcast(lt, Type.u32, Type.u64); const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op); @@ -2078,6 +2078,7 @@ fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue { const wasm_bits = toWasmBits(bitsize) orelse { return self.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{bitsize}); }; + if (wasm_bits == bitsize) return operand; if (wasm_bits == 128) { @@ -2424,15 +2425,16 @@ fn valueAsI32(self: Self, val: Value, ty: Type) i32 { fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; - const block_ty = genBlockType(self.air.getRefType(ty_pl.ty), self.target); + const block_ty = self.air.getRefType(ty_pl.ty); + const wasm_block_ty = genBlockType(block_ty, self.target); const extra = self.air.extraData(Air.Block, ty_pl.payload); const body = self.air.extra[extra.end..][0..extra.data.body_len]; - // if block_ty is non-empty, we create a register to store the temporary value - const block_result: WValue = if (block_ty != wasm.block_empty) - try self.allocLocal(self.air.getRefType(ty_pl.ty)) - else - WValue.none; + // if wasm_block_ty is non-empty, we create a register to store the temporary value + const block_result: WValue = if (wasm_block_ty != wasm.block_empty) blk: { + const ty: Type = if (isByRef(block_ty, self.target)) Type.u32 else block_ty; + break :blk try self.allocLocal(ty); + } else WValue.none; try self.startBlock(.block, wasm.block_empty); // Here we set the current block idx, so breaks know the depth to jump @@ -2600,16 +2602,43 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); - try self.emitWValue(operand); + const operand_ty = self.air.typeOf(ty_op.operand); - // wasm does not have booleans nor the `not` instruction, therefore compare with 0 - // to create the same logic - try self.addTag(.i32_eqz); + if (operand_ty.zigTypeTag() == .Bool) { + try self.emitWValue(operand); + try self.addTag(.i32_eqz); + const not_tmp = try self.allocLocal(operand_ty); + try self.addLabel(.local_set, not_tmp.local); + return not_tmp; + } else { + const operand_bits = operand_ty.intInfo(self.target).bits; + const wasm_bits = toWasmBits(operand_bits) orelse { + return self.fail("TODO: Implement binary NOT for integer with bitsize '{d}'", .{operand_bits}); + }; - // save the result in the local - const not_tmp = try self.allocLocal(Type.initTag(.i32)); - try self.addLabel(.local_set, not_tmp.local); - return not_tmp; + switch (wasm_bits) { + 32 => { + const bin_op = try self.binOp(operand, .{ .imm32 = ~@as(u32, 0) }, operand_ty, .xor); + return self.wrapOperand(bin_op, operand_ty); + }, + 64 => { + const bin_op = try self.binOp(operand, .{ .imm64 = ~@as(u64, 0) }, operand_ty, .xor); + return self.wrapOperand(bin_op, operand_ty); + }, + 128 => { + const result_ptr = try self.allocStack(operand_ty); + const msb = try self.load(operand, Type.u64, 0); + const lsb = try self.load(operand, Type.u64, 8); + + const msb_xor = try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); + const lsb_xor = try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); + try self.store(result_ptr, msb_xor, Type.u64, 0); + try self.store(result_ptr, lsb_xor, Type.u64, 8); + return result_ptr; + }, + else => unreachable, + } + } } fn airBreakpoint(self: *Self, inst: Air.Inst.Index) InnerError!WValue { From ed25ce77f58cd0ddfd5e3f5412c4cb66d1cc1331 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 17 May 2022 21:56:11 +0200 Subject: [PATCH 1564/2031] wasm: Implement {add/sub}WithOverflow for 128bit --- src/arch/wasm/CodeGen.zig | 63 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index feaa1cf145..60d03ccafd 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3801,6 +3801,7 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std /// Compares big integers by checking both its high bits and low bits. /// TODO: Lower this to compiler_rt call when bitsize > 128 fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { + assert(operand_ty.abiSize(self.target) >= 16); if (operand_ty.intInfo(self.target).bits > 128) { return self.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(self.target).bits}); } @@ -4093,6 +4094,10 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W return self.fail("TODO: Implement {{add/sub}}_with_overflow for integer bitsize: {d}", .{int_info.bits}); }; + if (wasm_bits == 128) { + return self.airAddSubWithOverflowBigInt(lhs_op, rhs_op, lhs_ty, self.air.typeOfIndex(inst), op); + } + const zero = switch (wasm_bits) { 32 => WValue{ .imm32 = 0 }, 64 => WValue{ .imm64 = 0 }, @@ -4144,6 +4149,64 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W return result_ptr; } +fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, result_ty: Type, op: Op) InnerError!WValue { + assert(op == .add or op == .sub); + const int_info = ty.intInfo(self.target); + const is_signed = int_info.signedness == .signed; + if (int_info.bits != 128) { + return self.fail("TODO: Implement @{{add/sub}}WithOverflow for integer bitsize '{d}'", .{int_info.bits}); + } + + const lhs_high_bit = try self.load(lhs, Type.u64, 0); + const lhs_low_bit = try self.load(lhs, Type.u64, 8); + const rhs_high_bit = try self.load(rhs, Type.u64, 0); + const rhs_low_bit = try self.load(rhs, Type.u64, 8); + + const low_op_res = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op); + const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op); + + const lt = if (op == .add) blk: { + break :blk try self.cmp(high_op_res, lhs_high_bit, Type.u64, .lt); + } else if (op == .sub) blk: { + break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt); + } else unreachable; + const tmp = try self.intcast(lt, Type.u32, Type.u64); + const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op); + + const overflow_bit = if (is_signed) blk: { + const xor_op = try self.binOp(lhs_low_bit, tmp_op, Type.u64, .xor); + const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor); + const to_wrap = if (op == .add) wrap: { + break :wrap try self.binOp(xor_low, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); + } else xor_low; + const wrap = try self.binOp(to_wrap, xor_op, Type.u64, .@"and"); + break :blk try self.cmp(wrap, .{ .imm64 = 0 }, Type.i64, .lt); // i64 because signed + } else blk: { + const eq = try self.cmp(tmp_op, lhs_low_bit, Type.u64, .eq); + const op_eq = try self.cmp(tmp_op, lhs_low_bit, Type.u64, if (op == .add) .lt else .gt); + + const first_arg = if (op == .sub) arg: { + break :arg try self.cmp(high_op_res, lhs_high_bit, Type.u64, .gt); + } else lt; + + try self.emitWValue(first_arg); + try self.emitWValue(op_eq); + try self.emitWValue(eq); + try self.addTag(.select); + + const overflow_bit = try self.allocLocal(Type.initTag(.u1)); + try self.addLabel(.local_set, overflow_bit.local); + break :blk overflow_bit; + }; + + const result_ptr = try self.allocStack(result_ty); + try self.store(result_ptr, high_op_res, Type.u64, 0); + try self.store(result_ptr, tmp_op, Type.u64, 8); + try self.store(result_ptr, overflow_bit, Type.initTag(.u1), 16); + + return result_ptr; +} + fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; From e252f92b9947e82f2473a37a74d5f9dc278a1c1d Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 17 May 2022 23:07:07 +0200 Subject: [PATCH 1565/2031] wasm: enable 128bit integer behavior tests --- test/behavior/align.zig | 3 --- test/behavior/bugs/421.zig | 1 - test/behavior/error.zig | 1 - test/behavior/int128.zig | 4 ---- test/behavior/math.zig | 4 ---- test/behavior/popcount.zig | 1 - test/behavior/struct.zig | 1 - test/behavior/widening.zig | 2 -- 8 files changed, 17 deletions(-) diff --git a/test/behavior/align.zig b/test/behavior/align.zig index b89cd5ee69..ffe53f9088 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -234,7 +234,6 @@ test "return error union with 128-bit integer" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; try expect(3 == try give()); } @@ -409,7 +408,6 @@ const DefaultAligned = struct { test "read 128-bit field from default aligned struct in stack memory" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @@ -428,7 +426,6 @@ var default_aligned_global = DefaultAligned{ test "read 128-bit field from default aligned struct in global memory" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; diff --git a/test/behavior/bugs/421.zig b/test/behavior/bugs/421.zig index a9545188c7..800d4e72c2 100644 --- a/test/behavior/bugs/421.zig +++ b/test/behavior/bugs/421.zig @@ -2,7 +2,6 @@ const builtin = @import("builtin"); const expect = @import("std").testing.expect; test "bitCast to array" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 3c19471705..9a2a1352d8 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -649,7 +649,6 @@ test "error union payload is properly aligned" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { a: u128, diff --git a/test/behavior/int128.zig b/test/behavior/int128.zig index 08c6dd0e4d..3a1b198aa1 100644 --- a/test/behavior/int128.zig +++ b/test/behavior/int128.zig @@ -5,7 +5,6 @@ const minInt = std.math.minInt; const builtin = @import("builtin"); test "uint128" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -24,7 +23,6 @@ test "uint128" { } test "undefined 128 bit int" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -42,7 +40,6 @@ test "undefined 128 bit int" { } test "int128" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -60,7 +57,6 @@ test "int128" { } test "truncate int128" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 2f8cf06ee7..60c71010d4 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -76,7 +76,6 @@ fn testClz() !void { } test "@clz big ints" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -361,8 +360,6 @@ fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int } test "binary not" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - try expect(comptime x: { break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101; }); @@ -938,7 +935,6 @@ test "@subWithOverflow" { test "@shlWithOverflow" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO { var result: u4 = undefined; diff --git a/test/behavior/popcount.zig b/test/behavior/popcount.zig index 9658e694a8..dbfeb64111 100644 --- a/test/behavior/popcount.zig +++ b/test/behavior/popcount.zig @@ -13,7 +13,6 @@ test "@popCount integers" { } test "@popCount 128bit integer" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 24365d49b7..ab7d761de0 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -844,7 +844,6 @@ test "non-packed struct with u128 entry in union" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const U = union(enum) { Num: u128, diff --git a/test/behavior/widening.zig b/test/behavior/widening.zig index 5c25f7f627..a6475e88e6 100644 --- a/test/behavior/widening.zig +++ b/test/behavior/widening.zig @@ -5,7 +5,6 @@ const builtin = @import("builtin"); const has_f80_rt = @import("builtin").cpu.arch == .x86_64; test "integer widening" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -29,7 +28,6 @@ test "integer widening u0 to u8" { } test "implicit unsigned integer to signed integer" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From 00f3d84f38c54e70716cf8e2908c899b49de1d88 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 May 2022 20:26:20 -0700 Subject: [PATCH 1566/2031] LLVM: support mixing extern and export with the same symbol name --- src/codegen/llvm.zig | 71 ++++++++++++++++++++++++++++++++++------- test/behavior/basic.zig | 6 +++- 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index dfb0f8f03b..fb7a2c39bc 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -395,11 +395,11 @@ pub const Object = struct { return slice.ptr; } - fn genErrorNameTable(self: *Object, comp: *Compilation) !void { + fn genErrorNameTable(self: *Object) !void { // If self.error_name_table is null, there was no instruction that actually referenced the error table. const error_name_table_ptr_global = self.error_name_table orelse return; - const mod = comp.bin_file.options.module.?; + const mod = self.module; const target = mod.getTarget(); const llvm_ptr_ty = self.context.intType(8).pointerType(0); // TODO: Address space @@ -413,8 +413,8 @@ pub const Object = struct { const slice_alignment = slice_ty.abiAlignment(target); const error_name_list = mod.error_name_list.items; - const llvm_errors = try comp.gpa.alloc(*const llvm.Value, error_name_list.len); - defer comp.gpa.free(llvm_errors); + const llvm_errors = try mod.gpa.alloc(*const llvm.Value, error_name_list.len); + defer mod.gpa.free(llvm_errors); llvm_errors[0] = llvm_slice_ty.getUndef(); for (llvm_errors[1..]) |*llvm_error, i| { @@ -447,10 +447,10 @@ pub const Object = struct { error_name_table_ptr_global.setInitializer(error_name_table_ptr); } - fn genCmpLtErrorsLenFunction(object: *Object, comp: *Compilation) !void { + fn genCmpLtErrorsLenFunction(object: *Object) !void { // If there is no such function in the module, it means the source code does not need it. const llvm_fn = object.llvm_module.getNamedFunction(lt_errors_fn_name) orelse return; - const mod = comp.bin_file.options.module.?; + const mod = object.module; const errors_len = mod.global_error_set.count(); // Delete previous implementation. We replace it with every flush() because the @@ -476,10 +476,10 @@ pub const Object = struct { _ = builder.buildRet(is_lt); } - fn genModuleLevelAssembly(object: *Object, comp: *Compilation) !void { - const mod = comp.bin_file.options.module.?; + fn genModuleLevelAssembly(object: *Object) !void { + const mod = object.module; if (mod.global_assembly.count() == 0) return; - var buffer = std.ArrayList(u8).init(comp.gpa); + var buffer = std.ArrayList(u8).init(mod.gpa); defer buffer.deinit(); var it = mod.global_assembly.iterator(); while (it.next()) |kv| { @@ -489,15 +489,53 @@ pub const Object = struct { object.llvm_module.setModuleInlineAsm2(buffer.items.ptr, buffer.items.len - 1); } + fn resolveExportExternCollisions(object: *Object) !void { + const mod = object.module; + + const export_keys = mod.decl_exports.keys(); + for (mod.decl_exports.values()) |export_list, i| { + const decl_index = export_keys[i]; + const llvm_global = object.decl_map.get(decl_index) orelse continue; + for (export_list) |exp| { + // Detect if the LLVM global has already been created as an extern. In such + // case, we need to replace all uses of it with this exported global. + // TODO update std.builtin.ExportOptions to have the name be a + // null-terminated slice. + const exp_name_z = try mod.gpa.dupeZ(u8, exp.options.name); + defer mod.gpa.free(exp_name_z); + + const other_global = object.getLlvmGlobal(exp_name_z.ptr) orelse continue; + if (other_global == llvm_global) continue; + + // replaceAllUsesWith requires the type to be unchanged. So we bitcast + // the new global to the old type and use that as the thing to replace + // old uses. + const new_global_ptr = llvm_global.constBitCast(other_global.typeOf()); + other_global.replaceAllUsesWith(new_global_ptr); + llvm_global.takeName(other_global); + other_global.deleteGlobal(); + // Problem: now we need to replace in the decl_map that + // the extern decl index points to this new global. However we don't + // know the decl index. + // Even if we did, a future incremental update to the extern would then + // treat the LLVM global as an extern rather than an export, so it would + // need a way to check that. + // This is a TODO that needs to be solved when making + // the LLVM backend support incremental compilation. + } + } + } + pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void { var sub_prog_node = prog_node.start("LLVM Emit Object", 0); sub_prog_node.activate(); sub_prog_node.context.refresh(); defer sub_prog_node.end(); - try self.genErrorNameTable(comp); - try self.genCmpLtErrorsLenFunction(comp); - try self.genModuleLevelAssembly(comp); + try self.resolveExportExternCollisions(); + try self.genErrorNameTable(); + try self.genCmpLtErrorsLenFunction(); + try self.genModuleLevelAssembly(); if (self.di_builder) |dib| { // When lowering debug info for pointers, we emitted the element types as @@ -761,6 +799,14 @@ pub const Object = struct { try self.updateDeclExports(module, decl_index, decl_exports); } + /// TODO replace this with a call to `Module::getNamedValue`. This will require adding + /// a new wrapper in zig_llvm.h/zig_llvm.cpp. + fn getLlvmGlobal(o: Object, name: [*:0]const u8) ?*const llvm.Value { + if (o.llvm_module.getNamedFunction(name)) |x| return x; + if (o.llvm_module.getNamedGlobal(name)) |x| return x; + return null; + } + pub fn updateDeclExports( self: *Object, module: *Module, @@ -827,6 +873,7 @@ pub const Object = struct { llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel); } } + // If a Decl is exported more than one time (which is rare), // we add aliases for all but the first export. // TODO LLVM C API does not support deleting aliases. We need to diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 12ead179f4..10e48c6c7b 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -797,7 +797,11 @@ test "auto created variables have correct alignment" { } test "extern variable with non-pointer opaque type" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @export(var_to_export, .{ .name = "opaque_extern_var" }); try expect(@ptrCast(*align(1) u32, &opaque_extern_var).* == 42); From 95f5e17e49d32a301d6a9d6f9948719d65469b09 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 May 2022 20:44:40 -0700 Subject: [PATCH 1567/2031] behavior tests: correction of C pointer test This test was also covering this behavior: ```zig test "equality of pointers to comptime const" { const a: i32 = undefined; comptime assert(&a == &a); } ``` This check belongs in its own behavior test which isolates this behavior; not bundled along with a C pointer test. --- test/behavior/eval.zig | 7 +++++++ test/behavior/pointers.zig | 8 ++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 007ea48d87..88be755b61 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -1110,3 +1110,10 @@ test "no dependency loop for alignment of self tagged union" { }; try S.doTheTest(); } + +test "equality of pointers to comptime const" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const a: i32 = undefined; + comptime assert(&a == &a); +} diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 5b9c3c8cf0..42939986d4 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -214,7 +214,10 @@ test "allowzero pointer and slice" { } test "assign null directly to C pointer and test null equality" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO var x: [*c]i32 = null; try expect(x == null); @@ -238,7 +241,8 @@ test "assign null directly to C pointer and test null equality" { @panic("fail"); } const othery: i32 = undefined; - comptime try expect((y orelse &othery) == &othery); + const ptr_othery = &othery; + comptime try expect((y orelse ptr_othery) == ptr_othery); var n: i32 = 1234; var x1: [*c]i32 = &n; From b6798c26efc4689cf35c5f4ac0436b4510a1f813 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 May 2022 01:52:02 -0700 Subject: [PATCH 1568/2031] stage2: fix pointer arithmetic result type This makes it so the result of doing pointer arithmetic creates a new pointer type that has adjusted alignment. --- lib/std/heap.zig | 2 +- src/Air.zig | 8 +- src/Liveness.zig | 31 +++--- src/Sema.zig | 70 +++++++++---- src/arch/aarch64/CodeGen.zig | 166 +++++++++++++++++------------- src/arch/arm/CodeGen.zig | 192 +++++++++++++++++++++-------------- src/arch/riscv64/CodeGen.zig | 23 ++++- src/arch/sparc64/CodeGen.zig | 119 +++++++++++++--------- src/arch/wasm/CodeGen.zig | 3 +- src/arch/x86_64/CodeGen.zig | 82 ++++++++------- src/codegen/c.zig | 36 +++---- src/codegen/llvm.zig | 6 +- src/print_air.zig | 23 ++--- src/value.zig | 22 ---- test/behavior/align.zig | 21 +++- test/behavior/pointers.zig | 2 - 16 files changed, 465 insertions(+), 341 deletions(-) diff --git a/lib/std/heap.zig b/lib/std/heap.zig index 65952f6c50..cbbe111a26 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -345,7 +345,7 @@ const PageAllocator = struct { // Unmap extra pages const aligned_buffer_len = alloc_len - drop_len; if (aligned_buffer_len > aligned_len) { - os.munmap(result_ptr[aligned_len..aligned_buffer_len]); + os.munmap(@alignCast(mem.page_size, result_ptr[aligned_len..aligned_buffer_len])); } const new_hint = @alignCast(mem.page_size, result_ptr + aligned_len); diff --git a/src/Air.zig b/src/Air.zig index 169449a066..8211e39454 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -113,13 +113,13 @@ pub const Inst = struct { /// The offset is in element type units, not bytes. /// Wrapping is undefined behavior. /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs. - /// Uses the `bin_op` field. + /// Uses the `ty_pl` field. Payload is `Bin`. ptr_add, /// Subtract an offset from a pointer, returning a new pointer. /// The offset is in element type units, not bytes. /// Wrapping is undefined behavior. /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs. - /// Uses the `bin_op` field. + /// Uses the `ty_pl` field. Payload is `Bin`. ptr_sub, /// Given two operands which can be floats, integers, or vectors, returns the /// greater of the operands. For vectors it operates element-wise. @@ -916,8 +916,6 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .bit_and, .bit_or, .xor, - .ptr_add, - .ptr_sub, .shr, .shr_exact, .shl, @@ -989,6 +987,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .sub_with_overflow, .mul_with_overflow, .shl_with_overflow, + .ptr_add, + .ptr_sub, => return air.getRefType(datas[inst].ty_pl.ty), .not, diff --git a/src/Liveness.zig b/src/Liveness.zig index c14c460d67..b37e220086 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -312,8 +312,6 @@ fn analyzeInst( .div_exact, .rem, .mod, - .ptr_add, - .ptr_sub, .bit_and, .bit_or, .xor, @@ -441,6 +439,21 @@ fn analyzeInst( return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none }); }, + .add_with_overflow, + .sub_with_overflow, + .mul_with_overflow, + .shl_with_overflow, + .ptr_add, + .ptr_sub, + .ptr_elem_ptr, + .slice_elem_ptr, + .slice, + => { + const ty_pl = inst_datas[inst].ty_pl; + const extra = a.air.extraData(Air.Bin, ty_pl.payload).data; + return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none }); + }, + .dbg_var_ptr, .dbg_var_val, => { @@ -529,10 +542,6 @@ fn analyzeInst( const extra = a.air.extraData(Air.FieldParentPtr, inst_datas[inst].ty_pl.payload).data; return trackOperands(a, new_set, inst, main_tomb, .{ extra.field_ptr, .none, .none }); }, - .ptr_elem_ptr, .slice_elem_ptr, .slice => { - 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 }); - }, .cmpxchg_strong, .cmpxchg_weak => { const extra = a.air.extraData(Air.Cmpxchg, inst_datas[inst].ty_pl.payload).data; return trackOperands(a, new_set, inst, main_tomb, .{ extra.ptr, extra.expected_value, extra.new_value }); @@ -558,15 +567,7 @@ fn analyzeInst( const extra = a.air.extraData(Air.Bin, pl_op.payload).data; return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs }); }, - .add_with_overflow, - .sub_with_overflow, - .mul_with_overflow, - .shl_with_overflow, - => { - const ty_pl = inst_datas[inst].ty_pl; - const extra = a.air.extraData(Air.Bin, ty_pl.payload).data; - return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none }); - }, + .br => { const br = inst_datas[inst].br; return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none }); diff --git a/src/Sema.zig b/src/Sema.zig index c45079eaa7..7086bb3f13 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -10610,28 +10610,55 @@ fn analyzePtrArithmetic( // TODO if the operand is comptime-known to be negative, or is a negative int, // coerce to isize instead of usize. const offset = try sema.coerce(block, Type.usize, uncasted_offset, offset_src); - // TODO adjust the return type according to alignment and other factors const target = sema.mod.getTarget(); - const runtime_src = rs: { - if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |ptr_val| { - if (try sema.resolveMaybeUndefVal(block, offset_src, offset)) |offset_val| { - const ptr_ty = sema.typeOf(ptr); - const new_ptr_ty = ptr_ty; // TODO modify alignment + const opt_ptr_val = try sema.resolveMaybeUndefVal(block, ptr_src, ptr); + const opt_off_val = try sema.resolveDefinedValue(block, offset_src, offset); + const ptr_ty = sema.typeOf(ptr); + const ptr_info = ptr_ty.ptrInfo().data; + const elem_ty = if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag() == .Array) + ptr_info.pointee_type.childType() + else + ptr_info.pointee_type; - if (ptr_val.isUndef() or offset_val.isUndef()) { - return sema.addConstUndef(new_ptr_ty); - } + const new_ptr_ty = t: { + // Calculate the new pointer alignment. + if (ptr_info.@"align" == 0) { + // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness. + break :t ptr_ty; + } + // If the addend is not a comptime-known value we can still count on + // it being a multiple of the type size. + const elem_size = elem_ty.abiSize(target); + const addend = if (opt_off_val) |off_val| a: { + const off_int = try sema.usizeCast(block, offset_src, off_val.toUnsignedInt(target)); + break :a elem_size * off_int; + } else elem_size; + + // The resulting pointer is aligned to the lcd between the offset (an + // arbitrary number) and the alignment factor (always a power of two, + // non zero). + const new_align = @as(u32, 1) << @intCast(u5, @ctz(u64, addend | ptr_info.@"align")); + + break :t try Type.ptr(sema.arena, sema.mod, .{ + .pointee_type = ptr_info.pointee_type, + .sentinel = ptr_info.sentinel, + .@"align" = new_align, + .@"addrspace" = ptr_info.@"addrspace", + .mutable = ptr_info.mutable, + .@"allowzero" = ptr_info.@"allowzero", + .@"volatile" = ptr_info.@"volatile", + .size = ptr_info.size, + }); + }; + + const runtime_src = rs: { + if (opt_ptr_val) |ptr_val| { + if (opt_off_val) |offset_val| { + if (ptr_val.isUndef()) return sema.addConstUndef(new_ptr_ty); const offset_int = try sema.usizeCast(block, offset_src, offset_val.toUnsignedInt(target)); - // TODO I tried to put this check earlier but it the LLVM backend generate invalid instructinons if (offset_int == 0) return ptr; if (try ptr_val.getUnsignedIntAdvanced(target, sema.kit(block, ptr_src))) |addr| { - const ptr_child_ty = ptr_ty.childType(); - const elem_ty = if (ptr_ty.isSinglePointer() and ptr_child_ty.zigTypeTag() == .Array) - ptr_child_ty.childType() - else - ptr_child_ty; - const elem_size = elem_ty.abiSize(target); const new_addr = switch (air_tag) { .ptr_add => addr + elem_size * offset_int, @@ -10651,7 +10678,16 @@ fn analyzePtrArithmetic( }; try sema.requireRuntimeBlock(block, runtime_src); - return block.addBinOp(air_tag, ptr, offset); + return block.addInst(.{ + .tag = air_tag, + .data = .{ .ty_pl = .{ + .ty = try sema.addType(new_ptr_ty), + .payload = try sema.addExtra(Air.Bin{ + .lhs = ptr, + .rhs = offset, + }), + } }, + }); } fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 95a29b840e..0993d352d7 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -545,18 +545,30 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off - .add, .ptr_add => try self.airBinOp(inst), - .addwrap => try self.airBinOp(inst), + .add => try self.airBinOp(inst, .add), + .addwrap => try self.airBinOp(inst, .addwrap), + .sub => try self.airBinOp(inst, .sub), + .subwrap => try self.airBinOp(inst, .subwrap), + .mul => try self.airBinOp(inst, .mul), + .mulwrap => try self.airBinOp(inst, .mulwrap), + .shl => try self.airBinOp(inst, .shl), + .shl_exact => try self.airBinOp(inst, .shl_exact), + .bool_and => try self.airBinOp(inst, .bool_and), + .bool_or => try self.airBinOp(inst, .bool_or), + .bit_and => try self.airBinOp(inst, .bit_and), + .bit_or => try self.airBinOp(inst, .bit_or), + .xor => try self.airBinOp(inst, .xor), + .shr => try self.airBinOp(inst, .shr), + .shr_exact => try self.airBinOp(inst, .shr_exact), + + .ptr_add => try self.airPtrArithmetic(inst, .ptr_add), + .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub), + .add_sat => try self.airAddSat(inst), - .sub, .ptr_sub => try self.airBinOp(inst), - .subwrap => try self.airBinOp(inst), .sub_sat => try self.airSubSat(inst), - .mul => try self.airBinOp(inst), - .mulwrap => try self.airBinOp(inst), .mul_sat => try self.airMulSat(inst), .rem => try self.airRem(inst), .mod => try self.airMod(inst), - .shl, .shl_exact => try self.airBinOp(inst), .shl_sat => try self.airShlSat(inst), .min => try self.airMin(inst), .max => try self.airMax(inst), @@ -595,13 +607,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_vector => try self.airCmpVector(inst), .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), - .bool_and => try self.airBinOp(inst), - .bool_or => try self.airBinOp(inst), - .bit_and => try self.airBinOp(inst), - .bit_or => try self.airBinOp(inst), - .xor => try self.airBinOp(inst), - .shr, .shr_exact => try self.airBinOp(inst), - .alloc => try self.airAlloc(inst), .ret_ptr => try self.airRetPtr(inst), .arg => try self.airArg(inst), @@ -1260,11 +1265,11 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { fn binOpRegister( self: *Self, mir_tag: Mir.Inst.Tag, - maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, lhs_ty: Type, rhs_ty: Type, + metadata: ?BinOpMetadata, ) !MCValue { const lhs_is_register = lhs == .register; const rhs_is_register = rhs == .register; @@ -1284,9 +1289,8 @@ fn binOpRegister( const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; const lhs_reg = if (lhs_is_register) lhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - break :inst Air.refToIndex(bin_op.lhs).?; + const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { + break :inst Air.refToIndex(md.lhs).?; } else null; const raw_reg = try self.register_manager.allocReg(track_inst); @@ -1300,9 +1304,8 @@ fn binOpRegister( defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - break :inst Air.refToIndex(bin_op.rhs).?; + const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { + break :inst Air.refToIndex(md.rhs).?; } else null; const raw_reg = try self.register_manager.allocReg(track_inst); @@ -1317,15 +1320,13 @@ fn binOpRegister( const dest_reg = switch (mir_tag) { .cmp_shifted_register => undefined, // cmp has no destination register - else => if (maybe_inst) |inst| blk: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - - if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { + else => if (metadata) |md| blk: { + if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) { break :blk lhs_reg; - } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { + } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) { break :blk rhs_reg; } else { - const raw_reg = try self.register_manager.allocReg(inst); + const raw_reg = try self.register_manager.allocReg(md.inst); break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); } } else blk: { @@ -1407,11 +1408,11 @@ fn binOpRegister( fn binOpImmediate( self: *Self, mir_tag: Mir.Inst.Tag, - maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, lhs_ty: Type, lhs_and_rhs_swapped: bool, + metadata: ?BinOpMetadata, ) !MCValue { const lhs_is_register = lhs == .register; @@ -1424,10 +1425,9 @@ fn binOpImmediate( const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; const lhs_reg = if (lhs_is_register) lhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { break :inst Air.refToIndex( - if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, + if (lhs_and_rhs_swapped) md.rhs else md.lhs, ).?; } else null; @@ -1443,18 +1443,16 @@ fn binOpImmediate( const dest_reg = switch (mir_tag) { .cmp_immediate => undefined, // cmp has no destination register - else => if (maybe_inst) |inst| blk: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - + else => if (metadata) |md| blk: { if (lhs_is_register and self.reuseOperand( - inst, - if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, + md.inst, + if (lhs_and_rhs_swapped) md.rhs else md.lhs, if (lhs_and_rhs_swapped) 1 else 0, lhs, )) { break :blk lhs_reg; } else { - const raw_reg = try self.register_manager.allocReg(inst); + const raw_reg = try self.register_manager.allocReg(md.inst); break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); } } else blk: { @@ -1498,6 +1496,12 @@ fn binOpImmediate( return MCValue{ .register = dest_reg }; } +const BinOpMetadata = struct { + inst: Air.Inst.Index, + lhs: Air.Inst.Ref, + rhs: Air.Inst.Ref, +}; + /// For all your binary operation needs, this function will generate /// the corresponding Mir instruction(s). Returns the location of the /// result. @@ -1513,11 +1517,11 @@ fn binOpImmediate( fn binOp( self: *Self, tag: Air.Inst.Tag, - maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, lhs_ty: Type, rhs_ty: Type, + metadata: ?BinOpMetadata, ) InnerError!MCValue { const mod = self.bin_file.options.module.?; switch (tag) { @@ -1562,12 +1566,12 @@ fn binOp( }; if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag_immediate, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, metadata); } else if (lhs_immediate_ok) { // swap lhs and rhs - return try self.binOpImmediate(mir_tag_immediate, maybe_inst, rhs, lhs, rhs_ty, true); + return try self.binOpImmediate(mir_tag_immediate, rhs, lhs, rhs_ty, true, metadata); } else { - return try self.binOpRegister(mir_tag_register, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata); } } else { return self.fail("TODO binary operations on int with bits > 64", .{}); @@ -1586,7 +1590,7 @@ fn binOp( // TODO add optimisations for multiplication // with immediates, for example a * 2 can be // lowered to a << 1 - return try self.binOpRegister(.mul, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(.mul, lhs, rhs, lhs_ty, rhs_ty, metadata); } else { return self.fail("TODO binary operations on int with bits > 64", .{}); } @@ -1606,7 +1610,7 @@ fn binOp( }; // Generate an add/sub/mul - const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); // Truncate if necessary switch (lhs_ty.zigTypeTag()) { @@ -1642,7 +1646,7 @@ fn binOp( else => unreachable, }; - return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } else { return self.fail("TODO binary operations on int with bits > 64", .{}); } @@ -1678,9 +1682,9 @@ fn binOp( }; if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag_immediate, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, metadata); } else { - return try self.binOpRegister(mir_tag_register, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata); } } else { return self.fail("TODO binary operations on int with bits > 64", .{}); @@ -1699,7 +1703,7 @@ fn binOp( }; // Generate a shl_exact/shr_exact - const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); // Truncate if necessary switch (tag) { @@ -1735,7 +1739,7 @@ fn binOp( else => unreachable, }; - return try self.binOpRegister(mir_tag_register, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata); }, else => unreachable, } @@ -1759,12 +1763,12 @@ fn binOp( else => unreachable, }; - return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } else { // convert the offset into a byte offset by // multiplying it with elem_size - const offset = try self.binOp(.mul, null, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize); - const addr = try self.binOp(tag, null, lhs, offset, Type.initTag(.manyptr_u8), Type.usize); + const offset = try self.binOp(.mul, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize, null); + const addr = try self.binOp(tag, lhs, offset, Type.initTag(.manyptr_u8), Type.usize, null); return addr; } }, @@ -1775,8 +1779,7 @@ fn binOp( } } -fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { - const tag = self.air.instructions.items(.tag)[inst]; +fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); @@ -1786,7 +1789,30 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { const result: MCValue = if (self.liveness.isUnused(inst)) .dead else - try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); + try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ + .inst = inst, + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + }); + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +} + +fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ + .inst = inst, + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + }); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1841,7 +1867,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { .sub_with_overflow => .sub, else => unreachable, }; - const dest = try self.binOp(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); + const dest = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, null); const dest_reg = dest.register; const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_reg_lock); @@ -1855,7 +1881,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); // cmp dest, truncated - _ = try self.binOp(.cmp_eq, null, dest, .{ .register = truncated_reg }, Type.usize, Type.usize); + _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, Type.usize, Type.usize, null); try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); @@ -1894,12 +1920,12 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { const dest = blk: { if (rhs_immediate_ok) { - break :blk try self.binOpImmediate(mir_tag_immediate, null, lhs, rhs, lhs_ty, false); + break :blk try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, null); } else if (lhs_immediate_ok) { // swap lhs and rhs - break :blk try self.binOpImmediate(mir_tag_immediate, null, rhs, lhs, rhs_ty, true); + break :blk try self.binOpImmediate(mir_tag_immediate, rhs, lhs, rhs_ty, true, null); } else { - break :blk try self.binOpRegister(mir_tag_register, null, lhs, rhs, lhs_ty, rhs_ty); + break :blk try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, null); } }; @@ -1952,7 +1978,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { .unsigned => .umull, }; - const dest = try self.binOpRegister(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); + const dest = try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, null); const dest_reg = dest.register; const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_reg_lock); @@ -2136,11 +2162,11 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { _ = try self.binOp( .cmp_eq, - null, .{ .register = dest_high_reg }, .{ .immediate = 0 }, Type.usize, Type.usize, + null, ); if (int_info.bits < 64) { @@ -2156,11 +2182,11 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { _ = try self.binOp( .cmp_eq, - null, .{ .register = dest_high_reg }, .{ .immediate = 0 }, Type.usize, Type.usize, + null, ); } }, @@ -2218,16 +2244,16 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { self.compare_flags_inst = null; // lsl dest, lhs, rhs - const dest = try self.binOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty); + const dest = try self.binOp(.shl, lhs, rhs, lhs_ty, rhs_ty, null); const dest_reg = dest.register; const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_reg_lock); // asr/lsr reconstructed, dest, rhs - const reconstructed = try self.binOp(.shr, null, dest, rhs, lhs_ty, rhs_ty); + const reconstructed = try self.binOp(.shr, dest, rhs, lhs_ty, rhs_ty, null); // cmp lhs, reconstructed - _ = try self.binOp(.cmp_eq, null, lhs, reconstructed, lhs_ty, lhs_ty); + _ = try self.binOp(.cmp_eq, lhs, reconstructed, lhs_ty, lhs_ty, null); try self.genSetStack(lhs_ty, stack_offset, dest); try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ @@ -2489,7 +2515,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { switch (elem_size) { else => { const dest = try self.allocRegOrMem(inst, true); - const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ptr_field_type, Type.usize); + const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ptr_field_type, Type.usize, null); try self.load(dest, addr, slice_ptr_field_type); break :result dest; @@ -2933,11 +2959,11 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde const dest = try self.binOp( .add, - null, .{ .register = addr_reg }, .{ .register = offset_reg }, Type.usize, Type.usize, + null, ); break :result dest; @@ -3302,7 +3328,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { const int_info = int_ty.intInfo(self.target.*); if (int_info.bits <= 64) { - _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty); + _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, BinOpMetadata{ + .inst = inst, + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + }); try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 0154548911..bfc7f687fa 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -552,21 +552,34 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off - .add, .ptr_add => try self.airBinOp(inst), - .addwrap => try self.airBinOp(inst), + .add, => try self.airBinOp(inst, .add), + .addwrap => try self.airBinOp(inst, .addwrap), + .sub, => try self.airBinOp(inst, .sub), + .subwrap => try self.airBinOp(inst, .subwrap), + .mul => try self.airBinOp(inst, .mul), + .mulwrap => try self.airBinOp(inst, .mulwrap), + .shl => try self.airBinOp(inst, .shl), + .shl_exact => try self.airBinOp(inst, .shl_exact), + .bool_and => try self.airBinOp(inst, .bool_and), + .bool_or => try self.airBinOp(inst, .bool_or), + .bit_and => try self.airBinOp(inst, .bit_and), + .bit_or => try self.airBinOp(inst, .bit_or), + .xor => try self.airBinOp(inst, .xor), + .shr => try self.airBinOp(inst, .shr), + .shr_exact => try self.airBinOp(inst, .shr_exact), + + .ptr_add => try self.airPtrArithmetic(inst, .ptr_add), + .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub), + + .min => try self.airMinMax(inst), + .max => try self.airMinMax(inst), + .add_sat => try self.airAddSat(inst), - .sub, .ptr_sub => try self.airBinOp(inst), - .subwrap => try self.airBinOp(inst), .sub_sat => try self.airSubSat(inst), - .mul => try self.airBinOp(inst), - .mulwrap => try self.airBinOp(inst), .mul_sat => try self.airMulSat(inst), .rem => try self.airRem(inst), .mod => try self.airMod(inst), - .shl, .shl_exact => try self.airBinOp(inst), .shl_sat => try self.airShlSat(inst), - .min => try self.airMinMax(inst), - .max => try self.airMinMax(inst), .slice => try self.airSlice(inst), .sqrt, @@ -602,13 +615,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_vector => try self.airCmpVector(inst), .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), - .bool_and => try self.airBinOp(inst), - .bool_or => try self.airBinOp(inst), - .bit_and => try self.airBinOp(inst), - .bit_or => try self.airBinOp(inst), - .xor => try self.airBinOp(inst), - .shr, .shr_exact => try self.airBinOp(inst), - .alloc => try self.airAlloc(inst), .ret_ptr => try self.airRetPtr(inst), .arg => try self.airArg(inst), @@ -1260,7 +1266,7 @@ fn minMax( // register. assert(lhs_reg != rhs_reg); // see note above - _ = try self.binOpRegister(.cmp, null, .{ .register = lhs_reg }, .{ .register = rhs_reg }, lhs_ty, rhs_ty); + _ = try self.binOpRegister(.cmp, .{ .register = lhs_reg }, .{ .register = rhs_reg }, lhs_ty, rhs_ty, null); const cond_choose_lhs: Condition = switch (tag) { .max => switch (int_info.signedness) { @@ -1340,15 +1346,40 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { - const tag = self.air.instructions.items(.tag)[inst]; +fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const lhs_ty = self.air.typeOf(bin_op.lhs); const rhs_ty = self.air.typeOf(bin_op.rhs); - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + .inst = inst, + }); + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +} + +fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + .inst = inst, + }); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1402,7 +1433,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { .sub_with_overflow => .sub, else => unreachable, }; - const dest = try self.binOp(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); + const dest = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, null); const dest_reg = dest.register; const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_reg_lock); @@ -1415,7 +1446,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); // cmp dest, truncated - _ = try self.binOp(.cmp_eq, null, dest, .{ .register = truncated_reg }, Type.usize, Type.usize); + _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, Type.usize, Type.usize, null); try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); @@ -1448,12 +1479,12 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { const dest = blk: { if (rhs_immediate_ok) { - break :blk try self.binOpImmediate(mir_tag, null, lhs, rhs, lhs_ty, false); + break :blk try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, null); } else if (lhs_immediate_ok) { // swap lhs and rhs - break :blk try self.binOpImmediate(mir_tag, null, rhs, lhs, rhs_ty, true); + break :blk try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, null); } else { - break :blk try self.binOpRegister(mir_tag, null, lhs, rhs, lhs_ty, rhs_ty); + break :blk try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, null); } }; @@ -1507,7 +1538,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { .unsigned => .mul, }; - const dest = try self.binOpRegister(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); + const dest = try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, null); const dest_reg = dest.register; const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_reg_lock); @@ -1520,7 +1551,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); // cmp dest, truncated - _ = try self.binOp(.cmp_eq, null, dest, .{ .register = truncated_reg }, Type.usize, Type.usize); + _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, Type.usize, Type.usize, null); try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); @@ -1594,7 +1625,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); // cmp truncated, rdlo - _ = try self.binOp(.cmp_eq, null, .{ .register = truncated_reg }, .{ .register = rdlo }, Type.usize, Type.usize); + _ = try self.binOp(.cmp_eq, .{ .register = truncated_reg }, .{ .register = rdlo }, Type.usize, Type.usize, null); // mov rdlo, #0 _ = try self.addInst(.{ @@ -1618,7 +1649,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { }); // cmp rdhi, #0 - _ = try self.binOp(.cmp_eq, null, .{ .register = rdhi }, .{ .immediate = 0 }, Type.usize, Type.usize); + _ = try self.binOp(.cmp_eq, .{ .register = rdhi }, .{ .immediate = 0 }, Type.usize, Type.usize, null); // movne rdlo, #1 _ = try self.addInst(.{ @@ -1677,16 +1708,16 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { self.compare_flags_inst = null; // lsl dest, lhs, rhs - const dest = try self.binOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty); + const dest = try self.binOp(.shl, lhs, rhs, lhs_ty, rhs_ty, null); const dest_reg = dest.register; const dest_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_lock); // asr/lsr reconstructed, dest, rhs - const reconstructed = try self.binOp(.shr, null, dest, rhs, lhs_ty, rhs_ty); + const reconstructed = try self.binOp(.shr, dest, rhs, lhs_ty, rhs_ty, null); // cmp lhs, reconstructed - _ = try self.binOp(.cmp_eq, null, lhs, reconstructed, lhs_ty, lhs_ty); + _ = try self.binOp(.cmp_eq, lhs, reconstructed, lhs_ty, lhs_ty, null); try self.genSetStack(lhs_ty, stack_offset, dest); try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); @@ -2031,7 +2062,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { }, else => { const dest = try self.allocRegOrMem(inst, true); - const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ptr_field_type, Type.usize); + const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ptr_field_type, Type.usize, null); try self.load(dest, addr, slice_ptr_field_type); break :result dest; @@ -2051,7 +2082,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { const slice_ty = self.air.typeOf(extra.lhs); - const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ty, Type.usize); + const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ty, Type.usize, null); break :result addr; }; return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); @@ -2079,7 +2110,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { const ptr_ty = self.air.typeOf(extra.lhs); - const addr = try self.binOp(.ptr_add, null, ptr_mcv, index_mcv, ptr_ty, Type.usize); + const addr = try self.binOp(.ptr_add, ptr_mcv, index_mcv, ptr_ty, Type.usize, null); break :result addr; }; return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); @@ -2411,11 +2442,11 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde const dest = try self.binOp( .add, - null, .{ .register = addr_reg }, .{ .register = offset_reg }, Type.usize, Type.usize, + null, ); break :result dest; @@ -2514,11 +2545,11 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { fn binOpRegister( self: *Self, mir_tag: Mir.Inst.Tag, - maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, lhs_ty: Type, rhs_ty: Type, + metadata: ?BinOpMetadata, ) !MCValue { const lhs_is_register = lhs == .register; const rhs_is_register = rhs == .register; @@ -2532,9 +2563,8 @@ fn binOpRegister( const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; const lhs_reg = if (lhs_is_register) lhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - break :inst Air.refToIndex(bin_op.lhs).?; + const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { + break :inst Air.refToIndex(md.lhs).?; } else null; const reg = try self.register_manager.allocReg(track_inst); @@ -2547,9 +2577,8 @@ fn binOpRegister( defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - break :inst Air.refToIndex(bin_op.rhs).?; + const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { + break :inst Air.refToIndex(md.rhs).?; } else null; const reg = try self.register_manager.allocReg(track_inst); @@ -2563,15 +2592,13 @@ fn binOpRegister( const dest_reg = switch (mir_tag) { .cmp => .r0, // cmp has no destination regardless - else => if (maybe_inst) |inst| blk: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - - if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { + else => if (metadata) |md| blk: { + if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) { break :blk lhs_reg; - } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { + } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) { break :blk rhs_reg; } else { - break :blk try self.register_manager.allocReg(inst); + break :blk try self.register_manager.allocReg(md.inst); } } else try self.register_manager.allocReg(null), }; @@ -2634,11 +2661,11 @@ fn binOpRegister( fn binOpImmediate( self: *Self, mir_tag: Mir.Inst.Tag, - maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, lhs_ty: Type, lhs_and_rhs_swapped: bool, + metadata: ?BinOpMetadata, ) !MCValue { const lhs_is_register = lhs == .register; @@ -2651,10 +2678,9 @@ fn binOpImmediate( const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; const lhs_reg = if (lhs_is_register) lhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { break :inst Air.refToIndex( - if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, + if (lhs_and_rhs_swapped) md.rhs else md.lhs, ).?; } else null; @@ -2669,18 +2695,16 @@ fn binOpImmediate( const dest_reg = switch (mir_tag) { .cmp => .r0, // cmp has no destination reg - else => if (maybe_inst) |inst| blk: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - + else => if (metadata) |md| blk: { if (lhs_is_register and self.reuseOperand( - inst, - if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, + md.inst, + if (lhs_and_rhs_swapped) md.rhs else md.lhs, if (lhs_and_rhs_swapped) 1 else 0, lhs, )) { break :blk lhs_reg; } else { - break :blk try self.register_manager.allocReg(inst); + break :blk try self.register_manager.allocReg(md.inst); } } else try self.register_manager.allocReg(null), }; @@ -2720,6 +2744,12 @@ fn binOpImmediate( return MCValue{ .register = dest_reg }; } +const BinOpMetadata = struct { + inst: Air.Inst.Index, + lhs: Air.Inst.Ref, + rhs: Air.Inst.Ref, +}; + /// For all your binary operation needs, this function will generate /// the corresponding Mir instruction(s). Returns the location of the /// result. @@ -2735,11 +2765,11 @@ fn binOpImmediate( fn binOp( self: *Self, tag: Air.Inst.Tag, - maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, lhs_ty: Type, rhs_ty: Type, + metadata: ?BinOpMetadata, ) InnerError!MCValue { switch (tag) { .add, @@ -2780,12 +2810,12 @@ fn binOp( }; if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); } else if (lhs_immediate_ok) { // swap lhs and rhs - return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true); + return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata); } else { - return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } } else { return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); @@ -2806,7 +2836,7 @@ fn binOp( // TODO add optimisations for multiplication // with immediates, for example a * 2 can be // lowered to a << 1 - return try self.binOpRegister(.mul, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(.mul, lhs, rhs, lhs_ty, rhs_ty, metadata); } else { return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); } @@ -2826,7 +2856,7 @@ fn binOp( }; // Generate an add/sub/mul - const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); // Truncate if necessary switch (lhs_ty.zigTypeTag()) { @@ -2869,12 +2899,12 @@ fn binOp( }; if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); } else if (lhs_immediate_ok) { // swap lhs and rhs - return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true); + return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata); } else { - return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } } else { return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); @@ -2903,9 +2933,9 @@ fn binOp( }; if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); } else { - return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } } else { return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); @@ -2924,7 +2954,7 @@ fn binOp( }; // Generate a shl_exact/shr_exact - const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); // Truncate if necessary switch (tag) { @@ -2964,12 +2994,12 @@ fn binOp( }; if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); } else if (lhs_immediate_ok) { // swap lhs and rhs - return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true); + return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata); } else { - return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } }, else => unreachable, @@ -2994,12 +3024,12 @@ fn binOp( else => unreachable, }; - return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } else { // convert the offset into a byte offset by // multiplying it with elem_size - const offset = try self.binOp(.mul, null, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize); - const addr = try self.binOp(tag, null, lhs, offset, Type.initTag(.manyptr_u8), Type.usize); + const offset = try self.binOp(.mul, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize, null); + const addr = try self.binOp(tag, lhs, offset, Type.initTag(.manyptr_u8), Type.usize, null); return addr; } }, @@ -3575,7 +3605,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; - _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty); + _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, BinOpMetadata{ + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + .inst = inst, + }); break :result switch (int_info.signedness) { .signed => MCValue{ .compare_flags_signed = op }, @@ -3865,7 +3899,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { } const error_mcv = try self.errUnionErr(operand, ty); - _ = try self.binOp(.cmp_eq, null, error_mcv, .{ .immediate = 0 }, error_int_type, error_int_type); + _ = try self.binOp(.cmp_eq, error_mcv, .{ .immediate = 0 }, error_int_type, error_int_type, null); return MCValue{ .compare_flags_unsigned = .gt }; } diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 22c97b9aec..c2f9b2e36d 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -481,10 +481,14 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off - .add, .ptr_add => try self.airBinOp(inst), + .ptr_add => try self.airPtrArithmetic(inst, .ptr_add), + .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub), + + .add => try self.airBinOp(inst, .add), + .sub => try self.airBinOp(inst, .sub), + .addwrap => try self.airAddWrap(inst), .add_sat => try self.airAddSat(inst), - .sub, .ptr_sub => try self.airBinOp(inst), .subwrap => try self.airSubWrap(inst), .sub_sat => try self.airSubSat(inst), .mul => try self.airMul(inst), @@ -1091,8 +1095,7 @@ fn binOp( } } -fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { - const tag = self.air.instructions.items(.tag)[inst]; +fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); @@ -1103,6 +1106,18 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +} + fn airAddWrap(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 addwrap for {}", .{self.target.cpu.arch}); diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 2ec8587273..aa679cac6d 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -483,10 +483,13 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off - .add, .ptr_add => try self.airBinOp(inst), + .ptr_add => try self.airPtrArithmetic(inst, .ptr_add), + .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub), + + .add => try self.airBinOp(inst, .add), .addwrap => @panic("TODO try self.airAddWrap(inst)"), .add_sat => @panic("TODO try self.airAddSat(inst)"), - .sub, .ptr_sub => @panic("TODO try self.airBinOp(inst)"), + .sub => @panic("TODO try self.airBinOp(inst)"), .subwrap => @panic("TODO try self.airSubWrap(inst)"), .sub_sat => @panic("TODO try self.airSubSat(inst)"), .mul => @panic("TODO try self.airMul(inst)"), @@ -827,18 +830,38 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, mcv, .{ .none, .none, .none }); } -fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { - const tag = self.air.instructions.items(.tag)[inst]; +fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const lhs_ty = self.air.typeOf(bin_op.lhs); const rhs_ty = self.air.typeOf(bin_op.rhs); - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else - try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); + try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + .inst = inst, + }); + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +} + +fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + .inst = inst, + }); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1030,7 +1053,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { var int_buffer: Type.Payload.Bits = undefined; const int_ty = switch (lhs_ty.zigTypeTag()) { - .Vector => unreachable, // Should be handled by cmp_vector? + .Vector => unreachable, // Handled by cmp_vector. .Enum => lhs_ty.intTagType(&int_buffer), .Int => lhs_ty, .Bool => Type.initTag(.u1), @@ -1053,7 +1076,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { const int_info = int_ty.intInfo(self.target.*); if (int_info.bits <= 64) { - _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty); + _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, BinOpMetadata{ + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + .inst = inst, + }); try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; @@ -1426,7 +1453,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { // TODO skip the ptr_add emission entirely and use native addressing modes // i.e sllx/mulx then R+R or scale immediate then R+I const dest = try self.allocRegOrMem(inst, true); - const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ptr_field_type, Type.usize); + const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ptr_field_type, Type.usize, null); try self.load(dest, addr, slice_ptr_field_type); break :result dest; @@ -1595,6 +1622,12 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { return MCValue{ .stack_offset = stack_offset }; } +const BinOpMetadata = struct { + inst: Air.Inst.Index, + lhs: Air.Inst.Ref, + rhs: Air.Inst.Ref, +}; + /// For all your binary operation needs, this function will generate /// the corresponding Mir instruction(s). Returns the location of the /// result. @@ -1610,11 +1643,11 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { fn binOp( self: *Self, tag: Air.Inst.Tag, - maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, lhs_ty: Type, rhs_ty: Type, + metadata: ?BinOpMetadata, ) InnerError!MCValue { const mod = self.bin_file.options.module.?; switch (tag) { @@ -1649,13 +1682,13 @@ fn binOp( }; if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); } else if (lhs_immediate_ok) { // swap lhs and rhs - return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true); + return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata); } else { // TODO convert large immediates to register before adding - return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } } else { return self.fail("TODO binary operations on int with bits > 64", .{}); @@ -1683,10 +1716,10 @@ fn binOp( // If it's a power of two immediate then we emit an shl instead // TODO add similar checks for LHS if (new_rhs == .immediate and math.isPowerOfTwo(new_rhs.immediate)) { - return try self.binOp(.shl, maybe_inst, new_lhs, .{ .immediate = math.log2(new_rhs.immediate) }, new_lhs_ty, Type.usize); + return try self.binOp(.shl, new_lhs, .{ .immediate = math.log2(new_rhs.immediate) }, new_lhs_ty, Type.usize, metadata); } - return try self.binOpRegister(.mulx, maybe_inst, new_lhs, new_rhs, new_lhs_ty, new_rhs_ty); + return try self.binOpRegister(.mulx, new_lhs, new_rhs, new_lhs_ty, new_rhs_ty, metadata); } else { return self.fail("TODO binary operations on int with bits > 64", .{}); } @@ -1711,13 +1744,13 @@ fn binOp( else => unreachable, }; - return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } else { // convert the offset into a byte offset by // multiplying it with elem_size - const offset = try self.binOp(.mul, null, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize); - const addr = try self.binOp(tag, null, lhs, offset, Type.initTag(.manyptr_u8), Type.usize); + const offset = try self.binOp(.mul, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize, null); + const addr = try self.binOp(tag, lhs, offset, Type.initTag(.manyptr_u8), Type.usize, null); return addr; } }, @@ -1732,7 +1765,7 @@ fn binOp( }; // Generate a shl_exact/shr_exact - const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); // Truncate if necessary switch (tag) { @@ -1768,9 +1801,9 @@ fn binOp( }; if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); } else { - return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } } else { return self.fail("TODO binary operations on int with bits > 64", .{}); @@ -1792,18 +1825,17 @@ fn binOp( /// op dest, lhs, #rhs_imm /// /// Set lhs_and_rhs_swapped to true iff inst.bin_op.lhs corresponds to -/// rhs and vice versa. This parameter is only used when maybe_inst != -/// null. +/// rhs and vice versa. This parameter is only used when metadata != null. /// /// Asserts that generating an instruction of that form is possible. fn binOpImmediate( self: *Self, mir_tag: Mir.Inst.Tag, - maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, lhs_ty: Type, lhs_and_rhs_swapped: bool, + metadata: ?BinOpMetadata, ) !MCValue { const lhs_is_register = lhs == .register; @@ -1816,10 +1848,9 @@ fn binOpImmediate( const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; const lhs_reg = if (lhs_is_register) lhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { break :inst Air.refToIndex( - if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, + if (lhs_and_rhs_swapped) md.rhs else md.lhs, ).?; } else null; @@ -1833,18 +1864,16 @@ fn binOpImmediate( defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const dest_reg = switch (mir_tag) { - else => if (maybe_inst) |inst| blk: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - + else => if (metadata) |md| blk: { if (lhs_is_register and self.reuseOperand( - inst, - if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, + md.inst, + if (lhs_and_rhs_swapped) md.rhs else md.lhs, if (lhs_and_rhs_swapped) 1 else 0, lhs, )) { break :blk lhs_reg; } else { - break :blk try self.register_manager.allocReg(inst); + break :blk try self.register_manager.allocReg(md.inst); } } else blk: { break :blk try self.register_manager.allocReg(null); @@ -1896,11 +1925,11 @@ fn binOpImmediate( fn binOpRegister( self: *Self, mir_tag: Mir.Inst.Tag, - maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, lhs_ty: Type, rhs_ty: Type, + metadata: ?BinOpMetadata, ) !MCValue { const lhs_is_register = lhs == .register; const rhs_is_register = rhs == .register; @@ -1920,9 +1949,8 @@ fn binOpRegister( const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; const lhs_reg = if (lhs_is_register) lhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - break :inst Air.refToIndex(bin_op.lhs).?; + const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { + break :inst Air.refToIndex(md.lhs).?; } else null; const reg = try self.register_manager.allocReg(track_inst); @@ -1934,9 +1962,8 @@ fn binOpRegister( defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - break :inst Air.refToIndex(bin_op.rhs).?; + const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { + break :inst Air.refToIndex(md.rhs).?; } else null; const reg = try self.register_manager.allocReg(track_inst); @@ -1948,15 +1975,13 @@ fn binOpRegister( defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); const dest_reg = switch (mir_tag) { - else => if (maybe_inst) |inst| blk: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - - if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { + else => if (metadata) |md| blk: { + if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) { break :blk lhs_reg; - } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { + } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) { break :blk rhs_reg; } else { - break :blk try self.register_manager.allocReg(inst); + break :blk try self.register_manager.allocReg(md.inst); } } else blk: { break :blk try self.register_manager.allocReg(null); @@ -3069,11 +3094,11 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde const dest = try self.binOp( .add, - null, .{ .register = addr_reg }, .{ .register = offset_reg }, Type.usize, Type.usize, + null, ); break :result dest; diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 60d03ccafd..a35589f043 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3397,7 +3397,8 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; - const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const ptr = try self.resolveInst(bin_op.lhs); const offset = try self.resolveInst(bin_op.rhs); const ptr_ty = self.air.typeOf(bin_op.lhs); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index bb3ebec5db..f1455b0591 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -574,23 +574,33 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off - .add => try self.airBinOp(inst), - .addwrap => try self.airBinOp(inst), - .add_sat => try self.airAddSat(inst), - .sub => try self.airBinOp(inst), - .subwrap => try self.airBinOp(inst), - .sub_sat => try self.airSubSat(inst), + .add => try self.airBinOp(inst, .add), + .addwrap => try self.airBinOp(inst, .addwrap), + .sub => try self.airBinOp(inst, .sub), + .subwrap => try self.airBinOp(inst, .subwrap), + .bool_and => try self.airBinOp(inst, .bool_and), + .bool_or => try self.airBinOp(inst, .bool_or), + .bit_and => try self.airBinOp(inst, .bit_and), + .bit_or => try self.airBinOp(inst, .bit_or), + .xor => try self.airBinOp(inst, .xor), + + .ptr_add => try self.airPtrArithmetic(inst, .ptr_add), + .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub), + + .shr, .shr_exact => try self.airShlShrBinOp(inst), + .shl, .shl_exact => try self.airShlShrBinOp(inst), + .mul => try self.airMulDivBinOp(inst), .mulwrap => try self.airMulDivBinOp(inst), - .mul_sat => try self.airMulSat(inst), .rem => try self.airMulDivBinOp(inst), .mod => try self.airMulDivBinOp(inst), - .shl, .shl_exact => try self.airShlShrBinOp(inst), + + .add_sat => try self.airAddSat(inst), + .sub_sat => try self.airSubSat(inst), + .mul_sat => try self.airMulSat(inst), .shl_sat => try self.airShlSat(inst), .min => try self.airMin(inst), .max => try self.airMax(inst), - .ptr_add => try self.airBinOp(inst), - .ptr_sub => try self.airBinOp(inst), .slice => try self.airSlice(inst), .sqrt, @@ -626,13 +636,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_vector => try self.airCmpVector(inst), .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), - .bool_and => try self.airBinOp(inst), - .bool_or => try self.airBinOp(inst), - .bit_and => try self.airBinOp(inst), - .bit_or => try self.airBinOp(inst), - .xor => try self.airBinOp(inst), - .shr, .shr_exact => try self.airShlShrBinOp(inst), - .alloc => try self.airAlloc(inst), .ret_ptr => try self.airRetPtr(inst), .arg => try self.airArg(inst), @@ -1231,21 +1234,26 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { +fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; if (self.liveness.isUnused(inst)) { return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); } - const tag = self.air.instructions.items(.tag)[inst]; - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - const lhs_ty = self.air.typeOf(bin_op.lhs); - const rhs_ty = self.air.typeOf(bin_op.rhs); + const result = try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs); + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +} - const result = try self.genBinOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); +fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + } + + const result = try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1316,13 +1324,12 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.spillRegisters(1, .{.rcx}); } - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - const partial: MCValue = switch (tag) { - .add_with_overflow => try self.genBinOp(.add, null, lhs, rhs, ty, ty), - .sub_with_overflow => try self.genBinOp(.sub, null, lhs, rhs, ty, ty), + .add_with_overflow => try self.genBinOp(null, .add, bin_op.lhs, bin_op.rhs), + .sub_with_overflow => try self.genBinOp(null, .sub, bin_op.lhs, bin_op.rhs), .shl_with_overflow => blk: { + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); const shift_ty = self.air.typeOf(bin_op.rhs); break :blk try self.genShiftBinOp(.shl, null, lhs, rhs, ty, shift_ty); }, @@ -3310,13 +3317,15 @@ fn genMulDivBinOp( /// Result is always a register. fn genBinOp( self: *Self, - tag: Air.Inst.Tag, maybe_inst: ?Air.Inst.Index, - lhs: MCValue, - rhs: MCValue, - lhs_ty: Type, - rhs_ty: Type, + tag: Air.Inst.Tag, + lhs_air: Air.Inst.Ref, + rhs_air: Air.Inst.Ref, ) !MCValue { + const lhs = try self.resolveInst(lhs_air); + const rhs = try self.resolveInst(rhs_air); + const lhs_ty = self.air.typeOf(lhs_air); + const rhs_ty = self.air.typeOf(rhs_air); if (lhs_ty.zigTypeTag() == .Vector or lhs_ty.zigTypeTag() == .Float) { return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()}); } @@ -3352,11 +3361,10 @@ fn genBinOp( var flipped: bool = false; const dst_mcv: MCValue = blk: { if (maybe_inst) |inst| { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - if (self.reuseOperand(inst, bin_op.lhs, 0, lhs) and lhs.isRegister()) { + if (self.reuseOperand(inst, lhs_air, 0, lhs) and lhs.isRegister()) { break :blk lhs; } - if (is_commutative and self.reuseOperand(inst, bin_op.rhs, 1, rhs) and rhs.isRegister()) { + if (is_commutative and self.reuseOperand(inst, rhs_air, 1, rhs) and rhs.isRegister()) { flipped = true; break :blk rhs; } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 92770168f4..5f61f8586e 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1711,21 +1711,18 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .unreach => try airUnreach(f), .fence => try airFence(f, inst), - // TODO use a different strategy for add that communicates to the optimizer - // that wrapping is UB. - .add => try airBinOp (f, inst, " + "), - .ptr_add => try airPtrAddSub (f, inst, " + "), - // TODO use a different strategy for sub that communicates to the optimizer - // that wrapping is UB. - .sub => try airBinOp (f, inst, " - "), - .ptr_sub => try airPtrAddSub (f, inst, " - "), - // TODO use a different strategy for mul that communicates to the optimizer - // that wrapping is UB. - .mul => try airBinOp (f, inst, " * "), - // TODO use a different strategy for div that communicates to the optimizer - // that wrapping is UB. + .ptr_add => try airPtrAddSub(f, inst, " + "), + .ptr_sub => try airPtrAddSub(f, inst, " - "), + + // TODO use a different strategy for add, sub, mul, div + // that communicates to the optimizer that wrapping is UB. + .add => try airBinOp (f, inst, " + "), + .sub => try airBinOp (f, inst, " - "), + .mul => try airBinOp (f, inst, " * "), .div_float, .div_exact => try airBinOp( f, inst, " / "), - .div_trunc => blk: { + .rem => try airBinOp( f, inst, " % "), + + .div_trunc => blk: { const bin_op = f.air.instructions.items(.data)[inst].bin_op; const lhs_ty = f.air.typeOf(bin_op.lhs); // For binary operations @TypeOf(lhs)==@TypeOf(rhs), @@ -1735,9 +1732,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO else try airBinOpBuiltinCall(f, inst, "div_trunc"); }, - .div_floor => try airBinOpBuiltinCall(f, inst, "div_floor"), - .rem => try airBinOp( f, inst, " % "), - .mod => try airBinOpBuiltinCall(f, inst, "mod"), + .div_floor => try airBinOpBuiltinCall(f, inst, "div_floor"), + .mod => try airBinOpBuiltinCall(f, inst, "mod"), .addwrap => try airWrapOp(f, inst, " + ", "addw_"), .subwrap => try airWrapOp(f, inst, " - ", "subw_"), @@ -2617,10 +2613,10 @@ fn airEquality( } fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue { - if (f.liveness.isUnused(inst)) - return CValue.none; + if (f.liveness.isUnused(inst)) return CValue.none; - const bin_op = f.air.instructions.items(.data)[inst].bin_op; + const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; + const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index fb7a2c39bc..aba290060a 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5679,7 +5679,8 @@ pub const FuncGen = struct { fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; - const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const base_ptr = try self.resolveInst(bin_op.lhs); const offset = try self.resolveInst(bin_op.rhs); const ptr_ty = self.air.typeOf(bin_op.lhs); @@ -5698,7 +5699,8 @@ pub const FuncGen = struct { fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; - const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const base_ptr = try self.resolveInst(bin_op.lhs); const offset = try self.resolveInst(bin_op.rhs); const negative_offset = self.builder.buildNeg(offset, ""); diff --git a/src/print_air.zig b/src/print_air.zig index 0524356fa7..e62ca806b7 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -114,8 +114,6 @@ const Writer = struct { .div_exact, .rem, .mod, - .ptr_add, - .ptr_sub, .bit_and, .bit_or, .xor, @@ -231,6 +229,12 @@ const Writer = struct { .slice, .slice_elem_ptr, .ptr_elem_ptr, + .ptr_add, + .ptr_sub, + .add_with_overflow, + .sub_with_overflow, + .mul_with_overflow, + .shl_with_overflow, => try w.writeTyPlBin(s, inst), .call, @@ -275,12 +279,6 @@ const Writer = struct { .reduce => try w.writeReduce(s, inst), .cmp_vector => try w.writeCmpVector(s, inst), - .add_with_overflow, - .sub_with_overflow, - .mul_with_overflow, - .shl_with_overflow, - => try w.writeOverflow(s, inst), - .dbg_block_begin, .dbg_block_end => {}, } } @@ -478,15 +476,6 @@ const Writer = struct { try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) }); } - fn writeOverflow(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 writeMemset(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const pl_op = w.air.instructions.items(.data)[inst].pl_op; const extra = w.air.extraData(Air.Bin, pl_op.payload).data; diff --git a/src/value.zig b/src/value.zig index b7764327c0..588c7d2832 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1813,27 +1813,6 @@ pub const Value = extern union { }; } - /// Asserts the value is numeric - pub fn isZero(self: Value) bool { - return switch (self.tag()) { - .zero, .the_only_possible_value => true, - .one => false, - - .int_u64 => self.castTag(.int_u64).?.data == 0, - .int_i64 => self.castTag(.int_i64).?.data == 0, - - .float_16 => self.castTag(.float_16).?.data == 0, - .float_32 => self.castTag(.float_32).?.data == 0, - .float_64 => self.castTag(.float_64).?.data == 0, - .float_80 => self.castTag(.float_80).?.data == 0, - .float_128 => self.castTag(.float_128).?.data == 0, - - .int_big_positive => self.castTag(.int_big_positive).?.asBigInt().eqZero(), - .int_big_negative => self.castTag(.int_big_negative).?.asBigInt().eqZero(), - else => unreachable, - }; - } - pub fn orderAgainstZero(lhs: Value) std.math.Order { return orderAgainstZeroAdvanced(lhs, null) catch unreachable; } @@ -3442,7 +3421,6 @@ pub const Value = extern union { const info = ty.intInfo(target); if (info.bits == 0) { - assert(val.isZero()); // Sema should guarantee return val; } diff --git a/test/behavior/align.zig b/test/behavior/align.zig index ffe53f9088..5b753aa737 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -16,11 +16,22 @@ test "global variable alignment" { const slice = @as(*align(4) [1]u8, &foo)[0..]; comptime try expect(@TypeOf(slice) == *align(4) [1]u8); } - { - var runtime_zero: usize = 0; - const slice = @as(*align(4) [1]u8, &foo)[runtime_zero..]; - comptime try expect(@TypeOf(slice) == []align(4) u8); - } +} + +test "slicing array of length 1 can assume runtime index is always zero" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + // TODO reevaluate this test case, because notice that you can + // change `runtime_zero` to be `1` and the test still passes for stage1. + // Reconsider also this code: + // var array: [4]u8 = undefined; + // var runtime: usize = 4; + // var ptr = array[runtime..]; + // _ = ptr; + + var runtime_zero: usize = 0; + const slice = @as(*align(4) [1]u8, &foo)[runtime_zero..]; + comptime try expect(@TypeOf(slice) == []align(4) u8); } test "default alignment allows unspecified in type syntax" { diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 42939986d4..07bab40135 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -377,8 +377,6 @@ test "pointer to array at fixed address" { } test "pointer arithmetic affects the alignment" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - { var ptr: [*]align(8) u32 = undefined; var x: usize = 1; From 0fafc8cc4409b35649c19af19f7c259ff570c0b5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 May 2022 15:13:20 -0700 Subject: [PATCH 1569/2031] std.Thread: insert a missing `@alignCast` stage1 has a missing compile error for this situation. --- lib/std/Thread.zig | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 9e29c82672..06903a9e91 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -893,6 +893,7 @@ const LinuxThreadImpl = struct { }; fn spawn(config: SpawnConfig, comptime f: anytype, args: anytype) !Impl { + const page_size = std.mem.page_size; const Args = @TypeOf(args); const Instance = struct { fn_args: Args, @@ -915,11 +916,11 @@ const LinuxThreadImpl = struct { var instance_offset: usize = undefined; const map_bytes = blk: { - var bytes: usize = std.mem.page_size; + var bytes: usize = page_size; guard_offset = bytes; - bytes += std.math.max(std.mem.page_size, config.stack_size); - bytes = std.mem.alignForward(bytes, std.mem.page_size); + bytes += std.math.max(page_size, config.stack_size); + bytes = std.mem.alignForward(bytes, page_size); stack_offset = bytes; bytes = std.mem.alignForward(bytes, linux.tls.tls_image.alloc_align); @@ -930,7 +931,7 @@ const LinuxThreadImpl = struct { instance_offset = bytes; bytes += @sizeOf(Instance); - bytes = std.mem.alignForward(bytes, std.mem.page_size); + bytes = std.mem.alignForward(bytes, page_size); break :blk bytes; }; @@ -954,7 +955,7 @@ const LinuxThreadImpl = struct { // map everything but the guard page as read/write os.mprotect( - mapped[guard_offset..], + @alignCast(page_size, mapped[guard_offset..]), os.PROT.READ | os.PROT.WRITE, ) catch |err| switch (err) { error.AccessDenied => unreachable, From 691fba38d8cf28a32e6d4a4cd2111c401e763d08 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 May 2022 15:21:33 -0700 Subject: [PATCH 1570/2031] enable passing behavior test --- test/behavior/cast.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 231fa25b2d..b98eaea37a 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -326,8 +326,7 @@ test "array coersion to undefined at runtime" { @setRuntimeSafety(true); - // TODO implement @setRuntimeSafety in stage2 - if (builtin.zig_backend != .stage1 and builtin.mode != .Debug and builtin.mode != .ReleaseSafe) { + if (builtin.mode != .Debug and builtin.mode != .ReleaseSafe) { return error.SkipZigTest; } From 9031cc54f27b4483c62ab904dd94063a9499c842 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 May 2022 16:51:35 -0700 Subject: [PATCH 1571/2031] Sema: implement `@intCast` for vectors --- src/Sema.zig | 95 +++++++++++++++++++++++++++++++----------- test/behavior/cast.zig | 6 ++- 2 files changed, 75 insertions(+), 26 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 7086bb3f13..5866cc21c9 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7020,22 +7020,25 @@ fn intCast( operand_src: LazySrcLoc, runtime_safety: bool, ) CompileError!Air.Inst.Ref { - // TODO: Add support for vectors - const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_ty); - _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand)); + const operand_ty = sema.typeOf(operand); + const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, dest_ty_src); + const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); if (try sema.isComptimeKnown(block, operand_src, operand)) { return sema.coerce(block, dest_ty, operand, operand_src); - } else if (dest_is_comptime_int) { + } else if (dest_scalar_ty.zigTypeTag() == .ComptimeInt) { return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{}); } + try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, dest_ty_src, operand_src); + const is_vector = dest_ty.zigTypeTag() == .Vector; + if ((try sema.typeHasOnePossibleValue(block, dest_ty_src, dest_ty))) |opv| { // requirement: intCast(u0, input) iff input == 0 if (runtime_safety and block.wantSafety()) { try sema.requireRuntimeBlock(block, operand_src); const target = sema.mod.getTarget(); - const wanted_info = dest_ty.intInfo(target); + const wanted_info = dest_scalar_ty.intInfo(target); const wanted_bits = wanted_info.bits; if (wanted_bits == 0) { @@ -7051,9 +7054,8 @@ fn intCast( try sema.requireRuntimeBlock(block, operand_src); if (runtime_safety and block.wantSafety()) { const target = sema.mod.getTarget(); - const operand_ty = sema.typeOf(operand); - const actual_info = operand_ty.intInfo(target); - const wanted_info = dest_ty.intInfo(target); + const actual_info = operand_scalar_ty.intInfo(target); + const wanted_info = dest_scalar_ty.intInfo(target); const actual_bits = actual_info.bits; const wanted_bits = wanted_info.bits; const actual_value_bits = actual_bits - @boolToInt(actual_info.signedness == .signed); @@ -7062,7 +7064,11 @@ fn intCast( // range shrinkage // requirement: int value fits into target type if (wanted_value_bits < actual_value_bits) { - const dest_max_val = try dest_ty.maxInt(sema.arena, target); + const dest_max_val_scalar = try dest_scalar_ty.maxInt(sema.arena, target); + const dest_max_val = if (is_vector) + try Value.Tag.repeated.create(sema.arena, dest_max_val_scalar) + else + dest_max_val_scalar; const dest_max = try sema.addConstant(operand_ty, dest_max_val); const diff = try block.addBinOp(.subwrap, dest_max, operand); @@ -7080,19 +7086,59 @@ fn intCast( } else dest_max_val; const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val); - const is_in_range = try block.addBinOp(.cmp_lte, diff_unsigned, dest_range); - try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data); + const ok = if (is_vector) ok: { + const is_in_range = try block.addCmpVector(diff_unsigned, dest_range, .lte, try sema.addType(operand_ty)); + const all_in_range = try block.addInst(.{ + .tag = .reduce, + .data = .{ .reduce = .{ + .operand = is_in_range, + .operation = .And, + } }, + }); + break :ok all_in_range; + } else ok: { + const is_in_range = try block.addBinOp(.cmp_lte, diff_unsigned, dest_range); + break :ok is_in_range; + }; + try sema.addSafetyCheck(block, ok, .cast_truncated_data); } else { - const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max); - try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data); + const ok = if (is_vector) ok: { + const is_in_range = try block.addCmpVector(diff, dest_max, .lte, try sema.addType(operand_ty)); + const all_in_range = try block.addInst(.{ + .tag = .reduce, + .data = .{ .reduce = .{ + .operand = is_in_range, + .operation = .And, + } }, + }); + break :ok all_in_range; + } else ok: { + const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max); + break :ok is_in_range; + }; + try sema.addSafetyCheck(block, ok, .cast_truncated_data); } - } - // no shrinkage, yes sign loss - // requirement: signed to unsigned >= 0 - else if (actual_info.signedness == .signed and wanted_info.signedness == .unsigned) { - const zero_inst = try sema.addConstant(operand_ty, Value.zero); - const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst); - try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data); + } else if (actual_info.signedness == .signed and wanted_info.signedness == .unsigned) { + // no shrinkage, yes sign loss + // requirement: signed to unsigned >= 0 + const ok = if (is_vector) ok: { + const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); + const zero_inst = try sema.addConstant(operand_ty, zero_val); + const is_in_range = try block.addCmpVector(operand, zero_inst, .lte, try sema.addType(operand_ty)); + const all_in_range = try block.addInst(.{ + .tag = .reduce, + .data = .{ .reduce = .{ + .operand = is_in_range, + .operation = .And, + } }, + }); + break :ok all_in_range; + } else ok: { + const zero_inst = try sema.addConstant(operand_ty, Value.zero); + const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst); + break :ok is_in_range; + }; + try sema.addSafetyCheck(block, ok, .cast_truncated_data); } } return block.addTyOp(.intcast, dest_ty, operand); @@ -14517,8 +14563,8 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const dest_scalar_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); const operand = sema.resolveInst(extra.rhs); const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_scalar_ty); - const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand, operand_src); const operand_ty = sema.typeOf(operand); + const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); const is_vector = operand_ty.zigTypeTag() == .Vector; const dest_ty = if (is_vector) try Type.vector(sema.arena, operand_ty.vectorLen(), dest_scalar_ty) @@ -14686,7 +14732,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); - const scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand, operand_src); + const scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); const target = sema.mod.getTarget(); const bits = scalar_ty.intInfo(target).bits; if (bits % 8 != 0) { @@ -14743,7 +14789,7 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); - _ = try sema.checkIntOrVectorAllowComptime(block, operand, operand_src); + _ = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); if (try sema.typeHasOnePossibleValue(block, operand_src, operand_ty)) |val| { return sema.addConstant(operand_ty, val); @@ -15095,10 +15141,9 @@ fn checkIntOrVector( fn checkIntOrVectorAllowComptime( sema: *Sema, block: *Block, - operand: Air.Inst.Ref, + operand_ty: Type, operand_src: LazySrcLoc, ) CompileError!Type { - const operand_ty = sema.typeOf(operand); switch (try operand_ty.zigTypeTagOrPoison()) { .Int, .ComptimeInt => return operand_ty, .Vector => { diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index b98eaea37a..6753b731f0 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -587,7 +587,11 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" { } test "vector casts" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { From 70d809e0bbc51efa78b54838cc64158b4d4a2dd2 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 11 May 2022 23:18:01 +0200 Subject: [PATCH 1572/2031] x64: add AVX registers and Vex prefix sub-encoder --- src/arch/x86_64/bits.zig | 211 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 209 insertions(+), 2 deletions(-) diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 02f032ab72..df056fab2c 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -8,7 +8,7 @@ const DW = std.dwarf; // zig fmt: off -/// Definitions of all of the x64 registers. The order is semantically meaningful. +/// Definitions of all of the general purpose x64 registers. The order is semantically meaningful. /// The registers are defined such that IDs go in descending order of 64-bit, /// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen /// registers. This results in some useful properties: @@ -126,6 +126,52 @@ pub const Register = enum(u7) { } }; +/// AVX registers. +/// TODO missing dwarfLocOp implementation. +/// TODO add support for AVX-512 +pub const AvxRegister = enum(u6) { + // 256-bit registers + ymm0, ymm1, ymm2, ymm3, ymm4, ymm5, ymm6, ymm7, + ymm8, ymm9, ymm10, ymm11, ymm12, ymm13, ymm14, ymm15, + + // 128-bit registers + xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, + xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, + + // Pseudo, used only for MIR to signify that the + // operand is not a register but an immediate, etc. + none, + + /// Returns the bit-width of the register. + pub fn size(self: AvxRegister) u4 { + return switch (@enumToInt(self)) { + 0...15 => 256, + 16...31 => 128, + else => unreachable, + }; + } + + /// This returns the 4-bit register ID. + pub fn id(self: AvxRegister) u4 { + return @truncate(u4, @enumToInt(self)); + } + + /// Like id, but only returns the lower 3 bits. + pub fn lowId(self: AvxRegister) u3 { + return @truncate(u3, @enumToInt(self)); + } + + /// Convert from any register to its 256 bit alias. + pub fn to256(self: AvxRegister) AvxRegister { + return @intToEnum(AvxRegister, self.id()); + } + + /// Convert from any register to its 128 bit alias. + pub fn to128(self: AvxRegister) AvxRegister { + return @intToEnum(AvxRegister, @as(u8, self.id()) + 16); + } +}; + // zig fmt: on /// Encoding helper functions for x86_64 instructions @@ -251,6 +297,98 @@ pub const Encoder = struct { self.code.appendAssumeCapacity(0x66); } + pub fn Vex(comptime count: comptime_int) type { + if (count < 2 or count > 3) { + @compileError("VEX prefix can either be 2- or 3-byte long"); + } + + return struct { + bytes: [count]u8 = switch (count) { + 2 => .{ 0xc5, 0xf8 }, + 3 => .{ 0xc4, 0xe1, 0xf8 }, + else => unreachable, + }, + + pub fn rex(self: *@This(), prefix: Rex) void { + const byte = &self.bytes[1]; + if (prefix.w) switch (count) { + 3 => self.bytes[2] &= 0b0111_1111, + else => unreachable, + }; + if (prefix.r) byte.* &= 0b0111_1111; + if (prefix.x) switch (count) { + 3 => byte.* &= 0b1011_1111, + else => unreachable, + }; + if (prefix.b) switch (count) { + 3 => byte.* &= 0b1101_1111, + else => unreachable, + }; + } + + pub fn leading_opcode_0f(self: *@This()) void { + switch (count) { + 3 => self.bytes[1] |= 0b0_0001, + else => {}, + } + } + + pub fn leading_opcode_0f_38(self: *@This()) void { + switch (count) { + 3 => self.bytes[1] |= 0b0_0010, + else => unreachable, + } + } + + pub fn leading_opcode_0f_3a(self: *@This()) void { + switch (count) { + 3 => self.bytes[1] |= 0b0_0011, + else => unreachable, + } + } + + pub fn reg(self: *@This(), register: u4) void { + const byte = &self.bytes[count - 1]; + const mask = 0b1_0000_111; + byte.* &= mask; + byte.* |= @intCast(u7, ~register) << 3; + } + + pub fn len_128(self: *@This()) void { + const byte = &self.bytes[count - 1]; + byte.* &= 0b0_11; + } + + pub fn len_256(self: *@This()) void { + const byte = &self.bytes[count - 1]; + byte.* |= 0b1_00; + } + + pub fn simd_prefix_66(self: *@This()) void { + const byte = &self.bytes[count - 1]; + byte.* |= 0b01; + } + + pub fn simd_prefix_f2(self: *@This()) void { + const byte = &self.bytes[count - 1]; + byte.* |= 0b11; + } + + pub fn simd_prefix_f3(self: *@This()) void { + const byte = &self.bytes[count - 1]; + byte.* |= 0b10; + } + }; + } + + pub fn vex_2byte(self: Self, prefix: Vex(2)) void { + self.code.appendSliceAssumeCapacity(&prefix.bytes); + } + + pub fn vex_3byte(self: Self, prefix: Vex(3)) void { + self.code.appendSliceAssumeCapacity(&prefix.bytes); + } + /// From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB pub const Rex = struct { /// Wide, enables 64-bit operation @@ -543,7 +681,7 @@ pub const Encoder = struct { } }; -test "x86_64 Encoder helpers" { +test "Encoder helpers - general purpose registers" { var code = ArrayList(u8).init(testing.allocator); defer code.deinit(); @@ -615,6 +753,75 @@ test "x86_64 Encoder helpers" { } } +test "Encoder helpers - Vex prefix" { + { + var vex_prefix = Encoder.Vex(2){}; + vex_prefix.rex(.{ + .r = true, + }); + try testing.expectEqualSlices(u8, &[_]u8{ 0xc5, 0x78 }, &vex_prefix.bytes); + } + + { + var vex_prefix = Encoder.Vex(2){}; + vex_prefix.reg(AvxRegister.xmm15.id()); + try testing.expectEqualSlices(u8, &[_]u8{ 0xc5, 0x80 }, &vex_prefix.bytes); + } + + { + var vex_prefix = Encoder.Vex(3){}; + vex_prefix.rex(.{ + .w = true, + .x = true, + }); + try testing.expectEqualSlices(u8, &[_]u8{ 0xc4, 0b101_0_0001, 0b0_1111_0_00 }, &vex_prefix.bytes); + } + + { + var vex_prefix = Encoder.Vex(3){}; + vex_prefix.rex(.{ + .w = true, + .r = true, + }); + vex_prefix.len_256(); + vex_prefix.leading_opcode_0f(); + vex_prefix.simd_prefix_66(); + try testing.expectEqualSlices(u8, &[_]u8{ 0xc4, 0b011_0_0001, 0b0_1111_1_01 }, &vex_prefix.bytes); + } + + var code = ArrayList(u8).init(testing.allocator); + defer code.deinit(); + + { + // vmovapd xmm1, xmm2 + const encoder = try Encoder.init(&code, 4); + var vex = Encoder.Vex(2){}; + vex.simd_prefix_66(); + encoder.vex_2byte(vex); // use 64 bit operation + encoder.opcode_1byte(0x28); + encoder.modRm_direct(0, AvxRegister.xmm1.lowId()); + try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0xF9, 0x28, 0xC1 }, code.items); + } + + { + try code.resize(0); + + // vmovhpd xmm13, xmm1, qword ptr [rip] + const encoder = try Encoder.init(&code, 9); + var vex = Encoder.Vex(2){}; + vex.len_128(); + vex.simd_prefix_66(); + vex.leading_opcode_0f(); + vex.rex(.{ .r = true }); + vex.reg(AvxRegister.xmm1.id()); + encoder.vex_2byte(vex); + encoder.opcode_1byte(0x16); + encoder.modRm_RIPDisp32(AvxRegister.xmm13.lowId()); + encoder.disp32(0); + try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0x71, 0x16, 0x2D, 0x00, 0x00, 0x00, 0x00 }, code.items); + } +} + // TODO add these registers to the enum and populate dwarfLocOp // // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register. // RA = (16, "RA"), From 875a16030c2e58f9f8b9a2b66b43b565e3932cf5 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 12 May 2022 17:40:54 +0200 Subject: [PATCH 1573/2031] x64: extend Emit to allow for AVX registers --- src/arch/x86_64/Emit.zig | 167 +++++++++++++++++++++++++++------------ src/arch/x86_64/bits.zig | 5 ++ 2 files changed, 122 insertions(+), 50 deletions(-) diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 57100abc0f..52183a542b 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -25,7 +25,8 @@ const MCValue = @import("CodeGen.zig").MCValue; const Mir = @import("Mir.zig"); const Module = @import("../../Module.zig"); const Instruction = bits.Instruction; -const Register = bits.Register; +const GpRegister = bits.Register; +const AvxRegister = bits.Register; const Type = @import("../../type.zig").Type; mir: Mir, @@ -248,7 +249,7 @@ fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { switch (ops.flags) { 0b00 => { // PUSH/POP reg - return lowerToOEnc(tag, ops.reg1, emit.code); + return lowerToOEnc(tag, .{ .register = ops.reg1 }, emit.code); }, 0b01 => { // PUSH/POP r/m64 @@ -271,6 +272,7 @@ fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { 0b11 => unreachable, } } + fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); const payload = emit.mir.instructions.items(.data)[inst].payload; @@ -283,9 +285,9 @@ fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.I try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ .disp = @bitCast(u32, -@intCast(i32, disp)), .base = ops.reg1, - }), reg.to64(), emit.code); + }), .{ .register = reg.to64() }, emit.code); } else { - try lowerToRmEnc(.mov, reg.to64(), RegisterOrMemory.mem(.qword_ptr, .{ + try lowerToRmEnc(.mov, .{ .register = reg.to64() }, RegisterOrMemory.mem(.qword_ptr, .{ .disp = @bitCast(u32, -@intCast(i32, disp)), .base = ops.reg1, }), emit.code); @@ -319,7 +321,7 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ .disp = imm }), emit.code); } // JMP/CALL reg - return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); + return lowerToMEnc(tag, RegisterOrMemory.reg(.{ .register = ops.reg1 }), emit.code); }, 0b10 => { // JMP/CALL r/m64 @@ -392,13 +394,13 @@ fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) Inne }, else => unreachable, }; - return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code); + return lowerToMEnc(tag, RegisterOrMemory.reg(.{ .register = ops.reg1.to8() }), emit.code); } fn mirCondMov(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); if (ops.flags == 0b00) { - return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); + return lowerToRmEnc(tag, .{ .register = ops.reg1 }, RegisterOrMemory.reg(.{ .register = ops.reg2 }), emit.code); } const imm = emit.mir.instructions.items(.data)[inst].imm; const ptr_size: Memory.PtrSize = switch (ops.flags) { @@ -407,7 +409,7 @@ fn mirCondMov(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { 0b10 => .dword_ptr, 0b11 => .qword_ptr, }; - return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(ptr_size, .{ + return lowerToRmEnc(tag, .{ .register = ops.reg1 }, RegisterOrMemory.mem(ptr_size, .{ .disp = imm, .base = ops.reg2, }), emit.code); @@ -428,10 +430,15 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { // I return lowerToIEnc(.@"test", imm, emit.code); } - return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, emit.code); + return lowerToMiEnc(.@"test", RegisterOrMemory.reg(.{ .register = ops.reg1 }), imm, emit.code); } // TEST r/m64, r64 - return lowerToMrEnc(.@"test", RegisterOrMemory.reg(ops.reg1), ops.reg2, emit.code); + return lowerToMrEnc( + .@"test", + RegisterOrMemory.reg(.{ .register = ops.reg1 }), + .{ .register = ops.reg2 }, + emit.code, + ); }, else => return emit.fail("TODO more TEST alternatives", .{}), } @@ -471,18 +478,18 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { // mov reg1, imm32 // MI const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToMiEnc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code); + return lowerToMiEnc(tag, RegisterOrMemory.reg(.{ .register = ops.reg1 }), imm, emit.code); } // mov reg1, reg2 // RM - return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); + return lowerToRmEnc(tag, .{ .register = ops.reg1 }, RegisterOrMemory.reg(.{ .register = ops.reg2 }), emit.code); }, 0b01 => { // mov reg1, [reg2 + imm32] // RM const imm = emit.mir.instructions.items(.data)[inst].imm; - const src_reg: ?Register = if (ops.reg2 == .none) null else ops.reg2; - return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ + const src_reg: ?GpRegister = if (ops.reg2 == .none) null else ops.reg2; + return lowerToRmEnc(tag, .{ .register = ops.reg1 }, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ .disp = imm, .base = src_reg, }), emit.code); @@ -497,7 +504,7 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg2.size()), .{ .disp = imm, .base = ops.reg1, - }), ops.reg2, emit.code); + }), .{ .register = ops.reg2 }, emit.code); }, 0b11 => { return emit.fail("TODO unused variant: mov reg1, reg2, 0b11", .{}); @@ -523,11 +530,16 @@ fn mirArithMemImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { } inline fn setRexWRegister(reg: Register) bool { - if (reg.size() == 64) return true; - return switch (reg) { - .ah, .bh, .ch, .dh => true, - else => false, - }; + switch (reg) { + .avx_register => return false, + .register => |r| { + if (r.size() == 64) return true; + return switch (r) { + .ah, .bh, .ch, .dh => true, + else => false, + }; + }, + } } inline fn immOpSize(u_imm: u32) u8 { @@ -550,7 +562,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void .scale = scale, .index = .rcx, }; - return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ + return lowerToRmEnc(tag, .{ .register = ops.reg1 }, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ .disp = imm, .base = ops.reg2, .scale_index = scale_index, @@ -578,7 +590,7 @@ fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void .disp = imm, .base = ops.reg1, .scale_index = scale_index, - }), ops.reg2, emit.code); + }), .{ .register = ops.reg2 }, emit.code); } fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { @@ -629,22 +641,27 @@ fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { switch (ops.flags) { 0b00 => { const tag: Tag = if (ops.reg2.size() == 32) .movsxd else .movsx; - return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); + return lowerToRmEnc( + tag, + .{ .register = ops.reg1 }, + RegisterOrMemory.reg(.{ .register = ops.reg2 }), + emit.code, + ); }, 0b01 => { - return lowerToRmEnc(.movsx, ops.reg1, RegisterOrMemory.mem(.byte_ptr, .{ + return lowerToRmEnc(.movsx, .{ .register = ops.reg1 }, RegisterOrMemory.mem(.byte_ptr, .{ .disp = imm, .base = ops.reg2, }), emit.code); }, 0b10 => { - return lowerToRmEnc(.movsx, ops.reg1, RegisterOrMemory.mem(.word_ptr, .{ + return lowerToRmEnc(.movsx, .{ .register = ops.reg1 }, RegisterOrMemory.mem(.word_ptr, .{ .disp = imm, .base = ops.reg2, }), emit.code); }, 0b11 => { - return lowerToRmEnc(.movsxd, ops.reg1, RegisterOrMemory.mem(.dword_ptr, .{ + return lowerToRmEnc(.movsxd, .{ .register = ops.reg1 }, RegisterOrMemory.mem(.dword_ptr, .{ .disp = imm, .base = ops.reg2, }), emit.code); @@ -659,16 +676,21 @@ fn mirMovZeroExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const imm = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].imm else undefined; switch (ops.flags) { 0b00 => { - return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); + return lowerToRmEnc( + .movzx, + .{ .register = ops.reg1 }, + RegisterOrMemory.reg(.{ .register = ops.reg2 }), + emit.code, + ); }, 0b01 => { - return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.mem(.byte_ptr, .{ + return lowerToRmEnc(.movzx, .{ .register = ops.reg1 }, RegisterOrMemory.mem(.byte_ptr, .{ .disp = imm, .base = ops.reg2, }), emit.code); }, 0b10 => { - return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.mem(.word_ptr, .{ + return lowerToRmEnc(.movzx, .{ .register = ops.reg1 }, RegisterOrMemory.mem(.word_ptr, .{ .disp = imm, .base = ops.reg2, }), emit.code); @@ -691,16 +713,16 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { if (ops.flags == 0b00) { // movabs reg, imm64 // OI - return lowerToOiEnc(.mov, ops.reg1, imm, emit.code); + return lowerToOiEnc(.mov, .{ .register = ops.reg1 }, imm, emit.code); } if (ops.reg1 == .none) { // movabs moffs64, rax // TD - return lowerToTdEnc(.mov, imm, ops.reg2, emit.code); + return lowerToTdEnc(.mov, imm, .{ .register = ops.reg2 }, emit.code); } // movabs rax, moffs64 // FD - return lowerToFdEnc(.mov, ops.reg1, imm, emit.code); + return lowerToFdEnc(.mov, .{ .register = ops.reg1 }, imm, emit.code); } fn mirFisttp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { @@ -751,18 +773,18 @@ fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { 0b00 => { // sal reg1, 1 // M1 - return lowerToM1Enc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); + return lowerToM1Enc(tag, RegisterOrMemory.reg(.{ .register = ops.reg1 }), emit.code); }, 0b01 => { // sal reg1, .cl // MC - return lowerToMcEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); + return lowerToMcEnc(tag, RegisterOrMemory.reg(.{ .register = ops.reg1 }), emit.code); }, 0b10 => { // sal reg1, imm8 // MI const imm = @truncate(u8, emit.mir.instructions.items(.data)[inst].imm); - return lowerToMiImm8Enc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code); + return lowerToMiImm8Enc(tag, RegisterOrMemory.reg(.{ .register = ops.reg1 }), imm, emit.code); }, 0b11 => { return emit.fail("TODO unused variant: SHIFT reg1, 0b11", .{}); @@ -774,7 +796,7 @@ fn mirMulDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); if (ops.reg1 != .none) { assert(ops.reg2 == .none); - return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); + return lowerToMEnc(tag, RegisterOrMemory.reg(.{ .register = ops.reg1 }), emit.code); } assert(ops.reg1 == .none); assert(ops.reg2 != .none); @@ -797,24 +819,35 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { 0b00 => { - return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); + return lowerToRmEnc( + .imul, + .{ .register = ops.reg1 }, + RegisterOrMemory.reg(.{ .register = ops.reg2 }), + emit.code, + ); }, 0b01 => { const imm = emit.mir.instructions.items(.data)[inst].imm; - const src_reg: ?Register = if (ops.reg2 == .none) null else ops.reg2; - return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{ + const src_reg: ?GpRegister = if (ops.reg2 == .none) null else ops.reg2; + return lowerToRmEnc(.imul, .{ .register = ops.reg1 }, RegisterOrMemory.mem(.qword_ptr, .{ .disp = imm, .base = src_reg, }), emit.code); }, 0b10 => { const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code); + return lowerToRmiEnc( + .imul, + .{ .register = ops.reg1 }, + RegisterOrMemory.reg(.{ .register = ops.reg2 }), + imm, + emit.code, + ); }, 0b11 => { const payload = emit.mir.instructions.items(.data)[inst].payload; const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; - return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{ + return lowerToRmiEnc(.imul, .{ .register = ops.reg1 }, RegisterOrMemory.mem(.qword_ptr, .{ .disp = imm_pair.dest_off, .base = ops.reg2, }), imm_pair.operand, emit.code); @@ -842,10 +875,10 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { // lea reg1, [reg2 + imm32] // RM const imm = emit.mir.instructions.items(.data)[inst].imm; - const src_reg: ?Register = if (ops.reg2 == .none) null else ops.reg2; + const src_reg: ?GpRegister = if (ops.reg2 == .none) null else ops.reg2; return lowerToRmEnc( .lea, - ops.reg1, + .{ .register = ops.reg1 }, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ .disp = imm, .base = src_reg, @@ -859,7 +892,7 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const start_offset = emit.code.items.len; try lowerToRmEnc( .lea, - ops.reg1, + .{ .register = ops.reg1 }, RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0), emit.code, ); @@ -873,14 +906,14 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { 0b10 => { // lea reg, [rbp + rcx + imm32] const imm = emit.mir.instructions.items(.data)[inst].imm; - const src_reg: ?Register = if (ops.reg2 == .none) null else ops.reg2; + const src_reg: ?GpRegister = if (ops.reg2 == .none) null else ops.reg2; const scale_index = ScaleIndex{ .scale = 0, .index = .rcx, }; return lowerToRmEnc( .lea, - ops.reg1, + .{ .register = ops.reg1 }, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ .disp = imm, .base = src_reg, @@ -903,7 +936,7 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { // RM try lowerToRmEnc( .lea, - ops.reg1, + .{ .register = ops.reg1 }, RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0), emit.code, ); @@ -1489,11 +1522,11 @@ inline fn getModRmExt(tag: Tag) ?u3 { const ScaleIndex = struct { scale: u2, - index: Register, + index: GpRegister, }; const Memory = struct { - base: ?Register, + base: ?GpRegister, rip: bool = false, disp: u32, ptr_size: PtrSize, @@ -1595,6 +1628,40 @@ fn encodeImm(encoder: Encoder, imm: u32, size: u64) void { } } +const Register = union(enum) { + register: GpRegister, + avx_register: AvxRegister, + + fn reg(register: GpRegister) Register { + return .{ .register = register }; + } + + fn avxReg(register: AvxRegister) Register { + return .{ .avx_register = register }; + } + + fn lowId(register: Register) u3 { + return switch (register) { + .register => |r| r.lowId(), + .avx_register => |r| r.lowId(), + }; + } + + fn size(register: Register) u64 { + return switch (register) { + .register => |r| r.size(), + .avx_register => |r| r.size(), + }; + } + + fn isExtended(register: Register) bool { + return switch (register) { + .register => |r| r.isExtended(), + .avx_register => |r| r.isExtended(), + }; + } +}; + const RegisterOrMemory = union(enum) { register: Register, memory: Memory, @@ -1605,7 +1672,7 @@ const RegisterOrMemory = union(enum) { fn mem(ptr_size: Memory.PtrSize, args: struct { disp: u32, - base: ?Register = null, + base: ?GpRegister = null, scale_index: ?ScaleIndex = null, }) RegisterOrMemory { return .{ diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index df056fab2c..43d5b92eb2 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -151,6 +151,11 @@ pub const AvxRegister = enum(u6) { }; } + /// Returns whether the register is *extended*. + pub fn isExtended(self: Register) bool { + return @enumToInt(self) & 0x08 != 0; + } + /// This returns the 4-bit register ID. pub fn id(self: AvxRegister) u4 { return @truncate(u4, @enumToInt(self)); From 019cc94ec7291c652b3e18980194e028f8df9ec1 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 12 May 2022 20:52:52 +0200 Subject: [PATCH 1574/2031] x64: clean up populating VEX prefix --- src/arch/x86_64/Emit.zig | 217 +++++++++++++++++++------------------- src/arch/x86_64/bits.zig | 220 ++++++++++++++++++++++----------------- 2 files changed, 229 insertions(+), 208 deletions(-) diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 52183a542b..848371f4ba 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -249,7 +249,7 @@ fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { switch (ops.flags) { 0b00 => { // PUSH/POP reg - return lowerToOEnc(tag, .{ .register = ops.reg1 }, emit.code); + return lowerToOEnc(tag, Register.reg(ops.reg1), emit.code); }, 0b01 => { // PUSH/POP r/m64 @@ -285,9 +285,9 @@ fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.I try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ .disp = @bitCast(u32, -@intCast(i32, disp)), .base = ops.reg1, - }), .{ .register = reg.to64() }, emit.code); + }), Register.reg(reg.to64()), emit.code); } else { - try lowerToRmEnc(.mov, .{ .register = reg.to64() }, RegisterOrMemory.mem(.qword_ptr, .{ + try lowerToRmEnc(.mov, Register.reg(reg.to64()), RegisterOrMemory.mem(.qword_ptr, .{ .disp = @bitCast(u32, -@intCast(i32, disp)), .base = ops.reg1, }), emit.code); @@ -321,7 +321,7 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { return lowerToMEnc(tag, RegisterOrMemory.mem(ptr_size, .{ .disp = imm }), emit.code); } // JMP/CALL reg - return lowerToMEnc(tag, RegisterOrMemory.reg(.{ .register = ops.reg1 }), emit.code); + return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); }, 0b10 => { // JMP/CALL r/m64 @@ -394,13 +394,13 @@ fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) Inne }, else => unreachable, }; - return lowerToMEnc(tag, RegisterOrMemory.reg(.{ .register = ops.reg1.to8() }), emit.code); + return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code); } fn mirCondMov(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); if (ops.flags == 0b00) { - return lowerToRmEnc(tag, .{ .register = ops.reg1 }, RegisterOrMemory.reg(.{ .register = ops.reg2 }), emit.code); + return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), emit.code); } const imm = emit.mir.instructions.items(.data)[inst].imm; const ptr_size: Memory.PtrSize = switch (ops.flags) { @@ -409,7 +409,7 @@ fn mirCondMov(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { 0b10 => .dword_ptr, 0b11 => .qword_ptr, }; - return lowerToRmEnc(tag, .{ .register = ops.reg1 }, RegisterOrMemory.mem(ptr_size, .{ + return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.mem(ptr_size, .{ .disp = imm, .base = ops.reg2, }), emit.code); @@ -430,13 +430,13 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { // I return lowerToIEnc(.@"test", imm, emit.code); } - return lowerToMiEnc(.@"test", RegisterOrMemory.reg(.{ .register = ops.reg1 }), imm, emit.code); + return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, emit.code); } // TEST r/m64, r64 return lowerToMrEnc( .@"test", - RegisterOrMemory.reg(.{ .register = ops.reg1 }), - .{ .register = ops.reg2 }, + RegisterOrMemory.reg(ops.reg1), + Register.reg(ops.reg2), emit.code, ); }, @@ -478,18 +478,18 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { // mov reg1, imm32 // MI const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToMiEnc(tag, RegisterOrMemory.reg(.{ .register = ops.reg1 }), imm, emit.code); + return lowerToMiEnc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code); } // mov reg1, reg2 // RM - return lowerToRmEnc(tag, .{ .register = ops.reg1 }, RegisterOrMemory.reg(.{ .register = ops.reg2 }), emit.code); + return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), emit.code); }, 0b01 => { // mov reg1, [reg2 + imm32] // RM const imm = emit.mir.instructions.items(.data)[inst].imm; const src_reg: ?GpRegister = if (ops.reg2 == .none) null else ops.reg2; - return lowerToRmEnc(tag, .{ .register = ops.reg1 }, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ + return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ .disp = imm, .base = src_reg, }), emit.code); @@ -504,7 +504,7 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg2.size()), .{ .disp = imm, .base = ops.reg1, - }), .{ .register = ops.reg2 }, emit.code); + }), Register.reg(ops.reg2), emit.code); }, 0b11 => { return emit.fail("TODO unused variant: mov reg1, reg2, 0b11", .{}); @@ -562,7 +562,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void .scale = scale, .index = .rcx, }; - return lowerToRmEnc(tag, .{ .register = ops.reg1 }, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ + return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ .disp = imm, .base = ops.reg2, .scale_index = scale_index, @@ -590,7 +590,7 @@ fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void .disp = imm, .base = ops.reg1, .scale_index = scale_index, - }), .{ .register = ops.reg2 }, emit.code); + }), Register.reg(ops.reg2), emit.code); } fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { @@ -641,27 +641,22 @@ fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { switch (ops.flags) { 0b00 => { const tag: Tag = if (ops.reg2.size() == 32) .movsxd else .movsx; - return lowerToRmEnc( - tag, - .{ .register = ops.reg1 }, - RegisterOrMemory.reg(.{ .register = ops.reg2 }), - emit.code, - ); + return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), emit.code); }, 0b01 => { - return lowerToRmEnc(.movsx, .{ .register = ops.reg1 }, RegisterOrMemory.mem(.byte_ptr, .{ + return lowerToRmEnc(.movsx, Register.reg(ops.reg1), RegisterOrMemory.mem(.byte_ptr, .{ .disp = imm, .base = ops.reg2, }), emit.code); }, 0b10 => { - return lowerToRmEnc(.movsx, .{ .register = ops.reg1 }, RegisterOrMemory.mem(.word_ptr, .{ + return lowerToRmEnc(.movsx, Register.reg(ops.reg1), RegisterOrMemory.mem(.word_ptr, .{ .disp = imm, .base = ops.reg2, }), emit.code); }, 0b11 => { - return lowerToRmEnc(.movsxd, .{ .register = ops.reg1 }, RegisterOrMemory.mem(.dword_ptr, .{ + return lowerToRmEnc(.movsxd, Register.reg(ops.reg1), RegisterOrMemory.mem(.dword_ptr, .{ .disp = imm, .base = ops.reg2, }), emit.code); @@ -676,21 +671,16 @@ fn mirMovZeroExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const imm = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].imm else undefined; switch (ops.flags) { 0b00 => { - return lowerToRmEnc( - .movzx, - .{ .register = ops.reg1 }, - RegisterOrMemory.reg(.{ .register = ops.reg2 }), - emit.code, - ); + return lowerToRmEnc(.movzx, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), emit.code); }, 0b01 => { - return lowerToRmEnc(.movzx, .{ .register = ops.reg1 }, RegisterOrMemory.mem(.byte_ptr, .{ + return lowerToRmEnc(.movzx, Register.reg(ops.reg1), RegisterOrMemory.mem(.byte_ptr, .{ .disp = imm, .base = ops.reg2, }), emit.code); }, 0b10 => { - return lowerToRmEnc(.movzx, .{ .register = ops.reg1 }, RegisterOrMemory.mem(.word_ptr, .{ + return lowerToRmEnc(.movzx, Register.reg(ops.reg1), RegisterOrMemory.mem(.word_ptr, .{ .disp = imm, .base = ops.reg2, }), emit.code); @@ -713,16 +703,16 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { if (ops.flags == 0b00) { // movabs reg, imm64 // OI - return lowerToOiEnc(.mov, .{ .register = ops.reg1 }, imm, emit.code); + return lowerToOiEnc(.mov, Register.reg(ops.reg1), imm, emit.code); } if (ops.reg1 == .none) { // movabs moffs64, rax // TD - return lowerToTdEnc(.mov, imm, .{ .register = ops.reg2 }, emit.code); + return lowerToTdEnc(.mov, imm, Register.reg(ops.reg2), emit.code); } // movabs rax, moffs64 // FD - return lowerToFdEnc(.mov, .{ .register = ops.reg1 }, imm, emit.code); + return lowerToFdEnc(.mov, Register.reg(ops.reg1), imm, emit.code); } fn mirFisttp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { @@ -773,18 +763,18 @@ fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { 0b00 => { // sal reg1, 1 // M1 - return lowerToM1Enc(tag, RegisterOrMemory.reg(.{ .register = ops.reg1 }), emit.code); + return lowerToM1Enc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); }, 0b01 => { // sal reg1, .cl // MC - return lowerToMcEnc(tag, RegisterOrMemory.reg(.{ .register = ops.reg1 }), emit.code); + return lowerToMcEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); }, 0b10 => { // sal reg1, imm8 // MI const imm = @truncate(u8, emit.mir.instructions.items(.data)[inst].imm); - return lowerToMiImm8Enc(tag, RegisterOrMemory.reg(.{ .register = ops.reg1 }), imm, emit.code); + return lowerToMiImm8Enc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code); }, 0b11 => { return emit.fail("TODO unused variant: SHIFT reg1, 0b11", .{}); @@ -796,7 +786,7 @@ fn mirMulDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); if (ops.reg1 != .none) { assert(ops.reg2 == .none); - return lowerToMEnc(tag, RegisterOrMemory.reg(.{ .register = ops.reg1 }), emit.code); + return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); } assert(ops.reg1 == .none); assert(ops.reg2 != .none); @@ -819,35 +809,24 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { 0b00 => { - return lowerToRmEnc( - .imul, - .{ .register = ops.reg1 }, - RegisterOrMemory.reg(.{ .register = ops.reg2 }), - emit.code, - ); + return lowerToRmEnc(.imul, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), emit.code); }, 0b01 => { const imm = emit.mir.instructions.items(.data)[inst].imm; const src_reg: ?GpRegister = if (ops.reg2 == .none) null else ops.reg2; - return lowerToRmEnc(.imul, .{ .register = ops.reg1 }, RegisterOrMemory.mem(.qword_ptr, .{ + return lowerToRmEnc(.imul, Register.reg(ops.reg1), RegisterOrMemory.mem(.qword_ptr, .{ .disp = imm, .base = src_reg, }), emit.code); }, 0b10 => { const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToRmiEnc( - .imul, - .{ .register = ops.reg1 }, - RegisterOrMemory.reg(.{ .register = ops.reg2 }), - imm, - emit.code, - ); + return lowerToRmiEnc(.imul, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), imm, emit.code); }, 0b11 => { const payload = emit.mir.instructions.items(.data)[inst].payload; const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; - return lowerToRmiEnc(.imul, .{ .register = ops.reg1 }, RegisterOrMemory.mem(.qword_ptr, .{ + return lowerToRmiEnc(.imul, Register.reg(ops.reg1), RegisterOrMemory.mem(.qword_ptr, .{ .disp = imm_pair.dest_off, .base = ops.reg2, }), imm_pair.operand, emit.code); @@ -878,7 +857,7 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const src_reg: ?GpRegister = if (ops.reg2 == .none) null else ops.reg2; return lowerToRmEnc( .lea, - .{ .register = ops.reg1 }, + Register.reg(ops.reg1), RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ .disp = imm, .base = src_reg, @@ -892,7 +871,7 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const start_offset = emit.code.items.len; try lowerToRmEnc( .lea, - .{ .register = ops.reg1 }, + Register.reg(ops.reg1), RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0), emit.code, ); @@ -913,7 +892,7 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { }; return lowerToRmEnc( .lea, - .{ .register = ops.reg1 }, + Register.reg(ops.reg1), RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ .disp = imm, .base = src_reg, @@ -936,7 +915,7 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { // RM try lowerToRmEnc( .lea, - .{ .register = ops.reg1 }, + Register.reg(ops.reg1), RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0), emit.code, ); @@ -1666,8 +1645,12 @@ const RegisterOrMemory = union(enum) { register: Register, memory: Memory, - fn reg(register: Register) RegisterOrMemory { - return .{ .register = register }; + fn reg(register: GpRegister) RegisterOrMemory { + return .{ .register = Register.reg(register) }; + } + + fn avxReg(register: AvxRegister) RegisterOrMemory { + return .{ .register = Register.avxReg(register) }; } fn mem(ptr_size: Memory.PtrSize, args: struct { @@ -1976,22 +1959,32 @@ fn lowerToMrEnc( encoder.modRm_direct(reg.lowId(), dst_reg.lowId()); }, .memory => |dst_mem| { - const encoder = try Encoder.init(code, 9); - if (reg.size() == 16) { - encoder.prefix16BitMode(); - } - if (dst_mem.base) |base| { - encoder.rex(.{ - .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), - .r = reg.isExtended(), - .b = base.isExtended(), - }); - } else { - encoder.rex(.{ - .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), - .r = reg.isExtended(), - }); - } + const encoder = blk: { + switch (reg) { + .register => { + const encoder = try Encoder.init(code, 9); + if (reg.size() == 16) { + encoder.prefix16BitMode(); + } + if (dst_mem.base) |base| { + encoder.rex(.{ + .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), + .r = reg.isExtended(), + .b = base.isExtended(), + }); + } else { + encoder.rex(.{ + .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), + .r = reg.isExtended(), + }); + } + break :blk encoder; + }, + .avx_register => { + unreachable; + }, + } + }; opc.encode(encoder); dst_mem.encode(encoder, reg.lowId()); }, @@ -2168,23 +2161,23 @@ test "lower MI encoding" { test "lower RM encoding" { var emit = TestEmit.init(); defer emit.deinit(); - try lowerToRmEnc(.mov, .rax, RegisterOrMemory.reg(.rbx), emit.code()); + try lowerToRmEnc(.mov, Register.reg(.rax), RegisterOrMemory.reg(.rbx), emit.code()); try expectEqualHexStrings("\x48\x8b\xc3", emit.lowered(), "mov rax, rbx"); - try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0, .base = .r11 }), emit.code()); + try lowerToRmEnc(.mov, Register.reg(.rax), RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0, .base = .r11 }), emit.code()); try expectEqualHexStrings("\x49\x8b\x03", emit.lowered(), "mov rax, qword ptr [r11 + 0]"); - try lowerToRmEnc(.add, .r11, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000 }), emit.code()); + try lowerToRmEnc(.add, Register.reg(.r11), RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000 }), emit.code()); try expectEqualHexStrings( "\x4C\x03\x1C\x25\x00\x00\x00\x10", emit.lowered(), "add r11, qword ptr [ds:0x10000000]", ); - try lowerToRmEnc(.add, .r12b, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), emit.code()); + try lowerToRmEnc(.add, Register.reg(.r12b), RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), emit.code()); try expectEqualHexStrings( "\x44\x02\x24\x25\x00\x00\x00\x10", emit.lowered(), "add r11b, byte ptr [ds:0x10000000]", ); - try lowerToRmEnc(.sub, .r11, RegisterOrMemory.mem(.qword_ptr, .{ + try lowerToRmEnc(.sub, Register.reg(.r11), RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000, .base = .r13, }), emit.code()); @@ -2193,7 +2186,7 @@ test "lower RM encoding" { emit.lowered(), "sub r11, qword ptr [r13 + 0x10000000]", ); - try lowerToRmEnc(.sub, .r11, RegisterOrMemory.mem(.qword_ptr, .{ + try lowerToRmEnc(.sub, Register.reg(.r11), RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000, .base = .r12, }), emit.code()); @@ -2202,14 +2195,14 @@ test "lower RM encoding" { emit.lowered(), "sub r11, qword ptr [r12 + 0x10000000]", ); - try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ + try lowerToRmEnc(.mov, Register.reg(.rax), RegisterOrMemory.mem(.qword_ptr, .{ .disp = @bitCast(u32, @as(i32, -4)), .base = .rbp, }), emit.code()); try expectEqualHexStrings("\x48\x8B\x45\xFC", emit.lowered(), "mov rax, qword ptr [rbp - 4]"); - try lowerToRmEnc(.lea, .rax, RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code()); + try lowerToRmEnc(.lea, Register.reg(.rax), RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code()); try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", emit.lowered(), "lea rax, [rip + 0x10]"); - try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ + try lowerToRmEnc(.mov, Register.reg(.rax), RegisterOrMemory.mem(.qword_ptr, .{ .disp = @bitCast(u32, @as(i32, -8)), .base = .rbp, .scale_index = .{ @@ -2218,7 +2211,7 @@ test "lower RM encoding" { }, }), emit.code()); try expectEqualHexStrings("\x48\x8B\x44\x0D\xF8", emit.lowered(), "mov rax, qword ptr [rbp + rcx*1 - 8]"); - try lowerToRmEnc(.mov, .eax, RegisterOrMemory.mem(.dword_ptr, .{ + try lowerToRmEnc(.mov, Register.reg(.eax), RegisterOrMemory.mem(.dword_ptr, .{ .disp = @bitCast(u32, @as(i32, -4)), .base = .rbp, .scale_index = .{ @@ -2227,7 +2220,7 @@ test "lower RM encoding" { }, }), emit.code()); try expectEqualHexStrings("\x8B\x44\x95\xFC", emit.lowered(), "mov eax, dword ptr [rbp + rdx*4 - 4]"); - try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ + try lowerToRmEnc(.mov, Register.reg(.rax), RegisterOrMemory.mem(.qword_ptr, .{ .disp = @bitCast(u32, @as(i32, -8)), .base = .rbp, .scale_index = .{ @@ -2236,7 +2229,7 @@ test "lower RM encoding" { }, }), emit.code()); try expectEqualHexStrings("\x48\x8B\x44\xCD\xF8", emit.lowered(), "mov rax, qword ptr [rbp + rcx*8 - 8]"); - try lowerToRmEnc(.mov, .r8b, RegisterOrMemory.mem(.byte_ptr, .{ + try lowerToRmEnc(.mov, Register.reg(.r8b), RegisterOrMemory.mem(.byte_ptr, .{ .disp = @bitCast(u32, @as(i32, -24)), .base = .rsi, .scale_index = .{ @@ -2245,7 +2238,7 @@ test "lower RM encoding" { }, }), emit.code()); try expectEqualHexStrings("\x44\x8A\x44\x0E\xE8", emit.lowered(), "mov r8b, byte ptr [rsi + rcx*1 - 24]"); - try lowerToRmEnc(.lea, .rsi, RegisterOrMemory.mem(.qword_ptr, .{ + try lowerToRmEnc(.lea, Register.reg(.rsi), RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0, .base = .rbp, .scale_index = .{ @@ -2259,20 +2252,20 @@ test "lower RM encoding" { test "lower MR encoding" { var emit = TestEmit.init(); defer emit.deinit(); - try lowerToMrEnc(.mov, RegisterOrMemory.reg(.rax), .rbx, emit.code()); + try lowerToMrEnc(.mov, RegisterOrMemory.reg(.rax), Register.reg(.rbx), emit.code()); try expectEqualHexStrings("\x48\x89\xd8", emit.lowered(), "mov rax, rbx"); try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ .disp = @bitCast(u32, @as(i32, -4)), .base = .rbp, - }), .r11, emit.code()); + }), Register.reg(.r11), emit.code()); try expectEqualHexStrings("\x4c\x89\x5d\xfc", emit.lowered(), "mov qword ptr [rbp - 4], r11"); - try lowerToMrEnc(.add, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), .r12b, emit.code()); + try lowerToMrEnc(.add, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), Register.reg(.r12b), emit.code()); try expectEqualHexStrings( "\x44\x00\x24\x25\x00\x00\x00\x10", emit.lowered(), "add byte ptr [ds:0x10000000], r12b", ); - try lowerToMrEnc(.add, RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0x10000000 }), .r12d, emit.code()); + try lowerToMrEnc(.add, RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0x10000000 }), Register.reg(.r12d), emit.code()); try expectEqualHexStrings( "\x44\x01\x24\x25\x00\x00\x00\x10", emit.lowered(), @@ -2281,53 +2274,53 @@ test "lower MR encoding" { try lowerToMrEnc(.sub, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000, .base = .r11, - }), .r12, emit.code()); + }), Register.reg(.r12), emit.code()); try expectEqualHexStrings( "\x4D\x29\xA3\x00\x00\x00\x10", emit.lowered(), "sub qword ptr [r11 + 0x10000000], r12", ); - try lowerToMrEnc(.mov, RegisterOrMemory.rip(.qword_ptr, 0x10), .r12, emit.code()); + try lowerToMrEnc(.mov, RegisterOrMemory.rip(.qword_ptr, 0x10), Register.reg(.r12), emit.code()); try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", emit.lowered(), "mov qword ptr [rip + 0x10], r12"); } test "lower OI encoding" { var emit = TestEmit.init(); defer emit.deinit(); - try lowerToOiEnc(.mov, .rax, 0x1000000000000000, emit.code()); + try lowerToOiEnc(.mov, Register.reg(.rax), 0x1000000000000000, emit.code()); try expectEqualHexStrings( "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10", emit.lowered(), "movabs rax, 0x1000000000000000", ); - try lowerToOiEnc(.mov, .r11, 0x1000000000000000, emit.code()); + try lowerToOiEnc(.mov, Register.reg(.r11), 0x1000000000000000, emit.code()); try expectEqualHexStrings( "\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10", emit.lowered(), "movabs r11, 0x1000000000000000", ); - try lowerToOiEnc(.mov, .r11d, 0x10000000, emit.code()); + try lowerToOiEnc(.mov, Register.reg(.r11d), 0x10000000, emit.code()); try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", emit.lowered(), "mov r11d, 0x10000000"); - try lowerToOiEnc(.mov, .r11w, 0x1000, emit.code()); + try lowerToOiEnc(.mov, Register.reg(.r11w), 0x1000, emit.code()); try expectEqualHexStrings("\x66\x41\xBB\x00\x10", emit.lowered(), "mov r11w, 0x1000"); - try lowerToOiEnc(.mov, .r11b, 0x10, emit.code()); + try lowerToOiEnc(.mov, Register.reg(.r11b), 0x10, emit.code()); try expectEqualHexStrings("\x41\xB3\x10", emit.lowered(), "mov r11b, 0x10"); } test "lower FD/TD encoding" { var emit = TestEmit.init(); defer emit.deinit(); - try lowerToFdEnc(.mov, .rax, 0x1000000000000000, emit.code()); + try lowerToFdEnc(.mov, Register.reg(.rax), 0x1000000000000000, emit.code()); try expectEqualHexStrings( "\x48\xa1\x00\x00\x00\x00\x00\x00\x00\x10", emit.lowered(), "mov rax, ds:0x1000000000000000", ); - try lowerToFdEnc(.mov, .eax, 0x10000000, emit.code()); + try lowerToFdEnc(.mov, Register.reg(.eax), 0x10000000, emit.code()); try expectEqualHexStrings("\xa1\x00\x00\x00\x10", emit.lowered(), "mov eax, ds:0x10000000"); - try lowerToFdEnc(.mov, .ax, 0x1000, emit.code()); + try lowerToFdEnc(.mov, Register.reg(.ax), 0x1000, emit.code()); try expectEqualHexStrings("\x66\xa1\x00\x10", emit.lowered(), "mov ax, ds:0x1000"); - try lowerToFdEnc(.mov, .al, 0x10, emit.code()); + try lowerToFdEnc(.mov, Register.reg(.al), 0x10, emit.code()); try expectEqualHexStrings("\xa0\x10", emit.lowered(), "mov al, ds:0x10"); } @@ -2403,16 +2396,16 @@ test "lower M1 and MC encodings" { test "lower O encoding" { var emit = TestEmit.init(); defer emit.deinit(); - try lowerToOEnc(.pop, .r12, emit.code()); + try lowerToOEnc(.pop, Register.reg(.r12), emit.code()); try expectEqualHexStrings("\x41\x5c", emit.lowered(), "pop r12"); - try lowerToOEnc(.push, .r12w, emit.code()); + try lowerToOEnc(.push, Register.reg(.r12w), emit.code()); try expectEqualHexStrings("\x66\x41\x54", emit.lowered(), "push r12w"); } test "lower RMI encoding" { var emit = TestEmit.init(); defer emit.deinit(); - try lowerToRmiEnc(.imul, .rax, RegisterOrMemory.mem(.qword_ptr, .{ + try lowerToRmiEnc(.imul, Register.reg(.rax), RegisterOrMemory.mem(.qword_ptr, .{ .disp = @bitCast(u32, @as(i32, -8)), .base = .rbp, }), 0x10, emit.code()); @@ -2421,18 +2414,18 @@ test "lower RMI encoding" { emit.lowered(), "imul rax, qword ptr [rbp - 8], 0x10", ); - try lowerToRmiEnc(.imul, .eax, RegisterOrMemory.mem(.dword_ptr, .{ + try lowerToRmiEnc(.imul, Register.reg(.eax), RegisterOrMemory.mem(.dword_ptr, .{ .disp = @bitCast(u32, @as(i32, -4)), .base = .rbp, }), 0x10, emit.code()); try expectEqualHexStrings("\x69\x45\xFC\x10\x00\x00\x00", emit.lowered(), "imul eax, dword ptr [rbp - 4], 0x10"); - try lowerToRmiEnc(.imul, .ax, RegisterOrMemory.mem(.word_ptr, .{ + try lowerToRmiEnc(.imul, Register.reg(.ax), RegisterOrMemory.mem(.word_ptr, .{ .disp = @bitCast(u32, @as(i32, -2)), .base = .rbp, }), 0x10, emit.code()); try expectEqualHexStrings("\x66\x69\x45\xFE\x10\x00", emit.lowered(), "imul ax, word ptr [rbp - 2], 0x10"); - try lowerToRmiEnc(.imul, .r12, RegisterOrMemory.reg(.r12), 0x10, emit.code()); + try lowerToRmiEnc(.imul, Register.reg(.r12), RegisterOrMemory.reg(.r12), 0x10, emit.code()); try expectEqualHexStrings("\x4D\x69\xE4\x10\x00\x00\x00", emit.lowered(), "imul r12, r12, 0x10"); - try lowerToRmiEnc(.imul, .r12w, RegisterOrMemory.reg(.r12w), 0x10, emit.code()); + try lowerToRmiEnc(.imul, Register.reg(.r12w), RegisterOrMemory.reg(.r12w), 0x10, emit.code()); try expectEqualHexStrings("\x66\x45\x69\xE4\x10\x00", emit.lowered(), "imul r12w, r12w, 0x10"); } diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 43d5b92eb2..9a91311e09 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -302,96 +302,113 @@ pub const Encoder = struct { self.code.appendAssumeCapacity(0x66); } - pub fn Vex(comptime count: comptime_int) type { - if (count < 2 or count > 3) { - @compileError("VEX prefix can either be 2- or 3-byte long"); + pub const Vex = struct { + rex_prefix: Rex = .{}, + lead_opc: u5 = 0b0_0001, + register: u4 = 0b1111, + length: u1 = 0b0, + simd_prefix: u2 = 0b00, + wig: bool = false, + lig: bool = false, + lz: bool = false, + + pub fn rex(self: *Vex, r: Rex) void { + self.rex_prefix = r; } - return struct { - bytes: [count]u8 = switch (count) { - 2 => .{ 0xc5, 0xf8 }, - 3 => .{ 0xc4, 0xe1, 0xf8 }, - else => unreachable, - }, + pub fn lead_opc_0f(self: *Vex) void { + self.lead_opc = 0b0_0001; + } - pub fn rex(self: *@This(), prefix: Rex) void { - const byte = &self.bytes[1]; - if (prefix.w) switch (count) { - 3 => self.bytes[2] &= 0b0111_1111, - else => unreachable, - }; - if (prefix.r) byte.* &= 0b0111_1111; - if (prefix.x) switch (count) { - 3 => byte.* &= 0b1011_1111, - else => unreachable, - }; - if (prefix.b) switch (count) { - 3 => byte.* &= 0b1101_1111, - else => unreachable, - }; + pub fn lead_opc_0f_38(self: *Vex) void { + self.lead_opc = 0b0_0010; + } + + pub fn lead_opc_0f_3a(self: *Vex) void { + self.lead_opc = 0b0_0011; + } + + pub fn reg(self: *Vex, register: u4) void { + self.register = ~register; + } + + pub fn len_128(self: *Vex) void { + self.length = 0; + } + + pub fn len_256(self: *Vex) void { + assert(!self.lz); + self.length = 1; + } + + pub fn simd_prefix_66(self: *Vex) void { + self.simd_prefix = 0b01; + } + + pub fn simd_prefix_f3(self: *Vex) void { + self.simd_prefix = 0b10; + } + + pub fn simd_prefix_f2(self: *Vex) void { + self.simd_prefix = 0b11; + } + + pub fn wig(self: *Vex) void { + self.wig = true; + } + + pub fn lig(self: *Vex) void { + self.lig = true; + } + + pub fn lz(self: *Vex) void { + self.lz = true; + } + + pub fn write(self: Vex, writer: anytype) usize { + var buf: [3]u8 = .{0} ** 3; + const form_3byte: bool = blk: { + if (self.rex_prefix.w and !self.wig) break :blk true; + if (self.rex_prefix.x or self.rex_prefix.b) break :blk true; + break :blk self.lead_opc != 0b0_0001; + }; + + if (self.lz) { + assert(self.length == 0); } - pub fn leading_opcode_0f(self: *@This()) void { - switch (count) { - 3 => self.bytes[1] |= 0b0_0001, - else => {}, - } + if (form_3byte) { + // First byte + buf[0] = 0xc4; + // Second byte + const rxb_mask: u3 = @intCast(u3, @boolToInt(!self.rex_prefix.r)) << 2 | + @intCast(u2, @boolToInt(!self.rex_prefix.x)) << 1 | + @boolToInt(!self.rex_prefix.b); + buf[1] |= @intCast(u8, rxb_mask) << 5; + buf[1] |= self.lead_opc; + // Third byte + buf[2] |= @intCast(u8, @boolToInt(!self.rex_prefix.w)) << 7; + buf[2] |= @intCast(u7, self.register) << 3; + buf[2] |= @intCast(u3, self.length) << 2; + buf[2] |= self.simd_prefix; + } else { + // First byte + buf[0] = 0xc5; + // Second byte + buf[1] |= @intCast(u8, @boolToInt(!self.rex_prefix.r)) << 7; + buf[1] |= @intCast(u7, self.register) << 3; + buf[1] |= @intCast(u3, self.length) << 2; + buf[1] |= self.simd_prefix; } - pub fn leading_opcode_0f_38(self: *@This()) void { - switch (count) { - 3 => self.bytes[1] |= 0b0_0010, - else => unreachable, - } - } + const count: usize = if (form_3byte) 3 else 2; + _ = writer.writeAll(buf[0..count]) catch unreachable; + return count; + } + }; - pub fn leading_opcode_0f_3a(self: *@This()) void { - switch (count) { - 3 => self.bytes[1] |= 0b0_0011, - else => unreachable, - } - } - - pub fn reg(self: *@This(), register: u4) void { - const byte = &self.bytes[count - 1]; - const mask = 0b1_0000_111; - byte.* &= mask; - byte.* |= @intCast(u7, ~register) << 3; - } - - pub fn len_128(self: *@This()) void { - const byte = &self.bytes[count - 1]; - byte.* &= 0b0_11; - } - - pub fn len_256(self: *@This()) void { - const byte = &self.bytes[count - 1]; - byte.* |= 0b1_00; - } - - pub fn simd_prefix_66(self: *@This()) void { - const byte = &self.bytes[count - 1]; - byte.* |= 0b01; - } - - pub fn simd_prefix_f2(self: *@This()) void { - const byte = &self.bytes[count - 1]; - byte.* |= 0b11; - } - - pub fn simd_prefix_f3(self: *@This()) void { - const byte = &self.bytes[count - 1]; - byte.* |= 0b10; - } - }; - } - - pub fn vex_2byte(self: Self, prefix: Vex(2)) void { - self.code.appendSliceAssumeCapacity(&prefix.bytes); - } - - pub fn vex_3byte(self: Self, prefix: Vex(3)) void { - self.code.appendSliceAssumeCapacity(&prefix.bytes); + pub fn vex(self: Self, prefix: Vex) void { + _ = prefix.write(self.code.writer()); } /// From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB @@ -759,39 +776,50 @@ test "Encoder helpers - general purpose registers" { } test "Encoder helpers - Vex prefix" { + var buf: [3]u8 = undefined; + var stream = std.io.fixedBufferStream(&buf); + const writer = stream.writer(); + { - var vex_prefix = Encoder.Vex(2){}; + var vex_prefix = Encoder.Vex{}; vex_prefix.rex(.{ .r = true, }); - try testing.expectEqualSlices(u8, &[_]u8{ 0xc5, 0x78 }, &vex_prefix.bytes); + const nwritten = vex_prefix.write(writer); + try testing.expectEqualSlices(u8, &[_]u8{ 0xc5, 0x78 }, buf[0..nwritten]); } { - var vex_prefix = Encoder.Vex(2){}; + stream.reset(); + var vex_prefix = Encoder.Vex{}; vex_prefix.reg(AvxRegister.xmm15.id()); - try testing.expectEqualSlices(u8, &[_]u8{ 0xc5, 0x80 }, &vex_prefix.bytes); + const nwritten = vex_prefix.write(writer); + try testing.expectEqualSlices(u8, &[_]u8{ 0xc5, 0x80 }, buf[0..nwritten]); } { - var vex_prefix = Encoder.Vex(3){}; + stream.reset(); + var vex_prefix = Encoder.Vex{}; vex_prefix.rex(.{ .w = true, .x = true, }); - try testing.expectEqualSlices(u8, &[_]u8{ 0xc4, 0b101_0_0001, 0b0_1111_0_00 }, &vex_prefix.bytes); + const nwritten = vex_prefix.write(writer); + try testing.expectEqualSlices(u8, &[_]u8{ 0xc4, 0b101_0_0001, 0b0_1111_0_00 }, buf[0..nwritten]); } { - var vex_prefix = Encoder.Vex(3){}; + stream.reset(); + var vex_prefix = Encoder.Vex{}; vex_prefix.rex(.{ .w = true, .r = true, }); vex_prefix.len_256(); - vex_prefix.leading_opcode_0f(); + vex_prefix.lead_opc_0f(); vex_prefix.simd_prefix_66(); - try testing.expectEqualSlices(u8, &[_]u8{ 0xc4, 0b011_0_0001, 0b0_1111_1_01 }, &vex_prefix.bytes); + const nwritten = vex_prefix.write(writer); + try testing.expectEqualSlices(u8, &[_]u8{ 0xc4, 0b011_0_0001, 0b0_1111_1_01 }, buf[0..nwritten]); } var code = ArrayList(u8).init(testing.allocator); @@ -800,9 +828,9 @@ test "Encoder helpers - Vex prefix" { { // vmovapd xmm1, xmm2 const encoder = try Encoder.init(&code, 4); - var vex = Encoder.Vex(2){}; + var vex = Encoder.Vex{}; vex.simd_prefix_66(); - encoder.vex_2byte(vex); // use 64 bit operation + encoder.vex(vex); // use 64 bit operation encoder.opcode_1byte(0x28); encoder.modRm_direct(0, AvxRegister.xmm1.lowId()); try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0xF9, 0x28, 0xC1 }, code.items); @@ -813,13 +841,13 @@ test "Encoder helpers - Vex prefix" { // vmovhpd xmm13, xmm1, qword ptr [rip] const encoder = try Encoder.init(&code, 9); - var vex = Encoder.Vex(2){}; + var vex = Encoder.Vex{}; vex.len_128(); vex.simd_prefix_66(); - vex.leading_opcode_0f(); + vex.lead_opc_0f(); vex.rex(.{ .r = true }); vex.reg(AvxRegister.xmm1.id()); - encoder.vex_2byte(vex); + encoder.vex(vex); encoder.opcode_1byte(0x16); encoder.modRm_RIPDisp32(AvxRegister.xmm13.lowId()); encoder.disp32(0); From 0835486249f5f3d187db61c57d4d075d65e10177 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 12 May 2022 21:43:44 +0200 Subject: [PATCH 1575/2031] x64: add vmovsd RM and MR lowerings (first draft) --- src/arch/x86_64/Emit.zig | 175 ++++++++++++++++++++++++++++++++++----- src/arch/x86_64/bits.zig | 22 ++--- 2 files changed, 165 insertions(+), 32 deletions(-) diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 848371f4ba..9015a805fb 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -26,7 +26,7 @@ const Mir = @import("Mir.zig"); const Module = @import("../../Module.zig"); const Instruction = bits.Instruction; const GpRegister = bits.Register; -const AvxRegister = bits.Register; +const AvxRegister = bits.AvxRegister; const Type = @import("../../type.zig").Type; mir: Mir, @@ -1180,6 +1180,7 @@ const Tag = enum { cmovng, cmovb, cmovnae, + vmovsd, fn isSetCC(tag: Tag) bool { return switch (tag) { @@ -1393,6 +1394,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .cmp => OpCode.oneByte(if (is_one_byte) 0x38 else 0x39), .mov => OpCode.oneByte(if (is_one_byte) 0x88 else 0x89), .@"test" => OpCode.oneByte(if (is_one_byte) 0x84 else 0x85), + .vmovsd => OpCode.oneByte(0x11), else => null, }, .rm => return switch (tag) { @@ -1413,6 +1415,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .cmove, .cmovz => OpCode.twoByte(0x0f, 0x44), .cmovb, .cmovnae => OpCode.twoByte(0x0f, 0x42), .cmovl, .cmovng => OpCode.twoByte(0x0f, 0x4c), + .vmovsd => OpCode.oneByte(0x10), else => null, }, .oi => return switch (tag) { @@ -1499,6 +1502,80 @@ inline fn getModRmExt(tag: Tag) ?u3 { }; } +const VexPrefix = struct { + prefix: Encoder.Vex, + reg: ?enum { + ndd, + nds, + dds, + }, +}; + +inline fn getVexPrefix(tag: Tag, enc: Encoding) ?VexPrefix { + const desc: struct { + reg: enum { + none, + ndd, + nds, + dds, + } = .none, + len_256: bool = false, + wig: bool = false, + lig: bool = false, + lz: bool = false, + lead_opc: enum { + l_0f, + l_0f_3a, + l_0f_38, + } = .l_0f, + simd_prefix: enum { + none, + p_66, + p_f2, + p_f3, + } = .none, + } = blk: { + switch (enc) { + .mr => switch (tag) { + .vmovsd => break :blk .{ .lig = true, .simd_prefix = .p_f2, .wig = true }, + else => return null, + }, + .rm => switch (tag) { + .vmovsd => break :blk .{ .lig = true, .simd_prefix = .p_f2, .wig = true }, + else => return null, + }, + else => unreachable, + } + }; + + var vex: Encoder.Vex = .{}; + + if (desc.len_256) vex.len_256(); + if (desc.wig) vex.wig(); + if (desc.lig) vex.lig(); + if (desc.lz) vex.lz(); + + switch (desc.lead_opc) { + .l_0f => {}, + .l_0f_3a => vex.lead_opc_0f_3a(), + .l_0f_38 => vex.lead_opc_0f_38(), + } + + switch (desc.simd_prefix) { + .none => {}, + .p_66 => vex.simd_prefix_66(), + .p_f2 => vex.simd_prefix_f2(), + .p_f3 => vex.simd_prefix_f3(), + } + + return VexPrefix{ .prefix = vex, .reg = switch (desc.reg) { + .none => null, + .nds => .nds, + .dds => .dds, + .ndd => .ndd, + } }; +} + const ScaleIndex = struct { scale: u2, index: GpRegister, @@ -1913,24 +1990,48 @@ fn lowerToRmEnc( encoder.modRm_direct(reg.lowId(), src_reg.lowId()); }, .memory => |src_mem| { - const encoder = try Encoder.init(code, 9); - if (reg.size() == 16) { - encoder.prefix16BitMode(); - } - if (src_mem.base) |base| { - // TODO handle 32-bit base register - requires prefix 0x67 - // Intel Manual, Vol 1, chapter 3.6 and 3.6.1 - encoder.rex(.{ - .w = setRexWRegister(reg), - .r = reg.isExtended(), - .b = base.isExtended(), - }); - } else { - encoder.rex(.{ - .w = setRexWRegister(reg), - .r = reg.isExtended(), - }); - } + const encoder: Encoder = blk: { + switch (reg) { + .register => { + const encoder = try Encoder.init(code, 9); + if (reg.size() == 16) { + encoder.prefix16BitMode(); + } + if (src_mem.base) |base| { + // TODO handle 32-bit base register - requires prefix 0x67 + // Intel Manual, Vol 1, chapter 3.6 and 3.6.1 + encoder.rex(.{ + .w = setRexWRegister(reg), + .r = reg.isExtended(), + .b = base.isExtended(), + }); + } else { + encoder.rex(.{ + .w = setRexWRegister(reg), + .r = reg.isExtended(), + }); + } + break :blk encoder; + }, + .avx_register => { + const encoder = try Encoder.init(code, 10); + var vex_prefix = getVexPrefix(tag, .rm).?; + const vex = &vex_prefix.prefix; + if (src_mem.base) |base| { + vex.rex(.{ + .r = reg.isExtended(), + .b = base.isExtended(), + }); + } else { + vex.rex(.{ + .r = reg.isExtended(), + }); + } + encoder.vex(vex_prefix.prefix); + break :blk encoder; + }, + } + }; opc.encode(encoder); src_mem.encode(encoder, reg.lowId()); }, @@ -1959,7 +2060,7 @@ fn lowerToMrEnc( encoder.modRm_direct(reg.lowId(), dst_reg.lowId()); }, .memory => |dst_mem| { - const encoder = blk: { + const encoder: Encoder = blk: { switch (reg) { .register => { const encoder = try Encoder.init(code, 9); @@ -1981,7 +2082,23 @@ fn lowerToMrEnc( break :blk encoder; }, .avx_register => { - unreachable; + const encoder = try Encoder.init(code, 10); + var vex_prefix = getVexPrefix(tag, .mr).?; + const vex = &vex_prefix.prefix; + if (dst_mem.base) |base| { + vex.rex(.{ + .w = dst_mem.ptr_size == .qword_ptr, + .r = reg.isExtended(), + .b = base.isExtended(), + }); + } else { + vex.rex(.{ + .w = dst_mem.ptr_size == .qword_ptr, + .r = reg.isExtended(), + }); + } + encoder.vex(vex_prefix.prefix); + break :blk encoder; }, } }; @@ -2247,6 +2364,14 @@ test "lower RM encoding" { }, }), emit.code()); try expectEqualHexStrings("\x48\x8D\x74\x0D\x00", emit.lowered(), "lea rsi, qword ptr [rbp + rcx*1 + 0]"); + + // AVX extension tests + try lowerToRmEnc(.vmovsd, Register.avxReg(.xmm1), RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code()); + try expectEqualHexStrings( + "\xC5\xFB\x10\x0D\x10\x00\x00\x00", + emit.lowered(), + "vmovsd xmm1, qword ptr [rip + 0x10]", + ); } test "lower MR encoding" { @@ -2282,6 +2407,14 @@ test "lower MR encoding" { ); try lowerToMrEnc(.mov, RegisterOrMemory.rip(.qword_ptr, 0x10), Register.reg(.r12), emit.code()); try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", emit.lowered(), "mov qword ptr [rip + 0x10], r12"); + + // AVX extension tests + try lowerToMrEnc(.vmovsd, RegisterOrMemory.rip(.qword_ptr, 0x10), Register.avxReg(.xmm1), emit.code()); + try expectEqualHexStrings( + "\xC5\xFB\x11\x0D\x10\x00\x00\x00", + emit.lowered(), + "vmovsd qword ptr [rip + 0x10], xmm1", + ); } test "lower OI encoding" { diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 9a91311e09..0bba295c20 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -143,7 +143,7 @@ pub const AvxRegister = enum(u6) { none, /// Returns the bit-width of the register. - pub fn size(self: AvxRegister) u4 { + pub fn size(self: AvxRegister) u9 { return switch (@enumToInt(self)) { 0...15 => 256, 16...31 => 128, @@ -152,7 +152,7 @@ pub const AvxRegister = enum(u6) { } /// Returns whether the register is *extended*. - pub fn isExtended(self: Register) bool { + pub fn isExtended(self: AvxRegister) bool { return @enumToInt(self) & 0x08 != 0; } @@ -308,9 +308,9 @@ pub const Encoder = struct { register: u4 = 0b1111, length: u1 = 0b0, simd_prefix: u2 = 0b00, - wig: bool = false, - lig: bool = false, - lz: bool = false, + wig_desc: bool = false, + lig_desc: bool = false, + lz_desc: bool = false, pub fn rex(self: *Vex, r: Rex) void { self.rex_prefix = r; @@ -337,7 +337,7 @@ pub const Encoder = struct { } pub fn len_256(self: *Vex) void { - assert(!self.lz); + assert(!self.lz_desc); self.length = 1; } @@ -354,26 +354,26 @@ pub const Encoder = struct { } pub fn wig(self: *Vex) void { - self.wig = true; + self.wig_desc = true; } pub fn lig(self: *Vex) void { - self.lig = true; + self.lig_desc = true; } pub fn lz(self: *Vex) void { - self.lz = true; + self.lz_desc = true; } pub fn write(self: Vex, writer: anytype) usize { var buf: [3]u8 = .{0} ** 3; const form_3byte: bool = blk: { - if (self.rex_prefix.w and !self.wig) break :blk true; + if (self.rex_prefix.w and !self.wig_desc) break :blk true; if (self.rex_prefix.x or self.rex_prefix.b) break :blk true; break :blk self.lead_opc != 0b0_0001; }; - if (self.lz) { + if (self.lz_desc) { assert(self.length == 0); } From 357561840d705bafbeb22b4919d6a79b208cd8fe Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 13 May 2022 21:28:53 +0200 Subject: [PATCH 1576/2031] x64: load/store to/from AVX registers for f64 --- src/arch/x86_64/CodeGen.zig | 410 ++++++++++++++++++++++++------------ src/arch/x86_64/Emit.zig | 86 +++++--- src/arch/x86_64/Mir.zig | 55 +++-- src/arch/x86_64/abi.zig | 6 + src/codegen.zig | 24 +-- src/register_manager.zig | 18 +- src/test.zig | 18 +- 7 files changed, 410 insertions(+), 207 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index f1455b0591..d0bed75fbd 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -22,8 +22,6 @@ const Liveness = @import("../../Liveness.zig"); const Mir = @import("Mir.zig"); const Module = @import("../../Module.zig"); const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; -const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers); -const RegisterLock = RegisterManager.RegisterLock; const Target = std.Target; const Type = @import("../../type.zig").Type; const TypedValue = @import("../../TypedValue.zig"); @@ -31,12 +29,19 @@ const Value = @import("../../value.zig").Value; const bits = @import("bits.zig"); const abi = @import("abi.zig"); -const Register = bits.Register; + const callee_preserved_regs = abi.callee_preserved_regs; const caller_preserved_regs = abi.caller_preserved_regs; const allocatable_registers = abi.allocatable_registers; const c_abi_int_param_regs = abi.c_abi_int_param_regs; const c_abi_int_return_regs = abi.c_abi_int_return_regs; +const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers, spillInstruction); +const RegisterLock = RegisterManager.RegisterLock; +const Register = bits.Register; + +const AvxRegisterManager = RegisterManagerFn(Self, AvxRegister, &abi.avx_regs, spillInstructionAvx); +const AvxRegisterLock = AvxRegisterManager.RegisterLock; +const AvxRegister = bits.AvxRegister; const InnerError = error{ OutOfMemory, @@ -87,7 +92,8 @@ branch_stack: *std.ArrayList(Branch), // Key is the block instruction blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, -register_manager: RegisterManager = .{}, +register_manager: RegisterManager, +avx_register_manager: AvxRegisterManager, /// Maps offset to what is stored there. stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, @@ -119,14 +125,16 @@ pub const MCValue = union(enum) { /// A pointer-sized integer that fits in a register. /// If the type is a pointer, this is the pointer address in virtual address space. immediate: u64, - /// The value is in a target-specific register. + /// The value is in a GP register. register: Register, - /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the register, + /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register, /// and the operation is an unsigned operation. register_overflow_unsigned: Register, - /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the register, + /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register, /// and the operation is a signed operation. register_overflow_signed: Register, + /// The value is in an AVX register. + avx_register: AvxRegister, /// The value is in memory at a hard-coded address. /// If the type is a pointer, it means the pointer address is at this memory location. memory: u64, @@ -295,7 +303,11 @@ pub fn generate( .mir_to_air_map = if (builtin.mode == .Debug) std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index).init(bin_file.allocator) else {}, + .register_manager = undefined, + .avx_register_manager = undefined, }; + function.register_manager = .{ .function = &function }; + function.avx_register_manager = .{ .function = &function }; defer function.stack.deinit(bin_file.allocator); defer function.blocks.deinit(bin_file.allocator); defer function.exitlude_jump_relocs.deinit(bin_file.allocator); @@ -387,14 +399,14 @@ fn gen(self: *Self) InnerError!void { if (cc != .Naked) { _ = try self.addInst(.{ .tag = .push, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rbp, }).encode(), .data = undefined, // unused for push reg, }); _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rbp, .reg2 = .rsp, }).encode(), @@ -434,7 +446,7 @@ fn gen(self: *Self) InnerError!void { // push the callee_preserved_regs that were used const backpatch_push_callee_preserved_regs_i = try self.addInst(.{ .tag = .push_regs_from_callee_preserved_regs, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rbp, }).encode(), .data = .{ .payload = undefined }, // to be backpatched @@ -476,7 +488,7 @@ fn gen(self: *Self) InnerError!void { // pop the callee_preserved_regs _ = try self.addInst(.{ .tag = .pop_regs_from_callee_preserved_regs, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rbp, }).encode(), .data = .{ .payload = callee_preserved_regs_payload }, @@ -497,7 +509,7 @@ fn gen(self: *Self) InnerError!void { _ = try self.addInst(.{ .tag = .pop, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rbp, }).encode(), .data = undefined, @@ -505,7 +517,7 @@ fn gen(self: *Self) InnerError!void { _ = try self.addInst(.{ .tag = .ret, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .flags = 0b11, }).encode(), .data = undefined, @@ -521,14 +533,14 @@ fn gen(self: *Self) InnerError!void { if (aligned_stack_end > 0) { self.mir_instructions.set(backpatch_stack_sub, .{ .tag = .sub, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rsp, }).encode(), .data = .{ .imm = aligned_stack_end }, }); self.mir_instructions.set(backpatch_stack_add, .{ .tag = .add, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rsp, }).encode(), .data = .{ .imm = aligned_stack_end }, @@ -889,13 +901,27 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { self.stack_align = abi_align; if (reg_ok) { - // Make sure the type can fit in a register before we try to allocate one. - const ptr_bits = self.target.cpu.arch.ptrBitWidth(); - const ptr_bytes: u64 = @divExact(ptr_bits, 8); - if (abi_size <= ptr_bytes) { - if (self.register_manager.tryAllocReg(inst)) |reg| { - return MCValue{ .register = registerAlias(reg, abi_size) }; - } + switch (elem_ty.zigTypeTag()) { + .Vector => return self.fail("TODO allocRegOrMem for Vector type", .{}), + .Float => { + // TODO check if AVX available + const ptr_bytes: u64 = 32; + if (abi_size <= ptr_bytes) { + if (self.avx_register_manager.tryAllocReg(inst)) |reg| { + return MCValue{ .avx_register = avxRegisterAlias(reg, abi_size) }; + } + } + }, + else => { + // Make sure the type can fit in a register before we try to allocate one. + const ptr_bits = self.target.cpu.arch.ptrBitWidth(); + const ptr_bytes: u64 = @divExact(ptr_bits, 8); + if (abi_size <= ptr_bytes) { + if (self.register_manager.tryAllocReg(inst)) |reg| { + return MCValue{ .register = registerAlias(reg, abi_size) }; + } + } + }, } } const stack_offset = try self.allocMem(inst, abi_size, abi_align); @@ -920,6 +946,21 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{}); } +pub fn spillInstructionAvx(self: *Self, reg: AvxRegister, inst: Air.Inst.Index) !void { + const stack_mcv = try self.allocRegOrMem(inst, false); + log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); + const reg_mcv = self.getResolvedInstValue(inst); + switch (reg_mcv) { + .avx_register => |other| { + assert(reg.to256() == other.to256()); + }, + else => {}, + } + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; + try branch.inst_table.put(self.gpa, inst, stack_mcv); + try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{}); +} + pub fn spillCompareFlagsIfOccupied(self: *Self) !void { if (self.compare_flags_inst) |inst_to_save| { const mcv = self.getResolvedInstValue(inst_to_save); @@ -1192,7 +1233,7 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void { const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, rhs_mcv); _ = try self.addInst(.{ .tag = if (signedness == .signed) .cond_mov_lt else .cond_mov_below, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = dst_mcv.register, .reg2 = lhs_reg, }).encode(), @@ -1396,7 +1437,7 @@ fn genSetStackTruncatedOverflowCompare( }; _ = try self.addInst(.{ .tag = .cond_set_byte_overflow, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = overflow_reg.to8(), .flags = flags, }).encode(), @@ -1416,7 +1457,7 @@ fn genSetStackTruncatedOverflowCompare( const eq_reg = temp_regs[2]; _ = try self.addInst(.{ .tag = .cond_set_byte_eq_ne, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = eq_reg.to8(), .flags = 0b00, }).encode(), @@ -1565,7 +1606,7 @@ fn genIntMulDivOpMir( .signed => { _ = try self.addInst(.{ .tag = .cwd, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .flags = 0b11, }).encode(), .data = undefined, @@ -1574,7 +1615,7 @@ fn genIntMulDivOpMir( .unsigned => { _ = try self.addInst(.{ .tag = .xor, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rdx, .reg2 = .rdx, }).encode(), @@ -1596,7 +1637,7 @@ fn genIntMulDivOpMir( .register => |reg| { _ = try self.addInst(.{ .tag = tag, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = reg, }).encode(), .data = undefined, @@ -1605,7 +1646,7 @@ fn genIntMulDivOpMir( .stack_offset => |off| { _ = try self.addInst(.{ .tag = tag, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg2 = .rbp, .flags = switch (abi_size) { 1 => 0b00, @@ -1647,7 +1688,7 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa _ = try self.addInst(.{ .tag = .xor, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = divisor.to64(), .reg2 = dividend.to64(), }).encode(), @@ -1655,7 +1696,7 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa }); _ = try self.addInst(.{ .tag = .sar, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = divisor.to64(), .flags = 0b10, }).encode(), @@ -1663,7 +1704,7 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa }); _ = try self.addInst(.{ .tag = .@"test", - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rdx, .reg2 = .rdx, }).encode(), @@ -1671,7 +1712,7 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa }); _ = try self.addInst(.{ .tag = .cond_mov_eq, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = divisor.to64(), .reg2 = .rdx, }).encode(), @@ -2058,7 +2099,7 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { // mov reg, [rbp - 8] _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = addr_reg.to64(), .reg2 = .rbp, .flags = 0b01, @@ -2143,7 +2184,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { // lea reg, [rbp] _ = try self.addInst(.{ .tag = .lea, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = addr_reg.to64(), .reg2 = .rbp, }).encode(), @@ -2154,7 +2195,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { // lea reg, [rbp] _ = try self.addInst(.{ .tag = .lea, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = addr_reg.to64(), .reg2 = .rbp, }).encode(), @@ -2222,7 +2263,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { // mov dst_mcv, [dst_mcv] _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .flags = 0b01, .reg1 = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)), .reg2 = dst_mcv.register, @@ -2456,6 +2497,7 @@ fn reuseOperand( fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { const elem_ty = ptr_ty.elemType(); const abi_size = elem_ty.abiSize(self.target.*); + std.log.warn("{} => {}, {}", .{ ptr_ty.fmtDebug(), ptr, dst_mcv }); switch (ptr) { .none => unreachable, .undef => unreachable, @@ -2488,7 +2530,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo // mov dst_reg, [reg] _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)), .reg2 = reg, .flags = 0b01, @@ -2508,6 +2550,9 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo else => return self.fail("TODO implement loading from register into {}", .{dst_mcv}), } }, + .avx_register => { + return self.fail("TODO load for AVX register", .{}); + }, .memory, .got_load, .direct_load, @@ -2559,7 +2604,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl); _ = try self.addInst(.{ .tag = .lea_pie, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(reg, abi_size), .flags = flags, }).encode(), @@ -2582,6 +2627,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { const abi_size = value_ty.abiSize(self.target.*); + std.log.warn("{} => {}, {} => {}", .{ ptr_ty.fmtDebug(), ptr, value_ty.fmtDebug(), value }); switch (ptr) { .none => unreachable, .undef => unreachable, @@ -2623,7 +2669,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type }); _ = try self.addInst(.{ .tag = .mov_mem_imm, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = reg.to64(), .flags = switch (abi_size) { 1 => 0b00, @@ -2645,7 +2691,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type const tmp_reg = try self.copyToTmpRegister(value_ty, value); _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = reg.to64(), .reg2 = tmp_reg.to64(), .flags = 0b10, @@ -2661,7 +2707,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .register => |src_reg| { _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = reg.to64(), .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)), .flags = 0b10, @@ -2689,6 +2735,9 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type }, } }, + .avx_register => { + return self.fail("TODO store for AVX register", .{}); + }, .got_load, .direct_load, .memory, @@ -2709,7 +2758,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type // mov reg, [reg] _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = addr_reg.to64(), .reg2 = addr_reg.to64(), .flags = 0b01, @@ -2748,7 +2797,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type } _ = try self.addInst(.{ .tag = .mov_mem_imm, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = addr_reg.to64(), .flags = flags, }).encode(), @@ -2758,7 +2807,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .register => |reg| { _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = addr_reg.to64(), .reg2 = reg, .flags = 0b10, @@ -2779,7 +2828,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = tmp_reg, .reg2 = tmp_reg, .flags = 0b01, @@ -2788,7 +2837,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type }); _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = addr_reg.to64(), .reg2 = tmp_reg, .flags = 0b10, @@ -2806,7 +2855,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type const tmp_reg = try self.copyToTmpRegister(value_ty, value); _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = addr_reg.to64(), .reg2 = tmp_reg, .flags = 0b10, @@ -2967,7 +3016,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { if (signedness == .signed and field_size < 8) { _ = try self.addInst(.{ .tag = .mov_sign_extend, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = dst_mcv.register, .reg2 = registerAlias(dst_mcv.register, field_size), }).encode(), @@ -2998,7 +3047,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { }; _ = try self.addInst(.{ .tag = .cond_set_byte_overflow, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = dst_reg.to8(), .flags = flags, }).encode(), @@ -3042,7 +3091,7 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi 1 => { _ = try self.addInst(.{ .tag = tag, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(reg, abi_size), .flags = 0b00, }).encode(), @@ -3053,7 +3102,7 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi else => { _ = try self.addInst(.{ .tag = tag, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(reg, abi_size), .flags = 0b10, }).encode(), @@ -3074,7 +3123,7 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi _ = try self.addInst(.{ .tag = tag, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(reg, abi_size), .flags = 0b01, }).encode(), @@ -3453,17 +3502,20 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .register => |src_reg| { _ = try self.addInst(.{ .tag = mir_tag, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(dst_reg, abi_size), .reg2 = registerAlias(src_reg, abi_size), }).encode(), .data = undefined, }); }, + .avx_register => { + return self.fail("TODO genBinOp for AVX register", .{}); + }, .immediate => |imm| { _ = try self.addInst(.{ .tag = mir_tag, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(dst_reg, abi_size), }).encode(), .data = .{ .imm = @truncate(u32, imm) }, @@ -3488,7 +3540,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu } _ = try self.addInst(.{ .tag = mir_tag, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(dst_reg, abi_size), .reg2 = .rbp, .flags = 0b01, @@ -3498,6 +3550,9 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu }, } }, + .avx_register => { + return self.fail("TODO genBinOp for AVX register", .{}); + }, .ptr_stack_offset, .stack_offset => |off| { if (off > math.maxInt(i32)) { return self.fail("stack offset too large", .{}); @@ -3515,7 +3570,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .register => |src_reg| { _ = try self.addInst(.{ .tag = mir_tag, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rbp, .reg2 = registerAlias(src_reg, abi_size), .flags = 0b10, @@ -3523,6 +3578,9 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .data = .{ .imm = @bitCast(u32, -off) }, }); }, + .avx_register => { + return self.fail("TODO genBinOp for AVX register", .{}); + }, .immediate => |imm| { const tag: Mir.Inst.Tag = switch (mir_tag) { .add => .add_mem_imm, @@ -3546,7 +3604,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu }); _ = try self.addInst(.{ .tag = tag, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rbp, .flags = flags, }).encode(), @@ -3592,6 +3650,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .ptr_stack_offset => unreachable, .register_overflow_unsigned => unreachable, .register_overflow_signed => unreachable, + .avx_register => unreachable, .register => |dst_reg| { switch (src_mcv) { .none => unreachable, @@ -3600,11 +3659,12 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .ptr_stack_offset => unreachable, .register_overflow_unsigned => unreachable, .register_overflow_signed => unreachable, + .avx_register => unreachable, .register => |src_reg| { // register, register _ = try self.addInst(.{ .tag = .imul_complex, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(dst_reg, abi_size), .reg2 = registerAlias(src_reg, abi_size), }).encode(), @@ -3617,7 +3677,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M if (math.minInt(i32) <= imm and imm <= math.maxInt(i32)) { _ = try self.addInst(.{ .tag = .imul_complex, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = dst_reg.to32(), .reg2 = dst_reg.to32(), .flags = 0b10, @@ -3633,7 +3693,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .stack_offset => |off| { _ = try self.addInst(.{ .tag = .imul_complex, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(dst_reg, abi_size), .reg2 = .rbp, .flags = 0b01, @@ -3663,6 +3723,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .ptr_stack_offset => unreachable, .register_overflow_unsigned => unreachable, .register_overflow_signed => unreachable, + .avx_register => unreachable, .register => |src_reg| { // copy dst to a register const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); @@ -3670,7 +3731,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M // register, register _ = try self.addInst(.{ .tag = .imul_complex, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(dst_reg, abi_size), .reg2 = registerAlias(src_reg, abi_size), }).encode(), @@ -3865,6 +3926,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .ptr_stack_offset => { return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); }, + .avx_register => { + return self.fail("TODO implement calling with MCValue.avx_register arg", .{}); + }, .undef => unreachable, .immediate => unreachable, .unreach => unreachable, @@ -3883,7 +3947,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. // Adjust the stack _ = try self.addInst(.{ .tag = .sub, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rsp, }).encode(), .data = .{ .imm = info.stack_byte_count }, @@ -3909,7 +3973,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. unreachable; _ = try self.addInst(.{ .tag = .call, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .flags = 0b01, }).encode(), .data = .{ .imm = @truncate(u32, got_addr) }, @@ -3925,7 +3989,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. try self.genSetReg(Type.initTag(.usize), .rax, mcv); _ = try self.addInst(.{ .tag = .call, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rax, .flags = 0b01, }).encode(), @@ -3943,7 +4007,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. // callq *%rax _ = try self.addInst(.{ .tag = .call, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rax, .flags = 0b01, }).encode(), @@ -3978,7 +4042,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. try self.genSetReg(Type.initTag(.usize), .rax, mcv); _ = try self.addInst(.{ .tag = .call, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rax, .flags = 0b01, }).encode(), @@ -3996,7 +4060,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const fn_got_addr = got_addr + got_index * ptr_bytes; _ = try self.addInst(.{ .tag = .call, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .flags = 0b01, }).encode(), .data = .{ .imm = @intCast(u32, fn_got_addr) }, @@ -4008,7 +4072,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. try self.genSetReg(Type.initTag(.usize), .rax, mcv); _ = try self.addInst(.{ .tag = .call, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rax, .flags = 0b01, }).encode(), @@ -4021,7 +4085,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. // Readjust the stack _ = try self.addInst(.{ .tag = .add, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rsp, }).encode(), .data = .{ .imm = info.stack_byte_count }, @@ -4081,7 +4145,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { // which is available if the jump is 127 bytes or less forward. const jmp_reloc = try self.addInst(.{ .tag = .jmp, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .flags = 0b00, }).encode(), .data = .{ .inst = undefined }, @@ -4116,7 +4180,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { // which is available if the jump is 127 bytes or less forward. const jmp_reloc = try self.addInst(.{ .tag = .jmp, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .flags = 0b00, }).encode(), .data = .{ .inst = undefined }, @@ -4362,7 +4426,7 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { Mir.Inst.Tag.cond_jmp_greater_less; return self.addInst(.{ .tag = tag, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .flags = flags, }).encode(), .data = .{ .inst = undefined }, @@ -4372,7 +4436,7 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { try self.spillCompareFlagsIfOccupied(); _ = try self.addInst(.{ .tag = .@"test", - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = reg, .flags = 0b00, }).encode(), @@ -4380,7 +4444,7 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { }); return self.addInst(.{ .tag = .cond_jmp_eq_ne, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .flags = 0b01, }).encode(), .data = .{ .inst = undefined }, @@ -4776,7 +4840,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { try self.genBody(body); _ = try self.addInst(.{ .tag = .jmp, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .flags = 0b00, }).encode(), .data = .{ .inst = jmp_target }, @@ -4829,7 +4893,7 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u .immediate => |imm| { _ = try self.addInst(.{ .tag = .xor, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(cond_reg, abi_size), }).encode(), .data = .{ .imm = @intCast(u32, imm) }, @@ -4838,7 +4902,7 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u .register => |reg| { _ = try self.addInst(.{ .tag = .xor, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(cond_reg, abi_size), .reg2 = registerAlias(reg, abi_size), }).encode(), @@ -4860,7 +4924,7 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u _ = try self.addInst(.{ .tag = .@"test", - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(cond_reg, abi_size), .reg2 = registerAlias(cond_reg, abi_size), }).encode(), @@ -4868,7 +4932,7 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u }); return self.addInst(.{ .tag = .cond_jmp_eq_ne, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .flags = 0b00, }).encode(), .data = .{ .inst = undefined }, @@ -5020,6 +5084,12 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); break :blk new_mcv; }, + .avx_register => blk: { + // TODO not needed; return operand_mcv ones we can transfer between XMM registers + const new_mcv = try self.allocRegOrMem(block, false); + try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); + break :blk new_mcv; + }, else => return self.fail("TODO implement block_data.mcv = operand_mcv for {}", .{operand_mcv}), }; } else { @@ -5036,7 +5106,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { // Leave the jump offset undefined const jmp_reloc = try self.addInst(.{ .tag = .jmp, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .flags = 0b00, }).encode(), .data = .{ .inst = undefined }, @@ -5126,7 +5196,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { }; _ = try self.addInst(.{ .tag = .push, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .flags = 0b10, }).encode(), .data = .{ .imm = n }, @@ -5137,7 +5207,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { return self.fail("unrecognized register: '{s}'", .{reg_name}); _ = try self.addInst(.{ .tag = .push, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = reg, }).encode(), .data = undefined, @@ -5151,7 +5221,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { return self.fail("unrecognized register: '{s}'", .{reg_name}); _ = try self.addInst(.{ .tag = .pop, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = reg, }).encode(), .data = undefined, @@ -5217,6 +5287,7 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { .none => return, .immediate => unreachable, .register => |reg| return self.genSetReg(ty, reg, val), + .avx_register => |reg| return self.genSetAvxReg(ty, reg, val), .stack_offset => |off| return self.genSetStack(ty, off, val, .{}), .memory => { return self.fail("TODO implement setRegOrMem for memory", .{}); @@ -5247,6 +5318,9 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE .register_overflow_unsigned, .register_overflow_signed, => return self.fail("TODO genSetStackArg for register with overflow bit", .{}), + .avx_register => { + return self.fail("TODO genSetStackArg for AVX register", .{}); + }, .compare_flags_unsigned, .compare_flags_signed, => { @@ -5265,7 +5339,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE }); _ = try self.addInst(.{ .tag = .mov_mem_imm, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rsp, .flags = switch (abi_size) { 1 => 0b00, @@ -5301,7 +5375,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE .register => |reg| { _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rsp, .reg2 = registerAlias(reg, @intCast(u32, abi_size)), .flags = 0b10, @@ -5368,7 +5442,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl }; _ = try self.addInst(.{ .tag = .cond_set_byte_overflow, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = tmp_reg.to8(), .flags = flags, }).encode(), @@ -5398,7 +5472,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl }); _ = try self.addInst(.{ .tag = .mov_mem_imm, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = base_reg, .flags = switch (abi_size) { 1 => 0b00, @@ -5420,7 +5494,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl }); _ = try self.addInst(.{ .tag = .mov_mem_imm, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = base_reg, .flags = 0b10, }).encode(), @@ -5434,7 +5508,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl }); _ = try self.addInst(.{ .tag = .mov_mem_imm, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = base_reg, .flags = 0b10, }).encode(), @@ -5466,7 +5540,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = base_reg, .reg2 = registerAlias(tmp_reg, nearest_power_of_two), .flags = 0b10, @@ -5484,7 +5558,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl } else { _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = base_reg, .reg2 = registerAlias(reg, @intCast(u32, abi_size)), .flags = 0b10, @@ -5493,6 +5567,27 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl }); } }, + .avx_register => |reg| { + const base_reg = opts.dest_stack_base orelse .rbp; + switch (ty.zigTypeTag()) { + .Float => switch (ty.tag()) { + .f32 => return self.fail("TODO genSetStack for AVX register for f32", .{}), + .f64 => { + _ = try self.addInst(.{ + .tag = .mov_f64, + .ops = (Mir.Ops(Register, AvxRegister){ + .reg1 = base_reg, + .reg2 = reg.to128(), + .flags = 0b01, + }).encode(), + .data = .{ .imm = @bitCast(u32, -stack_offset) }, + }); + }, + else => return self.fail("TODO genSetStack for AVX register for type {}", .{ty.fmtDebug()}), + }, + else => return self.fail("TODO genSetStack for AVX register for type {}", .{ty.fmtDebug()}), + } + }, .memory, .got_load, .direct_load, @@ -5569,7 +5664,7 @@ fn genInlineMemcpy( .ptr_stack_offset, .stack_offset => |off| { _ = try self.addInst(.{ .tag = .lea, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = dst_addr_reg.to64(), .reg2 = opts.dest_stack_base orelse .rbp, }).encode(), @@ -5579,7 +5674,7 @@ fn genInlineMemcpy( .register => |reg| { _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(dst_addr_reg, @divExact(reg.size(), 8)), .reg2 = reg, }).encode(), @@ -5604,7 +5699,7 @@ fn genInlineMemcpy( .ptr_stack_offset, .stack_offset => |off| { _ = try self.addInst(.{ .tag = .lea, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = src_addr_reg.to64(), .reg2 = opts.source_stack_base orelse .rbp, }).encode(), @@ -5614,7 +5709,7 @@ fn genInlineMemcpy( .register => |reg| { _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(src_addr_reg, @divExact(reg.size(), 8)), .reg2 = reg, }).encode(), @@ -5637,7 +5732,7 @@ fn genInlineMemcpy( // mov rcx, 0 _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rcx, }).encode(), .data = .{ .imm = 0 }, @@ -5646,7 +5741,7 @@ fn genInlineMemcpy( // mov rax, 0 _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rax, }).encode(), .data = .{ .imm = 0 }, @@ -5656,7 +5751,7 @@ fn genInlineMemcpy( // cmp count, 0 const loop_start = try self.addInst(.{ .tag = .cmp, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = count_reg, }).encode(), .data = .{ .imm = 0 }, @@ -5665,14 +5760,14 @@ fn genInlineMemcpy( // je end const loop_reloc = try self.addInst(.{ .tag = .cond_jmp_eq_ne, - .ops = (Mir.Ops{ .flags = 0b01 }).encode(), + .ops = (Mir.Ops(Register, Register){ .flags = 0b01 }).encode(), .data = .{ .inst = undefined }, }); // mov tmp, [addr + rcx] _ = try self.addInst(.{ .tag = .mov_scale_src, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = tmp_reg.to8(), .reg2 = src_addr_reg, }).encode(), @@ -5682,7 +5777,7 @@ fn genInlineMemcpy( // mov [stack_offset + rax], tmp _ = try self.addInst(.{ .tag = .mov_scale_dst, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = dst_addr_reg, .reg2 = tmp_reg.to8(), }).encode(), @@ -5692,7 +5787,7 @@ fn genInlineMemcpy( // add rcx, 1 _ = try self.addInst(.{ .tag = .add, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rcx, }).encode(), .data = .{ .imm = 1 }, @@ -5701,7 +5796,7 @@ fn genInlineMemcpy( // add rax, 1 _ = try self.addInst(.{ .tag = .add, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rax, }).encode(), .data = .{ .imm = 1 }, @@ -5710,7 +5805,7 @@ fn genInlineMemcpy( // sub count, 1 _ = try self.addInst(.{ .tag = .sub, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = count_reg, }).encode(), .data = .{ .imm = 1 }, @@ -5719,7 +5814,7 @@ fn genInlineMemcpy( // jmp loop _ = try self.addInst(.{ .tag = .jmp, - .ops = (Mir.Ops{ .flags = 0b00 }).encode(), + .ops = (Mir.Ops(Register, Register){ .flags = 0b00 }).encode(), .data = .{ .inst = loop_start }, }); @@ -5751,7 +5846,7 @@ fn genInlineMemset( .ptr_stack_offset, .stack_offset => |off| { _ = try self.addInst(.{ .tag = .lea, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = addr_reg.to64(), .reg2 = opts.dest_stack_base orelse .rbp, }).encode(), @@ -5761,7 +5856,7 @@ fn genInlineMemset( .register => |reg| { _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(addr_reg, @divExact(reg.size(), 8)), .reg2 = reg, }).encode(), @@ -5782,7 +5877,7 @@ fn genInlineMemset( // cmp rax, -1 const loop_start = try self.addInst(.{ .tag = .cmp, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rax, }).encode(), .data = .{ .imm = @bitCast(u32, @as(i32, -1)) }, @@ -5791,7 +5886,7 @@ fn genInlineMemset( // je end const loop_reloc = try self.addInst(.{ .tag = .cond_jmp_eq_ne, - .ops = (Mir.Ops{ .flags = 0b01 }).encode(), + .ops = (Mir.Ops(Register, Register){ .flags = 0b01 }).encode(), .data = .{ .inst = undefined }, }); @@ -5807,7 +5902,7 @@ fn genInlineMemset( }); _ = try self.addInst(.{ .tag = .mov_mem_index_imm, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = addr_reg, }).encode(), .data = .{ .payload = payload }, @@ -5819,7 +5914,7 @@ fn genInlineMemset( // sub rax, 1 _ = try self.addInst(.{ .tag = .sub, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rax, }).encode(), .data = .{ .imm = 1 }, @@ -5828,7 +5923,7 @@ fn genInlineMemset( // jmp loop _ = try self.addInst(.{ .tag = .jmp, - .ops = (Mir.Ops{ .flags = 0b00 }).encode(), + .ops = (Mir.Ops(Register, Register){ .flags = 0b00 }).encode(), .data = .{ .inst = loop_start }, }); @@ -5836,6 +5931,43 @@ fn genInlineMemset( try self.performReloc(loop_reloc); } +fn genSetAvxReg(self: *Self, ty: Type, reg: AvxRegister, mcv: MCValue) InnerError!void { + switch (mcv) { + .dead => unreachable, + .register_overflow_unsigned, + .register_overflow_signed, + => unreachable, + .stack_offset => |off| { + if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { + return self.fail("stack offset too large", .{}); + } + + switch (ty.zigTypeTag()) { + .Float => { + switch (ty.tag()) { + .f32 => return self.fail("TODO genSetAvxReg from stack offset for f32", .{}), + .f64 => { + _ = try self.addInst(.{ + .tag = .mov_f64, + .ops = (Mir.Ops(AvxRegister, Register){ + .reg1 = reg.to128(), + .reg2 = .rbp, + }).encode(), + .data = .{ .imm = @bitCast(u32, -off) }, + }); + }, + else => return self.fail("TODO genSetAvxReg from stack offset for {}", .{ty.fmtDebug()}), + } + }, + else => return self.fail("TODO genSetAvxReg from stack offset for type {}", .{ty.fmtDebug()}), + } + }, + else => |other| { + return self.fail("TODO genSetAvxReg from {}", .{other}); + }, + } +} + fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { const abi_size = @intCast(u32, ty.abiSize(self.target.*)); switch (mcv) { @@ -5843,13 +5975,14 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .register_overflow_unsigned, .register_overflow_signed, => unreachable, + .avx_register => unreachable, .ptr_stack_offset => |off| { if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { return self.fail("stack offset too large", .{}); } _ = try self.addInst(.{ .tag = .lea, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(reg, abi_size), .reg2 = .rbp, }).encode(), @@ -5889,7 +6022,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }; _ = try self.addInst(.{ .tag = tag, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = reg.to8(), .flags = flags, }).encode(), @@ -5902,7 +6035,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void if (x == 0) { _ = try self.addInst(.{ .tag = .xor, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = reg.to32(), .reg2 = reg.to32(), }).encode(), @@ -5914,7 +6047,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void // Next best case: if we set the lower four bytes, the upper four will be zeroed. _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(reg, abi_size), }).encode(), .data = .{ .imm = @truncate(u32, x) }, @@ -5931,7 +6064,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void const payload = try self.addExtra(Mir.Imm64.encode(x)); _ = try self.addInst(.{ .tag = .movabs, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = reg.to64(), }).encode(), .data = .{ .payload = payload }, @@ -5948,7 +6081,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void if (abi_size > 4) break :blk; _ = try self.addInst(.{ .tag = .mov_sign_extend, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = reg.to64(), .reg2 = registerAlias(src_reg, abi_size), }).encode(), @@ -5959,7 +6092,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void if (abi_size > 2) break :blk; _ = try self.addInst(.{ .tag = .mov_zero_extend, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = reg.to64(), .reg2 = registerAlias(src_reg, abi_size), }).encode(), @@ -5972,7 +6105,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(reg, abi_size), .reg2 = registerAlias(src_reg, abi_size), }).encode(), @@ -5985,7 +6118,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void try self.loadMemPtrIntoRegister(reg, Type.usize, mcv); _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(reg, abi_size), .reg2 = reg.to64(), .flags = 0b01, @@ -5998,7 +6131,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void // mov reg, [ds:imm32] _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(reg, abi_size), .flags = 0b01, }).encode(), @@ -6012,7 +6145,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void const payload = try self.addExtra(Mir.Imm64.encode(x)); _ = try self.addInst(.{ .tag = .movabs, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = .rax, .flags = 0b01, // imm64 will become moffs64 }).encode(), @@ -6025,7 +6158,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void // mov reg, [reg + 0x0] _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(reg, abi_size), .reg2 = reg.to64(), .flags = 0b01, @@ -6051,7 +6184,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }; _ = try self.addInst(.{ .tag = .mov_sign_extend, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = reg.to64(), .reg2 = .rbp, .flags = flags, @@ -6067,7 +6200,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }; _ = try self.addInst(.{ .tag = .mov_zero_extend, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = reg.to64(), .reg2 = .rbp, .flags = flags, @@ -6081,7 +6214,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = registerAlias(reg, abi_size), .reg2 = .rbp, .flags = 0b01, @@ -6152,7 +6285,7 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { }; _ = try self.addInst(.{ .tag = .fld, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .flags = switch (src_ty.abiSize(self.target.*)) { 4 => 0b01, 8 => 0b10, @@ -6167,7 +6300,7 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { const stack_dst = try self.allocRegOrMem(inst, false); _ = try self.addInst(.{ .tag = .fisttp, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .flags = switch (dst_ty.abiSize(self.target.*)) { 1...2 => 0b00, 3...4 => 0b01, @@ -6271,7 +6404,7 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr); _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops{ + .ops = (Mir.Ops(Register, Register){ .reg1 = reg, .reg2 = reg, .flags = 0b01, @@ -6840,7 +6973,20 @@ fn registerAlias(reg: Register, size_bytes: u32) Register { } else if (size_bytes <= 8) { return reg.to64(); } else { - unreachable; // TODO handle floating-point registers + unreachable; + } +} + +/// Returns AVX register wide enough to hold at least `size_bytes`. +fn avxRegisterAlias(reg: AvxRegister, size_bytes: u32) AvxRegister { + if (size_bytes == 0) { + unreachable; // should be comptime known + } else if (size_bytes <= 16) { + return reg.to128(); + } else if (size_bytes <= 32) { + return reg.to256(); + } else { + unreachable; } } diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 9015a805fb..80034ab536 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -68,6 +68,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void { const inst = @intCast(u32, index); try emit.code_offset_mapping.putNoClobber(emit.bin_file.allocator, inst, emit.code.items.len); switch (tag) { + // GPR instructions .adc => try emit.mirArith(.adc, inst), .add => try emit.mirArith(.add, inst), .sub => try emit.mirArith(.sub, inst), @@ -182,6 +183,10 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .interrupt => try emit.mirInterrupt(inst), .nop => try emit.mirNop(), + // AVX instructions + .mov_f64 => try emit.mirMovF64(inst), + + // Pseudo-instructions .call_extern => try emit.mirCallExtern(inst), .dbg_line => try emit.mirDbgLine(inst), @@ -245,7 +250,7 @@ fn mirSyscall(emit: *Emit) InnerError!void { } fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { 0b00 => { // PUSH/POP reg @@ -274,7 +279,7 @@ fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { } fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); const payload = emit.mir.instructions.items(.data)[inst].payload; const data = emit.mir.extraData(Mir.RegsToPushOrPop, payload).data; const regs = data.regs; @@ -297,7 +302,7 @@ fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.I } fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { 0b00 => { const target = emit.mir.instructions.items(.data)[inst].inst; @@ -336,7 +341,7 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { } fn mirCondJmp(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); const target = emit.mir.instructions.items(.data)[inst].inst; const tag = switch (mir_tag) { .cond_jmp_greater_less => switch (ops.flags) { @@ -368,7 +373,7 @@ fn mirCondJmp(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerErr } fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); const tag = switch (mir_tag) { .cond_set_byte_greater_less => switch (ops.flags) { 0b00 => Tag.setge, @@ -398,7 +403,7 @@ fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) Inne } fn mirCondMov(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); if (ops.flags == 0b00) { return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), emit.code); } @@ -418,7 +423,7 @@ fn mirCondMov(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .@"test"); - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { 0b00 => { if (ops.reg2 == .none) { @@ -447,7 +452,7 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .ret); - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { 0b00 => { // RETF imm16 @@ -471,7 +476,7 @@ fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { 0b00 => { if (ops.reg2 == .none) { @@ -513,7 +518,7 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { } fn mirArithMemImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); assert(ops.reg2 == .none); const payload = emit.mir.instructions.items(.data)[inst].payload; const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; @@ -554,7 +559,7 @@ inline fn immOpSize(u_imm: u32) u8 { } fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); const scale = ops.flags; const imm = emit.mir.instructions.items(.data)[inst].imm; // OP reg1, [reg2 + scale*rcx + imm32] @@ -570,7 +575,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void } fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); const scale = ops.flags; const imm = emit.mir.instructions.items(.data)[inst].imm; const scale_index = ScaleIndex{ @@ -594,7 +599,7 @@ fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void } fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); const scale = ops.flags; const payload = emit.mir.instructions.items(.data)[inst].payload; const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; @@ -611,7 +616,7 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void } fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); assert(ops.reg2 == .none); const payload = emit.mir.instructions.items(.data)[inst].payload; const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; @@ -636,7 +641,7 @@ fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!v fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const mir_tag = emit.mir.instructions.items(.tag)[inst]; assert(mir_tag == .mov_sign_extend); - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); const imm = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].imm else undefined; switch (ops.flags) { 0b00 => { @@ -667,7 +672,7 @@ fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { fn mirMovZeroExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const mir_tag = emit.mir.instructions.items(.tag)[inst]; assert(mir_tag == .mov_zero_extend); - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); const imm = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].imm else undefined; switch (ops.flags) { 0b00 => { @@ -694,7 +699,7 @@ fn mirMovZeroExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .movabs); - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); const imm: u64 = if (ops.reg1.size() == 64) blk: { const payload = emit.mir.instructions.items(.data)[inst].payload; const imm = emit.mir.extraData(Mir.Imm64, payload).data; @@ -718,7 +723,7 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { fn mirFisttp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .fisttp); - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); // the selecting between operand sizes for this particular `fisttp` instruction // is done via opcode instead of the usual prefixes. @@ -740,7 +745,7 @@ fn mirFisttp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { fn mirFld(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .fld); - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); // the selecting between operand sizes for this particular `fisttp` instruction // is done via opcode instead of the usual prefixes. @@ -757,8 +762,9 @@ fn mirFld(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { }; return lowerToMEnc(opcode, .{ .memory = mem_or_reg }, emit.code); } + fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { 0b00 => { // sal reg1, 1 @@ -783,7 +789,7 @@ fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { } fn mirMulDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); if (ops.reg1 != .none) { assert(ops.reg2 == .none); return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); @@ -806,7 +812,7 @@ fn mirMulDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .imul_complex); - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { 0b00 => { return lowerToRmEnc(.imul, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), emit.code); @@ -835,7 +841,7 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } fn mirCwd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); const tag: Tag = switch (ops.flags) { 0b00 => .cbw, 0b01 => .cwd, @@ -848,7 +854,7 @@ fn mirCwd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .lea); - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { 0b00 => { // lea reg1, [reg2 + imm32] @@ -908,7 +914,7 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .lea_pie); - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); const load_reloc = emit.mir.instructions.items(.data)[inst].load_reloc; // lea reg1, [rip + reloc] @@ -947,6 +953,36 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } } +// AVX instructions + +fn mirMovF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; + assert(tag == .mov_f64); + const ops = emit.mir.instructions.items(.ops)[inst]; + const flags = @truncate(u2, ops); + const imm = emit.mir.instructions.items(.data)[inst].imm; + + switch (flags) { + 0b00 => { + const decoded = Mir.Ops(AvxRegister, GpRegister).decode(ops); + return lowerToRmEnc(.vmovsd, Register.avxReg(decoded.reg1), RegisterOrMemory.mem(.qword_ptr, .{ + .disp = imm, + .base = decoded.reg2, + }), emit.code); + }, + 0b01 => { + const decoded = Mir.Ops(GpRegister, AvxRegister).decode(ops); + return lowerToMrEnc(.vmovsd, RegisterOrMemory.mem(.qword_ptr, .{ + .disp = imm, + .base = decoded.reg1, + }), Register.avxReg(decoded.reg2), emit.code); + }, + else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{flags}), + } +} + +// Pseudo-instructions + fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .call_extern); diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 30f4351cb0..ef9679496d 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -14,7 +14,8 @@ const assert = std.debug.assert; const bits = @import("bits.zig"); const Air = @import("../../Air.zig"); const CodeGen = @import("CodeGen.zig"); -const Register = bits.Register; +const GpRegister = bits.Register; +const AvxRegister = bits.AvxRegister; instructions: std.MultiArrayList(Inst).Slice, /// The meaning of this data is determined by `Inst.Tag` value. @@ -349,6 +350,12 @@ pub const Inst = struct { /// Nop nop, + /// AVX instructions + /// ops flags: form: + /// 0b00 reg1, qword ptr [reg2 + imm32] + /// 0b10 qword ptr [reg1 + imm32], reg2 + mov_f64, + /// Pseudo-instructions /// call extern function /// Notes: @@ -450,30 +457,32 @@ pub const DbgLineColumn = struct { column: u32, }; -pub const Ops = struct { - reg1: Register = .none, - reg2: Register = .none, - flags: u2 = 0b00, +pub fn Ops(comptime Reg1: type, comptime Reg2: type) type { + return struct { + reg1: Reg1 = .none, + reg2: Reg2 = .none, + flags: u2 = 0b00, - pub fn encode(self: Ops) u16 { - var ops: u16 = 0; - ops |= @intCast(u16, @enumToInt(self.reg1)) << 9; - ops |= @intCast(u16, @enumToInt(self.reg2)) << 2; - ops |= self.flags; - return ops; - } + pub fn encode(self: @This()) u16 { + var ops: u16 = 0; + ops |= @intCast(u16, @enumToInt(self.reg1)) << 9; + ops |= @intCast(u16, @enumToInt(self.reg2)) << 2; + ops |= self.flags; + return ops; + } - pub fn decode(ops: u16) Ops { - const reg1 = @intToEnum(Register, @truncate(u7, ops >> 9)); - const reg2 = @intToEnum(Register, @truncate(u7, ops >> 2)); - const flags = @truncate(u2, ops); - return .{ - .reg1 = reg1, - .reg2 = reg2, - .flags = flags, - }; - } -}; + pub fn decode(ops: u16) @This() { + const reg1 = @intToEnum(Reg1, @truncate(u7, ops >> 9)); + const reg2 = @intToEnum(Reg2, @truncate(u7, ops >> 2)); + const flags = @truncate(u2, ops); + return .{ + .reg1 = reg1, + .reg2 = reg2, + .flags = flags, + }; + } + }; +} pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { mir.instructions.deinit(gpa); diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index da2e3da394..a53c82530d 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -3,6 +3,7 @@ const Type = @import("../../type.zig").Type; const Target = std.Target; const assert = std.debug.assert; const Register = @import("bits.zig").Register; +const AvxRegister = @import("bits.zig").AvxRegister; pub const Class = enum { integer, sse, sseup, x87, x87up, complex_x87, memory, none }; @@ -381,3 +382,8 @@ pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8 pub const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs; pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; + +pub const avx_regs = [_]AvxRegister{ + .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7, + .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15, +}; diff --git a/src/codegen.zig b/src/codegen.zig index def69d952f..c14fb39629 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -78,13 +78,13 @@ pub fn generateFunction( debug_output: DebugInfoOutput, ) GenerateSymbolError!FnResult { switch (bin_file.options.target.cpu.arch) { - .arm, - .armeb, - => return @import("arch/arm/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), - .aarch64, - .aarch64_be, - .aarch64_32, - => return @import("arch/aarch64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), + // .arm, + // .armeb, + // => return @import("arch/arm/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), + // .aarch64, + // .aarch64_be, + // .aarch64_32, + // => return @import("arch/aarch64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.arc => return Function(.arc).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.avr => return Function(.avr).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.bpfel => return Function(.bpfel).generate(bin_file, src_loc, func, air, liveness, code, debug_output), @@ -101,9 +101,9 @@ pub fn generateFunction( //.r600 => return Function(.r600).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.amdgcn => return Function(.amdgcn).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.riscv32 => return Function(.riscv32).generate(bin_file, src_loc, func, air, liveness, code, debug_output), - .riscv64 => return @import("arch/riscv64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), + // .riscv64 => return @import("arch/riscv64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.sparc => return Function(.sparc).generate(bin_file, src_loc, func, air, liveness, code, debug_output), - .sparc64 => return @import("arch/sparc64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), + // .sparc64 => return @import("arch/sparc64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.sparcel => return Function(.sparcel).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.s390x => return Function(.s390x).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.tce => return Function(.tce).generate(bin_file, src_loc, func, air, liveness, code, debug_output), @@ -129,9 +129,9 @@ pub fn generateFunction( //.renderscript32 => return Function(.renderscript32).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.renderscript64 => return Function(.renderscript64).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.ve => return Function(.ve).generate(bin_file, src_loc, func, air, liveness, code, debug_output), - .wasm32, - .wasm64, - => return @import("arch/wasm/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), + // .wasm32, + // .wasm64, + // => return @import("arch/wasm/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."), } } diff --git a/src/register_manager.zig b/src/register_manager.zig index 2c0502e867..bcec8e14e6 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -23,10 +23,15 @@ pub const AllocateRegistersError = error{ CodegenFail, }; +pub fn SpillFn(comptime Function: type, comptime Register: type) type { + return fn (*Function, Register, Air.Inst.Index) anyerror!void; +} + pub fn RegisterManager( comptime Function: type, comptime Register: type, comptime tracked_registers: []const Register, + comptime spill_fn: SpillFn(Function, Register), ) type { // architectures which do not have a concept of registers should // refrain from using RegisterManager @@ -47,6 +52,7 @@ pub fn RegisterManager( allocated_registers: FreeRegInt = 0, /// Tracks registers which are locked from being allocated locked_registers: FreeRegInt = 0, + function: *Function, const Self = @This(); @@ -55,8 +61,8 @@ pub fn RegisterManager( const FreeRegInt = std.meta.Int(.unsigned, tracked_registers.len); const ShiftInt = math.Log2Int(FreeRegInt); - fn getFunction(self: *Self) *Function { - return @fieldParentPtr(Function, "register_manager", self); + fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) AllocateRegistersError!void { + return try spill_fn(self.function, reg, inst); } fn getRegisterMask(reg: Register) ?FreeRegInt { @@ -251,14 +257,14 @@ pub fn RegisterManager( self.markRegUsed(reg); } else { const spilled_inst = self.registers[index]; - try self.getFunction().spillInstruction(reg, spilled_inst); + try self.spillInstruction(reg, spilled_inst); } self.registers[index] = inst; } else { // Don't track the register if (!self.isRegFree(reg)) { const spilled_inst = self.registers[index]; - try self.getFunction().spillInstruction(reg, spilled_inst); + try self.spillInstruction(reg, spilled_inst); self.freeReg(reg); } } @@ -293,7 +299,7 @@ pub fn RegisterManager( // stack allocation. const spilled_inst = self.registers[index]; self.registers[index] = tracked_inst; - try self.getFunction().spillInstruction(reg, spilled_inst); + try self.spillInstruction(reg, spilled_inst); } else { self.getRegAssumeFree(reg, tracked_inst); } @@ -302,7 +308,7 @@ pub fn RegisterManager( // Move the instruction that was previously there to a // stack allocation. const spilled_inst = self.registers[index]; - try self.getFunction().spillInstruction(reg, spilled_inst); + try self.spillInstruction(reg, spilled_inst); self.freeReg(reg); } } diff --git a/src/test.zig b/src/test.zig index a6537e77c7..b272d05718 100644 --- a/src/test.zig +++ b/src/test.zig @@ -34,18 +34,18 @@ test { var ctx = TestContext.init(std.testing.allocator, arena); defer ctx.deinit(); - { - const dir_path = try std.fs.path.join(arena, &.{ - std.fs.path.dirname(@src().file).?, "..", "test", "cases", - }); + // { + // const dir_path = try std.fs.path.join(arena, &.{ + // std.fs.path.dirname(@src().file).?, "..", "test", "cases", + // }); - var dir = try std.fs.cwd().openDir(dir_path, .{ .iterate = true }); - defer dir.close(); + // var dir = try std.fs.cwd().openDir(dir_path, .{ .iterate = true }); + // defer dir.close(); - ctx.addTestCasesFromDir(dir); - } + // ctx.addTestCasesFromDir(dir); + // } - try @import("test_cases").addCases(&ctx); + // try @import("test_cases").addCases(&ctx); try ctx.run(); } From 2aee2302515ba444999b82c2e40cbc35dee08baf Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 14 May 2022 01:50:07 +0200 Subject: [PATCH 1577/2031] x64: add unordered cmp with EFLAGS --- src/arch/x86_64/CodeGen.zig | 133 ++++++++++++- src/arch/x86_64/Emit.zig | 217 +++++++++++++++++++- src/arch/x86_64/Mir.zig | 11 +- src/register_manager.zig | 386 ++++++++++++++++++------------------ 4 files changed, 538 insertions(+), 209 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index d0bed75fbd..e81f2d5435 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -202,6 +202,7 @@ pub const MCValue = union(enum) { fn isRegister(mcv: MCValue) bool { return switch (mcv) { .register => true, + .avx_register => true, else => false, }; } @@ -971,6 +972,7 @@ pub fn spillCompareFlagsIfOccupied(self: *Self) !void { .compare_flags_signed, .compare_flags_unsigned, => try self.allocRegOrMem(inst_to_save, true), + .avx_register => try self.allocRegOrMem(inst_to_save, false), else => unreachable, }; @@ -988,6 +990,7 @@ pub fn spillCompareFlagsIfOccupied(self: *Self) !void { .register_overflow_signed, .register_overflow_unsigned, => |reg| self.register_manager.freeReg(reg), + .avx_register => |reg| self.avx_register_manager.freeReg(reg), else => {}, } } @@ -2497,7 +2500,6 @@ fn reuseOperand( fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { const elem_ty = ptr_ty.elemType(); const abi_size = elem_ty.abiSize(self.target.*); - std.log.warn("{} => {}, {}", .{ ptr_ty.fmtDebug(), ptr, dst_mcv }); switch (ptr) { .none => unreachable, .undef => unreachable, @@ -2627,7 +2629,6 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { const abi_size = value_ty.abiSize(self.target.*); - std.log.warn("{} => {}, {} => {}", .{ ptr_ty.fmtDebug(), ptr, value_ty.fmtDebug(), value }); switch (ptr) { .none => unreachable, .undef => unreachable, @@ -3375,13 +3376,39 @@ fn genBinOp( const rhs = try self.resolveInst(rhs_air); const lhs_ty = self.air.typeOf(lhs_air); const rhs_ty = self.air.typeOf(rhs_air); - if (lhs_ty.zigTypeTag() == .Vector or lhs_ty.zigTypeTag() == .Float) { + if (lhs_ty.zigTypeTag() == .Vector) { return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()}); } if (lhs_ty.abiSize(self.target.*) > 8) { return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()}); } + if (lhs_ty.zigTypeTag() == .Float) { + switch (tag) { + .add => { + const dst_reg: AvxRegister = blk: { + const reg = try self.avx_register_manager.allocReg(null); + try self.genSetAvxReg(lhs_ty, reg, lhs); + break :blk reg.to128(); + }; + const dst_lock = self.avx_register_manager.lockRegAssumeUnused(dst_reg); + defer self.avx_register_manager.unlockReg(dst_lock); + + const src_reg: AvxRegister = blk: { + const reg = try self.avx_register_manager.allocReg(null); + try self.genSetAvxReg(lhs_ty, reg, rhs); + break :blk reg.to128(); + }; + const src_lock = self.avx_register_manager.lockRegAssumeUnused(src_reg); + defer self.avx_register_manager.unlockReg(src_lock); + + try self.genBinOpMir(.add_f64, lhs_ty, .{ .avx_register = dst_reg }, .{ .avx_register = src_reg }); + return MCValue{ .avx_register = dst_reg }; + }, + else => unreachable, + } + } + const is_commutative: bool = switch (tag) { .add, .addwrap, @@ -3550,8 +3577,28 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu }, } }, - .avx_register => { - return self.fail("TODO genBinOp for AVX register", .{}); + .avx_register => |dst_reg| { + switch (src_mcv) { + .avx_register => |src_reg| { + switch (dst_ty.zigTypeTag()) { + .Float => switch (dst_ty.tag()) { + .f64 => { + _ = try self.addInst(.{ + .tag = mir_tag, + .ops = (Mir.Ops(AvxRegister, AvxRegister){ + .reg1 = dst_reg.to128(), + .reg2 = src_reg.to128(), + }).encode(), + .data = undefined, + }); + }, + else => return self.fail("TODO genBinOp for AVX register and type {}", .{dst_ty.fmtDebug()}), + }, + else => return self.fail("TODO genBinOp for AVX register and type {}", .{dst_ty.fmtDebug()}), + } + }, + else => return self.fail("TODO genBinOp for AVX register", .{}), + } }, .ptr_stack_offset, .stack_offset => |off| { if (off > math.maxInt(i32)) { @@ -4209,6 +4256,37 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { self.compare_flags_inst = inst; const result: MCValue = result: { + if (ty.zigTypeTag() == .Float) { + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + const dst_reg: AvxRegister = blk: { + const reg = try self.avx_register_manager.allocReg(null); + try self.genSetAvxReg(ty, reg, lhs); + break :blk reg.to128(); + }; + const dst_lock = self.avx_register_manager.lockRegAssumeUnused(dst_reg); + defer self.avx_register_manager.unlockReg(dst_lock); + + const src_reg: AvxRegister = blk: { + const reg = try self.avx_register_manager.allocReg(null); + try self.genSetAvxReg(ty, reg, rhs); + break :blk reg.to128(); + }; + const src_lock = self.avx_register_manager.lockRegAssumeUnused(src_reg); + defer self.avx_register_manager.unlockReg(src_lock); + + _ = try self.addInst(.{ + .tag = .cmp_f64, + .ops = (Mir.Ops(AvxRegister, AvxRegister){ + .reg1 = dst_reg, + .reg2 = src_reg, + }).encode(), + .data = undefined, + }); + + break :result MCValue{ .compare_flags_unsigned = op }; + } // There are 2 operands, destination and source. // Either one, but not both, can be a memory operand. // Source operand can be an immediate, 8 bits or 32 bits. @@ -5962,6 +6040,51 @@ fn genSetAvxReg(self: *Self, ty: Type, reg: AvxRegister, mcv: MCValue) InnerErro else => return self.fail("TODO genSetAvxReg from stack offset for type {}", .{ty.fmtDebug()}), } }, + .avx_register => |src_reg| { + switch (ty.zigTypeTag()) { + .Float => { + switch (ty.tag()) { + .f32 => return self.fail("TODO genSetAvxReg from register for f32", .{}), + .f64 => { + _ = try self.addInst(.{ + .tag = .mov_f64, + .ops = (Mir.Ops(AvxRegister, AvxRegister){ + .reg1 = reg.to128(), + .reg2 = src_reg.to128(), + .flags = 0b10, + }).encode(), + .data = undefined, + }); + }, + else => return self.fail("TODO genSetAvxReg from register for {}", .{ty.fmtDebug()}), + } + }, + else => return self.fail("TODO genSetAvxReg from register for type {}", .{ty.fmtDebug()}), + } + }, + .memory => { + switch (ty.zigTypeTag()) { + .Float => { + switch (ty.tag()) { + .f32 => return self.fail("TODO genSetAvxReg from memory for f32", .{}), + .f64 => { + const base_reg = try self.register_manager.allocReg(null); + try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); + _ = try self.addInst(.{ + .tag = .mov_f64, + .ops = (Mir.Ops(AvxRegister, Register){ + .reg1 = reg.to128(), + .reg2 = base_reg.to64(), + }).encode(), + .data = .{ .imm = 0 }, + }); + }, + else => return self.fail("TODO genSetAvxReg from memory for {}", .{ty.fmtDebug()}), + } + }, + else => return self.fail("TODO genSetAvxReg from memory for type {}", .{ty.fmtDebug()}), + } + }, else => |other| { return self.fail("TODO genSetAvxReg from {}", .{other}); }, diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 80034ab536..79341df8cd 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -186,6 +186,10 @@ pub fn lowerMir(emit: *Emit) InnerError!void { // AVX instructions .mov_f64 => try emit.mirMovF64(inst), + .add_f64 => try emit.mirAddF64(inst), + + .cmp_f64 => try emit.mirCmpF64(inst), + // Pseudo-instructions .call_extern => try emit.mirCallExtern(inst), @@ -960,11 +964,11 @@ fn mirMovF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { assert(tag == .mov_f64); const ops = emit.mir.instructions.items(.ops)[inst]; const flags = @truncate(u2, ops); - const imm = emit.mir.instructions.items(.data)[inst].imm; switch (flags) { 0b00 => { const decoded = Mir.Ops(AvxRegister, GpRegister).decode(ops); + const imm = emit.mir.instructions.items(.data)[inst].imm; return lowerToRmEnc(.vmovsd, Register.avxReg(decoded.reg1), RegisterOrMemory.mem(.qword_ptr, .{ .disp = imm, .base = decoded.reg2, @@ -972,11 +976,63 @@ fn mirMovF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { }, 0b01 => { const decoded = Mir.Ops(GpRegister, AvxRegister).decode(ops); + const imm = emit.mir.instructions.items(.data)[inst].imm; return lowerToMrEnc(.vmovsd, RegisterOrMemory.mem(.qword_ptr, .{ .disp = imm, .base = decoded.reg1, }), Register.avxReg(decoded.reg2), emit.code); }, + 0b10 => { + const decoded = Mir.Ops(AvxRegister, AvxRegister).decode(ops); + return lowerToRvmEnc( + .vmovsd, + Register.avxReg(decoded.reg1), + Register.avxReg(decoded.reg1), + RegisterOrMemory.avxReg(decoded.reg2), + emit.code, + ); + }, + else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{flags}), + } +} + +fn mirAddF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; + assert(tag == .add_f64); + const ops = emit.mir.instructions.items(.ops)[inst]; + const flags = @truncate(u2, ops); + + switch (flags) { + 0b00 => { + const decoded = Mir.Ops(AvxRegister, AvxRegister).decode(ops); + return lowerToRvmEnc( + .vaddsd, + Register.avxReg(decoded.reg1), + Register.avxReg(decoded.reg1), + RegisterOrMemory.avxReg(decoded.reg2), + emit.code, + ); + }, + else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{flags}), + } +} + +fn mirCmpF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; + assert(tag == .cmp_f64); + const ops = emit.mir.instructions.items(.ops)[inst]; + const flags = @truncate(u2, ops); + + switch (flags) { + 0b00 => { + const decoded = Mir.Ops(AvxRegister, AvxRegister).decode(ops); + return lowerToRmEnc( + .vucomisd, + Register.avxReg(decoded.reg1), + RegisterOrMemory.avxReg(decoded.reg2), + emit.code, + ); + }, else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{flags}), } } @@ -1217,6 +1273,9 @@ const Tag = enum { cmovb, cmovnae, vmovsd, + vaddsd, + vcmpsd, + vucomisd, fn isSetCC(tag: Tag) bool { return switch (tag) { @@ -1301,6 +1360,12 @@ const Encoding = enum { /// OP r64, r/m64, imm32 rmi, + + /// OP xmm1, xmm2, xmm3/m64 + rvm, + + /// OP xmm1, xmm2, xmm3/m64, imm8 + rvmi, }; const OpCode = union(enum) { @@ -1452,6 +1517,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .cmovb, .cmovnae => OpCode.twoByte(0x0f, 0x42), .cmovl, .cmovng => OpCode.twoByte(0x0f, 0x4c), .vmovsd => OpCode.oneByte(0x10), + .vucomisd => OpCode.oneByte(0x2e), else => null, }, .oi => return switch (tag) { @@ -1470,6 +1536,15 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .imul => OpCode.oneByte(if (is_one_byte) 0x6b else 0x69), else => null, }, + .rvm => return switch (tag) { + .vaddsd => OpCode.oneByte(0x58), + .vmovsd => OpCode.oneByte(0x10), + else => null, + }, + .rvmi => return switch (tag) { + .vcmpsd => OpCode.oneByte(0xc2), + else => null, + }, } } @@ -1578,6 +1653,16 @@ inline fn getVexPrefix(tag: Tag, enc: Encoding) ?VexPrefix { }, .rm => switch (tag) { .vmovsd => break :blk .{ .lig = true, .simd_prefix = .p_f2, .wig = true }, + .vucomisd => break :blk .{ .lig = true, .simd_prefix = .p_66, .wig = true }, + else => return null, + }, + .rvm => switch (tag) { + .vaddsd => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f2, .wig = true }, + .vmovsd => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f2, .wig = true }, + else => return null, + }, + .rvmi => switch (tag) { + .vcmpsd => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f2, .wig = true }, else => return null, }, else => unreachable, @@ -2013,15 +2098,33 @@ fn lowerToRmEnc( const opc = getOpCode(tag, .rm, reg.size() == 8 or reg_or_mem.size() == 8).?; switch (reg_or_mem) { .register => |src_reg| { - const encoder = try Encoder.init(code, 4); - if (reg.size() == 16) { - encoder.prefix16BitMode(); - } - encoder.rex(.{ - .w = setRexWRegister(reg) or setRexWRegister(src_reg), - .r = reg.isExtended(), - .b = src_reg.isExtended(), - }); + const encoder: Encoder = blk: { + switch (reg) { + .register => { + const encoder = try Encoder.init(code, 4); + if (reg.size() == 16) { + encoder.prefix16BitMode(); + } + encoder.rex(.{ + .w = setRexWRegister(reg) or setRexWRegister(src_reg), + .r = reg.isExtended(), + .b = src_reg.isExtended(), + }); + break :blk encoder; + }, + .avx_register => { + const encoder = try Encoder.init(code, 5); + var vex_prefix = getVexPrefix(tag, .rm).?; + const vex = &vex_prefix.prefix; + vex.rex(.{ + .r = reg.isExtended(), + .b = src_reg.isExtended(), + }); + encoder.vex(vex_prefix.prefix); + break :blk encoder; + }, + } + }; opc.encode(encoder); encoder.modRm_direct(reg.lowId(), src_reg.lowId()); }, @@ -2188,6 +2291,79 @@ fn lowerToRmiEnc( encodeImm(encoder, imm, reg.size()); } +fn lowerToRvmEnc( + tag: Tag, + reg1: Register, + reg2: Register, + reg_or_mem: RegisterOrMemory, + code: *std.ArrayList(u8), +) InnerError!void { + const opc = getOpCode(tag, .rvm, false).?; + var vex_prefix = getVexPrefix(tag, .rvm).?; + const vex = &vex_prefix.prefix; + switch (reg_or_mem) { + .register => |reg3| { + if (vex_prefix.reg) |vvvv| { + switch (vvvv) { + .nds => vex.reg(reg2.avx_register.id()), + else => unreachable, // TODO + } + } + const encoder = try Encoder.init(code, 5); + vex.rex(.{ + .r = reg1.isExtended(), + .b = reg3.isExtended(), + }); + encoder.vex(vex_prefix.prefix); + opc.encode(encoder); + encoder.modRm_direct(reg1.lowId(), reg3.lowId()); + }, + .memory => |dst_mem| { + _ = dst_mem; + unreachable; // TODO + }, + } +} + +fn lowerToRvmiEnc( + tag: Tag, + reg1: Register, + reg2: Register, + reg_or_mem: RegisterOrMemory, + imm: u32, + code: *std.ArrayList(u8), +) InnerError!void { + const opc = getOpCode(tag, .rvmi, false).?; + var vex_prefix = getVexPrefix(tag, .rvmi).?; + const vex = &vex_prefix.prefix; + const encoder: Encoder = blk: { + switch (reg_or_mem) { + .register => |reg3| { + if (vex_prefix.reg) |vvvv| { + switch (vvvv) { + .nds => vex.reg(reg2.avx_register.id()), + else => unreachable, // TODO + } + } + const encoder = try Encoder.init(code, 5); + vex.rex(.{ + .r = reg1.isExtended(), + .b = reg3.isExtended(), + }); + encoder.vex(vex_prefix.prefix); + opc.encode(encoder); + encoder.modRm_direct(reg1.lowId(), reg3.lowId()); + break :blk encoder; + }, + .memory => |dst_mem| { + _ = dst_mem; + unreachable; // TODO + }, + } + }; + encodeImm(encoder, imm, 8); // TODO +} + fn expectEqualHexStrings(expected: []const u8, given: []const u8, assembly: []const u8) !void { assert(expected.len > 0); if (mem.eql(u8, expected, given)) return; @@ -2598,3 +2774,24 @@ test "lower RMI encoding" { try lowerToRmiEnc(.imul, Register.reg(.r12w), RegisterOrMemory.reg(.r12w), 0x10, emit.code()); try expectEqualHexStrings("\x66\x45\x69\xE4\x10\x00", emit.lowered(), "imul r12w, r12w, 0x10"); } + +test "lower to RVM encoding" { + var emit = TestEmit.init(); + defer emit.deinit(); + try lowerToRvmEnc( + .vaddsd, + Register.avxReg(.xmm0), + Register.avxReg(.xmm1), + RegisterOrMemory.avxReg(.xmm2), + emit.code(), + ); + try expectEqualHexStrings("\xC5\xF3\x58\xC2", emit.lowered(), "vaddsd xmm0, xmm1, xmm2"); + try lowerToRvmEnc( + .vaddsd, + Register.avxReg(.xmm0), + Register.avxReg(.xmm0), + RegisterOrMemory.avxReg(.xmm1), + emit.code(), + ); + try expectEqualHexStrings("\xC5\xFB\x58\xC1", emit.lowered(), "vaddsd xmm0, xmm0, xmm1"); +} diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index ef9679496d..ef50279d03 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -353,9 +353,18 @@ pub const Inst = struct { /// AVX instructions /// ops flags: form: /// 0b00 reg1, qword ptr [reg2 + imm32] - /// 0b10 qword ptr [reg1 + imm32], reg2 + /// 0b01 qword ptr [reg1 + imm32], reg2 + /// 0b10 reg1, reg2 mov_f64, + /// ops flags: form: + /// 0b00 reg1, reg1, reg2 + add_f64, + + /// ops flags: form: + /// + cmp_f64, + /// Pseudo-instructions /// call extern function /// Notes: diff --git a/src/register_manager.zig b/src/register_manager.zig index bcec8e14e6..6fc3641467 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -338,253 +338,253 @@ pub fn RegisterManager( }; } -const MockRegister1 = enum(u2) { - r0, - r1, - r2, - r3, +//const MockRegister1 = enum(u2) { +// r0, +// r1, +// r2, +// r3, - pub fn id(reg: MockRegister1) u2 { - return @enumToInt(reg); - } +// pub fn id(reg: MockRegister1) u2 { +// return @enumToInt(reg); +// } - const allocatable_registers = [_]MockRegister1{ .r2, .r3 }; -}; +// const allocatable_registers = [_]MockRegister1{ .r2, .r3 }; +//}; -const MockRegister2 = enum(u2) { - r0, - r1, - r2, - r3, +//const MockRegister2 = enum(u2) { +// r0, +// r1, +// r2, +// r3, - pub fn id(reg: MockRegister2) u2 { - return @enumToInt(reg); - } +// pub fn id(reg: MockRegister2) u2 { +// return @enumToInt(reg); +// } - const allocatable_registers = [_]MockRegister2{ .r0, .r1, .r2, .r3 }; -}; +// const allocatable_registers = [_]MockRegister2{ .r0, .r1, .r2, .r3 }; +//}; -fn MockFunction(comptime Register: type) type { - return struct { - allocator: Allocator, - register_manager: RegisterManager(Self, Register, &Register.allocatable_registers) = .{}, - spilled: std.ArrayListUnmanaged(Register) = .{}, +//fn MockFunction(comptime Register: type) type { +// return struct { +// allocator: Allocator, +// register_manager: RegisterManager(Self, Register, &Register.allocatable_registers) = .{}, +// spilled: std.ArrayListUnmanaged(Register) = .{}, - const Self = @This(); +// const Self = @This(); - pub fn deinit(self: *Self) void { - self.spilled.deinit(self.allocator); - } +// pub fn deinit(self: *Self) void { +// self.spilled.deinit(self.allocator); +// } - pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { - _ = inst; - try self.spilled.append(self.allocator, reg); - } +// pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { +// _ = inst; +// try self.spilled.append(self.allocator, reg); +// } - pub fn genAdd(self: *Self, res: Register, lhs: Register, rhs: Register) !void { - _ = self; - _ = res; - _ = lhs; - _ = rhs; - } - }; -} +// pub fn genAdd(self: *Self, res: Register, lhs: Register, rhs: Register) !void { +// _ = self; +// _ = res; +// _ = lhs; +// _ = rhs; +// } +// }; +//} -const MockFunction1 = MockFunction(MockRegister1); -const MockFunction2 = MockFunction(MockRegister2); +//const MockFunction1 = MockFunction(MockRegister1); +//const MockFunction2 = MockFunction(MockRegister2); -test "default state" { - const allocator = std.testing.allocator; +//test "default state" { +// const allocator = std.testing.allocator; - var function = MockFunction1{ - .allocator = allocator, - }; - defer function.deinit(); +// var function = MockFunction1{ +// .allocator = allocator, +// }; +// defer function.deinit(); - try expect(!function.register_manager.isRegAllocated(.r2)); - try expect(!function.register_manager.isRegAllocated(.r3)); - try expect(function.register_manager.isRegFree(.r2)); - try expect(function.register_manager.isRegFree(.r3)); -} +// try expect(!function.register_manager.isRegAllocated(.r2)); +// try expect(!function.register_manager.isRegAllocated(.r3)); +// try expect(function.register_manager.isRegFree(.r2)); +// try expect(function.register_manager.isRegFree(.r3)); +//} -test "tryAllocReg: no spilling" { - const allocator = std.testing.allocator; +//test "tryAllocReg: no spilling" { +// const allocator = std.testing.allocator; - var function = MockFunction1{ - .allocator = allocator, - }; - defer function.deinit(); +// var function = MockFunction1{ +// .allocator = allocator, +// }; +// defer function.deinit(); - const mock_instruction: Air.Inst.Index = 1; +// const mock_instruction: Air.Inst.Index = 1; - try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(mock_instruction)); - try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(mock_instruction)); - try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(mock_instruction)); +// try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(mock_instruction)); +// try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(mock_instruction)); +// try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(mock_instruction)); - try expect(function.register_manager.isRegAllocated(.r2)); - try expect(function.register_manager.isRegAllocated(.r3)); - try expect(!function.register_manager.isRegFree(.r2)); - try expect(!function.register_manager.isRegFree(.r3)); +// try expect(function.register_manager.isRegAllocated(.r2)); +// try expect(function.register_manager.isRegAllocated(.r3)); +// try expect(!function.register_manager.isRegFree(.r2)); +// try expect(!function.register_manager.isRegFree(.r3)); - function.register_manager.freeReg(.r2); - function.register_manager.freeReg(.r3); +// function.register_manager.freeReg(.r2); +// function.register_manager.freeReg(.r3); - try expect(function.register_manager.isRegAllocated(.r2)); - try expect(function.register_manager.isRegAllocated(.r3)); - try expect(function.register_manager.isRegFree(.r2)); - try expect(function.register_manager.isRegFree(.r3)); -} +// try expect(function.register_manager.isRegAllocated(.r2)); +// try expect(function.register_manager.isRegAllocated(.r3)); +// try expect(function.register_manager.isRegFree(.r2)); +// try expect(function.register_manager.isRegFree(.r3)); +//} -test "allocReg: spilling" { - const allocator = std.testing.allocator; +//test "allocReg: spilling" { +// const allocator = std.testing.allocator; - var function = MockFunction1{ - .allocator = allocator, - }; - defer function.deinit(); +// var function = MockFunction1{ +// .allocator = allocator, +// }; +// defer function.deinit(); - const mock_instruction: Air.Inst.Index = 1; +// const mock_instruction: Air.Inst.Index = 1; - try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction)); - try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); +// try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction)); +// try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); - // Spill a register - try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction)); - try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); +// // Spill a register +// try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction)); +// try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); - // No spilling necessary - function.register_manager.freeReg(.r3); - try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); - try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); +// // No spilling necessary +// function.register_manager.freeReg(.r3); +// try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); +// try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); - // Locked registers - function.register_manager.freeReg(.r3); - { - const lock = function.register_manager.lockReg(.r2); - defer if (lock) |reg| function.register_manager.unlockReg(reg); +// // Locked registers +// function.register_manager.freeReg(.r3); +// { +// const lock = function.register_manager.lockReg(.r2); +// defer if (lock) |reg| function.register_manager.unlockReg(reg); - try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); - } - try expect(!function.register_manager.lockedRegsExist()); -} +// try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); +// } +// try expect(!function.register_manager.lockedRegsExist()); +//} -test "tryAllocRegs" { - const allocator = std.testing.allocator; +//test "tryAllocRegs" { +// const allocator = std.testing.allocator; - var function = MockFunction2{ - .allocator = allocator, - }; - defer function.deinit(); +// var function = MockFunction2{ +// .allocator = allocator, +// }; +// defer function.deinit(); - try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?); +// try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?); - try expect(function.register_manager.isRegAllocated(.r0)); - try expect(function.register_manager.isRegAllocated(.r1)); - try expect(function.register_manager.isRegAllocated(.r2)); - try expect(!function.register_manager.isRegAllocated(.r3)); +// try expect(function.register_manager.isRegAllocated(.r0)); +// try expect(function.register_manager.isRegAllocated(.r1)); +// try expect(function.register_manager.isRegAllocated(.r2)); +// try expect(!function.register_manager.isRegAllocated(.r3)); - // Locked registers - function.register_manager.freeReg(.r0); - function.register_manager.freeReg(.r2); - function.register_manager.freeReg(.r3); - { - const lock = function.register_manager.lockReg(.r1); - defer if (lock) |reg| function.register_manager.unlockReg(reg); +// // Locked registers +// function.register_manager.freeReg(.r0); +// function.register_manager.freeReg(.r2); +// function.register_manager.freeReg(.r3); +// { +// const lock = function.register_manager.lockReg(.r1); +// defer if (lock) |reg| function.register_manager.unlockReg(reg); - try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?); - } - try expect(!function.register_manager.lockedRegsExist()); +// try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?); +// } +// try expect(!function.register_manager.lockedRegsExist()); - try expect(function.register_manager.isRegAllocated(.r0)); - try expect(function.register_manager.isRegAllocated(.r1)); - try expect(function.register_manager.isRegAllocated(.r2)); - try expect(function.register_manager.isRegAllocated(.r3)); -} +// try expect(function.register_manager.isRegAllocated(.r0)); +// try expect(function.register_manager.isRegAllocated(.r1)); +// try expect(function.register_manager.isRegAllocated(.r2)); +// try expect(function.register_manager.isRegAllocated(.r3)); +//} -test "allocRegs: normal usage" { - // TODO: convert this into a decltest once that is supported +//test "allocRegs: normal usage" { +// // TODO: convert this into a decltest once that is supported - const allocator = std.testing.allocator; +// const allocator = std.testing.allocator; - var function = MockFunction2{ - .allocator = allocator, - }; - defer function.deinit(); +// var function = MockFunction2{ +// .allocator = allocator, +// }; +// defer function.deinit(); - { - const result_reg: MockRegister2 = .r1; +// { +// const result_reg: MockRegister2 = .r1; - // The result register is known and fixed at this point, we - // don't want to accidentally allocate lhs or rhs to the - // result register, this is why we lock it. - // - // Using defer unlock right after lock is a good idea in - // most cases as you probably are using the locked registers - // in the remainder of this scope and don't need to use it - // after the end of this scope. However, in some situations, - // it may make sense to manually unlock registers before the - // end of the scope when you are certain that they don't - // contain any valuable data anymore and can be reused. For an - // example of that, see `selectively reducing register - // pressure`. - const lock = function.register_manager.lockReg(result_reg); - defer if (lock) |reg| function.register_manager.unlockReg(reg); +// // The result register is known and fixed at this point, we +// // don't want to accidentally allocate lhs or rhs to the +// // result register, this is why we lock it. +// // +// // Using defer unlock right after lock is a good idea in +// // most cases as you probably are using the locked registers +// // in the remainder of this scope and don't need to use it +// // after the end of this scope. However, in some situations, +// // it may make sense to manually unlock registers before the +// // end of the scope when you are certain that they don't +// // contain any valuable data anymore and can be reused. For an +// // example of that, see `selectively reducing register +// // pressure`. +// const lock = function.register_manager.lockReg(result_reg); +// defer if (lock) |reg| function.register_manager.unlockReg(reg); - const regs = try function.register_manager.allocRegs(2, .{ null, null }); - try function.genAdd(result_reg, regs[0], regs[1]); - } -} +// const regs = try function.register_manager.allocRegs(2, .{ null, null }); +// try function.genAdd(result_reg, regs[0], regs[1]); +// } +//} -test "allocRegs: selectively reducing register pressure" { - // TODO: convert this into a decltest once that is supported +//test "allocRegs: selectively reducing register pressure" { +// // TODO: convert this into a decltest once that is supported - const allocator = std.testing.allocator; +// const allocator = std.testing.allocator; - var function = MockFunction2{ - .allocator = allocator, - }; - defer function.deinit(); +// var function = MockFunction2{ +// .allocator = allocator, +// }; +// defer function.deinit(); - { - const result_reg: MockRegister2 = .r1; +// { +// const result_reg: MockRegister2 = .r1; - const lock = function.register_manager.lockReg(result_reg); +// const lock = function.register_manager.lockReg(result_reg); - // Here, we don't defer unlock because we manually unlock - // after genAdd - const regs = try function.register_manager.allocRegs(2, .{ null, null }); +// // Here, we don't defer unlock because we manually unlock +// // after genAdd +// const regs = try function.register_manager.allocRegs(2, .{ null, null }); - try function.genAdd(result_reg, regs[0], regs[1]); - function.register_manager.unlockReg(lock.?); +// try function.genAdd(result_reg, regs[0], regs[1]); +// function.register_manager.unlockReg(lock.?); - const extra_summand_reg = try function.register_manager.allocReg(null); - try function.genAdd(result_reg, result_reg, extra_summand_reg); - } -} +// const extra_summand_reg = try function.register_manager.allocReg(null); +// try function.genAdd(result_reg, result_reg, extra_summand_reg); +// } +//} -test "getReg" { - const allocator = std.testing.allocator; +//test "getReg" { +// const allocator = std.testing.allocator; - var function = MockFunction1{ - .allocator = allocator, - }; - defer function.deinit(); +// var function = MockFunction1{ +// .allocator = allocator, +// }; +// defer function.deinit(); - const mock_instruction: Air.Inst.Index = 1; +// const mock_instruction: Air.Inst.Index = 1; - try function.register_manager.getReg(.r3, mock_instruction); +// try function.register_manager.getReg(.r3, mock_instruction); - try expect(!function.register_manager.isRegAllocated(.r2)); - try expect(function.register_manager.isRegAllocated(.r3)); - try expect(function.register_manager.isRegFree(.r2)); - try expect(!function.register_manager.isRegFree(.r3)); +// try expect(!function.register_manager.isRegAllocated(.r2)); +// try expect(function.register_manager.isRegAllocated(.r3)); +// try expect(function.register_manager.isRegFree(.r2)); +// try expect(!function.register_manager.isRegFree(.r3)); - // Spill r3 - try function.register_manager.getReg(.r3, mock_instruction); +// // Spill r3 +// try function.register_manager.getReg(.r3, mock_instruction); - try expect(!function.register_manager.isRegAllocated(.r2)); - try expect(function.register_manager.isRegAllocated(.r3)); - try expect(function.register_manager.isRegFree(.r2)); - try expect(!function.register_manager.isRegFree(.r3)); - try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r3}, function.spilled.items); -} +// try expect(!function.register_manager.isRegAllocated(.r2)); +// try expect(function.register_manager.isRegAllocated(.r3)); +// try expect(function.register_manager.isRegFree(.r2)); +// try expect(!function.register_manager.isRegFree(.r3)); +// try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r3}, function.spilled.items); +//} From 9e5c8cb008f75c2e570a0e48d5d014e936c103ca Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 16 May 2022 19:45:30 +0200 Subject: [PATCH 1578/2031] x64: merge general purpose with simd register into one bitset This way, we do not have to tweak the `RegisterManager` to handle multiple register types - we have one linear space instead. Additionally we can use the bitset itself to separate the registers into overlapping (the ones that are aliases of differing bitwidths) and nonoverlapping classes (for example, AVX registers do not overlap general purpose registers, thus they can be allocated simultaneously). Another huge benefit of this simple approach is the fact that we can still refer to *all* registers regardless of their class via enum literals which makes the code so much more readable. Finally, `RegisterLock` is universal across different register classes. --- src/arch/x86_64/CodeGen.zig | 1060 ++++++++++++++--------------------- src/arch/x86_64/Emit.zig | 772 +++++++++++++------------ src/arch/x86_64/Mir.zig | 66 ++- src/arch/x86_64/abi.zig | 17 +- src/arch/x86_64/bits.zig | 100 ++-- src/codegen.zig | 24 +- src/register_manager.zig | 404 +++++++------ 7 files changed, 1102 insertions(+), 1341 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index e81f2d5435..aa6d04d5d9 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -35,14 +35,10 @@ const caller_preserved_regs = abi.caller_preserved_regs; const allocatable_registers = abi.allocatable_registers; const c_abi_int_param_regs = abi.c_abi_int_param_regs; const c_abi_int_return_regs = abi.c_abi_int_return_regs; -const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers, spillInstruction); +const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers); const RegisterLock = RegisterManager.RegisterLock; const Register = bits.Register; -const AvxRegisterManager = RegisterManagerFn(Self, AvxRegister, &abi.avx_regs, spillInstructionAvx); -const AvxRegisterLock = AvxRegisterManager.RegisterLock; -const AvxRegister = bits.AvxRegister; - const InnerError = error{ OutOfMemory, CodegenFail, @@ -92,8 +88,7 @@ branch_stack: *std.ArrayList(Branch), // Key is the block instruction blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, -register_manager: RegisterManager, -avx_register_manager: AvxRegisterManager, +register_manager: RegisterManager = .{}, /// Maps offset to what is stored there. stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, @@ -133,8 +128,6 @@ pub const MCValue = union(enum) { /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register, /// and the operation is a signed operation. register_overflow_signed: Register, - /// The value is in an AVX register. - avx_register: AvxRegister, /// The value is in memory at a hard-coded address. /// If the type is a pointer, it means the pointer address is at this memory location. memory: u64, @@ -202,7 +195,6 @@ pub const MCValue = union(enum) { fn isRegister(mcv: MCValue) bool { return switch (mcv) { .register => true, - .avx_register => true, else => false, }; } @@ -304,11 +296,7 @@ pub fn generate( .mir_to_air_map = if (builtin.mode == .Debug) std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index).init(bin_file.allocator) else {}, - .register_manager = undefined, - .avx_register_manager = undefined, }; - function.register_manager = .{ .function = &function }; - function.avx_register_manager = .{ .function = &function }; defer function.stack.deinit(bin_file.allocator); defer function.blocks.deinit(bin_file.allocator); defer function.exitlude_jump_relocs.deinit(bin_file.allocator); @@ -400,17 +388,15 @@ fn gen(self: *Self) InnerError!void { if (cc != .Naked) { _ = try self.addInst(.{ .tag = .push, - .ops = (Mir.Ops(Register, Register){ - .reg1 = .rbp, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), .data = undefined, // unused for push reg, }); _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp, .reg2 = .rsp, - }).encode(), + }), .data = undefined, }); // We want to subtract the aligned stack frame size from rsp here, but we don't @@ -447,9 +433,7 @@ fn gen(self: *Self) InnerError!void { // push the callee_preserved_regs that were used const backpatch_push_callee_preserved_regs_i = try self.addInst(.{ .tag = .push_regs_from_callee_preserved_regs, - .ops = (Mir.Ops(Register, Register){ - .reg1 = .rbp, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), .data = .{ .payload = undefined }, // to be backpatched }); @@ -489,9 +473,7 @@ fn gen(self: *Self) InnerError!void { // pop the callee_preserved_regs _ = try self.addInst(.{ .tag = .pop_regs_from_callee_preserved_regs, - .ops = (Mir.Ops(Register, Register){ - .reg1 = .rbp, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), .data = .{ .payload = callee_preserved_regs_payload }, }); @@ -510,17 +492,13 @@ fn gen(self: *Self) InnerError!void { _ = try self.addInst(.{ .tag = .pop, - .ops = (Mir.Ops(Register, Register){ - .reg1 = .rbp, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), .data = undefined, }); _ = try self.addInst(.{ .tag = .ret, - .ops = (Mir.Ops(Register, Register){ - .flags = 0b11, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .flags = 0b11 }), .data = undefined, }); @@ -534,16 +512,12 @@ fn gen(self: *Self) InnerError!void { if (aligned_stack_end > 0) { self.mir_instructions.set(backpatch_stack_sub, .{ .tag = .sub, - .ops = (Mir.Ops(Register, Register){ - .reg1 = .rsp, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), .data = .{ .imm = aligned_stack_end }, }); self.mir_instructions.set(backpatch_stack_add, .{ .tag = .add, - .ops = (Mir.Ops(Register, Register){ - .reg1 = .rsp, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), .data = .{ .imm = aligned_stack_end }, }); } @@ -908,8 +882,8 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { // TODO check if AVX available const ptr_bytes: u64 = 32; if (abi_size <= ptr_bytes) { - if (self.avx_register_manager.tryAllocReg(inst)) |reg| { - return MCValue{ .avx_register = avxRegisterAlias(reg, abi_size) }; + if (self.register_manager.tryAllocReg(inst)) |reg| { + return MCValue{ .register = registerAlias(reg, abi_size) }; } } }, @@ -947,21 +921,6 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{}); } -pub fn spillInstructionAvx(self: *Self, reg: AvxRegister, inst: Air.Inst.Index) !void { - const stack_mcv = try self.allocRegOrMem(inst, false); - log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); - const reg_mcv = self.getResolvedInstValue(inst); - switch (reg_mcv) { - .avx_register => |other| { - assert(reg.to256() == other.to256()); - }, - else => {}, - } - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; - try branch.inst_table.put(self.gpa, inst, stack_mcv); - try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{}); -} - pub fn spillCompareFlagsIfOccupied(self: *Self) !void { if (self.compare_flags_inst) |inst_to_save| { const mcv = self.getResolvedInstValue(inst_to_save); @@ -972,7 +931,6 @@ pub fn spillCompareFlagsIfOccupied(self: *Self) !void { .compare_flags_signed, .compare_flags_unsigned, => try self.allocRegOrMem(inst_to_save, true), - .avx_register => try self.allocRegOrMem(inst_to_save, false), else => unreachable, }; @@ -990,7 +948,6 @@ pub fn spillCompareFlagsIfOccupied(self: *Self) !void { .register_overflow_signed, .register_overflow_unsigned, => |reg| self.register_manager.freeReg(reg), - .avx_register => |reg| self.avx_register_manager.freeReg(reg), else => {}, } } @@ -1236,10 +1193,10 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void { const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, rhs_mcv); _ = try self.addInst(.{ .tag = if (signedness == .signed) .cond_mov_lt else .cond_mov_below, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = dst_mcv.register, .reg2 = lhs_reg, - }).encode(), + }), .data = undefined, }); @@ -1440,10 +1397,10 @@ fn genSetStackTruncatedOverflowCompare( }; _ = try self.addInst(.{ .tag = .cond_set_byte_overflow, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = overflow_reg.to8(), .flags = flags, - }).encode(), + }), .data = undefined, }); @@ -1460,10 +1417,7 @@ fn genSetStackTruncatedOverflowCompare( const eq_reg = temp_regs[2]; _ = try self.addInst(.{ .tag = .cond_set_byte_eq_ne, - .ops = (Mir.Ops(Register, Register){ - .reg1 = eq_reg.to8(), - .flags = 0b00, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = eq_reg.to8() }), .data = undefined, }); @@ -1609,19 +1563,17 @@ fn genIntMulDivOpMir( .signed => { _ = try self.addInst(.{ .tag = .cwd, - .ops = (Mir.Ops(Register, Register){ - .flags = 0b11, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .flags = 0b11 }), .data = undefined, }); }, .unsigned => { _ = try self.addInst(.{ .tag = .xor, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rdx, .reg2 = .rdx, - }).encode(), + }), .data = undefined, }); }, @@ -1640,16 +1592,14 @@ fn genIntMulDivOpMir( .register => |reg| { _ = try self.addInst(.{ .tag = tag, - .ops = (Mir.Ops(Register, Register){ - .reg1 = reg, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), .data = undefined, }); }, .stack_offset => |off| { _ = try self.addInst(.{ .tag = tag, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg2 = .rbp, .flags = switch (abi_size) { 1 => 0b00, @@ -1658,7 +1608,7 @@ fn genIntMulDivOpMir( 8 => 0b11, else => unreachable, }, - }).encode(), + }), .data = .{ .imm = @bitCast(u32, -off) }, }); }, @@ -1691,34 +1641,34 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa _ = try self.addInst(.{ .tag = .xor, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = divisor.to64(), .reg2 = dividend.to64(), - }).encode(), + }), .data = undefined, }); _ = try self.addInst(.{ .tag = .sar, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = divisor.to64(), .flags = 0b10, - }).encode(), + }), .data = .{ .imm = 63 }, }); _ = try self.addInst(.{ .tag = .@"test", - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rdx, .reg2 = .rdx, - }).encode(), + }), .data = undefined, }); _ = try self.addInst(.{ .tag = .cond_mov_eq, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = divisor.to64(), .reg2 = .rdx, - }).encode(), + }), .data = undefined, }); try self.genBinOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax }); @@ -2102,11 +2052,11 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { // mov reg, [rbp - 8] _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg.to64(), .reg2 = .rbp, .flags = 0b01, - }).encode(), + }), .data = .{ .imm = @bitCast(u32, -@intCast(i32, off)) }, }); }, @@ -2187,10 +2137,10 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { // lea reg, [rbp] _ = try self.addInst(.{ .tag = .lea, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg.to64(), .reg2 = .rbp, - }).encode(), + }), .data = .{ .imm = @bitCast(u32, -off) }, }); }, @@ -2198,10 +2148,10 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { // lea reg, [rbp] _ = try self.addInst(.{ .tag = .lea, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg.to64(), .reg2 = .rbp, - }).encode(), + }), .data = .{ .imm = @bitCast(u32, -off) }, }); }, @@ -2266,11 +2216,11 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { // mov dst_mcv, [dst_mcv] _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ - .flags = 0b01, + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)), .reg2 = dst_mcv.register, - }).encode(), + .flags = 0b01, + }), .data = .{ .imm = 0 }, }); break :result .{ .register = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)) }; @@ -2532,11 +2482,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo // mov dst_reg, [reg] _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)), .reg2 = reg, .flags = 0b01, - }).encode(), + }), .data = .{ .imm = 0 }, }); }, @@ -2552,9 +2502,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo else => return self.fail("TODO implement loading from register into {}", .{dst_mcv}), } }, - .avx_register => { - return self.fail("TODO load for AVX register", .{}); - }, .memory, .got_load, .direct_load, @@ -2606,10 +2553,10 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl); _ = try self.addInst(.{ .tag = .lea_pie, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size), .flags = flags, - }).encode(), + }), .data = .{ .load_reloc = .{ .atom_index = fn_owner_decl.link.macho.local_sym_index, @@ -2670,7 +2617,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type }); _ = try self.addInst(.{ .tag = .mov_mem_imm, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg.to64(), .flags = switch (abi_size) { 1 => 0b00, @@ -2678,7 +2625,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type 4 => 0b10, else => unreachable, }, - }).encode(), + }), .data = .{ .payload = payload }, }); }, @@ -2692,11 +2639,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type const tmp_reg = try self.copyToTmpRegister(value_ty, value); _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg.to64(), .reg2 = tmp_reg.to64(), .flags = 0b10, - }).encode(), + }), .data = .{ .imm = 0 }, }); }, @@ -2708,11 +2655,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .register => |src_reg| { _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg.to64(), .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)), .flags = 0b10, - }).encode(), + }), .data = .{ .imm = 0 }, }); }, @@ -2736,9 +2683,6 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type }, } }, - .avx_register => { - return self.fail("TODO store for AVX register", .{}); - }, .got_load, .direct_load, .memory, @@ -2759,11 +2703,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type // mov reg, [reg] _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg.to64(), .reg2 = addr_reg.to64(), .flags = 0b01, - }).encode(), + }), .data = .{ .imm = 0 }, }); @@ -2798,21 +2742,21 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type } _ = try self.addInst(.{ .tag = .mov_mem_imm, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg.to64(), .flags = flags, - }).encode(), + }), .data = .{ .payload = payload }, }); }, .register => |reg| { _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg.to64(), .reg2 = reg, .flags = 0b10, - }).encode(), + }), .data = .{ .imm = 0 }, }); }, @@ -2829,20 +2773,20 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = tmp_reg, .reg2 = tmp_reg, .flags = 0b01, - }).encode(), + }), .data = .{ .imm = 0 }, }); _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg.to64(), .reg2 = tmp_reg, .flags = 0b10, - }).encode(), + }), .data = .{ .imm = 0 }, }); return; @@ -2856,11 +2800,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type const tmp_reg = try self.copyToTmpRegister(value_ty, value); _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg.to64(), .reg2 = tmp_reg, .flags = 0b10, - }).encode(), + }), .data = .{ .imm = 0 }, }); return; @@ -3017,10 +2961,10 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { if (signedness == .signed and field_size < 8) { _ = try self.addInst(.{ .tag = .mov_sign_extend, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = dst_mcv.register, .reg2 = registerAlias(dst_mcv.register, field_size), - }).encode(), + }), .data = undefined, }); } @@ -3048,10 +2992,10 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { }; _ = try self.addInst(.{ .tag = .cond_set_byte_overflow, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = dst_reg.to8(), .flags = flags, - }).encode(), + }), .data = undefined, }); break :result MCValue{ .register = dst_reg.to8() }; @@ -3092,10 +3036,7 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi 1 => { _ = try self.addInst(.{ .tag = tag, - .ops = (Mir.Ops(Register, Register){ - .reg1 = registerAlias(reg, abi_size), - .flags = 0b00, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size) }), .data = undefined, }); return; @@ -3103,10 +3044,10 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi else => { _ = try self.addInst(.{ .tag = tag, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size), .flags = 0b10, - }).encode(), + }), .data = .{ .imm = @intCast(u8, imm) }, }); return; @@ -3124,10 +3065,10 @@ fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shi _ = try self.addInst(.{ .tag = tag, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size), .flags = 0b01, - }).encode(), + }), .data = undefined, }); } @@ -3383,32 +3324,6 @@ fn genBinOp( return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()}); } - if (lhs_ty.zigTypeTag() == .Float) { - switch (tag) { - .add => { - const dst_reg: AvxRegister = blk: { - const reg = try self.avx_register_manager.allocReg(null); - try self.genSetAvxReg(lhs_ty, reg, lhs); - break :blk reg.to128(); - }; - const dst_lock = self.avx_register_manager.lockRegAssumeUnused(dst_reg); - defer self.avx_register_manager.unlockReg(dst_lock); - - const src_reg: AvxRegister = blk: { - const reg = try self.avx_register_manager.allocReg(null); - try self.genSetAvxReg(lhs_ty, reg, rhs); - break :blk reg.to128(); - }; - const src_lock = self.avx_register_manager.lockRegAssumeUnused(src_reg); - defer self.avx_register_manager.unlockReg(src_lock); - - try self.genBinOpMir(.add_f64, lhs_ty, .{ .avx_register = dst_reg }, .{ .avx_register = src_reg }); - return MCValue{ .avx_register = dst_reg }; - }, - else => unreachable, - } - } - const is_commutative: bool = switch (tag) { .add, .addwrap, @@ -3526,25 +3441,39 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu const reg = try self.copyToTmpRegister(dst_ty, src_mcv); return self.genBinOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); }, - .register => |src_reg| { - _ = try self.addInst(.{ - .tag = mir_tag, - .ops = (Mir.Ops(Register, Register){ - .reg1 = registerAlias(dst_reg, abi_size), - .reg2 = registerAlias(src_reg, abi_size), - }).encode(), - .data = undefined, - }); - }, - .avx_register => { - return self.fail("TODO genBinOp for AVX register", .{}); + .register => |src_reg| switch (dst_ty.zigTypeTag()) { + .Float => switch (dst_ty.tag()) { + .f64 => { + _ = try self.addInst(.{ + .tag = switch (mir_tag) { + .add => .add_f64, + .cmp => .cmp_f64, + else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}), + }, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = dst_reg.to128(), + .reg2 = src_reg.to128(), + }), + .data = undefined, + }); + }, + else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}), + }, + else => { + _ = try self.addInst(.{ + .tag = mir_tag, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = registerAlias(dst_reg, abi_size), + .reg2 = registerAlias(src_reg, abi_size), + }), + .data = undefined, + }); + }, }, .immediate => |imm| { _ = try self.addInst(.{ .tag = mir_tag, - .ops = (Mir.Ops(Register, Register){ - .reg1 = registerAlias(dst_reg, abi_size), - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(dst_reg, abi_size) }), .data = .{ .imm = @truncate(u32, imm) }, }); }, @@ -3567,39 +3496,16 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu } _ = try self.addInst(.{ .tag = mir_tag, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(dst_reg, abi_size), .reg2 = .rbp, .flags = 0b01, - }).encode(), + }), .data = .{ .imm = @bitCast(u32, -off) }, }); }, } }, - .avx_register => |dst_reg| { - switch (src_mcv) { - .avx_register => |src_reg| { - switch (dst_ty.zigTypeTag()) { - .Float => switch (dst_ty.tag()) { - .f64 => { - _ = try self.addInst(.{ - .tag = mir_tag, - .ops = (Mir.Ops(AvxRegister, AvxRegister){ - .reg1 = dst_reg.to128(), - .reg2 = src_reg.to128(), - }).encode(), - .data = undefined, - }); - }, - else => return self.fail("TODO genBinOp for AVX register and type {}", .{dst_ty.fmtDebug()}), - }, - else => return self.fail("TODO genBinOp for AVX register and type {}", .{dst_ty.fmtDebug()}), - } - }, - else => return self.fail("TODO genBinOp for AVX register", .{}), - } - }, .ptr_stack_offset, .stack_offset => |off| { if (off > math.maxInt(i32)) { return self.fail("stack offset too large", .{}); @@ -3617,17 +3523,14 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .register => |src_reg| { _ = try self.addInst(.{ .tag = mir_tag, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp, .reg2 = registerAlias(src_reg, abi_size), .flags = 0b10, - }).encode(), + }), .data = .{ .imm = @bitCast(u32, -off) }, }); }, - .avx_register => { - return self.fail("TODO genBinOp for AVX register", .{}); - }, .immediate => |imm| { const tag: Mir.Inst.Tag = switch (mir_tag) { .add => .add_mem_imm, @@ -3651,10 +3554,10 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu }); _ = try self.addInst(.{ .tag = tag, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp, .flags = flags, - }).encode(), + }), .data = .{ .payload = payload }, }); }, @@ -3697,7 +3600,6 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .ptr_stack_offset => unreachable, .register_overflow_unsigned => unreachable, .register_overflow_signed => unreachable, - .avx_register => unreachable, .register => |dst_reg| { switch (src_mcv) { .none => unreachable, @@ -3706,15 +3608,14 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .ptr_stack_offset => unreachable, .register_overflow_unsigned => unreachable, .register_overflow_signed => unreachable, - .avx_register => unreachable, .register => |src_reg| { // register, register _ = try self.addInst(.{ .tag = .imul_complex, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(dst_reg, abi_size), .reg2 = registerAlias(src_reg, abi_size), - }).encode(), + }), .data = undefined, }); }, @@ -3724,11 +3625,11 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M if (math.minInt(i32) <= imm and imm <= math.maxInt(i32)) { _ = try self.addInst(.{ .tag = .imul_complex, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = dst_reg.to32(), .reg2 = dst_reg.to32(), .flags = 0b10, - }).encode(), + }), .data = .{ .imm = @truncate(u32, imm) }, }); } else { @@ -3740,11 +3641,11 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .stack_offset => |off| { _ = try self.addInst(.{ .tag = .imul_complex, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(dst_reg, abi_size), .reg2 = .rbp, .flags = 0b01, - }).encode(), + }), .data = .{ .imm = @bitCast(u32, -off) }, }); }, @@ -3770,7 +3671,6 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .ptr_stack_offset => unreachable, .register_overflow_unsigned => unreachable, .register_overflow_signed => unreachable, - .avx_register => unreachable, .register => |src_reg| { // copy dst to a register const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); @@ -3778,10 +3678,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M // register, register _ = try self.addInst(.{ .tag = .imul_complex, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(dst_reg, abi_size), .reg2 = registerAlias(src_reg, abi_size), - }).encode(), + }), .data = undefined, }); // copy dst_reg back out @@ -3888,9 +3788,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { fn airBreakpoint(self: *Self) !void { _ = try self.addInst(.{ .tag = .interrupt, - .ops = (Mir.Ops{ - .flags = 0b00, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{}), .data = undefined, }); return self.finishAirBookkeeping(); @@ -3973,9 +3871,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .ptr_stack_offset => { return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); }, - .avx_register => { - return self.fail("TODO implement calling with MCValue.avx_register arg", .{}); - }, .undef => unreachable, .immediate => unreachable, .unreach => unreachable, @@ -3994,9 +3889,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. // Adjust the stack _ = try self.addInst(.{ .tag = .sub, - .ops = (Mir.Ops(Register, Register){ - .reg1 = .rsp, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), .data = .{ .imm = info.stack_byte_count }, }); } @@ -4020,9 +3913,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. unreachable; _ = try self.addInst(.{ .tag = .call, - .ops = (Mir.Ops(Register, Register){ - .flags = 0b01, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), .data = .{ .imm = @truncate(u32, got_addr) }, }); } else if (func_value.castTag(.extern_fn)) |_| { @@ -4036,10 +3927,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. try self.genSetReg(Type.initTag(.usize), .rax, mcv); _ = try self.addInst(.{ .tag = .call, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax, .flags = 0b01, - }).encode(), + }), .data = undefined, }); } @@ -4054,10 +3945,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. // callq *%rax _ = try self.addInst(.{ .tag = .call, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax, .flags = 0b01, - }).encode(), + }), .data = undefined, }); } else if (func_value.castTag(.extern_fn)) |func_payload| { @@ -4089,10 +3980,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. try self.genSetReg(Type.initTag(.usize), .rax, mcv); _ = try self.addInst(.{ .tag = .call, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax, .flags = 0b01, - }).encode(), + }), .data = undefined, }); } @@ -4107,9 +3998,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const fn_got_addr = got_addr + got_index * ptr_bytes; _ = try self.addInst(.{ .tag = .call, - .ops = (Mir.Ops(Register, Register){ - .flags = 0b01, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), .data = .{ .imm = @intCast(u32, fn_got_addr) }, }); } else return self.fail("TODO implement calling extern fn on plan9", .{}); @@ -4119,10 +4008,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. try self.genSetReg(Type.initTag(.usize), .rax, mcv); _ = try self.addInst(.{ .tag = .call, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax, .flags = 0b01, - }).encode(), + }), .data = undefined, }); } @@ -4132,9 +4021,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. // Readjust the stack _ = try self.addInst(.{ .tag = .add, - .ops = (Mir.Ops(Register, Register){ - .reg1 = .rsp, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), .data = .{ .imm = info.stack_byte_count }, }); } @@ -4192,9 +4079,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { // which is available if the jump is 127 bytes or less forward. const jmp_reloc = try self.addInst(.{ .tag = .jmp, - .ops = (Mir.Ops(Register, Register){ - .flags = 0b00, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{}), .data = .{ .inst = undefined }, }); try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc); @@ -4227,9 +4112,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { // which is available if the jump is 127 bytes or less forward. const jmp_reloc = try self.addInst(.{ .tag = .jmp, - .ops = (Mir.Ops(Register, Register){ - .flags = 0b00, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{}), .data = .{ .inst = undefined }, }); try self.exitlude_jump_relocs.append(self.gpa, jmp_reloc); @@ -4256,37 +4139,6 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { self.compare_flags_inst = inst; const result: MCValue = result: { - if (ty.zigTypeTag() == .Float) { - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - - const dst_reg: AvxRegister = blk: { - const reg = try self.avx_register_manager.allocReg(null); - try self.genSetAvxReg(ty, reg, lhs); - break :blk reg.to128(); - }; - const dst_lock = self.avx_register_manager.lockRegAssumeUnused(dst_reg); - defer self.avx_register_manager.unlockReg(dst_lock); - - const src_reg: AvxRegister = blk: { - const reg = try self.avx_register_manager.allocReg(null); - try self.genSetAvxReg(ty, reg, rhs); - break :blk reg.to128(); - }; - const src_lock = self.avx_register_manager.lockRegAssumeUnused(src_reg); - defer self.avx_register_manager.unlockReg(src_lock); - - _ = try self.addInst(.{ - .tag = .cmp_f64, - .ops = (Mir.Ops(AvxRegister, AvxRegister){ - .reg1 = dst_reg, - .reg2 = src_reg, - }).encode(), - .data = undefined, - }); - - break :result MCValue{ .compare_flags_unsigned = op }; - } // There are 2 operands, destination and source. // Either one, but not both, can be a memory operand. // Source operand can be an immediate, 8 bits or 32 bits. @@ -4304,8 +4156,28 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { const dst_mcv = MCValue{ .register = dst_reg }; + const rhs_ty = self.air.typeOf(bin_op.rhs); // This instruction supports only signed 32-bit immediates at most. - const src_mcv = try self.limitImmediateType(bin_op.rhs, i32); + const src_mcv: MCValue = blk: { + switch (rhs_ty.zigTypeTag()) { + .Float => { + const rhs = try self.resolveInst(bin_op.rhs); + const rhs_lock: ?RegisterLock = switch (rhs) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); + const src_reg = try self.copyToTmpRegister(rhs_ty, rhs); + break :blk MCValue{ .register = src_reg }; + }, + else => break :blk try self.limitImmediateType(bin_op.rhs, i32), + } + }; + const src_lock: ?RegisterLock = switch (src_mcv) { + .register => |reg| self.register_manager.lockReg(reg), + else => null, + }; + defer if (src_lock) |lock| self.register_manager.unlockReg(lock); try self.genBinOpMir(.cmp, ty, dst_mcv, src_mcv); break :result switch (signedness) { @@ -4504,9 +4376,7 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { Mir.Inst.Tag.cond_jmp_greater_less; return self.addInst(.{ .tag = tag, - .ops = (Mir.Ops(Register, Register){ - .flags = flags, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .flags = flags }), .data = .{ .inst = undefined }, }); }, @@ -4514,17 +4384,12 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { try self.spillCompareFlagsIfOccupied(); _ = try self.addInst(.{ .tag = .@"test", - .ops = (Mir.Ops(Register, Register){ - .reg1 = reg, - .flags = 0b00, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), .data = .{ .imm = 1 }, }); return self.addInst(.{ .tag = .cond_jmp_eq_ne, - .ops = (Mir.Ops(Register, Register){ - .flags = 0b01, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), .data = .{ .inst = undefined }, }); }, @@ -4918,9 +4783,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { try self.genBody(body); _ = try self.addInst(.{ .tag = .jmp, - .ops = (Mir.Ops(Register, Register){ - .flags = 0b00, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{}), .data = .{ .inst = jmp_target }, }); return self.finishAirBookkeeping(); @@ -4971,19 +4834,17 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u .immediate => |imm| { _ = try self.addInst(.{ .tag = .xor, - .ops = (Mir.Ops(Register, Register){ - .reg1 = registerAlias(cond_reg, abi_size), - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(cond_reg, abi_size) }), .data = .{ .imm = @intCast(u32, imm) }, }); }, .register => |reg| { _ = try self.addInst(.{ .tag = .xor, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(cond_reg, abi_size), .reg2 = registerAlias(reg, abi_size), - }).encode(), + }), .data = undefined, }); }, @@ -5002,17 +4863,15 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u _ = try self.addInst(.{ .tag = .@"test", - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(cond_reg, abi_size), .reg2 = registerAlias(cond_reg, abi_size), - }).encode(), + }), .data = undefined, }); return self.addInst(.{ .tag = .cond_jmp_eq_ne, - .ops = (Mir.Ops(Register, Register){ - .flags = 0b00, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{}), .data = .{ .inst = undefined }, }); }, @@ -5156,17 +5015,20 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { if (block_mcv == .none) { block_data.mcv = switch (operand_mcv) { .none, .dead, .unreach => unreachable, - .register, .stack_offset, .memory => operand_mcv, + .stack_offset, .memory => operand_mcv, .compare_flags_signed, .compare_flags_unsigned, .immediate => blk: { const new_mcv = try self.allocRegOrMem(block, true); try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); break :blk new_mcv; }, - .avx_register => blk: { - // TODO not needed; return operand_mcv ones we can transfer between XMM registers - const new_mcv = try self.allocRegOrMem(block, false); - try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); - break :blk new_mcv; + .register => blk: { + if (self.air.typeOfIndex(block).zigTypeTag() == .Float) { + // TODO not needed; return operand_mcv ones we can transfer between XMM registers + const new_mcv = try self.allocRegOrMem(block, false); + try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); + break :blk new_mcv; + } + break :blk operand_mcv; }, else => return self.fail("TODO implement block_data.mcv = operand_mcv for {}", .{operand_mcv}), }; @@ -5184,9 +5046,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { // Leave the jump offset undefined const jmp_reloc = try self.addInst(.{ .tag = .jmp, - .ops = (Mir.Ops(Register, Register){ - .flags = 0b00, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{}), .data = .{ .inst = undefined }, }); block_data.relocs.appendAssumeCapacity(jmp_reloc); @@ -5274,9 +5134,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { }; _ = try self.addInst(.{ .tag = .push, - .ops = (Mir.Ops(Register, Register){ - .flags = 0b10, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .flags = 0b10 }), .data = .{ .imm = n }, }); } else if (mem.indexOf(u8, arg, "%%")) |l| { @@ -5285,9 +5143,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { return self.fail("unrecognized register: '{s}'", .{reg_name}); _ = try self.addInst(.{ .tag = .push, - .ops = (Mir.Ops(Register, Register){ - .reg1 = reg, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), .data = undefined, }); } else return self.fail("TODO more push operands", .{}); @@ -5299,9 +5155,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { return self.fail("unrecognized register: '{s}'", .{reg_name}); _ = try self.addInst(.{ .tag = .pop, - .ops = (Mir.Ops(Register, Register){ - .reg1 = reg, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), .data = undefined, }); } else return self.fail("TODO more pop operands", .{}); @@ -5365,7 +5219,6 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { .none => return, .immediate => unreachable, .register => |reg| return self.genSetReg(ty, reg, val), - .avx_register => |reg| return self.genSetAvxReg(ty, reg, val), .stack_offset => |off| return self.genSetStack(ty, off, val, .{}), .memory => { return self.fail("TODO implement setRegOrMem for memory", .{}); @@ -5396,9 +5249,6 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE .register_overflow_unsigned, .register_overflow_signed, => return self.fail("TODO genSetStackArg for register with overflow bit", .{}), - .avx_register => { - return self.fail("TODO genSetStackArg for AVX register", .{}); - }, .compare_flags_unsigned, .compare_flags_signed, => { @@ -5417,7 +5267,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE }); _ = try self.addInst(.{ .tag = .mov_mem_imm, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp, .flags = switch (abi_size) { 1 => 0b00, @@ -5425,7 +5275,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE 4 => 0b10, else => unreachable, }, - }).encode(), + }), .data = .{ .payload = payload }, }); }, @@ -5453,11 +5303,11 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE .register => |reg| { _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp, .reg2 = registerAlias(reg, @intCast(u32, abi_size)), .flags = 0b10, - }).encode(), + }), .data = .{ .imm = @bitCast(u32, -stack_offset) }, }); }, @@ -5520,10 +5370,10 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl }; _ = try self.addInst(.{ .tag = .cond_set_byte_overflow, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = tmp_reg.to8(), .flags = flags, - }).encode(), + }), .data = undefined, }); @@ -5550,7 +5400,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl }); _ = try self.addInst(.{ .tag = .mov_mem_imm, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = base_reg, .flags = switch (abi_size) { 1 => 0b00, @@ -5558,7 +5408,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl 4 => 0b10, else => unreachable, }, - }).encode(), + }), .data = .{ .payload = payload }, }); }, @@ -5572,10 +5422,10 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl }); _ = try self.addInst(.{ .tag = .mov_mem_imm, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = base_reg, .flags = 0b10, - }).encode(), + }), .data = .{ .payload = payload }, }); } @@ -5586,10 +5436,10 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl }); _ = try self.addInst(.{ .tag = .mov_mem_imm, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = base_reg, .flags = 0b10, - }).encode(), + }), .data = .{ .payload = payload }, }); } @@ -5605,65 +5455,64 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl } const base_reg = opts.dest_stack_base orelse .rbp; - if (!math.isPowerOfTwo(abi_size)) { - const reg_lock = self.register_manager.lockReg(reg); - defer if (reg_lock) |lock| self.register_manager.unlockReg(lock); - const tmp_reg = try self.copyToTmpRegister(ty, mcv); - - var next_offset = stack_offset; - var remainder = abi_size; - while (remainder > 0) { - const nearest_power_of_two = @as(u6, 1) << math.log2_int(u3, @intCast(u3, remainder)); - - _ = try self.addInst(.{ - .tag = .mov, - .ops = (Mir.Ops(Register, Register){ - .reg1 = base_reg, - .reg2 = registerAlias(tmp_reg, nearest_power_of_two), - .flags = 0b10, - }).encode(), - .data = .{ .imm = @bitCast(u32, -next_offset) }, - }); - - if (nearest_power_of_two > 1) { - try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{ .immediate = nearest_power_of_two * 8 }); - } - - remainder -= nearest_power_of_two; - next_offset -= nearest_power_of_two; - } - } else { - _ = try self.addInst(.{ - .tag = .mov, - .ops = (Mir.Ops(Register, Register){ - .reg1 = base_reg, - .reg2 = registerAlias(reg, @intCast(u32, abi_size)), - .flags = 0b10, - }).encode(), - .data = .{ .imm = @bitCast(u32, -stack_offset) }, - }); - } - }, - .avx_register => |reg| { - const base_reg = opts.dest_stack_base orelse .rbp; switch (ty.zigTypeTag()) { .Float => switch (ty.tag()) { - .f32 => return self.fail("TODO genSetStack for AVX register for f32", .{}), + .f32 => return self.fail("TODO genSetStack for register for f32", .{}), .f64 => { _ = try self.addInst(.{ .tag = .mov_f64, - .ops = (Mir.Ops(Register, AvxRegister){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = base_reg, .reg2 = reg.to128(), .flags = 0b01, - }).encode(), + }), .data = .{ .imm = @bitCast(u32, -stack_offset) }, }); }, - else => return self.fail("TODO genSetStack for AVX register for type {}", .{ty.fmtDebug()}), + else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}), + }, + else => { + if (!math.isPowerOfTwo(abi_size)) { + const reg_lock = self.register_manager.lockReg(reg); + defer if (reg_lock) |lock| self.register_manager.unlockReg(lock); + + const tmp_reg = try self.copyToTmpRegister(ty, mcv); + + var next_offset = stack_offset; + var remainder = abi_size; + while (remainder > 0) { + const nearest_power_of_two = @as(u6, 1) << math.log2_int(u3, @intCast(u3, remainder)); + + _ = try self.addInst(.{ + .tag = .mov, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = base_reg, + .reg2 = registerAlias(tmp_reg, nearest_power_of_two), + .flags = 0b10, + }), + .data = .{ .imm = @bitCast(u32, -next_offset) }, + }); + + if (nearest_power_of_two > 1) { + try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{ .immediate = nearest_power_of_two * 8 }); + } + + remainder -= nearest_power_of_two; + next_offset -= nearest_power_of_two; + } + } else { + _ = try self.addInst(.{ + .tag = .mov, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = base_reg, + .reg2 = registerAlias(reg, @intCast(u32, abi_size)), + .flags = 0b10, + }), + .data = .{ .imm = @bitCast(u32, -stack_offset) }, + }); + } }, - else => return self.fail("TODO genSetStack for AVX register for type {}", .{ty.fmtDebug()}), } }, .memory, @@ -5742,20 +5591,20 @@ fn genInlineMemcpy( .ptr_stack_offset, .stack_offset => |off| { _ = try self.addInst(.{ .tag = .lea, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = dst_addr_reg.to64(), .reg2 = opts.dest_stack_base orelse .rbp, - }).encode(), + }), .data = .{ .imm = @bitCast(u32, -off) }, }); }, .register => |reg| { _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(dst_addr_reg, @divExact(reg.size(), 8)), .reg2 = reg, - }).encode(), + }), .data = undefined, }); }, @@ -5777,20 +5626,20 @@ fn genInlineMemcpy( .ptr_stack_offset, .stack_offset => |off| { _ = try self.addInst(.{ .tag = .lea, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = src_addr_reg.to64(), .reg2 = opts.source_stack_base orelse .rbp, - }).encode(), + }), .data = .{ .imm = @bitCast(u32, -off) }, }); }, .register => |reg| { _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(src_addr_reg, @divExact(reg.size(), 8)), .reg2 = reg, - }).encode(), + }), .data = undefined, }); }, @@ -5810,18 +5659,14 @@ fn genInlineMemcpy( // mov rcx, 0 _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ - .reg1 = .rcx, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rcx }), .data = .{ .imm = 0 }, }); // mov rax, 0 _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ - .reg1 = .rax, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), .data = .{ .imm = 0 }, }); @@ -5829,70 +5674,62 @@ fn genInlineMemcpy( // cmp count, 0 const loop_start = try self.addInst(.{ .tag = .cmp, - .ops = (Mir.Ops(Register, Register){ - .reg1 = count_reg, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = count_reg }), .data = .{ .imm = 0 }, }); // je end const loop_reloc = try self.addInst(.{ .tag = .cond_jmp_eq_ne, - .ops = (Mir.Ops(Register, Register){ .flags = 0b01 }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), .data = .{ .inst = undefined }, }); // mov tmp, [addr + rcx] _ = try self.addInst(.{ .tag = .mov_scale_src, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = tmp_reg.to8(), .reg2 = src_addr_reg, - }).encode(), + }), .data = .{ .imm = 0 }, }); // mov [stack_offset + rax], tmp _ = try self.addInst(.{ .tag = .mov_scale_dst, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = dst_addr_reg, .reg2 = tmp_reg.to8(), - }).encode(), + }), .data = .{ .imm = 0 }, }); // add rcx, 1 _ = try self.addInst(.{ .tag = .add, - .ops = (Mir.Ops(Register, Register){ - .reg1 = .rcx, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rcx }), .data = .{ .imm = 1 }, }); // add rax, 1 _ = try self.addInst(.{ .tag = .add, - .ops = (Mir.Ops(Register, Register){ - .reg1 = .rax, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), .data = .{ .imm = 1 }, }); // sub count, 1 _ = try self.addInst(.{ .tag = .sub, - .ops = (Mir.Ops(Register, Register){ - .reg1 = count_reg, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = count_reg }), .data = .{ .imm = 1 }, }); // jmp loop _ = try self.addInst(.{ .tag = .jmp, - .ops = (Mir.Ops(Register, Register){ .flags = 0b00 }).encode(), + .ops = Mir.Inst.Ops.encode(.{}), .data = .{ .inst = loop_start }, }); @@ -5924,20 +5761,20 @@ fn genInlineMemset( .ptr_stack_offset, .stack_offset => |off| { _ = try self.addInst(.{ .tag = .lea, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg.to64(), .reg2 = opts.dest_stack_base orelse .rbp, - }).encode(), + }), .data = .{ .imm = @bitCast(u32, -off) }, }); }, .register => |reg| { _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(addr_reg, @divExact(reg.size(), 8)), .reg2 = reg, - }).encode(), + }), .data = undefined, }); }, @@ -5955,16 +5792,14 @@ fn genInlineMemset( // cmp rax, -1 const loop_start = try self.addInst(.{ .tag = .cmp, - .ops = (Mir.Ops(Register, Register){ - .reg1 = .rax, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), .data = .{ .imm = @bitCast(u32, @as(i32, -1)) }, }); // je end const loop_reloc = try self.addInst(.{ .tag = .cond_jmp_eq_ne, - .ops = (Mir.Ops(Register, Register){ .flags = 0b01 }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), .data = .{ .inst = undefined }, }); @@ -5980,9 +5815,7 @@ fn genInlineMemset( }); _ = try self.addInst(.{ .tag = .mov_mem_index_imm, - .ops = (Mir.Ops(Register, Register){ - .reg1 = addr_reg, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = addr_reg }), .data = .{ .payload = payload }, }); }, @@ -5992,16 +5825,14 @@ fn genInlineMemset( // sub rax, 1 _ = try self.addInst(.{ .tag = .sub, - .ops = (Mir.Ops(Register, Register){ - .reg1 = .rax, - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rax }), .data = .{ .imm = 1 }, }); // jmp loop _ = try self.addInst(.{ .tag = .jmp, - .ops = (Mir.Ops(Register, Register){ .flags = 0b00 }).encode(), + .ops = Mir.Inst.Ops.encode(.{}), .data = .{ .inst = loop_start }, }); @@ -6009,88 +5840,6 @@ fn genInlineMemset( try self.performReloc(loop_reloc); } -fn genSetAvxReg(self: *Self, ty: Type, reg: AvxRegister, mcv: MCValue) InnerError!void { - switch (mcv) { - .dead => unreachable, - .register_overflow_unsigned, - .register_overflow_signed, - => unreachable, - .stack_offset => |off| { - if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { - return self.fail("stack offset too large", .{}); - } - - switch (ty.zigTypeTag()) { - .Float => { - switch (ty.tag()) { - .f32 => return self.fail("TODO genSetAvxReg from stack offset for f32", .{}), - .f64 => { - _ = try self.addInst(.{ - .tag = .mov_f64, - .ops = (Mir.Ops(AvxRegister, Register){ - .reg1 = reg.to128(), - .reg2 = .rbp, - }).encode(), - .data = .{ .imm = @bitCast(u32, -off) }, - }); - }, - else => return self.fail("TODO genSetAvxReg from stack offset for {}", .{ty.fmtDebug()}), - } - }, - else => return self.fail("TODO genSetAvxReg from stack offset for type {}", .{ty.fmtDebug()}), - } - }, - .avx_register => |src_reg| { - switch (ty.zigTypeTag()) { - .Float => { - switch (ty.tag()) { - .f32 => return self.fail("TODO genSetAvxReg from register for f32", .{}), - .f64 => { - _ = try self.addInst(.{ - .tag = .mov_f64, - .ops = (Mir.Ops(AvxRegister, AvxRegister){ - .reg1 = reg.to128(), - .reg2 = src_reg.to128(), - .flags = 0b10, - }).encode(), - .data = undefined, - }); - }, - else => return self.fail("TODO genSetAvxReg from register for {}", .{ty.fmtDebug()}), - } - }, - else => return self.fail("TODO genSetAvxReg from register for type {}", .{ty.fmtDebug()}), - } - }, - .memory => { - switch (ty.zigTypeTag()) { - .Float => { - switch (ty.tag()) { - .f32 => return self.fail("TODO genSetAvxReg from memory for f32", .{}), - .f64 => { - const base_reg = try self.register_manager.allocReg(null); - try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); - _ = try self.addInst(.{ - .tag = .mov_f64, - .ops = (Mir.Ops(AvxRegister, Register){ - .reg1 = reg.to128(), - .reg2 = base_reg.to64(), - }).encode(), - .data = .{ .imm = 0 }, - }); - }, - else => return self.fail("TODO genSetAvxReg from memory for {}", .{ty.fmtDebug()}), - } - }, - else => return self.fail("TODO genSetAvxReg from memory for type {}", .{ty.fmtDebug()}), - } - }, - else => |other| { - return self.fail("TODO genSetAvxReg from {}", .{other}); - }, - } -} - fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { const abi_size = @intCast(u32, ty.abiSize(self.target.*)); switch (mcv) { @@ -6098,17 +5847,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .register_overflow_unsigned, .register_overflow_signed, => unreachable, - .avx_register => unreachable, .ptr_stack_offset => |off| { if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { return self.fail("stack offset too large", .{}); } _ = try self.addInst(.{ .tag = .lea, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size), .reg2 = .rbp, - }).encode(), + }), .data = .{ .imm = @bitCast(u32, -off) }, }); }, @@ -6145,10 +5893,10 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }; _ = try self.addInst(.{ .tag = tag, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg.to8(), .flags = flags, - }).encode(), + }), .data = undefined, }); }, @@ -6158,10 +5906,10 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void if (x == 0) { _ = try self.addInst(.{ .tag = .xor, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg.to32(), .reg2 = reg.to32(), - }).encode(), + }), .data = undefined, }); return; @@ -6170,9 +5918,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void // Next best case: if we set the lower four bytes, the upper four will be zeroed. _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ - .reg1 = registerAlias(reg, abi_size), - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size) }), .data = .{ .imm = @truncate(u32, x) }, }); return; @@ -6187,9 +5933,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void const payload = try self.addExtra(Mir.Imm64.encode(x)); _ = try self.addInst(.{ .tag = .movabs, - .ops = (Mir.Ops(Register, Register){ - .reg1 = reg.to64(), - }).encode(), + .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg.to64() }), .data = .{ .payload = payload }, }); }, @@ -6198,40 +5942,60 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void if (src_reg.id() == reg.id()) return; - if (ty.zigTypeTag() == .Int) blk: { - switch (ty.intInfo(self.target.*).signedness) { + switch (ty.zigTypeTag()) { + .Int => switch (ty.intInfo(self.target.*).signedness) { .signed => { - if (abi_size > 4) break :blk; - _ = try self.addInst(.{ - .tag = .mov_sign_extend, - .ops = (Mir.Ops(Register, Register){ - .reg1 = reg.to64(), - .reg2 = registerAlias(src_reg, abi_size), - }).encode(), - .data = undefined, - }); + if (abi_size <= 4) { + _ = try self.addInst(.{ + .tag = .mov_sign_extend, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = reg.to64(), + .reg2 = registerAlias(src_reg, abi_size), + }), + .data = undefined, + }); + return; + } }, .unsigned => { - if (abi_size > 2) break :blk; + if (abi_size <= 2) { + _ = try self.addInst(.{ + .tag = .mov_zero_extend, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = reg.to64(), + .reg2 = registerAlias(src_reg, abi_size), + }), + .data = undefined, + }); + return; + } + }, + }, + .Float => switch (ty.tag()) { + .f32 => return self.fail("TODO genSetReg from register for f32", .{}), + .f64 => { _ = try self.addInst(.{ - .tag = .mov_zero_extend, - .ops = (Mir.Ops(Register, Register){ - .reg1 = reg.to64(), - .reg2 = registerAlias(src_reg, abi_size), - }).encode(), + .tag = .mov_f64, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = reg.to128(), + .reg2 = src_reg.to128(), + .flags = 0b10, + }), .data = undefined, }); + return; }, - } - return; + else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}), + }, + else => {}, } _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size), .reg2 = registerAlias(src_reg, abi_size), - }).encode(), + }), .data = undefined, }); }, @@ -6241,107 +6005,148 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void try self.loadMemPtrIntoRegister(reg, Type.usize, mcv); _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size), .reg2 = reg.to64(), .flags = 0b01, - }).encode(), + }), .data = .{ .imm = 0 }, }); }, - .memory => |x| { - if (x <= math.maxInt(i32)) { - // mov reg, [ds:imm32] - _ = try self.addInst(.{ - .tag = .mov, - .ops = (Mir.Ops(Register, Register){ - .reg1 = registerAlias(reg, abi_size), - .flags = 0b01, - }).encode(), - .data = .{ .imm = @truncate(u32, x) }, - }); - } else { - // If this is RAX, we can use a direct load. - // Otherwise, we need to load the address, then indirectly load the value. - if (reg.id() == 0) { - // movabs rax, ds:moffs64 - const payload = try self.addExtra(Mir.Imm64.encode(x)); - _ = try self.addInst(.{ - .tag = .movabs, - .ops = (Mir.Ops(Register, Register){ - .reg1 = .rax, - .flags = 0b01, // imm64 will become moffs64 - }).encode(), - .data = .{ .payload = payload }, - }); - } else { - // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue. - try self.genSetReg(ty, reg, MCValue{ .immediate = x }); - - // mov reg, [reg + 0x0] + .memory => |x| switch (ty.zigTypeTag()) { + .Float => { + switch (ty.tag()) { + .f32 => return self.fail("TODO genSetReg from memory for f32", .{}), + .f64 => { + const base_reg = try self.register_manager.allocReg(null); + try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); + _ = try self.addInst(.{ + .tag = .mov_f64, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = reg.to128(), + .reg2 = base_reg.to64(), + }), + .data = .{ .imm = 0 }, + }); + }, + else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), + } + }, + else => { + if (x <= math.maxInt(i32)) { + // mov reg, [ds:imm32] _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size), - .reg2 = reg.to64(), .flags = 0b01, - }).encode(), - .data = .{ .imm = 0 }, + }), + .data = .{ .imm = @truncate(u32, x) }, }); + } else { + // If this is RAX, we can use a direct load. + // Otherwise, we need to load the address, then indirectly load the value. + if (reg.id() == 0) { + // movabs rax, ds:moffs64 + const payload = try self.addExtra(Mir.Imm64.encode(x)); + _ = try self.addInst(.{ + .tag = .movabs, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = .rax, + .flags = 0b01, // imm64 will become moffs64 + }), + .data = .{ .payload = payload }, + }); + } else { + // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue. + try self.genSetReg(ty, reg, MCValue{ .immediate = x }); + + // mov reg, [reg + 0x0] + _ = try self.addInst(.{ + .tag = .mov, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = registerAlias(reg, abi_size), + .reg2 = reg.to64(), + .flags = 0b01, + }), + .data = .{ .imm = 0 }, + }); + } } - } + }, }, .stack_offset => |off| { if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { return self.fail("stack offset too large", .{}); } - if (ty.zigTypeTag() == .Int) blk: { - switch (ty.intInfo(self.target.*).signedness) { + switch (ty.zigTypeTag()) { + .Int => switch (ty.intInfo(self.target.*).signedness) { .signed => { - const flags: u2 = switch (abi_size) { - 1 => 0b01, - 2 => 0b10, - 4 => 0b11, - else => break :blk, - }; - _ = try self.addInst(.{ - .tag = .mov_sign_extend, - .ops = (Mir.Ops(Register, Register){ - .reg1 = reg.to64(), - .reg2 = .rbp, - .flags = flags, - }).encode(), - .data = .{ .imm = @bitCast(u32, -off) }, - }); + if (abi_size <= 4) { + const flags: u2 = switch (abi_size) { + 1 => 0b01, + 2 => 0b10, + 4 => 0b11, + else => unreachable, + }; + _ = try self.addInst(.{ + .tag = .mov_sign_extend, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = reg.to64(), + .reg2 = .rbp, + .flags = flags, + }), + .data = .{ .imm = @bitCast(u32, -off) }, + }); + return; + } }, .unsigned => { - const flags: u2 = switch (abi_size) { - 1 => 0b01, - 2 => 0b10, - else => break :blk, - }; + if (abi_size <= 2) { + const flags: u2 = switch (abi_size) { + 1 => 0b01, + 2 => 0b10, + else => unreachable, + }; + _ = try self.addInst(.{ + .tag = .mov_zero_extend, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = reg.to64(), + .reg2 = .rbp, + .flags = flags, + }), + .data = .{ .imm = @bitCast(u32, -off) }, + }); + return; + } + }, + }, + .Float => switch (ty.tag()) { + .f32 => return self.fail("TODO genSetReg from stack offset for f32", .{}), + .f64 => { _ = try self.addInst(.{ - .tag = .mov_zero_extend, - .ops = (Mir.Ops(Register, Register){ - .reg1 = reg.to64(), + .tag = .mov_f64, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = reg.to128(), .reg2 = .rbp, - .flags = flags, - }).encode(), + }), .data = .{ .imm = @bitCast(u32, -off) }, }); + return; }, - } - return; + else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}), + }, + else => {}, } _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = registerAlias(reg, abi_size), .reg2 = .rbp, .flags = 0b01, - }).encode(), + }), .data = .{ .imm = @bitCast(u32, -off) }, }); }, @@ -6408,14 +6213,14 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { }; _ = try self.addInst(.{ .tag = .fld, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = .rbp, .flags = switch (src_ty.abiSize(self.target.*)) { 4 => 0b01, 8 => 0b10, else => |size| return self.fail("TODO load ST(0) with abiSize={}", .{size}), }, - .reg1 = .rbp, - }).encode(), + }), .data = .{ .imm = @bitCast(u32, -stack_offset) }, }); @@ -6423,15 +6228,15 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { const stack_dst = try self.allocRegOrMem(inst, false); _ = try self.addInst(.{ .tag = .fisttp, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = .rbp, .flags = switch (dst_ty.abiSize(self.target.*)) { 1...2 => 0b00, 3...4 => 0b01, 5...8 => 0b10, else => |size| return self.fail("TODO convert float with abiSize={}", .{size}), }, - .reg1 = .rbp, - }).encode(), + }), .data = .{ .imm = @bitCast(u32, -stack_dst.stack_offset) }, }); @@ -6527,11 +6332,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr); _ = try self.addInst(.{ .tag = .mov, - .ops = (Mir.Ops(Register, Register){ + .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg, .reg2 = reg, .flags = 0b01, - }).encode(), + }), .data = .{ .imm = 0 }, }); break :blk MCValue{ .register = reg }; @@ -7095,22 +6900,11 @@ fn registerAlias(reg: Register, size_bytes: u32) Register { return reg.to32(); } else if (size_bytes <= 8) { return reg.to64(); - } else { - unreachable; - } -} - -/// Returns AVX register wide enough to hold at least `size_bytes`. -fn avxRegisterAlias(reg: AvxRegister, size_bytes: u32) AvxRegister { - if (size_bytes == 0) { - unreachable; // should be comptime known } else if (size_bytes <= 16) { return reg.to128(); } else if (size_bytes <= 32) { return reg.to256(); - } else { - unreachable; - } + } else unreachable; } /// Truncates the value in the register in place. diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 79341df8cd..5ad8e86374 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -25,9 +25,8 @@ const MCValue = @import("CodeGen.zig").MCValue; const Mir = @import("Mir.zig"); const Module = @import("../../Module.zig"); const Instruction = bits.Instruction; -const GpRegister = bits.Register; -const AvxRegister = bits.AvxRegister; const Type = @import("../../type.zig").Type; +const Register = bits.Register; mir: Mir, bin_file: *link.File, @@ -238,7 +237,7 @@ fn fixupRelocs(emit: *Emit) InnerError!void { fn mirInterrupt(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .interrupt); - const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => return lowerToZoEnc(.int3, emit.code), else => return emit.fail("TODO handle variant 0b{b} of interrupt instruction", .{ops.flags}), @@ -254,11 +253,11 @@ fn mirSyscall(emit: *Emit) InnerError!void { } fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { // PUSH/POP reg - return lowerToOEnc(tag, Register.reg(ops.reg1), emit.code); + return lowerToOEnc(tag, ops.reg1, emit.code); }, 0b01 => { // PUSH/POP r/m64 @@ -283,7 +282,7 @@ fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { } fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); const payload = emit.mir.instructions.items(.data)[inst].payload; const data = emit.mir.extraData(Mir.RegsToPushOrPop, payload).data; const regs = data.regs; @@ -294,9 +293,9 @@ fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.I try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ .disp = @bitCast(u32, -@intCast(i32, disp)), .base = ops.reg1, - }), Register.reg(reg.to64()), emit.code); + }), reg.to64(), emit.code); } else { - try lowerToRmEnc(.mov, Register.reg(reg.to64()), RegisterOrMemory.mem(.qword_ptr, .{ + try lowerToRmEnc(.mov, reg.to64(), RegisterOrMemory.mem(.qword_ptr, .{ .disp = @bitCast(u32, -@intCast(i32, disp)), .base = ops.reg1, }), emit.code); @@ -306,7 +305,7 @@ fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.I } fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { const target = emit.mir.instructions.items(.data)[inst].inst; @@ -335,7 +334,7 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { 0b10 => { // JMP/CALL r/m64 const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToMEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ + return lowerToMEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ .disp = imm, .base = ops.reg1, }), emit.code); @@ -345,7 +344,7 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { } fn mirCondJmp(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); const target = emit.mir.instructions.items(.data)[inst].inst; const tag = switch (mir_tag) { .cond_jmp_greater_less => switch (ops.flags) { @@ -377,7 +376,7 @@ fn mirCondJmp(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerErr } fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); const tag = switch (mir_tag) { .cond_set_byte_greater_less => switch (ops.flags) { 0b00 => Tag.setge, @@ -407,9 +406,9 @@ fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) Inne } fn mirCondMov(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); if (ops.flags == 0b00) { - return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), emit.code); + return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); } const imm = emit.mir.instructions.items(.data)[inst].imm; const ptr_size: Memory.PtrSize = switch (ops.flags) { @@ -418,7 +417,7 @@ fn mirCondMov(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { 0b10 => .dword_ptr, 0b11 => .qword_ptr, }; - return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.mem(ptr_size, .{ + return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(ptr_size, .{ .disp = imm, .base = ops.reg2, }), emit.code); @@ -427,7 +426,7 @@ fn mirCondMov(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .@"test"); - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { if (ops.reg2 == .none) { @@ -442,12 +441,7 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, emit.code); } // TEST r/m64, r64 - return lowerToMrEnc( - .@"test", - RegisterOrMemory.reg(ops.reg1), - Register.reg(ops.reg2), - emit.code, - ); + return lowerToMrEnc(.@"test", RegisterOrMemory.reg(ops.reg1), ops.reg2, emit.code); }, else => return emit.fail("TODO more TEST alternatives", .{}), } @@ -456,7 +450,7 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .ret); - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { // RETF imm16 @@ -480,7 +474,7 @@ fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { if (ops.reg2 == .none) { @@ -491,14 +485,14 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { } // mov reg1, reg2 // RM - return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), emit.code); + return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); }, 0b01 => { // mov reg1, [reg2 + imm32] // RM const imm = emit.mir.instructions.items(.data)[inst].imm; - const src_reg: ?GpRegister = if (ops.reg2 == .none) null else ops.reg2; - return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ + const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; + return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ .disp = imm, .base = src_reg, }), emit.code); @@ -510,10 +504,10 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { // mov [reg1 + imm32], reg2 // MR const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg2.size()), .{ + return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{ .disp = imm, .base = ops.reg1, - }), Register.reg(ops.reg2), emit.code); + }), ops.reg2, emit.code); }, 0b11 => { return emit.fail("TODO unused variant: mov reg1, reg2, 0b11", .{}); @@ -522,7 +516,7 @@ fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { } fn mirArithMemImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); assert(ops.reg2 == .none); const payload = emit.mir.instructions.items(.data)[inst].payload; const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; @@ -539,19 +533,14 @@ fn mirArithMemImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { } inline fn setRexWRegister(reg: Register) bool { - switch (reg) { - .avx_register => return false, - .register => |r| { - if (r.size() == 64) return true; - return switch (r) { - .ah, .bh, .ch, .dh => true, - else => false, - }; - }, - } + if (reg.size() == 64) return true; + return switch (reg) { + .ah, .ch, .dh, .bh => true, + else => false, + }; } -inline fn immOpSize(u_imm: u32) u8 { +inline fn immOpSize(u_imm: u32) u6 { const imm = @bitCast(i32, u_imm); if (math.minInt(i8) <= imm and imm <= math.maxInt(i8)) { return 8; @@ -563,7 +552,7 @@ inline fn immOpSize(u_imm: u32) u8 { } fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); const scale = ops.flags; const imm = emit.mir.instructions.items(.data)[inst].imm; // OP reg1, [reg2 + scale*rcx + imm32] @@ -571,7 +560,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void .scale = scale, .index = .rcx, }; - return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ + return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ .disp = imm, .base = ops.reg2, .scale_index = scale_index, @@ -579,7 +568,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void } fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); const scale = ops.flags; const imm = emit.mir.instructions.items(.data)[inst].imm; const scale_index = ScaleIndex{ @@ -595,15 +584,15 @@ fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void }), imm, emit.code); } // OP [reg1 + scale*rax + imm32], reg2 - return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg2.size()), .{ + return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{ .disp = imm, .base = ops.reg1, .scale_index = scale_index, - }), Register.reg(ops.reg2), emit.code); + }), ops.reg2, emit.code); } fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); const scale = ops.flags; const payload = emit.mir.instructions.items(.data)[inst].payload; const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; @@ -620,7 +609,7 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void } fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); assert(ops.reg2 == .none); const payload = emit.mir.instructions.items(.data)[inst].payload; const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; @@ -645,27 +634,27 @@ fn mirArithMemIndexImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!v fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const mir_tag = emit.mir.instructions.items(.tag)[inst]; assert(mir_tag == .mov_sign_extend); - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); const imm = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].imm else undefined; switch (ops.flags) { 0b00 => { const tag: Tag = if (ops.reg2.size() == 32) .movsxd else .movsx; - return lowerToRmEnc(tag, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), emit.code); + return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); }, 0b01 => { - return lowerToRmEnc(.movsx, Register.reg(ops.reg1), RegisterOrMemory.mem(.byte_ptr, .{ + return lowerToRmEnc(.movsx, ops.reg1, RegisterOrMemory.mem(.byte_ptr, .{ .disp = imm, .base = ops.reg2, }), emit.code); }, 0b10 => { - return lowerToRmEnc(.movsx, Register.reg(ops.reg1), RegisterOrMemory.mem(.word_ptr, .{ + return lowerToRmEnc(.movsx, ops.reg1, RegisterOrMemory.mem(.word_ptr, .{ .disp = imm, .base = ops.reg2, }), emit.code); }, 0b11 => { - return lowerToRmEnc(.movsxd, Register.reg(ops.reg1), RegisterOrMemory.mem(.dword_ptr, .{ + return lowerToRmEnc(.movsxd, ops.reg1, RegisterOrMemory.mem(.dword_ptr, .{ .disp = imm, .base = ops.reg2, }), emit.code); @@ -676,20 +665,20 @@ fn mirMovSignExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { fn mirMovZeroExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const mir_tag = emit.mir.instructions.items(.tag)[inst]; assert(mir_tag == .mov_zero_extend); - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); const imm = if (ops.flags != 0b00) emit.mir.instructions.items(.data)[inst].imm else undefined; switch (ops.flags) { 0b00 => { - return lowerToRmEnc(.movzx, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), emit.code); + return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); }, 0b01 => { - return lowerToRmEnc(.movzx, Register.reg(ops.reg1), RegisterOrMemory.mem(.byte_ptr, .{ + return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.mem(.byte_ptr, .{ .disp = imm, .base = ops.reg2, }), emit.code); }, 0b10 => { - return lowerToRmEnc(.movzx, Register.reg(ops.reg1), RegisterOrMemory.mem(.word_ptr, .{ + return lowerToRmEnc(.movzx, ops.reg1, RegisterOrMemory.mem(.word_ptr, .{ .disp = imm, .base = ops.reg2, }), emit.code); @@ -703,31 +692,46 @@ fn mirMovZeroExtend(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .movabs); - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); - const imm: u64 = if (ops.reg1.size() == 64) blk: { - const payload = emit.mir.instructions.items(.data)[inst].payload; - const imm = emit.mir.extraData(Mir.Imm64, payload).data; - break :blk imm.decode(); - } else emit.mir.instructions.items(.data)[inst].imm; - if (ops.flags == 0b00) { - // movabs reg, imm64 - // OI - return lowerToOiEnc(.mov, Register.reg(ops.reg1), imm, emit.code); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); + switch (ops.flags) { + 0b00 => { + const imm: u64 = if (ops.reg1.size() == 64) blk: { + const payload = emit.mir.instructions.items(.data)[inst].payload; + const imm = emit.mir.extraData(Mir.Imm64, payload).data; + break :blk imm.decode(); + } else emit.mir.instructions.items(.data)[inst].imm; + // movabs reg, imm64 + // OI + return lowerToOiEnc(.mov, ops.reg1, imm, emit.code); + }, + 0b01 => { + if (ops.reg1 == .none) { + const imm: u64 = if (ops.reg2.size() == 64) blk: { + const payload = emit.mir.instructions.items(.data)[inst].payload; + const imm = emit.mir.extraData(Mir.Imm64, payload).data; + break :blk imm.decode(); + } else emit.mir.instructions.items(.data)[inst].imm; + // movabs moffs64, rax + // TD + return lowerToTdEnc(.mov, imm, ops.reg2, emit.code); + } + const imm: u64 = if (ops.reg1.size() == 64) blk: { + const payload = emit.mir.instructions.items(.data)[inst].payload; + const imm = emit.mir.extraData(Mir.Imm64, payload).data; + break :blk imm.decode(); + } else emit.mir.instructions.items(.data)[inst].imm; + // movabs rax, moffs64 + // FD + return lowerToFdEnc(.mov, ops.reg1, imm, emit.code); + }, + else => return emit.fail("TODO unused variant: movabs 0b{b}", .{ops.flags}), } - if (ops.reg1 == .none) { - // movabs moffs64, rax - // TD - return lowerToTdEnc(.mov, imm, Register.reg(ops.reg2), emit.code); - } - // movabs rax, moffs64 - // FD - return lowerToFdEnc(.mov, Register.reg(ops.reg1), imm, emit.code); } fn mirFisttp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .fisttp); - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); // the selecting between operand sizes for this particular `fisttp` instruction // is done via opcode instead of the usual prefixes. @@ -749,7 +753,7 @@ fn mirFisttp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { fn mirFld(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .fld); - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); // the selecting between operand sizes for this particular `fisttp` instruction // is done via opcode instead of the usual prefixes. @@ -768,7 +772,7 @@ fn mirFld(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { // sal reg1, 1 @@ -793,12 +797,11 @@ fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { } fn mirMulDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); if (ops.reg1 != .none) { assert(ops.reg2 == .none); return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); } - assert(ops.reg1 == .none); assert(ops.reg2 != .none); const imm = emit.mir.instructions.items(.data)[inst].imm; const ptr_size: Memory.PtrSize = switch (ops.flags) { @@ -816,27 +819,27 @@ fn mirMulDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .imul_complex); - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { - return lowerToRmEnc(.imul, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), emit.code); + return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); }, 0b01 => { const imm = emit.mir.instructions.items(.data)[inst].imm; - const src_reg: ?GpRegister = if (ops.reg2 == .none) null else ops.reg2; - return lowerToRmEnc(.imul, Register.reg(ops.reg1), RegisterOrMemory.mem(.qword_ptr, .{ + const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; + return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{ .disp = imm, .base = src_reg, }), emit.code); }, 0b10 => { const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToRmiEnc(.imul, Register.reg(ops.reg1), RegisterOrMemory.reg(ops.reg2), imm, emit.code); + return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code); }, 0b11 => { const payload = emit.mir.instructions.items(.data)[inst].payload; const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data; - return lowerToRmiEnc(.imul, Register.reg(ops.reg1), RegisterOrMemory.mem(.qword_ptr, .{ + return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{ .disp = imm_pair.dest_off, .base = ops.reg2, }), imm_pair.operand, emit.code); @@ -845,7 +848,7 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } fn mirCwd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); const tag: Tag = switch (ops.flags) { 0b00 => .cbw, 0b01 => .cwd, @@ -858,17 +861,17 @@ fn mirCwd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .lea); - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { // lea reg1, [reg2 + imm32] // RM const imm = emit.mir.instructions.items(.data)[inst].imm; - const src_reg: ?GpRegister = if (ops.reg2 == .none) null else ops.reg2; + const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; return lowerToRmEnc( .lea, - Register.reg(ops.reg1), - RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ + ops.reg1, + RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ .disp = imm, .base = src_reg, }), @@ -881,8 +884,8 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const start_offset = emit.code.items.len; try lowerToRmEnc( .lea, - Register.reg(ops.reg1), - RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0), + ops.reg1, + RegisterOrMemory.rip(Memory.PtrSize.new(ops.reg1.size()), 0), emit.code, ); const end_offset = emit.code.items.len; @@ -895,15 +898,15 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { 0b10 => { // lea reg, [rbp + rcx + imm32] const imm = emit.mir.instructions.items(.data)[inst].imm; - const src_reg: ?GpRegister = if (ops.reg2 == .none) null else ops.reg2; + const src_reg: ?Register = if (ops.reg2 != .none) ops.reg2 else null; const scale_index = ScaleIndex{ .scale = 0, .index = .rcx, }; return lowerToRmEnc( .lea, - Register.reg(ops.reg1), - RegisterOrMemory.mem(Memory.PtrSize.fromBits(ops.reg1.size()), .{ + ops.reg1, + RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ .disp = imm, .base = src_reg, .scale_index = scale_index, @@ -918,15 +921,15 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .lea_pie); - const ops = Mir.Ops(GpRegister, GpRegister).decode(emit.mir.instructions.items(.ops)[inst]); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); const load_reloc = emit.mir.instructions.items(.data)[inst].load_reloc; // lea reg1, [rip + reloc] // RM try lowerToRmEnc( .lea, - Register.reg(ops.reg1), - RegisterOrMemory.rip(Memory.PtrSize.fromBits(ops.reg1.size()), 0), + ops.reg1, + RegisterOrMemory.rip(Memory.PtrSize.new(ops.reg1.size()), 0), emit.code, ); @@ -962,78 +965,70 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { fn mirMovF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .mov_f64); - const ops = emit.mir.instructions.items(.ops)[inst]; - const flags = @truncate(u2, ops); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (flags) { + switch (ops.flags) { 0b00 => { - const decoded = Mir.Ops(AvxRegister, GpRegister).decode(ops); const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToRmEnc(.vmovsd, Register.avxReg(decoded.reg1), RegisterOrMemory.mem(.qword_ptr, .{ + return lowerToVmEnc(.vmovsd, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{ .disp = imm, - .base = decoded.reg2, + .base = ops.reg2, }), emit.code); }, 0b01 => { - const decoded = Mir.Ops(GpRegister, AvxRegister).decode(ops); const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToMrEnc(.vmovsd, RegisterOrMemory.mem(.qword_ptr, .{ + return lowerToMvEnc(.vmovsd, RegisterOrMemory.mem(.qword_ptr, .{ .disp = imm, - .base = decoded.reg1, - }), Register.avxReg(decoded.reg2), emit.code); + .base = ops.reg1, + }), ops.reg2, emit.code); }, 0b10 => { - const decoded = Mir.Ops(AvxRegister, AvxRegister).decode(ops); return lowerToRvmEnc( .vmovsd, - Register.avxReg(decoded.reg1), - Register.avxReg(decoded.reg1), - RegisterOrMemory.avxReg(decoded.reg2), + ops.reg1, + ops.reg1, + RegisterOrMemory.reg(ops.reg2), emit.code, ); }, - else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{flags}), + else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{ops.flags}), } } fn mirAddF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .add_f64); - const ops = emit.mir.instructions.items(.ops)[inst]; - const flags = @truncate(u2, ops); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (flags) { + switch (ops.flags) { 0b00 => { - const decoded = Mir.Ops(AvxRegister, AvxRegister).decode(ops); return lowerToRvmEnc( .vaddsd, - Register.avxReg(decoded.reg1), - Register.avxReg(decoded.reg1), - RegisterOrMemory.avxReg(decoded.reg2), + ops.reg1, + ops.reg1, + RegisterOrMemory.reg(ops.reg2), emit.code, ); }, - else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{flags}), + else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{ops.flags}), } } fn mirCmpF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { const tag = emit.mir.instructions.items(.tag)[inst]; assert(tag == .cmp_f64); - const ops = emit.mir.instructions.items(.ops)[inst]; - const flags = @truncate(u2, ops); + const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (flags) { + switch (ops.flags) { 0b00 => { - const decoded = Mir.Ops(AvxRegister, AvxRegister).decode(ops); - return lowerToRmEnc( + return lowerToVmEnc( .vucomisd, - Register.avxReg(decoded.reg1), - RegisterOrMemory.avxReg(decoded.reg2), + ops.reg1, + RegisterOrMemory.reg(ops.reg2), emit.code, ); }, - else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{flags}), + else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{ops.flags}), } } @@ -1277,6 +1272,18 @@ const Tag = enum { vcmpsd, vucomisd, + fn isAvx(tag: Tag) bool { + return switch (tag) { + .vmovsd, + .vaddsd, + .vcmpsd, + .vucomisd, + => true, + + else => false, + }; + } + fn isSetCC(tag: Tag) bool { return switch (tag) { .seto, @@ -1361,6 +1368,12 @@ const Encoding = enum { /// OP r64, r/m64, imm32 rmi, + /// OP xmm1, xmm2/m64 + vm, + + /// OP m64, xmm1 + mv, + /// OP xmm1, xmm2, xmm3/m64 rvm, @@ -1389,7 +1402,7 @@ const OpCode = union(enum) { fn encodeWithReg(opc: OpCode, encoder: Encoder, reg: Register) void { assert(opc == .one_byte); - encoder.opcode_withReg(opc.one_byte, reg.lowId()); + encoder.opcode_withReg(opc.one_byte, reg.lowEnc()); } }; @@ -1536,6 +1549,15 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .imul => OpCode.oneByte(if (is_one_byte) 0x6b else 0x69), else => null, }, + .mv => return switch (tag) { + .vmovsd => OpCode.oneByte(0x11), + else => null, + }, + .vm => return switch (tag) { + .vmovsd => OpCode.oneByte(0x10), + .vucomisd => OpCode.oneByte(0x2e), + else => null, + }, .rvm => return switch (tag) { .vaddsd => OpCode.oneByte(0x58), .vmovsd => OpCode.oneByte(0x10), @@ -1647,11 +1669,11 @@ inline fn getVexPrefix(tag: Tag, enc: Encoding) ?VexPrefix { } = .none, } = blk: { switch (enc) { - .mr => switch (tag) { + .mv => switch (tag) { .vmovsd => break :blk .{ .lig = true, .simd_prefix = .p_f2, .wig = true }, else => return null, }, - .rm => switch (tag) { + .vm => switch (tag) { .vmovsd => break :blk .{ .lig = true, .simd_prefix = .p_f2, .wig = true }, .vucomisd => break :blk .{ .lig = true, .simd_prefix = .p_66, .wig = true }, else => return null, @@ -1697,61 +1719,50 @@ inline fn getVexPrefix(tag: Tag, enc: Encoding) ?VexPrefix { } }; } -const ScaleIndex = struct { +const ScaleIndex = packed struct { scale: u2, - index: GpRegister, + index: Register, }; const Memory = struct { - base: ?GpRegister, + base: ?Register, rip: bool = false, disp: u32, ptr_size: PtrSize, scale_index: ?ScaleIndex = null, - const PtrSize = enum { - byte_ptr, - word_ptr, - dword_ptr, - qword_ptr, + const PtrSize = enum(u2) { + byte_ptr = 0b00, + word_ptr = 0b01, + dword_ptr = 0b10, + qword_ptr = 0b11, - fn fromBits(in_bits: u64) PtrSize { - return switch (in_bits) { - 8 => .byte_ptr, - 16 => .word_ptr, - 32 => .dword_ptr, - 64 => .qword_ptr, - else => unreachable, - }; + fn new(bit_size: u64) PtrSize { + return @intToEnum(PtrSize, math.log2_int(u4, @intCast(u4, @divExact(bit_size, 8)))); } /// Returns size in bits. fn size(ptr_size: PtrSize) u64 { - return switch (ptr_size) { - .byte_ptr => 8, - .word_ptr => 16, - .dword_ptr => 32, - .qword_ptr => 64, - }; + return 8 * (math.powi(u8, 2, @enumToInt(ptr_size)) catch unreachable); } }; fn encode(mem_op: Memory, encoder: Encoder, operand: u3) void { if (mem_op.base) |base| { - const dst = base.lowId(); + const dst = base.lowEnc(); const src = operand; if (dst == 4 or mem_op.scale_index != null) { if (mem_op.disp == 0 and dst != 5) { encoder.modRm_SIBDisp0(src); if (mem_op.scale_index) |si| { - encoder.sib_scaleIndexBase(si.scale, si.index.lowId(), dst); + encoder.sib_scaleIndexBase(si.scale, si.index.lowEnc(), dst); } else { encoder.sib_base(dst); } } else if (immOpSize(mem_op.disp) == 8) { encoder.modRm_SIBDisp8(src); if (mem_op.scale_index) |si| { - encoder.sib_scaleIndexBaseDisp8(si.scale, si.index.lowId(), dst); + encoder.sib_scaleIndexBaseDisp8(si.scale, si.index.lowEnc(), dst); } else { encoder.sib_baseDisp8(dst); } @@ -1759,7 +1770,7 @@ const Memory = struct { } else { encoder.modRm_SIBDisp32(src); if (mem_op.scale_index) |si| { - encoder.sib_scaleIndexBaseDisp32(si.scale, si.index.lowId(), dst); + encoder.sib_scaleIndexBaseDisp32(si.scale, si.index.lowEnc(), dst); } else { encoder.sib_baseDisp32(dst); } @@ -1782,7 +1793,7 @@ const Memory = struct { } else { encoder.modRm_SIBDisp0(operand); if (mem_op.scale_index) |si| { - encoder.sib_scaleIndexDisp32(si.scale, si.index.lowId()); + encoder.sib_scaleIndexDisp32(si.scale, si.index.lowEnc()); } else { encoder.sib_disp32(); } @@ -1791,6 +1802,7 @@ const Memory = struct { } } + /// Returns size in bits. fn size(memory: Memory) u64 { return memory.ptr_size.size(); } @@ -1805,55 +1817,17 @@ fn encodeImm(encoder: Encoder, imm: u32, size: u64) void { } } -const Register = union(enum) { - register: GpRegister, - avx_register: AvxRegister, - - fn reg(register: GpRegister) Register { - return .{ .register = register }; - } - - fn avxReg(register: AvxRegister) Register { - return .{ .avx_register = register }; - } - - fn lowId(register: Register) u3 { - return switch (register) { - .register => |r| r.lowId(), - .avx_register => |r| r.lowId(), - }; - } - - fn size(register: Register) u64 { - return switch (register) { - .register => |r| r.size(), - .avx_register => |r| r.size(), - }; - } - - fn isExtended(register: Register) bool { - return switch (register) { - .register => |r| r.isExtended(), - .avx_register => |r| r.isExtended(), - }; - } -}; - const RegisterOrMemory = union(enum) { register: Register, memory: Memory, - fn reg(register: GpRegister) RegisterOrMemory { - return .{ .register = Register.reg(register) }; - } - - fn avxReg(register: AvxRegister) RegisterOrMemory { - return .{ .register = Register.avxReg(register) }; + fn reg(register: Register) RegisterOrMemory { + return .{ .register = register }; } fn mem(ptr_size: Memory.PtrSize, args: struct { disp: u32, - base: ?GpRegister = null, + base: ?Register = null, scale_index: ?ScaleIndex = null, }) RegisterOrMemory { return .{ @@ -1877,6 +1851,7 @@ const RegisterOrMemory = union(enum) { }; } + /// Returns size in bits. fn size(reg_or_mem: RegisterOrMemory) u64 { return switch (reg_or_mem) { .register => |reg| reg.size(), @@ -1886,6 +1861,7 @@ const RegisterOrMemory = union(enum) { }; fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) InnerError!void { + assert(!tag.isAvx()); const opc = getOpCode(tag, .zo, false).?; const encoder = try Encoder.init(code, 2); switch (tag) { @@ -1900,6 +1876,7 @@ fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) InnerError!void { } fn lowerToIEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void { + assert(!tag.isAvx()); if (tag == .ret_far or tag == .ret_near) { const encoder = try Encoder.init(code, 3); const opc = getOpCode(tag, .i, false).?; @@ -1917,6 +1894,7 @@ fn lowerToIEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void { } fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!void { + assert(!tag.isAvx()); const opc = getOpCode(tag, .o, false).?; const encoder = try Encoder.init(code, 3); if (reg.size() == 16) { @@ -1930,6 +1908,7 @@ fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!voi } fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void { + assert(!tag.isAvx()); const opc = getOpCode(tag, .d, false).?; const encoder = try Encoder.init(code, 6); opc.encode(encoder); @@ -1937,6 +1916,7 @@ fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void { } fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *std.ArrayList(u8)) InnerError!void { + assert(!tag.isAvx()); const opc = getOpCode(tag, enc, reg_or_mem.size() == 8).?; const modrm_ext = getModRmExt(tag).?; switch (reg_or_mem) { @@ -1951,7 +1931,7 @@ fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *st .b = reg.isExtended(), }); opc.encode(encoder); - encoder.modRm_direct(modrm_ext, reg.lowId()); + encoder.modRm_direct(modrm_ext, reg.lowEnc()); }, .memory => |mem_op| { const encoder = try Encoder.init(code, 8); @@ -1992,6 +1972,7 @@ fn lowerToFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8)) I } fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8), td: bool) InnerError!void { + assert(!tag.isAvx()); const opc = if (td) getOpCode(tag, .td, reg.size() == 8).? else @@ -2014,6 +1995,7 @@ fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8), } fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) InnerError!void { + assert(!tag.isAvx()); const opc = getOpCode(tag, .oi, reg.size() == 8).?; const encoder = try Encoder.init(code, 10); if (reg.size() == 16) { @@ -2040,6 +2022,7 @@ fn lowerToMiXEnc( enc: Encoding, code: *std.ArrayList(u8), ) InnerError!void { + assert(!tag.isAvx()); const modrm_ext = getModRmExt(tag).?; const opc = getOpCode(tag, enc, reg_or_mem.size() == 8).?; switch (reg_or_mem) { @@ -2056,7 +2039,7 @@ fn lowerToMiXEnc( .b = dst_reg.isExtended(), }); opc.encode(encoder); - encoder.modRm_direct(modrm_ext, dst_reg.lowId()); + encoder.modRm_direct(modrm_ext, dst_reg.lowEnc()); encodeImm(encoder, imm, if (enc == .mi8) 8 else dst_reg.size()); }, .memory => |dst_mem| { @@ -2095,84 +2078,43 @@ fn lowerToRmEnc( reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8), ) InnerError!void { + assert(!tag.isAvx()); const opc = getOpCode(tag, .rm, reg.size() == 8 or reg_or_mem.size() == 8).?; switch (reg_or_mem) { .register => |src_reg| { - const encoder: Encoder = blk: { - switch (reg) { - .register => { - const encoder = try Encoder.init(code, 4); - if (reg.size() == 16) { - encoder.prefix16BitMode(); - } - encoder.rex(.{ - .w = setRexWRegister(reg) or setRexWRegister(src_reg), - .r = reg.isExtended(), - .b = src_reg.isExtended(), - }); - break :blk encoder; - }, - .avx_register => { - const encoder = try Encoder.init(code, 5); - var vex_prefix = getVexPrefix(tag, .rm).?; - const vex = &vex_prefix.prefix; - vex.rex(.{ - .r = reg.isExtended(), - .b = src_reg.isExtended(), - }); - encoder.vex(vex_prefix.prefix); - break :blk encoder; - }, - } - }; + const encoder = try Encoder.init(code, 5); + if (reg.size() == 16) { + encoder.prefix16BitMode(); + } + encoder.rex(.{ + .w = setRexWRegister(reg) or setRexWRegister(src_reg), + .r = reg.isExtended(), + .b = src_reg.isExtended(), + }); opc.encode(encoder); - encoder.modRm_direct(reg.lowId(), src_reg.lowId()); + encoder.modRm_direct(reg.lowEnc(), src_reg.lowEnc()); }, .memory => |src_mem| { - const encoder: Encoder = blk: { - switch (reg) { - .register => { - const encoder = try Encoder.init(code, 9); - if (reg.size() == 16) { - encoder.prefix16BitMode(); - } - if (src_mem.base) |base| { - // TODO handle 32-bit base register - requires prefix 0x67 - // Intel Manual, Vol 1, chapter 3.6 and 3.6.1 - encoder.rex(.{ - .w = setRexWRegister(reg), - .r = reg.isExtended(), - .b = base.isExtended(), - }); - } else { - encoder.rex(.{ - .w = setRexWRegister(reg), - .r = reg.isExtended(), - }); - } - break :blk encoder; - }, - .avx_register => { - const encoder = try Encoder.init(code, 10); - var vex_prefix = getVexPrefix(tag, .rm).?; - const vex = &vex_prefix.prefix; - if (src_mem.base) |base| { - vex.rex(.{ - .r = reg.isExtended(), - .b = base.isExtended(), - }); - } else { - vex.rex(.{ - .r = reg.isExtended(), - }); - } - encoder.vex(vex_prefix.prefix); - break :blk encoder; - }, - } - }; + const encoder = try Encoder.init(code, 9); + if (reg.size() == 16) { + encoder.prefix16BitMode(); + } + if (src_mem.base) |base| { + // TODO handle 32-bit base register - requires prefix 0x67 + // Intel Manual, Vol 1, chapter 3.6 and 3.6.1 + encoder.rex(.{ + .w = setRexWRegister(reg), + .r = reg.isExtended(), + .b = base.isExtended(), + }); + } else { + encoder.rex(.{ + .w = setRexWRegister(reg), + .r = reg.isExtended(), + }); + } opc.encode(encoder); - src_mem.encode(encoder, reg.lowId()); + src_mem.encode(encoder, reg.lowEnc()); }, } } @@ -2183,6 +2125,7 @@ fn lowerToMrEnc( reg: Register, code: *std.ArrayList(u8), ) InnerError!void { + assert(!tag.isAvx()); const opc = getOpCode(tag, .mr, reg.size() == 8 or reg_or_mem.size() == 8).?; switch (reg_or_mem) { .register => |dst_reg| { @@ -2196,53 +2139,27 @@ fn lowerToMrEnc( .b = dst_reg.isExtended(), }); opc.encode(encoder); - encoder.modRm_direct(reg.lowId(), dst_reg.lowId()); + encoder.modRm_direct(reg.lowEnc(), dst_reg.lowEnc()); }, .memory => |dst_mem| { - const encoder: Encoder = blk: { - switch (reg) { - .register => { - const encoder = try Encoder.init(code, 9); - if (reg.size() == 16) { - encoder.prefix16BitMode(); - } - if (dst_mem.base) |base| { - encoder.rex(.{ - .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), - .r = reg.isExtended(), - .b = base.isExtended(), - }); - } else { - encoder.rex(.{ - .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), - .r = reg.isExtended(), - }); - } - break :blk encoder; - }, - .avx_register => { - const encoder = try Encoder.init(code, 10); - var vex_prefix = getVexPrefix(tag, .mr).?; - const vex = &vex_prefix.prefix; - if (dst_mem.base) |base| { - vex.rex(.{ - .w = dst_mem.ptr_size == .qword_ptr, - .r = reg.isExtended(), - .b = base.isExtended(), - }); - } else { - vex.rex(.{ - .w = dst_mem.ptr_size == .qword_ptr, - .r = reg.isExtended(), - }); - } - encoder.vex(vex_prefix.prefix); - break :blk encoder; - }, - } - }; + const encoder = try Encoder.init(code, 9); + if (reg.size() == 16) { + encoder.prefix16BitMode(); + } + if (dst_mem.base) |base| { + encoder.rex(.{ + .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), + .r = reg.isExtended(), + .b = base.isExtended(), + }); + } else { + encoder.rex(.{ + .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), + .r = reg.isExtended(), + }); + } opc.encode(encoder); - dst_mem.encode(encoder, reg.lowId()); + dst_mem.encode(encoder, reg.lowEnc()); }, } } @@ -2254,6 +2171,7 @@ fn lowerToRmiEnc( imm: u32, code: *std.ArrayList(u8), ) InnerError!void { + assert(!tag.isAvx()); const opc = getOpCode(tag, .rmi, false).?; const encoder = try Encoder.init(code, 13); if (reg.size() == 16) { @@ -2267,7 +2185,7 @@ fn lowerToRmiEnc( .b = src_reg.isExtended(), }); opc.encode(encoder); - encoder.modRm_direct(reg.lowId(), src_reg.lowId()); + encoder.modRm_direct(reg.lowEnc(), src_reg.lowEnc()); }, .memory => |src_mem| { if (src_mem.base) |base| { @@ -2285,12 +2203,94 @@ fn lowerToRmiEnc( }); } opc.encode(encoder); - src_mem.encode(encoder, reg.lowId()); + src_mem.encode(encoder, reg.lowEnc()); }, } encodeImm(encoder, imm, reg.size()); } +/// Also referred to as XM encoding in Intel manual. +fn lowerToVmEnc( + tag: Tag, + reg: Register, + reg_or_mem: RegisterOrMemory, + code: *std.ArrayList(u8), +) InnerError!void { + const opc = getOpCode(tag, .vm, false).?; + var vex_prefix = getVexPrefix(tag, .vm).?; + const vex = &vex_prefix.prefix; + switch (reg_or_mem) { + .register => |src_reg| { + const encoder = try Encoder.init(code, 5); + vex.rex(.{ + .r = reg.isExtended(), + .b = src_reg.isExtended(), + }); + encoder.vex(vex_prefix.prefix); + opc.encode(encoder); + encoder.modRm_direct(reg.lowEnc(), src_reg.lowEnc()); + }, + .memory => |src_mem| { + assert(src_mem.ptr_size == .qword_ptr); + const encoder = try Encoder.init(code, 10); + if (src_mem.base) |base| { + vex.rex(.{ + .r = reg.isExtended(), + .b = base.isExtended(), + }); + } else { + vex.rex(.{ + .r = reg.isExtended(), + }); + } + encoder.vex(vex_prefix.prefix); + opc.encode(encoder); + src_mem.encode(encoder, reg.lowEnc()); + }, + } +} + +/// Usually referred to as MR encoding with V/V in Intel manual. +fn lowerToMvEnc( + tag: Tag, + reg_or_mem: RegisterOrMemory, + reg: Register, + code: *std.ArrayList(u8), +) InnerError!void { + const opc = getOpCode(tag, .mv, false).?; + var vex_prefix = getVexPrefix(tag, .mv).?; + const vex = &vex_prefix.prefix; + switch (reg_or_mem) { + .register => |dst_reg| { + const encoder = try Encoder.init(code, 4); + vex.rex(.{ + .r = reg.isExtended(), + .b = dst_reg.isExtended(), + }); + encoder.vex(vex_prefix.prefix); + opc.encode(encoder); + encoder.modRm_direct(reg.lowEnc(), dst_reg.lowEnc()); + }, + .memory => |dst_mem| { + assert(dst_mem.ptr_size == .qword_ptr); + const encoder = try Encoder.init(code, 10); + if (dst_mem.base) |base| { + vex.rex(.{ + .r = reg.isExtended(), + .b = base.isExtended(), + }); + } else { + vex.rex(.{ + .r = reg.isExtended(), + }); + } + encoder.vex(vex_prefix.prefix); + opc.encode(encoder); + dst_mem.encode(encoder, reg.lowEnc()); + }, + } +} + fn lowerToRvmEnc( tag: Tag, reg1: Register, @@ -2305,7 +2305,7 @@ fn lowerToRvmEnc( .register => |reg3| { if (vex_prefix.reg) |vvvv| { switch (vvvv) { - .nds => vex.reg(reg2.avx_register.id()), + .nds => vex.reg(reg2.enc()), else => unreachable, // TODO } } @@ -2316,7 +2316,7 @@ fn lowerToRvmEnc( }); encoder.vex(vex_prefix.prefix); opc.encode(encoder); - encoder.modRm_direct(reg1.lowId(), reg3.lowId()); + encoder.modRm_direct(reg1.lowEnc(), reg3.lowEnc()); }, .memory => |dst_mem| { _ = dst_mem; @@ -2341,7 +2341,7 @@ fn lowerToRvmiEnc( .register => |reg3| { if (vex_prefix.reg) |vvvv| { switch (vvvv) { - .nds => vex.reg(reg2.avx_register.id()), + .nds => vex.reg(reg2.enc()), else => unreachable, // TODO } } @@ -2352,7 +2352,7 @@ fn lowerToRvmiEnc( }); encoder.vex(vex_prefix.prefix); opc.encode(encoder); - encoder.modRm_direct(reg1.lowId(), reg3.lowId()); + encoder.modRm_direct(reg1.lowEnc(), reg3.lowEnc()); break :blk encoder; }, .memory => |dst_mem| { @@ -2490,23 +2490,23 @@ test "lower MI encoding" { test "lower RM encoding" { var emit = TestEmit.init(); defer emit.deinit(); - try lowerToRmEnc(.mov, Register.reg(.rax), RegisterOrMemory.reg(.rbx), emit.code()); + try lowerToRmEnc(.mov, .rax, RegisterOrMemory.reg(.rbx), emit.code()); try expectEqualHexStrings("\x48\x8b\xc3", emit.lowered(), "mov rax, rbx"); - try lowerToRmEnc(.mov, Register.reg(.rax), RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0, .base = .r11 }), emit.code()); + try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0, .base = .r11 }), emit.code()); try expectEqualHexStrings("\x49\x8b\x03", emit.lowered(), "mov rax, qword ptr [r11 + 0]"); - try lowerToRmEnc(.add, Register.reg(.r11), RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000 }), emit.code()); + try lowerToRmEnc(.add, .r11, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000 }), emit.code()); try expectEqualHexStrings( "\x4C\x03\x1C\x25\x00\x00\x00\x10", emit.lowered(), "add r11, qword ptr [ds:0x10000000]", ); - try lowerToRmEnc(.add, Register.reg(.r12b), RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), emit.code()); + try lowerToRmEnc(.add, .r12b, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), emit.code()); try expectEqualHexStrings( "\x44\x02\x24\x25\x00\x00\x00\x10", emit.lowered(), "add r11b, byte ptr [ds:0x10000000]", ); - try lowerToRmEnc(.sub, Register.reg(.r11), RegisterOrMemory.mem(.qword_ptr, .{ + try lowerToRmEnc(.sub, .r11, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000, .base = .r13, }), emit.code()); @@ -2515,7 +2515,7 @@ test "lower RM encoding" { emit.lowered(), "sub r11, qword ptr [r13 + 0x10000000]", ); - try lowerToRmEnc(.sub, Register.reg(.r11), RegisterOrMemory.mem(.qword_ptr, .{ + try lowerToRmEnc(.sub, .r11, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000, .base = .r12, }), emit.code()); @@ -2524,14 +2524,14 @@ test "lower RM encoding" { emit.lowered(), "sub r11, qword ptr [r12 + 0x10000000]", ); - try lowerToRmEnc(.mov, Register.reg(.rax), RegisterOrMemory.mem(.qword_ptr, .{ + try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ .disp = @bitCast(u32, @as(i32, -4)), .base = .rbp, }), emit.code()); try expectEqualHexStrings("\x48\x8B\x45\xFC", emit.lowered(), "mov rax, qword ptr [rbp - 4]"); - try lowerToRmEnc(.lea, Register.reg(.rax), RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code()); + try lowerToRmEnc(.lea, .rax, RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code()); try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", emit.lowered(), "lea rax, [rip + 0x10]"); - try lowerToRmEnc(.mov, Register.reg(.rax), RegisterOrMemory.mem(.qword_ptr, .{ + try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ .disp = @bitCast(u32, @as(i32, -8)), .base = .rbp, .scale_index = .{ @@ -2540,7 +2540,7 @@ test "lower RM encoding" { }, }), emit.code()); try expectEqualHexStrings("\x48\x8B\x44\x0D\xF8", emit.lowered(), "mov rax, qword ptr [rbp + rcx*1 - 8]"); - try lowerToRmEnc(.mov, Register.reg(.eax), RegisterOrMemory.mem(.dword_ptr, .{ + try lowerToRmEnc(.mov, .eax, RegisterOrMemory.mem(.dword_ptr, .{ .disp = @bitCast(u32, @as(i32, -4)), .base = .rbp, .scale_index = .{ @@ -2549,7 +2549,7 @@ test "lower RM encoding" { }, }), emit.code()); try expectEqualHexStrings("\x8B\x44\x95\xFC", emit.lowered(), "mov eax, dword ptr [rbp + rdx*4 - 4]"); - try lowerToRmEnc(.mov, Register.reg(.rax), RegisterOrMemory.mem(.qword_ptr, .{ + try lowerToRmEnc(.mov, .rax, RegisterOrMemory.mem(.qword_ptr, .{ .disp = @bitCast(u32, @as(i32, -8)), .base = .rbp, .scale_index = .{ @@ -2558,7 +2558,7 @@ test "lower RM encoding" { }, }), emit.code()); try expectEqualHexStrings("\x48\x8B\x44\xCD\xF8", emit.lowered(), "mov rax, qword ptr [rbp + rcx*8 - 8]"); - try lowerToRmEnc(.mov, Register.reg(.r8b), RegisterOrMemory.mem(.byte_ptr, .{ + try lowerToRmEnc(.mov, .r8b, RegisterOrMemory.mem(.byte_ptr, .{ .disp = @bitCast(u32, @as(i32, -24)), .base = .rsi, .scale_index = .{ @@ -2567,7 +2567,7 @@ test "lower RM encoding" { }, }), emit.code()); try expectEqualHexStrings("\x44\x8A\x44\x0E\xE8", emit.lowered(), "mov r8b, byte ptr [rsi + rcx*1 - 24]"); - try lowerToRmEnc(.lea, Register.reg(.rsi), RegisterOrMemory.mem(.qword_ptr, .{ + try lowerToRmEnc(.lea, .rsi, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0, .base = .rbp, .scale_index = .{ @@ -2576,33 +2576,25 @@ test "lower RM encoding" { }, }), emit.code()); try expectEqualHexStrings("\x48\x8D\x74\x0D\x00", emit.lowered(), "lea rsi, qword ptr [rbp + rcx*1 + 0]"); - - // AVX extension tests - try lowerToRmEnc(.vmovsd, Register.avxReg(.xmm1), RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code()); - try expectEqualHexStrings( - "\xC5\xFB\x10\x0D\x10\x00\x00\x00", - emit.lowered(), - "vmovsd xmm1, qword ptr [rip + 0x10]", - ); } test "lower MR encoding" { var emit = TestEmit.init(); defer emit.deinit(); - try lowerToMrEnc(.mov, RegisterOrMemory.reg(.rax), Register.reg(.rbx), emit.code()); + try lowerToMrEnc(.mov, RegisterOrMemory.reg(.rax), .rbx, emit.code()); try expectEqualHexStrings("\x48\x89\xd8", emit.lowered(), "mov rax, rbx"); try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ .disp = @bitCast(u32, @as(i32, -4)), .base = .rbp, - }), Register.reg(.r11), emit.code()); + }), .r11, emit.code()); try expectEqualHexStrings("\x4c\x89\x5d\xfc", emit.lowered(), "mov qword ptr [rbp - 4], r11"); - try lowerToMrEnc(.add, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), Register.reg(.r12b), emit.code()); + try lowerToMrEnc(.add, RegisterOrMemory.mem(.byte_ptr, .{ .disp = 0x10000000 }), .r12b, emit.code()); try expectEqualHexStrings( "\x44\x00\x24\x25\x00\x00\x00\x10", emit.lowered(), "add byte ptr [ds:0x10000000], r12b", ); - try lowerToMrEnc(.add, RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0x10000000 }), Register.reg(.r12d), emit.code()); + try lowerToMrEnc(.add, RegisterOrMemory.mem(.dword_ptr, .{ .disp = 0x10000000 }), .r12d, emit.code()); try expectEqualHexStrings( "\x44\x01\x24\x25\x00\x00\x00\x10", emit.lowered(), @@ -2611,61 +2603,53 @@ test "lower MR encoding" { try lowerToMrEnc(.sub, RegisterOrMemory.mem(.qword_ptr, .{ .disp = 0x10000000, .base = .r11, - }), Register.reg(.r12), emit.code()); + }), .r12, emit.code()); try expectEqualHexStrings( "\x4D\x29\xA3\x00\x00\x00\x10", emit.lowered(), "sub qword ptr [r11 + 0x10000000], r12", ); - try lowerToMrEnc(.mov, RegisterOrMemory.rip(.qword_ptr, 0x10), Register.reg(.r12), emit.code()); + try lowerToMrEnc(.mov, RegisterOrMemory.rip(.qword_ptr, 0x10), .r12, emit.code()); try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", emit.lowered(), "mov qword ptr [rip + 0x10], r12"); - - // AVX extension tests - try lowerToMrEnc(.vmovsd, RegisterOrMemory.rip(.qword_ptr, 0x10), Register.avxReg(.xmm1), emit.code()); - try expectEqualHexStrings( - "\xC5\xFB\x11\x0D\x10\x00\x00\x00", - emit.lowered(), - "vmovsd qword ptr [rip + 0x10], xmm1", - ); } test "lower OI encoding" { var emit = TestEmit.init(); defer emit.deinit(); - try lowerToOiEnc(.mov, Register.reg(.rax), 0x1000000000000000, emit.code()); + try lowerToOiEnc(.mov, .rax, 0x1000000000000000, emit.code()); try expectEqualHexStrings( "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10", emit.lowered(), "movabs rax, 0x1000000000000000", ); - try lowerToOiEnc(.mov, Register.reg(.r11), 0x1000000000000000, emit.code()); + try lowerToOiEnc(.mov, .r11, 0x1000000000000000, emit.code()); try expectEqualHexStrings( "\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10", emit.lowered(), "movabs r11, 0x1000000000000000", ); - try lowerToOiEnc(.mov, Register.reg(.r11d), 0x10000000, emit.code()); + try lowerToOiEnc(.mov, .r11d, 0x10000000, emit.code()); try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", emit.lowered(), "mov r11d, 0x10000000"); - try lowerToOiEnc(.mov, Register.reg(.r11w), 0x1000, emit.code()); + try lowerToOiEnc(.mov, .r11w, 0x1000, emit.code()); try expectEqualHexStrings("\x66\x41\xBB\x00\x10", emit.lowered(), "mov r11w, 0x1000"); - try lowerToOiEnc(.mov, Register.reg(.r11b), 0x10, emit.code()); + try lowerToOiEnc(.mov, .r11b, 0x10, emit.code()); try expectEqualHexStrings("\x41\xB3\x10", emit.lowered(), "mov r11b, 0x10"); } test "lower FD/TD encoding" { var emit = TestEmit.init(); defer emit.deinit(); - try lowerToFdEnc(.mov, Register.reg(.rax), 0x1000000000000000, emit.code()); + try lowerToFdEnc(.mov, .rax, 0x1000000000000000, emit.code()); try expectEqualHexStrings( "\x48\xa1\x00\x00\x00\x00\x00\x00\x00\x10", emit.lowered(), "mov rax, ds:0x1000000000000000", ); - try lowerToFdEnc(.mov, Register.reg(.eax), 0x10000000, emit.code()); + try lowerToFdEnc(.mov, .eax, 0x10000000, emit.code()); try expectEqualHexStrings("\xa1\x00\x00\x00\x10", emit.lowered(), "mov eax, ds:0x10000000"); - try lowerToFdEnc(.mov, Register.reg(.ax), 0x1000, emit.code()); + try lowerToFdEnc(.mov, .ax, 0x1000, emit.code()); try expectEqualHexStrings("\x66\xa1\x00\x10", emit.lowered(), "mov ax, ds:0x1000"); - try lowerToFdEnc(.mov, Register.reg(.al), 0x10, emit.code()); + try lowerToFdEnc(.mov, .al, 0x10, emit.code()); try expectEqualHexStrings("\xa0\x10", emit.lowered(), "mov al, ds:0x10"); } @@ -2741,16 +2725,16 @@ test "lower M1 and MC encodings" { test "lower O encoding" { var emit = TestEmit.init(); defer emit.deinit(); - try lowerToOEnc(.pop, Register.reg(.r12), emit.code()); + try lowerToOEnc(.pop, .r12, emit.code()); try expectEqualHexStrings("\x41\x5c", emit.lowered(), "pop r12"); - try lowerToOEnc(.push, Register.reg(.r12w), emit.code()); + try lowerToOEnc(.push, .r12w, emit.code()); try expectEqualHexStrings("\x66\x41\x54", emit.lowered(), "push r12w"); } test "lower RMI encoding" { var emit = TestEmit.init(); defer emit.deinit(); - try lowerToRmiEnc(.imul, Register.reg(.rax), RegisterOrMemory.mem(.qword_ptr, .{ + try lowerToRmiEnc(.imul, .rax, RegisterOrMemory.mem(.qword_ptr, .{ .disp = @bitCast(u32, @as(i32, -8)), .base = .rbp, }), 0x10, emit.code()); @@ -2759,39 +2743,49 @@ test "lower RMI encoding" { emit.lowered(), "imul rax, qword ptr [rbp - 8], 0x10", ); - try lowerToRmiEnc(.imul, Register.reg(.eax), RegisterOrMemory.mem(.dword_ptr, .{ + try lowerToRmiEnc(.imul, .eax, RegisterOrMemory.mem(.dword_ptr, .{ .disp = @bitCast(u32, @as(i32, -4)), .base = .rbp, }), 0x10, emit.code()); try expectEqualHexStrings("\x69\x45\xFC\x10\x00\x00\x00", emit.lowered(), "imul eax, dword ptr [rbp - 4], 0x10"); - try lowerToRmiEnc(.imul, Register.reg(.ax), RegisterOrMemory.mem(.word_ptr, .{ + try lowerToRmiEnc(.imul, .ax, RegisterOrMemory.mem(.word_ptr, .{ .disp = @bitCast(u32, @as(i32, -2)), .base = .rbp, }), 0x10, emit.code()); try expectEqualHexStrings("\x66\x69\x45\xFE\x10\x00", emit.lowered(), "imul ax, word ptr [rbp - 2], 0x10"); - try lowerToRmiEnc(.imul, Register.reg(.r12), RegisterOrMemory.reg(.r12), 0x10, emit.code()); + try lowerToRmiEnc(.imul, .r12, RegisterOrMemory.reg(.r12), 0x10, emit.code()); try expectEqualHexStrings("\x4D\x69\xE4\x10\x00\x00\x00", emit.lowered(), "imul r12, r12, 0x10"); - try lowerToRmiEnc(.imul, Register.reg(.r12w), RegisterOrMemory.reg(.r12w), 0x10, emit.code()); + try lowerToRmiEnc(.imul, .r12w, RegisterOrMemory.reg(.r12w), 0x10, emit.code()); try expectEqualHexStrings("\x66\x45\x69\xE4\x10\x00", emit.lowered(), "imul r12w, r12w, 0x10"); } +test "lower MV encoding" { + var emit = TestEmit.init(); + defer emit.deinit(); + try lowerToMvEnc(.vmovsd, RegisterOrMemory.rip(.qword_ptr, 0x10), .xmm1, emit.code()); + try expectEqualHexStrings( + "\xC5\xFB\x11\x0D\x10\x00\x00\x00", + emit.lowered(), + "vmovsd qword ptr [rip + 0x10], xmm1", + ); +} + +test "lower VM encoding" { + var emit = TestEmit.init(); + defer emit.deinit(); + try lowerToVmEnc(.vmovsd, .xmm1, RegisterOrMemory.rip(.qword_ptr, 0x10), emit.code()); + try expectEqualHexStrings( + "\xC5\xFB\x10\x0D\x10\x00\x00\x00", + emit.lowered(), + "vmovsd xmm1, qword ptr [rip + 0x10]", + ); +} + test "lower to RVM encoding" { var emit = TestEmit.init(); defer emit.deinit(); - try lowerToRvmEnc( - .vaddsd, - Register.avxReg(.xmm0), - Register.avxReg(.xmm1), - RegisterOrMemory.avxReg(.xmm2), - emit.code(), - ); + try lowerToRvmEnc(.vaddsd, .xmm0, .xmm1, RegisterOrMemory.reg(.xmm2), emit.code()); try expectEqualHexStrings("\xC5\xF3\x58\xC2", emit.lowered(), "vaddsd xmm0, xmm1, xmm2"); - try lowerToRvmEnc( - .vaddsd, - Register.avxReg(.xmm0), - Register.avxReg(.xmm0), - RegisterOrMemory.avxReg(.xmm1), - emit.code(), - ); + try lowerToRvmEnc(.vaddsd, .xmm0, .xmm0, RegisterOrMemory.reg(.xmm1), emit.code()); try expectEqualHexStrings("\xC5\xFB\x58\xC1", emit.lowered(), "vaddsd xmm0, xmm0, xmm1"); } diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index ef50279d03..a1062ba6b4 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -14,8 +14,7 @@ const assert = std.debug.assert; const bits = @import("bits.zig"); const Air = @import("../../Air.zig"); const CodeGen = @import("CodeGen.zig"); -const GpRegister = bits.Register; -const AvxRegister = bits.AvxRegister; +const Register = bits.Register; instructions: std.MultiArrayList(Inst).Slice, /// The meaning of this data is determined by `Inst.Tag` value. @@ -23,11 +22,7 @@ extra: []const u32, pub const Inst = struct { tag: Tag, - /// This is 3 fields, and the meaning of each depends on `tag`. - /// reg1: Register - /// reg2: Register - /// flags: u2 - ops: u16, + ops: Ops, /// The meaning of this depends on `tag` and `ops`. data: Data, @@ -397,6 +392,36 @@ pub const Inst = struct { /// The position of an MIR instruction within the `Mir` instructions array. pub const Index = u32; + pub const Ops = packed struct { + reg1: u7, + reg2: u7, + flags: u2, + + pub fn encode(vals: struct { + reg1: Register = .none, + reg2: Register = .none, + flags: u2 = 0b00, + }) Ops { + return .{ + .reg1 = @enumToInt(vals.reg1), + .reg2 = @enumToInt(vals.reg2), + .flags = vals.flags, + }; + } + + pub fn decode(ops: Ops) struct { + reg1: Register, + reg2: Register, + flags: u2, + } { + return .{ + .reg1 = @intToEnum(Register, ops.reg1), + .reg2 = @intToEnum(Register, ops.reg2), + .flags = ops.flags, + }; + } + }; + /// All instructions have a 4-byte payload, which is contained within /// this union. `Tag` determines which union field is active, as well as /// how to interpret the data within. @@ -466,33 +491,6 @@ pub const DbgLineColumn = struct { column: u32, }; -pub fn Ops(comptime Reg1: type, comptime Reg2: type) type { - return struct { - reg1: Reg1 = .none, - reg2: Reg2 = .none, - flags: u2 = 0b00, - - pub fn encode(self: @This()) u16 { - var ops: u16 = 0; - ops |= @intCast(u16, @enumToInt(self.reg1)) << 9; - ops |= @intCast(u16, @enumToInt(self.reg2)) << 2; - ops |= self.flags; - return ops; - } - - pub fn decode(ops: u16) @This() { - const reg1 = @intToEnum(Reg1, @truncate(u7, ops >> 9)); - const reg2 = @intToEnum(Reg2, @truncate(u7, ops >> 2)); - const flags = @truncate(u2, ops); - return .{ - .reg1 = reg1, - .reg2 = reg2, - .flags = flags, - }; - } - }; -} - pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { mir.instructions.deinit(gpa); gpa.free(mir.extra); diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index a53c82530d..85bf3a0790 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -3,7 +3,6 @@ const Type = @import("../../type.zig").Type; const Target = std.Target; const assert = std.debug.assert; const Register = @import("bits.zig").Register; -const AvxRegister = @import("bits.zig").AvxRegister; pub const Class = enum { integer, sse, sseup, x87, x87up, complex_x87, memory, none }; @@ -379,11 +378,17 @@ pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 }; /// the caller relinquishes control to a subroutine via call instruction (or similar). /// In other words, these registers are free to use by the callee. pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; -pub const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs; -pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; -pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; - -pub const avx_regs = [_]AvxRegister{ +pub const avx_regs = [_]Register{ .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7, .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15, }; +pub const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ avx_regs; + +// Masks for register manager +const FreeRegInt = std.meta.Int(.unsigned, allocatable_registers.len); +// TODO +pub const gp_mask: FreeRegInt = 0x3fff; +pub const avx_mask: FreeRegInt = 0x3fff_c000; + +pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; +pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 0bba295c20..85bd190e2b 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -43,17 +43,36 @@ pub const Register = enum(u7) { al, cl, dl, bl, ah, ch, dh, bh, r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b, - // Pseudo, used only for MIR to signify that the - // operand is not a register but an immediate, etc. + // 64-79, 256-bit registers. + // id is int value - 64. + ymm0, ymm1, ymm2, ymm3, ymm4, ymm5, ymm6, ymm7, + ymm8, ymm9, ymm10, ymm11, ymm12, ymm13, ymm14, ymm15, + + // 80-95, 128-bit registers. + // id is int value - 80. + xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, + xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, + + // Pseudo-value for MIR instructions. none, + pub fn id(self: Register) u5 { + return switch (@enumToInt(self)) { + 0...63 => @as(u5, @truncate(u4, @enumToInt(self))), + 64...79 => @truncate(u5, @enumToInt(self)), + else => unreachable, + }; + } + /// Returns the bit-width of the register. - pub fn size(self: Register) u7 { + pub fn size(self: Register) u9 { return switch (@enumToInt(self)) { 0...15 => 64, 16...31 => 32, 32...47 => 16, - 48...64 => 8, + 48...63 => 8, + 64...79 => 256, + 80...95 => 128, else => unreachable, }; } @@ -72,15 +91,23 @@ pub const Register = enum(u7) { /// an instruction (@see isExtended), and requires special handling. The /// lower three bits are often embedded directly in instructions (such as /// the B8 variant of moves), or used in R/M bytes. - pub fn id(self: Register) u4 { + pub fn enc(self: Register) u4 { return @truncate(u4, @enumToInt(self)); } - /// Like id, but only returns the lower 3 bits. - pub fn lowId(self: Register) u3 { + /// Like enc, but only returns the lower 3 bits. + pub fn lowEnc(self: Register) u3 { return @truncate(u3, @enumToInt(self)); } + pub fn to256(self: Register) Register { + return @intToEnum(Register, @as(u8, self.id()) + 64); + } + + pub fn to128(self: Register) Register { + return @intToEnum(Register, @as(u8, self.id()) + 80); + } + /// Convert from any register to its 64 bit alias. pub fn to64(self: Register) Register { return @intToEnum(Register, self.id()); @@ -126,57 +153,6 @@ pub const Register = enum(u7) { } }; -/// AVX registers. -/// TODO missing dwarfLocOp implementation. -/// TODO add support for AVX-512 -pub const AvxRegister = enum(u6) { - // 256-bit registers - ymm0, ymm1, ymm2, ymm3, ymm4, ymm5, ymm6, ymm7, - ymm8, ymm9, ymm10, ymm11, ymm12, ymm13, ymm14, ymm15, - - // 128-bit registers - xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, - xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, - - // Pseudo, used only for MIR to signify that the - // operand is not a register but an immediate, etc. - none, - - /// Returns the bit-width of the register. - pub fn size(self: AvxRegister) u9 { - return switch (@enumToInt(self)) { - 0...15 => 256, - 16...31 => 128, - else => unreachable, - }; - } - - /// Returns whether the register is *extended*. - pub fn isExtended(self: AvxRegister) bool { - return @enumToInt(self) & 0x08 != 0; - } - - /// This returns the 4-bit register ID. - pub fn id(self: AvxRegister) u4 { - return @truncate(u4, @enumToInt(self)); - } - - /// Like id, but only returns the lower 3 bits. - pub fn lowId(self: AvxRegister) u3 { - return @truncate(u3, @enumToInt(self)); - } - - /// Convert from any register to its 256 bit alias. - pub fn to256(self: AvxRegister) AvxRegister { - return @intToEnum(AvxRegister, self.id()); - } - - /// Convert from any register to its 128 bit alias. - pub fn to128(self: AvxRegister) AvxRegister { - return @intToEnum(AvxRegister, @as(u8, self.id()) + 16); - } -}; - // zig fmt: on /// Encoding helper functions for x86_64 instructions @@ -792,7 +768,7 @@ test "Encoder helpers - Vex prefix" { { stream.reset(); var vex_prefix = Encoder.Vex{}; - vex_prefix.reg(AvxRegister.xmm15.id()); + vex_prefix.reg(Register.xmm15.id()); const nwritten = vex_prefix.write(writer); try testing.expectEqualSlices(u8, &[_]u8{ 0xc5, 0x80 }, buf[0..nwritten]); } @@ -832,7 +808,7 @@ test "Encoder helpers - Vex prefix" { vex.simd_prefix_66(); encoder.vex(vex); // use 64 bit operation encoder.opcode_1byte(0x28); - encoder.modRm_direct(0, AvxRegister.xmm1.lowId()); + encoder.modRm_direct(0, Register.xmm1.lowId()); try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0xF9, 0x28, 0xC1 }, code.items); } @@ -846,10 +822,10 @@ test "Encoder helpers - Vex prefix" { vex.simd_prefix_66(); vex.lead_opc_0f(); vex.rex(.{ .r = true }); - vex.reg(AvxRegister.xmm1.id()); + vex.reg(Register.xmm1.id()); encoder.vex(vex); encoder.opcode_1byte(0x16); - encoder.modRm_RIPDisp32(AvxRegister.xmm13.lowId()); + encoder.modRm_RIPDisp32(Register.xmm13.lowId()); encoder.disp32(0); try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0x71, 0x16, 0x2D, 0x00, 0x00, 0x00, 0x00 }, code.items); } diff --git a/src/codegen.zig b/src/codegen.zig index c14fb39629..def69d952f 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -78,13 +78,13 @@ pub fn generateFunction( debug_output: DebugInfoOutput, ) GenerateSymbolError!FnResult { switch (bin_file.options.target.cpu.arch) { - // .arm, - // .armeb, - // => return @import("arch/arm/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), - // .aarch64, - // .aarch64_be, - // .aarch64_32, - // => return @import("arch/aarch64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), + .arm, + .armeb, + => return @import("arch/arm/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), + .aarch64, + .aarch64_be, + .aarch64_32, + => return @import("arch/aarch64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.arc => return Function(.arc).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.avr => return Function(.avr).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.bpfel => return Function(.bpfel).generate(bin_file, src_loc, func, air, liveness, code, debug_output), @@ -101,9 +101,9 @@ pub fn generateFunction( //.r600 => return Function(.r600).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.amdgcn => return Function(.amdgcn).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.riscv32 => return Function(.riscv32).generate(bin_file, src_loc, func, air, liveness, code, debug_output), - // .riscv64 => return @import("arch/riscv64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), + .riscv64 => return @import("arch/riscv64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.sparc => return Function(.sparc).generate(bin_file, src_loc, func, air, liveness, code, debug_output), - // .sparc64 => return @import("arch/sparc64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), + .sparc64 => return @import("arch/sparc64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.sparcel => return Function(.sparcel).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.s390x => return Function(.s390x).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.tce => return Function(.tce).generate(bin_file, src_loc, func, air, liveness, code, debug_output), @@ -129,9 +129,9 @@ pub fn generateFunction( //.renderscript32 => return Function(.renderscript32).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.renderscript64 => return Function(.renderscript64).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.ve => return Function(.ve).generate(bin_file, src_loc, func, air, liveness, code, debug_output), - // .wasm32, - // .wasm64, - // => return @import("arch/wasm/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), + .wasm32, + .wasm64, + => return @import("arch/wasm/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."), } } diff --git a/src/register_manager.zig b/src/register_manager.zig index 6fc3641467..2c0502e867 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -23,15 +23,10 @@ pub const AllocateRegistersError = error{ CodegenFail, }; -pub fn SpillFn(comptime Function: type, comptime Register: type) type { - return fn (*Function, Register, Air.Inst.Index) anyerror!void; -} - pub fn RegisterManager( comptime Function: type, comptime Register: type, comptime tracked_registers: []const Register, - comptime spill_fn: SpillFn(Function, Register), ) type { // architectures which do not have a concept of registers should // refrain from using RegisterManager @@ -52,7 +47,6 @@ pub fn RegisterManager( allocated_registers: FreeRegInt = 0, /// Tracks registers which are locked from being allocated locked_registers: FreeRegInt = 0, - function: *Function, const Self = @This(); @@ -61,8 +55,8 @@ pub fn RegisterManager( const FreeRegInt = std.meta.Int(.unsigned, tracked_registers.len); const ShiftInt = math.Log2Int(FreeRegInt); - fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) AllocateRegistersError!void { - return try spill_fn(self.function, reg, inst); + fn getFunction(self: *Self) *Function { + return @fieldParentPtr(Function, "register_manager", self); } fn getRegisterMask(reg: Register) ?FreeRegInt { @@ -257,14 +251,14 @@ pub fn RegisterManager( self.markRegUsed(reg); } else { const spilled_inst = self.registers[index]; - try self.spillInstruction(reg, spilled_inst); + try self.getFunction().spillInstruction(reg, spilled_inst); } self.registers[index] = inst; } else { // Don't track the register if (!self.isRegFree(reg)) { const spilled_inst = self.registers[index]; - try self.spillInstruction(reg, spilled_inst); + try self.getFunction().spillInstruction(reg, spilled_inst); self.freeReg(reg); } } @@ -299,7 +293,7 @@ pub fn RegisterManager( // stack allocation. const spilled_inst = self.registers[index]; self.registers[index] = tracked_inst; - try self.spillInstruction(reg, spilled_inst); + try self.getFunction().spillInstruction(reg, spilled_inst); } else { self.getRegAssumeFree(reg, tracked_inst); } @@ -308,7 +302,7 @@ pub fn RegisterManager( // Move the instruction that was previously there to a // stack allocation. const spilled_inst = self.registers[index]; - try self.spillInstruction(reg, spilled_inst); + try self.getFunction().spillInstruction(reg, spilled_inst); self.freeReg(reg); } } @@ -338,253 +332,253 @@ pub fn RegisterManager( }; } -//const MockRegister1 = enum(u2) { -// r0, -// r1, -// r2, -// r3, +const MockRegister1 = enum(u2) { + r0, + r1, + r2, + r3, -// pub fn id(reg: MockRegister1) u2 { -// return @enumToInt(reg); -// } + pub fn id(reg: MockRegister1) u2 { + return @enumToInt(reg); + } -// const allocatable_registers = [_]MockRegister1{ .r2, .r3 }; -//}; + const allocatable_registers = [_]MockRegister1{ .r2, .r3 }; +}; -//const MockRegister2 = enum(u2) { -// r0, -// r1, -// r2, -// r3, +const MockRegister2 = enum(u2) { + r0, + r1, + r2, + r3, -// pub fn id(reg: MockRegister2) u2 { -// return @enumToInt(reg); -// } + pub fn id(reg: MockRegister2) u2 { + return @enumToInt(reg); + } -// const allocatable_registers = [_]MockRegister2{ .r0, .r1, .r2, .r3 }; -//}; + const allocatable_registers = [_]MockRegister2{ .r0, .r1, .r2, .r3 }; +}; -//fn MockFunction(comptime Register: type) type { -// return struct { -// allocator: Allocator, -// register_manager: RegisterManager(Self, Register, &Register.allocatable_registers) = .{}, -// spilled: std.ArrayListUnmanaged(Register) = .{}, +fn MockFunction(comptime Register: type) type { + return struct { + allocator: Allocator, + register_manager: RegisterManager(Self, Register, &Register.allocatable_registers) = .{}, + spilled: std.ArrayListUnmanaged(Register) = .{}, -// const Self = @This(); + const Self = @This(); -// pub fn deinit(self: *Self) void { -// self.spilled.deinit(self.allocator); -// } + pub fn deinit(self: *Self) void { + self.spilled.deinit(self.allocator); + } -// pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { -// _ = inst; -// try self.spilled.append(self.allocator, reg); -// } + pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { + _ = inst; + try self.spilled.append(self.allocator, reg); + } -// pub fn genAdd(self: *Self, res: Register, lhs: Register, rhs: Register) !void { -// _ = self; -// _ = res; -// _ = lhs; -// _ = rhs; -// } -// }; -//} + pub fn genAdd(self: *Self, res: Register, lhs: Register, rhs: Register) !void { + _ = self; + _ = res; + _ = lhs; + _ = rhs; + } + }; +} -//const MockFunction1 = MockFunction(MockRegister1); -//const MockFunction2 = MockFunction(MockRegister2); +const MockFunction1 = MockFunction(MockRegister1); +const MockFunction2 = MockFunction(MockRegister2); -//test "default state" { -// const allocator = std.testing.allocator; +test "default state" { + const allocator = std.testing.allocator; -// var function = MockFunction1{ -// .allocator = allocator, -// }; -// defer function.deinit(); + var function = MockFunction1{ + .allocator = allocator, + }; + defer function.deinit(); -// try expect(!function.register_manager.isRegAllocated(.r2)); -// try expect(!function.register_manager.isRegAllocated(.r3)); -// try expect(function.register_manager.isRegFree(.r2)); -// try expect(function.register_manager.isRegFree(.r3)); -//} + try expect(!function.register_manager.isRegAllocated(.r2)); + try expect(!function.register_manager.isRegAllocated(.r3)); + try expect(function.register_manager.isRegFree(.r2)); + try expect(function.register_manager.isRegFree(.r3)); +} -//test "tryAllocReg: no spilling" { -// const allocator = std.testing.allocator; +test "tryAllocReg: no spilling" { + const allocator = std.testing.allocator; -// var function = MockFunction1{ -// .allocator = allocator, -// }; -// defer function.deinit(); + var function = MockFunction1{ + .allocator = allocator, + }; + defer function.deinit(); -// const mock_instruction: Air.Inst.Index = 1; + const mock_instruction: Air.Inst.Index = 1; -// try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(mock_instruction)); -// try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(mock_instruction)); -// try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(mock_instruction)); + try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(mock_instruction)); + try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(mock_instruction)); + try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(mock_instruction)); -// try expect(function.register_manager.isRegAllocated(.r2)); -// try expect(function.register_manager.isRegAllocated(.r3)); -// try expect(!function.register_manager.isRegFree(.r2)); -// try expect(!function.register_manager.isRegFree(.r3)); + try expect(function.register_manager.isRegAllocated(.r2)); + try expect(function.register_manager.isRegAllocated(.r3)); + try expect(!function.register_manager.isRegFree(.r2)); + try expect(!function.register_manager.isRegFree(.r3)); -// function.register_manager.freeReg(.r2); -// function.register_manager.freeReg(.r3); + function.register_manager.freeReg(.r2); + function.register_manager.freeReg(.r3); -// try expect(function.register_manager.isRegAllocated(.r2)); -// try expect(function.register_manager.isRegAllocated(.r3)); -// try expect(function.register_manager.isRegFree(.r2)); -// try expect(function.register_manager.isRegFree(.r3)); -//} + try expect(function.register_manager.isRegAllocated(.r2)); + try expect(function.register_manager.isRegAllocated(.r3)); + try expect(function.register_manager.isRegFree(.r2)); + try expect(function.register_manager.isRegFree(.r3)); +} -//test "allocReg: spilling" { -// const allocator = std.testing.allocator; +test "allocReg: spilling" { + const allocator = std.testing.allocator; -// var function = MockFunction1{ -// .allocator = allocator, -// }; -// defer function.deinit(); + var function = MockFunction1{ + .allocator = allocator, + }; + defer function.deinit(); -// const mock_instruction: Air.Inst.Index = 1; + const mock_instruction: Air.Inst.Index = 1; -// try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction)); -// try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); + try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction)); + try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); -// // Spill a register -// try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction)); -// try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); + // Spill a register + try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction)); + try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); -// // No spilling necessary -// function.register_manager.freeReg(.r3); -// try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); -// try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); + // No spilling necessary + function.register_manager.freeReg(.r3); + try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); + try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); -// // Locked registers -// function.register_manager.freeReg(.r3); -// { -// const lock = function.register_manager.lockReg(.r2); -// defer if (lock) |reg| function.register_manager.unlockReg(reg); + // Locked registers + function.register_manager.freeReg(.r3); + { + const lock = function.register_manager.lockReg(.r2); + defer if (lock) |reg| function.register_manager.unlockReg(reg); -// try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); -// } -// try expect(!function.register_manager.lockedRegsExist()); -//} + try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); + } + try expect(!function.register_manager.lockedRegsExist()); +} -//test "tryAllocRegs" { -// const allocator = std.testing.allocator; +test "tryAllocRegs" { + const allocator = std.testing.allocator; -// var function = MockFunction2{ -// .allocator = allocator, -// }; -// defer function.deinit(); + var function = MockFunction2{ + .allocator = allocator, + }; + defer function.deinit(); -// try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?); + try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?); -// try expect(function.register_manager.isRegAllocated(.r0)); -// try expect(function.register_manager.isRegAllocated(.r1)); -// try expect(function.register_manager.isRegAllocated(.r2)); -// try expect(!function.register_manager.isRegAllocated(.r3)); + try expect(function.register_manager.isRegAllocated(.r0)); + try expect(function.register_manager.isRegAllocated(.r1)); + try expect(function.register_manager.isRegAllocated(.r2)); + try expect(!function.register_manager.isRegAllocated(.r3)); -// // Locked registers -// function.register_manager.freeReg(.r0); -// function.register_manager.freeReg(.r2); -// function.register_manager.freeReg(.r3); -// { -// const lock = function.register_manager.lockReg(.r1); -// defer if (lock) |reg| function.register_manager.unlockReg(reg); + // Locked registers + function.register_manager.freeReg(.r0); + function.register_manager.freeReg(.r2); + function.register_manager.freeReg(.r3); + { + const lock = function.register_manager.lockReg(.r1); + defer if (lock) |reg| function.register_manager.unlockReg(reg); -// try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?); -// } -// try expect(!function.register_manager.lockedRegsExist()); + try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?); + } + try expect(!function.register_manager.lockedRegsExist()); -// try expect(function.register_manager.isRegAllocated(.r0)); -// try expect(function.register_manager.isRegAllocated(.r1)); -// try expect(function.register_manager.isRegAllocated(.r2)); -// try expect(function.register_manager.isRegAllocated(.r3)); -//} + try expect(function.register_manager.isRegAllocated(.r0)); + try expect(function.register_manager.isRegAllocated(.r1)); + try expect(function.register_manager.isRegAllocated(.r2)); + try expect(function.register_manager.isRegAllocated(.r3)); +} -//test "allocRegs: normal usage" { -// // TODO: convert this into a decltest once that is supported +test "allocRegs: normal usage" { + // TODO: convert this into a decltest once that is supported -// const allocator = std.testing.allocator; + const allocator = std.testing.allocator; -// var function = MockFunction2{ -// .allocator = allocator, -// }; -// defer function.deinit(); + var function = MockFunction2{ + .allocator = allocator, + }; + defer function.deinit(); -// { -// const result_reg: MockRegister2 = .r1; + { + const result_reg: MockRegister2 = .r1; -// // The result register is known and fixed at this point, we -// // don't want to accidentally allocate lhs or rhs to the -// // result register, this is why we lock it. -// // -// // Using defer unlock right after lock is a good idea in -// // most cases as you probably are using the locked registers -// // in the remainder of this scope and don't need to use it -// // after the end of this scope. However, in some situations, -// // it may make sense to manually unlock registers before the -// // end of the scope when you are certain that they don't -// // contain any valuable data anymore and can be reused. For an -// // example of that, see `selectively reducing register -// // pressure`. -// const lock = function.register_manager.lockReg(result_reg); -// defer if (lock) |reg| function.register_manager.unlockReg(reg); + // The result register is known and fixed at this point, we + // don't want to accidentally allocate lhs or rhs to the + // result register, this is why we lock it. + // + // Using defer unlock right after lock is a good idea in + // most cases as you probably are using the locked registers + // in the remainder of this scope and don't need to use it + // after the end of this scope. However, in some situations, + // it may make sense to manually unlock registers before the + // end of the scope when you are certain that they don't + // contain any valuable data anymore and can be reused. For an + // example of that, see `selectively reducing register + // pressure`. + const lock = function.register_manager.lockReg(result_reg); + defer if (lock) |reg| function.register_manager.unlockReg(reg); -// const regs = try function.register_manager.allocRegs(2, .{ null, null }); -// try function.genAdd(result_reg, regs[0], regs[1]); -// } -//} + const regs = try function.register_manager.allocRegs(2, .{ null, null }); + try function.genAdd(result_reg, regs[0], regs[1]); + } +} -//test "allocRegs: selectively reducing register pressure" { -// // TODO: convert this into a decltest once that is supported +test "allocRegs: selectively reducing register pressure" { + // TODO: convert this into a decltest once that is supported -// const allocator = std.testing.allocator; + const allocator = std.testing.allocator; -// var function = MockFunction2{ -// .allocator = allocator, -// }; -// defer function.deinit(); + var function = MockFunction2{ + .allocator = allocator, + }; + defer function.deinit(); -// { -// const result_reg: MockRegister2 = .r1; + { + const result_reg: MockRegister2 = .r1; -// const lock = function.register_manager.lockReg(result_reg); + const lock = function.register_manager.lockReg(result_reg); -// // Here, we don't defer unlock because we manually unlock -// // after genAdd -// const regs = try function.register_manager.allocRegs(2, .{ null, null }); + // Here, we don't defer unlock because we manually unlock + // after genAdd + const regs = try function.register_manager.allocRegs(2, .{ null, null }); -// try function.genAdd(result_reg, regs[0], regs[1]); -// function.register_manager.unlockReg(lock.?); + try function.genAdd(result_reg, regs[0], regs[1]); + function.register_manager.unlockReg(lock.?); -// const extra_summand_reg = try function.register_manager.allocReg(null); -// try function.genAdd(result_reg, result_reg, extra_summand_reg); -// } -//} + const extra_summand_reg = try function.register_manager.allocReg(null); + try function.genAdd(result_reg, result_reg, extra_summand_reg); + } +} -//test "getReg" { -// const allocator = std.testing.allocator; +test "getReg" { + const allocator = std.testing.allocator; -// var function = MockFunction1{ -// .allocator = allocator, -// }; -// defer function.deinit(); + var function = MockFunction1{ + .allocator = allocator, + }; + defer function.deinit(); -// const mock_instruction: Air.Inst.Index = 1; + const mock_instruction: Air.Inst.Index = 1; -// try function.register_manager.getReg(.r3, mock_instruction); + try function.register_manager.getReg(.r3, mock_instruction); -// try expect(!function.register_manager.isRegAllocated(.r2)); -// try expect(function.register_manager.isRegAllocated(.r3)); -// try expect(function.register_manager.isRegFree(.r2)); -// try expect(!function.register_manager.isRegFree(.r3)); + try expect(!function.register_manager.isRegAllocated(.r2)); + try expect(function.register_manager.isRegAllocated(.r3)); + try expect(function.register_manager.isRegFree(.r2)); + try expect(!function.register_manager.isRegFree(.r3)); -// // Spill r3 -// try function.register_manager.getReg(.r3, mock_instruction); + // Spill r3 + try function.register_manager.getReg(.r3, mock_instruction); -// try expect(!function.register_manager.isRegAllocated(.r2)); -// try expect(function.register_manager.isRegAllocated(.r3)); -// try expect(function.register_manager.isRegFree(.r2)); -// try expect(!function.register_manager.isRegFree(.r3)); -// try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r3}, function.spilled.items); -//} + try expect(!function.register_manager.isRegAllocated(.r2)); + try expect(function.register_manager.isRegAllocated(.r3)); + try expect(function.register_manager.isRegFree(.r2)); + try expect(!function.register_manager.isRegFree(.r3)); + try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r3}, function.spilled.items); +} From 549174f743d3b56544e2d3d6993f4d43bba1e7fd Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 18 May 2022 11:15:04 +0200 Subject: [PATCH 1579/2031] regalloc: allow for optional selector mask when allocating --- src/arch/aarch64/CodeGen.zig | 56 +++++++++++++++---------------- src/arch/arm/CodeGen.zig | 64 ++++++++++++++++++------------------ src/arch/riscv64/CodeGen.zig | 16 ++++----- src/arch/sparc64/CodeGen.zig | 26 +++++++-------- src/arch/x86_64/CodeGen.zig | 40 +++++++++++----------- src/register_manager.zig | 17 +++++++--- 6 files changed, 113 insertions(+), 106 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 0993d352d7..a3116587d4 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -888,7 +888,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { if (reg_ok) { // Make sure the type can fit in a register before we try to allocate one. if (abi_size <= 8) { - if (self.register_manager.tryAllocReg(inst)) |reg| { + if (self.register_manager.tryAllocReg(inst, .{})) |reg| { return MCValue{ .register = registerAlias(reg, abi_size) }; } } @@ -951,7 +951,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { - const raw_reg = try self.register_manager.allocReg(null); + const raw_reg = try self.register_manager.allocReg(null, .{}); const reg = registerAlias(raw_reg, ty.abiSize(self.target.*)); try self.genSetReg(ty, reg, mcv); return reg; @@ -961,7 +961,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { /// `reg_owner` is the instruction that gets associated with the register in the register table. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { - const raw_reg = try self.register_manager.allocReg(reg_owner); + const raw_reg = try self.register_manager.allocReg(reg_owner, .{}); const ty = self.air.typeOfIndex(reg_owner); const reg = registerAlias(raw_reg, ty.abiSize(self.target.*)); try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv); @@ -1074,11 +1074,11 @@ fn trunc( if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { break :blk registerAlias(operand_reg, dest_ty.abiSize(self.target.*)); } else { - const raw_reg = try self.register_manager.allocReg(inst); + const raw_reg = try self.register_manager.allocReg(inst, .{}); break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); } } else blk: { - const raw_reg = try self.register_manager.allocReg(null); + const raw_reg = try self.register_manager.allocReg(null, .{}); break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); }; @@ -1160,7 +1160,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { break :blk op_reg; } - const raw_reg = try self.register_manager.allocReg(null); + const raw_reg = try self.register_manager.allocReg(null, .{}); break :blk raw_reg.to32(); }; @@ -1193,7 +1193,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { break :blk op_reg; } - const raw_reg = try self.register_manager.allocReg(null); + const raw_reg = try self.register_manager.allocReg(null, .{}); break :blk registerAlias(raw_reg, operand_ty.abiSize(self.target.*)); }; @@ -1293,7 +1293,7 @@ fn binOpRegister( break :inst Air.refToIndex(md.lhs).?; } else null; - const raw_reg = try self.register_manager.allocReg(track_inst); + const raw_reg = try self.register_manager.allocReg(track_inst, .{}); const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -1308,7 +1308,7 @@ fn binOpRegister( break :inst Air.refToIndex(md.rhs).?; } else null; - const raw_reg = try self.register_manager.allocReg(track_inst); + const raw_reg = try self.register_manager.allocReg(track_inst, .{}); const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*)); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -1326,11 +1326,11 @@ fn binOpRegister( } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) { break :blk rhs_reg; } else { - const raw_reg = try self.register_manager.allocReg(md.inst); + const raw_reg = try self.register_manager.allocReg(md.inst, .{}); break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); } } else blk: { - const raw_reg = try self.register_manager.allocReg(null); + const raw_reg = try self.register_manager.allocReg(null, .{}); break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); }, }; @@ -1431,7 +1431,7 @@ fn binOpImmediate( ).?; } else null; - const raw_reg = try self.register_manager.allocReg(track_inst); + const raw_reg = try self.register_manager.allocReg(track_inst, .{}); const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -1452,11 +1452,11 @@ fn binOpImmediate( )) { break :blk lhs_reg; } else { - const raw_reg = try self.register_manager.allocReg(md.inst); + const raw_reg = try self.register_manager.allocReg(md.inst, .{}); break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); } } else blk: { - const raw_reg = try self.register_manager.allocReg(null); + const raw_reg = try self.register_manager.allocReg(null, .{}); break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); }, }; @@ -1872,7 +1872,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_reg_lock); - const raw_truncated_reg = try self.register_manager.allocReg(null); + const raw_truncated_reg = try self.register_manager.allocReg(null, .{}); const truncated_reg = registerAlias(raw_truncated_reg, lhs_ty.abiSize(self.target.*)); const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); defer self.register_manager.unlockReg(truncated_reg_lock); @@ -1983,7 +1983,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_reg_lock); - const truncated_reg = try self.register_manager.allocReg(null); + const truncated_reg = try self.register_manager.allocReg(null, .{}); const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); defer self.register_manager.unlockReg(truncated_reg_lock); @@ -2048,7 +2048,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); const lhs_reg = if (lhs_is_register) lhs.register else blk: { - const raw_reg = try self.register_manager.allocReg(null); + const raw_reg = try self.register_manager.allocReg(null, .{}); const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); break :blk reg; }; @@ -2056,7 +2056,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else blk: { - const raw_reg = try self.register_manager.allocReg(null); + const raw_reg = try self.register_manager.allocReg(null, .{}); const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*)); break :blk reg; }; @@ -2067,7 +2067,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); const dest_reg = blk: { - const raw_reg = try self.register_manager.allocReg(null); + const raw_reg = try self.register_manager.allocReg(null, .{}); const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); break :blk reg; }; @@ -2086,7 +2086,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { } }, }); - const dest_high_reg = try self.register_manager.allocReg(null); + const dest_high_reg = try self.register_manager.allocReg(null, .{}); const dest_high_reg_lock = self.register_manager.lockRegAssumeUnused(dest_high_reg); defer self.register_manager.unlockReg(dest_high_reg_lock); @@ -2136,7 +2136,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { } }, .unsigned => { - const dest_high_reg = try self.register_manager.allocReg(null); + const dest_high_reg = try self.register_manager.allocReg(null, .{}); const dest_high_reg_lock = self.register_manager.lockRegAssumeUnused(dest_high_reg); defer self.register_manager.unlockReg(dest_high_reg_lock); @@ -2192,7 +2192,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { }, } - const truncated_reg = try self.register_manager.allocReg(null); + const truncated_reg = try self.register_manager.allocReg(null, .{}); const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); defer self.register_manager.unlockReg(truncated_reg_lock); @@ -2663,7 +2663,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo }, .stack_offset => |off| { if (elem_size <= 8) { - const raw_tmp_reg = try self.register_manager.allocReg(null); + const raw_tmp_reg = try self.register_manager.allocReg(null, .{}); const tmp_reg = registerAlias(raw_tmp_reg, elem_size); const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); defer self.register_manager.unlockReg(tmp_reg_lock); @@ -2672,7 +2672,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); } else { // TODO optimize the register allocation - const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }); + const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, .{}); const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs); defer for (regs_locks) |reg| { self.register_manager.unlockReg(reg); @@ -2887,7 +2887,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type }, else => { if (abi_size <= 8) { - const raw_tmp_reg = try self.register_manager.allocReg(null); + const raw_tmp_reg = try self.register_manager.allocReg(null, .{}); const tmp_reg = registerAlias(raw_tmp_reg, abi_size); const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); defer self.register_manager.unlockReg(tmp_reg_lock); @@ -3002,7 +3002,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { // TODO return special MCValue condition flags // get overflow bit: set register to C flag // resp. V flag - const raw_dest_reg = try self.register_manager.allocReg(null); + const raw_dest_reg = try self.register_manager.allocReg(null, .{}); const dest_reg = raw_dest_reg.to32(); // C flag: cset reg, cs @@ -4065,7 +4065,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro const overflow_bit_ty = ty.structFieldType(1); const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*)); - const raw_cond_reg = try self.register_manager.allocReg(null); + const raw_cond_reg = try self.register_manager.allocReg(null, .{}); const cond_reg = registerAlias( raw_cond_reg, @intCast(u32, overflow_bit_ty.abiSize(self.target.*)), @@ -4113,7 +4113,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro const ptr_ty = Type.initPayload(&ptr_ty_payload.base); // TODO call extern memcpy - const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }); + const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, .{}); const regs_locks = self.register_manager.lockRegsAssumeUnused(5, regs); defer for (regs_locks) |reg| { self.register_manager.unlockReg(reg); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index bfc7f687fa..c792615c79 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -874,7 +874,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); if (abi_size <= ptr_bytes) { - if (self.register_manager.tryAllocReg(inst)) |reg| { + if (self.register_manager.tryAllocReg(inst, .{})) |reg| { return MCValue{ .register = reg }; } } @@ -939,7 +939,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { - const reg = try self.register_manager.allocReg(null); + const reg = try self.register_manager.allocReg(null, .{}); try self.genSetReg(ty, reg, mcv); return reg; } @@ -948,7 +948,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { /// `reg_owner` is the instruction that gets associated with the register in the register table. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { - const reg = try self.register_manager.allocReg(reg_owner); + const reg = try self.register_manager.allocReg(reg_owner, .{}); try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv); return MCValue{ .register = reg }; } @@ -1065,9 +1065,9 @@ fn trunc( if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { break :blk operand_reg; } else { - break :blk try self.register_manager.allocReg(inst); + break :blk try self.register_manager.allocReg(inst, .{}); } - } else try self.register_manager.allocReg(null); + } else try self.register_manager.allocReg(null, .{}); switch (info_b.bits) { 32 => { @@ -1153,7 +1153,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { break :blk op_reg; } - break :blk try self.register_manager.allocReg(null); + break :blk try self.register_manager.allocReg(null, .{}); }; _ = try self.addInst(.{ @@ -1183,7 +1183,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { break :blk op_reg; } - break :blk try self.register_manager.allocReg(null); + break :blk try self.register_manager.allocReg(null, .{}); }; _ = try self.addInst(.{ @@ -1254,9 +1254,9 @@ fn minMax( } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { break :blk rhs_reg; } else { - break :blk try self.register_manager.allocReg(inst); + break :blk try self.register_manager.allocReg(inst, .{}); } - } else try self.register_manager.allocReg(null); + } else try self.register_manager.allocReg(null, .{}); // lhs == reg should have been checked by airMinMax // @@ -1438,7 +1438,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_reg_lock); - const truncated_reg = try self.register_manager.allocReg(null); + const truncated_reg = try self.register_manager.allocReg(null, .{}); const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); defer self.register_manager.unlockReg(truncated_reg_lock); @@ -1543,7 +1543,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_reg_lock); - const truncated_reg = try self.register_manager.allocReg(null); + const truncated_reg = try self.register_manager.allocReg(null, .{}); const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); defer self.register_manager.unlockReg(truncated_reg_lock); @@ -1582,18 +1582,18 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const lhs_reg = if (lhs_is_register) lhs.register else - try self.register_manager.allocReg(null); + try self.register_manager.allocReg(null, .{}); const new_lhs_lock = self.register_manager.lockReg(lhs_reg); defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else - try self.register_manager.allocReg(null); + try self.register_manager.allocReg(null, .{}); const new_rhs_lock = self.register_manager.lockReg(rhs_reg); defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); - const dest_regs = try self.register_manager.allocRegs(2, .{ null, null }); + const dest_regs = try self.register_manager.allocRegs(2, .{ null, null }, .{}); const dest_regs_locks = self.register_manager.lockRegsAssumeUnused(2, dest_regs); defer for (dest_regs_locks) |reg| { self.register_manager.unlockReg(reg); @@ -1604,7 +1604,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); - const truncated_reg = try self.register_manager.allocReg(null); + const truncated_reg = try self.register_manager.allocReg(null, .{}); const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); defer self.register_manager.unlockReg(truncated_reg_lock); @@ -2026,7 +2026,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { const base_reg_lock = self.register_manager.lockRegAssumeUnused(base_reg); defer self.register_manager.unlockReg(base_reg_lock); - const dst_reg = try self.register_manager.allocReg(inst); + const dst_reg = try self.register_manager.allocReg(inst, .{}); const dst_mcv = MCValue{ .register = dst_reg }; const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); defer self.register_manager.unlockReg(dst_reg_lock); @@ -2234,7 +2234,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo }, .stack_offset => |off| { if (elem_size <= 4) { - const tmp_reg = try self.register_manager.allocReg(null); + const tmp_reg = try self.register_manager.allocReg(null, .{}); const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); defer self.register_manager.unlockReg(tmp_reg_lock); @@ -2242,7 +2242,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); } else { // TODO optimize the register allocation - const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }); + const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, .{}); const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs); defer for (regs_locks) |reg_locked| { self.register_manager.unlockReg(reg_locked); @@ -2271,7 +2271,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .stack_offset, .stack_argument_offset, => { - const reg = try self.register_manager.allocReg(null); + const reg = try self.register_manager.allocReg(null, .{}); const reg_lock = self.register_manager.lockRegAssumeUnused(reg); defer self.register_manager.unlockReg(reg_lock); @@ -2338,14 +2338,14 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type }, else => { if (elem_size <= 4) { - const tmp_reg = try self.register_manager.allocReg(null); + const tmp_reg = try self.register_manager.allocReg(null, .{}); const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); defer self.register_manager.unlockReg(tmp_reg_lock); try self.genSetReg(value_ty, tmp_reg, value); try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); } else { - const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }); + const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, .{}); const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs); defer for (regs_locks) |reg| { self.register_manager.unlockReg(reg); @@ -2487,7 +2487,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { 1 => { // get overflow bit: set register to C flag // resp. V flag - const dest_reg = try self.register_manager.allocReg(null); + const dest_reg = try self.register_manager.allocReg(null, .{}); // mov reg, #0 _ = try self.addInst(.{ @@ -2567,7 +2567,7 @@ fn binOpRegister( break :inst Air.refToIndex(md.lhs).?; } else null; - const reg = try self.register_manager.allocReg(track_inst); + const reg = try self.register_manager.allocReg(track_inst, .{}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -2581,7 +2581,7 @@ fn binOpRegister( break :inst Air.refToIndex(md.rhs).?; } else null; - const reg = try self.register_manager.allocReg(track_inst); + const reg = try self.register_manager.allocReg(track_inst, .{}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -2598,9 +2598,9 @@ fn binOpRegister( } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) { break :blk rhs_reg; } else { - break :blk try self.register_manager.allocReg(md.inst); + break :blk try self.register_manager.allocReg(md.inst, .{}); } - } else try self.register_manager.allocReg(null), + } else try self.register_manager.allocReg(null, .{}), }; if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); @@ -2684,7 +2684,7 @@ fn binOpImmediate( ).?; } else null; - const reg = try self.register_manager.allocReg(track_inst); + const reg = try self.register_manager.allocReg(track_inst, .{}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -2704,9 +2704,9 @@ fn binOpImmediate( )) { break :blk lhs_reg; } else { - break :blk try self.register_manager.allocReg(md.inst); + break :blk try self.register_manager.allocReg(md.inst, .{}); } - } else try self.register_manager.allocReg(null), + } else try self.register_manager.allocReg(null, .{}), }; if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); @@ -4363,7 +4363,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro const overflow_bit_ty = ty.structFieldType(1); const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*)); - const cond_reg = try self.register_manager.allocReg(null); + const cond_reg = try self.register_manager.allocReg(null, .{}); // C flag: movcs reg, #1 // V flag: movvs reg, #1 @@ -4408,7 +4408,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro const ptr_ty = Type.initPayload(&ptr_ty_payload.base); // TODO call extern memcpy - const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }); + const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, .{}); const src_reg = regs[0]; const dst_reg = regs[1]; const len_reg = regs[2]; @@ -4782,7 +4782,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I const ptr_ty = Type.initPayload(&ptr_ty_payload.base); // TODO call extern memcpy - const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }); + const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, .{}); const src_reg = regs[0]; const dst_reg = regs[1]; const len_reg = regs[2]; diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index c2f9b2e36d..79bedb30d8 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -803,7 +803,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); if (abi_size <= ptr_bytes) { - if (self.register_manager.tryAllocReg(inst)) |reg| { + if (self.register_manager.tryAllocReg(inst, .{})) |reg| { return MCValue{ .register = reg }; } } @@ -826,7 +826,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { - const reg = try self.register_manager.allocReg(null); + const reg = try self.register_manager.allocReg(null, .{}); try self.genSetReg(ty, reg, mcv); return reg; } @@ -835,7 +835,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { /// `reg_owner` is the instruction that gets associated with the register in the register table. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { - const reg = try self.register_manager.allocReg(reg_owner); + const reg = try self.register_manager.allocReg(reg_owner, .{}); try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv); return MCValue{ .register = reg }; } @@ -958,7 +958,7 @@ fn binOpRegister( break :inst Air.refToIndex(bin_op.lhs).?; } else null; - const reg = try self.register_manager.allocReg(track_inst); + const reg = try self.register_manager.allocReg(track_inst, .{}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -973,7 +973,7 @@ fn binOpRegister( break :inst Air.refToIndex(bin_op.rhs).?; } else null; - const reg = try self.register_manager.allocReg(track_inst); + const reg = try self.register_manager.allocReg(track_inst, .{}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -990,9 +990,9 @@ fn binOpRegister( } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { break :blk rhs_reg; } else { - break :blk try self.register_manager.allocReg(inst); + break :blk try self.register_manager.allocReg(inst, .{}); } - } else try self.register_manager.allocReg(null); + } else try self.register_manager.allocReg(null, .{}); if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); @@ -1482,7 +1482,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .memory, .stack_offset, => { - const reg = try self.register_manager.allocReg(null); + const reg = try self.register_manager.allocReg(null, .{}); const reg_lock = self.register_manager.lockRegAssumeUnused(reg); defer self.register_manager.unlockReg(reg_lock); diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index aa679cac6d..e8cb5d3100 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1613,7 +1613,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { if (reg_ok) { // Make sure the type can fit in a register before we try to allocate one. if (abi_size <= 8) { - if (self.register_manager.tryAllocReg(inst)) |reg| { + if (self.register_manager.tryAllocReg(inst, .{})) |reg| { return MCValue{ .register = reg }; } } @@ -1854,7 +1854,7 @@ fn binOpImmediate( ).?; } else null; - const reg = try self.register_manager.allocReg(track_inst); + const reg = try self.register_manager.allocReg(track_inst, .{}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -1873,10 +1873,10 @@ fn binOpImmediate( )) { break :blk lhs_reg; } else { - break :blk try self.register_manager.allocReg(md.inst); + break :blk try self.register_manager.allocReg(md.inst, .{}); } } else blk: { - break :blk try self.register_manager.allocReg(null); + break :blk try self.register_manager.allocReg(null, .{}); }, }; @@ -1953,7 +1953,7 @@ fn binOpRegister( break :inst Air.refToIndex(md.lhs).?; } else null; - const reg = try self.register_manager.allocReg(track_inst); + const reg = try self.register_manager.allocReg(track_inst, .{}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); break :blk reg; @@ -1966,7 +1966,7 @@ fn binOpRegister( break :inst Air.refToIndex(md.rhs).?; } else null; - const reg = try self.register_manager.allocReg(track_inst); + const reg = try self.register_manager.allocReg(track_inst, .{}); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); break :blk reg; @@ -1981,10 +1981,10 @@ fn binOpRegister( } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) { break :blk rhs_reg; } else { - break :blk try self.register_manager.allocReg(md.inst); + break :blk try self.register_manager.allocReg(md.inst, .{}); } } else blk: { - break :blk try self.register_manager.allocReg(null); + break :blk try self.register_manager.allocReg(null, .{}); }, }; @@ -2077,7 +2077,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { - const reg = try self.register_manager.allocReg(null); + const reg = try self.register_manager.allocReg(null, .{}); try self.genSetReg(ty, reg, mcv); return reg; } @@ -2364,7 +2364,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }); } else { // Need to allocate a temporary register to load 64-bit immediates. - const tmp_reg = try self.register_manager.allocReg(null); + const tmp_reg = try self.register_manager.allocReg(null, .{}); try self.genSetReg(ty, tmp_reg, .{ .immediate = @truncate(u32, x) }); try self.genSetReg(ty, reg, .{ .immediate = @truncate(u32, x >> 32) }); @@ -2478,7 +2478,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro }; const ptr_ty = Type.initPayload(&ptr_ty_payload.base); - const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }); + const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, .{}); const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs); defer for (regs_locks) |reg| { self.register_manager.unlockReg(reg); @@ -2717,14 +2717,14 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo }, .stack_offset => |off| { if (elem_size <= 8) { - const tmp_reg = try self.register_manager.allocReg(null); + const tmp_reg = try self.register_manager.allocReg(null, .{}); const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); defer self.register_manager.unlockReg(tmp_reg_lock); try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); } else { - const regs = try self.register_manager.allocRegs(3, .{ null, null, null }); + const regs = try self.register_manager.allocRegs(3, .{ null, null, null }, .{}); const regs_locks = self.register_manager.lockRegsAssumeUnused(3, regs); defer for (regs_locks) |reg| { self.register_manager.unlockReg(reg); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index aa6d04d5d9..aebbc03e46 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -882,7 +882,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { // TODO check if AVX available const ptr_bytes: u64 = 32; if (abi_size <= ptr_bytes) { - if (self.register_manager.tryAllocReg(inst)) |reg| { + if (self.register_manager.tryAllocReg(inst, .{})) |reg| { return MCValue{ .register = registerAlias(reg, abi_size) }; } } @@ -892,7 +892,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); if (abi_size <= ptr_bytes) { - if (self.register_manager.tryAllocReg(inst)) |reg| { + if (self.register_manager.tryAllocReg(inst, .{})) |reg| { return MCValue{ .register = registerAlias(reg, abi_size) }; } } @@ -963,7 +963,7 @@ pub fn spillRegisters(self: *Self, comptime count: comptime_int, registers: [cou /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { - const reg = try self.register_manager.allocReg(null); + const reg = try self.register_manager.allocReg(null, .{}); try self.genSetReg(ty, reg, mcv); return reg; } @@ -973,7 +973,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { /// This can have a side effect of spilling instructions to the stack to free up a register. /// WARNING make sure that the allocated register matches the returned MCValue from an instruction! fn copyToRegisterWithInstTracking(self: *Self, reg_owner: Air.Inst.Index, ty: Type, mcv: MCValue) !MCValue { - const reg = try self.register_manager.allocReg(reg_owner); + const reg = try self.register_manager.allocReg(reg_owner, .{}); try self.genSetReg(ty, reg, mcv); return MCValue{ .register = reg }; } @@ -1029,7 +1029,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { }; defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); - const reg = try self.register_manager.allocReg(inst); + const reg = try self.register_manager.allocReg(inst, .{}); try self.genSetReg(dest_ty, reg, .{ .immediate = 0 }); try self.genSetReg(operand_ty, reg, operand); break :blk MCValue{ .register = reg }; @@ -1384,7 +1384,7 @@ fn genSetStackTruncatedOverflowCompare( .unsigned => ty, }; - const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }); + const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }, .{}); const temp_regs_locks = self.register_manager.lockRegsAssumeUnused(3, temp_regs); defer for (temp_regs_locks) |rreg| { self.register_manager.unlockReg(rreg); @@ -2046,7 +2046,7 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); defer self.register_manager.unlockReg(offset_reg_lock); - const addr_reg = try self.register_manager.allocReg(null); + const addr_reg = try self.register_manager.allocReg(null, .{}); switch (slice_mcv) { .stack_offset => |off| { // mov reg, [rbp - 8] @@ -2125,7 +2125,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); defer self.register_manager.unlockReg(offset_reg_lock); - const addr_reg = try self.register_manager.allocReg(null); + const addr_reg = try self.register_manager.allocReg(null, .{}); switch (array) { .register => { const off = @intCast(i32, try self.allocMem( @@ -2492,7 +2492,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo }, .stack_offset => |off| { if (abi_size <= 8) { - const tmp_reg = try self.register_manager.allocReg(null); + const tmp_reg = try self.register_manager.allocReg(null, .{}); try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); return self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }, .{}); } @@ -2693,7 +2693,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type }; defer if (value_lock) |lock| self.register_manager.unlockReg(lock); - const addr_reg = try self.register_manager.allocReg(null); + const addr_reg = try self.register_manager.allocReg(null, .{}); const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); defer self.register_manager.unlockReg(addr_reg_lock); @@ -2765,7 +2765,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .memory, => { if (abi_size <= 8) { - const tmp_reg = try self.register_manager.allocReg(null); + const tmp_reg = try self.register_manager.allocReg(null, .{}); const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); defer self.register_manager.unlockReg(tmp_reg_lock); @@ -2883,7 +2883,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde if (can_reuse_operand) { break :blk reg; } else { - const result_reg = try self.register_manager.allocReg(inst); + const result_reg = try self.register_manager.allocReg(inst, .{}); try self.genSetReg(ptr_ty, result_reg, mcv); break :blk result_reg; } @@ -2984,7 +2984,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const reg_lock = self.register_manager.lockRegAssumeUnused(reg); defer self.register_manager.unlockReg(reg_lock); - const dst_reg = try self.register_manager.allocReg(inst); + const dst_reg = try self.register_manager.allocReg(inst, .{}); const flags: u2 = switch (mcv) { .register_overflow_unsigned => 0b10, .register_overflow_signed => 0b00, @@ -5362,7 +5362,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl const overflow_bit_ty = ty.structFieldType(1); const overflow_bit_offset = ty.structFieldOffset(1, self.target.*); - const tmp_reg = try self.register_manager.allocReg(null); + const tmp_reg = try self.register_manager.allocReg(null, .{}); const flags: u2 = switch (mcv) { .register_overflow_unsigned => 0b10, .register_overflow_signed => 0b00, @@ -5580,7 +5580,7 @@ fn genInlineMemcpy( null; defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock); - const dst_addr_reg = try self.register_manager.allocReg(null); + const dst_addr_reg = try self.register_manager.allocReg(null, .{}); switch (dst_ptr) { .memory, .got_load, @@ -5615,7 +5615,7 @@ fn genInlineMemcpy( const dst_addr_reg_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg); defer self.register_manager.unlockReg(dst_addr_reg_lock); - const src_addr_reg = try self.register_manager.allocReg(null); + const src_addr_reg = try self.register_manager.allocReg(null, .{}); switch (src_ptr) { .memory, .got_load, @@ -5650,7 +5650,7 @@ fn genInlineMemcpy( const src_addr_reg_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg); defer self.register_manager.unlockReg(src_addr_reg_lock); - const regs = try self.register_manager.allocRegs(2, .{ null, null }); + const regs = try self.register_manager.allocRegs(2, .{ null, null }, .{}); const count_reg = regs[0].to64(); const tmp_reg = regs[1].to8(); @@ -5750,7 +5750,7 @@ fn genInlineMemset( const rax_lock = self.register_manager.lockRegAssumeUnused(.rax); defer self.register_manager.unlockReg(rax_lock); - const addr_reg = try self.register_manager.allocReg(null); + const addr_reg = try self.register_manager.allocReg(null, .{}); switch (dst_ptr) { .memory, .got_load, @@ -6018,7 +6018,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void switch (ty.tag()) { .f32 => return self.fail("TODO genSetReg from memory for f32", .{}), .f64 => { - const base_reg = try self.register_manager.allocReg(null); + const base_reg = try self.register_manager.allocReg(null, .{}); try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); _ = try self.addInst(.{ .tag = .mov_f64, @@ -6328,7 +6328,7 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { const src: MCValue = blk: { switch (src_ptr) { .got_load, .direct_load, .memory => { - const reg = try self.register_manager.allocReg(null); + const reg = try self.register_manager.allocReg(null, .{}); try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr); _ = try self.addInst(.{ .tag = .mov, diff --git a/src/register_manager.zig b/src/register_manager.zig index 2c0502e867..25d8ef8675 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -173,6 +173,10 @@ pub fn RegisterManager( return self.locked_registers != 0; } + const AllocOpts = struct { + selector_mask: ?FreeRegInt = null, + }; + /// Allocates a specified number of registers, optionally /// tracking them. Returns `null` if not enough registers are /// free. @@ -180,7 +184,9 @@ pub fn RegisterManager( self: *Self, comptime count: comptime_int, insts: [count]?Air.Inst.Index, + opts: AllocOpts, ) ?[count]Register { + _ = opts; comptime assert(count > 0 and count <= tracked_registers.len); const free_and_not_locked_registers = self.free_registers & ~self.locked_registers; @@ -216,8 +222,8 @@ pub fn RegisterManager( /// Allocates a register and optionally tracks it with a /// corresponding instruction. Returns `null` if all registers /// are allocated. - pub fn tryAllocReg(self: *Self, inst: ?Air.Inst.Index) ?Register { - return if (tryAllocRegs(self, 1, .{inst})) |regs| regs[0] else null; + pub fn tryAllocReg(self: *Self, inst: ?Air.Inst.Index, opts: AllocOpts) ?Register { + return if (tryAllocRegs(self, 1, .{inst}, opts)) |regs| regs[0] else null; } /// Allocates a specified number of registers, optionally @@ -227,12 +233,13 @@ pub fn RegisterManager( self: *Self, comptime count: comptime_int, insts: [count]?Air.Inst.Index, + opts: AllocOpts, ) AllocateRegistersError![count]Register { comptime assert(count > 0 and count <= tracked_registers.len); const locked_registers_count = @popCount(FreeRegInt, self.locked_registers); if (count > tracked_registers.len - locked_registers_count) return error.OutOfRegisters; - const result = self.tryAllocRegs(count, insts) orelse blk: { + const result = self.tryAllocRegs(count, insts, opts) orelse blk: { // We'll take over the first count registers. Spill // the instructions that were previously there to a // stack allocations. @@ -275,8 +282,8 @@ pub fn RegisterManager( /// Allocates a register and optionally tracks it with a /// corresponding instruction. - pub fn allocReg(self: *Self, inst: ?Air.Inst.Index) AllocateRegistersError!Register { - return (try self.allocRegs(1, .{inst}))[0]; + pub fn allocReg(self: *Self, inst: ?Air.Inst.Index, opts: AllocOpts) AllocateRegistersError!Register { + return (try self.allocRegs(1, .{inst}, opts))[0]; } /// Spills the register if it is currently allocated. If a From f34615082058549d8945a39195cf0dc826b688e1 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 18 May 2022 12:23:07 +0200 Subject: [PATCH 1580/2031] x64: use register classes mask to select between gp and avx --- src/arch/x86_64/CodeGen.zig | 69 ++++++++++++++++++++++++++----------- src/arch/x86_64/abi.zig | 13 +++---- src/arch/x86_64/bits.zig | 18 +++++----- src/register_manager.zig | 20 ++++++++--- 4 files changed, 81 insertions(+), 39 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index aebbc03e46..7f41d62141 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -38,6 +38,9 @@ const c_abi_int_return_regs = abi.c_abi_int_return_regs; const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers); const RegisterLock = RegisterManager.RegisterLock; const Register = bits.Register; +const RegisterClass = abi.RegisterClass; +const gp = RegisterClass.gp; +const avx = RegisterClass.avx; const InnerError = error{ OutOfMemory, @@ -882,7 +885,9 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { // TODO check if AVX available const ptr_bytes: u64 = 32; if (abi_size <= ptr_bytes) { - if (self.register_manager.tryAllocReg(inst, .{})) |reg| { + if (self.register_manager.tryAllocReg(inst, .{ + .selector_mask = avx, + })) |reg| { return MCValue{ .register = registerAlias(reg, abi_size) }; } } @@ -892,7 +897,9 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); if (abi_size <= ptr_bytes) { - if (self.register_manager.tryAllocReg(inst, .{})) |reg| { + if (self.register_manager.tryAllocReg(inst, .{ + .selector_mask = gp, + })) |reg| { return MCValue{ .register = registerAlias(reg, abi_size) }; } } @@ -963,7 +970,13 @@ pub fn spillRegisters(self: *Self, comptime count: comptime_int, registers: [cou /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { - const reg = try self.register_manager.allocReg(null, .{}); + const mask = switch (ty.zigTypeTag()) { + .Float => avx, + else => gp, + }; + const reg: Register = try self.register_manager.allocReg(null, .{ + .selector_mask = mask, + }); try self.genSetReg(ty, reg, mcv); return reg; } @@ -973,7 +986,13 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { /// This can have a side effect of spilling instructions to the stack to free up a register. /// WARNING make sure that the allocated register matches the returned MCValue from an instruction! fn copyToRegisterWithInstTracking(self: *Self, reg_owner: Air.Inst.Index, ty: Type, mcv: MCValue) !MCValue { - const reg = try self.register_manager.allocReg(reg_owner, .{}); + const mask = switch (ty.zigTypeTag()) { + .Float => avx, + else => gp, + }; + const reg: Register = try self.register_manager.allocReg(reg_owner, .{ + .selector_mask = mask, + }); try self.genSetReg(ty, reg, mcv); return MCValue{ .register = reg }; } @@ -1029,7 +1048,9 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { }; defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); - const reg = try self.register_manager.allocReg(inst, .{}); + const reg = try self.register_manager.allocReg(inst, .{ + .selector_mask = gp, + }); try self.genSetReg(dest_ty, reg, .{ .immediate = 0 }); try self.genSetReg(operand_ty, reg, operand); break :blk MCValue{ .register = reg }; @@ -1384,7 +1405,9 @@ fn genSetStackTruncatedOverflowCompare( .unsigned => ty, }; - const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }, .{}); + const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }, .{ + .selector_mask = gp, + }); const temp_regs_locks = self.register_manager.lockRegsAssumeUnused(3, temp_regs); defer for (temp_regs_locks) |rreg| { self.register_manager.unlockReg(rreg); @@ -2046,7 +2069,9 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); defer self.register_manager.unlockReg(offset_reg_lock); - const addr_reg = try self.register_manager.allocReg(null, .{}); + const addr_reg = try self.register_manager.allocReg(null, .{ + .selector_mask = gp, + }); switch (slice_mcv) { .stack_offset => |off| { // mov reg, [rbp - 8] @@ -2125,7 +2150,9 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); defer self.register_manager.unlockReg(offset_reg_lock); - const addr_reg = try self.register_manager.allocReg(null, .{}); + const addr_reg = try self.register_manager.allocReg(null, .{ + .selector_mask = gp, + }); switch (array) { .register => { const off = @intCast(i32, try self.allocMem( @@ -2492,7 +2519,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo }, .stack_offset => |off| { if (abi_size <= 8) { - const tmp_reg = try self.register_manager.allocReg(null, .{}); + const tmp_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); return self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }, .{}); } @@ -2693,7 +2720,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type }; defer if (value_lock) |lock| self.register_manager.unlockReg(lock); - const addr_reg = try self.register_manager.allocReg(null, .{}); + const addr_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); defer self.register_manager.unlockReg(addr_reg_lock); @@ -2765,7 +2792,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .memory, => { if (abi_size <= 8) { - const tmp_reg = try self.register_manager.allocReg(null, .{}); + const tmp_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); defer self.register_manager.unlockReg(tmp_reg_lock); @@ -2883,7 +2910,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde if (can_reuse_operand) { break :blk reg; } else { - const result_reg = try self.register_manager.allocReg(inst, .{}); + const result_reg = try self.register_manager.allocReg(inst, .{ .selector_mask = gp }); try self.genSetReg(ptr_ty, result_reg, mcv); break :blk result_reg; } @@ -2984,7 +3011,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const reg_lock = self.register_manager.lockRegAssumeUnused(reg); defer self.register_manager.unlockReg(reg_lock); - const dst_reg = try self.register_manager.allocReg(inst, .{}); + const dst_reg = try self.register_manager.allocReg(inst, .{ .selector_mask = gp }); const flags: u2 = switch (mcv) { .register_overflow_unsigned => 0b10, .register_overflow_signed => 0b00, @@ -5362,7 +5389,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl const overflow_bit_ty = ty.structFieldType(1); const overflow_bit_offset = ty.structFieldOffset(1, self.target.*); - const tmp_reg = try self.register_manager.allocReg(null, .{}); + const tmp_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); const flags: u2 = switch (mcv) { .register_overflow_unsigned => 0b10, .register_overflow_signed => 0b00, @@ -5580,7 +5607,7 @@ fn genInlineMemcpy( null; defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock); - const dst_addr_reg = try self.register_manager.allocReg(null, .{}); + const dst_addr_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); switch (dst_ptr) { .memory, .got_load, @@ -5615,7 +5642,7 @@ fn genInlineMemcpy( const dst_addr_reg_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg); defer self.register_manager.unlockReg(dst_addr_reg_lock); - const src_addr_reg = try self.register_manager.allocReg(null, .{}); + const src_addr_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); switch (src_ptr) { .memory, .got_load, @@ -5650,7 +5677,9 @@ fn genInlineMemcpy( const src_addr_reg_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg); defer self.register_manager.unlockReg(src_addr_reg_lock); - const regs = try self.register_manager.allocRegs(2, .{ null, null }, .{}); + const regs = try self.register_manager.allocRegs(2, .{ null, null }, .{ + .selector_mask = gp, + }); const count_reg = regs[0].to64(); const tmp_reg = regs[1].to8(); @@ -5750,7 +5779,7 @@ fn genInlineMemset( const rax_lock = self.register_manager.lockRegAssumeUnused(.rax); defer self.register_manager.unlockReg(rax_lock); - const addr_reg = try self.register_manager.allocReg(null, .{}); + const addr_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); switch (dst_ptr) { .memory, .got_load, @@ -6018,7 +6047,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void switch (ty.tag()) { .f32 => return self.fail("TODO genSetReg from memory for f32", .{}), .f64 => { - const base_reg = try self.register_manager.allocReg(null, .{}); + const base_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); _ = try self.addInst(.{ .tag = .mov_f64, @@ -6328,7 +6357,7 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { const src: MCValue = blk: { switch (src_ptr) { .got_load, .direct_load, .memory => { - const reg = try self.register_manager.allocReg(null, .{}); + const reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr); _ = try self.addInst(.{ .tag = .mov, diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index 85bf3a0790..046948ff68 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -384,11 +384,12 @@ pub const avx_regs = [_]Register{ }; pub const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ avx_regs; -// Masks for register manager -const FreeRegInt = std.meta.Int(.unsigned, allocatable_registers.len); -// TODO -pub const gp_mask: FreeRegInt = 0x3fff; -pub const avx_mask: FreeRegInt = 0x3fff_c000; - pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; + +// Masks for register manager +const FreeRegInt = std.meta.Int(.unsigned, allocatable_registers.len); +pub const RegisterClass = struct { + pub const gp: FreeRegInt = 0x3fff; + pub const avx: FreeRegInt = 0x3fff_c000; +}; diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 85bd190e2b..a0ca774cae 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -56,10 +56,10 @@ pub const Register = enum(u7) { // Pseudo-value for MIR instructions. none, - pub fn id(self: Register) u5 { + pub fn id(self: Register) u7 { return switch (@enumToInt(self)) { - 0...63 => @as(u5, @truncate(u4, @enumToInt(self))), - 64...79 => @truncate(u5, @enumToInt(self)), + 0...63 => @as(u7, @truncate(u4, @enumToInt(self))), + 64...79 => @enumToInt(self), else => unreachable, }; } @@ -101,31 +101,31 @@ pub const Register = enum(u7) { } pub fn to256(self: Register) Register { - return @intToEnum(Register, @as(u8, self.id()) + 64); + return @intToEnum(Register, @as(u8, self.enc()) + 64); } pub fn to128(self: Register) Register { - return @intToEnum(Register, @as(u8, self.id()) + 80); + return @intToEnum(Register, @as(u8, self.enc()) + 80); } /// Convert from any register to its 64 bit alias. pub fn to64(self: Register) Register { - return @intToEnum(Register, self.id()); + return @intToEnum(Register, self.enc()); } /// Convert from any register to its 32 bit alias. pub fn to32(self: Register) Register { - return @intToEnum(Register, @as(u8, self.id()) + 16); + return @intToEnum(Register, @as(u8, self.enc()) + 16); } /// Convert from any register to its 16 bit alias. pub fn to16(self: Register) Register { - return @intToEnum(Register, @as(u8, self.id()) + 32); + return @intToEnum(Register, @as(u8, self.enc()) + 32); } /// Convert from any register to its 8 bit alias. pub fn to8(self: Register) Register { - return @intToEnum(Register, @as(u8, self.id()) + 48); + return @intToEnum(Register, @as(u8, self.enc()) + 48); } pub fn dwarfLocOp(self: Register) u8 { diff --git a/src/register_manager.zig b/src/register_manager.zig index 25d8ef8675..fbdf3ef2c3 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -66,6 +66,11 @@ pub fn RegisterManager( return mask; } + fn excludeRegister(reg: Register, mask: FreeRegInt) bool { + const reg_mask = getRegisterMask(reg) orelse return true; + return reg_mask & mask == 0; + } + fn markRegAllocated(self: *Self, reg: Register) void { const mask = getRegisterMask(reg) orelse return; self.allocated_registers |= mask; @@ -186,10 +191,11 @@ pub fn RegisterManager( insts: [count]?Air.Inst.Index, opts: AllocOpts, ) ?[count]Register { - _ = opts; comptime assert(count > 0 and count <= tracked_registers.len); - const free_and_not_locked_registers = self.free_registers & ~self.locked_registers; + const selector_mask = if (opts.selector_mask) |mask| mask else ~@as(FreeRegInt, 0); + const free_registers = self.free_registers & selector_mask; + const free_and_not_locked_registers = free_registers & ~self.locked_registers; const free_and_not_locked_registers_count = @popCount(FreeRegInt, free_and_not_locked_registers); if (free_and_not_locked_registers_count < count) return null; @@ -197,6 +203,7 @@ pub fn RegisterManager( var i: usize = 0; for (tracked_registers) |reg| { if (i >= count) break; + if (excludeRegister(reg, selector_mask)) continue; if (self.isRegLocked(reg)) continue; if (!self.isRegFree(reg)) continue; @@ -236,8 +243,12 @@ pub fn RegisterManager( opts: AllocOpts, ) AllocateRegistersError![count]Register { comptime assert(count > 0 and count <= tracked_registers.len); - const locked_registers_count = @popCount(FreeRegInt, self.locked_registers); - if (count > tracked_registers.len - locked_registers_count) return error.OutOfRegisters; + + const selector_mask = if (opts.selector_mask) |mask| mask else ~@as(FreeRegInt, 0); + const available_registers_count = @popCount(FreeRegInt, selector_mask); + const locked_registers = self.locked_registers & selector_mask; + const locked_registers_count = @popCount(FreeRegInt, locked_registers); + if (count > available_registers_count - locked_registers_count) return error.OutOfRegisters; const result = self.tryAllocRegs(count, insts, opts) orelse blk: { // We'll take over the first count registers. Spill @@ -247,6 +258,7 @@ pub fn RegisterManager( var i: usize = 0; for (tracked_registers) |reg| { if (i >= count) break; + if (excludeRegister(reg, selector_mask)) continue; if (self.isRegLocked(reg)) continue; regs[i] = reg; From 020f99d893be794376eea329af06838474744e80 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 18 May 2022 12:50:19 +0200 Subject: [PATCH 1581/2031] x64: remove special-casing of AVX for br() --- src/arch/x86_64/CodeGen.zig | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 7f41d62141..6d7684eef8 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -5042,21 +5042,12 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { if (block_mcv == .none) { block_data.mcv = switch (operand_mcv) { .none, .dead, .unreach => unreachable, - .stack_offset, .memory => operand_mcv, + .register, .stack_offset, .memory => operand_mcv, .compare_flags_signed, .compare_flags_unsigned, .immediate => blk: { const new_mcv = try self.allocRegOrMem(block, true); try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); break :blk new_mcv; }, - .register => blk: { - if (self.air.typeOfIndex(block).zigTypeTag() == .Float) { - // TODO not needed; return operand_mcv ones we can transfer between XMM registers - const new_mcv = try self.allocRegOrMem(block, false); - try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); - break :blk new_mcv; - } - break :blk operand_mcv; - }, else => return self.fail("TODO implement block_data.mcv = operand_mcv for {}", .{operand_mcv}), }; } else { From 36b939e8db902b01870d709faeb6d2725f4f73ae Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 18 May 2022 14:15:27 +0200 Subject: [PATCH 1582/2031] x64: handle basic f32 using AVX registers --- src/arch/x86_64/CodeGen.zig | 170 +++++++++++--------- src/arch/x86_64/Emit.zig | 208 +++++++++++------------- src/arch/x86_64/Mir.zig | 3 + test/behavior.zig | 312 ++++++++++++++++++------------------ 4 files changed, 352 insertions(+), 341 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 6d7684eef8..7896a2f3b7 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -3469,22 +3469,28 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu return self.genBinOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); }, .register => |src_reg| switch (dst_ty.zigTypeTag()) { - .Float => switch (dst_ty.tag()) { - .f64 => { - _ = try self.addInst(.{ - .tag = switch (mir_tag) { - .add => .add_f64, - .cmp => .cmp_f64, - else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}), - }, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = dst_reg.to128(), - .reg2 = src_reg.to128(), - }), - .data = undefined, - }); - }, - else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}), + .Float => { + const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) { + .f32 => switch (mir_tag) { + .add => Mir.Inst.Tag.add_f32, + .cmp => Mir.Inst.Tag.cmp_f32, + else => return self.fail("TODO genBinOpMir for f32 register-register with MIR tag {}", .{mir_tag}), + }, + .f64 => switch (mir_tag) { + .add => Mir.Inst.Tag.add_f64, + .cmp => Mir.Inst.Tag.cmp_f64, + else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}), + }, + else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}), + }; + _ = try self.addInst(.{ + .tag = actual_tag, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = dst_reg.to128(), + .reg2 = src_reg.to128(), + }), + .data = undefined, + }); }, else => { _ = try self.addInst(.{ @@ -5475,20 +5481,25 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl const base_reg = opts.dest_stack_base orelse .rbp; switch (ty.zigTypeTag()) { - .Float => switch (ty.tag()) { - .f32 => return self.fail("TODO genSetStack for register for f32", .{}), - .f64 => { - _ = try self.addInst(.{ - .tag = .mov_f64, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = base_reg, - .reg2 = reg.to128(), - .flags = 0b01, - }), - .data = .{ .imm = @bitCast(u32, -stack_offset) }, - }); - }, - else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}), + .Float => { + const tag: Mir.Inst.Tag = switch (ty.tag()) { + .f32 => .mov_f32, + .f64 => .mov_f64, + else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}), + }; + _ = try self.addInst(.{ + .tag = tag, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = switch (ty.tag()) { + .f32 => base_reg.to32(), + .f64 => base_reg.to64(), + else => unreachable, + }, + .reg2 = reg.to128(), + .flags = 0b01, + }), + .data = .{ .imm = @bitCast(u32, -stack_offset) }, + }); }, else => { if (!math.isPowerOfTwo(abi_size)) { @@ -5991,21 +6002,22 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void } }, }, - .Float => switch (ty.tag()) { - .f32 => return self.fail("TODO genSetReg from register for f32", .{}), - .f64 => { - _ = try self.addInst(.{ - .tag = .mov_f64, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to128(), - .reg2 = src_reg.to128(), - .flags = 0b10, - }), - .data = undefined, - }); - return; - }, - else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}), + .Float => { + const tag: Mir.Inst.Tag = switch (ty.tag()) { + .f32 => .mov_f32, + .f64 => .mov_f64, + else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}), + }; + _ = try self.addInst(.{ + .tag = tag, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = reg.to128(), + .reg2 = src_reg.to128(), + .flags = 0b10, + }), + .data = undefined, + }); + return; }, else => {}, } @@ -6035,22 +6047,27 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }, .memory => |x| switch (ty.zigTypeTag()) { .Float => { - switch (ty.tag()) { - .f32 => return self.fail("TODO genSetReg from memory for f32", .{}), - .f64 => { - const base_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); - try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); - _ = try self.addInst(.{ - .tag = .mov_f64, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to128(), - .reg2 = base_reg.to64(), - }), - .data = .{ .imm = 0 }, - }); - }, + const base_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); + try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); + + const tag: Mir.Inst.Tag = switch (ty.tag()) { + .f32 => .mov_f32, + .f64 => .mov_f64, else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), - } + }; + + _ = try self.addInst(.{ + .tag = tag, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = reg.to128(), + .reg2 = switch (ty.tag()) { + .f32 => base_reg.to32(), + .f64 => base_reg.to64(), + else => unreachable, + }, + }), + .data = .{ .imm = 0 }, + }); }, else => { if (x <= math.maxInt(i32)) { @@ -6142,20 +6159,25 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void } }, }, - .Float => switch (ty.tag()) { - .f32 => return self.fail("TODO genSetReg from stack offset for f32", .{}), - .f64 => { - _ = try self.addInst(.{ - .tag = .mov_f64, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to128(), - .reg2 = .rbp, - }), - .data = .{ .imm = @bitCast(u32, -off) }, - }); - return; - }, - else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}), + .Float => { + const tag: Mir.Inst.Tag = switch (ty.tag()) { + .f32 => .mov_f32, + .f64 => .mov_f64, + else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}), + }; + _ = try self.addInst(.{ + .tag = tag, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = reg.to128(), + .reg2 = switch (ty.tag()) { + .f32 => .ebp, + .f64 => .rbp, + else => unreachable, + }, + }), + .data = .{ .imm = @bitCast(u32, -off) }, + }); + return; }, else => {}, } diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 5ad8e86374..96f640b610 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -183,11 +183,14 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .nop => try emit.mirNop(), // AVX instructions - .mov_f64 => try emit.mirMovF64(inst), + .mov_f64 => try emit.mirMovFloatAvx(.vmovsd, inst), + .mov_f32 => try emit.mirMovFloatAvx(.vmovss, inst), - .add_f64 => try emit.mirAddF64(inst), + .add_f64 => try emit.mirAddFloatAvx(.vaddsd, inst), + .add_f32 => try emit.mirAddFloatAvx(.vaddss, inst), - .cmp_f64 => try emit.mirCmpF64(inst), + .cmp_f64 => try emit.mirCmpFloatAvx(.vucomisd, inst), + .cmp_f32 => try emit.mirCmpFloatAvx(.vucomiss, inst), // Pseudo-instructions .call_extern => try emit.mirCallExtern(inst), @@ -962,71 +965,48 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { // AVX instructions -fn mirMovF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .mov_f64); +fn mirMovFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToVmEnc(.vmovsd, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{ + return lowerToVmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{ .disp = imm, .base = ops.reg2, }), emit.code); }, 0b01 => { const imm = emit.mir.instructions.items(.data)[inst].imm; - return lowerToMvEnc(.vmovsd, RegisterOrMemory.mem(.qword_ptr, .{ + return lowerToMvEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ .disp = imm, .base = ops.reg1, }), ops.reg2, emit.code); }, 0b10 => { - return lowerToRvmEnc( - .vmovsd, - ops.reg1, - ops.reg1, - RegisterOrMemory.reg(ops.reg2), - emit.code, - ); + return lowerToRvmEnc(tag, ops.reg1, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); }, else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{ops.flags}), } } -fn mirAddF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .add_f64); +fn mirAddFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { - return lowerToRvmEnc( - .vaddsd, - ops.reg1, - ops.reg1, - RegisterOrMemory.reg(ops.reg2), - emit.code, - ); + return lowerToRvmEnc(tag, ops.reg1, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); }, else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{ops.flags}), } } -fn mirCmpF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .cmp_f64); +fn mirCmpFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); switch (ops.flags) { 0b00 => { - return lowerToVmEnc( - .vucomisd, - ops.reg1, - RegisterOrMemory.reg(ops.reg2), - emit.code, - ); + return lowerToVmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); }, else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{ops.flags}), } @@ -1268,16 +1248,24 @@ const Tag = enum { cmovb, cmovnae, vmovsd, + vmovss, vaddsd, + vaddss, vcmpsd, + vcmpss, vucomisd, + vucomiss, fn isAvx(tag: Tag) bool { return switch (tag) { .vmovsd, + .vmovss, .vaddsd, + .vaddss, .vcmpsd, + .vcmpss, .vucomisd, + .vucomiss, => true, else => false, @@ -1406,7 +1394,7 @@ const OpCode = union(enum) { } }; -inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { +inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) OpCode { switch (enc) { .zo => return switch (tag) { .ret_near => OpCode.oneByte(0xc3), @@ -1416,7 +1404,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .syscall => OpCode.twoByte(0x0f, 0x05), .cbw => OpCode.oneByte(0x98), .cwd, .cdq, .cqo => OpCode.oneByte(0x99), - else => null, + else => unreachable, }, .d => return switch (tag) { .jmp_near => OpCode.oneByte(0xe9), @@ -1437,7 +1425,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .jge, .jnl => if (is_one_byte) OpCode.oneByte(0x7d) else OpCode.twoByte(0x0f, 0x8d), .jle, .jng => if (is_one_byte) OpCode.oneByte(0x7e) else OpCode.twoByte(0x0f, 0x8e), .jg, .jnle => if (is_one_byte) OpCode.oneByte(0x7f) else OpCode.twoByte(0x0f, 0x8f), - else => null, + else => unreachable, }, .m => return switch (tag) { .jmp_near, .call_near, .push => OpCode.oneByte(0xff), @@ -1464,38 +1452,38 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .fisttp64 => OpCode.oneByte(0xdd), .fld32 => OpCode.oneByte(0xd9), .fld64 => OpCode.oneByte(0xdd), - else => null, + else => unreachable, }, .o => return switch (tag) { .push => OpCode.oneByte(0x50), .pop => OpCode.oneByte(0x58), - else => null, + else => unreachable, }, .i => return switch (tag) { .push => OpCode.oneByte(if (is_one_byte) 0x6a else 0x68), .@"test" => OpCode.oneByte(if (is_one_byte) 0xa8 else 0xa9), .ret_near => OpCode.oneByte(0xc2), .ret_far => OpCode.oneByte(0xca), - else => null, + else => unreachable, }, .m1 => return switch (tag) { .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd0 else 0xd1), - else => null, + else => unreachable, }, .mc => return switch (tag) { .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd2 else 0xd3), - else => null, + else => unreachable, }, .mi => return switch (tag) { .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(if (is_one_byte) 0x80 else 0x81), .mov => OpCode.oneByte(if (is_one_byte) 0xc6 else 0xc7), .@"test" => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7), - else => null, + else => unreachable, }, .mi8 => return switch (tag) { .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(0x83), .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xc0 else 0xc1), - else => null, + else => unreachable, }, .mr => return switch (tag) { .adc => OpCode.oneByte(if (is_one_byte) 0x10 else 0x11), @@ -1508,8 +1496,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .cmp => OpCode.oneByte(if (is_one_byte) 0x38 else 0x39), .mov => OpCode.oneByte(if (is_one_byte) 0x88 else 0x89), .@"test" => OpCode.oneByte(if (is_one_byte) 0x84 else 0x85), - .vmovsd => OpCode.oneByte(0x11), - else => null, + else => unreachable, }, .rm => return switch (tag) { .adc => OpCode.oneByte(if (is_one_byte) 0x12 else 0x13), @@ -1529,48 +1516,46 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .cmove, .cmovz => OpCode.twoByte(0x0f, 0x44), .cmovb, .cmovnae => OpCode.twoByte(0x0f, 0x42), .cmovl, .cmovng => OpCode.twoByte(0x0f, 0x4c), - .vmovsd => OpCode.oneByte(0x10), - .vucomisd => OpCode.oneByte(0x2e), - else => null, + else => unreachable, }, .oi => return switch (tag) { .mov => OpCode.oneByte(if (is_one_byte) 0xb0 else 0xb8), - else => null, + else => unreachable, }, .fd => return switch (tag) { .mov => OpCode.oneByte(if (is_one_byte) 0xa0 else 0xa1), - else => null, + else => unreachable, }, .td => return switch (tag) { .mov => OpCode.oneByte(if (is_one_byte) 0xa2 else 0xa3), - else => null, + else => unreachable, }, .rmi => return switch (tag) { .imul => OpCode.oneByte(if (is_one_byte) 0x6b else 0x69), - else => null, + else => unreachable, }, .mv => return switch (tag) { - .vmovsd => OpCode.oneByte(0x11), - else => null, + .vmovsd, .vmovss => OpCode.oneByte(0x11), + else => unreachable, }, .vm => return switch (tag) { - .vmovsd => OpCode.oneByte(0x10), - .vucomisd => OpCode.oneByte(0x2e), - else => null, + .vmovsd, .vmovss => OpCode.oneByte(0x10), + .vucomisd, .vucomiss => OpCode.oneByte(0x2e), + else => unreachable, }, .rvm => return switch (tag) { - .vaddsd => OpCode.oneByte(0x58), - .vmovsd => OpCode.oneByte(0x10), - else => null, + .vaddsd, .vaddss => OpCode.oneByte(0x58), + .vmovsd, .vmovss => OpCode.oneByte(0x10), + else => unreachable, }, .rvmi => return switch (tag) { - .vcmpsd => OpCode.oneByte(0xc2), - else => null, + .vcmpsd, .vcmpss => OpCode.oneByte(0xc2), + else => unreachable, }, } } -inline fn getModRmExt(tag: Tag) ?u3 { +inline fn getModRmExt(tag: Tag) u3 { return switch (tag) { .adc => 0x2, .add => 0x0, @@ -1631,11 +1616,11 @@ inline fn getModRmExt(tag: Tag) ?u3 { .fisttp64 => 0x1, .fld32 => 0x0, .fld64 => 0x0, - else => null, + else => unreachable, }; } -const VexPrefix = struct { +const VexEncoding = struct { prefix: Encoder.Vex, reg: ?enum { ndd, @@ -1644,7 +1629,7 @@ const VexPrefix = struct { }, }; -inline fn getVexPrefix(tag: Tag, enc: Encoding) ?VexPrefix { +inline fn getVexEncoding(tag: Tag, enc: Encoding) VexEncoding { const desc: struct { reg: enum { none, @@ -1671,21 +1656,27 @@ inline fn getVexPrefix(tag: Tag, enc: Encoding) ?VexPrefix { switch (enc) { .mv => switch (tag) { .vmovsd => break :blk .{ .lig = true, .simd_prefix = .p_f2, .wig = true }, - else => return null, + .vmovss => break :blk .{ .lig = true, .simd_prefix = .p_f3, .wig = true }, + else => unreachable, }, .vm => switch (tag) { .vmovsd => break :blk .{ .lig = true, .simd_prefix = .p_f2, .wig = true }, + .vmovss => break :blk .{ .lig = true, .simd_prefix = .p_f3, .wig = true }, .vucomisd => break :blk .{ .lig = true, .simd_prefix = .p_66, .wig = true }, - else => return null, + .vucomiss => break :blk .{ .lig = true, .wig = true }, + else => unreachable, }, .rvm => switch (tag) { .vaddsd => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f2, .wig = true }, + .vaddss => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f3, .wig = true }, .vmovsd => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f2, .wig = true }, - else => return null, + .vmovss => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f3, .wig = true }, + else => unreachable, }, .rvmi => switch (tag) { .vcmpsd => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f2, .wig = true }, - else => return null, + .vcmpss => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f3, .wig = true }, + else => unreachable, }, else => unreachable, } @@ -1711,7 +1702,7 @@ inline fn getVexPrefix(tag: Tag, enc: Encoding) ?VexPrefix { .p_f3 => vex.simd_prefix_f3(), } - return VexPrefix{ .prefix = vex, .reg = switch (desc.reg) { + return VexEncoding{ .prefix = vex, .reg = switch (desc.reg) { .none => null, .nds => .nds, .dds => .dds, @@ -1862,7 +1853,7 @@ const RegisterOrMemory = union(enum) { fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) InnerError!void { assert(!tag.isAvx()); - const opc = getOpCode(tag, .zo, false).?; + const opc = getOpCode(tag, .zo, false); const encoder = try Encoder.init(code, 2); switch (tag) { .cqo => { @@ -1879,12 +1870,12 @@ fn lowerToIEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void { assert(!tag.isAvx()); if (tag == .ret_far or tag == .ret_near) { const encoder = try Encoder.init(code, 3); - const opc = getOpCode(tag, .i, false).?; + const opc = getOpCode(tag, .i, false); opc.encode(encoder); encoder.imm16(@bitCast(i16, @truncate(u16, imm))); return; } - const opc = getOpCode(tag, .i, immOpSize(imm) == 8).?; + const opc = getOpCode(tag, .i, immOpSize(imm) == 8); const encoder = try Encoder.init(code, 5); if (immOpSize(imm) == 16) { encoder.prefix16BitMode(); @@ -1895,7 +1886,7 @@ fn lowerToIEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void { fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!void { assert(!tag.isAvx()); - const opc = getOpCode(tag, .o, false).?; + const opc = getOpCode(tag, .o, false); const encoder = try Encoder.init(code, 3); if (reg.size() == 16) { encoder.prefix16BitMode(); @@ -1909,7 +1900,7 @@ fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!voi fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void { assert(!tag.isAvx()); - const opc = getOpCode(tag, .d, false).?; + const opc = getOpCode(tag, .d, false); const encoder = try Encoder.init(code, 6); opc.encode(encoder); encoder.imm32(@bitCast(i32, imm)); @@ -1917,8 +1908,8 @@ fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void { fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *std.ArrayList(u8)) InnerError!void { assert(!tag.isAvx()); - const opc = getOpCode(tag, enc, reg_or_mem.size() == 8).?; - const modrm_ext = getModRmExt(tag).?; + const opc = getOpCode(tag, enc, reg_or_mem.size() == 8); + const modrm_ext = getModRmExt(tag); switch (reg_or_mem) { .register => |reg| { const encoder = try Encoder.init(code, 4); @@ -1973,10 +1964,7 @@ fn lowerToFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8)) I fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8), td: bool) InnerError!void { assert(!tag.isAvx()); - const opc = if (td) - getOpCode(tag, .td, reg.size() == 8).? - else - getOpCode(tag, .fd, reg.size() == 8).?; + const opc = if (td) getOpCode(tag, .td, reg.size() == 8) else getOpCode(tag, .fd, reg.size() == 8); const encoder = try Encoder.init(code, 10); if (reg.size() == 16) { encoder.prefix16BitMode(); @@ -1996,7 +1984,7 @@ fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8), fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) InnerError!void { assert(!tag.isAvx()); - const opc = getOpCode(tag, .oi, reg.size() == 8).?; + const opc = getOpCode(tag, .oi, reg.size() == 8); const encoder = try Encoder.init(code, 10); if (reg.size() == 16) { encoder.prefix16BitMode(); @@ -2023,8 +2011,8 @@ fn lowerToMiXEnc( code: *std.ArrayList(u8), ) InnerError!void { assert(!tag.isAvx()); - const modrm_ext = getModRmExt(tag).?; - const opc = getOpCode(tag, enc, reg_or_mem.size() == 8).?; + const modrm_ext = getModRmExt(tag); + const opc = getOpCode(tag, enc, reg_or_mem.size() == 8); switch (reg_or_mem) { .register => |dst_reg| { const encoder = try Encoder.init(code, 7); @@ -2079,7 +2067,7 @@ fn lowerToRmEnc( code: *std.ArrayList(u8), ) InnerError!void { assert(!tag.isAvx()); - const opc = getOpCode(tag, .rm, reg.size() == 8 or reg_or_mem.size() == 8).?; + const opc = getOpCode(tag, .rm, reg.size() == 8 or reg_or_mem.size() == 8); switch (reg_or_mem) { .register => |src_reg| { const encoder = try Encoder.init(code, 5); @@ -2126,7 +2114,7 @@ fn lowerToMrEnc( code: *std.ArrayList(u8), ) InnerError!void { assert(!tag.isAvx()); - const opc = getOpCode(tag, .mr, reg.size() == 8 or reg_or_mem.size() == 8).?; + const opc = getOpCode(tag, .mr, reg.size() == 8 or reg_or_mem.size() == 8); switch (reg_or_mem) { .register => |dst_reg| { const encoder = try Encoder.init(code, 4); @@ -2172,7 +2160,7 @@ fn lowerToRmiEnc( code: *std.ArrayList(u8), ) InnerError!void { assert(!tag.isAvx()); - const opc = getOpCode(tag, .rmi, false).?; + const opc = getOpCode(tag, .rmi, false); const encoder = try Encoder.init(code, 13); if (reg.size() == 16) { encoder.prefix16BitMode(); @@ -2216,9 +2204,9 @@ fn lowerToVmEnc( reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8), ) InnerError!void { - const opc = getOpCode(tag, .vm, false).?; - var vex_prefix = getVexPrefix(tag, .vm).?; - const vex = &vex_prefix.prefix; + const opc = getOpCode(tag, .vm, false); + var enc = getVexEncoding(tag, .vm); + const vex = &enc.prefix; switch (reg_or_mem) { .register => |src_reg| { const encoder = try Encoder.init(code, 5); @@ -2226,12 +2214,11 @@ fn lowerToVmEnc( .r = reg.isExtended(), .b = src_reg.isExtended(), }); - encoder.vex(vex_prefix.prefix); + encoder.vex(enc.prefix); opc.encode(encoder); encoder.modRm_direct(reg.lowEnc(), src_reg.lowEnc()); }, .memory => |src_mem| { - assert(src_mem.ptr_size == .qword_ptr); const encoder = try Encoder.init(code, 10); if (src_mem.base) |base| { vex.rex(.{ @@ -2243,7 +2230,7 @@ fn lowerToVmEnc( .r = reg.isExtended(), }); } - encoder.vex(vex_prefix.prefix); + encoder.vex(enc.prefix); opc.encode(encoder); src_mem.encode(encoder, reg.lowEnc()); }, @@ -2257,9 +2244,9 @@ fn lowerToMvEnc( reg: Register, code: *std.ArrayList(u8), ) InnerError!void { - const opc = getOpCode(tag, .mv, false).?; - var vex_prefix = getVexPrefix(tag, .mv).?; - const vex = &vex_prefix.prefix; + const opc = getOpCode(tag, .mv, false); + var enc = getVexEncoding(tag, .mv); + const vex = &enc.prefix; switch (reg_or_mem) { .register => |dst_reg| { const encoder = try Encoder.init(code, 4); @@ -2267,12 +2254,11 @@ fn lowerToMvEnc( .r = reg.isExtended(), .b = dst_reg.isExtended(), }); - encoder.vex(vex_prefix.prefix); + encoder.vex(enc.prefix); opc.encode(encoder); encoder.modRm_direct(reg.lowEnc(), dst_reg.lowEnc()); }, .memory => |dst_mem| { - assert(dst_mem.ptr_size == .qword_ptr); const encoder = try Encoder.init(code, 10); if (dst_mem.base) |base| { vex.rex(.{ @@ -2284,7 +2270,7 @@ fn lowerToMvEnc( .r = reg.isExtended(), }); } - encoder.vex(vex_prefix.prefix); + encoder.vex(enc.prefix); opc.encode(encoder); dst_mem.encode(encoder, reg.lowEnc()); }, @@ -2298,12 +2284,12 @@ fn lowerToRvmEnc( reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8), ) InnerError!void { - const opc = getOpCode(tag, .rvm, false).?; - var vex_prefix = getVexPrefix(tag, .rvm).?; - const vex = &vex_prefix.prefix; + const opc = getOpCode(tag, .rvm, false); + var enc = getVexEncoding(tag, .rvm); + const vex = &enc.prefix; switch (reg_or_mem) { .register => |reg3| { - if (vex_prefix.reg) |vvvv| { + if (enc.reg) |vvvv| { switch (vvvv) { .nds => vex.reg(reg2.enc()), else => unreachable, // TODO @@ -2314,7 +2300,7 @@ fn lowerToRvmEnc( .r = reg1.isExtended(), .b = reg3.isExtended(), }); - encoder.vex(vex_prefix.prefix); + encoder.vex(enc.prefix); opc.encode(encoder); encoder.modRm_direct(reg1.lowEnc(), reg3.lowEnc()); }, @@ -2333,13 +2319,13 @@ fn lowerToRvmiEnc( imm: u32, code: *std.ArrayList(u8), ) InnerError!void { - const opc = getOpCode(tag, .rvmi, false).?; - var vex_prefix = getVexPrefix(tag, .rvmi).?; - const vex = &vex_prefix.prefix; + const opc = getOpCode(tag, .rvmi, false); + var enc = getVexEncoding(tag, .rvmi); + const vex = &enc.prefix; const encoder: Encoder = blk: { switch (reg_or_mem) { .register => |reg3| { - if (vex_prefix.reg) |vvvv| { + if (enc.reg) |vvvv| { switch (vvvv) { .nds => vex.reg(reg2.enc()), else => unreachable, // TODO @@ -2350,7 +2336,7 @@ fn lowerToRvmiEnc( .r = reg1.isExtended(), .b = reg3.isExtended(), }); - encoder.vex(vex_prefix.prefix); + encoder.vex(enc.prefix); opc.encode(encoder); encoder.modRm_direct(reg1.lowEnc(), reg3.lowEnc()); break :blk encoder; diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index a1062ba6b4..dc8c1fa0b2 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -351,14 +351,17 @@ pub const Inst = struct { /// 0b01 qword ptr [reg1 + imm32], reg2 /// 0b10 reg1, reg2 mov_f64, + mov_f32, /// ops flags: form: /// 0b00 reg1, reg1, reg2 add_f64, + add_f32, /// ops flags: form: /// cmp_f64, + cmp_f32, /// Pseudo-instructions /// call extern function diff --git a/test/behavior.zig b/test/behavior.zig index 4d8b3587bc..cea8ded1ba 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,163 +1,163 @@ const builtin = @import("builtin"); test { - _ = @import("behavior/align.zig"); - _ = @import("behavior/alignof.zig"); - _ = @import("behavior/array.zig"); - _ = @import("behavior/async_fn.zig"); - _ = @import("behavior/atomics.zig"); - _ = @import("behavior/await_struct.zig"); + // _ = @import("behavior/align.zig"); + // _ = @import("behavior/alignof.zig"); + // _ = @import("behavior/array.zig"); + // _ = @import("behavior/async_fn.zig"); + // _ = @import("behavior/atomics.zig"); + // _ = @import("behavior/await_struct.zig"); _ = @import("behavior/basic.zig"); - _ = @import("behavior/bit_shifting.zig"); - _ = @import("behavior/bitcast.zig"); - _ = @import("behavior/bitreverse.zig"); - _ = @import("behavior/bool.zig"); - _ = @import("behavior/bugs/394.zig"); - _ = @import("behavior/bugs/421.zig"); - _ = @import("behavior/bugs/529.zig"); - _ = @import("behavior/bugs/624.zig"); - _ = @import("behavior/bugs/655.zig"); - _ = @import("behavior/bugs/656.zig"); - _ = @import("behavior/bugs/679.zig"); - _ = @import("behavior/bugs/704.zig"); - _ = @import("behavior/bugs/718.zig"); - _ = @import("behavior/bugs/726.zig"); - _ = @import("behavior/bugs/828.zig"); - _ = @import("behavior/bugs/920.zig"); - _ = @import("behavior/bugs/1025.zig"); - _ = @import("behavior/bugs/1076.zig"); - _ = @import("behavior/bugs/1111.zig"); - _ = @import("behavior/bugs/1120.zig"); - _ = @import("behavior/bugs/1277.zig"); - _ = @import("behavior/bugs/1310.zig"); - _ = @import("behavior/bugs/1381.zig"); - _ = @import("behavior/bugs/1421.zig"); - _ = @import("behavior/bugs/1442.zig"); - _ = @import("behavior/bugs/1486.zig"); - _ = @import("behavior/bugs/1500.zig"); - _ = @import("behavior/bugs/1607.zig"); - _ = @import("behavior/bugs/1735.zig"); - _ = @import("behavior/bugs/1741.zig"); - _ = @import("behavior/bugs/1851.zig"); - _ = @import("behavior/bugs/1914.zig"); - _ = @import("behavior/bugs/2006.zig"); - _ = @import("behavior/bugs/2114.zig"); - _ = @import("behavior/bugs/2346.zig"); - _ = @import("behavior/bugs/2578.zig"); - _ = @import("behavior/bugs/2692.zig"); - _ = @import("behavior/bugs/2889.zig"); - _ = @import("behavior/bugs/3007.zig"); - _ = @import("behavior/bugs/3046.zig"); - _ = @import("behavior/bugs/3112.zig"); - _ = @import("behavior/bugs/3367.zig"); - _ = @import("behavior/bugs/3384.zig"); - _ = @import("behavior/bugs/3586.zig"); - _ = @import("behavior/bugs/3742.zig"); - _ = @import("behavior/bugs/3779.zig"); - _ = @import("behavior/bugs/4328.zig"); - _ = @import("behavior/bugs/4560.zig"); - _ = @import("behavior/bugs/4769_a.zig"); - _ = @import("behavior/bugs/4769_b.zig"); - _ = @import("behavior/bugs/4954.zig"); - _ = @import("behavior/bugs/5398.zig"); - _ = @import("behavior/bugs/5413.zig"); - _ = @import("behavior/bugs/5474.zig"); - _ = @import("behavior/bugs/5487.zig"); - _ = @import("behavior/bugs/6456.zig"); - _ = @import("behavior/bugs/6781.zig"); - _ = @import("behavior/bugs/6850.zig"); - _ = @import("behavior/bugs/7003.zig"); - _ = @import("behavior/bugs/7027.zig"); - _ = @import("behavior/bugs/7047.zig"); - _ = @import("behavior/bugs/7187.zig"); - _ = @import("behavior/bugs/7250.zig"); - _ = @import("behavior/bugs/9584.zig"); - _ = @import("behavior/bugs/10138.zig"); - _ = @import("behavior/bugs/10147.zig"); - _ = @import("behavior/bugs/10970.zig"); - _ = @import("behavior/bugs/11046.zig"); - _ = @import("behavior/bugs/11100.zig"); - _ = @import("behavior/bugs/11139.zig"); - _ = @import("behavior/bugs/11159.zig"); - _ = @import("behavior/bugs/11162.zig"); - _ = @import("behavior/bugs/11165.zig"); - _ = @import("behavior/bugs/11181.zig"); - _ = @import("behavior/bugs/11182.zig"); - _ = @import("behavior/bugs/11213.zig"); - _ = @import("behavior/byteswap.zig"); - _ = @import("behavior/byval_arg_var.zig"); - _ = @import("behavior/call.zig"); - _ = @import("behavior/cast.zig"); - _ = @import("behavior/cast_int.zig"); - _ = @import("behavior/comptime_memory.zig"); - _ = @import("behavior/const_slice_child.zig"); - _ = @import("behavior/defer.zig"); - _ = @import("behavior/enum.zig"); - _ = @import("behavior/error.zig"); - _ = @import("behavior/eval.zig"); - _ = @import("behavior/field_parent_ptr.zig"); - _ = @import("behavior/floatop.zig"); - _ = @import("behavior/fn.zig"); - _ = @import("behavior/fn_delegation.zig"); - _ = @import("behavior/fn_in_struct_in_comptime.zig"); - _ = @import("behavior/for.zig"); - _ = @import("behavior/generics.zig"); - _ = @import("behavior/hasdecl.zig"); - _ = @import("behavior/hasfield.zig"); - _ = @import("behavior/if.zig"); - _ = @import("behavior/import.zig"); - _ = @import("behavior/incomplete_struct_param_tld.zig"); - _ = @import("behavior/int128.zig"); - _ = @import("behavior/int_div.zig"); - _ = @import("behavior/inttoptr.zig"); - _ = @import("behavior/ir_block_deps.zig"); - _ = @import("behavior/math.zig"); - _ = @import("behavior/maximum_minimum.zig"); - _ = @import("behavior/member_func.zig"); - _ = @import("behavior/merge_error_sets.zig"); - _ = @import("behavior/muladd.zig"); - _ = @import("behavior/namespace_depends_on_compile_var.zig"); - _ = @import("behavior/null.zig"); - _ = @import("behavior/optional.zig"); - _ = @import("behavior/pointers.zig"); - _ = @import("behavior/popcount.zig"); - _ = @import("behavior/prefetch.zig"); - _ = @import("behavior/ptrcast.zig"); - _ = @import("behavior/pub_enum.zig"); - _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); - _ = @import("behavior/reflection.zig"); - _ = @import("behavior/saturating_arithmetic.zig"); - _ = @import("behavior/select.zig"); - _ = @import("behavior/shuffle.zig"); - _ = @import("behavior/sizeof_and_typeof.zig"); - _ = @import("behavior/slice.zig"); - _ = @import("behavior/slice_sentinel_comptime.zig"); - _ = @import("behavior/src.zig"); - _ = @import("behavior/struct.zig"); - _ = @import("behavior/packed-struct.zig"); - _ = @import("behavior/struct_contains_null_ptr_itself.zig"); - _ = @import("behavior/struct_contains_slice_of_itself.zig"); - _ = @import("behavior/switch.zig"); - _ = @import("behavior/switch_prong_err_enum.zig"); - _ = @import("behavior/switch_prong_implicit_cast.zig"); - _ = @import("behavior/this.zig"); - _ = @import("behavior/translate_c_macros.zig"); - _ = @import("behavior/truncate.zig"); - _ = @import("behavior/try.zig"); - _ = @import("behavior/tuple.zig"); - _ = @import("behavior/type.zig"); - _ = @import("behavior/type_info.zig"); - _ = @import("behavior/typename.zig"); - _ = @import("behavior/undefined.zig"); - _ = @import("behavior/underscore.zig"); - _ = @import("behavior/union.zig"); - _ = @import("behavior/union_with_members.zig"); - _ = @import("behavior/usingnamespace.zig"); - _ = @import("behavior/var_args.zig"); - _ = @import("behavior/vector.zig"); - _ = @import("behavior/void.zig"); - _ = @import("behavior/while.zig"); - _ = @import("behavior/widening.zig"); + // _ = @import("behavior/bit_shifting.zig"); + // _ = @import("behavior/bitcast.zig"); + // _ = @import("behavior/bitreverse.zig"); + // _ = @import("behavior/bool.zig"); + // _ = @import("behavior/bugs/394.zig"); + // _ = @import("behavior/bugs/421.zig"); + // _ = @import("behavior/bugs/529.zig"); + // _ = @import("behavior/bugs/624.zig"); + // _ = @import("behavior/bugs/655.zig"); + // _ = @import("behavior/bugs/656.zig"); + // _ = @import("behavior/bugs/679.zig"); + // _ = @import("behavior/bugs/704.zig"); + // _ = @import("behavior/bugs/718.zig"); + // _ = @import("behavior/bugs/726.zig"); + // _ = @import("behavior/bugs/828.zig"); + // _ = @import("behavior/bugs/920.zig"); + // _ = @import("behavior/bugs/1025.zig"); + // _ = @import("behavior/bugs/1076.zig"); + // _ = @import("behavior/bugs/1111.zig"); + // _ = @import("behavior/bugs/1120.zig"); + // _ = @import("behavior/bugs/1277.zig"); + // _ = @import("behavior/bugs/1310.zig"); + // _ = @import("behavior/bugs/1381.zig"); + // _ = @import("behavior/bugs/1421.zig"); + // _ = @import("behavior/bugs/1442.zig"); + // _ = @import("behavior/bugs/1486.zig"); + // _ = @import("behavior/bugs/1500.zig"); + // _ = @import("behavior/bugs/1607.zig"); + // _ = @import("behavior/bugs/1735.zig"); + // _ = @import("behavior/bugs/1741.zig"); + // _ = @import("behavior/bugs/1851.zig"); + // _ = @import("behavior/bugs/1914.zig"); + // _ = @import("behavior/bugs/2006.zig"); + // _ = @import("behavior/bugs/2114.zig"); + // _ = @import("behavior/bugs/2346.zig"); + // _ = @import("behavior/bugs/2578.zig"); + // _ = @import("behavior/bugs/2692.zig"); + // _ = @import("behavior/bugs/2889.zig"); + // _ = @import("behavior/bugs/3007.zig"); + // _ = @import("behavior/bugs/3046.zig"); + // _ = @import("behavior/bugs/3112.zig"); + // _ = @import("behavior/bugs/3367.zig"); + // _ = @import("behavior/bugs/3384.zig"); + // _ = @import("behavior/bugs/3586.zig"); + // _ = @import("behavior/bugs/3742.zig"); + // _ = @import("behavior/bugs/3779.zig"); + // _ = @import("behavior/bugs/4328.zig"); + // _ = @import("behavior/bugs/4560.zig"); + // _ = @import("behavior/bugs/4769_a.zig"); + // _ = @import("behavior/bugs/4769_b.zig"); + // _ = @import("behavior/bugs/4954.zig"); + // _ = @import("behavior/bugs/5398.zig"); + // _ = @import("behavior/bugs/5413.zig"); + // _ = @import("behavior/bugs/5474.zig"); + // _ = @import("behavior/bugs/5487.zig"); + // _ = @import("behavior/bugs/6456.zig"); + // _ = @import("behavior/bugs/6781.zig"); + // _ = @import("behavior/bugs/6850.zig"); + // _ = @import("behavior/bugs/7003.zig"); + // _ = @import("behavior/bugs/7027.zig"); + // _ = @import("behavior/bugs/7047.zig"); + // _ = @import("behavior/bugs/7187.zig"); + // _ = @import("behavior/bugs/7250.zig"); + // _ = @import("behavior/bugs/9584.zig"); + // _ = @import("behavior/bugs/10138.zig"); + // _ = @import("behavior/bugs/10147.zig"); + // _ = @import("behavior/bugs/10970.zig"); + // _ = @import("behavior/bugs/11046.zig"); + // _ = @import("behavior/bugs/11100.zig"); + // _ = @import("behavior/bugs/11139.zig"); + // _ = @import("behavior/bugs/11159.zig"); + // _ = @import("behavior/bugs/11162.zig"); + // _ = @import("behavior/bugs/11165.zig"); + // _ = @import("behavior/bugs/11181.zig"); + // _ = @import("behavior/bugs/11182.zig"); + // _ = @import("behavior/bugs/11213.zig"); + // _ = @import("behavior/byteswap.zig"); + // _ = @import("behavior/byval_arg_var.zig"); + // _ = @import("behavior/call.zig"); + // _ = @import("behavior/cast.zig"); + // _ = @import("behavior/cast_int.zig"); + // _ = @import("behavior/comptime_memory.zig"); + // _ = @import("behavior/const_slice_child.zig"); + // _ = @import("behavior/defer.zig"); + // _ = @import("behavior/enum.zig"); + // _ = @import("behavior/error.zig"); + // _ = @import("behavior/eval.zig"); + // _ = @import("behavior/field_parent_ptr.zig"); + // _ = @import("behavior/floatop.zig"); + // _ = @import("behavior/fn.zig"); + // _ = @import("behavior/fn_delegation.zig"); + // _ = @import("behavior/fn_in_struct_in_comptime.zig"); + // _ = @import("behavior/for.zig"); + // _ = @import("behavior/generics.zig"); + // _ = @import("behavior/hasdecl.zig"); + // _ = @import("behavior/hasfield.zig"); + // _ = @import("behavior/if.zig"); + // _ = @import("behavior/import.zig"); + // _ = @import("behavior/incomplete_struct_param_tld.zig"); + // _ = @import("behavior/int128.zig"); + // _ = @import("behavior/int_div.zig"); + // _ = @import("behavior/inttoptr.zig"); + // _ = @import("behavior/ir_block_deps.zig"); + // _ = @import("behavior/math.zig"); + // _ = @import("behavior/maximum_minimum.zig"); + // _ = @import("behavior/member_func.zig"); + // _ = @import("behavior/merge_error_sets.zig"); + // _ = @import("behavior/muladd.zig"); + // _ = @import("behavior/namespace_depends_on_compile_var.zig"); + // _ = @import("behavior/null.zig"); + // _ = @import("behavior/optional.zig"); + // _ = @import("behavior/pointers.zig"); + // _ = @import("behavior/popcount.zig"); + // _ = @import("behavior/prefetch.zig"); + // _ = @import("behavior/ptrcast.zig"); + // _ = @import("behavior/pub_enum.zig"); + // _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); + // _ = @import("behavior/reflection.zig"); + // _ = @import("behavior/saturating_arithmetic.zig"); + // _ = @import("behavior/select.zig"); + // _ = @import("behavior/shuffle.zig"); + // _ = @import("behavior/sizeof_and_typeof.zig"); + // _ = @import("behavior/slice.zig"); + // _ = @import("behavior/slice_sentinel_comptime.zig"); + // _ = @import("behavior/src.zig"); + // _ = @import("behavior/struct.zig"); + // _ = @import("behavior/packed-struct.zig"); + // _ = @import("behavior/struct_contains_null_ptr_itself.zig"); + // _ = @import("behavior/struct_contains_slice_of_itself.zig"); + // _ = @import("behavior/switch.zig"); + // _ = @import("behavior/switch_prong_err_enum.zig"); + // _ = @import("behavior/switch_prong_implicit_cast.zig"); + // _ = @import("behavior/this.zig"); + // _ = @import("behavior/translate_c_macros.zig"); + // _ = @import("behavior/truncate.zig"); + // _ = @import("behavior/try.zig"); + // _ = @import("behavior/tuple.zig"); + // _ = @import("behavior/type.zig"); + // _ = @import("behavior/type_info.zig"); + // _ = @import("behavior/typename.zig"); + // _ = @import("behavior/undefined.zig"); + // _ = @import("behavior/underscore.zig"); + // _ = @import("behavior/union.zig"); + // _ = @import("behavior/union_with_members.zig"); + // _ = @import("behavior/usingnamespace.zig"); + // _ = @import("behavior/var_args.zig"); + // _ = @import("behavior/vector.zig"); + // _ = @import("behavior/void.zig"); + // _ = @import("behavior/while.zig"); + // _ = @import("behavior/widening.zig"); if (builtin.stage2_arch == .wasm32) { _ = @import("behavior/wasm.zig"); From 6d32498c5522b3385d8adeb55f94f8f56ac108b3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 18 May 2022 17:37:17 +0200 Subject: [PATCH 1583/2031] x64: re-enable behavior tests --- src/arch/x86_64/CodeGen.zig | 42 +++-- test/behavior.zig | 312 ++++++++++++++++++------------------ test/behavior/basic.zig | 3 + test/behavior/math.zig | 1 - test/behavior/union.zig | 1 + 5 files changed, 193 insertions(+), 166 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 7896a2f3b7..0cae799e02 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -5325,15 +5325,39 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE }); }, .register => |reg| { - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = .rsp, - .reg2 = registerAlias(reg, @intCast(u32, abi_size)), - .flags = 0b10, - }), - .data = .{ .imm = @bitCast(u32, -stack_offset) }, - }); + switch (ty.zigTypeTag()) { + .Float => { + const tag: Mir.Inst.Tag = switch (ty.tag()) { + .f32 => .mov_f32, + .f64 => .mov_f64, + else => return self.fail("TODO genSetStackArg for register for type {}", .{ty.fmtDebug()}), + }; + _ = try self.addInst(.{ + .tag = tag, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = switch (ty.tag()) { + .f32 => .esp, + .f64 => .rsp, + else => unreachable, + }, + .reg2 = reg.to128(), + .flags = 0b01, + }), + .data = .{ .imm = @bitCast(u32, -stack_offset) }, + }); + }, + else => { + _ = try self.addInst(.{ + .tag = .mov, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = .rsp, + .reg2 = registerAlias(reg, @intCast(u32, abi_size)), + .flags = 0b10, + }), + .data = .{ .imm = @bitCast(u32, -stack_offset) }, + }); + }, + } }, .ptr_stack_offset => { const reg = try self.copyToTmpRegister(ty, mcv); diff --git a/test/behavior.zig b/test/behavior.zig index cea8ded1ba..4d8b3587bc 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -1,163 +1,163 @@ const builtin = @import("builtin"); test { - // _ = @import("behavior/align.zig"); - // _ = @import("behavior/alignof.zig"); - // _ = @import("behavior/array.zig"); - // _ = @import("behavior/async_fn.zig"); - // _ = @import("behavior/atomics.zig"); - // _ = @import("behavior/await_struct.zig"); + _ = @import("behavior/align.zig"); + _ = @import("behavior/alignof.zig"); + _ = @import("behavior/array.zig"); + _ = @import("behavior/async_fn.zig"); + _ = @import("behavior/atomics.zig"); + _ = @import("behavior/await_struct.zig"); _ = @import("behavior/basic.zig"); - // _ = @import("behavior/bit_shifting.zig"); - // _ = @import("behavior/bitcast.zig"); - // _ = @import("behavior/bitreverse.zig"); - // _ = @import("behavior/bool.zig"); - // _ = @import("behavior/bugs/394.zig"); - // _ = @import("behavior/bugs/421.zig"); - // _ = @import("behavior/bugs/529.zig"); - // _ = @import("behavior/bugs/624.zig"); - // _ = @import("behavior/bugs/655.zig"); - // _ = @import("behavior/bugs/656.zig"); - // _ = @import("behavior/bugs/679.zig"); - // _ = @import("behavior/bugs/704.zig"); - // _ = @import("behavior/bugs/718.zig"); - // _ = @import("behavior/bugs/726.zig"); - // _ = @import("behavior/bugs/828.zig"); - // _ = @import("behavior/bugs/920.zig"); - // _ = @import("behavior/bugs/1025.zig"); - // _ = @import("behavior/bugs/1076.zig"); - // _ = @import("behavior/bugs/1111.zig"); - // _ = @import("behavior/bugs/1120.zig"); - // _ = @import("behavior/bugs/1277.zig"); - // _ = @import("behavior/bugs/1310.zig"); - // _ = @import("behavior/bugs/1381.zig"); - // _ = @import("behavior/bugs/1421.zig"); - // _ = @import("behavior/bugs/1442.zig"); - // _ = @import("behavior/bugs/1486.zig"); - // _ = @import("behavior/bugs/1500.zig"); - // _ = @import("behavior/bugs/1607.zig"); - // _ = @import("behavior/bugs/1735.zig"); - // _ = @import("behavior/bugs/1741.zig"); - // _ = @import("behavior/bugs/1851.zig"); - // _ = @import("behavior/bugs/1914.zig"); - // _ = @import("behavior/bugs/2006.zig"); - // _ = @import("behavior/bugs/2114.zig"); - // _ = @import("behavior/bugs/2346.zig"); - // _ = @import("behavior/bugs/2578.zig"); - // _ = @import("behavior/bugs/2692.zig"); - // _ = @import("behavior/bugs/2889.zig"); - // _ = @import("behavior/bugs/3007.zig"); - // _ = @import("behavior/bugs/3046.zig"); - // _ = @import("behavior/bugs/3112.zig"); - // _ = @import("behavior/bugs/3367.zig"); - // _ = @import("behavior/bugs/3384.zig"); - // _ = @import("behavior/bugs/3586.zig"); - // _ = @import("behavior/bugs/3742.zig"); - // _ = @import("behavior/bugs/3779.zig"); - // _ = @import("behavior/bugs/4328.zig"); - // _ = @import("behavior/bugs/4560.zig"); - // _ = @import("behavior/bugs/4769_a.zig"); - // _ = @import("behavior/bugs/4769_b.zig"); - // _ = @import("behavior/bugs/4954.zig"); - // _ = @import("behavior/bugs/5398.zig"); - // _ = @import("behavior/bugs/5413.zig"); - // _ = @import("behavior/bugs/5474.zig"); - // _ = @import("behavior/bugs/5487.zig"); - // _ = @import("behavior/bugs/6456.zig"); - // _ = @import("behavior/bugs/6781.zig"); - // _ = @import("behavior/bugs/6850.zig"); - // _ = @import("behavior/bugs/7003.zig"); - // _ = @import("behavior/bugs/7027.zig"); - // _ = @import("behavior/bugs/7047.zig"); - // _ = @import("behavior/bugs/7187.zig"); - // _ = @import("behavior/bugs/7250.zig"); - // _ = @import("behavior/bugs/9584.zig"); - // _ = @import("behavior/bugs/10138.zig"); - // _ = @import("behavior/bugs/10147.zig"); - // _ = @import("behavior/bugs/10970.zig"); - // _ = @import("behavior/bugs/11046.zig"); - // _ = @import("behavior/bugs/11100.zig"); - // _ = @import("behavior/bugs/11139.zig"); - // _ = @import("behavior/bugs/11159.zig"); - // _ = @import("behavior/bugs/11162.zig"); - // _ = @import("behavior/bugs/11165.zig"); - // _ = @import("behavior/bugs/11181.zig"); - // _ = @import("behavior/bugs/11182.zig"); - // _ = @import("behavior/bugs/11213.zig"); - // _ = @import("behavior/byteswap.zig"); - // _ = @import("behavior/byval_arg_var.zig"); - // _ = @import("behavior/call.zig"); - // _ = @import("behavior/cast.zig"); - // _ = @import("behavior/cast_int.zig"); - // _ = @import("behavior/comptime_memory.zig"); - // _ = @import("behavior/const_slice_child.zig"); - // _ = @import("behavior/defer.zig"); - // _ = @import("behavior/enum.zig"); - // _ = @import("behavior/error.zig"); - // _ = @import("behavior/eval.zig"); - // _ = @import("behavior/field_parent_ptr.zig"); - // _ = @import("behavior/floatop.zig"); - // _ = @import("behavior/fn.zig"); - // _ = @import("behavior/fn_delegation.zig"); - // _ = @import("behavior/fn_in_struct_in_comptime.zig"); - // _ = @import("behavior/for.zig"); - // _ = @import("behavior/generics.zig"); - // _ = @import("behavior/hasdecl.zig"); - // _ = @import("behavior/hasfield.zig"); - // _ = @import("behavior/if.zig"); - // _ = @import("behavior/import.zig"); - // _ = @import("behavior/incomplete_struct_param_tld.zig"); - // _ = @import("behavior/int128.zig"); - // _ = @import("behavior/int_div.zig"); - // _ = @import("behavior/inttoptr.zig"); - // _ = @import("behavior/ir_block_deps.zig"); - // _ = @import("behavior/math.zig"); - // _ = @import("behavior/maximum_minimum.zig"); - // _ = @import("behavior/member_func.zig"); - // _ = @import("behavior/merge_error_sets.zig"); - // _ = @import("behavior/muladd.zig"); - // _ = @import("behavior/namespace_depends_on_compile_var.zig"); - // _ = @import("behavior/null.zig"); - // _ = @import("behavior/optional.zig"); - // _ = @import("behavior/pointers.zig"); - // _ = @import("behavior/popcount.zig"); - // _ = @import("behavior/prefetch.zig"); - // _ = @import("behavior/ptrcast.zig"); - // _ = @import("behavior/pub_enum.zig"); - // _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); - // _ = @import("behavior/reflection.zig"); - // _ = @import("behavior/saturating_arithmetic.zig"); - // _ = @import("behavior/select.zig"); - // _ = @import("behavior/shuffle.zig"); - // _ = @import("behavior/sizeof_and_typeof.zig"); - // _ = @import("behavior/slice.zig"); - // _ = @import("behavior/slice_sentinel_comptime.zig"); - // _ = @import("behavior/src.zig"); - // _ = @import("behavior/struct.zig"); - // _ = @import("behavior/packed-struct.zig"); - // _ = @import("behavior/struct_contains_null_ptr_itself.zig"); - // _ = @import("behavior/struct_contains_slice_of_itself.zig"); - // _ = @import("behavior/switch.zig"); - // _ = @import("behavior/switch_prong_err_enum.zig"); - // _ = @import("behavior/switch_prong_implicit_cast.zig"); - // _ = @import("behavior/this.zig"); - // _ = @import("behavior/translate_c_macros.zig"); - // _ = @import("behavior/truncate.zig"); - // _ = @import("behavior/try.zig"); - // _ = @import("behavior/tuple.zig"); - // _ = @import("behavior/type.zig"); - // _ = @import("behavior/type_info.zig"); - // _ = @import("behavior/typename.zig"); - // _ = @import("behavior/undefined.zig"); - // _ = @import("behavior/underscore.zig"); - // _ = @import("behavior/union.zig"); - // _ = @import("behavior/union_with_members.zig"); - // _ = @import("behavior/usingnamespace.zig"); - // _ = @import("behavior/var_args.zig"); - // _ = @import("behavior/vector.zig"); - // _ = @import("behavior/void.zig"); - // _ = @import("behavior/while.zig"); - // _ = @import("behavior/widening.zig"); + _ = @import("behavior/bit_shifting.zig"); + _ = @import("behavior/bitcast.zig"); + _ = @import("behavior/bitreverse.zig"); + _ = @import("behavior/bool.zig"); + _ = @import("behavior/bugs/394.zig"); + _ = @import("behavior/bugs/421.zig"); + _ = @import("behavior/bugs/529.zig"); + _ = @import("behavior/bugs/624.zig"); + _ = @import("behavior/bugs/655.zig"); + _ = @import("behavior/bugs/656.zig"); + _ = @import("behavior/bugs/679.zig"); + _ = @import("behavior/bugs/704.zig"); + _ = @import("behavior/bugs/718.zig"); + _ = @import("behavior/bugs/726.zig"); + _ = @import("behavior/bugs/828.zig"); + _ = @import("behavior/bugs/920.zig"); + _ = @import("behavior/bugs/1025.zig"); + _ = @import("behavior/bugs/1076.zig"); + _ = @import("behavior/bugs/1111.zig"); + _ = @import("behavior/bugs/1120.zig"); + _ = @import("behavior/bugs/1277.zig"); + _ = @import("behavior/bugs/1310.zig"); + _ = @import("behavior/bugs/1381.zig"); + _ = @import("behavior/bugs/1421.zig"); + _ = @import("behavior/bugs/1442.zig"); + _ = @import("behavior/bugs/1486.zig"); + _ = @import("behavior/bugs/1500.zig"); + _ = @import("behavior/bugs/1607.zig"); + _ = @import("behavior/bugs/1735.zig"); + _ = @import("behavior/bugs/1741.zig"); + _ = @import("behavior/bugs/1851.zig"); + _ = @import("behavior/bugs/1914.zig"); + _ = @import("behavior/bugs/2006.zig"); + _ = @import("behavior/bugs/2114.zig"); + _ = @import("behavior/bugs/2346.zig"); + _ = @import("behavior/bugs/2578.zig"); + _ = @import("behavior/bugs/2692.zig"); + _ = @import("behavior/bugs/2889.zig"); + _ = @import("behavior/bugs/3007.zig"); + _ = @import("behavior/bugs/3046.zig"); + _ = @import("behavior/bugs/3112.zig"); + _ = @import("behavior/bugs/3367.zig"); + _ = @import("behavior/bugs/3384.zig"); + _ = @import("behavior/bugs/3586.zig"); + _ = @import("behavior/bugs/3742.zig"); + _ = @import("behavior/bugs/3779.zig"); + _ = @import("behavior/bugs/4328.zig"); + _ = @import("behavior/bugs/4560.zig"); + _ = @import("behavior/bugs/4769_a.zig"); + _ = @import("behavior/bugs/4769_b.zig"); + _ = @import("behavior/bugs/4954.zig"); + _ = @import("behavior/bugs/5398.zig"); + _ = @import("behavior/bugs/5413.zig"); + _ = @import("behavior/bugs/5474.zig"); + _ = @import("behavior/bugs/5487.zig"); + _ = @import("behavior/bugs/6456.zig"); + _ = @import("behavior/bugs/6781.zig"); + _ = @import("behavior/bugs/6850.zig"); + _ = @import("behavior/bugs/7003.zig"); + _ = @import("behavior/bugs/7027.zig"); + _ = @import("behavior/bugs/7047.zig"); + _ = @import("behavior/bugs/7187.zig"); + _ = @import("behavior/bugs/7250.zig"); + _ = @import("behavior/bugs/9584.zig"); + _ = @import("behavior/bugs/10138.zig"); + _ = @import("behavior/bugs/10147.zig"); + _ = @import("behavior/bugs/10970.zig"); + _ = @import("behavior/bugs/11046.zig"); + _ = @import("behavior/bugs/11100.zig"); + _ = @import("behavior/bugs/11139.zig"); + _ = @import("behavior/bugs/11159.zig"); + _ = @import("behavior/bugs/11162.zig"); + _ = @import("behavior/bugs/11165.zig"); + _ = @import("behavior/bugs/11181.zig"); + _ = @import("behavior/bugs/11182.zig"); + _ = @import("behavior/bugs/11213.zig"); + _ = @import("behavior/byteswap.zig"); + _ = @import("behavior/byval_arg_var.zig"); + _ = @import("behavior/call.zig"); + _ = @import("behavior/cast.zig"); + _ = @import("behavior/cast_int.zig"); + _ = @import("behavior/comptime_memory.zig"); + _ = @import("behavior/const_slice_child.zig"); + _ = @import("behavior/defer.zig"); + _ = @import("behavior/enum.zig"); + _ = @import("behavior/error.zig"); + _ = @import("behavior/eval.zig"); + _ = @import("behavior/field_parent_ptr.zig"); + _ = @import("behavior/floatop.zig"); + _ = @import("behavior/fn.zig"); + _ = @import("behavior/fn_delegation.zig"); + _ = @import("behavior/fn_in_struct_in_comptime.zig"); + _ = @import("behavior/for.zig"); + _ = @import("behavior/generics.zig"); + _ = @import("behavior/hasdecl.zig"); + _ = @import("behavior/hasfield.zig"); + _ = @import("behavior/if.zig"); + _ = @import("behavior/import.zig"); + _ = @import("behavior/incomplete_struct_param_tld.zig"); + _ = @import("behavior/int128.zig"); + _ = @import("behavior/int_div.zig"); + _ = @import("behavior/inttoptr.zig"); + _ = @import("behavior/ir_block_deps.zig"); + _ = @import("behavior/math.zig"); + _ = @import("behavior/maximum_minimum.zig"); + _ = @import("behavior/member_func.zig"); + _ = @import("behavior/merge_error_sets.zig"); + _ = @import("behavior/muladd.zig"); + _ = @import("behavior/namespace_depends_on_compile_var.zig"); + _ = @import("behavior/null.zig"); + _ = @import("behavior/optional.zig"); + _ = @import("behavior/pointers.zig"); + _ = @import("behavior/popcount.zig"); + _ = @import("behavior/prefetch.zig"); + _ = @import("behavior/ptrcast.zig"); + _ = @import("behavior/pub_enum.zig"); + _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); + _ = @import("behavior/reflection.zig"); + _ = @import("behavior/saturating_arithmetic.zig"); + _ = @import("behavior/select.zig"); + _ = @import("behavior/shuffle.zig"); + _ = @import("behavior/sizeof_and_typeof.zig"); + _ = @import("behavior/slice.zig"); + _ = @import("behavior/slice_sentinel_comptime.zig"); + _ = @import("behavior/src.zig"); + _ = @import("behavior/struct.zig"); + _ = @import("behavior/packed-struct.zig"); + _ = @import("behavior/struct_contains_null_ptr_itself.zig"); + _ = @import("behavior/struct_contains_slice_of_itself.zig"); + _ = @import("behavior/switch.zig"); + _ = @import("behavior/switch_prong_err_enum.zig"); + _ = @import("behavior/switch_prong_implicit_cast.zig"); + _ = @import("behavior/this.zig"); + _ = @import("behavior/translate_c_macros.zig"); + _ = @import("behavior/truncate.zig"); + _ = @import("behavior/try.zig"); + _ = @import("behavior/tuple.zig"); + _ = @import("behavior/type.zig"); + _ = @import("behavior/type_info.zig"); + _ = @import("behavior/typename.zig"); + _ = @import("behavior/undefined.zig"); + _ = @import("behavior/underscore.zig"); + _ = @import("behavior/union.zig"); + _ = @import("behavior/union_with_members.zig"); + _ = @import("behavior/usingnamespace.zig"); + _ = @import("behavior/var_args.zig"); + _ = @import("behavior/vector.zig"); + _ = @import("behavior/void.zig"); + _ = @import("behavior/while.zig"); + _ = @import("behavior/widening.zig"); if (builtin.stage2_arch == .wasm32) { _ = @import("behavior/wasm.zig"); diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 10e48c6c7b..d62ba75dee 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -402,6 +402,7 @@ fn testPointerToVoidReturnType2() *const void { test "array 2D const double ptr" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const rect_2d_vertexes = [_][1]f32{ @@ -414,6 +415,7 @@ test "array 2D const double ptr" { test "array 2D const double ptr with offset" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; const rect_2d_vertexes = [_][2]f32{ @@ -426,6 +428,7 @@ test "array 2D const double ptr with offset" { test "array 3D const double ptr with offset" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const rect_3d_vertexes = [_][2][2]f32{ diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 60c71010d4..3b73d93c01 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -198,7 +198,6 @@ test "const number literal" { const ten = 10; test "float equality" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 8315ea8a22..7f17ff50c6 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -882,6 +882,7 @@ test "extern union doesn't trigger field check at comptime" { test "anonymous union literal syntax" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const S = struct { const Number = union { From 080d138b9d06402a8fbb70b4addc90751c6b798e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 18 May 2022 17:58:05 +0200 Subject: [PATCH 1584/2031] x64: re-enable incremental tests --- src/arch/x86_64/bits.zig | 18 +++++++++--------- src/register_manager.zig | 26 +++++++++++++------------- src/test.zig | 18 +++++++++--------- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index a0ca774cae..64a067d050 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -696,8 +696,8 @@ test "Encoder helpers - general purpose registers" { }); encoder.opcode_2byte(0x0f, 0xaf); encoder.modRm_direct( - Register.eax.lowId(), - Register.edi.lowId(), + Register.eax.lowEnc(), + Register.edi.lowEnc(), ); try testing.expectEqualSlices(u8, &[_]u8{ 0x0f, 0xaf, 0xc7 }, code.items); @@ -716,8 +716,8 @@ test "Encoder helpers - general purpose registers" { }); encoder.opcode_1byte(0x89); encoder.modRm_direct( - Register.edi.lowId(), - Register.eax.lowId(), + Register.edi.lowEnc(), + Register.eax.lowEnc(), ); try testing.expectEqualSlices(u8, &[_]u8{ 0x89, 0xf8 }, code.items); @@ -743,7 +743,7 @@ test "Encoder helpers - general purpose registers" { encoder.opcode_1byte(0x81); encoder.modRm_direct( 0, - Register.rcx.lowId(), + Register.rcx.lowEnc(), ); encoder.imm32(2147483647); @@ -768,7 +768,7 @@ test "Encoder helpers - Vex prefix" { { stream.reset(); var vex_prefix = Encoder.Vex{}; - vex_prefix.reg(Register.xmm15.id()); + vex_prefix.reg(Register.xmm15.enc()); const nwritten = vex_prefix.write(writer); try testing.expectEqualSlices(u8, &[_]u8{ 0xc5, 0x80 }, buf[0..nwritten]); } @@ -808,7 +808,7 @@ test "Encoder helpers - Vex prefix" { vex.simd_prefix_66(); encoder.vex(vex); // use 64 bit operation encoder.opcode_1byte(0x28); - encoder.modRm_direct(0, Register.xmm1.lowId()); + encoder.modRm_direct(0, Register.xmm1.lowEnc()); try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0xF9, 0x28, 0xC1 }, code.items); } @@ -822,10 +822,10 @@ test "Encoder helpers - Vex prefix" { vex.simd_prefix_66(); vex.lead_opc_0f(); vex.rex(.{ .r = true }); - vex.reg(Register.xmm1.id()); + vex.reg(Register.xmm1.enc()); encoder.vex(vex); encoder.opcode_1byte(0x16); - encoder.modRm_RIPDisp32(Register.xmm13.lowId()); + encoder.modRm_RIPDisp32(Register.xmm13.lowEnc()); encoder.disp32(0); try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0x71, 0x16, 0x2D, 0x00, 0x00, 0x00, 0x00 }, code.items); } diff --git a/src/register_manager.zig b/src/register_manager.zig index fbdf3ef2c3..a5ab4b904b 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -430,9 +430,9 @@ test "tryAllocReg: no spilling" { const mock_instruction: Air.Inst.Index = 1; - try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(mock_instruction)); - try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(mock_instruction)); - try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(mock_instruction)); + try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(mock_instruction, .{})); + try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(mock_instruction, .{})); + try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(mock_instruction, .{})); try expect(function.register_manager.isRegAllocated(.r2)); try expect(function.register_manager.isRegAllocated(.r3)); @@ -458,16 +458,16 @@ test "allocReg: spilling" { const mock_instruction: Air.Inst.Index = 1; - try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction)); - try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); + try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction, .{})); + try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, .{})); // Spill a register - try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction)); + try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction, .{})); try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); // No spilling necessary function.register_manager.freeReg(.r3); - try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); + try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, .{})); try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); // Locked registers @@ -476,7 +476,7 @@ test "allocReg: spilling" { const lock = function.register_manager.lockReg(.r2); defer if (lock) |reg| function.register_manager.unlockReg(reg); - try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction)); + try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, .{})); } try expect(!function.register_manager.lockedRegsExist()); } @@ -489,7 +489,7 @@ test "tryAllocRegs" { }; defer function.deinit(); - try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?); + try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, .{}).?); try expect(function.register_manager.isRegAllocated(.r0)); try expect(function.register_manager.isRegAllocated(.r1)); @@ -504,7 +504,7 @@ test "tryAllocRegs" { const lock = function.register_manager.lockReg(.r1); defer if (lock) |reg| function.register_manager.unlockReg(reg); - try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?); + try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, .{}).?); } try expect(!function.register_manager.lockedRegsExist()); @@ -543,7 +543,7 @@ test "allocRegs: normal usage" { const lock = function.register_manager.lockReg(result_reg); defer if (lock) |reg| function.register_manager.unlockReg(reg); - const regs = try function.register_manager.allocRegs(2, .{ null, null }); + const regs = try function.register_manager.allocRegs(2, .{ null, null }, .{}); try function.genAdd(result_reg, regs[0], regs[1]); } } @@ -565,12 +565,12 @@ test "allocRegs: selectively reducing register pressure" { // Here, we don't defer unlock because we manually unlock // after genAdd - const regs = try function.register_manager.allocRegs(2, .{ null, null }); + const regs = try function.register_manager.allocRegs(2, .{ null, null }, .{}); try function.genAdd(result_reg, regs[0], regs[1]); function.register_manager.unlockReg(lock.?); - const extra_summand_reg = try function.register_manager.allocReg(null); + const extra_summand_reg = try function.register_manager.allocReg(null, .{}); try function.genAdd(result_reg, result_reg, extra_summand_reg); } } diff --git a/src/test.zig b/src/test.zig index b272d05718..a6537e77c7 100644 --- a/src/test.zig +++ b/src/test.zig @@ -34,18 +34,18 @@ test { var ctx = TestContext.init(std.testing.allocator, arena); defer ctx.deinit(); - // { - // const dir_path = try std.fs.path.join(arena, &.{ - // std.fs.path.dirname(@src().file).?, "..", "test", "cases", - // }); + { + const dir_path = try std.fs.path.join(arena, &.{ + std.fs.path.dirname(@src().file).?, "..", "test", "cases", + }); - // var dir = try std.fs.cwd().openDir(dir_path, .{ .iterate = true }); - // defer dir.close(); + var dir = try std.fs.cwd().openDir(dir_path, .{ .iterate = true }); + defer dir.close(); - // ctx.addTestCasesFromDir(dir); - // } + ctx.addTestCasesFromDir(dir); + } - // try @import("test_cases").addCases(&ctx); + try @import("test_cases").addCases(&ctx); try ctx.run(); } From 283f40e4e9c44986353ba8abcc760684e9adf6cc Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 19 May 2022 15:34:13 +0200 Subject: [PATCH 1585/2031] x64: use StaticBitSet instead of an integer internally in RegisterManager --- src/arch/x86_64/CodeGen.zig | 11 ++-- src/arch/x86_64/abi.zig | 37 +++++++++---- src/register_manager.zig | 100 +++++++++++++++++------------------- 3 files changed, 79 insertions(+), 69 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 0cae799e02..3109470620 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -21,7 +21,6 @@ const Emit = @import("Emit.zig"); const Liveness = @import("../../Liveness.zig"); const Mir = @import("Mir.zig"); const Module = @import("../../Module.zig"); -const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; const Target = std.Target; const Type = @import("../../type.zig").Type; const TypedValue = @import("../../TypedValue.zig"); @@ -32,15 +31,15 @@ const abi = @import("abi.zig"); const callee_preserved_regs = abi.callee_preserved_regs; const caller_preserved_regs = abi.caller_preserved_regs; -const allocatable_registers = abi.allocatable_registers; const c_abi_int_param_regs = abi.c_abi_int_param_regs; const c_abi_int_return_regs = abi.c_abi_int_return_regs; -const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers); + +const RegisterManager = abi.RegisterManager; const RegisterLock = RegisterManager.RegisterLock; const Register = bits.Register; -const RegisterClass = abi.RegisterClass; -const gp = RegisterClass.gp; -const avx = RegisterClass.avx; + +const gp = abi.RegisterClass.gp; +const avx = abi.RegisterClass.avx; const InnerError = error{ OutOfMemory, diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index 046948ff68..bf85f002d1 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -3,6 +3,7 @@ const Type = @import("../../type.zig").Type; const Target = std.Target; const assert = std.debug.assert; const Register = @import("bits.zig").Register; +const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; pub const Class = enum { integer, sse, sseup, x87, x87up, complex_x87, memory, none }; @@ -378,18 +379,34 @@ pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 }; /// the caller relinquishes control to a subroutine via call instruction (or similar). /// In other words, these registers are free to use by the callee. pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; -pub const avx_regs = [_]Register{ - .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7, - .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15, -}; -pub const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ avx_regs; pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; -// Masks for register manager -const FreeRegInt = std.meta.Int(.unsigned, allocatable_registers.len); -pub const RegisterClass = struct { - pub const gp: FreeRegInt = 0x3fff; - pub const avx: FreeRegInt = 0x3fff_c000; +const avx_regs = [_]Register{ + .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7, + .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15, +}; +const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ avx_regs; +pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers); + +// Register classes +const RegisterBitSet = RegisterManager.RegisterBitSet; +pub const RegisterClass = struct { + pub const gp: RegisterBitSet = blk: { + var set = RegisterBitSet.initEmpty(); + set.setRangeValue(.{ + .start = 0, + .end = caller_preserved_regs.len + callee_preserved_regs.len, + }, true); + break :blk set; + }; + pub const avx: RegisterBitSet = blk: { + var set = RegisterBitSet.initEmpty(); + set.setRangeValue(.{ + .start = caller_preserved_regs.len + callee_preserved_regs.len, + .end = allocatable_registers.len, + }, true); + break :blk set; + }; }; diff --git a/src/register_manager.zig b/src/register_manager.zig index a5ab4b904b..7b82253e22 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -4,6 +4,7 @@ const mem = std.mem; const assert = std.debug.assert; const Allocator = std.mem.Allocator; const Air = @import("Air.zig"); +const StaticBitSet = std.bit_set.StaticBitSet; const Type = @import("type.zig").Type; const Module = @import("Module.zig"); const expect = std.testing.expect; @@ -41,66 +42,54 @@ pub fn RegisterManager( registers: [tracked_registers.len]Air.Inst.Index = undefined, /// Tracks which registers are free (in which case the /// corresponding bit is set to 1) - free_registers: FreeRegInt = math.maxInt(FreeRegInt), + free_registers: RegisterBitSet = RegisterBitSet.initFull(), /// Tracks all registers allocated in the course of this /// function - allocated_registers: FreeRegInt = 0, + allocated_registers: RegisterBitSet = RegisterBitSet.initEmpty(), /// Tracks registers which are locked from being allocated - locked_registers: FreeRegInt = 0, + locked_registers: RegisterBitSet = RegisterBitSet.initEmpty(), const Self = @This(); - /// An integer whose bits represent all the registers and - /// whether they are free. - const FreeRegInt = std.meta.Int(.unsigned, tracked_registers.len); - const ShiftInt = math.Log2Int(FreeRegInt); + pub const RegisterBitSet = StaticBitSet(tracked_registers.len); fn getFunction(self: *Self) *Function { return @fieldParentPtr(Function, "register_manager", self); } - fn getRegisterMask(reg: Register) ?FreeRegInt { - const index = indexOfRegIntoTracked(reg) orelse return null; - const shift = @intCast(ShiftInt, index); - const mask = @as(FreeRegInt, 1) << shift; - return mask; - } - - fn excludeRegister(reg: Register, mask: FreeRegInt) bool { - const reg_mask = getRegisterMask(reg) orelse return true; - return reg_mask & mask == 0; - } - fn markRegAllocated(self: *Self, reg: Register) void { - const mask = getRegisterMask(reg) orelse return; - self.allocated_registers |= mask; + const index = indexOfRegIntoTracked(reg) orelse return; + self.allocated_registers.set(index); } fn markRegUsed(self: *Self, reg: Register) void { - const mask = getRegisterMask(reg) orelse return; - self.free_registers &= ~mask; + const index = indexOfRegIntoTracked(reg) orelse return; + self.free_registers.unset(index); } fn markRegFree(self: *Self, reg: Register) void { - const mask = getRegisterMask(reg) orelse return; - self.free_registers |= mask; + const index = indexOfRegIntoTracked(reg) orelse return; + self.free_registers.set(index); } - pub fn indexOfReg(comptime registers: []const Register, reg: Register) ?std.math.IntFittingRange(0, registers.len - 1) { + pub fn indexOfReg( + comptime registers: []const Register, + reg: Register, + ) ?std.math.IntFittingRange(0, registers.len - 1) { inline for (tracked_registers) |cpreg, i| { if (reg.id() == cpreg.id()) return i; } return null; } - pub fn indexOfRegIntoTracked(reg: Register) ?ShiftInt { + pub fn indexOfRegIntoTracked(reg: Register) ?RegisterBitSet.ShiftInt { return indexOfReg(tracked_registers, reg); } /// Returns true when this register is not tracked pub fn isRegFree(self: Self, reg: Register) bool { - const mask = getRegisterMask(reg) orelse return true; - return self.free_registers & mask != 0; + const index = indexOfRegIntoTracked(reg) orelse return true; + return self.free_registers.isSet(index); } /// Returns whether this register was allocated in the course @@ -108,16 +97,16 @@ pub fn RegisterManager( /// /// Returns false when this register is not tracked pub fn isRegAllocated(self: Self, reg: Register) bool { - const mask = getRegisterMask(reg) orelse return false; - return self.allocated_registers & mask != 0; + const index = indexOfRegIntoTracked(reg) orelse return false; + return self.allocated_registers.isSet(index); } /// Returns whether this register is locked /// /// Returns false when this register is not tracked pub fn isRegLocked(self: Self, reg: Register) bool { - const mask = getRegisterMask(reg) orelse return false; - return self.locked_registers & mask != 0; + const index = indexOfRegIntoTracked(reg) orelse return false; + return self.locked_registers.isSet(index); } pub const RegisterLock = struct { @@ -136,8 +125,8 @@ pub fn RegisterManager( log.debug(" register already locked", .{}); return null; } - const mask = getRegisterMask(reg) orelse return null; - self.locked_registers |= mask; + const index = indexOfRegIntoTracked(reg) orelse return null; + self.locked_registers.set(index); return RegisterLock{ .register = reg }; } @@ -146,8 +135,8 @@ pub fn RegisterManager( pub fn lockRegAssumeUnused(self: *Self, reg: Register) RegisterLock { log.debug("locking asserting free {}", .{reg}); assert(!self.isRegLocked(reg)); - const mask = getRegisterMask(reg) orelse unreachable; - self.locked_registers |= mask; + const index = indexOfRegIntoTracked(reg) orelse unreachable; + self.locked_registers.set(index); return RegisterLock{ .register = reg }; } @@ -169,17 +158,17 @@ pub fn RegisterManager( /// Call `lockReg` to obtain the lock first. pub fn unlockReg(self: *Self, lock: RegisterLock) void { log.debug("unlocking {}", .{lock.register}); - const mask = getRegisterMask(lock.register) orelse return; - self.locked_registers &= ~mask; + const index = indexOfRegIntoTracked(lock.register) orelse return; + self.locked_registers.unset(index); } /// Returns true when at least one register is locked pub fn lockedRegsExist(self: Self) bool { - return self.locked_registers != 0; + return self.locked_registers.count() > 0; } const AllocOpts = struct { - selector_mask: ?FreeRegInt = null, + selector_mask: ?RegisterBitSet = null, }; /// Allocates a specified number of registers, optionally @@ -193,17 +182,22 @@ pub fn RegisterManager( ) ?[count]Register { comptime assert(count > 0 and count <= tracked_registers.len); - const selector_mask = if (opts.selector_mask) |mask| mask else ~@as(FreeRegInt, 0); - const free_registers = self.free_registers & selector_mask; - const free_and_not_locked_registers = free_registers & ~self.locked_registers; - const free_and_not_locked_registers_count = @popCount(FreeRegInt, free_and_not_locked_registers); - if (free_and_not_locked_registers_count < count) return null; + const available_registers = opts.selector_mask orelse RegisterBitSet.initFull(); + + var free_and_not_locked_registers = self.free_registers; + free_and_not_locked_registers.setIntersection(available_registers); + + var unlocked_registers = self.locked_registers; + unlocked_registers.toggleAll(); + + free_and_not_locked_registers.setIntersection(unlocked_registers); + + if (free_and_not_locked_registers.count() < count) return null; var regs: [count]Register = undefined; var i: usize = 0; for (tracked_registers) |reg| { if (i >= count) break; - if (excludeRegister(reg, selector_mask)) continue; if (self.isRegLocked(reg)) continue; if (!self.isRegFree(reg)) continue; @@ -244,11 +238,12 @@ pub fn RegisterManager( ) AllocateRegistersError![count]Register { comptime assert(count > 0 and count <= tracked_registers.len); - const selector_mask = if (opts.selector_mask) |mask| mask else ~@as(FreeRegInt, 0); - const available_registers_count = @popCount(FreeRegInt, selector_mask); - const locked_registers = self.locked_registers & selector_mask; - const locked_registers_count = @popCount(FreeRegInt, locked_registers); - if (count > available_registers_count - locked_registers_count) return error.OutOfRegisters; + const available_registers = opts.selector_mask orelse RegisterBitSet.initFull(); + + var locked_registers = self.locked_registers; + locked_registers.setIntersection(available_registers); + + if (count > available_registers.count() - locked_registers.count()) return error.OutOfRegisters; const result = self.tryAllocRegs(count, insts, opts) orelse blk: { // We'll take over the first count registers. Spill @@ -258,7 +253,6 @@ pub fn RegisterManager( var i: usize = 0; for (tracked_registers) |reg| { if (i >= count) break; - if (excludeRegister(reg, selector_mask)) continue; if (self.isRegLocked(reg)) continue; regs[i] = reg; From 5cbfd5819e423cdc6b092d1eb687189fb204b075 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 19 May 2022 17:36:04 +0200 Subject: [PATCH 1586/2031] x64: check for floating-point intrinsics in codegen --- src/arch/x86_64/CodeGen.zig | 283 +++++++++++++++++++++--------------- src/arch/x86_64/Emit.zig | 12 +- src/arch/x86_64/Mir.zig | 12 +- src/arch/x86_64/abi.zig | 6 +- 4 files changed, 179 insertions(+), 134 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 3109470620..2e4a396c9f 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -39,7 +39,7 @@ const RegisterLock = RegisterManager.RegisterLock; const Register = bits.Register; const gp = abi.RegisterClass.gp; -const avx = abi.RegisterClass.avx; +const sse = abi.RegisterClass.sse; const InnerError = error{ OutOfMemory, @@ -881,15 +881,18 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { switch (elem_ty.zigTypeTag()) { .Vector => return self.fail("TODO allocRegOrMem for Vector type", .{}), .Float => { - // TODO check if AVX available - const ptr_bytes: u64 = 32; - if (abi_size <= ptr_bytes) { - if (self.register_manager.tryAllocReg(inst, .{ - .selector_mask = avx, - })) |reg| { - return MCValue{ .register = registerAlias(reg, abi_size) }; + if (self.intrinsicsAllowed(elem_ty)) { + const ptr_bytes: u64 = 32; + if (abi_size <= ptr_bytes) { + if (self.register_manager.tryAllocReg(inst, .{ + .selector_mask = sse, + })) |reg| { + return MCValue{ .register = registerAlias(reg, abi_size) }; + } } } + + return self.fail("TODO allocRegOrMem for Float type without SSE/AVX support", .{}); }, else => { // Make sure the type can fit in a register before we try to allocate one. @@ -969,8 +972,11 @@ pub fn spillRegisters(self: *Self, comptime count: comptime_int, registers: [cou /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { - const mask = switch (ty.zigTypeTag()) { - .Float => avx, + const mask: RegisterManager.RegisterBitSet = switch (ty.zigTypeTag()) { + .Float => blk: { + if (self.intrinsicsAllowed(ty)) break :blk sse; + return self.fail("TODO copy {} to register", .{ty.fmtDebug()}); + }, else => gp, }; const reg: Register = try self.register_manager.allocReg(null, .{ @@ -985,8 +991,11 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { /// This can have a side effect of spilling instructions to the stack to free up a register. /// WARNING make sure that the allocated register matches the returned MCValue from an instruction! fn copyToRegisterWithInstTracking(self: *Self, reg_owner: Air.Inst.Index, ty: Type, mcv: MCValue) !MCValue { - const mask = switch (ty.zigTypeTag()) { - .Float => avx, + const mask: RegisterManager.RegisterBitSet = switch (ty.zigTypeTag()) { + .Float => blk: { + if (self.intrinsicsAllowed(ty)) break :blk sse; + return self.fail("TODO copy {} to register", .{ty.fmtDebug()}); + }, else => gp, }; const reg: Register = try self.register_manager.allocReg(reg_owner, .{ @@ -3469,27 +3478,32 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu }, .register => |src_reg| switch (dst_ty.zigTypeTag()) { .Float => { - const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) { - .f32 => switch (mir_tag) { - .add => Mir.Inst.Tag.add_f32, - .cmp => Mir.Inst.Tag.cmp_f32, - else => return self.fail("TODO genBinOpMir for f32 register-register with MIR tag {}", .{mir_tag}), - }, - .f64 => switch (mir_tag) { - .add => Mir.Inst.Tag.add_f64, - .cmp => Mir.Inst.Tag.cmp_f64, - else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}), - }, - else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}), - }; - _ = try self.addInst(.{ - .tag = actual_tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = dst_reg.to128(), - .reg2 = src_reg.to128(), - }), - .data = undefined, - }); + if (self.intrinsicsAllowed(dst_ty)) { + const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) { + .f32 => switch (mir_tag) { + .add => Mir.Inst.Tag.add_f32_avx, + .cmp => Mir.Inst.Tag.cmp_f32_avx, + else => return self.fail("TODO genBinOpMir for f32 register-register with MIR tag {}", .{mir_tag}), + }, + .f64 => switch (mir_tag) { + .add => Mir.Inst.Tag.add_f64_avx, + .cmp => Mir.Inst.Tag.cmp_f64_avx, + else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}), + }, + else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}), + }; + _ = try self.addInst(.{ + .tag = actual_tag, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = dst_reg.to128(), + .reg2 = src_reg.to128(), + }), + .data = undefined, + }); + return; + } + + return self.fail("TODO genBinOpMir for float register-register and no intrinsics", .{}); }, else => { _ = try self.addInst(.{ @@ -5326,24 +5340,29 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE .register => |reg| { switch (ty.zigTypeTag()) { .Float => { - const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => .mov_f32, - .f64 => .mov_f64, - else => return self.fail("TODO genSetStackArg for register for type {}", .{ty.fmtDebug()}), - }; - _ = try self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = switch (ty.tag()) { - .f32 => .esp, - .f64 => .rsp, - else => unreachable, - }, - .reg2 = reg.to128(), - .flags = 0b01, - }), - .data = .{ .imm = @bitCast(u32, -stack_offset) }, - }); + if (self.intrinsicsAllowed(ty)) { + const tag: Mir.Inst.Tag = switch (ty.tag()) { + .f32 => .mov_f32_avx, + .f64 => .mov_f64_avx, + else => return self.fail("TODO genSetStackArg for register for type {}", .{ty.fmtDebug()}), + }; + _ = try self.addInst(.{ + .tag = tag, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = switch (ty.tag()) { + .f32 => .esp, + .f64 => .rsp, + else => unreachable, + }, + .reg2 = reg.to128(), + .flags = 0b01, + }), + .data = .{ .imm = @bitCast(u32, -stack_offset) }, + }); + return; + } + + return self.fail("TODO genSetStackArg for register with no intrinsics", .{}); }, else => { _ = try self.addInst(.{ @@ -5505,24 +5524,29 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl switch (ty.zigTypeTag()) { .Float => { - const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => .mov_f32, - .f64 => .mov_f64, - else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}), - }; - _ = try self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = switch (ty.tag()) { - .f32 => base_reg.to32(), - .f64 => base_reg.to64(), - else => unreachable, - }, - .reg2 = reg.to128(), - .flags = 0b01, - }), - .data = .{ .imm = @bitCast(u32, -stack_offset) }, - }); + if (self.intrinsicsAllowed(ty)) { + const tag: Mir.Inst.Tag = switch (ty.tag()) { + .f32 => .mov_f32_avx, + .f64 => .mov_f64_avx, + else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}), + }; + _ = try self.addInst(.{ + .tag = tag, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = switch (ty.tag()) { + .f32 => base_reg.to32(), + .f64 => base_reg.to64(), + else => unreachable, + }, + .reg2 = reg.to128(), + .flags = 0b01, + }), + .data = .{ .imm = @bitCast(u32, -stack_offset) }, + }); + return; + } + + return self.fail("TODO genSetStack for register for type float with no intrinsics", .{}); }, else => { if (!math.isPowerOfTwo(abi_size)) { @@ -6026,21 +6050,25 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }, }, .Float => { - const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => .mov_f32, - .f64 => .mov_f64, - else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}), - }; - _ = try self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to128(), - .reg2 = src_reg.to128(), - .flags = 0b10, - }), - .data = undefined, - }); - return; + if (self.intrinsicsAllowed(ty)) { + const tag: Mir.Inst.Tag = switch (ty.tag()) { + .f32 => .mov_f32_avx, + .f64 => .mov_f64_avx, + else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}), + }; + _ = try self.addInst(.{ + .tag = tag, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = reg.to128(), + .reg2 = src_reg.to128(), + .flags = 0b10, + }), + .data = undefined, + }); + return; + } + + return self.fail("TODO genSetReg from register for float with no intrinsics", .{}); }, else => {}, } @@ -6073,24 +6101,29 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void const base_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); - const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => .mov_f32, - .f64 => .mov_f64, - else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), - }; + if (self.intrinsicsAllowed(ty)) { + const tag: Mir.Inst.Tag = switch (ty.tag()) { + .f32 => .mov_f32_avx, + .f64 => .mov_f64_avx, + else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), + }; - _ = try self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to128(), - .reg2 = switch (ty.tag()) { - .f32 => base_reg.to32(), - .f64 => base_reg.to64(), - else => unreachable, - }, - }), - .data = .{ .imm = 0 }, - }); + _ = try self.addInst(.{ + .tag = tag, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = reg.to128(), + .reg2 = switch (ty.tag()) { + .f32 => base_reg.to32(), + .f64 => base_reg.to64(), + else => unreachable, + }, + }), + .data = .{ .imm = 0 }, + }); + return; + } + + return self.fail("TODO genSetReg from memory for float with no intrinsics", .{}); }, else => { if (x <= math.maxInt(i32)) { @@ -6183,24 +6216,27 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }, }, .Float => { - const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => .mov_f32, - .f64 => .mov_f64, - else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}), - }; - _ = try self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to128(), - .reg2 = switch (ty.tag()) { - .f32 => .ebp, - .f64 => .rbp, - else => unreachable, - }, - }), - .data = .{ .imm = @bitCast(u32, -off) }, - }); - return; + if (self.intrinsicsAllowed(ty)) { + const tag: Mir.Inst.Tag = switch (ty.tag()) { + .f32 => .mov_f32_avx, + .f64 => .mov_f64_avx, + else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}), + }; + _ = try self.addInst(.{ + .tag = tag, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = reg.to128(), + .reg2 = switch (ty.tag()) { + .f32 => .ebp, + .f64 => .rbp, + else => unreachable, + }, + }), + .data = .{ .imm = @bitCast(u32, -off) }, + }); + return; + } + return self.fail("TODO genSetReg from stack offset for float with no intrinsics", .{}); }, else => {}, } @@ -6995,3 +7031,12 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { }, } } + +fn intrinsicsAllowed(self: *Self, ty: Type) bool { + return switch (ty.tag()) { + .f32, + .f64, + => Target.x86.featureSetHasAny(self.target.cpu.features, .{ .avx, .avx2 }), + else => unreachable, // TODO finish this off + }; +} diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 96f640b610..fbcd8359f7 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -183,14 +183,14 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .nop => try emit.mirNop(), // AVX instructions - .mov_f64 => try emit.mirMovFloatAvx(.vmovsd, inst), - .mov_f32 => try emit.mirMovFloatAvx(.vmovss, inst), + .mov_f64_avx => try emit.mirMovFloatAvx(.vmovsd, inst), + .mov_f32_avx => try emit.mirMovFloatAvx(.vmovss, inst), - .add_f64 => try emit.mirAddFloatAvx(.vaddsd, inst), - .add_f32 => try emit.mirAddFloatAvx(.vaddss, inst), + .add_f64_avx => try emit.mirAddFloatAvx(.vaddsd, inst), + .add_f32_avx => try emit.mirAddFloatAvx(.vaddss, inst), - .cmp_f64 => try emit.mirCmpFloatAvx(.vucomisd, inst), - .cmp_f32 => try emit.mirCmpFloatAvx(.vucomiss, inst), + .cmp_f64_avx => try emit.mirCmpFloatAvx(.vucomisd, inst), + .cmp_f32_avx => try emit.mirCmpFloatAvx(.vucomiss, inst), // Pseudo-instructions .call_extern => try emit.mirCallExtern(inst), diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index dc8c1fa0b2..0f200d43e6 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -350,18 +350,18 @@ pub const Inst = struct { /// 0b00 reg1, qword ptr [reg2 + imm32] /// 0b01 qword ptr [reg1 + imm32], reg2 /// 0b10 reg1, reg2 - mov_f64, - mov_f32, + mov_f64_avx, + mov_f32_avx, /// ops flags: form: /// 0b00 reg1, reg1, reg2 - add_f64, - add_f32, + add_f64_avx, + add_f32_avx, /// ops flags: form: /// - cmp_f64, - cmp_f32, + cmp_f64_avx, + cmp_f32_avx, /// Pseudo-instructions /// call extern function diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index bf85f002d1..7e2025a23d 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -383,11 +383,11 @@ pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8 pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; -const avx_regs = [_]Register{ +const sse_avx_regs = [_]Register{ .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7, .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15, }; -const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ avx_regs; +const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ sse_avx_regs; pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers); // Register classes @@ -401,7 +401,7 @@ pub const RegisterClass = struct { }, true); break :blk set; }; - pub const avx: RegisterBitSet = blk: { + pub const sse: RegisterBitSet = blk: { var set = RegisterBitSet.initEmpty(); set.setRangeValue(.{ .start = caller_preserved_regs.len + callee_preserved_regs.len, From f766b25f82a6c94d6b77f6a4172bfc4feb1d9271 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 19 May 2022 20:24:06 +0200 Subject: [PATCH 1587/2031] x64: load float from memory to register on PIE targets --- src/arch/x86_64/CodeGen.zig | 52 ++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 2e4a396c9f..bd17b2a9ae 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -6085,16 +6085,48 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .direct_load, .got_load, => { - try self.loadMemPtrIntoRegister(reg, Type.usize, mcv); - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = registerAlias(reg, abi_size), - .reg2 = reg.to64(), - .flags = 0b01, - }), - .data = .{ .imm = 0 }, - }); + switch (ty.zigTypeTag()) { + .Float => { + const base_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); + try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); + + if (self.intrinsicsAllowed(ty)) { + const tag: Mir.Inst.Tag = switch (ty.tag()) { + .f32 => .mov_f32_avx, + .f64 => .mov_f64_avx, + else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), + }; + + _ = try self.addInst(.{ + .tag = tag, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = reg.to128(), + .reg2 = switch (ty.tag()) { + .f32 => base_reg.to32(), + .f64 => base_reg.to64(), + else => unreachable, + }, + }), + .data = .{ .imm = 0 }, + }); + return; + } + + return self.fail("TODO genSetReg from memory for float with no intrinsics", .{}); + }, + else => { + try self.loadMemPtrIntoRegister(reg, Type.usize, mcv); + _ = try self.addInst(.{ + .tag = .mov, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = registerAlias(reg, abi_size), + .reg2 = reg.to64(), + .flags = 0b01, + }), + .data = .{ .imm = 0 }, + }); + }, + } }, .memory => |x| switch (ty.zigTypeTag()) { .Float => { From 7b63f98cd7d86337e8157afc9600e1e17c27db80 Mon Sep 17 00:00:00 2001 From: frmdstryr Date: Thu, 19 May 2022 15:04:40 -0400 Subject: [PATCH 1588/2031] Add aliases to math builtins back into std.math (#11666) --- lib/std/math.zig | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/lib/std/math.zig b/lib/std/math.zig index 97868a4f7e..b936183482 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -270,6 +270,41 @@ pub const sinh = @import("math/sinh.zig").sinh; pub const cosh = @import("math/cosh.zig").cosh; pub const tanh = @import("math/tanh.zig").tanh; +/// Sine trigonometric function on a floating point number. +/// Uses a dedicated hardware instruction when available. +/// This is the same as calling the builtin @sin +pub inline fn sin(value: anytype) @TypeOf(value) { + return @sin(value); +} + +/// Cosine trigonometric function on a floating point number. +/// Uses a dedicated hardware instruction when available. +/// This is the same as calling the builtin @cos +pub inline fn cos(value: anytype) @TypeOf(value) { + return @cos(value); +} + +/// Tangent trigonometric function on a floating point number. +/// Uses a dedicated hardware instruction when available. +/// This is the same as calling the builtin @tan +pub inline fn tan(value: anytype) @TypeOf(value) { + return @tan(value); +} + +/// Base-e exponential function on a floating point number. +/// Uses a dedicated hardware instruction when available. +/// This is the same as calling the builtin @exp +pub inline fn exp(value: anytype) @TypeOf(value) { + return @exp(value); +} + +/// Base-2 exponential function on a floating point number. +/// Uses a dedicated hardware instruction when available. +/// This is the same as calling the builtin @exp2 +pub inline fn exp2(value: anytype) @TypeOf(value) { + return @exp2(value); +} + pub const complex = @import("math/complex.zig"); pub const Complex = complex.Complex; @@ -887,6 +922,13 @@ fn testRem() !void { try testing.expectError(error.DivisionByZero, rem(f32, 10, 0)); } +/// Returns the absolute value of a floating point number. +/// Uses a dedicated hardware instruction when available. +/// This is the same as calling the builtin @fabs +pub inline fn fabs(value: anytype) @TypeOf(value) { + return @fabs(value); +} + /// Returns the absolute value of the integer parameter. /// Result is an unsigned integer. pub fn absCast(x: anytype) switch (@typeInfo(@TypeOf(x))) { @@ -987,6 +1029,27 @@ pub fn isPowerOfTwo(v: anytype) bool { return (v & (v - 1)) == 0; } +/// Rounds the given floating point number to an integer, away from zero. +/// Uses a dedicated hardware instruction when available. +/// This is the same as calling the builtin @round +pub inline fn round(value: anytype) @TypeOf(value) { + return @round(value); +} + +/// Rounds the given floating point number to an integer, towards zero. +/// Uses a dedicated hardware instruction when available. +/// This is the same as calling the builtin @trunc +pub inline fn trunc(value: anytype) @TypeOf(value) { + return @trunc(value); +} + +/// Returns the largest integral value not greater than the given floating point number. +/// Uses a dedicated hardware instruction when available. +/// This is the same as calling the builtin @floor +pub inline fn floor(value: anytype) @TypeOf(value) { + return @floor(value); +} + /// Returns the nearest power of two less than or equal to value, or /// zero if value is less than or equal to zero. pub fn floorPowerOfTwo(comptime T: type, value: T) T { @@ -1015,6 +1078,13 @@ fn testFloorPowerOfTwo() !void { try testing.expect(floorPowerOfTwo(i4, 0) == 0); } +/// Returns the smallest integral value not less than the given floating point number. +/// Uses a dedicated hardware instruction when available. +/// This is the same as calling the builtin @ceil +pub inline fn ceil(value: anytype) @TypeOf(value) { + return @ceil(value); +} + /// Returns the next power of two (if the value is not already a power of two). /// Only unsigned integers can be used. Zero is not an allowed input. /// Result is a type with 1 more bit than the input type. From e95dfac03eea942b53bbdf6117a716aaeba5eb4b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 19 May 2022 23:17:43 +0200 Subject: [PATCH 1589/2031] regalloc: make register class bitmask non-optional --- src/arch/aarch64/CodeGen.zig | 62 +++++++++---------- src/arch/aarch64/abi.zig | 17 ++++++ src/arch/arm/CodeGen.zig | 71 +++++++++++----------- src/arch/arm/abi.zig | 18 +++++- src/arch/riscv64/CodeGen.zig | 22 +++---- src/arch/riscv64/abi.zig | 17 ++++++ src/arch/sparc64/CodeGen.zig | 32 +++++----- src/arch/sparc64/abi.zig | 18 +++++- src/arch/x86_64/CodeGen.zig | 64 ++++++++------------ src/register_manager.zig | 111 +++++++++++++++++++++++++---------- 10 files changed, 263 insertions(+), 169 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index a3116587d4..f4f2b1e5e5 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -21,9 +21,6 @@ const DW = std.dwarf; const leb128 = std.leb; const log = std.log.scoped(.codegen); const build_options = @import("build_options"); -const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; -const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs); -const RegisterLock = RegisterManager.RegisterLock; const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; const FnResult = @import("../../codegen.zig").FnResult; @@ -31,11 +28,14 @@ const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; const bits = @import("bits.zig"); const abi = @import("abi.zig"); +const RegisterManager = abi.RegisterManager; +const RegisterLock = RegisterManager.RegisterLock; const Register = bits.Register; const Instruction = bits.Instruction; const callee_preserved_regs = abi.callee_preserved_regs; const c_abi_int_param_regs = abi.c_abi_int_param_regs; const c_abi_int_return_regs = abi.c_abi_int_return_regs; +const gp = abi.RegisterClass.gp; const InnerError = error{ OutOfMemory, @@ -888,7 +888,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { if (reg_ok) { // Make sure the type can fit in a register before we try to allocate one. if (abi_size <= 8) { - if (self.register_manager.tryAllocReg(inst, .{})) |reg| { + if (self.register_manager.tryAllocReg(inst, gp)) |reg| { return MCValue{ .register = registerAlias(reg, abi_size) }; } } @@ -951,7 +951,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { - const raw_reg = try self.register_manager.allocReg(null, .{}); + const raw_reg = try self.register_manager.allocReg(null, gp); const reg = registerAlias(raw_reg, ty.abiSize(self.target.*)); try self.genSetReg(ty, reg, mcv); return reg; @@ -961,7 +961,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { /// `reg_owner` is the instruction that gets associated with the register in the register table. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { - const raw_reg = try self.register_manager.allocReg(reg_owner, .{}); + const raw_reg = try self.register_manager.allocReg(reg_owner, gp); const ty = self.air.typeOfIndex(reg_owner); const reg = registerAlias(raw_reg, ty.abiSize(self.target.*)); try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv); @@ -1074,11 +1074,11 @@ fn trunc( if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { break :blk registerAlias(operand_reg, dest_ty.abiSize(self.target.*)); } else { - const raw_reg = try self.register_manager.allocReg(inst, .{}); + const raw_reg = try self.register_manager.allocReg(inst, gp); break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); } } else blk: { - const raw_reg = try self.register_manager.allocReg(null, .{}); + const raw_reg = try self.register_manager.allocReg(null, gp); break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); }; @@ -1160,7 +1160,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { break :blk op_reg; } - const raw_reg = try self.register_manager.allocReg(null, .{}); + const raw_reg = try self.register_manager.allocReg(null, gp); break :blk raw_reg.to32(); }; @@ -1193,7 +1193,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { break :blk op_reg; } - const raw_reg = try self.register_manager.allocReg(null, .{}); + const raw_reg = try self.register_manager.allocReg(null, gp); break :blk registerAlias(raw_reg, operand_ty.abiSize(self.target.*)); }; @@ -1293,7 +1293,7 @@ fn binOpRegister( break :inst Air.refToIndex(md.lhs).?; } else null; - const raw_reg = try self.register_manager.allocReg(track_inst, .{}); + const raw_reg = try self.register_manager.allocReg(track_inst, gp); const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -1308,7 +1308,7 @@ fn binOpRegister( break :inst Air.refToIndex(md.rhs).?; } else null; - const raw_reg = try self.register_manager.allocReg(track_inst, .{}); + const raw_reg = try self.register_manager.allocReg(track_inst, gp); const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*)); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -1326,11 +1326,11 @@ fn binOpRegister( } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) { break :blk rhs_reg; } else { - const raw_reg = try self.register_manager.allocReg(md.inst, .{}); + const raw_reg = try self.register_manager.allocReg(md.inst, gp); break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); } } else blk: { - const raw_reg = try self.register_manager.allocReg(null, .{}); + const raw_reg = try self.register_manager.allocReg(null, gp); break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); }, }; @@ -1431,7 +1431,7 @@ fn binOpImmediate( ).?; } else null; - const raw_reg = try self.register_manager.allocReg(track_inst, .{}); + const raw_reg = try self.register_manager.allocReg(track_inst, gp); const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -1452,11 +1452,11 @@ fn binOpImmediate( )) { break :blk lhs_reg; } else { - const raw_reg = try self.register_manager.allocReg(md.inst, .{}); + const raw_reg = try self.register_manager.allocReg(md.inst, gp); break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); } } else blk: { - const raw_reg = try self.register_manager.allocReg(null, .{}); + const raw_reg = try self.register_manager.allocReg(null, gp); break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); }, }; @@ -1872,7 +1872,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_reg_lock); - const raw_truncated_reg = try self.register_manager.allocReg(null, .{}); + const raw_truncated_reg = try self.register_manager.allocReg(null, gp); const truncated_reg = registerAlias(raw_truncated_reg, lhs_ty.abiSize(self.target.*)); const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); defer self.register_manager.unlockReg(truncated_reg_lock); @@ -1983,7 +1983,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_reg_lock); - const truncated_reg = try self.register_manager.allocReg(null, .{}); + const truncated_reg = try self.register_manager.allocReg(null, gp); const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); defer self.register_manager.unlockReg(truncated_reg_lock); @@ -2048,7 +2048,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); const lhs_reg = if (lhs_is_register) lhs.register else blk: { - const raw_reg = try self.register_manager.allocReg(null, .{}); + const raw_reg = try self.register_manager.allocReg(null, gp); const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); break :blk reg; }; @@ -2056,7 +2056,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else blk: { - const raw_reg = try self.register_manager.allocReg(null, .{}); + const raw_reg = try self.register_manager.allocReg(null, gp); const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*)); break :blk reg; }; @@ -2067,7 +2067,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); const dest_reg = blk: { - const raw_reg = try self.register_manager.allocReg(null, .{}); + const raw_reg = try self.register_manager.allocReg(null, gp); const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); break :blk reg; }; @@ -2086,7 +2086,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { } }, }); - const dest_high_reg = try self.register_manager.allocReg(null, .{}); + const dest_high_reg = try self.register_manager.allocReg(null, gp); const dest_high_reg_lock = self.register_manager.lockRegAssumeUnused(dest_high_reg); defer self.register_manager.unlockReg(dest_high_reg_lock); @@ -2136,7 +2136,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { } }, .unsigned => { - const dest_high_reg = try self.register_manager.allocReg(null, .{}); + const dest_high_reg = try self.register_manager.allocReg(null, gp); const dest_high_reg_lock = self.register_manager.lockRegAssumeUnused(dest_high_reg); defer self.register_manager.unlockReg(dest_high_reg_lock); @@ -2192,7 +2192,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { }, } - const truncated_reg = try self.register_manager.allocReg(null, .{}); + const truncated_reg = try self.register_manager.allocReg(null, gp); const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); defer self.register_manager.unlockReg(truncated_reg_lock); @@ -2663,7 +2663,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo }, .stack_offset => |off| { if (elem_size <= 8) { - const raw_tmp_reg = try self.register_manager.allocReg(null, .{}); + const raw_tmp_reg = try self.register_manager.allocReg(null, gp); const tmp_reg = registerAlias(raw_tmp_reg, elem_size); const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); defer self.register_manager.unlockReg(tmp_reg_lock); @@ -2672,7 +2672,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); } else { // TODO optimize the register allocation - const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, .{}); + const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp); const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs); defer for (regs_locks) |reg| { self.register_manager.unlockReg(reg); @@ -2887,7 +2887,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type }, else => { if (abi_size <= 8) { - const raw_tmp_reg = try self.register_manager.allocReg(null, .{}); + const raw_tmp_reg = try self.register_manager.allocReg(null, gp); const tmp_reg = registerAlias(raw_tmp_reg, abi_size); const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); defer self.register_manager.unlockReg(tmp_reg_lock); @@ -3002,7 +3002,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { // TODO return special MCValue condition flags // get overflow bit: set register to C flag // resp. V flag - const raw_dest_reg = try self.register_manager.allocReg(null, .{}); + const raw_dest_reg = try self.register_manager.allocReg(null, gp); const dest_reg = raw_dest_reg.to32(); // C flag: cset reg, cs @@ -4065,7 +4065,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro const overflow_bit_ty = ty.structFieldType(1); const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*)); - const raw_cond_reg = try self.register_manager.allocReg(null, .{}); + const raw_cond_reg = try self.register_manager.allocReg(null, gp); const cond_reg = registerAlias( raw_cond_reg, @intCast(u32, overflow_bit_ty.abiSize(self.target.*)), @@ -4113,7 +4113,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro const ptr_ty = Type.initPayload(&ptr_ty_payload.base); // TODO call extern memcpy - const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, .{}); + const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp); const regs_locks = self.register_manager.lockRegsAssumeUnused(5, regs); defer for (regs_locks) |reg| { self.register_manager.unlockReg(reg); diff --git a/src/arch/aarch64/abi.zig b/src/arch/aarch64/abi.zig index 1c5225104a..839b2789e4 100644 --- a/src/arch/aarch64/abi.zig +++ b/src/arch/aarch64/abi.zig @@ -1,6 +1,7 @@ const builtin = @import("builtin"); const bits = @import("bits.zig"); const Register = bits.Register; +const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; const callee_preserved_regs_impl = if (builtin.os.tag.isDarwin()) struct { pub const callee_preserved_regs = [_]Register{ @@ -18,3 +19,19 @@ pub const callee_preserved_regs = callee_preserved_regs_impl.callee_preserved_re pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; + +const allocatable_registers = callee_preserved_regs; +pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers); + +// Register classes +const RegisterBitSet = RegisterManager.RegisterBitSet; +pub const RegisterClass = struct { + pub const gp: RegisterBitSet = blk: { + var set = RegisterBitSet.initEmpty(); + set.setRangeValue(.{ + .start = 0, + .end = callee_preserved_regs.len, + }, true); + break :blk set; + }; +}; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index c792615c79..4f121dd56e 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -21,9 +21,6 @@ const DW = std.dwarf; const leb128 = std.leb; const log = std.log.scoped(.codegen); const build_options = @import("build_options"); -const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; -const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers); -const RegisterLock = RegisterManager.RegisterLock; const FnResult = @import("../../codegen.zig").FnResult; const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; @@ -31,14 +28,16 @@ const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; const bits = @import("bits.zig"); const abi = @import("abi.zig"); +const RegisterManager = abi.RegisterManager; +const RegisterLock = RegisterManager.RegisterLock; const Register = bits.Register; const Instruction = bits.Instruction; const Condition = bits.Condition; const callee_preserved_regs = abi.callee_preserved_regs; const caller_preserved_regs = abi.caller_preserved_regs; -const allocatable_registers = abi.allocatable_registers; const c_abi_int_param_regs = abi.c_abi_int_param_regs; const c_abi_int_return_regs = abi.c_abi_int_return_regs; +const gp = abi.RegisterClass.gp; const InnerError = error{ OutOfMemory, @@ -874,7 +873,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); if (abi_size <= ptr_bytes) { - if (self.register_manager.tryAllocReg(inst, .{})) |reg| { + if (self.register_manager.tryAllocReg(inst, gp)) |reg| { return MCValue{ .register = reg }; } } @@ -939,7 +938,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { - const reg = try self.register_manager.allocReg(null, .{}); + const reg = try self.register_manager.allocReg(null, gp); try self.genSetReg(ty, reg, mcv); return reg; } @@ -948,7 +947,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { /// `reg_owner` is the instruction that gets associated with the register in the register table. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { - const reg = try self.register_manager.allocReg(reg_owner, .{}); + const reg = try self.register_manager.allocReg(reg_owner, gp); try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv); return MCValue{ .register = reg }; } @@ -1065,9 +1064,9 @@ fn trunc( if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { break :blk operand_reg; } else { - break :blk try self.register_manager.allocReg(inst, .{}); + break :blk try self.register_manager.allocReg(inst, gp); } - } else try self.register_manager.allocReg(null, .{}); + } else try self.register_manager.allocReg(null, gp); switch (info_b.bits) { 32 => { @@ -1153,7 +1152,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { break :blk op_reg; } - break :blk try self.register_manager.allocReg(null, .{}); + break :blk try self.register_manager.allocReg(null, gp); }; _ = try self.addInst(.{ @@ -1183,7 +1182,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { break :blk op_reg; } - break :blk try self.register_manager.allocReg(null, .{}); + break :blk try self.register_manager.allocReg(null, gp); }; _ = try self.addInst(.{ @@ -1254,9 +1253,9 @@ fn minMax( } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { break :blk rhs_reg; } else { - break :blk try self.register_manager.allocReg(inst, .{}); + break :blk try self.register_manager.allocReg(inst, gp); } - } else try self.register_manager.allocReg(null, .{}); + } else try self.register_manager.allocReg(null, gp); // lhs == reg should have been checked by airMinMax // @@ -1438,7 +1437,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_reg_lock); - const truncated_reg = try self.register_manager.allocReg(null, .{}); + const truncated_reg = try self.register_manager.allocReg(null, gp); const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); defer self.register_manager.unlockReg(truncated_reg_lock); @@ -1543,7 +1542,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_reg_lock); - const truncated_reg = try self.register_manager.allocReg(null, .{}); + const truncated_reg = try self.register_manager.allocReg(null, gp); const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); defer self.register_manager.unlockReg(truncated_reg_lock); @@ -1582,18 +1581,18 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const lhs_reg = if (lhs_is_register) lhs.register else - try self.register_manager.allocReg(null, .{}); + try self.register_manager.allocReg(null, gp); const new_lhs_lock = self.register_manager.lockReg(lhs_reg); defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else - try self.register_manager.allocReg(null, .{}); + try self.register_manager.allocReg(null, gp); const new_rhs_lock = self.register_manager.lockReg(rhs_reg); defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); - const dest_regs = try self.register_manager.allocRegs(2, .{ null, null }, .{}); + const dest_regs = try self.register_manager.allocRegs(2, .{ null, null }, gp); const dest_regs_locks = self.register_manager.lockRegsAssumeUnused(2, dest_regs); defer for (dest_regs_locks) |reg| { self.register_manager.unlockReg(reg); @@ -1604,7 +1603,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); - const truncated_reg = try self.register_manager.allocReg(null, .{}); + const truncated_reg = try self.register_manager.allocReg(null, gp); const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); defer self.register_manager.unlockReg(truncated_reg_lock); @@ -2026,7 +2025,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { const base_reg_lock = self.register_manager.lockRegAssumeUnused(base_reg); defer self.register_manager.unlockReg(base_reg_lock); - const dst_reg = try self.register_manager.allocReg(inst, .{}); + const dst_reg = try self.register_manager.allocReg(inst, gp); const dst_mcv = MCValue{ .register = dst_reg }; const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); defer self.register_manager.unlockReg(dst_reg_lock); @@ -2234,7 +2233,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo }, .stack_offset => |off| { if (elem_size <= 4) { - const tmp_reg = try self.register_manager.allocReg(null, .{}); + const tmp_reg = try self.register_manager.allocReg(null, gp); const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); defer self.register_manager.unlockReg(tmp_reg_lock); @@ -2242,7 +2241,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); } else { // TODO optimize the register allocation - const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, .{}); + const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp); const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs); defer for (regs_locks) |reg_locked| { self.register_manager.unlockReg(reg_locked); @@ -2271,7 +2270,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .stack_offset, .stack_argument_offset, => { - const reg = try self.register_manager.allocReg(null, .{}); + const reg = try self.register_manager.allocReg(null, gp); const reg_lock = self.register_manager.lockRegAssumeUnused(reg); defer self.register_manager.unlockReg(reg_lock); @@ -2338,14 +2337,14 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type }, else => { if (elem_size <= 4) { - const tmp_reg = try self.register_manager.allocReg(null, .{}); + const tmp_reg = try self.register_manager.allocReg(null, gp); const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); defer self.register_manager.unlockReg(tmp_reg_lock); try self.genSetReg(value_ty, tmp_reg, value); try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); } else { - const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, .{}); + const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp); const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs); defer for (regs_locks) |reg| { self.register_manager.unlockReg(reg); @@ -2487,7 +2486,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { 1 => { // get overflow bit: set register to C flag // resp. V flag - const dest_reg = try self.register_manager.allocReg(null, .{}); + const dest_reg = try self.register_manager.allocReg(null, gp); // mov reg, #0 _ = try self.addInst(.{ @@ -2567,7 +2566,7 @@ fn binOpRegister( break :inst Air.refToIndex(md.lhs).?; } else null; - const reg = try self.register_manager.allocReg(track_inst, .{}); + const reg = try self.register_manager.allocReg(track_inst, gp); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -2581,7 +2580,7 @@ fn binOpRegister( break :inst Air.refToIndex(md.rhs).?; } else null; - const reg = try self.register_manager.allocReg(track_inst, .{}); + const reg = try self.register_manager.allocReg(track_inst, gp); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -2598,9 +2597,9 @@ fn binOpRegister( } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) { break :blk rhs_reg; } else { - break :blk try self.register_manager.allocReg(md.inst, .{}); + break :blk try self.register_manager.allocReg(md.inst, gp); } - } else try self.register_manager.allocReg(null, .{}), + } else try self.register_manager.allocReg(null, gp), }; if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); @@ -2684,7 +2683,7 @@ fn binOpImmediate( ).?; } else null; - const reg = try self.register_manager.allocReg(track_inst, .{}); + const reg = try self.register_manager.allocReg(track_inst, gp); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -2704,9 +2703,9 @@ fn binOpImmediate( )) { break :blk lhs_reg; } else { - break :blk try self.register_manager.allocReg(md.inst, .{}); + break :blk try self.register_manager.allocReg(md.inst, gp); } - } else try self.register_manager.allocReg(null, .{}), + } else try self.register_manager.allocReg(null, gp), }; if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); @@ -4363,7 +4362,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro const overflow_bit_ty = ty.structFieldType(1); const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*)); - const cond_reg = try self.register_manager.allocReg(null, .{}); + const cond_reg = try self.register_manager.allocReg(null, gp); // C flag: movcs reg, #1 // V flag: movvs reg, #1 @@ -4408,7 +4407,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro const ptr_ty = Type.initPayload(&ptr_ty_payload.base); // TODO call extern memcpy - const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, .{}); + const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp); const src_reg = regs[0]; const dst_reg = regs[1]; const len_reg = regs[2]; @@ -4782,7 +4781,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I const ptr_ty = Type.initPayload(&ptr_ty_payload.base); // TODO call extern memcpy - const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, .{}); + const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp); const src_reg = regs[0]; const dst_reg = regs[1]; const len_reg = regs[2]; diff --git a/src/arch/arm/abi.zig b/src/arch/arm/abi.zig index 4073b92222..dbee1f9b90 100644 --- a/src/arch/arm/abi.zig +++ b/src/arch/arm/abi.zig @@ -1,9 +1,25 @@ const bits = @import("bits.zig"); const Register = bits.Register; +const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 }; pub const caller_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3 }; -pub const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs; pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; + +const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs; +pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers); + +// Register classes +const RegisterBitSet = RegisterManager.RegisterBitSet; +pub const RegisterClass = struct { + pub const gp: RegisterBitSet = blk: { + var set = RegisterBitSet.initEmpty(); + set.setRangeValue(.{ + .start = 0, + .end = caller_preserved_regs.len + callee_preserved_regs.len, + }, true); + break :blk set; + }; +}; diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 79bedb30d8..b713161053 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -21,9 +21,6 @@ const DW = std.dwarf; const leb128 = std.leb; const log = std.log.scoped(.codegen); const build_options = @import("build_options"); -const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; -const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs); -const RegisterLock = RegisterManager.RegisterLock; const FnResult = @import("../../codegen.zig").FnResult; const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; @@ -32,8 +29,11 @@ const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; const bits = @import("bits.zig"); const abi = @import("abi.zig"); const Register = bits.Register; +const RegisterManager = abi.RegisterManager; +const RegisterLock = RegisterManager.RegisterLock; const Instruction = abi.Instruction; const callee_preserved_regs = abi.callee_preserved_regs; +const gp = abi.RegisterClass.gp; const InnerError = error{ OutOfMemory, @@ -803,7 +803,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); if (abi_size <= ptr_bytes) { - if (self.register_manager.tryAllocReg(inst, .{})) |reg| { + if (self.register_manager.tryAllocReg(inst, gp)) |reg| { return MCValue{ .register = reg }; } } @@ -826,7 +826,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { - const reg = try self.register_manager.allocReg(null, .{}); + const reg = try self.register_manager.allocReg(null, gp); try self.genSetReg(ty, reg, mcv); return reg; } @@ -835,7 +835,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { /// `reg_owner` is the instruction that gets associated with the register in the register table. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { - const reg = try self.register_manager.allocReg(reg_owner, .{}); + const reg = try self.register_manager.allocReg(reg_owner, gp); try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv); return MCValue{ .register = reg }; } @@ -958,7 +958,7 @@ fn binOpRegister( break :inst Air.refToIndex(bin_op.lhs).?; } else null; - const reg = try self.register_manager.allocReg(track_inst, .{}); + const reg = try self.register_manager.allocReg(track_inst, gp); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -973,7 +973,7 @@ fn binOpRegister( break :inst Air.refToIndex(bin_op.rhs).?; } else null; - const reg = try self.register_manager.allocReg(track_inst, .{}); + const reg = try self.register_manager.allocReg(track_inst, gp); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -990,9 +990,9 @@ fn binOpRegister( } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { break :blk rhs_reg; } else { - break :blk try self.register_manager.allocReg(inst, .{}); + break :blk try self.register_manager.allocReg(inst, gp); } - } else try self.register_manager.allocReg(null, .{}); + } else try self.register_manager.allocReg(null, gp); if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); @@ -1482,7 +1482,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .memory, .stack_offset, => { - const reg = try self.register_manager.allocReg(null, .{}); + const reg = try self.register_manager.allocReg(null, gp); const reg_lock = self.register_manager.lockRegAssumeUnused(reg); defer self.register_manager.unlockReg(reg_lock); diff --git a/src/arch/riscv64/abi.zig b/src/arch/riscv64/abi.zig index dd0feeea49..459fd40c9c 100644 --- a/src/arch/riscv64/abi.zig +++ b/src/arch/riscv64/abi.zig @@ -1,6 +1,23 @@ const bits = @import("bits.zig"); const Register = bits.Register; +const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; pub const callee_preserved_regs = [_]Register{ .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11, }; + +const allocatable_registers = callee_preserved_regs; +pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers); + +// Register classes +const RegisterBitSet = RegisterManager.RegisterBitSet; +pub const RegisterClass = struct { + pub const gp: RegisterBitSet = blk: { + var set = RegisterBitSet.initEmpty(); + set.setRangeValue(.{ + .start = 0, + .end = callee_preserved_regs.len, + }, true); + break :blk set; + }; +}; diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index e8cb5d3100..ff066f78f2 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -21,9 +21,6 @@ const Type = @import("../../type.zig").Type; const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; const FnResult = @import("../../codegen.zig").FnResult; const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; -const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; -const RegisterManager = RegisterManagerFn(Self, Register, &abi.allocatable_regs); -const RegisterLock = RegisterManager.RegisterLock; const build_options = @import("build_options"); @@ -31,7 +28,10 @@ const bits = @import("bits.zig"); const abi = @import("abi.zig"); const Instruction = bits.Instruction; const ShiftWidth = Instruction.ShiftWidth; +const RegisterManager = abi.RegisterManager; +const RegisterLock = RegisterManager.RegisterLock; const Register = bits.Register; +const gp = abi.RegisterClass.gp; const Self = @This(); @@ -1613,7 +1613,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { if (reg_ok) { // Make sure the type can fit in a register before we try to allocate one. if (abi_size <= 8) { - if (self.register_manager.tryAllocReg(inst, .{})) |reg| { + if (self.register_manager.tryAllocReg(inst, gp)) |reg| { return MCValue{ .register = reg }; } } @@ -1854,7 +1854,7 @@ fn binOpImmediate( ).?; } else null; - const reg = try self.register_manager.allocReg(track_inst, .{}); + const reg = try self.register_manager.allocReg(track_inst, gp); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -1873,10 +1873,10 @@ fn binOpImmediate( )) { break :blk lhs_reg; } else { - break :blk try self.register_manager.allocReg(md.inst, .{}); + break :blk try self.register_manager.allocReg(md.inst, gp); } } else blk: { - break :blk try self.register_manager.allocReg(null, .{}); + break :blk try self.register_manager.allocReg(null, gp); }, }; @@ -1953,7 +1953,7 @@ fn binOpRegister( break :inst Air.refToIndex(md.lhs).?; } else null; - const reg = try self.register_manager.allocReg(track_inst, .{}); + const reg = try self.register_manager.allocReg(track_inst, gp); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); break :blk reg; @@ -1966,7 +1966,7 @@ fn binOpRegister( break :inst Air.refToIndex(md.rhs).?; } else null; - const reg = try self.register_manager.allocReg(track_inst, .{}); + const reg = try self.register_manager.allocReg(track_inst, gp); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); break :blk reg; @@ -1981,10 +1981,10 @@ fn binOpRegister( } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) { break :blk rhs_reg; } else { - break :blk try self.register_manager.allocReg(md.inst, .{}); + break :blk try self.register_manager.allocReg(md.inst, gp); } } else blk: { - break :blk try self.register_manager.allocReg(null, .{}); + break :blk try self.register_manager.allocReg(null, gp); }, }; @@ -2077,7 +2077,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { - const reg = try self.register_manager.allocReg(null, .{}); + const reg = try self.register_manager.allocReg(null, gp); try self.genSetReg(ty, reg, mcv); return reg; } @@ -2364,7 +2364,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }); } else { // Need to allocate a temporary register to load 64-bit immediates. - const tmp_reg = try self.register_manager.allocReg(null, .{}); + const tmp_reg = try self.register_manager.allocReg(null, gp); try self.genSetReg(ty, tmp_reg, .{ .immediate = @truncate(u32, x) }); try self.genSetReg(ty, reg, .{ .immediate = @truncate(u32, x >> 32) }); @@ -2478,7 +2478,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro }; const ptr_ty = Type.initPayload(&ptr_ty_payload.base); - const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, .{}); + const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp); const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs); defer for (regs_locks) |reg| { self.register_manager.unlockReg(reg); @@ -2717,14 +2717,14 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo }, .stack_offset => |off| { if (elem_size <= 8) { - const tmp_reg = try self.register_manager.allocReg(null, .{}); + const tmp_reg = try self.register_manager.allocReg(null, gp); const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); defer self.register_manager.unlockReg(tmp_reg_lock); try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); } else { - const regs = try self.register_manager.allocRegs(3, .{ null, null, null }, .{}); + const regs = try self.register_manager.allocRegs(3, .{ null, null, null }, gp); const regs_locks = self.register_manager.lockRegsAssumeUnused(3, regs); defer for (regs_locks) |reg| { self.register_manager.unlockReg(reg); diff --git a/src/arch/sparc64/abi.zig b/src/arch/sparc64/abi.zig index a9001c7dc7..b1985a7b0b 100644 --- a/src/arch/sparc64/abi.zig +++ b/src/arch/sparc64/abi.zig @@ -1,5 +1,6 @@ const bits = @import("bits.zig"); const Register = bits.Register; +const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; // SPARCv9 stack constants. // See: Registers and the Stack Frame, page 3P-8, SCD 2.4.1. @@ -21,7 +22,7 @@ pub const stack_save_area = 176; pub const caller_preserved_regs = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5, .g1, .g4, .g5 }; // Try to allocate i, l, o, then g sets of registers, in order of priority. -pub const allocatable_regs = [_]Register{ +const allocatable_regs = [_]Register{ // zig fmt: off .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5", .l0, .l1, .l2, .l3, .l4, .l5, .l6, .l7, @@ -35,3 +36,18 @@ pub const c_abi_int_param_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2" pub const c_abi_int_return_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3 }; pub const c_abi_int_return_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3" }; + +pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_regs); + +// Register classes +const RegisterBitSet = RegisterManager.RegisterBitSet; +pub const RegisterClass = struct { + pub const gp: RegisterBitSet = blk: { + var set = RegisterBitSet.initEmpty(); + set.setRangeValue(.{ + .start = 0, + .end = allocatable_regs.len, + }, true); + break :blk set; + }; +}; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index bd17b2a9ae..8ce0d5884b 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -884,9 +884,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { if (self.intrinsicsAllowed(elem_ty)) { const ptr_bytes: u64 = 32; if (abi_size <= ptr_bytes) { - if (self.register_manager.tryAllocReg(inst, .{ - .selector_mask = sse, - })) |reg| { + if (self.register_manager.tryAllocReg(inst, sse)) |reg| { return MCValue{ .register = registerAlias(reg, abi_size) }; } } @@ -899,9 +897,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); if (abi_size <= ptr_bytes) { - if (self.register_manager.tryAllocReg(inst, .{ - .selector_mask = gp, - })) |reg| { + if (self.register_manager.tryAllocReg(inst, gp)) |reg| { return MCValue{ .register = registerAlias(reg, abi_size) }; } } @@ -972,16 +968,14 @@ pub fn spillRegisters(self: *Self, comptime count: comptime_int, registers: [cou /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { - const mask: RegisterManager.RegisterBitSet = switch (ty.zigTypeTag()) { + const reg_class: RegisterManager.RegisterBitSet = switch (ty.zigTypeTag()) { .Float => blk: { if (self.intrinsicsAllowed(ty)) break :blk sse; return self.fail("TODO copy {} to register", .{ty.fmtDebug()}); }, else => gp, }; - const reg: Register = try self.register_manager.allocReg(null, .{ - .selector_mask = mask, - }); + const reg: Register = try self.register_manager.allocReg(null, reg_class); try self.genSetReg(ty, reg, mcv); return reg; } @@ -991,16 +985,14 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { /// This can have a side effect of spilling instructions to the stack to free up a register. /// WARNING make sure that the allocated register matches the returned MCValue from an instruction! fn copyToRegisterWithInstTracking(self: *Self, reg_owner: Air.Inst.Index, ty: Type, mcv: MCValue) !MCValue { - const mask: RegisterManager.RegisterBitSet = switch (ty.zigTypeTag()) { + const reg_class: RegisterManager.RegisterBitSet = switch (ty.zigTypeTag()) { .Float => blk: { if (self.intrinsicsAllowed(ty)) break :blk sse; return self.fail("TODO copy {} to register", .{ty.fmtDebug()}); }, else => gp, }; - const reg: Register = try self.register_manager.allocReg(reg_owner, .{ - .selector_mask = mask, - }); + const reg: Register = try self.register_manager.allocReg(reg_owner, reg_class); try self.genSetReg(ty, reg, mcv); return MCValue{ .register = reg }; } @@ -1056,9 +1048,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { }; defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); - const reg = try self.register_manager.allocReg(inst, .{ - .selector_mask = gp, - }); + const reg = try self.register_manager.allocReg(inst, gp); try self.genSetReg(dest_ty, reg, .{ .immediate = 0 }); try self.genSetReg(operand_ty, reg, operand); break :blk MCValue{ .register = reg }; @@ -1413,9 +1403,7 @@ fn genSetStackTruncatedOverflowCompare( .unsigned => ty, }; - const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }, .{ - .selector_mask = gp, - }); + const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }, gp); const temp_regs_locks = self.register_manager.lockRegsAssumeUnused(3, temp_regs); defer for (temp_regs_locks) |rreg| { self.register_manager.unlockReg(rreg); @@ -2077,9 +2065,7 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); defer self.register_manager.unlockReg(offset_reg_lock); - const addr_reg = try self.register_manager.allocReg(null, .{ - .selector_mask = gp, - }); + const addr_reg = try self.register_manager.allocReg(null, gp); switch (slice_mcv) { .stack_offset => |off| { // mov reg, [rbp - 8] @@ -2158,9 +2144,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); defer self.register_manager.unlockReg(offset_reg_lock); - const addr_reg = try self.register_manager.allocReg(null, .{ - .selector_mask = gp, - }); + const addr_reg = try self.register_manager.allocReg(null, gp); switch (array) { .register => { const off = @intCast(i32, try self.allocMem( @@ -2527,7 +2511,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo }, .stack_offset => |off| { if (abi_size <= 8) { - const tmp_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); + const tmp_reg = try self.register_manager.allocReg(null, gp); try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); return self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }, .{}); } @@ -2728,7 +2712,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type }; defer if (value_lock) |lock| self.register_manager.unlockReg(lock); - const addr_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); + const addr_reg = try self.register_manager.allocReg(null, gp); const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); defer self.register_manager.unlockReg(addr_reg_lock); @@ -2800,7 +2784,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .memory, => { if (abi_size <= 8) { - const tmp_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); + const tmp_reg = try self.register_manager.allocReg(null, gp); const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); defer self.register_manager.unlockReg(tmp_reg_lock); @@ -2918,7 +2902,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde if (can_reuse_operand) { break :blk reg; } else { - const result_reg = try self.register_manager.allocReg(inst, .{ .selector_mask = gp }); + const result_reg = try self.register_manager.allocReg(inst, gp); try self.genSetReg(ptr_ty, result_reg, mcv); break :blk result_reg; } @@ -3019,7 +3003,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const reg_lock = self.register_manager.lockRegAssumeUnused(reg); defer self.register_manager.unlockReg(reg_lock); - const dst_reg = try self.register_manager.allocReg(inst, .{ .selector_mask = gp }); + const dst_reg = try self.register_manager.allocReg(inst, gp); const flags: u2 = switch (mcv) { .register_overflow_unsigned => 0b10, .register_overflow_signed => 0b00, @@ -5428,7 +5412,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl const overflow_bit_ty = ty.structFieldType(1); const overflow_bit_offset = ty.structFieldOffset(1, self.target.*); - const tmp_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); + const tmp_reg = try self.register_manager.allocReg(null, gp); const flags: u2 = switch (mcv) { .register_overflow_unsigned => 0b10, .register_overflow_signed => 0b00, @@ -5656,7 +5640,7 @@ fn genInlineMemcpy( null; defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock); - const dst_addr_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); + const dst_addr_reg = try self.register_manager.allocReg(null, gp); switch (dst_ptr) { .memory, .got_load, @@ -5691,7 +5675,7 @@ fn genInlineMemcpy( const dst_addr_reg_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg); defer self.register_manager.unlockReg(dst_addr_reg_lock); - const src_addr_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); + const src_addr_reg = try self.register_manager.allocReg(null, gp); switch (src_ptr) { .memory, .got_load, @@ -5726,9 +5710,7 @@ fn genInlineMemcpy( const src_addr_reg_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg); defer self.register_manager.unlockReg(src_addr_reg_lock); - const regs = try self.register_manager.allocRegs(2, .{ null, null }, .{ - .selector_mask = gp, - }); + const regs = try self.register_manager.allocRegs(2, .{ null, null }, gp); const count_reg = regs[0].to64(); const tmp_reg = regs[1].to8(); @@ -5828,7 +5810,7 @@ fn genInlineMemset( const rax_lock = self.register_manager.lockRegAssumeUnused(.rax); defer self.register_manager.unlockReg(rax_lock); - const addr_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); + const addr_reg = try self.register_manager.allocReg(null, gp); switch (dst_ptr) { .memory, .got_load, @@ -6087,7 +6069,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void => { switch (ty.zigTypeTag()) { .Float => { - const base_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); + const base_reg = try self.register_manager.allocReg(null, gp); try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); if (self.intrinsicsAllowed(ty)) { @@ -6130,7 +6112,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }, .memory => |x| switch (ty.zigTypeTag()) { .Float => { - const base_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); + const base_reg = try self.register_manager.allocReg(null, gp); try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); if (self.intrinsicsAllowed(ty)) { @@ -6461,7 +6443,7 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { const src: MCValue = blk: { switch (src_ptr) { .got_load, .direct_load, .memory => { - const reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); + const reg = try self.register_manager.allocReg(null, gp); try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr); _ = try self.addInst(.{ .tag = .mov, diff --git a/src/register_manager.zig b/src/register_manager.zig index 7b82253e22..a73f345e0c 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -57,6 +57,11 @@ pub fn RegisterManager( return @fieldParentPtr(Function, "register_manager", self); } + fn excludeRegister(reg: Register, register_class: RegisterBitSet) bool { + const index = indexOfRegIntoTracked(reg) orelse return true; + return !register_class.isSet(index); + } + fn markRegAllocated(self: *Self, reg: Register) void { const index = indexOfRegIntoTracked(reg) orelse return; self.allocated_registers.set(index); @@ -167,10 +172,6 @@ pub fn RegisterManager( return self.locked_registers.count() > 0; } - const AllocOpts = struct { - selector_mask: ?RegisterBitSet = null, - }; - /// Allocates a specified number of registers, optionally /// tracking them. Returns `null` if not enough registers are /// free. @@ -178,14 +179,12 @@ pub fn RegisterManager( self: *Self, comptime count: comptime_int, insts: [count]?Air.Inst.Index, - opts: AllocOpts, + register_class: RegisterBitSet, ) ?[count]Register { comptime assert(count > 0 and count <= tracked_registers.len); - const available_registers = opts.selector_mask orelse RegisterBitSet.initFull(); - var free_and_not_locked_registers = self.free_registers; - free_and_not_locked_registers.setIntersection(available_registers); + free_and_not_locked_registers.setIntersection(register_class); var unlocked_registers = self.locked_registers; unlocked_registers.toggleAll(); @@ -198,6 +197,7 @@ pub fn RegisterManager( var i: usize = 0; for (tracked_registers) |reg| { if (i >= count) break; + if (excludeRegister(reg, register_class)) continue; if (self.isRegLocked(reg)) continue; if (!self.isRegFree(reg)) continue; @@ -223,8 +223,8 @@ pub fn RegisterManager( /// Allocates a register and optionally tracks it with a /// corresponding instruction. Returns `null` if all registers /// are allocated. - pub fn tryAllocReg(self: *Self, inst: ?Air.Inst.Index, opts: AllocOpts) ?Register { - return if (tryAllocRegs(self, 1, .{inst}, opts)) |regs| regs[0] else null; + pub fn tryAllocReg(self: *Self, inst: ?Air.Inst.Index, register_class: RegisterBitSet) ?Register { + return if (tryAllocRegs(self, 1, .{inst}, register_class)) |regs| regs[0] else null; } /// Allocates a specified number of registers, optionally @@ -234,18 +234,16 @@ pub fn RegisterManager( self: *Self, comptime count: comptime_int, insts: [count]?Air.Inst.Index, - opts: AllocOpts, + register_class: RegisterBitSet, ) AllocateRegistersError![count]Register { comptime assert(count > 0 and count <= tracked_registers.len); - const available_registers = opts.selector_mask orelse RegisterBitSet.initFull(); - var locked_registers = self.locked_registers; - locked_registers.setIntersection(available_registers); + locked_registers.setIntersection(register_class); - if (count > available_registers.count() - locked_registers.count()) return error.OutOfRegisters; + if (count > register_class.count() - locked_registers.count()) return error.OutOfRegisters; - const result = self.tryAllocRegs(count, insts, opts) orelse blk: { + const result = self.tryAllocRegs(count, insts, register_class) orelse blk: { // We'll take over the first count registers. Spill // the instructions that were previously there to a // stack allocations. @@ -253,6 +251,7 @@ pub fn RegisterManager( var i: usize = 0; for (tracked_registers) |reg| { if (i >= count) break; + if (excludeRegister(reg, register_class)) continue; if (self.isRegLocked(reg)) continue; regs[i] = reg; @@ -288,8 +287,12 @@ pub fn RegisterManager( /// Allocates a register and optionally tracks it with a /// corresponding instruction. - pub fn allocReg(self: *Self, inst: ?Air.Inst.Index, opts: AllocOpts) AllocateRegistersError!Register { - return (try self.allocRegs(1, .{inst}, opts))[0]; + pub fn allocReg( + self: *Self, + inst: ?Air.Inst.Index, + register_class: RegisterBitSet, + ) AllocateRegistersError!Register { + return (try self.allocRegs(1, .{inst}, register_class))[0]; } /// Spills the register if it is currently allocated. If a @@ -374,11 +377,15 @@ const MockRegister2 = enum(u2) { fn MockFunction(comptime Register: type) type { return struct { allocator: Allocator, - register_manager: RegisterManager(Self, Register, &Register.allocatable_registers) = .{}, + register_manager: RegisterManagerT = .{}, spilled: std.ArrayListUnmanaged(Register) = .{}, const Self = @This(); + const RegisterManagerT = RegisterManager(Self, Register, &Register.allocatable_registers); + + pub const reg_class: RegisterManagerT.RegisterBitSet = RegisterManagerT.RegisterBitSet.initFull(); + pub fn deinit(self: *Self) void { self.spilled.deinit(self.allocator); } @@ -423,10 +430,20 @@ test "tryAllocReg: no spilling" { defer function.deinit(); const mock_instruction: Air.Inst.Index = 1; + const reg_class = MockFunction1.reg_class; - try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(mock_instruction, .{})); - try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(mock_instruction, .{})); - try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(mock_instruction, .{})); + try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg( + mock_instruction, + reg_class, + )); + try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg( + mock_instruction, + reg_class, + )); + try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg( + mock_instruction, + reg_class, + )); try expect(function.register_manager.isRegAllocated(.r2)); try expect(function.register_manager.isRegAllocated(.r3)); @@ -451,17 +468,30 @@ test "allocReg: spilling" { defer function.deinit(); const mock_instruction: Air.Inst.Index = 1; + const reg_class = MockFunction1.reg_class; - try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction, .{})); - try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, .{})); + try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg( + mock_instruction, + reg_class, + )); + try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg( + mock_instruction, + reg_class, + )); // Spill a register - try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction, .{})); + try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg( + mock_instruction, + reg_class, + )); try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); // No spilling necessary function.register_manager.freeReg(.r3); - try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, .{})); + try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg( + mock_instruction, + reg_class, + )); try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); // Locked registers @@ -470,7 +500,10 @@ test "allocReg: spilling" { const lock = function.register_manager.lockReg(.r2); defer if (lock) |reg| function.register_manager.unlockReg(reg); - try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, .{})); + try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg( + mock_instruction, + reg_class, + )); } try expect(!function.register_manager.lockedRegsExist()); } @@ -483,7 +516,13 @@ test "tryAllocRegs" { }; defer function.deinit(); - try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, .{}).?); + const reg_class = MockFunction2.reg_class; + + try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs( + 3, + .{ null, null, null }, + reg_class, + ).?); try expect(function.register_manager.isRegAllocated(.r0)); try expect(function.register_manager.isRegAllocated(.r1)); @@ -498,7 +537,11 @@ test "tryAllocRegs" { const lock = function.register_manager.lockReg(.r1); defer if (lock) |reg| function.register_manager.unlockReg(reg); - try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, .{}).?); + try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs( + 3, + .{ null, null, null }, + reg_class, + ).?); } try expect(!function.register_manager.lockedRegsExist()); @@ -518,6 +561,8 @@ test "allocRegs: normal usage" { }; defer function.deinit(); + const reg_class = MockFunction2.reg_class; + { const result_reg: MockRegister2 = .r1; @@ -537,7 +582,7 @@ test "allocRegs: normal usage" { const lock = function.register_manager.lockReg(result_reg); defer if (lock) |reg| function.register_manager.unlockReg(reg); - const regs = try function.register_manager.allocRegs(2, .{ null, null }, .{}); + const regs = try function.register_manager.allocRegs(2, .{ null, null }, reg_class); try function.genAdd(result_reg, regs[0], regs[1]); } } @@ -552,6 +597,8 @@ test "allocRegs: selectively reducing register pressure" { }; defer function.deinit(); + const reg_class = MockFunction2.reg_class; + { const result_reg: MockRegister2 = .r1; @@ -559,12 +606,12 @@ test "allocRegs: selectively reducing register pressure" { // Here, we don't defer unlock because we manually unlock // after genAdd - const regs = try function.register_manager.allocRegs(2, .{ null, null }, .{}); + const regs = try function.register_manager.allocRegs(2, .{ null, null }, reg_class); try function.genAdd(result_reg, regs[0], regs[1]); function.register_manager.unlockReg(lock.?); - const extra_summand_reg = try function.register_manager.allocReg(null, .{}); + const extra_summand_reg = try function.register_manager.allocReg(null, reg_class); try function.genAdd(result_reg, result_reg, extra_summand_reg); } } From 1d532f12b568c924baead9de78701f66a526e16b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Thu, 19 May 2022 16:36:01 +0300 Subject: [PATCH 1590/2031] [Elf] add -z nocopyreloc Warnings about non-implemented `-z nocopyreloc` are common when compiling go code (including Go's tests themselves). Let's just make it stop complaining. --- src/Compilation.zig | 7 +++++-- src/link.zig | 1 + src/link/Coff.zig | 2 +- src/link/Elf.zig | 7 ++++++- src/link/MachO.zig | 2 +- src/link/Wasm.zig | 2 +- src/main.zig | 1 + 7 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 687b0a6dc6..7ac1e49196 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -764,6 +764,7 @@ pub const InitOptions = struct { linker_z_noexecstack: bool = false, linker_z_now: bool = false, linker_z_relro: bool = false, + linker_z_nocopyreloc: bool = false, linker_tsaware: bool = false, linker_nxcompat: bool = false, linker_dynamicbase: bool = false, @@ -1597,6 +1598,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .z_notext = options.linker_z_notext, .z_defs = options.linker_z_defs, .z_origin = options.linker_z_origin, + .z_nocopyreloc = options.linker_z_nocopyreloc, .z_noexecstack = options.linker_z_noexecstack, .z_now = options.linker_z_now, .z_relro = options.linker_z_relro, @@ -2255,7 +2257,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo /// to remind the programmer to update multiple related pieces of code that /// are in different locations. Bump this number when adding or deleting /// anything from the link cache manifest. -pub const link_hash_implementation_version = 2; +pub const link_hash_implementation_version = 3; fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void { const gpa = comp.gpa; @@ -2265,7 +2267,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - comptime assert(link_hash_implementation_version == 2); + comptime assert(link_hash_implementation_version == 3); if (comp.bin_file.options.module) |mod| { const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{ @@ -2333,6 +2335,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes man.hash.add(comp.bin_file.options.z_notext); man.hash.add(comp.bin_file.options.z_defs); man.hash.add(comp.bin_file.options.z_origin); + man.hash.add(comp.bin_file.options.z_nocopyreloc); man.hash.add(comp.bin_file.options.z_noexecstack); man.hash.add(comp.bin_file.options.z_now); man.hash.add(comp.bin_file.options.z_relro); diff --git a/src/link.zig b/src/link.zig index ebcec92ac1..8647259651 100644 --- a/src/link.zig +++ b/src/link.zig @@ -112,6 +112,7 @@ pub const Options = struct { z_notext: bool, z_defs: bool, z_origin: bool, + z_nocopyreloc: bool, z_noexecstack: bool, z_now: bool, z_relro: bool, diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 178a6ab6b4..37ec8d0758 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -969,7 +969,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) ! man = comp.cache_parent.obtain(); self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 2); + comptime assert(Compilation.link_hash_implementation_version == 3); for (self.base.options.objects) |obj| { _ = try man.addFile(obj.path, null); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index be366076b4..06b241eb4d 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1294,7 +1294,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // We are about to obtain this lock, so here we give other processes a chance first. self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 2); + comptime assert(Compilation.link_hash_implementation_version == 3); try man.addOptionalFile(self.base.options.linker_script); try man.addOptionalFile(self.base.options.version_script); @@ -1325,6 +1325,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v man.hash.add(self.base.options.z_notext); man.hash.add(self.base.options.z_defs); man.hash.add(self.base.options.z_origin); + man.hash.add(self.base.options.z_nocopyreloc); man.hash.add(self.base.options.z_noexecstack); man.hash.add(self.base.options.z_now); man.hash.add(self.base.options.z_relro); @@ -1496,6 +1497,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append("-z"); try argv.append("origin"); } + if (self.base.options.z_nocopyreloc) { + try argv.append("-z"); + try argv.append("nocopyreloc"); + } if (self.base.options.z_noexecstack) { try argv.append("-z"); try argv.append("noexecstack"); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 964279c5d1..853d62bdce 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -527,7 +527,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No // We are about to obtain this lock, so here we give other processes a chance first. self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 2); + comptime assert(Compilation.link_hash_implementation_version == 3); for (self.base.options.objects) |obj| { _ = try man.addFile(obj.path, null); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index d8c87703a6..2b4b9bb089 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2274,7 +2274,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! // We are about to obtain this lock, so here we give other processes a chance first. self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 2); + comptime assert(Compilation.link_hash_implementation_version == 3); for (self.base.options.objects) |obj| { _ = try man.addFile(obj.path, null); diff --git a/src/main.zig b/src/main.zig index bd923ed85f..55def40d96 100644 --- a/src/main.zig +++ b/src/main.zig @@ -430,6 +430,7 @@ const usage_build_generic = \\ notext Permit read-only relocations in read-only segments \\ defs Force a fatal error if any undefined symbols remain \\ origin Indicate that the object must have its origin processed + \\ nocopyreloc Disable the creation of copy relocations \\ noexecstack Indicate that the object requires an executable stack \\ now Force all relocations to be processed on load \\ relro Force all relocations to be resolved and be read-only on load From 0e43d007c098837d1863f2dab80b99e95910e0aa Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 May 2022 00:04:56 +0200 Subject: [PATCH 1591/2031] regalloc: temporarily nerf back to raw ints until stage2 catches up --- src/arch/aarch64/abi.zig | 19 +- src/arch/arm/abi.zig | 19 +- src/arch/riscv64/abi.zig | 19 +- src/arch/sparc64/abi.zig | 19 +- src/arch/x86_64/abi.zig | 38 ++-- src/register_manager.zig | 414 +++++++++++++++++++++++++++++++++++---- 6 files changed, 439 insertions(+), 89 deletions(-) diff --git a/src/arch/aarch64/abi.zig b/src/arch/aarch64/abi.zig index 839b2789e4..89a3a6c21d 100644 --- a/src/arch/aarch64/abi.zig +++ b/src/arch/aarch64/abi.zig @@ -1,3 +1,4 @@ +const std = @import("std"); const builtin = @import("builtin"); const bits = @import("bits.zig"); const Register = bits.Register; @@ -26,12 +27,14 @@ pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, // Register classes const RegisterBitSet = RegisterManager.RegisterBitSet; pub const RegisterClass = struct { - pub const gp: RegisterBitSet = blk: { - var set = RegisterBitSet.initEmpty(); - set.setRangeValue(.{ - .start = 0, - .end = callee_preserved_regs.len, - }, true); - break :blk set; - }; + pub const gp: RegisterBitSet = std.math.maxInt(RegisterBitSet); + // TODO uncomment once #11680 is fixed. + // pub const gp: RegisterBitSet = blk: { + // var set = RegisterBitSet.initEmpty(); + // set.setRangeValue(.{ + // .start = 0, + // .end = callee_preserved_regs.len, + // }, true); + // break :blk set; + // }; }; diff --git a/src/arch/arm/abi.zig b/src/arch/arm/abi.zig index dbee1f9b90..c76c3b0ea0 100644 --- a/src/arch/arm/abi.zig +++ b/src/arch/arm/abi.zig @@ -1,3 +1,4 @@ +const std = @import("std"); const bits = @import("bits.zig"); const Register = bits.Register; const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; @@ -14,12 +15,14 @@ pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, // Register classes const RegisterBitSet = RegisterManager.RegisterBitSet; pub const RegisterClass = struct { - pub const gp: RegisterBitSet = blk: { - var set = RegisterBitSet.initEmpty(); - set.setRangeValue(.{ - .start = 0, - .end = caller_preserved_regs.len + callee_preserved_regs.len, - }, true); - break :blk set; - }; + pub const gp: RegisterBitSet = std.math.maxInt(RegisterBitSet); + // TODO uncomment once #11680 is fixed. + // pub const gp: RegisterBitSet = blk: { + // var set = RegisterBitSet.initEmpty(); + // set.setRangeValue(.{ + // .start = 0, + // .end = caller_preserved_regs.len + callee_preserved_regs.len, + // }, true); + // break :blk set; + // }; }; diff --git a/src/arch/riscv64/abi.zig b/src/arch/riscv64/abi.zig index 459fd40c9c..30d3719a46 100644 --- a/src/arch/riscv64/abi.zig +++ b/src/arch/riscv64/abi.zig @@ -1,3 +1,4 @@ +const std = @import("std"); const bits = @import("bits.zig"); const Register = bits.Register; const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; @@ -12,12 +13,14 @@ pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, // Register classes const RegisterBitSet = RegisterManager.RegisterBitSet; pub const RegisterClass = struct { - pub const gp: RegisterBitSet = blk: { - var set = RegisterBitSet.initEmpty(); - set.setRangeValue(.{ - .start = 0, - .end = callee_preserved_regs.len, - }, true); - break :blk set; - }; + pub const gp: RegisterBitSet = std.math.maxInt(RegisterBitSet); + // TODO uncomment once #11680 is fixed. + // pub const gp: RegisterBitSet = blk: { + // var set = RegisterBitSet.initEmpty(); + // set.setRangeValue(.{ + // .start = 0, + // .end = callee_preserved_regs.len, + // }, true); + // break :blk set; + // }; }; diff --git a/src/arch/sparc64/abi.zig b/src/arch/sparc64/abi.zig index b1985a7b0b..1c6d40941f 100644 --- a/src/arch/sparc64/abi.zig +++ b/src/arch/sparc64/abi.zig @@ -1,3 +1,4 @@ +const std = @import("std"); const bits = @import("bits.zig"); const Register = bits.Register; const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; @@ -42,12 +43,14 @@ pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, // Register classes const RegisterBitSet = RegisterManager.RegisterBitSet; pub const RegisterClass = struct { - pub const gp: RegisterBitSet = blk: { - var set = RegisterBitSet.initEmpty(); - set.setRangeValue(.{ - .start = 0, - .end = allocatable_regs.len, - }, true); - break :blk set; - }; + pub const gp: RegisterBitSet = std.math.maxInt(RegisterBitSet); + // TODO uncomment once #11680 is fixed. + // pub const gp: RegisterBitSet = blk: { + // var set = RegisterBitSet.initEmpty(); + // set.setRangeValue(.{ + // .start = 0, + // .end = allocatable_regs.len, + // }, true); + // break :blk set; + // }; }; diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index 7e2025a23d..77f28c11f4 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -393,20 +393,26 @@ pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, // Register classes const RegisterBitSet = RegisterManager.RegisterBitSet; pub const RegisterClass = struct { - pub const gp: RegisterBitSet = blk: { - var set = RegisterBitSet.initEmpty(); - set.setRangeValue(.{ - .start = 0, - .end = caller_preserved_regs.len + callee_preserved_regs.len, - }, true); - break :blk set; - }; - pub const sse: RegisterBitSet = blk: { - var set = RegisterBitSet.initEmpty(); - set.setRangeValue(.{ - .start = caller_preserved_regs.len + callee_preserved_regs.len, - .end = allocatable_registers.len, - }, true); - break :blk set; - }; + pub const gp: RegisterBitSet = @as(RegisterBitSet, std.math.maxInt(std.meta.Int( + .unsigned, + caller_preserved_regs.len + callee_preserved_regs.len, + ))); + pub const sse: RegisterBitSet = std.math.maxInt(RegisterBitSet) - gp; + // TODO uncomment once #11680 is fixed. + // pub const gp: RegisterBitSet = blk: { + // var set = RegisterBitSet.initEmpty(); + // set.setRangeValue(.{ + // .start = 0, + // .end = caller_preserved_regs.len + callee_preserved_regs.len, + // }, true); + // break :blk set; + // }; + // pub const sse: RegisterBitSet = blk: { + // var set = RegisterBitSet.initEmpty(); + // set.setRangeValue(.{ + // .start = caller_preserved_regs.len + callee_preserved_regs.len, + // .end = allocatable_registers.len, + // }, true); + // break :blk set; + // }; }; diff --git a/src/register_manager.zig b/src/register_manager.zig index a73f345e0c..347c916769 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -4,7 +4,6 @@ const mem = std.mem; const assert = std.debug.assert; const Allocator = std.mem.Allocator; const Air = @import("Air.zig"); -const StaticBitSet = std.bit_set.StaticBitSet; const Type = @import("type.zig").Type; const Module = @import("Module.zig"); const expect = std.testing.expect; @@ -42,39 +41,49 @@ pub fn RegisterManager( registers: [tracked_registers.len]Air.Inst.Index = undefined, /// Tracks which registers are free (in which case the /// corresponding bit is set to 1) - free_registers: RegisterBitSet = RegisterBitSet.initFull(), + free_registers: RegisterBitSet = math.maxInt(RegisterBitSet), /// Tracks all registers allocated in the course of this /// function - allocated_registers: RegisterBitSet = RegisterBitSet.initEmpty(), + allocated_registers: RegisterBitSet = 0, /// Tracks registers which are locked from being allocated - locked_registers: RegisterBitSet = RegisterBitSet.initEmpty(), + locked_registers: RegisterBitSet = 0, const Self = @This(); - pub const RegisterBitSet = StaticBitSet(tracked_registers.len); + /// An integer whose bits represent all the registers and + /// whether they are free. + pub const RegisterBitSet = std.meta.Int(.unsigned, tracked_registers.len); + const ShiftInt = math.Log2Int(RegisterBitSet); fn getFunction(self: *Self) *Function { return @fieldParentPtr(Function, "register_manager", self); } fn excludeRegister(reg: Register, register_class: RegisterBitSet) bool { - const index = indexOfRegIntoTracked(reg) orelse return true; - return !register_class.isSet(index); + const mask = getRegisterMask(reg) orelse return true; + return mask & register_class == 0; + } + + fn getRegisterMask(reg: Register) ?RegisterBitSet { + const index = indexOfRegIntoTracked(reg) orelse return null; + const shift = @intCast(ShiftInt, index); + const mask = @as(RegisterBitSet, 1) << shift; + return mask; } fn markRegAllocated(self: *Self, reg: Register) void { - const index = indexOfRegIntoTracked(reg) orelse return; - self.allocated_registers.set(index); + const mask = getRegisterMask(reg) orelse return; + self.allocated_registers |= mask; } fn markRegUsed(self: *Self, reg: Register) void { - const index = indexOfRegIntoTracked(reg) orelse return; - self.free_registers.unset(index); + const mask = getRegisterMask(reg) orelse return; + self.free_registers &= ~mask; } fn markRegFree(self: *Self, reg: Register) void { - const index = indexOfRegIntoTracked(reg) orelse return; - self.free_registers.set(index); + const mask = getRegisterMask(reg) orelse return; + self.free_registers |= mask; } pub fn indexOfReg( @@ -87,14 +96,14 @@ pub fn RegisterManager( return null; } - pub fn indexOfRegIntoTracked(reg: Register) ?RegisterBitSet.ShiftInt { + pub fn indexOfRegIntoTracked(reg: Register) ?ShiftInt { return indexOfReg(tracked_registers, reg); } /// Returns true when this register is not tracked pub fn isRegFree(self: Self, reg: Register) bool { - const index = indexOfRegIntoTracked(reg) orelse return true; - return self.free_registers.isSet(index); + const mask = getRegisterMask(reg) orelse return true; + return self.free_registers & mask != 0; } /// Returns whether this register was allocated in the course @@ -102,16 +111,16 @@ pub fn RegisterManager( /// /// Returns false when this register is not tracked pub fn isRegAllocated(self: Self, reg: Register) bool { - const index = indexOfRegIntoTracked(reg) orelse return false; - return self.allocated_registers.isSet(index); + const mask = getRegisterMask(reg) orelse return false; + return self.allocated_registers & mask != 0; } /// Returns whether this register is locked /// /// Returns false when this register is not tracked pub fn isRegLocked(self: Self, reg: Register) bool { - const index = indexOfRegIntoTracked(reg) orelse return false; - return self.locked_registers.isSet(index); + const mask = getRegisterMask(reg) orelse return false; + return self.locked_registers & mask != 0; } pub const RegisterLock = struct { @@ -130,8 +139,8 @@ pub fn RegisterManager( log.debug(" register already locked", .{}); return null; } - const index = indexOfRegIntoTracked(reg) orelse return null; - self.locked_registers.set(index); + const mask = getRegisterMask(reg) orelse return null; + self.locked_registers |= mask; return RegisterLock{ .register = reg }; } @@ -140,8 +149,8 @@ pub fn RegisterManager( pub fn lockRegAssumeUnused(self: *Self, reg: Register) RegisterLock { log.debug("locking asserting free {}", .{reg}); assert(!self.isRegLocked(reg)); - const index = indexOfRegIntoTracked(reg) orelse unreachable; - self.locked_registers.set(index); + const mask = getRegisterMask(reg) orelse unreachable; + self.locked_registers |= mask; return RegisterLock{ .register = reg }; } @@ -163,13 +172,13 @@ pub fn RegisterManager( /// Call `lockReg` to obtain the lock first. pub fn unlockReg(self: *Self, lock: RegisterLock) void { log.debug("unlocking {}", .{lock.register}); - const index = indexOfRegIntoTracked(lock.register) orelse return; - self.locked_registers.unset(index); + const mask = getRegisterMask(lock.register) orelse return; + self.locked_registers &= ~mask; } /// Returns true when at least one register is locked pub fn lockedRegsExist(self: Self) bool { - return self.locked_registers.count() > 0; + return self.locked_registers != 0; } /// Allocates a specified number of registers, optionally @@ -183,15 +192,10 @@ pub fn RegisterManager( ) ?[count]Register { comptime assert(count > 0 and count <= tracked_registers.len); - var free_and_not_locked_registers = self.free_registers; - free_and_not_locked_registers.setIntersection(register_class); - - var unlocked_registers = self.locked_registers; - unlocked_registers.toggleAll(); - - free_and_not_locked_registers.setIntersection(unlocked_registers); - - if (free_and_not_locked_registers.count() < count) return null; + const free_registers = self.free_registers & register_class; + const free_and_not_locked_registers = free_registers & ~self.locked_registers; + const free_and_not_locked_registers_count = @popCount(RegisterBitSet, free_and_not_locked_registers); + if (free_and_not_locked_registers_count < count) return null; var regs: [count]Register = undefined; var i: usize = 0; @@ -238,10 +242,10 @@ pub fn RegisterManager( ) AllocateRegistersError![count]Register { comptime assert(count > 0 and count <= tracked_registers.len); - var locked_registers = self.locked_registers; - locked_registers.setIntersection(register_class); - - if (count > register_class.count() - locked_registers.count()) return error.OutOfRegisters; + const available_registers_count = @popCount(RegisterBitSet, register_class); + const locked_registers = self.locked_registers & register_class; + const locked_registers_count = @popCount(RegisterBitSet, locked_registers); + if (count > available_registers_count - locked_registers_count) return error.OutOfRegisters; const result = self.tryAllocRegs(count, insts, register_class) orelse blk: { // We'll take over the first count registers. Spill @@ -348,6 +352,334 @@ pub fn RegisterManager( }; } +// TODO delete current implementation of RegisterManager above, and uncomment the one +// below once #11680 is fixed: +// https://github.com/ziglang/zig/issues/11680 + +//pub fn RegisterManager( +// comptime Function: type, +// comptime Register: type, +// comptime tracked_registers: []const Register, +//) type { +// // architectures which do not have a concept of registers should +// // refrain from using RegisterManager +// assert(tracked_registers.len > 0); // see note above + +// return struct { +// /// Tracks the AIR instruction allocated to every register. If +// /// no instruction is allocated to a register (i.e. the +// /// register is free), the value in that slot is undefined. +// /// +// /// The key must be canonical register. +// registers: [tracked_registers.len]Air.Inst.Index = undefined, +// /// Tracks which registers are free (in which case the +// /// corresponding bit is set to 1) +// free_registers: RegisterBitSet = RegisterBitSet.initFull(), +// /// Tracks all registers allocated in the course of this +// /// function +// allocated_registers: RegisterBitSet = RegisterBitSet.initEmpty(), +// /// Tracks registers which are locked from being allocated +// locked_registers: RegisterBitSet = RegisterBitSet.initEmpty(), + +// const Self = @This(); + +// pub const RegisterBitSet = StaticBitSet(tracked_registers.len); + +// fn getFunction(self: *Self) *Function { +// return @fieldParentPtr(Function, "register_manager", self); +// } + +// fn excludeRegister(reg: Register, register_class: RegisterBitSet) bool { +// const index = indexOfRegIntoTracked(reg) orelse return true; +// return !register_class.isSet(index); +// } + +// fn markRegAllocated(self: *Self, reg: Register) void { +// const index = indexOfRegIntoTracked(reg) orelse return; +// self.allocated_registers.set(index); +// } + +// fn markRegUsed(self: *Self, reg: Register) void { +// const index = indexOfRegIntoTracked(reg) orelse return; +// self.free_registers.unset(index); +// } + +// fn markRegFree(self: *Self, reg: Register) void { +// const index = indexOfRegIntoTracked(reg) orelse return; +// self.free_registers.set(index); +// } + +// pub fn indexOfReg( +// comptime registers: []const Register, +// reg: Register, +// ) ?std.math.IntFittingRange(0, registers.len - 1) { +// inline for (tracked_registers) |cpreg, i| { +// if (reg.id() == cpreg.id()) return i; +// } +// return null; +// } + +// pub fn indexOfRegIntoTracked(reg: Register) ?RegisterBitSet.ShiftInt { +// return indexOfReg(tracked_registers, reg); +// } + +// /// Returns true when this register is not tracked +// pub fn isRegFree(self: Self, reg: Register) bool { +// const index = indexOfRegIntoTracked(reg) orelse return true; +// return self.free_registers.isSet(index); +// } + +// /// Returns whether this register was allocated in the course +// /// of this function. +// /// +// /// Returns false when this register is not tracked +// pub fn isRegAllocated(self: Self, reg: Register) bool { +// const index = indexOfRegIntoTracked(reg) orelse return false; +// return self.allocated_registers.isSet(index); +// } + +// /// Returns whether this register is locked +// /// +// /// Returns false when this register is not tracked +// pub fn isRegLocked(self: Self, reg: Register) bool { +// const index = indexOfRegIntoTracked(reg) orelse return false; +// return self.locked_registers.isSet(index); +// } + +// pub const RegisterLock = struct { +// register: Register, +// }; + +// /// Prevents the register from being allocated until they are +// /// unlocked again. +// /// Returns `RegisterLock` if the register was not already +// /// locked, or `null` otherwise. +// /// Only the owner of the `RegisterLock` can unlock the +// /// register later. +// pub fn lockReg(self: *Self, reg: Register) ?RegisterLock { +// log.debug("locking {}", .{reg}); +// if (self.isRegLocked(reg)) { +// log.debug(" register already locked", .{}); +// return null; +// } +// const index = indexOfRegIntoTracked(reg) orelse return null; +// self.locked_registers.set(index); +// return RegisterLock{ .register = reg }; +// } + +// /// Like `lockReg` but asserts the register was unused always +// /// returning a valid lock. +// pub fn lockRegAssumeUnused(self: *Self, reg: Register) RegisterLock { +// log.debug("locking asserting free {}", .{reg}); +// assert(!self.isRegLocked(reg)); +// const index = indexOfRegIntoTracked(reg) orelse unreachable; +// self.locked_registers.set(index); +// return RegisterLock{ .register = reg }; +// } + +// /// Like `lockRegAssumeUnused` but locks multiple registers. +// pub fn lockRegsAssumeUnused( +// self: *Self, +// comptime count: comptime_int, +// regs: [count]Register, +// ) [count]RegisterLock { +// var buf: [count]RegisterLock = undefined; +// for (regs) |reg, i| { +// buf[i] = self.lockRegAssumeUnused(reg); +// } +// return buf; +// } + +// /// Unlocks the register allowing its re-allocation and re-use. +// /// Requires `RegisterLock` to unlock a register. +// /// Call `lockReg` to obtain the lock first. +// pub fn unlockReg(self: *Self, lock: RegisterLock) void { +// log.debug("unlocking {}", .{lock.register}); +// const index = indexOfRegIntoTracked(lock.register) orelse return; +// self.locked_registers.unset(index); +// } + +// /// Returns true when at least one register is locked +// pub fn lockedRegsExist(self: Self) bool { +// return self.locked_registers.count() > 0; +// } + +// /// Allocates a specified number of registers, optionally +// /// tracking them. Returns `null` if not enough registers are +// /// free. +// pub fn tryAllocRegs( +// self: *Self, +// comptime count: comptime_int, +// insts: [count]?Air.Inst.Index, +// register_class: RegisterBitSet, +// ) ?[count]Register { +// comptime assert(count > 0 and count <= tracked_registers.len); + +// var free_and_not_locked_registers = self.free_registers; +// free_and_not_locked_registers.setIntersection(register_class); + +// var unlocked_registers = self.locked_registers; +// unlocked_registers.toggleAll(); + +// free_and_not_locked_registers.setIntersection(unlocked_registers); + +// if (free_and_not_locked_registers.count() < count) return null; + +// var regs: [count]Register = undefined; +// var i: usize = 0; +// for (tracked_registers) |reg| { +// if (i >= count) break; +// if (excludeRegister(reg, register_class)) continue; +// if (self.isRegLocked(reg)) continue; +// if (!self.isRegFree(reg)) continue; + +// regs[i] = reg; +// i += 1; +// } +// assert(i == count); + +// for (regs) |reg, j| { +// self.markRegAllocated(reg); + +// if (insts[j]) |inst| { +// // Track the register +// const index = indexOfRegIntoTracked(reg).?; // indexOfReg() on a callee-preserved reg should never return null +// self.registers[index] = inst; +// self.markRegUsed(reg); +// } +// } + +// return regs; +// } + +// /// Allocates a register and optionally tracks it with a +// /// corresponding instruction. Returns `null` if all registers +// /// are allocated. +// pub fn tryAllocReg(self: *Self, inst: ?Air.Inst.Index, register_class: RegisterBitSet) ?Register { +// return if (tryAllocRegs(self, 1, .{inst}, register_class)) |regs| regs[0] else null; +// } + +// /// Allocates a specified number of registers, optionally +// /// tracking them. Asserts that count is not +// /// larger than the total number of registers available. +// pub fn allocRegs( +// self: *Self, +// comptime count: comptime_int, +// insts: [count]?Air.Inst.Index, +// register_class: RegisterBitSet, +// ) AllocateRegistersError![count]Register { +// comptime assert(count > 0 and count <= tracked_registers.len); + +// var locked_registers = self.locked_registers; +// locked_registers.setIntersection(register_class); + +// if (count > register_class.count() - locked_registers.count()) return error.OutOfRegisters; + +// const result = self.tryAllocRegs(count, insts, register_class) orelse blk: { +// // We'll take over the first count registers. Spill +// // the instructions that were previously there to a +// // stack allocations. +// var regs: [count]Register = undefined; +// var i: usize = 0; +// for (tracked_registers) |reg| { +// if (i >= count) break; +// if (excludeRegister(reg, register_class)) break; +// if (self.isRegLocked(reg)) continue; + +// regs[i] = reg; +// self.markRegAllocated(reg); +// const index = indexOfRegIntoTracked(reg).?; // indexOfReg() on a callee-preserved reg should never return null +// if (insts[i]) |inst| { +// // Track the register +// if (self.isRegFree(reg)) { +// self.markRegUsed(reg); +// } else { +// const spilled_inst = self.registers[index]; +// try self.getFunction().spillInstruction(reg, spilled_inst); +// } +// self.registers[index] = inst; +// } else { +// // Don't track the register +// if (!self.isRegFree(reg)) { +// const spilled_inst = self.registers[index]; +// try self.getFunction().spillInstruction(reg, spilled_inst); +// self.freeReg(reg); +// } +// } + +// i += 1; +// } + +// break :blk regs; +// }; + +// log.debug("allocated registers {any} for insts {any}", .{ result, insts }); +// return result; +// } + +// /// Allocates a register and optionally tracks it with a +// /// corresponding instruction. +// pub fn allocReg( +// self: *Self, +// inst: ?Air.Inst.Index, +// register_class: RegisterBitSet, +// ) AllocateRegistersError!Register { +// return (try self.allocRegs(1, .{inst}, register_class))[0]; +// } + +// /// Spills the register if it is currently allocated. If a +// /// corresponding instruction is passed, will also track this +// /// register. +// pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocateRegistersError!void { +// const index = indexOfRegIntoTracked(reg) orelse return; +// log.debug("getReg {} for inst {}", .{ reg, inst }); +// self.markRegAllocated(reg); + +// if (inst) |tracked_inst| +// if (!self.isRegFree(reg)) { +// // Move the instruction that was previously there to a +// // stack allocation. +// const spilled_inst = self.registers[index]; +// self.registers[index] = tracked_inst; +// try self.getFunction().spillInstruction(reg, spilled_inst); +// } else { +// self.getRegAssumeFree(reg, tracked_inst); +// } +// else { +// if (!self.isRegFree(reg)) { +// // Move the instruction that was previously there to a +// // stack allocation. +// const spilled_inst = self.registers[index]; +// try self.getFunction().spillInstruction(reg, spilled_inst); +// self.freeReg(reg); +// } +// } +// } + +// /// Allocates the specified register with the specified +// /// instruction. Asserts that the register is free and no +// /// spilling is necessary. +// pub fn getRegAssumeFree(self: *Self, reg: Register, inst: Air.Inst.Index) void { +// const index = indexOfRegIntoTracked(reg) orelse return; +// log.debug("getRegAssumeFree {} for inst {}", .{ reg, inst }); +// self.markRegAllocated(reg); + +// assert(self.isRegFree(reg)); +// self.registers[index] = inst; +// self.markRegUsed(reg); +// } + +// /// Marks the specified register as free +// pub fn freeReg(self: *Self, reg: Register) void { +// const index = indexOfRegIntoTracked(reg) orelse return; +// log.debug("freeing register {}", .{reg}); + +// self.registers[index] = undefined; +// self.markRegFree(reg); +// } +// }; +//} + const MockRegister1 = enum(u2) { r0, r1, @@ -384,7 +716,7 @@ fn MockFunction(comptime Register: type) type { const RegisterManagerT = RegisterManager(Self, Register, &Register.allocatable_registers); - pub const reg_class: RegisterManagerT.RegisterBitSet = RegisterManagerT.RegisterBitSet.initFull(); + pub const reg_class: RegisterManagerT.RegisterBitSet = math.maxInt(RegisterManagerT.RegisterBitSet); pub fn deinit(self: *Self) void { self.spilled.deinit(self.allocator); From 58943fc6276c49b138c98bda5abac8be2806f94f Mon Sep 17 00:00:00 2001 From: Ali Chraghi Date: Thu, 19 May 2022 16:32:18 +0430 Subject: [PATCH 1592/2031] wasm-linker: add -mwasm64 linker parameter for wasm64 target --- src/link/Wasm.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 2b4b9bb089..ee2ed19ed5 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2486,6 +2486,10 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! full_out_path, }); + if (target.cpu.arch == .wasm64) { + try argv.append("-mwasm64"); + } + if (target.os.tag == .wasi) { const is_exe_or_dyn_lib = self.base.options.output_mode == .Exe or (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Dynamic); From 5626bb45d24cd4a57a4f6a1c0f41ec0feb276f7b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 May 2022 14:46:04 -0700 Subject: [PATCH 1593/2031] Sema: fix comptime `@floatCast` downcast --- src/Sema.zig | 4 ++-- test/behavior/cast.zig | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index b28536fe48..feed4d0e8a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7236,8 +7236,8 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A ), } - if (try sema.isComptimeKnown(block, operand_src, operand)) { - return sema.coerce(block, dest_ty, operand, operand_src); + if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| { + return sema.addConstant(dest_ty, try operand_val.floatCast(sema.arena, dest_ty, target)); } if (dest_is_comptime_float) { return sema.fail(block, src, "unable to cast runtime value to 'comptime_float'", .{}); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 6753b731f0..58b458ad1e 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -630,7 +630,9 @@ test "vector casts" { } test "@floatCast cast down" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO { var double: f64 = 0.001534; From cd04b49041200b36c5af23ac3700cbfa82f037ca Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 May 2022 17:01:45 -0700 Subject: [PATCH 1594/2031] stage2: fix `@call` when used in a comptime or nosuspend block `@call` allows specifying the modifier explicitly, however it can still appear in a context that overrides the modifier. This commit adds flags to the BuiltinCall ZIR encoding. Since we have unused bits I also threw in the ensure_result_used mechanism. I also deleted a behavior test that was checking for bound function behavior where I think stage2 behavior is correct and stage1 behavior is incorrect. --- src/AstGen.zig | 15 ++++++++++++- src/Sema.zig | 49 ++++++++++++++++++++++++++++++++++++------ src/Zir.zig | 26 ++++++++++++++++++---- src/print_zir.zig | 9 ++++++-- test/behavior/call.zig | 31 ++++++++------------------ 5 files changed, 94 insertions(+), 36 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 3e502625db..30ed680226 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -71,6 +71,7 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void { Zir.Inst.Ref => @enumToInt(@field(extra, field.name)), i32 => @bitCast(u32, @field(extra, field.name)), Zir.Inst.Call.Flags => @bitCast(u32, @field(extra, field.name)), + Zir.Inst.BuiltinCall.Flags => @bitCast(u32, @field(extra, field.name)), Zir.Inst.SwitchBlock.Bits => @bitCast(u32, @field(extra, field.name)), Zir.Inst.ExtendedFunc.Bits => @bitCast(u32, @field(extra, field.name)), else => @compileError("bad field type"), @@ -2213,6 +2214,14 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner slot.* = @bitCast(u32, flags); break :b true; }, + .builtin_call => { + const extra_index = gz.astgen.instructions.items(.data)[inst].pl_node.payload_index; + const slot = &gz.astgen.extra.items[extra_index]; + var flags = @bitCast(Zir.Inst.BuiltinCall.Flags, slot.*); + flags.ensure_result_used = true; + slot.* = @bitCast(u32, flags); + break :b true; + }, // ZIR instructions that might be a type other than `noreturn` or `void`. .add, @@ -2412,7 +2421,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .atomic_load, .atomic_rmw, .mul_add, - .builtin_call, .field_parent_ptr, .maximum, .minimum, @@ -7502,6 +7510,11 @@ fn builtinCall( .options = options, .callee = callee, .args = args, + .flags = .{ + .is_nosuspend = gz.nosuspend_node != 0, + .is_comptime = gz.force_comptime, + .ensure_result_used = false, + }, }); return rvalue(gz, rl, result, node); }, diff --git a/src/Sema.zig b/src/Sema.zig index feed4d0e8a..13a3573112 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -16097,12 +16097,12 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const args_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; const call_src = inst_data.src(); - const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index); - var func = sema.resolveInst(extra.data.callee); - const options = sema.resolveInst(extra.data.options); - const args = sema.resolveInst(extra.data.args); + const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data; + var func = sema.resolveInst(extra.callee); + const options = sema.resolveInst(extra.options); + const args = sema.resolveInst(extra.args); - const modifier: std.builtin.CallOptions.Modifier = modifier: { + const wanted_modifier: std.builtin.CallOptions.Modifier = modifier: { const call_options_ty = try sema.getBuiltinType(block, options_src, "CallOptions"); const coerced_options = try sema.coerce(block, call_options_ty, options, options_src); @@ -16118,6 +16118,41 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError break :modifier modifier_val.toEnum(std.builtin.CallOptions.Modifier); }; + const modifier: std.builtin.CallOptions.Modifier = switch (wanted_modifier) { + // These can be upgraded to comptime or nosuspend calls. + .auto, .never_tail, .no_async => m: { + if (extra.flags.is_comptime) { + break :m .compile_time; + } + if (extra.flags.is_nosuspend) { + break :m .no_async; + } + break :m wanted_modifier; + }, + // These can be upgraded to comptime. nosuspend bit can be safely ignored. + .always_tail, .always_inline, .compile_time => m: { + if (extra.flags.is_comptime) { + break :m .compile_time; + } + break :m wanted_modifier; + }, + .async_kw => m: { + if (extra.flags.is_nosuspend) { + return sema.fail(block, options_src, "modifier 'async_kw' cannot be used inside nosuspend block", .{}); + } + if (extra.flags.is_comptime) { + return sema.fail(block, options_src, "modifier 'async_kw' cannot be used in combination with comptime function call", .{}); + } + break :m wanted_modifier; + }, + .never_inline => m: { + if (extra.flags.is_comptime) { + return sema.fail(block, options_src, "modifier 'never_inline' cannot be used in combination with comptime function call", .{}); + } + break :m wanted_modifier; + }, + }; + const args_ty = sema.typeOf(args); if (!args_ty.isTuple() and args_ty.tag() != .empty_struct_literal) { return sema.fail(block, args_src, "expected a tuple, found {}", .{args_ty.fmt(sema.mod)}); @@ -16141,8 +16176,8 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @intCast(u32, i), args_ty); } } - - return sema.analyzeCall(block, func, func_src, call_src, modifier, false, resolved_args); + const ensure_result_used = extra.flags.ensure_result_used; + return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args); } fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { diff --git a/src/Zir.zig b/src/Zir.zig index 30edd6d0e1..c722457303 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -72,6 +72,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, en Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]), i32 => @bitCast(i32, code.extra[i]), Inst.Call.Flags => @bitCast(Inst.Call.Flags, code.extra[i]), + Inst.BuiltinCall.Flags => @bitCast(Inst.BuiltinCall.Flags, code.extra[i]), Inst.SwitchBlock.Bits => @bitCast(Inst.SwitchBlock.Bits, code.extra[i]), Inst.ExtendedFunc.Bits => @bitCast(Inst.ExtendedFunc.Bits, code.extra[i]), else => @compileError("bad field type"), @@ -280,8 +281,13 @@ pub const Inst = struct { /// Uses the `break` union field. break_inline, /// Function call. - /// Uses `pl_node`. AST node is the function call. Payload is `Call`. + /// Uses the `pl_node` union field with payload `Call`. + /// AST node is the function call. call, + /// Implements the `@call` builtin. + /// Uses the `pl_node` union field with payload `BuiltinCall`. + /// AST node is the builtin call. + builtin_call, /// `<` /// Uses the `pl_node` union field. Payload is `Bin`. cmp_lt, @@ -916,9 +922,6 @@ pub const Inst = struct { /// The addend communicates the type of the builtin. /// The mulends need to be coerced to the same type. mul_add, - /// Implements the `@call` builtin. - /// Uses the `pl_node` union field with payload `BuiltinCall`. - builtin_call, /// Implements the `@fieldParentPtr` builtin. /// Uses the `pl_node` union field with payload `FieldParentPtr`. field_parent_ptr, @@ -2733,9 +2736,24 @@ pub const Inst = struct { }; pub const BuiltinCall = struct { + // Note: Flags *must* come first so that unusedResultExpr + // can find it when it goes to modify them. + flags: Flags, options: Ref, callee: Ref, args: Ref, + + pub const Flags = packed struct { + is_nosuspend: bool, + is_comptime: bool, + ensure_result_used: bool, + _: u29 = undefined, + + comptime { + if (@sizeOf(Flags) != 4 or @bitSizeOf(Flags) != 32) + @compileError("Layout of BuiltinCall.Flags needs to be updated!"); + } + }; }; /// This data is stored inside extra, with two sets of trailing `Ref`: diff --git a/src/print_zir.zig b/src/print_zir.zig index a9d0f4690f..1f243acc30 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -365,7 +365,7 @@ const Writer = struct { .@"export" => try self.writePlNodeExport(stream, inst), .export_value => try self.writePlNodeExportValue(stream, inst), - .call => try self.writePlNodeCall(stream, inst), + .call => try self.writeCall(stream, inst), .block, .block_inline, @@ -793,6 +793,11 @@ const Writer = struct { fn writeBuiltinCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data; + + try self.writeFlag(stream, "nodiscard ", extra.flags.ensure_result_used); + try self.writeFlag(stream, "nosuspend ", extra.flags.is_nosuspend); + try self.writeFlag(stream, "comptime ", extra.flags.is_comptime); + try self.writeInstRef(stream, extra.options); try stream.writeAll(", "); try self.writeInstRef(stream, extra.callee); @@ -1144,7 +1149,7 @@ const Writer = struct { try self.writeSrc(stream, src); } - fn writePlNodeCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + fn writeCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.Call, inst_data.payload_index); const args = self.code.refSlice(extra.end, extra.data.flags.args_len); diff --git a/test/behavior/call.zig b/test/behavior/call.zig index 27d0bbf1d6..0297582234 100644 --- a/test/behavior/call.zig +++ b/test/behavior/call.zig @@ -19,7 +19,11 @@ test "super basic invocations" { } test "basic invocations" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const foo = struct { fn foo() i32 { @@ -41,7 +45,10 @@ test "basic invocations" { } { // call of non comptime-known function - var alias_foo = foo; + var alias_foo = switch (builtin.zig_backend) { + .stage1 => foo, + else => &foo, + }; try expect(@call(.{ .modifier = .no_async }, alias_foo, .{}) == 1234); try expect(@call(.{ .modifier = .never_tail }, alias_foo, .{}) == 1234); try expect(@call(.{ .modifier = .never_inline }, alias_foo, .{}) == 1234); @@ -79,26 +86,6 @@ test "tuple parameters" { } } -test "comptime call with bound function as parameter" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const S = struct { - fn ReturnType(func: anytype) type { - return switch (@typeInfo(@TypeOf(func))) { - .BoundFn => |info| info, - else => unreachable, - }.return_type orelse void; - } - - fn call_me_maybe() ?i32 { - return 123; - } - }; - - var inst: S = undefined; - try expectEqual(?i32, S.ReturnType(inst.call_me_maybe)); -} - test "result location of function call argument through runtime condition and struct init" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From fcd4efd8ecda01fe06735ed8b7e2cd2aa93daa19 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 May 2022 21:24:42 -0700 Subject: [PATCH 1595/2031] Sema: introduce laziness to `@sizeOf` Motivation: the behavior test that is now passing. The main change in this commit is introducing `Type.abiSizeAdvanced`, `Value.Tag.lazy_size`, and adjusting `Sema.zirSizeOf` to take advantage of these. However, the bulk of lines changed in this commit ended up being moving logic from value.zig and type.zig into Sema.zig. This logic had no business being in Type/Value as it was only called from a Sema context, and we need access to the Sema context for error reporting when a lazy Value is resolved. Also worth mentioning is that I bumped up the comptime `@floatToInt` implementation from using f64 to f128. --- src/Sema.zig | 949 ++++++++++++++++++++++++---- src/TypedValue.zig | 5 + src/type.zig | 293 +++++---- src/value.zig | 624 +++--------------- test/behavior/sizeof_and_typeof.zig | 2 - 5 files changed, 1064 insertions(+), 809 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 13a3573112..cb34fd158f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2228,7 +2228,6 @@ fn zirEnumDecl( enum_obj.tag_ty_inferred = true; } } - const target = mod.getTarget(); try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { @@ -2291,7 +2290,7 @@ fn zirEnumDecl( }); } else if (any_values) { const tag_val = if (last_tag_val) |val| - try val.intAdd(Value.one, enum_obj.tag_ty, sema.arena, target) + try sema.intAdd(block, src, val, Value.one, enum_obj.tag_ty) else Value.zero; last_tag_val = tag_val; @@ -6054,7 +6053,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A if (int_val.isUndef()) { return sema.failWithUseOfUndef(block, operand_src); } - if (!dest_ty.enumHasInt(int_val, sema.mod)) { + if (!(try sema.enumHasInt(block, src, dest_ty, int_val))) { const msg = msg: { const msg = try sema.errMsg( block, @@ -7082,7 +7081,7 @@ fn intCast( // range to account for negative values. const dest_range_val = if (wanted_info.signedness == .signed) range_val: { const range_minus_one = try dest_max_val.shl(Value.one, unsigned_operand_ty, sema.arena, target); - break :range_val try range_minus_one.intAdd(Value.one, unsigned_operand_ty, sema.arena, target); + break :range_val try sema.intAdd(block, operand_src, range_minus_one, Value.one, unsigned_operand_ty); } else dest_max_val; const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val); @@ -8203,8 +8202,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError // Validation above ensured these will succeed. const first_tv = sema.resolveInstConst(&child_block, .unneeded, item_first) catch unreachable; const last_tv = sema.resolveInstConst(&child_block, .unneeded, item_last) catch unreachable; - if (Value.compare(operand_val, .gte, first_tv.val, operand_ty, sema.mod) and - Value.compare(operand_val, .lte, last_tv.val, operand_ty, sema.mod)) + if ((try sema.compare(block, src, operand_val, .gte, first_tv.val, operand_ty)) and + (try sema.compare(block, src, operand_val, .lte, last_tv.val, operand_ty))) { return sema.resolveBlockBody(block, src, &child_block, body, inst, merges); } @@ -8878,7 +8877,7 @@ fn zirShl( if (rhs_val.isUndef()) { return sema.addConstUndef(sema.typeOf(lhs)); } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return lhs; } } @@ -8895,7 +8894,7 @@ fn zirShl( } const int_info = scalar_ty.intInfo(target); const truncated = try shifted.intTrunc(lhs_ty, sema.arena, int_info.signedness, int_info.bits, target); - if (truncated.compare(.eq, shifted, lhs_ty, sema.mod)) { + if (try sema.compare(block, src, truncated, .eq, shifted, lhs_ty)) { break :val shifted; } return sema.addConstUndef(lhs_ty); @@ -8999,13 +8998,13 @@ fn zirShr( return sema.addConstUndef(lhs_ty); } // If rhs is 0, return lhs without doing any calculations. - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.addConstant(lhs_ty, lhs_val); } if (air_tag == .shr_exact) { // Detect if any ones would be shifted out. const truncated = try lhs_val.intTruncBitsAsValue(lhs_ty, sema.arena, .unsigned, rhs_val, target); - if (!truncated.compareWithZero(.eq)) { + if (!(try truncated.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) { return sema.addConstUndef(lhs_ty); } } @@ -9015,7 +9014,7 @@ fn zirShr( // Even if lhs is not comptime known, we can still deduce certain things based // on rhs. // If rhs is 0, return lhs without doing any calculations. - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return lhs; } break :rs lhs_src; @@ -9578,12 +9577,12 @@ fn zirOverflowArithmetic( // to the result, even if it is undefined.. // Otherwise, if either of the argument is undefined, undefined is returned. if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { + if (!lhs_val.isUndef() and (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) { break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs }; } } if (maybe_rhs_val) |rhs_val| { - if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) { + if (!rhs_val.isUndef() and (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) { break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; } } @@ -9593,7 +9592,7 @@ fn zirOverflowArithmetic( break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) }; } - const result = try lhs_val.intAddWithOverflow(rhs_val, dest_ty, sema.arena, target); + const result = try sema.intAddWithOverflow(block, src, lhs_val, rhs_val, dest_ty); const overflowed = try sema.addConstant(overflowed_ty, result.overflowed); const wrapped = try sema.addConstant(dest_ty, result.wrapped_result); break :result .{ .overflowed = overflowed, .wrapped = wrapped }; @@ -9606,14 +9605,14 @@ fn zirOverflowArithmetic( if (maybe_rhs_val) |rhs_val| { if (rhs_val.isUndef()) { break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) }; - } else if (rhs_val.compareWithZero(.eq)) { + } else if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; } else if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) }; } - const result = try lhs_val.intSubWithOverflow(rhs_val, dest_ty, sema.arena, target); + const result = try sema.intSubWithOverflow(block, src, lhs_val, rhs_val, dest_ty); const overflowed = try sema.addConstant(overflowed_ty, result.overflowed); const wrapped = try sema.addConstant(dest_ty, result.wrapped_result); break :result .{ .overflowed = overflowed, .wrapped = wrapped }; @@ -9626,9 +9625,9 @@ fn zirOverflowArithmetic( // Otherwise, if either of the arguments is undefined, both results are undefined. if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { - if (lhs_val.compareWithZero(.eq)) { + if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; - } else if (lhs_val.compare(.eq, Value.one, dest_ty, mod)) { + } else if (try sema.compare(block, src, lhs_val, .eq, Value.one, dest_ty)) { break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs }; } } @@ -9636,9 +9635,9 @@ fn zirOverflowArithmetic( if (maybe_rhs_val) |rhs_val| { if (!rhs_val.isUndef()) { - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs }; - } else if (rhs_val.compare(.eq, Value.one, dest_ty, mod)) { + } else if (try sema.compare(block, src, rhs_val, .eq, Value.one, dest_ty)) { break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; } } @@ -9662,12 +9661,12 @@ fn zirOverflowArithmetic( // If rhs is zero, the result is lhs (even if undefined) and no overflow occurred. // Oterhwise if either of the arguments is undefined, both results are undefined. if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { + if (!lhs_val.isUndef() and (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) { break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; } } if (maybe_rhs_val) |rhs_val| { - if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) { + if (!rhs_val.isUndef() and (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) { break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs }; } } @@ -9815,7 +9814,7 @@ fn analyzeArithmetic( // overflow (max_int), causing illegal behavior. // For floats: either operand being undef makes the result undef. if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { + if (!lhs_val.isUndef() and (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) { return casted_rhs; } } @@ -9827,7 +9826,7 @@ fn analyzeArithmetic( return sema.addConstUndef(resolved_type); } } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return casted_lhs; } } @@ -9841,15 +9840,15 @@ fn analyzeArithmetic( } if (maybe_rhs_val) |rhs_val| { if (is_int) { - const sum = try lhs_val.intAdd(rhs_val, resolved_type, sema.arena, target); - if (!sum.intFitsInType(resolved_type, target)) { + const sum = try sema.intAdd(block, src, lhs_val, rhs_val, resolved_type); + if (!(try sema.intFitsInType(block, src, sum, resolved_type))) { return sema.failWithIntegerOverflow(block, src, resolved_type, sum); } return sema.addConstant(resolved_type, sum); } else { return sema.addConstant( resolved_type, - try lhs_val.floatAdd(rhs_val, resolved_type, sema.arena, target), + try sema.floatAdd(lhs_val, rhs_val, resolved_type), ); } } else break :rs .{ .src = rhs_src, .air_tag = .add }; @@ -9860,7 +9859,7 @@ fn analyzeArithmetic( // If either of the operands are zero, the other operand is returned. // If either of the operands are undefined, the result is undefined. if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { + if (!lhs_val.isUndef() and (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) { return casted_rhs; } } @@ -9868,13 +9867,13 @@ fn analyzeArithmetic( if (rhs_val.isUndef()) { return sema.addConstUndef(resolved_type); } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { return sema.addConstant( resolved_type, - try lhs_val.numberAddWrap(rhs_val, resolved_type, sema.arena, target), + try sema.numberAddWrap(block, src, lhs_val, rhs_val, resolved_type), ); } else break :rs .{ .src = lhs_src, .air_tag = .addwrap }; } else break :rs .{ .src = rhs_src, .air_tag = .addwrap }; @@ -9884,7 +9883,7 @@ fn analyzeArithmetic( // If either of the operands are zero, then the other operand is returned. // If either of the operands are undefined, the result is undefined. if (maybe_lhs_val) |lhs_val| { - if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { + if (!lhs_val.isUndef() and (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) { return casted_rhs; } } @@ -9892,12 +9891,12 @@ fn analyzeArithmetic( if (rhs_val.isUndef()) { return sema.addConstUndef(resolved_type); } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { const val = if (scalar_tag == .ComptimeInt) - try lhs_val.intAdd(rhs_val, resolved_type, sema.arena, target) + try sema.intAdd(block, src, lhs_val, rhs_val, resolved_type) else try lhs_val.intAddSat(rhs_val, resolved_type, sema.arena, target); @@ -9921,7 +9920,7 @@ fn analyzeArithmetic( return sema.addConstUndef(resolved_type); } } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return casted_lhs; } } @@ -9935,15 +9934,15 @@ fn analyzeArithmetic( } if (maybe_rhs_val) |rhs_val| { if (is_int) { - const diff = try lhs_val.intSub(rhs_val, resolved_type, sema.arena, target); - if (!diff.intFitsInType(resolved_type, target)) { + const diff = try sema.intSub(block, src, lhs_val, rhs_val, resolved_type); + if (!(try sema.intFitsInType(block, src, diff, resolved_type))) { return sema.failWithIntegerOverflow(block, src, resolved_type, diff); } return sema.addConstant(resolved_type, diff); } else { return sema.addConstant( resolved_type, - try lhs_val.floatSub(rhs_val, resolved_type, sema.arena, target), + try sema.floatSub(lhs_val, rhs_val, resolved_type), ); } } else break :rs .{ .src = rhs_src, .air_tag = .sub }; @@ -9957,7 +9956,7 @@ fn analyzeArithmetic( if (rhs_val.isUndef()) { return sema.addConstUndef(resolved_type); } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return casted_lhs; } } @@ -9968,7 +9967,7 @@ fn analyzeArithmetic( if (maybe_rhs_val) |rhs_val| { return sema.addConstant( resolved_type, - try lhs_val.numberSubWrap(rhs_val, resolved_type, sema.arena, target), + try sema.numberSubWrap(block, src, lhs_val, rhs_val, resolved_type), ); } else break :rs .{ .src = rhs_src, .air_tag = .subwrap }; } else break :rs .{ .src = lhs_src, .air_tag = .subwrap }; @@ -9981,7 +9980,7 @@ fn analyzeArithmetic( if (rhs_val.isUndef()) { return sema.addConstUndef(resolved_type); } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return casted_lhs; } } @@ -9991,7 +9990,7 @@ fn analyzeArithmetic( } if (maybe_rhs_val) |rhs_val| { const val = if (scalar_tag == .ComptimeInt) - try lhs_val.intSub(rhs_val, resolved_type, sema.arena, target) + try sema.intSub(block, src, lhs_val, rhs_val, resolved_type) else try lhs_val.intSubSat(rhs_val, resolved_type, sema.arena, target); @@ -10032,7 +10031,7 @@ fn analyzeArithmetic( .Int, .ComptimeInt, .ComptimeFloat => { if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { - if (lhs_val.compareWithZero(.eq)) { + if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.addConstant(resolved_type, Value.zero); } } @@ -10041,7 +10040,7 @@ fn analyzeArithmetic( if (rhs_val.isUndef()) { return sema.failWithUseOfUndef(block, rhs_src); } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.failWithDivideByZero(block, rhs_src); } } @@ -10053,7 +10052,7 @@ fn analyzeArithmetic( if (lhs_val.isUndef()) { if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compare(.neq, Value.negative_one, resolved_type, mod)) { + if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) { return sema.addConstUndef(resolved_type); } } @@ -10111,7 +10110,7 @@ fn analyzeArithmetic( // If the lhs is undefined, result is undefined. if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { - if (lhs_val.compareWithZero(.eq)) { + if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.addConstant(resolved_type, Value.zero); } } @@ -10120,7 +10119,7 @@ fn analyzeArithmetic( if (rhs_val.isUndef()) { return sema.failWithUseOfUndef(block, rhs_src); } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.failWithDivideByZero(block, rhs_src); } } @@ -10128,7 +10127,7 @@ fn analyzeArithmetic( if (lhs_val.isUndef()) { if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compare(.neq, Value.negative_one, resolved_type, mod)) { + if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) { return sema.addConstUndef(resolved_type); } } @@ -10174,7 +10173,7 @@ fn analyzeArithmetic( // If the lhs is undefined, result is undefined. if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { - if (lhs_val.compareWithZero(.eq)) { + if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.addConstant(resolved_type, Value.zero); } } @@ -10183,7 +10182,7 @@ fn analyzeArithmetic( if (rhs_val.isUndef()) { return sema.failWithUseOfUndef(block, rhs_src); } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.failWithDivideByZero(block, rhs_src); } } @@ -10191,7 +10190,7 @@ fn analyzeArithmetic( if (lhs_val.isUndef()) { if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compare(.neq, Value.negative_one, resolved_type, mod)) { + if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) { return sema.addConstUndef(resolved_type); } } @@ -10236,7 +10235,7 @@ fn analyzeArithmetic( if (lhs_val.isUndef()) { return sema.failWithUseOfUndef(block, rhs_src); } else { - if (lhs_val.compareWithZero(.eq)) { + if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.addConstant(resolved_type, Value.zero); } } @@ -10245,7 +10244,7 @@ fn analyzeArithmetic( if (rhs_val.isUndef()) { return sema.failWithUseOfUndef(block, rhs_src); } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.failWithDivideByZero(block, rhs_src); } } @@ -10278,10 +10277,10 @@ fn analyzeArithmetic( // For floats: either operand being undef makes the result undef. if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { - if (lhs_val.compareWithZero(.eq)) { + if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.addConstant(resolved_type, Value.zero); } - if (lhs_val.compare(.eq, Value.one, resolved_type, mod)) { + if (try sema.compare(block, src, lhs_val, .eq, Value.one, resolved_type)) { return casted_rhs; } } @@ -10294,10 +10293,10 @@ fn analyzeArithmetic( return sema.addConstUndef(resolved_type); } } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.addConstant(resolved_type, Value.zero); } - if (rhs_val.compare(.eq, Value.one, resolved_type, mod)) { + if (try sema.compare(block, src, rhs_val, .eq, Value.one, resolved_type)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { @@ -10310,7 +10309,7 @@ fn analyzeArithmetic( } if (is_int) { const product = try lhs_val.intMul(rhs_val, resolved_type, sema.arena, target); - if (!product.intFitsInType(resolved_type, target)) { + if (!(try sema.intFitsInType(block, src, product, resolved_type))) { return sema.failWithIntegerOverflow(block, src, resolved_type, product); } return sema.addConstant(resolved_type, product); @@ -10330,10 +10329,10 @@ fn analyzeArithmetic( // If either of the operands are undefined, result is undefined. if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { - if (lhs_val.compareWithZero(.eq)) { + if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.addConstant(resolved_type, Value.zero); } - if (lhs_val.compare(.eq, Value.one, resolved_type, mod)) { + if (try sema.compare(block, src, lhs_val, .eq, Value.one, resolved_type)) { return casted_rhs; } } @@ -10342,10 +10341,10 @@ fn analyzeArithmetic( if (rhs_val.isUndef()) { return sema.addConstUndef(resolved_type); } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.addConstant(resolved_type, Value.zero); } - if (rhs_val.compare(.eq, Value.one, resolved_type, mod)) { + if (try sema.compare(block, src, rhs_val, .eq, Value.one, resolved_type)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { @@ -10366,10 +10365,10 @@ fn analyzeArithmetic( // If either of the operands are undefined, result is undefined. if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { - if (lhs_val.compareWithZero(.eq)) { + if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.addConstant(resolved_type, Value.zero); } - if (lhs_val.compare(.eq, Value.one, resolved_type, mod)) { + if (try sema.compare(block, src, lhs_val, .eq, Value.one, resolved_type)) { return casted_rhs; } } @@ -10378,10 +10377,10 @@ fn analyzeArithmetic( if (rhs_val.isUndef()) { return sema.addConstUndef(resolved_type); } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.addConstant(resolved_type, Value.zero); } - if (rhs_val.compare(.eq, Value.one, resolved_type, mod)) { + if (try sema.compare(block, src, rhs_val, .eq, Value.one, resolved_type)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { @@ -10417,7 +10416,7 @@ fn analyzeArithmetic( if (lhs_val.isUndef()) { return sema.failWithUseOfUndef(block, lhs_src); } - if (lhs_val.compareWithZero(.eq)) { + if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.addConstant(resolved_type, Value.zero); } } else if (lhs_scalar_ty.isSignedInt()) { @@ -10427,23 +10426,23 @@ fn analyzeArithmetic( if (rhs_val.isUndef()) { return sema.failWithUseOfUndef(block, rhs_src); } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.failWithDivideByZero(block, rhs_src); } if (maybe_lhs_val) |lhs_val| { const rem_result = try lhs_val.intRem(rhs_val, resolved_type, sema.arena, target); // If this answer could possibly be different by doing `intMod`, // we must emit a compile error. Otherwise, it's OK. - if (rhs_val.compareWithZero(.lt) != lhs_val.compareWithZero(.lt) and - !rem_result.compareWithZero(.eq)) + if ((try rhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) != (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) and + !(try rem_result.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) { - const bad_src = if (lhs_val.compareWithZero(.lt)) + const bad_src = if (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) lhs_src else rhs_src; return sema.failWithModRemNegative(block, bad_src, lhs_ty, rhs_ty); } - if (lhs_val.compareWithZero(.lt)) { + if (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) { // Negative return sema.addConstant(resolved_type, Value.zero); } @@ -10461,14 +10460,14 @@ fn analyzeArithmetic( if (rhs_val.isUndef()) { return sema.failWithUseOfUndef(block, rhs_src); } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.failWithDivideByZero(block, rhs_src); } - if (rhs_val.compareWithZero(.lt)) { + if (try rhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) { return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); } if (maybe_lhs_val) |lhs_val| { - if (lhs_val.isUndef() or lhs_val.compareWithZero(.lt)) { + if (lhs_val.isUndef() or (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src)))) { return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); } return sema.addConstant( @@ -10504,7 +10503,7 @@ fn analyzeArithmetic( if (rhs_val.isUndef()) { return sema.failWithUseOfUndef(block, rhs_src); } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.failWithDivideByZero(block, rhs_src); } if (maybe_lhs_val) |lhs_val| { @@ -10523,7 +10522,7 @@ fn analyzeArithmetic( if (rhs_val.isUndef()) { return sema.failWithUseOfUndef(block, rhs_src); } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.failWithDivideByZero(block, rhs_src); } } @@ -10561,7 +10560,7 @@ fn analyzeArithmetic( if (rhs_val.isUndef()) { return sema.failWithUseOfUndef(block, rhs_src); } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.failWithDivideByZero(block, rhs_src); } if (maybe_lhs_val) |lhs_val| { @@ -10580,7 +10579,7 @@ fn analyzeArithmetic( if (rhs_val.isUndef()) { return sema.failWithUseOfUndef(block, rhs_src); } - if (rhs_val.compareWithZero(.eq)) { + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return sema.failWithDivideByZero(block, rhs_src); } } @@ -11095,11 +11094,11 @@ fn cmpSelf( if (resolved_type.zigTypeTag() == .Vector) { const result_ty = try Type.vector(sema.arena, resolved_type.vectorLen(), Type.@"bool"); - const cmp_val = try lhs_val.compareVector(op, rhs_val, resolved_type, sema.arena, sema.mod); + const cmp_val = try sema.compareVector(block, lhs_src, lhs_val, op, rhs_val, resolved_type); return sema.addConstant(result_ty, cmp_val); } - if (lhs_val.compare(op, rhs_val, resolved_type, sema.mod)) { + if (try sema.compare(block, lhs_src, lhs_val, op, rhs_val, resolved_type)) { return Air.Inst.Ref.bool_true; } else { return Air.Inst.Ref.bool_false; @@ -11157,24 +11156,22 @@ fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand); - try sema.resolveTypeLayout(block, src, operand_ty); - const target = sema.mod.getTarget(); - const abi_size = switch (operand_ty.zigTypeTag()) { + const ty = try sema.resolveType(block, operand_src, inst_data.operand); + switch (ty.zigTypeTag()) { .Fn => unreachable, .NoReturn, .Undefined, .Null, .BoundFn, .Opaque, - => return sema.fail(block, src, "no size available for type '{}'", .{operand_ty.fmt(sema.mod)}), + => return sema.fail(block, src, "no size available for type '{}'", .{ty.fmt(sema.mod)}), .Type, .EnumLiteral, .ComptimeFloat, .ComptimeInt, .Void, - => 0, + => return sema.addIntUnsigned(Type.comptime_int, 0), .Bool, .Int, @@ -11190,9 +11187,14 @@ fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. .Vector, .Frame, .AnyFrame, - => operand_ty.abiSize(target), - }; - return sema.addIntUnsigned(Type.comptime_int, abi_size); + => {}, + } + const target = sema.mod.getTarget(); + const val = try ty.lazyAbiSize(target, sema.arena); + if (val.tag() == .lazy_size) { + try sema.queueFullTypeResolution(ty); + } + return sema.addConstant(Type.comptime_int, val); } fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -11202,7 +11204,7 @@ fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const operand_ty = try sema.resolveTypeFields(block, operand_src, unresolved_operand_ty); const target = sema.mod.getTarget(); const bit_size = operand_ty.bitSize(target); - return sema.addIntUnsigned(Type.initTag(.comptime_int), bit_size); + return sema.addIntUnsigned(Type.comptime_int, bit_size); } fn zirThis( @@ -13516,10 +13518,11 @@ fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const ty = try sema.resolveType(block, operand_src, inst_data.operand); const target = sema.mod.getTarget(); - return sema.addConstant( - Type.comptime_int, - try ty.lazyAbiAlignment(target, sema.arena), - ); + const val = try ty.lazyAbiAlignment(target, sema.arena); + if (val.tag() == .lazy_align) { + try sema.queueFullTypeResolution(ty); + } + return sema.addConstant(Type.comptime_int, val); } fn zirBoolToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -14362,16 +14365,7 @@ fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! try sema.checkFloatType(block, operand_src, operand_ty); if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { - const target = sema.mod.getTarget(); - const result_val = val.floatToInt(sema.arena, operand_ty, dest_ty, target) catch |err| switch (err) { - error.FloatCannotFit => { - return sema.fail(block, operand_src, "integer value {d} cannot be stored in type '{}'", .{ - @floor(val.toFloat(f64)), - dest_ty.fmt(sema.mod), - }); - }, - else => |e| return e, - }; + const result_val = try sema.floatToInt(block, operand_src, val, operand_ty, dest_ty); return sema.addConstant(dest_ty, result_val); } @@ -15563,7 +15557,7 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. .Xor => accum = try accum.bitwiseXor(elem_val, scalar_ty, sema.arena, target), .Min => accum = accum.numberMin(elem_val, target), .Max => accum = accum.numberMax(elem_val, target), - .Add => accum = try accum.numberAddWrap(elem_val, scalar_ty, sema.arena, target), + .Add => accum = try sema.numberAddWrap(block, operand_src, accum, elem_val, scalar_ty), .Mul => accum = try accum.numberMulWrap(elem_val, scalar_ty, sema.arena, target), } } @@ -15958,14 +15952,14 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const new_val = switch (op) { // zig fmt: off .Xchg => operand_val, - .Add => try stored_val.numberAddWrap(operand_val, elem_ty, sema.arena, target), - .Sub => try stored_val.numberSubWrap(operand_val, elem_ty, sema.arena, target), - .And => try stored_val.bitwiseAnd (operand_val, elem_ty, sema.arena, target), - .Nand => try stored_val.bitwiseNand (operand_val, elem_ty, sema.arena, target), - .Or => try stored_val.bitwiseOr (operand_val, elem_ty, sema.arena, target), - .Xor => try stored_val.bitwiseXor (operand_val, elem_ty, sema.arena, target), - .Max => stored_val.numberMax (operand_val, target), - .Min => stored_val.numberMin (operand_val, target), + .Add => try sema.numberAddWrap(block, src, stored_val, operand_val, elem_ty), + .Sub => try sema.numberSubWrap(block, src, stored_val, operand_val, elem_ty), + .And => try stored_val.bitwiseAnd (operand_val, elem_ty, sema.arena, target), + .Nand => try stored_val.bitwiseNand (operand_val, elem_ty, sema.arena, target), + .Or => try stored_val.bitwiseOr (operand_val, elem_ty, sema.arena, target), + .Xor => try stored_val.bitwiseXor (operand_val, elem_ty, sema.arena, target), + .Max => stored_val.numberMax (operand_val, target), + .Min => stored_val.numberMin (operand_val, target), // zig fmt: on }; try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty); @@ -18890,18 +18884,13 @@ fn coerce( .{ val.fmtValue(inst_ty, sema.mod), dest_ty.fmt(sema.mod) }, ); } - const result_val = val.floatToInt(sema.arena, inst_ty, dest_ty, target) catch |err| switch (err) { - error.FloatCannotFit => { - return sema.fail(block, inst_src, "integer value {d} cannot be stored in type '{}'", .{ @floor(val.toFloat(f64)), dest_ty.fmt(sema.mod) }); - }, - else => |e| return e, - }; + const result_val = try sema.floatToInt(block, inst_src, val, inst_ty, dest_ty); return try sema.addConstant(dest_ty, result_val); }, .Int, .ComptimeInt => { if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| { // comptime known integer to other number - if (!val.intFitsInType(dest_ty, target)) { + if (!(try sema.intFitsInType(block, inst_src, val, dest_ty))) { return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) }); } return try sema.addConstant(dest_ty, val); @@ -21093,7 +21082,7 @@ fn analyzeSlice( sema.arena, array_ty.arrayLenIncludingSentinel(), ); - if (end_val.compare(.gt, len_s_val, Type.usize, mod)) { + if (try sema.compare(block, src, end_val, .gt, len_s_val, Type.usize)) { const sentinel_label: []const u8 = if (array_ty.sentinel() != null) " +1 (sentinel)" else @@ -21133,7 +21122,7 @@ fn analyzeSlice( .data = slice_val.sliceLen(mod) + @boolToInt(has_sentinel), }; const slice_len_val = Value.initPayload(&int_payload.base); - if (end_val.compare(.gt, slice_len_val, Type.usize, mod)) { + if (try sema.compare(block, src, end_val, .gt, slice_len_val, Type.usize)) { const sentinel_label: []const u8 = if (has_sentinel) " +1 (sentinel)" else @@ -21191,7 +21180,7 @@ fn analyzeSlice( // requirement: start <= end if (try sema.resolveDefinedValue(block, src, end)) |end_val| { if (try sema.resolveDefinedValue(block, src, start)) |start_val| { - if (start_val.compare(.gt, end_val, Type.usize, mod)) { + if (try sema.compare(block, src, start_val, .gt, end_val, Type.usize)) { return sema.fail( block, start_src, @@ -21399,11 +21388,11 @@ fn cmpNumeric( // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float, // add/subtract 1. const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| - lhs_val.compareWithZero(.lt) + (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) else (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt()); const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| - rhs_val.compareWithZero(.lt) + (try rhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) else (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt()); const dest_int_is_signed = lhs_is_signed or rhs_is_signed; @@ -21541,7 +21530,7 @@ fn cmpVector( if (lhs_val.isUndef() or rhs_val.isUndef()) { return sema.addConstUndef(result_ty); } - const cmp_val = try lhs_val.compareVector(op, rhs_val, lhs_ty, sema.arena, sema.mod); + const cmp_val = try sema.compareVector(block, src, lhs_val, op, rhs_val, lhs_ty); return sema.addConstant(result_ty, cmp_val); } else { break :src rhs_src; @@ -22904,8 +22893,6 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil enum_field_names = &union_obj.tag_ty.castTag(.enum_simple).?.data.fields; } - const target = sema.mod.getTarget(); - const bits_per_field = 4; const fields_per_u32 = 32 / bits_per_field; const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; @@ -22969,7 +22956,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil }); } else { const val = if (last_tag_val) |val| - try val.intAdd(Value.one, int_tag_ty, sema.arena, target) + try sema.intAdd(block, src, val, Value.one, int_tag_ty) else Value.zero; last_tag_val = val; @@ -24119,3 +24106,707 @@ fn queueFullTypeResolution(sema: *Sema, ty: Type) !void { const inst_ref = try sema.addType(ty); try sema.types_to_resolve.append(sema.gpa, inst_ref); } + +fn intAdd(sema: *Sema, block: *Block, src: LazySrcLoc, lhs: Value, rhs: Value, ty: Type) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try sema.arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try sema.intAddScalar(block, src, lhs.indexVectorlike(i), rhs.indexVectorlike(i)); + } + return Value.Tag.aggregate.create(sema.arena, result_data); + } + return sema.intAddScalar(block, src, lhs, rhs); +} + +fn intAddScalar(sema: *Sema, block: *Block, src: LazySrcLoc, lhs: Value, rhs: Value) !Value { + // TODO is this a performance issue? maybe we should try the operation without + // resorting to BigInt first. + var lhs_space: Value.BigIntSpace = undefined; + var rhs_space: Value.BigIntSpace = undefined; + const target = sema.mod.getTarget(); + const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, target, sema.kit(block, src)); + const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, target, sema.kit(block, src)); + const limbs = try sema.arena.alloc( + std.math.big.Limb, + std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, + ); + var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; + result_bigint.add(lhs_bigint, rhs_bigint); + return Value.fromBigInt(sema.arena, result_bigint.toConst()); +} + +/// Supports both (vectors of) floats and ints; handles undefined scalars. +fn numberAddWrap( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + lhs: Value, + rhs: Value, + ty: Type, +) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try sema.arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try sema.numberAddWrapScalar(block, src, lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType()); + } + return Value.Tag.aggregate.create(sema.arena, result_data); + } + return sema.numberAddWrapScalar(block, src, lhs, rhs, ty); +} + +/// Supports both floats and ints; handles undefined. +fn numberAddWrapScalar( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + lhs: Value, + rhs: Value, + ty: Type, +) !Value { + if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); + + if (ty.zigTypeTag() == .ComptimeInt) { + return sema.intAdd(block, src, lhs, rhs, ty); + } + + if (ty.isAnyFloat()) { + return sema.floatAdd(lhs, rhs, ty); + } + + const overflow_result = try sema.intAddWithOverflow(block, src, lhs, rhs, ty); + return overflow_result.wrapped_result; +} + +fn intSub( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + lhs: Value, + rhs: Value, + ty: Type, +) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try sema.arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try sema.intSubScalar(block, src, lhs.indexVectorlike(i), rhs.indexVectorlike(i)); + } + return Value.Tag.aggregate.create(sema.arena, result_data); + } + return sema.intSubScalar(block, src, lhs, rhs); +} + +fn intSubScalar(sema: *Sema, block: *Block, src: LazySrcLoc, lhs: Value, rhs: Value) !Value { + // TODO is this a performance issue? maybe we should try the operation without + // resorting to BigInt first. + var lhs_space: Value.BigIntSpace = undefined; + var rhs_space: Value.BigIntSpace = undefined; + const target = sema.mod.getTarget(); + const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, target, sema.kit(block, src)); + const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, target, sema.kit(block, src)); + const limbs = try sema.arena.alloc( + std.math.big.Limb, + std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, + ); + var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; + result_bigint.sub(lhs_bigint, rhs_bigint); + return Value.fromBigInt(sema.arena, result_bigint.toConst()); +} + +/// Supports both (vectors of) floats and ints; handles undefined scalars. +fn numberSubWrap( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + lhs: Value, + rhs: Value, + ty: Type, +) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try sema.arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try sema.numberSubWrapScalar(block, src, lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType()); + } + return Value.Tag.aggregate.create(sema.arena, result_data); + } + return sema.numberSubWrapScalar(block, src, lhs, rhs, ty); +} + +/// Supports both floats and ints; handles undefined. +fn numberSubWrapScalar( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + lhs: Value, + rhs: Value, + ty: Type, +) !Value { + if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); + + if (ty.zigTypeTag() == .ComptimeInt) { + return sema.intSub(block, src, lhs, rhs, ty); + } + + if (ty.isAnyFloat()) { + return sema.floatSub(lhs, rhs, ty); + } + + const overflow_result = try sema.intSubWithOverflow(block, src, lhs, rhs, ty); + return overflow_result.wrapped_result; +} + +fn floatAdd( + sema: *Sema, + lhs: Value, + rhs: Value, + float_type: Type, +) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try sema.arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try sema.floatAddScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType()); + } + return Value.Tag.aggregate.create(sema.arena, result_data); + } + return sema.floatAddScalar(lhs, rhs, float_type); +} + +fn floatAddScalar( + sema: *Sema, + lhs: Value, + rhs: Value, + float_type: Type, +) !Value { + const target = sema.mod.getTarget(); + switch (float_type.floatBits(target)) { + 16 => { + const lhs_val = lhs.toFloat(f16); + const rhs_val = rhs.toFloat(f16); + return Value.Tag.float_16.create(sema.arena, lhs_val + rhs_val); + }, + 32 => { + const lhs_val = lhs.toFloat(f32); + const rhs_val = rhs.toFloat(f32); + return Value.Tag.float_32.create(sema.arena, lhs_val + rhs_val); + }, + 64 => { + const lhs_val = lhs.toFloat(f64); + const rhs_val = rhs.toFloat(f64); + return Value.Tag.float_64.create(sema.arena, lhs_val + rhs_val); + }, + 80 => { + const lhs_val = lhs.toFloat(f80); + const rhs_val = rhs.toFloat(f80); + return Value.Tag.float_80.create(sema.arena, lhs_val + rhs_val); + }, + 128 => { + const lhs_val = lhs.toFloat(f128); + const rhs_val = rhs.toFloat(f128); + return Value.Tag.float_128.create(sema.arena, lhs_val + rhs_val); + }, + else => unreachable, + } +} + +fn floatSub( + sema: *Sema, + lhs: Value, + rhs: Value, + float_type: Type, +) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try sema.arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try sema.floatSubScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType()); + } + return Value.Tag.aggregate.create(sema.arena, result_data); + } + return sema.floatSubScalar(lhs, rhs, float_type); +} + +fn floatSubScalar( + sema: *Sema, + lhs: Value, + rhs: Value, + float_type: Type, +) !Value { + const target = sema.mod.getTarget(); + switch (float_type.floatBits(target)) { + 16 => { + const lhs_val = lhs.toFloat(f16); + const rhs_val = rhs.toFloat(f16); + return Value.Tag.float_16.create(sema.arena, lhs_val - rhs_val); + }, + 32 => { + const lhs_val = lhs.toFloat(f32); + const rhs_val = rhs.toFloat(f32); + return Value.Tag.float_32.create(sema.arena, lhs_val - rhs_val); + }, + 64 => { + const lhs_val = lhs.toFloat(f64); + const rhs_val = rhs.toFloat(f64); + return Value.Tag.float_64.create(sema.arena, lhs_val - rhs_val); + }, + 80 => { + const lhs_val = lhs.toFloat(f80); + const rhs_val = rhs.toFloat(f80); + return Value.Tag.float_80.create(sema.arena, lhs_val - rhs_val); + }, + 128 => { + const lhs_val = lhs.toFloat(f128); + const rhs_val = rhs.toFloat(f128); + return Value.Tag.float_128.create(sema.arena, lhs_val - rhs_val); + }, + else => unreachable, + } +} + +fn intSubWithOverflow( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + lhs: Value, + rhs: Value, + ty: Type, +) !Value.OverflowArithmeticResult { + if (ty.zigTypeTag() == .Vector) { + const overflowed_data = try sema.arena.alloc(Value, ty.vectorLen()); + const result_data = try sema.arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + const of_math_result = try sema.intSubWithOverflowScalar(block, src, lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType()); + overflowed_data[i] = of_math_result.overflowed; + scalar.* = of_math_result.wrapped_result; + } + return Value.OverflowArithmeticResult{ + .overflowed = try Value.Tag.aggregate.create(sema.arena, overflowed_data), + .wrapped_result = try Value.Tag.aggregate.create(sema.arena, result_data), + }; + } + return sema.intSubWithOverflowScalar(block, src, lhs, rhs, ty); +} + +fn intSubWithOverflowScalar( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + lhs: Value, + rhs: Value, + ty: Type, +) !Value.OverflowArithmeticResult { + const target = sema.mod.getTarget(); + const info = ty.intInfo(target); + + var lhs_space: Value.BigIntSpace = undefined; + var rhs_space: Value.BigIntSpace = undefined; + const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, target, sema.kit(block, src)); + const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, target, sema.kit(block, src)); + const limbs = try sema.arena.alloc( + std.math.big.Limb, + std.math.big.int.calcTwosCompLimbCount(info.bits), + ); + var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; + const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); + const wrapped_result = try Value.fromBigInt(sema.arena, result_bigint.toConst()); + return Value.OverflowArithmeticResult{ + .overflowed = Value.makeBool(overflowed), + .wrapped_result = wrapped_result, + }; +} + +fn floatToInt( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + val: Value, + float_ty: Type, + int_ty: Type, +) CompileError!Value { + if (float_ty.zigTypeTag() == .Vector) { + const elem_ty = float_ty.childType(); + const result_data = try sema.arena.alloc(Value, float_ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try sema.floatToIntScalar(block, src, val.indexVectorlike(i), elem_ty, int_ty.scalarType()); + } + return Value.Tag.aggregate.create(sema.arena, result_data); + } + return sema.floatToIntScalar(block, src, val, float_ty, int_ty); +} + +fn floatToIntScalar( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + val: Value, + float_ty: Type, + int_ty: Type, +) CompileError!Value { + const Limb = std.math.big.Limb; + + const float = val.toFloat(f128); + if (std.math.isNan(float)) { + return sema.fail(block, src, "float value NaN cannot be stored in integer type '{}'", .{ + int_ty.fmt(sema.mod), + }); + } + if (std.math.isInf(float)) { + return sema.fail(block, src, "float value Inf cannot be stored in integer type '{}'", .{ + int_ty.fmt(sema.mod), + }); + } + + const is_negative = std.math.signbit(float); + const floored = @floor(@fabs(float)); + + var rational = try std.math.big.Rational.init(sema.arena); + defer rational.deinit(); + rational.setFloat(f128, floored) catch |err| switch (err) { + error.NonFiniteFloat => unreachable, + error.OutOfMemory => return error.OutOfMemory, + }; + + // The float is reduced in rational.setFloat, so we assert that denominator is equal to one + const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true }; + assert(rational.q.toConst().eqAbs(big_one)); + + const result_limbs = try sema.arena.dupe(Limb, rational.p.toConst().limbs); + const result = if (is_negative) + try Value.Tag.int_big_negative.create(sema.arena, result_limbs) + else + try Value.Tag.int_big_positive.create(sema.arena, result_limbs); + + if (!(try sema.intFitsInType(block, src, result, int_ty))) { + return sema.fail(block, src, "float value {} cannot be stored in integer type '{}'", .{ + val.fmtValue(float_ty, sema.mod), int_ty.fmt(sema.mod), + }); + } + return result; +} + +/// Asserts the value is an integer, and the destination type is ComptimeInt or Int. +/// Vectors are also accepted. Vector results are reduced with AND. +fn intFitsInType( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + self: Value, + ty: Type, +) CompileError!bool { + const target = sema.mod.getTarget(); + switch (self.tag()) { + .zero, + .undef, + .bool_false, + => return true, + + .one, + .bool_true, + => switch (ty.zigTypeTag()) { + .Int => { + const info = ty.intInfo(target); + return switch (info.signedness) { + .signed => info.bits >= 2, + .unsigned => info.bits >= 1, + }; + }, + .ComptimeInt => return true, + else => unreachable, + }, + + .lazy_align => { + const info = ty.intInfo(target); + const max_needed_bits = @as(u16, 16) + @boolToInt(info.signedness == .signed); + // If it is u16 or bigger we know the alignment fits without resolving it. + if (info.bits >= max_needed_bits) return true; + const x = try sema.typeAbiAlignment(block, src, self.castTag(.lazy_align).?.data); + if (x == 0) return true; + const actual_needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed); + return info.bits >= actual_needed_bits; + }, + .lazy_size => { + const info = ty.intInfo(target); + const max_needed_bits = @as(u16, 64) + @boolToInt(info.signedness == .signed); + // If it is u64 or bigger we know the size fits without resolving it. + if (info.bits >= max_needed_bits) return true; + const x = try sema.typeAbiSize(block, src, self.castTag(.lazy_size).?.data); + if (x == 0) return true; + const actual_needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed); + return info.bits >= actual_needed_bits; + }, + + .int_u64 => switch (ty.zigTypeTag()) { + .Int => { + const x = self.castTag(.int_u64).?.data; + if (x == 0) return true; + const info = ty.intInfo(target); + const needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed); + return info.bits >= needed_bits; + }, + .ComptimeInt => return true, + else => unreachable, + }, + .int_i64 => switch (ty.zigTypeTag()) { + .Int => { + const x = self.castTag(.int_i64).?.data; + if (x == 0) return true; + const info = ty.intInfo(target); + if (info.signedness == .unsigned and x < 0) + return false; + var buffer: Value.BigIntSpace = undefined; + return (try self.toBigIntAdvanced(&buffer, target, sema.kit(block, src))).fitsInTwosComp(info.signedness, info.bits); + }, + .ComptimeInt => return true, + else => unreachable, + }, + .int_big_positive => switch (ty.zigTypeTag()) { + .Int => { + const info = ty.intInfo(target); + return self.castTag(.int_big_positive).?.asBigInt().fitsInTwosComp(info.signedness, info.bits); + }, + .ComptimeInt => return true, + else => unreachable, + }, + .int_big_negative => switch (ty.zigTypeTag()) { + .Int => { + const info = ty.intInfo(target); + return self.castTag(.int_big_negative).?.asBigInt().fitsInTwosComp(info.signedness, info.bits); + }, + .ComptimeInt => return true, + else => unreachable, + }, + + .the_only_possible_value => { + assert(ty.intInfo(target).bits == 0); + return true; + }, + + .decl_ref_mut, + .extern_fn, + .decl_ref, + .function, + .variable, + => switch (ty.zigTypeTag()) { + .Int => { + const info = ty.intInfo(target); + const ptr_bits = target.cpu.arch.ptrBitWidth(); + return switch (info.signedness) { + .signed => info.bits > ptr_bits, + .unsigned => info.bits >= ptr_bits, + }; + }, + .ComptimeInt => return true, + else => unreachable, + }, + + .aggregate => { + assert(ty.zigTypeTag() == .Vector); + for (self.castTag(.aggregate).?.data) |elem| { + if (!(try sema.intFitsInType(block, src, elem, ty.scalarType()))) { + return false; + } + } + return true; + }, + + else => unreachable, + } +} + +fn intInRange( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + tag_ty: Type, + int_val: Value, + end: usize, +) !bool { + if (try int_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) return false; + var end_payload: Value.Payload.U64 = .{ + .base = .{ .tag = .int_u64 }, + .data = end, + }; + const end_val = Value.initPayload(&end_payload.base); + if (try sema.compare(block, src, int_val, .gte, end_val, tag_ty)) return false; + return true; +} + +/// Asserts the type is an enum. +fn enumHasInt( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + ty: Type, + int: Value, +) CompileError!bool { + switch (ty.tag()) { + .enum_nonexhaustive => return sema.intFitsInType(block, src, int, ty), + .enum_full => { + const enum_full = ty.castTag(.enum_full).?.data; + const tag_ty = enum_full.tag_ty; + if (enum_full.values.count() == 0) { + return intInRange(sema, block, src, tag_ty, int, enum_full.fields.count()); + } else { + return enum_full.values.containsContext(int, .{ + .ty = tag_ty, + .mod = sema.mod, + }); + } + }, + .enum_numbered => { + const enum_obj = ty.castTag(.enum_numbered).?.data; + const tag_ty = enum_obj.tag_ty; + if (enum_obj.values.count() == 0) { + return intInRange(sema, block, src, tag_ty, int, enum_obj.fields.count()); + } else { + return enum_obj.values.containsContext(int, .{ + .ty = tag_ty, + .mod = sema.mod, + }); + } + }, + .enum_simple => { + const enum_simple = ty.castTag(.enum_simple).?.data; + const fields_len = enum_simple.fields.count(); + const bits = std.math.log2_int_ceil(usize, fields_len); + var buffer: Type.Payload.Bits = .{ + .base = .{ .tag = .int_unsigned }, + .data = bits, + }; + const tag_ty = Type.initPayload(&buffer.base); + return intInRange(sema, block, src, tag_ty, int, fields_len); + }, + .atomic_order, + .atomic_rmw_op, + .calling_convention, + .address_space, + .float_mode, + .reduce_op, + .call_options, + .prefetch_options, + .export_options, + .extern_options, + => unreachable, + + else => unreachable, + } +} + +fn intAddWithOverflow( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + lhs: Value, + rhs: Value, + ty: Type, +) !Value.OverflowArithmeticResult { + if (ty.zigTypeTag() == .Vector) { + const overflowed_data = try sema.arena.alloc(Value, ty.vectorLen()); + const result_data = try sema.arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + const of_math_result = try sema.intAddWithOverflowScalar(block, src, lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType()); + overflowed_data[i] = of_math_result.overflowed; + scalar.* = of_math_result.wrapped_result; + } + return Value.OverflowArithmeticResult{ + .overflowed = try Value.Tag.aggregate.create(sema.arena, overflowed_data), + .wrapped_result = try Value.Tag.aggregate.create(sema.arena, result_data), + }; + } + return sema.intAddWithOverflowScalar(block, src, lhs, rhs, ty); +} + +fn intAddWithOverflowScalar( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + lhs: Value, + rhs: Value, + ty: Type, +) !Value.OverflowArithmeticResult { + const target = sema.mod.getTarget(); + const info = ty.intInfo(target); + + var lhs_space: Value.BigIntSpace = undefined; + var rhs_space: Value.BigIntSpace = undefined; + const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, target, sema.kit(block, src)); + const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, target, sema.kit(block, src)); + const limbs = try sema.arena.alloc( + std.math.big.Limb, + std.math.big.int.calcTwosCompLimbCount(info.bits), + ); + var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; + const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); + const result = try Value.fromBigInt(sema.arena, result_bigint.toConst()); + return Value.OverflowArithmeticResult{ + .overflowed = Value.makeBool(overflowed), + .wrapped_result = result, + }; +} + +/// Asserts the values are comparable. Both operands have type `ty`. +/// Vector results will be reduced with AND. +fn compare( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + lhs: Value, + op: std.math.CompareOperator, + rhs: Value, + ty: Type, +) CompileError!bool { + if (ty.zigTypeTag() == .Vector) { + var i: usize = 0; + while (i < ty.vectorLen()) : (i += 1) { + if (!(try sema.compareScalar(block, src, lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType()))) { + return false; + } + } + return true; + } + return sema.compareScalar(block, src, lhs, op, rhs, ty); +} + +/// Asserts the values are comparable. Both operands have type `ty`. +fn compareScalar( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + lhs: Value, + op: std.math.CompareOperator, + rhs: Value, + ty: Type, +) CompileError!bool { + switch (op) { + .eq => return sema.valuesEqual(block, src, lhs, rhs, ty), + .neq => return !(try sema.valuesEqual(block, src, lhs, rhs, ty)), + else => return Value.compareHeteroAdvanced(lhs, op, rhs, sema.mod.getTarget(), sema.kit(block, src)), + } +} + +fn valuesEqual( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + lhs: Value, + rhs: Value, + ty: Type, +) CompileError!bool { + return Value.eqlAdvanced(lhs, rhs, ty, sema.mod, sema.kit(block, src)); +} + +/// Asserts the values are comparable vectors of type `ty`. +pub fn compareVector( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + lhs: Value, + op: std.math.CompareOperator, + rhs: Value, + ty: Type, +) !Value { + assert(ty.zigTypeTag() == .Vector); + const result_data = try sema.arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + const res_bool = try sema.compareScalar(block, src, lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType()); + scalar.* = Value.makeBool(res_bool); + } + return Value.Tag.aggregate.create(sema.arena, result_data); +} diff --git a/src/TypedValue.zig b/src/TypedValue.zig index 43c26b254e..b6aee29a4b 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -232,6 +232,11 @@ pub fn print( const x = sub_ty.abiAlignment(target); return writer.print("{d}", .{x}); }, + .lazy_size => { + const sub_ty = val.castTag(.lazy_size).?.data; + const x = sub_ty.abiSize(target); + return writer.print("{d}", .{x}); + }, .function => return writer.print("(function '{s}')", .{ mod.declPtr(val.castTag(.function).?.data.owner_decl).name, }), diff --git a/src/type.zig b/src/type.zig index cc00f712f0..ea65cc8916 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2760,7 +2760,7 @@ pub const Type = extern union { .sema_kit => |sk| sk, else => null, }; - return switch (ty.tag()) { + switch (ty.tag()) { .u1, .u8, .i8, @@ -3028,7 +3028,7 @@ pub const Type = extern union { => unreachable, .generic_poison => unreachable, - }; + } } pub fn abiAlignmentAdvancedUnion( @@ -3076,10 +3076,37 @@ pub const Type = extern union { return AbiAlignmentAdvanced{ .scalar = max_align }; } + /// May capture a reference to `ty`. + pub fn lazyAbiSize(ty: Type, target: Target, arena: Allocator) !Value { + switch (try ty.abiSizeAdvanced(target, .{ .lazy = arena })) { + .val => |val| return val, + .scalar => |x| return Value.Tag.int_u64.create(arena, x), + } + } + /// Asserts the type has the ABI size already resolved. /// Types that return false for hasRuntimeBits() return 0. - pub fn abiSize(self: Type, target: Target) u64 { - return switch (self.tag()) { + pub fn abiSize(ty: Type, target: Target) u64 { + return (abiSizeAdvanced(ty, target, .eager) catch unreachable).scalar; + } + + const AbiSizeAdvanced = union(enum) { + scalar: u64, + val: Value, + }; + + /// If you pass `eager` you will get back `scalar` and assert the type is resolved. + /// In this case there will be no error, guaranteed. + /// If you pass `lazy` you may get back `scalar` or `val`. + /// If `val` is returned, a reference to `ty` has been captured. + /// If you pass `sema_kit` you will get back `scalar` and resolve the type if + /// necessary, possibly returning a CompileError. + pub fn abiSizeAdvanced( + ty: Type, + target: Target, + strat: AbiAlignmentAdvancedStrat, + ) Module.CompileError!AbiSizeAdvanced { + switch (ty.tag()) { .fn_noreturn_no_args => unreachable, // represents machine code; not a pointer .fn_void_no_args => unreachable, // represents machine code; not a pointer .fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer @@ -3109,32 +3136,59 @@ pub const Type = extern union { .empty_struct_literal, .empty_struct, .void, - => 0, + => return AbiSizeAdvanced{ .scalar = 0 }, - .@"struct", .tuple, .anon_struct => switch (self.containerLayout()) { + .@"struct", .tuple, .anon_struct => switch (ty.containerLayout()) { .Packed => { - const struct_obj = self.castTag(.@"struct").?.data; + const struct_obj = ty.castTag(.@"struct").?.data; + switch (strat) { + .sema_kit => |sk| _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty), + .lazy => |arena| { + if (!struct_obj.haveFieldTypes()) { + return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) }; + } + }, + .eager => {}, + } var buf: Type.Payload.Bits = undefined; const int_ty = struct_obj.packedIntegerType(target, &buf); - return int_ty.abiSize(target); + return AbiSizeAdvanced{ .scalar = int_ty.abiSize(target) }; }, else => { - const field_count = self.structFieldCount(); - if (field_count == 0) { - return 0; + switch (strat) { + .sema_kit => |sk| try sk.sema.resolveTypeLayout(sk.block, sk.src, ty), + .lazy => |arena| { + if (ty.castTag(.@"struct")) |payload| { + const struct_obj = payload.data; + if (!struct_obj.haveLayout()) { + return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) }; + } + } + }, + .eager => {}, } - return self.structFieldOffset(field_count, target); + const field_count = ty.structFieldCount(); + if (field_count == 0) { + return AbiSizeAdvanced{ .scalar = 0 }; + } + return AbiSizeAdvanced{ .scalar = ty.structFieldOffset(field_count, target) }; }, }, .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { var buffer: Payload.Bits = undefined; - const int_tag_ty = self.intTagType(&buffer); - return int_tag_ty.abiSize(target); + const int_tag_ty = ty.intTagType(&buffer); + return AbiSizeAdvanced{ .scalar = int_tag_ty.abiSize(target) }; + }, + .@"union" => { + const union_obj = ty.castTag(.@"union").?.data; + // TODO pass `true` for have_tag when unions have a safety tag + return abiSizeAdvancedUnion(ty, target, strat, union_obj, false); + }, + .union_tagged => { + const union_obj = ty.castTag(.union_tagged).?.data; + return abiSizeAdvancedUnion(ty, target, strat, union_obj, true); }, - // TODO pass `true` for have_tag when unions have a safety tag - .@"union" => return self.castTag(.@"union").?.data.abiSize(target, false), - .union_tagged => return self.castTag(.union_tagged).?.data.abiSize(target, true), .u1, .u8, @@ -3146,21 +3200,31 @@ pub const Type = extern union { .address_space, .float_mode, .reduce_op, - => return 1, + => return AbiSizeAdvanced{ .scalar = 1 }, - .array_u8 => self.castTag(.array_u8).?.data, - .array_u8_sentinel_0 => self.castTag(.array_u8_sentinel_0).?.data + 1, + .array_u8 => return AbiSizeAdvanced{ .scalar = ty.castTag(.array_u8).?.data }, + .array_u8_sentinel_0 => return AbiSizeAdvanced{ .scalar = ty.castTag(.array_u8_sentinel_0).?.data + 1 }, .array, .vector => { - const payload = self.cast(Payload.Array).?.data; - const elem_size = payload.elem_type.abiSize(target); - assert(elem_size >= payload.elem_type.abiAlignment(target)); - return payload.len * elem_size; + const payload = ty.cast(Payload.Array).?.data; + switch (try payload.elem_type.abiSizeAdvanced(target, strat)) { + .scalar => |elem_size| return AbiSizeAdvanced{ .scalar = payload.len * elem_size }, + .val => switch (strat) { + .sema_kit => unreachable, + .eager => unreachable, + .lazy => |arena| return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) }, + }, + } }, .array_sentinel => { - const payload = self.castTag(.array_sentinel).?.data; - const elem_size = payload.elem_type.abiSize(target); - assert(elem_size >= payload.elem_type.abiAlignment(target)); - return (payload.len + 1) * elem_size; + const payload = ty.castTag(.array_sentinel).?.data; + switch (try payload.elem_type.abiSizeAdvanced(target, strat)) { + .scalar => |elem_size| return AbiSizeAdvanced{ .scalar = (payload.len + 1) * elem_size }, + .val => switch (strat) { + .sema_kit => unreachable, + .eager => unreachable, + .lazy => |arena| return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) }, + }, + } }, .isize, @@ -3178,95 +3242,96 @@ pub const Type = extern union { .manyptr_u8, .manyptr_const_u8, .manyptr_const_u8_sentinel_0, - => return @divExact(target.cpu.arch.ptrBitWidth(), 8), + => return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }, .const_slice, .mut_slice, .const_slice_u8, .const_slice_u8_sentinel_0, - => return @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2, + => return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2 }, - .pointer => switch (self.castTag(.pointer).?.data.size) { - .Slice => @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2, - else => @divExact(target.cpu.arch.ptrBitWidth(), 8), + .pointer => switch (ty.castTag(.pointer).?.data.size) { + .Slice => return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) * 2 }, + else => return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }, }, - .c_short => return @divExact(CType.short.sizeInBits(target), 8), - .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8), - .c_int => return @divExact(CType.int.sizeInBits(target), 8), - .c_uint => return @divExact(CType.uint.sizeInBits(target), 8), - .c_long => return @divExact(CType.long.sizeInBits(target), 8), - .c_ulong => return @divExact(CType.ulong.sizeInBits(target), 8), - .c_longlong => return @divExact(CType.longlong.sizeInBits(target), 8), - .c_ulonglong => return @divExact(CType.ulonglong.sizeInBits(target), 8), + .c_short => return AbiSizeAdvanced{ .scalar = @divExact(CType.short.sizeInBits(target), 8) }, + .c_ushort => return AbiSizeAdvanced{ .scalar = @divExact(CType.ushort.sizeInBits(target), 8) }, + .c_int => return AbiSizeAdvanced{ .scalar = @divExact(CType.int.sizeInBits(target), 8) }, + .c_uint => return AbiSizeAdvanced{ .scalar = @divExact(CType.uint.sizeInBits(target), 8) }, + .c_long => return AbiSizeAdvanced{ .scalar = @divExact(CType.long.sizeInBits(target), 8) }, + .c_ulong => return AbiSizeAdvanced{ .scalar = @divExact(CType.ulong.sizeInBits(target), 8) }, + .c_longlong => return AbiSizeAdvanced{ .scalar = @divExact(CType.longlong.sizeInBits(target), 8) }, + .c_ulonglong => return AbiSizeAdvanced{ .scalar = @divExact(CType.ulonglong.sizeInBits(target), 8) }, - .f16 => return 2, - .f32 => return 4, - .f64 => return 8, - .f128 => return 16, + .f16 => return AbiSizeAdvanced{ .scalar = 2 }, + .f32 => return AbiSizeAdvanced{ .scalar = 4 }, + .f64 => return AbiSizeAdvanced{ .scalar = 8 }, + .f128 => return AbiSizeAdvanced{ .scalar = 16 }, .f80 => switch (target.cpu.arch) { - .i386 => return 12, - .x86_64 => return 16, + .i386 => return AbiSizeAdvanced{ .scalar = 12 }, + .x86_64 => return AbiSizeAdvanced{ .scalar = 16 }, else => { var payload: Payload.Bits = .{ .base = .{ .tag = .int_unsigned }, .data = 80, }; const u80_ty = initPayload(&payload.base); - return abiSize(u80_ty, target); + return AbiSizeAdvanced{ .scalar = abiSize(u80_ty, target) }; }, }, .c_longdouble => switch (CType.longdouble.sizeInBits(target)) { - 16 => return abiSize(Type.f16, target), - 32 => return abiSize(Type.f32, target), - 64 => return abiSize(Type.f64, target), - 80 => return abiSize(Type.f80, target), - 128 => return abiSize(Type.f128, target), + 16 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f16, target) }, + 32 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f32, target) }, + 64 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f64, target) }, + 80 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f80, target) }, + 128 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f128, target) }, else => unreachable, }, + // TODO revisit this when we have the concept of the error tag type .error_set, .error_set_single, .anyerror_void_error_union, .anyerror, .error_set_inferred, .error_set_merged, - => return 2, // TODO revisit this when we have the concept of the error tag type + => return AbiSizeAdvanced{ .scalar = 2 }, - .i16, .u16 => return intAbiSize(16, target), - .i32, .u32 => return intAbiSize(32, target), - .i64, .u64 => return intAbiSize(64, target), - .u128, .i128 => return intAbiSize(128, target), + .i16, .u16 => return AbiSizeAdvanced{ .scalar = intAbiSize(16, target) }, + .i32, .u32 => return AbiSizeAdvanced{ .scalar = intAbiSize(32, target) }, + .i64, .u64 => return AbiSizeAdvanced{ .scalar = intAbiSize(64, target) }, + .u128, .i128 => return AbiSizeAdvanced{ .scalar = intAbiSize(128, target) }, .int_signed, .int_unsigned => { - const bits: u16 = self.cast(Payload.Bits).?.data; - if (bits == 0) return 0; - return intAbiSize(bits, target); + const bits: u16 = ty.cast(Payload.Bits).?.data; + if (bits == 0) return AbiSizeAdvanced{ .scalar = 0 }; + return AbiSizeAdvanced{ .scalar = intAbiSize(bits, target) }; }, .optional => { var buf: Payload.ElemType = undefined; - const child_type = self.optionalChild(&buf); - if (!child_type.hasRuntimeBits()) return 1; + const child_type = ty.optionalChild(&buf); + if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 }; if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr() and !child_type.isSlice()) - return @divExact(target.cpu.arch.ptrBitWidth(), 8); + return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }; // Optional types are represented as a struct with the child type as the first // field and a boolean as the second. Since the child type's abi alignment is // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal // to the child type's ABI alignment. - return child_type.abiAlignment(target) + child_type.abiSize(target); + return AbiSizeAdvanced{ .scalar = child_type.abiAlignment(target) + child_type.abiSize(target) }; }, .error_union => { - const data = self.castTag(.error_union).?.data; + const data = ty.castTag(.error_union).?.data; if (!data.error_set.hasRuntimeBits() and !data.payload.hasRuntimeBits()) { - return 0; + return AbiSizeAdvanced{ .scalar = 0 }; } else if (!data.error_set.hasRuntimeBits()) { - return data.payload.abiSize(target); + return AbiSizeAdvanced{ .scalar = data.payload.abiSize(target) }; } else if (!data.payload.hasRuntimeBits()) { - return data.error_set.abiSize(target); + return AbiSizeAdvanced{ .scalar = data.error_set.abiSize(target) }; } const code_align = abiAlignment(data.error_set, target); const payload_align = abiAlignment(data.payload, target); @@ -3278,9 +3343,28 @@ pub const Type = extern union { size = std.mem.alignForwardGeneric(u64, size, payload_align); size += payload_size; size = std.mem.alignForwardGeneric(u64, size, big_align); - return size; + return AbiSizeAdvanced{ .scalar = size }; }, - }; + } + } + + pub fn abiSizeAdvancedUnion( + ty: Type, + target: Target, + strat: AbiAlignmentAdvancedStrat, + union_obj: *Module.Union, + have_tag: bool, + ) Module.CompileError!AbiSizeAdvanced { + switch (strat) { + .sema_kit => |sk| try sk.sema.resolveTypeLayout(sk.block, sk.src, ty), + .lazy => |arena| { + if (!union_obj.haveLayout()) { + return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) }; + } + }, + .eager => {}, + } + return AbiSizeAdvanced{ .scalar = union_obj.abiSize(target, have_tag) }; } fn intAbiSize(bits: u16, target: Target) u64 { @@ -5448,73 +5532,6 @@ pub const Type = extern union { } } - /// Asserts the type is an enum. - pub fn enumHasInt(ty: Type, int: Value, mod: *Module) bool { - const S = struct { - fn intInRange(tag_ty: Type, int_val: Value, end: usize, m: *Module) bool { - if (int_val.compareWithZero(.lt)) return false; - var end_payload: Value.Payload.U64 = .{ - .base = .{ .tag = .int_u64 }, - .data = end, - }; - const end_val = Value.initPayload(&end_payload.base); - if (int_val.compare(.gte, end_val, tag_ty, m)) return false; - return true; - } - }; - switch (ty.tag()) { - .enum_nonexhaustive => return int.intFitsInType(ty, mod.getTarget()), - .enum_full => { - const enum_full = ty.castTag(.enum_full).?.data; - const tag_ty = enum_full.tag_ty; - if (enum_full.values.count() == 0) { - return S.intInRange(tag_ty, int, enum_full.fields.count(), mod); - } else { - return enum_full.values.containsContext(int, .{ - .ty = tag_ty, - .mod = mod, - }); - } - }, - .enum_numbered => { - const enum_obj = ty.castTag(.enum_numbered).?.data; - const tag_ty = enum_obj.tag_ty; - if (enum_obj.values.count() == 0) { - return S.intInRange(tag_ty, int, enum_obj.fields.count(), mod); - } else { - return enum_obj.values.containsContext(int, .{ - .ty = tag_ty, - .mod = mod, - }); - } - }, - .enum_simple => { - const enum_simple = ty.castTag(.enum_simple).?.data; - const fields_len = enum_simple.fields.count(); - const bits = std.math.log2_int_ceil(usize, fields_len); - var buffer: Payload.Bits = .{ - .base = .{ .tag = .int_unsigned }, - .data = bits, - }; - const tag_ty = Type.initPayload(&buffer.base); - return S.intInRange(tag_ty, int, fields_len, mod); - }, - .atomic_order, - .atomic_rmw_op, - .calling_convention, - .address_space, - .float_mode, - .reduce_op, - .call_options, - .prefetch_options, - .export_options, - .extern_options, - => unreachable, - - else => unreachable, - } - } - /// This enum does not directly correspond to `std.builtin.TypeId` because /// it has extra enum tags in it, as a way of using less memory. For example, /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types diff --git a/src/value.zig b/src/value.zig index 588c7d2832..1280adf1e0 100644 --- a/src/value.zig +++ b/src/value.zig @@ -179,6 +179,8 @@ pub const Value = extern union { bound_fn, /// The ABI alignment of the payload type. lazy_align, + /// The ABI alignment of the payload type. + lazy_size, pub const last_no_payload_tag = Tag.empty_array; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -289,6 +291,7 @@ pub const Value = extern union { .ty, .lazy_align, + .lazy_size, => Payload.Ty, .int_type => Payload.IntType, @@ -460,7 +463,7 @@ pub const Value = extern union { .bound_fn, => unreachable, - .ty, .lazy_align => { + .ty, .lazy_align, .lazy_size => { const payload = self.cast(Payload.Ty).?; const new_payload = try arena.create(Payload.Ty); new_payload.* = .{ @@ -720,6 +723,11 @@ pub const Value = extern union { try val.castTag(.lazy_align).?.data.dump("", options, out_stream); return try out_stream.writeAll(")"); }, + .lazy_size => { + try out_stream.writeAll("@sizeOf("); + try val.castTag(.lazy_size).?.data.dump("", options, out_stream); + return try out_stream.writeAll(")"); + }, .int_type => { const int_type = val.castTag(.int_type).?.data; return out_stream.print("{s}{d}", .{ @@ -1040,6 +1048,14 @@ pub const Value = extern union { const x = ty.abiAlignment(target); return BigIntMutable.init(&space.limbs, x).toConst(); }, + .lazy_size => { + const ty = val.castTag(.lazy_size).?.data; + if (sema_kit) |sk| { + try sk.sema.resolveTypeLayout(sk.block, sk.src, ty); + } + const x = ty.abiSize(target); + return BigIntMutable.init(&space.limbs, x).toConst(); + }, .elem_ptr => { const elem_ptr = val.castTag(.elem_ptr).?.data; @@ -1087,6 +1103,14 @@ pub const Value = extern union { return ty.abiAlignment(target); } }, + .lazy_size => { + const ty = val.castTag(.lazy_size).?.data; + if (sema_kit) |sk| { + return (try ty.abiSizeAdvanced(target, .{ .sema_kit = sk })).scalar; + } else { + return ty.abiSize(target); + } + }, else => return null, } @@ -1670,118 +1694,6 @@ pub const Value = extern union { } } - /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. - /// Vectors are also accepted. Vector results are reduced with AND. - pub fn intFitsInType(self: Value, ty: Type, target: Target) bool { - switch (self.tag()) { - .zero, - .undef, - .bool_false, - => return true, - - .one, - .bool_true, - => switch (ty.zigTypeTag()) { - .Int => { - const info = ty.intInfo(target); - return switch (info.signedness) { - .signed => info.bits >= 2, - .unsigned => info.bits >= 1, - }; - }, - .ComptimeInt => return true, - else => unreachable, - }, - - .lazy_align => { - const info = ty.intInfo(target); - const max_needed_bits = @as(u16, 16) + @boolToInt(info.signedness == .signed); - // If it is u16 or bigger we know the alignment fits without resolving it. - if (info.bits >= max_needed_bits) return true; - const x = self.castTag(.lazy_align).?.data.abiAlignment(target); - if (x == 0) return true; - const actual_needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed); - return info.bits >= actual_needed_bits; - }, - - .int_u64 => switch (ty.zigTypeTag()) { - .Int => { - const x = self.castTag(.int_u64).?.data; - if (x == 0) return true; - const info = ty.intInfo(target); - const needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed); - return info.bits >= needed_bits; - }, - .ComptimeInt => return true, - else => unreachable, - }, - .int_i64 => switch (ty.zigTypeTag()) { - .Int => { - const x = self.castTag(.int_i64).?.data; - if (x == 0) return true; - const info = ty.intInfo(target); - if (info.signedness == .unsigned and x < 0) - return false; - var buffer: BigIntSpace = undefined; - return self.toBigInt(&buffer, target).fitsInTwosComp(info.signedness, info.bits); - }, - .ComptimeInt => return true, - else => unreachable, - }, - .int_big_positive => switch (ty.zigTypeTag()) { - .Int => { - const info = ty.intInfo(target); - return self.castTag(.int_big_positive).?.asBigInt().fitsInTwosComp(info.signedness, info.bits); - }, - .ComptimeInt => return true, - else => unreachable, - }, - .int_big_negative => switch (ty.zigTypeTag()) { - .Int => { - const info = ty.intInfo(target); - return self.castTag(.int_big_negative).?.asBigInt().fitsInTwosComp(info.signedness, info.bits); - }, - .ComptimeInt => return true, - else => unreachable, - }, - - .the_only_possible_value => { - assert(ty.intInfo(target).bits == 0); - return true; - }, - - .decl_ref_mut, - .extern_fn, - .decl_ref, - .function, - .variable, - => switch (ty.zigTypeTag()) { - .Int => { - const info = ty.intInfo(target); - const ptr_bits = target.cpu.arch.ptrBitWidth(); - return switch (info.signedness) { - .signed => info.bits > ptr_bits, - .unsigned => info.bits >= ptr_bits, - }; - }, - .ComptimeInt => return true, - else => unreachable, - }, - - .aggregate => { - assert(ty.zigTypeTag() == .Vector); - for (self.castTag(.aggregate).?.data) |elem| { - if (!elem.intFitsInType(ty.scalarType(), target)) { - return false; - } - } - return true; - }, - - else => unreachable, - } - } - /// Converts an integer or a float to a float. May result in a loss of information. /// Caller can find out by equality checking the result against the operand. pub fn floatCast(self: Value, arena: Allocator, dest_ty: Type, target: Target) !Value { @@ -1849,6 +1761,14 @@ pub const Value = extern union { return .eq; } }, + .lazy_size => { + const ty = lhs.castTag(.lazy_size).?.data; + if (try ty.hasRuntimeBitsAdvanced(false, sema_kit)) { + return .gt; + } else { + return .eq; + } + }, .float_16 => std.math.order(lhs.castTag(.float_16).?.data, 0), .float_32 => std.math.order(lhs.castTag(.float_32).?.data, 0), @@ -1992,38 +1912,28 @@ pub const Value = extern union { }; } - /// Asserts the values are comparable vectors of type `ty`. - pub fn compareVector( - lhs: Value, - op: std.math.CompareOperator, - rhs: Value, - ty: Type, - allocator: Allocator, - mod: *Module, - ) !Value { - assert(ty.zigTypeTag() == .Vector); - const result_data = try allocator.alloc(Value, ty.vectorLen()); - for (result_data) |*scalar, i| { - const res_bool = compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType(), mod); - scalar.* = makeBool(res_bool); - } - return Value.Tag.aggregate.create(allocator, result_data); - } - /// Asserts the value is comparable. /// Vector results will be reduced with AND. pub fn compareWithZero(lhs: Value, op: std.math.CompareOperator) bool { + return compareWithZeroAdvanced(lhs, op, null) catch unreachable; + } + + pub fn compareWithZeroAdvanced( + lhs: Value, + op: std.math.CompareOperator, + sema_kit: ?Module.WipAnalysis, + ) Module.CompileError!bool { switch (lhs.tag()) { - .repeated => return lhs.castTag(.repeated).?.data.compareWithZero(op), + .repeated => return lhs.castTag(.repeated).?.data.compareWithZeroAdvanced(op, sema_kit), .aggregate => { for (lhs.castTag(.aggregate).?.data) |elem_val| { - if (!elem_val.compareWithZero(op)) return false; + if (!(try elem_val.compareWithZeroAdvanced(op, sema_kit))) return false; } return true; }, else => {}, } - return orderAgainstZero(lhs).compare(op); + return (try orderAgainstZeroAdvanced(lhs, sema_kit)).compare(op); } /// This function is used by hash maps and so treats floating-point NaNs as equal @@ -2032,9 +1942,20 @@ pub const Value = extern union { /// This function has to be able to support implicit coercion of `a` to `ty`. That is, /// `ty` will be an exactly correct Type for `b` but it may be a post-coerced Type /// for `a`. This function must act *as if* `a` has been coerced to `ty`. This complication - /// is required in order to make generic function instantiation effecient - specifically + /// is required in order to make generic function instantiation efficient - specifically /// the insertion into the monomorphized function table. pub fn eql(a: Value, b: Value, ty: Type, mod: *Module) bool { + return eqlAdvanced(a, b, ty, mod, null) catch unreachable; + } + + /// If `null` is provided for `sema_kit` then it is guaranteed no error will be returned. + pub fn eqlAdvanced( + a: Value, + b: Value, + ty: Type, + mod: *Module, + sema_kit: ?Module.WipAnalysis, + ) Module.CompileError!bool { const target = mod.getTarget(); const a_tag = a.tag(); const b_tag = b.tag(); @@ -2055,31 +1976,33 @@ pub const Value = extern union { const a_payload = a.castTag(.opt_payload).?.data; const b_payload = b.castTag(.opt_payload).?.data; var buffer: Type.Payload.ElemType = undefined; - return eql(a_payload, b_payload, ty.optionalChild(&buffer), mod); + return eqlAdvanced(a_payload, b_payload, ty.optionalChild(&buffer), mod, sema_kit); }, .slice => { const a_payload = a.castTag(.slice).?.data; const b_payload = b.castTag(.slice).?.data; - if (!eql(a_payload.len, b_payload.len, Type.usize, mod)) return false; + if (!(try eqlAdvanced(a_payload.len, b_payload.len, Type.usize, mod, sema_kit))) { + return false; + } var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; const ptr_ty = ty.slicePtrFieldType(&ptr_buf); - return eql(a_payload.ptr, b_payload.ptr, ptr_ty, mod); + return eqlAdvanced(a_payload.ptr, b_payload.ptr, ptr_ty, mod, sema_kit); }, .elem_ptr => { const a_payload = a.castTag(.elem_ptr).?.data; const b_payload = b.castTag(.elem_ptr).?.data; if (a_payload.index != b_payload.index) return false; - return eql(a_payload.array_ptr, b_payload.array_ptr, ty, mod); + return eqlAdvanced(a_payload.array_ptr, b_payload.array_ptr, ty, mod, sema_kit); }, .field_ptr => { const a_payload = a.castTag(.field_ptr).?.data; const b_payload = b.castTag(.field_ptr).?.data; if (a_payload.field_index != b_payload.field_index) return false; - return eql(a_payload.container_ptr, b_payload.container_ptr, ty, mod); + return eqlAdvanced(a_payload.container_ptr, b_payload.container_ptr, ty, mod, sema_kit); }, .@"error" => { const a_name = a.castTag(.@"error").?.data.name; @@ -2089,7 +2012,7 @@ pub const Value = extern union { .eu_payload => { const a_payload = a.castTag(.eu_payload).?.data; const b_payload = b.castTag(.eu_payload).?.data; - return eql(a_payload, b_payload, ty.errorUnionPayload(), mod); + return eqlAdvanced(a_payload, b_payload, ty.errorUnionPayload(), mod, sema_kit); }, .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"), .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"), @@ -2107,7 +2030,9 @@ pub const Value = extern union { const types = ty.tupleFields().types; assert(types.len == a_field_vals.len); for (types) |field_ty, i| { - if (!eql(a_field_vals[i], b_field_vals[i], field_ty, mod)) return false; + if (!(try eqlAdvanced(a_field_vals[i], b_field_vals[i], field_ty, mod, sema_kit))) { + return false; + } } return true; } @@ -2116,7 +2041,9 @@ pub const Value = extern union { const fields = ty.structFields().values(); assert(fields.len == a_field_vals.len); for (fields) |field, i| { - if (!eql(a_field_vals[i], b_field_vals[i], field.ty, mod)) return false; + if (!(try eqlAdvanced(a_field_vals[i], b_field_vals[i], field.ty, mod, sema_kit))) { + return false; + } } return true; } @@ -2125,7 +2052,9 @@ pub const Value = extern union { for (a_field_vals) |a_elem, i| { const b_elem = b_field_vals[i]; - if (!eql(a_elem, b_elem, elem_ty, mod)) return false; + if (!(try eqlAdvanced(a_elem, b_elem, elem_ty, mod, sema_kit))) { + return false; + } } return true; }, @@ -2135,7 +2064,7 @@ pub const Value = extern union { switch (ty.containerLayout()) { .Packed, .Extern => { const tag_ty = ty.unionTagTypeHypothetical(); - if (!a_union.tag.eql(b_union.tag, tag_ty, mod)) { + if (!(try a_union.tag.eqlAdvanced(b_union.tag, tag_ty, mod, sema_kit))) { // In this case, we must disregard mismatching tags and compare // based on the in-memory bytes of the payloads. @panic("TODO comptime comparison of extern union values with mismatching tags"); @@ -2143,13 +2072,13 @@ pub const Value = extern union { }, .Auto => { const tag_ty = ty.unionTagTypeHypothetical(); - if (!a_union.tag.eql(b_union.tag, tag_ty, mod)) { + if (!(try a_union.tag.eqlAdvanced(b_union.tag, tag_ty, mod, sema_kit))) { return false; } }, } const active_field_ty = ty.unionFieldType(a_union.tag, mod); - return a_union.val.eql(b_union.val, active_field_ty, mod); + return a_union.val.eqlAdvanced(b_union.val, active_field_ty, mod, sema_kit); }, else => {}, } else if (a_tag == .null_value or b_tag == .null_value) { @@ -2183,7 +2112,7 @@ pub const Value = extern union { const b_val = b.enumToInt(ty, &buf_b); var buf_ty: Type.Payload.Bits = undefined; const int_ty = ty.intTagType(&buf_ty); - return eql(a_val, b_val, int_ty, mod); + return eqlAdvanced(a_val, b_val, int_ty, mod, sema_kit); }, .Array, .Vector => { const len = ty.arrayLen(); @@ -2194,7 +2123,9 @@ pub const Value = extern union { while (i < len) : (i += 1) { const a_elem = elemValueBuffer(a, mod, i, &a_buf); const b_elem = elemValueBuffer(b, mod, i, &b_buf); - if (!eql(a_elem, b_elem, elem_ty, mod)) return false; + if (!(try eqlAdvanced(a_elem, b_elem, elem_ty, mod, sema_kit))) { + return false; + } } return true; }, @@ -2218,12 +2149,12 @@ pub const Value = extern union { .base = .{ .tag = .opt_payload }, .data = a, }; - return eql(Value.initPayload(&buffer.base), b, ty, mod); + return eqlAdvanced(Value.initPayload(&buffer.base), b, ty, mod, sema_kit); } }, else => {}, } - return order(a, b, target).compare(.eq); + return (try orderAdvanced(a, b, target, sema_kit)).compare(.eq); } /// This function is used by hash maps and so treats floating-point NaNs as equal @@ -2502,6 +2433,7 @@ pub const Value = extern union { .bool_true, .the_only_possible_value, .lazy_align, + .lazy_size, => return hashInt(ptr_val, hasher, target), else => unreachable, @@ -2882,54 +2814,6 @@ pub const Value = extern union { } } - pub fn floatToInt(val: Value, arena: Allocator, float_ty: Type, int_ty: Type, target: Target) error{ FloatCannotFit, OutOfMemory }!Value { - if (float_ty.zigTypeTag() == .Vector) { - const result_data = try arena.alloc(Value, float_ty.vectorLen()); - for (result_data) |*scalar, i| { - scalar.* = try floatToIntScalar(val.indexVectorlike(i), arena, int_ty.scalarType(), target); - } - return Value.Tag.aggregate.create(arena, result_data); - } - return floatToIntScalar(val, arena, int_ty, target); - } - - pub fn floatToIntScalar(val: Value, arena: Allocator, int_ty: Type, target: Target) error{ FloatCannotFit, OutOfMemory }!Value { - const Limb = std.math.big.Limb; - - var value = val.toFloat(f64); // TODO: f128 ? - if (std.math.isNan(value) or std.math.isInf(value)) { - return error.FloatCannotFit; - } - - const isNegative = std.math.signbit(value); - value = @fabs(value); - - const floored = @floor(value); - - var rational = try std.math.big.Rational.init(arena); - defer rational.deinit(); - rational.setFloat(f64, floored) catch |err| switch (err) { - error.NonFiniteFloat => unreachable, - error.OutOfMemory => return error.OutOfMemory, - }; - - // The float is reduced in rational.setFloat, so we assert that denominator is equal to one - const bigOne = std.math.big.int.Const{ .limbs = &.{1}, .positive = true }; - assert(rational.q.toConst().eqAbs(bigOne)); - - const result_limbs = try arena.dupe(Limb, rational.p.toConst().limbs); - const result = if (isNegative) - try Value.Tag.int_big_negative.create(arena, result_limbs) - else - try Value.Tag.int_big_positive.create(arena, result_limbs); - - if (result.intFitsInType(int_ty, target)) { - return result; - } else { - return error.FloatCannotFit; - } - } - fn calcLimbLenFloat(scalar: anytype) usize { if (scalar == 0) { return 1; @@ -2945,96 +2829,7 @@ pub const Value = extern union { wrapped_result: Value, }; - pub fn intAddWithOverflow( - lhs: Value, - rhs: Value, - ty: Type, - arena: Allocator, - target: Target, - ) !OverflowArithmeticResult { - if (ty.zigTypeTag() == .Vector) { - const overflowed_data = try arena.alloc(Value, ty.vectorLen()); - const result_data = try arena.alloc(Value, ty.vectorLen()); - for (result_data) |*scalar, i| { - const of_math_result = try intAddWithOverflowScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); - overflowed_data[i] = of_math_result.overflowed; - scalar.* = of_math_result.wrapped_result; - } - return OverflowArithmeticResult{ - .overflowed = try Value.Tag.aggregate.create(arena, overflowed_data), - .wrapped_result = try Value.Tag.aggregate.create(arena, result_data), - }; - } - return intAddWithOverflowScalar(lhs, rhs, ty, arena, target); - } - - pub fn intAddWithOverflowScalar( - lhs: Value, - rhs: Value, - ty: Type, - arena: Allocator, - target: Target, - ) !OverflowArithmeticResult { - const info = ty.intInfo(target); - - var lhs_space: Value.BigIntSpace = undefined; - var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space, target); - const rhs_bigint = rhs.toBigInt(&rhs_space, target); - const limbs = try arena.alloc( - std.math.big.Limb, - std.math.big.int.calcTwosCompLimbCount(info.bits), - ); - var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; - const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); - const result = try fromBigInt(arena, result_bigint.toConst()); - return OverflowArithmeticResult{ - .overflowed = makeBool(overflowed), - .wrapped_result = result, - }; - } - - /// Supports both (vectors of) floats and ints; handles undefined scalars. - pub fn numberAddWrap( - lhs: Value, - rhs: Value, - ty: Type, - arena: Allocator, - target: Target, - ) !Value { - if (ty.zigTypeTag() == .Vector) { - const result_data = try arena.alloc(Value, ty.vectorLen()); - for (result_data) |*scalar, i| { - scalar.* = try numberAddWrapScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); - } - return Value.Tag.aggregate.create(arena, result_data); - } - return numberAddWrapScalar(lhs, rhs, ty, arena, target); - } - - /// Supports both floats and ints; handles undefined. - pub fn numberAddWrapScalar( - lhs: Value, - rhs: Value, - ty: Type, - arena: Allocator, - target: Target, - ) !Value { - if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); - - if (ty.zigTypeTag() == .ComptimeInt) { - return intAdd(lhs, rhs, ty, arena, target); - } - - if (ty.isAnyFloat()) { - return floatAdd(lhs, rhs, ty, arena, target); - } - - const overflow_result = try intAddWithOverflow(lhs, rhs, ty, arena, target); - return overflow_result.wrapped_result; - } - - fn fromBigInt(arena: Allocator, big_int: BigIntConst) !Value { + pub fn fromBigInt(arena: Allocator, big_int: BigIntConst) !Value { if (big_int.positive) { if (big_int.to(u64)) |x| { return Value.Tag.int_u64.create(arena, x); @@ -3094,95 +2889,6 @@ pub const Value = extern union { return fromBigInt(arena, result_bigint.toConst()); } - pub fn intSubWithOverflow( - lhs: Value, - rhs: Value, - ty: Type, - arena: Allocator, - target: Target, - ) !OverflowArithmeticResult { - if (ty.zigTypeTag() == .Vector) { - const overflowed_data = try arena.alloc(Value, ty.vectorLen()); - const result_data = try arena.alloc(Value, ty.vectorLen()); - for (result_data) |*scalar, i| { - const of_math_result = try intSubWithOverflowScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); - overflowed_data[i] = of_math_result.overflowed; - scalar.* = of_math_result.wrapped_result; - } - return OverflowArithmeticResult{ - .overflowed = try Value.Tag.aggregate.create(arena, overflowed_data), - .wrapped_result = try Value.Tag.aggregate.create(arena, result_data), - }; - } - return intSubWithOverflowScalar(lhs, rhs, ty, arena, target); - } - - pub fn intSubWithOverflowScalar( - lhs: Value, - rhs: Value, - ty: Type, - arena: Allocator, - target: Target, - ) !OverflowArithmeticResult { - const info = ty.intInfo(target); - - var lhs_space: Value.BigIntSpace = undefined; - var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space, target); - const rhs_bigint = rhs.toBigInt(&rhs_space, target); - const limbs = try arena.alloc( - std.math.big.Limb, - std.math.big.int.calcTwosCompLimbCount(info.bits), - ); - var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; - const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); - const wrapped_result = try fromBigInt(arena, result_bigint.toConst()); - return OverflowArithmeticResult{ - .overflowed = makeBool(overflowed), - .wrapped_result = wrapped_result, - }; - } - - /// Supports both (vectors of) floats and ints; handles undefined scalars. - pub fn numberSubWrap( - lhs: Value, - rhs: Value, - ty: Type, - arena: Allocator, - target: Target, - ) !Value { - if (ty.zigTypeTag() == .Vector) { - const result_data = try arena.alloc(Value, ty.vectorLen()); - for (result_data) |*scalar, i| { - scalar.* = try numberSubWrapScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); - } - return Value.Tag.aggregate.create(arena, result_data); - } - return numberSubWrapScalar(lhs, rhs, ty, arena, target); - } - - /// Supports both floats and ints; handles undefined. - pub fn numberSubWrapScalar( - lhs: Value, - rhs: Value, - ty: Type, - arena: Allocator, - target: Target, - ) !Value { - if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); - - if (ty.zigTypeTag() == .ComptimeInt) { - return intSub(lhs, rhs, ty, arena, target); - } - - if (ty.isAnyFloat()) { - return floatSub(lhs, rhs, ty, arena, target); - } - - const overflow_result = try intSubWithOverflow(lhs, rhs, ty, arena, target); - return overflow_result.wrapped_result; - } - /// Supports (vectors of) integers only; asserts neither operand is undefined. pub fn intSubSat( lhs: Value, @@ -3559,60 +3265,6 @@ pub const Value = extern union { return fromBigInt(arena, result_bigint.toConst()); } - pub fn intAdd(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { - if (ty.zigTypeTag() == .Vector) { - const result_data = try allocator.alloc(Value, ty.vectorLen()); - for (result_data) |*scalar, i| { - scalar.* = try intAddScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator, target); - } - return Value.Tag.aggregate.create(allocator, result_data); - } - return intAddScalar(lhs, rhs, allocator, target); - } - - pub fn intAddScalar(lhs: Value, rhs: Value, allocator: Allocator, target: Target) !Value { - // TODO is this a performance issue? maybe we should try the operation without - // resorting to BigInt first. - var lhs_space: Value.BigIntSpace = undefined; - var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space, target); - const rhs_bigint = rhs.toBigInt(&rhs_space, target); - const limbs = try allocator.alloc( - std.math.big.Limb, - std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, - ); - var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; - result_bigint.add(lhs_bigint, rhs_bigint); - return fromBigInt(allocator, result_bigint.toConst()); - } - - pub fn intSub(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { - if (ty.zigTypeTag() == .Vector) { - const result_data = try allocator.alloc(Value, ty.vectorLen()); - for (result_data) |*scalar, i| { - scalar.* = try intSubScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator, target); - } - return Value.Tag.aggregate.create(allocator, result_data); - } - return intSubScalar(lhs, rhs, allocator, target); - } - - pub fn intSubScalar(lhs: Value, rhs: Value, allocator: Allocator, target: Target) !Value { - // TODO is this a performance issue? maybe we should try the operation without - // resorting to BigInt first. - var lhs_space: Value.BigIntSpace = undefined; - var rhs_space: Value.BigIntSpace = undefined; - const lhs_bigint = lhs.toBigInt(&lhs_space, target); - const rhs_bigint = rhs.toBigInt(&rhs_space, target); - const limbs = try allocator.alloc( - std.math.big.Limb, - std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, - ); - var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; - result_bigint.sub(lhs_bigint, rhs_bigint); - return fromBigInt(allocator, result_bigint.toConst()); - } - pub fn intDiv(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value { if (ty.zigTypeTag() == .Vector) { const result_data = try allocator.alloc(Value, ty.vectorLen()); @@ -4129,114 +3781,6 @@ pub const Value = extern union { return fromBigInt(allocator, result_bigint.toConst()); } - pub fn floatAdd( - lhs: Value, - rhs: Value, - float_type: Type, - arena: Allocator, - target: Target, - ) !Value { - if (float_type.zigTypeTag() == .Vector) { - const result_data = try arena.alloc(Value, float_type.vectorLen()); - for (result_data) |*scalar, i| { - scalar.* = try floatAddScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType(), arena, target); - } - return Value.Tag.aggregate.create(arena, result_data); - } - return floatAddScalar(lhs, rhs, float_type, arena, target); - } - - pub fn floatAddScalar( - lhs: Value, - rhs: Value, - float_type: Type, - arena: Allocator, - target: Target, - ) !Value { - switch (float_type.floatBits(target)) { - 16 => { - const lhs_val = lhs.toFloat(f16); - const rhs_val = rhs.toFloat(f16); - return Value.Tag.float_16.create(arena, lhs_val + rhs_val); - }, - 32 => { - const lhs_val = lhs.toFloat(f32); - const rhs_val = rhs.toFloat(f32); - return Value.Tag.float_32.create(arena, lhs_val + rhs_val); - }, - 64 => { - const lhs_val = lhs.toFloat(f64); - const rhs_val = rhs.toFloat(f64); - return Value.Tag.float_64.create(arena, lhs_val + rhs_val); - }, - 80 => { - const lhs_val = lhs.toFloat(f80); - const rhs_val = rhs.toFloat(f80); - return Value.Tag.float_80.create(arena, lhs_val + rhs_val); - }, - 128 => { - const lhs_val = lhs.toFloat(f128); - const rhs_val = rhs.toFloat(f128); - return Value.Tag.float_128.create(arena, lhs_val + rhs_val); - }, - else => unreachable, - } - } - - pub fn floatSub( - lhs: Value, - rhs: Value, - float_type: Type, - arena: Allocator, - target: Target, - ) !Value { - if (float_type.zigTypeTag() == .Vector) { - const result_data = try arena.alloc(Value, float_type.vectorLen()); - for (result_data) |*scalar, i| { - scalar.* = try floatSubScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType(), arena, target); - } - return Value.Tag.aggregate.create(arena, result_data); - } - return floatSubScalar(lhs, rhs, float_type, arena, target); - } - - pub fn floatSubScalar( - lhs: Value, - rhs: Value, - float_type: Type, - arena: Allocator, - target: Target, - ) !Value { - switch (float_type.floatBits(target)) { - 16 => { - const lhs_val = lhs.toFloat(f16); - const rhs_val = rhs.toFloat(f16); - return Value.Tag.float_16.create(arena, lhs_val - rhs_val); - }, - 32 => { - const lhs_val = lhs.toFloat(f32); - const rhs_val = rhs.toFloat(f32); - return Value.Tag.float_32.create(arena, lhs_val - rhs_val); - }, - 64 => { - const lhs_val = lhs.toFloat(f64); - const rhs_val = rhs.toFloat(f64); - return Value.Tag.float_64.create(arena, lhs_val - rhs_val); - }, - 80 => { - const lhs_val = lhs.toFloat(f80); - const rhs_val = rhs.toFloat(f80); - return Value.Tag.float_80.create(arena, lhs_val - rhs_val); - }, - 128 => { - const lhs_val = lhs.toFloat(f128); - const rhs_val = rhs.toFloat(f128); - return Value.Tag.float_128.create(arena, lhs_val - rhs_val); - }, - else => unreachable, - } - } - pub fn floatNeg( val: Value, float_type: Type, diff --git a/test/behavior/sizeof_and_typeof.zig b/test/behavior/sizeof_and_typeof.zig index 3dd786d1f7..293acda267 100644 --- a/test/behavior/sizeof_and_typeof.zig +++ b/test/behavior/sizeof_and_typeof.zig @@ -169,8 +169,6 @@ test "@bitOffsetOf" { } test "@sizeOf(T) == 0 doesn't force resolving struct size" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - const S = struct { const Foo = struct { y: if (@sizeOf(Foo) == 0) u64 else u32, From 274654d73e36df29fdc017a009c8607fb46a15e4 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 May 2022 13:00:59 +0200 Subject: [PATCH 1596/2031] x64: implement matching SSE instructions for generic cross-comp target --- src/arch/x86_64/CodeGen.zig | 108 +++++++--- src/arch/x86_64/Emit.zig | 403 ++++++++++++++++++++++++------------ src/arch/x86_64/Mir.zig | 22 +- src/arch/x86_64/bits.zig | 11 + 4 files changed, 384 insertions(+), 160 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 8ce0d5884b..eeb4cab04f 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -881,7 +881,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { switch (elem_ty.zigTypeTag()) { .Vector => return self.fail("TODO allocRegOrMem for Vector type", .{}), .Float => { - if (self.intrinsicsAllowed(elem_ty)) { + if (intrinsicsAllowed(self.target.*, elem_ty)) { const ptr_bytes: u64 = 32; if (abi_size <= ptr_bytes) { if (self.register_manager.tryAllocReg(inst, sse)) |reg| { @@ -970,7 +970,7 @@ pub fn spillRegisters(self: *Self, comptime count: comptime_int, registers: [cou fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { const reg_class: RegisterManager.RegisterBitSet = switch (ty.zigTypeTag()) { .Float => blk: { - if (self.intrinsicsAllowed(ty)) break :blk sse; + if (intrinsicsAllowed(self.target.*, ty)) break :blk sse; return self.fail("TODO copy {} to register", .{ty.fmtDebug()}); }, else => gp, @@ -987,7 +987,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { fn copyToRegisterWithInstTracking(self: *Self, reg_owner: Air.Inst.Index, ty: Type, mcv: MCValue) !MCValue { const reg_class: RegisterManager.RegisterBitSet = switch (ty.zigTypeTag()) { .Float => blk: { - if (self.intrinsicsAllowed(ty)) break :blk sse; + if (intrinsicsAllowed(self.target.*, ty)) break :blk sse; return self.fail("TODO copy {} to register", .{ty.fmtDebug()}); }, else => gp, @@ -3462,16 +3462,28 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu }, .register => |src_reg| switch (dst_ty.zigTypeTag()) { .Float => { - if (self.intrinsicsAllowed(dst_ty)) { + if (intrinsicsAllowed(self.target.*, dst_ty)) { const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) { .f32 => switch (mir_tag) { - .add => Mir.Inst.Tag.add_f32_avx, - .cmp => Mir.Inst.Tag.cmp_f32_avx, + .add => if (hasAvxSupport(self.target.*)) + Mir.Inst.Tag.add_f32_avx + else + Mir.Inst.Tag.add_f32_sse, + .cmp => if (hasAvxSupport(self.target.*)) + Mir.Inst.Tag.cmp_f32_avx + else + Mir.Inst.Tag.cmp_f32_sse, else => return self.fail("TODO genBinOpMir for f32 register-register with MIR tag {}", .{mir_tag}), }, .f64 => switch (mir_tag) { - .add => Mir.Inst.Tag.add_f64_avx, - .cmp => Mir.Inst.Tag.cmp_f64_avx, + .add => if (hasAvxSupport(self.target.*)) + Mir.Inst.Tag.add_f64_avx + else + Mir.Inst.Tag.add_f64_sse, + .cmp => if (hasAvxSupport(self.target.*)) + Mir.Inst.Tag.cmp_f64_avx + else + Mir.Inst.Tag.cmp_f64_sse, else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}), }, else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}), @@ -5324,10 +5336,16 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE .register => |reg| { switch (ty.zigTypeTag()) { .Float => { - if (self.intrinsicsAllowed(ty)) { + if (intrinsicsAllowed(self.target.*, ty)) { const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => .mov_f32_avx, - .f64 => .mov_f64_avx, + .f32 => if (hasAvxSupport(self.target.*)) + Mir.Inst.Tag.mov_f32_avx + else + Mir.Inst.Tag.mov_f32_sse, + .f64 => if (hasAvxSupport(self.target.*)) + Mir.Inst.Tag.mov_f64_avx + else + Mir.Inst.Tag.mov_f64_sse, else => return self.fail("TODO genSetStackArg for register for type {}", .{ty.fmtDebug()}), }; _ = try self.addInst(.{ @@ -5508,10 +5526,16 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl switch (ty.zigTypeTag()) { .Float => { - if (self.intrinsicsAllowed(ty)) { + if (intrinsicsAllowed(self.target.*, ty)) { const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => .mov_f32_avx, - .f64 => .mov_f64_avx, + .f32 => if (hasAvxSupport(self.target.*)) + Mir.Inst.Tag.mov_f32_avx + else + Mir.Inst.Tag.mov_f32_sse, + .f64 => if (hasAvxSupport(self.target.*)) + Mir.Inst.Tag.mov_f64_avx + else + Mir.Inst.Tag.mov_f64_sse, else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}), }; _ = try self.addInst(.{ @@ -6032,10 +6056,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }, }, .Float => { - if (self.intrinsicsAllowed(ty)) { + if (intrinsicsAllowed(self.target.*, ty)) { const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => .mov_f32_avx, - .f64 => .mov_f64_avx, + .f32 => if (hasAvxSupport(self.target.*)) + Mir.Inst.Tag.mov_f32_avx + else + Mir.Inst.Tag.mov_f32_sse, + .f64 => if (hasAvxSupport(self.target.*)) + Mir.Inst.Tag.mov_f64_avx + else + Mir.Inst.Tag.mov_f64_sse, else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}), }; _ = try self.addInst(.{ @@ -6072,10 +6102,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void const base_reg = try self.register_manager.allocReg(null, gp); try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); - if (self.intrinsicsAllowed(ty)) { + if (intrinsicsAllowed(self.target.*, ty)) { const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => .mov_f32_avx, - .f64 => .mov_f64_avx, + .f32 => if (hasAvxSupport(self.target.*)) + Mir.Inst.Tag.mov_f32_avx + else + Mir.Inst.Tag.mov_f32_sse, + .f64 => if (hasAvxSupport(self.target.*)) + Mir.Inst.Tag.mov_f64_avx + else + Mir.Inst.Tag.mov_f64_sse, else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), }; @@ -6115,10 +6151,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void const base_reg = try self.register_manager.allocReg(null, gp); try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); - if (self.intrinsicsAllowed(ty)) { + if (intrinsicsAllowed(self.target.*, ty)) { const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => .mov_f32_avx, - .f64 => .mov_f64_avx, + .f32 => if (hasAvxSupport(self.target.*)) + Mir.Inst.Tag.mov_f32_avx + else + Mir.Inst.Tag.mov_f32_sse, + .f64 => if (hasAvxSupport(self.target.*)) + Mir.Inst.Tag.mov_f64_avx + else + Mir.Inst.Tag.mov_f64_sse, else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), }; @@ -6230,10 +6272,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }, }, .Float => { - if (self.intrinsicsAllowed(ty)) { + if (intrinsicsAllowed(self.target.*, ty)) { const tag: Mir.Inst.Tag = switch (ty.tag()) { - .f32 => .mov_f32_avx, - .f64 => .mov_f64_avx, + .f32 => if (hasAvxSupport(self.target.*)) + Mir.Inst.Tag.mov_f32_avx + else + Mir.Inst.Tag.mov_f32_sse, + .f64 => if (hasAvxSupport(self.target.*)) + Mir.Inst.Tag.mov_f64_avx + else + Mir.Inst.Tag.mov_f64_sse, else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}), }; _ = try self.addInst(.{ @@ -7046,11 +7094,15 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { } } -fn intrinsicsAllowed(self: *Self, ty: Type) bool { +fn intrinsicsAllowed(target: Target, ty: Type) bool { return switch (ty.tag()) { .f32, .f64, - => Target.x86.featureSetHasAny(self.target.cpu.features, .{ .avx, .avx2 }), + => Target.x86.featureSetHasAny(target.cpu.features, .{ .sse2, .avx, .avx2 }), else => unreachable, // TODO finish this off }; } + +fn hasAvxSupport(target: Target) bool { + return Target.x86.featureSetHasAny(target.cpu.features, .{ .avx, .avx2 }); +} diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index fbcd8359f7..84955a8aac 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -182,6 +182,16 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .interrupt => try emit.mirInterrupt(inst), .nop => try emit.mirNop(), + // SSE instructions + .mov_f64_sse => try emit.mirMovFloatSse(.movsd, inst), + .mov_f32_sse => try emit.mirMovFloatSse(.movss, inst), + + .add_f64_sse => try emit.mirAddFloatSse(.addsd, inst), + .add_f32_sse => try emit.mirAddFloatSse(.addss, inst), + + .cmp_f64_sse => try emit.mirCmpFloatSse(.ucomisd, inst), + .cmp_f32_sse => try emit.mirCmpFloatSse(.ucomiss, inst), + // AVX instructions .mov_f64_avx => try emit.mirMovFloatAvx(.vmovsd, inst), .mov_f32_avx => try emit.mirMovFloatAvx(.vmovss, inst), @@ -536,6 +546,7 @@ fn mirArithMemImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { } inline fn setRexWRegister(reg: Register) bool { + if (reg.size() > 64) return false; if (reg.size() == 64) return true; return switch (reg) { .ah, .ch, .dh, .bh => true, @@ -963,11 +974,55 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } } +// SSE instructions + +fn mirMovFloatSse(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { + const ops = emit.mir.instructions.items(.ops)[inst].decode(); + switch (ops.flags) { + 0b00 => { + const imm = emit.mir.instructions.items(.data)[inst].imm; + return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{ + .disp = imm, + .base = ops.reg2, + }), emit.code); + }, + 0b01 => { + const imm = emit.mir.instructions.items(.data)[inst].imm; + return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{ + .disp = imm, + .base = ops.reg1, + }), ops.reg2, emit.code); + }, + 0b10 => { + return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); + }, + else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }), + } +} + +fn mirAddFloatSse(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { + const ops = emit.mir.instructions.items(.ops)[inst].decode(); + switch (ops.flags) { + 0b00 => { + return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); + }, + else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }), + } +} + +fn mirCmpFloatSse(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { + const ops = emit.mir.instructions.items(.ops)[inst].decode(); + switch (ops.flags) { + 0b00 => { + return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); + }, + else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }), + } +} // AVX instructions fn mirMovFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { 0b00 => { const imm = emit.mir.instructions.items(.data)[inst].imm; @@ -986,24 +1041,22 @@ fn mirMovFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { 0b10 => { return lowerToRvmEnc(tag, ops.reg1, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); }, - else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{ops.flags}), + else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }), } } fn mirAddFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { 0b00 => { return lowerToRvmEnc(tag, ops.reg1, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); }, - else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{ops.flags}), + else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }), } } fn mirCmpFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); - switch (ops.flags) { 0b00 => { return lowerToVmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); @@ -1247,6 +1300,14 @@ const Tag = enum { cmovng, cmovb, cmovnae, + movsd, + movss, + addsd, + addss, + cmpsd, + cmpss, + ucomisd, + ucomiss, vmovsd, vmovss, vaddsd, @@ -1256,6 +1317,22 @@ const Tag = enum { vucomisd, vucomiss, + fn isSse(tag: Tag) bool { + return switch (tag) { + .movsd, + .movss, + .addsd, + .addss, + .cmpsd, + .cmpss, + .ucomisd, + .ucomiss, + => true, + + else => false, + }; + } + fn isAvx(tag: Tag) bool { return switch (tag) { .vmovsd, @@ -1369,190 +1446,256 @@ const Encoding = enum { rvmi, }; -const OpCode = union(enum) { - one_byte: u8, - two_byte: struct { _1: u8, _2: u8 }, +const OpCode = struct { + bytes: [3]u8, + count: usize, - fn oneByte(opc: u8) OpCode { - return .{ .one_byte = opc }; - } - - fn twoByte(opc1: u8, opc2: u8) OpCode { - return .{ .two_byte = .{ ._1 = opc1, ._2 = opc2 } }; + fn init(comptime in_bytes: []const u8) OpCode { + comptime assert(in_bytes.len <= 3); + comptime var bytes: [3]u8 = undefined; + inline for (in_bytes) |x, i| { + bytes[i] = x; + } + return .{ .bytes = bytes, .count = in_bytes.len }; } fn encode(opc: OpCode, encoder: Encoder) void { - switch (opc) { - .one_byte => |v| encoder.opcode_1byte(v), - .two_byte => |v| encoder.opcode_2byte(v._1, v._2), + switch (opc.count) { + 1 => encoder.opcode_1byte(opc.bytes[0]), + 2 => encoder.opcode_2byte(opc.bytes[0], opc.bytes[1]), + 3 => encoder.opcode_3byte(opc.bytes[0], opc.bytes[1], opc.bytes[2]), + else => unreachable, } } fn encodeWithReg(opc: OpCode, encoder: Encoder, reg: Register) void { - assert(opc == .one_byte); - encoder.opcode_withReg(opc.one_byte, reg.lowEnc()); + assert(opc.count == 1); + encoder.opcode_withReg(opc.bytes[0], reg.lowEnc()); } }; inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) OpCode { + // zig fmt: off switch (enc) { .zo => return switch (tag) { - .ret_near => OpCode.oneByte(0xc3), - .ret_far => OpCode.oneByte(0xcb), - .int3 => OpCode.oneByte(0xcc), - .nop => OpCode.oneByte(0x90), - .syscall => OpCode.twoByte(0x0f, 0x05), - .cbw => OpCode.oneByte(0x98), - .cwd, .cdq, .cqo => OpCode.oneByte(0x99), - else => unreachable, + .ret_near => OpCode.init(&.{0xc3}), + .ret_far => OpCode.init(&.{0xcb}), + .int3 => OpCode.init(&.{0xcc}), + .nop => OpCode.init(&.{0x90}), + .syscall => OpCode.init(&.{ 0x0f, 0x05 }), + .cbw => OpCode.init(&.{0x98}), + .cwd, + .cdq, + .cqo => OpCode.init(&.{0x99}), + else => unreachable, }, .d => return switch (tag) { - .jmp_near => OpCode.oneByte(0xe9), - .call_near => OpCode.oneByte(0xe8), - .jo => if (is_one_byte) OpCode.oneByte(0x70) else OpCode.twoByte(0x0f, 0x80), - .jno => if (is_one_byte) OpCode.oneByte(0x71) else OpCode.twoByte(0x0f, 0x81), - .jb, .jc, .jnae => if (is_one_byte) OpCode.oneByte(0x72) else OpCode.twoByte(0x0f, 0x82), - .jnb, .jnc, .jae => if (is_one_byte) OpCode.oneByte(0x73) else OpCode.twoByte(0x0f, 0x83), - .je, .jz => if (is_one_byte) OpCode.oneByte(0x74) else OpCode.twoByte(0x0f, 0x84), - .jne, .jnz => if (is_one_byte) OpCode.oneByte(0x75) else OpCode.twoByte(0x0f, 0x85), - .jna, .jbe => if (is_one_byte) OpCode.oneByte(0x76) else OpCode.twoByte(0x0f, 0x86), - .jnbe, .ja => if (is_one_byte) OpCode.oneByte(0x77) else OpCode.twoByte(0x0f, 0x87), - .js => if (is_one_byte) OpCode.oneByte(0x78) else OpCode.twoByte(0x0f, 0x88), - .jns => if (is_one_byte) OpCode.oneByte(0x79) else OpCode.twoByte(0x0f, 0x89), - .jpe, .jp => if (is_one_byte) OpCode.oneByte(0x7a) else OpCode.twoByte(0x0f, 0x8a), - .jpo, .jnp => if (is_one_byte) OpCode.oneByte(0x7b) else OpCode.twoByte(0x0f, 0x8b), - .jnge, .jl => if (is_one_byte) OpCode.oneByte(0x7c) else OpCode.twoByte(0x0f, 0x8c), - .jge, .jnl => if (is_one_byte) OpCode.oneByte(0x7d) else OpCode.twoByte(0x0f, 0x8d), - .jle, .jng => if (is_one_byte) OpCode.oneByte(0x7e) else OpCode.twoByte(0x0f, 0x8e), - .jg, .jnle => if (is_one_byte) OpCode.oneByte(0x7f) else OpCode.twoByte(0x0f, 0x8f), - else => unreachable, + .jmp_near => OpCode.init(&.{0xe9}), + .call_near => OpCode.init(&.{0xe8}), + .jo => if (is_one_byte) OpCode.init(&.{0x70}) else OpCode.init(&.{0x0f,0x80}), + .jno => if (is_one_byte) OpCode.init(&.{0x71}) else OpCode.init(&.{0x0f,0x81}), + .jb, + .jc, + .jnae => if (is_one_byte) OpCode.init(&.{0x72}) else OpCode.init(&.{0x0f,0x82}), + .jnb, + .jnc, + .jae => if (is_one_byte) OpCode.init(&.{0x73}) else OpCode.init(&.{0x0f,0x83}), + .je, + .jz => if (is_one_byte) OpCode.init(&.{0x74}) else OpCode.init(&.{0x0f,0x84}), + .jne, + .jnz => if (is_one_byte) OpCode.init(&.{0x75}) else OpCode.init(&.{0x0f,0x85}), + .jna, + .jbe => if (is_one_byte) OpCode.init(&.{0x76}) else OpCode.init(&.{0x0f,0x86}), + .jnbe, + .ja => if (is_one_byte) OpCode.init(&.{0x77}) else OpCode.init(&.{0x0f,0x87}), + .js => if (is_one_byte) OpCode.init(&.{0x78}) else OpCode.init(&.{0x0f,0x88}), + .jns => if (is_one_byte) OpCode.init(&.{0x79}) else OpCode.init(&.{0x0f,0x89}), + .jpe, + .jp => if (is_one_byte) OpCode.init(&.{0x7a}) else OpCode.init(&.{0x0f,0x8a}), + .jpo, + .jnp => if (is_one_byte) OpCode.init(&.{0x7b}) else OpCode.init(&.{0x0f,0x8b}), + .jnge, + .jl => if (is_one_byte) OpCode.init(&.{0x7c}) else OpCode.init(&.{0x0f,0x8c}), + .jge, + .jnl => if (is_one_byte) OpCode.init(&.{0x7d}) else OpCode.init(&.{0x0f,0x8d}), + .jle, + .jng => if (is_one_byte) OpCode.init(&.{0x7e}) else OpCode.init(&.{0x0f,0x8e}), + .jg, + .jnle => if (is_one_byte) OpCode.init(&.{0x7f}) else OpCode.init(&.{0x0f,0x8f}), + else => unreachable, }, .m => return switch (tag) { - .jmp_near, .call_near, .push => OpCode.oneByte(0xff), - .pop => OpCode.oneByte(0x8f), - .seto => OpCode.twoByte(0x0f, 0x90), - .setno => OpCode.twoByte(0x0f, 0x91), - .setb, .setc, .setnae => OpCode.twoByte(0x0f, 0x92), - .setnb, .setnc, .setae => OpCode.twoByte(0x0f, 0x93), - .sete, .setz => OpCode.twoByte(0x0f, 0x94), - .setne, .setnz => OpCode.twoByte(0x0f, 0x95), - .setbe, .setna => OpCode.twoByte(0x0f, 0x96), - .seta, .setnbe => OpCode.twoByte(0x0f, 0x97), - .sets => OpCode.twoByte(0x0f, 0x98), - .setns => OpCode.twoByte(0x0f, 0x99), - .setp, .setpe => OpCode.twoByte(0x0f, 0x9a), - .setnp, .setop => OpCode.twoByte(0x0f, 0x9b), - .setl, .setnge => OpCode.twoByte(0x0f, 0x9c), - .setnl, .setge => OpCode.twoByte(0x0f, 0x9d), - .setle, .setng => OpCode.twoByte(0x0f, 0x9e), - .setnle, .setg => OpCode.twoByte(0x0f, 0x9f), - .idiv, .div, .imul, .mul => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7), - .fisttp16 => OpCode.oneByte(0xdf), - .fisttp32 => OpCode.oneByte(0xdb), - .fisttp64 => OpCode.oneByte(0xdd), - .fld32 => OpCode.oneByte(0xd9), - .fld64 => OpCode.oneByte(0xdd), - else => unreachable, + .jmp_near, + .call_near, + .push => OpCode.init(&.{0xff}), + .pop => OpCode.init(&.{0x8f}), + .seto => OpCode.init(&.{0x0f,0x90}), + .setno => OpCode.init(&.{0x0f,0x91}), + .setb, + .setc, + .setnae => OpCode.init(&.{0x0f,0x92}), + .setnb, + .setnc, + .setae => OpCode.init(&.{0x0f,0x93}), + .sete, + .setz => OpCode.init(&.{0x0f,0x94}), + .setne, + .setnz => OpCode.init(&.{0x0f,0x95}), + .setbe, + .setna => OpCode.init(&.{0x0f,0x96}), + .seta, + .setnbe => OpCode.init(&.{0x0f,0x97}), + .sets => OpCode.init(&.{0x0f,0x98}), + .setns => OpCode.init(&.{0x0f,0x99}), + .setp, + .setpe => OpCode.init(&.{0x0f,0x9a}), + .setnp, + .setop => OpCode.init(&.{0x0f,0x9b}), + .setl, + .setnge => OpCode.init(&.{0x0f,0x9c}), + .setnl, + .setge => OpCode.init(&.{0x0f,0x9d}), + .setle, + .setng => OpCode.init(&.{0x0f,0x9e}), + .setnle, + .setg => OpCode.init(&.{0x0f,0x9f}), + .idiv, + .div, + .imul, + .mul => if (is_one_byte) OpCode.init(&.{0xf6}) else OpCode.init(&.{0xf7}), + .fisttp16 => OpCode.init(&.{0xdf}), + .fisttp32 => OpCode.init(&.{0xdb}), + .fisttp64 => OpCode.init(&.{0xdd}), + .fld32 => OpCode.init(&.{0xd9}), + .fld64 => OpCode.init(&.{0xdd}), + else => unreachable, }, .o => return switch (tag) { - .push => OpCode.oneByte(0x50), - .pop => OpCode.oneByte(0x58), - else => unreachable, + .push => OpCode.init(&.{0x50}), + .pop => OpCode.init(&.{0x58}), + else => unreachable, }, .i => return switch (tag) { - .push => OpCode.oneByte(if (is_one_byte) 0x6a else 0x68), - .@"test" => OpCode.oneByte(if (is_one_byte) 0xa8 else 0xa9), - .ret_near => OpCode.oneByte(0xc2), - .ret_far => OpCode.oneByte(0xca), - else => unreachable, + .push => if (is_one_byte) OpCode.init(&.{0x6a}) else OpCode.init(&.{0x68}), + .@"test" => if (is_one_byte) OpCode.init(&.{0xa8}) else OpCode.init(&.{0xa9}), + .ret_near => OpCode.init(&.{0xc2}), + .ret_far => OpCode.init(&.{0xca}), + else => unreachable, }, .m1 => return switch (tag) { - .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd0 else 0xd1), - else => unreachable, + .shl, .sal, + .shr, .sar => if (is_one_byte) OpCode.init(&.{0xd0}) else OpCode.init(&.{0xd1}), + else => unreachable, }, .mc => return switch (tag) { - .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd2 else 0xd3), - else => unreachable, + .shl, .sal, + .shr, .sar => if (is_one_byte) OpCode.init(&.{0xd2}) else OpCode.init(&.{0xd3}), + else => unreachable, }, .mi => return switch (tag) { - .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(if (is_one_byte) 0x80 else 0x81), - .mov => OpCode.oneByte(if (is_one_byte) 0xc6 else 0xc7), - .@"test" => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7), - else => unreachable, + .adc, .add, + .sub, .xor, + .@"and", .@"or", + .sbb, .cmp => if (is_one_byte) OpCode.init(&.{0x80}) else OpCode.init(&.{0x81}), + .mov => if (is_one_byte) OpCode.init(&.{0xc6}) else OpCode.init(&.{0xc7}), + .@"test" => if (is_one_byte) OpCode.init(&.{0xf6}) else OpCode.init(&.{0xf7}), + else => unreachable, }, .mi8 => return switch (tag) { - .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(0x83), - .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xc0 else 0xc1), - else => unreachable, + .adc, .add, + .sub, .xor, + .@"and", .@"or", + .sbb, .cmp => OpCode.init(&.{0x83}), + .shl, .sal, + .shr, .sar => if (is_one_byte) OpCode.init(&.{0xc0}) else OpCode.init(&.{0xc1}), + else => unreachable, }, .mr => return switch (tag) { - .adc => OpCode.oneByte(if (is_one_byte) 0x10 else 0x11), - .add => OpCode.oneByte(if (is_one_byte) 0x00 else 0x01), - .sub => OpCode.oneByte(if (is_one_byte) 0x28 else 0x29), - .xor => OpCode.oneByte(if (is_one_byte) 0x30 else 0x31), - .@"and" => OpCode.oneByte(if (is_one_byte) 0x20 else 0x21), - .@"or" => OpCode.oneByte(if (is_one_byte) 0x08 else 0x09), - .sbb => OpCode.oneByte(if (is_one_byte) 0x18 else 0x19), - .cmp => OpCode.oneByte(if (is_one_byte) 0x38 else 0x39), - .mov => OpCode.oneByte(if (is_one_byte) 0x88 else 0x89), - .@"test" => OpCode.oneByte(if (is_one_byte) 0x84 else 0x85), - else => unreachable, + .adc => if (is_one_byte) OpCode.init(&.{0x10}) else OpCode.init(&.{0x11}), + .add => if (is_one_byte) OpCode.init(&.{0x00}) else OpCode.init(&.{0x01}), + .sub => if (is_one_byte) OpCode.init(&.{0x28}) else OpCode.init(&.{0x29}), + .xor => if (is_one_byte) OpCode.init(&.{0x30}) else OpCode.init(&.{0x31}), + .@"and" => if (is_one_byte) OpCode.init(&.{0x20}) else OpCode.init(&.{0x21}), + .@"or" => if (is_one_byte) OpCode.init(&.{0x08}) else OpCode.init(&.{0x09}), + .sbb => if (is_one_byte) OpCode.init(&.{0x18}) else OpCode.init(&.{0x19}), + .cmp => if (is_one_byte) OpCode.init(&.{0x38}) else OpCode.init(&.{0x39}), + .mov => if (is_one_byte) OpCode.init(&.{0x88}) else OpCode.init(&.{0x89}), + .@"test" => if (is_one_byte) OpCode.init(&.{0x84}) else OpCode.init(&.{0x85}), + .movsd => OpCode.init(&.{0xf2,0x0f,0x11}), + .movss => OpCode.init(&.{0xf3,0x0f,0x11}), + else => unreachable, }, .rm => return switch (tag) { - .adc => OpCode.oneByte(if (is_one_byte) 0x12 else 0x13), - .add => OpCode.oneByte(if (is_one_byte) 0x02 else 0x03), - .sub => OpCode.oneByte(if (is_one_byte) 0x2a else 0x2b), - .xor => OpCode.oneByte(if (is_one_byte) 0x32 else 0x33), - .@"and" => OpCode.oneByte(if (is_one_byte) 0x22 else 0x23), - .@"or" => OpCode.oneByte(if (is_one_byte) 0x0a else 0x0b), - .sbb => OpCode.oneByte(if (is_one_byte) 0x1a else 0x1b), - .cmp => OpCode.oneByte(if (is_one_byte) 0x3a else 0x3b), - .mov => OpCode.oneByte(if (is_one_byte) 0x8a else 0x8b), - .movsx => OpCode.twoByte(0x0f, if (is_one_byte) 0xbe else 0xbf), - .movsxd => OpCode.oneByte(0x63), - .movzx => OpCode.twoByte(0x0f, if (is_one_byte) 0xb6 else 0xb7), - .lea => OpCode.oneByte(if (is_one_byte) 0x8c else 0x8d), - .imul => OpCode.twoByte(0x0f, 0xaf), - .cmove, .cmovz => OpCode.twoByte(0x0f, 0x44), - .cmovb, .cmovnae => OpCode.twoByte(0x0f, 0x42), - .cmovl, .cmovng => OpCode.twoByte(0x0f, 0x4c), + .adc => if (is_one_byte) OpCode.init(&.{0x12}) else OpCode.init(&.{0x13}), + .add => if (is_one_byte) OpCode.init(&.{0x02}) else OpCode.init(&.{0x03}), + .sub => if (is_one_byte) OpCode.init(&.{0x2a}) else OpCode.init(&.{0x2b}), + .xor => if (is_one_byte) OpCode.init(&.{0x32}) else OpCode.init(&.{0x33}), + .@"and" => if (is_one_byte) OpCode.init(&.{0x22}) else OpCode.init(&.{0x23}), + .@"or" => if (is_one_byte) OpCode.init(&.{0x0a}) else OpCode.init(&.{0x0b}), + .sbb => if (is_one_byte) OpCode.init(&.{0x1a}) else OpCode.init(&.{0x1b}), + .cmp => if (is_one_byte) OpCode.init(&.{0x3a}) else OpCode.init(&.{0x3b}), + .mov => if (is_one_byte) OpCode.init(&.{0x8a}) else OpCode.init(&.{0x8b}), + .movsx => if (is_one_byte) OpCode.init(&.{0x0f,0xbe}) else OpCode.init(&.{0x0f,0xbf}), + .movsxd => OpCode.init(&.{0x63}), + .movzx => if (is_one_byte) OpCode.init(&.{0x0f,0xb6}) else OpCode.init(&.{0x0f,0xb7}), + .lea => if (is_one_byte) OpCode.init(&.{0x8c}) else OpCode.init(&.{0x8d}), + .imul => OpCode.init(&.{0x0f,0xaf}), + .cmove, + .cmovz => OpCode.init(&.{0x0f,0x44}), + .cmovb, + .cmovnae => OpCode.init(&.{0x0f,0x42}), + .cmovl, + .cmovng => OpCode.init(&.{0x0f,0x4c}), + .movsd => OpCode.init(&.{0xf2,0x0f,0x10}), + .movss => OpCode.init(&.{0xf3,0x0f,0x10}), + .addsd => OpCode.init(&.{0xf2,0x0f,0x58}), + .addss => OpCode.init(&.{0xf3,0x0f,0x58}), + .ucomisd => OpCode.init(&.{0x66,0x0f,0x2e}), + .ucomiss => OpCode.init(&.{0x0f,0x2e}), else => unreachable, }, .oi => return switch (tag) { - .mov => OpCode.oneByte(if (is_one_byte) 0xb0 else 0xb8), + .mov => if (is_one_byte) OpCode.init(&.{0xb0}) else OpCode.init(&.{0xb8}), else => unreachable, }, .fd => return switch (tag) { - .mov => OpCode.oneByte(if (is_one_byte) 0xa0 else 0xa1), + .mov => if (is_one_byte) OpCode.init(&.{0xa0}) else OpCode.init(&.{0xa1}), else => unreachable, }, .td => return switch (tag) { - .mov => OpCode.oneByte(if (is_one_byte) 0xa2 else 0xa3), + .mov => if (is_one_byte) OpCode.init(&.{0xa2}) else OpCode.init(&.{0xa3}), else => unreachable, }, .rmi => return switch (tag) { - .imul => OpCode.oneByte(if (is_one_byte) 0x6b else 0x69), - else => unreachable, + .imul => if (is_one_byte) OpCode.init(&.{0x6b}) else OpCode.init(&.{0x69}), + else => unreachable, }, .mv => return switch (tag) { - .vmovsd, .vmovss => OpCode.oneByte(0x11), + .vmovsd, + .vmovss => OpCode.init(&.{0x11}), else => unreachable, }, .vm => return switch (tag) { - .vmovsd, .vmovss => OpCode.oneByte(0x10), - .vucomisd, .vucomiss => OpCode.oneByte(0x2e), + .vmovsd, + .vmovss => OpCode.init(&.{0x10}), + .vucomisd, + .vucomiss => OpCode.init(&.{0x2e}), else => unreachable, }, .rvm => return switch (tag) { - .vaddsd, .vaddss => OpCode.oneByte(0x58), - .vmovsd, .vmovss => OpCode.oneByte(0x10), + .vaddsd, + .vaddss => OpCode.init(&.{0x58}), + .vmovsd, + .vmovss => OpCode.init(&.{0x10}), else => unreachable, }, .rvmi => return switch (tag) { - .vcmpsd, .vcmpss => OpCode.oneByte(0xc2), - else => unreachable, + .vcmpsd, + .vcmpss => OpCode.init(&.{0xc2}), + else => unreachable, }, } + // zig fmt: on } inline fn getModRmExt(tag: Tag) u3 { diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 0f200d43e6..a35231a9b8 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -345,11 +345,29 @@ pub const Inst = struct { /// Nop nop, - /// AVX instructions + /// SSE instructions /// ops flags: form: /// 0b00 reg1, qword ptr [reg2 + imm32] /// 0b01 qword ptr [reg1 + imm32], reg2 /// 0b10 reg1, reg2 + mov_f64_sse, + mov_f32_sse, + + /// ops flags: form: + /// 0b00 reg1, reg2 + add_f64_sse, + add_f32_sse, + + /// ops flags: form: + /// 0b00 reg1, reg2 + cmp_f64_sse, + cmp_f32_sse, + + /// AVX instructions + /// ops flags: form: + /// 0b00 reg1, qword ptr [reg2 + imm32] + /// 0b01 qword ptr [reg1 + imm32], reg2 + /// 0b10 reg1, reg1, reg2 mov_f64_avx, mov_f32_avx, @@ -359,7 +377,7 @@ pub const Inst = struct { add_f32_avx, /// ops flags: form: - /// + /// 0b00 reg1, reg1, reg2 cmp_f64_avx, cmp_f32_avx, diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 64a067d050..6429781516 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -441,6 +441,17 @@ pub const Encoder = struct { self.code.appendAssumeCapacity(opcode); } + /// Encodes a 3 byte opcode + /// + /// e.g. MOVSD has the opcode 0xf2 0x0f 0x10 + /// + /// encoder.opcode_3byte(0xf2, 0x0f, 0x10); + pub fn opcode_3byte(self: Self, prefix_1: u8, prefix_2: u8, opcode: u8) void { + self.code.appendAssumeCapacity(prefix_1); + self.code.appendAssumeCapacity(prefix_2); + self.code.appendAssumeCapacity(opcode); + } + /// Encodes a 1 byte opcode with a reg field /// /// Remember to add a REX prefix byte if reg is extended! From 1697a6f0443ca8896081f98bd8648bc7bdb2cc58 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 20 May 2022 21:14:53 -0700 Subject: [PATCH 1597/2031] LLVM: rework calling convention lowering The previous implementation of calling conventions was hacky and broken. This commit reworks lowerFnParamTy into iterateParamTypes which returns enum tags indicating how to handle each parameter. This is then used in the three places that matter: * lowering a function type to llvm type * converting function parameters to the canonical type representation (with respect to isByRef). * converting canonical type representation to function arguments at callsites (again with respect to isByRef). As a result, we are one step closer to the C ABI tests passing. Before this commit, attempting to build them crashed the compiler. I isolated the broken function and verified that it now is lowered correctly. I will keep working on this one piece at a time until all the C ABI tests pass, and then I will enable all of them in the CI. --- src/codegen/llvm.zig | 729 ++++++++++++++++++++++++---------- src/codegen/llvm/bindings.zig | 3 + 2 files changed, 519 insertions(+), 213 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index aba290060a..aa10cd4fd4 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -682,25 +682,130 @@ pub const Object = struct { else null; + // This is the list of args we will use that correspond directly to the AIR arg + // instructions. Depending on the calling convention, this list is not necessarily + // a bijection with the actual LLVM parameters of the function. var args = std.ArrayList(*const llvm.Value).init(gpa); defer args.deinit(); - const param_offset = @as(c_uint, @boolToInt(ret_ptr != null)) + @boolToInt(err_return_tracing); - for (fn_info.param_types) |param_ty| { - if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; + { + var llvm_arg_i = @as(c_uint, @boolToInt(ret_ptr != null)) + @boolToInt(err_return_tracing); + var it = iterateParamTypes(&dg, fn_info); + while (it.next()) |lowering| switch (lowering) { + .no_bits => continue, + .byval => { + const param_ty = fn_info.param_types[it.zig_index - 1]; + const param = llvm_func.getParam(llvm_arg_i); + llvm_arg_i += 1; - const llvm_arg_i = @intCast(c_uint, args.items.len) + param_offset; - const param = llvm_func.getParam(llvm_arg_i); - // It is possible for the calling convention to make the argument's by-reference nature - // disagree with our canonical value for it, in which case we must dereference here. - const need_deref = !param_ty.isPtrAtRuntime() and !isByRef(param_ty) and - (param.typeOf().getTypeKind() == .Pointer); - const loaded_param = if (!need_deref) param else l: { - const load_inst = builder.buildLoad(param, ""); - load_inst.setAlignment(param_ty.abiAlignment(target)); - break :l load_inst; + if (isByRef(param_ty)) { + const alignment = param_ty.abiAlignment(target); + const param_llvm_ty = param.typeOf(); + const arg_ptr = buildAllocaInner(builder, llvm_func, false, param_llvm_ty); + arg_ptr.setAlignment(alignment); + const store_inst = builder.buildStore(param, arg_ptr); + store_inst.setAlignment(alignment); + try args.append(arg_ptr); + } else { + try args.append(param); + } + }, + .byref => { + const param_ty = fn_info.param_types[it.zig_index - 1]; + const param = llvm_func.getParam(llvm_arg_i); + llvm_arg_i += 1; + + if (isByRef(param_ty)) { + try args.append(param); + } else { + const alignment = param_ty.abiAlignment(target); + const load_inst = builder.buildLoad(param, ""); + load_inst.setAlignment(alignment); + try args.append(load_inst); + } + }, + .abi_sized_int => { + const param_ty = fn_info.param_types[it.zig_index - 1]; + const param = llvm_func.getParam(llvm_arg_i); + llvm_arg_i += 1; + + const param_llvm_ty = try dg.llvmType(param_ty); + const abi_size = @intCast(c_uint, param_ty.abiSize(target)); + const int_llvm_ty = dg.context.intType(abi_size * 8); + const int_ptr_llvm_ty = int_llvm_ty.pointerType(0); + const alignment = @maximum( + param_ty.abiAlignment(target), + dg.object.target_data.abiAlignmentOfType(int_llvm_ty), + ); + const arg_ptr = buildAllocaInner(builder, llvm_func, false, param_llvm_ty); + arg_ptr.setAlignment(alignment); + const casted_ptr = builder.buildBitCast(arg_ptr, int_ptr_llvm_ty, ""); + const store_inst = builder.buildStore(param, casted_ptr); + store_inst.setAlignment(alignment); + + if (isByRef(param_ty)) { + try args.append(arg_ptr); + } else { + const load_inst = builder.buildLoad(arg_ptr, ""); + load_inst.setAlignment(alignment); + try args.append(load_inst); + } + }, + .multiple_llvm_ints => { + const param_ty = fn_info.param_types[it.zig_index - 1]; + const llvm_ints = it.llvm_types_buffer[0..it.llvm_types_len]; + const is_by_ref = isByRef(param_ty); + switch (param_ty.zigTypeTag()) { + .Struct => { + const fields = param_ty.structFields().values(); + if (is_by_ref) { + const param_llvm_ty = try dg.llvmType(param_ty); + const arg_ptr = buildAllocaInner(builder, llvm_func, false, param_llvm_ty); + arg_ptr.setAlignment(param_ty.abiAlignment(target)); + + var field_i: u32 = 0; + for (llvm_ints) |int_bits| { + const param = llvm_func.getParam(llvm_arg_i); + llvm_arg_i += 1; + + const big_int_ty = dg.context.intType(int_bits); + var bits_used: u16 = 0; + while (bits_used < int_bits) { + const field = fields[field_i]; + const field_abi_bits = @intCast(u16, field.ty.abiSize(target)) * 8; + const field_int_ty = dg.context.intType(field_abi_bits); + const shifted = if (bits_used == 0) param else s: { + const shift_amt = big_int_ty.constInt(bits_used, .False); + break :s builder.buildLShr(param, shift_amt, ""); + }; + const field_as_int = builder.buildTrunc(shifted, field_int_ty, ""); + var ty_buf: Type.Payload.Pointer = undefined; + const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?; + const field_ptr = builder.buildStructGEP(arg_ptr, llvm_i, ""); + const field_alignment = field.normalAlignment(target); + const casted_ptr = builder.buildBitCast(field_ptr, field_int_ty.pointerType(0), ""); + const store_inst = builder.buildStore(field_as_int, casted_ptr); + store_inst.setAlignment(field_alignment); + + bits_used += field_abi_bits; + field_i += 1; + if (field_i >= fields.len) break; + } + if (field_i >= fields.len) break; + } + + try args.append(arg_ptr); + } else { + @panic("TODO: LLVM backend: implement C calling convention on x86_64 with byval struct parameter"); + } + }, + .Union => { + @panic("TODO: LLVM backend: implement C calling convention on x86_64 with union parameter"); + }, + else => unreachable, + } + }, }; - try args.append(loaded_param); } var di_file: ?*llvm.DIFile = null; @@ -2072,22 +2177,31 @@ pub const DeclGen = struct { } // Set parameter attributes. - var llvm_param_i: c_uint = @as(c_uint, @boolToInt(sret)) + @boolToInt(err_return_tracing); - for (fn_info.param_types) |param_ty| { - if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; - - if (isByRef(param_ty)) { - dg.addArgAttr(llvm_fn, llvm_param_i, "nonnull"); - // TODO readonly, noalias, align - } - llvm_param_i += 1; - } - // TODO: more attributes. see codegen.cpp `make_fn_llvm_value`. - if (fn_info.cc == .Naked) { - dg.addFnAttr(llvm_fn, "naked"); - } else { - llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target)); + switch (fn_info.cc) { + .Unspecified, .Inline => { + var llvm_param_i: c_uint = @as(c_uint, @boolToInt(sret)) + @boolToInt(err_return_tracing); + for (fn_info.param_types) |param_ty| { + if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; + + if (isByRef(param_ty)) { + dg.addArgAttr(llvm_fn, llvm_param_i, "nonnull"); + // TODO readonly, noalias, align + } + llvm_param_i += 1; + } + llvm_fn.setFunctionCallConv(.Fast); + }, + .Naked => { + dg.addFnAttr(llvm_fn, "naked"); + }, + .Async => { + llvm_fn.setFunctionCallConv(.Fast); + @panic("TODO: LLVM backend lower async function"); + }, + else => { + llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target)); + }, } if (fn_info.alignment != 0) { @@ -2518,42 +2632,7 @@ pub const DeclGen = struct { llvm_union_ty.structSetBody(&llvm_fields, llvm_fields_len, .False); return llvm_union_ty; }, - .Fn => { - const fn_info = t.fnInfo(); - const llvm_ret_ty = try lowerFnRetTy(dg, fn_info); - - var llvm_params = std.ArrayList(*const llvm.Type).init(dg.gpa); - defer llvm_params.deinit(); - - if (firstParamSRet(fn_info, target)) { - const llvm_sret_ty = try dg.llvmType(fn_info.return_type); - try llvm_params.append(llvm_sret_ty.pointerType(0)); - } - - if (fn_info.return_type.isError() and - dg.module.comp.bin_file.options.error_return_tracing) - { - var ptr_ty_payload: Type.Payload.ElemType = .{ - .base = .{ .tag = .single_mut_pointer }, - .data = dg.object.getStackTraceType(), - }; - const ptr_ty = Type.initPayload(&ptr_ty_payload.base); - try llvm_params.append(try lowerFnParamTy(dg, fn_info.cc, ptr_ty)); - } - - for (fn_info.param_types) |param_ty| { - if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; - - try llvm_params.append(try lowerFnParamTy(dg, fn_info.cc, param_ty)); - } - - return llvm.functionType( - llvm_ret_ty, - llvm_params.items.ptr, - @intCast(c_uint, llvm_params.items.len), - llvm.Bool.fromBool(fn_info.is_var_args), - ); - }, + .Fn => return llvmTypeFn(dg, t), .ComptimeInt => unreachable, .ComptimeFloat => unreachable, .Type => unreachable, @@ -2568,6 +2647,63 @@ pub const DeclGen = struct { } } + fn llvmTypeFn(dg: *DeclGen, fn_ty: Type) Allocator.Error!*const llvm.Type { + const target = dg.module.getTarget(); + const fn_info = fn_ty.fnInfo(); + const llvm_ret_ty = try lowerFnRetTy(dg, fn_info); + + var llvm_params = std.ArrayList(*const llvm.Type).init(dg.gpa); + defer llvm_params.deinit(); + + if (firstParamSRet(fn_info, target)) { + const llvm_sret_ty = try dg.llvmType(fn_info.return_type); + try llvm_params.append(llvm_sret_ty.pointerType(0)); + } + + if (fn_info.return_type.isError() and + dg.module.comp.bin_file.options.error_return_tracing) + { + var ptr_ty_payload: Type.Payload.ElemType = .{ + .base = .{ .tag = .single_mut_pointer }, + .data = dg.object.getStackTraceType(), + }; + const ptr_ty = Type.initPayload(&ptr_ty_payload.base); + try llvm_params.append(try dg.llvmType(ptr_ty)); + } + + var it = iterateParamTypes(dg, fn_info); + while (it.next()) |lowering| switch (lowering) { + .no_bits => continue, + .byval => { + const param_ty = fn_info.param_types[it.zig_index - 1]; + try llvm_params.append(try dg.llvmType(param_ty)); + }, + .byref => { + const param_ty = fn_info.param_types[it.zig_index - 1]; + const raw_llvm_ty = try dg.llvmType(param_ty); + try llvm_params.append(raw_llvm_ty.pointerType(0)); + }, + .abi_sized_int => { + const param_ty = fn_info.param_types[it.zig_index - 1]; + const abi_size = @intCast(c_uint, param_ty.abiSize(target)); + try llvm_params.append(dg.context.intType(abi_size * 8)); + }, + .multiple_llvm_ints => { + try llvm_params.ensureUnusedCapacity(it.llvm_types_len); + for (it.llvm_types_buffer[0..it.llvm_types_len]) |int_bits| { + llvm_params.appendAssumeCapacity(dg.context.intType(int_bits)); + } + }, + }; + + return llvm.functionType( + llvm_ret_ty, + llvm_params.items.ptr, + @intCast(c_uint, llvm_params.items.len), + llvm.Bool.fromBool(fn_info.is_var_args), + ); + } + fn genTypedValue(dg: *DeclGen, tv: TypedValue) Error!*const llvm.Value { if (tv.val.isUndef()) { const llvm_type = try dg.llvmType(tv.ty); @@ -3848,52 +3984,138 @@ pub const FuncGen = struct { try llvm_args.append(self.err_ret_trace.?); } - for (args) |arg| { - const param_ty = self.air.typeOf(arg); - if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; - - const llvm_arg = try self.resolveInst(arg); - const abi_llvm_ty = try lowerFnParamTy(self.dg, fn_info.cc, param_ty); - const param_llvm_ty = llvm_arg.typeOf(); - if (abi_llvm_ty == param_llvm_ty) { - try llvm_args.append(llvm_arg); - continue; - } - - // In this case the function param type is honoring the calling convention - // by having a different LLVM type than the usual one. We solve this here - // at the callsite by bitcasting a pointer to our canonical type, then - // loading it if necessary. - const alignment = param_ty.abiAlignment(target); - const ptr_abi_ty = abi_llvm_ty.pointerType(0); - - const casted_ptr = if (isByRef(param_ty)) - self.builder.buildBitCast(llvm_arg, ptr_abi_ty, "") - else p: { - const arg_ptr = self.buildAlloca(param_llvm_ty); - arg_ptr.setAlignment(alignment); - const store_inst = self.builder.buildStore(llvm_arg, arg_ptr); - store_inst.setAlignment(alignment); - - if (abi_llvm_ty.getTypeKind() == .Pointer) { - // In this case, the calling convention wants a pointer, but - // we have a value. - if (arg_ptr.typeOf() == abi_llvm_ty) { - try llvm_args.append(arg_ptr); - continue; + var it = iterateParamTypes(self.dg, fn_info); + while (it.next()) |lowering| switch (lowering) { + .no_bits => continue, + .byval => { + const arg = args[it.zig_index - 1]; + const param_ty = self.air.typeOf(arg); + const llvm_arg = try self.resolveInst(arg); + if (isByRef(param_ty)) { + const alignment = param_ty.abiAlignment(target); + const load_inst = self.builder.buildLoad(llvm_arg, ""); + load_inst.setAlignment(alignment); + try llvm_args.append(load_inst); + } else { + if (param_ty.zigTypeTag() == .Pointer) { + // We need a bitcast in case of two possibilities: + // 1. The parameter type is a pointer to zero-sized type, + // which is always lowered to an LLVM type of `*i8`. + // 2. The argument is a global which does act as a pointer, however + // a bitcast is needed in order for the LLVM types to match. + const llvm_param_ty = try self.dg.llvmType(param_ty); + const casted_ptr = self.builder.buildBitCast(llvm_arg, llvm_param_ty, ""); + try llvm_args.append(casted_ptr); + } else { + try llvm_args.append(llvm_arg); } - const casted_ptr = self.builder.buildBitCast(arg_ptr, abi_llvm_ty, ""); - try llvm_args.append(casted_ptr); - continue; } + }, + .byref => { + const arg = args[it.zig_index - 1]; + const param_ty = self.air.typeOf(arg); + const llvm_arg = try self.resolveInst(arg); + if (isByRef(param_ty)) { + try llvm_args.append(llvm_arg); + } else { + const alignment = param_ty.abiAlignment(target); + const param_llvm_ty = llvm_arg.typeOf(); + const arg_ptr = self.buildAlloca(param_llvm_ty); + arg_ptr.setAlignment(alignment); + const store_inst = self.builder.buildStore(llvm_arg, arg_ptr); + store_inst.setAlignment(alignment); + try llvm_args.append(arg_ptr); + } + }, + .abi_sized_int => { + const arg = args[it.zig_index - 1]; + const param_ty = self.air.typeOf(arg); + const llvm_arg = try self.resolveInst(arg); + const abi_size = @intCast(c_uint, param_ty.abiSize(target)); + const int_llvm_ty = self.dg.context.intType(abi_size * 8); + const int_ptr_llvm_ty = int_llvm_ty.pointerType(0); - break :p self.builder.buildBitCast(arg_ptr, ptr_abi_ty, ""); - }; + if (isByRef(param_ty)) { + const alignment = param_ty.abiAlignment(target); + const casted_ptr = self.builder.buildBitCast(llvm_arg, int_ptr_llvm_ty, ""); + const load_inst = self.builder.buildLoad(casted_ptr, ""); + load_inst.setAlignment(alignment); + try llvm_args.append(load_inst); + } else { + // LLVM does not allow bitcasting structs so we must allocate + // a local, bitcast its pointer, store, and then load. + const alignment = @maximum( + param_ty.abiAlignment(target), + self.dg.object.target_data.abiAlignmentOfType(int_llvm_ty), + ); + const int_ptr = self.buildAlloca(int_llvm_ty); + int_ptr.setAlignment(alignment); + const param_llvm_ty = try self.dg.llvmType(param_ty); + const casted_ptr = self.builder.buildBitCast(int_ptr, param_llvm_ty.pointerType(0), ""); + const store_inst = self.builder.buildStore(llvm_arg, casted_ptr); + store_inst.setAlignment(alignment); + const load_inst = self.builder.buildLoad(int_ptr, ""); + load_inst.setAlignment(alignment); + try llvm_args.append(load_inst); + } + }, + .multiple_llvm_ints => { + const arg = args[it.zig_index - 1]; + const param_ty = self.air.typeOf(arg); + const llvm_ints = it.llvm_types_buffer[0..it.llvm_types_len]; + const llvm_arg = try self.resolveInst(arg); + const is_by_ref = isByRef(param_ty); + try llvm_args.ensureUnusedCapacity(it.llvm_types_len); + switch (param_ty.zigTypeTag()) { + .Struct => { + const fields = param_ty.structFields().values(); + var field_i: u32 = 0; + for (llvm_ints) |int_bits| { + const big_int_ty = self.dg.context.intType(int_bits); + var int_arg: *const llvm.Value = undefined; + var bits_used: u16 = 0; + while (bits_used < int_bits) { + const field = fields[field_i]; + var ty_buf: Type.Payload.Pointer = undefined; + const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?; + const field_abi_bits = @intCast(u16, field.ty.abiSize(target)) * 8; + const field_int_ty = self.dg.context.intType(field_abi_bits); + const llvm_field = if (is_by_ref) f: { + const field_ptr = self.builder.buildStructGEP(llvm_arg, llvm_i, ""); + const alignment = field.normalAlignment(target); + const casted_ptr = self.builder.buildBitCast(field_ptr, field_int_ty.pointerType(0), ""); + const load_inst = self.builder.buildLoad(casted_ptr, ""); + load_inst.setAlignment(alignment); + break :f load_inst; + } else f: { + const llvm_field = self.builder.buildExtractValue(llvm_arg, llvm_i, ""); + break :f self.builder.buildBitCast(llvm_field, field_int_ty, ""); + }; - const load_inst = self.builder.buildLoad(casted_ptr, ""); - load_inst.setAlignment(alignment); - try llvm_args.append(load_inst); - } + const extended = self.builder.buildZExt(llvm_field, big_int_ty, ""); + if (bits_used == 0) { + int_arg = extended; + } else { + const shift_amt = big_int_ty.constInt(bits_used, .False); + const shifted = self.builder.buildShl(extended, shift_amt, ""); + int_arg = self.builder.buildOr(int_arg, shifted, ""); + } + + bits_used += field_abi_bits; + field_i += 1; + if (field_i >= fields.len) break; + } + llvm_args.appendAssumeCapacity(int_arg); + if (field_i >= fields.len) break; + } + }, + .Union => { + return self.todo("airCall C calling convention on x86_64 with union argument ", .{}); + }, + else => unreachable, + } + }, + }; const call = self.builder.buildCall( llvm_fn, @@ -6489,24 +6711,7 @@ pub const FuncGen = struct { /// Use this instead of builder.buildAlloca, because this function makes sure to /// put the alloca instruction at the top of the function! fn buildAlloca(self: *FuncGen, llvm_ty: *const llvm.Type) *const llvm.Value { - const prev_block = self.builder.getInsertBlock(); - const prev_debug_location = self.builder.getCurrentDebugLocation2(); - defer { - self.builder.positionBuilderAtEnd(prev_block); - if (self.di_scope != null) { - self.builder.setCurrentDebugLocation2(prev_debug_location); - } - } - - const entry_block = self.llvm_func.getFirstBasicBlock().?; - if (entry_block.getFirstInstruction()) |first_inst| { - self.builder.positionBuilder(entry_block, first_inst); - } else { - self.builder.positionBuilderAtEnd(entry_block); - } - self.builder.clearCurrentDebugLocation(); - - return self.builder.buildAlloca(llvm_ty, ""); + return buildAllocaInner(self.builder, self.llvm_func, self.di_scope != null, llvm_ty); } fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { @@ -8296,99 +8501,171 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. } } -fn lowerFnParamTy(dg: *DeclGen, cc: std.builtin.CallingConvention, ty: Type) !*const llvm.Type { - assert(ty.hasRuntimeBitsIgnoreComptime()); - const target = dg.module.getTarget(); - switch (cc) { - .Unspecified, .Inline => { - const raw_llvm_ty = try dg.llvmType(ty); - if (isByRef(ty)) { - return raw_llvm_ty.pointerType(0); - } else { - return raw_llvm_ty; - } - }, - .C => { - const is_scalar = switch (ty.zigTypeTag()) { - .Void, - .Bool, - .NoReturn, - .Int, - .Float, - .Pointer, - .Optional, - .ErrorSet, - .Enum, - .AnyFrame, - .Vector, - => true, +const ParamTypeIterator = struct { + dg: *DeclGen, + fn_info: Type.Payload.Function.Data, + zig_index: u32, + llvm_index: u32, + target: std.Target, + llvm_types_len: u32, + llvm_types_buffer: [8]u16, - else => false, - }; - switch (target.cpu.arch) { - .mips, .mipsel => return dg.llvmType(ty), - .x86_64 => switch (target.os.tag) { - .windows => switch (x86_64_abi.classifyWindows(ty, target)) { - .integer => { - if (is_scalar) { - return dg.llvmType(ty); - } else { - const abi_size = ty.abiSize(target); - return dg.context.intType(@intCast(c_uint, abi_size * 8)); - } + const Lowering = enum { + no_bits, + byval, + byref, + abi_sized_int, + multiple_llvm_ints, + }; + + fn next(it: *ParamTypeIterator) ?Lowering { + if (it.zig_index >= it.fn_info.param_types.len) return null; + + const ty = it.fn_info.param_types[it.zig_index]; + if (!ty.hasRuntimeBitsIgnoreComptime()) { + it.zig_index += 1; + return .no_bits; + } + + switch (it.fn_info.cc) { + .Unspecified, .Inline => { + it.zig_index += 1; + it.llvm_index += 1; + if (isByRef(ty)) { + return .byref; + } else { + return .byval; + } + }, + .Async => { + @panic("TODO implement async function lowering in the LLVM backend"); + }, + .C => { + const is_scalar = switch (ty.zigTypeTag()) { + .Void, + .Bool, + .NoReturn, + .Int, + .Float, + .Pointer, + .Optional, + .ErrorSet, + .Enum, + .AnyFrame, + .Vector, + => true, + + else => false, + }; + switch (it.target.cpu.arch) { + .mips, .mipsel => { + it.zig_index += 1; + it.llvm_index += 1; + return .byval; + }, + .x86_64 => switch (it.target.os.tag) { + .windows => switch (x86_64_abi.classifyWindows(ty, it.target)) { + .integer => { + if (is_scalar) { + it.zig_index += 1; + it.llvm_index += 1; + return .byval; + } else { + it.zig_index += 1; + it.llvm_index += 1; + return .abi_sized_int; + } + }, + .memory => { + it.zig_index += 1; + it.llvm_index += 1; + return .byref; + }, + .sse => { + it.zig_index += 1; + it.llvm_index += 1; + return .byval; + }, + else => unreachable, }, - .memory => return (try dg.llvmType(ty)).pointerType(0), - .sse => return dg.llvmType(ty), - else => unreachable, - }, - else => { - if (is_scalar) { - return dg.llvmType(ty); - } - const classes = x86_64_abi.classifySystemV(ty, target); - if (classes[0] == .memory) { - return (try dg.llvmType(ty)).pointerType(0); - } - var llvm_types_buffer: [8]*const llvm.Type = undefined; - var llvm_types_index: u32 = 0; - for (classes) |class| { - switch (class) { - .integer => { - llvm_types_buffer[llvm_types_index] = dg.context.intType(64); - llvm_types_index += 1; - }, - .sse => { - @panic("TODO"); - }, - .sseup => { - @panic("TODO"); - }, - .x87 => { - @panic("TODO"); - }, - .x87up => { - @panic("TODO"); - }, - .complex_x87 => { - @panic("TODO"); - }, - .memory => unreachable, // handled above - .none => break, + else => { + if (is_scalar) { + it.zig_index += 1; + it.llvm_index += 1; + return .byval; } - } - if (classes[0] == .integer and classes[1] == .none) { - const abi_size = ty.abiSize(target); - return dg.context.intType(@intCast(c_uint, abi_size * 8)); - } - return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False); + const classes = x86_64_abi.classifySystemV(ty, it.target); + if (classes[0] == .memory) { + it.zig_index += 1; + it.llvm_index += 1; + return .byref; + } + var llvm_types_buffer: [8]u16 = undefined; + var llvm_types_index: u32 = 0; + for (classes) |class| { + switch (class) { + .integer => { + llvm_types_buffer[llvm_types_index] = 64; + llvm_types_index += 1; + }, + .sse => { + @panic("TODO"); + }, + .sseup => { + @panic("TODO"); + }, + .x87 => { + @panic("TODO"); + }, + .x87up => { + @panic("TODO"); + }, + .complex_x87 => { + @panic("TODO"); + }, + .memory => unreachable, // handled above + .none => break, + } + } + if (classes[0] == .integer and classes[1] == .none) { + it.zig_index += 1; + it.llvm_index += 1; + return .abi_sized_int; + } + it.llvm_types_buffer = llvm_types_buffer; + it.llvm_types_len = llvm_types_index; + it.llvm_index += llvm_types_index; + it.zig_index += 1; + return .multiple_llvm_ints; + }, }, - }, - // TODO investigate C ABI for other architectures - else => return dg.llvmType(ty), - } - }, - else => return dg.llvmType(ty), + // TODO investigate C ABI for other architectures + else => { + it.zig_index += 1; + it.llvm_index += 1; + return .byval; + }, + } + }, + else => { + it.zig_index += 1; + it.llvm_index += 1; + return .byval; + }, + } } +}; + +fn iterateParamTypes(dg: *DeclGen, fn_info: Type.Payload.Function.Data) ParamTypeIterator { + return .{ + .dg = dg, + .fn_info = fn_info, + .zig_index = 0, + .llvm_index = 0, + .target = dg.module.getTarget(), + .llvm_types_buffer = undefined, + .llvm_types_len = 0, + }; } fn isByRef(ty: Type) bool { @@ -8549,3 +8826,29 @@ fn compilerRtIntBits(bits: u16) u16 { } return bits; } + +fn buildAllocaInner( + builder: *const llvm.Builder, + llvm_func: *const llvm.Value, + di_scope_non_null: bool, + llvm_ty: *const llvm.Type, +) *const llvm.Value { + const prev_block = builder.getInsertBlock(); + const prev_debug_location = builder.getCurrentDebugLocation2(); + defer { + builder.positionBuilderAtEnd(prev_block); + if (di_scope_non_null) { + builder.setCurrentDebugLocation2(prev_debug_location); + } + } + + const entry_block = llvm_func.getFirstBasicBlock().?; + if (entry_block.getFirstInstruction()) |first_inst| { + builder.positionBuilder(entry_block, first_inst); + } else { + builder.positionBuilderAtEnd(entry_block); + } + builder.clearCurrentDebugLocation(); + + return builder.buildAlloca(llvm_ty, ""); +} diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index f3987a8665..425358865f 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -1019,6 +1019,9 @@ pub const TargetMachine = opaque { pub const TargetData = opaque { pub const dispose = LLVMDisposeTargetData; extern fn LLVMDisposeTargetData(*const TargetData) void; + + pub const abiAlignmentOfType = LLVMABIAlignmentOfType; + extern fn LLVMABIAlignmentOfType(TD: *const TargetData, Ty: *const Type) c_uint; }; pub const CodeModel = enum(c_int) { From 800edb03b5dca7998941eaba016d57a5512bf337 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 May 2022 21:18:17 +0200 Subject: [PATCH 1598/2031] regalloc: test allocating from multiple register claases --- src/register_manager.zig | 173 ++++++++++++++++++++++++++------------- 1 file changed, 118 insertions(+), 55 deletions(-) diff --git a/src/register_manager.zig b/src/register_manager.zig index 347c916769..98ee8c9ab3 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -691,6 +691,14 @@ const MockRegister1 = enum(u2) { } const allocatable_registers = [_]MockRegister1{ .r2, .r3 }; + + const RM = RegisterManager( + MockFunction1, + MockRegister1, + &MockRegister1.allocatable_registers, + ); + + const gp: RM.RegisterBitSet = std.math.maxInt(RM.RegisterBitSet); }; const MockRegister2 = enum(u2) { @@ -704,20 +712,62 @@ const MockRegister2 = enum(u2) { } const allocatable_registers = [_]MockRegister2{ .r0, .r1, .r2, .r3 }; + + const RM = RegisterManager( + MockFunction2, + MockRegister2, + &MockRegister2.allocatable_registers, + ); + + const gp: RM.RegisterBitSet = std.math.maxInt(RM.RegisterBitSet); +}; + +const MockRegister3 = enum(u3) { + r0, + r1, + r2, + r3, + x0, + x1, + x2, + x3, + + pub fn id(reg: MockRegister3) u3 { + return switch (@enumToInt(reg)) { + 0...3 => @as(u3, @truncate(u2, @enumToInt(reg))), + 4...7 => @enumToInt(reg), + }; + } + + pub fn enc(reg: MockRegister3) u2 { + return @truncate(u2, @enumToInt(reg)); + } + + const gp_regs = [_]MockRegister3{ .r0, .r1, .r2, .r3 }; + const ext_regs = [_]MockRegister3{ .x0, .x1, .x2, .x3 }; + const allocatable_registers = gp_regs ++ ext_regs; + + const RM = RegisterManager( + MockFunction3, + MockRegister3, + &MockRegister3.allocatable_registers, + ); + + const gp: RM.RegisterBitSet = @as(RM.RegisterBitSet, std.math.maxInt(std.meta.Int( + .unsigned, + gp_regs.len, + ))); + const ext: RM.RegisterBitSet = std.math.maxInt(RM.RegisterBitSet) - gp; }; fn MockFunction(comptime Register: type) type { return struct { allocator: Allocator, - register_manager: RegisterManagerT = .{}, + register_manager: Register.RM = .{}, spilled: std.ArrayListUnmanaged(Register) = .{}, const Self = @This(); - const RegisterManagerT = RegisterManager(Self, Register, &Register.allocatable_registers); - - pub const reg_class: RegisterManagerT.RegisterBitSet = math.maxInt(RegisterManagerT.RegisterBitSet); - pub fn deinit(self: *Self) void { self.spilled.deinit(self.allocator); } @@ -738,6 +788,7 @@ fn MockFunction(comptime Register: type) type { const MockFunction1 = MockFunction(MockRegister1); const MockFunction2 = MockFunction(MockRegister2); +const MockFunction3 = MockFunction(MockRegister3); test "default state" { const allocator = std.testing.allocator; @@ -762,20 +813,11 @@ test "tryAllocReg: no spilling" { defer function.deinit(); const mock_instruction: Air.Inst.Index = 1; - const reg_class = MockFunction1.reg_class; + const gp = MockRegister1.gp; - try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg( - mock_instruction, - reg_class, - )); - try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg( - mock_instruction, - reg_class, - )); - try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg( - mock_instruction, - reg_class, - )); + try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(mock_instruction, gp)); + try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(mock_instruction, gp)); + try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(mock_instruction, gp)); try expect(function.register_manager.isRegAllocated(.r2)); try expect(function.register_manager.isRegAllocated(.r3)); @@ -800,30 +842,18 @@ test "allocReg: spilling" { defer function.deinit(); const mock_instruction: Air.Inst.Index = 1; - const reg_class = MockFunction1.reg_class; + const gp = MockRegister1.gp; - try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg( - mock_instruction, - reg_class, - )); - try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg( - mock_instruction, - reg_class, - )); + try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction, gp)); + try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, gp)); // Spill a register - try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg( - mock_instruction, - reg_class, - )); + try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction, gp)); try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); // No spilling necessary function.register_manager.freeReg(.r3); - try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg( - mock_instruction, - reg_class, - )); + try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, gp)); try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); // Locked registers @@ -832,10 +862,7 @@ test "allocReg: spilling" { const lock = function.register_manager.lockReg(.r2); defer if (lock) |reg| function.register_manager.unlockReg(reg); - try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg( - mock_instruction, - reg_class, - )); + try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, gp)); } try expect(!function.register_manager.lockedRegsExist()); } @@ -848,13 +875,13 @@ test "tryAllocRegs" { }; defer function.deinit(); - const reg_class = MockFunction2.reg_class; + const gp = MockRegister2.gp; - try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs( - 3, - .{ null, null, null }, - reg_class, - ).?); + try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ + null, + null, + null, + }, gp).?); try expect(function.register_manager.isRegAllocated(.r0)); try expect(function.register_manager.isRegAllocated(.r1)); @@ -869,11 +896,11 @@ test "tryAllocRegs" { const lock = function.register_manager.lockReg(.r1); defer if (lock) |reg| function.register_manager.unlockReg(reg); - try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs( - 3, - .{ null, null, null }, - reg_class, - ).?); + try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ + null, + null, + null, + }, gp).?); } try expect(!function.register_manager.lockedRegsExist()); @@ -893,7 +920,7 @@ test "allocRegs: normal usage" { }; defer function.deinit(); - const reg_class = MockFunction2.reg_class; + const gp = MockRegister2.gp; { const result_reg: MockRegister2 = .r1; @@ -914,7 +941,7 @@ test "allocRegs: normal usage" { const lock = function.register_manager.lockReg(result_reg); defer if (lock) |reg| function.register_manager.unlockReg(reg); - const regs = try function.register_manager.allocRegs(2, .{ null, null }, reg_class); + const regs = try function.register_manager.allocRegs(2, .{ null, null }, gp); try function.genAdd(result_reg, regs[0], regs[1]); } } @@ -929,7 +956,7 @@ test "allocRegs: selectively reducing register pressure" { }; defer function.deinit(); - const reg_class = MockFunction2.reg_class; + const gp = MockRegister2.gp; { const result_reg: MockRegister2 = .r1; @@ -938,12 +965,12 @@ test "allocRegs: selectively reducing register pressure" { // Here, we don't defer unlock because we manually unlock // after genAdd - const regs = try function.register_manager.allocRegs(2, .{ null, null }, reg_class); + const regs = try function.register_manager.allocRegs(2, .{ null, null }, gp); try function.genAdd(result_reg, regs[0], regs[1]); function.register_manager.unlockReg(lock.?); - const extra_summand_reg = try function.register_manager.allocReg(null, reg_class); + const extra_summand_reg = try function.register_manager.allocReg(null, gp); try function.genAdd(result_reg, result_reg, extra_summand_reg); } } @@ -974,3 +1001,39 @@ test "getReg" { try expect(!function.register_manager.isRegFree(.r3)); try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r3}, function.spilled.items); } + +test "allocReg with multiple, non-overlapping register classes" { + const allocator = std.testing.allocator; + + var function = MockFunction3{ + .allocator = allocator, + }; + defer function.deinit(); + + const gp = MockRegister3.gp; + const ext = MockRegister3.ext; + + const gp_reg = try function.register_manager.allocReg(null, gp); + + try expect(function.register_manager.isRegAllocated(.r0)); + try expect(!function.register_manager.isRegAllocated(.x0)); + + const ext_reg = try function.register_manager.allocReg(null, ext); + + try expect(function.register_manager.isRegAllocated(.r0)); + try expect(!function.register_manager.isRegAllocated(.r1)); + try expect(function.register_manager.isRegAllocated(.x0)); + try expect(!function.register_manager.isRegAllocated(.x1)); + try expect(gp_reg.enc() == ext_reg.enc()); + + const ext_lock = function.register_manager.lockRegAssumeUnused(ext_reg); + defer function.register_manager.unlockReg(ext_lock); + + const ext_reg2 = try function.register_manager.allocReg(null, ext); + + try expect(function.register_manager.isRegAllocated(.r0)); + try expect(function.register_manager.isRegAllocated(.x0)); + try expect(!function.register_manager.isRegAllocated(.r1)); + try expect(function.register_manager.isRegAllocated(.x1)); + try expect(ext_reg2.enc() == MockRegister3.r1.enc()); +} From f31f86a86a482267a524cdca59fb2b940ecadf25 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 20 May 2022 23:52:01 -0700 Subject: [PATCH 1599/2031] LLVM: fix calling convention lowering involving pointers The previous commit caused LLVM module verification failure because we attemped to bitcast LLVM pointers to i64 parameters. This is exactly what we want, however it's technically not allowed according to LLVM's type system. It could have been fixed trivially by using ptrtoint instead of bitcast in the case of pointers, however, out of concern for inttoptr being problematic for the optimizer, I put in special code to detect when a given parameter can be treated as its actual type rather than an integer type. This makes Zig's output LLVM IR closer to what Clang outputs. --- src/codegen/llvm.zig | 115 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 104 insertions(+), 11 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index aa10cd4fd4..b9c8e5437f 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -764,15 +764,25 @@ pub const Object = struct { arg_ptr.setAlignment(param_ty.abiAlignment(target)); var field_i: u32 = 0; + var field_offset: u32 = 0; for (llvm_ints) |int_bits| { const param = llvm_func.getParam(llvm_arg_i); llvm_arg_i += 1; const big_int_ty = dg.context.intType(int_bits); - var bits_used: u16 = 0; + var bits_used: u32 = 0; while (bits_used < int_bits) { const field = fields[field_i]; - const field_abi_bits = @intCast(u16, field.ty.abiSize(target)) * 8; + const field_alignment = field.normalAlignment(target); + const prev_offset = field_offset; + field_offset = std.mem.alignForwardGeneric(u32, field_offset, field_alignment); + if (field_offset > prev_offset) { + // Padding counts as bits used. + bits_used += (field_offset - prev_offset) * 8; + if (bits_used >= int_bits) break; + } + const field_size = @intCast(u16, field.ty.abiSize(target)); + const field_abi_bits = field_size * 8; const field_int_ty = dg.context.intType(field_abi_bits); const shifted = if (bits_used == 0) param else s: { const shift_amt = big_int_ty.constInt(bits_used, .False); @@ -782,14 +792,15 @@ pub const Object = struct { var ty_buf: Type.Payload.Pointer = undefined; const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?; const field_ptr = builder.buildStructGEP(arg_ptr, llvm_i, ""); - const field_alignment = field.normalAlignment(target); const casted_ptr = builder.buildBitCast(field_ptr, field_int_ty.pointerType(0), ""); const store_inst = builder.buildStore(field_as_int, casted_ptr); store_inst.setAlignment(field_alignment); - bits_used += field_abi_bits; field_i += 1; if (field_i >= fields.len) break; + + bits_used += field_abi_bits; + field_offset += field_size; } if (field_i >= fields.len) break; } @@ -2689,9 +2700,65 @@ pub const DeclGen = struct { try llvm_params.append(dg.context.intType(abi_size * 8)); }, .multiple_llvm_ints => { + const param_ty = fn_info.param_types[it.zig_index - 1]; + const llvm_ints = it.llvm_types_buffer[0..it.llvm_types_len]; try llvm_params.ensureUnusedCapacity(it.llvm_types_len); - for (it.llvm_types_buffer[0..it.llvm_types_len]) |int_bits| { - llvm_params.appendAssumeCapacity(dg.context.intType(int_bits)); + + // The reason we have all this logic instead of simply appending + // big_int_ty is for the special case of a pointer type; + // we want to use a pointer type instead of inttoptr at the callsites, + // which may prevent optimization. + switch (param_ty.zigTypeTag()) { + .Struct => { + const fields = param_ty.structFields().values(); + var field_i: u32 = 0; + var field_offset: u32 = 0; + llvm_arg: for (llvm_ints) |int_bits| { + const big_int_ty = dg.context.intType(int_bits); + var bits_used: u32 = 0; + while (bits_used < int_bits) { + const field = fields[field_i]; + const field_alignment = field.normalAlignment(target); + const prev_offset = field_offset; + field_offset = std.mem.alignForwardGeneric(u32, field_offset, field_alignment); + if (field_offset > prev_offset) { + // Padding counts as bits used. + bits_used += (field_offset - prev_offset) * 8; + if (bits_used >= int_bits) break; + } + const field_size = @intCast(u16, field.ty.abiSize(target)); + const field_abi_bits = field_size * 8; + + // Special case for when the entire LLVM integer represents + // one field; in this case keep the type information + // to avoid the potentially costly ptrtoint/bitcast. + if (bits_used == 0 and field_abi_bits == int_bits) { + const llvm_field_ty = try dg.llvmType(field.ty); + llvm_params.appendAssumeCapacity(llvm_field_ty); + field_i += 1; + if (field_i >= fields.len) { + break :llvm_arg; + } else { + continue :llvm_arg; + } + } + + field_i += 1; + if (field_i >= fields.len) break; + + bits_used += field_abi_bits; + field_offset += field_size; + } + llvm_params.appendAssumeCapacity(big_int_ty); + if (field_i >= fields.len) break; + } + }, + else => { + for (llvm_ints) |int_bits| { + const big_int_ty = dg.context.intType(int_bits); + llvm_params.appendAssumeCapacity(big_int_ty); + } + }, } }, }; @@ -4070,22 +4137,46 @@ pub const FuncGen = struct { .Struct => { const fields = param_ty.structFields().values(); var field_i: u32 = 0; + var field_offset: u32 = 0; for (llvm_ints) |int_bits| { const big_int_ty = self.dg.context.intType(int_bits); var int_arg: *const llvm.Value = undefined; - var bits_used: u16 = 0; + var bits_used: u32 = 0; while (bits_used < int_bits) { const field = fields[field_i]; + const field_alignment = field.normalAlignment(target); + const prev_offset = field_offset; + field_offset = std.mem.alignForwardGeneric(u32, field_offset, field_alignment); + if (field_offset > prev_offset) { + // Padding counts as bits used. + bits_used += (field_offset - prev_offset) * 8; + if (bits_used >= int_bits) break; + } var ty_buf: Type.Payload.Pointer = undefined; const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?; - const field_abi_bits = @intCast(u16, field.ty.abiSize(target)) * 8; + const field_size = @intCast(u16, field.ty.abiSize(target)); + const field_abi_bits = field_size * 8; + + // Special case for when the entire LLVM integer represents + // one field; in this case keep the type information + // to avoid the potentially costly ptrtoint/bitcast. + if (bits_used == 0 and field_abi_bits == int_bits) { + int_arg = if (is_by_ref) f: { + const field_ptr = self.builder.buildStructGEP(llvm_arg, llvm_i, ""); + const load_inst = self.builder.buildLoad(field_ptr, ""); + load_inst.setAlignment(field_alignment); + break :f load_inst; + } else self.builder.buildExtractValue(llvm_arg, llvm_i, ""); + field_i += 1; + break; + } + const field_int_ty = self.dg.context.intType(field_abi_bits); const llvm_field = if (is_by_ref) f: { const field_ptr = self.builder.buildStructGEP(llvm_arg, llvm_i, ""); - const alignment = field.normalAlignment(target); const casted_ptr = self.builder.buildBitCast(field_ptr, field_int_ty.pointerType(0), ""); const load_inst = self.builder.buildLoad(casted_ptr, ""); - load_inst.setAlignment(alignment); + load_inst.setAlignment(field_alignment); break :f load_inst; } else f: { const llvm_field = self.builder.buildExtractValue(llvm_arg, llvm_i, ""); @@ -4101,9 +4192,11 @@ pub const FuncGen = struct { int_arg = self.builder.buildOr(int_arg, shifted, ""); } - bits_used += field_abi_bits; field_i += 1; if (field_i >= fields.len) break; + + bits_used += field_abi_bits; + field_offset += field_size; } llvm_args.appendAssumeCapacity(int_arg); if (field_i >= fields.len) break; From 9747303d16dfca61316a292d1e05ac901191e3a3 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 21 May 2022 19:06:05 +0200 Subject: [PATCH 1600/2031] stage2 ARM: Introduce MCValue.cpsr_flags MCValue.cpsr_flags replaces MCValue.compare_flags_{signed,unsigned}. This simplifies a lot of stuff and enables an MCValue to represent only the overflow bits in the CPU (previously, it was only possible to represent a register + the overflow bits). --- src/arch/arm/CodeGen.zig | 205 ++++++++++++++++----------------------- 1 file changed, 83 insertions(+), 122 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 4f121dd56e..095d572f53 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -96,7 +96,7 @@ register_manager: RegisterManager = .{}, /// Maps offset to what is stored there. stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, /// Tracks the current instruction allocated to the compare flags -compare_flags_inst: ?Air.Inst.Index = null, +cpsr_flags_inst: ?Air.Inst.Index = null, /// Offset from the stack base, representing the end of the stack frame. max_end_stack: u32 = 0, @@ -159,12 +159,11 @@ const MCValue = union(enum) { /// The value is a pointer to one of the stack variables (payload /// is stack offset). ptr_stack_offset: u32, - /// The value is in the compare flags assuming an unsigned - /// operation, with this operator applied on top of it. - compare_flags_unsigned: math.CompareOperator, - /// The value is in the compare flags assuming a signed operation, - /// with this operator applied on top of it. - compare_flags_signed: math.CompareOperator, + /// The value resides in the N, Z, C, V flags of the Current + /// Program Status Register (CPSR). The value is 1 (if the type is + /// u1) or true (if the type in bool) iff the specified condition + /// is true. + cpsr_flags: Condition, /// The value is a function argument passed via the stack. stack_argument_offset: u32, @@ -190,8 +189,7 @@ const MCValue = union(enum) { .immediate, .memory, - .compare_flags_unsigned, - .compare_flags_signed, + .cpsr_flags, .ptr_stack_offset, .undef, .stack_argument_offset, @@ -768,10 +766,10 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { .register_v_flag, => |reg| { self.register_manager.freeReg(reg); - self.compare_flags_inst = null; + self.cpsr_flags_inst = null; }, - .compare_flags_signed, .compare_flags_unsigned => { - self.compare_flags_inst = null; + .cpsr_flags => { + self.cpsr_flags_inst = null; }, else => {}, // TODO process stack allocation death } @@ -903,12 +901,10 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void /// Save the current instruction stored in the compare flags if /// occupied fn spillCompareFlagsIfOccupied(self: *Self) !void { - if (self.compare_flags_inst) |inst_to_save| { + if (self.cpsr_flags_inst) |inst_to_save| { const mcv = self.getResolvedInstValue(inst_to_save); const new_mcv = switch (mcv) { - .compare_flags_signed, - .compare_flags_unsigned, - => try self.allocRegOrMem(inst_to_save, true), + .cpsr_flags => try self.allocRegOrMem(inst_to_save, true), .register_c_flag, .register_v_flag, => try self.allocRegOrMem(inst_to_save, false), @@ -921,7 +917,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); - self.compare_flags_inst = null; + self.cpsr_flags_inst = null; // TODO consolidate with register manager and spillInstruction // this call should really belong in the register manager! @@ -1111,32 +1107,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { switch (operand) { .dead => unreachable, .unreach => unreachable, - .compare_flags_unsigned => |op| { - const r = MCValue{ - .compare_flags_unsigned = switch (op) { - .gte => .lt, - .gt => .lte, - .neq => .eq, - .lt => .gte, - .lte => .gt, - .eq => .neq, - }, - }; - break :result r; - }, - .compare_flags_signed => |op| { - const r = MCValue{ - .compare_flags_signed = switch (op) { - .gte => .lt, - .gt => .lte, - .neq => .eq, - .lt => .gte, - .lte => .gt, - .eq => .neq, - }, - }; - break :result r; - }, + .cpsr_flags => |cond| break :result MCValue{ .cpsr_flags = cond.negate() }, else => { switch (operand_ty.zigTypeTag()) { .Bool => { @@ -1425,7 +1396,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = null; + self.cpsr_flags_inst = null; const base_tag: Air.Inst.Tag = switch (tag) { .add_with_overflow => .add, @@ -1448,7 +1419,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, Type.usize, Type.usize, null); try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); - try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); + try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne }); break :result MCValue{ .stack_offset = stack_offset }; } else if (int_info.bits == 32) { @@ -1474,7 +1445,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { }; try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; + self.cpsr_flags_inst = inst; const dest = blk: { if (rhs_immediate_ok) { @@ -1530,7 +1501,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = null; + self.cpsr_flags_inst = null; const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { .signed => .smulbb, @@ -1553,14 +1524,14 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, Type.usize, Type.usize, null); try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); - try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); + try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne }); break :result MCValue{ .stack_offset = stack_offset }; } else if (int_info.bits <= 32) { const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = null; + self.cpsr_flags_inst = null; const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { .signed => .smull, @@ -1704,7 +1675,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = null; + self.cpsr_flags_inst = null; // lsl dest, lhs, rhs const dest = try self.binOp(.shl, lhs, rhs, lhs_ty, rhs_ty, null); @@ -1719,7 +1690,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { _ = try self.binOp(.cmp_eq, lhs, reconstructed, lhs_ty, lhs_ty, null); try self.genSetStack(lhs_ty, stack_offset, dest); - try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); + try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne }); break :result MCValue{ .stack_offset = stack_offset }; } else { @@ -2213,8 +2184,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .undef => unreachable, .unreach => unreachable, .dead => unreachable, - .compare_flags_unsigned, - .compare_flags_signed, + .cpsr_flags, .register_c_flag, .register_v_flag, => unreachable, // cannot hold an address @@ -2227,7 +2197,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo switch (dst_mcv) { .dead => unreachable, .undef => unreachable, - .compare_flags_signed, .compare_flags_unsigned => unreachable, + .cpsr_flags => unreachable, .register => |dst_reg| { try self.genLdrRegister(dst_reg, reg, elem_ty); }, @@ -2314,8 +2284,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .undef => unreachable, .unreach => unreachable, .dead => unreachable, - .compare_flags_unsigned, - .compare_flags_signed, + .cpsr_flags, .register_c_flag, .register_v_flag, => unreachable, // cannot hold an address @@ -2484,37 +2453,48 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { break :result MCValue{ .register = reg }; }, 1 => { - // get overflow bit: set register to C flag - // resp. V flag - const dest_reg = try self.register_manager.allocReg(null, gp); + // get overflow bit: return C or V flag + if (self.liveness.operandDies(inst, 0)) { + self.cpsr_flags_inst = inst; - // mov reg, #0 - _ = try self.addInst(.{ - .tag = .mov, - .data = .{ .rr_op = .{ - .rd = dest_reg, - .rn = .r0, - .op = Instruction.Operand.fromU32(0).?, - } }, - }); - - // C flag: movcs reg, #1 - // V flag: movvs reg, #1 - _ = try self.addInst(.{ - .tag = .mov, - .cond = switch (mcv) { + const cond: Condition = switch (mcv) { .register_c_flag => .cs, .register_v_flag => .vs, else => unreachable, - }, - .data = .{ .rr_op = .{ - .rd = dest_reg, - .rn = .r0, - .op = Instruction.Operand.fromU32(1).?, - } }, - }); + }; - break :result MCValue{ .register = dest_reg }; + break :result MCValue{ .cpsr_flags = cond }; + } else { + const dest_reg = try self.register_manager.allocReg(null, gp); + + // mov reg, #0 + _ = try self.addInst(.{ + .tag = .mov, + .data = .{ .rr_op = .{ + .rd = dest_reg, + .rn = .r0, + .op = Instruction.Operand.fromU32(0).?, + } }, + }); + + // C flag: movcs reg, #1 + // V flag: movvs reg, #1 + _ = try self.addInst(.{ + .tag = .mov, + .cond = switch (mcv) { + .register_c_flag => .cs, + .register_v_flag => .vs, + else => unreachable, + }, + .data = .{ .rr_op = .{ + .rd = dest_reg, + .rn = .r0, + .op = Instruction.Operand.fromU32(1).?, + } }, + }); + + break :result MCValue{ .register = dest_reg }; + } }, else => unreachable, } @@ -3602,7 +3582,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { const int_info = int_ty.intInfo(self.target.*); if (int_info.bits <= 32) { try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; + self.cpsr_flags_inst = inst; _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, BinOpMetadata{ .lhs = bin_op.lhs, @@ -3611,8 +3591,8 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { }); break :result switch (int_info.signedness) { - .signed => MCValue{ .compare_flags_signed = op }, - .unsigned => MCValue{ .compare_flags_unsigned = op }, + .signed => MCValue{ .cpsr_flags = Condition.fromCompareOperatorSigned(op) }, + .unsigned => MCValue{ .cpsr_flags = Condition.fromCompareOperatorUnsigned(op) }, }; } else { return self.fail("TODO ARM cmp for ints > 32 bits", .{}); @@ -3673,28 +3653,19 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { const pl_op = self.air.instructions.items(.data)[inst].pl_op; - const cond = try self.resolveInst(pl_op.operand); + const cond_inst = try self.resolveInst(pl_op.operand); const extra = self.air.extraData(Air.CondBr, pl_op.payload); const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len]; const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; const liveness_condbr = self.liveness.getCondBr(inst); const reloc: Mir.Inst.Index = reloc: { - const condition: Condition = switch (cond) { - .compare_flags_signed => |cmp_op| blk: { - // Here we map to the opposite condition because the jump is to the false branch. - const condition = Condition.fromCompareOperatorSigned(cmp_op); - break :blk condition.negate(); - }, - .compare_flags_unsigned => |cmp_op| blk: { - // Here we map to the opposite condition because the jump is to the false branch. - const condition = Condition.fromCompareOperatorUnsigned(cmp_op); - break :blk condition.negate(); - }, + const condition: Condition = switch (cond_inst) { + .cpsr_flags => |cond| cond.negate(), else => blk: { - const reg = switch (cond) { + const reg = switch (cond_inst) { .register => |r| r, - else => try self.copyToTmpRegister(Type.bool, cond), + else => try self.copyToTmpRegister(Type.bool, cond_inst), }; try self.spillCompareFlagsIfOccupied(); @@ -3739,7 +3710,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { var parent_stack = try self.stack.clone(self.gpa); defer parent_stack.deinit(self.gpa); const parent_registers = self.register_manager.registers; - const parent_compare_flags_inst = self.compare_flags_inst; + const parent_cpsr_flags_inst = self.cpsr_flags_inst; try self.branch_stack.append(.{}); errdefer { @@ -3758,7 +3729,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { defer saved_then_branch.deinit(self.gpa); self.register_manager.registers = parent_registers; - self.compare_flags_inst = parent_compare_flags_inst; + self.cpsr_flags_inst = parent_cpsr_flags_inst; self.stack.deinit(self.gpa); self.stack = parent_stack; @@ -3876,7 +3847,7 @@ fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue { } }, }); - return MCValue{ .compare_flags_unsigned = .eq }; + return MCValue{ .cpsr_flags = .eq }; } else { return self.fail("TODO implement non-pointer optionals", .{}); } @@ -3884,9 +3855,9 @@ fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue { fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue { const is_null_result = try self.isNull(ty, operand); - assert(is_null_result.compare_flags_unsigned == .eq); + assert(is_null_result.cpsr_flags == .eq); - return MCValue{ .compare_flags_unsigned = .neq }; + return MCValue{ .cpsr_flags = .ne }; } fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { @@ -3899,15 +3870,15 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { const error_mcv = try self.errUnionErr(operand, ty); _ = try self.binOp(.cmp_eq, error_mcv, .{ .immediate = 0 }, error_int_type, error_int_type, null); - return MCValue{ .compare_flags_unsigned = .gt }; + return MCValue{ .cpsr_flags = .hi }; } fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { const is_err_result = try self.isErr(ty, operand); switch (is_err_result) { - .compare_flags_unsigned => |op| { - assert(op == .gt); - return MCValue{ .compare_flags_unsigned = .lte }; + .cpsr_flags => |cond| { + assert(cond == .hi); + return MCValue{ .cpsr_flags = cond.negate() }; }, .immediate => |imm| { assert(imm == 0); @@ -3921,7 +3892,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; + self.cpsr_flags_inst = inst; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand = try self.resolveInst(un_op); @@ -4298,8 +4269,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro else => return self.fail("TODO implement memset", .{}), } }, - .compare_flags_unsigned, - .compare_flags_signed, + .cpsr_flags, .immediate, .ptr_stack_offset, => { @@ -4469,15 +4439,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void } }, }); }, - .compare_flags_unsigned, - .compare_flags_signed, - => |op| { - const condition = switch (mcv) { - .compare_flags_unsigned => Condition.fromCompareOperatorUnsigned(op), - .compare_flags_signed => Condition.fromCompareOperatorSigned(op), - else => unreachable, - }; - + .cpsr_flags => |condition| { const zero = Instruction.Operand.imm(0, 0); const one = Instruction.Operand.imm(1, 0); @@ -4826,8 +4788,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); } }, - .compare_flags_unsigned, - .compare_flags_signed, + .cpsr_flags, .immediate, .ptr_stack_offset, => { From 5b813f1a2acdc1668e39008b02a234f3da724552 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 15 May 2022 11:17:20 +0200 Subject: [PATCH 1601/2031] Set macOS/iPhoneOS/tvOS/watchOS ABI to none (unspecified) by default Prior to this change we would assume the ABI for Apple targets to be GNU which could result in subtle errors in LLVM emitting calls to non-existent system libc provided functions such as `_sincosf` which is a GNU extension and as such is not provided by macOS for example. This would result in linker errors where the linker would not be able to find the said symbol in `libSystem.tbd`. With this change, we now correctly identify macOS (and other Apple platforms) as having ABI `unknown` which translates to unspecified in LLVM under-the-hood: ``` // main.ll target triple = "aarch64-unknown-macos-unknown" ``` Note however that we never suffix the target OS with target version such as `macos11` or `macos12` which means we fail to instruct LLVM of potential optimisations provided by the OS such as the availability of function `___sincosf_stret`. I suggest we investigate that in a follow-up commit. --- ci/azure/macos_arm64_script | 8 ++++---- ci/azure/macos_script | 4 ++-- .../arm/_limits.h | 0 .../arm/_mcontext.h | 0 .../arm/_param.h | 0 .../arm/_types.h | 0 .../arm/arch.h | 0 .../arm/endian.h | 0 .../arm/limits.h | 0 .../arm/param.h | 0 .../arm/signal.h | 0 .../arm/types.h | 0 .../libkern/OSAtomic.h | 0 .../libkern/OSAtomicDeprecated.h | 0 .../libkern/OSAtomicQueue.h | 0 .../libkern/OSSpinLockDeprecated.h | 0 .../libkern/arm/OSByteOrder.h | 0 .../mach/arm/_structs.h | 0 .../mach/arm/boolean.h | 0 .../mach/arm/exception.h | 0 .../mach/arm/kern_return.h | 0 .../mach/arm/processor_info.h | 0 .../mach/arm/rpc.h | 0 .../mach/arm/thread_state.h | 0 .../mach/arm/thread_status.h | 0 .../mach/arm/vm_param.h | 0 .../mach/arm/vm_types.h | 0 .../arm/_limits.h | 0 .../arm/_mcontext.h | 0 .../arm/_param.h | 0 .../arm/_types.h | 0 .../arm/arch.h | 0 .../arm/endian.h | 0 .../arm/limits.h | 0 .../arm/param.h | 0 .../arm/signal.h | 0 .../arm/types.h | 0 .../libkern/OSAtomic.h | 0 .../libkern/OSAtomicDeprecated.h | 0 .../libkern/OSAtomicQueue.h | 0 .../libkern/OSSpinLockDeprecated.h | 0 .../libkern/arm/OSByteOrder.h | 0 .../mach/arm/_structs.h | 0 .../mach/arm/boolean.h | 0 .../mach/arm/exception.h | 0 .../mach/arm/kern_return.h | 0 .../mach/arm/processor_info.h | 0 .../mach/arm/rpc.h | 0 .../mach/arm/thread_state.h | 0 .../mach/arm/thread_status.h | 0 .../mach/arm/vm_param.h | 0 .../mach/arm/vm_types.h | 0 .../Availability.h | 0 .../AvailabilityInternal.h | 0 .../AvailabilityMacros.h | 0 .../TargetConditionals.h | 0 .../_ctermid.h | 0 .../bsm/audit.h | 0 .../dispatch/block.h | 0 .../dispatch/dispatch.h | 0 .../dispatch/group.h | 0 .../dispatch/object.h | 0 .../dispatch/queue.h | 0 .../dispatch/semaphore.h | 0 .../dispatch/source.h | 0 .../dispatch/workloop.h | 0 .../i386/_limits.h | 0 .../i386/_mcontext.h | 0 .../i386/_param.h | 0 .../i386/_types.h | 0 .../i386/eflags.h | 0 .../i386/endian.h | 0 .../i386/limits.h | 0 .../i386/param.h | 0 .../i386/signal.h | 0 .../i386/types.h | 0 .../libkern/OSAtomic.h | 0 .../libkern/OSAtomicDeprecated.h | 0 .../libkern/OSAtomicQueue.h | 0 .../libkern/OSByteOrder.h | 0 .../libkern/OSSpinLockDeprecated.h | 0 .../libkern/_OSByteOrder.h | 0 .../libkern/i386/OSByteOrder.h | 0 .../libkern/i386/_OSByteOrder.h | 0 .../libproc.h | 0 .../mach-o/compact_unwind_encoding.h | 0 .../mach-o/dyld.h | 0 .../mach-o/loader.h | 0 .../mach/exception_types.h | 0 .../mach/host_special_ports.h | 0 .../mach/i386/_structs.h | 0 .../mach/i386/boolean.h | 0 .../mach/i386/exception.h | 0 .../mach/i386/fp_reg.h | 0 .../mach/i386/kern_return.h | 0 .../mach/i386/processor_info.h | 0 .../mach/i386/rpc.h | 0 .../mach/i386/thread_state.h | 0 .../mach/i386/thread_status.h | 0 .../mach/i386/vm_param.h | 0 .../mach/i386/vm_types.h | 0 .../mach/kern_return.h | 0 .../mach/mach_init.h | 0 .../mach/mach_port.h | 0 .../mach/mach_traps.h | 0 .../mach/mach_types.h | 0 .../mach/machine.h | 0 .../mach/machine/_structs.h | 0 .../mach/machine/boolean.h | 0 .../mach/machine/exception.h | 0 .../mach/machine/kern_return.h | 0 .../mach/machine/processor_info.h | 0 .../mach/machine/rpc.h | 0 .../mach/machine/thread_state.h | 0 .../mach/machine/thread_status.h | 0 .../mach/machine/vm_param.h | 0 .../mach/machine/vm_types.h | 0 .../mach/memory_object_types.h | 0 .../mach/message.h | 0 .../mach/port.h | 0 .../mach/processor_set.h | 0 .../mach/task.h | 0 .../mach/task_info.h | 0 .../mach/task_policy.h | 0 .../mach/task_special_ports.h | 0 .../mach/thread_act.h | 0 .../mach/thread_special_ports.h | 0 .../mach/thread_status.h | 0 .../mach/vm_map.h | 0 .../mach/vm_param.h | 0 .../mach/vm_prot.h | 0 .../mach/vm_statistics.h | 0 .../mach/vm_types.h | 0 .../mach_debug/ipc_info.h | 0 .../machine/_mcontext.h | 0 .../machine/_param.h | 0 .../machine/_types.h | 0 .../machine/endian.h | 0 .../machine/limits.h | 0 .../machine/param.h | 0 .../machine/signal.h | 0 .../machine/types.h | 0 .../malloc/_malloc.h | 0 .../malloc/malloc.h | 0 .../math.h | 0 .../net/if.h | 0 .../net/if_var.h | 0 .../net/route.h | 0 .../netinet/in.h | 0 .../netinet/tcp.h | 0 .../netinet6/in6.h | 0 .../objc/objc-api.h | 0 .../objc/runtime.h | 0 .../os/base.h | 0 .../os/object.h | 0 .../pthread.h | 0 .../pthread/sched.h | 0 .../pthread_impl.h | 0 .../sched.h | 0 .../signal.h | 0 .../simd/common.h | 0 .../simd/conversion.h | 0 .../simd/logic.h | 0 .../simd/math.h | 0 .../simd/packed.h | 0 .../simd/quaternion.h | 0 .../spawn.h | 0 .../stdio.h | 0 .../stdlib.h | 0 .../string.h | 0 .../sys/_pthread/_pthread_attr_t.h | 0 .../sys/_pthread/_pthread_cond_t.h | 0 .../sys/_pthread/_pthread_condattr_t.h | 0 .../sys/_pthread/_pthread_rwlock_t.h | 0 .../sys/_pthread/_pthread_rwlockattr_t.h | 0 .../sys/_pthread/_pthread_t.h | 0 .../sys/_pthread/_pthread_types.h | 0 .../sys/_select.h | 0 .../sys/_symbol_aliasing.h | 0 .../sys/_types/_fd_def.h | 0 .../sys/_types/_int8_t.h | 0 .../sys/_types/_ucontext.h | 0 .../sys/acl.h | 0 .../sys/attr.h | 0 .../sys/cdefs.h | 0 .../sys/event.h | 0 .../sys/fcntl.h | 0 .../sys/ioccom.h | 0 .../sys/kauth.h | 0 .../sys/mman.h | 0 .../sys/mount.h | 0 .../sys/param.h | 0 .../sys/proc.h | 0 .../sys/proc_info.h | 0 .../sys/resource.h | 0 .../sys/shm.h | 0 .../sys/socket.h | 0 .../sys/sockio.h | 0 .../sys/spawn.h | 0 .../sys/stat.h | 0 .../sys/sysctl.h | 0 .../sys/syslimits.h | 0 .../sys/ucontext.h | 0 .../sys/uio.h | 0 .../sys/un.h | 0 .../time.h | 0 .../ucontext.h | 0 .../xlocale/_inttypes.h | 0 .../xlocale/_wchar.h | 0 .../xpc/availability.h | 0 .../xpc/base.h | 0 .../xpc/connection.h | 0 .../xpc/xpc.h | 0 .../i386/_limits.h | 0 .../i386/_mcontext.h | 0 .../i386/_param.h | 0 .../i386/_types.h | 0 .../i386/eflags.h | 0 .../i386/endian.h | 0 .../i386/limits.h | 0 .../i386/param.h | 0 .../i386/signal.h | 0 .../i386/types.h | 0 .../libkern/OSAtomic.h | 0 .../libkern/OSAtomicDeprecated.h | 0 .../libkern/OSAtomicQueue.h | 0 .../libkern/OSSpinLockDeprecated.h | 0 .../libkern/i386/OSByteOrder.h | 0 .../libkern/i386/_OSByteOrder.h | 0 .../mach/i386/_structs.h | 0 .../mach/i386/boolean.h | 0 .../mach/i386/exception.h | 0 .../mach/i386/fp_reg.h | 0 .../mach/i386/kern_return.h | 0 .../mach/i386/processor_info.h | 0 .../mach/i386/rpc.h | 0 .../mach/i386/thread_state.h | 0 .../mach/i386/thread_status.h | 0 .../mach/i386/vm_param.h | 0 .../mach/i386/vm_types.h | 0 .../i386/_limits.h | 0 .../i386/_mcontext.h | 0 .../i386/_param.h | 0 .../i386/_types.h | 0 .../i386/eflags.h | 0 .../i386/endian.h | 0 .../i386/limits.h | 0 .../i386/param.h | 0 .../i386/signal.h | 0 .../i386/types.h | 0 .../libkern/OSAtomic.h | 0 .../libkern/OSAtomicDeprecated.h | 0 .../libkern/OSAtomicQueue.h | 0 .../libkern/OSSpinLockDeprecated.h | 0 .../libkern/i386/OSByteOrder.h | 0 .../libkern/i386/_OSByteOrder.h | 0 .../mach/i386/_structs.h | 0 .../mach/i386/boolean.h | 0 .../mach/i386/exception.h | 0 .../mach/i386/fp_reg.h | 0 .../mach/i386/kern_return.h | 0 .../mach/i386/processor_info.h | 0 .../mach/i386/rpc.h | 0 .../mach/i386/thread_state.h | 0 .../mach/i386/thread_status.h | 0 .../mach/i386/vm_param.h | 0 .../mach/i386/vm_types.h | 0 lib/std/target.zig | 8 ++++---- src/Compilation.zig | 2 +- src/link/MachO/Dylib.zig | 2 +- src/stage1/target.cpp | 8 ++++---- src/target.zig | 10 +++++----- 272 files changed, 21 insertions(+), 21 deletions(-) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/arm/_limits.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/arm/_mcontext.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/arm/_param.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/arm/_types.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/arm/arch.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/arm/endian.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/arm/limits.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/arm/param.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/arm/signal.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/arm/types.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/libkern/OSAtomic.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/libkern/OSAtomicDeprecated.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/libkern/OSAtomicQueue.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/libkern/OSSpinLockDeprecated.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/libkern/arm/OSByteOrder.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/mach/arm/_structs.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/mach/arm/boolean.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/mach/arm/exception.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/mach/arm/kern_return.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/mach/arm/processor_info.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/mach/arm/rpc.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/mach/arm/thread_state.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/mach/arm/thread_status.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/mach/arm/vm_param.h (100%) rename lib/libc/include/{aarch64-macos.11-gnu => aarch64-macos.11-none}/mach/arm/vm_types.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/arm/_limits.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/arm/_mcontext.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/arm/_param.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/arm/_types.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/arm/arch.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/arm/endian.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/arm/limits.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/arm/param.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/arm/signal.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/arm/types.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/libkern/OSAtomic.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/libkern/OSAtomicDeprecated.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/libkern/OSAtomicQueue.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/libkern/OSSpinLockDeprecated.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/libkern/arm/OSByteOrder.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/mach/arm/_structs.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/mach/arm/boolean.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/mach/arm/exception.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/mach/arm/kern_return.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/mach/arm/processor_info.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/mach/arm/rpc.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/mach/arm/thread_state.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/mach/arm/thread_status.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/mach/arm/vm_param.h (100%) rename lib/libc/include/{aarch64-macos.12-gnu => aarch64-macos.12-none}/mach/arm/vm_types.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/Availability.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/AvailabilityInternal.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/AvailabilityMacros.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/TargetConditionals.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/_ctermid.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/bsm/audit.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/dispatch/block.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/dispatch/dispatch.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/dispatch/group.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/dispatch/object.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/dispatch/queue.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/dispatch/semaphore.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/dispatch/source.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/dispatch/workloop.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/i386/_limits.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/i386/_mcontext.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/i386/_param.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/i386/_types.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/i386/eflags.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/i386/endian.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/i386/limits.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/i386/param.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/i386/signal.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/i386/types.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/libkern/OSAtomic.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/libkern/OSAtomicDeprecated.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/libkern/OSAtomicQueue.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/libkern/OSByteOrder.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/libkern/OSSpinLockDeprecated.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/libkern/_OSByteOrder.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/libkern/i386/OSByteOrder.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/libkern/i386/_OSByteOrder.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/libproc.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach-o/compact_unwind_encoding.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach-o/dyld.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach-o/loader.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/exception_types.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/host_special_ports.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/i386/_structs.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/i386/boolean.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/i386/exception.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/i386/fp_reg.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/i386/kern_return.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/i386/processor_info.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/i386/rpc.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/i386/thread_state.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/i386/thread_status.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/i386/vm_param.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/i386/vm_types.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/kern_return.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/mach_init.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/mach_port.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/mach_traps.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/mach_types.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/machine.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/machine/_structs.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/machine/boolean.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/machine/exception.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/machine/kern_return.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/machine/processor_info.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/machine/rpc.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/machine/thread_state.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/machine/thread_status.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/machine/vm_param.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/machine/vm_types.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/memory_object_types.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/message.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/port.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/processor_set.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/task.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/task_info.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/task_policy.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/task_special_ports.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/thread_act.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/thread_special_ports.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/thread_status.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/vm_map.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/vm_param.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/vm_prot.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/vm_statistics.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach/vm_types.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/mach_debug/ipc_info.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/machine/_mcontext.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/machine/_param.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/machine/_types.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/machine/endian.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/machine/limits.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/machine/param.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/machine/signal.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/machine/types.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/malloc/_malloc.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/malloc/malloc.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/math.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/net/if.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/net/if_var.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/net/route.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/netinet/in.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/netinet/tcp.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/netinet6/in6.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/objc/objc-api.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/objc/runtime.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/os/base.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/os/object.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/pthread.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/pthread/sched.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/pthread_impl.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sched.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/signal.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/simd/common.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/simd/conversion.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/simd/logic.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/simd/math.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/simd/packed.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/simd/quaternion.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/spawn.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/stdio.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/stdlib.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/string.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/_pthread/_pthread_attr_t.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/_pthread/_pthread_cond_t.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/_pthread/_pthread_condattr_t.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/_pthread/_pthread_rwlock_t.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/_pthread/_pthread_rwlockattr_t.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/_pthread/_pthread_t.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/_pthread/_pthread_types.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/_select.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/_symbol_aliasing.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/_types/_fd_def.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/_types/_int8_t.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/_types/_ucontext.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/acl.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/attr.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/cdefs.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/event.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/fcntl.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/ioccom.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/kauth.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/mman.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/mount.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/param.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/proc.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/proc_info.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/resource.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/shm.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/socket.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/sockio.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/spawn.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/stat.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/sysctl.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/syslimits.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/ucontext.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/uio.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/sys/un.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/time.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/ucontext.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/xlocale/_inttypes.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/xlocale/_wchar.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/xpc/availability.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/xpc/base.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/xpc/connection.h (100%) rename lib/libc/include/{x86_64-macos.10-gnu => x86_64-macos.10-none}/xpc/xpc.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/i386/_limits.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/i386/_mcontext.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/i386/_param.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/i386/_types.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/i386/eflags.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/i386/endian.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/i386/limits.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/i386/param.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/i386/signal.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/i386/types.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/libkern/OSAtomic.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/libkern/OSAtomicDeprecated.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/libkern/OSAtomicQueue.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/libkern/OSSpinLockDeprecated.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/libkern/i386/OSByteOrder.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/libkern/i386/_OSByteOrder.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/mach/i386/_structs.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/mach/i386/boolean.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/mach/i386/exception.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/mach/i386/fp_reg.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/mach/i386/kern_return.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/mach/i386/processor_info.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/mach/i386/rpc.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/mach/i386/thread_state.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/mach/i386/thread_status.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/mach/i386/vm_param.h (100%) rename lib/libc/include/{x86_64-macos.11-gnu => x86_64-macos.11-none}/mach/i386/vm_types.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/i386/_limits.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/i386/_mcontext.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/i386/_param.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/i386/_types.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/i386/eflags.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/i386/endian.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/i386/limits.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/i386/param.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/i386/signal.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/i386/types.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/libkern/OSAtomic.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/libkern/OSAtomicDeprecated.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/libkern/OSAtomicQueue.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/libkern/OSSpinLockDeprecated.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/libkern/i386/OSByteOrder.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/libkern/i386/_OSByteOrder.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/mach/i386/_structs.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/mach/i386/boolean.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/mach/i386/exception.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/mach/i386/fp_reg.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/mach/i386/kern_return.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/mach/i386/processor_info.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/mach/i386/rpc.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/mach/i386/thread_state.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/mach/i386/thread_status.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/mach/i386/vm_param.h (100%) rename lib/libc/include/{x86_64-macos.12-gnu => x86_64-macos.12-none}/mach/i386/vm_types.h (100%) diff --git a/ci/azure/macos_arm64_script b/ci/azure/macos_arm64_script index 167276aaab..336de1db48 100755 --- a/ci/azure/macos_arm64_script +++ b/ci/azure/macos_arm64_script @@ -8,15 +8,15 @@ brew update && brew install ncurses s3cmd ZIGDIR="$(pwd)" HOST_ARCH="x86_64" -HOST_TARGET="$HOST_ARCH-macos-gnu" +HOST_TARGET="$HOST_ARCH-macos-none" HOST_MCPU="baseline" -HOST_CACHE_BASENAME="zig+llvm+lld+clang-$HOST_TARGET-0.9.1" +HOST_CACHE_BASENAME="zig+llvm+lld+clang-$HOST_TARGET-0.10.0-dev.2348+d43761808" HOST_PREFIX="$HOME/$HOST_CACHE_BASENAME" ARCH="aarch64" -TARGET="$ARCH-macos-gnu" +TARGET="$ARCH-macos-none" MCPU="apple_a14" -CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.9.1" +CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.10.0-dev.2348+d43761808" PREFIX="$HOME/$CACHE_BASENAME" JOBS="-j2" diff --git a/ci/azure/macos_script b/ci/azure/macos_script index 28a53b4501..9e32e7803e 100755 --- a/ci/azure/macos_script +++ b/ci/azure/macos_script @@ -7,9 +7,9 @@ brew update && brew install ncurses s3cmd ZIGDIR="$(pwd)" ARCH="x86_64" -TARGET="$ARCH-macos-gnu" +TARGET="$ARCH-macos-none" MCPU="baseline" -CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.9.1" +CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.10.0-dev.2348+d43761808" PREFIX="$HOME/$CACHE_BASENAME" JOBS="-j2" diff --git a/lib/libc/include/aarch64-macos.11-gnu/arm/_limits.h b/lib/libc/include/aarch64-macos.11-none/arm/_limits.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/arm/_limits.h rename to lib/libc/include/aarch64-macos.11-none/arm/_limits.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/arm/_mcontext.h b/lib/libc/include/aarch64-macos.11-none/arm/_mcontext.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/arm/_mcontext.h rename to lib/libc/include/aarch64-macos.11-none/arm/_mcontext.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/arm/_param.h b/lib/libc/include/aarch64-macos.11-none/arm/_param.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/arm/_param.h rename to lib/libc/include/aarch64-macos.11-none/arm/_param.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/arm/_types.h b/lib/libc/include/aarch64-macos.11-none/arm/_types.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/arm/_types.h rename to lib/libc/include/aarch64-macos.11-none/arm/_types.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/arm/arch.h b/lib/libc/include/aarch64-macos.11-none/arm/arch.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/arm/arch.h rename to lib/libc/include/aarch64-macos.11-none/arm/arch.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/arm/endian.h b/lib/libc/include/aarch64-macos.11-none/arm/endian.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/arm/endian.h rename to lib/libc/include/aarch64-macos.11-none/arm/endian.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/arm/limits.h b/lib/libc/include/aarch64-macos.11-none/arm/limits.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/arm/limits.h rename to lib/libc/include/aarch64-macos.11-none/arm/limits.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/arm/param.h b/lib/libc/include/aarch64-macos.11-none/arm/param.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/arm/param.h rename to lib/libc/include/aarch64-macos.11-none/arm/param.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/arm/signal.h b/lib/libc/include/aarch64-macos.11-none/arm/signal.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/arm/signal.h rename to lib/libc/include/aarch64-macos.11-none/arm/signal.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/arm/types.h b/lib/libc/include/aarch64-macos.11-none/arm/types.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/arm/types.h rename to lib/libc/include/aarch64-macos.11-none/arm/types.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/libkern/OSAtomic.h b/lib/libc/include/aarch64-macos.11-none/libkern/OSAtomic.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/libkern/OSAtomic.h rename to lib/libc/include/aarch64-macos.11-none/libkern/OSAtomic.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/libkern/OSAtomicDeprecated.h b/lib/libc/include/aarch64-macos.11-none/libkern/OSAtomicDeprecated.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/libkern/OSAtomicDeprecated.h rename to lib/libc/include/aarch64-macos.11-none/libkern/OSAtomicDeprecated.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/libkern/OSAtomicQueue.h b/lib/libc/include/aarch64-macos.11-none/libkern/OSAtomicQueue.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/libkern/OSAtomicQueue.h rename to lib/libc/include/aarch64-macos.11-none/libkern/OSAtomicQueue.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/libkern/OSSpinLockDeprecated.h b/lib/libc/include/aarch64-macos.11-none/libkern/OSSpinLockDeprecated.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/libkern/OSSpinLockDeprecated.h rename to lib/libc/include/aarch64-macos.11-none/libkern/OSSpinLockDeprecated.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/libkern/arm/OSByteOrder.h b/lib/libc/include/aarch64-macos.11-none/libkern/arm/OSByteOrder.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/libkern/arm/OSByteOrder.h rename to lib/libc/include/aarch64-macos.11-none/libkern/arm/OSByteOrder.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/mach/arm/_structs.h b/lib/libc/include/aarch64-macos.11-none/mach/arm/_structs.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/mach/arm/_structs.h rename to lib/libc/include/aarch64-macos.11-none/mach/arm/_structs.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/mach/arm/boolean.h b/lib/libc/include/aarch64-macos.11-none/mach/arm/boolean.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/mach/arm/boolean.h rename to lib/libc/include/aarch64-macos.11-none/mach/arm/boolean.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/mach/arm/exception.h b/lib/libc/include/aarch64-macos.11-none/mach/arm/exception.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/mach/arm/exception.h rename to lib/libc/include/aarch64-macos.11-none/mach/arm/exception.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/mach/arm/kern_return.h b/lib/libc/include/aarch64-macos.11-none/mach/arm/kern_return.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/mach/arm/kern_return.h rename to lib/libc/include/aarch64-macos.11-none/mach/arm/kern_return.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/mach/arm/processor_info.h b/lib/libc/include/aarch64-macos.11-none/mach/arm/processor_info.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/mach/arm/processor_info.h rename to lib/libc/include/aarch64-macos.11-none/mach/arm/processor_info.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/mach/arm/rpc.h b/lib/libc/include/aarch64-macos.11-none/mach/arm/rpc.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/mach/arm/rpc.h rename to lib/libc/include/aarch64-macos.11-none/mach/arm/rpc.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/mach/arm/thread_state.h b/lib/libc/include/aarch64-macos.11-none/mach/arm/thread_state.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/mach/arm/thread_state.h rename to lib/libc/include/aarch64-macos.11-none/mach/arm/thread_state.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/mach/arm/thread_status.h b/lib/libc/include/aarch64-macos.11-none/mach/arm/thread_status.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/mach/arm/thread_status.h rename to lib/libc/include/aarch64-macos.11-none/mach/arm/thread_status.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/mach/arm/vm_param.h b/lib/libc/include/aarch64-macos.11-none/mach/arm/vm_param.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/mach/arm/vm_param.h rename to lib/libc/include/aarch64-macos.11-none/mach/arm/vm_param.h diff --git a/lib/libc/include/aarch64-macos.11-gnu/mach/arm/vm_types.h b/lib/libc/include/aarch64-macos.11-none/mach/arm/vm_types.h similarity index 100% rename from lib/libc/include/aarch64-macos.11-gnu/mach/arm/vm_types.h rename to lib/libc/include/aarch64-macos.11-none/mach/arm/vm_types.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/arm/_limits.h b/lib/libc/include/aarch64-macos.12-none/arm/_limits.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/arm/_limits.h rename to lib/libc/include/aarch64-macos.12-none/arm/_limits.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/arm/_mcontext.h b/lib/libc/include/aarch64-macos.12-none/arm/_mcontext.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/arm/_mcontext.h rename to lib/libc/include/aarch64-macos.12-none/arm/_mcontext.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/arm/_param.h b/lib/libc/include/aarch64-macos.12-none/arm/_param.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/arm/_param.h rename to lib/libc/include/aarch64-macos.12-none/arm/_param.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/arm/_types.h b/lib/libc/include/aarch64-macos.12-none/arm/_types.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/arm/_types.h rename to lib/libc/include/aarch64-macos.12-none/arm/_types.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/arm/arch.h b/lib/libc/include/aarch64-macos.12-none/arm/arch.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/arm/arch.h rename to lib/libc/include/aarch64-macos.12-none/arm/arch.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/arm/endian.h b/lib/libc/include/aarch64-macos.12-none/arm/endian.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/arm/endian.h rename to lib/libc/include/aarch64-macos.12-none/arm/endian.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/arm/limits.h b/lib/libc/include/aarch64-macos.12-none/arm/limits.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/arm/limits.h rename to lib/libc/include/aarch64-macos.12-none/arm/limits.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/arm/param.h b/lib/libc/include/aarch64-macos.12-none/arm/param.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/arm/param.h rename to lib/libc/include/aarch64-macos.12-none/arm/param.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/arm/signal.h b/lib/libc/include/aarch64-macos.12-none/arm/signal.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/arm/signal.h rename to lib/libc/include/aarch64-macos.12-none/arm/signal.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/arm/types.h b/lib/libc/include/aarch64-macos.12-none/arm/types.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/arm/types.h rename to lib/libc/include/aarch64-macos.12-none/arm/types.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/libkern/OSAtomic.h b/lib/libc/include/aarch64-macos.12-none/libkern/OSAtomic.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/libkern/OSAtomic.h rename to lib/libc/include/aarch64-macos.12-none/libkern/OSAtomic.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/libkern/OSAtomicDeprecated.h b/lib/libc/include/aarch64-macos.12-none/libkern/OSAtomicDeprecated.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/libkern/OSAtomicDeprecated.h rename to lib/libc/include/aarch64-macos.12-none/libkern/OSAtomicDeprecated.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/libkern/OSAtomicQueue.h b/lib/libc/include/aarch64-macos.12-none/libkern/OSAtomicQueue.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/libkern/OSAtomicQueue.h rename to lib/libc/include/aarch64-macos.12-none/libkern/OSAtomicQueue.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/libkern/OSSpinLockDeprecated.h b/lib/libc/include/aarch64-macos.12-none/libkern/OSSpinLockDeprecated.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/libkern/OSSpinLockDeprecated.h rename to lib/libc/include/aarch64-macos.12-none/libkern/OSSpinLockDeprecated.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/libkern/arm/OSByteOrder.h b/lib/libc/include/aarch64-macos.12-none/libkern/arm/OSByteOrder.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/libkern/arm/OSByteOrder.h rename to lib/libc/include/aarch64-macos.12-none/libkern/arm/OSByteOrder.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/mach/arm/_structs.h b/lib/libc/include/aarch64-macos.12-none/mach/arm/_structs.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/mach/arm/_structs.h rename to lib/libc/include/aarch64-macos.12-none/mach/arm/_structs.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/mach/arm/boolean.h b/lib/libc/include/aarch64-macos.12-none/mach/arm/boolean.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/mach/arm/boolean.h rename to lib/libc/include/aarch64-macos.12-none/mach/arm/boolean.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/mach/arm/exception.h b/lib/libc/include/aarch64-macos.12-none/mach/arm/exception.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/mach/arm/exception.h rename to lib/libc/include/aarch64-macos.12-none/mach/arm/exception.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/mach/arm/kern_return.h b/lib/libc/include/aarch64-macos.12-none/mach/arm/kern_return.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/mach/arm/kern_return.h rename to lib/libc/include/aarch64-macos.12-none/mach/arm/kern_return.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/mach/arm/processor_info.h b/lib/libc/include/aarch64-macos.12-none/mach/arm/processor_info.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/mach/arm/processor_info.h rename to lib/libc/include/aarch64-macos.12-none/mach/arm/processor_info.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/mach/arm/rpc.h b/lib/libc/include/aarch64-macos.12-none/mach/arm/rpc.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/mach/arm/rpc.h rename to lib/libc/include/aarch64-macos.12-none/mach/arm/rpc.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/mach/arm/thread_state.h b/lib/libc/include/aarch64-macos.12-none/mach/arm/thread_state.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/mach/arm/thread_state.h rename to lib/libc/include/aarch64-macos.12-none/mach/arm/thread_state.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/mach/arm/thread_status.h b/lib/libc/include/aarch64-macos.12-none/mach/arm/thread_status.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/mach/arm/thread_status.h rename to lib/libc/include/aarch64-macos.12-none/mach/arm/thread_status.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/mach/arm/vm_param.h b/lib/libc/include/aarch64-macos.12-none/mach/arm/vm_param.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/mach/arm/vm_param.h rename to lib/libc/include/aarch64-macos.12-none/mach/arm/vm_param.h diff --git a/lib/libc/include/aarch64-macos.12-gnu/mach/arm/vm_types.h b/lib/libc/include/aarch64-macos.12-none/mach/arm/vm_types.h similarity index 100% rename from lib/libc/include/aarch64-macos.12-gnu/mach/arm/vm_types.h rename to lib/libc/include/aarch64-macos.12-none/mach/arm/vm_types.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/Availability.h b/lib/libc/include/x86_64-macos.10-none/Availability.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/Availability.h rename to lib/libc/include/x86_64-macos.10-none/Availability.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/AvailabilityInternal.h b/lib/libc/include/x86_64-macos.10-none/AvailabilityInternal.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/AvailabilityInternal.h rename to lib/libc/include/x86_64-macos.10-none/AvailabilityInternal.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/AvailabilityMacros.h b/lib/libc/include/x86_64-macos.10-none/AvailabilityMacros.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/AvailabilityMacros.h rename to lib/libc/include/x86_64-macos.10-none/AvailabilityMacros.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/TargetConditionals.h b/lib/libc/include/x86_64-macos.10-none/TargetConditionals.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/TargetConditionals.h rename to lib/libc/include/x86_64-macos.10-none/TargetConditionals.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/_ctermid.h b/lib/libc/include/x86_64-macos.10-none/_ctermid.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/_ctermid.h rename to lib/libc/include/x86_64-macos.10-none/_ctermid.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/bsm/audit.h b/lib/libc/include/x86_64-macos.10-none/bsm/audit.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/bsm/audit.h rename to lib/libc/include/x86_64-macos.10-none/bsm/audit.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/dispatch/block.h b/lib/libc/include/x86_64-macos.10-none/dispatch/block.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/dispatch/block.h rename to lib/libc/include/x86_64-macos.10-none/dispatch/block.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/dispatch/dispatch.h b/lib/libc/include/x86_64-macos.10-none/dispatch/dispatch.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/dispatch/dispatch.h rename to lib/libc/include/x86_64-macos.10-none/dispatch/dispatch.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/dispatch/group.h b/lib/libc/include/x86_64-macos.10-none/dispatch/group.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/dispatch/group.h rename to lib/libc/include/x86_64-macos.10-none/dispatch/group.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/dispatch/object.h b/lib/libc/include/x86_64-macos.10-none/dispatch/object.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/dispatch/object.h rename to lib/libc/include/x86_64-macos.10-none/dispatch/object.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/dispatch/queue.h b/lib/libc/include/x86_64-macos.10-none/dispatch/queue.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/dispatch/queue.h rename to lib/libc/include/x86_64-macos.10-none/dispatch/queue.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/dispatch/semaphore.h b/lib/libc/include/x86_64-macos.10-none/dispatch/semaphore.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/dispatch/semaphore.h rename to lib/libc/include/x86_64-macos.10-none/dispatch/semaphore.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/dispatch/source.h b/lib/libc/include/x86_64-macos.10-none/dispatch/source.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/dispatch/source.h rename to lib/libc/include/x86_64-macos.10-none/dispatch/source.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/dispatch/workloop.h b/lib/libc/include/x86_64-macos.10-none/dispatch/workloop.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/dispatch/workloop.h rename to lib/libc/include/x86_64-macos.10-none/dispatch/workloop.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/i386/_limits.h b/lib/libc/include/x86_64-macos.10-none/i386/_limits.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/i386/_limits.h rename to lib/libc/include/x86_64-macos.10-none/i386/_limits.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/i386/_mcontext.h b/lib/libc/include/x86_64-macos.10-none/i386/_mcontext.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/i386/_mcontext.h rename to lib/libc/include/x86_64-macos.10-none/i386/_mcontext.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/i386/_param.h b/lib/libc/include/x86_64-macos.10-none/i386/_param.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/i386/_param.h rename to lib/libc/include/x86_64-macos.10-none/i386/_param.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/i386/_types.h b/lib/libc/include/x86_64-macos.10-none/i386/_types.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/i386/_types.h rename to lib/libc/include/x86_64-macos.10-none/i386/_types.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/i386/eflags.h b/lib/libc/include/x86_64-macos.10-none/i386/eflags.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/i386/eflags.h rename to lib/libc/include/x86_64-macos.10-none/i386/eflags.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/i386/endian.h b/lib/libc/include/x86_64-macos.10-none/i386/endian.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/i386/endian.h rename to lib/libc/include/x86_64-macos.10-none/i386/endian.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/i386/limits.h b/lib/libc/include/x86_64-macos.10-none/i386/limits.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/i386/limits.h rename to lib/libc/include/x86_64-macos.10-none/i386/limits.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/i386/param.h b/lib/libc/include/x86_64-macos.10-none/i386/param.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/i386/param.h rename to lib/libc/include/x86_64-macos.10-none/i386/param.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/i386/signal.h b/lib/libc/include/x86_64-macos.10-none/i386/signal.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/i386/signal.h rename to lib/libc/include/x86_64-macos.10-none/i386/signal.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/i386/types.h b/lib/libc/include/x86_64-macos.10-none/i386/types.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/i386/types.h rename to lib/libc/include/x86_64-macos.10-none/i386/types.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/libkern/OSAtomic.h b/lib/libc/include/x86_64-macos.10-none/libkern/OSAtomic.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/libkern/OSAtomic.h rename to lib/libc/include/x86_64-macos.10-none/libkern/OSAtomic.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/libkern/OSAtomicDeprecated.h b/lib/libc/include/x86_64-macos.10-none/libkern/OSAtomicDeprecated.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/libkern/OSAtomicDeprecated.h rename to lib/libc/include/x86_64-macos.10-none/libkern/OSAtomicDeprecated.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/libkern/OSAtomicQueue.h b/lib/libc/include/x86_64-macos.10-none/libkern/OSAtomicQueue.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/libkern/OSAtomicQueue.h rename to lib/libc/include/x86_64-macos.10-none/libkern/OSAtomicQueue.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/libkern/OSByteOrder.h b/lib/libc/include/x86_64-macos.10-none/libkern/OSByteOrder.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/libkern/OSByteOrder.h rename to lib/libc/include/x86_64-macos.10-none/libkern/OSByteOrder.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/libkern/OSSpinLockDeprecated.h b/lib/libc/include/x86_64-macos.10-none/libkern/OSSpinLockDeprecated.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/libkern/OSSpinLockDeprecated.h rename to lib/libc/include/x86_64-macos.10-none/libkern/OSSpinLockDeprecated.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/libkern/_OSByteOrder.h b/lib/libc/include/x86_64-macos.10-none/libkern/_OSByteOrder.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/libkern/_OSByteOrder.h rename to lib/libc/include/x86_64-macos.10-none/libkern/_OSByteOrder.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/libkern/i386/OSByteOrder.h b/lib/libc/include/x86_64-macos.10-none/libkern/i386/OSByteOrder.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/libkern/i386/OSByteOrder.h rename to lib/libc/include/x86_64-macos.10-none/libkern/i386/OSByteOrder.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/libkern/i386/_OSByteOrder.h b/lib/libc/include/x86_64-macos.10-none/libkern/i386/_OSByteOrder.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/libkern/i386/_OSByteOrder.h rename to lib/libc/include/x86_64-macos.10-none/libkern/i386/_OSByteOrder.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/libproc.h b/lib/libc/include/x86_64-macos.10-none/libproc.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/libproc.h rename to lib/libc/include/x86_64-macos.10-none/libproc.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach-o/compact_unwind_encoding.h b/lib/libc/include/x86_64-macos.10-none/mach-o/compact_unwind_encoding.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach-o/compact_unwind_encoding.h rename to lib/libc/include/x86_64-macos.10-none/mach-o/compact_unwind_encoding.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach-o/dyld.h b/lib/libc/include/x86_64-macos.10-none/mach-o/dyld.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach-o/dyld.h rename to lib/libc/include/x86_64-macos.10-none/mach-o/dyld.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach-o/loader.h b/lib/libc/include/x86_64-macos.10-none/mach-o/loader.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach-o/loader.h rename to lib/libc/include/x86_64-macos.10-none/mach-o/loader.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/exception_types.h b/lib/libc/include/x86_64-macos.10-none/mach/exception_types.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/exception_types.h rename to lib/libc/include/x86_64-macos.10-none/mach/exception_types.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/host_special_ports.h b/lib/libc/include/x86_64-macos.10-none/mach/host_special_ports.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/host_special_ports.h rename to lib/libc/include/x86_64-macos.10-none/mach/host_special_ports.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/i386/_structs.h b/lib/libc/include/x86_64-macos.10-none/mach/i386/_structs.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/i386/_structs.h rename to lib/libc/include/x86_64-macos.10-none/mach/i386/_structs.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/i386/boolean.h b/lib/libc/include/x86_64-macos.10-none/mach/i386/boolean.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/i386/boolean.h rename to lib/libc/include/x86_64-macos.10-none/mach/i386/boolean.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/i386/exception.h b/lib/libc/include/x86_64-macos.10-none/mach/i386/exception.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/i386/exception.h rename to lib/libc/include/x86_64-macos.10-none/mach/i386/exception.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/i386/fp_reg.h b/lib/libc/include/x86_64-macos.10-none/mach/i386/fp_reg.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/i386/fp_reg.h rename to lib/libc/include/x86_64-macos.10-none/mach/i386/fp_reg.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/i386/kern_return.h b/lib/libc/include/x86_64-macos.10-none/mach/i386/kern_return.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/i386/kern_return.h rename to lib/libc/include/x86_64-macos.10-none/mach/i386/kern_return.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/i386/processor_info.h b/lib/libc/include/x86_64-macos.10-none/mach/i386/processor_info.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/i386/processor_info.h rename to lib/libc/include/x86_64-macos.10-none/mach/i386/processor_info.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/i386/rpc.h b/lib/libc/include/x86_64-macos.10-none/mach/i386/rpc.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/i386/rpc.h rename to lib/libc/include/x86_64-macos.10-none/mach/i386/rpc.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/i386/thread_state.h b/lib/libc/include/x86_64-macos.10-none/mach/i386/thread_state.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/i386/thread_state.h rename to lib/libc/include/x86_64-macos.10-none/mach/i386/thread_state.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/i386/thread_status.h b/lib/libc/include/x86_64-macos.10-none/mach/i386/thread_status.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/i386/thread_status.h rename to lib/libc/include/x86_64-macos.10-none/mach/i386/thread_status.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/i386/vm_param.h b/lib/libc/include/x86_64-macos.10-none/mach/i386/vm_param.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/i386/vm_param.h rename to lib/libc/include/x86_64-macos.10-none/mach/i386/vm_param.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/i386/vm_types.h b/lib/libc/include/x86_64-macos.10-none/mach/i386/vm_types.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/i386/vm_types.h rename to lib/libc/include/x86_64-macos.10-none/mach/i386/vm_types.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/kern_return.h b/lib/libc/include/x86_64-macos.10-none/mach/kern_return.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/kern_return.h rename to lib/libc/include/x86_64-macos.10-none/mach/kern_return.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/mach_init.h b/lib/libc/include/x86_64-macos.10-none/mach/mach_init.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/mach_init.h rename to lib/libc/include/x86_64-macos.10-none/mach/mach_init.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/mach_port.h b/lib/libc/include/x86_64-macos.10-none/mach/mach_port.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/mach_port.h rename to lib/libc/include/x86_64-macos.10-none/mach/mach_port.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/mach_traps.h b/lib/libc/include/x86_64-macos.10-none/mach/mach_traps.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/mach_traps.h rename to lib/libc/include/x86_64-macos.10-none/mach/mach_traps.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/mach_types.h b/lib/libc/include/x86_64-macos.10-none/mach/mach_types.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/mach_types.h rename to lib/libc/include/x86_64-macos.10-none/mach/mach_types.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/machine.h b/lib/libc/include/x86_64-macos.10-none/mach/machine.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/machine.h rename to lib/libc/include/x86_64-macos.10-none/mach/machine.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/machine/_structs.h b/lib/libc/include/x86_64-macos.10-none/mach/machine/_structs.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/machine/_structs.h rename to lib/libc/include/x86_64-macos.10-none/mach/machine/_structs.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/machine/boolean.h b/lib/libc/include/x86_64-macos.10-none/mach/machine/boolean.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/machine/boolean.h rename to lib/libc/include/x86_64-macos.10-none/mach/machine/boolean.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/machine/exception.h b/lib/libc/include/x86_64-macos.10-none/mach/machine/exception.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/machine/exception.h rename to lib/libc/include/x86_64-macos.10-none/mach/machine/exception.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/machine/kern_return.h b/lib/libc/include/x86_64-macos.10-none/mach/machine/kern_return.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/machine/kern_return.h rename to lib/libc/include/x86_64-macos.10-none/mach/machine/kern_return.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/machine/processor_info.h b/lib/libc/include/x86_64-macos.10-none/mach/machine/processor_info.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/machine/processor_info.h rename to lib/libc/include/x86_64-macos.10-none/mach/machine/processor_info.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/machine/rpc.h b/lib/libc/include/x86_64-macos.10-none/mach/machine/rpc.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/machine/rpc.h rename to lib/libc/include/x86_64-macos.10-none/mach/machine/rpc.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/machine/thread_state.h b/lib/libc/include/x86_64-macos.10-none/mach/machine/thread_state.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/machine/thread_state.h rename to lib/libc/include/x86_64-macos.10-none/mach/machine/thread_state.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/machine/thread_status.h b/lib/libc/include/x86_64-macos.10-none/mach/machine/thread_status.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/machine/thread_status.h rename to lib/libc/include/x86_64-macos.10-none/mach/machine/thread_status.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/machine/vm_param.h b/lib/libc/include/x86_64-macos.10-none/mach/machine/vm_param.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/machine/vm_param.h rename to lib/libc/include/x86_64-macos.10-none/mach/machine/vm_param.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/machine/vm_types.h b/lib/libc/include/x86_64-macos.10-none/mach/machine/vm_types.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/machine/vm_types.h rename to lib/libc/include/x86_64-macos.10-none/mach/machine/vm_types.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/memory_object_types.h b/lib/libc/include/x86_64-macos.10-none/mach/memory_object_types.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/memory_object_types.h rename to lib/libc/include/x86_64-macos.10-none/mach/memory_object_types.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/message.h b/lib/libc/include/x86_64-macos.10-none/mach/message.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/message.h rename to lib/libc/include/x86_64-macos.10-none/mach/message.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/port.h b/lib/libc/include/x86_64-macos.10-none/mach/port.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/port.h rename to lib/libc/include/x86_64-macos.10-none/mach/port.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/processor_set.h b/lib/libc/include/x86_64-macos.10-none/mach/processor_set.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/processor_set.h rename to lib/libc/include/x86_64-macos.10-none/mach/processor_set.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/task.h b/lib/libc/include/x86_64-macos.10-none/mach/task.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/task.h rename to lib/libc/include/x86_64-macos.10-none/mach/task.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/task_info.h b/lib/libc/include/x86_64-macos.10-none/mach/task_info.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/task_info.h rename to lib/libc/include/x86_64-macos.10-none/mach/task_info.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/task_policy.h b/lib/libc/include/x86_64-macos.10-none/mach/task_policy.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/task_policy.h rename to lib/libc/include/x86_64-macos.10-none/mach/task_policy.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/task_special_ports.h b/lib/libc/include/x86_64-macos.10-none/mach/task_special_ports.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/task_special_ports.h rename to lib/libc/include/x86_64-macos.10-none/mach/task_special_ports.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/thread_act.h b/lib/libc/include/x86_64-macos.10-none/mach/thread_act.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/thread_act.h rename to lib/libc/include/x86_64-macos.10-none/mach/thread_act.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/thread_special_ports.h b/lib/libc/include/x86_64-macos.10-none/mach/thread_special_ports.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/thread_special_ports.h rename to lib/libc/include/x86_64-macos.10-none/mach/thread_special_ports.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/thread_status.h b/lib/libc/include/x86_64-macos.10-none/mach/thread_status.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/thread_status.h rename to lib/libc/include/x86_64-macos.10-none/mach/thread_status.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/vm_map.h b/lib/libc/include/x86_64-macos.10-none/mach/vm_map.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/vm_map.h rename to lib/libc/include/x86_64-macos.10-none/mach/vm_map.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/vm_param.h b/lib/libc/include/x86_64-macos.10-none/mach/vm_param.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/vm_param.h rename to lib/libc/include/x86_64-macos.10-none/mach/vm_param.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/vm_prot.h b/lib/libc/include/x86_64-macos.10-none/mach/vm_prot.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/vm_prot.h rename to lib/libc/include/x86_64-macos.10-none/mach/vm_prot.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/vm_statistics.h b/lib/libc/include/x86_64-macos.10-none/mach/vm_statistics.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/vm_statistics.h rename to lib/libc/include/x86_64-macos.10-none/mach/vm_statistics.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach/vm_types.h b/lib/libc/include/x86_64-macos.10-none/mach/vm_types.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach/vm_types.h rename to lib/libc/include/x86_64-macos.10-none/mach/vm_types.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/mach_debug/ipc_info.h b/lib/libc/include/x86_64-macos.10-none/mach_debug/ipc_info.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/mach_debug/ipc_info.h rename to lib/libc/include/x86_64-macos.10-none/mach_debug/ipc_info.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/machine/_mcontext.h b/lib/libc/include/x86_64-macos.10-none/machine/_mcontext.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/machine/_mcontext.h rename to lib/libc/include/x86_64-macos.10-none/machine/_mcontext.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/machine/_param.h b/lib/libc/include/x86_64-macos.10-none/machine/_param.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/machine/_param.h rename to lib/libc/include/x86_64-macos.10-none/machine/_param.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/machine/_types.h b/lib/libc/include/x86_64-macos.10-none/machine/_types.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/machine/_types.h rename to lib/libc/include/x86_64-macos.10-none/machine/_types.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/machine/endian.h b/lib/libc/include/x86_64-macos.10-none/machine/endian.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/machine/endian.h rename to lib/libc/include/x86_64-macos.10-none/machine/endian.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/machine/limits.h b/lib/libc/include/x86_64-macos.10-none/machine/limits.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/machine/limits.h rename to lib/libc/include/x86_64-macos.10-none/machine/limits.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/machine/param.h b/lib/libc/include/x86_64-macos.10-none/machine/param.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/machine/param.h rename to lib/libc/include/x86_64-macos.10-none/machine/param.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/machine/signal.h b/lib/libc/include/x86_64-macos.10-none/machine/signal.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/machine/signal.h rename to lib/libc/include/x86_64-macos.10-none/machine/signal.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/machine/types.h b/lib/libc/include/x86_64-macos.10-none/machine/types.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/machine/types.h rename to lib/libc/include/x86_64-macos.10-none/machine/types.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/malloc/_malloc.h b/lib/libc/include/x86_64-macos.10-none/malloc/_malloc.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/malloc/_malloc.h rename to lib/libc/include/x86_64-macos.10-none/malloc/_malloc.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/malloc/malloc.h b/lib/libc/include/x86_64-macos.10-none/malloc/malloc.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/malloc/malloc.h rename to lib/libc/include/x86_64-macos.10-none/malloc/malloc.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/math.h b/lib/libc/include/x86_64-macos.10-none/math.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/math.h rename to lib/libc/include/x86_64-macos.10-none/math.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/net/if.h b/lib/libc/include/x86_64-macos.10-none/net/if.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/net/if.h rename to lib/libc/include/x86_64-macos.10-none/net/if.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/net/if_var.h b/lib/libc/include/x86_64-macos.10-none/net/if_var.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/net/if_var.h rename to lib/libc/include/x86_64-macos.10-none/net/if_var.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/net/route.h b/lib/libc/include/x86_64-macos.10-none/net/route.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/net/route.h rename to lib/libc/include/x86_64-macos.10-none/net/route.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/netinet/in.h b/lib/libc/include/x86_64-macos.10-none/netinet/in.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/netinet/in.h rename to lib/libc/include/x86_64-macos.10-none/netinet/in.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/netinet/tcp.h b/lib/libc/include/x86_64-macos.10-none/netinet/tcp.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/netinet/tcp.h rename to lib/libc/include/x86_64-macos.10-none/netinet/tcp.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/netinet6/in6.h b/lib/libc/include/x86_64-macos.10-none/netinet6/in6.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/netinet6/in6.h rename to lib/libc/include/x86_64-macos.10-none/netinet6/in6.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/objc/objc-api.h b/lib/libc/include/x86_64-macos.10-none/objc/objc-api.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/objc/objc-api.h rename to lib/libc/include/x86_64-macos.10-none/objc/objc-api.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/objc/runtime.h b/lib/libc/include/x86_64-macos.10-none/objc/runtime.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/objc/runtime.h rename to lib/libc/include/x86_64-macos.10-none/objc/runtime.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/os/base.h b/lib/libc/include/x86_64-macos.10-none/os/base.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/os/base.h rename to lib/libc/include/x86_64-macos.10-none/os/base.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/os/object.h b/lib/libc/include/x86_64-macos.10-none/os/object.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/os/object.h rename to lib/libc/include/x86_64-macos.10-none/os/object.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/pthread.h b/lib/libc/include/x86_64-macos.10-none/pthread.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/pthread.h rename to lib/libc/include/x86_64-macos.10-none/pthread.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/pthread/sched.h b/lib/libc/include/x86_64-macos.10-none/pthread/sched.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/pthread/sched.h rename to lib/libc/include/x86_64-macos.10-none/pthread/sched.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/pthread_impl.h b/lib/libc/include/x86_64-macos.10-none/pthread_impl.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/pthread_impl.h rename to lib/libc/include/x86_64-macos.10-none/pthread_impl.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sched.h b/lib/libc/include/x86_64-macos.10-none/sched.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sched.h rename to lib/libc/include/x86_64-macos.10-none/sched.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/signal.h b/lib/libc/include/x86_64-macos.10-none/signal.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/signal.h rename to lib/libc/include/x86_64-macos.10-none/signal.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/simd/common.h b/lib/libc/include/x86_64-macos.10-none/simd/common.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/simd/common.h rename to lib/libc/include/x86_64-macos.10-none/simd/common.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/simd/conversion.h b/lib/libc/include/x86_64-macos.10-none/simd/conversion.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/simd/conversion.h rename to lib/libc/include/x86_64-macos.10-none/simd/conversion.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/simd/logic.h b/lib/libc/include/x86_64-macos.10-none/simd/logic.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/simd/logic.h rename to lib/libc/include/x86_64-macos.10-none/simd/logic.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/simd/math.h b/lib/libc/include/x86_64-macos.10-none/simd/math.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/simd/math.h rename to lib/libc/include/x86_64-macos.10-none/simd/math.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/simd/packed.h b/lib/libc/include/x86_64-macos.10-none/simd/packed.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/simd/packed.h rename to lib/libc/include/x86_64-macos.10-none/simd/packed.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/simd/quaternion.h b/lib/libc/include/x86_64-macos.10-none/simd/quaternion.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/simd/quaternion.h rename to lib/libc/include/x86_64-macos.10-none/simd/quaternion.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/spawn.h b/lib/libc/include/x86_64-macos.10-none/spawn.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/spawn.h rename to lib/libc/include/x86_64-macos.10-none/spawn.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/stdio.h b/lib/libc/include/x86_64-macos.10-none/stdio.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/stdio.h rename to lib/libc/include/x86_64-macos.10-none/stdio.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/stdlib.h b/lib/libc/include/x86_64-macos.10-none/stdlib.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/stdlib.h rename to lib/libc/include/x86_64-macos.10-none/stdlib.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/string.h b/lib/libc/include/x86_64-macos.10-none/string.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/string.h rename to lib/libc/include/x86_64-macos.10-none/string.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/_pthread/_pthread_attr_t.h b/lib/libc/include/x86_64-macos.10-none/sys/_pthread/_pthread_attr_t.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/_pthread/_pthread_attr_t.h rename to lib/libc/include/x86_64-macos.10-none/sys/_pthread/_pthread_attr_t.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/_pthread/_pthread_cond_t.h b/lib/libc/include/x86_64-macos.10-none/sys/_pthread/_pthread_cond_t.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/_pthread/_pthread_cond_t.h rename to lib/libc/include/x86_64-macos.10-none/sys/_pthread/_pthread_cond_t.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/_pthread/_pthread_condattr_t.h b/lib/libc/include/x86_64-macos.10-none/sys/_pthread/_pthread_condattr_t.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/_pthread/_pthread_condattr_t.h rename to lib/libc/include/x86_64-macos.10-none/sys/_pthread/_pthread_condattr_t.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/_pthread/_pthread_rwlock_t.h b/lib/libc/include/x86_64-macos.10-none/sys/_pthread/_pthread_rwlock_t.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/_pthread/_pthread_rwlock_t.h rename to lib/libc/include/x86_64-macos.10-none/sys/_pthread/_pthread_rwlock_t.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/_pthread/_pthread_rwlockattr_t.h b/lib/libc/include/x86_64-macos.10-none/sys/_pthread/_pthread_rwlockattr_t.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/_pthread/_pthread_rwlockattr_t.h rename to lib/libc/include/x86_64-macos.10-none/sys/_pthread/_pthread_rwlockattr_t.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/_pthread/_pthread_t.h b/lib/libc/include/x86_64-macos.10-none/sys/_pthread/_pthread_t.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/_pthread/_pthread_t.h rename to lib/libc/include/x86_64-macos.10-none/sys/_pthread/_pthread_t.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/_pthread/_pthread_types.h b/lib/libc/include/x86_64-macos.10-none/sys/_pthread/_pthread_types.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/_pthread/_pthread_types.h rename to lib/libc/include/x86_64-macos.10-none/sys/_pthread/_pthread_types.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/_select.h b/lib/libc/include/x86_64-macos.10-none/sys/_select.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/_select.h rename to lib/libc/include/x86_64-macos.10-none/sys/_select.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/_symbol_aliasing.h b/lib/libc/include/x86_64-macos.10-none/sys/_symbol_aliasing.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/_symbol_aliasing.h rename to lib/libc/include/x86_64-macos.10-none/sys/_symbol_aliasing.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/_types/_fd_def.h b/lib/libc/include/x86_64-macos.10-none/sys/_types/_fd_def.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/_types/_fd_def.h rename to lib/libc/include/x86_64-macos.10-none/sys/_types/_fd_def.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/_types/_int8_t.h b/lib/libc/include/x86_64-macos.10-none/sys/_types/_int8_t.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/_types/_int8_t.h rename to lib/libc/include/x86_64-macos.10-none/sys/_types/_int8_t.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/_types/_ucontext.h b/lib/libc/include/x86_64-macos.10-none/sys/_types/_ucontext.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/_types/_ucontext.h rename to lib/libc/include/x86_64-macos.10-none/sys/_types/_ucontext.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/acl.h b/lib/libc/include/x86_64-macos.10-none/sys/acl.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/acl.h rename to lib/libc/include/x86_64-macos.10-none/sys/acl.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/attr.h b/lib/libc/include/x86_64-macos.10-none/sys/attr.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/attr.h rename to lib/libc/include/x86_64-macos.10-none/sys/attr.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/cdefs.h b/lib/libc/include/x86_64-macos.10-none/sys/cdefs.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/cdefs.h rename to lib/libc/include/x86_64-macos.10-none/sys/cdefs.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/event.h b/lib/libc/include/x86_64-macos.10-none/sys/event.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/event.h rename to lib/libc/include/x86_64-macos.10-none/sys/event.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/fcntl.h b/lib/libc/include/x86_64-macos.10-none/sys/fcntl.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/fcntl.h rename to lib/libc/include/x86_64-macos.10-none/sys/fcntl.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/ioccom.h b/lib/libc/include/x86_64-macos.10-none/sys/ioccom.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/ioccom.h rename to lib/libc/include/x86_64-macos.10-none/sys/ioccom.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/kauth.h b/lib/libc/include/x86_64-macos.10-none/sys/kauth.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/kauth.h rename to lib/libc/include/x86_64-macos.10-none/sys/kauth.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/mman.h b/lib/libc/include/x86_64-macos.10-none/sys/mman.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/mman.h rename to lib/libc/include/x86_64-macos.10-none/sys/mman.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/mount.h b/lib/libc/include/x86_64-macos.10-none/sys/mount.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/mount.h rename to lib/libc/include/x86_64-macos.10-none/sys/mount.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/param.h b/lib/libc/include/x86_64-macos.10-none/sys/param.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/param.h rename to lib/libc/include/x86_64-macos.10-none/sys/param.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/proc.h b/lib/libc/include/x86_64-macos.10-none/sys/proc.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/proc.h rename to lib/libc/include/x86_64-macos.10-none/sys/proc.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/proc_info.h b/lib/libc/include/x86_64-macos.10-none/sys/proc_info.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/proc_info.h rename to lib/libc/include/x86_64-macos.10-none/sys/proc_info.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/resource.h b/lib/libc/include/x86_64-macos.10-none/sys/resource.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/resource.h rename to lib/libc/include/x86_64-macos.10-none/sys/resource.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/shm.h b/lib/libc/include/x86_64-macos.10-none/sys/shm.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/shm.h rename to lib/libc/include/x86_64-macos.10-none/sys/shm.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/socket.h b/lib/libc/include/x86_64-macos.10-none/sys/socket.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/socket.h rename to lib/libc/include/x86_64-macos.10-none/sys/socket.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/sockio.h b/lib/libc/include/x86_64-macos.10-none/sys/sockio.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/sockio.h rename to lib/libc/include/x86_64-macos.10-none/sys/sockio.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/spawn.h b/lib/libc/include/x86_64-macos.10-none/sys/spawn.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/spawn.h rename to lib/libc/include/x86_64-macos.10-none/sys/spawn.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/stat.h b/lib/libc/include/x86_64-macos.10-none/sys/stat.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/stat.h rename to lib/libc/include/x86_64-macos.10-none/sys/stat.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/sysctl.h b/lib/libc/include/x86_64-macos.10-none/sys/sysctl.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/sysctl.h rename to lib/libc/include/x86_64-macos.10-none/sys/sysctl.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/syslimits.h b/lib/libc/include/x86_64-macos.10-none/sys/syslimits.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/syslimits.h rename to lib/libc/include/x86_64-macos.10-none/sys/syslimits.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/ucontext.h b/lib/libc/include/x86_64-macos.10-none/sys/ucontext.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/ucontext.h rename to lib/libc/include/x86_64-macos.10-none/sys/ucontext.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/uio.h b/lib/libc/include/x86_64-macos.10-none/sys/uio.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/uio.h rename to lib/libc/include/x86_64-macos.10-none/sys/uio.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/sys/un.h b/lib/libc/include/x86_64-macos.10-none/sys/un.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/sys/un.h rename to lib/libc/include/x86_64-macos.10-none/sys/un.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/time.h b/lib/libc/include/x86_64-macos.10-none/time.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/time.h rename to lib/libc/include/x86_64-macos.10-none/time.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/ucontext.h b/lib/libc/include/x86_64-macos.10-none/ucontext.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/ucontext.h rename to lib/libc/include/x86_64-macos.10-none/ucontext.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/xlocale/_inttypes.h b/lib/libc/include/x86_64-macos.10-none/xlocale/_inttypes.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/xlocale/_inttypes.h rename to lib/libc/include/x86_64-macos.10-none/xlocale/_inttypes.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/xlocale/_wchar.h b/lib/libc/include/x86_64-macos.10-none/xlocale/_wchar.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/xlocale/_wchar.h rename to lib/libc/include/x86_64-macos.10-none/xlocale/_wchar.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/xpc/availability.h b/lib/libc/include/x86_64-macos.10-none/xpc/availability.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/xpc/availability.h rename to lib/libc/include/x86_64-macos.10-none/xpc/availability.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/xpc/base.h b/lib/libc/include/x86_64-macos.10-none/xpc/base.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/xpc/base.h rename to lib/libc/include/x86_64-macos.10-none/xpc/base.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/xpc/connection.h b/lib/libc/include/x86_64-macos.10-none/xpc/connection.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/xpc/connection.h rename to lib/libc/include/x86_64-macos.10-none/xpc/connection.h diff --git a/lib/libc/include/x86_64-macos.10-gnu/xpc/xpc.h b/lib/libc/include/x86_64-macos.10-none/xpc/xpc.h similarity index 100% rename from lib/libc/include/x86_64-macos.10-gnu/xpc/xpc.h rename to lib/libc/include/x86_64-macos.10-none/xpc/xpc.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/i386/_limits.h b/lib/libc/include/x86_64-macos.11-none/i386/_limits.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/i386/_limits.h rename to lib/libc/include/x86_64-macos.11-none/i386/_limits.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/i386/_mcontext.h b/lib/libc/include/x86_64-macos.11-none/i386/_mcontext.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/i386/_mcontext.h rename to lib/libc/include/x86_64-macos.11-none/i386/_mcontext.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/i386/_param.h b/lib/libc/include/x86_64-macos.11-none/i386/_param.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/i386/_param.h rename to lib/libc/include/x86_64-macos.11-none/i386/_param.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/i386/_types.h b/lib/libc/include/x86_64-macos.11-none/i386/_types.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/i386/_types.h rename to lib/libc/include/x86_64-macos.11-none/i386/_types.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/i386/eflags.h b/lib/libc/include/x86_64-macos.11-none/i386/eflags.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/i386/eflags.h rename to lib/libc/include/x86_64-macos.11-none/i386/eflags.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/i386/endian.h b/lib/libc/include/x86_64-macos.11-none/i386/endian.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/i386/endian.h rename to lib/libc/include/x86_64-macos.11-none/i386/endian.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/i386/limits.h b/lib/libc/include/x86_64-macos.11-none/i386/limits.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/i386/limits.h rename to lib/libc/include/x86_64-macos.11-none/i386/limits.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/i386/param.h b/lib/libc/include/x86_64-macos.11-none/i386/param.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/i386/param.h rename to lib/libc/include/x86_64-macos.11-none/i386/param.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/i386/signal.h b/lib/libc/include/x86_64-macos.11-none/i386/signal.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/i386/signal.h rename to lib/libc/include/x86_64-macos.11-none/i386/signal.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/i386/types.h b/lib/libc/include/x86_64-macos.11-none/i386/types.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/i386/types.h rename to lib/libc/include/x86_64-macos.11-none/i386/types.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/libkern/OSAtomic.h b/lib/libc/include/x86_64-macos.11-none/libkern/OSAtomic.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/libkern/OSAtomic.h rename to lib/libc/include/x86_64-macos.11-none/libkern/OSAtomic.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/libkern/OSAtomicDeprecated.h b/lib/libc/include/x86_64-macos.11-none/libkern/OSAtomicDeprecated.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/libkern/OSAtomicDeprecated.h rename to lib/libc/include/x86_64-macos.11-none/libkern/OSAtomicDeprecated.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/libkern/OSAtomicQueue.h b/lib/libc/include/x86_64-macos.11-none/libkern/OSAtomicQueue.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/libkern/OSAtomicQueue.h rename to lib/libc/include/x86_64-macos.11-none/libkern/OSAtomicQueue.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/libkern/OSSpinLockDeprecated.h b/lib/libc/include/x86_64-macos.11-none/libkern/OSSpinLockDeprecated.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/libkern/OSSpinLockDeprecated.h rename to lib/libc/include/x86_64-macos.11-none/libkern/OSSpinLockDeprecated.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/libkern/i386/OSByteOrder.h b/lib/libc/include/x86_64-macos.11-none/libkern/i386/OSByteOrder.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/libkern/i386/OSByteOrder.h rename to lib/libc/include/x86_64-macos.11-none/libkern/i386/OSByteOrder.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/libkern/i386/_OSByteOrder.h b/lib/libc/include/x86_64-macos.11-none/libkern/i386/_OSByteOrder.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/libkern/i386/_OSByteOrder.h rename to lib/libc/include/x86_64-macos.11-none/libkern/i386/_OSByteOrder.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/mach/i386/_structs.h b/lib/libc/include/x86_64-macos.11-none/mach/i386/_structs.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/mach/i386/_structs.h rename to lib/libc/include/x86_64-macos.11-none/mach/i386/_structs.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/mach/i386/boolean.h b/lib/libc/include/x86_64-macos.11-none/mach/i386/boolean.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/mach/i386/boolean.h rename to lib/libc/include/x86_64-macos.11-none/mach/i386/boolean.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/mach/i386/exception.h b/lib/libc/include/x86_64-macos.11-none/mach/i386/exception.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/mach/i386/exception.h rename to lib/libc/include/x86_64-macos.11-none/mach/i386/exception.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/mach/i386/fp_reg.h b/lib/libc/include/x86_64-macos.11-none/mach/i386/fp_reg.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/mach/i386/fp_reg.h rename to lib/libc/include/x86_64-macos.11-none/mach/i386/fp_reg.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/mach/i386/kern_return.h b/lib/libc/include/x86_64-macos.11-none/mach/i386/kern_return.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/mach/i386/kern_return.h rename to lib/libc/include/x86_64-macos.11-none/mach/i386/kern_return.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/mach/i386/processor_info.h b/lib/libc/include/x86_64-macos.11-none/mach/i386/processor_info.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/mach/i386/processor_info.h rename to lib/libc/include/x86_64-macos.11-none/mach/i386/processor_info.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/mach/i386/rpc.h b/lib/libc/include/x86_64-macos.11-none/mach/i386/rpc.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/mach/i386/rpc.h rename to lib/libc/include/x86_64-macos.11-none/mach/i386/rpc.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/mach/i386/thread_state.h b/lib/libc/include/x86_64-macos.11-none/mach/i386/thread_state.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/mach/i386/thread_state.h rename to lib/libc/include/x86_64-macos.11-none/mach/i386/thread_state.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/mach/i386/thread_status.h b/lib/libc/include/x86_64-macos.11-none/mach/i386/thread_status.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/mach/i386/thread_status.h rename to lib/libc/include/x86_64-macos.11-none/mach/i386/thread_status.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/mach/i386/vm_param.h b/lib/libc/include/x86_64-macos.11-none/mach/i386/vm_param.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/mach/i386/vm_param.h rename to lib/libc/include/x86_64-macos.11-none/mach/i386/vm_param.h diff --git a/lib/libc/include/x86_64-macos.11-gnu/mach/i386/vm_types.h b/lib/libc/include/x86_64-macos.11-none/mach/i386/vm_types.h similarity index 100% rename from lib/libc/include/x86_64-macos.11-gnu/mach/i386/vm_types.h rename to lib/libc/include/x86_64-macos.11-none/mach/i386/vm_types.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/i386/_limits.h b/lib/libc/include/x86_64-macos.12-none/i386/_limits.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/i386/_limits.h rename to lib/libc/include/x86_64-macos.12-none/i386/_limits.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/i386/_mcontext.h b/lib/libc/include/x86_64-macos.12-none/i386/_mcontext.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/i386/_mcontext.h rename to lib/libc/include/x86_64-macos.12-none/i386/_mcontext.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/i386/_param.h b/lib/libc/include/x86_64-macos.12-none/i386/_param.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/i386/_param.h rename to lib/libc/include/x86_64-macos.12-none/i386/_param.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/i386/_types.h b/lib/libc/include/x86_64-macos.12-none/i386/_types.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/i386/_types.h rename to lib/libc/include/x86_64-macos.12-none/i386/_types.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/i386/eflags.h b/lib/libc/include/x86_64-macos.12-none/i386/eflags.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/i386/eflags.h rename to lib/libc/include/x86_64-macos.12-none/i386/eflags.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/i386/endian.h b/lib/libc/include/x86_64-macos.12-none/i386/endian.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/i386/endian.h rename to lib/libc/include/x86_64-macos.12-none/i386/endian.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/i386/limits.h b/lib/libc/include/x86_64-macos.12-none/i386/limits.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/i386/limits.h rename to lib/libc/include/x86_64-macos.12-none/i386/limits.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/i386/param.h b/lib/libc/include/x86_64-macos.12-none/i386/param.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/i386/param.h rename to lib/libc/include/x86_64-macos.12-none/i386/param.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/i386/signal.h b/lib/libc/include/x86_64-macos.12-none/i386/signal.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/i386/signal.h rename to lib/libc/include/x86_64-macos.12-none/i386/signal.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/i386/types.h b/lib/libc/include/x86_64-macos.12-none/i386/types.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/i386/types.h rename to lib/libc/include/x86_64-macos.12-none/i386/types.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/libkern/OSAtomic.h b/lib/libc/include/x86_64-macos.12-none/libkern/OSAtomic.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/libkern/OSAtomic.h rename to lib/libc/include/x86_64-macos.12-none/libkern/OSAtomic.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/libkern/OSAtomicDeprecated.h b/lib/libc/include/x86_64-macos.12-none/libkern/OSAtomicDeprecated.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/libkern/OSAtomicDeprecated.h rename to lib/libc/include/x86_64-macos.12-none/libkern/OSAtomicDeprecated.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/libkern/OSAtomicQueue.h b/lib/libc/include/x86_64-macos.12-none/libkern/OSAtomicQueue.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/libkern/OSAtomicQueue.h rename to lib/libc/include/x86_64-macos.12-none/libkern/OSAtomicQueue.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/libkern/OSSpinLockDeprecated.h b/lib/libc/include/x86_64-macos.12-none/libkern/OSSpinLockDeprecated.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/libkern/OSSpinLockDeprecated.h rename to lib/libc/include/x86_64-macos.12-none/libkern/OSSpinLockDeprecated.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/libkern/i386/OSByteOrder.h b/lib/libc/include/x86_64-macos.12-none/libkern/i386/OSByteOrder.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/libkern/i386/OSByteOrder.h rename to lib/libc/include/x86_64-macos.12-none/libkern/i386/OSByteOrder.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/libkern/i386/_OSByteOrder.h b/lib/libc/include/x86_64-macos.12-none/libkern/i386/_OSByteOrder.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/libkern/i386/_OSByteOrder.h rename to lib/libc/include/x86_64-macos.12-none/libkern/i386/_OSByteOrder.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/mach/i386/_structs.h b/lib/libc/include/x86_64-macos.12-none/mach/i386/_structs.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/mach/i386/_structs.h rename to lib/libc/include/x86_64-macos.12-none/mach/i386/_structs.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/mach/i386/boolean.h b/lib/libc/include/x86_64-macos.12-none/mach/i386/boolean.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/mach/i386/boolean.h rename to lib/libc/include/x86_64-macos.12-none/mach/i386/boolean.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/mach/i386/exception.h b/lib/libc/include/x86_64-macos.12-none/mach/i386/exception.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/mach/i386/exception.h rename to lib/libc/include/x86_64-macos.12-none/mach/i386/exception.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/mach/i386/fp_reg.h b/lib/libc/include/x86_64-macos.12-none/mach/i386/fp_reg.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/mach/i386/fp_reg.h rename to lib/libc/include/x86_64-macos.12-none/mach/i386/fp_reg.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/mach/i386/kern_return.h b/lib/libc/include/x86_64-macos.12-none/mach/i386/kern_return.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/mach/i386/kern_return.h rename to lib/libc/include/x86_64-macos.12-none/mach/i386/kern_return.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/mach/i386/processor_info.h b/lib/libc/include/x86_64-macos.12-none/mach/i386/processor_info.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/mach/i386/processor_info.h rename to lib/libc/include/x86_64-macos.12-none/mach/i386/processor_info.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/mach/i386/rpc.h b/lib/libc/include/x86_64-macos.12-none/mach/i386/rpc.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/mach/i386/rpc.h rename to lib/libc/include/x86_64-macos.12-none/mach/i386/rpc.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/mach/i386/thread_state.h b/lib/libc/include/x86_64-macos.12-none/mach/i386/thread_state.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/mach/i386/thread_state.h rename to lib/libc/include/x86_64-macos.12-none/mach/i386/thread_state.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/mach/i386/thread_status.h b/lib/libc/include/x86_64-macos.12-none/mach/i386/thread_status.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/mach/i386/thread_status.h rename to lib/libc/include/x86_64-macos.12-none/mach/i386/thread_status.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/mach/i386/vm_param.h b/lib/libc/include/x86_64-macos.12-none/mach/i386/vm_param.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/mach/i386/vm_param.h rename to lib/libc/include/x86_64-macos.12-none/mach/i386/vm_param.h diff --git a/lib/libc/include/x86_64-macos.12-gnu/mach/i386/vm_types.h b/lib/libc/include/x86_64-macos.12-none/mach/i386/vm_types.h similarity index 100% rename from lib/libc/include/x86_64-macos.12-gnu/mach/i386/vm_types.h rename to lib/libc/include/x86_64-macos.12-none/mach/i386/vm_types.h diff --git a/lib/std/target.zig b/lib/std/target.zig index b11408cfce..27251f1d64 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -510,11 +510,7 @@ pub const Target = struct { .other, => return .eabi, .openbsd, - .macos, .freebsd, - .ios, - .tvos, - .watchos, .fuchsia, .kfreebsd, .netbsd, @@ -531,6 +527,10 @@ pub const Target = struct { .glsl450, .vulkan, .plan9, // TODO specify abi + .macos, + .ios, + .tvos, + .watchos, => return .none, } } diff --git a/src/Compilation.zig b/src/Compilation.zig index 7ac1e49196..2c15cb95a3 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -4289,7 +4289,7 @@ fn getZigShippedLibCIncludeDirsDarwin(arena: Allocator, zig_lib_dir: []const u8, list[0] = try std.fmt.allocPrint( arena, - "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-gnu", + "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-none", .{ zig_lib_dir, arch_name, os_name }, ); list[1] = try std.fmt.allocPrint( diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 57c8238827..b8b8f50e67 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -303,7 +303,7 @@ const TargetMatcher = struct { }; const os = @tagName(target.os.tag); const abi: ?[]const u8 = switch (target.abi) { - .gnu => null, + .none => null, .simulator => "simulator", else => unreachable, }; diff --git a/src/stage1/target.cpp b/src/stage1/target.cpp index da6565f0be..81377b5d49 100644 --- a/src/stage1/target.cpp +++ b/src/stage1/target.cpp @@ -971,11 +971,7 @@ ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) { case OsOther: return ZigLLVM_EABI; case OsOpenBSD: - case OsMacOSX: case OsFreeBSD: - case OsIOS: - case OsTvOS: - case OsWatchOS: case OsFuchsia: case OsKFreeBSD: case OsNetBSD: @@ -994,6 +990,10 @@ ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) { case OsGLSL450: case OsVulkan: case OsPlan9: + case OsMacOSX: + case OsIOS: + case OsTvOS: + case OsWatchOS: return ZigLLVM_UnknownEnvironment; } zig_unreachable(); diff --git a/src/target.zig b/src/target.zig index c794ea52b0..14af2675d2 100644 --- a/src/target.zig +++ b/src/target.zig @@ -16,8 +16,8 @@ pub const available_libcs = [_]ArchOsAbi{ .{ .arch = .aarch64, .os = .linux, .abi = .gnu }, .{ .arch = .aarch64, .os = .linux, .abi = .musl }, .{ .arch = .aarch64, .os = .windows, .abi = .gnu }, - .{ .arch = .aarch64, .os = .macos, .abi = .gnu, .os_ver = .{ .major = 11, .minor = 0 } }, - .{ .arch = .aarch64, .os = .macos, .abi = .gnu, .os_ver = .{ .major = 12, .minor = 0 } }, + .{ .arch = .aarch64, .os = .macos, .abi = .none, .os_ver = .{ .major = 11, .minor = 0 } }, + .{ .arch = .aarch64, .os = .macos, .abi = .none, .os_ver = .{ .major = 12, .minor = 0 } }, .{ .arch = .armeb, .os = .linux, .abi = .gnueabi }, .{ .arch = .armeb, .os = .linux, .abi = .gnueabihf }, .{ .arch = .armeb, .os = .linux, .abi = .musleabi }, @@ -70,9 +70,9 @@ pub const available_libcs = [_]ArchOsAbi{ .{ .arch = .x86_64, .os = .linux, .abi = .gnux32 }, .{ .arch = .x86_64, .os = .linux, .abi = .musl }, .{ .arch = .x86_64, .os = .windows, .abi = .gnu }, - .{ .arch = .x86_64, .os = .macos, .abi = .gnu, .os_ver = .{ .major = 10, .minor = 0 } }, - .{ .arch = .x86_64, .os = .macos, .abi = .gnu, .os_ver = .{ .major = 11, .minor = 0 } }, - .{ .arch = .x86_64, .os = .macos, .abi = .gnu, .os_ver = .{ .major = 12, .minor = 0 } }, + .{ .arch = .x86_64, .os = .macos, .abi = .none, .os_ver = .{ .major = 10, .minor = 0 } }, + .{ .arch = .x86_64, .os = .macos, .abi = .none, .os_ver = .{ .major = 11, .minor = 0 } }, + .{ .arch = .x86_64, .os = .macos, .abi = .none, .os_ver = .{ .major = 12, .minor = 0 } }, }; pub fn libCGenericName(target: std.Target) [:0]const u8 { From f8a1a2c4a18a2a5f274029c4c59f3a8e83f36b6b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 15 May 2022 17:56:51 +0200 Subject: [PATCH 1602/2031] stage2: append min version to target triple when lowering to LLVM --- src/codegen/llvm.zig | 109 ++++++++++++++++++++++++++----------------- test/tests.zig | 8 ++-- 2 files changed, 70 insertions(+), 47 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index b9c8e5437f..5f071af017 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -86,50 +86,73 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![:0]u8 { .spirv64 => return error.@"LLVM backend does not support SPIR-V", }; - const llvm_os = switch (target.os.tag) { - .freestanding => "unknown", - .ananas => "ananas", - .cloudabi => "cloudabi", - .dragonfly => "dragonfly", - .freebsd => "freebsd", - .fuchsia => "fuchsia", - .ios => "ios", - .kfreebsd => "kfreebsd", - .linux => "linux", - .lv2 => "lv2", - .macos => "macosx", - .netbsd => "netbsd", - .openbsd => "openbsd", - .solaris => "solaris", - .windows => "windows", - .zos => "zos", - .haiku => "haiku", - .minix => "minix", - .rtems => "rtems", - .nacl => "nacl", - .aix => "aix", - .cuda => "cuda", - .nvcl => "nvcl", - .amdhsa => "amdhsa", - .ps4 => "ps4", - .elfiamcu => "elfiamcu", - .tvos => "tvos", - .watchos => "watchos", - .mesa3d => "mesa3d", - .contiki => "contiki", - .amdpal => "amdpal", - .hermit => "hermit", - .hurd => "hurd", - .wasi => "wasi", - .emscripten => "emscripten", - .uefi => "windows", + const llvm_os = blk: { + if (target.os.tag.isDarwin()) { + const min_version = target.os.version_range.semver.min; + const llvm_os = switch (target.os.tag) { + .macos => "macosx", + .ios => "ios", + .tvos => "tvos", + .watchos => "watchos", + else => unreachable, + }; + break :blk try std.fmt.allocPrintZ(allocator, "{s}{d}.{d}.{d}", .{ + llvm_os, + min_version.major, + min_version.minor, + min_version.patch, + }); + } - .opencl, - .glsl450, - .vulkan, - .plan9, - .other, - => "unknown", + const llvm_os = switch (target.os.tag) { + .freestanding => "unknown", + .ananas => "ananas", + .cloudabi => "cloudabi", + .dragonfly => "dragonfly", + .freebsd => "freebsd", + .fuchsia => "fuchsia", + .kfreebsd => "kfreebsd", + .linux => "linux", + .lv2 => "lv2", + .netbsd => "netbsd", + .openbsd => "openbsd", + .solaris => "solaris", + .windows => "windows", + .zos => "zos", + .haiku => "haiku", + .minix => "minix", + .rtems => "rtems", + .nacl => "nacl", + .aix => "aix", + .cuda => "cuda", + .nvcl => "nvcl", + .amdhsa => "amdhsa", + .ps4 => "ps4", + .elfiamcu => "elfiamcu", + .mesa3d => "mesa3d", + .contiki => "contiki", + .amdpal => "amdpal", + .hermit => "hermit", + .hurd => "hurd", + .wasi => "wasi", + .emscripten => "emscripten", + .uefi => "windows", + + .opencl, + .glsl450, + .vulkan, + .plan9, + .other, + => "unknown", + + .macos, + .ios, + .tvos, + .watchos, + => unreachable, + }; + + break :blk llvm_os; }; const llvm_abi = switch (target.abi) { diff --git a/test/tests.zig b/test/tests.zig index e44b190bb4..3666ef1028 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -95,7 +95,7 @@ const test_targets = blk: { .target = .{ .cpu_arch = .aarch64, .os_tag = .macos, - .abi = .gnu, + .abi = .none, }, .backend = .stage2_aarch64, }, @@ -103,7 +103,7 @@ const test_targets = blk: { .target = .{ .cpu_arch = .x86_64, .os_tag = .macos, - .abi = .gnu, + .abi = .none, }, .backend = .stage2_x86_64, }, @@ -337,7 +337,7 @@ const test_targets = blk: { .target = .{ .cpu_arch = .x86_64, .os_tag = .macos, - .abi = .gnu, + .abi = .none, }, }, @@ -345,7 +345,7 @@ const test_targets = blk: { .target = .{ .cpu_arch = .aarch64, .os_tag = .macos, - .abi = .gnu, + .abi = .none, }, }, From e306d04473818ac8b58779aa1ff20b12edb8e94a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 May 2022 16:54:52 +0200 Subject: [PATCH 1603/2031] Return an error when macOS ABI is not {none, simulator, macabi} --- src/Compilation.zig | 9 +++++++++ src/codegen/llvm.zig | 5 ++++- src/link/MachO/Dylib.zig | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 2c15cb95a3..31782e732b 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1719,6 +1719,15 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { const have_bin_emit = comp.bin_file.options.emit != null or comp.whole_bin_sub_path != null; if (have_bin_emit and !comp.bin_file.options.skip_linker_dependencies) { + if (comp.getTarget().isDarwin()) { + switch (comp.getTarget().abi) { + .none, + .simulator, + .macabi, + => {}, + else => return error.LibCUnavailable, + } + } // If we need to build glibc for the target, add work items for it. // We go through the work queue so that building can be done in parallel. if (comp.wantBuildGLibCFromSource()) { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 5f071af017..452bb9b497 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -86,6 +86,9 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![:0]u8 { .spirv64 => return error.@"LLVM backend does not support SPIR-V", }; + var arena = std.heap.ArenaAllocator.init(allocator); + defer arena.deinit(); + const llvm_os = blk: { if (target.os.tag.isDarwin()) { const min_version = target.os.version_range.semver.min; @@ -96,7 +99,7 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![:0]u8 { .watchos => "watchos", else => unreachable, }; - break :blk try std.fmt.allocPrintZ(allocator, "{s}{d}.{d}.{d}", .{ + break :blk try std.fmt.allocPrintZ(arena.allocator(), "{s}{d}.{d}.{d}", .{ llvm_os, min_version.major, min_version.minor, diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index b8b8f50e67..73e7913d71 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -305,6 +305,7 @@ const TargetMatcher = struct { const abi: ?[]const u8 = switch (target.abi) { .none => null, .simulator => "simulator", + .macabi => "maccatalyst", else => unreachable, }; if (abi) |x| { From b56b4428a3a2fd8cf244580f997ec0ed2010fce2 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sun, 22 May 2022 15:36:45 +0200 Subject: [PATCH 1604/2031] stage2 ARM: fix recursive fibonacci Some handling of register_c_flag/register_v_flag was incorrect. --- src/arch/arm/CodeGen.zig | 47 ++++++++++++++---------------- src/arch/arm/Emit.zig | 2 +- test/behavior/underscore.zig | 1 - test/cases/recursive_fibonacci.zig | 2 +- 4 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 095d572f53..75fe8f6403 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -2447,6 +2447,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { .register_c_flag, .register_v_flag, => |reg| { + const reg_lock = self.register_manager.lockRegAssumeUnused(reg); + defer self.register_manager.unlockReg(reg_lock); + switch (index) { 0 => { // get wrapped value: return register @@ -5062,6 +5065,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { } fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { + log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() }); if (typed_value.val.isUndef()) return MCValue{ .undef = {} }; const ptr_bits = self.target.cpu.arch.ptrBitWidth(); @@ -5075,24 +5079,14 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { const target = self.target.*; switch (typed_value.ty.zigTypeTag()) { - .Array => { - return self.lowerUnnamedConst(typed_value); - }, .Pointer => switch (typed_value.ty.ptrSize()) { - .Slice => { - return self.lowerUnnamedConst(typed_value); - }, + .Slice => {}, else => { switch (typed_value.val.tag()) { .int_u64 => { return MCValue{ .immediate = @intCast(u32, typed_value.val.toUnsignedInt(target)) }; }, - .slice => { - return self.lowerUnnamedConst(typed_value); - }, - else => { - return self.fail("TODO codegen more kinds of const pointers", .{}); - }, + else => {}, } }, }, @@ -5115,8 +5109,6 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { .Bool => { return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; }, - .ComptimeInt => unreachable, // semantic analysis prevents this - .ComptimeFloat => unreachable, // semantic analysis prevents this .Optional => { if (typed_value.ty.isPtrLikeOptional()) { if (typed_value.val.isNull()) @@ -5130,7 +5122,6 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { } else if (typed_value.ty.abiSize(self.target.*) == 1) { return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) }; } - return self.fail("TODO non pointer optionals", .{}); }, .Enum => { if (typed_value.val.castTag(.enum_field_index)) |field_index| { @@ -5166,28 +5157,34 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { const error_type = typed_value.ty.errorUnionSet(); const payload_type = typed_value.ty.errorUnionPayload(); - if (typed_value.val.castTag(.eu_payload)) |pl| { + if (typed_value.val.castTag(.eu_payload)) |_| { if (!payload_type.hasRuntimeBits()) { // We use the error type directly as the type. return MCValue{ .immediate = 0 }; } - - _ = pl; - return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty.fmtDebug()}); } else { if (!payload_type.hasRuntimeBits()) { // We use the error type directly as the type. return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); } - - return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty.fmtDebug()}); } }, - .Struct => { - return self.lowerUnnamedConst(typed_value); - }, - else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}), + + .ComptimeInt => unreachable, // semantic analysis prevents this + .ComptimeFloat => unreachable, // semantic analysis prevents this + .Type => unreachable, + .EnumLiteral => unreachable, + .Void => unreachable, + .NoReturn => unreachable, + .Undefined => unreachable, + .Null => unreachable, + .BoundFn => unreachable, + .Opaque => unreachable, + + else => {}, } + + return self.lowerUnnamedConst(typed_value); } const CallMCValues = struct { diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index 8db830bfba..ff2084fea7 100644 --- a/src/arch/arm/Emit.zig +++ b/src/arch/arm/Emit.zig @@ -394,7 +394,7 @@ fn mirDataProcessing(emit: *Emit, inst: Mir.Inst.Index) !void { .orr => try emit.writeInstruction(Instruction.orr(cond, rr_op.rd, rr_op.rn, rr_op.op)), .rsb => try emit.writeInstruction(Instruction.rsb(cond, rr_op.rd, rr_op.rn, rr_op.op)), .sub => try emit.writeInstruction(Instruction.sub(cond, rr_op.rd, rr_op.rn, rr_op.op)), - .subs => try emit.writeInstruction(Instruction.sub(cond, rr_op.rd, rr_op.rn, rr_op.op)), + .subs => try emit.writeInstruction(Instruction.subs(cond, rr_op.rd, rr_op.rn, rr_op.op)), else => unreachable, } } diff --git a/test/behavior/underscore.zig b/test/behavior/underscore.zig index 9305149f5a..dcbe037065 100644 --- a/test/behavior/underscore.zig +++ b/test/behavior/underscore.zig @@ -7,7 +7,6 @@ test "ignore lval with underscore" { } test "ignore lval with underscore (while loop)" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; while (optionalReturnError()) |_| { diff --git a/test/cases/recursive_fibonacci.zig b/test/cases/recursive_fibonacci.zig index a2b8436dd7..24a7f75666 100644 --- a/test/cases/recursive_fibonacci.zig +++ b/test/cases/recursive_fibonacci.zig @@ -20,5 +20,5 @@ fn assert(ok: bool) void { } // run -// target=x86_64-linux,x86_64-macos,wasm32-wasi +// target=x86_64-linux,x86_64-macos,arm-linux,wasm32-wasi // From cbefd354a662800c5bc662773146ce978631f717 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 22 May 2022 11:30:52 +0200 Subject: [PATCH 1605/2031] Bump support macOS versions; clean up allocs in llvm.targetTriple --- lib/std/target.zig | 9 ++- src/codegen/llvm.zig | 132 ++++++++++++++++++++----------------------- 2 files changed, 65 insertions(+), 76 deletions(-) diff --git a/lib/std/target.zig b/lib/std/target.zig index 27251f1d64..eae227fc37 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -269,19 +269,18 @@ pub const Target = struct { .macos => return switch (arch) { .aarch64 => VersionRange{ .semver = .{ - .min = .{ .major = 11, .minor = 6 }, - .max = .{ .major = 12, .minor = 0 }, + .min = .{ .major = 11, .minor = 6, .patch = 6 }, + .max = .{ .major = 12, .minor = 4 }, }, }, .x86_64 => VersionRange{ .semver = .{ - .min = .{ .major = 10, .minor = 13 }, - .max = .{ .major = 12, .minor = 0 }, + .min = .{ .major = 10, .minor = 15, .patch = 7 }, + .max = .{ .major = 12, .minor = 4 }, }, }, else => unreachable, }, - .ios => return .{ .semver = .{ .min = .{ .major = 12, .minor = 0 }, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 452bb9b497..6de001e5fd 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -26,6 +26,9 @@ const x86_64_abi = @import("../arch/x86_64/abi.zig"); const Error = error{ OutOfMemory, CodegenFail }; pub fn targetTriple(allocator: Allocator, target: std.Target) ![:0]u8 { + var llvm_triple = std.ArrayList(u8).init(allocator); + defer llvm_triple.deinit(); + const llvm_arch = switch (target.cpu.arch) { .arm => "arm", .armeb => "armeb", @@ -85,78 +88,64 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![:0]u8 { .spirv32 => return error.@"LLVM backend does not support SPIR-V", .spirv64 => return error.@"LLVM backend does not support SPIR-V", }; + try llvm_triple.appendSlice(llvm_arch); + try llvm_triple.appendSlice("-unknown-"); - var arena = std.heap.ArenaAllocator.init(allocator); - defer arena.deinit(); - - const llvm_os = blk: { - if (target.os.tag.isDarwin()) { - const min_version = target.os.version_range.semver.min; - const llvm_os = switch (target.os.tag) { - .macos => "macosx", - .ios => "ios", - .tvos => "tvos", - .watchos => "watchos", - else => unreachable, - }; - break :blk try std.fmt.allocPrintZ(arena.allocator(), "{s}{d}.{d}.{d}", .{ - llvm_os, - min_version.major, - min_version.minor, - min_version.patch, - }); - } - - const llvm_os = switch (target.os.tag) { - .freestanding => "unknown", - .ananas => "ananas", - .cloudabi => "cloudabi", - .dragonfly => "dragonfly", - .freebsd => "freebsd", - .fuchsia => "fuchsia", - .kfreebsd => "kfreebsd", - .linux => "linux", - .lv2 => "lv2", - .netbsd => "netbsd", - .openbsd => "openbsd", - .solaris => "solaris", - .windows => "windows", - .zos => "zos", - .haiku => "haiku", - .minix => "minix", - .rtems => "rtems", - .nacl => "nacl", - .aix => "aix", - .cuda => "cuda", - .nvcl => "nvcl", - .amdhsa => "amdhsa", - .ps4 => "ps4", - .elfiamcu => "elfiamcu", - .mesa3d => "mesa3d", - .contiki => "contiki", - .amdpal => "amdpal", - .hermit => "hermit", - .hurd => "hurd", - .wasi => "wasi", - .emscripten => "emscripten", - .uefi => "windows", - - .opencl, - .glsl450, - .vulkan, - .plan9, - .other, - => "unknown", - - .macos, - .ios, - .tvos, - .watchos, - => unreachable, - }; - - break :blk llvm_os; + const llvm_os = switch (target.os.tag) { + .freestanding => "unknown", + .ananas => "ananas", + .cloudabi => "cloudabi", + .dragonfly => "dragonfly", + .freebsd => "freebsd", + .fuchsia => "fuchsia", + .kfreebsd => "kfreebsd", + .linux => "linux", + .lv2 => "lv2", + .netbsd => "netbsd", + .openbsd => "openbsd", + .solaris => "solaris", + .windows => "windows", + .zos => "zos", + .haiku => "haiku", + .minix => "minix", + .rtems => "rtems", + .nacl => "nacl", + .aix => "aix", + .cuda => "cuda", + .nvcl => "nvcl", + .amdhsa => "amdhsa", + .ps4 => "ps4", + .elfiamcu => "elfiamcu", + .mesa3d => "mesa3d", + .contiki => "contiki", + .amdpal => "amdpal", + .hermit => "hermit", + .hurd => "hurd", + .wasi => "wasi", + .emscripten => "emscripten", + .uefi => "windows", + .macos => "macosx", + .ios => "ios", + .tvos => "tvos", + .watchos => "watchos", + .opencl, + .glsl450, + .vulkan, + .plan9, + .other, + => "unknown", }; + try llvm_triple.appendSlice(llvm_os); + + if (target.os.tag.isDarwin()) { + const min_version = target.os.version_range.semver.min; + try llvm_triple.writer().print("{d}.{d}.{d}", .{ + min_version.major, + min_version.minor, + min_version.patch, + }); + } + try llvm_triple.append('-'); const llvm_abi = switch (target.abi) { .none => "unknown", @@ -182,8 +171,9 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![:0]u8 { .simulator => "simulator", .macabi => "macabi", }; + try llvm_triple.appendSlice(llvm_abi); - return std.fmt.allocPrintZ(allocator, "{s}-unknown-{s}-{s}", .{ llvm_arch, llvm_os, llvm_abi }); + return llvm_triple.toOwnedSliceSentinel(0); } pub const Object = struct { From 64de32b34127df819322b91bc7643a0eb392d499 Mon Sep 17 00:00:00 2001 From: Thomas Cheng Date: Sat, 21 May 2022 09:31:28 +0100 Subject: [PATCH 1606/2031] Fix segfault in error note --- src/stage1/ir.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 62834e564d..52044e9dce 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -15500,7 +15500,7 @@ static Stage1AirInst *ir_analyze_struct_field_ptr(IrAnalyze *ira, Scope *scope, is_const, is_volatile, PtrLenSingle, field->align, (uint32_t)(ptr_bit_offset + field->bit_offset_in_host), (uint32_t)host_int_bytes_for_result_type, false); - + if (field == struct_type->data.structure.misaligned_field) { // If field is the last single misaligned field it will be represented as array // of bytes in LLVM but get_pointer_to_type_extra will set its host_int_bytes to 0. @@ -24781,7 +24781,9 @@ static Stage1AirInst *ir_analyze_instruction_end_expr(IrAnalyze *ira, Stage1ZirI if (type_is_invalid(store_ptr->value->type)) { if (instruction->result_loc->id == ResultLocIdReturn && (value->value->type->id == ZigTypeIdErrorUnion || value->value->type->id == ZigTypeIdErrorSet) && - ira->explicit_return_type->id != ZigTypeIdErrorUnion && ira->explicit_return_type->id != ZigTypeIdErrorSet) + ira->explicit_return_type->id != ZigTypeIdErrorUnion && ira->explicit_return_type->id != ZigTypeIdErrorSet && + // Only add error note if we have a node to attach it to + ira->explicit_return_type_source_node) { add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg, ira->explicit_return_type_source_node, buf_create_from_str("function cannot return an error")); From 8171972cbb477672ee1a99d953df4aaecb744a0c Mon Sep 17 00:00:00 2001 From: Dominic Tarr Date: Mon, 23 May 2022 20:58:13 +1200 Subject: [PATCH 1607/2031] document bufPrint, and format now uses `writer` not `output` --- lib/std/fmt.zig | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 64351088f5..54b68572b5 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -24,9 +24,9 @@ pub const FormatOptions = struct { fill: u8 = ' ', }; -/// Renders fmt string with args, calling output with slices of bytes. -/// If `output` returns an error, the error is returned from `format` and -/// `output` is not called again. +/// Renders fmt string with args, calling `writer` with slices of bytes. +/// If `writer` returns an error, the error is returned from `format` and +/// `writer` is not called again. /// /// The format string must be comptime known and may contain placeholders following /// this format: @@ -1869,6 +1869,9 @@ pub const BufPrintError = error{ /// As much as possible was written to the buffer, but it was too small to fit all the printed bytes. NoSpaceLeft, }; + +/// print a Formatter string into `buf`. Actually just a thin wrapper around `format` and `fixedBufferStream`. +/// returns a slice of the bytes printed to. pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintError![]u8 { var fbs = std.io.fixedBufferStream(buf); try format(fbs.writer(), fmt, args); From 818fbd9c567b907031c44961e4299ce1f9059be6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 May 2022 00:10:56 -0700 Subject: [PATCH 1608/2031] stage2: string literal interning This is a temporary addition to stage2 in order to match stage1 behavior, however the end-game once the lang spec is settled will be to use a global InternPool for comptime memoized objects, making this behavior consistent across all types, not only string literals. Or, we might decide to not guarantee string literals to have equal comptime pointers, in which case this commit can be reverted. --- src/Module.zig | 67 +++++++++++++++++++++++++++--- src/Sema.zig | 92 ++++++++++++++++++++++++++++++++++++------ src/TypedValue.zig | 5 +++ src/codegen.zig | 16 +++++++- src/codegen/llvm.zig | 32 ++++++++++++++- src/value.zig | 41 +++++++++++++++++++ test/behavior/eval.zig | 13 +++++- 7 files changed, 242 insertions(+), 24 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 11549ccda6..24495d8591 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -70,6 +70,17 @@ import_table: std.StringArrayHashMapUnmanaged(*File) = .{}, /// Keys are fully resolved file paths. This table owns the keys and values. embed_table: std.StringHashMapUnmanaged(*EmbedFile) = .{}, +/// This is a temporary addition to stage2 in order to match stage1 behavior, +/// however the end-game once the lang spec is settled will be to use a global +/// InternPool for comptime memoized objects, making this behavior consistent across all types, +/// not only string literals. Or, we might decide to not guarantee string literals +/// to have equal comptime pointers, in which case this field can be deleted (perhaps +/// the commit that introduced it can simply be reverted). +/// This table uses an optional index so that when a Decl is destroyed, the string literal +/// is still reclaimable by a future Decl. +string_literal_table: std.HashMapUnmanaged(StringLiteralContext.Key, Decl.OptionalIndex, StringLiteralContext, std.hash_map.default_max_load_percentage) = .{}, +string_literal_bytes: std.ArrayListUnmanaged(u8) = .{}, + /// The set of all the generic function instantiations. This is used so that when a generic /// function is called twice with the same comptime parameter arguments, both calls dispatch /// to the same function. @@ -157,6 +168,39 @@ decls_free_list: std.ArrayListUnmanaged(Decl.Index) = .{}, global_assembly: std.AutoHashMapUnmanaged(Decl.Index, []u8) = .{}, +pub const StringLiteralContext = struct { + bytes: *std.ArrayListUnmanaged(u8), + + pub const Key = struct { + index: u32, + len: u32, + }; + + pub fn eql(self: @This(), a: Key, b: Key) bool { + _ = self; + return a.index == b.index and a.len == b.len; + } + + pub fn hash(self: @This(), x: Key) u64 { + const x_slice = self.bytes.items[x.index..][0..x.len]; + return std.hash_map.hashString(x_slice); + } +}; + +pub const StringLiteralAdapter = struct { + bytes: *std.ArrayListUnmanaged(u8), + + pub fn eql(self: @This(), a_slice: []const u8, b: StringLiteralContext.Key) bool { + const b_slice = self.bytes.items[b.index..][0..b.len]; + return mem.eql(u8, a_slice, b_slice); + } + + pub fn hash(self: @This(), adapted_key: []const u8) u64 { + _ = self; + return std.hash_map.hashString(adapted_key); + } +}; + const MonomorphedFuncsSet = std.HashMapUnmanaged( *Fn, void, @@ -507,7 +551,8 @@ pub const Decl = struct { decl.name = undefined; } - pub fn clearValues(decl: *Decl, gpa: Allocator) void { + pub fn clearValues(decl: *Decl, mod: *Module) void { + const gpa = mod.gpa; if (decl.getExternFn()) |extern_fn| { extern_fn.deinit(gpa); gpa.destroy(extern_fn); @@ -521,6 +566,13 @@ pub const Decl = struct { gpa.destroy(variable); } if (decl.value_arena) |arena_state| { + if (decl.owns_tv) { + if (decl.val.castTag(.str_lit)) |str_lit| { + mod.string_literal_table.getPtrContext(str_lit.data, .{ + .bytes = &mod.string_literal_bytes, + }).?.* = .none; + } + } arena_state.promote(gpa).deinit(); decl.value_arena = null; decl.has_tv = false; @@ -2839,6 +2891,9 @@ pub fn deinit(mod: *Module) void { mod.decls_free_list.deinit(gpa); mod.allocated_decls.deinit(gpa); mod.global_assembly.deinit(gpa); + + mod.string_literal_table.deinit(gpa); + mod.string_literal_bytes.deinit(gpa); } pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void { @@ -2857,7 +2912,7 @@ pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void { if (decl.getInnerNamespace()) |namespace| { namespace.destroyDecls(mod); } - decl.clearValues(gpa); + decl.clearValues(mod); } decl.dependants.deinit(gpa); decl.dependencies.deinit(gpa); @@ -4034,7 +4089,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { if (decl.getFunction()) |prev_func| { prev_is_inline = prev_func.state == .inline_only; } - decl.clearValues(gpa); + decl.clearValues(mod); } decl.ty = try decl_tv.ty.copy(decl_arena_allocator); @@ -4080,7 +4135,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { var type_changed = true; if (decl.has_tv) { type_changed = !decl.ty.eql(decl_tv.ty, mod); - decl.clearValues(gpa); + decl.clearValues(mod); } decl.owns_tv = false; @@ -4694,7 +4749,7 @@ pub fn clearDecl( if (decl.getInnerNamespace()) |namespace| { try namespace.deleteAllDecls(mod, outdated_decls); } - decl.clearValues(gpa); + decl.clearValues(mod); } if (decl.deletion_flag) { @@ -5623,7 +5678,7 @@ pub fn populateTestFunctions(mod: *Module) !void { // Since we are replacing the Decl's value we must perform cleanup on the // previous value. - decl.clearValues(gpa); + decl.clearValues(mod); decl.ty = new_ty; decl.val = new_val; decl.has_tv = true; diff --git a/src/Sema.zig b/src/Sema.zig index cb34fd158f..d3fca6d2b2 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3842,20 +3842,44 @@ fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins fn addStrLit(sema: *Sema, block: *Block, zir_bytes: []const u8) CompileError!Air.Inst.Ref { // `zir_bytes` references memory inside the ZIR module, which can get deallocated // after semantic analysis is complete, for example in the case of the initialization - // expression of a variable declaration. We need the memory to be in the new - // anonymous Decl's arena. - var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded); - defer anon_decl.deinit(); + // expression of a variable declaration. + const mod = sema.mod; + const gpa = sema.gpa; + const string_bytes = &mod.string_literal_bytes; + const StringLiteralAdapter = Module.StringLiteralAdapter; + const StringLiteralContext = Module.StringLiteralContext; + try string_bytes.ensureUnusedCapacity(gpa, zir_bytes.len); + const gop = try mod.string_literal_table.getOrPutContextAdapted(gpa, zir_bytes, StringLiteralAdapter{ + .bytes = string_bytes, + }, StringLiteralContext{ + .bytes = string_bytes, + }); + if (!gop.found_existing) { + gop.key_ptr.* = .{ + .index = @intCast(u32, string_bytes.items.len), + .len = @intCast(u32, zir_bytes.len), + }; + string_bytes.appendSliceAssumeCapacity(zir_bytes); + gop.value_ptr.* = .none; + } + const decl_index = gop.value_ptr.unwrap() orelse di: { + var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded); + defer anon_decl.deinit(); - const bytes = try anon_decl.arena().dupeZ(u8, zir_bytes); + const decl_index = try anon_decl.finish( + try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), gop.key_ptr.len), + try Value.Tag.str_lit.create(anon_decl.arena(), gop.key_ptr.*), + 0, // default alignment + ); - const new_decl = try anon_decl.finish( - try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), - try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), - 0, // default alignment - ); + // Needed so that `Decl.clearValues` will additionally set the corresponding + // string literal table value back to `Decl.OptionalIndex.none`. + mod.declPtr(decl_index).owns_tv = true; - return sema.analyzeDeclRef(new_decl); + gop.value_ptr.* = decl_index.toOptional(); + break :di decl_index; + }; + return sema.analyzeDeclRef(decl_index); } fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -19762,6 +19786,35 @@ fn beginComptimePtrMutation( .ty = elem_ty, }; }, + .str_lit => { + // An array is memory-optimized to store a slice of bytes, but we are about + // to modify an individual field and the representation has to change. + // If we wanted to avoid this, there would need to be special detection + // elsewhere to identify when writing a value to an array element that is stored + // using the `str_lit` tag, and handle it without making a call to this function. + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); + + const str_lit = parent.val.castTag(.str_lit).?.data; + const dest_len = parent.ty.arrayLenIncludingSentinel(); + const bytes = sema.mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; + const elems = try arena.alloc(Value, @intCast(usize, dest_len)); + for (bytes) |byte, i| { + elems[i] = try Value.Tag.int_u64.create(arena, byte); + } + if (parent.ty.sentinel()) |sent_val| { + assert(elems.len == bytes.len + 1); + elems[bytes.len] = sent_val; + } + + parent.val.* = try Value.Tag.aggregate.create(arena, elems); + + return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .val = &elems[elem_ptr.index], + .ty = elem_ty, + }; + }, .repeated => { // An array is memory-optimized to store only a single element value, and // that value is understood to be the same for the entire length of the array. @@ -20097,10 +20150,23 @@ fn beginComptimePtrLoad( } } - deref.pointee = if (elem_ptr.index < check_len) TypedValue{ + if (elem_ptr.index >= check_len) { + deref.pointee = null; + break :blk deref; + } + if (elem_ptr.index == check_len - 1) { + if (array_tv.ty.sentinel()) |sent| { + deref.pointee = TypedValue{ + .ty = elem_ty, + .val = sent, + }; + break :blk deref; + } + } + deref.pointee = TypedValue{ .ty = elem_ty, .val = try array_tv.val.elemValue(sema.mod, sema.arena, elem_ptr.index), - } else null; + }; break :blk deref; }, diff --git a/src/TypedValue.zig b/src/TypedValue.zig index b6aee29a4b..9f69e4c8bd 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -295,6 +295,11 @@ pub fn print( return writer.print(".{s}", .{ty.enumFieldName(val.castTag(.enum_field_index).?.data)}); }, .bytes => return writer.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}), + .str_lit => { + const str_lit = val.castTag(.str_lit).?.data; + const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; + return writer.print("\"{}\"", .{std.zig.fmtEscapes(bytes)}); + }, .repeated => { if (level == 0) { return writer.writeAll(".{ ... }"); diff --git a/src/codegen.zig b/src/codegen.zig index def69d952f..bd556baa5e 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -203,11 +203,23 @@ pub fn generateSymbol( }, .Array => switch (typed_value.val.tag()) { .bytes => { - const payload = typed_value.val.castTag(.bytes).?; + const bytes = typed_value.val.castTag(.bytes).?.data; const len = @intCast(usize, typed_value.ty.arrayLenIncludingSentinel()); // The bytes payload already includes the sentinel, if any try code.ensureUnusedCapacity(len); - code.appendSliceAssumeCapacity(payload.data[0..len]); + code.appendSliceAssumeCapacity(bytes[0..len]); + return Result{ .appended = {} }; + }, + .str_lit => { + const str_lit = typed_value.val.castTag(.str_lit).?.data; + const mod = bin_file.options.module.?; + const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; + try code.ensureUnusedCapacity(bytes.len + 1); + code.appendSliceAssumeCapacity(bytes); + if (typed_value.ty.sentinel()) |sent_val| { + const byte = @intCast(u8, sent_val.toUnsignedInt(target)); + code.appendAssumeCapacity(byte); + } return Result{ .appended = {} }; }, .aggregate => { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 6de001e5fd..ef33f39f55 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2936,9 +2936,39 @@ pub const DeclGen = struct { return dg.context.constString( bytes.ptr, @intCast(c_uint, tv.ty.arrayLenIncludingSentinel()), - .True, // don't null terminate. bytes has the sentinel, if any. + .True, // Don't null terminate. Bytes has the sentinel, if any. ); }, + .str_lit => { + const str_lit = tv.val.castTag(.str_lit).?.data; + const bytes = dg.module.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; + if (tv.ty.sentinel()) |sent_val| { + const byte = @intCast(u8, sent_val.toUnsignedInt(target)); + if (byte == 0 and bytes.len > 0) { + return dg.context.constString( + bytes.ptr, + @intCast(c_uint, bytes.len), + .False, // Yes, null terminate. + ); + } + var array = std.ArrayList(u8).init(dg.gpa); + defer array.deinit(); + try array.ensureUnusedCapacity(bytes.len + 1); + array.appendSliceAssumeCapacity(bytes); + array.appendAssumeCapacity(byte); + return dg.context.constString( + array.items.ptr, + @intCast(c_uint, array.items.len), + .True, // Don't null terminate. + ); + } else { + return dg.context.constString( + bytes.ptr, + @intCast(c_uint, bytes.len), + .True, // Don't null terminate. `bytes` has the sentinel, if any. + ); + } + }, .aggregate => { const elem_vals = tv.val.castTag(.aggregate).?.data; const elem_ty = tv.ty.elemType(); diff --git a/src/value.zig b/src/value.zig index 1280adf1e0..310384c3c3 100644 --- a/src/value.zig +++ b/src/value.zig @@ -126,6 +126,8 @@ pub const Value = extern union { field_ptr, /// A slice of u8 whose memory is managed externally. bytes, + /// Similar to bytes however it stores an index relative to `Module.string_literal_bytes`. + str_lit, /// This value is repeated some number of times. The amount of times to repeat /// is stored externally. repeated, @@ -285,6 +287,7 @@ pub const Value = extern union { .enum_literal, => Payload.Bytes, + .str_lit => Payload.StrLit, .slice => Payload.Slice, .enum_field_index => Payload.U32, @@ -538,6 +541,7 @@ pub const Value = extern union { }; return Value{ .ptr_otherwise = &new_payload.base }; }, + .str_lit => return self.copyPayloadShallow(arena, Payload.StrLit), .repeated, .eu_payload, .opt_payload, @@ -764,6 +768,12 @@ pub const Value = extern union { .enum_literal => return out_stream.print(".{}", .{std.zig.fmtId(val.castTag(.enum_literal).?.data)}), .enum_field_index => return out_stream.print("(enum field {d})", .{val.castTag(.enum_field_index).?.data}), .bytes => return out_stream.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}), + .str_lit => { + const str_lit = val.castTag(.str_lit).?.data; + return out_stream.print("(.str_lit index={d} len={d})", .{ + str_lit.index, str_lit.len, + }); + }, .repeated => { try out_stream.writeAll("(repeated) "); val = val.castTag(.repeated).?.data; @@ -824,6 +834,11 @@ pub const Value = extern union { const adjusted_bytes = bytes[0..adjusted_len]; return allocator.dupe(u8, adjusted_bytes); }, + .str_lit => { + const str_lit = val.castTag(.str_lit).?.data; + const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; + return allocator.dupe(u8, bytes); + }, .enum_literal => return allocator.dupe(u8, val.castTag(.enum_literal).?.data), .repeated => { const byte = @intCast(u8, val.castTag(.repeated).?.data.toUnsignedInt(target)); @@ -2537,6 +2552,20 @@ pub const Value = extern union { return initPayload(&buffer.base); } }, + .str_lit => { + const str_lit = val.castTag(.str_lit).?.data; + const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; + const byte = bytes[index]; + if (arena) |a| { + return Tag.int_u64.create(a, byte); + } else { + buffer.* = .{ + .base = .{ .tag = .int_u64 }, + .data = byte, + }; + return initPayload(&buffer.base); + } + }, // No matter the index; all the elements are the same! .repeated => return val.castTag(.repeated).?.data, @@ -2570,6 +2599,13 @@ pub const Value = extern union { return switch (val.tag()) { .empty_array_sentinel => if (start == 0 and end == 1) val else Value.initTag(.empty_array), .bytes => Tag.bytes.create(arena, val.castTag(.bytes).?.data[start..end]), + .str_lit => { + const str_lit = val.castTag(.str_lit).?.data; + return Tag.str_lit.create(arena, .{ + .index = @intCast(u32, str_lit.index + start), + .len = @intCast(u32, end - start), + }); + }, .aggregate => Tag.aggregate.create(arena, val.castTag(.aggregate).?.data[start..end]), .slice => sliceArray(val.castTag(.slice).?.data.ptr, mod, arena, start, end), @@ -4721,6 +4757,11 @@ pub const Value = extern union { data: []const u8, }; + pub const StrLit = struct { + base: Payload, + data: Module.StringLiteralContext.Key, + }; + pub const Aggregate = struct { base: Payload, /// Field values. The types are according to the struct or array type. diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 88be755b61..e0e787509a 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -643,9 +643,18 @@ fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) !void { try expect(ptr1 == ptr2); } +// This one is still up for debate in the language specification. +// Application code should not rely on this behavior until it is solidified. +// Currently, stage1 has special case code to make this pass for string literals +// but it does not work if the values are constructed with comptime code, or if +// arrays of non-u8 elements are used instead. +// The official language specification might not make this guarantee. However, if +// it does make this guarantee, it will make it consistently for all types, not +// only string literals. This is why stage2 currently has a string table for +// string literals, to match stage1 and pass this test, however the end-game once +// the lang spec issue is settled would be to use a global InternPool for comptime +// memoized objects, making this behavior consistent across all types. test "string literal used as comptime slice is memoized" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - const a = "link"; const b = "link"; comptime try expect(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node); From 953e2778d4402cf85b99061ef9bdb89aa3e5f2db Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 May 2022 01:20:18 -0700 Subject: [PATCH 1609/2031] clean up a compile error test case These should be build-obj, not build-exe, where possible. --- test/cases/llvm/pointer_with_different_address_spaces.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/llvm/pointer_with_different_address_spaces.zig b/test/cases/llvm/pointer_with_different_address_spaces.zig index 8fbd8b07cf..26540862ff 100644 --- a/test/cases/llvm/pointer_with_different_address_spaces.zig +++ b/test/cases/llvm/pointer_with_different_address_spaces.zig @@ -1,12 +1,12 @@ fn entry(a: *addrspace(.gs) i32) *addrspace(.fs) i32 { return a; } -pub fn main() void { +export fn entry2() void { _ = entry; } // error -// output_mode=Exe +// output_mode=Obj // backend=stage2,llvm // target=x86_64-linux,x86_64-macos // From 3264abe3d8f658e1b7275d2be80e43eddfc098dc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 22 May 2022 21:51:44 -0700 Subject: [PATCH 1610/2031] stage2: fixes for error union semantics * Sema: avoid unnecessary safety checks when an error set is empty. * Sema: make zirErrorToInt handle comptime errors that are represented as integers. * Sema: make empty error sets properly integrate with typeHasOnePossibleValue. * Type: correct the ABI alignment and size of error unions which have both zero-bit error set and zero-bit payload. The previous code did not account for the fact that we still need to store a bit for whether there is an error. * LLVM: lower error unions possibly with the payload first or with the error code first, depending on alignment. Previously it always put the error code first and used a padding array. * LLVM: lower functions which have an empty error set as the return type the same as anyerror, so that they can be used where fn()anyerror function pointers are expected. In such functions, Zig will lower ret to returning zero instead of void. As a result, one more behavior test is passing. --- lib/std/debug.zig | 2 +- src/Sema.zig | 63 ++++++++++--- src/codegen/llvm.zig | 202 ++++++++++++++++++++++++++++------------ src/type.zig | 197 ++++++++++++++++++++++++++++++--------- test/behavior/error.zig | 31 +++++- 5 files changed, 370 insertions(+), 125 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 83667c758b..86ed1c5a65 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1798,7 +1798,7 @@ fn resetSegfaultHandler() void { .mask = os.empty_sigset, .flags = 0, }; - // do nothing if an error happens to avoid a double-panic + // To avoid a double-panic, do nothing if an error happens here. updateSegfaultHandler(&act) catch {}; } diff --git a/src/Sema.zig b/src/Sema.zig index d3fca6d2b2..b718912a38 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5899,12 +5899,22 @@ fn zirErrorToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! if (val.isUndef()) { return sema.addConstUndef(result_ty); } - const payload = try sema.arena.create(Value.Payload.U64); - payload.* = .{ - .base = .{ .tag = .int_u64 }, - .data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value, - }; - return sema.addConstant(result_ty, Value.initPayload(&payload.base)); + switch (val.tag()) { + .@"error" => { + const payload = try sema.arena.create(Value.Payload.U64); + payload.* = .{ + .base = .{ .tag = .int_u64 }, + .data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value, + }; + return sema.addConstant(result_ty, Value.initPayload(&payload.base)); + }, + + // This is not a valid combination with the type `anyerror`. + .the_only_possible_value => unreachable, + + // Assume it's already encoded as an integer. + else => return sema.addConstant(result_ty, val), + } } try sema.requireRuntimeBlock(block, src); @@ -6261,19 +6271,24 @@ fn zirErrUnionPayload( }); } + const result_ty = operand_ty.errorUnionPayload(); if (try sema.resolveDefinedValue(block, src, operand)) |val| { if (val.getError()) |name| { return sema.fail(block, src, "caught unexpected error '{s}'", .{name}); } const data = val.castTag(.eu_payload).?.data; - const result_ty = operand_ty.errorUnionPayload(); return sema.addConstant(result_ty, data); } + try sema.requireRuntimeBlock(block, src); - if (safety_check and block.wantSafety()) { + + // If the error set has no fields then no safety check is needed. + if (safety_check and block.wantSafety() and + operand_ty.errorUnionSet().errorSetCardinality() != .zero) + { try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err); } - const result_ty = operand_ty.errorUnionPayload(); + return block.addTyOp(.unwrap_errunion_payload, result_ty, operand); } @@ -6311,7 +6326,8 @@ fn analyzeErrUnionPayloadPtr( }); } - const payload_ty = operand_ty.elemType().errorUnionPayload(); + const err_union_ty = operand_ty.elemType(); + const payload_ty = err_union_ty.errorUnionPayload(); const operand_pointer_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = payload_ty, .mutable = !operand_ty.isConstPtr(), @@ -6351,9 +6367,14 @@ fn analyzeErrUnionPayloadPtr( } try sema.requireRuntimeBlock(block, src); - if (safety_check and block.wantSafety()) { + + // If the error set has no fields then no safety check is needed. + if (safety_check and block.wantSafety() and + err_union_ty.errorUnionSet().errorSetCardinality() != .zero) + { try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr); } + const air_tag: Air.Inst.Tag = if (initializing) .errunion_payload_ptr_set else @@ -23301,10 +23322,7 @@ pub fn typeHasOnePossibleValue( .enum_literal, .anyerror_void_error_union, .error_union, - .error_set, - .error_set_single, .error_set_inferred, - .error_set_merged, .@"opaque", .var_args_param, .manyptr_u8, @@ -23333,6 +23351,23 @@ pub fn typeHasOnePossibleValue( .bound_fn, => return null, + .error_set_single => { + const name = ty.castTag(.error_set_single).?.data; + return try Value.Tag.@"error".create(sema.arena, .{ .name = name }); + }, + .error_set => { + const err_set_obj = ty.castTag(.error_set).?.data; + const names = err_set_obj.names.keys(); + if (names.len > 1) return null; + return try Value.Tag.@"error".create(sema.arena, .{ .name = names[0] }); + }, + .error_set_merged => { + const name_map = ty.castTag(.error_set_merged).?.data; + const names = name_map.keys(); + if (names.len > 1) return null; + return try Value.Tag.@"error".create(sema.arena, .{ .name = names[0] }); + }, + .@"struct" => { const resolved_ty = try sema.resolveTypeFields(block, src, ty); const s = resolved_ty.castTag(.@"struct").?.data; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index ef33f39f55..95d12dff3a 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2451,20 +2451,22 @@ pub const DeclGen = struct { .ErrorUnion => { const error_type = t.errorUnionSet(); const payload_type = t.errorUnionPayload(); - const llvm_error_type = try dg.llvmType(error_type); - if (!payload_type.hasRuntimeBitsIgnoreComptime()) { - return llvm_error_type; + if (error_type.errorSetCardinality() == .zero) { + return dg.llvmType(payload_type); } + if (!payload_type.hasRuntimeBitsIgnoreComptime()) { + return try dg.llvmType(Type.anyerror); + } + const llvm_error_type = try dg.llvmType(error_type); const llvm_payload_type = try dg.llvmType(payload_type); const payload_align = payload_type.abiAlignment(target); - const error_size = error_type.abiSize(target); - if (payload_align > error_size) { - const pad_type = dg.context.intType(8).arrayType(@intCast(u32, payload_align - error_size)); - const fields: [3]*const llvm.Type = .{ llvm_error_type, pad_type, llvm_payload_type }; + const error_align = Type.anyerror.abiAlignment(target); + if (error_align > payload_align) { + const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type }; return dg.context.structType(&fields, fields.len, .False); } else { - const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type }; + const fields: [2]*const llvm.Type = .{ llvm_payload_type, llvm_error_type }; return dg.context.structType(&fields, fields.len, .False); } }, @@ -3103,6 +3105,10 @@ pub const DeclGen = struct { .ErrorUnion => { const error_type = tv.ty.errorUnionSet(); const payload_type = tv.ty.errorUnionPayload(); + if (error_type.errorSetCardinality() == .zero) { + const payload_val = tv.val.castTag(.eu_payload).?.data; + return dg.genTypedValue(.{ .ty = payload_type, .val = payload_val }); + } const is_pl = tv.val.errorUnionIsPayload(); if (!payload_type.hasRuntimeBitsIgnoreComptime()) { @@ -3110,28 +3116,24 @@ pub const DeclGen = struct { const err_val = if (!is_pl) tv.val else Value.initTag(.zero); return dg.genTypedValue(.{ .ty = error_type, .val = err_val }); } - var len: u8 = 2; - var fields: [3]*const llvm.Value = .{ - try dg.genTypedValue(.{ - .ty = error_type, - .val = if (is_pl) Value.initTag(.zero) else tv.val, - }), - try dg.genTypedValue(.{ - .ty = payload_type, - .val = if (tv.val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef), - }), - undefined, - }; const payload_align = payload_type.abiAlignment(target); - const error_size = error_type.abiSize(target); - if (payload_align > error_size) { - fields[2] = fields[1]; - const pad_type = dg.context.intType(8).arrayType(@intCast(u32, payload_align - error_size)); - fields[1] = pad_type.getUndef(); - len += 1; + const error_align = Type.anyerror.abiAlignment(target); + const llvm_error_value = try dg.genTypedValue(.{ + .ty = error_type, + .val = if (is_pl) Value.initTag(.zero) else tv.val, + }); + const llvm_payload_value = try dg.genTypedValue(.{ + .ty = payload_type, + .val = if (tv.val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef), + }); + if (error_align > payload_align) { + const fields: [2]*const llvm.Value = .{ llvm_error_value, llvm_payload_value }; + return dg.context.constStruct(&fields, fields.len, .False); + } else { + const fields: [2]*const llvm.Value = .{ llvm_payload_value, llvm_error_value }; + return dg.context.constStruct(&fields, fields.len, .False); } - return dg.context.constStruct(&fields, len, .False); }, .Struct => { const llvm_struct_ty = try dg.llvmType(tv.ty); @@ -4338,11 +4340,19 @@ pub const FuncGen = struct { _ = self.builder.buildRetVoid(); return null; } + const fn_info = self.dg.decl.ty.fnInfo(); if (!ret_ty.hasRuntimeBitsIgnoreComptime()) { - _ = self.builder.buildRetVoid(); + if (fn_info.return_type.isError()) { + // Functions with an empty error set are emitted with an error code + // return type and return zero so they can be function pointers coerced + // to functions that return anyerror. + const err_int = try self.dg.llvmType(Type.anyerror); + _ = self.builder.buildRet(err_int.constInt(0, .False)); + } else { + _ = self.builder.buildRetVoid(); + } return null; } - const fn_info = self.dg.decl.ty.fnInfo(); const abi_ret_ty = try lowerFnRetTy(self.dg, fn_info); const operand = try self.resolveInst(un_op); const llvm_ret_ty = operand.typeOf(); @@ -4369,13 +4379,25 @@ pub const FuncGen = struct { const un_op = self.air.instructions.items(.data)[inst].un_op; const ptr_ty = self.air.typeOf(un_op); const ret_ty = ptr_ty.childType(); - if (!ret_ty.hasRuntimeBitsIgnoreComptime() or self.ret_ptr != null) { + const fn_info = self.dg.decl.ty.fnInfo(); + if (!ret_ty.hasRuntimeBitsIgnoreComptime()) { + if (fn_info.return_type.isError()) { + // Functions with an empty error set are emitted with an error code + // return type and return zero so they can be function pointers coerced + // to functions that return anyerror. + const err_int = try self.dg.llvmType(Type.anyerror); + _ = self.builder.buildRet(err_int.constInt(0, .False)); + } else { + _ = self.builder.buildRetVoid(); + } + return null; + } + if (self.ret_ptr != null) { _ = self.builder.buildRetVoid(); return null; } const ptr = try self.resolveInst(un_op); const target = self.dg.module.getTarget(); - const fn_info = self.dg.decl.ty.fnInfo(); const abi_ret_ty = try lowerFnRetTy(self.dg, fn_info); const llvm_ret_ty = try self.dg.llvmType(ret_ty); const casted_ptr = if (abi_ret_ty == llvm_ret_ty) ptr else p: { @@ -5433,18 +5455,30 @@ pub const FuncGen = struct { const err_set_ty = try self.dg.llvmType(Type.initTag(.anyerror)); const zero = err_set_ty.constNull(); + if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { + const llvm_i1 = self.context.intType(1); + switch (op) { + .EQ => return llvm_i1.constInt(1, .False), // 0 == 0 + .NE => return llvm_i1.constInt(0, .False), // 0 != 0 + else => unreachable, + } + } + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand; return self.builder.buildICmp(op, loaded, zero, ""); } + const target = self.dg.module.getTarget(); + const err_field_index = errUnionErrorOffset(payload_ty, target); + if (operand_is_ptr or isByRef(err_union_ty)) { - const err_field_ptr = self.builder.buildStructGEP(operand, 0, ""); + const err_field_ptr = self.builder.buildStructGEP(operand, err_field_index, ""); const loaded = self.builder.buildLoad(err_field_ptr, ""); return self.builder.buildICmp(op, loaded, zero, ""); } - const loaded = self.builder.buildExtractValue(operand, 0, ""); + const loaded = self.builder.buildExtractValue(operand, err_field_index, ""); return self.builder.buildICmp(op, loaded, zero, ""); } @@ -5544,11 +5578,17 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); - const result_ty = self.air.getRefType(ty_op.ty); + const operand_ty = self.air.typeOf(ty_op.operand); + const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; + // If the error set has no fields, then the payload and the error + // union are the same value. + if (error_union_ty.errorUnionSet().errorSetCardinality() == .zero) { + return operand; + } + const result_ty = self.air.typeOfIndex(inst); const payload_ty = if (operand_is_ptr) result_ty.childType() else result_ty; - const target = self.dg.module.getTarget(); - const offset: u8 = if (payload_ty.abiAlignment(target) > Type.anyerror.abiSize(target)) 2 else 1; + const offset = errUnionPayloadOffset(payload_ty, target); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { if (!operand_is_ptr) return null; @@ -5574,54 +5614,70 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); const operand_ty = self.air.typeOf(ty_op.operand); - const err_set_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; + const err_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; + if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { + const err_llvm_ty = try self.dg.llvmType(Type.anyerror); + if (operand_is_ptr) { + return self.builder.buildBitCast(operand, err_llvm_ty.pointerType(0), ""); + } else { + return err_llvm_ty.constInt(0, .False); + } + } - const payload_ty = err_set_ty.errorUnionPayload(); + const payload_ty = err_union_ty.errorUnionPayload(); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { if (!operand_is_ptr) return operand; return self.builder.buildLoad(operand, ""); } - if (operand_is_ptr or isByRef(err_set_ty)) { - const err_field_ptr = self.builder.buildStructGEP(operand, 0, ""); + const target = self.dg.module.getTarget(); + const offset = errUnionErrorOffset(payload_ty, target); + + if (operand_is_ptr or isByRef(err_union_ty)) { + const err_field_ptr = self.builder.buildStructGEP(operand, offset, ""); return self.builder.buildLoad(err_field_ptr, ""); } - return self.builder.buildExtractValue(operand, 0, ""); + return self.builder.buildExtractValue(operand, offset, ""); } fn airErrUnionPayloadPtrSet(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); - const error_set_ty = self.air.typeOf(ty_op.operand).childType(); + const error_union_ty = self.air.typeOf(ty_op.operand).childType(); - const error_ty = error_set_ty.errorUnionSet(); - const payload_ty = error_set_ty.errorUnionPayload(); + const error_ty = error_union_ty.errorUnionSet(); + if (error_ty.errorSetCardinality() == .zero) { + // TODO: write undefined bytes through the pointer here + return operand; + } + const payload_ty = error_union_ty.errorUnionPayload(); const non_error_val = try self.dg.genTypedValue(.{ .ty = error_ty, .val = Value.zero }); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { - // We have a pointer to a i1. We need to set it to 1 and then return the same pointer. _ = self.builder.buildStore(non_error_val, operand); return operand; } const index_type = self.context.intType(32); + const target = self.dg.module.getTarget(); { + const error_offset = errUnionErrorOffset(payload_ty, target); // First set the non-error value. const indices: [2]*const llvm.Value = .{ index_type.constNull(), // dereference the pointer - index_type.constNull(), // first field is the payload + index_type.constInt(error_offset, .False), }; const non_null_ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""); - _ = self.builder.buildStore(non_error_val, non_null_ptr); + const store_inst = self.builder.buildStore(non_error_val, non_null_ptr); + store_inst.setAlignment(Type.anyerror.abiAlignment(target)); } // Then return the payload pointer (only if it is used). if (self.liveness.isUnused(inst)) return null; - const target = self.dg.module.getTarget(); - const payload_offset: u8 = if (payload_ty.abiAlignment(target) > Type.anyerror.abiSize(target)) 2 else 1; + const payload_offset = errUnionPayloadOffset(payload_ty, target); const indices: [2]*const llvm.Value = .{ index_type.constNull(), // dereference the pointer - index_type.constInt(payload_offset, .False), // second field is the payload + index_type.constInt(payload_offset, .False), }; return self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""); } @@ -5669,21 +5725,26 @@ pub const FuncGen = struct { if (self.liveness.isUnused(inst)) return null; const ty_op = self.air.instructions.items(.data)[inst].ty_op; - const payload_ty = self.air.typeOf(ty_op.operand); + const inst_ty = self.air.typeOfIndex(inst); const operand = try self.resolveInst(ty_op.operand); + if (inst_ty.errorUnionSet().errorSetCardinality() == .zero) { + return operand; + } + const payload_ty = self.air.typeOf(ty_op.operand); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { return operand; } - const inst_ty = self.air.typeOfIndex(inst); - const ok_err_code = self.context.intType(16).constNull(); + const ok_err_code = (try self.dg.llvmType(Type.anyerror)).constNull(); const err_un_llvm_ty = try self.dg.llvmType(inst_ty); const target = self.dg.module.getTarget(); - const payload_offset: u8 = if (payload_ty.abiAlignment(target) > Type.anyerror.abiSize(target)) 2 else 1; + const payload_offset = errUnionPayloadOffset(payload_ty, target); + const error_offset = errUnionErrorOffset(payload_ty, target); if (isByRef(inst_ty)) { const result_ptr = self.buildAlloca(err_un_llvm_ty); - const err_ptr = self.builder.buildStructGEP(result_ptr, 0, ""); - _ = self.builder.buildStore(ok_err_code, err_ptr); + const err_ptr = self.builder.buildStructGEP(result_ptr, error_offset, ""); + const store_inst = self.builder.buildStore(ok_err_code, err_ptr); + store_inst.setAlignment(Type.anyerror.abiAlignment(target)); const payload_ptr = self.builder.buildStructGEP(result_ptr, payload_offset, ""); var ptr_ty_payload: Type.Payload.ElemType = .{ .base = .{ .tag = .single_mut_pointer }, @@ -5694,7 +5755,7 @@ pub const FuncGen = struct { return result_ptr; } - const partial = self.builder.buildInsertValue(err_un_llvm_ty.getUndef(), ok_err_code, 0, ""); + const partial = self.builder.buildInsertValue(err_un_llvm_ty.getUndef(), ok_err_code, error_offset, ""); return self.builder.buildInsertValue(partial, operand, payload_offset, ""); } @@ -5711,11 +5772,13 @@ pub const FuncGen = struct { const err_un_llvm_ty = try self.dg.llvmType(err_un_ty); const target = self.dg.module.getTarget(); - const payload_offset: u8 = if (payload_ty.abiAlignment(target) > Type.anyerror.abiSize(target)) 2 else 1; + const payload_offset = errUnionPayloadOffset(payload_ty, target); + const error_offset = errUnionErrorOffset(payload_ty, target); if (isByRef(err_un_ty)) { const result_ptr = self.buildAlloca(err_un_llvm_ty); - const err_ptr = self.builder.buildStructGEP(result_ptr, 0, ""); - _ = self.builder.buildStore(operand, err_ptr); + const err_ptr = self.builder.buildStructGEP(result_ptr, error_offset, ""); + const store_inst = self.builder.buildStore(operand, err_ptr); + store_inst.setAlignment(Type.anyerror.abiAlignment(target)); const payload_ptr = self.builder.buildStructGEP(result_ptr, payload_offset, ""); var ptr_ty_payload: Type.Payload.ElemType = .{ .base = .{ .tag = .single_mut_pointer }, @@ -5728,7 +5791,7 @@ pub const FuncGen = struct { return result_ptr; } - const partial = self.builder.buildInsertValue(err_un_llvm_ty.getUndef(), operand, 0, ""); + const partial = self.builder.buildInsertValue(err_un_llvm_ty.getUndef(), operand, error_offset, ""); // TODO set payload bytes to undef return partial; } @@ -8546,7 +8609,14 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool /// be effectively bitcasted to the actual return type. fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm.Type { if (!fn_info.return_type.hasRuntimeBitsIgnoreComptime()) { - return dg.context.voidType(); + // If the return type is an error set or an error union, then we make this + // anyerror return type instead, so that it can be coerced into a function + // pointer type which has anyerror as the return type. + if (fn_info.return_type.isError()) { + return dg.llvmType(Type.anyerror); + } else { + return dg.context.voidType(); + } } const target = dg.module.getTarget(); switch (fn_info.cc) { @@ -8991,3 +9061,11 @@ fn buildAllocaInner( return builder.buildAlloca(llvm_ty, ""); } + +fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u1 { + return @boolToInt(Type.anyerror.abiAlignment(target) > payload_ty.abiAlignment(target)); +} + +fn errUnionErrorOffset(payload_ty: Type, target: std.Target) u1 { + return @boolToInt(Type.anyerror.abiAlignment(target) <= payload_ty.abiAlignment(target)); +} diff --git a/src/type.zig b/src/type.zig index ea65cc8916..4b8a41915f 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2317,10 +2317,7 @@ pub const Type = extern union { .const_slice_u8_sentinel_0, .array_u8_sentinel_0, .anyerror_void_error_union, - .error_set, - .error_set_single, .error_set_inferred, - .error_set_merged, .manyptr_u8, .manyptr_const_u8, .manyptr_const_u8_sentinel_0, @@ -2361,8 +2358,20 @@ pub const Type = extern union { .fn_void_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, + .error_set_single, => return false, + .error_set => { + const err_set_obj = ty.castTag(.error_set).?.data; + const names = err_set_obj.names.keys(); + return names.len > 1; + }, + .error_set_merged => { + const name_map = ty.castTag(.error_set_merged).?.data; + const names = name_map.keys(); + return names.len > 1; + }, + // These types have more than one possible value, so the result is the same as // asking whether they are comptime-only types. .anyframe_T, @@ -2388,6 +2397,21 @@ pub const Type = extern union { } }, + .error_union => { + // This code needs to be kept in sync with the equivalent switch prong + // in abiSizeAdvanced. + const data = ty.castTag(.error_union).?.data; + if (data.error_set.errorSetCardinality() == .zero) { + return hasRuntimeBitsAdvanced(data.payload, ignore_comptime_only, sema_kit); + } else if (ignore_comptime_only) { + return true; + } else if (sema_kit) |sk| { + return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, ty)); + } else { + return !comptimeOnly(ty); + } + }, + .@"struct" => { const struct_obj = ty.castTag(.@"struct").?.data; if (sema_kit) |sk| { @@ -2467,12 +2491,6 @@ pub const Type = extern union { .int_signed, .int_unsigned => return ty.cast(Payload.Bits).?.data != 0, - .error_union => { - const payload = ty.castTag(.error_union).?.data; - return (try payload.error_set.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) or - (try payload.payload.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)); - }, - .tuple, .anon_struct => { const tuple = ty.tupleFields(); for (tuple.types) |field_ty, i| { @@ -2852,13 +2870,30 @@ pub const Type = extern union { else => unreachable, }, - .error_set, - .error_set_single, + // TODO revisit this when we have the concept of the error tag type .anyerror_void_error_union, .anyerror, .error_set_inferred, - .error_set_merged, - => return AbiAlignmentAdvanced{ .scalar = 2 }, // TODO revisit this when we have the concept of the error tag type + => return AbiAlignmentAdvanced{ .scalar = 2 }, + + .error_set => { + const err_set_obj = ty.castTag(.error_set).?.data; + const names = err_set_obj.names.keys(); + if (names.len <= 1) { + return AbiAlignmentAdvanced{ .scalar = 0 }; + } else { + return AbiAlignmentAdvanced{ .scalar = 2 }; + } + }, + .error_set_merged => { + const name_map = ty.castTag(.error_set_merged).?.data; + const names = name_map.keys(); + if (names.len <= 1) { + return AbiAlignmentAdvanced{ .scalar = 0 }; + } else { + return AbiAlignmentAdvanced{ .scalar = 2 }; + } + }, .array, .array_sentinel => return ty.elemType().abiAlignmentAdvanced(target, strat), @@ -2900,31 +2935,29 @@ pub const Type = extern union { }, .error_union => { + // This code needs to be kept in sync with the equivalent switch prong + // in abiSizeAdvanced. const data = ty.castTag(.error_union).?.data; + if (data.error_set.errorSetCardinality() == .zero) { + return abiAlignmentAdvanced(data.payload, target, strat); + } + const code_align = abiAlignment(Type.anyerror, target); switch (strat) { .eager, .sema_kit => { - if (!(try data.error_set.hasRuntimeBitsAdvanced(false, sema_kit))) { - return data.payload.abiAlignmentAdvanced(target, strat); - } else if (!(try data.payload.hasRuntimeBitsAdvanced(false, sema_kit))) { - return data.error_set.abiAlignmentAdvanced(target, strat); + if (!(try data.payload.hasRuntimeBitsAdvanced(false, sema_kit))) { + return AbiAlignmentAdvanced{ .scalar = code_align }; } return AbiAlignmentAdvanced{ .scalar = @maximum( + code_align, (try data.payload.abiAlignmentAdvanced(target, strat)).scalar, - (try data.error_set.abiAlignmentAdvanced(target, strat)).scalar, ) }; }, .lazy => |arena| { switch (try data.payload.abiAlignmentAdvanced(target, strat)) { .scalar => |payload_align| { - if (payload_align == 0) { - return data.error_set.abiAlignmentAdvanced(target, strat); - } - switch (try data.error_set.abiAlignmentAdvanced(target, strat)) { - .scalar => |err_set_align| { - return AbiAlignmentAdvanced{ .scalar = @maximum(payload_align, err_set_align) }; - }, - .val => {}, - } + return AbiAlignmentAdvanced{ + .scalar = @maximum(code_align, payload_align), + }; }, .val => {}, } @@ -3018,6 +3051,7 @@ pub const Type = extern union { .@"undefined", .enum_literal, .type_info, + .error_set_single, => return AbiAlignmentAdvanced{ .scalar = 0 }, .noreturn, @@ -3136,6 +3170,7 @@ pub const Type = extern union { .empty_struct_literal, .empty_struct, .void, + .error_set_single, => return AbiSizeAdvanced{ .scalar = 0 }, .@"struct", .tuple, .anon_struct => switch (ty.containerLayout()) { @@ -3291,14 +3326,30 @@ pub const Type = extern union { }, // TODO revisit this when we have the concept of the error tag type - .error_set, - .error_set_single, .anyerror_void_error_union, .anyerror, .error_set_inferred, - .error_set_merged, => return AbiSizeAdvanced{ .scalar = 2 }, + .error_set => { + const err_set_obj = ty.castTag(.error_set).?.data; + const names = err_set_obj.names.keys(); + if (names.len <= 1) { + return AbiSizeAdvanced{ .scalar = 0 }; + } else { + return AbiSizeAdvanced{ .scalar = 2 }; + } + }, + .error_set_merged => { + const name_map = ty.castTag(.error_set_merged).?.data; + const names = name_map.keys(); + if (names.len <= 1) { + return AbiSizeAdvanced{ .scalar = 0 }; + } else { + return AbiSizeAdvanced{ .scalar = 2 }; + } + }, + .i16, .u16 => return AbiSizeAdvanced{ .scalar = intAbiSize(16, target) }, .i32, .u32 => return AbiSizeAdvanced{ .scalar = intAbiSize(32, target) }, .i64, .u64 => return AbiSizeAdvanced{ .scalar = intAbiSize(64, target) }, @@ -3325,24 +3376,42 @@ pub const Type = extern union { }, .error_union => { + // This code needs to be kept in sync with the equivalent switch prong + // in abiAlignmentAdvanced. const data = ty.castTag(.error_union).?.data; - if (!data.error_set.hasRuntimeBits() and !data.payload.hasRuntimeBits()) { - return AbiSizeAdvanced{ .scalar = 0 }; - } else if (!data.error_set.hasRuntimeBits()) { - return AbiSizeAdvanced{ .scalar = data.payload.abiSize(target) }; - } else if (!data.payload.hasRuntimeBits()) { - return AbiSizeAdvanced{ .scalar = data.error_set.abiSize(target) }; + // Here we need to care whether or not the error set is *empty* or whether + // it only has *one possible value*. In the former case, it means there + // cannot possibly be an error, meaning the ABI size is equivalent to the + // payload ABI size. In the latter case, we need to account for the "tag" + // because even if both the payload type and the error set type of an + // error union have no runtime bits, an error union still has + // 1 bit of data which is whether or not the value is an error. + // Zig still uses the error code encoding at runtime, even when only 1 bit + // would suffice. This prevents coercions from needing to branch. + if (data.error_set.errorSetCardinality() == .zero) { + return abiSizeAdvanced(data.payload, target, strat); } - const code_align = abiAlignment(data.error_set, target); + const code_size = abiSize(Type.anyerror, target); + if (!data.payload.hasRuntimeBits()) { + // Same as anyerror. + return AbiSizeAdvanced{ .scalar = code_size }; + } + const code_align = abiAlignment(Type.anyerror, target); const payload_align = abiAlignment(data.payload, target); - const big_align = @maximum(code_align, payload_align); const payload_size = abiSize(data.payload, target); var size: u64 = 0; - size += abiSize(data.error_set, target); - size = std.mem.alignForwardGeneric(u64, size, payload_align); - size += payload_size; - size = std.mem.alignForwardGeneric(u64, size, big_align); + if (code_align > payload_align) { + size += code_size; + size = std.mem.alignForwardGeneric(u64, size, payload_align); + size += payload_size; + size = std.mem.alignForwardGeneric(u64, size, code_align); + } else { + size += payload_size; + size = std.mem.alignForwardGeneric(u64, size, code_align); + size += code_size; + size = std.mem.alignForwardGeneric(u64, size, payload_align); + } return AbiSizeAdvanced{ .scalar = size }; }, } @@ -4166,6 +4235,35 @@ pub const Type = extern union { }; } + const ErrorSetCardinality = enum { zero, one, many }; + + pub fn errorSetCardinality(ty: Type) ErrorSetCardinality { + switch (ty.tag()) { + .anyerror => return .many, + .error_set_inferred => return .many, + .error_set_single => return .one, + .error_set => { + const err_set_obj = ty.castTag(.error_set).?.data; + const names = err_set_obj.names.keys(); + switch (names.len) { + 0 => return .zero, + 1 => return .one, + else => return .many, + } + }, + .error_set_merged => { + const name_map = ty.castTag(.error_set_merged).?.data; + const names = name_map.keys(); + switch (names.len) { + 0 => return .zero, + 1 => return .one, + else => return .many, + } + }, + else => unreachable, + } + } + /// Returns true if it is an error set that includes anyerror, false otherwise. /// Note that the result may be a false negative if the type did not get error set /// resolution prior to this call. @@ -4664,10 +4762,7 @@ pub const Type = extern union { .enum_literal, .anyerror_void_error_union, .error_union, - .error_set, - .error_set_single, .error_set_inferred, - .error_set_merged, .@"opaque", .var_args_param, .manyptr_u8, @@ -4696,6 +4791,18 @@ pub const Type = extern union { .bound_fn, => return null, + .error_set_single => return Value.initTag(.the_only_possible_value), + .error_set => { + const err_set_obj = ty.castTag(.error_set).?.data; + if (err_set_obj.names.count() > 1) return null; + return Value.initTag(.the_only_possible_value); + }, + .error_set_merged => { + const name_map = ty.castTag(.error_set_merged).?.data; + if (name_map.count() > 1) return null; + return Value.initTag(.the_only_possible_value); + }, + .@"struct" => { const s = ty.castTag(.@"struct").?.data; assert(s.haveFieldTypes()); diff --git a/test/behavior/error.zig b/test/behavior/error.zig index ada0f3bbf1..376d1bdf09 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -148,18 +148,39 @@ test "implicit cast to optional to error union to return result loc" { //comptime S.entry(); TODO } -test "error: fn returning empty error set can be passed as fn returning any error" { +test "fn returning empty error set can be passed as fn returning any error" { entry(); comptime entry(); } +test "fn returning empty error set can be passed as fn returning any error - pointer" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + + entryPtr(); + comptime entryPtr(); +} + fn entry() void { foo2(bar2); } +fn entryPtr() void { + var ptr = &bar2; + fooPtr(ptr); +} + fn foo2(f: fn () anyerror!void) void { const x = f(); - x catch {}; + x catch { + @panic("fail"); + }; +} + +fn fooPtr(f: *const fn () anyerror!void) void { + const x = f(); + x catch { + @panic("fail"); + }; } fn bar2() (error{}!void) {} @@ -239,7 +260,11 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void { } test "comptime err to int of error set with only 1 possible value" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); From c90a97f9be9ffef858b0e450de5006f61a12fafd Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Mon, 23 May 2022 18:24:03 +0200 Subject: [PATCH 1611/2031] codegen: Order error union fields per alignment Based on the size of the payload the native backends will lower the error union with its fields (errorset & payload) in the correct order. e.g. ErrorA!u8 will first lower the error set's value and then the payload. In the event of ErrorA!u32 will lower the payload first. --- src/codegen.zig | 50 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index bd556baa5e..81b303ab82 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -702,16 +702,50 @@ pub fn generateSymbol( .ErrorUnion => { const error_ty = typed_value.ty.errorUnionSet(); const payload_ty = typed_value.ty.errorUnionPayload(); + + if (error_ty.errorSetCardinality() == .zero) { + const payload_val = typed_value.val.castTag(.eu_payload).?.data; + return generateSymbol(bin_file, src_loc, .{ + .ty = payload_ty, + .val = payload_val, + }, code, debug_output, reloc_info); + } + const is_payload = typed_value.val.errorUnionIsPayload(); + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + const err_val = if (!is_payload) typed_value.val else Value.initTag(.zero); + return generateSymbol(bin_file, src_loc, .{ + .ty = error_ty, + .val = err_val, + }, code, debug_output, reloc_info); + } + + const payload_align = payload_ty.abiAlignment(target); + const error_align = Type.anyerror.abiAlignment(target); const abi_align = typed_value.ty.abiAlignment(target); - { - const error_val = if (!is_payload) typed_value.val else Value.initTag(.zero); - const begin = code.items.len; + // error value first when its type is larger than the error union's payload + if (error_align > payload_align) { switch (try generateSymbol(bin_file, src_loc, .{ .ty = error_ty, - .val = error_val, + .val = if (is_payload) Value.initTag(.zero) else typed_value.val, + }, code, debug_output, reloc_info)) { + .appended => {}, + .externally_managed => |external_slice| { + code.appendSliceAssumeCapacity(external_slice); + }, + .fail => |em| return Result{ .fail = em }, + } + } + + // emit payload part of the error union + { + const begin = code.items.len; + const payload_val = if (typed_value.val.castTag(.eu_payload)) |val| val.data else Value.initTag(.undef); + switch (try generateSymbol(bin_file, src_loc, .{ + .ty = payload_ty, + .val = payload_val, }, code, debug_output, reloc_info)) { .appended => {}, .externally_managed => |external_slice| { @@ -728,12 +762,12 @@ pub fn generateSymbol( } } - if (payload_ty.hasRuntimeBits()) { + // Payload size is larger than error set, so emit our error set last + if (error_align < payload_align) { const begin = code.items.len; - const payload_val = if (typed_value.val.castTag(.eu_payload)) |val| val.data else Value.initTag(.undef); switch (try generateSymbol(bin_file, src_loc, .{ - .ty = payload_ty, - .val = payload_val, + .ty = error_ty, + .val = if (is_payload) Value.initTag(.zero) else typed_value.val, }, code, debug_output, reloc_info)) { .appended => {}, .externally_managed => |external_slice| { From 3a059ebe4c84a1e541bb3b2ccee2e7cc25686a4d Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Mon, 23 May 2022 22:06:27 +0200 Subject: [PATCH 1612/2031] wasm: Fixes for error union semantics --- src/arch/wasm/CodeGen.zig | 158 ++++++++++++++++++++++++++------------ src/codegen.zig | 6 +- test/behavior/error.zig | 1 - 3 files changed, 112 insertions(+), 53 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index a35589f043..b74651859c 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -636,7 +636,7 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue { // means we must generate it from a constant. const val = self.air.value(ref).?; const ty = self.air.typeOf(ref); - if (!ty.hasRuntimeBitsIgnoreComptime() and !ty.isInt()) { + if (!ty.hasRuntimeBitsIgnoreComptime() and !ty.isInt() and !ty.isError()) { gop.value_ptr.* = WValue{ .none = {} }; return gop.value_ptr.*; } @@ -804,6 +804,8 @@ fn genFunctype(gpa: Allocator, fn_info: Type.Payload.Function.Data, target: std. } else { try returns.append(typeToValtype(fn_info.return_type, target)); } + } else if (fn_info.return_type.isError()) { + try returns.append(.i32); } // param types @@ -1373,10 +1375,15 @@ fn isByRef(ty: Type, target: std.Target) bool { .Int => return ty.intInfo(target).bits > 64, .Float => return ty.floatBits(target) > 64, .ErrorUnion => { - const has_tag = ty.errorUnionSet().hasRuntimeBitsIgnoreComptime(); - const has_pl = ty.errorUnionPayload().hasRuntimeBitsIgnoreComptime(); - if (!has_tag or !has_pl) return false; - return ty.hasRuntimeBitsIgnoreComptime(); + const err_ty = ty.errorUnionSet(); + const pl_ty = ty.errorUnionPayload(); + if (err_ty.errorSetCardinality() == .zero) { + return isByRef(pl_ty, target); + } + if (!pl_ty.hasRuntimeBitsIgnoreComptime()) { + return false; + } + return true; }, .Optional => { if (ty.isPtrLikeOptional()) return false; @@ -1624,13 +1631,14 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); - const ret_ty = self.decl.ty.fnReturnType(); + const fn_info = self.decl.ty.fnInfo(); + const ret_ty = fn_info.return_type; // result must be stored in the stack and we return a pointer // to the stack instead if (self.return_value != .none) { - try self.store(self.return_value, operand, self.decl.ty.fnReturnType(), 0); - } else if (self.decl.ty.fnInfo().cc == .C and ret_ty.hasRuntimeBitsIgnoreComptime()) { + try self.store(self.return_value, operand, ret_ty, 0); + } else if (fn_info.cc == .C and ret_ty.hasRuntimeBitsIgnoreComptime()) { switch (ret_ty.zigTypeTag()) { // Aggregate types can be lowered as a singular value .Struct, .Union => { @@ -1650,7 +1658,11 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { else => try self.emitWValue(operand), } } else { - try self.emitWValue(operand); + if (!ret_ty.hasRuntimeBitsIgnoreComptime() and ret_ty.isError()) { + try self.addImm32(0); + } else { + try self.emitWValue(operand); + } } try self.restoreStackPointer(); try self.addTag(.@"return"); @@ -1675,7 +1687,13 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); const ret_ty = self.air.typeOf(un_op).childType(); - if (!ret_ty.hasRuntimeBitsIgnoreComptime()) return WValue.none; + if (!ret_ty.hasRuntimeBitsIgnoreComptime()) { + if (ret_ty.isError()) { + try self.addImm32(0); + } else { + return WValue.none; + } + } if (!firstParamSRet(self.decl.ty.fnInfo(), self.target)) { const result = try self.load(operand, ret_ty, 0); @@ -1723,8 +1741,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const sret = if (first_param_sret) blk: { const sret_local = try self.allocStack(ret_ty); - const ptr_offset = try self.buildPointerOffset(sret_local, 0, .new); - try self.emitWValue(ptr_offset); + try self.lowerToStack(sret_local); break :blk sret_local; } else WValue{ .none = {} }; @@ -1754,7 +1771,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. try self.addLabel(.call_indirect, fn_type_index); } - if (self.liveness.isUnused(inst) or !ret_ty.hasRuntimeBitsIgnoreComptime()) { + if (self.liveness.isUnused(inst) or (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError())) { return WValue.none; } else if (ret_ty.isNoReturn()) { try self.addTag(.@"unreachable"); @@ -1796,8 +1813,11 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro .ErrorUnion => { const err_ty = ty.errorUnionSet(); const pl_ty = ty.errorUnionPayload(); + if (err_ty.errorSetCardinality() == .zero) { + return self.store(lhs, rhs, pl_ty, 0); + } if (!pl_ty.hasRuntimeBitsIgnoreComptime()) { - return self.store(lhs, rhs, err_ty, 0); + return self.store(lhs, rhs, Type.anyerror, 0); } const len = @intCast(u32, ty.abiSize(self.target)); @@ -2256,6 +2276,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { const target = self.target; switch (ty.zigTypeTag()) { + .Void => return WValue{ .none = {} }, .Int => { const int_info = ty.intInfo(self.target); switch (int_info.signedness) { @@ -2324,6 +2345,10 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { }, .ErrorUnion => { const error_type = ty.errorUnionSet(); + if (error_type.errorSetCardinality() == .zero) { + const pl_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef); + return self.lowerConstant(pl_val, ty.errorUnionPayload()); + } const is_pl = val.errorUnionIsPayload(); const err_val = if (!is_pl) val else Value.initTag(.zero); return self.lowerConstant(err_val, error_type); @@ -2892,12 +2917,19 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W const err_ty = self.air.typeOf(un_op); const pl_ty = err_ty.errorUnionPayload(); - // load the error tag value + if (err_ty.errorUnionSet().errorSetCardinality() == .zero) { + switch (opcode) { + .i32_ne => return WValue{ .imm32 = 0 }, + .i32_eq => return WValue{ .imm32 = 1 }, + else => unreachable, + } + } + try self.emitWValue(operand); if (pl_ty.hasRuntimeBitsIgnoreComptime()) { try self.addMemArg(.i32_load16_u, .{ - .offset = operand.offset(), - .alignment = err_ty.errorUnionSet().abiAlignment(self.target), + .offset = operand.offset() + errUnionErrorOffset(pl_ty, self.target), + .alignment = Type.anyerror.abiAlignment(self.target), }); } @@ -2905,7 +2937,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W try self.addImm32(0); try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); - const is_err_tmp = try self.allocLocal(Type.initTag(.i32)); // result is always an i32 + const is_err_tmp = try self.allocLocal(Type.i32); try self.addLabel(.local_set, is_err_tmp.local); return is_err_tmp; } @@ -2917,14 +2949,18 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) const op_ty = self.air.typeOf(ty_op.operand); const err_ty = if (op_is_ptr) op_ty.childType() else op_ty; const payload_ty = err_ty.errorUnionPayload(); - if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} }; - const err_align = err_ty.abiAlignment(self.target); - const set_size = err_ty.errorUnionSet().abiSize(self.target); - const offset = mem.alignForwardGeneric(u64, set_size, err_align); - if (op_is_ptr or isByRef(payload_ty, self.target)) { - return self.buildPointerOffset(operand, offset, .new); + + if (err_ty.errorUnionSet().errorSetCardinality() == .zero) { + return operand; } - return self.load(operand, payload_ty, @intCast(u32, offset)); + + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} }; + + const pl_offset = errUnionPayloadOffset(payload_ty, self.target); + if (op_is_ptr or isByRef(payload_ty, self.target)) { + return self.buildPointerOffset(operand, pl_offset, .new); + } + return self.load(operand, payload_ty, pl_offset); } fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!WValue { @@ -2935,11 +2971,16 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) In const op_ty = self.air.typeOf(ty_op.operand); const err_ty = if (op_is_ptr) op_ty.childType() else op_ty; const payload_ty = err_ty.errorUnionPayload(); + + if (err_ty.errorUnionSet().errorSetCardinality() == .zero) { + return WValue{ .imm32 = 0 }; + } + if (op_is_ptr or !payload_ty.hasRuntimeBitsIgnoreComptime()) { return operand; } - return self.load(operand, err_ty.errorUnionSet(), 0); + return self.load(operand, Type.anyerror, errUnionErrorOffset(payload_ty, self.target)); } fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -2947,22 +2988,26 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); + const err_ty = self.air.typeOfIndex(inst); - const op_ty = self.air.typeOf(ty_op.operand); - if (!op_ty.hasRuntimeBitsIgnoreComptime()) return operand; - const err_union_ty = self.air.getRefType(ty_op.ty); - const err_align = err_union_ty.abiAlignment(self.target); - const set_size = err_union_ty.errorUnionSet().abiSize(self.target); - const offset = mem.alignForwardGeneric(u64, set_size, err_align); + if (err_ty.errorUnionSet().errorSetCardinality() == .zero) { + return operand; + } - const err_union = try self.allocStack(err_union_ty); - const payload_ptr = try self.buildPointerOffset(err_union, offset, .new); - try self.store(payload_ptr, operand, op_ty, 0); + const pl_ty = self.air.typeOf(ty_op.operand); + if (!pl_ty.hasRuntimeBitsIgnoreComptime()) { + return operand; + } + + const err_union = try self.allocStack(err_ty); + const payload_ptr = try self.buildPointerOffset(err_union, errUnionPayloadOffset(pl_ty, self.target), .new); + try self.store(payload_ptr, operand, pl_ty, 0); // ensure we also write '0' to the error part, so any present stack value gets overwritten by it. try self.emitWValue(err_union); try self.addImm32(0); - try self.addMemArg(.i32_store16, .{ .offset = err_union.offset(), .alignment = 2 }); + const err_val_offset = errUnionErrorOffset(pl_ty, self.target); + try self.addMemArg(.i32_store16, .{ .offset = err_union.offset() + err_val_offset, .alignment = 2 }); return err_union; } @@ -2973,17 +3018,18 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); const err_ty = self.air.getRefType(ty_op.ty); + const pl_ty = err_ty.errorUnionPayload(); - if (!err_ty.errorUnionPayload().hasRuntimeBitsIgnoreComptime()) return operand; + if (!pl_ty.hasRuntimeBitsIgnoreComptime()) { + return operand; + } const err_union = try self.allocStack(err_ty); - try self.store(err_union, operand, err_ty.errorUnionSet(), 0); + // store error value + try self.store(err_union, operand, Type.anyerror, errUnionErrorOffset(pl_ty, self.target)); // write 'undefined' to the payload - const err_align = err_ty.abiAlignment(self.target); - const set_size = err_ty.errorUnionSet().abiSize(self.target); - const offset = mem.alignForwardGeneric(u64, set_size, err_align); - const payload_ptr = try self.buildPointerOffset(err_union, offset, .new); + const payload_ptr = try self.buildPointerOffset(err_union, errUnionPayloadOffset(pl_ty, self.target), .new); const len = @intCast(u32, err_ty.errorUnionPayload().abiSize(self.target)); try self.memset(payload_ptr, .{ .imm32 = len }, .{ .imm32 = 0xaaaaaaaa }); @@ -3927,12 +3973,16 @@ fn airFptrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const err_set_ty = self.air.typeOf(ty_op.operand).childType(); - const err_ty = err_set_ty.errorUnionSet(); const payload_ty = err_set_ty.errorUnionPayload(); const operand = try self.resolveInst(ty_op.operand); // set error-tag to '0' to annotate error union is non-error - try self.store(operand, .{ .imm32 = 0 }, err_ty, 0); + try self.store( + operand, + .{ .imm32 = 0 }, + Type.anyerror, + errUnionErrorOffset(payload_ty, self.target), + ); if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; @@ -3940,11 +3990,7 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue return operand; } - const err_align = err_set_ty.abiAlignment(self.target); - const set_size = err_ty.abiSize(self.target); - const offset = mem.alignForwardGeneric(u64, set_size, err_align); - - return self.buildPointerOffset(operand, @intCast(u32, offset), .new); + return self.buildPointerOffset(operand, errUnionPayloadOffset(payload_ty, self.target), .new); } fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -4572,3 +4618,17 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !WValue { } }); return WValue{ .none = {} }; } + +fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u32 { + if (Type.anyerror.abiAlignment(target) > payload_ty.abiAlignment(target)) { + return @intCast(u32, Type.anyerror.abiSize(target)); + } + return 0; +} + +fn errUnionErrorOffset(payload_ty: Type, target: std.Target) u32 { + if (Type.anyerror.abiAlignment(target) > payload_ty.abiAlignment(target)) { + return 0; + } + return @intCast(u32, payload_ty.abiSize(target)); +} diff --git a/src/codegen.zig b/src/codegen.zig index 81b303ab82..0f411dc481 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -714,7 +714,7 @@ pub fn generateSymbol( const is_payload = typed_value.val.errorUnionIsPayload(); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { - const err_val = if (!is_payload) typed_value.val else Value.initTag(.zero); + const err_val = if (is_payload) Value.initTag(.zero) else typed_value.val; return generateSymbol(bin_file, src_loc, .{ .ty = error_ty, .val = err_val, @@ -763,7 +763,7 @@ pub fn generateSymbol( } // Payload size is larger than error set, so emit our error set last - if (error_align < payload_align) { + if (error_align <= payload_align) { const begin = code.items.len; switch (try generateSymbol(bin_file, src_loc, .{ .ty = error_ty, @@ -794,7 +794,7 @@ pub fn generateSymbol( try code.writer().writeInt(u32, kv.value, endian); }, else => { - try code.writer().writeByteNTimes(0, @intCast(usize, typed_value.ty.abiSize(target))); + try code.writer().writeByteNTimes(0, @intCast(usize, Type.anyerror.abiSize(target))); }, } return Result{ .appended = {} }; diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 376d1bdf09..459ffb12d0 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -260,7 +260,6 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void { } test "comptime err to int of error set with only 1 possible value" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From c97c7f9e3bade44136f2bdf8ec4015f1b1b8303f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 23 May 2022 14:36:21 -0700 Subject: [PATCH 1613/2031] C backend: update to new error union semantics --- src/codegen/c.zig | 169 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 117 insertions(+), 52 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 5f61f8586e..63082d46be 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -749,6 +749,12 @@ pub const DeclGen = struct { const error_type = ty.errorUnionSet(); const payload_type = ty.errorUnionPayload(); + if (error_type.errorSetCardinality() == .zero) { + // We use the payload directly as the type. + const payload_val = val.castTag(.eu_payload).?.data; + return dg.renderValue(writer, payload_type, payload_val, location); + } + if (!payload_type.hasRuntimeBits()) { // We use the error type directly as the type. const err_val = if (val.errorUnionIsPayload()) Value.initTag(.zero) else val; @@ -894,10 +900,12 @@ pub const DeclGen = struct { try w.writeAll("ZIG_COLD "); } } - const return_ty = dg.decl.ty.fnReturnType(); - if (return_ty.hasRuntimeBits()) { - try dg.renderType(w, return_ty); - } else if (return_ty.zigTypeTag() == .NoReturn) { + const fn_info = dg.decl.ty.fnInfo(); + if (fn_info.return_type.hasRuntimeBits()) { + try dg.renderType(w, fn_info.return_type); + } else if (fn_info.return_type.isError()) { + try dg.renderType(w, Type.anyerror); + } else if (fn_info.return_type.zigTypeTag() == .NoReturn) { try w.writeAll("zig_noreturn void"); } else { try w.writeAll("void"); @@ -905,22 +913,19 @@ pub const DeclGen = struct { try w.writeAll(" "); try dg.renderDeclName(w, dg.decl_index); try w.writeAll("("); - const param_len = dg.decl.ty.fnParamLen(); - var index: usize = 0; var params_written: usize = 0; - while (index < param_len) : (index += 1) { - const param_type = dg.decl.ty.fnParamType(index); + for (fn_info.param_types) |param_type, index| { if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; if (params_written > 0) { try w.writeAll(", "); } const name = CValue{ .arg = index }; - try dg.renderTypeAndName(w, dg.decl.ty.fnParamType(index), name, .Mut, 0); + try dg.renderTypeAndName(w, param_type, name, .Mut, 0); params_written += 1; } - if (dg.decl.ty.fnIsVarArgs()) { + if (fn_info.is_var_args) { if (params_written != 0) try w.writeAll(", "); try w.writeAll("..."); } else if (params_written == 0) { @@ -1156,26 +1161,36 @@ pub const DeclGen = struct { } fn renderErrorUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { - const child_type = t.errorUnionPayload(); - const err_set_type = t.errorUnionSet(); + const payload_ty = t.errorUnionPayload(); + const error_ty = t.errorUnionSet(); var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); defer buffer.deinit(); const bw = buffer.writer(); - try bw.writeAll("typedef struct { "); const payload_name = CValue{ .bytes = "payload" }; - try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, 0); - try bw.writeAll("; uint16_t error; } "); + const target = dg.module.getTarget(); + const payload_align = payload_ty.abiAlignment(target); + const error_align = Type.anyerror.abiAlignment(target); + if (error_align > payload_align) { + try bw.writeAll("typedef struct { "); + try dg.renderTypeAndName(bw, payload_ty, payload_name, .Mut, 0); + try bw.writeAll("; uint16_t error; } "); + } else { + try bw.writeAll("typedef struct { uint16_t error; "); + try dg.renderTypeAndName(bw, payload_ty, payload_name, .Mut, 0); + try bw.writeAll("; } "); + } + const name_index = buffer.items.len; - if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| { + if (error_ty.castTag(.error_set_inferred)) |inf_err_set_payload| { const func = inf_err_set_payload.data.func; try bw.writeAll("zig_E_"); try dg.renderDeclName(bw, func.owner_decl); try bw.writeAll(";\n"); } else { try bw.print("zig_E_{s}_{s};\n", .{ - typeToCIdentifier(err_set_type, dg.module), typeToCIdentifier(child_type, dg.module), + typeToCIdentifier(error_ty, dg.module), typeToCIdentifier(payload_ty, dg.module), }); } @@ -1359,12 +1374,19 @@ pub const DeclGen = struct { return w.writeAll(name); }, .ErrorSet => { - comptime assert(Type.initTag(.anyerror).abiSize(builtin.target) == 2); + comptime assert(Type.anyerror.abiSize(builtin.target) == 2); return w.writeAll("uint16_t"); }, .ErrorUnion => { - if (t.errorUnionPayload().abiSize(target) == 0) { - return dg.renderType(w, t.errorUnionSet()); + const error_ty = t.errorUnionSet(); + const payload_ty = t.errorUnionPayload(); + + if (error_ty.errorSetCardinality() == .zero) { + return dg.renderType(w, payload_ty); + } + + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + return dg.renderType(w, Type.anyerror); } const name = dg.getTypedefName(t) orelse @@ -1901,8 +1923,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .array_elem_val => try airArrayElemVal(f, inst), .unwrap_errunion_payload => try airUnwrapErrUnionPay(f, inst, ""), - .unwrap_errunion_err => try airUnwrapErrUnionErr(f, inst), .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(f, inst, "&"), + .unwrap_errunion_err => try airUnwrapErrUnionErr(f, inst), .unwrap_errunion_err_ptr => try airUnwrapErrUnionErr(f, inst), .wrap_errunion_payload => try airWrapErrUnionPay(f, inst), .wrap_errunion_err => try airWrapErrUnionErr(f, inst), @@ -2120,11 +2142,14 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { fn airRet(f: *Function, inst: Air.Inst.Index) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; const writer = f.object.writer(); - if (f.air.typeOf(un_op).isFnOrHasRuntimeBitsIgnoreComptime()) { + const ret_ty = f.air.typeOf(un_op); + if (ret_ty.isFnOrHasRuntimeBitsIgnoreComptime()) { const operand = try f.resolveInst(un_op); try writer.writeAll("return "); try f.writeCValue(writer, operand); try writer.writeAll(";\n"); + } else if (ret_ty.isError()) { + try writer.writeAll("return 0;"); } else { try writer.writeAll("return;\n"); } @@ -2136,13 +2161,16 @@ fn airRetLoad(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const ptr_ty = f.air.typeOf(un_op); const ret_ty = ptr_ty.childType(); - if (!ret_ty.isFnOrHasRuntimeBitsIgnoreComptime()) { + if (ret_ty.isFnOrHasRuntimeBitsIgnoreComptime()) { + const ptr = try f.resolveInst(un_op); + try writer.writeAll("return *"); + try f.writeCValue(writer, ptr); + try writer.writeAll(";\n"); + } else if (ret_ty.isError()) { + try writer.writeAll("return 0;\n"); + } else { try writer.writeAll("return;\n"); } - const ptr = try f.resolveInst(un_op); - try writer.writeAll("return *"); - try f.writeCValue(writer, ptr); - try writer.writeAll(";\n"); return CValue.none; } @@ -2713,19 +2741,20 @@ fn airCall( .Pointer => callee_ty.childType(), else => unreachable, }; - const ret_ty = fn_ty.fnReturnType(); - const unused_result = f.liveness.isUnused(inst); const writer = f.object.writer(); - var result_local: CValue = .none; - if (unused_result) { - if (ret_ty.hasRuntimeBits()) { - try writer.print("(void)", .{}); + const result_local: CValue = r: { + if (f.liveness.isUnused(inst)) { + if (loweredFnRetTyHasBits(fn_ty)) { + try writer.print("(void)", .{}); + } + break :r .none; + } else { + const local = try f.allocLocal(fn_ty.fnReturnType(), .Const); + try writer.writeAll(" = "); + break :r local; } - } else { - result_local = try f.allocLocal(ret_ty, .Const); - try writer.writeAll(" = "); - } + }; callee: { known: { @@ -3307,7 +3336,8 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { return local; } -// *(E!T) -> E NOT *E +/// *(E!T) -> E +/// Note that the result is never a pointer. fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; @@ -3319,7 +3349,11 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { const operand_ty = f.air.typeOf(ty_op.operand); if (operand_ty.zigTypeTag() == .Pointer) { - if (!operand_ty.childType().errorUnionPayload().hasRuntimeBits()) { + const err_union_ty = operand_ty.childType(); + if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { + return CValue{ .bytes = "0" }; + } + if (!err_union_ty.errorUnionPayload().hasRuntimeBits()) { return operand; } const local = try f.allocLocal(inst_ty, .Const); @@ -3328,6 +3362,9 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(";\n"); return local; } + if (operand_ty.errorUnionSet().errorSetCardinality() == .zero) { + return CValue{ .bytes = "0" }; + } if (!operand_ty.errorUnionPayload().hasRuntimeBits()) { return operand; } @@ -3343,7 +3380,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { return local; } -fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, maybe_addrof: []const u8) !CValue { +fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, maybe_addrof: [*:0]const u8) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; @@ -3351,17 +3388,19 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, maybe_addrof: []cons const writer = f.object.writer(); const operand = try f.resolveInst(ty_op.operand); const operand_ty = f.air.typeOf(ty_op.operand); + const operand_is_ptr = operand_ty.zigTypeTag() == .Pointer; + const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; + + if (error_union_ty.errorUnionSet().errorSetCardinality() == .zero) { + return operand; + } - const error_union_ty = if (operand_ty.zigTypeTag() == .Pointer) - operand_ty.childType() - else - operand_ty; if (!error_union_ty.errorUnionPayload().hasRuntimeBits()) { return CValue.none; } const inst_ty = f.air.typeOfIndex(inst); - const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else "."; + const maybe_deref = if (operand_is_ptr) "->" else "."; const local = try f.allocLocal(inst_ty, .Const); try writer.print(" = {s}(", .{maybe_addrof}); @@ -3421,6 +3460,11 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { const error_ty = error_union_ty.errorUnionSet(); const payload_ty = error_union_ty.errorUnionPayload(); + if (error_ty.errorSetCardinality() == .zero) { + // TODO: write undefined bytes through the pointer here + return operand; + } + // First, set the non-error value. if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { try f.writeCValueDeref(writer, operand); @@ -3464,6 +3508,9 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(ty_op.operand); const inst_ty = f.air.typeOfIndex(inst); + if (inst_ty.errorUnionSet().errorSetCardinality() == .zero) { + return operand; + } const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = { .error = 0, .payload = "); try f.writeCValue(writer, operand); @@ -3486,16 +3533,23 @@ fn airIsErr( const operand_ty = f.air.typeOf(un_op); const local = try f.allocLocal(Type.initTag(.bool), .Const); const payload_ty = operand_ty.errorUnionPayload(); + const error_ty = operand_ty.errorUnionSet(); + try writer.writeAll(" = "); - if (is_ptr) { - try f.writeCValueDeref(writer, operand); + + if (error_ty.errorSetCardinality() == .zero) { + try writer.print("0 {s} 0;\n", .{op_str}); } else { - try f.writeCValue(writer, operand); + if (is_ptr) { + try f.writeCValueDeref(writer, operand); + } else { + try f.writeCValue(writer, operand); + } + if (payload_ty.hasRuntimeBits()) { + try writer.writeAll(".error"); + } + try writer.print(" {s} 0;\n", .{op_str}); } - if (payload_ty.hasRuntimeBits()) { - try writer.writeAll(".error"); - } - try writer.print(" {s} 0;\n", .{op_str}); return local; } @@ -4129,3 +4183,14 @@ fn intMin(ty: Type, target: std.Target, buf: []u8) []const u8 { }, } } + +fn loweredFnRetTyHasBits(fn_ty: Type) bool { + const ret_ty = fn_ty.fnReturnType(); + if (ret_ty.hasRuntimeBitsIgnoreComptime()) { + return true; + } + if (ret_ty.isError()) { + return true; + } + return false; +} From 02e9d9b43b3b1cd9a4858a1f2bff302057dc2ee2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 23 May 2022 18:48:10 -0700 Subject: [PATCH 1614/2031] stage2: make `?anyerror` represented the same as `anyerror` I was able to get the backend implementation working on LLVM and the C backend, but I'm going to ask for some help on the other backends. --- src/arch/wasm/CodeGen.zig | 23 +++++++++------- src/codegen.zig | 2 +- src/codegen/c.zig | 13 +++++---- src/codegen/llvm.zig | 34 +++++++++++++++-------- src/type.zig | 58 +++++++++++++++++++++++++++++++++++---- test/behavior/error.zig | 3 +- 6 files changed, 98 insertions(+), 35 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index b74651859c..6d0f3a9d23 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1386,7 +1386,7 @@ fn isByRef(ty: Type, target: std.Target) bool { return true; }, .Optional => { - if (ty.isPtrLikeOptional()) return false; + if (ty.optionalReprIsPayload()) return false; var buf: Type.Payload.ElemType = undefined; return ty.optionalChild(&buf).hasRuntimeBitsIgnoreComptime(); }, @@ -1832,6 +1832,9 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro if (!pl_ty.hasRuntimeBitsIgnoreComptime()) { return self.store(lhs, rhs, Type.u8, 0); } + if (pl_ty.zigTypeTag() == .ErrorSet) { + return self.store(lhs, rhs, Type.anyerror, 0); + } const len = @intCast(u32, ty.abiSize(self.target)); return self.memcpy(lhs, rhs, .{ .imm32 = len }); @@ -2198,7 +2201,7 @@ fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WV const parent_ptr = try self.lowerParentPtr(payload_ptr.container_ptr, payload_ptr.container_ty); var buf: Type.Payload.ElemType = undefined; const payload_ty = payload_ptr.container_ty.optionalChild(&buf); - if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.isPtrLikeOptional()) { + if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.optionalReprIsPayload()) { return parent_ptr; } @@ -2353,7 +2356,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { const err_val = if (!is_pl) val else Value.initTag(.zero); return self.lowerConstant(err_val, error_type); }, - .Optional => if (ty.isPtrLikeOptional()) { + .Optional => if (ty.optionalReprIsPayload()) { var buf: Type.Payload.ElemType = undefined; const pl_ty = ty.optionalChild(&buf); if (val.castTag(.opt_payload)) |payload| { @@ -2392,7 +2395,7 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!WValue { .Optional => { var buf: Type.Payload.ElemType = undefined; const pl_ty = ty.optionalChild(&buf); - if (ty.isPtrLikeOptional()) { + if (ty.optionalReprIsPayload()) { return self.emitUndefined(pl_ty); } return WValue{ .imm32 = 0xaaaaaaaa }; @@ -2542,7 +2545,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner } fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOperator) InnerError!WValue { - if (ty.zigTypeTag() == .Optional and !ty.isPtrLikeOptional()) { + if (ty.zigTypeTag() == .Optional and !ty.optionalReprIsPayload()) { var buf: Type.Payload.ElemType = undefined; const payload_ty = ty.optionalChild(&buf); if (payload_ty.hasRuntimeBitsIgnoreComptime()) { @@ -3120,7 +3123,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: en fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue { try self.emitWValue(operand); - if (!optional_ty.isPtrLikeOptional()) { + if (!optional_ty.optionalReprIsPayload()) { var buf: Type.Payload.ElemType = undefined; const payload_ty = optional_ty.optionalChild(&buf); // When payload is zero-bits, we can treat operand as a value, rather than @@ -3146,7 +3149,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const opt_ty = self.air.typeOf(ty_op.operand); const payload_ty = self.air.typeOfIndex(inst); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} }; - if (opt_ty.isPtrLikeOptional()) return operand; + if (opt_ty.optionalReprIsPayload()) return operand; const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target); @@ -3166,7 +3169,7 @@ fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { var buf: Type.Payload.ElemType = undefined; const payload_ty = opt_ty.optionalChild(&buf); - if (!payload_ty.hasRuntimeBitsIgnoreComptime() or opt_ty.isPtrLikeOptional()) { + if (!payload_ty.hasRuntimeBitsIgnoreComptime() or opt_ty.optionalReprIsPayload()) { return operand; } @@ -3184,7 +3187,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue return self.fail("TODO: Implement OptionalPayloadPtrSet for optional with zero-sized type {}", .{payload_ty.fmtDebug()}); } - if (opt_ty.isPtrLikeOptional()) { + if (opt_ty.optionalReprIsPayload()) { return operand; } @@ -3215,7 +3218,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const operand = try self.resolveInst(ty_op.operand); const op_ty = self.air.typeOfIndex(inst); - if (op_ty.isPtrLikeOptional()) { + if (op_ty.optionalReprIsPayload()) { return operand; } const offset = std.math.cast(u32, op_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) catch { diff --git a/src/codegen.zig b/src/codegen.zig index 0f411dc481..eea8095a62 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -654,7 +654,7 @@ pub fn generateSymbol( return Result{ .appended = {} }; } - if (typed_value.ty.isPtrLikeOptional()) { + if (typed_value.ty.optionalReprIsPayload()) { if (typed_value.val.castTag(.opt_payload)) |payload| { switch (try generateSymbol(bin_file, src_loc, .{ .ty = payload_type, diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 63082d46be..1b6708c1cf 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -712,7 +712,7 @@ pub const DeclGen = struct { .Optional => { var opt_buf: Type.Payload.ElemType = undefined; const payload_type = ty.optionalChild(&opt_buf); - if (ty.isPtrLikeOptional()) { + if (ty.optionalReprIsPayload()) { return dg.renderValue(writer, payload_type, val, location); } if (payload_type.abiSize(target) == 0) { @@ -1360,7 +1360,7 @@ pub const DeclGen = struct { var opt_buf: Type.Payload.ElemType = undefined; const child_type = t.optionalChild(&opt_buf); - if (t.isPtrLikeOptional()) { + if (t.optionalReprIsPayload()) { return dg.renderType(w, child_type); } @@ -3161,6 +3161,8 @@ fn airIsNull( if (ty.isPtrLikeOptional()) { // operand is a regular pointer, test `operand !=/== NULL` try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator }); + } else if (payload_type.zigTypeTag() == .ErrorSet) { + try writer.print("){s} {s} 0;\n", .{ deref_suffix, operator }); } else if (payload_type.abiSize(target) == 0) { try writer.print("){s} {s} true;\n", .{ deref_suffix, operator }); } else { @@ -3183,7 +3185,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { else operand_ty; - if (opt_ty.isPtrLikeOptional()) { + if (opt_ty.optionalReprIsPayload()) { // the operand is just a regular pointer, no need to do anything special. // *?*T -> **T and ?*T -> *T are **T -> **T and *T -> *T in C return operand; @@ -3209,7 +3211,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { const opt_ty = operand_ty.elemType(); - if (opt_ty.isPtrLikeOptional()) { + if (opt_ty.optionalReprIsPayload()) { // The payload and the optional are the same value. // Setting to non-null will be done when the payload is set. return operand; @@ -3419,8 +3421,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(ty_op.operand); const inst_ty = f.air.typeOfIndex(inst); - if (inst_ty.isPtrLikeOptional()) { - // the operand is just a regular pointer, no need to do anything special. + if (inst_ty.optionalReprIsPayload()) { return operand; } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 95d12dff3a..e76b2941c2 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1390,7 +1390,7 @@ pub const Object = struct { gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_ty); return di_ty; } - if (ty.isPtrLikeOptional()) { + if (ty.optionalReprIsPayload()) { const ptr_di_ty = try o.lowerDebugType(child_ty, resolve); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .mod = o.module }); @@ -1472,6 +1472,12 @@ pub const Object = struct { .ErrorUnion => { const err_set_ty = ty.errorUnionSet(); const payload_ty = ty.errorUnionPayload(); + if (err_set_ty.errorSetCardinality() == .zero) { + const payload_di_ty = try o.lowerDebugType(payload_ty, .full); + // The recursive call to `lowerDebugType` means we can't use `gop` anymore. + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(payload_di_ty), .{ .mod = o.module }); + return payload_di_ty; + } if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { const err_set_di_ty = try o.lowerDebugType(err_set_ty, .full); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. @@ -2439,7 +2445,7 @@ pub const DeclGen = struct { return dg.context.intType(1); } const payload_llvm_ty = try dg.llvmType(child_ty); - if (t.isPtrLikeOptional()) { + if (t.optionalReprIsPayload()) { return payload_llvm_ty; } @@ -3058,7 +3064,7 @@ pub const DeclGen = struct { if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { return non_null_bit; } - if (tv.ty.isPtrLikeOptional()) { + if (tv.ty.optionalReprIsPayload()) { if (tv.val.castTag(.opt_payload)) |payload| { return dg.genTypedValue(.{ .ty = payload_ty, .val = payload.data }); } else if (is_pl) { @@ -3557,7 +3563,9 @@ pub const DeclGen = struct { const payload_ty = opt_payload_ptr.container_ty.optionalChild(&buf); bitcast_needed = !payload_ty.eql(ptr_child_ty, dg.module); - if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.isPtrLikeOptional()) { + if (!payload_ty.hasRuntimeBitsIgnoreComptime() or + payload_ty.optionalReprIsPayload()) + { // In this case, we represent pointer to optional the same as pointer // to the payload. break :blk parent_llvm_ptr; @@ -4461,7 +4469,9 @@ pub const FuncGen = struct { .Int, .Bool, .Pointer, .ErrorSet => scalar_ty, .Optional => blk: { const payload_ty = operand_ty.optionalChild(&opt_buffer); - if (!payload_ty.hasRuntimeBitsIgnoreComptime() or operand_ty.isPtrLikeOptional()) { + if (!payload_ty.hasRuntimeBitsIgnoreComptime() or + operand_ty.optionalReprIsPayload()) + { break :blk operand_ty; } // We need to emit instructions to check for equality/inequality @@ -5414,7 +5424,7 @@ pub const FuncGen = struct { const operand = try self.resolveInst(un_op); const operand_ty = self.air.typeOf(un_op); const optional_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; - if (optional_ty.isPtrLikeOptional()) { + if (optional_ty.optionalReprIsPayload()) { const optional_llvm_ty = try self.dg.llvmType(optional_ty); const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand; return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), ""); @@ -5499,7 +5509,7 @@ pub const FuncGen = struct { const res_ptr_ty = try self.dg.llvmType(result_ty); return self.builder.buildBitCast(operand, res_ptr_ty, ""); } - if (optional_ty.isPtrLikeOptional()) { + if (optional_ty.optionalReprIsPayload()) { // The payload and the optional are the same value. return operand; } @@ -5527,7 +5537,7 @@ pub const FuncGen = struct { const res_ptr_ty = try self.dg.llvmType(result_ty); return self.builder.buildBitCast(operand, res_ptr_ty, ""); } - if (optional_ty.isPtrLikeOptional()) { + if (optional_ty.optionalReprIsPayload()) { // The payload and the optional are the same value. // Setting to non-null will be done when the payload is set. return operand; @@ -5561,7 +5571,7 @@ pub const FuncGen = struct { const payload_ty = self.air.typeOfIndex(inst); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return null; - if (optional_ty.isPtrLikeOptional()) { + if (optional_ty.optionalReprIsPayload()) { // Payload value is the same as the optional value. return operand; } @@ -5702,7 +5712,9 @@ pub const FuncGen = struct { if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return non_null_bit; const operand = try self.resolveInst(ty_op.operand); const optional_ty = self.air.typeOfIndex(inst); - if (optional_ty.isPtrLikeOptional()) return operand; + if (optional_ty.optionalReprIsPayload()) { + return operand; + } const llvm_optional_ty = try self.dg.llvmType(optional_ty); if (isByRef(optional_ty)) { const optional_ptr = self.buildAlloca(llvm_optional_ty); @@ -7038,7 +7050,7 @@ pub const FuncGen = struct { } const success_bit = self.builder.buildExtractValue(result, 1, ""); - if (optional_ty.isPtrLikeOptional()) { + if (optional_ty.optionalReprIsPayload()) { return self.builder.buildSelect(success_bit, payload.typeOf().constNull(), payload, ""); } diff --git a/src/type.zig b/src/type.zig index 4b8a41915f..1c59cf9e59 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2916,8 +2916,10 @@ pub const Type = extern union { var buf: Payload.ElemType = undefined; const child_type = ty.optionalChild(&buf); - if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr()) { - return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }; + switch (child_type.zigTypeTag()) { + .Pointer => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }, + .ErrorSet => return abiAlignmentAdvanced(Type.anyerror, target, strat), + else => {}, } switch (strat) { @@ -3365,14 +3367,29 @@ pub const Type = extern union { const child_type = ty.optionalChild(&buf); if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 }; - if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr() and !child_type.isSlice()) - return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }; + switch (child_type.zigTypeTag()) { + .Pointer => { + const ptr_info = child_type.ptrInfo().data; + const has_null = switch (ptr_info.size) { + .Slice, .C => true, + else => ptr_info.@"allowzero", + }; + if (!has_null) { + const ptr_size_bytes = @divExact(target.cpu.arch.ptrBitWidth(), 8); + return AbiSizeAdvanced{ .scalar = ptr_size_bytes }; + } + }, + .ErrorSet => return abiSizeAdvanced(Type.anyerror, target, strat), + else => {}, + } // Optional types are represented as a struct with the child type as the first // field and a boolean as the second. Since the child type's abi alignment is // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal // to the child type's ABI alignment. - return AbiSizeAdvanced{ .scalar = child_type.abiAlignment(target) + child_type.abiSize(target) }; + return AbiSizeAdvanced{ + .scalar = child_type.abiAlignment(target) + child_type.abiSize(target), + }; }, .error_union => { @@ -3901,8 +3918,39 @@ pub const Type = extern union { return ty.ptrInfo().data.@"allowzero"; } + /// See also `isPtrLikeOptional`. + pub fn optionalReprIsPayload(ty: Type) bool { + switch (ty.tag()) { + .optional_single_const_pointer, + .optional_single_mut_pointer, + .c_const_pointer, + .c_mut_pointer, + => return true, + + .optional => { + const child_ty = ty.castTag(.optional).?.data; + switch (child_ty.zigTypeTag()) { + .Pointer => { + const info = child_ty.ptrInfo().data; + switch (info.size) { + .Slice, .C => return false, + .Many, .One => return !info.@"allowzero", + } + }, + .ErrorSet => return true, + else => return false, + } + }, + + .pointer => return ty.castTag(.pointer).?.data.size == .C, + + else => return false, + } + } + /// Returns true if the type is optional and would be lowered to a single pointer /// address value, using 0 for null. Note that this returns true for C pointers. + /// See also `hasOptionalRepr`. pub fn isPtrLikeOptional(self: Type) bool { switch (self.tag()) { .optional_single_const_pointer, diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 459ffb12d0..1b2a67bd57 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -433,9 +433,8 @@ test "return function call to error set from error union function" { } test "optional error set is the same size as error set" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - comptime try expect(@sizeOf(?anyerror) == @sizeOf(anyerror)); + comptime try expect(@alignOf(?anyerror) == @alignOf(anyerror)); const S = struct { fn returnsOptErrSet() ?anyerror { return null; From 1bf7a6dff5e39aeeeefe2016a423d16f73ba2263 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 23 May 2022 19:12:30 -0700 Subject: [PATCH 1615/2031] enable passing behavior test This was disabled for macOS but I just tested it on my M1 and it works fine. --- test/behavior/eval.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index e0e787509a..2d53122706 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -425,7 +425,6 @@ test "f64 at compile time is lossy" { } test { - if (builtin.zig_backend != .stage1 and builtin.os.tag == .macos) return error.SkipZigTest; comptime try expect(@as(f128, 1 << 113) == 10384593717069655257060992658440192); } From 7db39384f7384b73c51dd9cab5b23ad0a3699fd0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 23 May 2022 19:49:04 -0700 Subject: [PATCH 1616/2031] move bound function behavior test to compile error test --- test/behavior/eval.zig | 22 ------------------- ... on bound fn referring to var instance.zig | 20 +++++++++++++++++ 2 files changed, 20 insertions(+), 22 deletions(-) create mode 100644 test/cases/compile_errors/call method on bound fn referring to var instance.zig diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 2d53122706..3ffa0a3a12 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -572,28 +572,6 @@ test "inlined loop has array literal with elided runtime scope on first iteratio } } -test "call method on bound fn referring to var instance" { - if (builtin.zig_backend != .stage1) { - // Let's delay solving this one; I want to try to eliminate bound functions from - // the language. - return error.SkipZigTest; // TODO - } - - try expect(bound_fn() == 1237); -} - -const SimpleStruct = struct { - field: i32, - - fn method(self: *const SimpleStruct) i32 { - return self.field + 3; - } -}; - -var simple_struct = SimpleStruct{ .field = 1234 }; - -const bound_fn = simple_struct.method; - test "ptr to local array argument at comptime" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO diff --git a/test/cases/compile_errors/call method on bound fn referring to var instance.zig b/test/cases/compile_errors/call method on bound fn referring to var instance.zig new file mode 100644 index 0000000000..10ff584124 --- /dev/null +++ b/test/cases/compile_errors/call method on bound fn referring to var instance.zig @@ -0,0 +1,20 @@ +export fn entry() void { + bad(bound_fn() == 1237); +} +const SimpleStruct = struct { + field: i32, + + fn method(self: *const SimpleStruct) i32 { + return self.field + 3; + } +}; +var simple_struct = SimpleStruct{ .field = 1234 }; +const bound_fn = simple_struct.method; +fn bad(ok: bool) void { + _ = ok; +} +// error +// target=native +// backend=stage2 +// +// :12:18: error: unable to resolve comptime value From cd59b8277d018a3418267b9fa0c219debdff5ca3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 23 May 2022 21:05:24 -0700 Subject: [PATCH 1617/2031] LLVM: rename two functions llvmType -> lowerType genTypedValue -> lowerValue --- src/codegen/llvm.zig | 366 +++++++++++++++++++++---------------------- 1 file changed, 183 insertions(+), 183 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index e76b2941c2..cf0188b060 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -745,7 +745,7 @@ pub const Object = struct { const param = llvm_func.getParam(llvm_arg_i); llvm_arg_i += 1; - const param_llvm_ty = try dg.llvmType(param_ty); + const param_llvm_ty = try dg.lowerType(param_ty); const abi_size = @intCast(c_uint, param_ty.abiSize(target)); const int_llvm_ty = dg.context.intType(abi_size * 8); const int_ptr_llvm_ty = int_llvm_ty.pointerType(0); @@ -775,7 +775,7 @@ pub const Object = struct { .Struct => { const fields = param_ty.structFields().values(); if (is_by_ref) { - const param_llvm_ty = try dg.llvmType(param_ty); + const param_llvm_ty = try dg.lowerType(param_ty); const arg_ptr = buildAllocaInner(builder, llvm_func, false, param_llvm_ty); arg_ptr.setAlignment(param_ty.abiAlignment(target)); @@ -2100,7 +2100,7 @@ pub const DeclGen = struct { break :init_val decl.val; }; if (init_val.tag() != .unreachable_value) { - const llvm_init = try dg.genTypedValue(.{ .ty = decl.ty, .val = init_val }); + const llvm_init = try dg.lowerValue(.{ .ty = decl.ty, .val = init_val }); if (global.globalGetValueType() == llvm_init.typeOf()) { global.setInitializer(llvm_init); } else { @@ -2171,7 +2171,7 @@ pub const DeclGen = struct { const target = dg.module.getTarget(); const sret = firstParamSRet(fn_info, target); - const fn_type = try dg.llvmType(zig_fn_type); + const fn_type = try dg.lowerType(zig_fn_type); const fqn = try decl.getFullyQualifiedName(dg.module); defer dg.gpa.free(fqn); @@ -2198,7 +2198,7 @@ pub const DeclGen = struct { dg.addArgAttr(llvm_fn, 0, "nonnull"); // Sret pointers must not be address 0 dg.addArgAttr(llvm_fn, 0, "noalias"); - const raw_llvm_ret_ty = try dg.llvmType(fn_info.return_type); + const raw_llvm_ret_ty = try dg.lowerType(fn_info.return_type); llvm_fn.addSretAttr(0, raw_llvm_ret_ty); } @@ -2291,7 +2291,7 @@ pub const DeclGen = struct { const fqn = try decl.getFullyQualifiedName(dg.module); defer dg.gpa.free(fqn); - const llvm_type = try dg.llvmType(decl.ty); + const llvm_type = try dg.lowerType(decl.ty); const llvm_addrspace = dg.llvmAddressSpace(decl.@"addrspace"); const llvm_global = dg.object.llvm_module.addGlobalInAddressSpace(llvm_type, fqn, llvm_addrspace); gop.value_ptr.* = llvm_global; @@ -2345,15 +2345,15 @@ pub const DeclGen = struct { } fn isUnnamedType(dg: *DeclGen, ty: Type, val: *const llvm.Value) bool { - // Once `llvmType` succeeds, successive calls to it with the same Zig type - // are guaranteed to succeed. So if a call to `llvmType` fails here it means + // Once `lowerType` succeeds, successive calls to it with the same Zig type + // are guaranteed to succeed. So if a call to `lowerType` fails here it means // it is the first time lowering the type, which means the value can't possible // have that type. - const llvm_ty = dg.llvmType(ty) catch return true; + const llvm_ty = dg.lowerType(ty) catch return true; return val.typeOf() != llvm_ty; } - fn llvmType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type { + fn lowerType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type { const gpa = dg.gpa; const target = dg.module.getTarget(); switch (t.zigTypeTag()) { @@ -2385,8 +2385,8 @@ pub const DeclGen = struct { const ptr_type = t.slicePtrFieldType(&buf); const fields: [2]*const llvm.Type = .{ - try dg.llvmType(ptr_type), - try dg.llvmType(Type.usize), + try dg.lowerType(ptr_type), + try dg.lowerType(Type.usize), }; return dg.context.structType(&fields, fields.len, .False); } @@ -2402,7 +2402,7 @@ pub const DeclGen = struct { else => elem_ty.hasRuntimeBitsIgnoreComptime(), }; const llvm_elem_ty = if (lower_elem_ty) - try dg.llvmType(elem_ty) + try dg.lowerType(elem_ty) else dg.context.intType(8); return llvm_elem_ty.pointerType(llvm_addrspace); @@ -2430,12 +2430,12 @@ pub const DeclGen = struct { .Array => { const elem_ty = t.childType(); assert(elem_ty.onePossibleValue() == null); - const elem_llvm_ty = try dg.llvmType(elem_ty); + const elem_llvm_ty = try dg.lowerType(elem_ty); const total_len = t.arrayLen() + @boolToInt(t.sentinel() != null); return elem_llvm_ty.arrayType(@intCast(c_uint, total_len)); }, .Vector => { - const elem_type = try dg.llvmType(t.childType()); + const elem_type = try dg.lowerType(t.childType()); return elem_type.vectorType(t.vectorLen()); }, .Optional => { @@ -2444,7 +2444,7 @@ pub const DeclGen = struct { if (!child_ty.hasRuntimeBitsIgnoreComptime()) { return dg.context.intType(1); } - const payload_llvm_ty = try dg.llvmType(child_ty); + const payload_llvm_ty = try dg.lowerType(child_ty); if (t.optionalReprIsPayload()) { return payload_llvm_ty; } @@ -2458,13 +2458,13 @@ pub const DeclGen = struct { const error_type = t.errorUnionSet(); const payload_type = t.errorUnionPayload(); if (error_type.errorSetCardinality() == .zero) { - return dg.llvmType(payload_type); + return dg.lowerType(payload_type); } if (!payload_type.hasRuntimeBitsIgnoreComptime()) { - return try dg.llvmType(Type.anyerror); + return try dg.lowerType(Type.anyerror); } - const llvm_error_type = try dg.llvmType(error_type); - const llvm_payload_type = try dg.llvmType(payload_type); + const llvm_error_type = try dg.lowerType(error_type); + const llvm_payload_type = try dg.lowerType(payload_type); const payload_align = payload_type.abiAlignment(target); const error_align = Type.anyerror.abiAlignment(target); @@ -2515,7 +2515,7 @@ pub const DeclGen = struct { const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len)); try llvm_field_types.append(gpa, llvm_array_ty); } - const field_llvm_ty = try dg.llvmType(field_ty); + const field_llvm_ty = try dg.lowerType(field_ty); try llvm_field_types.append(gpa, field_llvm_ty); offset += field_ty.abiSize(target); @@ -2544,7 +2544,7 @@ pub const DeclGen = struct { if (struct_obj.layout == .Packed) { var buf: Type.Payload.Bits = undefined; const int_ty = struct_obj.packedIntegerType(target, &buf); - const int_llvm_ty = try dg.llvmType(int_ty); + const int_llvm_ty = try dg.lowerType(int_ty); gop.value_ptr.* = int_llvm_ty; return int_llvm_ty; } @@ -2579,7 +2579,7 @@ pub const DeclGen = struct { const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len)); try llvm_field_types.append(gpa, llvm_array_ty); } - const field_llvm_ty = try dg.llvmType(field.ty); + const field_llvm_ty = try dg.lowerType(field.ty); try llvm_field_types.append(gpa, field_llvm_ty); offset += field.ty.abiSize(target); @@ -2614,7 +2614,7 @@ pub const DeclGen = struct { const union_obj = t.cast(Type.Payload.Union).?.data; if (layout.payload_size == 0) { - const enum_tag_llvm_ty = try dg.llvmType(union_obj.tag_ty); + const enum_tag_llvm_ty = try dg.lowerType(union_obj.tag_ty); gop.value_ptr.* = enum_tag_llvm_ty; return enum_tag_llvm_ty; } @@ -2626,7 +2626,7 @@ pub const DeclGen = struct { gop.value_ptr.* = llvm_union_ty; // must be done before any recursive calls const aligned_field = union_obj.fields.values()[layout.most_aligned_field]; - const llvm_aligned_field_ty = try dg.llvmType(aligned_field.ty); + const llvm_aligned_field_ty = try dg.lowerType(aligned_field.ty); const llvm_payload_ty = t: { if (layout.most_aligned_field_size == layout.payload_size) { @@ -2645,7 +2645,7 @@ pub const DeclGen = struct { llvm_union_ty.structSetBody(&llvm_fields, llvm_fields.len, .False); return llvm_union_ty; } - const enum_tag_llvm_ty = try dg.llvmType(union_obj.tag_ty); + const enum_tag_llvm_ty = try dg.lowerType(union_obj.tag_ty); // Put the tag before or after the payload depending on which one's // alignment is greater. @@ -2667,7 +2667,7 @@ pub const DeclGen = struct { llvm_union_ty.structSetBody(&llvm_fields, llvm_fields_len, .False); return llvm_union_ty; }, - .Fn => return llvmTypeFn(dg, t), + .Fn => return lowerTypeFn(dg, t), .ComptimeInt => unreachable, .ComptimeFloat => unreachable, .Type => unreachable, @@ -2682,7 +2682,7 @@ pub const DeclGen = struct { } } - fn llvmTypeFn(dg: *DeclGen, fn_ty: Type) Allocator.Error!*const llvm.Type { + fn lowerTypeFn(dg: *DeclGen, fn_ty: Type) Allocator.Error!*const llvm.Type { const target = dg.module.getTarget(); const fn_info = fn_ty.fnInfo(); const llvm_ret_ty = try lowerFnRetTy(dg, fn_info); @@ -2691,7 +2691,7 @@ pub const DeclGen = struct { defer llvm_params.deinit(); if (firstParamSRet(fn_info, target)) { - const llvm_sret_ty = try dg.llvmType(fn_info.return_type); + const llvm_sret_ty = try dg.lowerType(fn_info.return_type); try llvm_params.append(llvm_sret_ty.pointerType(0)); } @@ -2703,7 +2703,7 @@ pub const DeclGen = struct { .data = dg.object.getStackTraceType(), }; const ptr_ty = Type.initPayload(&ptr_ty_payload.base); - try llvm_params.append(try dg.llvmType(ptr_ty)); + try llvm_params.append(try dg.lowerType(ptr_ty)); } var it = iterateParamTypes(dg, fn_info); @@ -2711,11 +2711,11 @@ pub const DeclGen = struct { .no_bits => continue, .byval => { const param_ty = fn_info.param_types[it.zig_index - 1]; - try llvm_params.append(try dg.llvmType(param_ty)); + try llvm_params.append(try dg.lowerType(param_ty)); }, .byref => { const param_ty = fn_info.param_types[it.zig_index - 1]; - const raw_llvm_ty = try dg.llvmType(param_ty); + const raw_llvm_ty = try dg.lowerType(param_ty); try llvm_params.append(raw_llvm_ty.pointerType(0)); }, .abi_sized_int => { @@ -2757,7 +2757,7 @@ pub const DeclGen = struct { // one field; in this case keep the type information // to avoid the potentially costly ptrtoint/bitcast. if (bits_used == 0 and field_abi_bits == int_bits) { - const llvm_field_ty = try dg.llvmType(field.ty); + const llvm_field_ty = try dg.lowerType(field.ty); llvm_params.appendAssumeCapacity(llvm_field_ty); field_i += 1; if (field_i >= fields.len) { @@ -2795,16 +2795,16 @@ pub const DeclGen = struct { ); } - fn genTypedValue(dg: *DeclGen, tv: TypedValue) Error!*const llvm.Value { + fn lowerValue(dg: *DeclGen, tv: TypedValue) Error!*const llvm.Value { if (tv.val.isUndef()) { - const llvm_type = try dg.llvmType(tv.ty); + const llvm_type = try dg.lowerType(tv.ty); return llvm_type.getUndef(); } const target = dg.module.getTarget(); switch (tv.ty.zigTypeTag()) { .Bool => { - const llvm_type = try dg.llvmType(tv.ty); + const llvm_type = try dg.lowerType(tv.ty); return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(); }, // TODO this duplicates code with Pointer but they should share the handling @@ -2865,7 +2865,7 @@ pub const DeclGen = struct { return unsigned_val; }, .Float => { - const llvm_ty = try dg.llvmType(tv.ty); + const llvm_ty = try dg.lowerType(tv.ty); switch (tv.ty.floatBits(target)) { 16, 32, 64 => return llvm_ty.constReal(tv.val.toFloat(f64)), 80 => { @@ -2902,7 +2902,7 @@ pub const DeclGen = struct { const decl = dg.module.declPtr(decl_index); dg.module.markDeclAlive(decl); const val = try dg.resolveGlobalDecl(decl_index); - const llvm_var_type = try dg.llvmType(tv.ty); + const llvm_var_type = try dg.lowerType(tv.ty); const llvm_addrspace = dg.llvmAddressSpace(decl.@"addrspace"); const llvm_type = llvm_var_type.pointerType(llvm_addrspace); return val.constBitCast(llvm_type); @@ -2911,11 +2911,11 @@ pub const DeclGen = struct { const slice = tv.val.castTag(.slice).?.data; var buf: Type.SlicePtrFieldTypeBuffer = undefined; const fields: [2]*const llvm.Value = .{ - try dg.genTypedValue(.{ + try dg.lowerValue(.{ .ty = tv.ty.slicePtrFieldType(&buf), .val = slice.ptr, }), - try dg.genTypedValue(.{ + try dg.lowerValue(.{ .ty = Type.usize, .val = slice.len, }), @@ -2923,15 +2923,15 @@ pub const DeclGen = struct { return dg.context.constStruct(&fields, fields.len, .False); }, .int_u64, .one, .int_big_positive => { - const llvm_usize = try dg.llvmType(Type.usize); + const llvm_usize = try dg.lowerType(Type.usize); const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(target), .False); - return llvm_int.constIntToPtr(try dg.llvmType(tv.ty)); + return llvm_int.constIntToPtr(try dg.lowerType(tv.ty)); }, .field_ptr, .opt_payload_ptr, .eu_payload_ptr, .elem_ptr => { return dg.lowerParentPtr(tv.val, tv.ty.childType()); }, .null_value, .zero => { - const llvm_type = try dg.llvmType(tv.ty); + const llvm_type = try dg.lowerType(tv.ty); return llvm_type.constNull(); }, else => |tag| return dg.todo("implement const of pointer type '{}' ({})", .{ @@ -2986,7 +2986,7 @@ pub const DeclGen = struct { defer gpa.free(llvm_elems); var need_unnamed = false; for (elem_vals[0..len]) |elem_val, i| { - llvm_elems[i] = try dg.genTypedValue(.{ .ty = elem_ty, .val = elem_val }); + llvm_elems[i] = try dg.lowerValue(.{ .ty = elem_ty, .val = elem_val }); need_unnamed = need_unnamed or dg.isUnnamedType(elem_ty, llvm_elems[i]); } if (need_unnamed) { @@ -2996,7 +2996,7 @@ pub const DeclGen = struct { .True, ); } else { - const llvm_elem_ty = try dg.llvmType(elem_ty); + const llvm_elem_ty = try dg.lowerType(elem_ty); return llvm_elem_ty.constArray( llvm_elems.ptr, @intCast(c_uint, llvm_elems.len), @@ -3016,13 +3016,13 @@ pub const DeclGen = struct { var need_unnamed = false; if (len != 0) { for (llvm_elems[0..len]) |*elem| { - elem.* = try dg.genTypedValue(.{ .ty = elem_ty, .val = val }); + elem.* = try dg.lowerValue(.{ .ty = elem_ty, .val = val }); } need_unnamed = need_unnamed or dg.isUnnamedType(elem_ty, llvm_elems[0]); } if (sentinel) |sent| { - llvm_elems[len] = try dg.genTypedValue(.{ .ty = elem_ty, .val = sent }); + llvm_elems[len] = try dg.lowerValue(.{ .ty = elem_ty, .val = sent }); need_unnamed = need_unnamed or dg.isUnnamedType(elem_ty, llvm_elems[len]); } @@ -3033,7 +3033,7 @@ pub const DeclGen = struct { .True, ); } else { - const llvm_elem_ty = try dg.llvmType(elem_ty); + const llvm_elem_ty = try dg.lowerType(elem_ty); return llvm_elem_ty.constArray( llvm_elems.ptr, @intCast(c_uint, llvm_elems.len), @@ -3043,13 +3043,13 @@ pub const DeclGen = struct { .empty_array_sentinel => { const elem_ty = tv.ty.elemType(); const sent_val = tv.ty.sentinel().?; - const sentinel = try dg.genTypedValue(.{ .ty = elem_ty, .val = sent_val }); + const sentinel = try dg.lowerValue(.{ .ty = elem_ty, .val = sent_val }); const llvm_elems: [1]*const llvm.Value = .{sentinel}; const need_unnamed = dg.isUnnamedType(elem_ty, llvm_elems[0]); if (need_unnamed) { return dg.context.constStruct(&llvm_elems, llvm_elems.len, .True); } else { - const llvm_elem_ty = try dg.llvmType(elem_ty); + const llvm_elem_ty = try dg.lowerType(elem_ty); return llvm_elem_ty.constArray(&llvm_elems, llvm_elems.len); } }, @@ -3066,17 +3066,17 @@ pub const DeclGen = struct { } if (tv.ty.optionalReprIsPayload()) { if (tv.val.castTag(.opt_payload)) |payload| { - return dg.genTypedValue(.{ .ty = payload_ty, .val = payload.data }); + return dg.lowerValue(.{ .ty = payload_ty, .val = payload.data }); } else if (is_pl) { - return dg.genTypedValue(.{ .ty = payload_ty, .val = tv.val }); + return dg.lowerValue(.{ .ty = payload_ty, .val = tv.val }); } else { - const llvm_ty = try dg.llvmType(tv.ty); + const llvm_ty = try dg.lowerType(tv.ty); return llvm_ty.constNull(); } } assert(payload_ty.zigTypeTag() != .Fn); const fields: [2]*const llvm.Value = .{ - try dg.genTypedValue(.{ + try dg.lowerValue(.{ .ty = payload_ty, .val = if (tv.val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef), }), @@ -3095,7 +3095,7 @@ pub const DeclGen = struct { return dg.resolveLlvmFunction(fn_decl_index); }, .ErrorSet => { - const llvm_ty = try dg.llvmType(tv.ty); + const llvm_ty = try dg.lowerType(tv.ty); switch (tv.val.tag()) { .@"error" => { const err_name = tv.val.castTag(.@"error").?.data.name; @@ -3113,23 +3113,23 @@ pub const DeclGen = struct { const payload_type = tv.ty.errorUnionPayload(); if (error_type.errorSetCardinality() == .zero) { const payload_val = tv.val.castTag(.eu_payload).?.data; - return dg.genTypedValue(.{ .ty = payload_type, .val = payload_val }); + return dg.lowerValue(.{ .ty = payload_type, .val = payload_val }); } const is_pl = tv.val.errorUnionIsPayload(); if (!payload_type.hasRuntimeBitsIgnoreComptime()) { // We use the error type directly as the type. const err_val = if (!is_pl) tv.val else Value.initTag(.zero); - return dg.genTypedValue(.{ .ty = error_type, .val = err_val }); + return dg.lowerValue(.{ .ty = error_type, .val = err_val }); } const payload_align = payload_type.abiAlignment(target); const error_align = Type.anyerror.abiAlignment(target); - const llvm_error_value = try dg.genTypedValue(.{ + const llvm_error_value = try dg.lowerValue(.{ .ty = error_type, .val = if (is_pl) Value.initTag(.zero) else tv.val, }); - const llvm_payload_value = try dg.genTypedValue(.{ + const llvm_payload_value = try dg.lowerValue(.{ .ty = payload_type, .val = if (tv.val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef), }); @@ -3142,7 +3142,7 @@ pub const DeclGen = struct { } }, .Struct => { - const llvm_struct_ty = try dg.llvmType(tv.ty); + const llvm_struct_ty = try dg.lowerType(tv.ty); const field_vals = tv.val.castTag(.aggregate).?.data; const gpa = dg.gpa; @@ -3175,7 +3175,7 @@ pub const DeclGen = struct { llvm_fields.appendAssumeCapacity(llvm_array_ty.getUndef()); } - const field_llvm_val = try dg.genTypedValue(.{ + const field_llvm_val = try dg.lowerValue(.{ .ty = field_ty, .val = field_vals[i], }); @@ -3223,7 +3223,7 @@ pub const DeclGen = struct { const field = fields[i]; if (!field.ty.hasRuntimeBitsIgnoreComptime()) continue; - const non_int_val = try dg.genTypedValue(.{ + const non_int_val = try dg.lowerValue(.{ .ty = field.ty, .val = field_val, }); @@ -3267,7 +3267,7 @@ pub const DeclGen = struct { llvm_fields.appendAssumeCapacity(llvm_array_ty.getUndef()); } - const field_llvm_val = try dg.genTypedValue(.{ + const field_llvm_val = try dg.lowerValue(.{ .ty = field.ty, .val = field_vals[i], }); @@ -3302,13 +3302,13 @@ pub const DeclGen = struct { } }, .Union => { - const llvm_union_ty = try dg.llvmType(tv.ty); + const llvm_union_ty = try dg.lowerType(tv.ty); const tag_and_val = tv.val.castTag(.@"union").?.data; const layout = tv.ty.unionGetLayout(target); if (layout.payload_size == 0) { - return genTypedValue(dg, .{ + return lowerValue(dg, .{ .ty = tv.ty.unionTagType().?, .val = tag_and_val.tag, }); @@ -3322,7 +3322,7 @@ pub const DeclGen = struct { const padding_len = @intCast(c_uint, layout.payload_size); break :p dg.context.intType(8).arrayType(padding_len).getUndef(); } - const field = try genTypedValue(dg, .{ .ty = field_ty, .val = tag_and_val.val }); + const field = try lowerValue(dg, .{ .ty = field_ty, .val = tag_and_val.val }); const field_size = field_ty.abiSize(target); if (field_size == layout.payload_size) { break :p field; @@ -3348,7 +3348,7 @@ pub const DeclGen = struct { return llvm_union_ty.constNamedStruct(&fields, fields.len); } } - const llvm_tag_value = try genTypedValue(dg, .{ + const llvm_tag_value = try lowerValue(dg, .{ .ty = tv.ty.unionTagType().?, .val = tag_and_val.tag, }); @@ -3385,7 +3385,7 @@ pub const DeclGen = struct { .data = bytes[i], }; - elem.* = try dg.genTypedValue(.{ + elem.* = try dg.lowerValue(.{ .ty = elem_ty, .val = Value.initPayload(&byte_payload.base), }); @@ -3405,7 +3405,7 @@ pub const DeclGen = struct { const llvm_elems = try dg.gpa.alloc(*const llvm.Value, vector_len); defer dg.gpa.free(llvm_elems); for (llvm_elems) |*elem, i| { - elem.* = try dg.genTypedValue(.{ .ty = elem_ty, .val = elem_vals[i] }); + elem.* = try dg.lowerValue(.{ .ty = elem_ty, .val = elem_vals[i] }); } return llvm.constVector( llvm_elems.ptr, @@ -3420,7 +3420,7 @@ pub const DeclGen = struct { const llvm_elems = try dg.gpa.alloc(*const llvm.Value, len); defer dg.gpa.free(llvm_elems); for (llvm_elems) |*elem| { - elem.* = try dg.genTypedValue(.{ .ty = elem_ty, .val = val }); + elem.* = try dg.lowerValue(.{ .ty = elem_ty, .val = val }); } return llvm.constVector( llvm_elems.ptr, @@ -3470,7 +3470,7 @@ pub const DeclGen = struct { if (ptr_child_ty.eql(decl.ty, dg.module)) { return llvm_ptr; } else { - return llvm_ptr.constBitCast((try dg.llvmType(ptr_child_ty)).pointerType(0)); + return llvm_ptr.constBitCast((try dg.lowerType(ptr_child_ty)).pointerType(0)); } } @@ -3492,15 +3492,15 @@ pub const DeclGen = struct { }, .int_i64 => { const int = ptr_val.castTag(.int_i64).?.data; - const llvm_usize = try dg.llvmType(Type.usize); + const llvm_usize = try dg.lowerType(Type.usize); const llvm_int = llvm_usize.constInt(@bitCast(u64, int), .False); - return llvm_int.constIntToPtr((try dg.llvmType(ptr_child_ty)).pointerType(0)); + return llvm_int.constIntToPtr((try dg.lowerType(ptr_child_ty)).pointerType(0)); }, .int_u64 => { const int = ptr_val.castTag(.int_u64).?.data; - const llvm_usize = try dg.llvmType(Type.usize); + const llvm_usize = try dg.lowerType(Type.usize); const llvm_int = llvm_usize.constInt(int, .False); - return llvm_int.constIntToPtr((try dg.llvmType(ptr_child_ty)).pointerType(0)); + return llvm_int.constIntToPtr((try dg.lowerType(ptr_child_ty)).pointerType(0)); }, .field_ptr => blk: { const field_ptr = ptr_val.castTag(.field_ptr).?.data; @@ -3549,7 +3549,7 @@ pub const DeclGen = struct { const parent_llvm_ptr = try dg.lowerParentPtr(elem_ptr.array_ptr, elem_ptr.elem_ty); bitcast_needed = !elem_ptr.elem_ty.eql(ptr_child_ty, dg.module); - const llvm_usize = try dg.llvmType(Type.usize); + const llvm_usize = try dg.lowerType(Type.usize); const indices: [1]*const llvm.Value = .{ llvm_usize.constInt(elem_ptr.index, .False), }; @@ -3602,7 +3602,7 @@ pub const DeclGen = struct { else => unreachable, }; if (bitcast_needed) { - return llvm_ptr.constBitCast((try dg.llvmType(ptr_child_ty)).pointerType(0)); + return llvm_ptr.constBitCast((try dg.lowerType(ptr_child_ty)).pointerType(0)); } else { return llvm_ptr; } @@ -3621,11 +3621,11 @@ pub const DeclGen = struct { .data = tv.val.sliceLen(self.module), }; const fields: [2]*const llvm.Value = .{ - try self.genTypedValue(.{ + try self.lowerValue(.{ .ty = ptr_ty, .val = tv.val, }), - try self.genTypedValue(.{ + try self.lowerValue(.{ .ty = Type.usize, .val = Value.initPayload(&slice_len.base), }), @@ -3657,7 +3657,7 @@ pub const DeclGen = struct { else try self.resolveGlobalDecl(decl_index); - const llvm_type = try self.llvmType(tv.ty); + const llvm_type = try self.lowerType(tv.ty); if (tv.ty.zigTypeTag() == .Int) { return llvm_val.constPtrToInt(llvm_type); } else { @@ -3672,8 +3672,8 @@ pub const DeclGen = struct { // The value cannot be undefined, because we use the `nonnull` annotation // for non-optional pointers. We also need to respect the alignment, even though // the address will never be dereferenced. - const llvm_usize = try dg.llvmType(Type.usize); - const llvm_ptr_ty = try dg.llvmType(ptr_ty); + const llvm_usize = try dg.lowerType(Type.usize); + const llvm_ptr_ty = try dg.lowerType(ptr_ty); if (alignment != 0) { return llvm_usize.constInt(alignment, .False).constIntToPtr(llvm_ptr_ty); } @@ -3852,7 +3852,7 @@ pub const FuncGen = struct { const val = self.air.value(inst).?; const ty = self.air.typeOf(inst); - const llvm_val = try self.dg.genTypedValue(.{ .ty = ty, .val = val }); + const llvm_val = try self.dg.lowerValue(.{ .ty = ty, .val = val }); if (!isByRef(ty)) { gop.value_ptr.* = llvm_val; return llvm_val; @@ -3870,7 +3870,7 @@ pub const FuncGen = struct { // Because of LLVM limitations for lowering certain types such as unions, // the type of global constants might not match the type it is supposed to // be, and so we must bitcast the pointer at the usage sites. - const wanted_llvm_ty = try self.dg.llvmType(ty); + const wanted_llvm_ty = try self.dg.lowerType(ty); const wanted_llvm_ptr_ty = wanted_llvm_ty.pointerType(0); const casted_ptr = global.constBitCast(wanted_llvm_ptr_ty); gop.value_ptr.* = casted_ptr; @@ -4094,7 +4094,7 @@ pub const FuncGen = struct { defer llvm_args.deinit(); const ret_ptr = if (!sret) null else blk: { - const llvm_ret_ty = try self.dg.llvmType(return_type); + const llvm_ret_ty = try self.dg.lowerType(return_type); const ret_ptr = self.buildAlloca(llvm_ret_ty); ret_ptr.setAlignment(return_type.abiAlignment(target)); try llvm_args.append(ret_ptr); @@ -4126,7 +4126,7 @@ pub const FuncGen = struct { // which is always lowered to an LLVM type of `*i8`. // 2. The argument is a global which does act as a pointer, however // a bitcast is needed in order for the LLVM types to match. - const llvm_param_ty = try self.dg.llvmType(param_ty); + const llvm_param_ty = try self.dg.lowerType(param_ty); const casted_ptr = self.builder.buildBitCast(llvm_arg, llvm_param_ty, ""); try llvm_args.append(casted_ptr); } else { @@ -4173,7 +4173,7 @@ pub const FuncGen = struct { ); const int_ptr = self.buildAlloca(int_llvm_ty); int_ptr.setAlignment(alignment); - const param_llvm_ty = try self.dg.llvmType(param_ty); + const param_llvm_ty = try self.dg.lowerType(param_ty); const casted_ptr = self.builder.buildBitCast(int_ptr, param_llvm_ty.pointerType(0), ""); const store_inst = self.builder.buildStore(llvm_arg, casted_ptr); store_inst.setAlignment(alignment); @@ -4284,7 +4284,7 @@ pub const FuncGen = struct { return null; } - const llvm_ret_ty = try self.dg.llvmType(return_type); + const llvm_ret_ty = try self.dg.lowerType(return_type); if (ret_ptr) |rp| { call.setCallSret(llvm_ret_ty); @@ -4354,7 +4354,7 @@ pub const FuncGen = struct { // Functions with an empty error set are emitted with an error code // return type and return zero so they can be function pointers coerced // to functions that return anyerror. - const err_int = try self.dg.llvmType(Type.anyerror); + const err_int = try self.dg.lowerType(Type.anyerror); _ = self.builder.buildRet(err_int.constInt(0, .False)); } else { _ = self.builder.buildRetVoid(); @@ -4393,7 +4393,7 @@ pub const FuncGen = struct { // Functions with an empty error set are emitted with an error code // return type and return zero so they can be function pointers coerced // to functions that return anyerror. - const err_int = try self.dg.llvmType(Type.anyerror); + const err_int = try self.dg.lowerType(Type.anyerror); _ = self.builder.buildRet(err_int.constInt(0, .False)); } else { _ = self.builder.buildRetVoid(); @@ -4407,7 +4407,7 @@ pub const FuncGen = struct { const ptr = try self.resolveInst(un_op); const target = self.dg.module.getTarget(); const abi_ret_ty = try lowerFnRetTy(self.dg, fn_info); - const llvm_ret_ty = try self.dg.llvmType(ret_ty); + const llvm_ret_ty = try self.dg.lowerType(ret_ty); const casted_ptr = if (abi_ret_ty == llvm_ret_ty) ptr else p: { const ptr_abi_ty = abi_ret_ty.pointerType(0); break :p self.builder.buildBitCast(ptr, ptr_abi_ty, ""); @@ -4588,7 +4588,7 @@ pub const FuncGen = struct { const is_body = inst_ty.zigTypeTag() == .Fn; if (!is_body and !inst_ty.hasRuntimeBitsIgnoreComptime()) return null; - const raw_llvm_ty = try self.dg.llvmType(inst_ty); + const raw_llvm_ty = try self.dg.lowerType(inst_ty); const llvm_ty = ty: { // If the zig tag type is a function, this represents an actual function body; not @@ -4728,9 +4728,9 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand_ty = self.air.typeOf(ty_op.operand); const array_ty = operand_ty.childType(); - const llvm_usize = try self.dg.llvmType(Type.usize); + const llvm_usize = try self.dg.lowerType(Type.usize); const len = llvm_usize.constInt(array_ty.arrayLen(), .False); - const slice_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst)); + const slice_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst)); if (!array_ty.hasRuntimeBitsIgnoreComptime()) { return self.builder.buildInsertValue(slice_llvm_ty.getUndef(), len, 1, ""); } @@ -4755,7 +4755,7 @@ pub const FuncGen = struct { const dest_ty = self.air.typeOfIndex(inst); const dest_scalar_ty = dest_ty.scalarType(); - const dest_llvm_ty = try self.dg.llvmType(dest_ty); + const dest_llvm_ty = try self.dg.lowerType(dest_ty); const target = self.dg.module.getTarget(); if (intrinsicsAllowed(dest_scalar_ty, target)) { @@ -4806,7 +4806,7 @@ pub const FuncGen = struct { const dest_ty = self.air.typeOfIndex(inst); const dest_scalar_ty = dest_ty.scalarType(); - const dest_llvm_ty = try self.dg.llvmType(dest_ty); + const dest_llvm_ty = try self.dg.lowerType(dest_ty); if (intrinsicsAllowed(operand_scalar_ty, target)) { // TODO set fast math flag @@ -4833,7 +4833,7 @@ pub const FuncGen = struct { compiler_rt_dest_abbrev, }) catch unreachable; - const operand_llvm_ty = try self.dg.llvmType(operand_ty); + const operand_llvm_ty = try self.dg.lowerType(operand_ty); const param_types = [1]*const llvm.Type{operand_llvm_ty}; const libc_fn = self.getLibcFunction(fn_name, ¶m_types, libc_ret_ty); const params = [1]*const llvm.Value{operand}; @@ -4994,7 +4994,7 @@ pub const FuncGen = struct { const containing_int = struct_llvm_val; const shift_amt = containing_int.typeOf().constInt(bit_offset, .False); const shifted_value = self.builder.buildLShr(containing_int, shift_amt, ""); - const elem_llvm_ty = try self.dg.llvmType(field_ty); + const elem_llvm_ty = try self.dg.lowerType(field_ty); if (field_ty.zigTypeTag() == .Float) { const elem_bits = @intCast(c_uint, field_ty.bitSize(target)); const same_size_int = self.context.intType(elem_bits); @@ -5026,7 +5026,7 @@ pub const FuncGen = struct { return self.load(field_ptr, field_ptr_ty); }, .Union => { - const llvm_field_ty = try self.dg.llvmType(field_ty); + const llvm_field_ty = try self.dg.lowerType(field_ty); const layout = struct_ty.unionGetLayout(target); const payload_index = @boolToInt(layout.tag_align >= layout.payload_align); const union_field_ptr = self.builder.buildStructGEP(struct_llvm_val, payload_index, ""); @@ -5053,7 +5053,7 @@ pub const FuncGen = struct { const struct_ty = self.air.getRefType(ty_pl.ty).childType(); const field_offset = struct_ty.structFieldOffset(extra.field_index, target); - const res_ty = try self.dg.llvmType(self.air.getRefType(ty_pl.ty)); + const res_ty = try self.dg.lowerType(self.air.getRefType(ty_pl.ty)); if (field_offset == 0) { return self.builder.buildBitCast(field_ptr, res_ty, ""); } @@ -5383,7 +5383,7 @@ pub const FuncGen = struct { } const ret_ty = self.air.typeOfIndex(inst); - const ret_llvm_ty = try self.dg.llvmType(ret_ty); + const ret_llvm_ty = try self.dg.lowerType(ret_ty); const llvm_fn_ty = llvm.functionType( ret_llvm_ty, llvm_param_types.ptr, @@ -5425,7 +5425,7 @@ pub const FuncGen = struct { const operand_ty = self.air.typeOf(un_op); const optional_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; if (optional_ty.optionalReprIsPayload()) { - const optional_llvm_ty = try self.dg.llvmType(optional_ty); + const optional_llvm_ty = try self.dg.lowerType(optional_ty); const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand; return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), ""); } @@ -5462,7 +5462,7 @@ pub const FuncGen = struct { const operand = try self.resolveInst(un_op); const err_union_ty = self.air.typeOf(un_op); const payload_ty = err_union_ty.errorUnionPayload(); - const err_set_ty = try self.dg.llvmType(Type.initTag(.anyerror)); + const err_set_ty = try self.dg.lowerType(Type.initTag(.anyerror)); const zero = err_set_ty.constNull(); if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { @@ -5506,7 +5506,7 @@ pub const FuncGen = struct { // a pointer to a zero-bit value. // TODO once we update to LLVM 14 this bitcast won't be necessary. - const res_ptr_ty = try self.dg.llvmType(result_ty); + const res_ptr_ty = try self.dg.lowerType(result_ty); return self.builder.buildBitCast(operand, res_ptr_ty, ""); } if (optional_ty.optionalReprIsPayload()) { @@ -5534,7 +5534,7 @@ pub const FuncGen = struct { _ = self.builder.buildStore(non_null_bit, operand); // TODO once we update to LLVM 14 this bitcast won't be necessary. - const res_ptr_ty = try self.dg.llvmType(result_ty); + const res_ptr_ty = try self.dg.lowerType(result_ty); return self.builder.buildBitCast(operand, res_ptr_ty, ""); } if (optional_ty.optionalReprIsPayload()) { @@ -5604,7 +5604,7 @@ pub const FuncGen = struct { if (!operand_is_ptr) return null; // TODO once we update to LLVM 14 this bitcast won't be necessary. - const res_ptr_ty = try self.dg.llvmType(result_ty); + const res_ptr_ty = try self.dg.lowerType(result_ty); return self.builder.buildBitCast(operand, res_ptr_ty, ""); } if (operand_is_ptr or isByRef(payload_ty)) { @@ -5626,7 +5626,7 @@ pub const FuncGen = struct { const operand_ty = self.air.typeOf(ty_op.operand); const err_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { - const err_llvm_ty = try self.dg.llvmType(Type.anyerror); + const err_llvm_ty = try self.dg.lowerType(Type.anyerror); if (operand_is_ptr) { return self.builder.buildBitCast(operand, err_llvm_ty.pointerType(0), ""); } else { @@ -5662,7 +5662,7 @@ pub const FuncGen = struct { return operand; } const payload_ty = error_union_ty.errorUnionPayload(); - const non_error_val = try self.dg.genTypedValue(.{ .ty = error_ty, .val = Value.zero }); + const non_error_val = try self.dg.lowerValue(.{ .ty = error_ty, .val = Value.zero }); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { _ = self.builder.buildStore(non_error_val, operand); return operand; @@ -5715,7 +5715,7 @@ pub const FuncGen = struct { if (optional_ty.optionalReprIsPayload()) { return operand; } - const llvm_optional_ty = try self.dg.llvmType(optional_ty); + const llvm_optional_ty = try self.dg.lowerType(optional_ty); if (isByRef(optional_ty)) { const optional_ptr = self.buildAlloca(llvm_optional_ty); const payload_ptr = self.builder.buildStructGEP(optional_ptr, 0, ""); @@ -5746,8 +5746,8 @@ pub const FuncGen = struct { if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { return operand; } - const ok_err_code = (try self.dg.llvmType(Type.anyerror)).constNull(); - const err_un_llvm_ty = try self.dg.llvmType(inst_ty); + const ok_err_code = (try self.dg.lowerType(Type.anyerror)).constNull(); + const err_un_llvm_ty = try self.dg.lowerType(inst_ty); const target = self.dg.module.getTarget(); const payload_offset = errUnionPayloadOffset(payload_ty, target); @@ -5781,7 +5781,7 @@ pub const FuncGen = struct { if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { return operand; } - const err_un_llvm_ty = try self.dg.llvmType(err_un_ty); + const err_un_llvm_ty = try self.dg.lowerType(err_un_ty); const target = self.dg.module.getTarget(); const payload_offset = errUnionPayloadOffset(payload_ty, target); @@ -5866,7 +5866,7 @@ pub const FuncGen = struct { const ptr = try self.resolveInst(bin_op.lhs); const len = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); - const llvm_slice_ty = try self.dg.llvmType(inst_ty); + const llvm_slice_ty = try self.dg.lowerType(inst_ty); // In case of slicing a global, the result type looks something like `{ i8*, i64 }` // but `ptr` is pointing to the global directly. If it's an array, we would want to @@ -5874,7 +5874,7 @@ pub const FuncGen = struct { // This prevents an assertion failure. var buf: Type.SlicePtrFieldTypeBuffer = undefined; const ptr_ty = inst_ty.slicePtrFieldType(&buf); - const ptr_llvm_ty = try self.dg.llvmType(ptr_ty); + const ptr_llvm_ty = try self.dg.lowerType(ptr_ty); const casted_ptr = self.builder.buildBitCast(ptr, ptr_llvm_ty, ""); const partial = self.builder.buildInsertValue(llvm_slice_ty.getUndef(), casted_ptr, 0, ""); return self.builder.buildInsertValue(partial, len, 1, ""); @@ -6040,7 +6040,7 @@ pub const FuncGen = struct { // const d = @divTrunc(a, b); // const r = @rem(a, b); // return if (r == 0) d else d - ((a < 0) ^ (b < 0)); - const result_llvm_ty = try self.dg.llvmType(inst_ty); + const result_llvm_ty = try self.dg.lowerType(inst_ty); const zero = result_llvm_ty.constNull(); const div_trunc = self.builder.buildSDiv(lhs, rhs, ""); const rem = self.builder.buildSRem(lhs, rhs, ""); @@ -6090,7 +6090,7 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); - const inst_llvm_ty = try self.dg.llvmType(inst_ty); + const inst_llvm_ty = try self.dg.lowerType(inst_ty); const scalar_ty = inst_ty.scalarType(); if (scalar_ty.isRuntimeFloat()) { @@ -6174,8 +6174,8 @@ pub const FuncGen = struct { const intrinsic_name = if (scalar_ty.isSignedInt()) signed_intrinsic else unsigned_intrinsic; - const llvm_lhs_ty = try self.dg.llvmType(lhs_ty); - const llvm_dest_ty = try self.dg.llvmType(dest_ty); + const llvm_lhs_ty = try self.dg.lowerType(lhs_ty); + const llvm_dest_ty = try self.dg.lowerType(dest_ty); const tg = self.dg.module.getTarget(); @@ -6283,7 +6283,7 @@ pub const FuncGen = struct { ) !*const llvm.Value { const target = self.dg.module.getTarget(); const scalar_ty = ty.scalarType(); - const scalar_llvm_ty = try self.dg.llvmType(scalar_ty); + const scalar_llvm_ty = try self.dg.lowerType(scalar_ty); if (intrinsicsAllowed(scalar_ty, target)) { const llvm_predicate: llvm.RealPredicate = switch (pred) { @@ -6383,8 +6383,8 @@ pub const FuncGen = struct { ) !*const llvm.Value { const target = self.dg.module.getTarget(); const scalar_ty = ty.scalarType(); - const llvm_ty = try self.dg.llvmType(ty); - const scalar_llvm_ty = try self.dg.llvmType(scalar_ty); + const llvm_ty = try self.dg.lowerType(ty); + const scalar_llvm_ty = try self.dg.lowerType(scalar_ty); const intrinsics_allowed = op != .tan and intrinsicsAllowed(scalar_ty, target); var fn_name_buf: [64]u8 = undefined; @@ -6478,12 +6478,12 @@ pub const FuncGen = struct { const rhs_scalar_ty = rhs_ty.scalarType(); const dest_ty = self.air.typeOfIndex(inst); - const llvm_dest_ty = try self.dg.llvmType(dest_ty); + const llvm_dest_ty = try self.dg.lowerType(dest_ty); const tg = self.dg.module.getTarget(); const casted_rhs = if (rhs_scalar_ty.bitSize(tg) < lhs_scalar_ty.bitSize(tg)) - self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_ty), "") + self.builder.buildZExt(rhs, try self.dg.lowerType(lhs_ty), "") else rhs; @@ -6543,7 +6543,7 @@ pub const FuncGen = struct { const tg = self.dg.module.getTarget(); const casted_rhs = if (rhs_scalar_ty.bitSize(tg) < lhs_scalar_ty.bitSize(tg)) - self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_ty), "") + self.builder.buildZExt(rhs, try self.dg.lowerType(lhs_ty), "") else rhs; if (lhs_scalar_ty.isSignedInt()) return self.builder.buildNSWShl(lhs, casted_rhs, ""); @@ -6566,7 +6566,7 @@ pub const FuncGen = struct { const tg = self.dg.module.getTarget(); const casted_rhs = if (rhs_scalar_ty.bitSize(tg) < lhs_scalar_ty.bitSize(tg)) - self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "") + self.builder.buildZExt(rhs, try self.dg.lowerType(lhs_type), "") else rhs; return self.builder.buildShl(lhs, casted_rhs, ""); @@ -6588,7 +6588,7 @@ pub const FuncGen = struct { const tg = self.dg.module.getTarget(); const casted_rhs = if (rhs_scalar_ty.bitSize(tg) < lhs_scalar_ty.bitSize(tg)) - self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_ty), "") + self.builder.buildZExt(rhs, try self.dg.lowerType(lhs_ty), "") else rhs; if (lhs_scalar_ty.isSignedInt()) return self.builder.buildSShlSat(lhs, casted_rhs, ""); @@ -6611,7 +6611,7 @@ pub const FuncGen = struct { const tg = self.dg.module.getTarget(); const casted_rhs = if (rhs_scalar_ty.bitSize(tg) < lhs_scalar_ty.bitSize(tg)) - self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_ty), "") + self.builder.buildZExt(rhs, try self.dg.lowerType(lhs_ty), "") else rhs; const is_signed_int = lhs_scalar_ty.isSignedInt(); @@ -6639,7 +6639,7 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const dest_ty = self.air.typeOfIndex(inst); const dest_info = dest_ty.intInfo(target); - const dest_llvm_ty = try self.dg.llvmType(dest_ty); + const dest_llvm_ty = try self.dg.lowerType(dest_ty); const operand = try self.resolveInst(ty_op.operand); const operand_ty = self.air.typeOf(ty_op.operand); const operand_info = operand_ty.intInfo(target); @@ -6661,7 +6661,7 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); - const dest_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst)); + const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst)); return self.builder.buildTrunc(operand, dest_llvm_ty, ""); } @@ -6679,7 +6679,7 @@ pub const FuncGen = struct { if (!backendSupportsF80(target) and (src_bits == 80 or dest_bits == 80)) { return softF80TruncOrExt(self, operand, src_bits, dest_bits); } - const dest_llvm_ty = try self.dg.llvmType(dest_ty); + const dest_llvm_ty = try self.dg.lowerType(dest_ty); return self.builder.buildFPTrunc(operand, dest_llvm_ty, ""); } @@ -6697,7 +6697,7 @@ pub const FuncGen = struct { if (!backendSupportsF80(target) and (src_bits == 80 or dest_bits == 80)) { return softF80TruncOrExt(self, operand, src_bits, dest_bits); } - const dest_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst)); + const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst)); return self.builder.buildFPExt(operand, dest_llvm_ty, ""); } @@ -6707,7 +6707,7 @@ pub const FuncGen = struct { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); - const dest_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst)); + const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst)); return self.builder.buildPtrToInt(operand, dest_llvm_ty, ""); } @@ -6720,7 +6720,7 @@ pub const FuncGen = struct { const inst_ty = self.air.typeOfIndex(inst); const operand_is_ref = isByRef(operand_ty); const result_is_ref = isByRef(inst_ty); - const llvm_dest_ty = try self.dg.llvmType(inst_ty); + const llvm_dest_ty = try self.dg.lowerType(inst_ty); const target = self.dg.module.getTarget(); if (operand_is_ref and result_is_ref) { @@ -6740,14 +6740,14 @@ pub const FuncGen = struct { const array_ptr = self.buildAlloca(llvm_dest_ty); const bitcast_ok = elem_ty.bitSize(target) == elem_ty.abiSize(target) * 8; if (bitcast_ok) { - const llvm_vector_ty = try self.dg.llvmType(operand_ty); + const llvm_vector_ty = try self.dg.lowerType(operand_ty); const casted_ptr = self.builder.buildBitCast(array_ptr, llvm_vector_ty.pointerType(0), ""); const llvm_store = self.builder.buildStore(operand, casted_ptr); llvm_store.setAlignment(inst_ty.abiAlignment(target)); } else { // If the ABI size of the element type is not evenly divisible by size in bits; // a simple bitcast will not work, and we fall back to extractelement. - const llvm_usize = try self.dg.llvmType(Type.usize); + const llvm_usize = try self.dg.lowerType(Type.usize); const llvm_u32 = self.context.intType(32); const zero = llvm_usize.constNull(); const vector_len = operand_ty.arrayLen(); @@ -6764,7 +6764,7 @@ pub const FuncGen = struct { return array_ptr; } else if (operand_ty.zigTypeTag() == .Array and inst_ty.zigTypeTag() == .Vector) { const elem_ty = operand_ty.childType(); - const llvm_vector_ty = try self.dg.llvmType(inst_ty); + const llvm_vector_ty = try self.dg.lowerType(inst_ty); if (!operand_is_ref) { return self.dg.todo("implement bitcast non-ref array to vector", .{}); } @@ -6781,7 +6781,7 @@ pub const FuncGen = struct { } else { // If the ABI size of the element type is not evenly divisible by size in bits; // a simple bitcast will not work, and we fall back to extractelement. - const llvm_usize = try self.dg.llvmType(Type.usize); + const llvm_usize = try self.dg.lowerType(Type.usize); const llvm_u32 = self.context.intType(32); const zero = llvm_usize.constNull(); const vector_len = operand_ty.arrayLen(); @@ -6813,7 +6813,7 @@ pub const FuncGen = struct { const alignment = @maximum(operand_ty.abiAlignment(target), inst_ty.abiAlignment(target)); const result_ptr = self.buildAlloca(llvm_dest_ty); result_ptr.setAlignment(alignment); - const operand_llvm_ty = try self.dg.llvmType(operand_ty); + const operand_llvm_ty = try self.dg.lowerType(operand_ty); const casted_ptr = self.builder.buildBitCast(result_ptr, operand_llvm_ty.pointerType(0), ""); const store_inst = self.builder.buildStore(operand, casted_ptr); store_inst.setAlignment(alignment); @@ -6827,7 +6827,7 @@ pub const FuncGen = struct { const alignment = @maximum(operand_ty.abiAlignment(target), inst_ty.abiAlignment(target)); const result_ptr = self.buildAlloca(llvm_dest_ty); result_ptr.setAlignment(alignment); - const operand_llvm_ty = try self.dg.llvmType(operand_ty); + const operand_llvm_ty = try self.dg.lowerType(operand_ty); const casted_ptr = self.builder.buildBitCast(result_ptr, operand_llvm_ty.pointerType(0), ""); const store_inst = self.builder.buildStore(operand, casted_ptr); store_inst.setAlignment(alignment); @@ -6901,7 +6901,7 @@ pub const FuncGen = struct { const pointee_type = ptr_ty.childType(); if (!pointee_type.isFnOrHasRuntimeBitsIgnoreComptime()) return self.dg.lowerPtrToVoid(ptr_ty); - const pointee_llvm_ty = try self.dg.llvmType(pointee_type); + const pointee_llvm_ty = try self.dg.lowerType(pointee_type); const alloca_inst = self.buildAlloca(pointee_llvm_ty); const target = self.dg.module.getTarget(); const alignment = ptr_ty.ptrAlignment(target); @@ -6915,7 +6915,7 @@ pub const FuncGen = struct { const ret_ty = ptr_ty.childType(); if (!ret_ty.isFnOrHasRuntimeBitsIgnoreComptime()) return self.dg.lowerPtrToVoid(ptr_ty); if (self.ret_ptr) |ret_ptr| return ret_ptr; - const ret_llvm_ty = try self.dg.llvmType(ret_ty); + const ret_llvm_ty = try self.dg.lowerType(ret_ty); const target = self.dg.module.getTarget(); const alloca_inst = self.buildAlloca(ret_llvm_ty); alloca_inst.setAlignment(ptr_ty.ptrAlignment(target)); @@ -6946,7 +6946,7 @@ pub const FuncGen = struct { const dest_ptr_u8 = self.builder.buildBitCast(dest_ptr, ptr_u8_llvm_ty, ""); const fill_char = u8_llvm_ty.constInt(0xaa, .False); const dest_ptr_align = ptr_ty.ptrAlignment(target); - const usize_llvm_ty = try self.dg.llvmType(Type.usize); + const usize_llvm_ty = try self.dg.lowerType(Type.usize); const len = usize_llvm_ty.constInt(operand_size, .False); _ = self.builder.buildMemSet(dest_ptr_u8, fill_char, len, dest_ptr_align, ptr_ty.isVolatilePtr()); if (self.dg.module.comp.bin_file.options.valgrind) { @@ -6983,7 +6983,7 @@ pub const FuncGen = struct { const llvm_fn = self.getIntrinsic("llvm.returnaddress", &.{}); const params = [_]*const llvm.Value{llvm_i32.constNull()}; const ptr_val = self.builder.buildCall(llvm_fn, ¶ms, params.len, .Fast, .Auto, ""); - const llvm_usize = try self.dg.llvmType(Type.usize); + const llvm_usize = try self.dg.lowerType(Type.usize); return self.builder.buildPtrToInt(ptr_val, llvm_usize, ""); } @@ -7001,7 +7001,7 @@ pub const FuncGen = struct { const params = [_]*const llvm.Value{llvm_i32.constNull()}; const ptr_val = self.builder.buildCall(llvm_fn, ¶ms, params.len, .Fast, .Auto, ""); - const llvm_usize = try self.dg.llvmType(Type.usize); + const llvm_usize = try self.dg.lowerType(Type.usize); return self.builder.buildPtrToInt(ptr_val, llvm_usize, ""); } @@ -7046,7 +7046,7 @@ pub const FuncGen = struct { var payload = self.builder.buildExtractValue(result, 0, ""); if (opt_abi_ty != null) { - payload = self.builder.buildTrunc(payload, try self.dg.llvmType(operand_ty), ""); + payload = self.builder.buildTrunc(payload, try self.dg.lowerType(operand_ty), ""); } const success_bit = self.builder.buildExtractValue(result, 1, ""); @@ -7054,7 +7054,7 @@ pub const FuncGen = struct { return self.builder.buildSelect(success_bit, payload.typeOf().constNull(), payload, ""); } - const optional_llvm_ty = try self.dg.llvmType(optional_ty); + const optional_llvm_ty = try self.dg.lowerType(optional_ty); const non_null_bit = self.builder.buildNot(success_bit, ""); const partial = self.builder.buildInsertValue(optional_llvm_ty.getUndef(), payload, 0, ""); return self.builder.buildInsertValue(partial, non_null_bit, 1, ""); @@ -7090,7 +7090,7 @@ pub const FuncGen = struct { ordering, single_threaded, ); - const operand_llvm_ty = try self.dg.llvmType(operand_ty); + const operand_llvm_ty = try self.dg.lowerType(operand_ty); if (is_float) { return self.builder.buildBitCast(uncasted_result, operand_llvm_ty, ""); } else { @@ -7103,7 +7103,7 @@ pub const FuncGen = struct { } // It's a pointer but we need to treat it as an int. - const usize_llvm_ty = try self.dg.llvmType(Type.usize); + const usize_llvm_ty = try self.dg.lowerType(Type.usize); const casted_ptr = self.builder.buildBitCast(ptr, usize_llvm_ty.pointerType(0), ""); const casted_operand = self.builder.buildPtrToInt(operand, usize_llvm_ty, ""); const uncasted_result = self.builder.buildAtomicRmw( @@ -7113,7 +7113,7 @@ pub const FuncGen = struct { ordering, single_threaded, ); - const operand_llvm_ty = try self.dg.llvmType(operand_ty); + const operand_llvm_ty = try self.dg.lowerType(operand_ty); return self.builder.buildIntToPtr(uncasted_result, operand_llvm_ty, ""); } @@ -7132,7 +7132,7 @@ pub const FuncGen = struct { const casted_ptr = self.builder.buildBitCast(ptr, abi_ty.pointerType(0), ""); const load_inst = (try self.load(casted_ptr, ptr_ty)).?; load_inst.setOrdering(ordering); - return self.builder.buildTrunc(load_inst, try self.dg.llvmType(operand_ty), ""); + return self.builder.buildTrunc(load_inst, try self.dg.lowerType(operand_ty), ""); } const load_inst = (try self.load(ptr, ptr_ty)).?; load_inst.setOrdering(ordering); @@ -7273,13 +7273,13 @@ pub const FuncGen = struct { const operand = try self.resolveInst(ty_op.operand); const llvm_i1 = self.context.intType(1); - const operand_llvm_ty = try self.dg.llvmType(operand_ty); + const operand_llvm_ty = try self.dg.lowerType(operand_ty); const fn_val = self.getIntrinsic(llvm_fn_name, &.{operand_llvm_ty}); const params = [_]*const llvm.Value{ operand, llvm_i1.constNull() }; const wrong_size_result = self.builder.buildCall(fn_val, ¶ms, params.len, .C, .Auto, ""); const result_ty = self.air.typeOfIndex(inst); - const result_llvm_ty = try self.dg.llvmType(result_ty); + const result_llvm_ty = try self.dg.lowerType(result_ty); const target = self.dg.module.getTarget(); const bits = operand_ty.intInfo(target).bits; @@ -7301,12 +7301,12 @@ pub const FuncGen = struct { const operand = try self.resolveInst(ty_op.operand); const params = [_]*const llvm.Value{operand}; - const operand_llvm_ty = try self.dg.llvmType(operand_ty); + const operand_llvm_ty = try self.dg.lowerType(operand_ty); const fn_val = self.getIntrinsic(llvm_fn_name, &.{operand_llvm_ty}); const wrong_size_result = self.builder.buildCall(fn_val, ¶ms, params.len, .C, .Auto, ""); const result_ty = self.air.typeOfIndex(inst); - const result_llvm_ty = try self.dg.llvmType(result_ty); + const result_llvm_ty = try self.dg.lowerType(result_ty); const target = self.dg.module.getTarget(); const bits = operand_ty.intInfo(target).bits; @@ -7330,7 +7330,7 @@ pub const FuncGen = struct { assert(bits % 8 == 0); var operand = try self.resolveInst(ty_op.operand); - var operand_llvm_ty = try self.dg.llvmType(operand_ty); + var operand_llvm_ty = try self.dg.lowerType(operand_ty); if (bits % 16 == 8) { // If not an even byte-multiple, we need zero-extend + shift-left 1 byte @@ -7364,7 +7364,7 @@ pub const FuncGen = struct { const wrong_size_result = self.builder.buildCall(fn_val, ¶ms, params.len, .C, .Auto, ""); const result_ty = self.air.typeOfIndex(inst); - const result_llvm_ty = try self.dg.llvmType(result_ty); + const result_llvm_ty = try self.dg.lowerType(result_ty); const result_bits = result_ty.intInfo(target).bits; if (bits > result_bits) { return self.builder.buildTrunc(wrong_size_result, result_llvm_ty, ""); @@ -7407,14 +7407,14 @@ pub const FuncGen = struct { } const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); - const llvm_ret_ty = try self.dg.llvmType(slice_ty); - const usize_llvm_ty = try self.dg.llvmType(Type.usize); + const llvm_ret_ty = try self.dg.lowerType(slice_ty); + const usize_llvm_ty = try self.dg.lowerType(Type.usize); const target = self.dg.module.getTarget(); const slice_alignment = slice_ty.abiAlignment(target); var int_tag_type_buffer: Type.Payload.Bits = undefined; const int_tag_ty = enum_ty.intTagType(&int_tag_type_buffer); - const param_types = [_]*const llvm.Type{try self.dg.llvmType(int_tag_ty)}; + const param_types = [_]*const llvm.Type{try self.dg.lowerType(int_tag_ty)}; const fn_type = llvm.functionType(llvm_ret_ty, ¶m_types, param_types.len, .False); const fn_val = self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type); @@ -7471,7 +7471,7 @@ pub const FuncGen = struct { .base = .{ .tag = .enum_field_index }, .data = @intCast(u32, field_index), }; - break :int try self.dg.genTypedValue(.{ + break :int try self.dg.lowerValue(.{ .ty = enum_ty, .val = Value.initPayload(&tag_val_payload.base), }); @@ -7496,8 +7496,8 @@ pub const FuncGen = struct { // Function signature: fn (anyerror) bool - const ret_llvm_ty = try self.dg.llvmType(Type.bool); - const anyerror_llvm_ty = try self.dg.llvmType(Type.anyerror); + const ret_llvm_ty = try self.dg.lowerType(Type.bool); + const anyerror_llvm_ty = try self.dg.lowerType(Type.anyerror); const param_types = [_]*const llvm.Type{anyerror_llvm_ty}; const fn_type = llvm.functionType(ret_llvm_ty, ¶m_types, param_types.len, .False); @@ -7606,7 +7606,7 @@ pub const FuncGen = struct { .Add => switch (scalar_ty.zigTypeTag()) { .Int => return self.builder.buildAddReduce(operand), .Float => { - const scalar_llvm_ty = try self.dg.llvmType(scalar_ty); + const scalar_llvm_ty = try self.dg.lowerType(scalar_ty); const neutral_value = scalar_llvm_ty.constReal(-0.0); return self.builder.buildFPAddReduce(neutral_value, operand); }, @@ -7615,7 +7615,7 @@ pub const FuncGen = struct { .Mul => switch (scalar_ty.zigTypeTag()) { .Int => return self.builder.buildMulReduce(operand), .Float => { - const scalar_llvm_ty = try self.dg.llvmType(scalar_ty); + const scalar_llvm_ty = try self.dg.lowerType(scalar_ty); const neutral_value = scalar_llvm_ty.constReal(1.0); return self.builder.buildFPMulReduce(neutral_value, operand); }, @@ -7631,7 +7631,7 @@ pub const FuncGen = struct { const result_ty = self.air.typeOfIndex(inst); const len = @intCast(usize, result_ty.arrayLen()); const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); - const llvm_result_ty = try self.dg.llvmType(result_ty); + const llvm_result_ty = try self.dg.lowerType(result_ty); const target = self.dg.module.getTarget(); switch (result_ty.zigTypeTag()) { @@ -7719,7 +7719,7 @@ pub const FuncGen = struct { .Array => { assert(isByRef(result_ty)); - const llvm_usize = try self.dg.llvmType(Type.usize); + const llvm_usize = try self.dg.lowerType(Type.usize); const alloca_inst = self.buildAlloca(llvm_result_ty); alloca_inst.setAlignment(result_ty.abiAlignment(target)); @@ -7754,7 +7754,7 @@ pub const FuncGen = struct { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; const union_ty = self.air.typeOfIndex(inst); - const union_llvm_ty = try self.dg.llvmType(union_ty); + const union_llvm_ty = try self.dg.lowerType(union_ty); const target = self.dg.module.getTarget(); const layout = union_ty.unionGetLayout(target); if (layout.payload_size == 0) { @@ -7774,8 +7774,8 @@ pub const FuncGen = struct { const union_obj = union_ty.cast(Type.Payload.Union).?.data; assert(union_obj.haveFieldTypes()); const field = union_obj.fields.values()[extra.field_index]; - const field_llvm_ty = try self.dg.llvmType(field.ty); - const tag_llvm_ty = try self.dg.llvmType(union_obj.tag_ty); + const field_llvm_ty = try self.dg.lowerType(field.ty); + const tag_llvm_ty = try self.dg.lowerType(union_obj.tag_ty); const field_size = field.ty.abiSize(target); const field_align = field.normalAlignment(target); @@ -8011,7 +8011,7 @@ pub const FuncGen = struct { const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); const slice_alignment = slice_ty.abiAlignment(self.dg.module.getTarget()); - const llvm_slice_ty = try self.dg.llvmType(slice_ty); + const llvm_slice_ty = try self.dg.lowerType(slice_ty); const llvm_slice_ptr_ty = llvm_slice_ty.pointerType(0); // TODO: Address space const error_name_table_global = self.dg.object.llvm_module.addGlobal(llvm_slice_ptr_ty, "__zig_err_name_table"); @@ -8075,7 +8075,7 @@ pub const FuncGen = struct { // out the relevant bits when accessing the pointee. // Here we perform a bitcast because we want to use the host_size // as the llvm pointer element type. - const result_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst)); + const result_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst)); // TODO this can be removed if we change host_size to be bits instead // of bytes. return self.builder.buildBitCast(struct_ptr, result_llvm_ty, ""); @@ -8090,7 +8090,7 @@ pub const FuncGen = struct { // end of the struct. Treat our struct pointer as an array of two and get // the index to the element at index `1` to get a pointer to the end of // the struct. - const llvm_usize = try self.dg.llvmType(Type.usize); + const llvm_usize = try self.dg.lowerType(Type.usize); const llvm_index = llvm_usize.constInt(1, .False); const indices: [1]*const llvm.Value = .{llvm_index}; return self.builder.buildInBoundsGEP(struct_ptr, &indices, indices.len, ""); @@ -8111,7 +8111,7 @@ pub const FuncGen = struct { ) !?*const llvm.Value { const union_obj = union_ty.cast(Type.Payload.Union).?.data; const field = &union_obj.fields.values()[field_index]; - const result_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst)); + const result_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst)); if (!field.ty.hasRuntimeBitsIgnoreComptime()) { return null; } @@ -8150,7 +8150,7 @@ pub const FuncGen = struct { const ptr_volatile = llvm.Bool.fromBool(ptr_ty.isVolatilePtr()); if (info.host_size == 0) { if (isByRef(info.pointee_type)) { - const elem_llvm_ty = try self.dg.llvmType(info.pointee_type); + const elem_llvm_ty = try self.dg.lowerType(info.pointee_type); const result_align = info.pointee_type.abiAlignment(target); const max_align = @maximum(result_align, ptr_alignment); const result_ptr = self.buildAlloca(elem_llvm_ty); @@ -8183,7 +8183,7 @@ pub const FuncGen = struct { const elem_bits = @intCast(c_uint, ptr_ty.elemType().bitSize(target)); const shift_amt = containing_int.typeOf().constInt(info.bit_offset, .False); const shifted_value = self.builder.buildLShr(containing_int, shift_amt, ""); - const elem_llvm_ty = try self.dg.llvmType(info.pointee_type); + const elem_llvm_ty = try self.dg.lowerType(info.pointee_type); if (isByRef(info.pointee_type)) { const result_align = info.pointee_type.abiAlignment(target); @@ -8625,7 +8625,7 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. // anyerror return type instead, so that it can be coerced into a function // pointer type which has anyerror as the return type. if (fn_info.return_type.isError()) { - return dg.llvmType(Type.anyerror); + return dg.lowerType(Type.anyerror); } else { return dg.context.voidType(); } @@ -8636,7 +8636,7 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. if (isByRef(fn_info.return_type)) { return dg.context.voidType(); } else { - return dg.llvmType(fn_info.return_type); + return dg.lowerType(fn_info.return_type); } }, .C => { @@ -8657,24 +8657,24 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. else => false, }; switch (target.cpu.arch) { - .mips, .mipsel => return dg.llvmType(fn_info.return_type), + .mips, .mipsel => return dg.lowerType(fn_info.return_type), .x86_64 => switch (target.os.tag) { .windows => switch (x86_64_abi.classifyWindows(fn_info.return_type, target)) { .integer => { if (is_scalar) { - return dg.llvmType(fn_info.return_type); + return dg.lowerType(fn_info.return_type); } else { const abi_size = fn_info.return_type.abiSize(target); return dg.context.intType(@intCast(c_uint, abi_size * 8)); } }, .memory => return dg.context.voidType(), - .sse => return dg.llvmType(fn_info.return_type), + .sse => return dg.lowerType(fn_info.return_type), else => unreachable, }, else => { if (is_scalar) { - return dg.llvmType(fn_info.return_type); + return dg.lowerType(fn_info.return_type); } const classes = x86_64_abi.classifySystemV(fn_info.return_type, target); if (classes[0] == .memory) { @@ -8715,10 +8715,10 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. }, }, // TODO investigate C ABI for other architectures - else => return dg.llvmType(fn_info.return_type), + else => return dg.lowerType(fn_info.return_type), } }, - else => return dg.llvmType(fn_info.return_type), + else => return dg.lowerType(fn_info.return_type), } } From b42100c70fc306c6d6f69a55e9225a9a91e363ef Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 23 May 2022 22:10:50 +0200 Subject: [PATCH 1618/2031] dwarf: update abbrev info generation for new error union layout --- src/arch/x86_64/CodeGen.zig | 1 - src/link/Dwarf.zig | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index eeb4cab04f..68c8d3449b 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -4377,7 +4377,6 @@ fn genVarDbgInfo( fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { switch (self.debug_output) { .dwarf => |dw| { - assert(ty.hasRuntimeBits()); const dbg_info = &dw.dbg_info; const index = dbg_info.items.len; try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index a204dd91ae..61bec1f880 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -498,9 +498,11 @@ pub const DeclState = struct { .ErrorUnion => { const error_ty = ty.errorUnionSet(); const payload_ty = ty.errorUnionPayload(); + const payload_align = payload_ty.abiAlignment(target); + const error_align = Type.anyerror.abiAlignment(target); const abi_size = ty.abiSize(target); - const abi_align = ty.abiAlignment(target); - const payload_off = mem.alignForwardGeneric(u64, error_ty.abiSize(target), abi_align); + const payload_off = if (error_align >= payload_align) Type.anyerror.abiSize(target) else 0; + const error_off = if (error_align >= payload_align) 0 else payload_ty.abiSize(target); // DW.AT.structure_type try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_type)); @@ -534,7 +536,7 @@ pub const DeclState = struct { try dbg_info_buffer.resize(index + 4); try self.addTypeReloc(atom, error_ty, @intCast(u32, index), null); // DW.AT.data_member_location, DW.FORM.sdata - try dbg_info_buffer.append(0); + try leb128.writeULEB128(dbg_info_buffer.writer(), error_off); // DW.AT.structure_type delimit children try dbg_info_buffer.append(0); @@ -2293,7 +2295,7 @@ fn addDbgInfoErrorSet( // DW.AT.enumeration_type try dbg_info_buffer.append(@enumToInt(AbbrevKind.enum_type)); // DW.AT.byte_size, DW.FORM.sdata - const abi_size = ty.abiSize(target); + const abi_size = Type.anyerror.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); // DW.AT.name, DW.FORM.string const name = try ty.nameAllocArena(arena, module); From 41f517e5f506500c4e3f0bea53d73db0a1daf456 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 23 May 2022 23:07:12 +0200 Subject: [PATCH 1619/2031] x64: update for new error union layout --- src/arch/x86_64/CodeGen.zig | 248 +++++++++++++++++++++++++----------- src/codegen.zig | 5 +- 2 files changed, 175 insertions(+), 78 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 68c8d3449b..dc2f55f6ef 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -854,7 +854,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { const ptr_ty = self.air.typeOfIndex(inst); const elem_ty = ptr_ty.elemType(); - if (!elem_ty.hasRuntimeBits()) { + if (!elem_ty.hasRuntimeBitsIgnoreComptime()) { return self.allocMem(inst, @sizeOf(usize), @alignOf(usize)); } @@ -1786,21 +1786,34 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { const err_ty = err_union_ty.errorUnionSet(); const payload_ty = err_union_ty.errorUnionPayload(); const operand = try self.resolveInst(ty_op.operand); - const operand_lock: ?RegisterLock = switch (operand) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); const result: MCValue = result: { - if (!payload_ty.hasRuntimeBits()) break :result operand; + if (err_ty.errorSetCardinality() == .zero) { + break :result MCValue{ .immediate = 0 }; + } + + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + break :result operand; + } + + const err_off = errUnionErrOffset(err_union_ty, self.target.*); switch (operand) { .stack_offset => |off| { - break :result MCValue{ .stack_offset = off }; + const offset = off - @intCast(i32, err_off); + break :result MCValue{ .stack_offset = offset }; }, - .register => { + .register => |reg| { // TODO reuse operand - break :result try self.copyToRegisterWithInstTracking(inst, err_ty, operand); + const lock = self.register_manager.lockRegAssumeUnused(reg); + defer self.register_manager.unlockReg(lock); + const result = try self.copyToRegisterWithInstTracking(inst, err_union_ty, operand); + if (err_off > 0) { + const shift = @intCast(u6, err_off * 8); + try self.genShiftBinOpMir(.shr, err_union_ty, result.register, .{ .immediate = shift }); + } else { + try self.truncateRegister(Type.anyerror, result.register); + } + break :result result; }, else => return self.fail("TODO implement unwrap_err_err for {}", .{operand}), } @@ -1815,32 +1828,37 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { } const err_union_ty = self.air.typeOf(ty_op.operand); const payload_ty = err_union_ty.errorUnionPayload(); + const err_ty = err_union_ty.errorUnionSet(); + const operand = try self.resolveInst(ty_op.operand); + const result: MCValue = result: { - if (!payload_ty.hasRuntimeBits()) break :result MCValue.none; + if (err_ty.errorSetCardinality() == .zero) { + // TODO check if we can reuse + break :result operand; + } - const operand = try self.resolveInst(ty_op.operand); - const operand_lock: ?RegisterLock = switch (operand) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + break :result MCValue.none; + } - const abi_align = err_union_ty.abiAlignment(self.target.*); - const err_ty = err_union_ty.errorUnionSet(); - const err_abi_size = mem.alignForwardGeneric(u32, @intCast(u32, err_ty.abiSize(self.target.*)), abi_align); + const payload_off = errUnionPayloadOffset(err_union_ty, self.target.*); switch (operand) { .stack_offset => |off| { - const offset = off - @intCast(i32, err_abi_size); + const offset = off - @intCast(i32, payload_off); break :result MCValue{ .stack_offset = offset }; }, - .register => { + .register => |reg| { // TODO reuse operand - const shift = @intCast(u6, err_abi_size * @sizeOf(usize)); + const lock = self.register_manager.lockRegAssumeUnused(reg); + defer self.register_manager.unlockReg(lock); const result = try self.copyToRegisterWithInstTracking(inst, err_union_ty, operand); - try self.genShiftBinOpMir(.shr, Type.usize, result.register, .{ .immediate = shift }); - break :result MCValue{ - .register = registerAlias(result.register, @intCast(u32, payload_ty.abiSize(self.target.*))), - }; + if (payload_off > 0) { + const shift = @intCast(u6, payload_off * 8); + try self.genShiftBinOpMir(.shr, err_union_ty, result.register, .{ .immediate = shift }); + } else { + try self.truncateRegister(payload_ty, result.register); + } + break :result result; }, else => return self.fail("TODO implement unwrap_err_payload for {}", .{operand}), } @@ -1935,24 +1953,37 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { /// T to E!T fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; + if (self.liveness.isUnused(inst)) { return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); } + const error_union_ty = self.air.getRefType(ty_op.ty); const error_ty = error_union_ty.errorUnionSet(); const payload_ty = error_union_ty.errorUnionPayload(); const operand = try self.resolveInst(ty_op.operand); - assert(payload_ty.hasRuntimeBits()); - const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*)); - const abi_align = error_union_ty.abiAlignment(self.target.*); - const err_abi_size = @intCast(u32, error_ty.abiSize(self.target.*)); - const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align)); - const offset = mem.alignForwardGeneric(u32, err_abi_size, abi_align); - try self.genSetStack(error_ty, stack_offset, .{ .immediate = 0 }, .{}); - try self.genSetStack(payload_ty, stack_offset - @intCast(i32, offset), operand, .{}); + const result: MCValue = result: { + if (error_ty.errorSetCardinality() == .zero) { + break :result operand; + } - return self.finishAir(inst, .{ .stack_offset = stack_offset }, .{ ty_op.operand, .none, .none }); + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + break :result operand; + } + + const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*)); + const abi_align = error_union_ty.abiAlignment(self.target.*); + const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align)); + const payload_off = errUnionPayloadOffset(error_union_ty, self.target.*); + const err_off = errUnionErrOffset(error_union_ty, self.target.*); + try self.genSetStack(payload_ty, stack_offset - @intCast(i32, payload_off), operand, .{}); + try self.genSetStack(Type.anyerror, stack_offset - @intCast(i32, err_off), .{ .immediate = 0 }, .{}); + + break :result MCValue{ .stack_offset = stack_offset }; + }; + + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } /// E to E!T @@ -1962,19 +1993,22 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); } const error_union_ty = self.air.getRefType(ty_op.ty); - const error_ty = error_union_ty.errorUnionSet(); const payload_ty = error_union_ty.errorUnionPayload(); - const err = try self.resolveInst(ty_op.operand); + const operand = try self.resolveInst(ty_op.operand); + const result: MCValue = result: { - if (!payload_ty.hasRuntimeBits()) break :result err; + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + break :result operand; + } const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*)); const abi_align = error_union_ty.abiAlignment(self.target.*); - const err_abi_size = @intCast(u32, error_ty.abiSize(self.target.*)); const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align)); - const offset = mem.alignForwardGeneric(u32, err_abi_size, abi_align); - try self.genSetStack(error_ty, stack_offset, err, .{}); - try self.genSetStack(payload_ty, stack_offset - @intCast(i32, offset), .undef, .{}); + const payload_off = errUnionPayloadOffset(error_union_ty, self.target.*); + const err_off = errUnionErrOffset(error_union_ty, self.target.*); + try self.genSetStack(Type.anyerror, stack_offset - @intCast(i32, err_off), operand, .{}); + try self.genSetStack(payload_ty, stack_offset - @intCast(i32, payload_off), .undef, .{}); + break :result MCValue{ .stack_offset = stack_offset }; }; @@ -2535,7 +2569,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const elem_ty = self.air.typeOfIndex(inst); const result: MCValue = result: { - if (!elem_ty.hasRuntimeBits()) + if (!elem_ty.hasRuntimeBitsIgnoreComptime()) break :result MCValue.none; const ptr = try self.resolveInst(ty_op.operand); @@ -4102,6 +4136,9 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { const operand = try self.resolveInst(un_op); const ret_ty = self.fn_type.fnReturnType(); switch (self.ret_mcv) { + .immediate => { + assert(ret_ty.isError()); + }, .stack_offset => { const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv); const reg_lock = self.register_manager.lockRegAssumeUnused(reg); @@ -4134,6 +4171,9 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { const ptr_ty = self.air.typeOf(un_op); const elem_ty = ptr_ty.elemType(); switch (self.ret_mcv) { + .immediate => { + assert(elem_ty.isError()); + }, .stack_offset => { const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv); const reg_lock = self.register_manager.lockRegAssumeUnused(reg); @@ -4603,7 +4643,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValu const cmp_ty: Type = if (!ty.isPtrLikeOptional()) blk: { var buf: Type.Payload.ElemType = undefined; const payload_ty = ty.optionalChild(&buf); - break :blk if (payload_ty.hasRuntimeBits()) Type.bool else ty; + break :blk if (payload_ty.hasRuntimeBitsIgnoreComptime()) Type.bool else ty; } else ty; try self.genBinOpMir(.cmp, cmp_ty, operand, MCValue{ .immediate = 0 }); @@ -4619,25 +4659,36 @@ fn isNonNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCV fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { const err_type = ty.errorUnionSet(); - const payload_type = ty.errorUnionPayload(); - if (!err_type.hasRuntimeBits()) { + + if (err_type.errorSetCardinality() == .zero) { return MCValue{ .immediate = 0 }; // always false } try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; - if (!payload_type.hasRuntimeBits()) { - if (err_type.abiSize(self.target.*) <= 8) { - try self.genBinOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 }); - return MCValue{ .compare_flags_unsigned = .gt }; - } else { - return self.fail("TODO isErr for errors with size larger than register size", .{}); - } - } else { - try self.genBinOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 }); - return MCValue{ .compare_flags_unsigned = .gt }; + const err_off = errUnionErrOffset(ty, self.target.*); + switch (operand) { + .stack_offset => |off| { + const offset = off - @intCast(i32, err_off); + try self.genBinOpMir(.cmp, Type.anyerror, .{ .stack_offset = offset }, .{ .immediate = 0 }); + }, + .register => |reg| { + const maybe_lock = self.register_manager.lockReg(reg); + defer if (maybe_lock) |lock| self.register_manager.unlockReg(lock); + const tmp_reg = try self.copyToTmpRegister(ty, operand); + if (err_off > 0) { + const shift = @intCast(u6, err_off * 8); + try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{ .immediate = shift }); + } else { + try self.truncateRegister(Type.anyerror, tmp_reg); + } + try self.genBinOpMir(.cmp, Type.anyerror, .{ .register = tmp_reg }, .{ .immediate = 0 }); + }, + else => return self.fail("TODO implement isErr for {}", .{operand}), } + + return MCValue{ .compare_flags_unsigned = .gt }; } fn isNonErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { @@ -5460,6 +5511,21 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl .immediate => |x_big| { const base_reg = opts.dest_stack_base orelse .rbp; switch (abi_size) { + 0 => { + assert(ty.isError()); + const payload = try self.addExtra(Mir.ImmPair{ + .dest_off = @bitCast(u32, -stack_offset), + .operand = @truncate(u32, x_big), + }); + _ = try self.addInst(.{ + .tag = .mov_mem_imm, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = base_reg, + .flags = 0b00, + }), + .data = .{ .payload = payload }, + }); + }, 1, 2, 4 => { const payload = try self.addExtra(Mir.ImmPair{ .dest_off = @bitCast(u32, -stack_offset), @@ -6642,7 +6708,7 @@ pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { const ref_int = @enumToInt(inst); if (ref_int < Air.Inst.Ref.typed_value_map.len) { const tv = Air.Inst.Ref.typed_value_map[ref_int]; - if (!tv.ty.hasRuntimeBits()) { + if (!tv.ty.hasRuntimeBitsIgnoreComptime() and !tv.ty.isError()) { return MCValue{ .none = {} }; } return self.genTypedValue(tv); @@ -6650,7 +6716,7 @@ pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { // If the type has no codegen bits, no need to store it. const inst_ty = self.air.typeOf(inst); - if (!inst_ty.hasRuntimeBits()) + if (!inst_ty.hasRuntimeBitsIgnoreComptime() and !inst_ty.isError()) return MCValue{ .none = {} }; const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len); @@ -6779,6 +6845,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { const target = self.target.*; switch (typed_value.ty.zigTypeTag()) { + .Void => return MCValue{ .none = {} }, .Pointer => switch (typed_value.ty.ptrSize()) { .Slice => {}, else => { @@ -6840,26 +6907,35 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { } }, .ErrorSet => { - const err_name = typed_value.val.castTag(.@"error").?.data.name; - const module = self.bin_file.options.module.?; - const global_error_set = module.global_error_set; - const error_index = global_error_set.get(err_name).?; - return MCValue{ .immediate = error_index }; + switch (typed_value.val.tag()) { + .@"error" => { + const err_name = typed_value.val.castTag(.@"error").?.data.name; + const module = self.bin_file.options.module.?; + const global_error_set = module.global_error_set; + const error_index = global_error_set.get(err_name).?; + return MCValue{ .immediate = error_index }; + }, + else => { + // In this case we are rendering an error union which has a 0 bits payload. + return MCValue{ .immediate = 0 }; + }, + } }, .ErrorUnion => { const error_type = typed_value.ty.errorUnionSet(); const payload_type = typed_value.ty.errorUnionPayload(); - if (typed_value.val.castTag(.eu_payload)) |_| { - if (!payload_type.hasRuntimeBits()) { - // We use the error type directly as the type. - return MCValue{ .immediate = 0 }; - } - } else { - if (!payload_type.hasRuntimeBits()) { - // We use the error type directly as the type. - return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); - } + if (error_type.errorSetCardinality() == .zero) { + const payload_val = typed_value.val.castTag(.eu_payload).?.data; + return self.genTypedValue(.{ .ty = payload_type, .val = payload_val }); + } + + const is_pl = typed_value.val.errorUnionIsPayload(); + + if (!payload_type.hasRuntimeBitsIgnoreComptime()) { + // We use the error type directly as the type. + const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero); + return self.genTypedValue(.{ .ty = error_type, .val = err_val }); } }, @@ -6867,7 +6943,6 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { .ComptimeFloat => unreachable, .Type => unreachable, .EnumLiteral => unreachable, - .Void => unreachable, .NoReturn => unreachable, .Undefined => unreachable, .Null => unreachable, @@ -6921,11 +6996,14 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { // Return values if (ret_ty.zigTypeTag() == .NoReturn) { result.return_value = .{ .unreach = {} }; - } else if (!ret_ty.hasRuntimeBits()) { + } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { result.return_value = .{ .none = {} }; } else { const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); - if (ret_ty_size <= 8) { + if (ret_ty_size == 0) { + assert(ret_ty.isError()); + result.return_value = .{ .immediate = 0 }; + } else if (ret_ty_size <= 8) { const aliased_reg = registerAlias(c_abi_int_return_regs[0], ret_ty_size); result.return_value = .{ .register = aliased_reg }; } else { @@ -7105,3 +7183,19 @@ fn intrinsicsAllowed(target: Target, ty: Type) bool { fn hasAvxSupport(target: Target) bool { return Target.x86.featureSetHasAny(target.cpu.features, .{ .avx, .avx2 }); } + +fn errUnionPayloadOffset(ty: Type, target: std.Target) u64 { + const payload_ty = ty.errorUnionPayload(); + return if (Type.anyerror.abiAlignment(target) >= payload_ty.abiAlignment(target)) + Type.anyerror.abiSize(target) + else + 0; +} + +fn errUnionErrOffset(ty: Type, target: std.Target) u64 { + const payload_ty = ty.errorUnionPayload(); + return if (Type.anyerror.abiAlignment(target) >= payload_ty.abiAlignment(target)) + 0 + else + payload_ty.abiSize(target); +} diff --git a/src/codegen.zig b/src/codegen.zig index eea8095a62..4f400fa7fc 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -442,7 +442,10 @@ pub fn generateSymbol( .Int => { const info = typed_value.ty.intInfo(target); if (info.bits <= 8) { - const x = @intCast(u8, typed_value.val.toUnsignedInt(target)); + const x: u8 = switch (info.signedness) { + .unsigned => @intCast(u8, typed_value.val.toUnsignedInt(target)), + .signed => @bitCast(u8, @intCast(i8, typed_value.val.toSignedInt())), + }; try code.append(x); return Result{ .appended = {} }; } From c043d57cabdc4db20a55a9877ec607c81d15442f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 24 May 2022 17:35:02 +0200 Subject: [PATCH 1620/2031] x64,arm,aarch64: omit unsupported tests for now --- test/behavior/error.zig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 1b2a67bd57..18cfb03457 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -149,12 +149,19 @@ test "implicit cast to optional to error union to return result loc" { } test "fn returning empty error set can be passed as fn returning any error" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + entry(); comptime entry(); } test "fn returning empty error set can be passed as fn returning any error - pointer" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO entryPtr(); comptime entryPtr(); From 8c49420928b29271429cc09b5d5f1447a942f8d6 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 24 May 2022 19:23:33 +0200 Subject: [PATCH 1621/2031] aarch64: update for new error union layout --- src/arch/aarch64/CodeGen.zig | 114 ++++++++++++++++++++++------------- src/arch/x86_64/CodeGen.zig | 25 ++------ src/codegen.zig | 16 +++++ test/behavior/error.zig | 2 + 4 files changed, 95 insertions(+), 62 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index f4f2b1e5e5..5f358efb09 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -3,6 +3,7 @@ const builtin = @import("builtin"); const mem = std.mem; const math = std.math; const assert = std.debug.assert; +const codegen = @import("../../codegen.zig"); const Air = @import("../../Air.zig"); const Mir = @import("Mir.zig"); const Emit = @import("Emit.zig"); @@ -22,12 +23,14 @@ const leb128 = std.leb; const log = std.log.scoped(.codegen); const build_options = @import("build_options"); -const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; -const FnResult = @import("../../codegen.zig").FnResult; -const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; +const GenerateSymbolError = codegen.GenerateSymbolError; +const FnResult = codegen.FnResult; +const DebugInfoOutput = codegen.DebugInfoOutput; const bits = @import("bits.zig"); const abi = @import("abi.zig"); +const errUnionPayloadOffset = codegen.errUnionPayloadOffset; +const errUnionErrOffset = codegen.errUnionErrOffset; const RegisterManager = abi.RegisterManager; const RegisterLock = RegisterManager.RegisterLock; const Register = bits.Register; @@ -3272,7 +3275,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. fn ret(self: *Self, mcv: MCValue) !void { const ret_ty = self.fn_type.fnReturnType(); - try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); + switch (self.ret_mcv) { + .immediate => { + assert(ret_ty.isError()); + }, + else => { + try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); + }, + } // Just add space for an instruction, patch this later const index = try self.addInst(.{ .tag = .nop, @@ -3601,30 +3611,39 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { const error_type = ty.errorUnionSet(); const payload_type = ty.errorUnionPayload(); - if (!error_type.hasRuntimeBits()) { + if (error_type.errorSetCardinality() == .zero) { return MCValue{ .immediate = 0 }; // always false - } else if (!payload_type.hasRuntimeBits()) { - if (error_type.abiSize(self.target.*) <= 8) { - const reg_mcv: MCValue = switch (operand) { - .register => operand, - else => .{ .register = try self.copyToTmpRegister(error_type, operand) }, - }; + } + const err_off = errUnionErrOffset(ty, self.target.*); + switch (operand) { + .stack_offset => |off| { + const offset = off - @intCast(u32, err_off); + const tmp_reg = try self.copyToTmpRegister(Type.anyerror, .{ .stack_offset = offset }); _ = try self.addInst(.{ .tag = .cmp_immediate, .data = .{ .r_imm12_sh = .{ - .rn = reg_mcv.register, + .rn = tmp_reg, .imm12 = 0, } }, }); - - return MCValue{ .compare_flags_unsigned = .gt }; - } else { - return self.fail("TODO isErr for errors with size > 8", .{}); - } - } else { - return self.fail("TODO isErr for non-empty payloads", .{}); + }, + .register => |reg| { + if (err_off > 0 or payload_type.hasRuntimeBitsIgnoreComptime()) { + return self.fail("TODO implement isErr for register operand with payload bits", .{}); + } + _ = try self.addInst(.{ + .tag = .cmp_immediate, + .data = .{ .r_imm12_sh = .{ + .rn = reg, + .imm12 = 0, + } }, + }); + }, + else => return self.fail("TODO implement isErr for {}", .{operand}), } + + return MCValue{ .compare_flags_unsigned = .gt }; } fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { @@ -4483,7 +4502,7 @@ fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { const ref_int = @enumToInt(inst); if (ref_int < Air.Inst.Ref.typed_value_map.len) { const tv = Air.Inst.Ref.typed_value_map[ref_int]; - if (!tv.ty.hasRuntimeBits()) { + if (!tv.ty.hasRuntimeBitsIgnoreComptime() and !tv.ty.isError()) { return MCValue{ .none = {} }; } return self.genTypedValue(tv); @@ -4491,7 +4510,7 @@ fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { // If the type has no codegen bits, no need to store it. const inst_ty = self.air.typeOf(inst); - if (!inst_ty.hasRuntimeBits()) + if (!inst_ty.hasRuntimeBitsIgnoreComptime() and !inst_ty.isError()) return MCValue{ .none = {} }; const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len); @@ -4674,32 +4693,38 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { } }, .ErrorSet => { - const err_name = typed_value.val.castTag(.@"error").?.data.name; - const module = self.bin_file.options.module.?; - const global_error_set = module.global_error_set; - const error_index = global_error_set.get(err_name).?; - return MCValue{ .immediate = error_index }; + switch (typed_value.val.tag()) { + .@"error" => { + const err_name = typed_value.val.castTag(.@"error").?.data.name; + const module = self.bin_file.options.module.?; + const global_error_set = module.global_error_set; + const error_index = global_error_set.get(err_name).?; + return MCValue{ .immediate = error_index }; + }, + else => { + // In this case we are rendering an error union which has a 0 bits payload. + return MCValue{ .immediate = 0 }; + }, + } }, .ErrorUnion => { const error_type = typed_value.ty.errorUnionSet(); const payload_type = typed_value.ty.errorUnionPayload(); - if (typed_value.val.castTag(.eu_payload)) |pl| { - if (!payload_type.hasRuntimeBits()) { - // We use the error type directly as the type. - return MCValue{ .immediate = 0 }; - } - - _ = pl; - return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty.fmtDebug()}); - } else { - if (!payload_type.hasRuntimeBits()) { - // We use the error type directly as the type. - return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); - } - - return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty.fmtDebug()}); + if (error_type.errorSetCardinality() == .zero) { + const payload_val = typed_value.val.castTag(.eu_payload).?.data; + return self.genTypedValue(.{ .ty = payload_type, .val = payload_val }); } + + const is_pl = typed_value.val.errorUnionIsPayload(); + + if (!payload_type.hasRuntimeBitsIgnoreComptime()) { + // We use the error type directly as the type. + const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero); + return self.genTypedValue(.{ .ty = error_type, .val = err_val }); + } + + return self.lowerUnnamedConst(typed_value); }, .Struct => { return self.lowerUnnamedConst(typed_value); @@ -4796,13 +4821,16 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { if (ret_ty.zigTypeTag() == .NoReturn) { result.return_value = .{ .unreach = {} }; - } else if (!ret_ty.hasRuntimeBits()) { + } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { result.return_value = .{ .none = {} }; } else switch (cc) { .Naked => unreachable, .Unspecified, .C => { const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); - if (ret_ty_size <= 8) { + if (ret_ty_size == 0) { + assert(ret_ty.isError()); + result.return_value = .{ .immediate = 0 }; + } else if (ret_ty_size <= 8) { result.return_value = .{ .register = registerAlias(c_abi_int_return_regs[0], ret_ty_size) }; } else { return self.fail("TODO support more return types for ARM backend", .{}); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index dc2f55f6ef..ba550f6d82 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2,6 +2,7 @@ const std = @import("std"); const build_options = @import("build_options"); const builtin = @import("builtin"); const assert = std.debug.assert; +const codegen = @import("../../codegen.zig"); const leb128 = std.leb; const link = @import("../../link.zig"); const log = std.log.scoped(.codegen); @@ -12,11 +13,11 @@ const trace = @import("../../tracy.zig").trace; const Air = @import("../../Air.zig"); const Allocator = mem.Allocator; const Compilation = @import("../../Compilation.zig"); -const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; +const DebugInfoOutput = codegen.DebugInfoOutput; const DW = std.dwarf; const ErrorMsg = Module.ErrorMsg; -const FnResult = @import("../../codegen.zig").FnResult; -const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; +const FnResult = codegen.FnResult; +const GenerateSymbolError = codegen.GenerateSymbolError; const Emit = @import("Emit.zig"); const Liveness = @import("../../Liveness.zig"); const Mir = @import("Mir.zig"); @@ -28,6 +29,8 @@ const Value = @import("../../value.zig").Value; const bits = @import("bits.zig"); const abi = @import("abi.zig"); +const errUnionPayloadOffset = codegen.errUnionPayloadOffset; +const errUnionErrOffset = codegen.errUnionErrOffset; const callee_preserved_regs = abi.callee_preserved_regs; const caller_preserved_regs = abi.caller_preserved_regs; @@ -7183,19 +7186,3 @@ fn intrinsicsAllowed(target: Target, ty: Type) bool { fn hasAvxSupport(target: Target) bool { return Target.x86.featureSetHasAny(target.cpu.features, .{ .avx, .avx2 }); } - -fn errUnionPayloadOffset(ty: Type, target: std.Target) u64 { - const payload_ty = ty.errorUnionPayload(); - return if (Type.anyerror.abiAlignment(target) >= payload_ty.abiAlignment(target)) - Type.anyerror.abiSize(target) - else - 0; -} - -fn errUnionErrOffset(ty: Type, target: std.Target) u64 { - const payload_ty = ty.errorUnionPayload(); - return if (Type.anyerror.abiAlignment(target) >= payload_ty.abiAlignment(target)) - 0 - else - payload_ty.abiSize(target); -} diff --git a/src/codegen.zig b/src/codegen.zig index 4f400fa7fc..86f2613b5f 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -890,3 +890,19 @@ fn lowerDeclRef( return Result{ .appended = {} }; } + +pub fn errUnionPayloadOffset(ty: Type, target: std.Target) u64 { + const payload_ty = ty.errorUnionPayload(); + return if (Type.anyerror.abiAlignment(target) >= payload_ty.abiAlignment(target)) + Type.anyerror.abiSize(target) + else + 0; +} + +pub fn errUnionErrOffset(ty: Type, target: std.Target) u64 { + const payload_ty = ty.errorUnionPayload(); + return if (Type.anyerror.abiAlignment(target) >= payload_ty.abiAlignment(target)) + 0 + else + payload_ty.abiSize(target); +} diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 18cfb03457..230c2540dc 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -440,6 +440,8 @@ test "return function call to error set from error union function" { } test "optional error set is the same size as error set" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + comptime try expect(@sizeOf(?anyerror) == @sizeOf(anyerror)); comptime try expect(@alignOf(?anyerror) == @alignOf(anyerror)); const S = struct { From 26376c9fda910e28a686d3f772dbda4319abc16d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 24 May 2022 19:48:51 +0200 Subject: [PATCH 1622/2031] wasm: use errUnionPayloadOffset and errUnionErrOffset from codegen.zig --- src/arch/wasm/CodeGen.zig | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 6d0f3a9d23..cfa2c8bb4e 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -22,6 +22,8 @@ const Liveness = @import("../../Liveness.zig"); const Mir = @import("Mir.zig"); const Emit = @import("Emit.zig"); const abi = @import("abi.zig"); +const errUnionPayloadOffset = codegen.errUnionPayloadOffset; +const errUnionErrOffset = codegen.errUnionErrOffset; /// Wasm Value, created when generating an instruction const WValue = union(enum) { @@ -2931,7 +2933,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W try self.emitWValue(operand); if (pl_ty.hasRuntimeBitsIgnoreComptime()) { try self.addMemArg(.i32_load16_u, .{ - .offset = operand.offset() + errUnionErrorOffset(pl_ty, self.target), + .offset = operand.offset() + @intCast(u32, errUnionErrOffset(pl_ty, self.target)), .alignment = Type.anyerror.abiAlignment(self.target), }); } @@ -2959,7 +2961,7 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} }; - const pl_offset = errUnionPayloadOffset(payload_ty, self.target); + const pl_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target)); if (op_is_ptr or isByRef(payload_ty, self.target)) { return self.buildPointerOffset(operand, pl_offset, .new); } @@ -2983,7 +2985,7 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) In return operand; } - return self.load(operand, Type.anyerror, errUnionErrorOffset(payload_ty, self.target)); + return self.load(operand, Type.anyerror, @intCast(u32, errUnionErrOffset(payload_ty, self.target))); } fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -3003,13 +3005,13 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } const err_union = try self.allocStack(err_ty); - const payload_ptr = try self.buildPointerOffset(err_union, errUnionPayloadOffset(pl_ty, self.target), .new); + const payload_ptr = try self.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, self.target)), .new); try self.store(payload_ptr, operand, pl_ty, 0); // ensure we also write '0' to the error part, so any present stack value gets overwritten by it. try self.emitWValue(err_union); try self.addImm32(0); - const err_val_offset = errUnionErrorOffset(pl_ty, self.target); + const err_val_offset = @intCast(u32, errUnionErrOffset(pl_ty, self.target)); try self.addMemArg(.i32_store16, .{ .offset = err_union.offset() + err_val_offset, .alignment = 2 }); return err_union; @@ -3029,10 +3031,10 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const err_union = try self.allocStack(err_ty); // store error value - try self.store(err_union, operand, Type.anyerror, errUnionErrorOffset(pl_ty, self.target)); + try self.store(err_union, operand, Type.anyerror, @intCast(u32, errUnionErrOffset(pl_ty, self.target))); // write 'undefined' to the payload - const payload_ptr = try self.buildPointerOffset(err_union, errUnionPayloadOffset(pl_ty, self.target), .new); + const payload_ptr = try self.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, self.target)), .new); const len = @intCast(u32, err_ty.errorUnionPayload().abiSize(self.target)); try self.memset(payload_ptr, .{ .imm32 = len }, .{ .imm32 = 0xaaaaaaaa }); @@ -3984,7 +3986,7 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue operand, .{ .imm32 = 0 }, Type.anyerror, - errUnionErrorOffset(payload_ty, self.target), + @intCast(u32, errUnionErrOffset(payload_ty, self.target)), ); if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; @@ -3993,7 +3995,7 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue return operand; } - return self.buildPointerOffset(operand, errUnionPayloadOffset(payload_ty, self.target), .new); + return self.buildPointerOffset(operand, @intCast(u32, errUnionPayloadOffset(payload_ty, self.target)), .new); } fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -4621,17 +4623,3 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !WValue { } }); return WValue{ .none = {} }; } - -fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u32 { - if (Type.anyerror.abiAlignment(target) > payload_ty.abiAlignment(target)) { - return @intCast(u32, Type.anyerror.abiSize(target)); - } - return 0; -} - -fn errUnionErrorOffset(payload_ty: Type, target: std.Target) u32 { - if (Type.anyerror.abiAlignment(target) > payload_ty.abiAlignment(target)) { - return 0; - } - return @intCast(u32, payload_ty.abiSize(target)); -} From c847a462ae11e0d483ad877b3ecc9ec291c29bb3 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Tue, 24 May 2022 20:47:45 +0200 Subject: [PATCH 1623/2031] stage2 ARM: update to new union layout --- src/arch/arm/CodeGen.zig | 101 +++++++++++++++++++++++++-------------- test/behavior/error.zig | 1 + 2 files changed, 65 insertions(+), 37 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 75fe8f6403..3d69e4022b 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -3,6 +3,7 @@ const builtin = @import("builtin"); const mem = std.mem; const math = std.math; const assert = std.debug.assert; +const codegen = @import("../../codegen.zig"); const Air = @import("../../Air.zig"); const Mir = @import("Mir.zig"); const Emit = @import("Emit.zig"); @@ -22,12 +23,14 @@ const leb128 = std.leb; const log = std.log.scoped(.codegen); const build_options = @import("build_options"); -const FnResult = @import("../../codegen.zig").FnResult; -const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; -const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; +const FnResult = codegen.FnResult; +const GenerateSymbolError = codegen.GenerateSymbolError; +const DebugInfoOutput = codegen.DebugInfoOutput; const bits = @import("bits.zig"); const abi = @import("abi.zig"); +const errUnionPayloadOffset = codegen.errUnionPayloadOffset; +const errUnionErrOffset = codegen.errUnionErrOffset; const RegisterManager = abi.RegisterManager; const RegisterLock = RegisterManager.RegisterLock; const Register = bits.Register; @@ -1763,19 +1766,26 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { /// Given an error union, returns the error fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { + const err_ty = error_union_ty.errorUnionSet(); const payload_ty = error_union_ty.errorUnionPayload(); - if (!payload_ty.hasRuntimeBits()) return error_union_mcv; + if (err_ty.errorSetCardinality() == .zero) { + return MCValue{ .immediate = 0 }; + } + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + return error_union_mcv; + } + const err_offset = @intCast(u32, errUnionErrOffset(error_union_ty, self.target.*)); switch (error_union_mcv) { .register => return self.fail("TODO errUnionErr for registers", .{}), .stack_argument_offset => |off| { - return MCValue{ .stack_argument_offset = off }; + return MCValue{ .stack_argument_offset = off - err_offset }; }, .stack_offset => |off| { - return MCValue{ .stack_offset = off }; + return MCValue{ .stack_offset = off - err_offset }; }, .memory => |addr| { - return MCValue{ .memory = addr }; + return MCValue{ .memory = addr + err_offset }; }, else => unreachable, // invalid MCValue for an error union } @@ -1793,24 +1803,26 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { /// Given an error union, returns the payload fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { + const err_ty = error_union_ty.errorUnionSet(); const payload_ty = error_union_ty.errorUnionPayload(); - if (!payload_ty.hasRuntimeBits()) return MCValue.none; - - const error_ty = error_union_ty.errorUnionSet(); - const error_size = @intCast(u32, error_ty.abiSize(self.target.*)); - const eu_align = @intCast(u32, error_union_ty.abiAlignment(self.target.*)); - const offset = std.mem.alignForwardGeneric(u32, error_size, eu_align); + if (err_ty.errorSetCardinality() == .zero) { + return error_union_mcv; + } + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + return MCValue.none; + } + const payload_offset = @intCast(u32, errUnionPayloadOffset(error_union_ty, self.target.*)); switch (error_union_mcv) { .register => return self.fail("TODO errUnionPayload for registers", .{}), .stack_argument_offset => |off| { - return MCValue{ .stack_argument_offset = off - offset }; + return MCValue{ .stack_argument_offset = off - payload_offset }; }, .stack_offset => |off| { - return MCValue{ .stack_offset = off - offset }; + return MCValue{ .stack_offset = off - payload_offset }; }, .memory => |addr| { - return MCValue{ .memory = addr - offset }; + return MCValue{ .memory = addr + payload_offset }; }, else => unreachable, // invalid MCValue for an error union } @@ -3478,6 +3490,9 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { switch (self.ret_mcv) { .none => {}, + .immediate => { + assert(ret_ty.isError()); + }, .register => |reg| { // Return result by value try self.genSetReg(ret_ty, reg, operand); @@ -3867,7 +3882,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { const error_type = ty.errorUnionSet(); const error_int_type = Type.initTag(.u16); - if (!error_type.hasRuntimeBits()) { + if (error_type.errorSetCardinality() == .zero) { return MCValue{ .immediate = 0 }; // always false } @@ -4975,7 +4990,7 @@ fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { const ref_int = @enumToInt(inst); if (ref_int < Air.Inst.Ref.typed_value_map.len) { const tv = Air.Inst.Ref.typed_value_map[ref_int]; - if (!tv.ty.hasRuntimeBits()) { + if (!tv.ty.hasRuntimeBitsIgnoreComptime() and !tv.ty.isError()) { return MCValue{ .none = {} }; } return self.genTypedValue(tv); @@ -4983,7 +4998,7 @@ fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { // If the type has no codegen bits, no need to store it. const inst_ty = self.air.typeOf(inst); - if (!inst_ty.hasRuntimeBits()) + if (!inst_ty.hasRuntimeBitsIgnoreComptime() and !inst_ty.isError()) return MCValue{ .none = {} }; const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len); @@ -5147,26 +5162,35 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { } }, .ErrorSet => { - const err_name = typed_value.val.castTag(.@"error").?.data.name; - const module = self.bin_file.options.module.?; - const global_error_set = module.global_error_set; - const error_index = global_error_set.get(err_name).?; - return MCValue{ .immediate = error_index }; + switch (typed_value.val.tag()) { + .@"error" => { + const err_name = typed_value.val.castTag(.@"error").?.data.name; + const module = self.bin_file.options.module.?; + const global_error_set = module.global_error_set; + const error_index = global_error_set.get(err_name).?; + return MCValue{ .immediate = error_index }; + }, + else => { + // In this case we are rendering an error union which has a 0 bits payload. + return MCValue{ .immediate = 0 }; + }, + } }, .ErrorUnion => { const error_type = typed_value.ty.errorUnionSet(); const payload_type = typed_value.ty.errorUnionPayload(); - if (typed_value.val.castTag(.eu_payload)) |_| { - if (!payload_type.hasRuntimeBits()) { - // We use the error type directly as the type. - return MCValue{ .immediate = 0 }; - } - } else { - if (!payload_type.hasRuntimeBits()) { - // We use the error type directly as the type. - return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); - } + if (error_type.errorSetCardinality() == .zero) { + const payload_val = typed_value.val.castTag(.eu_payload).?.data; + return self.genTypedValue(.{ .ty = payload_type, .val = payload_val }); + } + + const is_pl = typed_value.val.errorUnionIsPayload(); + + if (!payload_type.hasRuntimeBitsIgnoreComptime()) { + // We use the error type directly as the type. + const err_val = if (!is_pl) typed_value.val else Value.initTag(.zero); + return self.genTypedValue(.{ .ty = error_type, .val = err_val }); } }, @@ -5231,7 +5255,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { if (ret_ty.zigTypeTag() == .NoReturn) { result.return_value = .{ .unreach = {} }; - } else if (!ret_ty.hasRuntimeBits()) { + } else if (!ret_ty.hasRuntimeBitsIgnoreComptime()) { result.return_value = .{ .none = {} }; } else { const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); @@ -5278,11 +5302,14 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { .Unspecified => { if (ret_ty.zigTypeTag() == .NoReturn) { result.return_value = .{ .unreach = {} }; - } else if (!ret_ty.hasRuntimeBits()) { + } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { result.return_value = .{ .none = {} }; } else { const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); - if (ret_ty_size <= 4) { + if (ret_ty_size == 0) { + assert(ret_ty.isError()); + result.return_value = .{ .immediate = 0 }; + } else if (ret_ty_size <= 4) { result.return_value = .{ .register = .r0 }; } else { // The result is returned by reference, not by diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 230c2540dc..83a9384d71 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -441,6 +441,7 @@ test "return function call to error set from error union function" { test "optional error set is the same size as error set" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO comptime try expect(@sizeOf(?anyerror) == @sizeOf(anyerror)); comptime try expect(@alignOf(?anyerror) == @alignOf(anyerror)); From c711c788f0a840f45d0d7423efe2f946b47caafb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 May 2022 15:10:18 -0700 Subject: [PATCH 1624/2031] stage2: fixes for error unions, optionals, errors * `?E` where E is an error set with only one field now lowers the same as `bool`. * Fix implementation of errUnionErrOffset and errUnionPayloadOffset to properly compute the offset of each field. Also name them the same as the corresponding LLVM functions and have the same function signature, to avoid confusion. This fixes a bug where wasm was passing the error union type instead of the payload type. * Fix C backend handling of optionals with zero-bit payload types. * C backend: separate out airOptionalPayload and airOptionalPayloadPtr which reduces branching and cleans up control flow. * Make Type.isNoReturn return true for error sets with no fields. * Make `?error{}` have only one possible value (null). --- src/Sema.zig | 11 ++++- src/arch/aarch64/CodeGen.zig | 4 +- src/arch/arm/CodeGen.zig | 6 +-- src/arch/wasm/CodeGen.zig | 18 ++++---- src/arch/x86_64/CodeGen.zig | 16 +++---- src/codegen.zig | 28 +++++++----- src/codegen/c.zig | 88 +++++++++++++++++++++++------------- src/type.zig | 64 ++++++++++++++++++++++---- test/behavior/error.zig | 34 +++++++++++++- 9 files changed, 192 insertions(+), 77 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index b718912a38..b0c3c17483 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -23316,7 +23316,6 @@ pub fn typeHasOnePossibleValue( .const_slice, .mut_slice, .anyopaque, - .optional, .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, @@ -23351,6 +23350,16 @@ pub fn typeHasOnePossibleValue( .bound_fn, => return null, + .optional => { + var buf: Type.Payload.ElemType = undefined; + const child_ty = ty.optionalChild(&buf); + if (child_ty.isNoReturn()) { + return Value.@"null"; + } else { + return null; + } + }, + .error_set_single => { const name = ty.castTag(.error_set_single).?.data; return try Value.Tag.@"error".create(sema.arena, .{ .name = name }); diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 5f358efb09..2a71f3138a 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -30,7 +30,7 @@ const DebugInfoOutput = codegen.DebugInfoOutput; const bits = @import("bits.zig"); const abi = @import("abi.zig"); const errUnionPayloadOffset = codegen.errUnionPayloadOffset; -const errUnionErrOffset = codegen.errUnionErrOffset; +const errUnionErrorOffset = codegen.errUnionErrorOffset; const RegisterManager = abi.RegisterManager; const RegisterLock = RegisterManager.RegisterLock; const Register = bits.Register; @@ -3615,7 +3615,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { return MCValue{ .immediate = 0 }; // always false } - const err_off = errUnionErrOffset(ty, self.target.*); + const err_off = errUnionErrorOffset(payload_type, self.target.*); switch (operand) { .stack_offset => |off| { const offset = off - @intCast(u32, err_off); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 3d69e4022b..b7682a5b9a 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -30,7 +30,7 @@ const DebugInfoOutput = codegen.DebugInfoOutput; const bits = @import("bits.zig"); const abi = @import("abi.zig"); const errUnionPayloadOffset = codegen.errUnionPayloadOffset; -const errUnionErrOffset = codegen.errUnionErrOffset; +const errUnionErrorOffset = codegen.errUnionErrorOffset; const RegisterManager = abi.RegisterManager; const RegisterLock = RegisterManager.RegisterLock; const Register = bits.Register; @@ -1775,7 +1775,7 @@ fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCV return error_union_mcv; } - const err_offset = @intCast(u32, errUnionErrOffset(error_union_ty, self.target.*)); + const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*)); switch (error_union_mcv) { .register => return self.fail("TODO errUnionErr for registers", .{}), .stack_argument_offset => |off| { @@ -1812,7 +1812,7 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) return MCValue.none; } - const payload_offset = @intCast(u32, errUnionPayloadOffset(error_union_ty, self.target.*)); + const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*)); switch (error_union_mcv) { .register => return self.fail("TODO errUnionPayload for registers", .{}), .stack_argument_offset => |off| { diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index cfa2c8bb4e..1eddb7441b 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -23,7 +23,7 @@ const Mir = @import("Mir.zig"); const Emit = @import("Emit.zig"); const abi = @import("abi.zig"); const errUnionPayloadOffset = codegen.errUnionPayloadOffset; -const errUnionErrOffset = codegen.errUnionErrOffset; +const errUnionErrorOffset = codegen.errUnionErrorOffset; /// Wasm Value, created when generating an instruction const WValue = union(enum) { @@ -2919,10 +2919,10 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); - const err_ty = self.air.typeOf(un_op); - const pl_ty = err_ty.errorUnionPayload(); + const err_union_ty = self.air.typeOf(un_op); + const pl_ty = err_union_ty.errorUnionPayload(); - if (err_ty.errorUnionSet().errorSetCardinality() == .zero) { + if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { switch (opcode) { .i32_ne => return WValue{ .imm32 = 0 }, .i32_eq => return WValue{ .imm32 = 1 }, @@ -2933,7 +2933,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W try self.emitWValue(operand); if (pl_ty.hasRuntimeBitsIgnoreComptime()) { try self.addMemArg(.i32_load16_u, .{ - .offset = operand.offset() + @intCast(u32, errUnionErrOffset(pl_ty, self.target)), + .offset = operand.offset() + @intCast(u32, errUnionErrorOffset(pl_ty, self.target)), .alignment = Type.anyerror.abiAlignment(self.target), }); } @@ -2985,7 +2985,7 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) In return operand; } - return self.load(operand, Type.anyerror, @intCast(u32, errUnionErrOffset(payload_ty, self.target))); + return self.load(operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(payload_ty, self.target))); } fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { @@ -3011,7 +3011,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { // ensure we also write '0' to the error part, so any present stack value gets overwritten by it. try self.emitWValue(err_union); try self.addImm32(0); - const err_val_offset = @intCast(u32, errUnionErrOffset(pl_ty, self.target)); + const err_val_offset = @intCast(u32, errUnionErrorOffset(pl_ty, self.target)); try self.addMemArg(.i32_store16, .{ .offset = err_union.offset() + err_val_offset, .alignment = 2 }); return err_union; @@ -3031,7 +3031,7 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const err_union = try self.allocStack(err_ty); // store error value - try self.store(err_union, operand, Type.anyerror, @intCast(u32, errUnionErrOffset(pl_ty, self.target))); + try self.store(err_union, operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(pl_ty, self.target))); // write 'undefined' to the payload const payload_ptr = try self.buildPointerOffset(err_union, @intCast(u32, errUnionPayloadOffset(pl_ty, self.target)), .new); @@ -3986,7 +3986,7 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue operand, .{ .imm32 = 0 }, Type.anyerror, - @intCast(u32, errUnionErrOffset(payload_ty, self.target)), + @intCast(u32, errUnionErrorOffset(payload_ty, self.target)), ); if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index ba550f6d82..5c69f78724 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -30,7 +30,7 @@ const Value = @import("../../value.zig").Value; const bits = @import("bits.zig"); const abi = @import("abi.zig"); const errUnionPayloadOffset = codegen.errUnionPayloadOffset; -const errUnionErrOffset = codegen.errUnionErrOffset; +const errUnionErrorOffset = codegen.errUnionErrorOffset; const callee_preserved_regs = abi.callee_preserved_regs; const caller_preserved_regs = abi.caller_preserved_regs; @@ -1799,7 +1799,7 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { break :result operand; } - const err_off = errUnionErrOffset(err_union_ty, self.target.*); + const err_off = errUnionErrorOffset(payload_ty, self.target.*); switch (operand) { .stack_offset => |off| { const offset = off - @intCast(i32, err_off); @@ -1844,7 +1844,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { break :result MCValue.none; } - const payload_off = errUnionPayloadOffset(err_union_ty, self.target.*); + const payload_off = errUnionPayloadOffset(payload_ty, self.target.*); switch (operand) { .stack_offset => |off| { const offset = off - @intCast(i32, payload_off); @@ -1978,8 +1978,8 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*)); const abi_align = error_union_ty.abiAlignment(self.target.*); const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align)); - const payload_off = errUnionPayloadOffset(error_union_ty, self.target.*); - const err_off = errUnionErrOffset(error_union_ty, self.target.*); + const payload_off = errUnionPayloadOffset(payload_ty, self.target.*); + const err_off = errUnionErrorOffset(payload_ty, self.target.*); try self.genSetStack(payload_ty, stack_offset - @intCast(i32, payload_off), operand, .{}); try self.genSetStack(Type.anyerror, stack_offset - @intCast(i32, err_off), .{ .immediate = 0 }, .{}); @@ -2007,8 +2007,8 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*)); const abi_align = error_union_ty.abiAlignment(self.target.*); const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align)); - const payload_off = errUnionPayloadOffset(error_union_ty, self.target.*); - const err_off = errUnionErrOffset(error_union_ty, self.target.*); + const payload_off = errUnionPayloadOffset(payload_ty, self.target.*); + const err_off = errUnionErrorOffset(payload_ty, self.target.*); try self.genSetStack(Type.anyerror, stack_offset - @intCast(i32, err_off), operand, .{}); try self.genSetStack(payload_ty, stack_offset - @intCast(i32, payload_off), .undef, .{}); @@ -4670,7 +4670,7 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; - const err_off = errUnionErrOffset(ty, self.target.*); + const err_off = errUnionErrorOffset(ty.errorUnionPayload(), self.target.*); switch (operand) { .stack_offset => |off| { const offset = off - @intCast(i32, err_off); diff --git a/src/codegen.zig b/src/codegen.zig index 86f2613b5f..fbe462959e 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -891,18 +891,22 @@ fn lowerDeclRef( return Result{ .appended = {} }; } -pub fn errUnionPayloadOffset(ty: Type, target: std.Target) u64 { - const payload_ty = ty.errorUnionPayload(); - return if (Type.anyerror.abiAlignment(target) >= payload_ty.abiAlignment(target)) - Type.anyerror.abiSize(target) - else - 0; +pub fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u64 { + const payload_align = payload_ty.abiAlignment(target); + const error_align = Type.anyerror.abiAlignment(target); + if (payload_align >= error_align) { + return 0; + } else { + return mem.alignForwardGeneric(u64, Type.anyerror.abiSize(target), payload_align); + } } -pub fn errUnionErrOffset(ty: Type, target: std.Target) u64 { - const payload_ty = ty.errorUnionPayload(); - return if (Type.anyerror.abiAlignment(target) >= payload_ty.abiAlignment(target)) - 0 - else - payload_ty.abiSize(target); +pub fn errUnionErrorOffset(payload_ty: Type, target: std.Target) u64 { + const payload_align = payload_ty.abiAlignment(target); + const error_align = Type.anyerror.abiAlignment(target); + if (payload_align >= error_align) { + return mem.alignForwardGeneric(u64, payload_ty.abiSize(target), error_align); + } else { + return 0; + } } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 1b6708c1cf..1e45090648 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -711,21 +711,24 @@ pub const DeclGen = struct { .Bool => return writer.print("{}", .{val.toBool()}), .Optional => { var opt_buf: Type.Payload.ElemType = undefined; - const payload_type = ty.optionalChild(&opt_buf); - if (ty.optionalReprIsPayload()) { - return dg.renderValue(writer, payload_type, val, location); - } - if (payload_type.abiSize(target) == 0) { + const payload_ty = ty.optionalChild(&opt_buf); + + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { const is_null = val.castTag(.opt_payload) == null; return writer.print("{}", .{is_null}); } + + if (ty.optionalReprIsPayload()) { + return dg.renderValue(writer, payload_ty, val, location); + } + try writer.writeByte('('); try dg.renderTypecast(writer, ty); try writer.writeAll("){"); if (val.castTag(.opt_payload)) |pl| { const payload_val = pl.data; try writer.writeAll(" .is_null = false, .payload = "); - try dg.renderValue(writer, payload_type, payload_val, location); + try dg.renderValue(writer, payload_ty, payload_val, location); try writer.writeAll(" }"); } else { try writer.writeAll(" .is_null = true }"); @@ -1360,12 +1363,12 @@ pub const DeclGen = struct { var opt_buf: Type.Payload.ElemType = undefined; const child_type = t.optionalChild(&opt_buf); - if (t.optionalReprIsPayload()) { - return dg.renderType(w, child_type); + if (!child_type.hasRuntimeBitsIgnoreComptime()) { + return w.writeAll("bool"); } - if (child_type.abiSize(target) == 0) { - return w.writeAll("bool"); + if (t.optionalReprIsPayload()) { + return dg.renderType(w, child_type); } const name = dg.getTypedefName(t) orelse @@ -1816,8 +1819,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .not => try airNot (f, inst), .optional_payload => try airOptionalPayload(f, inst), - .optional_payload_ptr => try airOptionalPayload(f, inst), + .optional_payload_ptr => try airOptionalPayloadPtr(f, inst), .optional_payload_ptr_set => try airOptionalPayloadPtrSet(f, inst), + .wrap_optional => try airWrapOptional(f, inst), .is_err => try airIsErr(f, inst, false, "!="), .is_non_err => try airIsErr(f, inst, false, "=="), @@ -1846,7 +1850,6 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .cond_br => try airCondBr(f, inst), .br => try airBr(f, inst), .switch_br => try airSwitchBr(f, inst), - .wrap_optional => try airWrapOptional(f, inst), .struct_field_ptr => try airStructFieldPtr(f, inst), .array_to_slice => try airArrayToSlice(f, inst), .cmpxchg_weak => try airCmpxchg(f, inst, "weak"), @@ -3145,7 +3148,6 @@ fn airIsNull( const un_op = f.air.instructions.items(.data)[inst].un_op; const writer = f.object.writer(); const operand = try f.resolveInst(un_op); - const target = f.object.dg.module.getTarget(); const local = try f.allocLocal(Type.initTag(.bool), .Const); try writer.writeAll(" = ("); @@ -3153,18 +3155,18 @@ fn airIsNull( const ty = f.air.typeOf(un_op); var opt_buf: Type.Payload.ElemType = undefined; - const payload_type = if (ty.zigTypeTag() == .Pointer) + const payload_ty = if (ty.zigTypeTag() == .Pointer) ty.childType().optionalChild(&opt_buf) else ty.optionalChild(&opt_buf); - if (ty.isPtrLikeOptional()) { + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + try writer.print("){s} {s} true;\n", .{ deref_suffix, operator }); + } else if (ty.isPtrLikeOptional()) { // operand is a regular pointer, test `operand !=/== NULL` try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator }); - } else if (payload_type.zigTypeTag() == .ErrorSet) { + } else if (payload_ty.zigTypeTag() == .ErrorSet) { try writer.print("){s} {s} 0;\n", .{ deref_suffix, operator }); - } else if (payload_type.abiSize(target) == 0) { - try writer.print("){s} {s} true;\n", .{ deref_suffix, operator }); } else { try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator }); } @@ -3172,18 +3174,46 @@ fn airIsNull( } fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) - return CValue.none; + if (f.liveness.isUnused(inst)) return CValue.none; const ty_op = f.air.instructions.items(.data)[inst].ty_op; const writer = f.object.writer(); const operand = try f.resolveInst(ty_op.operand); - const operand_ty = f.air.typeOf(ty_op.operand); + const opt_ty = f.air.typeOf(ty_op.operand); - const opt_ty = if (operand_ty.zigTypeTag() == .Pointer) - operand_ty.elemType() - else - operand_ty; + var buf: Type.Payload.ElemType = undefined; + const payload_ty = opt_ty.optionalChild(&buf); + + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + return CValue.none; + } + + if (opt_ty.optionalReprIsPayload()) { + return operand; + } + + const inst_ty = f.air.typeOfIndex(inst); + const local = try f.allocLocal(inst_ty, .Const); + try writer.writeAll(" = ("); + try f.writeCValue(writer, operand); + try writer.writeAll(").payload;\n"); + return local; +} + +fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { + if (f.liveness.isUnused(inst)) return CValue.none; + + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + const writer = f.object.writer(); + const operand = try f.resolveInst(ty_op.operand); + const ptr_ty = f.air.typeOf(ty_op.operand); + const opt_ty = ptr_ty.childType(); + var buf: Type.Payload.ElemType = undefined; + const payload_ty = opt_ty.optionalChild(&buf); + + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + return operand; + } if (opt_ty.optionalReprIsPayload()) { // the operand is just a regular pointer, no need to do anything special. @@ -3192,14 +3222,10 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { } const inst_ty = f.air.typeOfIndex(inst); - const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else "."; - const maybe_addrof = if (inst_ty.zigTypeTag() == .Pointer) "&" else ""; - const local = try f.allocLocal(inst_ty, .Const); - try writer.print(" = {s}(", .{maybe_addrof}); + try writer.writeAll(" = &("); try f.writeCValue(writer, operand); - - try writer.print("){s}payload;\n", .{maybe_deref}); + try writer.writeAll(")->payload;\n"); return local; } diff --git a/src/type.zig b/src/type.zig index 1c59cf9e59..4325d6d772 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2375,7 +2375,6 @@ pub const Type = extern union { // These types have more than one possible value, so the result is the same as // asking whether they are comptime-only types. .anyframe_T, - .optional, .optional_single_mut_pointer, .optional_single_const_pointer, .single_const_pointer, @@ -2397,6 +2396,22 @@ pub const Type = extern union { } }, + .optional => { + var buf: Payload.ElemType = undefined; + const child_ty = ty.optionalChild(&buf); + if (child_ty.isNoReturn()) { + // Then the optional is comptime-known to be null. + return false; + } + if (ignore_comptime_only) { + return true; + } else if (sema_kit) |sk| { + return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, child_ty)); + } else { + return !comptimeOnly(child_ty); + } + }, + .error_union => { // This code needs to be kept in sync with the equivalent switch prong // in abiSizeAdvanced. @@ -2665,13 +2680,22 @@ pub const Type = extern union { }; } - pub fn isNoReturn(self: Type) bool { - const definitely_correct_result = - self.tag_if_small_enough != .bound_fn and - self.zigTypeTag() == .NoReturn; - const fast_result = self.tag_if_small_enough == Tag.noreturn; - assert(fast_result == definitely_correct_result); - return fast_result; + /// TODO add enums with no fields here + pub fn isNoReturn(ty: Type) bool { + switch (ty.tag()) { + .noreturn => return true, + .error_set => { + const err_set_obj = ty.castTag(.error_set).?.data; + const names = err_set_obj.names.keys(); + return names.len == 0; + }, + .error_set_merged => { + const name_map = ty.castTag(.error_set_merged).?.data; + const names = name_map.keys(); + return names.len == 0; + }, + else => return false, + } } /// Returns 0 if the pointer is naturally aligned and the element type is 0-bit. @@ -2918,7 +2942,13 @@ pub const Type = extern union { switch (child_type.zigTypeTag()) { .Pointer => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }, - .ErrorSet => return abiAlignmentAdvanced(Type.anyerror, target, strat), + .ErrorSet => switch (child_type.errorSetCardinality()) { + // `?error{}` is comptime-known to be null. + .zero => return AbiAlignmentAdvanced{ .scalar = 0 }, + .one => return AbiAlignmentAdvanced{ .scalar = 1 }, + .many => return abiAlignmentAdvanced(Type.anyerror, target, strat), + }, + .NoReturn => return AbiAlignmentAdvanced{ .scalar = 0 }, else => {}, } @@ -3365,6 +3395,11 @@ pub const Type = extern union { .optional => { var buf: Payload.ElemType = undefined; const child_type = ty.optionalChild(&buf); + + if (child_type.isNoReturn()) { + return AbiSizeAdvanced{ .scalar = 0 }; + } + if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 }; switch (child_type.zigTypeTag()) { @@ -4804,7 +4839,6 @@ pub const Type = extern union { .const_slice, .mut_slice, .anyopaque, - .optional, .optional_single_mut_pointer, .optional_single_const_pointer, .enum_literal, @@ -4839,6 +4873,16 @@ pub const Type = extern union { .bound_fn, => return null, + .optional => { + var buf: Payload.ElemType = undefined; + const child_ty = ty.optionalChild(&buf); + if (child_ty.isNoReturn()) { + return Value.@"null"; + } else { + return null; + } + }, + .error_set_single => return Value.initTag(.the_only_possible_value), .error_set => { const err_set_obj = ty.castTag(.error_set).?.data; diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 83a9384d71..4f316aeab2 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -121,7 +121,7 @@ test "debug info for optional error set" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - const SomeError = error{Hello}; + const SomeError = error{ Hello, Hello2 }; var a_local_variable: ?SomeError = null; _ = a_local_variable; } @@ -454,6 +454,38 @@ test "optional error set is the same size as error set" { comptime try expect(S.returnsOptErrSet() == null); } +test "optional error set with only one error is the same size as bool" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const E = error{only}; + comptime try expect(@sizeOf(?E) == @sizeOf(bool)); + comptime try expect(@alignOf(?E) == @alignOf(bool)); + const S = struct { + fn gimmeNull() ?E { + return null; + } + fn gimmeErr() ?E { + return error.only; + } + }; + try expect(S.gimmeNull() == null); + try expect(error.only == S.gimmeErr().?); + comptime try expect(S.gimmeNull() == null); + comptime try expect(error.only == S.gimmeErr().?); +} + +test "optional empty error set" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + + const T = ?error{}; + var t: T = undefined; + if (t != null) { + @compileError("test failed"); + } +} + test "nested catch" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From 1f16b07d6fe43f96287b6cca8e8b58996199481f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 May 2022 17:53:04 -0700 Subject: [PATCH 1625/2031] stage2: treat `error{}!void` as a zero-bit type --- src/Sema.zig | 24 ++++++- src/codegen/llvm.zig | 151 +++++++++++++++++++++------------------- src/type.zig | 65 +++++++++++++---- test/behavior/error.zig | 33 ++++++++- 4 files changed, 183 insertions(+), 90 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index b0c3c17483..e625539286 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -23320,7 +23320,6 @@ pub fn typeHasOnePossibleValue( .optional_single_const_pointer, .enum_literal, .anyerror_void_error_union, - .error_union, .error_set_inferred, .@"opaque", .var_args_param, @@ -23360,6 +23359,29 @@ pub fn typeHasOnePossibleValue( } }, + .error_union => { + const error_ty = ty.errorUnionSet(); + switch (error_ty.errorSetCardinality()) { + .zero => { + const payload_ty = ty.errorUnionPayload(); + if (try typeHasOnePossibleValue(sema, block, src, payload_ty)) |payload_val| { + return try Value.Tag.eu_payload.create(sema.arena, payload_val); + } else { + return null; + } + }, + .one => { + if (ty.errorUnionPayload().isNoReturn()) { + const error_val = (try typeHasOnePossibleValue(sema, block, src, error_ty)).?; + return error_val; + } else { + return null; + } + }, + .many => return null, + } + }, + .error_set_single => { const name = ty.castTag(.error_set_single).?.data; return try Value.Tag.@"error".create(sema.arena, .{ .name = name }); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index cf0188b060..ec71297c10 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1470,16 +1470,25 @@ pub const Object = struct { return full_di_ty; }, .ErrorUnion => { - const err_set_ty = ty.errorUnionSet(); const payload_ty = ty.errorUnionPayload(); - if (err_set_ty.errorSetCardinality() == .zero) { - const payload_di_ty = try o.lowerDebugType(payload_ty, .full); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(payload_di_ty), .{ .mod = o.module }); - return payload_di_ty; + switch (ty.errorUnionSet().errorSetCardinality()) { + .zero => { + const payload_di_ty = try o.lowerDebugType(payload_ty, .full); + // The recursive call to `lowerDebugType` means we can't use `gop` anymore. + try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(payload_di_ty), .{ .mod = o.module }); + return payload_di_ty; + }, + .one => { + if (payload_ty.isNoReturn()) { + const di_type = dib.createBasicType("void", 0, DW.ATE.signed); + gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_type); + return di_type; + } + }, + .many => {}, } if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { - const err_set_di_ty = try o.lowerDebugType(err_set_ty, .full); + const err_set_di_ty = try o.lowerDebugType(Type.anyerror, .full); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(err_set_di_ty), .{ .mod = o.module }); return err_set_di_ty; @@ -1502,56 +1511,51 @@ pub const Object = struct { break :blk fwd_decl; }; - const err_set_size = err_set_ty.abiSize(target); - const err_set_align = err_set_ty.abiAlignment(target); + const error_size = Type.anyerror.abiSize(target); + const error_align = Type.anyerror.abiAlignment(target); const payload_size = payload_ty.abiSize(target); const payload_align = payload_ty.abiAlignment(target); - var offset: u64 = 0; - offset += err_set_size; - offset = std.mem.alignForwardGeneric(u64, offset, payload_align); - const payload_offset = offset; - - var len: u8 = 2; - var fields: [3]*llvm.DIType = .{ - dib.createMemberType( - fwd_decl.toScope(), - "tag", - di_file, - line, - err_set_size * 8, // size in bits - err_set_align * 8, // align in bits - 0, // offset in bits - 0, // flags - try o.lowerDebugType(err_set_ty, .full), - ), - dib.createMemberType( - fwd_decl.toScope(), - "value", - di_file, - line, - payload_size * 8, // size in bits - payload_align * 8, // align in bits - payload_offset * 8, // offset in bits - 0, // flags - try o.lowerDebugType(payload_ty, .full), - ), - undefined, - }; - - const error_size = Type.anyerror.abiSize(target); - if (payload_align > error_size) { - fields[2] = fields[1]; - const pad_len = @intCast(u32, payload_align - error_size); - fields[1] = dib.createArrayType( - pad_len * 8, - 8, - try o.lowerDebugType(Type.u8, .full), - @intCast(c_int, pad_len), - ); - len += 1; + var error_index: u32 = undefined; + var payload_index: u32 = undefined; + var error_offset: u64 = undefined; + var payload_offset: u64 = undefined; + if (error_align > payload_align) { + error_index = 0; + payload_index = 1; + error_offset = 0; + payload_offset = std.mem.alignForwardGeneric(u64, error_size, payload_align); + } else { + payload_index = 0; + error_index = 1; + payload_offset = 0; + error_offset = std.mem.alignForwardGeneric(u64, payload_size, error_align); } + var fields: [2]*llvm.DIType = undefined; + fields[error_index] = dib.createMemberType( + fwd_decl.toScope(), + "tag", + di_file, + line, + error_size * 8, // size in bits + error_align * 8, // align in bits + error_offset * 8, // offset in bits + 0, // flags + try o.lowerDebugType(Type.anyerror, .full), + ); + fields[payload_index] = dib.createMemberType( + fwd_decl.toScope(), + "value", + di_file, + line, + payload_size * 8, // size in bits + payload_align * 8, // align in bits + payload_offset * 8, // offset in bits + 0, // flags + try o.lowerDebugType(payload_ty, .full), + ); + const full_di_ty = dib.createStructType( compile_unit_scope, name.ptr, @@ -1562,7 +1566,7 @@ pub const Object = struct { 0, // flags null, // derived from &fields, - len, + fields.len, 0, // run time lang null, // vtable holder "", // unique id @@ -2455,18 +2459,23 @@ pub const DeclGen = struct { return dg.context.structType(&fields, fields.len, .False); }, .ErrorUnion => { - const error_type = t.errorUnionSet(); - const payload_type = t.errorUnionPayload(); - if (error_type.errorSetCardinality() == .zero) { - return dg.lowerType(payload_type); + const payload_ty = t.errorUnionPayload(); + switch (t.errorUnionSet().errorSetCardinality()) { + .zero => return dg.lowerType(payload_ty), + .one => { + if (payload_ty.isNoReturn()) { + return dg.context.voidType(); + } + }, + .many => {}, } - if (!payload_type.hasRuntimeBitsIgnoreComptime()) { + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { return try dg.lowerType(Type.anyerror); } - const llvm_error_type = try dg.lowerType(error_type); - const llvm_payload_type = try dg.lowerType(payload_type); + const llvm_error_type = try dg.lowerType(Type.anyerror); + const llvm_payload_type = try dg.lowerType(payload_ty); - const payload_align = payload_type.abiAlignment(target); + const payload_align = payload_ty.abiAlignment(target); const error_align = Type.anyerror.abiAlignment(target); if (error_align > payload_align) { const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type }; @@ -2476,9 +2485,7 @@ pub const DeclGen = struct { return dg.context.structType(&fields, fields.len, .False); } }, - .ErrorSet => { - return dg.context.intType(16); - }, + .ErrorSet => return dg.context.intType(16), .Struct => { const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .mod = dg.module }); if (gop.found_existing) return gop.value_ptr.*; @@ -3095,7 +3102,7 @@ pub const DeclGen = struct { return dg.resolveLlvmFunction(fn_decl_index); }, .ErrorSet => { - const llvm_ty = try dg.lowerType(tv.ty); + const llvm_ty = try dg.lowerType(Type.anyerror); switch (tv.val.tag()) { .@"error" => { const err_name = tv.val.castTag(.@"error").?.data.name; @@ -3109,9 +3116,8 @@ pub const DeclGen = struct { } }, .ErrorUnion => { - const error_type = tv.ty.errorUnionSet(); const payload_type = tv.ty.errorUnionPayload(); - if (error_type.errorSetCardinality() == .zero) { + if (tv.ty.errorUnionSet().errorSetCardinality() == .zero) { const payload_val = tv.val.castTag(.eu_payload).?.data; return dg.lowerValue(.{ .ty = payload_type, .val = payload_val }); } @@ -3120,13 +3126,13 @@ pub const DeclGen = struct { if (!payload_type.hasRuntimeBitsIgnoreComptime()) { // We use the error type directly as the type. const err_val = if (!is_pl) tv.val else Value.initTag(.zero); - return dg.lowerValue(.{ .ty = error_type, .val = err_val }); + return dg.lowerValue(.{ .ty = Type.anyerror, .val = err_val }); } const payload_align = payload_type.abiAlignment(target); const error_align = Type.anyerror.abiAlignment(target); const llvm_error_value = try dg.lowerValue(.{ - .ty = error_type, + .ty = Type.anyerror, .val = if (is_pl) Value.initTag(.zero) else tv.val, }); const llvm_payload_value = try dg.lowerValue(.{ @@ -5656,13 +5662,12 @@ pub const FuncGen = struct { const operand = try self.resolveInst(ty_op.operand); const error_union_ty = self.air.typeOf(ty_op.operand).childType(); - const error_ty = error_union_ty.errorUnionSet(); - if (error_ty.errorSetCardinality() == .zero) { + if (error_union_ty.errorUnionSet().errorSetCardinality() == .zero) { // TODO: write undefined bytes through the pointer here return operand; } const payload_ty = error_union_ty.errorUnionPayload(); - const non_error_val = try self.dg.lowerValue(.{ .ty = error_ty, .val = Value.zero }); + const non_error_val = try self.dg.lowerValue(.{ .ty = Type.anyerror, .val = Value.zero }); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { _ = self.builder.buildStore(non_error_val, operand); return operand; @@ -6715,9 +6720,9 @@ pub const FuncGen = struct { 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 operand_ty = self.air.typeOf(ty_op.operand); const inst_ty = self.air.typeOfIndex(inst); + const operand = try self.resolveInst(ty_op.operand); const operand_is_ref = isByRef(operand_ty); const result_is_ref = isByRef(inst_ty); const llvm_dest_ty = try self.dg.lowerType(inst_ty); diff --git a/src/type.zig b/src/type.zig index 4325d6d772..145ae4904a 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2416,14 +2416,18 @@ pub const Type = extern union { // This code needs to be kept in sync with the equivalent switch prong // in abiSizeAdvanced. const data = ty.castTag(.error_union).?.data; - if (data.error_set.errorSetCardinality() == .zero) { - return hasRuntimeBitsAdvanced(data.payload, ignore_comptime_only, sema_kit); - } else if (ignore_comptime_only) { - return true; - } else if (sema_kit) |sk| { - return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, ty)); - } else { - return !comptimeOnly(ty); + switch (data.error_set.errorSetCardinality()) { + .zero => return hasRuntimeBitsAdvanced(data.payload, ignore_comptime_only, sema_kit), + .one => return !data.payload.isNoReturn(), + .many => { + if (ignore_comptime_only) { + return true; + } else if (sema_kit) |sk| { + return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, ty)); + } else { + return !comptimeOnly(ty); + } + }, } }, @@ -2970,8 +2974,14 @@ pub const Type = extern union { // This code needs to be kept in sync with the equivalent switch prong // in abiSizeAdvanced. const data = ty.castTag(.error_union).?.data; - if (data.error_set.errorSetCardinality() == .zero) { - return abiAlignmentAdvanced(data.payload, target, strat); + switch (data.error_set.errorSetCardinality()) { + .zero => return abiAlignmentAdvanced(data.payload, target, strat), + .one => { + if (data.payload.isNoReturn()) { + return AbiAlignmentAdvanced{ .scalar = 0 }; + } + }, + .many => {}, } const code_align = abiAlignment(Type.anyerror, target); switch (strat) { @@ -3440,8 +3450,14 @@ pub const Type = extern union { // 1 bit of data which is whether or not the value is an error. // Zig still uses the error code encoding at runtime, even when only 1 bit // would suffice. This prevents coercions from needing to branch. - if (data.error_set.errorSetCardinality() == .zero) { - return abiSizeAdvanced(data.payload, target, strat); + switch (data.error_set.errorSetCardinality()) { + .zero => return abiSizeAdvanced(data.payload, target, strat), + .one => { + if (data.payload.isNoReturn()) { + return AbiSizeAdvanced{ .scalar = 0 }; + } + }, + .many => {}, } const code_size = abiSize(Type.anyerror, target); if (!data.payload.hasRuntimeBits()) { @@ -4843,7 +4859,6 @@ pub const Type = extern union { .optional_single_const_pointer, .enum_literal, .anyerror_void_error_union, - .error_union, .error_set_inferred, .@"opaque", .var_args_param, @@ -4883,6 +4898,30 @@ pub const Type = extern union { } }, + .error_union => { + const error_ty = ty.errorUnionSet(); + switch (error_ty.errorSetCardinality()) { + .zero => { + const payload_ty = ty.errorUnionPayload(); + if (onePossibleValue(payload_ty)) |payload_val| { + _ = payload_val; + return Value.initTag(.the_only_possible_value); + } else { + return null; + } + }, + .one => { + if (ty.errorUnionPayload().isNoReturn()) { + const error_val = onePossibleValue(error_ty).?; + return error_val; + } else { + return null; + } + }, + .many => return null, + } + }, + .error_set_single => return Value.initTag(.the_only_possible_value), .error_set => { const err_set_obj = ty.castTag(.error_set).?.data; diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 4f316aeab2..312ab1524a 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -479,13 +479,40 @@ test "optional error set with only one error is the same size as bool" { test "optional empty error set" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - const T = ?error{}; - var t: T = undefined; - if (t != null) { + comptime try expect(@sizeOf(error{}!void) == @sizeOf(void)); + comptime try expect(@alignOf(error{}!void) == @alignOf(void)); + + var x: ?error{} = undefined; + if (x != null) { @compileError("test failed"); } } +test "empty error set plus zero-bit payload" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + comptime try expect(@sizeOf(error{}!void) == @sizeOf(void)); + comptime try expect(@alignOf(error{}!void) == @alignOf(void)); + + var x: error{}!void = undefined; + if (x) |payload| { + if (payload != {}) { + @compileError("test failed"); + } + } else |_| { + @compileError("test failed"); + } + const S = struct { + fn empty() error{}!void {} + fn inferred() !void { + return empty(); + } + }; + try S.inferred(); +} + test "nested catch" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From a4ff94804cfcdee49fb9c70812c15ff7d2829ee5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 May 2022 18:20:03 -0700 Subject: [PATCH 1626/2031] Sema: additional check for one-possible-value types in analyzeLoad This is needed because pointers to zero-bit types are not necessarily comptime known, but when doing a load, only the element type having one possible value is relevant. --- src/Sema.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index e625539286..cf9b5aa57f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -20950,6 +20950,11 @@ fn analyzeLoad( .Pointer => ptr_ty.childType(), else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)}), }; + + if (try sema.typeHasOnePossibleValue(block, src, elem_ty)) |opv| { + return sema.addConstant(elem_ty, opv); + } + if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { if (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) |elem_val| { return sema.addConstant(elem_ty, elem_val); From 60af42705d62417c73a13481e60b0861423e77fe Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 May 2022 18:21:34 -0700 Subject: [PATCH 1627/2031] mark two behavior tests as passing --- test/behavior/eval.zig | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 3ffa0a3a12..383c32172c 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -646,8 +646,6 @@ pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { } test "comptime function with mutable pointer is not memoized" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - comptime { var x: i32 = 1; const ptr = &x; @@ -662,8 +660,6 @@ fn increment(value: *i32) void { } test "const ptr to comptime mutable data is not memoized" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - comptime { var foo = SingleFieldStruct{ .x = 1 }; try expect(foo.read_x() == 1); From a0775fdaa1e7427dcd6be9b19726d4996344e9fa Mon Sep 17 00:00:00 2001 From: Francesco Alemanno <50984334+francescoalemanno@users.noreply.github.com> Date: Tue, 24 May 2022 15:01:28 +0100 Subject: [PATCH 1628/2031] Add std.rand.RomuTrio Co-authored-by: ominitay <37453713+ominitay@users.noreply.github.com> --- lib/std/rand.zig | 1 + lib/std/rand/RomuTrio.zig | 132 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 lib/std/rand/RomuTrio.zig diff --git a/lib/std/rand.zig b/lib/std/rand.zig index 980722cdff..72537e952f 100644 --- a/lib/std/rand.zig +++ b/lib/std/rand.zig @@ -26,6 +26,7 @@ pub const Pcg = @import("rand/Pcg.zig"); pub const Xoroshiro128 = @import("rand/Xoroshiro128.zig"); pub const Xoshiro256 = @import("rand/Xoshiro256.zig"); pub const Sfc64 = @import("rand/Sfc64.zig"); +pub const RomuTrio = @import("rand/RomuTrio.zig"); pub const Random = struct { ptr: *anyopaque, diff --git a/lib/std/rand/RomuTrio.zig b/lib/std/rand/RomuTrio.zig new file mode 100644 index 0000000000..ff7b4deac1 --- /dev/null +++ b/lib/std/rand/RomuTrio.zig @@ -0,0 +1,132 @@ +// Website: romu-random.org +// Reference paper: http://arxiv.org/abs/2002.11331 +// Beware: this PRNG is trivially predictable. While fast, it should *never* be used for cryptographic purposes. + +const std = @import("std"); +const Random = std.rand.Random; +const math = std.math; +const RomuTrio = @This(); + +x_state: u64, +y_state: u64, +z_state: u64, // set to nonzero seed + +pub fn init(init_s: u64) RomuTrio { + var x = RomuTrio{ .x_state = undefined, .y_state = undefined, .z_state = undefined }; + x.seed(init_s); + return x; +} + +pub fn random(self: *RomuTrio) Random { + return Random.init(self, fill); +} + +fn next(self: *RomuTrio) u64 { + const xp = self.x_state; + const yp = self.y_state; + const zp = self.z_state; + self.x_state = 15241094284759029579 *% zp; + self.y_state = yp -% xp; + self.y_state = std.math.rotl(u64, self.y_state, 12); + self.z_state = zp -% yp; + self.z_state = std.math.rotl(u64, self.z_state, 44); + return xp; +} + +pub fn seedWithBuf(self: *RomuTrio, buf: [24]u8) void { + const seed_buf = @bitCast([3]u64, buf); + self.x_state = seed_buf[0]; + self.y_state = seed_buf[1]; + self.z_state = seed_buf[2]; +} + +pub fn seed(self: *RomuTrio, init_s: u64) void { + // RomuTrio requires 192-bits of seed. + var gen = std.rand.SplitMix64.init(init_s); + + self.x_state = gen.next(); + self.y_state = gen.next(); + self.z_state = gen.next(); +} + +pub fn fill(self: *RomuTrio, buf: []u8) void { + var i: usize = 0; + const aligned_len = buf.len - (buf.len & 7); + + // Complete 8 byte segments. + while (i < aligned_len) : (i += 8) { + var n = self.next(); + comptime var j: usize = 0; + inline while (j < 8) : (j += 1) { + buf[i + j] = @truncate(u8, n); + n >>= 8; + } + } + + // Remaining. (cuts the stream) + if (i != buf.len) { + var n = self.next(); + while (i < buf.len) : (i += 1) { + buf[i] = @truncate(u8, n); + n >>= 8; + } + } +} + +test "RomuTrio sequence" { + // Unfortunately there does not seem to be an official test sequence. + var r = RomuTrio.init(0); + + const seq = [_]u64{ + 16294208416658607535, + 13964609475759908645, + 4703697494102998476, + 3425221541186733346, + 2285772463536419399, + 9454187757529463048, + 13695907680080547496, + 8328236714879408626, + 12323357569716880909, + 12375466223337721820, + }; + + for (seq) |s| { + try std.testing.expectEqual(s, r.next()); + } +} + +test "RomuTrio fill" { + // Unfortunately there does not seem to be an official test sequence. + var r = RomuTrio.init(0); + + const seq = [_]u64{ + 16294208416658607535, + 13964609475759908645, + 4703697494102998476, + 3425221541186733346, + 2285772463536419399, + 9454187757529463048, + 13695907680080547496, + 8328236714879408626, + 12323357569716880909, + 12375466223337721820, + }; + + for (seq) |s| { + var buf0: [8]u8 = undefined; + var buf1: [7]u8 = undefined; + std.mem.writeIntLittle(u64, &buf0, s); + r.fill(&buf1); + try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..])); + } +} + +test "RomuTrio buf seeding test" { + const buf0 = @bitCast([24]u8, [3]u64{ 16294208416658607535, 13964609475759908645, 4703697494102998476 }); + const resulting_state = .{ .x = 16294208416658607535, .y = 13964609475759908645, .z = 4703697494102998476 }; + var r = RomuTrio.init(0); + r.seedWithBuf(buf0); + try std.testing.expect(r.x_state == resulting_state.x); + try std.testing.expect(r.y_state == resulting_state.y); + try std.testing.expect(r.z_state == resulting_state.z); +} From 71e2a56e3ef7aba10cc0648aab786973cf8416bc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 25 May 2022 00:12:56 -0700 Subject: [PATCH 1629/2031] mark some more behavior tests as passing --- test/behavior/align.zig | 11 ++---- test/behavior/bitcast.zig | 7 ---- test/behavior/packed-struct.zig | 61 ++++----------------------------- test/behavior/shuffle.zig | 6 ++-- 4 files changed, 12 insertions(+), 73 deletions(-) diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 5b753aa737..32b0777583 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -435,10 +435,8 @@ var default_aligned_global = DefaultAligned{ }; test "read 128-bit field from default aligned struct in global memory" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; try expect(12 == default_aligned_global.badguy); @@ -465,12 +463,7 @@ test "struct field explicit alignment" { } test "align(@alignOf(T)) T does not force resolution of T" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend != .stage1) return error.SkipZigTest; const S = struct { const A = struct { diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index 4922b9473e..c571851fe4 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -245,13 +245,6 @@ test "comptime bitcast used in expression has the correct type" { } test "bitcast passed as tuple element" { - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - const S = struct { fn foo(args: anytype) !void { comptime try expect(@TypeOf(args[0]) == f32); diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index 44ac21aea9..73c71ddf43 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -5,15 +5,6 @@ const expectEqual = std.testing.expectEqual; const native_endian = builtin.cpu.arch.endian(); test "correct size of packed structs" { - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - const T1 = packed struct { one: u8, three: [3]u8 }; try expectEqual(4, @sizeOf(T1)); @@ -41,14 +32,7 @@ test "correct size of packed structs" { } test "flags in packed structs" { - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.zig_backend != .stage1) return error.SkipZigTest; const Flags1 = packed struct { // byte 0 @@ -134,14 +118,7 @@ test "flags in packed structs" { } test "arrays in packed structs" { - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.zig_backend != .stage1) return error.SkipZigTest; const T1 = packed struct { array: [3][3]u8 }; const T2 = packed struct { array: [9]u8 }; @@ -153,14 +130,7 @@ test "arrays in packed structs" { } test "consistent size of packed structs" { - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.zig_backend != .stage1) return error.SkipZigTest; const TxData1 = packed struct { data: u8, _23: u23, full: bool = false }; const TxData2 = packed struct { data: u9, _22: u22, full: bool = false }; @@ -196,14 +166,7 @@ test "consistent size of packed structs" { } test "correct sizeOf and offsets in packed structs" { - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.zig_backend != .stage1) return error.SkipZigTest; const PStruct = packed struct { bool_a: bool, @@ -274,14 +237,7 @@ test "correct sizeOf and offsets in packed structs" { } test "nested packed structs" { - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.zig_backend != .stage1) return error.SkipZigTest; const S1 = packed struct { a: u8, b: u8, c: u8 }; @@ -324,14 +280,11 @@ test "nested packed structs" { } test "regular in irregular packed struct" { - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const Irregular = packed struct { bar: Regular = Regular{}, diff --git a/test/behavior/shuffle.zig b/test/behavior/shuffle.zig index a7ea280603..6c084b64d8 100644 --- a/test/behavior/shuffle.zig +++ b/test/behavior/shuffle.zig @@ -44,7 +44,7 @@ test "@shuffle int" { comptime try S.doTheTest(); } -test "@shuffle bool" { +test "@shuffle bool 1" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -60,11 +60,11 @@ test "@shuffle bool" { try expect(mem.eql(bool, &@as([4]bool, res), &[4]bool{ false, false, true, false })); } }; - if (builtin.zig_backend == .stage1) try S.doTheTest(); + try S.doTheTest(); comptime try S.doTheTest(); } -test "@shuffle bool" { +test "@shuffle bool 2" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO From 7b3e5ce0b3d3e6c8e24dd147d23eec3fe5088d74 Mon Sep 17 00:00:00 2001 From: Vincent Rischmann Date: Thu, 12 May 2022 13:28:34 +0200 Subject: [PATCH 1630/2031] io_uring: add provide_buffers and remove_buffers These functions are needed to implement automatic buffer selection. This maps to the IORING_OP_PROVIDE_BUFFERS and IORING_OP_PROVIDE_BUFFERS ops. --- lib/std/os/linux/io_uring.zig | 57 +++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index be8b442611..9646a923d7 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -857,6 +857,41 @@ pub const IO_Uring = struct { return sqe; } + /// Queues (but does not submit) an SQE to provide a group of buffers used for commands that read/receive data. + /// Returns a pointer to the SQE. + /// + /// Provided buffers can be used in `read`, `recv` or `recvmsg` commands via .buffer_selection. + /// + /// The kernel expects a contiguous block of memory of size (buffers_count * buffer_size). + pub fn provide_buffers( + self: *IO_Uring, + user_data: u64, + buffers: [*]u8, + buffers_count: usize, + buffer_size: usize, + group_id: usize, + buffer_id: usize, + ) !*io_uring_sqe { + const sqe = try self.get_sqe(); + io_uring_prep_provide_buffers(sqe, buffers, buffers_count, buffer_size, group_id, buffer_id); + sqe.user_data = user_data; + return sqe; + } + + /// Queues (but does not submit) an SQE to remove a group of provided buffers. + /// Returns a pointer to the SQE. + pub fn remove_buffers( + self: *IO_Uring, + user_data: u64, + buffers_count: usize, + group_id: usize, + ) !*io_uring_sqe { + const sqe = try self.get_sqe(); + io_uring_prep_remove_buffers(sqe, buffers_count, group_id); + sqe.user_data = user_data; + return sqe; + } + /// Registers an array of file descriptors. /// Every time a file descriptor is put in an SQE and submitted to the kernel, the kernel must /// retrieve a reference to the file, and once I/O has completed the file reference must be @@ -1508,6 +1543,28 @@ pub fn io_uring_prep_linkat( sqe.rw_flags = flags; } +pub fn io_uring_prep_provide_buffers( + sqe: *io_uring_sqe, + buffers: [*]u8, + num: usize, + buffer_len: usize, + group_id: usize, + buffer_id: usize, +) void { + const ptr = @ptrToInt(buffers); + io_uring_prep_rw(.PROVIDE_BUFFERS, sqe, @intCast(i32, num), ptr, buffer_len, buffer_id); + sqe.buf_index = @intCast(u16, group_id); +} + +pub fn io_uring_prep_remove_buffers( + sqe: *io_uring_sqe, + num: usize, + group_id: usize, +) void { + io_uring_prep_rw(.REMOVE_BUFFERS, sqe, @intCast(i32, num), 0, 0, 0); + sqe.buf_index = @intCast(u16, group_id); +} + test "structs/offsets/entries" { if (builtin.os.tag != .linux) return error.SkipZigTest; From 52dd468cc3e279e7d1a764c2c3f6fde13a404a14 Mon Sep 17 00:00:00 2001 From: Vincent Rischmann Date: Mon, 16 May 2022 19:02:22 +0200 Subject: [PATCH 1631/2031] io_uring: change read() to use a ReadBuffer instead Reads can be done in two ways with io_uring: * using a simple buffer * using a automatic buffer selection which requires the user to have provided a number of buffers before ReadBuffer let's the caller choose where the data should be read. --- lib/std/os/linux/io_uring.zig | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index 9646a923d7..74e6416102 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -358,17 +358,38 @@ pub const IO_Uring = struct { return sqe; } + /// Used to select how the read should be handled. + pub const ReadBuffer = union(enum) { + /// io_uring will read directly into this buffer + buffer: []u8, + + /// io_uring will select a buffer that has previously been provided with `provide_buffers`. + /// The buffer group reference by `group_id` must contain at least one buffer for the read to work. + /// `len` controls the number of bytes to read into the selected buffer. + buffer_selection: struct { + group_id: u16, + len: usize, + }, + }; + /// Queues (but does not submit) an SQE to perform a `read(2)`. /// Returns a pointer to the SQE. pub fn read( self: *IO_Uring, user_data: u64, fd: os.fd_t, - buffer: []u8, + buffer: ReadBuffer, offset: u64, ) !*io_uring_sqe { const sqe = try self.get_sqe(); - io_uring_prep_read(sqe, fd, buffer, offset); + switch (buffer) { + .buffer => |slice| io_uring_prep_read(sqe, fd, slice, offset), + .buffer_selection => |selection| { + io_uring_prep_rw(.READ, sqe, fd, 0, selection.len, offset); + sqe.flags |= linux.IOSQE_BUFFER_SELECT; + sqe.buf_index = selection.group_id; + }, + } sqe.user_data = user_data; return sqe; } @@ -1778,7 +1799,7 @@ test "write/read" { try testing.expectEqual(linux.IORING_OP.WRITE, sqe_write.opcode); try testing.expectEqual(@as(u64, 10), sqe_write.off); sqe_write.flags |= linux.IOSQE_IO_LINK; - const sqe_read = try ring.read(0x22222222, fd, buffer_read[0..], 10); + const sqe_read = try ring.read(0x22222222, fd, .{ .buffer = buffer_read[0..] }, 10); try testing.expectEqual(linux.IORING_OP.READ, sqe_read.opcode); try testing.expectEqual(@as(u64, 10), sqe_read.off); try testing.expectEqual(@as(u32, 2), try ring.submit()); @@ -2520,7 +2541,7 @@ test "register_files_update" { var buffer = [_]u8{42} ** 128; { - const sqe = try ring.read(0xcccccccc, fd_index, &buffer, 0); + const sqe = try ring.read(0xcccccccc, fd_index, .{ .buffer = &buffer }, 0); try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode); sqe.flags |= linux.IOSQE_FIXED_FILE; @@ -2541,7 +2562,7 @@ test "register_files_update" { { // Next read should still work since fd_index in the registered file descriptors hasn't been updated yet. - const sqe = try ring.read(0xcccccccc, fd_index, &buffer, 0); + const sqe = try ring.read(0xcccccccc, fd_index, .{ .buffer = &buffer }, 0); try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode); sqe.flags |= linux.IOSQE_FIXED_FILE; @@ -2558,7 +2579,7 @@ test "register_files_update" { { // Now this should fail since both fds are sparse (-1) - const sqe = try ring.read(0xcccccccc, fd_index, &buffer, 0); + const sqe = try ring.read(0xcccccccc, fd_index, .{ .buffer = &buffer }, 0); try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode); sqe.flags |= linux.IOSQE_FIXED_FILE; From 270a5039d4bc09bb5fbe017334d5d9ba6c4ac584 Mon Sep 17 00:00:00 2001 From: Vincent Rischmann Date: Mon, 16 May 2022 19:48:55 +0200 Subject: [PATCH 1632/2031] io_uring: change recv() to use a RecvBuffer instead RecvBuffer is equivalent to ReadBuffer but tailored for recv only. --- lib/std/os/linux/io_uring.zig | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index 74e6416102..ad78f7bfab 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -528,17 +528,39 @@ pub const IO_Uring = struct { return sqe; } + /// Used to select how the recv call should be handled. + pub const RecvBuffer = union(enum) { + /// io_uring will recv directly into this buffer + buffer: []u8, + + /// io_uring will select a buffer that has previously been provided with `provide_buffers`. + /// The buffer group referenced by `group_id` must contain at least one buffer for the recv call to work. + /// `len` controls the number of bytes to read into the selected buffer. + buffer_selection: struct { + group_id: u16, + len: usize, + }, + }; + /// Queues (but does not submit) an SQE to perform a `recv(2)`. /// Returns a pointer to the SQE. pub fn recv( self: *IO_Uring, user_data: u64, fd: os.fd_t, - buffer: []u8, + buffer: RecvBuffer, flags: u32, ) !*io_uring_sqe { const sqe = try self.get_sqe(); - io_uring_prep_recv(sqe, fd, buffer, flags); + switch (buffer) { + .buffer => |slice| io_uring_prep_recv(sqe, fd, slice, flags), + .buffer_selection => |selection| { + io_uring_prep_rw(.RECV, sqe, fd, 0, selection.len, 0); + sqe.rw_flags = flags; + sqe.flags |= linux.IOSQE_BUFFER_SELECT; + sqe.buf_index = selection.group_id; + }, + } sqe.user_data = user_data; return sqe; } @@ -2014,7 +2036,7 @@ test "accept/connect/send/recv" { const send = try ring.send(0xeeeeeeee, client, buffer_send[0..], 0); send.flags |= linux.IOSQE_IO_LINK; - _ = try ring.recv(0xffffffff, cqe_accept.res, buffer_recv[0..], 0); + _ = try ring.recv(0xffffffff, cqe_accept.res, .{ .buffer = buffer_recv[0..] }, 0); try testing.expectEqual(@as(u32, 2), try ring.submit()); const cqe_send = try ring.copy_cqe(); @@ -2282,7 +2304,7 @@ test "accept/connect/recv/link_timeout" { .flags = 0, }, cqe_connect); - const sqe_recv = try ring.recv(0xffffffff, cqe_accept.res, buffer_recv[0..], 0); + const sqe_recv = try ring.recv(0xffffffff, cqe_accept.res, .{ .buffer = buffer_recv[0..] }, 0); sqe_recv.flags |= linux.IOSQE_IO_LINK; const ts = os.linux.kernel_timespec{ .tv_sec = 0, .tv_nsec = 1000000 }; @@ -2469,7 +2491,7 @@ test "accept/connect/recv/cancel" { .flags = 0, }, cqe_connect); - _ = try ring.recv(0xffffffff, cqe_accept.res, buffer_recv[0..], 0); + _ = try ring.recv(0xffffffff, cqe_accept.res, .{ .buffer = buffer_recv[0..] }, 0); try testing.expectEqual(@as(u32, 1), try ring.submit()); const sqe_cancel = try ring.cancel(0x99999999, 0xffffffff, 0); From 456716b30dffc343df0bed9a69cbce54d10787aa Mon Sep 17 00:00:00 2001 From: Vincent Rischmann Date: Mon, 16 May 2022 21:54:14 +0200 Subject: [PATCH 1633/2031] io_uring: add a test harness for server/client TCP socket tests --- lib/std/os/linux/io_uring.zig | 68 +++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index ad78f7bfab..ef669d2369 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -2943,3 +2943,71 @@ test "linkat" { const read = try second_file.readAll(&second_file_data); try testing.expectEqualStrings("hello", second_file_data[0..read]); } + +/// Used for testing server/client interactions. +const SocketTestHarness = struct { + listener: os.socket_t, + server: os.socket_t, + client: os.socket_t, + + fn close(self: SocketTestHarness) void { + os.closeSocket(self.client); + os.closeSocket(self.listener); + } +}; + +fn createSocketTestHarness(ring: *IO_Uring) !SocketTestHarness { + // Create a TCP server socket + + const address = try net.Address.parseIp4("127.0.0.1", 3131); + const kernel_backlog = 1; + const listener_socket = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); + errdefer os.closeSocket(listener_socket); + + try os.setsockopt(listener_socket, os.SOL.SOCKET, os.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1))); + try os.bind(listener_socket, &address.any, address.getOsSockLen()); + try os.listen(listener_socket, kernel_backlog); + + // Submit 1 accept + var accept_addr: os.sockaddr = undefined; + var accept_addr_len: os.socklen_t = @sizeOf(@TypeOf(accept_addr)); + _ = try ring.accept(0xaaaaaaaa, listener_socket, &accept_addr, &accept_addr_len, 0); + + // Create a TCP client socket + const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); + errdefer os.closeSocket(client); + _ = try ring.connect(0xcccccccc, client, &address.any, address.getOsSockLen()); + + try testing.expectEqual(@as(u32, 2), try ring.submit()); + + var cqe_accept = try ring.copy_cqe(); + if (cqe_accept.err() == .INVAL) return error.SkipZigTest; + var cqe_connect = try ring.copy_cqe(); + if (cqe_connect.err() == .INVAL) return error.SkipZigTest; + + // The accept/connect CQEs may arrive in any order, the connect CQE will sometimes come first: + if (cqe_accept.user_data == 0xcccccccc and cqe_connect.user_data == 0xaaaaaaaa) { + const a = cqe_accept; + const b = cqe_connect; + cqe_accept = b; + cqe_connect = a; + } + + try testing.expectEqual(@as(u64, 0xaaaaaaaa), cqe_accept.user_data); + if (cqe_accept.res <= 0) std.debug.print("\ncqe_accept.res={}\n", .{cqe_accept.res}); + try testing.expect(cqe_accept.res > 0); + try testing.expectEqual(@as(u32, 0), cqe_accept.flags); + try testing.expectEqual(linux.io_uring_cqe{ + .user_data = 0xcccccccc, + .res = 0, + .flags = 0, + }, cqe_connect); + + // All good + + return SocketTestHarness{ + .listener = listener_socket, + .server = cqe_accept.res, + .client = client, + }; +} From d8798ef0cd22532d26157aa1939b96aabc02223c Mon Sep 17 00:00:00 2001 From: Vincent Rischmann Date: Mon, 16 May 2022 21:45:56 +0200 Subject: [PATCH 1634/2031] io_uring: use the socket test harness --- lib/std/os/linux/io_uring.zig | 134 +++------------------------------- 1 file changed, 10 insertions(+), 124 deletions(-) diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index ef669d2369..bb48c63719 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -1990,53 +1990,15 @@ test "accept/connect/send/recv" { }; defer ring.deinit(); - const address = try net.Address.parseIp4("127.0.0.1", 3131); - const kernel_backlog = 1; - const server = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); - defer os.close(server); - try os.setsockopt(server, os.SOL.SOCKET, os.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1))); - try os.bind(server, &address.any, address.getOsSockLen()); - try os.listen(server, kernel_backlog); + const socket_test_harness = try createSocketTestHarness(&ring); + defer socket_test_harness.close(); const buffer_send = [_]u8{ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 }; var buffer_recv = [_]u8{ 0, 1, 0, 1, 0 }; - var accept_addr: os.sockaddr = undefined; - var accept_addr_len: os.socklen_t = @sizeOf(@TypeOf(accept_addr)); - _ = try ring.accept(0xaaaaaaaa, server, &accept_addr, &accept_addr_len, 0); - try testing.expectEqual(@as(u32, 1), try ring.submit()); - - const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); - defer os.close(client); - _ = try ring.connect(0xcccccccc, client, &address.any, address.getOsSockLen()); - try testing.expectEqual(@as(u32, 1), try ring.submit()); - - var cqe_accept = try ring.copy_cqe(); - if (cqe_accept.err() == .INVAL) return error.SkipZigTest; - var cqe_connect = try ring.copy_cqe(); - if (cqe_connect.err() == .INVAL) return error.SkipZigTest; - - // The accept/connect CQEs may arrive in any order, the connect CQE will sometimes come first: - if (cqe_accept.user_data == 0xcccccccc and cqe_connect.user_data == 0xaaaaaaaa) { - const a = cqe_accept; - const b = cqe_connect; - cqe_accept = b; - cqe_connect = a; - } - - try testing.expectEqual(@as(u64, 0xaaaaaaaa), cqe_accept.user_data); - if (cqe_accept.res <= 0) std.debug.print("\ncqe_accept.res={}\n", .{cqe_accept.res}); - try testing.expect(cqe_accept.res > 0); - try testing.expectEqual(@as(u32, 0), cqe_accept.flags); - try testing.expectEqual(linux.io_uring_cqe{ - .user_data = 0xcccccccc, - .res = 0, - .flags = 0, - }, cqe_connect); - - const send = try ring.send(0xeeeeeeee, client, buffer_send[0..], 0); + const send = try ring.send(0xeeeeeeee, socket_test_harness.client, buffer_send[0..], 0); send.flags |= linux.IOSQE_IO_LINK; - _ = try ring.recv(0xffffffff, cqe_accept.res, .{ .buffer = buffer_recv[0..] }, 0); + _ = try ring.recv(0xffffffff, socket_test_harness.server, .{ .buffer = buffer_recv[0..] }, 0); try testing.expectEqual(@as(u32, 2), try ring.submit()); const cqe_send = try ring.copy_cqe(); @@ -2261,50 +2223,12 @@ test "accept/connect/recv/link_timeout" { }; defer ring.deinit(); - const address = try net.Address.parseIp4("127.0.0.1", 3131); - const kernel_backlog = 1; - const server = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); - defer os.close(server); - try os.setsockopt(server, os.SOL.SOCKET, os.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1))); - try os.bind(server, &address.any, address.getOsSockLen()); - try os.listen(server, kernel_backlog); + const socket_test_harness = try createSocketTestHarness(&ring); + defer socket_test_harness.close(); var buffer_recv = [_]u8{ 0, 1, 0, 1, 0 }; - var accept_addr: os.sockaddr = undefined; - var accept_addr_len: os.socklen_t = @sizeOf(@TypeOf(accept_addr)); - _ = try ring.accept(0xaaaaaaaa, server, &accept_addr, &accept_addr_len, 0); - try testing.expectEqual(@as(u32, 1), try ring.submit()); - - const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); - defer os.close(client); - _ = try ring.connect(0xcccccccc, client, &address.any, address.getOsSockLen()); - try testing.expectEqual(@as(u32, 1), try ring.submit()); - - var cqe_accept = try ring.copy_cqe(); - if (cqe_accept.err() == .INVAL) return error.SkipZigTest; - var cqe_connect = try ring.copy_cqe(); - if (cqe_connect.err() == .INVAL) return error.SkipZigTest; - - // The accept/connect CQEs may arrive in any order, the connect CQE will sometimes come first: - if (cqe_accept.user_data == 0xcccccccc and cqe_connect.user_data == 0xaaaaaaaa) { - const a = cqe_accept; - const b = cqe_connect; - cqe_accept = b; - cqe_connect = a; - } - - try testing.expectEqual(@as(u64, 0xaaaaaaaa), cqe_accept.user_data); - if (cqe_accept.res <= 0) std.debug.print("\ncqe_accept.res={}\n", .{cqe_accept.res}); - try testing.expect(cqe_accept.res > 0); - try testing.expectEqual(@as(u32, 0), cqe_accept.flags); - try testing.expectEqual(linux.io_uring_cqe{ - .user_data = 0xcccccccc, - .res = 0, - .flags = 0, - }, cqe_connect); - - const sqe_recv = try ring.recv(0xffffffff, cqe_accept.res, .{ .buffer = buffer_recv[0..] }, 0); + const sqe_recv = try ring.recv(0xffffffff, socket_test_harness.server, .{ .buffer = buffer_recv[0..] }, 0); sqe_recv.flags |= linux.IOSQE_IO_LINK; const ts = os.linux.kernel_timespec{ .tv_sec = 0, .tv_nsec = 1000000 }; @@ -2448,50 +2372,12 @@ test "accept/connect/recv/cancel" { }; defer ring.deinit(); - const address = try net.Address.parseIp4("127.0.0.1", 3131); - const kernel_backlog = 1; - const server = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); - defer os.close(server); - try os.setsockopt(server, os.SOL.SOCKET, os.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1))); - try os.bind(server, &address.any, address.getOsSockLen()); - try os.listen(server, kernel_backlog); + const socket_test_harness = try createSocketTestHarness(&ring); + defer socket_test_harness.close(); var buffer_recv = [_]u8{ 0, 1, 0, 1, 0 }; - var accept_addr: os.sockaddr = undefined; - var accept_addr_len: os.socklen_t = @sizeOf(@TypeOf(accept_addr)); - _ = try ring.accept(0xaaaaaaaa, server, &accept_addr, &accept_addr_len, 0); - try testing.expectEqual(@as(u32, 1), try ring.submit()); - - const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); - defer os.close(client); - _ = try ring.connect(0xcccccccc, client, &address.any, address.getOsSockLen()); - try testing.expectEqual(@as(u32, 1), try ring.submit()); - - var cqe_accept = try ring.copy_cqe(); - if (cqe_accept.err() == .INVAL) return error.SkipZigTest; - var cqe_connect = try ring.copy_cqe(); - if (cqe_connect.err() == .INVAL) return error.SkipZigTest; - - // The accept/connect CQEs may arrive in any order, the connect CQE will sometimes come first: - if (cqe_accept.user_data == 0xcccccccc and cqe_connect.user_data == 0xaaaaaaaa) { - const a = cqe_accept; - const b = cqe_connect; - cqe_accept = b; - cqe_connect = a; - } - - try testing.expectEqual(@as(u64, 0xaaaaaaaa), cqe_accept.user_data); - if (cqe_accept.res <= 0) std.debug.print("\ncqe_accept.res={}\n", .{cqe_accept.res}); - try testing.expect(cqe_accept.res > 0); - try testing.expectEqual(@as(u32, 0), cqe_accept.flags); - try testing.expectEqual(linux.io_uring_cqe{ - .user_data = 0xcccccccc, - .res = 0, - .flags = 0, - }, cqe_connect); - - _ = try ring.recv(0xffffffff, cqe_accept.res, .{ .buffer = buffer_recv[0..] }, 0); + _ = try ring.recv(0xffffffff, socket_test_harness.server, .{ .buffer = buffer_recv[0..] }, 0); try testing.expectEqual(@as(u32, 1), try ring.submit()); const sqe_cancel = try ring.cancel(0x99999999, 0xffffffff, 0); From acb8af468f496dadbc7550196d7ba05fb8efd586 Mon Sep 17 00:00:00 2001 From: Vincent Rischmann Date: Mon, 16 May 2022 21:54:29 +0200 Subject: [PATCH 1635/2031] io_uring: add tests for automatic buffer selection --- lib/std/os/linux/io_uring.zig | 384 ++++++++++++++++++++++++++++++++++ 1 file changed, 384 insertions(+) diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index bb48c63719..a62362c963 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -2830,6 +2830,390 @@ test "linkat" { try testing.expectEqualStrings("hello", second_file_data[0..read]); } +test "provide_buffers: read" { + if (builtin.os.tag != .linux) return error.SkipZigTest; + + var ring = IO_Uring.init(1, 0) catch |err| switch (err) { + error.SystemOutdated => return error.SkipZigTest, + error.PermissionDenied => return error.SkipZigTest, + else => return err, + }; + defer ring.deinit(); + + const fd = try os.openZ("/dev/zero", os.O.RDONLY | os.O.CLOEXEC, 0); + defer os.close(fd); + + const group_id = 1337; + const buffer_id = 0; + + const buffer_len = 128; + + var buffers: [4][buffer_len]u8 = undefined; + + // Provide 4 buffers + + { + const sqe = try ring.provide_buffers(0xcccccccc, @ptrCast([*]u8, &buffers), buffers.len, buffer_len, group_id, buffer_id); + try testing.expectEqual(linux.IORING_OP.PROVIDE_BUFFERS, sqe.opcode); + try testing.expectEqual(@as(i32, buffers.len), sqe.fd); + try testing.expectEqual(@as(u32, buffers[0].len), sqe.len); + try testing.expectEqual(@as(u16, group_id), sqe.buf_index); + try testing.expectEqual(@as(u32, 1), try ring.submit()); + + const cqe = try ring.copy_cqe(); + switch (cqe.err()) { + // Happens when the kernel is < 5.7 + .INVAL => return error.SkipZigTest, + .SUCCESS => {}, + else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), + } + try testing.expectEqual(@as(u64, 0xcccccccc), cqe.user_data); + } + + // Do 4 reads which should consume all buffers + + var i: usize = 0; + while (i < buffers.len) : (i += 1) { + var sqe = try ring.read(0xdededede, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); + try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode); + try testing.expectEqual(@as(i32, fd), sqe.fd); + try testing.expectEqual(@as(u64, 0), sqe.addr); + try testing.expectEqual(@as(u32, buffer_len), sqe.len); + try testing.expectEqual(@as(u16, group_id), sqe.buf_index); + try testing.expectEqual(@as(u32, 1), try ring.submit()); + + const cqe = try ring.copy_cqe(); + switch (cqe.err()) { + .SUCCESS => {}, + else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), + } + + try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == linux.IORING_CQE_F_BUFFER); + const used_buffer_id = cqe.flags >> 16; + try testing.expect(used_buffer_id >= 0 and used_buffer_id <= 3); + try testing.expectEqual(@as(i32, buffer_len), cqe.res); + + try testing.expectEqual(@as(u64, 0xdededede), cqe.user_data); + try testing.expectEqualSlices(u8, &([_]u8{0} ** buffer_len), buffers[used_buffer_id][0..@intCast(usize, cqe.res)]); + } + + // This read should fail + + { + var sqe = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); + try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode); + try testing.expectEqual(@as(i32, fd), sqe.fd); + try testing.expectEqual(@as(u64, 0), sqe.addr); + try testing.expectEqual(@as(u32, buffer_len), sqe.len); + try testing.expectEqual(@as(u16, group_id), sqe.buf_index); + try testing.expectEqual(@as(u32, 1), try ring.submit()); + + const cqe = try ring.copy_cqe(); + switch (cqe.err()) { + // Expected + .NOBUFS => {}, + .SUCCESS => std.debug.panic("unexpected success", .{}), + else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), + } + try testing.expectEqual(@as(u64, 0xdfdfdfdf), cqe.user_data); + } + + // Provide 1 buffer again + + // Deliberately put something we don't expect in the buffers + mem.set(u8, mem.sliceAsBytes(&buffers), 42); + + const reprovided_buffer_id = 2; + + { + _ = try ring.provide_buffers(0xabababab, @ptrCast([*]u8, &buffers[reprovided_buffer_id]), 1, buffer_len, group_id, reprovided_buffer_id); + try testing.expectEqual(@as(u32, 1), try ring.submit()); + + const cqe = try ring.copy_cqe(); + switch (cqe.err()) { + .SUCCESS => {}, + else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), + } + } + + // Final read which should work + + { + var sqe = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); + try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode); + try testing.expectEqual(@as(i32, fd), sqe.fd); + try testing.expectEqual(@as(u64, 0), sqe.addr); + try testing.expectEqual(@as(u32, buffer_len), sqe.len); + try testing.expectEqual(@as(u16, group_id), sqe.buf_index); + try testing.expectEqual(@as(u32, 1), try ring.submit()); + + const cqe = try ring.copy_cqe(); + switch (cqe.err()) { + .SUCCESS => {}, + else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), + } + + try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == linux.IORING_CQE_F_BUFFER); + const used_buffer_id = cqe.flags >> 16; + try testing.expectEqual(used_buffer_id, reprovided_buffer_id); + try testing.expectEqual(@as(i32, buffer_len), cqe.res); + try testing.expectEqual(@as(u64, 0xdfdfdfdf), cqe.user_data); + try testing.expectEqualSlices(u8, &([_]u8{0} ** buffer_len), buffers[used_buffer_id][0..@intCast(usize, cqe.res)]); + } +} + +test "remove_buffers" { + if (builtin.os.tag != .linux) return error.SkipZigTest; + + var ring = IO_Uring.init(1, 0) catch |err| switch (err) { + error.SystemOutdated => return error.SkipZigTest, + error.PermissionDenied => return error.SkipZigTest, + else => return err, + }; + defer ring.deinit(); + + const fd = try os.openZ("/dev/zero", os.O.RDONLY | os.O.CLOEXEC, 0); + defer os.close(fd); + + const group_id = 1337; + const buffer_id = 0; + + const buffer_len = 128; + + var buffers: [4][buffer_len]u8 = undefined; + + // Provide 4 buffers + + { + _ = try ring.provide_buffers(0xcccccccc, @ptrCast([*]u8, &buffers), buffers.len, buffer_len, group_id, buffer_id); + try testing.expectEqual(@as(u32, 1), try ring.submit()); + + const cqe = try ring.copy_cqe(); + switch (cqe.err()) { + .SUCCESS => {}, + else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), + } + try testing.expectEqual(@as(u64, 0xcccccccc), cqe.user_data); + } + + // Remove the first 3 buffers + + { + var sqe = try ring.remove_buffers(0xbababababa, 3, group_id); + try testing.expectEqual(linux.IORING_OP.REMOVE_BUFFERS, sqe.opcode); + try testing.expectEqual(@as(i32, 3), sqe.fd); + try testing.expectEqual(@as(u64, 0), sqe.addr); + try testing.expectEqual(@as(u16, group_id), sqe.buf_index); + try testing.expectEqual(@as(u32, 1), try ring.submit()); + + const cqe = try ring.copy_cqe(); + switch (cqe.err()) { + .SUCCESS => {}, + else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), + } + try testing.expectEqual(@as(u64, 0xbababababa), cqe.user_data); + } + + // This read should work + + { + _ = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); + try testing.expectEqual(@as(u32, 1), try ring.submit()); + + const cqe = try ring.copy_cqe(); + switch (cqe.err()) { + .SUCCESS => {}, + else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), + } + + try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == linux.IORING_CQE_F_BUFFER); + const used_buffer_id = cqe.flags >> 16; + try testing.expectEqual(used_buffer_id, 0); + try testing.expectEqual(@as(i32, buffer_len), cqe.res); + try testing.expectEqual(@as(u64, 0xdfdfdfdf), cqe.user_data); + try testing.expectEqualSlices(u8, &([_]u8{0} ** buffer_len), buffers[used_buffer_id][0..@intCast(usize, cqe.res)]); + } + + // Final read should _not_ work + + { + _ = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); + try testing.expectEqual(@as(u32, 1), try ring.submit()); + + const cqe = try ring.copy_cqe(); + switch (cqe.err()) { + // Expected + .NOBUFS => {}, + .SUCCESS => std.debug.panic("unexpected success", .{}), + else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), + } + } +} + +test "provide_buffers: accept/connect/send/recv" { + if (builtin.os.tag != .linux) return error.SkipZigTest; + + var ring = IO_Uring.init(16, 0) catch |err| switch (err) { + error.SystemOutdated => return error.SkipZigTest, + error.PermissionDenied => return error.SkipZigTest, + else => return err, + }; + defer ring.deinit(); + + const group_id = 1337; + const buffer_id = 0; + + const buffer_len = 128; + var buffers: [4][buffer_len]u8 = undefined; + + // Provide 4 buffers + + { + const sqe = try ring.provide_buffers(0xcccccccc, @ptrCast([*]u8, &buffers), buffers.len, buffer_len, group_id, buffer_id); + try testing.expectEqual(linux.IORING_OP.PROVIDE_BUFFERS, sqe.opcode); + try testing.expectEqual(@as(i32, buffers.len), sqe.fd); + try testing.expectEqual(@as(u32, buffer_len), sqe.len); + try testing.expectEqual(@as(u16, group_id), sqe.buf_index); + try testing.expectEqual(@as(u32, 1), try ring.submit()); + + const cqe = try ring.copy_cqe(); + switch (cqe.err()) { + // Happens when the kernel is < 5.7 + .INVAL => return error.SkipZigTest, + .SUCCESS => {}, + else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), + } + try testing.expectEqual(@as(u64, 0xcccccccc), cqe.user_data); + } + + const socket_test_harness = try createSocketTestHarness(&ring); + defer socket_test_harness.close(); + + // Do 4 send on the socket + + { + var i: usize = 0; + while (i < buffers.len) : (i += 1) { + _ = try ring.send(0xdeaddead, socket_test_harness.server, &([_]u8{'z'} ** buffer_len), 0); + try testing.expectEqual(@as(u32, 1), try ring.submit()); + } + + var cqes: [4]linux.io_uring_cqe = undefined; + try testing.expectEqual(@as(u32, 4), try ring.copy_cqes(&cqes, 4)); + } + + // Do 4 recv which should consume all buffers + + // Deliberately put something we don't expect in the buffers + mem.set(u8, mem.sliceAsBytes(&buffers), 1); + + var i: usize = 0; + while (i < buffers.len) : (i += 1) { + var sqe = try ring.recv(0xdededede, socket_test_harness.client, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); + try testing.expectEqual(linux.IORING_OP.RECV, sqe.opcode); + try testing.expectEqual(@as(i32, socket_test_harness.client), sqe.fd); + try testing.expectEqual(@as(u64, 0), sqe.addr); + try testing.expectEqual(@as(u32, buffer_len), sqe.len); + try testing.expectEqual(@as(u16, group_id), sqe.buf_index); + try testing.expectEqual(@as(u32, 0), sqe.rw_flags); + try testing.expectEqual(@as(u32, linux.IOSQE_BUFFER_SELECT), sqe.flags); + try testing.expectEqual(@as(u32, 1), try ring.submit()); + + const cqe = try ring.copy_cqe(); + switch (cqe.err()) { + .SUCCESS => {}, + else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), + } + + try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == linux.IORING_CQE_F_BUFFER); + const used_buffer_id = cqe.flags >> 16; + try testing.expect(used_buffer_id >= 0 and used_buffer_id <= 3); + try testing.expectEqual(@as(i32, buffer_len), cqe.res); + + try testing.expectEqual(@as(u64, 0xdededede), cqe.user_data); + const buffer = buffers[used_buffer_id][0..@intCast(usize, cqe.res)]; + try testing.expectEqualSlices(u8, &([_]u8{'z'} ** buffer_len), buffer); + } + + // This recv should fail + + { + var sqe = try ring.recv(0xdfdfdfdf, socket_test_harness.client, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); + try testing.expectEqual(linux.IORING_OP.RECV, sqe.opcode); + try testing.expectEqual(@as(i32, socket_test_harness.client), sqe.fd); + try testing.expectEqual(@as(u64, 0), sqe.addr); + try testing.expectEqual(@as(u32, buffer_len), sqe.len); + try testing.expectEqual(@as(u16, group_id), sqe.buf_index); + try testing.expectEqual(@as(u32, 0), sqe.rw_flags); + try testing.expectEqual(@as(u32, linux.IOSQE_BUFFER_SELECT), sqe.flags); + try testing.expectEqual(@as(u32, 1), try ring.submit()); + + const cqe = try ring.copy_cqe(); + switch (cqe.err()) { + // Expected + .NOBUFS => {}, + .SUCCESS => std.debug.panic("unexpected success", .{}), + else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), + } + try testing.expectEqual(@as(u64, 0xdfdfdfdf), cqe.user_data); + } + + // Provide 1 buffer again + + const reprovided_buffer_id = 2; + + { + _ = try ring.provide_buffers(0xabababab, @ptrCast([*]u8, &buffers[reprovided_buffer_id]), 1, buffer_len, group_id, reprovided_buffer_id); + try testing.expectEqual(@as(u32, 1), try ring.submit()); + + const cqe = try ring.copy_cqe(); + switch (cqe.err()) { + .SUCCESS => {}, + else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), + } + } + + // Redo 1 send on the server socket + + { + _ = try ring.send(0xdeaddead, socket_test_harness.server, &([_]u8{'w'} ** buffer_len), 0); + try testing.expectEqual(@as(u32, 1), try ring.submit()); + + _ = try ring.copy_cqe(); + } + + // Final recv which should work + + // Deliberately put something we don't expect in the buffers + mem.set(u8, mem.sliceAsBytes(&buffers), 1); + + { + var sqe = try ring.recv(0xdfdfdfdf, socket_test_harness.client, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); + try testing.expectEqual(linux.IORING_OP.RECV, sqe.opcode); + try testing.expectEqual(@as(i32, socket_test_harness.client), sqe.fd); + try testing.expectEqual(@as(u64, 0), sqe.addr); + try testing.expectEqual(@as(u32, buffer_len), sqe.len); + try testing.expectEqual(@as(u16, group_id), sqe.buf_index); + try testing.expectEqual(@as(u32, 0), sqe.rw_flags); + try testing.expectEqual(@as(u32, linux.IOSQE_BUFFER_SELECT), sqe.flags); + try testing.expectEqual(@as(u32, 1), try ring.submit()); + + const cqe = try ring.copy_cqe(); + switch (cqe.err()) { + .SUCCESS => {}, + else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), + } + + try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == linux.IORING_CQE_F_BUFFER); + const used_buffer_id = cqe.flags >> 16; + try testing.expectEqual(used_buffer_id, reprovided_buffer_id); + try testing.expectEqual(@as(i32, buffer_len), cqe.res); + try testing.expectEqual(@as(u64, 0xdfdfdfdf), cqe.user_data); + const buffer = buffers[used_buffer_id][0..@intCast(usize, cqe.res)]; + try testing.expectEqualSlices(u8, &([_]u8{'w'} ** buffer_len), buffer); + } +} + /// Used for testing server/client interactions. const SocketTestHarness = struct { listener: os.socket_t, From 3c58d3e281fca421879fe1d2ae7943bd31dccc0e Mon Sep 17 00:00:00 2001 From: Vincent Rischmann Date: Fri, 20 May 2022 20:41:56 +0200 Subject: [PATCH 1636/2031] io_uring: replace the readv method with a read on a new ReadBuffer type readv() is essentially identical to read() except for the buffer type, this simplifies the API for the caller at the cost of not clearly mapping to the liburing C API. --- lib/std/os/linux/io_uring.zig | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index a62362c963..31c416c8a1 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -363,6 +363,9 @@ pub const IO_Uring = struct { /// io_uring will read directly into this buffer buffer: []u8, + /// io_uring will read directly into these buffers using readv. + iovecs: []const os.iovec, + /// io_uring will select a buffer that has previously been provided with `provide_buffers`. /// The buffer group reference by `group_id` must contain at least one buffer for the read to work. /// `len` controls the number of bytes to read into the selected buffer. @@ -372,7 +375,11 @@ pub const IO_Uring = struct { }, }; - /// Queues (but does not submit) an SQE to perform a `read(2)`. + /// Queues (but does not submit) an SQE to perform a `read(2)` or `preadv` depending on the buffer type. + /// * Reading into a `ReadBuffer.buffer` uses `read(2)` + /// * Reading into a `ReadBuffer.iovecs` uses `preadv(2)` + /// If you want to do a `preadv2()` then set `rw_flags` on the returned SQE. See https://linux.die.net/man/2/preadv. + /// /// Returns a pointer to the SQE. pub fn read( self: *IO_Uring, @@ -384,6 +391,7 @@ pub const IO_Uring = struct { const sqe = try self.get_sqe(); switch (buffer) { .buffer => |slice| io_uring_prep_read(sqe, fd, slice, offset), + .iovecs => |vecs| io_uring_prep_readv(sqe, fd, vecs, offset), .buffer_selection => |selection| { io_uring_prep_rw(.READ, sqe, fd, 0, selection.len, offset); sqe.flags |= linux.IOSQE_BUFFER_SELECT; @@ -409,23 +417,6 @@ pub const IO_Uring = struct { return sqe; } - /// Queues (but does not submit) an SQE to perform a `preadv()`. - /// Returns a pointer to the SQE so that you can further modify the SQE for advanced use cases. - /// For example, if you want to do a `preadv2()` then set `rw_flags` on the returned SQE. - /// See https://linux.die.net/man/2/preadv. - pub fn readv( - self: *IO_Uring, - user_data: u64, - fd: os.fd_t, - iovecs: []const os.iovec, - offset: u64, - ) !*io_uring_sqe { - const sqe = try self.get_sqe(); - io_uring_prep_readv(sqe, fd, iovecs, offset); - sqe.user_data = user_data; - return sqe; - } - /// Queues (but does not submit) an SQE to perform a IORING_OP_READ_FIXED. /// The `buffer` provided must be registered with the kernel by calling `register_buffers` first. /// The `buffer_index` must be the same as its index in the array provided to `register_buffers`. @@ -1715,7 +1706,7 @@ test "readv" { var buffer = [_]u8{42} ** 128; var iovecs = [_]os.iovec{os.iovec{ .iov_base = &buffer, .iov_len = buffer.len }}; - const sqe = try ring.readv(0xcccccccc, fd_index, iovecs[0..], 0); + const sqe = try ring.read(0xcccccccc, fd_index, .{ .iovecs = iovecs[0..] }, 0); try testing.expectEqual(linux.IORING_OP.READV, sqe.opcode); sqe.flags |= linux.IOSQE_FIXED_FILE; @@ -1766,7 +1757,7 @@ test "writev/fsync/readv" { try testing.expectEqual(fd, sqe_fsync.fd); sqe_fsync.flags |= linux.IOSQE_IO_LINK; - const sqe_readv = try ring.readv(0xffffffff, fd, iovecs_read[0..], 17); + const sqe_readv = try ring.read(0xffffffff, fd, .{ .iovecs = iovecs_read[0..] }, 17); try testing.expectEqual(linux.IORING_OP.READV, sqe_readv.opcode); try testing.expectEqual(@as(u64, 17), sqe_readv.off); From b0e8bf15f5017cf101eb31f74dd264eaf136045f Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 25 May 2022 17:39:55 +0300 Subject: [PATCH 1637/2031] Sema: add error for dereferencing comptime value at runtime --- src/Sema.zig | 3 ++ .../compile_errors/dereference_anyopaque.zig | 50 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 test/cases/compile_errors/dereference_anyopaque.zig diff --git a/src/Sema.zig b/src/Sema.zig index cf9b5aa57f..223cda8313 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -20961,6 +20961,9 @@ fn analyzeLoad( } } + const valid_rt = try sema.validateRunTimeType(block, src, elem_ty, false); + if (!valid_rt) return sema.failWithNeededComptime(block, src); + try sema.requireRuntimeBlock(block, src); return block.addTyOp(.load, elem_ty, ptr); } diff --git a/test/cases/compile_errors/dereference_anyopaque.zig b/test/cases/compile_errors/dereference_anyopaque.zig new file mode 100644 index 0000000000..44636b0851 --- /dev/null +++ b/test/cases/compile_errors/dereference_anyopaque.zig @@ -0,0 +1,50 @@ +const std = @import("std"); + +const Error = error{Something}; + +fn next() Error!void { + return; +} + +fn parse(comptime T: type, allocator: std.mem.Allocator) !void { + parseFree(T, undefined, allocator); + _ = (try next()) != null; +} + +fn parseFree(comptime T: type, value: T, allocator: std.mem.Allocator) void { + switch (@typeInfo(T)) { + .Struct => |structInfo| { + inline for (structInfo.fields) |field| { + if (!field.is_comptime) + parseFree(field.field_type, undefined, allocator); + } + }, + .Pointer => |ptrInfo| { + switch (ptrInfo.size) { + .One => { + parseFree(ptrInfo.child, value.*, allocator); + }, + .Slice => { + for (value) |v| + parseFree(ptrInfo.child, v, allocator); + }, + else => unreachable, + } + }, + else => unreachable, + } +} + +pub export fn entry() void { + const allocator = std.testing.allocator_instance.allocator(); + _ = parse(std.StringArrayHashMap(bool), allocator) catch return; +} + +// error +// backend=llvm +// +// :11:22: error: comparison of 'void' with null +// :25:51: error: unable to resolve comptime value +// :25:51: error: unable to resolve comptime value +// :25:51: error: unable to resolve comptime value +// :25:51: error: unable to resolve comptime value From 5fb7070642b660a83a2333a23d9163032f0dfcb9 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 25 May 2022 17:06:47 +0200 Subject: [PATCH 1638/2031] x64: move from compare_flags_* mcv to eflags with condition codes enum --- src/arch/x86_64/CodeGen.zig | 326 +++++++++++++++--------------------- src/arch/x86_64/Emit.zig | 296 +++++++++++++++++++++++--------- src/arch/x86_64/Mir.zig | 49 +++--- src/arch/x86_64/bits.zig | 129 ++++++++++++++ 4 files changed, 505 insertions(+), 295 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5c69f78724..206d20e766 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -37,6 +37,7 @@ const caller_preserved_regs = abi.caller_preserved_regs; const c_abi_int_param_regs = abi.c_abi_int_param_regs; const c_abi_int_return_regs = abi.c_abi_int_return_regs; +const Condition = bits.Condition; const RegisterManager = abi.RegisterManager; const RegisterLock = RegisterManager.RegisterLock; const Register = bits.Register; @@ -65,7 +66,7 @@ arg_index: u32, src_loc: Module.SrcLoc, stack_align: u32, -compare_flags_inst: ?Air.Inst.Index = null, +eflags_inst: ?Air.Inst.Index = null, /// MIR Instructions mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, @@ -149,12 +150,8 @@ pub const MCValue = union(enum) { stack_offset: i32, /// The value is a pointer to one of the stack variables (payload is stack offset). ptr_stack_offset: i32, - /// The value is in the compare flags assuming an unsigned operation, - /// with this operator applied on top of it. - compare_flags_unsigned: math.CompareOperator, - /// The value is in the compare flags assuming a signed operation, - /// with this operator applied on top of it. - compare_flags_signed: math.CompareOperator, + /// The value resides in the EFLAGS register. + eflags: Condition, fn isMemory(mcv: MCValue) bool { return switch (mcv) { @@ -183,8 +180,7 @@ pub const MCValue = union(enum) { .immediate, .memory, - .compare_flags_unsigned, - .compare_flags_signed, + .eflags, .ptr_stack_offset, .undef, .register_overflow_unsigned, @@ -780,10 +776,10 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { }, .register_overflow_signed, .register_overflow_unsigned => |reg| { self.register_manager.freeReg(reg.to64()); - self.compare_flags_inst = null; + self.eflags_inst = null; }, - .compare_flags_signed, .compare_flags_unsigned => { - self.compare_flags_inst = null; + .eflags => { + self.eflags_inst = null; }, else => {}, // TODO process stack allocation death } @@ -929,16 +925,14 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{}); } -pub fn spillCompareFlagsIfOccupied(self: *Self) !void { - if (self.compare_flags_inst) |inst_to_save| { +pub fn spillEflagsIfOccupied(self: *Self) !void { + if (self.eflags_inst) |inst_to_save| { const mcv = self.getResolvedInstValue(inst_to_save); const new_mcv = switch (mcv) { .register_overflow_signed, .register_overflow_unsigned, => try self.allocRegOrMem(inst_to_save, false), - .compare_flags_signed, - .compare_flags_unsigned, - => try self.allocRegOrMem(inst_to_save, true), + .eflags => try self.allocRegOrMem(inst_to_save, true), else => unreachable, }; @@ -948,7 +942,7 @@ pub fn spillCompareFlagsIfOccupied(self: *Self) !void { const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); - self.compare_flags_inst = null; + self.eflags_inst = null; // TODO consolidate with register manager and spillInstruction // this call should really belong in the register manager! @@ -1123,31 +1117,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { switch (operand) { .dead => unreachable, .unreach => unreachable, - .compare_flags_unsigned => |op| { - const r = MCValue{ - .compare_flags_unsigned = switch (op) { - .gte => .lt, - .gt => .lte, - .neq => .eq, - .lt => .gte, - .lte => .gt, - .eq => .neq, - }, - }; - break :result r; - }, - .compare_flags_signed => |op| { - const r = MCValue{ - .compare_flags_signed = switch (op) { - .gte => .lt, - .gt => .lte, - .neq => .eq, - .lt => .gte, - .lte => .gt, - .eq => .neq, - }, - }; - break :result r; + .eflags => |cc| { + break :result MCValue{ .eflags = cc.negate() }; }, else => {}, } @@ -1213,13 +1184,17 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void { try self.genBinOpMir(.cmp, ty, .{ .register = lhs_reg }, rhs_mcv); const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, rhs_mcv); + const cc: Condition = switch (signedness) { + .unsigned => .b, + .signed => .l, + }; _ = try self.addInst(.{ - .tag = if (signedness == .signed) .cond_mov_lt else .cond_mov_below, + .tag = .cond_mov, .ops = Mir.Inst.Ops.encode(.{ .reg1 = dst_mcv.register, .reg2 = lhs_reg, }), - .data = undefined, + .data = .{ .cc = cc }, }); break :result dst_mcv; @@ -1341,7 +1316,7 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement add/sub/shl with overflow for Ints larger than 64bits", .{}); } - try self.spillCompareFlagsIfOccupied(); + try self.spillEflagsIfOccupied(); if (tag == .shl_with_overflow) { try self.spillRegisters(1, .{.rcx}); @@ -1362,7 +1337,7 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const int_info = ty.intInfo(self.target.*); if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) { - self.compare_flags_inst = inst; + self.eflags_inst = inst; const result: MCValue = switch (int_info.signedness) { .signed => .{ .register_overflow_signed = partial.register }, @@ -1371,7 +1346,7 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { break :result result; } - self.compare_flags_inst = null; + self.eflags_inst = null; const tuple_ty = self.air.typeOfIndex(inst); const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); @@ -1413,17 +1388,16 @@ fn genSetStackTruncatedOverflowCompare( }; const overflow_reg = temp_regs[0]; - const flags: u2 = switch (int_info.signedness) { - .signed => 0b00, - .unsigned => 0b10, + const cc: Condition = switch (int_info.signedness) { + .signed => .o, + .unsigned => .c, }; _ = try self.addInst(.{ - .tag = .cond_set_byte_overflow, + .tag = .cond_set_byte, .ops = Mir.Inst.Ops.encode(.{ .reg1 = overflow_reg.to8(), - .flags = flags, }), - .data = undefined, + .data = .{ .cc = cc }, }); const scratch_reg = temp_regs[1]; @@ -1438,9 +1412,9 @@ fn genSetStackTruncatedOverflowCompare( const eq_reg = temp_regs[2]; _ = try self.addInst(.{ - .tag = .cond_set_byte_eq_ne, + .tag = .cond_set_byte, .ops = Mir.Inst.Ops.encode(.{ .reg1 = eq_reg.to8() }), - .data = undefined, + .data = .{ .cc = .ne }, }); try self.genBinOpMir( @@ -1477,8 +1451,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const int_info = ty.intInfo(self.target.*); if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) { - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; + try self.spillEflagsIfOccupied(); + self.eflags_inst = inst; try self.spillRegisters(2, .{ .rax, .rdx }); @@ -1492,8 +1466,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { }; } - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = null; + try self.spillEflagsIfOccupied(); + self.eflags_inst = null; const dst_reg: Register = dst_reg: { switch (int_info.signedness) { @@ -1686,12 +1660,12 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa .data = undefined, }); _ = try self.addInst(.{ - .tag = .cond_mov_eq, + .tag = .cond_mov, .ops = Mir.Inst.Ops.encode(.{ .reg1 = divisor.to64(), .reg2 = .rdx, }), - .data = undefined, + .data = .{ .cc = .e }, }); try self.genBinOpMir(.add, Type.isize, .{ .register = divisor }, .{ .register = .rax }); return MCValue{ .register = divisor }; @@ -2511,8 +2485,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .undef => unreachable, .unreach => unreachable, .dead => unreachable, - .compare_flags_unsigned => unreachable, - .compare_flags_signed => unreachable, + .eflags => unreachable, .register_overflow_unsigned => unreachable, .register_overflow_signed => unreachable, .immediate => |imm| { @@ -2532,8 +2505,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo switch (dst_mcv) { .dead => unreachable, .undef => unreachable, - .compare_flags_unsigned => unreachable, - .compare_flags_signed => unreachable, + .eflags => unreachable, .register => |dst_reg| { // mov dst_reg, [reg] _ = try self.addInst(.{ @@ -2637,8 +2609,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .undef => unreachable, .unreach => unreachable, .dead => unreachable, - .compare_flags_unsigned => unreachable, - .compare_flags_signed => unreachable, + .eflags => unreachable, .register_overflow_unsigned => unreachable, .register_overflow_signed => unreachable, .immediate => |imm| { @@ -2660,8 +2631,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .undef => unreachable, .dead => unreachable, .unreach => unreachable, - .compare_flags_unsigned => unreachable, - .compare_flags_signed => unreachable, + .eflags => unreachable, .immediate => |imm| { switch (abi_size) { 1, 2, 4 => { @@ -3041,18 +3011,17 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { defer self.register_manager.unlockReg(reg_lock); const dst_reg = try self.register_manager.allocReg(inst, gp); - const flags: u2 = switch (mcv) { - .register_overflow_unsigned => 0b10, - .register_overflow_signed => 0b00, + const cc: Condition = switch (mcv) { + .register_overflow_unsigned => .c, + .register_overflow_signed => .o, else => unreachable, }; _ = try self.addInst(.{ - .tag = .cond_set_byte_overflow, + .tag = .cond_set_byte, .ops = Mir.Inst.Ops.encode(.{ .reg1 = dst_reg.to8(), - .flags = flags, }), - .data = undefined, + .data = .{ .cc = cc }, }); break :result MCValue{ .register = dst_reg.to8() }; }, @@ -3479,8 +3448,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .none => unreachable, .undef => unreachable, .dead, .unreach, .immediate => unreachable, - .compare_flags_unsigned => unreachable, - .compare_flags_signed => unreachable, + .eflags => unreachable, .register_overflow_unsigned => unreachable, .register_overflow_signed => unreachable, .register => |dst_reg| { @@ -3559,8 +3527,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .memory, .got_load, .direct_load, - .compare_flags_signed, - .compare_flags_unsigned, + .eflags, => { assert(abi_size <= 8); const dst_reg_lock = self.register_manager.lockReg(dst_reg); @@ -3649,11 +3616,8 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .got_load, .direct_load => { return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{}); }, - .compare_flags_unsigned => { - return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{}); - }, - .compare_flags_signed => { - return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{}); + .eflags => { + return self.fail("TODO implement x86 ADD/SUB/CMP source eflags", .{}); }, } }, @@ -3674,8 +3638,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .none => unreachable, .undef => unreachable, .dead, .unreach, .immediate => unreachable, - .compare_flags_unsigned => unreachable, - .compare_flags_signed => unreachable, + .eflags => unreachable, .ptr_stack_offset => unreachable, .register_overflow_unsigned => unreachable, .register_overflow_signed => unreachable, @@ -3734,11 +3697,8 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .got_load, .direct_load => { return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); }, - .compare_flags_unsigned => { - return self.fail("TODO implement x86 multiply source compare flag (unsigned)", .{}); - }, - .compare_flags_signed => { - return self.fail("TODO implement x86 multiply source compare flag (signed)", .{}); + .eflags => { + return self.fail("TODO implement x86 multiply source eflags", .{}); }, } }, @@ -3782,11 +3742,8 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .got_load, .direct_load => { return self.fail("TODO implement x86 multiply source symbol at index in linker", .{}); }, - .compare_flags_unsigned => { - return self.fail("TODO implement x86 multiply source compare flag (unsigned)", .{}); - }, - .compare_flags_signed => { - return self.fail("TODO implement x86 multiply source compare flag (signed)", .{}); + .eflags => { + return self.fail("TODO implement x86 multiply source eflags", .{}); }, } }, @@ -3905,7 +3862,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. var info = try self.resolveCallingConventionValues(fn_ty); defer info.deinit(self); - try self.spillCompareFlagsIfOccupied(); + try self.spillEflagsIfOccupied(); for (caller_preserved_regs) |reg| { try self.register_manager.getReg(reg, null); @@ -3957,8 +3914,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .memory => unreachable, .got_load => unreachable, .direct_load => unreachable, - .compare_flags_signed => unreachable, - .compare_flags_unsigned => unreachable, + .eflags => unreachable, .register_overflow_signed => unreachable, .register_overflow_unsigned => unreachable, } @@ -4220,8 +4176,8 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { break :blk ty.intInfo(self.target.*).signedness; }; - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; + try self.spillEflagsIfOccupied(); + self.eflags_inst = inst; const result: MCValue = result: { // There are 2 operands, destination and source. @@ -4265,9 +4221,10 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { defer if (src_lock) |lock| self.register_manager.unlockReg(lock); try self.genBinOpMir(.cmp, ty, dst_mcv, src_mcv); + break :result switch (signedness) { - .signed => MCValue{ .compare_flags_signed = op }, - .unsigned => MCValue{ .compare_flags_unsigned = op }, + .signed => MCValue{ .eflags = Condition.fromCompareOperatorSigned(op) }, + .unsigned => MCValue{ .eflags = Condition.fromCompareOperatorUnsigned(op) }, }; }; @@ -4440,47 +4397,39 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { const abi_size = ty.abiSize(self.target.*); switch (mcv) { - .compare_flags_unsigned, - .compare_flags_signed, - => |cmp_op| { - // Here we map the opposites since the jump is to the false branch. - const flags: u2 = switch (cmp_op) { - .gte => 0b10, - .gt => 0b11, - .neq => 0b01, - .lt => 0b00, - .lte => 0b01, - .eq => 0b00, - }; - const tag: Mir.Inst.Tag = if (cmp_op == .neq or cmp_op == .eq) - .cond_jmp_eq_ne - else if (mcv == .compare_flags_unsigned) - Mir.Inst.Tag.cond_jmp_above_below - else - Mir.Inst.Tag.cond_jmp_greater_less; + .eflags => |cc| { return self.addInst(.{ - .tag = tag, - .ops = Mir.Inst.Ops.encode(.{ .flags = flags }), - .data = .{ .inst = undefined }, + .tag = .cond_jmp, + .ops = Mir.Inst.Ops.encode(.{}), + .data = .{ + .inst_cc = .{ + .inst = undefined, + // Here we map the opposites since the jump is to the false branch. + .cc = cc.negate(), + }, + }, }); }, .register => |reg| { - try self.spillCompareFlagsIfOccupied(); + try self.spillEflagsIfOccupied(); _ = try self.addInst(.{ .tag = .@"test", .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg }), .data = .{ .imm = 1 }, }); return self.addInst(.{ - .tag = .cond_jmp_eq_ne, - .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), - .data = .{ .inst = undefined }, + .tag = .cond_jmp, + .ops = Mir.Inst.Ops.encode(.{}), + .data = .{ .inst_cc = .{ + .inst = undefined, + .cc = .e, + } }, }); }, .immediate, .stack_offset, => { - try self.spillCompareFlagsIfOccupied(); + try self.spillEflagsIfOccupied(); if (abi_size <= 8) { const reg = try self.copyToTmpRegister(ty, mcv); return self.genCondBrMir(ty, .{ .register = reg }); @@ -4516,7 +4465,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { // Capture the state of register and stack allocation state so that we can revert to it. const parent_next_stack_offset = self.next_stack_offset; const parent_free_registers = self.register_manager.free_registers; - const parent_compare_flags_inst = self.compare_flags_inst; + const parent_eflags_inst = self.eflags_inst; var parent_stack = try self.stack.clone(self.gpa); defer parent_stack.deinit(self.gpa); const parent_registers = self.register_manager.registers; @@ -4538,7 +4487,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { defer saved_then_branch.deinit(self.gpa); self.register_manager.registers = parent_registers; - self.compare_flags_inst = parent_compare_flags_inst; + self.eflags_inst = parent_eflags_inst; self.stack.deinit(self.gpa); self.stack = parent_stack; @@ -4640,8 +4589,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { } fn isNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; + try self.spillEflagsIfOccupied(); + self.eflags_inst = inst; const cmp_ty: Type = if (!ty.isPtrLikeOptional()) blk: { var buf: Type.Payload.ElemType = undefined; @@ -4651,13 +4600,13 @@ fn isNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValu try self.genBinOpMir(.cmp, cmp_ty, operand, MCValue{ .immediate = 0 }); - return MCValue{ .compare_flags_unsigned = .eq }; + return MCValue{ .eflags = .e }; } fn isNonNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { const is_null_res = try self.isNull(inst, ty, operand); - assert(is_null_res.compare_flags_unsigned == .eq); - return MCValue{ .compare_flags_unsigned = .neq }; + assert(is_null_res.eflags == .e); + return MCValue{ .eflags = is_null_res.eflags.negate() }; } fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { @@ -4667,8 +4616,8 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue return MCValue{ .immediate = 0 }; // always false } - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; + try self.spillEflagsIfOccupied(); + self.eflags_inst = inst; const err_off = errUnionErrorOffset(ty.errorUnionPayload(), self.target.*); switch (operand) { @@ -4691,15 +4640,15 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue else => return self.fail("TODO implement isErr for {}", .{operand}), } - return MCValue{ .compare_flags_unsigned = .gt }; + return MCValue{ .eflags = .a }; } fn isNonErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { const is_err_res = try self.isErr(inst, ty, operand); switch (is_err_res) { - .compare_flags_unsigned => |op| { - assert(op == .gt); - return MCValue{ .compare_flags_unsigned = .lte }; + .eflags => |cc| { + assert(cc == .a); + return MCValue{ .eflags = cc.negate() }; }, .immediate => |imm| { assert(imm == 0); @@ -4914,10 +4863,9 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u .none => unreachable, .undef => unreachable, .dead, .unreach => unreachable, - .compare_flags_signed => unreachable, - .compare_flags_unsigned => unreachable, + .eflags => unreachable, .register => |cond_reg| { - try self.spillCompareFlagsIfOccupied(); + try self.spillEflagsIfOccupied(); const cond_reg_lock = self.register_manager.lockReg(cond_reg); defer if (cond_reg_lock) |lock| self.register_manager.unlockReg(lock); @@ -4965,13 +4913,16 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u .data = undefined, }); return self.addInst(.{ - .tag = .cond_jmp_eq_ne, + .tag = .cond_jmp, .ops = Mir.Inst.Ops.encode(.{}), - .data = .{ .inst = undefined }, + .data = .{ .inst_cc = .{ + .inst = undefined, + .cc = .ne, + } }, }); }, .stack_offset => { - try self.spillCompareFlagsIfOccupied(); + try self.spillEflagsIfOccupied(); if (abi_size <= 8) { const reg = try self.copyToTmpRegister(ty, condition); @@ -5030,7 +4981,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { // Capture the state of register and stack allocation state so that we can revert to it. const parent_next_stack_offset = self.next_stack_offset; const parent_free_registers = self.register_manager.free_registers; - const parent_compare_flags_inst = self.compare_flags_inst; + const parent_eflags_inst = self.eflags_inst; var parent_stack = try self.stack.clone(self.gpa); defer parent_stack.deinit(self.gpa); const parent_registers = self.register_manager.registers; @@ -5052,7 +5003,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { defer saved_case_branch.deinit(self.gpa); self.register_manager.registers = parent_registers; - self.compare_flags_inst = parent_compare_flags_inst; + self.eflags_inst = parent_eflags_inst; self.stack.deinit(self.gpa); self.stack = parent_stack; parent_stack = .{}; @@ -5092,7 +5043,15 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { const next_inst = @intCast(u32, self.mir_instructions.len); - self.mir_instructions.items(.data)[reloc].inst = next_inst; + switch (self.mir_instructions.items(.tag)[reloc]) { + .cond_jmp => { + self.mir_instructions.items(.data)[reloc].inst_cc.inst = next_inst; + }, + .jmp => { + self.mir_instructions.items(.data)[reloc].inst = next_inst; + }, + else => unreachable, + } } fn airBr(self: *Self, inst: Air.Inst.Index) !void { @@ -5111,7 +5070,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { block_data.mcv = switch (operand_mcv) { .none, .dead, .unreach => unreachable, .register, .stack_offset, .memory => operand_mcv, - .compare_flags_signed, .compare_flags_unsigned, .immediate => blk: { + .eflags, .immediate => blk: { const new_mcv = try self.allocRegOrMem(block, true); try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); break :blk new_mcv; @@ -5335,9 +5294,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE .register_overflow_unsigned, .register_overflow_signed, => return self.fail("TODO genSetStackArg for register with overflow bit", .{}), - .compare_flags_unsigned, - .compare_flags_signed, - => { + .eflags => { const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStackArg(ty, stack_offset, .{ .register = reg }); }, @@ -5484,18 +5441,17 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl const overflow_bit_ty = ty.structFieldType(1); const overflow_bit_offset = ty.structFieldOffset(1, self.target.*); const tmp_reg = try self.register_manager.allocReg(null, gp); - const flags: u2 = switch (mcv) { - .register_overflow_unsigned => 0b10, - .register_overflow_signed => 0b00, + const cc: Condition = switch (mcv) { + .register_overflow_unsigned => .c, + .register_overflow_signed => .o, else => unreachable, }; _ = try self.addInst(.{ - .tag = .cond_set_byte_overflow, + .tag = .cond_set_byte, .ops = Mir.Inst.Ops.encode(.{ .reg1 = tmp_reg.to8(), - .flags = flags, }), - .data = undefined, + .data = .{ .cc = cc }, }); return self.genSetStack( @@ -5505,9 +5461,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl .{}, ); }, - .compare_flags_unsigned, - .compare_flags_signed, - => { + .eflags => { const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStack(ty, stack_offset, .{ .register = reg }, opts); }, @@ -5832,9 +5786,12 @@ fn genInlineMemcpy( // je end const loop_reloc = try self.addInst(.{ - .tag = .cond_jmp_eq_ne, - .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), - .data = .{ .inst = undefined }, + .tag = .cond_jmp, + .ops = Mir.Inst.Ops.encode(.{}), + .data = .{ .inst_cc = .{ + .inst = undefined, + .cc = .e, + } }, }); // mov tmp, [addr + rcx] @@ -5950,9 +5907,12 @@ fn genInlineMemset( // je end const loop_reloc = try self.addInst(.{ - .tag = .cond_jmp_eq_ne, - .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), - .data = .{ .inst = undefined }, + .tag = .cond_jmp, + .ops = Mir.Inst.Ops.encode(.{}), + .data = .{ .inst_cc = .{ + .inst = undefined, + .cc = .e, + } }, }); switch (value) { @@ -6025,31 +5985,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void else => unreachable, } }, - .compare_flags_unsigned, - .compare_flags_signed, - => |op| { - const tag: Mir.Inst.Tag = switch (op) { - .gte, .gt, .lt, .lte => if (mcv == .compare_flags_unsigned) - Mir.Inst.Tag.cond_set_byte_above_below - else - Mir.Inst.Tag.cond_set_byte_greater_less, - .eq, .neq => .cond_set_byte_eq_ne, - }; - const flags: u2 = switch (op) { - .gte => 0b00, - .gt => 0b01, - .lt => 0b10, - .lte => 0b11, - .eq => 0b01, - .neq => 0b00, - }; + .eflags => |cc| { _ = try self.addInst(.{ - .tag = tag, + .tag = .cond_set_byte, .ops = Mir.Inst.Ops.encode(.{ .reg1 = reg.to8(), - .flags = flags, }), - .data = undefined, + .data = .{ .cc = cc }, }); }, .immediate => |x| { diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 84955a8aac..654775cc21 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -158,20 +158,9 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .jmp => try emit.mirJmpCall(.jmp_near, inst), .call => try emit.mirJmpCall(.call_near, inst), - .cond_jmp_greater_less, - .cond_jmp_above_below, - .cond_jmp_eq_ne, - => try emit.mirCondJmp(tag, inst), - - .cond_set_byte_greater_less, - .cond_set_byte_above_below, - .cond_set_byte_eq_ne, - .cond_set_byte_overflow, - => try emit.mirCondSetByte(tag, inst), - - .cond_mov_eq => try emit.mirCondMov(.cmove, inst), - .cond_mov_lt => try emit.mirCondMov(.cmovl, inst), - .cond_mov_below => try emit.mirCondMov(.cmovb, inst), + .cond_jmp => try emit.mirCondJmp(inst), + .cond_set_byte => try emit.mirCondSetByte(inst), + .cond_mov => try emit.mirCondMov(inst), .ret => try emit.mirRet(inst), @@ -356,70 +345,130 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { } } -fn mirCondJmp(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst].decode(); - const target = emit.mir.instructions.items(.data)[inst].inst; - const tag = switch (mir_tag) { - .cond_jmp_greater_less => switch (ops.flags) { - 0b00 => Tag.jge, - 0b01 => Tag.jg, - 0b10 => Tag.jl, - 0b11 => Tag.jle, - }, - .cond_jmp_above_below => switch (ops.flags) { - 0b00 => Tag.jae, - 0b01 => Tag.ja, - 0b10 => Tag.jb, - 0b11 => Tag.jbe, - }, - .cond_jmp_eq_ne => switch (@truncate(u1, ops.flags)) { - 0b0 => Tag.jne, - 0b1 => Tag.je, - }, - else => unreachable, +fn mirCondJmp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const mir_tag = emit.mir.instructions.items(.tag)[inst]; + assert(mir_tag == .cond_jmp); + const inst_cc = emit.mir.instructions.items(.data)[inst].inst_cc; + const tag: Tag = switch (inst_cc.cc) { + .a => .ja, + .ae => .jae, + .b => .jb, + .be => .jbe, + .c => .jc, + .e => .je, + .g => .jg, + .ge => .jge, + .l => .jl, + .le => .jle, + .na => .jna, + .nae => .jnae, + .nb => .jnb, + .nbe => .jnbe, + .nc => .jnc, + .ne => .jne, + .ng => .jng, + .nge => .jnge, + .nl => .jnl, + .nle => .jnle, + .no => .jno, + .np => .jnp, + .ns => .jns, + .nz => .jnz, + .o => .jo, + .p => .jp, + .pe => .jpe, + .po => .jpo, + .s => .js, + .z => .jz, }; const source = emit.code.items.len; try lowerToDEnc(tag, 0, emit.code); try emit.relocs.append(emit.bin_file.allocator, .{ .source = source, - .target = target, + .target = inst_cc.inst, .offset = emit.code.items.len - 4, .length = 6, }); } -fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirCondSetByte(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const mir_tag = emit.mir.instructions.items(.tag)[inst]; + assert(mir_tag == .cond_set_byte); const ops = emit.mir.instructions.items(.ops)[inst].decode(); - const tag = switch (mir_tag) { - .cond_set_byte_greater_less => switch (ops.flags) { - 0b00 => Tag.setge, - 0b01 => Tag.setg, - 0b10 => Tag.setl, - 0b11 => Tag.setle, - }, - .cond_set_byte_above_below => switch (ops.flags) { - 0b00 => Tag.setae, - 0b01 => Tag.seta, - 0b10 => Tag.setb, - 0b11 => Tag.setbe, - }, - .cond_set_byte_eq_ne => switch (@truncate(u1, ops.flags)) { - 0b0 => Tag.setne, - 0b1 => Tag.sete, - }, - .cond_set_byte_overflow => switch (ops.flags) { - 0b00 => Tag.seto, - 0b01 => Tag.setno, - 0b10 => Tag.setc, - 0b11 => Tag.setnc, - }, - else => unreachable, + const cc = emit.mir.instructions.items(.data)[inst].cc; + const tag: Tag = switch (cc) { + .a => .seta, + .ae => .setae, + .b => .setb, + .be => .setbe, + .c => .setc, + .e => .sete, + .g => .setg, + .ge => .setge, + .l => .setl, + .le => .setle, + .na => .setna, + .nae => .setnae, + .nb => .setnb, + .nbe => .setnbe, + .nc => .setnc, + .ne => .setne, + .ng => .setng, + .nge => .setnge, + .nl => .setnl, + .nle => .setnle, + .no => .setno, + .np => .setnp, + .ns => .setns, + .nz => .setnz, + .o => .seto, + .p => .setp, + .pe => .setpe, + .po => .setpo, + .s => .sets, + .z => .setz, }; return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1.to8()), emit.code); } -fn mirCondMov(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirCondMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const mir_tag = emit.mir.instructions.items(.tag)[inst]; + assert(mir_tag == .cond_mov); const ops = emit.mir.instructions.items(.ops)[inst].decode(); + const cc = emit.mir.instructions.items(.data)[inst].cc; + const tag: Tag = switch (cc) { + .a => .cmova, + .ae => .cmovae, + .b => .cmovb, + .be => .cmovbe, + .c => .cmovc, + .e => .cmove, + .g => .cmovg, + .ge => .cmovge, + .l => .cmovl, + .le => .cmovle, + .na => .cmovna, + .nae => .cmovnae, + .nb => .cmovnb, + .nbe => .cmovnbe, + .nc => .cmovnc, + .ne => .cmovne, + .ng => .cmovng, + .nge => .cmovnge, + .nl => .cmovnl, + .nle => .cmovnle, + .no => .cmovno, + .np => .cmovnp, + .ns => .cmovns, + .nz => .cmovnz, + .o => .cmovo, + .p => .cmovp, + .pe => .cmovpe, + .po => .cmovpo, + .s => .cmovs, + .z => .cmovz, + }; + if (ops.flags == 0b00) { return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code); } @@ -1277,7 +1326,7 @@ const Tag = enum { setp, setpe, setnp, - setop, + setpo, setl, setnge, setnl, @@ -1286,6 +1335,36 @@ const Tag = enum { setng, setnle, setg, + cmovo, + cmovno, + cmovb, + cmovc, + cmovnae, + cmovnb, + cmovnc, + cmovae, + cmove, + cmovz, + cmovne, + cmovnz, + cmovbe, + cmovna, + cmova, + cmovnbe, + cmovs, + cmovns, + cmovp, + cmovpe, + cmovnp, + cmovpo, + cmovl, + cmovnge, + cmovnl, + cmovge, + cmovle, + cmovng, + cmovnle, + cmovg, shl, sal, shr, @@ -1294,12 +1373,6 @@ const Tag = enum { cwd, cdq, cqo, - cmove, - cmovz, - cmovl, - cmovng, - cmovb, - cmovnae, movsd, movss, addsd, @@ -1372,7 +1445,7 @@ const Tag = enum { .setp, .setpe, .setnp, - .setop, + .setpo, .setl, .setnge, .setnl, @@ -1492,77 +1565,110 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) OpCode { .d => return switch (tag) { .jmp_near => OpCode.init(&.{0xe9}), .call_near => OpCode.init(&.{0xe8}), + .jo => if (is_one_byte) OpCode.init(&.{0x70}) else OpCode.init(&.{0x0f,0x80}), + .jno => if (is_one_byte) OpCode.init(&.{0x71}) else OpCode.init(&.{0x0f,0x81}), + .jb, .jc, .jnae => if (is_one_byte) OpCode.init(&.{0x72}) else OpCode.init(&.{0x0f,0x82}), + .jnb, .jnc, .jae => if (is_one_byte) OpCode.init(&.{0x73}) else OpCode.init(&.{0x0f,0x83}), + .je, .jz => if (is_one_byte) OpCode.init(&.{0x74}) else OpCode.init(&.{0x0f,0x84}), + .jne, .jnz => if (is_one_byte) OpCode.init(&.{0x75}) else OpCode.init(&.{0x0f,0x85}), + .jna, .jbe => if (is_one_byte) OpCode.init(&.{0x76}) else OpCode.init(&.{0x0f,0x86}), + .jnbe, .ja => if (is_one_byte) OpCode.init(&.{0x77}) else OpCode.init(&.{0x0f,0x87}), + .js => if (is_one_byte) OpCode.init(&.{0x78}) else OpCode.init(&.{0x0f,0x88}), + .jns => if (is_one_byte) OpCode.init(&.{0x79}) else OpCode.init(&.{0x0f,0x89}), + .jpe, .jp => if (is_one_byte) OpCode.init(&.{0x7a}) else OpCode.init(&.{0x0f,0x8a}), + .jpo, .jnp => if (is_one_byte) OpCode.init(&.{0x7b}) else OpCode.init(&.{0x0f,0x8b}), + .jnge, .jl => if (is_one_byte) OpCode.init(&.{0x7c}) else OpCode.init(&.{0x0f,0x8c}), + .jge, .jnl => if (is_one_byte) OpCode.init(&.{0x7d}) else OpCode.init(&.{0x0f,0x8d}), + .jle, .jng => if (is_one_byte) OpCode.init(&.{0x7e}) else OpCode.init(&.{0x0f,0x8e}), + .jg, .jnle => if (is_one_byte) OpCode.init(&.{0x7f}) else OpCode.init(&.{0x0f,0x8f}), + else => unreachable, }, .m => return switch (tag) { .jmp_near, .call_near, .push => OpCode.init(&.{0xff}), + .pop => OpCode.init(&.{0x8f}), .seto => OpCode.init(&.{0x0f,0x90}), .setno => OpCode.init(&.{0x0f,0x91}), + .setb, .setc, .setnae => OpCode.init(&.{0x0f,0x92}), + .setnb, .setnc, .setae => OpCode.init(&.{0x0f,0x93}), + .sete, .setz => OpCode.init(&.{0x0f,0x94}), + .setne, .setnz => OpCode.init(&.{0x0f,0x95}), + .setbe, .setna => OpCode.init(&.{0x0f,0x96}), + .seta, .setnbe => OpCode.init(&.{0x0f,0x97}), + .sets => OpCode.init(&.{0x0f,0x98}), .setns => OpCode.init(&.{0x0f,0x99}), + .setp, .setpe => OpCode.init(&.{0x0f,0x9a}), + .setnp, - .setop => OpCode.init(&.{0x0f,0x9b}), + .setpo => OpCode.init(&.{0x0f,0x9b}), + .setl, .setnge => OpCode.init(&.{0x0f,0x9c}), + .setnl, .setge => OpCode.init(&.{0x0f,0x9d}), + .setle, .setng => OpCode.init(&.{0x0f,0x9e}), + .setnle, .setg => OpCode.init(&.{0x0f,0x9f}), + .idiv, .div, .imul, .mul => if (is_one_byte) OpCode.init(&.{0xf6}) else OpCode.init(&.{0xf7}), + .fisttp16 => OpCode.init(&.{0xdf}), .fisttp32 => OpCode.init(&.{0xdb}), .fisttp64 => OpCode.init(&.{0xdd}), @@ -1640,12 +1746,52 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) OpCode { .movzx => if (is_one_byte) OpCode.init(&.{0x0f,0xb6}) else OpCode.init(&.{0x0f,0xb7}), .lea => if (is_one_byte) OpCode.init(&.{0x8c}) else OpCode.init(&.{0x8d}), .imul => OpCode.init(&.{0x0f,0xaf}), - .cmove, - .cmovz => OpCode.init(&.{0x0f,0x44}), + + .cmova, + .cmovnbe, => OpCode.init(&.{0x0f,0x47}), + + .cmovae, + .cmovnb, => OpCode.init(&.{0x0f,0x43}), + .cmovb, + .cmovc, .cmovnae => OpCode.init(&.{0x0f,0x42}), + + .cmovbe, + .cmovna, => OpCode.init(&.{0x0f,0x46}), + + .cmove, + .cmovz, => OpCode.init(&.{0x0f,0x44}), + + .cmovg, + .cmovnle, => OpCode.init(&.{0x0f,0x4f}), + + .cmovge, + .cmovnl, => OpCode.init(&.{0x0f,0x4d}), + .cmovl, - .cmovng => OpCode.init(&.{0x0f,0x4c}), + .cmovnge, => OpCode.init(&.{0x0f,0x4c}), + + .cmovle, + .cmovng, => OpCode.init(&.{0x0f,0x4e}), + + .cmovne, + .cmovnz, => OpCode.init(&.{0x0f,0x45}), + + .cmovno => OpCode.init(&.{0x0f,0x41}), + + .cmovnp, + .cmovpo, => OpCode.init(&.{0x0f,0x4b}), + + .cmovns => OpCode.init(&.{0x0f,0x49}), + + .cmovo => OpCode.init(&.{0x0f,0x40}), + + .cmovp, + .cmovpe, => OpCode.init(&.{0x0f,0x4a}), + + .cmovs => OpCode.init(&.{0x0f,0x48}), + .movsd => OpCode.init(&.{0xf2,0x0f,0x10}), .movss => OpCode.init(&.{0xf3,0x0f,0x10}), .addsd => OpCode.init(&.{0xf2,0x0f,0x58}), @@ -1735,7 +1881,7 @@ inline fn getModRmExt(tag: Tag) u3 { .setp, .setpe, .setnp, - .setop, + .setpo, .setl, .setnge, .setnl, diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index a35231a9b8..0918d67f3c 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -275,42 +275,25 @@ pub const Inst = struct { call, /// ops flags: - /// 0b00 gte - /// 0b01 gt - /// 0b10 lt - /// 0b11 lte - cond_jmp_greater_less, - cond_set_byte_greater_less, + /// unused + /// Notes: + /// * uses `inst_cc` in Data. + cond_jmp, /// ops flags: - /// 0b00 above or equal - /// 0b01 above - /// 0b10 below - /// 0b11 below or equal - cond_jmp_above_below, - cond_set_byte_above_below, - - /// ops flags: - /// 0bX0 ne - /// 0bX1 eq - cond_jmp_eq_ne, - cond_set_byte_eq_ne, + /// 0b00 reg1 + /// Notes: + /// * uses condition code (CC) stored as part of data + cond_set_byte, /// ops flags: /// 0b00 reg1, reg2, /// 0b01 reg1, word ptr [reg2 + imm] /// 0b10 reg1, dword ptr [reg2 + imm] /// 0b11 reg1, qword ptr [reg2 + imm] - cond_mov_eq, - cond_mov_lt, - cond_mov_below, - - /// ops flags: - /// 0b00 reg1 if OF = 1 - /// 0b01 reg1 if OF = 0 - /// 0b10 reg1 if CF = 1 - /// 0b11 reg1 if CF = 0 - cond_set_byte_overflow, + /// Notes: + /// * uses condition code (CC) stored as part of data + cond_mov, /// ops flags: form: /// 0b00 reg1 @@ -451,6 +434,16 @@ pub const Inst = struct { inst: Index, /// A 32-bit immediate value. imm: u32, + /// A condition code for use with EFLAGS register. + cc: bits.Condition, + /// Another instruction with condition code. + /// Used by `cond_jmp`. + inst_cc: struct { + /// Another instruction. + inst: Index, + /// A condition code for use with EFLAGS register. + cc: bits.Condition, + }, /// An extern function. extern_fn: struct { /// Index of the containing atom. diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 6429781516..a2d523e84c 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -6,6 +6,135 @@ const ArrayList = std.ArrayList; const Allocator = std.mem.Allocator; const DW = std.dwarf; +/// EFLAGS condition codes +pub const Condition = enum(u5) { + /// above + a, + /// above or equal + ae, + /// below + b, + /// below or equal + be, + /// carry + c, + /// equal + e, + /// greater + g, + /// greater or equal + ge, + /// less + l, + /// less or equal + le, + /// not above + na, + /// not above or equal + nae, + /// not below + nb, + /// not below or equal + nbe, + /// not carry + nc, + /// not equal + ne, + /// not greater + ng, + /// not greater or equal + nge, + /// not less + nl, + /// not less or equal + nle, + /// not overflow + no, + /// not parity + np, + /// not sign + ns, + /// not zero + nz, + /// overflow + o, + /// parity + p, + /// parity even + pe, + /// parity odd + po, + /// sign + s, + /// zero + z, + + /// Converts a std.math.CompareOperator into a condition flag, + /// i.e. returns the condition that is true iff the result of the + /// comparison is true. Assumes signed comparison + pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition { + return switch (op) { + .gte => .ge, + .gt => .g, + .neq => .ne, + .lt => .l, + .lte => .le, + .eq => .e, + }; + } + + /// Converts a std.math.CompareOperator into a condition flag, + /// i.e. returns the condition that is true iff the result of the + /// comparison is true. Assumes unsigned comparison + pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition { + return switch (op) { + .gte => .ae, + .gt => .a, + .neq => .ne, + .lt => .b, + .lte => .be, + .eq => .e, + }; + } + + /// Returns the condition which is true iff the given condition is + /// false (if such a condition exists) + pub fn negate(cond: Condition) Condition { + return switch (cond) { + .a => .na, + .ae => .nae, + .b => .nb, + .be => .nbe, + .c => .nc, + .e => .ne, + .g => .ng, + .ge => .nge, + .l => .nl, + .le => .nle, + .na => .a, + .nae => .ae, + .nb => .b, + .nbe => .be, + .nc => .c, + .ne => .e, + .ng => .g, + .nge => .ge, + .nl => .l, + .nle => .le, + .no => .o, + .np => .p, + .ns => .s, + .nz => .z, + .o => .no, + .p => .np, + .pe => unreachable, + .po => unreachable, + .s => .ns, + .z => .nz, + }; + } +}; + // zig fmt: off /// Definitions of all of the general purpose x64 registers. The order is semantically meaningful. From 7000395a7d002a6d242d717de60614516f4e29af Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 25 May 2022 17:18:43 +0200 Subject: [PATCH 1639/2031] x64: use new condition codes enum for register with overflow mcv --- src/arch/x86_64/CodeGen.zig | 137 +++++++++++++++--------------------- 1 file changed, 55 insertions(+), 82 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 206d20e766..8adad94e24 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -128,12 +128,8 @@ pub const MCValue = union(enum) { immediate: u64, /// The value is in a GP register. register: Register, - /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register, - /// and the operation is an unsigned operation. - register_overflow_unsigned: Register, - /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register, - /// and the operation is a signed operation. - register_overflow_signed: Register, + /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register. + register_overflow: struct { reg: Register, eflags: Condition }, /// The value is in memory at a hard-coded address. /// If the type is a pointer, it means the pointer address is at this memory location. memory: u64, @@ -183,8 +179,7 @@ pub const MCValue = union(enum) { .eflags, .ptr_stack_offset, .undef, - .register_overflow_unsigned, - .register_overflow_signed, + .register_overflow, => false, .register, @@ -774,8 +769,8 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { .register => |reg| { self.register_manager.freeReg(reg.to64()); }, - .register_overflow_signed, .register_overflow_unsigned => |reg| { - self.register_manager.freeReg(reg.to64()); + .register_overflow => |ro| { + self.register_manager.freeReg(ro.reg.to64()); self.eflags_inst = null; }, .eflags => { @@ -809,20 +804,22 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; branch.inst_table.putAssumeCapacityNoClobber(inst, result); + // In some cases (such as bitcast), an operand + // may be the same MCValue as the result. If + // that operand died and was a register, it + // was freed by processDeath. We have to + // "re-allocate" the register. switch (result) { - .register, - .register_overflow_signed, - .register_overflow_unsigned, - => |reg| { - // In some cases (such as bitcast), an operand - // may be the same MCValue as the result. If - // that operand died and was a register, it - // was freed by processDeath. We have to - // "re-allocate" the register. + .register => |reg| { if (self.register_manager.isRegFree(reg)) { self.register_manager.getRegAssumeFree(reg, inst); } }, + .register_overflow => |ro| { + if (self.register_manager.isRegFree(ro.reg)) { + self.register_manager.getRegAssumeFree(ro.reg, inst); + } + }, else => {}, } } @@ -912,12 +909,12 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); const reg_mcv = self.getResolvedInstValue(inst); switch (reg_mcv) { - .register, - .register_overflow_unsigned, - .register_overflow_signed, - => |other| { + .register => |other| { assert(reg.to64() == other.to64()); }, + .register_overflow => |ro| { + assert(reg.to64() == ro.reg.to64()); + }, else => {}, } const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; @@ -929,9 +926,7 @@ pub fn spillEflagsIfOccupied(self: *Self) !void { if (self.eflags_inst) |inst_to_save| { const mcv = self.getResolvedInstValue(inst_to_save); const new_mcv = switch (mcv) { - .register_overflow_signed, - .register_overflow_unsigned, - => try self.allocRegOrMem(inst_to_save, false), + .register_overflow => try self.allocRegOrMem(inst_to_save, false), .eflags => try self.allocRegOrMem(inst_to_save, true), else => unreachable, }; @@ -947,9 +942,7 @@ pub fn spillEflagsIfOccupied(self: *Self) !void { // TODO consolidate with register manager and spillInstruction // this call should really belong in the register manager! switch (mcv) { - .register_overflow_signed, - .register_overflow_unsigned, - => |reg| self.register_manager.freeReg(reg), + .register_overflow => |ro| self.register_manager.freeReg(ro.reg), else => {}, } } @@ -1339,11 +1332,14 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) { self.eflags_inst = inst; - const result: MCValue = switch (int_info.signedness) { - .signed => .{ .register_overflow_signed = partial.register }, - .unsigned => .{ .register_overflow_unsigned = partial.register }, + const cc: Condition = switch (int_info.signedness) { + .unsigned => .c, + .signed => .o, }; - break :result result; + break :result MCValue{ .register_overflow = .{ + .reg = partial.register, + .eflags = cc, + } }; } self.eflags_inst = null; @@ -1460,10 +1456,14 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const rhs = try self.resolveInst(bin_op.rhs); const partial = try self.genMulDivBinOp(.mul, null, ty, lhs, rhs); - break :result switch (int_info.signedness) { - .signed => MCValue{ .register_overflow_signed = partial.register }, - .unsigned => MCValue{ .register_overflow_unsigned = partial.register }, + const cc: Condition = switch (int_info.signedness) { + .unsigned => .c, + .signed => .o, }; + break :result MCValue{ .register_overflow = .{ + .reg = partial.register, + .eflags = cc, + } }; } try self.spillEflagsIfOccupied(); @@ -2486,8 +2486,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .unreach => unreachable, .dead => unreachable, .eflags => unreachable, - .register_overflow_unsigned => unreachable, - .register_overflow_signed => unreachable, + .register_overflow => unreachable, .immediate => |imm| { try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }); }, @@ -2610,8 +2609,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .unreach => unreachable, .dead => unreachable, .eflags => unreachable, - .register_overflow_unsigned => unreachable, - .register_overflow_signed => unreachable, + .register_overflow => unreachable, .immediate => |imm| { try self.setRegOrMem(value_ty, .{ .memory = imm }, value); }, @@ -2997,31 +2995,24 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { break :result dst_mcv; }, - .register_overflow_unsigned, - .register_overflow_signed, - => |reg| { + .register_overflow => |ro| { switch (index) { 0 => { // Get wrapped value for overflow operation. - break :result MCValue{ .register = reg }; + break :result MCValue{ .register = ro.reg }; }, 1 => { // Get overflow bit. - const reg_lock = self.register_manager.lockRegAssumeUnused(reg); + const reg_lock = self.register_manager.lockRegAssumeUnused(ro.reg); defer self.register_manager.unlockReg(reg_lock); const dst_reg = try self.register_manager.allocReg(inst, gp); - const cc: Condition = switch (mcv) { - .register_overflow_unsigned => .c, - .register_overflow_signed => .o, - else => unreachable, - }; _ = try self.addInst(.{ .tag = .cond_set_byte, .ops = Mir.Inst.Ops.encode(.{ .reg1 = dst_reg.to8(), }), - .data = .{ .cc = cc }, + .data = .{ .cc = ro.eflags }, }); break :result MCValue{ .register = dst_reg.to8() }; }, @@ -3449,15 +3440,13 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .undef => unreachable, .dead, .unreach, .immediate => unreachable, .eflags => unreachable, - .register_overflow_unsigned => unreachable, - .register_overflow_signed => unreachable, + .register_overflow => unreachable, .register => |dst_reg| { switch (src_mcv) { .none => unreachable, .undef => unreachable, .dead, .unreach => unreachable, - .register_overflow_unsigned => unreachable, - .register_overflow_signed => unreachable, + .register_overflow => unreachable, .ptr_stack_offset => { const dst_reg_lock = self.register_manager.lockReg(dst_reg); defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); @@ -3564,8 +3553,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .none => unreachable, .undef => unreachable, .dead, .unreach => unreachable, - .register_overflow_unsigned => unreachable, - .register_overflow_signed => unreachable, + .register_overflow => unreachable, .register => |src_reg| { _ = try self.addInst(.{ .tag = mir_tag, @@ -3640,16 +3628,14 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .dead, .unreach, .immediate => unreachable, .eflags => unreachable, .ptr_stack_offset => unreachable, - .register_overflow_unsigned => unreachable, - .register_overflow_signed => unreachable, + .register_overflow => unreachable, .register => |dst_reg| { switch (src_mcv) { .none => unreachable, .undef => try self.genSetReg(dst_ty, dst_reg, .undef), .dead, .unreach => unreachable, .ptr_stack_offset => unreachable, - .register_overflow_unsigned => unreachable, - .register_overflow_signed => unreachable, + .register_overflow => unreachable, .register => |src_reg| { // register, register _ = try self.addInst(.{ @@ -3708,8 +3694,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .undef => return self.genSetStack(dst_ty, off, .undef, .{}), .dead, .unreach => unreachable, .ptr_stack_offset => unreachable, - .register_overflow_unsigned => unreachable, - .register_overflow_signed => unreachable, + .register_overflow => unreachable, .register => |src_reg| { // copy dst to a register const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); @@ -3915,8 +3900,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .got_load => unreachable, .direct_load => unreachable, .eflags => unreachable, - .register_overflow_signed => unreachable, - .register_overflow_unsigned => unreachable, + .register_overflow => unreachable, } } @@ -5291,9 +5275,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE .{ .dest_stack_base = .rsp }, ); }, - .register_overflow_unsigned, - .register_overflow_signed, - => return self.fail("TODO genSetStackArg for register with overflow bit", .{}), + .register_overflow => return self.fail("TODO genSetStackArg for register with overflow bit", .{}), .eflags => { const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStackArg(ty, stack_offset, .{ .register = reg }); @@ -5429,29 +5411,22 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl ), } }, - .register_overflow_unsigned, - .register_overflow_signed, - => |reg| { - const reg_lock = self.register_manager.lockReg(reg); + .register_overflow => |ro| { + const reg_lock = self.register_manager.lockReg(ro.reg); defer if (reg_lock) |lock| self.register_manager.unlockReg(lock); const wrapped_ty = ty.structFieldType(0); - try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg }, .{}); + try self.genSetStack(wrapped_ty, stack_offset, .{ .register = ro.reg }, .{}); const overflow_bit_ty = ty.structFieldType(1); const overflow_bit_offset = ty.structFieldOffset(1, self.target.*); const tmp_reg = try self.register_manager.allocReg(null, gp); - const cc: Condition = switch (mcv) { - .register_overflow_unsigned => .c, - .register_overflow_signed => .o, - else => unreachable, - }; _ = try self.addInst(.{ .tag = .cond_set_byte, .ops = Mir.Inst.Ops.encode(.{ .reg1 = tmp_reg.to8(), }), - .data = .{ .cc = cc }, + .data = .{ .cc = ro.eflags }, }); return self.genSetStack( @@ -5956,9 +5931,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void const abi_size = @intCast(u32, ty.abiSize(self.target.*)); switch (mcv) { .dead => unreachable, - .register_overflow_unsigned, - .register_overflow_signed, - => unreachable, + .register_overflow => unreachable, .ptr_stack_offset => |off| { if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { return self.fail("stack offset too large", .{}); From d214b6bdf05204377d897afe3eacc0cac33c59a3 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 25 May 2022 18:08:51 +0300 Subject: [PATCH 1640/2031] stage2: packed struct fields do not have a byte offset --- src/Sema.zig | 6 +++++- src/value.zig | 1 + test/behavior/struct.zig | 21 +++++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 223cda8313..340b1f9db3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -20197,7 +20197,11 @@ fn beginComptimePtrLoad( var deref = try beginComptimePtrLoad(sema, block, src, field_ptr.container_ptr, field_ptr.container_ty); if (field_ptr.container_ty.hasWellDefinedLayout()) { - if (deref.parent) |*parent| { + const struct_ty = field_ptr.container_ty.castTag(.@"struct"); + if (struct_ty != null and struct_ty.?.data.layout == .Packed) { + // packed structs are not byte addressable + deref.parent = null; + } else if (deref.parent) |*parent| { // Update the byte offset (in-place) try sema.resolveTypeLayout(block, src, field_ptr.container_ty); const field_offset = field_ptr.container_ty.structFieldOffset(field_index, target); diff --git a/src/value.zig b/src/value.zig index 310384c3c3..da8f7a2c62 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2645,6 +2645,7 @@ pub const Value = extern union { } unreachable; }, + .undef => return Value.undef, else => unreachable, } diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index ab7d761de0..8370cd763e 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1319,3 +1319,24 @@ test "packed struct aggregate init" { const result = @bitCast(u8, S.foo(1, 2)); try expect(result == 9); } + +test "packed struct field access via pointer" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + const S = packed struct { a: u30 }; + var s1: S = .{ .a = 1 }; + var s2 = &s1; + try expect(s2.a == 1); + var s3: S = undefined; + var s4 = &s3; + _ = s4; + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} From eba66f4a584bbbfed93ac4de890c8a23f245fb1f Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 25 May 2022 18:27:17 +0300 Subject: [PATCH 1641/2031] Sema: handle block.is_typeof in more places --- src/Sema.zig | 7 ++++++- test/behavior/sizeof_and_typeof.zig | 10 ++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 340b1f9db3..2d2e7f9030 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1513,6 +1513,7 @@ fn resolveDefinedValue( ) CompileError!?Value { if (try sema.resolveMaybeUndefVal(block, src, air_ref)) |val| { if (val.isUndef()) { + if (block.is_typeof) return null; return sema.failWithUseOfUndef(block, src); } return val; @@ -12268,6 +12269,7 @@ fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr .inlining = block.inlining, .is_comptime = false, .is_typeof = true, + .want_safety = false, }; defer child_block.instructions.deinit(sema.gpa); @@ -20832,7 +20834,7 @@ fn analyzeDeclVal( const decl_ref = try sema.analyzeDeclRef(decl_index); const result = try sema.analyzeLoad(block, src, decl_ref, src); if (Air.refToIndex(result)) |index| { - if (sema.air_instructions.items(.tag)[index] == .constant) { + if (sema.air_instructions.items(.tag)[index] == .constant and !block.is_typeof) { try sema.decl_val_table.put(sema.gpa, decl_index, result); } } @@ -20963,6 +20965,9 @@ fn analyzeLoad( if (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) |elem_val| { return sema.addConstant(elem_ty, elem_val); } + if (block.is_typeof) { + return sema.addConstUndef(elem_ty); + } } const valid_rt = try sema.validateRunTimeType(block, src, elem_ty, false); diff --git a/test/behavior/sizeof_and_typeof.zig b/test/behavior/sizeof_and_typeof.zig index 293acda267..d1f26c8167 100644 --- a/test/behavior/sizeof_and_typeof.zig +++ b/test/behavior/sizeof_and_typeof.zig @@ -280,3 +280,13 @@ test "@sizeOf comparison against zero" { try S.doTheTest(S1, true); try S.doTheTest(U1, true); } + +test "hardcoded address in typeof expression" { + const S = struct { + fn func() @TypeOf(@intToPtr(*[]u8, 0x10).*[0]) { + return 0; + } + }; + try expect(S.func() == 0); + comptime try expect(S.func() == 0); +} From e0be22b6c06530d56308853723f982c492e768fd Mon Sep 17 00:00:00 2001 From: Helio Machado <0x2b3bfa0+git@googlemail.com> Date: Wed, 25 May 2022 19:23:49 +0200 Subject: [PATCH 1642/2031] std.crypto: cosmetic improvement to AES multiplication algorithm (#11616) std.crypto: cosmetic improvement to AES multiplication algorithm (#11616) --- lib/std/crypto/aes/soft.zig | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/std/crypto/aes/soft.zig b/lib/std/crypto/aes/soft.zig index 7d676eb8ce..57d433c115 100644 --- a/lib/std/crypto/aes/soft.zig +++ b/lib/std/crypto/aes/soft.zig @@ -489,21 +489,18 @@ fn generateTable(invert: bool) [4][256]u32 { fn mul(a: u8, b: u8) u8 { @setEvalBranchQuota(30000); - var i: u9 = a; - var j: u8 = b; - var k: u8 = 1; + var i: u8 = a; + var j: u9 = b; var s: u9 = 0; - while (k < 0x100 and j != 0) : (k <<= 1) { - if (j & k != 0) { - s ^= i; - j ^= k; + while (i > 0) : (i >>= 1) { + if (i & 1 != 0) { + s ^= j; } - i <<= 1; - - if (i & 0x100 != 0) { - i ^= poly; + j *= 2; + if (j & 0x100 != 0) { + j ^= poly; } } From f409d925ad4640ea71cc3ef5e83b13ae6e216959 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 25 May 2022 23:09:10 +0300 Subject: [PATCH 1643/2031] Sema: check for generic poison in `resolveInst` --- src/Sema.zig | 419 ++++++++++++++-------------- test/behavior/sizeof_and_typeof.zig | 10 + 2 files changed, 220 insertions(+), 209 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 2d2e7f9030..0b50d9199b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -584,7 +584,7 @@ fn resolveBody( sema.comptime_break_inst = break_data.inst; return error.ComptimeBreak; } - return sema.resolveInst(break_data.operand); + return try sema.resolveInst(break_data.operand); } pub fn analyzeBody( @@ -1191,7 +1191,7 @@ fn analyzeBodyInner( const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse break always_noreturn; if (inst == break_data.block_inst) { - break :blk sema.resolveInst(break_data.operand); + break :blk try sema.resolveInst(break_data.operand); } else { break break_data.inst; } @@ -1214,7 +1214,7 @@ fn analyzeBodyInner( const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse break always_noreturn; if (inst == break_data.block_inst) { - break :blk sema.resolveInst(break_data.operand); + break :blk try sema.resolveInst(break_data.operand); } else { break break_data.inst; } @@ -1283,7 +1283,7 @@ fn analyzeBodyInner( const break_data = opt_break_data orelse break always_noreturn; if (inst == break_data.block_inst) { - break :blk sema.resolveInst(break_data.operand); + break :blk try sema.resolveInst(break_data.operand); } else { break break_data.inst; } @@ -1301,7 +1301,7 @@ fn analyzeBodyInner( const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse break always_noreturn; if (inst == break_data.block_inst) { - break :blk sema.resolveInst(break_data.operand); + break :blk try sema.resolveInst(break_data.operand); } else { break break_data.inst; } @@ -1317,7 +1317,7 @@ fn analyzeBodyInner( const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse break always_noreturn; if (inst == break_data.block_inst) { - break :blk sema.resolveInst(break_data.operand); + break :blk try sema.resolveInst(break_data.operand); } else { break break_data.inst; } @@ -1350,7 +1350,7 @@ fn analyzeBodyInner( return result; } -pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) Air.Inst.Ref { +pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref { var i: usize = @enumToInt(zir_ref); // First section of indexes correspond to a set number of constant values. @@ -1361,7 +1361,9 @@ pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) Air.Inst.Ref { i -= Zir.Inst.Ref.typed_value_map.len; // Finally, the last section of indexes refers to the map of ZIR=>AIR. - return sema.inst_map.get(@intCast(u32, i)).?; + const inst = sema.inst_map.get(@intCast(u32, i)).?; + if (sema.typeOf(inst).tag() == .generic_poison) return error.GenericPoison; + return inst; } fn resolveConstBool( @@ -1370,7 +1372,7 @@ fn resolveConstBool( src: LazySrcLoc, zir_ref: Zir.Inst.Ref, ) !bool { - const air_inst = sema.resolveInst(zir_ref); + const air_inst = try sema.resolveInst(zir_ref); const wanted_type = Type.bool; const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); const val = try sema.resolveConstValue(block, src, coerced_inst); @@ -1383,7 +1385,7 @@ pub fn resolveConstString( src: LazySrcLoc, zir_ref: Zir.Inst.Ref, ) ![]u8 { - const air_inst = sema.resolveInst(zir_ref); + const air_inst = try sema.resolveInst(zir_ref); const wanted_type = Type.initTag(.const_slice_u8); const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); const val = try sema.resolveConstValue(block, src, coerced_inst); @@ -1391,7 +1393,7 @@ pub fn resolveConstString( } pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type { - const air_inst = sema.resolveInst(zir_ref); + const air_inst = try sema.resolveInst(zir_ref); const ty = try sema.analyzeAsType(block, src, air_inst); if (ty.tag() == .generic_poison) return error.GenericPoison; return ty; @@ -1738,7 +1740,7 @@ fn resolveInt( zir_ref: Zir.Inst.Ref, dest_ty: Type, ) !u64 { - const air_inst = sema.resolveInst(zir_ref); + const air_inst = try sema.resolveInst(zir_ref); const coerced = try sema.coerce(block, dest_ty, air_inst, src); const val = try sema.resolveConstValue(block, src, coerced); const target = sema.mod.getTarget(); @@ -1753,7 +1755,7 @@ pub fn resolveInstConst( src: LazySrcLoc, zir_ref: Zir.Inst.Ref, ) CompileError!TypedValue { - const air_ref = sema.resolveInst(zir_ref); + const air_ref = try sema.resolveInst(zir_ref); const val = try sema.resolveConstValue(block, src, air_ref); return TypedValue{ .ty = sema.typeOf(air_ref), @@ -1769,7 +1771,7 @@ pub fn resolveInstValue( src: LazySrcLoc, zir_ref: Zir.Inst.Ref, ) CompileError!TypedValue { - const air_ref = sema.resolveInst(zir_ref); + const air_ref = try sema.resolveInst(zir_ref); const val = try sema.resolveValue(block, src, air_ref); return TypedValue{ .ty = sema.typeOf(air_ref), @@ -1784,7 +1786,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const src: LazySrcLoc = sema.src; const bin_inst = sema.code.instructions.items(.data)[inst].bin; const pointee_ty = try sema.resolveType(block, src, bin_inst.lhs); - const ptr = sema.resolveInst(bin_inst.rhs); + const ptr = try sema.resolveInst(bin_inst.rhs); const target = sema.mod.getTarget(); const addr_space = target_util.defaultAddressSpace(target, .local); @@ -2532,7 +2534,7 @@ fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].un_tok; - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); return sema.analyzeRef(block, inst_data.src(), operand); } @@ -2551,7 +2553,7 @@ fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compile defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const src = inst_data.src(); return sema.ensureResultUsed(block, operand, src); @@ -2575,7 +2577,7 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const src = inst_data.src(); const operand_ty = sema.typeOf(operand); switch (operand_ty.zigTypeTag()) { @@ -2590,7 +2592,7 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - const object = sema.resolveInst(inst_data.operand); + const object = try sema.resolveInst(inst_data.operand); const object_ty = sema.typeOf(object); const is_pointer_to = object_ty.isSinglePointer(); @@ -2709,7 +2711,7 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const ptr = sema.resolveInst(inst_data.operand); + const ptr = try sema.resolveInst(inst_data.operand); const ptr_ty = sema.typeOf(ptr); var ptr_info = ptr_ty.ptrInfo().data; ptr_info.mutable = false; @@ -2825,7 +2827,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; - const ptr = sema.resolveInst(inst_data.operand); + const ptr = try sema.resolveInst(inst_data.operand); const ptr_inst = Air.refToIndex(ptr).?; assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant); const value_index = sema.air_instructions.items(.data)[ptr_inst].ty_pl.payload; @@ -2987,7 +2989,7 @@ fn zirArrayBasePtr( const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - const start_ptr = sema.resolveInst(inst_data.operand); + const start_ptr = try sema.resolveInst(inst_data.operand); var base_ptr = start_ptr; while (true) switch (sema.typeOf(base_ptr).childType().zigTypeTag()) { .ErrorUnion => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true), @@ -3012,7 +3014,7 @@ fn zirFieldBasePtr( const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - const start_ptr = sema.resolveInst(inst_data.operand); + const start_ptr = try sema.resolveInst(inst_data.operand); var base_ptr = start_ptr; while (true) switch (sema.typeOf(base_ptr).childType().zigTypeTag()) { .ErrorUnion => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true), @@ -3076,7 +3078,7 @@ fn zirValidateStructInit( const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len]; const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node; const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; - const object_ptr = sema.resolveInst(field_ptr_extra.lhs); + const object_ptr = try sema.resolveInst(field_ptr_extra.lhs); const agg_ty = sema.typeOf(object_ptr).childType(); switch (agg_ty.zigTypeTag()) { .Struct => return sema.validateStructInit( @@ -3252,7 +3254,7 @@ fn validateStructInit( var root_msg: ?*Module.ErrorMsg = null; const fields = struct_obj.fields.values(); - const struct_ptr = sema.resolveInst(struct_ptr_zir_ref); + const struct_ptr = try sema.resolveInst(struct_ptr_zir_ref); const struct_ty = sema.typeOf(struct_ptr).childType(); if (is_comptime or block.is_comptime) { @@ -3449,7 +3451,7 @@ fn zirValidateArrayInit( const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len]; const first_elem_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node; const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data; - const array_ptr = sema.resolveInst(elem_ptr_extra.ptr); + const array_ptr = try sema.resolveInst(elem_ptr_extra.ptr); const array_ty = sema.typeOf(array_ptr).childType(); const array_len = array_ty.arrayLen(); @@ -3647,7 +3649,7 @@ fn zirStoreToBlockPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE // This is an elided instruction, but AstGen was unable to omit it. return; }; - const operand = sema.resolveInst(bin_inst.rhs); + const operand = try sema.resolveInst(bin_inst.rhs); const src: LazySrcLoc = sema.src; blk: { const ptr_inst = Air.refToIndex(ptr) orelse break :blk; @@ -3676,8 +3678,8 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi const src: LazySrcLoc = sema.src; const bin_inst = sema.code.instructions.items(.data)[inst].bin; - const ptr = sema.resolveInst(bin_inst.lhs); - const operand = sema.resolveInst(bin_inst.rhs); + const ptr = try sema.resolveInst(bin_inst.lhs); + const operand = try sema.resolveInst(bin_inst.rhs); const ptr_inst = Air.refToIndex(ptr).?; assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant); const air_datas = sema.air_instructions.items(.data); @@ -3759,8 +3761,8 @@ fn zirStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void defer tracy.end(); const bin_inst = sema.code.instructions.items(.data)[inst].bin; - const ptr = sema.resolveInst(bin_inst.lhs); - const value = sema.resolveInst(bin_inst.rhs); + const ptr = try sema.resolveInst(bin_inst.lhs); + const value = try sema.resolveInst(bin_inst.rhs); return sema.storePtr(block, sema.src, ptr, value); } @@ -3773,8 +3775,8 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v const inst_data = zir_datas[inst].pl_node; const src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const ptr = sema.resolveInst(extra.lhs); - const operand = sema.resolveInst(extra.rhs); + const ptr = try sema.resolveInst(extra.lhs); + const operand = try sema.resolveInst(extra.rhs); // Check for the possibility of this pattern: // %a = ret_ptr @@ -3799,7 +3801,7 @@ fn zirParamType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const callee_src = sema.src; const inst_data = sema.code.instructions.items(.data)[inst].param_type; - const callee = sema.resolveInst(inst_data.callee); + const callee = try sema.resolveInst(inst_data.callee); const callee_ty = sema.typeOf(callee); var param_index = inst_data.param_index; @@ -3960,7 +3962,7 @@ fn zirCompileLog( for (args) |arg_ref, i| { if (i != 0) try writer.print(", ", .{}); - const arg = sema.resolveInst(arg_ref); + const arg = try sema.resolveInst(arg_ref); const arg_ty = sema.typeOf(arg); if (try sema.resolveMaybeUndefVal(block, src, arg)) |val| { try writer.print("@as({}, {})", .{ @@ -3982,7 +3984,7 @@ fn zirCompileLog( fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src: LazySrcLoc = inst_data.src(); - const msg_inst = sema.resolveInst(inst_data.operand); + const msg_inst = try sema.resolveInst(inst_data.operand); return sema.panicWithMsg(block, src, msg_inst); } @@ -4206,7 +4208,7 @@ fn resolveBlockBody( const break_inst = sema.comptime_break_inst; const break_data = sema.code.instructions.items(.data)[break_inst].@"break"; if (break_data.block_inst == body_inst) { - return sema.resolveInst(break_data.operand); + return try sema.resolveInst(break_data.operand); } else { return error.ComptimeBreak; } @@ -4536,7 +4538,7 @@ fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].@"break"; - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const zir_block = inst_data.block_inst; var block = start_block; @@ -4602,7 +4604,7 @@ fn zirDbgVar( if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return; const str_op = sema.code.instructions.items(.data)[inst].str_op; - const operand = sema.resolveInst(str_op.operand); + const operand = try sema.resolveInst(str_op.operand); const name = str_op.getStr(sema.code); try sema.addDbgVar(block, operand, air_tag, name); } @@ -4785,7 +4787,7 @@ fn zirCall( const modifier = @intToEnum(std.builtin.CallOptions.Modifier, extra.data.flags.packed_modifier); const ensure_result_used = extra.data.flags.ensure_result_used; - var func = sema.resolveInst(extra.data.callee); + var func = try sema.resolveInst(extra.data.callee); var resolved_args: []Air.Inst.Ref = undefined; const func_type = sema.typeOf(func); @@ -4798,12 +4800,12 @@ fn zirCall( resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len + 1); resolved_args[0] = bound_data.arg0_inst; for (args) |zir_arg, i| { - resolved_args[i + 1] = sema.resolveInst(zir_arg); + resolved_args[i + 1] = try sema.resolveInst(zir_arg); } } else { resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len); for (args) |zir_arg, i| { - resolved_args[i] = sema.resolveInst(zir_arg); + resolved_args[i] = try sema.resolveInst(zir_arg); } } @@ -5827,7 +5829,7 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil const elem_src: LazySrcLoc = .{ .node_offset_array_type_elem = inst_data.src_node }; const len = try sema.resolveInt(block, len_src, extra.len, Type.usize); const elem_type = try sema.resolveType(block, elem_src, extra.elem_type); - const uncasted_sentinel = sema.resolveInst(extra.sentinel); + const uncasted_sentinel = try sema.resolveInst(extra.sentinel); const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src); const sentinel_val = try sema.resolveConstValue(block, sentinel_src, sentinel); const array_ty = try Type.array(sema.arena, len, sentinel_val, elem_type, sema.mod); @@ -5892,7 +5894,7 @@ fn zirErrorToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const op = sema.resolveInst(inst_data.operand); + const op = try sema.resolveInst(inst_data.operand); const op_coerced = try sema.coerce(block, Type.anyerror, op, operand_src); const result_ty = Type.u16; @@ -5929,7 +5931,7 @@ fn zirIntToError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const uncasted_operand = sema.resolveInst(inst_data.operand); + const uncasted_operand = try sema.resolveInst(inst_data.operand); const operand = try sema.coerce(block, Type.u16, uncasted_operand, operand_src); const target = sema.mod.getTarget(); @@ -5967,8 +5969,8 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; - const lhs = sema.resolveInst(extra.lhs); - const rhs = sema.resolveInst(extra.rhs); + const lhs = try sema.resolveInst(extra.lhs); + const rhs = try sema.resolveInst(extra.rhs); if (sema.typeOf(lhs).zigTypeTag() == .Bool and sema.typeOf(rhs).zigTypeTag() == .Bool) { const msg = msg: { const msg = try sema.errMsg(block, lhs_src, "expected error set type, found 'bool'", .{}); @@ -6027,7 +6029,7 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag()) { @@ -6075,7 +6077,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); - const operand = sema.resolveInst(extra.rhs); + const operand = try sema.resolveInst(extra.rhs); if (dest_ty.zigTypeTag() != .Enum) { return sema.fail(block, dest_ty_src, "expected enum, found {}", .{dest_ty.fmt(sema.mod)}); @@ -6126,7 +6128,7 @@ fn zirOptionalPayloadPtr( defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const optional_ptr = sema.resolveInst(inst_data.operand); + const optional_ptr = try sema.resolveInst(inst_data.operand); const src = inst_data.src(); return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check, false); @@ -6211,7 +6213,7 @@ fn zirOptionalPayload( const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); const result_ty = switch (operand_ty.zigTypeTag()) { .Optional => try operand_ty.optionalChildAlloc(sema.arena), @@ -6263,7 +6265,7 @@ fn zirErrUnionPayload( const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const operand_src = src; const operand_ty = sema.typeOf(operand); if (operand_ty.zigTypeTag() != .ErrorUnion) { @@ -6304,7 +6306,7 @@ fn zirErrUnionPayloadPtr( defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const src = inst_data.src(); return sema.analyzeErrUnionPayloadPtr(block, src, operand, safety_check, false); @@ -6390,7 +6392,7 @@ fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); if (operand_ty.zigTypeTag() != .ErrorUnion) { return sema.fail(block, src, "expected error union type, found '{}'", .{ @@ -6416,7 +6418,7 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); assert(operand_ty.zigTypeTag() == .Pointer); @@ -6445,7 +6447,7 @@ fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com const inst_data = sema.code.instructions.items(.data)[inst].un_tok; const src = inst_data.src(); - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); if (operand_ty.zigTypeTag() != .ErrorUnion) { return sema.fail(block, src, "expected error union type, found '{}'", .{ @@ -6941,7 +6943,7 @@ fn analyzeAs( zir_operand: Zir.Inst.Ref, ) CompileError!Air.Inst.Ref { const dest_ty = try sema.resolveType(block, src, zir_dest_type); - const operand = sema.resolveInst(zir_operand); + const operand = try sema.resolveInst(zir_operand); if (dest_ty.tag() == .var_args_param) return operand; return sema.coerce(block, dest_ty, operand, src); } @@ -6952,7 +6954,7 @@ fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const inst_data = sema.code.instructions.items(.data)[inst].un_node; const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const ptr = sema.resolveInst(inst_data.operand); + const ptr = try sema.resolveInst(inst_data.operand); const ptr_ty = sema.typeOf(ptr); if (!ptr_ty.isPtrAtRuntime()) { return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)}); @@ -6973,7 +6975,7 @@ fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; const field_name = sema.code.nullTerminatedString(extra.field_name_start); - const object = sema.resolveInst(extra.lhs); + const object = try sema.resolveInst(extra.lhs); return sema.fieldVal(block, src, object, field_name, field_name_src); } @@ -6986,7 +6988,7 @@ fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; const field_name = sema.code.nullTerminatedString(extra.field_name_start); - const object_ptr = sema.resolveInst(extra.lhs); + const object_ptr = try sema.resolveInst(extra.lhs); return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src); } @@ -6999,7 +7001,7 @@ fn zirFieldCallBind(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; const field_name = sema.code.nullTerminatedString(extra.field_name_start); - const object_ptr = sema.resolveInst(extra.lhs); + const object_ptr = try sema.resolveInst(extra.lhs); return sema.fieldCallBind(block, src, object_ptr, field_name, field_name_src); } @@ -7011,7 +7013,7 @@ fn zirFieldValNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const src = inst_data.src(); const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; - const object = sema.resolveInst(extra.lhs); + const object = try sema.resolveInst(extra.lhs); const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); return sema.fieldVal(block, src, object, field_name, field_name_src); } @@ -7024,7 +7026,7 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const src = inst_data.src(); const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; - const object_ptr = sema.resolveInst(extra.lhs); + const object_ptr = try sema.resolveInst(extra.lhs); const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src); } @@ -7036,7 +7038,7 @@ fn zirFieldCallBindNamed(sema: *Sema, block: *Block, extended: Zir.Inst.Extended const extra = sema.code.extraData(Zir.Inst.FieldNamedNode, extended.operand).data; const src: LazySrcLoc = .{ .node_offset = extra.node }; const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; - const object_ptr = sema.resolveInst(extra.lhs); + const object_ptr = try sema.resolveInst(extra.lhs); const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); return sema.fieldCallBind(block, src, object_ptr, field_name, field_name_src); } @@ -7051,7 +7053,7 @@ fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); - const operand = sema.resolveInst(extra.rhs); + const operand = try sema.resolveInst(extra.rhs); return sema.intCast(block, dest_ty, dest_ty_src, operand, operand_src, true); } @@ -7241,7 +7243,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air => {}, } - const operand = sema.resolveInst(extra.rhs); + const operand = try sema.resolveInst(extra.rhs); return sema.bitCast(block, dest_ty, operand, operand_src); } @@ -7256,7 +7258,7 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); - const operand = sema.resolveInst(extra.rhs); + const operand = try sema.resolveInst(extra.rhs); const target = sema.mod.getTarget(); const dest_is_comptime_float = switch (dest_ty.zigTypeTag()) { @@ -7301,8 +7303,8 @@ fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air defer tracy.end(); const bin_inst = sema.code.instructions.items(.data)[inst].bin; - const array = sema.resolveInst(bin_inst.lhs); - const elem_index = sema.resolveInst(bin_inst.rhs); + const array = try sema.resolveInst(bin_inst.lhs); + const elem_index = try sema.resolveInst(bin_inst.rhs); return sema.elemVal(block, sema.src, array, elem_index, sema.src); } @@ -7314,8 +7316,8 @@ fn zirElemValNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const src = inst_data.src(); const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const array = sema.resolveInst(extra.lhs); - const elem_index = sema.resolveInst(extra.rhs); + const array = try sema.resolveInst(extra.lhs); + const elem_index = try sema.resolveInst(extra.rhs); return sema.elemVal(block, src, array, elem_index, elem_index_src); } @@ -7324,8 +7326,8 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air defer tracy.end(); const bin_inst = sema.code.instructions.items(.data)[inst].bin; - const array_ptr = sema.resolveInst(bin_inst.lhs); - const elem_index = sema.resolveInst(bin_inst.rhs); + const array_ptr = try sema.resolveInst(bin_inst.lhs); + const elem_index = try sema.resolveInst(bin_inst.rhs); return sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src); } @@ -7337,8 +7339,8 @@ fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const src = inst_data.src(); const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const array_ptr = sema.resolveInst(extra.lhs); - const elem_index = sema.resolveInst(extra.rhs); + const array_ptr = try sema.resolveInst(extra.lhs); + const elem_index = try sema.resolveInst(extra.rhs); return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src); } @@ -7349,7 +7351,7 @@ fn zirElemPtrImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data; - const array_ptr = sema.resolveInst(extra.ptr); + const array_ptr = try sema.resolveInst(extra.ptr); const elem_index = try sema.addIntUnsigned(Type.usize, extra.index); return sema.elemPtr(block, src, array_ptr, elem_index, src); } @@ -7361,8 +7363,8 @@ fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data; - const array_ptr = sema.resolveInst(extra.lhs); - const start = sema.resolveInst(extra.start); + const array_ptr = try sema.resolveInst(extra.lhs); + const start = try sema.resolveInst(extra.start); return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded); } @@ -7374,9 +7376,9 @@ fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data; - const array_ptr = sema.resolveInst(extra.lhs); - const start = sema.resolveInst(extra.start); - const end = sema.resolveInst(extra.end); + const array_ptr = try sema.resolveInst(extra.lhs); + const start = try sema.resolveInst(extra.start); + const end = try sema.resolveInst(extra.end); return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded); } @@ -7389,10 +7391,10 @@ fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const src = inst_data.src(); const sentinel_src: LazySrcLoc = .{ .node_offset_slice_sentinel = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data; - const array_ptr = sema.resolveInst(extra.lhs); - const start = sema.resolveInst(extra.start); - const end = sema.resolveInst(extra.end); - const sentinel = sema.resolveInst(extra.sentinel); + const array_ptr = try sema.resolveInst(extra.lhs); + const start = try sema.resolveInst(extra.start); + const end = try sema.resolveInst(extra.end); + const sentinel = try sema.resolveInst(extra.sentinel); return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src); } @@ -7416,7 +7418,7 @@ fn zirSwitchCapture( const operand_is_ref = switch_extra.data.bits.is_ref; const cond_inst = Zir.refToIndex(switch_extra.data.operand).?; const cond_info = sema.code.instructions.items(.data)[cond_inst].un_node; - const operand_ptr = sema.resolveInst(cond_info.operand); + const operand_ptr = try sema.resolveInst(cond_info.operand); const operand_ptr_ty = sema.typeOf(operand_ptr); const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty; @@ -7450,7 +7452,7 @@ fn zirSwitchCapture( const union_obj = operand_ty.cast(Type.Payload.Union).?.data; const enum_ty = union_obj.tag_ty; - const first_item = sema.resolveInst(items[0]); + const first_item = try sema.resolveInst(items[0]); // Previous switch validation ensured this will succeed const first_item_val = sema.resolveConstValue(block, .unneeded, first_item) catch unreachable; @@ -7458,7 +7460,7 @@ fn zirSwitchCapture( const first_field = union_obj.fields.values()[first_field_index]; for (items[1..]) |item| { - const item_ref = sema.resolveInst(item); + const item_ref = try sema.resolveInst(item); // Previous switch validation ensured this will succeed const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable; @@ -7515,7 +7517,7 @@ fn zirSwitchCapture( var names: Module.ErrorSet.NameMap = .{}; try names.ensureUnusedCapacity(sema.arena, items.len); for (items) |item| { - const item_ref = sema.resolveInst(item); + const item_ref = try sema.resolveInst(item); // Previous switch validation ensured this will succeed const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable; names.putAssumeCapacityNoClobber( @@ -7529,7 +7531,7 @@ fn zirSwitchCapture( return sema.bitCast(block, else_error_ty, operand, operand_src); } else { - const item_ref = sema.resolveInst(items[0]); + const item_ref = try sema.resolveInst(items[0]); // Previous switch validation ensured this will succeed const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable; @@ -7559,7 +7561,7 @@ fn zirSwitchCond( const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand_src = src; // TODO make this point at the switch operand - const operand_ptr = sema.resolveInst(inst_data.operand); + const operand_ptr = try sema.resolveInst(inst_data.operand); const operand = if (is_ref) try sema.analyzeLoad(block, src, operand_ptr, operand_src) else @@ -7630,7 +7632,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset }; const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index); - const operand = sema.resolveInst(extra.data.operand); + const operand = try sema.resolveInst(extra.data.operand); var header_extra_index: usize = extra.end; @@ -8208,7 +8210,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const body = sema.code.extra[extra_index..][0..body_len]; extra_index += body_len; - const item = sema.resolveInst(item_ref); + const item = try sema.resolveInst(item_ref); // Validation above ensured these will succeed. const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable; if (operand_val.eql(item_val, operand_ty, sema.mod)) { @@ -8230,7 +8232,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const body = sema.code.extra[extra_index + 2 * ranges_len ..][0..body_len]; for (items) |item_ref| { - const item = sema.resolveInst(item_ref); + const item = try sema.resolveInst(item_ref); // Validation above ensured these will succeed. const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable; if (operand_val.eql(item_val, operand_ty, sema.mod)) { @@ -8298,7 +8300,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError case_block.instructions.shrinkRetainingCapacity(0); case_block.wip_capture_scope = wip_captures.scope; - const item = sema.resolveInst(item_ref); + const item = try sema.resolveInst(item_ref); // `item` is already guaranteed to be constant known. _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) { @@ -8375,14 +8377,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len)); for (items) |item_ref| { - const item = sema.resolveInst(item_ref); + const item = try sema.resolveInst(item_ref); cases_extra.appendAssumeCapacity(@enumToInt(item)); } cases_extra.appendSliceAssumeCapacity(case_block.instructions.items); } else { for (items) |item_ref| { - const item = sema.resolveInst(item_ref); + const item = try sema.resolveInst(item_ref); const cmp_ok = try case_block.addBinOp(.cmp_eq, operand, item); if (any_ok != .none) { any_ok = try case_block.addBinOp(.bool_or, any_ok, cmp_ok); @@ -8398,8 +8400,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const last_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const item_first = sema.resolveInst(first_ref); - const item_last = sema.resolveInst(last_ref); + const item_first = try sema.resolveInst(first_ref); + const item_last = try sema.resolveInst(last_ref); // operand >= first and operand <= last const range_first_ok = try case_block.addBinOp( @@ -8552,7 +8554,7 @@ fn resolveSwitchItemVal( switch_prong_src: Module.SwitchProngSrc, range_expand: Module.SwitchProngSrc.RangeExpand, ) CompileError!TypedValue { - const item = sema.resolveInst(item_ref); + const item = try sema.resolveInst(item_ref); const item_ty = sema.typeOf(item); // Constructing a LazySrcLoc is costly because we only have the switch AST node. // Only if we know for sure we need to report a compile error do we resolve the @@ -8903,8 +8905,8 @@ fn zirShl( const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const lhs = sema.resolveInst(extra.lhs); - const rhs = sema.resolveInst(extra.rhs); + const lhs = try sema.resolveInst(extra.lhs); + const rhs = try sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); const target = sema.mod.getTarget(); @@ -9031,8 +9033,8 @@ fn zirShr( const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const lhs = sema.resolveInst(extra.lhs); - const rhs = sema.resolveInst(extra.rhs); + const lhs = try sema.resolveInst(extra.lhs); + const rhs = try sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); @@ -9085,8 +9087,8 @@ fn zirBitwise( const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const lhs = sema.resolveInst(extra.lhs); - const rhs = sema.resolveInst(extra.rhs); + const lhs = try sema.resolveInst(extra.lhs); + const rhs = try sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); @@ -9130,7 +9132,7 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const src = inst_data.src(); const operand_src = src; // TODO put this on the operand, not the '~' - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const operand_type = sema.typeOf(operand); const scalar_type = operand_type.scalarType(); const target = sema.mod.getTarget(); @@ -9245,8 +9247,8 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const lhs = sema.resolveInst(extra.lhs); - const rhs = sema.resolveInst(extra.rhs); + const lhs = try sema.resolveInst(extra.lhs); + const rhs = try sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); @@ -9429,7 +9431,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const lhs = sema.resolveInst(extra.lhs); + const lhs = try sema.resolveInst(extra.lhs); const lhs_ty = sema.typeOf(lhs); const src: LazySrcLoc = inst_data.src(); const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; @@ -9509,7 +9511,7 @@ fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const lhs_src = src; const rhs_src = src; // TODO better source location - const rhs = sema.resolveInst(inst_data.operand); + const rhs = try sema.resolveInst(inst_data.operand); const rhs_ty = sema.typeOf(rhs); const rhs_scalar_ty = rhs_ty.scalarType(); @@ -9529,7 +9531,7 @@ fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const lhs = if (rhs_ty.zigTypeTag() == .Vector) try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, Value.zero)) else - sema.resolveInst(.zero); + try sema.resolveInst(.zero); return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src); } @@ -9540,13 +9542,13 @@ fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const lhs_src = src; const rhs_src = src; // TODO better source location - const rhs = sema.resolveInst(inst_data.operand); + const rhs = try sema.resolveInst(inst_data.operand); const rhs_ty = sema.typeOf(rhs); const lhs = if (rhs_ty.zigTypeTag() == .Vector) try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, Value.zero)) else - sema.resolveInst(.zero); + try sema.resolveInst(.zero); return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src); } @@ -9565,8 +9567,8 @@ fn zirArithmetic( const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const lhs = sema.resolveInst(extra.lhs); - const rhs = sema.resolveInst(extra.rhs); + const lhs = try sema.resolveInst(extra.lhs); + const rhs = try sema.resolveInst(extra.rhs); return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src); } @@ -9587,9 +9589,9 @@ fn zirOverflowArithmetic( const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node }; - const lhs = sema.resolveInst(extra.lhs); - const rhs = sema.resolveInst(extra.rhs); - const ptr = sema.resolveInst(extra.ptr); + const lhs = try sema.resolveInst(extra.lhs); + const rhs = try sema.resolveInst(extra.rhs); + const ptr = try sema.resolveInst(extra.ptr); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); @@ -10788,7 +10790,7 @@ fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.In const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const ptr_src: LazySrcLoc = .{ .node_offset_deref_ptr = inst_data.src_node }; - const ptr = sema.resolveInst(inst_data.operand); + const ptr = try sema.resolveInst(inst_data.operand); return sema.analyzeLoad(block, src, ptr, ptr_src); } @@ -10880,7 +10882,7 @@ fn zirAsm( const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i); extra_i = input.end; - const uncasted_arg = sema.resolveInst(input.data.operand); + const uncasted_arg = try sema.resolveInst(input.data.operand); const uncasted_arg_ty = sema.typeOf(uncasted_arg); switch (uncasted_arg_ty.zigTypeTag()) { .ComptimeInt => arg.* = try sema.coerce(block, Type.initTag(.usize), uncasted_arg, src), @@ -10969,8 +10971,8 @@ fn zirCmpEq( const src: LazySrcLoc = inst_data.src(); const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; - const lhs = sema.resolveInst(extra.lhs); - const rhs = sema.resolveInst(extra.rhs); + const lhs = try sema.resolveInst(extra.lhs); + const rhs = try sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); @@ -11081,8 +11083,8 @@ fn zirCmp( const src: LazySrcLoc = inst_data.src(); const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; - const lhs = sema.resolveInst(extra.lhs); - const rhs = sema.resolveInst(extra.rhs); + const lhs = try sema.resolveInst(extra.lhs); + const rhs = try sema.resolveInst(extra.rhs); return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, false); } @@ -11276,7 +11278,7 @@ fn zirClosureCapture( // fn foo(x: anytype) void { const S = struct {field: @TypeOf(x)}; } // ...in which case the closure_capture instruction has access to a runtime // value only. In such case we preserve the type and use a dummy runtime value. - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const val = (try sema.resolveMaybeUndefValAllowVariables(block, src, operand)) orelse Value.initTag(.generic_poison); @@ -12249,7 +12251,7 @@ fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. _ = block; const zir_datas = sema.code.instructions.items(.data); const inst_data = zir_datas[inst].un_node; - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); return sema.addType(operand_ty); } @@ -12282,7 +12284,7 @@ fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); const res_ty = try sema.log2IntType(block, operand_ty, src); return sema.addType(res_ty); @@ -12364,8 +12366,7 @@ fn zirTypeofPeer( defer sema.gpa.free(inst_list); for (args) |arg_ref, i| { - inst_list[i] = sema.resolveInst(arg_ref); - if (sema.typeOf(inst_list[i]).tag() == .generic_poison) return error.GenericPoison; + inst_list[i] = try sema.resolveInst(arg_ref); } const result_type = try sema.resolvePeerTypes(block, src, inst_list, .{ .typeof_builtin_call_node_offset = extra.data.src_node }); @@ -12379,7 +12380,7 @@ fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand_src = src; // TODO put this on the operand, not the `!` - const uncasted_operand = sema.resolveInst(inst_data.operand); + const uncasted_operand = try sema.resolveInst(inst_data.operand); const operand = try sema.coerce(block, Type.bool, uncasted_operand, operand_src); if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { @@ -12405,7 +12406,7 @@ fn zirBoolBr( const datas = sema.code.instructions.items(.data); const inst_data = datas[inst].bool_br; - const lhs = sema.resolveInst(inst_data.lhs); + const lhs = try sema.resolveInst(inst_data.lhs); const lhs_src = sema.src; const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); const body = sema.code.extra[extra.end..][0..extra.data.body_len]; @@ -12490,7 +12491,7 @@ fn zirIsNonNull( const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); return sema.analyzeIsNull(block, src, operand, true); } @@ -12504,7 +12505,7 @@ fn zirIsNonNullPtr( const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - const ptr = sema.resolveInst(inst_data.operand); + const ptr = try sema.resolveInst(inst_data.operand); if ((try sema.resolveMaybeUndefVal(block, src, ptr)) == null) { return block.addUnOp(.is_non_null_ptr, ptr); } @@ -12517,7 +12518,7 @@ fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); return sema.analyzeIsNonErr(block, inst_data.src(), operand); } @@ -12527,7 +12528,7 @@ fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - const ptr = sema.resolveInst(inst_data.operand); + const ptr = try sema.resolveInst(inst_data.operand); const loaded = try sema.analyzeLoad(block, src, ptr, src); return sema.analyzeIsNonErr(block, src, loaded); } @@ -12548,7 +12549,7 @@ fn zirCondbr( const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len]; const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; - const uncasted_cond = sema.resolveInst(extra.data.condition); + const uncasted_cond = try sema.resolveInst(extra.data.condition); const cond = try sema.coerce(parent_block, Type.bool, uncasted_cond, cond_src); if (try sema.resolveDefinedValue(parent_block, src, cond)) |cond_val| { @@ -12656,7 +12657,7 @@ fn addRuntimeBreak(sema: *Sema, child_block: *Block, break_data: BreakData) !voi break :blk labeled_block; }; - const operand = sema.resolveInst(break_data.operand); + const operand = try sema.resolveInst(break_data.operand); const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand); try labeled_block.label.merges.results.append(sema.gpa, operand); try labeled_block.label.merges.br_list.append(sema.gpa, Air.refToIndex(br_ref).?); @@ -12707,7 +12708,7 @@ fn zirRetTok( defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].un_tok; - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const src = inst_data.src(); return sema.analyzeRet(block, operand, src); @@ -12718,7 +12719,7 @@ fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const src = inst_data.src(); return sema.analyzeRet(block, operand, src); @@ -12730,7 +12731,7 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - const ret_ptr = sema.resolveInst(inst_data.operand); + const ret_ptr = try sema.resolveInst(inst_data.operand); if (block.is_comptime or block.inlining != null) { const operand = try sema.analyzeLoad(block, src, ret_ptr, src); @@ -12853,7 +12854,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const abi_align: u32 = if (inst_data.flags.has_align) blk: { const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); extra_i += 1; - const coerced = try sema.coerce(block, Type.u32, sema.resolveInst(ref), src); + const coerced = try sema.coerce(block, Type.u32, try sema.resolveInst(ref), src); const val = try sema.resolveConstValue(block, src, coerced); // Check if this happens to be the lazy alignment of our element type, in // which case we can make this 0 without resolving it. @@ -12980,7 +12981,7 @@ fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data; const union_ty = try sema.resolveType(block, ty_src, extra.union_type); const field_name = try sema.resolveConstString(block, field_src, extra.field_name); - const init = sema.resolveInst(extra.init); + const init = try sema.resolveInst(extra.init); return sema.unionInit(block, init, init_src, union_ty, ty_src, field_name, field_src); } @@ -13070,7 +13071,7 @@ fn zirStructInit( return sema.failWithOwnedErrorMsg(block, msg); } found_fields[field_index] = item.data.field_type; - field_inits[field_index] = sema.resolveInst(item.data.init); + field_inits[field_index] = try sema.resolveInst(item.data.init); } var root_msg: ?*Module.ErrorMsg = null; @@ -13107,7 +13108,7 @@ fn zirStructInit( const field_name = sema.code.nullTerminatedString(field_type_extra.name_start); const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src); - const init_inst = sema.resolveInst(item.data.init); + const init_inst = try sema.resolveInst(item.data.init); if (try sema.resolveMaybeUndefVal(block, field_src, init_inst)) |val| { const tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index); return sema.addConstantMaybeRef( @@ -13215,7 +13216,7 @@ fn zirStructInitAnon( extra_index = item.end; names[i] = sema.code.nullTerminatedString(item.data.field_name); - const init = sema.resolveInst(item.data.init); + const init = try sema.resolveInst(item.data.init); field_ty.* = sema.typeOf(init); const init_src = src; // TODO better source location if (try sema.resolveMaybeUndefVal(block, init_src, init)) |init_val| { @@ -13260,7 +13261,7 @@ fn zirStructInitAnon( .pointee_type = field_ty, }); if (values[i].tag() == .unreachable_value) { - const init = sema.resolveInst(item.data.init); + const init = try sema.resolveInst(item.data.init); const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); _ = try block.addBinOp(.store, field_ptr, init); } @@ -13274,7 +13275,7 @@ fn zirStructInitAnon( for (types) |_, i| { const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); extra_index = item.end; - element_refs[i] = sema.resolveInst(item.data.init); + element_refs[i] = try sema.resolveInst(item.data.init); } return block.addAggregateInit(tuple_ty, element_refs); @@ -13298,7 +13299,7 @@ fn zirArrayInit( const resolved_args = try gpa.alloc(Air.Inst.Ref, args.len); defer gpa.free(resolved_args); - for (args) |arg, i| resolved_args[i] = sema.resolveInst(arg); + for (args) |arg, i| resolved_args[i] = try sema.resolveInst(arg); const elem_ty = sema.typeOf(resolved_args[0]); const array_ty = blk: { @@ -13382,7 +13383,7 @@ fn zirArrayInitAnon( const opt_runtime_src = rs: { var runtime_src: ?LazySrcLoc = null; for (operands) |operand, i| { - const elem = sema.resolveInst(operand); + const elem = try sema.resolveInst(operand); types[i] = sema.typeOf(elem); const operand_src = src; // TODO better source location if (try sema.resolveMaybeUndefVal(block, operand_src, elem)) |val| { @@ -13423,7 +13424,7 @@ fn zirArrayInitAnon( }); if (values[i].tag() == .unreachable_value) { const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); - _ = try block.addBinOp(.store, field_ptr, sema.resolveInst(operand)); + _ = try block.addBinOp(.store, field_ptr, try sema.resolveInst(operand)); } } @@ -13432,7 +13433,7 @@ fn zirArrayInitAnon( const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len); for (operands) |operand, i| { - element_refs[i] = sema.resolveInst(operand); + element_refs[i] = try sema.resolveInst(operand); } return block.addAggregateInit(tuple_ty, element_refs); @@ -13575,7 +13576,7 @@ fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air fn zirBoolToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { if (val.isUndef()) return sema.addConstUndef(Type.initTag(.u1)); const bool_ints = [2]Air.Inst.Ref{ .zero, .one }; @@ -13588,7 +13589,7 @@ fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); _ = src; - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { @@ -13612,7 +13613,7 @@ fn zirUnaryMath( defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_ty = sema.typeOf(operand); const target = sema.mod.getTarget(); @@ -13672,7 +13673,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const src = inst_data.src(); - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); const mod = sema.mod; @@ -13730,7 +13731,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const type_info_ty = try sema.resolveBuiltinTypeFields(block, src, "Type"); - const uncasted_operand = sema.resolveInst(inst_data.operand); + const uncasted_operand = try sema.resolveInst(inst_data.operand); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src); const val = try sema.resolveConstValue(block, operand_src, type_info); @@ -14405,7 +14406,7 @@ fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const dest_ty = try sema.resolveType(block, ty_src, extra.lhs); - const operand = sema.resolveInst(extra.rhs); + const operand = try sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); _ = try sema.checkIntType(block, ty_src, dest_ty); @@ -14426,7 +14427,7 @@ fn zirIntToFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const dest_ty = try sema.resolveType(block, ty_src, extra.lhs); - const operand = sema.resolveInst(extra.rhs); + const operand = try sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); try sema.checkFloatType(block, ty_src, dest_ty); @@ -14449,7 +14450,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; - const operand_res = sema.resolveInst(extra.rhs); + const operand_res = try sema.resolveInst(extra.rhs); const operand_coerced = try sema.coerce(block, Type.usize, operand_res, operand_src); const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; @@ -14505,7 +14506,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); - const operand = sema.resolveInst(extra.rhs); + const operand = try sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); try sema.checkErrorSetType(block, dest_ty_src, dest_ty); try sema.checkErrorSetType(block, operand_src, operand_ty); @@ -14593,7 +14594,7 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); - const operand = sema.resolveInst(extra.rhs); + const operand = try sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); const target = sema.mod.getTarget(); @@ -14653,7 +14654,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const dest_scalar_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); - const operand = sema.resolveInst(extra.rhs); + const operand = try sema.resolveInst(extra.rhs); const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_scalar_ty); const operand_ty = sema.typeOf(operand); const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); @@ -14736,7 +14737,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const align_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const dest_align = try sema.resolveAlign(block, align_src, extra.lhs); - const ptr = sema.resolveInst(extra.rhs); + const ptr = try sema.resolveInst(extra.rhs); const ptr_ty = sema.typeOf(ptr); // TODO in addition to pointers, this instruction is supposed to work for @@ -14770,7 +14771,7 @@ fn zirBitCount( ) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); _ = try checkIntOrVector(sema, block, operand, operand_src); const target = sema.mod.getTarget(); @@ -14822,7 +14823,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const inst_data = sema.code.instructions.items(.data)[inst].un_node; const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); const scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); const target = sema.mod.getTarget(); @@ -14879,7 +14880,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; - const operand = sema.resolveInst(inst_data.operand); + const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); _ = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); @@ -15365,7 +15366,7 @@ fn resolveExportOptions( zir_ref: Zir.Inst.Ref, ) CompileError!std.builtin.ExportOptions { const export_options_ty = try sema.getBuiltinType(block, src, "ExportOptions"); - const air_ref = sema.resolveInst(zir_ref); + const air_ref = try sema.resolveInst(zir_ref); const options = try sema.coerce(block, export_options_ty, air_ref, src); const name_operand = try sema.fieldVal(block, src, options, "name", src); @@ -15410,7 +15411,7 @@ fn resolveBuiltinEnum( comptime name: []const u8, ) CompileError!@field(std.builtin, name) { const ty = try sema.getBuiltinType(block, src, name); - const air_ref = sema.resolveInst(zir_ref); + const air_ref = try sema.resolveInst(zir_ref); const coerced = try sema.coerce(block, ty, air_ref, src); const val = try sema.resolveConstValue(block, src, coerced); return val.toEnum(@field(std.builtin, name)); @@ -15451,7 +15452,7 @@ fn zirCmpxchg( const success_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node }; const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = inst_data.src_node }; // zig fmt: on - const expected_value = sema.resolveInst(extra.expected_value); + const expected_value = try sema.resolveInst(extra.expected_value); const elem_ty = sema.typeOf(expected_value); if (elem_ty.zigTypeTag() == .Float) { return sema.fail( @@ -15461,9 +15462,9 @@ fn zirCmpxchg( .{elem_ty.fmt(sema.mod)}, ); } - const uncasted_ptr = sema.resolveInst(extra.ptr); + const uncasted_ptr = try sema.resolveInst(extra.ptr); const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); - const new_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.new_value), new_value_src); + const new_value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.new_value), new_value_src); const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order); const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order); @@ -15531,7 +15532,7 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const len_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const scalar_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; const len = @intCast(u32, try sema.resolveInt(block, len_src, extra.lhs, Type.u32)); - const scalar = sema.resolveInst(extra.rhs); + const scalar = try sema.resolveInst(extra.rhs); const scalar_ty = sema.typeOf(scalar); try sema.checkVectorElemType(block, scalar_src, scalar_ty); const vector_ty = try Type.Tag.vector.create(sema.arena, .{ @@ -15557,7 +15558,7 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const op_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, "ReduceOp"); - const operand = sema.resolveInst(extra.rhs); + const operand = try sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); const target = sema.mod.getTarget(); @@ -15629,9 +15630,9 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); try sema.checkVectorElemType(block, elem_ty_src, elem_ty); - var a = sema.resolveInst(extra.a); - var b = sema.resolveInst(extra.b); - var mask = sema.resolveInst(extra.mask); + var a = try sema.resolveInst(extra.a); + var b = try sema.resolveInst(extra.b); + var mask = try sema.resolveInst(extra.mask); var mask_ty = sema.typeOf(mask); const mask_len = switch (sema.typeOf(mask).zigTypeTag()) { @@ -15823,7 +15824,7 @@ fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); try sema.checkVectorElemType(block, elem_ty_src, elem_ty); - const pred_uncoerced = sema.resolveInst(extra.pred); + const pred_uncoerced = try sema.resolveInst(extra.pred); const pred_ty = sema.typeOf(pred_uncoerced); const vec_len_u64 = switch (try pred_ty.zigTypeTagOrPoison()) { @@ -15836,8 +15837,8 @@ fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const pred = try sema.coerce(block, bool_vec_ty, pred_uncoerced, pred_src); const vec_ty = try Type.vector(sema.arena, vec_len, elem_ty); - const a = try sema.coerce(block, vec_ty, sema.resolveInst(extra.a), a_src); - const b = try sema.coerce(block, vec_ty, sema.resolveInst(extra.b), b_src); + const a = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.a), a_src); + const b = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.b), b_src); const maybe_pred = try sema.resolveMaybeUndefVal(block, pred_src, pred); const maybe_a = try sema.resolveMaybeUndefVal(block, a_src, a); @@ -15909,7 +15910,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; // zig fmt: on const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); - const uncasted_ptr = sema.resolveInst(extra.ptr); + const uncasted_ptr = try sema.resolveInst(extra.ptr); const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true); const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); @@ -15956,9 +15957,9 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node }; // zig fmt: on - const operand = sema.resolveInst(extra.operand); + const operand = try sema.resolveInst(extra.operand); const elem_ty = sema.typeOf(operand); - const uncasted_ptr = sema.resolveInst(extra.ptr); + const uncasted_ptr = try sema.resolveInst(extra.ptr); const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation); @@ -16039,9 +16040,9 @@ fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; // zig fmt: on - const operand = sema.resolveInst(extra.operand); + const operand = try sema.resolveInst(extra.operand); const elem_ty = sema.typeOf(operand); - const uncasted_ptr = sema.resolveInst(extra.ptr); + const uncasted_ptr = try sema.resolveInst(extra.ptr); const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); @@ -16072,10 +16073,10 @@ fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const mulend2_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; const addend_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node }; - const addend = sema.resolveInst(extra.addend); + const addend = try sema.resolveInst(extra.addend); const ty = sema.typeOf(addend); - const mulend1 = try sema.coerce(block, ty, sema.resolveInst(extra.mulend1), mulend1_src); - const mulend2 = try sema.coerce(block, ty, sema.resolveInst(extra.mulend2), mulend2_src); + const mulend1 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend1), mulend1_src); + const mulend2 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend2), mulend2_src); const target = sema.mod.getTarget(); @@ -16139,9 +16140,9 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const call_src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data; - var func = sema.resolveInst(extra.callee); - const options = sema.resolveInst(extra.options); - const args = sema.resolveInst(extra.args); + var func = try sema.resolveInst(extra.callee); + const options = try sema.resolveInst(extra.options); + const args = try sema.resolveInst(extra.args); const wanted_modifier: std.builtin.CallOptions.Modifier = modifier: { const call_options_ty = try sema.getBuiltinType(block, options_src, "CallOptions"); @@ -16231,7 +16232,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const struct_ty = try sema.resolveType(block, ty_src, extra.parent_type); const field_name = try sema.resolveConstString(block, name_src, extra.field_name); - const field_ptr = sema.resolveInst(extra.field_ptr); + const field_ptr = try sema.resolveInst(extra.field_ptr); const field_ptr_ty = sema.typeOf(field_ptr); if (struct_ty.zigTypeTag() != .Struct) { @@ -16296,8 +16297,8 @@ fn zirMinMax( const src = inst_data.src(); const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; - const lhs = sema.resolveInst(extra.lhs); - const rhs = sema.resolveInst(extra.rhs); + const lhs = try sema.resolveInst(extra.lhs); + const rhs = try sema.resolveInst(extra.rhs); try sema.checkNumericType(block, lhs_src, sema.typeOf(lhs)); try sema.checkNumericType(block, rhs_src, sema.typeOf(rhs)); return sema.analyzeMinMax(block, src, lhs, rhs, air_tag, lhs_src, rhs_src); @@ -16364,7 +16365,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; - const dest_ptr = sema.resolveInst(extra.dest); + const dest_ptr = try sema.resolveInst(extra.dest); const dest_ptr_ty = sema.typeOf(dest_ptr); try sema.checkPtrOperand(block, dest_src, dest_ptr_ty); @@ -16372,7 +16373,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)}); } - const uncasted_src_ptr = sema.resolveInst(extra.source); + const uncasted_src_ptr = try sema.resolveInst(extra.source); const uncasted_src_ptr_ty = sema.typeOf(uncasted_src_ptr); try sema.checkPtrOperand(block, src_src, uncasted_src_ptr_ty); const src_ptr_info = uncasted_src_ptr_ty.ptrInfo().data; @@ -16386,7 +16387,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void .size = .Many, }); const src_ptr = try sema.coerce(block, wanted_src_ptr_ty, uncasted_src_ptr, src_src); - const len = try sema.coerce(block, Type.usize, sema.resolveInst(extra.byte_count), len_src); + const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src); const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: { if (!dest_ptr_val.isComptimeMutablePtr()) break :rs dest_src; @@ -16421,15 +16422,15 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; - const dest_ptr = sema.resolveInst(extra.dest); + const dest_ptr = try sema.resolveInst(extra.dest); const dest_ptr_ty = sema.typeOf(dest_ptr); try sema.checkPtrOperand(block, dest_src, dest_ptr_ty); if (dest_ptr_ty.isConstPtr()) { return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)}); } const elem_ty = dest_ptr_ty.elemType2(); - const value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.byte), value_src); - const len = try sema.coerce(block, Type.usize, sema.resolveInst(extra.byte_count), len_src); + const value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.byte), value_src); + const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src); const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |ptr_val| rs: { if (!ptr_val.isComptimeMutablePtr()) break :rs dest_src; @@ -16523,7 +16524,7 @@ fn zirVarExtended( const uncasted_init: Air.Inst.Ref = if (small.has_init) blk: { const init_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - break :blk sema.resolveInst(init_ref); + break :blk try sema.resolveInst(init_ref); } else .none; const have_ty = extra.data.var_type != .none; @@ -16671,7 +16672,7 @@ fn zirCDefine( const val_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; const name = try sema.resolveConstString(block, name_src, extra.lhs); - const rhs = sema.resolveInst(extra.rhs); + const rhs = try sema.resolveInst(extra.rhs); if (sema.typeOf(rhs).zigTypeTag() != .Void) { const value = try sema.resolveConstString(block, val_src, extra.rhs); try block.c_import_buf.?.writer().print("#define {s} {s}\n", .{ name, value }); @@ -16720,7 +16721,7 @@ fn zirWasmMemoryGrow( } const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.lhs, Type.u32)); - const delta = try sema.coerce(block, Type.u32, sema.resolveInst(extra.rhs), delta_src); + const delta = try sema.coerce(block, Type.u32, try sema.resolveInst(extra.rhs), delta_src); try sema.requireRuntimeBlock(block, builtin_src); return block.addInst(.{ @@ -16741,9 +16742,9 @@ fn zirPrefetch( const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const opts_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; const options_ty = try sema.getBuiltinType(block, opts_src, "PrefetchOptions"); - const ptr = sema.resolveInst(extra.lhs); + const ptr = try sema.resolveInst(extra.lhs); try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr)); - const options = try sema.coerce(block, options_ty, sema.resolveInst(extra.rhs), opts_src); + const options = try sema.coerce(block, options_ty, try sema.resolveInst(extra.rhs), opts_src); const target = sema.mod.getTarget(); const rw = try sema.fieldVal(block, opts_src, options, "rw", opts_src); @@ -16784,7 +16785,7 @@ fn zirBuiltinExtern( const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; var ty = try sema.resolveType(block, ty_src, extra.lhs); - const options_inst = sema.resolveInst(extra.rhs); + const options_inst = try sema.resolveInst(extra.rhs); const mod = sema.mod; const options = options: { @@ -22857,7 +22858,7 @@ fn semaStructFields( if (has_default) { const default_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); extra_index += 1; - const default_inst = sema.resolveInst(default_ref); + const default_inst = try sema.resolveInst(default_ref); // TODO: if we need to report an error here, use a source location // that points to this default value expression rather than the struct. // But only resolve the source location if we need to emit a compile error. @@ -23041,7 +23042,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil const tag_ref: Zir.Inst.Ref = if (has_tag) blk: { const tag_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); extra_index += 1; - break :blk sema.resolveInst(tag_ref); + break :blk try sema.resolveInst(tag_ref); } else .none; if (enum_value_map) |map| { @@ -23666,7 +23667,7 @@ fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref { fn addBool(sema: *Sema, ty: Type, boolean: bool) CompileError!Air.Inst.Ref { return switch (ty.zigTypeTag()) { .Vector => sema.addConstant(ty, try Value.Tag.repeated.create(sema.arena, Value.makeBool(boolean))), - .Bool => sema.resolveInst(if (boolean) .bool_true else .bool_false), + .Bool => try sema.resolveInst(if (boolean) .bool_true else .bool_false), else => unreachable, }; } diff --git a/test/behavior/sizeof_and_typeof.zig b/test/behavior/sizeof_and_typeof.zig index d1f26c8167..4421b59f96 100644 --- a/test/behavior/sizeof_and_typeof.zig +++ b/test/behavior/sizeof_and_typeof.zig @@ -290,3 +290,13 @@ test "hardcoded address in typeof expression" { try expect(S.func() == 0); comptime try expect(S.func() == 0); } + +test "array access of generic param in typeof expression" { + const S = struct { + fn first(comptime items: anytype) @TypeOf(items[0]) { + return items[0]; + } + }; + try expect(S.first("a") == 'a'); + comptime try expect(S.first("a") == 'a'); +} From 97816e3cb826b64df578baa72781caba93252fd0 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 24 May 2022 20:29:15 +0200 Subject: [PATCH 1644/2031] aarch64: check lo/cc flag for unsigned sub_with_overflow With this change, we are now correctly lowering `sub_with_overflow` for signed and unsigned integers of register-sized integers (32- or 64-bit precisely). We also match LLVM's behavior and so, the condition flags we now set are: * unsigned: - `add_with_overflow`: `hs`/`cs` (carry set) - `sub_with_overflow`: `lo`/`cc` (carry clear) * signed: - `add_with_overflow`/`sub_with_overflow`: `vs` (overflow) --- src/arch/aarch64/CodeGen.zig | 93 ++++++++++++------------------------ 1 file changed, 31 insertions(+), 62 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 2a71f3138a..e0c36e70f9 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -128,18 +128,11 @@ const MCValue = union(enum) { register: Register, /// The value is a tuple { wrapped: u32, overflow: u1 } where /// wrapped is stored in the register and the overflow bit is - /// stored in the C flag of the CPSR. + /// stored in the C (signed) or V (unsigned) flag of the CPSR. /// /// This MCValue is only generated by a add_with_overflow or - /// sub_with_overflow instruction operating on u32. - register_c_flag: Register, - /// The value is a tuple { wrapped: i32, overflow: u1 } where - /// wrapped is stored in the register and the overflow bit is - /// stored in the V flag of the CPSR. - /// - /// This MCValue is only generated by a add_with_overflow or - /// sub_with_overflow instruction operating on i32. - register_v_flag: Register, + /// sub_with_overflow instruction operating on 32- or 64-bit values. + register_with_overflow: struct { reg: Register, flag: bits.Instruction.Condition }, /// The value is in memory at a hard-coded address. /// /// If the type is a pointer, it means the pointer address is at @@ -760,10 +753,8 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { .register => |reg| { self.register_manager.freeReg(reg); }, - .register_c_flag, - .register_v_flag, - => |reg| { - self.register_manager.freeReg(reg); + .register_with_overflow => |rwo| { + self.register_manager.freeReg(rwo.reg); self.compare_flags_inst = null; }, .compare_flags_signed, .compare_flags_unsigned => { @@ -905,10 +896,8 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); const reg_mcv = self.getResolvedInstValue(inst); switch (reg_mcv) { - .register, - .register_c_flag, - .register_v_flag, - => |r| assert(reg.id() == r.id()), + .register => |r| assert(reg.id() == r.id()), + .register_with_overflow => |rwo| assert(rwo.reg.id() == reg.id()), else => unreachable, // not a register } const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; @@ -925,9 +914,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { .compare_flags_signed, .compare_flags_unsigned, => try self.allocRegOrMem(inst_to_save, true), - .register_c_flag, - .register_v_flag, - => try self.allocRegOrMem(inst_to_save, false), + .register_with_overflow => try self.allocRegOrMem(inst_to_save, false), else => unreachable, // mcv doesn't occupy the compare flags }; @@ -942,9 +929,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { // TODO consolidate with register manager and spillInstruction // this call should really belong in the register manager! switch (mcv) { - .register_c_flag, - .register_v_flag, - => |reg| self.register_manager.freeReg(reg), + .register_with_overflow => |rwo| self.register_manager.freeReg(rwo.reg), else => {}, } } @@ -1932,14 +1917,18 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { } }; - if (tag == .sub_with_overflow) { - break :result MCValue{ .register_v_flag = dest.register }; - } - - switch (int_info.signedness) { - .unsigned => break :result MCValue{ .register_c_flag = dest.register }, - .signed => break :result MCValue{ .register_v_flag = dest.register }, - } + const flag: bits.Instruction.Condition = switch (int_info.signedness) { + .unsigned => switch (tag) { + .add_with_overflow => bits.Instruction.Condition.cs, + .sub_with_overflow => bits.Instruction.Condition.cc, + else => unreachable, + }, + .signed => .vs, + }; + break :result MCValue{ .register_with_overflow = .{ + .reg = dest.register, + .flag = flag, + } }; }, else => return self.fail("TODO overflow operations on integers > u32/i32", .{}), } @@ -2648,8 +2637,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .dead => unreachable, .compare_flags_unsigned, .compare_flags_signed, - .register_c_flag, - .register_v_flag, + .register_with_overflow, => unreachable, // cannot hold an address .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), @@ -2871,8 +2859,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .dead => unreachable, .compare_flags_unsigned, .compare_flags_signed, - .register_c_flag, - .register_v_flag, + .register_with_overflow, => unreachable, // cannot hold an address .immediate => |imm| { try self.setRegOrMem(value_ty, .{ .memory = imm }, value); @@ -2993,13 +2980,11 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { .memory => |addr| { break :result MCValue{ .memory = addr + struct_field_offset }; }, - .register_c_flag, - .register_v_flag, - => |reg| { + .register_with_overflow => |rwo| { switch (index) { 0 => { // get wrapped value: return register - break :result MCValue{ .register = reg }; + break :result MCValue{ .register = rwo.reg }; }, 1 => { // TODO return special MCValue condition flags @@ -3008,17 +2993,11 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const raw_dest_reg = try self.register_manager.allocReg(null, gp); const dest_reg = raw_dest_reg.to32(); - // C flag: cset reg, cs - // V flag: cset reg, vs _ = try self.addInst(.{ .tag = .cset, .data = .{ .r_cond = .{ .rd = dest_reg, - .cond = switch (mcv) { - .register_c_flag => .cs, - .register_v_flag => .vs, - else => unreachable, - }, + .cond = rwo.flag, } }, }); @@ -4073,14 +4052,12 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), } }, - .register_c_flag, - .register_v_flag, - => |reg| { - const reg_lock = self.register_manager.lockReg(reg); + .register_with_overflow => |rwo| { + const reg_lock = self.register_manager.lockReg(rwo.reg); defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg); const wrapped_ty = ty.structFieldType(0); - try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg }); + try self.genSetStack(wrapped_ty, stack_offset, .{ .register = rwo.reg }); const overflow_bit_ty = ty.structFieldType(1); const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*)); @@ -4090,17 +4067,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro @intCast(u32, overflow_bit_ty.abiSize(self.target.*)), ); - // C flag: cset reg, cs - // V flag: cset reg, vs _ = try self.addInst(.{ .tag = .cset, .data = .{ .r_cond = .{ .rd = cond_reg, - .cond = switch (mcv) { - .register_c_flag => .cs, - .register_v_flag => .vs, - else => unreachable, - }, + .cond = rwo.flag, } }, }); @@ -4270,9 +4241,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .data = .{ .rr = .{ .rd = reg, .rn = src_reg } }, }); }, - .register_c_flag, - .register_v_flag, - => unreachable, // doesn't fit into a register + .register_with_overflow => unreachable, // doesn't fit into a register .got_load, .direct_load, => |sym_index| { From ab88165326abfd81c5046e8c064bd6603198ed94 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 25 May 2022 17:27:21 -0700 Subject: [PATCH 1645/2031] Sema: generic function instantiations inherit branch quota --- src/Module.zig | 5 +++++ src/Sema.zig | 10 +++++++--- test/behavior/eval.zig | 2 -- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 24495d8591..bc84d84ae8 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1476,6 +1476,10 @@ pub const Fn = struct { lbrace_column: u16, rbrace_column: u16, + /// When a generic function is instantiated, this value is inherited from the + /// active Sema context. Importantly, this value is also updated when an existing + /// generic function instantiation is found and called. + branch_quota: u32, state: Analysis, is_cold: bool = false, is_noinline: bool = false, @@ -4891,6 +4895,7 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air { .func = func, .fn_ret_ty = decl.ty.fnReturnType(), .owner_func = func, + .branch_quota = @maximum(func.branch_quota, Sema.default_branch_quota), }; defer sema.deinit(); diff --git a/src/Sema.zig b/src/Sema.zig index 0b50d9199b..80184cb287 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -38,7 +38,7 @@ func: ?*Module.Fn, /// generic function which uses a type expression for the return type. /// The type will be `void` in the case that `func` is `null`. fn_ret_ty: Type, -branch_quota: u32 = 1000, +branch_quota: u32 = default_branch_quota, branch_count: u32 = 0, /// Populated when returning `error.ComptimeBreak`. Used to communicate the /// break instruction up the stack to find the corresponding Block. @@ -102,6 +102,8 @@ const Package = @import("Package.zig"); const crash_report = @import("crash_report.zig"); const build_options = @import("build_options"); +pub const default_branch_quota = 1000; + pub const InstMap = std.AutoHashMapUnmanaged(Zir.Inst.Index, Air.Inst.Ref); /// This is the context needed to semantically analyze ZIR instructions and @@ -3752,8 +3754,7 @@ fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const quota = @intCast(u32, try sema.resolveInt(block, src, inst_data.operand, Type.u32)); - if (sema.branch_quota < quota) - sema.branch_quota = quota; + sema.branch_quota = @maximum(sema.branch_quota, quota); } fn zirStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { @@ -5679,6 +5680,8 @@ fn instantiateGenericCall( break :callee new_func; } else gop.key_ptr.*; + callee.branch_quota = @maximum(callee.branch_quota, sema.branch_quota); + const callee_inst = try sema.analyzeDeclVal(block, func_src, callee.owner_decl); // Make a runtime call to the new function, making sure to omit the comptime args. @@ -6773,6 +6776,7 @@ fn funcCommon( .lbrace_column = @truncate(u16, src_locs.columns), .rbrace_column = @truncate(u16, src_locs.columns >> 16), .param_names = param_names, + .branch_quota = default_branch_quota, }; if (maybe_inferred_error_set_node) |node| { new_func.inferred_error_sets.prepend(node); diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 383c32172c..80d39e00d7 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -704,8 +704,6 @@ test "call method with comptime pass-by-non-copying-value self parameter" { } test "setting backward branch quota just before a generic fn call" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - @setEvalBranchQuota(1001); loopNTimes(1001); } From b82081e7092a6198e6f8c65524a5829e2e08527b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 25 May 2022 20:29:12 -0700 Subject: [PATCH 1646/2031] Sema: implement array concatenation with runtime operands --- src/Sema.zig | 186 ++++++++++++++++++++++++++++------------- test/behavior/eval.zig | 44 ++++++++-- 2 files changed, 164 insertions(+), 66 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 80184cb287..648174dcd1 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9255,6 +9255,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const rhs = try sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); + const src = inst_data.src(); if (lhs_ty.isTuple() and rhs_ty.isTuple()) { return sema.analyzeTupleCat(block, inst_data.src_node, lhs, rhs); @@ -9267,73 +9268,138 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty.fmt(sema.mod)}); const rhs_info = (try sema.getArrayCatInfo(block, rhs_src, rhs)) orelse return sema.fail(block, rhs_src, "expected array, found '{}'", .{rhs_ty.fmt(sema.mod)}); - if (!lhs_info.elem_type.eql(rhs_info.elem_type, sema.mod)) { - return sema.fail(block, rhs_src, "expected array of type '{}', found '{}'", .{ - lhs_info.elem_type.fmt(sema.mod), rhs_ty.fmt(sema.mod), + + const resolved_elem_ty = t: { + var trash_block = block.makeSubBlock(); + trash_block.is_comptime = false; + defer trash_block.instructions.deinit(sema.gpa); + + const instructions = [_]Air.Inst.Ref{ + try trash_block.addBitCast(lhs_info.elem_type, .void_value), + try trash_block.addBitCast(rhs_info.elem_type, .void_value), + }; + break :t try sema.resolvePeerTypes(block, src, &instructions, .{ + .override = &[_]LazySrcLoc{ lhs_src, rhs_src }, }); - } + }; - // When there is a sentinel mismatch, no sentinel on the result. The type system - // will catch this if it is a problem. - var res_sent: ?Value = null; - if (rhs_info.sentinel != null and lhs_info.sentinel != null) { - if (rhs_info.sentinel.?.eql(lhs_info.sentinel.?, lhs_info.elem_type, sema.mod)) { - res_sent = lhs_info.sentinel.?; - } - } - - if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { - if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| { - const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); - const rhs_len = try sema.usizeCast(block, lhs_src, rhs_info.len); - const final_len = lhs_len + rhs_len; - const final_len_including_sent = final_len + @boolToInt(res_sent != null); - const lhs_single_ptr = lhs_ty.isSinglePointer(); - const rhs_single_ptr = rhs_ty.isSinglePointer(); - const lhs_sub_val = if (lhs_single_ptr) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val; - const rhs_sub_val = if (rhs_single_ptr) (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).? else rhs_val; - var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded); - defer anon_decl.deinit(); - - const buf = try anon_decl.arena().alloc(Value, final_len_including_sent); - { - var i: usize = 0; - while (i < lhs_len) : (i += 1) { - const val = try lhs_sub_val.elemValue(sema.mod, sema.arena, i); - buf[i] = try val.copy(anon_decl.arena()); + // When there is a sentinel mismatch, no sentinel on the result. + // Otherwise, use the sentinel value provided by either operand, + // coercing it to the peer-resolved element type. + const res_sent_val: ?Value = s: { + if (lhs_info.sentinel) |lhs_sent_val| { + const lhs_sent = try sema.addConstant(lhs_info.elem_type, lhs_sent_val); + if (rhs_info.sentinel) |rhs_sent_val| { + const rhs_sent = try sema.addConstant(rhs_info.elem_type, rhs_sent_val); + const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src); + const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src); + const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted); + const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted); + if (try sema.valuesEqual(block, src, lhs_sent_casted_val, rhs_sent_casted_val, resolved_elem_ty)) { + break :s lhs_sent_casted_val; + } else { + break :s null; } - } - { - var i: usize = 0; - while (i < rhs_len) : (i += 1) { - const val = try rhs_sub_val.elemValue(sema.mod, sema.arena, i); - buf[lhs_len + i] = try val.copy(anon_decl.arena()); - } - } - const ty = if (res_sent) |rs| ty: { - buf[final_len] = try rs.copy(anon_decl.arena()); - break :ty try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ - .len = final_len, - .elem_type = try lhs_info.elem_type.copy(anon_decl.arena()), - .sentinel = try rs.copy(anon_decl.arena()), - }); - } else try Type.Tag.array.create(anon_decl.arena(), .{ - .len = final_len, - .elem_type = try lhs_info.elem_type.copy(anon_decl.arena()), - }); - const val = try Value.Tag.aggregate.create(anon_decl.arena(), buf); - const decl = try anon_decl.finish(ty, val, 0); - if (lhs_ty.zigTypeTag() == .Pointer or rhs_ty.zigTypeTag() == .Pointer) { - return sema.analyzeDeclRef(decl); } else { - return sema.analyzeDeclVal(block, .unneeded, decl); + const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src); + const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted); + break :s lhs_sent_casted_val; } } else { - return sema.fail(block, lhs_src, "TODO runtime array_cat", .{}); + if (rhs_info.sentinel) |rhs_sent_val| { + const rhs_sent = try sema.addConstant(rhs_info.elem_type, rhs_sent_val); + const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src); + const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted); + break :s rhs_sent_casted_val; + } else { + break :s null; + } } - } else { - return sema.fail(block, lhs_src, "TODO runtime array_cat", .{}); + }; + + const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); + const rhs_len = try sema.usizeCast(block, lhs_src, rhs_info.len); + const result_len = lhs_len + rhs_len; + const result_ty = try Type.array(sema.arena, result_len, res_sent_val, resolved_elem_ty, sema.mod); + const is_ref = lhs_ty.zigTypeTag() == .Pointer or rhs_ty.zigTypeTag() == .Pointer; + + const runtime_src = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| rs: { + if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| { + const lhs_sub_val = if (lhs_ty.isSinglePointer()) + (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? + else + lhs_val; + + const rhs_sub_val = if (rhs_ty.isSinglePointer()) + (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).? + else + rhs_val; + + const final_len_including_sent = result_len + @boolToInt(res_sent_val != null); + const element_vals = try sema.arena.alloc(Value, final_len_including_sent); + var elem_i: usize = 0; + while (elem_i < lhs_len) : (elem_i += 1) { + element_vals[elem_i] = try lhs_sub_val.elemValue(sema.mod, sema.arena, elem_i); + } + while (elem_i < result_len) : (elem_i += 1) { + element_vals[elem_i] = try rhs_sub_val.elemValue(sema.mod, sema.arena, elem_i - lhs_len); + } + if (res_sent_val) |sent_val| { + element_vals[result_len] = sent_val; + } + const val = try Value.Tag.aggregate.create(sema.arena, element_vals); + return sema.addConstantMaybeRef(block, src, result_ty, val, is_ref); + } else break :rs rhs_src; + } else lhs_src; + + try sema.requireRuntimeBlock(block, runtime_src); + + if (is_ref) { + const target = sema.mod.getTarget(); + const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ + .pointee_type = result_ty, + .@"addrspace" = target_util.defaultAddressSpace(target, .local), + }); + const alloc = try block.addTy(.alloc, alloc_ty); + const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ + .pointee_type = resolved_elem_ty, + .@"addrspace" = target_util.defaultAddressSpace(target, .local), + }); + + var elem_i: usize = 0; + while (elem_i < lhs_len) : (elem_i += 1) { + const elem_index = try sema.addIntUnsigned(Type.usize, elem_i); + const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); + const init = try sema.elemVal(block, lhs_src, lhs, elem_index, src); + try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); + } + while (elem_i < result_len) : (elem_i += 1) { + const elem_index = try sema.addIntUnsigned(Type.usize, elem_i); + const rhs_index = try sema.addIntUnsigned(Type.usize, elem_i - lhs_len); + const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); + const init = try sema.elemVal(block, rhs_src, rhs, rhs_index, src); + try sema.storePtr2(block, src, elem_ptr, src, init, rhs_src, .store); + } + + return alloc; } + + const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len); + { + var elem_i: usize = 0; + while (elem_i < lhs_len) : (elem_i += 1) { + const index = try sema.addIntUnsigned(Type.usize, elem_i); + const init = try sema.elemVal(block, lhs_src, lhs, index, src); + element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, lhs_src); + } + while (elem_i < result_len) : (elem_i += 1) { + const index = try sema.addIntUnsigned(Type.usize, elem_i - lhs_len); + const init = try sema.elemVal(block, rhs_src, rhs, index, src); + element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, rhs_src); + } + } + + return block.addAggregateInit(result_ty, element_refs); } fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, inst: Air.Inst.Ref) !?Type.ArrayInfo { @@ -24947,7 +25013,7 @@ fn valuesEqual( } /// Asserts the values are comparable vectors of type `ty`. -pub fn compareVector( +fn compareVector( sema: *Sema, block: *Block, src: LazySrcLoc, diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 80d39e00d7..cb0de8ed59 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -733,17 +733,49 @@ test "*align(1) u16 is the same as *align(1:0:2) u16" { } } -test "array concatenation forces comptime" { - if (builtin.zig_backend != .stage1) { - // note: our plan is to change the language to support runtime array - // concatenation instead of making this test pass. - return error.SkipZigTest; // TODO - } +test "array concatenation of function calls" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; var a = oneItem(3) ++ oneItem(4); try expect(std.mem.eql(i32, &a, &[_]i32{ 3, 4 })); } +test "array concatenation peer resolves element types - value" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + var a = [2]u3{ 1, 7 }; + var b = [3]u8{ 200, 225, 255 }; + var c = a ++ b; + try expect(@TypeOf(c) == [5]u8); + try expect(c[0] == 1); + try expect(c[1] == 7); + try expect(c[2] == 200); + try expect(c[3] == 225); + try expect(c[4] == 255); +} + +test "array concatenation peer resolves element types - pointer" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + var a = [2]u3{ 1, 7 }; + var b = [3]u8{ 200, 225, 255 }; + var c = &a ++ &b; + try expect(@TypeOf(c) == *[5]u8); + try expect(c[0] == 1); + try expect(c[1] == 7); + try expect(c[2] == 200); + try expect(c[3] == 225); + try expect(c[4] == 255); +} + test "array multiplication forces comptime" { if (builtin.zig_backend != .stage1) { // note: our plan is to change the language to support runtime array From 83f69af971723d3a0774deb9dfda4b3fcbf0006f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 25 May 2022 22:23:32 -0700 Subject: [PATCH 1647/2031] stage2: implement runtime array multiplication Additionally: * Sema: fix array cat/mul not setting the sentinel value - This required an LLVM backend enhancement to the handling of the AIR instruction aggregate_init that likely needs to be propagated to the other backends. * Sema: report integer overflow of array concatenation in a proper compile error instead of crashing. * Sema: fix not using proper pointer address space for array cat/mul --- src/Air.zig | 2 + src/Sema.zig | 198 ++++++++++++++++++++++++++--------------- src/codegen/llvm.zig | 27 ++++-- test/behavior/eval.zig | 102 ++++++++++++++++++--- 4 files changed, 236 insertions(+), 93 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 8211e39454..5571fc6359 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -609,6 +609,8 @@ pub const Inst = struct { /// Some of the elements may be comptime-known. /// Uses the `ty_pl` field, payload is index of an array of elements, each of which /// is a `Ref`. Length of the array is given by the vector type. + /// If the type is an array with a sentinel, the AIR elements do not include it + /// explicitly. aggregate_init, /// Constructs a union from a field index and a runtime-known init value. diff --git a/src/Sema.zig b/src/Sema.zig index 648174dcd1..38886cc78f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9264,10 +9264,8 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; - const lhs_info = (try sema.getArrayCatInfo(block, lhs_src, lhs)) orelse - return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty.fmt(sema.mod)}); - const rhs_info = (try sema.getArrayCatInfo(block, rhs_src, rhs)) orelse - return sema.fail(block, rhs_src, "expected array, found '{}'", .{rhs_ty.fmt(sema.mod)}); + const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs); + const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs); const resolved_elem_ty = t: { var trash_block = block.makeSubBlock(); @@ -9319,9 +9317,21 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); const rhs_len = try sema.usizeCast(block, lhs_src, rhs_info.len); - const result_len = lhs_len + rhs_len; + const result_len = std.math.add(usize, lhs_len, rhs_len) catch |err| switch (err) { + error.Overflow => return sema.fail( + block, + src, + "concatenating arrays of length {d} and {d} produces an array too large for this compiler implementation to handle", + .{ lhs_len, rhs_len }, + ), + }; + const result_ty = try Type.array(sema.arena, result_len, res_sent_val, resolved_elem_ty, sema.mod); - const is_ref = lhs_ty.zigTypeTag() == .Pointer or rhs_ty.zigTypeTag() == .Pointer; + const ptr_addrspace = p: { + if (lhs_ty.zigTypeTag() == .Pointer) break :p lhs_ty.ptrAddressSpace(); + if (rhs_ty.zigTypeTag() == .Pointer) break :p rhs_ty.ptrAddressSpace(); + break :p null; + }; const runtime_src = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| rs: { if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| { @@ -9348,22 +9358,21 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai element_vals[result_len] = sent_val; } const val = try Value.Tag.aggregate.create(sema.arena, element_vals); - return sema.addConstantMaybeRef(block, src, result_ty, val, is_ref); + return sema.addConstantMaybeRef(block, src, result_ty, val, ptr_addrspace != null); } else break :rs rhs_src; } else lhs_src; try sema.requireRuntimeBlock(block, runtime_src); - if (is_ref) { - const target = sema.mod.getTarget(); + if (ptr_addrspace) |ptr_as| { const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = result_ty, - .@"addrspace" = target_util.defaultAddressSpace(target, .local), + .@"addrspace" = ptr_as, }); const alloc = try block.addTy(.alloc, alloc_ty); const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = resolved_elem_ty, - .@"addrspace" = target_util.defaultAddressSpace(target, .local), + .@"addrspace" = ptr_as, }); var elem_i: usize = 0; @@ -9380,6 +9389,12 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const init = try sema.elemVal(block, rhs_src, rhs, rhs_index, src); try sema.storePtr2(block, src, elem_ptr, src, init, rhs_src, .store); } + if (res_sent_val) |sent_val| { + const elem_index = try sema.addIntUnsigned(Type.usize, result_len); + const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); + const init = try sema.addConstant(lhs_info.elem_type, sent_val); + try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); + } return alloc; } @@ -9402,30 +9417,35 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return block.addAggregateInit(result_ty, element_refs); } -fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, inst: Air.Inst.Ref) !?Type.ArrayInfo { - const t = sema.typeOf(inst); - return switch (t.zigTypeTag()) { - .Array => t.arrayInfo(), - .Pointer => blk: { - const ptrinfo = t.ptrInfo().data; - switch (ptrinfo.size) { +fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) !Type.ArrayInfo { + const operand_ty = sema.typeOf(operand); + switch (operand_ty.zigTypeTag()) { + .Array => return operand_ty.arrayInfo(), + .Pointer => { + const ptr_info = operand_ty.ptrInfo().data; + switch (ptr_info.size) { + // TODO: in the Many case here this should only work if the type + // has a sentinel, and this code should compute the length based + // on the sentinel value. .Slice, .Many => { - const val = try sema.resolveConstValue(block, src, inst); + const val = try sema.resolveConstValue(block, src, operand); return Type.ArrayInfo{ - .elem_type = t.childType(), - .sentinel = t.sentinel(), + .elem_type = ptr_info.pointee_type, + .sentinel = ptr_info.sentinel, .len = val.sliceLen(sema.mod), }; }, .One => { - if (ptrinfo.pointee_type.zigTypeTag() != .Array) return null; - break :blk ptrinfo.pointee_type.arrayInfo(); + if (ptr_info.pointee_type.zigTypeTag() == .Array) { + return ptr_info.pointee_type.arrayInfo(); + } }, - .C => return null, + .C => {}, } }, - else => null, - }; + else => {}, + } + return sema.fail(block, src, "expected indexable; found '{}'", .{operand_ty.fmt(sema.mod)}); } fn analyzeTupleMul( @@ -9514,65 +9534,99 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor); } - const mulinfo = (try sema.getArrayCatInfo(block, lhs_src, lhs)) orelse - return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty.fmt(sema.mod)}); + const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs); - const final_len_u64 = std.math.mul(u64, mulinfo.len, factor) catch + const result_len_u64 = std.math.mul(u64, lhs_info.len, factor) catch return sema.fail(block, rhs_src, "operation results in overflow", .{}); + const result_len = try sema.usizeCast(block, src, result_len_u64); + + const result_ty = try Type.array(sema.arena, result_len, lhs_info.sentinel, lhs_info.elem_type, sema.mod); + + const ptr_addrspace = if (lhs_ty.zigTypeTag() == .Pointer) lhs_ty.ptrAddressSpace() else null; + const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { - const final_len = try sema.usizeCast(block, src, final_len_u64); - const final_len_including_sent = final_len + @boolToInt(mulinfo.sentinel != null); - const lhs_len = try sema.usizeCast(block, lhs_src, mulinfo.len); + const final_len_including_sent = result_len + @boolToInt(lhs_info.sentinel != null); - const is_single_ptr = lhs_ty.zigTypeTag() == .Pointer and !lhs_ty.isSlice(); - const lhs_sub_val = if (is_single_ptr) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val; - - var anon_decl = try block.startAnonDecl(src); - defer anon_decl.deinit(); - - const final_ty = if (mulinfo.sentinel) |sent| - try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ - .len = final_len, - .elem_type = try mulinfo.elem_type.copy(anon_decl.arena()), - .sentinel = try sent.copy(anon_decl.arena()), - }) + const lhs_sub_val = if (lhs_ty.isSinglePointer()) + (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else - try Type.Tag.array.create(anon_decl.arena(), .{ - .len = final_len, - .elem_type = try mulinfo.elem_type.copy(anon_decl.arena()), - }); - const buf = try anon_decl.arena().alloc(Value, final_len_including_sent); + lhs_val; - // Optimization for the common pattern of a single element repeated N times, such - // as zero-filling a byte array. - const val = if (lhs_len == 1) blk: { - const elem_val = try lhs_sub_val.elemValue(sema.mod, sema.arena, 0); - const copied_val = try elem_val.copy(anon_decl.arena()); - break :blk try Value.Tag.repeated.create(anon_decl.arena(), copied_val); - } else blk: { - // the actual loop - var i: usize = 0; - while (i < factor) : (i += 1) { - var j: usize = 0; - while (j < lhs_len) : (j += 1) { - const val = try lhs_sub_val.elemValue(sema.mod, sema.arena, j); - buf[lhs_len * i + j] = try val.copy(anon_decl.arena()); + const val = v: { + // Optimization for the common pattern of a single element repeated N times, such + // as zero-filling a byte array. + if (lhs_len == 1) { + const elem_val = try lhs_sub_val.elemValue(sema.mod, sema.arena, 0); + break :v try Value.Tag.repeated.create(sema.arena, elem_val); + } + + const element_vals = try sema.arena.alloc(Value, final_len_including_sent); + var elem_i: usize = 0; + while (elem_i < result_len) { + var lhs_i: usize = 0; + while (lhs_i < lhs_len) : (lhs_i += 1) { + const elem_val = try lhs_sub_val.elemValue(sema.mod, sema.arena, lhs_i); + element_vals[elem_i] = elem_val; + elem_i += 1; } } - if (mulinfo.sentinel) |sent| { - buf[final_len] = try sent.copy(anon_decl.arena()); + if (lhs_info.sentinel) |sent_val| { + element_vals[result_len] = sent_val; } - break :blk try Value.Tag.aggregate.create(anon_decl.arena(), buf); + break :v try Value.Tag.aggregate.create(sema.arena, element_vals); }; - const decl = try anon_decl.finish(final_ty, val, 0); - if (lhs_ty.zigTypeTag() == .Pointer) { - return sema.analyzeDeclRef(decl); - } else { - return sema.analyzeDeclVal(block, .unneeded, decl); + return sema.addConstantMaybeRef(block, src, result_ty, val, ptr_addrspace != null); + } + + try sema.requireRuntimeBlock(block, lhs_src); + + if (ptr_addrspace) |ptr_as| { + const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ + .pointee_type = result_ty, + .@"addrspace" = ptr_as, + }); + const alloc = try block.addTy(.alloc, alloc_ty); + const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ + .pointee_type = lhs_info.elem_type, + .@"addrspace" = ptr_as, + }); + + var elem_i: usize = 0; + while (elem_i < result_len) { + var lhs_i: usize = 0; + while (lhs_i < lhs_len) : (lhs_i += 1) { + const elem_index = try sema.addIntUnsigned(Type.usize, elem_i); + elem_i += 1; + const lhs_index = try sema.addIntUnsigned(Type.usize, lhs_i); + const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); + const init = try sema.elemVal(block, lhs_src, lhs, lhs_index, src); + try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); + } + } + if (lhs_info.sentinel) |sent_val| { + const elem_index = try sema.addIntUnsigned(Type.usize, result_len); + const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); + const init = try sema.addConstant(lhs_info.elem_type, sent_val); + try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); + } + + return alloc; + } + + const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len); + var elem_i: usize = 0; + while (elem_i < result_len) { + var lhs_i: usize = 0; + while (lhs_i < lhs_len) : (lhs_i += 1) { + const lhs_index = try sema.addIntUnsigned(Type.usize, lhs_i); + const init = try sema.elemVal(block, lhs_src, lhs, lhs_index, src); + element_refs[elem_i] = init; + elem_i += 1; } } - return sema.fail(block, lhs_src, "TODO runtime array_mul", .{}); + + return block.addAggregateInit(result_ty, element_refs); } fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index ec71297c10..f958fdfbb3 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -7728,7 +7728,14 @@ pub const FuncGen = struct { const alloca_inst = self.buildAlloca(llvm_result_ty); alloca_inst.setAlignment(result_ty.abiAlignment(target)); - const elem_ty = result_ty.childType(); + const array_info = result_ty.arrayInfo(); + var elem_ptr_payload: Type.Payload.Pointer = .{ + .data = .{ + .pointee_type = array_info.elem_type, + .@"addrspace" = .generic, + }, + }; + const elem_ptr_ty = Type.initPayload(&elem_ptr_payload.base); for (elements) |elem, i| { const indices: [2]*const llvm.Value = .{ @@ -7737,13 +7744,19 @@ pub const FuncGen = struct { }; const elem_ptr = self.builder.buildInBoundsGEP(alloca_inst, &indices, indices.len, ""); const llvm_elem = try self.resolveInst(elem); - var elem_ptr_payload: Type.Payload.Pointer = .{ - .data = .{ - .pointee_type = elem_ty, - .@"addrspace" = .generic, - }, + self.store(elem_ptr, elem_ptr_ty, llvm_elem, .NotAtomic); + } + if (array_info.sentinel) |sent_val| { + const indices: [2]*const llvm.Value = .{ + llvm_usize.constNull(), + llvm_usize.constInt(@intCast(c_uint, array_info.len), .False), }; - const elem_ptr_ty = Type.initPayload(&elem_ptr_payload.base); + const elem_ptr = self.builder.buildInBoundsGEP(alloca_inst, &indices, indices.len, ""); + const llvm_elem = try self.dg.lowerValue(.{ + .ty = array_info.elem_type, + .val = sent_val, + }); + self.store(elem_ptr, elem_ptr_ty, llvm_elem, .NotAtomic); } diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index cb0de8ed59..63eef12207 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -742,6 +742,23 @@ test "array concatenation of function calls" { try expect(std.mem.eql(i32, &a, &[_]i32{ 3, 4 })); } +test "array multiplication of function calls" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + var a = oneItem(3) ** scalar(2); + try expect(std.mem.eql(i32, &a, &[_]i32{ 3, 3 })); +} + +fn oneItem(x: i32) [1]i32 { + return [_]i32{x}; +} + +fn scalar(x: u32) u32 { + return x; +} + test "array concatenation peer resolves element types - value" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; @@ -751,7 +768,7 @@ test "array concatenation peer resolves element types - value" { var a = [2]u3{ 1, 7 }; var b = [3]u8{ 200, 225, 255 }; var c = a ++ b; - try expect(@TypeOf(c) == [5]u8); + comptime assert(@TypeOf(c) == [5]u8); try expect(c[0] == 1); try expect(c[1] == 7); try expect(c[2] == 200); @@ -768,7 +785,7 @@ test "array concatenation peer resolves element types - pointer" { var a = [2]u3{ 1, 7 }; var b = [3]u8{ 200, 225, 255 }; var c = &a ++ &b; - try expect(@TypeOf(c) == *[5]u8); + comptime assert(@TypeOf(c) == *[5]u8); try expect(c[0] == 1); try expect(c[1] == 7); try expect(c[2] == 200); @@ -776,23 +793,80 @@ test "array concatenation peer resolves element types - pointer" { try expect(c[4] == 255); } -test "array multiplication forces comptime" { - if (builtin.zig_backend != .stage1) { - // note: our plan is to change the language to support runtime array - // multiplication instead of making this test pass. - return error.SkipZigTest; // TODO - } +test "array concatenation sets the sentinel - value" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - var a = oneItem(3) ** scalar(2); - try expect(std.mem.eql(i32, &a, &[_]i32{ 3, 3 })); + var a = [2]u3{ 1, 7 }; + var b = [3:69]u8{ 200, 225, 255 }; + var c = a ++ b; + comptime assert(@TypeOf(c) == [5:69]u8); + try expect(c[0] == 1); + try expect(c[1] == 7); + try expect(c[2] == 200); + try expect(c[3] == 225); + try expect(c[4] == 255); + var ptr: [*]const u8 = &c; + try expect(ptr[5] == 69); } -fn oneItem(x: i32) [1]i32 { - return [_]i32{x}; +test "array concatenation sets the sentinel - pointer" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + var a = [2]u3{ 1, 7 }; + var b = [3:69]u8{ 200, 225, 255 }; + var c = &a ++ &b; + comptime assert(@TypeOf(c) == *[5:69]u8); + try expect(c[0] == 1); + try expect(c[1] == 7); + try expect(c[2] == 200); + try expect(c[3] == 225); + try expect(c[4] == 255); + var ptr: [*]const u8 = c; + try expect(ptr[5] == 69); } -fn scalar(x: u32) u32 { - return x; +test "array multiplication sets the sentinel - value" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + var a = [2:7]u3{ 1, 6 }; + var b = a ** 2; + comptime assert(@TypeOf(b) == [4:7]u3); + try expect(b[0] == 1); + try expect(b[1] == 6); + try expect(b[2] == 1); + try expect(b[3] == 6); + var ptr: [*]const u3 = &b; + try expect(ptr[4] == 7); +} + +test "array multiplication sets the sentinel - pointer" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + var a = [2:7]u3{ 1, 6 }; + var b = &a ** 2; + comptime assert(@TypeOf(b) == *[4:7]u3); + try expect(b[0] == 1); + try expect(b[1] == 6); + try expect(b[2] == 1); + try expect(b[3] == 6); + var ptr: [*]const u3 = b; + try expect(ptr[4] == 7); } test "comptime assign int to optional int" { From 0310d88d7e6a855b3641b59c25f5b0f13726d14f Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 26 May 2022 13:08:39 +0700 Subject: [PATCH 1648/2031] stage2: sparc64: Add cmp and mov synthetic instructions --- src/arch/sparc64/CodeGen.zig | 43 ++++++++++++++++++++++-------------- src/arch/sparc64/Emit.zig | 8 +++++++ src/arch/sparc64/Mir.zig | 20 ++++++++++++++--- 3 files changed, 51 insertions(+), 20 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index ff066f78f2..8b6bcfcb06 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1677,7 +1677,7 @@ fn binOp( const mir_tag: Mir.Inst.Tag = switch (tag) { .add => .add, - .cmp_eq => .subcc, + .cmp_eq => .cmp, else => unreachable, }; @@ -1903,6 +1903,13 @@ fn binOpImmediate( .rs2_or_imm = .{ .imm = @intCast(u6, rhs.immediate) }, }, }, + .cmp => .{ + .arithmetic_2op = .{ + .is_imm = true, + .rs1 = lhs_reg, + .rs2_or_imm = .{ .imm = @intCast(i13, rhs.immediate) }, + }, + }, else => unreachable, }; @@ -2012,6 +2019,13 @@ fn binOpRegister( .rs2_or_imm = .{ .rs2 = rhs_reg }, }, }, + .cmp => .{ + .arithmetic_2op = .{ + .is_imm = false, + .rs1 = lhs_reg, + .rs2_or_imm = .{ .rs2 = rhs_reg }, + }, + }, else => unreachable, }; @@ -2303,12 +2317,11 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .immediate => |x| { if (x <= math.maxInt(u12)) { _ = try self.addInst(.{ - .tag = .@"or", + .tag = .mov, .data = .{ - .arithmetic_3op = .{ + .arithmetic_2op = .{ .is_imm = true, - .rd = reg, - .rs1 = .g0, + .rs1 = reg, .rs2_or_imm = .{ .imm = @truncate(u12, x) }, }, }, @@ -2400,14 +2413,12 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void if (src_reg.id() == reg.id()) return; - // or %g0, src, dst (aka mov src, dst) _ = try self.addInst(.{ - .tag = .@"or", + .tag = .mov, .data = .{ - .arithmetic_3op = .{ + .arithmetic_2op = .{ .is_imm = false, - .rd = reg, - .rs1 = .g0, + .rs1 = reg, .rs2_or_imm = .{ .rs2 = src_reg }, }, }, @@ -2625,12 +2636,11 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { }; _ = try self.addInst(.{ - .tag = .subcc, - .data = .{ .arithmetic_3op = .{ + .tag = .cmp, + .data = .{ .arithmetic_2op = .{ .is_imm = true, .rs1 = reg_mcv.register, .rs2_or_imm = .{ .imm = 0 }, - .rd = .g0, } }, }); @@ -3163,12 +3173,11 @@ fn truncRegister( }, 64 => { _ = try self.addInst(.{ - .tag = .@"or", + .tag = .mov, .data = .{ - .arithmetic_3op = .{ + .arithmetic_2op = .{ .is_imm = true, - .rd = dest_reg, - .rs1 = .g0, + .rs1 = dest_reg, .rs2_or_imm = .{ .rs2 = operand_reg }, }, }, diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 6f30f785c5..dc1b4caedc 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -121,6 +121,10 @@ pub fn emitMir( .subcc => try emit.mirArithmetic3Op(inst), .tcc => try emit.mirTrap(inst), + + .cmp => try emit.mirArithmetic2Op(inst), + + .mov => try emit.mirArithmetic2Op(inst), } } } @@ -179,12 +183,16 @@ fn mirArithmetic2Op(emit: *Emit, inst: Mir.Inst.Index) !void { const imm = data.rs2_or_imm.imm; switch (tag) { .@"return" => try emit.writeInstruction(Instruction.@"return"(i13, rs1, imm)), + .cmp => try emit.writeInstruction(Instruction.subcc(i13, rs1, imm, .g0)), + .mov => try emit.writeInstruction(Instruction.@"or"(i13, .g0, imm, rs1)), else => unreachable, } } else { const rs2 = data.rs2_or_imm.rs2; switch (tag) { .@"return" => try emit.writeInstruction(Instruction.@"return"(Register, rs1, rs2)), + .cmp => try emit.writeInstruction(Instruction.subcc(Register, rs1, rs2, .g0)), + .mov => try emit.writeInstruction(Instruction.@"or"(Register, .g0, rs2, rs1)), else => unreachable, } } diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index 441e151cea..2ef66a1fa4 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -125,9 +125,23 @@ pub const Inst = struct { /// This uses the trap field. tcc, - // TODO add synthetic instructions - // TODO add cmp synthetic instruction to avoid wasting a register when - // comparing with subcc + // SPARCv9 synthetic instructions + // Note that the instructions that is added here are only those that + // will simplify backend development. Synthetic instructions that is + // only used to provide syntactic sugar in, e.g. inline assembly should + // be deconstructed inside the parser instead. + // See also: G.3 Synthetic Instructions + // TODO add more synthetic instructions + + /// Comparison + /// This uses the arithmetic_2op field. + cmp, // cmp rs1, rs2/imm -> subcc rs1, rs2/imm, %g0 + + /// Copy register/immediate contents to another register + /// This uses the arithmetic_2op field, with rs1 + /// being the *destination* register. + // TODO is it okay to abuse rs1 in this way? + mov, // mov rs2/imm, rs1 -> or %g0, rs2/imm, rs1 }; /// The position of an MIR instruction within the `Mir` instructions array. From 5fa971610e0f987938d43da9d3a40e2a31fe4605 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 26 May 2022 13:09:56 +0700 Subject: [PATCH 1649/2031] stage2: sparc64: Change binOpImmediate immediates to u12 Sync with the check in binOp. --- src/arch/sparc64/CodeGen.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 8b6bcfcb06..228691721d 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1891,7 +1891,7 @@ fn binOpImmediate( .is_imm = true, .rd = dest_reg, .rs1 = lhs_reg, - .rs2_or_imm = .{ .imm = @intCast(i13, rhs.immediate) }, + .rs2_or_imm = .{ .imm = @intCast(u12, rhs.immediate) }, }, }, .sllx => .{ @@ -1907,7 +1907,7 @@ fn binOpImmediate( .arithmetic_2op = .{ .is_imm = true, .rs1 = lhs_reg, - .rs2_or_imm = .{ .imm = @intCast(i13, rhs.immediate) }, + .rs2_or_imm = .{ .imm = @intCast(u12, rhs.immediate) }, }, }, else => unreachable, From 3923722cc617251de91d5bc9a5d4f9c0ca93ee1a Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 26 May 2022 13:24:57 +0700 Subject: [PATCH 1650/2031] stage2: sparc64: Account for stack bias & reserved area in genSetReg genSetReg with ptr_stack_offset should add the bias and reserved area before emitting the instruction. --- src/arch/sparc64/CodeGen.zig | 12 ++++++------ src/arch/sparc64/abi.zig | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 228691721d..bf08e9a309 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -352,7 +352,7 @@ fn gen(self: *Self) !void { if (cc != .Naked) { // TODO Finish function prologue and epilogue for sparc64. - // save %sp, stack_save_area, %sp + // save %sp, stack_reserved_area, %sp const save_inst = try self.addInst(.{ .tag = .save, .data = .{ @@ -360,7 +360,7 @@ fn gen(self: *Self) !void { .is_imm = true, .rd = .sp, .rs1 = .sp, - .rs2_or_imm = .{ .imm = -abi.stack_save_area }, + .rs2_or_imm = .{ .imm = -abi.stack_reserved_area }, }, }, }); @@ -407,7 +407,7 @@ fn gen(self: *Self) !void { } // Backpatch stack offset - const total_stack_size = self.max_end_stack + abi.stack_save_area; // TODO + self.saved_regs_stack_space; + const total_stack_size = self.max_end_stack + abi.stack_reserved_area; // TODO + self.saved_regs_stack_space; const stack_size = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align); if (math.cast(i13, stack_size)) |size| { self.mir_instructions.set(save_inst, .{ @@ -2299,7 +2299,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); }, .ptr_stack_offset => |off| { - const simm13 = math.cast(u12, off) catch + const simm13 = math.cast(u12, off + abi.stack_bias + abi.stack_reserved_area) catch return self.fail("TODO larger stack offsets", .{}); _ = try self.addInst(.{ @@ -2431,7 +2431,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void try self.genLoad(reg, reg, i13, 0, ty.abiSize(self.target.*)); }, .stack_offset => |off| { - const real_offset = off + abi.stack_bias + abi.stack_save_area; + const real_offset = off + abi.stack_bias + abi.stack_reserved_area; const simm13 = math.cast(i13, real_offset) catch return self.fail("TODO larger stack offsets", .{}); try self.genLoad(reg, .sp, i13, simm13, ty.abiSize(self.target.*)); @@ -2465,7 +2465,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); }, .register => |reg| { - const real_offset = stack_offset + abi.stack_bias + abi.stack_save_area; + const real_offset = stack_offset + abi.stack_bias + abi.stack_reserved_area; const simm13 = math.cast(i13, real_offset) catch return self.fail("TODO larger stack offsets", .{}); return self.genStore(reg, .sp, i13, simm13, abi_size); diff --git a/src/arch/sparc64/abi.zig b/src/arch/sparc64/abi.zig index 1c6d40941f..99f83eee7b 100644 --- a/src/arch/sparc64/abi.zig +++ b/src/arch/sparc64/abi.zig @@ -3,17 +3,17 @@ const bits = @import("bits.zig"); const Register = bits.Register; const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; -// SPARCv9 stack constants. +// SPARCv9 SysV ABI stack constants. // See: Registers and the Stack Frame, page 3P-8, SCD 2.4.1. -// On SPARCv9, %sp points to top of stack + stack bias, -// and %fp points to top of previous frame + stack bias. +// The ABI specifies that %sp points to top of stack - stack bias, +// and %fp points to top of previous frame - stack bias. pub const stack_bias = 2047; -// The first 176 bytes of the stack is reserved for register saving purposes. -// SPARCv9 requires to reserve space in the stack for the first six arguments, -// even though they are usually passed in registers. -pub const stack_save_area = 176; +// The first 128 bytes of the stack is reserved for register saving purposes. +// The ABI also requires to reserve space in the stack for the first six +// outgoing arguments, even though they are usually passed in registers. +pub const stack_reserved_area = 128 + 48; // There are no callee-preserved registers since the windowing // mechanism already takes care of them. From c5b99267c0580b166d7ccc2bbf77e9c92566962f Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 26 May 2022 13:48:39 +0700 Subject: [PATCH 1651/2031] stage2: sparc64: Remove saved_regs_stack_space calculation SPARC does not have an explicit notion of saving/restoring registers. The usual windowing mechanism (save/restore/return) already takes care of that for us. --- src/arch/sparc64/CodeGen.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index bf08e9a309..f36d4ba45f 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -407,7 +407,7 @@ fn gen(self: *Self) !void { } // Backpatch stack offset - const total_stack_size = self.max_end_stack + abi.stack_reserved_area; // TODO + self.saved_regs_stack_space; + const total_stack_size = self.max_end_stack + abi.stack_reserved_area; const stack_size = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align); if (math.cast(i13, stack_size)) |size| { self.mir_instructions.set(save_inst, .{ From 0e0b00fd48ea680f2d69207297e877f54dd9ec89 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 26 May 2022 09:06:42 +0200 Subject: [PATCH 1652/2031] regalloc: use StaticBitSet internally --- src/arch/aarch64/abi.zig | 18 +- src/arch/arm/abi.zig | 18 +- src/arch/riscv64/abi.zig | 18 +- src/arch/sparc64/abi.zig | 18 +- src/arch/x86_64/abi.zig | 38 ++-- src/register_manager.zig | 453 +++++++-------------------------------- 6 files changed, 121 insertions(+), 442 deletions(-) diff --git a/src/arch/aarch64/abi.zig b/src/arch/aarch64/abi.zig index 89a3a6c21d..cb28b1fffa 100644 --- a/src/arch/aarch64/abi.zig +++ b/src/arch/aarch64/abi.zig @@ -27,14 +27,12 @@ pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, // Register classes const RegisterBitSet = RegisterManager.RegisterBitSet; pub const RegisterClass = struct { - pub const gp: RegisterBitSet = std.math.maxInt(RegisterBitSet); - // TODO uncomment once #11680 is fixed. - // pub const gp: RegisterBitSet = blk: { - // var set = RegisterBitSet.initEmpty(); - // set.setRangeValue(.{ - // .start = 0, - // .end = callee_preserved_regs.len, - // }, true); - // break :blk set; - // }; + pub const gp: RegisterBitSet = blk: { + var set = RegisterBitSet.initEmpty(); + set.setRangeValue(.{ + .start = 0, + .end = callee_preserved_regs.len, + }, true); + break :blk set; + }; }; diff --git a/src/arch/arm/abi.zig b/src/arch/arm/abi.zig index c76c3b0ea0..3fcfb0e561 100644 --- a/src/arch/arm/abi.zig +++ b/src/arch/arm/abi.zig @@ -15,14 +15,12 @@ pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, // Register classes const RegisterBitSet = RegisterManager.RegisterBitSet; pub const RegisterClass = struct { - pub const gp: RegisterBitSet = std.math.maxInt(RegisterBitSet); - // TODO uncomment once #11680 is fixed. - // pub const gp: RegisterBitSet = blk: { - // var set = RegisterBitSet.initEmpty(); - // set.setRangeValue(.{ - // .start = 0, - // .end = caller_preserved_regs.len + callee_preserved_regs.len, - // }, true); - // break :blk set; - // }; + pub const gp: RegisterBitSet = blk: { + var set = RegisterBitSet.initEmpty(); + set.setRangeValue(.{ + .start = 0, + .end = caller_preserved_regs.len + callee_preserved_regs.len, + }, true); + break :blk set; + }; }; diff --git a/src/arch/riscv64/abi.zig b/src/arch/riscv64/abi.zig index 30d3719a46..3792c4ab18 100644 --- a/src/arch/riscv64/abi.zig +++ b/src/arch/riscv64/abi.zig @@ -13,14 +13,12 @@ pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, // Register classes const RegisterBitSet = RegisterManager.RegisterBitSet; pub const RegisterClass = struct { - pub const gp: RegisterBitSet = std.math.maxInt(RegisterBitSet); - // TODO uncomment once #11680 is fixed. - // pub const gp: RegisterBitSet = blk: { - // var set = RegisterBitSet.initEmpty(); - // set.setRangeValue(.{ - // .start = 0, - // .end = callee_preserved_regs.len, - // }, true); - // break :blk set; - // }; + pub const gp: RegisterBitSet = blk: { + var set = RegisterBitSet.initEmpty(); + set.setRangeValue(.{ + .start = 0, + .end = callee_preserved_regs.len, + }, true); + break :blk set; + }; }; diff --git a/src/arch/sparc64/abi.zig b/src/arch/sparc64/abi.zig index 1c6d40941f..a18262f62b 100644 --- a/src/arch/sparc64/abi.zig +++ b/src/arch/sparc64/abi.zig @@ -43,14 +43,12 @@ pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, // Register classes const RegisterBitSet = RegisterManager.RegisterBitSet; pub const RegisterClass = struct { - pub const gp: RegisterBitSet = std.math.maxInt(RegisterBitSet); - // TODO uncomment once #11680 is fixed. - // pub const gp: RegisterBitSet = blk: { - // var set = RegisterBitSet.initEmpty(); - // set.setRangeValue(.{ - // .start = 0, - // .end = allocatable_regs.len, - // }, true); - // break :blk set; - // }; + pub const gp: RegisterBitSet = blk: { + var set = RegisterBitSet.initEmpty(); + set.setRangeValue(.{ + .start = 0, + .end = allocatable_regs.len, + }, true); + break :blk set; + }; }; diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index 77f28c11f4..7e2025a23d 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -393,26 +393,20 @@ pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, // Register classes const RegisterBitSet = RegisterManager.RegisterBitSet; pub const RegisterClass = struct { - pub const gp: RegisterBitSet = @as(RegisterBitSet, std.math.maxInt(std.meta.Int( - .unsigned, - caller_preserved_regs.len + callee_preserved_regs.len, - ))); - pub const sse: RegisterBitSet = std.math.maxInt(RegisterBitSet) - gp; - // TODO uncomment once #11680 is fixed. - // pub const gp: RegisterBitSet = blk: { - // var set = RegisterBitSet.initEmpty(); - // set.setRangeValue(.{ - // .start = 0, - // .end = caller_preserved_regs.len + callee_preserved_regs.len, - // }, true); - // break :blk set; - // }; - // pub const sse: RegisterBitSet = blk: { - // var set = RegisterBitSet.initEmpty(); - // set.setRangeValue(.{ - // .start = caller_preserved_regs.len + callee_preserved_regs.len, - // .end = allocatable_registers.len, - // }, true); - // break :blk set; - // }; + pub const gp: RegisterBitSet = blk: { + var set = RegisterBitSet.initEmpty(); + set.setRangeValue(.{ + .start = 0, + .end = caller_preserved_regs.len + callee_preserved_regs.len, + }, true); + break :blk set; + }; + pub const sse: RegisterBitSet = blk: { + var set = RegisterBitSet.initEmpty(); + set.setRangeValue(.{ + .start = caller_preserved_regs.len + callee_preserved_regs.len, + .end = allocatable_registers.len, + }, true); + break :blk set; + }; }; diff --git a/src/register_manager.zig b/src/register_manager.zig index 98ee8c9ab3..64f6609a9b 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -4,6 +4,7 @@ const mem = std.mem; const assert = std.debug.assert; const Allocator = std.mem.Allocator; const Air = @import("Air.zig"); +const StaticBitSet = std.bit_set.StaticBitSet; const Type = @import("type.zig").Type; const Module = @import("Module.zig"); const expect = std.testing.expect; @@ -41,49 +42,39 @@ pub fn RegisterManager( registers: [tracked_registers.len]Air.Inst.Index = undefined, /// Tracks which registers are free (in which case the /// corresponding bit is set to 1) - free_registers: RegisterBitSet = math.maxInt(RegisterBitSet), + free_registers: RegisterBitSet = RegisterBitSet.initFull(), /// Tracks all registers allocated in the course of this /// function - allocated_registers: RegisterBitSet = 0, + allocated_registers: RegisterBitSet = RegisterBitSet.initEmpty(), /// Tracks registers which are locked from being allocated - locked_registers: RegisterBitSet = 0, + locked_registers: RegisterBitSet = RegisterBitSet.initEmpty(), const Self = @This(); - /// An integer whose bits represent all the registers and - /// whether they are free. - pub const RegisterBitSet = std.meta.Int(.unsigned, tracked_registers.len); - const ShiftInt = math.Log2Int(RegisterBitSet); + pub const RegisterBitSet = StaticBitSet(tracked_registers.len); fn getFunction(self: *Self) *Function { return @fieldParentPtr(Function, "register_manager", self); } fn excludeRegister(reg: Register, register_class: RegisterBitSet) bool { - const mask = getRegisterMask(reg) orelse return true; - return mask & register_class == 0; - } - - fn getRegisterMask(reg: Register) ?RegisterBitSet { - const index = indexOfRegIntoTracked(reg) orelse return null; - const shift = @intCast(ShiftInt, index); - const mask = @as(RegisterBitSet, 1) << shift; - return mask; + const index = indexOfRegIntoTracked(reg) orelse return true; + return !register_class.isSet(index); } fn markRegAllocated(self: *Self, reg: Register) void { - const mask = getRegisterMask(reg) orelse return; - self.allocated_registers |= mask; + const index = indexOfRegIntoTracked(reg) orelse return; + self.allocated_registers.set(index); } fn markRegUsed(self: *Self, reg: Register) void { - const mask = getRegisterMask(reg) orelse return; - self.free_registers &= ~mask; + const index = indexOfRegIntoTracked(reg) orelse return; + self.free_registers.unset(index); } fn markRegFree(self: *Self, reg: Register) void { - const mask = getRegisterMask(reg) orelse return; - self.free_registers |= mask; + const index = indexOfRegIntoTracked(reg) orelse return; + self.free_registers.set(index); } pub fn indexOfReg( @@ -96,14 +87,14 @@ pub fn RegisterManager( return null; } - pub fn indexOfRegIntoTracked(reg: Register) ?ShiftInt { + pub fn indexOfRegIntoTracked(reg: Register) ?RegisterBitSet.ShiftInt { return indexOfReg(tracked_registers, reg); } /// Returns true when this register is not tracked pub fn isRegFree(self: Self, reg: Register) bool { - const mask = getRegisterMask(reg) orelse return true; - return self.free_registers & mask != 0; + const index = indexOfRegIntoTracked(reg) orelse return true; + return self.free_registers.isSet(index); } /// Returns whether this register was allocated in the course @@ -111,16 +102,16 @@ pub fn RegisterManager( /// /// Returns false when this register is not tracked pub fn isRegAllocated(self: Self, reg: Register) bool { - const mask = getRegisterMask(reg) orelse return false; - return self.allocated_registers & mask != 0; + const index = indexOfRegIntoTracked(reg) orelse return false; + return self.allocated_registers.isSet(index); } /// Returns whether this register is locked /// /// Returns false when this register is not tracked pub fn isRegLocked(self: Self, reg: Register) bool { - const mask = getRegisterMask(reg) orelse return false; - return self.locked_registers & mask != 0; + const index = indexOfRegIntoTracked(reg) orelse return false; + return self.locked_registers.isSet(index); } pub const RegisterLock = struct { @@ -139,8 +130,8 @@ pub fn RegisterManager( log.debug(" register already locked", .{}); return null; } - const mask = getRegisterMask(reg) orelse return null; - self.locked_registers |= mask; + const index = indexOfRegIntoTracked(reg) orelse return null; + self.locked_registers.set(index); return RegisterLock{ .register = reg }; } @@ -149,8 +140,8 @@ pub fn RegisterManager( pub fn lockRegAssumeUnused(self: *Self, reg: Register) RegisterLock { log.debug("locking asserting free {}", .{reg}); assert(!self.isRegLocked(reg)); - const mask = getRegisterMask(reg) orelse unreachable; - self.locked_registers |= mask; + const index = indexOfRegIntoTracked(reg) orelse unreachable; + self.locked_registers.set(index); return RegisterLock{ .register = reg }; } @@ -172,13 +163,13 @@ pub fn RegisterManager( /// Call `lockReg` to obtain the lock first. pub fn unlockReg(self: *Self, lock: RegisterLock) void { log.debug("unlocking {}", .{lock.register}); - const mask = getRegisterMask(lock.register) orelse return; - self.locked_registers &= ~mask; + const index = indexOfRegIntoTracked(lock.register) orelse return; + self.locked_registers.unset(index); } /// Returns true when at least one register is locked pub fn lockedRegsExist(self: Self) bool { - return self.locked_registers != 0; + return self.locked_registers.count() > 0; } /// Allocates a specified number of registers, optionally @@ -192,10 +183,15 @@ pub fn RegisterManager( ) ?[count]Register { comptime assert(count > 0 and count <= tracked_registers.len); - const free_registers = self.free_registers & register_class; - const free_and_not_locked_registers = free_registers & ~self.locked_registers; - const free_and_not_locked_registers_count = @popCount(RegisterBitSet, free_and_not_locked_registers); - if (free_and_not_locked_registers_count < count) return null; + var free_and_not_locked_registers = self.free_registers; + free_and_not_locked_registers.setIntersection(register_class); + + var unlocked_registers = self.locked_registers; + unlocked_registers.toggleAll(); + + free_and_not_locked_registers.setIntersection(unlocked_registers); + + if (free_and_not_locked_registers.count() < count) return null; var regs: [count]Register = undefined; var i: usize = 0; @@ -242,10 +238,10 @@ pub fn RegisterManager( ) AllocateRegistersError![count]Register { comptime assert(count > 0 and count <= tracked_registers.len); - const available_registers_count = @popCount(RegisterBitSet, register_class); - const locked_registers = self.locked_registers & register_class; - const locked_registers_count = @popCount(RegisterBitSet, locked_registers); - if (count > available_registers_count - locked_registers_count) return error.OutOfRegisters; + var locked_registers = self.locked_registers; + locked_registers.setIntersection(register_class); + + if (count > register_class.count() - locked_registers.count()) return error.OutOfRegisters; const result = self.tryAllocRegs(count, insts, register_class) orelse blk: { // We'll take over the first count registers. Spill @@ -255,7 +251,7 @@ pub fn RegisterManager( var i: usize = 0; for (tracked_registers) |reg| { if (i >= count) break; - if (excludeRegister(reg, register_class)) continue; + if (excludeRegister(reg, register_class)) break; if (self.isRegLocked(reg)) continue; regs[i] = reg; @@ -352,334 +348,6 @@ pub fn RegisterManager( }; } -// TODO delete current implementation of RegisterManager above, and uncomment the one -// below once #11680 is fixed: -// https://github.com/ziglang/zig/issues/11680 - -//pub fn RegisterManager( -// comptime Function: type, -// comptime Register: type, -// comptime tracked_registers: []const Register, -//) type { -// // architectures which do not have a concept of registers should -// // refrain from using RegisterManager -// assert(tracked_registers.len > 0); // see note above - -// return struct { -// /// Tracks the AIR instruction allocated to every register. If -// /// no instruction is allocated to a register (i.e. the -// /// register is free), the value in that slot is undefined. -// /// -// /// The key must be canonical register. -// registers: [tracked_registers.len]Air.Inst.Index = undefined, -// /// Tracks which registers are free (in which case the -// /// corresponding bit is set to 1) -// free_registers: RegisterBitSet = RegisterBitSet.initFull(), -// /// Tracks all registers allocated in the course of this -// /// function -// allocated_registers: RegisterBitSet = RegisterBitSet.initEmpty(), -// /// Tracks registers which are locked from being allocated -// locked_registers: RegisterBitSet = RegisterBitSet.initEmpty(), - -// const Self = @This(); - -// pub const RegisterBitSet = StaticBitSet(tracked_registers.len); - -// fn getFunction(self: *Self) *Function { -// return @fieldParentPtr(Function, "register_manager", self); -// } - -// fn excludeRegister(reg: Register, register_class: RegisterBitSet) bool { -// const index = indexOfRegIntoTracked(reg) orelse return true; -// return !register_class.isSet(index); -// } - -// fn markRegAllocated(self: *Self, reg: Register) void { -// const index = indexOfRegIntoTracked(reg) orelse return; -// self.allocated_registers.set(index); -// } - -// fn markRegUsed(self: *Self, reg: Register) void { -// const index = indexOfRegIntoTracked(reg) orelse return; -// self.free_registers.unset(index); -// } - -// fn markRegFree(self: *Self, reg: Register) void { -// const index = indexOfRegIntoTracked(reg) orelse return; -// self.free_registers.set(index); -// } - -// pub fn indexOfReg( -// comptime registers: []const Register, -// reg: Register, -// ) ?std.math.IntFittingRange(0, registers.len - 1) { -// inline for (tracked_registers) |cpreg, i| { -// if (reg.id() == cpreg.id()) return i; -// } -// return null; -// } - -// pub fn indexOfRegIntoTracked(reg: Register) ?RegisterBitSet.ShiftInt { -// return indexOfReg(tracked_registers, reg); -// } - -// /// Returns true when this register is not tracked -// pub fn isRegFree(self: Self, reg: Register) bool { -// const index = indexOfRegIntoTracked(reg) orelse return true; -// return self.free_registers.isSet(index); -// } - -// /// Returns whether this register was allocated in the course -// /// of this function. -// /// -// /// Returns false when this register is not tracked -// pub fn isRegAllocated(self: Self, reg: Register) bool { -// const index = indexOfRegIntoTracked(reg) orelse return false; -// return self.allocated_registers.isSet(index); -// } - -// /// Returns whether this register is locked -// /// -// /// Returns false when this register is not tracked -// pub fn isRegLocked(self: Self, reg: Register) bool { -// const index = indexOfRegIntoTracked(reg) orelse return false; -// return self.locked_registers.isSet(index); -// } - -// pub const RegisterLock = struct { -// register: Register, -// }; - -// /// Prevents the register from being allocated until they are -// /// unlocked again. -// /// Returns `RegisterLock` if the register was not already -// /// locked, or `null` otherwise. -// /// Only the owner of the `RegisterLock` can unlock the -// /// register later. -// pub fn lockReg(self: *Self, reg: Register) ?RegisterLock { -// log.debug("locking {}", .{reg}); -// if (self.isRegLocked(reg)) { -// log.debug(" register already locked", .{}); -// return null; -// } -// const index = indexOfRegIntoTracked(reg) orelse return null; -// self.locked_registers.set(index); -// return RegisterLock{ .register = reg }; -// } - -// /// Like `lockReg` but asserts the register was unused always -// /// returning a valid lock. -// pub fn lockRegAssumeUnused(self: *Self, reg: Register) RegisterLock { -// log.debug("locking asserting free {}", .{reg}); -// assert(!self.isRegLocked(reg)); -// const index = indexOfRegIntoTracked(reg) orelse unreachable; -// self.locked_registers.set(index); -// return RegisterLock{ .register = reg }; -// } - -// /// Like `lockRegAssumeUnused` but locks multiple registers. -// pub fn lockRegsAssumeUnused( -// self: *Self, -// comptime count: comptime_int, -// regs: [count]Register, -// ) [count]RegisterLock { -// var buf: [count]RegisterLock = undefined; -// for (regs) |reg, i| { -// buf[i] = self.lockRegAssumeUnused(reg); -// } -// return buf; -// } - -// /// Unlocks the register allowing its re-allocation and re-use. -// /// Requires `RegisterLock` to unlock a register. -// /// Call `lockReg` to obtain the lock first. -// pub fn unlockReg(self: *Self, lock: RegisterLock) void { -// log.debug("unlocking {}", .{lock.register}); -// const index = indexOfRegIntoTracked(lock.register) orelse return; -// self.locked_registers.unset(index); -// } - -// /// Returns true when at least one register is locked -// pub fn lockedRegsExist(self: Self) bool { -// return self.locked_registers.count() > 0; -// } - -// /// Allocates a specified number of registers, optionally -// /// tracking them. Returns `null` if not enough registers are -// /// free. -// pub fn tryAllocRegs( -// self: *Self, -// comptime count: comptime_int, -// insts: [count]?Air.Inst.Index, -// register_class: RegisterBitSet, -// ) ?[count]Register { -// comptime assert(count > 0 and count <= tracked_registers.len); - -// var free_and_not_locked_registers = self.free_registers; -// free_and_not_locked_registers.setIntersection(register_class); - -// var unlocked_registers = self.locked_registers; -// unlocked_registers.toggleAll(); - -// free_and_not_locked_registers.setIntersection(unlocked_registers); - -// if (free_and_not_locked_registers.count() < count) return null; - -// var regs: [count]Register = undefined; -// var i: usize = 0; -// for (tracked_registers) |reg| { -// if (i >= count) break; -// if (excludeRegister(reg, register_class)) continue; -// if (self.isRegLocked(reg)) continue; -// if (!self.isRegFree(reg)) continue; - -// regs[i] = reg; -// i += 1; -// } -// assert(i == count); - -// for (regs) |reg, j| { -// self.markRegAllocated(reg); - -// if (insts[j]) |inst| { -// // Track the register -// const index = indexOfRegIntoTracked(reg).?; // indexOfReg() on a callee-preserved reg should never return null -// self.registers[index] = inst; -// self.markRegUsed(reg); -// } -// } - -// return regs; -// } - -// /// Allocates a register and optionally tracks it with a -// /// corresponding instruction. Returns `null` if all registers -// /// are allocated. -// pub fn tryAllocReg(self: *Self, inst: ?Air.Inst.Index, register_class: RegisterBitSet) ?Register { -// return if (tryAllocRegs(self, 1, .{inst}, register_class)) |regs| regs[0] else null; -// } - -// /// Allocates a specified number of registers, optionally -// /// tracking them. Asserts that count is not -// /// larger than the total number of registers available. -// pub fn allocRegs( -// self: *Self, -// comptime count: comptime_int, -// insts: [count]?Air.Inst.Index, -// register_class: RegisterBitSet, -// ) AllocateRegistersError![count]Register { -// comptime assert(count > 0 and count <= tracked_registers.len); - -// var locked_registers = self.locked_registers; -// locked_registers.setIntersection(register_class); - -// if (count > register_class.count() - locked_registers.count()) return error.OutOfRegisters; - -// const result = self.tryAllocRegs(count, insts, register_class) orelse blk: { -// // We'll take over the first count registers. Spill -// // the instructions that were previously there to a -// // stack allocations. -// var regs: [count]Register = undefined; -// var i: usize = 0; -// for (tracked_registers) |reg| { -// if (i >= count) break; -// if (excludeRegister(reg, register_class)) break; -// if (self.isRegLocked(reg)) continue; - -// regs[i] = reg; -// self.markRegAllocated(reg); -// const index = indexOfRegIntoTracked(reg).?; // indexOfReg() on a callee-preserved reg should never return null -// if (insts[i]) |inst| { -// // Track the register -// if (self.isRegFree(reg)) { -// self.markRegUsed(reg); -// } else { -// const spilled_inst = self.registers[index]; -// try self.getFunction().spillInstruction(reg, spilled_inst); -// } -// self.registers[index] = inst; -// } else { -// // Don't track the register -// if (!self.isRegFree(reg)) { -// const spilled_inst = self.registers[index]; -// try self.getFunction().spillInstruction(reg, spilled_inst); -// self.freeReg(reg); -// } -// } - -// i += 1; -// } - -// break :blk regs; -// }; - -// log.debug("allocated registers {any} for insts {any}", .{ result, insts }); -// return result; -// } - -// /// Allocates a register and optionally tracks it with a -// /// corresponding instruction. -// pub fn allocReg( -// self: *Self, -// inst: ?Air.Inst.Index, -// register_class: RegisterBitSet, -// ) AllocateRegistersError!Register { -// return (try self.allocRegs(1, .{inst}, register_class))[0]; -// } - -// /// Spills the register if it is currently allocated. If a -// /// corresponding instruction is passed, will also track this -// /// register. -// pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocateRegistersError!void { -// const index = indexOfRegIntoTracked(reg) orelse return; -// log.debug("getReg {} for inst {}", .{ reg, inst }); -// self.markRegAllocated(reg); - -// if (inst) |tracked_inst| -// if (!self.isRegFree(reg)) { -// // Move the instruction that was previously there to a -// // stack allocation. -// const spilled_inst = self.registers[index]; -// self.registers[index] = tracked_inst; -// try self.getFunction().spillInstruction(reg, spilled_inst); -// } else { -// self.getRegAssumeFree(reg, tracked_inst); -// } -// else { -// if (!self.isRegFree(reg)) { -// // Move the instruction that was previously there to a -// // stack allocation. -// const spilled_inst = self.registers[index]; -// try self.getFunction().spillInstruction(reg, spilled_inst); -// self.freeReg(reg); -// } -// } -// } - -// /// Allocates the specified register with the specified -// /// instruction. Asserts that the register is free and no -// /// spilling is necessary. -// pub fn getRegAssumeFree(self: *Self, reg: Register, inst: Air.Inst.Index) void { -// const index = indexOfRegIntoTracked(reg) orelse return; -// log.debug("getRegAssumeFree {} for inst {}", .{ reg, inst }); -// self.markRegAllocated(reg); - -// assert(self.isRegFree(reg)); -// self.registers[index] = inst; -// self.markRegUsed(reg); -// } - -// /// Marks the specified register as free -// pub fn freeReg(self: *Self, reg: Register) void { -// const index = indexOfRegIntoTracked(reg) orelse return; -// log.debug("freeing register {}", .{reg}); - -// self.registers[index] = undefined; -// self.markRegFree(reg); -// } -// }; -//} - const MockRegister1 = enum(u2) { r0, r1, @@ -698,7 +366,14 @@ const MockRegister1 = enum(u2) { &MockRegister1.allocatable_registers, ); - const gp: RM.RegisterBitSet = std.math.maxInt(RM.RegisterBitSet); + const gp: RM.RegisterBitSet = blk: { + var set = RM.RegisterBitSet.initEmpty(); + set.setRangeValue(.{ + .start = 0, + .end = allocatable_registers.len, + }, true); + break :blk set; + }; }; const MockRegister2 = enum(u2) { @@ -719,7 +394,14 @@ const MockRegister2 = enum(u2) { &MockRegister2.allocatable_registers, ); - const gp: RM.RegisterBitSet = std.math.maxInt(RM.RegisterBitSet); + const gp: RM.RegisterBitSet = blk: { + var set = RM.RegisterBitSet.initEmpty(); + set.setRangeValue(.{ + .start = 0, + .end = allocatable_registers.len, + }, true); + break :blk set; + }; }; const MockRegister3 = enum(u3) { @@ -753,11 +435,22 @@ const MockRegister3 = enum(u3) { &MockRegister3.allocatable_registers, ); - const gp: RM.RegisterBitSet = @as(RM.RegisterBitSet, std.math.maxInt(std.meta.Int( - .unsigned, - gp_regs.len, - ))); - const ext: RM.RegisterBitSet = std.math.maxInt(RM.RegisterBitSet) - gp; + const gp: RM.RegisterBitSet = blk: { + var set = RM.RegisterBitSet.initEmpty(); + set.setRangeValue(.{ + .start = 0, + .end = gp_regs.len, + }, true); + break :blk set; + }; + const ext: RM.RegisterBitSet = blk: { + var set = RM.RegisterBitSet.initEmpty(); + set.setRangeValue(.{ + .start = gp_regs.len, + .end = allocatable_registers.len, + }, true); + break :blk set; + }; }; fn MockFunction(comptime Register: type) type { From 92c4e4f2c234816a70472250702f834a1842f4e6 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 26 May 2022 12:42:50 +0300 Subject: [PATCH 1653/2031] Sema: `zirArrayType` does need source location --- src/Sema.zig | 6 ++++-- test/cases/compile_errors/invalid_array_elem_ty.zig | 11 +++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 test/cases/compile_errors/invalid_array_elem_ty.zig diff --git a/src/Sema.zig b/src/Sema.zig index 80184cb287..41111cd650 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5814,8 +5814,10 @@ fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A defer tracy.end(); const bin_inst = sema.code.instructions.items(.data)[inst].bin; - const len = try sema.resolveInt(block, .unneeded, bin_inst.lhs, Type.usize); - const elem_type = try sema.resolveType(block, .unneeded, bin_inst.rhs); + const len_src = sema.src; // TODO better source location + const elem_src = sema.src; // TODO better source location + const len = try sema.resolveInt(block, len_src, bin_inst.lhs, Type.usize); + const elem_type = try sema.resolveType(block, elem_src, bin_inst.rhs); const array_ty = try Type.array(sema.arena, len, null, elem_type, sema.mod); return sema.addType(array_ty); diff --git a/test/cases/compile_errors/invalid_array_elem_ty.zig b/test/cases/compile_errors/invalid_array_elem_ty.zig new file mode 100644 index 0000000000..bfa79a104b --- /dev/null +++ b/test/cases/compile_errors/invalid_array_elem_ty.zig @@ -0,0 +1,11 @@ +pub fn S() type { + return struct {}; +} +pub export fn entry() void { + _ = [0]S; +} + +// error +// backend=stage2,llvm +// +// :4:1: error: expected type, found fn() type From b08d32ceb5aac5b1ba73c84449c6afee630710bb Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 26 May 2022 01:38:32 +0200 Subject: [PATCH 1654/2031] crypto/25519: add scalar.random(), use CompressedScalar type Add the ability to generate a random, canonical curve25519 scalar, like we do for p256. Also leverage the existing CompressedScalar type to represent these scalars. --- lib/std/crypto/25519/scalar.zig | 79 +++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 19 deletions(-) diff --git a/lib/std/crypto/25519/scalar.zig b/lib/std/crypto/25519/scalar.zig index ee3a59c244..c3170673d1 100644 --- a/lib/std/crypto/25519/scalar.zig +++ b/lib/std/crypto/25519/scalar.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const crypto = std.crypto; const mem = std.mem; const NonCanonicalError = std.crypto.errors.NonCanonicalError; @@ -15,7 +16,7 @@ pub const CompressedScalar = [32]u8; pub const zero = [_]u8{0} ** 32; /// Reject a scalar whose encoding is not canonical. -pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void { +pub fn rejectNonCanonical(s: CompressedScalar) NonCanonicalError!void { var c: u8 = 0; var n: u8 = 1; var i: usize = 31; @@ -32,34 +33,34 @@ pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void { } /// Reduce a scalar to the field size. -pub fn reduce(s: [32]u8) [32]u8 { +pub fn reduce(s: CompressedScalar) CompressedScalar { return Scalar.fromBytes(s).toBytes(); } /// Reduce a 64-bytes scalar to the field size. -pub fn reduce64(s: [64]u8) [32]u8 { +pub fn reduce64(s: [64]u8) CompressedScalar { return ScalarDouble.fromBytes64(s).toBytes(); } /// Perform the X25519 "clamping" operation. /// The scalar is then guaranteed to be a multiple of the cofactor. -pub inline fn clamp(s: *[32]u8) void { +pub inline fn clamp(s: *CompressedScalar) void { s[0] &= 248; s[31] = (s[31] & 127) | 64; } /// Return a*b (mod L) -pub fn mul(a: [32]u8, b: [32]u8) [32]u8 { +pub fn mul(a: CompressedScalar, b: CompressedScalar) CompressedScalar { return Scalar.fromBytes(a).mul(Scalar.fromBytes(b)).toBytes(); } /// Return a*b+c (mod L) -pub fn mulAdd(a: [32]u8, b: [32]u8, c: [32]u8) [32]u8 { +pub fn mulAdd(a: CompressedScalar, b: CompressedScalar, c: CompressedScalar) CompressedScalar { return Scalar.fromBytes(a).mul(Scalar.fromBytes(b)).add(Scalar.fromBytes(c)).toBytes(); } /// Return a*8 (mod L) -pub fn mul8(s: [32]u8) [32]u8 { +pub fn mul8(s: CompressedScalar) CompressedScalar { var x = Scalar.fromBytes(s); x = x.add(x); x = x.add(x); @@ -68,12 +69,12 @@ pub fn mul8(s: [32]u8) [32]u8 { } /// Return a+b (mod L) -pub fn add(a: [32]u8, b: [32]u8) [32]u8 { +pub fn add(a: CompressedScalar, b: CompressedScalar) CompressedScalar { return Scalar.fromBytes(a).add(Scalar.fromBytes(b)).toBytes(); } /// Return -s (mod L) -pub fn neg(s: [32]u8) [32]u8 { +pub fn neg(s: CompressedScalar) CompressedScalar { const fs: [64]u8 = field_size ++ [_]u8{0} ** 32; var sx: [64]u8 = undefined; mem.copy(u8, sx[0..32], s[0..]); @@ -89,23 +90,33 @@ pub fn neg(s: [32]u8) [32]u8 { } /// Return (a-b) (mod L) -pub fn sub(a: [32]u8, b: [32]u8) [32]u8 { +pub fn sub(a: CompressedScalar, b: CompressedScalar) CompressedScalar { return add(a, neg(b)); } +/// Return a random scalar < L +pub fn random() CompressedScalar { + return Scalar.random().toBytes(); +} + /// A scalar in unpacked representation pub const Scalar = struct { const Limbs = [5]u64; limbs: Limbs = undefined, /// Unpack a 32-byte representation of a scalar - pub fn fromBytes(bytes: [32]u8) Scalar { + pub fn fromBytes(bytes: CompressedScalar) Scalar { return ScalarDouble.fromBytes32(bytes).reduce(5); } + /// Unpack a 64-byte representation of a scalar + pub fn fromBytes64(bytes: [64]u8) Scalar { + return ScalarDouble.fromBytes64(bytes).reduce(5); + } + /// Pack a scalar into bytes - pub fn toBytes(expanded: *const Scalar) [32]u8 { - var bytes: [32]u8 = undefined; + pub fn toBytes(expanded: *const Scalar) CompressedScalar { + var bytes: CompressedScalar = undefined; var i: usize = 0; while (i < 4) : (i += 1) { mem.writeIntLittle(u64, bytes[i * 7 ..][0..8], expanded.limbs[i]); @@ -114,7 +125,13 @@ pub const Scalar = struct { return bytes; } - /// Return x+y (mod l) + /// Return true if the scalar is zero + pub fn isZero(n: Scalar) bool { + const limbs = n.limbs; + return (limbs[0] | limbs[1] | limbs[2] | limbs[3] | limbs[4]) == 0; + } + + /// Return x+y (mod L) pub fn add(x: Scalar, y: Scalar) Scalar { const carry0 = (x.limbs[0] + y.limbs[0]) >> 56; const t0 = (x.limbs[0] + y.limbs[0]) & 0xffffffffffffff; @@ -171,7 +188,7 @@ pub const Scalar = struct { return Scalar{ .limbs = .{ z00, z10, z20, z30, z40 } }; } - /// Return x*r (mod l) + /// Return x*r (mod L) pub fn mul(x: Scalar, y: Scalar) Scalar { const xy000 = @as(u128, x.limbs[0]) * @as(u128, y.limbs[0]); const xy010 = @as(u128, x.limbs[0]) * @as(u128, y.limbs[1]); @@ -483,7 +500,7 @@ pub const Scalar = struct { return Scalar{ .limbs = .{ z04, z14, z24, z34, z44 } }; } - /// Return x^2 (mod l) + /// Return x^2 (mod L) pub fn sq(x: Scalar) Scalar { return x.mul(x); } @@ -503,7 +520,7 @@ pub const Scalar = struct { return x.sqn(n).mul(y); } - /// Return the inverse of a scalar (mod l), or 0 if x=0. + /// Return the inverse of a scalar (mod L), or 0 if x=0. pub fn invert(x: Scalar) Scalar { const _10 = x.sq(); const _11 = x.mul(_10); @@ -533,6 +550,18 @@ pub const Scalar = struct { .sqn_mul(9, _1101011).sqn_mul(6, _1011).sqn_mul(14, _10010011).sqn_mul(10, _1100011) .sqn_mul(9, _10010111).sqn_mul(10, _11110101).sqn_mul(8, _11010011).sqn_mul(8, _11101011); } + + /// Return a random scalar < L. + pub fn random() Scalar { + var s: [64]u8 = undefined; + while (true) { + crypto.random.bytes(&s); + const n = Scalar.fromBytes64(s); + if (!n.isZero()) { + return n; + } + } + } }; const ScalarDouble = struct { @@ -549,7 +578,7 @@ const ScalarDouble = struct { return ScalarDouble{ .limbs = limbs }; } - fn fromBytes32(bytes: [32]u8) ScalarDouble { + fn fromBytes32(bytes: CompressedScalar) ScalarDouble { var limbs: Limbs = undefined; var i: usize = 0; while (i < 4) : (i += 1) { @@ -560,7 +589,7 @@ const ScalarDouble = struct { return ScalarDouble{ .limbs = limbs }; } - fn toBytes(expanded_double: *ScalarDouble) [32]u8 { + fn toBytes(expanded_double: *ScalarDouble) CompressedScalar { return expanded_double.reduce(10).toBytes(); } @@ -840,3 +869,15 @@ test "scalar field inversion" { const recovered_x = inv.invert(); try std.testing.expectEqualSlices(u8, &bytes, &recovered_x.toBytes()); } + +test "random scalar" { + const s1 = random(); + const s2 = random(); + try std.testing.expect(!mem.eql(u8, &s1, &s2)); +} + +test "64-bit reduction" { + const bytes = field_size ++ [_]u8{0} ** 32; + const x = Scalar.fromBytes64(bytes); + try std.testing.expect(x.isZero()); +} From d1e46006758a82f039d23ea26030e3b685a5fa86 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 26 May 2022 16:23:52 +0200 Subject: [PATCH 1655/2031] test: correctly track identical error msgs in handled errors list Prior to this change, for an example compiler error test case with multiple identical errors messages such as ``` :1:2: error: foo :1:2: error: foo ``` the test harness would never increment the error index thus only marking the very first error message as handled yielding a false positive. Additionally, while here, regress `dereference_anyopaque` test case as not passing on `wasm32-wasi` target. --- src/test.zig | 2 ++ test/cases/compile_errors/dereference_anyopaque.zig | 1 + 2 files changed, 3 insertions(+) diff --git a/src/test.zig b/src/test.zig index a6537e77c7..3d1f536f2c 100644 --- a/src/test.zig +++ b/src/test.zig @@ -1574,6 +1574,8 @@ pub const TestContext = struct { for (actual_errors.list) |actual_error| { for (case_error_list) |case_msg, i| { + if (handled_errors[i]) continue; + const ex_tag: std.meta.Tag(@TypeOf(case_msg)) = case_msg; switch (actual_error) { .src => |actual_msg| { diff --git a/test/cases/compile_errors/dereference_anyopaque.zig b/test/cases/compile_errors/dereference_anyopaque.zig index 44636b0851..cef57a5189 100644 --- a/test/cases/compile_errors/dereference_anyopaque.zig +++ b/test/cases/compile_errors/dereference_anyopaque.zig @@ -42,6 +42,7 @@ pub export fn entry() void { // error // backend=llvm +// target=x86_64-linux,aarch64-linux,arm-linux,x86_64-macos,aarch64-macos // // :11:22: error: comparison of 'void' with null // :25:51: error: unable to resolve comptime value From 5ce9c878cf83b5d8ab0407b798fe4aa35768c498 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 26 May 2022 16:55:31 +0200 Subject: [PATCH 1656/2031] apply Vexu's suggestion to use failing_allocator for now --- test/cases/compile_errors/dereference_anyopaque.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/cases/compile_errors/dereference_anyopaque.zig b/test/cases/compile_errors/dereference_anyopaque.zig index cef57a5189..68be3c97ed 100644 --- a/test/cases/compile_errors/dereference_anyopaque.zig +++ b/test/cases/compile_errors/dereference_anyopaque.zig @@ -36,13 +36,12 @@ fn parseFree(comptime T: type, value: T, allocator: std.mem.Allocator) void { } pub export fn entry() void { - const allocator = std.testing.allocator_instance.allocator(); + const allocator = std.testing.failing_allocator; _ = parse(std.StringArrayHashMap(bool), allocator) catch return; } // error // backend=llvm -// target=x86_64-linux,aarch64-linux,arm-linux,x86_64-macos,aarch64-macos // // :11:22: error: comparison of 'void' with null // :25:51: error: unable to resolve comptime value From 7deae071014237e995ec3017825f7534305ec0c4 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Thu, 26 May 2022 18:23:07 +0200 Subject: [PATCH 1657/2031] std.PriorityQueue: fix missing siftUp in remove When the replacement node is smaller than its parent, we need to sift up instead of sifting down. --- lib/std/priority_queue.zig | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/std/priority_queue.zig b/lib/std/priority_queue.zig index 52d9f59951..ebc13a9974 100644 --- a/lib/std/priority_queue.zig +++ b/lib/std/priority_queue.zig @@ -100,7 +100,19 @@ pub fn PriorityQueue(comptime T: type, comptime Context: type, comptime compareF const item = self.items[index]; self.items[index] = last; self.len -= 1; - siftDown(self, index); + + if (index == 0) { + siftDown(self, index); + } else { + const parent_index = ((index - 1) >> 1); + const parent = self.items[parent_index]; + if (compareFn(self.context, last, parent) == .gt) { + siftDown(self, index); + } else { + siftUp(self, index); + } + } + return item; } @@ -576,6 +588,20 @@ test "std.PriorityQueue: update same max heap" { try expectEqual(@as(u32, 1), queue.remove()); } +test "std.PriorityQueue: siftUp in remove" { + var queue = PQlt.init(testing.allocator, {}); + defer queue.deinit(); + + try queue.addSlice(&.{ 0, 1, 100, 2, 3, 101, 102, 4, 5, 6, 7, 103, 104, 105, 106, 8 }); + + _ = queue.removeIndex(std.mem.indexOfScalar(u32, queue.items[0..queue.len], 102).?); + + const sorted_items = [_]u32{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 100, 101, 103, 104, 105, 106 }; + for (sorted_items) |e| { + try expectEqual(e, queue.remove()); + } +} + fn contextLessThan(context: []const u32, a: usize, b: usize) Order { return std.math.order(context[a], context[b]); } From 67d5bfefba48d28c02e2841f1a47a213d28d4693 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 26 May 2022 16:22:47 -0700 Subject: [PATCH 1658/2031] std.testing: remove tight coupling with executing zig as child process This tight coupling causes problems for various targets, requires hacky "get args" functionality, and bungles relative file system paths, making invalid assumptions about the zig-cache directory. In short, these are not unit tests; these should be standalone tests instead. Reverts e5d4a694ea7dd251e10d6434c9321b5e0a548d4b Reverts d976456ef665bf0aba3a83a8e7fccb4a92b2d3b2 Reverts dbbda0f41a7c5e214801925f8447a15193c3c731 Closes #11542 --- lib/std/child_process.zig | 88 --------------------------------------- lib/std/testing.zig | 51 ----------------------- 2 files changed, 139 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 0bb737decb..6e6d23d2bd 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -1323,91 +1323,3 @@ test "createNullDelimitedEnvMap" { } } } - -const childstr = - \\ const std = @import("std"); - \\ const builtin = @import("builtin"); - \\ pub fn main() !void { - \\ var it = try std.process.argsWithAllocator(std.testing.allocator); - \\ defer it.deinit(); // no-op unless WASI or Windows - \\ _ = it.next() orelse unreachable; // skip binary name - \\ const input = it.next() orelse unreachable; - \\ var expect_helloworld = "hello world".*; - \\ try std.testing.expect(std.mem.eql(u8, &expect_helloworld, input)); - \\ try std.testing.expect(it.next() == null); - \\ try std.testing.expect(!it.skip()); - \\ } -; - -test "build and call child_process" { - if (builtin.os.tag == .wasi) return error.SkipZigTest; - const testing = std.testing; - const allocator = testing.allocator; - - var it = try std.process.argsWithAllocator(allocator); - defer it.deinit(); // no-op unless WASI or Windows - const testargs = try testing.getTestArgs(&it); - - var tmp = testing.tmpDir(.{ .no_follow = true }); // ie zig-cache/tmp/8DLgoSEqz593PAEE - defer tmp.cleanup(); - const tmpdirpath = try tmp.getFullPath(allocator); - defer allocator.free(tmpdirpath); - const child_name = "child"; // no need for suffixes (.exe, .wasm) due to '-femit-bin' - const suffix_zig = ".zig"; - const child_path = try fs.path.join(allocator, &[_][]const u8{ tmpdirpath, child_name }); - defer allocator.free(child_path); - const child_zig = try mem.concat(allocator, u8, &[_][]const u8{ child_path, suffix_zig }); - defer allocator.free(child_zig); - - try tmp.dir.writeFile("child.zig", childstr); - try testing.buildExe(testargs.zigexec, child_zig, child_path); - - // spawn compiled file as child_process with argument 'hello world' + expect success - const args = [_][]const u8{ child_path, "hello world" }; - var child_proc = ChildProcess.init(&args, allocator); - const ret_val = try child_proc.spawnAndWait(); - try testing.expectEqual(ret_val, .{ .Exited = 0 }); -} - -test "creating a child process with stdin and stdout behavior set to StdIo.Pipe" { - if (builtin.os.tag == .wasi) return error.SkipZigTest; - const testing = std.testing; - const allocator = testing.allocator; - - var child_process = std.ChildProcess.init( - &[_][]const u8{ testing.zig_exe_path, "fmt", "--stdin" }, - allocator, - ); - child_process.stdin_behavior = .Pipe; - child_process.stdout_behavior = .Pipe; - - try child_process.spawn(); - - const input_program = - \\ const std = @import("std"); - \\ pub fn main() void { - \\ std.debug.print("Hello World", .{}); - \\ } - ; - - try child_process.stdin.?.writer().writeAll(input_program); - child_process.stdin.?.close(); - child_process.stdin = null; - - const out_bytes = try child_process.stdout.?.reader().readAllAlloc(allocator, std.math.maxInt(usize)); - defer allocator.free(out_bytes); - - switch (try child_process.wait()) { - .Exited => |code| if (code == 0) { - const expected_program = - \\const std = @import("std"); - \\pub fn main() void { - \\ std.debug.print("Hello World", .{}); - \\} - \\ - ; - try testing.expectEqualStrings(expected_program, out_bytes); - }, - else => unreachable, - } -} diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 174e898bca..4e43413d28 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -355,19 +355,6 @@ pub const TmpDir = struct { const random_bytes_count = 12; const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count); - /// caller owns memory - pub fn getFullPath(self: *TmpDir, alloc: std.mem.Allocator) ![]u8 { - const cwd_str = try std.process.getCwdAlloc(alloc); - defer alloc.free(cwd_str); - const path = try std.fs.path.join(alloc, &[_][]const u8{ - cwd_str, - "zig-cache", - "tmp", - &self.sub_path, - }); - return path; - } - pub fn cleanup(self: *TmpDir) void { self.dir.close(); self.parent_dir.deleteTree(&self.sub_path) catch {}; @@ -413,44 +400,6 @@ pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir { }; } -const TestArgs = struct { - testexec: [:0]const u8 = undefined, - zigexec: [:0]const u8 = undefined, -}; - -/// Get test arguments inside test block by regular test runner ('zig test file.zig') -/// Caller must provide backing ArgIterator -pub fn getTestArgs(it: *std.process.ArgIterator) !TestArgs { - var testargs = TestArgs{}; - testargs.testexec = it.next() orelse unreachable; - testargs.zigexec = it.next() orelse unreachable; - try expect(!it.skip()); - return testargs; -} - -test "getTestArgs" { - var it = try std.process.argsWithAllocator(allocator); - const testargs = try getTestArgs(&it); - defer it.deinit(); // no-op unless WASI or Windows - try expect(testargs.testexec.len > 0); // zig compiler executable path - try expect(testargs.zigexec.len > 0); // test runner executable path -} - -/// Spawns child process with 'zigexec build-exe zigfile -femit-bin=binfile' -/// and expects success -pub fn buildExe(zigexec: []const u8, zigfile: []const u8, binfile: []const u8) !void { - const flag_emit = "-femit-bin="; - const cmd_emit = try std.mem.concat(allocator, u8, &[_][]const u8{ flag_emit, binfile }); - defer allocator.free(cmd_emit); - - const args = [_][]const u8{ zigexec, "build-exe", zigfile, cmd_emit }; - var procCompileChild = std.ChildProcess.init(&args, allocator); - try procCompileChild.spawn(); - - const ret_val = try procCompileChild.wait(); - try expectEqual(ret_val, .{ .Exited = 0 }); -} - test "expectEqual nested array" { const a = [2][2]f32{ [_]f32{ 1.0, 0.0 }, From 4e918873e7667e8d74b009cb34cbdd4df3305462 Mon Sep 17 00:00:00 2001 From: Hanna Date: Thu, 26 May 2022 19:32:28 -0400 Subject: [PATCH 1659/2031] Rename `std.build.Pkg.path` to `std.build.Pkg.source` (#11557) --- lib/std/build.zig | 22 +++++++++++----------- lib/std/build/OptionsStep.zig | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 0f196cc9e9..668720aa79 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -385,7 +385,7 @@ pub const Builder = struct { pub fn dupePkg(self: *Builder, package: Pkg) Pkg { var the_copy = Pkg{ .name = self.dupe(package.name), - .path = package.path.dupe(self), + .source = package.source.dupe(self), }; if (package.dependencies) |dependencies| { @@ -1353,7 +1353,7 @@ pub const Target = @compileError("deprecated; Use `std.zig.CrossTarget`"); pub const Pkg = struct { name: []const u8, - path: FileSource, + source: FileSource, dependencies: ?[]const Pkg = null, }; @@ -2228,7 +2228,7 @@ pub const LibExeObjStep = struct { } fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void { - package.path.addStepDependencies(&self.step); + package.source.addStepDependencies(&self.step); if (package.dependencies) |deps| { for (deps) |dep| { self.addRecursiveBuildDeps(dep); @@ -2239,7 +2239,7 @@ pub const LibExeObjStep = struct { pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void { self.addPackage(Pkg{ .name = self.builder.dupe(name), - .path = .{ .path = self.builder.dupe(pkg_index_path) }, + .source = .{ .path = self.builder.dupe(pkg_index_path) }, }); } @@ -2300,7 +2300,7 @@ pub const LibExeObjStep = struct { try zig_args.append("--pkg-begin"); try zig_args.append(pkg.name); - try zig_args.append(builder.pathFromRoot(pkg.path.getPath(self.builder))); + try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder))); if (pkg.dependencies) |dependencies| { for (dependencies) |sub_pkg| { @@ -3560,11 +3560,11 @@ test "Builder.dupePkg()" { var pkg_dep = Pkg{ .name = "pkg_dep", - .path = .{ .path = "/not/a/pkg_dep.zig" }, + .source = .{ .path = "/not/a/pkg_dep.zig" }, }; var pkg_top = Pkg{ .name = "pkg_top", - .path = .{ .path = "/not/a/pkg_top.zig" }, + .source = .{ .path = "/not/a/pkg_top.zig" }, .dependencies = &[_]Pkg{pkg_dep}, }; const dupe = builder.dupePkg(pkg_top); @@ -3583,9 +3583,9 @@ test "Builder.dupePkg()" { // the same as those in stack allocated package's fields try std.testing.expect(dupe_deps.ptr != original_deps.ptr); try std.testing.expect(dupe.name.ptr != pkg_top.name.ptr); - try std.testing.expect(dupe.path.path.ptr != pkg_top.path.path.ptr); + try std.testing.expect(dupe.source.path.ptr != pkg_top.source.path.ptr); try std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr); - try std.testing.expect(dupe_deps[0].path.path.ptr != pkg_dep.path.path.ptr); + try std.testing.expect(dupe_deps[0].source.path.ptr != pkg_dep.source.path.ptr); } test "LibExeObjStep.addPackage" { @@ -3605,11 +3605,11 @@ test "LibExeObjStep.addPackage" { const pkg_dep = Pkg{ .name = "pkg_dep", - .path = .{ .path = "/not/a/pkg_dep.zig" }, + .source = .{ .path = "/not/a/pkg_dep.zig" }, }; const pkg_top = Pkg{ .name = "pkg_dep", - .path = .{ .path = "/not/a/pkg_top.zig" }, + .source = .{ .path = "/not/a/pkg_top.zig" }, .dependencies = &[_]Pkg{pkg_dep}, }; diff --git a/lib/std/build/OptionsStep.zig b/lib/std/build/OptionsStep.zig index a615454659..7b219a210e 100644 --- a/lib/std/build/OptionsStep.zig +++ b/lib/std/build/OptionsStep.zig @@ -200,7 +200,7 @@ pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *LibExe } pub fn getPackage(self: *OptionsStep, package_name: []const u8) build.Pkg { - return .{ .name = package_name, .path = self.getSource() }; + return .{ .name = package_name, .source = self.getSource() }; } pub fn getSource(self: *OptionsStep) FileSource { From ee1a95b555464d5a309992b06e02efd07770026c Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sat, 14 May 2022 08:30:29 -0600 Subject: [PATCH 1660/2031] fix semantic error with std.os.linux.all_mask all_mask is a value of type sigset_t, which is defined as an array type [N]u32. However, all_mask references sigset_t.len, but, the array type does not have a len field. Fix is to use @typeInfo(sigset_t).Array.len instead. --- lib/std/os/linux.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 4c85e9c36d..7a0af50f9e 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -3064,7 +3064,7 @@ pub const NSIG = if (is_mips) 128 else 65; pub const sigset_t = [1024 / 32]u32; -pub const all_mask: sigset_t = [_]u32{0xffffffff} ** sigset_t.len; +pub const all_mask: sigset_t = [_]u32{0xffffffff} ** @typeInfo(sigset_t).Array.len; pub const app_mask: sigset_t = [2]u32{ 0xfffffffc, 0x7fffffff } ++ [_]u32{0xffffffff} ** 30; const k_sigaction_funcs = if (builtin.zig_backend == .stage1) struct { From 43373e61fa4ff7098b7b481fa72c1d205c530421 Mon Sep 17 00:00:00 2001 From: Yuto Oguchi Date: Fri, 27 May 2022 09:15:51 +0900 Subject: [PATCH 1661/2031] std.c: Fix incorrect dirent structure (#11602) d_name is a null-terminated string, but it is not guaranteed that the last byte of the array is null. --- lib/std/c/darwin.zig | 6 +++--- lib/std/c/netbsd.zig | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index e44da7e1f7..bb607cb7c3 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -928,12 +928,12 @@ pub const Sigaction = extern struct { }; pub const dirent = extern struct { - d_ino: usize, - d_seekoff: usize, + d_ino: u64, + d_seekoff: u64, d_reclen: u16, d_namlen: u16, d_type: u8, - d_name: u8, // field address is address of first byte of name + d_name: [1024]u8, pub fn reclen(self: dirent) u16 { return self.d_reclen; diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig index e481de0996..d52568d03a 100644 --- a/lib/std/c/netbsd.zig +++ b/lib/std/c/netbsd.zig @@ -337,7 +337,7 @@ pub const dirent = extern struct { d_reclen: u16, d_namlen: u16, d_type: u8, - d_name: [MAXNAMLEN:0]u8, + d_name: [MAXNAMLEN + 1]u8, pub fn reclen(self: dirent) u16 { return self.d_reclen; From 41162ed6467f7e157cc0a45154647dd93bcd2e74 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 26 May 2022 18:59:55 -0700 Subject: [PATCH 1662/2031] stage0: fix -fsingle-threaded CLI arg parsing closes #11730 --- src/stage1/zig0.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stage1/zig0.cpp b/src/stage1/zig0.cpp index c8491b25c4..95c7e34fe9 100644 --- a/src/stage1/zig0.cpp +++ b/src/stage1/zig0.cpp @@ -45,7 +45,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " -OReleaseFast build with optimizations on and safety off\n" " -OReleaseSafe build with optimizations on and safety on\n" " -OReleaseSmall build with size optimizations on and safety off\n" - " --single-threaded source may assume it is only used single-threaded\n" + " -fsingle-threaded source may assume it is only used single-threaded\n" " -dynamic create a shared library (.so; .dll; .dylib)\n" " --strip exclude debug symbols\n" " -target [name] -- see the targets command\n" @@ -282,7 +282,7 @@ int main(int argc, char **argv) { optimize_mode = BuildModeSafeRelease; } else if (strcmp(arg, "-OReleaseSmall") == 0) { optimize_mode = BuildModeSmallRelease; - } else if (strcmp(arg, "--single-threaded") == 0) { + } else if (strcmp(arg, "-fsingle-threaded") == 0) { single_threaded = true; } else if (strcmp(arg, "--help") == 0) { return print_full_usage(arg0, stdout, EXIT_SUCCESS); From 5a8b6149fb0f09d4e221f482b1de225f76bd1840 Mon Sep 17 00:00:00 2001 From: Andreas Reischuck Date: Fri, 27 May 2022 04:05:53 +0200 Subject: [PATCH 1663/2031] add more corner case tests to float_parse (#11727) also drop some unused constants Co-authored-by: Andrew Kelley --- lib/std/fmt/parse_float.zig | 10 ++++++++++ lib/std/fmt/parse_float/parse_float.zig | 2 -- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/std/fmt/parse_float.zig b/lib/std/fmt/parse_float.zig index 44b6fb2fc0..3013ca7865 100644 --- a/lib/std/fmt/parse_float.zig +++ b/lib/std/fmt/parse_float.zig @@ -57,6 +57,16 @@ test "fmt.parseFloat" { try expect(approxEqAbs(T, try parseFloat(T, "1e-2"), 0.01, epsilon)); try expect(approxEqAbs(T, try parseFloat(T, "1234e-2"), 12.34, epsilon)); + try expect(approxEqAbs(T, try parseFloat(T, "1."), 1, epsilon)); + try expect(approxEqAbs(T, try parseFloat(T, "0."), 0, epsilon)); + try expect(approxEqAbs(T, try parseFloat(T, ".1"), 0.1, epsilon)); + try expect(approxEqAbs(T, try parseFloat(T, ".0"), 0, epsilon)); + try expect(approxEqAbs(T, try parseFloat(T, ".1e-1"), 0.01, epsilon)); + + try expectError(error.InvalidCharacter, parseFloat(T, ".")); // At least one digit is required. + try expectError(error.InvalidCharacter, parseFloat(T, ".e1")); // At least one digit is required. + try expectError(error.InvalidCharacter, parseFloat(T, "0.e")); // At least one digit is required. + try expect(approxEqAbs(T, try parseFloat(T, "123142.1"), 123142.1, epsilon)); try expect(approxEqAbs(T, try parseFloat(T, "-123142.1124"), @as(T, -123142.1124), epsilon)); try expect(approxEqAbs(T, try parseFloat(T, "0.7062146892655368"), @as(T, 0.7062146892655368), epsilon)); diff --git a/lib/std/fmt/parse_float/parse_float.zig b/lib/std/fmt/parse_float/parse_float.zig index d781b11495..6d77346e9b 100644 --- a/lib/std/fmt/parse_float/parse_float.zig +++ b/lib/std/fmt/parse_float/parse_float.zig @@ -1,7 +1,5 @@ const std = @import("std"); const parse = @import("parse.zig"); -const parseNumber = parse.parseNumber; -const parseInfOrNan = parse.parseInfOrNan; const convertFast = @import("convert_fast.zig").convertFast; const convertEiselLemire = @import("convert_eisel_lemire.zig").convertEiselLemire; const convertSlow = @import("convert_slow.zig").convertSlow; From f2e8c79763723e394f57af84af977d91652568b5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 26 May 2022 19:36:52 -0700 Subject: [PATCH 1664/2031] std.Progress.log: adjust API Now it will fall back to std.debug.print if there is no tty. --- lib/std/Progress.zig | 5 ++++- lib/test_runner.zig | 3 --- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 925cefcb74..fe2f7bc795 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -297,7 +297,10 @@ fn refreshWithHeldLock(self: *Progress) void { } pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void { - const file = self.terminal orelse return; + const file = self.terminal orelse { + std.debug.print(format, args); + return; + }; self.refresh(); file.writer().print(format, args) catch { self.terminal = null; diff --git a/lib/test_runner.zig b/lib/test_runner.zig index dd88063fea..e3bc7fec86 100644 --- a/lib/test_runner.zig +++ b/lib/test_runner.zig @@ -74,7 +74,6 @@ pub fn main() void { skip_count += 1; test_node.end(); progress.log("SKIP (async test)\n", .{}); - if (!have_tty) std.debug.print("SKIP (async test)\n", .{}); continue; }, } else test_fn.func(); @@ -86,13 +85,11 @@ pub fn main() void { error.SkipZigTest => { skip_count += 1; progress.log("SKIP\n", .{}); - if (!have_tty) std.debug.print("SKIP\n", .{}); test_node.end(); }, else => { fail_count += 1; progress.log("FAIL ({s})\n", .{@errorName(err)}); - if (!have_tty) std.debug.print("FAIL ({s})\n", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } From 4751356d7f1da157665d37e29216628e61ddf079 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 26 May 2022 19:38:28 -0700 Subject: [PATCH 1665/2031] clean up some behavior tests * improve names * properly categorize a couple of bug cases * mark one as already passing --- test/behavior.zig | 2 -- test/behavior/bugs/10970.zig | 3 ++- test/behavior/bugs/11162.zig | 2 +- test/behavior/bugs/11182.zig | 10 ---------- test/behavior/bugs/7027.zig | 21 --------------------- test/behavior/eval.zig | 27 +++++++++++++++++++++++++++ test/behavior/floatop.zig | 6 +----- test/behavior/tuple.zig | 8 ++++++++ 8 files changed, 39 insertions(+), 40 deletions(-) delete mode 100644 test/behavior/bugs/11182.zig delete mode 100644 test/behavior/bugs/7027.zig diff --git a/test/behavior.zig b/test/behavior.zig index 4d8b3587bc..f2a060ce8b 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -67,7 +67,6 @@ test { _ = @import("behavior/bugs/6781.zig"); _ = @import("behavior/bugs/6850.zig"); _ = @import("behavior/bugs/7003.zig"); - _ = @import("behavior/bugs/7027.zig"); _ = @import("behavior/bugs/7047.zig"); _ = @import("behavior/bugs/7187.zig"); _ = @import("behavior/bugs/7250.zig"); @@ -82,7 +81,6 @@ test { _ = @import("behavior/bugs/11162.zig"); _ = @import("behavior/bugs/11165.zig"); _ = @import("behavior/bugs/11181.zig"); - _ = @import("behavior/bugs/11182.zig"); _ = @import("behavior/bugs/11213.zig"); _ = @import("behavior/byteswap.zig"); _ = @import("behavior/byval_arg_var.zig"); diff --git a/test/behavior/bugs/10970.zig b/test/behavior/bugs/10970.zig index 4f33d3d7a4..72084188d7 100644 --- a/test/behavior/bugs/10970.zig +++ b/test/behavior/bugs/10970.zig @@ -3,11 +3,12 @@ const builtin = @import("builtin"); fn retOpt() ?u32 { return null; } -test { +test "breaking from a loop in an if statement" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + var cond = true; const opt = while (cond) { if (retOpt()) |opt| { diff --git a/test/behavior/bugs/11162.zig b/test/behavior/bugs/11162.zig index ced435a305..7a512463e2 100644 --- a/test/behavior/bugs/11162.zig +++ b/test/behavior/bugs/11162.zig @@ -2,7 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); const expect = std.testing.expect; -test { +test "aggregate initializers should allow initializing comptime fields, verifying equality (stage2 only)" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; // TODO if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/11182.zig b/test/behavior/bugs/11182.zig deleted file mode 100644 index eb4ee8ac9d..0000000000 --- a/test/behavior/bugs/11182.zig +++ /dev/null @@ -1,10 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); - -test { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - - const T = @TypeOf(.{ @as(i32, 0), @as(u32, 0) }); - var a = T{ 0, 0 }; - _ = a; -} diff --git a/test/behavior/bugs/7027.zig b/test/behavior/bugs/7027.zig deleted file mode 100644 index a49a1ca1a4..0000000000 --- a/test/behavior/bugs/7027.zig +++ /dev/null @@ -1,21 +0,0 @@ -const Foobar = struct { - myTypes: [128]type, - str: [1024]u8, - - fn foo() @This() { - comptime var foobar: Foobar = undefined; - foobar.str = [_]u8{'a'} ** 1024; - return foobar; - } -}; - -fn foo(arg: anytype) void { - _ = arg; -} - -test { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO - - comptime var foobar = Foobar.foo(); - foo(foobar.str[0..10]); -} diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 63eef12207..4609b58ddb 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -1203,3 +1203,30 @@ test "equality of pointers to comptime const" { const a: i32 = undefined; comptime assert(&a == &a); } + +test "storing an array of type in a field" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() void { + comptime var foobar = Foobar.foo(); + foo(foobar.str[0..10]); + } + const Foobar = struct { + myTypes: [128]type, + str: [1024]u8, + + fn foo() @This() { + comptime var foobar: Foobar = undefined; + foobar.str = [_]u8{'a'} ** 1024; + return foobar; + } + }; + + fn foo(arg: anytype) void { + _ = arg; + } + }; + + S.doTheTest(); +} diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 536c365655..ac35834928 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -651,11 +651,7 @@ test "negation f128" { } test "eval @setFloatMode at compile-time" { - if (builtin.zig_backend != .stage1) { - // let's delay solving this one; I want to re-evaluate this language feature, and - // we don't rely on it for self-hosted. - return error.SkipZigTest; // TODO - } + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const result = comptime fnWithFloatMode(); try expect(result == 1234.0); diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index 80eaea5072..5e044abf4f 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -192,3 +192,11 @@ test "tuple as the result from a labeled block" { try S.doTheTest(); comptime try S.doTheTest(); } + +test "initializing tuple with explicit type" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const T = @TypeOf(.{ @as(i32, 0), @as(u32, 0) }); + var a = T{ 0, 0 }; + _ = a; +} From 83a2c41cd5230561899f9a3accdbb9e1a52af836 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 26 May 2022 21:55:51 -0700 Subject: [PATCH 1666/2031] fix alignment behavior test case As demonstrated by this new test case, stage1's functionality is incorrect since it does not handle slicing from len..len correctly. stage2 already has the correct behavior here. --- test/behavior/align.zig | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 32b0777583..6a819b4a7d 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -18,20 +18,16 @@ test "global variable alignment" { } } -test "slicing array of length 1 can assume runtime index is always zero" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO +test "slicing array of length 1 can not assume runtime index is always zero" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - // TODO reevaluate this test case, because notice that you can - // change `runtime_zero` to be `1` and the test still passes for stage1. - // Reconsider also this code: - // var array: [4]u8 = undefined; - // var runtime: usize = 4; - // var ptr = array[runtime..]; - // _ = ptr; - - var runtime_zero: usize = 0; - const slice = @as(*align(4) [1]u8, &foo)[runtime_zero..]; - comptime try expect(@TypeOf(slice) == []align(4) u8); + var runtime_index: usize = 1; + const slice = @as(*align(4) [1]u8, &foo)[runtime_index..]; + try expect(@TypeOf(slice) == []u8); + try expect(slice.len == 0); + try expect(@truncate(u2, @ptrToInt(slice.ptr) - 1) == 0); } test "default alignment allows unspecified in type syntax" { From b3672e073819fdb5c2d21c0b50127c1bfcdc9b3f Mon Sep 17 00:00:00 2001 From: r00ster Date: Thu, 26 May 2022 18:53:41 +0200 Subject: [PATCH 1667/2031] Fix grammatical error Fixes #11675 --- doc/langref.html.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 32320d5113..1f445b1b59 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -392,7 +392,7 @@ pub fn main() !void { The code sample begins by adding the {#link|Zig Standard Library#} to the build using the {#link|@import#} builtin function. The {#syntax#}@import("std"){#endsyntax#} function call creates a structure that represents the Zig Standard Library. The code then {#link|declares|Container Level Variables#} a - {#link|constant identifier|Assignment#}, named {#syntax#}std{#endsyntax#}, that gives access the features of the Zig Standard Library. + {#link|constant identifier|Assignment#}, named {#syntax#}std{#endsyntax#}, that gives access to the features of the Zig Standard Library.

Next, a {#link|public function|Functions#}, {#syntax#}pub fn{#endsyntax#}, named {#syntax#}main{#endsyntax#} From e08cdad53b25876b247322aed91257288e8b9230 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 26 May 2022 13:07:51 +0300 Subject: [PATCH 1668/2031] Sema: add error for runtime indexing comptime array --- src/Sema.zig | 42 ++++++++++++++++++- .../runtime_indexing_comptime_array.zig | 31 ++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 test/cases/compile_errors/runtime_indexing_comptime_array.zig diff --git a/src/Sema.zig b/src/Sema.zig index 41111cd650..170f1e5631 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7309,9 +7309,11 @@ fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air defer tracy.end(); const bin_inst = sema.code.instructions.items(.data)[inst].bin; + const src = sema.src; // TODO better source location + const elem_index_src = sema.src; // TODO better source location const array = try sema.resolveInst(bin_inst.lhs); const elem_index = try sema.resolveInst(bin_inst.rhs); - return sema.elemVal(block, sema.src, array, elem_index, sema.src); + return sema.elemVal(block, src, array, elem_index, elem_index_src); } fn zirElemValNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -18473,6 +18475,25 @@ fn elemValArray( } } + const valid_rt = try sema.validateRunTimeType(block, elem_index_src, elem_ty, false); + if (!valid_rt) { + const msg = msg: { + const msg = try sema.errMsg( + block, + elem_index_src, + "values of type '{}' must be comptime known, but index value is runtime known", + .{array_ty.fmt(sema.mod)}, + ); + errdefer msg.destroy(sema.gpa); + + const src_decl = sema.mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsComptime(block, elem_index_src, msg, array_src.toSrcLoc(src_decl), array_ty); + + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + const runtime_src = if (maybe_undef_array_val != null) elem_index_src else array_src; try sema.requireRuntimeBlock(block, runtime_src); if (block.wantSafety()) { @@ -18528,6 +18549,25 @@ fn elemPtrArray( } } + const valid_rt = try sema.validateRunTimeType(block, elem_index_src, array_ty.elemType2(), false); + if (!valid_rt) { + const msg = msg: { + const msg = try sema.errMsg( + block, + elem_index_src, + "values of type '{}' must be comptime known, but index value is runtime known", + .{array_ty.fmt(sema.mod)}, + ); + errdefer msg.destroy(sema.gpa); + + const src_decl = sema.mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsComptime(block, elem_index_src, msg, array_ptr_src.toSrcLoc(src_decl), array_ty); + + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src; try sema.requireRuntimeBlock(block, runtime_src); if (block.wantSafety()) { diff --git a/test/cases/compile_errors/runtime_indexing_comptime_array.zig b/test/cases/compile_errors/runtime_indexing_comptime_array.zig new file mode 100644 index 0000000000..da603d3630 --- /dev/null +++ b/test/cases/compile_errors/runtime_indexing_comptime_array.zig @@ -0,0 +1,31 @@ +fn foo() void {} +fn bar() void {} + +pub export fn entry1() void { + const TestFn = fn () void; + const test_fns = [_]TestFn{ foo, bar }; + for (test_fns) |testFn| { + testFn(); + } +} +pub export fn entry2() void { + const TestFn = fn () void; + const test_fns = [_]TestFn{ foo, bar }; + var i: usize = 0; + _ = test_fns[i]; +} +pub export fn entry3() void { + const TestFn = fn () void; + const test_fns = [_]TestFn{ foo, bar }; + var i: usize = 0; + _ = &test_fns[i]; +} +// error +// backend=stage2,llvm +// +// :6:33: error: values of type '[2]fn() callconv(.C) void' must be comptime known, but index value is runtime known +// :6:33: note: use '*const fn() callconv(.C) void' for a function pointer type +// :13:33: error: values of type '[2]fn() callconv(.C) void' must be comptime known, but index value is runtime known +// :13:33: note: use '*const fn() callconv(.C) void' for a function pointer type +// :19:33: error: values of type '[2]fn() callconv(.C) void' must be comptime known, but index value is runtime known +// :19:33: note: use '*const fn() callconv(.C) void' for a function pointer type From 8bf3e1f8d0902abd4133e2729b3625c25011c3ff Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 26 May 2022 15:30:42 +0300 Subject: [PATCH 1669/2031] AstGen: preserve inferred ptr result loc for breaks --- src/AstGen.zig | 2 +- src/Sema.zig | 22 ++++++++++++++++++++++ test/behavior/basic.zig | 12 ++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 30ed680226..ba27165cea 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -9915,7 +9915,7 @@ const GenZir = struct { .inferred_ptr => |ptr| { gz.rl_ty_inst = .none; gz.rl_ptr = ptr; - gz.break_result_loc = .{ .block_ptr = gz }; + gz.break_result_loc = parent_rl; }, .block_ptr => |parent_block_scope| { diff --git a/src/Sema.zig b/src/Sema.zig index 170f1e5631..38e5ce0836 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2881,6 +2881,28 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com if (var_is_mut) { try sema.validateVarType(block, ty_src, final_elem_ty, false); + + // The value might have been bitcasted into a comptime only + // pointer type such as `*@Type(.EnumLiteral)` so we must now + // update all the stores to not give backends invalid AIR. + + var air_tags = sema.air_instructions.items(.tag); + var air_data = sema.air_instructions.items(.data); + var peer_inst_index: usize = 0; + var i = ptr_inst; + while (i < air_tags.len and peer_inst_index < peer_inst_list.len) : (i += 1) { + if (air_tags[i] != .store) continue; + if (air_data[i].bin_op.rhs == peer_inst_list[peer_inst_index]) { + peer_inst_index += 1; + _ = (try sema.resolveMaybeUndefVal(block, .unneeded, air_data[i].bin_op.rhs)) orelse continue; + const coerced_val = try sema.coerce(block, final_elem_ty, air_data[i].bin_op.rhs, .unneeded); + air_tags = sema.air_instructions.items(.tag); + air_data = sema.air_instructions.items(.data); + + air_data[i].bin_op.lhs = ptr; + air_data[i].bin_op.rhs = coerced_val; + } + } } else ct: { // Detect if the value is comptime known. In such case, the // last 3 AIR instructions of the block will look like this: diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index d62ba75dee..adcba8721d 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -933,3 +933,15 @@ test "try in labeled block doesn't cast to wrong type" { }; _ = s; } + +test "comptime int in switch in catch is casted to correct inferred type" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + + var a: error{ A, B }!u64 = 0; + var b = a catch |err| switch (err) { + error.A => 0, + else => unreachable, + }; + _ = b; +} From 989c0f55e8bbc790dcdd742ddea3d18c1194eab5 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 26 May 2022 15:50:50 +0300 Subject: [PATCH 1670/2031] stage2: add test for fixed issue --- test/behavior.zig | 1 + test/behavior/bugs/11179.zig | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 test/behavior/bugs/11179.zig diff --git a/test/behavior.zig b/test/behavior.zig index 4d8b3587bc..fabc8c0df1 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -81,6 +81,7 @@ test { _ = @import("behavior/bugs/11159.zig"); _ = @import("behavior/bugs/11162.zig"); _ = @import("behavior/bugs/11165.zig"); + _ = @import("behavior/bugs/11179.zig"); _ = @import("behavior/bugs/11181.zig"); _ = @import("behavior/bugs/11182.zig"); _ = @import("behavior/bugs/11213.zig"); diff --git a/test/behavior/bugs/11179.zig b/test/behavior/bugs/11179.zig new file mode 100644 index 0000000000..84fa6183f3 --- /dev/null +++ b/test/behavior/bugs/11179.zig @@ -0,0 +1,18 @@ +const std = @import("std"); +const Type = std.builtin.Type; + +test "Tuple" { + const fields_list = fields(@TypeOf(.{})); + if (fields_list.len != 0) + @compileError("Argument count mismatch"); +} + +pub fn fields(comptime T: type) switch (@typeInfo(T)) { + .Struct => []const Type.StructField, + else => unreachable, +} { + return switch (@typeInfo(T)) { + .Struct => |info| info.fields, + else => unreachable, + }; +} From ddd5b57045d38b7d1f7d5a4120302797433233cd Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Thu, 26 May 2022 16:03:49 +0200 Subject: [PATCH 1671/2031] stage2 AArch64: complete genTypedValue --- src/arch/aarch64/CodeGen.zig | 36 ++++++++++++++--------------- test/behavior/align.zig | 1 - test/behavior/array.zig | 4 ---- test/behavior/basic.zig | 3 --- test/behavior/bitcast.zig | 1 - test/behavior/bitreverse.zig | 1 - test/behavior/bugs/11165.zig | 2 -- test/behavior/bugs/11181.zig | 2 -- test/behavior/bugs/11213.zig | 1 - test/behavior/bugs/1421.zig | 1 - test/behavior/bugs/394.zig | 1 - test/behavior/bugs/4560.zig | 2 -- test/behavior/bugs/7187.zig | 1 - test/behavior/byteswap.zig | 1 - test/behavior/cast.zig | 2 -- test/behavior/comptime_memory.zig | 4 ---- test/behavior/error.zig | 1 - test/behavior/eval.zig | 7 ------ test/behavior/fn.zig | 9 -------- test/behavior/fn_delegation.zig | 1 - test/behavior/generics.zig | 1 - test/behavior/optional.zig | 2 -- test/behavior/pointers.zig | 2 -- test/behavior/sizeof_and_typeof.zig | 1 - test/behavior/slice.zig | 2 -- test/behavior/struct.zig | 4 ---- test/behavior/type.zig | 2 -- test/behavior/type_info.zig | 1 - test/behavior/union.zig | 1 - test/behavior/void.zig | 2 -- 30 files changed, 18 insertions(+), 81 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index e0c36e70f9..1671f705c7 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -4572,6 +4572,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { } fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { + log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() }); if (typed_value.val.isUndef()) return MCValue{ .undef = {} }; @@ -4585,20 +4586,13 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { switch (typed_value.ty.zigTypeTag()) { .Pointer => switch (typed_value.ty.ptrSize()) { - .Slice => { - return self.lowerUnnamedConst(typed_value); - }, + .Slice => {}, else => { switch (typed_value.val.tag()) { .int_u64 => { return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; }, - .slice => { - return self.lowerUnnamedConst(typed_value); - }, - else => { - return self.fail("TODO codegen more kinds of const pointers: {}", .{typed_value.val.tag()}); - }, + else => {}, } }, }, @@ -4614,15 +4608,11 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { }; return MCValue{ .immediate = unsigned }; - } else { - return self.lowerUnnamedConst(typed_value); } }, .Bool => { return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; }, - .ComptimeInt => unreachable, // semantic analysis prevents this - .ComptimeFloat => unreachable, // semantic analysis prevents this .Optional => { if (typed_value.ty.isPtrLikeOptional()) { if (typed_value.val.isNull()) @@ -4636,7 +4626,6 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { } else if (typed_value.ty.abiSize(self.target.*) == 1) { return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) }; } - return self.fail("TODO non pointer optionals", .{}); }, .Enum => { if (typed_value.val.castTag(.enum_field_index)) |field_index| { @@ -4695,11 +4684,22 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { return self.lowerUnnamedConst(typed_value); }, - .Struct => { - return self.lowerUnnamedConst(typed_value); - }, - else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}), + + .ComptimeInt => unreachable, // semantic analysis prevents this + .ComptimeFloat => unreachable, // semantic analysis prevents this + .Type => unreachable, + .EnumLiteral => unreachable, + .Void => unreachable, + .NoReturn => unreachable, + .Undefined => unreachable, + .Null => unreachable, + .BoundFn => unreachable, + .Opaque => unreachable, + + else => {}, } + + return self.lowerUnnamedConst(typed_value); } const CallMCValues = struct { diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 6a819b4a7d..2c0893074e 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -7,7 +7,6 @@ var foo: u8 align(4) = 100; test "global variable alignment" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO comptime try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4); diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 728e05271a..93de42df67 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -95,8 +95,6 @@ test "array literal with specified size" { } test "array len field" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - var arr = [4]u8{ 0, 0, 0, 0 }; var ptr = &arr; try expect(arr.len == 4); @@ -217,8 +215,6 @@ fn doSomeMangling(array: *[4]u8) void { } test "implicit cast zero sized array ptr to slice" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - { var b = "".*; const c: []const u8 = &b; diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index adcba8721d..ac9e776c76 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -362,7 +362,6 @@ fn testMemcpyMemset() !void { } test "variable is allowed to be a pointer to an opaque type" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO @@ -611,7 +610,6 @@ test "self reference through fn ptr field" { } test "global variable initialized to global variable array element" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; try expect(global_ptr == &gdt[0]); @@ -677,7 +675,6 @@ test "explicit cast optional pointers" { } test "pointer comparison" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index c571851fe4..e851d7de09 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -7,7 +7,6 @@ const minInt = std.math.minInt; const native_endian = builtin.target.cpu.arch.endian(); test "@bitCast iX -> uX (32, 64)" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const bit_values = [_]usize{ 32, 64 }; diff --git a/test/behavior/bitreverse.zig b/test/behavior/bitreverse.zig index 36a3d3475c..821bc21f27 100644 --- a/test/behavior/bitreverse.zig +++ b/test/behavior/bitreverse.zig @@ -158,7 +158,6 @@ test "bitReverse vectors u0" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; comptime try vector0(); try vector0(); diff --git a/test/behavior/bugs/11165.zig b/test/behavior/bugs/11165.zig index cf2f9698f9..b3c4a32ec4 100644 --- a/test/behavior/bugs/11165.zig +++ b/test/behavior/bugs/11165.zig @@ -2,7 +2,6 @@ const builtin = @import("builtin"); test "bytes" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const S = struct { a: u32, @@ -25,7 +24,6 @@ test "bytes" { test "aggregate" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const S = struct { a: u32, diff --git a/test/behavior/bugs/11181.zig b/test/behavior/bugs/11181.zig index 738bd4e3e7..67d26d6b4e 100644 --- a/test/behavior/bugs/11181.zig +++ b/test/behavior/bugs/11181.zig @@ -2,7 +2,6 @@ const builtin = @import("builtin"); test "const inferred array of slices" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const T = struct { v: bool }; @@ -16,7 +15,6 @@ test "const inferred array of slices" { test "var inferred array of slices" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const T = struct { v: bool }; diff --git a/test/behavior/bugs/11213.zig b/test/behavior/bugs/11213.zig index d4e3580c07..f238eb5aab 100644 --- a/test/behavior/bugs/11213.zig +++ b/test/behavior/bugs/11213.zig @@ -4,7 +4,6 @@ const testing = std.testing; test { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const g: error{Test}!void = error.Test; diff --git a/test/behavior/bugs/1421.zig b/test/behavior/bugs/1421.zig index e4e42bd21b..d6ea8a2bad 100644 --- a/test/behavior/bugs/1421.zig +++ b/test/behavior/bugs/1421.zig @@ -9,7 +9,6 @@ const S = struct { }; test "functions with return type required to be comptime are generic" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; const ti = S.method(); diff --git a/test/behavior/bugs/394.zig b/test/behavior/bugs/394.zig index e7d6e80936..9f5165d1ba 100644 --- a/test/behavior/bugs/394.zig +++ b/test/behavior/bugs/394.zig @@ -11,7 +11,6 @@ const expect = @import("std").testing.expect; const builtin = @import("builtin"); test "fixed" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const x = S{ diff --git a/test/behavior/bugs/4560.zig b/test/behavior/bugs/4560.zig index 30d3677920..ea40e55da3 100644 --- a/test/behavior/bugs/4560.zig +++ b/test/behavior/bugs/4560.zig @@ -2,8 +2,6 @@ const std = @import("std"); const builtin = @import("builtin"); test "fixed" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - var s: S = .{ .a = 1, .b = .{ diff --git a/test/behavior/bugs/7187.zig b/test/behavior/bugs/7187.zig index 86f5aa6706..9ce63ad4cd 100644 --- a/test/behavior/bugs/7187.zig +++ b/test/behavior/bugs/7187.zig @@ -3,7 +3,6 @@ const builtin = @import("builtin"); const expect = std.testing.expect; test "miscompilation with bool return type" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var x: usize = 1; diff --git a/test/behavior/byteswap.zig b/test/behavior/byteswap.zig index 6e317b7fc2..13cc823e9d 100644 --- a/test/behavior/byteswap.zig +++ b/test/behavior/byteswap.zig @@ -118,7 +118,6 @@ test "@byteSwap vectors u0" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; comptime try vector0(); try vector0(); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 58b458ad1e..4ff2c78d47 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -843,7 +843,6 @@ test "peer cast *[0]T to []const T" { } test "peer cast *[N]T to [*]T" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var array = [4:99]i32{ 1, 2, 3, 4 }; @@ -1116,7 +1115,6 @@ fn incrementVoidPtrArray(array: ?*anyopaque, len: usize) void { test "compile time int to ptr of function" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO diff --git a/test/behavior/comptime_memory.zig b/test/behavior/comptime_memory.zig index 17cee27771..1a3e9ef606 100644 --- a/test/behavior/comptime_memory.zig +++ b/test/behavior/comptime_memory.zig @@ -5,7 +5,6 @@ const ptr_size = @sizeOf(usize); test "type pun signed and unsigned as single pointer" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO comptime { @@ -18,7 +17,6 @@ test "type pun signed and unsigned as single pointer" { test "type pun signed and unsigned as many pointer" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO comptime { var x: u32 = 0; @@ -30,7 +28,6 @@ test "type pun signed and unsigned as many pointer" { test "type pun signed and unsigned as array pointer" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO comptime { var x: u32 = 0; @@ -74,7 +71,6 @@ test "type pun signed and unsigned as array pointer" { test "type pun value and struct" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO comptime { const StructOfU32 = extern struct { x: u32 }; diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 312ab1524a..b735d70d73 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -119,7 +119,6 @@ test "widen cast integer payload of error union function call" { test "debug info for optional error set" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const SomeError = error{ Hello, Hello2 }; var a_local_variable: ?SomeError = null; diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 4609b58ddb..f14a25451d 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -406,8 +406,6 @@ var st_init_str_foo = StInitStrFoo{ }; test "inline for with same type but different values" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - var res: usize = 0; inline for ([_]type{ [2]u8, [1]u8, [2]u8 }) |T| { var a: T = undefined; @@ -487,7 +485,6 @@ test "comptime bitwise operators" { test "comptime shlWithOverflow" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const ct_shifted: u64 = comptime amt: { var amt = @as(u64, 0); @@ -559,8 +556,6 @@ pub fn vec3(x: f32, y: f32, z: f32) Vec3 { } test "inlined loop has array literal with elided runtime scope on first iteration but not second iteration" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - var runtime = [1]i32{3}; comptime var i: usize = 0; inline while (i < 2) : (i += 1) { @@ -611,7 +606,6 @@ const hi1 = "hi"; const hi2 = hi1; test "const global shares pointer with other same one" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try assertEqualPtrs(&hi1[0], &hi2[0]); comptime try expect(&hi1[0] == &hi2[0]); @@ -982,7 +976,6 @@ test "closure capture type of runtime-known parameter" { test "comptime break passing through runtime condition converted to runtime break" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 26185f6ac1..324a2ad247 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -184,8 +184,6 @@ test "function with complex callconv and return type expressions" { } test "pass by non-copying value" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - try expect(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); } @@ -211,8 +209,6 @@ fn addPointCoordsVar(pt: anytype) !i32 { } test "pass by non-copying value as method" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - var pt = Point2{ .x = 1, .y = 2 }; try expect(pt.addPointCoords() == 3); } @@ -227,8 +223,6 @@ const Point2 = struct { }; test "pass by non-copying value as method, which is generic" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - var pt = Point3{ .x = 1, .y = 2 }; try expect(pt.addPointCoords(i32) == 3); } @@ -244,8 +238,6 @@ const Point3 = struct { }; test "pass by non-copying value as method, at comptime" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - comptime { var pt = Point2{ .x = 1, .y = 2 }; try expect(pt.addPointCoords() == 3); @@ -409,7 +401,6 @@ test "ability to give comptime types and non comptime types to same parameter" { test "function with inferred error set but returning no error" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const S = struct { fn foo() !void {} diff --git a/test/behavior/fn_delegation.zig b/test/behavior/fn_delegation.zig index eee8f52490..25ec3dea1b 100644 --- a/test/behavior/fn_delegation.zig +++ b/test/behavior/fn_delegation.zig @@ -32,7 +32,6 @@ fn custom(comptime T: type, comptime num: u64) fn (T) u64 { } test "fn delegation" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const foo = Foo{}; diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index 2a18135fe0..cd4e018be3 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -206,7 +206,6 @@ fn foo2(arg: anytype) bool { test "generic struct" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; var a1 = GenNode(i32){ .value = 13, .next = null, diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index 6219a6a5be..4e5eb5061c 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -33,8 +33,6 @@ test "optional pointer to size zero struct" { } test "equality compare optional pointers" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - try testNullPtrsEql(); comptime try testNullPtrsEql(); } diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 07bab40135..a47d931883 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -85,7 +85,6 @@ test "assigning integer to C pointer" { test "C pointer comparison and arithmetic" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -477,7 +476,6 @@ test "element pointer arithmetic to slice" { } test "array slicing to slice" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { diff --git a/test/behavior/sizeof_and_typeof.zig b/test/behavior/sizeof_and_typeof.zig index 4421b59f96..6c7e16b502 100644 --- a/test/behavior/sizeof_and_typeof.zig +++ b/test/behavior/sizeof_and_typeof.zig @@ -19,7 +19,6 @@ test "@sizeOf on compile-time types" { test "@TypeOf() with multiple arguments" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; { diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 18c769f27c..e2694759bb 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -207,7 +207,6 @@ test "slice string literal has correct type" { } test "result location zero sized array inside struct field implicit cast to slice" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const E = struct { @@ -509,7 +508,6 @@ test "slice pointer-to-array null terminated" { test "slice pointer-to-array zero length" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO comptime { diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 8370cd763e..5cbb8e973e 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -168,14 +168,12 @@ const MemberFnTestFoo = struct { }; test "call member function directly" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const instance = MemberFnTestFoo{ .x = 1234 }; const result = MemberFnTestFoo.member(instance); try expect(result == 1234); } test "store member function in variable" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const instance = MemberFnTestFoo{ .x = 1234 }; const memberFn = MemberFnTestFoo.member; const result = memberFn(instance); @@ -1000,8 +998,6 @@ test "tuple element initialized with fn call" { } test "struct with union field" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - const Value = struct { ref: u32 = 2, kind: union(enum) { diff --git a/test/behavior/type.zig b/test/behavior/type.zig index c543d4f969..f4a32e5df0 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -473,7 +473,6 @@ test "Type.Union" { test "Type.Union from Type.Enum" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const Tag = @Type(.{ .Enum = .{ @@ -503,7 +502,6 @@ test "Type.Union from Type.Enum" { test "Type.Union from regular enum" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const E = enum { working_as_expected }; const T = @Type(.{ diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index 32b9671f96..7bbfbbc1ab 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -381,7 +381,6 @@ extern fn foo(a: usize, b: bool, ...) callconv(.C) usize; extern fn fooAligned(a: usize, b: bool, ...) align(4) callconv(.C) usize; test "type info: generic function types" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend != .stage1) { diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 7f17ff50c6..4daa9c1ee5 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1111,7 +1111,6 @@ test "union enum type gets a separate scope" { test "global variable struct contains union initialized to non-most-aligned field" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const T = struct { const U = union(enum) { diff --git a/test/behavior/void.zig b/test/behavior/void.zig index fc1972c506..66b969d0bf 100644 --- a/test/behavior/void.zig +++ b/test/behavior/void.zig @@ -42,8 +42,6 @@ test "void optional" { } test "void array as a local variable initializer" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - var x = [_]void{{}} ** 1004; _ = x[0]; } From 0e6285c8fc31ff866df96847fe34e660da38b4a9 Mon Sep 17 00:00:00 2001 From: Ali Chraghi Date: Sun, 22 May 2022 19:36:59 +0430 Subject: [PATCH 1672/2031] math: make `cast` return optional instead of an error --- lib/std/Thread.zig | 4 +-- lib/std/Thread/Condition.zig | 2 +- lib/std/Thread/Futex.zig | 10 +++---- lib/std/child_process.zig | 2 +- lib/std/debug.zig | 12 ++++---- lib/std/dwarf.zig | 6 ++-- lib/std/dynamic_library.zig | 3 +- lib/std/fmt.zig | 9 ++---- lib/std/fs.zig | 2 +- lib/std/fs/file.zig | 10 +++---- lib/std/io/fixed_buffer_stream.zig | 6 ++-- lib/std/math.zig | 21 +++++++------- lib/std/math/big/int.zig | 2 +- lib/std/os.zig | 30 +++++++++---------- lib/std/os/windows.zig | 22 +++++--------- lib/std/time.zig | 2 +- lib/std/zig/system/NativeTargetInfo.zig | 4 +-- src/Module.zig | 4 +-- src/Sema.zig | 8 ++---- src/arch/aarch64/CodeGen.zig | 10 +++---- src/arch/aarch64/Emit.zig | 12 ++++---- src/arch/arm/CodeGen.zig | 8 +++--- src/arch/arm/Emit.zig | 2 +- src/arch/riscv64/CodeGen.zig | 4 +-- src/arch/sparc64/CodeGen.zig | 12 ++++---- src/arch/sparc64/Emit.zig | 4 +-- src/arch/wasm/CodeGen.zig | 14 ++++----- src/arch/x86_64/CodeGen.zig | 4 +-- src/codegen.zig | 18 ++++++------ src/link/MachO.zig | 14 ++++----- src/link/MachO/Atom.zig | 38 ++++++++++++------------- src/link/MachO/DebugSymbols.zig | 4 +-- src/link/MachO/Dylib.zig | 2 +- src/link/MachO/Object.zig | 2 +- src/link/tapi/yaml.zig | 2 +- src/main.zig | 2 +- src/translate_c.zig | 16 +++++------ 37 files changed, 152 insertions(+), 175 deletions(-) diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 06903a9e91..aaf0fc3440 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -88,7 +88,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void { .windows => { var buf: [max_name_len]u16 = undefined; const len = try std.unicode.utf8ToUtf16Le(&buf, name); - const byte_len = math.cast(c_ushort, len * 2) catch return error.NameTooLong; + const byte_len = math.cast(c_ushort, len * 2) orelse return error.NameTooLong; // Note: NT allocates its own copy, no use-after-free here. const unicode_string = os.windows.UNICODE_STRING{ @@ -526,7 +526,7 @@ const WindowsThreadImpl = struct { // Windows appears to only support SYSTEM_INFO.dwAllocationGranularity minimum stack size. // Going lower makes it default to that specified in the executable (~1mb). // Its also fine if the limit here is incorrect as stack size is only a hint. - var stack_size = std.math.cast(u32, config.stack_size) catch std.math.maxInt(u32); + var stack_size = std.math.cast(u32, config.stack_size) orelse std.math.maxInt(u32); stack_size = std.math.max(64 * 1024, stack_size); instance.thread.thread_handle = windows.kernel32.CreateThread( diff --git a/lib/std/Thread/Condition.zig b/lib/std/Thread/Condition.zig index d5855ec066..392879f837 100644 --- a/lib/std/Thread/Condition.zig +++ b/lib/std/Thread/Condition.zig @@ -152,7 +152,7 @@ const WindowsImpl = struct { // Round the nanoseconds to the nearest millisecond, // then saturating cast it to windows DWORD for use in kernel32 call. const ms = (timeout_ns +| (std.time.ns_per_ms / 2)) / std.time.ns_per_ms; - timeout_ms = std.math.cast(os.windows.DWORD, ms) catch std.math.maxInt(os.windows.DWORD); + timeout_ms = std.math.cast(os.windows.DWORD, ms) orelse std.math.maxInt(os.windows.DWORD); // Track if the timeout overflowed into INFINITE and make sure not to wait forever. if (timeout_ms == os.windows.INFINITE) { diff --git a/lib/std/Thread/Futex.zig b/lib/std/Thread/Futex.zig index bbe6b813ba..0d9ccc8969 100644 --- a/lib/std/Thread/Futex.zig +++ b/lib/std/Thread/Futex.zig @@ -193,7 +193,7 @@ const DarwinImpl = struct { break :blk os.darwin.__ulock_wait2(flags, addr, expect, timeout_ns, 0); } - const timeout_us = std.math.cast(u32, timeout_ns / std.time.ns_per_us) catch overflow: { + const timeout_us = std.math.cast(u32, timeout_ns / std.time.ns_per_us) orelse overflow: { timeout_overflowed = true; break :overflow std.math.maxInt(u32); }; @@ -274,7 +274,7 @@ const LinuxImpl = struct { const rc = os.linux.futex_wake( @ptrCast(*const i32, &ptr.value), os.linux.FUTEX.PRIVATE_FLAG | os.linux.FUTEX.WAKE, - std.math.cast(i32, max_waiters) catch std.math.maxInt(i32), + std.math.cast(i32, max_waiters) orelse std.math.maxInt(i32), ); switch (os.linux.getErrno(rc)) { @@ -379,7 +379,7 @@ const OpenbsdImpl = struct { const rc = os.openbsd.futex( @ptrCast(*const volatile u32, &ptr.value), os.openbsd.FUTEX_WAKE | os.openbsd.FUTEX_PRIVATE_FLAG, - std.math.cast(c_int, max_waiters) catch std.math.maxInt(c_int), + std.math.cast(c_int, max_waiters) orelse std.math.maxInt(c_int), null, // FUTEX_WAKE takes no timeout ptr null, // FUTEX_WAKE takes no requeue address ); @@ -400,7 +400,7 @@ const DragonflyImpl = struct { if (timeout) |delay| { assert(delay != 0); // handled by timedWait(). - timeout_us = std.math.cast(c_int, delay / std.time.ns_per_us) catch blk: { + timeout_us = std.math.cast(c_int, delay / std.time.ns_per_us) orelse blk: { timeout_overflowed = true; break :blk std.math.maxInt(c_int); }; @@ -436,7 +436,7 @@ const DragonflyImpl = struct { fn wake(ptr: *const Atomic(u32), max_waiters: u32) void { // A count of zero means wake all waiters. assert(max_waiters != 0); - const to_wake = std.math.cast(c_int, max_waiters) catch 0; + const to_wake = std.math.cast(c_int, max_waiters) orelse 0; // https://man.dragonflybsd.org/?command=umtx§ion=2 // > umtx_wakeup() will generally return 0 unless the address is bad. diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 6e6d23d2bd..fb521eb784 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -284,7 +284,7 @@ pub const ChildProcess = struct { const next_buf = buf.unusedCapacitySlice(); if (next_buf.len == 0) return .full; var read_bytes: u32 = undefined; - const read_result = windows.kernel32.ReadFile(handle, next_buf.ptr, math.cast(u32, next_buf.len) catch maxInt(u32), &read_bytes, overlapped); + const read_result = windows.kernel32.ReadFile(handle, next_buf.ptr, math.cast(u32, next_buf.len) orelse maxInt(u32), &read_bytes, overlapped); if (read_result == 0) return switch (windows.kernel32.GetLastError()) { .IO_PENDING => .pending, .BROKEN_PIPE => .closed, diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 86ed1c5a65..7fb632084b 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -853,9 +853,9 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_file: File) !ModuleDebugInfo } } -fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 { - const start = try math.cast(usize, offset); - const end = start + try math.cast(usize, size); +fn chopSlice(ptr: []const u8, offset: u64, size: u64) error{Overflow}![]const u8 { + const start = math.cast(usize, offset) orelse return error.Overflow; + const end = start + (math.cast(usize, size) orelse return error.Overflow); return ptr[start..end]; } @@ -880,7 +880,7 @@ pub fn readElfDebugInfo(allocator: mem.Allocator, elf_file: File) !ModuleDebugIn const str_section_off = shoff + @as(u64, hdr.e_shentsize) * @as(u64, hdr.e_shstrndx); const str_shdr = @ptrCast( *const elf.Shdr, - @alignCast(@alignOf(elf.Shdr), &mapped_mem[try math.cast(usize, str_section_off)]), + @alignCast(@alignOf(elf.Shdr), &mapped_mem[math.cast(usize, str_section_off) orelse return error.Overflow]), ); const header_strings = mapped_mem[str_shdr.sh_offset .. str_shdr.sh_offset + str_shdr.sh_size]; const shdrs = @ptrCast( @@ -1119,7 +1119,7 @@ fn mapWholeFile(file: File) ![]align(mem.page_size) const u8 { nosuspend { defer file.close(); - const file_len = try math.cast(usize, try file.getEndPos()); + const file_len = math.cast(usize, try file.getEndPos()) orelse math.maxInt(usize); const mapped_mem = try os.mmap( null, file_len, @@ -1248,7 +1248,7 @@ pub const DebugInfo = struct { if (windows.kernel32.K32EnumProcessModules( process_handle, modules.ptr, - try math.cast(windows.DWORD, modules.len * @sizeOf(windows.HMODULE)), + math.cast(windows.DWORD, modules.len * @sizeOf(windows.HMODULE)) orelse return error.Overflow, &bytes_needed, ) == 0) return error.MissingDebugInfo; diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index ac7cdc761c..d61d198c7e 100644 --- a/lib/std/dwarf.zig +++ b/lib/std/dwarf.zig @@ -1068,7 +1068,7 @@ pub const DwarfInfo = struct { }); }, else => { - const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo; + const fwd_amt = math.cast(isize, op_size - 1) orelse return error.InvalidDebugInfo; try seekable.seekBy(fwd_amt); }, } @@ -1133,7 +1133,7 @@ pub const DwarfInfo = struct { fn getString(di: *DwarfInfo, offset: u64) ![]const u8 { if (offset > di.debug_str.len) return error.InvalidDebugInfo; - const casted_offset = math.cast(usize, offset) catch + const casted_offset = math.cast(usize, offset) orelse return error.InvalidDebugInfo; // Valid strings always have a terminating zero byte @@ -1148,7 +1148,7 @@ pub const DwarfInfo = struct { const debug_line_str = di.debug_line_str orelse return error.InvalidDebugInfo; if (offset > debug_line_str.len) return error.InvalidDebugInfo; - const casted_offset = math.cast(usize, offset) catch + const casted_offset = math.cast(usize, offset) orelse return error.InvalidDebugInfo; // Valid strings always have a terminating zero byte diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index 40a84fc76f..90fb88c5be 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -104,6 +104,7 @@ pub const ElfDynLib = struct { memory: []align(mem.page_size) u8, pub const Error = error{ + FileTooBig, NotElfFile, NotDynamicLibrary, MissingDynamicLinkingInformation, @@ -118,7 +119,7 @@ pub const ElfDynLib = struct { defer os.close(fd); const stat = try os.fstat(fd); - const size = try std.math.cast(usize, stat.size); + const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig; // This one is to read the ELF info. We do more mmapping later // corresponding to the actual LOAD sections. diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 54b68572b5..939921535d 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -1778,8 +1778,8 @@ fn parseWithSign( if (c == '_') continue; const digit = try charToDigit(c, buf_radix); - if (x != 0) x = try math.mul(T, x, try math.cast(T, buf_radix)); - x = try add(T, x, try math.cast(T, digit)); + if (x != 0) x = try math.mul(T, x, math.cast(T, buf_radix) orelse return error.Overflow); + x = try add(T, x, math.cast(T, digit) orelse return error.Overflow); } return x; @@ -1893,10 +1893,7 @@ pub fn count(comptime fmt: []const u8, args: anytype) u64 { pub const AllocPrintError = error{OutOfMemory}; pub fn allocPrint(allocator: mem.Allocator, comptime fmt: []const u8, args: anytype) AllocPrintError![]u8 { - const size = math.cast(usize, count(fmt, args)) catch |err| switch (err) { - // Output too long. Can't possibly allocate enough memory to display it. - error.Overflow => return error.OutOfMemory, - }; + const size = math.cast(usize, count(fmt, args)) orelse return error.OutOfMemory; const buf = try allocator.alloc(u8, size); return bufPrint(buf, fmt, args) catch |err| switch (err) { error.NoSpaceLeft => unreachable, // we just counted the size above diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 8f3a0de808..31354a2782 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -1899,7 +1899,7 @@ pub const Dir = struct { // If the file size doesn't fit a usize it'll be certainly greater than // `max_bytes` - const stat_size = size_hint orelse math.cast(usize, try file.getEndPos()) catch + const stat_size = size_hint orelse math.cast(usize, try file.getEndPos()) orelse return error.FileTooBig; return file.readToEndAllocOptions(allocator, max_bytes, stat_size, alignment, optional_sentinel); diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index 3ea8dd0285..5de746150b 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -907,12 +907,12 @@ pub const File = struct { } const times = [2]os.timespec{ os.timespec{ - .tv_sec = math.cast(isize, @divFloor(atime, std.time.ns_per_s)) catch maxInt(isize), - .tv_nsec = math.cast(isize, @mod(atime, std.time.ns_per_s)) catch maxInt(isize), + .tv_sec = math.cast(isize, @divFloor(atime, std.time.ns_per_s)) orelse maxInt(isize), + .tv_nsec = math.cast(isize, @mod(atime, std.time.ns_per_s)) orelse maxInt(isize), }, os.timespec{ - .tv_sec = math.cast(isize, @divFloor(mtime, std.time.ns_per_s)) catch maxInt(isize), - .tv_nsec = math.cast(isize, @mod(mtime, std.time.ns_per_s)) catch maxInt(isize), + .tv_sec = math.cast(isize, @divFloor(mtime, std.time.ns_per_s)) orelse maxInt(isize), + .tv_nsec = math.cast(isize, @mod(mtime, std.time.ns_per_s)) orelse maxInt(isize), }, }; try os.futimens(self.handle, ×); @@ -1218,7 +1218,7 @@ pub const File = struct { pub const CopyRangeError = os.CopyFileRangeError; pub fn copyRange(in: File, in_offset: u64, out: File, out_offset: u64, len: u64) CopyRangeError!u64 { - const adjusted_len = math.cast(usize, len) catch math.maxInt(usize); + const adjusted_len = math.cast(usize, len) orelse math.maxInt(usize); const result = try os.copy_file_range(in.handle, in_offset, out.handle, out_offset, adjusted_len, 0); return result; } diff --git a/lib/std/io/fixed_buffer_stream.zig b/lib/std/io/fixed_buffer_stream.zig index e105beb453..b002bb47b8 100644 --- a/lib/std/io/fixed_buffer_stream.zig +++ b/lib/std/io/fixed_buffer_stream.zig @@ -76,20 +76,20 @@ pub fn FixedBufferStream(comptime Buffer: type) type { } pub fn seekTo(self: *Self, pos: u64) SeekError!void { - self.pos = if (std.math.cast(usize, pos)) |x| std.math.min(self.buffer.len, x) else |_| self.buffer.len; + self.pos = if (std.math.cast(usize, pos)) |x| std.math.min(self.buffer.len, x) else self.buffer.len; } pub fn seekBy(self: *Self, amt: i64) SeekError!void { if (amt < 0) { const abs_amt = std.math.absCast(amt); - const abs_amt_usize = std.math.cast(usize, abs_amt) catch std.math.maxInt(usize); + const abs_amt_usize = std.math.cast(usize, abs_amt) orelse std.math.maxInt(usize); if (abs_amt_usize > self.pos) { self.pos = 0; } else { self.pos -= abs_amt_usize; } } else { - const amt_usize = std.math.cast(usize, amt) catch std.math.maxInt(usize); + const amt_usize = std.math.cast(usize, amt) orelse std.math.maxInt(usize); const new_pos = std.math.add(usize, self.pos, amt_usize) catch std.math.maxInt(usize); self.pos = std.math.min(self.buffer.len, new_pos); } diff --git a/lib/std/math.zig b/lib/std/math.zig index b936183482..de77663d5b 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -989,28 +989,27 @@ test "negateCast" { } /// Cast an integer to a different integer type. If the value doesn't fit, -/// return an error. -/// TODO make this an optional not an error. -pub fn cast(comptime T: type, x: anytype) (error{Overflow}!T) { +/// return null. +pub fn cast(comptime T: type, x: anytype) ?T { comptime assert(@typeInfo(T) == .Int); // must pass an integer comptime assert(@typeInfo(@TypeOf(x)) == .Int); // must pass an integer if (maxInt(@TypeOf(x)) > maxInt(T) and x > maxInt(T)) { - return error.Overflow; + return null; } else if (minInt(@TypeOf(x)) < minInt(T) and x < minInt(T)) { - return error.Overflow; + return null; } else { return @intCast(T, x); } } test "cast" { - try testing.expectError(error.Overflow, cast(u8, @as(u32, 300))); - try testing.expectError(error.Overflow, cast(i8, @as(i32, -200))); - try testing.expectError(error.Overflow, cast(u8, @as(i8, -1))); - try testing.expectError(error.Overflow, cast(u64, @as(i8, -1))); + try testing.expect(cast(u8, @as(u32, 300)) == null); + try testing.expect(cast(i8, @as(i32, -200)) == null); + try testing.expect(cast(u8, @as(i8, -1)) == null); + try testing.expect(cast(u64, @as(i8, -1)) == null); - try testing.expect((try cast(u8, @as(u32, 255))) == @as(u8, 255)); - try testing.expect(@TypeOf(try cast(u8, @as(u32, 255))) == u8); + try testing.expect(cast(u8, @as(u32, 255)).? == @as(u8, 255)); + try testing.expect(@TypeOf(cast(u8, @as(u32, 255)).?) == u8); } pub const AlignCastError = error{UnalignedMemory}; diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 614cf04f9b..b57e1896cc 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -2014,7 +2014,7 @@ pub const Const = struct { } else { if (math.cast(T, r)) |ok| { return -ok; - } else |_| { + } else { return minInt(T); } } diff --git a/lib/std/os.zig b/lib/std/os.zig index 27c8bd099a..18513f2eca 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -660,7 +660,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { else => |err| return unexpectedErrno(err), } } - const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31); + const iov_count = math.cast(u31, iov.len) orelse math.maxInt(u31); while (true) { // TODO handle the case when iov_len is too large and get rid of this @intCast const rc = system.readv(fd, iov.ptr, iov_count); @@ -877,7 +877,7 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize { } } - const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31); + const iov_count = math.cast(u31, iov.len) orelse math.maxInt(u31); const preadv_sym = if (builtin.os.tag == .linux and builtin.link_libc) system.preadv64 @@ -4163,9 +4163,9 @@ pub fn kevent( const rc = system.kevent( kq, changelist.ptr, - try math.cast(c_int, changelist.len), + math.cast(c_int, changelist.len) orelse return error.Overflow, eventlist.ptr, - try math.cast(c_int, eventlist.len), + math.cast(c_int, eventlist.len) orelse return error.Overflow, timeout, ); switch (errno(rc)) { @@ -4531,9 +4531,7 @@ pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32 return; } - const path_len_bytes = math.cast(u16, mem.sliceTo(sub_path_w, 0).len * 2) catch |err| switch (err) { - error.Overflow => return error.NameTooLong, - }; + const path_len_bytes = math.cast(u16, mem.sliceTo(sub_path_w, 0).len * 2) orelse return error.NameTooLong; var nt_name = windows.UNICODE_STRING{ .Length = path_len_bytes, .MaximumLength = path_len_bytes, @@ -4650,7 +4648,7 @@ pub fn sysctl( @panic("unsupported"); // TODO should be compile error, not panic } - const name_len = math.cast(c_uint, name.len) catch return error.NameTooLong; + const name_len = math.cast(c_uint, name.len) orelse return error.NameTooLong; switch (errno(system.sysctl(name.ptr, name_len, oldp, oldlenp, newp, newlen))) { .SUCCESS => return, .FAULT => unreachable, @@ -5191,8 +5189,8 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { /// Spurious wakeups are possible and no precision of timing is guaranteed. pub fn nanosleep(seconds: u64, nanoseconds: u64) void { var req = timespec{ - .tv_sec = math.cast(isize, seconds) catch math.maxInt(isize), - .tv_nsec = math.cast(isize, nanoseconds) catch math.maxInt(isize), + .tv_sec = math.cast(isize, seconds) orelse math.maxInt(isize), + .tv_nsec = math.cast(isize, nanoseconds) orelse math.maxInt(isize), }; var rem: timespec = undefined; while (true) { @@ -6006,10 +6004,10 @@ pub fn sendfile( if (headers.len != 0 or trailers.len != 0) { // Here we carefully avoid `@intCast` by returning partial writes when // too many io vectors are provided. - const hdr_cnt = math.cast(u31, headers.len) catch math.maxInt(u31); + const hdr_cnt = math.cast(u31, headers.len) orelse math.maxInt(u31); if (headers.len > hdr_cnt) return writev(out_fd, headers); - const trl_cnt = math.cast(u31, trailers.len) catch math.maxInt(u31); + const trl_cnt = math.cast(u31, trailers.len) orelse math.maxInt(u31); hdtr_data = std.c.sf_hdtr{ .headers = headers.ptr, @@ -6085,10 +6083,10 @@ pub fn sendfile( if (headers.len != 0 or trailers.len != 0) { // Here we carefully avoid `@intCast` by returning partial writes when // too many io vectors are provided. - const hdr_cnt = math.cast(u31, headers.len) catch math.maxInt(u31); + const hdr_cnt = math.cast(u31, headers.len) orelse math.maxInt(u31); if (headers.len > hdr_cnt) return writev(out_fd, headers); - const trl_cnt = math.cast(u31, trailers.len) catch math.maxInt(u31); + const trl_cnt = math.cast(u31, trailers.len) orelse math.maxInt(u31); hdtr_data = std.c.sf_hdtr{ .headers = headers.ptr, @@ -6276,7 +6274,7 @@ pub const PollError = error{ pub fn poll(fds: []pollfd, timeout: i32) PollError!usize { while (true) { - const fds_count = math.cast(nfds_t, fds.len) catch return error.SystemResources; + const fds_count = math.cast(nfds_t, fds.len) orelse return error.SystemResources; const rc = system.poll(fds.ptr, fds_count, timeout); if (builtin.os.tag == .windows) { if (rc == windows.ws2_32.SOCKET_ERROR) { @@ -6319,7 +6317,7 @@ pub fn ppoll(fds: []pollfd, timeout: ?*const timespec, mask: ?*const sigset_t) P ts_ptr = &ts; ts = timeout_ns.*; } - const fds_count = math.cast(nfds_t, fds.len) catch return error.SystemResources; + const fds_count = math.cast(nfds_t, fds.len) orelse return error.SystemResources; const rc = system.ppoll(fds.ptr, fds_count, ts_ptr, mask); switch (errno(rc)) { .SUCCESS => return @intCast(usize, rc), diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index b949f92feb..3e42ee5f2d 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -78,9 +78,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN var result: HANDLE = undefined; - const path_len_bytes = math.cast(u16, sub_path_w.len * 2) catch |err| switch (err) { - error.Overflow => return error.NameTooLong, - }; + const path_len_bytes = math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong; var nt_name = UNICODE_STRING{ .Length = path_len_bytes, .MaximumLength = path_len_bytes, @@ -551,7 +549,7 @@ pub fn WriteFile( }; loop.beginOneEvent(); suspend { - const adjusted_len = math.cast(DWORD, bytes.len) catch maxInt(DWORD); + const adjusted_len = math.cast(DWORD, bytes.len) orelse maxInt(DWORD); _ = kernel32.WriteFile(handle, bytes.ptr, adjusted_len, null, &resume_node.base.overlapped); } var bytes_transferred: DWORD = undefined; @@ -589,7 +587,7 @@ pub fn WriteFile( }; break :blk &overlapped_data; } else null; - const adjusted_len = math.cast(u32, bytes.len) catch maxInt(u32); + const adjusted_len = math.cast(u32, bytes.len) orelse maxInt(u32); if (kernel32.WriteFile(handle, bytes.ptr, adjusted_len, &bytes_written, overlapped) == 0) { switch (kernel32.GetLastError()) { .INVALID_USER_BUFFER => return error.SystemResources, @@ -618,9 +616,7 @@ pub const SetCurrentDirectoryError = error{ }; pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void { - const path_len_bytes = math.cast(u16, path_name.len * 2) catch |err| switch (err) { - error.Overflow => return error.NameTooLong, - }; + const path_len_bytes = math.cast(u16, path_name.len * 2) orelse return error.NameTooLong; var nt_name = UNICODE_STRING{ .Length = path_len_bytes, @@ -753,9 +749,7 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin // With the latter, we'd need to call `NtCreateFile` twice, once for file symlink, and if that // failed, again for dir symlink. Omitting any mention of file/dir flags makes it possible // to open the symlink there and then. - const path_len_bytes = math.cast(u16, sub_path_w.len * 2) catch |err| switch (err) { - error.Overflow => return error.NameTooLong, - }; + const path_len_bytes = math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong; var nt_name = UNICODE_STRING{ .Length = path_len_bytes, .MaximumLength = path_len_bytes, @@ -1013,9 +1007,7 @@ pub fn QueryObjectName( const info = @ptrCast(*OBJECT_NAME_INFORMATION, out_buffer_aligned); //buffer size is specified in bytes - const out_buffer_len = std.math.cast(ULONG, out_buffer_aligned.len * 2) catch |e| switch (e) { - error.Overflow => std.math.maxInt(ULONG), - }; + const out_buffer_len = std.math.cast(ULONG, out_buffer_aligned.len * 2) orelse std.math.maxInt(ULONG); //last argument would return the length required for full_buffer, not exposed here const rc = ntdll.NtQueryObject(handle, .ObjectNameInformation, info, out_buffer_len, null); switch (rc) { @@ -1221,7 +1213,7 @@ pub fn QueryInformationFile( out_buffer: []u8, ) QueryInformationFileError!void { var io: IO_STATUS_BLOCK = undefined; - const len_bytes = std.math.cast(u32, out_buffer.len) catch unreachable; + const len_bytes = std.math.cast(u32, out_buffer.len) orelse unreachable; const rc = ntdll.NtQueryInformationFile(handle, &io, out_buffer.ptr, len_bytes, info_class); switch (rc) { .SUCCESS => {}, diff --git a/lib/std/time.zig b/lib/std/time.zig index 668a4b2cf8..ea830d9dd3 100644 --- a/lib/std/time.zig +++ b/lib/std/time.zig @@ -16,7 +16,7 @@ pub fn sleep(nanoseconds: u64) void { if (builtin.os.tag == .windows) { const big_ms_from_ns = nanoseconds / ns_per_ms; - const ms = math.cast(os.windows.DWORD, big_ms_from_ns) catch math.maxInt(os.windows.DWORD); + const ms = math.cast(os.windows.DWORD, big_ms_from_ns) orelse math.maxInt(os.windows.DWORD); os.windows.kernel32.Sleep(ms); return; } diff --git a/lib/std/zig/system/NativeTargetInfo.zig b/lib/std/zig/system/NativeTargetInfo.zig index 0956e469af..fc5d833b0b 100644 --- a/lib/std/zig/system/NativeTargetInfo.zig +++ b/lib/std/zig/system/NativeTargetInfo.zig @@ -657,9 +657,7 @@ pub fn abiAndDynamicLinkerFromFile( const strtab_read_len = try preadMin(file, &strtab_buf, ds.offset, strtab_len); const strtab = strtab_buf[0..strtab_read_len]; // TODO this pointer cast should not be necessary - const rpoff_usize = std.math.cast(usize, rpoff) catch |err| switch (err) { - error.Overflow => return error.InvalidElfFile, - }; + const rpoff_usize = std.math.cast(usize, rpoff) orelse return error.InvalidElfFile; const rpath_list = mem.sliceTo(std.meta.assumeSentinel(strtab[rpoff_usize..].ptr, 0), 0); var it = mem.tokenize(u8, rpath_list, ":"); while (it.next()) |rpath| { diff --git a/src/Module.zig b/src/Module.zig index bc84d84ae8..7c19c4dab6 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4395,7 +4395,7 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb .inode = actual_stat.inode, .mtime = actual_stat.mtime, }; - const size_usize = try std.math.cast(usize, actual_stat.size); + const size_usize = std.math.cast(usize, actual_stat.size) orelse return error.Overflow; const bytes = try file.readToEndAllocOptions(gpa, std.math.maxInt(u32), size_usize, 1, 0); errdefer gpa.free(bytes); @@ -4435,7 +4435,7 @@ pub fn detectEmbedFileUpdate(mod: *Module, embed_file: *EmbedFile) !void { if (unchanged_metadata) return; const gpa = mod.gpa; - const size_usize = try std.math.cast(usize, stat.size); + const size_usize = std.math.cast(usize, stat.size) orelse return error.Overflow; const bytes = try file.readToEndAllocOptions(gpa, std.math.maxInt(u32), size_usize, 1, 0); gpa.free(embed_file.bytes); embed_file.bytes = bytes; diff --git a/src/Sema.zig b/src/Sema.zig index 269a9c7587..e0310e5ad7 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -21786,9 +21786,7 @@ fn cmpNumeric( const dest_ty = if (dest_float_type) |ft| ft else blk: { const max_bits = std.math.max(lhs_bits, rhs_bits); - const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) { - error.Overflow => return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits}), - }; + const casted_bits = std.math.cast(u16, max_bits) orelse return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits}); const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned; break :blk try Module.makeIntType(sema.arena, signedness, casted_bits); }; @@ -24073,9 +24071,7 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr /// is too big to fit. fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize { if (@bitSizeOf(u64) <= @bitSizeOf(usize)) return int; - return std.math.cast(usize, int) catch |err| switch (err) { - error.Overflow => return sema.fail(block, src, "expression produces integer value {d} which is too big for this compiler implementation to handle", .{int}), - }; + return std.math.cast(usize, int) orelse return sema.fail(block, src, "expression produces integer value {d} which is too big for this compiler implementation to handle", .{int}); } /// For pointer-like optionals, it returns the pointer type. For pointers, diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 1671f705c7..c85c280fdd 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -453,7 +453,7 @@ fn gen(self: *Self) !void { .tag = .sub_immediate, .data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = size } }, }); - } else |_| { + } else { return self.failSymbol("TODO AArch64: allow larger stacks", .{}); } @@ -860,7 +860,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { return @as(u32, 0); } - const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { + const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { const mod = self.bin_file.options.module.?; return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; @@ -871,7 +871,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const elem_ty = self.air.typeOfIndex(inst); - const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { + const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { const mod = self.bin_file.options.module.?; return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; @@ -3031,7 +3031,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { // Copy registers to the stack .register => |reg| blk: { const mod = self.bin_file.options.module.?; - const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch { + const abi_size = math.cast(u32, ty.abiSize(self.target.*)) orelse { return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(mod)}); }; const abi_align = ty.abiAlignment(self.target.*); @@ -4173,7 +4173,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }, .ptr_stack_offset => |off| { // TODO: maybe addressing from sp instead of fp - const imm12 = math.cast(u12, off) catch + const imm12 = math.cast(u12, off) orelse return self.fail("TODO larger stack offsets", .{}); _ = try self.addInst(.{ diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 9e4993b0d9..8ea6ab91e2 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -216,21 +216,21 @@ fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType { .cbz => { if (std.math.cast(i19, @shrExact(offset, 2))) |_| { return BranchType.cbz; - } else |_| { + } else { return emit.fail("TODO support cbz branches larger than +-1 MiB", .{}); } }, .b, .bl => { if (std.math.cast(i26, @shrExact(offset, 2))) |_| { return BranchType.unconditional_branch_immediate; - } else |_| { + } else { return emit.fail("TODO support unconditional branches larger than +-128 MiB", .{}); } }, .b_cond => { if (std.math.cast(i19, @shrExact(offset, 2))) |_| { return BranchType.b_cond; - } else |_| { + } else { return emit.fail("TODO support conditional branches larger than +-1 MiB", .{}); } }, @@ -927,7 +927,7 @@ fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void { .ldrb_stack, .ldrsb_stack, .strb_stack => blk: { if (math.cast(u12, raw_offset)) |imm| { break :blk Instruction.LoadStoreOffset.imm(imm); - } else |_| { + } else { return emit.fail("TODO load/store stack byte with larger offset", .{}); } }, @@ -935,7 +935,7 @@ fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void { assert(std.mem.isAlignedGeneric(u32, raw_offset, 2)); // misaligned stack entry if (math.cast(u12, @divExact(raw_offset, 2))) |imm| { break :blk Instruction.LoadStoreOffset.imm(imm); - } else |_| { + } else { return emit.fail("TODO load/store stack halfword with larger offset", .{}); } }, @@ -949,7 +949,7 @@ fn mirLoadStoreStack(emit: *Emit, inst: Mir.Inst.Index) !void { assert(std.mem.isAlignedGeneric(u32, raw_offset, alignment)); // misaligned stack entry if (math.cast(u12, @divExact(raw_offset, alignment))) |imm| { break :blk Instruction.LoadStoreOffset.imm(imm); - } else |_| { + } else { return emit.fail("TODO load/store stack with larger offset", .{}); } }, diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index b7682a5b9a..b5d0dcbef4 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -850,7 +850,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { return @as(u32, 0); } - const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { + const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { const mod = self.bin_file.options.module.?; return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; @@ -861,7 +861,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const elem_ty = self.air.typeOfIndex(inst); - const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { + const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { const mod = self.bin_file.options.module.?; return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; @@ -4299,7 +4299,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro 1, 4 => { const offset = if (math.cast(u12, stack_offset)) |imm| blk: { break :blk Instruction.Offset.imm(imm); - } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = stack_offset }), .none); + } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = stack_offset }), .none); const tag: Mir.Inst.Tag = switch (abi_size) { 1 => .strb, @@ -4707,7 +4707,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I 1, 4 => { const offset = if (math.cast(u12, stack_offset)) |imm| blk: { break :blk Instruction.Offset.imm(imm); - } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = stack_offset }), .none); + } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = stack_offset }), .none); const tag: Mir.Inst.Tag = switch (abi_size) { 1 => .strb, diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index ff2084fea7..47d508b34a 100644 --- a/src/arch/arm/Emit.zig +++ b/src/arch/arm/Emit.zig @@ -164,7 +164,7 @@ fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType { .b => { if (std.math.cast(i24, @divExact(offset, 4))) |_| { return BranchType.b; - } else |_| { + } else { return emit.fail("TODO support larger branches", .{}); } }, diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index b713161053..c94b4de378 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -779,7 +779,7 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u /// Use a pointer instruction as the basis for allocating stack memory. fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { const elem_ty = self.air.typeOfIndex(inst).elemType(); - const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { + const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { const mod = self.bin_file.options.module.?; return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; @@ -790,7 +790,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const elem_ty = self.air.typeOfIndex(inst); - const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { + const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { const mod = self.bin_file.options.module.?; return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index f36d4ba45f..eb9d9a4ad9 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -421,7 +421,7 @@ fn gen(self: *Self) !void { }, }, }); - } else |_| { + } else { // TODO for large stacks, replace the prologue with: // setx stack_size, %g1 // save %sp, %g1, %sp @@ -1591,7 +1591,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { return @as(u32, 0); } - const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { + const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { const mod = self.bin_file.options.module.?; return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; @@ -1602,7 +1602,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const elem_ty = self.air.typeOfIndex(inst); - const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { + const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { const mod = self.bin_file.options.module.?; return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; @@ -2299,7 +2299,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); }, .ptr_stack_offset => |off| { - const simm13 = math.cast(u12, off + abi.stack_bias + abi.stack_reserved_area) catch + const simm13 = math.cast(u12, off + abi.stack_bias + abi.stack_reserved_area) orelse return self.fail("TODO larger stack offsets", .{}); _ = try self.addInst(.{ @@ -2432,7 +2432,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }, .stack_offset => |off| { const real_offset = off + abi.stack_bias + abi.stack_reserved_area; - const simm13 = math.cast(i13, real_offset) catch + const simm13 = math.cast(i13, real_offset) orelse return self.fail("TODO larger stack offsets", .{}); try self.genLoad(reg, .sp, i13, simm13, ty.abiSize(self.target.*)); }, @@ -2466,7 +2466,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro }, .register => |reg| { const real_offset = stack_offset + abi.stack_bias + abi.stack_reserved_area; - const simm13 = math.cast(i13, real_offset) catch + const simm13 = math.cast(i13, real_offset) orelse return self.fail("TODO larger stack offsets", .{}); return self.genStore(reg, .sp, i13, simm13, abi_size); }, diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index dc1b4caedc..1b40cc6e21 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -519,14 +519,14 @@ fn optimalBranchType(emit: *Emit, tag: Mir.Inst.Tag, offset: i64) !BranchType { .bpcc => { if (std.math.cast(i21, offset)) |_| { return BranchType.bpcc; - } else |_| { + } else { return emit.fail("TODO support BPcc branches larger than +-1 MiB", .{}); } }, .bpr => { if (std.math.cast(i18, offset)) |_| { return BranchType.bpr; - } else |_| { + } else { return emit.fail("TODO support BPr branches larger than +-128 KiB", .{}); } }, diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 1eddb7441b..944ef16294 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1163,7 +1163,7 @@ fn allocStack(self: *Self, ty: Type) !WValue { try self.initializeStack(); } - const abi_size = std.math.cast(u32, ty.abiSize(self.target)) catch { + const abi_size = std.math.cast(u32, ty.abiSize(self.target)) orelse { const module = self.bin_file.base.options.module.?; return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ ty.fmt(module), ty.abiSize(self.target), @@ -1198,7 +1198,7 @@ fn allocStackPtr(self: *Self, inst: Air.Inst.Index) !WValue { } const abi_alignment = ptr_ty.ptrAlignment(self.target); - const abi_size = std.math.cast(u32, pointee_ty.abiSize(self.target)) catch { + const abi_size = std.math.cast(u32, pointee_ty.abiSize(self.target)) orelse { const module = self.bin_file.base.options.module.?; return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ pointee_ty.fmt(module), pointee_ty.abiSize(self.target), @@ -2695,7 +2695,7 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const extra = self.air.extraData(Air.StructField, ty_pl.payload); const struct_ptr = try self.resolveInst(extra.data.struct_operand); const struct_ty = self.air.typeOf(extra.data.struct_operand).childType(); - const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, self.target)) catch { + const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, self.target)) orelse { const module = self.bin_file.base.options.module.?; return self.fail("Field type '{}' too big to fit into stack frame", .{ struct_ty.structFieldType(extra.data.field_index).fmt(module), @@ -2709,7 +2709,7 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr const struct_ptr = try self.resolveInst(ty_op.operand); const struct_ty = self.air.typeOf(ty_op.operand).childType(); const field_ty = struct_ty.structFieldType(index); - const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch { + const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) orelse { const module = self.bin_file.base.options.module.?; return self.fail("Field type '{}' too big to fit into stack frame", .{ field_ty.fmt(module), @@ -2737,7 +2737,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const field_index = struct_field.field_index; const field_ty = struct_ty.structFieldType(field_index); if (!field_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} }; - const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch { + const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) orelse { const module = self.bin_file.base.options.module.?; return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(module)}); }; @@ -3193,7 +3193,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue return operand; } - const offset = std.math.cast(u32, opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) catch { + const offset = std.math.cast(u32, opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) orelse { const module = self.bin_file.base.options.module.?; return self.fail("Optional type {} too big to fit into stack frame", .{opt_ty.fmt(module)}); }; @@ -3223,7 +3223,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (op_ty.optionalReprIsPayload()) { return operand; } - const offset = std.math.cast(u32, op_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) catch { + const offset = std.math.cast(u32, op_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) orelse { const module = self.bin_file.base.options.module.?; return self.fail("Optional type {} too big to fit into stack frame", .{op_ty.fmt(module)}); }; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 8adad94e24..33734eda30 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -854,7 +854,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { return self.allocMem(inst, @sizeOf(usize), @alignOf(usize)); } - const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { + const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { const mod = self.bin_file.options.module.?; return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; @@ -865,7 +865,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const elem_ty = self.air.typeOfIndex(inst); - const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { + const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { const mod = self.bin_file.options.module.?; return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; diff --git a/src/codegen.zig b/src/codegen.zig index fbe462959e..84615d86d8 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -166,7 +166,7 @@ pub fn generateSymbol( }); if (typed_value.val.isUndefDeep()) { - const abi_size = try math.cast(usize, typed_value.ty.abiSize(target)); + const abi_size = math.cast(usize, typed_value.ty.abiSize(target)) orelse return error.Overflow; try code.appendNTimes(0xaa, abi_size); return Result{ .appended = {} }; } @@ -452,7 +452,7 @@ pub fn generateSymbol( if (info.bits > 64) { var bigint_buffer: Value.BigIntSpace = undefined; const bigint = typed_value.val.toBigInt(&bigint_buffer, target); - const abi_size = try math.cast(usize, typed_value.ty.abiSize(target)); + const abi_size = math.cast(usize, typed_value.ty.abiSize(target)) orelse return error.Overflow; const start = code.items.len; try code.resize(start + abi_size); bigint.writeTwosComplement(code.items[start..][0..abi_size], info.bits, abi_size, endian); @@ -571,7 +571,7 @@ pub fn generateSymbol( // Pad struct members if required const padded_field_end = typed_value.ty.structFieldOffset(index + 1, target); - const padding = try math.cast(usize, padded_field_end - unpadded_field_end); + const padding = math.cast(usize, padded_field_end - unpadded_field_end) orelse return error.Overflow; if (padding > 0) { try code.writer().writeByteNTimes(0, padding); @@ -611,7 +611,7 @@ pub fn generateSymbol( assert(union_ty.haveFieldTypes()); const field_ty = union_ty.fields.values()[field_index].ty; if (!field_ty.hasRuntimeBits()) { - try code.writer().writeByteNTimes(0xaa, try math.cast(usize, layout.payload_size)); + try code.writer().writeByteNTimes(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow); } else { switch (try generateSymbol(bin_file, src_loc, .{ .ty = field_ty, @@ -624,7 +624,7 @@ pub fn generateSymbol( .fail => |em| return Result{ .fail = em }, } - const padding = try math.cast(usize, layout.payload_size - field_ty.abiSize(target)); + const padding = math.cast(usize, layout.payload_size - field_ty.abiSize(target)) orelse return error.Overflow; if (padding > 0) { try code.writer().writeByteNTimes(0, padding); } @@ -649,8 +649,8 @@ pub fn generateSymbol( var opt_buf: Type.Payload.ElemType = undefined; const payload_type = typed_value.ty.optionalChild(&opt_buf); const is_pl = !typed_value.val.isNull(); - const abi_size = try math.cast(usize, typed_value.ty.abiSize(target)); - const offset = abi_size - try math.cast(usize, payload_type.abiSize(target)); + const abi_size = math.cast(usize, typed_value.ty.abiSize(target)) orelse return error.Overflow; + const offset = abi_size - (math.cast(usize, payload_type.abiSize(target)) orelse return error.Overflow); if (!payload_type.hasRuntimeBits()) { try code.writer().writeByteNTimes(@boolToInt(is_pl), abi_size); @@ -758,7 +758,7 @@ pub fn generateSymbol( } const unpadded_end = code.items.len - begin; const padded_end = mem.alignForwardGeneric(u64, unpadded_end, abi_align); - const padding = try math.cast(usize, padded_end - unpadded_end); + const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow; if (padding > 0) { try code.writer().writeByteNTimes(0, padding); @@ -780,7 +780,7 @@ pub fn generateSymbol( } const unpadded_end = code.items.len - begin; const padded_end = mem.alignForwardGeneric(u64, unpadded_end, abi_align); - const padding = try math.cast(usize, padded_end - unpadded_end); + const padding = math.cast(usize, padded_end - unpadded_end) orelse return error.Overflow; if (padding > 0) { try code.writer().writeByteNTimes(0, padding); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 853d62bdce..38a1319012 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -2022,7 +2022,7 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio } pub fn createEmptyAtom(self: *MachO, local_sym_index: u32, size: u64, alignment: u32) !*Atom { - const size_usize = try math.cast(usize, size); + const size_usize = math.cast(usize, size) orelse return error.Overflow; const atom = try self.base.allocator.create(Atom); errdefer self.base.allocator.destroy(atom); atom.* = Atom.empty; @@ -2157,7 +2157,7 @@ fn writeAllAtoms(self: *MachO) !void { var buffer = std.ArrayList(u8).init(self.base.allocator); defer buffer.deinit(); - try buffer.ensureTotalCapacity(try math.cast(usize, sect.size)); + try buffer.ensureTotalCapacity(math.cast(usize, sect.size) orelse return error.Overflow); log.debug("writing atoms in {s},{s}", .{ sect.segName(), sect.sectName() }); @@ -2170,7 +2170,7 @@ fn writeAllAtoms(self: *MachO) !void { const padding_size: usize = if (atom.next) |next| blk: { const next_sym = self.locals.items[next.local_sym_index]; const size = next_sym.n_value - (atom_sym.n_value + atom.size); - break :blk try math.cast(usize, size); + break :blk math.cast(usize, size) orelse return error.Overflow; } else 0; log.debug(" (adding atom {s} to buffer: {})", .{ self.getString(atom_sym.n_strx), atom_sym }); @@ -2507,7 +2507,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { .aarch64 => { const literal = blk: { const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4); - break :blk try math.cast(u18, div_res); + break :blk math.cast(u18, div_res) orelse return error.Overflow; }; // ldr w16, literal mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.ldrLiteral( @@ -5080,7 +5080,7 @@ fn growSegment(self: *MachO, seg_id: u16, new_size: u64) !void { self.base.file.?, next_seg.inner.fileoff, next_seg.inner.fileoff + offset_amt, - try math.cast(usize, next_seg.inner.filesize), + math.cast(usize, next_seg.inner.filesize) orelse return error.Overflow, ); next_seg.inner.fileoff += offset_amt; @@ -5165,7 +5165,7 @@ fn growSection(self: *MachO, match: MatchingSection, new_size: u32) !void { self.base.file.?, next_sect.offset, next_sect.offset + offset_amt, - try math.cast(usize, total_size), + math.cast(usize, total_size) orelse return error.Overflow, ); var next = match.sect + 1; @@ -5950,7 +5950,7 @@ fn writeDices(self: *MachO) !void { while (true) { if (atom.dices.items.len > 0) { const sym = self.locals.items[atom.local_sym_index]; - const base_off = try math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset); + const base_off = math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset) orelse return error.Overflow; try buf.ensureUnusedCapacity(atom.dices.items.len * @sizeOf(macho.data_in_code_entry)); for (atom.dices.items) |dice| { diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index dd2dc3c1f1..ff78b26989 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -763,17 +763,15 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { const displacement = math.cast( i28, @intCast(i64, target_addr) - @intCast(i64, source_addr), - ) catch |err| switch (err) { - error.Overflow => { - log.err("jump too big to encode as i28 displacement value", .{}); - log.err(" (target - source) = displacement => 0x{x} - 0x{x} = 0x{x}", .{ - target_addr, - source_addr, - @intCast(i64, target_addr) - @intCast(i64, source_addr), - }); - log.err(" TODO implement branch islands to extend jump distance for arm64", .{}); - return error.TODOImplementBranchIslands; - }, + ) orelse { + log.err("jump too big to encode as i28 displacement value", .{}); + log.err(" (target - source) = displacement => 0x{x} - 0x{x} = 0x{x}", .{ + target_addr, + source_addr, + @intCast(i64, target_addr) - @intCast(i64, source_addr), + }); + log.err(" TODO implement branch islands to extend jump distance for arm64", .{}); + return error.TODOImplementBranchIslands; }; const code = self.code.items[rel.offset..][0..4]; var inst = aarch64.Instruction{ @@ -915,7 +913,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { mem.writeIntLittle(u32, code, inst.toU32()); }, .ARM64_RELOC_POINTER_TO_GOT => { - const result = try math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr)); + const result = math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr)) orelse return error.Overflow; mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, result)); }, .ARM64_RELOC_UNSIGNED => { @@ -945,17 +943,17 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { .x86_64 => { switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) { .X86_64_RELOC_BRANCH => { - const displacement = try math.cast( + const displacement = math.cast( i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend, - ); + ) orelse return error.Overflow; mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement)); }, .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => { - const displacement = try math.cast( + const displacement = math.cast( i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend, - ); + ) orelse return error.Overflow; mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement)); }, .X86_64_RELOC_TLV => { @@ -963,10 +961,10 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { // We need to rewrite the opcode from movq to leaq. self.code.items[rel.offset - 2] = 0x8d; } - const displacement = try math.cast( + const displacement = math.cast( i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend, - ); + ) orelse return error.Overflow; mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement)); }, .X86_64_RELOC_SIGNED, @@ -982,10 +980,10 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { else => unreachable, }; const actual_target_addr = @intCast(i64, target_addr) + rel.addend; - const displacement = try math.cast( + const displacement = math.cast( i32, actual_target_addr - @intCast(i64, source_addr + correction + 4), - ); + ) orelse return error.Overflow; mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement)); }, .X86_64_RELOC_UNSIGNED => { diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 5fac857422..5e5aca26c1 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -616,7 +616,7 @@ fn writeSymbolTable(self: *DebugSymbols) !void { self.file, dwarf_seg.inner.fileoff, dwarf_seg.inner.fileoff + diff, - try math.cast(usize, dwarf_seg.inner.filesize), + math.cast(usize, dwarf_seg.inner.filesize) orelse return error.Overflow, ); const old_seg_fileoff = dwarf_seg.inner.fileoff; @@ -669,7 +669,7 @@ fn writeStringTable(self: *DebugSymbols) !void { self.file, dwarf_seg.inner.fileoff, dwarf_seg.inner.fileoff + diff, - try math.cast(usize, dwarf_seg.inner.filesize), + math.cast(usize, dwarf_seg.inner.filesize) orelse return error.Overflow, ); const old_seg_fileoff = dwarf_seg.inner.fileoff; diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 73e7913d71..801f810f80 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -83,7 +83,7 @@ pub const Id = struct { switch (version) { .int => |int| { var out: u32 = 0; - const major = try math.cast(u16, int); + const major = math.cast(u16, int) orelse return error.Overflow; out += @intCast(u32, major) << 16; return out; }, diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 6bbc8cd9a4..03291cefab 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -504,7 +504,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) ! for (dices) |dice| { atom.dices.appendAssumeCapacity(.{ - .offset = dice.offset - try math.cast(u32, sect.addr), + .offset = dice.offset - (math.cast(u32, sect.addr) orelse return error.Overflow), .length = dice.length, .kind = dice.kind, }); diff --git a/src/link/tapi/yaml.zig b/src/link/tapi/yaml.zig index e31aa41985..270ae2d8ce 100644 --- a/src/link/tapi/yaml.zig +++ b/src/link/tapi/yaml.zig @@ -316,7 +316,7 @@ pub const Yaml = struct { fn parseValue(self: *Yaml, comptime T: type, value: Value) Error!T { return switch (@typeInfo(T)) { - .Int => math.cast(T, try value.asInt()), + .Int => math.cast(T, try value.asInt()) orelse error.Overflow, .Float => math.lossyCast(T, try value.asFloat()), .Struct => self.parseStruct(T, try value.asMap()), .Union => self.parseUnion(T, value), diff --git a/src/main.zig b/src/main.zig index 55def40d96..a9085d7c7f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4067,7 +4067,7 @@ fn fmtPathFile( const source_code = try readSourceFileToEndAlloc( fmt.gpa, &source_file, - std.math.cast(usize, stat.size) catch return error.FileTooBig, + std.math.cast(usize, stat.size) orelse return error.FileTooBig, ); defer fmt.gpa.free(source_code); diff --git a/src/translate_c.zig b/src/translate_c.zig index 0139ec8ec3..8c4bd5e9bf 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -4526,9 +4526,7 @@ fn transCreateNodeBoolInfixOp( } fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node { - const num_limbs = math.cast(usize, int.getNumWords()) catch |err| switch (err) { - error.Overflow => return error.OutOfMemory, - }; + const num_limbs = math.cast(usize, int.getNumWords()) orelse return error.OutOfMemory; var aps_int = int; const is_negative = int.isSigned() and int.isNegative(); if (is_negative) aps_int = aps_int.negate(); @@ -5627,12 +5625,12 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node { // make the output less noisy by skipping promoteIntLiteral where // it's guaranteed to not be required because of C standard type constraints const guaranteed_to_fit = switch (suffix) { - .none => !meta.isError(math.cast(i16, value)), - .u => !meta.isError(math.cast(u16, value)), - .l => !meta.isError(math.cast(i32, value)), - .lu => !meta.isError(math.cast(u32, value)), - .ll => !meta.isError(math.cast(i64, value)), - .llu => !meta.isError(math.cast(u64, value)), + .none => math.cast(i16, value) != null, + .u => math.cast(u16, value) != null, + .l => math.cast(i32, value) != null, + .lu => math.cast(u32, value) != null, + .ll => math.cast(i64, value) != null, + .llu => math.cast(u64, value) != null, .f => unreachable, }; From 22cb6938891c73d64b749a2516c8eaf79aa25b03 Mon Sep 17 00:00:00 2001 From: TwoClocks <5883156+TwoClocks@users.noreply.github.com> Date: Sat, 15 Jan 2022 10:30:18 -0800 Subject: [PATCH 1673/2031] reserve correct space for bitfields --- src/clang.zig | 6 ++++ src/translate_c.zig | 65 +++++++++++++++++++++++++++++++++++++++----- src/zig_clang.cpp | 12 ++++++++ src/zig_clang.h | 2 ++ test/translate_c.zig | 54 +++++++++++++----------------------- 5 files changed, 97 insertions(+), 42 deletions(-) diff --git a/src/clang.zig b/src/clang.zig index 64c7da5091..769021fc44 100644 --- a/src/clang.zig +++ b/src/clang.zig @@ -476,6 +476,12 @@ pub const FieldDecl = opaque { pub const isBitField = ZigClangFieldDecl_isBitField; extern fn ZigClangFieldDecl_isBitField(*const FieldDecl) bool; + pub const getBitWidthValue = ZigClangFieldDecl_getBitWidthValue; + extern fn ZigClangFieldDecl_getBitWidthValue(*const FieldDecl, *const ASTContext) c_uint; + + pub const isZeroLengthBitField = ZigClangFieldDecl_isZeroLengthBitField; + extern fn ZigClangFieldDecl_isZeroLengthBitField(*const FieldDecl, *const ASTContext) bool; + pub const getType = ZigClangFieldDecl_getType; extern fn ZigClangFieldDecl_getType(*const FieldDecl) QualType; diff --git a/src/translate_c.zig b/src/translate_c.zig index 8c4bd5e9bf..874dbf0521 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1085,17 +1085,15 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD const layout = record_def.getASTRecordLayout(c.clang_context); const record_alignment = layout.getAlignment(); + var record_bitfield_count: u32 = 0; + var bits_unused: i32 = 0; + var bits_type: clang.BuiltinTypeKind = clang.BuiltinTypeKind.Void; + while (it.neq(end_it)) : (it = it.next()) { const field_decl = it.deref(); const field_loc = field_decl.getLocation(); const field_qt = field_decl.getType(); - if (field_decl.isBitField()) { - try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); - try warn(c, scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name}); - break :blk Tag.opaque_literal.init(); - } - var is_anon = false; var field_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin()); if (field_decl.isAnonymousStructOrUnion() or field_name.len == 0) { @@ -1125,6 +1123,53 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD else => |e| return e, }; + if (field_decl.isBitField()) { + var this_field_width = @intCast(i32, field_decl.getBitWidthValue(c.clang_context)); + + // are we starting new bitfield? + if (bits_unused <= 0) { + const size_map = std.ComptimeStringMap(u16, .{ .{ "u8", 8 }, .{ "c_ushort", 16 }, .{ "u16", 16 }, .{ "u32", 32 }, .{ "c_uint", 32 }, .{ "c_ulong", 32 }, .{ "c_ulonglong", 64 }, .{ "u64", 64 } }); + + bits_type = @ptrCast(*const clang.BuiltinType, field_qt.getTypePtr()).getKind(); + + var field_width = field_type.castTag(.type).?.*.data; // we just set it 10 lines back. this should not fail. + + if (size_map.get(field_width)) |sz| { + bits_unused = @intCast(i32, sz) - this_field_width; + field_name = try std.fmt.allocPrint(c.arena, "bitfield{d}", .{record_bitfield_count}); + record_bitfield_count += 1; + } else { + try warn(c, scope, field_loc, "{s} demoted to opaque type - bitfield type not unsigned or unknown. type:{s} ", .{ container_kind_name, field_width }); + break :blk Tag.opaque_literal.init(); + } + } else { + var this_type = @ptrCast(*const clang.BuiltinType, field_qt.getTypePtr()).getKind(); + if (field_decl.isZeroLengthBitField(c.clang_context)) { + try warn(c, scope, field_loc, "{s} demoted to opaque type - bitfield with zero size not supported", .{container_kind_name}); + break :blk Tag.opaque_literal.init(); + } else if (bits_type != this_type) { + try warn(c, scope, field_loc, "{s} demoted to opaque type - bitfield type changed in the middle of bitfield was;{s} is:{s}", .{ container_kind_name, @tagName(bits_type), @tagName(this_type) }); + break :blk Tag.opaque_literal.init(); + } else { + // next + bits_unused -= this_field_width; + if (bits_unused < 0) { + try warn(c, scope, field_loc, "{s} demoted to opaque type - bitfield overrun type size not supported", .{container_kind_name}); + break :blk Tag.opaque_literal.init(); + } + } + continue; // free the field_type? tag is alloc'd + } + } else { + if (bits_unused >= 8) { + // if they didn't add a "reserved:n" at the end, and the # of bits used is more than 1 byte of their requested size, + // the layout is to compiler specific. + try warn(c, scope, field_loc, "{s} demoted to opaque type - less bits used than field size. unused bit count:{d}", .{ container_kind_name, bits_unused }); + break :blk Tag.opaque_literal.init(); + } + bits_unused = 0; + } + const alignment = if (has_flexible_array and field_decl.getFieldIndex() == 0) @intCast(c_uint, record_alignment) else @@ -1140,6 +1185,10 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD .alignment = alignment, }); } + if (bits_unused >= 8) { // one last check if the last field was a bitfield + try warn(c, scope, record_loc, "{s} demoted to opaque type - less bits used than field size. unused bit count:{d}", .{ container_kind_name, bits_unused }); + break :blk Tag.opaque_literal.init(); + } const record_payload = try c.arena.create(ast.Payload.Record); record_payload.* = .{ @@ -2576,7 +2625,9 @@ fn transInitListExprRecord( continue; } - assert(init_i < init_count); + if (init_i >= init_count) { + return fail(c, error.UnsupportedTranslation, loc, "init list longer fields in record. Record has bitfield?", .{}); + } const elem_expr = expr.getInit(init_i); init_i += 1; diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index ea3f09c8d5..0ceffe4c3d 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -3335,6 +3335,18 @@ bool ZigClangFieldDecl_isBitField(const struct ZigClangFieldDecl *self) { return casted->isBitField(); } +unsigned ZigClangFieldDecl_getBitWidthValue( const struct ZigClangFieldDecl *self, const ZigClangASTContext *ctx) { + auto casted = reinterpret_cast(self); + auto casted_ctx = const_cast(reinterpret_cast(ctx)); + return casted->getBitWidthValue(*casted_ctx); +} + +bool ZigClangFieldDecl_isZeroLengthBitField( ZigClangFieldDecl *self, const ZigClangASTContext *ctx) { + auto casted = reinterpret_cast(self); + auto casted_ctx = const_cast(reinterpret_cast(ctx)); + return casted->isZeroLengthBitField(*casted_ctx); +} + bool ZigClangFieldDecl_isAnonymousStructOrUnion(const ZigClangFieldDecl *field_decl) { return reinterpret_cast(field_decl)->isAnonymousStructOrUnion(); } diff --git a/src/zig_clang.h b/src/zig_clang.h index 267edfaea8..0b6d97e07e 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -1407,6 +1407,8 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangMacroDefinitionRecord_getSour ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangMacroDefinitionRecord_getSourceRange_getEnd(const struct ZigClangMacroDefinitionRecord *); ZIG_EXTERN_C bool ZigClangFieldDecl_isBitField(const struct ZigClangFieldDecl *); +ZIG_EXTERN_C unsigned ZigClangFieldDecl_getBitWidthValue( const struct ZigClangFieldDecl *, const ZigClangASTContext *); +ZIG_EXTERN_C bool ZigClangFieldDecl_isZeroLengthBitField( ZigClangFieldDecl *, const ZigClangASTContext *); ZIG_EXTERN_C bool ZigClangFieldDecl_isAnonymousStructOrUnion(const ZigClangFieldDecl *); ZIG_EXTERN_C struct ZigClangQualType ZigClangFieldDecl_getType(const struct ZigClangFieldDecl *); ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangFieldDecl_getLocation(const struct ZigClangFieldDecl *); diff --git a/test/translate_c.zig b/test/translate_c.zig index c49aa55baa..14b5783fef 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -915,21 +915,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; }); - cases.add("pointer to struct demoted to opaque due to bit fields", - \\struct Foo { - \\ unsigned int: 1; - \\}; - \\struct Bar { - \\ struct Foo *foo; - \\}; - , &[_][]const u8{ - \\pub const struct_Foo = opaque {}; - , - \\pub const struct_Bar = extern struct { - \\ foo: ?*struct_Foo, - \\}; - }); - cases.add("macro with left shift", \\#define REDISMODULE_READ (1<<0) , &[_][]const u8{ @@ -3577,34 +3562,33 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add("Demote function that initializes opaque struct", - \\struct my_struct { - \\ unsigned a: 15; - \\ unsigned: 2; - \\ unsigned b: 15; - \\}; - \\void initialize(void) { - \\ struct my_struct S = {.a = 1, .b = 2}; - \\} - , &[_][]const u8{ - \\warning: cannot initialize opaque type - , - \\warning: unable to translate function, demoted to extern - \\pub extern fn initialize() void; - }); - - cases.add("Demote function that dereferences opaque type", + cases.add("function that dereferences bitfield works", \\struct my_struct { \\ unsigned a: 1; + \\ unsigned b: 28; \\}; \\void deref(struct my_struct *s) { \\ *s; \\} , &[_][]const u8{ - \\warning: cannot dereference opaque type + \\pub const struct_my_struct = extern struct { + \\ bitfield0: c_uint, , - \\warning: unable to translate function, demoted to extern - \\pub extern fn deref(arg_s: ?*struct_my_struct) void; + \\pub export fn deref(arg_s: ?*struct_my_struct) void { + \\ var s = arg_s; + \\ _ = s.*; + \\} + }); + + cases.add("bitfield don't cover requedted space", + \\struct inner { + \\ unsigned a: 1; + \\ char after; + \\}; + , &[_][]const u8{ + \\less bits used than field size. + , + \\pub const struct_inner = opaque {}; }); cases.add("Function prototype declared within function", From ee651c3cd358f40f60db0bbcd82ffde99aed9b88 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 29 May 2022 12:04:50 +0300 Subject: [PATCH 1674/2031] Revert "reserve correct space for bitfields" This reverts commit 22cb6938891c73d64b749a2516c8eaf79aa25b03. --- src/clang.zig | 6 ---- src/translate_c.zig | 65 +++++--------------------------------------- src/zig_clang.cpp | 12 -------- src/zig_clang.h | 2 -- test/translate_c.zig | 54 +++++++++++++++++++++++------------- 5 files changed, 42 insertions(+), 97 deletions(-) diff --git a/src/clang.zig b/src/clang.zig index 769021fc44..64c7da5091 100644 --- a/src/clang.zig +++ b/src/clang.zig @@ -476,12 +476,6 @@ pub const FieldDecl = opaque { pub const isBitField = ZigClangFieldDecl_isBitField; extern fn ZigClangFieldDecl_isBitField(*const FieldDecl) bool; - pub const getBitWidthValue = ZigClangFieldDecl_getBitWidthValue; - extern fn ZigClangFieldDecl_getBitWidthValue(*const FieldDecl, *const ASTContext) c_uint; - - pub const isZeroLengthBitField = ZigClangFieldDecl_isZeroLengthBitField; - extern fn ZigClangFieldDecl_isZeroLengthBitField(*const FieldDecl, *const ASTContext) bool; - pub const getType = ZigClangFieldDecl_getType; extern fn ZigClangFieldDecl_getType(*const FieldDecl) QualType; diff --git a/src/translate_c.zig b/src/translate_c.zig index 874dbf0521..8c4bd5e9bf 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1085,15 +1085,17 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD const layout = record_def.getASTRecordLayout(c.clang_context); const record_alignment = layout.getAlignment(); - var record_bitfield_count: u32 = 0; - var bits_unused: i32 = 0; - var bits_type: clang.BuiltinTypeKind = clang.BuiltinTypeKind.Void; - while (it.neq(end_it)) : (it = it.next()) { const field_decl = it.deref(); const field_loc = field_decl.getLocation(); const field_qt = field_decl.getType(); + if (field_decl.isBitField()) { + try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); + try warn(c, scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name}); + break :blk Tag.opaque_literal.init(); + } + var is_anon = false; var field_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin()); if (field_decl.isAnonymousStructOrUnion() or field_name.len == 0) { @@ -1123,53 +1125,6 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD else => |e| return e, }; - if (field_decl.isBitField()) { - var this_field_width = @intCast(i32, field_decl.getBitWidthValue(c.clang_context)); - - // are we starting new bitfield? - if (bits_unused <= 0) { - const size_map = std.ComptimeStringMap(u16, .{ .{ "u8", 8 }, .{ "c_ushort", 16 }, .{ "u16", 16 }, .{ "u32", 32 }, .{ "c_uint", 32 }, .{ "c_ulong", 32 }, .{ "c_ulonglong", 64 }, .{ "u64", 64 } }); - - bits_type = @ptrCast(*const clang.BuiltinType, field_qt.getTypePtr()).getKind(); - - var field_width = field_type.castTag(.type).?.*.data; // we just set it 10 lines back. this should not fail. - - if (size_map.get(field_width)) |sz| { - bits_unused = @intCast(i32, sz) - this_field_width; - field_name = try std.fmt.allocPrint(c.arena, "bitfield{d}", .{record_bitfield_count}); - record_bitfield_count += 1; - } else { - try warn(c, scope, field_loc, "{s} demoted to opaque type - bitfield type not unsigned or unknown. type:{s} ", .{ container_kind_name, field_width }); - break :blk Tag.opaque_literal.init(); - } - } else { - var this_type = @ptrCast(*const clang.BuiltinType, field_qt.getTypePtr()).getKind(); - if (field_decl.isZeroLengthBitField(c.clang_context)) { - try warn(c, scope, field_loc, "{s} demoted to opaque type - bitfield with zero size not supported", .{container_kind_name}); - break :blk Tag.opaque_literal.init(); - } else if (bits_type != this_type) { - try warn(c, scope, field_loc, "{s} demoted to opaque type - bitfield type changed in the middle of bitfield was;{s} is:{s}", .{ container_kind_name, @tagName(bits_type), @tagName(this_type) }); - break :blk Tag.opaque_literal.init(); - } else { - // next - bits_unused -= this_field_width; - if (bits_unused < 0) { - try warn(c, scope, field_loc, "{s} demoted to opaque type - bitfield overrun type size not supported", .{container_kind_name}); - break :blk Tag.opaque_literal.init(); - } - } - continue; // free the field_type? tag is alloc'd - } - } else { - if (bits_unused >= 8) { - // if they didn't add a "reserved:n" at the end, and the # of bits used is more than 1 byte of their requested size, - // the layout is to compiler specific. - try warn(c, scope, field_loc, "{s} demoted to opaque type - less bits used than field size. unused bit count:{d}", .{ container_kind_name, bits_unused }); - break :blk Tag.opaque_literal.init(); - } - bits_unused = 0; - } - const alignment = if (has_flexible_array and field_decl.getFieldIndex() == 0) @intCast(c_uint, record_alignment) else @@ -1185,10 +1140,6 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD .alignment = alignment, }); } - if (bits_unused >= 8) { // one last check if the last field was a bitfield - try warn(c, scope, record_loc, "{s} demoted to opaque type - less bits used than field size. unused bit count:{d}", .{ container_kind_name, bits_unused }); - break :blk Tag.opaque_literal.init(); - } const record_payload = try c.arena.create(ast.Payload.Record); record_payload.* = .{ @@ -2625,9 +2576,7 @@ fn transInitListExprRecord( continue; } - if (init_i >= init_count) { - return fail(c, error.UnsupportedTranslation, loc, "init list longer fields in record. Record has bitfield?", .{}); - } + assert(init_i < init_count); const elem_expr = expr.getInit(init_i); init_i += 1; diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index 0ceffe4c3d..ea3f09c8d5 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -3335,18 +3335,6 @@ bool ZigClangFieldDecl_isBitField(const struct ZigClangFieldDecl *self) { return casted->isBitField(); } -unsigned ZigClangFieldDecl_getBitWidthValue( const struct ZigClangFieldDecl *self, const ZigClangASTContext *ctx) { - auto casted = reinterpret_cast(self); - auto casted_ctx = const_cast(reinterpret_cast(ctx)); - return casted->getBitWidthValue(*casted_ctx); -} - -bool ZigClangFieldDecl_isZeroLengthBitField( ZigClangFieldDecl *self, const ZigClangASTContext *ctx) { - auto casted = reinterpret_cast(self); - auto casted_ctx = const_cast(reinterpret_cast(ctx)); - return casted->isZeroLengthBitField(*casted_ctx); -} - bool ZigClangFieldDecl_isAnonymousStructOrUnion(const ZigClangFieldDecl *field_decl) { return reinterpret_cast(field_decl)->isAnonymousStructOrUnion(); } diff --git a/src/zig_clang.h b/src/zig_clang.h index 0b6d97e07e..267edfaea8 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -1407,8 +1407,6 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangMacroDefinitionRecord_getSour ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangMacroDefinitionRecord_getSourceRange_getEnd(const struct ZigClangMacroDefinitionRecord *); ZIG_EXTERN_C bool ZigClangFieldDecl_isBitField(const struct ZigClangFieldDecl *); -ZIG_EXTERN_C unsigned ZigClangFieldDecl_getBitWidthValue( const struct ZigClangFieldDecl *, const ZigClangASTContext *); -ZIG_EXTERN_C bool ZigClangFieldDecl_isZeroLengthBitField( ZigClangFieldDecl *, const ZigClangASTContext *); ZIG_EXTERN_C bool ZigClangFieldDecl_isAnonymousStructOrUnion(const ZigClangFieldDecl *); ZIG_EXTERN_C struct ZigClangQualType ZigClangFieldDecl_getType(const struct ZigClangFieldDecl *); ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangFieldDecl_getLocation(const struct ZigClangFieldDecl *); diff --git a/test/translate_c.zig b/test/translate_c.zig index 14b5783fef..c49aa55baa 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -915,6 +915,21 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; }); + cases.add("pointer to struct demoted to opaque due to bit fields", + \\struct Foo { + \\ unsigned int: 1; + \\}; + \\struct Bar { + \\ struct Foo *foo; + \\}; + , &[_][]const u8{ + \\pub const struct_Foo = opaque {}; + , + \\pub const struct_Bar = extern struct { + \\ foo: ?*struct_Foo, + \\}; + }); + cases.add("macro with left shift", \\#define REDISMODULE_READ (1<<0) , &[_][]const u8{ @@ -3562,33 +3577,34 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add("function that dereferences bitfield works", + cases.add("Demote function that initializes opaque struct", + \\struct my_struct { + \\ unsigned a: 15; + \\ unsigned: 2; + \\ unsigned b: 15; + \\}; + \\void initialize(void) { + \\ struct my_struct S = {.a = 1, .b = 2}; + \\} + , &[_][]const u8{ + \\warning: cannot initialize opaque type + , + \\warning: unable to translate function, demoted to extern + \\pub extern fn initialize() void; + }); + + cases.add("Demote function that dereferences opaque type", \\struct my_struct { \\ unsigned a: 1; - \\ unsigned b: 28; \\}; \\void deref(struct my_struct *s) { \\ *s; \\} , &[_][]const u8{ - \\pub const struct_my_struct = extern struct { - \\ bitfield0: c_uint, + \\warning: cannot dereference opaque type , - \\pub export fn deref(arg_s: ?*struct_my_struct) void { - \\ var s = arg_s; - \\ _ = s.*; - \\} - }); - - cases.add("bitfield don't cover requedted space", - \\struct inner { - \\ unsigned a: 1; - \\ char after; - \\}; - , &[_][]const u8{ - \\less bits used than field size. - , - \\pub const struct_inner = opaque {}; + \\warning: unable to translate function, demoted to extern + \\pub extern fn deref(arg_s: ?*struct_my_struct) void; }); cases.add("Function prototype declared within function", From 36b4658752257a27b1d7db5a4396132784801997 Mon Sep 17 00:00:00 2001 From: TwoClocks <5883156+TwoClocks@users.noreply.github.com> Date: Fri, 31 Dec 2021 13:01:14 -0800 Subject: [PATCH 1675/2031] translate-c: check record fields for opaque demotions --- src/translate_c.zig | 11 ++++++++++- test/translate_c.zig | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 8c4bd5e9bf..06ccf53f63 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -4831,7 +4831,16 @@ fn qualTypeWasDemotedToOpaque(c: *Context, qt: clang.QualType) bool { const record_decl = record_ty.getDecl(); const canonical = @ptrToInt(record_decl.getCanonicalDecl()); - return c.opaque_demotes.contains(canonical); + if (c.opaque_demotes.contains(canonical)) return true; + + // check all childern for opaque types. + var it = record_decl.field_begin(); + const end_it = record_decl.field_end(); + while (it.neq(end_it)) : (it = it.next()) { + const field_decl = it.deref(); + if (qualTypeWasDemotedToOpaque(c, field_decl.getType())) return true; + } + return false; }, .Enum => { const enum_ty = @ptrCast(*const clang.EnumType, ty); diff --git a/test/translate_c.zig b/test/translate_c.zig index c49aa55baa..590e52f2ae 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -3607,6 +3607,30 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub extern fn deref(arg_s: ?*struct_my_struct) void; }); + cases.add("Demote function that dereference types that contain opaque type", + \\struct inner { + \\ _Atomic int a; + \\}; + \\struct outer { + \\ int thing; + \\ struct inner sub_struct; + \\}; + \\void deref(struct outer *s) { + \\ *s; + \\} + , &[_][]const u8{ + \\pub const struct_inner = opaque {}; + , + \\pub const struct_outer = extern struct { + \\ thing: c_int, + \\ sub_struct: struct_inner, + \\}; + , + \\warning: unable to translate function, demoted to extern + , + \\pub extern fn deref(arg_s: ?*struct_outer) void; + }); + cases.add("Function prototype declared within function", \\int foo(void) { \\ extern int bar(int, int); From 0274e2f1fd3b7a81344080d532cfd2d384427cd2 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 27 May 2022 16:43:21 +0300 Subject: [PATCH 1676/2031] translate-c: check variable types being demoted to opaque --- src/translate_c.zig | 9 +++++++-- test/translate_c.zig | 16 +++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 06ccf53f63..3bbe1e6f46 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -793,6 +793,10 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co var is_extern = storage_class == .Extern and !has_init; var is_export = !is_extern and storage_class != .Static; + if (!is_extern and qualTypeWasDemotedToOpaque(c, qual_type)) { + return failDecl(c, var_decl_loc, var_name, "non-extern variable has opaque type", .{}); + } + const type_node = transQualTypeMaybeInitialized(c, scope, qual_type, decl_init, var_decl_loc) catch |err| switch (err) { error.UnsupportedTranslation, error.UnsupportedType => { return failDecl(c, var_decl_loc, var_name, "unable to resolve variable type", .{}); @@ -1839,6 +1843,7 @@ fn transDeclStmtOne( .Var => { const var_decl = @ptrCast(*const clang.VarDecl, decl); const decl_init = var_decl.getInit(); + const loc = decl.getLocation(); const qual_type = var_decl.getTypeSourceInfo_getType(); const name = try c.str(@ptrCast(*const clang.NamedDecl, var_decl).getName_bytes_begin()); @@ -1848,12 +1853,12 @@ fn transDeclStmtOne( // This is actually a global variable, put it in the global scope and reference it. // `_ = mangled_name;` return visitVarDecl(c, var_decl, mangled_name); + } else if (qualTypeWasDemotedToOpaque(c, qual_type)) { + return fail(c, error.UnsupportedTranslation, loc, "local variable has opaque type", .{}); } const is_static_local = var_decl.isStaticLocal(); const is_const = qual_type.isConstQualified(); - - const loc = decl.getLocation(); const type_node = try transQualTypeMaybeInitialized(c, scope, qual_type, decl_init, loc); var init_node = if (decl_init) |expr| diff --git a/test/translate_c.zig b/test/translate_c.zig index 590e52f2ae..f5748f0659 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -6,6 +6,20 @@ const CrossTarget = std.zig.CrossTarget; pub fn addCases(cases: *tests.TranslateCContext) void { const default_enum_type = if (builtin.abi == .msvc) "c_int" else "c_uint"; + cases.add("variables check for opaque demotion", + \\struct A { + \\ _Atomic int a; + \\} a; + \\int main(void) { + \\ struct A a; + \\} + , &[_][]const u8{ + \\pub const struct_A = opaque {}; + \\pub const a = @compileError("non-extern variable has opaque type"); + , + \\pub extern fn main() c_int; + }); + cases.add("field access is grouped if necessary", \\unsigned long foo(unsigned long x) { \\ return ((union{unsigned long _x}){x})._x; @@ -3587,7 +3601,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ struct my_struct S = {.a = 1, .b = 2}; \\} , &[_][]const u8{ - \\warning: cannot initialize opaque type + \\warning: local variable has opaque type , \\warning: unable to translate function, demoted to extern \\pub extern fn initialize() void; From c7b778992ec539e237d8afa7c105dcbad7ee280c Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 28 May 2022 18:38:35 +0300 Subject: [PATCH 1677/2031] AstGen: improve generated Zir for array init exprs --- src/AstGen.zig | 114 +++++++++++----------------------------- src/Sema.zig | 45 ++++++---------- src/Zir.zig | 20 ------- src/print_zir.zig | 10 ++-- test/behavior/basic.zig | 7 +++ test/behavior/tuple.zig | 2 - 6 files changed, 57 insertions(+), 141 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index ba27165cea..ec1cae7386 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1273,16 +1273,8 @@ fn arrayInitExpr( assert(array_init.ast.elements.len != 0); // Otherwise it would be struct init. - const types: struct { - array: Zir.Inst.Ref, - elem: Zir.Inst.Ref, - sentinel: Zir.Inst.Ref, - } = inst: { - if (array_init.ast.type_expr == 0) break :inst .{ - .array = .none, - .elem = .none, - .sentinel = .none, - }; + const array_ty: Zir.Inst.Ref = inst: { + if (array_init.ast.type_expr == 0) break :inst .none; infer: { const array_type: Ast.full.ArrayType = switch (node_tags[array_init.ast.type_expr]) { @@ -1297,15 +1289,10 @@ fn arrayInitExpr( const len_inst = try gz.addInt(array_init.ast.elements.len); const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type); if (array_type.ast.sentinel == 0) { - const array_type_inst = try gz.addBin(.array_type, len_inst, elem_type); - break :inst .{ - .array = array_type_inst, - .elem = elem_type, - .sentinel = .none, - }; + break :inst try gz.addBin(.array_type, len_inst, elem_type); } else { const sentinel = try comptimeExpr(gz, scope, .{ .ty = elem_type }, array_type.ast.sentinel); - const array_type_inst = try gz.addPlNode( + break :inst try gz.addPlNode( .array_type_sentinel, array_init.ast.type_expr, Zir.Inst.ArrayTypeSentinel{ @@ -1314,76 +1301,57 @@ fn arrayInitExpr( .sentinel = sentinel, }, ); - break :inst .{ - .array = array_type_inst, - .elem = elem_type, - .sentinel = sentinel, - }; } } } const array_type_inst = try typeExpr(gz, scope, array_init.ast.type_expr); _ = try gz.addUnNode(.validate_array_init_ty, array_type_inst, node); - const elem_type = try gz.addUnNode(.elem_type, array_type_inst, array_init.ast.type_expr); - break :inst .{ - .array = array_type_inst, - .elem = elem_type, - .sentinel = .none, - }; + break :inst array_type_inst; }; switch (rl) { .discard => { + // TODO elements should still be coerced if type is provided for (array_init.ast.elements) |elem_init| { _ = try expr(gz, scope, .discard, elem_init); } return Zir.Inst.Ref.void_value; }, .ref => { - if (types.array != .none) { - return arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, types.sentinel, true); - } else { - return arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon_ref); - } + const tag: Zir.Inst.Tag = if (array_ty != .none) .array_init_ref else .array_init_anon_ref; + return arrayInitExprInner(gz, scope, node, array_init.ast.elements, array_ty, tag); }, .none => { - if (types.array != .none) { - return arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, types.sentinel, false); - } else { - return arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon); - } + const tag: Zir.Inst.Tag = if (array_ty != .none) .array_init else .array_init_anon; + return arrayInitExprInner(gz, scope, node, array_init.ast.elements, array_ty, tag); }, .ty, .coerced_ty => { - if (types.array != .none) { - const result = try arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, types.sentinel, false); - return rvalue(gz, rl, result, node); - } else { - const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon); - return rvalue(gz, rl, result, node); - } + const tag: Zir.Inst.Tag = if (array_ty != .none) .array_init else .array_init_anon; + const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, array_ty, tag); + return rvalue(gz, rl, result, node); }, .ptr => |ptr_inst| { - return arrayInitExprRlPtr(gz, scope, rl, node, ptr_inst, array_init.ast.elements, types.array); + return arrayInitExprRlPtr(gz, scope, rl, node, ptr_inst, array_init.ast.elements, array_ty); }, .inferred_ptr => |ptr_inst| { - if (types.array == .none) { + if (array_ty == .none) { // We treat this case differently so that we don't get a crash when // analyzing array_base_ptr against an alloc_inferred_mut. // See corresponding logic in structInitExpr. const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon); return rvalue(gz, rl, result, node); } else { - return arrayInitExprRlPtr(gz, scope, rl, node, ptr_inst, array_init.ast.elements, types.array); + return arrayInitExprRlPtr(gz, scope, rl, node, ptr_inst, array_init.ast.elements, array_ty); } }, .block_ptr => |block_gz| { // This condition is here for the same reason as the above condition in `inferred_ptr`. // See corresponding logic in structInitExpr. - if (types.array == .none and astgen.isInferred(block_gz.rl_ptr)) { + if (array_ty == .none and astgen.isInferred(block_gz.rl_ptr)) { const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon); return rvalue(gz, rl, result, node); } - return arrayInitExprRlPtr(gz, scope, rl, node, block_gz.rl_ptr, array_init.ast.elements, types.array); + return arrayInitExprRlPtr(gz, scope, rl, node, block_gz.rl_ptr, array_init.ast.elements, array_ty); }, } } @@ -1410,52 +1378,33 @@ fn arrayInitExprRlNone( return try gz.addPlNodePayloadIndex(tag, node, payload_index); } -fn arrayInitExprRlTy( +fn arrayInitExprInner( gz: *GenZir, scope: *Scope, node: Ast.Node.Index, elements: []const Ast.Node.Index, - elem_ty_inst: Zir.Inst.Ref, - sentinel: Zir.Inst.Ref, - ref: bool, + array_ty_inst: Zir.Inst.Ref, + tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { const astgen = gz.astgen; - const info: struct { - len: usize, - tag: Zir.Inst.Tag, - } = blk: { - if (sentinel != .none) { - break :blk .{ - .len = elements.len + 1, - .tag = if (ref) .array_init_sent_ref else .array_init_sent, - }; - } else { - break :blk .{ - .len = elements.len, - .tag = if (ref) .array_init_ref else .array_init, - }; - } - }; - + const len = elements.len + @boolToInt(array_ty_inst != .none); const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{ - .operands_len = @intCast(u32, info.len), + .operands_len = @intCast(u32, len), }); - var extra_index = try reserveExtra(astgen, info.len); + var extra_index = try reserveExtra(astgen, len); + if (array_ty_inst != .none) { + astgen.extra.items[extra_index] = @enumToInt(array_ty_inst); + extra_index += 1; + } - const elem_rl: ResultLoc = .{ .ty = elem_ty_inst }; for (elements) |elem_init| { - const elem_ref = try expr(gz, scope, elem_rl, elem_init); + const elem_ref = try expr(gz, scope, .none, elem_init); astgen.extra.items[extra_index] = @enumToInt(elem_ref); extra_index += 1; } - if (sentinel != .none) { - astgen.extra.items[extra_index] = @enumToInt(sentinel); - extra_index += 1; - } - - return try gz.addPlNodePayloadIndex(info.tag, node, payload_index); + return try gz.addPlNodePayloadIndex(tag, node, payload_index); } fn arrayInitExprRlPtr( @@ -2244,7 +2193,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .array_type, .array_type_sentinel, .vector_type, - .elem_type, .indexable_ptr_len, .anyframe_type, .as, @@ -2347,10 +2295,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .struct_init_anon_ref, .array_init, .array_init_anon, - .array_init_sent, .array_init_ref, .array_init_anon_ref, - .array_init_sent_ref, .union_init, .field_type, .field_type_ref, diff --git a/src/Sema.zig b/src/Sema.zig index e0310e5ad7..2a5b3e0260 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -726,7 +726,6 @@ fn analyzeBodyInner( .elem_ptr_imm => try sema.zirElemPtrImm(block, inst), .elem_val => try sema.zirElemVal(block, inst), .elem_val_node => try sema.zirElemValNode(block, inst), - .elem_type => try sema.zirElemType(block, inst), .enum_literal => try sema.zirEnumLiteral(block, inst), .enum_to_int => try sema.zirEnumToInt(block, inst), .int_to_enum => try sema.zirIntToEnum(block, inst), @@ -798,10 +797,8 @@ fn analyzeBodyInner( .struct_init_ref => try sema.zirStructInit(block, inst, true), .struct_init_anon => try sema.zirStructInitAnon(block, inst, false), .struct_init_anon_ref => try sema.zirStructInitAnon(block, inst, true), - .array_init => try sema.zirArrayInit(block, inst, false, false), - .array_init_sent => try sema.zirArrayInit(block, inst, false, true), - .array_init_ref => try sema.zirArrayInit(block, inst, true, false), - .array_init_sent_ref => try sema.zirArrayInit(block, inst, true, true), + .array_init => try sema.zirArrayInit(block, inst, false), + .array_init_ref => try sema.zirArrayInit(block, inst, true), .array_init_anon => try sema.zirArrayInitAnon(block, inst, false), .array_init_anon_ref => try sema.zirArrayInitAnon(block, inst, true), .union_init => try sema.zirUnionInit(block, inst), @@ -13436,7 +13433,6 @@ fn zirArrayInit( block: *Block, inst: Zir.Inst.Index, is_ref: bool, - is_sent: bool, ) CompileError!Air.Inst.Ref { const gpa = sema.gpa; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; @@ -13444,30 +13440,23 @@ fn zirArrayInit( const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); const args = sema.code.refSlice(extra.end, extra.data.operands_len); - assert(args.len != 0); + assert(args.len >= 2); // array_ty + at least one element - const resolved_args = try gpa.alloc(Air.Inst.Ref, args.len); + const array_ty = try sema.resolveType(block, src, args[0]); + const sentinel_val = array_ty.sentinel(); + + const resolved_args = try gpa.alloc(Air.Inst.Ref, args.len - 1 + @boolToInt(sentinel_val != null)); defer gpa.free(resolved_args); + const elem_ty = array_ty.elemType2(); + for (args[1..]) |arg, i| { + const resolved_arg = try sema.resolveInst(arg); + const arg_src = src; // TODO better source location + resolved_args[i] = try sema.coerce(block, elem_ty, resolved_arg, arg_src); + } - for (args) |arg, i| resolved_args[i] = try sema.resolveInst(arg); - - const elem_ty = sema.typeOf(resolved_args[0]); - const array_ty = blk: { - if (!is_sent) { - break :blk try Type.Tag.array.create(sema.arena, .{ - .len = resolved_args.len, - .elem_type = elem_ty, - }); - } - - const sentinel_ref = resolved_args[resolved_args.len - 1]; - const val = try sema.resolveConstValue(block, src, sentinel_ref); - break :blk try Type.Tag.array_sentinel.create(sema.arena, .{ - .len = resolved_args.len - 1, - .sentinel = val, - .elem_type = elem_ty, - }); - }; + if (sentinel_val) |some| { + resolved_args[resolved_args.len - 1] = try sema.addConstant(elem_ty, some); + } const opt_runtime_src: ?LazySrcLoc = for (resolved_args) |arg| { const arg_src = src; // TODO better source location @@ -13488,7 +13477,7 @@ fn zirArrayInit( }; try sema.requireRuntimeBlock(block, runtime_src); - try sema.queueFullTypeResolution(elem_ty); + try sema.queueFullTypeResolution(array_ty); if (is_ref) { const target = sema.mod.getTarget(); diff --git a/src/Zir.zig b/src/Zir.zig index c722457303..84e52a2e07 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -221,9 +221,6 @@ pub const Inst = struct { /// Uses the `pl_node` union field with `Bin` payload. /// lhs is length, rhs is element type. vector_type, - /// Given an array type, returns the element type. - /// Uses the `un_node` union field. - elem_type, /// Given a pointer to an indexable object, returns the len property. This is /// used by for loops. This instruction also emits a for-loop specific compile /// error if the indexable object is not indexable. @@ -737,20 +734,12 @@ pub const Inst = struct { /// Array initialization syntax. /// Uses the `pl_node` field. Payload is `MultiOp`. array_init, - /// Array initialization with sentinel. - /// Uses the `pl_node` field. Payload is `MultiOp`. - /// Final op in MultiOp is the sentinel. - array_init_sent, /// Anonymous array initialization syntax. /// Uses the `pl_node` field. Payload is `MultiOp`. array_init_anon, /// Array initialization syntax, make the result a pointer. /// Uses the `pl_node` field. Payload is `MultiOp`. array_init_ref, - /// Array initialization with sentinel. - /// Uses the `pl_node` field. Payload is `MultiOp`. - /// Final op in MultiOp is the sentinel. - array_init_sent_ref, /// Anonymous array initialization syntax, make the result a pointer. /// Uses the `pl_node` field. Payload is `MultiOp`. array_init_anon_ref, @@ -1019,7 +1008,6 @@ pub const Inst = struct { .array_type, .array_type_sentinel, .vector_type, - .elem_type, .indexable_ptr_len, .anyframe_type, .as, @@ -1153,10 +1141,8 @@ pub const Inst = struct { .struct_init_anon, .struct_init_anon_ref, .array_init, - .array_init_sent, .array_init_anon, .array_init_ref, - .array_init_sent_ref, .array_init_anon_ref, .union_init, .field_type, @@ -1314,7 +1300,6 @@ pub const Inst = struct { .array_type, .array_type_sentinel, .vector_type, - .elem_type, .indexable_ptr_len, .anyframe_type, .as, @@ -1426,10 +1411,8 @@ pub const Inst = struct { .struct_init_anon, .struct_init_anon_ref, .array_init, - .array_init_sent, .array_init_anon, .array_init_ref, - .array_init_sent_ref, .array_init_anon_ref, .union_init, .field_type, @@ -1554,7 +1537,6 @@ pub const Inst = struct { .array_type = .bin, .array_type_sentinel = .pl_node, .vector_type = .pl_node, - .elem_type = .un_node, .indexable_ptr_len = .un_node, .anyframe_type = .un_node, .as = .bin, @@ -1688,10 +1670,8 @@ pub const Inst = struct { .struct_init_anon = .pl_node, .struct_init_anon_ref = .pl_node, .array_init = .pl_node, - .array_init_sent = .pl_node, .array_init_anon = .pl_node, .array_init_ref = .pl_node, - .array_init_sent_ref = .pl_node, .array_init_anon_ref = .pl_node, .union_init = .pl_node, .type_info = .un_node, diff --git a/src/print_zir.zig b/src/print_zir.zig index 1f243acc30..0d6681da05 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -226,7 +226,6 @@ const Writer = struct { .pop_count, .byte_swap, .bit_reverse, - .elem_type, .@"resume", .@"await", .switch_cond, @@ -268,10 +267,6 @@ const Writer = struct { .array_init_anon_ref, => try self.writeArrayInit(stream, inst), - .array_init_sent, - .array_init_sent_ref, - => try self.writeArrayInitSent(stream, inst), - .slice_start => try self.writeSliceStart(stream, inst), .slice_end => try self.writeSliceEnd(stream, inst), .slice_sentinel => try self.writeSliceSentinel(stream, inst), @@ -2085,8 +2080,9 @@ const Writer = struct { const extra = self.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); const args = self.code.refSlice(extra.end, extra.data.operands_len); - try stream.writeAll(".{"); - for (args) |arg, i| { + try self.writeInstRef(stream, args[0]); + try stream.writeAll("{"); + for (args[1..]) |arg, i| { if (i != 0) try stream.writeAll(", "); try self.writeInstRef(stream, arg); } diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index ac9e776c76..11ecf089c5 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -942,3 +942,10 @@ test "comptime int in switch in catch is casted to correct inferred type" { }; _ = b; } + +test "vector initialized with array init syntax has proper type" { + comptime { + const actual = -@Vector(4, i32){ 1, 2, 3, 4 }; + try std.testing.expectEqual(@Vector(4, i32){ -1, -2, -3, -4 }, actual); + } +} diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index 5e044abf4f..f9237bb230 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -194,8 +194,6 @@ test "tuple as the result from a labeled block" { } test "initializing tuple with explicit type" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - const T = @TypeOf(.{ @as(i32, 0), @as(u32, 0) }); var a = T{ 0, 0 }; _ = a; From 0e8307789a3de41759e68c7c04c130889e277991 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 28 May 2022 22:04:52 +0300 Subject: [PATCH 1678/2031] AstGen: add tuple aware elem_type_index --- src/AstGen.zig | 68 ++++++++++++++++++++++++++++++----------- src/Sema.zig | 60 +++++++++++++++++++++++++++++------- src/Zir.zig | 6 ++++ src/print_zir.zig | 8 +++++ test/behavior/basic.zig | 28 +++++++++++++++++ 5 files changed, 141 insertions(+), 29 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index ec1cae7386..1a79e044fa 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1273,8 +1273,14 @@ fn arrayInitExpr( assert(array_init.ast.elements.len != 0); // Otherwise it would be struct init. - const array_ty: Zir.Inst.Ref = inst: { - if (array_init.ast.type_expr == 0) break :inst .none; + const types: struct { + array: Zir.Inst.Ref, + elem: Zir.Inst.Ref, + } = inst: { + if (array_init.ast.type_expr == 0) break :inst .{ + .array = .none, + .elem = .none, + }; infer: { const array_type: Ast.full.ArrayType = switch (node_tags[array_init.ast.type_expr]) { @@ -1289,10 +1295,14 @@ fn arrayInitExpr( const len_inst = try gz.addInt(array_init.ast.elements.len); const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type); if (array_type.ast.sentinel == 0) { - break :inst try gz.addBin(.array_type, len_inst, elem_type); + const array_type_inst = try gz.addBin(.array_type, len_inst, elem_type); + break :inst .{ + .array = array_type_inst, + .elem = elem_type, + }; } else { const sentinel = try comptimeExpr(gz, scope, .{ .ty = elem_type }, array_type.ast.sentinel); - break :inst try gz.addPlNode( + const array_type_inst = try gz.addPlNode( .array_type_sentinel, array_init.ast.type_expr, Zir.Inst.ArrayTypeSentinel{ @@ -1301,12 +1311,19 @@ fn arrayInitExpr( .sentinel = sentinel, }, ); + break :inst .{ + .array = array_type_inst, + .elem = elem_type, + }; } } } const array_type_inst = try typeExpr(gz, scope, array_init.ast.type_expr); _ = try gz.addUnNode(.validate_array_init_ty, array_type_inst, node); - break :inst array_type_inst; + break :inst .{ + .array = array_type_inst, + .elem = .none, + }; }; switch (rl) { @@ -1318,40 +1335,40 @@ fn arrayInitExpr( return Zir.Inst.Ref.void_value; }, .ref => { - const tag: Zir.Inst.Tag = if (array_ty != .none) .array_init_ref else .array_init_anon_ref; - return arrayInitExprInner(gz, scope, node, array_init.ast.elements, array_ty, tag); + const tag: Zir.Inst.Tag = if (types.array != .none) .array_init_ref else .array_init_anon_ref; + return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag); }, .none => { - const tag: Zir.Inst.Tag = if (array_ty != .none) .array_init else .array_init_anon; - return arrayInitExprInner(gz, scope, node, array_init.ast.elements, array_ty, tag); + const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon; + return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag); }, .ty, .coerced_ty => { - const tag: Zir.Inst.Tag = if (array_ty != .none) .array_init else .array_init_anon; - const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, array_ty, tag); + const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon; + const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag); return rvalue(gz, rl, result, node); }, .ptr => |ptr_inst| { - return arrayInitExprRlPtr(gz, scope, rl, node, ptr_inst, array_init.ast.elements, array_ty); + return arrayInitExprRlPtr(gz, scope, rl, node, ptr_inst, array_init.ast.elements, types.array); }, .inferred_ptr => |ptr_inst| { - if (array_ty == .none) { + if (types.array == .none) { // We treat this case differently so that we don't get a crash when // analyzing array_base_ptr against an alloc_inferred_mut. // See corresponding logic in structInitExpr. const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon); return rvalue(gz, rl, result, node); } else { - return arrayInitExprRlPtr(gz, scope, rl, node, ptr_inst, array_init.ast.elements, array_ty); + return arrayInitExprRlPtr(gz, scope, rl, node, ptr_inst, array_init.ast.elements, types.array); } }, .block_ptr => |block_gz| { // This condition is here for the same reason as the above condition in `inferred_ptr`. // See corresponding logic in structInitExpr. - if (array_ty == .none and astgen.isInferred(block_gz.rl_ptr)) { + if (types.array == .none and astgen.isInferred(block_gz.rl_ptr)) { const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon); return rvalue(gz, rl, result, node); } - return arrayInitExprRlPtr(gz, scope, rl, node, block_gz.rl_ptr, array_init.ast.elements, array_ty); + return arrayInitExprRlPtr(gz, scope, rl, node, block_gz.rl_ptr, array_init.ast.elements, types.array); }, } } @@ -1384,6 +1401,7 @@ fn arrayInitExprInner( node: Ast.Node.Index, elements: []const Ast.Node.Index, array_ty_inst: Zir.Inst.Ref, + elem_ty: Zir.Inst.Ref, tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { const astgen = gz.astgen; @@ -1398,8 +1416,21 @@ fn arrayInitExprInner( extra_index += 1; } - for (elements) |elem_init| { - const elem_ref = try expr(gz, scope, .none, elem_init); + for (elements) |elem_init, i| { + const rl = if (elem_ty != .none) + ResultLoc{ .coerced_ty = elem_ty } + else if (array_ty_inst != .none and nodeMayNeedMemoryLocation(astgen.tree, elem_init, true)) rl: { + const ty_expr = try gz.add(.{ + .tag = .elem_type_index, + .data = .{ .bin = .{ + .lhs = array_ty_inst, + .rhs = @intToEnum(Zir.Inst.Ref, i), + } }, + }); + break :rl ResultLoc{ .coerced_ty = ty_expr }; + } else ResultLoc{ .none = {} }; + + const elem_ref = try expr(gz, scope, rl, elem_init); astgen.extra.items[extra_index] = @enumToInt(elem_ref); extra_index += 1; } @@ -2192,6 +2223,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .array_mul, .array_type, .array_type_sentinel, + .elem_type_index, .vector_type, .indexable_ptr_len, .anyframe_type, diff --git a/src/Sema.zig b/src/Sema.zig index 2a5b3e0260..fc64e17789 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -726,6 +726,7 @@ fn analyzeBodyInner( .elem_ptr_imm => try sema.zirElemPtrImm(block, inst), .elem_val => try sema.zirElemVal(block, inst), .elem_val_node => try sema.zirElemValNode(block, inst), + .elem_type_index => try sema.zirElemTypeIndex(block, inst), .enum_literal => try sema.zirEnumLiteral(block, inst), .enum_to_int => try sema.zirEnumToInt(block, inst), .int_to_enum => try sema.zirIntToEnum(block, inst), @@ -3021,7 +3022,10 @@ fn zirArrayBasePtr( const elem_ty = sema.typeOf(base_ptr).childType(); switch (elem_ty.zigTypeTag()) { .Array, .Vector => return base_ptr, - .Struct => if (elem_ty.isTuple()) return base_ptr, + .Struct => if (elem_ty.isTuple()) { + // TODO validate element count + return base_ptr; + }, else => {}, } return sema.failWithArrayInitNotSupported(block, src, sema.typeOf(start_ptr).childType()); @@ -3062,7 +3066,10 @@ fn validateArrayInitTy( switch (ty.zigTypeTag()) { .Array, .Vector => return, - .Struct => if (ty.isTuple()) return, + .Struct => if (ty.isTuple()) { + // TODO validate element count + return; + }, else => {}, } return sema.failWithArrayInitNotSupported(block, src, ty); @@ -5805,12 +5812,17 @@ fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro return sema.addType(opt_type); } -fn zirElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const src = inst_data.src(); - const array_type = try sema.resolveType(block, src, inst_data.operand); - const elem_type = array_type.elemType(); - return sema.addType(elem_type); +fn zirElemTypeIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const bin = sema.code.instructions.items(.data)[inst].bin; + const indexable_ty = try sema.resolveType(block, .unneeded, bin.lhs); + assert(indexable_ty.isIndexable()); // validated by a previous instruction + if (indexable_ty.zigTypeTag() == .Struct) { + const elem_type = indexable_ty.tupleFields().types[@enumToInt(bin.rhs)]; + return sema.addType(elem_type); + } else { + const elem_type = indexable_ty.elemType2(); + return sema.addType(elem_type); + } } fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -13277,6 +13289,8 @@ fn zirStructInit( try sema.requireRuntimeBlock(block, src); try sema.queueFullTypeResolution(resolved_ty); return block.addUnionInit(resolved_ty, field_index, init_inst); + } else if (resolved_ty.isAnonStruct()) { + return sema.fail(block, src, "TODO anon struct init validation", .{}); } unreachable; } @@ -13447,15 +13461,18 @@ fn zirArrayInit( const resolved_args = try gpa.alloc(Air.Inst.Ref, args.len - 1 + @boolToInt(sentinel_val != null)); defer gpa.free(resolved_args); - const elem_ty = array_ty.elemType2(); for (args[1..]) |arg, i| { const resolved_arg = try sema.resolveInst(arg); const arg_src = src; // TODO better source location + const elem_ty = if (array_ty.zigTypeTag() == .Struct) + array_ty.tupleFields().types[i] + else + array_ty.elemType2(); resolved_args[i] = try sema.coerce(block, elem_ty, resolved_arg, arg_src); } if (sentinel_val) |some| { - resolved_args[resolved_args.len - 1] = try sema.addConstant(elem_ty, some); + resolved_args[resolved_args.len - 1] = try sema.addConstant(array_ty.elemType2(), some); } const opt_runtime_src: ?LazySrcLoc = for (resolved_args) |arg| { @@ -13487,10 +13504,27 @@ fn zirArrayInit( }); const alloc = try block.addTy(.alloc, alloc_ty); + if (array_ty.isTuple()) { + const types = array_ty.tupleFields().types; + for (resolved_args) |arg, i| { + const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ + .mutable = true, + .@"addrspace" = target_util.defaultAddressSpace(target, .local), + .pointee_type = types[i], + }); + const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty); + + const index = try sema.addIntUnsigned(Type.usize, i); + const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref); + _ = try block.addBinOp(.store, elem_ptr, arg); + } + return alloc; + } + const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ .mutable = true, .@"addrspace" = target_util.defaultAddressSpace(target, .local), - .pointee_type = elem_ty, + .pointee_type = array_ty.elemType2(), }); const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty); @@ -13632,6 +13666,10 @@ fn fieldType( while (true) { switch (cur_ty.zigTypeTag()) { .Struct => { + if (cur_ty.isAnonStruct()) { + const field_index = try sema.anonStructFieldIndex(block, cur_ty, field_name, field_src); + return sema.addType(cur_ty.tupleFields().types[field_index]); + } const struct_obj = cur_ty.castTag(.@"struct").?.data; const field = struct_obj.fields.get(field_name) orelse return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name); diff --git a/src/Zir.zig b/src/Zir.zig index 84e52a2e07..7516a5a873 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -221,6 +221,9 @@ pub const Inst = struct { /// Uses the `pl_node` union field with `Bin` payload. /// lhs is length, rhs is element type. vector_type, + /// Given an indexable type, returns the type of the element at given index. + /// Uses the `bin` union field. lhs is the indexable type, rhs is the index. + elem_type_index, /// Given a pointer to an indexable object, returns the len property. This is /// used by for loops. This instruction also emits a for-loop specific compile /// error if the indexable object is not indexable. @@ -1008,6 +1011,7 @@ pub const Inst = struct { .array_type, .array_type_sentinel, .vector_type, + .elem_type_index, .indexable_ptr_len, .anyframe_type, .as, @@ -1300,6 +1304,7 @@ pub const Inst = struct { .array_type, .array_type_sentinel, .vector_type, + .elem_type_index, .indexable_ptr_len, .anyframe_type, .as, @@ -1537,6 +1542,7 @@ pub const Inst = struct { .array_type = .bin, .array_type_sentinel = .pl_node, .vector_type = .pl_node, + .elem_type_index = .bin, .indexable_ptr_len = .un_node, .anyframe_type = .un_node, .as = .bin, diff --git a/src/print_zir.zig b/src/print_zir.zig index 0d6681da05..6bab2e5628 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -152,6 +152,8 @@ const Writer = struct { .store_to_inferred_ptr, => try self.writeBin(stream, inst), + .elem_type_index => try self.writeElemTypeIndex(stream, inst), + .alloc, .alloc_mut, .alloc_comptime_mut, @@ -538,6 +540,12 @@ const Writer = struct { try stream.writeByte(')'); } + fn writeElemTypeIndex(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].bin; + try self.writeInstRef(stream, inst_data.lhs); + try stream.print(", {d})", .{inst_data.rhs}); + } + fn writeUnNode( self: *Writer, stream: anytype, diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 11ecf089c5..e887a1d4b6 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -949,3 +949,31 @@ test "vector initialized with array init syntax has proper type" { try std.testing.expectEqual(@Vector(4, i32){ -1, -2, -3, -4 }, actual); } } + +test "weird array and tuple initializations" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const E = enum { a, b }; + const S = struct { e: E }; + var a = false; + const b = S{ .e = .a }; + + _ = &[_]S{ + if (a) .{ .e = .a } else .{ .e = .b }, + }; + + if (true) return error.SkipZigTest; + + const S2 = @TypeOf(.{ false, b }); + _ = &S2{ + true, + if (a) .{ .e = .a } else .{ .e = .b }, + }; + const S3 = @TypeOf(.{ .a = false, .b = b }); + _ = &S3{ + .a = true, + .b = if (a) .{ .e = .a } else .{ .e = .b }, + }; +} From 9da3a058d82573efeaf12fe61ab6a312649175ec Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 May 2022 17:54:16 -0700 Subject: [PATCH 1679/2031] stage2: add missing data to ZIR encoding of functions The main purpose of this commit is to prepare to implement support for callconv(), align(), linksection(), and addrspace() annotations on generic functions where the provided expression depends on comptime parameters (making the function generic). It's a rather involved change, so this commit only makes the necessary changes to AstGen without regressing any behavior, and a follow-up commit can finish the task by making the enhancements to Sema. By my quick estimation, the new encoding for functions is a negligible improvement - along the lines of 0.005% fewer total ZIR bytes on average. Still, it's nice that this commit, while adding more data into ZIR, actually ends up reducing the storage size thanks to a slightly more sophisticated encoding. Zir.Inst.ExtendedFunc is renamed to Zir.Inst.FuncFancy to eliminate confusion about it being an extended instruction (it used to be but is no longer). The encoding for this instruction is completely reworked. The encoding for Zir.Inst.Func is also changed slightly - when the return type body length is 1, then only a Zir.Inst.Ref is provided; not a full body. linksection() and addrspace() are now communicated via func_fancy ZIR instruction rather than as part of the corresponding decl. This allows their expressions to observe comptime parameters. --- src/AstGen.zig | 356 +++++++++++++++++++++++++++++++++++----------- src/Module.zig | 4 +- src/Sema.zig | 257 ++++++++++++++++++++++++--------- src/Zir.zig | 264 +++++++++++++++++++++++++++------- src/print_zir.zig | 171 +++++++++++++++++----- 5 files changed, 807 insertions(+), 245 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index ba27165cea..ff3404a51b 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -73,7 +73,7 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void { Zir.Inst.Call.Flags => @bitCast(u32, @field(extra, field.name)), Zir.Inst.BuiltinCall.Flags => @bitCast(u32, @field(extra, field.name)), Zir.Inst.SwitchBlock.Bits => @bitCast(u32, @field(extra, field.name)), - Zir.Inst.ExtendedFunc.Bits => @bitCast(u32, @field(extra, field.name)), + Zir.Inst.FuncFancy.Bits => @bitCast(u32, @field(extra, field.name)), else => @compileError("bad field type"), }; i += 1; @@ -1205,7 +1205,7 @@ fn fnProtoExpr( break :is_var_args false; }; - const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: { + const align_ref: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: { break :inst try expr(&block_scope, scope, align_rl, fn_proto.ast.align_expr); }; @@ -1232,19 +1232,24 @@ fn fnProtoExpr( if (is_inferred_error) { return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{}); } - var ret_gz = block_scope.makeSubBlock(scope); - defer ret_gz.unstack(); - const ret_ty = try expr(&ret_gz, scope, coerced_type_rl, fn_proto.ast.return_type); - const ret_br = try ret_gz.addBreak(.break_inline, 0, ret_ty); + const ret_ty = try expr(&block_scope, scope, coerced_type_rl, fn_proto.ast.return_type); const result = try block_scope.addFunc(.{ .src_node = fn_proto.ast.proto_node, + + .cc_ref = cc, + .cc_gz = null, + .align_ref = align_ref, + .align_gz = null, + .ret_ref = ret_ty, + .ret_gz = null, + .section_ref = .none, + .section_gz = null, + .addrspace_ref = .none, + .addrspace_gz = null, + .param_block = block_inst, - .ret_gz = &ret_gz, - .ret_br = ret_br, .body_gz = null, - .cc = cc, - .align_inst = align_inst, .lib_name = 0, .is_var_args = is_var_args, .is_inferred_error = false, @@ -2282,7 +2287,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .field_val_named, .func, .func_inferred, - .func_extended, + .func_fancy, .int, .int_big, .float, @@ -3395,9 +3400,8 @@ fn fnDecl( const doc_comment_index = try astgen.docCommentAsString(fn_proto.firstToken()); - const has_section_or_addrspace = fn_proto.ast.section_expr != 0 or fn_proto.ast.addrspace_expr != 0; - // Alignment is passed in the func instruction in this case. - wip_members.nextDecl(is_pub, is_export, false, has_section_or_addrspace); + // align, linksection, and addrspace is passed in the func instruction in this case. + wip_members.nextDecl(is_pub, is_export, false, false); var params_scope = &fn_gz.base; const is_var_args = is_var_args: { @@ -3483,17 +3487,49 @@ fn fnDecl( const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; const is_inferred_error = token_tags[maybe_bang] == .bang; - const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: { - break :inst try expr(&decl_gz, params_scope, align_rl, fn_proto.ast.align_expr); - }; - const addrspace_inst: Zir.Inst.Ref = if (fn_proto.ast.addrspace_expr == 0) .none else inst: { - break :inst try expr(&decl_gz, params_scope, .{ .ty = .address_space_type }, fn_proto.ast.addrspace_expr); - }; - const section_inst: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: { - break :inst try comptimeExpr(&decl_gz, params_scope, .{ .ty = .const_slice_u8_type }, fn_proto.ast.section_expr); + // After creating the function ZIR instruction, it will need to update the break + // instructions inside the expression blocks for align, addrspace, cc, and ret_ty + // to use the function instruction as the "block" to break from. + + var align_gz = decl_gz.makeSubBlock(params_scope); + defer align_gz.unstack(); + const align_ref: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: { + const inst = try expr(&decl_gz, params_scope, coerced_align_rl, fn_proto.ast.align_expr); + if (align_gz.instructionsSlice().len == 0) { + // In this case we will send a len=0 body which can be encoded more efficiently. + break :inst inst; + } + _ = try align_gz.addBreak(.break_inline, 0, inst); + break :inst inst; }; - const cc: Zir.Inst.Ref = blk: { + var addrspace_gz = decl_gz.makeSubBlock(params_scope); + defer addrspace_gz.unstack(); + const addrspace_ref: Zir.Inst.Ref = if (fn_proto.ast.addrspace_expr == 0) .none else inst: { + const inst = try expr(&decl_gz, params_scope, .{ .coerced_ty = .address_space_type }, fn_proto.ast.addrspace_expr); + if (addrspace_gz.instructionsSlice().len == 0) { + // In this case we will send a len=0 body which can be encoded more efficiently. + break :inst inst; + } + _ = try addrspace_gz.addBreak(.break_inline, 0, inst); + break :inst inst; + }; + + var section_gz = decl_gz.makeSubBlock(params_scope); + defer section_gz.unstack(); + const section_ref: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: { + const inst = try expr(&decl_gz, params_scope, .{ .coerced_ty = .const_slice_u8_type }, fn_proto.ast.section_expr); + if (section_gz.instructionsSlice().len == 0) { + // In this case we will send a len=0 body which can be encoded more efficiently. + break :inst inst; + } + _ = try section_gz.addBreak(.break_inline, 0, inst); + break :inst inst; + }; + + var cc_gz = decl_gz.makeSubBlock(params_scope); + defer cc_gz.unstack(); + const cc_ref: Zir.Inst.Ref = blk: { if (fn_proto.ast.callconv_expr != 0) { if (has_inline_keyword) { return astgen.failNode( @@ -3502,12 +3538,18 @@ fn fnDecl( .{}, ); } - break :blk try expr( + const inst = try expr( &decl_gz, params_scope, - .{ .ty = .calling_convention_type }, + .{ .coerced_ty = .calling_convention_type }, fn_proto.ast.callconv_expr, ); + if (cc_gz.instructionsSlice().len == 0) { + // In this case we will send a len=0 body which can be encoded more efficiently. + break :blk inst; + } + _ = try cc_gz.addBreak(.break_inline, 0, inst); + break :blk inst; } else if (is_extern) { // note: https://github.com/ziglang/zig/issues/5269 break :blk .calling_convention_c; @@ -3520,8 +3562,18 @@ fn fnDecl( var ret_gz = decl_gz.makeSubBlock(params_scope); defer ret_gz.unstack(); - const ret_ty = try expr(&ret_gz, params_scope, coerced_type_rl, fn_proto.ast.return_type); - const ret_br = try ret_gz.addBreak(.break_inline, 0, ret_ty); + const ret_ref: Zir.Inst.Ref = switch (nodePrimitive(tree, fn_proto.ast.return_type)) { + .none => inst: { + const inst = try expr(&ret_gz, params_scope, coerced_type_rl, fn_proto.ast.return_type); + if (ret_gz.instructionsSlice().len == 0) { + // In this case we will send a len=0 body which can be encoded more efficiently. + break :inst inst; + } + _ = try ret_gz.addBreak(.break_inline, 0, inst); + break :inst inst; + }, + else => |p| p, + }; const func_inst: Zir.Inst.Ref = if (body_node == 0) func: { if (!is_extern) { @@ -3532,12 +3584,18 @@ fn fnDecl( } break :func try decl_gz.addFunc(.{ .src_node = decl_node, + .cc_ref = cc_ref, + .cc_gz = &cc_gz, + .align_ref = align_ref, + .align_gz = &align_gz, + .ret_ref = ret_ref, .ret_gz = &ret_gz, - .ret_br = ret_br, + .section_ref = section_ref, + .section_gz = §ion_gz, + .addrspace_ref = addrspace_ref, + .addrspace_gz = &addrspace_gz, .param_block = block_inst, .body_gz = null, - .cc = cc, - .align_inst = align_inst, .lib_name = lib_name, .is_var_args = is_var_args, .is_inferred_error = false, @@ -3571,14 +3629,20 @@ fn fnDecl( break :func try decl_gz.addFunc(.{ .src_node = decl_node, + .cc_ref = cc_ref, + .cc_gz = &cc_gz, + .align_ref = align_ref, + .align_gz = &align_gz, + .ret_ref = ret_ref, + .ret_gz = &ret_gz, + .section_ref = section_ref, + .section_gz = §ion_gz, + .addrspace_ref = addrspace_ref, + .addrspace_gz = &addrspace_gz, .lbrace_line = lbrace_line, .lbrace_column = lbrace_column, .param_block = block_inst, - .ret_gz = &ret_gz, - .ret_br = ret_br, .body_gz = &fn_gz, - .cc = cc, - .align_inst = align_inst, .lib_name = lib_name, .is_var_args = is_var_args, .is_inferred_error = is_inferred_error, @@ -3604,10 +3668,6 @@ fn fnDecl( wip_members.appendToDecl(fn_name_str_index); wip_members.appendToDecl(block_inst); wip_members.appendToDecl(doc_comment_index); - if (has_section_or_addrspace) { - wip_members.appendToDecl(@enumToInt(section_inst)); - wip_members.appendToDecl(@enumToInt(addrspace_inst)); - } } fn globalVarDecl( @@ -4001,14 +4061,22 @@ fn testDecl( const func_inst = try decl_block.addFunc(.{ .src_node = node, + + .cc_ref = .none, + .cc_gz = null, + .align_ref = .none, + .align_gz = null, + .ret_ref = .void_type, + .ret_gz = null, + .section_ref = .none, + .section_gz = null, + .addrspace_ref = .none, + .addrspace_gz = null, + .lbrace_line = lbrace_line, .lbrace_column = lbrace_column, .param_block = block_inst, - .ret_gz = null, - .ret_br = 0, .body_gz = &fn_block, - .cc = .none, - .align_inst = .none, .lib_name = 0, .is_var_args = false, .is_inferred_error = true, @@ -8957,6 +9025,31 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool { } } +fn nodePrimitive(tree: *const Ast, start_node: Ast.Node.Index) Zir.Inst.Ref { + const node_tags = tree.nodes.items(.tag); + const node_datas = tree.nodes.items(.data); + + var node = start_node; + while (true) { + switch (node_tags[node]) { + // Forward the question to the LHS sub-expression. + .grouped_expression => node = node_datas[node].lhs, + + .identifier => { + const main_tokens = tree.nodes.items(.main_token); + const ident_bytes = tree.tokenSlice(main_tokens[node]); + if (primitives.get(ident_bytes)) |primitive| { + return primitive; + } else { + return .none; + } + }, + + else => return .none, + } + } +} + /// Applies `rl` semantics to `result`. Expressions which do not do their own handling of /// result locations must call this function on their result. /// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer. @@ -9952,17 +10045,34 @@ const GenZir = struct { gz.unstack(); } - /// Supports `body_gz` stacked on `ret_gz` stacked on `gz`. Unstacks `body_gz` and `ret_gz`. + /// Must be called with the following stack set up: + /// * gz (bottom) + /// * align_gz + /// * addrspace_gz + /// * section_gz + /// * cc_gz + /// * ret_gz + /// * body_gz (top) + /// Unstacks all of those except for `gz`. fn addFunc(gz: *GenZir, args: struct { src_node: Ast.Node.Index, lbrace_line: u32 = 0, lbrace_column: u32 = 0, - body_gz: ?*GenZir, param_block: Zir.Inst.Index, + + align_gz: ?*GenZir, + addrspace_gz: ?*GenZir, + section_gz: ?*GenZir, + cc_gz: ?*GenZir, ret_gz: ?*GenZir, - ret_br: Zir.Inst.Index, - cc: Zir.Inst.Ref, - align_inst: Zir.Inst.Ref, + body_gz: ?*GenZir, + + align_ref: Zir.Inst.Ref, + addrspace_ref: Zir.Inst.Ref, + section_ref: Zir.Inst.Ref, + cc_ref: Zir.Inst.Ref, + ret_ref: Zir.Inst.Ref, + lib_name: u32, is_var_args: bool, is_inferred_error: bool, @@ -9972,11 +10082,13 @@ const GenZir = struct { assert(args.src_node != 0); const astgen = gz.astgen; const gpa = astgen.gpa; + const ret_ref = if (args.ret_ref == .void_type) .none else args.ret_ref; + const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len); try astgen.instructions.ensureUnusedCapacity(gpa, 1); var body: []Zir.Inst.Index = &[0]Zir.Inst.Index{}; - var ret_ty: []Zir.Inst.Index = &[0]Zir.Inst.Index{}; + var ret_body: []Zir.Inst.Index = &[0]Zir.Inst.Index{}; var src_locs_buffer: [3]u32 = undefined; var src_locs: []u32 = src_locs_buffer[0..0]; if (args.body_gz) |body_gz| { @@ -10000,61 +10112,120 @@ const GenZir = struct { body = body_gz.instructionsSlice(); if (args.ret_gz) |ret_gz| - ret_ty = ret_gz.instructionsSliceUpto(body_gz); + ret_body = ret_gz.instructionsSliceUpto(body_gz); } else { if (args.ret_gz) |ret_gz| - ret_ty = ret_gz.instructionsSlice(); + ret_body = ret_gz.instructionsSlice(); } - if (args.cc != .none or args.lib_name != 0 or - args.is_var_args or args.is_test or args.align_inst != .none or - args.is_extern) + if (args.cc_ref != .none or args.lib_name != 0 or + args.is_var_args or args.is_test or args.is_extern or + args.align_ref != .none or args.section_ref != .none or + args.addrspace_ref != .none) { + var align_body: []Zir.Inst.Index = &.{}; + var addrspace_body: []Zir.Inst.Index = &.{}; + var section_body: []Zir.Inst.Index = &.{}; + var cc_body: []Zir.Inst.Index = &.{}; + if (args.ret_gz != null) { + align_body = args.align_gz.?.instructionsSliceUpto(args.addrspace_gz.?); + addrspace_body = args.addrspace_gz.?.instructionsSliceUpto(args.section_gz.?); + section_body = args.section_gz.?.instructionsSliceUpto(args.cc_gz.?); + cc_body = args.cc_gz.?.instructionsSliceUpto(args.ret_gz.?); + } + try astgen.extra.ensureUnusedCapacity( gpa, - @typeInfo(Zir.Inst.ExtendedFunc).Struct.fields.len + - ret_ty.len + body.len + src_locs.len + - @boolToInt(args.lib_name != 0) + - @boolToInt(args.align_inst != .none) + - @boolToInt(args.cc != .none), + @typeInfo(Zir.Inst.FuncFancy).Struct.fields.len + + fancyFnExprExtraLen(align_body, args.align_ref) + + fancyFnExprExtraLen(addrspace_body, args.addrspace_ref) + + fancyFnExprExtraLen(section_body, args.section_ref) + + fancyFnExprExtraLen(cc_body, args.cc_ref) + + fancyFnExprExtraLen(ret_body, ret_ref) + + body.len + src_locs.len + + @boolToInt(args.lib_name != 0), ); - const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedFunc{ + const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{ .param_block = args.param_block, - .ret_body_len = @intCast(u32, ret_ty.len), .body_len = @intCast(u32, body.len), .bits = .{ .is_var_args = args.is_var_args, .is_inferred_error = args.is_inferred_error, - .has_lib_name = args.lib_name != 0, - .has_cc = args.cc != .none, - .has_align = args.align_inst != .none, .is_test = args.is_test, .is_extern = args.is_extern, + .has_lib_name = args.lib_name != 0, + + .has_align_ref = args.align_ref != .none, + .has_addrspace_ref = args.addrspace_ref != .none, + .has_section_ref = args.section_ref != .none, + .has_cc_ref = args.cc_ref != .none, + .has_ret_ty_ref = ret_ref != .none, + + .has_align_body = align_body.len != 0, + .has_addrspace_body = addrspace_body.len != 0, + .has_section_body = section_body.len != 0, + .has_cc_body = cc_body.len != 0, + .has_ret_ty_body = ret_body.len != 0, }, }); if (args.lib_name != 0) { astgen.extra.appendAssumeCapacity(args.lib_name); } - if (args.cc != .none) { - astgen.extra.appendAssumeCapacity(@enumToInt(args.cc)); + + const zir_datas = astgen.instructions.items(.data); + if (align_body.len != 0) { + astgen.extra.appendAssumeCapacity(@intCast(u32, align_body.len)); + astgen.extra.appendSliceAssumeCapacity(align_body); + zir_datas[align_body[align_body.len - 1]].@"break".block_inst = new_index; + } else if (args.align_ref != .none) { + astgen.extra.appendAssumeCapacity(@enumToInt(args.align_ref)); } - if (args.align_inst != .none) { - astgen.extra.appendAssumeCapacity(@enumToInt(args.align_inst)); + if (addrspace_body.len != 0) { + astgen.extra.appendAssumeCapacity(@intCast(u32, addrspace_body.len)); + astgen.extra.appendSliceAssumeCapacity(addrspace_body); + zir_datas[addrspace_body[addrspace_body.len - 1]].@"break".block_inst = new_index; + } else if (args.addrspace_ref != .none) { + astgen.extra.appendAssumeCapacity(@enumToInt(args.addrspace_ref)); } - astgen.extra.appendSliceAssumeCapacity(ret_ty); + if (section_body.len != 0) { + astgen.extra.appendAssumeCapacity(@intCast(u32, section_body.len)); + astgen.extra.appendSliceAssumeCapacity(section_body); + zir_datas[section_body[section_body.len - 1]].@"break".block_inst = new_index; + } else if (args.section_ref != .none) { + astgen.extra.appendAssumeCapacity(@enumToInt(args.section_ref)); + } + if (cc_body.len != 0) { + astgen.extra.appendAssumeCapacity(@intCast(u32, cc_body.len)); + astgen.extra.appendSliceAssumeCapacity(cc_body); + zir_datas[cc_body[cc_body.len - 1]].@"break".block_inst = new_index; + } else if (args.cc_ref != .none) { + astgen.extra.appendAssumeCapacity(@enumToInt(args.cc_ref)); + } + if (ret_body.len != 0) { + astgen.extra.appendAssumeCapacity(@intCast(u32, ret_body.len)); + astgen.extra.appendSliceAssumeCapacity(ret_body); + zir_datas[ret_body[ret_body.len - 1]].@"break".block_inst = new_index; + } else if (ret_ref != .none) { + astgen.extra.appendAssumeCapacity(@enumToInt(ret_ref)); + } + astgen.extra.appendSliceAssumeCapacity(body); astgen.extra.appendSliceAssumeCapacity(src_locs); - // order is important when unstacking + + // Order is important when unstacking. if (args.body_gz) |body_gz| body_gz.unstack(); - if (args.ret_gz) |ret_gz| ret_gz.unstack(); + if (args.ret_gz != null) { + args.ret_gz.?.unstack(); + args.cc_gz.?.unstack(); + args.section_gz.?.unstack(); + args.addrspace_gz.?.unstack(); + args.align_gz.?.unstack(); + } + try gz.instructions.ensureUnusedCapacity(gpa, 1); - const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len); - if (args.ret_br != 0) { - astgen.instructions.items(.data)[args.ret_br].@"break".block_inst = new_index; - } astgen.instructions.appendAssumeCapacity(.{ - .tag = .func_extended, + .tag = .func_fancy, .data = .{ .pl_node = .{ .src_node = gz.nodeIndexToRelative(args.src_node), .payload_index = payload_index, @@ -10066,27 +10237,40 @@ const GenZir = struct { try astgen.extra.ensureUnusedCapacity( gpa, @typeInfo(Zir.Inst.Func).Struct.fields.len + - ret_ty.len + body.len + src_locs.len, + @maximum(ret_body.len, @boolToInt(ret_ref != .none)) + + body.len + src_locs.len, ); + const ret_body_len = if (ret_body.len != 0) + @intCast(u32, ret_body.len) + else + @boolToInt(ret_ref != .none); const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.Func{ .param_block = args.param_block, - .ret_body_len = @intCast(u32, ret_ty.len), + .ret_body_len = ret_body_len, .body_len = @intCast(u32, body.len), }); - astgen.extra.appendSliceAssumeCapacity(ret_ty); + const zir_datas = astgen.instructions.items(.data); + if (ret_body.len != 0) { + astgen.extra.appendSliceAssumeCapacity(ret_body); + zir_datas[ret_body[ret_body.len - 1]].@"break".block_inst = new_index; + } else if (ret_ref != .none) { + astgen.extra.appendAssumeCapacity(@enumToInt(ret_ref)); + } astgen.extra.appendSliceAssumeCapacity(body); astgen.extra.appendSliceAssumeCapacity(src_locs); - // order is important when unstacking + + // Order is important when unstacking. if (args.body_gz) |body_gz| body_gz.unstack(); if (args.ret_gz) |ret_gz| ret_gz.unstack(); + if (args.cc_gz) |cc_gz| cc_gz.unstack(); + if (args.section_gz) |section_gz| section_gz.unstack(); + if (args.addrspace_gz) |addrspace_gz| addrspace_gz.unstack(); + if (args.align_gz) |align_gz| align_gz.unstack(); + try gz.instructions.ensureUnusedCapacity(gpa, 1); const tag: Zir.Inst.Tag = if (args.is_inferred_error) .func_inferred else .func; - const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len); - if (args.ret_br != 0) { - astgen.instructions.items(.data)[args.ret_br].@"break".block_inst = new_index; - } astgen.instructions.appendAssumeCapacity(.{ .tag = tag, .data = .{ .pl_node = .{ @@ -10099,6 +10283,12 @@ const GenZir = struct { } } + fn fancyFnExprExtraLen(body: []Zir.Inst.Index, ref: Zir.Inst.Ref) usize { + // In the case of non-empty body, there is one for the body length, + // and then one for each instruction. + return body.len + @boolToInt(ref != .none); + } + fn addVar(gz: *GenZir, args: struct { align_inst: Zir.Inst.Ref, lib_name: u32, diff --git a/src/Module.zig b/src/Module.zig index 7c19c4dab6..f8d662ae1f 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1595,9 +1595,9 @@ pub const Fn = struct { switch (zir_tags[func.zir_body_inst]) { .func => return false, .func_inferred => return true, - .func_extended => { + .func_fancy => { const inst_data = zir.instructions.items(.data)[func.zir_body_inst].pl_node; - const extra = zir.extraData(Zir.Inst.ExtendedFunc, inst_data.payload_index); + const extra = zir.extraData(Zir.Inst.FuncFancy, inst_data.payload_index); return extra.data.bits.is_inferred_error; }, else => unreachable, diff --git a/src/Sema.zig b/src/Sema.zig index e0310e5ad7..bf8e10b1a4 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -747,7 +747,7 @@ fn analyzeBodyInner( .field_call_bind => try sema.zirFieldCallBind(block, inst), .func => try sema.zirFunc(block, inst, false), .func_inferred => try sema.zirFunc(block, inst, true), - .func_extended => try sema.zirFuncExtended(block, inst), + .func_fancy => try sema.zirFuncFancy(block, inst), .import => try sema.zirImport(block, inst), .indexable_ptr_len => try sema.zirIndexablePtrLen(block, inst), .int => try sema.zirInt(block, inst), @@ -5186,7 +5186,10 @@ fn analyzeCall( // on parameters, we must now do the same for the return type as we just did with // each of the parameters, resolving the return type and providing it to the child // `Sema` so that it can be used for the `ret_ptr` instruction. - const ret_ty_inst = try sema.resolveBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst); + const ret_ty_inst = if (fn_info.ret_ty_body.len != 0) + try sema.resolveBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst) + else + try sema.resolveInst(fn_info.ret_ty_ref); const ret_ty_src = func_src; // TODO better source location const bare_return_type = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst); // Create a fresh inferred error set type for inline/comptime calls. @@ -6497,9 +6500,34 @@ fn zirFunc( const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index); + const target = sema.mod.getTarget(); + const ret_ty_src = inst_data.src(); // TODO better source location + var extra_index = extra.end; - const ret_ty_body = sema.code.extra[extra_index..][0..extra.data.ret_body_len]; - extra_index += ret_ty_body.len; + + const ret_ty: Type = switch (extra.data.ret_body_len) { + 0 => Type.void, + 1 => blk: { + const ret_ty_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); + extra_index += 1; + if (sema.resolveType(block, ret_ty_src, ret_ty_ref)) |ret_ty| { + break :blk ret_ty; + } else |err| switch (err) { + error.GenericPoison => { + break :blk Type.initTag(.generic_poison); + }, + else => |e| return e, + } + }, + else => blk: { + const ret_ty_body = sema.code.extra[extra_index..][0..extra.data.ret_body_len]; + extra_index += ret_ty_body.len; + + const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, Type.type); + var buffer: Value.ToTypeBuffer = undefined; + break :blk try ret_ty_val.toType(&buffer).copy(sema.arena); + }, + }; var src_locs: Zir.Inst.Func.SrcLocs = undefined; const has_body = extra.data.body_len != 0; @@ -6517,9 +6545,11 @@ fn zirFunc( block, inst_data.src_node, inst, - ret_ty_body, + 0, + target_util.defaultAddressSpace(target, .function), + null, cc, - Value.@"null", + ret_ty, false, inferred_error_set, false, @@ -6529,6 +6559,44 @@ fn zirFunc( ); } +// TODO this function and its callsites along with funcCommon need to be reworked +// to handle when callconv, align, linksection, addrspace depend on comptime values +// (thus triggering error.GenericPoison) +fn resolveGenericBody( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + body: []const Zir.Inst.Index, + func_inst: Zir.Inst.Index, + dest_ty: Type, +) !Value { + assert(body.len != 0); + + const err = err: { + // Make sure any nested param instructions don't clobber our work. + const prev_params = block.params; + block.params = .{}; + defer { + block.params.deinit(sema.gpa); + block.params = prev_params; + } + const uncasted = sema.resolveBody(block, body, func_inst) catch |err| break :err err; + const result = sema.coerce(block, dest_ty, uncasted, src) catch |err| break :err err; + const val = sema.resolveConstValue(block, src, result) catch |err| break :err err; + return val; + }; + switch (err) { + error.GenericPoison => { + if (dest_ty.tag() == .type) { + return Value.initTag(.generic_poison_type); + } else { + return Value.initTag(.generic_poison); + } + }, + else => |e| return e, + } +} + /// Given a library name, examines if the library name should end up in /// `link.File.Options.system_libs` table (for example, libc is always /// specified via dedicated flag `link.File.Options.link_libc` instead), @@ -6597,9 +6665,11 @@ fn funcCommon( block: *Block, src_node_offset: i32, func_inst: Zir.Inst.Index, - ret_ty_body: []const Zir.Inst.Index, + alignment: u32, + address_space: std.builtin.AddressSpace, + section: ?[*:0]const u8, cc: std.builtin.CallingConvention, - align_val: Value, + bare_return_type: Type, var_args: bool, inferred_error_set: bool, is_extern: bool, @@ -6609,42 +6679,11 @@ fn funcCommon( ) CompileError!Air.Inst.Ref { const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; - // The return type body might be a type expression that depends on generic parameters. - // In such case we need to use a generic_poison value for the return type and mark - // the function as generic. - var is_generic = false; - const bare_return_type: Type = ret_ty: { - if (ret_ty_body.len == 0) break :ret_ty Type.void; - - const err = err: { - // Make sure any nested param instructions don't clobber our work. - const prev_params = block.params; - block.params = .{}; - defer { - block.params.deinit(sema.gpa); - block.params = prev_params; - } - if (sema.resolveBody(block, ret_ty_body, func_inst)) |ret_ty_inst| { - if (sema.analyzeAsType(block, ret_ty_src, ret_ty_inst)) |ret_ty| { - break :ret_ty ret_ty; - } else |err| break :err err; - } else |err| break :err err; - // Check for generic params. - for (block.params.items) |param| { - if (param.ty.tag() == .generic_poison) is_generic = true; - } - }; - switch (err) { - error.GenericPoison => { - // The type is not available until the generic instantiation. - is_generic = true; - break :ret_ty Type.initTag(.generic_poison); - }, - else => |e| return e, - } - }; - - const mod = sema.mod; + var is_generic = bare_return_type.tag() == .generic_poison; + // Check for generic params. + for (block.params.items) |param| { + if (param.ty.tag() == .generic_poison) is_generic = true; + } const new_func: *Module.Fn = new_func: { if (!has_body) break :new_func undefined; @@ -6661,18 +6700,7 @@ fn funcCommon( errdefer if (maybe_inferred_error_set_node) |node| sema.gpa.destroy(node); // Note: no need to errdefer since this will still be in its default state at the end of the function. - const target = mod.getTarget(); - const fn_ty: Type = fn_ty: { - const alignment: u32 = if (align_val.tag() == .null_value) 0 else a: { - const alignment = @intCast(u32, align_val.toUnsignedInt(target)); - if (alignment == target_util.defaultFunctionAlignment(target)) { - break :a 0; - } else { - break :a alignment; - } - }; - // Hot path for some common function types. // TODO can we eliminate some of these Type tag values? seems unnecessarily complicated. if (!is_generic and block.params.items.len == 0 and !var_args and @@ -6747,6 +6775,12 @@ fn funcCommon( }); }; + if (sema.owner_decl.owns_tv) { + sema.owner_decl.@"linksection" = section; + sema.owner_decl.@"align" = alignment; + sema.owner_decl.@"addrspace" = address_space; + } + if (is_extern) { const new_extern_fn = try sema.gpa.create(Module.ExternFn); errdefer sema.gpa.destroy(new_extern_fn); @@ -16723,16 +16757,20 @@ fn zirVarExtended( return result; } -fn zirFuncExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { +fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); - const extra = sema.code.extraData(Zir.Inst.ExtendedFunc, inst_data.payload_index); + const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index); + const target = sema.mod.getTarget(); - const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node }; const align_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at align + const addrspace_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at addrspace + const section_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at section + const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node }; + const ret_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at the return type var extra_index: usize = extra.end; @@ -16742,22 +16780,103 @@ fn zirFuncExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro break :blk lib_name; } else null; - const cc: std.builtin.CallingConvention = if (extra.data.bits.has_cc) blk: { + const @"align": u32 = if (extra.data.bits.has_align_body) blk: { + const body_len = sema.code.extra[extra_index]; + extra_index += 1; + const body = sema.code.extra[extra_index..][0..body_len]; + extra_index += body.len; + + const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u16); + const alignment = @intCast(u32, val.toUnsignedInt(target)); + if (alignment == target_util.defaultFunctionAlignment(target)) { + break :blk 0; + } else { + break :blk alignment; + } + } else if (extra.data.bits.has_align_ref) blk: { + const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); + extra_index += 1; + const align_tv = try sema.resolveInstConst(block, align_src, align_ref); + const alignment = @intCast(u32, align_tv.val.toUnsignedInt(target)); + if (alignment == target_util.defaultFunctionAlignment(target)) { + break :blk 0; + } else { + break :blk alignment; + } + } else 0; + + const @"addrspace": std.builtin.AddressSpace = if (extra.data.bits.has_addrspace_body) blk: { + const body_len = sema.code.extra[extra_index]; + extra_index += 1; + const body = sema.code.extra[extra_index..][0..body_len]; + extra_index += body.len; + + const addrspace_ty = try sema.getBuiltinType(block, addrspace_src, "AddressSpace"); + const val = try sema.resolveGenericBody(block, addrspace_src, body, inst, addrspace_ty); + break :blk val.toEnum(std.builtin.AddressSpace); + } else if (extra.data.bits.has_addrspace_ref) blk: { + const addrspace_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); + extra_index += 1; + const addrspace_tv = try sema.resolveInstConst(block, addrspace_src, addrspace_ref); + break :blk addrspace_tv.val.toEnum(std.builtin.AddressSpace); + } else target_util.defaultAddressSpace(target, .function); + + const @"linksection": ?[*:0]const u8 = if (extra.data.bits.has_section_body) { + const body_len = sema.code.extra[extra_index]; + extra_index += 1; + const body = sema.code.extra[extra_index..][0..body_len]; + extra_index += body.len; + + const val = try sema.resolveGenericBody(block, section_src, body, inst, Type.initTag(.const_slice_u8)); + _ = val; + return sema.fail(block, section_src, "TODO implement linksection on functions", .{}); + } else if (extra.data.bits.has_section_ref) { + const section_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); + extra_index += 1; + const section_tv = try sema.resolveInstConst(block, section_src, section_ref); + _ = section_tv; + return sema.fail(block, section_src, "TODO implement linksection on functions", .{}); + } else null; + + const cc: std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: { + const body_len = sema.code.extra[extra_index]; + extra_index += 1; + const body = sema.code.extra[extra_index..][0..body_len]; + extra_index += body.len; + + const cc_ty = try sema.getBuiltinType(block, addrspace_src, "CallingConvention"); + const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty); + break :blk val.toEnum(std.builtin.CallingConvention); + } else if (extra.data.bits.has_cc_ref) blk: { const cc_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; const cc_tv = try sema.resolveInstConst(block, cc_src, cc_ref); break :blk cc_tv.val.toEnum(std.builtin.CallingConvention); } else .Unspecified; - const align_val: Value = if (extra.data.bits.has_align) blk: { - const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); + const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: { + const body_len = sema.code.extra[extra_index]; extra_index += 1; - const align_tv = try sema.resolveInstConst(block, align_src, align_ref); - break :blk align_tv.val; - } else Value.@"null"; + const body = sema.code.extra[extra_index..][0..body_len]; + extra_index += body.len; - const ret_ty_body = sema.code.extra[extra_index..][0..extra.data.ret_body_len]; - extra_index += ret_ty_body.len; + const val = try sema.resolveGenericBody(block, ret_src, body, inst, Type.type); + var buffer: Value.ToTypeBuffer = undefined; + const ty = try val.toType(&buffer).copy(sema.arena); + break :blk ty; + } else if (extra.data.bits.has_ret_ty_ref) blk: { + const ret_ty_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); + extra_index += 1; + const ret_ty_tv = sema.resolveInstConst(block, ret_src, ret_ty_ref) catch |err| switch (err) { + error.GenericPoison => { + break :blk Type.initTag(.generic_poison); + }, + else => |e| return e, + }; + var buffer: Value.ToTypeBuffer = undefined; + const ty = try ret_ty_tv.val.toType(&buffer).copy(sema.arena); + break :blk ty; + } else Type.void; var src_locs: Zir.Inst.Func.SrcLocs = undefined; const has_body = extra.data.body_len != 0; @@ -16774,9 +16893,11 @@ fn zirFuncExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro block, inst_data.src_node, inst, - ret_ty_body, + @"align", + @"addrspace", + @"linksection", cc, - align_val, + ret_ty, is_var_args, is_inferred_error, is_extern, diff --git a/src/Zir.zig b/src/Zir.zig index c722457303..58ca443661 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -74,7 +74,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, en Inst.Call.Flags => @bitCast(Inst.Call.Flags, code.extra[i]), Inst.BuiltinCall.Flags => @bitCast(Inst.BuiltinCall.Flags, code.extra[i]), Inst.SwitchBlock.Bits => @bitCast(Inst.SwitchBlock.Bits, code.extra[i]), - Inst.ExtendedFunc.Bits => @bitCast(Inst.ExtendedFunc.Bits, code.extra[i]), + Inst.FuncFancy.Bits => @bitCast(Inst.FuncFancy.Bits, code.extra[i]), else => @compileError("bad field type"), }; i += 1; @@ -424,8 +424,8 @@ pub const Inst = struct { func_inferred, /// Represents a function declaration or function prototype, depending on /// whether body_len is 0. - /// Uses the `pl_node` union field. `payload_index` points to a `ExtendedFunc`. - func_extended, + /// Uses the `pl_node` union field. `payload_index` points to a `FuncFancy`. + func_fancy, /// Implements the `@import` builtin. /// Uses the `str_tok` field. import, @@ -1070,7 +1070,7 @@ pub const Inst = struct { .field_val_named, .func, .func_inferred, - .func_extended, + .func_fancy, .has_decl, .int, .int_big, @@ -1356,7 +1356,7 @@ pub const Inst = struct { .field_val_named, .func, .func_inferred, - .func_extended, + .func_fancy, .has_decl, .int, .int_big, @@ -1611,7 +1611,7 @@ pub const Inst = struct { .field_call_bind = .pl_node, .func = .pl_node, .func_inferred = .pl_node, - .func_extended = .pl_node, + .func_fancy = .pl_node, .import = .str_tok, .int = .int, .int_big = .str, @@ -2620,29 +2620,100 @@ pub const Inst = struct { }; /// Trailing: - /// 0. lib_name: u32, // null terminated string index, if has_lib_name is set - /// 1. cc: Ref, // if has_cc is set - /// 2. align: Ref, // if has_align is set - /// 3. return_type: Index // for each ret_body_len - /// 4. body: Index // for each body_len - /// 5. src_locs: Func.SrcLocs // if body_len != 0 - pub const ExtendedFunc = struct { + /// if (ret_body_len == 1) { + /// 0. return_type: Ref + /// } + /// if (ret_body_len > 1) { + /// 1. return_type: Index // for each ret_body_len + /// } + /// 2. body: Index // for each body_len + /// 3. src_locs: SrcLocs // if body_len != 0 + pub const Func = struct { /// If this is 0 it means a void return type. + /// If this is 1 it means return_type is a simple Ref ret_body_len: u32, /// Points to the block that contains the param instructions for this function. param_block: Index, body_len: u32, + + pub const SrcLocs = struct { + /// Line index in the source file relative to the parent decl. + lbrace_line: u32, + /// Line index in the source file relative to the parent decl. + rbrace_line: u32, + /// lbrace_column is least significant bits u16 + /// rbrace_column is most significant bits u16 + columns: u32, + }; + }; + + /// Trailing: + /// 0. lib_name: u32, // null terminated string index, if has_lib_name is set + /// if (has_align_ref and !has_align_body) { + /// 1. align: Ref, + /// } + /// if (has_align_body) { + /// 2. align_body_len: u32 + /// 3. align_body: u32 // for each align_body_len + /// } + /// if (has_addrspace_ref and !has_addrspace_body) { + /// 4. addrspace: Ref, + /// } + /// if (has_addrspace_body) { + /// 5. addrspace_body_len: u32 + /// 6. addrspace_body: u32 // for each addrspace_body_len + /// } + /// if (has_section_ref and !has_section_body) { + /// 7. section: Ref, + /// } + /// if (has_section_body) { + /// 8. section_body_len: u32 + /// 9. section_body: u32 // for each section_body_len + /// } + /// if (has_cc_ref and !has_cc_body) { + /// 10. cc: Ref, + /// } + /// if (has_cc_body) { + /// 11. cc_body_len: u32 + /// 12. cc_body: u32 // for each cc_body_len + /// } + /// if (has_ret_ty_ref and !has_ret_ty_body) { + /// 13. ret_ty: Ref, + /// } + /// if (has_ret_ty_body) { + /// 14. ret_ty_body_len: u32 + /// 15. ret_ty_body: u32 // for each ret_ty_body_len + /// } + /// 16. body: Index // for each body_len + /// 17. src_locs: Func.SrcLocs // if body_len != 0 + pub const FuncFancy = struct { + /// Points to the block that contains the param instructions for this function. + param_block: Index, + body_len: u32, bits: Bits, + /// If both has_cc_ref and has_cc_body are false, it means auto calling convention. + /// If both has_align_ref and has_align_body are false, it means default alignment. + /// If both has_ret_ty_ref and has_ret_ty_body are false, it means void return type. + /// If both has_section_ref and has_section_body are false, it means default section. + /// If both has_addrspace_ref and has_addrspace_body are false, it means default addrspace. pub const Bits = packed struct { is_var_args: bool, is_inferred_error: bool, - has_lib_name: bool, - has_cc: bool, - has_align: bool, is_test: bool, is_extern: bool, - _: u25 = undefined, + has_align_ref: bool, + has_align_body: bool, + has_addrspace_ref: bool, + has_addrspace_body: bool, + has_section_ref: bool, + has_section_body: bool, + has_cc_ref: bool, + has_cc_body: bool, + has_ret_ty_ref: bool, + has_ret_ty_body: bool, + has_lib_name: bool, + _: u17 = undefined, }; }; @@ -2664,28 +2735,6 @@ pub const Inst = struct { }; }; - /// Trailing: - /// 0. return_type: Index // for each ret_body_len - /// 1. body: Index // for each body_len - /// 2. src_locs: SrcLocs // if body_len != 0 - pub const Func = struct { - /// If this is 0 it means a void return type. - ret_body_len: u32, - /// Points to the block that contains the param instructions for this function. - param_block: Index, - body_len: u32, - - pub const SrcLocs = struct { - /// Line index in the source file relative to the parent decl. - lbrace_line: u32, - /// Line index in the source file relative to the parent decl. - rbrace_line: u32, - /// lbrace_column is least significant bits u16 - /// rbrace_column is most significant bits u16 - columns: u32, - }; - }; - /// This data is stored inside extra, with trailing operands according to `operands_len`. /// Each operand is a `Ref`. pub const MultiOp = struct { @@ -3487,7 +3536,7 @@ pub fn declIterator(zir: Zir, decl_inst: u32) DeclIterator { switch (tags[decl_inst]) { // Functions are allowed and yield no iterations. // There is one case matching this in the extended instruction set below. - .func, .func_inferred, .func_extended => return declIteratorInner(zir, 0, 0), + .func, .func_inferred, .func_fancy => return declIteratorInner(zir, 0, 0), .extended => { const extended = datas[decl_inst].extended; @@ -3593,18 +3642,77 @@ fn findDeclsInner( const inst_data = datas[inst].pl_node; const extra = zir.extraData(Inst.Func, inst_data.payload_index); - const body = zir.extra[extra.end..][0..extra.data.body_len]; + var extra_index: usize = extra.end; + switch (extra.data.ret_body_len) { + 0 => {}, + 1 => extra_index += 1, + else => { + const body = zir.extra[extra_index..][0..extra.data.ret_body_len]; + extra_index += body.len; + try zir.findDeclsBody(list, body); + }, + } + const body = zir.extra[extra_index..][0..extra.data.body_len]; return zir.findDeclsBody(list, body); }, - .func_extended => { + .func_fancy => { try list.append(inst); const inst_data = datas[inst].pl_node; - const extra = zir.extraData(Inst.ExtendedFunc, inst_data.payload_index); + const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index); var extra_index: usize = extra.end; extra_index += @boolToInt(extra.data.bits.has_lib_name); - extra_index += @boolToInt(extra.data.bits.has_cc); - extra_index += @boolToInt(extra.data.bits.has_align); + + if (extra.data.bits.has_align_body) { + const body_len = zir.extra[extra_index]; + extra_index += 1; + const body = zir.extra[extra_index..][0..body_len]; + try zir.findDeclsBody(list, body); + extra_index += body.len; + } else if (extra.data.bits.has_align_ref) { + extra_index += 1; + } + + if (extra.data.bits.has_addrspace_body) { + const body_len = zir.extra[extra_index]; + extra_index += 1; + const body = zir.extra[extra_index..][0..body_len]; + try zir.findDeclsBody(list, body); + extra_index += body.len; + } else if (extra.data.bits.has_addrspace_ref) { + extra_index += 1; + } + + if (extra.data.bits.has_section_body) { + const body_len = zir.extra[extra_index]; + extra_index += 1; + const body = zir.extra[extra_index..][0..body_len]; + try zir.findDeclsBody(list, body); + extra_index += body.len; + } else if (extra.data.bits.has_section_ref) { + extra_index += 1; + } + + if (extra.data.bits.has_cc_body) { + const body_len = zir.extra[extra_index]; + extra_index += 1; + const body = zir.extra[extra_index..][0..body_len]; + try zir.findDeclsBody(list, body); + extra_index += body.len; + } else if (extra.data.bits.has_cc_ref) { + extra_index += 1; + } + + if (extra.data.bits.has_ret_ty_body) { + const body_len = zir.extra[extra_index]; + extra_index += 1; + const body = zir.extra[extra_index..][0..body_len]; + try zir.findDeclsBody(list, body); + extra_index += body.len; + } else if (extra.data.bits.has_ret_ty_ref) { + extra_index += 1; + } + const body = zir.extra[extra_index..][0..extra.data.body_len]; return zir.findDeclsBody(list, body); }, @@ -3729,6 +3837,7 @@ pub const FnInfo = struct { param_body_inst: Inst.Index, ret_ty_body: []const Inst.Index, body: []const Inst.Index, + ret_ty_ref: Zir.Inst.Ref, total_params_len: u32, }; @@ -3738,38 +3847,84 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { const info: struct { param_block: Inst.Index, body: []const Inst.Index, + ret_ty_ref: Inst.Ref, ret_ty_body: []const Inst.Index, } = switch (tags[fn_inst]) { .func, .func_inferred => blk: { const inst_data = datas[fn_inst].pl_node; const extra = zir.extraData(Inst.Func, inst_data.payload_index); - var extra_index: usize = extra.end; - const ret_ty_body = zir.extra[extra_index..][0..extra.data.ret_body_len]; - extra_index += ret_ty_body.len; + var extra_index: usize = extra.end; + var ret_ty_ref: Inst.Ref = .none; + var ret_ty_body: []const Inst.Index = &.{}; + + switch (extra.data.ret_body_len) { + 0 => { + ret_ty_ref = .void_type; + }, + 1 => { + ret_ty_ref = @intToEnum(Inst.Ref, zir.extra[extra_index]); + extra_index += 1; + }, + else => { + ret_ty_body = zir.extra[extra_index..][0..extra.data.ret_body_len]; + extra_index += ret_ty_body.len; + }, + } const body = zir.extra[extra_index..][0..extra.data.body_len]; extra_index += body.len; break :blk .{ .param_block = extra.data.param_block, + .ret_ty_ref = ret_ty_ref, .ret_ty_body = ret_ty_body, .body = body, }; }, - .func_extended => blk: { + .func_fancy => blk: { const inst_data = datas[fn_inst].pl_node; - const extra = zir.extraData(Inst.ExtendedFunc, inst_data.payload_index); + const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index); + var extra_index: usize = extra.end; + var ret_ty_ref: Inst.Ref = .void_type; + var ret_ty_body: []const Inst.Index = &.{}; + extra_index += @boolToInt(extra.data.bits.has_lib_name); - extra_index += @boolToInt(extra.data.bits.has_cc); - extra_index += @boolToInt(extra.data.bits.has_align); - const ret_ty_body = zir.extra[extra_index..][0..extra.data.ret_body_len]; - extra_index += ret_ty_body.len; + if (extra.data.bits.has_align_body) { + extra_index += zir.extra[extra_index] + 1; + } else if (extra.data.bits.has_align_ref) { + extra_index += 1; + } + if (extra.data.bits.has_addrspace_body) { + extra_index += zir.extra[extra_index] + 1; + } else if (extra.data.bits.has_addrspace_ref) { + extra_index += 1; + } + if (extra.data.bits.has_section_body) { + extra_index += zir.extra[extra_index] + 1; + } else if (extra.data.bits.has_section_ref) { + extra_index += 1; + } + if (extra.data.bits.has_cc_body) { + extra_index += zir.extra[extra_index] + 1; + } else if (extra.data.bits.has_cc_ref) { + extra_index += 1; + } + if (extra.data.bits.has_ret_ty_body) { + const body_len = zir.extra[extra_index]; + extra_index += 1; + ret_ty_body = zir.extra[extra_index..][0..body_len]; + extra_index += ret_ty_body.len; + } else if (extra.data.bits.has_ret_ty_ref) { + ret_ty_ref = @intToEnum(Inst.Ref, zir.extra[extra_index]); + extra_index += 1; + } const body = zir.extra[extra_index..][0..extra.data.body_len]; extra_index += body.len; break :blk .{ .param_block = extra.data.param_block, + .ret_ty_ref = ret_ty_ref, .ret_ty_body = ret_ty_body, .body = body, }; @@ -3792,6 +3947,7 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { .param_body = param_body, .param_body_inst = info.param_block, .ret_ty_body = info.ret_ty_body, + .ret_ty_ref = info.ret_ty_ref, .body = info.body, .total_params_len = total_params_len, }; diff --git a/src/print_zir.zig b/src/print_zir.zig index 1f243acc30..790563e221 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -429,7 +429,7 @@ const Writer = struct { .func => try self.writeFunc(stream, inst, false), .func_inferred => try self.writeFunc(stream, inst, true), - .func_extended => try self.writeFuncExtended(stream, inst), + .func_fancy => try self.writeFuncFancy(stream, inst), .@"unreachable" => try self.writeUnreachable(stream, inst), @@ -1912,10 +1912,24 @@ const Writer = struct { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const extra = self.code.extraData(Zir.Inst.Func, inst_data.payload_index); - var extra_index = extra.end; - const ret_ty_body = self.code.extra[extra_index..][0..extra.data.ret_body_len]; - extra_index += ret_ty_body.len; + var extra_index = extra.end; + var ret_ty_ref: Zir.Inst.Ref = .none; + var ret_ty_body: []const Zir.Inst.Index = &.{}; + + switch (extra.data.ret_body_len) { + 0 => { + ret_ty_ref = .void_type; + }, + 1 => { + ret_ty_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); + extra_index += 1; + }, + else => { + ret_ty_body = self.code.extra[extra_index..][0..extra.data.ret_body_len]; + extra_index += ret_ty_body.len; + }, + } const body = self.code.extra[extra_index..][0..extra.data.body_len]; extra_index += body.len; @@ -1926,43 +1940,96 @@ const Writer = struct { } return self.writeFuncCommon( stream, - ret_ty_body, inferred_error_set, false, false, + .none, + &.{}, .none, + &.{}, + .none, + &.{}, + .none, + &.{}, + ret_ty_ref, + ret_ty_body, + body, src, src_locs, ); } - fn writeFuncExtended(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + fn writeFuncFancy(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const extra = self.code.extraData(Zir.Inst.ExtendedFunc, inst_data.payload_index); + const extra = self.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index); const src = inst_data.src(); var extra_index: usize = extra.end; + var align_ref: Zir.Inst.Ref = .none; + var align_body: []const Zir.Inst.Index = &.{}; + var addrspace_ref: Zir.Inst.Ref = .none; + var addrspace_body: []const Zir.Inst.Index = &.{}; + var section_ref: Zir.Inst.Ref = .none; + var section_body: []const Zir.Inst.Index = &.{}; + var cc_ref: Zir.Inst.Ref = .none; + var cc_body: []const Zir.Inst.Index = &.{}; + var ret_ty_ref: Zir.Inst.Ref = .none; + var ret_ty_body: []const Zir.Inst.Index = &.{}; + if (extra.data.bits.has_lib_name) { const lib_name = self.code.nullTerminatedString(self.code.extra[extra_index]); extra_index += 1; try stream.print("lib_name=\"{}\", ", .{std.zig.fmtEscapes(lib_name)}); } try self.writeFlag(stream, "test, ", extra.data.bits.is_test); - const cc: Zir.Inst.Ref = if (!extra.data.bits.has_cc) .none else blk: { - const cc = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); - extra_index += 1; - break :blk cc; - }; - const align_inst: Zir.Inst.Ref = if (!extra.data.bits.has_align) .none else blk: { - const align_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); - extra_index += 1; - break :blk align_inst; - }; - const ret_ty_body = self.code.extra[extra_index..][0..extra.data.ret_body_len]; - extra_index += ret_ty_body.len; + if (extra.data.bits.has_align_body) { + const body_len = self.code.extra[extra_index]; + extra_index += 1; + align_body = self.code.extra[extra_index..][0..body_len]; + extra_index += align_body.len; + } else if (extra.data.bits.has_align_ref) { + align_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); + extra_index += 1; + } + if (extra.data.bits.has_addrspace_body) { + const body_len = self.code.extra[extra_index]; + extra_index += 1; + addrspace_body = self.code.extra[extra_index..][0..body_len]; + extra_index += addrspace_body.len; + } else if (extra.data.bits.has_addrspace_ref) { + addrspace_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); + extra_index += 1; + } + if (extra.data.bits.has_section_body) { + const body_len = self.code.extra[extra_index]; + extra_index += 1; + section_body = self.code.extra[extra_index..][0..body_len]; + extra_index += section_body.len; + } else if (extra.data.bits.has_section_ref) { + section_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); + extra_index += 1; + } + if (extra.data.bits.has_cc_body) { + const body_len = self.code.extra[extra_index]; + extra_index += 1; + cc_body = self.code.extra[extra_index..][0..body_len]; + extra_index += cc_body.len; + } else if (extra.data.bits.has_cc_ref) { + cc_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); + extra_index += 1; + } + if (extra.data.bits.has_ret_ty_body) { + const body_len = self.code.extra[extra_index]; + extra_index += 1; + ret_ty_body = self.code.extra[extra_index..][0..body_len]; + extra_index += ret_ty_body.len; + } else if (extra.data.bits.has_ret_ty_ref) { + ret_ty_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); + extra_index += 1; + } const body = self.code.extra[extra_index..][0..extra.data.body_len]; extra_index += body.len; @@ -1973,12 +2040,19 @@ const Writer = struct { } return self.writeFuncCommon( stream, - ret_ty_body, extra.data.bits.is_inferred_error, extra.data.bits.is_var_args, extra.data.bits.is_extern, - cc, - align_inst, + align_ref, + align_body, + addrspace_ref, + addrspace_body, + section_ref, + section_body, + cc_ref, + cc_body, + ret_ty_ref, + ret_ty_body, body, src, src_locs, @@ -2122,30 +2196,33 @@ const Writer = struct { fn writeFuncCommon( self: *Writer, stream: anytype, - ret_ty_body: []const Zir.Inst.Index, inferred_error_set: bool, var_args: bool, is_extern: bool, - cc: Zir.Inst.Ref, - align_inst: Zir.Inst.Ref, + align_ref: Zir.Inst.Ref, + align_body: []const Zir.Inst.Index, + addrspace_ref: Zir.Inst.Ref, + addrspace_body: []const Zir.Inst.Index, + section_ref: Zir.Inst.Ref, + section_body: []const Zir.Inst.Index, + cc_ref: Zir.Inst.Ref, + cc_body: []const Zir.Inst.Index, + ret_ty_ref: Zir.Inst.Ref, + ret_ty_body: []const Zir.Inst.Index, body: []const Zir.Inst.Index, src: LazySrcLoc, src_locs: Zir.Inst.Func.SrcLocs, ) !void { - if (ret_ty_body.len == 0) { - try stream.writeAll("ret_ty=void"); - } else { - try stream.writeAll("ret_ty="); - try self.writeBracedBody(stream, ret_ty_body); - } + try self.writeOptionalInstRefOrBody(stream, "align=", align_ref, align_body); + try self.writeOptionalInstRefOrBody(stream, "addrspace=", addrspace_ref, addrspace_body); + try self.writeOptionalInstRefOrBody(stream, "section=", section_ref, section_body); + try self.writeOptionalInstRefOrBody(stream, "cc=", cc_ref, cc_body); + try self.writeOptionalInstRefOrBody(stream, "ret_ty=", ret_ty_ref, ret_ty_body); + try self.writeFlag(stream, "vargs, ", var_args); + try self.writeFlag(stream, "extern, ", is_extern); + try self.writeFlag(stream, "inferror, ", inferred_error_set); - try self.writeOptionalInstRef(stream, ", cc=", cc); - try self.writeOptionalInstRef(stream, ", align=", align_inst); - try self.writeFlag(stream, ", vargs", var_args); - try self.writeFlag(stream, ", extern", is_extern); - try self.writeFlag(stream, ", inferror", inferred_error_set); - - try stream.writeAll(", body="); + try stream.writeAll("body="); try self.writeBracedBody(stream, body); try stream.writeAll(") "); if (body.len != 0) { @@ -2195,6 +2272,24 @@ const Writer = struct { try self.writeInstRef(stream, inst); } + fn writeOptionalInstRefOrBody( + self: *Writer, + stream: anytype, + prefix: []const u8, + ref: Zir.Inst.Ref, + body: []const Zir.Inst.Index, + ) !void { + if (body.len != 0) { + try stream.writeAll(prefix); + try self.writeBracedBody(stream, body); + try stream.writeAll(", "); + } else if (ref != .none) { + try stream.writeAll(prefix); + try self.writeInstRef(stream, ref); + try stream.writeAll(", "); + } + } + fn writeFlag( self: *Writer, stream: anytype, From 7e98b047ddc51062a32cbf2fb852d38e5023886c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 May 2022 18:12:38 -0700 Subject: [PATCH 1680/2031] AstGen: simplify function return type expressions This check for primitives is already handled by the generic logic that checks if the body ends up being empty. I kept this commit in the git history in case we ever want that nodePrimitive function again in the future, it might be useful. --- src/AstGen.zig | 42 +++++++----------------------------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index ff3404a51b..7343515008 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -3562,17 +3562,14 @@ fn fnDecl( var ret_gz = decl_gz.makeSubBlock(params_scope); defer ret_gz.unstack(); - const ret_ref: Zir.Inst.Ref = switch (nodePrimitive(tree, fn_proto.ast.return_type)) { - .none => inst: { - const inst = try expr(&ret_gz, params_scope, coerced_type_rl, fn_proto.ast.return_type); - if (ret_gz.instructionsSlice().len == 0) { - // In this case we will send a len=0 body which can be encoded more efficiently. - break :inst inst; - } - _ = try ret_gz.addBreak(.break_inline, 0, inst); + const ret_ref: Zir.Inst.Ref = inst: { + const inst = try expr(&ret_gz, params_scope, coerced_type_rl, fn_proto.ast.return_type); + if (ret_gz.instructionsSlice().len == 0) { + // In this case we will send a len=0 body which can be encoded more efficiently. break :inst inst; - }, - else => |p| p, + } + _ = try ret_gz.addBreak(.break_inline, 0, inst); + break :inst inst; }; const func_inst: Zir.Inst.Ref = if (body_node == 0) func: { @@ -9025,31 +9022,6 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool { } } -fn nodePrimitive(tree: *const Ast, start_node: Ast.Node.Index) Zir.Inst.Ref { - const node_tags = tree.nodes.items(.tag); - const node_datas = tree.nodes.items(.data); - - var node = start_node; - while (true) { - switch (node_tags[node]) { - // Forward the question to the LHS sub-expression. - .grouped_expression => node = node_datas[node].lhs, - - .identifier => { - const main_tokens = tree.nodes.items(.main_token); - const ident_bytes = tree.tokenSlice(main_tokens[node]); - if (primitives.get(ident_bytes)) |primitive| { - return primitive; - } else { - return .none; - } - }, - - else => return .none, - } - } -} - /// Applies `rl` semantics to `result`. Expressions which do not do their own handling of /// result locations must call this function on their result. /// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer. From bd89a73d5289536948b052eb7f052d6de193441b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 May 2022 14:16:28 -0700 Subject: [PATCH 1681/2031] Sema: implement functions generic across callconv() or align() --- src/Sema.zig | 118 ++++++++++++++++++++++++++++++---------- src/type.zig | 4 ++ test/behavior/align.zig | 43 +++++++++++---- 3 files changed, 125 insertions(+), 40 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index bf8e10b1a4..8d7a6dff92 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6547,7 +6547,7 @@ fn zirFunc( inst, 0, target_util.defaultAddressSpace(target, .function), - null, + FuncLinkSection.default, cc, ret_ty, false, @@ -6660,15 +6660,26 @@ fn handleExternLibName( return sema.gpa.dupeZ(u8, lib_name); } +const FuncLinkSection = union(enum) { + generic, + default, + explicit: [*:0]const u8, +}; + fn funcCommon( sema: *Sema, block: *Block, src_node_offset: i32, func_inst: Zir.Inst.Index, - alignment: u32, - address_space: std.builtin.AddressSpace, - section: ?[*:0]const u8, - cc: std.builtin.CallingConvention, + /// null means generic poison + alignment: ?u32, + /// null means generic poison + address_space: ?std.builtin.AddressSpace, + /// outer null means generic poison; inner null means default link section + section: FuncLinkSection, + /// null means generic poison + cc: ?std.builtin.CallingConvention, + /// this might be Type.generic_poison bare_return_type: Type, var_args: bool, inferred_error_set: bool, @@ -6679,7 +6690,11 @@ fn funcCommon( ) CompileError!Air.Inst.Ref { const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; - var is_generic = bare_return_type.tag() == .generic_poison; + var is_generic = bare_return_type.tag() == .generic_poison or + alignment == null or + address_space == null or + section == .generic or + cc == null; // Check for generic params. for (block.params.items) |param| { if (param.ty.tag() == .generic_poison) is_generic = true; @@ -6700,25 +6715,28 @@ fn funcCommon( errdefer if (maybe_inferred_error_set_node) |node| sema.gpa.destroy(node); // Note: no need to errdefer since this will still be in its default state at the end of the function. + const target = sema.mod.getTarget(); const fn_ty: Type = fn_ty: { // Hot path for some common function types. // TODO can we eliminate some of these Type tag values? seems unnecessarily complicated. - if (!is_generic and block.params.items.len == 0 and !var_args and - alignment == 0 and !inferred_error_set) + if (!is_generic and block.params.items.len == 0 and !var_args and !inferred_error_set and + alignment.? == 0 and + address_space.? == target_util.defaultAddressSpace(target, .function) and + section == .default) { - if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { + if (bare_return_type.zigTypeTag() == .NoReturn and cc.? == .Unspecified) { break :fn_ty Type.initTag(.fn_noreturn_no_args); } - if (bare_return_type.zigTypeTag() == .Void and cc == .Unspecified) { + if (bare_return_type.zigTypeTag() == .Void and cc.? == .Unspecified) { break :fn_ty Type.initTag(.fn_void_no_args); } - if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Naked) { + if (bare_return_type.zigTypeTag() == .NoReturn and cc.? == .Naked) { break :fn_ty Type.initTag(.fn_naked_noreturn_no_args); } - if (bare_return_type.zigTypeTag() == .Void and cc == .C) { + if (bare_return_type.zigTypeTag() == .Void and cc.? == .C) { break :fn_ty Type.initTag(.fn_ccc_void_no_args); } } @@ -6764,21 +6782,33 @@ fn funcCommon( }); }; + // stage1 bug workaround + const cc_workaround = cc orelse undefined; + const align_workaround = alignment orelse @as(u32, undefined); + break :fn_ty try Type.Tag.function.create(sema.arena, .{ .param_types = param_types, .comptime_params = comptime_params.ptr, .return_type = return_type, - .cc = cc, - .alignment = alignment, + .cc = cc_workaround, + .cc_is_generic = cc == null, + .alignment = align_workaround, + .align_is_generic = alignment == null, + .section_is_generic = section == .generic, + .addrspace_is_generic = address_space == null, .is_var_args = var_args, .is_generic = is_generic, }); }; if (sema.owner_decl.owns_tv) { - sema.owner_decl.@"linksection" = section; - sema.owner_decl.@"align" = alignment; - sema.owner_decl.@"addrspace" = address_space; + switch (section) { + .generic => sema.owner_decl.@"linksection" = undefined, + .default => sema.owner_decl.@"linksection" = null, + .explicit => |s| sema.owner_decl.@"linksection" = s, + } + if (alignment) |a| sema.owner_decl.@"align" = a; + if (address_space) |a| sema.owner_decl.@"addrspace" = a; } if (is_extern) { @@ -16780,13 +16810,16 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A break :blk lib_name; } else null; - const @"align": u32 = if (extra.data.bits.has_align_body) blk: { + const @"align": ?u32 = if (extra.data.bits.has_align_body) blk: { const body_len = sema.code.extra[extra_index]; extra_index += 1; const body = sema.code.extra[extra_index..][0..body_len]; extra_index += body.len; const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u16); + if (val.tag() == .generic_poison) { + break :blk null; + } const alignment = @intCast(u32, val.toUnsignedInt(target)); if (alignment == target_util.defaultFunctionAlignment(target)) { break :blk 0; @@ -16796,7 +16829,12 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } else if (extra.data.bits.has_align_ref) blk: { const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const align_tv = try sema.resolveInstConst(block, align_src, align_ref); + const align_tv = sema.resolveInstConst(block, align_src, align_ref) catch |err| switch (err) { + error.GenericPoison => { + break :blk null; + }, + else => |e| return e, + }; const alignment = @intCast(u32, align_tv.val.toUnsignedInt(target)); if (alignment == target_util.defaultFunctionAlignment(target)) { break :blk 0; @@ -16805,7 +16843,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } } else 0; - const @"addrspace": std.builtin.AddressSpace = if (extra.data.bits.has_addrspace_body) blk: { + const @"addrspace": ?std.builtin.AddressSpace = if (extra.data.bits.has_addrspace_body) blk: { const body_len = sema.code.extra[extra_index]; extra_index += 1; const body = sema.code.extra[extra_index..][0..body_len]; @@ -16813,32 +16851,48 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const addrspace_ty = try sema.getBuiltinType(block, addrspace_src, "AddressSpace"); const val = try sema.resolveGenericBody(block, addrspace_src, body, inst, addrspace_ty); + if (val.tag() == .generic_poison) { + break :blk null; + } break :blk val.toEnum(std.builtin.AddressSpace); } else if (extra.data.bits.has_addrspace_ref) blk: { const addrspace_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const addrspace_tv = try sema.resolveInstConst(block, addrspace_src, addrspace_ref); + const addrspace_tv = sema.resolveInstConst(block, addrspace_src, addrspace_ref) catch |err| switch (err) { + error.GenericPoison => { + break :blk null; + }, + else => |e| return e, + }; break :blk addrspace_tv.val.toEnum(std.builtin.AddressSpace); } else target_util.defaultAddressSpace(target, .function); - const @"linksection": ?[*:0]const u8 = if (extra.data.bits.has_section_body) { + const @"linksection": FuncLinkSection = if (extra.data.bits.has_section_body) blk: { const body_len = sema.code.extra[extra_index]; extra_index += 1; const body = sema.code.extra[extra_index..][0..body_len]; extra_index += body.len; const val = try sema.resolveGenericBody(block, section_src, body, inst, Type.initTag(.const_slice_u8)); + if (val.tag() == .generic_poison) { + break :blk FuncLinkSection{ .generic = {} }; + } _ = val; return sema.fail(block, section_src, "TODO implement linksection on functions", .{}); - } else if (extra.data.bits.has_section_ref) { + } else if (extra.data.bits.has_section_ref) blk: { const section_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const section_tv = try sema.resolveInstConst(block, section_src, section_ref); + const section_tv = sema.resolveInstConst(block, section_src, section_ref) catch |err| switch (err) { + error.GenericPoison => { + break :blk FuncLinkSection{ .generic = {} }; + }, + else => |e| return e, + }; _ = section_tv; return sema.fail(block, section_src, "TODO implement linksection on functions", .{}); - } else null; + } else FuncLinkSection{ .default = {} }; - const cc: std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: { + const cc: ?std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: { const body_len = sema.code.extra[extra_index]; extra_index += 1; const body = sema.code.extra[extra_index..][0..body_len]; @@ -16846,13 +16900,21 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const cc_ty = try sema.getBuiltinType(block, addrspace_src, "CallingConvention"); const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty); + if (val.tag() == .generic_poison) { + break :blk null; + } break :blk val.toEnum(std.builtin.CallingConvention); } else if (extra.data.bits.has_cc_ref) blk: { const cc_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const cc_tv = try sema.resolveInstConst(block, cc_src, cc_ref); + const cc_tv = sema.resolveInstConst(block, cc_src, cc_ref) catch |err| switch (err) { + error.GenericPoison => { + break :blk null; + }, + else => |e| return e, + }; break :blk cc_tv.val.toEnum(std.builtin.CallingConvention); - } else .Unspecified; + } else std.builtin.CallingConvention.Unspecified; const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: { const body_len = sema.code.extra[extra_index]; diff --git a/src/type.zig b/src/type.zig index 145ae4904a..ebb8bfd7c3 100644 --- a/src/type.zig +++ b/src/type.zig @@ -6120,6 +6120,10 @@ pub const Type = extern union { cc: std.builtin.CallingConvention, is_var_args: bool, is_generic: bool, + align_is_generic: bool = false, + cc_is_generic: bool = false, + section_is_generic: bool = false, + addrspace_is_generic: bool = false, pub fn paramIsComptime(self: @This(), i: usize) bool { assert(i < self.param_types.len); diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 2c0893074e..d77a2153cc 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -334,25 +334,44 @@ fn simple4() align(4) i32 { return 0x19; } -test "generic function with align param" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; +test "function align expression depends on generic parameter" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO // function alignment is a compile error on wasm32/wasm64 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest; if (native_arch == .thumb) return error.SkipZigTest; - try expect(whyWouldYouEverDoThis(1) == 0x1); - try expect(whyWouldYouEverDoThis(4) == 0x1); - try expect(whyWouldYouEverDoThis(8) == 0x1); + const S = struct { + fn doTheTest() !void { + try expect(foobar(1) == 2); + try expect(foobar(4) == 5); + try expect(foobar(8) == 9); + } + + fn foobar(comptime align_bytes: u8) align(align_bytes) u8 { + return align_bytes + 1; + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); } -fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { - _ = align_bytes; - return 0x1; +test "function callconv expression depends on generic parameter" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + + const S = struct { + fn doTheTest() !void { + try expect(foobar(.C, 1) == 2); + try expect(foobar(.Unspecified, 2) == 3); + } + + fn foobar(comptime cc: std.builtin.CallingConvention, arg: u8) callconv(cc) u8 { + return arg + 1; + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); } test "runtime known array index has best alignment possible" { From 83beed09e1ce5a15e2b9801a50cf601352d8383e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 May 2022 00:39:14 -0700 Subject: [PATCH 1682/2031] LLVM: omit memset of 0xaa bytes in unsafe optimization modes This is one out of three changes I intend to make to address #11498. However I will put these changes in separate branches and merge them separately so that we can have three independent points on the perf charts. --- src/codegen/llvm.zig | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index f958fdfbb3..8cfa833cdd 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -6944,6 +6944,23 @@ pub const FuncGen = struct { // possibly do the safety 0xaa bytes for undefined. const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false; if (val_is_undef) { + { + // TODO let's handle this in AIR rather than by having each backend + // check the optimization mode of the compilation because the plan is + // to support setting the optimization mode at finer grained scopes + // which happens in Sema. Codegen should not be aware of this logic. + // I think this comment is basically the same as the other TODO comment just + // above but I'm leaving them both here to make it look super messy and + // thereby bait contributors (or let's be honest, probably myself) into + // fixing this instead of letting it rot. + const safety = switch (self.dg.module.comp.bin_file.options.optimize_mode) { + .ReleaseSmall, .ReleaseFast => false, + .Debug, .ReleaseSafe => true, + }; + if (!safety) { + return null; + } + } const target = self.dg.module.getTarget(); const operand_size = operand_ty.abiSize(target); const u8_llvm_ty = self.context.intType(8); From febc7d3cd63eefe91c7aaa95e3a274a0b44e353e Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 31 May 2022 16:21:36 +0300 Subject: [PATCH 1683/2031] Sema: take `dbg_stmt` into account in `zirResolveInferredAlloc` --- src/Sema.zig | 2 +- test/behavior/basic.zig | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 505810a158..ed8391a44d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2976,7 +2976,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com // Even though we reuse the constant instruction, we still remove it from the // block so that codegen does not see it. - block.instructions.shrinkRetainingCapacity(block.instructions.items.len - 3); + block.instructions.shrinkRetainingCapacity(search_index); sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, new_decl_index); // if bitcast ty ref needs to be made const, make_ptr_const // ZIR handles it later, so we can just use the ty ref here. diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index e887a1d4b6..8bc1ad5cf2 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -977,3 +977,13 @@ test "weird array and tuple initializations" { .b = if (a) .{ .e = .a } else .{ .e = .b }, }; } + +test "array type comes from generic function" { + const S = struct { + fn A() type { + return struct { a: u8 = 0 }; + } + }; + const args = [_]S.A(){.{}}; + _ = args; +} From 36df79cd3779b27a63f449618459603ce549660a Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 31 May 2022 16:43:58 +0300 Subject: [PATCH 1684/2031] stage2: ignore generic return type when hashing function type Generic parameter types are already ignored. --- src/type.zig | 4 +++- test/behavior/basic.zig | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/type.zig b/src/type.zig index ebb8bfd7c3..638145e8b1 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1036,7 +1036,9 @@ pub const Type = extern union { std.hash.autoHash(hasher, std.builtin.TypeId.Fn); const fn_info = ty.fnInfo(); - hashWithHasher(fn_info.return_type, hasher, mod); + if (fn_info.return_type.tag() != .generic_poison) { + hashWithHasher(fn_info.return_type, hasher, mod); + } std.hash.autoHash(hasher, fn_info.alignment); std.hash.autoHash(hasher, fn_info.cc); std.hash.autoHash(hasher, fn_info.is_var_args); diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 8bc1ad5cf2..32df664bae 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -987,3 +987,21 @@ test "array type comes from generic function" { const args = [_]S.A(){.{}}; _ = args; } + +test "generic function uses return type of other generic function" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const S = struct { + fn call( + f: anytype, + args: anytype, + ) @TypeOf(@call(.{}, f, @as(@TypeOf(args), undefined))) { + return @call(.{}, f, args); + } + + fn func(arg: anytype) @TypeOf(arg) { + return arg; + } + }; + try std.testing.expect(S.call(S.func, .{@as(u8, 1)}) == 1); +} From 56608cbb3d5577d8931830fa02d3e2920925ceab Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 31 May 2022 17:14:37 +0300 Subject: [PATCH 1685/2031] Sema: do not add calls to `returnError` for comptime known non-error values --- src/Sema.zig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index ed8391a44d..d4c49973a1 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -13011,10 +13011,13 @@ fn analyzeRet( const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm; - if ((sema.fn_ret_ty.zigTypeTag() == .ErrorSet or sema.typeOf(uncasted_operand).zigTypeTag() == .ErrorUnion) and + if (sema.fn_ret_ty.isError() and sema.mod.comp.bin_file.options.error_return_tracing and backend_supports_error_return_tracing) - { + ret_err: { + if (try sema.resolveMaybeUndefVal(block, src, operand)) |ret_val| { + if (ret_val.tag() != .@"error") break :ret_err; + } const return_err_fn = try sema.getBuiltin(block, src, "returnError"); const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); From 26aea8cfa1ea98329e18458cb23be2d60ef52507 Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Tue, 31 May 2022 17:29:38 +0200 Subject: [PATCH 1686/2031] crypto: add support for the NIST P-384 curve (#11735) After P-256, here comes P-384, also known as secp384r1. Like P-256, it is required for TLS, and is the current NIST recommendation for key exchange and signatures, for better or for worse. Like P-256, all the finite field arithmetic has been computed and verified to be correct by fiat-crypto. --- lib/std/crypto.zig | 2 + lib/std/crypto/pcurves/common.zig | 23 + lib/std/crypto/pcurves/p256.zig | 2 +- lib/std/crypto/pcurves/p256/field.zig | 2 +- lib/std/crypto/pcurves/p256/scalar.zig | 2 +- lib/std/crypto/pcurves/p384.zig | 478 +++ lib/std/crypto/pcurves/p384/field.zig | 12 + lib/std/crypto/pcurves/p384/p384_64.zig | 3578 ++++++++++++++++ .../crypto/pcurves/p384/p384_scalar_64.zig | 3632 +++++++++++++++++ lib/std/crypto/pcurves/p384/scalar.zig | 202 + .../pcurves/{tests.zig => tests/p256.zig} | 2 +- lib/std/crypto/pcurves/tests/p384.zig | 136 + 12 files changed, 8067 insertions(+), 4 deletions(-) create mode 100644 lib/std/crypto/pcurves/p384.zig create mode 100644 lib/std/crypto/pcurves/p384/field.zig create mode 100644 lib/std/crypto/pcurves/p384/p384_64.zig create mode 100644 lib/std/crypto/pcurves/p384/p384_scalar_64.zig create mode 100644 lib/std/crypto/pcurves/p384/scalar.zig rename lib/std/crypto/pcurves/{tests.zig => tests/p256.zig} (99%) create mode 100644 lib/std/crypto/pcurves/tests/p384.zig diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index c52e207eeb..0c2869fc01 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -62,6 +62,7 @@ pub const ecc = struct { pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519; pub const Edwards25519 = @import("crypto/25519/edwards25519.zig").Edwards25519; pub const P256 = @import("crypto/pcurves/p256.zig").P256; + pub const P384 = @import("crypto/pcurves/p384.zig").P384; pub const Ristretto255 = @import("crypto/25519/ristretto255.zig").Ristretto255; }; @@ -201,6 +202,7 @@ test { _ = ecc.Curve25519; _ = ecc.Edwards25519; _ = ecc.P256; + _ = ecc.P384; _ = ecc.Ristretto255; _ = hash.blake2; diff --git a/lib/std/crypto/pcurves/common.zig b/lib/std/crypto/pcurves/common.zig index 5eb4e48623..55607b33c0 100644 --- a/lib/std/crypto/pcurves/common.zig +++ b/lib/std/crypto/pcurves/common.zig @@ -253,6 +253,18 @@ pub fn Field(comptime params: FieldParams) type { const x47 = x15.mul(x53); const ls = x47.mul(((x53.sqn(17).mul(x2)).sqn(143).mul(x47)).sqn(47)).sq().mul(x2); return ls.equivalent(Fe.one); + } else if (field_order == 39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319) { + const t111 = x2.mul(x2.mul(x2.sq()).sq()); + const t111111 = t111.mul(t111.sqn(3)); + const t1111110 = t111111.sq(); + const t1111111 = x2.mul(t1111110); + const x12 = t1111110.sqn(5).mul(t111111); + const x31 = x12.sqn(12).mul(x12).sqn(7).mul(t1111111); + const x32 = x31.sq().mul(x2); + const x63 = x32.sqn(31).mul(x31); + const x126 = x63.sqn(63).mul(x63); + const ls = x126.sqn(126).mul(x126).sqn(3).mul(t111).sqn(33).mul(x32).sqn(95).mul(x31); + return ls.equivalent(Fe.one); } else { const ls = x2.pow(std.meta.Int(.unsigned, field_bits), (field_order - 1) / 2); // Legendre symbol return ls.equivalent(Fe.one); @@ -268,6 +280,17 @@ pub fn Field(comptime params: FieldParams) type { const t11111111 = t1111.mul(t1111.sqn(4)); const x16 = t11111111.sqn(8).mul(t11111111); return x16.sqn(16).mul(x16).sqn(32).mul(x2).sqn(96).mul(x2).sqn(94); + } else if (field_order == 39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319) { + const t111 = x2.mul(x2.mul(x2.sq()).sq()); + const t111111 = t111.mul(t111.sqn(3)); + const t1111110 = t111111.sq(); + const t1111111 = x2.mul(t1111110); + const x12 = t1111110.sqn(5).mul(t111111); + const x31 = x12.sqn(12).mul(x12).sqn(7).mul(t1111111); + const x32 = x31.sq().mul(x2); + const x63 = x32.sqn(31).mul(x31); + const x126 = x63.sqn(63).mul(x63); + return x126.sqn(126).mul(x126).sqn(3).mul(t111).sqn(33).mul(x32).sqn(64).mul(x2).sqn(30); } else { return x2.pow(std.meta.Int(.unsigned, field_bits), (field_order + 1) / 4); } diff --git a/lib/std/crypto/pcurves/p256.zig b/lib/std/crypto/pcurves/p256.zig index 06aeed450f..c7dac55b24 100644 --- a/lib/std/crypto/pcurves/p256.zig +++ b/lib/std/crypto/pcurves/p256.zig @@ -474,5 +474,5 @@ pub const AffineCoordinates = struct { }; test "p256" { - _ = @import("tests.zig"); + _ = @import("tests/p256.zig"); } diff --git a/lib/std/crypto/pcurves/p256/field.zig b/lib/std/crypto/pcurves/p256/field.zig index c36db58e87..b85973132b 100644 --- a/lib/std/crypto/pcurves/p256/field.zig +++ b/lib/std/crypto/pcurves/p256/field.zig @@ -7,6 +7,6 @@ pub const Fe = Field(.{ .fiat = @import("p256_64.zig"), .field_order = 115792089210356248762697446949407573530086143415290314195533631308867097853951, .field_bits = 256, - .saturated_bits = 255, + .saturated_bits = 256, .encoded_length = 32, }); diff --git a/lib/std/crypto/pcurves/p256/scalar.zig b/lib/std/crypto/pcurves/p256/scalar.zig index 9ed52a85d8..d6553afdd4 100644 --- a/lib/std/crypto/pcurves/p256/scalar.zig +++ b/lib/std/crypto/pcurves/p256/scalar.zig @@ -20,7 +20,7 @@ const Fe = Field(.{ .fiat = @import("p256_scalar_64.zig"), .field_order = 115792089210356248762697446949407573529996955224135760342422259061068512044369, .field_bits = 256, - .saturated_bits = 255, + .saturated_bits = 256, .encoded_length = encoded_length, }); diff --git a/lib/std/crypto/pcurves/p384.zig b/lib/std/crypto/pcurves/p384.zig new file mode 100644 index 0000000000..854343c1e4 --- /dev/null +++ b/lib/std/crypto/pcurves/p384.zig @@ -0,0 +1,478 @@ +const std = @import("std"); +const crypto = std.crypto; +const mem = std.mem; +const meta = std.meta; + +const EncodingError = crypto.errors.EncodingError; +const IdentityElementError = crypto.errors.IdentityElementError; +const NonCanonicalError = crypto.errors.NonCanonicalError; +const NotSquareError = crypto.errors.NotSquareError; + +/// Group operations over P384. +pub const P384 = struct { + /// The underlying prime field. + pub const Fe = @import("p384/field.zig").Fe; + /// Field arithmetic mod the order of the main subgroup. + pub const scalar = @import("p384/scalar.zig"); + + x: Fe, + y: Fe, + z: Fe = Fe.one, + + is_base: bool = false, + + /// The P384 base point. + pub const basePoint = P384{ + .x = Fe.fromInt(26247035095799689268623156744566981891852923491109213387815615900925518854738050089022388053975719786650872476732087) catch unreachable, + .y = Fe.fromInt(8325710961489029985546751289520108179287853048861315594709205902480503199884419224438643760392947333078086511627871) catch unreachable, + .z = Fe.one, + .is_base = true, + }; + + /// The P384 neutral element. + pub const identityElement = P384{ .x = Fe.zero, .y = Fe.one, .z = Fe.zero }; + + pub const B = Fe.fromInt(27580193559959705877849011840389048093056905856361568521428707301988689241309860865136260764883745107765439761230575) catch unreachable; + + /// Reject the neutral element. + pub fn rejectIdentity(p: P384) IdentityElementError!void { + if (p.x.isZero()) { + return error.IdentityElement; + } + } + + /// Create a point from affine coordinates after checking that they match the curve equation. + pub fn fromAffineCoordinates(p: AffineCoordinates) EncodingError!P384 { + const x = p.x; + const y = p.y; + const x3AxB = x.sq().mul(x).sub(x).sub(x).sub(x).add(B); + const yy = y.sq(); + const on_curve = @boolToInt(x3AxB.equivalent(yy)); + const is_identity = @boolToInt(x.equivalent(AffineCoordinates.identityElement.x)) & @boolToInt(y.equivalent(AffineCoordinates.identityElement.y)); + if ((on_curve | is_identity) == 0) { + return error.InvalidEncoding; + } + var ret = P384{ .x = x, .y = y, .z = Fe.one }; + ret.z.cMov(P384.identityElement.z, is_identity); + return ret; + } + + /// Create a point from serialized affine coordinates. + pub fn fromSerializedAffineCoordinates(xs: [48]u8, ys: [48]u8, endian: std.builtin.Endian) (NonCanonicalError || EncodingError)!P384 { + const x = try Fe.fromBytes(xs, endian); + const y = try Fe.fromBytes(ys, endian); + return fromAffineCoordinates(.{ .x = x, .y = y }); + } + + /// Recover the Y coordinate from the X coordinate. + pub fn recoverY(x: Fe, is_odd: bool) NotSquareError!Fe { + const x3AxB = x.sq().mul(x).sub(x).sub(x).sub(x).add(B); + var y = try x3AxB.sqrt(); + const yn = y.neg(); + y.cMov(yn, @boolToInt(is_odd) ^ @boolToInt(y.isOdd())); + return y; + } + + /// Deserialize a SEC1-encoded point. + pub fn fromSec1(s: []const u8) (EncodingError || NotSquareError || NonCanonicalError)!P384 { + if (s.len < 1) return error.InvalidEncoding; + const encoding_type = s[0]; + const encoded = s[1..]; + switch (encoding_type) { + 0 => { + if (encoded.len != 0) return error.InvalidEncoding; + return P384.identityElement; + }, + 2, 3 => { + if (encoded.len != 48) return error.InvalidEncoding; + const x = try Fe.fromBytes(encoded[0..48].*, .Big); + const y_is_odd = (encoding_type == 3); + const y = try recoverY(x, y_is_odd); + return P384{ .x = x, .y = y }; + }, + 4 => { + if (encoded.len != 96) return error.InvalidEncoding; + const x = try Fe.fromBytes(encoded[0..48].*, .Big); + const y = try Fe.fromBytes(encoded[48..96].*, .Big); + return P384.fromAffineCoordinates(.{ .x = x, .y = y }); + }, + else => return error.InvalidEncoding, + } + } + + /// Serialize a point using the compressed SEC-1 format. + pub fn toCompressedSec1(p: P384) [49]u8 { + var out: [49]u8 = undefined; + const xy = p.affineCoordinates(); + out[0] = if (xy.y.isOdd()) 3 else 2; + mem.copy(u8, out[1..], &xy.x.toBytes(.Big)); + return out; + } + + /// Serialize a point using the uncompressed SEC-1 format. + pub fn toUncompressedSec1(p: P384) [97]u8 { + var out: [97]u8 = undefined; + out[0] = 4; + const xy = p.affineCoordinates(); + mem.copy(u8, out[1..49], &xy.x.toBytes(.Big)); + mem.copy(u8, out[49..97], &xy.y.toBytes(.Big)); + return out; + } + + /// Return a random point. + pub fn random() P384 { + const n = scalar.random(.Little); + return basePoint.mul(n, .Little) catch unreachable; + } + + /// Flip the sign of the X coordinate. + pub fn neg(p: P384) P384 { + return .{ .x = p.x, .y = p.y.neg(), .z = p.z }; + } + + /// Double a P384 point. + // Algorithm 6 from https://eprint.iacr.org/2015/1060.pdf + pub fn dbl(p: P384) P384 { + var t0 = p.x.sq(); + var t1 = p.y.sq(); + var t2 = p.z.sq(); + var t3 = p.x.mul(p.y); + t3 = t3.dbl(); + var Z3 = p.x.mul(p.z); + Z3 = Z3.add(Z3); + var Y3 = B.mul(t2); + Y3 = Y3.sub(Z3); + var X3 = Y3.dbl(); + Y3 = X3.add(Y3); + X3 = t1.sub(Y3); + Y3 = t1.add(Y3); + Y3 = X3.mul(Y3); + X3 = X3.mul(t3); + t3 = t2.dbl(); + t2 = t2.add(t3); + Z3 = B.mul(Z3); + Z3 = Z3.sub(t2); + Z3 = Z3.sub(t0); + t3 = Z3.dbl(); + Z3 = Z3.add(t3); + t3 = t0.dbl(); + t0 = t3.add(t0); + t0 = t0.sub(t2); + t0 = t0.mul(Z3); + Y3 = Y3.add(t0); + t0 = p.y.mul(p.z); + t0 = t0.dbl(); + Z3 = t0.mul(Z3); + X3 = X3.sub(Z3); + Z3 = t0.mul(t1); + Z3 = Z3.dbl().dbl(); + return .{ + .x = X3, + .y = Y3, + .z = Z3, + }; + } + + /// Add P384 points, the second being specified using affine coordinates. + // Algorithm 5 from https://eprint.iacr.org/2015/1060.pdf + pub fn addMixed(p: P384, q: AffineCoordinates) P384 { + var t0 = p.x.mul(q.x); + var t1 = p.y.mul(q.y); + var t3 = q.x.add(q.y); + var t4 = p.x.add(p.y); + t3 = t3.mul(t4); + t4 = t0.add(t1); + t3 = t3.sub(t4); + t4 = q.y.mul(p.z); + t4 = t4.add(p.y); + var Y3 = q.x.mul(p.z); + Y3 = Y3.add(p.x); + var Z3 = B.mul(p.z); + var X3 = Y3.sub(Z3); + Z3 = X3.dbl(); + X3 = X3.add(Z3); + Z3 = t1.sub(X3); + X3 = t1.add(X3); + Y3 = B.mul(Y3); + t1 = p.z.dbl(); + var t2 = t1.add(p.z); + Y3 = Y3.sub(t2); + Y3 = Y3.sub(t0); + t1 = Y3.dbl(); + Y3 = t1.add(Y3); + t1 = t0.dbl(); + t0 = t1.add(t0); + t0 = t0.sub(t2); + t1 = t4.mul(Y3); + t2 = t0.mul(Y3); + Y3 = X3.mul(Z3); + Y3 = Y3.add(t2); + X3 = t3.mul(X3); + X3 = X3.sub(t1); + Z3 = t4.mul(Z3); + t1 = t3.mul(t0); + Z3 = Z3.add(t1); + var ret = P384{ + .x = X3, + .y = Y3, + .z = Z3, + }; + ret.cMov(p, @boolToInt(q.x.isZero())); + return ret; + } + + /// Add P384 points. + // Algorithm 4 from https://eprint.iacr.org/2015/1060.pdf + pub fn add(p: P384, q: P384) P384 { + var t0 = p.x.mul(q.x); + var t1 = p.y.mul(q.y); + var t2 = p.z.mul(q.z); + var t3 = p.x.add(p.y); + var t4 = q.x.add(q.y); + t3 = t3.mul(t4); + t4 = t0.add(t1); + t3 = t3.sub(t4); + t4 = p.y.add(p.z); + var X3 = q.y.add(q.z); + t4 = t4.mul(X3); + X3 = t1.add(t2); + t4 = t4.sub(X3); + X3 = p.x.add(p.z); + var Y3 = q.x.add(q.z); + X3 = X3.mul(Y3); + Y3 = t0.add(t2); + Y3 = X3.sub(Y3); + var Z3 = B.mul(t2); + X3 = Y3.sub(Z3); + Z3 = X3.dbl(); + X3 = X3.add(Z3); + Z3 = t1.sub(X3); + X3 = t1.add(X3); + Y3 = B.mul(Y3); + t1 = t2.dbl(); + t2 = t1.add(t2); + Y3 = Y3.sub(t2); + Y3 = Y3.sub(t0); + t1 = Y3.dbl(); + Y3 = t1.add(Y3); + t1 = t0.dbl(); + t0 = t1.add(t0); + t0 = t0.sub(t2); + t1 = t4.mul(Y3); + t2 = t0.mul(Y3); + Y3 = X3.mul(Z3); + Y3 = Y3.add(t2); + X3 = t3.mul(X3); + X3 = X3.sub(t1); + Z3 = t4.mul(Z3); + t1 = t3.mul(t0); + Z3 = Z3.add(t1); + return .{ + .x = X3, + .y = Y3, + .z = Z3, + }; + } + + /// Subtract P384 points. + pub fn sub(p: P384, q: P384) P384 { + return p.add(q.neg()); + } + + /// Subtract P384 points, the second being specified using affine coordinates. + pub fn subMixed(p: P384, q: AffineCoordinates) P384 { + return p.addMixed(q.neg()); + } + + /// Return affine coordinates. + pub fn affineCoordinates(p: P384) AffineCoordinates { + const zinv = p.z.invert(); + var ret = AffineCoordinates{ + .x = p.x.mul(zinv), + .y = p.y.mul(zinv), + }; + ret.cMov(AffineCoordinates.identityElement, @boolToInt(p.x.isZero())); + return ret; + } + + /// Return true if both coordinate sets represent the same point. + pub fn equivalent(a: P384, b: P384) bool { + if (a.sub(b).rejectIdentity()) { + return false; + } else |_| { + return true; + } + } + + fn cMov(p: *P384, a: P384, c: u1) void { + p.x.cMov(a.x, c); + p.y.cMov(a.y, c); + p.z.cMov(a.z, c); + } + + fn pcSelect(comptime n: usize, pc: *const [n]P384, b: u8) P384 { + var t = P384.identityElement; + comptime var i: u8 = 1; + inline while (i < pc.len) : (i += 1) { + t.cMov(pc[i], @truncate(u1, (@as(usize, b ^ i) -% 1) >> 8)); + } + return t; + } + + fn slide(s: [48]u8) [2 * 48 + 1]i8 { + var e: [2 * 48 + 1]i8 = undefined; + for (s) |x, i| { + e[i * 2 + 0] = @as(i8, @truncate(u4, x)); + e[i * 2 + 1] = @as(i8, @truncate(u4, x >> 4)); + } + // Now, e[0..63] is between 0 and 15, e[63] is between 0 and 7 + var carry: i8 = 0; + for (e[0..96]) |*x| { + x.* += carry; + carry = (x.* + 8) >> 4; + x.* -= carry * 16; + std.debug.assert(x.* >= -8 and x.* <= 8); + } + e[96] = carry; + // Now, e[*] is between -8 and 8, including e[64] + std.debug.assert(carry >= -8 and carry <= 8); + return e; + } + + fn pcMul(pc: *const [9]P384, s: [48]u8, comptime vartime: bool) IdentityElementError!P384 { + std.debug.assert(vartime); + const e = slide(s); + var q = P384.identityElement; + var pos = e.len - 1; + while (true) : (pos -= 1) { + const slot = e[pos]; + if (slot > 0) { + q = q.add(pc[@intCast(usize, slot)]); + } else if (slot < 0) { + q = q.sub(pc[@intCast(usize, -slot)]); + } + if (pos == 0) break; + q = q.dbl().dbl().dbl().dbl(); + } + try q.rejectIdentity(); + return q; + } + + fn pcMul16(pc: *const [16]P384, s: [48]u8, comptime vartime: bool) IdentityElementError!P384 { + var q = P384.identityElement; + var pos: usize = 380; + while (true) : (pos -= 4) { + const slot = @truncate(u4, (s[pos >> 3] >> @truncate(u3, pos))); + if (vartime) { + if (slot != 0) { + q = q.add(pc[slot]); + } + } else { + q = q.add(pcSelect(16, pc, slot)); + } + if (pos == 0) break; + q = q.dbl().dbl().dbl().dbl(); + } + try q.rejectIdentity(); + return q; + } + + fn precompute(p: P384, comptime count: usize) [1 + count]P384 { + var pc: [1 + count]P384 = undefined; + pc[0] = P384.identityElement; + pc[1] = p; + var i: usize = 2; + while (i <= count) : (i += 1) { + pc[i] = if (i % 2 == 0) pc[i / 2].dbl() else pc[i - 1].add(p); + } + return pc; + } + + const basePointPc = pc: { + @setEvalBranchQuota(50000); + break :pc precompute(P384.basePoint, 15); + }; + + /// Multiply an elliptic curve point by a scalar. + /// Return error.IdentityElement if the result is the identity element. + pub fn mul(p: P384, s_: [48]u8, endian: std.builtin.Endian) IdentityElementError!P384 { + const s = if (endian == .Little) s_ else Fe.orderSwap(s_); + if (p.is_base) { + return pcMul16(&basePointPc, s, false); + } + try p.rejectIdentity(); + const pc = precompute(p, 15); + return pcMul16(&pc, s, false); + } + + /// Multiply an elliptic curve point by a *PUBLIC* scalar *IN VARIABLE TIME* + /// This can be used for signature verification. + pub fn mulPublic(p: P384, s_: [48]u8, endian: std.builtin.Endian) IdentityElementError!P384 { + const s = if (endian == .Little) s_ else Fe.orderSwap(s_); + if (p.is_base) { + return pcMul16(&basePointPc, s, true); + } + try p.rejectIdentity(); + const pc = precompute(p, 8); + return pcMul(&pc, s, true); + } + + /// Double-base multiplication of public parameters - Compute (p1*s1)+(p2*s2) *IN VARIABLE TIME* + /// This can be used for signature verification. + pub fn mulDoubleBasePublic(p1: P384, s1_: [48]u8, p2: P384, s2_: [48]u8, endian: std.builtin.Endian) IdentityElementError!P384 { + const s1 = if (endian == .Little) s1_ else Fe.orderSwap(s1_); + const s2 = if (endian == .Little) s2_ else Fe.orderSwap(s2_); + try p1.rejectIdentity(); + var pc1_array: [9]P384 = undefined; + const pc1 = if (p1.is_base) basePointPc[0..9] else pc: { + pc1_array = precompute(p1, 8); + break :pc &pc1_array; + }; + try p2.rejectIdentity(); + var pc2_array: [9]P384 = undefined; + const pc2 = if (p2.is_base) basePointPc[0..9] else pc: { + pc2_array = precompute(p2, 8); + break :pc &pc2_array; + }; + const e1 = slide(s1); + const e2 = slide(s2); + var q = P384.identityElement; + var pos: usize = 2 * 48 - 1; + while (true) : (pos -= 1) { + const slot1 = e1[pos]; + if (slot1 > 0) { + q = q.add(pc1[@intCast(usize, slot1)]); + } else if (slot1 < 0) { + q = q.sub(pc1[@intCast(usize, -slot1)]); + } + const slot2 = e2[pos]; + if (slot2 > 0) { + q = q.add(pc2[@intCast(usize, slot2)]); + } else if (slot2 < 0) { + q = q.sub(pc2[@intCast(usize, -slot2)]); + } + if (pos == 0) break; + q = q.dbl().dbl().dbl().dbl(); + } + try q.rejectIdentity(); + return q; + } +}; + +/// A point in affine coordinates. +pub const AffineCoordinates = struct { + x: P384.Fe, + y: P384.Fe, + + /// Identity element in affine coordinates. + pub const identityElement = AffineCoordinates{ .x = P384.identityElement.x, .y = P384.identityElement.y }; + + fn cMov(p: *AffineCoordinates, a: AffineCoordinates, c: u1) void { + p.x.cMov(a.x, c); + p.y.cMov(a.y, c); + } +}; + +test "p384" { + _ = @import("tests/p384.zig"); +} diff --git a/lib/std/crypto/pcurves/p384/field.zig b/lib/std/crypto/pcurves/p384/field.zig new file mode 100644 index 0000000000..b05a4349be --- /dev/null +++ b/lib/std/crypto/pcurves/p384/field.zig @@ -0,0 +1,12 @@ +const std = @import("std"); +const common = @import("../common.zig"); + +const Field = common.Field; + +pub const Fe = Field(.{ + .fiat = @import("p384_64.zig"), + .field_order = 39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319, + .field_bits = 384, + .saturated_bits = 384, + .encoded_length = 48, +}); diff --git a/lib/std/crypto/pcurves/p384/p384_64.zig b/lib/std/crypto/pcurves/p384/p384_64.zig new file mode 100644 index 0000000000..bd39fc527a --- /dev/null +++ b/lib/std/crypto/pcurves/p384/p384_64.zig @@ -0,0 +1,3578 @@ +// Autogenerated: 'src/ExtractionOCaml/word_by_word_montgomery' --lang Zig --internal-static --public-function-case camelCase --private-function-case camelCase --public-type-case UpperCamelCase --private-type-case UpperCamelCase --no-prefix-fiat --package-name p384 '' 64 '2^384 - 2^128 - 2^96 + 2^32 - 1' mul square add sub opp from_montgomery to_montgomery nonzero selectznz to_bytes from_bytes one msat divstep divstep_precomp +// curve description (via package name): p384 +// machine_wordsize = 64 (from "64") +// requested operations: mul, square, add, sub, opp, from_montgomery, to_montgomery, nonzero, selectznz, to_bytes, from_bytes, one, msat, divstep, divstep_precomp +// m = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff (from "2^384 - 2^128 - 2^96 + 2^32 - 1") +// +// NOTE: In addition to the bounds specified above each function, all +// functions synthesized for this Montgomery arithmetic require the +// input to be strictly less than the prime modulus (m), and also +// require the input to be in the unique saturated representation. +// All functions also ensure that these two properties are true of +// return values. +// +// Computed values: +// eval z = z[0] + (z[1] << 64) + (z[2] << 128) + (z[3] << 192) + (z[4] << 256) + (z[5] << 0x140) +// bytes_eval z = z[0] + (z[1] << 8) + (z[2] << 16) + (z[3] << 24) + (z[4] << 32) + (z[5] << 40) + (z[6] << 48) + (z[7] << 56) + (z[8] << 64) + (z[9] << 72) + (z[10] << 80) + (z[11] << 88) + (z[12] << 96) + (z[13] << 104) + (z[14] << 112) + (z[15] << 120) + (z[16] << 128) + (z[17] << 136) + (z[18] << 144) + (z[19] << 152) + (z[20] << 160) + (z[21] << 168) + (z[22] << 176) + (z[23] << 184) + (z[24] << 192) + (z[25] << 200) + (z[26] << 208) + (z[27] << 216) + (z[28] << 224) + (z[29] << 232) + (z[30] << 240) + (z[31] << 248) + (z[32] << 256) + (z[33] << 0x108) + (z[34] << 0x110) + (z[35] << 0x118) + (z[36] << 0x120) + (z[37] << 0x128) + (z[38] << 0x130) + (z[39] << 0x138) + (z[40] << 0x140) + (z[41] << 0x148) + (z[42] << 0x150) + (z[43] << 0x158) + (z[44] << 0x160) + (z[45] << 0x168) + (z[46] << 0x170) + (z[47] << 0x178) +// twos_complement_eval z = let x1 := z[0] + (z[1] << 64) + (z[2] << 128) + (z[3] << 192) + (z[4] << 256) + (z[5] << 0x140) in +// if x1 & (2^384-1) < 2^383 then x1 & (2^384-1) else (x1 & (2^384-1)) - 2^384 + +const std = @import("std"); +const mode = @import("builtin").mode; // Checked arithmetic is disabled in non-debug modes to avoid side channels + +// The type MontgomeryDomainFieldElement is a field element in the Montgomery domain. +// Bounds: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub const MontgomeryDomainFieldElement = [6]u64; + +// The type NonMontgomeryDomainFieldElement is a field element NOT in the Montgomery domain. +// Bounds: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub const NonMontgomeryDomainFieldElement = [6]u64; + +/// The function addcarryxU64 is an addition with carry. +/// +/// Postconditions: +/// out1 = (arg1 + arg2 + arg3) mod 2^64 +/// out2 = ⌊(arg1 + arg2 + arg3) / 2^64⌋ +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// arg3: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [0x0 ~> 0x1] +inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { + @setRuntimeSafety(mode == .Debug); + + var t: u64 = undefined; + const carry1 = @addWithOverflow(u64, arg2, arg3, &t); + const carry2 = @addWithOverflow(u64, t, arg1, out1); + out2.* = @boolToInt(carry1) | @boolToInt(carry2); +} + +/// The function subborrowxU64 is a subtraction with borrow. +/// +/// Postconditions: +/// out1 = (-arg1 + arg2 + -arg3) mod 2^64 +/// out2 = -⌊(-arg1 + arg2 + -arg3) / 2^64⌋ +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// arg3: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [0x0 ~> 0x1] +inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { + @setRuntimeSafety(mode == .Debug); + + var t: u64 = undefined; + const carry1 = @subWithOverflow(u64, arg2, arg3, &t); + const carry2 = @subWithOverflow(u64, t, arg1, out1); + out2.* = @boolToInt(carry1) | @boolToInt(carry2); +} + +/// The function mulxU64 is a multiplication, returning the full double-width result. +/// +/// Postconditions: +/// out1 = (arg1 * arg2) mod 2^64 +/// out2 = ⌊arg1 * arg2 / 2^64⌋ +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0xffffffffffffffff] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [0x0 ~> 0xffffffffffffffff] +inline fn mulxU64(out1: *u64, out2: *u64, arg1: u64, arg2: u64) void { + @setRuntimeSafety(mode == .Debug); + + const x = @as(u128, arg1) * @as(u128, arg2); + out1.* = @truncate(u64, x); + out2.* = @truncate(u64, x >> 64); +} + +/// The function cmovznzU64 is a single-word conditional move. +/// +/// Postconditions: +/// out1 = (if arg1 = 0 then arg2 else arg3) +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// arg3: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +inline fn cmovznzU64(out1: *u64, arg1: u1, arg2: u64, arg3: u64) void { + @setRuntimeSafety(mode == .Debug); + + const mask = 0 -% @as(u64, arg1); + out1.* = (mask & arg3) | ((~mask) & arg2); +} + +/// The function mul multiplies two field elements in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// 0 ≤ eval arg2 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m +/// 0 ≤ eval out1 < m +/// +pub fn mul(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement, arg2: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[1]); + const x2 = (arg1[2]); + const x3 = (arg1[3]); + const x4 = (arg1[4]); + const x5 = (arg1[5]); + const x6 = (arg1[0]); + var x7: u64 = undefined; + var x8: u64 = undefined; + mulxU64(&x7, &x8, x6, (arg2[5])); + var x9: u64 = undefined; + var x10: u64 = undefined; + mulxU64(&x9, &x10, x6, (arg2[4])); + var x11: u64 = undefined; + var x12: u64 = undefined; + mulxU64(&x11, &x12, x6, (arg2[3])); + var x13: u64 = undefined; + var x14: u64 = undefined; + mulxU64(&x13, &x14, x6, (arg2[2])); + var x15: u64 = undefined; + var x16: u64 = undefined; + mulxU64(&x15, &x16, x6, (arg2[1])); + var x17: u64 = undefined; + var x18: u64 = undefined; + mulxU64(&x17, &x18, x6, (arg2[0])); + var x19: u64 = undefined; + var x20: u1 = undefined; + addcarryxU64(&x19, &x20, 0x0, x18, x15); + var x21: u64 = undefined; + var x22: u1 = undefined; + addcarryxU64(&x21, &x22, x20, x16, x13); + var x23: u64 = undefined; + var x24: u1 = undefined; + addcarryxU64(&x23, &x24, x22, x14, x11); + var x25: u64 = undefined; + var x26: u1 = undefined; + addcarryxU64(&x25, &x26, x24, x12, x9); + var x27: u64 = undefined; + var x28: u1 = undefined; + addcarryxU64(&x27, &x28, x26, x10, x7); + const x29 = (@as(u64, x28) + x8); + var x30: u64 = undefined; + var x31: u64 = undefined; + mulxU64(&x30, &x31, x17, 0x100000001); + var x32: u64 = undefined; + var x33: u64 = undefined; + mulxU64(&x32, &x33, x30, 0xffffffffffffffff); + var x34: u64 = undefined; + var x35: u64 = undefined; + mulxU64(&x34, &x35, x30, 0xffffffffffffffff); + var x36: u64 = undefined; + var x37: u64 = undefined; + mulxU64(&x36, &x37, x30, 0xffffffffffffffff); + var x38: u64 = undefined; + var x39: u64 = undefined; + mulxU64(&x38, &x39, x30, 0xfffffffffffffffe); + var x40: u64 = undefined; + var x41: u64 = undefined; + mulxU64(&x40, &x41, x30, 0xffffffff00000000); + var x42: u64 = undefined; + var x43: u64 = undefined; + mulxU64(&x42, &x43, x30, 0xffffffff); + var x44: u64 = undefined; + var x45: u1 = undefined; + addcarryxU64(&x44, &x45, 0x0, x43, x40); + var x46: u64 = undefined; + var x47: u1 = undefined; + addcarryxU64(&x46, &x47, x45, x41, x38); + var x48: u64 = undefined; + var x49: u1 = undefined; + addcarryxU64(&x48, &x49, x47, x39, x36); + var x50: u64 = undefined; + var x51: u1 = undefined; + addcarryxU64(&x50, &x51, x49, x37, x34); + var x52: u64 = undefined; + var x53: u1 = undefined; + addcarryxU64(&x52, &x53, x51, x35, x32); + const x54 = (@as(u64, x53) + x33); + var x55: u64 = undefined; + var x56: u1 = undefined; + addcarryxU64(&x55, &x56, 0x0, x17, x42); + var x57: u64 = undefined; + var x58: u1 = undefined; + addcarryxU64(&x57, &x58, x56, x19, x44); + var x59: u64 = undefined; + var x60: u1 = undefined; + addcarryxU64(&x59, &x60, x58, x21, x46); + var x61: u64 = undefined; + var x62: u1 = undefined; + addcarryxU64(&x61, &x62, x60, x23, x48); + var x63: u64 = undefined; + var x64: u1 = undefined; + addcarryxU64(&x63, &x64, x62, x25, x50); + var x65: u64 = undefined; + var x66: u1 = undefined; + addcarryxU64(&x65, &x66, x64, x27, x52); + var x67: u64 = undefined; + var x68: u1 = undefined; + addcarryxU64(&x67, &x68, x66, x29, x54); + var x69: u64 = undefined; + var x70: u64 = undefined; + mulxU64(&x69, &x70, x1, (arg2[5])); + var x71: u64 = undefined; + var x72: u64 = undefined; + mulxU64(&x71, &x72, x1, (arg2[4])); + var x73: u64 = undefined; + var x74: u64 = undefined; + mulxU64(&x73, &x74, x1, (arg2[3])); + var x75: u64 = undefined; + var x76: u64 = undefined; + mulxU64(&x75, &x76, x1, (arg2[2])); + var x77: u64 = undefined; + var x78: u64 = undefined; + mulxU64(&x77, &x78, x1, (arg2[1])); + var x79: u64 = undefined; + var x80: u64 = undefined; + mulxU64(&x79, &x80, x1, (arg2[0])); + var x81: u64 = undefined; + var x82: u1 = undefined; + addcarryxU64(&x81, &x82, 0x0, x80, x77); + var x83: u64 = undefined; + var x84: u1 = undefined; + addcarryxU64(&x83, &x84, x82, x78, x75); + var x85: u64 = undefined; + var x86: u1 = undefined; + addcarryxU64(&x85, &x86, x84, x76, x73); + var x87: u64 = undefined; + var x88: u1 = undefined; + addcarryxU64(&x87, &x88, x86, x74, x71); + var x89: u64 = undefined; + var x90: u1 = undefined; + addcarryxU64(&x89, &x90, x88, x72, x69); + const x91 = (@as(u64, x90) + x70); + var x92: u64 = undefined; + var x93: u1 = undefined; + addcarryxU64(&x92, &x93, 0x0, x57, x79); + var x94: u64 = undefined; + var x95: u1 = undefined; + addcarryxU64(&x94, &x95, x93, x59, x81); + var x96: u64 = undefined; + var x97: u1 = undefined; + addcarryxU64(&x96, &x97, x95, x61, x83); + var x98: u64 = undefined; + var x99: u1 = undefined; + addcarryxU64(&x98, &x99, x97, x63, x85); + var x100: u64 = undefined; + var x101: u1 = undefined; + addcarryxU64(&x100, &x101, x99, x65, x87); + var x102: u64 = undefined; + var x103: u1 = undefined; + addcarryxU64(&x102, &x103, x101, x67, x89); + var x104: u64 = undefined; + var x105: u1 = undefined; + addcarryxU64(&x104, &x105, x103, @as(u64, x68), x91); + var x106: u64 = undefined; + var x107: u64 = undefined; + mulxU64(&x106, &x107, x92, 0x100000001); + var x108: u64 = undefined; + var x109: u64 = undefined; + mulxU64(&x108, &x109, x106, 0xffffffffffffffff); + var x110: u64 = undefined; + var x111: u64 = undefined; + mulxU64(&x110, &x111, x106, 0xffffffffffffffff); + var x112: u64 = undefined; + var x113: u64 = undefined; + mulxU64(&x112, &x113, x106, 0xffffffffffffffff); + var x114: u64 = undefined; + var x115: u64 = undefined; + mulxU64(&x114, &x115, x106, 0xfffffffffffffffe); + var x116: u64 = undefined; + var x117: u64 = undefined; + mulxU64(&x116, &x117, x106, 0xffffffff00000000); + var x118: u64 = undefined; + var x119: u64 = undefined; + mulxU64(&x118, &x119, x106, 0xffffffff); + var x120: u64 = undefined; + var x121: u1 = undefined; + addcarryxU64(&x120, &x121, 0x0, x119, x116); + var x122: u64 = undefined; + var x123: u1 = undefined; + addcarryxU64(&x122, &x123, x121, x117, x114); + var x124: u64 = undefined; + var x125: u1 = undefined; + addcarryxU64(&x124, &x125, x123, x115, x112); + var x126: u64 = undefined; + var x127: u1 = undefined; + addcarryxU64(&x126, &x127, x125, x113, x110); + var x128: u64 = undefined; + var x129: u1 = undefined; + addcarryxU64(&x128, &x129, x127, x111, x108); + const x130 = (@as(u64, x129) + x109); + var x131: u64 = undefined; + var x132: u1 = undefined; + addcarryxU64(&x131, &x132, 0x0, x92, x118); + var x133: u64 = undefined; + var x134: u1 = undefined; + addcarryxU64(&x133, &x134, x132, x94, x120); + var x135: u64 = undefined; + var x136: u1 = undefined; + addcarryxU64(&x135, &x136, x134, x96, x122); + var x137: u64 = undefined; + var x138: u1 = undefined; + addcarryxU64(&x137, &x138, x136, x98, x124); + var x139: u64 = undefined; + var x140: u1 = undefined; + addcarryxU64(&x139, &x140, x138, x100, x126); + var x141: u64 = undefined; + var x142: u1 = undefined; + addcarryxU64(&x141, &x142, x140, x102, x128); + var x143: u64 = undefined; + var x144: u1 = undefined; + addcarryxU64(&x143, &x144, x142, x104, x130); + const x145 = (@as(u64, x144) + @as(u64, x105)); + var x146: u64 = undefined; + var x147: u64 = undefined; + mulxU64(&x146, &x147, x2, (arg2[5])); + var x148: u64 = undefined; + var x149: u64 = undefined; + mulxU64(&x148, &x149, x2, (arg2[4])); + var x150: u64 = undefined; + var x151: u64 = undefined; + mulxU64(&x150, &x151, x2, (arg2[3])); + var x152: u64 = undefined; + var x153: u64 = undefined; + mulxU64(&x152, &x153, x2, (arg2[2])); + var x154: u64 = undefined; + var x155: u64 = undefined; + mulxU64(&x154, &x155, x2, (arg2[1])); + var x156: u64 = undefined; + var x157: u64 = undefined; + mulxU64(&x156, &x157, x2, (arg2[0])); + var x158: u64 = undefined; + var x159: u1 = undefined; + addcarryxU64(&x158, &x159, 0x0, x157, x154); + var x160: u64 = undefined; + var x161: u1 = undefined; + addcarryxU64(&x160, &x161, x159, x155, x152); + var x162: u64 = undefined; + var x163: u1 = undefined; + addcarryxU64(&x162, &x163, x161, x153, x150); + var x164: u64 = undefined; + var x165: u1 = undefined; + addcarryxU64(&x164, &x165, x163, x151, x148); + var x166: u64 = undefined; + var x167: u1 = undefined; + addcarryxU64(&x166, &x167, x165, x149, x146); + const x168 = (@as(u64, x167) + x147); + var x169: u64 = undefined; + var x170: u1 = undefined; + addcarryxU64(&x169, &x170, 0x0, x133, x156); + var x171: u64 = undefined; + var x172: u1 = undefined; + addcarryxU64(&x171, &x172, x170, x135, x158); + var x173: u64 = undefined; + var x174: u1 = undefined; + addcarryxU64(&x173, &x174, x172, x137, x160); + var x175: u64 = undefined; + var x176: u1 = undefined; + addcarryxU64(&x175, &x176, x174, x139, x162); + var x177: u64 = undefined; + var x178: u1 = undefined; + addcarryxU64(&x177, &x178, x176, x141, x164); + var x179: u64 = undefined; + var x180: u1 = undefined; + addcarryxU64(&x179, &x180, x178, x143, x166); + var x181: u64 = undefined; + var x182: u1 = undefined; + addcarryxU64(&x181, &x182, x180, x145, x168); + var x183: u64 = undefined; + var x184: u64 = undefined; + mulxU64(&x183, &x184, x169, 0x100000001); + var x185: u64 = undefined; + var x186: u64 = undefined; + mulxU64(&x185, &x186, x183, 0xffffffffffffffff); + var x187: u64 = undefined; + var x188: u64 = undefined; + mulxU64(&x187, &x188, x183, 0xffffffffffffffff); + var x189: u64 = undefined; + var x190: u64 = undefined; + mulxU64(&x189, &x190, x183, 0xffffffffffffffff); + var x191: u64 = undefined; + var x192: u64 = undefined; + mulxU64(&x191, &x192, x183, 0xfffffffffffffffe); + var x193: u64 = undefined; + var x194: u64 = undefined; + mulxU64(&x193, &x194, x183, 0xffffffff00000000); + var x195: u64 = undefined; + var x196: u64 = undefined; + mulxU64(&x195, &x196, x183, 0xffffffff); + var x197: u64 = undefined; + var x198: u1 = undefined; + addcarryxU64(&x197, &x198, 0x0, x196, x193); + var x199: u64 = undefined; + var x200: u1 = undefined; + addcarryxU64(&x199, &x200, x198, x194, x191); + var x201: u64 = undefined; + var x202: u1 = undefined; + addcarryxU64(&x201, &x202, x200, x192, x189); + var x203: u64 = undefined; + var x204: u1 = undefined; + addcarryxU64(&x203, &x204, x202, x190, x187); + var x205: u64 = undefined; + var x206: u1 = undefined; + addcarryxU64(&x205, &x206, x204, x188, x185); + const x207 = (@as(u64, x206) + x186); + var x208: u64 = undefined; + var x209: u1 = undefined; + addcarryxU64(&x208, &x209, 0x0, x169, x195); + var x210: u64 = undefined; + var x211: u1 = undefined; + addcarryxU64(&x210, &x211, x209, x171, x197); + var x212: u64 = undefined; + var x213: u1 = undefined; + addcarryxU64(&x212, &x213, x211, x173, x199); + var x214: u64 = undefined; + var x215: u1 = undefined; + addcarryxU64(&x214, &x215, x213, x175, x201); + var x216: u64 = undefined; + var x217: u1 = undefined; + addcarryxU64(&x216, &x217, x215, x177, x203); + var x218: u64 = undefined; + var x219: u1 = undefined; + addcarryxU64(&x218, &x219, x217, x179, x205); + var x220: u64 = undefined; + var x221: u1 = undefined; + addcarryxU64(&x220, &x221, x219, x181, x207); + const x222 = (@as(u64, x221) + @as(u64, x182)); + var x223: u64 = undefined; + var x224: u64 = undefined; + mulxU64(&x223, &x224, x3, (arg2[5])); + var x225: u64 = undefined; + var x226: u64 = undefined; + mulxU64(&x225, &x226, x3, (arg2[4])); + var x227: u64 = undefined; + var x228: u64 = undefined; + mulxU64(&x227, &x228, x3, (arg2[3])); + var x229: u64 = undefined; + var x230: u64 = undefined; + mulxU64(&x229, &x230, x3, (arg2[2])); + var x231: u64 = undefined; + var x232: u64 = undefined; + mulxU64(&x231, &x232, x3, (arg2[1])); + var x233: u64 = undefined; + var x234: u64 = undefined; + mulxU64(&x233, &x234, x3, (arg2[0])); + var x235: u64 = undefined; + var x236: u1 = undefined; + addcarryxU64(&x235, &x236, 0x0, x234, x231); + var x237: u64 = undefined; + var x238: u1 = undefined; + addcarryxU64(&x237, &x238, x236, x232, x229); + var x239: u64 = undefined; + var x240: u1 = undefined; + addcarryxU64(&x239, &x240, x238, x230, x227); + var x241: u64 = undefined; + var x242: u1 = undefined; + addcarryxU64(&x241, &x242, x240, x228, x225); + var x243: u64 = undefined; + var x244: u1 = undefined; + addcarryxU64(&x243, &x244, x242, x226, x223); + const x245 = (@as(u64, x244) + x224); + var x246: u64 = undefined; + var x247: u1 = undefined; + addcarryxU64(&x246, &x247, 0x0, x210, x233); + var x248: u64 = undefined; + var x249: u1 = undefined; + addcarryxU64(&x248, &x249, x247, x212, x235); + var x250: u64 = undefined; + var x251: u1 = undefined; + addcarryxU64(&x250, &x251, x249, x214, x237); + var x252: u64 = undefined; + var x253: u1 = undefined; + addcarryxU64(&x252, &x253, x251, x216, x239); + var x254: u64 = undefined; + var x255: u1 = undefined; + addcarryxU64(&x254, &x255, x253, x218, x241); + var x256: u64 = undefined; + var x257: u1 = undefined; + addcarryxU64(&x256, &x257, x255, x220, x243); + var x258: u64 = undefined; + var x259: u1 = undefined; + addcarryxU64(&x258, &x259, x257, x222, x245); + var x260: u64 = undefined; + var x261: u64 = undefined; + mulxU64(&x260, &x261, x246, 0x100000001); + var x262: u64 = undefined; + var x263: u64 = undefined; + mulxU64(&x262, &x263, x260, 0xffffffffffffffff); + var x264: u64 = undefined; + var x265: u64 = undefined; + mulxU64(&x264, &x265, x260, 0xffffffffffffffff); + var x266: u64 = undefined; + var x267: u64 = undefined; + mulxU64(&x266, &x267, x260, 0xffffffffffffffff); + var x268: u64 = undefined; + var x269: u64 = undefined; + mulxU64(&x268, &x269, x260, 0xfffffffffffffffe); + var x270: u64 = undefined; + var x271: u64 = undefined; + mulxU64(&x270, &x271, x260, 0xffffffff00000000); + var x272: u64 = undefined; + var x273: u64 = undefined; + mulxU64(&x272, &x273, x260, 0xffffffff); + var x274: u64 = undefined; + var x275: u1 = undefined; + addcarryxU64(&x274, &x275, 0x0, x273, x270); + var x276: u64 = undefined; + var x277: u1 = undefined; + addcarryxU64(&x276, &x277, x275, x271, x268); + var x278: u64 = undefined; + var x279: u1 = undefined; + addcarryxU64(&x278, &x279, x277, x269, x266); + var x280: u64 = undefined; + var x281: u1 = undefined; + addcarryxU64(&x280, &x281, x279, x267, x264); + var x282: u64 = undefined; + var x283: u1 = undefined; + addcarryxU64(&x282, &x283, x281, x265, x262); + const x284 = (@as(u64, x283) + x263); + var x285: u64 = undefined; + var x286: u1 = undefined; + addcarryxU64(&x285, &x286, 0x0, x246, x272); + var x287: u64 = undefined; + var x288: u1 = undefined; + addcarryxU64(&x287, &x288, x286, x248, x274); + var x289: u64 = undefined; + var x290: u1 = undefined; + addcarryxU64(&x289, &x290, x288, x250, x276); + var x291: u64 = undefined; + var x292: u1 = undefined; + addcarryxU64(&x291, &x292, x290, x252, x278); + var x293: u64 = undefined; + var x294: u1 = undefined; + addcarryxU64(&x293, &x294, x292, x254, x280); + var x295: u64 = undefined; + var x296: u1 = undefined; + addcarryxU64(&x295, &x296, x294, x256, x282); + var x297: u64 = undefined; + var x298: u1 = undefined; + addcarryxU64(&x297, &x298, x296, x258, x284); + const x299 = (@as(u64, x298) + @as(u64, x259)); + var x300: u64 = undefined; + var x301: u64 = undefined; + mulxU64(&x300, &x301, x4, (arg2[5])); + var x302: u64 = undefined; + var x303: u64 = undefined; + mulxU64(&x302, &x303, x4, (arg2[4])); + var x304: u64 = undefined; + var x305: u64 = undefined; + mulxU64(&x304, &x305, x4, (arg2[3])); + var x306: u64 = undefined; + var x307: u64 = undefined; + mulxU64(&x306, &x307, x4, (arg2[2])); + var x308: u64 = undefined; + var x309: u64 = undefined; + mulxU64(&x308, &x309, x4, (arg2[1])); + var x310: u64 = undefined; + var x311: u64 = undefined; + mulxU64(&x310, &x311, x4, (arg2[0])); + var x312: u64 = undefined; + var x313: u1 = undefined; + addcarryxU64(&x312, &x313, 0x0, x311, x308); + var x314: u64 = undefined; + var x315: u1 = undefined; + addcarryxU64(&x314, &x315, x313, x309, x306); + var x316: u64 = undefined; + var x317: u1 = undefined; + addcarryxU64(&x316, &x317, x315, x307, x304); + var x318: u64 = undefined; + var x319: u1 = undefined; + addcarryxU64(&x318, &x319, x317, x305, x302); + var x320: u64 = undefined; + var x321: u1 = undefined; + addcarryxU64(&x320, &x321, x319, x303, x300); + const x322 = (@as(u64, x321) + x301); + var x323: u64 = undefined; + var x324: u1 = undefined; + addcarryxU64(&x323, &x324, 0x0, x287, x310); + var x325: u64 = undefined; + var x326: u1 = undefined; + addcarryxU64(&x325, &x326, x324, x289, x312); + var x327: u64 = undefined; + var x328: u1 = undefined; + addcarryxU64(&x327, &x328, x326, x291, x314); + var x329: u64 = undefined; + var x330: u1 = undefined; + addcarryxU64(&x329, &x330, x328, x293, x316); + var x331: u64 = undefined; + var x332: u1 = undefined; + addcarryxU64(&x331, &x332, x330, x295, x318); + var x333: u64 = undefined; + var x334: u1 = undefined; + addcarryxU64(&x333, &x334, x332, x297, x320); + var x335: u64 = undefined; + var x336: u1 = undefined; + addcarryxU64(&x335, &x336, x334, x299, x322); + var x337: u64 = undefined; + var x338: u64 = undefined; + mulxU64(&x337, &x338, x323, 0x100000001); + var x339: u64 = undefined; + var x340: u64 = undefined; + mulxU64(&x339, &x340, x337, 0xffffffffffffffff); + var x341: u64 = undefined; + var x342: u64 = undefined; + mulxU64(&x341, &x342, x337, 0xffffffffffffffff); + var x343: u64 = undefined; + var x344: u64 = undefined; + mulxU64(&x343, &x344, x337, 0xffffffffffffffff); + var x345: u64 = undefined; + var x346: u64 = undefined; + mulxU64(&x345, &x346, x337, 0xfffffffffffffffe); + var x347: u64 = undefined; + var x348: u64 = undefined; + mulxU64(&x347, &x348, x337, 0xffffffff00000000); + var x349: u64 = undefined; + var x350: u64 = undefined; + mulxU64(&x349, &x350, x337, 0xffffffff); + var x351: u64 = undefined; + var x352: u1 = undefined; + addcarryxU64(&x351, &x352, 0x0, x350, x347); + var x353: u64 = undefined; + var x354: u1 = undefined; + addcarryxU64(&x353, &x354, x352, x348, x345); + var x355: u64 = undefined; + var x356: u1 = undefined; + addcarryxU64(&x355, &x356, x354, x346, x343); + var x357: u64 = undefined; + var x358: u1 = undefined; + addcarryxU64(&x357, &x358, x356, x344, x341); + var x359: u64 = undefined; + var x360: u1 = undefined; + addcarryxU64(&x359, &x360, x358, x342, x339); + const x361 = (@as(u64, x360) + x340); + var x362: u64 = undefined; + var x363: u1 = undefined; + addcarryxU64(&x362, &x363, 0x0, x323, x349); + var x364: u64 = undefined; + var x365: u1 = undefined; + addcarryxU64(&x364, &x365, x363, x325, x351); + var x366: u64 = undefined; + var x367: u1 = undefined; + addcarryxU64(&x366, &x367, x365, x327, x353); + var x368: u64 = undefined; + var x369: u1 = undefined; + addcarryxU64(&x368, &x369, x367, x329, x355); + var x370: u64 = undefined; + var x371: u1 = undefined; + addcarryxU64(&x370, &x371, x369, x331, x357); + var x372: u64 = undefined; + var x373: u1 = undefined; + addcarryxU64(&x372, &x373, x371, x333, x359); + var x374: u64 = undefined; + var x375: u1 = undefined; + addcarryxU64(&x374, &x375, x373, x335, x361); + const x376 = (@as(u64, x375) + @as(u64, x336)); + var x377: u64 = undefined; + var x378: u64 = undefined; + mulxU64(&x377, &x378, x5, (arg2[5])); + var x379: u64 = undefined; + var x380: u64 = undefined; + mulxU64(&x379, &x380, x5, (arg2[4])); + var x381: u64 = undefined; + var x382: u64 = undefined; + mulxU64(&x381, &x382, x5, (arg2[3])); + var x383: u64 = undefined; + var x384: u64 = undefined; + mulxU64(&x383, &x384, x5, (arg2[2])); + var x385: u64 = undefined; + var x386: u64 = undefined; + mulxU64(&x385, &x386, x5, (arg2[1])); + var x387: u64 = undefined; + var x388: u64 = undefined; + mulxU64(&x387, &x388, x5, (arg2[0])); + var x389: u64 = undefined; + var x390: u1 = undefined; + addcarryxU64(&x389, &x390, 0x0, x388, x385); + var x391: u64 = undefined; + var x392: u1 = undefined; + addcarryxU64(&x391, &x392, x390, x386, x383); + var x393: u64 = undefined; + var x394: u1 = undefined; + addcarryxU64(&x393, &x394, x392, x384, x381); + var x395: u64 = undefined; + var x396: u1 = undefined; + addcarryxU64(&x395, &x396, x394, x382, x379); + var x397: u64 = undefined; + var x398: u1 = undefined; + addcarryxU64(&x397, &x398, x396, x380, x377); + const x399 = (@as(u64, x398) + x378); + var x400: u64 = undefined; + var x401: u1 = undefined; + addcarryxU64(&x400, &x401, 0x0, x364, x387); + var x402: u64 = undefined; + var x403: u1 = undefined; + addcarryxU64(&x402, &x403, x401, x366, x389); + var x404: u64 = undefined; + var x405: u1 = undefined; + addcarryxU64(&x404, &x405, x403, x368, x391); + var x406: u64 = undefined; + var x407: u1 = undefined; + addcarryxU64(&x406, &x407, x405, x370, x393); + var x408: u64 = undefined; + var x409: u1 = undefined; + addcarryxU64(&x408, &x409, x407, x372, x395); + var x410: u64 = undefined; + var x411: u1 = undefined; + addcarryxU64(&x410, &x411, x409, x374, x397); + var x412: u64 = undefined; + var x413: u1 = undefined; + addcarryxU64(&x412, &x413, x411, x376, x399); + var x414: u64 = undefined; + var x415: u64 = undefined; + mulxU64(&x414, &x415, x400, 0x100000001); + var x416: u64 = undefined; + var x417: u64 = undefined; + mulxU64(&x416, &x417, x414, 0xffffffffffffffff); + var x418: u64 = undefined; + var x419: u64 = undefined; + mulxU64(&x418, &x419, x414, 0xffffffffffffffff); + var x420: u64 = undefined; + var x421: u64 = undefined; + mulxU64(&x420, &x421, x414, 0xffffffffffffffff); + var x422: u64 = undefined; + var x423: u64 = undefined; + mulxU64(&x422, &x423, x414, 0xfffffffffffffffe); + var x424: u64 = undefined; + var x425: u64 = undefined; + mulxU64(&x424, &x425, x414, 0xffffffff00000000); + var x426: u64 = undefined; + var x427: u64 = undefined; + mulxU64(&x426, &x427, x414, 0xffffffff); + var x428: u64 = undefined; + var x429: u1 = undefined; + addcarryxU64(&x428, &x429, 0x0, x427, x424); + var x430: u64 = undefined; + var x431: u1 = undefined; + addcarryxU64(&x430, &x431, x429, x425, x422); + var x432: u64 = undefined; + var x433: u1 = undefined; + addcarryxU64(&x432, &x433, x431, x423, x420); + var x434: u64 = undefined; + var x435: u1 = undefined; + addcarryxU64(&x434, &x435, x433, x421, x418); + var x436: u64 = undefined; + var x437: u1 = undefined; + addcarryxU64(&x436, &x437, x435, x419, x416); + const x438 = (@as(u64, x437) + x417); + var x439: u64 = undefined; + var x440: u1 = undefined; + addcarryxU64(&x439, &x440, 0x0, x400, x426); + var x441: u64 = undefined; + var x442: u1 = undefined; + addcarryxU64(&x441, &x442, x440, x402, x428); + var x443: u64 = undefined; + var x444: u1 = undefined; + addcarryxU64(&x443, &x444, x442, x404, x430); + var x445: u64 = undefined; + var x446: u1 = undefined; + addcarryxU64(&x445, &x446, x444, x406, x432); + var x447: u64 = undefined; + var x448: u1 = undefined; + addcarryxU64(&x447, &x448, x446, x408, x434); + var x449: u64 = undefined; + var x450: u1 = undefined; + addcarryxU64(&x449, &x450, x448, x410, x436); + var x451: u64 = undefined; + var x452: u1 = undefined; + addcarryxU64(&x451, &x452, x450, x412, x438); + const x453 = (@as(u64, x452) + @as(u64, x413)); + var x454: u64 = undefined; + var x455: u1 = undefined; + subborrowxU64(&x454, &x455, 0x0, x441, 0xffffffff); + var x456: u64 = undefined; + var x457: u1 = undefined; + subborrowxU64(&x456, &x457, x455, x443, 0xffffffff00000000); + var x458: u64 = undefined; + var x459: u1 = undefined; + subborrowxU64(&x458, &x459, x457, x445, 0xfffffffffffffffe); + var x460: u64 = undefined; + var x461: u1 = undefined; + subborrowxU64(&x460, &x461, x459, x447, 0xffffffffffffffff); + var x462: u64 = undefined; + var x463: u1 = undefined; + subborrowxU64(&x462, &x463, x461, x449, 0xffffffffffffffff); + var x464: u64 = undefined; + var x465: u1 = undefined; + subborrowxU64(&x464, &x465, x463, x451, 0xffffffffffffffff); + var x466: u64 = undefined; + var x467: u1 = undefined; + subborrowxU64(&x466, &x467, x465, x453, 0x0); + var x468: u64 = undefined; + cmovznzU64(&x468, x467, x454, x441); + var x469: u64 = undefined; + cmovznzU64(&x469, x467, x456, x443); + var x470: u64 = undefined; + cmovznzU64(&x470, x467, x458, x445); + var x471: u64 = undefined; + cmovznzU64(&x471, x467, x460, x447); + var x472: u64 = undefined; + cmovznzU64(&x472, x467, x462, x449); + var x473: u64 = undefined; + cmovznzU64(&x473, x467, x464, x451); + out1[0] = x468; + out1[1] = x469; + out1[2] = x470; + out1[3] = x471; + out1[4] = x472; + out1[5] = x473; +} + +/// The function square squares a field element in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg1)) mod m +/// 0 ≤ eval out1 < m +/// +pub fn square(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[1]); + const x2 = (arg1[2]); + const x3 = (arg1[3]); + const x4 = (arg1[4]); + const x5 = (arg1[5]); + const x6 = (arg1[0]); + var x7: u64 = undefined; + var x8: u64 = undefined; + mulxU64(&x7, &x8, x6, (arg1[5])); + var x9: u64 = undefined; + var x10: u64 = undefined; + mulxU64(&x9, &x10, x6, (arg1[4])); + var x11: u64 = undefined; + var x12: u64 = undefined; + mulxU64(&x11, &x12, x6, (arg1[3])); + var x13: u64 = undefined; + var x14: u64 = undefined; + mulxU64(&x13, &x14, x6, (arg1[2])); + var x15: u64 = undefined; + var x16: u64 = undefined; + mulxU64(&x15, &x16, x6, (arg1[1])); + var x17: u64 = undefined; + var x18: u64 = undefined; + mulxU64(&x17, &x18, x6, (arg1[0])); + var x19: u64 = undefined; + var x20: u1 = undefined; + addcarryxU64(&x19, &x20, 0x0, x18, x15); + var x21: u64 = undefined; + var x22: u1 = undefined; + addcarryxU64(&x21, &x22, x20, x16, x13); + var x23: u64 = undefined; + var x24: u1 = undefined; + addcarryxU64(&x23, &x24, x22, x14, x11); + var x25: u64 = undefined; + var x26: u1 = undefined; + addcarryxU64(&x25, &x26, x24, x12, x9); + var x27: u64 = undefined; + var x28: u1 = undefined; + addcarryxU64(&x27, &x28, x26, x10, x7); + const x29 = (@as(u64, x28) + x8); + var x30: u64 = undefined; + var x31: u64 = undefined; + mulxU64(&x30, &x31, x17, 0x100000001); + var x32: u64 = undefined; + var x33: u64 = undefined; + mulxU64(&x32, &x33, x30, 0xffffffffffffffff); + var x34: u64 = undefined; + var x35: u64 = undefined; + mulxU64(&x34, &x35, x30, 0xffffffffffffffff); + var x36: u64 = undefined; + var x37: u64 = undefined; + mulxU64(&x36, &x37, x30, 0xffffffffffffffff); + var x38: u64 = undefined; + var x39: u64 = undefined; + mulxU64(&x38, &x39, x30, 0xfffffffffffffffe); + var x40: u64 = undefined; + var x41: u64 = undefined; + mulxU64(&x40, &x41, x30, 0xffffffff00000000); + var x42: u64 = undefined; + var x43: u64 = undefined; + mulxU64(&x42, &x43, x30, 0xffffffff); + var x44: u64 = undefined; + var x45: u1 = undefined; + addcarryxU64(&x44, &x45, 0x0, x43, x40); + var x46: u64 = undefined; + var x47: u1 = undefined; + addcarryxU64(&x46, &x47, x45, x41, x38); + var x48: u64 = undefined; + var x49: u1 = undefined; + addcarryxU64(&x48, &x49, x47, x39, x36); + var x50: u64 = undefined; + var x51: u1 = undefined; + addcarryxU64(&x50, &x51, x49, x37, x34); + var x52: u64 = undefined; + var x53: u1 = undefined; + addcarryxU64(&x52, &x53, x51, x35, x32); + const x54 = (@as(u64, x53) + x33); + var x55: u64 = undefined; + var x56: u1 = undefined; + addcarryxU64(&x55, &x56, 0x0, x17, x42); + var x57: u64 = undefined; + var x58: u1 = undefined; + addcarryxU64(&x57, &x58, x56, x19, x44); + var x59: u64 = undefined; + var x60: u1 = undefined; + addcarryxU64(&x59, &x60, x58, x21, x46); + var x61: u64 = undefined; + var x62: u1 = undefined; + addcarryxU64(&x61, &x62, x60, x23, x48); + var x63: u64 = undefined; + var x64: u1 = undefined; + addcarryxU64(&x63, &x64, x62, x25, x50); + var x65: u64 = undefined; + var x66: u1 = undefined; + addcarryxU64(&x65, &x66, x64, x27, x52); + var x67: u64 = undefined; + var x68: u1 = undefined; + addcarryxU64(&x67, &x68, x66, x29, x54); + var x69: u64 = undefined; + var x70: u64 = undefined; + mulxU64(&x69, &x70, x1, (arg1[5])); + var x71: u64 = undefined; + var x72: u64 = undefined; + mulxU64(&x71, &x72, x1, (arg1[4])); + var x73: u64 = undefined; + var x74: u64 = undefined; + mulxU64(&x73, &x74, x1, (arg1[3])); + var x75: u64 = undefined; + var x76: u64 = undefined; + mulxU64(&x75, &x76, x1, (arg1[2])); + var x77: u64 = undefined; + var x78: u64 = undefined; + mulxU64(&x77, &x78, x1, (arg1[1])); + var x79: u64 = undefined; + var x80: u64 = undefined; + mulxU64(&x79, &x80, x1, (arg1[0])); + var x81: u64 = undefined; + var x82: u1 = undefined; + addcarryxU64(&x81, &x82, 0x0, x80, x77); + var x83: u64 = undefined; + var x84: u1 = undefined; + addcarryxU64(&x83, &x84, x82, x78, x75); + var x85: u64 = undefined; + var x86: u1 = undefined; + addcarryxU64(&x85, &x86, x84, x76, x73); + var x87: u64 = undefined; + var x88: u1 = undefined; + addcarryxU64(&x87, &x88, x86, x74, x71); + var x89: u64 = undefined; + var x90: u1 = undefined; + addcarryxU64(&x89, &x90, x88, x72, x69); + const x91 = (@as(u64, x90) + x70); + var x92: u64 = undefined; + var x93: u1 = undefined; + addcarryxU64(&x92, &x93, 0x0, x57, x79); + var x94: u64 = undefined; + var x95: u1 = undefined; + addcarryxU64(&x94, &x95, x93, x59, x81); + var x96: u64 = undefined; + var x97: u1 = undefined; + addcarryxU64(&x96, &x97, x95, x61, x83); + var x98: u64 = undefined; + var x99: u1 = undefined; + addcarryxU64(&x98, &x99, x97, x63, x85); + var x100: u64 = undefined; + var x101: u1 = undefined; + addcarryxU64(&x100, &x101, x99, x65, x87); + var x102: u64 = undefined; + var x103: u1 = undefined; + addcarryxU64(&x102, &x103, x101, x67, x89); + var x104: u64 = undefined; + var x105: u1 = undefined; + addcarryxU64(&x104, &x105, x103, @as(u64, x68), x91); + var x106: u64 = undefined; + var x107: u64 = undefined; + mulxU64(&x106, &x107, x92, 0x100000001); + var x108: u64 = undefined; + var x109: u64 = undefined; + mulxU64(&x108, &x109, x106, 0xffffffffffffffff); + var x110: u64 = undefined; + var x111: u64 = undefined; + mulxU64(&x110, &x111, x106, 0xffffffffffffffff); + var x112: u64 = undefined; + var x113: u64 = undefined; + mulxU64(&x112, &x113, x106, 0xffffffffffffffff); + var x114: u64 = undefined; + var x115: u64 = undefined; + mulxU64(&x114, &x115, x106, 0xfffffffffffffffe); + var x116: u64 = undefined; + var x117: u64 = undefined; + mulxU64(&x116, &x117, x106, 0xffffffff00000000); + var x118: u64 = undefined; + var x119: u64 = undefined; + mulxU64(&x118, &x119, x106, 0xffffffff); + var x120: u64 = undefined; + var x121: u1 = undefined; + addcarryxU64(&x120, &x121, 0x0, x119, x116); + var x122: u64 = undefined; + var x123: u1 = undefined; + addcarryxU64(&x122, &x123, x121, x117, x114); + var x124: u64 = undefined; + var x125: u1 = undefined; + addcarryxU64(&x124, &x125, x123, x115, x112); + var x126: u64 = undefined; + var x127: u1 = undefined; + addcarryxU64(&x126, &x127, x125, x113, x110); + var x128: u64 = undefined; + var x129: u1 = undefined; + addcarryxU64(&x128, &x129, x127, x111, x108); + const x130 = (@as(u64, x129) + x109); + var x131: u64 = undefined; + var x132: u1 = undefined; + addcarryxU64(&x131, &x132, 0x0, x92, x118); + var x133: u64 = undefined; + var x134: u1 = undefined; + addcarryxU64(&x133, &x134, x132, x94, x120); + var x135: u64 = undefined; + var x136: u1 = undefined; + addcarryxU64(&x135, &x136, x134, x96, x122); + var x137: u64 = undefined; + var x138: u1 = undefined; + addcarryxU64(&x137, &x138, x136, x98, x124); + var x139: u64 = undefined; + var x140: u1 = undefined; + addcarryxU64(&x139, &x140, x138, x100, x126); + var x141: u64 = undefined; + var x142: u1 = undefined; + addcarryxU64(&x141, &x142, x140, x102, x128); + var x143: u64 = undefined; + var x144: u1 = undefined; + addcarryxU64(&x143, &x144, x142, x104, x130); + const x145 = (@as(u64, x144) + @as(u64, x105)); + var x146: u64 = undefined; + var x147: u64 = undefined; + mulxU64(&x146, &x147, x2, (arg1[5])); + var x148: u64 = undefined; + var x149: u64 = undefined; + mulxU64(&x148, &x149, x2, (arg1[4])); + var x150: u64 = undefined; + var x151: u64 = undefined; + mulxU64(&x150, &x151, x2, (arg1[3])); + var x152: u64 = undefined; + var x153: u64 = undefined; + mulxU64(&x152, &x153, x2, (arg1[2])); + var x154: u64 = undefined; + var x155: u64 = undefined; + mulxU64(&x154, &x155, x2, (arg1[1])); + var x156: u64 = undefined; + var x157: u64 = undefined; + mulxU64(&x156, &x157, x2, (arg1[0])); + var x158: u64 = undefined; + var x159: u1 = undefined; + addcarryxU64(&x158, &x159, 0x0, x157, x154); + var x160: u64 = undefined; + var x161: u1 = undefined; + addcarryxU64(&x160, &x161, x159, x155, x152); + var x162: u64 = undefined; + var x163: u1 = undefined; + addcarryxU64(&x162, &x163, x161, x153, x150); + var x164: u64 = undefined; + var x165: u1 = undefined; + addcarryxU64(&x164, &x165, x163, x151, x148); + var x166: u64 = undefined; + var x167: u1 = undefined; + addcarryxU64(&x166, &x167, x165, x149, x146); + const x168 = (@as(u64, x167) + x147); + var x169: u64 = undefined; + var x170: u1 = undefined; + addcarryxU64(&x169, &x170, 0x0, x133, x156); + var x171: u64 = undefined; + var x172: u1 = undefined; + addcarryxU64(&x171, &x172, x170, x135, x158); + var x173: u64 = undefined; + var x174: u1 = undefined; + addcarryxU64(&x173, &x174, x172, x137, x160); + var x175: u64 = undefined; + var x176: u1 = undefined; + addcarryxU64(&x175, &x176, x174, x139, x162); + var x177: u64 = undefined; + var x178: u1 = undefined; + addcarryxU64(&x177, &x178, x176, x141, x164); + var x179: u64 = undefined; + var x180: u1 = undefined; + addcarryxU64(&x179, &x180, x178, x143, x166); + var x181: u64 = undefined; + var x182: u1 = undefined; + addcarryxU64(&x181, &x182, x180, x145, x168); + var x183: u64 = undefined; + var x184: u64 = undefined; + mulxU64(&x183, &x184, x169, 0x100000001); + var x185: u64 = undefined; + var x186: u64 = undefined; + mulxU64(&x185, &x186, x183, 0xffffffffffffffff); + var x187: u64 = undefined; + var x188: u64 = undefined; + mulxU64(&x187, &x188, x183, 0xffffffffffffffff); + var x189: u64 = undefined; + var x190: u64 = undefined; + mulxU64(&x189, &x190, x183, 0xffffffffffffffff); + var x191: u64 = undefined; + var x192: u64 = undefined; + mulxU64(&x191, &x192, x183, 0xfffffffffffffffe); + var x193: u64 = undefined; + var x194: u64 = undefined; + mulxU64(&x193, &x194, x183, 0xffffffff00000000); + var x195: u64 = undefined; + var x196: u64 = undefined; + mulxU64(&x195, &x196, x183, 0xffffffff); + var x197: u64 = undefined; + var x198: u1 = undefined; + addcarryxU64(&x197, &x198, 0x0, x196, x193); + var x199: u64 = undefined; + var x200: u1 = undefined; + addcarryxU64(&x199, &x200, x198, x194, x191); + var x201: u64 = undefined; + var x202: u1 = undefined; + addcarryxU64(&x201, &x202, x200, x192, x189); + var x203: u64 = undefined; + var x204: u1 = undefined; + addcarryxU64(&x203, &x204, x202, x190, x187); + var x205: u64 = undefined; + var x206: u1 = undefined; + addcarryxU64(&x205, &x206, x204, x188, x185); + const x207 = (@as(u64, x206) + x186); + var x208: u64 = undefined; + var x209: u1 = undefined; + addcarryxU64(&x208, &x209, 0x0, x169, x195); + var x210: u64 = undefined; + var x211: u1 = undefined; + addcarryxU64(&x210, &x211, x209, x171, x197); + var x212: u64 = undefined; + var x213: u1 = undefined; + addcarryxU64(&x212, &x213, x211, x173, x199); + var x214: u64 = undefined; + var x215: u1 = undefined; + addcarryxU64(&x214, &x215, x213, x175, x201); + var x216: u64 = undefined; + var x217: u1 = undefined; + addcarryxU64(&x216, &x217, x215, x177, x203); + var x218: u64 = undefined; + var x219: u1 = undefined; + addcarryxU64(&x218, &x219, x217, x179, x205); + var x220: u64 = undefined; + var x221: u1 = undefined; + addcarryxU64(&x220, &x221, x219, x181, x207); + const x222 = (@as(u64, x221) + @as(u64, x182)); + var x223: u64 = undefined; + var x224: u64 = undefined; + mulxU64(&x223, &x224, x3, (arg1[5])); + var x225: u64 = undefined; + var x226: u64 = undefined; + mulxU64(&x225, &x226, x3, (arg1[4])); + var x227: u64 = undefined; + var x228: u64 = undefined; + mulxU64(&x227, &x228, x3, (arg1[3])); + var x229: u64 = undefined; + var x230: u64 = undefined; + mulxU64(&x229, &x230, x3, (arg1[2])); + var x231: u64 = undefined; + var x232: u64 = undefined; + mulxU64(&x231, &x232, x3, (arg1[1])); + var x233: u64 = undefined; + var x234: u64 = undefined; + mulxU64(&x233, &x234, x3, (arg1[0])); + var x235: u64 = undefined; + var x236: u1 = undefined; + addcarryxU64(&x235, &x236, 0x0, x234, x231); + var x237: u64 = undefined; + var x238: u1 = undefined; + addcarryxU64(&x237, &x238, x236, x232, x229); + var x239: u64 = undefined; + var x240: u1 = undefined; + addcarryxU64(&x239, &x240, x238, x230, x227); + var x241: u64 = undefined; + var x242: u1 = undefined; + addcarryxU64(&x241, &x242, x240, x228, x225); + var x243: u64 = undefined; + var x244: u1 = undefined; + addcarryxU64(&x243, &x244, x242, x226, x223); + const x245 = (@as(u64, x244) + x224); + var x246: u64 = undefined; + var x247: u1 = undefined; + addcarryxU64(&x246, &x247, 0x0, x210, x233); + var x248: u64 = undefined; + var x249: u1 = undefined; + addcarryxU64(&x248, &x249, x247, x212, x235); + var x250: u64 = undefined; + var x251: u1 = undefined; + addcarryxU64(&x250, &x251, x249, x214, x237); + var x252: u64 = undefined; + var x253: u1 = undefined; + addcarryxU64(&x252, &x253, x251, x216, x239); + var x254: u64 = undefined; + var x255: u1 = undefined; + addcarryxU64(&x254, &x255, x253, x218, x241); + var x256: u64 = undefined; + var x257: u1 = undefined; + addcarryxU64(&x256, &x257, x255, x220, x243); + var x258: u64 = undefined; + var x259: u1 = undefined; + addcarryxU64(&x258, &x259, x257, x222, x245); + var x260: u64 = undefined; + var x261: u64 = undefined; + mulxU64(&x260, &x261, x246, 0x100000001); + var x262: u64 = undefined; + var x263: u64 = undefined; + mulxU64(&x262, &x263, x260, 0xffffffffffffffff); + var x264: u64 = undefined; + var x265: u64 = undefined; + mulxU64(&x264, &x265, x260, 0xffffffffffffffff); + var x266: u64 = undefined; + var x267: u64 = undefined; + mulxU64(&x266, &x267, x260, 0xffffffffffffffff); + var x268: u64 = undefined; + var x269: u64 = undefined; + mulxU64(&x268, &x269, x260, 0xfffffffffffffffe); + var x270: u64 = undefined; + var x271: u64 = undefined; + mulxU64(&x270, &x271, x260, 0xffffffff00000000); + var x272: u64 = undefined; + var x273: u64 = undefined; + mulxU64(&x272, &x273, x260, 0xffffffff); + var x274: u64 = undefined; + var x275: u1 = undefined; + addcarryxU64(&x274, &x275, 0x0, x273, x270); + var x276: u64 = undefined; + var x277: u1 = undefined; + addcarryxU64(&x276, &x277, x275, x271, x268); + var x278: u64 = undefined; + var x279: u1 = undefined; + addcarryxU64(&x278, &x279, x277, x269, x266); + var x280: u64 = undefined; + var x281: u1 = undefined; + addcarryxU64(&x280, &x281, x279, x267, x264); + var x282: u64 = undefined; + var x283: u1 = undefined; + addcarryxU64(&x282, &x283, x281, x265, x262); + const x284 = (@as(u64, x283) + x263); + var x285: u64 = undefined; + var x286: u1 = undefined; + addcarryxU64(&x285, &x286, 0x0, x246, x272); + var x287: u64 = undefined; + var x288: u1 = undefined; + addcarryxU64(&x287, &x288, x286, x248, x274); + var x289: u64 = undefined; + var x290: u1 = undefined; + addcarryxU64(&x289, &x290, x288, x250, x276); + var x291: u64 = undefined; + var x292: u1 = undefined; + addcarryxU64(&x291, &x292, x290, x252, x278); + var x293: u64 = undefined; + var x294: u1 = undefined; + addcarryxU64(&x293, &x294, x292, x254, x280); + var x295: u64 = undefined; + var x296: u1 = undefined; + addcarryxU64(&x295, &x296, x294, x256, x282); + var x297: u64 = undefined; + var x298: u1 = undefined; + addcarryxU64(&x297, &x298, x296, x258, x284); + const x299 = (@as(u64, x298) + @as(u64, x259)); + var x300: u64 = undefined; + var x301: u64 = undefined; + mulxU64(&x300, &x301, x4, (arg1[5])); + var x302: u64 = undefined; + var x303: u64 = undefined; + mulxU64(&x302, &x303, x4, (arg1[4])); + var x304: u64 = undefined; + var x305: u64 = undefined; + mulxU64(&x304, &x305, x4, (arg1[3])); + var x306: u64 = undefined; + var x307: u64 = undefined; + mulxU64(&x306, &x307, x4, (arg1[2])); + var x308: u64 = undefined; + var x309: u64 = undefined; + mulxU64(&x308, &x309, x4, (arg1[1])); + var x310: u64 = undefined; + var x311: u64 = undefined; + mulxU64(&x310, &x311, x4, (arg1[0])); + var x312: u64 = undefined; + var x313: u1 = undefined; + addcarryxU64(&x312, &x313, 0x0, x311, x308); + var x314: u64 = undefined; + var x315: u1 = undefined; + addcarryxU64(&x314, &x315, x313, x309, x306); + var x316: u64 = undefined; + var x317: u1 = undefined; + addcarryxU64(&x316, &x317, x315, x307, x304); + var x318: u64 = undefined; + var x319: u1 = undefined; + addcarryxU64(&x318, &x319, x317, x305, x302); + var x320: u64 = undefined; + var x321: u1 = undefined; + addcarryxU64(&x320, &x321, x319, x303, x300); + const x322 = (@as(u64, x321) + x301); + var x323: u64 = undefined; + var x324: u1 = undefined; + addcarryxU64(&x323, &x324, 0x0, x287, x310); + var x325: u64 = undefined; + var x326: u1 = undefined; + addcarryxU64(&x325, &x326, x324, x289, x312); + var x327: u64 = undefined; + var x328: u1 = undefined; + addcarryxU64(&x327, &x328, x326, x291, x314); + var x329: u64 = undefined; + var x330: u1 = undefined; + addcarryxU64(&x329, &x330, x328, x293, x316); + var x331: u64 = undefined; + var x332: u1 = undefined; + addcarryxU64(&x331, &x332, x330, x295, x318); + var x333: u64 = undefined; + var x334: u1 = undefined; + addcarryxU64(&x333, &x334, x332, x297, x320); + var x335: u64 = undefined; + var x336: u1 = undefined; + addcarryxU64(&x335, &x336, x334, x299, x322); + var x337: u64 = undefined; + var x338: u64 = undefined; + mulxU64(&x337, &x338, x323, 0x100000001); + var x339: u64 = undefined; + var x340: u64 = undefined; + mulxU64(&x339, &x340, x337, 0xffffffffffffffff); + var x341: u64 = undefined; + var x342: u64 = undefined; + mulxU64(&x341, &x342, x337, 0xffffffffffffffff); + var x343: u64 = undefined; + var x344: u64 = undefined; + mulxU64(&x343, &x344, x337, 0xffffffffffffffff); + var x345: u64 = undefined; + var x346: u64 = undefined; + mulxU64(&x345, &x346, x337, 0xfffffffffffffffe); + var x347: u64 = undefined; + var x348: u64 = undefined; + mulxU64(&x347, &x348, x337, 0xffffffff00000000); + var x349: u64 = undefined; + var x350: u64 = undefined; + mulxU64(&x349, &x350, x337, 0xffffffff); + var x351: u64 = undefined; + var x352: u1 = undefined; + addcarryxU64(&x351, &x352, 0x0, x350, x347); + var x353: u64 = undefined; + var x354: u1 = undefined; + addcarryxU64(&x353, &x354, x352, x348, x345); + var x355: u64 = undefined; + var x356: u1 = undefined; + addcarryxU64(&x355, &x356, x354, x346, x343); + var x357: u64 = undefined; + var x358: u1 = undefined; + addcarryxU64(&x357, &x358, x356, x344, x341); + var x359: u64 = undefined; + var x360: u1 = undefined; + addcarryxU64(&x359, &x360, x358, x342, x339); + const x361 = (@as(u64, x360) + x340); + var x362: u64 = undefined; + var x363: u1 = undefined; + addcarryxU64(&x362, &x363, 0x0, x323, x349); + var x364: u64 = undefined; + var x365: u1 = undefined; + addcarryxU64(&x364, &x365, x363, x325, x351); + var x366: u64 = undefined; + var x367: u1 = undefined; + addcarryxU64(&x366, &x367, x365, x327, x353); + var x368: u64 = undefined; + var x369: u1 = undefined; + addcarryxU64(&x368, &x369, x367, x329, x355); + var x370: u64 = undefined; + var x371: u1 = undefined; + addcarryxU64(&x370, &x371, x369, x331, x357); + var x372: u64 = undefined; + var x373: u1 = undefined; + addcarryxU64(&x372, &x373, x371, x333, x359); + var x374: u64 = undefined; + var x375: u1 = undefined; + addcarryxU64(&x374, &x375, x373, x335, x361); + const x376 = (@as(u64, x375) + @as(u64, x336)); + var x377: u64 = undefined; + var x378: u64 = undefined; + mulxU64(&x377, &x378, x5, (arg1[5])); + var x379: u64 = undefined; + var x380: u64 = undefined; + mulxU64(&x379, &x380, x5, (arg1[4])); + var x381: u64 = undefined; + var x382: u64 = undefined; + mulxU64(&x381, &x382, x5, (arg1[3])); + var x383: u64 = undefined; + var x384: u64 = undefined; + mulxU64(&x383, &x384, x5, (arg1[2])); + var x385: u64 = undefined; + var x386: u64 = undefined; + mulxU64(&x385, &x386, x5, (arg1[1])); + var x387: u64 = undefined; + var x388: u64 = undefined; + mulxU64(&x387, &x388, x5, (arg1[0])); + var x389: u64 = undefined; + var x390: u1 = undefined; + addcarryxU64(&x389, &x390, 0x0, x388, x385); + var x391: u64 = undefined; + var x392: u1 = undefined; + addcarryxU64(&x391, &x392, x390, x386, x383); + var x393: u64 = undefined; + var x394: u1 = undefined; + addcarryxU64(&x393, &x394, x392, x384, x381); + var x395: u64 = undefined; + var x396: u1 = undefined; + addcarryxU64(&x395, &x396, x394, x382, x379); + var x397: u64 = undefined; + var x398: u1 = undefined; + addcarryxU64(&x397, &x398, x396, x380, x377); + const x399 = (@as(u64, x398) + x378); + var x400: u64 = undefined; + var x401: u1 = undefined; + addcarryxU64(&x400, &x401, 0x0, x364, x387); + var x402: u64 = undefined; + var x403: u1 = undefined; + addcarryxU64(&x402, &x403, x401, x366, x389); + var x404: u64 = undefined; + var x405: u1 = undefined; + addcarryxU64(&x404, &x405, x403, x368, x391); + var x406: u64 = undefined; + var x407: u1 = undefined; + addcarryxU64(&x406, &x407, x405, x370, x393); + var x408: u64 = undefined; + var x409: u1 = undefined; + addcarryxU64(&x408, &x409, x407, x372, x395); + var x410: u64 = undefined; + var x411: u1 = undefined; + addcarryxU64(&x410, &x411, x409, x374, x397); + var x412: u64 = undefined; + var x413: u1 = undefined; + addcarryxU64(&x412, &x413, x411, x376, x399); + var x414: u64 = undefined; + var x415: u64 = undefined; + mulxU64(&x414, &x415, x400, 0x100000001); + var x416: u64 = undefined; + var x417: u64 = undefined; + mulxU64(&x416, &x417, x414, 0xffffffffffffffff); + var x418: u64 = undefined; + var x419: u64 = undefined; + mulxU64(&x418, &x419, x414, 0xffffffffffffffff); + var x420: u64 = undefined; + var x421: u64 = undefined; + mulxU64(&x420, &x421, x414, 0xffffffffffffffff); + var x422: u64 = undefined; + var x423: u64 = undefined; + mulxU64(&x422, &x423, x414, 0xfffffffffffffffe); + var x424: u64 = undefined; + var x425: u64 = undefined; + mulxU64(&x424, &x425, x414, 0xffffffff00000000); + var x426: u64 = undefined; + var x427: u64 = undefined; + mulxU64(&x426, &x427, x414, 0xffffffff); + var x428: u64 = undefined; + var x429: u1 = undefined; + addcarryxU64(&x428, &x429, 0x0, x427, x424); + var x430: u64 = undefined; + var x431: u1 = undefined; + addcarryxU64(&x430, &x431, x429, x425, x422); + var x432: u64 = undefined; + var x433: u1 = undefined; + addcarryxU64(&x432, &x433, x431, x423, x420); + var x434: u64 = undefined; + var x435: u1 = undefined; + addcarryxU64(&x434, &x435, x433, x421, x418); + var x436: u64 = undefined; + var x437: u1 = undefined; + addcarryxU64(&x436, &x437, x435, x419, x416); + const x438 = (@as(u64, x437) + x417); + var x439: u64 = undefined; + var x440: u1 = undefined; + addcarryxU64(&x439, &x440, 0x0, x400, x426); + var x441: u64 = undefined; + var x442: u1 = undefined; + addcarryxU64(&x441, &x442, x440, x402, x428); + var x443: u64 = undefined; + var x444: u1 = undefined; + addcarryxU64(&x443, &x444, x442, x404, x430); + var x445: u64 = undefined; + var x446: u1 = undefined; + addcarryxU64(&x445, &x446, x444, x406, x432); + var x447: u64 = undefined; + var x448: u1 = undefined; + addcarryxU64(&x447, &x448, x446, x408, x434); + var x449: u64 = undefined; + var x450: u1 = undefined; + addcarryxU64(&x449, &x450, x448, x410, x436); + var x451: u64 = undefined; + var x452: u1 = undefined; + addcarryxU64(&x451, &x452, x450, x412, x438); + const x453 = (@as(u64, x452) + @as(u64, x413)); + var x454: u64 = undefined; + var x455: u1 = undefined; + subborrowxU64(&x454, &x455, 0x0, x441, 0xffffffff); + var x456: u64 = undefined; + var x457: u1 = undefined; + subborrowxU64(&x456, &x457, x455, x443, 0xffffffff00000000); + var x458: u64 = undefined; + var x459: u1 = undefined; + subborrowxU64(&x458, &x459, x457, x445, 0xfffffffffffffffe); + var x460: u64 = undefined; + var x461: u1 = undefined; + subborrowxU64(&x460, &x461, x459, x447, 0xffffffffffffffff); + var x462: u64 = undefined; + var x463: u1 = undefined; + subborrowxU64(&x462, &x463, x461, x449, 0xffffffffffffffff); + var x464: u64 = undefined; + var x465: u1 = undefined; + subborrowxU64(&x464, &x465, x463, x451, 0xffffffffffffffff); + var x466: u64 = undefined; + var x467: u1 = undefined; + subborrowxU64(&x466, &x467, x465, x453, 0x0); + var x468: u64 = undefined; + cmovznzU64(&x468, x467, x454, x441); + var x469: u64 = undefined; + cmovznzU64(&x469, x467, x456, x443); + var x470: u64 = undefined; + cmovznzU64(&x470, x467, x458, x445); + var x471: u64 = undefined; + cmovznzU64(&x471, x467, x460, x447); + var x472: u64 = undefined; + cmovznzU64(&x472, x467, x462, x449); + var x473: u64 = undefined; + cmovznzU64(&x473, x467, x464, x451); + out1[0] = x468; + out1[1] = x469; + out1[2] = x470; + out1[3] = x471; + out1[4] = x472; + out1[5] = x473; +} + +/// The function add adds two field elements in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// 0 ≤ eval arg2 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m +/// 0 ≤ eval out1 < m +/// +pub fn add(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement, arg2: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + addcarryxU64(&x1, &x2, 0x0, (arg1[0]), (arg2[0])); + var x3: u64 = undefined; + var x4: u1 = undefined; + addcarryxU64(&x3, &x4, x2, (arg1[1]), (arg2[1])); + var x5: u64 = undefined; + var x6: u1 = undefined; + addcarryxU64(&x5, &x6, x4, (arg1[2]), (arg2[2])); + var x7: u64 = undefined; + var x8: u1 = undefined; + addcarryxU64(&x7, &x8, x6, (arg1[3]), (arg2[3])); + var x9: u64 = undefined; + var x10: u1 = undefined; + addcarryxU64(&x9, &x10, x8, (arg1[4]), (arg2[4])); + var x11: u64 = undefined; + var x12: u1 = undefined; + addcarryxU64(&x11, &x12, x10, (arg1[5]), (arg2[5])); + var x13: u64 = undefined; + var x14: u1 = undefined; + subborrowxU64(&x13, &x14, 0x0, x1, 0xffffffff); + var x15: u64 = undefined; + var x16: u1 = undefined; + subborrowxU64(&x15, &x16, x14, x3, 0xffffffff00000000); + var x17: u64 = undefined; + var x18: u1 = undefined; + subborrowxU64(&x17, &x18, x16, x5, 0xfffffffffffffffe); + var x19: u64 = undefined; + var x20: u1 = undefined; + subborrowxU64(&x19, &x20, x18, x7, 0xffffffffffffffff); + var x21: u64 = undefined; + var x22: u1 = undefined; + subborrowxU64(&x21, &x22, x20, x9, 0xffffffffffffffff); + var x23: u64 = undefined; + var x24: u1 = undefined; + subborrowxU64(&x23, &x24, x22, x11, 0xffffffffffffffff); + var x25: u64 = undefined; + var x26: u1 = undefined; + subborrowxU64(&x25, &x26, x24, @as(u64, x12), 0x0); + var x27: u64 = undefined; + cmovznzU64(&x27, x26, x13, x1); + var x28: u64 = undefined; + cmovznzU64(&x28, x26, x15, x3); + var x29: u64 = undefined; + cmovznzU64(&x29, x26, x17, x5); + var x30: u64 = undefined; + cmovznzU64(&x30, x26, x19, x7); + var x31: u64 = undefined; + cmovznzU64(&x31, x26, x21, x9); + var x32: u64 = undefined; + cmovznzU64(&x32, x26, x23, x11); + out1[0] = x27; + out1[1] = x28; + out1[2] = x29; + out1[3] = x30; + out1[4] = x31; + out1[5] = x32; +} + +/// The function sub subtracts two field elements in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// 0 ≤ eval arg2 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m +/// 0 ≤ eval out1 < m +/// +pub fn sub(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement, arg2: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + subborrowxU64(&x1, &x2, 0x0, (arg1[0]), (arg2[0])); + var x3: u64 = undefined; + var x4: u1 = undefined; + subborrowxU64(&x3, &x4, x2, (arg1[1]), (arg2[1])); + var x5: u64 = undefined; + var x6: u1 = undefined; + subborrowxU64(&x5, &x6, x4, (arg1[2]), (arg2[2])); + var x7: u64 = undefined; + var x8: u1 = undefined; + subborrowxU64(&x7, &x8, x6, (arg1[3]), (arg2[3])); + var x9: u64 = undefined; + var x10: u1 = undefined; + subborrowxU64(&x9, &x10, x8, (arg1[4]), (arg2[4])); + var x11: u64 = undefined; + var x12: u1 = undefined; + subborrowxU64(&x11, &x12, x10, (arg1[5]), (arg2[5])); + var x13: u64 = undefined; + cmovznzU64(&x13, x12, 0x0, 0xffffffffffffffff); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, 0x0, x1, (x13 & 0xffffffff)); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, x3, (x13 & 0xffffffff00000000)); + var x18: u64 = undefined; + var x19: u1 = undefined; + addcarryxU64(&x18, &x19, x17, x5, (x13 & 0xfffffffffffffffe)); + var x20: u64 = undefined; + var x21: u1 = undefined; + addcarryxU64(&x20, &x21, x19, x7, x13); + var x22: u64 = undefined; + var x23: u1 = undefined; + addcarryxU64(&x22, &x23, x21, x9, x13); + var x24: u64 = undefined; + var x25: u1 = undefined; + addcarryxU64(&x24, &x25, x23, x11, x13); + out1[0] = x14; + out1[1] = x16; + out1[2] = x18; + out1[3] = x20; + out1[4] = x22; + out1[5] = x24; +} + +/// The function opp negates a field element in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = -eval (from_montgomery arg1) mod m +/// 0 ≤ eval out1 < m +/// +pub fn opp(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + subborrowxU64(&x1, &x2, 0x0, 0x0, (arg1[0])); + var x3: u64 = undefined; + var x4: u1 = undefined; + subborrowxU64(&x3, &x4, x2, 0x0, (arg1[1])); + var x5: u64 = undefined; + var x6: u1 = undefined; + subborrowxU64(&x5, &x6, x4, 0x0, (arg1[2])); + var x7: u64 = undefined; + var x8: u1 = undefined; + subborrowxU64(&x7, &x8, x6, 0x0, (arg1[3])); + var x9: u64 = undefined; + var x10: u1 = undefined; + subborrowxU64(&x9, &x10, x8, 0x0, (arg1[4])); + var x11: u64 = undefined; + var x12: u1 = undefined; + subborrowxU64(&x11, &x12, x10, 0x0, (arg1[5])); + var x13: u64 = undefined; + cmovznzU64(&x13, x12, 0x0, 0xffffffffffffffff); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, 0x0, x1, (x13 & 0xffffffff)); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, x3, (x13 & 0xffffffff00000000)); + var x18: u64 = undefined; + var x19: u1 = undefined; + addcarryxU64(&x18, &x19, x17, x5, (x13 & 0xfffffffffffffffe)); + var x20: u64 = undefined; + var x21: u1 = undefined; + addcarryxU64(&x20, &x21, x19, x7, x13); + var x22: u64 = undefined; + var x23: u1 = undefined; + addcarryxU64(&x22, &x23, x21, x9, x13); + var x24: u64 = undefined; + var x25: u1 = undefined; + addcarryxU64(&x24, &x25, x23, x11, x13); + out1[0] = x14; + out1[1] = x16; + out1[2] = x18; + out1[3] = x20; + out1[4] = x22; + out1[5] = x24; +} + +/// The function fromMontgomery translates a field element out of the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^6) mod m +/// 0 ≤ eval out1 < m +/// +pub fn fromMontgomery(out1: *NonMontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[0]); + var x2: u64 = undefined; + var x3: u64 = undefined; + mulxU64(&x2, &x3, x1, 0x100000001); + var x4: u64 = undefined; + var x5: u64 = undefined; + mulxU64(&x4, &x5, x2, 0xffffffffffffffff); + var x6: u64 = undefined; + var x7: u64 = undefined; + mulxU64(&x6, &x7, x2, 0xffffffffffffffff); + var x8: u64 = undefined; + var x9: u64 = undefined; + mulxU64(&x8, &x9, x2, 0xffffffffffffffff); + var x10: u64 = undefined; + var x11: u64 = undefined; + mulxU64(&x10, &x11, x2, 0xfffffffffffffffe); + var x12: u64 = undefined; + var x13: u64 = undefined; + mulxU64(&x12, &x13, x2, 0xffffffff00000000); + var x14: u64 = undefined; + var x15: u64 = undefined; + mulxU64(&x14, &x15, x2, 0xffffffff); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, 0x0, x15, x12); + var x18: u64 = undefined; + var x19: u1 = undefined; + addcarryxU64(&x18, &x19, x17, x13, x10); + var x20: u64 = undefined; + var x21: u1 = undefined; + addcarryxU64(&x20, &x21, x19, x11, x8); + var x22: u64 = undefined; + var x23: u1 = undefined; + addcarryxU64(&x22, &x23, x21, x9, x6); + var x24: u64 = undefined; + var x25: u1 = undefined; + addcarryxU64(&x24, &x25, x23, x7, x4); + var x26: u64 = undefined; + var x27: u1 = undefined; + addcarryxU64(&x26, &x27, 0x0, x1, x14); + var x28: u64 = undefined; + var x29: u1 = undefined; + addcarryxU64(&x28, &x29, x27, 0x0, x16); + var x30: u64 = undefined; + var x31: u1 = undefined; + addcarryxU64(&x30, &x31, x29, 0x0, x18); + var x32: u64 = undefined; + var x33: u1 = undefined; + addcarryxU64(&x32, &x33, x31, 0x0, x20); + var x34: u64 = undefined; + var x35: u1 = undefined; + addcarryxU64(&x34, &x35, x33, 0x0, x22); + var x36: u64 = undefined; + var x37: u1 = undefined; + addcarryxU64(&x36, &x37, x35, 0x0, x24); + var x38: u64 = undefined; + var x39: u1 = undefined; + addcarryxU64(&x38, &x39, x37, 0x0, (@as(u64, x25) + x5)); + var x40: u64 = undefined; + var x41: u1 = undefined; + addcarryxU64(&x40, &x41, 0x0, x28, (arg1[1])); + var x42: u64 = undefined; + var x43: u1 = undefined; + addcarryxU64(&x42, &x43, x41, x30, 0x0); + var x44: u64 = undefined; + var x45: u1 = undefined; + addcarryxU64(&x44, &x45, x43, x32, 0x0); + var x46: u64 = undefined; + var x47: u1 = undefined; + addcarryxU64(&x46, &x47, x45, x34, 0x0); + var x48: u64 = undefined; + var x49: u1 = undefined; + addcarryxU64(&x48, &x49, x47, x36, 0x0); + var x50: u64 = undefined; + var x51: u1 = undefined; + addcarryxU64(&x50, &x51, x49, x38, 0x0); + var x52: u64 = undefined; + var x53: u64 = undefined; + mulxU64(&x52, &x53, x40, 0x100000001); + var x54: u64 = undefined; + var x55: u64 = undefined; + mulxU64(&x54, &x55, x52, 0xffffffffffffffff); + var x56: u64 = undefined; + var x57: u64 = undefined; + mulxU64(&x56, &x57, x52, 0xffffffffffffffff); + var x58: u64 = undefined; + var x59: u64 = undefined; + mulxU64(&x58, &x59, x52, 0xffffffffffffffff); + var x60: u64 = undefined; + var x61: u64 = undefined; + mulxU64(&x60, &x61, x52, 0xfffffffffffffffe); + var x62: u64 = undefined; + var x63: u64 = undefined; + mulxU64(&x62, &x63, x52, 0xffffffff00000000); + var x64: u64 = undefined; + var x65: u64 = undefined; + mulxU64(&x64, &x65, x52, 0xffffffff); + var x66: u64 = undefined; + var x67: u1 = undefined; + addcarryxU64(&x66, &x67, 0x0, x65, x62); + var x68: u64 = undefined; + var x69: u1 = undefined; + addcarryxU64(&x68, &x69, x67, x63, x60); + var x70: u64 = undefined; + var x71: u1 = undefined; + addcarryxU64(&x70, &x71, x69, x61, x58); + var x72: u64 = undefined; + var x73: u1 = undefined; + addcarryxU64(&x72, &x73, x71, x59, x56); + var x74: u64 = undefined; + var x75: u1 = undefined; + addcarryxU64(&x74, &x75, x73, x57, x54); + var x76: u64 = undefined; + var x77: u1 = undefined; + addcarryxU64(&x76, &x77, 0x0, x40, x64); + var x78: u64 = undefined; + var x79: u1 = undefined; + addcarryxU64(&x78, &x79, x77, x42, x66); + var x80: u64 = undefined; + var x81: u1 = undefined; + addcarryxU64(&x80, &x81, x79, x44, x68); + var x82: u64 = undefined; + var x83: u1 = undefined; + addcarryxU64(&x82, &x83, x81, x46, x70); + var x84: u64 = undefined; + var x85: u1 = undefined; + addcarryxU64(&x84, &x85, x83, x48, x72); + var x86: u64 = undefined; + var x87: u1 = undefined; + addcarryxU64(&x86, &x87, x85, x50, x74); + var x88: u64 = undefined; + var x89: u1 = undefined; + addcarryxU64(&x88, &x89, x87, (@as(u64, x51) + @as(u64, x39)), (@as(u64, x75) + x55)); + var x90: u64 = undefined; + var x91: u1 = undefined; + addcarryxU64(&x90, &x91, 0x0, x78, (arg1[2])); + var x92: u64 = undefined; + var x93: u1 = undefined; + addcarryxU64(&x92, &x93, x91, x80, 0x0); + var x94: u64 = undefined; + var x95: u1 = undefined; + addcarryxU64(&x94, &x95, x93, x82, 0x0); + var x96: u64 = undefined; + var x97: u1 = undefined; + addcarryxU64(&x96, &x97, x95, x84, 0x0); + var x98: u64 = undefined; + var x99: u1 = undefined; + addcarryxU64(&x98, &x99, x97, x86, 0x0); + var x100: u64 = undefined; + var x101: u1 = undefined; + addcarryxU64(&x100, &x101, x99, x88, 0x0); + var x102: u64 = undefined; + var x103: u64 = undefined; + mulxU64(&x102, &x103, x90, 0x100000001); + var x104: u64 = undefined; + var x105: u64 = undefined; + mulxU64(&x104, &x105, x102, 0xffffffffffffffff); + var x106: u64 = undefined; + var x107: u64 = undefined; + mulxU64(&x106, &x107, x102, 0xffffffffffffffff); + var x108: u64 = undefined; + var x109: u64 = undefined; + mulxU64(&x108, &x109, x102, 0xffffffffffffffff); + var x110: u64 = undefined; + var x111: u64 = undefined; + mulxU64(&x110, &x111, x102, 0xfffffffffffffffe); + var x112: u64 = undefined; + var x113: u64 = undefined; + mulxU64(&x112, &x113, x102, 0xffffffff00000000); + var x114: u64 = undefined; + var x115: u64 = undefined; + mulxU64(&x114, &x115, x102, 0xffffffff); + var x116: u64 = undefined; + var x117: u1 = undefined; + addcarryxU64(&x116, &x117, 0x0, x115, x112); + var x118: u64 = undefined; + var x119: u1 = undefined; + addcarryxU64(&x118, &x119, x117, x113, x110); + var x120: u64 = undefined; + var x121: u1 = undefined; + addcarryxU64(&x120, &x121, x119, x111, x108); + var x122: u64 = undefined; + var x123: u1 = undefined; + addcarryxU64(&x122, &x123, x121, x109, x106); + var x124: u64 = undefined; + var x125: u1 = undefined; + addcarryxU64(&x124, &x125, x123, x107, x104); + var x126: u64 = undefined; + var x127: u1 = undefined; + addcarryxU64(&x126, &x127, 0x0, x90, x114); + var x128: u64 = undefined; + var x129: u1 = undefined; + addcarryxU64(&x128, &x129, x127, x92, x116); + var x130: u64 = undefined; + var x131: u1 = undefined; + addcarryxU64(&x130, &x131, x129, x94, x118); + var x132: u64 = undefined; + var x133: u1 = undefined; + addcarryxU64(&x132, &x133, x131, x96, x120); + var x134: u64 = undefined; + var x135: u1 = undefined; + addcarryxU64(&x134, &x135, x133, x98, x122); + var x136: u64 = undefined; + var x137: u1 = undefined; + addcarryxU64(&x136, &x137, x135, x100, x124); + var x138: u64 = undefined; + var x139: u1 = undefined; + addcarryxU64(&x138, &x139, x137, (@as(u64, x101) + @as(u64, x89)), (@as(u64, x125) + x105)); + var x140: u64 = undefined; + var x141: u1 = undefined; + addcarryxU64(&x140, &x141, 0x0, x128, (arg1[3])); + var x142: u64 = undefined; + var x143: u1 = undefined; + addcarryxU64(&x142, &x143, x141, x130, 0x0); + var x144: u64 = undefined; + var x145: u1 = undefined; + addcarryxU64(&x144, &x145, x143, x132, 0x0); + var x146: u64 = undefined; + var x147: u1 = undefined; + addcarryxU64(&x146, &x147, x145, x134, 0x0); + var x148: u64 = undefined; + var x149: u1 = undefined; + addcarryxU64(&x148, &x149, x147, x136, 0x0); + var x150: u64 = undefined; + var x151: u1 = undefined; + addcarryxU64(&x150, &x151, x149, x138, 0x0); + var x152: u64 = undefined; + var x153: u64 = undefined; + mulxU64(&x152, &x153, x140, 0x100000001); + var x154: u64 = undefined; + var x155: u64 = undefined; + mulxU64(&x154, &x155, x152, 0xffffffffffffffff); + var x156: u64 = undefined; + var x157: u64 = undefined; + mulxU64(&x156, &x157, x152, 0xffffffffffffffff); + var x158: u64 = undefined; + var x159: u64 = undefined; + mulxU64(&x158, &x159, x152, 0xffffffffffffffff); + var x160: u64 = undefined; + var x161: u64 = undefined; + mulxU64(&x160, &x161, x152, 0xfffffffffffffffe); + var x162: u64 = undefined; + var x163: u64 = undefined; + mulxU64(&x162, &x163, x152, 0xffffffff00000000); + var x164: u64 = undefined; + var x165: u64 = undefined; + mulxU64(&x164, &x165, x152, 0xffffffff); + var x166: u64 = undefined; + var x167: u1 = undefined; + addcarryxU64(&x166, &x167, 0x0, x165, x162); + var x168: u64 = undefined; + var x169: u1 = undefined; + addcarryxU64(&x168, &x169, x167, x163, x160); + var x170: u64 = undefined; + var x171: u1 = undefined; + addcarryxU64(&x170, &x171, x169, x161, x158); + var x172: u64 = undefined; + var x173: u1 = undefined; + addcarryxU64(&x172, &x173, x171, x159, x156); + var x174: u64 = undefined; + var x175: u1 = undefined; + addcarryxU64(&x174, &x175, x173, x157, x154); + var x176: u64 = undefined; + var x177: u1 = undefined; + addcarryxU64(&x176, &x177, 0x0, x140, x164); + var x178: u64 = undefined; + var x179: u1 = undefined; + addcarryxU64(&x178, &x179, x177, x142, x166); + var x180: u64 = undefined; + var x181: u1 = undefined; + addcarryxU64(&x180, &x181, x179, x144, x168); + var x182: u64 = undefined; + var x183: u1 = undefined; + addcarryxU64(&x182, &x183, x181, x146, x170); + var x184: u64 = undefined; + var x185: u1 = undefined; + addcarryxU64(&x184, &x185, x183, x148, x172); + var x186: u64 = undefined; + var x187: u1 = undefined; + addcarryxU64(&x186, &x187, x185, x150, x174); + var x188: u64 = undefined; + var x189: u1 = undefined; + addcarryxU64(&x188, &x189, x187, (@as(u64, x151) + @as(u64, x139)), (@as(u64, x175) + x155)); + var x190: u64 = undefined; + var x191: u1 = undefined; + addcarryxU64(&x190, &x191, 0x0, x178, (arg1[4])); + var x192: u64 = undefined; + var x193: u1 = undefined; + addcarryxU64(&x192, &x193, x191, x180, 0x0); + var x194: u64 = undefined; + var x195: u1 = undefined; + addcarryxU64(&x194, &x195, x193, x182, 0x0); + var x196: u64 = undefined; + var x197: u1 = undefined; + addcarryxU64(&x196, &x197, x195, x184, 0x0); + var x198: u64 = undefined; + var x199: u1 = undefined; + addcarryxU64(&x198, &x199, x197, x186, 0x0); + var x200: u64 = undefined; + var x201: u1 = undefined; + addcarryxU64(&x200, &x201, x199, x188, 0x0); + var x202: u64 = undefined; + var x203: u64 = undefined; + mulxU64(&x202, &x203, x190, 0x100000001); + var x204: u64 = undefined; + var x205: u64 = undefined; + mulxU64(&x204, &x205, x202, 0xffffffffffffffff); + var x206: u64 = undefined; + var x207: u64 = undefined; + mulxU64(&x206, &x207, x202, 0xffffffffffffffff); + var x208: u64 = undefined; + var x209: u64 = undefined; + mulxU64(&x208, &x209, x202, 0xffffffffffffffff); + var x210: u64 = undefined; + var x211: u64 = undefined; + mulxU64(&x210, &x211, x202, 0xfffffffffffffffe); + var x212: u64 = undefined; + var x213: u64 = undefined; + mulxU64(&x212, &x213, x202, 0xffffffff00000000); + var x214: u64 = undefined; + var x215: u64 = undefined; + mulxU64(&x214, &x215, x202, 0xffffffff); + var x216: u64 = undefined; + var x217: u1 = undefined; + addcarryxU64(&x216, &x217, 0x0, x215, x212); + var x218: u64 = undefined; + var x219: u1 = undefined; + addcarryxU64(&x218, &x219, x217, x213, x210); + var x220: u64 = undefined; + var x221: u1 = undefined; + addcarryxU64(&x220, &x221, x219, x211, x208); + var x222: u64 = undefined; + var x223: u1 = undefined; + addcarryxU64(&x222, &x223, x221, x209, x206); + var x224: u64 = undefined; + var x225: u1 = undefined; + addcarryxU64(&x224, &x225, x223, x207, x204); + var x226: u64 = undefined; + var x227: u1 = undefined; + addcarryxU64(&x226, &x227, 0x0, x190, x214); + var x228: u64 = undefined; + var x229: u1 = undefined; + addcarryxU64(&x228, &x229, x227, x192, x216); + var x230: u64 = undefined; + var x231: u1 = undefined; + addcarryxU64(&x230, &x231, x229, x194, x218); + var x232: u64 = undefined; + var x233: u1 = undefined; + addcarryxU64(&x232, &x233, x231, x196, x220); + var x234: u64 = undefined; + var x235: u1 = undefined; + addcarryxU64(&x234, &x235, x233, x198, x222); + var x236: u64 = undefined; + var x237: u1 = undefined; + addcarryxU64(&x236, &x237, x235, x200, x224); + var x238: u64 = undefined; + var x239: u1 = undefined; + addcarryxU64(&x238, &x239, x237, (@as(u64, x201) + @as(u64, x189)), (@as(u64, x225) + x205)); + var x240: u64 = undefined; + var x241: u1 = undefined; + addcarryxU64(&x240, &x241, 0x0, x228, (arg1[5])); + var x242: u64 = undefined; + var x243: u1 = undefined; + addcarryxU64(&x242, &x243, x241, x230, 0x0); + var x244: u64 = undefined; + var x245: u1 = undefined; + addcarryxU64(&x244, &x245, x243, x232, 0x0); + var x246: u64 = undefined; + var x247: u1 = undefined; + addcarryxU64(&x246, &x247, x245, x234, 0x0); + var x248: u64 = undefined; + var x249: u1 = undefined; + addcarryxU64(&x248, &x249, x247, x236, 0x0); + var x250: u64 = undefined; + var x251: u1 = undefined; + addcarryxU64(&x250, &x251, x249, x238, 0x0); + var x252: u64 = undefined; + var x253: u64 = undefined; + mulxU64(&x252, &x253, x240, 0x100000001); + var x254: u64 = undefined; + var x255: u64 = undefined; + mulxU64(&x254, &x255, x252, 0xffffffffffffffff); + var x256: u64 = undefined; + var x257: u64 = undefined; + mulxU64(&x256, &x257, x252, 0xffffffffffffffff); + var x258: u64 = undefined; + var x259: u64 = undefined; + mulxU64(&x258, &x259, x252, 0xffffffffffffffff); + var x260: u64 = undefined; + var x261: u64 = undefined; + mulxU64(&x260, &x261, x252, 0xfffffffffffffffe); + var x262: u64 = undefined; + var x263: u64 = undefined; + mulxU64(&x262, &x263, x252, 0xffffffff00000000); + var x264: u64 = undefined; + var x265: u64 = undefined; + mulxU64(&x264, &x265, x252, 0xffffffff); + var x266: u64 = undefined; + var x267: u1 = undefined; + addcarryxU64(&x266, &x267, 0x0, x265, x262); + var x268: u64 = undefined; + var x269: u1 = undefined; + addcarryxU64(&x268, &x269, x267, x263, x260); + var x270: u64 = undefined; + var x271: u1 = undefined; + addcarryxU64(&x270, &x271, x269, x261, x258); + var x272: u64 = undefined; + var x273: u1 = undefined; + addcarryxU64(&x272, &x273, x271, x259, x256); + var x274: u64 = undefined; + var x275: u1 = undefined; + addcarryxU64(&x274, &x275, x273, x257, x254); + var x276: u64 = undefined; + var x277: u1 = undefined; + addcarryxU64(&x276, &x277, 0x0, x240, x264); + var x278: u64 = undefined; + var x279: u1 = undefined; + addcarryxU64(&x278, &x279, x277, x242, x266); + var x280: u64 = undefined; + var x281: u1 = undefined; + addcarryxU64(&x280, &x281, x279, x244, x268); + var x282: u64 = undefined; + var x283: u1 = undefined; + addcarryxU64(&x282, &x283, x281, x246, x270); + var x284: u64 = undefined; + var x285: u1 = undefined; + addcarryxU64(&x284, &x285, x283, x248, x272); + var x286: u64 = undefined; + var x287: u1 = undefined; + addcarryxU64(&x286, &x287, x285, x250, x274); + var x288: u64 = undefined; + var x289: u1 = undefined; + addcarryxU64(&x288, &x289, x287, (@as(u64, x251) + @as(u64, x239)), (@as(u64, x275) + x255)); + var x290: u64 = undefined; + var x291: u1 = undefined; + subborrowxU64(&x290, &x291, 0x0, x278, 0xffffffff); + var x292: u64 = undefined; + var x293: u1 = undefined; + subborrowxU64(&x292, &x293, x291, x280, 0xffffffff00000000); + var x294: u64 = undefined; + var x295: u1 = undefined; + subborrowxU64(&x294, &x295, x293, x282, 0xfffffffffffffffe); + var x296: u64 = undefined; + var x297: u1 = undefined; + subborrowxU64(&x296, &x297, x295, x284, 0xffffffffffffffff); + var x298: u64 = undefined; + var x299: u1 = undefined; + subborrowxU64(&x298, &x299, x297, x286, 0xffffffffffffffff); + var x300: u64 = undefined; + var x301: u1 = undefined; + subborrowxU64(&x300, &x301, x299, x288, 0xffffffffffffffff); + var x302: u64 = undefined; + var x303: u1 = undefined; + subborrowxU64(&x302, &x303, x301, @as(u64, x289), 0x0); + var x304: u64 = undefined; + cmovznzU64(&x304, x303, x290, x278); + var x305: u64 = undefined; + cmovznzU64(&x305, x303, x292, x280); + var x306: u64 = undefined; + cmovznzU64(&x306, x303, x294, x282); + var x307: u64 = undefined; + cmovznzU64(&x307, x303, x296, x284); + var x308: u64 = undefined; + cmovznzU64(&x308, x303, x298, x286); + var x309: u64 = undefined; + cmovznzU64(&x309, x303, x300, x288); + out1[0] = x304; + out1[1] = x305; + out1[2] = x306; + out1[3] = x307; + out1[4] = x308; + out1[5] = x309; +} + +/// The function toMontgomery translates a field element into the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = eval arg1 mod m +/// 0 ≤ eval out1 < m +/// +pub fn toMontgomery(out1: *MontgomeryDomainFieldElement, arg1: NonMontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[1]); + const x2 = (arg1[2]); + const x3 = (arg1[3]); + const x4 = (arg1[4]); + const x5 = (arg1[5]); + const x6 = (arg1[0]); + var x7: u64 = undefined; + var x8: u64 = undefined; + mulxU64(&x7, &x8, x6, 0x200000000); + var x9: u64 = undefined; + var x10: u64 = undefined; + mulxU64(&x9, &x10, x6, 0xfffffffe00000000); + var x11: u64 = undefined; + var x12: u64 = undefined; + mulxU64(&x11, &x12, x6, 0x200000000); + var x13: u64 = undefined; + var x14: u64 = undefined; + mulxU64(&x13, &x14, x6, 0xfffffffe00000001); + var x15: u64 = undefined; + var x16: u1 = undefined; + addcarryxU64(&x15, &x16, 0x0, x14, x11); + var x17: u64 = undefined; + var x18: u1 = undefined; + addcarryxU64(&x17, &x18, x16, x12, x9); + var x19: u64 = undefined; + var x20: u1 = undefined; + addcarryxU64(&x19, &x20, x18, x10, x7); + var x21: u64 = undefined; + var x22: u1 = undefined; + addcarryxU64(&x21, &x22, x20, x8, x6); + var x23: u64 = undefined; + var x24: u64 = undefined; + mulxU64(&x23, &x24, x13, 0x100000001); + var x25: u64 = undefined; + var x26: u64 = undefined; + mulxU64(&x25, &x26, x23, 0xffffffffffffffff); + var x27: u64 = undefined; + var x28: u64 = undefined; + mulxU64(&x27, &x28, x23, 0xffffffffffffffff); + var x29: u64 = undefined; + var x30: u64 = undefined; + mulxU64(&x29, &x30, x23, 0xffffffffffffffff); + var x31: u64 = undefined; + var x32: u64 = undefined; + mulxU64(&x31, &x32, x23, 0xfffffffffffffffe); + var x33: u64 = undefined; + var x34: u64 = undefined; + mulxU64(&x33, &x34, x23, 0xffffffff00000000); + var x35: u64 = undefined; + var x36: u64 = undefined; + mulxU64(&x35, &x36, x23, 0xffffffff); + var x37: u64 = undefined; + var x38: u1 = undefined; + addcarryxU64(&x37, &x38, 0x0, x36, x33); + var x39: u64 = undefined; + var x40: u1 = undefined; + addcarryxU64(&x39, &x40, x38, x34, x31); + var x41: u64 = undefined; + var x42: u1 = undefined; + addcarryxU64(&x41, &x42, x40, x32, x29); + var x43: u64 = undefined; + var x44: u1 = undefined; + addcarryxU64(&x43, &x44, x42, x30, x27); + var x45: u64 = undefined; + var x46: u1 = undefined; + addcarryxU64(&x45, &x46, x44, x28, x25); + var x47: u64 = undefined; + var x48: u1 = undefined; + addcarryxU64(&x47, &x48, 0x0, x13, x35); + var x49: u64 = undefined; + var x50: u1 = undefined; + addcarryxU64(&x49, &x50, x48, x15, x37); + var x51: u64 = undefined; + var x52: u1 = undefined; + addcarryxU64(&x51, &x52, x50, x17, x39); + var x53: u64 = undefined; + var x54: u1 = undefined; + addcarryxU64(&x53, &x54, x52, x19, x41); + var x55: u64 = undefined; + var x56: u1 = undefined; + addcarryxU64(&x55, &x56, x54, x21, x43); + var x57: u64 = undefined; + var x58: u1 = undefined; + addcarryxU64(&x57, &x58, x56, @as(u64, x22), x45); + var x59: u64 = undefined; + var x60: u1 = undefined; + addcarryxU64(&x59, &x60, x58, 0x0, (@as(u64, x46) + x26)); + var x61: u64 = undefined; + var x62: u64 = undefined; + mulxU64(&x61, &x62, x1, 0x200000000); + var x63: u64 = undefined; + var x64: u64 = undefined; + mulxU64(&x63, &x64, x1, 0xfffffffe00000000); + var x65: u64 = undefined; + var x66: u64 = undefined; + mulxU64(&x65, &x66, x1, 0x200000000); + var x67: u64 = undefined; + var x68: u64 = undefined; + mulxU64(&x67, &x68, x1, 0xfffffffe00000001); + var x69: u64 = undefined; + var x70: u1 = undefined; + addcarryxU64(&x69, &x70, 0x0, x68, x65); + var x71: u64 = undefined; + var x72: u1 = undefined; + addcarryxU64(&x71, &x72, x70, x66, x63); + var x73: u64 = undefined; + var x74: u1 = undefined; + addcarryxU64(&x73, &x74, x72, x64, x61); + var x75: u64 = undefined; + var x76: u1 = undefined; + addcarryxU64(&x75, &x76, x74, x62, x1); + var x77: u64 = undefined; + var x78: u1 = undefined; + addcarryxU64(&x77, &x78, 0x0, x49, x67); + var x79: u64 = undefined; + var x80: u1 = undefined; + addcarryxU64(&x79, &x80, x78, x51, x69); + var x81: u64 = undefined; + var x82: u1 = undefined; + addcarryxU64(&x81, &x82, x80, x53, x71); + var x83: u64 = undefined; + var x84: u1 = undefined; + addcarryxU64(&x83, &x84, x82, x55, x73); + var x85: u64 = undefined; + var x86: u1 = undefined; + addcarryxU64(&x85, &x86, x84, x57, x75); + var x87: u64 = undefined; + var x88: u1 = undefined; + addcarryxU64(&x87, &x88, x86, x59, @as(u64, x76)); + var x89: u64 = undefined; + var x90: u64 = undefined; + mulxU64(&x89, &x90, x77, 0x100000001); + var x91: u64 = undefined; + var x92: u64 = undefined; + mulxU64(&x91, &x92, x89, 0xffffffffffffffff); + var x93: u64 = undefined; + var x94: u64 = undefined; + mulxU64(&x93, &x94, x89, 0xffffffffffffffff); + var x95: u64 = undefined; + var x96: u64 = undefined; + mulxU64(&x95, &x96, x89, 0xffffffffffffffff); + var x97: u64 = undefined; + var x98: u64 = undefined; + mulxU64(&x97, &x98, x89, 0xfffffffffffffffe); + var x99: u64 = undefined; + var x100: u64 = undefined; + mulxU64(&x99, &x100, x89, 0xffffffff00000000); + var x101: u64 = undefined; + var x102: u64 = undefined; + mulxU64(&x101, &x102, x89, 0xffffffff); + var x103: u64 = undefined; + var x104: u1 = undefined; + addcarryxU64(&x103, &x104, 0x0, x102, x99); + var x105: u64 = undefined; + var x106: u1 = undefined; + addcarryxU64(&x105, &x106, x104, x100, x97); + var x107: u64 = undefined; + var x108: u1 = undefined; + addcarryxU64(&x107, &x108, x106, x98, x95); + var x109: u64 = undefined; + var x110: u1 = undefined; + addcarryxU64(&x109, &x110, x108, x96, x93); + var x111: u64 = undefined; + var x112: u1 = undefined; + addcarryxU64(&x111, &x112, x110, x94, x91); + var x113: u64 = undefined; + var x114: u1 = undefined; + addcarryxU64(&x113, &x114, 0x0, x77, x101); + var x115: u64 = undefined; + var x116: u1 = undefined; + addcarryxU64(&x115, &x116, x114, x79, x103); + var x117: u64 = undefined; + var x118: u1 = undefined; + addcarryxU64(&x117, &x118, x116, x81, x105); + var x119: u64 = undefined; + var x120: u1 = undefined; + addcarryxU64(&x119, &x120, x118, x83, x107); + var x121: u64 = undefined; + var x122: u1 = undefined; + addcarryxU64(&x121, &x122, x120, x85, x109); + var x123: u64 = undefined; + var x124: u1 = undefined; + addcarryxU64(&x123, &x124, x122, x87, x111); + var x125: u64 = undefined; + var x126: u1 = undefined; + addcarryxU64(&x125, &x126, x124, (@as(u64, x88) + @as(u64, x60)), (@as(u64, x112) + x92)); + var x127: u64 = undefined; + var x128: u64 = undefined; + mulxU64(&x127, &x128, x2, 0x200000000); + var x129: u64 = undefined; + var x130: u64 = undefined; + mulxU64(&x129, &x130, x2, 0xfffffffe00000000); + var x131: u64 = undefined; + var x132: u64 = undefined; + mulxU64(&x131, &x132, x2, 0x200000000); + var x133: u64 = undefined; + var x134: u64 = undefined; + mulxU64(&x133, &x134, x2, 0xfffffffe00000001); + var x135: u64 = undefined; + var x136: u1 = undefined; + addcarryxU64(&x135, &x136, 0x0, x134, x131); + var x137: u64 = undefined; + var x138: u1 = undefined; + addcarryxU64(&x137, &x138, x136, x132, x129); + var x139: u64 = undefined; + var x140: u1 = undefined; + addcarryxU64(&x139, &x140, x138, x130, x127); + var x141: u64 = undefined; + var x142: u1 = undefined; + addcarryxU64(&x141, &x142, x140, x128, x2); + var x143: u64 = undefined; + var x144: u1 = undefined; + addcarryxU64(&x143, &x144, 0x0, x115, x133); + var x145: u64 = undefined; + var x146: u1 = undefined; + addcarryxU64(&x145, &x146, x144, x117, x135); + var x147: u64 = undefined; + var x148: u1 = undefined; + addcarryxU64(&x147, &x148, x146, x119, x137); + var x149: u64 = undefined; + var x150: u1 = undefined; + addcarryxU64(&x149, &x150, x148, x121, x139); + var x151: u64 = undefined; + var x152: u1 = undefined; + addcarryxU64(&x151, &x152, x150, x123, x141); + var x153: u64 = undefined; + var x154: u1 = undefined; + addcarryxU64(&x153, &x154, x152, x125, @as(u64, x142)); + var x155: u64 = undefined; + var x156: u64 = undefined; + mulxU64(&x155, &x156, x143, 0x100000001); + var x157: u64 = undefined; + var x158: u64 = undefined; + mulxU64(&x157, &x158, x155, 0xffffffffffffffff); + var x159: u64 = undefined; + var x160: u64 = undefined; + mulxU64(&x159, &x160, x155, 0xffffffffffffffff); + var x161: u64 = undefined; + var x162: u64 = undefined; + mulxU64(&x161, &x162, x155, 0xffffffffffffffff); + var x163: u64 = undefined; + var x164: u64 = undefined; + mulxU64(&x163, &x164, x155, 0xfffffffffffffffe); + var x165: u64 = undefined; + var x166: u64 = undefined; + mulxU64(&x165, &x166, x155, 0xffffffff00000000); + var x167: u64 = undefined; + var x168: u64 = undefined; + mulxU64(&x167, &x168, x155, 0xffffffff); + var x169: u64 = undefined; + var x170: u1 = undefined; + addcarryxU64(&x169, &x170, 0x0, x168, x165); + var x171: u64 = undefined; + var x172: u1 = undefined; + addcarryxU64(&x171, &x172, x170, x166, x163); + var x173: u64 = undefined; + var x174: u1 = undefined; + addcarryxU64(&x173, &x174, x172, x164, x161); + var x175: u64 = undefined; + var x176: u1 = undefined; + addcarryxU64(&x175, &x176, x174, x162, x159); + var x177: u64 = undefined; + var x178: u1 = undefined; + addcarryxU64(&x177, &x178, x176, x160, x157); + var x179: u64 = undefined; + var x180: u1 = undefined; + addcarryxU64(&x179, &x180, 0x0, x143, x167); + var x181: u64 = undefined; + var x182: u1 = undefined; + addcarryxU64(&x181, &x182, x180, x145, x169); + var x183: u64 = undefined; + var x184: u1 = undefined; + addcarryxU64(&x183, &x184, x182, x147, x171); + var x185: u64 = undefined; + var x186: u1 = undefined; + addcarryxU64(&x185, &x186, x184, x149, x173); + var x187: u64 = undefined; + var x188: u1 = undefined; + addcarryxU64(&x187, &x188, x186, x151, x175); + var x189: u64 = undefined; + var x190: u1 = undefined; + addcarryxU64(&x189, &x190, x188, x153, x177); + var x191: u64 = undefined; + var x192: u1 = undefined; + addcarryxU64(&x191, &x192, x190, (@as(u64, x154) + @as(u64, x126)), (@as(u64, x178) + x158)); + var x193: u64 = undefined; + var x194: u64 = undefined; + mulxU64(&x193, &x194, x3, 0x200000000); + var x195: u64 = undefined; + var x196: u64 = undefined; + mulxU64(&x195, &x196, x3, 0xfffffffe00000000); + var x197: u64 = undefined; + var x198: u64 = undefined; + mulxU64(&x197, &x198, x3, 0x200000000); + var x199: u64 = undefined; + var x200: u64 = undefined; + mulxU64(&x199, &x200, x3, 0xfffffffe00000001); + var x201: u64 = undefined; + var x202: u1 = undefined; + addcarryxU64(&x201, &x202, 0x0, x200, x197); + var x203: u64 = undefined; + var x204: u1 = undefined; + addcarryxU64(&x203, &x204, x202, x198, x195); + var x205: u64 = undefined; + var x206: u1 = undefined; + addcarryxU64(&x205, &x206, x204, x196, x193); + var x207: u64 = undefined; + var x208: u1 = undefined; + addcarryxU64(&x207, &x208, x206, x194, x3); + var x209: u64 = undefined; + var x210: u1 = undefined; + addcarryxU64(&x209, &x210, 0x0, x181, x199); + var x211: u64 = undefined; + var x212: u1 = undefined; + addcarryxU64(&x211, &x212, x210, x183, x201); + var x213: u64 = undefined; + var x214: u1 = undefined; + addcarryxU64(&x213, &x214, x212, x185, x203); + var x215: u64 = undefined; + var x216: u1 = undefined; + addcarryxU64(&x215, &x216, x214, x187, x205); + var x217: u64 = undefined; + var x218: u1 = undefined; + addcarryxU64(&x217, &x218, x216, x189, x207); + var x219: u64 = undefined; + var x220: u1 = undefined; + addcarryxU64(&x219, &x220, x218, x191, @as(u64, x208)); + var x221: u64 = undefined; + var x222: u64 = undefined; + mulxU64(&x221, &x222, x209, 0x100000001); + var x223: u64 = undefined; + var x224: u64 = undefined; + mulxU64(&x223, &x224, x221, 0xffffffffffffffff); + var x225: u64 = undefined; + var x226: u64 = undefined; + mulxU64(&x225, &x226, x221, 0xffffffffffffffff); + var x227: u64 = undefined; + var x228: u64 = undefined; + mulxU64(&x227, &x228, x221, 0xffffffffffffffff); + var x229: u64 = undefined; + var x230: u64 = undefined; + mulxU64(&x229, &x230, x221, 0xfffffffffffffffe); + var x231: u64 = undefined; + var x232: u64 = undefined; + mulxU64(&x231, &x232, x221, 0xffffffff00000000); + var x233: u64 = undefined; + var x234: u64 = undefined; + mulxU64(&x233, &x234, x221, 0xffffffff); + var x235: u64 = undefined; + var x236: u1 = undefined; + addcarryxU64(&x235, &x236, 0x0, x234, x231); + var x237: u64 = undefined; + var x238: u1 = undefined; + addcarryxU64(&x237, &x238, x236, x232, x229); + var x239: u64 = undefined; + var x240: u1 = undefined; + addcarryxU64(&x239, &x240, x238, x230, x227); + var x241: u64 = undefined; + var x242: u1 = undefined; + addcarryxU64(&x241, &x242, x240, x228, x225); + var x243: u64 = undefined; + var x244: u1 = undefined; + addcarryxU64(&x243, &x244, x242, x226, x223); + var x245: u64 = undefined; + var x246: u1 = undefined; + addcarryxU64(&x245, &x246, 0x0, x209, x233); + var x247: u64 = undefined; + var x248: u1 = undefined; + addcarryxU64(&x247, &x248, x246, x211, x235); + var x249: u64 = undefined; + var x250: u1 = undefined; + addcarryxU64(&x249, &x250, x248, x213, x237); + var x251: u64 = undefined; + var x252: u1 = undefined; + addcarryxU64(&x251, &x252, x250, x215, x239); + var x253: u64 = undefined; + var x254: u1 = undefined; + addcarryxU64(&x253, &x254, x252, x217, x241); + var x255: u64 = undefined; + var x256: u1 = undefined; + addcarryxU64(&x255, &x256, x254, x219, x243); + var x257: u64 = undefined; + var x258: u1 = undefined; + addcarryxU64(&x257, &x258, x256, (@as(u64, x220) + @as(u64, x192)), (@as(u64, x244) + x224)); + var x259: u64 = undefined; + var x260: u64 = undefined; + mulxU64(&x259, &x260, x4, 0x200000000); + var x261: u64 = undefined; + var x262: u64 = undefined; + mulxU64(&x261, &x262, x4, 0xfffffffe00000000); + var x263: u64 = undefined; + var x264: u64 = undefined; + mulxU64(&x263, &x264, x4, 0x200000000); + var x265: u64 = undefined; + var x266: u64 = undefined; + mulxU64(&x265, &x266, x4, 0xfffffffe00000001); + var x267: u64 = undefined; + var x268: u1 = undefined; + addcarryxU64(&x267, &x268, 0x0, x266, x263); + var x269: u64 = undefined; + var x270: u1 = undefined; + addcarryxU64(&x269, &x270, x268, x264, x261); + var x271: u64 = undefined; + var x272: u1 = undefined; + addcarryxU64(&x271, &x272, x270, x262, x259); + var x273: u64 = undefined; + var x274: u1 = undefined; + addcarryxU64(&x273, &x274, x272, x260, x4); + var x275: u64 = undefined; + var x276: u1 = undefined; + addcarryxU64(&x275, &x276, 0x0, x247, x265); + var x277: u64 = undefined; + var x278: u1 = undefined; + addcarryxU64(&x277, &x278, x276, x249, x267); + var x279: u64 = undefined; + var x280: u1 = undefined; + addcarryxU64(&x279, &x280, x278, x251, x269); + var x281: u64 = undefined; + var x282: u1 = undefined; + addcarryxU64(&x281, &x282, x280, x253, x271); + var x283: u64 = undefined; + var x284: u1 = undefined; + addcarryxU64(&x283, &x284, x282, x255, x273); + var x285: u64 = undefined; + var x286: u1 = undefined; + addcarryxU64(&x285, &x286, x284, x257, @as(u64, x274)); + var x287: u64 = undefined; + var x288: u64 = undefined; + mulxU64(&x287, &x288, x275, 0x100000001); + var x289: u64 = undefined; + var x290: u64 = undefined; + mulxU64(&x289, &x290, x287, 0xffffffffffffffff); + var x291: u64 = undefined; + var x292: u64 = undefined; + mulxU64(&x291, &x292, x287, 0xffffffffffffffff); + var x293: u64 = undefined; + var x294: u64 = undefined; + mulxU64(&x293, &x294, x287, 0xffffffffffffffff); + var x295: u64 = undefined; + var x296: u64 = undefined; + mulxU64(&x295, &x296, x287, 0xfffffffffffffffe); + var x297: u64 = undefined; + var x298: u64 = undefined; + mulxU64(&x297, &x298, x287, 0xffffffff00000000); + var x299: u64 = undefined; + var x300: u64 = undefined; + mulxU64(&x299, &x300, x287, 0xffffffff); + var x301: u64 = undefined; + var x302: u1 = undefined; + addcarryxU64(&x301, &x302, 0x0, x300, x297); + var x303: u64 = undefined; + var x304: u1 = undefined; + addcarryxU64(&x303, &x304, x302, x298, x295); + var x305: u64 = undefined; + var x306: u1 = undefined; + addcarryxU64(&x305, &x306, x304, x296, x293); + var x307: u64 = undefined; + var x308: u1 = undefined; + addcarryxU64(&x307, &x308, x306, x294, x291); + var x309: u64 = undefined; + var x310: u1 = undefined; + addcarryxU64(&x309, &x310, x308, x292, x289); + var x311: u64 = undefined; + var x312: u1 = undefined; + addcarryxU64(&x311, &x312, 0x0, x275, x299); + var x313: u64 = undefined; + var x314: u1 = undefined; + addcarryxU64(&x313, &x314, x312, x277, x301); + var x315: u64 = undefined; + var x316: u1 = undefined; + addcarryxU64(&x315, &x316, x314, x279, x303); + var x317: u64 = undefined; + var x318: u1 = undefined; + addcarryxU64(&x317, &x318, x316, x281, x305); + var x319: u64 = undefined; + var x320: u1 = undefined; + addcarryxU64(&x319, &x320, x318, x283, x307); + var x321: u64 = undefined; + var x322: u1 = undefined; + addcarryxU64(&x321, &x322, x320, x285, x309); + var x323: u64 = undefined; + var x324: u1 = undefined; + addcarryxU64(&x323, &x324, x322, (@as(u64, x286) + @as(u64, x258)), (@as(u64, x310) + x290)); + var x325: u64 = undefined; + var x326: u64 = undefined; + mulxU64(&x325, &x326, x5, 0x200000000); + var x327: u64 = undefined; + var x328: u64 = undefined; + mulxU64(&x327, &x328, x5, 0xfffffffe00000000); + var x329: u64 = undefined; + var x330: u64 = undefined; + mulxU64(&x329, &x330, x5, 0x200000000); + var x331: u64 = undefined; + var x332: u64 = undefined; + mulxU64(&x331, &x332, x5, 0xfffffffe00000001); + var x333: u64 = undefined; + var x334: u1 = undefined; + addcarryxU64(&x333, &x334, 0x0, x332, x329); + var x335: u64 = undefined; + var x336: u1 = undefined; + addcarryxU64(&x335, &x336, x334, x330, x327); + var x337: u64 = undefined; + var x338: u1 = undefined; + addcarryxU64(&x337, &x338, x336, x328, x325); + var x339: u64 = undefined; + var x340: u1 = undefined; + addcarryxU64(&x339, &x340, x338, x326, x5); + var x341: u64 = undefined; + var x342: u1 = undefined; + addcarryxU64(&x341, &x342, 0x0, x313, x331); + var x343: u64 = undefined; + var x344: u1 = undefined; + addcarryxU64(&x343, &x344, x342, x315, x333); + var x345: u64 = undefined; + var x346: u1 = undefined; + addcarryxU64(&x345, &x346, x344, x317, x335); + var x347: u64 = undefined; + var x348: u1 = undefined; + addcarryxU64(&x347, &x348, x346, x319, x337); + var x349: u64 = undefined; + var x350: u1 = undefined; + addcarryxU64(&x349, &x350, x348, x321, x339); + var x351: u64 = undefined; + var x352: u1 = undefined; + addcarryxU64(&x351, &x352, x350, x323, @as(u64, x340)); + var x353: u64 = undefined; + var x354: u64 = undefined; + mulxU64(&x353, &x354, x341, 0x100000001); + var x355: u64 = undefined; + var x356: u64 = undefined; + mulxU64(&x355, &x356, x353, 0xffffffffffffffff); + var x357: u64 = undefined; + var x358: u64 = undefined; + mulxU64(&x357, &x358, x353, 0xffffffffffffffff); + var x359: u64 = undefined; + var x360: u64 = undefined; + mulxU64(&x359, &x360, x353, 0xffffffffffffffff); + var x361: u64 = undefined; + var x362: u64 = undefined; + mulxU64(&x361, &x362, x353, 0xfffffffffffffffe); + var x363: u64 = undefined; + var x364: u64 = undefined; + mulxU64(&x363, &x364, x353, 0xffffffff00000000); + var x365: u64 = undefined; + var x366: u64 = undefined; + mulxU64(&x365, &x366, x353, 0xffffffff); + var x367: u64 = undefined; + var x368: u1 = undefined; + addcarryxU64(&x367, &x368, 0x0, x366, x363); + var x369: u64 = undefined; + var x370: u1 = undefined; + addcarryxU64(&x369, &x370, x368, x364, x361); + var x371: u64 = undefined; + var x372: u1 = undefined; + addcarryxU64(&x371, &x372, x370, x362, x359); + var x373: u64 = undefined; + var x374: u1 = undefined; + addcarryxU64(&x373, &x374, x372, x360, x357); + var x375: u64 = undefined; + var x376: u1 = undefined; + addcarryxU64(&x375, &x376, x374, x358, x355); + var x377: u64 = undefined; + var x378: u1 = undefined; + addcarryxU64(&x377, &x378, 0x0, x341, x365); + var x379: u64 = undefined; + var x380: u1 = undefined; + addcarryxU64(&x379, &x380, x378, x343, x367); + var x381: u64 = undefined; + var x382: u1 = undefined; + addcarryxU64(&x381, &x382, x380, x345, x369); + var x383: u64 = undefined; + var x384: u1 = undefined; + addcarryxU64(&x383, &x384, x382, x347, x371); + var x385: u64 = undefined; + var x386: u1 = undefined; + addcarryxU64(&x385, &x386, x384, x349, x373); + var x387: u64 = undefined; + var x388: u1 = undefined; + addcarryxU64(&x387, &x388, x386, x351, x375); + var x389: u64 = undefined; + var x390: u1 = undefined; + addcarryxU64(&x389, &x390, x388, (@as(u64, x352) + @as(u64, x324)), (@as(u64, x376) + x356)); + var x391: u64 = undefined; + var x392: u1 = undefined; + subborrowxU64(&x391, &x392, 0x0, x379, 0xffffffff); + var x393: u64 = undefined; + var x394: u1 = undefined; + subborrowxU64(&x393, &x394, x392, x381, 0xffffffff00000000); + var x395: u64 = undefined; + var x396: u1 = undefined; + subborrowxU64(&x395, &x396, x394, x383, 0xfffffffffffffffe); + var x397: u64 = undefined; + var x398: u1 = undefined; + subborrowxU64(&x397, &x398, x396, x385, 0xffffffffffffffff); + var x399: u64 = undefined; + var x400: u1 = undefined; + subborrowxU64(&x399, &x400, x398, x387, 0xffffffffffffffff); + var x401: u64 = undefined; + var x402: u1 = undefined; + subborrowxU64(&x401, &x402, x400, x389, 0xffffffffffffffff); + var x403: u64 = undefined; + var x404: u1 = undefined; + subborrowxU64(&x403, &x404, x402, @as(u64, x390), 0x0); + var x405: u64 = undefined; + cmovznzU64(&x405, x404, x391, x379); + var x406: u64 = undefined; + cmovznzU64(&x406, x404, x393, x381); + var x407: u64 = undefined; + cmovznzU64(&x407, x404, x395, x383); + var x408: u64 = undefined; + cmovznzU64(&x408, x404, x397, x385); + var x409: u64 = undefined; + cmovznzU64(&x409, x404, x399, x387); + var x410: u64 = undefined; + cmovznzU64(&x410, x404, x401, x389); + out1[0] = x405; + out1[1] = x406; + out1[2] = x407; + out1[3] = x408; + out1[4] = x409; + out1[5] = x410; +} + +/// The function nonzero outputs a single non-zero word if the input is non-zero and zero otherwise. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// out1 = 0 ↔ eval (from_montgomery arg1) mod m = 0 +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +pub fn nonzero(out1: *u64, arg1: [6]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = ((arg1[0]) | ((arg1[1]) | ((arg1[2]) | ((arg1[3]) | ((arg1[4]) | (arg1[5])))))); + out1.* = x1; +} + +/// The function selectznz is a multi-limb conditional select. +/// +/// Postconditions: +/// out1 = (if arg1 = 0 then arg2 else arg3) +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn selectznz(out1: *[6]u64, arg1: u1, arg2: [6]u64, arg3: [6]u64) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + cmovznzU64(&x1, arg1, (arg2[0]), (arg3[0])); + var x2: u64 = undefined; + cmovznzU64(&x2, arg1, (arg2[1]), (arg3[1])); + var x3: u64 = undefined; + cmovznzU64(&x3, arg1, (arg2[2]), (arg3[2])); + var x4: u64 = undefined; + cmovznzU64(&x4, arg1, (arg2[3]), (arg3[3])); + var x5: u64 = undefined; + cmovznzU64(&x5, arg1, (arg2[4]), (arg3[4])); + var x6: u64 = undefined; + cmovznzU64(&x6, arg1, (arg2[5]), (arg3[5])); + out1[0] = x1; + out1[1] = x2; + out1[2] = x3; + out1[3] = x4; + out1[4] = x5; + out1[5] = x6; +} + +/// The function toBytes serializes a field element NOT in the Montgomery domain to bytes in little-endian order. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// out1 = map (λ x, ⌊((eval arg1 mod m) mod 2^(8 * (x + 1))) / 2^(8 * x)⌋) [0..47] +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]] +pub fn toBytes(out1: *[48]u8, arg1: [6]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[5]); + const x2 = (arg1[4]); + const x3 = (arg1[3]); + const x4 = (arg1[2]); + const x5 = (arg1[1]); + const x6 = (arg1[0]); + const x7 = @truncate(u8, (x6 & 0xff)); + const x8 = (x6 >> 8); + const x9 = @truncate(u8, (x8 & 0xff)); + const x10 = (x8 >> 8); + const x11 = @truncate(u8, (x10 & 0xff)); + const x12 = (x10 >> 8); + const x13 = @truncate(u8, (x12 & 0xff)); + const x14 = (x12 >> 8); + const x15 = @truncate(u8, (x14 & 0xff)); + const x16 = (x14 >> 8); + const x17 = @truncate(u8, (x16 & 0xff)); + const x18 = (x16 >> 8); + const x19 = @truncate(u8, (x18 & 0xff)); + const x20 = @truncate(u8, (x18 >> 8)); + const x21 = @truncate(u8, (x5 & 0xff)); + const x22 = (x5 >> 8); + const x23 = @truncate(u8, (x22 & 0xff)); + const x24 = (x22 >> 8); + const x25 = @truncate(u8, (x24 & 0xff)); + const x26 = (x24 >> 8); + const x27 = @truncate(u8, (x26 & 0xff)); + const x28 = (x26 >> 8); + const x29 = @truncate(u8, (x28 & 0xff)); + const x30 = (x28 >> 8); + const x31 = @truncate(u8, (x30 & 0xff)); + const x32 = (x30 >> 8); + const x33 = @truncate(u8, (x32 & 0xff)); + const x34 = @truncate(u8, (x32 >> 8)); + const x35 = @truncate(u8, (x4 & 0xff)); + const x36 = (x4 >> 8); + const x37 = @truncate(u8, (x36 & 0xff)); + const x38 = (x36 >> 8); + const x39 = @truncate(u8, (x38 & 0xff)); + const x40 = (x38 >> 8); + const x41 = @truncate(u8, (x40 & 0xff)); + const x42 = (x40 >> 8); + const x43 = @truncate(u8, (x42 & 0xff)); + const x44 = (x42 >> 8); + const x45 = @truncate(u8, (x44 & 0xff)); + const x46 = (x44 >> 8); + const x47 = @truncate(u8, (x46 & 0xff)); + const x48 = @truncate(u8, (x46 >> 8)); + const x49 = @truncate(u8, (x3 & 0xff)); + const x50 = (x3 >> 8); + const x51 = @truncate(u8, (x50 & 0xff)); + const x52 = (x50 >> 8); + const x53 = @truncate(u8, (x52 & 0xff)); + const x54 = (x52 >> 8); + const x55 = @truncate(u8, (x54 & 0xff)); + const x56 = (x54 >> 8); + const x57 = @truncate(u8, (x56 & 0xff)); + const x58 = (x56 >> 8); + const x59 = @truncate(u8, (x58 & 0xff)); + const x60 = (x58 >> 8); + const x61 = @truncate(u8, (x60 & 0xff)); + const x62 = @truncate(u8, (x60 >> 8)); + const x63 = @truncate(u8, (x2 & 0xff)); + const x64 = (x2 >> 8); + const x65 = @truncate(u8, (x64 & 0xff)); + const x66 = (x64 >> 8); + const x67 = @truncate(u8, (x66 & 0xff)); + const x68 = (x66 >> 8); + const x69 = @truncate(u8, (x68 & 0xff)); + const x70 = (x68 >> 8); + const x71 = @truncate(u8, (x70 & 0xff)); + const x72 = (x70 >> 8); + const x73 = @truncate(u8, (x72 & 0xff)); + const x74 = (x72 >> 8); + const x75 = @truncate(u8, (x74 & 0xff)); + const x76 = @truncate(u8, (x74 >> 8)); + const x77 = @truncate(u8, (x1 & 0xff)); + const x78 = (x1 >> 8); + const x79 = @truncate(u8, (x78 & 0xff)); + const x80 = (x78 >> 8); + const x81 = @truncate(u8, (x80 & 0xff)); + const x82 = (x80 >> 8); + const x83 = @truncate(u8, (x82 & 0xff)); + const x84 = (x82 >> 8); + const x85 = @truncate(u8, (x84 & 0xff)); + const x86 = (x84 >> 8); + const x87 = @truncate(u8, (x86 & 0xff)); + const x88 = (x86 >> 8); + const x89 = @truncate(u8, (x88 & 0xff)); + const x90 = @truncate(u8, (x88 >> 8)); + out1[0] = x7; + out1[1] = x9; + out1[2] = x11; + out1[3] = x13; + out1[4] = x15; + out1[5] = x17; + out1[6] = x19; + out1[7] = x20; + out1[8] = x21; + out1[9] = x23; + out1[10] = x25; + out1[11] = x27; + out1[12] = x29; + out1[13] = x31; + out1[14] = x33; + out1[15] = x34; + out1[16] = x35; + out1[17] = x37; + out1[18] = x39; + out1[19] = x41; + out1[20] = x43; + out1[21] = x45; + out1[22] = x47; + out1[23] = x48; + out1[24] = x49; + out1[25] = x51; + out1[26] = x53; + out1[27] = x55; + out1[28] = x57; + out1[29] = x59; + out1[30] = x61; + out1[31] = x62; + out1[32] = x63; + out1[33] = x65; + out1[34] = x67; + out1[35] = x69; + out1[36] = x71; + out1[37] = x73; + out1[38] = x75; + out1[39] = x76; + out1[40] = x77; + out1[41] = x79; + out1[42] = x81; + out1[43] = x83; + out1[44] = x85; + out1[45] = x87; + out1[46] = x89; + out1[47] = x90; +} + +/// The function fromBytes deserializes a field element NOT in the Montgomery domain from bytes in little-endian order. +/// +/// Preconditions: +/// 0 ≤ bytes_eval arg1 < m +/// Postconditions: +/// eval out1 mod m = bytes_eval arg1 mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn fromBytes(out1: *[6]u64, arg1: [48]u8) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (@as(u64, (arg1[47])) << 56); + const x2 = (@as(u64, (arg1[46])) << 48); + const x3 = (@as(u64, (arg1[45])) << 40); + const x4 = (@as(u64, (arg1[44])) << 32); + const x5 = (@as(u64, (arg1[43])) << 24); + const x6 = (@as(u64, (arg1[42])) << 16); + const x7 = (@as(u64, (arg1[41])) << 8); + const x8 = (arg1[40]); + const x9 = (@as(u64, (arg1[39])) << 56); + const x10 = (@as(u64, (arg1[38])) << 48); + const x11 = (@as(u64, (arg1[37])) << 40); + const x12 = (@as(u64, (arg1[36])) << 32); + const x13 = (@as(u64, (arg1[35])) << 24); + const x14 = (@as(u64, (arg1[34])) << 16); + const x15 = (@as(u64, (arg1[33])) << 8); + const x16 = (arg1[32]); + const x17 = (@as(u64, (arg1[31])) << 56); + const x18 = (@as(u64, (arg1[30])) << 48); + const x19 = (@as(u64, (arg1[29])) << 40); + const x20 = (@as(u64, (arg1[28])) << 32); + const x21 = (@as(u64, (arg1[27])) << 24); + const x22 = (@as(u64, (arg1[26])) << 16); + const x23 = (@as(u64, (arg1[25])) << 8); + const x24 = (arg1[24]); + const x25 = (@as(u64, (arg1[23])) << 56); + const x26 = (@as(u64, (arg1[22])) << 48); + const x27 = (@as(u64, (arg1[21])) << 40); + const x28 = (@as(u64, (arg1[20])) << 32); + const x29 = (@as(u64, (arg1[19])) << 24); + const x30 = (@as(u64, (arg1[18])) << 16); + const x31 = (@as(u64, (arg1[17])) << 8); + const x32 = (arg1[16]); + const x33 = (@as(u64, (arg1[15])) << 56); + const x34 = (@as(u64, (arg1[14])) << 48); + const x35 = (@as(u64, (arg1[13])) << 40); + const x36 = (@as(u64, (arg1[12])) << 32); + const x37 = (@as(u64, (arg1[11])) << 24); + const x38 = (@as(u64, (arg1[10])) << 16); + const x39 = (@as(u64, (arg1[9])) << 8); + const x40 = (arg1[8]); + const x41 = (@as(u64, (arg1[7])) << 56); + const x42 = (@as(u64, (arg1[6])) << 48); + const x43 = (@as(u64, (arg1[5])) << 40); + const x44 = (@as(u64, (arg1[4])) << 32); + const x45 = (@as(u64, (arg1[3])) << 24); + const x46 = (@as(u64, (arg1[2])) << 16); + const x47 = (@as(u64, (arg1[1])) << 8); + const x48 = (arg1[0]); + const x49 = (x47 + @as(u64, x48)); + const x50 = (x46 + x49); + const x51 = (x45 + x50); + const x52 = (x44 + x51); + const x53 = (x43 + x52); + const x54 = (x42 + x53); + const x55 = (x41 + x54); + const x56 = (x39 + @as(u64, x40)); + const x57 = (x38 + x56); + const x58 = (x37 + x57); + const x59 = (x36 + x58); + const x60 = (x35 + x59); + const x61 = (x34 + x60); + const x62 = (x33 + x61); + const x63 = (x31 + @as(u64, x32)); + const x64 = (x30 + x63); + const x65 = (x29 + x64); + const x66 = (x28 + x65); + const x67 = (x27 + x66); + const x68 = (x26 + x67); + const x69 = (x25 + x68); + const x70 = (x23 + @as(u64, x24)); + const x71 = (x22 + x70); + const x72 = (x21 + x71); + const x73 = (x20 + x72); + const x74 = (x19 + x73); + const x75 = (x18 + x74); + const x76 = (x17 + x75); + const x77 = (x15 + @as(u64, x16)); + const x78 = (x14 + x77); + const x79 = (x13 + x78); + const x80 = (x12 + x79); + const x81 = (x11 + x80); + const x82 = (x10 + x81); + const x83 = (x9 + x82); + const x84 = (x7 + @as(u64, x8)); + const x85 = (x6 + x84); + const x86 = (x5 + x85); + const x87 = (x4 + x86); + const x88 = (x3 + x87); + const x89 = (x2 + x88); + const x90 = (x1 + x89); + out1[0] = x55; + out1[1] = x62; + out1[2] = x69; + out1[3] = x76; + out1[4] = x83; + out1[5] = x90; +} + +/// The function setOne returns the field element one in the Montgomery domain. +/// +/// Postconditions: +/// eval (from_montgomery out1) mod m = 1 mod m +/// 0 ≤ eval out1 < m +/// +pub fn setOne(out1: *MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + out1[0] = 0xffffffff00000001; + out1[1] = 0xffffffff; + out1[2] = 0x1; + out1[3] = 0x0; + out1[4] = 0x0; + out1[5] = 0x0; +} + +/// The function msat returns the saturated representation of the prime modulus. +/// +/// Postconditions: +/// twos_complement_eval out1 = m +/// 0 ≤ eval out1 < m +/// +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn msat(out1: *[7]u64) void { + @setRuntimeSafety(mode == .Debug); + + out1[0] = 0xffffffff; + out1[1] = 0xffffffff00000000; + out1[2] = 0xfffffffffffffffe; + out1[3] = 0xffffffffffffffff; + out1[4] = 0xffffffffffffffff; + out1[5] = 0xffffffffffffffff; + out1[6] = 0x0; +} + +/// The function divstep computes a divstep. +/// +/// Preconditions: +/// 0 ≤ eval arg4 < m +/// 0 ≤ eval arg5 < m +/// Postconditions: +/// out1 = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then 1 - arg1 else 1 + arg1) +/// twos_complement_eval out2 = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then twos_complement_eval arg3 else twos_complement_eval arg2) +/// twos_complement_eval out3 = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then ⌊(twos_complement_eval arg3 - twos_complement_eval arg2) / 2⌋ else ⌊(twos_complement_eval arg3 + (twos_complement_eval arg3 mod 2) * twos_complement_eval arg2) / 2⌋) +/// eval (from_montgomery out4) mod m = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then (2 * eval (from_montgomery arg5)) mod m else (2 * eval (from_montgomery arg4)) mod m) +/// eval (from_montgomery out5) mod m = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then (eval (from_montgomery arg4) - eval (from_montgomery arg4)) mod m else (eval (from_montgomery arg5) + (twos_complement_eval arg3 mod 2) * eval (from_montgomery arg4)) mod m) +/// 0 ≤ eval out5 < m +/// 0 ≤ eval out5 < m +/// 0 ≤ eval out2 < m +/// 0 ≤ eval out3 < m +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0xffffffffffffffff] +/// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg4: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg5: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// out3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// out4: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// out5: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn divstep(out1: *u64, out2: *[7]u64, out3: *[7]u64, out4: *[6]u64, out5: *[6]u64, arg1: u64, arg2: [7]u64, arg3: [7]u64, arg4: [6]u64, arg5: [6]u64) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + addcarryxU64(&x1, &x2, 0x0, (~arg1), 0x1); + const x3 = (@truncate(u1, (x1 >> 63)) & @truncate(u1, ((arg3[0]) & 0x1))); + var x4: u64 = undefined; + var x5: u1 = undefined; + addcarryxU64(&x4, &x5, 0x0, (~arg1), 0x1); + var x6: u64 = undefined; + cmovznzU64(&x6, x3, arg1, x4); + var x7: u64 = undefined; + cmovznzU64(&x7, x3, (arg2[0]), (arg3[0])); + var x8: u64 = undefined; + cmovznzU64(&x8, x3, (arg2[1]), (arg3[1])); + var x9: u64 = undefined; + cmovznzU64(&x9, x3, (arg2[2]), (arg3[2])); + var x10: u64 = undefined; + cmovznzU64(&x10, x3, (arg2[3]), (arg3[3])); + var x11: u64 = undefined; + cmovznzU64(&x11, x3, (arg2[4]), (arg3[4])); + var x12: u64 = undefined; + cmovznzU64(&x12, x3, (arg2[5]), (arg3[5])); + var x13: u64 = undefined; + cmovznzU64(&x13, x3, (arg2[6]), (arg3[6])); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, 0x0, 0x1, (~(arg2[0]))); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, 0x0, (~(arg2[1]))); + var x18: u64 = undefined; + var x19: u1 = undefined; + addcarryxU64(&x18, &x19, x17, 0x0, (~(arg2[2]))); + var x20: u64 = undefined; + var x21: u1 = undefined; + addcarryxU64(&x20, &x21, x19, 0x0, (~(arg2[3]))); + var x22: u64 = undefined; + var x23: u1 = undefined; + addcarryxU64(&x22, &x23, x21, 0x0, (~(arg2[4]))); + var x24: u64 = undefined; + var x25: u1 = undefined; + addcarryxU64(&x24, &x25, x23, 0x0, (~(arg2[5]))); + var x26: u64 = undefined; + var x27: u1 = undefined; + addcarryxU64(&x26, &x27, x25, 0x0, (~(arg2[6]))); + var x28: u64 = undefined; + cmovznzU64(&x28, x3, (arg3[0]), x14); + var x29: u64 = undefined; + cmovznzU64(&x29, x3, (arg3[1]), x16); + var x30: u64 = undefined; + cmovznzU64(&x30, x3, (arg3[2]), x18); + var x31: u64 = undefined; + cmovznzU64(&x31, x3, (arg3[3]), x20); + var x32: u64 = undefined; + cmovznzU64(&x32, x3, (arg3[4]), x22); + var x33: u64 = undefined; + cmovznzU64(&x33, x3, (arg3[5]), x24); + var x34: u64 = undefined; + cmovznzU64(&x34, x3, (arg3[6]), x26); + var x35: u64 = undefined; + cmovznzU64(&x35, x3, (arg4[0]), (arg5[0])); + var x36: u64 = undefined; + cmovznzU64(&x36, x3, (arg4[1]), (arg5[1])); + var x37: u64 = undefined; + cmovznzU64(&x37, x3, (arg4[2]), (arg5[2])); + var x38: u64 = undefined; + cmovznzU64(&x38, x3, (arg4[3]), (arg5[3])); + var x39: u64 = undefined; + cmovznzU64(&x39, x3, (arg4[4]), (arg5[4])); + var x40: u64 = undefined; + cmovznzU64(&x40, x3, (arg4[5]), (arg5[5])); + var x41: u64 = undefined; + var x42: u1 = undefined; + addcarryxU64(&x41, &x42, 0x0, x35, x35); + var x43: u64 = undefined; + var x44: u1 = undefined; + addcarryxU64(&x43, &x44, x42, x36, x36); + var x45: u64 = undefined; + var x46: u1 = undefined; + addcarryxU64(&x45, &x46, x44, x37, x37); + var x47: u64 = undefined; + var x48: u1 = undefined; + addcarryxU64(&x47, &x48, x46, x38, x38); + var x49: u64 = undefined; + var x50: u1 = undefined; + addcarryxU64(&x49, &x50, x48, x39, x39); + var x51: u64 = undefined; + var x52: u1 = undefined; + addcarryxU64(&x51, &x52, x50, x40, x40); + var x53: u64 = undefined; + var x54: u1 = undefined; + subborrowxU64(&x53, &x54, 0x0, x41, 0xffffffff); + var x55: u64 = undefined; + var x56: u1 = undefined; + subborrowxU64(&x55, &x56, x54, x43, 0xffffffff00000000); + var x57: u64 = undefined; + var x58: u1 = undefined; + subborrowxU64(&x57, &x58, x56, x45, 0xfffffffffffffffe); + var x59: u64 = undefined; + var x60: u1 = undefined; + subborrowxU64(&x59, &x60, x58, x47, 0xffffffffffffffff); + var x61: u64 = undefined; + var x62: u1 = undefined; + subborrowxU64(&x61, &x62, x60, x49, 0xffffffffffffffff); + var x63: u64 = undefined; + var x64: u1 = undefined; + subborrowxU64(&x63, &x64, x62, x51, 0xffffffffffffffff); + var x65: u64 = undefined; + var x66: u1 = undefined; + subborrowxU64(&x65, &x66, x64, @as(u64, x52), 0x0); + const x67 = (arg4[5]); + const x68 = (arg4[4]); + const x69 = (arg4[3]); + const x70 = (arg4[2]); + const x71 = (arg4[1]); + const x72 = (arg4[0]); + var x73: u64 = undefined; + var x74: u1 = undefined; + subborrowxU64(&x73, &x74, 0x0, 0x0, x72); + var x75: u64 = undefined; + var x76: u1 = undefined; + subborrowxU64(&x75, &x76, x74, 0x0, x71); + var x77: u64 = undefined; + var x78: u1 = undefined; + subborrowxU64(&x77, &x78, x76, 0x0, x70); + var x79: u64 = undefined; + var x80: u1 = undefined; + subborrowxU64(&x79, &x80, x78, 0x0, x69); + var x81: u64 = undefined; + var x82: u1 = undefined; + subborrowxU64(&x81, &x82, x80, 0x0, x68); + var x83: u64 = undefined; + var x84: u1 = undefined; + subborrowxU64(&x83, &x84, x82, 0x0, x67); + var x85: u64 = undefined; + cmovznzU64(&x85, x84, 0x0, 0xffffffffffffffff); + var x86: u64 = undefined; + var x87: u1 = undefined; + addcarryxU64(&x86, &x87, 0x0, x73, (x85 & 0xffffffff)); + var x88: u64 = undefined; + var x89: u1 = undefined; + addcarryxU64(&x88, &x89, x87, x75, (x85 & 0xffffffff00000000)); + var x90: u64 = undefined; + var x91: u1 = undefined; + addcarryxU64(&x90, &x91, x89, x77, (x85 & 0xfffffffffffffffe)); + var x92: u64 = undefined; + var x93: u1 = undefined; + addcarryxU64(&x92, &x93, x91, x79, x85); + var x94: u64 = undefined; + var x95: u1 = undefined; + addcarryxU64(&x94, &x95, x93, x81, x85); + var x96: u64 = undefined; + var x97: u1 = undefined; + addcarryxU64(&x96, &x97, x95, x83, x85); + var x98: u64 = undefined; + cmovznzU64(&x98, x3, (arg5[0]), x86); + var x99: u64 = undefined; + cmovznzU64(&x99, x3, (arg5[1]), x88); + var x100: u64 = undefined; + cmovznzU64(&x100, x3, (arg5[2]), x90); + var x101: u64 = undefined; + cmovznzU64(&x101, x3, (arg5[3]), x92); + var x102: u64 = undefined; + cmovznzU64(&x102, x3, (arg5[4]), x94); + var x103: u64 = undefined; + cmovznzU64(&x103, x3, (arg5[5]), x96); + const x104 = @truncate(u1, (x28 & 0x1)); + var x105: u64 = undefined; + cmovznzU64(&x105, x104, 0x0, x7); + var x106: u64 = undefined; + cmovznzU64(&x106, x104, 0x0, x8); + var x107: u64 = undefined; + cmovznzU64(&x107, x104, 0x0, x9); + var x108: u64 = undefined; + cmovznzU64(&x108, x104, 0x0, x10); + var x109: u64 = undefined; + cmovznzU64(&x109, x104, 0x0, x11); + var x110: u64 = undefined; + cmovznzU64(&x110, x104, 0x0, x12); + var x111: u64 = undefined; + cmovznzU64(&x111, x104, 0x0, x13); + var x112: u64 = undefined; + var x113: u1 = undefined; + addcarryxU64(&x112, &x113, 0x0, x28, x105); + var x114: u64 = undefined; + var x115: u1 = undefined; + addcarryxU64(&x114, &x115, x113, x29, x106); + var x116: u64 = undefined; + var x117: u1 = undefined; + addcarryxU64(&x116, &x117, x115, x30, x107); + var x118: u64 = undefined; + var x119: u1 = undefined; + addcarryxU64(&x118, &x119, x117, x31, x108); + var x120: u64 = undefined; + var x121: u1 = undefined; + addcarryxU64(&x120, &x121, x119, x32, x109); + var x122: u64 = undefined; + var x123: u1 = undefined; + addcarryxU64(&x122, &x123, x121, x33, x110); + var x124: u64 = undefined; + var x125: u1 = undefined; + addcarryxU64(&x124, &x125, x123, x34, x111); + var x126: u64 = undefined; + cmovznzU64(&x126, x104, 0x0, x35); + var x127: u64 = undefined; + cmovznzU64(&x127, x104, 0x0, x36); + var x128: u64 = undefined; + cmovznzU64(&x128, x104, 0x0, x37); + var x129: u64 = undefined; + cmovznzU64(&x129, x104, 0x0, x38); + var x130: u64 = undefined; + cmovznzU64(&x130, x104, 0x0, x39); + var x131: u64 = undefined; + cmovznzU64(&x131, x104, 0x0, x40); + var x132: u64 = undefined; + var x133: u1 = undefined; + addcarryxU64(&x132, &x133, 0x0, x98, x126); + var x134: u64 = undefined; + var x135: u1 = undefined; + addcarryxU64(&x134, &x135, x133, x99, x127); + var x136: u64 = undefined; + var x137: u1 = undefined; + addcarryxU64(&x136, &x137, x135, x100, x128); + var x138: u64 = undefined; + var x139: u1 = undefined; + addcarryxU64(&x138, &x139, x137, x101, x129); + var x140: u64 = undefined; + var x141: u1 = undefined; + addcarryxU64(&x140, &x141, x139, x102, x130); + var x142: u64 = undefined; + var x143: u1 = undefined; + addcarryxU64(&x142, &x143, x141, x103, x131); + var x144: u64 = undefined; + var x145: u1 = undefined; + subborrowxU64(&x144, &x145, 0x0, x132, 0xffffffff); + var x146: u64 = undefined; + var x147: u1 = undefined; + subborrowxU64(&x146, &x147, x145, x134, 0xffffffff00000000); + var x148: u64 = undefined; + var x149: u1 = undefined; + subborrowxU64(&x148, &x149, x147, x136, 0xfffffffffffffffe); + var x150: u64 = undefined; + var x151: u1 = undefined; + subborrowxU64(&x150, &x151, x149, x138, 0xffffffffffffffff); + var x152: u64 = undefined; + var x153: u1 = undefined; + subborrowxU64(&x152, &x153, x151, x140, 0xffffffffffffffff); + var x154: u64 = undefined; + var x155: u1 = undefined; + subborrowxU64(&x154, &x155, x153, x142, 0xffffffffffffffff); + var x156: u64 = undefined; + var x157: u1 = undefined; + subborrowxU64(&x156, &x157, x155, @as(u64, x143), 0x0); + var x158: u64 = undefined; + var x159: u1 = undefined; + addcarryxU64(&x158, &x159, 0x0, x6, 0x1); + const x160 = ((x112 >> 1) | ((x114 << 63) & 0xffffffffffffffff)); + const x161 = ((x114 >> 1) | ((x116 << 63) & 0xffffffffffffffff)); + const x162 = ((x116 >> 1) | ((x118 << 63) & 0xffffffffffffffff)); + const x163 = ((x118 >> 1) | ((x120 << 63) & 0xffffffffffffffff)); + const x164 = ((x120 >> 1) | ((x122 << 63) & 0xffffffffffffffff)); + const x165 = ((x122 >> 1) | ((x124 << 63) & 0xffffffffffffffff)); + const x166 = ((x124 & 0x8000000000000000) | (x124 >> 1)); + var x167: u64 = undefined; + cmovznzU64(&x167, x66, x53, x41); + var x168: u64 = undefined; + cmovznzU64(&x168, x66, x55, x43); + var x169: u64 = undefined; + cmovznzU64(&x169, x66, x57, x45); + var x170: u64 = undefined; + cmovznzU64(&x170, x66, x59, x47); + var x171: u64 = undefined; + cmovznzU64(&x171, x66, x61, x49); + var x172: u64 = undefined; + cmovznzU64(&x172, x66, x63, x51); + var x173: u64 = undefined; + cmovznzU64(&x173, x157, x144, x132); + var x174: u64 = undefined; + cmovznzU64(&x174, x157, x146, x134); + var x175: u64 = undefined; + cmovznzU64(&x175, x157, x148, x136); + var x176: u64 = undefined; + cmovznzU64(&x176, x157, x150, x138); + var x177: u64 = undefined; + cmovznzU64(&x177, x157, x152, x140); + var x178: u64 = undefined; + cmovznzU64(&x178, x157, x154, x142); + out1.* = x158; + out2[0] = x7; + out2[1] = x8; + out2[2] = x9; + out2[3] = x10; + out2[4] = x11; + out2[5] = x12; + out2[6] = x13; + out3[0] = x160; + out3[1] = x161; + out3[2] = x162; + out3[3] = x163; + out3[4] = x164; + out3[5] = x165; + out3[6] = x166; + out4[0] = x167; + out4[1] = x168; + out4[2] = x169; + out4[3] = x170; + out4[4] = x171; + out4[5] = x172; + out5[0] = x173; + out5[1] = x174; + out5[2] = x175; + out5[3] = x176; + out5[4] = x177; + out5[5] = x178; +} + +/// The function divstepPrecomp returns the precomputed value for Bernstein-Yang-inversion (in montgomery form). +/// +/// Postconditions: +/// eval (from_montgomery out1) = ⌊(m - 1) / 2⌋^(if ⌊log2 m⌋ + 1 < 46 then ⌊(49 * (⌊log2 m⌋ + 1) + 80) / 17⌋ else ⌊(49 * (⌊log2 m⌋ + 1) + 57) / 17⌋) +/// 0 ≤ eval out1 < m +/// +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn divstepPrecomp(out1: *[6]u64) void { + @setRuntimeSafety(mode == .Debug); + + out1[0] = 0xfff69400fff18fff; + out1[1] = 0x2b7feffffd3ff; + out1[2] = 0xfffedbfffffe97ff; + out1[3] = 0x2840000002fff; + out1[4] = 0x6040000050400; + out1[5] = 0xfffc480000038000; +} diff --git a/lib/std/crypto/pcurves/p384/p384_scalar_64.zig b/lib/std/crypto/pcurves/p384/p384_scalar_64.zig new file mode 100644 index 0000000000..e45e43f98c --- /dev/null +++ b/lib/std/crypto/pcurves/p384/p384_scalar_64.zig @@ -0,0 +1,3632 @@ +// Autogenerated: 'src/ExtractionOCaml/word_by_word_montgomery' --lang Zig --internal-static --public-function-case camelCase --private-function-case camelCase --public-type-case UpperCamelCase --private-type-case UpperCamelCase --no-prefix-fiat --package-name p384_scalar '' 64 '2^384 - 1388124618062372383947042015309946732620727252194336364173' mul square add sub opp from_montgomery to_montgomery nonzero selectznz to_bytes from_bytes one msat divstep divstep_precomp +// curve description (via package name): p384_scalar +// machine_wordsize = 64 (from "64") +// requested operations: mul, square, add, sub, opp, from_montgomery, to_montgomery, nonzero, selectznz, to_bytes, from_bytes, one, msat, divstep, divstep_precomp +// m = 0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973 (from "2^384 - 1388124618062372383947042015309946732620727252194336364173") +// +// NOTE: In addition to the bounds specified above each function, all +// functions synthesized for this Montgomery arithmetic require the +// input to be strictly less than the prime modulus (m), and also +// require the input to be in the unique saturated representation. +// All functions also ensure that these two properties are true of +// return values. +// +// Computed values: +// eval z = z[0] + (z[1] << 64) + (z[2] << 128) + (z[3] << 192) + (z[4] << 256) + (z[5] << 0x140) +// bytes_eval z = z[0] + (z[1] << 8) + (z[2] << 16) + (z[3] << 24) + (z[4] << 32) + (z[5] << 40) + (z[6] << 48) + (z[7] << 56) + (z[8] << 64) + (z[9] << 72) + (z[10] << 80) + (z[11] << 88) + (z[12] << 96) + (z[13] << 104) + (z[14] << 112) + (z[15] << 120) + (z[16] << 128) + (z[17] << 136) + (z[18] << 144) + (z[19] << 152) + (z[20] << 160) + (z[21] << 168) + (z[22] << 176) + (z[23] << 184) + (z[24] << 192) + (z[25] << 200) + (z[26] << 208) + (z[27] << 216) + (z[28] << 224) + (z[29] << 232) + (z[30] << 240) + (z[31] << 248) + (z[32] << 256) + (z[33] << 0x108) + (z[34] << 0x110) + (z[35] << 0x118) + (z[36] << 0x120) + (z[37] << 0x128) + (z[38] << 0x130) + (z[39] << 0x138) + (z[40] << 0x140) + (z[41] << 0x148) + (z[42] << 0x150) + (z[43] << 0x158) + (z[44] << 0x160) + (z[45] << 0x168) + (z[46] << 0x170) + (z[47] << 0x178) +// twos_complement_eval z = let x1 := z[0] + (z[1] << 64) + (z[2] << 128) + (z[3] << 192) + (z[4] << 256) + (z[5] << 0x140) in +// if x1 & (2^384-1) < 2^383 then x1 & (2^384-1) else (x1 & (2^384-1)) - 2^384 + +const std = @import("std"); +const mode = @import("builtin").mode; // Checked arithmetic is disabled in non-debug modes to avoid side channels + +// The type MontgomeryDomainFieldElement is a field element in the Montgomery domain. +// Bounds: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub const MontgomeryDomainFieldElement = [6]u64; + +// The type NonMontgomeryDomainFieldElement is a field element NOT in the Montgomery domain. +// Bounds: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub const NonMontgomeryDomainFieldElement = [6]u64; + +/// The function addcarryxU64 is an addition with carry. +/// +/// Postconditions: +/// out1 = (arg1 + arg2 + arg3) mod 2^64 +/// out2 = ⌊(arg1 + arg2 + arg3) / 2^64⌋ +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// arg3: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [0x0 ~> 0x1] +inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { + @setRuntimeSafety(mode == .Debug); + + var t: u64 = undefined; + const carry1 = @addWithOverflow(u64, arg2, arg3, &t); + const carry2 = @addWithOverflow(u64, t, arg1, out1); + out2.* = @boolToInt(carry1) | @boolToInt(carry2); +} + +/// The function subborrowxU64 is a subtraction with borrow. +/// +/// Postconditions: +/// out1 = (-arg1 + arg2 + -arg3) mod 2^64 +/// out2 = -⌊(-arg1 + arg2 + -arg3) / 2^64⌋ +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// arg3: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [0x0 ~> 0x1] +inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { + @setRuntimeSafety(mode == .Debug); + + var t: u64 = undefined; + const carry1 = @subWithOverflow(u64, arg2, arg3, &t); + const carry2 = @subWithOverflow(u64, t, arg1, out1); + out2.* = @boolToInt(carry1) | @boolToInt(carry2); +} + +/// The function mulxU64 is a multiplication, returning the full double-width result. +/// +/// Postconditions: +/// out1 = (arg1 * arg2) mod 2^64 +/// out2 = ⌊arg1 * arg2 / 2^64⌋ +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0xffffffffffffffff] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [0x0 ~> 0xffffffffffffffff] +inline fn mulxU64(out1: *u64, out2: *u64, arg1: u64, arg2: u64) void { + @setRuntimeSafety(mode == .Debug); + + const x = @as(u128, arg1) * @as(u128, arg2); + out1.* = @truncate(u64, x); + out2.* = @truncate(u64, x >> 64); +} + +/// The function cmovznzU64 is a single-word conditional move. +/// +/// Postconditions: +/// out1 = (if arg1 = 0 then arg2 else arg3) +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// arg3: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +inline fn cmovznzU64(out1: *u64, arg1: u1, arg2: u64, arg3: u64) void { + @setRuntimeSafety(mode == .Debug); + + const mask = 0 -% @as(u64, arg1); + out1.* = (mask & arg3) | ((~mask) & arg2); +} + +/// The function mul multiplies two field elements in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// 0 ≤ eval arg2 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m +/// 0 ≤ eval out1 < m +/// +pub fn mul(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement, arg2: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[1]); + const x2 = (arg1[2]); + const x3 = (arg1[3]); + const x4 = (arg1[4]); + const x5 = (arg1[5]); + const x6 = (arg1[0]); + var x7: u64 = undefined; + var x8: u64 = undefined; + mulxU64(&x7, &x8, x6, (arg2[5])); + var x9: u64 = undefined; + var x10: u64 = undefined; + mulxU64(&x9, &x10, x6, (arg2[4])); + var x11: u64 = undefined; + var x12: u64 = undefined; + mulxU64(&x11, &x12, x6, (arg2[3])); + var x13: u64 = undefined; + var x14: u64 = undefined; + mulxU64(&x13, &x14, x6, (arg2[2])); + var x15: u64 = undefined; + var x16: u64 = undefined; + mulxU64(&x15, &x16, x6, (arg2[1])); + var x17: u64 = undefined; + var x18: u64 = undefined; + mulxU64(&x17, &x18, x6, (arg2[0])); + var x19: u64 = undefined; + var x20: u1 = undefined; + addcarryxU64(&x19, &x20, 0x0, x18, x15); + var x21: u64 = undefined; + var x22: u1 = undefined; + addcarryxU64(&x21, &x22, x20, x16, x13); + var x23: u64 = undefined; + var x24: u1 = undefined; + addcarryxU64(&x23, &x24, x22, x14, x11); + var x25: u64 = undefined; + var x26: u1 = undefined; + addcarryxU64(&x25, &x26, x24, x12, x9); + var x27: u64 = undefined; + var x28: u1 = undefined; + addcarryxU64(&x27, &x28, x26, x10, x7); + const x29 = (@as(u64, x28) + x8); + var x30: u64 = undefined; + var x31: u64 = undefined; + mulxU64(&x30, &x31, x17, 0x6ed46089e88fdc45); + var x32: u64 = undefined; + var x33: u64 = undefined; + mulxU64(&x32, &x33, x30, 0xffffffffffffffff); + var x34: u64 = undefined; + var x35: u64 = undefined; + mulxU64(&x34, &x35, x30, 0xffffffffffffffff); + var x36: u64 = undefined; + var x37: u64 = undefined; + mulxU64(&x36, &x37, x30, 0xffffffffffffffff); + var x38: u64 = undefined; + var x39: u64 = undefined; + mulxU64(&x38, &x39, x30, 0xc7634d81f4372ddf); + var x40: u64 = undefined; + var x41: u64 = undefined; + mulxU64(&x40, &x41, x30, 0x581a0db248b0a77a); + var x42: u64 = undefined; + var x43: u64 = undefined; + mulxU64(&x42, &x43, x30, 0xecec196accc52973); + var x44: u64 = undefined; + var x45: u1 = undefined; + addcarryxU64(&x44, &x45, 0x0, x43, x40); + var x46: u64 = undefined; + var x47: u1 = undefined; + addcarryxU64(&x46, &x47, x45, x41, x38); + var x48: u64 = undefined; + var x49: u1 = undefined; + addcarryxU64(&x48, &x49, x47, x39, x36); + var x50: u64 = undefined; + var x51: u1 = undefined; + addcarryxU64(&x50, &x51, x49, x37, x34); + var x52: u64 = undefined; + var x53: u1 = undefined; + addcarryxU64(&x52, &x53, x51, x35, x32); + const x54 = (@as(u64, x53) + x33); + var x55: u64 = undefined; + var x56: u1 = undefined; + addcarryxU64(&x55, &x56, 0x0, x17, x42); + var x57: u64 = undefined; + var x58: u1 = undefined; + addcarryxU64(&x57, &x58, x56, x19, x44); + var x59: u64 = undefined; + var x60: u1 = undefined; + addcarryxU64(&x59, &x60, x58, x21, x46); + var x61: u64 = undefined; + var x62: u1 = undefined; + addcarryxU64(&x61, &x62, x60, x23, x48); + var x63: u64 = undefined; + var x64: u1 = undefined; + addcarryxU64(&x63, &x64, x62, x25, x50); + var x65: u64 = undefined; + var x66: u1 = undefined; + addcarryxU64(&x65, &x66, x64, x27, x52); + var x67: u64 = undefined; + var x68: u1 = undefined; + addcarryxU64(&x67, &x68, x66, x29, x54); + var x69: u64 = undefined; + var x70: u64 = undefined; + mulxU64(&x69, &x70, x1, (arg2[5])); + var x71: u64 = undefined; + var x72: u64 = undefined; + mulxU64(&x71, &x72, x1, (arg2[4])); + var x73: u64 = undefined; + var x74: u64 = undefined; + mulxU64(&x73, &x74, x1, (arg2[3])); + var x75: u64 = undefined; + var x76: u64 = undefined; + mulxU64(&x75, &x76, x1, (arg2[2])); + var x77: u64 = undefined; + var x78: u64 = undefined; + mulxU64(&x77, &x78, x1, (arg2[1])); + var x79: u64 = undefined; + var x80: u64 = undefined; + mulxU64(&x79, &x80, x1, (arg2[0])); + var x81: u64 = undefined; + var x82: u1 = undefined; + addcarryxU64(&x81, &x82, 0x0, x80, x77); + var x83: u64 = undefined; + var x84: u1 = undefined; + addcarryxU64(&x83, &x84, x82, x78, x75); + var x85: u64 = undefined; + var x86: u1 = undefined; + addcarryxU64(&x85, &x86, x84, x76, x73); + var x87: u64 = undefined; + var x88: u1 = undefined; + addcarryxU64(&x87, &x88, x86, x74, x71); + var x89: u64 = undefined; + var x90: u1 = undefined; + addcarryxU64(&x89, &x90, x88, x72, x69); + const x91 = (@as(u64, x90) + x70); + var x92: u64 = undefined; + var x93: u1 = undefined; + addcarryxU64(&x92, &x93, 0x0, x57, x79); + var x94: u64 = undefined; + var x95: u1 = undefined; + addcarryxU64(&x94, &x95, x93, x59, x81); + var x96: u64 = undefined; + var x97: u1 = undefined; + addcarryxU64(&x96, &x97, x95, x61, x83); + var x98: u64 = undefined; + var x99: u1 = undefined; + addcarryxU64(&x98, &x99, x97, x63, x85); + var x100: u64 = undefined; + var x101: u1 = undefined; + addcarryxU64(&x100, &x101, x99, x65, x87); + var x102: u64 = undefined; + var x103: u1 = undefined; + addcarryxU64(&x102, &x103, x101, x67, x89); + var x104: u64 = undefined; + var x105: u1 = undefined; + addcarryxU64(&x104, &x105, x103, @as(u64, x68), x91); + var x106: u64 = undefined; + var x107: u64 = undefined; + mulxU64(&x106, &x107, x92, 0x6ed46089e88fdc45); + var x108: u64 = undefined; + var x109: u64 = undefined; + mulxU64(&x108, &x109, x106, 0xffffffffffffffff); + var x110: u64 = undefined; + var x111: u64 = undefined; + mulxU64(&x110, &x111, x106, 0xffffffffffffffff); + var x112: u64 = undefined; + var x113: u64 = undefined; + mulxU64(&x112, &x113, x106, 0xffffffffffffffff); + var x114: u64 = undefined; + var x115: u64 = undefined; + mulxU64(&x114, &x115, x106, 0xc7634d81f4372ddf); + var x116: u64 = undefined; + var x117: u64 = undefined; + mulxU64(&x116, &x117, x106, 0x581a0db248b0a77a); + var x118: u64 = undefined; + var x119: u64 = undefined; + mulxU64(&x118, &x119, x106, 0xecec196accc52973); + var x120: u64 = undefined; + var x121: u1 = undefined; + addcarryxU64(&x120, &x121, 0x0, x119, x116); + var x122: u64 = undefined; + var x123: u1 = undefined; + addcarryxU64(&x122, &x123, x121, x117, x114); + var x124: u64 = undefined; + var x125: u1 = undefined; + addcarryxU64(&x124, &x125, x123, x115, x112); + var x126: u64 = undefined; + var x127: u1 = undefined; + addcarryxU64(&x126, &x127, x125, x113, x110); + var x128: u64 = undefined; + var x129: u1 = undefined; + addcarryxU64(&x128, &x129, x127, x111, x108); + const x130 = (@as(u64, x129) + x109); + var x131: u64 = undefined; + var x132: u1 = undefined; + addcarryxU64(&x131, &x132, 0x0, x92, x118); + var x133: u64 = undefined; + var x134: u1 = undefined; + addcarryxU64(&x133, &x134, x132, x94, x120); + var x135: u64 = undefined; + var x136: u1 = undefined; + addcarryxU64(&x135, &x136, x134, x96, x122); + var x137: u64 = undefined; + var x138: u1 = undefined; + addcarryxU64(&x137, &x138, x136, x98, x124); + var x139: u64 = undefined; + var x140: u1 = undefined; + addcarryxU64(&x139, &x140, x138, x100, x126); + var x141: u64 = undefined; + var x142: u1 = undefined; + addcarryxU64(&x141, &x142, x140, x102, x128); + var x143: u64 = undefined; + var x144: u1 = undefined; + addcarryxU64(&x143, &x144, x142, x104, x130); + const x145 = (@as(u64, x144) + @as(u64, x105)); + var x146: u64 = undefined; + var x147: u64 = undefined; + mulxU64(&x146, &x147, x2, (arg2[5])); + var x148: u64 = undefined; + var x149: u64 = undefined; + mulxU64(&x148, &x149, x2, (arg2[4])); + var x150: u64 = undefined; + var x151: u64 = undefined; + mulxU64(&x150, &x151, x2, (arg2[3])); + var x152: u64 = undefined; + var x153: u64 = undefined; + mulxU64(&x152, &x153, x2, (arg2[2])); + var x154: u64 = undefined; + var x155: u64 = undefined; + mulxU64(&x154, &x155, x2, (arg2[1])); + var x156: u64 = undefined; + var x157: u64 = undefined; + mulxU64(&x156, &x157, x2, (arg2[0])); + var x158: u64 = undefined; + var x159: u1 = undefined; + addcarryxU64(&x158, &x159, 0x0, x157, x154); + var x160: u64 = undefined; + var x161: u1 = undefined; + addcarryxU64(&x160, &x161, x159, x155, x152); + var x162: u64 = undefined; + var x163: u1 = undefined; + addcarryxU64(&x162, &x163, x161, x153, x150); + var x164: u64 = undefined; + var x165: u1 = undefined; + addcarryxU64(&x164, &x165, x163, x151, x148); + var x166: u64 = undefined; + var x167: u1 = undefined; + addcarryxU64(&x166, &x167, x165, x149, x146); + const x168 = (@as(u64, x167) + x147); + var x169: u64 = undefined; + var x170: u1 = undefined; + addcarryxU64(&x169, &x170, 0x0, x133, x156); + var x171: u64 = undefined; + var x172: u1 = undefined; + addcarryxU64(&x171, &x172, x170, x135, x158); + var x173: u64 = undefined; + var x174: u1 = undefined; + addcarryxU64(&x173, &x174, x172, x137, x160); + var x175: u64 = undefined; + var x176: u1 = undefined; + addcarryxU64(&x175, &x176, x174, x139, x162); + var x177: u64 = undefined; + var x178: u1 = undefined; + addcarryxU64(&x177, &x178, x176, x141, x164); + var x179: u64 = undefined; + var x180: u1 = undefined; + addcarryxU64(&x179, &x180, x178, x143, x166); + var x181: u64 = undefined; + var x182: u1 = undefined; + addcarryxU64(&x181, &x182, x180, x145, x168); + var x183: u64 = undefined; + var x184: u64 = undefined; + mulxU64(&x183, &x184, x169, 0x6ed46089e88fdc45); + var x185: u64 = undefined; + var x186: u64 = undefined; + mulxU64(&x185, &x186, x183, 0xffffffffffffffff); + var x187: u64 = undefined; + var x188: u64 = undefined; + mulxU64(&x187, &x188, x183, 0xffffffffffffffff); + var x189: u64 = undefined; + var x190: u64 = undefined; + mulxU64(&x189, &x190, x183, 0xffffffffffffffff); + var x191: u64 = undefined; + var x192: u64 = undefined; + mulxU64(&x191, &x192, x183, 0xc7634d81f4372ddf); + var x193: u64 = undefined; + var x194: u64 = undefined; + mulxU64(&x193, &x194, x183, 0x581a0db248b0a77a); + var x195: u64 = undefined; + var x196: u64 = undefined; + mulxU64(&x195, &x196, x183, 0xecec196accc52973); + var x197: u64 = undefined; + var x198: u1 = undefined; + addcarryxU64(&x197, &x198, 0x0, x196, x193); + var x199: u64 = undefined; + var x200: u1 = undefined; + addcarryxU64(&x199, &x200, x198, x194, x191); + var x201: u64 = undefined; + var x202: u1 = undefined; + addcarryxU64(&x201, &x202, x200, x192, x189); + var x203: u64 = undefined; + var x204: u1 = undefined; + addcarryxU64(&x203, &x204, x202, x190, x187); + var x205: u64 = undefined; + var x206: u1 = undefined; + addcarryxU64(&x205, &x206, x204, x188, x185); + const x207 = (@as(u64, x206) + x186); + var x208: u64 = undefined; + var x209: u1 = undefined; + addcarryxU64(&x208, &x209, 0x0, x169, x195); + var x210: u64 = undefined; + var x211: u1 = undefined; + addcarryxU64(&x210, &x211, x209, x171, x197); + var x212: u64 = undefined; + var x213: u1 = undefined; + addcarryxU64(&x212, &x213, x211, x173, x199); + var x214: u64 = undefined; + var x215: u1 = undefined; + addcarryxU64(&x214, &x215, x213, x175, x201); + var x216: u64 = undefined; + var x217: u1 = undefined; + addcarryxU64(&x216, &x217, x215, x177, x203); + var x218: u64 = undefined; + var x219: u1 = undefined; + addcarryxU64(&x218, &x219, x217, x179, x205); + var x220: u64 = undefined; + var x221: u1 = undefined; + addcarryxU64(&x220, &x221, x219, x181, x207); + const x222 = (@as(u64, x221) + @as(u64, x182)); + var x223: u64 = undefined; + var x224: u64 = undefined; + mulxU64(&x223, &x224, x3, (arg2[5])); + var x225: u64 = undefined; + var x226: u64 = undefined; + mulxU64(&x225, &x226, x3, (arg2[4])); + var x227: u64 = undefined; + var x228: u64 = undefined; + mulxU64(&x227, &x228, x3, (arg2[3])); + var x229: u64 = undefined; + var x230: u64 = undefined; + mulxU64(&x229, &x230, x3, (arg2[2])); + var x231: u64 = undefined; + var x232: u64 = undefined; + mulxU64(&x231, &x232, x3, (arg2[1])); + var x233: u64 = undefined; + var x234: u64 = undefined; + mulxU64(&x233, &x234, x3, (arg2[0])); + var x235: u64 = undefined; + var x236: u1 = undefined; + addcarryxU64(&x235, &x236, 0x0, x234, x231); + var x237: u64 = undefined; + var x238: u1 = undefined; + addcarryxU64(&x237, &x238, x236, x232, x229); + var x239: u64 = undefined; + var x240: u1 = undefined; + addcarryxU64(&x239, &x240, x238, x230, x227); + var x241: u64 = undefined; + var x242: u1 = undefined; + addcarryxU64(&x241, &x242, x240, x228, x225); + var x243: u64 = undefined; + var x244: u1 = undefined; + addcarryxU64(&x243, &x244, x242, x226, x223); + const x245 = (@as(u64, x244) + x224); + var x246: u64 = undefined; + var x247: u1 = undefined; + addcarryxU64(&x246, &x247, 0x0, x210, x233); + var x248: u64 = undefined; + var x249: u1 = undefined; + addcarryxU64(&x248, &x249, x247, x212, x235); + var x250: u64 = undefined; + var x251: u1 = undefined; + addcarryxU64(&x250, &x251, x249, x214, x237); + var x252: u64 = undefined; + var x253: u1 = undefined; + addcarryxU64(&x252, &x253, x251, x216, x239); + var x254: u64 = undefined; + var x255: u1 = undefined; + addcarryxU64(&x254, &x255, x253, x218, x241); + var x256: u64 = undefined; + var x257: u1 = undefined; + addcarryxU64(&x256, &x257, x255, x220, x243); + var x258: u64 = undefined; + var x259: u1 = undefined; + addcarryxU64(&x258, &x259, x257, x222, x245); + var x260: u64 = undefined; + var x261: u64 = undefined; + mulxU64(&x260, &x261, x246, 0x6ed46089e88fdc45); + var x262: u64 = undefined; + var x263: u64 = undefined; + mulxU64(&x262, &x263, x260, 0xffffffffffffffff); + var x264: u64 = undefined; + var x265: u64 = undefined; + mulxU64(&x264, &x265, x260, 0xffffffffffffffff); + var x266: u64 = undefined; + var x267: u64 = undefined; + mulxU64(&x266, &x267, x260, 0xffffffffffffffff); + var x268: u64 = undefined; + var x269: u64 = undefined; + mulxU64(&x268, &x269, x260, 0xc7634d81f4372ddf); + var x270: u64 = undefined; + var x271: u64 = undefined; + mulxU64(&x270, &x271, x260, 0x581a0db248b0a77a); + var x272: u64 = undefined; + var x273: u64 = undefined; + mulxU64(&x272, &x273, x260, 0xecec196accc52973); + var x274: u64 = undefined; + var x275: u1 = undefined; + addcarryxU64(&x274, &x275, 0x0, x273, x270); + var x276: u64 = undefined; + var x277: u1 = undefined; + addcarryxU64(&x276, &x277, x275, x271, x268); + var x278: u64 = undefined; + var x279: u1 = undefined; + addcarryxU64(&x278, &x279, x277, x269, x266); + var x280: u64 = undefined; + var x281: u1 = undefined; + addcarryxU64(&x280, &x281, x279, x267, x264); + var x282: u64 = undefined; + var x283: u1 = undefined; + addcarryxU64(&x282, &x283, x281, x265, x262); + const x284 = (@as(u64, x283) + x263); + var x285: u64 = undefined; + var x286: u1 = undefined; + addcarryxU64(&x285, &x286, 0x0, x246, x272); + var x287: u64 = undefined; + var x288: u1 = undefined; + addcarryxU64(&x287, &x288, x286, x248, x274); + var x289: u64 = undefined; + var x290: u1 = undefined; + addcarryxU64(&x289, &x290, x288, x250, x276); + var x291: u64 = undefined; + var x292: u1 = undefined; + addcarryxU64(&x291, &x292, x290, x252, x278); + var x293: u64 = undefined; + var x294: u1 = undefined; + addcarryxU64(&x293, &x294, x292, x254, x280); + var x295: u64 = undefined; + var x296: u1 = undefined; + addcarryxU64(&x295, &x296, x294, x256, x282); + var x297: u64 = undefined; + var x298: u1 = undefined; + addcarryxU64(&x297, &x298, x296, x258, x284); + const x299 = (@as(u64, x298) + @as(u64, x259)); + var x300: u64 = undefined; + var x301: u64 = undefined; + mulxU64(&x300, &x301, x4, (arg2[5])); + var x302: u64 = undefined; + var x303: u64 = undefined; + mulxU64(&x302, &x303, x4, (arg2[4])); + var x304: u64 = undefined; + var x305: u64 = undefined; + mulxU64(&x304, &x305, x4, (arg2[3])); + var x306: u64 = undefined; + var x307: u64 = undefined; + mulxU64(&x306, &x307, x4, (arg2[2])); + var x308: u64 = undefined; + var x309: u64 = undefined; + mulxU64(&x308, &x309, x4, (arg2[1])); + var x310: u64 = undefined; + var x311: u64 = undefined; + mulxU64(&x310, &x311, x4, (arg2[0])); + var x312: u64 = undefined; + var x313: u1 = undefined; + addcarryxU64(&x312, &x313, 0x0, x311, x308); + var x314: u64 = undefined; + var x315: u1 = undefined; + addcarryxU64(&x314, &x315, x313, x309, x306); + var x316: u64 = undefined; + var x317: u1 = undefined; + addcarryxU64(&x316, &x317, x315, x307, x304); + var x318: u64 = undefined; + var x319: u1 = undefined; + addcarryxU64(&x318, &x319, x317, x305, x302); + var x320: u64 = undefined; + var x321: u1 = undefined; + addcarryxU64(&x320, &x321, x319, x303, x300); + const x322 = (@as(u64, x321) + x301); + var x323: u64 = undefined; + var x324: u1 = undefined; + addcarryxU64(&x323, &x324, 0x0, x287, x310); + var x325: u64 = undefined; + var x326: u1 = undefined; + addcarryxU64(&x325, &x326, x324, x289, x312); + var x327: u64 = undefined; + var x328: u1 = undefined; + addcarryxU64(&x327, &x328, x326, x291, x314); + var x329: u64 = undefined; + var x330: u1 = undefined; + addcarryxU64(&x329, &x330, x328, x293, x316); + var x331: u64 = undefined; + var x332: u1 = undefined; + addcarryxU64(&x331, &x332, x330, x295, x318); + var x333: u64 = undefined; + var x334: u1 = undefined; + addcarryxU64(&x333, &x334, x332, x297, x320); + var x335: u64 = undefined; + var x336: u1 = undefined; + addcarryxU64(&x335, &x336, x334, x299, x322); + var x337: u64 = undefined; + var x338: u64 = undefined; + mulxU64(&x337, &x338, x323, 0x6ed46089e88fdc45); + var x339: u64 = undefined; + var x340: u64 = undefined; + mulxU64(&x339, &x340, x337, 0xffffffffffffffff); + var x341: u64 = undefined; + var x342: u64 = undefined; + mulxU64(&x341, &x342, x337, 0xffffffffffffffff); + var x343: u64 = undefined; + var x344: u64 = undefined; + mulxU64(&x343, &x344, x337, 0xffffffffffffffff); + var x345: u64 = undefined; + var x346: u64 = undefined; + mulxU64(&x345, &x346, x337, 0xc7634d81f4372ddf); + var x347: u64 = undefined; + var x348: u64 = undefined; + mulxU64(&x347, &x348, x337, 0x581a0db248b0a77a); + var x349: u64 = undefined; + var x350: u64 = undefined; + mulxU64(&x349, &x350, x337, 0xecec196accc52973); + var x351: u64 = undefined; + var x352: u1 = undefined; + addcarryxU64(&x351, &x352, 0x0, x350, x347); + var x353: u64 = undefined; + var x354: u1 = undefined; + addcarryxU64(&x353, &x354, x352, x348, x345); + var x355: u64 = undefined; + var x356: u1 = undefined; + addcarryxU64(&x355, &x356, x354, x346, x343); + var x357: u64 = undefined; + var x358: u1 = undefined; + addcarryxU64(&x357, &x358, x356, x344, x341); + var x359: u64 = undefined; + var x360: u1 = undefined; + addcarryxU64(&x359, &x360, x358, x342, x339); + const x361 = (@as(u64, x360) + x340); + var x362: u64 = undefined; + var x363: u1 = undefined; + addcarryxU64(&x362, &x363, 0x0, x323, x349); + var x364: u64 = undefined; + var x365: u1 = undefined; + addcarryxU64(&x364, &x365, x363, x325, x351); + var x366: u64 = undefined; + var x367: u1 = undefined; + addcarryxU64(&x366, &x367, x365, x327, x353); + var x368: u64 = undefined; + var x369: u1 = undefined; + addcarryxU64(&x368, &x369, x367, x329, x355); + var x370: u64 = undefined; + var x371: u1 = undefined; + addcarryxU64(&x370, &x371, x369, x331, x357); + var x372: u64 = undefined; + var x373: u1 = undefined; + addcarryxU64(&x372, &x373, x371, x333, x359); + var x374: u64 = undefined; + var x375: u1 = undefined; + addcarryxU64(&x374, &x375, x373, x335, x361); + const x376 = (@as(u64, x375) + @as(u64, x336)); + var x377: u64 = undefined; + var x378: u64 = undefined; + mulxU64(&x377, &x378, x5, (arg2[5])); + var x379: u64 = undefined; + var x380: u64 = undefined; + mulxU64(&x379, &x380, x5, (arg2[4])); + var x381: u64 = undefined; + var x382: u64 = undefined; + mulxU64(&x381, &x382, x5, (arg2[3])); + var x383: u64 = undefined; + var x384: u64 = undefined; + mulxU64(&x383, &x384, x5, (arg2[2])); + var x385: u64 = undefined; + var x386: u64 = undefined; + mulxU64(&x385, &x386, x5, (arg2[1])); + var x387: u64 = undefined; + var x388: u64 = undefined; + mulxU64(&x387, &x388, x5, (arg2[0])); + var x389: u64 = undefined; + var x390: u1 = undefined; + addcarryxU64(&x389, &x390, 0x0, x388, x385); + var x391: u64 = undefined; + var x392: u1 = undefined; + addcarryxU64(&x391, &x392, x390, x386, x383); + var x393: u64 = undefined; + var x394: u1 = undefined; + addcarryxU64(&x393, &x394, x392, x384, x381); + var x395: u64 = undefined; + var x396: u1 = undefined; + addcarryxU64(&x395, &x396, x394, x382, x379); + var x397: u64 = undefined; + var x398: u1 = undefined; + addcarryxU64(&x397, &x398, x396, x380, x377); + const x399 = (@as(u64, x398) + x378); + var x400: u64 = undefined; + var x401: u1 = undefined; + addcarryxU64(&x400, &x401, 0x0, x364, x387); + var x402: u64 = undefined; + var x403: u1 = undefined; + addcarryxU64(&x402, &x403, x401, x366, x389); + var x404: u64 = undefined; + var x405: u1 = undefined; + addcarryxU64(&x404, &x405, x403, x368, x391); + var x406: u64 = undefined; + var x407: u1 = undefined; + addcarryxU64(&x406, &x407, x405, x370, x393); + var x408: u64 = undefined; + var x409: u1 = undefined; + addcarryxU64(&x408, &x409, x407, x372, x395); + var x410: u64 = undefined; + var x411: u1 = undefined; + addcarryxU64(&x410, &x411, x409, x374, x397); + var x412: u64 = undefined; + var x413: u1 = undefined; + addcarryxU64(&x412, &x413, x411, x376, x399); + var x414: u64 = undefined; + var x415: u64 = undefined; + mulxU64(&x414, &x415, x400, 0x6ed46089e88fdc45); + var x416: u64 = undefined; + var x417: u64 = undefined; + mulxU64(&x416, &x417, x414, 0xffffffffffffffff); + var x418: u64 = undefined; + var x419: u64 = undefined; + mulxU64(&x418, &x419, x414, 0xffffffffffffffff); + var x420: u64 = undefined; + var x421: u64 = undefined; + mulxU64(&x420, &x421, x414, 0xffffffffffffffff); + var x422: u64 = undefined; + var x423: u64 = undefined; + mulxU64(&x422, &x423, x414, 0xc7634d81f4372ddf); + var x424: u64 = undefined; + var x425: u64 = undefined; + mulxU64(&x424, &x425, x414, 0x581a0db248b0a77a); + var x426: u64 = undefined; + var x427: u64 = undefined; + mulxU64(&x426, &x427, x414, 0xecec196accc52973); + var x428: u64 = undefined; + var x429: u1 = undefined; + addcarryxU64(&x428, &x429, 0x0, x427, x424); + var x430: u64 = undefined; + var x431: u1 = undefined; + addcarryxU64(&x430, &x431, x429, x425, x422); + var x432: u64 = undefined; + var x433: u1 = undefined; + addcarryxU64(&x432, &x433, x431, x423, x420); + var x434: u64 = undefined; + var x435: u1 = undefined; + addcarryxU64(&x434, &x435, x433, x421, x418); + var x436: u64 = undefined; + var x437: u1 = undefined; + addcarryxU64(&x436, &x437, x435, x419, x416); + const x438 = (@as(u64, x437) + x417); + var x439: u64 = undefined; + var x440: u1 = undefined; + addcarryxU64(&x439, &x440, 0x0, x400, x426); + var x441: u64 = undefined; + var x442: u1 = undefined; + addcarryxU64(&x441, &x442, x440, x402, x428); + var x443: u64 = undefined; + var x444: u1 = undefined; + addcarryxU64(&x443, &x444, x442, x404, x430); + var x445: u64 = undefined; + var x446: u1 = undefined; + addcarryxU64(&x445, &x446, x444, x406, x432); + var x447: u64 = undefined; + var x448: u1 = undefined; + addcarryxU64(&x447, &x448, x446, x408, x434); + var x449: u64 = undefined; + var x450: u1 = undefined; + addcarryxU64(&x449, &x450, x448, x410, x436); + var x451: u64 = undefined; + var x452: u1 = undefined; + addcarryxU64(&x451, &x452, x450, x412, x438); + const x453 = (@as(u64, x452) + @as(u64, x413)); + var x454: u64 = undefined; + var x455: u1 = undefined; + subborrowxU64(&x454, &x455, 0x0, x441, 0xecec196accc52973); + var x456: u64 = undefined; + var x457: u1 = undefined; + subborrowxU64(&x456, &x457, x455, x443, 0x581a0db248b0a77a); + var x458: u64 = undefined; + var x459: u1 = undefined; + subborrowxU64(&x458, &x459, x457, x445, 0xc7634d81f4372ddf); + var x460: u64 = undefined; + var x461: u1 = undefined; + subborrowxU64(&x460, &x461, x459, x447, 0xffffffffffffffff); + var x462: u64 = undefined; + var x463: u1 = undefined; + subborrowxU64(&x462, &x463, x461, x449, 0xffffffffffffffff); + var x464: u64 = undefined; + var x465: u1 = undefined; + subborrowxU64(&x464, &x465, x463, x451, 0xffffffffffffffff); + var x466: u64 = undefined; + var x467: u1 = undefined; + subborrowxU64(&x466, &x467, x465, x453, 0x0); + var x468: u64 = undefined; + cmovznzU64(&x468, x467, x454, x441); + var x469: u64 = undefined; + cmovznzU64(&x469, x467, x456, x443); + var x470: u64 = undefined; + cmovznzU64(&x470, x467, x458, x445); + var x471: u64 = undefined; + cmovznzU64(&x471, x467, x460, x447); + var x472: u64 = undefined; + cmovznzU64(&x472, x467, x462, x449); + var x473: u64 = undefined; + cmovznzU64(&x473, x467, x464, x451); + out1[0] = x468; + out1[1] = x469; + out1[2] = x470; + out1[3] = x471; + out1[4] = x472; + out1[5] = x473; +} + +/// The function square squares a field element in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg1)) mod m +/// 0 ≤ eval out1 < m +/// +pub fn square(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[1]); + const x2 = (arg1[2]); + const x3 = (arg1[3]); + const x4 = (arg1[4]); + const x5 = (arg1[5]); + const x6 = (arg1[0]); + var x7: u64 = undefined; + var x8: u64 = undefined; + mulxU64(&x7, &x8, x6, (arg1[5])); + var x9: u64 = undefined; + var x10: u64 = undefined; + mulxU64(&x9, &x10, x6, (arg1[4])); + var x11: u64 = undefined; + var x12: u64 = undefined; + mulxU64(&x11, &x12, x6, (arg1[3])); + var x13: u64 = undefined; + var x14: u64 = undefined; + mulxU64(&x13, &x14, x6, (arg1[2])); + var x15: u64 = undefined; + var x16: u64 = undefined; + mulxU64(&x15, &x16, x6, (arg1[1])); + var x17: u64 = undefined; + var x18: u64 = undefined; + mulxU64(&x17, &x18, x6, (arg1[0])); + var x19: u64 = undefined; + var x20: u1 = undefined; + addcarryxU64(&x19, &x20, 0x0, x18, x15); + var x21: u64 = undefined; + var x22: u1 = undefined; + addcarryxU64(&x21, &x22, x20, x16, x13); + var x23: u64 = undefined; + var x24: u1 = undefined; + addcarryxU64(&x23, &x24, x22, x14, x11); + var x25: u64 = undefined; + var x26: u1 = undefined; + addcarryxU64(&x25, &x26, x24, x12, x9); + var x27: u64 = undefined; + var x28: u1 = undefined; + addcarryxU64(&x27, &x28, x26, x10, x7); + const x29 = (@as(u64, x28) + x8); + var x30: u64 = undefined; + var x31: u64 = undefined; + mulxU64(&x30, &x31, x17, 0x6ed46089e88fdc45); + var x32: u64 = undefined; + var x33: u64 = undefined; + mulxU64(&x32, &x33, x30, 0xffffffffffffffff); + var x34: u64 = undefined; + var x35: u64 = undefined; + mulxU64(&x34, &x35, x30, 0xffffffffffffffff); + var x36: u64 = undefined; + var x37: u64 = undefined; + mulxU64(&x36, &x37, x30, 0xffffffffffffffff); + var x38: u64 = undefined; + var x39: u64 = undefined; + mulxU64(&x38, &x39, x30, 0xc7634d81f4372ddf); + var x40: u64 = undefined; + var x41: u64 = undefined; + mulxU64(&x40, &x41, x30, 0x581a0db248b0a77a); + var x42: u64 = undefined; + var x43: u64 = undefined; + mulxU64(&x42, &x43, x30, 0xecec196accc52973); + var x44: u64 = undefined; + var x45: u1 = undefined; + addcarryxU64(&x44, &x45, 0x0, x43, x40); + var x46: u64 = undefined; + var x47: u1 = undefined; + addcarryxU64(&x46, &x47, x45, x41, x38); + var x48: u64 = undefined; + var x49: u1 = undefined; + addcarryxU64(&x48, &x49, x47, x39, x36); + var x50: u64 = undefined; + var x51: u1 = undefined; + addcarryxU64(&x50, &x51, x49, x37, x34); + var x52: u64 = undefined; + var x53: u1 = undefined; + addcarryxU64(&x52, &x53, x51, x35, x32); + const x54 = (@as(u64, x53) + x33); + var x55: u64 = undefined; + var x56: u1 = undefined; + addcarryxU64(&x55, &x56, 0x0, x17, x42); + var x57: u64 = undefined; + var x58: u1 = undefined; + addcarryxU64(&x57, &x58, x56, x19, x44); + var x59: u64 = undefined; + var x60: u1 = undefined; + addcarryxU64(&x59, &x60, x58, x21, x46); + var x61: u64 = undefined; + var x62: u1 = undefined; + addcarryxU64(&x61, &x62, x60, x23, x48); + var x63: u64 = undefined; + var x64: u1 = undefined; + addcarryxU64(&x63, &x64, x62, x25, x50); + var x65: u64 = undefined; + var x66: u1 = undefined; + addcarryxU64(&x65, &x66, x64, x27, x52); + var x67: u64 = undefined; + var x68: u1 = undefined; + addcarryxU64(&x67, &x68, x66, x29, x54); + var x69: u64 = undefined; + var x70: u64 = undefined; + mulxU64(&x69, &x70, x1, (arg1[5])); + var x71: u64 = undefined; + var x72: u64 = undefined; + mulxU64(&x71, &x72, x1, (arg1[4])); + var x73: u64 = undefined; + var x74: u64 = undefined; + mulxU64(&x73, &x74, x1, (arg1[3])); + var x75: u64 = undefined; + var x76: u64 = undefined; + mulxU64(&x75, &x76, x1, (arg1[2])); + var x77: u64 = undefined; + var x78: u64 = undefined; + mulxU64(&x77, &x78, x1, (arg1[1])); + var x79: u64 = undefined; + var x80: u64 = undefined; + mulxU64(&x79, &x80, x1, (arg1[0])); + var x81: u64 = undefined; + var x82: u1 = undefined; + addcarryxU64(&x81, &x82, 0x0, x80, x77); + var x83: u64 = undefined; + var x84: u1 = undefined; + addcarryxU64(&x83, &x84, x82, x78, x75); + var x85: u64 = undefined; + var x86: u1 = undefined; + addcarryxU64(&x85, &x86, x84, x76, x73); + var x87: u64 = undefined; + var x88: u1 = undefined; + addcarryxU64(&x87, &x88, x86, x74, x71); + var x89: u64 = undefined; + var x90: u1 = undefined; + addcarryxU64(&x89, &x90, x88, x72, x69); + const x91 = (@as(u64, x90) + x70); + var x92: u64 = undefined; + var x93: u1 = undefined; + addcarryxU64(&x92, &x93, 0x0, x57, x79); + var x94: u64 = undefined; + var x95: u1 = undefined; + addcarryxU64(&x94, &x95, x93, x59, x81); + var x96: u64 = undefined; + var x97: u1 = undefined; + addcarryxU64(&x96, &x97, x95, x61, x83); + var x98: u64 = undefined; + var x99: u1 = undefined; + addcarryxU64(&x98, &x99, x97, x63, x85); + var x100: u64 = undefined; + var x101: u1 = undefined; + addcarryxU64(&x100, &x101, x99, x65, x87); + var x102: u64 = undefined; + var x103: u1 = undefined; + addcarryxU64(&x102, &x103, x101, x67, x89); + var x104: u64 = undefined; + var x105: u1 = undefined; + addcarryxU64(&x104, &x105, x103, @as(u64, x68), x91); + var x106: u64 = undefined; + var x107: u64 = undefined; + mulxU64(&x106, &x107, x92, 0x6ed46089e88fdc45); + var x108: u64 = undefined; + var x109: u64 = undefined; + mulxU64(&x108, &x109, x106, 0xffffffffffffffff); + var x110: u64 = undefined; + var x111: u64 = undefined; + mulxU64(&x110, &x111, x106, 0xffffffffffffffff); + var x112: u64 = undefined; + var x113: u64 = undefined; + mulxU64(&x112, &x113, x106, 0xffffffffffffffff); + var x114: u64 = undefined; + var x115: u64 = undefined; + mulxU64(&x114, &x115, x106, 0xc7634d81f4372ddf); + var x116: u64 = undefined; + var x117: u64 = undefined; + mulxU64(&x116, &x117, x106, 0x581a0db248b0a77a); + var x118: u64 = undefined; + var x119: u64 = undefined; + mulxU64(&x118, &x119, x106, 0xecec196accc52973); + var x120: u64 = undefined; + var x121: u1 = undefined; + addcarryxU64(&x120, &x121, 0x0, x119, x116); + var x122: u64 = undefined; + var x123: u1 = undefined; + addcarryxU64(&x122, &x123, x121, x117, x114); + var x124: u64 = undefined; + var x125: u1 = undefined; + addcarryxU64(&x124, &x125, x123, x115, x112); + var x126: u64 = undefined; + var x127: u1 = undefined; + addcarryxU64(&x126, &x127, x125, x113, x110); + var x128: u64 = undefined; + var x129: u1 = undefined; + addcarryxU64(&x128, &x129, x127, x111, x108); + const x130 = (@as(u64, x129) + x109); + var x131: u64 = undefined; + var x132: u1 = undefined; + addcarryxU64(&x131, &x132, 0x0, x92, x118); + var x133: u64 = undefined; + var x134: u1 = undefined; + addcarryxU64(&x133, &x134, x132, x94, x120); + var x135: u64 = undefined; + var x136: u1 = undefined; + addcarryxU64(&x135, &x136, x134, x96, x122); + var x137: u64 = undefined; + var x138: u1 = undefined; + addcarryxU64(&x137, &x138, x136, x98, x124); + var x139: u64 = undefined; + var x140: u1 = undefined; + addcarryxU64(&x139, &x140, x138, x100, x126); + var x141: u64 = undefined; + var x142: u1 = undefined; + addcarryxU64(&x141, &x142, x140, x102, x128); + var x143: u64 = undefined; + var x144: u1 = undefined; + addcarryxU64(&x143, &x144, x142, x104, x130); + const x145 = (@as(u64, x144) + @as(u64, x105)); + var x146: u64 = undefined; + var x147: u64 = undefined; + mulxU64(&x146, &x147, x2, (arg1[5])); + var x148: u64 = undefined; + var x149: u64 = undefined; + mulxU64(&x148, &x149, x2, (arg1[4])); + var x150: u64 = undefined; + var x151: u64 = undefined; + mulxU64(&x150, &x151, x2, (arg1[3])); + var x152: u64 = undefined; + var x153: u64 = undefined; + mulxU64(&x152, &x153, x2, (arg1[2])); + var x154: u64 = undefined; + var x155: u64 = undefined; + mulxU64(&x154, &x155, x2, (arg1[1])); + var x156: u64 = undefined; + var x157: u64 = undefined; + mulxU64(&x156, &x157, x2, (arg1[0])); + var x158: u64 = undefined; + var x159: u1 = undefined; + addcarryxU64(&x158, &x159, 0x0, x157, x154); + var x160: u64 = undefined; + var x161: u1 = undefined; + addcarryxU64(&x160, &x161, x159, x155, x152); + var x162: u64 = undefined; + var x163: u1 = undefined; + addcarryxU64(&x162, &x163, x161, x153, x150); + var x164: u64 = undefined; + var x165: u1 = undefined; + addcarryxU64(&x164, &x165, x163, x151, x148); + var x166: u64 = undefined; + var x167: u1 = undefined; + addcarryxU64(&x166, &x167, x165, x149, x146); + const x168 = (@as(u64, x167) + x147); + var x169: u64 = undefined; + var x170: u1 = undefined; + addcarryxU64(&x169, &x170, 0x0, x133, x156); + var x171: u64 = undefined; + var x172: u1 = undefined; + addcarryxU64(&x171, &x172, x170, x135, x158); + var x173: u64 = undefined; + var x174: u1 = undefined; + addcarryxU64(&x173, &x174, x172, x137, x160); + var x175: u64 = undefined; + var x176: u1 = undefined; + addcarryxU64(&x175, &x176, x174, x139, x162); + var x177: u64 = undefined; + var x178: u1 = undefined; + addcarryxU64(&x177, &x178, x176, x141, x164); + var x179: u64 = undefined; + var x180: u1 = undefined; + addcarryxU64(&x179, &x180, x178, x143, x166); + var x181: u64 = undefined; + var x182: u1 = undefined; + addcarryxU64(&x181, &x182, x180, x145, x168); + var x183: u64 = undefined; + var x184: u64 = undefined; + mulxU64(&x183, &x184, x169, 0x6ed46089e88fdc45); + var x185: u64 = undefined; + var x186: u64 = undefined; + mulxU64(&x185, &x186, x183, 0xffffffffffffffff); + var x187: u64 = undefined; + var x188: u64 = undefined; + mulxU64(&x187, &x188, x183, 0xffffffffffffffff); + var x189: u64 = undefined; + var x190: u64 = undefined; + mulxU64(&x189, &x190, x183, 0xffffffffffffffff); + var x191: u64 = undefined; + var x192: u64 = undefined; + mulxU64(&x191, &x192, x183, 0xc7634d81f4372ddf); + var x193: u64 = undefined; + var x194: u64 = undefined; + mulxU64(&x193, &x194, x183, 0x581a0db248b0a77a); + var x195: u64 = undefined; + var x196: u64 = undefined; + mulxU64(&x195, &x196, x183, 0xecec196accc52973); + var x197: u64 = undefined; + var x198: u1 = undefined; + addcarryxU64(&x197, &x198, 0x0, x196, x193); + var x199: u64 = undefined; + var x200: u1 = undefined; + addcarryxU64(&x199, &x200, x198, x194, x191); + var x201: u64 = undefined; + var x202: u1 = undefined; + addcarryxU64(&x201, &x202, x200, x192, x189); + var x203: u64 = undefined; + var x204: u1 = undefined; + addcarryxU64(&x203, &x204, x202, x190, x187); + var x205: u64 = undefined; + var x206: u1 = undefined; + addcarryxU64(&x205, &x206, x204, x188, x185); + const x207 = (@as(u64, x206) + x186); + var x208: u64 = undefined; + var x209: u1 = undefined; + addcarryxU64(&x208, &x209, 0x0, x169, x195); + var x210: u64 = undefined; + var x211: u1 = undefined; + addcarryxU64(&x210, &x211, x209, x171, x197); + var x212: u64 = undefined; + var x213: u1 = undefined; + addcarryxU64(&x212, &x213, x211, x173, x199); + var x214: u64 = undefined; + var x215: u1 = undefined; + addcarryxU64(&x214, &x215, x213, x175, x201); + var x216: u64 = undefined; + var x217: u1 = undefined; + addcarryxU64(&x216, &x217, x215, x177, x203); + var x218: u64 = undefined; + var x219: u1 = undefined; + addcarryxU64(&x218, &x219, x217, x179, x205); + var x220: u64 = undefined; + var x221: u1 = undefined; + addcarryxU64(&x220, &x221, x219, x181, x207); + const x222 = (@as(u64, x221) + @as(u64, x182)); + var x223: u64 = undefined; + var x224: u64 = undefined; + mulxU64(&x223, &x224, x3, (arg1[5])); + var x225: u64 = undefined; + var x226: u64 = undefined; + mulxU64(&x225, &x226, x3, (arg1[4])); + var x227: u64 = undefined; + var x228: u64 = undefined; + mulxU64(&x227, &x228, x3, (arg1[3])); + var x229: u64 = undefined; + var x230: u64 = undefined; + mulxU64(&x229, &x230, x3, (arg1[2])); + var x231: u64 = undefined; + var x232: u64 = undefined; + mulxU64(&x231, &x232, x3, (arg1[1])); + var x233: u64 = undefined; + var x234: u64 = undefined; + mulxU64(&x233, &x234, x3, (arg1[0])); + var x235: u64 = undefined; + var x236: u1 = undefined; + addcarryxU64(&x235, &x236, 0x0, x234, x231); + var x237: u64 = undefined; + var x238: u1 = undefined; + addcarryxU64(&x237, &x238, x236, x232, x229); + var x239: u64 = undefined; + var x240: u1 = undefined; + addcarryxU64(&x239, &x240, x238, x230, x227); + var x241: u64 = undefined; + var x242: u1 = undefined; + addcarryxU64(&x241, &x242, x240, x228, x225); + var x243: u64 = undefined; + var x244: u1 = undefined; + addcarryxU64(&x243, &x244, x242, x226, x223); + const x245 = (@as(u64, x244) + x224); + var x246: u64 = undefined; + var x247: u1 = undefined; + addcarryxU64(&x246, &x247, 0x0, x210, x233); + var x248: u64 = undefined; + var x249: u1 = undefined; + addcarryxU64(&x248, &x249, x247, x212, x235); + var x250: u64 = undefined; + var x251: u1 = undefined; + addcarryxU64(&x250, &x251, x249, x214, x237); + var x252: u64 = undefined; + var x253: u1 = undefined; + addcarryxU64(&x252, &x253, x251, x216, x239); + var x254: u64 = undefined; + var x255: u1 = undefined; + addcarryxU64(&x254, &x255, x253, x218, x241); + var x256: u64 = undefined; + var x257: u1 = undefined; + addcarryxU64(&x256, &x257, x255, x220, x243); + var x258: u64 = undefined; + var x259: u1 = undefined; + addcarryxU64(&x258, &x259, x257, x222, x245); + var x260: u64 = undefined; + var x261: u64 = undefined; + mulxU64(&x260, &x261, x246, 0x6ed46089e88fdc45); + var x262: u64 = undefined; + var x263: u64 = undefined; + mulxU64(&x262, &x263, x260, 0xffffffffffffffff); + var x264: u64 = undefined; + var x265: u64 = undefined; + mulxU64(&x264, &x265, x260, 0xffffffffffffffff); + var x266: u64 = undefined; + var x267: u64 = undefined; + mulxU64(&x266, &x267, x260, 0xffffffffffffffff); + var x268: u64 = undefined; + var x269: u64 = undefined; + mulxU64(&x268, &x269, x260, 0xc7634d81f4372ddf); + var x270: u64 = undefined; + var x271: u64 = undefined; + mulxU64(&x270, &x271, x260, 0x581a0db248b0a77a); + var x272: u64 = undefined; + var x273: u64 = undefined; + mulxU64(&x272, &x273, x260, 0xecec196accc52973); + var x274: u64 = undefined; + var x275: u1 = undefined; + addcarryxU64(&x274, &x275, 0x0, x273, x270); + var x276: u64 = undefined; + var x277: u1 = undefined; + addcarryxU64(&x276, &x277, x275, x271, x268); + var x278: u64 = undefined; + var x279: u1 = undefined; + addcarryxU64(&x278, &x279, x277, x269, x266); + var x280: u64 = undefined; + var x281: u1 = undefined; + addcarryxU64(&x280, &x281, x279, x267, x264); + var x282: u64 = undefined; + var x283: u1 = undefined; + addcarryxU64(&x282, &x283, x281, x265, x262); + const x284 = (@as(u64, x283) + x263); + var x285: u64 = undefined; + var x286: u1 = undefined; + addcarryxU64(&x285, &x286, 0x0, x246, x272); + var x287: u64 = undefined; + var x288: u1 = undefined; + addcarryxU64(&x287, &x288, x286, x248, x274); + var x289: u64 = undefined; + var x290: u1 = undefined; + addcarryxU64(&x289, &x290, x288, x250, x276); + var x291: u64 = undefined; + var x292: u1 = undefined; + addcarryxU64(&x291, &x292, x290, x252, x278); + var x293: u64 = undefined; + var x294: u1 = undefined; + addcarryxU64(&x293, &x294, x292, x254, x280); + var x295: u64 = undefined; + var x296: u1 = undefined; + addcarryxU64(&x295, &x296, x294, x256, x282); + var x297: u64 = undefined; + var x298: u1 = undefined; + addcarryxU64(&x297, &x298, x296, x258, x284); + const x299 = (@as(u64, x298) + @as(u64, x259)); + var x300: u64 = undefined; + var x301: u64 = undefined; + mulxU64(&x300, &x301, x4, (arg1[5])); + var x302: u64 = undefined; + var x303: u64 = undefined; + mulxU64(&x302, &x303, x4, (arg1[4])); + var x304: u64 = undefined; + var x305: u64 = undefined; + mulxU64(&x304, &x305, x4, (arg1[3])); + var x306: u64 = undefined; + var x307: u64 = undefined; + mulxU64(&x306, &x307, x4, (arg1[2])); + var x308: u64 = undefined; + var x309: u64 = undefined; + mulxU64(&x308, &x309, x4, (arg1[1])); + var x310: u64 = undefined; + var x311: u64 = undefined; + mulxU64(&x310, &x311, x4, (arg1[0])); + var x312: u64 = undefined; + var x313: u1 = undefined; + addcarryxU64(&x312, &x313, 0x0, x311, x308); + var x314: u64 = undefined; + var x315: u1 = undefined; + addcarryxU64(&x314, &x315, x313, x309, x306); + var x316: u64 = undefined; + var x317: u1 = undefined; + addcarryxU64(&x316, &x317, x315, x307, x304); + var x318: u64 = undefined; + var x319: u1 = undefined; + addcarryxU64(&x318, &x319, x317, x305, x302); + var x320: u64 = undefined; + var x321: u1 = undefined; + addcarryxU64(&x320, &x321, x319, x303, x300); + const x322 = (@as(u64, x321) + x301); + var x323: u64 = undefined; + var x324: u1 = undefined; + addcarryxU64(&x323, &x324, 0x0, x287, x310); + var x325: u64 = undefined; + var x326: u1 = undefined; + addcarryxU64(&x325, &x326, x324, x289, x312); + var x327: u64 = undefined; + var x328: u1 = undefined; + addcarryxU64(&x327, &x328, x326, x291, x314); + var x329: u64 = undefined; + var x330: u1 = undefined; + addcarryxU64(&x329, &x330, x328, x293, x316); + var x331: u64 = undefined; + var x332: u1 = undefined; + addcarryxU64(&x331, &x332, x330, x295, x318); + var x333: u64 = undefined; + var x334: u1 = undefined; + addcarryxU64(&x333, &x334, x332, x297, x320); + var x335: u64 = undefined; + var x336: u1 = undefined; + addcarryxU64(&x335, &x336, x334, x299, x322); + var x337: u64 = undefined; + var x338: u64 = undefined; + mulxU64(&x337, &x338, x323, 0x6ed46089e88fdc45); + var x339: u64 = undefined; + var x340: u64 = undefined; + mulxU64(&x339, &x340, x337, 0xffffffffffffffff); + var x341: u64 = undefined; + var x342: u64 = undefined; + mulxU64(&x341, &x342, x337, 0xffffffffffffffff); + var x343: u64 = undefined; + var x344: u64 = undefined; + mulxU64(&x343, &x344, x337, 0xffffffffffffffff); + var x345: u64 = undefined; + var x346: u64 = undefined; + mulxU64(&x345, &x346, x337, 0xc7634d81f4372ddf); + var x347: u64 = undefined; + var x348: u64 = undefined; + mulxU64(&x347, &x348, x337, 0x581a0db248b0a77a); + var x349: u64 = undefined; + var x350: u64 = undefined; + mulxU64(&x349, &x350, x337, 0xecec196accc52973); + var x351: u64 = undefined; + var x352: u1 = undefined; + addcarryxU64(&x351, &x352, 0x0, x350, x347); + var x353: u64 = undefined; + var x354: u1 = undefined; + addcarryxU64(&x353, &x354, x352, x348, x345); + var x355: u64 = undefined; + var x356: u1 = undefined; + addcarryxU64(&x355, &x356, x354, x346, x343); + var x357: u64 = undefined; + var x358: u1 = undefined; + addcarryxU64(&x357, &x358, x356, x344, x341); + var x359: u64 = undefined; + var x360: u1 = undefined; + addcarryxU64(&x359, &x360, x358, x342, x339); + const x361 = (@as(u64, x360) + x340); + var x362: u64 = undefined; + var x363: u1 = undefined; + addcarryxU64(&x362, &x363, 0x0, x323, x349); + var x364: u64 = undefined; + var x365: u1 = undefined; + addcarryxU64(&x364, &x365, x363, x325, x351); + var x366: u64 = undefined; + var x367: u1 = undefined; + addcarryxU64(&x366, &x367, x365, x327, x353); + var x368: u64 = undefined; + var x369: u1 = undefined; + addcarryxU64(&x368, &x369, x367, x329, x355); + var x370: u64 = undefined; + var x371: u1 = undefined; + addcarryxU64(&x370, &x371, x369, x331, x357); + var x372: u64 = undefined; + var x373: u1 = undefined; + addcarryxU64(&x372, &x373, x371, x333, x359); + var x374: u64 = undefined; + var x375: u1 = undefined; + addcarryxU64(&x374, &x375, x373, x335, x361); + const x376 = (@as(u64, x375) + @as(u64, x336)); + var x377: u64 = undefined; + var x378: u64 = undefined; + mulxU64(&x377, &x378, x5, (arg1[5])); + var x379: u64 = undefined; + var x380: u64 = undefined; + mulxU64(&x379, &x380, x5, (arg1[4])); + var x381: u64 = undefined; + var x382: u64 = undefined; + mulxU64(&x381, &x382, x5, (arg1[3])); + var x383: u64 = undefined; + var x384: u64 = undefined; + mulxU64(&x383, &x384, x5, (arg1[2])); + var x385: u64 = undefined; + var x386: u64 = undefined; + mulxU64(&x385, &x386, x5, (arg1[1])); + var x387: u64 = undefined; + var x388: u64 = undefined; + mulxU64(&x387, &x388, x5, (arg1[0])); + var x389: u64 = undefined; + var x390: u1 = undefined; + addcarryxU64(&x389, &x390, 0x0, x388, x385); + var x391: u64 = undefined; + var x392: u1 = undefined; + addcarryxU64(&x391, &x392, x390, x386, x383); + var x393: u64 = undefined; + var x394: u1 = undefined; + addcarryxU64(&x393, &x394, x392, x384, x381); + var x395: u64 = undefined; + var x396: u1 = undefined; + addcarryxU64(&x395, &x396, x394, x382, x379); + var x397: u64 = undefined; + var x398: u1 = undefined; + addcarryxU64(&x397, &x398, x396, x380, x377); + const x399 = (@as(u64, x398) + x378); + var x400: u64 = undefined; + var x401: u1 = undefined; + addcarryxU64(&x400, &x401, 0x0, x364, x387); + var x402: u64 = undefined; + var x403: u1 = undefined; + addcarryxU64(&x402, &x403, x401, x366, x389); + var x404: u64 = undefined; + var x405: u1 = undefined; + addcarryxU64(&x404, &x405, x403, x368, x391); + var x406: u64 = undefined; + var x407: u1 = undefined; + addcarryxU64(&x406, &x407, x405, x370, x393); + var x408: u64 = undefined; + var x409: u1 = undefined; + addcarryxU64(&x408, &x409, x407, x372, x395); + var x410: u64 = undefined; + var x411: u1 = undefined; + addcarryxU64(&x410, &x411, x409, x374, x397); + var x412: u64 = undefined; + var x413: u1 = undefined; + addcarryxU64(&x412, &x413, x411, x376, x399); + var x414: u64 = undefined; + var x415: u64 = undefined; + mulxU64(&x414, &x415, x400, 0x6ed46089e88fdc45); + var x416: u64 = undefined; + var x417: u64 = undefined; + mulxU64(&x416, &x417, x414, 0xffffffffffffffff); + var x418: u64 = undefined; + var x419: u64 = undefined; + mulxU64(&x418, &x419, x414, 0xffffffffffffffff); + var x420: u64 = undefined; + var x421: u64 = undefined; + mulxU64(&x420, &x421, x414, 0xffffffffffffffff); + var x422: u64 = undefined; + var x423: u64 = undefined; + mulxU64(&x422, &x423, x414, 0xc7634d81f4372ddf); + var x424: u64 = undefined; + var x425: u64 = undefined; + mulxU64(&x424, &x425, x414, 0x581a0db248b0a77a); + var x426: u64 = undefined; + var x427: u64 = undefined; + mulxU64(&x426, &x427, x414, 0xecec196accc52973); + var x428: u64 = undefined; + var x429: u1 = undefined; + addcarryxU64(&x428, &x429, 0x0, x427, x424); + var x430: u64 = undefined; + var x431: u1 = undefined; + addcarryxU64(&x430, &x431, x429, x425, x422); + var x432: u64 = undefined; + var x433: u1 = undefined; + addcarryxU64(&x432, &x433, x431, x423, x420); + var x434: u64 = undefined; + var x435: u1 = undefined; + addcarryxU64(&x434, &x435, x433, x421, x418); + var x436: u64 = undefined; + var x437: u1 = undefined; + addcarryxU64(&x436, &x437, x435, x419, x416); + const x438 = (@as(u64, x437) + x417); + var x439: u64 = undefined; + var x440: u1 = undefined; + addcarryxU64(&x439, &x440, 0x0, x400, x426); + var x441: u64 = undefined; + var x442: u1 = undefined; + addcarryxU64(&x441, &x442, x440, x402, x428); + var x443: u64 = undefined; + var x444: u1 = undefined; + addcarryxU64(&x443, &x444, x442, x404, x430); + var x445: u64 = undefined; + var x446: u1 = undefined; + addcarryxU64(&x445, &x446, x444, x406, x432); + var x447: u64 = undefined; + var x448: u1 = undefined; + addcarryxU64(&x447, &x448, x446, x408, x434); + var x449: u64 = undefined; + var x450: u1 = undefined; + addcarryxU64(&x449, &x450, x448, x410, x436); + var x451: u64 = undefined; + var x452: u1 = undefined; + addcarryxU64(&x451, &x452, x450, x412, x438); + const x453 = (@as(u64, x452) + @as(u64, x413)); + var x454: u64 = undefined; + var x455: u1 = undefined; + subborrowxU64(&x454, &x455, 0x0, x441, 0xecec196accc52973); + var x456: u64 = undefined; + var x457: u1 = undefined; + subborrowxU64(&x456, &x457, x455, x443, 0x581a0db248b0a77a); + var x458: u64 = undefined; + var x459: u1 = undefined; + subborrowxU64(&x458, &x459, x457, x445, 0xc7634d81f4372ddf); + var x460: u64 = undefined; + var x461: u1 = undefined; + subborrowxU64(&x460, &x461, x459, x447, 0xffffffffffffffff); + var x462: u64 = undefined; + var x463: u1 = undefined; + subborrowxU64(&x462, &x463, x461, x449, 0xffffffffffffffff); + var x464: u64 = undefined; + var x465: u1 = undefined; + subborrowxU64(&x464, &x465, x463, x451, 0xffffffffffffffff); + var x466: u64 = undefined; + var x467: u1 = undefined; + subborrowxU64(&x466, &x467, x465, x453, 0x0); + var x468: u64 = undefined; + cmovznzU64(&x468, x467, x454, x441); + var x469: u64 = undefined; + cmovznzU64(&x469, x467, x456, x443); + var x470: u64 = undefined; + cmovznzU64(&x470, x467, x458, x445); + var x471: u64 = undefined; + cmovznzU64(&x471, x467, x460, x447); + var x472: u64 = undefined; + cmovznzU64(&x472, x467, x462, x449); + var x473: u64 = undefined; + cmovznzU64(&x473, x467, x464, x451); + out1[0] = x468; + out1[1] = x469; + out1[2] = x470; + out1[3] = x471; + out1[4] = x472; + out1[5] = x473; +} + +/// The function add adds two field elements in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// 0 ≤ eval arg2 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m +/// 0 ≤ eval out1 < m +/// +pub fn add(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement, arg2: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + addcarryxU64(&x1, &x2, 0x0, (arg1[0]), (arg2[0])); + var x3: u64 = undefined; + var x4: u1 = undefined; + addcarryxU64(&x3, &x4, x2, (arg1[1]), (arg2[1])); + var x5: u64 = undefined; + var x6: u1 = undefined; + addcarryxU64(&x5, &x6, x4, (arg1[2]), (arg2[2])); + var x7: u64 = undefined; + var x8: u1 = undefined; + addcarryxU64(&x7, &x8, x6, (arg1[3]), (arg2[3])); + var x9: u64 = undefined; + var x10: u1 = undefined; + addcarryxU64(&x9, &x10, x8, (arg1[4]), (arg2[4])); + var x11: u64 = undefined; + var x12: u1 = undefined; + addcarryxU64(&x11, &x12, x10, (arg1[5]), (arg2[5])); + var x13: u64 = undefined; + var x14: u1 = undefined; + subborrowxU64(&x13, &x14, 0x0, x1, 0xecec196accc52973); + var x15: u64 = undefined; + var x16: u1 = undefined; + subborrowxU64(&x15, &x16, x14, x3, 0x581a0db248b0a77a); + var x17: u64 = undefined; + var x18: u1 = undefined; + subborrowxU64(&x17, &x18, x16, x5, 0xc7634d81f4372ddf); + var x19: u64 = undefined; + var x20: u1 = undefined; + subborrowxU64(&x19, &x20, x18, x7, 0xffffffffffffffff); + var x21: u64 = undefined; + var x22: u1 = undefined; + subborrowxU64(&x21, &x22, x20, x9, 0xffffffffffffffff); + var x23: u64 = undefined; + var x24: u1 = undefined; + subborrowxU64(&x23, &x24, x22, x11, 0xffffffffffffffff); + var x25: u64 = undefined; + var x26: u1 = undefined; + subborrowxU64(&x25, &x26, x24, @as(u64, x12), 0x0); + var x27: u64 = undefined; + cmovznzU64(&x27, x26, x13, x1); + var x28: u64 = undefined; + cmovznzU64(&x28, x26, x15, x3); + var x29: u64 = undefined; + cmovznzU64(&x29, x26, x17, x5); + var x30: u64 = undefined; + cmovznzU64(&x30, x26, x19, x7); + var x31: u64 = undefined; + cmovznzU64(&x31, x26, x21, x9); + var x32: u64 = undefined; + cmovznzU64(&x32, x26, x23, x11); + out1[0] = x27; + out1[1] = x28; + out1[2] = x29; + out1[3] = x30; + out1[4] = x31; + out1[5] = x32; +} + +/// The function sub subtracts two field elements in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// 0 ≤ eval arg2 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m +/// 0 ≤ eval out1 < m +/// +pub fn sub(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement, arg2: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + subborrowxU64(&x1, &x2, 0x0, (arg1[0]), (arg2[0])); + var x3: u64 = undefined; + var x4: u1 = undefined; + subborrowxU64(&x3, &x4, x2, (arg1[1]), (arg2[1])); + var x5: u64 = undefined; + var x6: u1 = undefined; + subborrowxU64(&x5, &x6, x4, (arg1[2]), (arg2[2])); + var x7: u64 = undefined; + var x8: u1 = undefined; + subborrowxU64(&x7, &x8, x6, (arg1[3]), (arg2[3])); + var x9: u64 = undefined; + var x10: u1 = undefined; + subborrowxU64(&x9, &x10, x8, (arg1[4]), (arg2[4])); + var x11: u64 = undefined; + var x12: u1 = undefined; + subborrowxU64(&x11, &x12, x10, (arg1[5]), (arg2[5])); + var x13: u64 = undefined; + cmovznzU64(&x13, x12, 0x0, 0xffffffffffffffff); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, 0x0, x1, (x13 & 0xecec196accc52973)); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, x3, (x13 & 0x581a0db248b0a77a)); + var x18: u64 = undefined; + var x19: u1 = undefined; + addcarryxU64(&x18, &x19, x17, x5, (x13 & 0xc7634d81f4372ddf)); + var x20: u64 = undefined; + var x21: u1 = undefined; + addcarryxU64(&x20, &x21, x19, x7, x13); + var x22: u64 = undefined; + var x23: u1 = undefined; + addcarryxU64(&x22, &x23, x21, x9, x13); + var x24: u64 = undefined; + var x25: u1 = undefined; + addcarryxU64(&x24, &x25, x23, x11, x13); + out1[0] = x14; + out1[1] = x16; + out1[2] = x18; + out1[3] = x20; + out1[4] = x22; + out1[5] = x24; +} + +/// The function opp negates a field element in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = -eval (from_montgomery arg1) mod m +/// 0 ≤ eval out1 < m +/// +pub fn opp(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + subborrowxU64(&x1, &x2, 0x0, 0x0, (arg1[0])); + var x3: u64 = undefined; + var x4: u1 = undefined; + subborrowxU64(&x3, &x4, x2, 0x0, (arg1[1])); + var x5: u64 = undefined; + var x6: u1 = undefined; + subborrowxU64(&x5, &x6, x4, 0x0, (arg1[2])); + var x7: u64 = undefined; + var x8: u1 = undefined; + subborrowxU64(&x7, &x8, x6, 0x0, (arg1[3])); + var x9: u64 = undefined; + var x10: u1 = undefined; + subborrowxU64(&x9, &x10, x8, 0x0, (arg1[4])); + var x11: u64 = undefined; + var x12: u1 = undefined; + subborrowxU64(&x11, &x12, x10, 0x0, (arg1[5])); + var x13: u64 = undefined; + cmovznzU64(&x13, x12, 0x0, 0xffffffffffffffff); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, 0x0, x1, (x13 & 0xecec196accc52973)); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, x3, (x13 & 0x581a0db248b0a77a)); + var x18: u64 = undefined; + var x19: u1 = undefined; + addcarryxU64(&x18, &x19, x17, x5, (x13 & 0xc7634d81f4372ddf)); + var x20: u64 = undefined; + var x21: u1 = undefined; + addcarryxU64(&x20, &x21, x19, x7, x13); + var x22: u64 = undefined; + var x23: u1 = undefined; + addcarryxU64(&x22, &x23, x21, x9, x13); + var x24: u64 = undefined; + var x25: u1 = undefined; + addcarryxU64(&x24, &x25, x23, x11, x13); + out1[0] = x14; + out1[1] = x16; + out1[2] = x18; + out1[3] = x20; + out1[4] = x22; + out1[5] = x24; +} + +/// The function fromMontgomery translates a field element out of the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^6) mod m +/// 0 ≤ eval out1 < m +/// +pub fn fromMontgomery(out1: *NonMontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[0]); + var x2: u64 = undefined; + var x3: u64 = undefined; + mulxU64(&x2, &x3, x1, 0x6ed46089e88fdc45); + var x4: u64 = undefined; + var x5: u64 = undefined; + mulxU64(&x4, &x5, x2, 0xffffffffffffffff); + var x6: u64 = undefined; + var x7: u64 = undefined; + mulxU64(&x6, &x7, x2, 0xffffffffffffffff); + var x8: u64 = undefined; + var x9: u64 = undefined; + mulxU64(&x8, &x9, x2, 0xffffffffffffffff); + var x10: u64 = undefined; + var x11: u64 = undefined; + mulxU64(&x10, &x11, x2, 0xc7634d81f4372ddf); + var x12: u64 = undefined; + var x13: u64 = undefined; + mulxU64(&x12, &x13, x2, 0x581a0db248b0a77a); + var x14: u64 = undefined; + var x15: u64 = undefined; + mulxU64(&x14, &x15, x2, 0xecec196accc52973); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, 0x0, x15, x12); + var x18: u64 = undefined; + var x19: u1 = undefined; + addcarryxU64(&x18, &x19, x17, x13, x10); + var x20: u64 = undefined; + var x21: u1 = undefined; + addcarryxU64(&x20, &x21, x19, x11, x8); + var x22: u64 = undefined; + var x23: u1 = undefined; + addcarryxU64(&x22, &x23, x21, x9, x6); + var x24: u64 = undefined; + var x25: u1 = undefined; + addcarryxU64(&x24, &x25, x23, x7, x4); + var x26: u64 = undefined; + var x27: u1 = undefined; + addcarryxU64(&x26, &x27, 0x0, x1, x14); + var x28: u64 = undefined; + var x29: u1 = undefined; + addcarryxU64(&x28, &x29, x27, 0x0, x16); + var x30: u64 = undefined; + var x31: u1 = undefined; + addcarryxU64(&x30, &x31, x29, 0x0, x18); + var x32: u64 = undefined; + var x33: u1 = undefined; + addcarryxU64(&x32, &x33, x31, 0x0, x20); + var x34: u64 = undefined; + var x35: u1 = undefined; + addcarryxU64(&x34, &x35, x33, 0x0, x22); + var x36: u64 = undefined; + var x37: u1 = undefined; + addcarryxU64(&x36, &x37, x35, 0x0, x24); + var x38: u64 = undefined; + var x39: u1 = undefined; + addcarryxU64(&x38, &x39, x37, 0x0, (@as(u64, x25) + x5)); + var x40: u64 = undefined; + var x41: u1 = undefined; + addcarryxU64(&x40, &x41, 0x0, x28, (arg1[1])); + var x42: u64 = undefined; + var x43: u1 = undefined; + addcarryxU64(&x42, &x43, x41, x30, 0x0); + var x44: u64 = undefined; + var x45: u1 = undefined; + addcarryxU64(&x44, &x45, x43, x32, 0x0); + var x46: u64 = undefined; + var x47: u1 = undefined; + addcarryxU64(&x46, &x47, x45, x34, 0x0); + var x48: u64 = undefined; + var x49: u1 = undefined; + addcarryxU64(&x48, &x49, x47, x36, 0x0); + var x50: u64 = undefined; + var x51: u1 = undefined; + addcarryxU64(&x50, &x51, x49, x38, 0x0); + var x52: u64 = undefined; + var x53: u64 = undefined; + mulxU64(&x52, &x53, x40, 0x6ed46089e88fdc45); + var x54: u64 = undefined; + var x55: u64 = undefined; + mulxU64(&x54, &x55, x52, 0xffffffffffffffff); + var x56: u64 = undefined; + var x57: u64 = undefined; + mulxU64(&x56, &x57, x52, 0xffffffffffffffff); + var x58: u64 = undefined; + var x59: u64 = undefined; + mulxU64(&x58, &x59, x52, 0xffffffffffffffff); + var x60: u64 = undefined; + var x61: u64 = undefined; + mulxU64(&x60, &x61, x52, 0xc7634d81f4372ddf); + var x62: u64 = undefined; + var x63: u64 = undefined; + mulxU64(&x62, &x63, x52, 0x581a0db248b0a77a); + var x64: u64 = undefined; + var x65: u64 = undefined; + mulxU64(&x64, &x65, x52, 0xecec196accc52973); + var x66: u64 = undefined; + var x67: u1 = undefined; + addcarryxU64(&x66, &x67, 0x0, x65, x62); + var x68: u64 = undefined; + var x69: u1 = undefined; + addcarryxU64(&x68, &x69, x67, x63, x60); + var x70: u64 = undefined; + var x71: u1 = undefined; + addcarryxU64(&x70, &x71, x69, x61, x58); + var x72: u64 = undefined; + var x73: u1 = undefined; + addcarryxU64(&x72, &x73, x71, x59, x56); + var x74: u64 = undefined; + var x75: u1 = undefined; + addcarryxU64(&x74, &x75, x73, x57, x54); + var x76: u64 = undefined; + var x77: u1 = undefined; + addcarryxU64(&x76, &x77, 0x0, x40, x64); + var x78: u64 = undefined; + var x79: u1 = undefined; + addcarryxU64(&x78, &x79, x77, x42, x66); + var x80: u64 = undefined; + var x81: u1 = undefined; + addcarryxU64(&x80, &x81, x79, x44, x68); + var x82: u64 = undefined; + var x83: u1 = undefined; + addcarryxU64(&x82, &x83, x81, x46, x70); + var x84: u64 = undefined; + var x85: u1 = undefined; + addcarryxU64(&x84, &x85, x83, x48, x72); + var x86: u64 = undefined; + var x87: u1 = undefined; + addcarryxU64(&x86, &x87, x85, x50, x74); + var x88: u64 = undefined; + var x89: u1 = undefined; + addcarryxU64(&x88, &x89, x87, (@as(u64, x51) + @as(u64, x39)), (@as(u64, x75) + x55)); + var x90: u64 = undefined; + var x91: u1 = undefined; + addcarryxU64(&x90, &x91, 0x0, x78, (arg1[2])); + var x92: u64 = undefined; + var x93: u1 = undefined; + addcarryxU64(&x92, &x93, x91, x80, 0x0); + var x94: u64 = undefined; + var x95: u1 = undefined; + addcarryxU64(&x94, &x95, x93, x82, 0x0); + var x96: u64 = undefined; + var x97: u1 = undefined; + addcarryxU64(&x96, &x97, x95, x84, 0x0); + var x98: u64 = undefined; + var x99: u1 = undefined; + addcarryxU64(&x98, &x99, x97, x86, 0x0); + var x100: u64 = undefined; + var x101: u1 = undefined; + addcarryxU64(&x100, &x101, x99, x88, 0x0); + var x102: u64 = undefined; + var x103: u64 = undefined; + mulxU64(&x102, &x103, x90, 0x6ed46089e88fdc45); + var x104: u64 = undefined; + var x105: u64 = undefined; + mulxU64(&x104, &x105, x102, 0xffffffffffffffff); + var x106: u64 = undefined; + var x107: u64 = undefined; + mulxU64(&x106, &x107, x102, 0xffffffffffffffff); + var x108: u64 = undefined; + var x109: u64 = undefined; + mulxU64(&x108, &x109, x102, 0xffffffffffffffff); + var x110: u64 = undefined; + var x111: u64 = undefined; + mulxU64(&x110, &x111, x102, 0xc7634d81f4372ddf); + var x112: u64 = undefined; + var x113: u64 = undefined; + mulxU64(&x112, &x113, x102, 0x581a0db248b0a77a); + var x114: u64 = undefined; + var x115: u64 = undefined; + mulxU64(&x114, &x115, x102, 0xecec196accc52973); + var x116: u64 = undefined; + var x117: u1 = undefined; + addcarryxU64(&x116, &x117, 0x0, x115, x112); + var x118: u64 = undefined; + var x119: u1 = undefined; + addcarryxU64(&x118, &x119, x117, x113, x110); + var x120: u64 = undefined; + var x121: u1 = undefined; + addcarryxU64(&x120, &x121, x119, x111, x108); + var x122: u64 = undefined; + var x123: u1 = undefined; + addcarryxU64(&x122, &x123, x121, x109, x106); + var x124: u64 = undefined; + var x125: u1 = undefined; + addcarryxU64(&x124, &x125, x123, x107, x104); + var x126: u64 = undefined; + var x127: u1 = undefined; + addcarryxU64(&x126, &x127, 0x0, x90, x114); + var x128: u64 = undefined; + var x129: u1 = undefined; + addcarryxU64(&x128, &x129, x127, x92, x116); + var x130: u64 = undefined; + var x131: u1 = undefined; + addcarryxU64(&x130, &x131, x129, x94, x118); + var x132: u64 = undefined; + var x133: u1 = undefined; + addcarryxU64(&x132, &x133, x131, x96, x120); + var x134: u64 = undefined; + var x135: u1 = undefined; + addcarryxU64(&x134, &x135, x133, x98, x122); + var x136: u64 = undefined; + var x137: u1 = undefined; + addcarryxU64(&x136, &x137, x135, x100, x124); + var x138: u64 = undefined; + var x139: u1 = undefined; + addcarryxU64(&x138, &x139, x137, (@as(u64, x101) + @as(u64, x89)), (@as(u64, x125) + x105)); + var x140: u64 = undefined; + var x141: u1 = undefined; + addcarryxU64(&x140, &x141, 0x0, x128, (arg1[3])); + var x142: u64 = undefined; + var x143: u1 = undefined; + addcarryxU64(&x142, &x143, x141, x130, 0x0); + var x144: u64 = undefined; + var x145: u1 = undefined; + addcarryxU64(&x144, &x145, x143, x132, 0x0); + var x146: u64 = undefined; + var x147: u1 = undefined; + addcarryxU64(&x146, &x147, x145, x134, 0x0); + var x148: u64 = undefined; + var x149: u1 = undefined; + addcarryxU64(&x148, &x149, x147, x136, 0x0); + var x150: u64 = undefined; + var x151: u1 = undefined; + addcarryxU64(&x150, &x151, x149, x138, 0x0); + var x152: u64 = undefined; + var x153: u64 = undefined; + mulxU64(&x152, &x153, x140, 0x6ed46089e88fdc45); + var x154: u64 = undefined; + var x155: u64 = undefined; + mulxU64(&x154, &x155, x152, 0xffffffffffffffff); + var x156: u64 = undefined; + var x157: u64 = undefined; + mulxU64(&x156, &x157, x152, 0xffffffffffffffff); + var x158: u64 = undefined; + var x159: u64 = undefined; + mulxU64(&x158, &x159, x152, 0xffffffffffffffff); + var x160: u64 = undefined; + var x161: u64 = undefined; + mulxU64(&x160, &x161, x152, 0xc7634d81f4372ddf); + var x162: u64 = undefined; + var x163: u64 = undefined; + mulxU64(&x162, &x163, x152, 0x581a0db248b0a77a); + var x164: u64 = undefined; + var x165: u64 = undefined; + mulxU64(&x164, &x165, x152, 0xecec196accc52973); + var x166: u64 = undefined; + var x167: u1 = undefined; + addcarryxU64(&x166, &x167, 0x0, x165, x162); + var x168: u64 = undefined; + var x169: u1 = undefined; + addcarryxU64(&x168, &x169, x167, x163, x160); + var x170: u64 = undefined; + var x171: u1 = undefined; + addcarryxU64(&x170, &x171, x169, x161, x158); + var x172: u64 = undefined; + var x173: u1 = undefined; + addcarryxU64(&x172, &x173, x171, x159, x156); + var x174: u64 = undefined; + var x175: u1 = undefined; + addcarryxU64(&x174, &x175, x173, x157, x154); + var x176: u64 = undefined; + var x177: u1 = undefined; + addcarryxU64(&x176, &x177, 0x0, x140, x164); + var x178: u64 = undefined; + var x179: u1 = undefined; + addcarryxU64(&x178, &x179, x177, x142, x166); + var x180: u64 = undefined; + var x181: u1 = undefined; + addcarryxU64(&x180, &x181, x179, x144, x168); + var x182: u64 = undefined; + var x183: u1 = undefined; + addcarryxU64(&x182, &x183, x181, x146, x170); + var x184: u64 = undefined; + var x185: u1 = undefined; + addcarryxU64(&x184, &x185, x183, x148, x172); + var x186: u64 = undefined; + var x187: u1 = undefined; + addcarryxU64(&x186, &x187, x185, x150, x174); + var x188: u64 = undefined; + var x189: u1 = undefined; + addcarryxU64(&x188, &x189, x187, (@as(u64, x151) + @as(u64, x139)), (@as(u64, x175) + x155)); + var x190: u64 = undefined; + var x191: u1 = undefined; + addcarryxU64(&x190, &x191, 0x0, x178, (arg1[4])); + var x192: u64 = undefined; + var x193: u1 = undefined; + addcarryxU64(&x192, &x193, x191, x180, 0x0); + var x194: u64 = undefined; + var x195: u1 = undefined; + addcarryxU64(&x194, &x195, x193, x182, 0x0); + var x196: u64 = undefined; + var x197: u1 = undefined; + addcarryxU64(&x196, &x197, x195, x184, 0x0); + var x198: u64 = undefined; + var x199: u1 = undefined; + addcarryxU64(&x198, &x199, x197, x186, 0x0); + var x200: u64 = undefined; + var x201: u1 = undefined; + addcarryxU64(&x200, &x201, x199, x188, 0x0); + var x202: u64 = undefined; + var x203: u64 = undefined; + mulxU64(&x202, &x203, x190, 0x6ed46089e88fdc45); + var x204: u64 = undefined; + var x205: u64 = undefined; + mulxU64(&x204, &x205, x202, 0xffffffffffffffff); + var x206: u64 = undefined; + var x207: u64 = undefined; + mulxU64(&x206, &x207, x202, 0xffffffffffffffff); + var x208: u64 = undefined; + var x209: u64 = undefined; + mulxU64(&x208, &x209, x202, 0xffffffffffffffff); + var x210: u64 = undefined; + var x211: u64 = undefined; + mulxU64(&x210, &x211, x202, 0xc7634d81f4372ddf); + var x212: u64 = undefined; + var x213: u64 = undefined; + mulxU64(&x212, &x213, x202, 0x581a0db248b0a77a); + var x214: u64 = undefined; + var x215: u64 = undefined; + mulxU64(&x214, &x215, x202, 0xecec196accc52973); + var x216: u64 = undefined; + var x217: u1 = undefined; + addcarryxU64(&x216, &x217, 0x0, x215, x212); + var x218: u64 = undefined; + var x219: u1 = undefined; + addcarryxU64(&x218, &x219, x217, x213, x210); + var x220: u64 = undefined; + var x221: u1 = undefined; + addcarryxU64(&x220, &x221, x219, x211, x208); + var x222: u64 = undefined; + var x223: u1 = undefined; + addcarryxU64(&x222, &x223, x221, x209, x206); + var x224: u64 = undefined; + var x225: u1 = undefined; + addcarryxU64(&x224, &x225, x223, x207, x204); + var x226: u64 = undefined; + var x227: u1 = undefined; + addcarryxU64(&x226, &x227, 0x0, x190, x214); + var x228: u64 = undefined; + var x229: u1 = undefined; + addcarryxU64(&x228, &x229, x227, x192, x216); + var x230: u64 = undefined; + var x231: u1 = undefined; + addcarryxU64(&x230, &x231, x229, x194, x218); + var x232: u64 = undefined; + var x233: u1 = undefined; + addcarryxU64(&x232, &x233, x231, x196, x220); + var x234: u64 = undefined; + var x235: u1 = undefined; + addcarryxU64(&x234, &x235, x233, x198, x222); + var x236: u64 = undefined; + var x237: u1 = undefined; + addcarryxU64(&x236, &x237, x235, x200, x224); + var x238: u64 = undefined; + var x239: u1 = undefined; + addcarryxU64(&x238, &x239, x237, (@as(u64, x201) + @as(u64, x189)), (@as(u64, x225) + x205)); + var x240: u64 = undefined; + var x241: u1 = undefined; + addcarryxU64(&x240, &x241, 0x0, x228, (arg1[5])); + var x242: u64 = undefined; + var x243: u1 = undefined; + addcarryxU64(&x242, &x243, x241, x230, 0x0); + var x244: u64 = undefined; + var x245: u1 = undefined; + addcarryxU64(&x244, &x245, x243, x232, 0x0); + var x246: u64 = undefined; + var x247: u1 = undefined; + addcarryxU64(&x246, &x247, x245, x234, 0x0); + var x248: u64 = undefined; + var x249: u1 = undefined; + addcarryxU64(&x248, &x249, x247, x236, 0x0); + var x250: u64 = undefined; + var x251: u1 = undefined; + addcarryxU64(&x250, &x251, x249, x238, 0x0); + var x252: u64 = undefined; + var x253: u64 = undefined; + mulxU64(&x252, &x253, x240, 0x6ed46089e88fdc45); + var x254: u64 = undefined; + var x255: u64 = undefined; + mulxU64(&x254, &x255, x252, 0xffffffffffffffff); + var x256: u64 = undefined; + var x257: u64 = undefined; + mulxU64(&x256, &x257, x252, 0xffffffffffffffff); + var x258: u64 = undefined; + var x259: u64 = undefined; + mulxU64(&x258, &x259, x252, 0xffffffffffffffff); + var x260: u64 = undefined; + var x261: u64 = undefined; + mulxU64(&x260, &x261, x252, 0xc7634d81f4372ddf); + var x262: u64 = undefined; + var x263: u64 = undefined; + mulxU64(&x262, &x263, x252, 0x581a0db248b0a77a); + var x264: u64 = undefined; + var x265: u64 = undefined; + mulxU64(&x264, &x265, x252, 0xecec196accc52973); + var x266: u64 = undefined; + var x267: u1 = undefined; + addcarryxU64(&x266, &x267, 0x0, x265, x262); + var x268: u64 = undefined; + var x269: u1 = undefined; + addcarryxU64(&x268, &x269, x267, x263, x260); + var x270: u64 = undefined; + var x271: u1 = undefined; + addcarryxU64(&x270, &x271, x269, x261, x258); + var x272: u64 = undefined; + var x273: u1 = undefined; + addcarryxU64(&x272, &x273, x271, x259, x256); + var x274: u64 = undefined; + var x275: u1 = undefined; + addcarryxU64(&x274, &x275, x273, x257, x254); + var x276: u64 = undefined; + var x277: u1 = undefined; + addcarryxU64(&x276, &x277, 0x0, x240, x264); + var x278: u64 = undefined; + var x279: u1 = undefined; + addcarryxU64(&x278, &x279, x277, x242, x266); + var x280: u64 = undefined; + var x281: u1 = undefined; + addcarryxU64(&x280, &x281, x279, x244, x268); + var x282: u64 = undefined; + var x283: u1 = undefined; + addcarryxU64(&x282, &x283, x281, x246, x270); + var x284: u64 = undefined; + var x285: u1 = undefined; + addcarryxU64(&x284, &x285, x283, x248, x272); + var x286: u64 = undefined; + var x287: u1 = undefined; + addcarryxU64(&x286, &x287, x285, x250, x274); + var x288: u64 = undefined; + var x289: u1 = undefined; + addcarryxU64(&x288, &x289, x287, (@as(u64, x251) + @as(u64, x239)), (@as(u64, x275) + x255)); + var x290: u64 = undefined; + var x291: u1 = undefined; + subborrowxU64(&x290, &x291, 0x0, x278, 0xecec196accc52973); + var x292: u64 = undefined; + var x293: u1 = undefined; + subborrowxU64(&x292, &x293, x291, x280, 0x581a0db248b0a77a); + var x294: u64 = undefined; + var x295: u1 = undefined; + subborrowxU64(&x294, &x295, x293, x282, 0xc7634d81f4372ddf); + var x296: u64 = undefined; + var x297: u1 = undefined; + subborrowxU64(&x296, &x297, x295, x284, 0xffffffffffffffff); + var x298: u64 = undefined; + var x299: u1 = undefined; + subborrowxU64(&x298, &x299, x297, x286, 0xffffffffffffffff); + var x300: u64 = undefined; + var x301: u1 = undefined; + subborrowxU64(&x300, &x301, x299, x288, 0xffffffffffffffff); + var x302: u64 = undefined; + var x303: u1 = undefined; + subborrowxU64(&x302, &x303, x301, @as(u64, x289), 0x0); + var x304: u64 = undefined; + cmovznzU64(&x304, x303, x290, x278); + var x305: u64 = undefined; + cmovznzU64(&x305, x303, x292, x280); + var x306: u64 = undefined; + cmovznzU64(&x306, x303, x294, x282); + var x307: u64 = undefined; + cmovznzU64(&x307, x303, x296, x284); + var x308: u64 = undefined; + cmovznzU64(&x308, x303, x298, x286); + var x309: u64 = undefined; + cmovznzU64(&x309, x303, x300, x288); + out1[0] = x304; + out1[1] = x305; + out1[2] = x306; + out1[3] = x307; + out1[4] = x308; + out1[5] = x309; +} + +/// The function toMontgomery translates a field element into the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = eval arg1 mod m +/// 0 ≤ eval out1 < m +/// +pub fn toMontgomery(out1: *MontgomeryDomainFieldElement, arg1: NonMontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[1]); + const x2 = (arg1[2]); + const x3 = (arg1[3]); + const x4 = (arg1[4]); + const x5 = (arg1[5]); + const x6 = (arg1[0]); + var x7: u64 = undefined; + var x8: u64 = undefined; + mulxU64(&x7, &x8, x6, 0xc84ee012b39bf21); + var x9: u64 = undefined; + var x10: u64 = undefined; + mulxU64(&x9, &x10, x6, 0x3fb05b7a28266895); + var x11: u64 = undefined; + var x12: u64 = undefined; + mulxU64(&x11, &x12, x6, 0xd40d49174aab1cc5); + var x13: u64 = undefined; + var x14: u64 = undefined; + mulxU64(&x13, &x14, x6, 0xbc3e483afcb82947); + var x15: u64 = undefined; + var x16: u64 = undefined; + mulxU64(&x15, &x16, x6, 0xff3d81e5df1aa419); + var x17: u64 = undefined; + var x18: u64 = undefined; + mulxU64(&x17, &x18, x6, 0x2d319b2419b409a9); + var x19: u64 = undefined; + var x20: u1 = undefined; + addcarryxU64(&x19, &x20, 0x0, x18, x15); + var x21: u64 = undefined; + var x22: u1 = undefined; + addcarryxU64(&x21, &x22, x20, x16, x13); + var x23: u64 = undefined; + var x24: u1 = undefined; + addcarryxU64(&x23, &x24, x22, x14, x11); + var x25: u64 = undefined; + var x26: u1 = undefined; + addcarryxU64(&x25, &x26, x24, x12, x9); + var x27: u64 = undefined; + var x28: u1 = undefined; + addcarryxU64(&x27, &x28, x26, x10, x7); + var x29: u64 = undefined; + var x30: u64 = undefined; + mulxU64(&x29, &x30, x17, 0x6ed46089e88fdc45); + var x31: u64 = undefined; + var x32: u64 = undefined; + mulxU64(&x31, &x32, x29, 0xffffffffffffffff); + var x33: u64 = undefined; + var x34: u64 = undefined; + mulxU64(&x33, &x34, x29, 0xffffffffffffffff); + var x35: u64 = undefined; + var x36: u64 = undefined; + mulxU64(&x35, &x36, x29, 0xffffffffffffffff); + var x37: u64 = undefined; + var x38: u64 = undefined; + mulxU64(&x37, &x38, x29, 0xc7634d81f4372ddf); + var x39: u64 = undefined; + var x40: u64 = undefined; + mulxU64(&x39, &x40, x29, 0x581a0db248b0a77a); + var x41: u64 = undefined; + var x42: u64 = undefined; + mulxU64(&x41, &x42, x29, 0xecec196accc52973); + var x43: u64 = undefined; + var x44: u1 = undefined; + addcarryxU64(&x43, &x44, 0x0, x42, x39); + var x45: u64 = undefined; + var x46: u1 = undefined; + addcarryxU64(&x45, &x46, x44, x40, x37); + var x47: u64 = undefined; + var x48: u1 = undefined; + addcarryxU64(&x47, &x48, x46, x38, x35); + var x49: u64 = undefined; + var x50: u1 = undefined; + addcarryxU64(&x49, &x50, x48, x36, x33); + var x51: u64 = undefined; + var x52: u1 = undefined; + addcarryxU64(&x51, &x52, x50, x34, x31); + var x53: u64 = undefined; + var x54: u1 = undefined; + addcarryxU64(&x53, &x54, 0x0, x17, x41); + var x55: u64 = undefined; + var x56: u1 = undefined; + addcarryxU64(&x55, &x56, x54, x19, x43); + var x57: u64 = undefined; + var x58: u1 = undefined; + addcarryxU64(&x57, &x58, x56, x21, x45); + var x59: u64 = undefined; + var x60: u1 = undefined; + addcarryxU64(&x59, &x60, x58, x23, x47); + var x61: u64 = undefined; + var x62: u1 = undefined; + addcarryxU64(&x61, &x62, x60, x25, x49); + var x63: u64 = undefined; + var x64: u1 = undefined; + addcarryxU64(&x63, &x64, x62, x27, x51); + var x65: u64 = undefined; + var x66: u1 = undefined; + addcarryxU64(&x65, &x66, x64, (@as(u64, x28) + x8), (@as(u64, x52) + x32)); + var x67: u64 = undefined; + var x68: u64 = undefined; + mulxU64(&x67, &x68, x1, 0xc84ee012b39bf21); + var x69: u64 = undefined; + var x70: u64 = undefined; + mulxU64(&x69, &x70, x1, 0x3fb05b7a28266895); + var x71: u64 = undefined; + var x72: u64 = undefined; + mulxU64(&x71, &x72, x1, 0xd40d49174aab1cc5); + var x73: u64 = undefined; + var x74: u64 = undefined; + mulxU64(&x73, &x74, x1, 0xbc3e483afcb82947); + var x75: u64 = undefined; + var x76: u64 = undefined; + mulxU64(&x75, &x76, x1, 0xff3d81e5df1aa419); + var x77: u64 = undefined; + var x78: u64 = undefined; + mulxU64(&x77, &x78, x1, 0x2d319b2419b409a9); + var x79: u64 = undefined; + var x80: u1 = undefined; + addcarryxU64(&x79, &x80, 0x0, x78, x75); + var x81: u64 = undefined; + var x82: u1 = undefined; + addcarryxU64(&x81, &x82, x80, x76, x73); + var x83: u64 = undefined; + var x84: u1 = undefined; + addcarryxU64(&x83, &x84, x82, x74, x71); + var x85: u64 = undefined; + var x86: u1 = undefined; + addcarryxU64(&x85, &x86, x84, x72, x69); + var x87: u64 = undefined; + var x88: u1 = undefined; + addcarryxU64(&x87, &x88, x86, x70, x67); + var x89: u64 = undefined; + var x90: u1 = undefined; + addcarryxU64(&x89, &x90, 0x0, x55, x77); + var x91: u64 = undefined; + var x92: u1 = undefined; + addcarryxU64(&x91, &x92, x90, x57, x79); + var x93: u64 = undefined; + var x94: u1 = undefined; + addcarryxU64(&x93, &x94, x92, x59, x81); + var x95: u64 = undefined; + var x96: u1 = undefined; + addcarryxU64(&x95, &x96, x94, x61, x83); + var x97: u64 = undefined; + var x98: u1 = undefined; + addcarryxU64(&x97, &x98, x96, x63, x85); + var x99: u64 = undefined; + var x100: u1 = undefined; + addcarryxU64(&x99, &x100, x98, x65, x87); + var x101: u64 = undefined; + var x102: u64 = undefined; + mulxU64(&x101, &x102, x89, 0x6ed46089e88fdc45); + var x103: u64 = undefined; + var x104: u64 = undefined; + mulxU64(&x103, &x104, x101, 0xffffffffffffffff); + var x105: u64 = undefined; + var x106: u64 = undefined; + mulxU64(&x105, &x106, x101, 0xffffffffffffffff); + var x107: u64 = undefined; + var x108: u64 = undefined; + mulxU64(&x107, &x108, x101, 0xffffffffffffffff); + var x109: u64 = undefined; + var x110: u64 = undefined; + mulxU64(&x109, &x110, x101, 0xc7634d81f4372ddf); + var x111: u64 = undefined; + var x112: u64 = undefined; + mulxU64(&x111, &x112, x101, 0x581a0db248b0a77a); + var x113: u64 = undefined; + var x114: u64 = undefined; + mulxU64(&x113, &x114, x101, 0xecec196accc52973); + var x115: u64 = undefined; + var x116: u1 = undefined; + addcarryxU64(&x115, &x116, 0x0, x114, x111); + var x117: u64 = undefined; + var x118: u1 = undefined; + addcarryxU64(&x117, &x118, x116, x112, x109); + var x119: u64 = undefined; + var x120: u1 = undefined; + addcarryxU64(&x119, &x120, x118, x110, x107); + var x121: u64 = undefined; + var x122: u1 = undefined; + addcarryxU64(&x121, &x122, x120, x108, x105); + var x123: u64 = undefined; + var x124: u1 = undefined; + addcarryxU64(&x123, &x124, x122, x106, x103); + var x125: u64 = undefined; + var x126: u1 = undefined; + addcarryxU64(&x125, &x126, 0x0, x89, x113); + var x127: u64 = undefined; + var x128: u1 = undefined; + addcarryxU64(&x127, &x128, x126, x91, x115); + var x129: u64 = undefined; + var x130: u1 = undefined; + addcarryxU64(&x129, &x130, x128, x93, x117); + var x131: u64 = undefined; + var x132: u1 = undefined; + addcarryxU64(&x131, &x132, x130, x95, x119); + var x133: u64 = undefined; + var x134: u1 = undefined; + addcarryxU64(&x133, &x134, x132, x97, x121); + var x135: u64 = undefined; + var x136: u1 = undefined; + addcarryxU64(&x135, &x136, x134, x99, x123); + var x137: u64 = undefined; + var x138: u1 = undefined; + addcarryxU64(&x137, &x138, x136, ((@as(u64, x100) + @as(u64, x66)) + (@as(u64, x88) + x68)), (@as(u64, x124) + x104)); + var x139: u64 = undefined; + var x140: u64 = undefined; + mulxU64(&x139, &x140, x2, 0xc84ee012b39bf21); + var x141: u64 = undefined; + var x142: u64 = undefined; + mulxU64(&x141, &x142, x2, 0x3fb05b7a28266895); + var x143: u64 = undefined; + var x144: u64 = undefined; + mulxU64(&x143, &x144, x2, 0xd40d49174aab1cc5); + var x145: u64 = undefined; + var x146: u64 = undefined; + mulxU64(&x145, &x146, x2, 0xbc3e483afcb82947); + var x147: u64 = undefined; + var x148: u64 = undefined; + mulxU64(&x147, &x148, x2, 0xff3d81e5df1aa419); + var x149: u64 = undefined; + var x150: u64 = undefined; + mulxU64(&x149, &x150, x2, 0x2d319b2419b409a9); + var x151: u64 = undefined; + var x152: u1 = undefined; + addcarryxU64(&x151, &x152, 0x0, x150, x147); + var x153: u64 = undefined; + var x154: u1 = undefined; + addcarryxU64(&x153, &x154, x152, x148, x145); + var x155: u64 = undefined; + var x156: u1 = undefined; + addcarryxU64(&x155, &x156, x154, x146, x143); + var x157: u64 = undefined; + var x158: u1 = undefined; + addcarryxU64(&x157, &x158, x156, x144, x141); + var x159: u64 = undefined; + var x160: u1 = undefined; + addcarryxU64(&x159, &x160, x158, x142, x139); + var x161: u64 = undefined; + var x162: u1 = undefined; + addcarryxU64(&x161, &x162, 0x0, x127, x149); + var x163: u64 = undefined; + var x164: u1 = undefined; + addcarryxU64(&x163, &x164, x162, x129, x151); + var x165: u64 = undefined; + var x166: u1 = undefined; + addcarryxU64(&x165, &x166, x164, x131, x153); + var x167: u64 = undefined; + var x168: u1 = undefined; + addcarryxU64(&x167, &x168, x166, x133, x155); + var x169: u64 = undefined; + var x170: u1 = undefined; + addcarryxU64(&x169, &x170, x168, x135, x157); + var x171: u64 = undefined; + var x172: u1 = undefined; + addcarryxU64(&x171, &x172, x170, x137, x159); + var x173: u64 = undefined; + var x174: u64 = undefined; + mulxU64(&x173, &x174, x161, 0x6ed46089e88fdc45); + var x175: u64 = undefined; + var x176: u64 = undefined; + mulxU64(&x175, &x176, x173, 0xffffffffffffffff); + var x177: u64 = undefined; + var x178: u64 = undefined; + mulxU64(&x177, &x178, x173, 0xffffffffffffffff); + var x179: u64 = undefined; + var x180: u64 = undefined; + mulxU64(&x179, &x180, x173, 0xffffffffffffffff); + var x181: u64 = undefined; + var x182: u64 = undefined; + mulxU64(&x181, &x182, x173, 0xc7634d81f4372ddf); + var x183: u64 = undefined; + var x184: u64 = undefined; + mulxU64(&x183, &x184, x173, 0x581a0db248b0a77a); + var x185: u64 = undefined; + var x186: u64 = undefined; + mulxU64(&x185, &x186, x173, 0xecec196accc52973); + var x187: u64 = undefined; + var x188: u1 = undefined; + addcarryxU64(&x187, &x188, 0x0, x186, x183); + var x189: u64 = undefined; + var x190: u1 = undefined; + addcarryxU64(&x189, &x190, x188, x184, x181); + var x191: u64 = undefined; + var x192: u1 = undefined; + addcarryxU64(&x191, &x192, x190, x182, x179); + var x193: u64 = undefined; + var x194: u1 = undefined; + addcarryxU64(&x193, &x194, x192, x180, x177); + var x195: u64 = undefined; + var x196: u1 = undefined; + addcarryxU64(&x195, &x196, x194, x178, x175); + var x197: u64 = undefined; + var x198: u1 = undefined; + addcarryxU64(&x197, &x198, 0x0, x161, x185); + var x199: u64 = undefined; + var x200: u1 = undefined; + addcarryxU64(&x199, &x200, x198, x163, x187); + var x201: u64 = undefined; + var x202: u1 = undefined; + addcarryxU64(&x201, &x202, x200, x165, x189); + var x203: u64 = undefined; + var x204: u1 = undefined; + addcarryxU64(&x203, &x204, x202, x167, x191); + var x205: u64 = undefined; + var x206: u1 = undefined; + addcarryxU64(&x205, &x206, x204, x169, x193); + var x207: u64 = undefined; + var x208: u1 = undefined; + addcarryxU64(&x207, &x208, x206, x171, x195); + var x209: u64 = undefined; + var x210: u1 = undefined; + addcarryxU64(&x209, &x210, x208, ((@as(u64, x172) + @as(u64, x138)) + (@as(u64, x160) + x140)), (@as(u64, x196) + x176)); + var x211: u64 = undefined; + var x212: u64 = undefined; + mulxU64(&x211, &x212, x3, 0xc84ee012b39bf21); + var x213: u64 = undefined; + var x214: u64 = undefined; + mulxU64(&x213, &x214, x3, 0x3fb05b7a28266895); + var x215: u64 = undefined; + var x216: u64 = undefined; + mulxU64(&x215, &x216, x3, 0xd40d49174aab1cc5); + var x217: u64 = undefined; + var x218: u64 = undefined; + mulxU64(&x217, &x218, x3, 0xbc3e483afcb82947); + var x219: u64 = undefined; + var x220: u64 = undefined; + mulxU64(&x219, &x220, x3, 0xff3d81e5df1aa419); + var x221: u64 = undefined; + var x222: u64 = undefined; + mulxU64(&x221, &x222, x3, 0x2d319b2419b409a9); + var x223: u64 = undefined; + var x224: u1 = undefined; + addcarryxU64(&x223, &x224, 0x0, x222, x219); + var x225: u64 = undefined; + var x226: u1 = undefined; + addcarryxU64(&x225, &x226, x224, x220, x217); + var x227: u64 = undefined; + var x228: u1 = undefined; + addcarryxU64(&x227, &x228, x226, x218, x215); + var x229: u64 = undefined; + var x230: u1 = undefined; + addcarryxU64(&x229, &x230, x228, x216, x213); + var x231: u64 = undefined; + var x232: u1 = undefined; + addcarryxU64(&x231, &x232, x230, x214, x211); + var x233: u64 = undefined; + var x234: u1 = undefined; + addcarryxU64(&x233, &x234, 0x0, x199, x221); + var x235: u64 = undefined; + var x236: u1 = undefined; + addcarryxU64(&x235, &x236, x234, x201, x223); + var x237: u64 = undefined; + var x238: u1 = undefined; + addcarryxU64(&x237, &x238, x236, x203, x225); + var x239: u64 = undefined; + var x240: u1 = undefined; + addcarryxU64(&x239, &x240, x238, x205, x227); + var x241: u64 = undefined; + var x242: u1 = undefined; + addcarryxU64(&x241, &x242, x240, x207, x229); + var x243: u64 = undefined; + var x244: u1 = undefined; + addcarryxU64(&x243, &x244, x242, x209, x231); + var x245: u64 = undefined; + var x246: u64 = undefined; + mulxU64(&x245, &x246, x233, 0x6ed46089e88fdc45); + var x247: u64 = undefined; + var x248: u64 = undefined; + mulxU64(&x247, &x248, x245, 0xffffffffffffffff); + var x249: u64 = undefined; + var x250: u64 = undefined; + mulxU64(&x249, &x250, x245, 0xffffffffffffffff); + var x251: u64 = undefined; + var x252: u64 = undefined; + mulxU64(&x251, &x252, x245, 0xffffffffffffffff); + var x253: u64 = undefined; + var x254: u64 = undefined; + mulxU64(&x253, &x254, x245, 0xc7634d81f4372ddf); + var x255: u64 = undefined; + var x256: u64 = undefined; + mulxU64(&x255, &x256, x245, 0x581a0db248b0a77a); + var x257: u64 = undefined; + var x258: u64 = undefined; + mulxU64(&x257, &x258, x245, 0xecec196accc52973); + var x259: u64 = undefined; + var x260: u1 = undefined; + addcarryxU64(&x259, &x260, 0x0, x258, x255); + var x261: u64 = undefined; + var x262: u1 = undefined; + addcarryxU64(&x261, &x262, x260, x256, x253); + var x263: u64 = undefined; + var x264: u1 = undefined; + addcarryxU64(&x263, &x264, x262, x254, x251); + var x265: u64 = undefined; + var x266: u1 = undefined; + addcarryxU64(&x265, &x266, x264, x252, x249); + var x267: u64 = undefined; + var x268: u1 = undefined; + addcarryxU64(&x267, &x268, x266, x250, x247); + var x269: u64 = undefined; + var x270: u1 = undefined; + addcarryxU64(&x269, &x270, 0x0, x233, x257); + var x271: u64 = undefined; + var x272: u1 = undefined; + addcarryxU64(&x271, &x272, x270, x235, x259); + var x273: u64 = undefined; + var x274: u1 = undefined; + addcarryxU64(&x273, &x274, x272, x237, x261); + var x275: u64 = undefined; + var x276: u1 = undefined; + addcarryxU64(&x275, &x276, x274, x239, x263); + var x277: u64 = undefined; + var x278: u1 = undefined; + addcarryxU64(&x277, &x278, x276, x241, x265); + var x279: u64 = undefined; + var x280: u1 = undefined; + addcarryxU64(&x279, &x280, x278, x243, x267); + var x281: u64 = undefined; + var x282: u1 = undefined; + addcarryxU64(&x281, &x282, x280, ((@as(u64, x244) + @as(u64, x210)) + (@as(u64, x232) + x212)), (@as(u64, x268) + x248)); + var x283: u64 = undefined; + var x284: u64 = undefined; + mulxU64(&x283, &x284, x4, 0xc84ee012b39bf21); + var x285: u64 = undefined; + var x286: u64 = undefined; + mulxU64(&x285, &x286, x4, 0x3fb05b7a28266895); + var x287: u64 = undefined; + var x288: u64 = undefined; + mulxU64(&x287, &x288, x4, 0xd40d49174aab1cc5); + var x289: u64 = undefined; + var x290: u64 = undefined; + mulxU64(&x289, &x290, x4, 0xbc3e483afcb82947); + var x291: u64 = undefined; + var x292: u64 = undefined; + mulxU64(&x291, &x292, x4, 0xff3d81e5df1aa419); + var x293: u64 = undefined; + var x294: u64 = undefined; + mulxU64(&x293, &x294, x4, 0x2d319b2419b409a9); + var x295: u64 = undefined; + var x296: u1 = undefined; + addcarryxU64(&x295, &x296, 0x0, x294, x291); + var x297: u64 = undefined; + var x298: u1 = undefined; + addcarryxU64(&x297, &x298, x296, x292, x289); + var x299: u64 = undefined; + var x300: u1 = undefined; + addcarryxU64(&x299, &x300, x298, x290, x287); + var x301: u64 = undefined; + var x302: u1 = undefined; + addcarryxU64(&x301, &x302, x300, x288, x285); + var x303: u64 = undefined; + var x304: u1 = undefined; + addcarryxU64(&x303, &x304, x302, x286, x283); + var x305: u64 = undefined; + var x306: u1 = undefined; + addcarryxU64(&x305, &x306, 0x0, x271, x293); + var x307: u64 = undefined; + var x308: u1 = undefined; + addcarryxU64(&x307, &x308, x306, x273, x295); + var x309: u64 = undefined; + var x310: u1 = undefined; + addcarryxU64(&x309, &x310, x308, x275, x297); + var x311: u64 = undefined; + var x312: u1 = undefined; + addcarryxU64(&x311, &x312, x310, x277, x299); + var x313: u64 = undefined; + var x314: u1 = undefined; + addcarryxU64(&x313, &x314, x312, x279, x301); + var x315: u64 = undefined; + var x316: u1 = undefined; + addcarryxU64(&x315, &x316, x314, x281, x303); + var x317: u64 = undefined; + var x318: u64 = undefined; + mulxU64(&x317, &x318, x305, 0x6ed46089e88fdc45); + var x319: u64 = undefined; + var x320: u64 = undefined; + mulxU64(&x319, &x320, x317, 0xffffffffffffffff); + var x321: u64 = undefined; + var x322: u64 = undefined; + mulxU64(&x321, &x322, x317, 0xffffffffffffffff); + var x323: u64 = undefined; + var x324: u64 = undefined; + mulxU64(&x323, &x324, x317, 0xffffffffffffffff); + var x325: u64 = undefined; + var x326: u64 = undefined; + mulxU64(&x325, &x326, x317, 0xc7634d81f4372ddf); + var x327: u64 = undefined; + var x328: u64 = undefined; + mulxU64(&x327, &x328, x317, 0x581a0db248b0a77a); + var x329: u64 = undefined; + var x330: u64 = undefined; + mulxU64(&x329, &x330, x317, 0xecec196accc52973); + var x331: u64 = undefined; + var x332: u1 = undefined; + addcarryxU64(&x331, &x332, 0x0, x330, x327); + var x333: u64 = undefined; + var x334: u1 = undefined; + addcarryxU64(&x333, &x334, x332, x328, x325); + var x335: u64 = undefined; + var x336: u1 = undefined; + addcarryxU64(&x335, &x336, x334, x326, x323); + var x337: u64 = undefined; + var x338: u1 = undefined; + addcarryxU64(&x337, &x338, x336, x324, x321); + var x339: u64 = undefined; + var x340: u1 = undefined; + addcarryxU64(&x339, &x340, x338, x322, x319); + var x341: u64 = undefined; + var x342: u1 = undefined; + addcarryxU64(&x341, &x342, 0x0, x305, x329); + var x343: u64 = undefined; + var x344: u1 = undefined; + addcarryxU64(&x343, &x344, x342, x307, x331); + var x345: u64 = undefined; + var x346: u1 = undefined; + addcarryxU64(&x345, &x346, x344, x309, x333); + var x347: u64 = undefined; + var x348: u1 = undefined; + addcarryxU64(&x347, &x348, x346, x311, x335); + var x349: u64 = undefined; + var x350: u1 = undefined; + addcarryxU64(&x349, &x350, x348, x313, x337); + var x351: u64 = undefined; + var x352: u1 = undefined; + addcarryxU64(&x351, &x352, x350, x315, x339); + var x353: u64 = undefined; + var x354: u1 = undefined; + addcarryxU64(&x353, &x354, x352, ((@as(u64, x316) + @as(u64, x282)) + (@as(u64, x304) + x284)), (@as(u64, x340) + x320)); + var x355: u64 = undefined; + var x356: u64 = undefined; + mulxU64(&x355, &x356, x5, 0xc84ee012b39bf21); + var x357: u64 = undefined; + var x358: u64 = undefined; + mulxU64(&x357, &x358, x5, 0x3fb05b7a28266895); + var x359: u64 = undefined; + var x360: u64 = undefined; + mulxU64(&x359, &x360, x5, 0xd40d49174aab1cc5); + var x361: u64 = undefined; + var x362: u64 = undefined; + mulxU64(&x361, &x362, x5, 0xbc3e483afcb82947); + var x363: u64 = undefined; + var x364: u64 = undefined; + mulxU64(&x363, &x364, x5, 0xff3d81e5df1aa419); + var x365: u64 = undefined; + var x366: u64 = undefined; + mulxU64(&x365, &x366, x5, 0x2d319b2419b409a9); + var x367: u64 = undefined; + var x368: u1 = undefined; + addcarryxU64(&x367, &x368, 0x0, x366, x363); + var x369: u64 = undefined; + var x370: u1 = undefined; + addcarryxU64(&x369, &x370, x368, x364, x361); + var x371: u64 = undefined; + var x372: u1 = undefined; + addcarryxU64(&x371, &x372, x370, x362, x359); + var x373: u64 = undefined; + var x374: u1 = undefined; + addcarryxU64(&x373, &x374, x372, x360, x357); + var x375: u64 = undefined; + var x376: u1 = undefined; + addcarryxU64(&x375, &x376, x374, x358, x355); + var x377: u64 = undefined; + var x378: u1 = undefined; + addcarryxU64(&x377, &x378, 0x0, x343, x365); + var x379: u64 = undefined; + var x380: u1 = undefined; + addcarryxU64(&x379, &x380, x378, x345, x367); + var x381: u64 = undefined; + var x382: u1 = undefined; + addcarryxU64(&x381, &x382, x380, x347, x369); + var x383: u64 = undefined; + var x384: u1 = undefined; + addcarryxU64(&x383, &x384, x382, x349, x371); + var x385: u64 = undefined; + var x386: u1 = undefined; + addcarryxU64(&x385, &x386, x384, x351, x373); + var x387: u64 = undefined; + var x388: u1 = undefined; + addcarryxU64(&x387, &x388, x386, x353, x375); + var x389: u64 = undefined; + var x390: u64 = undefined; + mulxU64(&x389, &x390, x377, 0x6ed46089e88fdc45); + var x391: u64 = undefined; + var x392: u64 = undefined; + mulxU64(&x391, &x392, x389, 0xffffffffffffffff); + var x393: u64 = undefined; + var x394: u64 = undefined; + mulxU64(&x393, &x394, x389, 0xffffffffffffffff); + var x395: u64 = undefined; + var x396: u64 = undefined; + mulxU64(&x395, &x396, x389, 0xffffffffffffffff); + var x397: u64 = undefined; + var x398: u64 = undefined; + mulxU64(&x397, &x398, x389, 0xc7634d81f4372ddf); + var x399: u64 = undefined; + var x400: u64 = undefined; + mulxU64(&x399, &x400, x389, 0x581a0db248b0a77a); + var x401: u64 = undefined; + var x402: u64 = undefined; + mulxU64(&x401, &x402, x389, 0xecec196accc52973); + var x403: u64 = undefined; + var x404: u1 = undefined; + addcarryxU64(&x403, &x404, 0x0, x402, x399); + var x405: u64 = undefined; + var x406: u1 = undefined; + addcarryxU64(&x405, &x406, x404, x400, x397); + var x407: u64 = undefined; + var x408: u1 = undefined; + addcarryxU64(&x407, &x408, x406, x398, x395); + var x409: u64 = undefined; + var x410: u1 = undefined; + addcarryxU64(&x409, &x410, x408, x396, x393); + var x411: u64 = undefined; + var x412: u1 = undefined; + addcarryxU64(&x411, &x412, x410, x394, x391); + var x413: u64 = undefined; + var x414: u1 = undefined; + addcarryxU64(&x413, &x414, 0x0, x377, x401); + var x415: u64 = undefined; + var x416: u1 = undefined; + addcarryxU64(&x415, &x416, x414, x379, x403); + var x417: u64 = undefined; + var x418: u1 = undefined; + addcarryxU64(&x417, &x418, x416, x381, x405); + var x419: u64 = undefined; + var x420: u1 = undefined; + addcarryxU64(&x419, &x420, x418, x383, x407); + var x421: u64 = undefined; + var x422: u1 = undefined; + addcarryxU64(&x421, &x422, x420, x385, x409); + var x423: u64 = undefined; + var x424: u1 = undefined; + addcarryxU64(&x423, &x424, x422, x387, x411); + var x425: u64 = undefined; + var x426: u1 = undefined; + addcarryxU64(&x425, &x426, x424, ((@as(u64, x388) + @as(u64, x354)) + (@as(u64, x376) + x356)), (@as(u64, x412) + x392)); + var x427: u64 = undefined; + var x428: u1 = undefined; + subborrowxU64(&x427, &x428, 0x0, x415, 0xecec196accc52973); + var x429: u64 = undefined; + var x430: u1 = undefined; + subborrowxU64(&x429, &x430, x428, x417, 0x581a0db248b0a77a); + var x431: u64 = undefined; + var x432: u1 = undefined; + subborrowxU64(&x431, &x432, x430, x419, 0xc7634d81f4372ddf); + var x433: u64 = undefined; + var x434: u1 = undefined; + subborrowxU64(&x433, &x434, x432, x421, 0xffffffffffffffff); + var x435: u64 = undefined; + var x436: u1 = undefined; + subborrowxU64(&x435, &x436, x434, x423, 0xffffffffffffffff); + var x437: u64 = undefined; + var x438: u1 = undefined; + subborrowxU64(&x437, &x438, x436, x425, 0xffffffffffffffff); + var x439: u64 = undefined; + var x440: u1 = undefined; + subborrowxU64(&x439, &x440, x438, @as(u64, x426), 0x0); + var x441: u64 = undefined; + cmovznzU64(&x441, x440, x427, x415); + var x442: u64 = undefined; + cmovznzU64(&x442, x440, x429, x417); + var x443: u64 = undefined; + cmovznzU64(&x443, x440, x431, x419); + var x444: u64 = undefined; + cmovznzU64(&x444, x440, x433, x421); + var x445: u64 = undefined; + cmovznzU64(&x445, x440, x435, x423); + var x446: u64 = undefined; + cmovznzU64(&x446, x440, x437, x425); + out1[0] = x441; + out1[1] = x442; + out1[2] = x443; + out1[3] = x444; + out1[4] = x445; + out1[5] = x446; +} + +/// The function nonzero outputs a single non-zero word if the input is non-zero and zero otherwise. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// out1 = 0 ↔ eval (from_montgomery arg1) mod m = 0 +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +pub fn nonzero(out1: *u64, arg1: [6]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = ((arg1[0]) | ((arg1[1]) | ((arg1[2]) | ((arg1[3]) | ((arg1[4]) | (arg1[5])))))); + out1.* = x1; +} + +/// The function selectznz is a multi-limb conditional select. +/// +/// Postconditions: +/// out1 = (if arg1 = 0 then arg2 else arg3) +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn selectznz(out1: *[6]u64, arg1: u1, arg2: [6]u64, arg3: [6]u64) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + cmovznzU64(&x1, arg1, (arg2[0]), (arg3[0])); + var x2: u64 = undefined; + cmovznzU64(&x2, arg1, (arg2[1]), (arg3[1])); + var x3: u64 = undefined; + cmovznzU64(&x3, arg1, (arg2[2]), (arg3[2])); + var x4: u64 = undefined; + cmovznzU64(&x4, arg1, (arg2[3]), (arg3[3])); + var x5: u64 = undefined; + cmovznzU64(&x5, arg1, (arg2[4]), (arg3[4])); + var x6: u64 = undefined; + cmovznzU64(&x6, arg1, (arg2[5]), (arg3[5])); + out1[0] = x1; + out1[1] = x2; + out1[2] = x3; + out1[3] = x4; + out1[4] = x5; + out1[5] = x6; +} + +/// The function toBytes serializes a field element NOT in the Montgomery domain to bytes in little-endian order. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// out1 = map (λ x, ⌊((eval arg1 mod m) mod 2^(8 * (x + 1))) / 2^(8 * x)⌋) [0..47] +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]] +pub fn toBytes(out1: *[48]u8, arg1: [6]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[5]); + const x2 = (arg1[4]); + const x3 = (arg1[3]); + const x4 = (arg1[2]); + const x5 = (arg1[1]); + const x6 = (arg1[0]); + const x7 = @truncate(u8, (x6 & 0xff)); + const x8 = (x6 >> 8); + const x9 = @truncate(u8, (x8 & 0xff)); + const x10 = (x8 >> 8); + const x11 = @truncate(u8, (x10 & 0xff)); + const x12 = (x10 >> 8); + const x13 = @truncate(u8, (x12 & 0xff)); + const x14 = (x12 >> 8); + const x15 = @truncate(u8, (x14 & 0xff)); + const x16 = (x14 >> 8); + const x17 = @truncate(u8, (x16 & 0xff)); + const x18 = (x16 >> 8); + const x19 = @truncate(u8, (x18 & 0xff)); + const x20 = @truncate(u8, (x18 >> 8)); + const x21 = @truncate(u8, (x5 & 0xff)); + const x22 = (x5 >> 8); + const x23 = @truncate(u8, (x22 & 0xff)); + const x24 = (x22 >> 8); + const x25 = @truncate(u8, (x24 & 0xff)); + const x26 = (x24 >> 8); + const x27 = @truncate(u8, (x26 & 0xff)); + const x28 = (x26 >> 8); + const x29 = @truncate(u8, (x28 & 0xff)); + const x30 = (x28 >> 8); + const x31 = @truncate(u8, (x30 & 0xff)); + const x32 = (x30 >> 8); + const x33 = @truncate(u8, (x32 & 0xff)); + const x34 = @truncate(u8, (x32 >> 8)); + const x35 = @truncate(u8, (x4 & 0xff)); + const x36 = (x4 >> 8); + const x37 = @truncate(u8, (x36 & 0xff)); + const x38 = (x36 >> 8); + const x39 = @truncate(u8, (x38 & 0xff)); + const x40 = (x38 >> 8); + const x41 = @truncate(u8, (x40 & 0xff)); + const x42 = (x40 >> 8); + const x43 = @truncate(u8, (x42 & 0xff)); + const x44 = (x42 >> 8); + const x45 = @truncate(u8, (x44 & 0xff)); + const x46 = (x44 >> 8); + const x47 = @truncate(u8, (x46 & 0xff)); + const x48 = @truncate(u8, (x46 >> 8)); + const x49 = @truncate(u8, (x3 & 0xff)); + const x50 = (x3 >> 8); + const x51 = @truncate(u8, (x50 & 0xff)); + const x52 = (x50 >> 8); + const x53 = @truncate(u8, (x52 & 0xff)); + const x54 = (x52 >> 8); + const x55 = @truncate(u8, (x54 & 0xff)); + const x56 = (x54 >> 8); + const x57 = @truncate(u8, (x56 & 0xff)); + const x58 = (x56 >> 8); + const x59 = @truncate(u8, (x58 & 0xff)); + const x60 = (x58 >> 8); + const x61 = @truncate(u8, (x60 & 0xff)); + const x62 = @truncate(u8, (x60 >> 8)); + const x63 = @truncate(u8, (x2 & 0xff)); + const x64 = (x2 >> 8); + const x65 = @truncate(u8, (x64 & 0xff)); + const x66 = (x64 >> 8); + const x67 = @truncate(u8, (x66 & 0xff)); + const x68 = (x66 >> 8); + const x69 = @truncate(u8, (x68 & 0xff)); + const x70 = (x68 >> 8); + const x71 = @truncate(u8, (x70 & 0xff)); + const x72 = (x70 >> 8); + const x73 = @truncate(u8, (x72 & 0xff)); + const x74 = (x72 >> 8); + const x75 = @truncate(u8, (x74 & 0xff)); + const x76 = @truncate(u8, (x74 >> 8)); + const x77 = @truncate(u8, (x1 & 0xff)); + const x78 = (x1 >> 8); + const x79 = @truncate(u8, (x78 & 0xff)); + const x80 = (x78 >> 8); + const x81 = @truncate(u8, (x80 & 0xff)); + const x82 = (x80 >> 8); + const x83 = @truncate(u8, (x82 & 0xff)); + const x84 = (x82 >> 8); + const x85 = @truncate(u8, (x84 & 0xff)); + const x86 = (x84 >> 8); + const x87 = @truncate(u8, (x86 & 0xff)); + const x88 = (x86 >> 8); + const x89 = @truncate(u8, (x88 & 0xff)); + const x90 = @truncate(u8, (x88 >> 8)); + out1[0] = x7; + out1[1] = x9; + out1[2] = x11; + out1[3] = x13; + out1[4] = x15; + out1[5] = x17; + out1[6] = x19; + out1[7] = x20; + out1[8] = x21; + out1[9] = x23; + out1[10] = x25; + out1[11] = x27; + out1[12] = x29; + out1[13] = x31; + out1[14] = x33; + out1[15] = x34; + out1[16] = x35; + out1[17] = x37; + out1[18] = x39; + out1[19] = x41; + out1[20] = x43; + out1[21] = x45; + out1[22] = x47; + out1[23] = x48; + out1[24] = x49; + out1[25] = x51; + out1[26] = x53; + out1[27] = x55; + out1[28] = x57; + out1[29] = x59; + out1[30] = x61; + out1[31] = x62; + out1[32] = x63; + out1[33] = x65; + out1[34] = x67; + out1[35] = x69; + out1[36] = x71; + out1[37] = x73; + out1[38] = x75; + out1[39] = x76; + out1[40] = x77; + out1[41] = x79; + out1[42] = x81; + out1[43] = x83; + out1[44] = x85; + out1[45] = x87; + out1[46] = x89; + out1[47] = x90; +} + +/// The function fromBytes deserializes a field element NOT in the Montgomery domain from bytes in little-endian order. +/// +/// Preconditions: +/// 0 ≤ bytes_eval arg1 < m +/// Postconditions: +/// eval out1 mod m = bytes_eval arg1 mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn fromBytes(out1: *[6]u64, arg1: [48]u8) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (@as(u64, (arg1[47])) << 56); + const x2 = (@as(u64, (arg1[46])) << 48); + const x3 = (@as(u64, (arg1[45])) << 40); + const x4 = (@as(u64, (arg1[44])) << 32); + const x5 = (@as(u64, (arg1[43])) << 24); + const x6 = (@as(u64, (arg1[42])) << 16); + const x7 = (@as(u64, (arg1[41])) << 8); + const x8 = (arg1[40]); + const x9 = (@as(u64, (arg1[39])) << 56); + const x10 = (@as(u64, (arg1[38])) << 48); + const x11 = (@as(u64, (arg1[37])) << 40); + const x12 = (@as(u64, (arg1[36])) << 32); + const x13 = (@as(u64, (arg1[35])) << 24); + const x14 = (@as(u64, (arg1[34])) << 16); + const x15 = (@as(u64, (arg1[33])) << 8); + const x16 = (arg1[32]); + const x17 = (@as(u64, (arg1[31])) << 56); + const x18 = (@as(u64, (arg1[30])) << 48); + const x19 = (@as(u64, (arg1[29])) << 40); + const x20 = (@as(u64, (arg1[28])) << 32); + const x21 = (@as(u64, (arg1[27])) << 24); + const x22 = (@as(u64, (arg1[26])) << 16); + const x23 = (@as(u64, (arg1[25])) << 8); + const x24 = (arg1[24]); + const x25 = (@as(u64, (arg1[23])) << 56); + const x26 = (@as(u64, (arg1[22])) << 48); + const x27 = (@as(u64, (arg1[21])) << 40); + const x28 = (@as(u64, (arg1[20])) << 32); + const x29 = (@as(u64, (arg1[19])) << 24); + const x30 = (@as(u64, (arg1[18])) << 16); + const x31 = (@as(u64, (arg1[17])) << 8); + const x32 = (arg1[16]); + const x33 = (@as(u64, (arg1[15])) << 56); + const x34 = (@as(u64, (arg1[14])) << 48); + const x35 = (@as(u64, (arg1[13])) << 40); + const x36 = (@as(u64, (arg1[12])) << 32); + const x37 = (@as(u64, (arg1[11])) << 24); + const x38 = (@as(u64, (arg1[10])) << 16); + const x39 = (@as(u64, (arg1[9])) << 8); + const x40 = (arg1[8]); + const x41 = (@as(u64, (arg1[7])) << 56); + const x42 = (@as(u64, (arg1[6])) << 48); + const x43 = (@as(u64, (arg1[5])) << 40); + const x44 = (@as(u64, (arg1[4])) << 32); + const x45 = (@as(u64, (arg1[3])) << 24); + const x46 = (@as(u64, (arg1[2])) << 16); + const x47 = (@as(u64, (arg1[1])) << 8); + const x48 = (arg1[0]); + const x49 = (x47 + @as(u64, x48)); + const x50 = (x46 + x49); + const x51 = (x45 + x50); + const x52 = (x44 + x51); + const x53 = (x43 + x52); + const x54 = (x42 + x53); + const x55 = (x41 + x54); + const x56 = (x39 + @as(u64, x40)); + const x57 = (x38 + x56); + const x58 = (x37 + x57); + const x59 = (x36 + x58); + const x60 = (x35 + x59); + const x61 = (x34 + x60); + const x62 = (x33 + x61); + const x63 = (x31 + @as(u64, x32)); + const x64 = (x30 + x63); + const x65 = (x29 + x64); + const x66 = (x28 + x65); + const x67 = (x27 + x66); + const x68 = (x26 + x67); + const x69 = (x25 + x68); + const x70 = (x23 + @as(u64, x24)); + const x71 = (x22 + x70); + const x72 = (x21 + x71); + const x73 = (x20 + x72); + const x74 = (x19 + x73); + const x75 = (x18 + x74); + const x76 = (x17 + x75); + const x77 = (x15 + @as(u64, x16)); + const x78 = (x14 + x77); + const x79 = (x13 + x78); + const x80 = (x12 + x79); + const x81 = (x11 + x80); + const x82 = (x10 + x81); + const x83 = (x9 + x82); + const x84 = (x7 + @as(u64, x8)); + const x85 = (x6 + x84); + const x86 = (x5 + x85); + const x87 = (x4 + x86); + const x88 = (x3 + x87); + const x89 = (x2 + x88); + const x90 = (x1 + x89); + out1[0] = x55; + out1[1] = x62; + out1[2] = x69; + out1[3] = x76; + out1[4] = x83; + out1[5] = x90; +} + +/// The function setOne returns the field element one in the Montgomery domain. +/// +/// Postconditions: +/// eval (from_montgomery out1) mod m = 1 mod m +/// 0 ≤ eval out1 < m +/// +pub fn setOne(out1: *MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + out1[0] = 0x1313e695333ad68d; + out1[1] = 0xa7e5f24db74f5885; + out1[2] = 0x389cb27e0bc8d220; + out1[3] = 0x0; + out1[4] = 0x0; + out1[5] = 0x0; +} + +/// The function msat returns the saturated representation of the prime modulus. +/// +/// Postconditions: +/// twos_complement_eval out1 = m +/// 0 ≤ eval out1 < m +/// +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn msat(out1: *[7]u64) void { + @setRuntimeSafety(mode == .Debug); + + out1[0] = 0xecec196accc52973; + out1[1] = 0x581a0db248b0a77a; + out1[2] = 0xc7634d81f4372ddf; + out1[3] = 0xffffffffffffffff; + out1[4] = 0xffffffffffffffff; + out1[5] = 0xffffffffffffffff; + out1[6] = 0x0; +} + +/// The function divstep computes a divstep. +/// +/// Preconditions: +/// 0 ≤ eval arg4 < m +/// 0 ≤ eval arg5 < m +/// Postconditions: +/// out1 = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then 1 - arg1 else 1 + arg1) +/// twos_complement_eval out2 = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then twos_complement_eval arg3 else twos_complement_eval arg2) +/// twos_complement_eval out3 = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then ⌊(twos_complement_eval arg3 - twos_complement_eval arg2) / 2⌋ else ⌊(twos_complement_eval arg3 + (twos_complement_eval arg3 mod 2) * twos_complement_eval arg2) / 2⌋) +/// eval (from_montgomery out4) mod m = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then (2 * eval (from_montgomery arg5)) mod m else (2 * eval (from_montgomery arg4)) mod m) +/// eval (from_montgomery out5) mod m = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then (eval (from_montgomery arg4) - eval (from_montgomery arg4)) mod m else (eval (from_montgomery arg5) + (twos_complement_eval arg3 mod 2) * eval (from_montgomery arg4)) mod m) +/// 0 ≤ eval out5 < m +/// 0 ≤ eval out5 < m +/// 0 ≤ eval out2 < m +/// 0 ≤ eval out3 < m +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0xffffffffffffffff] +/// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg4: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg5: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// out3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// out4: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// out5: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn divstep(out1: *u64, out2: *[7]u64, out3: *[7]u64, out4: *[6]u64, out5: *[6]u64, arg1: u64, arg2: [7]u64, arg3: [7]u64, arg4: [6]u64, arg5: [6]u64) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + addcarryxU64(&x1, &x2, 0x0, (~arg1), 0x1); + const x3 = (@truncate(u1, (x1 >> 63)) & @truncate(u1, ((arg3[0]) & 0x1))); + var x4: u64 = undefined; + var x5: u1 = undefined; + addcarryxU64(&x4, &x5, 0x0, (~arg1), 0x1); + var x6: u64 = undefined; + cmovznzU64(&x6, x3, arg1, x4); + var x7: u64 = undefined; + cmovznzU64(&x7, x3, (arg2[0]), (arg3[0])); + var x8: u64 = undefined; + cmovznzU64(&x8, x3, (arg2[1]), (arg3[1])); + var x9: u64 = undefined; + cmovznzU64(&x9, x3, (arg2[2]), (arg3[2])); + var x10: u64 = undefined; + cmovznzU64(&x10, x3, (arg2[3]), (arg3[3])); + var x11: u64 = undefined; + cmovznzU64(&x11, x3, (arg2[4]), (arg3[4])); + var x12: u64 = undefined; + cmovznzU64(&x12, x3, (arg2[5]), (arg3[5])); + var x13: u64 = undefined; + cmovznzU64(&x13, x3, (arg2[6]), (arg3[6])); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, 0x0, 0x1, (~(arg2[0]))); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, 0x0, (~(arg2[1]))); + var x18: u64 = undefined; + var x19: u1 = undefined; + addcarryxU64(&x18, &x19, x17, 0x0, (~(arg2[2]))); + var x20: u64 = undefined; + var x21: u1 = undefined; + addcarryxU64(&x20, &x21, x19, 0x0, (~(arg2[3]))); + var x22: u64 = undefined; + var x23: u1 = undefined; + addcarryxU64(&x22, &x23, x21, 0x0, (~(arg2[4]))); + var x24: u64 = undefined; + var x25: u1 = undefined; + addcarryxU64(&x24, &x25, x23, 0x0, (~(arg2[5]))); + var x26: u64 = undefined; + var x27: u1 = undefined; + addcarryxU64(&x26, &x27, x25, 0x0, (~(arg2[6]))); + var x28: u64 = undefined; + cmovznzU64(&x28, x3, (arg3[0]), x14); + var x29: u64 = undefined; + cmovznzU64(&x29, x3, (arg3[1]), x16); + var x30: u64 = undefined; + cmovznzU64(&x30, x3, (arg3[2]), x18); + var x31: u64 = undefined; + cmovznzU64(&x31, x3, (arg3[3]), x20); + var x32: u64 = undefined; + cmovznzU64(&x32, x3, (arg3[4]), x22); + var x33: u64 = undefined; + cmovznzU64(&x33, x3, (arg3[5]), x24); + var x34: u64 = undefined; + cmovznzU64(&x34, x3, (arg3[6]), x26); + var x35: u64 = undefined; + cmovznzU64(&x35, x3, (arg4[0]), (arg5[0])); + var x36: u64 = undefined; + cmovznzU64(&x36, x3, (arg4[1]), (arg5[1])); + var x37: u64 = undefined; + cmovznzU64(&x37, x3, (arg4[2]), (arg5[2])); + var x38: u64 = undefined; + cmovznzU64(&x38, x3, (arg4[3]), (arg5[3])); + var x39: u64 = undefined; + cmovznzU64(&x39, x3, (arg4[4]), (arg5[4])); + var x40: u64 = undefined; + cmovznzU64(&x40, x3, (arg4[5]), (arg5[5])); + var x41: u64 = undefined; + var x42: u1 = undefined; + addcarryxU64(&x41, &x42, 0x0, x35, x35); + var x43: u64 = undefined; + var x44: u1 = undefined; + addcarryxU64(&x43, &x44, x42, x36, x36); + var x45: u64 = undefined; + var x46: u1 = undefined; + addcarryxU64(&x45, &x46, x44, x37, x37); + var x47: u64 = undefined; + var x48: u1 = undefined; + addcarryxU64(&x47, &x48, x46, x38, x38); + var x49: u64 = undefined; + var x50: u1 = undefined; + addcarryxU64(&x49, &x50, x48, x39, x39); + var x51: u64 = undefined; + var x52: u1 = undefined; + addcarryxU64(&x51, &x52, x50, x40, x40); + var x53: u64 = undefined; + var x54: u1 = undefined; + subborrowxU64(&x53, &x54, 0x0, x41, 0xecec196accc52973); + var x55: u64 = undefined; + var x56: u1 = undefined; + subborrowxU64(&x55, &x56, x54, x43, 0x581a0db248b0a77a); + var x57: u64 = undefined; + var x58: u1 = undefined; + subborrowxU64(&x57, &x58, x56, x45, 0xc7634d81f4372ddf); + var x59: u64 = undefined; + var x60: u1 = undefined; + subborrowxU64(&x59, &x60, x58, x47, 0xffffffffffffffff); + var x61: u64 = undefined; + var x62: u1 = undefined; + subborrowxU64(&x61, &x62, x60, x49, 0xffffffffffffffff); + var x63: u64 = undefined; + var x64: u1 = undefined; + subborrowxU64(&x63, &x64, x62, x51, 0xffffffffffffffff); + var x65: u64 = undefined; + var x66: u1 = undefined; + subborrowxU64(&x65, &x66, x64, @as(u64, x52), 0x0); + const x67 = (arg4[5]); + const x68 = (arg4[4]); + const x69 = (arg4[3]); + const x70 = (arg4[2]); + const x71 = (arg4[1]); + const x72 = (arg4[0]); + var x73: u64 = undefined; + var x74: u1 = undefined; + subborrowxU64(&x73, &x74, 0x0, 0x0, x72); + var x75: u64 = undefined; + var x76: u1 = undefined; + subborrowxU64(&x75, &x76, x74, 0x0, x71); + var x77: u64 = undefined; + var x78: u1 = undefined; + subborrowxU64(&x77, &x78, x76, 0x0, x70); + var x79: u64 = undefined; + var x80: u1 = undefined; + subborrowxU64(&x79, &x80, x78, 0x0, x69); + var x81: u64 = undefined; + var x82: u1 = undefined; + subborrowxU64(&x81, &x82, x80, 0x0, x68); + var x83: u64 = undefined; + var x84: u1 = undefined; + subborrowxU64(&x83, &x84, x82, 0x0, x67); + var x85: u64 = undefined; + cmovznzU64(&x85, x84, 0x0, 0xffffffffffffffff); + var x86: u64 = undefined; + var x87: u1 = undefined; + addcarryxU64(&x86, &x87, 0x0, x73, (x85 & 0xecec196accc52973)); + var x88: u64 = undefined; + var x89: u1 = undefined; + addcarryxU64(&x88, &x89, x87, x75, (x85 & 0x581a0db248b0a77a)); + var x90: u64 = undefined; + var x91: u1 = undefined; + addcarryxU64(&x90, &x91, x89, x77, (x85 & 0xc7634d81f4372ddf)); + var x92: u64 = undefined; + var x93: u1 = undefined; + addcarryxU64(&x92, &x93, x91, x79, x85); + var x94: u64 = undefined; + var x95: u1 = undefined; + addcarryxU64(&x94, &x95, x93, x81, x85); + var x96: u64 = undefined; + var x97: u1 = undefined; + addcarryxU64(&x96, &x97, x95, x83, x85); + var x98: u64 = undefined; + cmovznzU64(&x98, x3, (arg5[0]), x86); + var x99: u64 = undefined; + cmovznzU64(&x99, x3, (arg5[1]), x88); + var x100: u64 = undefined; + cmovznzU64(&x100, x3, (arg5[2]), x90); + var x101: u64 = undefined; + cmovznzU64(&x101, x3, (arg5[3]), x92); + var x102: u64 = undefined; + cmovznzU64(&x102, x3, (arg5[4]), x94); + var x103: u64 = undefined; + cmovznzU64(&x103, x3, (arg5[5]), x96); + const x104 = @truncate(u1, (x28 & 0x1)); + var x105: u64 = undefined; + cmovznzU64(&x105, x104, 0x0, x7); + var x106: u64 = undefined; + cmovznzU64(&x106, x104, 0x0, x8); + var x107: u64 = undefined; + cmovznzU64(&x107, x104, 0x0, x9); + var x108: u64 = undefined; + cmovznzU64(&x108, x104, 0x0, x10); + var x109: u64 = undefined; + cmovznzU64(&x109, x104, 0x0, x11); + var x110: u64 = undefined; + cmovznzU64(&x110, x104, 0x0, x12); + var x111: u64 = undefined; + cmovznzU64(&x111, x104, 0x0, x13); + var x112: u64 = undefined; + var x113: u1 = undefined; + addcarryxU64(&x112, &x113, 0x0, x28, x105); + var x114: u64 = undefined; + var x115: u1 = undefined; + addcarryxU64(&x114, &x115, x113, x29, x106); + var x116: u64 = undefined; + var x117: u1 = undefined; + addcarryxU64(&x116, &x117, x115, x30, x107); + var x118: u64 = undefined; + var x119: u1 = undefined; + addcarryxU64(&x118, &x119, x117, x31, x108); + var x120: u64 = undefined; + var x121: u1 = undefined; + addcarryxU64(&x120, &x121, x119, x32, x109); + var x122: u64 = undefined; + var x123: u1 = undefined; + addcarryxU64(&x122, &x123, x121, x33, x110); + var x124: u64 = undefined; + var x125: u1 = undefined; + addcarryxU64(&x124, &x125, x123, x34, x111); + var x126: u64 = undefined; + cmovznzU64(&x126, x104, 0x0, x35); + var x127: u64 = undefined; + cmovznzU64(&x127, x104, 0x0, x36); + var x128: u64 = undefined; + cmovznzU64(&x128, x104, 0x0, x37); + var x129: u64 = undefined; + cmovznzU64(&x129, x104, 0x0, x38); + var x130: u64 = undefined; + cmovznzU64(&x130, x104, 0x0, x39); + var x131: u64 = undefined; + cmovznzU64(&x131, x104, 0x0, x40); + var x132: u64 = undefined; + var x133: u1 = undefined; + addcarryxU64(&x132, &x133, 0x0, x98, x126); + var x134: u64 = undefined; + var x135: u1 = undefined; + addcarryxU64(&x134, &x135, x133, x99, x127); + var x136: u64 = undefined; + var x137: u1 = undefined; + addcarryxU64(&x136, &x137, x135, x100, x128); + var x138: u64 = undefined; + var x139: u1 = undefined; + addcarryxU64(&x138, &x139, x137, x101, x129); + var x140: u64 = undefined; + var x141: u1 = undefined; + addcarryxU64(&x140, &x141, x139, x102, x130); + var x142: u64 = undefined; + var x143: u1 = undefined; + addcarryxU64(&x142, &x143, x141, x103, x131); + var x144: u64 = undefined; + var x145: u1 = undefined; + subborrowxU64(&x144, &x145, 0x0, x132, 0xecec196accc52973); + var x146: u64 = undefined; + var x147: u1 = undefined; + subborrowxU64(&x146, &x147, x145, x134, 0x581a0db248b0a77a); + var x148: u64 = undefined; + var x149: u1 = undefined; + subborrowxU64(&x148, &x149, x147, x136, 0xc7634d81f4372ddf); + var x150: u64 = undefined; + var x151: u1 = undefined; + subborrowxU64(&x150, &x151, x149, x138, 0xffffffffffffffff); + var x152: u64 = undefined; + var x153: u1 = undefined; + subborrowxU64(&x152, &x153, x151, x140, 0xffffffffffffffff); + var x154: u64 = undefined; + var x155: u1 = undefined; + subborrowxU64(&x154, &x155, x153, x142, 0xffffffffffffffff); + var x156: u64 = undefined; + var x157: u1 = undefined; + subborrowxU64(&x156, &x157, x155, @as(u64, x143), 0x0); + var x158: u64 = undefined; + var x159: u1 = undefined; + addcarryxU64(&x158, &x159, 0x0, x6, 0x1); + const x160 = ((x112 >> 1) | ((x114 << 63) & 0xffffffffffffffff)); + const x161 = ((x114 >> 1) | ((x116 << 63) & 0xffffffffffffffff)); + const x162 = ((x116 >> 1) | ((x118 << 63) & 0xffffffffffffffff)); + const x163 = ((x118 >> 1) | ((x120 << 63) & 0xffffffffffffffff)); + const x164 = ((x120 >> 1) | ((x122 << 63) & 0xffffffffffffffff)); + const x165 = ((x122 >> 1) | ((x124 << 63) & 0xffffffffffffffff)); + const x166 = ((x124 & 0x8000000000000000) | (x124 >> 1)); + var x167: u64 = undefined; + cmovznzU64(&x167, x66, x53, x41); + var x168: u64 = undefined; + cmovznzU64(&x168, x66, x55, x43); + var x169: u64 = undefined; + cmovznzU64(&x169, x66, x57, x45); + var x170: u64 = undefined; + cmovznzU64(&x170, x66, x59, x47); + var x171: u64 = undefined; + cmovznzU64(&x171, x66, x61, x49); + var x172: u64 = undefined; + cmovznzU64(&x172, x66, x63, x51); + var x173: u64 = undefined; + cmovznzU64(&x173, x157, x144, x132); + var x174: u64 = undefined; + cmovznzU64(&x174, x157, x146, x134); + var x175: u64 = undefined; + cmovznzU64(&x175, x157, x148, x136); + var x176: u64 = undefined; + cmovznzU64(&x176, x157, x150, x138); + var x177: u64 = undefined; + cmovznzU64(&x177, x157, x152, x140); + var x178: u64 = undefined; + cmovznzU64(&x178, x157, x154, x142); + out1.* = x158; + out2[0] = x7; + out2[1] = x8; + out2[2] = x9; + out2[3] = x10; + out2[4] = x11; + out2[5] = x12; + out2[6] = x13; + out3[0] = x160; + out3[1] = x161; + out3[2] = x162; + out3[3] = x163; + out3[4] = x164; + out3[5] = x165; + out3[6] = x166; + out4[0] = x167; + out4[1] = x168; + out4[2] = x169; + out4[3] = x170; + out4[4] = x171; + out4[5] = x172; + out5[0] = x173; + out5[1] = x174; + out5[2] = x175; + out5[3] = x176; + out5[4] = x177; + out5[5] = x178; +} + +/// The function divstepPrecomp returns the precomputed value for Bernstein-Yang-inversion (in montgomery form). +/// +/// Postconditions: +/// eval (from_montgomery out1) = ⌊(m - 1) / 2⌋^(if ⌊log2 m⌋ + 1 < 46 then ⌊(49 * (⌊log2 m⌋ + 1) + 80) / 17⌋ else ⌊(49 * (⌊log2 m⌋ + 1) + 57) / 17⌋) +/// 0 ≤ eval out1 < m +/// +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn divstepPrecomp(out1: *[6]u64) void { + @setRuntimeSafety(mode == .Debug); + + out1[0] = 0x49589ae0e6045b6a; + out1[1] = 0x3c9a5352870040ed; + out1[2] = 0xdacb097e977dc242; + out1[3] = 0xb5ab30a6d1ecbe36; + out1[4] = 0x97d7a1081f959973; + out1[5] = 0x2ba012f8d27192bc; +} diff --git a/lib/std/crypto/pcurves/p384/scalar.zig b/lib/std/crypto/pcurves/p384/scalar.zig new file mode 100644 index 0000000000..0c6fcea17c --- /dev/null +++ b/lib/std/crypto/pcurves/p384/scalar.zig @@ -0,0 +1,202 @@ +const std = @import("std"); +const common = @import("../common.zig"); +const crypto = std.crypto; +const debug = std.debug; +const math = std.math; +const mem = std.mem; + +const Field = common.Field; + +const NonCanonicalError = std.crypto.errors.NonCanonicalError; +const NotSquareError = std.crypto.errors.NotSquareError; + +/// Number of bytes required to encode a scalar. +pub const encoded_length = 48; + +/// A compressed scalar, in canonical form. +pub const CompressedScalar = [encoded_length]u8; + +const Fe = Field(.{ + .fiat = @import("p384_scalar_64.zig"), + .field_order = 39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643, + .field_bits = 384, + .saturated_bits = 384, + .encoded_length = encoded_length, +}); + +/// Reject a scalar whose encoding is not canonical. +pub fn rejectNonCanonical(s: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!void { + return Fe.rejectNonCanonical(s, endian); +} + +/// Reduce a 64-bytes scalar to the field size. +pub fn reduce64(s: [64]u8, endian: std.builtin.Endian) CompressedScalar { + return ScalarDouble.fromBytes64(s, endian).toBytes(endian); +} + +/// Return a*b (mod L) +pub fn mul(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!CompressedScalar { + return (try Scalar.fromBytes(a, endian)).mul(try Scalar.fromBytes(b, endian)).toBytes(endian); +} + +/// Return a*b+c (mod L) +pub fn mulAdd(a: CompressedScalar, b: CompressedScalar, c: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!CompressedScalar { + return (try Scalar.fromBytes(a, endian)).mul(try Scalar.fromBytes(b, endian)).add(try Scalar.fromBytes(c, endian)).toBytes(endian); +} + +/// Return a+b (mod L) +pub fn add(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!CompressedScalar { + return (try Scalar.fromBytes(a, endian)).add(try Scalar.fromBytes(b, endian)).toBytes(endian); +} + +/// Return -s (mod L) +pub fn neg(s: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!CompressedScalar { + return (try Scalar.fromBytes(s, endian)).neg().toBytes(endian); +} + +/// Return (a-b) (mod L) +pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!CompressedScalar { + return (try Scalar.fromBytes(a, endian)).sub(try Scalar.fromBytes(b.endian)).toBytes(endian); +} + +/// Return a random scalar +pub fn random(endian: std.builtin.Endian) CompressedScalar { + return Scalar.random().toBytes(endian); +} + +/// A scalar in unpacked representation. +pub const Scalar = struct { + fe: Fe, + + /// Zero. + pub const zero = Scalar{ .fe = Fe.zero }; + + /// One. + pub const one = Scalar{ .fe = Fe.one }; + + /// Unpack a serialized representation of a scalar. + pub fn fromBytes(s: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!Scalar { + return Scalar{ .fe = try Fe.fromBytes(s, endian) }; + } + + /// Reduce a 512 bit input to the field size. + pub fn fromBytes64(s: [64]u8, endian: std.builtin.Endian) Scalar { + const t = ScalarDouble.fromBytes(512, s, endian); + return t.reduce(512); + } + + /// Pack a scalar into bytes. + pub fn toBytes(n: Scalar, endian: std.builtin.Endian) CompressedScalar { + return n.fe.toBytes(endian); + } + + /// Return true if the scalar is zero.. + pub fn isZero(n: Scalar) bool { + return n.fe.isZero(); + } + + /// Return true if a and b are equivalent. + pub fn equivalent(a: Scalar, b: Scalar) bool { + return a.fe.equivalent(b.fe); + } + + /// Compute x+y (mod L) + pub fn add(x: Scalar, y: Scalar) Scalar { + return Scalar{ .fe = x.fe.add(y.fe) }; + } + + /// Compute x-y (mod L) + pub fn sub(x: Scalar, y: Scalar) Scalar { + return Scalar{ .fe = x.fe.sub(y.fe) }; + } + + /// Compute 2n (mod L) + pub fn dbl(n: Scalar) Scalar { + return Scalar{ .fe = n.fe.dbl() }; + } + + /// Compute x*y (mod L) + pub fn mul(x: Scalar, y: Scalar) Scalar { + return Scalar{ .fe = x.fe.mul(y.fe) }; + } + + /// Compute x^2 (mod L) + pub fn sq(n: Scalar) Scalar { + return Scalar{ .fe = n.fe.sq() }; + } + + /// Compute x^n (mod L) + pub fn pow(a: Scalar, comptime T: type, comptime n: T) Scalar { + return Scalar{ .fe = a.fe.pow(n) }; + } + + /// Compute -x (mod L) + pub fn neg(n: Scalar) Scalar { + return Scalar{ .fe = n.fe.neg() }; + } + + /// Compute x^-1 (mod L) + pub fn invert(n: Scalar) Scalar { + return Scalar{ .fe = n.fe.invert() }; + } + + /// Return true if n is a quadratic residue mod L. + pub fn isSquare(n: Scalar) Scalar { + return n.fe.isSquare(); + } + + /// Return the square root of L, or NotSquare if there isn't any solutions. + pub fn sqrt(n: Scalar) NotSquareError!Scalar { + return Scalar{ .fe = try n.fe.sqrt() }; + } + + /// Return a random scalar < L. + pub fn random() Scalar { + var s: [64]u8 = undefined; + while (true) { + crypto.random.bytes(&s); + const n = Scalar.fromBytes64(s, .Little); + if (!n.isZero()) { + return n; + } + } + } +}; + +const ScalarDouble = struct { + x1: Fe, + x2: Fe, + + fn fromBytes(comptime bits: usize, s_: [bits / 8]u8, endian: std.builtin.Endian) ScalarDouble { + debug.assert(bits > 0 and bits <= 512 and bits >= Fe.saturated_bits and bits <= Fe.saturated_bits * 2); + + var s = s_; + if (endian == .Big) { + for (s_) |x, i| s[s.len - 1 - i] = x; + } + var t = ScalarDouble{ .x1 = undefined, .x2 = Fe.zero }; + { + var b = [_]u8{0} ** encoded_length; + const len = math.min(s.len, 32); + mem.copy(u8, b[0..len], s[0..len]); + t.x1 = Fe.fromBytes(b, .Little) catch unreachable; + } + if (s_.len >= 32) { + var b = [_]u8{0} ** encoded_length; + const len = math.min(s.len - 32, 32); + mem.copy(u8, b[0..len], s[32..][0..len]); + t.x2 = Fe.fromBytes(b, .Little) catch unreachable; + } + return t; + } + + fn reduce(expanded: ScalarDouble, comptime bits: usize) Scalar { + debug.assert(bits > 0 and bits <= Fe.saturated_bits * 3 and bits <= 512); + var fe = expanded.x1; + if (bits >= 256) { + const st1 = Fe.fromInt(1 << 256) catch unreachable; + fe = fe.add(expanded.x2.mul(st1)); + } + return Scalar{ .fe = fe }; + } +}; diff --git a/lib/std/crypto/pcurves/tests.zig b/lib/std/crypto/pcurves/tests/p256.zig similarity index 99% rename from lib/std/crypto/pcurves/tests.zig rename to lib/std/crypto/pcurves/tests/p256.zig index bc60b8c257..ce21ac8ba0 100644 --- a/lib/std/crypto/pcurves/tests.zig +++ b/lib/std/crypto/pcurves/tests/p256.zig @@ -2,7 +2,7 @@ const std = @import("std"); const fmt = std.fmt; const testing = std.testing; -const P256 = @import("p256.zig").P256; +const P256 = @import("../p256.zig").P256; test "p256 ECDH key exchange" { const dha = P256.scalar.random(.Little); diff --git a/lib/std/crypto/pcurves/tests/p384.zig b/lib/std/crypto/pcurves/tests/p384.zig new file mode 100644 index 0000000000..54c5148ae0 --- /dev/null +++ b/lib/std/crypto/pcurves/tests/p384.zig @@ -0,0 +1,136 @@ +const std = @import("std"); +const fmt = std.fmt; +const testing = std.testing; + +const P384 = @import("../p384.zig").P384; + +test "p384 ECDH key exchange" { + const dha = P384.scalar.random(.Little); + const dhb = P384.scalar.random(.Little); + const dhA = try P384.basePoint.mul(dha, .Little); + const dhB = try P384.basePoint.mul(dhb, .Little); + const shareda = try dhA.mul(dhb, .Little); + const sharedb = try dhB.mul(dha, .Little); + try testing.expect(shareda.equivalent(sharedb)); +} + +test "p384 point from affine coordinates" { + const xh = "aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7"; + const yh = "3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f"; + var xs: [48]u8 = undefined; + _ = try fmt.hexToBytes(&xs, xh); + var ys: [48]u8 = undefined; + _ = try fmt.hexToBytes(&ys, yh); + var p = try P384.fromSerializedAffineCoordinates(xs, ys, .Big); + try testing.expect(p.equivalent(P384.basePoint)); +} + +test "p384 test vectors" { + const expected = [_][]const u8{ + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", + "08D999057BA3D2D969260045C55B97F089025959A6F434D651D207D19FB96E9E4FE0E86EBE0E64F85B96A9C75295DF61", + "077A41D4606FFA1464793C7E5FDC7D98CB9D3910202DCD06BEA4F240D3566DA6B408BBAE5026580D02D7E5C70500C831", + "138251CD52AC9298C1C8AAD977321DEB97E709BD0B4CA0ACA55DC8AD51DCFC9D1589A1597E3A5120E1EFD631C63E1835", + "11DE24A2C251C777573CAC5EA025E467F208E51DBFF98FC54F6661CBE56583B037882F4A1CA297E60ABCDBC3836D84BC", + "627BE1ACD064D2B2226FE0D26F2D15D3C33EBCBB7F0F5DA51CBD41F26257383021317D7202FF30E50937F0854E35C5DF", + "283C1D7365CE4788F29F8EBF234EDFFEAD6FE997FBEA5FFA2D58CC9DFA7B1C508B05526F55B9EBB2040F05B48FB6D0E1", + "1692778EA596E0BE75114297A6FA383445BF227FBE58190A900C3C73256F11FB5A3258D6F403D5ECE6E9B269D822C87D", + "8F0A39A4049BCB3EF1BF29B8B025B78F2216F7291E6FD3BAC6CB1EE285FB6E21C388528BFEE2B9535C55E4461079118B", + "A669C5563BD67EEC678D29D6EF4FDE864F372D90B79B9E88931D5C29291238CCED8E85AB507BF91AA9CB2D13186658FB", + }; + var p = P384.identityElement; + for (expected) |xh| { + const x = p.affineCoordinates().x; + p = p.add(P384.basePoint); + var xs: [48]u8 = undefined; + _ = try fmt.hexToBytes(&xs, xh); + try testing.expectEqualSlices(u8, &x.toBytes(.Big), &xs); + } +} + +test "p384 test vectors - doubling" { + const expected = [_][]const u8{ + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", + "08D999057BA3D2D969260045C55B97F089025959A6F434D651D207D19FB96E9E4FE0E86EBE0E64F85B96A9C75295DF61", + "138251CD52AC9298C1C8AAD977321DEB97E709BD0B4CA0ACA55DC8AD51DCFC9D1589A1597E3A5120E1EFD631C63E1835", + "1692778EA596E0BE75114297A6FA383445BF227FBE58190A900C3C73256F11FB5A3258D6F403D5ECE6E9B269D822C87D", + }; + var p = P384.basePoint; + for (expected) |xh| { + const x = p.affineCoordinates().x; + p = p.dbl(); + var xs: [48]u8 = undefined; + _ = try fmt.hexToBytes(&xs, xh); + try testing.expectEqualSlices(u8, &x.toBytes(.Big), &xs); + } +} + +test "p384 compressed sec1 encoding/decoding" { + const p = P384.random(); + const s0 = p.toUncompressedSec1(); + const s = p.toCompressedSec1(); + try testing.expectEqualSlices(u8, s0[1..49], s[1..49]); + const q = try P384.fromSec1(&s); + try testing.expect(p.equivalent(q)); +} + +test "p384 uncompressed sec1 encoding/decoding" { + const p = P384.random(); + const s = p.toUncompressedSec1(); + const q = try P384.fromSec1(&s); + try testing.expect(p.equivalent(q)); +} + +test "p384 public key is the neutral element" { + const n = P384.scalar.Scalar.zero.toBytes(.Little); + const p = P384.random(); + try testing.expectError(error.IdentityElement, p.mul(n, .Little)); +} + +test "p384 public key is the neutral element (public verification)" { + const n = P384.scalar.Scalar.zero.toBytes(.Little); + const p = P384.random(); + try testing.expectError(error.IdentityElement, p.mulPublic(n, .Little)); +} + +test "p384 field element non-canonical encoding" { + const s = [_]u8{0xff} ** 48; + try testing.expectError(error.NonCanonical, P384.Fe.fromBytes(s, .Little)); +} + +test "p384 neutral element decoding" { + try testing.expectError(error.InvalidEncoding, P384.fromAffineCoordinates(.{ .x = P384.Fe.zero, .y = P384.Fe.zero })); + const p = try P384.fromAffineCoordinates(.{ .x = P384.Fe.zero, .y = P384.Fe.one }); + try testing.expectError(error.IdentityElement, p.rejectIdentity()); +} + +test "p384 double base multiplication" { + const p1 = P384.basePoint; + const p2 = P384.basePoint.dbl(); + const s1 = [_]u8{0x01} ** 48; + const s2 = [_]u8{0x02} ** 48; + const pr1 = try P384.mulDoubleBasePublic(p1, s1, p2, s2, .Little); + const pr2 = (try p1.mul(s1, .Little)).add(try p2.mul(s2, .Little)); + try testing.expect(pr1.equivalent(pr2)); +} + +test "p384 scalar inverse" { + const expected = "a3cc705f33b5679a66e76ce66e68055c927c5dba531b2837b18fe86119511091b54d733f26b2e7a0f6fa2e7ea21ca806"; + var out: [48]u8 = undefined; + _ = try std.fmt.hexToBytes(&out, expected); + + const scalar = try P384.scalar.Scalar.fromBytes(.{ + 0x94, 0xa1, 0xbb, 0xb1, 0x4b, 0x90, 0x6a, 0x61, 0xa2, 0x80, 0xf2, 0x45, 0xf9, 0xe9, 0x3c, 0x7f, + 0x3b, 0x4a, 0x62, 0x47, 0x82, 0x4f, 0x5d, 0x33, 0xb9, 0x67, 0x07, 0x87, 0x64, 0x2a, 0x68, 0xde, + 0x38, 0x36, 0xe8, 0x0f, 0xa2, 0x84, 0x6b, 0x4e, 0xf3, 0x9a, 0x02, 0x31, 0x24, 0x41, 0x22, 0xca, + }, .Big); + const inverse = scalar.invert(); + const inverse2 = inverse.invert(); + try testing.expectEqualSlices(u8, &out, &inverse.toBytes(.Big)); + try testing.expect(inverse2.equivalent(scalar)); + + const sq = scalar.sq(); + const sqr = try sq.sqrt(); + try testing.expect(sqr.equivalent(scalar)); +} From d410693dadfe791e616e78239fa0cec707b95cfa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 May 2022 00:19:23 -0700 Subject: [PATCH 1687/2031] LLVM: elide some loads when lowering Generally, the load instruction may need to make a copy of an isByRef=true value, such as in the case of the following code: ```zig pub fn swap(comptime T: type, a: *T, b: *T) void { const tmp = a.*; a.* = b.*; b.* = tmp; } ``` However, it only needs to do so if there are any instructions which can possibly write to memory. When calling functions with isByRef=true parameters, the AIR code that is generated looks like loads followed directly by call. This allows for a peephole optimization when lowering loads: if the load instruction operates on an isByRef=true type and dies before any side effects occur, then we can safely lower the load as a no-op that returns its operand. This is one out of three changes I intend to make to address #11498. However I will put these changes in separate branches and merge them separately so that we can have three independent points on the perf charts. --- src/Liveness.zig | 396 +++++++++++++++++++++++++++++++++++++++++++ src/codegen/llvm.zig | 32 +++- 2 files changed, 423 insertions(+), 5 deletions(-) diff --git a/src/Liveness.zig b/src/Liveness.zig index b37e220086..b4576c0f18 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -112,6 +112,402 @@ pub fn clearOperandDeath(l: Liveness, inst: Air.Inst.Index, operand: OperandInt) l.tomb_bits[usize_index] &= ~mask; } +const OperandCategory = enum { + /// The operand lives on, but this instruction cannot possibly mutate memory. + none, + /// The operand lives on and this instruction can mutate memory. + write, + /// The operand dies at this instruction. + tomb, + /// The operand lives on, and this instruction is noreturn. + noret, + /// This instruction is too complicated for analysis, no information is available. + complex, +}; + +/// Given an instruction that we are examining, and an operand that we are looking for, +/// returns a classification. +pub fn categorizeOperand( + l: Liveness, + air: Air, + inst: Air.Inst.Index, + operand: Air.Inst.Index, +) OperandCategory { + const air_tags = air.instructions.items(.tag); + const air_datas = air.instructions.items(.data); + const operand_ref = Air.indexToRef(operand); + switch (air_tags[inst]) { + .add, + .addwrap, + .add_sat, + .sub, + .subwrap, + .sub_sat, + .mul, + .mulwrap, + .mul_sat, + .div_float, + .div_trunc, + .div_floor, + .div_exact, + .rem, + .mod, + .bit_and, + .bit_or, + .xor, + .cmp_lt, + .cmp_lte, + .cmp_eq, + .cmp_gte, + .cmp_gt, + .cmp_neq, + .bool_and, + .bool_or, + .array_elem_val, + .slice_elem_val, + .ptr_elem_val, + .shl, + .shl_exact, + .shl_sat, + .shr, + .shr_exact, + .min, + .max, + => { + const o = air_datas[inst].bin_op; + if (o.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); + if (o.rhs == operand_ref) return matchOperandSmallIndex(l, inst, 1, .none); + return .none; + }, + + .store, + .atomic_store_unordered, + .atomic_store_monotonic, + .atomic_store_release, + .atomic_store_seq_cst, + .set_union_tag, + => { + const o = air_datas[inst].bin_op; + if (o.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); + if (o.rhs == operand_ref) return matchOperandSmallIndex(l, inst, 1, .write); + return .write; + }, + + .arg, + .alloc, + .ret_ptr, + .constant, + .const_ty, + .breakpoint, + .dbg_stmt, + .dbg_inline_begin, + .dbg_inline_end, + .dbg_block_begin, + .dbg_block_end, + .unreach, + .ret_addr, + .frame_addr, + .wasm_memory_size, + .err_return_trace, + => return .none, + + .fence => return .write, + + .not, + .bitcast, + .load, + .fpext, + .fptrunc, + .intcast, + .trunc, + .optional_payload, + .optional_payload_ptr, + .wrap_optional, + .unwrap_errunion_payload, + .unwrap_errunion_err, + .unwrap_errunion_payload_ptr, + .unwrap_errunion_err_ptr, + .wrap_errunion_payload, + .wrap_errunion_err, + .slice_ptr, + .slice_len, + .ptr_slice_len_ptr, + .ptr_slice_ptr_ptr, + .struct_field_ptr_index_0, + .struct_field_ptr_index_1, + .struct_field_ptr_index_2, + .struct_field_ptr_index_3, + .array_to_slice, + .float_to_int, + .int_to_float, + .get_union_tag, + .clz, + .ctz, + .popcount, + .byte_swap, + .bit_reverse, + .splat, + => { + const o = air_datas[inst].ty_op; + if (o.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); + return .none; + }, + + .optional_payload_ptr_set, + .errunion_payload_ptr_set, + => { + const o = air_datas[inst].ty_op; + if (o.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); + return .write; + }, + + .is_null, + .is_non_null, + .is_null_ptr, + .is_non_null_ptr, + .is_err, + .is_non_err, + .is_err_ptr, + .is_non_err_ptr, + .ptrtoint, + .bool_to_int, + .tag_name, + .error_name, + .sqrt, + .sin, + .cos, + .tan, + .exp, + .exp2, + .log, + .log2, + .log10, + .fabs, + .floor, + .ceil, + .round, + .trunc_float, + .cmp_lt_errors_len, + => { + const o = air_datas[inst].un_op; + if (o == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); + return .none; + }, + + .ret, + .ret_load, + => { + const o = air_datas[inst].un_op; + if (o == operand_ref) return matchOperandSmallIndex(l, inst, 0, .noret); + return .noret; + }, + + .set_err_return_trace => { + const o = air_datas[inst].un_op; + if (o == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); + return .write; + }, + + .add_with_overflow, + .sub_with_overflow, + .mul_with_overflow, + .shl_with_overflow, + .ptr_add, + .ptr_sub, + .ptr_elem_ptr, + .slice_elem_ptr, + .slice, + => { + const ty_pl = air_datas[inst].ty_pl; + const extra = air.extraData(Air.Bin, ty_pl.payload).data; + if (extra.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); + if (extra.rhs == operand_ref) return matchOperandSmallIndex(l, inst, 1, .none); + return .none; + }, + + .dbg_var_ptr, + .dbg_var_val, + => { + const o = air_datas[inst].pl_op.operand; + if (o == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); + return .none; + }, + + .prefetch => { + const prefetch = air_datas[inst].prefetch; + if (prefetch.ptr == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); + return .none; + }, + + .call, .call_always_tail, .call_never_tail, .call_never_inline => { + const inst_data = air_datas[inst].pl_op; + const callee = inst_data.operand; + const extra = air.extraData(Air.Call, inst_data.payload); + const args = @ptrCast([]const Air.Inst.Ref, air.extra[extra.end..][0..extra.data.args_len]); + if (args.len + 1 <= bpi - 1) { + if (callee == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); + for (args) |arg, i| { + if (arg == operand_ref) return matchOperandSmallIndex(l, inst, @intCast(OperandInt, i + 1), .write); + } + return .write; + } + var bt = l.iterateBigTomb(inst); + if (bt.feed()) { + if (callee == operand_ref) return .tomb; + } else { + if (callee == operand_ref) return .write; + } + for (args) |arg| { + if (bt.feed()) { + if (arg == operand_ref) return .tomb; + } else { + if (arg == operand_ref) return .write; + } + } + return .write; + }, + .select => { + const pl_op = air_datas[inst].pl_op; + const extra = air.extraData(Air.Bin, pl_op.payload).data; + if (pl_op.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); + if (extra.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 1, .none); + if (extra.rhs == operand_ref) return matchOperandSmallIndex(l, inst, 2, .none); + return .none; + }, + .shuffle => { + const extra = air.extraData(Air.Shuffle, air_datas[inst].ty_pl.payload).data; + if (extra.a == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); + if (extra.b == operand_ref) return matchOperandSmallIndex(l, inst, 1, .none); + return .none; + }, + .reduce => { + const reduce = air_datas[inst].reduce; + if (reduce.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); + return .none; + }, + .cmp_vector => { + const extra = air.extraData(Air.VectorCmp, air_datas[inst].ty_pl.payload).data; + if (extra.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); + if (extra.rhs == operand_ref) return matchOperandSmallIndex(l, inst, 1, .none); + return .none; + }, + .aggregate_init => { + const ty_pl = air_datas[inst].ty_pl; + const aggregate_ty = air.getRefType(ty_pl.ty); + const len = @intCast(usize, aggregate_ty.arrayLen()); + const elements = @ptrCast([]const Air.Inst.Ref, air.extra[ty_pl.payload..][0..len]); + + if (elements.len <= bpi - 1) { + for (elements) |elem, i| { + if (elem == operand_ref) return matchOperandSmallIndex(l, inst, @intCast(OperandInt, i), .none); + } + return .none; + } + + var bt = l.iterateBigTomb(inst); + for (elements) |elem| { + if (bt.feed()) { + if (elem == operand_ref) return .tomb; + } else { + if (elem == operand_ref) return .write; + } + } + return .write; + }, + .union_init => { + const extra = air.extraData(Air.UnionInit, air_datas[inst].ty_pl.payload).data; + if (extra.init == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); + return .none; + }, + .struct_field_ptr, .struct_field_val => { + const extra = air.extraData(Air.StructField, air_datas[inst].ty_pl.payload).data; + if (extra.struct_operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); + return .none; + }, + .field_parent_ptr => { + const extra = air.extraData(Air.FieldParentPtr, air_datas[inst].ty_pl.payload).data; + if (extra.field_ptr == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); + return .none; + }, + .cmpxchg_strong, .cmpxchg_weak => { + const extra = air.extraData(Air.Cmpxchg, air_datas[inst].ty_pl.payload).data; + if (extra.ptr == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); + if (extra.expected_value == operand_ref) return matchOperandSmallIndex(l, inst, 1, .write); + if (extra.new_value == operand_ref) return matchOperandSmallIndex(l, inst, 2, .write); + return .write; + }, + .mul_add => { + const pl_op = air_datas[inst].pl_op; + const extra = air.extraData(Air.Bin, pl_op.payload).data; + if (extra.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); + if (extra.rhs == operand_ref) return matchOperandSmallIndex(l, inst, 1, .none); + if (pl_op.operand == operand_ref) return matchOperandSmallIndex(l, inst, 2, .none); + return .none; + }, + .atomic_load => { + const ptr = air_datas[inst].atomic_load.ptr; + if (ptr == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); + return .none; + }, + .atomic_rmw => { + const pl_op = air_datas[inst].pl_op; + const extra = air.extraData(Air.AtomicRmw, pl_op.payload).data; + if (pl_op.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); + if (extra.operand == operand_ref) return matchOperandSmallIndex(l, inst, 1, .write); + return .write; + }, + .memset, + .memcpy, + => { + const pl_op = air_datas[inst].pl_op; + const extra = air.extraData(Air.Bin, pl_op.payload).data; + if (pl_op.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); + if (extra.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 1, .write); + if (extra.rhs == operand_ref) return matchOperandSmallIndex(l, inst, 2, .write); + return .write; + }, + + .br => { + const br = air_datas[inst].br; + if (br.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .noret); + return .noret; + }, + .assembly => { + return .complex; + }, + .block => { + return .complex; + }, + .loop => { + return .complex; + }, + .cond_br => { + return .complex; + }, + .switch_br => { + return .complex; + }, + .wasm_memory_grow => { + const pl_op = air_datas[inst].pl_op; + if (pl_op.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); + return .none; + }, + } +} + +fn matchOperandSmallIndex( + l: Liveness, + inst: Air.Inst.Index, + operand: OperandInt, + default: OperandCategory, +) OperandCategory { + if (operandDies(l, inst, operand)) { + return .tomb; + } else { + return default; + } +} + /// Higher level API. pub const CondBrSlices = struct { then_deaths: []const Air.Inst.Index, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 8cfa833cdd..cca83eca9b 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3885,7 +3885,7 @@ pub const FuncGen = struct { fn genBody(self: *FuncGen, body: []const Air.Inst.Index) Error!void { const air_tags = self.air.instructions.items(.tag); - for (body) |inst| { + for (body) |inst, i| { const opt_value: ?*const llvm.Value = switch (air_tags[inst]) { // zig fmt: off .add => try self.airAdd(inst), @@ -3976,7 +3976,7 @@ pub const FuncGen = struct { .fptrunc => try self.airFptrunc(inst), .fpext => try self.airFpext(inst), .ptrtoint => try self.airPtrToInt(inst), - .load => try self.airLoad(inst), + .load => try self.airLoad(inst, body, i + 1), .loop => try self.airLoop(inst), .not => try self.airNot(inst), .ret => try self.airRet(inst), @@ -6982,11 +6982,33 @@ pub const FuncGen = struct { return null; } - fn airLoad(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + fn airLoad( + self: *FuncGen, + inst: Air.Inst.Index, + body: []const Air.Inst.Index, + body_i: usize, + ) !?*const llvm.Value { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const ptr_ty = self.air.typeOf(ty_op.operand); - if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) - return null; + elide: { + const ptr_info = ptr_ty.ptrInfo().data; + if (ptr_info.@"volatile") break :elide; + if (self.liveness.isUnused(inst)) return null; + if (!isByRef(ptr_info.pointee_type)) break :elide; + + // It would be valid to fall back to the code below here that simply calls + // load(). However, as an optimization, we want to avoid unnecessary copies + // of isByRef=true types. Here, we scan forward in the current block, + // looking to see if this load dies before any side effects occur. + // In such case, we can safely return the operand without making a copy. + for (body[body_i..]) |body_inst| { + switch (self.liveness.categorizeOperand(self.air, body_inst, inst)) { + .none => continue, + .write, .noret, .complex => break :elide, + .tomb => return try self.resolveInst(ty_op.operand), + } + } else unreachable; + } const ptr = try self.resolveInst(ty_op.operand); return self.load(ptr, ptr_ty); } From 282437c7538e3e70ce06cfee7affe976de28a780 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 May 2022 12:17:48 -0700 Subject: [PATCH 1688/2031] stage2: fix hash/eql on function types to account for generic callconv and generic alignment. --- src/type.zig | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/type.zig b/src/type.zig index 638145e8b1..9a072fa911 100644 --- a/src/type.zig +++ b/src/type.zig @@ -640,18 +640,18 @@ pub const Type = extern union { if (!eql(a_info.return_type, b_info.return_type, mod)) return false; - if (a_info.cc != b_info.cc) - return false; - - if (a_info.alignment != b_info.alignment) - return false; - if (a_info.is_var_args != b_info.is_var_args) return false; if (a_info.is_generic != b_info.is_generic) return false; + if (!a_info.cc_is_generic and a_info.cc != b_info.cc) + return false; + + if (!a_info.align_is_generic and a_info.alignment != b_info.alignment) + return false; + if (a_info.param_types.len != b_info.param_types.len) return false; @@ -1039,8 +1039,12 @@ pub const Type = extern union { if (fn_info.return_type.tag() != .generic_poison) { hashWithHasher(fn_info.return_type, hasher, mod); } - std.hash.autoHash(hasher, fn_info.alignment); - std.hash.autoHash(hasher, fn_info.cc); + if (!fn_info.align_is_generic) { + std.hash.autoHash(hasher, fn_info.alignment); + } + if (!fn_info.cc_is_generic) { + std.hash.autoHash(hasher, fn_info.cc); + } std.hash.autoHash(hasher, fn_info.is_var_args); std.hash.autoHash(hasher, fn_info.is_generic); From 59219e7e91cbfd785f89ec792d3950b9b9ad9b05 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 May 2022 13:36:33 -0700 Subject: [PATCH 1689/2031] stage2: add support for -fbuild-id,-fno-build-id closes #3047 --- build.zig | 1 + lib/std/build.zig | 14 ++++++++++++++ src/Compilation.zig | 3 +++ src/link.zig | 1 + src/link/Elf.zig | 9 ++++++++- src/main.zig | 24 +++++++++++++++++++++--- 6 files changed, 48 insertions(+), 4 deletions(-) diff --git a/build.zig b/build.zig index 24d874b924..163c0bb5a7 100644 --- a/build.zig +++ b/build.zig @@ -145,6 +145,7 @@ pub fn build(b: *Builder) !void { const exe = b.addExecutable("zig", main_file); exe.strip = strip; + exe.build_id = !strip; exe.install(); exe.setBuildMode(mode); exe.setTarget(target); diff --git a/lib/std/build.zig b/lib/std/build.zig index 668720aa79..a738a39a1b 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1549,6 +1549,12 @@ pub const LibExeObjStep = struct { valgrind_support: ?bool = null, each_lib_rpath: ?bool = null, + /// On ELF targets, this will emit a link section called ".note.gnu.build-id" + /// which can be used to coordinate a stripped binary with its debug symbols. + /// As an example, the bloaty project refuses to work unless its inputs have + /// build ids, in order to prevent accidental mismatches. + /// The default is to not include this section because it slows down linking. + build_id: ?bool = null, /// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF /// file. @@ -2953,6 +2959,14 @@ pub const LibExeObjStep = struct { } } + if (self.build_id) |build_id| { + if (build_id) { + try zig_args.append("-fbuild-id"); + } else { + try zig_args.append("-fno-build-id"); + } + } + if (self.override_lib_dir) |dir| { try zig_args.append("--zig-lib-dir"); try zig_args.append(builder.pathFromRoot(dir)); diff --git a/src/Compilation.zig b/src/Compilation.zig index 31782e732b..eb8b2b4d69 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -756,6 +756,7 @@ pub const InitOptions = struct { linker_global_base: ?u64 = null, linker_export_symbol_names: []const []const u8 = &.{}, each_lib_rpath: ?bool = null, + build_id: ?bool = null, disable_c_depfile: bool = false, linker_z_nodelete: bool = false, linker_z_notext: bool = false, @@ -1639,6 +1640,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .skip_linker_dependencies = options.skip_linker_dependencies, .parent_compilation_link_libc = options.parent_compilation_link_libc, .each_lib_rpath = options.each_lib_rpath orelse options.is_native_os, + .build_id = options.build_id orelse false, .cache_mode = cache_mode, .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole, .subsystem = options.subsystem, @@ -2339,6 +2341,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes man.hash.addListOfBytes(comp.bin_file.options.lib_dirs); man.hash.addListOfBytes(comp.bin_file.options.rpath_list); man.hash.add(comp.bin_file.options.each_lib_rpath); + man.hash.add(comp.bin_file.options.build_id); man.hash.add(comp.bin_file.options.skip_linker_dependencies); man.hash.add(comp.bin_file.options.z_nodelete); man.hash.add(comp.bin_file.options.z_notext); diff --git a/src/link.zig b/src/link.zig index 8647259651..60baa6a92a 100644 --- a/src/link.zig +++ b/src/link.zig @@ -146,6 +146,7 @@ pub const Options = struct { skip_linker_dependencies: bool, parent_compilation_link_libc: bool, each_lib_rpath: bool, + build_id: bool, disable_lld_caching: bool, is_test: bool, use_stage1: bool, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 06b241eb4d..442ad73ea7 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1311,7 +1311,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // We can skip hashing libc and libc++ components that we are in charge of building from Zig // installation sources because they are always a product of the compiler version + target information. man.hash.addOptionalBytes(self.base.options.entry); - man.hash.add(stack_size); man.hash.addOptional(self.base.options.image_base_override); man.hash.add(gc_sections); man.hash.add(self.base.options.eh_frame_hdr); @@ -1320,6 +1319,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v man.hash.addListOfBytes(self.base.options.lib_dirs); man.hash.addListOfBytes(self.base.options.rpath_list); man.hash.add(self.base.options.each_lib_rpath); + if (self.base.options.output_mode == .Exe) { + man.hash.add(stack_size); + man.hash.add(self.base.options.build_id); + } man.hash.add(self.base.options.skip_linker_dependencies); man.hash.add(self.base.options.z_nodelete); man.hash.add(self.base.options.z_notext); @@ -1450,6 +1453,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v if (self.base.options.output_mode == .Exe) { try argv.append("-z"); try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size})); + + if (self.base.options.build_id) { + try argv.append("--build-id"); + } } if (self.base.options.image_base_override) |image_base| { diff --git a/src/main.zig b/src/main.zig index a9085d7c7f..37c584ba57 100644 --- a/src/main.zig +++ b/src/main.zig @@ -423,6 +423,8 @@ const usage_build_generic = \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library \\ -fallow-shlib-undefined Allows undefined symbols in shared libraries \\ -fno-allow-shlib-undefined Disallows undefined symbols in shared libraries + \\ -fbuild-id Helps coordinate stripped binaries with debug symbols + \\ -fno-build-id (default) Saves a bit of time linking \\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker \\ --emit-relocs Enable output of relocation sections for post build tools \\ -z [arg] Set linker extension flags @@ -671,6 +673,7 @@ fn buildOutputType( var link_eh_frame_hdr = false; var link_emit_relocs = false; var each_lib_rpath: ?bool = null; + var build_id: ?bool = null; var sysroot: ?[]const u8 = null; var libc_paths_file: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIBC"); var machine_code_model: std.builtin.CodeModel = .default; @@ -1030,6 +1033,10 @@ fn buildOutputType( each_lib_rpath = true; } else if (mem.eql(u8, arg, "-fno-each-lib-rpath")) { each_lib_rpath = false; + } else if (mem.eql(u8, arg, "-fbuild-id")) { + build_id = true; + } else if (mem.eql(u8, arg, "-fno-build-id")) { + build_id = false; } else if (mem.eql(u8, arg, "--enable-cache")) { enable_cache = true; } else if (mem.eql(u8, arg, "--test-cmd-bin")) { @@ -1415,10 +1422,20 @@ fn buildOutputType( while (split_it.next()) |linker_arg| { // Handle nested-joined args like `-Wl,-rpath=foo`. // Must be prefixed with 1 or 2 dashes. - if (linker_arg.len >= 3 and linker_arg[0] == '-' and linker_arg[2] != '-') { + if (linker_arg.len >= 3 and + linker_arg[0] == '-' and + linker_arg[2] != '-') + { if (mem.indexOfScalar(u8, linker_arg, '=')) |equals_pos| { - try linker_args.append(linker_arg[0..equals_pos]); - try linker_args.append(linker_arg[equals_pos + 1 ..]); + const key = linker_arg[0..equals_pos]; + const value = linker_arg[equals_pos + 1 ..]; + if (mem.eql(u8, key, "build-id")) { + build_id = true; + warn("ignoring build-id style argument: '{s}'", .{value}); + continue; + } + try linker_args.append(key); + try linker_args.append(value); continue; } } @@ -2727,6 +2744,7 @@ fn buildOutputType( .stack_report = stack_report, .is_test = arg_mode == .zig_test, .each_lib_rpath = each_lib_rpath, + .build_id = build_id, .test_evented_io = test_evented_io, .test_filter = test_filter, .test_name_prefix = test_name_prefix, From 6d691d3540b1979f0a8d604922c93209b9c0dba0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 May 2022 13:51:59 -0700 Subject: [PATCH 1690/2031] build: include a build-id note only if explicitly requested --- build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig b/build.zig index 163c0bb5a7..9f05fc982e 100644 --- a/build.zig +++ b/build.zig @@ -145,7 +145,7 @@ pub fn build(b: *Builder) !void { const exe = b.addExecutable("zig", main_file); exe.strip = strip; - exe.build_id = !strip; + exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false; exe.install(); exe.setBuildMode(mode); exe.setTarget(target); From 43311e19f440a1b6294653f9fdf97c64f3bdd0ae Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 May 2022 15:16:38 -0700 Subject: [PATCH 1691/2031] LLVM: add readonly, nonnull, align attributes to pointer params --- src/codegen/llvm.zig | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index cca83eca9b..1a423086ca 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2214,16 +2214,31 @@ pub const DeclGen = struct { } // Set parameter attributes. - // TODO: more attributes. see codegen.cpp `make_fn_llvm_value`. switch (fn_info.cc) { .Unspecified, .Inline => { var llvm_param_i: c_uint = @as(c_uint, @boolToInt(sret)) + @boolToInt(err_return_tracing); for (fn_info.param_types) |param_ty| { if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; + // TODO: noalias attribute + if (isByRef(param_ty)) { dg.addArgAttr(llvm_fn, llvm_param_i, "nonnull"); - // TODO readonly, noalias, align + dg.addArgAttr(llvm_fn, llvm_param_i, "readonly"); + dg.addArgAttrInt(llvm_fn, llvm_param_i, "align", param_ty.abiAlignment(target)); + } else if (param_ty.isPtrAtRuntime()) { + const ptr_info = param_ty.ptrInfo().data; + if (!param_ty.isPtrLikeOptional() and !ptr_info.@"allowzero") { + dg.addArgAttr(llvm_fn, llvm_param_i, "nonnull"); + } + if (!ptr_info.mutable) { + dg.addArgAttr(llvm_fn, llvm_param_i, "readonly"); + } + if (ptr_info.@"align" != 0) { + dg.addArgAttrInt(llvm_fn, llvm_param_i, "align", ptr_info.@"align"); + } else { + dg.addArgAttrInt(llvm_fn, llvm_param_i, "align", ptr_info.pointee_type.abiAlignment(target)); + } } llvm_param_i += 1; } @@ -2237,6 +2252,12 @@ pub const DeclGen = struct { @panic("TODO: LLVM backend lower async function"); }, else => { + // TODO set attributes such as noalias, nonnull, readonly, and align + // Note that there is not a one to one correspondence between fn_info.param_types + // and llvm parameters due to C ABI lowering. This will need to involve + // iterateParamTypes which is currently happening over in updateFunc. + // Probably this whole "set parameter attributes" section of code should + // move there and integrate with this abstraction. llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target)); }, } @@ -3705,6 +3726,10 @@ pub const DeclGen = struct { return dg.addAttr(fn_val, param_index + 1, attr_name); } + fn addArgAttrInt(dg: DeclGen, fn_val: *const llvm.Value, param_index: u32, attr_name: []const u8, int: u64) void { + return dg.addAttrInt(fn_val, param_index + 1, attr_name, int); + } + fn removeAttr(val: *const llvm.Value, index: llvm.AttributeIndex, name: []const u8) void { const kind_id = llvm.getEnumAttributeKindForName(name.ptr, name.len); assert(kind_id != 0); From e49fd39463be413a66920087d77eac983a91cb71 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 1 Jun 2022 00:08:29 +0300 Subject: [PATCH 1692/2031] Sema: detect comptime values in `zirMakePtrConst` --- src/Sema.zig | 67 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 6 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index d4c49973a1..42b43d656f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2711,17 +2711,72 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const ptr = try sema.resolveInst(inst_data.operand); - const ptr_ty = sema.typeOf(ptr); - var ptr_info = ptr_ty.ptrInfo().data; + const src = inst_data.src(); + const alloc = try sema.resolveInst(inst_data.operand); + const alloc_ty = sema.typeOf(alloc); + + var ptr_info = alloc_ty.ptrInfo().data; + const elem_ty = ptr_info.pointee_type; + + // Detect if all stores to an `.alloc` were comptime known. + ct: { + var search_index: usize = block.instructions.items.len; + const air_tags = sema.air_instructions.items(.tag); + const air_datas = sema.air_instructions.items(.data); + + const store_inst = while (true) { + if (search_index == 0) break :ct; + search_index -= 1; + + const candidate = block.instructions.items[search_index]; + switch (air_tags[candidate]) { + .dbg_stmt => continue, + .store => break candidate, + else => break :ct, + } + } else unreachable; // TODO shouldn't need this + + while (true) { + if (search_index == 0) break :ct; + search_index -= 1; + + const candidate = block.instructions.items[search_index]; + switch (air_tags[candidate]) { + .dbg_stmt => continue, + .alloc => { + if (Air.indexToRef(candidate) != alloc) break :ct; + break; + }, + else => break :ct, + } + } + + const store_op = air_datas[store_inst].bin_op; + const store_val = (try sema.resolveMaybeUndefVal(block, src, store_op.rhs)) orelse break :ct; + if (store_op.lhs != alloc) break :ct; + + // Remove all the unnecessary runtime instructions. + block.instructions.shrinkRetainingCapacity(search_index); + + var anon_decl = try block.startAnonDecl(src); + defer anon_decl.deinit(); + return sema.analyzeDeclRef(try anon_decl.finish( + try elem_ty.copy(anon_decl.arena()), + try store_val.copy(anon_decl.arena()), + ptr_info.@"align", + )); + } + ptr_info.mutable = false; const const_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_info); - if (try sema.resolveMaybeUndefVal(block, inst_data.src(), ptr)) |val| { + // Detect if a comptime value simply needs to have its type changed. + if (try sema.resolveMaybeUndefVal(block, inst_data.src(), alloc)) |val| { return sema.addConstant(const_ptr_ty, val); } - try sema.requireRuntimeBlock(block, inst_data.src()); - return block.addBitCast(const_ptr_ty, ptr); + + try sema.requireRuntimeBlock(block, src); + return block.addBitCast(const_ptr_ty, alloc); } fn zirAllocInferredComptime( From f2626a3d8ecddf48a08982ca78bff03e9d39d321 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 1 Jun 2022 00:37:28 +0300 Subject: [PATCH 1693/2031] Sema: `validate{Array,Struct}Init` shortcut only valid if base ptr is comptime known --- src/Sema.zig | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 42b43d656f..42e37c7312 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3340,7 +3340,9 @@ fn validateStructInit( const struct_ptr = try sema.resolveInst(struct_ptr_zir_ref); const struct_ty = sema.typeOf(struct_ptr).childType(); - if (is_comptime or block.is_comptime) { + if ((is_comptime or block.is_comptime) and + (try sema.resolveDefinedValue(block, init_src, struct_ptr)) != null) + { // In this case the only thing we need to do is evaluate the implicit // store instructions for default field values, and report any missing fields. // Avoid the cost of the extra machinery for detecting a comptime struct init value. @@ -3544,7 +3546,9 @@ fn zirValidateArrayInit( }); } - if (is_comptime or block.is_comptime) { + if ((is_comptime or block.is_comptime) and + (try sema.resolveDefinedValue(block, init_src, array_ptr)) != null) + { // In this case the comptime machinery will have evaluated the store instructions // at comptime so we have almost nothing to do here. However, in case of a // sentinel-terminated array, the sentinel will not have been populated by From ec919c3c9b915ea0795c24f07c3014915ca0038a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 May 2022 15:30:56 -0700 Subject: [PATCH 1694/2031] LLVM: integrate param attrs with iterateParamTypes This moves some logic from resolveLlvmFunction to updateFunc and takes advantage of the iteration we already do that takes into account C ABI lowering, making LLVM parameter attributes accurate for C ABI functions as well as our own unspecified calling convention. Related to #11498. --- src/codegen/llvm.zig | 56 +++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 1a423086ca..a6ea6255e3 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -712,7 +712,6 @@ pub const Object = struct { .byval => { const param_ty = fn_info.param_types[it.zig_index - 1]; const param = llvm_func.getParam(llvm_arg_i); - llvm_arg_i += 1; if (isByRef(param_ty)) { const alignment = param_ty.abiAlignment(target); @@ -724,11 +723,33 @@ pub const Object = struct { try args.append(arg_ptr); } else { try args.append(param); + + if (param_ty.isPtrAtRuntime()) { + // TODO noalias attribute + const ptr_info = param_ty.ptrInfo().data; + if (!param_ty.isPtrLikeOptional() and !ptr_info.@"allowzero") { + dg.addArgAttr(llvm_func, llvm_arg_i, "nonnull"); + } + if (!ptr_info.mutable) { + dg.addArgAttr(llvm_func, llvm_arg_i, "readonly"); + } + if (ptr_info.@"align" != 0) { + dg.addArgAttrInt(llvm_func, llvm_arg_i, "align", ptr_info.@"align"); + } else { + dg.addArgAttrInt(llvm_func, llvm_arg_i, "align", ptr_info.pointee_type.abiAlignment(target)); + } + } } + llvm_arg_i += 1; }, .byref => { const param_ty = fn_info.param_types[it.zig_index - 1]; const param = llvm_func.getParam(llvm_arg_i); + + dg.addArgAttr(llvm_func, llvm_arg_i, "nonnull"); + dg.addArgAttr(llvm_func, llvm_arg_i, "readonly"); + dg.addArgAttrInt(llvm_func, llvm_arg_i, "align", param_ty.abiAlignment(target)); + llvm_arg_i += 1; if (isByRef(param_ty)) { @@ -2213,35 +2234,8 @@ pub const DeclGen = struct { dg.addArgAttr(llvm_fn, @boolToInt(sret), "nonnull"); } - // Set parameter attributes. switch (fn_info.cc) { .Unspecified, .Inline => { - var llvm_param_i: c_uint = @as(c_uint, @boolToInt(sret)) + @boolToInt(err_return_tracing); - for (fn_info.param_types) |param_ty| { - if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; - - // TODO: noalias attribute - - if (isByRef(param_ty)) { - dg.addArgAttr(llvm_fn, llvm_param_i, "nonnull"); - dg.addArgAttr(llvm_fn, llvm_param_i, "readonly"); - dg.addArgAttrInt(llvm_fn, llvm_param_i, "align", param_ty.abiAlignment(target)); - } else if (param_ty.isPtrAtRuntime()) { - const ptr_info = param_ty.ptrInfo().data; - if (!param_ty.isPtrLikeOptional() and !ptr_info.@"allowzero") { - dg.addArgAttr(llvm_fn, llvm_param_i, "nonnull"); - } - if (!ptr_info.mutable) { - dg.addArgAttr(llvm_fn, llvm_param_i, "readonly"); - } - if (ptr_info.@"align" != 0) { - dg.addArgAttrInt(llvm_fn, llvm_param_i, "align", ptr_info.@"align"); - } else { - dg.addArgAttrInt(llvm_fn, llvm_param_i, "align", ptr_info.pointee_type.abiAlignment(target)); - } - } - llvm_param_i += 1; - } llvm_fn.setFunctionCallConv(.Fast); }, .Naked => { @@ -2252,12 +2246,6 @@ pub const DeclGen = struct { @panic("TODO: LLVM backend lower async function"); }, else => { - // TODO set attributes such as noalias, nonnull, readonly, and align - // Note that there is not a one to one correspondence between fn_info.param_types - // and llvm parameters due to C ABI lowering. This will need to involve - // iterateParamTypes which is currently happening over in updateFunc. - // Probably this whole "set parameter attributes" section of code should - // move there and integrate with this abstraction. llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target)); }, } From a73895339a3f28268873fc4c86cc0da729392b0d Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 1 Jun 2022 00:44:26 +0300 Subject: [PATCH 1695/2031] Sema: handle bitcasts produced by `coerce_result_ptr` in `validate{Array,Struct}Init` --- src/Sema.zig | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 42e37c7312..2e86e21a9f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3444,7 +3444,15 @@ fn validateStructInit( } if (air_tags[store_inst] != .store) continue; const bin_op = air_datas[store_inst].bin_op; - if (bin_op.lhs != field_ptr_air_ref) continue; + var lhs = bin_op.lhs; + { + const lhs_index = Air.refToIndex(lhs) orelse continue; + if (air_tags[lhs_index] == .bitcast) { + lhs = air_datas[lhs_index].ty_op.operand; + block_index -= 1; + } + } + if (lhs != field_ptr_air_ref) continue; if (block_index > 0 and field_ptr_air_inst == block.instructions.items[block_index - 1]) { @@ -3603,7 +3611,14 @@ fn zirValidateArrayInit( switch (air_tags[next_air_inst]) { .store => { const bin_op = air_datas[next_air_inst].bin_op; - if (bin_op.lhs != elem_ptr_air_ref) { + var lhs = bin_op.lhs; + if (Air.refToIndex(lhs)) |lhs_index| { + if (air_tags[lhs_index] == .bitcast) { + lhs = air_datas[lhs_index].ty_op.operand; + block_index -= 1; + } + } + if (lhs != elem_ptr_air_ref) { array_is_comptime = false; continue; } From 3c4e7abfbff9fbdb967308659951ca091b346bac Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 1 Jun 2022 02:18:45 +0300 Subject: [PATCH 1696/2031] Sema: handle `dbg_smtt`s when deleting runtime instructions in `validateStructInit` --- src/Sema.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index 2e86e21a9f..4e1c650034 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3453,6 +3453,10 @@ fn validateStructInit( } } if (lhs != field_ptr_air_ref) continue; + while (block_index > 0) : (block_index -= 1) { + const block_inst = block.instructions.items[block_index - 1]; + if (air_tags[block_inst] != .dbg_stmt) break; + } if (block_index > 0 and field_ptr_air_inst == block.instructions.items[block_index - 1]) { From 356a865b871db458149f70bc103a3971780d6962 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 May 2022 17:38:42 -0700 Subject: [PATCH 1697/2031] stage2: introduce support for noalias Not implemented yet is enhancements to coerceInMemory to account for noalias parameters. Related to #11498. --- src/AstGen.zig | 43 +++++++++++++++++++++++++++++++++---------- src/Sema.zig | 17 ++++++++++++----- src/Zir.zig | 14 +++++++++++--- src/codegen/llvm.zig | 6 +++++- src/print_zir.zig | 13 +++++++++++++ src/type.zig | 38 ++++++++++++++++++++++++++++++++++---- 6 files changed, 108 insertions(+), 23 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 5c4fc88483..ab5befa4ba 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1155,14 +1155,20 @@ fn fnProtoExpr( const block_inst = try gz.makeBlockInst(.block_inline, node); + var noalias_bits: u32 = 0; const is_var_args = is_var_args: { var param_type_i: usize = 0; var it = fn_proto.iterate(tree); while (it.next()) |param| : (param_type_i += 1) { - const is_comptime = if (param.comptime_noalias) |token| - token_tags[token] == .keyword_comptime - else - false; + const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) { + .keyword_noalias => is_comptime: { + noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse + return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{})); + break :is_comptime false; + }, + .keyword_comptime => true, + else => false, + } else false; const is_anytype = if (param.anytype_ellipsis3) |token| blk: { switch (token_tags[token]) { @@ -1255,6 +1261,7 @@ fn fnProtoExpr( .is_inferred_error = false, .is_test = false, .is_extern = false, + .noalias_bits = noalias_bits, }); _ = try block_scope.addBreak(.break_inline, block_inst, result); @@ -3381,15 +3388,21 @@ fn fnDecl( // align, linksection, and addrspace is passed in the func instruction in this case. wip_members.nextDecl(is_pub, is_export, false, false); + var noalias_bits: u32 = 0; var params_scope = &fn_gz.base; const is_var_args = is_var_args: { var param_type_i: usize = 0; var it = fn_proto.iterate(tree); while (it.next()) |param| : (param_type_i += 1) { - const is_comptime = if (param.comptime_noalias) |token| - token_tags[token] == .keyword_comptime - else - false; + const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) { + .keyword_noalias => is_comptime: { + noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse + return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{})); + break :is_comptime false; + }, + .keyword_comptime => true, + else => false, + } else false; const is_anytype = if (param.anytype_ellipsis3) |token| blk: { switch (token_tags[token]) { @@ -3576,6 +3589,7 @@ fn fnDecl( .is_inferred_error = false, .is_test = false, .is_extern = true, + .noalias_bits = noalias_bits, }); } else func: { if (is_var_args) { @@ -3623,6 +3637,7 @@ fn fnDecl( .is_inferred_error = is_inferred_error, .is_test = false, .is_extern = false, + .noalias_bits = noalias_bits, }); }; @@ -4057,6 +4072,7 @@ fn testDecl( .is_inferred_error = true, .is_test = true, .is_extern = false, + .noalias_bits = 0, }); _ = try decl_block.addBreak(.break_inline, block_inst, func_inst); @@ -10024,6 +10040,7 @@ const GenZir = struct { ret_ref: Zir.Inst.Ref, lib_name: u32, + noalias_bits: u32, is_var_args: bool, is_inferred_error: bool, is_test: bool, @@ -10071,7 +10088,7 @@ const GenZir = struct { if (args.cc_ref != .none or args.lib_name != 0 or args.is_var_args or args.is_test or args.is_extern or args.align_ref != .none or args.section_ref != .none or - args.addrspace_ref != .none) + args.addrspace_ref != .none or args.noalias_bits != 0) { var align_body: []Zir.Inst.Index = &.{}; var addrspace_body: []Zir.Inst.Index = &.{}; @@ -10093,7 +10110,8 @@ const GenZir = struct { fancyFnExprExtraLen(cc_body, args.cc_ref) + fancyFnExprExtraLen(ret_body, ret_ref) + body.len + src_locs.len + - @boolToInt(args.lib_name != 0), + @boolToInt(args.lib_name != 0) + + @boolToInt(args.noalias_bits != 0), ); const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{ .param_block = args.param_block, @@ -10104,6 +10122,7 @@ const GenZir = struct { .is_test = args.is_test, .is_extern = args.is_extern, .has_lib_name = args.lib_name != 0, + .has_any_noalias = args.noalias_bits != 0, .has_align_ref = args.align_ref != .none, .has_addrspace_ref = args.addrspace_ref != .none, @@ -10159,6 +10178,10 @@ const GenZir = struct { astgen.extra.appendAssumeCapacity(@enumToInt(ret_ref)); } + if (args.noalias_bits != 0) { + astgen.extra.appendAssumeCapacity(args.noalias_bits); + } + astgen.extra.appendSliceAssumeCapacity(body); astgen.extra.appendSliceAssumeCapacity(src_locs); diff --git a/src/Sema.zig b/src/Sema.zig index d4c49973a1..9f45e7ffd0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6565,12 +6565,10 @@ fn zirFunc( has_body, src_locs, null, + 0, ); } -// TODO this function and its callsites along with funcCommon need to be reworked -// to handle when callconv, align, linksection, addrspace depend on comptime values -// (thus triggering error.GenericPoison) fn resolveGenericBody( sema: *Sema, block: *Block, @@ -6696,6 +6694,7 @@ fn funcCommon( has_body: bool, src_locs: Zir.Inst.Func.SrcLocs, opt_lib_name: ?[]const u8, + noalias_bits: u32, ) CompileError!Air.Inst.Ref { const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; @@ -6807,6 +6806,7 @@ fn funcCommon( .addrspace_is_generic = address_space == null, .is_var_args = var_args, .is_generic = is_generic, + .noalias_bits = noalias_bits, }); }; @@ -9626,7 +9626,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; - // In `**` rhs has to be comptime-known, but lhs can be runtime-known + // In `**` rhs must be comptime-known, but lhs can be runtime-known const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize); if (lhs_ty.isTuple()) { @@ -11916,7 +11916,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai try sema.queueFullTypeResolution(try error_field_ty.copy(sema.arena)); - // If the error set is inferred it has to be resolved at this point + // If the error set is inferred it must be resolved at this point try sema.resolveInferredErrorSetTy(block, src, ty); // Build our list of Error values @@ -16970,6 +16970,12 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A break :blk ty; } else Type.void; + const noalias_bits: u32 = if (extra.data.bits.has_any_noalias) blk: { + const x = sema.code.extra[extra_index]; + extra_index += 1; + break :blk x; + } else 0; + var src_locs: Zir.Inst.Func.SrcLocs = undefined; const has_body = extra.data.body_len != 0; if (has_body) { @@ -16996,6 +17002,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A has_body, src_locs, lib_name, + noalias_bits, ); } diff --git a/src/Zir.zig b/src/Zir.zig index 040f54cc39..f09f2015e0 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -2670,8 +2670,10 @@ pub const Inst = struct { /// 14. ret_ty_body_len: u32 /// 15. ret_ty_body: u32 // for each ret_ty_body_len /// } - /// 16. body: Index // for each body_len - /// 17. src_locs: Func.SrcLocs // if body_len != 0 + /// 16. noalias_bits: u32 // if has_any_noalias + /// - each bit starting with LSB corresponds to parameter indexes + /// 17. body: Index // for each body_len + /// 18. src_locs: Func.SrcLocs // if body_len != 0 pub const FuncFancy = struct { /// Points to the block that contains the param instructions for this function. param_block: Index, @@ -2699,7 +2701,8 @@ pub const Inst = struct { has_ret_ty_ref: bool, has_ret_ty_body: bool, has_lib_name: bool, - _: u17 = undefined, + has_any_noalias: bool, + _: u16 = undefined, }; }; @@ -3699,6 +3702,8 @@ fn findDeclsInner( extra_index += 1; } + extra_index += @boolToInt(extra.data.bits.has_any_noalias); + const body = zir.extra[extra_index..][0..extra.data.body_len]; return zir.findDeclsBody(list, body); }, @@ -3906,6 +3911,9 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { ret_ty_ref = @intToEnum(Inst.Ref, zir.extra[extra_index]); extra_index += 1; } + + extra_index += @boolToInt(extra.data.bits.has_any_noalias); + const body = zir.extra[extra_index..][0..extra.data.body_len]; extra_index += body.len; break :blk .{ diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a6ea6255e3..34203b9536 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -725,8 +725,12 @@ pub const Object = struct { try args.append(param); if (param_ty.isPtrAtRuntime()) { - // TODO noalias attribute const ptr_info = param_ty.ptrInfo().data; + if (math.cast(u5, it.zig_index - 1)) |i| { + if (@truncate(u1, fn_info.noalias_bits >> i) != 0) { + dg.addArgAttr(llvm_func, llvm_arg_i, "noalias"); + } + } if (!param_ty.isPtrLikeOptional() and !ptr_info.@"allowzero") { dg.addArgAttr(llvm_func, llvm_arg_i, "nonnull"); } diff --git a/src/print_zir.zig b/src/print_zir.zig index 30098f5372..8081a94093 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -1961,6 +1961,7 @@ const Writer = struct { body, src, src_locs, + 0, ); } @@ -2034,6 +2035,12 @@ const Writer = struct { extra_index += 1; } + const noalias_bits: u32 = if (extra.data.bits.has_any_noalias) blk: { + const x = self.code.extra[extra_index]; + extra_index += 1; + break :blk x; + } else 0; + const body = self.code.extra[extra_index..][0..extra.data.body_len]; extra_index += body.len; @@ -2059,6 +2066,7 @@ const Writer = struct { body, src, src_locs, + noalias_bits, ); } @@ -2216,6 +2224,7 @@ const Writer = struct { body: []const Zir.Inst.Index, src: LazySrcLoc, src_locs: Zir.Inst.Func.SrcLocs, + noalias_bits: u32, ) !void { try self.writeOptionalInstRefOrBody(stream, "align=", align_ref, align_body); try self.writeOptionalInstRefOrBody(stream, "addrspace=", addrspace_ref, addrspace_body); @@ -2226,6 +2235,10 @@ const Writer = struct { try self.writeFlag(stream, "extern, ", is_extern); try self.writeFlag(stream, "inferror, ", inferred_error_set); + if (noalias_bits != 0) { + try stream.print("noalias=0b{b}, ", .{noalias_bits}); + } + try stream.writeAll("body="); try self.writeBracedBody(stream, body); try stream.writeAll(") "); diff --git a/src/type.zig b/src/type.zig index 9a072fa911..94fcd0a96c 100644 --- a/src/type.zig +++ b/src/type.zig @@ -646,6 +646,9 @@ pub const Type = extern union { if (a_info.is_generic != b_info.is_generic) return false; + if (a_info.noalias_bits != b_info.noalias_bits) + return false; + if (!a_info.cc_is_generic and a_info.cc != b_info.cc) return false; @@ -1047,6 +1050,7 @@ pub const Type = extern union { } std.hash.autoHash(hasher, fn_info.is_var_args); std.hash.autoHash(hasher, fn_info.is_generic); + std.hash.autoHash(hasher, fn_info.noalias_bits); std.hash.autoHash(hasher, fn_info.param_types.len); for (fn_info.param_types) |param_ty, i| { @@ -1424,6 +1428,11 @@ pub const Type = extern union { .is_var_args = payload.is_var_args, .is_generic = payload.is_generic, .comptime_params = comptime_params.ptr, + .align_is_generic = payload.align_is_generic, + .cc_is_generic = payload.cc_is_generic, + .section_is_generic = payload.section_is_generic, + .addrspace_is_generic = payload.addrspace_is_generic, + .noalias_bits = payload.noalias_bits, }); }, .pointer => { @@ -4738,6 +4747,11 @@ pub const Type = extern union { .alignment = 0, .is_var_args = false, .is_generic = false, + .align_is_generic = false, + .cc_is_generic = false, + .section_is_generic = false, + .addrspace_is_generic = false, + .noalias_bits = 0, }, .fn_void_no_args => .{ .param_types = &.{}, @@ -4747,6 +4761,11 @@ pub const Type = extern union { .alignment = 0, .is_var_args = false, .is_generic = false, + .align_is_generic = false, + .cc_is_generic = false, + .section_is_generic = false, + .addrspace_is_generic = false, + .noalias_bits = 0, }, .fn_naked_noreturn_no_args => .{ .param_types = &.{}, @@ -4756,6 +4775,11 @@ pub const Type = extern union { .alignment = 0, .is_var_args = false, .is_generic = false, + .align_is_generic = false, + .cc_is_generic = false, + .section_is_generic = false, + .addrspace_is_generic = false, + .noalias_bits = 0, }, .fn_ccc_void_no_args => .{ .param_types = &.{}, @@ -4765,6 +4789,11 @@ pub const Type = extern union { .alignment = 0, .is_var_args = false, .is_generic = false, + .align_is_generic = false, + .cc_is_generic = false, + .section_is_generic = false, + .addrspace_is_generic = false, + .noalias_bits = 0, }, .function => ty.castTag(.function).?.data, @@ -6123,13 +6152,14 @@ pub const Type = extern union { return_type: Type, /// If zero use default target function code alignment. alignment: u32, + noalias_bits: u32, cc: std.builtin.CallingConvention, is_var_args: bool, is_generic: bool, - align_is_generic: bool = false, - cc_is_generic: bool = false, - section_is_generic: bool = false, - addrspace_is_generic: bool = false, + align_is_generic: bool, + cc_is_generic: bool, + section_is_generic: bool, + addrspace_is_generic: bool, pub fn paramIsComptime(self: @This(), i: usize) bool { assert(i < self.param_types.len); From 2f9533f639311f680f3080747aa03b411ca63100 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 May 2022 18:25:57 -0700 Subject: [PATCH 1698/2031] LLVM: pass slices as ptr/len combo LLVM optimization passes handle this better, and it allows Zig to specify pointer parameter attributes such as readonly, nonnull, noalias, and alignment. closes #561 --- src/codegen/llvm.zig | 70 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 34203b9536..80fd93bbbe 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -712,6 +712,7 @@ pub const Object = struct { .byval => { const param_ty = fn_info.param_types[it.zig_index - 1]; const param = llvm_func.getParam(llvm_arg_i); + try args.ensureUnusedCapacity(1); if (isByRef(param_ty)) { const alignment = param_ty.abiAlignment(target); @@ -720,9 +721,9 @@ pub const Object = struct { arg_ptr.setAlignment(alignment); const store_inst = builder.buildStore(param, arg_ptr); store_inst.setAlignment(alignment); - try args.append(arg_ptr); + args.appendAssumeCapacity(arg_ptr); } else { - try args.append(param); + args.appendAssumeCapacity(param); if (param_ty.isPtrAtRuntime()) { const ptr_info = param_ty.ptrInfo().data; @@ -756,13 +757,15 @@ pub const Object = struct { llvm_arg_i += 1; + try args.ensureUnusedCapacity(1); + if (isByRef(param_ty)) { - try args.append(param); + args.appendAssumeCapacity(param); } else { const alignment = param_ty.abiAlignment(target); const load_inst = builder.buildLoad(param, ""); load_inst.setAlignment(alignment); - try args.append(load_inst); + args.appendAssumeCapacity(load_inst); } }, .abi_sized_int => { @@ -784,14 +787,44 @@ pub const Object = struct { const store_inst = builder.buildStore(param, casted_ptr); store_inst.setAlignment(alignment); + try args.ensureUnusedCapacity(1); + if (isByRef(param_ty)) { - try args.append(arg_ptr); + args.appendAssumeCapacity(arg_ptr); } else { const load_inst = builder.buildLoad(arg_ptr, ""); load_inst.setAlignment(alignment); - try args.append(load_inst); + args.appendAssumeCapacity(load_inst); } }, + .slice => { + const param_ty = fn_info.param_types[it.zig_index - 1]; + const ptr_info = param_ty.ptrInfo().data; + + if (math.cast(u5, it.zig_index - 1)) |i| { + if (@truncate(u1, fn_info.noalias_bits >> i) != 0) { + dg.addArgAttr(llvm_func, llvm_arg_i, "noalias"); + } + } + dg.addArgAttr(llvm_func, llvm_arg_i, "nonnull"); + if (!ptr_info.mutable) { + dg.addArgAttr(llvm_func, llvm_arg_i, "readonly"); + } + if (ptr_info.@"align" != 0) { + dg.addArgAttrInt(llvm_func, llvm_arg_i, "align", ptr_info.@"align"); + } else { + dg.addArgAttrInt(llvm_func, llvm_arg_i, "align", ptr_info.pointee_type.abiAlignment(target)); + } + const ptr_param = llvm_func.getParam(llvm_arg_i); + llvm_arg_i += 1; + const len_param = llvm_func.getParam(llvm_arg_i); + llvm_arg_i += 1; + + const slice_llvm_ty = try dg.lowerType(param_ty); + const partial = builder.buildInsertValue(slice_llvm_ty.getUndef(), ptr_param, 0, ""); + const aggregate = builder.buildInsertValue(partial, len_param, 1, ""); + try args.append(aggregate); + }, .multiple_llvm_ints => { const param_ty = fn_info.param_types[it.zig_index - 1]; const llvm_ints = it.llvm_types_buffer[0..it.llvm_types_len]; @@ -2743,6 +2776,17 @@ pub const DeclGen = struct { const abi_size = @intCast(c_uint, param_ty.abiSize(target)); try llvm_params.append(dg.context.intType(abi_size * 8)); }, + .slice => { + const param_ty = fn_info.param_types[it.zig_index - 1]; + var buf: Type.SlicePtrFieldTypeBuffer = undefined; + const ptr_ty = param_ty.slicePtrFieldType(&buf); + const ptr_llvm_ty = try dg.lowerType(ptr_ty); + const len_llvm_ty = try dg.lowerType(Type.usize); + + try llvm_params.ensureUnusedCapacity(2); + llvm_params.appendAssumeCapacity(ptr_llvm_ty); + llvm_params.appendAssumeCapacity(len_llvm_ty); + }, .multiple_llvm_ints => { const param_ty = fn_info.param_types[it.zig_index - 1]; const llvm_ints = it.llvm_types_buffer[0..it.llvm_types_len]; @@ -4205,6 +4249,15 @@ pub const FuncGen = struct { try llvm_args.append(load_inst); } }, + .slice => { + const arg = args[it.zig_index - 1]; + const llvm_arg = try self.resolveInst(arg); + const ptr = self.builder.buildExtractValue(llvm_arg, 0, ""); + const len = self.builder.buildExtractValue(llvm_arg, 1, ""); + try llvm_args.ensureUnusedCapacity(2); + llvm_args.appendAssumeCapacity(ptr); + llvm_args.appendAssumeCapacity(len); + }, .multiple_llvm_ints => { const arg = args[it.zig_index - 1]; const param_ty = self.air.typeOf(arg); @@ -8811,6 +8864,7 @@ const ParamTypeIterator = struct { byref, abi_sized_int, multiple_llvm_ints, + slice, }; fn next(it: *ParamTypeIterator) ?Lowering { @@ -8826,7 +8880,9 @@ const ParamTypeIterator = struct { .Unspecified, .Inline => { it.zig_index += 1; it.llvm_index += 1; - if (isByRef(ty)) { + if (ty.isSlice()) { + return .slice; + } else if (isByRef(ty)) { return .byref; } else { return .byval; From 8c0f4e6f54eefc56f69bf8def333ff8cf67c0783 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 May 2022 22:13:24 -0700 Subject: [PATCH 1699/2031] LLVM: add target-cpu and target-features fn attributes --- src/codegen/llvm.zig | 21 ++++++++++++++------- src/codegen/llvm/bindings.zig | 3 +++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 80fd93bbbe..179216a52f 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2302,19 +2302,21 @@ pub const DeclGen = struct { } fn addCommonFnAttributes(dg: *DeclGen, llvm_fn: *const llvm.Value) void { - if (!dg.module.comp.bin_file.options.red_zone) { + const comp = dg.module.comp; + + if (!comp.bin_file.options.red_zone) { dg.addFnAttr(llvm_fn, "noredzone"); } - if (dg.module.comp.bin_file.options.omit_frame_pointer) { + if (comp.bin_file.options.omit_frame_pointer) { dg.addFnAttrString(llvm_fn, "frame-pointer", "none"); } else { dg.addFnAttrString(llvm_fn, "frame-pointer", "all"); } dg.addFnAttr(llvm_fn, "nounwind"); - if (dg.module.comp.unwind_tables) { + if (comp.unwind_tables) { dg.addFnAttr(llvm_fn, "uwtable"); } - if (dg.module.comp.bin_file.options.skip_linker_dependencies) { + if (comp.bin_file.options.skip_linker_dependencies) { // The intent here is for compiler-rt and libc functions to not generate // infinite recursion. For example, if we are compiling the memcpy function, // and llvm detects that the body is equivalent to memcpy, it may replace the @@ -2322,14 +2324,19 @@ pub const DeclGen = struct { // overflow instead of performing memcpy. dg.addFnAttr(llvm_fn, "nobuiltin"); } - if (dg.module.comp.bin_file.options.optimize_mode == .ReleaseSmall) { + if (comp.bin_file.options.optimize_mode == .ReleaseSmall) { dg.addFnAttr(llvm_fn, "minsize"); dg.addFnAttr(llvm_fn, "optsize"); } - if (dg.module.comp.bin_file.options.tsan) { + if (comp.bin_file.options.tsan) { dg.addFnAttr(llvm_fn, "sanitize_thread"); } - // TODO add target-cpu and target-features fn attributes + if (comp.getTarget().cpu.model.llvm_name) |s| { + llvm_fn.addFunctionAttr("target-cpu", s); + } + if (comp.bin_file.options.llvm_cpu_features) |s| { + llvm_fn.addFunctionAttr("target-features", s); + } } fn resolveGlobalDecl(dg: *DeclGen, decl_index: Module.Decl.Index) Error!*const llvm.Value { diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 425358865f..ca748d3ce3 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -239,6 +239,9 @@ pub const Value = opaque { pub const getAlignment = LLVMGetAlignment; extern fn LLVMGetAlignment(V: *const Value) c_uint; + + pub const addFunctionAttr = ZigLLVMAddFunctionAttr; + extern fn ZigLLVMAddFunctionAttr(Fn: *const Value, attr_name: [*:0]const u8, attr_value: [*:0]const u8) void; }; pub const Type = opaque { From cbb806da6e0b8f2f4d0d12ca57618c291acfa0c0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 May 2022 22:56:39 -0700 Subject: [PATCH 1700/2031] stage2: -fbuild-id causes default linker to be LLD until zig's self-hosted linker gains this functionality. --- src/Compilation.zig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index eb8b2b4d69..04e9d97719 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -975,6 +975,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { const unwind_tables = options.want_unwind_tables orelse (link_libunwind or target_util.needUnwindTables(options.target)); const link_eh_frame_hdr = options.link_eh_frame_hdr or unwind_tables; + const build_id = options.build_id orelse false; // Make a decision on whether to use LLD or our own linker. const use_lld = options.use_lld orelse blk: { @@ -1005,7 +1006,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { options.output_mode == .Lib or options.image_base_override != null or options.linker_script != null or options.version_script != null or - options.emit_implib != null) + options.emit_implib != null or + build_id) { break :blk true; } @@ -1640,7 +1642,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .skip_linker_dependencies = options.skip_linker_dependencies, .parent_compilation_link_libc = options.parent_compilation_link_libc, .each_lib_rpath = options.each_lib_rpath orelse options.is_native_os, - .build_id = options.build_id orelse false, + .build_id = build_id, .cache_mode = cache_mode, .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole, .subsystem = options.subsystem, From 9431100736abe63c361506411a40d143b23091d9 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 1 Jun 2022 02:19:40 +0300 Subject: [PATCH 1701/2031] Sema: apply previous changes to `validateUnionInit` --- lib/std/math/big/int.zig | 5 ++--- src/Sema.zig | 17 ++++++++++++-- test/behavior/basic.zig | 48 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index b57e1896cc..170272f287 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -2063,9 +2063,8 @@ pub const Const = struct { // This is the inverse of calcDivLimbsBufferLen const available_len = (limbs.len / 3) - 2; - // TODO https://github.com/ziglang/zig/issues/11439 - const biggest = comptime Const{ - .limbs = &([1]Limb{math.maxInt(Limb)} ** available_len), + const biggest: Const = .{ + .limbs = &([1]Limb{comptime math.maxInt(Limb)} ** available_len), .positive = false, }; var buf: [biggest.sizeInBaseUpperBound(radix)]u8 = undefined; diff --git a/src/Sema.zig b/src/Sema.zig index 4e1c650034..64e981236a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3215,7 +3215,9 @@ fn validateUnionInit( return sema.failWithOwnedErrorMsg(block, msg); } - if (is_comptime or block.is_comptime) { + if ((is_comptime or block.is_comptime) and + (try sema.resolveDefinedValue(block, init_src, union_ptr)) != null) + { // In this case, comptime machinery already did everything. No work to do here. return; } @@ -3261,7 +3263,18 @@ fn validateUnionInit( if (store_inst == field_ptr_air_inst) break; if (air_tags[store_inst] != .store) continue; const bin_op = air_datas[store_inst].bin_op; - if (bin_op.lhs != field_ptr_air_ref) continue; + var lhs = bin_op.lhs; + if (Air.refToIndex(lhs)) |lhs_index| { + if (air_tags[lhs_index] == .bitcast) { + lhs = air_datas[lhs_index].ty_op.operand; + block_index -= 1; + } + } + if (lhs != field_ptr_air_ref) continue; + while (block_index > 0) : (block_index -= 1) { + const block_inst = block.instructions.items[block_index - 1]; + if (air_tags[block_inst] != .dbg_stmt) break; + } if (block_index > 0 and field_ptr_air_inst == block.instructions.items[block_index - 1]) { diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 32df664bae..3129091091 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -1005,3 +1005,51 @@ test "generic function uses return type of other generic function" { }; try std.testing.expect(S.call(S.func, .{@as(u8, 1)}) == 1); } + +test "const alloc with comptime known initializer is made comptime known" { + const S = struct { + a: bool, + b: [2]u8, + }; + { + const s: S = .{ + .a = false, + .b = .{ 1, 2 }, + }; + if (s.a) @compileError("bad"); + } + { + const s: S = .{ + .a = false, + .b = [2]u8{ 1, 2 }, + }; + if (s.a) @compileError("bad"); + } + { + const s: S = comptime .{ + .a = false, + .b = .{ 1, 2 }, + }; + if (s.a) @compileError("bad"); + } + { + const Const = struct { + limbs: []const usize, + positive: bool, + }; + const biggest: Const = .{ + .limbs = &([1]usize{comptime std.math.maxInt(usize)} ** 128), + .positive = false, + }; + if (biggest.positive) @compileError("bad"); + } + { + const U = union(enum) { + a: usize, + }; + const u: U = .{ + .a = comptime std.math.maxInt(usize), + }; + if (u.a == 0) @compileError("bad"); + } +} From 94624893d88526d7a0f88f048e2a26abea6454c2 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 1 Jun 2022 02:24:03 +0300 Subject: [PATCH 1702/2031] disable failing test --- test/behavior/bugs/1741.zig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/behavior/bugs/1741.zig b/test/behavior/bugs/1741.zig index f4cc2101c4..b91ac8e2fb 100644 --- a/test/behavior/bugs/1741.zig +++ b/test/behavior/bugs/1741.zig @@ -2,9 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); test "fixed" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend != .stage1) return error.SkipZigTest; const x: f32 align(128) = 12.34; try std.testing.expect(@ptrToInt(&x) % 128 == 0); } From 69323fc1432146bf166175060dea9d0248dbeba4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Jun 2022 11:19:19 -0700 Subject: [PATCH 1703/2031] CI: skip non native ReleaseFast and ReleaseSafe tests on drone Because we're hitting the 2 hour time limit. --- ci/drone/linux_script_test | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/drone/linux_script_test b/ci/drone/linux_script_test index 15a12b59ee..539a372f30 100755 --- a/ci/drone/linux_script_test +++ b/ci/drone/linux_script_test @@ -17,11 +17,11 @@ case "$1" in ;; 3) # ReleaseSafe - ./build/zig build $BUILD_FLAGS test-std -Dskip-debug -Dskip-release-fast -Dskip-release-small + ./build/zig build $BUILD_FLAGS test-std -Dskip-debug -Dskip-release-fast -Dskip-release-small -Dskip-non-native ;; 4) - # Releasefast - ./build/zig build $BUILD_FLAGS test-std -Dskip-debug -Dskip-release-safe -Dskip-release-small + # ReleaseFast + ./build/zig build $BUILD_FLAGS test-std -Dskip-debug -Dskip-release-safe -Dskip-release-small -Dskip-non-native ;; 5) # ReleaseSmall From de14fba2478019ba6070407502b422d349ebf754 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Jun 2022 11:53:00 -0700 Subject: [PATCH 1704/2031] LLVM: convert two ArrayLists into a MultiArrayList --- src/codegen/llvm.zig | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 179216a52f..c7282fa22b 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3893,15 +3893,16 @@ pub const FuncGen = struct { /// This data structure is used to implement breaking to blocks. blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, struct { parent_bb: *const llvm.BasicBlock, - break_bbs: *BreakBasicBlocks, - break_vals: *BreakValues, + breaks: *BreakList, }), single_threaded: bool, const DbgState = struct { loc: *llvm.DILocation, scope: *llvm.DIScope, base_line: u32 }; - const BreakBasicBlocks = std.ArrayListUnmanaged(*const llvm.BasicBlock); - const BreakValues = std.ArrayListUnmanaged(*const llvm.Value); + const BreakList = std.MultiArrayList(struct { + bb: *const llvm.BasicBlock, + val: *const llvm.Value, + }); fn deinit(self: *FuncGen) void { self.builder.dispose(); @@ -4649,16 +4650,12 @@ pub const FuncGen = struct { return null; } - var break_bbs: BreakBasicBlocks = .{}; - defer break_bbs.deinit(self.gpa); - - var break_vals: BreakValues = .{}; - defer break_vals.deinit(self.gpa); + var breaks: BreakList = .{}; + defer breaks.deinit(self.gpa); try self.blocks.putNoClobber(self.gpa, inst, .{ .parent_bb = parent_bb, - .break_bbs = &break_bbs, - .break_vals = &break_vals, + .breaks = &breaks, }); defer assert(self.blocks.remove(inst)); @@ -4667,7 +4664,7 @@ pub const FuncGen = struct { self.llvm_func.appendExistingBasicBlock(parent_bb); self.builder.positionBuilderAtEnd(parent_bb); - // If the block does not return a value, we dont have to create a phi node. + // Create a phi node only if the block returns a value. const is_body = inst_ty.zigTypeTag() == .Fn; if (!is_body and !inst_ty.hasRuntimeBitsIgnoreComptime()) return null; @@ -4686,9 +4683,9 @@ pub const FuncGen = struct { const phi_node = self.builder.buildPhi(llvm_ty, ""); phi_node.addIncoming( - break_vals.items.ptr, - break_bbs.items.ptr, - @intCast(c_uint, break_vals.items.len), + breaks.items(.val).ptr, + breaks.items(.bb).ptr, + @intCast(c_uint, breaks.len), ); return phi_node; } @@ -4697,16 +4694,17 @@ pub const FuncGen = struct { const branch = self.air.instructions.items(.data)[inst].br; const block = self.blocks.get(branch.block_inst).?; - // If the break doesn't break a value, then we don't have to add - // the values to the lists. + // Add the values to the lists only if the break provides a value. const operand_ty = self.air.typeOf(branch.operand); if (operand_ty.hasRuntimeBitsIgnoreComptime() or operand_ty.zigTypeTag() == .Fn) { const val = try self.resolveInst(branch.operand); // For the phi node, we need the basic blocks and the values of the // break instructions. - try block.break_bbs.append(self.gpa, self.builder.getInsertBlock()); - try block.break_vals.append(self.gpa, val); + try block.breaks.append(self.gpa, .{ + .bb = self.builder.getInsertBlock(), + .val = val, + }); } _ = self.builder.buildBr(block.parent_bb); return null; From b095aa6986badc3b8c2255ad2c824ca4ea9959d9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Jun 2022 14:05:58 -0700 Subject: [PATCH 1705/2031] CI: skip single-threaded tests on Drone sure would be nice if they would just give us another hour of CI run time. --- build.zig | 5 +++-- ci/drone/linux_script_test | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build.zig b/build.zig index 9f05fc982e..3ce7cc24c2 100644 --- a/build.zig +++ b/build.zig @@ -54,6 +54,7 @@ pub fn build(b: *Builder) !void { const skip_release_safe = b.option(bool, "skip-release-safe", "Main test suite skips release-safe builds") orelse skip_release; const skip_non_native = b.option(bool, "skip-non-native", "Main test suite skips non-native builds") orelse false; const skip_libc = b.option(bool, "skip-libc", "Main test suite skips tests that link libc") orelse false; + const skip_single_threaded = b.option(bool, "skip-single-threaded", "Main test suite skips tests that are single-threaded") orelse false; const skip_stage1 = b.option(bool, "skip-stage1", "Main test suite skips stage1 compile error tests") orelse false; const skip_run_translated_c = b.option(bool, "skip-run-translated-c", "Main test suite skips run-translated-c tests") orelse false; const skip_stage2_tests = b.option(bool, "skip-stage2-tests", "Main test suite skips self-hosted compiler tests") orelse false; @@ -444,7 +445,7 @@ pub fn build(b: *Builder) !void { "behavior", "Run the behavior tests", modes, - false, // skip_single_threaded + skip_single_threaded, skip_non_native, skip_libc, skip_stage1, @@ -501,7 +502,7 @@ pub fn build(b: *Builder) !void { "std", "Run the standard library tests", modes, - false, + skip_single_threaded, skip_non_native, skip_libc, skip_stage1, diff --git a/ci/drone/linux_script_test b/ci/drone/linux_script_test index 539a372f30..b748745da9 100755 --- a/ci/drone/linux_script_test +++ b/ci/drone/linux_script_test @@ -17,11 +17,11 @@ case "$1" in ;; 3) # ReleaseSafe - ./build/zig build $BUILD_FLAGS test-std -Dskip-debug -Dskip-release-fast -Dskip-release-small -Dskip-non-native + ./build/zig build $BUILD_FLAGS test-std -Dskip-debug -Dskip-release-fast -Dskip-release-small -Dskip-non-native -Dskip-single-threaded ;; 4) # ReleaseFast - ./build/zig build $BUILD_FLAGS test-std -Dskip-debug -Dskip-release-safe -Dskip-release-small -Dskip-non-native + ./build/zig build $BUILD_FLAGS test-std -Dskip-debug -Dskip-release-safe -Dskip-release-small -Dskip-non-native -Dskip-single-threaded ;; 5) # ReleaseSmall From b82cccc9e9b2230097f81fecec12ac0fdae97518 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Jun 2022 15:43:21 -0700 Subject: [PATCH 1706/2031] Sema: fix alignment of element ptr result type --- src/Sema.zig | 89 ++++++++++++++++++++++++++++++----------- src/type.zig | 14 ------- test/behavior/align.zig | 30 +++++++------- 3 files changed, 81 insertions(+), 52 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 9f45e7ffd0..a70f372d8a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -10938,6 +10938,7 @@ fn analyzePtrArithmetic( const new_ptr_ty = t: { // Calculate the new pointer alignment. + // This code is duplicated in `elemPtrType`. if (ptr_info.@"align" == 0) { // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness. break :t ptr_ty; @@ -18617,20 +18618,20 @@ fn elemPtr( .Pointer => { // In all below cases, we have to deref the ptr operand to get the actual indexable pointer. const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); - const result_ty = try indexable_ty.elemPtrType(sema.arena, sema.mod); switch (indexable_ty.ptrSize()) { .Slice => return sema.elemPtrSlice(block, indexable_ptr_src, indexable, elem_index_src, elem_index), .Many, .C => { const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_ptr_src, indexable); 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 indexable_ptr_src; const index_val = maybe_index_val orelse break :rs elem_index_src; const index = @intCast(usize, index_val.toUnsignedInt(target)); const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, sema.mod); + const result_ty = try sema.elemPtrType(indexable_ty, index); return sema.addConstant(result_ty, elem_ptr); }; + const result_ty = try sema.elemPtrType(indexable_ty, null); try sema.requireRuntimeBlock(block, runtime_src); return block.addPtrElemPtr(indexable, elem_index, result_ty); @@ -18883,29 +18884,29 @@ fn elemPtrArray( const array_sent = array_ty.sentinel() != null; const array_len = array_ty.arrayLen(); const array_len_s = array_len + @boolToInt(array_sent); - const elem_ptr_ty = try array_ptr_ty.elemPtrType(sema.arena, sema.mod); if (array_len_s == 0) { return sema.fail(block, elem_index_src, "indexing into empty array", .{}); } const maybe_undef_array_ptr_val = try sema.resolveMaybeUndefVal(block, array_ptr_src, array_ptr); - // index must be defined since it can index out of bounds - 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(target)); + // The index must not be undefined since it can be out of bounds. + const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { + const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(target)); if (index >= array_len_s) { const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else ""; return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); } - } + break :o index; + } else null; + + const elem_ptr_ty = try sema.elemPtrType(array_ptr_ty, offset); + if (maybe_undef_array_ptr_val) |array_ptr_val| { if (array_ptr_val.isUndef()) { return sema.addConstUndef(elem_ptr_ty); } - if (maybe_index_val) |index_val| { - const index = @intCast(usize, index_val.toUnsignedInt(target)); + if (offset) |index| { const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, index, sema.mod); return sema.addConstant(elem_ptr_ty, elem_ptr); } @@ -18932,14 +18933,14 @@ fn elemPtrArray( const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src; try sema.requireRuntimeBlock(block, runtime_src); - if (block.wantSafety()) { - // Runtime check is only needed if unable to comptime check - if (maybe_index_val == null) { - const len_inst = try sema.addIntUnsigned(Type.usize, array_len); - const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt; - try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op); - } + + // Runtime check is only needed if unable to comptime check. + if (block.wantSafety() and offset == null) { + const len_inst = try sema.addIntUnsigned(Type.usize, array_len); + const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt; + try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op); } + return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty); } @@ -19007,11 +19008,15 @@ fn elemPtrSlice( const target = sema.mod.getTarget(); const slice_ty = sema.typeOf(slice); const slice_sent = slice_ty.sentinel() != null; - const elem_ptr_ty = try slice_ty.elemPtrType(sema.arena, sema.mod); const maybe_undef_slice_val = try sema.resolveMaybeUndefVal(block, slice_src, slice); - // index must be defined since it can index out of bounds - const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); + // The index must not be undefined since it can be out of bounds. + const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { + const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(target)); + break :o index; + } else null; + + const elem_ptr_ty = try sema.elemPtrType(slice_ty, null); if (maybe_undef_slice_val) |slice_val| { if (slice_val.isUndef()) { @@ -19022,8 +19027,7 @@ fn elemPtrSlice( if (slice_len_s == 0) { return sema.fail(block, elem_index_src, "indexing into empty slice", .{}); } - if (maybe_index_val) |index_val| { - const index = @intCast(usize, index_val.toUnsignedInt(target)); + if (offset) |index| { if (index >= slice_len_s) { const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); @@ -25364,3 +25368,42 @@ fn compareVector( } return Value.Tag.aggregate.create(sema.arena, result_data); } + +/// 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. +/// This code is duplicated in `analyzePtrArithmetic`. +fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type { + const ptr_info = ptr_ty.ptrInfo().data; + const elem_ty = ptr_ty.elemType2(); + const allow_zero = ptr_info.@"allowzero" and (offset orelse 0) == 0; + const alignment: u32 = a: { + // Calculate the new pointer alignment. + if (ptr_info.@"align" == 0) { + // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness. + break :a 0; + } + // If the addend is not a comptime-known value we can still count on + // it being a multiple of the type size. + const target = sema.mod.getTarget(); + const elem_size = elem_ty.abiSize(target); + const addend = if (offset) |off| elem_size * off else elem_size; + + // The resulting pointer is aligned to the lcd between the offset (an + // arbitrary number) and the alignment factor (always a power of two, + // non zero). + const new_align = @as(u32, 1) << @intCast(u5, @ctz(u64, addend | ptr_info.@"align")); + break :a new_align; + }; + return try Type.ptr(sema.arena, sema.mod, .{ + .pointee_type = elem_ty, + .mutable = ptr_info.mutable, + .@"addrspace" = ptr_info.@"addrspace", + .@"allowzero" = allow_zero, + .@"volatile" = ptr_info.@"volatile", + .@"align" = alignment, + }); +} diff --git a/src/type.zig b/src/type.zig index 94fcd0a96c..fc9841e28b 100644 --- a/src/type.zig +++ b/src/type.zig @@ -4177,20 +4177,6 @@ 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, mod: *Module) !Type { - return try Type.ptr(arena, mod, .{ - .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/test/behavior/align.zig b/test/behavior/align.zig index d77a2153cc..4d21aac483 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -2,6 +2,7 @@ const std = @import("std"); const expect = std.testing.expect; const builtin = @import("builtin"); const native_arch = builtin.target.cpu.arch; +const assert = std.debug.assert; var foo: u8 align(4) = 100; @@ -375,38 +376,37 @@ test "function callconv expression depends on generic parameter" { } test "runtime known array index has best alignment possible" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO // take full advantage of over-alignment var array align(4) = [_]u8{ 1, 2, 3, 4 }; - try expect(@TypeOf(&array[0]) == *align(4) u8); - try expect(@TypeOf(&array[1]) == *u8); - try expect(@TypeOf(&array[2]) == *align(2) u8); - try expect(@TypeOf(&array[3]) == *u8); + comptime assert(@TypeOf(&array[0]) == *align(4) u8); + comptime assert(@TypeOf(&array[1]) == *u8); + comptime assert(@TypeOf(&array[2]) == *align(2) u8); + comptime assert(@TypeOf(&array[3]) == *u8); // because align is too small but we still figure out to use 2 var bigger align(2) = [_]u64{ 1, 2, 3, 4 }; - try expect(@TypeOf(&bigger[0]) == *align(2) u64); - try expect(@TypeOf(&bigger[1]) == *align(2) u64); - try expect(@TypeOf(&bigger[2]) == *align(2) u64); - try expect(@TypeOf(&bigger[3]) == *align(2) u64); + comptime assert(@TypeOf(&bigger[0]) == *align(2) u64); + comptime assert(@TypeOf(&bigger[1]) == *align(2) u64); + comptime assert(@TypeOf(&bigger[2]) == *align(2) u64); + comptime assert(@TypeOf(&bigger[3]) == *align(2) u64); // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2 var smaller align(2) = [_]u32{ 1, 2, 3, 4 }; var runtime_zero: usize = 0; - comptime try expect(@TypeOf(smaller[runtime_zero..]) == []align(2) u32); - comptime try expect(@TypeOf(smaller[runtime_zero..].ptr) == [*]align(2) u32); + comptime assert(@TypeOf(smaller[runtime_zero..]) == []align(2) u32); + comptime assert(@TypeOf(smaller[runtime_zero..].ptr) == [*]align(2) u32); try testIndex(smaller[runtime_zero..].ptr, 0, *align(2) u32); try testIndex(smaller[runtime_zero..].ptr, 1, *align(2) u32); try testIndex(smaller[runtime_zero..].ptr, 2, *align(2) u32); try testIndex(smaller[runtime_zero..].ptr, 3, *align(2) u32); // has to use ABI alignment because index known at runtime only - try testIndex2(array[runtime_zero..].ptr, 0, *u8); - try testIndex2(array[runtime_zero..].ptr, 1, *u8); - try testIndex2(array[runtime_zero..].ptr, 2, *u8); - try testIndex2(array[runtime_zero..].ptr, 3, *u8); + try testIndex2(&array, 0, *u8); + try testIndex2(&array, 1, *u8); + try testIndex2(&array, 2, *u8); + try testIndex2(&array, 3, *u8); } fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) !void { comptime try expect(@TypeOf(&smaller[index]) == T); From 288e89b606b46328a5ab358b2eef2c5dc277bc8f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Jun 2022 16:45:28 -0700 Subject: [PATCH 1707/2031] Sema: fix compiler crash with comptime arithmetic involving `@ptrToInt` --- src/Sema.zig | 52 +++++++++++++++++++++++++++---------- test/behavior/align.zig | 11 +++----- test/behavior/bugs/1741.zig | 5 +++- 3 files changed, 47 insertions(+), 21 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 88847ced0a..fd3dab4866 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1540,6 +1540,24 @@ fn resolveMaybeUndefVal( } } +/// Value Tag `variable` results in `null`. +/// Value Tag `undef` results in the Value. +/// Value Tag `generic_poison` causes `error.GenericPoison` to be returned. +/// Value Tag `decl_ref` and `decl_ref_mut` or any nested such value results in `null`. +fn resolveMaybeUndefValIntable( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + inst: Air.Inst.Ref, +) CompileError!?Value { + const val = (try sema.resolveMaybeUndefValAllowVariables(block, src, inst)) orelse return null; + switch (val.tag()) { + .variable, .decl_ref, .decl_ref_mut => return null, + .generic_poison => return error.GenericPoison, + else => return val, + } +} + /// Returns all Value tags including `variable` and `undef`. fn resolveMaybeUndefValAllowVariables( sema: *Sema, @@ -9302,19 +9320,27 @@ fn zirBitwise( return sema.fail(block, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag()), @tagName(rhs_ty.zigTypeTag()) }); } - if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { - if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { - const result_val = switch (air_tag) { - .bit_and => try lhs_val.bitwiseAnd(rhs_val, resolved_type, sema.arena, target), - .bit_or => try lhs_val.bitwiseOr(rhs_val, resolved_type, sema.arena, target), - .xor => try lhs_val.bitwiseXor(rhs_val, resolved_type, sema.arena, target), - else => unreachable, - }; - return sema.addConstant(resolved_type, result_val); + const runtime_src = runtime: { + // TODO: ask the linker what kind of relocations are available, and + // in some cases emit a Value that means "this decl's address AND'd with this operand". + if (try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs)) |lhs_val| { + if (try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs)) |rhs_val| { + const result_val = switch (air_tag) { + .bit_and => try lhs_val.bitwiseAnd(rhs_val, resolved_type, sema.arena, target), + .bit_or => try lhs_val.bitwiseOr(rhs_val, resolved_type, sema.arena, target), + .xor => try lhs_val.bitwiseXor(rhs_val, resolved_type, sema.arena, target), + else => unreachable, + }; + return sema.addConstant(resolved_type, result_val); + } else { + break :runtime rhs_src; + } + } else { + break :runtime lhs_src; } - } + }; - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, runtime_src); return block.addBinOp(air_tag, casted_lhs, casted_rhs); } @@ -10163,8 +10189,8 @@ fn analyzeArithmetic( const mod = sema.mod; const target = mod.getTarget(); - const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs); - const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs); + const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs); + const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs); const rs: struct { src: LazySrcLoc, air_tag: Air.Inst.Tag } = rs: { switch (zir_tag) { .add => { diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 4d21aac483..ad35db2171 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -502,12 +502,9 @@ test "align(@alignOf(T)) T does not force resolution of T" { test "align(N) on functions" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO // function alignment is a compile error on wasm32/wasm64 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest; @@ -531,5 +528,5 @@ test "comptime alloc alignment" { comptime var bytes2 align(256) = [_]u8{0}; var bytes2_addr = @ptrToInt(&bytes2); - try std.testing.expect(bytes2_addr & 0xff == 0); + try expect(bytes2_addr & 0xff == 0); } diff --git a/test/behavior/bugs/1741.zig b/test/behavior/bugs/1741.zig index b91ac8e2fb..63de6e83dc 100644 --- a/test/behavior/bugs/1741.zig +++ b/test/behavior/bugs/1741.zig @@ -2,7 +2,10 @@ const std = @import("std"); const builtin = @import("builtin"); test "fixed" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + const x: f32 align(128) = 12.34; try std.testing.expect(@ptrToInt(&x) % 128 == 0); } From e498fb155051f548071da1a13098b8793f527275 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 1 Jun 2022 23:44:23 +0200 Subject: [PATCH 1708/2031] tapi: sync with upstream gitrev kubkon/zig-yaml 8cf8dc3bb901fac8189f441392fc0989ad14cf71 Calculate line and col info indexed by token index. We can then re-use this info to track current column number (aka indentation level) of each "key:value" pair (map) or "- element" (list). This significantly cleans up the code, and leads naturally to handling of unindented lists in tbd files. --- src/link/tapi/Tokenizer.zig | 57 ++++++++---- src/link/tapi/parse.zig | 162 ++++++++++++++++------------------- src/link/tapi/parse/test.zig | 6 +- src/link/tapi/yaml.zig | 75 ++++++++++++++++ 4 files changed, 190 insertions(+), 110 deletions(-) diff --git a/src/link/tapi/Tokenizer.zig b/src/link/tapi/Tokenizer.zig index 37fcedbfce..d28700a02c 100644 --- a/src/link/tapi/Tokenizer.zig +++ b/src/link/tapi/Tokenizer.zig @@ -11,9 +11,6 @@ pub const Token = struct { id: Id, start: usize, end: usize, - // Count of spaces/tabs. - // Only active for .Space and .Tab tokens. - count: ?usize = null, pub const Id = enum { Eof, @@ -87,8 +84,8 @@ pub fn next(self: *Tokenizer) Token { var state: union(enum) { Start, NewLine, - Space: usize, - Tab: usize, + Space, + Tab, Hyphen: usize, Dot: usize, Literal, @@ -99,10 +96,10 @@ pub fn next(self: *Tokenizer) Token { switch (state) { .Start => switch (c) { ' ' => { - state = .{ .Space = 1 }; + state = .Space; }, '\t' => { - state = .{ .Tab = 1 }; + state = .Tab; }, '\n' => { result.id = .NewLine; @@ -182,23 +179,17 @@ pub fn next(self: *Tokenizer) Token { state = .Literal; }, }, - .Space => |*count| switch (c) { - ' ' => { - count.* += 1; - }, + .Space => switch (c) { + ' ' => {}, else => { result.id = .Space; - result.count = count.*; break; }, }, - .Tab => |*count| switch (c) { - ' ' => { - count.* += 1; - }, + .Tab => switch (c) { + '\t' => {}, else => { result.id = .Tab; - result.count = count.*; break; }, }, @@ -272,10 +263,18 @@ fn testExpected(source: []const u8, expected: []const Token.Id) !void { .buffer = source, }; + var token_len: usize = 0; for (expected) |exp| { + token_len += 1; const token = tokenizer.next(); try testing.expectEqual(exp, token.id); } + + while (tokenizer.next().id != .Eof) { + token_len += 1; // consume all tokens + } + + try testing.expectEqual(expected.len, token_len); } test "empty doc" { @@ -376,7 +375,7 @@ test "inline mapped sequence of values" { }); } -test "part of tdb" { +test "part of tbd" { try testExpected( \\--- !tapi-tbd \\tbd-version: 4 @@ -437,3 +436,25 @@ test "part of tdb" { .Eof, }); } + +test "Unindented list" { + try testExpected( + \\b: + \\- foo: 1 + \\c: 1 + , &[_]Token.Id{ + .Literal, + .MapValueInd, + .NewLine, + .SeqItemInd, + .Literal, + .MapValueInd, + .Space, + .Literal, + .NewLine, + .Literal, + .MapValueInd, + .Space, + .Literal, + }); +} diff --git a/src/link/tapi/parse.zig b/src/link/tapi/parse.zig index 0c40f613dc..00c93b1f32 100644 --- a/src/link/tapi/parse.zig +++ b/src/link/tapi/parse.zig @@ -42,7 +42,7 @@ pub const Node = struct { .doc => @fieldParentPtr(Node.Doc, "base", self).deinit(allocator), .map => @fieldParentPtr(Node.Map, "base", self).deinit(allocator), .list => @fieldParentPtr(Node.List, "base", self).deinit(allocator), - .value => {}, + .value => @fieldParentPtr(Node.Value, "base", self).deinit(allocator), } } @@ -82,8 +82,8 @@ pub const Node = struct { options: std.fmt.FormatOptions, writer: anytype, ) !void { - _ = fmt; _ = options; + _ = fmt; if (self.directive) |id| { try std.fmt.format(writer, "{{ ", .{}); const directive = self.base.tree.tokens[id]; @@ -127,8 +127,8 @@ pub const Node = struct { options: std.fmt.FormatOptions, writer: anytype, ) !void { - _ = fmt; _ = options; + _ = fmt; try std.fmt.format(writer, "{{ ", .{}); for (self.values.items) |entry| { const key = self.base.tree.tokens[entry.key]; @@ -163,8 +163,8 @@ pub const Node = struct { options: std.fmt.FormatOptions, writer: anytype, ) !void { - _ = fmt; _ = options; + _ = fmt; try std.fmt.format(writer, "[ ", .{}); for (self.values.items) |node| { try std.fmt.format(writer, "{}, ", .{node}); @@ -180,14 +180,19 @@ pub const Node = struct { pub const base_tag: Node.Tag = .value; + pub fn deinit(self: *Value, allocator: Allocator) void { + _ = self; + _ = allocator; + } + pub fn format( self: *const Value, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype, ) !void { - _ = fmt; _ = options; + _ = fmt; const start = self.base.tree.tokens[self.start.?]; const end = self.base.tree.tokens[self.end.?]; return std.fmt.format(writer, "{s}", .{ @@ -197,10 +202,16 @@ pub const Node = struct { }; }; +pub const LineCol = struct { + line: usize, + col: usize, +}; + pub const Tree = struct { allocator: Allocator, source: []const u8, tokens: []Token, + line_cols: std.AutoHashMap(TokenIndex, LineCol), docs: std.ArrayListUnmanaged(*Node) = .{}, pub fn init(allocator: Allocator) Tree { @@ -208,11 +219,13 @@ pub const Tree = struct { .allocator = allocator, .source = undefined, .tokens = undefined, + .line_cols = std.AutoHashMap(TokenIndex, LineCol).init(allocator), }; } pub fn deinit(self: *Tree) void { self.allocator.free(self.tokens); + self.line_cols.deinit(); for (self.docs.items) |doc| { doc.deinit(self.allocator); self.allocator.destroy(doc); @@ -223,12 +236,29 @@ pub const Tree = struct { pub fn parse(self: *Tree, source: []const u8) !void { var tokenizer = Tokenizer{ .buffer = source }; var tokens = std.ArrayList(Token).init(self.allocator); - errdefer tokens.deinit(); + defer tokens.deinit(); + + var line: usize = 0; + var prev_line_last_col: usize = 0; while (true) { const token = tokenizer.next(); + const tok_id = tokens.items.len; try tokens.append(token); - if (token.id == .Eof) break; + + try self.line_cols.putNoClobber(tok_id, .{ + .line = line, + .col = token.start - prev_line_last_col, + }); + + switch (token.id) { + .Eof => break, + .NewLine => { + line += 1; + prev_line_last_col = token.end; + }, + else => {}, + } } self.source = source; @@ -239,15 +269,12 @@ pub const Tree = struct { .allocator = self.allocator, .tree = self, .token_it = &it, + .line_cols = &self.line_cols, }; - defer parser.deinit(); - - try parser.scopes.append(self.allocator, .{ - .indent = 0, - }); while (true) { if (parser.token_it.peek() == null) return; + const pos = parser.token_it.pos; const token = parser.token_it.next(); @@ -269,22 +296,12 @@ const Parser = struct { allocator: Allocator, tree: *Tree, token_it: *TokenIterator, - scopes: std.ArrayListUnmanaged(Scope) = .{}, - - const Scope = struct { - indent: usize, - }; - - fn deinit(self: *Parser) void { - self.scopes.deinit(self.allocator); - } + line_cols: *const std.AutoHashMap(TokenIndex, LineCol), fn doc(self: *Parser, start: TokenIndex) ParseError!*Node.Doc { const node = try self.allocator.create(Node.Doc); errdefer self.allocator.destroy(node); - node.* = .{ - .start = start, - }; + node.* = .{ .start = start }; node.base.tree = self.tree; self.token_it.seekTo(start); @@ -346,19 +363,24 @@ const Parser = struct { fn map(self: *Parser, start: TokenIndex) ParseError!*Node.Map { const node = try self.allocator.create(Node.Map); errdefer self.allocator.destroy(node); - node.* = .{ - .start = start, - }; + node.* = .{ .start = start }; node.base.tree = self.tree; self.token_it.seekTo(start); log.debug("Map start: {}, {}", .{ start, self.tree.tokens[start] }); - log.debug("Current scope: {}", .{self.scopes.items[self.scopes.items.len - 1]}); + + const col = self.getCol(start); while (true) { + self.eatCommentsAndSpace(); + // Parse key. const key_pos = self.token_it.pos; + if (self.getCol(key_pos) != col) { + break; + } + const key = self.token_it.next(); switch (key.id) { .Literal => {}, @@ -372,13 +394,13 @@ const Parser = struct { // Separator _ = try self.expectToken(.MapValueInd); - self.eatCommentsAndSpace(); // Parse value. const value: *Node = value: { if (self.eatToken(.NewLine)) |_| { + self.eatCommentsAndSpace(); + // Explicit, complex value such as list or map. - try self.openScope(); const value_pos = self.token_it.pos; const value = self.token_it.next(); switch (value.id) { @@ -398,6 +420,8 @@ const Parser = struct { }, } } else { + self.eatCommentsAndSpace(); + const value_pos = self.token_it.pos; const value = self.token_it.next(); switch (value.id) { @@ -424,11 +448,7 @@ const Parser = struct { .value = value, }); - if (self.eatToken(.NewLine)) |_| { - if (try self.closeScope()) { - break; - } - } + _ = self.eatToken(.NewLine); } node.end = self.token_it.pos - 1; @@ -449,14 +469,18 @@ const Parser = struct { self.token_it.seekTo(start); log.debug("List start: {}, {}", .{ start, self.tree.tokens[start] }); - log.debug("Current scope: {}", .{self.scopes.items[self.scopes.items.len - 1]}); + + const col = self.getCol(start); while (true) { + self.eatCommentsAndSpace(); + + if (self.getCol(self.token_it.pos) != col) { + break; + } _ = self.eatToken(.SeqItemInd) orelse { - _ = try self.closeScope(); break; }; - self.eatCommentsAndSpace(); const pos = self.token_it.pos; const token = self.token_it.next(); @@ -464,9 +488,6 @@ const Parser = struct { switch (token.id) { .Literal, .SingleQuote, .DoubleQuote => { if (self.eatToken(.MapValueInd)) |_| { - if (self.eatToken(.NewLine)) |_| { - try self.openScope(); - } // nested map const map_node = try self.map(pos); break :value &map_node.base; @@ -501,15 +522,12 @@ const Parser = struct { fn list_bracketed(self: *Parser, start: TokenIndex) ParseError!*Node.List { const node = try self.allocator.create(Node.List); errdefer self.allocator.destroy(node); - node.* = .{ - .start = start, - }; + node.* = .{ .start = start }; node.base.tree = self.tree; self.token_it.seekTo(start); log.debug("List start: {}, {}", .{ start, self.tree.tokens[start] }); - log.debug("Current scope: {}", .{self.scopes.items[self.scopes.items.len - 1]}); _ = try self.expectToken(.FlowSeqStart); @@ -556,9 +574,7 @@ const Parser = struct { fn leaf_value(self: *Parser, start: TokenIndex) ParseError!*Node.Value { const node = try self.allocator.create(Node.Value); errdefer self.allocator.destroy(node); - node.* = .{ - .start = start, - }; + node.* = .{ .start = start }; node.base.tree = self.tree; self.token_it.seekTo(start); @@ -625,48 +641,6 @@ const Parser = struct { return node; } - fn openScope(self: *Parser) !void { - const peek = self.token_it.peek() orelse return error.UnexpectedEof; - if (peek.id != .Space and peek.id != .Tab) { - // No need to open scope. - return; - } - const indent = self.token_it.next().count.?; - const prev_scope = self.scopes.items[self.scopes.items.len - 1]; - if (indent < prev_scope.indent) { - return error.MalformedYaml; - } - - log.debug("Opening scope...", .{}); - - try self.scopes.append(self.allocator, .{ - .indent = indent, - }); - } - - fn closeScope(self: *Parser) !bool { - const indent = indent: { - const peek = self.token_it.peek() orelse return error.UnexpectedEof; - switch (peek.id) { - .Space, .Tab => { - break :indent self.token_it.next().count.?; - }, - else => { - break :indent 0; - }, - } - }; - - const scope = self.scopes.items[self.scopes.items.len - 1]; - if (indent < scope.indent) { - log.debug("Closing scope...", .{}); - _ = self.scopes.pop(); - return true; - } - - return false; - } - fn eatCommentsAndSpace(self: *Parser) void { while (true) { _ = self.token_it.peek() orelse return; @@ -701,6 +675,14 @@ const Parser = struct { fn expectToken(self: *Parser, id: Token.Id) ParseError!TokenIndex { return self.eatToken(id) orelse error.UnexpectedToken; } + + fn getLine(self: *Parser, index: TokenIndex) usize { + return self.line_cols.get(index).?.line; + } + + fn getCol(self: *Parser, index: TokenIndex) usize { + return self.line_cols.get(index).?.col; + } }; test { diff --git a/src/link/tapi/parse/test.zig b/src/link/tapi/parse/test.zig index fc0bed5df6..b310a5c0bd 100644 --- a/src/link/tapi/parse/test.zig +++ b/src/link/tapi/parse/test.zig @@ -1,8 +1,10 @@ const std = @import("std"); const mem = std.mem; const testing = std.testing; -const Tree = @import("../parse.zig").Tree; -const Node = @import("../parse.zig").Node; +const parse = @import("../parse.zig"); + +const Node = parse.Node; +const Tree = parse.Tree; test "explicit doc" { const source = diff --git a/src/link/tapi/yaml.zig b/src/link/tapi/yaml.zig index 270ae2d8ce..eed5cb639f 100644 --- a/src/link/tapi/yaml.zig +++ b/src/link/tapi/yaml.zig @@ -511,6 +511,81 @@ test "simple map untyped" { try testing.expectEqual(map.get("a").?.int, 0); } +test "simple map untyped with a list of maps" { + const source = + \\a: 0 + \\b: + \\ - foo: 1 + \\ bar: 2 + \\ - foo: 3 + \\ bar: 4 + \\c: 1 + ; + + var yaml = try Yaml.load(testing.allocator, source); + defer yaml.deinit(); + + try testing.expectEqual(yaml.docs.items.len, 1); + + const map = yaml.docs.items[0].map; + try testing.expect(map.contains("a")); + try testing.expect(map.contains("b")); + try testing.expect(map.contains("c")); + try testing.expectEqual(map.get("a").?.int, 0); + try testing.expectEqual(map.get("c").?.int, 1); + try testing.expectEqual(map.get("b").?.list[0].map.get("foo").?.int, 1); + try testing.expectEqual(map.get("b").?.list[0].map.get("bar").?.int, 2); + try testing.expectEqual(map.get("b").?.list[1].map.get("foo").?.int, 3); + try testing.expectEqual(map.get("b").?.list[1].map.get("bar").?.int, 4); +} + +test "simple map untyped with a list of maps. no indent" { + const source = + \\b: + \\- foo: 1 + \\c: 1 + ; + + var yaml = try Yaml.load(testing.allocator, source); + defer yaml.deinit(); + + try testing.expectEqual(yaml.docs.items.len, 1); + + const map = yaml.docs.items[0].map; + try testing.expect(map.contains("b")); + try testing.expect(map.contains("c")); + try testing.expectEqual(map.get("c").?.int, 1); + try testing.expectEqual(map.get("b").?.list[0].map.get("foo").?.int, 1); +} + +test "simple map untyped with a list of maps. no indent 2" { + const source = + \\a: 0 + \\b: + \\- foo: 1 + \\ bar: 2 + \\- foo: 3 + \\ bar: 4 + \\c: 1 + ; + + var yaml = try Yaml.load(testing.allocator, source); + defer yaml.deinit(); + + try testing.expectEqual(yaml.docs.items.len, 1); + + const map = yaml.docs.items[0].map; + try testing.expect(map.contains("a")); + try testing.expect(map.contains("b")); + try testing.expect(map.contains("c")); + try testing.expectEqual(map.get("a").?.int, 0); + try testing.expectEqual(map.get("c").?.int, 1); + try testing.expectEqual(map.get("b").?.list[0].map.get("foo").?.int, 1); + try testing.expectEqual(map.get("b").?.list[0].map.get("bar").?.int, 2); + try testing.expectEqual(map.get("b").?.list[1].map.get("foo").?.int, 3); + try testing.expectEqual(map.get("b").?.list[1].map.get("bar").?.int, 4); +} + test "simple map typed" { const source = \\a: 0 From 4fdacca51231668b72bf302ce97335d2a8d93128 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Thu, 2 Jun 2022 20:19:18 +0200 Subject: [PATCH 1709/2031] stage2 ARM: rework cmp in preparation for switch --- src/arch/arm/CodeGen.zig | 336 ++++++++++++++++++++++++++++----------- 1 file changed, 241 insertions(+), 95 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index b5d0dcbef4..b6b364ddc4 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -813,6 +813,17 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live self.register_manager.getRegAssumeFree(reg, inst); } }, + .register_c_flag, + .register_v_flag, + => |reg| { + if (self.register_manager.isRegFree(reg)) { + self.register_manager.getRegAssumeFree(reg, inst); + } + self.cpsr_flags_inst = inst; + }, + .cpsr_flags => { + self.cpsr_flags_inst = inst; + }, else => {}, } } @@ -2462,56 +2473,28 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const reg_lock = self.register_manager.lockRegAssumeUnused(reg); defer self.register_manager.unlockReg(reg_lock); - switch (index) { - 0 => { - // get wrapped value: return register - break :result MCValue{ .register = reg }; - }, - 1 => { - // get overflow bit: return C or V flag - if (self.liveness.operandDies(inst, 0)) { - self.cpsr_flags_inst = inst; + const field: MCValue = switch (index) { + // get wrapped value: return register + 0 => MCValue{ .register = reg }, - const cond: Condition = switch (mcv) { - .register_c_flag => .cs, - .register_v_flag => .vs, - else => unreachable, - }; + // get overflow bit: return C or V flag + 1 => MCValue{ .cpsr_flags = switch (mcv) { + .register_c_flag => .cs, + .register_v_flag => .vs, + else => unreachable, + } }, - break :result MCValue{ .cpsr_flags = cond }; - } else { - const dest_reg = try self.register_manager.allocReg(null, gp); - - // mov reg, #0 - _ = try self.addInst(.{ - .tag = .mov, - .data = .{ .rr_op = .{ - .rd = dest_reg, - .rn = .r0, - .op = Instruction.Operand.fromU32(0).?, - } }, - }); - - // C flag: movcs reg, #1 - // V flag: movvs reg, #1 - _ = try self.addInst(.{ - .tag = .mov, - .cond = switch (mcv) { - .register_c_flag => .cs, - .register_v_flag => .vs, - else => unreachable, - }, - .data = .{ .rr_op = .{ - .rd = dest_reg, - .rn = .r0, - .op = Instruction.Operand.fromU32(1).?, - } }, - }); - - break :result MCValue{ .register = dest_reg }; - } - }, else => unreachable, + }; + + if (self.liveness.operandDies(inst, 0)) { + break :result field; + } else { + // Copy to new register + const dest_reg = try self.register_manager.allocReg(null, gp); + try self.genSetReg(struct_ty.structFieldType(index), dest_reg, field); + + break :result MCValue{ .register = dest_reg }; } }, else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), @@ -3570,55 +3553,91 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - const lhs_ty = self.air.typeOf(bin_op.lhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); - var int_buffer: Type.Payload.Bits = undefined; - const int_ty = switch (lhs_ty.zigTypeTag()) { - .Optional => blk: { - var opt_buffer: Type.Payload.ElemType = undefined; - const payload_ty = lhs_ty.optionalChild(&opt_buffer); - if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { - break :blk Type.initTag(.u1); - } else if (lhs_ty.isPtrLikeOptional()) { - break :blk Type.usize; - } else { - return self.fail("TODO ARM cmp non-pointer optionals", .{}); - } - }, - .Float => return self.fail("TODO ARM cmp floats", .{}), - .Enum => lhs_ty.intTagType(&int_buffer), - .Int => lhs_ty, - .Bool => Type.initTag(.u1), - .Pointer => Type.usize, - .ErrorSet => Type.initTag(.u16), - else => unreachable, - }; - - const int_info = int_ty.intInfo(self.target.*); - if (int_info.bits <= 32) { - try self.spillCompareFlagsIfOccupied(); - self.cpsr_flags_inst = inst; - - _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, BinOpMetadata{ - .lhs = bin_op.lhs, - .rhs = bin_op.rhs, - .inst = inst, - }); - - break :result switch (int_info.signedness) { - .signed => MCValue{ .cpsr_flags = Condition.fromCompareOperatorSigned(op) }, - .unsigned => MCValue{ .cpsr_flags = Condition.fromCompareOperatorUnsigned(op) }, - }; - } else { - return self.fail("TODO ARM cmp for ints > 32 bits", .{}); - } + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { + const operands: BinOpOperands = .{ .inst = .{ + .inst = inst, + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + } }; + break :blk try self.cmp(operands, lhs_ty, op); }; + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +const BinOpOperands = union(enum) { + inst: struct { + inst: Air.Inst.Index, + lhs: Air.Inst.Ref, + rhs: Air.Inst.Ref, + }, + mcv: struct { + lhs: MCValue, + rhs: MCValue, + }, +}; + +fn cmp( + self: *Self, + operands: BinOpOperands, + lhs_ty: Type, + op: math.CompareOperator, +) !MCValue { + var int_buffer: Type.Payload.Bits = undefined; + const int_ty = switch (lhs_ty.zigTypeTag()) { + .Optional => blk: { + var opt_buffer: Type.Payload.ElemType = undefined; + const payload_ty = lhs_ty.optionalChild(&opt_buffer); + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + break :blk Type.initTag(.u1); + } else if (lhs_ty.isPtrLikeOptional()) { + break :blk Type.usize; + } else { + return self.fail("TODO ARM cmp non-pointer optionals", .{}); + } + }, + .Float => return self.fail("TODO ARM cmp floats", .{}), + .Enum => lhs_ty.intTagType(&int_buffer), + .Int => lhs_ty, + .Bool => Type.initTag(.u1), + .Pointer => Type.usize, + .ErrorSet => Type.initTag(.u16), + else => unreachable, + }; + + const int_info = int_ty.intInfo(self.target.*); + if (int_info.bits <= 32) { + try self.spillCompareFlagsIfOccupied(); + + switch (operands) { + .inst => |inst_op| { + const metadata: BinOpMetadata = .{ + .inst = inst_op.inst, + .lhs = inst_op.lhs, + .rhs = inst_op.rhs, + }; + const lhs = try self.resolveInst(inst_op.lhs); + const rhs = try self.resolveInst(inst_op.rhs); + + self.cpsr_flags_inst = inst_op.inst; + _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, metadata); + }, + .mcv => |mcv_op| { + _ = try self.binOp(.cmp_eq, mcv_op.lhs, mcv_op.rhs, int_ty, int_ty, null); + }, + } + + return switch (int_info.signedness) { + .signed => MCValue{ .cpsr_flags = Condition.fromCompareOperatorSigned(op) }, + .unsigned => MCValue{ .cpsr_flags = Condition.fromCompareOperatorUnsigned(op) }, + }; + } else { + return self.fail("TODO ARM cmp for ints > 32 bits", .{}); + } +} + fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { _ = inst; return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); @@ -4081,10 +4100,137 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { const pl_op = self.air.instructions.items(.data)[inst].pl_op; - const condition = pl_op.operand; - _ = condition; - return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch}); - // return self.finishAir(inst, .dead, .{ condition, .none, .none }); + const condition_ty = self.air.typeOf(pl_op.operand); + const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload); + const liveness = try self.liveness.getSwitchBr( + self.gpa, + inst, + switch_br.data.cases_len + 1, + ); + defer self.gpa.free(liveness.deaths); + + // If the condition dies here in this switch instruction, process + // that death now instead of later as this has an effect on + // whether it needs to be spilled in the branches + if (self.liveness.operandDies(inst, 0)) { + const op_int = @enumToInt(pl_op.operand); + if (op_int >= Air.Inst.Ref.typed_value_map.len) { + const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); + self.processDeath(op_index); + } + } + + var extra_index: usize = switch_br.end; + var case_i: u32 = 0; + while (case_i < switch_br.data.cases_len) : (case_i += 1) { + const case = self.air.extraData(Air.SwitchBr.Case, extra_index); + const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); + assert(items.len > 0); + const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; + extra_index = case.end + items.len + case_body.len; + + var relocs = try self.gpa.alloc(u32, items.len); + defer self.gpa.free(relocs); + + if (items.len == 1) { + const condition = try self.resolveInst(pl_op.operand); + const item = try self.resolveInst(items[0]); + + const operands: BinOpOperands = .{ .mcv = .{ + .lhs = condition, + .rhs = item, + } }; + const cmp_result = try self.cmp(operands, condition_ty, .neq); + + relocs[0] = try self.addInst(.{ + .tag = .b, + .cond = cmp_result.cpsr_flags, + .data = .{ .inst = undefined }, // populated later through performReloc + }); + } else { + return self.fail("TODO switch with multiple items", .{}); + } + + // Capture the state of register and stack allocation state so that we can revert to it. + const parent_next_stack_offset = self.next_stack_offset; + const parent_free_registers = self.register_manager.free_registers; + const parent_cpsr_flags_inst = self.cpsr_flags_inst; + var parent_stack = try self.stack.clone(self.gpa); + defer parent_stack.deinit(self.gpa); + const parent_registers = self.register_manager.registers; + + try self.branch_stack.append(.{}); + errdefer { + _ = self.branch_stack.pop(); + } + + try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len); + for (liveness.deaths[case_i]) |operand| { + self.processDeath(operand); + } + try self.genBody(case_body); + + // Revert to the previous register and stack allocation state. + var saved_case_branch = self.branch_stack.pop(); + defer saved_case_branch.deinit(self.gpa); + + self.register_manager.registers = parent_registers; + self.cpsr_flags_inst = parent_cpsr_flags_inst; + self.stack.deinit(self.gpa); + self.stack = parent_stack; + parent_stack = .{}; + + self.next_stack_offset = parent_next_stack_offset; + self.register_manager.free_registers = parent_free_registers; + + for (relocs) |reloc| { + try self.performReloc(reloc); + } + } + + if (switch_br.data.else_body_len > 0) { + const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; + + // Capture the state of register and stack allocation state so that we can revert to it. + const parent_next_stack_offset = self.next_stack_offset; + const parent_free_registers = self.register_manager.free_registers; + const parent_cpsr_flags_inst = self.cpsr_flags_inst; + var parent_stack = try self.stack.clone(self.gpa); + defer parent_stack.deinit(self.gpa); + const parent_registers = self.register_manager.registers; + + try self.branch_stack.append(.{}); + errdefer { + _ = self.branch_stack.pop(); + } + + const else_deaths = liveness.deaths.len - 1; + try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len); + for (liveness.deaths[else_deaths]) |operand| { + self.processDeath(operand); + } + try self.genBody(else_body); + + // Revert to the previous register and stack allocation state. + var saved_case_branch = self.branch_stack.pop(); + defer saved_case_branch.deinit(self.gpa); + + self.register_manager.registers = parent_registers; + self.cpsr_flags_inst = parent_cpsr_flags_inst; + self.stack.deinit(self.gpa); + self.stack = parent_stack; + parent_stack = .{}; + + self.next_stack_offset = parent_next_stack_offset; + self.register_manager.free_registers = parent_free_registers; + + // TODO consolidate returned MCValues between prongs and else branch like we do + // in airCondBr. + } + + // We already took care of pl_op.operand earlier, so we're going + // to pass .none here + return self.finishAir(inst, .unreach, .{ .none, .none, .none }); } fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { From 9aae4d57cb6baf409b54d67a8a312624f8ad84b6 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 3 Jun 2022 14:27:45 +0300 Subject: [PATCH 1710/2031] compiler_rt: fix infinite loop --- lib/compiler_rt/log2.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/compiler_rt/log2.zig b/lib/compiler_rt/log2.zig index 53f35c9a80..aa294b33fd 100644 --- a/lib/compiler_rt/log2.zig +++ b/lib/compiler_rt/log2.zig @@ -147,7 +147,8 @@ pub fn __log2x(a: f80) callconv(.C) f80 { } pub fn log2q(a: f128) callconv(.C) f128 { - return math.log2(a); + // TODO: more correct implementation + return log2(@floatCast(f64, a)); } pub fn log2l(x: c_longdouble) callconv(.C) c_longdouble { From 1258b5f7d6fc9b56379c2c671ed55286f7e5b663 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 3 Jun 2022 14:28:10 +0300 Subject: [PATCH 1711/2031] Type: implement elemType2 for anyframe --- src/type.zig | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/type.zig b/src/type.zig index fc9841e28b..a1adec491d 100644 --- a/src/type.zig +++ b/src/type.zig @@ -784,7 +784,7 @@ pub const Type = extern union { .anyframe_T => { if (b.zigTypeTag() != .AnyFrame) return false; - return a.childType().eql(b.childType(), mod); + return a.elemType2().eql(b.elemType2(), mod); }, .empty_struct => { @@ -4125,14 +4125,15 @@ pub const Type = extern union { /// TODO this is deprecated in favor of `childType`. pub const elemType = childType; - /// For *[N]T, returns T. - /// For ?*T, returns T. - /// For ?*[N]T, returns T. - /// For ?[*]T, returns T. - /// For *T, returns T. - /// For [*]T, returns T. - /// For [N]T, returns T. - /// For []T, returns T. + /// For *[N]T, returns T. + /// For ?*T, returns T. + /// For ?*[N]T, returns T. + /// For ?[*]T, returns T. + /// For *T, returns T. + /// For [*]T, returns T. + /// For [N]T, returns T. + /// For []T, returns T. + /// For anyframe->T, returns T. pub fn elemType2(ty: Type) Type { return switch (ty.tag()) { .vector => ty.castTag(.vector).?.data.elem_type, @@ -4173,6 +4174,9 @@ pub const Type = extern union { .optional_single_mut_pointer => ty.castPointer().?.data, .optional_single_const_pointer => ty.castPointer().?.data, + .anyframe_T => ty.castTag(.anyframe_T).?.data, + .@"anyframe" => Type.@"void", + else => unreachable, }; } From 3db4513b33425d34ad9def8af8bd52536a85fecd Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 3 Jun 2022 14:28:56 +0300 Subject: [PATCH 1712/2031] Sema: fix type of alloc --- src/Sema.zig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index fd3dab4866..e56f5f1e03 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -13465,7 +13465,12 @@ fn zirStructInit( } if (is_ref) { - const alloc = try block.addTy(.alloc, resolved_ty); + const target = sema.mod.getTarget(); + const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ + .pointee_type = resolved_ty, + .@"addrspace" = target_util.defaultAddressSpace(target, .local), + }); + const alloc = try block.addTy(.alloc, alloc_ty); const field_ptr = try sema.unionFieldPtr(block, field_src, alloc, field_name, field_src, resolved_ty); try sema.storePtr(block, src, field_ptr, init_inst); return alloc; From 5c65b086d68280d49037ae316db0aba76fe5cd72 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 3 Jun 2022 14:29:24 +0300 Subject: [PATCH 1713/2031] Value: implement {read,write}Value for more types --- src/value.zig | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/value.zig b/src/value.zig index da8f7a2c62..a80d788894 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1174,6 +1174,10 @@ pub const Value = extern union { return; } switch (ty.zigTypeTag()) { + .Void => {}, + .Bool => { + buffer[0] = @boolToInt(val.toBool()); + }, .Int => { var bigint_buffer: BigIntSpace = undefined; const bigint = val.toBigInt(&bigint_buffer, target); @@ -1291,6 +1295,14 @@ pub const Value = extern union { ) Allocator.Error!Value { const target = mod.getTarget(); switch (ty.zigTypeTag()) { + .Void => return Value.@"void", + .Bool => { + if (buffer[0] == 0) { + return Value.@"false"; + } else { + return Value.@"true"; + } + }, .Int => { if (buffer.len == 0) return Value.zero; const int_info = ty.intInfo(target); @@ -1311,7 +1323,7 @@ pub const Value = extern union { 128 => return Value.Tag.float_128.create(arena, floatReadFromMemory(f128, target, buffer)), else => unreachable, }, - .Array => { + .Array, .Vector => { const elem_ty = ty.childType(); const elem_size = elem_ty.abiSize(target); const elems = try arena.alloc(Value, @intCast(usize, ty.arrayLen())); From 8f45e81c840c79097850bb87bbee1303e6d87dd4 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 3 Jun 2022 15:10:37 +0300 Subject: [PATCH 1714/2031] stage2: ignore asm inputs named `_` This is a hacky solution but the entire asm syntax is supposed to be reworked anyways. --- src/codegen/llvm.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index c7282fa22b..aab2dfe51a 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5389,7 +5389,9 @@ pub const FuncGen = struct { } llvm_constraints.appendSliceAssumeCapacity(constraint); - name_map.putAssumeCapacityNoClobber(name, {}); + if (!std.mem.eql(u8, name, "_")) { + name_map.putAssumeCapacityNoClobber(name, {}); + } llvm_param_i += 1; total_i += 1; } From 019537cb2a447fc28365685e71d323092e1830b0 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 3 Jun 2022 15:24:58 +0300 Subject: [PATCH 1715/2031] Sema: `@sizeOf` function should give an error --- src/Sema.zig | 2 +- src/type.zig | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index e56f5f1e03..074a102c95 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -11547,7 +11547,7 @@ fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const ty = try sema.resolveType(block, operand_src, inst_data.operand); switch (ty.zigTypeTag()) { - .Fn => unreachable, + .Fn, .NoReturn, .Undefined, .Null, diff --git a/src/type.zig b/src/type.zig index a1adec491d..ee669df620 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2035,7 +2035,11 @@ pub const Type = extern union { try writer.writeAll("fn("); for (fn_info.param_types) |param_ty, i| { if (i != 0) try writer.writeAll(", "); - try print(param_ty, writer, mod); + if (param_ty.tag() == .generic_poison) { + try writer.writeAll("anytype"); + } else { + try print(param_ty, writer, mod); + } } if (fn_info.is_var_args) { if (fn_info.param_types.len != 0) { @@ -2052,7 +2056,11 @@ pub const Type = extern union { if (fn_info.alignment != 0) { try writer.print("align({d}) ", .{fn_info.alignment}); } - try print(fn_info.return_type, writer, mod); + if (fn_info.return_type.tag() == .generic_poison) { + try writer.writeAll("anytype"); + } else { + try print(fn_info.return_type, writer, mod); + } }, .error_union => { From 4e1aa5d54377aa2b8d1395d666efb6eb935b7917 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 3 Jun 2022 15:25:53 +0300 Subject: [PATCH 1716/2031] Sema: handle the_only_possible_value in beginComptimePtrMutation --- src/Sema.zig | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index 074a102c95..05ce2adb77 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -20405,6 +20405,16 @@ fn beginComptimePtrMutation( .ty = elem_ty, }, + .the_only_possible_value => { + const duped = try sema.arena.create(Value); + duped.* = Value.initTag(.the_only_possible_value); + return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .val = duped, + .ty = elem_ty, + }; + }, + else => unreachable, } }, From 2b93546b39a85e9316c2bf12f336c357577114fa Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 3 Jun 2022 16:10:18 +0300 Subject: [PATCH 1717/2031] Sema: fix initialization of array with comptime only elem type --- src/Sema.zig | 20 +++++++++++--------- test/behavior/array.zig | 9 +++++++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 05ce2adb77..d3ca6a8cc5 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3598,7 +3598,7 @@ fn zirValidateArrayInit( // any ZIR instructions at comptime; we need to do that here. if (array_ty.sentinel()) |sentinel_val| { const array_len_ref = try sema.addIntUnsigned(Type.usize, array_len); - const sentinel_ptr = try sema.elemPtrArray(block, init_src, array_ptr, init_src, array_len_ref); + const sentinel_ptr = try sema.elemPtrArray(block, init_src, array_ptr, init_src, array_len_ref, true); const sentinel = try sema.addConstant(array_ty.childType(), sentinel_val); try sema.storePtr2(block, init_src, sentinel_ptr, init_src, sentinel, init_src, .store); } @@ -7540,7 +7540,7 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const bin_inst = sema.code.instructions.items(.data)[inst].bin; const array_ptr = try sema.resolveInst(bin_inst.lhs); const elem_index = try sema.resolveInst(bin_inst.rhs); - return sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src); + return sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src, false); } fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -7553,7 +7553,7 @@ fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const array_ptr = try sema.resolveInst(extra.lhs); const elem_index = try sema.resolveInst(extra.rhs); - return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src); + return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false); } fn zirElemPtrImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -7565,7 +7565,7 @@ fn zirElemPtrImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data; const array_ptr = try sema.resolveInst(extra.ptr); const elem_index = try sema.addIntUnsigned(Type.usize, extra.index); - return sema.elemPtr(block, src, array_ptr, elem_index, src); + return sema.elemPtr(block, src, array_ptr, elem_index, src, true); } fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -18724,6 +18724,7 @@ fn elemPtr( indexable_ptr: Air.Inst.Ref, elem_index: Air.Inst.Ref, elem_index_src: LazySrcLoc, + init: bool, ) CompileError!Air.Inst.Ref { const indexable_ptr_src = src; // TODO better source location const indexable_ptr_ty = sema.typeOf(indexable_ptr); @@ -18760,11 +18761,11 @@ fn elemPtr( }, .One => { assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable - return sema.elemPtrArray(block, indexable_ptr_src, indexable, elem_index_src, elem_index); + return sema.elemPtrArray(block, indexable_ptr_src, indexable, elem_index_src, elem_index, init); }, } }, - .Array, .Vector => return sema.elemPtrArray(block, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index), + .Array, .Vector => return sema.elemPtrArray(block, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init), .Struct => { // Tuple field access. const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index); @@ -18818,7 +18819,7 @@ fn elemVal( }, .One => { assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable - const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src); + const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false); return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src); }, }, @@ -18999,6 +19000,7 @@ fn elemPtrArray( array_ptr: Air.Inst.Ref, elem_index_src: LazySrcLoc, elem_index: Air.Inst.Ref, + init: bool, ) CompileError!Air.Inst.Ref { const target = sema.mod.getTarget(); const array_ptr_ty = sema.typeOf(array_ptr); @@ -19035,7 +19037,7 @@ fn elemPtrArray( } const valid_rt = try sema.validateRunTimeType(block, elem_index_src, array_ty.elemType2(), false); - if (!valid_rt) { + if (!valid_rt and !init) { const msg = msg: { const msg = try sema.errMsg( block, @@ -20138,7 +20140,7 @@ fn storePtr2( const elem_src = operand_src; // TODO better source location const elem = try tupleField(sema, block, operand_src, uncasted_operand, elem_src, i); const elem_index = try sema.addIntUnsigned(Type.usize, i); - const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src); + const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false); try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store); } return; diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 93de42df67..bccff1edf0 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -573,3 +573,12 @@ test "type coercion of pointer to anon struct literal to pointer to array" { try S.doTheTest(); comptime try S.doTheTest(); } + +test "array with comptime only element type" { + const a = [_]type{ + u32, + i32, + }; + try testing.expect(a[0] == u32); + try testing.expect(a[1] == i32); +} From 1a7b4ddeaedb81255cfa8907958c3cf09dd340ee Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 3 Jun 2022 15:30:25 +0300 Subject: [PATCH 1718/2031] std: disable tests that crash stage2 --- lib/std/compress.zig | 1 + lib/std/event/batch.zig | 1 + lib/std/fmt.zig | 4 ++++ lib/std/segmented_list.zig | 5 ++++- lib/std/unicode.zig | 1 + lib/std/x.zig | 1 + 6 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/std/compress.zig b/lib/std/compress.zig index 7fa25175d5..1d671f1aa6 100644 --- a/lib/std/compress.zig +++ b/lib/std/compress.zig @@ -5,6 +5,7 @@ pub const gzip = @import("compress/gzip.zig"); pub const zlib = @import("compress/zlib.zig"); test { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; _ = deflate; _ = gzip; _ = zlib; diff --git a/lib/std/event/batch.zig b/lib/std/event/batch.zig index 4165f88f48..ba50d4bee5 100644 --- a/lib/std/event/batch.zig +++ b/lib/std/event/batch.zig @@ -109,6 +109,7 @@ pub fn Batch( } test "std.event.Batch" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; var count: usize = 0; var batch = Batch(void, 2, .auto_async).init(); batch.add(&async sleepALittle(&count)); diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 939921535d..20f1319d50 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -2111,6 +2111,7 @@ test "slice" { } test "escape non-printable" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; try expectFmt("abc", "{s}", .{fmtSliceEscapeLower("abc")}); try expectFmt("ab\\xffc", "{s}", .{fmtSliceEscapeLower("ab\xffc")}); try expectFmt("ab\\xFFc", "{s}", .{fmtSliceEscapeUpper("ab\xffc")}); @@ -2146,6 +2147,7 @@ test "cstr" { } test "filesize" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; try expectFmt("file size: 42B\n", "file size: {}\n", .{fmtIntSizeDec(42)}); try expectFmt("file size: 42B\n", "file size: {}\n", .{fmtIntSizeBin(42)}); try expectFmt("file size: 63MB\n", "file size: {}\n", .{fmtIntSizeDec(63 * 1000 * 1000)}); @@ -2445,6 +2447,7 @@ test "struct.zero-size" { } test "bytes.hex" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; const some_bytes = "\xCA\xFE\xBA\xBE"; try expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{fmtSliceHexLower(some_bytes)}); try expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{fmtSliceHexUpper(some_bytes)}); @@ -2476,6 +2479,7 @@ pub fn hexToBytes(out: []u8, input: []const u8) ![]u8 { } test "hexToBytes" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; var buf: [32]u8 = undefined; try expectFmt("90" ** 32, "{s}", .{fmtSliceHexUpper(try hexToBytes(&buf, "90" ** 32))}); try expectFmt("ABCD", "{s}", .{fmtSliceHexUpper(try hexToBytes(&buf, "ABCD"))}); diff --git a/lib/std/segmented_list.zig b/lib/std/segmented_list.zig index 72e956a637..81ad6f6211 100644 --- a/lib/std/segmented_list.zig +++ b/lib/std/segmented_list.zig @@ -391,7 +391,10 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } test "SegmentedList basic usage" { - try testSegmentedList(0); + if (@import("builtin").zig_backend == .stage1) { + // https://github.com/ziglang/zig/issues/11787 + try testSegmentedList(0); + } try testSegmentedList(1); try testSegmentedList(2); try testSegmentedList(4); diff --git a/lib/std/unicode.zig b/lib/std/unicode.zig index 81a7ed838f..a0cf7f6624 100644 --- a/lib/std/unicode.zig +++ b/lib/std/unicode.zig @@ -804,6 +804,7 @@ pub fn fmtUtf16le(utf16le: []const u16) std.fmt.Formatter(formatUtf16le) { } test "fmtUtf16le" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; const expectFmt = std.testing.expectFmt; try expectFmt("", "{}", .{fmtUtf16le(utf8ToUtf16LeStringLiteral(""))}); try expectFmt("foo", "{}", .{fmtUtf16le(utf8ToUtf16LeStringLiteral("foo"))}); diff --git a/lib/std/x.zig b/lib/std/x.zig index 64caf324ed..bafcdd5426 100644 --- a/lib/std/x.zig +++ b/lib/std/x.zig @@ -13,6 +13,7 @@ pub const net = struct { }; test { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; inline for (.{ os, net }) |module| { std.testing.refAllDecls(module); } From 6d44c0a16c90a13cb3507751e2015edf51c642cf Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 3 Jun 2022 16:02:23 +0300 Subject: [PATCH 1719/2031] std: update tests to stage2 semantics --- lib/std/crypto/25519/scalar.zig | 12 +- lib/std/crypto/blake3.zig | 9 +- lib/std/crypto/sha3.zig | 8 +- lib/std/fmt.zig | 1 + lib/std/heap.zig | 3 +- lib/std/heap/log_to_writer_allocator.zig | 3 +- lib/std/io/reader.zig | 96 ++++++---- lib/std/json.zig | 219 ++++++++++++++--------- lib/std/meta.zig | 5 +- lib/std/net.zig | 12 +- lib/std/zig/c_translation.zig | 36 +++- 11 files changed, 261 insertions(+), 143 deletions(-) diff --git a/lib/std/crypto/25519/scalar.zig b/lib/std/crypto/25519/scalar.zig index c3170673d1..c3e7fb4fa0 100644 --- a/lib/std/crypto/25519/scalar.zig +++ b/lib/std/crypto/25519/scalar.zig @@ -34,12 +34,14 @@ pub fn rejectNonCanonical(s: CompressedScalar) NonCanonicalError!void { /// Reduce a scalar to the field size. pub fn reduce(s: CompressedScalar) CompressedScalar { - return Scalar.fromBytes(s).toBytes(); + var scalar = Scalar.fromBytes(s); + return scalar.toBytes(); } /// Reduce a 64-bytes scalar to the field size. pub fn reduce64(s: [64]u8) CompressedScalar { - return ScalarDouble.fromBytes64(s).toBytes(); + var scalar = ScalarDouble.fromBytes64(s); + return scalar.toBytes(); } /// Perform the X25519 "clamping" operation. @@ -106,12 +108,14 @@ pub const Scalar = struct { /// Unpack a 32-byte representation of a scalar pub fn fromBytes(bytes: CompressedScalar) Scalar { - return ScalarDouble.fromBytes32(bytes).reduce(5); + var scalar = ScalarDouble.fromBytes32(bytes); + return scalar.reduce(5); } /// Unpack a 64-byte representation of a scalar pub fn fromBytes64(bytes: [64]u8) Scalar { - return ScalarDouble.fromBytes64(bytes).reduce(5); + var scalar = ScalarDouble.fromBytes64(bytes); + return scalar.reduce(5); } /// Pack a scalar into bytes diff --git a/lib/std/crypto/blake3.zig b/lib/std/crypto/blake3.zig index a31dcc814a..762ec67f31 100644 --- a/lib/std/crypto/blake3.zig +++ b/lib/std/crypto/blake3.zig @@ -679,9 +679,12 @@ fn testBlake3(hasher: *Blake3, input_len: usize, expected_hex: [262]u8) !void { } test "BLAKE3 reference test cases" { - var hash = &Blake3.init(.{}); - var keyed_hash = &Blake3.init(.{ .key = reference_test.key.* }); - var derive_key = &Blake3.initKdf(reference_test.context_string, .{}); + var hash_state = Blake3.init(.{}); + const hash = &hash_state; + var keyed_hash_state = Blake3.init(.{ .key = reference_test.key.* }); + const keyed_hash = &keyed_hash_state; + var derive_key_state = Blake3.initKdf(reference_test.context_string, .{}); + const derive_key = &derive_key_state; for (reference_test.cases) |t| { try testBlake3(hash, t.input_len, t.hash.*); diff --git a/lib/std/crypto/sha3.zig b/lib/std/crypto/sha3.zig index ccc0775df1..567b39bfc3 100644 --- a/lib/std/crypto/sha3.zig +++ b/lib/std/crypto/sha3.zig @@ -128,18 +128,16 @@ fn keccakF(comptime F: usize, d: *[F / 8]u8) void { r.* = mem.readIntLittle(u64, d[8 * i ..][0..8]); } - comptime var x: usize = 0; - comptime var y: usize = 0; for (RC[0..no_rounds]) |round| { // theta - x = 0; + comptime var x: usize = 0; inline while (x < 5) : (x += 1) { c[x] = s[x] ^ s[x + 5] ^ s[x + 10] ^ s[x + 15] ^ s[x + 20]; } x = 0; inline while (x < 5) : (x += 1) { t[0] = c[M5[x + 4]] ^ math.rotl(u64, c[M5[x + 1]], @as(usize, 1)); - y = 0; + comptime var y: usize = 0; inline while (y < 5) : (y += 1) { s[x + y * 5] ^= t[0]; } @@ -155,7 +153,7 @@ fn keccakF(comptime F: usize, d: *[F / 8]u8) void { } // chi - y = 0; + comptime var y: usize = 0; inline while (y < 5) : (y += 1) { x = 0; inline while (x < 5) : (x += 1) { diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 20f1319d50..9afb556b5a 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -2123,6 +2123,7 @@ test "pointer" { try expectFmt("pointer: i32@deadbeef\n", "pointer: {}\n", .{value}); try expectFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", .{value}); } + if (builtin.zig_backend != .stage1) return error.SkipZigTest; { const value = @intToPtr(fn () void, 0xdeadbeef); try expectFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); diff --git a/lib/std/heap.zig b/lib/std/heap.zig index cbbe111a26..b71cdb7932 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -1210,7 +1210,8 @@ pub fn testAllocatorAlignedShrink(base_allocator: mem.Allocator) !void { const allocator = validationAllocator.allocator(); var debug_buffer: [1000]u8 = undefined; - const debug_allocator = FixedBufferAllocator.init(&debug_buffer).allocator(); + var fib = FixedBufferAllocator.init(&debug_buffer); + const debug_allocator = fib.allocator(); const alloc_size = mem.page_size * 2 + 50; var slice = try allocator.alignedAlloc(u8, 16, alloc_size); diff --git a/lib/std/heap/log_to_writer_allocator.zig b/lib/std/heap/log_to_writer_allocator.zig index c63c1a826f..15f1f30b40 100644 --- a/lib/std/heap/log_to_writer_allocator.zig +++ b/lib/std/heap/log_to_writer_allocator.zig @@ -91,7 +91,8 @@ test "LogToWriterAllocator" { var allocator_buf: [10]u8 = undefined; var fixedBufferAllocator = std.mem.validationWrap(std.heap.FixedBufferAllocator.init(&allocator_buf)); - const allocator = logToWriterAllocator(fixedBufferAllocator.allocator(), fbs.writer()).allocator(); + var allocator_state = logToWriterAllocator(fixedBufferAllocator.allocator(), fbs.writer()); + const allocator = allocator_state.allocator(); var a = try allocator.alloc(u8, 10); a = allocator.shrink(a, 5); diff --git a/lib/std/io/reader.zig b/lib/std/io/reader.zig index 6d216c71a5..16acef8e48 100644 --- a/lib/std/io/reader.zig +++ b/lib/std/io/reader.zig @@ -344,7 +344,8 @@ pub fn Reader( test "Reader" { var buf = "a\x02".*; - const reader = std.io.fixedBufferStream(&buf).reader(); + var fis = std.io.fixedBufferStream(&buf); + const reader = fis.reader(); try testing.expect((try reader.readByte()) == 'a'); try testing.expect((try reader.readEnum(enum(u8) { a = 0, @@ -356,13 +357,15 @@ test "Reader" { } test "Reader.isBytes" { - const reader = std.io.fixedBufferStream("foobar").reader(); + var fis = std.io.fixedBufferStream("foobar"); + const reader = fis.reader(); try testing.expectEqual(true, try reader.isBytes("foo")); try testing.expectEqual(false, try reader.isBytes("qux")); } test "Reader.skipBytes" { - const reader = std.io.fixedBufferStream("foobar").reader(); + var fis = std.io.fixedBufferStream("foobar"); + const reader = fis.reader(); try reader.skipBytes(3, .{}); try testing.expect(try reader.isBytes("bar")); try reader.skipBytes(0, .{}); @@ -374,7 +377,8 @@ test "Reader.readUntilDelimiterArrayList returns ArrayLists with bytes read unti var list = std.ArrayList(u8).init(a); defer list.deinit(); - const reader = std.io.fixedBufferStream("0000\n1234\n").reader(); + var fis = std.io.fixedBufferStream("0000\n1234\n"); + const reader = fis.reader(); try reader.readUntilDelimiterArrayList(&list, '\n', 5); try std.testing.expectEqualStrings("0000", list.items); @@ -388,7 +392,8 @@ test "Reader.readUntilDelimiterArrayList returns an empty ArrayList" { var list = std.ArrayList(u8).init(a); defer list.deinit(); - const reader = std.io.fixedBufferStream("\n").reader(); + var fis = std.io.fixedBufferStream("\n"); + const reader = fis.reader(); try reader.readUntilDelimiterArrayList(&list, '\n', 5); try std.testing.expectEqualStrings("", list.items); @@ -399,7 +404,8 @@ test "Reader.readUntilDelimiterArrayList returns StreamTooLong, then an ArrayLis var list = std.ArrayList(u8).init(a); defer list.deinit(); - const reader = std.io.fixedBufferStream("1234567\n").reader(); + var fis = std.io.fixedBufferStream("1234567\n"); + const reader = fis.reader(); try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterArrayList(&list, '\n', 5)); try std.testing.expectEqualStrings("12345", list.items); @@ -412,7 +418,8 @@ test "Reader.readUntilDelimiterArrayList returns EndOfStream" { var list = std.ArrayList(u8).init(a); defer list.deinit(); - const reader = std.io.fixedBufferStream("1234").reader(); + var fis = std.io.fixedBufferStream("1234"); + const reader = fis.reader(); try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterArrayList(&list, '\n', 5)); try std.testing.expectEqualStrings("1234", list.items); @@ -421,7 +428,8 @@ test "Reader.readUntilDelimiterArrayList returns EndOfStream" { test "Reader.readUntilDelimiterAlloc returns ArrayLists with bytes read until the delimiter, then EndOfStream" { const a = std.testing.allocator; - const reader = std.io.fixedBufferStream("0000\n1234\n").reader(); + var fis = std.io.fixedBufferStream("0000\n1234\n"); + const reader = fis.reader(); { var result = try reader.readUntilDelimiterAlloc(a, '\n', 5); @@ -441,7 +449,8 @@ test "Reader.readUntilDelimiterAlloc returns ArrayLists with bytes read until th test "Reader.readUntilDelimiterAlloc returns an empty ArrayList" { const a = std.testing.allocator; - const reader = std.io.fixedBufferStream("\n").reader(); + var fis = std.io.fixedBufferStream("\n"); + const reader = fis.reader(); { var result = try reader.readUntilDelimiterAlloc(a, '\n', 5); @@ -453,7 +462,8 @@ test "Reader.readUntilDelimiterAlloc returns an empty ArrayList" { test "Reader.readUntilDelimiterAlloc returns StreamTooLong, then an ArrayList with bytes read until the delimiter" { const a = std.testing.allocator; - const reader = std.io.fixedBufferStream("1234567\n").reader(); + var fis = std.io.fixedBufferStream("1234567\n"); + const reader = fis.reader(); try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterAlloc(a, '\n', 5)); @@ -465,67 +475,77 @@ test "Reader.readUntilDelimiterAlloc returns StreamTooLong, then an ArrayList wi test "Reader.readUntilDelimiterAlloc returns EndOfStream" { const a = std.testing.allocator; - const reader = std.io.fixedBufferStream("1234").reader(); + var fis = std.io.fixedBufferStream("1234"); + const reader = fis.reader(); try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterAlloc(a, '\n', 5)); } test "Reader.readUntilDelimiter returns bytes read until the delimiter" { var buf: [5]u8 = undefined; - const reader = std.io.fixedBufferStream("0000\n1234\n").reader(); + var fis = std.io.fixedBufferStream("0000\n1234\n"); + const reader = fis.reader(); try std.testing.expectEqualStrings("0000", try reader.readUntilDelimiter(&buf, '\n')); try std.testing.expectEqualStrings("1234", try reader.readUntilDelimiter(&buf, '\n')); } test "Reader.readUntilDelimiter returns an empty string" { var buf: [5]u8 = undefined; - const reader = std.io.fixedBufferStream("\n").reader(); + var fis = std.io.fixedBufferStream("\n"); + const reader = fis.reader(); try std.testing.expectEqualStrings("", try reader.readUntilDelimiter(&buf, '\n')); } test "Reader.readUntilDelimiter returns StreamTooLong, then an empty string" { var buf: [5]u8 = undefined; - const reader = std.io.fixedBufferStream("12345\n").reader(); + var fis = std.io.fixedBufferStream("12345\n"); + const reader = fis.reader(); try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n')); try std.testing.expectEqualStrings("", try reader.readUntilDelimiter(&buf, '\n')); } test "Reader.readUntilDelimiter returns StreamTooLong, then bytes read until the delimiter" { var buf: [5]u8 = undefined; - const reader = std.io.fixedBufferStream("1234567\n").reader(); + var fis = std.io.fixedBufferStream("1234567\n"); + const reader = fis.reader(); try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n')); try std.testing.expectEqualStrings("67", try reader.readUntilDelimiter(&buf, '\n')); } test "Reader.readUntilDelimiter returns EndOfStream" { var buf: [5]u8 = undefined; - const reader = std.io.fixedBufferStream("").reader(); + var fis = std.io.fixedBufferStream(""); + const reader = fis.reader(); try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n')); } test "Reader.readUntilDelimiter returns bytes read until delimiter, then EndOfStream" { var buf: [5]u8 = undefined; - const reader = std.io.fixedBufferStream("1234\n").reader(); + var fis = std.io.fixedBufferStream("1234\n"); + const reader = fis.reader(); try std.testing.expectEqualStrings("1234", try reader.readUntilDelimiter(&buf, '\n')); try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n')); } test "Reader.readUntilDelimiter returns EndOfStream" { var buf: [5]u8 = undefined; - const reader = std.io.fixedBufferStream("1234").reader(); + var fis = std.io.fixedBufferStream("1234"); + const reader = fis.reader(); try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n')); } test "Reader.readUntilDelimiter returns StreamTooLong, then EndOfStream" { var buf: [5]u8 = undefined; - const reader = std.io.fixedBufferStream("12345").reader(); + var fis = std.io.fixedBufferStream("12345"); + const reader = fis.reader(); try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n')); try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n')); } test "Reader.readUntilDelimiter writes all bytes read to the output buffer" { var buf: [5]u8 = undefined; - const reader = std.io.fixedBufferStream("0000\n12345").reader(); + var fis = std.io.fixedBufferStream("0000\n12345"); + const reader = fis.reader(); _ = try reader.readUntilDelimiter(&buf, '\n'); try std.testing.expectEqualStrings("0000\n", &buf); try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n')); @@ -535,7 +555,8 @@ test "Reader.readUntilDelimiter writes all bytes read to the output buffer" { test "Reader.readUntilDelimiterOrEofAlloc returns ArrayLists with bytes read until the delimiter, then EndOfStream" { const a = std.testing.allocator; - const reader = std.io.fixedBufferStream("0000\n1234\n").reader(); + var fis = std.io.fixedBufferStream("0000\n1234\n"); + const reader = fis.reader(); { var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?; @@ -555,7 +576,8 @@ test "Reader.readUntilDelimiterOrEofAlloc returns ArrayLists with bytes read unt test "Reader.readUntilDelimiterOrEofAlloc returns an empty ArrayList" { const a = std.testing.allocator; - const reader = std.io.fixedBufferStream("\n").reader(); + var fis = std.io.fixedBufferStream("\n"); + const reader = fis.reader(); { var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?; @@ -567,7 +589,8 @@ test "Reader.readUntilDelimiterOrEofAlloc returns an empty ArrayList" { test "Reader.readUntilDelimiterOrEofAlloc returns StreamTooLong, then an ArrayList with bytes read until the delimiter" { const a = std.testing.allocator; - const reader = std.io.fixedBufferStream("1234567\n").reader(); + var fis = std.io.fixedBufferStream("1234567\n"); + const reader = fis.reader(); try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)); @@ -578,60 +601,69 @@ test "Reader.readUntilDelimiterOrEofAlloc returns StreamTooLong, then an ArrayLi test "Reader.readUntilDelimiterOrEof returns bytes read until the delimiter" { var buf: [5]u8 = undefined; - const reader = std.io.fixedBufferStream("0000\n1234\n").reader(); + var fis = std.io.fixedBufferStream("0000\n1234\n"); + const reader = fis.reader(); try std.testing.expectEqualStrings("0000", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); } test "Reader.readUntilDelimiterOrEof returns an empty string" { var buf: [5]u8 = undefined; - const reader = std.io.fixedBufferStream("\n").reader(); + var fis = std.io.fixedBufferStream("\n"); + const reader = fis.reader(); try std.testing.expectEqualStrings("", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); } test "Reader.readUntilDelimiterOrEof returns StreamTooLong, then an empty string" { var buf: [5]u8 = undefined; - const reader = std.io.fixedBufferStream("12345\n").reader(); + var fis = std.io.fixedBufferStream("12345\n"); + const reader = fis.reader(); try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n')); try std.testing.expectEqualStrings("", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); } test "Reader.readUntilDelimiterOrEof returns StreamTooLong, then bytes read until the delimiter" { var buf: [5]u8 = undefined; - const reader = std.io.fixedBufferStream("1234567\n").reader(); + var fis = std.io.fixedBufferStream("1234567\n"); + const reader = fis.reader(); try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n')); try std.testing.expectEqualStrings("67", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); } test "Reader.readUntilDelimiterOrEof returns null" { var buf: [5]u8 = undefined; - const reader = std.io.fixedBufferStream("").reader(); + var fis = std.io.fixedBufferStream(""); + const reader = fis.reader(); try std.testing.expect((try reader.readUntilDelimiterOrEof(&buf, '\n')) == null); } test "Reader.readUntilDelimiterOrEof returns bytes read until delimiter, then null" { var buf: [5]u8 = undefined; - const reader = std.io.fixedBufferStream("1234\n").reader(); + var fis = std.io.fixedBufferStream("1234\n"); + const reader = fis.reader(); try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); try std.testing.expect((try reader.readUntilDelimiterOrEof(&buf, '\n')) == null); } test "Reader.readUntilDelimiterOrEof returns bytes read until end-of-stream" { var buf: [5]u8 = undefined; - const reader = std.io.fixedBufferStream("1234").reader(); + var fis = std.io.fixedBufferStream("1234"); + const reader = fis.reader(); try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); } test "Reader.readUntilDelimiterOrEof returns StreamTooLong, then bytes read until end-of-stream" { var buf: [5]u8 = undefined; - const reader = std.io.fixedBufferStream("1234567").reader(); + var fis = std.io.fixedBufferStream("1234567"); + const reader = fis.reader(); try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n')); try std.testing.expectEqualStrings("67", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?); } test "Reader.readUntilDelimiterOrEof writes all bytes read to the output buffer" { var buf: [5]u8 = undefined; - const reader = std.io.fixedBufferStream("0000\n12345").reader(); + var fis = std.io.fixedBufferStream("0000\n12345"); + const reader = fis.reader(); _ = try reader.readUntilDelimiterOrEof(&buf, '\n'); try std.testing.expectEqualStrings("0000\n", &buf); try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n')); diff --git a/lib/std/json.zig b/lib/std/json.zig index 2650b98822..cc82c1d0a5 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -1506,42 +1506,46 @@ fn skipValue(tokens: *TokenStream) SkipValueError!void { } test "skipValue" { - try skipValue(&TokenStream.init("false")); - try skipValue(&TokenStream.init("true")); - try skipValue(&TokenStream.init("null")); - try skipValue(&TokenStream.init("42")); - try skipValue(&TokenStream.init("42.0")); - try skipValue(&TokenStream.init("\"foo\"")); - try skipValue(&TokenStream.init("[101, 111, 121]")); - try skipValue(&TokenStream.init("{}")); - try skipValue(&TokenStream.init("{\"foo\": \"bar\"}")); + var ts = TokenStream.init("false"); + try skipValue(&ts); + ts = TokenStream.init("true"); + try skipValue(&ts); + ts = TokenStream.init("null"); + try skipValue(&ts); + ts = TokenStream.init("42"); + try skipValue(&ts); + ts = TokenStream.init("42.0"); + try skipValue(&ts); + ts = TokenStream.init("\"foo\""); + try skipValue(&ts); + ts = TokenStream.init("[101, 111, 121]"); + try skipValue(&ts); + ts = TokenStream.init("{}"); + try skipValue(&ts); + ts = TokenStream.init("{\"foo\": \"bar\"}"); + try skipValue(&ts); { // An absurd number of nestings const nestings = StreamingParser.default_max_nestings + 1; - try testing.expectError( - error.TooManyNestedItems, - skipValue(&TokenStream.init("[" ** nestings ++ "]" ** nestings)), - ); + ts = TokenStream.init("[" ** nestings ++ "]" ** nestings); + try testing.expectError(error.TooManyNestedItems, skipValue(&ts)); } { // Would a number token cause problems in a deeply-nested array? const nestings = StreamingParser.default_max_nestings; const deeply_nested_array = "[" ** nestings ++ "0.118, 999, 881.99, 911.9, 725, 3" ++ "]" ** nestings; - try skipValue(&TokenStream.init(deeply_nested_array)); + ts = TokenStream.init(deeply_nested_array); + try skipValue(&ts); - try testing.expectError( - error.TooManyNestedItems, - skipValue(&TokenStream.init("[" ++ deeply_nested_array ++ "]")), - ); + ts = TokenStream.init("[" ++ deeply_nested_array ++ "]"); + try testing.expectError(error.TooManyNestedItems, skipValue(&ts)); } // Mismatched brace/square bracket - try testing.expectError( - error.UnexpectedClosingBrace, - skipValue(&TokenStream.init("[102, 111, 111}")), - ); + ts = TokenStream.init("[102, 111, 111}"); + try testing.expectError(error.UnexpectedClosingBrace, skipValue(&ts)); { // should fail if no value found (e.g. immediate close of object) var empty_object = TokenStream.init("{}"); @@ -1980,18 +1984,29 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void { } test "parse" { - try testing.expectEqual(false, try parse(bool, &TokenStream.init("false"), ParseOptions{})); - try testing.expectEqual(true, try parse(bool, &TokenStream.init("true"), ParseOptions{})); - try testing.expectEqual(@as(u1, 1), try parse(u1, &TokenStream.init("1"), ParseOptions{})); - try testing.expectError(error.Overflow, parse(u1, &TokenStream.init("50"), ParseOptions{})); - try testing.expectEqual(@as(u64, 42), try parse(u64, &TokenStream.init("42"), ParseOptions{})); - try testing.expectEqual(@as(f64, 42), try parse(f64, &TokenStream.init("42.0"), ParseOptions{})); - try testing.expectEqual(@as(?bool, null), try parse(?bool, &TokenStream.init("null"), ParseOptions{})); - try testing.expectEqual(@as(?bool, true), try parse(?bool, &TokenStream.init("true"), ParseOptions{})); + var ts = TokenStream.init("false"); + try testing.expectEqual(false, try parse(bool, &ts, ParseOptions{})); + ts = TokenStream.init("true"); + try testing.expectEqual(true, try parse(bool, &ts, ParseOptions{})); + ts = TokenStream.init("1"); + try testing.expectEqual(@as(u1, 1), try parse(u1, &ts, ParseOptions{})); + ts = TokenStream.init("50"); + try testing.expectError(error.Overflow, parse(u1, &ts, ParseOptions{})); + ts = TokenStream.init("42"); + try testing.expectEqual(@as(u64, 42), try parse(u64, &ts, ParseOptions{})); + ts = TokenStream.init("42.0"); + try testing.expectEqual(@as(f64, 42), try parse(f64, &ts, ParseOptions{})); + ts = TokenStream.init("null"); + try testing.expectEqual(@as(?bool, null), try parse(?bool, &ts, ParseOptions{})); + ts = TokenStream.init("true"); + try testing.expectEqual(@as(?bool, true), try parse(?bool, &ts, ParseOptions{})); - try testing.expectEqual(@as([3]u8, "foo".*), try parse([3]u8, &TokenStream.init("\"foo\""), ParseOptions{})); - try testing.expectEqual(@as([3]u8, "foo".*), try parse([3]u8, &TokenStream.init("[102, 111, 111]"), ParseOptions{})); - try testing.expectEqual(@as([0]u8, undefined), try parse([0]u8, &TokenStream.init("[]"), ParseOptions{})); + ts = TokenStream.init("\"foo\""); + try testing.expectEqual(@as([3]u8, "foo".*), try parse([3]u8, &ts, ParseOptions{})); + ts = TokenStream.init("[102, 111, 111]"); + try testing.expectEqual(@as([3]u8, "foo".*), try parse([3]u8, &ts, ParseOptions{})); + ts = TokenStream.init("[]"); + try testing.expectEqual(@as([0]u8, undefined), try parse([0]u8, &ts, ParseOptions{})); } test "parse into enum" { @@ -2000,36 +2015,48 @@ test "parse into enum" { Bar, @"with\\escape", }; - try testing.expectEqual(@as(T, .Foo), try parse(T, &TokenStream.init("\"Foo\""), ParseOptions{})); - try testing.expectEqual(@as(T, .Foo), try parse(T, &TokenStream.init("42"), ParseOptions{})); - try testing.expectEqual(@as(T, .@"with\\escape"), try parse(T, &TokenStream.init("\"with\\\\escape\""), ParseOptions{})); - try testing.expectError(error.InvalidEnumTag, parse(T, &TokenStream.init("5"), ParseOptions{})); - try testing.expectError(error.InvalidEnumTag, parse(T, &TokenStream.init("\"Qux\""), ParseOptions{})); + var ts = TokenStream.init("\"Foo\""); + try testing.expectEqual(@as(T, .Foo), try parse(T, &ts, ParseOptions{})); + ts = TokenStream.init("42"); + try testing.expectEqual(@as(T, .Foo), try parse(T, &ts, ParseOptions{})); + ts = TokenStream.init("\"with\\\\escape\""); + try testing.expectEqual(@as(T, .@"with\\escape"), try parse(T, &ts, ParseOptions{})); + ts = TokenStream.init("5"); + try testing.expectError(error.InvalidEnumTag, parse(T, &ts, ParseOptions{})); + ts = TokenStream.init("\"Qux\""); + try testing.expectError(error.InvalidEnumTag, parse(T, &ts, ParseOptions{})); } test "parse with trailing data" { - try testing.expectEqual(false, try parse(bool, &TokenStream.init("falsed"), ParseOptions{ .allow_trailing_data = true })); - try testing.expectError(error.InvalidTopLevelTrailing, parse(bool, &TokenStream.init("falsed"), ParseOptions{ .allow_trailing_data = false })); + var ts = TokenStream.init("falsed"); + try testing.expectEqual(false, try parse(bool, &ts, ParseOptions{ .allow_trailing_data = true })); + ts = TokenStream.init("falsed"); + try testing.expectError(error.InvalidTopLevelTrailing, parse(bool, &ts, ParseOptions{ .allow_trailing_data = false })); // trailing whitespace is okay - try testing.expectEqual(false, try parse(bool, &TokenStream.init("false \n"), ParseOptions{ .allow_trailing_data = false })); + ts = TokenStream.init("false \n"); + try testing.expectEqual(false, try parse(bool, &ts, ParseOptions{ .allow_trailing_data = false })); } test "parse into that allocates a slice" { - try testing.expectError(error.AllocatorRequired, parse([]u8, &TokenStream.init("\"foo\""), ParseOptions{})); + var ts = TokenStream.init("\"foo\""); + try testing.expectError(error.AllocatorRequired, parse([]u8, &ts, ParseOptions{})); const options = ParseOptions{ .allocator = testing.allocator }; { - const r = try parse([]u8, &TokenStream.init("\"foo\""), options); + ts = TokenStream.init("\"foo\""); + const r = try parse([]u8, &ts, options); defer parseFree([]u8, r, options); try testing.expectEqualSlices(u8, "foo", r); } { - const r = try parse([]u8, &TokenStream.init("[102, 111, 111]"), options); + ts = TokenStream.init("[102, 111, 111]"); + const r = try parse([]u8, &ts, options); defer parseFree([]u8, r, options); try testing.expectEqualSlices(u8, "foo", r); } { - const r = try parse([]u8, &TokenStream.init("\"with\\\\escape\""), options); + ts = TokenStream.init("\"with\\\\escape\""); + const r = try parse([]u8, &ts, options); defer parseFree([]u8, r, options); try testing.expectEqualSlices(u8, "with\\escape", r); } @@ -2042,7 +2069,8 @@ test "parse into tagged union" { float: f64, string: []const u8, }; - try testing.expectEqual(T{ .float = 1.5 }, try parse(T, &TokenStream.init("1.5"), ParseOptions{})); + var ts = TokenStream.init("1.5"); + try testing.expectEqual(T{ .float = 1.5 }, try parse(T, &ts, ParseOptions{})); } { // failing allocations should be bubbled up instantly without trying next member @@ -2053,7 +2081,8 @@ test "parse into tagged union" { string: []const u8, array: [3]u8, }; - try testing.expectError(error.OutOfMemory, parse(T, &TokenStream.init("[1,2,3]"), options)); + var ts = TokenStream.init("[1,2,3]"); + try testing.expectError(error.OutOfMemory, parse(T, &ts, options)); } { @@ -2062,7 +2091,8 @@ test "parse into tagged union" { x: u8, y: u8, }; - try testing.expectEqual(T{ .x = 42 }, try parse(T, &TokenStream.init("42"), ParseOptions{})); + var ts = TokenStream.init("42"); + try testing.expectEqual(T{ .x = 42 }, try parse(T, &ts, ParseOptions{})); } { // needs to back out when first union member doesn't match @@ -2070,7 +2100,8 @@ test "parse into tagged union" { A: struct { x: u32 }, B: struct { y: u32 }, }; - try testing.expectEqual(T{ .B = .{ .y = 42 } }, try parse(T, &TokenStream.init("{\"y\":42}"), ParseOptions{})); + var ts = TokenStream.init("{\"y\":42}"); + try testing.expectEqual(T{ .B = .{ .y = 42 } }, try parse(T, &ts, ParseOptions{})); } } @@ -2080,7 +2111,8 @@ test "parse union bubbles up AllocatorRequired" { string: []const u8, int: i32, }; - try testing.expectError(error.AllocatorRequired, parse(T, &TokenStream.init("42"), ParseOptions{})); + var ts = TokenStream.init("42"); + try testing.expectError(error.AllocatorRequired, parse(T, &ts, ParseOptions{})); } { // string member not first in union (and matching) @@ -2089,7 +2121,8 @@ test "parse union bubbles up AllocatorRequired" { float: f64, string: []const u8, }; - try testing.expectError(error.AllocatorRequired, parse(T, &TokenStream.init("\"foo\""), ParseOptions{})); + var ts = TokenStream.init("\"foo\""); + try testing.expectError(error.AllocatorRequired, parse(T, &ts, ParseOptions{})); } } @@ -2102,7 +2135,8 @@ test "parseFree descends into tagged union" { string: []const u8, }; // use a string with unicode escape so we know result can't be a reference to global constant - const r = try parse(T, &TokenStream.init("\"with\\u0105unicode\""), options); + var ts = TokenStream.init("\"with\\u0105unicode\""); + const r = try parse(T, &ts, options); try testing.expectEqual(std.meta.Tag(T).string, @as(std.meta.Tag(T), r)); try testing.expectEqualSlices(u8, "withąunicode", r.string); try testing.expectEqual(@as(usize, 0), fail_alloc.deallocations); @@ -2116,12 +2150,13 @@ test "parse with comptime field" { comptime a: i32 = 0, b: bool, }; - try testing.expectEqual(T{ .a = 0, .b = true }, try parse(T, &TokenStream.init( + var ts = TokenStream.init( \\{ \\ "a": 0, \\ "b": true \\} - ), ParseOptions{})); + ); + try testing.expectEqual(T{ .a = 0, .b = true }, try parse(T, &ts, ParseOptions{})); } { // string comptime values currently require an allocator @@ -2140,12 +2175,13 @@ test "parse with comptime field" { .allocator = std.testing.allocator, }; - const r = try parse(T, &TokenStream.init( + var ts = TokenStream.init( \\{ \\ "kind": "float", \\ "b": 1.0 \\} - ), options); + ); + const r = try parse(T, &ts, options); // check that parseFree doesn't try to free comptime fields parseFree(T, r, options); @@ -2154,7 +2190,8 @@ test "parse with comptime field" { test "parse into struct with no fields" { const T = struct {}; - try testing.expectEqual(T{}, try parse(T, &TokenStream.init("{}"), ParseOptions{})); + var ts = TokenStream.init("{}"); + try testing.expectEqual(T{}, try parse(T, &ts, ParseOptions{})); } test "parse into struct with misc fields" { @@ -2186,7 +2223,7 @@ test "parse into struct with misc fields" { string: []const u8, }; }; - const r = try parse(T, &TokenStream.init( + var ts = TokenStream.init( \\{ \\ "int": 420, \\ "float": 3.14, @@ -2208,7 +2245,8 @@ test "parse into struct with misc fields" { \\ ], \\ "a_union": 100000 \\} - ), options); + ); + const r = try parse(T, &ts, options); defer parseFree(T, r, options); try testing.expectEqual(@as(i64, 420), r.int); try testing.expectEqual(@as(f64, 3.14), r.float); @@ -2239,14 +2277,15 @@ test "parse into struct with strings and arrays with sentinels" { data: [:99]const i32, simple_data: []const i32, }; - const r = try parse(T, &TokenStream.init( + var ts = TokenStream.init( \\{ \\ "language": "zig", \\ "language_without_sentinel": "zig again!", \\ "data": [1, 2, 3], \\ "simple_data": [4, 5, 6] \\} - ), options); + ); + const r = try parse(T, &ts, options); defer parseFree(T, r, options); try testing.expectEqualSentinel(u8, 0, "zig", r.language); @@ -2275,19 +2314,25 @@ test "parse into struct with duplicate field" { const T1 = struct { a: *u64 }; // both .UseFirst and .UseLast should fail because second "a" value isn't a u64 - try testing.expectError(error.InvalidNumber, parse(T1, &TokenStream.init(str), options_first)); - try testing.expectError(error.InvalidNumber, parse(T1, &TokenStream.init(str), options_last)); + var ts = TokenStream.init(str); + try testing.expectError(error.InvalidNumber, parse(T1, &ts, options_first)); + ts = TokenStream.init(str); + try testing.expectError(error.InvalidNumber, parse(T1, &ts, options_last)); const T2 = struct { a: f64 }; - try testing.expectEqual(T2{ .a = 1.0 }, try parse(T2, &TokenStream.init(str), options_first)); - try testing.expectEqual(T2{ .a = 0.25 }, try parse(T2, &TokenStream.init(str), options_last)); + ts = TokenStream.init(str); + try testing.expectEqual(T2{ .a = 1.0 }, try parse(T2, &ts, options_first)); + ts = TokenStream.init(str); + try testing.expectEqual(T2{ .a = 0.25 }, try parse(T2, &ts, options_last)); const T3 = struct { comptime a: f64 = 1.0 }; // .UseFirst should succeed because second "a" value is unconditionally ignored (even though != 1.0) const t3 = T3{ .a = 1.0 }; - try testing.expectEqual(t3, try parse(T3, &TokenStream.init(str), options_first)); + ts = TokenStream.init(str); + try testing.expectEqual(t3, try parse(T3, &ts, options_first)); // .UseLast should fail because second "a" value is 0.25 which is not equal to default value of 1.0 - try testing.expectError(error.UnexpectedValue, parse(T3, &TokenStream.init(str), options_last)); + ts = TokenStream.init(str); + try testing.expectError(error.UnexpectedValue, parse(T3, &ts, options_last)); } test "parse into struct ignoring unknown fields" { @@ -2301,7 +2346,7 @@ test "parse into struct ignoring unknown fields" { .ignore_unknown_fields = true, }; - const r = try parse(T, &std.json.TokenStream.init( + var ts = TokenStream.init( \\{ \\ "int": 420, \\ "float": 3.14, @@ -2323,7 +2368,8 @@ test "parse into struct ignoring unknown fields" { \\ "a_union": 100000, \\ "language": "zig" \\} - ), ops); + ); + const r = try parse(T, &ts, ops); defer parseFree(T, r, ops); try testing.expectEqual(@as(i64, 420), r.int); @@ -2341,7 +2387,8 @@ test "parse into recursive union definition" { }; const ops = ParseOptions{ .allocator = testing.allocator }; - const r = try parse(T, &std.json.TokenStream.init("{\"values\":[58]}"), ops); + var ts = TokenStream.init("{\"values\":[58]}"); + const r = try parse(T, &ts, ops); defer parseFree(T, r, ops); try testing.expectEqual(@as(i64, 58), r.values.array[0].integer); @@ -2363,7 +2410,8 @@ test "parse into double recursive union definition" { }; const ops = ParseOptions{ .allocator = testing.allocator }; - const r = try parse(T, &std.json.TokenStream.init("{\"values\":[[58]]}"), ops); + var ts = TokenStream.init("{\"values\":[[58]]}"); + const r = try parse(T, &ts, ops); defer parseFree(T, r, ops); try testing.expectEqual(@as(i64, 58), r.values.array[0].array[0].integer); @@ -2806,10 +2854,13 @@ test "integer after float has proper type" { test "parse exponential into int" { const T = struct { int: i64 }; - const r = try parse(T, &TokenStream.init("{ \"int\": 4.2e2 }"), ParseOptions{}); + var ts = TokenStream.init("{ \"int\": 4.2e2 }"); + const r = try parse(T, &ts, ParseOptions{}); try testing.expectEqual(@as(i64, 420), r.int); - try testing.expectError(error.InvalidNumber, parse(T, &TokenStream.init("{ \"int\": 0.042e2 }"), ParseOptions{})); - try testing.expectError(error.Overflow, parse(T, &TokenStream.init("{ \"int\": 18446744073709551616.0 }"), ParseOptions{})); + ts = TokenStream.init("{ \"int\": 0.042e2 }"); + try testing.expectError(error.InvalidNumber, parse(T, &ts, ParseOptions{})); + ts = TokenStream.init("{ \"int\": 18446744073709551616.0 }"); + try testing.expectError(error.Overflow, parse(T, &ts, ParseOptions{})); } test "escaped characters" { @@ -2858,10 +2909,12 @@ test "string copy option" { defer arena_allocator.deinit(); const allocator = arena_allocator.allocator(); - const tree_nocopy = try Parser.init(allocator, false).parse(input); + var parser = Parser.init(allocator, false); + const tree_nocopy = try parser.parse(input); const obj_nocopy = tree_nocopy.root.Object; - const tree_copy = try Parser.init(allocator, true).parse(input); + parser = Parser.init(allocator, true); + const tree_copy = try parser.parse(input); const obj_copy = tree_copy.root.Object; for ([_][]const u8{ "noescape", "simple", "unicode", "surrogatepair" }) |field_name| { @@ -3376,14 +3429,12 @@ test "stringify null optional fields" { StringifyOptions{ .emit_null_optional_fields = false }, ); - try std.testing.expect(try parsesTo( - MyStruct, - MyStruct{}, - &TokenStream.init( - \\{"required":"something","another_required":"something else"} - ), - .{ .allocator = std.testing.allocator }, - )); + var ts = TokenStream.init( + \\{"required":"something","another_required":"something else"} + ); + try std.testing.expect(try parsesTo(MyStruct, MyStruct{}, &ts, .{ + .allocator = std.testing.allocator, + })); } // Same as `stringify` but accepts an Allocator and stores result in dynamically allocated memory instead of using a Writer. diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 0de51bfa68..c6717ad1c0 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -311,7 +311,10 @@ pub fn assumeSentinel(p: anytype, comptime sentinel_val: Elem(@TypeOf(p))) Senti const ReturnType = Sentinel(T, sentinel_val); switch (@typeInfo(T)) { .Pointer => |info| switch (info.size) { - .Slice => return @bitCast(ReturnType, p), + .Slice => if (@import("builtin").zig_backend == .stage1) + return @bitCast(ReturnType, p) + else + return @ptrCast(ReturnType, p), .Many, .One => return @ptrCast(ReturnType, p), .C => {}, }, diff --git a/lib/std/net.zig b/lib/std/net.zig index 0853a08c53..2bd3e6cfb1 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -1141,18 +1141,20 @@ fn linuxLookupNameFromHosts( }; defer file.close(); - const stream = std.io.bufferedReader(file.reader()).reader(); + var buffered_reader = std.io.bufferedReader(file.reader()); + const reader = buffered_reader.reader(); var line_buf: [512]u8 = undefined; - while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) { + while (reader.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) { error.StreamTooLong => blk: { - // Skip to the delimiter in the stream, to fix parsing - try stream.skipUntilDelimiterOrEof('\n'); + // Skip to the delimiter in the reader, to fix parsing + try reader.skipUntilDelimiterOrEof('\n'); // Use the truncated line. A truncated comment or hostname will be handled correctly. break :blk &line_buf; }, else => |e| return e, }) |line| { - const no_comment_line = mem.split(u8, line, "#").next().?; + var split_it = mem.split(u8, line, "#"); + const no_comment_line = split_it.next().?; var line_it = mem.tokenize(u8, no_comment_line, " \t"); const ip_text = line_it.next() orelse continue; diff --git a/lib/std/zig/c_translation.zig b/lib/std/zig/c_translation.zig index 494fa5ecae..b76b7078dc 100644 --- a/lib/std/zig/c_translation.zig +++ b/lib/std/zig/c_translation.zig @@ -8,10 +8,19 @@ pub fn cast(comptime DestType: type, target: anytype) DestType { // this function should behave like transCCast in translate-c, except it's for macros const SourceType = @TypeOf(target); switch (@typeInfo(DestType)) { - .Fn, .Pointer => return castToPtr(DestType, SourceType, target), + .Fn => if (@import("builtin").zig_backend == .stage1) + return castToPtr(DestType, SourceType, target) + else + return castToPtr(*const DestType, SourceType, target), + .Pointer => return castToPtr(DestType, SourceType, target), .Optional => |dest_opt| { - if (@typeInfo(dest_opt.child) == .Pointer or @typeInfo(dest_opt.child) == .Fn) { + if (@typeInfo(dest_opt.child) == .Pointer) { return castToPtr(DestType, SourceType, target); + } else if (@typeInfo(dest_opt.child) == .Fn) { + if (@import("builtin").zig_backend == .stage1) + return castToPtr(DestType, SourceType, target) + else + return castToPtr(?*const dest_opt.child, SourceType, target); } }, .Int => { @@ -124,7 +133,10 @@ test "cast" { try testing.expect(cast(?*anyopaque, -1) == @intToPtr(?*anyopaque, @bitCast(usize, @as(isize, -1)))); try testing.expect(cast(?*anyopaque, foo) == @intToPtr(?*anyopaque, @bitCast(usize, @as(isize, -1)))); - const FnPtr = ?fn (*anyopaque) void; + const FnPtr = if (@import("builtin").zig_backend == .stage1) + ?fn (*anyopaque) void + else + ?*const fn (*anyopaque) void; try testing.expect(cast(FnPtr, 0) == @intToPtr(FnPtr, @as(usize, 0))); try testing.expect(cast(FnPtr, foo) == @intToPtr(FnPtr, @bitCast(usize, @as(isize, -1)))); } @@ -135,9 +147,14 @@ pub fn sizeof(target: anytype) usize { switch (@typeInfo(T)) { .Float, .Int, .Struct, .Union, .Array, .Bool, .Vector => return @sizeOf(T), .Fn => { - // sizeof(main) returns 1, sizeof(&main) returns pointer size. - // We cannot distinguish those types in Zig, so use pointer size. - return @sizeOf(T); + if (@import("builtin").zig_backend == .stage1) { + // sizeof(main) returns 1, sizeof(&main) returns pointer size. + // We cannot distinguish those types in Zig, so use pointer size. + return @sizeOf(T); + } + + // sizeof(main) in C returns 1 + return 1; }, .Null => return @sizeOf(*anyopaque), .Void => { @@ -233,7 +250,12 @@ test "sizeof" { try testing.expect(sizeof(*const *const [4:0]u8) == ptr_size); try testing.expect(sizeof(*const [4]u8) == ptr_size); - try testing.expect(sizeof(sizeof) == @sizeOf(@TypeOf(sizeof))); + if (@import("builtin").zig_backend == .stage1) { + try testing.expect(sizeof(sizeof) == @sizeOf(@TypeOf(sizeof))); + } else if (false) { // TODO + try testing.expect(sizeof(&sizeof) == @sizeOf(@TypeOf(&sizeof))); + try testing.expect(sizeof(sizeof) == 1); + } try testing.expect(sizeof(void) == 1); try testing.expect(sizeof(anyopaque) == 1); From 50a6b0f3acb2a17f74d57301dbf3d4b13e30953b Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 3 Jun 2022 23:49:16 +0300 Subject: [PATCH 1720/2031] Sema: fix function type callconv inference --- src/Sema.zig | 6 +++++- .../runtime_indexing_comptime_array.zig | 12 ++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index d3ca6a8cc5..593b299833 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6654,7 +6654,11 @@ fn zirFunc( src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; } - const cc: std.builtin.CallingConvention = if (sema.owner_decl.is_exported) + // If this instruction has a body it means it's the type of the `owner_decl` + // otherwise it's a function type without a `callconv` attribute and should + // never be `.C`. + // NOTE: revisit when doing #1717 + const cc: std.builtin.CallingConvention = if (sema.owner_decl.is_exported and has_body) .C else .Unspecified; diff --git a/test/cases/compile_errors/runtime_indexing_comptime_array.zig b/test/cases/compile_errors/runtime_indexing_comptime_array.zig index da603d3630..3acd0feb61 100644 --- a/test/cases/compile_errors/runtime_indexing_comptime_array.zig +++ b/test/cases/compile_errors/runtime_indexing_comptime_array.zig @@ -23,9 +23,9 @@ pub export fn entry3() void { // error // backend=stage2,llvm // -// :6:33: error: values of type '[2]fn() callconv(.C) void' must be comptime known, but index value is runtime known -// :6:33: note: use '*const fn() callconv(.C) void' for a function pointer type -// :13:33: error: values of type '[2]fn() callconv(.C) void' must be comptime known, but index value is runtime known -// :13:33: note: use '*const fn() callconv(.C) void' for a function pointer type -// :19:33: error: values of type '[2]fn() callconv(.C) void' must be comptime known, but index value is runtime known -// :19:33: note: use '*const fn() callconv(.C) void' for a function pointer type +// :6:5: error: values of type '[2]fn() void' must be comptime known, but index value is runtime known +// :6:5: note: use '*const fn() void' for a function pointer type +// :13:5: error: values of type '[2]fn() void' must be comptime known, but index value is runtime known +// :13:5: note: use '*const fn() void' for a function pointer type +// :19:5: error: values of type '[2]fn() void' must be comptime known, but index value is runtime known +// :19:5: note: use '*const fn() void' for a function pointer type From d5ee45117722c1685f4b739686d74ccc7cf5b8d9 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Fri, 3 Jun 2022 15:52:03 +0200 Subject: [PATCH 1721/2031] stage2 ARM: introduce support for basic switch expressions --- src/arch/arm/CodeGen.zig | 76 ++++++++++++++++++++++++---------------- test/behavior/switch.zig | 6 ---- 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index b6b364ddc4..a0f9c12c34 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -953,15 +953,6 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { return reg; } -/// Allocates a new register and copies `mcv` into it. -/// `reg_owner` is the instruction that gets associated with the register in the register table. -/// This can have a side effect of spilling instructions to the stack to free up a register. -fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { - const reg = try self.register_manager.allocReg(reg_owner, gp); - try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv); - return MCValue{ .register = reg }; -} - fn airAlloc(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 }); @@ -2185,6 +2176,9 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind .stack_offset => |off| { log.debug("%{d} => stack offset {d} (reused)", .{ inst, off }); }, + .cpsr_flags => { + log.debug("%{d} => cpsr_flags (reused)", .{inst}); + }, else => return false, } @@ -2487,7 +2481,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { else => unreachable, }; - if (self.liveness.operandDies(inst, 0)) { + if (self.reuseOperand(inst, operand, 0, field)) { break :result field; } else { // Copy to new register @@ -2511,6 +2505,41 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +/// Allocates a new register. If Inst in non-null, additionally tracks +/// this register and the corresponding int and removes all previous +/// tracking. Does not do the actual moving (that is handled by +/// genSetReg). +fn prepareNewRegForMoving( + self: *Self, + track_inst: ?Air.Inst.Index, + register_class: RegisterManager.RegisterBitSet, + mcv: MCValue, +) !Register { + const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; + const reg = try self.register_manager.allocReg(track_inst, register_class); + + if (track_inst) |inst| { + // Overwrite the MCValue associated with this inst + branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); + + // If the previous MCValue occupied some space we track, we + // need to make sure it is marked as free now. + switch (mcv) { + .cpsr_flags => { + assert(self.cpsr_flags_inst.? == inst); + self.cpsr_flags_inst = null; + }, + .register => |prev_reg| { + assert(!self.register_manager.isRegFree(prev_reg)); + self.register_manager.freeReg(prev_reg); + }, + else => {}, + } + } + + return reg; +} + /// Don't call this function directly. Use binOp instead. /// /// Calling this function signals an intention to generate a Mir @@ -2537,18 +2566,12 @@ fn binOpRegister( null; defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; - const lhs_reg = if (lhs_is_register) lhs.register else blk: { const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { break :inst Air.refToIndex(md.lhs).?; } else null; - const reg = try self.register_manager.allocReg(track_inst, gp); - - if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); - - break :blk reg; + break :blk try self.prepareNewRegForMoving(track_inst, gp, lhs); }; const new_lhs_lock = self.register_manager.lockReg(lhs_reg); defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); @@ -2558,11 +2581,7 @@ fn binOpRegister( break :inst Air.refToIndex(md.rhs).?; } else null; - const reg = try self.register_manager.allocReg(track_inst, gp); - - if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); - - break :blk reg; + break :blk try self.prepareNewRegForMoving(track_inst, gp, rhs); }; const new_rhs_lock = self.register_manager.lockReg(rhs_reg); defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); @@ -2652,8 +2671,6 @@ fn binOpImmediate( null; defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); - const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; - const lhs_reg = if (lhs_is_register) lhs.register else blk: { const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { break :inst Air.refToIndex( @@ -2661,11 +2678,7 @@ fn binOpImmediate( ).?; } else null; - const reg = try self.register_manager.allocReg(track_inst, gp); - - if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); - - break :blk reg; + break :blk try self.prepareNewRegForMoving(track_inst, gp, lhs); }; const new_lhs_lock = self.register_manager.lockReg(lhs_reg); defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); @@ -3444,7 +3457,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. if (RegisterManager.indexOfRegIntoTracked(reg) == null) { // Save function return value into a tracked register log.debug("airCall: copying {} as it is not tracked", .{reg}); - break :result try self.copyToNewRegister(inst, info.return_value); + const new_reg = try self.copyToTmpRegister(fn_ty.fnReturnType(), info.return_value); + break :result MCValue{ .register = new_reg }; } }, else => {}, @@ -4124,7 +4138,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { var case_i: u32 = 0; while (case_i < switch_br.data.cases_len) : (case_i += 1) { const case = self.air.extraData(Air.SwitchBr.Case, extra_index); - const items = @bitCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); + const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); assert(items.len > 0); const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; extra_index = case.end + items.len + case_body.len; diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index 2d10ad0a13..c44f8fe223 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -5,7 +5,6 @@ const expectError = std.testing.expectError; const expectEqual = std.testing.expectEqual; test "switch with numbers" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testSwitchWithNumbers(13); @@ -21,7 +20,6 @@ fn testSwitchWithNumbers(x: u32) !void { } test "switch with all ranges" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try expect(testSwitchWithAllRanges(50, 3) == 1); @@ -176,7 +174,6 @@ test "undefined.u0" { } test "switch with disjoint range" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO var q: u8 = 0; @@ -397,7 +394,6 @@ fn switchWithUnreachable(x: i32) i32 { } test "capture value of switch with all unreachable prongs" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const x = return_a_number() catch |err| switch (err) { @@ -412,7 +408,6 @@ fn return_a_number() anyerror!i32 { test "switch on integer with else capturing expr" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const S = struct { @@ -658,7 +653,6 @@ test "switch capture copies its payload" { } test "capture of integer forwards the switch condition directly" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const S = struct { From 33826a6a2e035d2a2be65314ed80a6b7abaf7f12 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 5 Jun 2022 10:36:54 +0200 Subject: [PATCH 1722/2031] x64: disable misbehaving behavior tests --- test/behavior/bugs/1381.zig | 2 ++ test/behavior/bugs/1741.zig | 1 + 2 files changed, 3 insertions(+) diff --git a/test/behavior/bugs/1381.zig b/test/behavior/bugs/1381.zig index fb46869e99..242c852795 100644 --- a/test/behavior/bugs/1381.zig +++ b/test/behavior/bugs/1381.zig @@ -14,6 +14,8 @@ const A = union(enum) { test "union that needs padding bytes inside an array" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + var as = [_]A{ A{ .B = B{ .D = 1 } }, A{ .B = B{ .D = 1 } }, diff --git a/test/behavior/bugs/1741.zig b/test/behavior/bugs/1741.zig index 63de6e83dc..0d19eb20c8 100644 --- a/test/behavior/bugs/1741.zig +++ b/test/behavior/bugs/1741.zig @@ -5,6 +5,7 @@ test "fixed" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const x: f32 align(128) = 12.34; try std.testing.expect(@ptrToInt(&x) % 128 == 0); From 0224ad19b82bb307a2c246f2e30826af599aa895 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Jun 2022 16:50:33 -0700 Subject: [PATCH 1723/2031] AstGen: introduce `try` instruction This introduces two ZIR instructions: * `try` * `try_inline` This is part of an effort to implement #11772. --- src/AstGen.zig | 86 ++++++++++++++++++----------------------------- src/Sema.zig | 71 +++++++++++++++++++++++++++++++++----- src/Zir.zig | 33 ++++++++++++++++++ src/print_zir.zig | 27 +++++++++++---- 4 files changed, 150 insertions(+), 67 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index ab5befa4ba..86ac6633ba 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2425,6 +2425,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .param_type, .ret_ptr, .ret_type, + .@"try", + .try_inline, => break :b false, .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) { @@ -4871,68 +4873,30 @@ fn tryExpr( if (parent_gz.in_defer) return astgen.failNode(node, "'try' not allowed inside defer expression", .{}); - var block_scope = parent_gz.makeSubBlock(scope); - block_scope.setBreakResultLoc(rl); - defer block_scope.unstack(); - - const operand_rl: ResultLoc = switch (block_scope.break_result_loc) { + const operand_rl: ResultLoc = switch (rl) { .ref => .ref, else => .none, }; - const err_ops = switch (operand_rl) { - // zig fmt: off - .ref => [3]Zir.Inst.Tag{ .is_non_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr }, - else => [3]Zir.Inst.Tag{ .is_non_err, .err_union_code, .err_union_payload_unsafe }, - // zig fmt: on - }; - // This could be a pointer or value depending on the `operand_rl` parameter. - // We cannot use `block_scope.break_result_loc` because that has the bare - // type, whereas this expression has the optional type. Later we make - // up for this fact by calling rvalue on the else branch. - const operand = try expr(&block_scope, &block_scope.base, operand_rl, operand_node); - const cond = try block_scope.addUnNode(err_ops[0], operand, node); - const condbr = try block_scope.addCondBr(.condbr, node); + // This could be a pointer or value depending on the `rl` parameter. + const operand = try expr(parent_gz, scope, operand_rl, operand_node); + const is_inline = parent_gz.force_comptime; + const block_tag: Zir.Inst.Tag = if (is_inline) .try_inline else .@"try"; + const try_inst = try parent_gz.makeBlockInst(block_tag, node); + try parent_gz.instructions.append(astgen.gpa, try_inst); - const block = try parent_gz.makeBlockInst(.block, node); - try block_scope.setBlockBody(block); - // block_scope unstacked now, can add new instructions to parent_gz - try parent_gz.instructions.append(astgen.gpa, block); - - var then_scope = parent_gz.makeSubBlock(scope); - defer then_scope.unstack(); - - block_scope.break_count += 1; - // This could be a pointer or value depending on `err_ops[2]`. - const unwrapped_payload = try then_scope.addUnNode(err_ops[2], operand, node); - const then_result = switch (rl) { - .ref => unwrapped_payload, - else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node), - }; - - // else_scope will be stacked on then_scope as both are stacked on parent_gz var else_scope = parent_gz.makeSubBlock(scope); defer else_scope.unstack(); - const err_code = try else_scope.addUnNode(err_ops[1], operand, node); + const err_tag = switch (rl) { + .ref => Zir.Inst.Tag.err_union_code_ptr, + else => Zir.Inst.Tag.err_union_code, + }; + const err_code = try else_scope.addUnNode(err_tag, operand, node); try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code }); - const else_result = try else_scope.addUnNode(.ret_node, err_code, node); + _ = try else_scope.addUnNode(.ret_node, err_code, node); - const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break"; - return finishThenElseBlock( - parent_gz, - rl, - node, - &block_scope, - &then_scope, - &else_scope, - condbr, - cond, - then_result, - else_result, - block, - block, - break_tag, - ); + try else_scope.setTryBody(try_inst, operand); + return indexToRef(try_inst); } fn orelseCatchExpr( @@ -10011,6 +9975,22 @@ const GenZir = struct { gz.unstack(); } + /// Assumes nothing stacked on `gz`. Unstacks `gz`. + fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void { + const gpa = gz.astgen.gpa; + const body = gz.instructionsSlice(); + try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Try).Struct.fields.len + body.len); + const zir_datas = gz.astgen.instructions.items(.data); + zir_datas[inst].pl_node.payload_index = gz.astgen.addExtraAssumeCapacity( + Zir.Inst.Try{ + .operand = operand, + .body_len = @intCast(u32, body.len), + }, + ); + gz.astgen.extra.appendSliceAssumeCapacity(body); + gz.unstack(); + } + /// Must be called with the following stack set up: /// * gz (bottom) /// * align_gz diff --git a/src/Sema.zig b/src/Sema.zig index 593b299833..8331aed409 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1322,6 +1322,13 @@ fn analyzeBodyInner( break break_data.inst; } }, + .@"try" => blk: { + if (!block.is_comptime) break :blk try sema.zirTry(block, inst); + @panic("TODO"); + }, + .try_inline => { + @panic("TODO"); + }, }; if (sema.typeOf(air_inst).isNoReturn()) break always_noreturn; @@ -6415,32 +6422,43 @@ fn zirErrUnionPayload( const src = inst_data.src(); const operand = try sema.resolveInst(inst_data.operand); const operand_src = src; - const operand_ty = sema.typeOf(operand); - if (operand_ty.zigTypeTag() != .ErrorUnion) { + const err_union_ty = sema.typeOf(operand); + if (err_union_ty.zigTypeTag() != .ErrorUnion) { return sema.fail(block, operand_src, "expected error union type, found '{}'", .{ - operand_ty.fmt(sema.mod), + err_union_ty.fmt(sema.mod), }); } + return sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, safety_check); +} - const result_ty = operand_ty.errorUnionPayload(); - if (try sema.resolveDefinedValue(block, src, operand)) |val| { +fn analyzeErrUnionPayload( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + err_union_ty: Type, + operand: Zir.Inst.Ref, + operand_src: LazySrcLoc, + safety_check: bool, +) CompileError!Air.Inst.Ref { + const payload_ty = err_union_ty.errorUnionPayload(); + if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { if (val.getError()) |name| { return sema.fail(block, src, "caught unexpected error '{s}'", .{name}); } const data = val.castTag(.eu_payload).?.data; - return sema.addConstant(result_ty, data); + return sema.addConstant(payload_ty, data); } try sema.requireRuntimeBlock(block, src); // If the error set has no fields then no safety check is needed. if (safety_check and block.wantSafety() and - operand_ty.errorUnionSet().errorSetCardinality() != .zero) + err_union_ty.errorUnionSet().errorSetCardinality() != .zero) { try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err); } - return block.addTyOp(.unwrap_errunion_payload, result_ty, operand); + return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand); } /// Pointer in, pointer out. @@ -12958,6 +12976,43 @@ fn zirCondbr( return always_noreturn; } +fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Ref { + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; + const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); + const body = sema.code.extra[extra.end..][0..extra.data.body_len]; + const operand = try sema.resolveInst(extra.data.operand); + const is_ptr = sema.typeOf(operand).zigTypeTag() == .Pointer; + const err_union = if (is_ptr) + try sema.analyzeLoad(parent_block, src, operand, operand_src) + else + operand; + const err_union_ty = sema.typeOf(err_union); + if (err_union_ty.zigTypeTag() != .ErrorUnion) { + return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{ + err_union_ty.fmt(sema.mod), + }); + } + const is_non_err = try sema.analyzeIsNonErr(parent_block, operand_src, err_union); + + if (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)) |is_non_err_val| { + if (is_non_err_val.toBool()) { + if (is_ptr) { + return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false); + } else { + return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, operand, operand_src, false); + } + } + // We can analyze the body directly in the parent block because we know there are + // no breaks from the body possible, and that the body is noreturn. + return sema.resolveBody(parent_block, body, inst); + } + _ = body; + _ = is_non_err; + @panic("TODO"); +} + // A `break` statement is inside a runtime condition, but trying to // break from an inline loop. In such case we must convert it to // a runtime break. diff --git a/src/Zir.zig b/src/Zir.zig index f09f2015e0..a089c4089e 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -319,6 +319,19 @@ pub const Inst = struct { /// only the taken branch is analyzed. The then block and else block must /// terminate with an "inline" variant of a noreturn instruction. condbr_inline, + /// Given an operand which is an error union, splits control flow. In + /// case of error, control flow goes into the block that is part of this + /// instruction, which is guaranteed to end with a return instruction + /// and never breaks out of the block. + /// In the case of non-error, control flow proceeds to the next instruction + /// after the `try`, with the result of this instruction being the unwrapped + /// payload value, as if `err_union_payload_unsafe` was executed on the operand. + /// Uses the `pl_node` union field. Payload is `Try`. + @"try", + /// Same as `try` except the operand is coerced to a comptime value, and + /// only the taken branch is analyzed. The block must terminate with an "inline" + /// variant of a noreturn instruction. + try_inline, /// An error set type definition. Contains a list of field names. /// Uses the `pl_node` union field. Payload is `ErrorSetDecl`. error_set_decl, @@ -1231,6 +1244,8 @@ pub const Inst = struct { .closure_capture, .ret_ptr, .ret_type, + .@"try", + .try_inline, => false, .@"break", @@ -1509,6 +1524,8 @@ pub const Inst = struct { .repeat, .repeat_inline, .panic, + .@"try", + .try_inline, => false, .extended => switch (data.extended.opcode) { @@ -1569,6 +1586,8 @@ pub const Inst = struct { .coerce_result_ptr = .bin, .condbr = .pl_node, .condbr_inline = .pl_node, + .@"try" = .pl_node, + .try_inline = .pl_node, .error_set_decl = .pl_node, .error_set_decl_anon = .pl_node, .error_set_decl_func = .pl_node, @@ -2803,6 +2822,14 @@ pub const Inst = struct { else_body_len: u32, }; + /// This data is stored inside extra, trailed by: + /// * 0. body: Index // for each `body_len`. + pub const Try = struct { + /// The error union to unwrap. + operand: Ref, + body_len: u32, + }; + /// Stored in extra. Depending on the flags in Data, there will be up to 5 /// trailing Ref fields: /// 0. sentinel: Ref // if `has_sentinel` flag is set @@ -3739,6 +3766,12 @@ fn findDeclsInner( try zir.findDeclsBody(list, then_body); try zir.findDeclsBody(list, else_body); }, + .@"try", .try_inline => { + const inst_data = datas[inst].pl_node; + const extra = zir.extraData(Inst.Try, inst_data.payload_index); + const body = zir.extra[extra.end..][0..extra.data.body_len]; + try zir.findDeclsBody(list, body); + }, .switch_block => return findDeclsSwitch(zir, list, inst), .suspend_block => @panic("TODO iterate suspend block"), diff --git a/src/print_zir.zig b/src/print_zir.zig index 8081a94093..04289aa955 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -374,17 +374,21 @@ const Writer = struct { .validate_array_init_comptime, .c_import, .typeof_builtin, - => try self.writePlNodeBlock(stream, inst), + => try self.writeBlock(stream, inst), .condbr, .condbr_inline, - => try self.writePlNodeCondBr(stream, inst), + => try self.writeCondBr(stream, inst), + + .@"try", + .try_inline, + => try self.writeTry(stream, inst), .error_set_decl => try self.writeErrorSetDecl(stream, inst, .parent), .error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon), .error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func), - .switch_block => try self.writePlNodeSwitchBlock(stream, inst), + .switch_block => try self.writeSwitchBlock(stream, inst), .field_ptr, .field_val, @@ -1171,7 +1175,7 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } - fn writePlNodeBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + fn writeBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; try self.writePlNodeBlockWithoutSrc(stream, inst); try self.writeSrc(stream, inst_data.src()); @@ -1185,7 +1189,7 @@ const Writer = struct { try stream.writeAll(") "); } - fn writePlNodeCondBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + fn writeCondBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len]; @@ -1199,6 +1203,17 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } + fn writeTry(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Zir.Inst.Try, inst_data.payload_index); + const body = self.code.extra[extra.end..][0..extra.data.body_len]; + try self.writeInstRef(stream, extra.data.operand); + try stream.writeAll(", "); + try self.writeBracedBody(stream, body); + try stream.writeAll(") "); + try self.writeSrc(stream, inst_data.src()); + } + fn writeStructDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); @@ -1746,7 +1761,7 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } - fn writePlNodeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + fn writeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index); From ef885a78d606693c73641159731274cc57f6ea98 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Jun 2022 18:48:32 -0700 Subject: [PATCH 1724/2031] stage2: implement the new "try" ZIR/AIR instruction Implements semantic analysis for the new try/try_inline ZIR instruction. Adds the new try/try_ptr AIR instructions and implements them for the LLVM backend. Fixes not calling rvalue() for tryExpr in AstGen. This is part of an effort to implement #11772. --- src/Air.zig | 33 ++++++++++++++++ src/AstGen.zig | 3 +- src/Liveness.zig | 19 +++++++++ src/Sema.zig | 52 ++++++++++++++++++++++-- src/arch/aarch64/CodeGen.zig | 3 ++ src/arch/arm/CodeGen.zig | 3 ++ src/arch/riscv64/CodeGen.zig | 3 ++ src/arch/sparc64/CodeGen.zig | 3 ++ src/arch/wasm/CodeGen.zig | 3 ++ src/arch/x86_64/CodeGen.zig | 3 ++ src/codegen/c.zig | 3 ++ src/codegen/llvm.zig | 77 ++++++++++++++++++++++++++++++++++-- src/print_air.zig | 32 +++++++++++++++ 13 files changed, 229 insertions(+), 8 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 5571fc6359..efaa7f9b6b 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -320,6 +320,20 @@ pub const Inst = struct { /// Result type is always noreturn; no instructions in a block follow this one. /// Uses the `pl_op` field. Operand is the condition. Payload is `SwitchBr`. switch_br, + /// Given an operand which is an error union, splits control flow. In + /// case of error, control flow goes into the block that is part of this + /// instruction, which is guaranteed to end with a return instruction + /// and never breaks out of the block. + /// In the case of non-error, control flow proceeds to the next instruction + /// after the `try`, with the result of this instruction being the unwrapped + /// payload value, as if `unwrap_errunion_payload` was executed on the operand. + /// Uses the `pl_op` field. Payload is `Try`. + @"try", + /// Same as `try` except the operand is a pointer to an error union, and the + /// result is a pointer to the payload. Result is as if `unwrap_errunion_payload_ptr` + /// was executed on the operand. + /// Uses the `ty_pl` field. Payload is `TryPtr`. + try_ptr, /// A comptime-known value. Uses the `ty_pl` field, payload is index of /// `values` array. constant, @@ -780,6 +794,19 @@ pub const SwitchBr = struct { }; }; +/// This data is stored inside extra. Trailing: +/// 0. body: Inst.Index // for each body_len +pub const Try = struct { + body_len: u32, +}; + +/// This data is stored inside extra. Trailing: +/// 0. body: Inst.Index // for each body_len +pub const TryPtr = struct { + ptr: Inst.Ref, + body_len: u32, +}; + pub const StructField = struct { /// Whether this is a pointer or byval is determined by the AIR tag. struct_operand: Inst.Ref, @@ -1028,6 +1055,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .popcount, .byte_swap, .bit_reverse, + .try_ptr, => return air.getRefType(datas[inst].ty_op.ty), .loop, @@ -1102,6 +1130,11 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { const extra = air.extraData(Air.Bin, datas[inst].pl_op.payload).data; return air.typeOf(extra.lhs); }, + + .@"try" => { + const err_union_ty = air.typeOf(datas[inst].pl_op.operand); + return err_union_ty.errorUnionPayload(); + }, } } diff --git a/src/AstGen.zig b/src/AstGen.zig index 86ac6633ba..7874ed8218 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -4896,7 +4896,8 @@ fn tryExpr( _ = try else_scope.addUnNode(.ret_node, err_code, node); try else_scope.setTryBody(try_inst, operand); - return indexToRef(try_inst); + const result = indexToRef(try_inst); + return rvalue(parent_gz, rl, result, node); } fn orelseCatchExpr( diff --git a/src/Liveness.zig b/src/Liveness.zig index b4576c0f18..ecb755ae0a 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -478,6 +478,12 @@ pub fn categorizeOperand( .block => { return .complex; }, + .@"try" => { + return .complex; + }, + .try_ptr => { + return .complex; + }, .loop => { return .complex; }, @@ -1019,6 +1025,19 @@ fn analyzeInst( try analyzeWithContext(a, new_set, body); return; // Loop has no operands and it is always unreferenced. }, + .@"try" => { + const pl_op = inst_datas[inst].pl_op; + const extra = a.air.extraData(Air.Try, pl_op.payload); + const body = a.air.extra[extra.end..][0..extra.data.body_len]; + try analyzeWithContext(a, new_set, body); + return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, .none, .none }); + }, + .try_ptr => { + const extra = a.air.extraData(Air.TryPtr, inst_datas[inst].ty_pl.payload); + const body = a.air.extra[extra.end..][0..extra.data.body_len]; + try analyzeWithContext(a, new_set, body); + return trackOperands(a, new_set, inst, main_tomb, .{ extra.data.ptr, .none, .none }); + }, .cond_br => { // Each death that occurs inside one branch, but not the other, needs // to be added as a death immediately upon entering the other branch. diff --git a/src/Sema.zig b/src/Sema.zig index 8331aed409..de4b6c4236 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12983,7 +12983,8 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError! const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); const body = sema.code.extra[extra.end..][0..extra.data.body_len]; const operand = try sema.resolveInst(extra.data.operand); - const is_ptr = sema.typeOf(operand).zigTypeTag() == .Pointer; + const operand_ty = sema.typeOf(operand); + const is_ptr = operand_ty.zigTypeTag() == .Pointer; const err_union = if (is_ptr) try sema.analyzeLoad(parent_block, src, operand, operand_src) else @@ -13008,9 +13009,52 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError! // no breaks from the body possible, and that the body is noreturn. return sema.resolveBody(parent_block, body, inst); } - _ = body; - _ = is_non_err; - @panic("TODO"); + + var sub_block = parent_block.makeSubBlock(); + defer sub_block.instructions.deinit(sema.gpa); + + // This body is guaranteed to end with noreturn and has no breaks. + _ = try sema.analyzeBodyInner(&sub_block, body); + + if (is_ptr) { + const ptr_info = operand_ty.ptrInfo().data; + const res_ty = try Type.ptr(sema.arena, sema.mod, .{ + .pointee_type = err_union_ty.errorUnionPayload(), + .@"addrspace" = ptr_info.@"addrspace", + .mutable = ptr_info.mutable, + .@"allowzero" = ptr_info.@"allowzero", + .@"volatile" = ptr_info.@"volatile", + }); + const res_ty_ref = try sema.addType(res_ty); + try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len + + sub_block.instructions.items.len); + const try_inst = try parent_block.addInst(.{ + .tag = .try_ptr, + .data = .{ .ty_pl = .{ + .ty = res_ty_ref, + .payload = sema.addExtraAssumeCapacity(Air.TryPtr{ + .ptr = operand, + .body_len = @intCast(u32, sub_block.instructions.items.len), + }), + } }, + }); + sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items); + return try_inst; + } + + try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len + + sub_block.instructions.items.len); + const try_inst = try parent_block.addInst(.{ + .tag = .@"try", + .data = .{ .pl_op = .{ + .operand = operand, + .payload = sema.addExtraAssumeCapacity(Air.Try{ + .body_len = @intCast(u32, sub_block.instructions.items.len), + }), + } }, + }); + sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items); + return try_inst; } // A `break` statement is inside a runtime condition, but trying to diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index c85c280fdd..4574469920 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -665,6 +665,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .prefetch => try self.airPrefetch(inst), .mul_add => try self.airMulAdd(inst), + .@"try" => @panic("TODO"), + .try_ptr => @panic("TODO"), + .dbg_var_ptr, .dbg_var_val, => try self.airDbgVar(inst), diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index a0f9c12c34..16e4b6e07b 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -677,6 +677,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .prefetch => try self.airPrefetch(inst), .mul_add => try self.airMulAdd(inst), + .@"try" => @panic("TODO"), + .try_ptr => @panic("TODO"), + .dbg_var_ptr, .dbg_var_val, => try self.airDbgVar(inst), diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index c94b4de378..5d6d50fd09 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -604,6 +604,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .prefetch => try self.airPrefetch(inst), .mul_add => try self.airMulAdd(inst), + .@"try" => @panic("TODO"), + .try_ptr => @panic("TODO"), + .dbg_var_ptr, .dbg_var_val, => try self.airDbgVar(inst), diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index eb9d9a4ad9..780f01cdd4 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -604,6 +604,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .prefetch => @panic("TODO try self.airPrefetch(inst)"), .mul_add => @panic("TODO try self.airMulAdd(inst)"), + .@"try" => @panic("TODO try self.airTry(inst)"), + .try_ptr => @panic("TODO try self.airTryPtr(inst)"), + .dbg_var_ptr, .dbg_var_val, => try self.airDbgVar(inst), diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 944ef16294..d889babb58 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1490,6 +1490,9 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .int_to_float => self.airIntToFloat(inst), .get_union_tag => self.airGetUnionTag(inst), + .@"try" => @panic("TODO"), + .try_ptr => @panic("TODO"), + // TODO .dbg_inline_begin, .dbg_inline_end, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 33734eda30..7d2babed3e 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -681,6 +681,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .prefetch => try self.airPrefetch(inst), .mul_add => try self.airMulAdd(inst), + .@"try" => @panic("TODO"), + .try_ptr => @panic("TODO"), + .dbg_var_ptr, .dbg_var_val, => try self.airDbgVar(inst), diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 1e45090648..597b3b0f6b 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1875,6 +1875,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .union_init => try airUnionInit(f, inst), .prefetch => try airPrefetch(f, inst), + .@"try" => @panic("TODO"), + .try_ptr => @panic("TODO"), + .dbg_var_ptr, .dbg_var_val, => try airDbgVar(f, inst), diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index aab2dfe51a..9e9dd2cb0c 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4040,6 +4040,8 @@ pub const FuncGen = struct { .ret_addr => try self.airRetAddr(inst), .frame_addr => try self.airFrameAddress(inst), .cond_br => try self.airCondBr(inst), + .@"try" => try self.airTry(inst), + .try_ptr => try self.airTryPtr(inst), .intcast => try self.airIntCast(inst), .trunc => try self.airTrunc(inst), .fptrunc => try self.airFptrunc(inst), @@ -4731,6 +4733,75 @@ pub const FuncGen = struct { return null; } + fn airTry(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const err_union = try self.resolveInst(pl_op.operand); + const extra = self.air.extraData(Air.Try, pl_op.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + const err_union_ty = self.air.typeOf(pl_op.operand); + const result_ty = self.air.typeOfIndex(inst); + return lowerTry(self, err_union, body, err_union_ty, false, result_ty); + } + + fn airTryPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); + const err_union_ptr = try self.resolveInst(extra.data.ptr); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + const err_union_ty = self.air.typeOf(extra.data.ptr).childType(); + const result_ty = self.air.typeOfIndex(inst); + return lowerTry(self, err_union_ptr, body, err_union_ty, true, result_ty); + } + + fn lowerTry(fg: *FuncGen, err_union: *const llvm.Value, body: []const Air.Inst.Index, err_union_ty: Type, operand_is_ptr: bool, result_ty: Type) !?*const llvm.Value { + if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { + // If the error set has no fields, then the payload and the error + // union are the same value. + return err_union; + } + + const payload_ty = err_union_ty.errorUnionPayload(); + const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(); + const target = fg.dg.module.getTarget(); + const is_err = err: { + const err_set_ty = try fg.dg.lowerType(Type.anyerror); + const zero = err_set_ty.constNull(); + if (!payload_has_bits) { + const loaded = if (operand_is_ptr) fg.builder.buildLoad(err_union, "") else err_union; + break :err fg.builder.buildICmp(.NE, loaded, zero, ""); + } + const err_field_index = errUnionErrorOffset(payload_ty, target); + if (operand_is_ptr or isByRef(err_union_ty)) { + const err_field_ptr = fg.builder.buildStructGEP(err_union, err_field_index, ""); + const loaded = fg.builder.buildLoad(err_field_ptr, ""); + break :err fg.builder.buildICmp(.NE, loaded, zero, ""); + } + const loaded = fg.builder.buildExtractValue(err_union, err_field_index, ""); + break :err fg.builder.buildICmp(.NE, loaded, zero, ""); + }; + + const return_block = fg.context.appendBasicBlock(fg.llvm_func, "TryRet"); + const continue_block = fg.context.appendBasicBlock(fg.llvm_func, "TryCont"); + _ = fg.builder.buildCondBr(is_err, return_block, continue_block); + + fg.builder.positionBuilderAtEnd(return_block); + try fg.genBody(body); + + fg.builder.positionBuilderAtEnd(continue_block); + if (!payload_has_bits) { + if (!operand_is_ptr) return null; + + // TODO once we update to LLVM 14 this bitcast won't be necessary. + const res_ptr_ty = try fg.dg.lowerType(result_ty); + return fg.builder.buildBitCast(err_union, res_ptr_ty, ""); + } + const offset = errUnionPayloadOffset(payload_ty, target); + if (operand_is_ptr or isByRef(payload_ty)) { + return fg.builder.buildStructGEP(err_union, offset, ""); + } + return fg.builder.buildExtractValue(err_union, offset, ""); + } + fn airSwitchBr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { const pl_op = self.air.instructions.items(.data)[inst].pl_op; const cond = try self.resolveInst(pl_op.operand); @@ -5673,15 +5744,14 @@ pub const FuncGen = struct { const operand = try self.resolveInst(ty_op.operand); const operand_ty = self.air.typeOf(ty_op.operand); const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; - // If the error set has no fields, then the payload and the error - // union are the same value. if (error_union_ty.errorUnionSet().errorSetCardinality() == .zero) { + // If the error set has no fields, then the payload and the error + // union are the same value. return operand; } const result_ty = self.air.typeOfIndex(inst); const payload_ty = if (operand_is_ptr) result_ty.childType() else result_ty; const target = self.dg.module.getTarget(); - const offset = errUnionPayloadOffset(payload_ty, target); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { if (!operand_is_ptr) return null; @@ -5690,6 +5760,7 @@ pub const FuncGen = struct { const res_ptr_ty = try self.dg.lowerType(result_ty); return self.builder.buildBitCast(operand, res_ptr_ty, ""); } + const offset = errUnionPayloadOffset(payload_ty, target); if (operand_is_ptr or isByRef(payload_ty)) { return self.builder.buildStructGEP(operand, offset, ""); } diff --git a/src/print_air.zig b/src/print_air.zig index e62ca806b7..af1bcb8cfb 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -258,6 +258,8 @@ const Writer = struct { .union_init => try w.writeUnionInit(s, inst), .br => try w.writeBr(s, inst), .cond_br => try w.writeCondBr(s, inst), + .@"try" => try w.writeTry(s, inst), + .try_ptr => try w.writeTryPtr(s, inst), .switch_br => try w.writeSwitchBr(s, inst), .cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst), .fence => try w.writeFence(s, inst), @@ -624,6 +626,36 @@ const Writer = struct { try w.writeOperand(s, inst, 0, br.operand); } + fn writeTry(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const pl_op = w.air.instructions.items(.data)[inst].pl_op; + const extra = w.air.extraData(Air.Try, pl_op.payload); + const body = w.air.extra[extra.end..][0..extra.data.body_len]; + + try w.writeOperand(s, inst, 0, pl_op.operand); + try s.writeAll(", {\n"); + const old_indent = w.indent; + w.indent += 2; + try w.writeBody(s, body); + w.indent = old_indent; + try s.writeByteNTimes(' ', w.indent); + try s.writeAll("}"); + } + + fn writeTryPtr(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.TryPtr, ty_pl.payload); + const body = w.air.extra[extra.end..][0..extra.data.body_len]; + + try w.writeOperand(s, inst, 0, extra.data.ptr); + try s.print(", {}, {{\n", .{w.air.getRefType(ty_pl.ty).fmtDebug()}); + const old_indent = w.indent; + w.indent += 2; + try w.writeBody(s, body); + w.indent = old_indent; + try s.writeByteNTimes(' ', w.indent); + try s.writeAll("}"); + } + fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const pl_op = w.air.instructions.items(.data)[inst].pl_op; const extra = w.air.extraData(Air.CondBr, pl_op.payload); From 00720c52f6333c314aee68de31ef505b2665e44e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Jun 2022 19:05:53 -0700 Subject: [PATCH 1725/2031] Sema: implement try_inline --- src/Sema.zig | 62 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index de4b6c4236..d2186b75db 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1324,10 +1324,66 @@ fn analyzeBodyInner( }, .@"try" => blk: { if (!block.is_comptime) break :blk try sema.zirTry(block, inst); - @panic("TODO"); + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; + const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); + const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; + const operand = try sema.resolveInst(extra.data.operand); + const operand_ty = sema.typeOf(operand); + const is_ptr = operand_ty.zigTypeTag() == .Pointer; + const err_union = if (is_ptr) + try sema.analyzeLoad(block, src, operand, operand_src) + else + operand; + const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); + const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); + if (is_non_err_tv.val.toBool()) { + if (is_ptr) { + break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); + } else { + const err_union_ty = sema.typeOf(err_union); + break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false); + } + } + const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse + break always_noreturn; + if (inst == break_data.block_inst) { + break :blk try sema.resolveInst(break_data.operand); + } else { + break break_data.inst; + } }, - .try_inline => { - @panic("TODO"); + .try_inline => blk: { + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; + const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); + const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; + const operand = try sema.resolveInst(extra.data.operand); + const operand_ty = sema.typeOf(operand); + const is_ptr = operand_ty.zigTypeTag() == .Pointer; + const err_union = if (is_ptr) + try sema.analyzeLoad(block, src, operand, operand_src) + else + operand; + const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); + const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); + if (is_non_err_tv.val.toBool()) { + if (is_ptr) { + break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); + } else { + const err_union_ty = sema.typeOf(err_union); + break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false); + } + } + const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse + break always_noreturn; + if (inst == break_data.block_inst) { + break :blk try sema.resolveInst(break_data.operand); + } else { + break break_data.inst; + } }, }; if (sema.typeOf(air_inst).isNoReturn()) From 6d3586e0edf3278862a3a969debaa842014f8110 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Jun 2022 19:40:18 -0700 Subject: [PATCH 1726/2031] explicit "_ptr" variants of ZIR try instruction * Introduce "_ptr" variants of ZIR try instruction to disallow constructs such as `try` on a pointer value instead of an error union value. * Disable the "_inline" variants of the ZIR try instruction for now because we are out of ZIR tags. I will free up some space in an independent commit. * AstGen: fix tryExpr calling rvalue() on ResultLoc.ref --- src/Air.zig | 2 +- src/AstGen.zig | 20 ++++++++-- src/Sema.zig | 97 ++++++++++++++++++++++++++++++++--------------- src/Zir.zig | 26 +++++++++---- src/print_zir.zig | 2 +- 5 files changed, 104 insertions(+), 43 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index efaa7f9b6b..53421b6475 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -1018,6 +1018,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .shl_with_overflow, .ptr_add, .ptr_sub, + .try_ptr, => return air.getRefType(datas[inst].ty_pl.ty), .not, @@ -1055,7 +1056,6 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .popcount, .byte_swap, .bit_reverse, - .try_ptr, => return air.getRefType(datas[inst].ty_op.ty), .loop, diff --git a/src/AstGen.zig b/src/AstGen.zig index 7874ed8218..adb1223b71 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2426,7 +2426,9 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .ret_ptr, .ret_type, .@"try", - .try_inline, + .try_ptr, + //.try_inline, + //.try_ptr_inline, => break :b false, .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) { @@ -4880,7 +4882,16 @@ fn tryExpr( // This could be a pointer or value depending on the `rl` parameter. const operand = try expr(parent_gz, scope, operand_rl, operand_node); const is_inline = parent_gz.force_comptime; - const block_tag: Zir.Inst.Tag = if (is_inline) .try_inline else .@"try"; + const is_inline_bit = @as(u2, @boolToInt(is_inline)); + const is_ptr_bit = @as(u2, @boolToInt(operand_rl == .ref)) << 1; + const block_tag: Zir.Inst.Tag = switch (is_inline_bit | is_ptr_bit) { + 0b00 => .@"try", + 0b01 => .@"try", + //0b01 => .try_inline, + 0b10 => .try_ptr, + 0b11 => .try_ptr, + //0b11 => .try_ptr_inline, + }; const try_inst = try parent_gz.makeBlockInst(block_tag, node); try parent_gz.instructions.append(astgen.gpa, try_inst); @@ -4897,7 +4908,10 @@ fn tryExpr( try else_scope.setTryBody(try_inst, operand); const result = indexToRef(try_inst); - return rvalue(parent_gz, rl, result, node); + switch (rl) { + .ref => return result, + else => return rvalue(parent_gz, rl, result, node), + } } fn orelseCatchExpr( diff --git a/src/Sema.zig b/src/Sema.zig index d2186b75db..12ff5c5dbc 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1323,28 +1323,18 @@ fn analyzeBodyInner( } }, .@"try" => blk: { - if (!block.is_comptime) break :blk try sema.zirTry(block, inst); + if (!block.is_comptime) break :blk try sema.zirTry(block, inst, false); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; - const operand = try sema.resolveInst(extra.data.operand); - const operand_ty = sema.typeOf(operand); - const is_ptr = operand_ty.zigTypeTag() == .Pointer; - const err_union = if (is_ptr) - try sema.analyzeLoad(block, src, operand, operand_src) - else - operand; + const err_union = try sema.resolveInst(extra.data.operand); const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); if (is_non_err_tv.val.toBool()) { - if (is_ptr) { - break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); - } else { - const err_union_ty = sema.typeOf(err_union); - break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false); - } + const err_union_ty = sema.typeOf(err_union); + break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false); } const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse break always_noreturn; @@ -1354,28 +1344,50 @@ fn analyzeBodyInner( break break_data.inst; } }, - .try_inline => blk: { + //.try_inline => blk: { + // const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + // const src = inst_data.src(); + // const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; + // const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); + // const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; + // const operand = try sema.resolveInst(extra.data.operand); + // const operand_ty = sema.typeOf(operand); + // const is_ptr = operand_ty.zigTypeTag() == .Pointer; + // const err_union = if (is_ptr) + // try sema.analyzeLoad(block, src, operand, operand_src) + // else + // operand; + // const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); + // const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); + // if (is_non_err_tv.val.toBool()) { + // if (is_ptr) { + // break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); + // } else { + // const err_union_ty = sema.typeOf(err_union); + // break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false); + // } + // } + // const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse + // break always_noreturn; + // if (inst == break_data.block_inst) { + // break :blk try sema.resolveInst(break_data.operand); + // } else { + // break break_data.inst; + // } + //}, + .try_ptr => blk: { + if (!block.is_comptime) break :blk try sema.zirTry(block, inst, true); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; const operand = try sema.resolveInst(extra.data.operand); - const operand_ty = sema.typeOf(operand); - const is_ptr = operand_ty.zigTypeTag() == .Pointer; - const err_union = if (is_ptr) - try sema.analyzeLoad(block, src, operand, operand_src) - else - operand; + const err_union = try sema.analyzeLoad(block, src, operand, operand_src); const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); if (is_non_err_tv.val.toBool()) { - if (is_ptr) { - break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); - } else { - const err_union_ty = sema.typeOf(err_union); - break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false); - } + break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); } const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse break always_noreturn; @@ -1385,6 +1397,27 @@ fn analyzeBodyInner( break break_data.inst; } }, + //.try_ptr_inline => blk: { + // const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + // const src = inst_data.src(); + // const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; + // const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); + // const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; + // const operand = try sema.resolveInst(extra.data.operand); + // const err_union = try sema.analyzeLoad(block, src, operand, operand_src); + // const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); + // const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); + // if (is_non_err_tv.val.toBool()) { + // break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); + // } + // const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse + // break always_noreturn; + // if (inst == break_data.block_inst) { + // break :blk try sema.resolveInst(break_data.operand); + // } else { + // break break_data.inst; + // } + //}, }; if (sema.typeOf(air_inst).isNoReturn()) break always_noreturn; @@ -13032,15 +13065,18 @@ fn zirCondbr( return always_noreturn; } -fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Ref { +fn zirTry( + sema: *Sema, + parent_block: *Block, + inst: Zir.Inst.Index, + is_ptr: bool, +) CompileError!Zir.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); const body = sema.code.extra[extra.end..][0..extra.data.body_len]; const operand = try sema.resolveInst(extra.data.operand); - const operand_ty = sema.typeOf(operand); - const is_ptr = operand_ty.zigTypeTag() == .Pointer; const err_union = if (is_ptr) try sema.analyzeLoad(parent_block, src, operand, operand_src) else @@ -13073,6 +13109,7 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError! _ = try sema.analyzeBodyInner(&sub_block, body); if (is_ptr) { + const operand_ty = sema.typeOf(operand); const ptr_info = operand_ty.ptrInfo().data; const res_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = err_union_ty.errorUnionPayload(), diff --git a/src/Zir.zig b/src/Zir.zig index a089c4089e..02f9a97155 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -328,10 +328,14 @@ pub const Inst = struct { /// payload value, as if `err_union_payload_unsafe` was executed on the operand. /// Uses the `pl_node` union field. Payload is `Try`. @"try", - /// Same as `try` except the operand is coerced to a comptime value, and - /// only the taken branch is analyzed. The block must terminate with an "inline" - /// variant of a noreturn instruction. - try_inline, + ///// Same as `try` except the operand is coerced to a comptime value, and + ///// only the taken branch is analyzed. The block must terminate with an "inline" + ///// variant of a noreturn instruction. + //try_inline, + /// Same as `try` except the operand is a pointer and the result is a pointer. + try_ptr, + ///// Same as `try_inline` except the operand is a pointer and the result is a pointer. + //try_ptr_inline, /// An error set type definition. Contains a list of field names. /// Uses the `pl_node` union field. Payload is `ErrorSetDecl`. error_set_decl, @@ -1245,7 +1249,9 @@ pub const Inst = struct { .ret_ptr, .ret_type, .@"try", - .try_inline, + .try_ptr, + //.try_inline, + //.try_ptr_inline, => false, .@"break", @@ -1525,7 +1531,9 @@ pub const Inst = struct { .repeat_inline, .panic, .@"try", - .try_inline, + .try_ptr, + //.try_inline, + //.try_ptr_inline, => false, .extended => switch (data.extended.opcode) { @@ -1587,7 +1595,9 @@ pub const Inst = struct { .condbr = .pl_node, .condbr_inline = .pl_node, .@"try" = .pl_node, - .try_inline = .pl_node, + .try_ptr = .pl_node, + //.try_inline = .pl_node, + //.try_ptr_inline = .pl_node, .error_set_decl = .pl_node, .error_set_decl_anon = .pl_node, .error_set_decl_func = .pl_node, @@ -3766,7 +3776,7 @@ fn findDeclsInner( try zir.findDeclsBody(list, then_body); try zir.findDeclsBody(list, else_body); }, - .@"try", .try_inline => { + .@"try", .try_ptr => { const inst_data = datas[inst].pl_node; const extra = zir.extraData(Inst.Try, inst_data.payload_index); const body = zir.extra[extra.end..][0..extra.data.body_len]; diff --git a/src/print_zir.zig b/src/print_zir.zig index 04289aa955..3257a3cb58 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -381,7 +381,7 @@ const Writer = struct { => try self.writeCondBr(stream, inst), .@"try", - .try_inline, + .try_ptr, => try self.writeTry(stream, inst), .error_set_decl => try self.writeErrorSetDecl(stream, inst, .parent), From f4ac37f55da7258c25194e66e1cfe3270ca9d419 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Jun 2022 19:49:34 -0700 Subject: [PATCH 1727/2031] Sema: extract out zirTryPtr from zirTry This function took is_ptr: bool and then branched on it three times. Now, instead, each implementation does no branching and the logic is easier to follow, both for maintainers and compilers. I also fixed a bug with TryPtr not ensuring enough capacity in the extra array. --- src/Sema.zig | 109 +++++++++++++++++++++++++++++---------------------- 1 file changed, 63 insertions(+), 46 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 12ff5c5dbc..6d618fee2c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1323,7 +1323,7 @@ fn analyzeBodyInner( } }, .@"try" => blk: { - if (!block.is_comptime) break :blk try sema.zirTry(block, inst, false); + if (!block.is_comptime) break :blk try sema.zirTry(block, inst); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; @@ -1376,7 +1376,7 @@ fn analyzeBodyInner( // } //}, .try_ptr => blk: { - if (!block.is_comptime) break :blk try sema.zirTry(block, inst, true); + if (!block.is_comptime) break :blk try sema.zirTryPtr(block, inst); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; @@ -13065,22 +13065,13 @@ fn zirCondbr( return always_noreturn; } -fn zirTry( - sema: *Sema, - parent_block: *Block, - inst: Zir.Inst.Index, - is_ptr: bool, -) CompileError!Zir.Inst.Ref { +fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); const body = sema.code.extra[extra.end..][0..extra.data.body_len]; - const operand = try sema.resolveInst(extra.data.operand); - const err_union = if (is_ptr) - try sema.analyzeLoad(parent_block, src, operand, operand_src) - else - operand; + const err_union = try sema.resolveInst(extra.data.operand); const err_union_ty = sema.typeOf(err_union); if (err_union_ty.zigTypeTag() != .ErrorUnion) { return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{ @@ -13091,11 +13082,7 @@ fn zirTry( if (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)) |is_non_err_val| { if (is_non_err_val.toBool()) { - if (is_ptr) { - return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false); - } else { - return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, operand, operand_src, false); - } + return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false); } // We can analyze the body directly in the parent block because we know there are // no breaks from the body possible, and that the body is noreturn. @@ -13108,39 +13095,12 @@ fn zirTry( // This body is guaranteed to end with noreturn and has no breaks. _ = try sema.analyzeBodyInner(&sub_block, body); - if (is_ptr) { - const operand_ty = sema.typeOf(operand); - const ptr_info = operand_ty.ptrInfo().data; - const res_ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = err_union_ty.errorUnionPayload(), - .@"addrspace" = ptr_info.@"addrspace", - .mutable = ptr_info.mutable, - .@"allowzero" = ptr_info.@"allowzero", - .@"volatile" = ptr_info.@"volatile", - }); - const res_ty_ref = try sema.addType(res_ty); - try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len + - sub_block.instructions.items.len); - const try_inst = try parent_block.addInst(.{ - .tag = .try_ptr, - .data = .{ .ty_pl = .{ - .ty = res_ty_ref, - .payload = sema.addExtraAssumeCapacity(Air.TryPtr{ - .ptr = operand, - .body_len = @intCast(u32, sub_block.instructions.items.len), - }), - } }, - }); - sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items); - return try_inst; - } - try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len + sub_block.instructions.items.len); const try_inst = try parent_block.addInst(.{ .tag = .@"try", .data = .{ .pl_op = .{ - .operand = operand, + .operand = err_union, .payload = sema.addExtraAssumeCapacity(Air.Try{ .body_len = @intCast(u32, sub_block.instructions.items.len), }), @@ -13150,6 +13110,63 @@ fn zirTry( return try_inst; } +fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Ref { + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; + const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); + const body = sema.code.extra[extra.end..][0..extra.data.body_len]; + const operand = try sema.resolveInst(extra.data.operand); + const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src); + const err_union_ty = sema.typeOf(err_union); + if (err_union_ty.zigTypeTag() != .ErrorUnion) { + return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{ + err_union_ty.fmt(sema.mod), + }); + } + const is_non_err = try sema.analyzeIsNonErr(parent_block, operand_src, err_union); + + if (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)) |is_non_err_val| { + if (is_non_err_val.toBool()) { + return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false); + } + // We can analyze the body directly in the parent block because we know there are + // no breaks from the body possible, and that the body is noreturn. + return sema.resolveBody(parent_block, body, inst); + } + + var sub_block = parent_block.makeSubBlock(); + defer sub_block.instructions.deinit(sema.gpa); + + // This body is guaranteed to end with noreturn and has no breaks. + _ = try sema.analyzeBodyInner(&sub_block, body); + + const operand_ty = sema.typeOf(operand); + const ptr_info = operand_ty.ptrInfo().data; + const res_ty = try Type.ptr(sema.arena, sema.mod, .{ + .pointee_type = err_union_ty.errorUnionPayload(), + .@"addrspace" = ptr_info.@"addrspace", + .mutable = ptr_info.mutable, + .@"allowzero" = ptr_info.@"allowzero", + .@"volatile" = ptr_info.@"volatile", + }); + const res_ty_ref = try sema.addType(res_ty); + try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.TryPtr).Struct.fields.len + + sub_block.instructions.items.len); + const try_inst = try parent_block.addInst(.{ + .tag = .try_ptr, + .data = .{ .ty_pl = .{ + .ty = res_ty_ref, + .payload = sema.addExtraAssumeCapacity(Air.TryPtr{ + .ptr = operand, + .body_len = @intCast(u32, sub_block.instructions.items.len), + }), + } }, + }); + sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items); + return try_inst; +} + // A `break` statement is inside a runtime condition, but trying to // break from an inline loop. In such case we must convert it to // a runtime break. From 779770cff58923e5113b5419accf910059fddea7 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 3 Jun 2022 20:46:07 +0200 Subject: [PATCH 1728/2031] wasm: Implement `try` instruction --- src/arch/wasm/CodeGen.zig | 69 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index d889babb58..039d62b05c 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1490,8 +1490,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .int_to_float => self.airIntToFloat(inst), .get_union_tag => self.airGetUnionTag(inst), - .@"try" => @panic("TODO"), - .try_ptr => @panic("TODO"), + .@"try" => self.airTry(inst), + .try_ptr => self.airTryPtr(inst), // TODO .dbg_inline_begin, @@ -4626,3 +4626,68 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !WValue { } }); return WValue{ .none = {} }; } + +fn airTry(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const err_union = try self.resolveInst(pl_op.operand); + const extra = self.air.extraData(Air.Try, pl_op.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + const err_union_ty = self.air.typeOf(pl_op.operand); + return lowerTry(self, err_union, body, err_union_ty, false); +} + +fn airTryPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); + const err_union_ptr = try self.resolveInst(extra.data.ptr); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + const err_union_ty = self.air.typeOf(extra.data.ptr).childType(); + return lowerTry(self, err_union_ptr, body, err_union_ty, true); +} + +fn lowerTry( + self: *Self, + err_union: WValue, + body: []const Air.Inst.Index, + err_union_ty: Type, + operand_is_ptr: bool, +) InnerError!WValue { + if (operand_is_ptr) { + return self.fail("TODO: lowerTry for pointers", .{}); + } + + if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { + return err_union; + } + + const pl_ty = err_union_ty.errorUnionPayload(); + const pl_has_bits = pl_ty.hasRuntimeBitsIgnoreComptime(); + + // Block we can jump out of when error is not set + try self.startBlock(.block, wasm.block_empty); + + // check if the error tag is set for the error union. + try self.emitWValue(err_union); + if (pl_has_bits) { + const err_offset = @intCast(u32, errUnionErrorOffset(pl_ty, self.target)); + try self.addMemArg(.i32_load16_u, .{ + .offset = err_union.offset() + err_offset, + .alignment = Type.anyerror.abiAlignment(self.target), + }); + } + try self.addTag(.i32_eqz); + try self.addLabel(.br_if, 0); // jump out of block when error is '0' + try self.genBody(body); + try self.endBlock(); + + // if we reach here it means error was not set, and we want the payload + if (!pl_has_bits) { + return WValue{ .none = {} }; + } + + const pl_offset = @intCast(u32, errUnionPayloadOffset(pl_ty, self.target)); + if (isByRef(pl_ty, self.target)) { + return buildPointerOffset(self, err_union, pl_offset, .new); + } + return self.load(err_union, pl_ty, pl_offset); +} From d2f31d315e47c3918cd711d1008862df49df38f2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Jun 2022 16:10:41 -0700 Subject: [PATCH 1729/2031] C backend: implement `try` instruction --- src/codegen/c.zig | 94 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 597b3b0f6b..4c2239b306 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1875,8 +1875,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .union_init => try airUnionInit(f, inst), .prefetch => try airPrefetch(f, inst), - .@"try" => @panic("TODO"), - .try_ptr => @panic("TODO"), + .@"try" => try airTry(f, inst), + .try_ptr => try airTryPtr(f, inst), .dbg_var_ptr, .dbg_var_val, @@ -2864,6 +2864,91 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { return result; } +fn airTry(f: *Function, inst: Air.Inst.Index) !CValue { + const pl_op = f.air.instructions.items(.data)[inst].pl_op; + const err_union = try f.resolveInst(pl_op.operand); + const extra = f.air.extraData(Air.Try, pl_op.payload); + const body = f.air.extra[extra.end..][0..extra.data.body_len]; + const err_union_ty = f.air.typeOf(pl_op.operand); + const result_ty = f.air.typeOfIndex(inst); + return lowerTry(f, err_union, body, err_union_ty, false, result_ty); +} + +fn airTryPtr(f: *Function, inst: Air.Inst.Index) !CValue { + const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; + const extra = f.air.extraData(Air.TryPtr, ty_pl.payload); + const err_union_ptr = try f.resolveInst(extra.data.ptr); + const body = f.air.extra[extra.end..][0..extra.data.body_len]; + const err_union_ty = f.air.typeOf(extra.data.ptr).childType(); + const result_ty = f.air.typeOfIndex(inst); + return lowerTry(f, err_union_ptr, body, err_union_ty, true, result_ty); +} + +fn lowerTry( + f: *Function, + err_union: CValue, + body: []const Air.Inst.Index, + err_union_ty: Type, + operand_is_ptr: bool, + result_ty: Type, +) !CValue { + if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { + // If the error set has no fields, then the payload and the error + // union are the same value. + return err_union; + } + + const payload_ty = err_union_ty.errorUnionPayload(); + const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(); + + const writer = f.object.writer(); + + err: { + if (!payload_has_bits) { + if (operand_is_ptr) { + try writer.writeAll("if(*"); + } else { + try writer.writeAll("if("); + } + try f.writeCValue(writer, err_union); + try writer.writeAll(")"); + break :err; + } + if (operand_is_ptr or isByRef(err_union_ty)) { + try writer.writeAll("if("); + try f.writeCValue(writer, err_union); + try writer.writeAll("->error)"); + break :err; + } + try writer.writeAll("if("); + try f.writeCValue(writer, err_union); + try writer.writeAll(".error)"); + } + + try genBody(f, body); + try f.object.indent_writer.insertNewline(); + + if (!payload_has_bits) { + if (!operand_is_ptr) { + return CValue.none; + } else { + return err_union; + } + } + + const local = try f.allocLocal(result_ty, .Const); + if (operand_is_ptr or isByRef(payload_ty)) { + try writer.writeAll(" = &"); + try f.writeCValue(writer, err_union); + try writer.writeAll("->payload;\n"); + } else { + try writer.writeAll(" = "); + try f.writeCValue(writer, err_union); + try writer.writeAll(".payload;\n"); + } + return local; +} + fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { const branch = f.air.instructions.items(.data)[inst].br; const block = f.blocks.get(branch.block_inst).?; @@ -4224,3 +4309,8 @@ fn loweredFnRetTyHasBits(fn_ty: Type) bool { } return false; } + +fn isByRef(ty: Type) bool { + _ = ty; + return false; +} From 6da420419d79a735e88416056587c154b3cb3acd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Jun 2022 17:55:10 -0700 Subject: [PATCH 1730/2031] Sema: avoid emitting unused is_non_err AIR instruction --- src/Sema.zig | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 6d618fee2c..78d025d548 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1330,7 +1330,8 @@ fn analyzeBodyInner( const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; const err_union = try sema.resolveInst(extra.data.operand); - const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); + const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); + assert(is_non_err != .none); const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); if (is_non_err_tv.val.toBool()) { const err_union_ty = sema.typeOf(err_union); @@ -1357,7 +1358,8 @@ fn analyzeBodyInner( // try sema.analyzeLoad(block, src, operand, operand_src) // else // operand; - // const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); + // const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); + // assert(is_non_err != .none); // const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); // if (is_non_err_tv.val.toBool()) { // if (is_ptr) { @@ -1384,7 +1386,8 @@ fn analyzeBodyInner( const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; const operand = try sema.resolveInst(extra.data.operand); const err_union = try sema.analyzeLoad(block, src, operand, operand_src); - const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); + const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); + assert(is_non_err != .none); const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); if (is_non_err_tv.val.toBool()) { break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); @@ -1405,7 +1408,8 @@ fn analyzeBodyInner( // const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len]; // const operand = try sema.resolveInst(extra.data.operand); // const err_union = try sema.analyzeLoad(block, src, operand, operand_src); - // const is_non_err = try sema.analyzeIsNonErr(block, operand_src, err_union); + // const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); + // assert(is_non_err != .none); // const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); // if (is_non_err_tv.val.toBool()) { // break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); @@ -13078,9 +13082,9 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError! err_union_ty.fmt(sema.mod), }); } - const is_non_err = try sema.analyzeIsNonErr(parent_block, operand_src, err_union); - - if (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)) |is_non_err_val| { + const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union); + if (is_non_err != .none) { + const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?; if (is_non_err_val.toBool()) { return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false); } @@ -13124,9 +13128,9 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr err_union_ty.fmt(sema.mod), }); } - const is_non_err = try sema.analyzeIsNonErr(parent_block, operand_src, err_union); - - if (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)) |is_non_err_val| { + const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union); + if (is_non_err != .none) { + const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?; if (is_non_err_val.toBool()) { return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false); } @@ -21795,7 +21799,7 @@ fn analyzeIsNull( return block.addUnOp(air_tag, operand); } -fn analyzeIsNonErr( +fn analyzeIsNonErrComptimeOnly( sema: *Sema, block: *Block, src: LazySrcLoc, @@ -21847,8 +21851,22 @@ fn analyzeIsNonErr( return Air.Inst.Ref.bool_false; } } - try sema.requireRuntimeBlock(block, src); - return block.addUnOp(.is_non_err, operand); + return Air.Inst.Ref.none; +} + +fn analyzeIsNonErr( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + operand: Air.Inst.Ref, +) CompileError!Air.Inst.Ref { + const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand); + if (result == .none) { + try sema.requireRuntimeBlock(block, src); + return block.addUnOp(.is_non_err, operand); + } else { + return result; + } } fn analyzeSlice( From ff00bbf4de0c6a91a1e98076ceb68d4cec127aee Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 4 Jun 2022 19:54:00 +0200 Subject: [PATCH 1731/2031] x64: lower try and try_ptr --- src/arch/x86_64/CodeGen.zig | 81 +++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 7d2babed3e..e3ca7c3184 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -681,8 +681,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .prefetch => try self.airPrefetch(inst), .mul_add => try self.airMulAdd(inst), - .@"try" => @panic("TODO"), - .try_ptr => @panic("TODO"), + .@"try" => try self.airTry(inst), + .try_ptr => try self.airTryPtr(inst), .dbg_var_ptr, .dbg_var_val, @@ -1807,14 +1807,24 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); } const err_union_ty = self.air.typeOf(ty_op.operand); + const operand = try self.resolveInst(ty_op.operand); + const result = try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, operand); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + +fn genUnwrapErrorUnionPayloadMir( + self: *Self, + maybe_inst: ?Air.Inst.Index, + err_union_ty: Type, + err_union: MCValue, +) !MCValue { const payload_ty = err_union_ty.errorUnionPayload(); const err_ty = err_union_ty.errorUnionSet(); - const operand = try self.resolveInst(ty_op.operand); const result: MCValue = result: { if (err_ty.errorSetCardinality() == .zero) { // TODO check if we can reuse - break :result operand; + break :result err_union; } if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { @@ -1822,7 +1832,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { } const payload_off = errUnionPayloadOffset(payload_ty, self.target.*); - switch (operand) { + switch (err_union) { .stack_offset => |off| { const offset = off - @intCast(i32, payload_off); break :result MCValue{ .stack_offset = offset }; @@ -1831,19 +1841,23 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { // TODO reuse operand const lock = self.register_manager.lockRegAssumeUnused(reg); defer self.register_manager.unlockReg(lock); - const result = try self.copyToRegisterWithInstTracking(inst, err_union_ty, operand); + const result_reg: Register = if (maybe_inst) |inst| + (try self.copyToRegisterWithInstTracking(inst, err_union_ty, err_union)).register + else + try self.copyToTmpRegister(err_union_ty, err_union); if (payload_off > 0) { const shift = @intCast(u6, payload_off * 8); - try self.genShiftBinOpMir(.shr, err_union_ty, result.register, .{ .immediate = shift }); + try self.genShiftBinOpMir(.shr, err_union_ty, result_reg, .{ .immediate = shift }); } else { - try self.truncateRegister(payload_ty, result.register); + try self.truncateRegister(payload_ty, result_reg); } - break :result result; + break :result MCValue{ .register = result_reg }; }, - else => return self.fail("TODO implement unwrap_err_payload for {}", .{operand}), + else => return self.fail("TODO implement genUnwrapErrorUnionPayloadMir for {}", .{err_union}), } }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + + return result; } // *(E!T) -> E @@ -4231,6 +4245,45 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ un_op, .none, .none }); } +fn airTry(self: *Self, inst: Air.Inst.Index) !void { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const extra = self.air.extraData(Air.Try, pl_op.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + const err_union_ty = self.air.typeOf(pl_op.operand); + const err_union = try self.resolveInst(pl_op.operand); + const result = try self.genTry(inst, err_union, body, err_union_ty, false); + return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); +} + +fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + const err_union_ty = self.air.typeOf(extra.data.ptr).childType(); + const err_union_ptr = try self.resolveInst(extra.data.ptr); + const result = try self.genTry(inst, err_union_ptr, body, err_union_ty, true); + return self.finishAir(inst, result, .{ extra.data.ptr, .none, .none }); +} + +fn genTry( + self: *Self, + inst: Air.Inst.Index, + err_union: MCValue, + body: []const Air.Inst.Index, + err_union_ty: Type, + operand_is_ptr: bool, +) !MCValue { + if (operand_is_ptr) { + return self.fail("TODO genTry for pointers", .{}); + } + const is_err_mcv = try self.isErr(null, err_union_ty, err_union); + const reloc = try self.genCondBrMir(Type.anyerror, is_err_mcv); + try self.genBody(body); + try self.performReloc(reloc); + const result = try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, err_union); + return result; +} + fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; const payload = try self.addExtra(Mir.DbgLineColumn{ @@ -4596,7 +4649,7 @@ fn isNonNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCV return MCValue{ .eflags = is_null_res.eflags.negate() }; } -fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { +fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { const err_type = ty.errorUnionSet(); if (err_type.errorSetCardinality() == .zero) { @@ -4604,7 +4657,9 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue } try self.spillEflagsIfOccupied(); - self.eflags_inst = inst; + if (maybe_inst) |inst| { + self.eflags_inst = inst; + } const err_off = errUnionErrorOffset(ty.errorUnionPayload(), self.target.*); switch (operand) { From 95966f6fd7dc09cb1b03e5c10f1f4feded2537b6 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 5 Jun 2022 16:46:50 +0200 Subject: [PATCH 1732/2031] elf+macho: use explicit alignment on decl is specified --- src/link/Elf.zig | 2 +- src/link/MachO.zig | 29 +++++++++++++++++++++++------ test/behavior/bugs/1741.zig | 1 - 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 442ad73ea7..c0caf79b77 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2300,7 +2300,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s defer self.base.allocator.free(decl_name); log.debug("updateDeclCode {s}{*}", .{ decl_name, decl }); - const required_alignment = decl.ty.abiAlignment(self.base.options.target); + const required_alignment = decl.getAlignment(self.base.options.target); const decl_ptr = self.decls.getPtr(decl_index).?; if (decl_ptr.* == null) { diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 38a1319012..a4d14b985f 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -3786,7 +3786,13 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu atom.code.clearRetainingCapacity(); try atom.code.appendSlice(self.base.allocator, code); - const match = try self.getMatchingSectionAtom(atom, decl_name, typed_value.ty, typed_value.val); + const match = try self.getMatchingSectionAtom( + atom, + decl_name, + typed_value.ty, + typed_value.val, + required_alignment, + ); const addr = try self.allocateAtom(atom, code.len, required_alignment, match); log.debug("allocated atom for {s} at 0x{x}", .{ name, addr }); @@ -3949,11 +3955,16 @@ fn needsPointerRebase(ty: Type, val: Value, mod: *Module) bool { } } -fn getMatchingSectionAtom(self: *MachO, atom: *Atom, name: []const u8, ty: Type, val: Value) !MatchingSection { +fn getMatchingSectionAtom( + self: *MachO, + atom: *Atom, + name: []const u8, + ty: Type, + val: Value, + alignment: u32, +) !MatchingSection { const code = atom.code.items; - const target = self.base.options.target; const mod = self.base.options.module.?; - const alignment = ty.abiAlignment(target); const align_log_2 = math.log2(alignment); const zig_ty = ty.zigTypeTag(); const mode = self.base.options.optimize_mode; @@ -4039,7 +4050,7 @@ fn getMatchingSectionAtom(self: *MachO, atom: *Atom, name: []const u8, ty: Type, fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*macho.nlist_64 { const module = self.base.options.module.?; const decl = module.declPtr(decl_index); - const required_alignment = decl.ty.abiAlignment(self.base.options.target); + const required_alignment = decl.getAlignment(self.base.options.target); assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes() const symbol = &self.locals.items[decl.link.macho.local_sym_index]; @@ -4048,7 +4059,13 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac const decl_ptr = self.decls.getPtr(decl_index).?; if (decl_ptr.* == null) { - decl_ptr.* = try self.getMatchingSectionAtom(&decl.link.macho, sym_name, decl.ty, decl.val); + decl_ptr.* = try self.getMatchingSectionAtom( + &decl.link.macho, + sym_name, + decl.ty, + decl.val, + required_alignment, + ); } const match = decl_ptr.*.?; diff --git a/test/behavior/bugs/1741.zig b/test/behavior/bugs/1741.zig index 0d19eb20c8..63de6e83dc 100644 --- a/test/behavior/bugs/1741.zig +++ b/test/behavior/bugs/1741.zig @@ -5,7 +5,6 @@ test "fixed" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO const x: f32 align(128) = 12.34; try std.testing.expect(@ptrToInt(&x) % 128 == 0); From 5dade176d85bc2944f7f40760ae3105e951d5e65 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 5 Jun 2022 17:31:05 +0200 Subject: [PATCH 1733/2031] sema: set new Decl alignment to 0 for generic call instantiation --- src/Sema.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Sema.zig b/src/Sema.zig index 593b299833..f15440357d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5800,6 +5800,7 @@ fn instantiateGenericCall( } new_decl.val = try Value.Tag.function.create(new_decl_arena_allocator, new_func); + new_decl.@"align" = 0; new_decl.has_tv = true; new_decl.owns_tv = true; new_decl.analysis = .complete; From 135b91aecd9be1f6f5806b667e07e383dd481198 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Mon, 6 Jun 2022 04:13:52 -0600 Subject: [PATCH 1734/2031] Treat blocks with "return" as "noreturn" Block statements that end with "break" should not be considered "noreturn" for the enclosing scope, but other "noreturn" instructions (return, panic, compile error, etc.) should be. This differentiation necessitates handling "break" differently from the other "noreturn" instructions when inside a block statement. --- src/AstGen.zig | 10 ++++++++++ src/Zir.zig | 13 +++++++++++++ .../code_after_return_in_block_is_unreachable.zig | 14 ++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 test/cases/compile_errors/stage2/code_after_return_in_block_is_unreachable.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index ab5befa4ba..21e5170030 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1967,6 +1967,9 @@ fn blockExpr( } try blockExprStmts(gz, scope, statements); + if (gz.endsWithNoReturn() and !gz.endsWithBreak()) { + return Zir.Inst.Ref.unreachable_value; + } return rvalue(gz, rl, .void_value, block_node); } @@ -9930,6 +9933,13 @@ const GenZir = struct { return tags[last_inst].isNoReturn(); } + fn endsWithBreak(gz: GenZir) bool { + if (gz.isEmpty()) return false; + const tags = gz.astgen.instructions.items(.tag); + const last_inst = gz.instructions.items[gz.instructions.items.len - 1]; + return tags[last_inst].isBreak(); + } + /// TODO all uses of this should be replaced with uses of `endsWithNoReturn`. fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool { if (inst_ref == .unreachable_value) return true; diff --git a/src/Zir.zig b/src/Zir.zig index f09f2015e0..bad7b91488 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -1250,6 +1250,19 @@ pub const Inst = struct { }; } + /// Returns whether the instruction is a "break". This differs from + /// isNoReturn because a "break" in a block statement is not a + /// "noreturn" for the outer scope, whereas the other "noreturn" + /// instructions are. + pub fn isBreak(tag: Tag) bool { + return switch (tag) { + .@"break", + .break_inline, + => true, + else => false, + }; + } + /// AstGen uses this to find out if `Ref.void_value` should be used in place /// of the result of a given instruction. This allows Sema to forego adding /// the instruction to the map after analysis. diff --git a/test/cases/compile_errors/stage2/code_after_return_in_block_is_unreachable.zig b/test/cases/compile_errors/stage2/code_after_return_in_block_is_unreachable.zig new file mode 100644 index 0000000000..e62edcb358 --- /dev/null +++ b/test/cases/compile_errors/stage2/code_after_return_in_block_is_unreachable.zig @@ -0,0 +1,14 @@ +export fn entry() void { + { + return; + } + + return; +} + +// error +// target=native +// +// :6:5: error: unreachable code +// :2:5: note: control flow is diverted here + From a34f3ff04a9a6d1c3bf7b818a8de28c453cc53d6 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Mon, 6 Jun 2022 12:34:19 +0200 Subject: [PATCH 1735/2031] stage2 ARM: implement `try` AIR instruction --- src/arch/arm/CodeGen.zig | 114 ++++++++++++++++++++++++--------------- 1 file changed, 70 insertions(+), 44 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 16e4b6e07b..01f3f1ff6a 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -677,8 +677,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .prefetch => try self.airPrefetch(inst), .mul_add => try self.airMulAdd(inst), - .@"try" => @panic("TODO"), - .try_ptr => @panic("TODO"), + .@"try" => try self.airTry(inst), + .try_ptr => try self.airTryPtr(inst), .dbg_var_ptr, .dbg_var_val, @@ -1837,8 +1837,8 @@ fn airUnwrapErrPayload(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 result: { const error_union_ty = self.air.typeOf(ty_op.operand); - const mcv = try self.resolveInst(ty_op.operand); - break :result try self.errUnionPayload(mcv, error_union_ty); + const error_union = try self.resolveInst(ty_op.operand); + break :result try self.errUnionPayload(error_union, error_union_ty); }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -3705,6 +3705,42 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .dead, .{ operand, .none, .none }); } +/// Given a boolean condition, emit a jump that is taken when that +/// condition is false. +fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index { + const condition_code: Condition = switch (condition) { + .cpsr_flags => |cond| cond.negate(), + else => blk: { + const reg = switch (condition) { + .register => |r| r, + else => try self.copyToTmpRegister(Type.bool, condition), + }; + + try self.spillCompareFlagsIfOccupied(); + + // cmp reg, 1 + // bne ... + _ = try self.addInst(.{ + .tag = .cmp, + .cond = .al, + .data = .{ .rr_op = .{ + .rd = .r0, + .rn = reg, + .op = Instruction.Operand.imm(1, 0), + } }, + }); + + break :blk .ne; + }, + }; + + return try self.addInst(.{ + .tag = .b, + .cond = condition_code, + .data = .{ .inst = undefined }, // populated later through performReloc + }); +} + fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { const pl_op = self.air.instructions.items(.data)[inst].pl_op; const cond_inst = try self.resolveInst(pl_op.operand); @@ -3713,39 +3749,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; const liveness_condbr = self.liveness.getCondBr(inst); - const reloc: Mir.Inst.Index = reloc: { - const condition: Condition = switch (cond_inst) { - .cpsr_flags => |cond| cond.negate(), - else => blk: { - const reg = switch (cond_inst) { - .register => |r| r, - else => try self.copyToTmpRegister(Type.bool, cond_inst), - }; - - try self.spillCompareFlagsIfOccupied(); - - // cmp reg, 1 - // bne ... - _ = try self.addInst(.{ - .tag = .cmp, - .cond = .al, - .data = .{ .rr_op = .{ - .rd = .r0, - .rn = reg, - .op = Instruction.Operand.imm(1, 0), - } }, - }); - - break :blk .ne; - }, - }; - - break :reloc try self.addInst(.{ - .tag = .b, - .cond = condition, - .data = .{ .inst = undefined }, // populated later through performReloc - }); - }; + const reloc: Mir.Inst.Index = try self.condBr(cond_inst); // If the condition dies here in this condbr instruction, process // that death now instead of later as this has an effect on @@ -4157,13 +4161,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { .lhs = condition, .rhs = item, } }; - const cmp_result = try self.cmp(operands, condition_ty, .neq); - - relocs[0] = try self.addInst(.{ - .tag = .b, - .cond = cmp_result.cpsr_flags, - .data = .{ .inst = undefined }, // populated later through performReloc - }); + const cmp_result = try self.cmp(operands, condition_ty, .eq); + relocs[0] = try self.condBr(cmp_result); } else { return self.fail("TODO switch with multiple items", .{}); } @@ -5148,6 +5147,33 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); } +fn airTry(self: *Self, inst: Air.Inst.Index) !void { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const extra = self.air.extraData(Air.Try, pl_op.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + const result: MCValue = result: { + const error_union_ty = self.air.typeOf(pl_op.operand); + const error_union = try self.resolveInst(pl_op.operand); + const is_err_result = try self.isErr(error_union_ty, error_union); + const reloc = try self.condBr(is_err_result); + + try self.genBody(body); + + try self.performReloc(reloc); + break :result try self.errUnionPayload(error_union, error_union_ty); + }; + return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); +} + +fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + _ = body; + return self.fail("TODO implement airTryPtr for arm", .{}); + // return self.finishAir(inst, result, .{ extra.data.ptr, .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); From 38aa431e03b19897a7c3c8505f66f4305d8c29b2 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 26 May 2022 19:48:50 +0700 Subject: [PATCH 1736/2031] stage2: sparc64: Fix CompareOperator <-> ICondition mapping --- src/arch/sparc64/bits.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/sparc64/bits.zig b/src/arch/sparc64/bits.zig index f4226b49da..27c4a79c7d 100644 --- a/src/arch/sparc64/bits.zig +++ b/src/arch/sparc64/bits.zig @@ -644,7 +644,7 @@ pub const Instruction = union(enum) { .gt => .gu, .neq => .ne, .lt => .cs, - .lte => .le, + .lte => .leu, .eq => .eq, }; } From 093332c02ed828c6c24d6c7e113ae2804ba7e6f3 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 26 May 2022 20:20:48 +0700 Subject: [PATCH 1737/2031] stage2: sparc64: Implement condition code spilling --- src/arch/sparc64/CodeGen.zig | 88 ++++++++++++++++++++++++++++-------- src/arch/sparc64/Emit.zig | 2 + src/arch/sparc64/Mir.zig | 20 ++++++++ 3 files changed, 90 insertions(+), 20 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index eb9d9a4ad9..42dfc88254 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -131,12 +131,18 @@ const MCValue = union(enum) { stack_offset: u32, /// The value is a pointer to one of the stack variables (payload is stack offset). ptr_stack_offset: u32, - /// The value is in the compare flags assuming an unsigned operation, - /// with this operator applied on top of it. - compare_flags_unsigned: math.CompareOperator, - /// The value is in the compare flags assuming a signed operation, - /// with this operator applied on top of it. - compare_flags_signed: math.CompareOperator, + /// The value is in the specified CCR assuming an unsigned operation, + /// with the operator applied on top of it. + compare_flags_unsigned: struct { + cmp: math.CompareOperator, + ccr: Instruction.CCR, + }, + /// The value is in the specified CCR assuming an signed operation, + /// with the operator applied on top of it. + compare_flags_signed: struct { + cmp: math.CompareOperator, + ccr: Instruction.CCR, + }, fn isMemory(mcv: MCValue) bool { return switch (mcv) { @@ -1086,8 +1092,8 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { self.compare_flags_inst = inst; break :result switch (int_info.signedness) { - .signed => MCValue{ .compare_flags_signed = op }, - .unsigned => MCValue{ .compare_flags_unsigned = op }, + .signed => MCValue{ .compare_flags_signed = .{ .cmp = op, .ccr = .xcc } }, + .unsigned => MCValue{ .compare_flags_unsigned = .{ .cmp = op, .ccr = .xcc } }, }; } else { return self.fail("TODO SPARCv9 cmp for ints > 64 bits", .{}); @@ -1113,16 +1119,20 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { .tag = .bpcc, .data = .{ .branch_predict_int = .{ - .ccr = .xcc, + .ccr = switch (cond) { + .compare_flags_signed => |cmp_op| cmp_op.ccr, + .compare_flags_unsigned => |cmp_op| cmp_op.ccr, + else => unreachable, + }, .cond = switch (cond) { .compare_flags_signed => |cmp_op| blk: { // Here we map to the opposite condition because the jump is to the false branch. - const condition = Instruction.ICondition.fromCompareOperatorSigned(cmp_op); + const condition = Instruction.ICondition.fromCompareOperatorSigned(cmp_op.cmp); break :blk condition.negate(); }, .compare_flags_unsigned => |cmp_op| blk: { // Here we map to the opposite condition because the jump is to the false branch. - const condition = Instruction.ICondition.fromCompareOperatorUnsigned(cmp_op); + const condition = Instruction.ICondition.fromCompareOperatorUnsigned(cmp_op.cmp); break :blk condition.negate(); }, else => unreachable, @@ -2290,8 +2300,47 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void switch (mcv) { .dead => unreachable, .unreach, .none => return, // Nothing to do. - .compare_flags_signed => return self.fail("TODO: genSetReg for compare_flags_signed", .{}), - .compare_flags_unsigned => return self.fail("TODO: genSetReg for compare_flags_unsigned", .{}), + .compare_flags_signed, + .compare_flags_unsigned, + => { + const condition = switch (mcv) { + .compare_flags_unsigned => |op| Instruction.ICondition.fromCompareOperatorUnsigned(op.cmp), + .compare_flags_signed => |op| Instruction.ICondition.fromCompareOperatorSigned(op.cmp), + else => unreachable, + }; + + const ccr = switch (mcv) { + .compare_flags_unsigned => |op| op.ccr, + .compare_flags_signed => |op| op.ccr, + else => unreachable, + }; + // TODO handle floating point CCRs + assert(ccr == .xcc or ccr == .icc); + + _ = try self.addInst(.{ + .tag = .mov, + .data = .{ + .arithmetic_2op = .{ + .is_imm = false, + .rs1 = reg, + .rs2_or_imm = .{ .rs2 = .g0 }, + }, + }, + }); + + _ = try self.addInst(.{ + .tag = .movcc, + .data = .{ + .conditional_move = .{ + .ccr = ccr, + .cond = .{ .icond = condition }, + .is_imm = true, + .rd = reg, + .rs2_or_imm = .{ .imm = 1 }, + }, + }, + }); + }, .undef => { if (!self.wantSafety()) return; // The already existing value will do just fine. @@ -2644,7 +2693,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { } }, }); - return MCValue{ .compare_flags_unsigned = .gt }; + return MCValue{ .compare_flags_unsigned = .{ .cmp = .gt, .ccr = .xcc } }; } else { return self.fail("TODO isErr for errors with size > 8", .{}); } @@ -2658,8 +2707,8 @@ fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { const is_err_result = try self.isErr(ty, operand); switch (is_err_result) { .compare_flags_unsigned => |op| { - assert(op == .gt); - return MCValue{ .compare_flags_unsigned = .lte }; + assert(op.cmp == .gt); + return MCValue{ .compare_flags_unsigned = .{ .cmp = .gt, .ccr = op.ccr } }; }, .immediate => |imm| { assert(imm == 0); @@ -3014,14 +3063,13 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { fn spillCompareFlagsIfOccupied(self: *Self) !void { if (self.compare_flags_inst) |inst_to_save| { const mcv = self.getResolvedInstValue(inst_to_save); - switch (mcv) { + const new_mcv = switch (mcv) { .compare_flags_signed, .compare_flags_unsigned, - => {}, + => try self.allocRegOrMem(inst_to_save, true), else => unreachable, // mcv doesn't occupy the compare flags - } + }; - const new_mcv = try self.allocRegOrMem(inst_to_save, true); try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 1b40cc6e21..55de28d4b3 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -94,6 +94,8 @@ pub fn emitMir( .@"or" => try emit.mirArithmetic3Op(inst), + .movcc => @panic("TODO implement sparc64 movcc"), + .mulx => try emit.mirArithmetic3Op(inst), .nop => try emit.mirNop(), diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index 2ef66a1fa4..dada29ac18 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -74,6 +74,10 @@ pub const Inst = struct { // TODO add other operations. @"or", + /// A.35 Move Integer Register on Condition (MOVcc) + /// This uses the conditional_move field. + movcc, + /// A.37 Multiply and Divide (64-bit) /// This uses the arithmetic_3op field. // TODO add other operations. @@ -216,6 +220,22 @@ pub const Inst = struct { inst: Index, }, + /// Conditional move. + /// if is_imm true then it uses the imm field of rs2_or_imm, + /// otherwise it uses rs2 field. + /// + /// Used by e.g. movcc + conditional_move: struct { + is_imm: bool, + ccr: Instruction.CCR, + cond: Instruction.Condition, + rd: Register, + rs2_or_imm: union { + rs2: Register, + imm: i11, + }, + }, + /// No additional data /// /// Used by e.g. flushw From 3d662cfaf4579cb01546c3e5123eece70fd60b9a Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 27 May 2022 19:53:16 +0700 Subject: [PATCH 1738/2031] stage2: sparc64: Implement airAddSubOverflow --- src/arch/sparc64/CodeGen.zig | 150 ++++++++++++++++++++++++++++++++--- src/arch/sparc64/Emit.zig | 1 + src/arch/sparc64/Mir.zig | 1 + 3 files changed, 143 insertions(+), 9 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 42dfc88254..088ee8dcdb 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -123,6 +123,16 @@ const MCValue = union(enum) { immediate: u64, /// The value is in a target-specific register. register: Register, + /// The value is a tuple { wrapped, overflow } where + /// wrapped is stored in the register and the overflow bit is + /// stored in the C (signed) or V (unsigned) flag of the CCR. + /// + /// This MCValue is only generated by a add_with_overflow or + /// sub_with_overflow instruction operating on 32- or 64-bit values. + register_with_overflow: struct { + reg: Register, + flag: struct { cond: Instruction.ICondition, ccr: Instruction.CCR }, + }, /// The value is in memory at a hard-coded address. /// If the type is a pointer, it means the pointer address is at this memory location. memory: u64, @@ -525,8 +535,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .trunc_float, => @panic("TODO try self.airUnaryMath(inst)"), - .add_with_overflow => @panic("TODO try self.airAddWithOverflow(inst)"), - .sub_with_overflow => @panic("TODO try self.airSubWithOverflow(inst)"), + .add_with_overflow => try self.airAddSubWithOverflow(inst), + .sub_with_overflow => try self.airAddSubWithOverflow(inst), .mul_with_overflow => @panic("TODO try self.airMulWithOverflow(inst)"), .shl_with_overflow => @panic("TODO try self.airShlWithOverflow(inst)"), @@ -684,6 +694,88 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { } } +fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { + const tag = self.air.instructions.items(.tag)[inst]; + 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 result: { + const lhs = try self.resolveInst(extra.lhs); + const rhs = try self.resolveInst(extra.rhs); + const lhs_ty = self.air.typeOf(extra.lhs); + const rhs_ty = self.air.typeOf(extra.rhs); + + switch (lhs_ty.zigTypeTag()) { + .Vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}), + .Int => { + const mod = self.bin_file.options.module.?; + assert(lhs_ty.eql(rhs_ty, mod)); + const int_info = lhs_ty.intInfo(self.target.*); + switch (int_info.bits) { + 32, 64 => { + // Only say yes if the operation is + // commutative, i.e. we can swap both of the + // operands + const lhs_immediate_ok = switch (tag) { + .add_with_overflow => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), + .sub_with_overflow => false, + else => unreachable, + }; + const rhs_immediate_ok = switch (tag) { + .add_with_overflow, + .sub_with_overflow, + => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), + else => unreachable, + }; + + const mir_tag: Mir.Inst.Tag = switch (tag) { + .add_with_overflow => .addcc, + .sub_with_overflow => .subcc, + else => unreachable, + }; + + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = inst; + + const dest = blk: { + if (rhs_immediate_ok) { + break :blk try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, null); + } else if (lhs_immediate_ok) { + // swap lhs and rhs + break :blk try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, null); + } else { + break :blk try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, null); + } + }; + + const cond = switch (int_info.signedness) { + .unsigned => switch (tag) { + .add_with_overflow => Instruction.ICondition.cs, + .sub_with_overflow => Instruction.ICondition.cc, + else => unreachable, + }, + .signed => Instruction.ICondition.vs, + }; + + const ccr = switch (int_info.bits) { + 32 => Instruction.CCR.icc, + 64 => Instruction.CCR.xcc, + else => unreachable, + }; + + break :result MCValue{ .register_with_overflow = .{ + .reg = dest.register, + .flag = .{ .cond = cond, .ccr = ccr }, + } }; + }, + else => return self.fail("TODO overflow operations on other integer sizes", .{}), + } + }, + else => unreachable, + } + }; + return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); +} + fn airAlloc(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 }); @@ -955,13 +1047,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. switch (mc_arg) { .none => continue, - .undef => unreachable, - .immediate => unreachable, - .unreach => unreachable, - .dead => unreachable, - .memory => unreachable, - .compare_flags_signed => unreachable, - .compare_flags_unsigned => unreachable, .register => |reg| { try self.register_manager.getReg(reg, null); try self.genSetReg(arg_ty, reg, arg_mcv); @@ -972,6 +1057,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .ptr_stack_offset => { return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); }, + else => unreachable, } } @@ -1894,6 +1980,7 @@ fn binOpImmediate( const mir_data: Mir.Inst.Data = switch (mir_tag) { .add, + .addcc, .mulx, .subcc, => .{ @@ -2010,6 +2097,7 @@ fn binOpRegister( const mir_data: Mir.Inst.Data = switch (mir_tag) { .add, + .addcc, .mulx, .subcc, => .{ @@ -2473,6 +2561,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }, }); }, + .register_with_overflow => unreachable, .memory => |addr| { // The value is in memory at a hard-coded address. // If the type is a pointer, it means the pointer address is at this memory location. @@ -2519,6 +2608,47 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro return self.fail("TODO larger stack offsets", .{}); return self.genStore(reg, .sp, i13, simm13, abi_size); }, + .register_with_overflow => |rwo| { + const reg_lock = self.register_manager.lockReg(rwo.reg); + defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg); + + const wrapped_ty = ty.structFieldType(0); + try self.genSetStack(wrapped_ty, stack_offset, .{ .register = rwo.reg }); + + const overflow_bit_ty = ty.structFieldType(1); + const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*)); + const cond_reg = try self.register_manager.allocReg(null, gp); + + // TODO handle floating point CCRs + assert(rwo.flag.ccr == .xcc or rwo.flag.ccr == .icc); + + _ = try self.addInst(.{ + .tag = .mov, + .data = .{ + .arithmetic_2op = .{ + .is_imm = false, + .rs1 = cond_reg, + .rs2_or_imm = .{ .rs2 = .g0 }, + }, + }, + }); + + _ = try self.addInst(.{ + .tag = .movcc, + .data = .{ + .conditional_move = .{ + .ccr = rwo.flag.ccr, + .cond = .{ .icond = rwo.flag.cond }, + .is_imm = true, + .rd = cond_reg, + .rs2_or_imm = .{ .imm = 1 }, + }, + }, + }); + try self.genSetStack(overflow_bit_ty, stack_offset - overflow_bit_offset, .{ + .register = cond_reg, + }); + }, .memory, .stack_offset => { switch (mcv) { .stack_offset => |off| { @@ -2760,6 +2890,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .dead => unreachable, .compare_flags_unsigned, .compare_flags_signed, + .register_with_overflow, => unreachable, // cannot hold an address .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), @@ -3100,6 +3231,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .dead => unreachable, .compare_flags_unsigned, .compare_flags_signed, + .register_with_overflow, => unreachable, // cannot hold an address .immediate => |imm| { try self.setRegOrMem(value_ty, .{ .memory = imm }, value); diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 55de28d4b3..2383e6c146 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -79,6 +79,7 @@ pub fn emitMir( .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(), .add => try emit.mirArithmetic3Op(inst), + .addcc => @panic("TODO implement sparc64 addcc"), .bpr => try emit.mirConditionalBranch(inst), .bpcc => try emit.mirConditionalBranch(inst), diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index dada29ac18..36849eb48a 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -42,6 +42,7 @@ pub const Inst = struct { /// This uses the arithmetic_3op field. // TODO add other operations. add, + addcc, /// A.3 Branch on Integer Register with Prediction (BPr) /// This uses the branch_predict_reg field. From 9db81fee5df6abcf1eb1212ecc005d22f7b2ca32 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 27 May 2022 23:00:11 +0700 Subject: [PATCH 1739/2031] stage2: sparc64: Implement airStructFieldVal --- src/arch/sparc64/CodeGen.zig | 92 +++++++++++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 7 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 088ee8dcdb..f63995cf5c 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -592,7 +592,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .ret_load => try self.airRetLoad(inst), .store => try self.airStore(inst), .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"), - .struct_field_val=> @panic("TODO try self.airStructFieldVal(inst)"), + .struct_field_val=> try self.airStructFieldVal(inst), .array_to_slice => @panic("TODO try self.airArrayToSlice(inst)"), .int_to_float => @panic("TODO try self.airIntToFloat(inst)"), .float_to_int => @panic("TODO try self.airFloatToInt(inst)"), @@ -1598,6 +1598,75 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; + const operand = extra.struct_operand; + const index = extra.field_index; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const mcv = try self.resolveInst(operand); + const struct_ty = self.air.typeOf(operand); + const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); + + switch (mcv) { + .dead, .unreach => unreachable, + .stack_offset => |off| { + break :result MCValue{ .stack_offset = off - struct_field_offset }; + }, + .memory => |addr| { + break :result MCValue{ .memory = addr + struct_field_offset }; + }, + .register_with_overflow => |rwo| { + switch (index) { + 0 => { + // get wrapped value: return register + break :result MCValue{ .register = rwo.reg }; + }, + 1 => { + // TODO return special MCValue condition flags + // get overflow bit: set register to C flag + // resp. V flag + const dest_reg = try self.register_manager.allocReg(null, gp); + + // TODO handle floating point CCRs + assert(rwo.flag.ccr == .xcc or rwo.flag.ccr == .icc); + + _ = try self.addInst(.{ + .tag = .mov, + .data = .{ + .arithmetic_2op = .{ + .is_imm = false, + .rs1 = dest_reg, + .rs2_or_imm = .{ .rs2 = .g0 }, + }, + }, + }); + + _ = try self.addInst(.{ + .tag = .movcc, + .data = .{ + .conditional_move = .{ + .ccr = rwo.flag.ccr, + .cond = .{ .icond = rwo.flag.cond }, + .is_imm = true, + .rd = dest_reg, + .rs2_or_imm = .{ .imm = 1 }, + }, + }, + }); + + break :result MCValue{ .register = dest_reg }; + }, + else => unreachable, + } + }, + else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), + } + }; + + return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); +} + fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { _ = self; _ = inst; @@ -1664,8 +1733,8 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u if (abi_align > self.stack_align) self.stack_align = abi_align; // TODO find a free slot instead of always appending - const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align); - self.next_stack_offset = offset + abi_size; + const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size; + self.next_stack_offset = offset; if (self.next_stack_offset > self.max_end_stack) self.max_end_stack = self.next_stack_offset; try self.stack.putNoClobber(self.gpa, offset, .{ @@ -2436,8 +2505,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); }, .ptr_stack_offset => |off| { - const simm13 = math.cast(u12, off + abi.stack_bias + abi.stack_reserved_area) orelse - return self.fail("TODO larger stack offsets", .{}); + const real_offset = off + abi.stack_bias + abi.stack_reserved_area; + const simm13 = math.cast(i13, real_offset) orelse + return self.fail("TODO larger stack offsets: {}", .{real_offset}); _ = try self.addInst(.{ .tag = .add, @@ -2571,7 +2641,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .stack_offset => |off| { const real_offset = off + abi.stack_bias + abi.stack_reserved_area; const simm13 = math.cast(i13, real_offset) orelse - return self.fail("TODO larger stack offsets", .{}); + return self.fail("TODO larger stack offsets: {}", .{real_offset}); try self.genLoad(reg, .sp, i13, simm13, ty.abiSize(self.target.*)); }, } @@ -2605,7 +2675,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro .register => |reg| { const real_offset = stack_offset + abi.stack_bias + abi.stack_reserved_area; const simm13 = math.cast(i13, real_offset) orelse - return self.fail("TODO larger stack offsets", .{}); + return self.fail("TODO larger stack offsets: {}", .{real_offset}); return self.genStore(reg, .sp, i13, simm13, abi_size); }, .register_with_overflow => |rwo| { @@ -3198,6 +3268,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { .compare_flags_signed, .compare_flags_unsigned, => try self.allocRegOrMem(inst_to_save, true), + .register_with_overflow => try self.allocRegOrMem(inst_to_save, false), else => unreachable, // mcv doesn't occupy the compare flags }; @@ -3208,6 +3279,13 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); self.compare_flags_inst = null; + + // TODO consolidate with register manager and spillInstruction + // this call should really belong in the register manager! + switch (mcv) { + .register_with_overflow => |rwo| self.register_manager.freeReg(rwo.reg), + else => {}, + } } } From 3220e0b61c127b60188a040b5f4679765e8f6f40 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sat, 28 May 2022 09:05:57 +0700 Subject: [PATCH 1740/2031] stage2: sparc64: Proper handling of compare flags --- src/arch/sparc64/CodeGen.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index f63995cf5c..a4c6635b4c 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -2029,6 +2029,7 @@ fn binOpImmediate( defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const dest_reg = switch (mir_tag) { + .cmp => undefined, // cmp has no destination register else => if (metadata) |md| blk: { if (lhs_is_register and self.reuseOperand( md.inst, @@ -2148,6 +2149,7 @@ fn binOpRegister( defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); const dest_reg = switch (mir_tag) { + .cmp => undefined, // cmp has no destination register else => if (metadata) |md| blk: { if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) { break :blk lhs_reg; @@ -3064,6 +3066,10 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { .register => |reg| { self.register_manager.freeReg(reg); }, + .register_with_overflow => |rwo| { + self.register_manager.freeReg(rwo.reg); + self.compare_flags_inst = null; + }, .compare_flags_signed, .compare_flags_unsigned => { self.compare_flags_inst = null; }, From 89b4195c69c53e8b1fdd3f5ddd2c9658a61daaac Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sat, 28 May 2022 09:06:25 +0700 Subject: [PATCH 1741/2031] stage2: sparc64: Account for delay slot in airBlock --- src/arch/sparc64/CodeGen.zig | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index a4c6635b4c..bc73ea02b3 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -991,9 +991,14 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { const relocs = &self.blocks.getPtr(inst).?.relocs; if (relocs.items.len > 0 and relocs.items[relocs.items.len - 1] == self.mir_instructions.len - 1) { // If the last Mir instruction is the last relocation (which - // would just jump one instruction further), it can be safely + // would just jump two instruction further), it can be safely // removed - self.mir_instructions.orderedRemove(relocs.pop()); + const index = relocs.pop(); + + // First, remove the delay slot, then remove + // the branch instruction itself. + self.mir_instructions.orderedRemove(index + 1); + self.mir_instructions.orderedRemove(index); } for (relocs.items) |reloc| { try self.performReloc(reloc); From 2dfe307d6003f36279742621b8da29a162a7149e Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 1 Jun 2022 08:45:51 +0700 Subject: [PATCH 1742/2031] stage2: sparc64: Some bookkeeping fixes --- src/arch/sparc64/CodeGen.zig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index bc73ea02b3..616ff45b43 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -686,6 +686,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { // zig fmt: on } + assert(!self.register_manager.lockedRegsExist()); + if (std.debug.runtime_safety) { if (self.air_bookkeeping < old_air_bookkeeping + 1) { std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[inst] }); @@ -2915,7 +2917,7 @@ fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { switch (is_err_result) { .compare_flags_unsigned => |op| { assert(op.cmp == .gt); - return MCValue{ .compare_flags_unsigned = .{ .cmp = .gt, .ccr = op.ccr } }; + return MCValue{ .compare_flags_unsigned = .{ .cmp = .lte, .ccr = op.ccr } }; }, .immediate => |imm| { assert(imm == 0); @@ -3176,7 +3178,7 @@ fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { const ref_int = @enumToInt(inst); if (ref_int < Air.Inst.Ref.typed_value_map.len) { const tv = Air.Inst.Ref.typed_value_map[ref_int]; - if (!tv.ty.hasRuntimeBits()) { + if (!tv.ty.hasRuntimeBits() and !tv.ty.isError()) { return MCValue{ .none = {} }; } return self.genTypedValue(tv); @@ -3184,7 +3186,7 @@ fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { // If the type has no codegen bits, no need to store it. const inst_ty = self.air.typeOf(inst); - if (!inst_ty.hasRuntimeBits()) + if (!inst_ty.hasRuntimeBits() and !inst_ty.isError()) return MCValue{ .none = {} }; const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len); From 97c43afefe2938119b0c4d14032f9e4b41926716 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 1 Jun 2022 08:58:14 +0700 Subject: [PATCH 1743/2031] stage2: sparc64: Spill CCR before doing calls --- src/arch/sparc64/CodeGen.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 616ff45b43..5af1e856c8 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1047,6 +1047,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. var info = try self.resolveCallingConventionValues(fn_ty, .caller); defer info.deinit(self); + + // CCR is volatile across function calls + // (SCD 2.4.1, page 3P-10) + try self.spillCompareFlagsIfOccupied(); + for (info.args) |mc_arg, arg_i| { const arg = args[arg_i]; const arg_ty = self.air.typeOf(arg); From 9ad74b60874447b81747651e28246d3f2168940a Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 1 Jun 2022 09:41:40 +0700 Subject: [PATCH 1744/2031] stage2: sparc64: Implement SPARCv9 addcc and movcc --- src/arch/sparc64/Emit.zig | 34 ++++++++++++++++++++++++++++++++-- src/arch/sparc64/bits.zig | 16 ++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 2383e6c146..44c14752e4 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -79,7 +79,7 @@ pub fn emitMir( .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(), .add => try emit.mirArithmetic3Op(inst), - .addcc => @panic("TODO implement sparc64 addcc"), + .addcc => try emit.mirArithmetic3Op(inst), .bpr => try emit.mirConditionalBranch(inst), .bpcc => try emit.mirConditionalBranch(inst), @@ -95,7 +95,7 @@ pub fn emitMir( .@"or" => try emit.mirArithmetic3Op(inst), - .movcc => @panic("TODO implement sparc64 movcc"), + .movcc => try emit.mirConditionalMove(inst), .mulx => try emit.mirArithmetic3Op(inst), @@ -212,6 +212,7 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { const imm = data.rs2_or_imm.imm; switch (tag) { .add => try emit.writeInstruction(Instruction.add(i13, rs1, imm, rd)), + .addcc => try emit.writeInstruction(Instruction.addcc(i13, rs1, imm, rd)), .jmpl => try emit.writeInstruction(Instruction.jmpl(i13, rs1, imm, rd)), .ldub => try emit.writeInstruction(Instruction.ldub(i13, rs1, imm, rd)), .lduh => try emit.writeInstruction(Instruction.lduh(i13, rs1, imm, rd)), @@ -233,6 +234,7 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { const rs2 = data.rs2_or_imm.rs2; switch (tag) { .add => try emit.writeInstruction(Instruction.add(Register, rs1, rs2, rd)), + .addcc => try emit.writeInstruction(Instruction.addcc(Register, rs1, rs2, rd)), .jmpl => try emit.writeInstruction(Instruction.jmpl(Register, rs1, rs2, rd)), .ldub => try emit.writeInstruction(Instruction.ldub(Register, rs1, rs2, rd)), .lduh => try emit.writeInstruction(Instruction.lduh(Register, rs1, rs2, rd)), @@ -297,6 +299,34 @@ fn mirConditionalBranch(emit: *Emit, inst: Mir.Inst.Index) !void { } } +fn mirConditionalMove(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + + switch (tag) { + .movcc => { + const data = emit.mir.instructions.items(.data)[inst].conditional_move; + if (data.is_imm) { + try emit.writeInstruction(Instruction.movcc( + i11, + data.cond, + data.ccr, + data.rs2_or_imm.imm, + data.rd, + )); + } else { + try emit.writeInstruction(Instruction.movcc( + Register, + data.cond, + data.ccr, + data.rs2_or_imm.rs2, + data.rd, + )); + } + }, + else => unreachable, + } +} + fn mirNop(emit: *Emit) !void { try emit.writeInstruction(Instruction.nop()); } diff --git a/src/arch/sparc64/bits.zig b/src/arch/sparc64/bits.zig index 27c4a79c7d..615c224ae7 100644 --- a/src/arch/sparc64/bits.zig +++ b/src/arch/sparc64/bits.zig @@ -1141,6 +1141,14 @@ pub const Instruction = union(enum) { }; } + pub fn addcc(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b10, 0b01_0000, rs1, rs2, rd), + i13 => format3b(0b10, 0b01_0000, rs1, rs2, rd), + else => unreachable, + }; + } + pub fn bpcc(cond: ICondition, annul: bool, pt: bool, ccr: CCR, disp: i21) Instruction { return format2c(0b001, .{ .icond = cond }, annul, pt, ccr, disp); } @@ -1197,6 +1205,14 @@ pub const Instruction = union(enum) { }; } + pub fn movcc(comptime s2: type, cond: Condition, ccr: CCR, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format4c(0b10_1100, cond, ccr, rs2, rd), + i11 => format4d(0b10_1100, cond, ccr, rs2, rd), + else => unreachable, + }; + } + pub fn mulx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { return switch (s2) { Register => format3a(0b10, 0b00_1001, rs1, rs2, rd), From 23150de9c41330351bc5f7b62418617d5714203d Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 1 Jun 2022 19:13:43 +0700 Subject: [PATCH 1745/2031] stage2: sparc64: Implement airNot --- src/arch/sparc64/CodeGen.zig | 122 ++++++++++++++++++++++++++++++++++- src/arch/sparc64/Emit.zig | 4 ++ src/arch/sparc64/Mir.zig | 9 +++ 3 files changed, 134 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 5af1e856c8..8e1e3678a9 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -586,7 +586,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .is_err_ptr => @panic("TODO try self.airIsErrPtr(inst)"), .load => try self.airLoad(inst), .loop => try self.airLoop(inst), - .not => @panic("TODO try self.airNot(inst)"), + .not => try self.airNot(inst), .ptrtoint => @panic("TODO try self.airPtrToInt(inst)"), .ret => try self.airRet(inst), .ret_load => try self.airRetLoad(inst), @@ -1507,6 +1507,126 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { return self.finishAirBookkeeping(); } +fn airNot(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 result: { + const operand = try self.resolveInst(ty_op.operand); + const operand_ty = self.air.typeOf(ty_op.operand); + switch (operand) { + .dead => unreachable, + .unreach => unreachable, + .compare_flags_unsigned => |op| { + const r = MCValue{ + .compare_flags_unsigned = .{ + .cmp = switch (op.cmp) { + .gte => .lt, + .gt => .lte, + .neq => .eq, + .lt => .gte, + .lte => .gt, + .eq => .neq, + }, + .ccr = op.ccr, + }, + }; + break :result r; + }, + .compare_flags_signed => |op| { + const r = MCValue{ + .compare_flags_signed = .{ + .cmp = switch (op.cmp) { + .gte => .lt, + .gt => .lte, + .neq => .eq, + .lt => .gte, + .lte => .gt, + .eq => .neq, + }, + .ccr = op.ccr, + }, + }; + break :result r; + }, + else => { + switch (operand_ty.zigTypeTag()) { + .Bool => { + // TODO convert this to mvn + and + const op_reg = switch (operand) { + .register => |r| r, + else => try self.copyToTmpRegister(operand_ty, operand), + }; + const reg_lock = self.register_manager.lockRegAssumeUnused(op_reg); + defer self.register_manager.unlockReg(reg_lock); + + const dest_reg = blk: { + if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { + break :blk op_reg; + } + + const reg = try self.register_manager.allocReg(null, gp); + break :blk reg; + }; + + _ = try self.addInst(.{ + .tag = .xor, + .data = .{ + .arithmetic_3op = .{ + .is_imm = true, + .rd = dest_reg, + .rs1 = op_reg, + .rs2_or_imm = .{ .imm = 1 }, + }, + }, + }); + + break :result MCValue{ .register = dest_reg }; + }, + .Vector => return self.fail("TODO bitwise not for vectors", .{}), + .Int => { + const int_info = operand_ty.intInfo(self.target.*); + if (int_info.bits <= 64) { + const op_reg = switch (operand) { + .register => |r| r, + else => try self.copyToTmpRegister(operand_ty, operand), + }; + const reg_lock = self.register_manager.lockRegAssumeUnused(op_reg); + defer self.register_manager.unlockReg(reg_lock); + + const dest_reg = blk: { + if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { + break :blk op_reg; + } + + const reg = try self.register_manager.allocReg(null, gp); + break :blk reg; + }; + + _ = try self.addInst(.{ + .tag = .not, + .data = .{ + .arithmetic_2op = .{ + .is_imm = false, + .rs1 = dest_reg, + .rs2_or_imm = .{ .rs2 = op_reg }, + }, + }, + }); + + try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits); + + break :result MCValue{ .register = dest_reg }; + } else { + return self.fail("TODO AArch64 not on integers > u64/i64", .{}); + } + }, + else => unreachable, + } + }, + } + }; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airRet(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 44c14752e4..2fcb593585 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -94,6 +94,8 @@ pub fn emitMir( .ldx => try emit.mirArithmetic3Op(inst), .@"or" => try emit.mirArithmetic3Op(inst), + .xor => @panic("TODO implement sparc64 xor"), + .xnor => @panic("TODO implement sparc64 xnor"), .movcc => try emit.mirConditionalMove(inst), @@ -128,6 +130,8 @@ pub fn emitMir( .cmp => try emit.mirArithmetic2Op(inst), .mov => try emit.mirArithmetic2Op(inst), + + .not => @panic("TODO implement sparc64 not"), } } } diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index 36849eb48a..14867dde30 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -74,6 +74,8 @@ pub const Inst = struct { /// This uses the arithmetic_3op field. // TODO add other operations. @"or", + xor, + xnor, /// A.35 Move Integer Register on Condition (MOVcc) /// This uses the conditional_move field. @@ -147,6 +149,13 @@ pub const Inst = struct { /// being the *destination* register. // TODO is it okay to abuse rs1 in this way? mov, // mov rs2/imm, rs1 -> or %g0, rs2/imm, rs1 + + /// Bitwise negation + /// This uses the arithmetic_2op field, with rs1 + /// being the *destination* register. + // TODO is it okay to abuse rs1 in this way? + // TODO this differs from official encoding for convenience, fix it later + not, // not rs2/imm, rs1 -> xnor %g0, rs2/imm, rs1 }; /// The position of an MIR instruction within the `Mir` instructions array. From 97f9bf7e90ab10119b8225799590886e911775db Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 1 Jun 2022 19:20:40 +0700 Subject: [PATCH 1746/2031] stage2: sparc64: Add BPr relocation to performReloc --- src/arch/sparc64/CodeGen.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 8e1e3678a9..8279c8e123 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -3182,6 +3182,7 @@ fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { const tag = self.mir_instructions.items(.tag)[inst]; switch (tag) { .bpcc => self.mir_instructions.items(.data)[inst].branch_predict_int.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len), + .bpr => self.mir_instructions.items(.data)[inst].branch_predict_reg.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len), else => unreachable, } } From 4d50e52c37cad45bd3d8fd520359cdaee70aaf1f Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 1 Jun 2022 19:29:09 +0700 Subject: [PATCH 1747/2031] stage2: sparc64: Implement SPARCv9 xor, xnor, & not --- src/arch/sparc64/Emit.zig | 12 +++++++++--- src/arch/sparc64/bits.zig | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 2fcb593585..5a082f163a 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -94,8 +94,8 @@ pub fn emitMir( .ldx => try emit.mirArithmetic3Op(inst), .@"or" => try emit.mirArithmetic3Op(inst), - .xor => @panic("TODO implement sparc64 xor"), - .xnor => @panic("TODO implement sparc64 xnor"), + .xor => try emit.mirArithmetic3Op(inst), + .xnor => try emit.mirArithmetic3Op(inst), .movcc => try emit.mirConditionalMove(inst), @@ -131,7 +131,7 @@ pub fn emitMir( .mov => try emit.mirArithmetic2Op(inst), - .not => @panic("TODO implement sparc64 not"), + .not => try emit.mirArithmetic2Op(inst), } } } @@ -192,6 +192,7 @@ fn mirArithmetic2Op(emit: *Emit, inst: Mir.Inst.Index) !void { .@"return" => try emit.writeInstruction(Instruction.@"return"(i13, rs1, imm)), .cmp => try emit.writeInstruction(Instruction.subcc(i13, rs1, imm, .g0)), .mov => try emit.writeInstruction(Instruction.@"or"(i13, .g0, imm, rs1)), + .not => try emit.writeInstruction(Instruction.xnor(i13, .g0, imm, rs1)), else => unreachable, } } else { @@ -200,6 +201,7 @@ fn mirArithmetic2Op(emit: *Emit, inst: Mir.Inst.Index) !void { .@"return" => try emit.writeInstruction(Instruction.@"return"(Register, rs1, rs2)), .cmp => try emit.writeInstruction(Instruction.subcc(Register, rs1, rs2, .g0)), .mov => try emit.writeInstruction(Instruction.@"or"(Register, .g0, rs2, rs1)), + .not => try emit.writeInstruction(Instruction.xnor(Register, .g0, rs2, rs1)), else => unreachable, } } @@ -223,6 +225,8 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { .lduw => try emit.writeInstruction(Instruction.lduw(i13, rs1, imm, rd)), .ldx => try emit.writeInstruction(Instruction.ldx(i13, rs1, imm, rd)), .@"or" => try emit.writeInstruction(Instruction.@"or"(i13, rs1, imm, rd)), + .xor => try emit.writeInstruction(Instruction.xor(i13, rs1, imm, rd)), + .xnor => try emit.writeInstruction(Instruction.xnor(i13, rs1, imm, rd)), .mulx => try emit.writeInstruction(Instruction.mulx(i13, rs1, imm, rd)), .save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)), .restore => try emit.writeInstruction(Instruction.restore(i13, rs1, imm, rd)), @@ -245,6 +249,8 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { .lduw => try emit.writeInstruction(Instruction.lduw(Register, rs1, rs2, rd)), .ldx => try emit.writeInstruction(Instruction.ldx(Register, rs1, rs2, rd)), .@"or" => try emit.writeInstruction(Instruction.@"or"(Register, rs1, rs2, rd)), + .xor => try emit.writeInstruction(Instruction.xor(Register, rs1, rs2, rd)), + .xnor => try emit.writeInstruction(Instruction.xnor(Register, rs1, rs2, rd)), .mulx => try emit.writeInstruction(Instruction.mulx(Register, rs1, rs2, rd)), .save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)), .restore => try emit.writeInstruction(Instruction.restore(Register, rs1, rs2, rd)), diff --git a/src/arch/sparc64/bits.zig b/src/arch/sparc64/bits.zig index 615c224ae7..7b0342e2a3 100644 --- a/src/arch/sparc64/bits.zig +++ b/src/arch/sparc64/bits.zig @@ -1205,6 +1205,22 @@ pub const Instruction = union(enum) { }; } + pub fn xor(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b10, 0b00_0011, rs1, rs2, rd), + i13 => format3b(0b10, 0b00_0011, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn xnor(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b10, 0b00_0111, rs1, rs2, rd), + i13 => format3b(0b10, 0b00_0111, rs1, rs2, rd), + else => unreachable, + }; + } + pub fn movcc(comptime s2: type, cond: Condition, ccr: CCR, rs2: s2, rd: Register) Instruction { return switch (s2) { Register => format4c(0b10_1100, cond, ccr, rs2, rd), From 8b70abfcc66e375d71acf1e753d3589e38ba1a3d Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 1 Jun 2022 19:32:14 +0700 Subject: [PATCH 1748/2031] stage2: sparc64: Fix & optimize 64-bit truncRegister --- src/arch/sparc64/CodeGen.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 8279c8e123..a195da2318 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -3569,11 +3569,13 @@ fn truncRegister( }); }, 64 => { + if (dest_reg == operand_reg) + return; // Copy register to itself; nothing to do. _ = try self.addInst(.{ .tag = .mov, .data = .{ .arithmetic_2op = .{ - .is_imm = true, + .is_imm = false, .rs1 = dest_reg, .rs2_or_imm = .{ .rs2 = operand_reg }, }, From 5d61f32887c553415215ceadf44cba1bebc7b9da Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 1 Jun 2022 19:54:22 +0700 Subject: [PATCH 1749/2031] stage2: sparc64: Implement airSlice --- src/arch/sparc64/CodeGen.zig | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index a195da2318..3accb2b648 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -517,7 +517,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .shl_sat => @panic("TODO try self.airShlSat(inst)"), .min => @panic("TODO try self.airMin(inst)"), .max => @panic("TODO try self.airMax(inst)"), - .slice => @panic("TODO try self.airSlice(inst)"), + .slice => try self.airSlice(inst), .sqrt, .sin, @@ -1647,6 +1647,26 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); } +fn airSlice(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const ptr = try self.resolveInst(bin_op.lhs); + const ptr_ty = self.air.typeOf(bin_op.lhs); + const len = try self.resolveInst(bin_op.rhs); + const len_ty = self.air.typeOf(bin_op.rhs); + + const ptr_bits = self.target.cpu.arch.ptrBitWidth(); + const ptr_bytes = @divExact(ptr_bits, 8); + + const stack_offset = try self.allocMem(inst, ptr_bytes * 2, ptr_bytes * 2); + try self.genSetStack(ptr_ty, stack_offset, ptr); + try self.genSetStack(len_ty, stack_offset - ptr_bytes, len); + break :result MCValue{ .stack_offset = stack_offset }; + }; + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +} + fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { const is_volatile = false; // TODO const bin_op = self.air.instructions.items(.data)[inst].bin_op; From c00d493a0001cbc9ebc0c02197ae70d921242b22 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 2 Jun 2022 23:28:11 +0700 Subject: [PATCH 1750/2031] stage2: sparc64: Add some notes about stack space allocation --- src/arch/sparc64/abi.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/arch/sparc64/abi.zig b/src/arch/sparc64/abi.zig index 6cdd183b36..a26ab9a20b 100644 --- a/src/arch/sparc64/abi.zig +++ b/src/arch/sparc64/abi.zig @@ -13,6 +13,11 @@ pub const stack_bias = 2047; // The first 128 bytes of the stack is reserved for register saving purposes. // The ABI also requires to reserve space in the stack for the first six // outgoing arguments, even though they are usually passed in registers. +// TODO Don't allocate the argument space in leaf functions +// TODO Save an RO copy of outgoing arguments in reserved area when building in Debug +// TODO Should we also save it in ReleaseSafe? Solaris and OpenBSD binaries seem to ship +// with argument copying enabled and it doesn't seem to give them big slowdowns so +// I guess it would be okay to do in ReleaseSafe? pub const stack_reserved_area = 128 + 48; // There are no callee-preserved registers since the windowing From 31f24dbc5544fbebbd6259ee272aa9a7244b4d87 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 2 Jun 2022 23:34:21 +0700 Subject: [PATCH 1751/2031] stage2: sparc64: Implement airWrapErrUnionErr --- src/arch/sparc64/CodeGen.zig | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 3accb2b648..0cab96d609 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -679,7 +679,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .wrap_optional => @panic("TODO try self.airWrapOptional(inst)"), .wrap_errunion_payload => @panic("TODO try self.airWrapErrUnionPayload(inst)"), - .wrap_errunion_err => @panic("TODO try self.airWrapErrUnionErr(inst)"), + .wrap_errunion_err => try self.airWrapErrUnionErr(inst), .wasm_memory_size => unreachable, .wasm_memory_grow => unreachable, @@ -1851,6 +1851,20 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +/// E to E!T +fn airWrapErrUnionErr(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 result: { + const error_union_ty = self.air.getRefType(ty_op.ty); + const payload_ty = error_union_ty.errorUnionPayload(); + const mcv = try self.resolveInst(ty_op.operand); + if (!payload_ty.hasRuntimeBits()) break :result mcv; + + return self.fail("TODO implement wrap errunion error for non-empty payloads", .{}); + }; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + // Common helper functions /// Adds a Type to the .debug_info at the current position. The bytes will be populated later, From f87dd285bbbea90ee186550e5ca64b743b05451d Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 2 Jun 2022 23:45:52 +0700 Subject: [PATCH 1752/2031] stage2: sparc64: binOp/mul: Use template from `add` --- src/arch/sparc64/CodeGen.zig | 39 ++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 0cab96d609..2b6945810c 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -2036,21 +2036,34 @@ fn binOp( assert(lhs_ty.eql(rhs_ty, mod)); const int_info = lhs_ty.intInfo(self.target.*); if (int_info.bits <= 64) { - // If LHS is immediate, then swap it with RHS. - const lhs_is_imm = lhs == .immediate; - const new_lhs = if (lhs_is_imm) rhs else lhs; - const new_rhs = if (lhs_is_imm) lhs else rhs; - const new_lhs_ty = if (lhs_is_imm) rhs_ty else lhs_ty; - const new_rhs_ty = if (lhs_is_imm) lhs_ty else rhs_ty; + // Only say yes if the operation is + // commutative, i.e. we can swap both of the + // operands + const lhs_immediate_ok = switch (tag) { + .mul => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), + else => unreachable, + }; + const rhs_immediate_ok = switch (tag) { + .mul => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), + else => unreachable, + }; - // At this point, RHS might be an immediate - // If it's a power of two immediate then we emit an shl instead - // TODO add similar checks for LHS - if (new_rhs == .immediate and math.isPowerOfTwo(new_rhs.immediate)) { - return try self.binOp(.shl, new_lhs, .{ .immediate = math.log2(new_rhs.immediate) }, new_lhs_ty, Type.usize, metadata); + const mir_tag: Mir.Inst.Tag = switch (tag) { + .mul => .mulx, + else => unreachable, + }; + + if (rhs_immediate_ok) { + // At this point, rhs is an immediate + return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); + } else if (lhs_immediate_ok) { + // swap lhs and rhs + // At this point, lhs is an immediate + return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata); + } else { + // TODO convert large immediates to register before adding + return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } - - return try self.binOpRegister(.mulx, new_lhs, new_rhs, new_lhs_ty, new_rhs_ty, metadata); } else { return self.fail("TODO binary operations on int with bits > 64", .{}); } From 1bdc2b777bc247c52942189666bc484d8e740b16 Mon Sep 17 00:00:00 2001 From: BratishkaErik Date: Sun, 29 May 2022 14:37:16 +0600 Subject: [PATCH 1753/2031] tools: fix update-linux-headers.zig and process_headers.zig Signed-off-by: BratishkaErik --- test/standalone.zig | 4 ++++ tools/process_headers.zig | 3 ++- tools/update-linux-headers.zig | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/test/standalone.zig b/test/standalone.zig index 856a58df84..c34f9467d6 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -83,6 +83,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void { // Ensure the development tools are buildable. cases.add("tools/gen_spirv_spec.zig"); cases.add("tools/gen_stubs.zig"); + cases.add("tools/generate_linux_syscalls.zig"); + cases.add("tools/process_headers.zig"); + cases.add("tools/update-license-headers.zig"); + cases.add("tools/update-linux-headers.zig"); cases.add("tools/update_clang_options.zig"); cases.add("tools/update_cpu_features.zig"); cases.add("tools/update_glibc.zig"); diff --git a/tools/process_headers.zig b/tools/process_headers.zig index 39fbe08a4a..84126635dc 100644 --- a/tools/process_headers.zig +++ b/tools/process_headers.zig @@ -267,8 +267,9 @@ const DestTarget = struct { (@enumToInt(a.abi) *% @as(u32, 4082223418)); } - pub fn eql(self: @This(), a: DestTarget, b: DestTarget) bool { + pub fn eql(self: @This(), a: DestTarget, b: DestTarget, b_index: usize) bool { _ = self; + _ = b_index; return a.arch.eql(b.arch) and a.os == b.os and a.abi == b.abi; diff --git a/tools/update-linux-headers.zig b/tools/update-linux-headers.zig index 48fdfbbb34..cfa16fcd38 100644 --- a/tools/update-linux-headers.zig +++ b/tools/update-linux-headers.zig @@ -106,8 +106,9 @@ const DestTarget = struct { return @truncate(u32, hasher.final()); } - pub fn eql(self: @This(), a: DestTarget, b: DestTarget) bool { + pub fn eql(self: @This(), a: DestTarget, b: DestTarget, b_index: usize) bool { _ = self; + _ = b_index; return a.arch.eql(b.arch); } }; From f6eb83c91cce2419a01f217b9d4c989e08e6e729 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 6 Jun 2022 06:09:01 +0700 Subject: [PATCH 1754/2031] stage2: sparc64: Implement airArrayToSlice --- src/arch/sparc64/CodeGen.zig | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 2b6945810c..c02d279631 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -593,7 +593,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .store => try self.airStore(inst), .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"), .struct_field_val=> try self.airStructFieldVal(inst), - .array_to_slice => @panic("TODO try self.airArrayToSlice(inst)"), + .array_to_slice => try self.airArrayToSlice(inst), .int_to_float => @panic("TODO try self.airIntToFloat(inst)"), .float_to_int => @panic("TODO try self.airFloatToInt(inst)"), .cmpxchg_strong => @panic("TODO try self.airCmpxchg(inst)"), @@ -783,6 +783,25 @@ fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .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 result: { + const ptr_ty = self.air.typeOf(ty_op.operand); + const ptr = try self.resolveInst(ty_op.operand); + const array_ty = ptr_ty.childType(); + const array_len = @intCast(u32, array_ty.arrayLen()); + + const ptr_bits = self.target.cpu.arch.ptrBitWidth(); + const ptr_bytes = @divExact(ptr_bits, 8); + + const stack_offset = try self.allocMem(inst, ptr_bytes * 2, ptr_bytes * 2); + try self.genSetStack(ptr_ty, stack_offset, ptr); + try self.genSetStack(Type.initTag(.usize), stack_offset - ptr_bytes, .{ .immediate = array_len }); + break :result MCValue{ .stack_offset = stack_offset }; + }; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const extra = self.air.extraData(Air.Asm, ty_pl.payload); From ec7f2a105fa2b9c0d7a89e17f467c27008098c50 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 6 Jun 2022 06:09:16 +0700 Subject: [PATCH 1755/2031] stage2: sparc64: Implement airPtrElemPtr --- src/arch/sparc64/CodeGen.zig | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index c02d279631..c0d76d5b9b 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -660,7 +660,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .slice_elem_val => try self.airSliceElemVal(inst), .slice_elem_ptr => @panic("TODO try self.airSliceElemPtr(inst)"), .ptr_elem_val => @panic("TODO try self.airPtrElemVal(inst)"), - .ptr_elem_ptr => @panic("TODO try self.airPtrElemPtr(inst)"), + .ptr_elem_ptr => try self.airPtrElemPtr(inst), .constant => unreachable, // excluded from function bodies .const_ty => unreachable, // excluded from function bodies @@ -1646,6 +1646,13 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airPtrElemPtr(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 ptr_elem_ptr for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); +} + fn airRet(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); From 97d35a5147bb88c5b9ccf26c62bf303162fbf613 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 6 Jun 2022 06:30:08 +0700 Subject: [PATCH 1756/2031] behaviortest: Skip 'align(N) on functions' on sparc64 for now --- test/behavior/align.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/test/behavior/align.zig b/test/behavior/align.zig index ad35db2171..056354f237 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -505,6 +505,7 @@ test "align(N) on functions" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO // function alignment is a compile error on wasm32/wasm64 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest; From 0cab01adbf8ba495f214f948d34a3e6bd133bbc4 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 6 Jun 2022 09:43:05 +0200 Subject: [PATCH 1757/2031] elf: refactor and enhance logging symtab --- src/link/Elf.zig | 87 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 26 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 442ad73ea7..f9b6d7861a 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -497,7 +497,7 @@ fn makeString(self: *Elf, bytes: []const u8) !u32 { return @intCast(u32, result); } -fn getString(self: *Elf, str_off: u32) []const u8 { +fn getString(self: Elf, str_off: u32) []const u8 { assert(str_off < self.shstrtab.items.len); return mem.sliceTo(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off), 0); } @@ -1015,6 +1015,10 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // mixing local and global symbols within a symbol table. try self.writeAllGlobalSymbols(); + if (build_options.enable_logging) { + self.logSymtab(); + } + if (self.dwarf) |*dw| { if (self.debug_abbrev_section_dirty) { try dw.writeDbgAbbrev(&self.base); @@ -1167,7 +1171,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node for (buf) |*shdr, i| { shdr.* = sectHeaderTo32(self.sections.items[i]); - log.debug("writing section {}", .{shdr.*}); + log.debug("writing section {s}: {}", .{ self.getString(shdr.sh_name), shdr.* }); if (foreign_endian) { mem.byteSwapAllFields(elf.Elf32_Shdr, shdr); } @@ -1180,7 +1184,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node for (buf) |*shdr, i| { shdr.* = self.sections.items[i]; - log.debug("writing section {}", .{shdr.*}); + log.debug("writing section {s}: {}", .{ self.getString(shdr.sh_name), shdr.* }); if (foreign_endian) { mem.byteSwapAllFields(elf.Elf64_Shdr, shdr); } @@ -2794,8 +2798,14 @@ fn writeSymbol(self: *Elf, index: usize) !void { if (needed_size > self.allocatedSize(syms_sect.sh_offset)) { // Move all the symbols to a new file location. const new_offset = self.findFreeSpace(needed_size, sym_align); + log.debug("moving '.symtab' from 0x{x} to 0x{x}", .{ syms_sect.sh_offset, new_offset }); const existing_size = @as(u64, syms_sect.sh_info) * sym_size; - const amt = try self.base.file.?.copyRangeAll(syms_sect.sh_offset, self.base.file.?, new_offset, existing_size); + const amt = try self.base.file.?.copyRangeAll( + syms_sect.sh_offset, + self.base.file.?, + new_offset, + existing_size, + ); if (amt != existing_size) return error.InputOutput; syms_sect.sh_offset = new_offset; } @@ -2804,30 +2814,35 @@ fn writeSymbol(self: *Elf, index: usize) !void { self.shdr_table_dirty = true; // TODO look into only writing one section } const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); + const off = switch (self.ptr_width) { + .p32 => syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index, + .p64 => syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index, + }; + const local = self.local_symbols.items[index]; + log.debug("writing symbol {d}, '{s}' at 0x{x}", .{ index, self.getString(local.st_name), off }); + log.debug(" ({})", .{local}); switch (self.ptr_width) { .p32 => { var sym = [1]elf.Elf32_Sym{ .{ - .st_name = self.local_symbols.items[index].st_name, - .st_value = @intCast(u32, self.local_symbols.items[index].st_value), - .st_size = @intCast(u32, self.local_symbols.items[index].st_size), - .st_info = self.local_symbols.items[index].st_info, - .st_other = self.local_symbols.items[index].st_other, - .st_shndx = self.local_symbols.items[index].st_shndx, + .st_name = local.st_name, + .st_value = @intCast(u32, local.st_value), + .st_size = @intCast(u32, local.st_size), + .st_info = local.st_info, + .st_other = local.st_other, + .st_shndx = local.st_shndx, }, }; if (foreign_endian) { mem.byteSwapAllFields(elf.Elf32_Sym, &sym[0]); } - const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index; try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); }, .p64 => { - var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]}; + var sym = [1]elf.Elf64_Sym{local}; if (foreign_endian) { mem.byteSwapAllFields(elf.Elf64_Sym, &sym[0]); } - const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index; try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); }, } @@ -2847,8 +2862,14 @@ fn writeAllGlobalSymbols(self: *Elf) !void { if (needed_size > self.allocatedSize(syms_sect.sh_offset)) { // Move all the symbols to a new file location. const new_offset = self.findFreeSpace(needed_size, sym_align); + log.debug("moving '.symtab' from 0x{x} to 0x{x}", .{ syms_sect.sh_offset, new_offset }); const existing_size = @as(u64, syms_sect.sh_info) * sym_size; - const amt = try self.base.file.?.copyRangeAll(syms_sect.sh_offset, self.base.file.?, new_offset, existing_size); + const amt = try self.base.file.?.copyRangeAll( + syms_sect.sh_offset, + self.base.file.?, + new_offset, + existing_size, + ); if (amt != existing_size) return error.InputOutput; syms_sect.sh_offset = new_offset; } @@ -2857,19 +2878,21 @@ fn writeAllGlobalSymbols(self: *Elf) !void { const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size; + log.debug("writing {d} global symbols at 0x{x}", .{ self.global_symbols.items.len, global_syms_off }); switch (self.ptr_width) { .p32 => { const buf = try self.base.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len); defer self.base.allocator.free(buf); for (buf) |*sym, i| { + const global = self.global_symbols.items[i]; sym.* = .{ - .st_name = self.global_symbols.items[i].st_name, - .st_value = @intCast(u32, self.global_symbols.items[i].st_value), - .st_size = @intCast(u32, self.global_symbols.items[i].st_size), - .st_info = self.global_symbols.items[i].st_info, - .st_other = self.global_symbols.items[i].st_other, - .st_shndx = self.global_symbols.items[i].st_shndx, + .st_name = global.st_name, + .st_value = @intCast(u32, global.st_value), + .st_size = @intCast(u32, global.st_size), + .st_info = global.st_info, + .st_other = global.st_other, + .st_shndx = global.st_shndx, }; if (foreign_endian) { mem.byteSwapAllFields(elf.Elf32_Sym, sym); @@ -2882,13 +2905,14 @@ fn writeAllGlobalSymbols(self: *Elf) !void { defer self.base.allocator.free(buf); for (buf) |*sym, i| { + const global = self.global_symbols.items[i]; sym.* = .{ - .st_name = self.global_symbols.items[i].st_name, - .st_value = self.global_symbols.items[i].st_value, - .st_size = self.global_symbols.items[i].st_size, - .st_info = self.global_symbols.items[i].st_info, - .st_other = self.global_symbols.items[i].st_other, - .st_shndx = self.global_symbols.items[i].st_shndx, + .st_name = global.st_name, + .st_value = global.st_value, + .st_size = global.st_size, + .st_info = global.st_info, + .st_other = global.st_other, + .st_shndx = global.st_shndx, }; if (foreign_endian) { mem.byteSwapAllFields(elf.Elf64_Sym, sym); @@ -3194,3 +3218,14 @@ const CsuObjects = struct { self.crtn = crtn; } }; + +fn logSymtab(self: Elf) void { + log.debug("locals:", .{}); + for (self.local_symbols.items) |sym, id| { + log.debug(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.st_name), sym.st_value, sym.st_shndx }); + } + log.debug("globals:", .{}); + for (self.global_symbols.items) |sym, id| { + log.debug(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.st_name), sym.st_value, sym.st_shndx }); + } +} From e05de31a5fa8105b09eed37c9d023501b1289cd1 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 6 Jun 2022 09:44:07 +0200 Subject: [PATCH 1758/2031] dwarf: fix incorrect type reloc for unions Split type relocs into two kinds: local and global. Global relocs use a global type resolver and calculate offset to the existing definition of a type abbreviation. Local relocs use offset in the abbrev section of the containing atom plus addend to generate a local relocation. --- src/arch/arm/CodeGen.zig | 2 +- src/arch/riscv64/CodeGen.zig | 2 +- src/arch/sparc64/CodeGen.zig | 2 +- src/arch/wasm/CodeGen.zig | 2 +- src/arch/x86_64/CodeGen.zig | 2 +- src/link/Dwarf.zig | 115 +++++++++++++++++++---------------- 6 files changed, 69 insertions(+), 56 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index a0f9c12c34..d7443faaf9 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -3187,7 +3187,7 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) error{OutOfMemory}!void { .macho => unreachable, else => unreachable, }; - try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); + try dw.addTypeRelocGlobal(atom, ty, @intCast(u32, index)); }, .plan9 => {}, .none => {}, diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index c94b4de378..b1e86240ae 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -754,7 +754,7 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { .macho => unreachable, else => unreachable, }; - try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); + try dw.addTypeRelocGlobal(atom, ty, @intCast(u32, index)); }, .plan9 => {}, .none => {}, diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index eb9d9a4ad9..ca9c2f125f 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1550,7 +1550,7 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { .elf => &mod.declPtr(self.mod_fn.owner_decl).link.elf.dbg_info_atom, else => unreachable, }; - try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); + try dw.addTypeRelocGlobal(atom, ty, @intCast(u32, index)); }, else => {}, } diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 944ef16294..00907a03c0 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1050,7 +1050,7 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { const index = dbg_info.items.len; try dbg_info.resize(index + 4); const atom = &self.decl.link.wasm.dbg_info_atom; - try dwarf.addTypeReloc(atom, ty, @intCast(u32, index), null); + try dwarf.addTypeRelocGlobal(atom, ty, @intCast(u32, index)); }, .plan9 => unreachable, .none => {}, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 33734eda30..4ac9103c7c 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -4371,7 +4371,7 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { .macho => &fn_owner_decl.link.macho.dbg_info_atom, else => unreachable, }; - try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); + try dw.addTypeRelocGlobal(atom, ty, @intCast(u32, index)); }, .plan9 => {}, .none => {}, diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 61bec1f880..f291dd4255 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -110,13 +110,22 @@ pub const DeclState = struct { }); } - pub fn addTypeReloc( - self: *DeclState, - atom: *const Atom, - ty: Type, - offset: u32, - addend: ?u32, - ) !void { + /// Adds local type relocation of the form: @offset => @this + addend + /// @this signifies the offset within the .debug_abbrev section of the containing atom. + pub fn addTypeRelocLocal(self: *DeclState, atom: *const Atom, offset: u32, addend: u32) !void { + log.debug("{x}: @this + {x}", .{ offset, addend }); + try self.abbrev_relocs.append(self.gpa, .{ + .target = null, + .atom = atom, + .offset = offset, + .addend = addend, + }); + } + + /// Adds global type relocation of the form: @offset => @symbol + 0 + /// @symbol signifies a type abbreviation posititioned somewhere in the .debug_abbrev section + /// which we use as our target of the relocation. + pub fn addTypeRelocGlobal(self: *DeclState, atom: *const Atom, ty: Type, offset: u32) !void { const resolv = self.abbrev_resolver.getContext(ty, .{ .mod = self.mod, }) orelse blk: { @@ -134,14 +143,12 @@ pub const DeclState = struct { .mod = self.mod, }).?; }; - const add: u32 = addend orelse 0; - - log.debug("{x}: @{d} + {x}", .{ offset, resolv, add }); + log.debug("{x}: @{d} + 0", .{ offset, resolv }); try self.abbrev_relocs.append(self.gpa, .{ .target = resolv, .atom = atom, .offset = offset, - .addend = add, + .addend = 0, }); } @@ -213,7 +220,7 @@ pub const DeclState = struct { // DW.AT.type, DW.FORM.ref4 var index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, Type.bool, @intCast(u32, index), null); + try self.addTypeRelocGlobal(atom, Type.bool, @intCast(u32, index)); // DW.AT.data_member_location, DW.FORM.sdata try dbg_info_buffer.ensureUnusedCapacity(6); dbg_info_buffer.appendAssumeCapacity(0); @@ -225,7 +232,7 @@ pub const DeclState = struct { // DW.AT.type, DW.FORM.ref4 index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, payload_ty, @intCast(u32, index), null); + try self.addTypeRelocGlobal(atom, payload_ty, @intCast(u32, index)); // DW.AT.data_member_location, DW.FORM.sdata const offset = abi_size - payload_ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), offset); @@ -254,7 +261,7 @@ pub const DeclState = struct { try dbg_info_buffer.resize(index + 4); var buf = try arena.create(Type.SlicePtrFieldTypeBuffer); const ptr_ty = ty.slicePtrFieldType(buf); - try self.addTypeReloc(atom, ptr_ty, @intCast(u32, index), null); + try self.addTypeRelocGlobal(atom, ptr_ty, @intCast(u32, index)); // DW.AT.data_member_location, DW.FORM.sdata try dbg_info_buffer.ensureUnusedCapacity(6); dbg_info_buffer.appendAssumeCapacity(0); @@ -266,7 +273,7 @@ pub const DeclState = struct { // DW.AT.type, DW.FORM.ref4 index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, Type.usize, @intCast(u32, index), null); + try self.addTypeRelocGlobal(atom, Type.usize, @intCast(u32, index)); // DW.AT.data_member_location, DW.FORM.sdata try dbg_info_buffer.ensureUnusedCapacity(2); dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize)); @@ -278,7 +285,7 @@ pub const DeclState = struct { // DW.AT.type, DW.FORM.ref4 const index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, ty.childType(), @intCast(u32, index), null); + try self.addTypeRelocGlobal(atom, ty.childType(), @intCast(u32, index)); } }, .Array => { @@ -289,13 +296,13 @@ pub const DeclState = struct { // DW.AT.type, DW.FORM.ref4 var index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, ty.childType(), @intCast(u32, index), null); + try self.addTypeRelocGlobal(atom, ty.childType(), @intCast(u32, index)); // DW.AT.subrange_type try dbg_info_buffer.append(@enumToInt(AbbrevKind.array_dim)); // DW.AT.type, DW.FORM.ref4 index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, Type.usize, @intCast(u32, index), null); + try self.addTypeRelocGlobal(atom, Type.usize, @intCast(u32, index)); // DW.AT.count, DW.FORM.udata const len = ty.arrayLenIncludingSentinel(); try leb128.writeULEB128(dbg_info_buffer.writer(), len); @@ -323,7 +330,7 @@ pub const DeclState = struct { // DW.AT.type, DW.FORM.ref4 var index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, field, @intCast(u32, index), null); + try self.addTypeRelocGlobal(atom, field, @intCast(u32, index)); // DW.AT.data_member_location, DW.FORM.sdata const field_off = ty.structFieldOffset(field_index, target); try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); @@ -354,7 +361,7 @@ pub const DeclState = struct { // DW.AT.type, DW.FORM.ref4 var index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, field.ty, @intCast(u32, index), null); + try self.addTypeRelocGlobal(atom, field.ty, @intCast(u32, index)); // DW.AT.data_member_location, DW.FORM.sdata const field_off = ty.structFieldOffset(field_index, target); try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); @@ -434,7 +441,7 @@ pub const DeclState = struct { // DW.AT.type, DW.FORM.ref4 const inner_union_index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(inner_union_index + 4); - try self.addTypeReloc(atom, ty, @intCast(u32, inner_union_index), 5); + try self.addTypeRelocLocal(atom, @intCast(u32, inner_union_index), 5); // DW.AT.data_member_location, DW.FORM.sdata try leb128.writeULEB128(dbg_info_buffer.writer(), payload_offset); } @@ -461,7 +468,7 @@ pub const DeclState = struct { // DW.AT.type, DW.FORM.ref4 const index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, field.ty, @intCast(u32, index), null); + try self.addTypeRelocGlobal(atom, field.ty, @intCast(u32, index)); // DW.AT.data_member_location, DW.FORM.sdata try dbg_info_buffer.append(0); } @@ -478,7 +485,7 @@ pub const DeclState = struct { // DW.AT.type, DW.FORM.ref4 const index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, union_obj.tag_ty, @intCast(u32, index), null); + try self.addTypeRelocGlobal(atom, union_obj.tag_ty, @intCast(u32, index)); // DW.AT.data_member_location, DW.FORM.sdata try leb128.writeULEB128(dbg_info_buffer.writer(), tag_offset); @@ -521,7 +528,7 @@ pub const DeclState = struct { // DW.AT.type, DW.FORM.ref4 var index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, payload_ty, @intCast(u32, index), null); + try self.addTypeRelocGlobal(atom, payload_ty, @intCast(u32, index)); // DW.AT.data_member_location, DW.FORM.sdata try leb128.writeULEB128(dbg_info_buffer.writer(), payload_off); @@ -534,7 +541,7 @@ pub const DeclState = struct { // DW.AT.type, DW.FORM.ref4 index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); - try self.addTypeReloc(atom, error_ty, @intCast(u32, index), null); + try self.addTypeRelocGlobal(atom, error_ty, @intCast(u32, index)); // DW.AT.data_member_location, DW.FORM.sdata try leb128.writeULEB128(dbg_info_buffer.writer(), error_off); @@ -556,7 +563,9 @@ pub const AbbrevEntry = struct { }; pub const AbbrevRelocation = struct { - target: u32, + /// If target is null, we deal with a local relocation that is based on simple offset + addend + /// only. + target: ?u32, atom: *const Atom, offset: u32, addend: u32, @@ -740,12 +749,7 @@ pub fn initDeclState(self: *Dwarf, mod: *Module, decl: *Module.Decl) !DeclState .wasm => &decl.link.wasm.dbg_info_atom, else => unreachable, }; - try decl_state.addTypeReloc( - atom, - fn_ret_type, - @intCast(u32, dbg_info_buffer.items.len), - null, - ); + try decl_state.addTypeRelocGlobal(atom, fn_ret_type, @intCast(u32, dbg_info_buffer.items.len)); dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4 } @@ -1036,30 +1040,39 @@ pub fn commitDeclState( try self.updateDeclDebugInfoAllocation(file, atom, @intCast(u32, dbg_info_buffer.items.len)); while (decl_state.abbrev_relocs.popOrNull()) |reloc| { - const symbol = decl_state.abbrev_table.items[reloc.target]; - const ty = symbol.@"type"; - const deferred: bool = blk: { - if (ty.isAnyError()) break :blk true; - switch (ty.tag()) { - .error_set_inferred => { - if (!ty.castTag(.error_set_inferred).?.data.is_resolved) break :blk true; - }, - else => {}, + if (reloc.target) |target| { + const symbol = decl_state.abbrev_table.items[target]; + const ty = symbol.@"type"; + const deferred: bool = blk: { + if (ty.isAnyError()) break :blk true; + switch (ty.tag()) { + .error_set_inferred => { + if (!ty.castTag(.error_set_inferred).?.data.is_resolved) break :blk true; + }, + else => {}, + } + break :blk false; + }; + if (deferred) { + try self.global_abbrev_relocs.append(gpa, .{ + .target = null, + .offset = reloc.offset, + .atom = reloc.atom, + .addend = reloc.addend, + }); + } else { + mem.writeInt( + u32, + dbg_info_buffer.items[reloc.offset..][0..@sizeOf(u32)], + symbol.atom.off + symbol.offset + reloc.addend, + target_endian, + ); } - break :blk false; - }; - if (deferred) { - try self.global_abbrev_relocs.append(gpa, .{ - .target = undefined, - .offset = reloc.offset, - .atom = reloc.atom, - .addend = reloc.addend, - }); } else { mem.writeInt( u32, dbg_info_buffer.items[reloc.offset..][0..@sizeOf(u32)], - symbol.atom.off + symbol.offset + reloc.addend, + reloc.atom.off + reloc.offset + reloc.addend, target_endian, ); } From 41bf81dc3231eb763c93eb95b152e7ab8d3c5af8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 6 Jun 2022 11:31:54 -0700 Subject: [PATCH 1759/2031] Revert "Treat blocks with "return" as "noreturn"" This reverts commit 135b91aecd9be1f6f5806b667e07e383dd481198. "endsWithBreak()" is not a meaningful question to ask and should not be used this way. A simple example that defeats this logic is: ```zig export fn entry() void { outer: { { break :outer; } return; } } ``` --- src/AstGen.zig | 10 ---------- src/Zir.zig | 13 ------------- .../code_after_return_in_block_is_unreachable.zig | 14 -------------- 3 files changed, 37 deletions(-) delete mode 100644 test/cases/compile_errors/stage2/code_after_return_in_block_is_unreachable.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index 21e5170030..ab5befa4ba 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1967,9 +1967,6 @@ fn blockExpr( } try blockExprStmts(gz, scope, statements); - if (gz.endsWithNoReturn() and !gz.endsWithBreak()) { - return Zir.Inst.Ref.unreachable_value; - } return rvalue(gz, rl, .void_value, block_node); } @@ -9933,13 +9930,6 @@ const GenZir = struct { return tags[last_inst].isNoReturn(); } - fn endsWithBreak(gz: GenZir) bool { - if (gz.isEmpty()) return false; - const tags = gz.astgen.instructions.items(.tag); - const last_inst = gz.instructions.items[gz.instructions.items.len - 1]; - return tags[last_inst].isBreak(); - } - /// TODO all uses of this should be replaced with uses of `endsWithNoReturn`. fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool { if (inst_ref == .unreachable_value) return true; diff --git a/src/Zir.zig b/src/Zir.zig index bad7b91488..f09f2015e0 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -1250,19 +1250,6 @@ pub const Inst = struct { }; } - /// Returns whether the instruction is a "break". This differs from - /// isNoReturn because a "break" in a block statement is not a - /// "noreturn" for the outer scope, whereas the other "noreturn" - /// instructions are. - pub fn isBreak(tag: Tag) bool { - return switch (tag) { - .@"break", - .break_inline, - => true, - else => false, - }; - } - /// AstGen uses this to find out if `Ref.void_value` should be used in place /// of the result of a given instruction. This allows Sema to forego adding /// the instruction to the map after analysis. diff --git a/test/cases/compile_errors/stage2/code_after_return_in_block_is_unreachable.zig b/test/cases/compile_errors/stage2/code_after_return_in_block_is_unreachable.zig deleted file mode 100644 index e62edcb358..0000000000 --- a/test/cases/compile_errors/stage2/code_after_return_in_block_is_unreachable.zig +++ /dev/null @@ -1,14 +0,0 @@ -export fn entry() void { - { - return; - } - - return; -} - -// error -// target=native -// -// :6:5: error: unreachable code -// :2:5: note: control flow is diverted here - From 8ca6dc33d1523061f198474c84a68330085d6667 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Mon, 6 Jun 2022 21:27:36 +0200 Subject: [PATCH 1760/2031] stage2 AArch64: implement `try` AIR instruction --- src/arch/aarch64/CodeGen.zig | 134 +++++++++++++++++++++++++++-------- 1 file changed, 104 insertions(+), 30 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 4574469920..2875bda7e9 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -665,8 +665,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .prefetch => try self.airPrefetch(inst), .mul_add => try self.airMulAdd(inst), - .@"try" => @panic("TODO"), - .try_ptr => @panic("TODO"), + .@"try" => try self.airTry(inst), + .try_ptr => try self.airTryPtr(inst), .dbg_var_ptr, .dbg_var_val, @@ -2308,27 +2308,70 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +/// Given an error union, returns the error +fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { + const err_ty = error_union_ty.errorUnionSet(); + const payload_ty = error_union_ty.errorUnionPayload(); + if (err_ty.errorSetCardinality() == .zero) { + return MCValue{ .immediate = 0 }; + } + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + return error_union_mcv; + } + + const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*)); + switch (error_union_mcv) { + .register => return self.fail("TODO errUnionErr for registers", .{}), + .stack_offset => |off| { + return MCValue{ .stack_offset = off - err_offset }; + }, + .memory => |addr| { + return MCValue{ .memory = addr + err_offset }; + }, + else => unreachable, // invalid MCValue for an error union + } +} + fn airUnwrapErrErr(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 result: { const error_union_ty = self.air.typeOf(ty_op.operand); - const payload_ty = error_union_ty.errorUnionPayload(); const mcv = try self.resolveInst(ty_op.operand); - if (!payload_ty.hasRuntimeBits()) break :result mcv; - - return self.fail("TODO implement unwrap error union error for non-empty payloads", .{}); + break :result try self.errUnionErr(mcv, error_union_ty); }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +/// Given an error union, returns the payload +fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { + const err_ty = error_union_ty.errorUnionSet(); + const payload_ty = error_union_ty.errorUnionPayload(); + if (err_ty.errorSetCardinality() == .zero) { + return error_union_mcv; + } + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + return MCValue.none; + } + + const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*)); + switch (error_union_mcv) { + .register => return self.fail("TODO errUnionPayload for registers", .{}), + .stack_offset => |off| { + return MCValue{ .stack_offset = off - payload_offset }; + }, + .memory => |addr| { + return MCValue{ .memory = addr + payload_offset }; + }, + else => unreachable, // invalid MCValue for an error union + } +} + fn airUnwrapErrPayload(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 result: { const error_union_ty = self.air.typeOf(ty_op.operand); - const payload_ty = error_union_ty.errorUnionPayload(); - if (!payload_ty.hasRuntimeBits()) break :result MCValue.none; - - return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{}); + const error_union = try self.resolveInst(ty_op.operand); + break :result try self.errUnionPayload(error_union, error_union_ty); }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -3389,45 +3432,38 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .dead, .{ operand, .none, .none }); } -fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { - const pl_op = self.air.instructions.items(.data)[inst].pl_op; - const cond = try self.resolveInst(pl_op.operand); - const extra = self.air.extraData(Air.CondBr, pl_op.payload); - const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len]; - const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; - const liveness_condbr = self.liveness.getCondBr(inst); - - const reloc: Mir.Inst.Index = switch (cond) { +fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index { + switch (condition) { .compare_flags_signed, .compare_flags_unsigned, - => try self.addInst(.{ + => return try self.addInst(.{ .tag = .b_cond, .data = .{ .inst_cond = .{ .inst = undefined, // populated later through performReloc - .cond = switch (cond) { + .cond = switch (condition) { .compare_flags_signed => |cmp_op| blk: { // Here we map to the opposite condition because the jump is to the false branch. - const condition = Instruction.Condition.fromCompareOperatorSigned(cmp_op); - break :blk condition.negate(); + const condition_code = Instruction.Condition.fromCompareOperatorSigned(cmp_op); + break :blk condition_code.negate(); }, .compare_flags_unsigned => |cmp_op| blk: { // Here we map to the opposite condition because the jump is to the false branch. - const condition = Instruction.Condition.fromCompareOperatorUnsigned(cmp_op); - break :blk condition.negate(); + const condition_code = Instruction.Condition.fromCompareOperatorUnsigned(cmp_op); + break :blk condition_code.negate(); }, else => unreachable, }, }, }, }), - else => blk: { - const reg = switch (cond) { + else => { + const reg = switch (condition) { .register => |r| r, - else => try self.copyToTmpRegister(Type.bool, cond), + else => try self.copyToTmpRegister(Type.bool, condition), }; - break :blk try self.addInst(.{ + return try self.addInst(.{ .tag = .cbz, .data = .{ .r_inst = .{ @@ -3437,7 +3473,18 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { }, }); }, - }; + } +} + +fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const cond = try self.resolveInst(pl_op.operand); + const extra = self.air.extraData(Air.CondBr, pl_op.payload); + const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len]; + const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; + const liveness_condbr = self.liveness.getCondBr(inst); + + const reloc = try self.condBr(cond); // If the condition dies here in this condbr instruction, process // that death now instead of later as this has an effect on @@ -4469,6 +4516,33 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); } +fn airTry(self: *Self, inst: Air.Inst.Index) !void { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const extra = self.air.extraData(Air.Try, pl_op.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + const result: MCValue = result: { + const error_union_ty = self.air.typeOf(pl_op.operand); + const error_union = try self.resolveInst(pl_op.operand); + const is_err_result = try self.isErr(error_union_ty, error_union); + const reloc = try self.condBr(is_err_result); + + try self.genBody(body); + + try self.performReloc(reloc); + break :result try self.errUnionPayload(error_union, error_union_ty); + }; + return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); +} + +fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + _ = body; + return self.fail("TODO implement airTryPtr for arm", .{}); + // return self.finishAir(inst, result, .{ extra.data.ptr, .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); From a040ccb42f1b34ba612c975b7030ccdcbb3f8086 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 4 Jun 2022 12:20:28 +0300 Subject: [PATCH 1761/2031] Sema: fix coerce result ptr outside of functions --- src/Sema.zig | 9 ++++++++- test/behavior/basic.zig | 9 +++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 593b299833..5669b4811a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1875,7 +1875,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const dummy_ptr = try trash_block.addTy(.alloc, sema.typeOf(ptr)); const dummy_operand = try trash_block.addBitCast(pointee_ty, .void_value); - try sema.storePtr(&trash_block, src, dummy_ptr, dummy_operand); + try sema.storePtr2(&trash_block, src, dummy_ptr, src, dummy_operand, src, .bitcast); { const air_tags = sema.air_instructions.items(.tag); @@ -20187,6 +20187,13 @@ fn storePtr2( // TODO handle if the element type requires comptime + if (air_tag == .bitcast) { + // `air_tag == .bitcast` is used as a special case for `zirCoerceResultPtr` + // to avoid calling `requireRuntimeBlock` for the dummy block. + _ = try block.addBinOp(.store, ptr, operand); + return; + } + try sema.requireRuntimeBlock(block, runtime_src); try sema.queueFullTypeResolution(elem_ty); _ = try block.addBinOp(air_tag, ptr, operand); diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 3129091091..b45f5a0b49 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -1,5 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); +const assert = std.debug.assert; const mem = std.mem; const expect = std.testing.expect; const expectEqualStrings = std.testing.expectEqualStrings; @@ -1053,3 +1054,11 @@ test "const alloc with comptime known initializer is made comptime known" { if (u.a == 0) @compileError("bad"); } } + +comptime { + // coerce result ptr outside a function + const S = struct { a: comptime_int }; + var s: S = undefined; + s = S{ .a = 1 }; + assert(s.a == 1); +} From cb5d2b691aadde5665cefc54542e3e0651ebc2fa Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 4 Jun 2022 13:12:55 +0300 Subject: [PATCH 1762/2031] Sema: validate equality on store to comptime field --- src/Sema.zig | 47 +++++++++++++++---- src/TypedValue.zig | 10 ++++ src/value.zig | 45 +++++++++++++++++- test/behavior/struct.zig | 22 +++++++++ .../invalid_store_to_comptime_field.zig | 20 ++++++++ 5 files changed, 133 insertions(+), 11 deletions(-) create mode 100644 test/cases/compile_errors/invalid_store_to_comptime_field.zig diff --git a/src/Sema.zig b/src/Sema.zig index 5669b4811a..015b50ce4b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3616,7 +3616,7 @@ fn zirValidateArrayInit( const air_tags = sema.air_instructions.items(.tag); const air_datas = sema.air_instructions.items(.data); - for (instrs) |elem_ptr, i| { + outer: for (instrs) |elem_ptr, i| { const elem_ptr_data = sema.code.instructions.items(.data)[elem_ptr].pl_node; const elem_src: LazySrcLoc = .{ .node_offset = elem_ptr_data.src_node }; @@ -3630,6 +3630,10 @@ fn zirValidateArrayInit( // of the for loop. var block_index = block.instructions.items.len - 1; while (block.instructions.items[block_index] != elem_ptr_air_inst) { + if (block_index == 0) { + array_is_comptime = true; + continue :outer; + } block_index -= 1; } first_block_index = @minimum(first_block_index, block_index); @@ -3672,6 +3676,13 @@ fn zirValidateArrayInit( } if (array_is_comptime) { + if (try sema.resolveDefinedValue(block, init_src, array_ptr)) |ptr_val| { + if (ptr_val.tag() == .comptime_field_ptr) { + // This store was validated by the individual elem ptrs. + return; + } + } + // Our task is to delete all the `elem_ptr` and `store` instructions, and insert // instead a single `store` to the array_ptr with a comptime struct value. // Also to populate the sentinel value, if any. @@ -18462,14 +18473,11 @@ fn structFieldPtrByIndex( const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data); if (field.is_comptime) { - var anon_decl = try block.startAnonDecl(field_src); - defer anon_decl.deinit(); - const decl = try anon_decl.finish( - try field.ty.copy(anon_decl.arena()), - try field.default_val.copy(anon_decl.arena()), - ptr_ty_data.@"align", - ); - return sema.analyzeDeclRef(decl); + const val = try Value.Tag.comptime_field_ptr.create(sema.arena, .{ + .field_ty = try field.ty.copy(sema.arena), + .field_val = try field.default_val.copy(sema.arena), + }); + return sema.addConstant(ptr_field_ty, val); } if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| { @@ -20247,6 +20255,14 @@ fn storePtrVal( const bitcasted_val = try sema.bitCastVal(block, src, operand_val, operand_ty, mut_kit.ty, 0); + if (mut_kit.decl_ref_mut.runtime_index == std.math.maxInt(u32)) { + // Special case for comptime field ptr. + if (!mut_kit.val.eql(bitcasted_val, mut_kit.ty, sema.mod)) { + return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{}); + } + return; + } + const arena = mut_kit.beginArena(sema.mod); defer mut_kit.finishArena(sema.mod); @@ -20296,6 +20312,19 @@ fn beginComptimePtrMutation( .ty = decl.ty, }; }, + .comptime_field_ptr => { + const payload = ptr_val.castTag(.comptime_field_ptr).?.data; + const duped = try sema.arena.create(Value); + duped.* = payload.field_val; + return ComptimePtrMutationKit{ + .decl_ref_mut = .{ + .decl_index = @intToEnum(Module.Decl.Index, 0), + .runtime_index = std.math.maxInt(u32), + }, + .val = duped, + .ty = payload.field_ty, + }; + }, .elem_ptr => { const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr); diff --git a/src/TypedValue.zig b/src/TypedValue.zig index 9f69e4c8bd..242c4b11a9 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -264,6 +264,16 @@ pub fn print( .val = decl.val, }, writer, level - 1, mod); }, + .comptime_field_ptr => { + const payload = val.castTag(.comptime_field_ptr).?.data; + if (level == 0) { + return writer.writeAll("(comptime field ptr)"); + } + return print(.{ + .ty = payload.field_ty, + .val = payload.field_val, + }, writer, level - 1, mod); + }, .elem_ptr => { const elem_ptr = val.castTag(.elem_ptr).?.data; try writer.writeAll("&"); diff --git a/src/value.zig b/src/value.zig index a80d788894..37caeebd78 100644 --- a/src/value.zig +++ b/src/value.zig @@ -120,6 +120,8 @@ 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, + /// Behaves like `decl_ref_mut` but validates that the stored value matches the field value. + comptime_field_ptr, /// Pointer to a specific element of an array, vector or slice. elem_ptr, /// Pointer to a specific field of a struct or union. @@ -316,6 +318,7 @@ pub const Value = extern union { .aggregate => Payload.Aggregate, .@"union" => Payload.Union, .bound_fn => Payload.BoundFn, + .comptime_field_ptr => Payload.ComptimeFieldPtr, }; } @@ -506,6 +509,18 @@ pub const Value = extern union { }; return Value{ .ptr_otherwise = &new_payload.base }; }, + .comptime_field_ptr => { + const payload = self.cast(Payload.ComptimeFieldPtr).?; + const new_payload = try arena.create(Payload.ComptimeFieldPtr); + new_payload.* = .{ + .base = payload.base, + .data = .{ + .field_val = try payload.data.field_val.copy(arena), + .field_ty = try payload.data.field_ty.copy(arena), + }, + }; + return Value{ .ptr_otherwise = &new_payload.base }; + }, .elem_ptr => { const payload = self.castTag(.elem_ptr).?; const new_payload = try arena.create(Payload.ElemPtr); @@ -754,6 +769,9 @@ pub const Value = extern union { const decl_index = val.castTag(.decl_ref).?.data; return out_stream.print("(decl_ref {d})", .{decl_index}); }, + .comptime_field_ptr => { + return out_stream.writeAll("(comptime_field_ptr)"); + }, .elem_ptr => { const elem_ptr = val.castTag(.elem_ptr).?.data; try out_stream.print("&[{}] ", .{elem_ptr.index}); @@ -1706,6 +1724,7 @@ pub const Value = extern union { .int_big_negative => return self.castTag(.int_big_negative).?.asBigInt().bitCountTwosComp(), .decl_ref_mut, + .comptime_field_ptr, .extern_fn, .decl_ref, .function, @@ -1770,6 +1789,7 @@ pub const Value = extern union { .bool_true, .decl_ref, .decl_ref_mut, + .comptime_field_ptr, .extern_fn, .function, .variable, @@ -2362,7 +2382,7 @@ pub const Value = extern union { pub fn isComptimeMutablePtr(val: Value) bool { return switch (val.tag()) { - .decl_ref_mut => true, + .decl_ref_mut, .comptime_field_ptr => true, .elem_ptr => isComptimeMutablePtr(val.castTag(.elem_ptr).?.data.array_ptr), .field_ptr => isComptimeMutablePtr(val.castTag(.field_ptr).?.data.container_ptr), .eu_payload_ptr => isComptimeMutablePtr(val.castTag(.eu_payload_ptr).?.data.container_ptr), @@ -2426,6 +2446,9 @@ pub const Value = extern union { const decl: Module.Decl.Index = ptr_val.pointerDecl().?; std.hash.autoHash(hasher, decl); }, + .comptime_field_ptr => { + std.hash.autoHash(hasher, Value.Tag.comptime_field_ptr); + }, .elem_ptr => { const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; @@ -2471,7 +2494,7 @@ pub const Value = extern union { return switch (val.tag()) { .slice => val.castTag(.slice).?.data.ptr, // TODO this should require being a slice tag, and not allow decl_ref, field_ptr, etc. - .decl_ref, .decl_ref_mut, .field_ptr, .elem_ptr => val, + .decl_ref, .decl_ref_mut, .field_ptr, .elem_ptr, .comptime_field_ptr => val, else => unreachable, }; } @@ -2497,6 +2520,14 @@ pub const Value = extern union { return 1; } }, + .comptime_field_ptr => { + const payload = val.castTag(.comptime_field_ptr).?.data; + if (payload.field_ty.zigTypeTag() == .Array) { + return payload.field_ty.arrayLen(); + } else { + return 1; + } + }, else => unreachable, }; } @@ -2587,6 +2618,7 @@ pub const Value = extern union { .decl_ref => return mod.declPtr(val.castTag(.decl_ref).?.data).val.elemValueAdvanced(mod, index, arena, buffer), .decl_ref_mut => return mod.declPtr(val.castTag(.decl_ref_mut).?.data.decl_index).val.elemValueAdvanced(mod, index, arena, buffer), + .comptime_field_ptr => return val.castTag(.comptime_field_ptr).?.data.field_val.elemValueAdvanced(mod, index, arena, buffer), .elem_ptr => { const data = val.castTag(.elem_ptr).?.data; return data.array_ptr.elemValueAdvanced(mod, index + data.index, arena, buffer); @@ -2623,6 +2655,7 @@ pub const Value = extern union { .decl_ref => sliceArray(mod.declPtr(val.castTag(.decl_ref).?.data).val, mod, arena, start, end), .decl_ref_mut => sliceArray(mod.declPtr(val.castTag(.decl_ref_mut).?.data.decl_index).val, mod, arena, start, end), + .comptime_field_ptr => sliceArray(val.castTag(.comptime_field_ptr).?.data.field_val, mod, arena, start, end), .elem_ptr => blk: { const elem_ptr = val.castTag(.elem_ptr).?.data; break :blk sliceArray(elem_ptr.array_ptr, mod, arena, start + elem_ptr.index, end + elem_ptr.index); @@ -4742,6 +4775,14 @@ pub const Value = extern union { }, }; + pub const ComptimeFieldPtr = struct { + base: Payload, + data: struct { + field_val: Value, + field_ty: Type, + }, + }; + pub const ElemPtr = struct { pub const base_tag = Tag.elem_ptr; diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 5cbb8e973e..624f1609d4 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1336,3 +1336,25 @@ test "packed struct field access via pointer" { try S.doTheTest(); comptime try S.doTheTest(); } + +test "store to comptime field" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + + { + const S = struct { + comptime a: [2]u32 = [2]u32{ 1, 2 }, + }; + var s: S = .{}; + s.a = [2]u32{ 1, 2 }; + s.a[0] = 1; + } + { + const T = struct { a: u32, b: u32 }; + const S = struct { + comptime a: T = T{ .a = 1, .b = 2 }, + }; + var s: S = .{}; + s.a = T{ .a = 1, .b = 2 }; + s.a.a = 1; + } +} diff --git a/test/cases/compile_errors/invalid_store_to_comptime_field.zig b/test/cases/compile_errors/invalid_store_to_comptime_field.zig new file mode 100644 index 0000000000..3bbd9bbaa8 --- /dev/null +++ b/test/cases/compile_errors/invalid_store_to_comptime_field.zig @@ -0,0 +1,20 @@ +pub export fn entry() void { + const S = struct { + comptime a: [2]u32 = [2]u32{ 1, 2 }, + }; + var s: S = .{}; + s.a = [2]u32{ 2, 2 }; +} +pub export fn entry1() void { + const T = struct { a: u32, b: u32 }; + const S = struct { + comptime a: T = T{ .a = 1, .b = 2 }, + }; + var s: S = .{}; + s.a = T{ .a = 2, .b = 2 }; +} +// error +// backend=stage2,llvm +// +// :6:19: error: value stored in comptime field does not match the default value of the field +// :14:19: error: value stored in comptime field does not match the default value of the field From 8fa88c88c28420d89392a9984748070d35f18321 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 5 Jun 2022 20:08:02 +0300 Subject: [PATCH 1763/2031] AstGen: fix coercion scope type when stores are eliminated --- src/AstGen.zig | 3 +++ test/behavior/basic.zig | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/AstGen.zig b/src/AstGen.zig index ab5befa4ba..2fa2960ce1 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -9875,6 +9875,9 @@ const GenZir = struct { errdefer as_scope.unstack(); as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr); + // `rl_ty_inst` needs to be set in case the stores to `rl_ptr` are eliminated. + as_scope.rl_ty_inst = dest_type; + return as_scope; } diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index b45f5a0b49..dc0e5aaf30 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -1062,3 +1062,15 @@ comptime { s = S{ .a = 1 }; assert(s.a == 1); } + +test "switch inside @as gets correct type" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + var a: u32 = 0; + var b: [2]u32 = undefined; + b[0] = @as(u32, switch (a) { + 1 => 1, + else => 0, + }); +} From 84000aa820de2d00571f7e8fde5d3973e2fdc441 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 5 Jun 2022 20:36:56 +0300 Subject: [PATCH 1764/2031] Sema: fix inline call of func using ret_ptr with comptime only type --- src/Sema.zig | 2 +- test/behavior/basic.zig | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 015b50ce4b..5d70ba3a7f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2526,7 +2526,7 @@ fn zirRetPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const src: LazySrcLoc = .{ .node_offset = inst_data }; try sema.requireFunctionBlock(block, src); - if (block.is_comptime) { + if (block.is_comptime or try sema.typeRequiresComptime(block, src, sema.fn_ret_ty)) { const fn_ret_ty = try sema.resolveTypeFields(block, src, sema.fn_ret_ty); return sema.analyzeComptimeAlloc(block, fn_ret_ty, 0, src); } diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index dc0e5aaf30..a69df862c1 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -1074,3 +1074,15 @@ test "switch inside @as gets correct type" { else => 0, }); } + +test "inline call of function with a switch inside the return statement" { + const S = struct { + inline fn foo(x: anytype) @TypeOf(x) { + return switch (x) { + 1 => 1, + else => unreachable, + }; + } + }; + try expect(S.foo(1) == 1); +} From 15ec55406d4e29cb0b3a753ea76b912d789746d9 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 5 Jun 2022 20:08:20 +0300 Subject: [PATCH 1765/2031] std: fix ambiguous references --- lib/std/os/linux/io_uring.zig | 196 +++++++++++++++++----------------- 1 file changed, 96 insertions(+), 100 deletions(-) diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index 31c416c8a1..3672a94c69 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -7,10 +7,6 @@ const os = std.os; const linux = os.linux; const testing = std.testing; -const io_uring_params = linux.io_uring_params; -const io_uring_sqe = linux.io_uring_sqe; -const io_uring_cqe = linux.io_uring_cqe; - pub const IO_Uring = struct { fd: os.fd_t = -1, sq: SubmissionQueue, @@ -18,24 +14,24 @@ pub const IO_Uring = struct { flags: u32, features: u32, - /// A friendly way to setup an io_uring, with default io_uring_params. + /// A friendly way to setup an io_uring, with default linux.io_uring_params. /// `entries` must be a power of two between 1 and 4096, although the kernel will make the final /// call on how many entries the submission and completion queues will ultimately have, /// see https://github.com/torvalds/linux/blob/v5.8/fs/io_uring.c#L8027-L8050. /// Matches the interface of io_uring_queue_init() in liburing. pub fn init(entries: u13, flags: u32) !IO_Uring { - var params = mem.zeroInit(io_uring_params, .{ + var params = mem.zeroInit(linux.io_uring_params, .{ .flags = flags, .sq_thread_idle = 1000, }); return try IO_Uring.init_params(entries, ¶ms); } - /// A powerful way to setup an io_uring, if you want to tweak io_uring_params such as submission + /// A powerful way to setup an io_uring, if you want to tweak linux.io_uring_params such as submission /// queue thread cpu affinity or thread idle timeout (the kernel and our default is 1 second). /// `params` is passed by reference because the kernel needs to modify the parameters. /// Matches the interface of io_uring_queue_init_params() in liburing. - pub fn init_params(entries: u13, p: *io_uring_params) !IO_Uring { + pub fn init_params(entries: u13, p: *linux.io_uring_params) !IO_Uring { if (entries == 0) return error.EntriesZero; if (!std.math.isPowerOfTwo(entries)) return error.EntriesNotPowerOfTwo; @@ -53,7 +49,7 @@ pub const IO_Uring = struct { .FAULT => return error.ParamsOutsideAccessibleAddressSpace, // The resv array contains non-zero data, p.flags contains an unsupported flag, // entries out of bounds, IORING_SETUP_SQ_AFF was specified without IORING_SETUP_SQPOLL, - // or IORING_SETUP_CQSIZE was specified but io_uring_params.cq_entries was invalid: + // or IORING_SETUP_CQSIZE was specified but linux.io_uring_params.cq_entries was invalid: .INVAL => return error.ArgumentsInvalid, .MFILE => return error.ProcessFdQuotaExceeded, .NFILE => return error.SystemFdQuotaExceeded, @@ -135,7 +131,7 @@ pub const IO_Uring = struct { /// and the null return in liburing is more a C idiom than anything else, for lack of a better /// alternative. In Zig, we have first-class error handling... so let's use it. /// Matches the implementation of io_uring_get_sqe() in liburing. - pub fn get_sqe(self: *IO_Uring) !*io_uring_sqe { + pub fn get_sqe(self: *IO_Uring) !*linux.io_uring_sqe { const head = @atomicLoad(u32, self.sq.head, .Acquire); // Remember that these head and tail offsets wrap around every four billion operations. // We must therefore use wrapping addition and subtraction to avoid a runtime crash. @@ -268,7 +264,7 @@ pub const IO_Uring = struct { /// Faster, because we can now amortize the atomic store release to `cq.head` across the batch. /// See https://github.com/axboe/liburing/issues/103#issuecomment-686665007. /// Matches the implementation of io_uring_peek_batch_cqe() in liburing, but supports waiting. - pub fn copy_cqes(self: *IO_Uring, cqes: []io_uring_cqe, wait_nr: u32) !u32 { + pub fn copy_cqes(self: *IO_Uring, cqes: []linux.io_uring_cqe, wait_nr: u32) !u32 { const count = self.copy_cqes_ready(cqes, wait_nr); if (count > 0) return count; if (self.cq_ring_needs_flush() or wait_nr > 0) { @@ -278,7 +274,7 @@ pub const IO_Uring = struct { return 0; } - fn copy_cqes_ready(self: *IO_Uring, cqes: []io_uring_cqe, wait_nr: u32) u32 { + fn copy_cqes_ready(self: *IO_Uring, cqes: []linux.io_uring_cqe, wait_nr: u32) u32 { _ = wait_nr; const ready = self.cq_ready(); const count = std.math.min(cqes.len, ready); @@ -298,8 +294,8 @@ pub const IO_Uring = struct { /// Returns a copy of an I/O completion, waiting for it if necessary, and advancing the CQ ring. /// A convenience method for `copy_cqes()` for when you don't need to batch or peek. - pub fn copy_cqe(ring: *IO_Uring) !io_uring_cqe { - var cqes: [1]io_uring_cqe = undefined; + pub fn copy_cqe(ring: *IO_Uring) !linux.io_uring_cqe { + var cqes: [1]linux.io_uring_cqe = undefined; while (true) { const count = try ring.copy_cqes(&cqes, 1); if (count > 0) return cqes[0]; @@ -316,7 +312,7 @@ pub const IO_Uring = struct { /// Must be called exactly once after a zero-copy CQE has been processed by your application. /// Not idempotent, calling more than once will result in other CQEs being lost. /// Matches the implementation of cqe_seen() in liburing. - pub fn cqe_seen(self: *IO_Uring, cqe: *io_uring_cqe) void { + pub fn cqe_seen(self: *IO_Uring, cqe: *linux.io_uring_cqe) void { _ = cqe; self.cq_advance(1); } @@ -339,7 +335,7 @@ pub const IO_Uring = struct { /// apply to the write, since the fsync may complete before the write is issued to the disk. /// You should preferably use `link_with_next_sqe()` on a write's SQE to link it with an fsync, /// or else insert a full write barrier using `drain_previous_sqes()` when queueing an fsync. - pub fn fsync(self: *IO_Uring, user_data: u64, fd: os.fd_t, flags: u32) !*io_uring_sqe { + pub fn fsync(self: *IO_Uring, user_data: u64, fd: os.fd_t, flags: u32) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_fsync(sqe, fd, flags); sqe.user_data = user_data; @@ -351,7 +347,7 @@ pub const IO_Uring = struct { /// A no-op is more useful than may appear at first glance. /// For example, you could call `drain_previous_sqes()` on the returned SQE, to use the no-op to /// know when the ring is idle before acting on a kill signal. - pub fn nop(self: *IO_Uring, user_data: u64) !*io_uring_sqe { + pub fn nop(self: *IO_Uring, user_data: u64) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_nop(sqe); sqe.user_data = user_data; @@ -387,7 +383,7 @@ pub const IO_Uring = struct { fd: os.fd_t, buffer: ReadBuffer, offset: u64, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); switch (buffer) { .buffer => |slice| io_uring_prep_read(sqe, fd, slice, offset), @@ -410,7 +406,7 @@ pub const IO_Uring = struct { fd: os.fd_t, buffer: []const u8, offset: u64, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_write(sqe, fd, buffer, offset); sqe.user_data = user_data; @@ -429,7 +425,7 @@ pub const IO_Uring = struct { buffer: *os.iovec, offset: u64, buffer_index: u16, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_read_fixed(sqe, fd, buffer, offset, buffer_index); sqe.user_data = user_data; @@ -446,7 +442,7 @@ pub const IO_Uring = struct { fd: os.fd_t, iovecs: []const os.iovec_const, offset: u64, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_writev(sqe, fd, iovecs, offset); sqe.user_data = user_data; @@ -465,7 +461,7 @@ pub const IO_Uring = struct { buffer: *os.iovec, offset: u64, buffer_index: u16, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_write_fixed(sqe, fd, buffer, offset, buffer_index); sqe.user_data = user_data; @@ -481,7 +477,7 @@ pub const IO_Uring = struct { addr: *os.sockaddr, addrlen: *os.socklen_t, flags: u32, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_accept(sqe, fd, addr, addrlen, flags); sqe.user_data = user_data; @@ -496,7 +492,7 @@ pub const IO_Uring = struct { fd: os.fd_t, addr: *const os.sockaddr, addrlen: os.socklen_t, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_connect(sqe, fd, addr, addrlen); sqe.user_data = user_data; @@ -512,7 +508,7 @@ pub const IO_Uring = struct { fd: os.fd_t, op: u32, ev: ?*linux.epoll_event, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_epoll_ctl(sqe, epfd, fd, op, ev); sqe.user_data = user_data; @@ -541,7 +537,7 @@ pub const IO_Uring = struct { fd: os.fd_t, buffer: RecvBuffer, flags: u32, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); switch (buffer) { .buffer => |slice| io_uring_prep_recv(sqe, fd, slice, flags), @@ -564,7 +560,7 @@ pub const IO_Uring = struct { fd: os.fd_t, buffer: []const u8, flags: u32, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_send(sqe, fd, buffer, flags); sqe.user_data = user_data; @@ -579,7 +575,7 @@ pub const IO_Uring = struct { fd: os.fd_t, msg: *os.msghdr, flags: u32, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_recvmsg(sqe, fd, msg, flags); sqe.user_data = user_data; @@ -594,7 +590,7 @@ pub const IO_Uring = struct { fd: os.fd_t, msg: *const os.msghdr_const, flags: u32, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_sendmsg(sqe, fd, msg, flags); sqe.user_data = user_data; @@ -610,7 +606,7 @@ pub const IO_Uring = struct { path: [*:0]const u8, flags: u32, mode: os.mode_t, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_openat(sqe, fd, path, flags, mode); sqe.user_data = user_data; @@ -619,7 +615,7 @@ pub const IO_Uring = struct { /// Queues (but does not submit) an SQE to perform a `close(2)`. /// Returns a pointer to the SQE. - pub fn close(self: *IO_Uring, user_data: u64, fd: os.fd_t) !*io_uring_sqe { + pub fn close(self: *IO_Uring, user_data: u64, fd: os.fd_t) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_close(sqe, fd); sqe.user_data = user_data; @@ -645,7 +641,7 @@ pub const IO_Uring = struct { ts: *const os.linux.kernel_timespec, count: u32, flags: u32, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_timeout(sqe, ts, count, flags); sqe.user_data = user_data; @@ -665,7 +661,7 @@ pub const IO_Uring = struct { user_data: u64, timeout_user_data: u64, flags: u32, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_timeout_remove(sqe, timeout_user_data, flags); sqe.user_data = user_data; @@ -693,7 +689,7 @@ pub const IO_Uring = struct { user_data: u64, ts: *const os.linux.kernel_timespec, flags: u32, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_link_timeout(sqe, ts, flags); sqe.user_data = user_data; @@ -707,7 +703,7 @@ pub const IO_Uring = struct { user_data: u64, fd: os.fd_t, poll_mask: u32, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_poll_add(sqe, fd, poll_mask); sqe.user_data = user_data; @@ -720,7 +716,7 @@ pub const IO_Uring = struct { self: *IO_Uring, user_data: u64, target_user_data: u64, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_poll_remove(sqe, target_user_data); sqe.user_data = user_data; @@ -736,7 +732,7 @@ pub const IO_Uring = struct { new_user_data: u64, poll_mask: u32, flags: u32, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_poll_update(sqe, old_user_data, new_user_data, poll_mask, flags); sqe.user_data = user_data; @@ -752,7 +748,7 @@ pub const IO_Uring = struct { mode: i32, offset: u64, len: u64, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_fallocate(sqe, fd, mode, offset, len); sqe.user_data = user_data; @@ -769,7 +765,7 @@ pub const IO_Uring = struct { flags: u32, mask: u32, buf: *linux.Statx, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_statx(sqe, fd, path, flags, mask, buf); sqe.user_data = user_data; @@ -789,7 +785,7 @@ pub const IO_Uring = struct { user_data: u64, cancel_user_data: u64, flags: u32, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_cancel(sqe, cancel_user_data, flags); sqe.user_data = user_data; @@ -805,7 +801,7 @@ pub const IO_Uring = struct { user_data: u64, sockfd: os.socket_t, how: u32, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_shutdown(sqe, sockfd, how); sqe.user_data = user_data; @@ -822,7 +818,7 @@ pub const IO_Uring = struct { new_dir_fd: os.fd_t, new_path: [*:0]const u8, flags: u32, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_renameat(sqe, old_dir_fd, old_path, new_dir_fd, new_path, flags); sqe.user_data = user_data; @@ -837,7 +833,7 @@ pub const IO_Uring = struct { dir_fd: os.fd_t, path: [*:0]const u8, flags: u32, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_unlinkat(sqe, dir_fd, path, flags); sqe.user_data = user_data; @@ -852,7 +848,7 @@ pub const IO_Uring = struct { dir_fd: os.fd_t, path: [*:0]const u8, mode: os.mode_t, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_mkdirat(sqe, dir_fd, path, mode); sqe.user_data = user_data; @@ -867,7 +863,7 @@ pub const IO_Uring = struct { target: [*:0]const u8, new_dir_fd: os.fd_t, link_path: [*:0]const u8, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_symlinkat(sqe, target, new_dir_fd, link_path); sqe.user_data = user_data; @@ -884,7 +880,7 @@ pub const IO_Uring = struct { new_dir_fd: os.fd_t, new_path: [*:0]const u8, flags: u32, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_linkat(sqe, old_dir_fd, old_path, new_dir_fd, new_path, flags); sqe.user_data = user_data; @@ -905,7 +901,7 @@ pub const IO_Uring = struct { buffer_size: usize, group_id: usize, buffer_id: usize, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_provide_buffers(sqe, buffers, buffers_count, buffer_size, group_id, buffer_id); sqe.user_data = user_data; @@ -919,7 +915,7 @@ pub const IO_Uring = struct { user_data: u64, buffers_count: usize, group_id: usize, - ) !*io_uring_sqe { + ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); io_uring_prep_remove_buffers(sqe, buffers_count, group_id); sqe.user_data = user_data; @@ -1083,7 +1079,7 @@ pub const SubmissionQueue = struct { flags: *u32, dropped: *u32, array: []u32, - sqes: []io_uring_sqe, + sqes: []linux.io_uring_sqe, mmap: []align(mem.page_size) u8, mmap_sqes: []align(mem.page_size) u8, @@ -1094,12 +1090,12 @@ pub const SubmissionQueue = struct { sqe_head: u32 = 0, sqe_tail: u32 = 0, - pub fn init(fd: os.fd_t, p: io_uring_params) !SubmissionQueue { + pub fn init(fd: os.fd_t, p: linux.io_uring_params) !SubmissionQueue { assert(fd >= 0); assert((p.features & linux.IORING_FEAT_SINGLE_MMAP) != 0); const size = std.math.max( p.sq_off.array + p.sq_entries * @sizeOf(u32), - p.cq_off.cqes + p.cq_entries * @sizeOf(io_uring_cqe), + p.cq_off.cqes + p.cq_entries * @sizeOf(linux.io_uring_cqe), ); const mmap = try os.mmap( null, @@ -1113,8 +1109,8 @@ pub const SubmissionQueue = struct { assert(mmap.len == size); // The motivation for the `sqes` and `array` indirection is to make it possible for the - // application to preallocate static io_uring_sqe entries and then replay them when needed. - const size_sqes = p.sq_entries * @sizeOf(io_uring_sqe); + // application to preallocate static linux.io_uring_sqe entries and then replay them when needed. + const size_sqes = p.sq_entries * @sizeOf(linux.io_uring_sqe); const mmap_sqes = try os.mmap( null, size_sqes, @@ -1127,7 +1123,7 @@ pub const SubmissionQueue = struct { assert(mmap_sqes.len == size_sqes); const array = @ptrCast([*]u32, @alignCast(@alignOf(u32), &mmap[p.sq_off.array])); - const sqes = @ptrCast([*]io_uring_sqe, @alignCast(@alignOf(io_uring_sqe), &mmap_sqes[0])); + const sqes = @ptrCast([*]linux.io_uring_sqe, @alignCast(@alignOf(linux.io_uring_sqe), &mmap_sqes[0])); // We expect the kernel copies p.sq_entries to the u32 pointed to by p.sq_off.ring_entries, // see https://github.com/torvalds/linux/blob/v5.8/fs/io_uring.c#L7843-L7844. assert( @@ -1158,15 +1154,15 @@ pub const CompletionQueue = struct { tail: *u32, mask: u32, overflow: *u32, - cqes: []io_uring_cqe, + cqes: []linux.io_uring_cqe, - pub fn init(fd: os.fd_t, p: io_uring_params, sq: SubmissionQueue) !CompletionQueue { + pub fn init(fd: os.fd_t, p: linux.io_uring_params, sq: SubmissionQueue) !CompletionQueue { assert(fd >= 0); assert((p.features & linux.IORING_FEAT_SINGLE_MMAP) != 0); const mmap = sq.mmap; const cqes = @ptrCast( - [*]io_uring_cqe, - @alignCast(@alignOf(io_uring_cqe), &mmap[p.cq_off.cqes]), + [*]linux.io_uring_cqe, + @alignCast(@alignOf(linux.io_uring_cqe), &mmap[p.cq_off.cqes]), ); assert(p.cq_entries == @ptrCast(*u32, @alignCast(@alignOf(u32), &mmap[p.cq_off.ring_entries])).*); @@ -1186,7 +1182,7 @@ pub const CompletionQueue = struct { } }; -pub fn io_uring_prep_nop(sqe: *io_uring_sqe) void { +pub fn io_uring_prep_nop(sqe: *linux.io_uring_sqe) void { sqe.* = .{ .opcode = .NOP, .flags = 0, @@ -1204,7 +1200,7 @@ pub fn io_uring_prep_nop(sqe: *io_uring_sqe) void { }; } -pub fn io_uring_prep_fsync(sqe: *io_uring_sqe, fd: os.fd_t, flags: u32) void { +pub fn io_uring_prep_fsync(sqe: *linux.io_uring_sqe, fd: os.fd_t, flags: u32) void { sqe.* = .{ .opcode = .FSYNC, .flags = 0, @@ -1224,7 +1220,7 @@ pub fn io_uring_prep_fsync(sqe: *io_uring_sqe, fd: os.fd_t, flags: u32) void { pub fn io_uring_prep_rw( op: linux.IORING_OP, - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, fd: os.fd_t, addr: u64, len: usize, @@ -1247,16 +1243,16 @@ pub fn io_uring_prep_rw( }; } -pub fn io_uring_prep_read(sqe: *io_uring_sqe, fd: os.fd_t, buffer: []u8, offset: u64) void { +pub fn io_uring_prep_read(sqe: *linux.io_uring_sqe, fd: os.fd_t, buffer: []u8, offset: u64) void { io_uring_prep_rw(.READ, sqe, fd, @ptrToInt(buffer.ptr), buffer.len, offset); } -pub fn io_uring_prep_write(sqe: *io_uring_sqe, fd: os.fd_t, buffer: []const u8, offset: u64) void { +pub fn io_uring_prep_write(sqe: *linux.io_uring_sqe, fd: os.fd_t, buffer: []const u8, offset: u64) void { io_uring_prep_rw(.WRITE, sqe, fd, @ptrToInt(buffer.ptr), buffer.len, offset); } pub fn io_uring_prep_readv( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, fd: os.fd_t, iovecs: []const os.iovec, offset: u64, @@ -1265,7 +1261,7 @@ pub fn io_uring_prep_readv( } pub fn io_uring_prep_writev( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, fd: os.fd_t, iovecs: []const os.iovec_const, offset: u64, @@ -1273,12 +1269,12 @@ pub fn io_uring_prep_writev( io_uring_prep_rw(.WRITEV, sqe, fd, @ptrToInt(iovecs.ptr), iovecs.len, offset); } -pub fn io_uring_prep_read_fixed(sqe: *io_uring_sqe, fd: os.fd_t, buffer: *os.iovec, offset: u64, buffer_index: u16) void { +pub fn io_uring_prep_read_fixed(sqe: *linux.io_uring_sqe, fd: os.fd_t, buffer: *os.iovec, offset: u64, buffer_index: u16) void { io_uring_prep_rw(.READ_FIXED, sqe, fd, @ptrToInt(buffer.iov_base), buffer.iov_len, offset); sqe.buf_index = buffer_index; } -pub fn io_uring_prep_write_fixed(sqe: *io_uring_sqe, fd: os.fd_t, buffer: *os.iovec, offset: u64, buffer_index: u16) void { +pub fn io_uring_prep_write_fixed(sqe: *linux.io_uring_sqe, fd: os.fd_t, buffer: *os.iovec, offset: u64, buffer_index: u16) void { io_uring_prep_rw(.WRITE_FIXED, sqe, fd, @ptrToInt(buffer.iov_base), buffer.iov_len, offset); sqe.buf_index = buffer_index; } @@ -1294,7 +1290,7 @@ pub inline fn __io_uring_prep_poll_mask(poll_mask: u32) u32 { } pub fn io_uring_prep_accept( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, fd: os.fd_t, addr: *os.sockaddr, addrlen: *os.socklen_t, @@ -1307,7 +1303,7 @@ pub fn io_uring_prep_accept( } pub fn io_uring_prep_connect( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, fd: os.fd_t, addr: *const os.sockaddr, addrlen: os.socklen_t, @@ -1317,7 +1313,7 @@ pub fn io_uring_prep_connect( } pub fn io_uring_prep_epoll_ctl( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, epfd: os.fd_t, fd: os.fd_t, op: u32, @@ -1326,18 +1322,18 @@ pub fn io_uring_prep_epoll_ctl( io_uring_prep_rw(.EPOLL_CTL, sqe, epfd, @ptrToInt(ev), op, @intCast(u64, fd)); } -pub fn io_uring_prep_recv(sqe: *io_uring_sqe, fd: os.fd_t, buffer: []u8, flags: u32) void { +pub fn io_uring_prep_recv(sqe: *linux.io_uring_sqe, fd: os.fd_t, buffer: []u8, flags: u32) void { io_uring_prep_rw(.RECV, sqe, fd, @ptrToInt(buffer.ptr), buffer.len, 0); sqe.rw_flags = flags; } -pub fn io_uring_prep_send(sqe: *io_uring_sqe, fd: os.fd_t, buffer: []const u8, flags: u32) void { +pub fn io_uring_prep_send(sqe: *linux.io_uring_sqe, fd: os.fd_t, buffer: []const u8, flags: u32) void { io_uring_prep_rw(.SEND, sqe, fd, @ptrToInt(buffer.ptr), buffer.len, 0); sqe.rw_flags = flags; } pub fn io_uring_prep_recvmsg( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, fd: os.fd_t, msg: *os.msghdr, flags: u32, @@ -1347,7 +1343,7 @@ pub fn io_uring_prep_recvmsg( } pub fn io_uring_prep_sendmsg( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, fd: os.fd_t, msg: *const os.msghdr_const, flags: u32, @@ -1357,7 +1353,7 @@ pub fn io_uring_prep_sendmsg( } pub fn io_uring_prep_openat( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, fd: os.fd_t, path: [*:0]const u8, flags: u32, @@ -1367,7 +1363,7 @@ pub fn io_uring_prep_openat( sqe.rw_flags = flags; } -pub fn io_uring_prep_close(sqe: *io_uring_sqe, fd: os.fd_t) void { +pub fn io_uring_prep_close(sqe: *linux.io_uring_sqe, fd: os.fd_t) void { sqe.* = .{ .opcode = .CLOSE, .flags = 0, @@ -1386,7 +1382,7 @@ pub fn io_uring_prep_close(sqe: *io_uring_sqe, fd: os.fd_t) void { } pub fn io_uring_prep_timeout( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, ts: *const os.linux.kernel_timespec, count: u32, flags: u32, @@ -1395,7 +1391,7 @@ pub fn io_uring_prep_timeout( sqe.rw_flags = flags; } -pub fn io_uring_prep_timeout_remove(sqe: *io_uring_sqe, timeout_user_data: u64, flags: u32) void { +pub fn io_uring_prep_timeout_remove(sqe: *linux.io_uring_sqe, timeout_user_data: u64, flags: u32) void { sqe.* = .{ .opcode = .TIMEOUT_REMOVE, .flags = 0, @@ -1414,7 +1410,7 @@ pub fn io_uring_prep_timeout_remove(sqe: *io_uring_sqe, timeout_user_data: u64, } pub fn io_uring_prep_link_timeout( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, ts: *const os.linux.kernel_timespec, flags: u32, ) void { @@ -1423,7 +1419,7 @@ pub fn io_uring_prep_link_timeout( } pub fn io_uring_prep_poll_add( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, fd: os.fd_t, poll_mask: u32, ) void { @@ -1432,14 +1428,14 @@ pub fn io_uring_prep_poll_add( } pub fn io_uring_prep_poll_remove( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, target_user_data: u64, ) void { io_uring_prep_rw(.POLL_REMOVE, sqe, -1, target_user_data, 0, 0); } pub fn io_uring_prep_poll_update( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, old_user_data: u64, new_user_data: u64, poll_mask: u32, @@ -1450,7 +1446,7 @@ pub fn io_uring_prep_poll_update( } pub fn io_uring_prep_fallocate( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, fd: os.fd_t, mode: i32, offset: u64, @@ -1474,7 +1470,7 @@ pub fn io_uring_prep_fallocate( } pub fn io_uring_prep_statx( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, fd: os.fd_t, path: [*:0]const u8, flags: u32, @@ -1486,7 +1482,7 @@ pub fn io_uring_prep_statx( } pub fn io_uring_prep_cancel( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, cancel_user_data: u64, flags: u32, ) void { @@ -1495,7 +1491,7 @@ pub fn io_uring_prep_cancel( } pub fn io_uring_prep_shutdown( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, sockfd: os.socket_t, how: u32, ) void { @@ -1503,7 +1499,7 @@ pub fn io_uring_prep_shutdown( } pub fn io_uring_prep_renameat( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, old_dir_fd: os.fd_t, old_path: [*:0]const u8, new_dir_fd: os.fd_t, @@ -1523,7 +1519,7 @@ pub fn io_uring_prep_renameat( } pub fn io_uring_prep_unlinkat( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, dir_fd: os.fd_t, path: [*:0]const u8, flags: u32, @@ -1533,7 +1529,7 @@ pub fn io_uring_prep_unlinkat( } pub fn io_uring_prep_mkdirat( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, dir_fd: os.fd_t, path: [*:0]const u8, mode: os.mode_t, @@ -1542,7 +1538,7 @@ pub fn io_uring_prep_mkdirat( } pub fn io_uring_prep_symlinkat( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, target: [*:0]const u8, new_dir_fd: os.fd_t, link_path: [*:0]const u8, @@ -1558,7 +1554,7 @@ pub fn io_uring_prep_symlinkat( } pub fn io_uring_prep_linkat( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, old_dir_fd: os.fd_t, old_path: [*:0]const u8, new_dir_fd: os.fd_t, @@ -1578,7 +1574,7 @@ pub fn io_uring_prep_linkat( } pub fn io_uring_prep_provide_buffers( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, buffers: [*]u8, num: usize, buffer_len: usize, @@ -1591,7 +1587,7 @@ pub fn io_uring_prep_provide_buffers( } pub fn io_uring_prep_remove_buffers( - sqe: *io_uring_sqe, + sqe: *linux.io_uring_sqe, num: usize, group_id: usize, ) void { @@ -1602,9 +1598,9 @@ pub fn io_uring_prep_remove_buffers( test "structs/offsets/entries" { if (builtin.os.tag != .linux) return error.SkipZigTest; - try testing.expectEqual(@as(usize, 120), @sizeOf(io_uring_params)); - try testing.expectEqual(@as(usize, 64), @sizeOf(io_uring_sqe)); - try testing.expectEqual(@as(usize, 16), @sizeOf(io_uring_cqe)); + try testing.expectEqual(@as(usize, 120), @sizeOf(linux.io_uring_params)); + try testing.expectEqual(@as(usize, 64), @sizeOf(linux.io_uring_sqe)); + try testing.expectEqual(@as(usize, 16), @sizeOf(linux.io_uring_cqe)); try testing.expectEqual(0, linux.IORING_OFF_SQ_RING); try testing.expectEqual(0x8000000, linux.IORING_OFF_CQ_RING); @@ -1628,7 +1624,7 @@ test "nop" { } const sqe = try ring.nop(0xaaaaaaaa); - try testing.expectEqual(io_uring_sqe{ + try testing.expectEqual(linux.io_uring_sqe{ .opcode = .NOP, .flags = 0, .ioprio = 0, @@ -1658,7 +1654,7 @@ test "nop" { try testing.expectEqual(@as(u32, 0), ring.cq.head.*); try testing.expectEqual(@as(u32, 0), ring.sq_ready()); - try testing.expectEqual(io_uring_cqe{ + try testing.expectEqual(linux.io_uring_cqe{ .user_data = 0xaaaaaaaa, .res = 0, .flags = 0, @@ -1669,7 +1665,7 @@ test "nop" { const sqe_barrier = try ring.nop(0xbbbbbbbb); sqe_barrier.flags |= linux.IOSQE_IO_DRAIN; try testing.expectEqual(@as(u32, 1), try ring.submit()); - try testing.expectEqual(io_uring_cqe{ + try testing.expectEqual(linux.io_uring_cqe{ .user_data = 0xbbbbbbbb, .res = 0, .flags = 0, @@ -1909,7 +1905,7 @@ test "openat" { const flags: u32 = os.O.CLOEXEC | os.O.RDWR | os.O.CREAT; const mode: os.mode_t = 0o666; const sqe_openat = try ring.openat(0x33333333, linux.AT.FDCWD, path, flags, mode); - try testing.expectEqual(io_uring_sqe{ + try testing.expectEqual(linux.io_uring_sqe{ .opcode = .OPENAT, .flags = 0, .ioprio = 0, From bb84c87a4788ffed5478397549b363fe514658f3 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 5 Jun 2022 20:59:42 +0300 Subject: [PATCH 1766/2031] std: add necessary `@alignCast`s --- lib/std/crypto/scrypt.zig | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/std/crypto/scrypt.zig b/lib/std/crypto/scrypt.zig index 31c8b754ff..e8cb6bab7b 100644 --- a/lib/std/crypto/scrypt.zig +++ b/lib/std/crypto/scrypt.zig @@ -73,11 +73,11 @@ fn salsaXor(tmp: *align(16) [16]u32, in: []align(16) const u32, out: []align(16) } fn blockMix(tmp: *align(16) [16]u32, in: []align(16) const u32, out: []align(16) u32, r: u30) void { - blockCopy(tmp, in[(2 * r - 1) * 16 ..], 1); + blockCopy(tmp, @alignCast(16, in[(2 * r - 1) * 16 ..]), 1); var i: usize = 0; while (i < 2 * r) : (i += 2) { - salsaXor(tmp, in[i * 16 ..], out[i * 8 ..]); - salsaXor(tmp, in[i * 16 + 16 ..], out[i * 8 + r * 16 ..]); + salsaXor(tmp, @alignCast(16, in[i * 16 ..]), @alignCast(16, out[i * 8 ..])); + salsaXor(tmp, @alignCast(16, in[i * 16 + 16 ..]), @alignCast(16, out[i * 8 + r * 16 ..])); } } @@ -87,8 +87,8 @@ fn integerify(b: []align(16) const u32, r: u30) u64 { } fn smix(b: []align(16) u8, r: u30, n: usize, v: []align(16) u32, xy: []align(16) u32) void { - var x = xy[0 .. 32 * r]; - var y = xy[32 * r ..]; + var x = @alignCast(16, xy[0 .. 32 * r]); + var y = @alignCast(16, xy[32 * r ..]); for (x) |*v1, j| { v1.* = mem.readIntSliceLittle(u32, b[4 * j ..]); @@ -97,21 +97,21 @@ fn smix(b: []align(16) u8, r: u30, n: usize, v: []align(16) u32, xy: []align(16) var tmp: [16]u32 align(16) = undefined; var i: usize = 0; while (i < n) : (i += 2) { - blockCopy(v[i * (32 * r) ..], x, 2 * r); + blockCopy(@alignCast(16, v[i * (32 * r) ..]), x, 2 * r); blockMix(&tmp, x, y, r); - blockCopy(v[(i + 1) * (32 * r) ..], y, 2 * r); + blockCopy(@alignCast(16, v[(i + 1) * (32 * r) ..]), y, 2 * r); blockMix(&tmp, y, x, r); } i = 0; while (i < n) : (i += 2) { var j = @intCast(usize, integerify(x, r) & (n - 1)); - blockXor(x, v[j * (32 * r) ..], 2 * r); + blockXor(x, @alignCast(16, v[j * (32 * r) ..]), 2 * r); blockMix(&tmp, x, y, r); j = @intCast(usize, integerify(y, r) & (n - 1)); - blockXor(y, v[j * (32 * r) ..], 2 * r); + blockXor(y, @alignCast(16, v[j * (32 * r) ..]), 2 * r); blockMix(&tmp, y, x, r); } @@ -201,7 +201,7 @@ pub fn kdf( try pwhash.pbkdf2(dk, password, salt, 1, HmacSha256); var i: u32 = 0; while (i < params.p) : (i += 1) { - smix(dk[i * 128 * params.r ..], params.r, n, v, xy); + smix(@alignCast(16, dk[i * 128 * params.r ..]), params.r, n, v, xy); } try pwhash.pbkdf2(derived_key, password, dk, 1, HmacSha256); } From 61c5d8f8f19d4321a492cb8a1adc4d221024f7d9 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 5 Jun 2022 21:16:42 +0300 Subject: [PATCH 1767/2031] std.fs: fix incorrect passing of pointer to temporary --- lib/std/fs.zig | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 31354a2782..4a21af806e 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -887,10 +887,8 @@ pub const Dir = struct { } pub fn deinit(self: *Walker) void { - while (self.stack.popOrNull()) |*item| { - if (self.stack.items.len != 0) { - item.iter.dir.close(); - } + for (self.stack.items) |*item| { + item.iter.dir.close(); } self.stack.deinit(); self.name_buffer.deinit(); From 32568dba742a31051f2390b55b936a03b5146e17 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 5 Jun 2022 21:23:02 +0300 Subject: [PATCH 1768/2031] std: handle stage2 function pointer semantics in test --- lib/std/os/test.zig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 975d5a64eb..44f8b16b9e 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -766,8 +766,10 @@ test "sigaction" { } }; + const actual_handler = if (builtin.zig_backend == .stage1) S.handler else &S.handler; + var sa = os.Sigaction{ - .handler = .{ .sigaction = S.handler }, + .handler = .{ .sigaction = actual_handler }, .mask = os.empty_sigset, .flags = os.SA.SIGINFO | os.SA.RESETHAND, }; @@ -776,7 +778,7 @@ test "sigaction" { try os.sigaction(os.SIG.USR1, &sa, null); // Check that we can read it back correctly. try os.sigaction(os.SIG.USR1, null, &old_sa); - try testing.expectEqual(S.handler, old_sa.handler.sigaction.?); + try testing.expectEqual(actual_handler, old_sa.handler.sigaction.?); try testing.expect((old_sa.flags & os.SA.SIGINFO) != 0); // Invoke the handler. try os.raise(os.SIG.USR1); From 14685e59b26c8dc002ce6c25c6916cbad54e79d0 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 5 Jun 2022 21:48:22 +0300 Subject: [PATCH 1769/2031] stage2: use correct type (u29) for alignment --- src/AstGen.zig | 8 ++++++-- src/Module.zig | 2 +- src/Sema.zig | 15 +++++++++------ src/TypedValue.zig | 1 + src/Zir.zig | 5 +++++ src/type.zig | 20 ++++++++++++++++++++ src/value.zig | 9 +++++++-- 7 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 2fa2960ce1..3566610bb6 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -291,8 +291,8 @@ pub const ResultLoc = union(enum) { } }; -pub const align_rl: ResultLoc = .{ .ty = .u16_type }; -pub const coerced_align_rl: ResultLoc = .{ .coerced_ty = .u16_type }; +pub const align_rl: ResultLoc = .{ .ty = .u29_type }; +pub const coerced_align_rl: ResultLoc = .{ .coerced_ty = .u29_type }; pub const bool_rl: ResultLoc = .{ .ty = .bool_type }; pub const type_rl: ResultLoc = .{ .ty = .type_type }; pub const coerced_type_rl: ResultLoc = .{ .coerced_ty = .type_type }; @@ -8077,6 +8077,7 @@ const primitives = std.ComptimeStringMap(Zir.Inst.Ref, .{ .{ "true", .bool_true }, .{ "type", .type_type }, .{ "u16", .u16_type }, + .{ "u29", .u29_type }, .{ "u32", .u32_type }, .{ "u64", .u64_type }, .{ "u128", .u128_type }, @@ -8749,6 +8750,7 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In .isize_type, .type_type, .u16_type, + .u29_type, .u32_type, .u64_type, .u128_type, @@ -8988,6 +8990,7 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool { .i8_type, .isize_type, .u16_type, + .u29_type, .u32_type, .u64_type, .u128_type, @@ -9063,6 +9066,7 @@ fn rvalue( as_ty | @enumToInt(Zir.Inst.Ref.u8_type), as_ty | @enumToInt(Zir.Inst.Ref.i8_type), as_ty | @enumToInt(Zir.Inst.Ref.u16_type), + as_ty | @enumToInt(Zir.Inst.Ref.u29_type), as_ty | @enumToInt(Zir.Inst.Ref.i16_type), as_ty | @enumToInt(Zir.Inst.Ref.u32_type), as_ty | @enumToInt(Zir.Inst.Ref.i32_type), diff --git a/src/Module.zig b/src/Module.zig index f8d662ae1f..12d311046a 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4016,7 +4016,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { try wip_captures.finalize(); const src: LazySrcLoc = .{ .node_offset = 0 }; const decl_tv = try sema.resolveInstValue(&block_scope, src, result_ref); - const decl_align: u16 = blk: { + const decl_align: u32 = blk: { const align_ref = decl.zirAlignRef(); if (align_ref == .none) break :blk 0; break :blk try sema.resolveAlign(&block_scope, src, align_ref); diff --git a/src/Sema.zig b/src/Sema.zig index 5d70ba3a7f..381f922093 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1739,9 +1739,9 @@ pub fn resolveAlign( block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref, -) !u16 { - const alignment_big = try sema.resolveInt(block, src, zir_ref, Type.initTag(.u16)); - const alignment = @intCast(u16, alignment_big); // We coerce to u16 in the prev line. +) !u32 { + const alignment_big = try sema.resolveInt(block, src, zir_ref, Type.initTag(.u29)); + const alignment = @intCast(u32, alignment_big); // We coerce to u16 in the prev line. if (alignment == 0) return sema.fail(block, src, "alignment must be >= 1", .{}); if (!std.math.isPowerOfTwo(alignment)) { return sema.fail(block, src, "alignment value {d} is not a power of two", .{ @@ -2663,7 +2663,7 @@ fn zirAllocExtended( break :blk try sema.resolveType(block, ty_src, type_ref); } else undefined; - const alignment: u16 = if (small.has_align) blk: { + const alignment: u32 = if (small.has_align) blk: { const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; const alignment = try sema.resolveAlign(block, align_src, align_ref); @@ -14210,7 +14210,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I .size = ptr_size, .mutable = !is_const_val.toBool(), .@"volatile" = is_volatile_val.toBool(), - .@"align" = @intCast(u16, alignment_val.toUnsignedInt(target)), // TODO: Validate this value. + .@"align" = @intCast(u29, alignment_val.toUnsignedInt(target)), // TODO: Validate this value. .@"addrspace" = address_space_val.toEnum(std.builtin.AddressSpace), .pointee_type = try child_ty.copy(sema.arena), .@"allowzero" = is_allowzero_val.toBool(), @@ -16984,7 +16984,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const body = sema.code.extra[extra_index..][0..body_len]; extra_index += body.len; - const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u16); + const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u29); if (val.tag() == .generic_poison) { break :blk null; } @@ -23886,6 +23886,7 @@ pub fn typeHasOnePossibleValue( .i8, .u16, .i16, + .u29, .u32, .i32, .u64, @@ -24179,6 +24180,7 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref { .u8 => return .u8_type, .i8 => return .i8_type, .u16 => return .u16_type, + .u29 => return .u29_type, .i16 => return .i16_type, .u32 => return .u32_type, .i32 => return .i32_type, @@ -24549,6 +24551,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ .i8, .u16, .i16, + .u29, .u32, .i32, .u64, diff --git a/src/TypedValue.zig b/src/TypedValue.zig index 242c4b11a9..4b3bc23231 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -79,6 +79,7 @@ pub fn print( .i8_type => return writer.writeAll("i8"), .u16_type => return writer.writeAll("u16"), .i16_type => return writer.writeAll("i16"), + .u29_type => return writer.writeAll("u29"), .u32_type => return writer.writeAll("u32"), .i32_type => return writer.writeAll("i32"), .u64_type => return writer.writeAll("u64"), diff --git a/src/Zir.zig b/src/Zir.zig index f09f2015e0..9f1737d19d 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -1961,6 +1961,7 @@ pub const Inst = struct { i8_type, u16_type, i16_type, + u29_type, u32_type, i32_type, u64_type, @@ -2072,6 +2073,10 @@ pub const Inst = struct { .ty = Type.initTag(.type), .val = Value.initTag(.i16_type), }, + .u29_type = .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.u29_type), + }, .u32_type = .{ .ty = Type.initTag(.type), .val = Value.initTag(.u32_type), diff --git a/src/type.zig b/src/type.zig index ee669df620..14c613a947 100644 --- a/src/type.zig +++ b/src/type.zig @@ -36,6 +36,7 @@ pub const Type = extern union { .i8, .u16, .i16, + .u29, .u32, .i32, .u64, @@ -568,6 +569,7 @@ pub const Type = extern union { .i8, .u16, .i16, + .u29, .u32, .i32, .u64, @@ -979,6 +981,7 @@ pub const Type = extern union { .i8, .u16, .i16, + .u29, .u32, .i32, .u64, @@ -1261,6 +1264,7 @@ pub const Type = extern union { .i8, .u16, .i16, + .u29, .u32, .i32, .u64, @@ -1551,6 +1555,7 @@ pub const Type = extern union { .i8, .u16, .i16, + .u29, .u32, .i32, .u64, @@ -1935,6 +1940,7 @@ pub const Type = extern union { .i8, .u16, .i16, + .u29, .u32, .i32, .u64, @@ -2235,6 +2241,7 @@ pub const Type = extern union { .u8 => return Value.initTag(.u8_type), .i8 => return Value.initTag(.i8_type), .u16 => return Value.initTag(.u16_type), + .u29 => return Value.initTag(.u29_type), .i16 => return Value.initTag(.i16_type), .u32 => return Value.initTag(.u32_type), .i32 => return Value.initTag(.i32_type), @@ -2312,6 +2319,7 @@ pub const Type = extern union { .i8, .u16, .i16, + .u29, .u32, .i32, .u64, @@ -2560,6 +2568,7 @@ pub const Type = extern union { .i8, .u16, .i16, + .u29, .u32, .i32, .u64, @@ -2953,6 +2962,7 @@ pub const Type = extern union { .vector => return AbiAlignmentAdvanced{ .scalar = 16 }, .i16, .u16 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(16, target) }, + .u29 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(29, target) }, .i32, .u32 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(32, target) }, .i64, .u64 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(64, target) }, .u128, .i128 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(128, target) }, @@ -3416,6 +3426,7 @@ pub const Type = extern union { }, .i16, .u16 => return AbiSizeAdvanced{ .scalar = intAbiSize(16, target) }, + .u29 => return AbiSizeAdvanced{ .scalar = intAbiSize(29, target) }, .i32, .u32 => return AbiSizeAdvanced{ .scalar = intAbiSize(32, target) }, .i64, .u64 => return AbiSizeAdvanced{ .scalar = intAbiSize(64, target) }, .u128, .i128 => return AbiSizeAdvanced{ .scalar = intAbiSize(128, target) }, @@ -3569,6 +3580,7 @@ pub const Type = extern union { .bool, .u1 => 1, .u8, .i8 => 8, .i16, .u16, .f16 => 16, + .u29 => 29, .i32, .u32, .f32 => 32, .i64, .u64, .f64 => 64, .f80 => 80, @@ -4524,6 +4536,7 @@ pub const Type = extern union { .u1, .u8, .u16, + .u29, .u32, .u64, .u128, @@ -4550,6 +4563,7 @@ pub const Type = extern union { .i8 => return .{ .signedness = .signed, .bits = 8 }, .u16 => return .{ .signedness = .unsigned, .bits = 16 }, .i16 => return .{ .signedness = .signed, .bits = 16 }, + .u29 => return .{ .signedness = .unsigned, .bits = 29 }, .u32 => return .{ .signedness = .unsigned, .bits = 32 }, .i32 => return .{ .signedness = .signed, .bits = 32 }, .u64 => return .{ .signedness = .unsigned, .bits = 64 }, @@ -4814,6 +4828,7 @@ pub const Type = extern union { .i8, .u16, .i16, + .u29, .u32, .i32, .u64, @@ -4856,6 +4871,7 @@ pub const Type = extern union { .i8, .u16, .i16, + .u29, .u32, .i32, .u64, @@ -5072,6 +5088,7 @@ pub const Type = extern union { .i8, .u16, .i16, + .u29, .u32, .i32, .u64, @@ -5816,6 +5833,7 @@ pub const Type = extern union { i8, u16, i16, + u29, u32, i32, u64, @@ -5939,6 +5957,7 @@ pub const Type = extern union { .i8, .u16, .i16, + .u29, .u32, .i32, .u64, @@ -6302,6 +6321,7 @@ pub const Type = extern union { pub const @"u1" = initTag(.u1); pub const @"u8" = initTag(.u8); pub const @"u16" = initTag(.u16); + pub const @"u29" = initTag(.u29); pub const @"u32" = initTag(.u32); pub const @"u64" = initTag(.u64); diff --git a/src/value.zig b/src/value.zig index 37caeebd78..21fe52e706 100644 --- a/src/value.zig +++ b/src/value.zig @@ -30,6 +30,7 @@ pub const Value = extern union { i8_type, u16_type, i16_type, + u29_type, u32_type, i32_type, u64_type, @@ -196,6 +197,7 @@ pub const Value = extern union { .i8_type, .u16_type, .i16_type, + .u29_type, .u32_type, .i32_type, .u64_type, @@ -397,6 +399,7 @@ pub const Value = extern union { .i8_type, .u16_type, .i16_type, + .u29_type, .u32_type, .i32_type, .u64_type, @@ -660,6 +663,7 @@ pub const Value = extern union { .u8_type => return out_stream.writeAll("u8"), .i8_type => return out_stream.writeAll("i8"), .u16_type => return out_stream.writeAll("u16"), + .u29_type => return out_stream.writeAll("u29"), .i16_type => return out_stream.writeAll("i16"), .u32_type => return out_stream.writeAll("u32"), .i32_type => return out_stream.writeAll("i32"), @@ -900,6 +904,7 @@ pub const Value = extern union { .i8_type => Type.initTag(.i8), .u16_type => Type.initTag(.u16), .i16_type => Type.initTag(.i16), + .u29_type => Type.initTag(.u29), .u32_type => Type.initTag(.u32), .i32_type => Type.initTag(.i32), .u64_type => Type.initTag(.u64), @@ -4905,7 +4910,7 @@ pub const Value = extern union { /// `Module.resolvePeerTypes`. stored_inst_list: std.ArrayListUnmanaged(Air.Inst.Ref) = .{}, /// 0 means ABI-aligned. - alignment: u16, + alignment: u32, }, }; @@ -4916,7 +4921,7 @@ pub const Value = extern union { data: struct { decl_index: Module.Decl.Index, /// 0 means ABI-aligned. - alignment: u16, + alignment: u32, }, }; From d1e8b73939ae9f139d90157a4623c5fe68b0ae64 Mon Sep 17 00:00:00 2001 From: Jan Philipp Hafer Date: Mon, 2 May 2022 00:27:02 +0200 Subject: [PATCH 1770/2031] std.os.abort: ported signal handling from musl * Document deviation from Linux man page, which is identical to musl. Man page wants always enabled user-provided abort handlers. Worst case logic bug, which this can introduce: + user disables SIGABRT handler to prevent tear down to last safe state + abort() gets called and enables user-provided SIGABRT handler + SIGABRT tears down to supposed last safe state instead of crash + Application, instead of crashing, continues * Pid 1 within containers needs special handling. - fatal signals are not transmitted without privileges, so use exit as fallback * Fix some signaling bits * Add checks in Debug and ReleaseSafe for wrong sigprocmask --- lib/std/os.zig | 64 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 18513f2eca..d32618a82f 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -250,6 +250,15 @@ pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (builtin. else => undefined, }; +/// Atomic to guard correct program teardown in abort() +var abort_entered = impl: { + if (builtin.single_threaded) { + break :impl {}; + } else { + break :impl std.atomic.Atomic(bool).init(false); + } +}; + /// To obtain errno, call this function with the return value of the /// system function call. For some systems this will obtain the value directly /// from the return code; for others it will use a thread-local errno variable. @@ -442,6 +451,7 @@ fn getRandomBytesDevURandom(buf: []u8) !void { /// Causes abnormal process termination. /// If linking against libc, this calls the abort() libc function. Otherwise /// it raises SIGABRT followed by SIGKILL and finally lo +/// assume: Current signal handler for SIGABRT does **not call abort**. pub fn abort() noreturn { @setCold(true); // MSVCRT abort() sometimes opens a popup window which is undesirable, so @@ -454,11 +464,48 @@ pub fn abort() noreturn { windows.kernel32.ExitProcess(3); } if (!builtin.link_libc and builtin.os.tag == .linux) { + // Linux man page wants to first "unblock SIG.ABRT", but this is a footgun + // for user-defined signal handlers that want to restore some state in + // some program sections and crash in others + + // user installed SIGABRT handler is run, if installed raise(SIG.ABRT) catch {}; - // TODO the rest of the implementation of abort() from musl libc here + // disable all signal handlers + sigprocmask(SIG.BLOCK, &linux.all_mask, null); + // ensure teardown by one thread + if (!builtin.single_threaded) { + while (abort_entered.compareAndSwap(false, true, .SeqCst, .SeqCst)) |_| {} + } + + // install default handler to terminate + const sigact = Sigaction{ + .handler = .{ .sigaction = SIG.DFL }, + .mask = undefined, + .flags = undefined, + .restorer = undefined, + }; + sigaction(SIG.ABRT, &sigact, null) catch unreachable; + + // make sure we have a pending SIGABRT queued + const tid = std.Thread.getCurrentId(); + _ = linux.tkill(@intCast(i32, tid), SIG.ABRT); + + // SIG.ABRT signal will run default handler + const sigabrtmask: linux.sigset_t = [_]u32{0} ** 31 ++ [_]u32{1 << (SIG.ABRT - 1)}; + sigprocmask(SIG.UNBLOCK, &sigabrtmask, null); // [32]u32 + + // Beyond this point should be unreachable + + // abnormal termination without using signal handler + const nullptr: *allowzero volatile u8 = @intToPtr(*allowzero volatile u8, 0); + nullptr.* = 0; + + // try SIGKILL, which is no abnormal termination as defined by POSIX and ISO C raise(SIG.KILL) catch {}; + + // pid 1 might not be signalled in some containers exit(127); } if (builtin.os.tag == .uefi) { @@ -485,13 +532,13 @@ pub fn raise(sig: u8) RaiseError!void { if (builtin.os.tag == .linux) { var set: sigset_t = undefined; // block application signals - _ = linux.sigprocmask(SIG.BLOCK, &linux.app_mask, &set); + sigprocmask(SIG.BLOCK, &linux.app_mask, &set); const tid = linux.gettid(); const rc = linux.tkill(tid, sig); // restore signal mask - _ = linux.sigprocmask(SIG.SETMASK, &set, null); + sigprocmask(SIG.SETMASK, &set, null); switch (errno(rc)) { .SUCCESS => return, @@ -5441,6 +5488,17 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact } } +/// Set the thread signal mask +/// Invalid masks are checked in Debug and ReleaseFast +pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?*sigset_t) void { + switch (errno(system.sigprocmask(flags, set, oldset))) { + .SUCCESS => return, + .FAULT => unreachable, + .INVAL => unreachable, // main purpose: debug InvalidValue error + else => unreachable, + } +} + pub const FutimensError = error{ /// times is NULL, or both tv_nsec values are UTIME_NOW, and either: /// * the effective user ID of the caller does not match the owner From 073762395ef3beddf458d87a6b7aad4342b9af8e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 6 Jun 2022 15:29:34 -0700 Subject: [PATCH 1771/2031] std.os.abort patch cleanups * move global into function scope * clarify comments * avoid unnecessary usage of std.atomic API * switch on error instead of `catch unreachable` * call linux.gettid() instead of going through higher level API and doing unnecessary casting --- lib/std/os.zig | 59 +++++++++++++++++++------------------------------- 1 file changed, 22 insertions(+), 37 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index d32618a82f..c79bd6e7b0 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -250,15 +250,6 @@ pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (builtin. else => undefined, }; -/// Atomic to guard correct program teardown in abort() -var abort_entered = impl: { - if (builtin.single_threaded) { - break :impl {}; - } else { - break :impl std.atomic.Atomic(bool).init(false); - } -}; - /// To obtain errno, call this function with the return value of the /// system function call. For some systems this will obtain the value directly /// from the return code; for others it will use a thread-local errno variable. @@ -451,7 +442,7 @@ fn getRandomBytesDevURandom(buf: []u8) !void { /// Causes abnormal process termination. /// If linking against libc, this calls the abort() libc function. Otherwise /// it raises SIGABRT followed by SIGKILL and finally lo -/// assume: Current signal handler for SIGABRT does **not call abort**. +/// Invokes the current signal handler for SIGABRT, if any. pub fn abort() noreturn { @setCold(true); // MSVCRT abort() sometimes opens a popup window which is undesirable, so @@ -464,49 +455,44 @@ pub fn abort() noreturn { windows.kernel32.ExitProcess(3); } if (!builtin.link_libc and builtin.os.tag == .linux) { - // Linux man page wants to first "unblock SIG.ABRT", but this is a footgun + // The Linux man page says that the libc abort() function + // "first unblocks the SIGABRT signal", but this is a footgun // for user-defined signal handlers that want to restore some state in - // some program sections and crash in others - - // user installed SIGABRT handler is run, if installed + // some program sections and crash in others. + // So, the user-installed SIGABRT handler is run, if present. raise(SIG.ABRT) catch {}; - // disable all signal handlers + // Disable all signal handlers. sigprocmask(SIG.BLOCK, &linux.all_mask, null); - // ensure teardown by one thread + // Only one thread may proceed to the rest of abort(). if (!builtin.single_threaded) { - while (abort_entered.compareAndSwap(false, true, .SeqCst, .SeqCst)) |_| {} + const global = struct { + var abort_entered: bool = false; + }; + while (@cmpxchgWeak(bool, &global.abort_entered, false, true, .SeqCst, .SeqCst)) |_| {} } - // install default handler to terminate + // Install default handler so that the tkill below will terminate. const sigact = Sigaction{ .handler = .{ .sigaction = SIG.DFL }, .mask = undefined, .flags = undefined, .restorer = undefined, }; - sigaction(SIG.ABRT, &sigact, null) catch unreachable; + sigaction(SIG.ABRT, &sigact, null) catch |err| switch (err) { + error.OperationNotSupported => unreachable, + }; - // make sure we have a pending SIGABRT queued - const tid = std.Thread.getCurrentId(); - _ = linux.tkill(@intCast(i32, tid), SIG.ABRT); + _ = linux.tkill(linux.gettid(), SIG.ABRT); - // SIG.ABRT signal will run default handler const sigabrtmask: linux.sigset_t = [_]u32{0} ** 31 ++ [_]u32{1 << (SIG.ABRT - 1)}; - sigprocmask(SIG.UNBLOCK, &sigabrtmask, null); // [32]u32 + sigprocmask(SIG.UNBLOCK, &sigabrtmask, null); - // Beyond this point should be unreachable - - // abnormal termination without using signal handler - const nullptr: *allowzero volatile u8 = @intToPtr(*allowzero volatile u8, 0); - nullptr.* = 0; - - // try SIGKILL, which is no abnormal termination as defined by POSIX and ISO C + // Beyond this point should be unreachable. + @intToPtr(*allowzero volatile u8, 0).* = 0; raise(SIG.KILL) catch {}; - - // pid 1 might not be signalled in some containers - exit(127); + exit(127); // Pid 1 might not be signalled in some containers. } if (builtin.os.tag == .uefi) { exit(0); // TODO choose appropriate exit code @@ -5488,13 +5474,12 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact } } -/// Set the thread signal mask -/// Invalid masks are checked in Debug and ReleaseFast +/// Sets the thread signal mask. pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?*sigset_t) void { switch (errno(system.sigprocmask(flags, set, oldset))) { .SUCCESS => return, .FAULT => unreachable, - .INVAL => unreachable, // main purpose: debug InvalidValue error + .INVAL => unreachable, else => unreachable, } } From e9fc58eab77d60dfb02155ff17178b496d75d035 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 6 Jun 2022 18:21:24 -0700 Subject: [PATCH 1772/2031] LLVM: handle extern function name collisions Zig allows multiple extern functions with the same name, and the backends have to handle this possibility. For LLVM, we keep a sparse map of collisions, and then resolve them in flushModule(). This introduces some technical debt that will have to be resolved when adding incremental compilation support to the LLVM backend. --- src/codegen/llvm.zig | 58 ++++++++++++++++++++++++++++++-------- test/behavior/bugs/529.zig | 8 +++++- 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 9e9dd2cb0c..f7a1d732b4 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -214,6 +214,10 @@ pub const Object = struct { /// Note that the values are not added until flushModule, when all errors in /// the compilation are known. error_name_table: ?*const llvm.Value, + /// This map is usually very close to empty. It tracks only the cases when a + /// second extern Decl could not be emitted with the correct name due to a + /// name collision. + extern_collisions: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, void), pub const TypeMap = std.HashMapUnmanaged( Type, @@ -376,6 +380,7 @@ pub const Object = struct { .type_map_arena = std.heap.ArenaAllocator.init(gpa), .di_type_map = .{}, .error_name_table = null, + .extern_collisions = .{}, }; } @@ -392,6 +397,7 @@ pub const Object = struct { self.decl_map.deinit(gpa); self.type_map.deinit(gpa); self.type_map_arena.deinit(); + self.extern_collisions.deinit(gpa); self.* = undefined; } @@ -508,6 +514,22 @@ pub const Object = struct { fn resolveExportExternCollisions(object: *Object) !void { const mod = object.module; + // This map has externs with incorrect symbol names. + for (object.extern_collisions.keys()) |decl_index| { + const entry = object.decl_map.getEntry(decl_index) orelse continue; + const llvm_global = entry.value_ptr.*; + // Same logic as below but for externs instead of exports. + const decl = mod.declPtr(decl_index); + const other_global = object.getLlvmGlobal(decl.name) orelse continue; + if (other_global == llvm_global) continue; + + const new_global_ptr = other_global.constBitCast(llvm_global.typeOf()); + llvm_global.replaceAllUsesWith(new_global_ptr); + object.deleteLlvmGlobal(llvm_global); + entry.value_ptr.* = new_global_ptr; + } + object.extern_collisions.clearRetainingCapacity(); + const export_keys = mod.decl_exports.keys(); for (mod.decl_exports.values()) |export_list, i| { const decl_index = export_keys[i]; @@ -997,6 +1019,15 @@ pub const Object = struct { return null; } + /// TODO can this be done with simpler logic / different API binding? + fn deleteLlvmGlobal(o: Object, llvm_global: *const llvm.Value) void { + if (o.llvm_module.getNamedFunction(llvm_global.getValueName()) != null) { + llvm_global.deleteFunction(); + return; + } + return llvm_global.deleteGlobal(); + } + pub fn updateDeclExports( self: *Object, module: *Module, @@ -1009,6 +1040,12 @@ pub const Object = struct { const decl = module.declPtr(decl_index); if (decl.isExtern()) { llvm_global.setValueName(decl.name); + if (self.getLlvmGlobal(decl.name)) |other_global| { + if (other_global != llvm_global) { + log.debug("updateDeclExports isExtern()=true setValueName({s}) conflict", .{decl.name}); + try self.extern_collisions.put(module.gpa, decl_index, {}); + } + } llvm_global.setUnnamedAddr(.False); llvm_global.setLinkage(.External); if (self.di_map.get(decl)) |di_node| { @@ -2143,11 +2180,8 @@ pub const DeclGen = struct { log.debug("gen: {s} type: {}, value: {}", .{ decl.name, decl.ty.fmtDebug(), decl.val.fmtDebug(), }); - - if (decl.val.castTag(.function)) |func_payload| { - _ = func_payload; - @panic("TODO llvm backend genDecl function pointer"); - } else if (decl.val.castTag(.extern_fn)) |extern_fn| { + assert(decl.val.tag() != .function); + if (decl.val.castTag(.extern_fn)) |extern_fn| { _ = try dg.resolveLlvmFunction(extern_fn.data.owner_decl); } else { const target = dg.module.getTarget(); @@ -2246,12 +2280,14 @@ pub const DeclGen = struct { if (!is_extern) { llvm_fn.setLinkage(.Internal); llvm_fn.setUnnamedAddr(.True); - } else if (dg.module.getTarget().isWasm()) { - dg.addFnAttrString(llvm_fn, "wasm-import-name", std.mem.sliceTo(decl.name, 0)); - if (decl.getExternFn().?.lib_name) |lib_name| { - const module_name = std.mem.sliceTo(lib_name, 0); - if (!std.mem.eql(u8, module_name, "c")) { - dg.addFnAttrString(llvm_fn, "wasm-import-module", module_name); + } else { + if (dg.module.getTarget().isWasm()) { + dg.addFnAttrString(llvm_fn, "wasm-import-name", std.mem.sliceTo(decl.name, 0)); + if (decl.getExternFn().?.lib_name) |lib_name| { + const module_name = std.mem.sliceTo(lib_name, 0); + if (!std.mem.eql(u8, module_name, "c")) { + dg.addFnAttrString(llvm_fn, "wasm-import-module", module_name); + } } } } diff --git a/test/behavior/bugs/529.zig b/test/behavior/bugs/529.zig index e23321cd5c..b111cea564 100644 --- a/test/behavior/bugs/529.zig +++ b/test/behavior/bugs/529.zig @@ -8,8 +8,14 @@ comptime { _ = @import("529_other_file_2.zig"); } +const builtin = @import("builtin"); + test "issue 529 fixed" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @import("529_other_file.zig").issue529(null); issue529(null); From 117f9f69e79a628347c9fdb22e7ee8618de143c5 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Jun 2022 11:16:08 +0200 Subject: [PATCH 1773/2031] x64: simplify saving registers to stack in prologue --- src/arch/x86_64/CodeGen.zig | 144 ++++++++++++++++++++---------------- src/arch/x86_64/Emit.zig | 45 ++++++----- src/arch/x86_64/Mir.zig | 67 +++++++++++++---- src/register_manager.zig | 3 +- 4 files changed, 154 insertions(+), 105 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 6211a8cae8..0d4aae2688 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -409,13 +409,11 @@ fn gen(self: *Self) InnerError!void { // The address where to store the return value for the caller is in `.rdi` // register which the callee is free to clobber. Therefore, we purposely // spill it to stack immediately. - const ptr_ty = Type.usize; - const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*)); - const abi_align = ptr_ty.abiAlignment(self.target.*); - const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset + abi_size, abi_align); + const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset + 8, 8); self.next_stack_offset = stack_offset; self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset); - try self.genSetStack(ptr_ty, @intCast(i32, stack_offset), MCValue{ .register = .rdi }, .{}); + + try self.genSetStack(Type.usize, @intCast(i32, stack_offset), MCValue{ .register = .rdi }, .{}); self.ret_mcv = MCValue{ .stack_offset = @intCast(i32, stack_offset) }; log.debug("gen: spilling .rdi to stack at offset {}", .{stack_offset}); } @@ -426,11 +424,11 @@ fn gen(self: *Self) InnerError!void { .data = undefined, }); - // push the callee_preserved_regs that were used - const backpatch_push_callee_preserved_regs_i = try self.addInst(.{ - .tag = .push_regs_from_callee_preserved_regs, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), - .data = .{ .payload = undefined }, // to be backpatched + // Push callee-preserved regs that were used actually in use. + const backpatch_push_callee_preserved_regs = try self.addInst(.{ + .tag = .nop, + .ops = undefined, + .data = undefined, }); try self.genBody(self.air.getMainBody()); @@ -446,31 +444,21 @@ fn gen(self: *Self) InnerError!void { self.mir_instructions.items(.data)[jmp_reloc].inst = @intCast(u32, self.mir_instructions.len); } - // calculate the data for callee_preserved_regs to be pushed and popped - const callee_preserved_regs_payload = blk: { - var data = Mir.RegsToPushOrPop{ - .regs = 0, - .disp = mem.alignForwardGeneric(u32, self.next_stack_offset, 8), - }; - var disp = data.disp + 8; - inline for (callee_preserved_regs) |reg, i| { - if (self.register_manager.isRegAllocated(reg)) { - data.regs |= 1 << @intCast(u5, i); - self.max_end_stack += 8; - disp += 8; - } + // Create list of registers to save in the prologue. + // TODO handle register classes + var reg_list: Mir.RegisterList(Register, &callee_preserved_regs) = .{}; + inline for (callee_preserved_regs) |reg| { + if (self.register_manager.isRegAllocated(reg)) { + reg_list.push(reg); } - break :blk try self.addExtra(data); - }; + } + const saved_regs_stack_space: u32 = reg_list.count() * 8; - const data = self.mir_instructions.items(.data); - // backpatch the push instruction - data[backpatch_push_callee_preserved_regs_i].payload = callee_preserved_regs_payload; - // pop the callee_preserved_regs - _ = try self.addInst(.{ - .tag = .pop_regs_from_callee_preserved_regs, - .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), - .data = .{ .payload = callee_preserved_regs_payload }, + // Pop saved callee-preserved regs. + const backpatch_pop_callee_preserved_regs = try self.addInst(.{ + .tag = .nop, + .ops = undefined, + .data = undefined, }); _ = try self.addInst(.{ @@ -502,9 +490,11 @@ fn gen(self: *Self) InnerError!void { if (self.max_end_stack > math.maxInt(i32)) { return self.failSymbol("too much stack used in call parameters", .{}); } - // TODO we should reuse this mechanism to align the stack when calling any function even if - // we do not pass any args on the stack BUT we still push regs to stack with `push` inst. - const aligned_stack_end = @intCast(u32, mem.alignForward(self.max_end_stack, self.stack_align)); + + const aligned_stack_end = @intCast( + u32, + mem.alignForward(self.max_end_stack + saved_regs_stack_space, self.stack_align), + ); if (aligned_stack_end > 0) { self.mir_instructions.set(backpatch_stack_sub, .{ .tag = .sub, @@ -516,6 +506,21 @@ fn gen(self: *Self) InnerError!void { .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rsp }), .data = .{ .imm = aligned_stack_end }, }); + + const save_reg_list = try self.addExtra(Mir.SaveRegisterList{ + .register_list = reg_list.asInt(), + .stack_end = aligned_stack_end, + }); + self.mir_instructions.set(backpatch_push_callee_preserved_regs, .{ + .tag = .push_regs, + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), + .data = .{ .payload = save_reg_list }, + }); + self.mir_instructions.set(backpatch_pop_callee_preserved_regs, .{ + .tag = .pop_regs, + .ops = Mir.Inst.Ops.encode(.{ .reg1 = .rbp }), + .data = .{ .payload = save_reg_list }, + }); } } else { _ = try self.addInst(.{ @@ -907,6 +912,39 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { return MCValue{ .stack_offset = @intCast(i32, stack_offset) }; } +const State = struct { + next_stack_offset: u32, + registers: abi.RegisterManager.TrackedRegisters, + free_registers: abi.RegisterManager.RegisterBitSet, + eflags_inst: ?Air.Inst.Index, + stack: std.AutoHashMapUnmanaged(u32, StackAllocation), + + fn deinit(state: *State, gpa: Allocator) void { + state.stack.deinit(gpa); + } +}; + +fn captureState(self: *Self) !State { + return State{ + .next_stack_offset = self.next_stack_offset, + .registers = self.register_manager.registers, + .free_registers = self.register_manager.free_registers, + .eflags_inst = self.eflags_inst, + .stack = try self.stack.clone(self.gpa), + }; +} + +fn revertState(self: *Self, state: State) void { + self.register_manager.registers = state.registers; + self.eflags_inst = state.eflags_inst; + + self.stack.deinit(self.gpa); + self.stack = state.stack; + + self.next_stack_offset = state.next_stack_offset; + self.register_manager.free_registers = state.free_registers; +} + pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { const stack_mcv = try self.allocRegOrMem(inst, false); log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); @@ -4503,12 +4541,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { } // Capture the state of register and stack allocation state so that we can revert to it. - const parent_next_stack_offset = self.next_stack_offset; - const parent_free_registers = self.register_manager.free_registers; - const parent_eflags_inst = self.eflags_inst; - var parent_stack = try self.stack.clone(self.gpa); - defer parent_stack.deinit(self.gpa); - const parent_registers = self.register_manager.registers; + const saved_state = try self.captureState(); try self.branch_stack.append(.{}); errdefer { @@ -4526,17 +4559,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { var saved_then_branch = self.branch_stack.pop(); defer saved_then_branch.deinit(self.gpa); - self.register_manager.registers = parent_registers; - self.eflags_inst = parent_eflags_inst; - - self.stack.deinit(self.gpa); - self.stack = parent_stack; - parent_stack = .{}; - - self.next_stack_offset = parent_next_stack_offset; - self.register_manager.free_registers = parent_free_registers; + self.revertState(saved_state); try self.performReloc(reloc); + const else_branch = self.branch_stack.addOneAssumeCapacity(); else_branch.* = .{}; @@ -5021,12 +5047,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { } // Capture the state of register and stack allocation state so that we can revert to it. - const parent_next_stack_offset = self.next_stack_offset; - const parent_free_registers = self.register_manager.free_registers; - const parent_eflags_inst = self.eflags_inst; - var parent_stack = try self.stack.clone(self.gpa); - defer parent_stack.deinit(self.gpa); - const parent_registers = self.register_manager.registers; + const saved_state = try self.captureState(); try self.branch_stack.append(.{}); errdefer { @@ -5044,14 +5065,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { var saved_case_branch = self.branch_stack.pop(); defer saved_case_branch.deinit(self.gpa); - self.register_manager.registers = parent_registers; - self.eflags_inst = parent_eflags_inst; - self.stack.deinit(self.gpa); - self.stack = parent_stack; - parent_stack = .{}; - - self.next_stack_offset = parent_next_stack_offset; - self.register_manager.free_registers = parent_free_registers; + self.revertState(saved_state); for (relocs) |reloc| { try self.performReloc(reloc); diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 654775cc21..cc5b54fb55 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -169,7 +169,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .@"test" => try emit.mirTest(inst), .interrupt => try emit.mirInterrupt(inst), - .nop => try emit.mirNop(), + .nop => {}, // just skip it // SSE instructions .mov_f64_sse => try emit.mirMovFloatSse(.movsd, inst), @@ -198,8 +198,8 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .dbg_prologue_end => try emit.mirDbgPrologueEnd(inst), .dbg_epilogue_begin => try emit.mirDbgEpilogueBegin(inst), - .push_regs_from_callee_preserved_regs => try emit.mirPushPopRegsFromCalleePreservedRegs(.push, inst), - .pop_regs_from_callee_preserved_regs => try emit.mirPushPopRegsFromCalleePreservedRegs(.pop, inst), + .push_regs => try emit.mirPushPopRegisterList(.push, inst), + .pop_regs => try emit.mirPushPopRegisterList(.pop, inst), else => { return emit.fail("Implement MIR->Emit lowering for x86_64 for pseudo-inst: {s}", .{tag}); @@ -246,10 +246,6 @@ fn mirInterrupt(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } } -fn mirNop(emit: *Emit) InnerError!void { - return lowerToZoEnc(.nop, emit.code); -} - fn mirSyscall(emit: *Emit) InnerError!void { return lowerToZoEnc(.syscall, emit.code); } @@ -283,26 +279,27 @@ fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { } } -fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { +fn mirPushPopRegisterList(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { const ops = emit.mir.instructions.items(.ops)[inst].decode(); const payload = emit.mir.instructions.items(.data)[inst].payload; - const data = emit.mir.extraData(Mir.RegsToPushOrPop, payload).data; - const regs = data.regs; - var disp: u32 = data.disp + 8; - for (abi.callee_preserved_regs) |reg, i| { - if ((regs >> @intCast(u5, i)) & 1 == 0) continue; - if (tag == .push) { - try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ - .disp = @bitCast(u32, -@intCast(i32, disp)), - .base = ops.reg1, - }), reg.to64(), emit.code); - } else { - try lowerToRmEnc(.mov, reg.to64(), RegisterOrMemory.mem(.qword_ptr, .{ - .disp = @bitCast(u32, -@intCast(i32, disp)), - .base = ops.reg1, - }), emit.code); + const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data; + const reg_list = Mir.RegisterList(Register, &abi.callee_preserved_regs).fromInt(save_reg_list.register_list); + var disp: i32 = -@intCast(i32, save_reg_list.stack_end); + inline for (abi.callee_preserved_regs) |reg| { + if (reg_list.isSet(reg)) { + switch (tag) { + .push => try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ + .disp = @bitCast(u32, disp), + .base = ops.reg1, + }), reg, emit.code), + .pop => try lowerToRmEnc(.mov, reg, RegisterOrMemory.mem(.qword_ptr, .{ + .disp = @bitCast(u32, disp), + .base = ops.reg1, + }), emit.code), + else => unreachable, + } + disp += 8; } - disp += 8; } } diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 0918d67f3c..74b0ca0d12 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -14,6 +14,7 @@ const assert = std.debug.assert; const bits = @import("bits.zig"); const Air = @import("../../Air.zig"); const CodeGen = @import("CodeGen.zig"); +const IntegerBitSet = std.bit_set.IntegerBitSet; const Register = bits.Register; instructions: std.MultiArrayList(Inst).Slice, @@ -379,19 +380,13 @@ pub const Inst = struct { /// update debug line dbg_line, - /// push registers from the callee_preserved_regs - /// data is the bitfield of which regs to push - /// for example on x86_64, the callee_preserved_regs are [_]Register{ .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; }; - /// so to push rcx and r8 one would make data 0b00000000_00000000_00000000_00001001 (the first and fourth bits are set) - /// ops is unused - push_regs_from_callee_preserved_regs, + /// push registers + /// Uses `payload` field with `SaveRegisterList` as payload. + push_regs, - /// pop registers from the callee_preserved_regs - /// data is the bitfield of which regs to pop - /// for example on x86_64, the callee_preserved_regs are [_]Register{ .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; }; - /// so to pop rcx and r8 one would make data 0b00000000_00000000_00000000_00001001 (the first and fourth bits are set) - /// ops is unused - pop_regs_from_callee_preserved_regs, + /// pop registers + /// Uses `payload` field with `SaveRegisterList` as payload. + pop_regs, }; /// The position of an MIR instruction within the `Mir` instructions array. pub const Index = u32; @@ -471,9 +466,51 @@ pub const Inst = struct { } }; -pub const RegsToPushOrPop = struct { - regs: u32, - disp: u32, +pub fn RegisterList(comptime Reg: type, comptime registers: []const Reg) type { + assert(registers.len <= @bitSizeOf(u32)); + return struct { + bitset: RegBitSet = RegBitSet.initEmpty(), + + const RegBitSet = IntegerBitSet(registers.len); + const Self = @This(); + + fn getIndexForReg(reg: Reg) RegBitSet.MaskInt { + inline for (registers) |cpreg, i| { + if (reg.id() == cpreg.id()) return i; + } + unreachable; // register not in input register list! + } + + pub fn push(self: *Self, reg: Reg) void { + const index = getIndexForReg(reg); + self.bitset.set(index); + } + + pub fn isSet(self: Self, reg: Reg) bool { + const index = getIndexForReg(reg); + return self.bitset.isSet(index); + } + + pub fn asInt(self: Self) u32 { + return self.bitset.mask; + } + + pub fn fromInt(mask: u32) Self { + return .{ + .bitset = RegBitSet{ .mask = @intCast(RegBitSet.MaskInt, mask) }, + }; + } + + pub fn count(self: Self) u32 { + return @intCast(u32, self.bitset.count()); + } + }; +} + +pub const SaveRegisterList = struct { + /// Use `RegisterList` to populate. + register_list: u32, + stack_end: u32, }; pub const ImmPair = struct { diff --git a/src/register_manager.zig b/src/register_manager.zig index 64f6609a9b..0bd9ef62e9 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -39,7 +39,7 @@ pub fn RegisterManager( /// register is free), the value in that slot is undefined. /// /// The key must be canonical register. - registers: [tracked_registers.len]Air.Inst.Index = undefined, + registers: TrackedRegisters = undefined, /// Tracks which registers are free (in which case the /// corresponding bit is set to 1) free_registers: RegisterBitSet = RegisterBitSet.initFull(), @@ -51,6 +51,7 @@ pub fn RegisterManager( const Self = @This(); + pub const TrackedRegisters = [tracked_registers.len]Air.Inst.Index; pub const RegisterBitSet = StaticBitSet(tracked_registers.len); fn getFunction(self: *Self) *Function { From fc015231ad4e2e449e848fd08dd003efd049ad43 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Jun 2022 18:04:58 +0200 Subject: [PATCH 1774/2031] x64: account for non-pow-two stores via register deref In this case, we need to proceed rather carefully to avoid writing containing register width rather than the precise amount of bytes. --- src/arch/x86_64/CodeGen.zig | 49 ++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 0d4aae2688..be41f99b56 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2732,15 +2732,46 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type } }, .register => |src_reg| { - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to64(), - .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)), - .flags = 0b10, - }), - .data = .{ .imm = 0 }, - }); + const src_reg_lock = self.register_manager.lockReg(src_reg); + defer if (src_reg_lock) |lock| self.register_manager.unlockReg(lock); + + // TODO common code-path with genSetStack, refactor! + if (!math.isPowerOfTwo(abi_size)) { + const tmp_reg = try self.copyToTmpRegister(value_ty, value); + + var next_offset: i32 = 0; + var remainder = abi_size; + while (remainder > 0) { + const nearest_power_of_two = @as(u6, 1) << math.log2_int(u3, @intCast(u3, remainder)); + + _ = try self.addInst(.{ + .tag = .mov, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = reg.to64(), + .reg2 = registerAlias(tmp_reg, nearest_power_of_two), + .flags = 0b10, + }), + .data = .{ .imm = @bitCast(u32, -next_offset) }, + }); + + if (nearest_power_of_two > 1) { + try self.genShiftBinOpMir(.shr, value_ty, tmp_reg, .{ .immediate = nearest_power_of_two * 8 }); + } + + remainder -= nearest_power_of_two; + next_offset -= nearest_power_of_two; + } + } else { + _ = try self.addInst(.{ + .tag = .mov, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = reg.to64(), + .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)), + .flags = 0b10, + }), + .data = .{ .imm = 0 }, + }); + } }, .got_load, .direct_load, From 0c727604540df7dbff4f57e4599fa01e01b36695 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Jun 2022 18:10:28 +0200 Subject: [PATCH 1775/2031] x64: optimise element offset calculation if dealing with immediates If `index` MCValue is actually an immediate, we can calculate offset directly at "comptime" rather than at runtime. --- src/arch/x86_64/CodeGen.zig | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index be41f99b56..5c81233845 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2100,8 +2100,22 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { } fn elemOffset(self: *Self, index_ty: Type, index: MCValue, elem_size: u64) !Register { - const reg = try self.copyToTmpRegister(index_ty, index); - try self.genIntMulComplexOpMir(index_ty, .{ .register = reg }, .{ .immediate = elem_size }); + const reg: Register = blk: { + switch (index) { + .immediate => |imm| { + // Optimisation: if index MCValue is an immediate, we can multiply in `comptime` + // and set the register directly to the scaled offset as an immediate. + const reg = try self.register_manager.allocReg(null, gp); + try self.genSetReg(index_ty, reg, .{ .immediate = imm * elem_size }); + break :blk reg; + }, + else => { + const reg = try self.copyToTmpRegister(index_ty, index); + try self.genIntMulComplexOpMir(index_ty, .{ .register = reg }, .{ .immediate = elem_size }); + break :blk reg; + }, + } + }; return reg; } From a8bce8f14b5a2a3a6b5e069f3b434fd9430d9a8e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Jun 2022 18:12:45 +0200 Subject: [PATCH 1776/2031] x64: pass behavior test bugs/1381 --- test/behavior/bugs/1381.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/test/behavior/bugs/1381.zig b/test/behavior/bugs/1381.zig index 242c852795..ae80132cc1 100644 --- a/test/behavior/bugs/1381.zig +++ b/test/behavior/bugs/1381.zig @@ -14,7 +14,6 @@ const A = union(enum) { test "union that needs padding bytes inside an array" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; var as = [_]A{ A{ .B = B{ .D = 1 } }, From 03068ce6a67d2cf83954606dc96329b85bd4be1a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Jun 2022 18:30:59 +0200 Subject: [PATCH 1777/2031] x64: clean up store helper --- src/arch/x86_64/CodeGen.zig | 51 +++++++------------------------------ 1 file changed, 9 insertions(+), 42 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5c81233845..839063e0e4 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2730,15 +2730,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type // movabs does not support indirect register addressing // so we need an extra register and an extra mov. const tmp_reg = try self.copyToTmpRegister(value_ty, value); - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to64(), - .reg2 = tmp_reg.to64(), - .flags = 0b10, - }), - .data = .{ .imm = 0 }, - }); + return self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); }, else => { return self.fail("TODO implement set pointee with immediate of ABI size {d}", .{abi_size}); @@ -2835,6 +2827,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .data = .{ .imm = 0 }, }); + const new_ptr = MCValue{ .register = addr_reg.to64() }; + switch (value) { .immediate => |imm| { if (abi_size > 8) { @@ -2873,16 +2867,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .data = .{ .payload = payload }, }); }, - .register => |reg| { - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = addr_reg.to64(), - .reg2 = reg, - .flags = 0b10, - }), - .data = .{ .imm = 0 }, - }); + .register => { + return self.store(new_ptr, value, ptr_ty, value_ty); }, .got_load, .direct_load, @@ -2904,37 +2890,18 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type }), .data = .{ .imm = 0 }, }); - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = addr_reg.to64(), - .reg2 = tmp_reg, - .flags = 0b10, - }), - .data = .{ .imm = 0 }, - }); - return; + return self.store(new_ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); } - try self.genInlineMemcpy(.{ .register = addr_reg.to64() }, value, .{ .immediate = abi_size }, .{}); + try self.genInlineMemcpy(new_ptr, value, .{ .immediate = abi_size }, .{}); }, .stack_offset => { if (abi_size <= 8) { - // TODO this should really be a recursive call const tmp_reg = try self.copyToTmpRegister(value_ty, value); - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = addr_reg.to64(), - .reg2 = tmp_reg, - .flags = 0b10, - }), - .data = .{ .imm = 0 }, - }); - return; + return self.store(new_ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); } - try self.genInlineMemcpy(.{ .register = addr_reg.to64() }, value, .{ .immediate = abi_size }, .{}); + try self.genInlineMemcpy(new_ptr, value, .{ .immediate = abi_size }, .{}); }, else => return self.fail("TODO implement storing {} to MCValue.memory", .{value}), } From 76ad7af4d87982ef62876df2a2df7bbb2ac312e8 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Jun 2022 19:10:26 +0200 Subject: [PATCH 1778/2031] x64: pull common codepath between store and genSetStack into a helper --- src/arch/x86_64/CodeGen.zig | 142 ++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 79 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 839063e0e4..f4444d56a6 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2738,46 +2738,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type } }, .register => |src_reg| { - const src_reg_lock = self.register_manager.lockReg(src_reg); - defer if (src_reg_lock) |lock| self.register_manager.unlockReg(lock); - - // TODO common code-path with genSetStack, refactor! - if (!math.isPowerOfTwo(abi_size)) { - const tmp_reg = try self.copyToTmpRegister(value_ty, value); - - var next_offset: i32 = 0; - var remainder = abi_size; - while (remainder > 0) { - const nearest_power_of_two = @as(u6, 1) << math.log2_int(u3, @intCast(u3, remainder)); - - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to64(), - .reg2 = registerAlias(tmp_reg, nearest_power_of_two), - .flags = 0b10, - }), - .data = .{ .imm = @bitCast(u32, -next_offset) }, - }); - - if (nearest_power_of_two > 1) { - try self.genShiftBinOpMir(.shr, value_ty, tmp_reg, .{ .immediate = nearest_power_of_two * 8 }); - } - - remainder -= nearest_power_of_two; - next_offset -= nearest_power_of_two; - } - } else { - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = reg.to64(), - .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)), - .flags = 0b10, - }), - .data = .{ .imm = 0 }, - }); - } + try self.genInlineMemcpyRegisterRegister(value_ty, reg, src_reg, 0); }, .got_load, .direct_load, @@ -5638,45 +5599,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl return self.fail("TODO genSetStack for register for type float with no intrinsics", .{}); }, else => { - if (!math.isPowerOfTwo(abi_size)) { - const reg_lock = self.register_manager.lockReg(reg); - defer if (reg_lock) |lock| self.register_manager.unlockReg(lock); - - const tmp_reg = try self.copyToTmpRegister(ty, mcv); - - var next_offset = stack_offset; - var remainder = abi_size; - while (remainder > 0) { - const nearest_power_of_two = @as(u6, 1) << math.log2_int(u3, @intCast(u3, remainder)); - - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = base_reg, - .reg2 = registerAlias(tmp_reg, nearest_power_of_two), - .flags = 0b10, - }), - .data = .{ .imm = @bitCast(u32, -next_offset) }, - }); - - if (nearest_power_of_two > 1) { - try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{ .immediate = nearest_power_of_two * 8 }); - } - - remainder -= nearest_power_of_two; - next_offset -= nearest_power_of_two; - } - } else { - _ = try self.addInst(.{ - .tag = .mov, - .ops = Mir.Inst.Ops.encode(.{ - .reg1 = base_reg, - .reg2 = registerAlias(reg, @intCast(u32, abi_size)), - .flags = 0b10, - }), - .data = .{ .imm = @bitCast(u32, -stack_offset) }, - }); - } + try self.genInlineMemcpyRegisterRegister(ty, base_reg, reg, stack_offset); }, } }, @@ -5711,6 +5634,67 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl } } +/// Like `genInlineMemcpy` but copies value from a register to an address via dereferencing +/// of destination register. +/// Boils down to MOV r/m64, r64. +fn genInlineMemcpyRegisterRegister( + self: *Self, + ty: Type, + dst_reg: Register, + src_reg: Register, + offset: i32, +) InnerError!void { + assert(dst_reg.size() == 64); + + const dst_reg_lock = self.register_manager.lockReg(dst_reg); + defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); + + const src_reg_lock = self.register_manager.lockReg(src_reg); + defer if (src_reg_lock) |lock| self.register_manager.unlockReg(lock); + + const abi_size = @intCast(u32, ty.abiSize(self.target.*)); + + // TODO common code-path with genSetStack, refactor! + if (!math.isPowerOfTwo(abi_size)) { + const tmp_reg = try self.copyToTmpRegister(ty, .{ .register = src_reg }); + + var next_offset = offset; + var remainder = abi_size; + while (remainder > 0) { + const nearest_power_of_two = @as(u6, 1) << math.log2_int(u3, @intCast(u3, remainder)); + + _ = try self.addInst(.{ + .tag = .mov, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = dst_reg, + .reg2 = registerAlias(tmp_reg, nearest_power_of_two), + .flags = 0b10, + }), + .data = .{ .imm = @bitCast(u32, -next_offset) }, + }); + + if (nearest_power_of_two > 1) { + try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{ + .immediate = nearest_power_of_two * 8, + }); + } + + remainder -= nearest_power_of_two; + next_offset -= nearest_power_of_two; + } + } else { + _ = try self.addInst(.{ + .tag = .mov, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = dst_reg, + .reg2 = registerAlias(src_reg, @intCast(u32, abi_size)), + .flags = 0b10, + }), + .data = .{ .imm = @bitCast(u32, -offset) }, + }); + } +} + const InlineMemcpyOpts = struct { source_stack_base: ?Register = null, dest_stack_base: ?Register = null, From d5e3d5d74cefd64287b92d148f78353cdb84e447 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 7 Jun 2022 16:19:21 +0300 Subject: [PATCH 1779/2031] Sema: make `analyzeIsNonErr` even lazier for inferred error sets --- src/Sema.zig | 22 ++++++++++++++++++++++ test/behavior/error.zig | 15 +++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index d36af5abf2..7bc011e226 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -21870,6 +21870,28 @@ fn analyzeIsNonErrComptimeOnly( if (ies.is_anyerror) break :blk; if (ies.errors.count() != 0) break :blk; if (maybe_operand_val == null) { + // Try to avoid resolving inferred error set if possible. + if (ies.errors.count() != 0) break :blk; + if (ies.is_anyerror) break :blk; + var it = ies.inferred_error_sets.keyIterator(); + while (it.next()) |other_error_set_ptr| { + const other_ies: *Module.Fn.InferredErrorSet = other_error_set_ptr.*; + if (ies == other_ies) continue; + try sema.resolveInferredErrorSet(block, src, other_ies); + if (other_ies.is_anyerror) { + ies.is_anyerror = true; + ies.is_resolved = true; + break :blk; + } + + if (other_ies.errors.count() != 0) break :blk; + } + if (ies.func == sema.owner_func) { + // We're checking the inferred errorset of the current function and none of + // its child inferred error sets contained any errors meaning that any value + // so far with this type can't contain errors either. + return Air.Inst.Ref.bool_true; + } try sema.resolveInferredErrorSet(block, src, ies); if (ies.is_anyerror) break :blk; if (ies.errors.count() == 0) return Air.Inst.Ref.bool_true; diff --git a/test/behavior/error.zig b/test/behavior/error.zig index b735d70d73..0fedd7dff0 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -754,3 +754,18 @@ test "error union payload is properly aligned" { const blk = S.foo() catch unreachable; if (blk.a != 1) unreachable; } + +test "ret_ptr doesn't cause own inferred error set to be resolved" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const S = struct { + fn foo() !void {} + + fn doTheTest() !void { + errdefer @compileError("bad"); + + return try @This().foo(); + } + }; + try S.doTheTest(); +} From e4c0b848a46347fccced787488605ac66e83c38a Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 7 Jun 2022 17:48:53 +0300 Subject: [PATCH 1780/2031] Sema: allow simple else body even when all errors handled --- src/Sema.zig | 30 ++++++++++++++++++++++++++++-- test/behavior/error.zig | 27 +++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 7bc011e226..550190654b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7774,7 +7774,12 @@ fn zirSwitchCapture( } switch (operand_ty.zigTypeTag()) { - .ErrorSet => return sema.bitCast(block, block.switch_else_err_ty.?, operand, operand_src), + .ErrorSet => if (block.switch_else_err_ty) |some| { + return sema.bitCast(block, some, operand, operand_src); + } else { + try block.addUnreachable(operand_src, false); + return Air.Inst.Ref.unreachable_value; + }, else => return operand, } } @@ -8194,7 +8199,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError ); } else_error_ty = Type.@"anyerror"; - } else { + } else else_validation: { var maybe_msg: ?*Module.ErrorMsg = null; errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa); @@ -8231,6 +8236,27 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError } if (special_prong == .@"else" and seen_errors.count() == operand_ty.errorSetNames().len) { + + // In order to enable common patterns for generic code allow simple else bodies + // else => unreachable, + // else => return, + // else => |e| return e, + // even if all the possible errors were already handled. + const tags = sema.code.instructions.items(.tag); + for (special.body) |else_inst| switch (tags[else_inst]) { + .dbg_block_begin, + .dbg_block_end, + .dbg_stmt, + .dbg_var_val, + .switch_capture, + .ret_type, + .as_node, + .ret_node, + .@"unreachable", + => {}, + else => break, + } else break :else_validation; + return sema.fail( block, special_prong_src, diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 0fedd7dff0..ac51ec1eae 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -769,3 +769,30 @@ test "ret_ptr doesn't cause own inferred error set to be resolved" { }; try S.doTheTest(); } + +test "simple else prong allowed even when all errors handled" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + const S = struct { + fn foo() !u8 { + return error.Foo; + } + }; + var value = S.foo() catch |err| switch (err) { + error.Foo => 255, + else => |e| return e, + }; + try expect(value == 255); + value = S.foo() catch |err| switch (err) { + error.Foo => 255, + else => unreachable, + }; + try expect(value == 255); + value = S.foo() catch |err| switch (err) { + error.Foo => 255, + else => return, + }; + try expect(value == 255); +} From fbd7e4506f46b73e351e1f3eb5e7cfc16ebbfc1f Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 7 Jun 2022 19:13:50 +0300 Subject: [PATCH 1781/2031] stage2: implement asm with multiple outputs --- src/AstGen.zig | 5 +++- src/Sema.zig | 54 ++++++++++++++++++++------------------------ src/codegen/llvm.zig | 21 ++++++++++------- 3 files changed, 41 insertions(+), 39 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 36944c1a8a..831071c479 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -6876,6 +6876,9 @@ fn asmExpr( const constraint = (try astgen.strLitAsString(constraint_token)).index; const has_arrow = token_tags[symbolic_name + 4] == .arrow; if (has_arrow) { + if (output_type_bits != 0) { + return astgen.failNode(output_node, "inline assembly allows up to one output value", .{}); + } output_type_bits |= @as(u32, 1) << @intCast(u5, i); const out_type_node = node_datas[output_node].lhs; const out_type_inst = try typeExpr(gz, scope, out_type_node); @@ -6892,7 +6895,7 @@ fn asmExpr( outputs[i] = .{ .name = name, .constraint = constraint, - .operand = try localVarRef(gz, scope, rl, node, ident_token), + .operand = try localVarRef(gz, scope, .ref, node, ident_token), }; } } diff --git a/src/Sema.zig b/src/Sema.zig index 550190654b..5159d6f5d3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -11334,43 +11334,40 @@ fn zirAsm( try sema.requireRuntimeBlock(block, src); } - if (outputs_len > 1) { - return sema.fail(block, src, "TODO implement Sema for asm with more than 1 output", .{}); - } - var extra_i = extra.end; var output_type_bits = extra.data.output_type_bits; var needed_capacity: usize = @typeInfo(Air.Asm).Struct.fields.len + outputs_len + inputs_len; - const Output = struct { - constraint: []const u8, - name: []const u8, - ty: Type, - }; - const output: ?Output = if (outputs_len == 0) null else blk: { + const ConstraintName = struct { c: []const u8, n: []const u8 }; + const out_args = try sema.arena.alloc(Air.Inst.Ref, outputs_len); + const outputs = try sema.arena.alloc(ConstraintName, outputs_len); + var expr_ty = Air.Inst.Ref.void_type; + + for (out_args) |*arg, out_i| { const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i); extra_i = output.end; const is_type = @truncate(u1, output_type_bits) != 0; output_type_bits >>= 1; - if (!is_type) { - return sema.fail(block, src, "TODO implement Sema for asm with non `->` output", .{}); + if (is_type) { + // Indicate the output is the asm instruction return value. + arg.* = .none; + const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand); + expr_ty = try sema.addType(out_ty); + } else { + arg.* = try sema.resolveInst(output.data.operand); } const constraint = sema.code.nullTerminatedString(output.data.constraint); const name = sema.code.nullTerminatedString(output.data.name); needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; - break :blk Output{ - .constraint = constraint, - .name = name, - .ty = try sema.resolveType(block, ret_ty_src, output.data.operand), - }; - }; + outputs[out_i] = .{ .c = constraint, .n = name }; + } const args = try sema.arena.alloc(Air.Inst.Ref, inputs_len); - const inputs = try sema.arena.alloc(struct { c: []const u8, n: []const u8 }, inputs_len); + const inputs = try sema.arena.alloc(ConstraintName, inputs_len); for (args) |*arg, arg_i| { const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i); @@ -11405,7 +11402,7 @@ fn zirAsm( const asm_air = try block.addInst(.{ .tag = .assembly, .data = .{ .ty_pl = .{ - .ty = if (output) |o| try sema.addType(o.ty) else Air.Inst.Ref.void_type, + .ty = expr_ty, .payload = sema.addExtraAssumeCapacity(Air.Asm{ .source_len = @intCast(u32, asm_source.len), .outputs_len = outputs_len, @@ -11414,18 +11411,15 @@ fn zirAsm( }), } }, }); - if (output != null) { - // Indicate the output is the asm instruction return value. - sema.air_extra.appendAssumeCapacity(@enumToInt(Air.Inst.Ref.none)); - } + sema.appendRefsAssumeCapacity(out_args); sema.appendRefsAssumeCapacity(args); - if (output) |o| { + for (outputs) |o| { const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); - mem.copy(u8, buffer, o.constraint); - buffer[o.constraint.len] = 0; - mem.copy(u8, buffer[o.constraint.len + 1 ..], o.name); - buffer[o.constraint.len + 1 + o.name.len] = 0; - sema.air_extra.items.len += (o.constraint.len + o.name.len + (2 + 3)) / 4; + mem.copy(u8, buffer, o.c); + buffer[o.c.len] = 0; + mem.copy(u8, buffer[o.c.len + 1 ..], o.n); + buffer[o.c.len + 1 + o.n.len] = 0; + sema.air_extra.items.len += (o.c.len + o.n.len + (2 + 3)) / 4; } for (inputs) |input| { const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index f7a1d732b4..a56dd8cf6f 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5435,10 +5435,6 @@ pub const FuncGen = struct { const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; - if (outputs.len > 1) { - return self.todo("implement llvm codegen for asm with more than 1 output", .{}); - } - var llvm_constraints: std.ArrayListUnmanaged(u8) = .{}; defer llvm_constraints.deinit(self.gpa); @@ -5446,7 +5442,10 @@ pub const FuncGen = struct { defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const llvm_params_len = inputs.len; + const return_count: u8 = for (outputs) |output| { + if (output == .none) break 1; + } else 0; + const llvm_params_len = inputs.len + outputs.len - return_count; const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len); const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len); var llvm_param_i: usize = 0; @@ -5456,9 +5455,6 @@ pub const FuncGen = struct { try name_map.ensureUnusedCapacity(arena, outputs.len + inputs.len); for (outputs) |output| { - if (output != .none) { - return self.todo("implement inline asm with non-returned output", .{}); - } const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); @@ -5471,6 +5467,15 @@ pub const FuncGen = struct { llvm_constraints.appendAssumeCapacity(','); } llvm_constraints.appendAssumeCapacity('='); + if (output != .none) { + try llvm_constraints.ensureUnusedCapacity(self.gpa, llvm_constraints.capacity + 1); + llvm_constraints.appendAssumeCapacity('*'); + + const output_inst = try self.resolveInst(output); + llvm_param_values[llvm_param_i] = output_inst; + llvm_param_types[llvm_param_i] = output_inst.typeOf(); + llvm_param_i += 1; + } llvm_constraints.appendSliceAssumeCapacity(constraint[1..]); name_map.putAssumeCapacityNoClobber(name, {}); From 6de9eea7bce4023ae150fb5b4d417d66b9bf13fb Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 7 Jun 2022 19:49:40 +0300 Subject: [PATCH 1782/2031] stage2 llvm: fix float/int conversion compiler-rt calls --- src/codegen/llvm.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a56dd8cf6f..dcdf4888ea 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5012,7 +5012,7 @@ pub const FuncGen = struct { const compiler_rt_operand_abbrev = compilerRtFloatAbbrev(operand_bits); const compiler_rt_dest_abbrev = compilerRtIntAbbrev(rt_int_bits); - const sign_prefix = if (dest_scalar_ty.isSignedInt()) "" else "un"; + const sign_prefix = if (dest_scalar_ty.isSignedInt()) "" else "uns"; var fn_name_buf: [64]u8 = undefined; const fn_name = std.fmt.bufPrintZ(&fn_name_buf, "__fix{s}{s}f{s}i", .{ @@ -9289,7 +9289,7 @@ fn needDbgVarWorkaround(dg: *DeclGen, ty: Type) bool { } fn compilerRtIntBits(bits: u16) u16 { - inline for (.{ 8, 16, 32, 64, 128 }) |b| { + inline for (.{ 32, 64, 128 }) |b| { if (bits <= b) { return b; } From 413577c881963559f7f357bfd90f4ade6d6de20d Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 7 Jun 2022 19:14:07 +0300 Subject: [PATCH 1783/2031] std: adjust for stage2 semantics --- lib/std/io/bit_reader.zig | 10 +++------- lib/std/io/stream_source.zig | 1 + lib/std/math/big/rational.zig | 2 ++ lib/std/net.zig | 11 ++++++++--- lib/std/net/test.zig | 4 ++++ lib/std/priority_queue.zig | 1 + lib/std/simd.zig | 1 + 7 files changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/std/io/bit_reader.zig b/lib/std/io/bit_reader.zig index aebb189942..15262f67a2 100644 --- a/lib/std/io/bit_reader.zig +++ b/lib/std/io/bit_reader.zig @@ -87,13 +87,9 @@ pub fn BitReader(endian: std.builtin.Endian, comptime ReaderType: type) type { //copy bytes until we have enough bits, then leave the rest in bit_buffer while (out_bits.* < bits) { const n = bits - out_bits.*; - const next_byte = self.forward_reader.readByte() catch |err| { - if (err == error.EndOfStream) { - return @intCast(U, out_buffer); - } - //@BUG: See #1810. Not sure if the bug is that I have to do this for some - // streams, or that I don't for streams with emtpy errorsets. - return @errSetCast(Error, err); + const next_byte = self.forward_reader.readByte() catch |err| switch (err) { + error.EndOfStream => return @intCast(U, out_buffer), + else => |e| return e, }; switch (endian) { diff --git a/lib/std/io/stream_source.zig b/lib/std/io/stream_source.zig index ce5256028c..2345a97855 100644 --- a/lib/std/io/stream_source.zig +++ b/lib/std/io/stream_source.zig @@ -114,6 +114,7 @@ test "StreamSource (mutable buffer)" { } test "StreamSource (const buffer)" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; const buffer: [64]u8 = "Hello, World!".* ++ ([1]u8{0xAA} ** 51); var source = StreamSource{ .const_buffer = std.io.fixedBufferStream(&buffer) }; diff --git a/lib/std/math/big/rational.zig b/lib/std/math/big/rational.zig index de6804ca01..e83b017335 100644 --- a/lib/std/math/big/rational.zig +++ b/lib/std/math/big/rational.zig @@ -573,6 +573,7 @@ test "big.rational setFloatString" { } test "big.rational toFloat" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; var a = try Rational.init(testing.allocator); defer a.deinit(); @@ -586,6 +587,7 @@ test "big.rational toFloat" { } test "big.rational set/to Float round-trip" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; var a = try Rational.init(testing.allocator); defer a.deinit(); var prng = std.rand.DefaultPrng.init(0x5EED); diff --git a/lib/std/net.zig b/lib/std/net.zig index 2bd3e6cfb1..235ad8496a 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -1342,7 +1342,8 @@ fn getResolvConf(allocator: mem.Allocator, rc: *ResolvConf) !void { }; defer file.close(); - const stream = std.io.bufferedReader(file.reader()).reader(); + var buf_reader = std.io.bufferedReader(file.reader()); + const stream = buf_reader.reader(); var line_buf: [512]u8 = undefined; while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) { error.StreamTooLong => blk: { @@ -1353,7 +1354,10 @@ fn getResolvConf(allocator: mem.Allocator, rc: *ResolvConf) !void { }, else => |e| return e, }) |line| { - const no_comment_line = mem.split(u8, line, "#").next().?; + const no_comment_line = no_comment_line: { + var split = mem.split(u8, line, "#"); + break :no_comment_line split.next().?; + }; var line_it = mem.tokenize(u8, no_comment_line, " \t"); const token = line_it.next() orelse continue; @@ -1363,7 +1367,8 @@ fn getResolvConf(allocator: mem.Allocator, rc: *ResolvConf) !void { const name = colon_it.next().?; const value_txt = colon_it.next() orelse continue; const value = std.fmt.parseInt(u8, value_txt, 10) catch |err| switch (err) { - error.Overflow => 255, + // TODO https://github.com/ziglang/zig/issues/11812 + error.Overflow => @as(u8, 255), error.InvalidCharacter => continue, }; if (mem.eql(u8, name, "ndots")) { diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index f2946777bd..710eb91376 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -5,6 +5,7 @@ const mem = std.mem; const testing = std.testing; test "parse and render IPv6 addresses" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; if (builtin.os.tag == .wasi) return error.SkipZigTest; var buffer: [100]u8 = undefined; @@ -67,6 +68,7 @@ test "invalid but parseable IPv6 scope ids" { } test "parse and render IPv4 addresses" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; if (builtin.os.tag == .wasi) return error.SkipZigTest; var buffer: [18]u8 = undefined; @@ -91,6 +93,7 @@ test "parse and render IPv4 addresses" { } test "parse and render UNIX addresses" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; if (builtin.os.tag == .wasi) return error.SkipZigTest; if (!net.has_unix_sockets) return error.SkipZigTest; @@ -104,6 +107,7 @@ test "parse and render UNIX addresses" { } test "resolve DNS" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; if (builtin.os.tag == .wasi) return error.SkipZigTest; if (builtin.os.tag == .windows) { diff --git a/lib/std/priority_queue.zig b/lib/std/priority_queue.zig index ebc13a9974..51671bca78 100644 --- a/lib/std/priority_queue.zig +++ b/lib/std/priority_queue.zig @@ -399,6 +399,7 @@ test "std.PriorityQueue: fromOwnedSlice trivial case 1" { } test "std.PriorityQueue: fromOwnedSlice" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 }; const heap_items = try testing.allocator.dupe(u32, items[0..]); var queue = PQlt.fromOwnedSlice(testing.allocator, heap_items[0..], {}); diff --git a/lib/std/simd.zig b/lib/std/simd.zig index a30622aef6..a7ce0ab3fd 100644 --- a/lib/std/simd.zig +++ b/lib/std/simd.zig @@ -160,6 +160,7 @@ pub fn extract( } test "vector patterns" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; const base = @Vector(4, u32){ 10, 20, 30, 40 }; const other_base = @Vector(4, u32){ 55, 66, 77, 88 }; From 27dad11ef166d5834be1166c86289c8e37aa0471 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 7 Jun 2022 21:05:11 +0200 Subject: [PATCH 1784/2031] x64: remove outdated TODO comment --- src/arch/x86_64/CodeGen.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index f4444d56a6..5bda84c98c 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -5654,7 +5654,6 @@ fn genInlineMemcpyRegisterRegister( const abi_size = @intCast(u32, ty.abiSize(self.target.*)); - // TODO common code-path with genSetStack, refactor! if (!math.isPowerOfTwo(abi_size)) { const tmp_reg = try self.copyToTmpRegister(ty, .{ .register = src_reg }); From 523fae420bd569dc79153eb3a9adb176e18a115c Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Thu, 2 Jun 2022 15:51:22 -0600 Subject: [PATCH 1785/2031] add const to msghdr_const iov and control pointers alongside the typical msghdr struct, Zig has added a msghdr_const type that can be used with sendmsg which allows const data to be provided. I believe that data pointed to by the iov and control fields in msghdr are also left unmodified, in which case they can be marked const as well. --- lib/std/c/netbsd.zig | 4 ++-- lib/std/c/openbsd.zig | 4 ++-- lib/std/c/solaris.zig | 4 ++-- lib/std/os/linux/arm-eabi.zig | 4 ++-- lib/std/os/linux/arm64.zig | 4 ++-- lib/std/os/linux/i386.zig | 4 ++-- lib/std/os/linux/io_uring.zig | 2 +- lib/std/os/linux/mips.zig | 4 ++-- lib/std/os/linux/powerpc.zig | 4 ++-- lib/std/os/linux/powerpc64.zig | 4 ++-- lib/std/os/linux/riscv64.zig | 4 ++-- lib/std/os/linux/sparc64.zig | 4 ++-- lib/std/os/linux/x86_64.zig | 4 ++-- lib/std/os/windows/ws2_32.zig | 2 +- 14 files changed, 26 insertions(+), 26 deletions(-) diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig index d52568d03a..5739e6fac9 100644 --- a/lib/std/c/netbsd.zig +++ b/lib/std/c/netbsd.zig @@ -266,13 +266,13 @@ pub const msghdr_const = extern struct { msg_namelen: socklen_t, /// scatter/gather array - msg_iov: [*]iovec_const, + msg_iov: [*]const iovec_const, /// # elements in msg_iov msg_iovlen: i32, /// ancillary data - msg_control: ?*anyopaque, + msg_control: ?*const anyopaque, /// ancillary data buffer len msg_controllen: socklen_t, diff --git a/lib/std/c/openbsd.zig b/lib/std/c/openbsd.zig index 71c9d21ce3..3ef2be4628 100644 --- a/lib/std/c/openbsd.zig +++ b/lib/std/c/openbsd.zig @@ -279,13 +279,13 @@ pub const msghdr_const = extern struct { msg_namelen: socklen_t, /// scatter/gather array - msg_iov: [*]iovec_const, + msg_iov: [*]const iovec_const, /// # elements in msg_iov msg_iovlen: c_uint, /// ancillary data - msg_control: ?*anyopaque, + msg_control: ?*const anyopaque, /// ancillary data buffer len msg_controllen: socklen_t, diff --git a/lib/std/c/solaris.zig b/lib/std/c/solaris.zig index 7026cf5dc4..14f5bcf1c8 100644 --- a/lib/std/c/solaris.zig +++ b/lib/std/c/solaris.zig @@ -202,11 +202,11 @@ pub const msghdr_const = extern struct { /// size of address msg_namelen: socklen_t, /// scatter/gather array - msg_iov: [*]iovec_const, + msg_iov: [*]const iovec_const, /// # elements in msg_iov msg_iovlen: i32, /// ancillary data - msg_control: ?*anyopaque, + msg_control: ?*const anyopaque, /// ancillary data buffer len msg_controllen: socklen_t, /// flags on received message diff --git a/lib/std/os/linux/arm-eabi.zig b/lib/std/os/linux/arm-eabi.zig index c3a94312a0..cd7509d265 100644 --- a/lib/std/os/linux/arm-eabi.zig +++ b/lib/std/os/linux/arm-eabi.zig @@ -253,9 +253,9 @@ pub const msghdr = extern struct { pub const msghdr_const = extern struct { name: ?*const sockaddr, namelen: socklen_t, - iov: [*]iovec_const, + iov: [*]const iovec_const, iovlen: i32, - control: ?*anyopaque, + control: ?*const anyopaque, controllen: socklen_t, flags: i32, }; diff --git a/lib/std/os/linux/arm64.zig b/lib/std/os/linux/arm64.zig index 9bc0e32c19..dc6c7077ba 100644 --- a/lib/std/os/linux/arm64.zig +++ b/lib/std/os/linux/arm64.zig @@ -209,10 +209,10 @@ pub const msghdr = extern struct { pub const msghdr_const = extern struct { name: ?*const sockaddr, namelen: socklen_t, - iov: [*]iovec_const, + iov: [*]const iovec_const, iovlen: i32, __pad1: i32 = 0, - control: ?*anyopaque, + control: ?*const anyopaque, controllen: socklen_t, __pad2: socklen_t = 0, flags: i32, diff --git a/lib/std/os/linux/i386.zig b/lib/std/os/linux/i386.zig index 6149997a04..a85f6c713b 100644 --- a/lib/std/os/linux/i386.zig +++ b/lib/std/os/linux/i386.zig @@ -228,9 +228,9 @@ pub const msghdr = extern struct { pub const msghdr_const = extern struct { name: ?*const sockaddr, namelen: socklen_t, - iov: [*]iovec_const, + iov: [*]const iovec_const, iovlen: i32, - control: ?*anyopaque, + control: ?*const anyopaque, controllen: socklen_t, flags: i32, }; diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index 3672a94c69..144927771f 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -2029,7 +2029,7 @@ test "sendmsg/recvmsg" { defer os.close(client); const buffer_send = [_]u8{42} ** 128; - var iovecs_send = [_]os.iovec_const{ + const iovecs_send = [_]os.iovec_const{ os.iovec_const{ .iov_base = &buffer_send, .iov_len = buffer_send.len }, }; const msg_send = os.msghdr_const{ diff --git a/lib/std/os/linux/mips.zig b/lib/std/os/linux/mips.zig index 95b73396b0..4341949b2f 100644 --- a/lib/std/os/linux/mips.zig +++ b/lib/std/os/linux/mips.zig @@ -304,9 +304,9 @@ pub const msghdr = extern struct { pub const msghdr_const = extern struct { name: ?*const sockaddr, namelen: socklen_t, - iov: [*]iovec_const, + iov: [*]const iovec_const, iovlen: i32, - control: ?*anyopaque, + control: ?*const anyopaque, controllen: socklen_t, flags: i32, }; diff --git a/lib/std/os/linux/powerpc.zig b/lib/std/os/linux/powerpc.zig index bd4d172d44..c25f520ba2 100644 --- a/lib/std/os/linux/powerpc.zig +++ b/lib/std/os/linux/powerpc.zig @@ -234,9 +234,9 @@ pub const msghdr = extern struct { pub const msghdr_const = extern struct { name: ?*const sockaddr, namelen: socklen_t, - iov: [*]iovec_const, + iov: [*]const iovec_const, iovlen: usize, - control: ?*anyopaque, + control: ?*const anyopaque, controllen: socklen_t, flags: i32, }; diff --git a/lib/std/os/linux/powerpc64.zig b/lib/std/os/linux/powerpc64.zig index 4f838f6086..221fd935f2 100644 --- a/lib/std/os/linux/powerpc64.zig +++ b/lib/std/os/linux/powerpc64.zig @@ -235,9 +235,9 @@ pub const msghdr = extern struct { pub const msghdr_const = extern struct { name: ?*const sockaddr, namelen: socklen_t, - iov: [*]iovec_const, + iov: [*]const iovec_const, iovlen: usize, - control: ?*anyopaque, + control: ?*const anyopaque, controllen: usize, flags: i32, }; diff --git a/lib/std/os/linux/riscv64.zig b/lib/std/os/linux/riscv64.zig index 30b725a85d..079d501c32 100644 --- a/lib/std/os/linux/riscv64.zig +++ b/lib/std/os/linux/riscv64.zig @@ -199,10 +199,10 @@ pub const msghdr = extern struct { pub const msghdr_const = extern struct { name: ?*const sockaddr, namelen: socklen_t, - iov: [*]iovec_const, + iov: [*]const iovec_const, iovlen: i32, __pad1: i32 = 0, - control: ?*anyopaque, + control: ?*const anyopaque, controllen: socklen_t, __pad2: socklen_t = 0, flags: i32, diff --git a/lib/std/os/linux/sparc64.zig b/lib/std/os/linux/sparc64.zig index 351dd1413b..d7b74d95eb 100644 --- a/lib/std/os/linux/sparc64.zig +++ b/lib/std/os/linux/sparc64.zig @@ -285,9 +285,9 @@ pub const msghdr = extern struct { pub const msghdr_const = extern struct { name: ?*const sockaddr, namelen: socklen_t, - iov: [*]iovec_const, + iov: [*]const iovec_const, iovlen: u64, - control: ?*anyopaque, + control: ?*const anyopaque, controllen: u64, flags: i32, }; diff --git a/lib/std/os/linux/x86_64.zig b/lib/std/os/linux/x86_64.zig index 60fbb29557..3653c31584 100644 --- a/lib/std/os/linux/x86_64.zig +++ b/lib/std/os/linux/x86_64.zig @@ -256,10 +256,10 @@ pub const msghdr = extern struct { pub const msghdr_const = extern struct { name: ?*const sockaddr, namelen: socklen_t, - iov: [*]iovec_const, + iov: [*]const iovec_const, iovlen: i32, __pad1: i32 = 0, - control: ?*anyopaque, + control: ?*const anyopaque, controllen: socklen_t, __pad2: socklen_t = 0, flags: i32, diff --git a/lib/std/os/windows/ws2_32.zig b/lib/std/os/windows/ws2_32.zig index a956242980..4caf64de36 100644 --- a/lib/std/os/windows/ws2_32.zig +++ b/lib/std/os/windows/ws2_32.zig @@ -1143,7 +1143,7 @@ pub const msghdr_const = WSAMSG_const; pub const WSAMSG_const = extern struct { name: *const sockaddr, namelen: INT, - lpBuffers: [*]WSABUF, + lpBuffers: [*]const WSABUF, dwBufferCount: DWORD, Control: WSABUF, dwFlags: DWORD, From 70dc910086582b028d404d5de5049ceae0a95161 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Tue, 7 Jun 2022 10:58:49 -0700 Subject: [PATCH 1786/2031] std.math: Add O(log N) implementation of log2(x) for comptime_int Since Zig provides @clz and not @ffs (find-first-set), log2 for comptime integers needs to be computed algorithmically. To avoid hitting the backward branch quota, this updates log2(x) to use a simple O(log N) algorithm. --- lib/std/math/log2.zig | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/std/math/log2.zig b/lib/std/math/log2.zig index c83b170208..4158b290f0 100644 --- a/lib/std/math/log2.zig +++ b/lib/std/math/log2.zig @@ -17,12 +17,20 @@ pub fn log2(x: anytype) @TypeOf(x) { }, .Float => return @log2(x), .ComptimeInt => comptime { - var result = 0; var x_shifted = x; - while (b: { - x_shifted >>= 1; - break :b x_shifted != 0; - }) : (result += 1) {} + // First, calculate floorPowerOfTwo(x) + var shift_amt = 1; + while (x_shifted >> (shift_amt << 1) != 0) shift_amt <<= 1; + + // Answer is in the range [shift_amt, 2 * shift_amt - 1] + // We can find it in O(log(N)) using binary search. + var result = 0; + while (shift_amt != 0) : (shift_amt >>= 1) { + if (x_shifted >> shift_amt != 0) { + x_shifted >>= shift_amt; + result += shift_amt; + } + } return result; }, .Int => |IntType| switch (IntType.signedness) { @@ -36,4 +44,10 @@ pub fn log2(x: anytype) @TypeOf(x) { test "log2" { try expect(log2(@as(f32, 0.2)) == @log2(0.2)); try expect(log2(@as(f64, 0.2)) == @log2(0.2)); + comptime { + try expect(log2(1) == 0); + try expect(log2(15) == 3); + try expect(log2(16) == 4); + try expect(log2(1 << 4073) == 4073); + } } From 61844b6bd405b4cca3ab673284609aa6a651d506 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Tue, 7 Jun 2022 11:08:45 +0200 Subject: [PATCH 1787/2031] stage2 AArch64: introduce MCValue.condition_flags Follows 9747303d16dfca61316a292d1e05ac901191e3a3 for AArch64 --- src/arch/aarch64/CodeGen.zig | 141 ++++++++++------------------------- 1 file changed, 41 insertions(+), 100 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 2875bda7e9..7a7ee31117 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -35,6 +35,7 @@ const RegisterManager = abi.RegisterManager; const RegisterLock = RegisterManager.RegisterLock; const Register = bits.Register; const Instruction = bits.Instruction; +const Condition = bits.Instruction.Condition; const callee_preserved_regs = abi.callee_preserved_regs; const c_abi_int_param_regs = abi.c_abi_int_param_regs; const c_abi_int_return_regs = abi.c_abi_int_return_regs; @@ -90,7 +91,7 @@ register_manager: RegisterManager = .{}, /// Maps offset to what is stored there. stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, /// Tracks the current instruction allocated to the compare flags -compare_flags_inst: ?Air.Inst.Index = null, +condition_flags_inst: ?Air.Inst.Index = null, /// Offset from the stack base, representing the end of the stack frame. max_end_stack: u32 = 0, @@ -161,12 +162,10 @@ const MCValue = union(enum) { /// The value is a pointer to one of the stack variables (payload /// is stack offset). ptr_stack_offset: u32, - /// The value is in the compare flags assuming an unsigned - /// operation, with this operator applied on top of it. - compare_flags_unsigned: math.CompareOperator, - /// The value is in the compare flags assuming a signed operation, - /// with this operator applied on top of it. - compare_flags_signed: math.CompareOperator, + /// The value resides in the N, Z, C, V flags. The value is 1 (if + /// the type is u1) or true (if the type in bool) iff the + /// specified condition is true. + condition_flags: Condition, fn isMemory(mcv: MCValue) bool { return switch (mcv) { @@ -190,8 +189,7 @@ const MCValue = union(enum) { .immediate, .memory, - .compare_flags_unsigned, - .compare_flags_signed, + .condition_flags, .ptr_stack_offset, .undef, => false, @@ -758,10 +756,10 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { }, .register_with_overflow => |rwo| { self.register_manager.freeReg(rwo.reg); - self.compare_flags_inst = null; + self.condition_flags_inst = null; }, - .compare_flags_signed, .compare_flags_unsigned => { - self.compare_flags_inst = null; + .condition_flags => { + self.condition_flags_inst = null; }, else => {}, // TODO process stack allocation death } @@ -911,12 +909,10 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void /// Save the current instruction stored in the compare flags if /// occupied fn spillCompareFlagsIfOccupied(self: *Self) !void { - if (self.compare_flags_inst) |inst_to_save| { + if (self.condition_flags_inst) |inst_to_save| { const mcv = self.getResolvedInstValue(inst_to_save); const new_mcv = switch (mcv) { - .compare_flags_signed, - .compare_flags_unsigned, - => try self.allocRegOrMem(inst_to_save, true), + .condition_flags => try self.allocRegOrMem(inst_to_save, true), .register_with_overflow => try self.allocRegOrMem(inst_to_save, false), else => unreachable, // mcv doesn't occupy the compare flags }; @@ -927,7 +923,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); - self.compare_flags_inst = null; + self.condition_flags_inst = null; // TODO consolidate with register manager and spillInstruction // this call should really belong in the register manager! @@ -1109,32 +1105,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { switch (operand) { .dead => unreachable, .unreach => unreachable, - .compare_flags_unsigned => |op| { - const r = MCValue{ - .compare_flags_unsigned = switch (op) { - .gte => .lt, - .gt => .lte, - .neq => .eq, - .lt => .gte, - .lte => .gt, - .eq => .neq, - }, - }; - break :result r; - }, - .compare_flags_signed => |op| { - const r = MCValue{ - .compare_flags_signed = switch (op) { - .gte => .lt, - .gt => .lte, - .neq => .eq, - .lt => .gte, - .lte => .gt, - .eq => .neq, - }, - }; - break :result r; - }, + .condition_flags => |cond| break :result MCValue{ .condition_flags = cond.negate() }, else => { switch (operand_ty.zigTypeTag()) { .Bool => { @@ -1851,7 +1822,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = null; + self.condition_flags_inst = null; const base_tag: Air.Inst.Tag = switch (tag) { .add_with_overflow => .add, @@ -1875,7 +1846,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, Type.usize, Type.usize, null); try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); - try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); + try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne }); break :result MCValue{ .stack_offset = stack_offset }; }, @@ -1907,7 +1878,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { }; try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; + self.condition_flags_inst = inst; const dest = blk: { if (rhs_immediate_ok) { @@ -1966,7 +1937,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = null; + self.condition_flags_inst = null; const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { .signed => .smull, @@ -2015,16 +1986,14 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { } try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); - try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ - .compare_flags_unsigned = .neq, - }); + try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne }); break :result MCValue{ .stack_offset = stack_offset }; } else if (int_info.bits <= 64) { const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = null; + self.condition_flags_inst = null; // TODO this should really be put in a helper similar to `binOpRegister` const lhs_is_register = lhs == .register; @@ -2194,9 +2163,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); - try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ - .compare_flags_unsigned = .neq, - }); + try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne }); break :result MCValue{ .stack_offset = stack_offset }; } else return self.fail("TODO implement mul_with_overflow for integers > u64/i64", .{}); @@ -2236,7 +2203,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = null; + self.condition_flags_inst = null; // lsl dest, lhs, rhs const dest = try self.binOp(.shl, lhs, rhs, lhs_ty, rhs_ty, null); @@ -2251,9 +2218,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { _ = try self.binOp(.cmp_eq, lhs, reconstructed, lhs_ty, lhs_ty, null); try self.genSetStack(lhs_ty, stack_offset, dest); - try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ - .compare_flags_unsigned = .neq, - }); + try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne }); break :result MCValue{ .stack_offset = stack_offset }; } else { @@ -2681,8 +2646,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .undef => unreachable, .unreach => unreachable, .dead => unreachable, - .compare_flags_unsigned, - .compare_flags_signed, + .condition_flags, .register_with_overflow, => unreachable, // cannot hold an address .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), @@ -2694,7 +2658,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo switch (dst_mcv) { .dead => unreachable, .undef => unreachable, - .compare_flags_signed, .compare_flags_unsigned => unreachable, + .condition_flags => unreachable, .register => |dst_reg| { try self.genLdrRegister(dst_reg, addr_reg, elem_ty); }, @@ -2903,8 +2867,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .undef => unreachable, .unreach => unreachable, .dead => unreachable, - .compare_flags_unsigned, - .compare_flags_signed, + .condition_flags, .register_with_overflow, => unreachable, // cannot hold an address .immediate => |imm| { @@ -3370,11 +3333,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { }); try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; + self.condition_flags_inst = inst; break :result switch (int_info.signedness) { - .signed => MCValue{ .compare_flags_signed = op }, - .unsigned => MCValue{ .compare_flags_unsigned = op }, + .signed => MCValue{ .condition_flags = Condition.fromCompareOperatorSigned(op) }, + .unsigned => MCValue{ .condition_flags = Condition.fromCompareOperatorUnsigned(op) }, }; } else { return self.fail("TODO AArch64 cmp for ints > 64 bits", .{}); @@ -3434,26 +3397,13 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index { switch (condition) { - .compare_flags_signed, - .compare_flags_unsigned, - => return try self.addInst(.{ + .condition_flags => |cond| return try self.addInst(.{ .tag = .b_cond, .data = .{ .inst_cond = .{ .inst = undefined, // populated later through performReloc - .cond = switch (condition) { - .compare_flags_signed => |cmp_op| blk: { - // Here we map to the opposite condition because the jump is to the false branch. - const condition_code = Instruction.Condition.fromCompareOperatorSigned(cmp_op); - break :blk condition_code.negate(); - }, - .compare_flags_unsigned => |cmp_op| blk: { - // Here we map to the opposite condition because the jump is to the false branch. - const condition_code = Instruction.Condition.fromCompareOperatorUnsigned(cmp_op); - break :blk condition_code.negate(); - }, - else => unreachable, - }, + // Here we map to the opposite condition because the jump is to the false branch. + .cond = cond.negate(), }, }, }), @@ -3503,7 +3453,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { var parent_stack = try self.stack.clone(self.gpa); defer parent_stack.deinit(self.gpa); const parent_registers = self.register_manager.registers; - const parent_compare_flags_inst = self.compare_flags_inst; + const parent_condition_flags_inst = self.condition_flags_inst; try self.branch_stack.append(.{}); errdefer { @@ -3522,7 +3472,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { defer saved_then_branch.deinit(self.gpa); self.register_manager.registers = parent_registers; - self.compare_flags_inst = parent_compare_flags_inst; + self.condition_flags_inst = parent_condition_flags_inst; self.stack.deinit(self.gpa); self.stack = parent_stack; @@ -3672,15 +3622,15 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { else => return self.fail("TODO implement isErr for {}", .{operand}), } - return MCValue{ .compare_flags_unsigned = .gt }; + return MCValue{ .condition_flags = .hi }; } fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { const is_err_result = try self.isErr(ty, operand); switch (is_err_result) { - .compare_flags_unsigned => |op| { - assert(op == .gt); - return MCValue{ .compare_flags_unsigned = .lte }; + .condition_flags => |cond| { + assert(cond == .hi); + return MCValue{ .condition_flags = cond.negate() }; }, .immediate => |imm| { assert(imm == 0); @@ -3889,7 +3839,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { block_data.mcv = switch (operand_mcv) { .none, .dead, .unreach => unreachable, .register, .stack_offset, .memory => operand_mcv, - .immediate => blk: { + .immediate, .condition_flags => blk: { const new_mcv = try self.allocRegOrMem(block, true); try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); break :blk new_mcv; @@ -4072,8 +4022,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro else => return self.fail("TODO implement memset", .{}), } }, - .compare_flags_unsigned, - .compare_flags_signed, + .condition_flags, .immediate, .ptr_stack_offset, => { @@ -4235,15 +4184,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void } }, }); }, - .compare_flags_unsigned, - .compare_flags_signed, - => |op| { - const condition = switch (mcv) { - .compare_flags_unsigned => Instruction.Condition.fromCompareOperatorUnsigned(op), - .compare_flags_signed => Instruction.Condition.fromCompareOperatorSigned(op), - else => unreachable, - }; - + .condition_flags => |condition| { _ = try self.addInst(.{ .tag = .cset, .data = .{ .r_cond = .{ From 3e30ba3f20dce2d406253de3fc0eb86934a3eaa7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 7 Jun 2022 20:51:14 -0700 Subject: [PATCH 1788/2031] stage2: better codegen for byte-aligned packed struct fields * Sema: handle overaligned packed struct field pointers * LLVM: handle byte-aligned packed struct field pointers --- src/Sema.zig | 23 +++++++++++- src/codegen/llvm.zig | 40 ++++++++++++++------ src/type.zig | 21 +++++++++++ test/behavior/packed-struct.zig | 65 +++++++++++++++++++++++++++++++++ 4 files changed, 136 insertions(+), 13 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 5159d6f5d3..0cf449991d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -18678,8 +18678,6 @@ fn structFieldPtrByIndex( const target = sema.mod.getTarget(); - // TODO handle when the struct pointer is overaligned, we should return a potentially - // over-aligned field pointer too. if (struct_obj.layout == .Packed) { comptime assert(Type.packed_struct_layout_version == 2); @@ -18700,6 +18698,27 @@ fn structFieldPtrByIndex( ptr_ty_data.host_size = struct_ptr_ty_info.host_size; ptr_ty_data.bit_offset += struct_ptr_ty_info.bit_offset; } + + const parent_align = if (struct_ptr_ty_info.@"align" != 0) + struct_ptr_ty_info.@"align" + else + struct_ptr_ty_info.pointee_type.abiAlignment(target); + ptr_ty_data.@"align" = parent_align; + + // If the field happens to be byte-aligned, simplify the pointer type. + // The pointee type bit size must match its ABI byte size so that loads and stores + // do not interfere with the surrounding packed bits. + if (parent_align != 0 and ptr_ty_data.bit_offset % 8 == 0) { + const byte_offset = ptr_ty_data.bit_offset / 8; + const elem_size_bytes = ptr_ty_data.pointee_type.abiSize(target); + const elem_size_bits = ptr_ty_data.pointee_type.bitSize(target); + if (elem_size_bytes * 8 == elem_size_bits) { + const new_align = @as(u32, 1) << @intCast(u5, @ctz(u64, byte_offset | parent_align)); + ptr_ty_data.bit_offset = 0; + ptr_ty_data.host_size = 0; + ptr_ty_data.@"align" = new_align; + } + } } else { ptr_ty_data.@"align" = field.abi_align; } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index dcdf4888ea..188c2f6f11 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -8311,23 +8311,41 @@ pub const FuncGen = struct { field_index: u32, ) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; + + const target = self.dg.object.target; const struct_ty = struct_ptr_ty.childType(); switch (struct_ty.zigTypeTag()) { .Struct => switch (struct_ty.containerLayout()) { .Packed => { - // From LLVM's perspective, a pointer to a packed struct and a pointer - // to a field of a packed struct are the same. The difference is in the - // Zig pointer type which provides information for how to mask and shift - // out the relevant bits when accessing the pointee. - // Here we perform a bitcast because we want to use the host_size - // as the llvm pointer element type. - const result_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst)); - // TODO this can be removed if we change host_size to be bits instead - // of bytes. - return self.builder.buildBitCast(struct_ptr, result_llvm_ty, ""); + const result_ty = self.air.typeOfIndex(inst); + const result_ty_info = result_ty.ptrInfo().data; + const result_llvm_ty = try self.dg.lowerType(result_ty); + + if (result_ty_info.host_size != 0) { + // From LLVM's perspective, a pointer to a packed struct and a pointer + // to a field of a packed struct are the same. The difference is in the + // Zig pointer type which provides information for how to mask and shift + // out the relevant bits when accessing the pointee. + // Here we perform a bitcast because we want to use the host_size + // as the llvm pointer element type. + return self.builder.buildBitCast(struct_ptr, result_llvm_ty, ""); + } + + // We have a pointer to a packed struct field that happens to be byte-aligned. + // Offset our operand pointer by the correct number of bytes. + const byte_offset = struct_ty.packedStructFieldByteOffset(field_index, target); + if (byte_offset == 0) { + return self.builder.buildBitCast(struct_ptr, result_llvm_ty, ""); + } + const llvm_bytes_ptr_ty = self.context.intType(8).pointerType(0); + const ptr_as_bytes = self.builder.buildBitCast(struct_ptr, llvm_bytes_ptr_ty, ""); + const llvm_usize = try self.dg.lowerType(Type.usize); + const llvm_index = llvm_usize.constInt(byte_offset, .False); + const indices: [1]*const llvm.Value = .{llvm_index}; + const new_ptr = self.builder.buildInBoundsGEP(ptr_as_bytes, &indices, indices.len, ""); + return self.builder.buildBitCast(new_ptr, result_llvm_ty, ""); }, else => { - const target = self.dg.module.getTarget(); var ty_buf: Type.Payload.Pointer = undefined; if (llvmFieldIndex(struct_ty, field_index, target, &ty_buf)) |llvm_field_index| { return self.builder.buildStructGEP(struct_ptr, llvm_field_index, ""); diff --git a/src/type.zig b/src/type.zig index 14c613a947..8e5eaf0ec7 100644 --- a/src/type.zig +++ b/src/type.zig @@ -5591,6 +5591,27 @@ pub const Type = extern union { } } + pub fn packedStructFieldByteOffset(ty: Type, field_index: usize, target: Target) u32 { + const struct_obj = ty.castTag(.@"struct").?.data; + assert(struct_obj.layout == .Packed); + comptime assert(Type.packed_struct_layout_version == 2); + + var bit_offset: u16 = undefined; + var running_bits: u16 = 0; + for (struct_obj.fields.values()) |f, i| { + if (!f.ty.hasRuntimeBits()) continue; + + if (i == field_index) { + bit_offset = running_bits; + } + running_bits += @intCast(u16, f.ty.bitSize(target)); + } + const host_size = (running_bits + 7) / 8; + _ = host_size; // TODO big-endian + const byte_offset = bit_offset / 8; + return byte_offset; + } + pub const FieldOffset = struct { field: usize, offset: u64, diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index 73c71ddf43..ed06cc486d 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -1,5 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); +const assert = std.debug.assert; const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; const native_endian = builtin.cpu.arch.endian(); @@ -305,3 +306,67 @@ test "regular in irregular packed struct" { try expectEqual(@as(u16, 235), foo.bar.a); try expectEqual(@as(u8, 42), foo.bar.b); } + +test "byte-aligned field pointer offsets" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + + const S = struct { + const A = packed struct { + a: u8, + b: u8, + c: u8, + d: u8, + }; + + const B = packed struct { + a: u16, + b: u16, + }; + + fn doTheTest() !void { + var a: A = .{ + .a = 1, + .b = 2, + .c = 3, + .d = 4, + }; + comptime assert(@TypeOf(&a.a) == *align(4) u8); + comptime assert(@TypeOf(&a.b) == *u8); + comptime assert(@TypeOf(&a.c) == *align(2) u8); + comptime assert(@TypeOf(&a.d) == *u8); + try expect(a.a == 1); + try expect(a.b == 2); + try expect(a.c == 3); + try expect(a.d == 4); + a.a += 1; + a.b += 1; + a.c += 1; + a.d += 1; + try expect(a.a == 2); + try expect(a.b == 3); + try expect(a.c == 4); + try expect(a.d == 5); + + var b: B = .{ + .a = 1, + .b = 2, + }; + comptime assert(@TypeOf(&b.a) == *align(4) u16); + comptime assert(@TypeOf(&b.b) == *u16); + try expect(b.a == 1); + try expect(b.b == 2); + b.a += 1; + b.b += 1; + try expect(b.a == 2); + try expect(b.b == 3); + } + }; + + try S.doTheTest(); + comptime try S.doTheTest(); +} From 53c86febcbeee858e9c6536c54adf99ee644147e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 7 Jun 2022 22:47:08 -0700 Subject: [PATCH 1789/2031] stage2: packed struct fixes for big-endian targets --- src/Sema.zig | 11 +++++-- src/type.zig | 7 ++-- test/behavior/packed-struct.zig | 57 ++++++++++++++++++++++++++------- 3 files changed, 58 insertions(+), 17 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 0cf449991d..8ce9226bd4 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -18708,11 +18708,18 @@ fn structFieldPtrByIndex( // If the field happens to be byte-aligned, simplify the pointer type. // The pointee type bit size must match its ABI byte size so that loads and stores // do not interfere with the surrounding packed bits. - if (parent_align != 0 and ptr_ty_data.bit_offset % 8 == 0) { - const byte_offset = ptr_ty_data.bit_offset / 8; + // We do not attempt this with big-endian targets yet because of nested + // structs and floats. I need to double-check the desired behavior for big endian + // targets before adding the necessary complications to this code. This will not + // cause miscompilations; it only means the field pointer uses bit masking when it + // might not be strictly necessary. + if (parent_align != 0 and ptr_ty_data.bit_offset % 8 == 0 and + target.cpu.arch.endian() == .Little) + { const elem_size_bytes = ptr_ty_data.pointee_type.abiSize(target); const elem_size_bits = ptr_ty_data.pointee_type.bitSize(target); if (elem_size_bytes * 8 == elem_size_bits) { + const byte_offset = ptr_ty_data.bit_offset / 8; const new_align = @as(u32, 1) << @intCast(u5, @ctz(u64, byte_offset | parent_align)); ptr_ty_data.bit_offset = 0; ptr_ty_data.host_size = 0; diff --git a/src/type.zig b/src/type.zig index 8e5eaf0ec7..b6f8db4ca1 100644 --- a/src/type.zig +++ b/src/type.zig @@ -5597,17 +5597,18 @@ pub const Type = extern union { comptime assert(Type.packed_struct_layout_version == 2); var bit_offset: u16 = undefined; + var elem_size_bits: u16 = undefined; var running_bits: u16 = 0; for (struct_obj.fields.values()) |f, i| { if (!f.ty.hasRuntimeBits()) continue; + const field_bits = @intCast(u16, f.ty.bitSize(target)); if (i == field_index) { bit_offset = running_bits; + elem_size_bits = field_bits; } - running_bits += @intCast(u16, f.ty.bitSize(target)); + running_bits += field_bits; } - const host_size = (running_bits + 7) / 8; - _ = host_size; // TODO big-endian const byte_offset = bit_offset / 8; return byte_offset; } diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index ed06cc486d..2483bbea69 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -289,13 +289,7 @@ test "regular in irregular packed struct" { const Irregular = packed struct { bar: Regular = Regular{}, - - // This field forces the regular packed struct to be a part of single u48 - // and thus it all gets represented as an array of 6 bytes in LLVM _: u24 = 0, - - // This struct on its own can represent its fields directly in LLVM - // with no need to use array of bytes as underlaying representation. pub const Regular = packed struct { a: u16 = 0, b: u8 = 0 }; }; @@ -335,17 +329,44 @@ test "byte-aligned field pointer offsets" { .c = 3, .d = 4, }; - comptime assert(@TypeOf(&a.a) == *align(4) u8); - comptime assert(@TypeOf(&a.b) == *u8); - comptime assert(@TypeOf(&a.c) == *align(2) u8); - comptime assert(@TypeOf(&a.d) == *u8); + switch (comptime builtin.cpu.arch.endian()) { + .Little => { + comptime assert(@TypeOf(&a.a) == *align(4) u8); + comptime assert(@TypeOf(&a.b) == *u8); + comptime assert(@TypeOf(&a.c) == *align(2) u8); + comptime assert(@TypeOf(&a.d) == *u8); + }, + .Big => { + // TODO re-evaluate packed struct endianness + comptime assert(@TypeOf(&a.a) == *align(4:0:4) u8); + comptime assert(@TypeOf(&a.b) == *align(4:8:4) u8); + comptime assert(@TypeOf(&a.c) == *align(4:16:4) u8); + comptime assert(@TypeOf(&a.d) == *align(4:24:4) u8); + }, + } try expect(a.a == 1); try expect(a.b == 2); try expect(a.c == 3); try expect(a.d == 4); + a.a += 1; + try expect(a.a == 2); + try expect(a.b == 2); + try expect(a.c == 3); + try expect(a.d == 4); + a.b += 1; + try expect(a.a == 2); + try expect(a.b == 3); + try expect(a.c == 3); + try expect(a.d == 4); + a.c += 1; + try expect(a.a == 2); + try expect(a.b == 3); + try expect(a.c == 4); + try expect(a.d == 4); + a.d += 1; try expect(a.a == 2); try expect(a.b == 3); @@ -356,11 +377,23 @@ test "byte-aligned field pointer offsets" { .a = 1, .b = 2, }; - comptime assert(@TypeOf(&b.a) == *align(4) u16); - comptime assert(@TypeOf(&b.b) == *u16); + switch (comptime builtin.cpu.arch.endian()) { + .Little => { + comptime assert(@TypeOf(&b.a) == *align(4) u16); + comptime assert(@TypeOf(&b.b) == *u16); + }, + .Big => { + comptime assert(@TypeOf(&b.a) == *align(4:0:4) u16); + comptime assert(@TypeOf(&b.b) == *align(4:16:4) u16); + }, + } try expect(b.a == 1); try expect(b.b == 2); + b.a += 1; + try expect(b.a == 2); + try expect(b.b == 2); + b.b += 1; try expect(b.a == 2); try expect(b.b == 3); From 33817794268b5453794f98ee752403ff44693112 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 8 Jun 2022 14:33:11 +0200 Subject: [PATCH 1790/2031] linker: Enable full RELRO by default Full RELRO is a hardening feature that makes it impossible to perform certian attacks involving overwriting parts of the Global Offset Table to invoke arbitrary code. It requires all symbols to be resolved before execution of the program starts which may have an impact on startup time. However most if not all popular Linux distributions enable full RELRO by default for all binaries and this does not seem to make a noticeable difference in practice. "Partial RELRO" is equivalent to `-z relro -z lazy`. "Full RELRO" is equivalent to `-z relro -z now`. LLD defaults to `-z relro -z lazy`, which means Zig's current `-z relro` option has no effect on LLD's behavior. The changes made by this commit are as follows: - Document that `-z relro` is the default and add `-z norelro`. - Pass `-z now` to LLD by default to enable full RELRO by default. - Add `-z lazy` to disable passing `-z now`. --- src/Compilation.zig | 4 ++-- src/link/Elf.zig | 10 +++++----- src/main.zig | 18 ++++++++++++++---- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 04e9d97719..7d686b2f40 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -763,8 +763,8 @@ pub const InitOptions = struct { linker_z_defs: bool = false, linker_z_origin: bool = false, linker_z_noexecstack: bool = false, - linker_z_now: bool = false, - linker_z_relro: bool = false, + linker_z_now: bool = true, + linker_z_relro: bool = true, linker_z_nocopyreloc: bool = false, linker_tsaware: bool = false, linker_nxcompat: bool = false, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index efe14b4276..30504c7a1a 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1517,12 +1517,12 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append("noexecstack"); } if (self.base.options.z_now) { - try argv.append("-z"); - try argv.append("now"); + // LLD defaults to -zlazy + try argv.append("-znow"); } - if (self.base.options.z_relro) { - try argv.append("-z"); - try argv.append("relro"); + if (!self.base.options.z_relro) { + // LLD defaults to -zrelro + try argv.append("-znorelro"); } if (getLDMOption(target)) |ldm| { diff --git a/src/main.zig b/src/main.zig index 37c584ba57..eaff34ee9e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -434,8 +434,10 @@ const usage_build_generic = \\ origin Indicate that the object must have its origin processed \\ nocopyreloc Disable the creation of copy relocations \\ noexecstack Indicate that the object requires an executable stack - \\ now Force all relocations to be processed on load - \\ relro Force all relocations to be resolved and be read-only on load + \\ now (default) Force all relocations to be processed on load + \\ lazy Don't force all relocations to be processed on load + \\ relro (default) Force all relocations to be read-only after processing + \\ norelro Don't force all relocations to be read-only after processing \\ -dynamic Force output to be dynamically linked \\ -static Force output to be statically linked \\ -Bsymbolic Bind global references locally @@ -655,8 +657,8 @@ fn buildOutputType( var linker_z_defs = false; var linker_z_origin = false; var linker_z_noexecstack = false; - var linker_z_now = false; - var linker_z_relro = false; + var linker_z_now = true; + var linker_z_relro = true; var linker_tsaware = false; var linker_nxcompat = false; var linker_dynamicbase = false; @@ -1209,8 +1211,12 @@ fn buildOutputType( linker_z_noexecstack = true; } else if (mem.eql(u8, z_arg, "now")) { linker_z_now = true; + } else if (mem.eql(u8, z_arg, "lazy")) { + linker_z_now = false; } else if (mem.eql(u8, z_arg, "relro")) { linker_z_relro = true; + } else if (mem.eql(u8, z_arg, "norelro")) { + linker_z_relro = false; } else { warn("unsupported linker extension flag: -z {s}", .{z_arg}); } @@ -1691,8 +1697,12 @@ fn buildOutputType( linker_z_noexecstack = true; } else if (mem.eql(u8, z_arg, "now")) { linker_z_now = true; + } else if (mem.eql(u8, z_arg, "lazy")) { + linker_z_now = false; } else if (mem.eql(u8, z_arg, "relro")) { linker_z_relro = true; + } else if (mem.eql(u8, z_arg, "norelro")) { + linker_z_relro = false; } else { warn("unsupported linker extension flag: -z {s}", .{z_arg}); } From d8cae4d1974ae0948cfd4cad5a2bb6e8c4248609 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 8 Jun 2022 15:13:04 +0200 Subject: [PATCH 1791/2031] std.build: Expose `-z norelro` and `-z lazy` --- lib/std/build.zig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/std/build.zig b/lib/std/build.zig index a738a39a1b..ce527ff021 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1570,6 +1570,12 @@ pub const LibExeObjStep = struct { /// Permit read-only relocations in read-only segments. Disallowed by default. link_z_notext: bool = false, + /// Force all relocations to be read-only after processing. + link_z_relro: bool = true, + + /// Allow relocations to be lazily processed after load. + link_z_lazy: bool = false, + /// (Darwin) Install name for the dylib install_name: ?[]const u8 = null, @@ -2577,6 +2583,14 @@ pub const LibExeObjStep = struct { try zig_args.append("-z"); try zig_args.append("notext"); } + if (!self.link_z_relro) { + try zig_args.append("-z"); + try zig_args.append("norelro"); + } + if (self.link_z_lazy) { + try zig_args.append("-z"); + try zig_args.append("lazy"); + } if (self.libc_file) |libc_file| { try zig_args.append("--libc"); From 29dd9a58808f54423af8202049419f20aa362711 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 6 Jun 2022 16:13:50 +0200 Subject: [PATCH 1792/2031] parser: sync comments with formal grammar --- lib/std/zig/parse.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 984b137bff..2806d16952 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -232,7 +232,7 @@ const Parser = struct { /// / TopLevelComptime ContainerDeclarations /// / KEYWORD_pub? TopLevelDecl ContainerDeclarations /// / - /// TopLevelComptime <- KEYWORD_comptime BlockExpr + /// TopLevelComptime <- KEYWORD_comptime Block fn parseContainerMembers(p: *Parser) !Members { const scratch_top = p.scratch.items.len; defer p.scratch.shrinkRetainingCapacity(scratch_top); @@ -849,7 +849,7 @@ const Parser = struct { } } - /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON (KEYWORD_anytype / TypeExpr) ByteAlign?)? (EQUAL Expr)? + /// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)? fn expectContainerField(p: *Parser) !Node.Index { _ = p.eatToken(.keyword_comptime); const name_token = p.assertToken(.identifier); From 93d7fd95477b4a4803b52d5f52d3da9bda1926c8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 8 Jun 2022 15:17:53 -0700 Subject: [PATCH 1793/2031] test harness: fix sort comparator It was returning "true" for lessThan() when the objects were in fact equal. --- src/test.zig | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/test.zig b/src/test.zig index 3d1f536f2c..2a9b82027f 100644 --- a/src/test.zig +++ b/src/test.zig @@ -490,9 +490,7 @@ fn getTestFileNameParts(name: []const u8) struct { /// Sort test filenames in-place, so that incremental test cases ("foo.0.zig", /// "foo.1.zig", etc.) are contiguous and appear in numerical order. -fn sortTestFilenames( - filenames: [][]const u8, -) void { +fn sortTestFilenames(filenames: [][]const u8) void { const Context = struct { pub fn lessThan(_: @This(), a: []const u8, b: []const u8) bool { const a_parts = getTestFileNameParts(a); @@ -505,14 +503,20 @@ fn sortTestFilenames( .eq => switch (std.mem.order(u8, a_parts.file_ext, b_parts.file_ext)) { .lt => true, .gt => false, - .eq => b: { // a and b differ only in their ".X" part + .eq => { + // a and b differ only in their ".X" part // Sort "." before any ".X." - if (a_parts.test_index == null) break :b true; - if (b_parts.test_index == null) break :b false; - - // Make sure that incremental tests appear in linear order - return a_parts.test_index.? < b_parts.test_index.?; + if (a_parts.test_index) |a_index| { + if (b_parts.test_index) |b_index| { + // Make sure that incremental tests appear in linear order + return a_index < b_index; + } else { + return false; + } + } else { + return b_parts.test_index != null; + } }, }, }; From 434226c89d45570842e9b9f34d1163b4d40156af Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 8 Jun 2022 15:18:43 -0700 Subject: [PATCH 1794/2031] stage2: fix type printing of sub-byte pointers --- src/type.zig | 4 ++-- test/behavior.zig | 1 - .../incompatible sub-byte fields.zig} | 16 +++++++++------- 3 files changed, 11 insertions(+), 10 deletions(-) rename test/{behavior/bugs/1120.zig => cases/compile_errors/incompatible sub-byte fields.zig} (58%) diff --git a/src/type.zig b/src/type.zig index b6f8db4ca1..9b91572427 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1843,7 +1843,7 @@ pub const Type = extern union { if (payload.@"align" != 0 or payload.host_size != 0) { try writer.print("align({d}", .{payload.@"align"}); - if (payload.bit_offset != 0) { + if (payload.bit_offset != 0 or payload.host_size != 0) { try writer.print(":{d}:{d}", .{ payload.bit_offset, payload.host_size }); } try writer.writeAll(") "); @@ -2167,7 +2167,7 @@ pub const Type = extern union { if (info.@"align" != 0 or info.host_size != 0) { try writer.print("align({d}", .{info.@"align"}); - if (info.bit_offset != 0) { + if (info.bit_offset != 0 or info.host_size != 0) { try writer.print(":{d}:{d}", .{ info.bit_offset, info.host_size }); } try writer.writeAll(") "); diff --git a/test/behavior.zig b/test/behavior.zig index 6618ddb20d..18e65564ad 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -27,7 +27,6 @@ test { _ = @import("behavior/bugs/1025.zig"); _ = @import("behavior/bugs/1076.zig"); _ = @import("behavior/bugs/1111.zig"); - _ = @import("behavior/bugs/1120.zig"); _ = @import("behavior/bugs/1277.zig"); _ = @import("behavior/bugs/1310.zig"); _ = @import("behavior/bugs/1381.zig"); diff --git a/test/behavior/bugs/1120.zig b/test/cases/compile_errors/incompatible sub-byte fields.zig similarity index 58% rename from test/behavior/bugs/1120.zig rename to test/cases/compile_errors/incompatible sub-byte fields.zig index 84c51feeac..20fbf5e30c 100644 --- a/test/behavior/bugs/1120.zig +++ b/test/cases/compile_errors/incompatible sub-byte fields.zig @@ -1,6 +1,3 @@ -const std = @import("std"); -const expect = std.testing.expect; - const A = packed struct { a: u2, b: u6, @@ -10,9 +7,7 @@ const B = packed struct { a: u2, b: u6, }; -test "bug 1120" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO - +export fn entry() void { var a = A{ .a = 2, .b = 2 }; var b = B{ .q = 22, .a = 3, .b = 2 }; var t: usize = 0; @@ -21,5 +16,12 @@ test "bug 1120" { 1 => &b.a, else => unreachable, }; - try expect(ptr.* == 2); + if (ptr.* == 2) { + @compileError("wrong compile error"); + } } +// error +// backend=stage2 +// target=native +// +// :14:17: error: incompatible types: '*align(0:0:1) u2' and '*align(2:8:2) u2' From d557dedf6cb836e11038faada436299547044ffc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 8 Jun 2022 15:38:15 -0700 Subject: [PATCH 1795/2031] add a missing align() to a behavior test --- test/behavior/bugs/6781.zig | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/behavior/bugs/6781.zig b/test/behavior/bugs/6781.zig index 8315e81c67..d35612b695 100644 --- a/test/behavior/bugs/6781.zig +++ b/test/behavior/bugs/6781.zig @@ -1,3 +1,4 @@ +const builtin = @import("builtin"); const std = @import("std"); const assert = std.debug.assert; @@ -61,9 +62,13 @@ pub const JournalHeader = packed struct { }; test "fixed" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - var buffer = [_]u8{0} ** 65536; + var buffer align(@alignOf(JournalHeader)) = [_]u8{0} ** 65536; var entry = std.mem.bytesAsValue(JournalHeader, buffer[0..@sizeOf(JournalHeader)]); entry.* = .{ .prev_hash_chain_root = 0, From 7c0614ea659b3f404f9d702c990afcac5f0b1479 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 8 Jun 2022 15:51:48 -0700 Subject: [PATCH 1796/2031] Sema: implement zirRetErrValueCode --- src/Sema.zig | 17 ++++++++++++----- test/behavior/defer.zig | 5 ++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 8ce9226bd4..d64b067771 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -771,7 +771,7 @@ fn analyzeBodyInner( .ptr_type => try sema.zirPtrType(block, inst), .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst), .ref => try sema.zirRef(block, inst), - .ret_err_value_code => try sema.zirRetErrValueCode(block, inst), + .ret_err_value_code => try sema.zirRetErrValueCode(inst), .shr => try sema.zirShr(block, inst, .shr), .shr_exact => try sema.zirShr(block, inst, .shr_exact), .slice_end => try sema.zirSliceEnd(block, inst), @@ -9250,10 +9250,17 @@ fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A return sema.analyzeDeclRef(embed_file.owner_decl); } -fn zirRetErrValueCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - _ = block; - _ = inst; - return sema.fail(block, sema.src, "TODO implement zirRetErrValueCode", .{}); +fn zirRetErrValueCode(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const inst_data = sema.code.instructions.items(.data)[inst].str_tok; + const err_name = inst_data.get(sema.code); + + // Return the error code from the function. + const kv = try sema.mod.getErrorValue(err_name); + const result_inst = try sema.addConstant( + try Type.Tag.error_set_single.create(sema.arena, kv.key), + try Value.Tag.@"error".create(sema.arena, .{ .name = kv.key }), + ); + return result_inst; } fn zirShl( diff --git a/test/behavior/defer.zig b/test/behavior/defer.zig index c8239395eb..70053ba01f 100644 --- a/test/behavior/defer.zig +++ b/test/behavior/defer.zig @@ -108,7 +108,10 @@ test "mixing normal and error defers" { } test "errdefer with payload" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { fn foo() !i32 { From f4d5fcde727d93da9ebe17a27cc62270545ccda6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 8 Jun 2022 20:40:16 -0700 Subject: [PATCH 1797/2031] AstGen: avoid redundant "ref" instructions Whenever a `ref` instruction is needed, it is created and saved in `AstGen.ref_table` instead of being immediately appended to the current block body. Then, when the referenced instruction is being added to the parent block (e.g. from setBlockBody), if it has a ref_table entry, then the ref instruction is added directly after the instruction being referenced. This makes sure two properties are upheld: 1. All pointers to the same locals return the same address. This is required to be compliant with the language specification. 2. `ref` instructions will dominate their uses. This is a required property of ZIR. A complication arises when a ref instruction refs another ref instruction. The logic in appendBodyWithFixups must take this into account, recursively handling ref refs. --- src/AstGen.zig | 228 ++++++++++++++++++++++++++++++----------- src/Zir.zig | 12 +++ test/behavior/eval.zig | 2 - 3 files changed, 182 insertions(+), 60 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 831071c479..3f22146f0e 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -45,6 +45,17 @@ fn_block: ?*GenZir = null, imports: std.AutoArrayHashMapUnmanaged(u32, Ast.TokenIndex) = .{}, /// Used for temporary storage when building payloads. scratch: std.ArrayListUnmanaged(u32) = .{}, +/// Whenever a `ref` instruction is needed, it is created and saved in this +/// table instead of being immediately appended to the current block body. +/// Then, when the instruction is being added to the parent block (typically from +/// setBlockBody), if it has a ref_table entry, then the ref instruction is added +/// there. This makes sure two properties are upheld: +/// 1. All pointers to the same locals return the same address. This is required +/// to be compliant with the language specification. +/// 2. `ref` instructions will dominate their uses. This is a required property +/// of ZIR. +/// The key is the ref operand; the value is the ref instruction. +ref_table: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{}, const InnerError = error{ OutOfMemory, AnalysisFail }; @@ -199,6 +210,7 @@ pub fn deinit(astgen: *AstGen, gpa: Allocator) void { astgen.compile_errors.deinit(gpa); astgen.imports.deinit(gpa); astgen.scratch.deinit(gpa); + astgen.ref_table.deinit(gpa); } pub const ResultLoc = union(enum) { @@ -4216,11 +4228,12 @@ fn structDeclInner( } const body = block_scope.instructionsSlice(); + const body_len = astgen.countBodyLenAfterFixups(body); try gz.setStruct(decl_inst, .{ .src_node = node, .layout = layout, - .body_len = @intCast(u32, body.len), + .body_len = body_len, .fields_len = field_count, .decls_len = decl_count, .known_non_opv = known_non_opv, @@ -4230,9 +4243,9 @@ fn structDeclInner( wip_members.finishBits(bits_per_field); const decls_slice = wip_members.declsSlice(); const fields_slice = wip_members.fieldsSlice(); - try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + body.len + fields_slice.len); + try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + body_len + fields_slice.len); astgen.extra.appendSliceAssumeCapacity(decls_slice); - astgen.extra.appendSliceAssumeCapacity(body); + astgen.appendBodyWithFixups(body); astgen.extra.appendSliceAssumeCapacity(fields_slice); block_scope.unstack(); @@ -4364,12 +4377,13 @@ fn unionDeclInner( } const body = block_scope.instructionsSlice(); + const body_len = astgen.countBodyLenAfterFixups(body); try gz.setUnion(decl_inst, .{ .src_node = node, .layout = layout, .tag_type = arg_inst, - .body_len = @intCast(u32, body.len), + .body_len = body_len, .fields_len = field_count, .decls_len = decl_count, .auto_enum_tag = have_auto_enum, @@ -4378,9 +4392,9 @@ fn unionDeclInner( wip_members.finishBits(bits_per_field); const decls_slice = wip_members.declsSlice(); const fields_slice = wip_members.fieldsSlice(); - try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + body.len + fields_slice.len); + try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + body_len + fields_slice.len); astgen.extra.appendSliceAssumeCapacity(decls_slice); - astgen.extra.appendSliceAssumeCapacity(body); + astgen.appendBodyWithFixups(body); astgen.extra.appendSliceAssumeCapacity(fields_slice); block_scope.unstack(); @@ -4616,12 +4630,13 @@ fn containerDecl( } const body = block_scope.instructionsSlice(); + const body_len = astgen.countBodyLenAfterFixups(body); try gz.setEnum(decl_inst, .{ .src_node = node, .nonexhaustive = nonexhaustive, .tag_type = arg_inst, - .body_len = @intCast(u32, body.len), + .body_len = body_len, .fields_len = @intCast(u32, counts.total_fields), .decls_len = @intCast(u32, counts.decls), }); @@ -4629,9 +4644,9 @@ fn containerDecl( wip_members.finishBits(bits_per_field); const decls_slice = wip_members.declsSlice(); const fields_slice = wip_members.fieldsSlice(); - try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + body.len + fields_slice.len); + try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + body_len + fields_slice.len); astgen.extra.appendSliceAssumeCapacity(decls_slice); - astgen.extra.appendSliceAssumeCapacity(body); + astgen.appendBodyWithFixups(body); astgen.extra.appendSliceAssumeCapacity(fields_slice); block_scope.unstack(); @@ -5403,10 +5418,12 @@ fn setCondBrPayload( const astgen = then_scope.astgen; const then_body = then_scope.instructionsSliceUpto(else_scope); const else_body = else_scope.instructionsSlice(); - const then_body_len = @intCast(u32, then_body.len + @boolToInt(then_break != 0)); - const else_body_len = @intCast(u32, else_body.len + @boolToInt(else_break != 0)); - try astgen.extra.ensureUnusedCapacity(astgen.gpa, @typeInfo(Zir.Inst.CondBr).Struct.fields.len + - then_body_len + else_body_len); + const then_body_len = astgen.countBodyLenAfterFixups(then_body) + @boolToInt(then_break != 0); + const else_body_len = astgen.countBodyLenAfterFixups(else_body) + @boolToInt(else_break != 0); + try astgen.extra.ensureUnusedCapacity( + astgen.gpa, + @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_body_len + else_body_len, + ); const zir_datas = astgen.instructions.items(.data); zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{ @@ -5414,9 +5431,9 @@ fn setCondBrPayload( .then_body_len = then_body_len, .else_body_len = else_body_len, }); - astgen.extra.appendSliceAssumeCapacity(then_body); + astgen.appendBodyWithFixups(then_body); if (then_break != 0) astgen.extra.appendAssumeCapacity(then_break); - astgen.extra.appendSliceAssumeCapacity(else_body); + astgen.appendBodyWithFixups(else_body); if (else_break != 0) astgen.extra.appendAssumeCapacity(else_break); } @@ -5437,10 +5454,12 @@ fn setCondBrPayloadElideBlockStorePtr( const else_body = else_scope.instructionsSlice(); const has_then_break = then_break != 0; const has_else_break = else_break != 0; - const then_body_len = @intCast(u32, then_body.len + @boolToInt(has_then_break)); - const else_body_len = @intCast(u32, else_body.len + @boolToInt(has_else_break)); - try astgen.extra.ensureUnusedCapacity(astgen.gpa, @typeInfo(Zir.Inst.CondBr).Struct.fields.len + - then_body_len + else_body_len); + const then_body_len = astgen.countBodyLenAfterFixups(then_body) + @boolToInt(has_then_break); + const else_body_len = astgen.countBodyLenAfterFixups(else_body) + @boolToInt(has_else_break); + try astgen.extra.ensureUnusedCapacity( + astgen.gpa, + @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_body_len + else_body_len, + ); const zir_tags = astgen.instructions.items(.tag); const zir_datas = astgen.instructions.items(.data); @@ -5475,7 +5494,7 @@ fn setCondBrPayloadElideBlockStorePtr( continue; } } - astgen.extra.appendAssumeCapacity(src_inst); + appendPossiblyRefdBodyInst(astgen, &astgen.extra, src_inst); } if (has_then_break) astgen.extra.appendAssumeCapacity(then_break); @@ -5495,7 +5514,7 @@ fn setCondBrPayloadElideBlockStorePtr( continue; } } - astgen.extra.appendAssumeCapacity(src_inst); + appendPossiblyRefdBodyInst(astgen, &astgen.extra, src_inst); } if (has_else_break) astgen.extra.appendAssumeCapacity(else_break); } @@ -6249,8 +6268,10 @@ fn switchExpr( } const case_slice = case_scope.instructionsSlice(); - payloads.items[body_len_index] = @intCast(u32, case_slice.len); - try payloads.appendSlice(gpa, case_slice); + const body_len = astgen.countBodyLenAfterFixups(case_slice); + try payloads.ensureUnusedCapacity(gpa, body_len); + payloads.items[body_len_index] = body_len; + appendBodyWithFixupsArrayList(astgen, payloads, case_slice); } } // Now that the item expressions are generated we can add this. @@ -7043,10 +7064,11 @@ fn typeOf( node: Ast.Node.Index, args: []const Ast.Node.Index, ) InnerError!Zir.Inst.Ref { + const astgen = gz.astgen; if (args.len < 1) { - return gz.astgen.failNode(node, "expected at least 1 argument, found 0", .{}); + return astgen.failNode(node, "expected at least 1 argument, found 0", .{}); } - const gpa = gz.astgen.gpa; + const gpa = astgen.gpa; if (args.len == 1) { const typeof_inst = try gz.makeBlockInst(.typeof_builtin, node); @@ -7065,7 +7087,7 @@ fn typeOf( return rvalue(gz, rl, indexToRef(typeof_inst), node); } const payload_size: u32 = std.meta.fields(Zir.Inst.TypeOfPeer).len; - const payload_index = try reserveExtra(gz.astgen, payload_size + args.len); + const payload_index = try reserveExtra(astgen, payload_size + args.len); var args_index = payload_index + payload_size; const typeof_inst = try gz.addExtendedMultiOpPayloadIndex(.typeof_peer, payload_index, args.len); @@ -7075,17 +7097,19 @@ fn typeOf( for (args) |arg, i| { const param_ref = try reachableExpr(&typeof_scope, &typeof_scope.base, .none, arg, node); - gz.astgen.extra.items[args_index + i] = @enumToInt(param_ref); + astgen.extra.items[args_index + i] = @enumToInt(param_ref); } _ = try typeof_scope.addBreak(.break_inline, refToIndex(typeof_inst).?, .void_value); const body = typeof_scope.instructionsSlice(); - gz.astgen.setExtra(payload_index, Zir.Inst.TypeOfPeer{ - .body_len = @intCast(u32, body.len), - .body_index = @intCast(u32, gz.astgen.extra.items.len), + const body_len = astgen.countBodyLenAfterFixups(body); + astgen.setExtra(payload_index, Zir.Inst.TypeOfPeer{ + .body_len = @intCast(u32, body_len), + .body_index = @intCast(u32, astgen.extra.items.len), .src_node = gz.nodeIndexToRelative(node), }); - try gz.astgen.extra.appendSlice(gpa, body); + try astgen.extra.ensureUnusedCapacity(gpa, body_len); + astgen.appendBodyWithFixups(body); typeof_scope.unstack(); return rvalue(gz, rl, typeof_inst, node); @@ -9032,9 +9056,22 @@ fn rvalue( }, .ref => { // We need a pointer but we have a value. - const tree = gz.astgen.tree; + // Unfortunately it's not quite as simple as directly emitting a ref + // instruction here because we need subsequent address-of operator on + // const locals to return the same address. + const astgen = gz.astgen; + const tree = astgen.tree; const src_token = tree.firstToken(src_node); - return gz.addUnTok(.ref, result, src_token); + const result_index = refToIndex(result) orelse + return gz.addUnTok(.ref, result, src_token); + const zir_tags = gz.astgen.instructions.items(.tag); + if (zir_tags[result_index].isParam()) + return gz.addUnTok(.ref, result, src_token); + const gop = try astgen.ref_table.getOrPut(astgen.gpa, result_index); + if (!gop.found_existing) { + gop.value_ptr.* = try gz.makeUnTok(.ref, result, src_token); + } + return indexToRef(gop.value_ptr.*); }, .ty => |ty_inst| { // Quickly eliminate some common, unnecessary type coercion. @@ -9976,43 +10013,58 @@ const GenZir = struct { /// Assumes nothing stacked on `gz`. Unstacks `gz`. fn setBoolBrBody(gz: *GenZir, inst: Zir.Inst.Index) !void { - const gpa = gz.astgen.gpa; + const astgen = gz.astgen; + const gpa = astgen.gpa; const body = gz.instructionsSlice(); - try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Block).Struct.fields.len + body.len); - const zir_datas = gz.astgen.instructions.items(.data); - zir_datas[inst].bool_br.payload_index = gz.astgen.addExtraAssumeCapacity( - Zir.Inst.Block{ .body_len = @intCast(u32, body.len) }, + const body_len = astgen.countBodyLenAfterFixups(body); + try astgen.extra.ensureUnusedCapacity( + gpa, + @typeInfo(Zir.Inst.Block).Struct.fields.len + body_len, ); - gz.astgen.extra.appendSliceAssumeCapacity(body); + const zir_datas = astgen.instructions.items(.data); + zir_datas[inst].bool_br.payload_index = astgen.addExtraAssumeCapacity( + Zir.Inst.Block{ .body_len = body_len }, + ); + astgen.appendBodyWithFixups(body); gz.unstack(); } /// Assumes nothing stacked on `gz`. Unstacks `gz`. fn setBlockBody(gz: *GenZir, inst: Zir.Inst.Index) !void { - const gpa = gz.astgen.gpa; + const astgen = gz.astgen; + const gpa = astgen.gpa; const body = gz.instructionsSlice(); - try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Block).Struct.fields.len + body.len); - const zir_datas = gz.astgen.instructions.items(.data); - zir_datas[inst].pl_node.payload_index = gz.astgen.addExtraAssumeCapacity( - Zir.Inst.Block{ .body_len = @intCast(u32, body.len) }, + const body_len = astgen.countBodyLenAfterFixups(body); + try astgen.extra.ensureUnusedCapacity( + gpa, + @typeInfo(Zir.Inst.Block).Struct.fields.len + body_len, ); - gz.astgen.extra.appendSliceAssumeCapacity(body); + const zir_datas = astgen.instructions.items(.data); + zir_datas[inst].pl_node.payload_index = astgen.addExtraAssumeCapacity( + Zir.Inst.Block{ .body_len = body_len }, + ); + astgen.appendBodyWithFixups(body); gz.unstack(); } /// Assumes nothing stacked on `gz`. Unstacks `gz`. fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void { - const gpa = gz.astgen.gpa; + const astgen = gz.astgen; + const gpa = astgen.gpa; const body = gz.instructionsSlice(); - try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Try).Struct.fields.len + body.len); - const zir_datas = gz.astgen.instructions.items(.data); - zir_datas[inst].pl_node.payload_index = gz.astgen.addExtraAssumeCapacity( + const body_len = astgen.countBodyLenAfterFixups(body); + try astgen.extra.ensureUnusedCapacity( + gpa, + @typeInfo(Zir.Inst.Try).Struct.fields.len + body_len, + ); + const zir_datas = astgen.instructions.items(.data); + zir_datas[inst].pl_node.payload_index = astgen.addExtraAssumeCapacity( Zir.Inst.Try{ .operand = operand, - .body_len = @intCast(u32, body.len), + .body_len = body_len, }, ); - gz.astgen.extra.appendSliceAssumeCapacity(body); + astgen.appendBodyWithFixups(body); gz.unstack(); } @@ -10089,6 +10141,7 @@ const GenZir = struct { if (args.ret_gz) |ret_gz| ret_body = ret_gz.instructionsSlice(); } + const body_len = astgen.countBodyLenAfterFixups(body); if (args.cc_ref != .none or args.lib_name != 0 or args.is_var_args or args.is_test or args.is_extern or @@ -10114,13 +10167,13 @@ const GenZir = struct { fancyFnExprExtraLen(section_body, args.section_ref) + fancyFnExprExtraLen(cc_body, args.cc_ref) + fancyFnExprExtraLen(ret_body, ret_ref) + - body.len + src_locs.len + + body_len + src_locs.len + @boolToInt(args.lib_name != 0) + @boolToInt(args.noalias_bits != 0), ); const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{ .param_block = args.param_block, - .body_len = @intCast(u32, body.len), + .body_len = body_len, .bits = .{ .is_var_args = args.is_var_args, .is_inferred_error = args.is_inferred_error, @@ -10187,7 +10240,7 @@ const GenZir = struct { astgen.extra.appendAssumeCapacity(args.noalias_bits); } - astgen.extra.appendSliceAssumeCapacity(body); + astgen.appendBodyWithFixups(body); astgen.extra.appendSliceAssumeCapacity(src_locs); // Order is important when unstacking. @@ -10214,9 +10267,9 @@ const GenZir = struct { } else { try astgen.extra.ensureUnusedCapacity( gpa, - @typeInfo(Zir.Inst.Func).Struct.fields.len + + @typeInfo(Zir.Inst.Func).Struct.fields.len + 1 + @maximum(ret_body.len, @boolToInt(ret_ref != .none)) + - body.len + src_locs.len, + body_len + src_locs.len, ); const ret_body_len = if (ret_body.len != 0) @intCast(u32, ret_body.len) @@ -10226,7 +10279,7 @@ const GenZir = struct { const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.Func{ .param_block = args.param_block, .ret_body_len = ret_body_len, - .body_len = @intCast(u32, body.len), + .body_len = body_len, }); const zir_datas = astgen.instructions.items(.data); if (ret_body.len != 0) { @@ -10235,7 +10288,7 @@ const GenZir = struct { } else if (ret_ref != .none) { astgen.extra.appendAssumeCapacity(@enumToInt(ret_ref)); } - astgen.extra.appendSliceAssumeCapacity(body); + astgen.appendBodyWithFixups(body); astgen.extra.appendSliceAssumeCapacity(src_locs); // Order is important when unstacking. @@ -10593,6 +10646,26 @@ const GenZir = struct { }); } + fn makeUnTok( + gz: *GenZir, + tag: Zir.Inst.Tag, + operand: Zir.Inst.Ref, + /// Absolute token index. This function does the conversion to Decl offset. + abs_tok_index: Ast.TokenIndex, + ) !Zir.Inst.Index { + const astgen = gz.astgen; + const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len); + assert(operand != .none); + try astgen.instructions.append(astgen.gpa, .{ + .tag = tag, + .data = .{ .un_tok = .{ + .operand = operand, + .src_tok = gz.tokenIndexToRelative(abs_tok_index), + } }, + }); + return new_index; + } + fn addStrTok( gz: *GenZir, tag: Zir.Inst.Tag, @@ -11337,3 +11410,42 @@ fn isInferred(astgen: *AstGen, ref: Zir.Inst.Ref) bool { else => false, }; } + +/// Assumes capacity for body has already been added. Needed capacity taking into +/// account fixups can be found with `countBodyLenAfterFixups`. +fn appendBodyWithFixups(astgen: *AstGen, body: []const Zir.Inst.Index) void { + return appendBodyWithFixupsArrayList(astgen, &astgen.extra, body); +} + +fn appendBodyWithFixupsArrayList( + astgen: *AstGen, + list: *std.ArrayListUnmanaged(u32), + body: []const Zir.Inst.Index, +) void { + for (body) |body_inst| { + appendPossiblyRefdBodyInst(astgen, list, body_inst); + } +} + +fn appendPossiblyRefdBodyInst( + astgen: *AstGen, + list: *std.ArrayListUnmanaged(u32), + body_inst: Zir.Inst.Index, +) void { + list.appendAssumeCapacity(body_inst); + const kv = astgen.ref_table.fetchRemove(body_inst) orelse return; + const ref_inst = kv.value; + return appendPossiblyRefdBodyInst(astgen, list, ref_inst); +} + +fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 { + var count = body.len; + for (body) |body_inst| { + var check_inst = body_inst; + while (astgen.ref_table.get(check_inst)) |ref_inst| { + count += 1; + check_inst = ref_inst; + } + } + return @intCast(u32, count); +} diff --git a/src/Zir.zig b/src/Zir.zig index 98e4167355..370f996fd5 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -1271,6 +1271,18 @@ pub const Inst = struct { }; } + pub fn isParam(tag: Tag) bool { + return switch (tag) { + .param, + .param_comptime, + .param_anytype, + .param_anytype_comptime, + => true, + + else => false, + }; + } + /// AstGen uses this to find out if `Ref.void_value` should be used in place /// of the result of a given instruction. This allows Sema to forego adding /// the instruction to the map after analysis. diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index f14a25451d..220768c820 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -1191,8 +1191,6 @@ test "no dependency loop for alignment of self tagged union" { } test "equality of pointers to comptime const" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - const a: i32 = undefined; comptime assert(&a == &a); } From f1cff4fa4a28d42ac9055f94ee9a8f7fd2831cd7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Jun 2022 15:09:16 -0700 Subject: [PATCH 1798/2031] Sema: avoid use of undefined value for generic fn calls I saw some issues in Valgrind which are fixed after this commit. --- src/Sema.zig | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index d64b067771..2d3c38faeb 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7026,9 +7026,14 @@ fn funcCommon( }); }; - // stage1 bug workaround - const cc_workaround = cc orelse undefined; - const align_workaround = alignment orelse @as(u32, undefined); + // These locals are pulled out from the init expression below to work around + // a stage1 compiler bug. + // In the case of generic calling convention, or generic alignment, we use + // default values which are only meaningful for the generic function, *not* + // the instantiation, which can depend on comptime parameters. + // Related proposal: https://github.com/ziglang/zig/issues/11834 + const cc_workaround = cc orelse .Unspecified; + const align_workaround = alignment orelse 0; break :fn_ty try Type.Tag.function.create(sema.arena, .{ .param_types = param_types, From bac132bc8fb320620ca5ad554ce515ccb7e4dad1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 7 Jun 2022 17:48:53 -0700 Subject: [PATCH 1799/2031] introduce std.debug.Trace And use it to debug a LazySrcLoc in stage2 that is set to a bogus value. The actual fix in this commit is: ```diff - try sema.emitBackwardBranch(&child_block, call_src); + try sema.emitBackwardBranch(block, call_src); ``` --- lib/std/debug.zig | 57 ++++++++++++++++++++++++ src/Module.zig | 93 ++++++++++++++++++++++++++------------- src/Sema.zig | 90 ++++++++++++++++++------------------- src/Zir.zig | 10 ++--- src/arch/wasm/CodeGen.zig | 2 +- src/codegen/c.zig | 2 +- src/codegen/llvm.zig | 2 +- src/codegen/spirv.zig | 4 +- src/print_zir.zig | 20 ++++----- 9 files changed, 185 insertions(+), 95 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 7fb632084b..f4ca0f0354 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1943,3 +1943,60 @@ test "#4353: std.debug should manage resources correctly" { noinline fn showMyTrace() usize { return @returnAddress(); } + +pub fn Trace(comptime size: usize, comptime stack_frame_count: usize) type { + return struct { + addrs: [size][stack_frame_count]usize = undefined, + notes: [size][]const u8 = undefined, + index: usize = 0, + + const frames_init = [1]usize{0} ** stack_frame_count; + + pub noinline fn add(t: *@This(), note: []const u8) void { + return addAddr(t, @returnAddress(), note); + } + + pub fn addAddr(t: *@This(), addr: usize, note: []const u8) void { + if (t.index < size) { + t.notes[t.index] = note; + t.addrs[t.index] = [1]usize{0} ** stack_frame_count; + var stack_trace: std.builtin.StackTrace = .{ + .index = 0, + .instruction_addresses = &t.addrs[t.index], + }; + captureStackTrace(addr, &stack_trace); + } + // Keep counting even if the end is reached so that the + // user can find out how much more size they need. + t.index += 1; + } + + pub fn dump(t: @This()) void { + const tty_config = detectTTYConfig(); + const stderr = io.getStdErr().writer(); + const end = @maximum(t.index, size); + const debug_info = getSelfDebugInfo() catch |err| { + stderr.print( + "Unable to dump stack trace: Unable to open debug info: {s}\n", + .{@errorName(err)}, + ) catch return; + return; + }; + for (t.addrs[0..end]) |frames_array, i| { + stderr.print("{s}:\n", .{t.notes[i]}) catch return; + var frames_array_mutable = frames_array; + const frames = mem.sliceTo(frames_array_mutable[0..], 0); + const stack_trace: std.builtin.StackTrace = .{ + .index = frames.len, + .instruction_addresses = frames, + }; + writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, tty_config) catch continue; + } + if (t.index > end) { + stderr.print("{d} more traces not shown; consider increasing trace size\n", .{ + t.index - end, + }) catch return; + } + } + }; +} diff --git a/src/Module.zig b/src/Module.zig index 12d311046a..724fa2ebc4 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -659,7 +659,7 @@ pub const Decl = struct { } pub fn nodeSrcLoc(decl: Decl, node_index: Ast.Node.Index) LazySrcLoc { - return .{ .node_offset = decl.nodeIndexToRelative(node_index) }; + return LazySrcLoc.nodeOffset(decl.nodeIndexToRelative(node_index)); } pub fn srcLoc(decl: Decl) SrcLoc { @@ -670,7 +670,7 @@ pub const Decl = struct { return .{ .file_scope = decl.getFileScope(), .parent_decl_node = decl.src_node, - .lazy = .{ .node_offset = node_offset }, + .lazy = LazySrcLoc.nodeOffset(node_offset), }; } @@ -861,7 +861,7 @@ pub const ErrorSet = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = .{ .node_offset = self.node_offset }, + .lazy = LazySrcLoc.nodeOffset(self.node_offset), }; } @@ -947,7 +947,7 @@ pub const Struct = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = .{ .node_offset = s.node_offset }, + .lazy = LazySrcLoc.nodeOffset(s.node_offset), }; } @@ -1066,7 +1066,7 @@ pub const EnumSimple = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = .{ .node_offset = self.node_offset }, + .lazy = LazySrcLoc.nodeOffset(self.node_offset), }; } }; @@ -1097,7 +1097,7 @@ pub const EnumNumbered = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = .{ .node_offset = self.node_offset }, + .lazy = LazySrcLoc.nodeOffset(self.node_offset), }; } }; @@ -1131,7 +1131,7 @@ pub const EnumFull = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = .{ .node_offset = self.node_offset }, + .lazy = LazySrcLoc.nodeOffset(self.node_offset), }; } }; @@ -1197,7 +1197,7 @@ pub const Union = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = .{ .node_offset = self.node_offset }, + .lazy = LazySrcLoc.nodeOffset(self.node_offset), }; } @@ -1404,7 +1404,7 @@ pub const Opaque = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = .{ .node_offset = self.node_offset }, + .lazy = LazySrcLoc.nodeOffset(self.node_offset), }; } @@ -2105,7 +2105,17 @@ pub const SrcLoc = struct { const token_starts = tree.tokens.items(.start); return token_starts[tok_index]; }, - .node_offset, .node_offset_bin_op => |node_off| { + .node_offset => |traced_off| { + const node_off = traced_off.x; + const tree = try src_loc.file_scope.getTree(gpa); + const node = src_loc.declRelativeToNodeIndex(node_off); + assert(src_loc.file_scope.tree_loaded); + const main_tokens = tree.nodes.items(.main_token); + const tok_index = main_tokens[node]; + const token_starts = tree.tokens.items(.start); + return token_starts[tok_index]; + }, + .node_offset_bin_op => |node_off| { const tree = try src_loc.file_scope.getTree(gpa); const node = src_loc.declRelativeToNodeIndex(node_off); assert(src_loc.file_scope.tree_loaded); @@ -2515,6 +2525,17 @@ pub const SrcLoc = struct { } }; +/// This wraps a simple integer in debug builds so that later on we can find out +/// where in semantic analysis the value got set. +const TracedOffset = struct { + x: i32, + trace: Trace = trace_init, + + const want_tracing = builtin.mode == .Debug; + const trace_init = if (want_tracing) std.debug.Trace(1, 3){} else {}; + const Trace = @TypeOf(trace_init); +}; + /// Resolving a source location into a byte offset may require doing work /// that we would rather not do unless the error actually occurs. /// Therefore we need a data structure that contains the information necessary @@ -2555,7 +2576,7 @@ pub const LazySrcLoc = union(enum) { /// The source location points to an AST node, which is this value offset /// from its containing Decl node AST index. /// The Decl is determined contextually. - node_offset: i32, + node_offset: TracedOffset, /// The source location points to two tokens left of the first token of an AST node, /// which is this value offset from its containing Decl node AST index. /// The Decl is determined contextually. @@ -2705,6 +2726,18 @@ pub const LazySrcLoc = union(enum) { /// The Decl is determined contextually. node_offset_array_type_elem: i32, + pub const nodeOffset = if (TracedOffset.want_tracing) nodeOffsetDebug else nodeOffsetRelease; + + noinline fn nodeOffsetDebug(node_offset: i32) LazySrcLoc { + var result: LazySrcLoc = .{ .node_offset = .{ .x = node_offset } }; + result.node_offset.trace.addAddr(@returnAddress(), "init"); + return result; + } + + fn nodeOffsetRelease(node_offset: i32) LazySrcLoc { + return .{ .node_offset = .{ .x = node_offset } }; + } + /// Upgrade to a `SrcLoc` based on the `Decl` provided. pub fn toSrcLoc(lazy: LazySrcLoc, decl: *Decl) SrcLoc { return switch (lazy) { @@ -4014,7 +4047,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { const body = zir.extra[extra.end..][0..extra.data.body_len]; const result_ref = (try sema.analyzeBodyBreak(&block_scope, body)).?.operand; try wip_captures.finalize(); - const src: LazySrcLoc = .{ .node_offset = 0 }; + const src = LazySrcLoc.nodeOffset(0); const decl_tv = try sema.resolveInstValue(&block_scope, src, result_ref); const decl_align: u32 = blk: { const align_ref = decl.zirAlignRef(); @@ -5044,7 +5077,7 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air { // Crucially, this happens *after* we set the function state to success above, // so that dependencies on the function body will now be satisfied rather than // result in circular dependency errors. - const src: LazySrcLoc = .{ .node_offset = 0 }; + const src = LazySrcLoc.nodeOffset(0); sema.resolveFnTypes(&inner_block, src, fn_ty_info) catch |err| switch (err) { error.NeededSourceLocation => unreachable, error.GenericPoison => unreachable, @@ -5338,7 +5371,7 @@ pub const SwitchProngSrc = union(enum) { log.warn("unable to load {s}: {s}", .{ decl.getFileScope().sub_file_path, @errorName(err), }); - return LazySrcLoc{ .node_offset = 0 }; + return LazySrcLoc.nodeOffset(0); }; const switch_node = decl.relativeToNodeIndex(switch_node_offset); const main_tokens = tree.nodes.items(.main_token); @@ -5367,17 +5400,17 @@ pub const SwitchProngSrc = union(enum) { node_tags[case.ast.values[0]] == .switch_range; switch (prong_src) { - .scalar => |i| if (!is_multi and i == scalar_i) return LazySrcLoc{ - .node_offset = decl.nodeIndexToRelative(case.ast.values[0]), - }, + .scalar => |i| if (!is_multi and i == scalar_i) return LazySrcLoc.nodeOffset( + decl.nodeIndexToRelative(case.ast.values[0]), + ), .multi => |s| if (is_multi and s.prong == multi_i) { var item_i: u32 = 0; for (case.ast.values) |item_node| { if (node_tags[item_node] == .switch_range) continue; - if (item_i == s.item) return LazySrcLoc{ - .node_offset = decl.nodeIndexToRelative(item_node), - }; + if (item_i == s.item) return LazySrcLoc.nodeOffset( + decl.nodeIndexToRelative(item_node), + ); item_i += 1; } else unreachable; }, @@ -5387,15 +5420,15 @@ pub const SwitchProngSrc = union(enum) { if (node_tags[range] != .switch_range) continue; if (range_i == s.item) switch (range_expand) { - .none => return LazySrcLoc{ - .node_offset = decl.nodeIndexToRelative(range), - }, - .first => return LazySrcLoc{ - .node_offset = decl.nodeIndexToRelative(node_datas[range].lhs), - }, - .last => return LazySrcLoc{ - .node_offset = decl.nodeIndexToRelative(node_datas[range].rhs), - }, + .none => return LazySrcLoc.nodeOffset( + decl.nodeIndexToRelative(range), + ), + .first => return LazySrcLoc.nodeOffset( + decl.nodeIndexToRelative(node_datas[range].lhs), + ), + .last => return LazySrcLoc.nodeOffset( + decl.nodeIndexToRelative(node_datas[range].rhs), + ), }; range_i += 1; } else unreachable; @@ -5450,7 +5483,7 @@ pub const PeerTypeCandidateSrc = union(enum) { log.warn("unable to load {s}: {s}", .{ decl.getFileScope().sub_file_path, @errorName(err), }); - return LazySrcLoc{ .node_offset = 0 }; + return LazySrcLoc.nodeOffset(0); }; const node = decl.relativeToNodeIndex(node_offset); const node_datas = tree.nodes.items(.data); diff --git a/src/Sema.zig b/src/Sema.zig index 2d3c38faeb..882c18fe55 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1154,7 +1154,7 @@ fn analyzeBodyInner( .repeat => { if (block.is_comptime) { // Send comptime control flow back to the beginning of this block. - const src: LazySrcLoc = .{ .node_offset = datas[inst].node }; + const src = LazySrcLoc.nodeOffset(datas[inst].node); try sema.emitBackwardBranch(block, src); if (wip_captures.scope.captures.count() != orig_captures) { try wip_captures.reset(parent_capture_scope); @@ -1165,14 +1165,14 @@ fn analyzeBodyInner( continue; } else { const src_node = sema.code.instructions.items(.data)[inst].node; - const src: LazySrcLoc = .{ .node_offset = src_node }; + const src = LazySrcLoc.nodeOffset(src_node); try sema.requireRuntimeBlock(block, src); break always_noreturn; } }, .repeat_inline => { // Send comptime control flow back to the beginning of this block. - const src: LazySrcLoc = .{ .node_offset = datas[inst].node }; + const src = LazySrcLoc.nodeOffset(datas[inst].node); try sema.emitBackwardBranch(block, src); if (wip_captures.scope.captures.count() != orig_captures) { try wip_captures.reset(parent_capture_scope); @@ -2087,7 +2087,7 @@ fn zirStructDecl( const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); const src: LazySrcLoc = if (small.has_src_node) blk: { const node_offset = @bitCast(i32, sema.code.extra[extended.operand]); - break :blk .{ .node_offset = node_offset }; + break :blk LazySrcLoc.nodeOffset(node_offset); } else sema.src; var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); @@ -2108,7 +2108,7 @@ fn zirStructDecl( struct_obj.* = .{ .owner_decl = new_decl_index, .fields = .{}, - .node_offset = src.node_offset, + .node_offset = src.node_offset.x, .zir_index = inst, .layout = small.layout, .status = .none, @@ -2210,7 +2210,7 @@ fn zirEnumDecl( const src: LazySrcLoc = if (small.has_src_node) blk: { const node_offset = @bitCast(i32, sema.code.extra[extra_index]); extra_index += 1; - break :blk .{ .node_offset = node_offset }; + break :blk LazySrcLoc.nodeOffset(node_offset); } else sema.src; const tag_type_ref = if (small.has_tag_type) blk: { @@ -2263,7 +2263,7 @@ fn zirEnumDecl( .tag_ty_inferred = true, .fields = .{}, .values = .{}, - .node_offset = src.node_offset, + .node_offset = src.node_offset.x, .namespace = .{ .parent = block.namespace, .ty = enum_ty, @@ -2385,8 +2385,8 @@ fn zirEnumDecl( const gop = enum_obj.fields.getOrPutAssumeCapacity(field_name); if (gop.found_existing) { const tree = try sema.getAstTree(block); - const field_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset, field_i); - const other_tag_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset, gop.index); + const field_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset.x, field_i); + const other_tag_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset.x, gop.index); const msg = msg: { const msg = try sema.errMsg(block, field_src, "duplicate enum tag", .{}); errdefer msg.destroy(gpa); @@ -2442,7 +2442,7 @@ fn zirUnionDecl( const src: LazySrcLoc = if (small.has_src_node) blk: { const node_offset = @bitCast(i32, sema.code.extra[extra_index]); extra_index += 1; - break :blk .{ .node_offset = node_offset }; + break :blk LazySrcLoc.nodeOffset(node_offset); } else sema.src; extra_index += @boolToInt(small.has_tag_type); @@ -2480,7 +2480,7 @@ fn zirUnionDecl( .owner_decl = new_decl_index, .tag_ty = Type.initTag(.@"null"), .fields = .{}, - .node_offset = src.node_offset, + .node_offset = src.node_offset.x, .zir_index = inst, .layout = small.layout, .status = .none, @@ -2516,7 +2516,7 @@ fn zirOpaqueDecl( const src: LazySrcLoc = if (small.has_src_node) blk: { const node_offset = @bitCast(i32, sema.code.extra[extra_index]); extra_index += 1; - break :blk .{ .node_offset = node_offset }; + break :blk LazySrcLoc.nodeOffset(node_offset); } else sema.src; const decls_len = if (small.has_decls_len) blk: { @@ -2547,7 +2547,7 @@ fn zirOpaqueDecl( opaque_obj.* = .{ .owner_decl = new_decl_index, - .node_offset = src.node_offset, + .node_offset = src.node_offset.x, .namespace = .{ .parent = block.namespace, .ty = opaque_ty, @@ -2623,7 +2623,7 @@ fn zirRetPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].node; - const src: LazySrcLoc = .{ .node_offset = inst_data }; + const src = LazySrcLoc.nodeOffset(inst_data); try sema.requireFunctionBlock(block, src); if (block.is_comptime or try sema.typeRequiresComptime(block, src, sema.fn_ret_ty)) { @@ -2661,7 +2661,7 @@ fn zirRetType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].node; - const src: LazySrcLoc = .{ .node_offset = inst_data }; + const src = LazySrcLoc.nodeOffset(inst_data); try sema.requireFunctionBlock(block, src); return sema.addType(sema.fn_ret_ty); } @@ -2750,7 +2750,7 @@ fn zirAllocExtended( extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand); - const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; + const src = LazySrcLoc.nodeOffset(extra.data.src_node); const ty_src = src; // TODO better source location const align_src = src; // TODO better source location const small = @bitCast(Zir.Inst.AllocExtended.Small, extended.small); @@ -2903,7 +2903,7 @@ fn zirAllocInferredComptime( inferred_alloc_ty: Type, ) CompileError!Air.Inst.Ref { const src_node = sema.code.instructions.items(.data)[inst].node; - const src: LazySrcLoc = .{ .node_offset = src_node }; + const src = LazySrcLoc.nodeOffset(src_node); sema.src = src; return sema.addConstant( inferred_alloc_ty, @@ -2967,7 +2967,7 @@ fn zirAllocInferred( defer tracy.end(); const src_node = sema.code.instructions.items(.data)[inst].node; - const src: LazySrcLoc = .{ .node_offset = src_node }; + const src = LazySrcLoc.nodeOffset(src_node); sema.src = src; if (block.is_comptime) { @@ -3718,7 +3718,7 @@ fn zirValidateArrayInit( outer: for (instrs) |elem_ptr, i| { const elem_ptr_data = sema.code.instructions.items(.data)[elem_ptr].pl_node; - const elem_src: LazySrcLoc = .{ .node_offset = elem_ptr_data.src_node }; + const elem_src = LazySrcLoc.nodeOffset(elem_ptr_data.src_node); // Determine whether the value stored to this pointer is comptime-known. @@ -4203,7 +4203,7 @@ fn zirCompileLog( const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); const src_node = extra.data.src_node; - const src: LazySrcLoc = .{ .node_offset = src_node }; + const src = LazySrcLoc.nodeOffset(src_node); const args = sema.code.refSlice(extra.end, extended.small); for (args) |arg_ref, i| { @@ -4707,7 +4707,7 @@ pub fn analyzeExport( fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; - const src: LazySrcLoc = .{ .node_offset = extra.node }; + const src = LazySrcLoc.nodeOffset(extra.node); const alignment = try sema.resolveAlign(block, operand_src, extra.operand); if (alignment > 256) { return sema.fail(block, src, "attempt to @setAlignStack({d}); maximum is 256", .{ @@ -5312,7 +5312,7 @@ fn analyzeCall( delete_memoized_call_key = true; } - try sema.emitBackwardBranch(&child_block, call_src); + try sema.emitBackwardBranch(block, call_src); // Whether this call should be memoized, set to false if the call can mutate // comptime state. @@ -6988,7 +6988,7 @@ fn funcCommon( const param_types = try sema.arena.alloc(Type, block.params.items.len); const comptime_params = try sema.arena.alloc(bool, block.params.items.len); for (block.params.items) |param, i| { - const param_src: LazySrcLoc = .{ .node_offset = src_node_offset }; // TODO better src + const param_src = LazySrcLoc.nodeOffset(src_node_offset); // TODO better src param_types[i] = param.ty; comptime_params[i] = param.is_comptime or try sema.typeRequiresComptime(block, param_src, param.ty); @@ -7378,7 +7378,7 @@ fn zirFieldCallBindNamed(sema: *Sema, block: *Block, extended: Zir.Inst.Extended defer tracy.end(); const extra = sema.code.extraData(Zir.Inst.FieldNamedNode, extended.operand).data; - const src: LazySrcLoc = .{ .node_offset = extra.node }; + const src = LazySrcLoc.nodeOffset(extra.node); const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; const object_ptr = try sema.resolveInst(extra.lhs); const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); @@ -10088,7 +10088,7 @@ fn zirOverflowArithmetic( defer tracy.end(); const extra = sema.code.extraData(Zir.Inst.OverflowArithmetic, extended.operand).data; - const src: LazySrcLoc = .{ .node_offset = extra.node }; + const src = LazySrcLoc.nodeOffset(extra.node); const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; @@ -11309,7 +11309,7 @@ fn zirAsm( defer tracy.end(); const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand); - const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; + const src = LazySrcLoc.nodeOffset(extra.data.src_node); const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = extra.data.src_node }; const outputs_len = @truncate(u5, extended.small); const inputs_len = @truncate(u5, extended.small >> 5); @@ -11761,7 +11761,7 @@ fn zirThis( extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { const this_decl_index = block.namespace.getDeclIndex(); - const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; + const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand)); return sema.analyzeDeclVal(block, src, this_decl_index); } @@ -11815,7 +11815,7 @@ fn zirRetAddr( block: *Block, extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { - const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; + const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand)); try sema.requireRuntimeBlock(block, src); return try block.addNoOp(.ret_addr); } @@ -11825,7 +11825,7 @@ fn zirFrameAddress( block: *Block, extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { - const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; + const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand)); try sema.requireRuntimeBlock(block, src); return try block.addNoOp(.frame_addr); } @@ -11838,7 +11838,7 @@ fn zirBuiltinSrc( const tracy = trace(@src()); defer tracy.end(); - const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; + const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand)); const extra = sema.code.extraData(Zir.Inst.LineColumn, extended.operand).data; const func = sema.func orelse return sema.fail(block, src, "@src outside function", .{}); const fn_owner_decl = sema.mod.declPtr(func.owner_decl); @@ -12842,7 +12842,7 @@ fn zirTypeofPeer( defer tracy.end(); const extra = sema.code.extraData(Zir.Inst.TypeOfPeer, extended.operand); - const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; + const src = LazySrcLoc.nodeOffset(extra.data.src_node); const body = sema.code.extra[extra.data.body_index..][0..extra.data.body_len]; var child_block: Block = .{ @@ -14157,7 +14157,7 @@ fn zirErrorReturnTrace( block: *Block, extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { - const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; + const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand)); return sema.getErrorReturnTrace(block, src); } @@ -14185,7 +14185,7 @@ fn zirFrame( block: *Block, extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { - const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; + const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand)); return sema.fail(block, src, "TODO: Sema.zirFrame", .{}); } @@ -14629,7 +14629,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I .tag_ty_inferred = false, .fields = .{}, .values = .{}, - .node_offset = src.node_offset, + .node_offset = src.node_offset.x, .namespace = .{ .parent = block.namespace, .ty = enum_ty, @@ -14711,7 +14711,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I opaque_obj.* = .{ .owner_decl = new_decl_index, - .node_offset = src.node_offset, + .node_offset = src.node_offset.x, .namespace = .{ .parent = block.namespace, .ty = opaque_ty, @@ -14763,7 +14763,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I .owner_decl = new_decl_index, .tag_ty = Type.initTag(.@"null"), .fields = .{}, - .node_offset = src.node_offset, + .node_offset = src.node_offset.x, .zir_index = inst, .layout = layout_val.toEnum(std.builtin.Type.ContainerLayout), .status = .have_field_types, @@ -14930,7 +14930,7 @@ fn reifyStruct( struct_obj.* = .{ .owner_decl = new_decl_index, .fields = .{}, - .node_offset = src.node_offset, + .node_offset = src.node_offset.x, .zir_index = inst, .layout = layout_val.toEnum(std.builtin.Type.ContainerLayout), .status = .have_field_types, @@ -15130,7 +15130,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; - const src: LazySrcLoc = .{ .node_offset = extra.node }; + const src = LazySrcLoc.nodeOffset(extra.node); const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); @@ -17114,7 +17114,7 @@ fn zirAwaitNosuspend( extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; - const src: LazySrcLoc = .{ .node_offset = extra.node }; + const src = LazySrcLoc.nodeOffset(extra.node); return sema.fail(block, src, "TODO: Sema.zirAwaitNosuspend", .{}); } @@ -17443,7 +17443,7 @@ fn zirWasmMemorySize( ) CompileError!Air.Inst.Ref { const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; - const builtin_src: LazySrcLoc = .{ .node_offset = extra.node }; + const builtin_src = LazySrcLoc.nodeOffset(extra.node); const target = sema.mod.getTarget(); if (!target.isWasm()) { return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); @@ -17466,7 +17466,7 @@ fn zirWasmMemoryGrow( extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; - const builtin_src: LazySrcLoc = .{ .node_offset = extra.node }; + const builtin_src = LazySrcLoc.nodeOffset(extra.node); const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const delta_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; const target = sema.mod.getTarget(); @@ -17534,7 +17534,7 @@ fn zirBuiltinExtern( extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; - const src: LazySrcLoc = .{ .node_offset = extra.node }; + const src = LazySrcLoc.nodeOffset(extra.node); const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; @@ -23586,7 +23586,7 @@ fn semaStructFields( const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); var extra_index: usize = extended.operand; - const src: LazySrcLoc = .{ .node_offset = struct_obj.node_offset }; + const src = LazySrcLoc.nodeOffset(struct_obj.node_offset); extra_index += @boolToInt(small.has_src_node); const body_len = if (small.has_body_len) blk: { @@ -23773,7 +23773,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small); var extra_index: usize = extended.operand; - const src: LazySrcLoc = .{ .node_offset = union_obj.node_offset }; + const src = LazySrcLoc.nodeOffset(union_obj.node_offset); extra_index += @boolToInt(small.has_src_node); const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: { @@ -24459,7 +24459,7 @@ fn enumFieldSrcLoc( .container_field, => { if (it_index == field_index) { - return .{ .node_offset = decl.nodeIndexToRelative(member_node) }; + return LazySrcLoc.nodeOffset(decl.nodeIndexToRelative(member_node)); } it_index += 1; }, diff --git a/src/Zir.zig b/src/Zir.zig index 370f996fd5..1ca31755f7 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -2427,7 +2427,7 @@ pub const Inst = struct { operand: Ref, pub fn src(self: @This()) LazySrcLoc { - return .{ .node_offset = self.src_node }; + return LazySrcLoc.nodeOffset(self.src_node); } }, /// Used for unary operators, with a token source location. @@ -2450,7 +2450,7 @@ pub const Inst = struct { payload_index: u32, pub fn src(self: @This()) LazySrcLoc { - return .{ .node_offset = self.src_node }; + return LazySrcLoc.nodeOffset(self.src_node); } }, pl_tok: struct { @@ -2526,7 +2526,7 @@ pub const Inst = struct { bit_count: u16, pub fn src(self: @This()) LazySrcLoc { - return .{ .node_offset = self.src_node }; + return LazySrcLoc.nodeOffset(self.src_node); } }, bool_br: struct { @@ -2545,7 +2545,7 @@ pub const Inst = struct { force_comptime: bool, pub fn src(self: @This()) LazySrcLoc { - return .{ .node_offset = self.src_node }; + return LazySrcLoc.nodeOffset(self.src_node); } }, @"break": struct { @@ -2566,7 +2566,7 @@ pub const Inst = struct { inst: Index, pub fn src(self: @This()) LazySrcLoc { - return .{ .node_offset = self.src_node }; + return LazySrcLoc.nodeOffset(self.src_node); } }, str_op: struct { diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index da4e84b587..33cf5422f2 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -622,7 +622,7 @@ pub fn deinit(self: *Self) void { /// Sets `err_msg` on `CodeGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig fn fail(self: *Self, comptime fmt: []const u8, args: anytype) InnerError { - const src: LazySrcLoc = .{ .node_offset = 0 }; + const src = LazySrcLoc.nodeOffset(0); const src_loc = src.toSrcLoc(self.decl); self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, fmt, args); return error.CodegenFail; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 4c2239b306..45191f3107 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -363,7 +363,7 @@ pub const DeclGen = struct { fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { @setCold(true); - const src: LazySrcLoc = .{ .node_offset = 0 }; + const src = LazySrcLoc.nodeOffset(0); const src_loc = src.toSrcLoc(dg.decl); dg.error_msg = try Module.ErrorMsg.create(dg.module.gpa, src_loc, format, args); return error.AnalysisFail; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 188c2f6f11..60803eff69 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2163,7 +2163,7 @@ pub const DeclGen = struct { fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) Error { @setCold(true); assert(self.err_msg == null); - const src_loc = @as(LazySrcLoc, .{ .node_offset = 0 }).toSrcLoc(self.decl); + const src_loc = LazySrcLoc.nodeOffset(0).toSrcLoc(self.decl); self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, "TODO (LLVM): " ++ format, args); return error.CodegenFail; } diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 0dc39db134..9879bc7f35 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -184,7 +184,7 @@ pub const DeclGen = struct { fn fail(self: *DeclGen, comptime format: []const u8, args: anytype) Error { @setCold(true); - const src: LazySrcLoc = .{ .node_offset = 0 }; + const src = LazySrcLoc.nodeOffset(0); const src_loc = src.toSrcLoc(self.decl); assert(self.error_msg == null); self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args); @@ -193,7 +193,7 @@ pub const DeclGen = struct { fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) Error { @setCold(true); - const src: LazySrcLoc = .{ .node_offset = 0 }; + const src = LazySrcLoc.nodeOffset(0); const src_loc = src.toSrcLoc(self.decl); assert(self.error_msg == null); self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, "TODO (SPIR-V): " ++ format, args); diff --git a/src/print_zir.zig b/src/print_zir.zig index 3257a3cb58..1760c617ce 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -497,7 +497,7 @@ const Writer = struct { .wasm_memory_size, => { const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data; - const src: LazySrcLoc = .{ .node_offset = inst_data.node }; + const src = LazySrcLoc.nodeOffset(inst_data.node); try self.writeInstRef(stream, inst_data.operand); try stream.writeAll(")) "); try self.writeSrc(stream, src); @@ -510,7 +510,7 @@ const Writer = struct { .prefetch, => { const inst_data = self.code.extraData(Zir.Inst.BinNode, extended.operand).data; - const src: LazySrcLoc = .{ .node_offset = inst_data.node }; + const src = LazySrcLoc.nodeOffset(inst_data.node); try self.writeInstRef(stream, inst_data.lhs); try stream.writeAll(", "); try self.writeInstRef(stream, inst_data.rhs); @@ -520,7 +520,7 @@ const Writer = struct { .field_call_bind_named => { const extra = self.code.extraData(Zir.Inst.FieldNamedNode, extended.operand).data; - const src: LazySrcLoc = .{ .node_offset = extra.node }; + const src = LazySrcLoc.nodeOffset(extra.node); try self.writeInstRef(stream, extra.lhs); try stream.writeAll(", "); try self.writeInstRef(stream, extra.field_name); @@ -531,7 +531,7 @@ const Writer = struct { } fn writeExtNode(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { - const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; + const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand)); try stream.writeAll(")) "); try self.writeSrc(stream, src); } @@ -1050,7 +1050,7 @@ const Writer = struct { fn writeNodeMultiOp(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const extra = self.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); - const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; + const src = LazySrcLoc.nodeOffset(extra.data.src_node); const operands = self.code.refSlice(extra.end, extended.small); for (operands) |operand, i| { @@ -1074,7 +1074,7 @@ const Writer = struct { fn writeAsm(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const extra = self.code.extraData(Zir.Inst.Asm, extended.operand); - const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; + const src = LazySrcLoc.nodeOffset(extra.data.src_node); const outputs_len = @truncate(u5, extended.small); const inputs_len = @truncate(u5, extended.small >> 5); const clobbers_len = @truncate(u5, extended.small >> 10); @@ -1145,7 +1145,7 @@ const Writer = struct { fn writeOverflowArithmetic(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const extra = self.code.extraData(Zir.Inst.OverflowArithmetic, extended.operand).data; - const src: LazySrcLoc = .{ .node_offset = extra.node }; + const src = LazySrcLoc.nodeOffset(extra.node); try self.writeInstRef(stream, extra.lhs); try stream.writeAll(", "); @@ -1898,7 +1898,7 @@ const Writer = struct { inst: Zir.Inst.Index, ) (@TypeOf(stream).Error || error{OutOfMemory})!void { const src_node = self.code.instructions.items(.data)[inst].node; - const src: LazySrcLoc = .{ .node_offset = src_node }; + const src = LazySrcLoc.nodeOffset(src_node); try stream.writeAll(") "); try self.writeSrc(stream, src); } @@ -2117,7 +2117,7 @@ const Writer = struct { fn writeAllocExtended(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const extra = self.code.extraData(Zir.Inst.AllocExtended, extended.operand); const small = @bitCast(Zir.Inst.AllocExtended.Small, extended.small); - const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; + const src = LazySrcLoc.nodeOffset(extra.data.src_node); var extra_index: usize = extra.end; const type_inst: Zir.Inst.Ref = if (!small.has_type) .none else blk: { @@ -2351,7 +2351,7 @@ const Writer = struct { fn writeSrcNode(self: *Writer, stream: anytype, src_node: ?i32) !void { const node_offset = src_node orelse return; - const src: LazySrcLoc = .{ .node_offset = node_offset }; + const src = LazySrcLoc.nodeOffset(node_offset); try stream.writeAll(" "); return self.writeSrc(stream, src); } From 83f300218f1a35a32d80e92179011db44ac83d05 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 7 Jun 2022 17:58:18 -0700 Subject: [PATCH 1800/2031] upgrade behavior test to stage2 fn ptr semantics --- test/behavior/bugs/920.zig | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/test/behavior/bugs/920.zig b/test/behavior/bugs/920.zig index 19fce71549..7dae996dac 100644 --- a/test/behavior/bugs/920.zig +++ b/test/behavior/bugs/920.zig @@ -1,14 +1,24 @@ +const builtin = @import("builtin"); const std = @import("std"); const Random = std.rand.Random; +const zeroCaseFn = switch (builtin.zig_backend) { + .stage1 => fn (*Random, f64) f64, + else => *const fn (*Random, f64) f64, +}; +const pdfFn = switch (builtin.zig_backend) { + .stage1 => fn (f64) f64, + else => *const fn (f64) f64, +}; + const ZigTable = struct { r: f64, x: [257]f64, f: [257]f64, - pdf: fn (f64) f64, + pdf: pdfFn, is_symmetric: bool, - zero_case: fn (*Random, f64) f64, + zero_case: zeroCaseFn, }; fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn (f64) f64, comptime f_inv: fn (f64) f64, comptime zero_case: fn (*Random, f64) f64) ZigTable { @@ -56,7 +66,11 @@ const NormalDist = blk: { }; test "bug 920 fixed" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const NormalDist1 = blk: { break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case); From af909f6c93f06e409e98cb90a9896aa5216f1563 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 8 Jun 2022 12:55:24 -0700 Subject: [PATCH 1801/2031] std.debug.Trace: improve API Now `std.debug.Trace` is a concrete type with pre-chosen defaults. `std.debug.ConfigurableTrace` can be used for more advanced cases. --- lib/std/debug.zig | 37 +++++++++++++++++++++++++++++++------ src/Module.zig | 6 ++---- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index f4ca0f0354..ba45b16d1b 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1944,19 +1944,42 @@ noinline fn showMyTrace() usize { return @returnAddress(); } -pub fn Trace(comptime size: usize, comptime stack_frame_count: usize) type { +/// This API helps you track where a value originated and where it was mutated, +/// or any other points of interest. +/// In debug mode, it adds a small size penalty (104 bytes on 64-bit architectures) +/// to the aggregate that you add it to. +/// In release mode, it is size 0 and all methods are no-ops. +/// This is a pre-made type with default settings. +/// For more advanced usage, see `ConfigurableTrace`. +pub const Trace = ConfigurableTrace(2, 4, builtin.mode == .Debug); + +pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize, comptime enabled: bool) type { return struct { - addrs: [size][stack_frame_count]usize = undefined, - notes: [size][]const u8 = undefined, - index: usize = 0, + addrs: [actual_size][stack_frame_count]usize = undefined, + notes: [actual_size][]const u8 = undefined, + index: Index = 0, - const frames_init = [1]usize{0} ** stack_frame_count; + const actual_size = if (enabled) size else 0; + const Index = if (enabled) usize else u0; - pub noinline fn add(t: *@This(), note: []const u8) void { + pub const enabled = enabled; + + pub const add = if (enabled) addNoInline else addNoOp; + + pub noinline fn addNoInline(t: *@This(), note: []const u8) void { + comptime assert(enabled); return addAddr(t, @returnAddress(), note); } + pub inline fn addNoOp(t: *@This(), note: []const u8) void { + _ = t; + _ = note; + comptime assert(!enabled); + } + pub fn addAddr(t: *@This(), addr: usize, note: []const u8) void { + if (!enabled) return; + if (t.index < size) { t.notes[t.index] = note; t.addrs[t.index] = [1]usize{0} ** stack_frame_count; @@ -1972,6 +1995,8 @@ pub fn Trace(comptime size: usize, comptime stack_frame_count: usize) type { } pub fn dump(t: @This()) void { + if (!enabled) return; + const tty_config = detectTTYConfig(); const stderr = io.getStdErr().writer(); const end = @maximum(t.index, size); diff --git a/src/Module.zig b/src/Module.zig index 724fa2ebc4..602b91a5ba 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2529,11 +2529,9 @@ pub const SrcLoc = struct { /// where in semantic analysis the value got set. const TracedOffset = struct { x: i32, - trace: Trace = trace_init, + trace: std.debug.Trace = .{}, - const want_tracing = builtin.mode == .Debug; - const trace_init = if (want_tracing) std.debug.Trace(1, 3){} else {}; - const Trace = @TypeOf(trace_init); + const want_tracing = std.debug.Trace.enabled; }; /// Resolving a source location into a byte offset may require doing work From fd32f6890db8d338ec5cad92871035767f2e84ac Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 8 Jun 2022 14:20:10 -0700 Subject: [PATCH 1802/2031] stage2: fold redundant error notes --- src/Compilation.zig | 101 +++++++++++++++++++-- src/Sema.zig | 1 - test/cases/recursive_inline_function.1.zig | 3 + 3 files changed, 98 insertions(+), 7 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 7d686b2f40..c3d35f77b7 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -338,6 +338,8 @@ pub const AllErrors = struct { line: u32, column: u32, byte_offset: u32, + /// Usually one, but incremented for redundant messages. + count: u32 = 1, /// Does not include the trailing newline. source_line: ?[]const u8, notes: []Message = &.{}, @@ -345,8 +347,21 @@ pub const AllErrors = struct { plain: struct { msg: []const u8, notes: []Message = &.{}, + /// Usually one, but incremented for redundant messages. + count: u32 = 1, }, + pub fn incrementCount(msg: *Message) void { + switch (msg.*) { + .src => |*src| { + src.count += 1; + }, + .plain => |*plain| { + plain.count += 1; + }, + } + } + pub fn renderToStdErr(msg: Message, ttyconf: std.debug.TTY.Config) void { std.debug.getStderrMutex().lock(); defer std.debug.getStderrMutex().unlock(); @@ -376,7 +391,13 @@ pub const AllErrors = struct { try stderr.writeAll(kind); ttyconf.setColor(stderr, .Reset); ttyconf.setColor(stderr, .Bold); - try stderr.print(" {s}\n", .{src.msg}); + if (src.count == 1) { + try stderr.print(" {s}\n", .{src.msg}); + } else { + try stderr.print(" {s}", .{src.msg}); + ttyconf.setColor(stderr, .Dim); + try stderr.print(" ({d} times)\n", .{src.count}); + } ttyconf.setColor(stderr, .Reset); if (ttyconf != .no_color) { if (src.source_line) |line| { @@ -400,7 +421,13 @@ pub const AllErrors = struct { try stderr.writeByteNTimes(' ', indent); try stderr.writeAll(kind); ttyconf.setColor(stderr, .Reset); - try stderr.print(" {s}\n", .{plain.msg}); + if (plain.count == 1) { + try stderr.print(" {s}\n", .{plain.msg}); + } else { + try stderr.print(" {s}", .{plain.msg}); + ttyconf.setColor(stderr, .Dim); + try stderr.print(" ({d} times)\n", .{plain.count}); + } ttyconf.setColor(stderr, .Reset); for (plain.notes) |note| { try note.renderToStdErrInner(ttyconf, stderr_file, "error:", .Red, indent + 4); @@ -408,6 +435,50 @@ pub const AllErrors = struct { }, } } + + pub const HashContext = struct { + pub fn hash(ctx: HashContext, key: *Message) u64 { + _ = ctx; + var hasher = std.hash.Wyhash.init(0); + + switch (key.*) { + .src => |src| { + hasher.update(src.msg); + hasher.update(src.src_path); + std.hash.autoHash(&hasher, src.line); + std.hash.autoHash(&hasher, src.column); + std.hash.autoHash(&hasher, src.byte_offset); + }, + .plain => |plain| { + hasher.update(plain.msg); + }, + } + + return hasher.final(); + } + + pub fn eql(ctx: HashContext, a: *Message, b: *Message) bool { + _ = ctx; + switch (a.*) { + .src => |a_src| switch (b.*) { + .src => |b_src| { + return mem.eql(u8, a_src.msg, b_src.msg) and + mem.eql(u8, a_src.src_path, b_src.src_path) and + a_src.line == b_src.line and + a_src.column == b_src.column and + a_src.byte_offset == b_src.byte_offset; + }, + .plain => return false, + }, + .plain => |a_plain| switch (b.*) { + .src => return false, + .plain => |b_plain| { + return mem.eql(u8, a_plain.msg, b_plain.msg); + }, + }, + } + } + }; }; pub fn deinit(self: *AllErrors, gpa: Allocator) void { @@ -421,13 +492,25 @@ pub const AllErrors = struct { module_err_msg: Module.ErrorMsg, ) !void { const allocator = arena.allocator(); - const notes = try allocator.alloc(Message, module_err_msg.notes.len); - for (notes) |*note, i| { - const module_note = module_err_msg.notes[i]; + + const notes_buf = try allocator.alloc(Message, module_err_msg.notes.len); + var note_i: usize = 0; + + // De-duplicate error notes. The main use case in mind for this is + // too many "note: called from here" notes when eval branch quota is reached. + var seen_notes = std.HashMap( + *Message, + void, + Message.HashContext, + std.hash_map.default_max_load_percentage, + ).init(allocator); + + for (module_err_msg.notes) |module_note| { const source = try module_note.src_loc.file_scope.getSource(module.gpa); const byte_offset = try module_note.src_loc.byteOffset(module.gpa); const loc = std.zig.findLineColumn(source.bytes, byte_offset); const file_path = try module_note.src_loc.file_scope.fullPath(allocator); + const note = ¬es_buf[note_i]; note.* = .{ .src = .{ .src_path = file_path, @@ -438,6 +521,12 @@ pub const AllErrors = struct { .source_line = try allocator.dupe(u8, loc.source_line), }, }; + const gop = try seen_notes.getOrPut(note); + if (gop.found_existing) { + gop.key_ptr.*.incrementCount(); + } else { + note_i += 1; + } } if (module_err_msg.src_loc.lazy == .entire_file) { try errors.append(.{ @@ -458,7 +547,7 @@ pub const AllErrors = struct { .byte_offset = byte_offset, .line = @intCast(u32, loc.line), .column = @intCast(u32, loc.column), - .notes = notes, + .notes = notes_buf[0..note_i], .source_line = try allocator.dupe(u8, loc.source_line), }, }); diff --git a/src/Sema.zig b/src/Sema.zig index 882c18fe55..c0e8b80dbd 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -18061,7 +18061,6 @@ fn safetyPanic( fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void { sema.branch_count += 1; if (sema.branch_count > sema.branch_quota) { - // TODO show the "called from here" stack return sema.fail(block, src, "evaluation exceeded {d} backwards branches", .{sema.branch_quota}); } } diff --git a/test/cases/recursive_inline_function.1.zig b/test/cases/recursive_inline_function.1.zig index 139da1c371..8ed9bde8e8 100644 --- a/test/cases/recursive_inline_function.1.zig +++ b/test/cases/recursive_inline_function.1.zig @@ -14,3 +14,6 @@ inline fn fibonacci(n: usize) usize { // error // // :11:21: error: evaluation exceeded 1000 backwards branches +// :11:40: note: called from here (6 times) +// :11:21: note: called from here (495 times) +// :5:24: note: called from here From bc36da0cb80eebffbbd86a430b435492e00f8378 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 8 Jun 2022 15:32:55 -0700 Subject: [PATCH 1803/2031] test harness: fix handling of counts I'm not really happy with parsing compile errors; I think we should just be checking that the expected compile error matches the actual rendered version. I will save that change for a later date however. --- src/test.zig | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/src/test.zig b/src/test.zig index 2a9b82027f..9e8e3ccb95 100644 --- a/src/test.zig +++ b/src/test.zig @@ -61,10 +61,12 @@ const ErrorMsg = union(enum) { // this is a workaround for stage1 compiler bug I ran into when making it ?u32 column: u32, kind: Kind, + count: u32, }, plain: struct { msg: []const u8, kind: Kind, + count: u32, }, const Kind = enum { @@ -81,12 +83,14 @@ const ErrorMsg = union(enum) { .line = @intCast(u32, src.line), .column = @intCast(u32, src.column), .kind = kind, + .count = src.count, }, }, .plain => |plain| return .{ .plain = .{ .msg = plain.msg, .kind = kind, + .count = plain.count, }, }, } @@ -118,10 +122,16 @@ const ErrorMsg = union(enum) { try writer.writeAll("?: "); } } - return writer.print("{s}: {s}", .{ @tagName(src.kind), src.msg }); + try writer.print("{s}: {s}", .{ @tagName(src.kind), src.msg }); + if (src.count != 1) { + try writer.print(" ({d} times)", .{src.count}); + } }, .plain => |plain| { - return writer.print("{s}: {s}", .{ @tagName(plain.kind), plain.msg }); + try writer.print("{s}: {s}", .{ @tagName(plain.kind), plain.msg }); + if (plain.count != 1) { + try writer.print(" ({d} times)", .{plain.count}); + } }, } } @@ -647,12 +657,20 @@ pub const TestContext = struct { for (errors) |err_msg_line, i| { if (std.mem.startsWith(u8, err_msg_line, "error: ")) { array[i] = .{ - .plain = .{ .msg = err_msg_line["error: ".len..], .kind = .@"error" }, + .plain = .{ + .msg = err_msg_line["error: ".len..], + .kind = .@"error", + .count = 1, + }, }; continue; } else if (std.mem.startsWith(u8, err_msg_line, "note: ")) { array[i] = .{ - .plain = .{ .msg = err_msg_line["note: ".len..], .kind = .note }, + .plain = .{ + .msg = err_msg_line["note: ".len..], + .kind = .note, + .count = 1, + }, }; continue; } @@ -662,7 +680,7 @@ pub const TestContext = struct { const line_text = it.next() orelse @panic("missing line"); const col_text = it.next() orelse @panic("missing column"); const kind_text = it.next() orelse @panic("missing 'error'/'note'"); - const msg = it.rest()[1..]; // skip over the space at end of "error: " + var msg = it.rest()[1..]; // skip over the space at end of "error: " const line: ?u32 = if (std.mem.eql(u8, line_text, "?")) null @@ -695,6 +713,14 @@ pub const TestContext = struct { break :blk n - 1; } else std.math.maxInt(u32); + const suffix = " times)"; + const count = if (std.mem.endsWith(u8, msg, suffix)) count: { + const lparen = std.mem.lastIndexOfScalar(u8, msg, '(').?; + const count = std.fmt.parseInt(u32, msg[lparen + 1 .. msg.len - suffix.len], 10) catch @panic("bad error note count number"); + msg = msg[0 .. lparen - 1]; + break :count count; + } else 1; + array[i] = .{ .src = .{ .src_path = src_path, @@ -702,6 +728,7 @@ pub const TestContext = struct { .line = line_0based, .column = column_0based, .kind = kind, + .count = count, }, }; } @@ -1606,7 +1633,8 @@ pub const TestContext = struct { (case_msg.src.column == std.math.maxInt(u32) or actual_msg.column == case_msg.src.column) and std.mem.eql(u8, expected_msg, actual_msg.msg) and - case_msg.src.kind == .@"error") + case_msg.src.kind == .@"error" and + actual_msg.count == case_msg.src.count) { handled_errors[i] = true; break; @@ -1616,7 +1644,8 @@ pub const TestContext = struct { if (ex_tag != .plain) continue; if (std.mem.eql(u8, case_msg.plain.msg, plain.msg) and - case_msg.plain.kind == .@"error") + case_msg.plain.kind == .@"error" and + case_msg.plain.count == plain.count) { handled_errors[i] = true; break; From 5816d3eaec3f3bb04e70c89aa402ba9e0e5e7b2c Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Thu, 9 Jun 2022 00:06:16 +0200 Subject: [PATCH 1804/2031] linker: remove `-z noexecstack` option Note that the current documentation for the `-z noexecstack` is incorrect. This indicates that an object *does not* require an executable stack. This is actually the default of LLD, and there has never been a way to override this default by passing `-z execstack` to LLD. This commit removes the redundant `-z noexecstack` option from zig build-exe/build-lib/build-obj and ignores the option if passed to zig cc for compatibility. As far as I can tell, there is no reason for code to require an executable stack. This option only exists because the stack was originally executable by default and some programs came to depend on that behavior. Instead, mprotect(2) may be used to make memory pages executable. --- src/Compilation.zig | 3 --- src/link.zig | 1 - src/link/Elf.zig | 5 ----- src/main.zig | 7 +------ 4 files changed, 1 insertion(+), 15 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 7d686b2f40..b98548e9dc 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -762,7 +762,6 @@ pub const InitOptions = struct { linker_z_notext: bool = false, linker_z_defs: bool = false, linker_z_origin: bool = false, - linker_z_noexecstack: bool = false, linker_z_now: bool = true, linker_z_relro: bool = true, linker_z_nocopyreloc: bool = false, @@ -1602,7 +1601,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .z_defs = options.linker_z_defs, .z_origin = options.linker_z_origin, .z_nocopyreloc = options.linker_z_nocopyreloc, - .z_noexecstack = options.linker_z_noexecstack, .z_now = options.linker_z_now, .z_relro = options.linker_z_relro, .tsaware = options.linker_tsaware, @@ -2350,7 +2348,6 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes man.hash.add(comp.bin_file.options.z_defs); man.hash.add(comp.bin_file.options.z_origin); man.hash.add(comp.bin_file.options.z_nocopyreloc); - man.hash.add(comp.bin_file.options.z_noexecstack); man.hash.add(comp.bin_file.options.z_now); man.hash.add(comp.bin_file.options.z_relro); man.hash.add(comp.bin_file.options.hash_style); diff --git a/src/link.zig b/src/link.zig index 60baa6a92a..0aa5d0031d 100644 --- a/src/link.zig +++ b/src/link.zig @@ -113,7 +113,6 @@ pub const Options = struct { z_defs: bool, z_origin: bool, z_nocopyreloc: bool, - z_noexecstack: bool, z_now: bool, z_relro: bool, tsaware: bool, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 30504c7a1a..e0f114acd4 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1333,7 +1333,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v man.hash.add(self.base.options.z_defs); man.hash.add(self.base.options.z_origin); man.hash.add(self.base.options.z_nocopyreloc); - man.hash.add(self.base.options.z_noexecstack); man.hash.add(self.base.options.z_now); man.hash.add(self.base.options.z_relro); man.hash.add(self.base.options.hash_style); @@ -1512,10 +1511,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append("-z"); try argv.append("nocopyreloc"); } - if (self.base.options.z_noexecstack) { - try argv.append("-z"); - try argv.append("noexecstack"); - } if (self.base.options.z_now) { // LLD defaults to -zlazy try argv.append("-znow"); diff --git a/src/main.zig b/src/main.zig index eaff34ee9e..39237d1625 100644 --- a/src/main.zig +++ b/src/main.zig @@ -433,7 +433,6 @@ const usage_build_generic = \\ defs Force a fatal error if any undefined symbols remain \\ origin Indicate that the object must have its origin processed \\ nocopyreloc Disable the creation of copy relocations - \\ noexecstack Indicate that the object requires an executable stack \\ now (default) Force all relocations to be processed on load \\ lazy Don't force all relocations to be processed on load \\ relro (default) Force all relocations to be read-only after processing @@ -656,7 +655,6 @@ fn buildOutputType( var linker_z_notext = false; var linker_z_defs = false; var linker_z_origin = false; - var linker_z_noexecstack = false; var linker_z_now = true; var linker_z_relro = true; var linker_tsaware = false; @@ -1207,8 +1205,6 @@ fn buildOutputType( linker_z_defs = true; } else if (mem.eql(u8, z_arg, "origin")) { linker_z_origin = true; - } else if (mem.eql(u8, z_arg, "noexecstack")) { - linker_z_noexecstack = true; } else if (mem.eql(u8, z_arg, "now")) { linker_z_now = true; } else if (mem.eql(u8, z_arg, "lazy")) { @@ -1694,7 +1690,7 @@ fn buildOutputType( } else if (mem.eql(u8, z_arg, "origin")) { linker_z_origin = true; } else if (mem.eql(u8, z_arg, "noexecstack")) { - linker_z_noexecstack = true; + // noexecstack is the default when linking with LLD } else if (mem.eql(u8, z_arg, "now")) { linker_z_now = true; } else if (mem.eql(u8, z_arg, "lazy")) { @@ -2719,7 +2715,6 @@ fn buildOutputType( .linker_z_notext = linker_z_notext, .linker_z_defs = linker_z_defs, .linker_z_origin = linker_z_origin, - .linker_z_noexecstack = linker_z_noexecstack, .linker_z_now = linker_z_now, .linker_z_relro = linker_z_relro, .linker_tsaware = linker_tsaware, From 32c90cb5539c3b340ae1b0b13d2b1521ebb6b1b0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Jun 2022 19:23:36 -0700 Subject: [PATCH 1805/2031] stage2: fix handling of aggregates with mixed comptime-only fields --- src/Sema.zig | 4 +--- src/codegen/llvm.zig | 6 +++--- src/type.zig | 10 +--------- test/behavior/eval.zig | 33 ++++++++++++++++++++++++++++++++- 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 2d3c38faeb..f1cfd04865 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -25048,9 +25048,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ } pub fn typeHasRuntimeBits(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { - if ((try sema.typeHasOnePossibleValue(block, src, ty)) != null) return false; - if (try sema.typeRequiresComptime(block, src, ty)) return false; - return true; + return ty.hasRuntimeBitsAdvanced(false, sema.kit(block, src)); } fn typeAbiSize(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u64 { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 188c2f6f11..bda81711fb 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -9185,7 +9185,7 @@ fn isByRef(ty: Type) bool { .AnyFrame, => return false, - .Array, .Frame => return ty.hasRuntimeBitsIgnoreComptime(), + .Array, .Frame => return ty.hasRuntimeBits(), .Struct => { // Packed structs are represented to LLVM as integers. if (ty.containerLayout() == .Packed) return false; @@ -9204,7 +9204,7 @@ fn isByRef(ty: Type) bool { var count: usize = 0; const fields = ty.structFields(); for (fields.values()) |field| { - if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; + if (field.is_comptime or !field.ty.hasRuntimeBits()) continue; count += 1; if (count > max_fields_byval) return true; @@ -9212,7 +9212,7 @@ fn isByRef(ty: Type) bool { } return false; }, - .Union => return ty.hasRuntimeBitsIgnoreComptime(), + .Union => return ty.hasRuntimeBits(), .ErrorUnion => return isByRef(ty.errorUnionPayload()), .Optional => { var buf: Type.Payload.ElemType = undefined; diff --git a/src/type.zig b/src/type.zig index 9b91572427..049630ef49 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2365,6 +2365,7 @@ pub const Type = extern union { .@"anyframe", .anyopaque, .@"opaque", + .type_info, => return true, // These are false because they are comptime-only types. @@ -2379,7 +2380,6 @@ pub const Type = extern union { .enum_literal, .empty_struct, .empty_struct_literal, - .type_info, .bound_fn, // These are function *bodies*, not pointers. // Special exceptions have to be made when emitting functions due to @@ -2464,14 +2464,6 @@ pub const Type = extern union { .@"struct" => { const struct_obj = ty.castTag(.@"struct").?.data; - if (sema_kit) |sk| { - _ = try sk.sema.typeRequiresComptime(sk.block, sk.src, ty); - } - switch (struct_obj.requires_comptime) { - .yes => return false, - .wip, .no => if (struct_obj.known_non_opv) return true, - .unknown => {}, - } if (struct_obj.status == .field_types_wip) { // In this case, we guess that hasRuntimeBits() for this type is true, // and then later if our guess was incorrect, we emit a compile error. diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 220768c820..e56ea0cad5 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -1196,7 +1196,9 @@ test "equality of pointers to comptime const" { } test "storing an array of type in a field" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() void { @@ -1221,3 +1223,32 @@ test "storing an array of type in a field" { S.doTheTest(); } + +test "pass pointer to field of comptime-only type as a runtime parameter" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + const Mixed = struct { + T: type, + x: i32, + }; + const bag: Mixed = .{ + .T = bool, + .x = 1234, + }; + + var ok = false; + + fn doTheTest() !void { + foo(&bag.x); + try expect(ok); + } + + fn foo(ptr: *const i32) void { + ok = ptr.* == 1234; + } + }; + try S.doTheTest(); +} From 2bf532fc23ec014201bb1e69cb3b44e37e23f14c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Jun 2022 15:33:04 -0700 Subject: [PATCH 1806/2031] stage2: use std.debug.Trace only when explicitly enabled Because it bumps up the stack space requirements, which is making a test case fail on aarch64 drone CI. --- build.zig | 3 +++ ci/azure/build.zig | 2 ++ src/Module.zig | 2 +- src/config.zig.in | 1 + 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/build.zig b/build.zig index 3ce7cc24c2..0afbe9171a 100644 --- a/build.zig +++ b/build.zig @@ -131,6 +131,7 @@ pub fn build(b: *Builder) !void { const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse enable_llvm; const strip = b.option(bool, "strip", "Omit debug information") orelse false; const use_zig0 = b.option(bool, "zig0", "Bootstrap using zig0") orelse false; + const value_tracing = b.option(bool, "value-tracing", "Enable extra state tracking to help troubleshoot bugs in the compiler (using the std.debug.Trace API)") orelse false; const mem_leak_frames: u32 = b.option(u32, "mem-leak-frames", "How many stack frames to print when a memory leak occurs. Tests get 2x this amount.") orelse blk: { if (strip) break :blk @as(u32, 0); @@ -353,6 +354,7 @@ pub fn build(b: *Builder) !void { exe_options.addOption(bool, "enable_tracy", tracy != null); exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack); exe_options.addOption(bool, "enable_tracy_allocation", tracy_allocation); + exe_options.addOption(bool, "value_tracing", value_tracing); exe_options.addOption(bool, "is_stage1", is_stage1); exe_options.addOption(bool, "omit_stage2", omit_stage2); if (tracy) |tracy_path| { @@ -402,6 +404,7 @@ pub fn build(b: *Builder) !void { test_cases_options.addOption(bool, "enable_rosetta", b.enable_rosetta); test_cases_options.addOption(bool, "enable_darling", b.enable_darling); test_cases_options.addOption(u32, "mem_leak_frames", mem_leak_frames * 2); + test_cases_options.addOption(bool, "value_tracing", value_tracing); test_cases_options.addOption(?[]const u8, "glibc_runtimes_dir", b.glibc_runtimes_dir); test_cases_options.addOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version)); test_cases_options.addOption(std.SemanticVersion, "semver", semver); diff --git a/ci/azure/build.zig b/ci/azure/build.zig index 4596233bd8..12197fdf07 100644 --- a/ci/azure/build.zig +++ b/ci/azure/build.zig @@ -99,6 +99,7 @@ pub fn build(b: *Builder) !void { const force_gpa = b.option(bool, "force-gpa", "Force the compiler to use GeneralPurposeAllocator") orelse false; const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse enable_llvm; const strip = b.option(bool, "strip", "Omit debug information") orelse false; + const value_tracing = b.option(bool, "value-tracing", "Enable extra state tracking to help troubleshoot bugs in the compiler (using the std.debug.Trace API)") orelse false; const mem_leak_frames: u32 = b.option(u32, "mem-leak-frames", "How many stack frames to print when a memory leak occurs. Tests get 2x this amount.") orelse blk: { if (strip) break :blk @as(u32, 0); @@ -303,6 +304,7 @@ pub fn build(b: *Builder) !void { exe_options.addOption(bool, "enable_tracy", tracy != null); exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack); exe_options.addOption(bool, "enable_tracy_allocation", tracy_allocation); + exe_options.addOption(bool, "value_tracing", value_tracing); exe_options.addOption(bool, "is_stage1", is_stage1); exe_options.addOption(bool, "omit_stage2", omit_stage2); if (tracy) |tracy_path| { diff --git a/src/Module.zig b/src/Module.zig index 602b91a5ba..f03ba77a39 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2531,7 +2531,7 @@ const TracedOffset = struct { x: i32, trace: std.debug.Trace = .{}, - const want_tracing = std.debug.Trace.enabled; + const want_tracing = build_options.value_tracing; }; /// Resolving a source location into a byte offset may require doing work diff --git a/src/config.zig.in b/src/config.zig.in index f193fddb20..104c3ed8eb 100644 --- a/src/config.zig.in +++ b/src/config.zig.in @@ -8,6 +8,7 @@ pub const semver = @import("std").SemanticVersion.parse(version) catch unreachab pub const enable_logging: bool = @ZIG_ENABLE_LOGGING_BOOL@; pub const enable_link_snapshots: bool = false; pub const enable_tracy = false; +pub const value_tracing = false; pub const is_stage1 = true; pub const skip_non_native = false; pub const omit_stage2: bool = @ZIG_OMIT_STAGE2_BOOL@; From 436aafd3e2ef1a8f5998b974a9791b59939f57ad Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Jun 2022 19:14:52 -0700 Subject: [PATCH 1807/2031] remove a stage2 test case that relies on a very deep compiler stack It's causing Drone CI failure on aarch64. I will open a follow-up issue to track this missing test case. --- test/stage2/cbe.zig | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index ae810a31ca..47ed7a53be 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -233,30 +233,6 @@ pub fn addCases(ctx: *TestContext) !void { \\} , ""); } - // This will make a pretty deep call stack, so this test can only be enabled - // on hosts where Zig's linking strategy can honor the 16 MiB (default) we - // link the self-hosted compiler with. - const host_supports_custom_stack_size = @import("builtin").target.os.tag == .linux; - if (host_supports_custom_stack_size) { - var case = ctx.exeFromCompiledC("@setEvalBranchQuota", .{}); - - // TODO when adding result location support to function calls, revisit this test - // case. It can go back to what it was before, with `y` being comptime known. - // Because the ret_ptr will passed in with the inline fn call, and there will - // only be 1 store to it, and it will be comptime known. - case.addCompareOutput( - \\pub export fn main() i32 { - \\ @setEvalBranchQuota(1001); - \\ const y = rec(1001); - \\ return y - 1; - \\} - \\ - \\inline fn rec(n: i32) i32 { - \\ if (n <= 1) return n; - \\ return rec(n - 1); - \\} - , ""); - } { var case = ctx.exeFromCompiledC("control flow", .{}); From 58bc562cb45d5d27fed2e69ee289a4b5c9199cff Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Jun 2022 20:37:24 -0700 Subject: [PATCH 1808/2031] update packed struct behavior tests to new language semantics --- test/behavior/packed-struct.zig | 45 ++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index 2483bbea69..7e59ce052e 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -33,10 +33,10 @@ test "correct size of packed structs" { } test "flags in packed structs" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage1) return error.SkipZigTest; const Flags1 = packed struct { - // byte 0 + // first 8 bits b0_0: u1, b0_1: u1, b0_2: u1, @@ -46,7 +46,7 @@ test "flags in packed structs" { b0_6: u1, b0_7: u1, - // partial byte 1 (but not 8 bits) + // 7 more bits b1_0: u1, b1_1: u1, b1_2: u1, @@ -55,12 +55,12 @@ test "flags in packed structs" { b1_5: u1, b1_6: u1, - // some padding to fill to size 3 + // some padding to fill to 24 bits _: u9, }; - try expectEqual(3, @sizeOf(Flags1)); - try expectEqual(3 * 8, @bitSizeOf(Flags1)); + try expectEqual(@sizeOf(u24), @sizeOf(Flags1)); + try expectEqual(24, @bitSizeOf(Flags1)); const Flags2 = packed struct { // byte 0 @@ -86,8 +86,8 @@ test "flags in packed structs" { _: u10, }; - try expectEqual(4, @sizeOf(Flags2)); - try expectEqual(8 + 7 + 10, @bitSizeOf(Flags2)); + try expectEqual(@sizeOf(u25), @sizeOf(Flags2)); + try expectEqual(25, @bitSizeOf(Flags2)); const Flags3 = packed struct { // byte 0 @@ -114,30 +114,30 @@ test "flags in packed structs" { _: u16, // it works, if the padding is 8-based }; - try expectEqual(4, @sizeOf(Flags3)); - try expectEqual(4 * 8, @bitSizeOf(Flags3)); + try expectEqual(@sizeOf(u32), @sizeOf(Flags3)); + try expectEqual(32, @bitSizeOf(Flags3)); } test "arrays in packed structs" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage1) return error.SkipZigTest; const T1 = packed struct { array: [3][3]u8 }; const T2 = packed struct { array: [9]u8 }; - try expectEqual(9, @sizeOf(T1)); - try expectEqual(9 * 8, @bitSizeOf(T1)); - try expectEqual(9, @sizeOf(T2)); - try expectEqual(9 * 8, @bitSizeOf(T2)); + try expectEqual(@sizeOf(u72), @sizeOf(T1)); + try expectEqual(72, @bitSizeOf(T1)); + try expectEqual(@sizeOf(u72), @sizeOf(T2)); + try expectEqual(72, @bitSizeOf(T2)); } test "consistent size of packed structs" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage1) return error.SkipZigTest; const TxData1 = packed struct { data: u8, _23: u23, full: bool = false }; const TxData2 = packed struct { data: u9, _22: u22, full: bool = false }; const register_size_bits = 32; - const register_size_bytes = register_size_bits / 8; + const register_size_bytes = @sizeOf(u32); try expectEqual(register_size_bits, @bitSizeOf(TxData1)); try expectEqual(register_size_bytes, @sizeOf(TxData1)); @@ -151,7 +151,7 @@ test "consistent size of packed structs" { const TxData6 = packed struct { a: u24, b: u32 }; const expectedBitSize = 56; - const expectedByteSize = expectedBitSize / 8; + const expectedByteSize = @sizeOf(u56); try expectEqual(expectedBitSize, @bitSizeOf(TxData3)); try expectEqual(expectedByteSize, @sizeOf(TxData3)); @@ -167,7 +167,12 @@ test "consistent size of packed structs" { } test "correct sizeOf and offsets in packed structs" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const PStruct = packed struct { bool_a: bool, @@ -234,7 +239,7 @@ test "correct sizeOf and offsets in packed structs" { try expectEqual(16, @offsetOf(S, "b")); try expectEqual(128, @bitOffsetOf(S, "b")); - try expectEqual(20, @sizeOf(S)); + try expectEqual(@sizeOf(u160), @sizeOf(S)); } test "nested packed structs" { From 3c3bc5af29a5fcd1daaf8d8d39625c7b505e80bf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 10 Jun 2022 15:04:39 -0700 Subject: [PATCH 1809/2031] Sema: introduce bitSizeAdvanced to recursively resolve types Same pattern as abiSizeAdvanced. Fixes compiler crash for nested packed structs. --- src/Sema.zig | 6 +- src/type.zig | 103 ++++++++++++++++---------------- test/behavior/packed-struct.zig | 11 +++- 3 files changed, 63 insertions(+), 57 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index f1cfd04865..738098dd83 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -11747,11 +11747,11 @@ fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; + const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const unresolved_operand_ty = try sema.resolveType(block, operand_src, inst_data.operand); - const operand_ty = try sema.resolveTypeFields(block, operand_src, unresolved_operand_ty); + const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand); const target = sema.mod.getTarget(); - const bit_size = operand_ty.bitSize(target); + const bit_size = try operand_ty.bitSizeAdvanced(target, sema.kit(block, src)); return sema.addIntUnsigned(Type.comptime_int, bit_size); } diff --git a/src/type.zig b/src/type.zig index 049630ef49..8556526e18 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3542,9 +3542,19 @@ pub const Type = extern union { ); } - /// Asserts the type has the bit size already resolved. pub fn bitSize(ty: Type, target: Target) u64 { - return switch (ty.tag()) { + return bitSizeAdvanced(ty, target, null) catch unreachable; + } + + /// If you pass `sema_kit`, any recursive type resolutions will happen if + /// necessary, possibly returning a CompileError. Passing `null` instead asserts + /// the type is fully resolved, and there will be no error, guaranteed. + pub fn bitSizeAdvanced( + ty: Type, + target: Target, + sema_kit: ?Module.WipAnalysis, + ) Module.CompileError!u64 { + switch (ty.tag()) { .fn_noreturn_no_args => unreachable, // represents machine code; not a pointer .fn_void_no_args => unreachable, // represents machine code; not a pointer .fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer @@ -3568,40 +3578,30 @@ pub const Type = extern union { .generic_poison => unreachable, .bound_fn => unreachable, - .void => 0, - .bool, .u1 => 1, - .u8, .i8 => 8, - .i16, .u16, .f16 => 16, - .u29 => 29, - .i32, .u32, .f32 => 32, - .i64, .u64, .f64 => 64, - .f80 => 80, - .u128, .i128, .f128 => 128, + .void => return 0, + .bool, .u1 => return 1, + .u8, .i8 => return 8, + .i16, .u16, .f16 => return 16, + .u29 => return 29, + .i32, .u32, .f32 => return 32, + .i64, .u64, .f64 => return 64, + .f80 => return 80, + .u128, .i128, .f128 => return 128, .@"struct" => { - const field_count = ty.structFieldCount(); - if (field_count == 0) return 0; - - const struct_obj = ty.castTag(.@"struct").?.data; - assert(struct_obj.haveFieldTypes()); - - switch (struct_obj.layout) { - .Auto, .Extern => { - var total: u64 = 0; - for (struct_obj.fields.values()) |field| { - total += field.ty.bitSize(target); - } - return total; - }, - .Packed => return struct_obj.packedIntegerBits(target), + if (sema_kit) |sk| _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty); + var total: u64 = 0; + for (ty.structFields().values()) |field| { + total += try bitSizeAdvanced(field.ty, target, sema_kit); } + return total; }, .tuple, .anon_struct => { - const tuple = ty.tupleFields(); + if (sema_kit) |sk| _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty); var total: u64 = 0; - for (tuple.types) |field_ty| { - total += field_ty.bitSize(target); + for (ty.tupleFields().types) |field_ty| { + total += try bitSizeAdvanced(field_ty, target, sema_kit); } return total; }, @@ -3609,37 +3609,35 @@ pub const Type = extern union { .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { var buffer: Payload.Bits = undefined; const int_tag_ty = ty.intTagType(&buffer); - return int_tag_ty.bitSize(target); + return try bitSizeAdvanced(int_tag_ty, target, sema_kit); }, .@"union", .union_tagged => { + if (sema_kit) |sk| _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty); const union_obj = ty.cast(Payload.Union).?.data; - - const fields = union_obj.fields; - if (fields.count() == 0) return 0; - assert(union_obj.haveFieldTypes()); var size: u64 = 0; - for (fields.values()) |field| { - size = @maximum(size, field.ty.bitSize(target)); + for (union_obj.fields.values()) |field| { + size = @maximum(size, try bitSizeAdvanced(field.ty, target, sema_kit)); } return size; }, .vector => { const payload = ty.castTag(.vector).?.data; - const elem_bit_size = payload.elem_type.bitSize(target); + const elem_bit_size = try bitSizeAdvanced(payload.elem_type, target, sema_kit); return elem_bit_size * payload.len; }, - .array_u8 => 8 * ty.castTag(.array_u8).?.data, - .array_u8_sentinel_0 => 8 * (ty.castTag(.array_u8_sentinel_0).?.data + 1), + .array_u8 => return 8 * ty.castTag(.array_u8).?.data, + .array_u8_sentinel_0 => return 8 * (ty.castTag(.array_u8_sentinel_0).?.data + 1), .array => { const payload = ty.castTag(.array).?.data; const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target)); if (elem_size == 0 or payload.len == 0) - return 0; - return (payload.len - 1) * 8 * elem_size + payload.elem_type.bitSize(target); + return @as(u64, 0); + const elem_bit_size = try bitSizeAdvanced(payload.elem_type, target, sema_kit); + return (payload.len - 1) * 8 * elem_size + elem_bit_size; }, .array_sentinel => { const payload = ty.castTag(.array_sentinel).?.data; @@ -3647,14 +3645,15 @@ pub const Type = extern union { payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target), ); - return payload.len * 8 * elem_size + payload.elem_type.bitSize(target); + const elem_bit_size = try bitSizeAdvanced(payload.elem_type, target, sema_kit); + return payload.len * 8 * elem_size + elem_bit_size; }, .isize, .usize, .@"anyframe", .anyframe_T, - => target.cpu.arch.ptrBitWidth(), + => return target.cpu.arch.ptrBitWidth(), .const_slice, .mut_slice, @@ -3662,7 +3661,7 @@ pub const Type = extern union { .const_slice_u8, .const_slice_u8_sentinel_0, - => target.cpu.arch.ptrBitWidth() * 2, + => return target.cpu.arch.ptrBitWidth() * 2, .optional_single_const_pointer, .optional_single_mut_pointer, @@ -3681,8 +3680,8 @@ pub const Type = extern union { }, .pointer => switch (ty.castTag(.pointer).?.data.size) { - .Slice => target.cpu.arch.ptrBitWidth() * 2, - else => target.cpu.arch.ptrBitWidth(), + .Slice => return target.cpu.arch.ptrBitWidth() * 2, + else => return target.cpu.arch.ptrBitWidth(), }, .manyptr_u8, @@ -3708,7 +3707,7 @@ pub const Type = extern union { .error_set_merged, => return 16, // TODO revisit this when we have the concept of the error tag type - .int_signed, .int_unsigned => ty.cast(Payload.Bits).?.data, + .int_signed, .int_unsigned => return ty.cast(Payload.Bits).?.data, .optional => { var buf: Payload.ElemType = undefined; @@ -3722,7 +3721,8 @@ pub const Type = extern union { // field and a boolean as the second. Since the child type's abi alignment is // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal // to the child type's ABI alignment. - return child_type.bitSize(target) + 1; + const child_bit_size = try bitSizeAdvanced(child_type, target, sema_kit); + return child_bit_size + 1; }, .error_union => { @@ -3730,9 +3730,9 @@ pub const Type = extern union { if (!payload.error_set.hasRuntimeBits() and !payload.payload.hasRuntimeBits()) { return 0; } else if (!payload.error_set.hasRuntimeBits()) { - return payload.payload.bitSize(target); + return payload.payload.bitSizeAdvanced(target, sema_kit); } else if (!payload.payload.hasRuntimeBits()) { - return payload.error_set.bitSize(target); + return payload.error_set.bitSizeAdvanced(target, sema_kit); } @panic("TODO bitSize error union"); }, @@ -3749,7 +3749,7 @@ pub const Type = extern union { .extern_options, .type_info, => @panic("TODO at some point we gotta resolve builtin types"), - }; + } } pub fn isSinglePointer(self: Type) bool { @@ -5506,6 +5506,7 @@ pub const Type = extern union { switch (ty.tag()) { .@"struct" => { const struct_obj = ty.castTag(.@"struct").?.data; + assert(struct_obj.haveFieldTypes()); return struct_obj.fields.count(); }, .empty_struct, .empty_struct_literal => return 0, diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index 7e59ce052e..ab5adc554a 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -243,7 +243,12 @@ test "correct sizeOf and offsets in packed structs" { } test "nested packed structs" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S1 = packed struct { a: u8, b: u8, c: u8 }; @@ -253,7 +258,7 @@ test "nested packed structs" { const S3Padded = packed struct { s3: S3, pad: u16 }; try expectEqual(48, @bitSizeOf(S3)); - try expectEqual(6, @sizeOf(S3)); + try expectEqual(@sizeOf(u48), @sizeOf(S3)); try expectEqual(3, @offsetOf(S3, "y")); try expectEqual(24, @bitOffsetOf(S3, "y")); @@ -273,7 +278,7 @@ test "nested packed structs" { const S6 = packed struct { a: i32, b: S4, c: i8 }; const expectedBitSize = 80; - const expectedByteSize = expectedBitSize / 8; + const expectedByteSize = @sizeOf(u80); try expectEqual(expectedBitSize, @bitSizeOf(S5)); try expectEqual(expectedByteSize, @sizeOf(S5)); try expectEqual(expectedBitSize, @bitSizeOf(S6)); From 62023c60b4809aee5b23a62c70927075fcce3375 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Jun 2022 22:39:28 +0200 Subject: [PATCH 1810/2031] stage2: correctly work out dirname for ar --- src/link.zig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/link.zig b/src/link.zig index 0aa5d0031d..51712db40e 100644 --- a/src/link.zig +++ b/src/link.zig @@ -797,9 +797,8 @@ pub const File = struct { } else { try base.flushModule(comp, prog_node); } - break :blk try fs.path.join(arena, &.{ - fs.path.dirname(full_out_path_z).?, base.intermediary_basename.?, - }); + const dirname = fs.path.dirname(full_out_path_z) orelse "."; + break :blk try fs.path.join(arena, &.{ dirname, base.intermediary_basename.? }); } else null; log.debug("module_obj_path={s}", .{if (module_obj_path) |s| s else "(null)"}); From 6bf529dc381cb2f5a83c5f9e303ffdab7779ac4d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 10 Jun 2022 17:55:17 -0700 Subject: [PATCH 1811/2031] link/wasm: fix writing past the end of debug info buffer The function `writeDbgInfoNopsBuffered` was based on the function `pwriteDbgInfoNops`, originally written by me, and then modified to write to a memory buffer instead of an open file. When writing to a file, any extra bytes beyond the end of the file extend the size of the file, and the function body of `pwriteDbgInfoNops` takes advantage of this when `next_padding_bytes` causes the write to go beyond the end of the file. However, when writing to a memory buffer, the underlying array list must be expanded if the write would cause the buffer to expand. --- src/link/Dwarf.zig | 54 ++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index f291dd4255..efbd86bc7f 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -352,6 +352,7 @@ pub const DeclState = struct { const fields = ty.structFields(); for (fields.keys()) |field_name, field_index| { const field = fields.get(field_name).?; + if (!field.ty.hasRuntimeBits()) continue; // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2); dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); @@ -1037,6 +1038,7 @@ pub fn commitDeclState( } } + log.debug("updateDeclDebugInfoAllocation for '{s}'", .{decl.name}); try self.updateDeclDebugInfoAllocation(file, atom, @intCast(u32, dbg_info_buffer.items.len)); while (decl_state.abbrev_relocs.popOrNull()) |reloc| { @@ -1098,6 +1100,7 @@ pub fn commitDeclState( } } + log.debug("writeDeclDebugInfo for '{s}", .{decl.name}); try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items); } @@ -1141,7 +1144,10 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3 }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - writeDbgInfoNopsBuffered(wasm_file.debug_info.items, atom.off, 0, &.{0}, atom.len, false); + const segment_index = try wasm_file.getDebugInfoIndex(); + const segment = &wasm_file.segments.items[segment_index]; + const offset = segment.offset + atom.off; + try writeDbgInfoNopsToArrayList(gpa, &wasm_file.debug_info, offset, 0, &.{0}, atom.len, false); }, else => unreachable, } @@ -1283,8 +1289,12 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co debug_info.items.len = needed_size; } const offset = segment.offset + atom.off; - writeDbgInfoNopsBuffered( - debug_info.items, + log.debug(" writeDbgInfoNopsToArrayList debug_info_len={d} offset={d} content_len={d} next_padding_size={d}", .{ + debug_info.items.len, offset, dbg_info_buf.len, next_padding_size, + }); + try writeDbgInfoNopsToArrayList( + gpa, + debug_info, offset, prev_padding_size, dbg_info_buf, @@ -1678,7 +1688,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6 }, .wasm => { const wasm_file = file.cast(File.Wasm).?; - writeDbgInfoNopsBuffered(wasm_file.debug_info.items, 0, 0, di_buf.items, jmp_amt, false); + try writeDbgInfoNopsToArrayList(self.allocator, &wasm_file.debug_info, 0, 0, di_buf.items, jmp_amt, false); }, else => unreachable, } @@ -1884,35 +1894,25 @@ fn pwriteDbgInfoNops( try file.pwritevAll(vecs[0..vec_index], offset - prev_padding_size); } -fn writeDbgInfoNopsBuffered( - buf: []u8, +fn writeDbgInfoNopsToArrayList( + gpa: Allocator, + buffer: *std.ArrayListUnmanaged(u8), offset: u32, prev_padding_size: usize, content: []const u8, next_padding_size: usize, trailing_zero: bool, -) void { - assert(buf.len >= content.len + prev_padding_size + next_padding_size + @boolToInt(trailing_zero)); - const tracy = trace(@src()); - defer tracy.end(); - - { - var padding_left = prev_padding_size; - while (padding_left > 0) : (padding_left -= 1) { - buf[offset - padding_left] = @enumToInt(AbbrevKind.pad1); - } - } - - mem.copy(u8, buf[offset..], content); - { - var padding_left = next_padding_size; - while (padding_left > 0) : (padding_left -= 1) { - buf[offset + content.len + padding_left] = @enumToInt(AbbrevKind.pad1); - } - } +) Allocator.Error!void { + try buffer.resize(gpa, @maximum( + buffer.items.len, + offset + content.len + next_padding_size + 1, + )); + mem.set(u8, buffer.items[offset - prev_padding_size .. offset], @enumToInt(AbbrevKind.pad1)); + mem.copy(u8, buffer.items[offset..], content); + mem.set(u8, buffer.items[offset + content.len ..][0..next_padding_size], @enumToInt(AbbrevKind.pad1)); if (trailing_zero) { - buf[offset + content.len + next_padding_size] = 0; + buffer.items[offset + content.len + next_padding_size] = 0; } } @@ -2249,7 +2249,9 @@ pub fn flushModule(self: *Dwarf, file: *File, module: *Module) !void { try addDbgInfoErrorSet(arena, module, error_ty, self.target, &dbg_info_buffer); try self.managed_atoms.append(gpa, atom); + log.debug("updateDeclDebugInfoAllocation in flushModule", .{}); try self.updateDeclDebugInfoAllocation(file, atom, @intCast(u32, dbg_info_buffer.items.len)); + log.debug("writeDeclDebugInfo in flushModule", .{}); try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items); const file_pos = blk: { From 9b05474d797ea4600dbae36ae95d9eb042040bb2 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Fri, 10 Jun 2022 07:21:54 -0700 Subject: [PATCH 1812/2031] ThreadPool: Make join() a no-op in single-threaded mode This comptime gate is needed to make sure that purely single-threaded programs don't generate calls to the std.Thread API. WASI targets successfully build again with this change. --- src/ThreadPool.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ThreadPool.zig b/src/ThreadPool.zig index 7d1c8420af..55e40ea287 100644 --- a/src/ThreadPool.zig +++ b/src/ThreadPool.zig @@ -49,6 +49,10 @@ pub fn deinit(self: *ThreadPool) void { } fn join(self: *ThreadPool, spawned: usize) void { + if (builtin.single_threaded) { + return; + } + { self.mutex.lock(); defer self.mutex.unlock(); From 002df65b6ec55684d6bc6790ae7b0c0f4abb1375 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 9 Jun 2022 15:36:18 +0300 Subject: [PATCH 1813/2031] Sema: handle tuple and anon_struct in resolveTypeFully --- lib/std/fmt.zig | 4 ---- lib/std/unicode.zig | 1 - src/Sema.zig | 14 ++++++++++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 9afb556b5a..79302682db 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -2111,7 +2111,6 @@ test "slice" { } test "escape non-printable" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; try expectFmt("abc", "{s}", .{fmtSliceEscapeLower("abc")}); try expectFmt("ab\\xffc", "{s}", .{fmtSliceEscapeLower("ab\xffc")}); try expectFmt("ab\\xFFc", "{s}", .{fmtSliceEscapeUpper("ab\xffc")}); @@ -2148,7 +2147,6 @@ test "cstr" { } test "filesize" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; try expectFmt("file size: 42B\n", "file size: {}\n", .{fmtIntSizeDec(42)}); try expectFmt("file size: 42B\n", "file size: {}\n", .{fmtIntSizeBin(42)}); try expectFmt("file size: 63MB\n", "file size: {}\n", .{fmtIntSizeDec(63 * 1000 * 1000)}); @@ -2448,7 +2446,6 @@ test "struct.zero-size" { } test "bytes.hex" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; const some_bytes = "\xCA\xFE\xBA\xBE"; try expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{fmtSliceHexLower(some_bytes)}); try expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{fmtSliceHexUpper(some_bytes)}); @@ -2480,7 +2477,6 @@ pub fn hexToBytes(out: []u8, input: []const u8) ![]u8 { } test "hexToBytes" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; var buf: [32]u8 = undefined; try expectFmt("90" ** 32, "{s}", .{fmtSliceHexUpper(try hexToBytes(&buf, "90" ** 32))}); try expectFmt("ABCD", "{s}", .{fmtSliceHexUpper(try hexToBytes(&buf, "ABCD"))}); diff --git a/lib/std/unicode.zig b/lib/std/unicode.zig index a0cf7f6624..81a7ed838f 100644 --- a/lib/std/unicode.zig +++ b/lib/std/unicode.zig @@ -804,7 +804,6 @@ pub fn fmtUtf16le(utf16le: []const u16) std.fmt.Formatter(formatUtf16le) { } test "fmtUtf16le" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; const expectFmt = std.testing.expectFmt; try expectFmt("", "{}", .{fmtUtf16le(utf8ToUtf16LeStringLiteral(""))}); try expectFmt("foo", "{}", .{fmtUtf16le(utf8ToUtf16LeStringLiteral("foo"))}); diff --git a/src/Sema.zig b/src/Sema.zig index 048a702e7b..fd127bc04a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -23328,7 +23328,17 @@ pub fn resolveTypeFully( const child_ty = try sema.resolveTypeFields(block, src, ty.childType()); return resolveTypeFully(sema, block, src, child_ty); }, - .Struct => return resolveStructFully(sema, block, src, ty), + .Struct => switch (ty.tag()) { + .@"struct" => return resolveStructFully(sema, block, src, ty), + .tuple, .anon_struct => { + const tuple = ty.tupleFields(); + + for (tuple.types) |field_ty| { + try sema.resolveTypeFully(block, src, field_ty); + } + }, + else => {}, + }, .Union => return resolveUnionFully(sema, block, src, ty), .Array => return resolveTypeFully(sema, block, src, ty.childType()), .Optional => { @@ -23363,7 +23373,7 @@ fn resolveStructFully( try resolveStructLayout(sema, block, src, ty); const resolved_ty = try sema.resolveTypeFields(block, src, ty); - const payload = resolved_ty.castTag(.@"struct") orelse return; + const payload = resolved_ty.castTag(.@"struct").?; const struct_obj = payload.data; switch (struct_obj.status) { From 0f820d0bdf98b3f8429a9cf2f1b405c2c0a4d958 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 9 Jun 2022 17:54:32 +0300 Subject: [PATCH 1814/2031] stage2: improve debugging tools llvm: dump failed module when -femit-llvm-ir set print_air: * print fully qualified name * use Type.fmt and Value.fmtValue, fmtDebug is useless TypedValue * handle anon structs and tuples * fix bugs --- src/Module.zig | 7 +++- src/TypedValue.zig | 75 ++++++++++++++++++++++++++++++----- src/codegen/llvm.zig | 19 +++++---- src/codegen/llvm/bindings.zig | 3 ++ src/print_air.zig | 38 ++++++++++++++---- 5 files changed, 114 insertions(+), 28 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index f03ba77a39..bcf6491ce6 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3790,9 +3790,12 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void { defer liveness.deinit(gpa); if (builtin.mode == .Debug and mod.comp.verbose_air) { - std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name}); + const fqn = try decl.getFullyQualifiedName(mod); + defer mod.gpa.free(fqn); + + std.debug.print("# Begin Function AIR: {s}:\n", .{fqn}); @import("print_air.zig").dump(mod, air, liveness); - std.debug.print("# End Function AIR: {s}\n\n", .{decl.name}); + std.debug.print("# End Function AIR: {s}\n\n", .{fqn}); } mod.comp.bin_file.updateFunc(mod, func, air, liveness) catch |err| switch (err) { diff --git a/src/TypedValue.zig b/src/TypedValue.zig index 4b3bc23231..3c1b28e544 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -144,7 +144,41 @@ pub fn print( return writer.writeAll(".{ ... }"); } const vals = val.castTag(.aggregate).?.data; - if (ty.zigTypeTag() == .Struct) { + if (ty.castTag(.anon_struct)) |anon_struct| { + const field_names = anon_struct.data.names; + const types = anon_struct.data.types; + const max_len = std.math.min(types.len, max_aggregate_items); + + var i: u32 = 0; + while (i < max_len) : (i += 1) { + if (i != 0) try writer.writeAll(", "); + try writer.print(".{s} = ", .{field_names[i]}); + try print(.{ + .ty = types[i], + .val = vals[i], + }, writer, level - 1, mod); + } + if (types.len > max_aggregate_items) { + try writer.writeAll(", ..."); + } + return writer.writeAll(" }"); + } else if (ty.isTuple()) { + const fields = ty.tupleFields(); + const max_len = std.math.min(fields.types.len, max_aggregate_items); + + var i: u32 = 0; + while (i < max_len) : (i += 1) { + if (i != 0) try writer.writeAll(", "); + try print(.{ + .ty = fields.types[i], + .val = vals[i], + }, writer, level - 1, mod); + } + if (fields.types.len > max_aggregate_items) { + try writer.writeAll(", ..."); + } + return writer.writeAll(" }"); + } else if (ty.zigTypeTag() == .Struct) { try writer.writeAll(".{ "); const struct_fields = ty.structFields(); const len = struct_fields.count(); @@ -194,7 +228,7 @@ pub fn print( try writer.writeAll(".{ "); try print(.{ - .ty = ty.unionTagType().?, + .ty = ty.cast(Type.Payload.Union).?.data.tag_ty, .val = union_val.tag, }, writer, level - 1, mod); try writer.writeAll(" = "); @@ -278,19 +312,27 @@ pub fn print( .elem_ptr => { const elem_ptr = val.castTag(.elem_ptr).?.data; try writer.writeAll("&"); - try print(.{ - .ty = elem_ptr.elem_ty, - .val = elem_ptr.array_ptr, - }, writer, level - 1, mod); + if (level == 0) { + try writer.writeAll("(ptr)"); + } else { + try print(.{ + .ty = elem_ptr.elem_ty, + .val = elem_ptr.array_ptr, + }, writer, level - 1, mod); + } return writer.print("[{}]", .{elem_ptr.index}); }, .field_ptr => { const field_ptr = val.castTag(.field_ptr).?.data; try writer.writeAll("&"); - try print(.{ - .ty = field_ptr.container_ty, - .val = field_ptr.container_ptr, - }, writer, level - 1, mod); + if (level == 0) { + try writer.writeAll("(ptr)"); + } else { + try print(.{ + .ty = field_ptr.container_ty, + .val = field_ptr.container_ptr, + }, writer, level - 1, mod); + } if (field_ptr.container_ty.zigTypeTag() == .Struct) { const field_name = field_ptr.container_ty.structFields().keys()[field_ptr.field_index]; @@ -344,6 +386,9 @@ pub fn print( return writer.writeAll(" }"); }, .slice => { + if (level == 0) { + return writer.writeAll(".{ ... }"); + } const payload = val.castTag(.slice).?.data; try writer.writeAll(".{ "); const elem_ty = ty.elemType2(); @@ -372,17 +417,25 @@ pub fn print( .@"error" => return writer.print("error.{s}", .{val.castTag(.@"error").?.data.name}), .eu_payload => { val = val.castTag(.eu_payload).?.data; + ty = ty.errorUnionPayload(); }, .opt_payload => { val = val.castTag(.opt_payload).?.data; + var buf: Type.Payload.ElemType = undefined; + ty = ty.optionalChild(&buf); + return print(.{ .ty = ty, .val = val }, writer, level, mod); }, .eu_payload_ptr => { try writer.writeAll("&"); val = val.castTag(.eu_payload_ptr).?.data.container_ptr; + ty = ty.elemType2().errorUnionPayload(); }, .opt_payload_ptr => { try writer.writeAll("&"); - val = val.castTag(.opt_payload_ptr).?.data.container_ptr; + val = val.castTag(.opt_payload).?.data; + var buf: Type.Payload.ElemType = undefined; + ty = ty.elemType2().optionalChild(&buf); + return print(.{ .ty = ty, .val = val }, writer, level, mod); }, // TODO these should not appear in this function diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 19a6917be4..8031c4635e 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -599,6 +599,13 @@ pub const Object = struct { self.llvm_module.dump(); } + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + const mod = comp.bin_file.options.module.?; + const cache_dir = mod.zig_cache_artifact_directory; + if (std.debug.runtime_safety) { var error_message: [*:0]const u8 = undefined; // verifyModule always allocs the error_message even if there is no error @@ -606,17 +613,15 @@ pub const Object = struct { if (self.llvm_module.verify(.ReturnStatus, &error_message).toBool()) { std.debug.print("\n{s}\n", .{error_message}); + + if (try locPath(arena, comp.emit_llvm_ir, cache_dir)) |emit_llvm_ir_path| { + _ = self.llvm_module.printModuleToFile(emit_llvm_ir_path, &error_message); + } + @panic("LLVM module verification failed"); } } - var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); - - const mod = comp.bin_file.options.module.?; - const cache_dir = mod.zig_cache_artifact_directory; - var emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit| try emit.basenamePath(arena, try arena.dupeZ(u8, comp.bin_file.intermediary_basename.?)) else diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index ca748d3ce3..671014ba3b 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -390,6 +390,9 @@ pub const Module = opaque { pub const setModuleInlineAsm2 = LLVMSetModuleInlineAsm2; extern fn LLVMSetModuleInlineAsm2(M: *const Module, Asm: [*]const u8, Len: usize) void; + + pub const printModuleToFile = LLVMPrintModuleToFile; + extern fn LLVMPrintModuleToFile(M: *const Module, Filename: [*:0]const u8, ErrorMessage: *[*:0]const u8) Bool; }; pub const lookupIntrinsicID = LLVMLookupIntrinsicID; diff --git a/src/print_air.zig b/src/print_air.zig index af1bcb8cfb..d6db7ca75f 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -4,6 +4,7 @@ const fmtIntSizeBin = std.fmt.fmtIntSizeBin; const Module = @import("Module.zig"); const Value = @import("value.zig").Value; +const Type = @import("type.zig").Type; const Air = @import("Air.zig"); const Liveness = @import("Liveness.zig"); @@ -304,14 +305,27 @@ const Writer = struct { // no-op, no argument to write } + fn writeType(w: *Writer, s: anytype, ty: Type) !void { + const t = ty.tag(); + switch (t) { + .inferred_alloc_const => try s.writeAll("(inferred_alloc_const)"), + .inferred_alloc_mut => try s.writeAll("(inferred_alloc_mut)"), + .generic_poison => try s.writeAll("(generic_poison)"), + .var_args_param => try s.writeAll("(var_args_param)"), + .bound_fn => try s.writeAll("(bound_fn)"), + else => try ty.print(s, w.module), + } + } + fn writeTy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const ty = w.air.instructions.items(.data)[inst].ty; - try s.print("{}", .{ty.fmtDebug()}); + try w.writeType(s, ty); } fn writeTyOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const ty_op = w.air.instructions.items(.data)[inst].ty_op; - try s.print("{}, ", .{w.air.getRefType(ty_op.ty).fmtDebug()}); + try w.writeType(s, w.air.getRefType(ty_op.ty)); + try s.writeAll(", "); try w.writeOperand(s, inst, 0, ty_op.operand); } @@ -320,7 +334,8 @@ const Writer = struct { const extra = w.air.extraData(Air.Block, ty_pl.payload); const body = w.air.extra[extra.end..][0..extra.data.body_len]; - try s.print("{}, {{\n", .{w.air.getRefType(ty_pl.ty).fmtDebug()}); + try w.writeType(s, w.air.getRefType(ty_pl.ty)); + try s.writeAll(", {\n"); const old_indent = w.indent; w.indent += 2; try w.writeBody(s, body); @@ -335,7 +350,8 @@ const Writer = struct { const len = @intCast(usize, vector_ty.arrayLen()); const elements = @ptrCast([]const Air.Inst.Ref, w.air.extra[ty_pl.payload..][0..len]); - try s.print("{}, [", .{vector_ty.fmtDebug()}); + try w.writeType(s, vector_ty); + try s.writeAll(", ["); for (elements) |elem, i| { if (i != 0) try s.writeAll(", "); try w.writeOperand(s, inst, i, elem); @@ -408,7 +424,8 @@ const Writer = struct { const extra = w.air.extraData(Air.Bin, pl_op.payload).data; const elem_ty = w.air.typeOfIndex(inst).childType(); - try s.print("{}, ", .{elem_ty.fmtDebug()}); + try w.writeType(s, elem_ty); + try s.writeAll(", "); try w.writeOperand(s, inst, 0, pl_op.operand); try s.writeAll(", "); try w.writeOperand(s, inst, 1, extra.lhs); @@ -511,7 +528,9 @@ const Writer = struct { fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; const val = w.air.values[ty_pl.payload]; - try s.print("{}, {}", .{ w.air.getRefType(ty_pl.ty).fmtDebug(), val.fmtDebug() }); + const ty = w.air.getRefType(ty_pl.ty); + try w.writeType(s, ty); + try s.print(", {}", .{val.fmtValue(ty, w.module)}); } fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { @@ -523,7 +542,7 @@ const Writer = struct { var op_index: usize = 0; const ret_ty = w.air.typeOfIndex(inst); - try s.print("{}", .{ret_ty.fmtDebug()}); + try w.writeType(s, ret_ty); if (is_volatile) { try s.writeAll(", volatile"); @@ -647,7 +666,10 @@ const Writer = struct { const body = w.air.extra[extra.end..][0..extra.data.body_len]; try w.writeOperand(s, inst, 0, extra.data.ptr); - try s.print(", {}, {{\n", .{w.air.getRefType(ty_pl.ty).fmtDebug()}); + + try s.writeAll(", "); + try w.writeType(s, w.air.getRefType(ty_pl.ty)); + try s.writeAll(", {\n"); const old_indent = w.indent; w.indent += 2; try w.writeBody(s, body); From eaa6b04c3cb8073d5a1510560ec8f1e433984895 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 10 Jun 2022 00:11:46 +0300 Subject: [PATCH 1815/2031] Sema: skip decl causing namespace lookup when doing lookup --- src/Sema.zig | 2 ++ test/behavior/basic.zig | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index fd127bc04a..9a34cce6e0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4967,6 +4967,8 @@ fn lookupInNamespace( var it = check_ns.usingnamespace_set.iterator(); while (it.next()) |entry| { const sub_usingnamespace_decl_index = entry.key_ptr.*; + // Skip the decl we're currently analysing. + if (sub_usingnamespace_decl_index == sema.owner_decl_index) continue; const sub_usingnamespace_decl = mod.declPtr(sub_usingnamespace_decl_index); const sub_is_pub = entry.value_ptr.*; if (!sub_is_pub and src_file != sub_usingnamespace_decl.getFileScope()) { diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index a69df862c1..ac9d90d5e6 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -1086,3 +1086,26 @@ test "inline call of function with a switch inside the return statement" { }; try expect(S.foo(1) == 1); } + +test "namespace lookup ignores decl causing the lookup" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn Mixin(comptime T: type) type { + return struct { + fn foo() void { + const set = std.EnumSet(T.E).init(undefined); + _ = set; + } + }; + } + + const E = enum { a, b }; + usingnamespace Mixin(@This()); + }; + _ = S.foo(); +} From bc499de328f0d0110a8eaf3de7be55e05595f598 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Mon, 6 Jun 2022 21:20:51 +0200 Subject: [PATCH 1816/2031] wasm: implement `@byteSwap` for 16/32bit integers --- src/arch/wasm/CodeGen.zig | 49 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 33cf5422f2..1c9fe192d7 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1541,6 +1541,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .union_init => self.airUnionInit(inst), .prefetch => self.airPrefetch(inst), .popcount => self.airPopcount(inst), + .byte_swap => self.airByteSwap(inst), .slice => self.airSlice(inst), .slice_len => self.airSliceLen(inst), @@ -1590,7 +1591,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .shl_sat, .ret_addr, .frame_addr, - .byte_swap, .bit_reverse, .is_err_ptr, .is_non_err_ptr, @@ -4691,3 +4691,50 @@ fn lowerTry( } return self.load(err_union, pl_ty, pl_offset); } + +fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + if (self.liveness.isUnused(inst)) { + return WValue{ .none = {} }; + } + + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const ty = self.air.typeOfIndex(inst); + const operand = try self.resolveInst(ty_op.operand); + + if (ty.zigTypeTag() == .Vector) { + return self.fail("TODO: @byteSwap for vectors", .{}); + } + const int_info = ty.intInfo(self.target); + + // bytes are no-op + if (int_info.bits == 8) { + return operand; + } + + switch (int_info.bits) { + 16 => { + const shl_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl); + const tmp = try self.binOp(operand, .{ .imm32 = 0xFF00 }, ty, .@"and"); + const shr_res = try self.binOp(tmp, .{ .imm32 = 8 }, ty, .shr); + const res = if (int_info.signedness == .signed) blk: { + break :blk try self.wrapOperand(shr_res, Type.u8); + } else shr_res; + return self.binOp(shl_res, res, ty, .@"or"); + }, + 32 => { + const shl_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl); + const lhs = try self.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, ty, .@"and"); + const shr_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr); + const rhs = try self.binOp(shr_tmp, .{ .imm32 = 0xFF00FF }, ty, .@"and"); + const tmp_or = try self.binOp(lhs, rhs, ty, .@"or"); + + const shl = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shl); + const shr = try self.binOp(tmp_or, .{ .imm32 = 16 }, ty, .shr); + const res = if (int_info.signedness == .signed) blk: { + break :blk try self.wrapOperand(shr, Type.u16); + } else shr; + return self.binOp(shl, res, ty, .@"or"); + }, + else => return self.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}), + } +} From 180baa05465df6440f9953d8ff4d7880322b03f0 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 8 Jun 2022 19:08:52 +0200 Subject: [PATCH 1817/2031] wasm:`@byteSwap` for 24 bit integers --- src/arch/wasm/CodeGen.zig | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 1c9fe192d7..e628c42287 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -4714,12 +4714,31 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue { switch (int_info.bits) { 16 => { const shl_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl); - const tmp = try self.binOp(operand, .{ .imm32 = 0xFF00 }, ty, .@"and"); - const shr_res = try self.binOp(tmp, .{ .imm32 = 8 }, ty, .shr); + const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and"); + const shr_res = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shr); const res = if (int_info.signedness == .signed) blk: { break :blk try self.wrapOperand(shr_res, Type.u8); } else shr_res; - return self.binOp(shl_res, res, ty, .@"or"); + return self.binOp(lhs, res, ty, .@"or"); + }, + 24 => { + const msb = try self.wrapOperand(operand, Type.u16); + const lsb = try self.wrapBinOp(operand, .{ .imm32 = 16 }, Type.u8, .shr); + + const shl_res = try self.binOp(msb, .{ .imm32 = 8 }, Type.u16, .shl); + const lhs = try self.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and"); + const shr_res = try self.binOp(msb, .{ .imm32 = 8 }, ty, .shr); + + const res = if (int_info.signedness == .signed) blk: { + break :blk try self.wrapOperand(shr_res, Type.u8); + } else shr_res; + const lhs_tmp = try self.binOp(lhs, res, ty, .@"or"); + const lhs_result = try self.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr); + const rhs_wrap = try self.wrapOperand(msb, Type.u8); + const rhs_result = try self.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl); + + const tmp = try self.binOp(lhs_result, rhs_result, ty, .@"or"); + return self.binOp(tmp, lsb, ty, .@"or"); }, 32 => { const shl_tmp = try self.binOp(operand, .{ .imm32 = 8 }, ty, .shl); From 9b84f29503ede2088238e39daa4cf17a571ed790 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 8 Jun 2022 20:00:04 +0200 Subject: [PATCH 1818/2031] wasm: support all `@div{trunc/floor/exact}` ops This does however not support floats of bitsizes different than 32 or 64. f16, f80, f126 will require support for compiler-rt and are out-of-scope for this commit. Signed integers are currently not supported either. --- src/arch/wasm/CodeGen.zig | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index e628c42287..496c223970 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1441,7 +1441,11 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .subwrap => self.airWrapBinOp(inst, .sub), .mul => self.airBinOp(inst, .mul), .mulwrap => self.airWrapBinOp(inst, .mul), - .div_trunc => self.airBinOp(inst, .div), + .div_float, + .div_exact, + .div_trunc, + => self.airBinOp(inst, .div), + .div_floor => self.airDivFloor(inst), .bit_and => self.airBinOp(inst, .@"and"), .bit_or => self.airBinOp(inst, .@"or"), .bool_and => self.airBinOp(inst, .@"and"), @@ -1583,9 +1587,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .add_sat, .sub_sat, .mul_sat, - .div_float, - .div_floor, - .div_exact, .mod, .assembly, .shl_sat, @@ -4757,3 +4758,30 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue { else => return self.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}), } } + +fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const ty = self.air.typeOfIndex(inst); + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + const div_result = try self.binOp(lhs, rhs, ty, .div); + if (ty.isUnsignedInt()) { + return div_result; + } else if (ty.isSignedInt()) { + return self.fail("TODO: `@divFloor` for signed integers", .{}); + } + + try self.emitWValue(div_result); + switch (ty.floatBits(self.target)) { + 32 => try self.addTag(.f32_floor), + 64 => try self.addTag(.f64_floor), + else => |bit_size| return self.fail("TODO: `@divFloor` for floats with bitsize: {d}", .{bit_size}), + } + + const result = try self.allocLocal(ty); + try self.addLabel(.local_set, result.local); + return result; +} From 3011ef2d82fe29d47b9c6877b6a8bbea0938d7c4 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 10 Jun 2022 20:37:31 +0200 Subject: [PATCH 1819/2031] wasm: signed integer division (non-floor) Implements the non-floor variants of signed integer division. --- src/arch/wasm/CodeGen.zig | 49 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 496c223970..3e0612307e 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1444,7 +1444,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .div_float, .div_exact, .div_trunc, - => self.airBinOp(inst, .div), + => self.airDiv(inst), .div_floor => self.airDivFloor(inst), .bit_and => self.airBinOp(inst, .@"and"), .bit_or => self.airBinOp(inst, .@"or"), @@ -4759,6 +4759,20 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } } +fn airDiv(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const ty = self.air.typeOfIndex(inst); + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + if (ty.isSignedInt()) { + return self.divSigned(lhs, rhs, ty); + } + return self.binOp(lhs, rhs, ty, .div); +} + fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; @@ -4785,3 +4799,36 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.addLabel(.local_set, result.local); return result; } + +fn divSigned(self: *Self, lhs: WValue, rhs: WValue, ty: Type) InnerError!WValue { + const int_bits = ty.intInfo(self.target).bits; + const wasm_bits = toWasmBits(int_bits) orelse { + return self.fail("TODO: Implement signed division for integers with bitsize '{d}'", .{int_bits}); + }; + + if (wasm_bits == 128) { + return self.fail("TODO: Implement signed division for 128-bit integerrs", .{}); + } + + if (wasm_bits != int_bits) { + const shift_val = switch (wasm_bits) { + 32 => WValue{ .imm32 = wasm_bits - int_bits }, + 64 => WValue{ .imm64 = wasm_bits - int_bits }, + else => unreachable, + }; + const shl_lhs = try self.binOp(lhs, shift_val, ty, .shl); + const shr_lhs = try self.binOp(shl_lhs, shift_val, ty, .shr); + const shl_rhs = try self.binOp(rhs, shift_val, ty, .shl); + const shr_rhs = try self.binOp(shl_rhs, shift_val, ty, .shr); + try self.emitWValue(shr_lhs); + try self.emitWValue(shr_rhs); + } else { + try self.emitWValue(lhs); + try self.emitWValue(rhs); + } + try self.addTag(.i32_div_s); + + const result = try self.allocLocal(ty); + try self.addLabel(.local_set, result.local); + return result; +} From 18afcc34c61a18ada7bda0fc50f48e929866ab82 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 10 Jun 2022 21:53:43 +0200 Subject: [PATCH 1820/2031] wasm: implement `@divFloor` for signed integers --- src/arch/wasm/CodeGen.zig | 111 ++++++++++++++++++++++++++++++-------- 1 file changed, 89 insertions(+), 22 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 3e0612307e..41ca7d3a7b 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2266,7 +2266,7 @@ fn toTwosComplement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo( const WantedT = std.meta.Int(.unsigned, @typeInfo(T).Int.bits); if (value >= 0) return @bitCast(WantedT, value); const max_value = @intCast(u64, (@as(u65, 1) << bits) - 1); - const flipped = (~-value) + 1; + const flipped = @intCast(T, (~-@as(i65, value)) + 1); const result = @bitCast(WantedT, flipped) & max_value; return @intCast(WantedT, result); } @@ -2294,7 +2294,10 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { val.toSignedInt(), @intCast(u6, int_info.bits), )) }, - 33...64 => return WValue{ .imm64 = @bitCast(u64, val.toSignedInt()) }, + 33...64 => return WValue{ .imm64 = toTwosComplement( + val.toSignedInt(), + @intCast(u7, int_info.bits), + ) }, else => unreachable, }, .unsigned => switch (int_info.bits) { @@ -4781,18 +4784,56 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const div_result = try self.binOp(lhs, rhs, ty, .div); if (ty.isUnsignedInt()) { - return div_result; + return self.binOp(lhs, rhs, ty, .div); } else if (ty.isSignedInt()) { - return self.fail("TODO: `@divFloor` for signed integers", .{}); - } + const int_bits = ty.intInfo(self.target).bits; + const wasm_bits = toWasmBits(int_bits) orelse { + return self.fail("TODO: `@divFloor` for signed integers larger than '{d}' bits", .{int_bits}); + }; + const lhs_res = if (wasm_bits != int_bits) blk: { + break :blk try self.signAbsValue(lhs, ty); + } else lhs; + const rhs_res = if (wasm_bits != int_bits) blk: { + break :blk try self.signAbsValue(rhs, ty); + } else rhs; - try self.emitWValue(div_result); - switch (ty.floatBits(self.target)) { - 32 => try self.addTag(.f32_floor), - 64 => try self.addTag(.f64_floor), - else => |bit_size| return self.fail("TODO: `@divFloor` for floats with bitsize: {d}", .{bit_size}), + const div_result = try self.binOp(lhs_res, rhs_res, ty, .div); + const rem_result = try self.binOp(lhs_res, rhs_res, ty, .rem); + + const zero = switch (wasm_bits) { + 32 => WValue{ .imm32 = 0 }, + 64 => WValue{ .imm64 = 0 }, + else => unreachable, + }; + const lhs_less_than_zero = try self.cmp(lhs_res, zero, ty, .lt); + const rhs_less_than_zero = try self.cmp(rhs_res, zero, ty, .lt); + + try self.emitWValue(div_result); + try self.emitWValue(lhs_less_than_zero); + try self.emitWValue(rhs_less_than_zero); + switch (wasm_bits) { + 32 => { + try self.addTag(.i32_xor); + try self.addTag(.i32_sub); + }, + 64 => { + try self.addTag(.i64_xor); + try self.addTag(.i64_sub); + }, + else => unreachable, + } + try self.emitWValue(div_result); + try self.emitWValue(rem_result); + try self.addTag(.select); + } else { + const div_result = try self.binOp(lhs, rhs, ty, .div); + try self.emitWValue(div_result); + switch (ty.floatBits(self.target)) { + 32 => try self.addTag(.f32_floor), + 64 => try self.addTag(.f64_floor), + else => |bit_size| return self.fail("TODO: `@divFloor` for floats with bitsize: {d}", .{bit_size}), + } } const result = try self.allocLocal(ty); @@ -4811,17 +4852,10 @@ fn divSigned(self: *Self, lhs: WValue, rhs: WValue, ty: Type) InnerError!WValue } if (wasm_bits != int_bits) { - const shift_val = switch (wasm_bits) { - 32 => WValue{ .imm32 = wasm_bits - int_bits }, - 64 => WValue{ .imm64 = wasm_bits - int_bits }, - else => unreachable, - }; - const shl_lhs = try self.binOp(lhs, shift_val, ty, .shl); - const shr_lhs = try self.binOp(shl_lhs, shift_val, ty, .shr); - const shl_rhs = try self.binOp(rhs, shift_val, ty, .shl); - const shr_rhs = try self.binOp(shl_rhs, shift_val, ty, .shr); - try self.emitWValue(shr_lhs); - try self.emitWValue(shr_rhs); + const lhs_abs = try self.signAbsValue(lhs, ty); + const rhs_abs = try self.signAbsValue(rhs, ty); + try self.emitWValue(lhs_abs); + try self.emitWValue(rhs_abs); } else { try self.emitWValue(lhs); try self.emitWValue(rhs); @@ -4832,3 +4866,36 @@ fn divSigned(self: *Self, lhs: WValue, rhs: WValue, ty: Type) InnerError!WValue try self.addLabel(.local_set, result.local); return result; } + +fn signAbsValue(self: *Self, operand: WValue, ty: Type) InnerError!WValue { + const int_bits = ty.intInfo(self.target).bits; + const wasm_bits = toWasmBits(int_bits) orelse { + return self.fail("TODO: signAbsValue for signed integers larger than '{d}' bits", .{int_bits}); + }; + + const shift_val = switch (wasm_bits) { + 32 => WValue{ .imm32 = wasm_bits - int_bits }, + 64 => WValue{ .imm64 = wasm_bits - int_bits }, + else => return self.fail("TODO: signAbsValue for i128", .{}), + }; + + try self.emitWValue(operand); + switch (wasm_bits) { + 32 => { + try self.emitWValue(shift_val); + try self.addTag(.i32_shl); + try self.emitWValue(shift_val); + try self.addTag(.i32_shr_s); + }, + 64 => { + try self.emitWValue(shift_val); + try self.addTag(.i64_shl); + try self.emitWValue(shift_val); + try self.addTag(.i64_shr_s); + }, + else => unreachable, + } + const result = try self.allocLocal(ty); + try self.addLabel(.local_set, result.local); + return result; +} From f05e09a0cf7870fda463d9a0ceb42be4c3100825 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 10 Jun 2022 21:54:23 +0200 Subject: [PATCH 1821/2031] wasm: optimize & simplify sign extension Rather than storing all the shifts in temporaries, we perform the correct shifting without temporaries. This makes the runtime code more performant and also the backend code is simplified as we have a singular abstraction. --- src/arch/wasm/CodeGen.zig | 51 +++++++++------------------------------ 1 file changed, 12 insertions(+), 39 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 41ca7d3a7b..02a955d8a6 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -4162,22 +4162,14 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W 64 => WValue{ .imm64 = 0 }, else => unreachable, }; - const shift_amt = wasm_bits - int_info.bits; - const shift_val = switch (wasm_bits) { - 32 => WValue{ .imm32 = shift_amt }, - 64 => WValue{ .imm64 = shift_amt }, - else => unreachable, - }; // for signed integers, we first apply signed shifts by the difference in bits // to get the signed value, as we store it internally as 2's complement. const lhs = if (wasm_bits != int_info.bits and is_signed) blk: { - const shl = try self.binOp(lhs_op, shift_val, lhs_ty, .shl); - break :blk try self.binOp(shl, shift_val, lhs_ty, .shr); + break :blk try self.signAbsValue(lhs_op, lhs_ty); } else lhs_op; const rhs = if (wasm_bits != int_info.bits and is_signed) blk: { - const shl = try self.binOp(rhs_op, shift_val, lhs_ty, .shl); - break :blk try self.binOp(shl, shift_val, lhs_ty, .shr); + break :blk try self.signAbsValue(rhs_op, lhs_ty); } else rhs_op; const bin_op = try self.binOp(lhs, rhs, lhs_ty, op); @@ -4192,9 +4184,8 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W const lt = try self.cmp(bin_op, lhs, lhs_ty, .lt); break :blk try self.binOp(cmp_zero, lt, Type.u32, .xor); // result of cmp_zero and lt is always 32bit } - const shl = try self.binOp(bin_op, shift_val, lhs_ty, .shl); - const shr = try self.binOp(shl, shift_val, lhs_ty, .shr); - break :blk try self.cmp(shr, bin_op, lhs_ty, .neq); + const abs = try self.signAbsValue(bin_op, lhs_ty); + break :blk try self.cmp(abs, bin_op, lhs_ty, .neq); } else if (wasm_bits == int_info.bits) try self.cmp(bin_op, lhs, lhs_ty, cmp_op) else @@ -4289,17 +4280,9 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } else shl; const overflow_bit = if (wasm_bits != int_info.bits and is_signed) blk: { - const shift_amt = wasm_bits - int_info.bits; - const shift_val = switch (wasm_bits) { - 32 => WValue{ .imm32 = shift_amt }, - 64 => WValue{ .imm64 = shift_amt }, - else => unreachable, - }; - - const secondary_shl = try self.binOp(shl, shift_val, lhs_ty, .shl); - const initial_shr = try self.binOp(secondary_shl, shift_val, lhs_ty, .shr); - const shr = try self.wrapBinOp(initial_shr, rhs, lhs_ty, .shr); - break :blk try self.cmp(lhs, shr, lhs_ty, .neq); + const abs = try self.signAbsValue(shl, lhs_ty); + const wrapped = try self.wrapBinOp(abs, rhs, lhs_ty, .shr); + break :blk try self.cmp(lhs, wrapped, lhs_ty, .neq); } else blk: { const shr = try self.binOp(result, rhs, lhs_ty, .shr); break :blk try self.cmp(lhs, shr, lhs_ty, .neq); @@ -4367,21 +4350,11 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { break :blk down_cast; } } else if (int_info.signedness == .signed) blk: { - const shift_imm = if (wasm_bits == 32) - WValue{ .imm32 = wasm_bits - int_info.bits } - else - WValue{ .imm64 = wasm_bits - int_info.bits }; - - const lhs_shl = try self.binOp(lhs, shift_imm, lhs_ty, .shl); - const lhs_shr = try self.binOp(lhs_shl, shift_imm, lhs_ty, .shr); - const rhs_shl = try self.binOp(rhs, shift_imm, lhs_ty, .shl); - const rhs_shr = try self.binOp(rhs_shl, shift_imm, lhs_ty, .shr); - - const bin_op = try self.binOp(lhs_shr, rhs_shr, lhs_ty, .mul); - const shl = try self.binOp(bin_op, shift_imm, lhs_ty, .shl); - const shr = try self.binOp(shl, shift_imm, lhs_ty, .shr); - - const cmp_op = try self.cmp(shr, bin_op, lhs_ty, .neq); + const lhs_abs = try self.signAbsValue(lhs, lhs_ty); + const rhs_abs = try self.signAbsValue(rhs, lhs_ty); + const bin_op = try self.binOp(lhs_abs, rhs_abs, lhs_ty, .mul); + const mul_abs = try self.signAbsValue(bin_op, lhs_ty); + const cmp_op = try self.cmp(mul_abs, bin_op, lhs_ty, .neq); try self.emitWValue(cmp_op); try self.addLabel(.local_set, overflow_bit.local); break :blk try self.wrapOperand(bin_op, lhs_ty); From 13123afedbf05cf4646a7e5f39a95a4b06b45285 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 11 Jun 2022 16:14:52 +0200 Subject: [PATCH 1822/2031] wasm: implement `@ceil`, `@floor` and `@trunc` --- src/arch/wasm/CodeGen.zig | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 02a955d8a6..a0bb84a9d7 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1446,6 +1446,9 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .div_trunc, => self.airDiv(inst), .div_floor => self.airDivFloor(inst), + .ceil => self.airCeilFloorTrunc(inst, .ceil), + .floor => self.airCeilFloorTrunc(inst, .floor), + .trunc_float => self.airCeilFloorTrunc(inst, .trunc), .bit_and => self.airBinOp(inst, .@"and"), .bit_or => self.airBinOp(inst, .@"or"), .bool_and => self.airBinOp(inst, .@"and"), @@ -1606,10 +1609,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .log2, .log10, .fabs, - .floor, - .ceil, .round, - .trunc_float, .cmpxchg_weak, .cmpxchg_strong, @@ -4872,3 +4872,31 @@ fn signAbsValue(self: *Self, operand: WValue, ty: Type) InnerError!WValue { try self.addLabel(.local_set, result.local); return result; } + +fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + + const un_op = self.air.instructions.items(.data)[inst].un_op; + const ty = self.air.typeOfIndex(inst); + + if (ty.zigTypeTag() == .Vector) { + return self.fail("TODO: Implement `@ceil` for vectors", .{}); + } + + const operand = try self.resolveInst(un_op); + try self.emitWValue(operand); + switch (ty.floatBits(self.target)) { + 32, 64 => { + const opcode = buildOpcode(.{ + .op = op, + .valtype1 = typeToValtype(ty, self.target), + }); + try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); + }, + else => |bit_size| return self.fail("TODO: Implement `@ceil` for floats with bitsize {d}", .{bit_size}), + } + + const result = try self.allocLocal(ty); + try self.addLabel(.local_set, result.local); + return result; +} From 9360cfebc722d99a54b576fff8bcfc0f0354ad41 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 11 Jun 2022 12:18:04 -0700 Subject: [PATCH 1823/2031] Sema: type safety for "runtime_index" field This commit does not change any behavior, but changes the type of the runtime_index field from u32 to a non-exhaustive enum. This allows us to put `std.math.maxInt(u32)` only in the enum type definition and give it an official meaning. --- src/Sema.zig | 21 ++++++++++----------- src/value.zig | 12 +++++++++++- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 048a702e7b..073f1e7e2e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -135,7 +135,7 @@ pub const Block = struct { src_decl: Decl.Index, /// Non zero if a non-inline loop or a runtime conditional have been encountered. /// Stores to to comptime variables are only allowed when var.runtime_index <= runtime_index. - runtime_index: u32 = 0, + runtime_index: Value.RuntimeIndex = .zero, is_comptime: bool, is_typeof: bool = false, @@ -4276,7 +4276,7 @@ fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError child_block.label = &label; child_block.runtime_cond = null; child_block.runtime_loop = src; - child_block.runtime_index += 1; + child_block.runtime_index.increment(); const merges = &child_block.label.?.merges; defer child_block.instructions.deinit(gpa); @@ -4795,7 +4795,7 @@ fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError const br_ref = try start_block.addBr(label.merges.block_inst, operand); try label.merges.results.append(sema.gpa, operand); try label.merges.br_list.append(sema.gpa, Air.refToIndex(br_ref).?); - block.runtime_index += 1; + block.runtime_index.increment(); if (block.runtime_cond == null and block.runtime_loop == null) { block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop; block.runtime_loop = start_block.runtime_loop; @@ -8650,7 +8650,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError var case_block = child_block.makeSubBlock(); case_block.runtime_loop = null; case_block.runtime_cond = operand_src; - case_block.runtime_index += 1; + case_block.runtime_index.increment(); defer case_block.instructions.deinit(gpa); var extra_index: usize = special.end; @@ -12938,7 +12938,7 @@ fn zirBoolBr( var child_block = parent_block.makeSubBlock(); child_block.runtime_loop = null; child_block.runtime_cond = lhs_src; - child_block.runtime_index += 1; + child_block.runtime_index.increment(); defer child_block.instructions.deinit(gpa); var then_block = child_block.makeSubBlock(); @@ -13066,7 +13066,7 @@ fn zirCondbr( var sub_block = parent_block.makeSubBlock(); sub_block.runtime_loop = null; sub_block.runtime_cond = cond_src; - sub_block.runtime_index += 1; + sub_block.runtime_index.increment(); defer sub_block.instructions.deinit(gpa); _ = sema.analyzeBodyInner(&sub_block, then_body) catch |err| switch (err) { @@ -13263,7 +13263,7 @@ fn addRuntimeBreak(sema: *Sema, child_block: *Block, break_data: BreakData) !voi const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand); try labeled_block.label.merges.results.append(sema.gpa, operand); try labeled_block.label.merges.br_list.append(sema.gpa, Air.refToIndex(br_ref).?); - labeled_block.block.runtime_index += 1; + labeled_block.block.runtime_index.increment(); if (labeled_block.block.runtime_cond == null and labeled_block.block.runtime_loop == null) { labeled_block.block.runtime_cond = child_block.runtime_cond orelse child_block.runtime_loop; labeled_block.block.runtime_loop = child_block.runtime_loop; @@ -15812,7 +15812,7 @@ fn checkComptimeVarStore( src: LazySrcLoc, decl_ref_mut: Value.Payload.DeclRefMut.Data, ) CompileError!void { - if (decl_ref_mut.runtime_index < block.runtime_index) { + if (@enumToInt(decl_ref_mut.runtime_index) < @enumToInt(block.runtime_index)) { if (block.runtime_cond) |cond_src| { const msg = msg: { const msg = try sema.errMsg(block, src, "store to comptime variable depends on runtime condition", .{}); @@ -20526,8 +20526,7 @@ fn storePtrVal( const bitcasted_val = try sema.bitCastVal(block, src, operand_val, operand_ty, mut_kit.ty, 0); - if (mut_kit.decl_ref_mut.runtime_index == std.math.maxInt(u32)) { - // Special case for comptime field ptr. + if (mut_kit.decl_ref_mut.runtime_index == .comptime_field_ptr) { if (!mut_kit.val.eql(bitcasted_val, mut_kit.ty, sema.mod)) { return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{}); } @@ -20590,7 +20589,7 @@ fn beginComptimePtrMutation( return ComptimePtrMutationKit{ .decl_ref_mut = .{ .decl_index = @intToEnum(Module.Decl.Index, 0), - .runtime_index = std.math.maxInt(u32), + .runtime_index = .comptime_field_ptr, }, .val = duped, .ty = payload.field_ty, diff --git a/src/value.zig b/src/value.zig index 21fe52e706..e1500fe4ab 100644 --- a/src/value.zig +++ b/src/value.zig @@ -4768,7 +4768,7 @@ pub const Value = extern union { pub const Data = struct { decl_index: Module.Decl.Index, - runtime_index: u32, + runtime_index: RuntimeIndex, }; }; @@ -4965,6 +4965,16 @@ pub const Value = extern union { pub fn makeBool(x: bool) Value { return if (x) Value.@"true" else Value.@"false"; } + + pub const RuntimeIndex = enum(u32) { + zero = 0, + comptime_field_ptr = std.math.maxInt(u32), + _, + + pub fn increment(ri: *RuntimeIndex) void { + ri.* = @intToEnum(RuntimeIndex, @enumToInt(ri.*) + 1); + } + }; }; var negative_one_payload: Value.Payload.I64 = .{ From 95ab942184427e7c9b840d71f4d093931e3e48fb Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 11 Jun 2022 22:56:32 +0300 Subject: [PATCH 1824/2031] Sema: make `@src` give absolute paths --- src/Sema.zig | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 9a34cce6e0..d901998755 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -11861,10 +11861,14 @@ fn zirBuiltinSrc( const file_name_val = blk: { var anon_decl = try block.startAnonDecl(src); defer anon_decl.deinit(); - const name = try fn_owner_decl.getFileScope().fullPathZ(anon_decl.arena()); + const relative_path = try fn_owner_decl.getFileScope().fullPath(sema.arena); + const absolute_path = std.fs.realpathAlloc(sema.arena, relative_path) catch |err| { + return sema.fail(block, src, "failed to get absolute path of file '{s}': {s}", .{ relative_path, @errorName(err) }); + }; + const aboslute_duped = try anon_decl.arena().dupeZ(u8, absolute_path); const new_decl = try anon_decl.finish( - try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), name.len), - try Value.Tag.bytes.create(anon_decl.arena(), name[0 .. name.len + 1]), + try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), aboslute_duped.len), + try Value.Tag.bytes.create(anon_decl.arena(), aboslute_duped[0 .. aboslute_duped.len + 1]), 0, // default alignment ); break :blk try Value.Tag.decl_ref.create(sema.arena, new_decl); @@ -11875,6 +11879,7 @@ fn zirBuiltinSrc( field_values[0] = file_name_val; // fn_name: [:0]const u8, field_values[1] = func_name_val; + // TODO these should be runtime only! // line: u32 field_values[2] = try Value.Tag.int_u64.create(sema.arena, extra.line + 1); // column: u32, From 488e1e5f51905485f9db37038e74bdea31ebd16e Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 10 Jun 2022 12:10:14 +0300 Subject: [PATCH 1825/2031] stage2: small fixes + adjustments to std tests --- lib/std/compress.zig | 1 - lib/std/compress/deflate/compressor.zig | 5 ++- lib/std/compress/deflate/compressor_test.zig | 39 +++++++------------ lib/std/compress/deflate/decompressor.zig | 25 +++++++++--- .../compress/deflate/deflate_fast_test.zig | 18 +++------ lib/std/x.zig | 1 - lib/std/x/os/io.zig | 1 + lib/std/x/os/net.zig | 2 +- src/Sema.zig | 3 +- src/value.zig | 1 + test/behavior/cast.zig | 1 + 11 files changed, 50 insertions(+), 47 deletions(-) diff --git a/lib/std/compress.zig b/lib/std/compress.zig index 1d671f1aa6..7fa25175d5 100644 --- a/lib/std/compress.zig +++ b/lib/std/compress.zig @@ -5,7 +5,6 @@ pub const gzip = @import("compress/gzip.zig"); pub const zlib = @import("compress/zlib.zig"); test { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; _ = deflate; _ = gzip; _ = zlib; diff --git a/lib/std/compress/deflate/compressor.zig b/lib/std/compress/deflate/compressor.zig index 58b1955838..50836f2b9a 100644 --- a/lib/std/compress/deflate/compressor.zig +++ b/lib/std/compress/deflate/compressor.zig @@ -254,7 +254,10 @@ pub fn Compressor(comptime WriterType: anytype) type { // Inner writer wrapped in a HuffmanBitWriter hm_bw: hm_bw.HuffmanBitWriter(WriterType) = undefined, - bulk_hasher: fn ([]u8, []u32) u32, + bulk_hasher: if (@import("builtin").zig_backend == .stage1) + fn ([]u8, []u32) u32 + else + *const fn ([]u8, []u32) u32, sync: bool, // requesting flush best_speed_enc: *fast.DeflateFast, // Encoder for best_speed diff --git a/lib/std/compress/deflate/compressor_test.zig b/lib/std/compress/deflate/compressor_test.zig index b35857c731..db07a07064 100644 --- a/lib/std/compress/deflate/compressor_test.zig +++ b/lib/std/compress/deflate/compressor_test.zig @@ -122,11 +122,8 @@ fn testToFromWithLevelAndLimit(level: deflate.Compression, input: []const u8, li try expect(compressed.items.len <= limit); } - var decomp = try decompressor( - testing.allocator, - io.fixedBufferStream(compressed.items).reader(), - null, - ); + var fib = io.fixedBufferStream(compressed.items); + var decomp = try decompressor(testing.allocator, fib.reader(), null); defer decomp.deinit(); var decompressed = try testing.allocator.alloc(u8, input.len); @@ -136,7 +133,9 @@ fn testToFromWithLevelAndLimit(level: deflate.Compression, input: []const u8, li try expect(read == input.len); try expect(mem.eql(u8, input, decompressed)); - try testSync(level, input); + if (builtin.zig_backend == .stage1) { + try testSync(level, input); + } } fn testToFromWithLimit(input: []const u8, limit: [11]u32) !void { @@ -180,6 +179,7 @@ test "deflate/inflate" { } test "very long sparse chunk" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // A SparseReader returns a stream consisting of 0s ending with 65,536 (1<<16) 1s. // This tests missing hash references in a very large input. const SparseReader = struct { @@ -377,6 +377,7 @@ test "compressor dictionary" { // Update the hash for best_speed only if d.index < d.maxInsertIndex // See https://golang.org/issue/2508 test "Go non-regression test for 2508" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; var comp = try compressor( testing.allocator, io.null_writer, @@ -475,21 +476,16 @@ test "inflate reset" { try comp.close(); } - var decomp = try decompressor( - testing.allocator, - io.fixedBufferStream(compressed_strings[0].items).reader(), - null, - ); + var fib = io.fixedBufferStream(compressed_strings[0].items); + var decomp = try decompressor(testing.allocator, fib.reader(), null); defer decomp.deinit(); var decompressed_0: []u8 = try decomp.reader() .readAllAlloc(testing.allocator, math.maxInt(usize)); defer testing.allocator.free(decompressed_0); - try decomp.reset( - io.fixedBufferStream(compressed_strings[1].items).reader(), - null, - ); + fib = io.fixedBufferStream(compressed_strings[1].items); + try decomp.reset(fib.reader(), null); var decompressed_1: []u8 = try decomp.reader() .readAllAlloc(testing.allocator, math.maxInt(usize)); @@ -530,21 +526,16 @@ test "inflate reset dictionary" { try comp.close(); } - var decomp = try decompressor( - testing.allocator, - io.fixedBufferStream(compressed_strings[0].items).reader(), - dict, - ); + var fib = io.fixedBufferStream(compressed_strings[0].items); + var decomp = try decompressor(testing.allocator, fib.reader(), dict); defer decomp.deinit(); var decompressed_0: []u8 = try decomp.reader() .readAllAlloc(testing.allocator, math.maxInt(usize)); defer testing.allocator.free(decompressed_0); - try decomp.reset( - io.fixedBufferStream(compressed_strings[1].items).reader(), - dict, - ); + fib = io.fixedBufferStream(compressed_strings[1].items); + try decomp.reset(fib.reader(), dict); var decompressed_1: []u8 = try decomp.reader() .readAllAlloc(testing.allocator, math.maxInt(usize)); diff --git a/lib/std/compress/deflate/decompressor.zig b/lib/std/compress/deflate/decompressor.zig index 937af90d98..a906bb1037 100644 --- a/lib/std/compress/deflate/decompressor.zig +++ b/lib/std/compress/deflate/decompressor.zig @@ -334,7 +334,10 @@ pub fn Decompressor(comptime ReaderType: type) type { // Next step in the decompression, // and decompression state. - step: fn (*Self) Error!void, + step: if (@import("builtin").zig_backend == .stage1) + fn (*Self) Error!void + else + *const fn (*Self) Error!void, step_state: DecompressorState, final: bool, err: ?Error, @@ -479,7 +482,13 @@ pub fn Decompressor(comptime ReaderType: type) type { } pub fn close(self: *Self) ?Error { - if (self.err == Error.EndOfStreamWithNoError) { + if (@import("builtin").zig_backend == .stage1) { + if (self.err == Error.EndOfStreamWithNoError) { + return null; + } + return self.err; + } + if (self.err == @as(?Error, error.EndOfStreamWithNoError)) { return null; } return self.err; @@ -920,7 +929,8 @@ test "truncated input" { }; for (tests) |t| { - var r = io.fixedBufferStream(t.input).reader(); + var fib = io.fixedBufferStream(t.input); + const r = fib.reader(); var z = try decompressor(testing.allocator, r, null); defer z.deinit(); var zr = z.reader(); @@ -959,7 +969,8 @@ test "Go non-regression test for 9842" { }; for (tests) |t| { - const reader = std.io.fixedBufferStream(t.input).reader(); + var fib = std.io.fixedBufferStream(t.input); + const reader = fib.reader(); var decomp = try decompressor(testing.allocator, reader, null); defer decomp.deinit(); @@ -1017,7 +1028,8 @@ test "inflate A Tale of Two Cities (1859) intro" { \\ ; - const reader = std.io.fixedBufferStream(&compressed).reader(); + var fib = std.io.fixedBufferStream(&compressed); + const reader = fib.reader(); var decomp = try decompressor(testing.allocator, reader, null); defer decomp.deinit(); @@ -1082,7 +1094,8 @@ test "fuzzing" { fn decompress(input: []const u8) !void { const allocator = testing.allocator; - const reader = std.io.fixedBufferStream(input).reader(); + var fib = std.io.fixedBufferStream(input); + const reader = fib.reader(); var decomp = try decompressor(allocator, reader, null); defer decomp.deinit(); var output = try decomp.reader().readAllAlloc(allocator, math.maxInt(usize)); diff --git a/lib/std/compress/deflate/deflate_fast_test.zig b/lib/std/compress/deflate/deflate_fast_test.zig index 6a8e9fa421..b3a255e598 100644 --- a/lib/std/compress/deflate/deflate_fast_test.zig +++ b/lib/std/compress/deflate/deflate_fast_test.zig @@ -78,11 +78,8 @@ test "best speed" { var decompressed = try testing.allocator.alloc(u8, want.items.len); defer testing.allocator.free(decompressed); - var decomp = try inflate.decompressor( - testing.allocator, - io.fixedBufferStream(compressed.items).reader(), - null, - ); + var fib = io.fixedBufferStream(compressed.items); + var decomp = try inflate.decompressor(testing.allocator, fib.reader(), null); defer decomp.deinit(); var read = try decomp.reader().readAll(decompressed); @@ -122,13 +119,13 @@ test "best speed max match offset" { // zeros1 is between 0 and 30 zeros. // The difference between the two abc's will be offset, which // is max_match_offset plus or minus a small adjustment. - var src_len: usize = @intCast(usize, offset + abc.len + @intCast(i32, extra)); + var src_len: usize = @intCast(usize, offset + @as(i32, abc.len) + @intCast(i32, extra)); var src = try testing.allocator.alloc(u8, src_len); defer testing.allocator.free(src); mem.copy(u8, src, abc); if (!do_match_before) { - var src_offset: usize = @intCast(usize, offset - xyz.len); + var src_offset: usize = @intCast(usize, offset - @as(i32, xyz.len)); mem.copy(u8, src[src_offset..], xyz); } var src_offset: usize = @intCast(usize, offset); @@ -149,11 +146,8 @@ test "best speed max match offset" { var decompressed = try testing.allocator.alloc(u8, src.len); defer testing.allocator.free(decompressed); - var decomp = try inflate.decompressor( - testing.allocator, - io.fixedBufferStream(compressed.items).reader(), - null, - ); + var fib = io.fixedBufferStream(compressed.items); + var decomp = try inflate.decompressor(testing.allocator, fib.reader(), null); defer decomp.deinit(); var read = try decomp.reader().readAll(decompressed); _ = decomp.close(); diff --git a/lib/std/x.zig b/lib/std/x.zig index bafcdd5426..64caf324ed 100644 --- a/lib/std/x.zig +++ b/lib/std/x.zig @@ -13,7 +13,6 @@ pub const net = struct { }; test { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; inline for (.{ os, net }) |module| { std.testing.refAllDecls(module); } diff --git a/lib/std/x/os/io.zig b/lib/std/x/os/io.zig index 35e7c3e1ed..e61d212e52 100644 --- a/lib/std/x/os/io.zig +++ b/lib/std/x/os/io.zig @@ -117,6 +117,7 @@ pub const Reactor = struct { }; test "reactor/linux: drive async tcp client/listener pair" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; if (native_os.tag != .linux) return error.SkipZigTest; const ip = std.x.net.ip; diff --git a/lib/std/x/os/net.zig b/lib/std/x/os/net.zig index d4a17f8679..a7cc8fbc4a 100644 --- a/lib/std/x/os/net.zig +++ b/lib/std/x/os/net.zig @@ -381,7 +381,7 @@ pub const IPv6 = extern struct { }); } - const zero_span = span: { + const zero_span: struct { from: usize, to: usize } = span: { var i: usize = 0; while (i < self.octets.len) : (i += 2) { if (self.octets[i] == 0 and self.octets[i + 1] == 0) break; diff --git a/src/Sema.zig b/src/Sema.zig index d901998755..7dbf3d921f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -19702,7 +19702,8 @@ fn coerce( // pointer to tuple to slice if (inst_ty.isSinglePointer() and inst_ty.childType().isTuple() and - !dest_info.mutable and dest_info.size == .Slice) + (!dest_info.mutable or inst_ty.ptrIsMutable() or inst_ty.childType().tupleFields().types.len == 0) and + dest_info.size == .Slice) { return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src); } diff --git a/src/value.zig b/src/value.zig index 21fe52e706..9c3c8bbfcc 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1062,6 +1062,7 @@ pub const Value = extern union { sema_kit: ?Module.WipAnalysis, ) Module.CompileError!BigIntConst { switch (val.tag()) { + .null_value, .zero, .bool_false, .the_only_possible_value, // i0, u0 diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 4ff2c78d47..383b72e046 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1426,6 +1426,7 @@ test "coerce undefined single-item pointer of array to error union of slice" { } test "pointer to empty struct literal to mutable slice" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var x: []i32 = &.{}; try expect(x.len == 0); } From 0333ff4476d0132a2397122dcab964de7fc0f2d3 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 10 Jun 2022 14:11:59 +0300 Subject: [PATCH 1826/2031] stage2: make `error{}` the same size as `anyerror` Having `error{}` be a zero bit type causes issues when it interracts with empty inferred error sets which are the same size as `anyerror`. --- lib/std/compress/deflate/compressor_test.zig | 2 - src/Sema.zig | 59 ++---- src/arch/aarch64/CodeGen.zig | 11 +- src/arch/arm/CodeGen.zig | 12 +- src/arch/wasm/CodeGen.zig | 58 ++---- src/arch/x86_64/CodeGen.zig | 21 +- src/codegen.zig | 9 - src/codegen/c.zig | 78 +++----- src/codegen/llvm.zig | 98 +++------- src/type.zig | 192 +++---------------- test/behavior/error.zig | 59 ------ 11 files changed, 123 insertions(+), 476 deletions(-) diff --git a/lib/std/compress/deflate/compressor_test.zig b/lib/std/compress/deflate/compressor_test.zig index db07a07064..4f8efd0d6e 100644 --- a/lib/std/compress/deflate/compressor_test.zig +++ b/lib/std/compress/deflate/compressor_test.zig @@ -179,7 +179,6 @@ test "deflate/inflate" { } test "very long sparse chunk" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // A SparseReader returns a stream consisting of 0s ending with 65,536 (1<<16) 1s. // This tests missing hash references in a very large input. const SparseReader = struct { @@ -377,7 +376,6 @@ test "compressor dictionary" { // Update the hash for best_speed only if d.index < d.maxInsertIndex // See https://golang.org/issue/2508 test "Go non-regression test for 2508" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; var comp = try compressor( testing.allocator, io.null_writer, diff --git a/src/Sema.zig b/src/Sema.zig index 7dbf3d921f..9d09324193 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6182,6 +6182,17 @@ fn zirErrorToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! } } + const op_ty = sema.typeOf(op); + try sema.resolveInferredErrorSetTy(block, src, op_ty); + if (!op_ty.isAnyError()) { + const names = op_ty.errorSetNames(); + switch (names.len) { + 0 => return sema.addConstant(result_ty, Value.zero), + 1 => return sema.addIntUnsigned(result_ty, sema.mod.global_error_set.get(names[0]).?), + else => {}, + } + } + try sema.requireRuntimeBlock(block, src); return block.addBitCast(result_ty, op_coerced); } @@ -6560,7 +6571,7 @@ fn analyzeErrUnionPayload( // If the error set has no fields then no safety check is needed. if (safety_check and block.wantSafety() and - err_union_ty.errorUnionSet().errorSetCardinality() != .zero) + !err_union_ty.errorUnionSet().errorSetIsEmpty()) { try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err); } @@ -6646,7 +6657,7 @@ fn analyzeErrUnionPayloadPtr( // If the error set has no fields then no safety check is needed. if (safety_check and block.wantSafety() and - err_union_ty.errorUnionSet().errorSetCardinality() != .zero) + !err_union_ty.errorUnionSet().errorSetIsEmpty()) { try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr); } @@ -24231,6 +24242,10 @@ pub fn typeHasOnePossibleValue( .bool, .type, .anyerror, + .error_set_single, + .error_set, + .error_set_merged, + .error_union, .fn_noreturn_no_args, .fn_void_no_args, .fn_naked_noreturn_no_args, @@ -24287,46 +24302,6 @@ pub fn typeHasOnePossibleValue( } }, - .error_union => { - const error_ty = ty.errorUnionSet(); - switch (error_ty.errorSetCardinality()) { - .zero => { - const payload_ty = ty.errorUnionPayload(); - if (try typeHasOnePossibleValue(sema, block, src, payload_ty)) |payload_val| { - return try Value.Tag.eu_payload.create(sema.arena, payload_val); - } else { - return null; - } - }, - .one => { - if (ty.errorUnionPayload().isNoReturn()) { - const error_val = (try typeHasOnePossibleValue(sema, block, src, error_ty)).?; - return error_val; - } else { - return null; - } - }, - .many => return null, - } - }, - - .error_set_single => { - const name = ty.castTag(.error_set_single).?.data; - return try Value.Tag.@"error".create(sema.arena, .{ .name = name }); - }, - .error_set => { - const err_set_obj = ty.castTag(.error_set).?.data; - const names = err_set_obj.names.keys(); - if (names.len > 1) return null; - return try Value.Tag.@"error".create(sema.arena, .{ .name = names[0] }); - }, - .error_set_merged => { - const name_map = ty.castTag(.error_set_merged).?.data; - const names = name_map.keys(); - if (names.len > 1) return null; - return try Value.Tag.@"error".create(sema.arena, .{ .name = names[0] }); - }, - .@"struct" => { const resolved_ty = try sema.resolveTypeFields(block, src, ty); const s = resolved_ty.castTag(.@"struct").?.data; diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 7a7ee31117..e74fbd44ac 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -2277,7 +2277,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { const err_ty = error_union_ty.errorUnionSet(); const payload_ty = error_union_ty.errorUnionPayload(); - if (err_ty.errorSetCardinality() == .zero) { + if (err_ty.errorSetIsEmpty()) { return MCValue{ .immediate = 0 }; } if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { @@ -2311,7 +2311,7 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { const err_ty = error_union_ty.errorUnionSet(); const payload_ty = error_union_ty.errorUnionPayload(); - if (err_ty.errorSetCardinality() == .zero) { + if (err_ty.errorSetIsEmpty()) { return error_union_mcv; } if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { @@ -3590,7 +3590,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { const error_type = ty.errorUnionSet(); const payload_type = ty.errorUnionPayload(); - if (error_type.errorSetCardinality() == .zero) { + if (error_type.errorSetIsEmpty()) { return MCValue{ .immediate = 0 }; // always false } @@ -4687,11 +4687,6 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { const error_type = typed_value.ty.errorUnionSet(); const payload_type = typed_value.ty.errorUnionPayload(); - if (error_type.errorSetCardinality() == .zero) { - const payload_val = typed_value.val.castTag(.eu_payload).?.data; - return self.genTypedValue(.{ .ty = payload_type, .val = payload_val }); - } - const is_pl = typed_value.val.errorUnionIsPayload(); if (!payload_type.hasRuntimeBitsIgnoreComptime()) { diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 2bf57943c3..3cc853154b 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1773,7 +1773,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { const err_ty = error_union_ty.errorUnionSet(); const payload_ty = error_union_ty.errorUnionPayload(); - if (err_ty.errorSetCardinality() == .zero) { + if (err_ty.errorSetIsEmpty()) { return MCValue{ .immediate = 0 }; } if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { @@ -1810,7 +1810,7 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { const err_ty = error_union_ty.errorUnionSet(); const payload_ty = error_union_ty.errorUnionPayload(); - if (err_ty.errorSetCardinality() == .zero) { + if (err_ty.errorSetIsEmpty()) { return error_union_mcv; } if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { @@ -3922,7 +3922,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { const error_type = ty.errorUnionSet(); const error_int_type = Type.initTag(.u16); - if (error_type.errorSetCardinality() == .zero) { + if (error_type.errorSetIsEmpty()) { return MCValue{ .immediate = 0 }; // always false } @@ -5368,12 +5368,6 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { .ErrorUnion => { const error_type = typed_value.ty.errorUnionSet(); const payload_type = typed_value.ty.errorUnionPayload(); - - if (error_type.errorSetCardinality() == .zero) { - const payload_val = typed_value.val.castTag(.eu_payload).?.data; - return self.genTypedValue(.{ .ty = payload_type, .val = payload_val }); - } - const is_pl = typed_value.val.errorUnionIsPayload(); if (!payload_type.hasRuntimeBitsIgnoreComptime()) { diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 33cf5422f2..d799dda5bb 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1377,11 +1377,7 @@ fn isByRef(ty: Type, target: std.Target) bool { .Int => return ty.intInfo(target).bits > 64, .Float => return ty.floatBits(target) > 64, .ErrorUnion => { - const err_ty = ty.errorUnionSet(); const pl_ty = ty.errorUnionPayload(); - if (err_ty.errorSetCardinality() == .zero) { - return isByRef(pl_ty, target); - } if (!pl_ty.hasRuntimeBitsIgnoreComptime()) { return false; } @@ -1816,11 +1812,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void { switch (ty.zigTypeTag()) { .ErrorUnion => { - const err_ty = ty.errorUnionSet(); const pl_ty = ty.errorUnionPayload(); - if (err_ty.errorSetCardinality() == .zero) { - return self.store(lhs, rhs, pl_ty, 0); - } if (!pl_ty.hasRuntimeBitsIgnoreComptime()) { return self.store(lhs, rhs, Type.anyerror, 0); } @@ -2353,10 +2345,6 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { }, .ErrorUnion => { const error_type = ty.errorUnionSet(); - if (error_type.errorSetCardinality() == .zero) { - const pl_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef); - return self.lowerConstant(pl_val, ty.errorUnionPayload()); - } const is_pl = val.errorUnionIsPayload(); const err_val = if (!is_pl) val else Value.initTag(.zero); return self.lowerConstant(err_val, error_type); @@ -2925,7 +2913,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W const err_union_ty = self.air.typeOf(un_op); const pl_ty = err_union_ty.errorUnionPayload(); - if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { + if (err_union_ty.errorUnionSet().errorSetIsEmpty()) { switch (opcode) { .i32_ne => return WValue{ .imm32 = 0 }, .i32_eq => return WValue{ .imm32 = 1 }, @@ -2958,10 +2946,6 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) const err_ty = if (op_is_ptr) op_ty.childType() else op_ty; const payload_ty = err_ty.errorUnionPayload(); - if (err_ty.errorUnionSet().errorSetCardinality() == .zero) { - return operand; - } - if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} }; const pl_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target)); @@ -2980,7 +2964,7 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) In const err_ty = if (op_is_ptr) op_ty.childType() else op_ty; const payload_ty = err_ty.errorUnionPayload(); - if (err_ty.errorUnionSet().errorSetCardinality() == .zero) { + if (err_ty.errorUnionSet().errorSetIsEmpty()) { return WValue{ .imm32 = 0 }; } @@ -2998,10 +2982,6 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const operand = try self.resolveInst(ty_op.operand); const err_ty = self.air.typeOfIndex(inst); - if (err_ty.errorUnionSet().errorSetCardinality() == .zero) { - return operand; - } - const pl_ty = self.air.typeOf(ty_op.operand); if (!pl_ty.hasRuntimeBitsIgnoreComptime()) { return operand; @@ -4656,29 +4636,27 @@ fn lowerTry( return self.fail("TODO: lowerTry for pointers", .{}); } - if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { - return err_union; - } - const pl_ty = err_union_ty.errorUnionPayload(); const pl_has_bits = pl_ty.hasRuntimeBitsIgnoreComptime(); - // Block we can jump out of when error is not set - try self.startBlock(.block, wasm.block_empty); + if (!err_union_ty.errorUnionSet().errorSetIsEmpty()) { + // Block we can jump out of when error is not set + try self.startBlock(.block, wasm.block_empty); - // check if the error tag is set for the error union. - try self.emitWValue(err_union); - if (pl_has_bits) { - const err_offset = @intCast(u32, errUnionErrorOffset(pl_ty, self.target)); - try self.addMemArg(.i32_load16_u, .{ - .offset = err_union.offset() + err_offset, - .alignment = Type.anyerror.abiAlignment(self.target), - }); + // check if the error tag is set for the error union. + try self.emitWValue(err_union); + if (pl_has_bits) { + const err_offset = @intCast(u32, errUnionErrorOffset(pl_ty, self.target)); + try self.addMemArg(.i32_load16_u, .{ + .offset = err_union.offset() + err_offset, + .alignment = Type.anyerror.abiAlignment(self.target), + }); + } + try self.addTag(.i32_eqz); + try self.addLabel(.br_if, 0); // jump out of block when error is '0' + try self.genBody(body); + try self.endBlock(); } - try self.addTag(.i32_eqz); - try self.addLabel(.br_if, 0); // jump out of block when error is '0' - try self.genBody(body); - try self.endBlock(); // if we reach here it means error was not set, and we want the payload if (!pl_has_bits) { diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5bda84c98c..c94eaa37af 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1806,7 +1806,7 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { const operand = try self.resolveInst(ty_op.operand); const result: MCValue = result: { - if (err_ty.errorSetCardinality() == .zero) { + if (err_ty.errorSetIsEmpty()) { break :result MCValue{ .immediate = 0 }; } @@ -1857,14 +1857,8 @@ fn genUnwrapErrorUnionPayloadMir( err_union: MCValue, ) !MCValue { const payload_ty = err_union_ty.errorUnionPayload(); - const err_ty = err_union_ty.errorUnionSet(); const result: MCValue = result: { - if (err_ty.errorSetCardinality() == .zero) { - // TODO check if we can reuse - break :result err_union; - } - if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { break :result MCValue.none; } @@ -1991,15 +1985,10 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { } const error_union_ty = self.air.getRefType(ty_op.ty); - const error_ty = error_union_ty.errorUnionSet(); const payload_ty = error_union_ty.errorUnionPayload(); const operand = try self.resolveInst(ty_op.operand); const result: MCValue = result: { - if (error_ty.errorSetCardinality() == .zero) { - break :result operand; - } - if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { break :result operand; } @@ -4651,7 +4640,7 @@ fn isNonNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCV fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { const err_type = ty.errorUnionSet(); - if (err_type.errorSetCardinality() == .zero) { + if (err_type.errorSetIsEmpty()) { return MCValue{ .immediate = 0 }; // always false } @@ -6909,12 +6898,6 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { .ErrorUnion => { const error_type = typed_value.ty.errorUnionSet(); const payload_type = typed_value.ty.errorUnionPayload(); - - if (error_type.errorSetCardinality() == .zero) { - const payload_val = typed_value.val.castTag(.eu_payload).?.data; - return self.genTypedValue(.{ .ty = payload_type, .val = payload_val }); - } - const is_pl = typed_value.val.errorUnionIsPayload(); if (!payload_type.hasRuntimeBitsIgnoreComptime()) { diff --git a/src/codegen.zig b/src/codegen.zig index 84615d86d8..025decdb4b 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -705,15 +705,6 @@ pub fn generateSymbol( .ErrorUnion => { const error_ty = typed_value.ty.errorUnionSet(); const payload_ty = typed_value.ty.errorUnionPayload(); - - if (error_ty.errorSetCardinality() == .zero) { - const payload_val = typed_value.val.castTag(.eu_payload).?.data; - return generateSymbol(bin_file, src_loc, .{ - .ty = payload_ty, - .val = payload_val, - }, code, debug_output, reloc_info); - } - const is_payload = typed_value.val.errorUnionIsPayload(); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 45191f3107..949e2a67f2 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -752,12 +752,6 @@ pub const DeclGen = struct { const error_type = ty.errorUnionSet(); const payload_type = ty.errorUnionPayload(); - if (error_type.errorSetCardinality() == .zero) { - // We use the payload directly as the type. - const payload_val = val.castTag(.eu_payload).?.data; - return dg.renderValue(writer, payload_type, payload_val, location); - } - if (!payload_type.hasRuntimeBits()) { // We use the error type directly as the type. const err_val = if (val.errorUnionIsPayload()) Value.initTag(.zero) else val; @@ -1381,13 +1375,8 @@ pub const DeclGen = struct { return w.writeAll("uint16_t"); }, .ErrorUnion => { - const error_ty = t.errorUnionSet(); const payload_ty = t.errorUnionPayload(); - if (error_ty.errorSetCardinality() == .zero) { - return dg.renderType(w, payload_ty); - } - if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { return dg.renderType(w, Type.anyerror); } @@ -2892,41 +2881,36 @@ fn lowerTry( operand_is_ptr: bool, result_ty: Type, ) !CValue { - if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { - // If the error set has no fields, then the payload and the error - // union are the same value. - return err_union; - } - + const writer = f.object.writer(); const payload_ty = err_union_ty.errorUnionPayload(); const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(); - const writer = f.object.writer(); - - err: { - if (!payload_has_bits) { - if (operand_is_ptr) { - try writer.writeAll("if(*"); - } else { - try writer.writeAll("if("); + if (!err_union_ty.errorUnionSet().errorSetIsEmpty()) { + err: { + if (!payload_has_bits) { + if (operand_is_ptr) { + try writer.writeAll("if(*"); + } else { + try writer.writeAll("if("); + } + try f.writeCValue(writer, err_union); + try writer.writeAll(")"); + break :err; + } + if (operand_is_ptr or isByRef(err_union_ty)) { + try writer.writeAll("if("); + try f.writeCValue(writer, err_union); + try writer.writeAll("->error)"); + break :err; } - try f.writeCValue(writer, err_union); - try writer.writeAll(")"); - break :err; - } - if (operand_is_ptr or isByRef(err_union_ty)) { try writer.writeAll("if("); try f.writeCValue(writer, err_union); - try writer.writeAll("->error)"); - break :err; + try writer.writeAll(".error)"); } - try writer.writeAll("if("); - try f.writeCValue(writer, err_union); - try writer.writeAll(".error)"); - } - try genBody(f, body); - try f.object.indent_writer.insertNewline(); + try genBody(f, body); + try f.object.indent_writer.insertNewline(); + } if (!payload_has_bits) { if (!operand_is_ptr) { @@ -3466,7 +3450,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { if (operand_ty.zigTypeTag() == .Pointer) { const err_union_ty = operand_ty.childType(); - if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { + if (err_union_ty.errorUnionSet().errorSetIsEmpty()) { return CValue{ .bytes = "0" }; } if (!err_union_ty.errorUnionPayload().hasRuntimeBits()) { @@ -3478,7 +3462,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(";\n"); return local; } - if (operand_ty.errorUnionSet().errorSetCardinality() == .zero) { + if (operand_ty.errorUnionSet().errorSetIsEmpty()) { return CValue{ .bytes = "0" }; } if (!operand_ty.errorUnionPayload().hasRuntimeBits()) { @@ -3507,10 +3491,6 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, maybe_addrof: [*:0]c const operand_is_ptr = operand_ty.zigTypeTag() == .Pointer; const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; - if (error_union_ty.errorUnionSet().errorSetCardinality() == .zero) { - return operand; - } - if (!error_union_ty.errorUnionPayload().hasRuntimeBits()) { return CValue.none; } @@ -3575,11 +3555,6 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { const error_ty = error_union_ty.errorUnionSet(); const payload_ty = error_union_ty.errorUnionPayload(); - if (error_ty.errorSetCardinality() == .zero) { - // TODO: write undefined bytes through the pointer here - return operand; - } - // First, set the non-error value. if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { try f.writeCValueDeref(writer, operand); @@ -3623,9 +3598,6 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(ty_op.operand); const inst_ty = f.air.typeOfIndex(inst); - if (inst_ty.errorUnionSet().errorSetCardinality() == .zero) { - return operand; - } const local = try f.allocLocal(inst_ty, .Const); try writer.writeAll(" = { .error = 0, .payload = "); try f.writeCValue(writer, operand); @@ -3652,7 +3624,7 @@ fn airIsErr( try writer.writeAll(" = "); - if (error_ty.errorSetCardinality() == .zero) { + if (error_ty.errorSetIsEmpty()) { try writer.print("0 {s} 0;\n", .{op_str}); } else { if (is_ptr) { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 8031c4635e..efe6a754b6 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1571,22 +1571,6 @@ pub const Object = struct { }, .ErrorUnion => { const payload_ty = ty.errorUnionPayload(); - switch (ty.errorUnionSet().errorSetCardinality()) { - .zero => { - const payload_di_ty = try o.lowerDebugType(payload_ty, .full); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(payload_di_ty), .{ .mod = o.module }); - return payload_di_ty; - }, - .one => { - if (payload_ty.isNoReturn()) { - const di_type = dib.createBasicType("void", 0, DW.ATE.signed); - gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_type); - return di_type; - } - }, - .many => {}, - } if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { const err_set_di_ty = try o.lowerDebugType(Type.anyerror, .full); // The recursive call to `lowerDebugType` means we can't use `gop` anymore. @@ -2554,15 +2538,6 @@ pub const DeclGen = struct { }, .ErrorUnion => { const payload_ty = t.errorUnionPayload(); - switch (t.errorUnionSet().errorSetCardinality()) { - .zero => return dg.lowerType(payload_ty), - .one => { - if (payload_ty.isNoReturn()) { - return dg.context.voidType(); - } - }, - .many => {}, - } if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { return try dg.lowerType(Type.anyerror); } @@ -3222,10 +3197,6 @@ pub const DeclGen = struct { }, .ErrorUnion => { const payload_type = tv.ty.errorUnionPayload(); - if (tv.ty.errorUnionSet().errorSetCardinality() == .zero) { - const payload_val = tv.val.castTag(.eu_payload).?.data; - return dg.lowerValue(.{ .ty = payload_type, .val = payload_val }); - } const is_pl = tv.val.errorUnionIsPayload(); if (!payload_type.hasRuntimeBitsIgnoreComptime()) { @@ -4795,40 +4766,37 @@ pub const FuncGen = struct { } fn lowerTry(fg: *FuncGen, err_union: *const llvm.Value, body: []const Air.Inst.Index, err_union_ty: Type, operand_is_ptr: bool, result_ty: Type) !?*const llvm.Value { - if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { - // If the error set has no fields, then the payload and the error - // union are the same value. - return err_union; - } - const payload_ty = err_union_ty.errorUnionPayload(); const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(); const target = fg.dg.module.getTarget(); - const is_err = err: { - const err_set_ty = try fg.dg.lowerType(Type.anyerror); - const zero = err_set_ty.constNull(); - if (!payload_has_bits) { - const loaded = if (operand_is_ptr) fg.builder.buildLoad(err_union, "") else err_union; + + if (!err_union_ty.errorUnionSet().errorSetIsEmpty()) { + const is_err = err: { + const err_set_ty = try fg.dg.lowerType(Type.anyerror); + const zero = err_set_ty.constNull(); + if (!payload_has_bits) { + const loaded = if (operand_is_ptr) fg.builder.buildLoad(err_union, "") else err_union; + break :err fg.builder.buildICmp(.NE, loaded, zero, ""); + } + const err_field_index = errUnionErrorOffset(payload_ty, target); + if (operand_is_ptr or isByRef(err_union_ty)) { + const err_field_ptr = fg.builder.buildStructGEP(err_union, err_field_index, ""); + const loaded = fg.builder.buildLoad(err_field_ptr, ""); + break :err fg.builder.buildICmp(.NE, loaded, zero, ""); + } + const loaded = fg.builder.buildExtractValue(err_union, err_field_index, ""); break :err fg.builder.buildICmp(.NE, loaded, zero, ""); - } - const err_field_index = errUnionErrorOffset(payload_ty, target); - if (operand_is_ptr or isByRef(err_union_ty)) { - const err_field_ptr = fg.builder.buildStructGEP(err_union, err_field_index, ""); - const loaded = fg.builder.buildLoad(err_field_ptr, ""); - break :err fg.builder.buildICmp(.NE, loaded, zero, ""); - } - const loaded = fg.builder.buildExtractValue(err_union, err_field_index, ""); - break :err fg.builder.buildICmp(.NE, loaded, zero, ""); - }; + }; - const return_block = fg.context.appendBasicBlock(fg.llvm_func, "TryRet"); - const continue_block = fg.context.appendBasicBlock(fg.llvm_func, "TryCont"); - _ = fg.builder.buildCondBr(is_err, return_block, continue_block); + const return_block = fg.context.appendBasicBlock(fg.llvm_func, "TryRet"); + const continue_block = fg.context.appendBasicBlock(fg.llvm_func, "TryCont"); + _ = fg.builder.buildCondBr(is_err, return_block, continue_block); - fg.builder.positionBuilderAtEnd(return_block); - try fg.genBody(body); + fg.builder.positionBuilderAtEnd(return_block); + try fg.genBody(body); - fg.builder.positionBuilderAtEnd(continue_block); + fg.builder.positionBuilderAtEnd(continue_block); + } if (!payload_has_bits) { if (!operand_is_ptr) return null; @@ -5665,7 +5633,7 @@ pub const FuncGen = struct { const err_set_ty = try self.dg.lowerType(Type.initTag(.anyerror)); const zero = err_set_ty.constNull(); - if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { + if (err_union_ty.errorUnionSet().errorSetIsEmpty()) { const llvm_i1 = self.context.intType(1); switch (op) { .EQ => return llvm_i1.constInt(1, .False), // 0 == 0 @@ -5788,13 +5756,6 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); - const operand_ty = self.air.typeOf(ty_op.operand); - const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; - if (error_union_ty.errorUnionSet().errorSetCardinality() == .zero) { - // If the error set has no fields, then the payload and the error - // union are the same value. - return operand; - } const result_ty = self.air.typeOfIndex(inst); const payload_ty = if (operand_is_ptr) result_ty.childType() else result_ty; const target = self.dg.module.getTarget(); @@ -5825,7 +5786,7 @@ pub const FuncGen = struct { const operand = try self.resolveInst(ty_op.operand); const operand_ty = self.air.typeOf(ty_op.operand); const err_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; - if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) { + if (err_union_ty.errorUnionSet().errorSetIsEmpty()) { const err_llvm_ty = try self.dg.lowerType(Type.anyerror); if (operand_is_ptr) { return self.builder.buildBitCast(operand, err_llvm_ty.pointerType(0), ""); @@ -5856,10 +5817,6 @@ pub const FuncGen = struct { const operand = try self.resolveInst(ty_op.operand); const error_union_ty = self.air.typeOf(ty_op.operand).childType(); - if (error_union_ty.errorUnionSet().errorSetCardinality() == .zero) { - // TODO: write undefined bytes through the pointer here - return operand; - } const payload_ty = error_union_ty.errorUnionPayload(); const non_error_val = try self.dg.lowerValue(.{ .ty = Type.anyerror, .val = Value.zero }); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { @@ -5938,9 +5895,6 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const inst_ty = self.air.typeOfIndex(inst); const operand = try self.resolveInst(ty_op.operand); - if (inst_ty.errorUnionSet().errorSetCardinality() == .zero) { - return operand; - } const payload_ty = self.air.typeOf(ty_op.operand); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { return operand; diff --git a/src/type.zig b/src/type.zig index 8556526e18..0ca7ba83c5 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2366,6 +2366,10 @@ pub const Type = extern union { .anyopaque, .@"opaque", .type_info, + .error_set_single, + .error_union, + .error_set, + .error_set_merged, => return true, // These are false because they are comptime-only types. @@ -2389,20 +2393,8 @@ pub const Type = extern union { .fn_void_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, - .error_set_single, => return false, - .error_set => { - const err_set_obj = ty.castTag(.error_set).?.data; - const names = err_set_obj.names.keys(); - return names.len > 1; - }, - .error_set_merged => { - const name_map = ty.castTag(.error_set_merged).?.data; - const names = name_map.keys(); - return names.len > 1; - }, - // These types have more than one possible value, so the result is the same as // asking whether they are comptime-only types. .anyframe_T, @@ -2443,25 +2435,6 @@ pub const Type = extern union { } }, - .error_union => { - // This code needs to be kept in sync with the equivalent switch prong - // in abiSizeAdvanced. - const data = ty.castTag(.error_union).?.data; - switch (data.error_set.errorSetCardinality()) { - .zero => return hasRuntimeBitsAdvanced(data.payload, ignore_comptime_only, sema_kit), - .one => return !data.payload.isNoReturn(), - .many => { - if (ignore_comptime_only) { - return true; - } else if (sema_kit) |sk| { - return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, ty)); - } else { - return !comptimeOnly(ty); - } - }, - } - }, - .@"struct" => { const struct_obj = ty.castTag(.@"struct").?.data; if (struct_obj.status == .field_types_wip) { @@ -2926,27 +2899,11 @@ pub const Type = extern union { .anyerror_void_error_union, .anyerror, .error_set_inferred, + .error_set_single, + .error_set, + .error_set_merged, => return AbiAlignmentAdvanced{ .scalar = 2 }, - .error_set => { - const err_set_obj = ty.castTag(.error_set).?.data; - const names = err_set_obj.names.keys(); - if (names.len <= 1) { - return AbiAlignmentAdvanced{ .scalar = 0 }; - } else { - return AbiAlignmentAdvanced{ .scalar = 2 }; - } - }, - .error_set_merged => { - const name_map = ty.castTag(.error_set_merged).?.data; - const names = name_map.keys(); - if (names.len <= 1) { - return AbiAlignmentAdvanced{ .scalar = 0 }; - } else { - return AbiAlignmentAdvanced{ .scalar = 2 }; - } - }, - .array, .array_sentinel => return ty.elemType().abiAlignmentAdvanced(target, strat), // TODO audit this - is there any more complicated logic to determine @@ -2971,12 +2928,7 @@ pub const Type = extern union { switch (child_type.zigTypeTag()) { .Pointer => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }, - .ErrorSet => switch (child_type.errorSetCardinality()) { - // `?error{}` is comptime-known to be null. - .zero => return AbiAlignmentAdvanced{ .scalar = 0 }, - .one => return AbiAlignmentAdvanced{ .scalar = 1 }, - .many => return abiAlignmentAdvanced(Type.anyerror, target, strat), - }, + .ErrorSet => return abiAlignmentAdvanced(Type.anyerror, target, strat), .NoReturn => return AbiAlignmentAdvanced{ .scalar = 0 }, else => {}, } @@ -2999,15 +2951,6 @@ pub const Type = extern union { // This code needs to be kept in sync with the equivalent switch prong // in abiSizeAdvanced. const data = ty.castTag(.error_union).?.data; - switch (data.error_set.errorSetCardinality()) { - .zero => return abiAlignmentAdvanced(data.payload, target, strat), - .one => { - if (data.payload.isNoReturn()) { - return AbiAlignmentAdvanced{ .scalar = 0 }; - } - }, - .many => {}, - } const code_align = abiAlignment(Type.anyerror, target); switch (strat) { .eager, .sema_kit => { @@ -3118,7 +3061,6 @@ pub const Type = extern union { .@"undefined", .enum_literal, .type_info, - .error_set_single, => return AbiAlignmentAdvanced{ .scalar = 0 }, .noreturn, @@ -3237,7 +3179,6 @@ pub const Type = extern union { .empty_struct_literal, .empty_struct, .void, - .error_set_single, => return AbiSizeAdvanced{ .scalar = 0 }, .@"struct", .tuple, .anon_struct => switch (ty.containerLayout()) { @@ -3396,27 +3337,11 @@ pub const Type = extern union { .anyerror_void_error_union, .anyerror, .error_set_inferred, + .error_set, + .error_set_merged, + .error_set_single, => return AbiSizeAdvanced{ .scalar = 2 }, - .error_set => { - const err_set_obj = ty.castTag(.error_set).?.data; - const names = err_set_obj.names.keys(); - if (names.len <= 1) { - return AbiSizeAdvanced{ .scalar = 0 }; - } else { - return AbiSizeAdvanced{ .scalar = 2 }; - } - }, - .error_set_merged => { - const name_map = ty.castTag(.error_set_merged).?.data; - const names = name_map.keys(); - if (names.len <= 1) { - return AbiSizeAdvanced{ .scalar = 0 }; - } else { - return AbiSizeAdvanced{ .scalar = 2 }; - } - }, - .i16, .u16 => return AbiSizeAdvanced{ .scalar = intAbiSize(16, target) }, .u29 => return AbiSizeAdvanced{ .scalar = intAbiSize(29, target) }, .i32, .u32 => return AbiSizeAdvanced{ .scalar = intAbiSize(32, target) }, @@ -3467,24 +3392,6 @@ pub const Type = extern union { // This code needs to be kept in sync with the equivalent switch prong // in abiAlignmentAdvanced. const data = ty.castTag(.error_union).?.data; - // Here we need to care whether or not the error set is *empty* or whether - // it only has *one possible value*. In the former case, it means there - // cannot possibly be an error, meaning the ABI size is equivalent to the - // payload ABI size. In the latter case, we need to account for the "tag" - // because even if both the payload type and the error set type of an - // error union have no runtime bits, an error union still has - // 1 bit of data which is whether or not the value is an error. - // Zig still uses the error code encoding at runtime, even when only 1 bit - // would suffice. This prevents coercions from needing to branch. - switch (data.error_set.errorSetCardinality()) { - .zero => return abiSizeAdvanced(data.payload, target, strat), - .one => { - if (data.payload.isNoReturn()) { - return AbiSizeAdvanced{ .scalar = 0 }; - } - }, - .many => {}, - } const code_size = abiSize(Type.anyerror, target); if (!data.payload.hasRuntimeBits()) { // Same as anyerror. @@ -3727,11 +3634,7 @@ pub const Type = extern union { .error_union => { const payload = ty.castTag(.error_union).?.data; - if (!payload.error_set.hasRuntimeBits() and !payload.payload.hasRuntimeBits()) { - return 0; - } else if (!payload.error_set.hasRuntimeBits()) { - return payload.payload.bitSizeAdvanced(target, sema_kit); - } else if (!payload.payload.hasRuntimeBits()) { + if (!payload.payload.hasRuntimeBits()) { return payload.error_set.bitSizeAdvanced(target, sema_kit); } @panic("TODO bitSize error union"); @@ -4351,30 +4254,25 @@ pub const Type = extern union { }; } - const ErrorSetCardinality = enum { zero, one, many }; - - pub fn errorSetCardinality(ty: Type) ErrorSetCardinality { + /// Returns false for unresolved inferred error sets. + pub fn errorSetIsEmpty(ty: Type) bool { switch (ty.tag()) { - .anyerror => return .many, - .error_set_inferred => return .many, - .error_set_single => return .one, + .anyerror => return false, + .error_set_inferred => { + const inferred_error_set = ty.castTag(.error_set_inferred).?.data; + // Can't know for sure. + if (!inferred_error_set.is_resolved) return false; + if (inferred_error_set.is_anyerror) return false; + return inferred_error_set.errors.count() == 0; + }, + .error_set_single => return false, .error_set => { const err_set_obj = ty.castTag(.error_set).?.data; - const names = err_set_obj.names.keys(); - switch (names.len) { - 0 => return .zero, - 1 => return .one, - else => return .many, - } + return err_set_obj.names.count() == 0; }, .error_set_merged => { const name_map = ty.castTag(.error_set_merged).?.data; - const names = name_map.keys(); - switch (names.len) { - 0 => return .zero, - 1 => return .one, - else => return .many, - } + return name_map.count() == 0; }, else => unreachable, } @@ -4883,6 +4781,10 @@ pub const Type = extern union { .bool, .type, .anyerror, + .error_union, + .error_set_single, + .error_set, + .error_set_merged, .fn_noreturn_no_args, .fn_void_no_args, .fn_naked_noreturn_no_args, @@ -4939,42 +4841,6 @@ pub const Type = extern union { } }, - .error_union => { - const error_ty = ty.errorUnionSet(); - switch (error_ty.errorSetCardinality()) { - .zero => { - const payload_ty = ty.errorUnionPayload(); - if (onePossibleValue(payload_ty)) |payload_val| { - _ = payload_val; - return Value.initTag(.the_only_possible_value); - } else { - return null; - } - }, - .one => { - if (ty.errorUnionPayload().isNoReturn()) { - const error_val = onePossibleValue(error_ty).?; - return error_val; - } else { - return null; - } - }, - .many => return null, - } - }, - - .error_set_single => return Value.initTag(.the_only_possible_value), - .error_set => { - const err_set_obj = ty.castTag(.error_set).?.data; - if (err_set_obj.names.count() > 1) return null; - return Value.initTag(.the_only_possible_value); - }, - .error_set_merged => { - const name_map = ty.castTag(.error_set_merged).?.data; - if (name_map.count() > 1) return null; - return Value.initTag(.the_only_possible_value); - }, - .@"struct" => { const s = ty.castTag(.@"struct").?.data; assert(s.haveFieldTypes()); diff --git a/test/behavior/error.zig b/test/behavior/error.zig index ac51ec1eae..c9697a0f82 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -453,65 +453,6 @@ test "optional error set is the same size as error set" { comptime try expect(S.returnsOptErrSet() == null); } -test "optional error set with only one error is the same size as bool" { - if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - - const E = error{only}; - comptime try expect(@sizeOf(?E) == @sizeOf(bool)); - comptime try expect(@alignOf(?E) == @alignOf(bool)); - const S = struct { - fn gimmeNull() ?E { - return null; - } - fn gimmeErr() ?E { - return error.only; - } - }; - try expect(S.gimmeNull() == null); - try expect(error.only == S.gimmeErr().?); - comptime try expect(S.gimmeNull() == null); - comptime try expect(error.only == S.gimmeErr().?); -} - -test "optional empty error set" { - if (builtin.zig_backend == .stage1) return error.SkipZigTest; - - comptime try expect(@sizeOf(error{}!void) == @sizeOf(void)); - comptime try expect(@alignOf(error{}!void) == @alignOf(void)); - - var x: ?error{} = undefined; - if (x != null) { - @compileError("test failed"); - } -} - -test "empty error set plus zero-bit payload" { - if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - - comptime try expect(@sizeOf(error{}!void) == @sizeOf(void)); - comptime try expect(@alignOf(error{}!void) == @alignOf(void)); - - var x: error{}!void = undefined; - if (x) |payload| { - if (payload != {}) { - @compileError("test failed"); - } - } else |_| { - @compileError("test failed"); - } - const S = struct { - fn empty() error{}!void {} - fn inferred() !void { - return empty(); - } - }; - try S.inferred(); -} - test "nested catch" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From 35c7e376b89f41d8260b70e770e03ef6af68849d Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 11 Jun 2022 11:33:09 +0300 Subject: [PATCH 1827/2031] stage2: improve anon name strategy for local variables --- lib/std/fmt.zig | 64 +++++++++++++++++++++++++++----------- src/AstGen.zig | 7 +++++ src/Sema.zig | 49 ++++++++++++++++++++++------- src/Zir.zig | 2 ++ test/behavior/typename.zig | 61 +++++++++++++++--------------------- 5 files changed, 118 insertions(+), 65 deletions(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 79302682db..f151d4380d 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -2190,18 +2190,22 @@ test "enum" { } test "non-exhaustive enum" { + if (builtin.zig_backend == .stage1) { + // stage1 fails to return fully qualified namespaces. + return error.SkipZigTest; + } const Enum = enum(u16) { One = 0x000f, Two = 0xbeef, _, }; - try expectFmt("enum: Enum.One\n", "enum: {}\n", .{Enum.One}); - try expectFmt("enum: Enum.Two\n", "enum: {}\n", .{Enum.Two}); - try expectFmt("enum: Enum(4660)\n", "enum: {}\n", .{@intToEnum(Enum, 0x1234)}); - try expectFmt("enum: Enum.One\n", "enum: {x}\n", .{Enum.One}); - try expectFmt("enum: Enum.Two\n", "enum: {x}\n", .{Enum.Two}); - try expectFmt("enum: Enum.Two\n", "enum: {X}\n", .{Enum.Two}); - try expectFmt("enum: Enum(1234)\n", "enum: {x}\n", .{@intToEnum(Enum, 0x1234)}); + try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.One\n", "enum: {}\n", .{Enum.One}); + try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.Two\n", "enum: {}\n", .{Enum.Two}); + try expectFmt("enum: fmt.test.non-exhaustive enum.Enum(4660)\n", "enum: {}\n", .{@intToEnum(Enum, 0x1234)}); + try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.One\n", "enum: {x}\n", .{Enum.One}); + try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.Two\n", "enum: {x}\n", .{Enum.Two}); + try expectFmt("enum: fmt.test.non-exhaustive enum.Enum.Two\n", "enum: {X}\n", .{Enum.Two}); + try expectFmt("enum: fmt.test.non-exhaustive enum.Enum(1234)\n", "enum: {x}\n", .{@intToEnum(Enum, 0x1234)}); } test "float.scientific" { @@ -2357,6 +2361,10 @@ test "custom" { } test "struct" { + if (builtin.zig_backend == .stage1) { + // stage1 fails to return fully qualified namespaces. + return error.SkipZigTest; + } const S = struct { a: u32, b: anyerror, @@ -2367,7 +2375,7 @@ test "struct" { .b = error.Unused, }; - try expectFmt("S{ .a = 456, .b = error.Unused }", "{}", .{inst}); + try expectFmt("fmt.test.struct.S{ .a = 456, .b = error.Unused }", "{}", .{inst}); // Tuples try expectFmt("{ }", "{}", .{.{}}); try expectFmt("{ -1 }", "{}", .{.{-1}}); @@ -2375,6 +2383,10 @@ test "struct" { } test "union" { + if (builtin.zig_backend == .stage1) { + // stage1 fails to return fully qualified namespaces. + return error.SkipZigTest; + } const TU = union(enum) { float: f32, int: u32, @@ -2394,17 +2406,21 @@ test "union" { const uu_inst = UU{ .int = 456 }; const eu_inst = EU{ .float = 321.123 }; - try expectFmt("TU{ .int = 123 }", "{}", .{tu_inst}); + try expectFmt("fmt.test.union.TU{ .int = 123 }", "{}", .{tu_inst}); var buf: [100]u8 = undefined; const uu_result = try bufPrint(buf[0..], "{}", .{uu_inst}); - try std.testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); + try std.testing.expect(mem.eql(u8, uu_result[0..18], "fmt.test.union.UU@")); const eu_result = try bufPrint(buf[0..], "{}", .{eu_inst}); - try std.testing.expect(mem.eql(u8, eu_result[0..3], "EU@")); + try std.testing.expect(mem.eql(u8, eu_result[0..18], "fmt.test.union.EU@")); } test "enum" { + if (builtin.zig_backend == .stage1) { + // stage1 fails to return fully qualified namespaces. + return error.SkipZigTest; + } const E = enum { One, Two, @@ -2413,10 +2429,14 @@ test "enum" { const inst = E.Two; - try expectFmt("E.Two", "{}", .{inst}); + try expectFmt("fmt.test.enum.E.Two", "{}", .{inst}); } test "struct.self-referential" { + if (builtin.zig_backend == .stage1) { + // stage1 fails to return fully qualified namespaces. + return error.SkipZigTest; + } const S = struct { const SelfType = @This(); a: ?*SelfType, @@ -2427,10 +2447,14 @@ test "struct.self-referential" { }; inst.a = &inst; - try expectFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", .{inst}); + try expectFmt("fmt.test.struct.self-referential.S{ .a = fmt.test.struct.self-referential.S{ .a = fmt.test.struct.self-referential.S{ .a = fmt.test.struct.self-referential.S{ ... } } } }", "{}", .{inst}); } test "struct.zero-size" { + if (builtin.zig_backend == .stage1) { + // stage1 fails to return fully qualified namespaces. + return error.SkipZigTest; + } const A = struct { fn foo() void {} }; @@ -2442,7 +2466,7 @@ test "struct.zero-size" { const a = A{}; const b = B{ .a = a, .c = 0 }; - try expectFmt("B{ .a = A{ }, .c = 0 }", "{}", .{b}); + try expectFmt("fmt.test.struct.zero-size.B{ .a = fmt.test.struct.zero-size.A{ }, .c = 0 }", "{}", .{b}); } test "bytes.hex" { @@ -2508,6 +2532,10 @@ test "formatFloatValue with comptime_float" { } test "formatType max_depth" { + if (builtin.zig_backend == .stage1) { + // stage1 fails to return fully qualified namespaces. + return error.SkipZigTest; + } const Vec2 = struct { const SelfType = @This(); x: f32, @@ -2558,19 +2586,19 @@ test "formatType max_depth" { var buf: [1000]u8 = undefined; var fbs = std.io.fixedBufferStream(&buf); try formatType(inst, "", FormatOptions{}, fbs.writer(), 0); - try std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ ... }")); + try std.testing.expect(mem.eql(u8, fbs.getWritten(), "fmt.test.formatType max_depth.S{ ... }")); fbs.reset(); try formatType(inst, "", FormatOptions{}, fbs.writer(), 1); - try std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }")); + try std.testing.expect(mem.eql(u8, fbs.getWritten(), "fmt.test.formatType max_depth.S{ .a = fmt.test.formatType max_depth.S{ ... }, .tu = fmt.test.formatType max_depth.TU{ ... }, .e = fmt.test.formatType max_depth.E.Two, .vec = (10.200,2.220) }")); fbs.reset(); try formatType(inst, "", FormatOptions{}, fbs.writer(), 2); - try std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }")); + try std.testing.expect(mem.eql(u8, fbs.getWritten(), "fmt.test.formatType max_depth.S{ .a = fmt.test.formatType max_depth.S{ .a = fmt.test.formatType max_depth.S{ ... }, .tu = fmt.test.formatType max_depth.TU{ ... }, .e = fmt.test.formatType max_depth.E.Two, .vec = (10.200,2.220) }, .tu = fmt.test.formatType max_depth.TU{ .ptr = fmt.test.formatType max_depth.TU{ ... } }, .e = fmt.test.formatType max_depth.E.Two, .vec = (10.200,2.220) }")); fbs.reset(); try formatType(inst, "", FormatOptions{}, fbs.writer(), 3); - try std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }")); + try std.testing.expect(mem.eql(u8, fbs.getWritten(), "fmt.test.formatType max_depth.S{ .a = fmt.test.formatType max_depth.S{ .a = fmt.test.formatType max_depth.S{ .a = fmt.test.formatType max_depth.S{ ... }, .tu = fmt.test.formatType max_depth.TU{ ... }, .e = fmt.test.formatType max_depth.E.Two, .vec = (10.200,2.220) }, .tu = fmt.test.formatType max_depth.TU{ .ptr = fmt.test.formatType max_depth.TU{ ... } }, .e = fmt.test.formatType max_depth.E.Two, .vec = (10.200,2.220) }, .tu = fmt.test.formatType max_depth.TU{ .ptr = fmt.test.formatType max_depth.TU{ .ptr = fmt.test.formatType max_depth.TU{ ... } } }, .e = fmt.test.formatType max_depth.E.Two, .vec = (10.200,2.220) }")); } test "positional" { diff --git a/src/AstGen.zig b/src/AstGen.zig index 3f22146f0e..078523831c 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2753,7 +2753,10 @@ fn varDecl( const result_loc: ResultLoc = if (type_node != 0) .{ .ty = try typeExpr(gz, scope, type_node), } else .none; + const prev_anon_name_strategy = gz.anon_name_strategy; + gz.anon_name_strategy = .dbg_var; const init_inst = try reachableExpr(gz, scope, result_loc, var_decl.ast.init_node, node); + gz.anon_name_strategy = prev_anon_name_strategy; try gz.addDbgVar(.dbg_var_val, ident_name, init_inst); @@ -2777,6 +2780,7 @@ fn varDecl( var init_scope = gz.makeSubBlock(scope); // we may add more instructions to gz before stacking init_scope init_scope.instructions_top = GenZir.unstacked_top; + init_scope.anon_name_strategy = .dbg_var; defer init_scope.unstack(); var resolve_inferred_alloc: Zir.Inst.Ref = .none; @@ -2956,7 +2960,10 @@ fn varDecl( resolve_inferred_alloc = alloc; break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } }; }; + const prev_anon_name_strategy = gz.anon_name_strategy; + gz.anon_name_strategy = .dbg_var; _ = try reachableExprComptime(gz, scope, var_data.result_loc, var_decl.ast.init_node, node, is_comptime); + gz.anon_name_strategy = prev_anon_name_strategy; if (resolve_inferred_alloc != .none) { _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node); } diff --git a/src/Sema.zig b/src/Sema.zig index 9d09324193..f0eb67ec26 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -914,9 +914,9 @@ fn analyzeBodyInner( // zig fmt: off .variable => try sema.zirVarExtended( block, extended), .struct_decl => try sema.zirStructDecl( block, extended, inst), - .enum_decl => try sema.zirEnumDecl( block, extended), + .enum_decl => try sema.zirEnumDecl( block, extended, inst), .union_decl => try sema.zirUnionDecl( block, extended, inst), - .opaque_decl => try sema.zirOpaqueDecl( block, extended), + .opaque_decl => try sema.zirOpaqueDecl( block, extended, inst), .this => try sema.zirThis( block, extended), .ret_addr => try sema.zirRetAddr( block, extended), .builtin_src => try sema.zirBuiltinSrc( block, extended), @@ -2101,7 +2101,7 @@ fn zirStructDecl( const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = struct_val, - }, small.name_strategy, "struct"); + }, small.name_strategy, "struct", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); @@ -2133,6 +2133,7 @@ fn createAnonymousDeclTypeNamed( typed_value: TypedValue, name_strategy: Zir.Inst.NameStrategy, anon_prefix: []const u8, + inst: ?Zir.Inst.Index, ) !Decl.Index { const mod = sema.mod; const namespace = block.namespace; @@ -2152,11 +2153,13 @@ fn createAnonymousDeclTypeNamed( const name = try std.fmt.allocPrintZ(sema.gpa, "{s}__{s}_{d}", .{ src_decl.name, anon_prefix, @enumToInt(new_decl_index), }); + errdefer sema.gpa.free(name); try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name); return new_decl_index; }, .parent => { const name = try sema.gpa.dupeZ(u8, mem.sliceTo(sema.mod.declPtr(block.src_decl).name, 0)); + errdefer sema.gpa.free(name); try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name); return new_decl_index; }, @@ -2188,9 +2191,31 @@ fn createAnonymousDeclTypeNamed( try buf.appendSlice(")"); const name = try buf.toOwnedSliceSentinel(0); + errdefer sema.gpa.free(name); try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name); return new_decl_index; }, + .dbg_var => { + const ref = Zir.indexToRef(inst.?); + const zir_tags = sema.code.instructions.items(.tag); + const zir_data = sema.code.instructions.items(.data); + var i = inst.?; + while (i < zir_tags.len) : (i += 1) switch (zir_tags[i]) { + .dbg_var_ptr, .dbg_var_val => { + if (zir_data[i].str_op.operand != ref) continue; + + const name = try std.fmt.allocPrintZ(sema.gpa, "{s}.{s}", .{ + src_decl.name, zir_data[i].str_op.getStr(sema.code), + }); + errdefer sema.gpa.free(name); + + try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name); + return new_decl_index; + }, + else => {}, + }; + return sema.createAnonymousDeclTypeNamed(block, typed_value, .anon, anon_prefix, null); + }, } } @@ -2198,6 +2223,7 @@ fn zirEnumDecl( sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, + inst: Zir.Inst.Index, ) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -2252,7 +2278,7 @@ fn zirEnumDecl( const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = enum_val, - }, small.name_strategy, "enum"); + }, small.name_strategy, "enum", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); @@ -2472,7 +2498,7 @@ fn zirUnionDecl( const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = union_val, - }, small.name_strategy, "union"); + }, small.name_strategy, "union", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); @@ -2504,6 +2530,7 @@ fn zirOpaqueDecl( sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, + inst: Zir.Inst.Index, ) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -2540,7 +2567,7 @@ fn zirOpaqueDecl( const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = opaque_val, - }, small.name_strategy, "opaque"); + }, small.name_strategy, "opaque", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); @@ -2589,7 +2616,7 @@ fn zirErrorSetDecl( const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = error_set_val, - }, name_strategy, "error"); + }, name_strategy, "error", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); @@ -14632,7 +14659,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = enum_val, - }, .anon, "enum"); + }, .anon, "enum", null); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); @@ -14722,7 +14749,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = opaque_val, - }, .anon, "opaque"); + }, .anon, "opaque", null); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); @@ -14773,7 +14800,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = new_union_val, - }, .anon, "union"); + }, .anon, "union", null); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); @@ -14941,7 +14968,7 @@ fn reifyStruct( const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ .ty = Type.type, .val = new_struct_val, - }, .anon, "struct"); + }, .anon, "struct", null); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); diff --git a/src/Zir.zig b/src/Zir.zig index 1ca31755f7..b43b775dfa 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -3156,6 +3156,8 @@ pub const Inst = struct { /// Create an anonymous name for this declaration. /// Like this: "ParentDeclName_struct_69" anon, + /// Use the name specified in the next `dbg_var_{val,ptr}` instruction. + dbg_var, }; /// Trailing: diff --git a/test/behavior/typename.zig b/test/behavior/typename.zig index b237779886..e0cb5ba68c 100644 --- a/test/behavior/typename.zig +++ b/test/behavior/typename.zig @@ -137,43 +137,8 @@ const A_Enum = enum { fn regular() void {} -test "fn body decl" { - if (builtin.zig_backend == .stage1) { - // stage1 fails to return fully qualified namespaces. - return error.SkipZigTest; - } - - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - - try B.doTest(); -} - const B = struct { - fn doTest() !void { - const B_Struct = struct {}; - const B_Union = union { - unused: u8, - }; - const B_Enum = enum { - unused, - }; - - try expectEqualStringsIgnoreDigits( - "behavior.typename.B.doTest__struct_0", - @typeName(B_Struct), - ); - try expectEqualStringsIgnoreDigits( - "behavior.typename.B.doTest__union_0", - @typeName(B_Union), - ); - try expectEqualStringsIgnoreDigits( - "behavior.typename.B.doTest__enum_0", - @typeName(B_Enum), - ); - } + fn doTest() !void {} }; test "fn param" { @@ -246,3 +211,27 @@ pub fn expectEqualStringsIgnoreDigits(expected: []const u8, actual: []const u8) } return expectEqualStrings(expected, actual_buf[0..actual_i]); } + +test "local variable" { + if (builtin.zig_backend == .stage1) { + // stage1 fails to return fully qualified namespaces. + return error.SkipZigTest; + } + + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const Foo = struct { a: u32 }; + const Bar = union { a: u32 }; + const Baz = enum { a, b }; + const Qux = enum { a, b }; + const Quux = enum { a, b }; + + try expectEqualStrings("behavior.typename.test.local variable.Foo", @typeName(Foo)); + try expectEqualStrings("behavior.typename.test.local variable.Bar", @typeName(Bar)); + try expectEqualStrings("behavior.typename.test.local variable.Baz", @typeName(Baz)); + try expectEqualStrings("behavior.typename.test.local variable.Qux", @typeName(Qux)); + try expectEqualStrings("behavior.typename.test.local variable.Quux", @typeName(Quux)); +} From 6b36774adc407bc4c8271306410a20e13cbd55ad Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 11 Jun 2022 11:02:33 +0300 Subject: [PATCH 1828/2031] std: disable failing tests, add zig2 build test-std to CI --- ci/zinc/linux_test.sh | 1 + lib/std/bit_set.zig | 4 ++++ lib/std/crypto/argon2.zig | 2 ++ lib/std/crypto/bcrypt.zig | 1 + lib/std/crypto/phc_encoding.zig | 1 + lib/std/crypto/scrypt.zig | 1 + lib/std/fmt.zig | 2 ++ lib/std/json.zig | 1 + lib/std/math/copysign.zig | 1 + lib/std/math/signbit.zig | 1 + lib/std/mem.zig | 1 + lib/std/os/linux/io_uring.zig | 2 ++ lib/std/priority_queue.zig | 14 ++++++++++++++ lib/std/tz.zig | 3 +++ 14 files changed, 35 insertions(+) diff --git a/ci/zinc/linux_test.sh b/ci/zinc/linux_test.sh index 1c82992b25..0bb176d4ad 100755 --- a/ci/zinc/linux_test.sh +++ b/ci/zinc/linux_test.sh @@ -59,6 +59,7 @@ stage2/bin/zig build -Dtarget=arm-linux-musleabihf # test building self-hosted f # * https://github.com/ziglang/zig/issues/11367 (and corresponding workaround in compiler source) # * https://github.com/ziglang/zig/pull/11492#issuecomment-1112871321 stage2/bin/zig build test-behavior -fqemu -fwasmtime +stage2/bin/zig test lib/std/std.zig --zig-lib-dir lib $ZIG build test-behavior -fqemu -fwasmtime -Domit-stage2 $ZIG build test-compiler-rt -fqemu -fwasmtime diff --git a/lib/std/bit_set.zig b/lib/std/bit_set.zig index 8006a623b5..9b0079ec74 100644 --- a/lib/std/bit_set.zig +++ b/lib/std/bit_set.zig @@ -1330,6 +1330,7 @@ fn testStaticBitSet(comptime Set: type) !void { } test "IntegerBitSet" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO try testStaticBitSet(IntegerBitSet(0)); try testStaticBitSet(IntegerBitSet(1)); try testStaticBitSet(IntegerBitSet(2)); @@ -1341,6 +1342,7 @@ test "IntegerBitSet" { } test "ArrayBitSet" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO if (@import("builtin").cpu.arch == .aarch64) { // https://github.com/ziglang/zig/issues/9879 return error.SkipZigTest; @@ -1355,6 +1357,7 @@ test "ArrayBitSet" { } test "DynamicBitSetUnmanaged" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const allocator = std.testing.allocator; var a = try DynamicBitSetUnmanaged.initEmpty(allocator, 300); try testing.expectEqual(@as(usize, 0), a.count()); @@ -1395,6 +1398,7 @@ test "DynamicBitSetUnmanaged" { } test "DynamicBitSet" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const allocator = std.testing.allocator; var a = try DynamicBitSet.initEmpty(allocator, 300); try testing.expectEqual(@as(usize, 0), a.count()); diff --git a/lib/std/crypto/argon2.zig b/lib/std/crypto/argon2.zig index 7269470d5f..16c20a4a78 100644 --- a/lib/std/crypto/argon2.zig +++ b/lib/std/crypto/argon2.zig @@ -897,6 +897,7 @@ test "kdf" { } test "phc format hasher" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const allocator = std.testing.allocator; const password = "testpass"; @@ -912,6 +913,7 @@ test "phc format hasher" { } test "password hash and password verify" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const allocator = std.testing.allocator; const password = "testpass"; diff --git a/lib/std/crypto/bcrypt.zig b/lib/std/crypto/bcrypt.zig index d7a0e0fb80..d5e9fddaf1 100644 --- a/lib/std/crypto/bcrypt.zig +++ b/lib/std/crypto/bcrypt.zig @@ -802,6 +802,7 @@ test "bcrypt crypt format" { } test "bcrypt phc format" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const hash_options = HashOptions{ .params = .{ .rounds_log = 5 }, .encoding = .phc, diff --git a/lib/std/crypto/phc_encoding.zig b/lib/std/crypto/phc_encoding.zig index 56fa76b5c0..40074b2c6e 100644 --- a/lib/std/crypto/phc_encoding.zig +++ b/lib/std/crypto/phc_encoding.zig @@ -260,6 +260,7 @@ fn kvSplit(str: []const u8) !struct { key: []const u8, value: []const u8 } { } test "phc format - encoding/decoding" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const Input = struct { str: []const u8, HashResult: type, diff --git a/lib/std/crypto/scrypt.zig b/lib/std/crypto/scrypt.zig index e8cb6bab7b..11441df161 100644 --- a/lib/std/crypto/scrypt.zig +++ b/lib/std/crypto/scrypt.zig @@ -683,6 +683,7 @@ test "unix-scrypt" { } test "crypt format" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const str = "$7$C6..../....SodiumChloride$kBGj9fHznVYFQMEn/qDCfrDevf9YDtcDdKvEqHJLV8D"; const params = try crypt_format.deserialize(crypt_format.HashResult(32), str); var buf: [str.len]u8 = undefined; diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index f151d4380d..449cc6a919 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -2225,6 +2225,7 @@ test "float.scientific.precision" { } test "float.special" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO try expectFmt("f64: nan", "f64: {}", .{math.nan_f64}); // negative nan is not defined by IEE 754, // and ARM thus normalizes it to positive nan @@ -2236,6 +2237,7 @@ test "float.special" { } test "float.hexadecimal.special" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO try expectFmt("f64: nan", "f64: {x}", .{math.nan_f64}); // negative nan is not defined by IEE 754, // and ARM thus normalizes it to positive nan diff --git a/lib/std/json.zig b/lib/std/json.zig index cc82c1d0a5..2e09818b6f 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -1355,6 +1355,7 @@ pub const Value = union(enum) { }; test "Value.jsonStringify" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO { var buffer: [10]u8 = undefined; var fbs = std.io.fixedBufferStream(&buffer); diff --git a/lib/std/math/copysign.zig b/lib/std/math/copysign.zig index b5fd6d4d9a..521724a998 100644 --- a/lib/std/math/copysign.zig +++ b/lib/std/math/copysign.zig @@ -13,6 +13,7 @@ pub fn copysign(magnitude: anytype, sign: @TypeOf(magnitude)) @TypeOf(magnitude) } test "math.copysign" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { try expect(copysign(@as(T, 1.0), @as(T, 1.0)) == 1.0); try expect(copysign(@as(T, 2.0), @as(T, -2.0)) == -2.0); diff --git a/lib/std/math/signbit.zig b/lib/std/math/signbit.zig index 9aab487d37..cb19212b5b 100644 --- a/lib/std/math/signbit.zig +++ b/lib/std/math/signbit.zig @@ -10,6 +10,7 @@ pub fn signbit(x: anytype) bool { } test "math.signbit" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { try expect(!signbit(@as(T, 0.0))); try expect(!signbit(@as(T, 1.0))); diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 73cc24c45c..5b59c463be 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -2080,6 +2080,7 @@ fn testReadIntImpl() !void { } test "writeIntSlice" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO try testWriteIntImpl(); comptime try testWriteIntImpl(); } diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index 144927771f..e7ac17a1af 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -2154,6 +2154,7 @@ test "timeout (after a number of completions)" { } test "timeout_remove" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO if (builtin.os.tag != .linux) return error.SkipZigTest; var ring = IO_Uring.init(2, 0) catch |err| switch (err) { @@ -2950,6 +2951,7 @@ test "provide_buffers: read" { } test "remove_buffers" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO if (builtin.os.tag != .linux) return error.SkipZigTest; var ring = IO_Uring.init(1, 0) catch |err| switch (err) { diff --git a/lib/std/priority_queue.zig b/lib/std/priority_queue.zig index 51671bca78..1a52fda5be 100644 --- a/lib/std/priority_queue.zig +++ b/lib/std/priority_queue.zig @@ -286,6 +286,7 @@ const PQlt = PriorityQueue(u32, void, lessThan); const PQgt = PriorityQueue(u32, void, greaterThan); test "std.PriorityQueue: add and remove min heap" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); @@ -304,6 +305,7 @@ test "std.PriorityQueue: add and remove min heap" { } test "std.PriorityQueue: add and remove same min heap" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); @@ -353,6 +355,7 @@ test "std.PriorityQueue: peek" { } test "std.PriorityQueue: sift up with odd indices" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 }; @@ -367,6 +370,7 @@ test "std.PriorityQueue: sift up with odd indices" { } test "std.PriorityQueue: addSlice" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 }; @@ -412,6 +416,7 @@ test "std.PriorityQueue: fromOwnedSlice" { } test "std.PriorityQueue: add and remove max heap" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQgt.init(testing.allocator, {}); defer queue.deinit(); @@ -430,6 +435,7 @@ test "std.PriorityQueue: add and remove max heap" { } test "std.PriorityQueue: add and remove same max heap" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQgt.init(testing.allocator, {}); defer queue.deinit(); @@ -470,6 +476,7 @@ test "std.PriorityQueue: iterator" { } test "std.PriorityQueue: remove at index" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); @@ -505,6 +512,7 @@ test "std.PriorityQueue: iterator while empty" { } test "std.PriorityQueue: shrinkAndFree" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); @@ -528,6 +536,7 @@ test "std.PriorityQueue: shrinkAndFree" { } test "std.PriorityQueue: update min heap" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); @@ -543,6 +552,7 @@ test "std.PriorityQueue: update min heap" { } test "std.PriorityQueue: update same min heap" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); @@ -559,6 +569,7 @@ test "std.PriorityQueue: update same min heap" { } test "std.PriorityQueue: update max heap" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQgt.init(testing.allocator, {}); defer queue.deinit(); @@ -574,6 +585,7 @@ test "std.PriorityQueue: update max heap" { } test "std.PriorityQueue: update same max heap" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQgt.init(testing.allocator, {}); defer queue.deinit(); @@ -590,6 +602,7 @@ test "std.PriorityQueue: update same max heap" { } test "std.PriorityQueue: siftUp in remove" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); @@ -610,6 +623,7 @@ fn contextLessThan(context: []const u32, a: usize, b: usize) Order { const CPQlt = PriorityQueue(usize, []const u32, contextLessThan); test "std.PriorityQueue: add and remove min heap with contextful comparator" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const context = [_]u32{ 5, 3, 4, 2, 2, 8, 0 }; var queue = CPQlt.init(testing.allocator, context[0..]); diff --git a/lib/std/tz.zig b/lib/std/tz.zig index 9d733efdfd..cea845e148 100644 --- a/lib/std/tz.zig +++ b/lib/std/tz.zig @@ -214,6 +214,7 @@ pub const Tz = struct { }; test "slim" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const data = @embedFile("tz/asia_tokyo.tzif"); var in_stream = std.io.fixedBufferStream(data); @@ -227,6 +228,7 @@ test "slim" { } test "fat" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const data = @embedFile("tz/antarctica_davis.tzif"); var in_stream = std.io.fixedBufferStream(data); @@ -239,6 +241,7 @@ test "fat" { } test "legacy" { + if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO // Taken from Slackware 8.0, from 2001 const data = @embedFile("tz/europe_vatican.tzif"); var in_stream = std.io.fixedBufferStream(data); From 85492f2b9146b91470ca613cf5208906fff12701 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 12 Jun 2022 00:56:01 -0700 Subject: [PATCH 1829/2031] std.mem.zeroes: remove call to std.meta everybody is so horny for std.meta --- lib/std/mem.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 73cc24c45c..61d6b84874 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -268,7 +268,7 @@ pub fn zeroes(comptime T: type) T { }, .Struct => |struct_info| { if (@sizeOf(T) == 0) return T{}; - if (comptime meta.containerLayout(T) == .Extern) { + if (struct_info.layout == .Extern) { var item: T = undefined; set(u8, asBytes(&item), 0); return item; From c29746aa553a72fe2ef2d414c1b616ee2a94eab4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 12 Jun 2022 00:57:59 -0700 Subject: [PATCH 1830/2031] add std.debug.Trace.format This makes it show up in some useful places; for example in the self-hosted compiler we already print it now with --debug-compile-errors. --- lib/std/debug.zig | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index ba45b16d1b..ba1f509e6c 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -2023,5 +2023,22 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize }) catch return; } } + + pub fn format( + t: Trace, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = fmt; + _ = options; + if (enabled) { + try writer.writeAll("\n"); + t.dump(); + try writer.writeAll("\n"); + } else { + return writer.writeAll("(value tracing disabled)"); + } + } }; } From e64d5a0753dd31702032a458d95c327005c09f85 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 12 Jun 2022 01:00:33 -0700 Subject: [PATCH 1831/2031] Sema: rework beginComptimePtrMutation This comment is now deleted because the task is completed in this commit: ``` // TODO: Update this to behave like `beginComptimePtrLoad` and properly check/use // `container_ty` and `array_ty`, instead of trusting that the parent decl type // matches the type used to derive the elem_ptr/field_ptr/etc. // // This is needed because the types will not match if the pointer we're mutating // through is reinterpreting comptime memory. ``` The main strategy is to change the ComptimePtrMutationKit struct so that instead of `val: *Value` it now returns a tagged union which can be one of three possibilities: * The pointer type matches the actual comptime Value so a direct modification is possible. Before this commit, the implementation incorrectly assumed this was always the case. * In the case of needing to write through a reinterpreted pointer, a mutable base Value pointer is provided along with a byte offset pointing to the element value in virtual memory. * Otherwise, it means a compile error must be emitted because one or both of the types (the owner of the value, or the pointer type being used to write through) do not have a well-defined memory layout. After calling beginComptimePtrMutation, the one callsite now switches on this tagged union and does the appropriate thing. The main new logic is for the second case, which involves pointer reinterpretation, which now takes this strategy: 1. write the base value to a memory buffer. 2. perform the pointer store at the proper byte offset, thereby modifying a subset of the buffer. 3. read the base value from the memory buffer, overwriting the old base value. --- src/Sema.zig | 809 +++++++++++++++++---------- test/behavior/eval.zig | 17 + test/behavior/translate_c_macros.zig | 5 +- 3 files changed, 532 insertions(+), 299 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 073f1e7e2e..bad343f941 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -19212,9 +19212,9 @@ fn elemValArray( elem_index: Air.Inst.Ref, ) CompileError!Air.Inst.Ref { const array_ty = sema.typeOf(array); - const array_sent = array_ty.sentinel() != null; + const array_sent = array_ty.sentinel(); const array_len = array_ty.arrayLen(); - const array_len_s = array_len + @boolToInt(array_sent); + const array_len_s = array_len + @boolToInt(array_sent != null); const elem_ty = array_ty.childType(); if (array_len_s == 0) { @@ -19228,8 +19228,13 @@ fn elemValArray( if (maybe_index_val) |index_val| { const index = @intCast(usize, index_val.toUnsignedInt(target)); + if (array_sent) |s| { + if (index == array_len) { + return sema.addConstant(elem_ty, s); + } + } if (index >= array_len_s) { - const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else ""; + const sentinel_label: []const u8 = if (array_sent != null) " +1 (sentinel)" else ""; return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); } } @@ -19269,7 +19274,7 @@ fn elemValArray( // Runtime check is only needed if unable to comptime check if (maybe_index_val == null) { const len_inst = try sema.addIntUnsigned(Type.usize, array_len); - const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt; + const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt; try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op); } } @@ -20521,27 +20526,67 @@ fn storePtrVal( operand_val: Value, operand_ty: Type, ) !void { - var mut_kit = try beginComptimePtrMutation(sema, block, src, ptr_val); + var mut_kit = try beginComptimePtrMutation(sema, block, src, ptr_val, operand_ty); try sema.checkComptimeVarStore(block, src, mut_kit.decl_ref_mut); - const bitcasted_val = try sema.bitCastVal(block, src, operand_val, operand_ty, mut_kit.ty, 0); + switch (mut_kit.pointee) { + .direct => |val_ptr| { + if (mut_kit.decl_ref_mut.runtime_index == .comptime_field_ptr) { + if (!operand_val.eql(val_ptr.*, operand_ty, sema.mod)) { + // TODO add note showing where default value is provided + return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{}); + } + return; + } + const arena = mut_kit.beginArena(sema.mod); + defer mut_kit.finishArena(sema.mod); - if (mut_kit.decl_ref_mut.runtime_index == .comptime_field_ptr) { - if (!mut_kit.val.eql(bitcasted_val, mut_kit.ty, sema.mod)) { - return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{}); - } - return; + val_ptr.* = try operand_val.copy(arena); + }, + .reinterpret => |reinterpret| { + const target = sema.mod.getTarget(); + const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(target)); + const buffer = try sema.gpa.alloc(u8, abi_size); + defer sema.gpa.free(buffer); + reinterpret.val_ptr.*.writeToMemory(mut_kit.ty, sema.mod, buffer); + operand_val.writeToMemory(operand_ty, sema.mod, buffer[reinterpret.byte_offset..]); + + const arena = mut_kit.beginArena(sema.mod); + defer mut_kit.finishArena(sema.mod); + + reinterpret.val_ptr.* = try Value.readFromMemory(mut_kit.ty, sema.mod, buffer, arena); + }, + .bad_decl_ty, .bad_ptr_ty => { + // TODO show the decl declaration site in a note and explain whether the decl + // or the pointer is the problematic type + return sema.fail(block, src, "comptime mutation of a reinterpreted pointer requires type '{}' to have a well-defined memory layout", .{mut_kit.ty.fmt(sema.mod)}); + }, } - - const arena = mut_kit.beginArena(sema.mod); - defer mut_kit.finishArena(sema.mod); - - mut_kit.val.* = try bitcasted_val.copy(arena); } const ComptimePtrMutationKit = struct { decl_ref_mut: Value.Payload.DeclRefMut.Data, - val: *Value, + pointee: union(enum) { + /// The pointer type matches the actual comptime Value so a direct + /// modification is possible. + direct: *Value, + /// The largest parent Value containing pointee and having a well-defined memory layout. + /// This is used for bitcasting, if direct dereferencing failed. + reinterpret: struct { + val_ptr: *Value, + byte_offset: usize, + }, + /// If the root decl could not be used as parent, this means `ty` is the type that + /// caused that by not having a well-defined layout. + /// This one means the Decl that owns the value trying to be modified does not + /// have a well defined memory layout. + bad_decl_ty, + /// If the root decl could not be used as parent, this means `ty` is the type that + /// caused that by not having a well-defined layout. + /// This one means the pointer type that is being stored through does not + /// have a well defined memory layout. + bad_ptr_ty, + }, ty: Type, decl_arena: std.heap.ArenaAllocator = undefined, @@ -20563,354 +20608,469 @@ fn beginComptimePtrMutation( block: *Block, src: LazySrcLoc, ptr_val: Value, + ptr_elem_ty: Type, ) CompileError!ComptimePtrMutationKit { - - // TODO: Update this to behave like `beginComptimePtrLoad` and properly check/use - // `container_ty` and `array_ty`, instead of trusting that the parent decl type - // matches the type used to derive the elem_ptr/field_ptr/etc. - // - // This is needed because the types will not match if the pointer we're mutating - // through is reinterpreting comptime memory. - + const target = sema.mod.getTarget(); switch (ptr_val.tag()) { .decl_ref_mut => { const decl_ref_mut = ptr_val.castTag(.decl_ref_mut).?.data; const decl = sema.mod.declPtr(decl_ref_mut.decl_index); - return ComptimePtrMutationKit{ - .decl_ref_mut = decl_ref_mut, - .val = &decl.val, - .ty = decl.ty, - }; + return beginComptimePtrMutationInner(sema, block, src, decl.ty, &decl.val, ptr_elem_ty, decl_ref_mut); }, .comptime_field_ptr => { const payload = ptr_val.castTag(.comptime_field_ptr).?.data; const duped = try sema.arena.create(Value); duped.* = payload.field_val; - return ComptimePtrMutationKit{ - .decl_ref_mut = .{ - .decl_index = @intToEnum(Module.Decl.Index, 0), - .runtime_index = .comptime_field_ptr, - }, - .val = duped, - .ty = payload.field_ty, - }; + return beginComptimePtrMutationInner(sema, block, src, payload.field_ty, duped, ptr_elem_ty, .{ + .decl_index = @intToEnum(Module.Decl.Index, 0), + .runtime_index = .comptime_field_ptr, + }); }, .elem_ptr => { const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; - var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr); - switch (parent.ty.zigTypeTag()) { - .Array, .Vector => { - const check_len = parent.ty.arrayLenIncludingSentinel(); - if (elem_ptr.index >= check_len) { - // TODO have the parent include the decl so we can say "declared here" - return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{ - elem_ptr.index, check_len, - }); - } - const elem_ty = parent.ty.childType(); - switch (parent.val.tag()) { - .undef => { - // An array has been initialized to undefined at comptime and now we - // are for the first time setting an element. We must change the representation - // of the array from `undef` to `array`. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); + var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr, elem_ptr.elem_ty); + switch (parent.pointee) { + .direct => |val_ptr| switch (parent.ty.zigTypeTag()) { + .Array, .Vector => { + const check_len = parent.ty.arrayLenIncludingSentinel(); + if (elem_ptr.index >= check_len) { + // TODO have the parent include the decl so we can say "declared here" + return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{ + elem_ptr.index, check_len, + }); + } + const elem_ty = parent.ty.childType(); + switch (val_ptr.tag()) { + .undef => { + // An array has been initialized to undefined at comptime and now we + // are for the first time setting an element. We must change the representation + // of the array from `undef` to `array`. + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); - const array_len_including_sentinel = - try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel()); - const elems = try arena.alloc(Value, array_len_including_sentinel); - mem.set(Value, elems, Value.undef); + const array_len_including_sentinel = + try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel()); + const elems = try arena.alloc(Value, array_len_including_sentinel); + mem.set(Value, elems, Value.undef); - parent.val.* = try Value.Tag.aggregate.create(arena, elems); + val_ptr.* = try Value.Tag.aggregate.create(arena, elems); - return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .val = &elems[elem_ptr.index], - .ty = elem_ty, - }; - }, - .bytes => { - // An array is memory-optimized to store a slice of bytes, but we are about - // to modify an individual field and the representation has to change. - // If we wanted to avoid this, there would need to be special detection - // elsewhere to identify when writing a value to an array element that is stored - // using the `bytes` tag, and handle it without making a call to this function. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); + return beginComptimePtrMutationInner( + sema, + block, + src, + elem_ty, + &elems[elem_ptr.index], + ptr_elem_ty, + parent.decl_ref_mut, + ); + }, + .bytes => { + // An array is memory-optimized to store a slice of bytes, but we are about + // to modify an individual field and the representation has to change. + // If we wanted to avoid this, there would need to be special detection + // elsewhere to identify when writing a value to an array element that is stored + // using the `bytes` tag, and handle it without making a call to this function. + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); - const bytes = parent.val.castTag(.bytes).?.data; - const dest_len = parent.ty.arrayLenIncludingSentinel(); - // bytes.len may be one greater than dest_len because of the case when - // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted. - assert(bytes.len >= dest_len); - const elems = try arena.alloc(Value, @intCast(usize, dest_len)); - for (elems) |*elem, i| { - elem.* = try Value.Tag.int_u64.create(arena, bytes[i]); - } + const bytes = val_ptr.castTag(.bytes).?.data; + const dest_len = parent.ty.arrayLenIncludingSentinel(); + // bytes.len may be one greater than dest_len because of the case when + // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted. + assert(bytes.len >= dest_len); + const elems = try arena.alloc(Value, @intCast(usize, dest_len)); + for (elems) |*elem, i| { + elem.* = try Value.Tag.int_u64.create(arena, bytes[i]); + } - parent.val.* = try Value.Tag.aggregate.create(arena, elems); + val_ptr.* = try Value.Tag.aggregate.create(arena, elems); - return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .val = &elems[elem_ptr.index], - .ty = elem_ty, - }; - }, - .str_lit => { - // An array is memory-optimized to store a slice of bytes, but we are about - // to modify an individual field and the representation has to change. - // If we wanted to avoid this, there would need to be special detection - // elsewhere to identify when writing a value to an array element that is stored - // using the `str_lit` tag, and handle it without making a call to this function. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); + return beginComptimePtrMutationInner( + sema, + block, + src, + elem_ty, + &elems[elem_ptr.index], + ptr_elem_ty, + parent.decl_ref_mut, + ); + }, + .str_lit => { + // An array is memory-optimized to store a slice of bytes, but we are about + // to modify an individual field and the representation has to change. + // If we wanted to avoid this, there would need to be special detection + // elsewhere to identify when writing a value to an array element that is stored + // using the `str_lit` tag, and handle it without making a call to this function. + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); - const str_lit = parent.val.castTag(.str_lit).?.data; - const dest_len = parent.ty.arrayLenIncludingSentinel(); - const bytes = sema.mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; - const elems = try arena.alloc(Value, @intCast(usize, dest_len)); - for (bytes) |byte, i| { - elems[i] = try Value.Tag.int_u64.create(arena, byte); - } - if (parent.ty.sentinel()) |sent_val| { - assert(elems.len == bytes.len + 1); - elems[bytes.len] = sent_val; - } + const str_lit = val_ptr.castTag(.str_lit).?.data; + const dest_len = parent.ty.arrayLenIncludingSentinel(); + const bytes = sema.mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; + const elems = try arena.alloc(Value, @intCast(usize, dest_len)); + for (bytes) |byte, i| { + elems[i] = try Value.Tag.int_u64.create(arena, byte); + } + if (parent.ty.sentinel()) |sent_val| { + assert(elems.len == bytes.len + 1); + elems[bytes.len] = sent_val; + } - parent.val.* = try Value.Tag.aggregate.create(arena, elems); + val_ptr.* = try Value.Tag.aggregate.create(arena, elems); - return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .val = &elems[elem_ptr.index], - .ty = elem_ty, - }; - }, - .repeated => { - // An array is memory-optimized to store only a single element value, and - // that value is understood to be the same for the entire length of the array. - // However, now we want to modify an individual field and so the - // representation has to change. If we wanted to avoid this, there would - // need to be special detection elsewhere to identify when writing a value to an - // array element that is stored using the `repeated` tag, and handle it - // without making a call to this function. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); + return beginComptimePtrMutationInner( + sema, + block, + src, + elem_ty, + &elems[elem_ptr.index], + ptr_elem_ty, + parent.decl_ref_mut, + ); + }, + .repeated => { + // An array is memory-optimized to store only a single element value, and + // that value is understood to be the same for the entire length of the array. + // However, now we want to modify an individual field and so the + // representation has to change. If we wanted to avoid this, there would + // need to be special detection elsewhere to identify when writing a value to an + // array element that is stored using the `repeated` tag, and handle it + // without making a call to this function. + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); - const repeated_val = try parent.val.castTag(.repeated).?.data.copy(arena); - const array_len_including_sentinel = - try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel()); - const elems = try arena.alloc(Value, array_len_including_sentinel); - mem.set(Value, elems, repeated_val); + const repeated_val = try val_ptr.castTag(.repeated).?.data.copy(arena); + const array_len_including_sentinel = + try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel()); + const elems = try arena.alloc(Value, array_len_including_sentinel); + mem.set(Value, elems, repeated_val); - parent.val.* = try Value.Tag.aggregate.create(arena, elems); + val_ptr.* = try Value.Tag.aggregate.create(arena, elems); - return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .val = &elems[elem_ptr.index], - .ty = elem_ty, - }; - }, + return beginComptimePtrMutationInner( + sema, + block, + src, + elem_ty, + &elems[elem_ptr.index], + ptr_elem_ty, + parent.decl_ref_mut, + ); + }, - .aggregate => return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .val = &parent.val.castTag(.aggregate).?.data[elem_ptr.index], - .ty = elem_ty, - }, + .aggregate => return beginComptimePtrMutationInner( + sema, + block, + src, + elem_ty, + &val_ptr.castTag(.aggregate).?.data[elem_ptr.index], + ptr_elem_ty, + parent.decl_ref_mut, + ), - .the_only_possible_value => { - const duped = try sema.arena.create(Value); - duped.* = Value.initTag(.the_only_possible_value); - return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .val = duped, - .ty = elem_ty, - }; - }, + .the_only_possible_value => { + const duped = try sema.arena.create(Value); + duped.* = Value.initTag(.the_only_possible_value); + return beginComptimePtrMutationInner( + sema, + block, + src, + elem_ty, + duped, + ptr_elem_ty, + parent.decl_ref_mut, + ); + }, - else => unreachable, - } + else => unreachable, + } + }, + else => { + if (elem_ptr.index != 0) { + // TODO include a "declared here" note for the decl + return sema.fail(block, src, "out of bounds comptime store of index {d}", .{ + elem_ptr.index, + }); + } + return beginComptimePtrMutationInner( + sema, + block, + src, + parent.ty, + val_ptr, + ptr_elem_ty, + parent.decl_ref_mut, + ); + }, }, - else => { - if (elem_ptr.index != 0) { - // TODO include a "declared here" note for the decl - return sema.fail(block, src, "out of bounds comptime store of index {d}", .{ - elem_ptr.index, - }); + .reinterpret => |reinterpret| { + if (!elem_ptr.elem_ty.hasWellDefinedLayout()) { + // Even though the parent value type has well-defined memory layout, our + // pointer type does not. + return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .pointee = .bad_ptr_ty, + .ty = elem_ptr.elem_ty, + }; } + + const elem_abi_size_u64 = try sema.typeAbiSize(block, src, elem_ptr.elem_ty); + const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64); return ComptimePtrMutationKit{ .decl_ref_mut = parent.decl_ref_mut, - .val = parent.val, + .pointee = .{ .reinterpret = .{ + .val_ptr = reinterpret.val_ptr, + .byte_offset = reinterpret.byte_offset + elem_abi_size * elem_ptr.index, + } }, .ty = parent.ty, }; }, + .bad_decl_ty, .bad_ptr_ty => return parent, } }, .field_ptr => { const field_ptr = ptr_val.castTag(.field_ptr).?.data; - var parent = try beginComptimePtrMutation(sema, block, src, field_ptr.container_ptr); const field_index = @intCast(u32, field_ptr.field_index); - switch (parent.val.tag()) { - .undef => { - // A struct or union has been initialized to undefined at comptime and now we - // are for the first time setting a field. We must change the representation - // of the struct/union from `undef` to `struct`/`union`. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); - switch (parent.ty.zigTypeTag()) { - .Struct => { - const fields = try arena.alloc(Value, parent.ty.structFieldCount()); - mem.set(Value, fields, Value.undef); + var parent = try beginComptimePtrMutation(sema, block, src, field_ptr.container_ptr, field_ptr.container_ty); + switch (parent.pointee) { + .direct => |val_ptr| switch (val_ptr.tag()) { + .undef => { + // A struct or union has been initialized to undefined at comptime and now we + // are for the first time setting a field. We must change the representation + // of the struct/union from `undef` to `struct`/`union`. + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); - parent.val.* = try Value.Tag.aggregate.create(arena, fields); + switch (parent.ty.zigTypeTag()) { + .Struct => { + const fields = try arena.alloc(Value, parent.ty.structFieldCount()); + mem.set(Value, fields, Value.undef); - return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .val = &fields[field_index], - .ty = parent.ty.structFieldType(field_index), - }; - }, - .Union => { - const payload = try arena.create(Value.Payload.Union); - payload.* = .{ .data = .{ - .tag = try Value.Tag.enum_field_index.create(arena, field_index), - .val = Value.undef, - } }; + val_ptr.* = try Value.Tag.aggregate.create(arena, fields); - parent.val.* = Value.initPayload(&payload.base); + return beginComptimePtrMutationInner( + sema, + block, + src, + parent.ty.structFieldType(field_index), + &fields[field_index], + ptr_elem_ty, + parent.decl_ref_mut, + ); + }, + .Union => { + const payload = try arena.create(Value.Payload.Union); + payload.* = .{ .data = .{ + .tag = try Value.Tag.enum_field_index.create(arena, field_index), + .val = Value.undef, + } }; - return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .val = &payload.data.val, - .ty = parent.ty.structFieldType(field_index), - }; - }, - .Pointer => { - assert(parent.ty.isSlice()); - parent.val.* = try Value.Tag.slice.create(arena, .{ - .ptr = Value.undef, - .len = Value.undef, - }); + val_ptr.* = Value.initPayload(&payload.base); + + return beginComptimePtrMutationInner( + sema, + block, + src, + parent.ty.structFieldType(field_index), + &payload.data.val, + ptr_elem_ty, + parent.decl_ref_mut, + ); + }, + .Pointer => { + assert(parent.ty.isSlice()); + val_ptr.* = try Value.Tag.slice.create(arena, .{ + .ptr = Value.undef, + .len = Value.undef, + }); + + switch (field_index) { + Value.Payload.Slice.ptr_index => return beginComptimePtrMutationInner( + sema, + block, + src, + parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)), + &val_ptr.castTag(.slice).?.data.ptr, + ptr_elem_ty, + parent.decl_ref_mut, + ), + Value.Payload.Slice.len_index => return beginComptimePtrMutationInner( + sema, + block, + src, + Type.usize, + &val_ptr.castTag(.slice).?.data.len, + ptr_elem_ty, + parent.decl_ref_mut, + ), + + else => unreachable, + } + }, + else => unreachable, + } + }, + .aggregate => return beginComptimePtrMutationInner( + sema, + block, + src, + parent.ty.structFieldType(field_index), + &val_ptr.castTag(.aggregate).?.data[field_index], + ptr_elem_ty, + parent.decl_ref_mut, + ), + + .@"union" => { + // We need to set the active field of the union. + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); + + const payload = &val_ptr.castTag(.@"union").?.data; + payload.tag = try Value.Tag.enum_field_index.create(arena, field_index); + + return beginComptimePtrMutationInner( + sema, + block, + src, + parent.ty.structFieldType(field_index), + &payload.val, + ptr_elem_ty, + parent.decl_ref_mut, + ); + }, + .slice => switch (field_index) { + Value.Payload.Slice.ptr_index => return beginComptimePtrMutationInner( + sema, + block, + src, + parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)), + &val_ptr.castTag(.slice).?.data.ptr, + ptr_elem_ty, + parent.decl_ref_mut, + ), + + Value.Payload.Slice.len_index => return beginComptimePtrMutationInner( + sema, + block, + src, + Type.usize, + &val_ptr.castTag(.slice).?.data.len, + ptr_elem_ty, + parent.decl_ref_mut, + ), - switch (field_index) { - Value.Payload.Slice.ptr_index => return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .val = &parent.val.castTag(.slice).?.data.ptr, - .ty = parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)), - }, - Value.Payload.Slice.len_index => return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .val = &parent.val.castTag(.slice).?.data.len, - .ty = Type.usize, - }, - else => unreachable, - } - }, else => unreachable, - } - }, - .aggregate => return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .val = &parent.val.castTag(.aggregate).?.data[field_index], - .ty = parent.ty.structFieldType(field_index), - }, - .@"union" => { - // We need to set the active field of the union. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); - - const payload = &parent.val.castTag(.@"union").?.data; - payload.tag = try Value.Tag.enum_field_index.create(arena, field_index); - - return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .val = &payload.val, - .ty = parent.ty.structFieldType(field_index), - }; - }, - .slice => switch (field_index) { - Value.Payload.Slice.ptr_index => return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .val = &parent.val.castTag(.slice).?.data.ptr, - .ty = parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)), - }, - Value.Payload.Slice.len_index => return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .val = &parent.val.castTag(.slice).?.data.len, - .ty = Type.usize, }, + else => unreachable, }, - - else => unreachable, + .reinterpret => |reinterpret| { + const field_offset_u64 = field_ptr.container_ty.structFieldOffset(field_index, target); + const field_offset = try sema.usizeCast(block, src, field_offset_u64); + return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .pointee = .{ .reinterpret = .{ + .val_ptr = reinterpret.val_ptr, + .byte_offset = reinterpret.byte_offset + field_offset, + } }, + .ty = parent.ty, + }; + }, + .bad_decl_ty, .bad_ptr_ty => return parent, } }, .eu_payload_ptr => { const eu_ptr = ptr_val.castTag(.eu_payload_ptr).?.data; - var parent = try beginComptimePtrMutation(sema, block, src, eu_ptr.container_ptr); - const payload_ty = parent.ty.errorUnionPayload(); - switch (parent.val.tag()) { - else => { - // An error union has been initialized to undefined at comptime and now we - // are for the first time setting the payload. We must change the - // representation of the error union from `undef` to `opt_payload`. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); + var parent = try beginComptimePtrMutation(sema, block, src, eu_ptr.container_ptr, eu_ptr.container_ty); + switch (parent.pointee) { + .direct => |val_ptr| { + const payload_ty = parent.ty.errorUnionPayload(); + switch (val_ptr.tag()) { + else => { + // An error union has been initialized to undefined at comptime and now we + // are for the first time setting the payload. We must change the + // representation of the error union from `undef` to `opt_payload`. + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); - const payload = try arena.create(Value.Payload.SubValue); - payload.* = .{ - .base = .{ .tag = .eu_payload }, - .data = Value.undef, - }; + const payload = try arena.create(Value.Payload.SubValue); + payload.* = .{ + .base = .{ .tag = .eu_payload }, + .data = Value.undef, + }; - parent.val.* = Value.initPayload(&payload.base); + val_ptr.* = Value.initPayload(&payload.base); - return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .val = &payload.data, - .ty = payload_ty, - }; + return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .pointee = .{ .direct = &payload.data }, + .ty = payload_ty, + }; + }, + .eu_payload => return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .pointee = .{ .direct = &val_ptr.castTag(.eu_payload).?.data }, + .ty = payload_ty, + }, + } }, - .eu_payload => return ComptimePtrMutationKit{ + .bad_decl_ty, .bad_ptr_ty => return parent, + // Even though the parent value type has well-defined memory layout, our + // pointer type does not. + .reinterpret => return ComptimePtrMutationKit{ .decl_ref_mut = parent.decl_ref_mut, - .val = &parent.val.castTag(.eu_payload).?.data, - .ty = payload_ty, + .pointee = .bad_ptr_ty, + .ty = eu_ptr.container_ty, }, } }, .opt_payload_ptr => { const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data; - var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr.container_ptr); - const payload_ty = try parent.ty.optionalChildAlloc(sema.arena); - switch (parent.val.tag()) { - .undef, .null_value => { - // An optional has been initialized to undefined at comptime and now we - // are for the first time setting the payload. We must change the - // representation of the optional from `undef` to `opt_payload`. - const arena = parent.beginArena(sema.mod); - defer parent.finishArena(sema.mod); + var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr.container_ptr, opt_ptr.container_ty); + switch (parent.pointee) { + .direct => |val_ptr| { + const payload_ty = try parent.ty.optionalChildAlloc(sema.arena); + switch (val_ptr.tag()) { + .undef, .null_value => { + // An optional has been initialized to undefined at comptime and now we + // are for the first time setting the payload. We must change the + // representation of the optional from `undef` to `opt_payload`. + const arena = parent.beginArena(sema.mod); + defer parent.finishArena(sema.mod); - const payload = try arena.create(Value.Payload.SubValue); - payload.* = .{ - .base = .{ .tag = .opt_payload }, - .data = Value.undef, - }; + const payload = try arena.create(Value.Payload.SubValue); + payload.* = .{ + .base = .{ .tag = .opt_payload }, + .data = Value.undef, + }; - parent.val.* = Value.initPayload(&payload.base); + val_ptr.* = Value.initPayload(&payload.base); - return ComptimePtrMutationKit{ - .decl_ref_mut = parent.decl_ref_mut, - .val = &payload.data, - .ty = payload_ty, - }; + return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .pointee = .{ .direct = &payload.data }, + .ty = payload_ty, + }; + }, + .opt_payload => return ComptimePtrMutationKit{ + .decl_ref_mut = parent.decl_ref_mut, + .pointee = .{ .direct = &val_ptr.castTag(.opt_payload).?.data }, + .ty = payload_ty, + }, + + else => unreachable, + } }, - .opt_payload => return ComptimePtrMutationKit{ + .bad_decl_ty, .bad_ptr_ty => return parent, + // Even though the parent value type has well-defined memory layout, our + // pointer type does not. + .reinterpret => return ComptimePtrMutationKit{ .decl_ref_mut = parent.decl_ref_mut, - .val = &parent.val.castTag(.opt_payload).?.data, - .ty = payload_ty, + .pointee = .bad_ptr_ty, + .ty = opt_ptr.container_ty, }, - - else => unreachable, } }, .decl_ref => unreachable, // isComptimeMutablePtr() has been checked already @@ -20918,10 +21078,63 @@ fn beginComptimePtrMutation( } } +fn beginComptimePtrMutationInner( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + decl_ty: Type, + decl_val: *Value, + ptr_elem_ty: Type, + decl_ref_mut: Value.Payload.DeclRefMut.Data, +) CompileError!ComptimePtrMutationKit { + const target = sema.mod.getTarget(); + const coerce_ok = (try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_ty, true, target, src, src)) == .ok; + if (coerce_ok) { + return ComptimePtrMutationKit{ + .decl_ref_mut = decl_ref_mut, + .pointee = .{ .direct = decl_val }, + .ty = decl_ty, + }; + } + + // Handle the case that the decl is an array and we're actually trying to point to an element. + if (decl_ty.isArrayOrVector()) { + const decl_elem_ty = decl_ty.childType(); + if ((try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_elem_ty, true, target, src, src)) == .ok) { + return ComptimePtrMutationKit{ + .decl_ref_mut = decl_ref_mut, + .pointee = .{ .direct = decl_val }, + .ty = decl_ty, + }; + } + } + + if (!decl_ty.hasWellDefinedLayout()) { + return ComptimePtrMutationKit{ + .decl_ref_mut = decl_ref_mut, + .pointee = .{ .bad_decl_ty = {} }, + .ty = decl_ty, + }; + } + if (!ptr_elem_ty.hasWellDefinedLayout()) { + return ComptimePtrMutationKit{ + .decl_ref_mut = decl_ref_mut, + .pointee = .{ .bad_ptr_ty = {} }, + .ty = ptr_elem_ty, + }; + } + return ComptimePtrMutationKit{ + .decl_ref_mut = decl_ref_mut, + .pointee = .{ .reinterpret = .{ + .val_ptr = decl_val, + .byte_offset = 0, + } }, + .ty = decl_ty, + }; +} + const TypedValueAndOffset = struct { tv: TypedValue, - /// The starting byte offset of `val` from `root_val`. - /// If the type does not have a well-defined memory layout, this is null. byte_offset: usize, }; @@ -21197,7 +21410,7 @@ fn bitCast( return block.addBitCast(dest_ty, inst); } -pub fn bitCastVal( +fn bitCastVal( sema: *Sema, block: *Block, src: LazySrcLoc, diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index e56ea0cad5..0ea3a33990 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -1252,3 +1252,20 @@ test "pass pointer to field of comptime-only type as a runtime parameter" { }; try S.doTheTest(); } + +test "comptime write through extern struct reinterpreted as array" { + comptime { + const S = extern struct { + a: u8, + b: u8, + c: u8, + }; + var s: S = undefined; + @ptrCast(*[3]u8, &s)[0] = 1; + @ptrCast(*[3]u8, &s)[1] = 2; + @ptrCast(*[3]u8, &s)[2] = 3; + assert(s.a == 1); + assert(s.b == 2); + assert(s.c == 3); + } +} diff --git a/test/behavior/translate_c_macros.zig b/test/behavior/translate_c_macros.zig index ec2695b4a0..e44996b990 100644 --- a/test/behavior/translate_c_macros.zig +++ b/test/behavior/translate_c_macros.zig @@ -19,7 +19,10 @@ test "casting to void with a macro" { } test "initializer list expression" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try expectEqual(h.Color{ .r = 200, From 0a9d6956e7cac96c870ad062b4125b0a0a3b0143 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 12 Jun 2022 17:52:30 +0300 Subject: [PATCH 1832/2031] Sema: add missing set_union_tag --- lib/std/io/stream_source.zig | 1 - lib/std/json.zig | 1 - lib/std/os/linux/io_uring.zig | 2 -- src/Sema.zig | 4 +++- test/behavior/union.zig | 18 ++++++++++++++++++ 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/std/io/stream_source.zig b/lib/std/io/stream_source.zig index 2345a97855..ce5256028c 100644 --- a/lib/std/io/stream_source.zig +++ b/lib/std/io/stream_source.zig @@ -114,7 +114,6 @@ test "StreamSource (mutable buffer)" { } test "StreamSource (const buffer)" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; const buffer: [64]u8 = "Hello, World!".* ++ ([1]u8{0xAA} ** 51); var source = StreamSource{ .const_buffer = std.io.fixedBufferStream(&buffer) }; diff --git a/lib/std/json.zig b/lib/std/json.zig index 2e09818b6f..cc82c1d0a5 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -1355,7 +1355,6 @@ pub const Value = union(enum) { }; test "Value.jsonStringify" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO { var buffer: [10]u8 = undefined; var fbs = std.io.fixedBufferStream(&buffer); diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index e7ac17a1af..144927771f 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -2154,7 +2154,6 @@ test "timeout (after a number of completions)" { } test "timeout_remove" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO if (builtin.os.tag != .linux) return error.SkipZigTest; var ring = IO_Uring.init(2, 0) catch |err| switch (err) { @@ -2951,7 +2950,6 @@ test "provide_buffers: read" { } test "remove_buffers" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO if (builtin.os.tag != .linux) return error.SkipZigTest; var ring = IO_Uring.init(1, 0) catch |err| switch (err) { diff --git a/src/Sema.zig b/src/Sema.zig index f0eb67ec26..5ad9cacff9 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -13757,10 +13757,10 @@ fn zirStructInit( const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; const field_name = sema.code.nullTerminatedString(field_type_extra.name_start); const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src); + const tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index); const init_inst = try sema.resolveInst(item.data.init); if (try sema.resolveMaybeUndefVal(block, field_src, init_inst)) |val| { - const tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index); return sema.addConstantMaybeRef( block, src, @@ -13779,6 +13779,8 @@ fn zirStructInit( const alloc = try block.addTy(.alloc, alloc_ty); const field_ptr = try sema.unionFieldPtr(block, field_src, alloc, field_name, field_src, resolved_ty); try sema.storePtr(block, src, field_ptr, init_inst); + const new_tag = try sema.addConstant(resolved_ty.unionTagTypeHypothetical(), tag_val); + _ = try block.addBinOp(.set_union_tag, alloc, new_tag); return alloc; } diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 4daa9c1ee5..caa5d26aea 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1183,3 +1183,21 @@ test "comptime equality of extern unions with same tag" { const b = S.U{ .a = 1234 }; try expect(S.foo(a) == S.foo(b)); } + +test "union tag is set when initiated as a temporary value at runtime" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const U = union(enum) { + a, + b: u32, + c, + + fn doTheTest(u: @This()) !void { + try expect(u == .b); + } + }; + var b: u32 = 1; + try (U{ .b = b }).doTheTest(); +} From 13f02c30e62564b85c2fbaa98e632d44f854cf53 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 12 Jun 2022 14:34:43 -0700 Subject: [PATCH 1833/2031] stage2: fix some inline asm incompatibilities with stage1 --- src/Sema.zig | 6 ++++- src/codegen/llvm.zig | 59 +++++++++++++++++++++++++++++++++++++++---- test/behavior/asm.zig | 3 --- 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index a5e991c617..a769194776 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -11406,6 +11406,7 @@ fn zirAsm( // Indicate the output is the asm instruction return value. arg.* = .none; const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand); + try sema.queueFullTypeResolution(out_ty); expr_ty = try sema.addType(out_ty); } else { arg.* = try sema.resolveInst(output.data.operand); @@ -11430,7 +11431,10 @@ fn zirAsm( switch (uncasted_arg_ty.zigTypeTag()) { .ComptimeInt => arg.* = try sema.coerce(block, Type.initTag(.usize), uncasted_arg, src), .ComptimeFloat => arg.* = try sema.coerce(block, Type.initTag(.f64), uncasted_arg, src), - else => arg.* = uncasted_arg, + else => { + arg.* = uncasted_arg; + try sema.queueFullTypeResolution(uncasted_arg_ty); + }, } const constraint = sema.code.nullTerminatedString(input.data.constraint); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index efe6a754b6..bf09ec0f35 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5421,6 +5421,8 @@ pub const FuncGen = struct { const llvm_params_len = inputs.len + outputs.len - return_count; const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len); const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len); + const target = self.dg.module.getTarget(); + var llvm_param_i: usize = 0; var total_i: usize = 0; @@ -5449,7 +5451,18 @@ pub const FuncGen = struct { llvm_param_types[llvm_param_i] = output_inst.typeOf(); llvm_param_i += 1; } - llvm_constraints.appendSliceAssumeCapacity(constraint[1..]); + + // LLVM uses commas internally to separate different constraints, + // alternative constraints are achieved with pipes. + // We still allow the user to use commas in a way that is similar + // to GCC's inline assembly. + // http://llvm.org/docs/LangRef.html#constraint-codes + for (constraint[1..]) |byte| { + llvm_constraints.appendAssumeCapacity(switch (byte) { + ',' => '|', + else => byte, + }); + } name_map.putAssumeCapacityNoClobber(name, {}); total_i += 1; @@ -5464,15 +5477,43 @@ pub const FuncGen = struct { extra_i += (constraint.len + name.len + (2 + 3)) / 4; const arg_llvm_value = try self.resolveInst(input); - - llvm_param_values[llvm_param_i] = arg_llvm_value; - llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf(); + const arg_ty = self.air.typeOf(input); + if (isByRef(arg_ty)) { + if (constraintAllowsMemory(constraint)) { + llvm_param_values[llvm_param_i] = arg_llvm_value; + llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf(); + } else { + const alignment = arg_ty.abiAlignment(target); + const load_inst = self.builder.buildLoad(arg_llvm_value, ""); + load_inst.setAlignment(alignment); + llvm_param_values[llvm_param_i] = load_inst; + llvm_param_types[llvm_param_i] = load_inst.typeOf(); + } + } else { + if (constraintAllowsRegister(constraint)) { + llvm_param_values[llvm_param_i] = arg_llvm_value; + llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf(); + } else { + const alignment = arg_ty.abiAlignment(target); + const arg_ptr = self.buildAlloca(arg_llvm_value.typeOf()); + arg_ptr.setAlignment(alignment); + const store_inst = self.builder.buildStore(arg_llvm_value, arg_ptr); + store_inst.setAlignment(alignment); + llvm_param_values[llvm_param_i] = arg_ptr; + llvm_param_types[llvm_param_i] = arg_ptr.typeOf(); + } + } try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1); if (total_i != 0) { llvm_constraints.appendAssumeCapacity(','); } - llvm_constraints.appendSliceAssumeCapacity(constraint); + for (constraint) |byte| { + llvm_constraints.appendAssumeCapacity(switch (byte) { + ',' => '|', + else => byte, + }); + } if (!std.mem.eql(u8, name, "_")) { name_map.putAssumeCapacityNoClobber(name, {}); @@ -9307,3 +9348,11 @@ fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u1 { fn errUnionErrorOffset(payload_ty: Type, target: std.Target) u1 { return @boolToInt(Type.anyerror.abiAlignment(target) <= payload_ty.abiAlignment(target)); } + +fn constraintAllowsMemory(constraint: []const u8) bool { + return constraint[0] == 'm'; +} + +fn constraintAllowsRegister(constraint: []const u8) bool { + return constraint[0] != 'm'; +} diff --git a/test/behavior/asm.zig b/test/behavior/asm.zig index 9aa95a3a0b..8f235a384b 100644 --- a/test/behavior/asm.zig +++ b/test/behavior/asm.zig @@ -35,7 +35,6 @@ test "output constraint modifiers" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO // This is only testing compilation. var a: u32 = 3; @@ -57,7 +56,6 @@ test "alternative constraints" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO // Make sure we allow commas as a separator for alternative constraints. var a: u32 = 3; @@ -122,7 +120,6 @@ test "struct/array/union types as input values" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO asm volatile ("" : From 7c660d17cde9af6b4c7229a300ff0ccd31317bf3 Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Mon, 13 Jun 2022 08:13:52 +0200 Subject: [PATCH 1834/2031] crypto/pcurves: compute constants for inversion at comptime (#11780) --- lib/std/crypto/pcurves/common.zig | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/std/crypto/pcurves/common.zig b/lib/std/crypto/pcurves/common.zig index 55607b33c0..1160334fe4 100644 --- a/lib/std/crypto/pcurves/common.zig +++ b/lib/std/crypto/pcurves/common.zig @@ -203,19 +203,17 @@ pub fn Field(comptime params: FieldParams) type { const XLimbs = [a.limbs.len + 1]Word; var d: Word = 1; - var f: XLimbs = undefined; - fiat.msat(&f); - + var f = comptime blk: { + var f: XLimbs = undefined; + fiat.msat(&f); + break :blk f; + }; var g: XLimbs = undefined; fiat.fromMontgomery(g[0..a.limbs.len], a.limbs); g[g.len - 1] = 0; - var r: Limbs = undefined; - fiat.setOne(&r); - var v = mem.zeroes(Limbs); - - var precomp: Limbs = undefined; - fiat.divstepPrecomp(&precomp); + var r = Fe.one.limbs; + var v = Fe.zero.limbs; var out1: Word = undefined; var out2: XLimbs = undefined; @@ -236,6 +234,12 @@ pub fn Field(comptime params: FieldParams) type { var v_opp: Limbs = undefined; fiat.opp(&v_opp, v); fiat.selectznz(&v, @truncate(u1, f[f.len - 1] >> (@bitSizeOf(Word) - 1)), v, v_opp); + + const precomp = blk: { + var precomp: Limbs = undefined; + fiat.divstepPrecomp(&precomp); + break :blk precomp; + }; var fe: Fe = undefined; fiat.mul(&fe.limbs, v, precomp); return fe; From 47c4d4450281bfddebf89cbad4f9c1fdcc7b0b65 Mon Sep 17 00:00:00 2001 From: Mikael Berthe Date: Mon, 13 Jun 2022 17:19:37 +0200 Subject: [PATCH 1835/2031] std.math.big.int: update Managed.toString() to use provided allocator (#11839) --- lib/std/math/big/int.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 170272f287..661d370419 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -2593,9 +2593,8 @@ pub const Managed = struct { /// Converts self to a string in the requested base. Memory is allocated from the provided /// allocator and not the one present in self. pub fn toString(self: Managed, allocator: Allocator, base: u8, case: std.fmt.Case) ![]u8 { - _ = allocator; if (base < 2 or base > 16) return error.InvalidBase; - return self.toConst().toStringAlloc(self.allocator, base, case); + return self.toConst().toStringAlloc(allocator, base, case); } /// To allow `std.fmt.format` to work with `Managed`. From 22690efcc2378222503cb8aaad26a6f4a539f5aa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Jun 2022 18:59:52 -0700 Subject: [PATCH 1836/2031] multi-thread `zig build test-cases` Instead of always using std.testing.allocator, the test harness now follows the same logic as self-hosted for choosing an allocator - that is - it uses C allocator when linking libc, std.testing.allocator otherwise, and respects `-Dforce-gpa` to override the decision. I did this because I found GeneralPurposeAllocator to be prohibitively slow when doing multi-threading, even in the context of a debug build. There is now a second thread pool which is used to spawn each test case. The stage2 tests are passed the first thread pool. If it were only multi-threading the stage1 tests then we could use the same thread pool for everything. However, the problem with this strategy with stage2 is that stage2 wants to spawn tasks and then call wait() on the main thread. If we use the same thread pool for everything, we get a deadlock because all the threads end up all hanging at wait() and nothing is getting done. So we use our second thread pool to simulate a "process pool" of sorts. I spent most of the time working on this commit scratching my head trying to figure out why I was getting ETXTBSY when spawning the test cases. Turns out it's a fundamental Unix design flaw, already a known, unsolved issue by Go and Java maintainers: https://github.com/golang/go/issues/22315 https://bugs.openjdk.org/browse/JDK-8068370 With this change, the following command, executed on my laptop, went from 6m24s to 1m44s: ``` stage1/bin/zig build test-cases -fqemu -fwasmtime -Denable-llvm ``` closes #11818 --- build.zig | 1 + src/test.zig | 205 ++++++++++++++++++++++++++++++++++----------------- 2 files changed, 140 insertions(+), 66 deletions(-) diff --git a/build.zig b/build.zig index 0afbe9171a..fc97fc91bd 100644 --- a/build.zig +++ b/build.zig @@ -398,6 +398,7 @@ pub fn build(b: *Builder) !void { test_cases_options.addOption(bool, "llvm_has_csky", llvm_has_csky); test_cases_options.addOption(bool, "llvm_has_ve", llvm_has_ve); test_cases_options.addOption(bool, "llvm_has_arc", llvm_has_arc); + test_cases_options.addOption(bool, "force_gpa", force_gpa); test_cases_options.addOption(bool, "enable_qemu", b.enable_qemu); test_cases_options.addOption(bool, "enable_wine", b.enable_wine); test_cases_options.addOption(bool, "enable_wasmtime", b.enable_wasmtime); diff --git a/src/test.zig b/src/test.zig index 9e8e3ccb95..f8b8ed8ce8 100644 --- a/src/test.zig +++ b/src/test.zig @@ -1,11 +1,19 @@ const std = @import("std"); const builtin = @import("builtin"); +const Allocator = std.mem.Allocator; +const CrossTarget = std.zig.CrossTarget; +const print = std.debug.print; +const assert = std.debug.assert; + const link = @import("link.zig"); const Compilation = @import("Compilation.zig"); -const Allocator = std.mem.Allocator; const Package = @import("Package.zig"); const introspect = @import("introspect.zig"); const build_options = @import("build_options"); +const ThreadPool = @import("ThreadPool.zig"); +const WaitGroup = @import("WaitGroup.zig"); +const zig_h = link.File.C.zig_h; + const enable_qemu: bool = build_options.enable_qemu; const enable_wine: bool = build_options.enable_wine; const enable_wasmtime: bool = build_options.enable_wasmtime; @@ -13,12 +21,6 @@ const enable_darling: bool = build_options.enable_darling; const enable_rosetta: bool = build_options.enable_rosetta; const glibc_runtimes_dir: ?[]const u8 = build_options.glibc_runtimes_dir; const skip_stage1 = build_options.skip_stage1; -const ThreadPool = @import("ThreadPool.zig"); -const CrossTarget = std.zig.CrossTarget; -const print = std.debug.print; -const assert = std.debug.assert; - -const zig_h = link.File.C.zig_h; const hr = "=" ** 80; @@ -27,11 +29,24 @@ test { @import("stage1.zig").os_init(); } - var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator); + const use_gpa = build_options.force_gpa or !builtin.link_libc; + const gpa = gpa: { + if (use_gpa) { + break :gpa std.testing.allocator; + } + // We would prefer to use raw libc allocator here, but cannot + // use it if it won't support the alignment we need. + if (@alignOf(std.c.max_align_t) < @alignOf(i128)) { + break :gpa std.heap.c_allocator; + } + break :gpa std.heap.raw_c_allocator; + }; + + var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - var ctx = TestContext.init(std.testing.allocator, arena); + var ctx = TestContext.init(gpa, arena); defer ctx.deinit(); { @@ -536,6 +551,7 @@ fn sortTestFilenames(filenames: [][]const u8) void { } pub const TestContext = struct { + gpa: Allocator, arena: Allocator, cases: std.ArrayList(Case), @@ -604,6 +620,8 @@ pub const TestContext = struct { files: std.ArrayList(File), + result: anyerror!void = {}, + pub fn addSourceFile(case: *Case, name: []const u8, src: [:0]const u8) void { case.files.append(.{ .path = name, .src = src }) catch @panic("out of memory"); } @@ -1185,6 +1203,7 @@ pub const TestContext = struct { fn init(gpa: Allocator, arena: Allocator) TestContext { return .{ + .gpa = gpa, .cases = std.ArrayList(Case).init(gpa), .arena = arena, }; @@ -1204,19 +1223,23 @@ pub const TestContext = struct { } fn run(self: *TestContext) !void { - const host = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, .{}); + const host = try std.zig.system.NativeTargetInfo.detect(self.gpa, .{}); var progress = std.Progress{}; const root_node = progress.start("compiler", self.cases.items.len); defer root_node.end(); - var zig_lib_directory = try introspect.findZigLibDir(std.testing.allocator); + var zig_lib_directory = try introspect.findZigLibDir(self.gpa); defer zig_lib_directory.handle.close(); - defer std.testing.allocator.free(zig_lib_directory.path.?); + defer self.gpa.free(zig_lib_directory.path.?); - var thread_pool: ThreadPool = undefined; - try thread_pool.init(std.testing.allocator); - defer thread_pool.deinit(); + var aux_thread_pool: ThreadPool = undefined; + try aux_thread_pool.init(self.gpa); + defer aux_thread_pool.deinit(); + + var case_thread_pool: ThreadPool = undefined; + try case_thread_pool.init(self.gpa); + defer case_thread_pool.deinit(); // Use the same global cache dir for all the tests, such that we for example don't have to // rebuild musl libc for every case (when LLVM backend is enabled). @@ -1225,60 +1248,90 @@ pub const TestContext = struct { var cache_dir = try global_tmp.dir.makeOpenPath("zig-cache", .{}); defer cache_dir.close(); - const tmp_dir_path = try std.fs.path.join(std.testing.allocator, &[_][]const u8{ ".", "zig-cache", "tmp", &global_tmp.sub_path }); - defer std.testing.allocator.free(tmp_dir_path); + const tmp_dir_path = try std.fs.path.join(self.gpa, &[_][]const u8{ ".", "zig-cache", "tmp", &global_tmp.sub_path }); + defer self.gpa.free(tmp_dir_path); const global_cache_directory: Compilation.Directory = .{ .handle = cache_dir, - .path = try std.fs.path.join(std.testing.allocator, &[_][]const u8{ tmp_dir_path, "zig-cache" }), + .path = try std.fs.path.join(self.gpa, &[_][]const u8{ tmp_dir_path, "zig-cache" }), }; - defer std.testing.allocator.free(global_cache_directory.path.?); + defer self.gpa.free(global_cache_directory.path.?); + + { + var wait_group: WaitGroup = .{}; + defer wait_group.wait(); + + for (self.cases.items) |*case| { + if (build_options.skip_non_native) { + if (case.target.getCpuArch() != builtin.cpu.arch) + continue; + if (case.target.getObjectFormat() != builtin.object_format) + continue; + } + + // Skip tests that require LLVM backend when it is not available + if (!build_options.have_llvm and case.backend == .llvm) + continue; + + if (build_options.test_filter) |test_filter| { + if (std.mem.indexOf(u8, case.name, test_filter) == null) continue; + } + + wait_group.start(); + try case_thread_pool.spawn(workerRunOneCase, .{ + self.gpa, + root_node, + case, + zig_lib_directory, + &aux_thread_pool, + global_cache_directory, + host, + &wait_group, + }); + } + } var fail_count: usize = 0; - - for (self.cases.items) |case| { - if (build_options.skip_non_native) { - if (case.target.getCpuArch() != builtin.cpu.arch) - continue; - if (case.target.getObjectFormat() != builtin.object_format) - continue; - } - - // Skip tests that require LLVM backend when it is not available - if (!build_options.have_llvm and case.backend == .llvm) - continue; - - if (build_options.test_filter) |test_filter| { - if (std.mem.indexOf(u8, case.name, test_filter) == null) continue; - } - var prg_node = root_node.start(case.name, case.updates.items.len); - prg_node.activate(); - defer prg_node.end(); - - // So that we can see which test case failed when the leak checker goes off, - // or there's an internal error - progress.initial_delay_ns = 0; - progress.refresh_rate_ns = 0; - - runOneCase( - std.testing.allocator, - &prg_node, - case, - zig_lib_directory, - &thread_pool, - global_cache_directory, - host, - ) catch |err| { + for (self.cases.items) |*case| { + case.result catch |err| { fail_count += 1; - print("test '{s}' failed: {s}\n\n", .{ case.name, @errorName(err) }); + print("{s} failed: {s}\n", .{ case.name, @errorName(err) }); }; } + if (fail_count != 0) { print("{d} tests failed\n", .{fail_count}); return error.TestFailed; } } + fn workerRunOneCase( + gpa: Allocator, + root_node: *std.Progress.Node, + case: *Case, + zig_lib_directory: Compilation.Directory, + thread_pool: *ThreadPool, + global_cache_directory: Compilation.Directory, + host: std.zig.system.NativeTargetInfo, + wait_group: *WaitGroup, + ) void { + defer wait_group.finish(); + + var prg_node = root_node.start(case.name, case.updates.items.len); + prg_node.activate(); + defer prg_node.end(); + + case.result = runOneCase( + gpa, + &prg_node, + case.*, + zig_lib_directory, + thread_pool, + global_cache_directory, + host, + ); + } + fn runOneCase( allocator: Allocator, root_node: *std.Progress.Node, @@ -1368,6 +1421,11 @@ pub const TestContext = struct { try zig_args.append("-O"); try zig_args.append(@tagName(case.optimize_mode)); + // Prevent sub-process progress bar from interfering with the + // one in this parent process. + try zig_args.append("--color"); + try zig_args.append("off"); + const result = try std.ChildProcess.exec(.{ .allocator = arena, .argv = zig_args.items, @@ -1529,6 +1587,8 @@ pub const TestContext = struct { .use_llvm = use_llvm, .use_stage1 = null, // We already handled stage1 tests .self_exe_path = std.testing.zig_exe_path, + // TODO instead of turning off color, pass in a std.Progress.Node + .color = .off, }); defer comp.destroy(); @@ -1820,18 +1880,31 @@ pub const TestContext = struct { try comp.makeBinFileExecutable(); - break :x std.ChildProcess.exec(.{ - .allocator = allocator, - .argv = argv.items, - .cwd_dir = tmp.dir, - .cwd = tmp_dir_path, - }) catch |err| { - print("\nupdate_index={d} The following command failed with {s}:\n", .{ - update_index, @errorName(err), - }); - dumpArgs(argv.items); - return error.ChildProcessExecution; - }; + while (true) { + break :x std.ChildProcess.exec(.{ + .allocator = allocator, + .argv = argv.items, + .cwd_dir = tmp.dir, + .cwd = tmp_dir_path, + }) catch |err| switch (err) { + error.FileBusy => { + // There is a fundamental design flaw in Unix systems with how + // ETXTBSY interacts with fork+exec. + // https://github.com/golang/go/issues/22315 + // https://bugs.openjdk.org/browse/JDK-8068370 + // Unfortunately, this could be a real error, but we can't + // tell the difference here. + continue; + }, + else => { + print("\n{s}.{d} The following command failed with {s}:\n", .{ + case.name, update_index, @errorName(err), + }); + dumpArgs(argv.items); + return error.ChildProcessExecution; + }, + }; + } }; var test_node = update_node.start("test", 0); test_node.activate(); From 8c630376952546ddd09e76fcc72d3d2a8d12e0b6 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 14 Jun 2022 14:03:27 +0200 Subject: [PATCH 1837/2031] Update the WASI libc Update our copy of wasi-libc up to the commit 30094b6ed05f19cee102115215863d185f2db4f0 from the upstream repository. --- .../cloudlibc/src/libc/sys/stat/stat_impl.h | 6 + .../src/libc/sys/time/gettimeofday.c | 8 +- .../sources/__wasilibc_real.c | 12 -- .../wasi/libc-bottom-half/sources/accept.c | 51 ++++++++ .../wasi/libc-bottom-half/sources/chdir.c | 9 +- .../wasi/libc-bottom-half/sources/posix.c | 30 ++++- .../wasi/libc-top-half/musl/include/ctype.h | 2 + .../wasi/libc-top-half/musl/include/elf.h | 2 + .../wasi/libc-top-half/musl/include/locale.h | 4 +- .../musl/include/netinet/if_ether.h | 1 + .../libc-top-half/musl/include/netinet/in.h | 1 + .../libc-top-half/musl/include/netinet/tcp.h | 11 ++ .../wasi/libc-top-half/musl/include/pthread.h | 1 + .../wasi/libc-top-half/musl/include/setjmp.h | 14 +- .../wasi/libc-top-half/musl/include/signal.h | 8 ++ .../libc-top-half/musl/include/stdc-predef.h | 3 + .../wasi/libc-top-half/musl/include/stddef.h | 4 +- .../wasi/libc-top-half/musl/include/stdio.h | 4 +- .../wasi/libc-top-half/musl/include/stdlib.h | 5 +- .../wasi/libc-top-half/musl/include/string.h | 4 +- .../musl/include/sys/membarrier.h | 4 + .../libc-top-half/musl/include/sys/mman.h | 1 + .../libc-top-half/musl/include/sys/mount.h | 1 + .../libc-top-half/musl/include/sys/prctl.h | 16 +++ .../libc-top-half/musl/include/sys/ptrace.h | 9 ++ .../libc-top-half/musl/include/sys/socket.h | 5 +- .../libc-top-half/musl/include/sys/time.h | 4 +- .../wasi/libc-top-half/musl/include/time.h | 4 +- .../wasi/libc-top-half/musl/include/unistd.h | 6 +- .../wasi/libc-top-half/musl/include/wchar.h | 4 +- .../libc-top-half/musl/src/complex/cacosf.c | 4 +- .../libc-top-half/musl/src/complex/catanf.c | 4 +- .../libc-top-half/musl/src/complex/cproj.c | 2 +- .../libc-top-half/musl/src/complex/cprojf.c | 2 +- .../libc-top-half/musl/src/complex/cprojl.c | 2 +- .../libc-top-half/musl/src/ctype/nonspacing.h | 122 +++++++++--------- .../musl/src/env/__libc_start_main.c | 3 +- .../musl/src/env/__stack_chk_fail.c | 9 ++ .../libc-top-half/musl/src/errno/__strerror.h | 6 + .../musl/src/fenv/powerpc/fenv-sf.c | 2 +- .../musl/src/fenv/powerpc/fenv.S | 2 +- .../libc-top-half/musl/src/include/stdlib.h | 1 + .../musl/src/ldso/dl_iterate_phdr.c | 3 +- .../libc-top-half/musl/src/legacy/cuserid.c | 14 +- .../wasi/libc-top-half/musl/src/linux/epoll.c | 4 +- .../musl/src/locale/dcngettext.c | 3 + .../libc-top-half/musl/src/locale/duplocale.c | 5 + .../libc-top-half/musl/src/locale/strtod_l.c | 22 ++++ .../wasi/libc-top-half/musl/src/malloc/free.c | 2 +- .../musl/src/malloc/mallocng/aligned_alloc.c | 3 + .../musl/src/malloc/mallocng/free.c | 12 +- .../musl/src/malloc/oldmalloc/malloc.c | 6 +- .../wasi/libc-top-half/musl/src/math/acoshf.c | 8 +- .../wasi/libc-top-half/musl/src/math/expm1f.c | 3 +- .../wasi/libc-top-half/musl/src/math/fmaf.c | 25 ++-- .../musl/src/math/powerpc/fabs.c | 2 +- .../musl/src/math/powerpc/fabsf.c | 2 +- .../libc-top-half/musl/src/math/powerpc/fma.c | 2 +- .../musl/src/math/powerpc/fmaf.c | 2 +- .../wasi/libc-top-half/musl/src/misc/ioctl.c | 9 +- .../musl/src/passwd/nscd_query.c | 10 +- .../libc-top-half/musl/src/process/fdop.h | 5 + .../posix_spawn_file_actions_addclose.c | 1 + .../posix_spawn_file_actions_adddup2.c | 1 + .../posix_spawn_file_actions_addfchdir.c | 1 + .../posix_spawn_file_actions_addopen.c | 1 + .../musl/src/setjmp/powerpc/longjmp.S | 32 ++++- .../musl/src/setjmp/powerpc/setjmp.S | 32 ++++- .../libc-top-half/musl/src/signal/block.c | 4 +- .../libc-top-half/musl/src/stat/futimesat.c | 9 ++ .../libc-top-half/musl/src/stdio/fgetws.c | 7 +- .../wasi/libc-top-half/musl/src/stdio/fseek.c | 7 + .../libc-top-half/musl/src/stdio/getdelim.c | 8 +- .../wasi/libc-top-half/musl/src/stdio/popen.c | 24 +--- .../libc-top-half/musl/src/stdlib/qsort.c | 37 +++--- .../libc-top-half/musl/src/stdlib/qsort_nr.c | 14 ++ .../libc-top-half/musl/src/stdlib/strtod.c | 21 --- .../musl/src/thread/pthread_getname_np.c | 25 ++++ .../musl/src/thread/pthread_setname_np.c | 2 +- .../wasi/libc-top-half/musl/src/time/__tz.c | 40 +++--- .../wasi/libc-top-half/musl/src/unistd/nice.c | 9 +- 81 files changed, 583 insertions(+), 227 deletions(-) create mode 100644 lib/libc/wasi/libc-bottom-half/sources/accept.c create mode 100644 lib/libc/wasi/libc-top-half/musl/src/locale/strtod_l.c create mode 100644 lib/libc/wasi/libc-top-half/musl/src/stdlib/qsort_nr.c create mode 100644 lib/libc/wasi/libc-top-half/musl/src/thread/pthread_getname_np.c diff --git a/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/stat/stat_impl.h b/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/stat/stat_impl.h index c649d3b6d9..9726b51656 100644 --- a/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/stat/stat_impl.h +++ b/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/stat/stat_impl.h @@ -75,14 +75,18 @@ static inline bool utimens_get_timestamps(const struct timespec *times, if (times == NULL) { // Update both timestamps. *flags = __WASI_FSTFLAGS_ATIM_NOW | __WASI_FSTFLAGS_MTIM_NOW; + *st_atim = (__wasi_timestamp_t) { 0 }; + *st_mtim = (__wasi_timestamp_t) { 0 }; } else { // Set individual timestamps. *flags = 0; switch (times[0].tv_nsec) { case UTIME_NOW: *flags |= __WASI_FSTFLAGS_ATIM_NOW; + *st_atim = (__wasi_timestamp_t) { 0 }; break; case UTIME_OMIT: + *st_atim = (__wasi_timestamp_t) { 0 }; break; default: *flags |= __WASI_FSTFLAGS_ATIM; @@ -94,8 +98,10 @@ static inline bool utimens_get_timestamps(const struct timespec *times, switch (times[1].tv_nsec) { case UTIME_NOW: *flags |= __WASI_FSTFLAGS_MTIM_NOW; + *st_mtim = (__wasi_timestamp_t) { 0 }; break; case UTIME_OMIT: + *st_mtim = (__wasi_timestamp_t) { 0 }; break; default: *flags |= __WASI_FSTFLAGS_MTIM; diff --git a/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/time/gettimeofday.c b/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/time/gettimeofday.c index d58cbb9d21..596bdfff28 100644 --- a/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/time/gettimeofday.c +++ b/lib/libc/wasi/libc-bottom-half/cloudlibc/src/libc/sys/time/gettimeofday.c @@ -9,8 +9,10 @@ #include int gettimeofday(struct timeval *restrict tp, void *tz) { - __wasi_timestamp_t ts = 0; - (void)__wasi_clock_time_get(__WASI_CLOCKID_REALTIME, 1000, &ts); - *tp = timestamp_to_timeval(ts); + if (tp != NULL) { + __wasi_timestamp_t ts = 0; + (void)__wasi_clock_time_get(__WASI_CLOCKID_REALTIME, 1000, &ts); + *tp = timestamp_to_timeval(ts); + } return 0; } diff --git a/lib/libc/wasi/libc-bottom-half/sources/__wasilibc_real.c b/lib/libc/wasi/libc-bottom-half/sources/__wasilibc_real.c index e6481f701e..37ca7d95a1 100644 --- a/lib/libc/wasi/libc-bottom-half/sources/__wasilibc_real.c +++ b/lib/libc/wasi/libc-bottom-half/sources/__wasilibc_real.c @@ -574,18 +574,6 @@ _Noreturn void __wasi_proc_exit( __imported_wasi_snapshot_preview1_proc_exit((int32_t) rval); } -int32_t __imported_wasi_snapshot_preview1_proc_raise(int32_t arg0) __attribute__(( - __import_module__("wasi_snapshot_preview1"), - __import_name__("proc_raise") -)); - -__wasi_errno_t __wasi_proc_raise( - __wasi_signal_t sig -){ - int32_t ret = __imported_wasi_snapshot_preview1_proc_raise((int32_t) sig); - return (uint16_t) ret; -} - int32_t __imported_wasi_snapshot_preview1_sched_yield() __attribute__(( __import_module__("wasi_snapshot_preview1"), __import_name__("sched_yield") diff --git a/lib/libc/wasi/libc-bottom-half/sources/accept.c b/lib/libc/wasi/libc-bottom-half/sources/accept.c new file mode 100644 index 0000000000..902e7316f5 --- /dev/null +++ b/lib/libc/wasi/libc-bottom-half/sources/accept.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: BSD-2-Clause + +#include + +#include +#include +#include +#include + +int accept(int socket, struct sockaddr *restrict addr, socklen_t *restrict addrlen) { + int ret = -1; + + __wasi_errno_t error = __wasi_sock_accept(socket, 0, &ret); + + if (error != 0) { + errno = error; + return -1; + } + + // Clear sockaddr to indicate undefined address + memset(addr, 0, *addrlen); + // might be AF_UNIX or AF_INET + addr->sa_family = AF_UNSPEC; + *addrlen = sizeof(struct sockaddr); + + return ret; +} + +int accept4(int socket, struct sockaddr *restrict addr, socklen_t *restrict addrlen, int flags) { + int ret = -1; + + if (flags & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)) { + errno = EINVAL; + return -1; + } + + __wasi_errno_t error = __wasi_sock_accept(socket, (flags & SOCK_NONBLOCK) ? __WASI_FDFLAGS_NONBLOCK : 0, &ret); + + if (error != 0) { + errno = error; + return -1; + } + + // Clear sockaddr to indicate undefined address + memset(addr, 0, *addrlen); + // might be AF_UNIX or AF_INET + addr->sa_family = AF_UNSPEC; + *addrlen = sizeof(struct sockaddr); + + return ret; +} diff --git a/lib/libc/wasi/libc-bottom-half/sources/chdir.c b/lib/libc/wasi/libc-bottom-half/sources/chdir.c index 7820448ee1..1a102db20e 100644 --- a/lib/libc/wasi/libc-bottom-half/sources/chdir.c +++ b/lib/libc/wasi/libc-bottom-half/sources/chdir.c @@ -46,7 +46,7 @@ int chdir(const char *path) size_t len = strlen(abs) + 1; int copy_relative = strcmp(relative_buf, ".") != 0; int mid = copy_relative && abs[0] != 0; - char *new_cwd = malloc(len + (copy_relative ? strlen(relative_buf) + mid: 0)); + char *new_cwd = malloc(len + (copy_relative ? strlen(relative_buf) + mid: 0)+1); if (new_cwd == NULL) { errno = ENOMEM; return -1; @@ -54,7 +54,7 @@ int chdir(const char *path) new_cwd[0] = '/'; strcpy(new_cwd + 1, abs); if (mid) - new_cwd[strlen(abs) + 1] = '/'; + new_cwd[len] = '/'; if (copy_relative) strcpy(new_cwd + 1 + mid + strlen(abs), relative_buf); @@ -95,9 +95,10 @@ static const char *make_absolute(const char *path) { int need_slash = __wasilibc_cwd[cwd_len - 1] == '/' ? 0 : 1; size_t alloc_len = cwd_len + path_len + 1 + need_slash; if (alloc_len > make_absolute_len) { - make_absolute_buf = realloc(make_absolute_buf, alloc_len); - if (make_absolute_buf == NULL) + char *tmp = realloc(make_absolute_buf, alloc_len); + if (tmp == NULL) return NULL; + make_absolute_buf = tmp; make_absolute_len = alloc_len; } strcpy(make_absolute_buf, __wasilibc_cwd); diff --git a/lib/libc/wasi/libc-bottom-half/sources/posix.c b/lib/libc/wasi/libc-bottom-half/sources/posix.c index 153280fa6e..35dd99b309 100644 --- a/lib/libc/wasi/libc-bottom-half/sources/posix.c +++ b/lib/libc/wasi/libc-bottom-half/sources/posix.c @@ -31,16 +31,20 @@ static int find_relpath2( static int find_relpath(const char *path, char **relative) { static __thread char *relative_buf = NULL; static __thread size_t relative_buf_len = 0; + int fd = find_relpath2(path, &relative_buf, &relative_buf_len); + // find_relpath2 can update relative_buf, so assign it after the call *relative = relative_buf; - return find_relpath2(path, relative, &relative_buf_len); + return fd; } // same as `find_relpath`, but uses another set of static variables to cache static int find_relpath_alt(const char *path, char **relative) { static __thread char *relative_buf = NULL; static __thread size_t relative_buf_len = 0; + int fd = find_relpath2(path, &relative_buf, &relative_buf_len); + // find_relpath2 can update relative_buf, so assign it after the call *relative = relative_buf; - return find_relpath2(path, relative, &relative_buf_len); + return fd; } int open(const char *path, int oflag, ...) { @@ -139,6 +143,28 @@ int utime(const char *path, const struct utimbuf *times) { 0); } +int utimes(const char *path, const struct timeval times[2]) { + char *relative_path; + int dirfd = find_relpath(path, &relative_path); + + // If we can't find a preopen for it, indicate that we lack capabilities. + if (dirfd == -1) { + errno = ENOTCAPABLE; + return -1; + } + + return __wasilibc_nocwd_utimensat( + dirfd, relative_path, + times ? ((struct timespec [2]) { + { .tv_sec = times[0].tv_sec, + .tv_nsec = times[0].tv_usec * 1000 }, + { .tv_sec = times[1].tv_sec, + .tv_nsec = times[1].tv_usec * 1000 }, + }) + : NULL, + 0); +} + int unlink(const char *path) { char *relative_path; int dirfd = find_relpath(path, &relative_path); diff --git a/lib/libc/wasi/libc-top-half/musl/include/ctype.h b/lib/libc/wasi/libc-top-half/musl/include/ctype.h index 7936536f57..32bcef4dab 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/ctype.h +++ b/lib/libc/wasi/libc-top-half/musl/include/ctype.h @@ -64,7 +64,9 @@ int isascii(int); int toascii(int); #define _tolower(a) ((a)|0x20) #define _toupper(a) ((a)&0x5f) +#ifndef __cplusplus #define isascii(a) (0 ? isascii(a) : (unsigned)(a) < 128) +#endif #endif diff --git a/lib/libc/wasi/libc-top-half/musl/include/elf.h b/lib/libc/wasi/libc-top-half/musl/include/elf.h index b5e7befb02..86e2f0bb7d 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/elf.h +++ b/lib/libc/wasi/libc-top-half/musl/include/elf.h @@ -686,6 +686,8 @@ typedef struct { #define NT_ARM_PAC_MASK 0x406 #define NT_ARM_PACA_KEYS 0x407 #define NT_ARM_PACG_KEYS 0x408 +#define NT_ARM_TAGGED_ADDR_CTRL 0x409 +#define NT_ARM_PAC_ENABLED_KEYS 0x40a #define NT_METAG_CBUF 0x500 #define NT_METAG_RPIPE 0x501 #define NT_METAG_TLS 0x502 diff --git a/lib/libc/wasi/libc-top-half/musl/include/locale.h b/lib/libc/wasi/libc-top-half/musl/include/locale.h index 228e5026c8..6a62d1f5a7 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/locale.h +++ b/lib/libc/wasi/libc-top-half/musl/include/locale.h @@ -8,7 +8,9 @@ extern "C" { #include #ifdef __wasilibc_unmodified_upstream /* Use the compiler's definition of NULL */ -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/wasi/libc-top-half/musl/include/netinet/if_ether.h b/lib/libc/wasi/libc-top-half/musl/include/netinet/if_ether.h index 55a2ff1b17..3479f511bf 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/netinet/if_ether.h +++ b/lib/libc/wasi/libc-top-half/musl/include/netinet/if_ether.h @@ -66,6 +66,7 @@ #define ETH_P_1588 0x88F7 #define ETH_P_NCSI 0x88F8 #define ETH_P_PRP 0x88FB +#define ETH_P_CFM 0x8902 #define ETH_P_FCOE 0x8906 #define ETH_P_TDLS 0x890D #define ETH_P_FIP 0x8914 diff --git a/lib/libc/wasi/libc-top-half/musl/include/netinet/in.h b/lib/libc/wasi/libc-top-half/musl/include/netinet/in.h index b09efab018..d9422af2ea 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/netinet/in.h +++ b/lib/libc/wasi/libc-top-half/musl/include/netinet/in.h @@ -60,6 +60,7 @@ struct ipv6_mreq { #define INADDR_BROADCAST ((in_addr_t) 0xffffffff) #define INADDR_NONE ((in_addr_t) 0xffffffff) #define INADDR_LOOPBACK ((in_addr_t) 0x7f000001) +#define INADDR_DUMMY ((in_addr_t) 0xc0000008) #define INADDR_UNSPEC_GROUP ((in_addr_t) 0xe0000000) #define INADDR_ALLHOSTS_GROUP ((in_addr_t) 0xe0000001) diff --git a/lib/libc/wasi/libc-top-half/musl/include/netinet/tcp.h b/lib/libc/wasi/libc-top-half/musl/include/netinet/tcp.h index b7b997f5fd..fad1d84494 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/netinet/tcp.h +++ b/lib/libc/wasi/libc-top-half/musl/include/netinet/tcp.h @@ -80,6 +80,8 @@ enum { TCP_NLA_SRTT, TCP_NLA_TIMEOUT_REHASH, TCP_NLA_BYTES_NOTSENT, + TCP_NLA_EDT, + TCP_NLA_TTL, }; #if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) @@ -281,12 +283,21 @@ struct tcp_repair_window { uint32_t rcv_wup; }; +#define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1 + struct tcp_zerocopy_receive { uint64_t address; uint32_t length; uint32_t recv_skip_hint; uint32_t inq; int32_t err; + uint64_t copybuf_address; + int32_t copybuf_len; + uint32_t flags; + uint64_t msg_control; + uint64_t msg_controllen; + uint32_t msg_flags; + uint32_t reserved; }; #endif diff --git a/lib/libc/wasi/libc-top-half/musl/include/pthread.h b/lib/libc/wasi/libc-top-half/musl/include/pthread.h index b11a567d5c..b0801d4060 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/pthread.h +++ b/lib/libc/wasi/libc-top-half/musl/include/pthread.h @@ -227,6 +227,7 @@ int pthread_getaffinity_np(pthread_t, size_t, struct cpu_set_t *); int pthread_setaffinity_np(pthread_t, size_t, const struct cpu_set_t *); int pthread_getattr_np(pthread_t, pthread_attr_t *); int pthread_setname_np(pthread_t, const char *); +int pthread_getname_np(pthread_t, char *, size_t); int pthread_getattr_default_np(pthread_attr_t *); int pthread_setattr_default_np(const pthread_attr_t *); int pthread_tryjoin_np(pthread_t, void **); diff --git a/lib/libc/wasi/libc-top-half/musl/include/setjmp.h b/lib/libc/wasi/libc-top-half/musl/include/setjmp.h index 6a653efa3d..f505f8e4fa 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/setjmp.h +++ b/lib/libc/wasi/libc-top-half/musl/include/setjmp.h @@ -16,21 +16,27 @@ typedef struct __jmp_buf_tag { unsigned long __ss[128/sizeof(long)]; } jmp_buf[1]; +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) +#define __setjmp_attr __attribute__((__returns_twice__)) +#else +#define __setjmp_attr +#endif + #if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \ || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ || defined(_BSD_SOURCE) typedef jmp_buf sigjmp_buf; -int sigsetjmp (sigjmp_buf, int); +int sigsetjmp (sigjmp_buf, int) __setjmp_attr; _Noreturn void siglongjmp (sigjmp_buf, int); #endif #if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \ || defined(_BSD_SOURCE) -int _setjmp (jmp_buf); +int _setjmp (jmp_buf) __setjmp_attr; _Noreturn void _longjmp (jmp_buf, int); #endif -int setjmp (jmp_buf); +int setjmp (jmp_buf) __setjmp_attr; _Noreturn void longjmp (jmp_buf, int); #define setjmp setjmp @@ -38,6 +44,8 @@ _Noreturn void longjmp (jmp_buf, int); #warning setjmp is not yet implemented for WASI #endif +#undef __setjmp_attr + #ifdef __cplusplus } #endif diff --git a/lib/libc/wasi/libc-top-half/musl/include/signal.h b/lib/libc/wasi/libc-top-half/musl/include/signal.h index ae74966b96..75b5e55284 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/signal.h +++ b/lib/libc/wasi/libc-top-half/musl/include/signal.h @@ -82,6 +82,8 @@ typedef struct sigaltstack stack_t; #define SEGV_ACCERR 2 #define SEGV_BNDERR 3 #define SEGV_PKUERR 4 +#define SEGV_MTEAERR 8 +#define SEGV_MTESERR 9 #define BUS_ADRALN 1 #define BUS_ADRERR 2 @@ -183,6 +185,9 @@ struct sigaction { #define sa_handler __sa_handler.sa_handler #define sa_sigaction __sa_handler.sa_sigaction +#define SA_UNSUPPORTED 0x00000400 +#define SA_EXPOSE_TAGBITS 0x00000800 + struct sigevent { union sigval sigev_value; int sigev_signo; @@ -277,6 +282,9 @@ void (*sigset(int, void (*)(int)))(int); #if defined(_BSD_SOURCE) || defined(_GNU_SOURCE) #define NSIG _NSIG typedef void (*sig_t)(int); + +#define SYS_SECCOMP 1 +#define SYS_USER_DISPATCH 2 #endif #ifdef _GNU_SOURCE diff --git a/lib/libc/wasi/libc-top-half/musl/include/stdc-predef.h b/lib/libc/wasi/libc-top-half/musl/include/stdc-predef.h index f8cd4b8911..af1a27998f 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/stdc-predef.h +++ b/lib/libc/wasi/libc-top-half/musl/include/stdc-predef.h @@ -7,4 +7,7 @@ #define __STDC_IEC_559__ 1 #endif +#define __STDC_UTF_16__ 1 +#define __STDC_UTF_32__ 1 + #endif diff --git a/lib/libc/wasi/libc-top-half/musl/include/stddef.h b/lib/libc/wasi/libc-top-half/musl/include/stddef.h index bd75385350..f25b86396e 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/stddef.h +++ b/lib/libc/wasi/libc-top-half/musl/include/stddef.h @@ -1,7 +1,9 @@ #ifndef _STDDEF_H #define _STDDEF_H -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/wasi/libc-top-half/musl/include/stdio.h b/lib/libc/wasi/libc-top-half/musl/include/stdio.h index 0c3aff9c2c..d63d739f0f 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/stdio.h +++ b/lib/libc/wasi/libc-top-half/musl/include/stdio.h @@ -28,7 +28,9 @@ extern "C" { #include #ifdef __wasilibc_unmodified_upstream /* Use the compiler's definition of NULL */ -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/wasi/libc-top-half/musl/include/stdlib.h b/lib/libc/wasi/libc-top-half/musl/include/stdlib.h index e635275d68..1bcb9ab0aa 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/stdlib.h +++ b/lib/libc/wasi/libc-top-half/musl/include/stdlib.h @@ -13,7 +13,9 @@ extern "C" { #include #ifdef __wasilibc_unmodified_upstream /* Use the compiler's definition of NULL */ -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) @@ -171,6 +173,7 @@ int clearenv(void); #define WCOREDUMP(s) ((s) & 0x80) #define WIFCONTINUED(s) ((s) == 0xffff) void *reallocarray (void *, size_t, size_t); +void qsort_r (void *, size_t, size_t, int (*)(const void *, const void *, void *), void *); #endif #endif diff --git a/lib/libc/wasi/libc-top-half/musl/include/string.h b/lib/libc/wasi/libc-top-half/musl/include/string.h index c2d464c889..dc47b7aee0 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/string.h +++ b/lib/libc/wasi/libc-top-half/musl/include/string.h @@ -12,7 +12,9 @@ extern "C" { #include #ifdef __wasilibc_unmodified_upstream /* Use the compiler's definition of NULL */ -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/wasi/libc-top-half/musl/include/sys/membarrier.h b/lib/libc/wasi/libc-top-half/musl/include/sys/membarrier.h index 10cb31083c..11193eda15 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/sys/membarrier.h +++ b/lib/libc/wasi/libc-top-half/musl/include/sys/membarrier.h @@ -9,9 +9,13 @@ #define MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED 16 #define MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE 32 #define MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE 64 +#define MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ 128 +#define MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ 256 #define MEMBARRIER_CMD_SHARED MEMBARRIER_CMD_GLOBAL +#define MEMBARRIER_CMD_FLAG_CPU 1 + int membarrier(int, int); #endif diff --git a/lib/libc/wasi/libc-top-half/musl/include/sys/mman.h b/lib/libc/wasi/libc-top-half/musl/include/sys/mman.h index 80615c5d57..335ba2d714 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/sys/mman.h +++ b/lib/libc/wasi/libc-top-half/musl/include/sys/mman.h @@ -44,6 +44,7 @@ extern "C" { #define MAP_HUGE_SHIFT 26 #define MAP_HUGE_MASK 0x3f +#define MAP_HUGE_16KB (14 << 26) #define MAP_HUGE_64KB (16 << 26) #define MAP_HUGE_512KB (19 << 26) #define MAP_HUGE_1MB (20 << 26) diff --git a/lib/libc/wasi/libc-top-half/musl/include/sys/mount.h b/lib/libc/wasi/libc-top-half/musl/include/sys/mount.h index 57a89c09ec..09bd6e9dfe 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/sys/mount.h +++ b/lib/libc/wasi/libc-top-half/musl/include/sys/mount.h @@ -31,6 +31,7 @@ extern "C" { #define MS_REMOUNT 32 #define MS_MANDLOCK 64 #define MS_DIRSYNC 128 +#define MS_NOSYMFOLLOW 256 #define MS_NOATIME 1024 #define MS_NODIRATIME 2048 #define MS_BIND 4096 diff --git a/lib/libc/wasi/libc-top-half/musl/include/sys/prctl.h b/lib/libc/wasi/libc-top-half/musl/include/sys/prctl.h index 4b9fcc0508..087a75c9da 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/sys/prctl.h +++ b/lib/libc/wasi/libc-top-half/musl/include/sys/prctl.h @@ -157,10 +157,26 @@ struct prctl_mm_map { #define PR_SET_TAGGED_ADDR_CTRL 55 #define PR_GET_TAGGED_ADDR_CTRL 56 #define PR_TAGGED_ADDR_ENABLE (1UL << 0) +#define PR_MTE_TCF_SHIFT 1 +#define PR_MTE_TCF_NONE (0UL << 1) +#define PR_MTE_TCF_SYNC (1UL << 1) +#define PR_MTE_TCF_ASYNC (2UL << 1) +#define PR_MTE_TCF_MASK (3UL << 1) +#define PR_MTE_TAG_SHIFT 3 +#define PR_MTE_TAG_MASK (0xffffUL << 3) #define PR_SET_IO_FLUSHER 57 #define PR_GET_IO_FLUSHER 58 +#define PR_SET_SYSCALL_USER_DISPATCH 59 +#define PR_SYS_DISPATCH_OFF 0 +#define PR_SYS_DISPATCH_ON 1 +#define SYSCALL_DISPATCH_FILTER_ALLOW 0 +#define SYSCALL_DISPATCH_FILTER_BLOCK 1 + +#define PR_PAC_SET_ENABLED_KEYS 60 +#define PR_PAC_GET_ENABLED_KEYS 61 + int prctl (int, ...); #ifdef __cplusplus diff --git a/lib/libc/wasi/libc-top-half/musl/include/sys/ptrace.h b/lib/libc/wasi/libc-top-half/musl/include/sys/ptrace.h index 5d62a9859a..c72e3c061c 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/sys/ptrace.h +++ b/lib/libc/wasi/libc-top-half/musl/include/sys/ptrace.h @@ -42,6 +42,7 @@ extern "C" { #define PTRACE_SECCOMP_GET_FILTER 0x420c #define PTRACE_SECCOMP_GET_METADATA 0x420d #define PTRACE_GET_SYSCALL_INFO 0x420e +#define PTRACE_GET_RSEQ_CONFIGURATION 0x420f #define PT_READ_I PTRACE_PEEKTEXT #define PT_READ_D PTRACE_PEEKDATA @@ -130,6 +131,14 @@ struct __ptrace_syscall_info { }; }; +struct __ptrace_rseq_configuration { + uint64_t rseq_abi_pointer; + uint32_t rseq_abi_size; + uint32_t signature; + uint32_t flags; + uint32_t pad; +}; + long ptrace(int, ...); #ifdef __cplusplus diff --git a/lib/libc/wasi/libc-top-half/musl/include/sys/socket.h b/lib/libc/wasi/libc-top-half/musl/include/sys/socket.h index cea24cfd47..4d574c6627 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/sys/socket.h +++ b/lib/libc/wasi/libc-top-half/musl/include/sys/socket.h @@ -298,6 +298,8 @@ struct linger { #define SCM_TXTIME SO_TXTIME #define SO_BINDTOIFINDEX 62 #define SO_DETACH_REUSEPORT_BPF 68 +#define SO_PREFER_BUSY_POLL 69 +#define SO_BUSY_POLL_BUDGET 70 #ifndef SOL_SOCKET #define SOL_SOCKET 1 @@ -404,9 +406,10 @@ int shutdown (int, int); int bind (int, const struct sockaddr *, socklen_t); int connect (int, const struct sockaddr *, socklen_t); int listen (int, int); +#endif + int accept (int, struct sockaddr *__restrict, socklen_t *__restrict); int accept4(int, struct sockaddr *__restrict, socklen_t *__restrict, int); -#endif #ifdef __wasilibc_unmodified_upstream /* WASI has no getsockname/getpeername */ int getsockname (int, struct sockaddr *__restrict, socklen_t *__restrict); diff --git a/lib/libc/wasi/libc-top-half/musl/include/sys/time.h b/lib/libc/wasi/libc-top-half/musl/include/sys/time.h index 389cdcbb02..0f736551f6 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/sys/time.h +++ b/lib/libc/wasi/libc-top-half/musl/include/sys/time.h @@ -23,9 +23,7 @@ struct itimerval { int getitimer (int, struct itimerval *); int setitimer (int, const struct itimerval *__restrict, struct itimerval *__restrict); #endif -#ifdef __wasilibc_unmodified_upstream /* WASI libc doesn't build the legacy functions */ int utimes (const char *, const struct timeval [2]); -#endif #if defined(_GNU_SOURCE) || defined(_BSD_SOURCE) struct timezone { @@ -34,7 +32,9 @@ struct timezone { }; #ifdef __wasilibc_unmodified_upstream /* WASI libc doesn't build the legacy functions */ int futimes(int, const struct timeval [2]); +#endif int futimesat(int, const char *, const struct timeval [2]); +#ifdef __wasilibc_unmodified_upstream /* WASI libc doesn't build the legacy functions */ int lutimes(const char *, const struct timeval [2]); #endif #ifdef __wasilibc_unmodified_upstream /* WASI has no way to set the time */ diff --git a/lib/libc/wasi/libc-top-half/musl/include/time.h b/lib/libc/wasi/libc-top-half/musl/include/time.h index 1fb87689d1..f53414878a 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/time.h +++ b/lib/libc/wasi/libc-top-half/musl/include/time.h @@ -8,7 +8,9 @@ extern "C" { #include #ifdef __wasilibc_unmodified_upstream /* Use the compiler's definition of NULL */ -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/wasi/libc-top-half/musl/include/unistd.h b/lib/libc/wasi/libc-top-half/musl/include/unistd.h index 7ca99aeca0..9231d605c4 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/unistd.h +++ b/lib/libc/wasi/libc-top-half/musl/include/unistd.h @@ -15,12 +15,16 @@ extern "C" { #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 +#define SEEK_DATA 3 +#define SEEK_HOLE 4 #else #include <__header_unistd.h> #endif #ifdef __wasilibc_unmodified_upstream /* Use the compiler's definition of NULL */ -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/wasi/libc-top-half/musl/include/wchar.h b/lib/libc/wasi/libc-top-half/musl/include/wchar.h index 4f45539f16..06b088aa9b 100644 --- a/lib/libc/wasi/libc-top-half/musl/include/wchar.h +++ b/lib/libc/wasi/libc-top-half/musl/include/wchar.h @@ -41,7 +41,9 @@ extern "C" { #endif #ifdef __wasilibc_unmodified_upstream /* Use the compiler's definition of NULL */ -#ifdef __cplusplus +#if __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__cplusplus) #define NULL 0L #else #define NULL ((void*)0) diff --git a/lib/libc/wasi/libc-top-half/musl/src/complex/cacosf.c b/lib/libc/wasi/libc-top-half/musl/src/complex/cacosf.c index 2e048540fa..ed8acf0f5a 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/complex/cacosf.c +++ b/lib/libc/wasi/libc-top-half/musl/src/complex/cacosf.c @@ -2,8 +2,10 @@ // FIXME +static const float float_pi_2 = M_PI_2; + float complex cacosf(float complex z) { z = casinf(z); - return CMPLXF((float)M_PI_2 - crealf(z), -cimagf(z)); + return CMPLXF(float_pi_2 - crealf(z), -cimagf(z)); } diff --git a/lib/libc/wasi/libc-top-half/musl/src/complex/catanf.c b/lib/libc/wasi/libc-top-half/musl/src/complex/catanf.c index ef3907a506..1d569f2dac 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/complex/catanf.c +++ b/lib/libc/wasi/libc-top-half/musl/src/complex/catanf.c @@ -61,13 +61,15 @@ static const double DP1 = 3.140625; static const double DP2 = 9.67502593994140625E-4; static const double DP3 = 1.509957990978376432E-7; +static const float float_pi = M_PI; + static float _redupif(float xx) { float x, t; long i; x = xx; - t = x/(float)M_PI; + t = x/float_pi; if (t >= 0.0f) t += 0.5f; else diff --git a/lib/libc/wasi/libc-top-half/musl/src/complex/cproj.c b/lib/libc/wasi/libc-top-half/musl/src/complex/cproj.c index 9ae1e17c0d..d2b8f5a972 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/complex/cproj.c +++ b/lib/libc/wasi/libc-top-half/musl/src/complex/cproj.c @@ -3,6 +3,6 @@ double complex cproj(double complex z) { if (isinf(creal(z)) || isinf(cimag(z))) - return CMPLX(INFINITY, copysign(0.0, creal(z))); + return CMPLX(INFINITY, copysign(0.0, cimag(z))); return z; } diff --git a/lib/libc/wasi/libc-top-half/musl/src/complex/cprojf.c b/lib/libc/wasi/libc-top-half/musl/src/complex/cprojf.c index 03fab339d9..15a874bb2f 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/complex/cprojf.c +++ b/lib/libc/wasi/libc-top-half/musl/src/complex/cprojf.c @@ -3,6 +3,6 @@ float complex cprojf(float complex z) { if (isinf(crealf(z)) || isinf(cimagf(z))) - return CMPLXF(INFINITY, copysignf(0.0, crealf(z))); + return CMPLXF(INFINITY, copysignf(0.0, cimagf(z))); return z; } diff --git a/lib/libc/wasi/libc-top-half/musl/src/complex/cprojl.c b/lib/libc/wasi/libc-top-half/musl/src/complex/cprojl.c index 38a494c5c4..531ffa1c5e 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/complex/cprojl.c +++ b/lib/libc/wasi/libc-top-half/musl/src/complex/cprojl.c @@ -9,7 +9,7 @@ long double complex cprojl(long double complex z) long double complex cprojl(long double complex z) { if (isinf(creall(z)) || isinf(cimagl(z))) - return CMPLXL(INFINITY, copysignl(0.0, creall(z))); + return CMPLXL(INFINITY, copysignl(0.0, cimagl(z))); return z; } #endif diff --git a/lib/libc/wasi/libc-top-half/musl/src/ctype/nonspacing.h b/lib/libc/wasi/libc-top-half/musl/src/ctype/nonspacing.h index 5d05a3d1a0..7746f3b603 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/ctype/nonspacing.h +++ b/lib/libc/wasi/libc-top-half/musl/src/ctype/nonspacing.h @@ -1,23 +1,23 @@ -16,16,16,18,19,20,21,22,23,24,25,26,27,28,29,30,31,16,16,32,16,16,16,33,34,35, -36,37,38,39,16,16,40,16,16,16,16,16,16,16,16,16,16,16,41,42,16,16,43,16,16,16, +16,16,16,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,16,33,16,16,16,34,35,36, +37,38,39,40,16,16,41,16,16,16,16,16,16,16,16,16,16,16,42,43,16,16,44,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,44,16,45,46,47,48,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,45,16,46,47,48,49,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,50,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,51,16,16,52, +53,16,54,55,56,16,16,16,16,16,16,57,16,16,58,16,59,60,61,62,63,64,65,66,67,68, +69,70,16,71,72,73,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,74,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,75,76,16,16,16,77,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,49,16,16,50, -51,16,52,53,54,16,16,16,16,16,16,55,16,16,56,16,57,58,59,60,61,62,63,64,65,66, -67,68,16,69,70,71,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,72,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,73,74,16,16,16,75,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,16,16,16,16,16,76,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, -16,16,77,78,16,16,16,16,16,16,16,79,16,16,16,16,16,80,81,82,16,16,16,16,16,83, -84,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,16,16,16,16,16,78,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +16,16,79,80,16,16,16,16,16,16,16,81,16,16,16,16,16,82,83,84,16,16,16,16,16,85, +86,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, @@ -35,55 +35,57 @@ 242,7,128,127,0,0,0,0,0,0,0,0,0,0,0,0,242,31,0,63,0,0,0,0,0,0,0,0,0,3,0,0,160, 2,0,0,0,0,0,0,254,127,223,224,255,254,255,255,255,31,64,0,0,0,0,0,0,0,0,0,0,0, 0,224,253,102,0,0,0,195,1,0,30,0,100,32,0,32,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,0, -0,0,28,0,0,0,12,0,0,0,12,0,0,0,0,0,0,0,176,63,64,254,15,32,0,0,0,0,0,120,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,135,1,4,14,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,9,0,0,0,0,0,0,64,127, -229,31,248,159,0,0,0,0,0,0,255,127,0,0,0,0,0,0,0,0,15,0,0,0,0,0,208,23,4,0,0, -0,0,248,15,0,3,0,0,0,60,59,0,0,0,0,0,0,64,163,3,0,0,0,0,0,0,240,207,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,247,255,253,33,16,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255, +0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,224,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,28,0,0,0,28,0,0,0,12,0,0,0,12,0,0,0,0,0,0,0,176,63,64,254, +15,32,0,0,0,0,0,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,0,0,2,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,135,1,4,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +128,9,0,0,0,0,0,0,64,127,229,31,248,159,0,0,0,0,0,0,255,127,0,0,0,0,0,0,0,0, +15,0,0,0,0,0,208,23,4,0,0,0,0,248,15,0,3,0,0,0,60,59,0,0,0,0,0,0,64,163,3,0,0, +0,0,0,0,240,207,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,247,255,253,33,16, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255, 251,0,248,0,0,0,124,0,0,0,0,0,0,223,255,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255, 255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,3,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0, 0,60,0,0,0,0,0,0,0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,128,247,63,0,0,0,192,0,0,0,0,0,0,0,0,0,0,3,0,68,8,0,0,96,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,48,0,0,0,255,255,3,128,0,0,0,0,192,63,0,0,128,255,3,0, -0,0,0,0,7,0,0,0,0,0,200,51,0,0,0,0,32,0,0,0,0,0,0,0,0,126,102,0,8,16,0,0,0,0, -0,16,0,0,0,0,0,0,157,193,2,0,0,0,0,48,64, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,33,0,0,0,0,0,64, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,0,0,255,255,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,110,240,0,0,0,0,0,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0, -0,0,0,0,0,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,192,255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,255, -127,0,0,0,0,0,0,128,3,0,0,0,0,0,120,38,0,32,0,0,0,0,0,0,7,0,0,0,128,239,31,0, -0,0,0,0,0,0,8,0,3,0,0,0,0,0,192,127,0,30,0,0,0,0,0,0,0,0,0,0,0,128,211,64,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,248,7,0,0,3,0,0,0,0,0,0,24,1,0,0,0,192, -31,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,92,0,0,64,0,0,0,0,0, -0,0,0,0,0,248,133,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,60,176,1,0,0,48,0,0,0, -0,0,0,0,0,0,0,248,167,1,0,0,0,0,0,0,0,0,0,0,0,0,40,191,0,0,0,0,0,0,0,0,0,0,0, -0,224,188,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -128,255,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,12,1,0,0,0,254,7,0,0,0,0,248,121,128,0, -126,14,0,0,0,0,0,252,127,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,191,0,0,0, -0,0,0,0,0,0,0,252,255,255,252,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,126,180,191,0, -0,0,0,0,0,0,0,0,163,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,24, -0,0,0,0,0,0,0,255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,0,0,0,0,0,127,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0, -0,128,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,15, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,3,248,255,231,15,0,0,0,60,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,255,255,255,255,255,255,127,248,255,255,255,255,255,31,32,0,16,0,0,248, -254,255,0,0,0,0,0,0,0,0,0, -0,127,255,255,249,219,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,240,7,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,7,0,0,0,0,0,200,51,0,0,0,0,32,0,0, +0,0,0,0,0,0,126,102,0,8,16,0,0,0,0,0,16,0,0,0,0,0,0,157,193,2,0,0,0,0,48,64,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,33,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,0,0,0, +64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,0,0,255, +255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,110,240,0, +0,0,0,0,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,0,0,0,0,0,240,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,255,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,255,127,0,0,0,0,0,0,128, +3,0,0,0,0,0,120,38,0,32,0,0,0,0,0,0,7,0,0,0,128,239,31,0,0,0,0,0,0,0,8,0,3,0, +0,0,0,0,192,127,0,30,0,0,0,0,0,0,0,0,0,0,0,128,211,64,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,128,248,7,0,0,3,0,0,0,0,0,0,24,1,0,0,0,192,31,31,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,255,92,0,0,64,0,0,0,0,0,0,0,0,0,0,248,133,13,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,176,1,0,0,48,0,0,0,0,0,0,0,0,0,0, +248,167,1,0,0,0,0,0,0,0,0,0,0,0,0,40,191,0,0,0,0,0,0,0,0,0,0,0,0,224,188,15,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,255,6,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,240,12,1,0,0,0,254,7,0,0,0,0,248,121,128,0,126,14,0,0,0,0,0,252, +127,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,191,0,0,0,0,0,0,0,0,0,0,252,255, +255,252,109,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,126,180,191,0,0,0,0,0,0,0,0,0,163,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,0,0,0,0,0,0,0,255, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,128,7,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,15,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,3,248,255,231,15,0,0,0,60,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255, +255,255,255,255,127,248,255,255,255,255,255,31,32,0,16,0,0,248,254,255,0,0,0, +0,0,0,0,0,0,0,127,255,255,249,219,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,240,7,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, diff --git a/lib/libc/wasi/libc-top-half/musl/src/env/__libc_start_main.c b/lib/libc/wasi/libc-top-half/musl/src/env/__libc_start_main.c index 8fbe526271..c5b277bdcf 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/env/__libc_start_main.c +++ b/lib/libc/wasi/libc-top-half/musl/src/env/__libc_start_main.c @@ -69,7 +69,8 @@ weak_alias(libc_start_init, __libc_start_init); typedef int lsm2_fn(int (*)(int,char **,char **), int, char **); static lsm2_fn libc_start_main_stage2; -int __libc_start_main(int (*main)(int,char **,char **), int argc, char **argv) +int __libc_start_main(int (*main)(int,char **,char **), int argc, char **argv, + void (*init_dummy)(), void(*fini_dummy)(), void(*ldso_dummy)()) { char **envp = argv+argc+1; diff --git a/lib/libc/wasi/libc-top-half/musl/src/env/__stack_chk_fail.c b/lib/libc/wasi/libc-top-half/musl/src/env/__stack_chk_fail.c index bf5a280ad9..e53526020f 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/env/__stack_chk_fail.c +++ b/lib/libc/wasi/libc-top-half/musl/src/env/__stack_chk_fail.c @@ -9,6 +9,15 @@ void __init_ssp(void *entropy) if (entropy) memcpy(&__stack_chk_guard, entropy, sizeof(uintptr_t)); else __stack_chk_guard = (uintptr_t)&__stack_chk_guard * 1103515245; +#if UINTPTR_MAX >= 0xffffffffffffffff + /* Sacrifice 8 bits of entropy on 64bit to prevent leaking/ + * overwriting the canary via string-manipulation functions. + * The NULL byte is on the second byte so that off-by-ones can + * still be detected. Endianness is taken care of + * automatically. */ + ((char *)&__stack_chk_guard)[1] = 0; +#endif + __pthread_self()->canary = __stack_chk_guard; } diff --git a/lib/libc/wasi/libc-top-half/musl/src/errno/__strerror.h b/lib/libc/wasi/libc-top-half/musl/src/errno/__strerror.h index db0660d2ee..bd6dccbe2d 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/errno/__strerror.h +++ b/lib/libc/wasi/libc-top-half/musl/src/errno/__strerror.h @@ -124,6 +124,12 @@ E(ENOMEDIUM, "No medium found") E(EMEDIUMTYPE, "Wrong medium type") #endif E(EMULTIHOP, "Multihop attempted") +#ifdef __wasilibc_unmodified_upstream // errno value not in WASI +E(ENOKEY, "Required key not available") +E(EKEYEXPIRED, "Key has expired") +E(EKEYREVOKED, "Key has been revoked") +E(EKEYREJECTED, "Key was rejected by service") +#endif #ifdef __wasilibc_unmodified_upstream // errno value in WASI and not musl #else // WASI adds this errno code. diff --git a/lib/libc/wasi/libc-top-half/musl/src/fenv/powerpc/fenv-sf.c b/lib/libc/wasi/libc-top-half/musl/src/fenv/powerpc/fenv-sf.c index 85bef40f10..d4248f26f7 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/fenv/powerpc/fenv-sf.c +++ b/lib/libc/wasi/libc-top-half/musl/src/fenv/powerpc/fenv-sf.c @@ -1,3 +1,3 @@ -#ifdef _SOFT_FLOAT +#if defined(_SOFT_FLOAT) || defined(__NO_FPRS__) #include "../fenv.c" #endif diff --git a/lib/libc/wasi/libc-top-half/musl/src/fenv/powerpc/fenv.S b/lib/libc/wasi/libc-top-half/musl/src/fenv/powerpc/fenv.S index 22cea216a0..55055d0b3a 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/fenv/powerpc/fenv.S +++ b/lib/libc/wasi/libc-top-half/musl/src/fenv/powerpc/fenv.S @@ -1,4 +1,4 @@ -#ifndef _SOFT_FLOAT +#if !defined(_SOFT_FLOAT) && !defined(__NO_FPRS__) .global feclearexcept .type feclearexcept,@function feclearexcept: diff --git a/lib/libc/wasi/libc-top-half/musl/src/include/stdlib.h b/lib/libc/wasi/libc-top-half/musl/src/include/stdlib.h index e9da20158c..812b04de2f 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/include/stdlib.h +++ b/lib/libc/wasi/libc-top-half/musl/src/include/stdlib.h @@ -8,6 +8,7 @@ hidden void __env_rm_add(char *, char *); hidden int __mkostemps(char *, int, int); hidden int __ptsname_r(int, char *, size_t); hidden char *__randname(char *); +hidden void __qsort_r (void *, size_t, size_t, int (*)(const void *, const void *, void *), void *); hidden void *__libc_malloc(size_t); hidden void *__libc_malloc_impl(size_t); diff --git a/lib/libc/wasi/libc-top-half/musl/src/ldso/dl_iterate_phdr.c b/lib/libc/wasi/libc-top-half/musl/src/ldso/dl_iterate_phdr.c index 86c87ef835..9546dd3609 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/ldso/dl_iterate_phdr.c +++ b/lib/libc/wasi/libc-top-half/musl/src/ldso/dl_iterate_phdr.c @@ -1,5 +1,6 @@ #include #include +#include "pthread_impl.h" #include "libc.h" #define AUX_CNT 38 @@ -35,7 +36,7 @@ static int static_dl_iterate_phdr(int(*callback)(struct dl_phdr_info *info, size info.dlpi_subs = 0; if (tls_phdr) { info.dlpi_tls_modid = 1; - info.dlpi_tls_data = (void *)(base + tls_phdr->p_vaddr); + info.dlpi_tls_data = __tls_get_addr((tls_mod_off_t[]){1,0}); } else { info.dlpi_tls_modid = 0; info.dlpi_tls_data = 0; diff --git a/lib/libc/wasi/libc-top-half/musl/src/legacy/cuserid.c b/lib/libc/wasi/libc-top-half/musl/src/legacy/cuserid.c index 4e78798ded..dcaf73d4e6 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/legacy/cuserid.c +++ b/lib/libc/wasi/libc-top-half/musl/src/legacy/cuserid.c @@ -2,13 +2,21 @@ #include #include #include +#include char *cuserid(char *buf) { + static char usridbuf[L_cuserid]; struct passwd pw, *ppw; long pwb[256]; - if (getpwuid_r(geteuid(), &pw, (void *)pwb, sizeof pwb, &ppw)) - return 0; - snprintf(buf, L_cuserid, "%s", pw.pw_name); + if (buf) *buf = 0; + getpwuid_r(geteuid(), &pw, (void *)pwb, sizeof pwb, &ppw); + if (!ppw) + return buf; + size_t len = strnlen(pw.pw_name, L_cuserid); + if (len == L_cuserid) + return buf; + if (!buf) buf = usridbuf; + memcpy(buf, pw.pw_name, len+1); return buf; } diff --git a/lib/libc/wasi/libc-top-half/musl/src/linux/epoll.c b/lib/libc/wasi/libc-top-half/musl/src/linux/epoll.c index deff5b101a..93baa8147e 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/linux/epoll.c +++ b/lib/libc/wasi/libc-top-half/musl/src/linux/epoll.c @@ -24,9 +24,9 @@ int epoll_ctl(int fd, int op, int fd2, struct epoll_event *ev) int epoll_pwait(int fd, struct epoll_event *ev, int cnt, int to, const sigset_t *sigs) { - int r = __syscall(SYS_epoll_pwait, fd, ev, cnt, to, sigs, _NSIG/8); + int r = __syscall_cp(SYS_epoll_pwait, fd, ev, cnt, to, sigs, _NSIG/8); #ifdef SYS_epoll_wait - if (r==-ENOSYS && !sigs) r = __syscall(SYS_epoll_wait, fd, ev, cnt, to); + if (r==-ENOSYS && !sigs) r = __syscall_cp(SYS_epoll_wait, fd, ev, cnt, to); #endif return __syscall_ret(r); } diff --git a/lib/libc/wasi/libc-top-half/musl/src/locale/dcngettext.c b/lib/libc/wasi/libc-top-half/musl/src/locale/dcngettext.c index d1e6c6d13a..0b53286db7 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/locale/dcngettext.c +++ b/lib/libc/wasi/libc-top-half/musl/src/locale/dcngettext.c @@ -132,6 +132,9 @@ char *dcngettext(const char *domainname, const char *msgid1, const char *msgid2, struct binding *q; int old_errno = errno; + /* match gnu gettext behaviour */ + if (!msgid1) goto notrans; + if ((unsigned)category >= LC_ALL) goto notrans; if (!domainname) domainname = __gettextdomain(); diff --git a/lib/libc/wasi/libc-top-half/musl/src/locale/duplocale.c b/lib/libc/wasi/libc-top-half/musl/src/locale/duplocale.c index 030b64cb0e..5ce33ae6de 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/locale/duplocale.c +++ b/lib/libc/wasi/libc-top-half/musl/src/locale/duplocale.c @@ -3,6 +3,11 @@ #include "locale_impl.h" #include "libc.h" +#define malloc __libc_malloc +#define calloc undef +#define realloc undef +#define free undef + locale_t __duplocale(locale_t old) { locale_t new = malloc(sizeof *new); diff --git a/lib/libc/wasi/libc-top-half/musl/src/locale/strtod_l.c b/lib/libc/wasi/libc-top-half/musl/src/locale/strtod_l.c new file mode 100644 index 0000000000..574ba148e0 --- /dev/null +++ b/lib/libc/wasi/libc-top-half/musl/src/locale/strtod_l.c @@ -0,0 +1,22 @@ +#define _GNU_SOURCE +#include +#include + +float strtof_l(const char *restrict s, char **restrict p, locale_t l) +{ + return strtof(s, p); +} + +double strtod_l(const char *restrict s, char **restrict p, locale_t l) +{ + return strtod(s, p); +} + +long double strtold_l(const char *restrict s, char **restrict p, locale_t l) +{ + return strtold(s, p); +} + +weak_alias(strtof_l, __strtof_l); +weak_alias(strtod_l, __strtod_l); +weak_alias(strtold_l, __strtold_l); diff --git a/lib/libc/wasi/libc-top-half/musl/src/malloc/free.c b/lib/libc/wasi/libc-top-half/musl/src/malloc/free.c index f17a952cb4..3944f7b28f 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/malloc/free.c +++ b/lib/libc/wasi/libc-top-half/musl/src/malloc/free.c @@ -2,5 +2,5 @@ void free(void *p) { - return __libc_free(p); + __libc_free(p); } diff --git a/lib/libc/wasi/libc-top-half/musl/src/malloc/mallocng/aligned_alloc.c b/lib/libc/wasi/libc-top-half/musl/src/malloc/mallocng/aligned_alloc.c index 3411689600..e0862a83ae 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/malloc/mallocng/aligned_alloc.c +++ b/lib/libc/wasi/libc-top-half/musl/src/malloc/mallocng/aligned_alloc.c @@ -22,6 +22,9 @@ void *aligned_alloc(size_t align, size_t len) if (align <= UNIT) align = UNIT; unsigned char *p = malloc(len + align - UNIT); + if (!p) + return 0; + struct meta *g = get_meta(p); int idx = get_slot_index(p); size_t stride = get_stride(g); diff --git a/lib/libc/wasi/libc-top-half/musl/src/malloc/mallocng/free.c b/lib/libc/wasi/libc-top-half/musl/src/malloc/mallocng/free.c index 40745f97da..418a085c18 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/malloc/mallocng/free.c +++ b/lib/libc/wasi/libc-top-half/musl/src/malloc/mallocng/free.c @@ -119,7 +119,11 @@ void free(void *p) if (((uintptr_t)(start-1) ^ (uintptr_t)end) >= 2*PGSZ && g->last_idx) { unsigned char *base = start + (-(uintptr_t)start & (PGSZ-1)); size_t len = (end-base) & -PGSZ; - if (len) madvise(base, len, MADV_FREE); + if (len) { + int e = errno; + madvise(base, len, MADV_FREE); + errno = e; + } } // atomic free without locking if this is neither first or last slot @@ -139,5 +143,9 @@ void free(void *p) wrlock(); struct mapinfo mi = nontrivial_free(g, idx); unlock(); - if (mi.len) munmap(mi.base, mi.len); + if (mi.len) { + int e = errno; + munmap(mi.base, mi.len); + errno = e; + } } diff --git a/lib/libc/wasi/libc-top-half/musl/src/malloc/oldmalloc/malloc.c b/lib/libc/wasi/libc-top-half/musl/src/malloc/oldmalloc/malloc.c index 53f5f959ec..25d00d44de 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/malloc/oldmalloc/malloc.c +++ b/lib/libc/wasi/libc-top-half/musl/src/malloc/oldmalloc/malloc.c @@ -11,7 +11,7 @@ #include "malloc_impl.h" #include "fork_impl.h" -#define malloc __libc_malloc +#define malloc __libc_malloc_impl #define realloc __libc_realloc #define free __libc_free @@ -481,12 +481,14 @@ void __bin_chunk(struct chunk *self) if (size > RECLAIM && (size^(size-osize)) > size-osize) { uintptr_t a = (uintptr_t)self + SIZE_ALIGN+PAGE_SIZE-1 & -PAGE_SIZE; uintptr_t b = (uintptr_t)next - SIZE_ALIGN & -PAGE_SIZE; + int e = errno; #if 1 __madvise((void *)a, b-a, MADV_DONTNEED); #else __mmap((void *)a, b-a, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0); #endif + errno = e; } unlock_bin(i); @@ -499,7 +501,9 @@ static void unmap_chunk(struct chunk *self) size_t len = CHUNK_SIZE(self) + extra; /* Crash on double free */ if (extra & 1) a_crash(); + int e = errno; __munmap(base, len); + errno = e; } void free(void *p) diff --git a/lib/libc/wasi/libc-top-half/musl/src/math/acoshf.c b/lib/libc/wasi/libc-top-half/musl/src/math/acoshf.c index 8a4ec4d57e..b773d48e2b 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/math/acoshf.c +++ b/lib/libc/wasi/libc-top-half/musl/src/math/acoshf.c @@ -15,12 +15,12 @@ float acoshf(float x) uint32_t a = u.i & 0x7fffffff; if (a < 0x3f800000+(1<<23)) - /* |x| < 2, invalid if x < 1 or nan */ + /* |x| < 2, invalid if x < 1 */ /* up to 2ulp error in [1,1.125] */ return log1pf(x-1 + sqrtf((x-1)*(x-1)+2*(x-1))); - if (a < 0x3f800000+(12<<23)) - /* |x| < 0x1p12 */ + if (u.i < 0x3f800000+(12<<23)) + /* 2 <= x < 0x1p12 */ return logf(2*x - 1/(x+sqrtf(x*x-1))); - /* x >= 0x1p12 */ + /* x >= 0x1p12 or x <= -2 or nan */ return logf(x) + 0.693147180559945309417232121458176568f; } diff --git a/lib/libc/wasi/libc-top-half/musl/src/math/expm1f.c b/lib/libc/wasi/libc-top-half/musl/src/math/expm1f.c index 297e0b44a2..09a41afe7d 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/math/expm1f.c +++ b/lib/libc/wasi/libc-top-half/musl/src/math/expm1f.c @@ -16,7 +16,6 @@ #include "libm.h" static const float -o_threshold = 8.8721679688e+01, /* 0x42b17180 */ ln2_hi = 6.9313812256e-01, /* 0x3f317180 */ ln2_lo = 9.0580006145e-06, /* 0x3717f7d1 */ invln2 = 1.4426950216e+00, /* 0x3fb8aa3b */ @@ -41,7 +40,7 @@ float expm1f(float x) return x; if (sign) return -1; - if (x > o_threshold) { + if (hx > 0x42b17217) { /* x > log(FLT_MAX) */ x *= 0x1p127f; return x; } diff --git a/lib/libc/wasi/libc-top-half/musl/src/math/fmaf.c b/lib/libc/wasi/libc-top-half/musl/src/math/fmaf.c index 111c0aec98..7c65acf1fc 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/math/fmaf.c +++ b/lib/libc/wasi/libc-top-half/musl/src/math/fmaf.c @@ -77,21 +77,16 @@ float fmaf(float x, float y, float z) * If result is inexact, and exactly halfway between two float values, * we need to adjust the low-order bit in the direction of the error. */ -#ifdef FE_TOWARDZERO - fesetround(FE_TOWARDZERO); -#endif -#ifdef __wasilibc_unmodified_upstream // WASI doesn't need old GCC workarounds - volatile double vxy = xy; /* XXX work around gcc CSE bug */ -#else - double vxy = xy; -#endif - double adjusted_result = vxy + z; - fesetround(FE_TONEAREST); - if (result == adjusted_result) { - u.f = adjusted_result; + double err; + int neg = u.i >> 63; + if (neg == (z > xy)) + err = xy - result + z; + else + err = z - result + xy; + if (neg == (err < 0)) u.i++; - adjusted_result = u.f; - } - z = adjusted_result; + else + u.i--; + z = u.f; return z; } diff --git a/lib/libc/wasi/libc-top-half/musl/src/math/powerpc/fabs.c b/lib/libc/wasi/libc-top-half/musl/src/math/powerpc/fabs.c index 0efc21ef83..9453a3aa98 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/math/powerpc/fabs.c +++ b/lib/libc/wasi/libc-top-half/musl/src/math/powerpc/fabs.c @@ -1,6 +1,6 @@ #include -#if defined(_SOFT_FLOAT) || defined(BROKEN_PPC_D_ASM) +#if defined(_SOFT_FLOAT) || defined(__NO_FPRS__) || defined(BROKEN_PPC_D_ASM) #include "../fabs.c" diff --git a/lib/libc/wasi/libc-top-half/musl/src/math/powerpc/fabsf.c b/lib/libc/wasi/libc-top-half/musl/src/math/powerpc/fabsf.c index d88b5911c0..2e9da588dd 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/math/powerpc/fabsf.c +++ b/lib/libc/wasi/libc-top-half/musl/src/math/powerpc/fabsf.c @@ -1,6 +1,6 @@ #include -#ifdef _SOFT_FLOAT +#if defined(_SOFT_FLOAT) || defined(__NO_FPRS__) #include "../fabsf.c" diff --git a/lib/libc/wasi/libc-top-half/musl/src/math/powerpc/fma.c b/lib/libc/wasi/libc-top-half/musl/src/math/powerpc/fma.c index 135c990357..0eb2ba1ef5 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/math/powerpc/fma.c +++ b/lib/libc/wasi/libc-top-half/musl/src/math/powerpc/fma.c @@ -1,6 +1,6 @@ #include -#if defined(_SOFT_FLOAT) || defined(BROKEN_PPC_D_ASM) +#if defined(_SOFT_FLOAT) || defined(__NO_FPRS__) || defined(BROKEN_PPC_D_ASM) #include "../fma.c" diff --git a/lib/libc/wasi/libc-top-half/musl/src/math/powerpc/fmaf.c b/lib/libc/wasi/libc-top-half/musl/src/math/powerpc/fmaf.c index a99a2a3ba1..dc1a749d98 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/math/powerpc/fmaf.c +++ b/lib/libc/wasi/libc-top-half/musl/src/math/powerpc/fmaf.c @@ -1,6 +1,6 @@ #include -#ifdef _SOFT_FLOAT +#if defined(_SOFT_FLOAT) || defined(__NO_FPRS__) #include "../fmaf.c" diff --git a/lib/libc/wasi/libc-top-half/musl/src/misc/ioctl.c b/lib/libc/wasi/libc-top-half/musl/src/misc/ioctl.c index 492828119a..35804f026e 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/misc/ioctl.c +++ b/lib/libc/wasi/libc-top-half/musl/src/misc/ioctl.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "syscall.h" #define alignof(t) offsetof(struct { char c; t x; }, x) @@ -53,7 +54,7 @@ static const struct ioctl_compat_map compat_map[] = { { _IOWR('A', 0x23, char[136]), _IOWR('A', 0x23, char[132]), 0, WR, 1, 0 }, { 0, 0, 4, WR, 1, 0 }, /* snd_pcm_sync_ptr (flags only) */ { 0, 0, 32, WR, 1, OFFS(8,12,16,24,28) }, /* snd_pcm_mmap_status */ - { 0, 0, 8, WR, 1, OFFS(0,4) }, /* snd_pcm_mmap_control */ + { 0, 0, 4, WR, 1, 0 }, /* snd_pcm_mmap_control (each member) */ /* VIDIOC_QUERYBUF, VIDIOC_QBUF, VIDIOC_DQBUF, VIDIOC_PREPARE_BUF */ { _IOWR('V', 9, new_misaligned(68)), _IOWR('V', 9, char[68]), 68, WR, 1, OFFS(20, 24) }, @@ -90,7 +91,11 @@ static void convert_ioctl_struct(const struct ioctl_compat_map *map, char *old, * if another exception appears this needs changing. */ convert_ioctl_struct(map+1, old, new, dir); convert_ioctl_struct(map+2, old+4, new+8, dir); - convert_ioctl_struct(map+3, old+68, new+72, dir); + /* snd_pcm_mmap_control, special-cased due to kernel + * type definition having been botched. */ + int adj = BYTE_ORDER==BIG_ENDIAN ? 4 : 0; + convert_ioctl_struct(map+3, old+68, new+72+adj, dir); + convert_ioctl_struct(map+3, old+72, new+76+3*adj, dir); return; } for (int i=0; i < map->noffs; i++) { diff --git a/lib/libc/wasi/libc-top-half/musl/src/passwd/nscd_query.c b/lib/libc/wasi/libc-top-half/musl/src/passwd/nscd_query.c index d38e371bcd..dc3406b851 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/passwd/nscd_query.c +++ b/lib/libc/wasi/libc-top-half/musl/src/passwd/nscd_query.c @@ -40,7 +40,15 @@ retry: buf[0] = NSCDVERSION; fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - if (fd < 0) return NULL; + if (fd < 0) { + if (errno == EAFNOSUPPORT) { + f = fopen("/dev/null", "re"); + if (f) + errno = errno_save; + return f; + } + return 0; + } if(!(f = fdopen(fd, "r"))) { close(fd); diff --git a/lib/libc/wasi/libc-top-half/musl/src/process/fdop.h b/lib/libc/wasi/libc-top-half/musl/src/process/fdop.h index 5adf144387..7cf733b21d 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/process/fdop.h +++ b/lib/libc/wasi/libc-top-half/musl/src/process/fdop.h @@ -10,3 +10,8 @@ struct fdop { mode_t mode; char path[]; }; + +#define malloc __libc_malloc +#define calloc __libc_calloc +#define realloc undef +#define free __libc_free diff --git a/lib/libc/wasi/libc-top-half/musl/src/process/posix_spawn_file_actions_addclose.c b/lib/libc/wasi/libc-top-half/musl/src/process/posix_spawn_file_actions_addclose.c index cdda597991..0c2ef8fa37 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/process/posix_spawn_file_actions_addclose.c +++ b/lib/libc/wasi/libc-top-half/musl/src/process/posix_spawn_file_actions_addclose.c @@ -5,6 +5,7 @@ int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *fa, int fd) { + if (fd < 0) return EBADF; struct fdop *op = malloc(sizeof *op); if (!op) return ENOMEM; op->cmd = FDOP_CLOSE; diff --git a/lib/libc/wasi/libc-top-half/musl/src/process/posix_spawn_file_actions_adddup2.c b/lib/libc/wasi/libc-top-half/musl/src/process/posix_spawn_file_actions_adddup2.c index 0367498fd4..addca4d4f0 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/process/posix_spawn_file_actions_adddup2.c +++ b/lib/libc/wasi/libc-top-half/musl/src/process/posix_spawn_file_actions_adddup2.c @@ -5,6 +5,7 @@ int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa, int srcfd, int fd) { + if (srcfd < 0 || fd < 0) return EBADF; struct fdop *op = malloc(sizeof *op); if (!op) return ENOMEM; op->cmd = FDOP_DUP2; diff --git a/lib/libc/wasi/libc-top-half/musl/src/process/posix_spawn_file_actions_addfchdir.c b/lib/libc/wasi/libc-top-half/musl/src/process/posix_spawn_file_actions_addfchdir.c index 436c683d25..e89ede8c3c 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/process/posix_spawn_file_actions_addfchdir.c +++ b/lib/libc/wasi/libc-top-half/musl/src/process/posix_spawn_file_actions_addfchdir.c @@ -6,6 +6,7 @@ int posix_spawn_file_actions_addfchdir_np(posix_spawn_file_actions_t *fa, int fd) { + if (fd < 0) return EBADF; struct fdop *op = malloc(sizeof *op); if (!op) return ENOMEM; op->cmd = FDOP_FCHDIR; diff --git a/lib/libc/wasi/libc-top-half/musl/src/process/posix_spawn_file_actions_addopen.c b/lib/libc/wasi/libc-top-half/musl/src/process/posix_spawn_file_actions_addopen.c index 368922c76b..82bbcec9eb 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/process/posix_spawn_file_actions_addopen.c +++ b/lib/libc/wasi/libc-top-half/musl/src/process/posix_spawn_file_actions_addopen.c @@ -6,6 +6,7 @@ int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *restrict fa, int fd, const char *restrict path, int flags, mode_t mode) { + if (fd < 0) return EBADF; struct fdop *op = malloc(sizeof *op + strlen(path) + 1); if (!op) return ENOMEM; op->cmd = FDOP_OPEN; diff --git a/lib/libc/wasi/libc-top-half/musl/src/setjmp/powerpc/longjmp.S b/lib/libc/wasi/libc-top-half/musl/src/setjmp/powerpc/longjmp.S index e598bd056e..611389fed9 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/setjmp/powerpc/longjmp.S +++ b/lib/libc/wasi/libc-top-half/musl/src/setjmp/powerpc/longjmp.S @@ -37,7 +37,37 @@ longjmp: lwz 29, 72(3) lwz 30, 76(3) lwz 31, 80(3) -#ifndef _SOFT_FLOAT +#if defined(_SOFT_FLOAT) || defined(__NO_FPRS__) + mflr 0 + bl 1f + .hidden __hwcap + .long __hwcap-. +1: mflr 4 + lwz 5, 0(4) + lwzx 4, 4, 5 + andis. 4, 4, 0x80 + beq 1f + .long 0x11c35b01 /* evldd 14,88(3) */ + .long 0x11e36301 /* ... */ + .long 0x12036b01 + .long 0x12237301 + .long 0x12437b01 + .long 0x12638301 + .long 0x12838b01 + .long 0x12a39301 + .long 0x12c39b01 + .long 0x12e3a301 + .long 0x1303ab01 + .long 0x1323b301 + .long 0x1343bb01 + .long 0x1363c301 + .long 0x1383cb01 + .long 0x13a3d301 + .long 0x13c3db01 + .long 0x13e3e301 /* evldd 31,224(3) */ + .long 0x11a3eb01 /* evldd 13,232(3) */ +1: mtlr 0 +#else lfd 14,88(3) lfd 15,96(3) lfd 16,104(3) diff --git a/lib/libc/wasi/libc-top-half/musl/src/setjmp/powerpc/setjmp.S b/lib/libc/wasi/libc-top-half/musl/src/setjmp/powerpc/setjmp.S index cd91a207f5..f1fcce339e 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/setjmp/powerpc/setjmp.S +++ b/lib/libc/wasi/libc-top-half/musl/src/setjmp/powerpc/setjmp.S @@ -37,7 +37,37 @@ setjmp: stw 29, 72(3) stw 30, 76(3) stw 31, 80(3) -#ifndef _SOFT_FLOAT +#if defined(_SOFT_FLOAT) || defined(__NO_FPRS__) + mflr 0 + bl 1f + .hidden __hwcap + .long __hwcap-. +1: mflr 4 + lwz 5, 0(4) + lwzx 4, 4, 5 + andis. 4, 4, 0x80 + beq 1f + .long 0x11c35b21 /* evstdd 14,88(3) */ + .long 0x11e36321 /* ... */ + .long 0x12036b21 + .long 0x12237321 + .long 0x12437b21 + .long 0x12638321 + .long 0x12838b21 + .long 0x12a39321 + .long 0x12c39b21 + .long 0x12e3a321 + .long 0x1303ab21 + .long 0x1323b321 + .long 0x1343bb21 + .long 0x1363c321 + .long 0x1383cb21 + .long 0x13a3d321 + .long 0x13c3db21 + .long 0x13e3e321 /* evstdd 31,224(3) */ + .long 0x11a3eb21 /* evstdd 13,232(3) */ +1: mtlr 0 +#else stfd 14,88(3) stfd 15,96(3) stfd 16,104(3) diff --git a/lib/libc/wasi/libc-top-half/musl/src/signal/block.c b/lib/libc/wasi/libc-top-half/musl/src/signal/block.c index d7f6100134..cc8698f0bb 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/signal/block.c +++ b/lib/libc/wasi/libc-top-half/musl/src/signal/block.c @@ -3,9 +3,9 @@ #include static const unsigned long all_mask[] = { -#if ULONG_MAX == 0xffffffff && _NSIG == 129 +#if ULONG_MAX == 0xffffffff && _NSIG > 65 -1UL, -1UL, -1UL, -1UL -#elif ULONG_MAX == 0xffffffff +#elif ULONG_MAX == 0xffffffff || _NSIG > 65 -1UL, -1UL #else -1UL diff --git a/lib/libc/wasi/libc-top-half/musl/src/stat/futimesat.c b/lib/libc/wasi/libc-top-half/musl/src/stat/futimesat.c index 4bdb1c2965..c29b2b790f 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/stat/futimesat.c +++ b/lib/libc/wasi/libc-top-half/musl/src/stat/futimesat.c @@ -2,7 +2,9 @@ #include #include #include +#ifdef __wasilibc_unmodified_upstream // WASI has no syscall #include "syscall.h" +#endif int __futimesat(int dirfd, const char *pathname, const struct timeval times[2]) { @@ -10,8 +12,15 @@ int __futimesat(int dirfd, const char *pathname, const struct timeval times[2]) if (times) { int i; for (i=0; i<2; i++) { +#ifdef __wasilibc_unmodified_upstream // WASI has no syscall if (times[i].tv_usec >= 1000000ULL) return __syscall_ret(-EINVAL); +#else + if (times[i].tv_usec >= 1000000ULL) { + errno = EINVAL; + return -1; + } +#endif ts[i].tv_sec = times[i].tv_sec; ts[i].tv_nsec = times[i].tv_usec * 1000; } diff --git a/lib/libc/wasi/libc-top-half/musl/src/stdio/fgetws.c b/lib/libc/wasi/libc-top-half/musl/src/stdio/fgetws.c index b08b30491a..195cb4355a 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/stdio/fgetws.c +++ b/lib/libc/wasi/libc-top-half/musl/src/stdio/fgetws.c @@ -1,6 +1,5 @@ #include "stdio_impl.h" #include -#include wint_t __fgetwc_unlocked(FILE *); @@ -12,10 +11,6 @@ wchar_t *fgetws(wchar_t *restrict s, int n, FILE *restrict f) FLOCK(f); - /* Setup a dummy errno so we can detect EILSEQ. This is - * the only way to catch encoding errors in the form of a - * partial character just before EOF. */ - errno = EAGAIN; for (; n; n--) { wint_t c = __fgetwc_unlocked(f); if (c == WEOF) break; @@ -23,7 +18,7 @@ wchar_t *fgetws(wchar_t *restrict s, int n, FILE *restrict f) if (c == '\n') break; } *p = 0; - if (ferror(f) || errno==EILSEQ) p = s; + if (ferror(f)) p = s; FUNLOCK(f); diff --git a/lib/libc/wasi/libc-top-half/musl/src/stdio/fseek.c b/lib/libc/wasi/libc-top-half/musl/src/stdio/fseek.c index 439308f757..c07f7e9526 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/stdio/fseek.c +++ b/lib/libc/wasi/libc-top-half/musl/src/stdio/fseek.c @@ -1,7 +1,14 @@ #include "stdio_impl.h" +#include int __fseeko_unlocked(FILE *f, off_t off, int whence) { + /* Fail immediately for invalid whence argument. */ + if (whence != SEEK_CUR && whence != SEEK_SET && whence != SEEK_END) { + errno = EINVAL; + return -1; + } + /* Adjust relative offset for unread data in buffer, if any. */ if (whence == SEEK_CUR && f->rend) off -= f->rend - f->rpos; diff --git a/lib/libc/wasi/libc-top-half/musl/src/stdio/getdelim.c b/lib/libc/wasi/libc-top-half/musl/src/stdio/getdelim.c index d2f5b15ab1..df114441c7 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/stdio/getdelim.c +++ b/lib/libc/wasi/libc-top-half/musl/src/stdio/getdelim.c @@ -55,9 +55,11 @@ ssize_t getdelim(char **restrict s, size_t *restrict n, int delim, FILE *restric *s = tmp; *n = m; } - memcpy(*s+i, f->rpos, k); - f->rpos += k; - i += k; + if (k) { + memcpy(*s+i, f->rpos, k); + f->rpos += k; + i += k; + } if (z) break; if ((c = getc_unlocked(f)) == EOF) { if (!i || !feof(f)) { diff --git a/lib/libc/wasi/libc-top-half/musl/src/stdio/popen.c b/lib/libc/wasi/libc-top-half/musl/src/stdio/popen.c index 92cb57ee93..3ec833941c 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/stdio/popen.c +++ b/lib/libc/wasi/libc-top-half/musl/src/stdio/popen.c @@ -31,25 +31,12 @@ FILE *popen(const char *cmd, const char *mode) __syscall(SYS_close, p[1]); return NULL; } - FLOCK(f); - - /* If the child's end of the pipe happens to already be on the final - * fd number to which it will be assigned (either 0 or 1), it must - * be moved to a different fd. Otherwise, there is no safe way to - * remove the close-on-exec flag in the child without also creating - * a file descriptor leak race condition in the parent. */ - if (p[1-op] == 1-op) { - int tmp = fcntl(1-op, F_DUPFD_CLOEXEC, 0); - if (tmp < 0) { - e = errno; - goto fail; - } - __syscall(SYS_close, p[1-op]); - p[1-op] = tmp; - } e = ENOMEM; if (!posix_spawn_file_actions_init(&fa)) { + for (FILE *l = *__ofl_lock(); l; l=l->next) + if (l->pipe_pid && posix_spawn_file_actions_addclose(&fa, l->fd)) + goto fail; if (!posix_spawn_file_actions_adddup2(&fa, p[1-op], 1-op)) { if (!(e = posix_spawn(&pid, "/bin/sh", &fa, 0, (char *[]){ "sh", "-c", (char *)cmd, 0 }, __environ))) { @@ -58,13 +45,14 @@ FILE *popen(const char *cmd, const char *mode) if (!strchr(mode, 'e')) fcntl(p[op], F_SETFD, 0); __syscall(SYS_close, p[1-op]); - FUNLOCK(f); + __ofl_unlock(); return f; } } +fail: + __ofl_unlock(); posix_spawn_file_actions_destroy(&fa); } -fail: fclose(f); __syscall(SYS_close, p[1-op]); diff --git a/lib/libc/wasi/libc-top-half/musl/src/stdlib/qsort.c b/lib/libc/wasi/libc-top-half/musl/src/stdlib/qsort.c index da58fd3177..314ddc29da 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/stdlib/qsort.c +++ b/lib/libc/wasi/libc-top-half/musl/src/stdlib/qsort.c @@ -24,6 +24,7 @@ /* Smoothsort, an adaptive variant of Heapsort. Memory usage: O(1). Run time: Worst case O(n log n), close to O(n) in the mostly-sorted case. */ +#define _BSD_SOURCE #include #include #include @@ -31,7 +32,7 @@ #include "atomic.h" #define ntz(x) a_ctz_l((x)) -typedef int (*cmpfun)(const void *, const void *); +typedef int (*cmpfun)(const void *, const void *, void *); static inline int pntz(size_t p[2]) { int r = ntz(p[0] - 1); @@ -88,7 +89,7 @@ static inline void shr(size_t p[2], int n) p[1] >>= n; } -static void sift(unsigned char *head, size_t width, cmpfun cmp, int pshift, size_t lp[]) +static void sift(unsigned char *head, size_t width, cmpfun cmp, void *arg, int pshift, size_t lp[]) { unsigned char *rt, *lf; unsigned char *ar[14 * sizeof(size_t) + 1]; @@ -99,10 +100,10 @@ static void sift(unsigned char *head, size_t width, cmpfun cmp, int pshift, size rt = head - width; lf = head - width - lp[pshift - 2]; - if((*cmp)(ar[0], lf) >= 0 && (*cmp)(ar[0], rt) >= 0) { + if(cmp(ar[0], lf, arg) >= 0 && cmp(ar[0], rt, arg) >= 0) { break; } - if((*cmp)(lf, rt) >= 0) { + if(cmp(lf, rt, arg) >= 0) { ar[i++] = lf; head = lf; pshift -= 1; @@ -115,7 +116,7 @@ static void sift(unsigned char *head, size_t width, cmpfun cmp, int pshift, size cycle(width, ar, i); } -static void trinkle(unsigned char *head, size_t width, cmpfun cmp, size_t pp[2], int pshift, int trusty, size_t lp[]) +static void trinkle(unsigned char *head, size_t width, cmpfun cmp, void *arg, size_t pp[2], int pshift, int trusty, size_t lp[]) { unsigned char *stepson, *rt, *lf; @@ -130,13 +131,13 @@ static void trinkle(unsigned char *head, size_t width, cmpfun cmp, size_t pp[2], ar[0] = head; while(p[0] != 1 || p[1] != 0) { stepson = head - lp[pshift]; - if((*cmp)(stepson, ar[0]) <= 0) { + if(cmp(stepson, ar[0], arg) <= 0) { break; } if(!trusty && pshift > 1) { rt = head - width; lf = head - width - lp[pshift - 2]; - if((*cmp)(rt, stepson) >= 0 || (*cmp)(lf, stepson) >= 0) { + if(cmp(rt, stepson, arg) >= 0 || cmp(lf, stepson, arg) >= 0) { break; } } @@ -150,11 +151,11 @@ static void trinkle(unsigned char *head, size_t width, cmpfun cmp, size_t pp[2], } if(!trusty) { cycle(width, ar, i); - sift(head, width, cmp, pshift, lp); + sift(head, width, cmp, arg, pshift, lp); } } -void qsort(void *base, size_t nel, size_t width, cmpfun cmp) +void __qsort_r(void *base, size_t nel, size_t width, cmpfun cmp, void *arg) { size_t lp[12*sizeof(size_t)]; size_t i, size = width * nel; @@ -173,16 +174,16 @@ void qsort(void *base, size_t nel, size_t width, cmpfun cmp) while(head < high) { if((p[0] & 3) == 3) { - sift(head, width, cmp, pshift, lp); + sift(head, width, cmp, arg, pshift, lp); shr(p, 2); pshift += 2; } else { if(lp[pshift - 1] >= high - head) { - trinkle(head, width, cmp, p, pshift, 0, lp); + trinkle(head, width, cmp, arg, p, pshift, 0, lp); } else { - sift(head, width, cmp, pshift, lp); + sift(head, width, cmp, arg, pshift, lp); } - + if(pshift == 1) { shl(p, 1); pshift = 0; @@ -191,12 +192,12 @@ void qsort(void *base, size_t nel, size_t width, cmpfun cmp) pshift = 1; } } - + p[0] |= 1; head += width; } - trinkle(head, width, cmp, p, pshift, 0, lp); + trinkle(head, width, cmp, arg, p, pshift, 0, lp); while(pshift != 1 || p[0] != 1 || p[1] != 0) { if(pshift <= 1) { @@ -208,11 +209,13 @@ void qsort(void *base, size_t nel, size_t width, cmpfun cmp) pshift -= 2; p[0] ^= 7; shr(p, 1); - trinkle(head - lp[pshift] - width, width, cmp, p, pshift + 1, 1, lp); + trinkle(head - lp[pshift] - width, width, cmp, arg, p, pshift + 1, 1, lp); shl(p, 1); p[0] |= 1; - trinkle(head - width, width, cmp, p, pshift, 1, lp); + trinkle(head - width, width, cmp, arg, p, pshift, 1, lp); } head -= width; } } + +weak_alias(__qsort_r, qsort_r); diff --git a/lib/libc/wasi/libc-top-half/musl/src/stdlib/qsort_nr.c b/lib/libc/wasi/libc-top-half/musl/src/stdlib/qsort_nr.c new file mode 100644 index 0000000000..efe7ccecd1 --- /dev/null +++ b/lib/libc/wasi/libc-top-half/musl/src/stdlib/qsort_nr.c @@ -0,0 +1,14 @@ +#define _BSD_SOURCE +#include + +typedef int (*cmpfun)(const void *, const void *); + +static int wrapper_cmp(const void *v1, const void *v2, void *cmp) +{ + return ((cmpfun)cmp)(v1, v2); +} + +void qsort(void *base, size_t nel, size_t width, cmpfun cmp) +{ + __qsort_r(base, nel, width, wrapper_cmp, cmp); +} diff --git a/lib/libc/wasi/libc-top-half/musl/src/stdlib/strtod.c b/lib/libc/wasi/libc-top-half/musl/src/stdlib/strtod.c index 60158291df..184bca4629 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/stdlib/strtod.c +++ b/lib/libc/wasi/libc-top-half/musl/src/stdlib/strtod.c @@ -46,24 +46,3 @@ long double strtold(const char *restrict s, char **restrict p) return strtox(s, p, 2); #endif } - -#ifdef __wasilibc_unmodified_upstream // WASI doesn't support signature-changing aliases -weak_alias(strtof, strtof_l); -weak_alias(strtod, strtod_l); -weak_alias(strtold, strtold_l); -weak_alias(strtof, __strtof_l); -weak_alias(strtod, __strtod_l); -weak_alias(strtold, __strtold_l); -#else -// WebAssembly doesn't permit signature-changing aliases, so use wrapper -// functions instead. -weak float strtof_l(const char *restrict s, char **restrict p, locale_t loc) { - return strtof(s, p); -} -weak double strtod_l(const char *restrict s, char **restrict p, locale_t loc) { - return strtod(s, p); -} -weak long double strtold_l(const char *restrict s, char **restrict p, locale_t loc) { - return strtold(s, p); -} -#endif diff --git a/lib/libc/wasi/libc-top-half/musl/src/thread/pthread_getname_np.c b/lib/libc/wasi/libc-top-half/musl/src/thread/pthread_getname_np.c new file mode 100644 index 0000000000..85504e45dc --- /dev/null +++ b/lib/libc/wasi/libc-top-half/musl/src/thread/pthread_getname_np.c @@ -0,0 +1,25 @@ +#define _GNU_SOURCE +#include +#include +#include + +#include "pthread_impl.h" + +int pthread_getname_np(pthread_t thread, char *name, size_t len) +{ + int fd, cs, status = 0; + char f[sizeof "/proc/self/task//comm" + 3*sizeof(int)]; + + if (len < 16) return ERANGE; + + if (thread == pthread_self()) + return prctl(PR_GET_NAME, (unsigned long)name, 0UL, 0UL, 0UL) ? errno : 0; + + snprintf(f, sizeof f, "/proc/self/task/%d/comm", thread->tid); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); + if ((fd = open(f, O_RDONLY|O_CLOEXEC)) < 0 || (len = read(fd, name, len)) == -1) status = errno; + else name[len-1] = 0; /* remove trailing new line only if successful */ + if (fd >= 0) close(fd); + pthread_setcancelstate(cs, 0); + return status; +} diff --git a/lib/libc/wasi/libc-top-half/musl/src/thread/pthread_setname_np.c b/lib/libc/wasi/libc-top-half/musl/src/thread/pthread_setname_np.c index 82d35e17ed..fc2d230618 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/thread/pthread_setname_np.c +++ b/lib/libc/wasi/libc-top-half/musl/src/thread/pthread_setname_np.c @@ -19,7 +19,7 @@ int pthread_setname_np(pthread_t thread, const char *name) snprintf(f, sizeof f, "/proc/self/task/%d/comm", thread->tid); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); - if ((fd = open(f, O_WRONLY)) < 0 || write(fd, name, len) < 0) status = errno; + if ((fd = open(f, O_WRONLY|O_CLOEXEC)) < 0 || write(fd, name, len) < 0) status = errno; if (fd >= 0) close(fd); pthread_setcancelstate(cs, 0); return status; diff --git a/lib/libc/wasi/libc-top-half/musl/src/time/__tz.c b/lib/libc/wasi/libc-top-half/musl/src/time/__tz.c index 092d343df3..9551503448 100644 --- a/lib/libc/wasi/libc-top-half/musl/src/time/__tz.c +++ b/lib/libc/wasi/libc-top-half/musl/src/time/__tz.c @@ -5,6 +5,7 @@ #include #ifdef __wasilibc_unmodified_upstream // timezone data #include +#include #endif #include "libc.h" #include "lock.h" @@ -159,10 +160,21 @@ static void do_tzset() } if (old_tz) memcpy(old_tz, s, i+1); + int posix_form = 0; + if (*s != ':') { + p = s; + char dummy_name[TZNAME_MAX+1]; + getname(dummy_name, &p); + if (p!=s && (*p == '+' || *p == '-' || isdigit(*p) + || !strcmp(dummy_name, "UTC") + || !strcmp(dummy_name, "GMT"))) + posix_form = 1; + } + /* Non-suid can use an absolute tzfile pathname or a relative * pathame beginning with "."; in secure mode, only the * standard path will be searched. */ - if (*s == ':' || ((p=strchr(s, '/')) && !memchr(s, ',', p-s))) { + if (!posix_form) { if (*s == ':') s++; if (*s == '/' || *s == '.') { if (!libc.secure || !strcmp(s, "/etc/localtime")) @@ -286,22 +298,20 @@ static size_t scan_trans(long long t, int local, size_t *alt) n = (index-trans)>>scale; if (a == n-1) return -1; if (a == 0) { - x = zi_read32(trans + (a< +#include #include #include #include "syscall.h" @@ -12,5 +13,11 @@ int nice(int inc) prio += getpriority(PRIO_PROCESS, 0); if (prio > NZERO-1) prio = NZERO-1; if (prio < -NZERO) prio = -NZERO; - return setpriority(PRIO_PROCESS, 0, prio) ? -1 : prio; + if (setpriority(PRIO_PROCESS, 0, prio)) { + if (errno == EACCES) + errno = EPERM; + return -1; + } else { + return prio; + } } From 8caa20641787944651f7c6fe2f664e93c55d79ec Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Jun 2022 15:27:43 -0700 Subject: [PATCH 1838/2031] test-cases: fix race with `zig run` on C backend tests Also avoid redundantly doing compile-error checks on multiple targets for test cases where that is not helpful. --- src/test.zig | 7 ++++++- test/cases/compile_errors/dereference_anyopaque.zig | 1 + test/cases/compile_errors/invalid_array_elem_ty.zig | 3 ++- .../compile_errors/invalid_store_to_comptime_field.zig | 3 ++- .../compile_errors/runtime_indexing_comptime_array.zig | 3 ++- test/cases/compile_errors/stage2/comptime_unreachable.zig | 2 ++ 6 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/test.zig b/src/test.zig index f8b8ed8ce8..414a9d4d82 100644 --- a/src/test.zig +++ b/src/test.zig @@ -1808,6 +1808,11 @@ pub const TestContext = struct { // We wouldn't be able to run the compiled C code. return; // Pass test. } + // Use an absolute path here so that the unique directory name + // for this Case makes it into the cache hash, avoiding cache + // collisions from multiple threads doing `zig run` at the same + // time on the same test_case.c input filename. + const abs_exe_path = try tmp.dir.realpathAlloc(arena, bin_name); try argv.appendSlice(&[_][]const u8{ std.testing.zig_exe_path, "run", @@ -1818,7 +1823,7 @@ pub const TestContext = struct { "-Wno-incompatible-library-redeclaration", // https://github.com/ziglang/zig/issues/875 "--", "-lc", - exe_path, + abs_exe_path, }); } else switch (host.getExternalExecutor(target_info, .{ .link_libc = case.link_libc })) { .native => try argv.append(exe_path), diff --git a/test/cases/compile_errors/dereference_anyopaque.zig b/test/cases/compile_errors/dereference_anyopaque.zig index 68be3c97ed..e09f086016 100644 --- a/test/cases/compile_errors/dereference_anyopaque.zig +++ b/test/cases/compile_errors/dereference_anyopaque.zig @@ -41,6 +41,7 @@ pub export fn entry() void { } // error +// target=native // backend=llvm // // :11:22: error: comparison of 'void' with null diff --git a/test/cases/compile_errors/invalid_array_elem_ty.zig b/test/cases/compile_errors/invalid_array_elem_ty.zig index bfa79a104b..c7c2597246 100644 --- a/test/cases/compile_errors/invalid_array_elem_ty.zig +++ b/test/cases/compile_errors/invalid_array_elem_ty.zig @@ -6,6 +6,7 @@ pub export fn entry() void { } // error -// backend=stage2,llvm +// target=native +// backend=stage2 // // :4:1: error: expected type, found fn() type diff --git a/test/cases/compile_errors/invalid_store_to_comptime_field.zig b/test/cases/compile_errors/invalid_store_to_comptime_field.zig index 3bbd9bbaa8..48743f3ba1 100644 --- a/test/cases/compile_errors/invalid_store_to_comptime_field.zig +++ b/test/cases/compile_errors/invalid_store_to_comptime_field.zig @@ -14,7 +14,8 @@ pub export fn entry1() void { s.a = T{ .a = 2, .b = 2 }; } // error -// backend=stage2,llvm +// target=native +// backend=stage2 // // :6:19: error: value stored in comptime field does not match the default value of the field // :14:19: error: value stored in comptime field does not match the default value of the field diff --git a/test/cases/compile_errors/runtime_indexing_comptime_array.zig b/test/cases/compile_errors/runtime_indexing_comptime_array.zig index 3acd0feb61..16f7305f63 100644 --- a/test/cases/compile_errors/runtime_indexing_comptime_array.zig +++ b/test/cases/compile_errors/runtime_indexing_comptime_array.zig @@ -21,7 +21,8 @@ pub export fn entry3() void { _ = &test_fns[i]; } // error -// backend=stage2,llvm +// target=native +// backend=stage2 // // :6:5: error: values of type '[2]fn() void' must be comptime known, but index value is runtime known // :6:5: note: use '*const fn() void' for a function pointer type diff --git a/test/cases/compile_errors/stage2/comptime_unreachable.zig b/test/cases/compile_errors/stage2/comptime_unreachable.zig index 6086dd85c6..74b3516585 100644 --- a/test/cases/compile_errors/stage2/comptime_unreachable.zig +++ b/test/cases/compile_errors/stage2/comptime_unreachable.zig @@ -3,5 +3,7 @@ pub export fn entry() void { } // error +// target=native +// backend=stage2 // // :2:14: error: reached unreachable code From 0e9458a3fc18bb6e31dda23811943ac3988fa385 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Jun 2022 22:15:22 -0700 Subject: [PATCH 1839/2031] test-cases: avoid using realpath since it is not portable For example FreeBSD does not support this syscall. --- src/test.zig | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/test.zig b/src/test.zig index 414a9d4d82..6801433b7f 100644 --- a/src/test.zig +++ b/src/test.zig @@ -1800,19 +1800,21 @@ pub const TestContext = struct { exec_node.activate(); defer exec_node.end(); - // We use relative to cwd here because we pass a new cwd to the - // child process. - const exe_path = try std.fmt.allocPrint(arena, "." ++ std.fs.path.sep_str ++ "{s}", .{bin_name}); + // We go out of our way here to use the unique temporary directory name in + // the exe_path so that it makes its way into the cache hash, avoiding + // cache collisions from multiple threads doing `zig run` at the same time + // on the same test_case.c input filename. + const ss = std.fs.path.sep_str; + const exe_path = try std.fmt.allocPrint( + arena, + ".." ++ ss ++ "{s}" ++ ss ++ "{s}", + .{ &tmp.sub_path, bin_name }, + ); if (case.object_format != null and case.object_format.? == .c) { if (host.getExternalExecutor(target_info, .{ .link_libc = true }) != .native) { // We wouldn't be able to run the compiled C code. return; // Pass test. } - // Use an absolute path here so that the unique directory name - // for this Case makes it into the cache hash, avoiding cache - // collisions from multiple threads doing `zig run` at the same - // time on the same test_case.c input filename. - const abs_exe_path = try tmp.dir.realpathAlloc(arena, bin_name); try argv.appendSlice(&[_][]const u8{ std.testing.zig_exe_path, "run", @@ -1823,7 +1825,7 @@ pub const TestContext = struct { "-Wno-incompatible-library-redeclaration", // https://github.com/ziglang/zig/issues/875 "--", "-lc", - abs_exe_path, + exe_path, }); } else switch (host.getExternalExecutor(target_info, .{ .link_libc = case.link_libc })) { .native => try argv.append(exe_path), From 27610b0a0f0b2fc0695b0687d1e93ce910c155db Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Wed, 15 Jun 2022 08:55:39 +0200 Subject: [PATCH 1840/2031] std/crypto: add support for ECDSA signatures (#11855) ECDSA is the most commonly used signature scheme today, mainly for historical and conformance reasons. It is a necessary evil for many standard protocols such as TLS and JWT. It is tricky to implement securely and has been the root cause of multiple security disasters, from the Playstation 3 hack to multiple critical issues in OpenSSL and Java. This implementation combines lessons learned from the past with recent recommendations. In Zig, the NIST curves that ECDSA is almost always instantied with use formally verified field arithmetic, giving us peace of mind even on edge cases. And the API rejects neutral elements where it matters, and unconditionally checks for non-canonical encoding for scalars and group elements. This automatically eliminates common vulnerabilities such as https://sk.tl/2LpS695v . ECDSA's security heavily relies on the security of the random number generator, which is a concern in some environments. This implementation mitigates this by computing deterministic nonces using the conservative scheme from Pornin et al. with the optional addition of randomness as proposed in Ericsson's "Deterministic ECDSA and EdDSA Signatures with Additional Randomness" document. This approach mitigates both the implications of a weak RNG and the practical implications of fault attacks. Project Wycheproof is a Google project to test crypto libraries against known attacks by triggering edge cases. It discovered vulnerabilities in virtually all major ECDSA implementations. The entire set of ECDSA-P256-SHA256 test vectors from Project Wycheproof is included here. Zero defects were found in this implementation. The public API differs from the Ed25519 one. Instead of raw byte strings for keys and signatures, we introduce Signature, PublicKey and SecretKey structures. The reason is that a raw byte representation would not be optimal. There are multiple standard representations for keys and signatures, and decoding/encoding them may not be cheap (field elements have to be converted from/to the montgomery domain). So, the intent is to eventually move ed25519 to the same API, which is not going to introduce any performance regression, but will bring us a consistent API, that we can also reuse for RSA. --- lib/std/crypto.zig | 2 + lib/std/crypto/ecdsa.zig | 727 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 729 insertions(+) create mode 100644 lib/std/crypto/ecdsa.zig diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index 0c2869fc01..b9191585ec 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -125,6 +125,7 @@ pub const pwhash = struct { /// Digital signature functions. pub const sign = struct { pub const Ed25519 = @import("crypto/25519/ed25519.zig").Ed25519; + pub const ecdsa = @import("crypto/ecdsa.zig"); }; /// Stream ciphers. These do not provide any kind of authentication. @@ -232,6 +233,7 @@ test { _ = pwhash.phc_format; _ = sign.Ed25519; + _ = sign.ecdsa; _ = stream.chacha.ChaCha20IETF; _ = stream.chacha.ChaCha12IETF; diff --git a/lib/std/crypto/ecdsa.zig b/lib/std/crypto/ecdsa.zig new file mode 100644 index 0000000000..72c75ca163 --- /dev/null +++ b/lib/std/crypto/ecdsa.zig @@ -0,0 +1,727 @@ +const std = @import("std"); +const crypto = std.crypto; +const fmt = std.fmt; +const io = std.io; +const mem = std.mem; +const testing = std.testing; + +const EncodingError = crypto.errors.EncodingError; +const IdentityElementError = crypto.errors.IdentityElementError; +const NonCanonicalError = crypto.errors.NonCanonicalError; +const SignatureVerificationError = crypto.errors.SignatureVerificationError; + +/// ECDSA over P-256 with SHA-256. +pub const EcdsaP256Sha256 = Ecdsa(crypto.ecc.P256, crypto.hash.sha2.Sha256); +/// ECDSA over P-256 with SHA3-256. +pub const EcdsaP256Sha3_256 = Ecdsa(crypto.ecc.P256, crypto.hash.sha3.Sha3_256); +/// ECDSA over P-384 with SHA-384. +pub const EcdsaP384Sha384 = Ecdsa(crypto.ecc.P384, crypto.hash.sha2.Sha384); +/// ECDSA over P-384 with SHA3-384. +pub const EcdsaP256Sha3_384 = Ecdsa(crypto.ecc.P384, crypto.hash.sha3.Sha3_384); + +/// Elliptic Curve Digital Signature Algorithm (ECDSA). +pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type { + const Hmac = crypto.auth.hmac.Hmac(Hash); + + return struct { + /// Length (in bytes) of optional random bytes, for non-deterministic signatures. + pub const noise_length = Curve.scalar.encoded_length; + + /// An ECDSA secret key. + pub const SecretKey = struct { + /// Length (in bytes) of a raw secret key. + pub const encoded_length = Curve.scalar.encoded_length; + + bytes: Curve.scalar.CompressedScalar, + + pub fn fromBytes(bytes: [encoded_length]u8) !SecretKey { + return SecretKey{ .bytes = bytes }; + } + + pub fn toBytes(sk: SecretKey) [encoded_length]u8 { + return sk.bytes; + } + }; + + /// An ECDSA public key. + pub const PublicKey = struct { + /// Length (in bytes) of a compressed sec1-encoded key. + pub const compressed_sec1_encoded_length = 1 + Curve.Fe.encoded_length; + /// Length (in bytes) of a compressed sec1-encoded key. + pub const uncompressed_sec1_encoded_length = 1 + 2 * Curve.Fe.encoded_length; + + p: Curve, + + /// Create a public key from a SEC-1 representation. + pub fn fromSec1(sec1: []const u8) !PublicKey { + return PublicKey{ .p = try Curve.fromSec1(sec1) }; + } + + /// Encode the public key using the compressed SEC-1 format. + pub fn toCompressedSec1(p: Curve) [compressed_sec1_encoded_length]u8 { + return p.toCompressedSec1(); + } + + /// Encoding the public key using the uncompressed SEC-1 format. + pub fn toUncompressedSec1(p: Curve) [uncompressed_sec1_encoded_length]u8 { + return p.toUncompressedSec1(); + } + }; + + /// An ECDSA signature. + pub const Signature = struct { + /// Length (in bytes) of a raw signature. + pub const encoded_length = Curve.scalar.encoded_length * 2; + /// Maximum length (in bytes) of a DER-encoded signature. + pub const der_encoded_max_length = encoded_length + 2 + 2 * 3; + + /// The R component of an ECDSA signature. + r: Curve.scalar.CompressedScalar, + /// The S component of an ECDSA signature. + s: Curve.scalar.CompressedScalar, + + /// Verify the signature against a message and public key. + /// Return IdentityElement or NonCanonical if the public key or signature are not in the expected range, + /// or SignatureVerificationError if the signature is invalid for the given message and key. + pub fn verify(self: Signature, msg: []const u8, public_key: PublicKey) (IdentityElementError || NonCanonicalError || SignatureVerificationError)!void { + const r = try Curve.scalar.Scalar.fromBytes(self.r, .Big); + const s = try Curve.scalar.Scalar.fromBytes(self.s, .Big); + if (r.isZero() or s.isZero()) return error.IdentityElement; + + var h: [Hash.digest_length]u8 = undefined; + Hash.hash(msg, &h, .{}); + + const ht = Curve.scalar.encoded_length; + const z = reduceToScalar(ht, h[0..ht].*); + if (z.isZero()) { + return error.SignatureVerificationFailed; + } + + const s_inv = s.invert(); + const v1 = z.mul(s_inv).toBytes(.Little); + const v2 = r.mul(s_inv).toBytes(.Little); + const v1g = try Curve.basePoint.mulPublic(v1, .Little); + const v2pk = try public_key.p.mulPublic(v2, .Little); + const vxs = v1g.add(v2pk).affineCoordinates().x.toBytes(.Big); + const vr = reduceToScalar(Curve.Fe.encoded_length, vxs); + if (!r.equivalent(vr)) { + return error.SignatureVerificationFailed; + } + } + + /// Return the raw signature (r, s) in big-endian format. + pub fn toBytes(self: Signature) [encoded_length]u8 { + var bytes: [encoded_length]u8 = undefined; + mem.copy(u8, bytes[0 .. encoded_length / 2], &self.r); + mem.copy(u8, bytes[encoded_length / 2 ..], &self.s); + return bytes; + } + + /// Create a signature from a raw encoding of (r, s). + /// ECDSA always assumes big-endian. + pub fn fromBytes(bytes: [encoded_length]u8) Signature { + return Signature{ + .r = bytes[0 .. encoded_length / 2].*, + .s = bytes[encoded_length / 2 ..].*, + }; + } + + /// Encode the signature using the DER format. + /// The maximum length of the DER encoding is der_encoded_max_length. + /// The function returns a slice, that can be shorter than der_encoded_max_length. + pub fn toDer(self: Signature, buf: *[der_encoded_max_length]u8) []u8 { + var fb = io.fixedBufferStream(buf); + const w = fb.writer(); + const r_len = @intCast(u8, self.r.len + (self.r[0] >> 7)); + const s_len = @intCast(u8, self.s.len + (self.s[0] >> 7)); + const seq_len = @intCast(u8, 2 + r_len + 2 + s_len); + w.writeAll(&[_]u8{ 0x30, seq_len }) catch unreachable; + w.writeAll(&[_]u8{ 0x02, r_len }) catch unreachable; + if (self.r[0] >> 7 != 0) { + w.writeByte(0x00) catch unreachable; + } + w.writeAll(&self.r) catch unreachable; + w.writeAll(&[_]u8{ 0x02, s_len }) catch unreachable; + if (self.s[0] >> 7 != 0) { + w.writeByte(0x00) catch unreachable; + } + w.writeAll(&self.s) catch unreachable; + return fb.getWritten(); + } + + // Read a DER-encoded integer. + fn readDerInt(out: []u8, reader: anytype) EncodingError!void { + var buf: [2]u8 = undefined; + _ = reader.readNoEof(&buf) catch return error.InvalidEncoding; + if (buf[0] != 0x02) return error.InvalidEncoding; + var expected_len = @as(usize, buf[1]); + if (expected_len == 0 or expected_len > 1 + out.len) return error.InvalidEncoding; + var has_top_bit = false; + if (expected_len == 1 + out.len) { + if ((reader.readByte() catch return error.InvalidEncoding) != 0) return error.InvalidEncoding; + expected_len -= 1; + has_top_bit = true; + } + const out_slice = out[out.len - expected_len ..]; + reader.readNoEof(out_slice) catch return error.InvalidEncoding; + if (has_top_bit and out[0] >> 7 == 0) return error.InvalidEncoding; + } + + /// Create a signature from a DER representation. + /// Returns InvalidEncoding if the DER encoding is invalid. + pub fn fromDer(der: []const u8) EncodingError!Signature { + var sig: Signature = mem.zeroInit(Signature, .{}); + var fb = io.fixedBufferStream(der); + const reader = fb.reader(); + var buf: [2]u8 = undefined; + _ = reader.readNoEof(&buf) catch return error.InvalidEncoding; + if (buf[0] != 0x30 or @as(usize, buf[1]) + 2 != der.len) { + return error.InvalidEncoding; + } + try readDerInt(&sig.r, reader); + try readDerInt(&sig.s, reader); + if (fb.getPos() catch unreachable != der.len) return error.InvalidEncoding; + + return sig; + } + }; + + /// An ECDSA key pair. + pub const KeyPair = struct { + /// Length (in bytes) of a seed required to create a key pair. + pub const seed_length = noise_length; + + /// Public part. + public_key: PublicKey, + /// Secret scalar. + secret_key: SecretKey, + + /// Create a new key pair. The seed must be secret and indistinguishable from random. + /// The seed can also be left to null in order to generate a random key pair. + pub fn create(seed: ?[seed_length]u8) IdentityElementError!KeyPair { + var seed_ = seed; + if (seed_ == null) { + var random_seed: [seed_length]u8 = undefined; + crypto.random.bytes(&random_seed); + seed_ = random_seed; + } + const h = [_]u8{0x00} ** Hash.digest_length; + const k0 = [_]u8{0x01} ** SecretKey.encoded_length; + const secret_key = deterministicScalar(h, k0, seed_).toBytes(.Big); + return fromSecretKey(SecretKey{ .bytes = secret_key }); + } + + /// Return the public key corresponding to the secret key. + pub fn fromSecretKey(secret_key: SecretKey) IdentityElementError!KeyPair { + const public_key = try Curve.basePoint.mul(secret_key.bytes, .Big); + return KeyPair{ .secret_key = secret_key, .public_key = PublicKey{ .p = public_key } }; + } + + /// Sign a message using the key pair. + /// The noise can be null in order to create deterministic signatures. + /// If deterministic signatures are not required, the noise should be randomly generated instead. + /// This helps defend against fault attacks. + pub fn sign(key_pair: KeyPair, msg: []const u8, noise: ?[noise_length]u8) (IdentityElementError || NonCanonicalError)!Signature { + const secret_key = key_pair.secret_key; + + var h: [Hash.digest_length]u8 = undefined; + Hash.hash(msg, &h, .{}); + + const scalar_encoded_length = Curve.scalar.encoded_length; + std.debug.assert(h.len >= scalar_encoded_length); + const z = reduceToScalar(scalar_encoded_length, h[0..scalar_encoded_length].*); + + const k = deterministicScalar(h, secret_key.bytes, noise); + + const p = try Curve.basePoint.mul(k.toBytes(.Big), .Big); + const xs = p.affineCoordinates().x.toBytes(.Big); + const r = reduceToScalar(Curve.Fe.encoded_length, xs); + if (r.isZero()) return error.IdentityElement; + + const k_inv = k.invert(); + const zrs = z.add(r.mul(try Curve.scalar.Scalar.fromBytes(secret_key.bytes, .Big))); + const s = k_inv.mul(zrs); + if (s.isZero()) return error.IdentityElement; + + return Signature{ .r = r.toBytes(.Big), .s = s.toBytes(.Big) }; + } + }; + + // Reduce the coordinate of a field element to the scalar field. + fn reduceToScalar(comptime unreduced_len: usize, s: [unreduced_len]u8) Curve.scalar.Scalar { + if (unreduced_len >= 48) { + var xs = [_]u8{0} ** 64; + mem.copy(u8, xs[xs.len - s.len ..], s[0..]); + return Curve.scalar.Scalar.fromBytes64(xs, .Big); + } + var xs = [_]u8{0} ** 48; + mem.copy(u8, xs[xs.len - s.len ..], s[0..]); + return Curve.scalar.Scalar.fromBytes48(xs, .Big); + } + + // Create a deterministic scalar according to a secret key and optional noise. + // This uses the overly conservative scheme from the "Deterministic ECDSA and EdDSA Signatures with Additional Randomness" draft. + fn deterministicScalar(h: [Hash.digest_length]u8, secret_key: Curve.scalar.CompressedScalar, noise: ?[noise_length]u8) Curve.scalar.Scalar { + var k = [_]u8{0x00} ** h.len; + var m = [_]u8{0x00} ** (h.len + 1 + noise_length + secret_key.len + h.len); + const m_v = m[0..h.len]; + const m_i = &m[m_v.len]; + const m_z = m[m_v.len + 1 ..][0..noise_length]; + const m_x = m[m_v.len + 1 + noise_length ..][0..secret_key.len]; + const m_h = m[m.len - h.len ..]; + + mem.set(u8, m_v, 0x01); + m_i.* = 0x00; + if (noise) |n| mem.copy(u8, m_z, &n); + mem.copy(u8, m_x, &secret_key); + mem.copy(u8, m_h, &h); + Hmac.create(&k, &m, &k); + Hmac.create(m_v, m_v, &k); + mem.copy(u8, m_v, m_v); + m_i.* = 0x01; + Hmac.create(&k, &m, &k); + Hmac.create(m_v, m_v, &k); + while (true) { + Hmac.create(m_v, m_v, &k); + if (Curve.scalar.Scalar.fromBytes(m_v[0..Curve.scalar.encoded_length].*, .Big)) |s| return s else |_| {} + mem.copy(u8, m_v, m_v); + m_i.* = 0x00; + Hmac.create(&k, m[0 .. m_v.len + 1], &k); + Hmac.create(m_v, m_v, &k); + } + } + }; +} + +test "ECDSA - Basic operations" { + const Scheme = EcdsaP384Sha384; + const kp = try Scheme.KeyPair.create(null); + const msg = "test"; + + var noise: [Scheme.noise_length]u8 = undefined; + crypto.random.bytes(&noise); + const sig = try kp.sign(msg, noise); + try sig.verify(msg, kp.public_key); + + const sig2 = try kp.sign(msg, null); + try sig2.verify(msg, kp.public_key); +} + +const TestVector = struct { + key: []const u8, + msg: []const u8, + sig: []const u8, + result: enum { valid, invalid, acceptable }, +}; + +test "ECDSA - Test vectors from Project Wycheproof" { + const vectors = [_]TestVector{ + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1802204cd60b855d442f5b3c7b11eb6c4e0ae7525fe710fab9aa7c77a67f79e6fadd76", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180220b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .acceptable }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30814502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3082004502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304602202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3085010000004502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "308901000000000000004502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30847fffffff02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3084ffffffff02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3085ffffffffff02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3088ffffffffffffffff02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30ff02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "308002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502802ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18028000b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3047000002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0500", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304a498177304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30492500304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3047304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0004deadbeef", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304a222549817702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30492224250002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304d222202202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180004deadbeef022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304a02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e182226498177022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304902202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1822252500022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304d02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e182223022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0004deadbeef", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304daa00bb00cd00304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304baa02aabb304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304d2228aa00bb00cd0002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304b2226aa02aabb02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304d02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e182229aa00bb00cd00022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304b02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e182227aa02aabb022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3081", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3080304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3049228002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180000022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304902202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e182280022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3080314502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3049228003202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180000022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304902202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e182280032100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "0500", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "2e4502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "2f4502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "314502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "324502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "ff4502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30493001023044202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3044202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "308002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "308002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db00", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "308002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db05000000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "308002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db060811220000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "308002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000fe02beef", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "308002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0002beef", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3047300002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db3000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304802202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847dbbf7f00", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3047304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "302202202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "306802202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30460281202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304602202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1802812100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3047028200202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180282002100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502212ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3045021f2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022200b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022000b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304a028501000000202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304a02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180285010000002100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304e02890100000000000000202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304e02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18028901000000000000002100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304902847fffffff2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304902202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1802847fffffff00b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30490284ffffffff2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304902202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180284ffffffff00b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304a0285ffffffffff2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304a02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180285ffffffffff00b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304d0288ffffffffffffffff2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304d02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180288ffffffffffffffff00b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502ff2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1802ff00b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3023022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "302402022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "302302202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1802", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304702222ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180000022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022300b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3047022200002ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180223000000b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180000022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304702222ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180500022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022300b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0500", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30250281022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "302402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180281", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30250500022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "302402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180500", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304500202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304501202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304503202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304504202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3045ff202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18002100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18012100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18032100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18042100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18ff2100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30250200022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "302402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180200", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3049222402012b021fa3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304902202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1822250201000220b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3045022029a3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022102b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e98022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b491568475b", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3044021f2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3044021fa3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022000b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30460221ff2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304602202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180222ff00b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026090180022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "302502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18090180", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026020100022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "302502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18020100", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30460221012ba3a8bd6b94d5ed80a6d9d1190a436ebccc0833490686deac8635bcb9bf5369022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30460221ff2ba3a8bf6b94d5eb80a6d9d1190a436f42fe12d7fad749d4c512a036c0f908c7022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30450220d45c5741946b2a137f59262ee6f5bc91001af27a5e1117a64733950642a3d1e8022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100d45c5740946b2a147f59262ee6f5bc90bd01ed280528b62b3aed5fc93f06f739022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30460221fed45c5742946b2a127f59262ee6f5bc914333f7ccb6f979215379ca434640ac97022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30460221012ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100d45c5741946b2a137f59262ee6f5bc91001af27a5e1117a64733950642a3d1e8022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022101b329f478a2bbd0a6c384ee1493b1f518276e0e4a5375928d6fcd160c11cb6d2c", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180220b329f47aa2bbd0a4c384ee1493b1f518ada018ef05465583885980861905228a", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180221ff4cd60b865d442f5a3c7b11eb6c4e0ae79578ec6353a20bf783ecb4b6ea97b825", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180221fe4cd60b875d442f593c7b11eb6c4e0ae7d891f1b5ac8a6d729032e9f3ee3492d4", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022101b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "304402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1802204cd60b865d442f5a3c7b11eb6c4e0ae79578ec6353a20bf783ecb4b6ea97b825", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3006020100020100", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3006020100020101", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30060201000201ff", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026020100022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026020100022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026020100022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026020100022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026020100022100ffffffff00000001000000000000000000000001000000000000000000000000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3008020100090380fe01", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3006020100090142", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3006020101020100", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3006020101020101", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30060201010201ff", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026020101022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026020101022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026020101022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026020101022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026020101022100ffffffff00000001000000000000000000000001000000000000000000000000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3008020101090380fe01", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3006020101090142", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30060201ff020100", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30060201ff020101", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30060201ff0201ff", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30260201ff022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30260201ff022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30260201ff022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30260201ff022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30260201ff022100ffffffff00000001000000000000000000000001000000000000000000000000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30080201ff090380fe01", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30060201ff090142", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551020100", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551020101", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325510201ff", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551022100ffffffff00000001000000000000000000000001000000000000000000000000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3028022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551090380fe01", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551090142", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550020100", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550020101", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325500201ff", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550022100ffffffff00000001000000000000000000000001000000000000000000000000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3028022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550090380fe01", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550090142", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552020100", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552020101", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325520201ff", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552022100ffffffff00000001000000000000000000000001000000000000000000000000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3028022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552090380fe01", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552090142", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff020100", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff020101", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff0201ff", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff022100ffffffff00000001000000000000000000000001000000000000000000000000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3028022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff090380fe01", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff090142", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000001000000000000000000000001000000000000000000000000020100", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000001000000000000000000000001000000000000000000000000020101", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff000000010000000000000000000000010000000000000000000000000201ff", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000001000000000000000000000001000000000000000000000000022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000001000000000000000000000001000000000000000000000000022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000001000000000000000000000001000000000000000000000000022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000001000000000000000000000001000000000000000000000000022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3046022100ffffffff00000001000000000000000000000001000000000000000000000000022100ffffffff00000001000000000000000000000001000000000000000000000000", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3028022100ffffffff00000001000000000000000000000001000000000000000000000000090380fe01", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3026022100ffffffff00000001000000000000000000000001000000000000000000000000090142", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30060201010c0130", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30050201010c00", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30090c0225730c03732573", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "30080201013003020100", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3003020101", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313233343030", .sig = "3006020101010100", .result = .invalid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "3639383139", .sig = "3044022064a1aab5000d0e804f3e2fc02bdee9be8ff312334e2ba16d11547c97711c898e02206af015971cc30be6d1a206d4e013e0997772a2f91d73286ffd683b9bb2cf4f1b", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "343236343739373234", .sig = "3044022016aea964a2f6506d6f78c81c91fc7e8bded7d397738448de1e19a0ec580bf2660220252cd762130c6667cfe8b7bc47d27d78391e8e80c578d1cd38c3ff033be928e9", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "37313338363834383931", .sig = "30450221009cc98be2347d469bf476dfc26b9b733df2d26d6ef524af917c665baccb23c8820220093496459effe2d8d70727b82462f61d0ec1b7847929d10ea631dacb16b56c32", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "3130333539333331363638", .sig = "3044022073b3c90ecd390028058164524dde892703dce3dea0d53fa8093999f07ab8aa4302202f67b0b8e20636695bb7d8bf0a651c802ed25a395387b5f4188c0c4075c88634", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "33393439343031323135", .sig = "3046022100bfab3098252847b328fadf2f89b95c851a7f0eb390763378f37e90119d5ba3dd022100bdd64e234e832b1067c2d058ccb44d978195ccebb65c2aaf1e2da9b8b4987e3b", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "31333434323933303739", .sig = "30440220204a9784074b246d8bf8bf04a4ceb1c1f1c9aaab168b1596d17093c5cd21d2cd022051cce41670636783dc06a759c8847868a406c2506fe17975582fe648d1d88b52", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "33373036323131373132", .sig = "3046022100ed66dc34f551ac82f63d4aa4f81fe2cb0031a91d1314f835027bca0f1ceeaa0302210099ca123aa09b13cd194a422e18d5fda167623c3f6e5d4d6abb8953d67c0c48c7", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "333433363838373132", .sig = "30450220060b700bef665c68899d44f2356a578d126b062023ccc3c056bf0f60a237012b0221008d186c027832965f4fcc78a3366ca95dedbb410cbef3f26d6be5d581c11d3610", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "31333531353330333730", .sig = "30460221009f6adfe8d5eb5b2c24d7aa7934b6cf29c93ea76cd313c9132bb0c8e38c96831d022100b26a9c9e40e55ee0890c944cf271756c906a33e66b5bd15e051593883b5e9902", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "36353533323033313236", .sig = "3045022100a1af03ca91677b673ad2f33615e56174a1abf6da168cebfa8868f4ba273f16b7022020aa73ffe48afa6435cd258b173d0c2377d69022e7d098d75caf24c8c5e06b1c", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "31353634333436363033", .sig = "3045022100fdc70602766f8eed11a6c99a71c973d5659355507b843da6e327a28c11893db902203df5349688a085b137b1eacf456a9e9e0f6d15ec0078ca60a7f83f2b10d21350", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "34343239353339313137", .sig = "3046022100b516a314f2fce530d6537f6a6c49966c23456f63c643cf8e0dc738f7b876e675022100d39ffd033c92b6d717dd536fbc5efdf1967c4bd80954479ba66b0120cd16fff2", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "3130393533323631333531", .sig = "304402203b2cbf046eac45842ecb7984d475831582717bebb6492fd0a485c101e29ff0a802204c9b7b47a98b0f82de512bc9313aaf51701099cac5f76e68c8595fc1c1d99258", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "35393837333530303431", .sig = "3044022030c87d35e636f540841f14af54e2f9edd79d0312cfa1ab656c3fb15bfde48dcf022047c15a5a82d24b75c85a692bd6ecafeb71409ede23efd08e0db9abf6340677ed", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "33343633303036383738", .sig = "3044022038686ff0fda2cef6bc43b58cfe6647b9e2e8176d168dec3c68ff262113760f520220067ec3b651f422669601662167fa8717e976e2db5e6a4cf7c2ddabb3fde9d67d", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "39383137333230323837", .sig = "3044022044a3e23bf314f2b344fc25c7f2de8b6af3e17d27f5ee844b225985ab6e2775cf02202d48e223205e98041ddc87be532abed584f0411f5729500493c9cc3f4dd15e86", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "33323232303431303436", .sig = "304402202ded5b7ec8e90e7bf11f967a3d95110c41b99db3b5aa8d330eb9d638781688e902207d5792c53628155e1bfc46fb1a67e3088de049c328ae1f44ec69238a009808f9", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "36363636333037313034", .sig = "3046022100bdae7bcb580bf335efd3bc3d31870f923eaccafcd40ec2f605976f15137d8b8f022100f6dfa12f19e525270b0106eecfe257499f373a4fb318994f24838122ce7ec3c7", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "31303335393531383938", .sig = "3045022050f9c4f0cd6940e162720957ffff513799209b78596956d21ece251c2401f1c6022100d7033a0a787d338e889defaaabb106b95a4355e411a59c32aa5167dfab244726", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "31383436353937313935", .sig = "3045022100f612820687604fa01906066a378d67540982e29575d019aabe90924ead5c860d02203f9367702dd7dd4f75ea98afd20e328a1a99f4857b316525328230ce294b0fef", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "33313336303436313839", .sig = "30460221009505e407657d6e8bc93db5da7aa6f5081f61980c1949f56b0f2f507da5782a7a022100c60d31904e3669738ffbeccab6c3656c08e0ed5cb92b3cfa5e7f71784f9c5021", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "32363633373834323534", .sig = "3046022100bbd16fbbb656b6d0d83e6a7787cd691b08735aed371732723e1c68a40404517d0221009d8e35dba96028b7787d91315be675877d2d097be5e8ee34560e3e7fd25c0f00", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "31363532313030353234", .sig = "304402202ec9760122db98fd06ea76848d35a6da442d2ceef7559a30cf57c61e92df327e02207ab271da90859479701fccf86e462ee3393fb6814c27b760c4963625c0a19878", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "35373438303831363936", .sig = "3044022054e76b7683b6650baa6a7fc49b1c51eed9ba9dd463221f7a4f1005a89fe00c5902202ea076886c773eb937ec1cc8374b7915cfd11b1c1ae1166152f2f7806a31c8fd", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "36333433393133343638", .sig = "304402205291deaf24659ffbbce6e3c26f6021097a74abdbb69be4fb10419c0c496c9466022065d6fcf336d27cc7cdb982bb4e4ecef5827f84742f29f10abf83469270a03dc3", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "31353431313033353938", .sig = "30450220207a3241812d75d947419dc58efb05e8003b33fc17eb50f9d15166a88479f107022100cdee749f2e492b213ce80b32d0574f62f1c5d70793cf55e382d5caadf7592767", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "3130343738353830313238", .sig = "304502206554e49f82a855204328ac94913bf01bbe84437a355a0a37c0dee3cf81aa7728022100aea00de2507ddaf5c94e1e126980d3df16250a2eaebc8be486effe7f22b4f929", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "3130353336323835353638", .sig = "3046022100a54c5062648339d2bff06f71c88216c26c6e19b4d80a8c602990ac82707efdfc022100e99bbe7fcfafae3e69fd016777517aa01056317f467ad09aff09be73c9731b0d", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "393533393034313035", .sig = "3045022100975bd7157a8d363b309f1f444012b1a1d23096593133e71b4ca8b059cff37eaf02207faa7a28b1c822baa241793f2abc930bd4c69840fe090f2aacc46786bf919622", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "393738383438303339", .sig = "304402205694a6f84b8f875c276afd2ebcfe4d61de9ec90305afb1357b95b3e0da43885e02200dffad9ffd0b757d8051dec02ebdf70d8ee2dc5c7870c0823b6ccc7c679cbaa4", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "33363130363732343432", .sig = "3045022100a0c30e8026fdb2b4b4968a27d16a6d08f7098f1a98d21620d7454ba9790f1ba602205e470453a8a399f15baf463f9deceb53acc5ca64459149688bd2760c65424339", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "31303534323430373035", .sig = "30440220614ea84acf736527dd73602cd4bb4eea1dfebebd5ad8aca52aa0228cf7b99a880220737cc85f5f2d2f60d1b8183f3ed490e4de14368e96a9482c2a4dd193195c902f", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "35313734343438313937", .sig = "3045022100bead6734ebe44b810d3fb2ea00b1732945377338febfd439a8d74dfbd0f942fa02206bb18eae36616a7d3cad35919fd21a8af4bbe7a10f73b3e036a46b103ef56e2a", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "31393637353631323531", .sig = "30440220499625479e161dacd4db9d9ce64854c98d922cbf212703e9654fae182df9bad2022042c177cf37b8193a0131108d97819edd9439936028864ac195b64fca76d9d693", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "33343437323533333433", .sig = "3045022008f16b8093a8fb4d66a2c8065b541b3d31e3bfe694f6b89c50fb1aaa6ff6c9b20221009d6455e2d5d1779748573b611cb95d4a21f967410399b39b535ba3e5af81ca2e", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "333638323634333138", .sig = "3046022100be26231b6191658a19dd72ddb99ed8f8c579b6938d19bce8eed8dc2b338cb5f8022100e1d9a32ee56cffed37f0f22b2dcb57d5c943c14f79694a03b9c5e96952575c89", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "33323631313938363038", .sig = "3045022015e76880898316b16204ac920a02d58045f36a229d4aa4f812638c455abe0443022100e74d357d3fcb5c8c5337bd6aba4178b455ca10e226e13f9638196506a1939123", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "39363738373831303934", .sig = "30440220352ecb53f8df2c503a45f9846fc28d1d31e6307d3ddbffc1132315cc07f16dad02201348dfa9c482c558e1d05c5242ca1c39436726ecd28258b1899792887dd0a3c6", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "34393538383233383233", .sig = "304402204a40801a7e606ba78a0da9882ab23c7677b8642349ed3d652c5bfa5f2a9558fb02203a49b64848d682ef7f605f2832f7384bdc24ed2925825bf8ea77dc5981725782", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "383234363337383337", .sig = "3045022100eacc5e1a8304a74d2be412b078924b3bb3511bac855c05c9e5e9e44df3d61e9602207451cd8e18d6ed1885dd827714847f96ec4bb0ed4c36ce9808db8f714204f6d1", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "3131303230383333373736", .sig = "304502202f7a5e9e5771d424f30f67fdab61e8ce4f8cd1214882adb65f7de94c31577052022100ac4e69808345809b44acb0b2bd889175fb75dd050c5a449ab9528f8f78daa10c", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "313333383731363438", .sig = "3045022100ffcda40f792ce4d93e7e0f0e95e1a2147dddd7f6487621c30a03d710b3300219022079938b55f8a17f7ed7ba9ade8f2065a1fa77618f0b67add8d58c422c2453a49a", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "333232313434313632", .sig = "304602210081f2359c4faba6b53d3e8c8c3fcc16a948350f7ab3a588b28c17603a431e39a8022100cd6f6a5cc3b55ead0ff695d06c6860b509e46d99fccefb9f7f9e101857f74300", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "3130363836363535353436", .sig = "3045022100dfc8bf520445cbb8ee1596fb073ea283ea130251a6fdffa5c3f5f2aaf75ca8080220048e33efce147c9dd92823640e338e68bfd7d0dc7a4905b3a7ac711e577e90e7", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "3632313535323436", .sig = "3046022100ad019f74c6941d20efda70b46c53db166503a0e393e932f688227688ba6a576202210093320eb7ca0710255346bdbb3102cdcf7964ef2e0988e712bc05efe16c199345", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "37303330383138373734", .sig = "3046022100ac8096842e8add68c34e78ce11dd71e4b54316bd3ebf7fffdeb7bd5a3ebc1883022100f5ca2f4f23d674502d4caf85d187215d36e3ce9f0ce219709f21a3aac003b7a8", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "35393234353233373434", .sig = "30440220677b2d3a59b18a5ff939b70ea002250889ddcd7b7b9d776854b4943693fb92f702206b4ba856ade7677bf30307b21f3ccda35d2f63aee81efd0bab6972cc0795db55", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "31343935353836363231", .sig = "30450220479e1ded14bcaed0379ba8e1b73d3115d84d31d4b7c30e1f05e1fc0d5957cfb0022100918f79e35b3d89487cf634a4f05b2e0c30857ca879f97c771e877027355b2443", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "34303035333134343036", .sig = "3044022043dfccd0edb9e280d9a58f01164d55c3d711e14b12ac5cf3b64840ead512a0a302201dbe33fa8ba84533cd5c4934365b3442ca1174899b78ef9a3199f49584389772", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "33303936343537353132", .sig = "304402205b09ab637bd4caf0f4c7c7e4bca592fea20e9087c259d26a38bb4085f0bbff11022045b7eb467b6748af618e9d80d6fdcd6aa24964e5a13f885bca8101de08eb0d75", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "32373834303235363230", .sig = "304502205e9b1c5a028070df5728c5c8af9b74e0667afa570a6cfa0114a5039ed15ee06f022100b1360907e2d9785ead362bb8d7bd661b6c29eeffd3c5037744edaeb9ad990c20", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "32363138373837343138", .sig = "304502200671a0a85c2b72d54a2fb0990e34538b4890050f5a5712f6d1a7a5fb8578f32e022100db1846bab6b7361479ab9c3285ca41291808f27fd5bd4fdac720e5854713694c", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "31363432363235323632", .sig = "304402207673f8526748446477dbbb0590a45492c5d7d69859d301abbaedb35b2095103a02203dc70ddf9c6b524d886bed9e6af02e0e4dec0d417a414fed3807ef4422913d7c", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "36383234313839343336", .sig = "304402207f085441070ecd2bb21285089ebb1aa6450d1a06c36d3ff39dfd657a796d12b50220249712012029870a2459d18d47da9aa492a5e6cb4b2d8dafa9e4c5c54a2b9a8b", .result = .valid }, + .{ .key = "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", .msg = "343834323435343235", .sig = "3046022100914c67fb61dd1e27c867398ea7322d5ab76df04bc5aa6683a8e0f30a5d287348022100fa07474031481dda4953e3ac1959ee8cea7e66ec412b38d6c96d28f6d37304ea", .result = .valid }, + .{ .key = "040ad99500288d466940031d72a9f5445a4d43784640855bf0a69874d2de5fe103c5011e6ef2c42dcd50d5d3d29f99ae6eba2c80c9244f4c5422f0979ff0c3ba5e", .msg = "313233343030", .sig = "303502104319055358e8617b0c46353d039cdaab022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254e", .result = .valid }, + .{ .key = "040ad99500288d466940031d72a9f5445a4d43784640855bf0a69874d2de5fe103c5011e6ef2c42dcd50d5d3d29f99ae6eba2c80c9244f4c5422f0979ff0c3ba5e", .msg = "313233343030", .sig = "3046022100ffffffff00000001000000000000000000000000fffffffffffffffffffffffc022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254e", .result = .invalid }, + .{ .key = "04ab05fd9d0de26b9ce6f4819652d9fc69193d0aa398f0fba8013e09c58220455419235271228c786759095d12b75af0692dd4103f19f6a8c32f49435a1e9b8d45", .msg = "313233343030", .sig = "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254f022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254e", .result = .valid }, + .{ .key = "0480984f39a1ff38a86a68aa4201b6be5dfbfecf876219710b07badf6fdd4c6c5611feb97390d9826e7a06dfb41871c940d74415ed3cac2089f1445019bb55ed95", .msg = "313233343030", .sig = "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100909135bdb6799286170f5ead2de4f6511453fe50914f3df2de54a36383df8dd4", .result = .valid }, + .{ .key = "044201b4272944201c3294f5baa9a3232b6dd687495fcc19a70a95bc602b4f7c0595c37eba9ee8171c1bb5ac6feaf753bc36f463e3aef16629572c0c0a8fb0800e", .msg = "313233343030", .sig = "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022027b4577ca009376f71303fd5dd227dcef5deb773ad5f5a84360644669ca249a5", .result = .valid }, + .{ .key = "04a71af64de5126a4a4e02b7922d66ce9415ce88a4c9d25514d91082c8725ac9575d47723c8fbe580bb369fec9c2665d8e30a435b9932645482e7c9f11e872296b", .msg = "313233343030", .sig = "3006020105020101", .result = .valid }, + .{ .key = "046627cec4f0731ea23fc2931f90ebe5b7572f597d20df08fc2b31ee8ef16b15726170ed77d8d0a14fc5c9c3c4c9be7f0d3ee18f709bb275eaf2073e258fe694a5", .msg = "313233343030", .sig = "3006020105020103", .result = .valid }, + .{ .key = "045a7c8825e85691cce1f5e7544c54e73f14afc010cb731343262ca7ec5a77f5bfef6edf62a4497c1bd7b147fb6c3d22af3c39bfce95f30e13a16d3d7b2812f813", .msg = "313233343030", .sig = "3006020105020105", .result = .valid }, + .{ .key = "04cbe0c29132cd738364fedd603152990c048e5e2fff996d883fa6caca7978c73770af6a8ce44cb41224b2603606f4c04d188e80bff7cc31ad5189d4ab0d70e8c1", .msg = "313233343030", .sig = "3006020105020106", .result = .valid }, + .{ .key = "04cbe0c29132cd738364fedd603152990c048e5e2fff996d883fa6caca7978c73770af6a8ce44cb41224b2603606f4c04d188e80bff7cc31ad5189d4ab0d70e8c1", .msg = "313233343030", .sig = "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632556020106", .result = .invalid }, + .{ .key = "044be4178097002f0deab68f0d9a130e0ed33a6795d02a20796db83444b037e13920f13051e0eecdcfce4dacea0f50d1f247caa669f193c1b4075b51ae296d2d56", .msg = "313233343030", .sig = "3026020105022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc75fbd8", .result = .invalid }, + .{ .key = "04d0f73792203716afd4be4329faa48d269f15313ebbba379d7783c97bf3e890d9971f4a3206605bec21782bf5e275c714417e8f566549e6bc68690d2363c89cc1", .msg = "313233343030", .sig = "3027020201000221008f1e3c7862c58b16bb76eddbb76eddbb516af4f63f2d74d76e0d28c9bb75ea88", .result = .valid }, + .{ .key = "044838b2be35a6276a80ef9e228140f9d9b96ce83b7a254f71ccdebbb8054ce05ffa9cbc123c919b19e00238198d04069043bd660a828814051fcb8aac738a6c6b", .msg = "313233343030", .sig = "302c02072d9b4d347952d6022100ef3043e7329581dbb3974497710ab11505ee1c87ff907beebadd195a0ffe6d7a", .result = .valid }, + .{ .key = "047393983ca30a520bbc4783dc9960746aab444ef520c0a8e771119aa4e74b0f64e9d7be1ab01a0bf626e709863e6a486dbaf32793afccf774e2c6cd27b1857526", .msg = "313233343030", .sig = "3032020d1033e67e37b32b445580bf4eff0221008b748b74000000008b748b748b748b7466e769ad4a16d3dcd87129b8e91d1b4d", .result = .valid }, + .{ .key = "045ac331a1103fe966697379f356a937f350588a05477e308851b8a502d5dfcdc5fe9993df4b57939b2b8da095bf6d794265204cfe03be995a02e65d408c871c0b", .msg = "313233343030", .sig = "302702020100022100ef9f6ba4d97c09d03178fa20b4aaad83be3cf9cb824a879fec3270fc4b81ef5b", .result = .valid }, + .{ .key = "041d209be8de2de877095a399d3904c74cc458d926e27bb8e58e5eae5767c41509dd59e04c214f7b18dce351fc2a549893a6860e80163f38cc60a4f2c9d040d8c9", .msg = "313233343030", .sig = "3032020d062522bbd3ecbe7c39e93e7c25022100ef9f6ba4d97c09d03178fa20b4aaad83be3cf9cb824a879fec3270fc4b81ef5b", .result = .valid }, + .{ .key = "04083539fbee44625e3acaafa2fcb41349392cef0633a1b8fabecee0c133b10e99915c1ebe7bf00df8535196770a58047ae2a402f26326bb7d41d4d7616337911e", .msg = "313233343030", .sig = "3045022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6324d50220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70", .result = .valid }, + .{ .key = "048aeb368a7027a4d64abdea37390c0c1d6a26f399e2d9734de1eb3d0e1937387405bd13834715e1dbae9b875cf07bd55e1b6691c7f7536aef3b19bf7a4adf576d", .msg = "313233343030", .sig = "30250220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70020101", .result = .valid }, + .{ .key = "048aeb368a7027a4d64abdea37390c0c1d6a26f399e2d9734de1eb3d0e1937387405bd13834715e1dbae9b875cf07bd55e1b6691c7f7536aef3b19bf7a4adf576d", .msg = "313233343030", .sig = "30250220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70020100", .result = .invalid }, + .{ .key = "04b533d4695dd5b8c5e07757e55e6e516f7e2c88fa0239e23f60e8ec07dd70f2871b134ee58cc583278456863f33c3a85d881f7d4a39850143e29d4eaf009afe47", .msg = "313233343030", .sig = "304402207fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a80220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70", .result = .invalid }, + .{ .key = "04f50d371b91bfb1d7d14e1323523bc3aa8cbf2c57f9e284de628c8b4536787b86f94ad887ac94d527247cd2e7d0c8b1291c553c9730405380b14cbb209f5fa2dd", .msg = "313233343030", .sig = "304402207fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a902207fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a8", .result = .valid }, + .{ .key = "0468ec6e298eafe16539156ce57a14b04a7047c221bafc3a582eaeb0d857c4d94697bed1af17850117fdb39b2324f220a5698ed16c426a27335bb385ac8ca6fb30", .msg = "313233343030", .sig = "304402207fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a902207fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a9", .result = .valid }, + .{ .key = "0469da0364734d2e530fece94019265fefb781a0f1b08f6c8897bdf6557927c8b866d2d3c7dcd518b23d726960f069ad71a933d86ef8abbcce8b20f71e2a847002", .msg = "313233343030", .sig = "30450220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70022100bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca605023", .result = .valid }, + .{ .key = "04d8adc00023a8edc02576e2b63e3e30621a471e2b2320620187bf067a1ac1ff3233e2b50ec09807accb36131fff95ed12a09a86b4ea9690aa32861576ba2362e1", .msg = "313233343030", .sig = "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70022044a5ad0ad0636d9f12bc9e0a6bdd5e1cbcb012ea7bf091fcec15b0c43202d52e", .result = .valid }, + .{ .key = "043623ac973ced0a56fa6d882f03a7d5c7edca02cfc7b2401fab3690dbe75ab7858db06908e64b28613da7257e737f39793da8e713ba0643b92e9bb3252be7f8fe", .msg = "313233343030", .sig = "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c700220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70", .result = .valid }, + .{ .key = "04cf04ea77e9622523d894b93ff52dc3027b31959503b6fa3890e5e04263f922f1e8528fb7c006b3983c8b8400e57b4ed71740c2f3975438821199bedeaecab2e9", .msg = "313233343030", .sig = "30450220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70022100aaaaaaaa00000000aaaaaaaaaaaaaaaa7def51c91a0fbf034d26872ca84218e1", .result = .valid }, + .{ .key = "04db7a2c8a1ab573e5929dc24077b508d7e683d49227996bda3e9f78dbeff773504f417f3bc9a88075c2e0aadd5a13311730cf7cc76a82f11a36eaf08a6c99a206", .msg = "313233343030", .sig = "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100e91e1ba60fdedb76a46bcb51dc0b8b4b7e019f0a28721885fa5d3a8196623397", .result = .valid }, + .{ .key = "04dead11c7a5b396862f21974dc4752fadeff994efe9bbd05ab413765ea80b6e1f1de3f0640e8ac6edcf89cff53c40e265bb94078a343736df07aa0318fc7fe1ff", .msg = "313233343030", .sig = "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100fdea5843ffeb73af94313ba4831b53fe24f799e525b1e8e8c87b59b95b430ad9", .result = .valid }, + .{ .key = "04d0bc472e0d7c81ebaed3a6ef96c18613bb1fea6f994326fbe80e00dfde67c7e9986c723ea4843d48389b946f64ad56c83ad70ff17ba85335667d1bb9fa619efd", .msg = "313233343030", .sig = "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022003ffcabf2f1b4d2a65190db1680d62bb994e41c5251cd73b3c3dfc5e5bafc035", .result = .valid }, + .{ .key = "04a0a44ca947d66a2acb736008b9c08d1ab2ad03776e02640f78495d458dd51c326337fe5cf8c4604b1f1c409dc2d872d4294a4762420df43a30a2392e40426add", .msg = "313233343030", .sig = "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd02204dfbc401f971cd304b33dfdb17d0fed0fe4c1a88ae648e0d2847f74977534989", .result = .valid }, + .{ .key = "04c9c2115290d008b45fb65fad0f602389298c25420b775019d42b62c3ce8a96b73877d25a8080dc02d987ca730f0405c2c9dbefac46f9e601cc3f06e9713973fd", .msg = "313233343030", .sig = "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100bc4024761cd2ffd43dfdb17d0fed112b988977055cd3a8e54971eba9cda5ca71", .result = .valid }, + .{ .key = "045eca1ef4c287dddc66b8bccf1b88e8a24c0018962f3c5e7efa83bc1a5ff6033e5e79c4cb2c245b8c45abdce8a8e4da758d92a607c32cd407ecaef22f1c934a71", .msg = "313233343030", .sig = "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0220788048ed39a5ffa77bfb62fa1fda2257742bf35d128fb3459f2a0c909ee86f91", .result = .valid }, + .{ .key = "045caaa030e7fdf0e4936bc7ab5a96353e0a01e4130c3f8bf22d473e317029a47adeb6adc462f7058f2a20d371e9702254e9b201642005b3ceda926b42b178bef9", .msg = "313233343030", .sig = "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0220476d9131fd381bd917d0fed112bc9e0a5924b5ed5b11167edd8b23582b3cb15e", .result = .valid }, + .{ .key = "04c2fd20bac06e555bb8ac0ce69eb1ea20f83a1fc3501c8a66469b1a31f619b0986237050779f52b615bd7b8d76a25fc95ca2ed32525c75f27ffc87ac397e6cbaf", .msg = "313233343030", .sig = "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0221008374253e3e21bd154448d0a8f640fe46fafa8b19ce78d538f6cc0a19662d3601", .result = .valid }, + .{ .key = "043fd6a1ca7f77fb3b0bbe726c372010068426e11ea6ae78ce17bedae4bba86ced03ce5516406bf8cfaab8745eac1cd69018ad6f50b5461872ddfc56e0db3c8ff4", .msg = "313233343030", .sig = "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0220357cfd3be4d01d413c5b9ede36cba5452c11ee7fe14879e749ae6a2d897a52d6", .result = .valid }, + .{ .key = "049cb8e51e27a5ae3b624a60d6dc32734e4989db20e9bca3ede1edf7b086911114b4c104ab3c677e4b36d6556e8ad5f523410a19f2e277aa895fc57322b4427544", .msg = "313233343030", .sig = "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022029798c5c0ee287d4a5e8e6b799fd86b8df5225298e6ffc807cd2f2bc27a0a6d8", .result = .valid }, + .{ .key = "04a3e52c156dcaf10502620b7955bc2b40bc78ef3d569e1223c262512d8f49602a4a2039f31c1097024ad3cc86e57321de032355463486164cf192944977df147f", .msg = "313233343030", .sig = "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd02200b70f22c781092452dca1a5711fa3a5a1f72add1bf52c2ff7cae4820b30078dd", .result = .valid }, + .{ .key = "04f19b78928720d5bee8e670fb90010fb15c37bf91b58a5157c3f3c059b2655e88cf701ec962fb4a11dcf273f5dc357e58468560c7cfeb942d074abd4329260509", .msg = "313233343030", .sig = "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022016e1e458f021248a5b9434ae23f474b43ee55ba37ea585fef95c90416600f1ba", .result = .valid }, + .{ .key = "0483a744459ecdfb01a5cf52b27a05bb7337482d242f235d7b4cb89345545c90a8c05d49337b9649813287de9ffe90355fd905df5f3c32945828121f37cc50de6e", .msg = "313233343030", .sig = "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd02202252d6856831b6cf895e4f0535eeaf0e5e5809753df848fe760ad86219016a97", .result = .valid }, + .{ .key = "04dd13c6b34c56982ddae124f039dfd23f4b19bbe88cee8e528ae51e5d6f3a21d7bfad4c2e6f263fe5eb59ca974d039fc0e4c3345692fb5320bdae4bd3b42a45ff", .msg = "313233343030", .sig = "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd02210081ffe55f178da695b28c86d8b406b15dab1a9e39661a3ae017fbe390ac0972c3", .result = .valid }, + .{ .key = "0467e6f659cdde869a2f65f094e94e5b4dfad636bbf95192feeed01b0f3deb7460a37e0a51f258b7aeb51dfe592f5cfd5685bbe58712c8d9233c62886437c38ba0", .msg = "313233343030", .sig = "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd02207fffffffaaaaaaaaffffffffffffffffe9a2538f37b28a2c513dee40fecbb71a", .result = .valid }, + .{ .key = "042eb6412505aec05c6545f029932087e490d05511e8ec1f599617bb367f9ecaaf805f51efcc4803403f9b1ae0124890f06a43fedcddb31830f6669af292895cb0", .msg = "313233343030", .sig = "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100b62f26b5f2a2b26f6de86d42ad8a13da3ab3cccd0459b201de009e526adf21f2", .result = .valid }, + .{ .key = "0484db645868eab35e3a9fd80e056e2e855435e3a6b68d75a50a854625fe0d7f356d2589ac655edc9a11ef3e075eddda9abf92e72171570ef7bf43a2ee39338cfe", .msg = "313233343030", .sig = "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100bb1d9ac949dd748cd02bbbe749bd351cd57b38bb61403d700686aa7b4c90851e", .result = .valid }, + .{ .key = "0491b9e47c56278662d75c0983b22ca8ea6aa5059b7a2ff7637eb2975e386ad66349aa8ff283d0f77c18d6d11dc062165fd13c3c0310679c1408302a16854ecfbd", .msg = "313233343030", .sig = "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022066755a00638cdaec1c732513ca0234ece52545dac11f816e818f725b4f60aaf2", .result = .valid }, + .{ .key = "04f3ec2f13caf04d0192b47fb4c5311fb6d4dc6b0a9e802e5327f7ec5ee8e4834df97e3e468b7d0db867d6ecfe81e2b0f9531df87efdb47c1338ac321fefe5a432", .msg = "313233343030", .sig = "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022055a00c9fcdaebb6032513ca0234ecfffe98ebe492fdf02e48ca48e982beb3669", .result = .valid }, + .{ .key = "04d92b200aefcab6ac7dafd9acaf2fa10b3180235b8f46b4503e4693c670fccc885ef2f3aebf5b317475336256768f7c19efb7352d27e4cccadc85b6b8ab922c72", .msg = "313233343030", .sig = "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100ab40193f9b5d76c064a27940469d9fffd31d7c925fbe05c919491d3057d66cd2", .result = .valid }, + .{ .key = "040a88361eb92ecca2625b38e5f98bbabb96bf179b3d76fc48140a3bcd881523cde6bdf56033f84a5054035597375d90866aa2c96b86a41ccf6edebf47298ad489", .msg = "313233343030", .sig = "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100ca0234ebb5fdcb13ca0234ecffffffffcb0dadbbc7f549f8a26b4408d0dc8600", .result = .valid }, + .{ .key = "04d0fb17ccd8fafe827e0c1afc5d8d80366e2b20e7f14a563a2ba50469d84375e868612569d39e2bb9f554355564646de99ac602cc6349cf8c1e236a7de7637d93", .msg = "313233343030", .sig = "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100bfffffff3ea3677e082b9310572620ae19933a9e65b285598711c77298815ad3", .result = .valid }, + .{ .key = "04836f33bbc1dc0d3d3abbcef0d91f11e2ac4181076c9af0a22b1e4309d3edb2769ab443ff6f901e30c773867582997c2bec2b0cb8120d760236f3a95bbe881f75", .msg = "313233343030", .sig = "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0220266666663bbbbbbbe6666666666666665b37902e023fab7c8f055d86e5cc41f4", .result = .valid }, + .{ .key = "0492f99fbe973ed4a299719baee4b432741237034dec8d72ba5103cb33e55feeb8033dd0e91134c734174889f3ebcf1b7a1ac05767289280ee7a794cebd6e69697", .msg = "313233343030", .sig = "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100bfffffff36db6db7a492492492492492146c573f4c6dfc8d08a443e258970b09", .result = .valid }, + .{ .key = "04d35ba58da30197d378e618ec0fa7e2e2d12cffd73ebbb2049d130bba434af09eff83986e6875e41ea432b7585a49b3a6c77cbb3c47919f8e82874c794635c1d2", .msg = "313233343030", .sig = "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100bfffffff2aaaaaab7fffffffffffffffc815d0e60b3e596ecb1ad3a27cfd49c4", .result = .valid }, + .{ .key = "048651ce490f1b46d73f3ff475149be29136697334a519d7ddab0725c8d0793224e11c65bd8ca92dc8bc9ae82911f0b52751ce21dd9003ae60900bd825f590cc28", .msg = "313233343030", .sig = "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd02207fffffff55555555ffffffffffffffffd344a71e6f651458a27bdc81fd976e37", .result = .valid }, + .{ .key = "046d8e1b12c831a0da8795650ff95f101ed921d9e2f72b15b1cdaca9826b9cfc6def6d63e2bc5c089570394a4bc9f892d5e6c7a6a637b20469a58c106ad486bf37", .msg = "313233343030", .sig = "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd02203fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192aa", .result = .valid }, + .{ .key = "040ae580bae933b4ef2997cbdbb0922328ca9a410f627a0f7dff24cb4d920e15428911e7f8cc365a8a88eb81421a361ccc2b99e309d8dcd9a98ba83c3949d893e3", .msg = "313233343030", .sig = "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd02205d8ecd64a4eeba466815ddf3a4de9a8e6abd9c5db0a01eb80343553da648428f", .result = .valid }, + .{ .key = "045b812fd521aafa69835a849cce6fbdeb6983b442d2444fe70e134c027fc46963838a40f2a36092e9004e92d8d940cf5638550ce672ce8b8d4e15eba5499249e9", .msg = "313233343030", .sig = "304502206f2347cab7dd76858fe0555ac3bc99048c4aacafdfb6bcbe05ea6c42c4934569022100bb726660235793aa9957a61e76e00c2c435109cf9a15dd624d53f4301047856b", .result = .valid }, + .{ .key = "045b812fd521aafa69835a849cce6fbdeb6983b442d2444fe70e134c027fc469637c75bf0c5c9f6d17ffb16d2726bf30a9c7aaf31a8d317472b1ea145ab66db616", .msg = "313233343030", .sig = "304502206f2347cab7dd76858fe0555ac3bc99048c4aacafdfb6bcbe05ea6c42c4934569022100bb726660235793aa9957a61e76e00c2c435109cf9a15dd624d53f4301047856b", .result = .invalid }, + .{ .key = "046adda82b90261b0f319faa0d878665a6b6da497f09c903176222c34acfef72a647e6f50dcc40ad5d9b59f7602bb222fad71a41bf5e1f9df4959a364c62e488d9", .msg = "313233343030", .sig = "30250201010220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70", .result = .invalid }, + .{ .key = "042fca0d0a47914de77ed56e7eccc3276a601120c6df0069c825c8f6a01c9f382065f3450a1d17c6b24989a39beb1c7decfca8384fbdc294418e5d807b3c6ed7de", .msg = "313233343030", .sig = "3045022101000000000000000000000000000000000000000000000000000000000000000002203333333300000000333333333333333325c7cbbc549e52e763f1f55a327a3aa9", .result = .invalid }, + .{ .key = "04dd86d3b5f4a13e8511083b78002081c53ff467f11ebd98a51a633db76665d25045d5c8200c89f2fa10d849349226d21d8dfaed6ff8d5cb3e1b7e17474ebc18f7", .msg = "313233343030", .sig = "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c7002203333333300000000333333333333333325c7cbbc549e52e763f1f55a327a3aa9", .result = .invalid }, + .{ .key = "044fea55b32cb32aca0c12c4cd0abfb4e64b0f5a516e578c016591a93f5a0fbcc5d7d3fd10b2be668c547b212f6bb14c88f0fecd38a8a4b2c785ed3be62ce4b280", .msg = "313233343030", .sig = "304402207cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc476699780220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70", .result = .valid }, + .{ .key = "04c6a771527024227792170a6f8eee735bf32b7f98af669ead299802e32d7c3107bc3b4b5e65ab887bbd343572b3e5619261fe3a073e2ffd78412f726867db589e", .msg = "313233343030", .sig = "304502207cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978022100b6db6db6249249254924924924924924625bd7a09bec4ca81bcdd9f8fd6b63cc", .result = .valid }, + .{ .key = "04851c2bbad08e54ec7a9af99f49f03644d6ec6d59b207fec98de85a7d15b956efcee9960283045075684b410be8d0f7494b91aa2379f60727319f10ddeb0fe9d6", .msg = "313233343030", .sig = "304502207cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978022100cccccccc00000000cccccccccccccccc971f2ef152794b9d8fc7d568c9e8eaa7", .result = .valid }, + .{ .key = "04f6417c8a670584e388676949e53da7fc55911ff68318d1bf3061205acb19c48f8f2b743df34ad0f72674acb7505929784779cd9ac916c3669ead43026ab6d43f", .msg = "313233343030", .sig = "304402207cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc4766997802203333333300000000333333333333333325c7cbbc549e52e763f1f55a327a3aaa", .result = .valid }, + .{ .key = "04501421277be45a5eefec6c639930d636032565af420cf3373f557faa7f8a06438673d6cb6076e1cfcdc7dfe7384c8e5cac08d74501f2ae6e89cad195d0aa1371", .msg = "313233343030", .sig = "304402207cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978022049249248db6db6dbb6db6db6db6db6db5a8b230d0b2b51dcd7ebf0c9fef7c185", .result = .valid }, + .{ .key = "040d935bf9ffc115a527735f729ca8a4ca23ee01a4894adf0e3415ac84e808bb343195a3762fea29ed38912bd9ea6c4fde70c3050893a4375850ce61d82eba33c5", .msg = "313233343030", .sig = "304402207cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978022016a4502e2781e11ac82cbc9d1edd8c981584d13e18411e2f6e0478c34416e3bb", .result = .valid }, + .{ .key = "045e59f50708646be8a589355014308e60b668fb670196206c41e748e64e4dca215de37fee5c97bcaf7144d5b459982f52eeeafbdf03aacbafef38e213624a01de", .msg = "313233343030", .sig = "304402206b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2960220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70", .result = .valid }, + .{ .key = "04169fb797325843faff2f7a5b5445da9e2fd6226f7ef90ef0bfe924104b02db8e7bbb8de662c7b9b1cf9b22f7a2e582bd46d581d68878efb2b861b131d8a1d667", .msg = "313233343030", .sig = "304502206b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296022100b6db6db6249249254924924924924924625bd7a09bec4ca81bcdd9f8fd6b63cc", .result = .valid }, + .{ .key = "04271cd89c000143096b62d4e9e4ca885aef2f7023d18affdaf8b7b548981487540a1c6e954e32108435b55fa385b0f76481a609b9149ccb4b02b2ca47fe8e4da5", .msg = "313233343030", .sig = "304502206b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296022100cccccccc00000000cccccccccccccccc971f2ef152794b9d8fc7d568c9e8eaa7", .result = .valid }, + .{ .key = "043d0bc7ed8f09d2cb7ddb46ebc1ed799ab1563a9ab84bf524587a220afe499c12e22dc3b3c103824a4f378d96adb0a408abf19ce7d68aa6244f78cb216fa3f8df", .msg = "313233343030", .sig = "304402206b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c29602203333333300000000333333333333333325c7cbbc549e52e763f1f55a327a3aaa", .result = .valid }, + .{ .key = "04a6c885ade1a4c566f9bb010d066974abb281797fa701288c721bcbd23663a9b72e424b690957168d193a6096fc77a2b004a9c7d467e007e1f2058458f98af316", .msg = "313233343030", .sig = "304402206b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296022049249248db6db6dbb6db6db6db6db6db5a8b230d0b2b51dcd7ebf0c9fef7c185", .result = .valid }, + .{ .key = "048d3c2c2c3b765ba8289e6ac3812572a25bf75df62d87ab7330c3bdbad9ebfa5c4c6845442d66935b238578d43aec54f7caa1621d1af241d4632e0b780c423f5d", .msg = "313233343030", .sig = "304402206b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296022016a4502e2781e11ac82cbc9d1edd8c981584d13e18411e2f6e0478c34416e3bb", .result = .valid }, + .{ .key = "046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", .msg = "313233343030", .sig = "3045022100bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca6050230220249249246db6db6ddb6db6db6db6db6dad4591868595a8ee6bf5f864ff7be0c2", .result = .invalid }, + .{ .key = "046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", .msg = "313233343030", .sig = "3044022044a5ad0ad0636d9f12bc9e0a6bdd5e1cbcb012ea7bf091fcec15b0c43202d52e0220249249246db6db6ddb6db6db6db6db6dad4591868595a8ee6bf5f864ff7be0c2", .result = .invalid }, + .{ .key = "046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296b01cbd1c01e58065711814b583f061e9d431cca994cea1313449bf97c840ae0a", .msg = "313233343030", .sig = "3045022100bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca6050230220249249246db6db6ddb6db6db6db6db6dad4591868595a8ee6bf5f864ff7be0c2", .result = .invalid }, + .{ .key = "046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296b01cbd1c01e58065711814b583f061e9d431cca994cea1313449bf97c840ae0a", .msg = "313233343030", .sig = "3044022044a5ad0ad0636d9f12bc9e0a6bdd5e1cbcb012ea7bf091fcec15b0c43202d52e0220249249246db6db6ddb6db6db6db6db6dad4591868595a8ee6bf5f864ff7be0c2", .result = .invalid }, + .{ .key = "0404aaec73635726f213fb8a9e64da3b8632e41495a944d0045b522eba7240fad587d9315798aaa3a5ba01775787ced05eaaf7b4e09fc81d6d1aa546e8365d525d", .msg = "", .sig = "3045022100b292a619339f6e567a305c951c0dcbcc42d16e47f219f9e98e76e09d8770b34a02200177e60492c5a8242f76f07bfe3661bde59ec2a17ce5bd2dab2abebdf89a62e2", .result = .valid }, + .{ .key = "0404aaec73635726f213fb8a9e64da3b8632e41495a944d0045b522eba7240fad587d9315798aaa3a5ba01775787ced05eaaf7b4e09fc81d6d1aa546e8365d525d", .msg = "4d7367", .sig = "30450220530bd6b0c9af2d69ba897f6b5fb59695cfbf33afe66dbadcf5b8d2a2a6538e23022100d85e489cb7a161fd55ededcedbf4cc0c0987e3e3f0f242cae934c72caa3f43e9", .result = .valid }, + .{ .key = "0404aaec73635726f213fb8a9e64da3b8632e41495a944d0045b522eba7240fad587d9315798aaa3a5ba01775787ced05eaaf7b4e09fc81d6d1aa546e8365d525d", .msg = "313233343030", .sig = "3046022100a8ea150cb80125d7381c4c1f1da8e9de2711f9917060406a73d7904519e51388022100f3ab9fa68bd47973a73b2d40480c2ba50c22c9d76ec217257288293285449b86", .result = .valid }, + .{ .key = "0404aaec73635726f213fb8a9e64da3b8632e41495a944d0045b522eba7240fad587d9315798aaa3a5ba01775787ced05eaaf7b4e09fc81d6d1aa546e8365d525d", .msg = "0000000000000000000000000000000000000000", .sig = "3045022100986e65933ef2ed4ee5aada139f52b70539aaf63f00a91f29c69178490d57fb7102203dafedfb8da6189d372308cbf1489bbbdabf0c0217d1c0ff0f701aaa7a694b9c", .result = .valid }, + .{ .key = "044f337ccfd67726a805e4f1600ae2849df3807eca117380239fbd816900000000ed9dea124cc8c396416411e988c30f427eb504af43a3146cd5df7ea60666d685", .msg = "4d657373616765", .sig = "3046022100d434e262a49eab7781e353a3565e482550dd0fd5defa013c7f29745eff3569f10221009b0c0a93f267fb6052fd8077be769c2b98953195d7bc10de844218305c6ba17a", .result = .valid }, + .{ .key = "044f337ccfd67726a805e4f1600ae2849df3807eca117380239fbd816900000000ed9dea124cc8c396416411e988c30f427eb504af43a3146cd5df7ea60666d685", .msg = "4d657373616765", .sig = "304402200fe774355c04d060f76d79fd7a772e421463489221bf0a33add0be9b1979110b0220500dcba1c69a8fbd43fa4f57f743ce124ca8b91a1f325f3fac6181175df55737", .result = .valid }, + .{ .key = "044f337ccfd67726a805e4f1600ae2849df3807eca117380239fbd816900000000ed9dea124cc8c396416411e988c30f427eb504af43a3146cd5df7ea60666d685", .msg = "4d657373616765", .sig = "3045022100bb40bf217bed3fb3950c7d39f03d36dc8e3b2cd79693f125bfd06595ee1135e30220541bf3532351ebb032710bdb6a1bf1bfc89a1e291ac692b3fa4780745bb55677", .result = .valid }, + .{ .key = "043cf03d614d8939cfd499a07873fac281618f06b8ff87e8015c3f49726500493584fa174d791c72bf2ce3880a8960dd2a7c7a1338a82f85a9e59cdbde80000000", .msg = "4d657373616765", .sig = "30440220664eb7ee6db84a34df3c86ea31389a5405badd5ca99231ff556d3e75a233e73a022059f3c752e52eca46137642490a51560ce0badc678754b8f72e51a2901426a1bd", .result = .valid }, + .{ .key = "043cf03d614d8939cfd499a07873fac281618f06b8ff87e8015c3f49726500493584fa174d791c72bf2ce3880a8960dd2a7c7a1338a82f85a9e59cdbde80000000", .msg = "4d657373616765", .sig = "304502204cd0429bbabd2827009d6fcd843d4ce39c3e42e2d1631fd001985a79d1fd8b430221009638bf12dd682f60be7ef1d0e0d98f08b7bca77a1a2b869ae466189d2acdabe3", .result = .valid }, + .{ .key = "043cf03d614d8939cfd499a07873fac281618f06b8ff87e8015c3f49726500493584fa174d791c72bf2ce3880a8960dd2a7c7a1338a82f85a9e59cdbde80000000", .msg = "4d657373616765", .sig = "3046022100e56c6ea2d1b017091c44d8b6cb62b9f460e3ce9aed5e5fd41e8added97c56c04022100a308ec31f281e955be20b457e463440b4fcf2b80258078207fc1378180f89b55", .result = .valid }, + .{ .key = "043cf03d614d8939cfd499a07873fac281618f06b8ff87e8015c3f4972650049357b05e8b186e38d41d31c77f5769f22d58385ecc857d07a561a6324217fffffff", .msg = "4d657373616765", .sig = "304402201158a08d291500b4cabed3346d891eee57c176356a2624fb011f8fbbf34668300220228a8c486a736006e082325b85290c5bc91f378b75d487dda46798c18f285519", .result = .valid }, + .{ .key = "043cf03d614d8939cfd499a07873fac281618f06b8ff87e8015c3f4972650049357b05e8b186e38d41d31c77f5769f22d58385ecc857d07a561a6324217fffffff", .msg = "4d657373616765", .sig = "3045022100b1db9289649f59410ea36b0c0fc8d6aa2687b29176939dd23e0dde56d309fa9d02203e1535e4280559015b0dbd987366dcf43a6d1af5c23c7d584e1c3f48a1251336", .result = .valid }, + .{ .key = "043cf03d614d8939cfd499a07873fac281618f06b8ff87e8015c3f4972650049357b05e8b186e38d41d31c77f5769f22d58385ecc857d07a561a6324217fffffff", .msg = "4d657373616765", .sig = "3046022100b7b16e762286cb96446aa8d4e6e7578b0a341a79f2dd1a220ac6f0ca4e24ed86022100ddc60a700a139b04661c547d07bbb0721780146df799ccf55e55234ecb8f12bc", .result = .valid }, + .{ .key = "042829c31faa2e400e344ed94bca3fcd0545956ebcfe8ad0f6dfa5ff8effffffffa01aafaf000e52585855afa7676ade284113099052df57e7eb3bd37ebeb9222e", .msg = "4d657373616765", .sig = "3045022100d82a7c2717261187c8e00d8df963ff35d796edad36bc6e6bd1c91c670d9105b402203dcabddaf8fcaa61f4603e7cbac0f3c0351ecd5988efb23f680d07debd139929", .result = .valid }, + .{ .key = "042829c31faa2e400e344ed94bca3fcd0545956ebcfe8ad0f6dfa5ff8effffffffa01aafaf000e52585855afa7676ade284113099052df57e7eb3bd37ebeb9222e", .msg = "4d657373616765", .sig = "304402205eb9c8845de68eb13d5befe719f462d77787802baff30ce96a5cba063254af7802202c026ae9be2e2a5e7ca0ff9bbd92fb6e44972186228ee9a62b87ddbe2ef66fb5", .result = .valid }, + .{ .key = "042829c31faa2e400e344ed94bca3fcd0545956ebcfe8ad0f6dfa5ff8effffffffa01aafaf000e52585855afa7676ade284113099052df57e7eb3bd37ebeb9222e", .msg = "4d657373616765", .sig = "304602210096843dd03c22abd2f3b782b170239f90f277921becc117d0404a8e4e36230c28022100f2be378f526f74a543f67165976de9ed9a31214eb4d7e6db19e1ede123dd991d", .result = .valid }, + .{ .key = "04fffffff948081e6a0458dd8f9e738f2665ff9059ad6aac0708318c4ca9a7a4f55a8abcba2dda8474311ee54149b973cae0c0fb89557ad0bf78e6529a1663bd73", .msg = "4d657373616765", .sig = "30440220766456dce1857c906f9996af729339464d27e9d98edc2d0e3b760297067421f60220402385ecadae0d8081dccaf5d19037ec4e55376eced699e93646bfbbf19d0b41", .result = .valid }, + .{ .key = "04fffffff948081e6a0458dd8f9e738f2665ff9059ad6aac0708318c4ca9a7a4f55a8abcba2dda8474311ee54149b973cae0c0fb89557ad0bf78e6529a1663bd73", .msg = "4d657373616765", .sig = "3046022100c605c4b2edeab20419e6518a11b2dbc2b97ed8b07cced0b19c34f777de7b9fd9022100edf0f612c5f46e03c719647bc8af1b29b2cde2eda700fb1cff5e159d47326dba", .result = .valid }, + .{ .key = "04fffffff948081e6a0458dd8f9e738f2665ff9059ad6aac0708318c4ca9a7a4f55a8abcba2dda8474311ee54149b973cae0c0fb89557ad0bf78e6529a1663bd73", .msg = "4d657373616765", .sig = "3046022100d48b68e6cabfe03cf6141c9ac54141f210e64485d9929ad7b732bfe3b7eb8a84022100feedae50c61bd00e19dc26f9b7e2265e4508c389109ad2f208f0772315b6c941", .result = .valid }, + .{ .key = "0400000003fa15f963949d5f03a6f5c7f86f9e0015eeb23aebbff1173937ba748e1099872070e8e87c555fa13659cca5d7fadcfcb0023ea889548ca48af2ba7e71", .msg = "4d657373616765", .sig = "3046022100b7c81457d4aeb6aa65957098569f0479710ad7f6595d5874c35a93d12a5dd4c7022100b7961a0b652878c2d568069a432ca18a1a9199f2ca574dad4b9e3a05c0a1cdb3", .result = .valid }, + .{ .key = "0400000003fa15f963949d5f03a6f5c7f86f9e0015eeb23aebbff1173937ba748e1099872070e8e87c555fa13659cca5d7fadcfcb0023ea889548ca48af2ba7e71", .msg = "4d657373616765", .sig = "304402206b01332ddb6edfa9a30a1321d5858e1ee3cf97e263e669f8de5e9652e76ff3f702205939545fced457309a6a04ace2bd0f70139c8f7d86b02cb1cc58f9e69e96cd5a", .result = .valid }, + .{ .key = "0400000003fa15f963949d5f03a6f5c7f86f9e0015eeb23aebbff1173937ba748e1099872070e8e87c555fa13659cca5d7fadcfcb0023ea889548ca48af2ba7e71", .msg = "4d657373616765", .sig = "3046022100efdb884720eaeadc349f9fc356b6c0344101cd2fd8436b7d0e6a4fb93f106361022100f24bee6ad5dc05f7613975473aadf3aacba9e77de7d69b6ce48cb60d8113385d", .result = .valid }, + .{ .key = "04bcbb2914c79f045eaa6ecbbc612816b3be5d2d6796707d8125e9f851c18af015000000001352bb4a0fa2ea4cceb9ab63dd684ade5a1127bcf300a698a7193bc2", .msg = "4d657373616765", .sig = "3044022031230428405560dcb88fb5a646836aea9b23a23dd973dcbe8014c87b8b20eb0702200f9344d6e812ce166646747694a41b0aaf97374e19f3c5fb8bd7ae3d9bd0beff", .result = .valid }, + .{ .key = "04bcbb2914c79f045eaa6ecbbc612816b3be5d2d6796707d8125e9f851c18af015000000001352bb4a0fa2ea4cceb9ab63dd684ade5a1127bcf300a698a7193bc2", .msg = "4d657373616765", .sig = "3046022100caa797da65b320ab0d5c470cda0b36b294359c7db9841d679174db34c4855743022100cf543a62f23e212745391aaf7505f345123d2685ee3b941d3de6d9b36242e5a0", .result = .valid }, + .{ .key = "04bcbb2914c79f045eaa6ecbbc612816b3be5d2d6796707d8125e9f851c18af015000000001352bb4a0fa2ea4cceb9ab63dd684ade5a1127bcf300a698a7193bc2", .msg = "4d657373616765", .sig = "304502207e5f0ab5d900d3d3d7867657e5d6d36519bc54084536e7d21c336ed8001859450221009450c07f201faec94b82dfb322e5ac676688294aad35aa72e727ff0b19b646aa", .result = .valid }, + .{ .key = "04bcbb2914c79f045eaa6ecbbc612816b3be5d2d6796707d8125e9f851c18af015fffffffeecad44b6f05d15b33146549c2297b522a5eed8430cff596758e6c43d", .msg = "4d657373616765", .sig = "3046022100d7d70c581ae9e3f66dc6a480bf037ae23f8a1e4a2136fe4b03aa69f0ca25b35602210089c460f8a5a5c2bbba962c8a3ee833a413e85658e62a59e2af41d9127cc47224", .result = .valid }, + .{ .key = "04bcbb2914c79f045eaa6ecbbc612816b3be5d2d6796707d8125e9f851c18af015fffffffeecad44b6f05d15b33146549c2297b522a5eed8430cff596758e6c43d", .msg = "4d657373616765", .sig = "30440220341c1b9ff3c83dd5e0dfa0bf68bcdf4bb7aa20c625975e5eeee34bb396266b34022072b69f061b750fd5121b22b11366fad549c634e77765a017902a67099e0a4469", .result = .valid }, + .{ .key = "04bcbb2914c79f045eaa6ecbbc612816b3be5d2d6796707d8125e9f851c18af015fffffffeecad44b6f05d15b33146549c2297b522a5eed8430cff596758e6c43d", .msg = "4d657373616765", .sig = "3045022070bebe684cdcb5ca72a42f0d873879359bd1781a591809947628d313a3814f67022100aec03aca8f5587a4d535fa31027bbe9cc0e464b1c3577f4c2dcde6b2094798a9", .result = .valid }, + }; + for (vectors) |vector| { + if (tvTry(vector)) { + try std.testing.expect(vector.result == .valid or vector.result == .acceptable); + } else |_| { + try std.testing.expectEqual(vector.result, .invalid); + } + } +} + +fn tvTry(vector: TestVector) !void { + const Scheme = EcdsaP256Sha256; + var key_sec1_: [Scheme.PublicKey.uncompressed_sec1_encoded_length]u8 = undefined; + const key_sec1 = try fmt.hexToBytes(&key_sec1_, vector.key); + const pk = try Scheme.PublicKey.fromSec1(key_sec1); + var msg_: [20]u8 = undefined; + const msg = try fmt.hexToBytes(&msg_, vector.msg); + var sig_der_: [152]u8 = undefined; + const sig_der = try fmt.hexToBytes(&sig_der_, vector.sig); + const sig = try Scheme.Signature.fromDer(sig_der); + try sig.verify(msg, pk); +} From d506275a06dd5e9ad8be63f9e44abb7d8bea88b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Thu, 16 Jun 2022 14:55:45 +0300 Subject: [PATCH 1841/2031] [elf] understand -no-pie This passes -Wl,-no-pie linker arg. Golang uses that. From the `ld(1)` man page: Create a position dependent executable. This is the default. Not adding to the help text, because this is the default. --- src/main.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.zig b/src/main.zig index 39237d1625..cfd802d11c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1445,6 +1445,8 @@ fn buildOutputType( needed = false; } else if (mem.eql(u8, linker_arg, "--no-as-needed")) { needed = true; + } else if (mem.eql(u8, linker_arg, "-no-pie")) { + want_pie = false; } else if (mem.eql(u8, linker_arg, "--whole-archive") or mem.eql(u8, linker_arg, "-whole-archive")) { From ffa6f895fff52846bc4b82cc9b449d0f7224d7d9 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 13 Jun 2022 11:40:20 +0300 Subject: [PATCH 1842/2031] Sema: validateArrayInit detect bitcast before store --- src/Sema.zig | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index a769194776..af3e824855 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3795,6 +3795,32 @@ fn zirValidateArrayInit( } continue; }, + .bitcast => { + // %a = bitcast(*arr_ty, %array_base) + // %b = ptr_elem_ptr(%a, %index) + // %c = bitcast(*elem_ty, %b) + // %d = store(%c, %val) + if (air_datas[next_air_inst].ty_op.operand != elem_ptr_air_ref) { + array_is_comptime = false; + continue; + } + const store_inst = block.instructions.items[block_index + 2]; + if (air_tags[store_inst] != .store) { + array_is_comptime = false; + continue; + } + const bin_op = air_datas[store_inst].bin_op; + if (bin_op.lhs != Air.indexToRef(next_air_inst)) { + array_is_comptime = false; + continue; + } + if (try sema.resolveMaybeUndefValAllowVariables(block, elem_src, bin_op.rhs)) |val| { + element_vals[i] = val; + } else { + array_is_comptime = false; + } + continue; + }, else => { array_is_comptime = false; continue; From b9dcbe6b4c0bee37b31bf46424f284af331d4696 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 15 Jun 2022 18:00:29 +0300 Subject: [PATCH 1843/2031] Sema: handle sentinels in tupleToArray --- src/Sema.zig | 12 +++++++++--- test/behavior/array.zig | 11 +++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index af3e824855..1d5342b841 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -21793,7 +21793,7 @@ fn coerceTupleToArray( ) !Air.Inst.Ref { const inst_ty = sema.typeOf(inst); const inst_len = inst_ty.arrayLen(); - const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen()); + const dest_len = dest_ty.arrayLen(); if (dest_len != inst_len) { const msg = msg: { @@ -21808,13 +21808,19 @@ fn coerceTupleToArray( return sema.failWithOwnedErrorMsg(block, msg); } - const element_vals = try sema.arena.alloc(Value, dest_len); - const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len); + const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLenIncludingSentinel()); + const element_vals = try sema.arena.alloc(Value, dest_elems); + const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems); const dest_elem_ty = dest_ty.childType(); var runtime_src: ?LazySrcLoc = null; for (element_vals) |*elem, i_usize| { const i = @intCast(u32, i_usize); + if (i_usize == inst_len) { + elem.* = dest_ty.sentinel().?; + element_refs[i] = try sema.addConstant(dest_elem_ty, elem.*); + break; + } const elem_src = inst_src; // TODO better source location const elem_ref = try tupleField(sema, block, inst_src, inst, elem_src, i); const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); diff --git a/test/behavior/array.zig b/test/behavior/array.zig index bccff1edf0..6482bfa1fe 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -582,3 +582,14 @@ test "array with comptime only element type" { try testing.expect(a[0] == u32); try testing.expect(a[1] == i32); } + +test "tuple to array handles sentinel" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + const a = .{ 1, 2, 3 }; + var b: [3:0]u8 = a; + }; + try expect(S.b[0] == 1); +} From 69e2cac0d349cc32d4ead73541e18d1f72aac729 Mon Sep 17 00:00:00 2001 From: Omar Alhammadi Date: Fri, 17 Jun 2022 20:04:51 +0400 Subject: [PATCH 1844/2031] stage2: comptime @bitCast packed struct bug fix --- src/value.zig | 10 +++++----- test/behavior/bitcast.zig | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/value.zig b/src/value.zig index 996aa76bf5..543691bbb0 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1283,11 +1283,11 @@ pub const Value = extern union { const field_val = field_vals[i]; const field_bigint_const = switch (field.ty.zigTypeTag()) { .Float => switch (field.ty.floatBits(target)) { - 16 => bitcastFloatToBigInt(f16, val.toFloat(f16), &field_buf), - 32 => bitcastFloatToBigInt(f32, val.toFloat(f32), &field_buf), - 64 => bitcastFloatToBigInt(f64, val.toFloat(f64), &field_buf), - 80 => bitcastFloatToBigInt(f80, val.toFloat(f80), &field_buf), - 128 => bitcastFloatToBigInt(f128, val.toFloat(f128), &field_buf), + 16 => bitcastFloatToBigInt(f16, field_val.toFloat(f16), &field_buf), + 32 => bitcastFloatToBigInt(f32, field_val.toFloat(f32), &field_buf), + 64 => bitcastFloatToBigInt(f64, field_val.toFloat(f64), &field_buf), + 80 => bitcastFloatToBigInt(f80, field_val.toFloat(f80), &field_buf), + 128 => bitcastFloatToBigInt(f128, field_val.toFloat(f128), &field_buf), else => unreachable, }, .Int, .Bool => field_val.toBigInt(&field_space, target), diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index e851d7de09..b0c66fd92b 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -264,3 +264,38 @@ test "triple level result location with bitcast sandwich passed as tuple element }; try S.foo(.{@as(f64, @bitCast(f32, @as(u32, 0x414570A4)))}); } + +test "@bitCast packed struct of floats" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + + const Foo = packed struct { + a: f16 = 0, + b: f32 = 1, + c: f64 = 2, + d: f128 = 3, + }; + + const Foo2 = packed struct { + a: f16 = 0, + b: f32 = 1, + c: f64 = 2, + d: f128 = 3, + }; + + const S = struct { + fn doTheTest() !void { + var foo = Foo{}; + var v = @bitCast(Foo2, foo); + try expect(v.a == foo.a); + try expect(v.b == foo.b); + try expect(v.c == foo.c); + try expect(v.d == foo.d); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} From b66247c97af3deaa190d1ca8297166f932b022ff Mon Sep 17 00:00:00 2001 From: Xavier Bouchoux Date: Fri, 10 Jun 2022 22:26:19 +0000 Subject: [PATCH 1845/2031] stage2: coerce tuple to vector --- src/Sema.zig | 5 +++++ test/behavior/vector.zig | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index a769194776..2b97c0624e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -19988,6 +19988,11 @@ fn coerce( }, .Vector => switch (inst_ty.zigTypeTag()) { .Array, .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src), + .Struct => { + if (inst_ty.isTuple()) { + return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); + } + }, .Undefined => { return sema.addConstUndef(dest_ty); }, diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index cbd8787701..fc49bce6e2 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -167,6 +167,31 @@ test "array to vector" { comptime try S.doTheTest(); } +test "tuple to vector" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + const Vec3 = @Vector(3, i32); + var v: Vec3 = .{ 1, 0, 0 }; + for ([_]Vec3{ .{ 0, 1, 0 }, .{ 0, 0, 1 } }) |it| { + v += it; + } + + try std.testing.expectEqual(v, Vec3{ 1, 1, 1 }); + if (builtin.zig_backend != .stage1) { + try std.testing.expectEqual(v, .{ 1, 1, 1 }); + } + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + test "vector casts of sizes not divisible by 8" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO From a224dfceee07a94d961506122a434563129428a5 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 15 Jun 2022 18:34:56 +0300 Subject: [PATCH 1846/2031] std.tz: fix function returning pointer to local variable --- lib/std/tz.zig | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/std/tz.zig b/lib/std/tz.zig index cea845e148..76eb3b06fc 100644 --- a/lib/std/tz.zig +++ b/lib/std/tz.zig @@ -11,7 +11,7 @@ pub const Timetype = struct { flags: u8, name_data: [6:0]u8, - pub fn name(self: Timetype) [:0]const u8 { + pub fn name(self: *const Timetype) [:0]const u8 { return std.mem.sliceTo(self.name_data[0..], 0); } @@ -214,7 +214,6 @@ pub const Tz = struct { }; test "slim" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const data = @embedFile("tz/asia_tokyo.tzif"); var in_stream = std.io.fixedBufferStream(data); @@ -228,7 +227,6 @@ test "slim" { } test "fat" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const data = @embedFile("tz/antarctica_davis.tzif"); var in_stream = std.io.fixedBufferStream(data); @@ -241,7 +239,6 @@ test "fat" { } test "legacy" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO // Taken from Slackware 8.0, from 2001 const data = @embedFile("tz/europe_vatican.tzif"); var in_stream = std.io.fixedBufferStream(data); From 28986a059075c2dce662c2f4e823b3efd283df87 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 17 Jun 2022 18:53:40 +0300 Subject: [PATCH 1847/2031] stage2: check that struct is a tuple when value tags differ in eql --- lib/std/bit_set.zig | 4 ---- src/value.zig | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/std/bit_set.zig b/lib/std/bit_set.zig index 9b0079ec74..8006a623b5 100644 --- a/lib/std/bit_set.zig +++ b/lib/std/bit_set.zig @@ -1330,7 +1330,6 @@ fn testStaticBitSet(comptime Set: type) !void { } test "IntegerBitSet" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO try testStaticBitSet(IntegerBitSet(0)); try testStaticBitSet(IntegerBitSet(1)); try testStaticBitSet(IntegerBitSet(2)); @@ -1342,7 +1341,6 @@ test "IntegerBitSet" { } test "ArrayBitSet" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO if (@import("builtin").cpu.arch == .aarch64) { // https://github.com/ziglang/zig/issues/9879 return error.SkipZigTest; @@ -1357,7 +1355,6 @@ test "ArrayBitSet" { } test "DynamicBitSetUnmanaged" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const allocator = std.testing.allocator; var a = try DynamicBitSetUnmanaged.initEmpty(allocator, 300); try testing.expectEqual(@as(usize, 0), a.count()); @@ -1398,7 +1395,6 @@ test "DynamicBitSetUnmanaged" { } test "DynamicBitSet" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const allocator = std.testing.allocator; var a = try DynamicBitSet.initEmpty(allocator, 300); try testing.expectEqual(@as(usize, 0), a.count()); diff --git a/src/value.zig b/src/value.zig index 996aa76bf5..2239bb7b0e 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2186,7 +2186,7 @@ pub const Value = extern union { // A tuple can be represented with .empty_struct_value, // the_one_possible_value, .aggregate in which case we could // end up here and the values are equal if the type has zero fields. - return ty.structFieldCount() != 0; + return ty.isTupleOrAnonStruct() and ty.structFieldCount() != 0; }, .Float => { const a_nan = a.isNan(); From 80790be3094f65877231209532c04f7fdb4441a7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 10 Jun 2022 10:25:59 +0200 Subject: [PATCH 1848/2031] compiler_rt: compile each unit separately for improved archiving --- lib/compiler_rt.zig | 933 ++------------------------- lib/compiler_rt/absv.zig | 10 + lib/compiler_rt/addXf3.zig | 59 +- lib/compiler_rt/addo.zig | 10 + lib/compiler_rt/arm.zig | 72 +++ lib/compiler_rt/atomics.zig | 2 +- lib/compiler_rt/aulldiv.zig | 15 + lib/compiler_rt/bswap.zig | 9 + lib/compiler_rt/ceil.zig | 23 + lib/compiler_rt/clear_cache.zig | 9 +- lib/compiler_rt/cmp.zig | 12 + lib/compiler_rt/common.zig | 144 +++++ lib/compiler_rt/compareXf2.zig | 144 ++++- lib/compiler_rt/cos.zig | 23 + lib/compiler_rt/count0bits.zig | 15 + lib/compiler_rt/divdf3.zig | 137 +--- lib/compiler_rt/divsf3.zig | 28 +- lib/compiler_rt/divtf3.zig | 23 +- lib/compiler_rt/divti3.zig | 28 +- lib/compiler_rt/divxf3.zig | 14 +- lib/compiler_rt/emutls.zig | 14 +- lib/compiler_rt/exp.zig | 23 + lib/compiler_rt/exp2.zig | 23 + lib/compiler_rt/extendXfYf2.zig | 40 +- lib/compiler_rt/extend_f80.zig | 21 +- lib/compiler_rt/fabs.zig | 23 + lib/compiler_rt/fixXfYi.zig | 90 ++- lib/compiler_rt/floatXiYf.zig | 91 ++- lib/compiler_rt/floor.zig | 23 + lib/compiler_rt/fma.zig | 23 + lib/compiler_rt/fmax.zig | 23 + lib/compiler_rt/fmin.zig | 23 + lib/compiler_rt/fmod.zig | 26 +- lib/compiler_rt/int.zig | 35 + lib/compiler_rt/log.zig | 23 + lib/compiler_rt/log10.zig | 23 + lib/compiler_rt/log2.zig | 23 + lib/compiler_rt/modti3.zig | 29 +- lib/compiler_rt/mulXf3.zig | 28 +- lib/compiler_rt/muldi3.zig | 21 +- lib/compiler_rt/mulo.zig | 11 +- lib/compiler_rt/multi3.zig | 24 +- lib/compiler_rt/negXf2.zig | 17 + lib/compiler_rt/negXi2.zig | 9 + lib/compiler_rt/negv.zig | 11 + lib/compiler_rt/os_version_check.zig | 58 +- lib/compiler_rt/parity.zig | 9 + lib/compiler_rt/popcount.zig | 9 + lib/compiler_rt/round.zig | 23 + lib/compiler_rt/shift.zig | 38 +- lib/compiler_rt/sin.zig | 23 + lib/compiler_rt/sincos.zig | 27 +- lib/compiler_rt/sparc.zig | 34 + lib/compiler_rt/sqrt.zig | 23 + lib/compiler_rt/stack_probe.zig | 53 +- lib/compiler_rt/subo.zig | 10 + lib/compiler_rt/tan.zig | 24 + lib/compiler_rt/trunc.zig | 23 + lib/compiler_rt/truncXfYf2.zig | 44 +- lib/compiler_rt/trunc_f80.zig | 14 +- lib/compiler_rt/udivmodti4.zig | 28 +- lib/compiler_rt/udivti3.zig | 34 +- lib/compiler_rt/umodti3.zig | 33 +- src/Compilation.zig | 27 +- src/compiler_rt.zig | 186 ++++++ src/link/Coff.zig | 2 +- src/link/Elf.zig | 2 +- src/link/MachO.zig | 2 +- src/link/Wasm.zig | 2 +- src/musl.zig | 1 - 70 files changed, 2003 insertions(+), 1133 deletions(-) create mode 100644 lib/compiler_rt/common.zig create mode 100644 src/compiler_rt.zig diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig index 563d3d0820..92d783cc25 100644 --- a/lib/compiler_rt.zig +++ b/lib/compiler_rt.zig @@ -1,542 +1,69 @@ -const std = @import("std"); const builtin = @import("builtin"); -const is_test = builtin.is_test; -const os_tag = builtin.os.tag; -const arch = builtin.cpu.arch; -const abi = builtin.abi; - -const is_gnu = abi.isGnu(); -const is_mingw = os_tag == .windows and is_gnu; -const is_darwin = std.Target.Os.Tag.isDarwin(os_tag); -const is_ppc = arch.isPPC() or arch.isPPC64(); - -const linkage = if (is_test) - std.builtin.GlobalLinkage.Internal -else - std.builtin.GlobalLinkage.Weak; - -const strong_linkage = if (is_test) - std.builtin.GlobalLinkage.Internal -else - std.builtin.GlobalLinkage.Strong; +pub const panic = @import("compiler_rt/common.zig").panic; comptime { // These files do their own comptime exporting logic. _ = @import("compiler_rt/atomics.zig"); - if (builtin.zig_backend != .stage2_llvm) { // TODO - _ = @import("compiler_rt/clear_cache.zig").clear_cache; - } - - const __extenddftf2 = @import("compiler_rt/extendXfYf2.zig").__extenddftf2; - @export(__extenddftf2, .{ .name = "__extenddftf2", .linkage = linkage }); - const __extendsftf2 = @import("compiler_rt/extendXfYf2.zig").__extendsftf2; - @export(__extendsftf2, .{ .name = "__extendsftf2", .linkage = linkage }); - const __extendhfsf2 = @import("compiler_rt/extendXfYf2.zig").__extendhfsf2; - @export(__extendhfsf2, .{ .name = "__extendhfsf2", .linkage = linkage }); - const __extendhftf2 = @import("compiler_rt/extendXfYf2.zig").__extendhftf2; - @export(__extendhftf2, .{ .name = "__extendhftf2", .linkage = linkage }); - - const __extendhfxf2 = @import("compiler_rt/extend_f80.zig").__extendhfxf2; - @export(__extendhfxf2, .{ .name = "__extendhfxf2", .linkage = linkage }); - const __extendsfxf2 = @import("compiler_rt/extend_f80.zig").__extendsfxf2; - @export(__extendsfxf2, .{ .name = "__extendsfxf2", .linkage = linkage }); - const __extenddfxf2 = @import("compiler_rt/extend_f80.zig").__extenddfxf2; - @export(__extenddfxf2, .{ .name = "__extenddfxf2", .linkage = linkage }); - const __extendxftf2 = @import("compiler_rt/extend_f80.zig").__extendxftf2; - @export(__extendxftf2, .{ .name = "__extendxftf2", .linkage = linkage }); - - const __lesf2 = @import("compiler_rt/compareXf2.zig").__lesf2; - @export(__lesf2, .{ .name = "__lesf2", .linkage = linkage }); - const __ledf2 = @import("compiler_rt/compareXf2.zig").__ledf2; - @export(__ledf2, .{ .name = "__ledf2", .linkage = linkage }); - const __letf2 = @import("compiler_rt/compareXf2.zig").__letf2; - @export(__letf2, .{ .name = "__letf2", .linkage = linkage }); - const __lexf2 = @import("compiler_rt/compareXf2.zig").__lexf2; - @export(__lexf2, .{ .name = "__lexf2", .linkage = linkage }); - - const __gesf2 = @import("compiler_rt/compareXf2.zig").__gesf2; - @export(__gesf2, .{ .name = "__gesf2", .linkage = linkage }); - const __gedf2 = @import("compiler_rt/compareXf2.zig").__gedf2; - @export(__gedf2, .{ .name = "__gedf2", .linkage = linkage }); - const __getf2 = @import("compiler_rt/compareXf2.zig").__getf2; - @export(__getf2, .{ .name = "__getf2", .linkage = linkage }); - const __gexf2 = @import("compiler_rt/compareXf2.zig").__gexf2; - @export(__gexf2, .{ .name = "__gexf2", .linkage = linkage }); - - const __eqsf2 = @import("compiler_rt/compareXf2.zig").__eqsf2; - @export(__eqsf2, .{ .name = "__eqsf2", .linkage = linkage }); - const __eqdf2 = @import("compiler_rt/compareXf2.zig").__eqdf2; - @export(__eqdf2, .{ .name = "__eqdf2", .linkage = linkage }); - const __eqxf2 = @import("compiler_rt/compareXf2.zig").__eqxf2; - @export(__eqxf2, .{ .name = "__eqxf2", .linkage = linkage }); - - const __ltsf2 = @import("compiler_rt/compareXf2.zig").__ltsf2; - @export(__ltsf2, .{ .name = "__ltsf2", .linkage = linkage }); - const __ltdf2 = @import("compiler_rt/compareXf2.zig").__ltdf2; - @export(__ltdf2, .{ .name = "__ltdf2", .linkage = linkage }); - const __ltxf2 = @import("compiler_rt/compareXf2.zig").__ltxf2; - @export(__ltxf2, .{ .name = "__ltxf2", .linkage = linkage }); - - const __nesf2 = @import("compiler_rt/compareXf2.zig").__nesf2; - @export(__nesf2, .{ .name = "__nesf2", .linkage = linkage }); - const __nedf2 = @import("compiler_rt/compareXf2.zig").__nedf2; - @export(__nedf2, .{ .name = "__nedf2", .linkage = linkage }); - const __nexf2 = @import("compiler_rt/compareXf2.zig").__nexf2; - @export(__nexf2, .{ .name = "__nexf2", .linkage = linkage }); - - const __gtsf2 = @import("compiler_rt/compareXf2.zig").__gtsf2; - @export(__gtsf2, .{ .name = "__gtsf2", .linkage = linkage }); - const __gtdf2 = @import("compiler_rt/compareXf2.zig").__gtdf2; - @export(__gtdf2, .{ .name = "__gtdf2", .linkage = linkage }); - const __gtxf2 = @import("compiler_rt/compareXf2.zig").__gtxf2; - @export(__gtxf2, .{ .name = "__gtxf2", .linkage = linkage }); - - if (!is_test) { - @export(__lesf2, .{ .name = "__cmpsf2", .linkage = linkage }); - @export(__ledf2, .{ .name = "__cmpdf2", .linkage = linkage }); - @export(__letf2, .{ .name = "__cmptf2", .linkage = linkage }); - @export(__letf2, .{ .name = "__eqtf2", .linkage = linkage }); - @export(__letf2, .{ .name = "__lttf2", .linkage = linkage }); - @export(__getf2, .{ .name = "__gttf2", .linkage = linkage }); - @export(__letf2, .{ .name = "__netf2", .linkage = linkage }); - @export(__extendhfsf2, .{ .name = "__gnu_h2f_ieee", .linkage = linkage }); - } - - if (builtin.os.tag == .windows) { - // Default stack-probe functions emitted by LLVM - if (is_mingw) { - const _chkstk = @import("compiler_rt/stack_probe.zig")._chkstk; - @export(_chkstk, .{ .name = "_alloca", .linkage = strong_linkage }); - const ___chkstk_ms = @import("compiler_rt/stack_probe.zig").___chkstk_ms; - @export(___chkstk_ms, .{ .name = "___chkstk_ms", .linkage = strong_linkage }); - } else if (!builtin.link_libc) { - // This symbols are otherwise exported by MSVCRT.lib - const _chkstk = @import("compiler_rt/stack_probe.zig")._chkstk; - @export(_chkstk, .{ .name = "_chkstk", .linkage = strong_linkage }); - const __chkstk = @import("compiler_rt/stack_probe.zig").__chkstk; - @export(__chkstk, .{ .name = "__chkstk", .linkage = strong_linkage }); - } - - switch (arch) { - .i386 => { - const __divti3 = @import("compiler_rt/divti3.zig").__divti3; - @export(__divti3, .{ .name = "__divti3", .linkage = linkage }); - const __modti3 = @import("compiler_rt/modti3.zig").__modti3; - @export(__modti3, .{ .name = "__modti3", .linkage = linkage }); - const __multi3 = @import("compiler_rt/multi3.zig").__multi3; - @export(__multi3, .{ .name = "__multi3", .linkage = linkage }); - const __udivti3 = @import("compiler_rt/udivti3.zig").__udivti3; - @export(__udivti3, .{ .name = "__udivti3", .linkage = linkage }); - const __udivmodti4 = @import("compiler_rt/udivmodti4.zig").__udivmodti4; - @export(__udivmodti4, .{ .name = "__udivmodti4", .linkage = linkage }); - const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3; - @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage }); - }, - .x86_64 => { - // The "ti" functions must use Vector(2, u64) parameter types to adhere to the ABI - // that LLVM expects compiler-rt to have. - const __divti3_windows_x86_64 = @import("compiler_rt/divti3.zig").__divti3_windows_x86_64; - @export(__divti3_windows_x86_64, .{ .name = "__divti3", .linkage = linkage }); - const __modti3_windows_x86_64 = @import("compiler_rt/modti3.zig").__modti3_windows_x86_64; - @export(__modti3_windows_x86_64, .{ .name = "__modti3", .linkage = linkage }); - const __multi3_windows_x86_64 = @import("compiler_rt/multi3.zig").__multi3_windows_x86_64; - @export(__multi3_windows_x86_64, .{ .name = "__multi3", .linkage = linkage }); - const __udivti3_windows_x86_64 = @import("compiler_rt/udivti3.zig").__udivti3_windows_x86_64; - @export(__udivti3_windows_x86_64, .{ .name = "__udivti3", .linkage = linkage }); - const __udivmodti4_windows_x86_64 = @import("compiler_rt/udivmodti4.zig").__udivmodti4_windows_x86_64; - @export(__udivmodti4_windows_x86_64, .{ .name = "__udivmodti4", .linkage = linkage }); - const __umodti3_windows_x86_64 = @import("compiler_rt/umodti3.zig").__umodti3_windows_x86_64; - @export(__umodti3_windows_x86_64, .{ .name = "__umodti3", .linkage = linkage }); - }, - else => {}, - } - if (arch.isAARCH64()) { - const __chkstk = @import("compiler_rt/stack_probe.zig").__chkstk; - @export(__chkstk, .{ .name = "__chkstk", .linkage = strong_linkage }); - const __divti3_windows = @import("compiler_rt/divti3.zig").__divti3; - @export(__divti3_windows, .{ .name = "__divti3", .linkage = linkage }); - const __modti3 = @import("compiler_rt/modti3.zig").__modti3; - @export(__modti3, .{ .name = "__modti3", .linkage = linkage }); - const __udivti3_windows = @import("compiler_rt/udivti3.zig").__udivti3; - @export(__udivti3_windows, .{ .name = "__udivti3", .linkage = linkage }); - const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3; - @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage }); - } - } else { - const __divti3 = @import("compiler_rt/divti3.zig").__divti3; - @export(__divti3, .{ .name = "__divti3", .linkage = linkage }); - const __modti3 = @import("compiler_rt/modti3.zig").__modti3; - @export(__modti3, .{ .name = "__modti3", .linkage = linkage }); - const __multi3 = @import("compiler_rt/multi3.zig").__multi3; - @export(__multi3, .{ .name = "__multi3", .linkage = linkage }); - const __udivti3 = @import("compiler_rt/udivti3.zig").__udivti3; - @export(__udivti3, .{ .name = "__udivti3", .linkage = linkage }); - const __udivmodti4 = @import("compiler_rt/udivmodti4.zig").__udivmodti4; - @export(__udivmodti4, .{ .name = "__udivmodti4", .linkage = linkage }); - const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3; - @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage }); - } - - const __truncdfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfhf2; - @export(__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = linkage }); - const __trunctfhf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfhf2; - @export(__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = linkage }); - const __trunctfdf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfdf2; - @export(__trunctfdf2, .{ .name = "__trunctfdf2", .linkage = linkage }); - const __trunctfsf2 = @import("compiler_rt/truncXfYf2.zig").__trunctfsf2; - @export(__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = linkage }); - - const __truncdfsf2 = @import("compiler_rt/truncXfYf2.zig").__truncdfsf2; - @export(__truncdfsf2, .{ .name = "__truncdfsf2", .linkage = linkage }); - - const __truncxfhf2 = @import("compiler_rt/trunc_f80.zig").__truncxfhf2; - @export(__truncxfhf2, .{ .name = "__truncxfhf2", .linkage = linkage }); - const __truncxfsf2 = @import("compiler_rt/trunc_f80.zig").__truncxfsf2; - @export(__truncxfsf2, .{ .name = "__truncxfsf2", .linkage = linkage }); - const __truncxfdf2 = @import("compiler_rt/trunc_f80.zig").__truncxfdf2; - @export(__truncxfdf2, .{ .name = "__truncxfdf2", .linkage = linkage }); - const __trunctfxf2 = @import("compiler_rt/trunc_f80.zig").__trunctfxf2; - @export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage }); - - switch (arch) { - .i386, - .x86_64, - => { - const zig_probe_stack = @import("compiler_rt/stack_probe.zig").zig_probe_stack; - @export(zig_probe_stack, .{ - .name = "__zig_probe_stack", - .linkage = linkage, - }); - }, - else => {}, - } - - const __unordsf2 = @import("compiler_rt/compareXf2.zig").__unordsf2; - @export(__unordsf2, .{ .name = "__unordsf2", .linkage = linkage }); - const __unorddf2 = @import("compiler_rt/compareXf2.zig").__unorddf2; - @export(__unorddf2, .{ .name = "__unorddf2", .linkage = linkage }); - const __unordtf2 = @import("compiler_rt/compareXf2.zig").__unordtf2; - @export(__unordtf2, .{ .name = "__unordtf2", .linkage = linkage }); - - const __addsf3 = @import("compiler_rt/addXf3.zig").__addsf3; - @export(__addsf3, .{ .name = "__addsf3", .linkage = linkage }); - const __adddf3 = @import("compiler_rt/addXf3.zig").__adddf3; - @export(__adddf3, .{ .name = "__adddf3", .linkage = linkage }); - const __addxf3 = @import("compiler_rt/addXf3.zig").__addxf3; - @export(__addxf3, .{ .name = "__addxf3", .linkage = linkage }); - const __addtf3 = @import("compiler_rt/addXf3.zig").__addtf3; - @export(__addtf3, .{ .name = "__addtf3", .linkage = linkage }); - - const __subsf3 = @import("compiler_rt/addXf3.zig").__subsf3; - @export(__subsf3, .{ .name = "__subsf3", .linkage = linkage }); - const __subdf3 = @import("compiler_rt/addXf3.zig").__subdf3; - @export(__subdf3, .{ .name = "__subdf3", .linkage = linkage }); - const __subxf3 = @import("compiler_rt/addXf3.zig").__subxf3; - @export(__subxf3, .{ .name = "__subxf3", .linkage = linkage }); - const __subtf3 = @import("compiler_rt/addXf3.zig").__subtf3; - @export(__subtf3, .{ .name = "__subtf3", .linkage = linkage }); - - const __mulsf3 = @import("compiler_rt/mulXf3.zig").__mulsf3; - @export(__mulsf3, .{ .name = "__mulsf3", .linkage = linkage }); - const __muldf3 = @import("compiler_rt/mulXf3.zig").__muldf3; - @export(__muldf3, .{ .name = "__muldf3", .linkage = linkage }); - const __mulxf3 = @import("compiler_rt/mulXf3.zig").__mulxf3; - @export(__mulxf3, .{ .name = "__mulxf3", .linkage = linkage }); - const __multf3 = @import("compiler_rt/mulXf3.zig").__multf3; - @export(__multf3, .{ .name = "__multf3", .linkage = linkage }); - - const __divsf3 = @import("compiler_rt/divsf3.zig").__divsf3; - @export(__divsf3, .{ .name = "__divsf3", .linkage = linkage }); - const __divdf3 = @import("compiler_rt/divdf3.zig").__divdf3; - @export(__divdf3, .{ .name = "__divdf3", .linkage = linkage }); - const __divxf3 = @import("compiler_rt/divxf3.zig").__divxf3; - @export(__divxf3, .{ .name = "__divxf3", .linkage = linkage }); - const __divtf3 = @import("compiler_rt/divtf3.zig").__divtf3; - @export(__divtf3, .{ .name = "__divtf3", .linkage = linkage }); - - // Integer Bit operations - const __clzsi2 = @import("compiler_rt/count0bits.zig").__clzsi2; - @export(__clzsi2, .{ .name = "__clzsi2", .linkage = linkage }); - const __clzdi2 = @import("compiler_rt/count0bits.zig").__clzdi2; - @export(__clzdi2, .{ .name = "__clzdi2", .linkage = linkage }); - const __clzti2 = @import("compiler_rt/count0bits.zig").__clzti2; - @export(__clzti2, .{ .name = "__clzti2", .linkage = linkage }); - const __ctzsi2 = @import("compiler_rt/count0bits.zig").__ctzsi2; - @export(__ctzsi2, .{ .name = "__ctzsi2", .linkage = linkage }); - const __ctzdi2 = @import("compiler_rt/count0bits.zig").__ctzdi2; - @export(__ctzdi2, .{ .name = "__ctzdi2", .linkage = linkage }); - const __ctzti2 = @import("compiler_rt/count0bits.zig").__ctzti2; - @export(__ctzti2, .{ .name = "__ctzti2", .linkage = linkage }); - const __ffssi2 = @import("compiler_rt/count0bits.zig").__ffssi2; - @export(__ffssi2, .{ .name = "__ffssi2", .linkage = linkage }); - const __ffsdi2 = @import("compiler_rt/count0bits.zig").__ffsdi2; - @export(__ffsdi2, .{ .name = "__ffsdi2", .linkage = linkage }); - const __ffsti2 = @import("compiler_rt/count0bits.zig").__ffsti2; - @export(__ffsti2, .{ .name = "__ffsti2", .linkage = linkage }); - const __paritysi2 = @import("compiler_rt/parity.zig").__paritysi2; - @export(__paritysi2, .{ .name = "__paritysi2", .linkage = linkage }); - const __paritydi2 = @import("compiler_rt/parity.zig").__paritydi2; - @export(__paritydi2, .{ .name = "__paritydi2", .linkage = linkage }); - const __parityti2 = @import("compiler_rt/parity.zig").__parityti2; - @export(__parityti2, .{ .name = "__parityti2", .linkage = linkage }); - const __popcountsi2 = @import("compiler_rt/popcount.zig").__popcountsi2; - @export(__popcountsi2, .{ .name = "__popcountsi2", .linkage = linkage }); - const __popcountdi2 = @import("compiler_rt/popcount.zig").__popcountdi2; - @export(__popcountdi2, .{ .name = "__popcountdi2", .linkage = linkage }); - const __popcountti2 = @import("compiler_rt/popcount.zig").__popcountti2; - @export(__popcountti2, .{ .name = "__popcountti2", .linkage = linkage }); - const __bswapsi2 = @import("compiler_rt/bswap.zig").__bswapsi2; - @export(__bswapsi2, .{ .name = "__bswapsi2", .linkage = linkage }); - const __bswapdi2 = @import("compiler_rt/bswap.zig").__bswapdi2; - @export(__bswapdi2, .{ .name = "__bswapdi2", .linkage = linkage }); - const __bswapti2 = @import("compiler_rt/bswap.zig").__bswapti2; - @export(__bswapti2, .{ .name = "__bswapti2", .linkage = linkage }); - - // Integral -> Float Conversion - - // Conversion to f32 - const __floatsisf = @import("compiler_rt/floatXiYf.zig").__floatsisf; - @export(__floatsisf, .{ .name = "__floatsisf", .linkage = linkage }); - const __floatunsisf = @import("compiler_rt/floatXiYf.zig").__floatunsisf; - @export(__floatunsisf, .{ .name = "__floatunsisf", .linkage = linkage }); - - const __floatundisf = @import("compiler_rt/floatXiYf.zig").__floatundisf; - @export(__floatundisf, .{ .name = "__floatundisf", .linkage = linkage }); - const __floatdisf = @import("compiler_rt/floatXiYf.zig").__floatdisf; - @export(__floatdisf, .{ .name = "__floatdisf", .linkage = linkage }); - - const __floattisf = @import("compiler_rt/floatXiYf.zig").__floattisf; - @export(__floattisf, .{ .name = "__floattisf", .linkage = linkage }); - const __floatuntisf = @import("compiler_rt/floatXiYf.zig").__floatuntisf; - @export(__floatuntisf, .{ .name = "__floatuntisf", .linkage = linkage }); - - // Conversion to f64 - const __floatsidf = @import("compiler_rt/floatXiYf.zig").__floatsidf; - @export(__floatsidf, .{ .name = "__floatsidf", .linkage = linkage }); - const __floatunsidf = @import("compiler_rt/floatXiYf.zig").__floatunsidf; - @export(__floatunsidf, .{ .name = "__floatunsidf", .linkage = linkage }); - - const __floatdidf = @import("compiler_rt/floatXiYf.zig").__floatdidf; - @export(__floatdidf, .{ .name = "__floatdidf", .linkage = linkage }); - const __floatundidf = @import("compiler_rt/floatXiYf.zig").__floatundidf; - @export(__floatundidf, .{ .name = "__floatundidf", .linkage = linkage }); - - const __floattidf = @import("compiler_rt/floatXiYf.zig").__floattidf; - @export(__floattidf, .{ .name = "__floattidf", .linkage = linkage }); - const __floatuntidf = @import("compiler_rt/floatXiYf.zig").__floatuntidf; - @export(__floatuntidf, .{ .name = "__floatuntidf", .linkage = linkage }); - - // Conversion to f80 - const __floatsixf = @import("compiler_rt/floatXiYf.zig").__floatsixf; - @export(__floatsixf, .{ .name = "__floatsixf", .linkage = linkage }); - const __floatunsixf = @import("compiler_rt/floatXiYf.zig").__floatunsixf; - @export(__floatunsixf, .{ .name = "__floatunsixf", .linkage = linkage }); - - const __floatdixf = @import("compiler_rt/floatXiYf.zig").__floatdixf; - @export(__floatdixf, .{ .name = "__floatdixf", .linkage = linkage }); - const __floatundixf = @import("compiler_rt/floatXiYf.zig").__floatundixf; - @export(__floatundixf, .{ .name = "__floatundixf", .linkage = linkage }); - - const __floattixf = @import("compiler_rt/floatXiYf.zig").__floattixf; - @export(__floattixf, .{ .name = "__floattixf", .linkage = linkage }); - const __floatuntixf = @import("compiler_rt/floatXiYf.zig").__floatuntixf; - @export(__floatuntixf, .{ .name = "__floatuntixf", .linkage = linkage }); - - // Conversion to f128 - const __floatsitf = @import("compiler_rt/floatXiYf.zig").__floatsitf; - @export(__floatsitf, .{ .name = "__floatsitf", .linkage = linkage }); - const __floatunsitf = @import("compiler_rt/floatXiYf.zig").__floatunsitf; - @export(__floatunsitf, .{ .name = "__floatunsitf", .linkage = linkage }); - - const __floatditf = @import("compiler_rt/floatXiYf.zig").__floatditf; - @export(__floatditf, .{ .name = "__floatditf", .linkage = linkage }); - const __floatunditf = @import("compiler_rt/floatXiYf.zig").__floatunditf; - @export(__floatunditf, .{ .name = "__floatunditf", .linkage = linkage }); - - const __floattitf = @import("compiler_rt/floatXiYf.zig").__floattitf; - @export(__floattitf, .{ .name = "__floattitf", .linkage = linkage }); - const __floatuntitf = @import("compiler_rt/floatXiYf.zig").__floatuntitf; - @export(__floatuntitf, .{ .name = "__floatuntitf", .linkage = linkage }); - - // Float -> Integral Conversion - - // Conversion from f32 - const __fixsfsi = @import("compiler_rt/fixXfYi.zig").__fixsfsi; - @export(__fixsfsi, .{ .name = "__fixsfsi", .linkage = linkage }); - const __fixunssfsi = @import("compiler_rt/fixXfYi.zig").__fixunssfsi; - @export(__fixunssfsi, .{ .name = "__fixunssfsi", .linkage = linkage }); - - const __fixsfdi = @import("compiler_rt/fixXfYi.zig").__fixsfdi; - @export(__fixsfdi, .{ .name = "__fixsfdi", .linkage = linkage }); - const __fixunssfdi = @import("compiler_rt/fixXfYi.zig").__fixunssfdi; - @export(__fixunssfdi, .{ .name = "__fixunssfdi", .linkage = linkage }); - - const __fixsfti = @import("compiler_rt/fixXfYi.zig").__fixsfti; - @export(__fixsfti, .{ .name = "__fixsfti", .linkage = linkage }); - const __fixunssfti = @import("compiler_rt/fixXfYi.zig").__fixunssfti; - @export(__fixunssfti, .{ .name = "__fixunssfti", .linkage = linkage }); - - // Conversion from f64 - const __fixdfsi = @import("compiler_rt/fixXfYi.zig").__fixdfsi; - @export(__fixdfsi, .{ .name = "__fixdfsi", .linkage = linkage }); - const __fixunsdfsi = @import("compiler_rt/fixXfYi.zig").__fixunsdfsi; - @export(__fixunsdfsi, .{ .name = "__fixunsdfsi", .linkage = linkage }); - - const __fixdfdi = @import("compiler_rt/fixXfYi.zig").__fixdfdi; - @export(__fixdfdi, .{ .name = "__fixdfdi", .linkage = linkage }); - const __fixunsdfdi = @import("compiler_rt/fixXfYi.zig").__fixunsdfdi; - @export(__fixunsdfdi, .{ .name = "__fixunsdfdi", .linkage = linkage }); - - const __fixdfti = @import("compiler_rt/fixXfYi.zig").__fixdfti; - @export(__fixdfti, .{ .name = "__fixdfti", .linkage = linkage }); - const __fixunsdfti = @import("compiler_rt/fixXfYi.zig").__fixunsdfti; - @export(__fixunsdfti, .{ .name = "__fixunsdfti", .linkage = linkage }); - - // Conversion from f80 - const __fixxfsi = @import("compiler_rt/fixXfYi.zig").__fixxfsi; - @export(__fixxfsi, .{ .name = "__fixxfsi", .linkage = linkage }); - const __fixunsxfsi = @import("compiler_rt/fixXfYi.zig").__fixunsxfsi; - @export(__fixunsxfsi, .{ .name = "__fixunsxfsi", .linkage = linkage }); - - const __fixxfdi = @import("compiler_rt/fixXfYi.zig").__fixxfdi; - @export(__fixxfdi, .{ .name = "__fixxfdi", .linkage = linkage }); - const __fixunsxfdi = @import("compiler_rt/fixXfYi.zig").__fixunsxfdi; - @export(__fixunsxfdi, .{ .name = "__fixunsxfdi", .linkage = linkage }); - - const __fixxfti = @import("compiler_rt/fixXfYi.zig").__fixxfti; - @export(__fixxfti, .{ .name = "__fixxfti", .linkage = linkage }); - const __fixunsxfti = @import("compiler_rt/fixXfYi.zig").__fixunsxfti; - @export(__fixunsxfti, .{ .name = "__fixunsxfti", .linkage = linkage }); - - // Conversion from f128 - const __fixtfsi = @import("compiler_rt/fixXfYi.zig").__fixtfsi; - @export(__fixtfsi, .{ .name = "__fixtfsi", .linkage = linkage }); - const __fixunstfsi = @import("compiler_rt/fixXfYi.zig").__fixunstfsi; - @export(__fixunstfsi, .{ .name = "__fixunstfsi", .linkage = linkage }); - - const __fixtfdi = @import("compiler_rt/fixXfYi.zig").__fixtfdi; - @export(__fixtfdi, .{ .name = "__fixtfdi", .linkage = linkage }); - const __fixunstfdi = @import("compiler_rt/fixXfYi.zig").__fixunstfdi; - @export(__fixunstfdi, .{ .name = "__fixunstfdi", .linkage = linkage }); - - const __fixtfti = @import("compiler_rt/fixXfYi.zig").__fixtfti; - @export(__fixtfti, .{ .name = "__fixtfti", .linkage = linkage }); - const __fixunstfti = @import("compiler_rt/fixXfYi.zig").__fixunstfti; - @export(__fixunstfti, .{ .name = "__fixunstfti", .linkage = linkage }); - - const __udivmoddi4 = @import("compiler_rt/int.zig").__udivmoddi4; - @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = linkage }); - - const __truncsfhf2 = @import("compiler_rt/truncXfYf2.zig").__truncsfhf2; - @export(__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage }); - if (!is_test) { - @export(__truncsfhf2, .{ .name = "__gnu_f2h_ieee", .linkage = linkage }); - } - const __extendsfdf2 = @import("compiler_rt/extendXfYf2.zig").__extendsfdf2; - @export(__extendsfdf2, .{ .name = "__extendsfdf2", .linkage = linkage }); - - if (is_darwin) { - const __isPlatformVersionAtLeast = @import("compiler_rt/os_version_check.zig").__isPlatformVersionAtLeast; - @export(__isPlatformVersionAtLeast, .{ .name = "__isPlatformVersionAtLeast", .linkage = linkage }); - } - - // Integer Arithmetic - const __ashldi3 = @import("compiler_rt/shift.zig").__ashldi3; - @export(__ashldi3, .{ .name = "__ashldi3", .linkage = linkage }); - const __ashlti3 = @import("compiler_rt/shift.zig").__ashlti3; - @export(__ashlti3, .{ .name = "__ashlti3", .linkage = linkage }); - const __ashrdi3 = @import("compiler_rt/shift.zig").__ashrdi3; - @export(__ashrdi3, .{ .name = "__ashrdi3", .linkage = linkage }); - const __ashrti3 = @import("compiler_rt/shift.zig").__ashrti3; - @export(__ashrti3, .{ .name = "__ashrti3", .linkage = linkage }); - const __lshrdi3 = @import("compiler_rt/shift.zig").__lshrdi3; - @export(__lshrdi3, .{ .name = "__lshrdi3", .linkage = linkage }); - const __lshrti3 = @import("compiler_rt/shift.zig").__lshrti3; - @export(__lshrti3, .{ .name = "__lshrti3", .linkage = linkage }); - const __negsi2 = @import("compiler_rt/negXi2.zig").__negsi2; - @export(__negsi2, .{ .name = "__negsi2", .linkage = linkage }); - const __negdi2 = @import("compiler_rt/negXi2.zig").__negdi2; - @export(__negdi2, .{ .name = "__negdi2", .linkage = linkage }); - const __negti2 = @import("compiler_rt/negXi2.zig").__negti2; - @export(__negti2, .{ .name = "__negti2", .linkage = linkage }); - - const __mulsi3 = @import("compiler_rt/int.zig").__mulsi3; - @export(__mulsi3, .{ .name = "__mulsi3", .linkage = linkage }); - const __muldi3 = @import("compiler_rt/muldi3.zig").__muldi3; - @export(__muldi3, .{ .name = "__muldi3", .linkage = linkage }); - const __divmoddi4 = @import("compiler_rt/int.zig").__divmoddi4; - @export(__divmoddi4, .{ .name = "__divmoddi4", .linkage = linkage }); - const __divsi3 = @import("compiler_rt/int.zig").__divsi3; - @export(__divsi3, .{ .name = "__divsi3", .linkage = linkage }); - const __divdi3 = @import("compiler_rt/int.zig").__divdi3; - @export(__divdi3, .{ .name = "__divdi3", .linkage = linkage }); - const __udivsi3 = @import("compiler_rt/int.zig").__udivsi3; - @export(__udivsi3, .{ .name = "__udivsi3", .linkage = linkage }); - const __udivdi3 = @import("compiler_rt/int.zig").__udivdi3; - @export(__udivdi3, .{ .name = "__udivdi3", .linkage = linkage }); - const __modsi3 = @import("compiler_rt/int.zig").__modsi3; - @export(__modsi3, .{ .name = "__modsi3", .linkage = linkage }); - const __moddi3 = @import("compiler_rt/int.zig").__moddi3; - @export(__moddi3, .{ .name = "__moddi3", .linkage = linkage }); - const __umodsi3 = @import("compiler_rt/int.zig").__umodsi3; - @export(__umodsi3, .{ .name = "__umodsi3", .linkage = linkage }); - const __umoddi3 = @import("compiler_rt/int.zig").__umoddi3; - @export(__umoddi3, .{ .name = "__umoddi3", .linkage = linkage }); - const __divmodsi4 = @import("compiler_rt/int.zig").__divmodsi4; - @export(__divmodsi4, .{ .name = "__divmodsi4", .linkage = linkage }); - const __udivmodsi4 = @import("compiler_rt/int.zig").__udivmodsi4; - @export(__udivmodsi4, .{ .name = "__udivmodsi4", .linkage = linkage }); - - // Integer Arithmetic with trapping overflow - const __absvsi2 = @import("compiler_rt/absv.zig").__absvsi2; - @export(__absvsi2, .{ .name = "__absvsi2", .linkage = linkage }); - const __absvdi2 = @import("compiler_rt/absv.zig").__absvdi2; - @export(__absvdi2, .{ .name = "__absvdi2", .linkage = linkage }); - const __absvti2 = @import("compiler_rt/absv.zig").__absvti2; - @export(__absvti2, .{ .name = "__absvti2", .linkage = linkage }); - const __negvsi2 = @import("compiler_rt/negv.zig").__negvsi2; - @export(__negvsi2, .{ .name = "__negvsi2", .linkage = linkage }); - const __negvdi2 = @import("compiler_rt/negv.zig").__negvdi2; - @export(__negvdi2, .{ .name = "__negvdi2", .linkage = linkage }); - const __negvti2 = @import("compiler_rt/negv.zig").__negvti2; - @export(__negvti2, .{ .name = "__negvti2", .linkage = linkage }); - - // Integer arithmetic which returns if overflow - const __addosi4 = @import("compiler_rt/addo.zig").__addosi4; - @export(__addosi4, .{ .name = "__addosi4", .linkage = linkage }); - const __addodi4 = @import("compiler_rt/addo.zig").__addodi4; - @export(__addodi4, .{ .name = "__addodi4", .linkage = linkage }); - const __addoti4 = @import("compiler_rt/addo.zig").__addoti4; - @export(__addoti4, .{ .name = "__addoti4", .linkage = linkage }); - const __subosi4 = @import("compiler_rt/subo.zig").__subosi4; - @export(__subosi4, .{ .name = "__subosi4", .linkage = linkage }); - const __subodi4 = @import("compiler_rt/subo.zig").__subodi4; - @export(__subodi4, .{ .name = "__subodi4", .linkage = linkage }); - const __suboti4 = @import("compiler_rt/subo.zig").__suboti4; - @export(__suboti4, .{ .name = "__suboti4", .linkage = linkage }); - const __mulosi4 = @import("compiler_rt/mulo.zig").__mulosi4; - @export(__mulosi4, .{ .name = "__mulosi4", .linkage = linkage }); - const __mulodi4 = @import("compiler_rt/mulo.zig").__mulodi4; - @export(__mulodi4, .{ .name = "__mulodi4", .linkage = linkage }); - const __muloti4 = @import("compiler_rt/mulo.zig").__muloti4; - @export(__muloti4, .{ .name = "__muloti4", .linkage = linkage }); - - // Integer Comparison - // (a < b) => 0 - // (a == b) => 1 - // (a > b) => 2 - const __cmpsi2 = @import("compiler_rt/cmp.zig").__cmpsi2; - @export(__cmpsi2, .{ .name = "__cmpsi2", .linkage = linkage }); - const __cmpdi2 = @import("compiler_rt/cmp.zig").__cmpdi2; - @export(__cmpdi2, .{ .name = "__cmpdi2", .linkage = linkage }); - const __cmpti2 = @import("compiler_rt/cmp.zig").__cmpti2; - @export(__cmpti2, .{ .name = "__cmpti2", .linkage = linkage }); - const __ucmpsi2 = @import("compiler_rt/cmp.zig").__ucmpsi2; - @export(__ucmpsi2, .{ .name = "__ucmpsi2", .linkage = linkage }); - const __ucmpdi2 = @import("compiler_rt/cmp.zig").__ucmpdi2; - @export(__ucmpdi2, .{ .name = "__ucmpdi2", .linkage = linkage }); - const __ucmpti2 = @import("compiler_rt/cmp.zig").__ucmpti2; - @export(__ucmpti2, .{ .name = "__ucmpti2", .linkage = linkage }); + _ = @import("compiler_rt/sin.zig"); + _ = @import("compiler_rt/cos.zig"); + _ = @import("compiler_rt/sincos.zig"); + _ = @import("compiler_rt/ceil.zig"); + _ = @import("compiler_rt/exp.zig"); + _ = @import("compiler_rt/exp2.zig"); + _ = @import("compiler_rt/fabs.zig"); + _ = @import("compiler_rt/floor.zig"); + _ = @import("compiler_rt/fma.zig"); + _ = @import("compiler_rt/fmax.zig"); + _ = @import("compiler_rt/fmin.zig"); + _ = @import("compiler_rt/fmod.zig"); + _ = @import("compiler_rt/log.zig"); + _ = @import("compiler_rt/log10.zig"); + _ = @import("compiler_rt/log2.zig"); + _ = @import("compiler_rt/round.zig"); + _ = @import("compiler_rt/sqrt.zig"); + _ = @import("compiler_rt/tan.zig"); + _ = @import("compiler_rt/trunc.zig"); + _ = @import("compiler_rt/extendXfYf2.zig"); + _ = @import("compiler_rt/extend_f80.zig"); + _ = @import("compiler_rt/compareXf2.zig"); + _ = @import("compiler_rt/stack_probe.zig"); + _ = @import("compiler_rt/divti3.zig"); + _ = @import("compiler_rt/modti3.zig"); + _ = @import("compiler_rt/multi3.zig"); + _ = @import("compiler_rt/udivti3.zig"); + _ = @import("compiler_rt/udivmodti4.zig"); + _ = @import("compiler_rt/umodti3.zig"); + _ = @import("compiler_rt/truncXfYf2.zig"); + _ = @import("compiler_rt/trunc_f80.zig"); + _ = @import("compiler_rt/addXf3.zig"); + _ = @import("compiler_rt/mulXf3.zig"); + _ = @import("compiler_rt/divsf3.zig"); + _ = @import("compiler_rt/divdf3.zig"); + _ = @import("compiler_rt/divxf3.zig"); + _ = @import("compiler_rt/divtf3.zig"); + _ = @import("compiler_rt/floatXiYf.zig"); + _ = @import("compiler_rt/fixXfYi.zig"); + _ = @import("compiler_rt/count0bits.zig"); + _ = @import("compiler_rt/parity.zig"); + _ = @import("compiler_rt/popcount.zig"); + _ = @import("compiler_rt/bswap.zig"); + _ = @import("compiler_rt/int.zig"); + _ = @import("compiler_rt/shift.zig"); + _ = @import("compiler_rt/negXi2.zig"); + _ = @import("compiler_rt/muldi3.zig"); + _ = @import("compiler_rt/absv.zig"); + _ = @import("compiler_rt/negv.zig"); + _ = @import("compiler_rt/addo.zig"); + _ = @import("compiler_rt/subo.zig"); + _ = @import("compiler_rt/mulo.zig"); + _ = @import("compiler_rt/cmp.zig"); + _ = @import("compiler_rt/negXf2.zig"); + _ = @import("compiler_rt/os_version_check.zig"); + _ = @import("compiler_rt/emutls.zig"); + _ = @import("compiler_rt/arm.zig"); + _ = @import("compiler_rt/aulldiv.zig"); + _ = @import("compiler_rt/sparc.zig"); + _ = @import("compiler_rt/clear_cache.zig"); // missing: Floating point raised to integer power @@ -544,342 +71,4 @@ comptime { // (a + ib) * (c + id) // (a + ib) / (c + id) - const __negsf2 = @import("compiler_rt/negXf2.zig").__negsf2; - @export(__negsf2, .{ .name = "__negsf2", .linkage = linkage }); - const __negdf2 = @import("compiler_rt/negXf2.zig").__negdf2; - @export(__negdf2, .{ .name = "__negdf2", .linkage = linkage }); - - if (builtin.link_libc and os_tag == .openbsd) { - const __emutls_get_address = @import("compiler_rt/emutls.zig").__emutls_get_address; - @export(__emutls_get_address, .{ .name = "__emutls_get_address", .linkage = linkage }); - } - - if ((arch.isARM() or arch.isThumb()) and !is_test) { - const __aeabi_unwind_cpp_pr0 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr0; - @export(__aeabi_unwind_cpp_pr0, .{ .name = "__aeabi_unwind_cpp_pr0", .linkage = linkage }); - const __aeabi_unwind_cpp_pr1 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr1; - @export(__aeabi_unwind_cpp_pr1, .{ .name = "__aeabi_unwind_cpp_pr1", .linkage = linkage }); - const __aeabi_unwind_cpp_pr2 = @import("compiler_rt/arm.zig").__aeabi_unwind_cpp_pr2; - @export(__aeabi_unwind_cpp_pr2, .{ .name = "__aeabi_unwind_cpp_pr2", .linkage = linkage }); - - @export(__muldi3, .{ .name = "__aeabi_lmul", .linkage = linkage }); - - const __aeabi_ldivmod = @import("compiler_rt/arm.zig").__aeabi_ldivmod; - @export(__aeabi_ldivmod, .{ .name = "__aeabi_ldivmod", .linkage = linkage }); - const __aeabi_uldivmod = @import("compiler_rt/arm.zig").__aeabi_uldivmod; - @export(__aeabi_uldivmod, .{ .name = "__aeabi_uldivmod", .linkage = linkage }); - - @export(__divsi3, .{ .name = "__aeabi_idiv", .linkage = linkage }); - const __aeabi_idivmod = @import("compiler_rt/arm.zig").__aeabi_idivmod; - @export(__aeabi_idivmod, .{ .name = "__aeabi_idivmod", .linkage = linkage }); - @export(__udivsi3, .{ .name = "__aeabi_uidiv", .linkage = linkage }); - const __aeabi_uidivmod = @import("compiler_rt/arm.zig").__aeabi_uidivmod; - @export(__aeabi_uidivmod, .{ .name = "__aeabi_uidivmod", .linkage = linkage }); - - const __aeabi_memcpy = @import("compiler_rt/arm.zig").__aeabi_memcpy; - @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy", .linkage = linkage }); - @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy4", .linkage = linkage }); - @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy8", .linkage = linkage }); - - const __aeabi_memmove = @import("compiler_rt/arm.zig").__aeabi_memmove; - @export(__aeabi_memmove, .{ .name = "__aeabi_memmove", .linkage = linkage }); - @export(__aeabi_memmove, .{ .name = "__aeabi_memmove4", .linkage = linkage }); - @export(__aeabi_memmove, .{ .name = "__aeabi_memmove8", .linkage = linkage }); - - const __aeabi_memset = @import("compiler_rt/arm.zig").__aeabi_memset; - @export(__aeabi_memset, .{ .name = "__aeabi_memset", .linkage = linkage }); - @export(__aeabi_memset, .{ .name = "__aeabi_memset4", .linkage = linkage }); - @export(__aeabi_memset, .{ .name = "__aeabi_memset8", .linkage = linkage }); - - const __aeabi_memclr = @import("compiler_rt/arm.zig").__aeabi_memclr; - @export(__aeabi_memclr, .{ .name = "__aeabi_memclr", .linkage = linkage }); - @export(__aeabi_memclr, .{ .name = "__aeabi_memclr4", .linkage = linkage }); - @export(__aeabi_memclr, .{ .name = "__aeabi_memclr8", .linkage = linkage }); - - if (os_tag == .linux) { - const __aeabi_read_tp = @import("compiler_rt/arm.zig").__aeabi_read_tp; - @export(__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = linkage }); - } - - const __aeabi_f2d = @import("compiler_rt/extendXfYf2.zig").__aeabi_f2d; - @export(__aeabi_f2d, .{ .name = "__aeabi_f2d", .linkage = linkage }); - const __aeabi_i2d = @import("compiler_rt/floatXiYf.zig").__aeabi_i2d; - @export(__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = linkage }); - const __aeabi_l2d = @import("compiler_rt/floatXiYf.zig").__aeabi_l2d; - @export(__aeabi_l2d, .{ .name = "__aeabi_l2d", .linkage = linkage }); - const __aeabi_l2f = @import("compiler_rt/floatXiYf.zig").__aeabi_l2f; - @export(__aeabi_l2f, .{ .name = "__aeabi_l2f", .linkage = linkage }); - const __aeabi_ui2d = @import("compiler_rt/floatXiYf.zig").__aeabi_ui2d; - @export(__aeabi_ui2d, .{ .name = "__aeabi_ui2d", .linkage = linkage }); - const __aeabi_ul2d = @import("compiler_rt/floatXiYf.zig").__aeabi_ul2d; - @export(__aeabi_ul2d, .{ .name = "__aeabi_ul2d", .linkage = linkage }); - const __aeabi_ui2f = @import("compiler_rt/floatXiYf.zig").__aeabi_ui2f; - @export(__aeabi_ui2f, .{ .name = "__aeabi_ui2f", .linkage = linkage }); - const __aeabi_ul2f = @import("compiler_rt/floatXiYf.zig").__aeabi_ul2f; - @export(__aeabi_ul2f, .{ .name = "__aeabi_ul2f", .linkage = linkage }); - - const __aeabi_fneg = @import("compiler_rt/negXf2.zig").__aeabi_fneg; - @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = linkage }); - const __aeabi_dneg = @import("compiler_rt/negXf2.zig").__aeabi_dneg; - @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = linkage }); - - const __aeabi_fmul = @import("compiler_rt/mulXf3.zig").__aeabi_fmul; - @export(__aeabi_fmul, .{ .name = "__aeabi_fmul", .linkage = linkage }); - const __aeabi_dmul = @import("compiler_rt/mulXf3.zig").__aeabi_dmul; - @export(__aeabi_dmul, .{ .name = "__aeabi_dmul", .linkage = linkage }); - - const __aeabi_d2h = @import("compiler_rt/truncXfYf2.zig").__aeabi_d2h; - @export(__aeabi_d2h, .{ .name = "__aeabi_d2h", .linkage = linkage }); - - const __aeabi_f2ulz = @import("compiler_rt/fixXfYi.zig").__aeabi_f2ulz; - @export(__aeabi_f2ulz, .{ .name = "__aeabi_f2ulz", .linkage = linkage }); - const __aeabi_d2ulz = @import("compiler_rt/fixXfYi.zig").__aeabi_d2ulz; - @export(__aeabi_d2ulz, .{ .name = "__aeabi_d2ulz", .linkage = linkage }); - - const __aeabi_f2lz = @import("compiler_rt/fixXfYi.zig").__aeabi_f2lz; - @export(__aeabi_f2lz, .{ .name = "__aeabi_f2lz", .linkage = linkage }); - const __aeabi_d2lz = @import("compiler_rt/fixXfYi.zig").__aeabi_d2lz; - @export(__aeabi_d2lz, .{ .name = "__aeabi_d2lz", .linkage = linkage }); - - const __aeabi_d2uiz = @import("compiler_rt/fixXfYi.zig").__aeabi_d2uiz; - @export(__aeabi_d2uiz, .{ .name = "__aeabi_d2uiz", .linkage = linkage }); - - const __aeabi_h2f = @import("compiler_rt/extendXfYf2.zig").__aeabi_h2f; - @export(__aeabi_h2f, .{ .name = "__aeabi_h2f", .linkage = linkage }); - const __aeabi_f2h = @import("compiler_rt/truncXfYf2.zig").__aeabi_f2h; - @export(__aeabi_f2h, .{ .name = "__aeabi_f2h", .linkage = linkage }); - - const __aeabi_i2f = @import("compiler_rt/floatXiYf.zig").__aeabi_i2f; - @export(__aeabi_i2f, .{ .name = "__aeabi_i2f", .linkage = linkage }); - const __aeabi_d2f = @import("compiler_rt/truncXfYf2.zig").__aeabi_d2f; - @export(__aeabi_d2f, .{ .name = "__aeabi_d2f", .linkage = linkage }); - - const __aeabi_fadd = @import("compiler_rt/addXf3.zig").__aeabi_fadd; - @export(__aeabi_fadd, .{ .name = "__aeabi_fadd", .linkage = linkage }); - const __aeabi_dadd = @import("compiler_rt/addXf3.zig").__aeabi_dadd; - @export(__aeabi_dadd, .{ .name = "__aeabi_dadd", .linkage = linkage }); - const __aeabi_fsub = @import("compiler_rt/addXf3.zig").__aeabi_fsub; - @export(__aeabi_fsub, .{ .name = "__aeabi_fsub", .linkage = linkage }); - const __aeabi_dsub = @import("compiler_rt/addXf3.zig").__aeabi_dsub; - @export(__aeabi_dsub, .{ .name = "__aeabi_dsub", .linkage = linkage }); - - const __aeabi_f2uiz = @import("compiler_rt/fixXfYi.zig").__aeabi_f2uiz; - @export(__aeabi_f2uiz, .{ .name = "__aeabi_f2uiz", .linkage = linkage }); - - const __aeabi_f2iz = @import("compiler_rt/fixXfYi.zig").__aeabi_f2iz; - @export(__aeabi_f2iz, .{ .name = "__aeabi_f2iz", .linkage = linkage }); - const __aeabi_d2iz = @import("compiler_rt/fixXfYi.zig").__aeabi_d2iz; - @export(__aeabi_d2iz, .{ .name = "__aeabi_d2iz", .linkage = linkage }); - - const __aeabi_fdiv = @import("compiler_rt/divsf3.zig").__aeabi_fdiv; - @export(__aeabi_fdiv, .{ .name = "__aeabi_fdiv", .linkage = linkage }); - const __aeabi_ddiv = @import("compiler_rt/divdf3.zig").__aeabi_ddiv; - @export(__aeabi_ddiv, .{ .name = "__aeabi_ddiv", .linkage = linkage }); - - const __aeabi_llsl = @import("compiler_rt/shift.zig").__aeabi_llsl; - @export(__aeabi_llsl, .{ .name = "__aeabi_llsl", .linkage = linkage }); - const __aeabi_lasr = @import("compiler_rt/shift.zig").__aeabi_lasr; - @export(__aeabi_lasr, .{ .name = "__aeabi_lasr", .linkage = linkage }); - const __aeabi_llsr = @import("compiler_rt/shift.zig").__aeabi_llsr; - @export(__aeabi_llsr, .{ .name = "__aeabi_llsr", .linkage = linkage }); - - const __aeabi_fcmpeq = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpeq; - @export(__aeabi_fcmpeq, .{ .name = "__aeabi_fcmpeq", .linkage = linkage }); - const __aeabi_fcmplt = @import("compiler_rt/compareXf2.zig").__aeabi_fcmplt; - @export(__aeabi_fcmplt, .{ .name = "__aeabi_fcmplt", .linkage = linkage }); - const __aeabi_fcmple = @import("compiler_rt/compareXf2.zig").__aeabi_fcmple; - @export(__aeabi_fcmple, .{ .name = "__aeabi_fcmple", .linkage = linkage }); - const __aeabi_fcmpge = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpge; - @export(__aeabi_fcmpge, .{ .name = "__aeabi_fcmpge", .linkage = linkage }); - const __aeabi_fcmpgt = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpgt; - @export(__aeabi_fcmpgt, .{ .name = "__aeabi_fcmpgt", .linkage = linkage }); - const __aeabi_fcmpun = @import("compiler_rt/compareXf2.zig").__aeabi_fcmpun; - @export(__aeabi_fcmpun, .{ .name = "__aeabi_fcmpun", .linkage = linkage }); - - const __aeabi_dcmpeq = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpeq; - @export(__aeabi_dcmpeq, .{ .name = "__aeabi_dcmpeq", .linkage = linkage }); - const __aeabi_dcmplt = @import("compiler_rt/compareXf2.zig").__aeabi_dcmplt; - @export(__aeabi_dcmplt, .{ .name = "__aeabi_dcmplt", .linkage = linkage }); - const __aeabi_dcmple = @import("compiler_rt/compareXf2.zig").__aeabi_dcmple; - @export(__aeabi_dcmple, .{ .name = "__aeabi_dcmple", .linkage = linkage }); - const __aeabi_dcmpge = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpge; - @export(__aeabi_dcmpge, .{ .name = "__aeabi_dcmpge", .linkage = linkage }); - const __aeabi_dcmpgt = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpgt; - @export(__aeabi_dcmpgt, .{ .name = "__aeabi_dcmpgt", .linkage = linkage }); - const __aeabi_dcmpun = @import("compiler_rt/compareXf2.zig").__aeabi_dcmpun; - @export(__aeabi_dcmpun, .{ .name = "__aeabi_dcmpun", .linkage = linkage }); - } - - if (arch == .i386 and abi == .msvc) { - // Don't let LLVM apply the stdcall name mangling on those MSVC builtins - const _alldiv = @import("compiler_rt/aulldiv.zig")._alldiv; - @export(_alldiv, .{ .name = "\x01__alldiv", .linkage = strong_linkage }); - const _aulldiv = @import("compiler_rt/aulldiv.zig")._aulldiv; - @export(_aulldiv, .{ .name = "\x01__aulldiv", .linkage = strong_linkage }); - const _allrem = @import("compiler_rt/aullrem.zig")._allrem; - @export(_allrem, .{ .name = "\x01__allrem", .linkage = strong_linkage }); - const _aullrem = @import("compiler_rt/aullrem.zig")._aullrem; - @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage }); - } - - mathExport("ceil", @import("./compiler_rt/ceil.zig")); - mathExport("cos", @import("./compiler_rt/cos.zig")); - mathExport("exp", @import("./compiler_rt/exp.zig")); - mathExport("exp2", @import("./compiler_rt/exp2.zig")); - mathExport("fabs", @import("./compiler_rt/fabs.zig")); - mathExport("floor", @import("./compiler_rt/floor.zig")); - mathExport("fma", @import("./compiler_rt/fma.zig")); - mathExport("fmax", @import("./compiler_rt/fmax.zig")); - mathExport("fmin", @import("./compiler_rt/fmin.zig")); - mathExport("fmod", @import("./compiler_rt/fmod.zig")); - mathExport("log", @import("./compiler_rt/log.zig")); - mathExport("log10", @import("./compiler_rt/log10.zig")); - mathExport("log2", @import("./compiler_rt/log2.zig")); - mathExport("round", @import("./compiler_rt/round.zig")); - mathExport("sin", @import("./compiler_rt/sin.zig")); - mathExport("sincos", @import("./compiler_rt/sincos.zig")); - mathExport("sqrt", @import("./compiler_rt/sqrt.zig")); - mathExport("tan", @import("./compiler_rt/tan.zig")); - mathExport("trunc", @import("./compiler_rt/trunc.zig")); - - if (arch.isSPARC()) { - // SPARC systems use a different naming scheme - const _Qp_add = @import("compiler_rt/sparc.zig")._Qp_add; - @export(_Qp_add, .{ .name = "_Qp_add", .linkage = linkage }); - const _Qp_div = @import("compiler_rt/sparc.zig")._Qp_div; - @export(_Qp_div, .{ .name = "_Qp_div", .linkage = linkage }); - const _Qp_mul = @import("compiler_rt/sparc.zig")._Qp_mul; - @export(_Qp_mul, .{ .name = "_Qp_mul", .linkage = linkage }); - const _Qp_sub = @import("compiler_rt/sparc.zig")._Qp_sub; - @export(_Qp_sub, .{ .name = "_Qp_sub", .linkage = linkage }); - - const _Qp_cmp = @import("compiler_rt/sparc.zig")._Qp_cmp; - @export(_Qp_cmp, .{ .name = "_Qp_cmp", .linkage = linkage }); - const _Qp_feq = @import("compiler_rt/sparc.zig")._Qp_feq; - @export(_Qp_feq, .{ .name = "_Qp_feq", .linkage = linkage }); - const _Qp_fne = @import("compiler_rt/sparc.zig")._Qp_fne; - @export(_Qp_fne, .{ .name = "_Qp_fne", .linkage = linkage }); - const _Qp_flt = @import("compiler_rt/sparc.zig")._Qp_flt; - @export(_Qp_flt, .{ .name = "_Qp_flt", .linkage = linkage }); - const _Qp_fle = @import("compiler_rt/sparc.zig")._Qp_fle; - @export(_Qp_fle, .{ .name = "_Qp_fle", .linkage = linkage }); - const _Qp_fgt = @import("compiler_rt/sparc.zig")._Qp_fgt; - @export(_Qp_fgt, .{ .name = "_Qp_fgt", .linkage = linkage }); - const _Qp_fge = @import("compiler_rt/sparc.zig")._Qp_fge; - @export(_Qp_fge, .{ .name = "_Qp_fge", .linkage = linkage }); - - const _Qp_itoq = @import("compiler_rt/sparc.zig")._Qp_itoq; - @export(_Qp_itoq, .{ .name = "_Qp_itoq", .linkage = linkage }); - const _Qp_uitoq = @import("compiler_rt/sparc.zig")._Qp_uitoq; - @export(_Qp_uitoq, .{ .name = "_Qp_uitoq", .linkage = linkage }); - const _Qp_xtoq = @import("compiler_rt/sparc.zig")._Qp_xtoq; - @export(_Qp_xtoq, .{ .name = "_Qp_xtoq", .linkage = linkage }); - const _Qp_uxtoq = @import("compiler_rt/sparc.zig")._Qp_uxtoq; - @export(_Qp_uxtoq, .{ .name = "_Qp_uxtoq", .linkage = linkage }); - const _Qp_stoq = @import("compiler_rt/sparc.zig")._Qp_stoq; - @export(_Qp_stoq, .{ .name = "_Qp_stoq", .linkage = linkage }); - const _Qp_dtoq = @import("compiler_rt/sparc.zig")._Qp_dtoq; - @export(_Qp_dtoq, .{ .name = "_Qp_dtoq", .linkage = linkage }); - const _Qp_qtoi = @import("compiler_rt/sparc.zig")._Qp_qtoi; - @export(_Qp_qtoi, .{ .name = "_Qp_qtoi", .linkage = linkage }); - const _Qp_qtoui = @import("compiler_rt/sparc.zig")._Qp_qtoui; - @export(_Qp_qtoui, .{ .name = "_Qp_qtoui", .linkage = linkage }); - const _Qp_qtox = @import("compiler_rt/sparc.zig")._Qp_qtox; - @export(_Qp_qtox, .{ .name = "_Qp_qtox", .linkage = linkage }); - const _Qp_qtoux = @import("compiler_rt/sparc.zig")._Qp_qtoux; - @export(_Qp_qtoux, .{ .name = "_Qp_qtoux", .linkage = linkage }); - const _Qp_qtos = @import("compiler_rt/sparc.zig")._Qp_qtos; - @export(_Qp_qtos, .{ .name = "_Qp_qtos", .linkage = linkage }); - const _Qp_qtod = @import("compiler_rt/sparc.zig")._Qp_qtod; - @export(_Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage }); - } - - if (is_ppc and !is_test) { - @export(__addtf3, .{ .name = "__addkf3", .linkage = linkage }); - @export(__subtf3, .{ .name = "__subkf3", .linkage = linkage }); - @export(__multf3, .{ .name = "__mulkf3", .linkage = linkage }); - @export(__divtf3, .{ .name = "__divkf3", .linkage = linkage }); - @export(__extendsftf2, .{ .name = "__extendsfkf2", .linkage = linkage }); - @export(__extenddftf2, .{ .name = "__extenddfkf2", .linkage = linkage }); - @export(__trunctfsf2, .{ .name = "__trunckfsf2", .linkage = linkage }); - @export(__trunctfdf2, .{ .name = "__trunckfdf2", .linkage = linkage }); - @export(__fixtfdi, .{ .name = "__fixkfdi", .linkage = linkage }); - @export(__fixtfsi, .{ .name = "__fixkfsi", .linkage = linkage }); - @export(__fixunstfsi, .{ .name = "__fixunskfsi", .linkage = linkage }); - @export(__fixunstfdi, .{ .name = "__fixunskfdi", .linkage = linkage }); - @export(__floatsitf, .{ .name = "__floatsikf", .linkage = linkage }); - @export(__floatditf, .{ .name = "__floatdikf", .linkage = linkage }); - @export(__floatunditf, .{ .name = "__floatundikf", .linkage = linkage }); - @export(__floatunsitf, .{ .name = "__floatunsikf", .linkage = linkage }); - @export(__floatuntitf, .{ .name = "__floatuntikf", .linkage = linkage }); - - @export(__letf2, .{ .name = "__eqkf2", .linkage = linkage }); - @export(__letf2, .{ .name = "__nekf2", .linkage = linkage }); - @export(__getf2, .{ .name = "__gekf2", .linkage = linkage }); - @export(__letf2, .{ .name = "__ltkf2", .linkage = linkage }); - @export(__letf2, .{ .name = "__lekf2", .linkage = linkage }); - @export(__getf2, .{ .name = "__gtkf2", .linkage = linkage }); - @export(__unordtf2, .{ .name = "__unordkf2", .linkage = linkage }); - } -} - -inline fn mathExport(double_name: []const u8, comptime import: type) void { - const half_name = "__" ++ double_name ++ "h"; - const half_fn = @field(import, half_name); - const float_name = double_name ++ "f"; - const float_fn = @field(import, float_name); - const double_fn = @field(import, double_name); - const long_double_name = double_name ++ "l"; - const xf80_name = "__" ++ double_name ++ "x"; - const xf80_fn = @field(import, xf80_name); - const quad_name = double_name ++ "q"; - const quad_fn = @field(import, quad_name); - - @export(half_fn, .{ .name = half_name, .linkage = linkage }); - @export(float_fn, .{ .name = float_name, .linkage = linkage }); - @export(double_fn, .{ .name = double_name, .linkage = linkage }); - @export(xf80_fn, .{ .name = xf80_name, .linkage = linkage }); - @export(quad_fn, .{ .name = quad_name, .linkage = linkage }); - - if (is_test) return; - - const pairs = .{ - .{ f16, half_fn }, - .{ f32, float_fn }, - .{ f64, double_fn }, - .{ f80, xf80_fn }, - .{ f128, quad_fn }, - }; - - if (builtin.os.tag == .windows) { - // Weak aliases don't work on Windows, so we have to provide the 'l' variants - // as additional function definitions that jump to the real definition. - const long_double_fn = @field(import, long_double_name); - @export(long_double_fn, .{ .name = long_double_name, .linkage = linkage }); - } else { - inline for (pairs) |pair| { - const F = pair[0]; - const func = pair[1]; - if (builtin.target.longDoubleIs(F)) { - @export(func, .{ .name = long_double_name, .linkage = linkage }); - } - } - } - - if (is_ppc) { - // LLVM PPC backend lowers f128 ops with the suffix `f128` instead of `l`. - @export(quad_fn, .{ .name = double_name ++ "f128", .linkage = linkage }); - } -} - -// Avoid dragging in the runtime safety mechanisms into this .o file, -// unless we're trying to test this file. -pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn { - _ = error_return_trace; - @setCold(true); - if (is_test) { - std.debug.panic("{s}", .{msg}); - } else { - unreachable; - } } diff --git a/lib/compiler_rt/absv.zig b/lib/compiler_rt/absv.zig index f14497daf2..3d9476b6c7 100644 --- a/lib/compiler_rt/absv.zig +++ b/lib/compiler_rt/absv.zig @@ -1,6 +1,16 @@ // absv - absolute oVerflow // * @panic, if value can not be represented // - absvXi4_generic for unoptimized version +const std = @import("std"); +const builtin = @import("builtin"); +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__absvsi2, .{ .name = "__absvsi2", .linkage = linkage }); + @export(__absvdi2, .{ .name = "__absvdi2", .linkage = linkage }); + @export(__absvti2, .{ .name = "__absvti2", .linkage = linkage }); +} inline fn absvXi(comptime ST: type, a: ST) ST { const UT = switch (ST) { diff --git a/lib/compiler_rt/addXf3.zig b/lib/compiler_rt/addXf3.zig index 1a9de0fb74..e2cf9d0112 100644 --- a/lib/compiler_rt/addXf3.zig +++ b/lib/compiler_rt/addXf3.zig @@ -3,9 +3,41 @@ // https://github.com/llvm/llvm-project/blob/02d85149a05cb1f6dc49f0ba7a2ceca53718ae17/compiler-rt/lib/builtins/fp_add_impl.inc const std = @import("std"); -const math = std.math; const builtin = @import("builtin"); -const compiler_rt = @import("../compiler_rt.zig"); +const math = std.math; +const arch = builtin.cpu.arch; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; + +const common = @import("common.zig"); +const normalize = common.normalize; +pub const panic = common.panic; + +comptime { + @export(__addsf3, .{ .name = "__addsf3", .linkage = linkage }); + @export(__adddf3, .{ .name = "__adddf3", .linkage = linkage }); + @export(__addxf3, .{ .name = "__addxf3", .linkage = linkage }); + @export(__addtf3, .{ .name = "__addtf3", .linkage = linkage }); + + @export(__subsf3, .{ .name = "__subsf3", .linkage = linkage }); + @export(__subdf3, .{ .name = "__subdf3", .linkage = linkage }); + @export(__subxf3, .{ .name = "__subxf3", .linkage = linkage }); + @export(__subtf3, .{ .name = "__subtf3", .linkage = linkage }); + + if (!is_test) { + if (arch.isARM() or arch.isThumb()) { + @export(__aeabi_fadd, .{ .name = "__aeabi_fadd", .linkage = linkage }); + @export(__aeabi_dadd, .{ .name = "__aeabi_dadd", .linkage = linkage }); + @export(__aeabi_fsub, .{ .name = "__aeabi_fsub", .linkage = linkage }); + @export(__aeabi_dsub, .{ .name = "__aeabi_dsub", .linkage = linkage }); + } + + if (arch.isPPC() or arch.isPPC64()) { + @export(__addkf3, .{ .name = "__addkf3", .linkage = linkage }); + @export(__subkf3, .{ .name = "__subkf3", .linkage = linkage }); + } + } +} pub fn __addsf3(a: f32, b: f32) callconv(.C) f32 { return addXf3(f32, a, b); @@ -29,6 +61,10 @@ pub fn __addtf3(a: f128, b: f128) callconv(.C) f128 { return addXf3(f128, a, b); } +pub fn __addkf3(a: f128, b: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, __addtf3, .{ a, b }); +} + pub fn __subsf3(a: f32, b: f32) callconv(.C) f32 { const neg_b = @bitCast(f32, @bitCast(u32, b) ^ (@as(u32, 1) << 31)); return addXf3(f32, a, neg_b); @@ -44,6 +80,10 @@ pub fn __subtf3(a: f128, b: f128) callconv(.C) f128 { return addXf3(f128, a, neg_b); } +pub fn __subkf3(a: f128, b: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, __subtf3, .{ a, b }); +} + pub fn __aeabi_fadd(a: f32, b: f32) callconv(.AAPCS) f32 { @setRuntimeSafety(false); return @call(.{ .modifier = .always_inline }, __addsf3, .{ a, b }); @@ -65,20 +105,7 @@ pub fn __aeabi_dsub(a: f64, b: f64) callconv(.AAPCS) f64 { } // TODO: restore inline keyword, see: https://github.com/ziglang/zig/issues/2154 -fn normalize(comptime T: type, significand: *std.meta.Int(.unsigned, @typeInfo(T).Float.bits)) i32 { - const bits = @typeInfo(T).Float.bits; - const Z = std.meta.Int(.unsigned, bits); - const S = std.meta.Int(.unsigned, bits - @clz(Z, @as(Z, bits) - 1)); - const fractionalBits = math.floatFractionalBits(T); - const integerBit = @as(Z, 1) << fractionalBits; - - const shift = @clz(std.meta.Int(.unsigned, bits), significand.*) - @clz(Z, integerBit); - significand.* <<= @intCast(S, shift); - return @as(i32, 1) - shift; -} - -// TODO: restore inline keyword, see: https://github.com/ziglang/zig/issues/2154 -fn addXf3(comptime T: type, a: T, b: T) T { +pub fn addXf3(comptime T: type, a: T, b: T) T { const bits = @typeInfo(T).Float.bits; const Z = std.meta.Int(.unsigned, bits); const S = std.meta.Int(.unsigned, bits - @clz(Z, @as(Z, bits) - 1)); diff --git a/lib/compiler_rt/addo.zig b/lib/compiler_rt/addo.zig index 91ed15747c..d14fe36710 100644 --- a/lib/compiler_rt/addo.zig +++ b/lib/compiler_rt/addo.zig @@ -1,4 +1,14 @@ +const std = @import("std"); const builtin = @import("builtin"); +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__addosi4, .{ .name = "__addosi4", .linkage = linkage }); + @export(__addodi4, .{ .name = "__addodi4", .linkage = linkage }); + @export(__addoti4, .{ .name = "__addoti4", .linkage = linkage }); +} // addo - add overflow // * return a+%b. diff --git a/lib/compiler_rt/arm.zig b/lib/compiler_rt/arm.zig index f30d2fd6ec..f974f67cce 100644 --- a/lib/compiler_rt/arm.zig +++ b/lib/compiler_rt/arm.zig @@ -1,5 +1,45 @@ // ARM specific builtins +const std = @import("std"); const builtin = @import("builtin"); +const arch = builtin.cpu.arch; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + if (!builtin.is_test) { + if (arch.isARM() or arch.isThumb()) { + @export(__aeabi_unwind_cpp_pr0, .{ .name = "__aeabi_unwind_cpp_pr0", .linkage = linkage }); + @export(__aeabi_unwind_cpp_pr1, .{ .name = "__aeabi_unwind_cpp_pr1", .linkage = linkage }); + @export(__aeabi_unwind_cpp_pr2, .{ .name = "__aeabi_unwind_cpp_pr2", .linkage = linkage }); + + @export(__aeabi_ldivmod, .{ .name = "__aeabi_ldivmod", .linkage = linkage }); + @export(__aeabi_uldivmod, .{ .name = "__aeabi_uldivmod", .linkage = linkage }); + + @export(__aeabi_idivmod, .{ .name = "__aeabi_idivmod", .linkage = linkage }); + @export(__aeabi_uidivmod, .{ .name = "__aeabi_uidivmod", .linkage = linkage }); + + @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy", .linkage = linkage }); + @export(__aeabi_memcpy4, .{ .name = "__aeabi_memcpy4", .linkage = linkage }); + @export(__aeabi_memcpy8, .{ .name = "__aeabi_memcpy8", .linkage = linkage }); + + @export(__aeabi_memmove, .{ .name = "__aeabi_memmove", .linkage = linkage }); + @export(__aeabi_memmove4, .{ .name = "__aeabi_memmove4", .linkage = linkage }); + @export(__aeabi_memmove8, .{ .name = "__aeabi_memmove8", .linkage = linkage }); + + @export(__aeabi_memset, .{ .name = "__aeabi_memset", .linkage = linkage }); + @export(__aeabi_memset4, .{ .name = "__aeabi_memset4", .linkage = linkage }); + @export(__aeabi_memset8, .{ .name = "__aeabi_memset8", .linkage = linkage }); + + @export(__aeabi_memclr, .{ .name = "__aeabi_memclr", .linkage = linkage }); + @export(__aeabi_memclr4, .{ .name = "__aeabi_memclr4", .linkage = linkage }); + @export(__aeabi_memclr8, .{ .name = "__aeabi_memclr8", .linkage = linkage }); + + if (builtin.os.tag == .linux) { + @export(__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = linkage }); + } + } + } +} const __divmodsi4 = @import("int.zig").__divmodsi4; const __udivmodsi4 = @import("int.zig").__udivmodsi4; @@ -14,11 +54,27 @@ pub fn __aeabi_memcpy(dest: [*]u8, src: [*]u8, n: usize) callconv(.AAPCS) void { @setRuntimeSafety(false); _ = memcpy(dest, src, n); } +pub fn __aeabi_memcpy4(dest: [*]u8, src: [*]u8, n: usize) callconv(.AAPCS) void { + @setRuntimeSafety(false); + _ = memcpy(dest, src, n); +} +pub fn __aeabi_memcpy8(dest: [*]u8, src: [*]u8, n: usize) callconv(.AAPCS) void { + @setRuntimeSafety(false); + _ = memcpy(dest, src, n); +} pub fn __aeabi_memmove(dest: [*]u8, src: [*]u8, n: usize) callconv(.AAPCS) void { @setRuntimeSafety(false); _ = memmove(dest, src, n); } +pub fn __aeabi_memmove4(dest: [*]u8, src: [*]u8, n: usize) callconv(.AAPCS) void { + @setRuntimeSafety(false); + _ = memmove(dest, src, n); +} +pub fn __aeabi_memmove8(dest: [*]u8, src: [*]u8, n: usize) callconv(.AAPCS) void { + @setRuntimeSafety(false); + _ = memmove(dest, src, n); +} pub fn __aeabi_memset(dest: [*]u8, n: usize, c: u8) callconv(.AAPCS) void { @setRuntimeSafety(false); @@ -26,11 +82,27 @@ pub fn __aeabi_memset(dest: [*]u8, n: usize, c: u8) callconv(.AAPCS) void { // two arguments swapped _ = memset(dest, c, n); } +pub fn __aeabi_memset4(dest: [*]u8, n: usize, c: u8) callconv(.AAPCS) void { + @setRuntimeSafety(false); + _ = memset(dest, c, n); +} +pub fn __aeabi_memset8(dest: [*]u8, n: usize, c: u8) callconv(.AAPCS) void { + @setRuntimeSafety(false); + _ = memset(dest, c, n); +} pub fn __aeabi_memclr(dest: [*]u8, n: usize) callconv(.AAPCS) void { @setRuntimeSafety(false); _ = memset(dest, 0, n); } +pub fn __aeabi_memclr4(dest: [*]u8, n: usize) callconv(.AAPCS) void { + @setRuntimeSafety(false); + _ = memset(dest, 0, n); +} +pub fn __aeabi_memclr8(dest: [*]u8, n: usize) callconv(.AAPCS) void { + @setRuntimeSafety(false); + _ = memset(dest, 0, n); +} // Dummy functions to avoid errors during the linking phase pub fn __aeabi_unwind_cpp_pr0() callconv(.C) void {} diff --git a/lib/compiler_rt/atomics.zig b/lib/compiler_rt/atomics.zig index 20545d0791..6935a858aa 100644 --- a/lib/compiler_rt/atomics.zig +++ b/lib/compiler_rt/atomics.zig @@ -2,8 +2,8 @@ const std = @import("std"); const builtin = @import("builtin"); const cpu = builtin.cpu; const arch = cpu.arch; - const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; // This parameter is true iff the target architecture supports the bare minimum // to implement the atomic load/store intrinsics. diff --git a/lib/compiler_rt/aulldiv.zig b/lib/compiler_rt/aulldiv.zig index 7709e17e63..7154cb39a1 100644 --- a/lib/compiler_rt/aulldiv.zig +++ b/lib/compiler_rt/aulldiv.zig @@ -1,4 +1,19 @@ +const std = @import("std"); const builtin = @import("builtin"); +const arch = builtin.cpu.arch; +const abi = builtin.abi; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Strong; +pub const panic = @import("common.zig").panic; + +comptime { + if (arch == .i386 and abi == .msvc) { + // Don't let LLVM apply the stdcall name mangling on those MSVC builtins + @export(_alldiv, .{ .name = "\x01__alldiv", .linkage = linkage }); + @export(_aulldiv, .{ .name = "\x01__aulldiv", .linkage = linkage }); + @export(_allrem, .{ .name = "\x01__allrem", .linkage = linkage }); + @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = linkage }); + } +} pub fn _alldiv(a: i64, b: i64) callconv(.Stdcall) i64 { @setRuntimeSafety(builtin.is_test); diff --git a/lib/compiler_rt/bswap.zig b/lib/compiler_rt/bswap.zig index f1d2138811..bab39dfb59 100644 --- a/lib/compiler_rt/bswap.zig +++ b/lib/compiler_rt/bswap.zig @@ -1,5 +1,14 @@ const std = @import("std"); const builtin = @import("builtin"); +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__bswapsi2, .{ .name = "__bswapsi2", .linkage = linkage }); + @export(__bswapdi2, .{ .name = "__bswapdi2", .linkage = linkage }); + @export(__bswapti2, .{ .name = "__bswapti2", .linkage = linkage }); +} // bswap - byteswap // - bswapXi2 for unoptimized big and little endian diff --git a/lib/compiler_rt/ceil.zig b/lib/compiler_rt/ceil.zig index 06020ea8f8..9e7e4b3c2b 100644 --- a/lib/compiler_rt/ceil.zig +++ b/lib/compiler_rt/ceil.zig @@ -5,8 +5,27 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/ceil.c const std = @import("std"); +const builtin = @import("builtin"); +const arch = builtin.cpu.arch; const math = std.math; const expect = std.testing.expect; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__ceilh, .{ .name = "__ceilh", .linkage = linkage }); + @export(ceilf, .{ .name = "ceilf", .linkage = linkage }); + @export(ceil, .{ .name = "ceil", .linkage = linkage }); + @export(__ceilx, .{ .name = "__ceilx", .linkage = linkage }); + @export(ceilq, .{ .name = "ceilq", .linkage = linkage }); + @export(ceill, .{ .name = "ceill", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(ceilf128, .{ .name = "ceilf128", .linkage = linkage }); + } + } +} pub fn __ceilh(x: f16) callconv(.C) f16 { // TODO: more efficient implementation @@ -111,6 +130,10 @@ pub fn ceilq(x: f128) callconv(.C) f128 { } } +pub fn ceilf128(x: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, ceilq, .{x}); +} + pub fn ceill(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __ceilh(x), diff --git a/lib/compiler_rt/clear_cache.zig b/lib/compiler_rt/clear_cache.zig index 0765a23811..b21606814c 100644 --- a/lib/compiler_rt/clear_cache.zig +++ b/lib/compiler_rt/clear_cache.zig @@ -2,6 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); const arch = builtin.cpu.arch; const os = builtin.os.tag; +pub const panic = @import("common.zig").panic; // Ported from llvm-project d32170dbd5b0d54436537b6b75beaf44324e0c28 @@ -10,7 +11,13 @@ const os = builtin.os.tag; // It is expected to invalidate the instruction cache for the // specified range. -pub fn clear_cache(start: usize, end: usize) callconv(.C) void { +comptime { + if (builtin.zig_backend != .stage2_llvm) { + _ = clear_cache; + } +} + +fn clear_cache(start: usize, end: usize) callconv(.C) void { const x86 = switch (arch) { .i386, .x86_64 => true, else => false, diff --git a/lib/compiler_rt/cmp.zig b/lib/compiler_rt/cmp.zig index 9eb4227527..1ac2e93b06 100644 --- a/lib/compiler_rt/cmp.zig +++ b/lib/compiler_rt/cmp.zig @@ -1,5 +1,17 @@ const std = @import("std"); const builtin = @import("builtin"); +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__cmpsi2, .{ .name = "__cmpsi2", .linkage = linkage }); + @export(__cmpdi2, .{ .name = "__cmpdi2", .linkage = linkage }); + @export(__cmpti2, .{ .name = "__cmpti2", .linkage = linkage }); + @export(__ucmpsi2, .{ .name = "__ucmpsi2", .linkage = linkage }); + @export(__ucmpdi2, .{ .name = "__ucmpdi2", .linkage = linkage }); + @export(__ucmpti2, .{ .name = "__ucmpti2", .linkage = linkage }); +} // cmp - signed compare // - cmpXi2_generic for unoptimized little and big endian diff --git a/lib/compiler_rt/common.zig b/lib/compiler_rt/common.zig new file mode 100644 index 0000000000..7e9bdf81c8 --- /dev/null +++ b/lib/compiler_rt/common.zig @@ -0,0 +1,144 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const math = std.math; +const is_test = builtin.is_test; + +// Avoid dragging in the runtime safety mechanisms into this .o file, +// unless we're trying to test this file. +pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn { + _ = error_return_trace; + @setCold(true); + if (is_test) { + std.debug.panic("{s}", .{msg}); + } else { + unreachable; + } +} + +pub fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { + @setRuntimeSafety(is_test); + switch (Z) { + u16 => { + // 16x16 --> 32 bit multiply + const product = @as(u32, a) * @as(u32, b); + hi.* = @intCast(u16, product >> 16); + lo.* = @truncate(u16, product); + }, + u32 => { + // 32x32 --> 64 bit multiply + const product = @as(u64, a) * @as(u64, b); + hi.* = @truncate(u32, product >> 32); + lo.* = @truncate(u32, product); + }, + u64 => { + const S = struct { + fn loWord(x: u64) u64 { + return @truncate(u32, x); + } + fn hiWord(x: u64) u64 { + return @truncate(u32, x >> 32); + } + }; + // 64x64 -> 128 wide multiply for platforms that don't have such an operation; + // many 64-bit platforms have this operation, but they tend to have hardware + // floating-point, so we don't bother with a special case for them here. + // Each of the component 32x32 -> 64 products + const plolo: u64 = S.loWord(a) * S.loWord(b); + const plohi: u64 = S.loWord(a) * S.hiWord(b); + const philo: u64 = S.hiWord(a) * S.loWord(b); + const phihi: u64 = S.hiWord(a) * S.hiWord(b); + // Sum terms that contribute to lo in a way that allows us to get the carry + const r0: u64 = S.loWord(plolo); + const r1: u64 = S.hiWord(plolo) +% S.loWord(plohi) +% S.loWord(philo); + lo.* = r0 +% (r1 << 32); + // Sum terms contributing to hi with the carry from lo + hi.* = S.hiWord(plohi) +% S.hiWord(philo) +% S.hiWord(r1) +% phihi; + }, + u128 => { + const Word_LoMask = @as(u64, 0x00000000ffffffff); + const Word_HiMask = @as(u64, 0xffffffff00000000); + const Word_FullMask = @as(u64, 0xffffffffffffffff); + const S = struct { + fn Word_1(x: u128) u64 { + return @truncate(u32, x >> 96); + } + fn Word_2(x: u128) u64 { + return @truncate(u32, x >> 64); + } + fn Word_3(x: u128) u64 { + return @truncate(u32, x >> 32); + } + fn Word_4(x: u128) u64 { + return @truncate(u32, x); + } + }; + // 128x128 -> 256 wide multiply for platforms that don't have such an operation; + // many 64-bit platforms have this operation, but they tend to have hardware + // floating-point, so we don't bother with a special case for them here. + + const product11: u64 = S.Word_1(a) * S.Word_1(b); + const product12: u64 = S.Word_1(a) * S.Word_2(b); + const product13: u64 = S.Word_1(a) * S.Word_3(b); + const product14: u64 = S.Word_1(a) * S.Word_4(b); + const product21: u64 = S.Word_2(a) * S.Word_1(b); + const product22: u64 = S.Word_2(a) * S.Word_2(b); + const product23: u64 = S.Word_2(a) * S.Word_3(b); + const product24: u64 = S.Word_2(a) * S.Word_4(b); + const product31: u64 = S.Word_3(a) * S.Word_1(b); + const product32: u64 = S.Word_3(a) * S.Word_2(b); + const product33: u64 = S.Word_3(a) * S.Word_3(b); + const product34: u64 = S.Word_3(a) * S.Word_4(b); + const product41: u64 = S.Word_4(a) * S.Word_1(b); + const product42: u64 = S.Word_4(a) * S.Word_2(b); + const product43: u64 = S.Word_4(a) * S.Word_3(b); + const product44: u64 = S.Word_4(a) * S.Word_4(b); + + const sum0: u128 = @as(u128, product44); + const sum1: u128 = @as(u128, product34) +% + @as(u128, product43); + const sum2: u128 = @as(u128, product24) +% + @as(u128, product33) +% + @as(u128, product42); + const sum3: u128 = @as(u128, product14) +% + @as(u128, product23) +% + @as(u128, product32) +% + @as(u128, product41); + const sum4: u128 = @as(u128, product13) +% + @as(u128, product22) +% + @as(u128, product31); + const sum5: u128 = @as(u128, product12) +% + @as(u128, product21); + const sum6: u128 = @as(u128, product11); + + const r0: u128 = (sum0 & Word_FullMask) +% + ((sum1 & Word_LoMask) << 32); + const r1: u128 = (sum0 >> 64) +% + ((sum1 >> 32) & Word_FullMask) +% + (sum2 & Word_FullMask) +% + ((sum3 << 32) & Word_HiMask); + + lo.* = r0 +% (r1 << 64); + hi.* = (r1 >> 64) +% + (sum1 >> 96) +% + (sum2 >> 64) +% + (sum3 >> 32) +% + sum4 +% + (sum5 << 32) +% + (sum6 << 64); + }, + else => @compileError("unsupported"), + } +} + +// TODO: restore inline keyword, see: https://github.com/ziglang/zig/issues/2154 +pub fn normalize(comptime T: type, significand: *std.meta.Int(.unsigned, @typeInfo(T).Float.bits)) i32 { + const bits = @typeInfo(T).Float.bits; + const Z = std.meta.Int(.unsigned, bits); + const S = std.meta.Int(.unsigned, bits - @clz(Z, @as(Z, bits) - 1)); + const fractionalBits = math.floatFractionalBits(T); + const integerBit = @as(Z, 1) << fractionalBits; + + const shift = @clz(std.meta.Int(.unsigned, bits), significand.*) - @clz(Z, integerBit); + significand.* <<= @intCast(S, shift); + return @as(i32, 1) - shift; +} diff --git a/lib/compiler_rt/compareXf2.zig b/lib/compiler_rt/compareXf2.zig index 9640298f8f..d21a6777e4 100644 --- a/lib/compiler_rt/compareXf2.zig +++ b/lib/compiler_rt/compareXf2.zig @@ -4,6 +4,78 @@ const std = @import("std"); const builtin = @import("builtin"); +const is_test = builtin.is_test; +const arch = builtin.cpu.arch; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__lesf2, .{ .name = "__lesf2", .linkage = linkage }); + @export(__ledf2, .{ .name = "__ledf2", .linkage = linkage }); + @export(__letf2, .{ .name = "__letf2", .linkage = linkage }); + @export(__lexf2, .{ .name = "__lexf2", .linkage = linkage }); + + @export(__gesf2, .{ .name = "__gesf2", .linkage = linkage }); + @export(__gedf2, .{ .name = "__gedf2", .linkage = linkage }); + @export(__getf2, .{ .name = "__getf2", .linkage = linkage }); + @export(__gexf2, .{ .name = "__gexf2", .linkage = linkage }); + + @export(__eqsf2, .{ .name = "__eqsf2", .linkage = linkage }); + @export(__eqdf2, .{ .name = "__eqdf2", .linkage = linkage }); + @export(__eqxf2, .{ .name = "__eqxf2", .linkage = linkage }); + + @export(__ltsf2, .{ .name = "__ltsf2", .linkage = linkage }); + @export(__ltdf2, .{ .name = "__ltdf2", .linkage = linkage }); + @export(__ltxf2, .{ .name = "__ltxf2", .linkage = linkage }); + + @export(__nesf2, .{ .name = "__nesf2", .linkage = linkage }); + @export(__nedf2, .{ .name = "__nedf2", .linkage = linkage }); + @export(__nexf2, .{ .name = "__nexf2", .linkage = linkage }); + + @export(__gtsf2, .{ .name = "__gtsf2", .linkage = linkage }); + @export(__gtdf2, .{ .name = "__gtdf2", .linkage = linkage }); + @export(__gtxf2, .{ .name = "__gtxf2", .linkage = linkage }); + + @export(__unordsf2, .{ .name = "__unordsf2", .linkage = linkage }); + @export(__unorddf2, .{ .name = "__unorddf2", .linkage = linkage }); + @export(__unordtf2, .{ .name = "__unordtf2", .linkage = linkage }); + + if (!is_test) { + @export(__cmpsf2, .{ .name = "__cmpsf2", .linkage = linkage }); + @export(__cmpdf2, .{ .name = "__cmpdf2", .linkage = linkage }); + @export(__cmptf2, .{ .name = "__cmptf2", .linkage = linkage }); + @export(__eqtf2, .{ .name = "__eqtf2", .linkage = linkage }); + @export(__lttf2, .{ .name = "__lttf2", .linkage = linkage }); + @export(__gttf2, .{ .name = "__gttf2", .linkage = linkage }); + @export(__netf2, .{ .name = "__netf2", .linkage = linkage }); + + if (arch.isARM() or arch.isThumb()) { + @export(__aeabi_fcmpeq, .{ .name = "__aeabi_fcmpeq", .linkage = linkage }); + @export(__aeabi_fcmplt, .{ .name = "__aeabi_fcmplt", .linkage = linkage }); + @export(__aeabi_fcmple, .{ .name = "__aeabi_fcmple", .linkage = linkage }); + @export(__aeabi_fcmpge, .{ .name = "__aeabi_fcmpge", .linkage = linkage }); + @export(__aeabi_fcmpgt, .{ .name = "__aeabi_fcmpgt", .linkage = linkage }); + @export(__aeabi_fcmpun, .{ .name = "__aeabi_fcmpun", .linkage = linkage }); + + @export(__aeabi_dcmpeq, .{ .name = "__aeabi_dcmpeq", .linkage = linkage }); + @export(__aeabi_dcmplt, .{ .name = "__aeabi_dcmplt", .linkage = linkage }); + @export(__aeabi_dcmple, .{ .name = "__aeabi_dcmple", .linkage = linkage }); + @export(__aeabi_dcmpge, .{ .name = "__aeabi_dcmpge", .linkage = linkage }); + @export(__aeabi_dcmpgt, .{ .name = "__aeabi_dcmpgt", .linkage = linkage }); + @export(__aeabi_dcmpun, .{ .name = "__aeabi_dcmpun", .linkage = linkage }); + } + + if (arch.isPPC() or arch.isPPC64()) { + @export(__eqkf2, .{ .name = "__eqkf2", .linkage = linkage }); + @export(__nekf2, .{ .name = "__nekf2", .linkage = linkage }); + @export(__gekf2, .{ .name = "__gekf2", .linkage = linkage }); + @export(__ltkf2, .{ .name = "__ltkf2", .linkage = linkage }); + @export(__lekf2, .{ .name = "__lekf2", .linkage = linkage }); + @export(__gtkf2, .{ .name = "__gtkf2", .linkage = linkage }); + @export(__unordkf2, .{ .name = "__unordkf2", .linkage = linkage }); + } + } +} const LE = enum(i32) { Less = -1, @@ -98,20 +170,24 @@ pub fn __gesf2(a: f32, b: f32) callconv(.C) i32 { return @bitCast(i32, float); } +pub fn __cmpsf2(a: f32, b: f32) callconv(.C) i32 { + return @call(.{ .modifier = .always_inline }, __lesf2, .{ a, b }); +} + pub fn __eqsf2(a: f32, b: f32) callconv(.C) i32 { - return __lesf2(a, b); + return @call(.{ .modifier = .always_inline }, __lesf2, .{ a, b }); } pub fn __ltsf2(a: f32, b: f32) callconv(.C) i32 { - return __lesf2(a, b); + return @call(.{ .modifier = .always_inline }, __lesf2, .{ a, b }); } pub fn __nesf2(a: f32, b: f32) callconv(.C) i32 { - return __lesf2(a, b); + return @call(.{ .modifier = .always_inline }, __lesf2, .{ a, b }); } pub fn __gtsf2(a: f32, b: f32) callconv(.C) i32 { - return __gesf2(a, b); + return @call(.{ .modifier = .always_inline }, __gesf2, .{ a, b }); } // Comparison between f64 @@ -128,20 +204,24 @@ pub fn __gedf2(a: f64, b: f64) callconv(.C) i32 { return @bitCast(i32, float); } +pub fn __cmpdf2(a: f64, b: f64) callconv(.C) i32 { + return @call(.{ .modifier = .always_inline }, __ledf2, .{ a, b }); +} + pub fn __eqdf2(a: f64, b: f64) callconv(.C) i32 { - return __ledf2(a, b); + return @call(.{ .modifier = .always_inline }, __ledf2, .{ a, b }); } pub fn __ltdf2(a: f64, b: f64) callconv(.C) i32 { - return __ledf2(a, b); + return @call(.{ .modifier = .always_inline }, __ledf2, .{ a, b }); } pub fn __nedf2(a: f64, b: f64) callconv(.C) i32 { - return __ledf2(a, b); + return @call(.{ .modifier = .always_inline }, __ledf2, .{ a, b }); } pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 { - return __gedf2(a, b); + return @call(.{ .modifier = .always_inline }, __gedf2, .{ a, b }); } // Comparison between f80 @@ -196,19 +276,19 @@ pub fn __gexf2(a: f80, b: f80) callconv(.C) i32 { } pub fn __eqxf2(a: f80, b: f80) callconv(.C) i32 { - return __lexf2(a, b); + return @call(.{ .modifier = .always_inline }, __lexf2, .{ a, b }); } pub fn __ltxf2(a: f80, b: f80) callconv(.C) i32 { - return __lexf2(a, b); + return @call(.{ .modifier = .always_inline }, __lexf2, .{ a, b }); } pub fn __nexf2(a: f80, b: f80) callconv(.C) i32 { - return __lexf2(a, b); + return @call(.{ .modifier = .always_inline }, __lexf2, .{ a, b }); } pub fn __gtxf2(a: f80, b: f80) callconv(.C) i32 { - return __gexf2(a, b); + return @call(.{ .modifier = .always_inline }, __gexf2, .{ a, b }); } // Comparison between f128 @@ -225,20 +305,48 @@ pub fn __getf2(a: f128, b: f128) callconv(.C) i32 { return @bitCast(i32, float); } +pub fn __cmptf2(a: f128, b: f128) callconv(.C) i32 { + return @call(.{ .modifier = .always_inline }, __letf2, .{ a, b }); +} + pub fn __eqtf2(a: f128, b: f128) callconv(.C) i32 { - return __letf2(a, b); + return @call(.{ .modifier = .always_inline }, __letf2, .{ a, b }); } pub fn __lttf2(a: f128, b: f128) callconv(.C) i32 { - return __letf2(a, b); + return @call(.{ .modifier = .always_inline }, __letf2, .{ a, b }); } pub fn __netf2(a: f128, b: f128) callconv(.C) i32 { - return __letf2(a, b); + return @call(.{ .modifier = .always_inline }, __letf2, .{ a, b }); } pub fn __gttf2(a: f128, b: f128) callconv(.C) i32 { - return __getf2(a, b); + return @call(.{ .modifier = .always_inline }, __getf2, .{ a, b }); +} + +pub fn __eqkf2(a: f128, b: f128) callconv(.C) i32 { + return @call(.{ .modifier = .always_inline }, __letf2, .{ a, b }); +} + +pub fn __nekf2(a: f128, b: f128) callconv(.C) i32 { + return @call(.{ .modifier = .always_inline }, __letf2, .{ a, b }); +} + +pub fn __gekf2(a: f128, b: f128) callconv(.C) i32 { + return @call(.{ .modifier = .always_inline }, __getf2, .{ a, b }); +} + +pub fn __ltkf2(a: f128, b: f128) callconv(.C) i32 { + return @call(.{ .modifier = .always_inline }, __letf2, .{ a, b }); +} + +pub fn __lekf2(a: f128, b: f128) callconv(.C) i32 { + return @call(.{ .modifier = .always_inline }, __letf2, .{ a, b }); +} + +pub fn __gtkf2(a: f128, b: f128) callconv(.C) i32 { + return @call(.{ .modifier = .always_inline }, __getf2, .{ a, b }); } // Unordered comparison between f32/f64/f128 @@ -258,6 +366,10 @@ pub fn __unordtf2(a: f128, b: f128) callconv(.C) i32 { return unordcmp(f128, a, b); } +pub fn __unordkf2(a: f128, b: f128) callconv(.C) i32 { + return @call(.{ .modifier = .always_inline }, __unordtf2, .{ a, b }); +} + // ARM EABI intrinsics pub fn __aeabi_fcmpeq(a: f32, b: f32) callconv(.AAPCS) i32 { diff --git a/lib/compiler_rt/cos.zig b/lib/compiler_rt/cos.zig index e01f458243..22df0a707d 100644 --- a/lib/compiler_rt/cos.zig +++ b/lib/compiler_rt/cos.zig @@ -1,11 +1,30 @@ const std = @import("std"); +const builtin = @import("builtin"); +const arch = builtin.cpu.arch; const math = std.math; const expect = std.testing.expect; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; const trig = @import("trig.zig"); const rem_pio2 = @import("rem_pio2.zig").rem_pio2; const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; +comptime { + @export(__cosh, .{ .name = "__cosh", .linkage = linkage }); + @export(cosf, .{ .name = "cosf", .linkage = linkage }); + @export(cos, .{ .name = "cos", .linkage = linkage }); + @export(__cosx, .{ .name = "__cosx", .linkage = linkage }); + @export(cosq, .{ .name = "cosq", .linkage = linkage }); + @export(cosl, .{ .name = "cosl", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(cosf128, .{ .name = "cosf128", .linkage = linkage }); + } + } +} + pub fn __cosh(a: f16) callconv(.C) f16 { // TODO: more efficient implementation return @floatCast(f16, cosf(a)); @@ -107,6 +126,10 @@ pub fn cosq(a: f128) callconv(.C) f128 { return cos(@floatCast(f64, a)); } +pub fn cosf128(a: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, cosq, .{a}); +} + pub fn cosl(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __cosh(x), diff --git a/lib/compiler_rt/count0bits.zig b/lib/compiler_rt/count0bits.zig index 1f6d28ae0b..386a5c9657 100644 --- a/lib/compiler_rt/count0bits.zig +++ b/lib/compiler_rt/count0bits.zig @@ -1,5 +1,20 @@ const std = @import("std"); const builtin = @import("builtin"); +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__clzsi2, .{ .name = "__clzsi2", .linkage = linkage }); + @export(__clzdi2, .{ .name = "__clzdi2", .linkage = linkage }); + @export(__clzti2, .{ .name = "__clzti2", .linkage = linkage }); + @export(__ctzsi2, .{ .name = "__ctzsi2", .linkage = linkage }); + @export(__ctzdi2, .{ .name = "__ctzdi2", .linkage = linkage }); + @export(__ctzti2, .{ .name = "__ctzti2", .linkage = linkage }); + @export(__ffssi2, .{ .name = "__ffssi2", .linkage = linkage }); + @export(__ffsdi2, .{ .name = "__ffsdi2", .linkage = linkage }); + @export(__ffsti2, .{ .name = "__ffsti2", .linkage = linkage }); +} // clz - count leading zeroes // - clzXi2 for unoptimized little and big endian diff --git a/lib/compiler_rt/divdf3.zig b/lib/compiler_rt/divdf3.zig index 137f5c02f9..6d626272b5 100644 --- a/lib/compiler_rt/divdf3.zig +++ b/lib/compiler_rt/divdf3.zig @@ -4,6 +4,24 @@ const std = @import("std"); const builtin = @import("builtin"); +const arch = builtin.cpu.arch; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; + +const common = @import("common.zig"); +const normalize = common.normalize; +const wideMultiply = common.wideMultiply; +pub const panic = common.panic; + +comptime { + @export(__divdf3, .{ .name = "__divdf3", .linkage = linkage }); + + if (!is_test) { + if (arch.isARM() or arch.isThumb()) { + @export(__aeabi_ddiv, .{ .name = "__aeabi_ddiv", .linkage = linkage }); + } + } +} pub fn __divdf3(a: f64, b: f64) callconv(.C) f64 { @setRuntimeSafety(builtin.is_test); @@ -202,125 +220,6 @@ pub fn __divdf3(a: f64, b: f64) callconv(.C) f64 { } } -pub fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { - @setRuntimeSafety(builtin.is_test); - switch (Z) { - u32 => { - // 32x32 --> 64 bit multiply - const product = @as(u64, a) * @as(u64, b); - hi.* = @truncate(u32, product >> 32); - lo.* = @truncate(u32, product); - }, - u64 => { - const S = struct { - fn loWord(x: u64) u64 { - return @truncate(u32, x); - } - fn hiWord(x: u64) u64 { - return @truncate(u32, x >> 32); - } - }; - // 64x64 -> 128 wide multiply for platforms that don't have such an operation; - // many 64-bit platforms have this operation, but they tend to have hardware - // floating-point, so we don't bother with a special case for them here. - // Each of the component 32x32 -> 64 products - const plolo: u64 = S.loWord(a) * S.loWord(b); - const plohi: u64 = S.loWord(a) * S.hiWord(b); - const philo: u64 = S.hiWord(a) * S.loWord(b); - const phihi: u64 = S.hiWord(a) * S.hiWord(b); - // Sum terms that contribute to lo in a way that allows us to get the carry - const r0: u64 = S.loWord(plolo); - const r1: u64 = S.hiWord(plolo) +% S.loWord(plohi) +% S.loWord(philo); - lo.* = r0 +% (r1 << 32); - // Sum terms contributing to hi with the carry from lo - hi.* = S.hiWord(plohi) +% S.hiWord(philo) +% S.hiWord(r1) +% phihi; - }, - u128 => { - const Word_LoMask = @as(u64, 0x00000000ffffffff); - const Word_HiMask = @as(u64, 0xffffffff00000000); - const Word_FullMask = @as(u64, 0xffffffffffffffff); - const S = struct { - fn Word_1(x: u128) u64 { - return @truncate(u32, x >> 96); - } - fn Word_2(x: u128) u64 { - return @truncate(u32, x >> 64); - } - fn Word_3(x: u128) u64 { - return @truncate(u32, x >> 32); - } - fn Word_4(x: u128) u64 { - return @truncate(u32, x); - } - }; - // 128x128 -> 256 wide multiply for platforms that don't have such an operation; - // many 64-bit platforms have this operation, but they tend to have hardware - // floating-point, so we don't bother with a special case for them here. - - const product11: u64 = S.Word_1(a) * S.Word_1(b); - const product12: u64 = S.Word_1(a) * S.Word_2(b); - const product13: u64 = S.Word_1(a) * S.Word_3(b); - const product14: u64 = S.Word_1(a) * S.Word_4(b); - const product21: u64 = S.Word_2(a) * S.Word_1(b); - const product22: u64 = S.Word_2(a) * S.Word_2(b); - const product23: u64 = S.Word_2(a) * S.Word_3(b); - const product24: u64 = S.Word_2(a) * S.Word_4(b); - const product31: u64 = S.Word_3(a) * S.Word_1(b); - const product32: u64 = S.Word_3(a) * S.Word_2(b); - const product33: u64 = S.Word_3(a) * S.Word_3(b); - const product34: u64 = S.Word_3(a) * S.Word_4(b); - const product41: u64 = S.Word_4(a) * S.Word_1(b); - const product42: u64 = S.Word_4(a) * S.Word_2(b); - const product43: u64 = S.Word_4(a) * S.Word_3(b); - const product44: u64 = S.Word_4(a) * S.Word_4(b); - - const sum0: u128 = @as(u128, product44); - const sum1: u128 = @as(u128, product34) +% - @as(u128, product43); - const sum2: u128 = @as(u128, product24) +% - @as(u128, product33) +% - @as(u128, product42); - const sum3: u128 = @as(u128, product14) +% - @as(u128, product23) +% - @as(u128, product32) +% - @as(u128, product41); - const sum4: u128 = @as(u128, product13) +% - @as(u128, product22) +% - @as(u128, product31); - const sum5: u128 = @as(u128, product12) +% - @as(u128, product21); - const sum6: u128 = @as(u128, product11); - - const r0: u128 = (sum0 & Word_FullMask) +% - ((sum1 & Word_LoMask) << 32); - const r1: u128 = (sum0 >> 64) +% - ((sum1 >> 32) & Word_FullMask) +% - (sum2 & Word_FullMask) +% - ((sum3 << 32) & Word_HiMask); - - lo.* = r0 +% (r1 << 64); - hi.* = (r1 >> 64) +% - (sum1 >> 96) +% - (sum2 >> 64) +% - (sum3 >> 32) +% - sum4 +% - (sum5 << 32) +% - (sum6 << 64); - }, - else => @compileError("unsupported"), - } -} - -pub fn normalize(comptime T: type, significand: *std.meta.Int(.unsigned, @typeInfo(T).Float.bits)) i32 { - @setRuntimeSafety(builtin.is_test); - const Z = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); - const integerBit = @as(Z, 1) << std.math.floatFractionalBits(T); - - const shift = @clz(Z, significand.*) - @clz(Z, integerBit); - significand.* <<= @intCast(std.math.Log2Int(Z), shift); - return @as(i32, 1) - shift; -} - pub fn __aeabi_ddiv(a: f64, b: f64) callconv(.AAPCS) f64 { @setRuntimeSafety(false); return @call(.{ .modifier = .always_inline }, __divdf3, .{ a, b }); diff --git a/lib/compiler_rt/divsf3.zig b/lib/compiler_rt/divsf3.zig index 5e7dc7bb44..a0c1590eb3 100644 --- a/lib/compiler_rt/divsf3.zig +++ b/lib/compiler_rt/divsf3.zig @@ -4,6 +4,23 @@ const std = @import("std"); const builtin = @import("builtin"); +const arch = builtin.cpu.arch; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; + +const common = @import("common.zig"); +const normalize = common.normalize; +pub const panic = common.panic; + +comptime { + @export(__divsf3, .{ .name = "__divsf3", .linkage = linkage }); + + if (!is_test) { + if (arch.isARM() or arch.isThumb()) { + @export(__aeabi_fdiv, .{ .name = "__aeabi_fdiv", .linkage = linkage }); + } + } +} pub fn __divsf3(a: f32, b: f32) callconv(.C) f32 { @setRuntimeSafety(builtin.is_test); @@ -184,17 +201,6 @@ pub fn __divsf3(a: f32, b: f32) callconv(.C) f32 { } } -fn normalize(comptime T: type, significand: *std.meta.Int(.unsigned, @typeInfo(T).Float.bits)) i32 { - @setRuntimeSafety(builtin.is_test); - const Z = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); - const significandBits = std.math.floatMantissaBits(T); - const implicitBit = @as(Z, 1) << significandBits; - - const shift = @clz(Z, significand.*) - @clz(Z, implicitBit); - significand.* <<= @intCast(std.math.Log2Int(Z), shift); - return 1 - shift; -} - pub fn __aeabi_fdiv(a: f32, b: f32) callconv(.AAPCS) f32 { @setRuntimeSafety(false); return @call(.{ .modifier = .always_inline }, __divsf3, .{ a, b }); diff --git a/lib/compiler_rt/divtf3.zig b/lib/compiler_rt/divtf3.zig index fc26c60266..436db41dc2 100644 --- a/lib/compiler_rt/divtf3.zig +++ b/lib/compiler_rt/divtf3.zig @@ -1,8 +1,27 @@ const std = @import("std"); const builtin = @import("builtin"); +const arch = builtin.cpu.arch; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -const normalize = @import("divdf3.zig").normalize; -const wideMultiply = @import("divdf3.zig").wideMultiply; +const common = @import("common.zig"); +const normalize = common.normalize; +const wideMultiply = common.wideMultiply; +pub const panic = common.panic; + +comptime { + @export(__divtf3, .{ .name = "__divtf3", .linkage = linkage }); + + if (!is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(__divkf3, .{ .name = "__divkf3", .linkage = linkage }); + } + } +} + +pub fn __divkf3(a: f128, b: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, __divtf3, .{ a, b }); +} pub fn __divtf3(a: f128, b: f128) callconv(.C) f128 { @setRuntimeSafety(builtin.is_test); diff --git a/lib/compiler_rt/divti3.zig b/lib/compiler_rt/divti3.zig index 41286c3414..20da8d3e1d 100644 --- a/lib/compiler_rt/divti3.zig +++ b/lib/compiler_rt/divti3.zig @@ -1,5 +1,31 @@ -const udivmod = @import("udivmod.zig").udivmod; +const std = @import("std"); const builtin = @import("builtin"); +const udivmod = @import("udivmod.zig").udivmod; +const arch = builtin.cpu.arch; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + if (builtin.os.tag == .windows) { + switch (arch) { + .i386 => { + @export(__divti3, .{ .name = "__divti3", .linkage = linkage }); + }, + .x86_64 => { + // The "ti" functions must use Vector(2, u64) parameter types to adhere to the ABI + // that LLVM expects compiler-rt to have. + @export(__divti3_windows_x86_64, .{ .name = "__divti3", .linkage = linkage }); + }, + else => {}, + } + if (arch.isAARCH64()) { + @export(__divti3, .{ .name = "__divti3", .linkage = linkage }); + } + } else { + @export(__divti3, .{ .name = "__divti3", .linkage = linkage }); + } +} pub fn __divti3(a: i128, b: i128) callconv(.C) i128 { @setRuntimeSafety(builtin.is_test); diff --git a/lib/compiler_rt/divxf3.zig b/lib/compiler_rt/divxf3.zig index 5f5ed667ec..56811dfc8f 100644 --- a/lib/compiler_rt/divxf3.zig +++ b/lib/compiler_rt/divxf3.zig @@ -1,7 +1,17 @@ const std = @import("std"); const builtin = @import("builtin"); -const normalize = @import("divdf3.zig").normalize; -const wideMultiply = @import("divdf3.zig").wideMultiply; +const arch = builtin.cpu.arch; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; + +const common = @import("common.zig"); +const normalize = common.normalize; +const wideMultiply = common.wideMultiply; +pub const panic = common.panic; + +comptime { + @export(__divxf3, .{ .name = "__divxf3", .linkage = linkage }); +} pub fn __divxf3(a: f80, b: f80) callconv(.C) f80 { @setRuntimeSafety(builtin.is_test); diff --git a/lib/compiler_rt/emutls.zig b/lib/compiler_rt/emutls.zig index e6aa8930e9..7483177e0f 100644 --- a/lib/compiler_rt/emutls.zig +++ b/lib/compiler_rt/emutls.zig @@ -6,6 +6,8 @@ const std = @import("std"); const builtin = @import("builtin"); +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; const abort = std.os.abort; const assert = std.debug.assert; @@ -16,7 +18,9 @@ const expect = std.testing.expect; const gcc_word = usize; comptime { - assert(builtin.link_libc); + if (builtin.link_libc and builtin.os.tag == .openbsd) { + @export(__emutls_get_address, .{ .name = "__emutls_get_address", .linkage = linkage }); + } } /// public entrypoint for generated code using EmulatedTLS @@ -319,6 +323,8 @@ const emutls_control = extern struct { }; test "simple_allocator" { + if (!builtin.link_libc or builtin.os.tag != .openbsd) return error.SkipZigTest; + var data1: *[64]u8 = simple_allocator.alloc([64]u8); defer simple_allocator.free(data1); for (data1) |*c| { @@ -333,6 +339,8 @@ test "simple_allocator" { } test "__emutls_get_address zeroed" { + if (!builtin.link_libc or builtin.os.tag != .openbsd) return error.SkipZigTest; + var ctl = emutls_control.init(usize, null); try expect(ctl.object.index == 0); @@ -352,6 +360,8 @@ test "__emutls_get_address zeroed" { } test "__emutls_get_address with default_value" { + if (!builtin.link_libc or builtin.os.tag != .openbsd) return error.SkipZigTest; + var value: usize = 5678; // default value var ctl = emutls_control.init(usize, &value); try expect(ctl.object.index == 0); @@ -370,6 +380,8 @@ test "__emutls_get_address with default_value" { } test "test default_value with differents sizes" { + if (!builtin.link_libc or builtin.os.tag != .openbsd) return error.SkipZigTest; + const testType = struct { fn _testType(comptime T: type, value: T) !void { var def: T = value; diff --git a/lib/compiler_rt/exp.zig b/lib/compiler_rt/exp.zig index a2c5d0e550..c7f6c95da6 100644 --- a/lib/compiler_rt/exp.zig +++ b/lib/compiler_rt/exp.zig @@ -5,8 +5,27 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/exp.c const std = @import("std"); +const builtin = @import("builtin"); +const arch = builtin.cpu.arch; const math = std.math; const expect = std.testing.expect; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__exph, .{ .name = "__exph", .linkage = linkage }); + @export(expf, .{ .name = "expf", .linkage = linkage }); + @export(exp, .{ .name = "exp", .linkage = linkage }); + @export(__expx, .{ .name = "__expx", .linkage = linkage }); + @export(expq, .{ .name = "expq", .linkage = linkage }); + @export(expl, .{ .name = "expl", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(expf128, .{ .name = "expf128", .linkage = linkage }); + } + } +} pub fn __exph(a: f16) callconv(.C) f16 { // TODO: more efficient implementation @@ -182,6 +201,10 @@ pub fn expq(a: f128) callconv(.C) f128 { return exp(@floatCast(f64, a)); } +pub fn expf128(a: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, expq, .{a}); +} + pub fn expl(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __exph(x), diff --git a/lib/compiler_rt/exp2.zig b/lib/compiler_rt/exp2.zig index cbcb53c99f..614df2cec0 100644 --- a/lib/compiler_rt/exp2.zig +++ b/lib/compiler_rt/exp2.zig @@ -5,8 +5,27 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/exp2.c const std = @import("std"); +const builtin = @import("builtin"); +const arch = builtin.cpu.arch; const math = std.math; const expect = std.testing.expect; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__exp2h, .{ .name = "__exp2h", .linkage = linkage }); + @export(exp2f, .{ .name = "exp2f", .linkage = linkage }); + @export(exp2, .{ .name = "exp2", .linkage = linkage }); + @export(__exp2x, .{ .name = "__exp2x", .linkage = linkage }); + @export(exp2q, .{ .name = "exp2q", .linkage = linkage }); + @export(exp2l, .{ .name = "exp2l", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(exp2f128, .{ .name = "exp2f128", .linkage = linkage }); + } + } +} pub fn __exp2h(x: f16) callconv(.C) f16 { // TODO: more efficient implementation @@ -149,6 +168,10 @@ pub fn exp2q(x: f128) callconv(.C) f128 { return exp2(@floatCast(f64, x)); } +pub fn exp2f128(x: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, exp2q, .{x}); +} + pub fn exp2l(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __exp2h(x), diff --git a/lib/compiler_rt/extendXfYf2.zig b/lib/compiler_rt/extendXfYf2.zig index 8622fe1513..bb21e5f681 100644 --- a/lib/compiler_rt/extendXfYf2.zig +++ b/lib/compiler_rt/extendXfYf2.zig @@ -1,7 +1,31 @@ const std = @import("std"); const builtin = @import("builtin"); const is_test = builtin.is_test; -const native_arch = builtin.cpu.arch; +const arch = builtin.cpu.arch; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__extenddftf2, .{ .name = "__extenddftf2", .linkage = linkage }); + @export(__extendsftf2, .{ .name = "__extendsftf2", .linkage = linkage }); + @export(__extendhfsf2, .{ .name = "__extendhfsf2", .linkage = linkage }); + @export(__extendhftf2, .{ .name = "__extendhftf2", .linkage = linkage }); + @export(__extendsfdf2, .{ .name = "__extendsfdf2", .linkage = linkage }); + + if (!is_test) { + @export(__gnu_h2f_ieee, .{ .name = "__gnu_h2f_ieee", .linkage = linkage }); + + if (arch.isARM() or arch.isThumb()) { + @export(__aeabi_f2d, .{ .name = "__aeabi_f2d", .linkage = linkage }); + @export(__aeabi_h2f, .{ .name = "__aeabi_h2f", .linkage = linkage }); + } + + if (arch.isPPC() or arch.isPPC64()) { + @export(__extendsfkf2, .{ .name = "__extendsfkf2", .linkage = linkage }); + @export(__extenddfkf2, .{ .name = "__extenddfkf2", .linkage = linkage }); + } + } +} pub fn __extendsfdf2(a: f32) callconv(.C) f64 { return extendXfYf2(f64, f32, @bitCast(u32, a)); @@ -11,18 +35,30 @@ pub fn __extenddftf2(a: f64) callconv(.C) f128 { return extendXfYf2(f128, f64, @bitCast(u64, a)); } +pub fn __extenddfkf2(a: f64) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, __extenddftf2, .{a}); +} + pub fn __extendsftf2(a: f32) callconv(.C) f128 { return extendXfYf2(f128, f32, @bitCast(u32, a)); } +pub fn __extendsfkf2(a: f32) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, __extendsftf2, .{a}); +} + // AArch64 is the only ABI (at the moment) to support f16 arguments without the // need for extending them to wider fp types. -pub const F16T = if (native_arch.isAARCH64()) f16 else u16; +pub const F16T = if (arch.isAARCH64()) f16 else u16; pub fn __extendhfsf2(a: F16T) callconv(.C) f32 { return extendXfYf2(f32, f16, @bitCast(u16, a)); } +pub fn __gnu_h2f_ieee(a: F16T) callconv(.C) f32 { + return @call(.{ .modifier = .always_inline }, __extendhfsf2, .{a}); +} + pub fn __extendhftf2(a: F16T) callconv(.C) f128 { return extendXfYf2(f128, f16, @bitCast(u16, a)); } diff --git a/lib/compiler_rt/extend_f80.zig b/lib/compiler_rt/extend_f80.zig index e68fb5fcf8..fe5a38fc68 100644 --- a/lib/compiler_rt/extend_f80.zig +++ b/lib/compiler_rt/extend_f80.zig @@ -1,21 +1,30 @@ const std = @import("std"); const builtin = @import("builtin"); const is_test = builtin.is_test; -const native_arch = builtin.cpu.arch; +const arch = builtin.cpu.arch; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__extendhfxf2, .{ .name = "__extendhfxf2", .linkage = linkage }); + @export(__extendsfxf2, .{ .name = "__extendsfxf2", .linkage = linkage }); + @export(__extenddfxf2, .{ .name = "__extenddfxf2", .linkage = linkage }); + @export(__extendxftf2, .{ .name = "__extendxftf2", .linkage = linkage }); +} // AArch64 is the only ABI (at the moment) to support f16 arguments without the // need for extending them to wider fp types. -pub const F16T = if (native_arch.isAARCH64()) f16 else u16; +const F16T = if (arch.isAARCH64()) f16 else u16; -pub fn __extendhfxf2(a: F16T) callconv(.C) f80 { +fn __extendhfxf2(a: F16T) callconv(.C) f80 { return extendF80(f16, @bitCast(u16, a)); } -pub fn __extendsfxf2(a: f32) callconv(.C) f80 { +fn __extendsfxf2(a: f32) callconv(.C) f80 { return extendF80(f32, @bitCast(u32, a)); } -pub fn __extenddfxf2(a: f64) callconv(.C) f80 { +fn __extenddfxf2(a: f64) callconv(.C) f80 { return extendF80(f64, @bitCast(u64, a)); } @@ -86,7 +95,7 @@ inline fn extendF80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(s return std.math.make_f80(dst); } -pub fn __extendxftf2(a: f80) callconv(.C) f128 { +fn __extendxftf2(a: f80) callconv(.C) f128 { @setRuntimeSafety(builtin.is_test); const src_int_bit: u64 = 0x8000000000000000; diff --git a/lib/compiler_rt/fabs.zig b/lib/compiler_rt/fabs.zig index 396fdd46b7..afe231f098 100644 --- a/lib/compiler_rt/fabs.zig +++ b/lib/compiler_rt/fabs.zig @@ -1,4 +1,23 @@ const std = @import("std"); +const builtin = @import("builtin"); +const arch = builtin.cpu.arch; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__fabsh, .{ .name = "__fabsh", .linkage = linkage }); + @export(fabsf, .{ .name = "fabsf", .linkage = linkage }); + @export(fabs, .{ .name = "fabs", .linkage = linkage }); + @export(__fabsx, .{ .name = "__fabsx", .linkage = linkage }); + @export(fabsq, .{ .name = "fabsq", .linkage = linkage }); + @export(fabsl, .{ .name = "fabsl", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(fabsf128, .{ .name = "fabsf128", .linkage = linkage }); + } + } +} pub fn __fabsh(a: f16) callconv(.C) f16 { return generic_fabs(a); @@ -20,6 +39,10 @@ pub fn fabsq(a: f128) callconv(.C) f128 { return generic_fabs(a); } +pub fn fabsf128(a: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, fabsq, .{a}); +} + pub fn fabsl(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __fabsh(x), diff --git a/lib/compiler_rt/fixXfYi.zig b/lib/compiler_rt/fixXfYi.zig index 01832ec56f..c568bba366 100644 --- a/lib/compiler_rt/fixXfYi.zig +++ b/lib/compiler_rt/fixXfYi.zig @@ -1,7 +1,79 @@ const std = @import("std"); +const builtin = @import("builtin"); const math = std.math; const Log2Int = math.Log2Int; -const is_test = @import("builtin").is_test; +const arch = builtin.cpu.arch; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + // Float -> Integral Conversion + + // Conversion from f32 + @export(__fixsfsi, .{ .name = "__fixsfsi", .linkage = linkage }); + @export(__fixunssfsi, .{ .name = "__fixunssfsi", .linkage = linkage }); + + @export(__fixsfdi, .{ .name = "__fixsfdi", .linkage = linkage }); + @export(__fixunssfdi, .{ .name = "__fixunssfdi", .linkage = linkage }); + + @export(__fixsfti, .{ .name = "__fixsfti", .linkage = linkage }); + @export(__fixunssfti, .{ .name = "__fixunssfti", .linkage = linkage }); + + // Conversion from f64 + @export(__fixdfsi, .{ .name = "__fixdfsi", .linkage = linkage }); + @export(__fixunsdfsi, .{ .name = "__fixunsdfsi", .linkage = linkage }); + + @export(__fixdfdi, .{ .name = "__fixdfdi", .linkage = linkage }); + @export(__fixunsdfdi, .{ .name = "__fixunsdfdi", .linkage = linkage }); + + @export(__fixdfti, .{ .name = "__fixdfti", .linkage = linkage }); + @export(__fixunsdfti, .{ .name = "__fixunsdfti", .linkage = linkage }); + + // Conversion from f80 + @export(__fixxfsi, .{ .name = "__fixxfsi", .linkage = linkage }); + @export(__fixunsxfsi, .{ .name = "__fixunsxfsi", .linkage = linkage }); + + @export(__fixxfdi, .{ .name = "__fixxfdi", .linkage = linkage }); + @export(__fixunsxfdi, .{ .name = "__fixunsxfdi", .linkage = linkage }); + + @export(__fixxfti, .{ .name = "__fixxfti", .linkage = linkage }); + @export(__fixunsxfti, .{ .name = "__fixunsxfti", .linkage = linkage }); + + // Conversion from f128 + @export(__fixtfsi, .{ .name = "__fixtfsi", .linkage = linkage }); + @export(__fixunstfsi, .{ .name = "__fixunstfsi", .linkage = linkage }); + + @export(__fixtfdi, .{ .name = "__fixtfdi", .linkage = linkage }); + @export(__fixunstfdi, .{ .name = "__fixunstfdi", .linkage = linkage }); + + @export(__fixtfti, .{ .name = "__fixtfti", .linkage = linkage }); + @export(__fixunstfti, .{ .name = "__fixunstfti", .linkage = linkage }); + + if (!is_test) { + if (arch.isARM() or arch.isThumb()) { + @export(__aeabi_f2ulz, .{ .name = "__aeabi_f2ulz", .linkage = linkage }); + @export(__aeabi_d2ulz, .{ .name = "__aeabi_d2ulz", .linkage = linkage }); + + @export(__aeabi_f2lz, .{ .name = "__aeabi_f2lz", .linkage = linkage }); + @export(__aeabi_d2lz, .{ .name = "__aeabi_d2lz", .linkage = linkage }); + + @export(__aeabi_d2uiz, .{ .name = "__aeabi_d2uiz", .linkage = linkage }); + + @export(__aeabi_f2uiz, .{ .name = "__aeabi_f2uiz", .linkage = linkage }); + + @export(__aeabi_f2iz, .{ .name = "__aeabi_f2iz", .linkage = linkage }); + @export(__aeabi_d2iz, .{ .name = "__aeabi_d2iz", .linkage = linkage }); + } + + if (arch.isPPC() or arch.isPPC64()) { + @export(__fixkfdi, .{ .name = "__fixkfdi", .linkage = linkage }); + @export(__fixkfsi, .{ .name = "__fixkfsi", .linkage = linkage }); + @export(__fixunskfsi, .{ .name = "__fixunskfsi", .linkage = linkage }); + @export(__fixunskfdi, .{ .name = "__fixunskfdi", .linkage = linkage }); + } + } +} pub inline fn fixXfYi(comptime I: type, a: anytype) I { @setRuntimeSafety(is_test); @@ -163,18 +235,34 @@ pub fn __fixtfsi(a: f128) callconv(.C) i32 { return fixXfYi(i32, a); } +pub fn __fixkfsi(a: f128) callconv(.C) i32 { + return __fixtfsi(a); +} + pub fn __fixunstfsi(a: f128) callconv(.C) u32 { return fixXfYi(u32, a); } +pub fn __fixunskfsi(a: f128) callconv(.C) u32 { + return @call(.{ .modifier = .always_inline }, __fixunstfsi, .{a}); +} + pub fn __fixtfdi(a: f128) callconv(.C) i64 { return fixXfYi(i64, a); } +pub fn __fixkfdi(a: f128) callconv(.C) i64 { + return @call(.{ .modifier = .always_inline }, __fixtfdi, .{a}); +} + pub fn __fixunstfdi(a: f128) callconv(.C) u64 { return fixXfYi(u64, a); } +pub fn __fixunskfdi(a: f128) callconv(.C) u64 { + return @call(.{ .modifier = .always_inline }, __fixunstfdi, .{a}); +} + pub fn __fixtfti(a: f128) callconv(.C) i128 { return fixXfYi(i128, a); } diff --git a/lib/compiler_rt/floatXiYf.zig b/lib/compiler_rt/floatXiYf.zig index 068413f715..25f5743491 100644 --- a/lib/compiler_rt/floatXiYf.zig +++ b/lib/compiler_rt/floatXiYf.zig @@ -1,8 +1,77 @@ const builtin = @import("builtin"); -const is_test = builtin.is_test; const std = @import("std"); const math = std.math; const expect = std.testing.expect; +const arch = builtin.cpu.arch; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + // Integral -> Float Conversion + + // Conversion to f32 + @export(__floatsisf, .{ .name = "__floatsisf", .linkage = linkage }); + @export(__floatunsisf, .{ .name = "__floatunsisf", .linkage = linkage }); + + @export(__floatundisf, .{ .name = "__floatundisf", .linkage = linkage }); + @export(__floatdisf, .{ .name = "__floatdisf", .linkage = linkage }); + + @export(__floattisf, .{ .name = "__floattisf", .linkage = linkage }); + @export(__floatuntisf, .{ .name = "__floatuntisf", .linkage = linkage }); + + // Conversion to f64 + @export(__floatsidf, .{ .name = "__floatsidf", .linkage = linkage }); + @export(__floatunsidf, .{ .name = "__floatunsidf", .linkage = linkage }); + + @export(__floatdidf, .{ .name = "__floatdidf", .linkage = linkage }); + @export(__floatundidf, .{ .name = "__floatundidf", .linkage = linkage }); + + @export(__floattidf, .{ .name = "__floattidf", .linkage = linkage }); + @export(__floatuntidf, .{ .name = "__floatuntidf", .linkage = linkage }); + + // Conversion to f80 + @export(__floatsixf, .{ .name = "__floatsixf", .linkage = linkage }); + @export(__floatunsixf, .{ .name = "__floatunsixf", .linkage = linkage }); + + @export(__floatdixf, .{ .name = "__floatdixf", .linkage = linkage }); + @export(__floatundixf, .{ .name = "__floatundixf", .linkage = linkage }); + + @export(__floattixf, .{ .name = "__floattixf", .linkage = linkage }); + @export(__floatuntixf, .{ .name = "__floatuntixf", .linkage = linkage }); + + // Conversion to f128 + @export(__floatsitf, .{ .name = "__floatsitf", .linkage = linkage }); + @export(__floatunsitf, .{ .name = "__floatunsitf", .linkage = linkage }); + + @export(__floatditf, .{ .name = "__floatditf", .linkage = linkage }); + @export(__floatunditf, .{ .name = "__floatunditf", .linkage = linkage }); + + @export(__floattitf, .{ .name = "__floattitf", .linkage = linkage }); + @export(__floatuntitf, .{ .name = "__floatuntitf", .linkage = linkage }); + + if (!is_test) { + if (arch.isARM() or arch.isThumb()) { + @export(__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = linkage }); + @export(__aeabi_l2d, .{ .name = "__aeabi_l2d", .linkage = linkage }); + @export(__aeabi_l2f, .{ .name = "__aeabi_l2f", .linkage = linkage }); + @export(__aeabi_ui2d, .{ .name = "__aeabi_ui2d", .linkage = linkage }); + @export(__aeabi_ul2d, .{ .name = "__aeabi_ul2d", .linkage = linkage }); + @export(__aeabi_ui2f, .{ .name = "__aeabi_ui2f", .linkage = linkage }); + @export(__aeabi_ul2f, .{ .name = "__aeabi_ul2f", .linkage = linkage }); + + @export(__aeabi_i2f, .{ .name = "__aeabi_i2f", .linkage = linkage }); + } + + if (arch.isPPC() or arch.isPPC64()) { + @export(__floatsikf, .{ .name = "__floatsikf", .linkage = linkage }); + @export(__floatdikf, .{ .name = "__floatdikf", .linkage = linkage }); + @export(__floatundikf, .{ .name = "__floatundikf", .linkage = linkage }); + @export(__floatunsikf, .{ .name = "__floatunsikf", .linkage = linkage }); + @export(__floatuntikf, .{ .name = "__floatuntikf", .linkage = linkage }); + } + } +} pub fn floatXiYf(comptime T: type, x: anytype) T { @setRuntimeSafety(is_test); @@ -163,18 +232,34 @@ pub fn __floatsitf(a: i32) callconv(.C) f128 { return floatXiYf(f128, a); } +pub fn __floatsikf(a: i32) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, __floatsitf, .{a}); +} + pub fn __floatunsitf(a: u32) callconv(.C) f128 { return floatXiYf(f128, a); } +pub fn __floatunsikf(a: u32) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, __floatunsitf, .{a}); +} + pub fn __floatditf(a: i64) callconv(.C) f128 { return floatXiYf(f128, a); } +pub fn __floatdikf(a: i64) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, __floatditf, .{a}); +} + pub fn __floatunditf(a: u64) callconv(.C) f128 { return floatXiYf(f128, a); } +pub fn __floatundikf(a: u64) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, __floatunditf, .{a}); +} + pub fn __floattitf(a: i128) callconv(.C) f128 { return floatXiYf(f128, a); } @@ -183,6 +268,10 @@ pub fn __floatuntitf(a: u128) callconv(.C) f128 { return floatXiYf(f128, a); } +pub fn __floatuntikf(a: u128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, __floatuntitf, .{a}); +} + // Conversion to f32 pub fn __aeabi_ui2f(arg: u32) callconv(.AAPCS) f32 { return floatXiYf(f32, arg); diff --git a/lib/compiler_rt/floor.zig b/lib/compiler_rt/floor.zig index 783898fca7..f4b1c2fea4 100644 --- a/lib/compiler_rt/floor.zig +++ b/lib/compiler_rt/floor.zig @@ -5,8 +5,27 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/floor.c const std = @import("std"); +const builtin = @import("builtin"); const math = std.math; const expect = std.testing.expect; +const arch = builtin.cpu.arch; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__floorh, .{ .name = "__floorh", .linkage = linkage }); + @export(floorf, .{ .name = "floorf", .linkage = linkage }); + @export(floor, .{ .name = "floor", .linkage = linkage }); + @export(__floorx, .{ .name = "__floorx", .linkage = linkage }); + @export(floorq, .{ .name = "floorq", .linkage = linkage }); + @export(floorl, .{ .name = "floorl", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(floorf128, .{ .name = "floorf128", .linkage = linkage }); + } + } +} pub fn __floorh(x: f16) callconv(.C) f16 { var u = @bitCast(u16, x); @@ -141,6 +160,10 @@ pub fn floorq(x: f128) callconv(.C) f128 { } } +pub fn floorf128(x: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, floorq, .{x}); +} + pub fn floorl(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __floorh(x), diff --git a/lib/compiler_rt/fma.zig b/lib/compiler_rt/fma.zig index b121db212c..98e77e536c 100644 --- a/lib/compiler_rt/fma.zig +++ b/lib/compiler_rt/fma.zig @@ -6,8 +6,27 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/fma.c const std = @import("std"); +const builtin = @import("builtin"); const math = std.math; const expect = std.testing.expect; +const arch = builtin.cpu.arch; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__fmah, .{ .name = "__fmah", .linkage = linkage }); + @export(fmaf, .{ .name = "fmaf", .linkage = linkage }); + @export(fma, .{ .name = "fma", .linkage = linkage }); + @export(__fmax, .{ .name = "__fmax", .linkage = linkage }); + @export(fmaq, .{ .name = "fmaq", .linkage = linkage }); + @export(fmal, .{ .name = "fmal", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(fmaf128, .{ .name = "fmaf128", .linkage = linkage }); + } + } +} pub fn __fmah(x: f16, y: f16, z: f16) callconv(.C) f16 { // TODO: more efficient implementation @@ -135,6 +154,10 @@ pub fn fmaq(x: f128, y: f128, z: f128) callconv(.C) f128 { } } +pub fn fmaf128(x: f128, y: f128, z: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, fmaq, .{ x, y, z }); +} + pub fn fmal(x: c_longdouble, y: c_longdouble, z: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __fmah(x, y, z), diff --git a/lib/compiler_rt/fmax.zig b/lib/compiler_rt/fmax.zig index defc935afc..50c2ab5269 100644 --- a/lib/compiler_rt/fmax.zig +++ b/lib/compiler_rt/fmax.zig @@ -1,5 +1,24 @@ const std = @import("std"); +const builtin = @import("builtin"); const math = std.math; +const arch = builtin.cpu.arch; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__fmaxh, .{ .name = "__fmaxh", .linkage = linkage }); + @export(fmaxf, .{ .name = "fmaxf", .linkage = linkage }); + @export(fmax, .{ .name = "fmax", .linkage = linkage }); + @export(__fmaxx, .{ .name = "__fmaxx", .linkage = linkage }); + @export(fmaxq, .{ .name = "fmaxq", .linkage = linkage }); + @export(fmaxl, .{ .name = "fmaxl", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(fmaxf128, .{ .name = "fmaxf128", .linkage = linkage }); + } + } +} pub fn __fmaxh(x: f16, y: f16) callconv(.C) f16 { return generic_fmax(f16, x, y); @@ -21,6 +40,10 @@ pub fn fmaxq(x: f128, y: f128) callconv(.C) f128 { return generic_fmax(f128, x, y); } +pub fn fmaxf128(x: f128, y: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, fmaxq, .{ x, y }); +} + pub fn fmaxl(x: c_longdouble, y: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __fmaxh(x, y), diff --git a/lib/compiler_rt/fmin.zig b/lib/compiler_rt/fmin.zig index e93300bd4b..b4960d0544 100644 --- a/lib/compiler_rt/fmin.zig +++ b/lib/compiler_rt/fmin.zig @@ -1,5 +1,24 @@ const std = @import("std"); +const builtin = @import("builtin"); const math = std.math; +const arch = builtin.cpu.arch; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__fminh, .{ .name = "__fminh", .linkage = linkage }); + @export(fminf, .{ .name = "fminf", .linkage = linkage }); + @export(fmin, .{ .name = "fmin", .linkage = linkage }); + @export(__fminx, .{ .name = "__fminx", .linkage = linkage }); + @export(fminq, .{ .name = "fminq", .linkage = linkage }); + @export(fminl, .{ .name = "fminl", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(fminf128, .{ .name = "fminf128", .linkage = linkage }); + } + } +} pub fn __fminh(x: f16, y: f16) callconv(.C) f16 { return generic_fmin(f16, x, y); @@ -21,6 +40,10 @@ pub fn fminq(x: f128, y: f128) callconv(.C) f128 { return generic_fmin(f128, x, y); } +pub fn fminf128(x: f128, y: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, fminq, .{ x, y }); +} + pub fn fminl(x: c_longdouble, y: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __fminh(x, y), diff --git a/lib/compiler_rt/fmod.zig b/lib/compiler_rt/fmod.zig index 5d413ca37d..0f4c088777 100644 --- a/lib/compiler_rt/fmod.zig +++ b/lib/compiler_rt/fmod.zig @@ -2,7 +2,27 @@ const builtin = @import("builtin"); const std = @import("std"); const math = std.math; const assert = std.debug.assert; -const normalize = @import("divdf3.zig").normalize; +const arch = builtin.cpu.arch; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; + +const common = @import("common.zig"); +const normalize = common.normalize; +pub const panic = common.panic; + +comptime { + @export(__fmodh, .{ .name = "__fmodh", .linkage = linkage }); + @export(fmodf, .{ .name = "fmodf", .linkage = linkage }); + @export(fmod, .{ .name = "fmod", .linkage = linkage }); + @export(__fmodx, .{ .name = "__fmodx", .linkage = linkage }); + @export(fmodq, .{ .name = "fmodq", .linkage = linkage }); + @export(fmodl, .{ .name = "fmodl", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(fmodf128, .{ .name = "fmodf128", .linkage = linkage }); + } + } +} pub fn __fmodh(x: f16, y: f16) callconv(.C) f16 { // TODO: more efficient implementation @@ -237,6 +257,10 @@ pub fn fmodq(a: f128, b: f128) callconv(.C) f128 { return amod; } +pub fn fmodf128(a: f128, b: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, fmodq, .{ a, b }); +} + pub fn fmodl(a: c_longdouble, b: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __fmodh(a, b), diff --git a/lib/compiler_rt/int.zig b/lib/compiler_rt/int.zig index 0f3400d37e..447f867267 100644 --- a/lib/compiler_rt/int.zig +++ b/lib/compiler_rt/int.zig @@ -4,9 +4,36 @@ const std = @import("std"); const testing = std.testing; const maxInt = std.math.maxInt; const minInt = std.math.minInt; +const arch = builtin.cpu.arch; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; const udivmod = @import("udivmod.zig").udivmod; +comptime { + @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = linkage }); + @export(__mulsi3, .{ .name = "__mulsi3", .linkage = linkage }); + @export(__divmoddi4, .{ .name = "__divmoddi4", .linkage = linkage }); + @export(__divsi3, .{ .name = "__divsi3", .linkage = linkage }); + @export(__divdi3, .{ .name = "__divdi3", .linkage = linkage }); + @export(__udivsi3, .{ .name = "__udivsi3", .linkage = linkage }); + @export(__udivdi3, .{ .name = "__udivdi3", .linkage = linkage }); + @export(__modsi3, .{ .name = "__modsi3", .linkage = linkage }); + @export(__moddi3, .{ .name = "__moddi3", .linkage = linkage }); + @export(__umodsi3, .{ .name = "__umodsi3", .linkage = linkage }); + @export(__umoddi3, .{ .name = "__umoddi3", .linkage = linkage }); + @export(__divmodsi4, .{ .name = "__divmodsi4", .linkage = linkage }); + @export(__udivmodsi4, .{ .name = "__udivmodsi4", .linkage = linkage }); + + if (!is_test) { + if (arch.isARM() or arch.isThumb()) { + @export(__aeabi_idiv, .{ .name = "__aeabi_idiv", .linkage = linkage }); + @export(__aeabi_uidiv, .{ .name = "__aeabi_uidiv", .linkage = linkage }); + } + } +} + pub fn __divmoddi4(a: i64, b: i64, rem: *i64) callconv(.C) i64 { @setRuntimeSafety(builtin.is_test); @@ -187,6 +214,10 @@ pub fn __divsi3(n: i32, d: i32) callconv(.C) i32 { return @bitCast(i32, (res ^ sign) -% sign); } +pub fn __aeabi_idiv(n: i32, d: i32) callconv(.C) i32 { + return @call(.{ .modifier = .always_inline }, __divsi3, .{ n, d }); +} + test "test_divsi3" { const cases = [_][3]i32{ [_]i32{ 0, 1, 0 }, @@ -253,6 +284,10 @@ pub fn __udivsi3(n: u32, d: u32) callconv(.C) u32 { return q; } +pub fn __aeabi_uidiv(n: u32, d: u32) callconv(.C) u32 { + return @call(.{ .modifier = .always_inline }, __udivsi3, .{ n, d }); +} + test "test_udivsi3" { const cases = [_][3]u32{ [_]u32{ 0x00000000, 0x00000001, 0x00000000 }, diff --git a/lib/compiler_rt/log.zig b/lib/compiler_rt/log.zig index 6e705dae60..991c94ef27 100644 --- a/lib/compiler_rt/log.zig +++ b/lib/compiler_rt/log.zig @@ -5,8 +5,27 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/ln.c const std = @import("std"); +const builtin = @import("builtin"); const math = std.math; const testing = std.testing; +const arch = builtin.cpu.arch; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__logh, .{ .name = "__logh", .linkage = linkage }); + @export(logf, .{ .name = "logf", .linkage = linkage }); + @export(log, .{ .name = "log", .linkage = linkage }); + @export(__logx, .{ .name = "__logx", .linkage = linkage }); + @export(logq, .{ .name = "logq", .linkage = linkage }); + @export(logl, .{ .name = "logl", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(logf128, .{ .name = "logf128", .linkage = linkage }); + } + } +} pub fn __logh(a: f16) callconv(.C) f16 { // TODO: more efficient implementation @@ -131,6 +150,10 @@ pub fn logq(a: f128) callconv(.C) f128 { return log(@floatCast(f64, a)); } +pub fn logf128(a: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, logq, .{a}); +} + pub fn logl(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __logh(x), diff --git a/lib/compiler_rt/log10.zig b/lib/compiler_rt/log10.zig index 47499d2739..8031879761 100644 --- a/lib/compiler_rt/log10.zig +++ b/lib/compiler_rt/log10.zig @@ -5,9 +5,28 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/log10.c const std = @import("std"); +const builtin = @import("builtin"); const math = std.math; const testing = std.testing; const maxInt = std.math.maxInt; +const arch = builtin.cpu.arch; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__log10h, .{ .name = "__log10h", .linkage = linkage }); + @export(log10f, .{ .name = "log10f", .linkage = linkage }); + @export(log10, .{ .name = "log10", .linkage = linkage }); + @export(__log10x, .{ .name = "__log10x", .linkage = linkage }); + @export(log10q, .{ .name = "log10q", .linkage = linkage }); + @export(log10l, .{ .name = "log10l", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(log10f128, .{ .name = "log10f128", .linkage = linkage }); + } + } +} pub fn __log10h(a: f16) callconv(.C) f16 { // TODO: more efficient implementation @@ -159,6 +178,10 @@ pub fn log10q(a: f128) callconv(.C) f128 { return log10(@floatCast(f64, a)); } +pub fn log10f128(a: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, log10q, .{a}); +} + pub fn log10l(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __log10h(x), diff --git a/lib/compiler_rt/log2.zig b/lib/compiler_rt/log2.zig index aa294b33fd..d2a1dd1189 100644 --- a/lib/compiler_rt/log2.zig +++ b/lib/compiler_rt/log2.zig @@ -5,9 +5,28 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/log2.c const std = @import("std"); +const builtin = @import("builtin"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; +const arch = builtin.cpu.arch; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__log2h, .{ .name = "__log2h", .linkage = linkage }); + @export(log2f, .{ .name = "log2f", .linkage = linkage }); + @export(log2, .{ .name = "log2", .linkage = linkage }); + @export(__log2x, .{ .name = "__log2x", .linkage = linkage }); + @export(log2q, .{ .name = "log2q", .linkage = linkage }); + @export(log2l, .{ .name = "log2l", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(log2f128, .{ .name = "log2f128", .linkage = linkage }); + } + } +} pub fn __log2h(a: f16) callconv(.C) f16 { // TODO: more efficient implementation @@ -151,6 +170,10 @@ pub fn log2q(a: f128) callconv(.C) f128 { return log2(@floatCast(f64, a)); } +pub fn log2f128(a: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, log2q, .{a}); +} + pub fn log2l(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __log2h(x), diff --git a/lib/compiler_rt/modti3.zig b/lib/compiler_rt/modti3.zig index 42cbda9627..a0bd2615d4 100644 --- a/lib/compiler_rt/modti3.zig +++ b/lib/compiler_rt/modti3.zig @@ -2,9 +2,34 @@ // // https://github.com/llvm/llvm-project/blob/2ffb1b0413efa9a24eb3c49e710e36f92e2cb50b/compiler-rt/lib/builtins/modti3.c -const udivmod = @import("udivmod.zig").udivmod; +const std = @import("std"); const builtin = @import("builtin"); -const compiler_rt = @import("../compiler_rt.zig"); +const udivmod = @import("udivmod.zig").udivmod; +const arch = builtin.cpu.arch; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + if (builtin.os.tag == .windows) { + switch (arch) { + .i386 => { + @export(__modti3, .{ .name = "__modti3", .linkage = linkage }); + }, + .x86_64 => { + // The "ti" functions must use Vector(2, u64) parameter types to adhere to the ABI + // that LLVM expects compiler-rt to have. + @export(__modti3_windows_x86_64, .{ .name = "__modti3", .linkage = linkage }); + }, + else => {}, + } + if (arch.isAARCH64()) { + @export(__modti3, .{ .name = "__modti3", .linkage = linkage }); + } + } else { + @export(__modti3, .{ .name = "__modti3", .linkage = linkage }); + } +} pub fn __modti3(a: i128, b: i128) callconv(.C) i128 { @setRuntimeSafety(builtin.is_test); diff --git a/lib/compiler_rt/mulXf3.zig b/lib/compiler_rt/mulXf3.zig index 147efd0ab5..dc6ac98407 100644 --- a/lib/compiler_rt/mulXf3.zig +++ b/lib/compiler_rt/mulXf3.zig @@ -5,8 +5,32 @@ const std = @import("std"); const math = std.math; const builtin = @import("builtin"); -const compiler_rt = @import("../compiler_rt.zig"); +const arch = builtin.cpu.arch; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; +comptime { + @export(__mulsf3, .{ .name = "__mulsf3", .linkage = linkage }); + @export(__muldf3, .{ .name = "__muldf3", .linkage = linkage }); + @export(__mulxf3, .{ .name = "__mulxf3", .linkage = linkage }); + @export(__multf3, .{ .name = "__multf3", .linkage = linkage }); + + if (!is_test) { + if (arch.isARM() or arch.isThumb()) { + @export(__aeabi_fmul, .{ .name = "__aeabi_fmul", .linkage = linkage }); + @export(__aeabi_dmul, .{ .name = "__aeabi_dmul", .linkage = linkage }); + } + + if (arch.isPPC() or arch.isPPC64()) { + @export(__mulkf3, .{ .name = "__mulkf3", .linkage = linkage }); + } + } +} + +pub fn __mulkf3(a: f128, b: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, __multf3, .{ a, b }); +} pub fn __multf3(a: f128, b: f128) callconv(.C) f128 { return mulXf3(f128, a, b); } @@ -30,7 +54,7 @@ pub fn __aeabi_dmul(a: f64, b: f64) callconv(.C) f64 { return @call(.{ .modifier = .always_inline }, __muldf3, .{ a, b }); } -fn mulXf3(comptime T: type, a: T, b: T) T { +pub fn mulXf3(comptime T: type, a: T, b: T) T { @setRuntimeSafety(builtin.is_test); const typeWidth = @typeInfo(T).Float.bits; const significandBits = math.floatMantissaBits(T); diff --git a/lib/compiler_rt/muldi3.zig b/lib/compiler_rt/muldi3.zig index f0d857e1e9..8f317adee4 100644 --- a/lib/compiler_rt/muldi3.zig +++ b/lib/compiler_rt/muldi3.zig @@ -1,7 +1,20 @@ const std = @import("std"); const builtin = @import("builtin"); -const is_test = builtin.is_test; const native_endian = builtin.cpu.arch.endian(); +const arch = builtin.cpu.arch; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__muldi3, .{ .name = "__muldi3", .linkage = linkage }); + + if (!is_test) { + if (arch.isARM() or arch.isThumb()) { + @export(__aeabi_lmul, .{ .name = "__aeabi_lmul", .linkage = linkage }); + } + } +} // Ported from // https://github.com/llvm/llvm-project/blob/llvmorg-9.0.0/compiler-rt/lib/builtins/muldi3.c @@ -20,7 +33,11 @@ const dwords = extern union { }, }; -fn __muldsi3(a: u32, b: u32) i64 { +pub fn __aeabi_lmul(a: i64, b: i64) callconv(.C) i64 { + return @call(.{ .modifier = .always_inline }, __muldi3, .{ a, b }); +} + +pub fn __muldsi3(a: u32, b: u32) i64 { @setRuntimeSafety(is_test); const bits_in_word_2 = @sizeOf(i32) * 8 / 2; diff --git a/lib/compiler_rt/mulo.zig b/lib/compiler_rt/mulo.zig index 78590e5ce1..4fd99351bc 100644 --- a/lib/compiler_rt/mulo.zig +++ b/lib/compiler_rt/mulo.zig @@ -1,6 +1,15 @@ -const builtin = @import("builtin"); const std = @import("std"); +const builtin = @import("builtin"); const math = std.math; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__mulosi4, .{ .name = "__mulosi4", .linkage = linkage }); + @export(__mulodi4, .{ .name = "__mulodi4", .linkage = linkage }); + @export(__muloti4, .{ .name = "__muloti4", .linkage = linkage }); +} // mulo - multiplication overflow // * return a*%b. diff --git a/lib/compiler_rt/multi3.zig b/lib/compiler_rt/multi3.zig index a088dbcf9e..838cc3f1df 100644 --- a/lib/compiler_rt/multi3.zig +++ b/lib/compiler_rt/multi3.zig @@ -1,13 +1,33 @@ -const compiler_rt = @import("../compiler_rt.zig"); const std = @import("std"); const builtin = @import("builtin"); +const arch = builtin.cpu.arch; const is_test = builtin.is_test; const native_endian = builtin.cpu.arch.endian(); +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; // Ported from git@github.com:llvm-project/llvm-project-20170507.git // ae684fad6d34858c014c94da69c15e7774a633c3 // 2018-08-13 +comptime { + if (builtin.os.tag == .windows) { + switch (arch) { + .i386 => { + @export(__multi3, .{ .name = "__multi3", .linkage = linkage }); + }, + .x86_64 => { + // The "ti" functions must use Vector(2, u64) parameter types to adhere to the ABI + // that LLVM expects compiler-rt to have. + @export(__multi3_windows_x86_64, .{ .name = "__multi3", .linkage = linkage }); + }, + else => {}, + } + } else { + @export(__multi3, .{ .name = "__multi3", .linkage = linkage }); + } +} + pub fn __multi3(a: i128, b: i128) callconv(.C) i128 { @setRuntimeSafety(is_test); const x = twords{ .all = a }; @@ -25,7 +45,7 @@ pub fn __multi3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { })); } -fn __mulddi3(a: u64, b: u64) i128 { +pub fn __mulddi3(a: u64, b: u64) i128 { const bits_in_dword_2 = (@sizeOf(i64) * 8) / 2; const lower_mask = ~@as(u64, 0) >> bits_in_dword_2; var r: twords = undefined; diff --git a/lib/compiler_rt/negXf2.zig b/lib/compiler_rt/negXf2.zig index 06528b7570..f33336f965 100644 --- a/lib/compiler_rt/negXf2.zig +++ b/lib/compiler_rt/negXf2.zig @@ -1,4 +1,21 @@ const std = @import("std"); +const builtin = @import("builtin"); +const arch = builtin.cpu.arch; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__negsf2, .{ .name = "__negsf2", .linkage = linkage }); + @export(__negdf2, .{ .name = "__negdf2", .linkage = linkage }); + + if (!is_test) { + if (arch.isARM() or arch.isThumb()) { + @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = linkage }); + @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = linkage }); + } + } +} pub fn __negsf2(a: f32) callconv(.C) f32 { return negXf2(f32, a); diff --git a/lib/compiler_rt/negXi2.zig b/lib/compiler_rt/negXi2.zig index 15102b5df7..0ab5a93f62 100644 --- a/lib/compiler_rt/negXi2.zig +++ b/lib/compiler_rt/negXi2.zig @@ -1,5 +1,14 @@ const std = @import("std"); const builtin = @import("builtin"); +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__negsi2, .{ .name = "__negsi2", .linkage = linkage }); + @export(__negdi2, .{ .name = "__negdi2", .linkage = linkage }); + @export(__negti2, .{ .name = "__negti2", .linkage = linkage }); +} // neg - negate (the number) // - negXi2 for unoptimized little and big endian diff --git a/lib/compiler_rt/negv.zig b/lib/compiler_rt/negv.zig index 09abb040d5..b9198dd380 100644 --- a/lib/compiler_rt/negv.zig +++ b/lib/compiler_rt/negv.zig @@ -1,6 +1,17 @@ // negv - negate oVerflow // * @panic, if result can not be represented // - negvXi4_generic for unoptimized version +const std = @import("std"); +const builtin = @import("builtin"); +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__negvsi2, .{ .name = "__negvsi2", .linkage = linkage }); + @export(__negvdi2, .{ .name = "__negvdi2", .linkage = linkage }); + @export(__negvti2, .{ .name = "__negvti2", .linkage = linkage }); +} // assume -0 == 0 is gracefully handled by the hardware inline fn negvXi(comptime ST: type, a: ST) ST { diff --git a/lib/compiler_rt/os_version_check.zig b/lib/compiler_rt/os_version_check.zig index 55617dec75..53f332b13e 100644 --- a/lib/compiler_rt/os_version_check.zig +++ b/lib/compiler_rt/os_version_check.zig @@ -1,5 +1,17 @@ -const testing = @import("std").testing; +const std = @import("std"); +const testing = std.testing; const builtin = @import("builtin"); +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + if (builtin.os.tag.isDarwin()) { + @export(IsPlatformVersionAtLeast.__isPlatformVersionAtLeast, .{ + .name = "__isPlatformVersionAtLeast", + .linkage = linkage, + }); + } +} // Ported from llvm-project 13.0.0 d7b669b3a30345cfcdb2fde2af6f48aa4b94845d // @@ -16,34 +28,36 @@ const builtin = @import("builtin"); // the newer codepath, which merely calls out to the Darwin _availability_version_check API which is // available on macOS 10.15+, iOS 13+, tvOS 13+ and watchOS 6+. -inline fn constructVersion(major: u32, minor: u32, subminor: u32) u32 { - return ((major & 0xffff) << 16) | ((minor & 0xff) << 8) | (subminor & 0xff); -} +const IsPlatformVersionAtLeast = struct { + inline fn constructVersion(major: u32, minor: u32, subminor: u32) u32 { + return ((major & 0xffff) << 16) | ((minor & 0xff) << 8) | (subminor & 0xff); + } -// Darwin-only -pub fn __isPlatformVersionAtLeast(platform: u32, major: u32, minor: u32, subminor: u32) callconv(.C) i32 { - const build_version = dyld_build_version_t{ - .platform = platform, - .version = constructVersion(major, minor, subminor), + // Darwin-only + fn __isPlatformVersionAtLeast(platform: u32, major: u32, minor: u32, subminor: u32) callconv(.C) i32 { + const build_version = dyld_build_version_t{ + .platform = platform, + .version = constructVersion(major, minor, subminor), + }; + return @boolToInt(_availability_version_check(1, &[_]dyld_build_version_t{build_version})); + } + + // _availability_version_check darwin API support. + const dyld_platform_t = u32; + const dyld_build_version_t = extern struct { + platform: dyld_platform_t, + version: u32, }; - return @boolToInt(_availability_version_check(1, &[_]dyld_build_version_t{build_version})); -} - -// _availability_version_check darwin API support. -const dyld_platform_t = u32; -const dyld_build_version_t = extern struct { - platform: dyld_platform_t, - version: u32, + // Darwin-only + extern "c" fn _availability_version_check(count: u32, versions: [*c]const dyld_build_version_t) bool; }; -// Darwin-only -extern "c" fn _availability_version_check(count: u32, versions: [*c]const dyld_build_version_t) bool; test "isPlatformVersionAtLeast" { - if (!builtin.os.tag.isDarwin()) return error.SkipZigTest; + if (!comptime builtin.os.tag.isDarwin()) return error.SkipZigTest; // Note: this test depends on the actual host OS version since it is merely calling into the // native Darwin API. const macos_platform_constant = 1; - try testing.expect(__isPlatformVersionAtLeast(macos_platform_constant, 10, 0, 15) == 1); - try testing.expect(__isPlatformVersionAtLeast(macos_platform_constant, 99, 0, 0) == 0); + try testing.expect(IsPlatformVersionAtLeast.__isPlatformVersionAtLeast(macos_platform_constant, 10, 0, 15) == 1); + try testing.expect(IsPlatformVersionAtLeast.__isPlatformVersionAtLeast(macos_platform_constant, 99, 0, 0) == 0); } diff --git a/lib/compiler_rt/parity.zig b/lib/compiler_rt/parity.zig index ae634b0790..4f03e32244 100644 --- a/lib/compiler_rt/parity.zig +++ b/lib/compiler_rt/parity.zig @@ -1,5 +1,14 @@ const std = @import("std"); const builtin = @import("builtin"); +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__paritysi2, .{ .name = "__paritysi2", .linkage = linkage }); + @export(__paritydi2, .{ .name = "__paritydi2", .linkage = linkage }); + @export(__parityti2, .{ .name = "__parityti2", .linkage = linkage }); +} // parity - if number of bits set is even => 0, else => 1 // - pariytXi2_generic for big and little endian diff --git a/lib/compiler_rt/popcount.zig b/lib/compiler_rt/popcount.zig index 362b232fb8..6fa88cbfc6 100644 --- a/lib/compiler_rt/popcount.zig +++ b/lib/compiler_rt/popcount.zig @@ -1,5 +1,14 @@ const builtin = @import("builtin"); const std = @import("std"); +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__popcountsi2, .{ .name = "__popcountsi2", .linkage = linkage }); + @export(__popcountdi2, .{ .name = "__popcountdi2", .linkage = linkage }); + @export(__popcountti2, .{ .name = "__popcountti2", .linkage = linkage }); +} // popcount - population count // counts the number of 1 bits diff --git a/lib/compiler_rt/round.zig b/lib/compiler_rt/round.zig index 4f3266e00c..ddd651885a 100644 --- a/lib/compiler_rt/round.zig +++ b/lib/compiler_rt/round.zig @@ -5,8 +5,27 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/round.c const std = @import("std"); +const builtin = @import("builtin"); const math = std.math; const expect = std.testing.expect; +const arch = builtin.cpu.arch; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__roundh, .{ .name = "__roundh", .linkage = linkage }); + @export(roundf, .{ .name = "roundf", .linkage = linkage }); + @export(round, .{ .name = "round", .linkage = linkage }); + @export(__roundx, .{ .name = "__roundx", .linkage = linkage }); + @export(roundq, .{ .name = "roundq", .linkage = linkage }); + @export(roundl, .{ .name = "roundl", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(roundf128, .{ .name = "roundf128", .linkage = linkage }); + } + } +} pub fn __roundh(x: f16) callconv(.C) f16 { // TODO: more efficient implementation @@ -123,6 +142,10 @@ pub fn roundq(x_: f128) callconv(.C) f128 { } } +pub fn roundf128(x_: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, roundq, .{x_}); +} + pub fn roundl(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __roundh(x), diff --git a/lib/compiler_rt/shift.zig b/lib/compiler_rt/shift.zig index edcf246daf..031d5368ad 100644 --- a/lib/compiler_rt/shift.zig +++ b/lib/compiler_rt/shift.zig @@ -1,13 +1,35 @@ const std = @import("std"); +const builtin = @import("builtin"); const Log2Int = std.math.Log2Int; -const native_endian = @import("builtin").cpu.arch.endian(); +const native_endian = builtin.cpu.arch.endian(); +const arch = builtin.cpu.arch; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__ashldi3, .{ .name = "__ashldi3", .linkage = linkage }); + @export(__ashlti3, .{ .name = "__ashlti3", .linkage = linkage }); + @export(__ashrdi3, .{ .name = "__ashrdi3", .linkage = linkage }); + @export(__ashrti3, .{ .name = "__ashrti3", .linkage = linkage }); + @export(__lshrdi3, .{ .name = "__lshrdi3", .linkage = linkage }); + @export(__lshrti3, .{ .name = "__lshrti3", .linkage = linkage }); + + if (!is_test) { + if (arch.isARM() or arch.isThumb()) { + @export(__aeabi_llsl, .{ .name = "__aeabi_llsl", .linkage = linkage }); + @export(__aeabi_lasr, .{ .name = "__aeabi_lasr", .linkage = linkage }); + @export(__aeabi_llsr, .{ .name = "__aeabi_llsr", .linkage = linkage }); + } + } +} fn Dwords(comptime T: type, comptime signed_half: bool) type { return extern union { - pub const bits = @divExact(@typeInfo(T).Int.bits, 2); - pub const HalfTU = std.meta.Int(.unsigned, bits); - pub const HalfTS = std.meta.Int(.signed, bits); - pub const HalfT = if (signed_half) HalfTS else HalfTU; + const bits = @divExact(@typeInfo(T).Int.bits, 2); + const HalfTU = std.meta.Int(.unsigned, bits); + const HalfTS = std.meta.Int(.signed, bits); + const HalfT = if (signed_half) HalfTS else HalfTU; all: T, s: if (native_endian == .Little) @@ -19,7 +41,7 @@ fn Dwords(comptime T: type, comptime signed_half: bool) type { // Arithmetic shift left // Precondition: 0 <= b < bits_in_dword -pub inline fn ashlXi3(comptime T: type, a: T, b: i32) T { +inline fn ashlXi3(comptime T: type, a: T, b: i32) T { const dwords = Dwords(T, false); const S = Log2Int(dwords.HalfT); @@ -42,7 +64,7 @@ pub inline fn ashlXi3(comptime T: type, a: T, b: i32) T { // Arithmetic shift right // Precondition: 0 <= b < T.bit_count -pub inline fn ashrXi3(comptime T: type, a: T, b: i32) T { +inline fn ashrXi3(comptime T: type, a: T, b: i32) T { const dwords = Dwords(T, true); const S = Log2Int(dwords.HalfT); @@ -69,7 +91,7 @@ pub inline fn ashrXi3(comptime T: type, a: T, b: i32) T { // Logical shift right // Precondition: 0 <= b < T.bit_count -pub inline fn lshrXi3(comptime T: type, a: T, b: i32) T { +inline fn lshrXi3(comptime T: type, a: T, b: i32) T { const dwords = Dwords(T, false); const S = Log2Int(dwords.HalfT); diff --git a/lib/compiler_rt/sin.zig b/lib/compiler_rt/sin.zig index 20259bc309..9766b2d541 100644 --- a/lib/compiler_rt/sin.zig +++ b/lib/compiler_rt/sin.zig @@ -5,13 +5,32 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/sin.c const std = @import("std"); +const builtin = @import("builtin"); +const arch = builtin.cpu.arch; const math = std.math; const expect = std.testing.expect; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; const trig = @import("trig.zig"); const rem_pio2 = @import("rem_pio2.zig").rem_pio2; const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; +comptime { + @export(__sinh, .{ .name = "__sinh", .linkage = linkage }); + @export(sinf, .{ .name = "sinf", .linkage = linkage }); + @export(sin, .{ .name = "sin", .linkage = linkage }); + @export(__sinx, .{ .name = "__sinx", .linkage = linkage }); + @export(sinq, .{ .name = "sinq", .linkage = linkage }); + @export(sinl, .{ .name = "sinl", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(sinf128, .{ .name = "sinf128", .linkage = linkage }); + } + } +} + pub fn __sinh(x: f16) callconv(.C) f16 { // TODO: more efficient implementation return @floatCast(f16, sinf(x)); @@ -111,6 +130,10 @@ pub fn sinq(x: f128) callconv(.C) f128 { return sin(@floatCast(f64, x)); } +pub fn sinf128(x: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, sinq, .{x}); +} + pub fn sinl(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __sinh(x), diff --git a/lib/compiler_rt/sincos.zig b/lib/compiler_rt/sincos.zig index 8bc5b83ee5..da68022f89 100644 --- a/lib/compiler_rt/sincos.zig +++ b/lib/compiler_rt/sincos.zig @@ -1,10 +1,27 @@ const std = @import("std"); +const builtin = @import("builtin"); +const arch = builtin.cpu.arch; const math = std.math; -const sin = @import("sin.zig"); -const cos = @import("cos.zig"); const trig = @import("trig.zig"); const rem_pio2 = @import("rem_pio2.zig").rem_pio2; const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__sincosh, .{ .name = "__sincosh", .linkage = linkage }); + @export(sincosf, .{ .name = "sincosf", .linkage = linkage }); + @export(sincos, .{ .name = "sincos", .linkage = linkage }); + @export(__sincosx, .{ .name = "__sincosx", .linkage = linkage }); + @export(sincosq, .{ .name = "sincosq", .linkage = linkage }); + @export(sincosl, .{ .name = "sincosl", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(sincosf128, .{ .name = "sincosf128", .linkage = linkage }); + } + } +} pub fn __sincosh(x: f16, r_sin: *f16, r_cos: *f16) callconv(.C) void { // TODO: more efficient implementation @@ -181,6 +198,10 @@ pub fn sincosq(x: f128, r_sin: *f128, r_cos: *f128) callconv(.C) void { r_cos.* = small_cos; } +pub fn sincosf128(x: f128, r_sin: *f128, r_cos: *f128) callconv(.C) void { + return @call(.{ .modifier = .always_inline }, sincosq, .{ x, r_sin, r_cos }); +} + pub fn sincosl(x: c_longdouble, r_sin: *c_longdouble, r_cos: *c_longdouble) callconv(.C) void { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __sincosh(x, r_sin, r_cos), @@ -192,7 +213,7 @@ pub fn sincosl(x: c_longdouble, r_sin: *c_longdouble, r_cos: *c_longdouble) call } } -const rem_pio2_generic = @compileError("TODO"); +pub const rem_pio2_generic = @compileError("TODO"); /// Ported from musl sincosl.c. Needs the following dependencies to be complete: /// * rem_pio2_generic ported from __rem_pio2l.c diff --git a/lib/compiler_rt/sparc.zig b/lib/compiler_rt/sparc.zig index 3b33afd29a..f96ba4b41a 100644 --- a/lib/compiler_rt/sparc.zig +++ b/lib/compiler_rt/sparc.zig @@ -3,6 +3,40 @@ const std = @import("std"); const builtin = @import("builtin"); +const arch = builtin.cpu.arch; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + if (arch.isSPARC()) { + // SPARC systems use a different naming scheme + @export(_Qp_add, .{ .name = "_Qp_add", .linkage = linkage }); + @export(_Qp_div, .{ .name = "_Qp_div", .linkage = linkage }); + @export(_Qp_mul, .{ .name = "_Qp_mul", .linkage = linkage }); + @export(_Qp_sub, .{ .name = "_Qp_sub", .linkage = linkage }); + + @export(_Qp_cmp, .{ .name = "_Qp_cmp", .linkage = linkage }); + @export(_Qp_feq, .{ .name = "_Qp_feq", .linkage = linkage }); + @export(_Qp_fne, .{ .name = "_Qp_fne", .linkage = linkage }); + @export(_Qp_flt, .{ .name = "_Qp_flt", .linkage = linkage }); + @export(_Qp_fle, .{ .name = "_Qp_fle", .linkage = linkage }); + @export(_Qp_fgt, .{ .name = "_Qp_fgt", .linkage = linkage }); + @export(_Qp_fge, .{ .name = "_Qp_fge", .linkage = linkage }); + + @export(_Qp_itoq, .{ .name = "_Qp_itoq", .linkage = linkage }); + @export(_Qp_uitoq, .{ .name = "_Qp_uitoq", .linkage = linkage }); + @export(_Qp_xtoq, .{ .name = "_Qp_xtoq", .linkage = linkage }); + @export(_Qp_uxtoq, .{ .name = "_Qp_uxtoq", .linkage = linkage }); + @export(_Qp_stoq, .{ .name = "_Qp_stoq", .linkage = linkage }); + @export(_Qp_dtoq, .{ .name = "_Qp_dtoq", .linkage = linkage }); + @export(_Qp_qtoi, .{ .name = "_Qp_qtoi", .linkage = linkage }); + @export(_Qp_qtoui, .{ .name = "_Qp_qtoui", .linkage = linkage }); + @export(_Qp_qtox, .{ .name = "_Qp_qtox", .linkage = linkage }); + @export(_Qp_qtoux, .{ .name = "_Qp_qtoux", .linkage = linkage }); + @export(_Qp_qtos, .{ .name = "_Qp_qtos", .linkage = linkage }); + @export(_Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage }); + } +} // The SPARC Architecture Manual, Version 9: // A.13 Floating-Point Compare diff --git a/lib/compiler_rt/sqrt.zig b/lib/compiler_rt/sqrt.zig index 8d43949f99..ec62ff3d39 100644 --- a/lib/compiler_rt/sqrt.zig +++ b/lib/compiler_rt/sqrt.zig @@ -1,5 +1,24 @@ const std = @import("std"); +const builtin = @import("builtin"); +const arch = builtin.cpu.arch; const math = std.math; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__sqrth, .{ .name = "__sqrth", .linkage = linkage }); + @export(sqrtf, .{ .name = "sqrtf", .linkage = linkage }); + @export(sqrt, .{ .name = "sqrt", .linkage = linkage }); + @export(__sqrtx, .{ .name = "__sqrtx", .linkage = linkage }); + @export(sqrtq, .{ .name = "sqrtq", .linkage = linkage }); + @export(sqrtl, .{ .name = "sqrtl", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(sqrtf128, .{ .name = "sqrtf128", .linkage = linkage }); + } + } +} pub fn __sqrth(x: f16) callconv(.C) f16 { // TODO: more efficient implementation @@ -236,6 +255,10 @@ pub fn sqrtl(x: c_longdouble) callconv(.C) c_longdouble { } } +pub fn sqrtf128(x: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, sqrtq, .{x}); +} + test "sqrtf" { const V = [_]f32{ 0.0, diff --git a/lib/compiler_rt/stack_probe.zig b/lib/compiler_rt/stack_probe.zig index 90919dcbb8..5ebb851825 100644 --- a/lib/compiler_rt/stack_probe.zig +++ b/lib/compiler_rt/stack_probe.zig @@ -1,4 +1,43 @@ -const native_arch = @import("builtin").cpu.arch; +const std = @import("std"); +const builtin = @import("builtin"); +const os_tag = builtin.os.tag; +const arch = builtin.cpu.arch; +const abi = builtin.abi; +const is_test = builtin.is_test; + +const is_gnu = abi.isGnu(); +const is_mingw = os_tag == .windows and is_gnu; + +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +const strong_linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Strong; +pub const panic = @import("common.zig").panic; + +comptime { + if (builtin.os.tag == .windows) { + // Default stack-probe functions emitted by LLVM + if (is_mingw) { + @export(_chkstk, .{ .name = "_alloca", .linkage = strong_linkage }); + @export(___chkstk_ms, .{ .name = "___chkstk_ms", .linkage = strong_linkage }); + } else if (!builtin.link_libc) { + // This symbols are otherwise exported by MSVCRT.lib + @export(_chkstk, .{ .name = "_chkstk", .linkage = strong_linkage }); + @export(__chkstk, .{ .name = "__chkstk", .linkage = strong_linkage }); + } + + if (arch.isAARCH64()) { + @export(__chkstk, .{ .name = "__chkstk", .linkage = strong_linkage }); + } + } + + switch (arch) { + .i386, + .x86_64, + => { + @export(zig_probe_stack, .{ .name = "__zig_probe_stack", .linkage = linkage }); + }, + else => {}, + } +} // Zig's own stack-probe routine (available only on x86 and x86_64) pub fn zig_probe_stack() callconv(.Naked) void { @@ -8,7 +47,7 @@ pub fn zig_probe_stack() callconv(.Naked) void { // invalid so let's update it on the go, otherwise we'll get a segfault // instead of triggering the stack growth. - switch (native_arch) { + switch (arch) { .x86_64 => { // %rax = probe length, %rsp = stack pointer asm volatile ( @@ -60,7 +99,7 @@ pub fn zig_probe_stack() callconv(.Naked) void { fn win_probe_stack_only() void { @setRuntimeSafety(false); - switch (native_arch) { + switch (arch) { .x86_64 => { asm volatile ( \\ push %%rcx @@ -105,7 +144,7 @@ fn win_probe_stack_only() void { }, else => {}, } - if (comptime native_arch.isAARCH64()) { + if (comptime arch.isAARCH64()) { // NOTE: page size hardcoded to 4096 for now asm volatile ( \\ lsl x16, x15, #4 @@ -127,7 +166,7 @@ fn win_probe_stack_only() void { fn win_probe_stack_adjust_sp() void { @setRuntimeSafety(false); - switch (native_arch) { + switch (arch) { .x86_64 => { asm volatile ( \\ push %%rcx @@ -201,9 +240,9 @@ pub fn _chkstk() callconv(.Naked) void { } pub fn __chkstk() callconv(.Naked) void { @setRuntimeSafety(false); - if (comptime native_arch.isAARCH64()) { + if (comptime arch.isAARCH64()) { @call(.{ .modifier = .always_inline }, win_probe_stack_only, .{}); - } else switch (native_arch) { + } else switch (arch) { .i386 => @call(.{ .modifier = .always_inline }, win_probe_stack_adjust_sp, .{}), .x86_64 => @call(.{ .modifier = .always_inline }, win_probe_stack_only, .{}), else => unreachable, diff --git a/lib/compiler_rt/subo.zig b/lib/compiler_rt/subo.zig index af28c6eead..c2b73a599d 100644 --- a/lib/compiler_rt/subo.zig +++ b/lib/compiler_rt/subo.zig @@ -1,4 +1,14 @@ +const std = @import("std"); const builtin = @import("builtin"); +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__subosi4, .{ .name = "__subosi4", .linkage = linkage }); + @export(__subodi4, .{ .name = "__subodi4", .linkage = linkage }); + @export(__suboti4, .{ .name = "__suboti4", .linkage = linkage }); +} // subo - subtract overflow // * return a-%b. diff --git a/lib/compiler_rt/tan.zig b/lib/compiler_rt/tan.zig index d37022d918..c03c3e8649 100644 --- a/lib/compiler_rt/tan.zig +++ b/lib/compiler_rt/tan.zig @@ -6,6 +6,7 @@ // https://golang.org/src/math/tan.go const std = @import("std"); +const builtin = @import("builtin"); const math = std.math; const expect = std.testing.expect; @@ -13,6 +14,25 @@ const kernel = @import("trig.zig"); const rem_pio2 = @import("rem_pio2.zig").rem_pio2; const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; +const arch = builtin.cpu.arch; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__tanh, .{ .name = "__tanh", .linkage = linkage }); + @export(tanf, .{ .name = "tanf", .linkage = linkage }); + @export(tan, .{ .name = "tan", .linkage = linkage }); + @export(__tanx, .{ .name = "__tanx", .linkage = linkage }); + @export(tanq, .{ .name = "tanq", .linkage = linkage }); + @export(tanl, .{ .name = "tanl", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(tanf128, .{ .name = "tanf128", .linkage = linkage }); + } + } +} + pub fn __tanh(x: f16) callconv(.C) f16 { // TODO: more efficient implementation return @floatCast(f16, tanf(x)); @@ -96,6 +116,10 @@ pub fn tanq(x: f128) callconv(.C) f128 { return tan(@floatCast(f64, x)); } +pub fn tanf128(x: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, tanq, .{x}); +} + pub fn tanl(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __tanh(x), diff --git a/lib/compiler_rt/trunc.zig b/lib/compiler_rt/trunc.zig index d00df60d99..c377a86666 100644 --- a/lib/compiler_rt/trunc.zig +++ b/lib/compiler_rt/trunc.zig @@ -5,8 +5,27 @@ // https://git.musl-libc.org/cgit/musl/tree/src/math/trunc.c const std = @import("std"); +const builtin = @import("builtin"); const math = std.math; const expect = std.testing.expect; +const arch = builtin.cpu.arch; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__trunch, .{ .name = "__trunch", .linkage = linkage }); + @export(truncf, .{ .name = "truncf", .linkage = linkage }); + @export(trunc, .{ .name = "trunc", .linkage = linkage }); + @export(__truncx, .{ .name = "__truncx", .linkage = linkage }); + @export(truncq, .{ .name = "truncq", .linkage = linkage }); + @export(truncl, .{ .name = "truncl", .linkage = linkage }); + + if (!builtin.is_test) { + if (arch.isPPC() or arch.isPPC64()) { + @export(truncf128, .{ .name = "truncf128", .linkage = linkage }); + } + } +} pub fn __trunch(x: f16) callconv(.C) f16 { // TODO: more efficient implementation @@ -81,6 +100,10 @@ pub fn truncq(x: f128) callconv(.C) f128 { } } +pub fn truncf128(x: f128) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, truncq, .{x}); +} + pub fn truncl(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __trunch(x), diff --git a/lib/compiler_rt/truncXfYf2.zig b/lib/compiler_rt/truncXfYf2.zig index bf324269a6..77fabcbd75 100644 --- a/lib/compiler_rt/truncXfYf2.zig +++ b/lib/compiler_rt/truncXfYf2.zig @@ -1,17 +1,49 @@ const std = @import("std"); const builtin = @import("builtin"); -const native_arch = builtin.cpu.arch; +const arch = builtin.cpu.arch; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = linkage }); + @export(__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = linkage }); + @export(__trunctfdf2, .{ .name = "__trunctfdf2", .linkage = linkage }); + @export(__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = linkage }); + + @export(__truncdfsf2, .{ .name = "__truncdfsf2", .linkage = linkage }); + @export(__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage }); + + if (!is_test) { + @export(__gnu_f2h_ieee, .{ .name = "__gnu_f2h_ieee", .linkage = linkage }); + + if (arch.isARM() or arch.isThumb()) { + @export(__aeabi_d2h, .{ .name = "__aeabi_d2h", .linkage = linkage }); + @export(__aeabi_f2h, .{ .name = "__aeabi_f2h", .linkage = linkage }); + @export(__aeabi_d2f, .{ .name = "__aeabi_d2f", .linkage = linkage }); + } + + if (arch.isPPC() or arch.isPPC64()) { + @export(__trunckfsf2, .{ .name = "__trunckfsf2", .linkage = linkage }); + @export(__trunckfdf2, .{ .name = "__trunckfdf2", .linkage = linkage }); + } + } +} // AArch64 is the only ABI (at the moment) to support f16 arguments without the // need for extending them to wider fp types. // TODO remove this; do this type selection in the language rather than // here in compiler-rt. -pub const F16T = if (native_arch.isAARCH64()) f16 else u16; +const F16T = if (arch.isAARCH64()) f16 else u16; pub fn __truncsfhf2(a: f32) callconv(.C) F16T { return @bitCast(F16T, truncXfYf2(f16, f32, a)); } +pub fn __gnu_f2h_ieee(a: f32) callconv(.C) F16T { + return @call(.{ .modifier = .always_inline }, __truncsfhf2, .{a}); +} + pub fn __truncdfhf2(a: f64) callconv(.C) F16T { return @bitCast(F16T, truncXfYf2(f16, f64, a)); } @@ -24,10 +56,18 @@ pub fn __trunctfsf2(a: f128) callconv(.C) f32 { return truncXfYf2(f32, f128, a); } +pub fn __trunckfsf2(a: f128) callconv(.C) f32 { + return truncXfYf2(f32, f128, a); +} + pub fn __trunctfdf2(a: f128) callconv(.C) f64 { return truncXfYf2(f64, f128, a); } +pub fn __trunckfdf2(a: f128) callconv(.C) f64 { + return truncXfYf2(f64, f128, a); +} + pub fn __truncdfsf2(a: f64) callconv(.C) f32 { return truncXfYf2(f32, f64, a); } diff --git a/lib/compiler_rt/trunc_f80.zig b/lib/compiler_rt/trunc_f80.zig index 107874aeeb..43f113ea42 100644 --- a/lib/compiler_rt/trunc_f80.zig +++ b/lib/compiler_rt/trunc_f80.zig @@ -1,11 +1,21 @@ const std = @import("std"); const builtin = @import("builtin"); -const native_arch = builtin.cpu.arch; +const arch = builtin.cpu.arch; const testing = std.testing; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + @export(__truncxfhf2, .{ .name = "__truncxfhf2", .linkage = linkage }); + @export(__truncxfsf2, .{ .name = "__truncxfsf2", .linkage = linkage }); + @export(__truncxfdf2, .{ .name = "__truncxfdf2", .linkage = linkage }); + @export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage }); +} // AArch64 is the only ABI (at the moment) to support f16 arguments without the // need for extending them to wider fp types. -pub const F16T = if (native_arch.isAARCH64()) f16 else u16; +const F16T = if (arch.isAARCH64()) f16 else u16; pub fn __truncxfhf2(a: f80) callconv(.C) F16T { return @bitCast(F16T, trunc(f16, a)); diff --git a/lib/compiler_rt/udivmodti4.zig b/lib/compiler_rt/udivmodti4.zig index be9ed8237b..3bad0c822e 100644 --- a/lib/compiler_rt/udivmodti4.zig +++ b/lib/compiler_rt/udivmodti4.zig @@ -1,13 +1,35 @@ -const udivmod = @import("udivmod.zig").udivmod; +const std = @import("std"); const builtin = @import("builtin"); -const compiler_rt = @import("../compiler_rt.zig"); +const udivmod = @import("udivmod.zig").udivmod; +const arch = builtin.cpu.arch; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + if (builtin.os.tag == .windows) { + switch (arch) { + .i386 => { + @export(__udivmodti4, .{ .name = "__udivmodti4", .linkage = linkage }); + }, + .x86_64 => { + // The "ti" functions must use Vector(2, u64) parameter types to adhere to the ABI + // that LLVM expects compiler-rt to have. + @export(__udivmodti4_windows_x86_64, .{ .name = "__udivmodti4", .linkage = linkage }); + }, + else => {}, + } + } else { + @export(__udivmodti4, .{ .name = "__udivmodti4", .linkage = linkage }); + } +} pub fn __udivmodti4(a: u128, b: u128, maybe_rem: ?*u128) callconv(.C) u128 { @setRuntimeSafety(builtin.is_test); return udivmod(u128, a, b, maybe_rem); } -const v128 = @import("std").meta.Vector(2, u64); +const v128 = std.meta.Vector(2, u64); pub fn __udivmodti4_windows_x86_64(a: v128, b: v128, maybe_rem: ?*u128) callconv(.C) v128 { @setRuntimeSafety(builtin.is_test); return @bitCast(v128, udivmod(u128, @bitCast(u128, a), @bitCast(u128, b), maybe_rem)); diff --git a/lib/compiler_rt/udivti3.zig b/lib/compiler_rt/udivti3.zig index 52afa0420f..f12b215c59 100644 --- a/lib/compiler_rt/udivti3.zig +++ b/lib/compiler_rt/udivti3.zig @@ -1,13 +1,39 @@ -const udivmodti4 = @import("udivmodti4.zig"); +const std = @import("std"); const builtin = @import("builtin"); +const udivmod = @import("udivmod.zig").udivmod; +const arch = builtin.cpu.arch; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + if (builtin.os.tag == .windows) { + switch (arch) { + .i386 => { + @export(__udivti3, .{ .name = "__udivti3", .linkage = linkage }); + }, + .x86_64 => { + // The "ti" functions must use Vector(2, u64) parameter types to adhere to the ABI + // that LLVM expects compiler-rt to have. + @export(__udivti3_windows_x86_64, .{ .name = "__udivti3", .linkage = linkage }); + }, + else => {}, + } + if (arch.isAARCH64()) { + @export(__udivti3, .{ .name = "__udivti3", .linkage = linkage }); + } + } else { + @export(__udivti3, .{ .name = "__udivti3", .linkage = linkage }); + } +} pub fn __udivti3(a: u128, b: u128) callconv(.C) u128 { @setRuntimeSafety(builtin.is_test); - return udivmodti4.__udivmodti4(a, b, null); + return udivmod(u128, a, b, null); } -const v128 = @import("std").meta.Vector(2, u64); +const v128 = std.meta.Vector(2, u64); pub fn __udivti3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { @setRuntimeSafety(builtin.is_test); - return udivmodti4.__udivmodti4_windows_x86_64(a, b, null); + return @bitCast(v128, udivmod(u128, @bitCast(u128, a), @bitCast(u128, b), null)); } diff --git a/lib/compiler_rt/umodti3.zig b/lib/compiler_rt/umodti3.zig index 29eb572892..aef2ba434d 100644 --- a/lib/compiler_rt/umodti3.zig +++ b/lib/compiler_rt/umodti3.zig @@ -1,15 +1,40 @@ -const udivmodti4 = @import("udivmodti4.zig"); +const std = @import("std"); const builtin = @import("builtin"); -const compiler_rt = @import("../compiler_rt.zig"); +const udivmod = @import("udivmod.zig").udivmod; +const arch = builtin.cpu.arch; +const is_test = builtin.is_test; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const panic = @import("common.zig").panic; + +comptime { + if (builtin.os.tag == .windows) { + switch (arch) { + .i386 => { + @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage }); + }, + .x86_64 => { + // The "ti" functions must use Vector(2, u64) parameter types to adhere to the ABI + // that LLVM expects compiler-rt to have. + @export(__umodti3_windows_x86_64, .{ .name = "__umodti3", .linkage = linkage }); + }, + else => {}, + } + if (arch.isAARCH64()) { + @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage }); + } + } else { + @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage }); + } +} pub fn __umodti3(a: u128, b: u128) callconv(.C) u128 { @setRuntimeSafety(builtin.is_test); var r: u128 = undefined; - _ = udivmodti4.__udivmodti4(a, b, &r); + _ = udivmod(u128, a, b, &r); return r; } -const v128 = @import("std").meta.Vector(2, u64); +const v128 = std.meta.Vector(2, u64); pub fn __umodti3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { return @bitCast(v128, @call(.{ .modifier = .always_inline }, __umodti3, .{ @bitCast(u128, a), diff --git a/src/Compilation.zig b/src/Compilation.zig index 54d87faa7b..0cd40a7001 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -23,6 +23,7 @@ const mingw = @import("mingw.zig"); const libunwind = @import("libunwind.zig"); const libcxx = @import("libcxx.zig"); const wasi_libc = @import("wasi_libc.zig"); +const compiler_rt = @import("compiler_rt.zig"); const fatal = @import("main.zig").fatal; const clangMain = @import("main.zig").clangMain; const Module = @import("Module.zig"); @@ -131,7 +132,7 @@ libssp_static_lib: ?CRTFile = null, libc_static_lib: ?CRTFile = null, /// Populated when we build the libcompiler_rt static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). -compiler_rt_static_lib: ?CRTFile = null, +compiler_rt_static_lib: compiler_rt.CompilerRtLib = .{}, /// Populated when we build the compiler_rt_obj object. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). compiler_rt_obj: ?CRTFile = null, @@ -175,7 +176,7 @@ pub const CRTFile = struct { lock: Cache.Lock, full_object_path: []const u8, - fn deinit(self: *CRTFile, gpa: Allocator) void { + pub fn deinit(self: *CRTFile, gpa: Allocator) void { self.lock.release(); gpa.free(self.full_object_path); self.* = undefined; @@ -1978,9 +1979,7 @@ pub fn destroy(self: *Compilation) void { if (self.libcxxabi_static_lib) |*crt_file| { crt_file.deinit(gpa); } - if (self.compiler_rt_static_lib) |*crt_file| { - crt_file.deinit(gpa); - } + self.compiler_rt_static_lib.deinit(gpa); if (self.compiler_rt_obj) |*crt_file| { crt_file.deinit(gpa); } @@ -3138,11 +3137,9 @@ fn processOneJob(comp: *Compilation, job: Job) !void { const named_frame = tracy.namedFrame("compiler_rt_lib"); defer named_frame.end(); - comp.buildOutputFromZig( - "compiler_rt.zig", - .Lib, + compiler_rt.buildCompilerRtLib( + comp, &comp.compiler_rt_static_lib, - .compiler_rt, ) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.SubCompilationFailed => return, // error reported already @@ -4897,7 +4894,7 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void { } } -fn buildOutputFromZig( +pub fn buildOutputFromZig( comp: *Compilation, src_basename: []const u8, output_mode: std.builtin.OutputMode, @@ -4914,7 +4911,15 @@ fn buildOutputFromZig( .root_src_path = src_basename, }; defer main_pkg.deinitTable(comp.gpa); - const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len]; + + const root_name = root_name: { + const basename = if (std.fs.path.dirname(src_basename)) |dirname| + src_basename[dirname.len + 1 ..] + else + src_basename; + const root_name = basename[0 .. basename.len - std.fs.path.extension(basename).len]; + break :root_name root_name; + }; const target = comp.getTarget(); const bin_basename = try std.zig.binNameAlloc(comp.gpa, .{ .root_name = root_name, diff --git a/src/compiler_rt.zig b/src/compiler_rt.zig new file mode 100644 index 0000000000..82b482daa8 --- /dev/null +++ b/src/compiler_rt.zig @@ -0,0 +1,186 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const Allocator = std.mem.Allocator; +const mem = std.mem; +const tracy = @import("tracy.zig"); +const trace = tracy.trace; + +const Compilation = @import("Compilation.zig"); +const CRTFile = Compilation.CRTFile; +const LinkObject = Compilation.LinkObject; +const Package = @import("Package.zig"); + +pub const CompilerRtLib = struct { + crt_object_files: [sources.len]?CRTFile = undefined, + crt_lib_file: ?CRTFile = null, + + pub fn deinit(crt_lib: *CompilerRtLib, gpa: Allocator) void { + for (crt_lib.crt_object_files) |*crt_file| { + if (crt_file.*) |*cf| { + cf.deinit(gpa); + } + } + if (crt_lib.crt_lib_file) |*crt_file| { + crt_file.deinit(gpa); + } + } +}; + +pub fn buildCompilerRtLib(comp: *Compilation, compiler_rt_lib: *CompilerRtLib) !void { + const tracy_trace = trace(@src()); + defer tracy_trace.end(); + + var progress: std.Progress = .{ .dont_print_on_dumb = true }; + var progress_node = progress.start("Compile Compiler-RT", sources.len + 1); + defer progress_node.end(); + if (comp.color == .off) progress.terminal = null; + + progress_node.activate(); + + var link_objects: [sources.len]LinkObject = undefined; + for (sources) |source, i| { + var obj_progress_node = progress_node.start(source, 0); + obj_progress_node.activate(); + defer obj_progress_node.end(); + + try comp.buildOutputFromZig(source, .Obj, &compiler_rt_lib.crt_object_files[i], .compiler_rt); + link_objects[i] = .{ + .path = compiler_rt_lib.crt_object_files[i].?.full_object_path, + .must_link = true, + }; + } + + const root_name = "compiler_rt"; + + var lib_progress_node = progress_node.start(root_name, 0); + lib_progress_node.activate(); + defer lib_progress_node.end(); + + const target = comp.getTarget(); + const basename = try std.zig.binNameAlloc(comp.gpa, .{ + .root_name = root_name, + .target = target, + .output_mode = .Lib, + }); + errdefer comp.gpa.free(basename); + + // TODO: This is extracted into a local variable to work around a stage1 miscompilation. + const emit_bin = Compilation.EmitLoc{ + .directory = null, // Put it in the cache directory. + .basename = basename, + }; + const sub_compilation = try Compilation.create(comp.gpa, .{ + .local_cache_directory = comp.global_cache_directory, + .global_cache_directory = comp.global_cache_directory, + .zig_lib_directory = comp.zig_lib_directory, + .cache_mode = .whole, + .target = target, + .root_name = root_name, + .main_pkg = null, + .output_mode = .Lib, + .link_mode = .Static, + .function_sections = true, + .thread_pool = comp.thread_pool, + .libc_installation = comp.bin_file.options.libc_installation, + .emit_bin = emit_bin, + .optimize_mode = comp.compilerRtOptMode(), + .want_sanitize_c = false, + .want_stack_check = false, + .want_red_zone = comp.bin_file.options.red_zone, + .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer, + .want_valgrind = false, + .want_tsan = false, + .want_pic = comp.bin_file.options.pic, + .want_pie = comp.bin_file.options.pie, + .want_lto = comp.bin_file.options.lto, + .emit_h = null, + .strip = comp.compilerRtStrip(), + .is_native_os = comp.bin_file.options.is_native_os, + .is_native_abi = comp.bin_file.options.is_native_abi, + .self_exe_path = comp.self_exe_path, + .link_objects = &link_objects, + .verbose_cc = comp.verbose_cc, + .verbose_link = comp.bin_file.options.verbose_link, + .verbose_air = comp.verbose_air, + .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_cimport = comp.verbose_cimport, + .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, + .clang_passthrough_mode = comp.clang_passthrough_mode, + .skip_linker_dependencies = true, + .parent_compilation_link_libc = comp.bin_file.options.link_libc, + }); + defer sub_compilation.destroy(); + + try sub_compilation.updateSubCompilation(); + + compiler_rt_lib.crt_lib_file = .{ + .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ + sub_compilation.bin_file.options.emit.?.sub_path, + }), + .lock = sub_compilation.bin_file.toOwnedLock(), + }; +} + +const sources = &[_][]const u8{ + "compiler_rt/atomics.zig", + "compiler_rt/sin.zig", + "compiler_rt/cos.zig", + "compiler_rt/sincos.zig", + "compiler_rt/ceil.zig", + "compiler_rt/exp.zig", + "compiler_rt/exp2.zig", + "compiler_rt/fabs.zig", + "compiler_rt/floor.zig", + "compiler_rt/fma.zig", + "compiler_rt/fmax.zig", + "compiler_rt/fmin.zig", + "compiler_rt/fmod.zig", + "compiler_rt/log.zig", + "compiler_rt/log10.zig", + "compiler_rt/log2.zig", + "compiler_rt/round.zig", + "compiler_rt/sqrt.zig", + "compiler_rt/tan.zig", + "compiler_rt/trunc.zig", + "compiler_rt/extendXfYf2.zig", + "compiler_rt/extend_f80.zig", + "compiler_rt/compareXf2.zig", + "compiler_rt/stack_probe.zig", + "compiler_rt/divti3.zig", + "compiler_rt/modti3.zig", + "compiler_rt/multi3.zig", + "compiler_rt/udivti3.zig", + "compiler_rt/udivmodti4.zig", + "compiler_rt/umodti3.zig", + "compiler_rt/truncXfYf2.zig", + "compiler_rt/trunc_f80.zig", + "compiler_rt/addXf3.zig", + "compiler_rt/mulXf3.zig", + "compiler_rt/divsf3.zig", + "compiler_rt/divdf3.zig", + "compiler_rt/divxf3.zig", + "compiler_rt/divtf3.zig", + "compiler_rt/floatXiYf.zig", + "compiler_rt/fixXfYi.zig", + "compiler_rt/count0bits.zig", + "compiler_rt/parity.zig", + "compiler_rt/popcount.zig", + "compiler_rt/bswap.zig", + "compiler_rt/int.zig", + "compiler_rt/shift.zig", + "compiler_rt/negXi2.zig", + "compiler_rt/muldi3.zig", + "compiler_rt/absv.zig", + "compiler_rt/negv.zig", + "compiler_rt/addo.zig", + "compiler_rt/subo.zig", + "compiler_rt/mulo.zig", + "compiler_rt/cmp.zig", + "compiler_rt/negXf2.zig", + "compiler_rt/os_version_check.zig", + "compiler_rt/emutls.zig", + "compiler_rt/arm.zig", + "compiler_rt/aulldiv.zig", + "compiler_rt/sparc.zig", + "compiler_rt/clear_cache.zig", +}; diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 37ec8d0758..4ab7cee6e1 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -1354,7 +1354,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) ! } // MSVC compiler_rt is missing some stuff, so we build it unconditionally but // and rely on weak linkage to allow MSVC compiler_rt functions to override ours. - if (comp.compiler_rt_static_lib) |lib| { + if (comp.compiler_rt_static_lib.crt_lib_file) |lib| { try argv.append(lib.full_object_path); } } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index e0f114acd4..046aa2ec0a 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1272,7 +1272,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v const stack_size = self.base.options.stack_size_override orelse 16777216; const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os; const compiler_rt_path: ?[]const u8 = blk: { - if (comp.compiler_rt_static_lib) |x| break :blk x.full_object_path; + if (comp.compiler_rt_static_lib.crt_lib_file) |x| break :blk x.full_object_path; if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; break :blk null; }; diff --git a/src/link/MachO.zig b/src/link/MachO.zig index a4d14b985f..780b4b0483 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -738,7 +738,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try positionals.append(p); } - if (comp.compiler_rt_static_lib) |lib| { + if (comp.compiler_rt_static_lib.crt_lib_file) |lib| { try positionals.append(lib.full_object_path); } diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index ee2ed19ed5..30a1c49794 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2255,7 +2255,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! const is_obj = self.base.options.output_mode == .Obj; const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt and !is_obj) - comp.compiler_rt_static_lib.?.full_object_path + comp.compiler_rt_static_lib.crt_lib_file.?.full_object_path else null; diff --git a/src/musl.zig b/src/musl.zig index d061addc9a..68b524b415 100644 --- a/src/musl.zig +++ b/src/musl.zig @@ -4,7 +4,6 @@ const mem = std.mem; const path = std.fs.path; const assert = std.debug.assert; -const target_util = @import("target.zig"); const Compilation = @import("Compilation.zig"); const build_options = @import("build_options"); From 2259d629d3d4e361f78e4d1b79425dfae912f05c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 13 Jun 2022 01:27:09 +0200 Subject: [PATCH 1849/2031] compiler_rt: use single cache for libcompiler_rt.a static lib --- CMakeLists.txt | 1 + lib/compiler_rt/os_version_check.zig | 13 +- src/Compilation.zig | 8 +- src/compiler_rt.zig | 236 ++++++++++++++++----------- src/link/Coff.zig | 2 +- src/link/Elf.zig | 2 +- src/link/MachO.zig | 2 +- src/link/Wasm.zig | 2 +- 8 files changed, 156 insertions(+), 110 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cc962fcff1..7329c05918 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -490,6 +490,7 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/compiler_rt/ceil.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/clear_cache.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/cmp.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/common.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/compareXf2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/cos.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/count0bits.zig" diff --git a/lib/compiler_rt/os_version_check.zig b/lib/compiler_rt/os_version_check.zig index 53f332b13e..2c6cdb54dc 100644 --- a/lib/compiler_rt/os_version_check.zig +++ b/lib/compiler_rt/os_version_check.zig @@ -6,10 +6,7 @@ pub const panic = @import("common.zig").panic; comptime { if (builtin.os.tag.isDarwin()) { - @export(IsPlatformVersionAtLeast.__isPlatformVersionAtLeast, .{ - .name = "__isPlatformVersionAtLeast", - .linkage = linkage, - }); + @export(__isPlatformVersionAtLeast, .{ .name = "__isPlatformVersionAtLeast", .linkage = linkage }); } } @@ -28,7 +25,7 @@ comptime { // the newer codepath, which merely calls out to the Darwin _availability_version_check API which is // available on macOS 10.15+, iOS 13+, tvOS 13+ and watchOS 6+. -const IsPlatformVersionAtLeast = struct { +const __isPlatformVersionAtLeast = if (builtin.os.tag.isDarwin()) struct { inline fn constructVersion(major: u32, minor: u32, subminor: u32) u32 { return ((major & 0xffff) << 16) | ((minor & 0xff) << 8) | (subminor & 0xff); } @@ -50,7 +47,7 @@ const IsPlatformVersionAtLeast = struct { }; // Darwin-only extern "c" fn _availability_version_check(count: u32, versions: [*c]const dyld_build_version_t) bool; -}; +}.__isPlatformVersionAtLeast else struct {}; test "isPlatformVersionAtLeast" { if (!comptime builtin.os.tag.isDarwin()) return error.SkipZigTest; @@ -58,6 +55,6 @@ test "isPlatformVersionAtLeast" { // Note: this test depends on the actual host OS version since it is merely calling into the // native Darwin API. const macos_platform_constant = 1; - try testing.expect(IsPlatformVersionAtLeast.__isPlatformVersionAtLeast(macos_platform_constant, 10, 0, 15) == 1); - try testing.expect(IsPlatformVersionAtLeast.__isPlatformVersionAtLeast(macos_platform_constant, 99, 0, 0) == 0); + try testing.expect(__isPlatformVersionAtLeast(macos_platform_constant, 10, 0, 15) == 1); + try testing.expect(__isPlatformVersionAtLeast(macos_platform_constant, 99, 0, 0) == 0); } diff --git a/src/Compilation.zig b/src/Compilation.zig index 0cd40a7001..2858a28f42 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -132,7 +132,7 @@ libssp_static_lib: ?CRTFile = null, libc_static_lib: ?CRTFile = null, /// Populated when we build the libcompiler_rt static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). -compiler_rt_static_lib: compiler_rt.CompilerRtLib = .{}, +compiler_rt_lib: ?CRTFile = null, /// Populated when we build the compiler_rt_obj object. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). compiler_rt_obj: ?CRTFile = null, @@ -1979,7 +1979,9 @@ pub fn destroy(self: *Compilation) void { if (self.libcxxabi_static_lib) |*crt_file| { crt_file.deinit(gpa); } - self.compiler_rt_static_lib.deinit(gpa); + if (self.compiler_rt_lib) |*crt_file| { + crt_file.deinit(gpa); + } if (self.compiler_rt_obj) |*crt_file| { crt_file.deinit(gpa); } @@ -3139,7 +3141,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { compiler_rt.buildCompilerRtLib( comp, - &comp.compiler_rt_static_lib, + &comp.compiler_rt_lib, ) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.SubCompilationFailed => return, // error reported already diff --git a/src/compiler_rt.zig b/src/compiler_rt.zig index 82b482daa8..8df14de38c 100644 --- a/src/compiler_rt.zig +++ b/src/compiler_rt.zig @@ -1,123 +1,169 @@ const std = @import("std"); const builtin = @import("builtin"); +const build_options = @import("build_options"); const Allocator = std.mem.Allocator; +const assert = std.debug.assert; const mem = std.mem; const tracy = @import("tracy.zig"); const trace = tracy.trace; +const Cache = @import("Cache.zig"); const Compilation = @import("Compilation.zig"); const CRTFile = Compilation.CRTFile; const LinkObject = Compilation.LinkObject; const Package = @import("Package.zig"); -pub const CompilerRtLib = struct { - crt_object_files: [sources.len]?CRTFile = undefined, - crt_lib_file: ?CRTFile = null, - - pub fn deinit(crt_lib: *CompilerRtLib, gpa: Allocator) void { - for (crt_lib.crt_object_files) |*crt_file| { - if (crt_file.*) |*cf| { - cf.deinit(gpa); - } - } - if (crt_lib.crt_lib_file) |*crt_file| { - crt_file.deinit(gpa); - } - } -}; - -pub fn buildCompilerRtLib(comp: *Compilation, compiler_rt_lib: *CompilerRtLib) !void { +pub fn buildCompilerRtLib(comp: *Compilation, compiler_rt_lib: *?CRTFile) !void { const tracy_trace = trace(@src()); defer tracy_trace.end(); - var progress: std.Progress = .{ .dont_print_on_dumb = true }; - var progress_node = progress.start("Compile Compiler-RT", sources.len + 1); - defer progress_node.end(); - if (comp.color == .off) progress.terminal = null; - - progress_node.activate(); - - var link_objects: [sources.len]LinkObject = undefined; - for (sources) |source, i| { - var obj_progress_node = progress_node.start(source, 0); - obj_progress_node.activate(); - defer obj_progress_node.end(); - - try comp.buildOutputFromZig(source, .Obj, &compiler_rt_lib.crt_object_files[i], .compiler_rt); - link_objects[i] = .{ - .path = compiler_rt_lib.crt_object_files[i].?.full_object_path, - .must_link = true, - }; - } - - const root_name = "compiler_rt"; - - var lib_progress_node = progress_node.start(root_name, 0); - lib_progress_node.activate(); - defer lib_progress_node.end(); + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); const target = comp.getTarget(); - const basename = try std.zig.binNameAlloc(comp.gpa, .{ - .root_name = root_name, - .target = target, - .output_mode = .Lib, - }); - errdefer comp.gpa.free(basename); - // TODO: This is extracted into a local variable to work around a stage1 miscompilation. - const emit_bin = Compilation.EmitLoc{ - .directory = null, // Put it in the cache directory. - .basename = basename, + // Use the global cache directory. + var cache_parent: Cache = .{ + .gpa = comp.gpa, + .manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}), }; - const sub_compilation = try Compilation.create(comp.gpa, .{ - .local_cache_directory = comp.global_cache_directory, - .global_cache_directory = comp.global_cache_directory, - .zig_lib_directory = comp.zig_lib_directory, - .cache_mode = .whole, - .target = target, + defer cache_parent.manifest_dir.close(); + + var cache = cache_parent.obtain(); + defer cache.deinit(); + + cache.hash.add(sources.len); + for (sources) |source| { + const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{source}); + _ = try cache.addFile(full_path, null); + } + + cache.hash.addBytes(build_options.version); + cache.hash.addBytes(comp.zig_lib_directory.path orelse "."); + cache.hash.add(target.cpu.arch); + cache.hash.add(target.os.tag); + cache.hash.add(target.abi); + + const hit = try cache.hit(); + const digest = cache.final(); + const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); + + var o_directory: Compilation.Directory = .{ + .handle = try comp.global_cache_directory.handle.makeOpenPath(o_sub_path, .{}), + .path = try std.fs.path.join(arena, &[_][]const u8{ comp.global_cache_directory.path.?, o_sub_path }), + }; + defer o_directory.handle.close(); + + const ok_basename = "ok"; + const actual_hit = if (hit) blk: { + o_directory.handle.access(ok_basename, .{}) catch |err| switch (err) { + error.FileNotFound => break :blk false, + else => |e| return e, + }; + break :blk true; + } else false; + + const root_name = "compiler_rt"; + const basename = try std.zig.binNameAlloc(arena, .{ .root_name = root_name, - .main_pkg = null, + .target = target, .output_mode = .Lib, - .link_mode = .Static, - .function_sections = true, - .thread_pool = comp.thread_pool, - .libc_installation = comp.bin_file.options.libc_installation, - .emit_bin = emit_bin, - .optimize_mode = comp.compilerRtOptMode(), - .want_sanitize_c = false, - .want_stack_check = false, - .want_red_zone = comp.bin_file.options.red_zone, - .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer, - .want_valgrind = false, - .want_tsan = false, - .want_pic = comp.bin_file.options.pic, - .want_pie = comp.bin_file.options.pie, - .want_lto = comp.bin_file.options.lto, - .emit_h = null, - .strip = comp.compilerRtStrip(), - .is_native_os = comp.bin_file.options.is_native_os, - .is_native_abi = comp.bin_file.options.is_native_abi, - .self_exe_path = comp.self_exe_path, - .link_objects = &link_objects, - .verbose_cc = comp.verbose_cc, - .verbose_link = comp.bin_file.options.verbose_link, - .verbose_air = comp.verbose_air, - .verbose_llvm_ir = comp.verbose_llvm_ir, - .verbose_cimport = comp.verbose_cimport, - .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, - .clang_passthrough_mode = comp.clang_passthrough_mode, - .skip_linker_dependencies = true, - .parent_compilation_link_libc = comp.bin_file.options.link_libc, }); - defer sub_compilation.destroy(); - try sub_compilation.updateSubCompilation(); + if (!actual_hit) { + var progress: std.Progress = .{ .dont_print_on_dumb = true }; + var progress_node = progress.start("Compile Compiler-RT", sources.len + 1); + defer progress_node.end(); + if (comp.color == .off) progress.terminal = null; - compiler_rt_lib.crt_lib_file = .{ - .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ - sub_compilation.bin_file.options.emit.?.sub_path, + progress_node.activate(); + + var link_objects: [sources.len]LinkObject = undefined; + for (sources) |source, i| { + var obj_progress_node = progress_node.start(source, 0); + obj_progress_node.activate(); + defer obj_progress_node.end(); + + var tmp_crt_file: ?CRTFile = null; + defer if (tmp_crt_file) |*crt| crt.deinit(comp.gpa); + try comp.buildOutputFromZig(source, .Obj, &tmp_crt_file, .compiler_rt); + link_objects[i] = .{ + .path = try arena.dupe(u8, tmp_crt_file.?.full_object_path), + .must_link = true, + }; + } + + var lib_progress_node = progress_node.start(root_name, 0); + lib_progress_node.activate(); + defer lib_progress_node.end(); + + // TODO: This is extracted into a local variable to work around a stage1 miscompilation. + const emit_bin = Compilation.EmitLoc{ + .directory = o_directory, // Put it in the cache directory. + .basename = basename, + }; + const sub_compilation = try Compilation.create(comp.gpa, .{ + .local_cache_directory = comp.global_cache_directory, + .global_cache_directory = comp.global_cache_directory, + .zig_lib_directory = comp.zig_lib_directory, + .cache_mode = .whole, + .target = target, + .root_name = root_name, + .main_pkg = null, + .output_mode = .Lib, + .link_mode = .Static, + .function_sections = true, + .thread_pool = comp.thread_pool, + .libc_installation = comp.bin_file.options.libc_installation, + .emit_bin = emit_bin, + .optimize_mode = comp.compilerRtOptMode(), + .want_sanitize_c = false, + .want_stack_check = false, + .want_red_zone = comp.bin_file.options.red_zone, + .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer, + .want_valgrind = false, + .want_tsan = false, + .want_pic = comp.bin_file.options.pic, + .want_pie = comp.bin_file.options.pie, + .want_lto = comp.bin_file.options.lto, + .emit_h = null, + .strip = comp.compilerRtStrip(), + .is_native_os = comp.bin_file.options.is_native_os, + .is_native_abi = comp.bin_file.options.is_native_abi, + .self_exe_path = comp.self_exe_path, + .link_objects = &link_objects, + .verbose_cc = comp.verbose_cc, + .verbose_link = comp.bin_file.options.verbose_link, + .verbose_air = comp.verbose_air, + .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_cimport = comp.verbose_cimport, + .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, + .clang_passthrough_mode = comp.clang_passthrough_mode, + .skip_linker_dependencies = true, + .parent_compilation_link_libc = comp.bin_file.options.link_libc, + }); + defer sub_compilation.destroy(); + + try sub_compilation.updateSubCompilation(); + + if (o_directory.handle.createFile(ok_basename, .{})) |file| { + file.close(); + } else |err| { + std.log.warn("compiler-rt lib: failed to mark completion: {s}", .{@errorName(err)}); + } + } + + try cache.writeManifest(); + + assert(compiler_rt_lib.* == null); + compiler_rt_lib.* = .{ + .full_object_path = try std.fs.path.join(comp.gpa, &[_][]const u8{ + comp.global_cache_directory.path.?, + o_sub_path, + basename, }), - .lock = sub_compilation.bin_file.toOwnedLock(), + .lock = cache.toOwnedLock(), }; } diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 4ab7cee6e1..2943cae36a 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -1354,7 +1354,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) ! } // MSVC compiler_rt is missing some stuff, so we build it unconditionally but // and rely on weak linkage to allow MSVC compiler_rt functions to override ours. - if (comp.compiler_rt_static_lib.crt_lib_file) |lib| { + if (comp.compiler_rt_lib) |lib| { try argv.append(lib.full_object_path); } } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 046aa2ec0a..a0e40e5682 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1272,7 +1272,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v const stack_size = self.base.options.stack_size_override orelse 16777216; const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os; const compiler_rt_path: ?[]const u8 = blk: { - if (comp.compiler_rt_static_lib.crt_lib_file) |x| break :blk x.full_object_path; + if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; break :blk null; }; diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 780b4b0483..92a651ad7d 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -738,7 +738,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try positionals.append(p); } - if (comp.compiler_rt_static_lib.crt_lib_file) |lib| { + if (comp.compiler_rt_lib) |lib| { try positionals.append(lib.full_object_path); } diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 30a1c49794..5a910e188b 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2255,7 +2255,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! const is_obj = self.base.options.output_mode == .Obj; const compiler_rt_path: ?[]const u8 = if (self.base.options.include_compiler_rt and !is_obj) - comp.compiler_rt_static_lib.crt_lib_file.?.full_object_path + comp.compiler_rt_lib.?.full_object_path else null; From 57c530155f0c7754386c50fb9bc128198cab705c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 13 Jun 2022 19:21:38 +0200 Subject: [PATCH 1850/2031] compiler_rt: correctly export allrem and aullrem for i386-windows-msvc --- lib/compiler_rt.zig | 1 + lib/compiler_rt/aulldiv.zig | 2 -- lib/compiler_rt/aullrem.zig | 13 +++++++++++++ src/compiler_rt.zig | 1 + 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig index 92d783cc25..999cce3e65 100644 --- a/lib/compiler_rt.zig +++ b/lib/compiler_rt.zig @@ -62,6 +62,7 @@ comptime { _ = @import("compiler_rt/emutls.zig"); _ = @import("compiler_rt/arm.zig"); _ = @import("compiler_rt/aulldiv.zig"); + _ = @import("compiler_rt/aullrem.zig"); _ = @import("compiler_rt/sparc.zig"); _ = @import("compiler_rt/clear_cache.zig"); diff --git a/lib/compiler_rt/aulldiv.zig b/lib/compiler_rt/aulldiv.zig index 7154cb39a1..38009d7015 100644 --- a/lib/compiler_rt/aulldiv.zig +++ b/lib/compiler_rt/aulldiv.zig @@ -10,8 +10,6 @@ comptime { // Don't let LLVM apply the stdcall name mangling on those MSVC builtins @export(_alldiv, .{ .name = "\x01__alldiv", .linkage = linkage }); @export(_aulldiv, .{ .name = "\x01__aulldiv", .linkage = linkage }); - @export(_allrem, .{ .name = "\x01__allrem", .linkage = linkage }); - @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = linkage }); } } diff --git a/lib/compiler_rt/aullrem.zig b/lib/compiler_rt/aullrem.zig index dbd52cd377..18e9eea0c6 100644 --- a/lib/compiler_rt/aullrem.zig +++ b/lib/compiler_rt/aullrem.zig @@ -1,4 +1,17 @@ +const std = @import("std"); const builtin = @import("builtin"); +const arch = builtin.cpu.arch; +const abi = builtin.abi; +const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Strong; +pub const panic = @import("common.zig").panic; + +comptime { + if (arch == .i386 and abi == .msvc) { + // Don't let LLVM apply the stdcall name mangling on those MSVC builtins + @export(_allrem, .{ .name = "\x01__allrem", .linkage = linkage }); + @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = linkage }); + } +} pub fn _allrem(a: i64, b: i64) callconv(.Stdcall) i64 { @setRuntimeSafety(builtin.is_test); diff --git a/src/compiler_rt.zig b/src/compiler_rt.zig index 8df14de38c..5149ce192f 100644 --- a/src/compiler_rt.zig +++ b/src/compiler_rt.zig @@ -227,6 +227,7 @@ const sources = &[_][]const u8{ "compiler_rt/emutls.zig", "compiler_rt/arm.zig", "compiler_rt/aulldiv.zig", + "compiler_rt/aullrem.zig", "compiler_rt/sparc.zig", "compiler_rt/clear_cache.zig", }; From f572e5a0c4e4ff34566533131b3423688a439863 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 13 Jun 2022 23:51:25 +0200 Subject: [PATCH 1851/2031] compiler_rt: shuffle order of imports to mark relevant symbols for export --- lib/compiler_rt.zig | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig index 999cce3e65..fec4573f46 100644 --- a/lib/compiler_rt.zig +++ b/lib/compiler_rt.zig @@ -2,8 +2,21 @@ const builtin = @import("builtin"); pub const panic = @import("compiler_rt/common.zig").panic; comptime { - // These files do their own comptime exporting logic. + // TODO moving these around makes or breaks compilation of zig1.o for some reason + // Perhaps, until we switch to stage2, exports should be duplicated between this file + // and files included as a standalone units? _ = @import("compiler_rt/atomics.zig"); + _ = @import("compiler_rt/addXf3.zig"); + _ = @import("compiler_rt/mulXf3.zig"); + _ = @import("compiler_rt/compareXf2.zig"); + _ = @import("compiler_rt/extendXfYf2.zig"); + _ = @import("compiler_rt/extend_f80.zig"); + _ = @import("compiler_rt/truncXfYf2.zig"); + _ = @import("compiler_rt/trunc_f80.zig"); + _ = @import("compiler_rt/divtf3.zig"); + _ = @import("compiler_rt/divsf3.zig"); + _ = @import("compiler_rt/divdf3.zig"); + _ = @import("compiler_rt/divxf3.zig"); _ = @import("compiler_rt/sin.zig"); _ = @import("compiler_rt/cos.zig"); _ = @import("compiler_rt/sincos.zig"); @@ -23,9 +36,6 @@ comptime { _ = @import("compiler_rt/sqrt.zig"); _ = @import("compiler_rt/tan.zig"); _ = @import("compiler_rt/trunc.zig"); - _ = @import("compiler_rt/extendXfYf2.zig"); - _ = @import("compiler_rt/extend_f80.zig"); - _ = @import("compiler_rt/compareXf2.zig"); _ = @import("compiler_rt/stack_probe.zig"); _ = @import("compiler_rt/divti3.zig"); _ = @import("compiler_rt/modti3.zig"); @@ -33,14 +43,6 @@ comptime { _ = @import("compiler_rt/udivti3.zig"); _ = @import("compiler_rt/udivmodti4.zig"); _ = @import("compiler_rt/umodti3.zig"); - _ = @import("compiler_rt/truncXfYf2.zig"); - _ = @import("compiler_rt/trunc_f80.zig"); - _ = @import("compiler_rt/addXf3.zig"); - _ = @import("compiler_rt/mulXf3.zig"); - _ = @import("compiler_rt/divsf3.zig"); - _ = @import("compiler_rt/divdf3.zig"); - _ = @import("compiler_rt/divxf3.zig"); - _ = @import("compiler_rt/divtf3.zig"); _ = @import("compiler_rt/floatXiYf.zig"); _ = @import("compiler_rt/fixXfYi.zig"); _ = @import("compiler_rt/count0bits.zig"); From 47c834e477657113bccaaad32e91148ff837f1a4 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 14 Jun 2022 14:12:12 +0200 Subject: [PATCH 1852/2031] macho: unify flushing object path with other linkers --- src/link.zig | 7 ++----- src/link/MachO.zig | 44 +++++++++++++++++++------------------------- 2 files changed, 21 insertions(+), 30 deletions(-) diff --git a/src/link.zig b/src/link.zig index 51712db40e..65e9de8ca3 100644 --- a/src/link.zig +++ b/src/link.zig @@ -792,11 +792,8 @@ pub const File = struct { }), } } - if (base.options.object_format == .macho) { - try base.cast(MachO).?.flushObject(comp, prog_node); - } else { - try base.flushModule(comp, prog_node); - } + try base.flushModule(comp, prog_node); + const dirname = fs.path.dirname(full_out_path_z) orelse "."; break :blk try fs.path.join(arena, &.{ dirname, base.intermediary_basename.? }); } else null; diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 92a651ad7d..44d763289a 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -436,7 +436,7 @@ pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !v return error.TODOImplementWritingStaticLibFiles; } } - try self.flushModule(comp, prog_node); + return self.flushModule(comp, prog_node); } pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void { @@ -444,8 +444,19 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No defer tracy.end(); const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; - if (!use_stage1 and self.base.options.output_mode == .Obj) - return self.flushObject(comp, prog_node); + + if (build_options.have_llvm and !use_stage1) { + if (self.llvm_object) |llvm_object| { + try llvm_object.flushModule(comp, prog_node); + + llvm_object.destroy(self.base.allocator); + self.llvm_object = null; + } + } + + var sub_prog_node = prog_node.start("MachO Flush", 0); + sub_prog_node.activate(); + defer sub_prog_node.end(); var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); defer arena_allocator.deinit(); @@ -454,12 +465,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); - if (self.d_sym) |*d_sym| { - if (self.base.options.module) |module| { - try d_sym.dwarf.flushModule(&self.base, module); - } - } - // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { @@ -482,8 +487,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No const obj_basename = self.base.intermediary_basename orelse break :blk null; - try self.flushObject(comp, prog_node); - if (fs.path.dirname(full_out_path)) |dirname| { break :blk try fs.path.join(arena, &.{ dirname, obj_basename }); } else { @@ -491,9 +494,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No } } else null; - var sub_prog_node = prog_node.start("MachO Flush", 0); - sub_prog_node.activate(); - defer sub_prog_node.end(); + if (self.d_sym) |*d_sym| { + if (self.base.options.module) |module| { + try d_sym.dwarf.flushModule(&self.base, module); + } + } const is_lib = self.base.options.output_mode == .Lib; const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; @@ -1119,17 +1124,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No self.cold_start = false; } -pub fn flushObject(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void { - const tracy = trace(@src()); - defer tracy.end(); - - if (build_options.have_llvm) - if (self.llvm_object) |llvm_object| - return llvm_object.flushModule(comp, prog_node); - - return error.TODOImplementWritingObjFiles; -} - fn resolveSearchDir( arena: Allocator, dir: []const u8, From c99c085d70c9347ec9a15d9a4f73c19e628912b7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 15 Jun 2022 23:09:56 -0700 Subject: [PATCH 1853/2031] compiler-rt: break up functions even more The purpose of this branch is to switch to using an object file for each independent function, in order to make linking simpler - instead of relying on `-ffunction-sections` and `--gc-sections`, which involves the linker doing the work of linking everything and then undoing work via garbage collection, this will allow the linker to only include the compilation units that are depended on in the first place. This commit makes progress towards that goal. --- CMakeLists.txt | 101 +++- lib/compiler_rt.zig | 147 +++++- lib/compiler_rt/absv.zig | 30 +- lib/compiler_rt/absvdi2.zig | 12 + lib/compiler_rt/absvsi2.zig | 12 + lib/compiler_rt/absvti2.zig | 12 + lib/compiler_rt/adddf3.zig | 20 + lib/compiler_rt/{addXf3.zig => addf3.zig} | 111 +---- .../{addXf3_test.zig => addf3_test.zig} | 6 +- lib/compiler_rt/addsf3.zig | 20 + lib/compiler_rt/addtf3.zig | 20 + lib/compiler_rt/addxf3.zig | 12 + lib/compiler_rt/cmpdf2.zig | 68 +++ lib/compiler_rt/cmpsf2.zig | 68 +++ lib/compiler_rt/cmptf2.zig | 73 +++ lib/compiler_rt/cmpxf2.zig | 50 ++ lib/compiler_rt/common.zig | 27 +- lib/compiler_rt/compareXf2.zig | 440 ------------------ lib/compiler_rt/comparef.zig | 118 +++++ lib/compiler_rt/extend_f80.zig | 140 ------ lib/compiler_rt/extenddftf2.zig | 20 + lib/compiler_rt/extenddfxf2.zig | 12 + .../{extendXfYf2.zig => extendf.zig} | 146 +++--- lib/compiler_rt/extendhfsf2.zig | 26 ++ lib/compiler_rt/extendhftf2.zig | 12 + lib/compiler_rt/extendhfxf2.zig | 12 + lib/compiler_rt/extendsfdf2.zig | 20 + lib/compiler_rt/extendsftf2.zig | 20 + lib/compiler_rt/extendsfxf2.zig | 12 + lib/compiler_rt/extendxftf2.zig | 50 ++ lib/compiler_rt/fixXfYi.zig | 312 ------------- lib/compiler_rt/fixdfdi.zig | 20 + lib/compiler_rt/fixdfsi.zig | 20 + lib/compiler_rt/fixdfti.zig | 12 + lib/compiler_rt/fixhfdi.zig | 12 + lib/compiler_rt/fixhfsi.zig | 12 + lib/compiler_rt/fixhfti.zig | 12 + lib/compiler_rt/fixsfdi.zig | 20 + lib/compiler_rt/fixsfsi.zig | 20 + lib/compiler_rt/fixsfti.zig | 12 + lib/compiler_rt/fixtfdi.zig | 20 + lib/compiler_rt/fixtfsi.zig | 20 + lib/compiler_rt/fixtfti.zig | 12 + lib/compiler_rt/fixunsdfdi.zig | 20 + lib/compiler_rt/fixunsdfsi.zig | 20 + lib/compiler_rt/fixunsdfti.zig | 12 + lib/compiler_rt/fixunshfdi.zig | 12 + lib/compiler_rt/fixunshfsi.zig | 12 + lib/compiler_rt/fixunshfti.zig | 12 + lib/compiler_rt/fixunssfdi.zig | 20 + lib/compiler_rt/fixunssfsi.zig | 20 + lib/compiler_rt/fixunssfti.zig | 12 + lib/compiler_rt/fixunstfdi.zig | 20 + lib/compiler_rt/fixunstfsi.zig | 20 + lib/compiler_rt/fixunstfti.zig | 12 + lib/compiler_rt/fixunsxfdi.zig | 12 + lib/compiler_rt/fixunsxfsi.zig | 12 + lib/compiler_rt/fixunsxfti.zig | 12 + lib/compiler_rt/fixxfdi.zig | 12 + lib/compiler_rt/fixxfsi.zig | 12 + lib/compiler_rt/fixxfti.zig | 12 + lib/compiler_rt/floatXiYf.zig | 311 ------------- lib/compiler_rt/float_to_int.zig | 55 +++ ...fixXfYi_test.zig => float_to_int_test.zig} | 0 lib/compiler_rt/floatdidf.zig | 20 + lib/compiler_rt/floatdihf.zig | 12 + lib/compiler_rt/floatdisf.zig | 20 + lib/compiler_rt/floatditf.zig | 20 + lib/compiler_rt/floatdixf.zig | 12 + lib/compiler_rt/floatsidf.zig | 20 + lib/compiler_rt/floatsihf.zig | 12 + lib/compiler_rt/floatsisf.zig | 20 + lib/compiler_rt/floatsitf.zig | 20 + lib/compiler_rt/floatsixf.zig | 12 + lib/compiler_rt/floattidf.zig | 12 + lib/compiler_rt/floattihf.zig | 12 + lib/compiler_rt/floattisf.zig | 12 + lib/compiler_rt/floattitf.zig | 12 + lib/compiler_rt/floattixf.zig | 12 + lib/compiler_rt/floatundidf.zig | 20 + lib/compiler_rt/floatundihf.zig | 12 + lib/compiler_rt/floatundisf.zig | 20 + lib/compiler_rt/floatunditf.zig | 20 + lib/compiler_rt/floatundixf.zig | 12 + lib/compiler_rt/floatunsidf.zig | 20 + lib/compiler_rt/floatunsihf.zig | 12 + lib/compiler_rt/floatunsisf.zig | 20 + lib/compiler_rt/floatunsitf.zig | 20 + lib/compiler_rt/floatunsixf.zig | 12 + lib/compiler_rt/floatuntidf.zig | 12 + lib/compiler_rt/floatuntihf.zig | 12 + lib/compiler_rt/floatuntisf.zig | 12 + lib/compiler_rt/floatuntitf.zig | 20 + lib/compiler_rt/floatuntixf.zig | 12 + lib/compiler_rt/gedf2.zig | 36 ++ lib/compiler_rt/gesf2.zig | 36 ++ lib/compiler_rt/getf2.zig | 36 ++ lib/compiler_rt/gexf2.zig | 17 + lib/compiler_rt/int_to_float.zig | 58 +++ lib/compiler_rt/muldf3.zig | 20 + lib/compiler_rt/{mulXf3.zig => mulf3.zig} | 217 ++------- .../{mulXf3_test.zig => mulf3_test.zig} | 8 +- lib/compiler_rt/mulsf3.zig | 20 + lib/compiler_rt/multf3.zig | 20 + lib/compiler_rt/mulxf3.zig | 12 + lib/compiler_rt/sparc.zig | 6 +- lib/compiler_rt/subdf3.zig | 21 + lib/compiler_rt/subsf3.zig | 21 + lib/compiler_rt/subtf3.zig | 21 + lib/compiler_rt/subxf3.zig | 15 + lib/compiler_rt/trunc_f80.zig | 183 -------- lib/compiler_rt/truncdfhf2.zig | 20 + lib/compiler_rt/truncdfsf2.zig | 20 + .../{truncXfYf2.zig => truncf.zig} | 175 ++++--- .../{truncXfYf2_test.zig => truncf_test.zig} | 14 + lib/compiler_rt/truncsfhf2.zig | 26 ++ lib/compiler_rt/trunctfdf2.zig | 20 + lib/compiler_rt/trunctfhf2.zig | 12 + lib/compiler_rt/trunctfsf2.zig | 20 + lib/compiler_rt/trunctfxf2.zig | 66 +++ lib/compiler_rt/truncxfdf2.zig | 12 + lib/compiler_rt/truncxfhf2.zig | 12 + lib/compiler_rt/truncxfsf2.zig | 12 + lib/compiler_rt/unorddf2.zig | 20 + lib/compiler_rt/unordsf2.zig | 20 + lib/compiler_rt/unordtf2.zig | 20 + src/compiler_rt.zig | 4 +- 127 files changed, 2709 insertions(+), 1926 deletions(-) create mode 100644 lib/compiler_rt/absvdi2.zig create mode 100644 lib/compiler_rt/absvsi2.zig create mode 100644 lib/compiler_rt/absvti2.zig create mode 100644 lib/compiler_rt/adddf3.zig rename lib/compiler_rt/{addXf3.zig => addf3.zig} (62%) rename lib/compiler_rt/{addXf3_test.zig => addf3_test.zig} (97%) create mode 100644 lib/compiler_rt/addsf3.zig create mode 100644 lib/compiler_rt/addtf3.zig create mode 100644 lib/compiler_rt/addxf3.zig create mode 100644 lib/compiler_rt/cmpdf2.zig create mode 100644 lib/compiler_rt/cmpsf2.zig create mode 100644 lib/compiler_rt/cmptf2.zig create mode 100644 lib/compiler_rt/cmpxf2.zig delete mode 100644 lib/compiler_rt/compareXf2.zig create mode 100644 lib/compiler_rt/comparef.zig delete mode 100644 lib/compiler_rt/extend_f80.zig create mode 100644 lib/compiler_rt/extenddftf2.zig create mode 100644 lib/compiler_rt/extenddfxf2.zig rename lib/compiler_rt/{extendXfYf2.zig => extendf.zig} (51%) create mode 100644 lib/compiler_rt/extendhfsf2.zig create mode 100644 lib/compiler_rt/extendhftf2.zig create mode 100644 lib/compiler_rt/extendhfxf2.zig create mode 100644 lib/compiler_rt/extendsfdf2.zig create mode 100644 lib/compiler_rt/extendsftf2.zig create mode 100644 lib/compiler_rt/extendsfxf2.zig create mode 100644 lib/compiler_rt/extendxftf2.zig delete mode 100644 lib/compiler_rt/fixXfYi.zig create mode 100644 lib/compiler_rt/fixdfdi.zig create mode 100644 lib/compiler_rt/fixdfsi.zig create mode 100644 lib/compiler_rt/fixdfti.zig create mode 100644 lib/compiler_rt/fixhfdi.zig create mode 100644 lib/compiler_rt/fixhfsi.zig create mode 100644 lib/compiler_rt/fixhfti.zig create mode 100644 lib/compiler_rt/fixsfdi.zig create mode 100644 lib/compiler_rt/fixsfsi.zig create mode 100644 lib/compiler_rt/fixsfti.zig create mode 100644 lib/compiler_rt/fixtfdi.zig create mode 100644 lib/compiler_rt/fixtfsi.zig create mode 100644 lib/compiler_rt/fixtfti.zig create mode 100644 lib/compiler_rt/fixunsdfdi.zig create mode 100644 lib/compiler_rt/fixunsdfsi.zig create mode 100644 lib/compiler_rt/fixunsdfti.zig create mode 100644 lib/compiler_rt/fixunshfdi.zig create mode 100644 lib/compiler_rt/fixunshfsi.zig create mode 100644 lib/compiler_rt/fixunshfti.zig create mode 100644 lib/compiler_rt/fixunssfdi.zig create mode 100644 lib/compiler_rt/fixunssfsi.zig create mode 100644 lib/compiler_rt/fixunssfti.zig create mode 100644 lib/compiler_rt/fixunstfdi.zig create mode 100644 lib/compiler_rt/fixunstfsi.zig create mode 100644 lib/compiler_rt/fixunstfti.zig create mode 100644 lib/compiler_rt/fixunsxfdi.zig create mode 100644 lib/compiler_rt/fixunsxfsi.zig create mode 100644 lib/compiler_rt/fixunsxfti.zig create mode 100644 lib/compiler_rt/fixxfdi.zig create mode 100644 lib/compiler_rt/fixxfsi.zig create mode 100644 lib/compiler_rt/fixxfti.zig delete mode 100644 lib/compiler_rt/floatXiYf.zig create mode 100644 lib/compiler_rt/float_to_int.zig rename lib/compiler_rt/{fixXfYi_test.zig => float_to_int_test.zig} (100%) create mode 100644 lib/compiler_rt/floatdidf.zig create mode 100644 lib/compiler_rt/floatdihf.zig create mode 100644 lib/compiler_rt/floatdisf.zig create mode 100644 lib/compiler_rt/floatditf.zig create mode 100644 lib/compiler_rt/floatdixf.zig create mode 100644 lib/compiler_rt/floatsidf.zig create mode 100644 lib/compiler_rt/floatsihf.zig create mode 100644 lib/compiler_rt/floatsisf.zig create mode 100644 lib/compiler_rt/floatsitf.zig create mode 100644 lib/compiler_rt/floatsixf.zig create mode 100644 lib/compiler_rt/floattidf.zig create mode 100644 lib/compiler_rt/floattihf.zig create mode 100644 lib/compiler_rt/floattisf.zig create mode 100644 lib/compiler_rt/floattitf.zig create mode 100644 lib/compiler_rt/floattixf.zig create mode 100644 lib/compiler_rt/floatundidf.zig create mode 100644 lib/compiler_rt/floatundihf.zig create mode 100644 lib/compiler_rt/floatundisf.zig create mode 100644 lib/compiler_rt/floatunditf.zig create mode 100644 lib/compiler_rt/floatundixf.zig create mode 100644 lib/compiler_rt/floatunsidf.zig create mode 100644 lib/compiler_rt/floatunsihf.zig create mode 100644 lib/compiler_rt/floatunsisf.zig create mode 100644 lib/compiler_rt/floatunsitf.zig create mode 100644 lib/compiler_rt/floatunsixf.zig create mode 100644 lib/compiler_rt/floatuntidf.zig create mode 100644 lib/compiler_rt/floatuntihf.zig create mode 100644 lib/compiler_rt/floatuntisf.zig create mode 100644 lib/compiler_rt/floatuntitf.zig create mode 100644 lib/compiler_rt/floatuntixf.zig create mode 100644 lib/compiler_rt/gedf2.zig create mode 100644 lib/compiler_rt/gesf2.zig create mode 100644 lib/compiler_rt/getf2.zig create mode 100644 lib/compiler_rt/gexf2.zig create mode 100644 lib/compiler_rt/int_to_float.zig create mode 100644 lib/compiler_rt/muldf3.zig rename lib/compiler_rt/{mulXf3.zig => mulf3.zig} (51%) rename lib/compiler_rt/{mulXf3_test.zig => mulf3_test.zig} (97%) create mode 100644 lib/compiler_rt/mulsf3.zig create mode 100644 lib/compiler_rt/multf3.zig create mode 100644 lib/compiler_rt/mulxf3.zig create mode 100644 lib/compiler_rt/subdf3.zig create mode 100644 lib/compiler_rt/subsf3.zig create mode 100644 lib/compiler_rt/subtf3.zig create mode 100644 lib/compiler_rt/subxf3.zig delete mode 100644 lib/compiler_rt/trunc_f80.zig create mode 100644 lib/compiler_rt/truncdfhf2.zig create mode 100644 lib/compiler_rt/truncdfsf2.zig rename lib/compiler_rt/{truncXfYf2.zig => truncf.zig} (55%) rename lib/compiler_rt/{truncXfYf2_test.zig => truncf_test.zig} (96%) create mode 100644 lib/compiler_rt/truncsfhf2.zig create mode 100644 lib/compiler_rt/trunctfdf2.zig create mode 100644 lib/compiler_rt/trunctfhf2.zig create mode 100644 lib/compiler_rt/trunctfsf2.zig create mode 100644 lib/compiler_rt/trunctfxf2.zig create mode 100644 lib/compiler_rt/truncxfdf2.zig create mode 100644 lib/compiler_rt/truncxfhf2.zig create mode 100644 lib/compiler_rt/truncxfsf2.zig create mode 100644 lib/compiler_rt/unorddf2.zig create mode 100644 lib/compiler_rt/unordsf2.zig create mode 100644 lib/compiler_rt/unordtf2.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 7329c05918..a393249458 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -480,7 +480,14 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/sort.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/absv.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/addXf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/addf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/addsf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/addtf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/addxf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subdf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subsf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subtf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subxf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/addo.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/arm.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/atomics.zig" @@ -491,7 +498,18 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/compiler_rt/clear_cache.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/cmp.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/common.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/compareXf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/comparef.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/cmpsf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/cmpdf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/cmptf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/cmpxf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/gesf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/gedf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/getf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/gexf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/unordsf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/unorddf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/unordtf2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/cos.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/count0bits.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/divdf3.zig" @@ -502,11 +520,79 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/compiler_rt/emutls.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/exp.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/exp2.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extendXfYf2.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extend_f80.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extendf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extenddftf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extenddfxf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extendhfsf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extendhftf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extendhfxf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extendsfdf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extendsftf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extendsfxf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extendxftf2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fabs.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixXfYi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatXiYf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/int_to_float.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatsihf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatsisf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatsidf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatsitf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatsixf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatdihf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatdisf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatdidf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatditf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatdixf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floattihf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floattisf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floattidf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floattitf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floattixf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatundihf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatundisf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatundidf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatunditf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatundixf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatunsihf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatunsisf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatunsidf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatunsitf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatunsixf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatuntihf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatuntisf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatuntidf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatuntitf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatuntixf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/float_to_int.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixhfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixhfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixhfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixsfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixsfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixsfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixdfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixdfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixdfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixtfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixtfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixtfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixxfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixxfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixxfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunshfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunshfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunshfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunssfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunssfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunssfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunsdfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunsdfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunsdfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunstfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunstfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunstfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunsxfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunsxfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunsxfti.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floor.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fma.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fmax.zig" @@ -517,7 +603,6 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/compiler_rt/log10.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/log2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/modti3.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulXf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/muldi3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulo.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/multi3.zig" @@ -541,8 +626,6 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/compiler_rt/tan.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/trig.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/trunc.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/truncXfYf2.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/trunc_f80.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/udivmod.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/udivmodti4.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/udivti3.zig" diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig index fec4573f46..826530286d 100644 --- a/lib/compiler_rt.zig +++ b/lib/compiler_rt.zig @@ -1,18 +1,59 @@ -const builtin = @import("builtin"); pub const panic = @import("compiler_rt/common.zig").panic; comptime { - // TODO moving these around makes or breaks compilation of zig1.o for some reason - // Perhaps, until we switch to stage2, exports should be duplicated between this file - // and files included as a standalone units? _ = @import("compiler_rt/atomics.zig"); - _ = @import("compiler_rt/addXf3.zig"); - _ = @import("compiler_rt/mulXf3.zig"); - _ = @import("compiler_rt/compareXf2.zig"); - _ = @import("compiler_rt/extendXfYf2.zig"); - _ = @import("compiler_rt/extend_f80.zig"); - _ = @import("compiler_rt/truncXfYf2.zig"); - _ = @import("compiler_rt/trunc_f80.zig"); + + _ = @import("compiler_rt/addf3.zig"); + _ = @import("compiler_rt/addsf3.zig"); + _ = @import("compiler_rt/addtf3.zig"); + _ = @import("compiler_rt/addxf3.zig"); + _ = @import("compiler_rt/subdf3.zig"); + _ = @import("compiler_rt/subsf3.zig"); + _ = @import("compiler_rt/subtf3.zig"); + _ = @import("compiler_rt/subxf3.zig"); + + _ = @import("compiler_rt/mulf3.zig"); + _ = @import("compiler_rt/muldf3.zig"); + _ = @import("compiler_rt/mulsf3.zig"); + _ = @import("compiler_rt/multf3.zig"); + _ = @import("compiler_rt/mulxf3.zig"); + + _ = @import("compiler_rt/comparef.zig"); + _ = @import("compiler_rt/cmpsf2.zig"); + _ = @import("compiler_rt/cmpdf2.zig"); + _ = @import("compiler_rt/cmptf2.zig"); + _ = @import("compiler_rt/cmpxf2.zig"); + _ = @import("compiler_rt/gesf2.zig"); + _ = @import("compiler_rt/gedf2.zig"); + _ = @import("compiler_rt/getf2.zig"); + _ = @import("compiler_rt/gexf2.zig"); + _ = @import("compiler_rt/unordsf2.zig"); + _ = @import("compiler_rt/unorddf2.zig"); + _ = @import("compiler_rt/unordtf2.zig"); + + _ = @import("compiler_rt/extendf.zig"); + _ = @import("compiler_rt/extenddftf2.zig"); + _ = @import("compiler_rt/extenddfxf2.zig"); + _ = @import("compiler_rt/extendhfsf2.zig"); + _ = @import("compiler_rt/extendhftf2.zig"); + _ = @import("compiler_rt/extendhfxf2.zig"); + _ = @import("compiler_rt/extendsfdf2.zig"); + _ = @import("compiler_rt/extendsftf2.zig"); + _ = @import("compiler_rt/extendsfxf2.zig"); + _ = @import("compiler_rt/extendxftf2.zig"); + + _ = @import("compiler_rt/truncf.zig"); + _ = @import("compiler_rt/truncsfhf2.zig"); + _ = @import("compiler_rt/truncdfhf2.zig"); + _ = @import("compiler_rt/truncdfsf2.zig"); + _ = @import("compiler_rt/trunctfhf2.zig"); + _ = @import("compiler_rt/trunctfsf2.zig"); + _ = @import("compiler_rt/trunctfdf2.zig"); + _ = @import("compiler_rt/trunctfxf2.zig"); + _ = @import("compiler_rt/truncxfhf2.zig"); + _ = @import("compiler_rt/truncxfsf2.zig"); + _ = @import("compiler_rt/truncxfdf2.zig"); + _ = @import("compiler_rt/divtf3.zig"); _ = @import("compiler_rt/divsf3.zig"); _ = @import("compiler_rt/divdf3.zig"); @@ -43,35 +84,101 @@ comptime { _ = @import("compiler_rt/udivti3.zig"); _ = @import("compiler_rt/udivmodti4.zig"); _ = @import("compiler_rt/umodti3.zig"); - _ = @import("compiler_rt/floatXiYf.zig"); - _ = @import("compiler_rt/fixXfYi.zig"); + + _ = @import("compiler_rt/int_to_float.zig"); + _ = @import("compiler_rt/floatsihf.zig"); + _ = @import("compiler_rt/floatsisf.zig"); + _ = @import("compiler_rt/floatsidf.zig"); + _ = @import("compiler_rt/floatsitf.zig"); + _ = @import("compiler_rt/floatsixf.zig"); + _ = @import("compiler_rt/floatdihf.zig"); + _ = @import("compiler_rt/floatdisf.zig"); + _ = @import("compiler_rt/floatdidf.zig"); + _ = @import("compiler_rt/floatditf.zig"); + _ = @import("compiler_rt/floatdixf.zig"); + _ = @import("compiler_rt/floattihf.zig"); + _ = @import("compiler_rt/floattisf.zig"); + _ = @import("compiler_rt/floattidf.zig"); + _ = @import("compiler_rt/floattitf.zig"); + _ = @import("compiler_rt/floattixf.zig"); + _ = @import("compiler_rt/floatundihf.zig"); + _ = @import("compiler_rt/floatundisf.zig"); + _ = @import("compiler_rt/floatundidf.zig"); + _ = @import("compiler_rt/floatunditf.zig"); + _ = @import("compiler_rt/floatundixf.zig"); + _ = @import("compiler_rt/floatunsihf.zig"); + _ = @import("compiler_rt/floatunsisf.zig"); + _ = @import("compiler_rt/floatunsidf.zig"); + _ = @import("compiler_rt/floatunsitf.zig"); + _ = @import("compiler_rt/floatunsixf.zig"); + _ = @import("compiler_rt/floatuntihf.zig"); + _ = @import("compiler_rt/floatuntisf.zig"); + _ = @import("compiler_rt/floatuntidf.zig"); + _ = @import("compiler_rt/floatuntitf.zig"); + _ = @import("compiler_rt/floatuntixf.zig"); + + _ = @import("compiler_rt/float_to_int.zig"); + _ = @import("compiler_rt/fixhfsi.zig"); + _ = @import("compiler_rt/fixhfdi.zig"); + _ = @import("compiler_rt/fixhfti.zig"); + _ = @import("compiler_rt/fixsfsi.zig"); + _ = @import("compiler_rt/fixsfdi.zig"); + _ = @import("compiler_rt/fixsfti.zig"); + _ = @import("compiler_rt/fixdfsi.zig"); + _ = @import("compiler_rt/fixdfdi.zig"); + _ = @import("compiler_rt/fixdfti.zig"); + _ = @import("compiler_rt/fixtfsi.zig"); + _ = @import("compiler_rt/fixtfdi.zig"); + _ = @import("compiler_rt/fixtfti.zig"); + _ = @import("compiler_rt/fixxfsi.zig"); + _ = @import("compiler_rt/fixxfdi.zig"); + _ = @import("compiler_rt/fixxfti.zig"); + _ = @import("compiler_rt/fixunshfsi.zig"); + _ = @import("compiler_rt/fixunshfdi.zig"); + _ = @import("compiler_rt/fixunshfti.zig"); + _ = @import("compiler_rt/fixunssfsi.zig"); + _ = @import("compiler_rt/fixunssfdi.zig"); + _ = @import("compiler_rt/fixunssfti.zig"); + _ = @import("compiler_rt/fixunsdfsi.zig"); + _ = @import("compiler_rt/fixunsdfdi.zig"); + _ = @import("compiler_rt/fixunsdfti.zig"); + _ = @import("compiler_rt/fixunstfsi.zig"); + _ = @import("compiler_rt/fixunstfdi.zig"); + _ = @import("compiler_rt/fixunstfti.zig"); + _ = @import("compiler_rt/fixunsxfsi.zig"); + _ = @import("compiler_rt/fixunsxfdi.zig"); + _ = @import("compiler_rt/fixunsxfti.zig"); + _ = @import("compiler_rt/count0bits.zig"); _ = @import("compiler_rt/parity.zig"); _ = @import("compiler_rt/popcount.zig"); _ = @import("compiler_rt/bswap.zig"); _ = @import("compiler_rt/int.zig"); _ = @import("compiler_rt/shift.zig"); + _ = @import("compiler_rt/negXi2.zig"); + _ = @import("compiler_rt/muldi3.zig"); + _ = @import("compiler_rt/absv.zig"); + _ = @import("compiler_rt/absvsi2.zig"); + _ = @import("compiler_rt/absvdi2.zig"); + _ = @import("compiler_rt/absvti2.zig"); + _ = @import("compiler_rt/negv.zig"); _ = @import("compiler_rt/addo.zig"); _ = @import("compiler_rt/subo.zig"); _ = @import("compiler_rt/mulo.zig"); _ = @import("compiler_rt/cmp.zig"); + _ = @import("compiler_rt/negXf2.zig"); + _ = @import("compiler_rt/os_version_check.zig"); _ = @import("compiler_rt/emutls.zig"); _ = @import("compiler_rt/arm.zig"); _ = @import("compiler_rt/aulldiv.zig"); _ = @import("compiler_rt/aullrem.zig"); - _ = @import("compiler_rt/sparc.zig"); _ = @import("compiler_rt/clear_cache.zig"); - // missing: Floating point raised to integer power - - // missing: Complex arithmetic - // (a + ib) * (c + id) - // (a + ib) / (c + id) - + _ = @import("compiler_rt/sparc.zig"); } diff --git a/lib/compiler_rt/absv.zig b/lib/compiler_rt/absv.zig index 3d9476b6c7..8910a4a6b9 100644 --- a/lib/compiler_rt/absv.zig +++ b/lib/compiler_rt/absv.zig @@ -1,18 +1,6 @@ -// absv - absolute oVerflow -// * @panic, if value can not be represented -// - absvXi4_generic for unoptimized version -const std = @import("std"); -const builtin = @import("builtin"); -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; - -comptime { - @export(__absvsi2, .{ .name = "__absvsi2", .linkage = linkage }); - @export(__absvdi2, .{ .name = "__absvdi2", .linkage = linkage }); - @export(__absvti2, .{ .name = "__absvti2", .linkage = linkage }); -} - -inline fn absvXi(comptime ST: type, a: ST) ST { +/// absv - absolute oVerflow +/// * @panic if value can not be represented +pub inline fn absv(comptime ST: type, a: ST) ST { const UT = switch (ST) { i32 => u32, i64 => u64, @@ -31,18 +19,6 @@ inline fn absvXi(comptime ST: type, a: ST) ST { return x; } -pub fn __absvsi2(a: i32) callconv(.C) i32 { - return absvXi(i32, a); -} - -pub fn __absvdi2(a: i64) callconv(.C) i64 { - return absvXi(i64, a); -} - -pub fn __absvti2(a: i128) callconv(.C) i128 { - return absvXi(i128, a); -} - test { _ = @import("absvsi2_test.zig"); _ = @import("absvdi2_test.zig"); diff --git a/lib/compiler_rt/absvdi2.zig b/lib/compiler_rt/absvdi2.zig new file mode 100644 index 0000000000..1875d5bbaa --- /dev/null +++ b/lib/compiler_rt/absvdi2.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const absv = @import("./absv.zig").absv; + +pub const panic = common.panic; + +comptime { + @export(__absvdi2, .{ .name = "__absvdi2", .linkage = common.linkage }); +} + +fn __absvdi2(a: i64) callconv(.C) i64 { + return absv(i64, a); +} diff --git a/lib/compiler_rt/absvsi2.zig b/lib/compiler_rt/absvsi2.zig new file mode 100644 index 0000000000..965e39bc9b --- /dev/null +++ b/lib/compiler_rt/absvsi2.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const absv = @import("./absv.zig").absv; + +pub const panic = common.panic; + +comptime { + @export(__absvsi2, .{ .name = "__absvsi2", .linkage = common.linkage }); +} + +fn __absvsi2(a: i32) callconv(.C) i32 { + return absv(i32, a); +} diff --git a/lib/compiler_rt/absvti2.zig b/lib/compiler_rt/absvti2.zig new file mode 100644 index 0000000000..ada06c6563 --- /dev/null +++ b/lib/compiler_rt/absvti2.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const absv = @import("./absv.zig").absv; + +pub const panic = common.panic; + +comptime { + @export(__absvti2, .{ .name = "__absvti2", .linkage = common.linkage }); +} + +fn __absvti2(a: i128) callconv(.C) i128 { + return absv(i128, a); +} diff --git a/lib/compiler_rt/adddf3.zig b/lib/compiler_rt/adddf3.zig new file mode 100644 index 0000000000..1b511f78a4 --- /dev/null +++ b/lib/compiler_rt/adddf3.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const addf3 = @import("./addf3.zig").addf3; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_dadd, .{ .name = "__aeabi_dadd", .linkage = common.linkage }); + } else { + @export(__adddf3, .{ .name = "__adddf3", .linkage = common.linkage }); + } +} + +fn __adddf3(a: f64, b: f64) callconv(.C) f64 { + return addf3(f64, a, b); +} + +fn __aeabi_dadd(a: f64, b: f64) callconv(.AAPCS) f64 { + return addf3(f64, a, b); +} diff --git a/lib/compiler_rt/addXf3.zig b/lib/compiler_rt/addf3.zig similarity index 62% rename from lib/compiler_rt/addXf3.zig rename to lib/compiler_rt/addf3.zig index e2cf9d0112..7f2e368121 100644 --- a/lib/compiler_rt/addXf3.zig +++ b/lib/compiler_rt/addf3.zig @@ -1,111 +1,12 @@ -// Ported from: -// -// https://github.com/llvm/llvm-project/blob/02d85149a05cb1f6dc49f0ba7a2ceca53718ae17/compiler-rt/lib/builtins/fp_add_impl.inc - const std = @import("std"); -const builtin = @import("builtin"); const math = std.math; -const arch = builtin.cpu.arch; -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; - -const common = @import("common.zig"); +const common = @import("./common.zig"); const normalize = common.normalize; -pub const panic = common.panic; -comptime { - @export(__addsf3, .{ .name = "__addsf3", .linkage = linkage }); - @export(__adddf3, .{ .name = "__adddf3", .linkage = linkage }); - @export(__addxf3, .{ .name = "__addxf3", .linkage = linkage }); - @export(__addtf3, .{ .name = "__addtf3", .linkage = linkage }); - - @export(__subsf3, .{ .name = "__subsf3", .linkage = linkage }); - @export(__subdf3, .{ .name = "__subdf3", .linkage = linkage }); - @export(__subxf3, .{ .name = "__subxf3", .linkage = linkage }); - @export(__subtf3, .{ .name = "__subtf3", .linkage = linkage }); - - if (!is_test) { - if (arch.isARM() or arch.isThumb()) { - @export(__aeabi_fadd, .{ .name = "__aeabi_fadd", .linkage = linkage }); - @export(__aeabi_dadd, .{ .name = "__aeabi_dadd", .linkage = linkage }); - @export(__aeabi_fsub, .{ .name = "__aeabi_fsub", .linkage = linkage }); - @export(__aeabi_dsub, .{ .name = "__aeabi_dsub", .linkage = linkage }); - } - - if (arch.isPPC() or arch.isPPC64()) { - @export(__addkf3, .{ .name = "__addkf3", .linkage = linkage }); - @export(__subkf3, .{ .name = "__subkf3", .linkage = linkage }); - } - } -} - -pub fn __addsf3(a: f32, b: f32) callconv(.C) f32 { - return addXf3(f32, a, b); -} - -pub fn __adddf3(a: f64, b: f64) callconv(.C) f64 { - return addXf3(f64, a, b); -} - -pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { - return addXf3(f80, a, b); -} - -pub fn __subxf3(a: f80, b: f80) callconv(.C) f80 { - var b_rep = std.math.break_f80(b); - b_rep.exp ^= 0x8000; - return __addxf3(a, std.math.make_f80(b_rep)); -} - -pub fn __addtf3(a: f128, b: f128) callconv(.C) f128 { - return addXf3(f128, a, b); -} - -pub fn __addkf3(a: f128, b: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, __addtf3, .{ a, b }); -} - -pub fn __subsf3(a: f32, b: f32) callconv(.C) f32 { - const neg_b = @bitCast(f32, @bitCast(u32, b) ^ (@as(u32, 1) << 31)); - return addXf3(f32, a, neg_b); -} - -pub fn __subdf3(a: f64, b: f64) callconv(.C) f64 { - const neg_b = @bitCast(f64, @bitCast(u64, b) ^ (@as(u64, 1) << 63)); - return addXf3(f64, a, neg_b); -} - -pub fn __subtf3(a: f128, b: f128) callconv(.C) f128 { - const neg_b = @bitCast(f128, @bitCast(u128, b) ^ (@as(u128, 1) << 127)); - return addXf3(f128, a, neg_b); -} - -pub fn __subkf3(a: f128, b: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, __subtf3, .{ a, b }); -} - -pub fn __aeabi_fadd(a: f32, b: f32) callconv(.AAPCS) f32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __addsf3, .{ a, b }); -} - -pub fn __aeabi_dadd(a: f64, b: f64) callconv(.AAPCS) f64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __adddf3, .{ a, b }); -} - -pub fn __aeabi_fsub(a: f32, b: f32) callconv(.AAPCS) f32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __subsf3, .{ a, b }); -} - -pub fn __aeabi_dsub(a: f64, b: f64) callconv(.AAPCS) f64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __subdf3, .{ a, b }); -} - -// TODO: restore inline keyword, see: https://github.com/ziglang/zig/issues/2154 -pub fn addXf3(comptime T: type, a: T, b: T) T { +/// Ported from: +/// +/// https://github.com/llvm/llvm-project/blob/02d85149a05cb1f6dc49f0ba7a2ceca53718ae17/compiler-rt/lib/builtins/fp_add_impl.inc +pub inline fn addf3(comptime T: type, a: T, b: T) T { const bits = @typeInfo(T).Float.bits; const Z = std.meta.Int(.unsigned, bits); const S = std.meta.Int(.unsigned, bits - @clz(Z, @as(Z, bits) - 1)); @@ -267,5 +168,5 @@ pub fn addXf3(comptime T: type, a: T, b: T) T { } test { - _ = @import("addXf3_test.zig"); + _ = @import("addf3_test.zig"); } diff --git a/lib/compiler_rt/addXf3_test.zig b/lib/compiler_rt/addf3_test.zig similarity index 97% rename from lib/compiler_rt/addXf3_test.zig rename to lib/compiler_rt/addf3_test.zig index f38c9d6018..cd3f3ab89c 100644 --- a/lib/compiler_rt/addXf3_test.zig +++ b/lib/compiler_rt/addf3_test.zig @@ -7,7 +7,7 @@ const std = @import("std"); const math = std.math; const qnan128 = @bitCast(f128, @as(u128, 0x7fff800000000000) << 64); -const __addtf3 = @import("addXf3.zig").__addtf3; +const __addtf3 = @import("addf3.zig").__addtf3; fn test__addtf3(a: f128, b: f128, expected_hi: u64, expected_lo: u64) !void { const x = __addtf3(a, b); @@ -48,7 +48,7 @@ test "addtf3" { try test__addtf3(0x1.edcba52449872455634654321fp-1, 0x1.23456734245345543849abcdefp+5, 0x40042afc95c8b579, 0x61e58dd6c51eb77c); } -const __subtf3 = @import("addXf3.zig").__subtf3; +const __subtf3 = @import("addf3.zig").__subtf3; fn test__subtf3(a: f128, b: f128, expected_hi: u64, expected_lo: u64) !void { const x = __subtf3(a, b); @@ -87,7 +87,7 @@ test "subtf3" { try test__subtf3(0x1.ee9d7c52354a6936ab8d7654321fp-1, 0x1.234567829a3bcdef5678ade36734p+5, 0xc0041b8af1915166, 0xa44a7bca780a166c); } -const __addxf3 = @import("addXf3.zig").__addxf3; +const __addxf3 = @import("addf3.zig").__addxf3; const qnan80 = @bitCast(f80, @bitCast(u80, math.nan(f80)) | (1 << (math.floatFractionalBits(f80) - 1))); fn test__addxf3(a: f80, b: f80, expected: u80) !void { diff --git a/lib/compiler_rt/addsf3.zig b/lib/compiler_rt/addsf3.zig new file mode 100644 index 0000000000..83f8285371 --- /dev/null +++ b/lib/compiler_rt/addsf3.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const addf3 = @import("./addf3.zig").addf3; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_fadd, .{ .name = "__aeabi_fadd", .linkage = common.linkage }); + } else { + @export(__addsf3, .{ .name = "__addsf3", .linkage = common.linkage }); + } +} + +fn __addsf3(a: f32, b: f32) callconv(.C) f32 { + return addf3(f32, a, b); +} + +fn __aeabi_fadd(a: f32, b: f32) callconv(.AAPCS) f32 { + return addf3(f32, a, b); +} diff --git a/lib/compiler_rt/addtf3.zig b/lib/compiler_rt/addtf3.zig new file mode 100644 index 0000000000..40a6e6c8b7 --- /dev/null +++ b/lib/compiler_rt/addtf3.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const addf3 = @import("./addf3.zig").addf3; + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__addkf3, .{ .name = "__addkf3", .linkage = common.linkage }); + } else { + @export(__addtf3, .{ .name = "__addtf3", .linkage = common.linkage }); + } +} + +fn __addtf3(a: f128, b: f128) callconv(.C) f128 { + return addf3(f128, a, b); +} + +fn __addkf3(a: f128, b: f128) callconv(.C) f128 { + return addf3(f128, a, b); +} diff --git a/lib/compiler_rt/addxf3.zig b/lib/compiler_rt/addxf3.zig new file mode 100644 index 0000000000..67e7dd8491 --- /dev/null +++ b/lib/compiler_rt/addxf3.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const addf3 = @import("./addf3.zig").addf3; + +pub const panic = common.panic; + +comptime { + @export(__addxf3, .{ .name = "__addxf3", .linkage = common.linkage }); +} + +fn __addxf3(a: f80, b: f80) callconv(.C) f80 { + return addf3(f80, a, b); +} diff --git a/lib/compiler_rt/cmpdf2.zig b/lib/compiler_rt/cmpdf2.zig new file mode 100644 index 0000000000..f1661731fb --- /dev/null +++ b/lib/compiler_rt/cmpdf2.zig @@ -0,0 +1,68 @@ +///! The quoted behavior definitions are from +///! https://gcc.gnu.org/onlinedocs/gcc-12.1.0/gccint/Soft-float-library-routines.html#Soft-float-library-routines +const common = @import("./common.zig"); +const comparef = @import("./comparef.zig"); + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_dcmpeq, .{ .name = "__aeabi_dcmpeq", .linkage = common.linkage }); + @export(__aeabi_dcmplt, .{ .name = "__aeabi_dcmplt", .linkage = common.linkage }); + @export(__aeabi_dcmple, .{ .name = "__aeabi_dcmple", .linkage = common.linkage }); + } else { + @export(__eqdf2, .{ .name = "__eqdf2", .linkage = common.linkage }); + @export(__nedf2, .{ .name = "__nedf2", .linkage = common.linkage }); + @export(__ledf2, .{ .name = "__ledf2", .linkage = common.linkage }); + @export(__cmpdf2, .{ .name = "__cmpdf2", .linkage = common.linkage }); + @export(__ltdf2, .{ .name = "__ltdf2", .linkage = common.linkage }); + } +} + +/// "These functions calculate a <=> b. That is, if a is less than b, they return -1; +/// if a is greater than b, they return 1; and if a and b are equal they return 0. +/// If either argument is NaN they return 1..." +/// +/// Note that this matches the definition of `__ledf2`, `__eqdf2`, `__nedf2`, `__cmpdf2`, +/// and `__ltdf2`. +fn __cmpdf2(a: f64, b: f64) callconv(.C) i32 { + return @enumToInt(comparef.cmpf2(f64, comparef.LE, a, b)); +} + +/// "These functions return a value less than or equal to zero if neither argument is NaN, +/// and a is less than or equal to b." +fn __ledf2(a: f64, b: f64) callconv(.C) i32 { + return __cmpdf2(a, b); +} + +/// "These functions return zero if neither argument is NaN, and a and b are equal." +/// Note that due to some kind of historical accident, __eqdf2 and __nedf2 are defined +/// to have the same return value. +fn __eqdf2(a: f64, b: f64) callconv(.C) i32 { + return __cmpdf2(a, b); +} + +/// "These functions return a nonzero value if either argument is NaN, or if a and b are unequal." +/// Note that due to some kind of historical accident, __eqdf2 and __nedf2 are defined +/// to have the same return value. +fn __nedf2(a: f64, b: f64) callconv(.C) i32 { + return __cmpdf2(a, b); +} + +/// "These functions return a value less than zero if neither argument is NaN, and a +/// is strictly less than b." +fn __ltdf2(a: f64, b: f64) callconv(.C) i32 { + return __cmpdf2(a, b); +} + +fn __aeabi_dcmpeq(a: f64, b: f64) callconv(.AAPCS) i32 { + return @boolToInt(comparef.cmpf2(f64, comparef.LE, a, b) == .Equal); +} + +fn __aeabi_dcmplt(a: f64, b: f64) callconv(.AAPCS) i32 { + return @boolToInt(comparef.cmpf2(f64, comparef.LE, a, b) == .Less); +} + +fn __aeabi_dcmple(a: f64, b: f64) callconv(.AAPCS) i32 { + return @boolToInt(comparef.cmpf2(f64, comparef.LE, a, b) != .Greater); +} diff --git a/lib/compiler_rt/cmpsf2.zig b/lib/compiler_rt/cmpsf2.zig new file mode 100644 index 0000000000..23be99c3f7 --- /dev/null +++ b/lib/compiler_rt/cmpsf2.zig @@ -0,0 +1,68 @@ +///! The quoted behavior definitions are from +///! https://gcc.gnu.org/onlinedocs/gcc-12.1.0/gccint/Soft-float-library-routines.html#Soft-float-library-routines +const common = @import("./common.zig"); +const comparef = @import("./comparef.zig"); + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_fcmpeq, .{ .name = "__aeabi_fcmpeq", .linkage = common.linkage }); + @export(__aeabi_fcmplt, .{ .name = "__aeabi_fcmplt", .linkage = common.linkage }); + @export(__aeabi_fcmple, .{ .name = "__aeabi_fcmple", .linkage = common.linkage }); + } else { + @export(__eqsf2, .{ .name = "__eqsf2", .linkage = common.linkage }); + @export(__nesf2, .{ .name = "__nesf2", .linkage = common.linkage }); + @export(__lesf2, .{ .name = "__lesf2", .linkage = common.linkage }); + @export(__cmpsf2, .{ .name = "__cmpsf2", .linkage = common.linkage }); + @export(__ltsf2, .{ .name = "__ltsf2", .linkage = common.linkage }); + } +} + +/// "These functions calculate a <=> b. That is, if a is less than b, they return -1; +/// if a is greater than b, they return 1; and if a and b are equal they return 0. +/// If either argument is NaN they return 1..." +/// +/// Note that this matches the definition of `__lesf2`, `__eqsf2`, `__nesf2`, `__cmpsf2`, +/// and `__ltsf2`. +fn __cmpsf2(a: f32, b: f32) callconv(.C) i32 { + return @enumToInt(comparef.cmpf2(f32, comparef.LE, a, b)); +} + +/// "These functions return a value less than or equal to zero if neither argument is NaN, +/// and a is less than or equal to b." +fn __lesf2(a: f32, b: f32) callconv(.C) i32 { + return __cmpsf2(a, b); +} + +/// "These functions return zero if neither argument is NaN, and a and b are equal." +/// Note that due to some kind of historical accident, __eqsf2 and __nesf2 are defined +/// to have the same return value. +fn __eqsf2(a: f32, b: f32) callconv(.C) i32 { + return __cmpsf2(a, b); +} + +/// "These functions return a nonzero value if either argument is NaN, or if a and b are unequal." +/// Note that due to some kind of historical accident, __eqsf2 and __nesf2 are defined +/// to have the same return value. +fn __nesf2(a: f32, b: f32) callconv(.C) i32 { + return __cmpsf2(a, b); +} + +/// "These functions return a value less than zero if neither argument is NaN, and a +/// is strictly less than b." +fn __ltsf2(a: f32, b: f32) callconv(.C) i32 { + return __cmpsf2(a, b); +} + +fn __aeabi_fcmpeq(a: f32, b: f32) callconv(.AAPCS) i32 { + return @boolToInt(comparef.cmpf2(f32, comparef.LE, a, b) == .Equal); +} + +fn __aeabi_fcmplt(a: f32, b: f32) callconv(.AAPCS) i32 { + return @boolToInt(comparef.cmpf2(f32, comparef.LE, a, b) == .Less); +} + +fn __aeabi_fcmple(a: f32, b: f32) callconv(.AAPCS) i32 { + return @boolToInt(comparef.cmpf2(f32, comparef.LE, a, b) != .Greater); +} diff --git a/lib/compiler_rt/cmptf2.zig b/lib/compiler_rt/cmptf2.zig new file mode 100644 index 0000000000..86dc68ad56 --- /dev/null +++ b/lib/compiler_rt/cmptf2.zig @@ -0,0 +1,73 @@ +///! The quoted behavior definitions are from +///! https://gcc.gnu.org/onlinedocs/gcc-12.1.0/gccint/Soft-float-library-routines.html#Soft-float-library-routines +const common = @import("./common.zig"); +const comparef = @import("./comparef.zig"); + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__eqkf2, .{ .name = "__eqkf2", .linkage = common.linkage }); + @export(__nekf2, .{ .name = "__nekf2", .linkage = common.linkage }); + @export(__ltkf2, .{ .name = "__ltkf2", .linkage = common.linkage }); + @export(__lekf2, .{ .name = "__lekf2", .linkage = common.linkage }); + } else { + @export(__eqtf2, .{ .name = "__eqtf2", .linkage = common.linkage }); + @export(__netf2, .{ .name = "__netf2", .linkage = common.linkage }); + @export(__letf2, .{ .name = "__letf2", .linkage = common.linkage }); + @export(__cmptf2, .{ .name = "__cmptf2", .linkage = common.linkage }); + @export(__lttf2, .{ .name = "__lttf2", .linkage = common.linkage }); + } +} + +/// "These functions calculate a <=> b. That is, if a is less than b, they return -1; +/// if a is greater than b, they return 1; and if a and b are equal they return 0. +/// If either argument is NaN they return 1..." +/// +/// Note that this matches the definition of `__letf2`, `__eqtf2`, `__netf2`, `__cmptf2`, +/// and `__lttf2`. +fn __cmptf2(a: f128, b: f128) callconv(.C) i32 { + return @enumToInt(comparef.cmpf2(f128, comparef.LE, a, b)); +} + +/// "These functions return a value less than or equal to zero if neither argument is NaN, +/// and a is less than or equal to b." +fn __letf2(a: f128, b: f128) callconv(.C) i32 { + return __cmptf2(a, b); +} + +/// "These functions return zero if neither argument is NaN, and a and b are equal." +/// Note that due to some kind of historical accident, __eqtf2 and __netf2 are defined +/// to have the same return value. +fn __eqtf2(a: f128, b: f128) callconv(.C) i32 { + return __cmptf2(a, b); +} + +/// "These functions return a nonzero value if either argument is NaN, or if a and b are unequal." +/// Note that due to some kind of historical accident, __eqtf2 and __netf2 are defined +/// to have the same return value. +fn __netf2(a: f128, b: f128) callconv(.C) i32 { + return __cmptf2(a, b); +} + +/// "These functions return a value less than zero if neither argument is NaN, and a +/// is strictly less than b." +fn __lttf2(a: f128, b: f128) callconv(.C) i32 { + return __cmptf2(a, b); +} + +fn __eqkf2(a: f128, b: f128) callconv(.C) i32 { + return __cmptf2(a, b); +} + +fn __nekf2(a: f128, b: f128) callconv(.C) i32 { + return __cmptf2(a, b); +} + +fn __ltkf2(a: f128, b: f128) callconv(.C) i32 { + return __cmptf2(a, b); +} + +fn __lekf2(a: f128, b: f128) callconv(.C) i32 { + return __cmptf2(a, b); +} diff --git a/lib/compiler_rt/cmpxf2.zig b/lib/compiler_rt/cmpxf2.zig new file mode 100644 index 0000000000..7286316f99 --- /dev/null +++ b/lib/compiler_rt/cmpxf2.zig @@ -0,0 +1,50 @@ +///! The quoted behavior definitions are from +///! https://gcc.gnu.org/onlinedocs/gcc-12.1.0/gccint/Soft-float-library-routines.html#Soft-float-library-routines +const common = @import("./common.zig"); +const comparef = @import("./comparef.zig"); + +pub const panic = common.panic; + +comptime { + @export(__eqxf2, .{ .name = "__eqxf2", .linkage = common.linkage }); + @export(__nexf2, .{ .name = "__nexf2", .linkage = common.linkage }); + @export(__lexf2, .{ .name = "__lexf2", .linkage = common.linkage }); + @export(__cmpxf2, .{ .name = "__cmpxf2", .linkage = common.linkage }); + @export(__ltxf2, .{ .name = "__ltxf2", .linkage = common.linkage }); +} + +/// "These functions calculate a <=> b. That is, if a is less than b, they return -1; +/// if a is greater than b, they return 1; and if a and b are equal they return 0. +/// If either argument is NaN they return 1..." +/// +/// Note that this matches the definition of `__lexf2`, `__eqxf2`, `__nexf2`, `__cmpxf2`, +/// and `__ltxf2`. +fn __cmpxf2(a: f80, b: f80) callconv(.C) i32 { + return @enumToInt(comparef.cmp_f80(comparef.LE, a, b)); +} + +/// "These functions return a value less than or equal to zero if neither argument is NaN, +/// and a is less than or equal to b." +fn __lexf2(a: f80, b: f80) callconv(.C) i32 { + return __cmpxf2(a, b); +} + +/// "These functions return zero if neither argument is NaN, and a and b are equal." +/// Note that due to some kind of historical accident, __eqxf2 and __nexf2 are defined +/// to have the same return value. +fn __eqxf2(a: f80, b: f80) callconv(.C) i32 { + return __cmpxf2(a, b); +} + +/// "These functions return a nonzero value if either argument is NaN, or if a and b are unequal." +/// Note that due to some kind of historical accident, __eqxf2 and __nexf2 are defined +/// to have the same return value. +fn __nexf2(a: f80, b: f80) callconv(.C) i32 { + return __cmpxf2(a, b); +} + +/// "These functions return a value less than zero if neither argument is NaN, and a +/// is strictly less than b." +fn __ltxf2(a: f80, b: f80) callconv(.C) i32 { + return __cmpxf2(a, b); +} diff --git a/lib/compiler_rt/common.zig b/lib/compiler_rt/common.zig index 7e9bdf81c8..196dc33024 100644 --- a/lib/compiler_rt/common.zig +++ b/lib/compiler_rt/common.zig @@ -3,8 +3,14 @@ const builtin = @import("builtin"); const math = std.math; const is_test = builtin.is_test; +pub const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; +pub const want_aeabi = builtin.cpu.arch.isARM() or builtin.cpu.arch.isThumb(); +pub const want_ppc_abi = builtin.cpu.arch.isPPC() or builtin.cpu.arch.isPPC64(); +pub const want_msvc_abi = builtin.abi == .msvc; +pub const want_gnu_abi = builtin.abi.isGnu(); + // Avoid dragging in the runtime safety mechanisms into this .o file, -// unless we're trying to test this file. +// unless we're trying to test compiler-rt. pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn { _ = error_return_trace; @setCold(true); @@ -15,8 +21,13 @@ pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) nore } } +/// AArch64 is the only ABI (at the moment) to support f16 arguments without the +/// need for extending them to wider fp types. +/// TODO remove this; do this type selection in the language rather than +/// here in compiler-rt. +pub const F16T = if (builtin.cpu.arch.isAARCH64()) f16 else u16; + pub fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { - @setRuntimeSafety(is_test); switch (Z) { u16 => { // 16x16 --> 32 bit multiply @@ -130,15 +141,11 @@ pub fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { } } -// TODO: restore inline keyword, see: https://github.com/ziglang/zig/issues/2154 pub fn normalize(comptime T: type, significand: *std.meta.Int(.unsigned, @typeInfo(T).Float.bits)) i32 { - const bits = @typeInfo(T).Float.bits; - const Z = std.meta.Int(.unsigned, bits); - const S = std.meta.Int(.unsigned, bits - @clz(Z, @as(Z, bits) - 1)); - const fractionalBits = math.floatFractionalBits(T); - const integerBit = @as(Z, 1) << fractionalBits; + const Z = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); + const integerBit = @as(Z, 1) << std.math.floatFractionalBits(T); - const shift = @clz(std.meta.Int(.unsigned, bits), significand.*) - @clz(Z, integerBit); - significand.* <<= @intCast(S, shift); + const shift = @clz(Z, significand.*) - @clz(Z, integerBit); + significand.* <<= @intCast(std.math.Log2Int(Z), shift); return @as(i32, 1) - shift; } diff --git a/lib/compiler_rt/compareXf2.zig b/lib/compiler_rt/compareXf2.zig deleted file mode 100644 index d21a6777e4..0000000000 --- a/lib/compiler_rt/compareXf2.zig +++ /dev/null @@ -1,440 +0,0 @@ -// Ported from: -// -// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/lib/builtins/comparesf2.c - -const std = @import("std"); -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const arch = builtin.cpu.arch; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; - -comptime { - @export(__lesf2, .{ .name = "__lesf2", .linkage = linkage }); - @export(__ledf2, .{ .name = "__ledf2", .linkage = linkage }); - @export(__letf2, .{ .name = "__letf2", .linkage = linkage }); - @export(__lexf2, .{ .name = "__lexf2", .linkage = linkage }); - - @export(__gesf2, .{ .name = "__gesf2", .linkage = linkage }); - @export(__gedf2, .{ .name = "__gedf2", .linkage = linkage }); - @export(__getf2, .{ .name = "__getf2", .linkage = linkage }); - @export(__gexf2, .{ .name = "__gexf2", .linkage = linkage }); - - @export(__eqsf2, .{ .name = "__eqsf2", .linkage = linkage }); - @export(__eqdf2, .{ .name = "__eqdf2", .linkage = linkage }); - @export(__eqxf2, .{ .name = "__eqxf2", .linkage = linkage }); - - @export(__ltsf2, .{ .name = "__ltsf2", .linkage = linkage }); - @export(__ltdf2, .{ .name = "__ltdf2", .linkage = linkage }); - @export(__ltxf2, .{ .name = "__ltxf2", .linkage = linkage }); - - @export(__nesf2, .{ .name = "__nesf2", .linkage = linkage }); - @export(__nedf2, .{ .name = "__nedf2", .linkage = linkage }); - @export(__nexf2, .{ .name = "__nexf2", .linkage = linkage }); - - @export(__gtsf2, .{ .name = "__gtsf2", .linkage = linkage }); - @export(__gtdf2, .{ .name = "__gtdf2", .linkage = linkage }); - @export(__gtxf2, .{ .name = "__gtxf2", .linkage = linkage }); - - @export(__unordsf2, .{ .name = "__unordsf2", .linkage = linkage }); - @export(__unorddf2, .{ .name = "__unorddf2", .linkage = linkage }); - @export(__unordtf2, .{ .name = "__unordtf2", .linkage = linkage }); - - if (!is_test) { - @export(__cmpsf2, .{ .name = "__cmpsf2", .linkage = linkage }); - @export(__cmpdf2, .{ .name = "__cmpdf2", .linkage = linkage }); - @export(__cmptf2, .{ .name = "__cmptf2", .linkage = linkage }); - @export(__eqtf2, .{ .name = "__eqtf2", .linkage = linkage }); - @export(__lttf2, .{ .name = "__lttf2", .linkage = linkage }); - @export(__gttf2, .{ .name = "__gttf2", .linkage = linkage }); - @export(__netf2, .{ .name = "__netf2", .linkage = linkage }); - - if (arch.isARM() or arch.isThumb()) { - @export(__aeabi_fcmpeq, .{ .name = "__aeabi_fcmpeq", .linkage = linkage }); - @export(__aeabi_fcmplt, .{ .name = "__aeabi_fcmplt", .linkage = linkage }); - @export(__aeabi_fcmple, .{ .name = "__aeabi_fcmple", .linkage = linkage }); - @export(__aeabi_fcmpge, .{ .name = "__aeabi_fcmpge", .linkage = linkage }); - @export(__aeabi_fcmpgt, .{ .name = "__aeabi_fcmpgt", .linkage = linkage }); - @export(__aeabi_fcmpun, .{ .name = "__aeabi_fcmpun", .linkage = linkage }); - - @export(__aeabi_dcmpeq, .{ .name = "__aeabi_dcmpeq", .linkage = linkage }); - @export(__aeabi_dcmplt, .{ .name = "__aeabi_dcmplt", .linkage = linkage }); - @export(__aeabi_dcmple, .{ .name = "__aeabi_dcmple", .linkage = linkage }); - @export(__aeabi_dcmpge, .{ .name = "__aeabi_dcmpge", .linkage = linkage }); - @export(__aeabi_dcmpgt, .{ .name = "__aeabi_dcmpgt", .linkage = linkage }); - @export(__aeabi_dcmpun, .{ .name = "__aeabi_dcmpun", .linkage = linkage }); - } - - if (arch.isPPC() or arch.isPPC64()) { - @export(__eqkf2, .{ .name = "__eqkf2", .linkage = linkage }); - @export(__nekf2, .{ .name = "__nekf2", .linkage = linkage }); - @export(__gekf2, .{ .name = "__gekf2", .linkage = linkage }); - @export(__ltkf2, .{ .name = "__ltkf2", .linkage = linkage }); - @export(__lekf2, .{ .name = "__lekf2", .linkage = linkage }); - @export(__gtkf2, .{ .name = "__gtkf2", .linkage = linkage }); - @export(__unordkf2, .{ .name = "__unordkf2", .linkage = linkage }); - } - } -} - -const LE = enum(i32) { - Less = -1, - Equal = 0, - Greater = 1, - - const Unordered: LE = .Greater; -}; - -const GE = enum(i32) { - Less = -1, - Equal = 0, - Greater = 1, - - const Unordered: GE = .Less; -}; - -pub inline fn cmp(comptime T: type, comptime RT: type, a: T, b: T) RT { - @setRuntimeSafety(builtin.is_test); - - const bits = @typeInfo(T).Float.bits; - const srep_t = std.meta.Int(.signed, bits); - const rep_t = std.meta.Int(.unsigned, bits); - - const significandBits = std.math.floatMantissaBits(T); - const exponentBits = std.math.floatExponentBits(T); - const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); - const absMask = signBit - 1; - const infT = comptime std.math.inf(T); - const infRep = @bitCast(rep_t, infT); - - const aInt = @bitCast(srep_t, a); - const bInt = @bitCast(srep_t, b); - const aAbs = @bitCast(rep_t, aInt) & absMask; - const bAbs = @bitCast(rep_t, bInt) & absMask; - - // If either a or b is NaN, they are unordered. - if (aAbs > infRep or bAbs > infRep) return RT.Unordered; - - // If a and b are both zeros, they are equal. - if ((aAbs | bAbs) == 0) return .Equal; - - // If at least one of a and b is positive, we get the same result comparing - // a and b as signed integers as we would with a floating-point compare. - if ((aInt & bInt) >= 0) { - if (aInt < bInt) { - return .Less; - } else if (aInt == bInt) { - return .Equal; - } else return .Greater; - } else { - // Otherwise, both are negative, so we need to flip the sense of the - // comparison to get the correct result. (This assumes a twos- or ones- - // complement integer representation; if integers are represented in a - // sign-magnitude representation, then this flip is incorrect). - if (aInt > bInt) { - return .Less; - } else if (aInt == bInt) { - return .Equal; - } else return .Greater; - } -} - -pub inline fn unordcmp(comptime T: type, a: T, b: T) i32 { - @setRuntimeSafety(builtin.is_test); - - const rep_t = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); - - const significandBits = std.math.floatMantissaBits(T); - const exponentBits = std.math.floatExponentBits(T); - const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); - const absMask = signBit - 1; - const infRep = @bitCast(rep_t, std.math.inf(T)); - - const aAbs: rep_t = @bitCast(rep_t, a) & absMask; - const bAbs: rep_t = @bitCast(rep_t, b) & absMask; - - return @boolToInt(aAbs > infRep or bAbs > infRep); -} - -// Comparison between f32 - -pub fn __lesf2(a: f32, b: f32) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - const float = cmp(f32, LE, a, b); - return @bitCast(i32, float); -} - -pub fn __gesf2(a: f32, b: f32) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - const float = cmp(f32, GE, a, b); - return @bitCast(i32, float); -} - -pub fn __cmpsf2(a: f32, b: f32) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __lesf2, .{ a, b }); -} - -pub fn __eqsf2(a: f32, b: f32) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __lesf2, .{ a, b }); -} - -pub fn __ltsf2(a: f32, b: f32) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __lesf2, .{ a, b }); -} - -pub fn __nesf2(a: f32, b: f32) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __lesf2, .{ a, b }); -} - -pub fn __gtsf2(a: f32, b: f32) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __gesf2, .{ a, b }); -} - -// Comparison between f64 - -pub fn __ledf2(a: f64, b: f64) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - const float = cmp(f64, LE, a, b); - return @bitCast(i32, float); -} - -pub fn __gedf2(a: f64, b: f64) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - const float = cmp(f64, GE, a, b); - return @bitCast(i32, float); -} - -pub fn __cmpdf2(a: f64, b: f64) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __ledf2, .{ a, b }); -} - -pub fn __eqdf2(a: f64, b: f64) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __ledf2, .{ a, b }); -} - -pub fn __ltdf2(a: f64, b: f64) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __ledf2, .{ a, b }); -} - -pub fn __nedf2(a: f64, b: f64) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __ledf2, .{ a, b }); -} - -pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __gedf2, .{ a, b }); -} - -// Comparison between f80 - -pub inline fn cmp_f80(comptime RT: type, a: f80, b: f80) RT { - const a_rep = std.math.break_f80(a); - const b_rep = std.math.break_f80(b); - const sig_bits = std.math.floatMantissaBits(f80); - const int_bit = 0x8000000000000000; - const sign_bit = 0x8000; - const special_exp = 0x7FFF; - - // If either a or b is NaN, they are unordered. - if ((a_rep.exp & special_exp == special_exp and a_rep.fraction ^ int_bit != 0) or - (b_rep.exp & special_exp == special_exp and b_rep.fraction ^ int_bit != 0)) - return RT.Unordered; - - // If a and b are both zeros, they are equal. - if ((a_rep.fraction | b_rep.fraction) | ((a_rep.exp | b_rep.exp) & special_exp) == 0) - return .Equal; - - if (@boolToInt(a_rep.exp == b_rep.exp) & @boolToInt(a_rep.fraction == b_rep.fraction) != 0) { - return .Equal; - } else if (a_rep.exp & sign_bit != b_rep.exp & sign_bit) { - // signs are different - if (@bitCast(i16, a_rep.exp) < @bitCast(i16, b_rep.exp)) { - return .Less; - } else { - return .Greater; - } - } else { - const a_fraction = a_rep.fraction | (@as(u80, a_rep.exp) << sig_bits); - const b_fraction = b_rep.fraction | (@as(u80, b_rep.exp) << sig_bits); - if (a_fraction < b_fraction) { - return .Less; - } else { - return .Greater; - } - } -} - -pub fn __lexf2(a: f80, b: f80) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - const float = cmp_f80(LE, a, b); - return @bitCast(i32, float); -} - -pub fn __gexf2(a: f80, b: f80) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - const float = cmp_f80(GE, a, b); - return @bitCast(i32, float); -} - -pub fn __eqxf2(a: f80, b: f80) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __lexf2, .{ a, b }); -} - -pub fn __ltxf2(a: f80, b: f80) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __lexf2, .{ a, b }); -} - -pub fn __nexf2(a: f80, b: f80) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __lexf2, .{ a, b }); -} - -pub fn __gtxf2(a: f80, b: f80) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __gexf2, .{ a, b }); -} - -// Comparison between f128 - -pub fn __letf2(a: f128, b: f128) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - const float = cmp(f128, LE, a, b); - return @bitCast(i32, float); -} - -pub fn __getf2(a: f128, b: f128) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - const float = cmp(f128, GE, a, b); - return @bitCast(i32, float); -} - -pub fn __cmptf2(a: f128, b: f128) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __letf2, .{ a, b }); -} - -pub fn __eqtf2(a: f128, b: f128) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __letf2, .{ a, b }); -} - -pub fn __lttf2(a: f128, b: f128) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __letf2, .{ a, b }); -} - -pub fn __netf2(a: f128, b: f128) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __letf2, .{ a, b }); -} - -pub fn __gttf2(a: f128, b: f128) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __getf2, .{ a, b }); -} - -pub fn __eqkf2(a: f128, b: f128) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __letf2, .{ a, b }); -} - -pub fn __nekf2(a: f128, b: f128) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __letf2, .{ a, b }); -} - -pub fn __gekf2(a: f128, b: f128) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __getf2, .{ a, b }); -} - -pub fn __ltkf2(a: f128, b: f128) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __letf2, .{ a, b }); -} - -pub fn __lekf2(a: f128, b: f128) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __letf2, .{ a, b }); -} - -pub fn __gtkf2(a: f128, b: f128) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __getf2, .{ a, b }); -} - -// Unordered comparison between f32/f64/f128 - -pub fn __unordsf2(a: f32, b: f32) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - return unordcmp(f32, a, b); -} - -pub fn __unorddf2(a: f64, b: f64) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - return unordcmp(f64, a, b); -} - -pub fn __unordtf2(a: f128, b: f128) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - return unordcmp(f128, a, b); -} - -pub fn __unordkf2(a: f128, b: f128) callconv(.C) i32 { - return @call(.{ .modifier = .always_inline }, __unordtf2, .{ a, b }); -} - -// ARM EABI intrinsics - -pub fn __aeabi_fcmpeq(a: f32, b: f32) callconv(.AAPCS) i32 { - @setRuntimeSafety(false); - return @boolToInt(@call(.{ .modifier = .always_inline }, __eqsf2, .{ a, b }) == 0); -} - -pub fn __aeabi_fcmplt(a: f32, b: f32) callconv(.AAPCS) i32 { - @setRuntimeSafety(false); - return @boolToInt(@call(.{ .modifier = .always_inline }, __ltsf2, .{ a, b }) < 0); -} - -pub fn __aeabi_fcmple(a: f32, b: f32) callconv(.AAPCS) i32 { - @setRuntimeSafety(false); - return @boolToInt(@call(.{ .modifier = .always_inline }, __lesf2, .{ a, b }) <= 0); -} - -pub fn __aeabi_fcmpge(a: f32, b: f32) callconv(.AAPCS) i32 { - @setRuntimeSafety(false); - return @boolToInt(@call(.{ .modifier = .always_inline }, __gesf2, .{ a, b }) >= 0); -} - -pub fn __aeabi_fcmpgt(a: f32, b: f32) callconv(.AAPCS) i32 { - @setRuntimeSafety(false); - return @boolToInt(@call(.{ .modifier = .always_inline }, __gtsf2, .{ a, b }) > 0); -} - -pub fn __aeabi_fcmpun(a: f32, b: f32) callconv(.AAPCS) i32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __unordsf2, .{ a, b }); -} - -pub fn __aeabi_dcmpeq(a: f64, b: f64) callconv(.AAPCS) i32 { - @setRuntimeSafety(false); - return @boolToInt(@call(.{ .modifier = .always_inline }, __eqdf2, .{ a, b }) == 0); -} - -pub fn __aeabi_dcmplt(a: f64, b: f64) callconv(.AAPCS) i32 { - @setRuntimeSafety(false); - return @boolToInt(@call(.{ .modifier = .always_inline }, __ltdf2, .{ a, b }) < 0); -} - -pub fn __aeabi_dcmple(a: f64, b: f64) callconv(.AAPCS) i32 { - @setRuntimeSafety(false); - return @boolToInt(@call(.{ .modifier = .always_inline }, __ledf2, .{ a, b }) <= 0); -} - -pub fn __aeabi_dcmpge(a: f64, b: f64) callconv(.AAPCS) i32 { - @setRuntimeSafety(false); - return @boolToInt(@call(.{ .modifier = .always_inline }, __gedf2, .{ a, b }) >= 0); -} - -pub fn __aeabi_dcmpgt(a: f64, b: f64) callconv(.AAPCS) i32 { - @setRuntimeSafety(false); - return @boolToInt(@call(.{ .modifier = .always_inline }, __gtdf2, .{ a, b }) > 0); -} - -pub fn __aeabi_dcmpun(a: f64, b: f64) callconv(.AAPCS) i32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __unorddf2, .{ a, b }); -} - -test "comparesf2" { - _ = @import("comparesf2_test.zig"); -} -test "comparedf2" { - _ = @import("comparedf2_test.zig"); -} diff --git a/lib/compiler_rt/comparef.zig b/lib/compiler_rt/comparef.zig new file mode 100644 index 0000000000..1fb6d2dfa0 --- /dev/null +++ b/lib/compiler_rt/comparef.zig @@ -0,0 +1,118 @@ +const std = @import("std"); + +pub const LE = enum(i32) { + Less = -1, + Equal = 0, + Greater = 1, + + const Unordered: LE = .Greater; +}; + +pub const GE = enum(i32) { + Less = -1, + Equal = 0, + Greater = 1, + + const Unordered: GE = .Less; +}; + +pub inline fn cmpf2(comptime T: type, comptime RT: type, a: T, b: T) RT { + const bits = @typeInfo(T).Float.bits; + const srep_t = std.meta.Int(.signed, bits); + const rep_t = std.meta.Int(.unsigned, bits); + + const significandBits = std.math.floatMantissaBits(T); + const exponentBits = std.math.floatExponentBits(T); + const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); + const absMask = signBit - 1; + const infT = comptime std.math.inf(T); + const infRep = @bitCast(rep_t, infT); + + const aInt = @bitCast(srep_t, a); + const bInt = @bitCast(srep_t, b); + const aAbs = @bitCast(rep_t, aInt) & absMask; + const bAbs = @bitCast(rep_t, bInt) & absMask; + + // If either a or b is NaN, they are unordered. + if (aAbs > infRep or bAbs > infRep) return RT.Unordered; + + // If a and b are both zeros, they are equal. + if ((aAbs | bAbs) == 0) return .Equal; + + // If at least one of a and b is positive, we get the same result comparing + // a and b as signed integers as we would with a floating-point compare. + if ((aInt & bInt) >= 0) { + if (aInt < bInt) { + return .Less; + } else if (aInt == bInt) { + return .Equal; + } else return .Greater; + } else { + // Otherwise, both are negative, so we need to flip the sense of the + // comparison to get the correct result. (This assumes a twos- or ones- + // complement integer representation; if integers are represented in a + // sign-magnitude representation, then this flip is incorrect). + if (aInt > bInt) { + return .Less; + } else if (aInt == bInt) { + return .Equal; + } else return .Greater; + } +} + +pub inline fn cmp_f80(comptime RT: type, a: f80, b: f80) RT { + const a_rep = std.math.break_f80(a); + const b_rep = std.math.break_f80(b); + const sig_bits = std.math.floatMantissaBits(f80); + const int_bit = 0x8000000000000000; + const sign_bit = 0x8000; + const special_exp = 0x7FFF; + + // If either a or b is NaN, they are unordered. + if ((a_rep.exp & special_exp == special_exp and a_rep.fraction ^ int_bit != 0) or + (b_rep.exp & special_exp == special_exp and b_rep.fraction ^ int_bit != 0)) + return RT.Unordered; + + // If a and b are both zeros, they are equal. + if ((a_rep.fraction | b_rep.fraction) | ((a_rep.exp | b_rep.exp) & special_exp) == 0) + return .Equal; + + if (@boolToInt(a_rep.exp == b_rep.exp) & @boolToInt(a_rep.fraction == b_rep.fraction) != 0) { + return .Equal; + } else if (a_rep.exp & sign_bit != b_rep.exp & sign_bit) { + // signs are different + if (@bitCast(i16, a_rep.exp) < @bitCast(i16, b_rep.exp)) { + return .Less; + } else { + return .Greater; + } + } else { + const a_fraction = a_rep.fraction | (@as(u80, a_rep.exp) << sig_bits); + const b_fraction = b_rep.fraction | (@as(u80, b_rep.exp) << sig_bits); + if (a_fraction < b_fraction) { + return .Less; + } else { + return .Greater; + } + } +} + +pub inline fn unordcmp(comptime T: type, a: T, b: T) i32 { + const rep_t = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); + + const significandBits = std.math.floatMantissaBits(T); + const exponentBits = std.math.floatExponentBits(T); + const signBit = (@as(rep_t, 1) << (significandBits + exponentBits)); + const absMask = signBit - 1; + const infRep = @bitCast(rep_t, std.math.inf(T)); + + const aAbs: rep_t = @bitCast(rep_t, a) & absMask; + const bAbs: rep_t = @bitCast(rep_t, b) & absMask; + + return @boolToInt(aAbs > infRep or bAbs > infRep); +} + +test { + _ = @import("comparesf2_test.zig"); + _ = @import("comparedf2_test.zig"); +} diff --git a/lib/compiler_rt/extend_f80.zig b/lib/compiler_rt/extend_f80.zig deleted file mode 100644 index fe5a38fc68..0000000000 --- a/lib/compiler_rt/extend_f80.zig +++ /dev/null @@ -1,140 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const arch = builtin.cpu.arch; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; - -comptime { - @export(__extendhfxf2, .{ .name = "__extendhfxf2", .linkage = linkage }); - @export(__extendsfxf2, .{ .name = "__extendsfxf2", .linkage = linkage }); - @export(__extenddfxf2, .{ .name = "__extenddfxf2", .linkage = linkage }); - @export(__extendxftf2, .{ .name = "__extendxftf2", .linkage = linkage }); -} - -// AArch64 is the only ABI (at the moment) to support f16 arguments without the -// need for extending them to wider fp types. -const F16T = if (arch.isAARCH64()) f16 else u16; - -fn __extendhfxf2(a: F16T) callconv(.C) f80 { - return extendF80(f16, @bitCast(u16, a)); -} - -fn __extendsfxf2(a: f32) callconv(.C) f80 { - return extendF80(f32, @bitCast(u32, a)); -} - -fn __extenddfxf2(a: f64) callconv(.C) f80 { - return extendF80(f64, @bitCast(u64, a)); -} - -inline fn extendF80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits)) f80 { - @setRuntimeSafety(builtin.is_test); - - const src_rep_t = std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits); - const src_sig_bits = std.math.floatMantissaBits(src_t); - const dst_int_bit = 0x8000000000000000; - const dst_sig_bits = std.math.floatMantissaBits(f80) - 1; // -1 for the integer bit - - const dst_exp_bias = 16383; - - const src_bits = @bitSizeOf(src_t); - const src_exp_bits = src_bits - src_sig_bits - 1; - const src_inf_exp = (1 << src_exp_bits) - 1; - const src_exp_bias = src_inf_exp >> 1; - - const src_min_normal = 1 << src_sig_bits; - const src_inf = src_inf_exp << src_sig_bits; - const src_sign_mask = 1 << (src_sig_bits + src_exp_bits); - const src_abs_mask = src_sign_mask - 1; - const src_qnan = 1 << (src_sig_bits - 1); - const src_nan_code = src_qnan - 1; - - var dst: std.math.F80 = undefined; - - // Break a into a sign and representation of the absolute value - const a_abs = a & src_abs_mask; - const sign: u16 = if (a & src_sign_mask != 0) 0x8000 else 0; - - if (a_abs -% src_min_normal < src_inf - src_min_normal) { - // a is a normal number. - // Extend to the destination type by shifting the significand and - // exponent into the proper position and rebiasing the exponent. - dst.exp = @intCast(u16, a_abs >> src_sig_bits); - dst.exp += dst_exp_bias - src_exp_bias; - dst.fraction = @as(u64, a_abs) << (dst_sig_bits - src_sig_bits); - dst.fraction |= dst_int_bit; // bit 64 is always set for normal numbers - } else if (a_abs >= src_inf) { - // a is NaN or infinity. - // Conjure the result by beginning with infinity, then setting the qNaN - // bit (if needed) and right-aligning the rest of the trailing NaN - // payload field. - dst.exp = 0x7fff; - dst.fraction = dst_int_bit; - dst.fraction |= @as(u64, a_abs & src_qnan) << (dst_sig_bits - src_sig_bits); - dst.fraction |= @as(u64, a_abs & src_nan_code) << (dst_sig_bits - src_sig_bits); - } else if (a_abs != 0) { - // a is denormal. - // renormalize the significand and clear the leading bit, then insert - // the correct adjusted exponent in the destination type. - const scale: u16 = @clz(src_rep_t, a_abs) - - @clz(src_rep_t, @as(src_rep_t, src_min_normal)); - - dst.fraction = @as(u64, a_abs) << @intCast(u6, dst_sig_bits - src_sig_bits + scale); - dst.fraction |= dst_int_bit; // bit 64 is always set for normal numbers - dst.exp = @truncate(u16, a_abs >> @intCast(u4, src_sig_bits - scale)); - dst.exp ^= 1; - dst.exp |= dst_exp_bias - src_exp_bias - scale + 1; - } else { - // a is zero. - dst.exp = 0; - dst.fraction = 0; - } - - dst.exp |= sign; - return std.math.make_f80(dst); -} - -fn __extendxftf2(a: f80) callconv(.C) f128 { - @setRuntimeSafety(builtin.is_test); - - const src_int_bit: u64 = 0x8000000000000000; - const src_sig_mask = ~src_int_bit; - const src_sig_bits = std.math.floatMantissaBits(f80) - 1; // -1 for the integer bit - const dst_sig_bits = std.math.floatMantissaBits(f128); - - const dst_bits = @bitSizeOf(f128); - - const dst_min_normal = @as(u128, 1) << dst_sig_bits; - - // Break a into a sign and representation of the absolute value - var a_rep = std.math.break_f80(a); - const sign = a_rep.exp & 0x8000; - a_rep.exp &= 0x7FFF; - var abs_result: u128 = undefined; - - if (a_rep.exp == 0 and a_rep.fraction == 0) { - // zero - abs_result = 0; - } else if (a_rep.exp == 0x7FFF) { - // a is nan or infinite - abs_result = @as(u128, a_rep.fraction) << (dst_sig_bits - src_sig_bits); - abs_result |= @as(u128, a_rep.exp) << dst_sig_bits; - } else if (a_rep.fraction & src_int_bit != 0) { - // a is a normal value - abs_result = @as(u128, a_rep.fraction & src_sig_mask) << (dst_sig_bits - src_sig_bits); - abs_result |= @as(u128, a_rep.exp) << dst_sig_bits; - } else { - // a is denormal - // renormalize the significand and clear the leading bit and integer part, - // then insert the correct adjusted exponent in the destination type. - const scale: u32 = @clz(u64, a_rep.fraction); - abs_result = @as(u128, a_rep.fraction) << @intCast(u7, dst_sig_bits - src_sig_bits + scale + 1); - abs_result ^= dst_min_normal; - abs_result |= @as(u128, scale + 1) << dst_sig_bits; - } - - // Apply the signbit to (dst_t)abs(a). - const result: u128 align(@alignOf(f128)) = abs_result | @as(u128, sign) << (dst_bits - 16); - return @bitCast(f128, result); -} diff --git a/lib/compiler_rt/extenddftf2.zig b/lib/compiler_rt/extenddftf2.zig new file mode 100644 index 0000000000..c957d64a63 --- /dev/null +++ b/lib/compiler_rt/extenddftf2.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const extendf = @import("./extendf.zig").extendf; + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__extenddfkf2, .{ .name = "__extenddfkf2", .linkage = common.linkage }); + } else { + @export(__extenddftf2, .{ .name = "__extenddftf2", .linkage = common.linkage }); + } +} + +fn __extenddftf2(a: f64) callconv(.C) f128 { + return extendf(f128, f64, @bitCast(u64, a)); +} + +fn __extenddfkf2(a: f64) callconv(.C) f128 { + return extendf(f128, f64, @bitCast(u64, a)); +} diff --git a/lib/compiler_rt/extenddfxf2.zig b/lib/compiler_rt/extenddfxf2.zig new file mode 100644 index 0000000000..e76b2fc038 --- /dev/null +++ b/lib/compiler_rt/extenddfxf2.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const extend_f80 = @import("./extendf.zig").extend_f80; + +pub const panic = common.panic; + +comptime { + @export(__extenddfxf2, .{ .name = "__extenddfxf2", .linkage = common.linkage }); +} + +fn __extenddfxf2(a: f64) callconv(.C) f80 { + return extend_f80(f64, @bitCast(u64, a)); +} diff --git a/lib/compiler_rt/extendXfYf2.zig b/lib/compiler_rt/extendf.zig similarity index 51% rename from lib/compiler_rt/extendXfYf2.zig rename to lib/compiler_rt/extendf.zig index bb21e5f681..15fa1ce0e8 100644 --- a/lib/compiler_rt/extendXfYf2.zig +++ b/lib/compiler_rt/extendf.zig @@ -1,81 +1,10 @@ const std = @import("std"); -const builtin = @import("builtin"); -const is_test = builtin.is_test; -const arch = builtin.cpu.arch; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; - -comptime { - @export(__extenddftf2, .{ .name = "__extenddftf2", .linkage = linkage }); - @export(__extendsftf2, .{ .name = "__extendsftf2", .linkage = linkage }); - @export(__extendhfsf2, .{ .name = "__extendhfsf2", .linkage = linkage }); - @export(__extendhftf2, .{ .name = "__extendhftf2", .linkage = linkage }); - @export(__extendsfdf2, .{ .name = "__extendsfdf2", .linkage = linkage }); - - if (!is_test) { - @export(__gnu_h2f_ieee, .{ .name = "__gnu_h2f_ieee", .linkage = linkage }); - - if (arch.isARM() or arch.isThumb()) { - @export(__aeabi_f2d, .{ .name = "__aeabi_f2d", .linkage = linkage }); - @export(__aeabi_h2f, .{ .name = "__aeabi_h2f", .linkage = linkage }); - } - - if (arch.isPPC() or arch.isPPC64()) { - @export(__extendsfkf2, .{ .name = "__extendsfkf2", .linkage = linkage }); - @export(__extenddfkf2, .{ .name = "__extenddfkf2", .linkage = linkage }); - } - } -} - -pub fn __extendsfdf2(a: f32) callconv(.C) f64 { - return extendXfYf2(f64, f32, @bitCast(u32, a)); -} - -pub fn __extenddftf2(a: f64) callconv(.C) f128 { - return extendXfYf2(f128, f64, @bitCast(u64, a)); -} - -pub fn __extenddfkf2(a: f64) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, __extenddftf2, .{a}); -} - -pub fn __extendsftf2(a: f32) callconv(.C) f128 { - return extendXfYf2(f128, f32, @bitCast(u32, a)); -} - -pub fn __extendsfkf2(a: f32) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, __extendsftf2, .{a}); -} - -// AArch64 is the only ABI (at the moment) to support f16 arguments without the -// need for extending them to wider fp types. -pub const F16T = if (arch.isAARCH64()) f16 else u16; - -pub fn __extendhfsf2(a: F16T) callconv(.C) f32 { - return extendXfYf2(f32, f16, @bitCast(u16, a)); -} - -pub fn __gnu_h2f_ieee(a: F16T) callconv(.C) f32 { - return @call(.{ .modifier = .always_inline }, __extendhfsf2, .{a}); -} - -pub fn __extendhftf2(a: F16T) callconv(.C) f128 { - return extendXfYf2(f128, f16, @bitCast(u16, a)); -} - -pub fn __aeabi_h2f(arg: u16) callconv(.AAPCS) f32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f32, f16, arg }); -} - -pub fn __aeabi_f2d(arg: f32) callconv(.AAPCS) f64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f64, f32, @bitCast(u32, arg) }); -} - -inline fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits)) dst_t { - @setRuntimeSafety(builtin.is_test); +pub inline fn extendf( + comptime dst_t: type, + comptime src_t: type, + a: std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits), +) dst_t { const src_rep_t = std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits); const dst_rep_t = std.meta.Int(.unsigned, @typeInfo(dst_t).Float.bits); const srcSigBits = std.math.floatMantissaBits(src_t); @@ -143,6 +72,71 @@ inline fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: std.meta.In return @bitCast(dst_t, result); } +pub inline fn extend_f80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits)) f80 { + const src_rep_t = std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits); + const src_sig_bits = std.math.floatMantissaBits(src_t); + const dst_int_bit = 0x8000000000000000; + const dst_sig_bits = std.math.floatMantissaBits(f80) - 1; // -1 for the integer bit + + const dst_exp_bias = 16383; + + const src_bits = @bitSizeOf(src_t); + const src_exp_bits = src_bits - src_sig_bits - 1; + const src_inf_exp = (1 << src_exp_bits) - 1; + const src_exp_bias = src_inf_exp >> 1; + + const src_min_normal = 1 << src_sig_bits; + const src_inf = src_inf_exp << src_sig_bits; + const src_sign_mask = 1 << (src_sig_bits + src_exp_bits); + const src_abs_mask = src_sign_mask - 1; + const src_qnan = 1 << (src_sig_bits - 1); + const src_nan_code = src_qnan - 1; + + var dst: std.math.F80 = undefined; + + // Break a into a sign and representation of the absolute value + const a_abs = a & src_abs_mask; + const sign: u16 = if (a & src_sign_mask != 0) 0x8000 else 0; + + if (a_abs -% src_min_normal < src_inf - src_min_normal) { + // a is a normal number. + // Extend to the destination type by shifting the significand and + // exponent into the proper position and rebiasing the exponent. + dst.exp = @intCast(u16, a_abs >> src_sig_bits); + dst.exp += dst_exp_bias - src_exp_bias; + dst.fraction = @as(u64, a_abs) << (dst_sig_bits - src_sig_bits); + dst.fraction |= dst_int_bit; // bit 64 is always set for normal numbers + } else if (a_abs >= src_inf) { + // a is NaN or infinity. + // Conjure the result by beginning with infinity, then setting the qNaN + // bit (if needed) and right-aligning the rest of the trailing NaN + // payload field. + dst.exp = 0x7fff; + dst.fraction = dst_int_bit; + dst.fraction |= @as(u64, a_abs & src_qnan) << (dst_sig_bits - src_sig_bits); + dst.fraction |= @as(u64, a_abs & src_nan_code) << (dst_sig_bits - src_sig_bits); + } else if (a_abs != 0) { + // a is denormal. + // renormalize the significand and clear the leading bit, then insert + // the correct adjusted exponent in the destination type. + const scale: u16 = @clz(src_rep_t, a_abs) - + @clz(src_rep_t, @as(src_rep_t, src_min_normal)); + + dst.fraction = @as(u64, a_abs) << @intCast(u6, dst_sig_bits - src_sig_bits + scale); + dst.fraction |= dst_int_bit; // bit 64 is always set for normal numbers + dst.exp = @truncate(u16, a_abs >> @intCast(u4, src_sig_bits - scale)); + dst.exp ^= 1; + dst.exp |= dst_exp_bias - src_exp_bias - scale + 1; + } else { + // a is zero. + dst.exp = 0; + dst.fraction = 0; + } + + dst.exp |= sign; + return std.math.make_f80(dst); +} + test { _ = @import("extendXfYf2_test.zig"); } diff --git a/lib/compiler_rt/extendhfsf2.zig b/lib/compiler_rt/extendhfsf2.zig new file mode 100644 index 0000000000..6ad4ff37df --- /dev/null +++ b/lib/compiler_rt/extendhfsf2.zig @@ -0,0 +1,26 @@ +const common = @import("./common.zig"); +const extendf = @import("./extendf.zig").extendf; + +pub const panic = common.panic; + +comptime { + if (common.want_gnu_abi) { + @export(__gnu_h2f_ieee, .{ .name = "__gnu_h2f_ieee", .linkage = common.linkage }); + } else if (common.want_aeabi) { + @export(__aeabi_h2f, .{ .name = "__aeabi_h2f", .linkage = common.linkage }); + } else { + @export(__extendhfsf2, .{ .name = "__extendhfsf2", .linkage = common.linkage }); + } +} + +fn __extendhfsf2(a: common.F16T) callconv(.C) f32 { + return extendf(f32, f16, @bitCast(u16, a)); +} + +fn __gnu_h2f_ieee(a: common.F16T) callconv(.C) f32 { + return extendf(f32, f16, @bitCast(u16, a)); +} + +fn __aeabi_h2f(a: u16) callconv(.AAPCS) f32 { + return extendf(f32, f16, @bitCast(u16, a)); +} diff --git a/lib/compiler_rt/extendhftf2.zig b/lib/compiler_rt/extendhftf2.zig new file mode 100644 index 0000000000..a0c8eb5a1e --- /dev/null +++ b/lib/compiler_rt/extendhftf2.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const extendf = @import("./extendf.zig").extendf; + +pub const panic = common.panic; + +comptime { + @export(__extendhftf2, .{ .name = "__extendhftf2", .linkage = common.linkage }); +} + +fn __extendhftf2(a: common.F16T) callconv(.C) f128 { + return extendf(f128, f16, @bitCast(u16, a)); +} diff --git a/lib/compiler_rt/extendhfxf2.zig b/lib/compiler_rt/extendhfxf2.zig new file mode 100644 index 0000000000..e509f96575 --- /dev/null +++ b/lib/compiler_rt/extendhfxf2.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const extend_f80 = @import("./extendf.zig").extend_f80; + +pub const panic = common.panic; + +comptime { + @export(__extendhfxf2, .{ .name = "__extendhfxf2", .linkage = common.linkage }); +} + +fn __extendhfxf2(a: common.F16T) callconv(.C) f80 { + return extend_f80(f16, @bitCast(u16, a)); +} diff --git a/lib/compiler_rt/extendsfdf2.zig b/lib/compiler_rt/extendsfdf2.zig new file mode 100644 index 0000000000..7fd69f6c22 --- /dev/null +++ b/lib/compiler_rt/extendsfdf2.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const extendf = @import("./extendf.zig").extendf; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_f2d, .{ .name = "__aeabi_f2d", .linkage = common.linkage }); + } else { + @export(__extendsfdf2, .{ .name = "__extendsfdf2", .linkage = common.linkage }); + } +} + +fn __extendsfdf2(a: f32) callconv(.C) f64 { + return extendf(f64, f32, @bitCast(u32, a)); +} + +fn __aeabi_f2d(a: f32) callconv(.AAPCS) f64 { + return extendf(f64, f32, @bitCast(u32, a)); +} diff --git a/lib/compiler_rt/extendsftf2.zig b/lib/compiler_rt/extendsftf2.zig new file mode 100644 index 0000000000..8b496783c8 --- /dev/null +++ b/lib/compiler_rt/extendsftf2.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const extendf = @import("./extendf.zig").extendf; + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__extendsfkf2, .{ .name = "__extendsfkf2", .linkage = common.linkage }); + } else { + @export(__extendsftf2, .{ .name = "__extendsftf2", .linkage = common.linkage }); + } +} + +fn __extendsftf2(a: f32) callconv(.C) f128 { + return extendf(f128, f32, @bitCast(u32, a)); +} + +fn __extendsfkf2(a: f32) callconv(.C) f128 { + return extendf(f128, f32, @bitCast(u32, a)); +} diff --git a/lib/compiler_rt/extendsfxf2.zig b/lib/compiler_rt/extendsfxf2.zig new file mode 100644 index 0000000000..41bb5ace85 --- /dev/null +++ b/lib/compiler_rt/extendsfxf2.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const extend_f80 = @import("./extendf.zig").extend_f80; + +pub const panic = common.panic; + +comptime { + @export(__extendsfxf2, .{ .name = "__extendsfxf2", .linkage = common.linkage }); +} + +fn __extendsfxf2(a: f32) callconv(.C) f80 { + return extend_f80(f32, @bitCast(u32, a)); +} diff --git a/lib/compiler_rt/extendxftf2.zig b/lib/compiler_rt/extendxftf2.zig new file mode 100644 index 0000000000..bb5d6a377b --- /dev/null +++ b/lib/compiler_rt/extendxftf2.zig @@ -0,0 +1,50 @@ +const std = @import("std"); +const common = @import("./common.zig"); + +pub const panic = common.panic; + +comptime { + @export(__extendxftf2, .{ .name = "__extendxftf2", .linkage = common.linkage }); +} + +fn __extendxftf2(a: f80) callconv(.C) f128 { + const src_int_bit: u64 = 0x8000000000000000; + const src_sig_mask = ~src_int_bit; + const src_sig_bits = std.math.floatMantissaBits(f80) - 1; // -1 for the integer bit + const dst_sig_bits = std.math.floatMantissaBits(f128); + + const dst_bits = @bitSizeOf(f128); + + const dst_min_normal = @as(u128, 1) << dst_sig_bits; + + // Break a into a sign and representation of the absolute value + var a_rep = std.math.break_f80(a); + const sign = a_rep.exp & 0x8000; + a_rep.exp &= 0x7FFF; + var abs_result: u128 = undefined; + + if (a_rep.exp == 0 and a_rep.fraction == 0) { + // zero + abs_result = 0; + } else if (a_rep.exp == 0x7FFF) { + // a is nan or infinite + abs_result = @as(u128, a_rep.fraction) << (dst_sig_bits - src_sig_bits); + abs_result |= @as(u128, a_rep.exp) << dst_sig_bits; + } else if (a_rep.fraction & src_int_bit != 0) { + // a is a normal value + abs_result = @as(u128, a_rep.fraction & src_sig_mask) << (dst_sig_bits - src_sig_bits); + abs_result |= @as(u128, a_rep.exp) << dst_sig_bits; + } else { + // a is denormal + // renormalize the significand and clear the leading bit and integer part, + // then insert the correct adjusted exponent in the destination type. + const scale: u32 = @clz(u64, a_rep.fraction); + abs_result = @as(u128, a_rep.fraction) << @intCast(u7, dst_sig_bits - src_sig_bits + scale + 1); + abs_result ^= dst_min_normal; + abs_result |= @as(u128, scale + 1) << dst_sig_bits; + } + + // Apply the signbit to (dst_t)abs(a). + const result: u128 align(@alignOf(f128)) = abs_result | @as(u128, sign) << (dst_bits - 16); + return @bitCast(f128, result); +} diff --git a/lib/compiler_rt/fixXfYi.zig b/lib/compiler_rt/fixXfYi.zig deleted file mode 100644 index c568bba366..0000000000 --- a/lib/compiler_rt/fixXfYi.zig +++ /dev/null @@ -1,312 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const math = std.math; -const Log2Int = math.Log2Int; -const arch = builtin.cpu.arch; -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; - -comptime { - // Float -> Integral Conversion - - // Conversion from f32 - @export(__fixsfsi, .{ .name = "__fixsfsi", .linkage = linkage }); - @export(__fixunssfsi, .{ .name = "__fixunssfsi", .linkage = linkage }); - - @export(__fixsfdi, .{ .name = "__fixsfdi", .linkage = linkage }); - @export(__fixunssfdi, .{ .name = "__fixunssfdi", .linkage = linkage }); - - @export(__fixsfti, .{ .name = "__fixsfti", .linkage = linkage }); - @export(__fixunssfti, .{ .name = "__fixunssfti", .linkage = linkage }); - - // Conversion from f64 - @export(__fixdfsi, .{ .name = "__fixdfsi", .linkage = linkage }); - @export(__fixunsdfsi, .{ .name = "__fixunsdfsi", .linkage = linkage }); - - @export(__fixdfdi, .{ .name = "__fixdfdi", .linkage = linkage }); - @export(__fixunsdfdi, .{ .name = "__fixunsdfdi", .linkage = linkage }); - - @export(__fixdfti, .{ .name = "__fixdfti", .linkage = linkage }); - @export(__fixunsdfti, .{ .name = "__fixunsdfti", .linkage = linkage }); - - // Conversion from f80 - @export(__fixxfsi, .{ .name = "__fixxfsi", .linkage = linkage }); - @export(__fixunsxfsi, .{ .name = "__fixunsxfsi", .linkage = linkage }); - - @export(__fixxfdi, .{ .name = "__fixxfdi", .linkage = linkage }); - @export(__fixunsxfdi, .{ .name = "__fixunsxfdi", .linkage = linkage }); - - @export(__fixxfti, .{ .name = "__fixxfti", .linkage = linkage }); - @export(__fixunsxfti, .{ .name = "__fixunsxfti", .linkage = linkage }); - - // Conversion from f128 - @export(__fixtfsi, .{ .name = "__fixtfsi", .linkage = linkage }); - @export(__fixunstfsi, .{ .name = "__fixunstfsi", .linkage = linkage }); - - @export(__fixtfdi, .{ .name = "__fixtfdi", .linkage = linkage }); - @export(__fixunstfdi, .{ .name = "__fixunstfdi", .linkage = linkage }); - - @export(__fixtfti, .{ .name = "__fixtfti", .linkage = linkage }); - @export(__fixunstfti, .{ .name = "__fixunstfti", .linkage = linkage }); - - if (!is_test) { - if (arch.isARM() or arch.isThumb()) { - @export(__aeabi_f2ulz, .{ .name = "__aeabi_f2ulz", .linkage = linkage }); - @export(__aeabi_d2ulz, .{ .name = "__aeabi_d2ulz", .linkage = linkage }); - - @export(__aeabi_f2lz, .{ .name = "__aeabi_f2lz", .linkage = linkage }); - @export(__aeabi_d2lz, .{ .name = "__aeabi_d2lz", .linkage = linkage }); - - @export(__aeabi_d2uiz, .{ .name = "__aeabi_d2uiz", .linkage = linkage }); - - @export(__aeabi_f2uiz, .{ .name = "__aeabi_f2uiz", .linkage = linkage }); - - @export(__aeabi_f2iz, .{ .name = "__aeabi_f2iz", .linkage = linkage }); - @export(__aeabi_d2iz, .{ .name = "__aeabi_d2iz", .linkage = linkage }); - } - - if (arch.isPPC() or arch.isPPC64()) { - @export(__fixkfdi, .{ .name = "__fixkfdi", .linkage = linkage }); - @export(__fixkfsi, .{ .name = "__fixkfsi", .linkage = linkage }); - @export(__fixunskfsi, .{ .name = "__fixunskfsi", .linkage = linkage }); - @export(__fixunskfdi, .{ .name = "__fixunskfdi", .linkage = linkage }); - } - } -} - -pub inline fn fixXfYi(comptime I: type, a: anytype) I { - @setRuntimeSafety(is_test); - - const F = @TypeOf(a); - const float_bits = @typeInfo(F).Float.bits; - const int_bits = @typeInfo(I).Int.bits; - const rep_t = std.meta.Int(.unsigned, float_bits); - const sig_bits = math.floatMantissaBits(F); - const exp_bits = math.floatExponentBits(F); - const fractional_bits = math.floatFractionalBits(F); - - const implicit_bit = if (F != f80) (@as(rep_t, 1) << sig_bits) else 0; - const max_exp = (1 << (exp_bits - 1)); - const exp_bias = max_exp - 1; - const sig_mask = (@as(rep_t, 1) << sig_bits) - 1; - - // Break a into sign, exponent, significand - const a_rep: rep_t = @bitCast(rep_t, a); - const negative = (a_rep >> (float_bits - 1)) != 0; - const exponent = @intCast(i32, (a_rep << 1) >> (sig_bits + 1)) - exp_bias; - const significand: rep_t = (a_rep & sig_mask) | implicit_bit; - - // If the exponent is negative, the result rounds to zero. - if (exponent < 0) return 0; - - // If the value is too large for the integer type, saturate. - switch (@typeInfo(I).Int.signedness) { - .unsigned => { - if (negative) return 0; - if (@intCast(c_uint, exponent) >= @minimum(int_bits, max_exp)) return math.maxInt(I); - }, - .signed => if (@intCast(c_uint, exponent) >= @minimum(int_bits - 1, max_exp)) { - return if (negative) math.minInt(I) else math.maxInt(I); - }, - } - - // If 0 <= exponent < sig_bits, right shift to get the result. - // Otherwise, shift left. - var result: I = undefined; - if (exponent < fractional_bits) { - result = @intCast(I, significand >> @intCast(Log2Int(rep_t), fractional_bits - exponent)); - } else { - result = @intCast(I, significand) << @intCast(Log2Int(I), exponent - fractional_bits); - } - - if ((@typeInfo(I).Int.signedness == .signed) and negative) - return ~result +% 1; - return result; -} - -// Conversion from f16 - -pub fn __fixhfsi(a: f16) callconv(.C) i32 { - return fixXfYi(i32, a); -} - -pub fn __fixunshfsi(a: f16) callconv(.C) u32 { - return fixXfYi(u32, a); -} - -pub fn __fixhfdi(a: f16) callconv(.C) i64 { - return fixXfYi(i64, a); -} - -pub fn __fixunshfdi(a: f16) callconv(.C) u64 { - return fixXfYi(u64, a); -} - -pub fn __fixhfti(a: f16) callconv(.C) i128 { - return fixXfYi(i128, a); -} - -pub fn __fixunshfti(a: f16) callconv(.C) u128 { - return fixXfYi(u128, a); -} - -// Conversion from f32 - -pub fn __fixsfsi(a: f32) callconv(.C) i32 { - return fixXfYi(i32, a); -} - -pub fn __fixunssfsi(a: f32) callconv(.C) u32 { - return fixXfYi(u32, a); -} - -pub fn __fixsfdi(a: f32) callconv(.C) i64 { - return fixXfYi(i64, a); -} - -pub fn __fixunssfdi(a: f32) callconv(.C) u64 { - return fixXfYi(u64, a); -} - -pub fn __fixsfti(a: f32) callconv(.C) i128 { - return fixXfYi(i128, a); -} - -pub fn __fixunssfti(a: f32) callconv(.C) u128 { - return fixXfYi(u128, a); -} - -// Conversion from f64 - -pub fn __fixdfsi(a: f64) callconv(.C) i32 { - return fixXfYi(i32, a); -} - -pub fn __fixunsdfsi(a: f64) callconv(.C) u32 { - return fixXfYi(u32, a); -} - -pub fn __fixdfdi(a: f64) callconv(.C) i64 { - return fixXfYi(i64, a); -} - -pub fn __fixunsdfdi(a: f64) callconv(.C) u64 { - return fixXfYi(u64, a); -} - -pub fn __fixdfti(a: f64) callconv(.C) i128 { - return fixXfYi(i128, a); -} - -pub fn __fixunsdfti(a: f64) callconv(.C) u128 { - return fixXfYi(u128, a); -} - -// Conversion from f80 - -pub fn __fixxfsi(a: f80) callconv(.C) i32 { - return fixXfYi(i32, a); -} - -pub fn __fixunsxfsi(a: f80) callconv(.C) u32 { - return fixXfYi(u32, a); -} - -pub fn __fixxfdi(a: f80) callconv(.C) i64 { - return fixXfYi(i64, a); -} - -pub fn __fixunsxfdi(a: f80) callconv(.C) u64 { - return fixXfYi(u64, a); -} - -pub fn __fixxfti(a: f80) callconv(.C) i128 { - return fixXfYi(i128, a); -} - -pub fn __fixunsxfti(a: f80) callconv(.C) u128 { - return fixXfYi(u128, a); -} - -// Conversion from f128 - -pub fn __fixtfsi(a: f128) callconv(.C) i32 { - return fixXfYi(i32, a); -} - -pub fn __fixkfsi(a: f128) callconv(.C) i32 { - return __fixtfsi(a); -} - -pub fn __fixunstfsi(a: f128) callconv(.C) u32 { - return fixXfYi(u32, a); -} - -pub fn __fixunskfsi(a: f128) callconv(.C) u32 { - return @call(.{ .modifier = .always_inline }, __fixunstfsi, .{a}); -} - -pub fn __fixtfdi(a: f128) callconv(.C) i64 { - return fixXfYi(i64, a); -} - -pub fn __fixkfdi(a: f128) callconv(.C) i64 { - return @call(.{ .modifier = .always_inline }, __fixtfdi, .{a}); -} - -pub fn __fixunstfdi(a: f128) callconv(.C) u64 { - return fixXfYi(u64, a); -} - -pub fn __fixunskfdi(a: f128) callconv(.C) u64 { - return @call(.{ .modifier = .always_inline }, __fixunstfdi, .{a}); -} - -pub fn __fixtfti(a: f128) callconv(.C) i128 { - return fixXfYi(i128, a); -} - -pub fn __fixunstfti(a: f128) callconv(.C) u128 { - return fixXfYi(u128, a); -} - -// Conversion from f32 - -pub fn __aeabi_f2iz(a: f32) callconv(.AAPCS) i32 { - return fixXfYi(i32, a); -} - -pub fn __aeabi_f2uiz(a: f32) callconv(.AAPCS) u32 { - return fixXfYi(u32, a); -} - -pub fn __aeabi_f2lz(a: f32) callconv(.AAPCS) i64 { - return fixXfYi(i64, a); -} - -pub fn __aeabi_f2ulz(a: f32) callconv(.AAPCS) u64 { - return fixXfYi(u64, a); -} - -// Conversion from f64 - -pub fn __aeabi_d2iz(a: f64) callconv(.AAPCS) i32 { - return fixXfYi(i32, a); -} - -pub fn __aeabi_d2uiz(a: f64) callconv(.AAPCS) u32 { - return fixXfYi(u32, a); -} - -pub fn __aeabi_d2lz(a: f64) callconv(.AAPCS) i64 { - return fixXfYi(i64, a); -} - -pub fn __aeabi_d2ulz(a: f64) callconv(.AAPCS) u64 { - return fixXfYi(u64, a); -} - -test { - _ = @import("fixXfYi_test.zig"); -} diff --git a/lib/compiler_rt/fixdfdi.zig b/lib/compiler_rt/fixdfdi.zig new file mode 100644 index 0000000000..2c3204db65 --- /dev/null +++ b/lib/compiler_rt/fixdfdi.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_d2lz, .{ .name = "__aeabi_d2lz", .linkage = common.linkage }); + } else { + @export(__fixdfdi, .{ .name = "__fixdfdi", .linkage = common.linkage }); + } +} + +fn __fixdfdi(a: f64) callconv(.C) i64 { + return floatToInt(i64, a); +} + +fn __aeabi_d2lz(a: f64) callconv(.AAPCS) i64 { + return floatToInt(i64, a); +} diff --git a/lib/compiler_rt/fixdfsi.zig b/lib/compiler_rt/fixdfsi.zig new file mode 100644 index 0000000000..415160f1de --- /dev/null +++ b/lib/compiler_rt/fixdfsi.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_d2iz, .{ .name = "__aeabi_d2iz", .linkage = common.linkage }); + } else { + @export(__fixdfsi, .{ .name = "__fixdfsi", .linkage = common.linkage }); + } +} + +fn __fixdfsi(a: f64) callconv(.C) i32 { + return floatToInt(i32, a); +} + +fn __aeabi_d2iz(a: f64) callconv(.AAPCS) i32 { + return floatToInt(i32, a); +} diff --git a/lib/compiler_rt/fixdfti.zig b/lib/compiler_rt/fixdfti.zig new file mode 100644 index 0000000000..56ae2bbeb2 --- /dev/null +++ b/lib/compiler_rt/fixdfti.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + @export(__fixdfti, .{ .name = "__fixdfti", .linkage = common.linkage }); +} + +fn __fixdfti(a: f64) callconv(.C) i128 { + return floatToInt(i128, a); +} diff --git a/lib/compiler_rt/fixhfdi.zig b/lib/compiler_rt/fixhfdi.zig new file mode 100644 index 0000000000..28e871f495 --- /dev/null +++ b/lib/compiler_rt/fixhfdi.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + @export(__fixhfdi, .{ .name = "__fixhfdi", .linkage = common.linkage }); +} + +fn __fixhfdi(a: f16) callconv(.C) i64 { + return floatToInt(i64, a); +} diff --git a/lib/compiler_rt/fixhfsi.zig b/lib/compiler_rt/fixhfsi.zig new file mode 100644 index 0000000000..23440eea22 --- /dev/null +++ b/lib/compiler_rt/fixhfsi.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + @export(__fixhfsi, .{ .name = "__fixhfsi", .linkage = common.linkage }); +} + +fn __fixhfsi(a: f16) callconv(.C) i32 { + return floatToInt(i32, a); +} diff --git a/lib/compiler_rt/fixhfti.zig b/lib/compiler_rt/fixhfti.zig new file mode 100644 index 0000000000..36fc1bf607 --- /dev/null +++ b/lib/compiler_rt/fixhfti.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + @export(__fixhfti, .{ .name = "__fixhfti", .linkage = common.linkage }); +} + +fn __fixhfti(a: f16) callconv(.C) i128 { + return floatToInt(i128, a); +} diff --git a/lib/compiler_rt/fixsfdi.zig b/lib/compiler_rt/fixsfdi.zig new file mode 100644 index 0000000000..71b0f047e2 --- /dev/null +++ b/lib/compiler_rt/fixsfdi.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_f2lz, .{ .name = "__aeabi_f2lz", .linkage = common.linkage }); + } else { + @export(__fixsfdi, .{ .name = "__fixsfdi", .linkage = common.linkage }); + } +} + +fn __fixsfdi(a: f32) callconv(.C) i64 { + return floatToInt(i64, a); +} + +fn __aeabi_f2lz(a: f32) callconv(.AAPCS) i64 { + return floatToInt(i64, a); +} diff --git a/lib/compiler_rt/fixsfsi.zig b/lib/compiler_rt/fixsfsi.zig new file mode 100644 index 0000000000..d48162928b --- /dev/null +++ b/lib/compiler_rt/fixsfsi.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_f2iz, .{ .name = "__aeabi_f2iz", .linkage = common.linkage }); + } else { + @export(__fixsfsi, .{ .name = "__fixsfsi", .linkage = common.linkage }); + } +} + +fn __fixsfsi(a: f32) callconv(.C) i32 { + return floatToInt(i32, a); +} + +fn __aeabi_f2iz(a: f32) callconv(.AAPCS) i32 { + return floatToInt(i32, a); +} diff --git a/lib/compiler_rt/fixsfti.zig b/lib/compiler_rt/fixsfti.zig new file mode 100644 index 0000000000..aefde49768 --- /dev/null +++ b/lib/compiler_rt/fixsfti.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + @export(__fixsfti, .{ .name = "__fixsfti", .linkage = common.linkage }); +} + +fn __fixsfti(a: f32) callconv(.C) i128 { + return floatToInt(i128, a); +} diff --git a/lib/compiler_rt/fixtfdi.zig b/lib/compiler_rt/fixtfdi.zig new file mode 100644 index 0000000000..8f8d473b1f --- /dev/null +++ b/lib/compiler_rt/fixtfdi.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__fixkfdi, .{ .name = "__fixkfdi", .linkage = common.linkage }); + } else { + @export(__fixtfdi, .{ .name = "__fixtfdi", .linkage = common.linkage }); + } +} + +fn __fixtfdi(a: f128) callconv(.C) i64 { + return floatToInt(i64, a); +} + +fn __fixkfdi(a: f128) callconv(.C) i64 { + return floatToInt(i64, a); +} diff --git a/lib/compiler_rt/fixtfsi.zig b/lib/compiler_rt/fixtfsi.zig new file mode 100644 index 0000000000..507581d0cf --- /dev/null +++ b/lib/compiler_rt/fixtfsi.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__fixkfsi, .{ .name = "__fixkfsi", .linkage = common.linkage }); + } else { + @export(__fixtfsi, .{ .name = "__fixtfsi", .linkage = common.linkage }); + } +} + +fn __fixtfsi(a: f128) callconv(.C) i32 { + return floatToInt(i32, a); +} + +fn __fixkfsi(a: f128) callconv(.C) i32 { + return floatToInt(i32, a); +} diff --git a/lib/compiler_rt/fixtfti.zig b/lib/compiler_rt/fixtfti.zig new file mode 100644 index 0000000000..a50ee5aa7c --- /dev/null +++ b/lib/compiler_rt/fixtfti.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + @export(__fixtfti, .{ .name = "__fixtfti", .linkage = common.linkage }); +} + +fn __fixtfti(a: f128) callconv(.C) i128 { + return floatToInt(i128, a); +} diff --git a/lib/compiler_rt/fixunsdfdi.zig b/lib/compiler_rt/fixunsdfdi.zig new file mode 100644 index 0000000000..afcb969ac6 --- /dev/null +++ b/lib/compiler_rt/fixunsdfdi.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_d2ulz, .{ .name = "__aeabi_d2ulz", .linkage = common.linkage }); + } else { + @export(__fixunsdfdi, .{ .name = "__fixunsdfdi", .linkage = common.linkage }); + } +} + +fn __fixunsdfdi(a: f64) callconv(.C) u64 { + return floatToInt(u64, a); +} + +fn __aeabi_d2ulz(a: f64) callconv(.AAPCS) u64 { + return floatToInt(u64, a); +} diff --git a/lib/compiler_rt/fixunsdfsi.zig b/lib/compiler_rt/fixunsdfsi.zig new file mode 100644 index 0000000000..3675eb2c6f --- /dev/null +++ b/lib/compiler_rt/fixunsdfsi.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_d2uiz, .{ .name = "__aeabi_d2uiz", .linkage = common.linkage }); + } else { + @export(__fixunsdfsi, .{ .name = "__fixunsdfsi", .linkage = common.linkage }); + } +} + +fn __fixunsdfsi(a: f64) callconv(.C) u32 { + return floatToInt(u32, a); +} + +fn __aeabi_d2uiz(a: f64) callconv(.AAPCS) u32 { + return floatToInt(u32, a); +} diff --git a/lib/compiler_rt/fixunsdfti.zig b/lib/compiler_rt/fixunsdfti.zig new file mode 100644 index 0000000000..96dd242f6d --- /dev/null +++ b/lib/compiler_rt/fixunsdfti.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + @export(__fixunsdfti, .{ .name = "__fixunsdfti", .linkage = common.linkage }); +} + +fn __fixunsdfti(a: f64) callconv(.C) u128 { + return floatToInt(u128, a); +} diff --git a/lib/compiler_rt/fixunshfdi.zig b/lib/compiler_rt/fixunshfdi.zig new file mode 100644 index 0000000000..5058bc5e68 --- /dev/null +++ b/lib/compiler_rt/fixunshfdi.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + @export(__fixunshfdi, .{ .name = "__fixunshfdi", .linkage = common.linkage }); +} + +fn __fixunshfdi(a: f16) callconv(.C) u64 { + return floatToInt(u64, a); +} diff --git a/lib/compiler_rt/fixunshfsi.zig b/lib/compiler_rt/fixunshfsi.zig new file mode 100644 index 0000000000..5755048814 --- /dev/null +++ b/lib/compiler_rt/fixunshfsi.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + @export(__fixunshfsi, .{ .name = "__fixunshfsi", .linkage = common.linkage }); +} + +fn __fixunshfsi(a: f16) callconv(.C) u32 { + return floatToInt(u32, a); +} diff --git a/lib/compiler_rt/fixunshfti.zig b/lib/compiler_rt/fixunshfti.zig new file mode 100644 index 0000000000..ac013263eb --- /dev/null +++ b/lib/compiler_rt/fixunshfti.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + @export(__fixunshfti, .{ .name = "__fixunshfti", .linkage = common.linkage }); +} + +fn __fixunshfti(a: f16) callconv(.C) u128 { + return floatToInt(u128, a); +} diff --git a/lib/compiler_rt/fixunssfdi.zig b/lib/compiler_rt/fixunssfdi.zig new file mode 100644 index 0000000000..223bbcc2b5 --- /dev/null +++ b/lib/compiler_rt/fixunssfdi.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_f2ulz, .{ .name = "__aeabi_f2ulz", .linkage = common.linkage }); + } else { + @export(__fixunssfdi, .{ .name = "__fixunssfdi", .linkage = common.linkage }); + } +} + +fn __fixunssfdi(a: f32) callconv(.C) u64 { + return floatToInt(u64, a); +} + +fn __aeabi_f2ulz(a: f32) callconv(.AAPCS) u64 { + return floatToInt(u64, a); +} diff --git a/lib/compiler_rt/fixunssfsi.zig b/lib/compiler_rt/fixunssfsi.zig new file mode 100644 index 0000000000..0a113af529 --- /dev/null +++ b/lib/compiler_rt/fixunssfsi.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_f2uiz, .{ .name = "__aeabi_f2uiz", .linkage = common.linkage }); + } else { + @export(__fixunssfsi, .{ .name = "__fixunssfsi", .linkage = common.linkage }); + } +} + +fn __fixunssfsi(a: f32) callconv(.C) u32 { + return floatToInt(u32, a); +} + +fn __aeabi_f2uiz(a: f32) callconv(.AAPCS) u32 { + return floatToInt(u32, a); +} diff --git a/lib/compiler_rt/fixunssfti.zig b/lib/compiler_rt/fixunssfti.zig new file mode 100644 index 0000000000..6b7690784d --- /dev/null +++ b/lib/compiler_rt/fixunssfti.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + @export(__fixunssfti, .{ .name = "__fixunssfti", .linkage = common.linkage }); +} + +fn __fixunssfti(a: f32) callconv(.C) u128 { + return floatToInt(u128, a); +} diff --git a/lib/compiler_rt/fixunstfdi.zig b/lib/compiler_rt/fixunstfdi.zig new file mode 100644 index 0000000000..0130f21139 --- /dev/null +++ b/lib/compiler_rt/fixunstfdi.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__fixunskfdi, .{ .name = "__fixunskfdi", .linkage = common.linkage }); + } else { + @export(__fixunstfdi, .{ .name = "__fixunstfdi", .linkage = common.linkage }); + } +} + +fn __fixunstfdi(a: f128) callconv(.C) u64 { + return floatToInt(u64, a); +} + +fn __fixunskfdi(a: f128) callconv(.C) u64 { + return floatToInt(u64, a); +} diff --git a/lib/compiler_rt/fixunstfsi.zig b/lib/compiler_rt/fixunstfsi.zig new file mode 100644 index 0000000000..78e2cc83d4 --- /dev/null +++ b/lib/compiler_rt/fixunstfsi.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__fixunskfsi, .{ .name = "__fixunskfsi", .linkage = common.linkage }); + } else { + @export(__fixunstfsi, .{ .name = "__fixunstfsi", .linkage = common.linkage }); + } +} + +fn __fixunstfsi(a: f128) callconv(.C) u32 { + return floatToInt(u32, a); +} + +fn __fixunskfsi(a: f128) callconv(.C) u32 { + return floatToInt(u32, a); +} diff --git a/lib/compiler_rt/fixunstfti.zig b/lib/compiler_rt/fixunstfti.zig new file mode 100644 index 0000000000..ba3a3ed12e --- /dev/null +++ b/lib/compiler_rt/fixunstfti.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + @export(__fixunstfti, .{ .name = "__fixunstfti", .linkage = common.linkage }); +} + +fn __fixunstfti(a: f128) callconv(.C) u128 { + return floatToInt(u128, a); +} diff --git a/lib/compiler_rt/fixunsxfdi.zig b/lib/compiler_rt/fixunsxfdi.zig new file mode 100644 index 0000000000..cb2760af4e --- /dev/null +++ b/lib/compiler_rt/fixunsxfdi.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + @export(__fixunsxfdi, .{ .name = "__fixunsxfdi", .linkage = common.linkage }); +} + +fn __fixunsxfdi(a: f80) callconv(.C) u64 { + return floatToInt(u64, a); +} diff --git a/lib/compiler_rt/fixunsxfsi.zig b/lib/compiler_rt/fixunsxfsi.zig new file mode 100644 index 0000000000..bec36abbf4 --- /dev/null +++ b/lib/compiler_rt/fixunsxfsi.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + @export(__fixunsxfsi, .{ .name = "__fixunsxfsi", .linkage = common.linkage }); +} + +fn __fixunsxfsi(a: f80) callconv(.C) u32 { + return floatToInt(u32, a); +} diff --git a/lib/compiler_rt/fixunsxfti.zig b/lib/compiler_rt/fixunsxfti.zig new file mode 100644 index 0000000000..f2cd85cf2f --- /dev/null +++ b/lib/compiler_rt/fixunsxfti.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + @export(__fixunsxfti, .{ .name = "__fixunsxfti", .linkage = common.linkage }); +} + +fn __fixunsxfti(a: f80) callconv(.C) u128 { + return floatToInt(u128, a); +} diff --git a/lib/compiler_rt/fixxfdi.zig b/lib/compiler_rt/fixxfdi.zig new file mode 100644 index 0000000000..0f249e0a92 --- /dev/null +++ b/lib/compiler_rt/fixxfdi.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + @export(__fixxfdi, .{ .name = "__fixxfdi", .linkage = common.linkage }); +} + +fn __fixxfdi(a: f80) callconv(.C) i64 { + return floatToInt(i64, a); +} diff --git a/lib/compiler_rt/fixxfsi.zig b/lib/compiler_rt/fixxfsi.zig new file mode 100644 index 0000000000..ac2158b7b8 --- /dev/null +++ b/lib/compiler_rt/fixxfsi.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + @export(__fixxfsi, .{ .name = "__fixxfsi", .linkage = common.linkage }); +} + +fn __fixxfsi(a: f80) callconv(.C) i32 { + return floatToInt(i32, a); +} diff --git a/lib/compiler_rt/fixxfti.zig b/lib/compiler_rt/fixxfti.zig new file mode 100644 index 0000000000..fb547f4115 --- /dev/null +++ b/lib/compiler_rt/fixxfti.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const floatToInt = @import("./float_to_int.zig").floatToInt; + +pub const panic = common.panic; + +comptime { + @export(__fixxfti, .{ .name = "__fixxfti", .linkage = common.linkage }); +} + +fn __fixxfti(a: f80) callconv(.C) i128 { + return floatToInt(i128, a); +} diff --git a/lib/compiler_rt/floatXiYf.zig b/lib/compiler_rt/floatXiYf.zig deleted file mode 100644 index 25f5743491..0000000000 --- a/lib/compiler_rt/floatXiYf.zig +++ /dev/null @@ -1,311 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const math = std.math; -const expect = std.testing.expect; -const arch = builtin.cpu.arch; -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; - -comptime { - // Integral -> Float Conversion - - // Conversion to f32 - @export(__floatsisf, .{ .name = "__floatsisf", .linkage = linkage }); - @export(__floatunsisf, .{ .name = "__floatunsisf", .linkage = linkage }); - - @export(__floatundisf, .{ .name = "__floatundisf", .linkage = linkage }); - @export(__floatdisf, .{ .name = "__floatdisf", .linkage = linkage }); - - @export(__floattisf, .{ .name = "__floattisf", .linkage = linkage }); - @export(__floatuntisf, .{ .name = "__floatuntisf", .linkage = linkage }); - - // Conversion to f64 - @export(__floatsidf, .{ .name = "__floatsidf", .linkage = linkage }); - @export(__floatunsidf, .{ .name = "__floatunsidf", .linkage = linkage }); - - @export(__floatdidf, .{ .name = "__floatdidf", .linkage = linkage }); - @export(__floatundidf, .{ .name = "__floatundidf", .linkage = linkage }); - - @export(__floattidf, .{ .name = "__floattidf", .linkage = linkage }); - @export(__floatuntidf, .{ .name = "__floatuntidf", .linkage = linkage }); - - // Conversion to f80 - @export(__floatsixf, .{ .name = "__floatsixf", .linkage = linkage }); - @export(__floatunsixf, .{ .name = "__floatunsixf", .linkage = linkage }); - - @export(__floatdixf, .{ .name = "__floatdixf", .linkage = linkage }); - @export(__floatundixf, .{ .name = "__floatundixf", .linkage = linkage }); - - @export(__floattixf, .{ .name = "__floattixf", .linkage = linkage }); - @export(__floatuntixf, .{ .name = "__floatuntixf", .linkage = linkage }); - - // Conversion to f128 - @export(__floatsitf, .{ .name = "__floatsitf", .linkage = linkage }); - @export(__floatunsitf, .{ .name = "__floatunsitf", .linkage = linkage }); - - @export(__floatditf, .{ .name = "__floatditf", .linkage = linkage }); - @export(__floatunditf, .{ .name = "__floatunditf", .linkage = linkage }); - - @export(__floattitf, .{ .name = "__floattitf", .linkage = linkage }); - @export(__floatuntitf, .{ .name = "__floatuntitf", .linkage = linkage }); - - if (!is_test) { - if (arch.isARM() or arch.isThumb()) { - @export(__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = linkage }); - @export(__aeabi_l2d, .{ .name = "__aeabi_l2d", .linkage = linkage }); - @export(__aeabi_l2f, .{ .name = "__aeabi_l2f", .linkage = linkage }); - @export(__aeabi_ui2d, .{ .name = "__aeabi_ui2d", .linkage = linkage }); - @export(__aeabi_ul2d, .{ .name = "__aeabi_ul2d", .linkage = linkage }); - @export(__aeabi_ui2f, .{ .name = "__aeabi_ui2f", .linkage = linkage }); - @export(__aeabi_ul2f, .{ .name = "__aeabi_ul2f", .linkage = linkage }); - - @export(__aeabi_i2f, .{ .name = "__aeabi_i2f", .linkage = linkage }); - } - - if (arch.isPPC() or arch.isPPC64()) { - @export(__floatsikf, .{ .name = "__floatsikf", .linkage = linkage }); - @export(__floatdikf, .{ .name = "__floatdikf", .linkage = linkage }); - @export(__floatundikf, .{ .name = "__floatundikf", .linkage = linkage }); - @export(__floatunsikf, .{ .name = "__floatunsikf", .linkage = linkage }); - @export(__floatuntikf, .{ .name = "__floatuntikf", .linkage = linkage }); - } - } -} - -pub fn floatXiYf(comptime T: type, x: anytype) T { - @setRuntimeSafety(is_test); - - if (x == 0) return 0; - - // Various constants whose values follow from the type parameters. - // Any reasonable optimizer will fold and propagate all of these. - const Z = std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(x))); - const uT = std.meta.Int(.unsigned, @bitSizeOf(T)); - const inf = math.inf(T); - const float_bits = @bitSizeOf(T); - const int_bits = @bitSizeOf(@TypeOf(x)); - const exp_bits = math.floatExponentBits(T); - const fractional_bits = math.floatFractionalBits(T); - const exp_bias = math.maxInt(std.meta.Int(.unsigned, exp_bits - 1)); - const implicit_bit = if (T != f80) @as(uT, 1) << fractional_bits else 0; - const max_exp = exp_bias; - - // Sign - var abs_val = math.absCast(x); - const sign_bit = if (x < 0) @as(uT, 1) << (float_bits - 1) else 0; - var result: uT = sign_bit; - - // Compute significand - var exp = int_bits - @clz(Z, abs_val) - 1; - if (int_bits <= fractional_bits or exp <= fractional_bits) { - const shift_amt = fractional_bits - @intCast(math.Log2Int(uT), exp); - - // Shift up result to line up with the significand - no rounding required - result = (@intCast(uT, abs_val) << shift_amt); - result ^= implicit_bit; // Remove implicit integer bit - } else { - var shift_amt = @intCast(math.Log2Int(Z), exp - fractional_bits); - const exact_tie: bool = @ctz(Z, abs_val) == shift_amt - 1; - - // Shift down result and remove implicit integer bit - result = @intCast(uT, (abs_val >> (shift_amt - 1))) ^ (implicit_bit << 1); - - // Round result, including round-to-even for exact ties - result = ((result + 1) >> 1) & ~@as(uT, @boolToInt(exact_tie)); - } - - // Compute exponent - if ((int_bits > max_exp) and (exp > max_exp)) // If exponent too large, overflow to infinity - return @bitCast(T, sign_bit | @bitCast(uT, inf)); - - result += (@as(uT, exp) + exp_bias) << math.floatMantissaBits(T); - - // If the result included a carry, we need to restore the explicit integer bit - if (T == f80) result |= 1 << fractional_bits; - - return @bitCast(T, sign_bit | result); -} - -// Conversion to f16 -pub fn __floatsihf(a: i32) callconv(.C) f16 { - return floatXiYf(f16, a); -} - -pub fn __floatunsihf(a: u32) callconv(.C) f16 { - return floatXiYf(f16, a); -} - -pub fn __floatdihf(a: i64) callconv(.C) f16 { - return floatXiYf(f16, a); -} - -pub fn __floatundihf(a: u64) callconv(.C) f16 { - return floatXiYf(f16, a); -} - -pub fn __floattihf(a: i128) callconv(.C) f16 { - return floatXiYf(f16, a); -} - -pub fn __floatuntihf(a: u128) callconv(.C) f16 { - return floatXiYf(f16, a); -} - -// Conversion to f32 -pub fn __floatsisf(a: i32) callconv(.C) f32 { - return floatXiYf(f32, a); -} - -pub fn __floatunsisf(a: u32) callconv(.C) f32 { - return floatXiYf(f32, a); -} - -pub fn __floatdisf(a: i64) callconv(.C) f32 { - return floatXiYf(f32, a); -} - -pub fn __floatundisf(a: u64) callconv(.C) f32 { - return floatXiYf(f32, a); -} - -pub fn __floattisf(a: i128) callconv(.C) f32 { - return floatXiYf(f32, a); -} - -pub fn __floatuntisf(a: u128) callconv(.C) f32 { - return floatXiYf(f32, a); -} - -// Conversion to f64 -pub fn __floatsidf(a: i32) callconv(.C) f64 { - return floatXiYf(f64, a); -} - -pub fn __floatunsidf(a: u32) callconv(.C) f64 { - return floatXiYf(f64, a); -} - -pub fn __floatdidf(a: i64) callconv(.C) f64 { - return floatXiYf(f64, a); -} - -pub fn __floatundidf(a: u64) callconv(.C) f64 { - return floatXiYf(f64, a); -} - -pub fn __floattidf(a: i128) callconv(.C) f64 { - return floatXiYf(f64, a); -} - -pub fn __floatuntidf(a: u128) callconv(.C) f64 { - return floatXiYf(f64, a); -} - -// Conversion to f80 -pub fn __floatsixf(a: i32) callconv(.C) f80 { - return floatXiYf(f80, a); -} - -pub fn __floatunsixf(a: u32) callconv(.C) f80 { - return floatXiYf(f80, a); -} - -pub fn __floatdixf(a: i64) callconv(.C) f80 { - return floatXiYf(f80, a); -} - -pub fn __floatundixf(a: u64) callconv(.C) f80 { - return floatXiYf(f80, a); -} - -pub fn __floattixf(a: i128) callconv(.C) f80 { - return floatXiYf(f80, a); -} - -pub fn __floatuntixf(a: u128) callconv(.C) f80 { - return floatXiYf(f80, a); -} - -// Conversion to f128 -pub fn __floatsitf(a: i32) callconv(.C) f128 { - return floatXiYf(f128, a); -} - -pub fn __floatsikf(a: i32) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, __floatsitf, .{a}); -} - -pub fn __floatunsitf(a: u32) callconv(.C) f128 { - return floatXiYf(f128, a); -} - -pub fn __floatunsikf(a: u32) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, __floatunsitf, .{a}); -} - -pub fn __floatditf(a: i64) callconv(.C) f128 { - return floatXiYf(f128, a); -} - -pub fn __floatdikf(a: i64) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, __floatditf, .{a}); -} - -pub fn __floatunditf(a: u64) callconv(.C) f128 { - return floatXiYf(f128, a); -} - -pub fn __floatundikf(a: u64) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, __floatunditf, .{a}); -} - -pub fn __floattitf(a: i128) callconv(.C) f128 { - return floatXiYf(f128, a); -} - -pub fn __floatuntitf(a: u128) callconv(.C) f128 { - return floatXiYf(f128, a); -} - -pub fn __floatuntikf(a: u128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, __floatuntitf, .{a}); -} - -// Conversion to f32 -pub fn __aeabi_ui2f(arg: u32) callconv(.AAPCS) f32 { - return floatXiYf(f32, arg); -} - -pub fn __aeabi_i2f(arg: i32) callconv(.AAPCS) f32 { - return floatXiYf(f32, arg); -} - -pub fn __aeabi_ul2f(arg: u64) callconv(.AAPCS) f32 { - return floatXiYf(f32, arg); -} - -pub fn __aeabi_l2f(arg: i64) callconv(.AAPCS) f32 { - return floatXiYf(f32, arg); -} - -// Conversion to f64 -pub fn __aeabi_ui2d(arg: u32) callconv(.AAPCS) f64 { - return floatXiYf(f64, arg); -} - -pub fn __aeabi_i2d(arg: i32) callconv(.AAPCS) f64 { - return floatXiYf(f64, arg); -} - -pub fn __aeabi_ul2d(arg: u64) callconv(.AAPCS) f64 { - return floatXiYf(f64, arg); -} - -pub fn __aeabi_l2d(arg: i64) callconv(.AAPCS) f64 { - return floatXiYf(f64, arg); -} - -test { - _ = @import("floatXiYf_test.zig"); -} diff --git a/lib/compiler_rt/float_to_int.zig b/lib/compiler_rt/float_to_int.zig new file mode 100644 index 0000000000..49d41be442 --- /dev/null +++ b/lib/compiler_rt/float_to_int.zig @@ -0,0 +1,55 @@ +const Int = @import("std").meta.Int; +const math = @import("std").math; +const Log2Int = math.Log2Int; + +pub inline fn floatToInt(comptime I: type, a: anytype) I { + const F = @TypeOf(a); + const float_bits = @typeInfo(F).Float.bits; + const int_bits = @typeInfo(I).Int.bits; + const rep_t = Int(.unsigned, float_bits); + const sig_bits = math.floatMantissaBits(F); + const exp_bits = math.floatExponentBits(F); + const fractional_bits = math.floatFractionalBits(F); + + const implicit_bit = if (F != f80) (@as(rep_t, 1) << sig_bits) else 0; + const max_exp = (1 << (exp_bits - 1)); + const exp_bias = max_exp - 1; + const sig_mask = (@as(rep_t, 1) << sig_bits) - 1; + + // Break a into sign, exponent, significand + const a_rep: rep_t = @bitCast(rep_t, a); + const negative = (a_rep >> (float_bits - 1)) != 0; + const exponent = @intCast(i32, (a_rep << 1) >> (sig_bits + 1)) - exp_bias; + const significand: rep_t = (a_rep & sig_mask) | implicit_bit; + + // If the exponent is negative, the result rounds to zero. + if (exponent < 0) return 0; + + // If the value is too large for the integer type, saturate. + switch (@typeInfo(I).Int.signedness) { + .unsigned => { + if (negative) return 0; + if (@intCast(c_uint, exponent) >= @minimum(int_bits, max_exp)) return math.maxInt(I); + }, + .signed => if (@intCast(c_uint, exponent) >= @minimum(int_bits - 1, max_exp)) { + return if (negative) math.minInt(I) else math.maxInt(I); + }, + } + + // If 0 <= exponent < sig_bits, right shift to get the result. + // Otherwise, shift left. + var result: I = undefined; + if (exponent < fractional_bits) { + result = @intCast(I, significand >> @intCast(Log2Int(rep_t), fractional_bits - exponent)); + } else { + result = @intCast(I, significand) << @intCast(Log2Int(I), exponent - fractional_bits); + } + + if ((@typeInfo(I).Int.signedness == .signed) and negative) + return ~result +% 1; + return result; +} + +test { + _ = @import("float_to_int_test.zig"); +} diff --git a/lib/compiler_rt/fixXfYi_test.zig b/lib/compiler_rt/float_to_int_test.zig similarity index 100% rename from lib/compiler_rt/fixXfYi_test.zig rename to lib/compiler_rt/float_to_int_test.zig diff --git a/lib/compiler_rt/floatdidf.zig b/lib/compiler_rt/floatdidf.zig new file mode 100644 index 0000000000..9a0ecc6ed4 --- /dev/null +++ b/lib/compiler_rt/floatdidf.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_l2d, .{ .name = "__aeabi_l2d", .linkage = common.linkage }); + } else { + @export(__floatdidf, .{ .name = "__floatdidf", .linkage = common.linkage }); + } +} + +fn __floatdidf(a: i64) callconv(.C) f64 { + return intToFloat(f64, a); +} + +fn __aeabi_l2d(a: i64) callconv(.AAPCS) f64 { + return intToFloat(f64, a); +} diff --git a/lib/compiler_rt/floatdihf.zig b/lib/compiler_rt/floatdihf.zig new file mode 100644 index 0000000000..f2f7236d6f --- /dev/null +++ b/lib/compiler_rt/floatdihf.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + @export(__floatdihf, .{ .name = "__floatdihf", .linkage = common.linkage }); +} + +fn __floatdihf(a: i64) callconv(.C) f16 { + return intToFloat(f16, a); +} diff --git a/lib/compiler_rt/floatdisf.zig b/lib/compiler_rt/floatdisf.zig new file mode 100644 index 0000000000..d2ad3bb04b --- /dev/null +++ b/lib/compiler_rt/floatdisf.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_l2f, .{ .name = "__aeabi_l2f", .linkage = common.linkage }); + } else { + @export(__floatdisf, .{ .name = "__floatdisf", .linkage = common.linkage }); + } +} + +fn __floatdisf(a: i64) callconv(.C) f32 { + return intToFloat(f32, a); +} + +fn __aeabi_l2f(a: i64) callconv(.AAPCS) f32 { + return intToFloat(f32, a); +} diff --git a/lib/compiler_rt/floatditf.zig b/lib/compiler_rt/floatditf.zig new file mode 100644 index 0000000000..04132b9c24 --- /dev/null +++ b/lib/compiler_rt/floatditf.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__floatdikf, .{ .name = "__floatdikf", .linkage = common.linkage }); + } else { + @export(__floatditf, .{ .name = "__floatditf", .linkage = common.linkage }); + } +} + +fn __floatditf(a: i64) callconv(.C) f128 { + return intToFloat(f128, a); +} + +fn __floatdikf(a: i64) callconv(.C) f128 { + return intToFloat(f128, a); +} diff --git a/lib/compiler_rt/floatdixf.zig b/lib/compiler_rt/floatdixf.zig new file mode 100644 index 0000000000..7d80fdbeb8 --- /dev/null +++ b/lib/compiler_rt/floatdixf.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + @export(__floatdixf, .{ .name = "__floatdixf", .linkage = common.linkage }); +} + +fn __floatdixf(a: i64) callconv(.C) f80 { + return intToFloat(f80, a); +} diff --git a/lib/compiler_rt/floatsidf.zig b/lib/compiler_rt/floatsidf.zig new file mode 100644 index 0000000000..1775b08032 --- /dev/null +++ b/lib/compiler_rt/floatsidf.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_i2d, .{ .name = "__aeabi_i2d", .linkage = common.linkage }); + } else { + @export(__floatsidf, .{ .name = "__floatsidf", .linkage = common.linkage }); + } +} + +fn __floatsidf(a: i32) callconv(.C) f64 { + return intToFloat(f64, a); +} + +fn __aeabi_i2d(a: i32) callconv(.AAPCS) f64 { + return intToFloat(f64, a); +} diff --git a/lib/compiler_rt/floatsihf.zig b/lib/compiler_rt/floatsihf.zig new file mode 100644 index 0000000000..84b54298b5 --- /dev/null +++ b/lib/compiler_rt/floatsihf.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + @export(__floatsihf, .{ .name = "__floatsihf", .linkage = common.linkage }); +} + +fn __floatsihf(a: i32) callconv(.C) f16 { + return intToFloat(f16, a); +} diff --git a/lib/compiler_rt/floatsisf.zig b/lib/compiler_rt/floatsisf.zig new file mode 100644 index 0000000000..45c7f8d6e4 --- /dev/null +++ b/lib/compiler_rt/floatsisf.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_i2f, .{ .name = "__aeabi_i2f", .linkage = common.linkage }); + } else { + @export(__floatsisf, .{ .name = "__floatsisf", .linkage = common.linkage }); + } +} + +fn __floatsisf(a: i32) callconv(.C) f32 { + return intToFloat(f32, a); +} + +fn __aeabi_i2f(a: i32) callconv(.AAPCS) f32 { + return intToFloat(f32, a); +} diff --git a/lib/compiler_rt/floatsitf.zig b/lib/compiler_rt/floatsitf.zig new file mode 100644 index 0000000000..24f5e3bf42 --- /dev/null +++ b/lib/compiler_rt/floatsitf.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__floatsikf, .{ .name = "__floatsikf", .linkage = common.linkage }); + } else { + @export(__floatsitf, .{ .name = "__floatsitf", .linkage = common.linkage }); + } +} + +fn __floatsitf(a: i32) callconv(.C) f128 { + return intToFloat(f128, a); +} + +fn __floatsikf(a: i32) callconv(.C) f128 { + return intToFloat(f128, a); +} diff --git a/lib/compiler_rt/floatsixf.zig b/lib/compiler_rt/floatsixf.zig new file mode 100644 index 0000000000..76d266e17a --- /dev/null +++ b/lib/compiler_rt/floatsixf.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + @export(__floatsixf, .{ .name = "__floatsixf", .linkage = common.linkage }); +} + +fn __floatsixf(a: i32) callconv(.C) f80 { + return intToFloat(f80, a); +} diff --git a/lib/compiler_rt/floattidf.zig b/lib/compiler_rt/floattidf.zig new file mode 100644 index 0000000000..54fff192c7 --- /dev/null +++ b/lib/compiler_rt/floattidf.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + @export(__floattidf, .{ .name = "__floattidf", .linkage = common.linkage }); +} + +fn __floattidf(a: i128) callconv(.C) f64 { + return intToFloat(f64, a); +} diff --git a/lib/compiler_rt/floattihf.zig b/lib/compiler_rt/floattihf.zig new file mode 100644 index 0000000000..c7e45c7d53 --- /dev/null +++ b/lib/compiler_rt/floattihf.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + @export(__floattihf, .{ .name = "__floattihf", .linkage = common.linkage }); +} + +fn __floattihf(a: i128) callconv(.C) f16 { + return intToFloat(f16, a); +} diff --git a/lib/compiler_rt/floattisf.zig b/lib/compiler_rt/floattisf.zig new file mode 100644 index 0000000000..bc161d039f --- /dev/null +++ b/lib/compiler_rt/floattisf.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + @export(__floattisf, .{ .name = "__floattisf", .linkage = common.linkage }); +} + +fn __floattisf(a: i128) callconv(.C) f32 { + return intToFloat(f32, a); +} diff --git a/lib/compiler_rt/floattitf.zig b/lib/compiler_rt/floattitf.zig new file mode 100644 index 0000000000..bcd75d8d83 --- /dev/null +++ b/lib/compiler_rt/floattitf.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + @export(__floattitf, .{ .name = "__floattitf", .linkage = common.linkage }); +} + +fn __floattitf(a: i128) callconv(.C) f128 { + return intToFloat(f128, a); +} diff --git a/lib/compiler_rt/floattixf.zig b/lib/compiler_rt/floattixf.zig new file mode 100644 index 0000000000..def9bef4d5 --- /dev/null +++ b/lib/compiler_rt/floattixf.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + @export(__floattixf, .{ .name = "__floattixf", .linkage = common.linkage }); +} + +fn __floattixf(a: i128) callconv(.C) f80 { + return intToFloat(f80, a); +} diff --git a/lib/compiler_rt/floatundidf.zig b/lib/compiler_rt/floatundidf.zig new file mode 100644 index 0000000000..aed9f42139 --- /dev/null +++ b/lib/compiler_rt/floatundidf.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_ul2d, .{ .name = "__aeabi_ul2d", .linkage = common.linkage }); + } else { + @export(__floatundidf, .{ .name = "__floatundidf", .linkage = common.linkage }); + } +} + +fn __floatundidf(a: u64) callconv(.C) f64 { + return intToFloat(f64, a); +} + +fn __aeabi_ul2d(a: u64) callconv(.AAPCS) f64 { + return intToFloat(f64, a); +} diff --git a/lib/compiler_rt/floatundihf.zig b/lib/compiler_rt/floatundihf.zig new file mode 100644 index 0000000000..6eff8aaec3 --- /dev/null +++ b/lib/compiler_rt/floatundihf.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + @export(__floatundihf, .{ .name = "__floatundihf", .linkage = common.linkage }); +} + +fn __floatundihf(a: u64) callconv(.C) f16 { + return intToFloat(f16, a); +} diff --git a/lib/compiler_rt/floatundisf.zig b/lib/compiler_rt/floatundisf.zig new file mode 100644 index 0000000000..1e6094cead --- /dev/null +++ b/lib/compiler_rt/floatundisf.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_ul2f, .{ .name = "__aeabi_ul2f", .linkage = common.linkage }); + } else { + @export(__floatundisf, .{ .name = "__floatundisf", .linkage = common.linkage }); + } +} + +fn __floatundisf(a: u64) callconv(.C) f32 { + return intToFloat(f32, a); +} + +fn __aeabi_ul2f(a: u64) callconv(.AAPCS) f32 { + return intToFloat(f32, a); +} diff --git a/lib/compiler_rt/floatunditf.zig b/lib/compiler_rt/floatunditf.zig new file mode 100644 index 0000000000..40424d1a6c --- /dev/null +++ b/lib/compiler_rt/floatunditf.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__floatundikf, .{ .name = "__floatundikf", .linkage = common.linkage }); + } else { + @export(__floatunditf, .{ .name = "__floatunditf", .linkage = common.linkage }); + } +} + +fn __floatunditf(a: u64) callconv(.C) f128 { + return intToFloat(f128, a); +} + +fn __floatundikf(a: u64) callconv(.C) f128 { + return intToFloat(f128, a); +} diff --git a/lib/compiler_rt/floatundixf.zig b/lib/compiler_rt/floatundixf.zig new file mode 100644 index 0000000000..331b74df4f --- /dev/null +++ b/lib/compiler_rt/floatundixf.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + @export(__floatundixf, .{ .name = "__floatundixf", .linkage = common.linkage }); +} + +fn __floatundixf(a: u64) callconv(.C) f80 { + return intToFloat(f80, a); +} diff --git a/lib/compiler_rt/floatunsidf.zig b/lib/compiler_rt/floatunsidf.zig new file mode 100644 index 0000000000..472167841b --- /dev/null +++ b/lib/compiler_rt/floatunsidf.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_ui2d, .{ .name = "__aeabi_ui2d", .linkage = common.linkage }); + } else { + @export(__floatunsidf, .{ .name = "__floatunsidf", .linkage = common.linkage }); + } +} + +fn __floatunsidf(a: u32) callconv(.C) f64 { + return intToFloat(f64, a); +} + +fn __aeabi_ui2d(a: u32) callconv(.AAPCS) f64 { + return intToFloat(f64, a); +} diff --git a/lib/compiler_rt/floatunsihf.zig b/lib/compiler_rt/floatunsihf.zig new file mode 100644 index 0000000000..c95de9c536 --- /dev/null +++ b/lib/compiler_rt/floatunsihf.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + @export(__floatunsihf, .{ .name = "__floatunsihf", .linkage = common.linkage }); +} + +fn __floatunsihf(a: u32) callconv(.C) f16 { + return intToFloat(f16, a); +} diff --git a/lib/compiler_rt/floatunsisf.zig b/lib/compiler_rt/floatunsisf.zig new file mode 100644 index 0000000000..b2956e9e28 --- /dev/null +++ b/lib/compiler_rt/floatunsisf.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_ui2f, .{ .name = "__aeabi_ui2f", .linkage = common.linkage }); + } else { + @export(__floatunsisf, .{ .name = "__floatunsisf", .linkage = common.linkage }); + } +} + +fn __floatunsisf(a: u32) callconv(.C) f32 { + return intToFloat(f32, a); +} + +fn __aeabi_ui2f(a: u32) callconv(.AAPCS) f32 { + return intToFloat(f32, a); +} diff --git a/lib/compiler_rt/floatunsitf.zig b/lib/compiler_rt/floatunsitf.zig new file mode 100644 index 0000000000..3f626324d2 --- /dev/null +++ b/lib/compiler_rt/floatunsitf.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__floatunsikf, .{ .name = "__floatunsikf", .linkage = common.linkage }); + } else { + @export(__floatunsitf, .{ .name = "__floatunsitf", .linkage = common.linkage }); + } +} + +fn __floatunsitf(a: u32) callconv(.C) f128 { + return intToFloat(f128, a); +} + +fn __floatunsikf(a: u32) callconv(.C) f128 { + return intToFloat(f128, a); +} diff --git a/lib/compiler_rt/floatunsixf.zig b/lib/compiler_rt/floatunsixf.zig new file mode 100644 index 0000000000..40492564fc --- /dev/null +++ b/lib/compiler_rt/floatunsixf.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + @export(__floatunsixf, .{ .name = "__floatunsixf", .linkage = common.linkage }); +} + +fn __floatunsixf(a: u32) callconv(.C) f80 { + return intToFloat(f80, a); +} diff --git a/lib/compiler_rt/floatuntidf.zig b/lib/compiler_rt/floatuntidf.zig new file mode 100644 index 0000000000..5b35379d38 --- /dev/null +++ b/lib/compiler_rt/floatuntidf.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + @export(__floatuntidf, .{ .name = "__floatuntidf", .linkage = common.linkage }); +} + +fn __floatuntidf(a: u128) callconv(.C) f64 { + return intToFloat(f64, a); +} diff --git a/lib/compiler_rt/floatuntihf.zig b/lib/compiler_rt/floatuntihf.zig new file mode 100644 index 0000000000..0263b1da98 --- /dev/null +++ b/lib/compiler_rt/floatuntihf.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + @export(__floatuntihf, .{ .name = "__floatuntihf", .linkage = common.linkage }); +} + +fn __floatuntihf(a: u128) callconv(.C) f16 { + return intToFloat(f16, a); +} diff --git a/lib/compiler_rt/floatuntisf.zig b/lib/compiler_rt/floatuntisf.zig new file mode 100644 index 0000000000..a3799e50b3 --- /dev/null +++ b/lib/compiler_rt/floatuntisf.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + @export(__floatuntisf, .{ .name = "__floatuntisf", .linkage = common.linkage }); +} + +fn __floatuntisf(a: u128) callconv(.C) f32 { + return intToFloat(f32, a); +} diff --git a/lib/compiler_rt/floatuntitf.zig b/lib/compiler_rt/floatuntitf.zig new file mode 100644 index 0000000000..c1b4b21d56 --- /dev/null +++ b/lib/compiler_rt/floatuntitf.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__floatuntikf, .{ .name = "__floatuntikf", .linkage = common.linkage }); + } else { + @export(__floatuntitf, .{ .name = "__floatuntitf", .linkage = common.linkage }); + } +} + +fn __floatuntitf(a: u128) callconv(.C) f128 { + return intToFloat(f128, a); +} + +fn __floatuntikf(a: u128) callconv(.C) f128 { + return intToFloat(f128, a); +} diff --git a/lib/compiler_rt/floatuntixf.zig b/lib/compiler_rt/floatuntixf.zig new file mode 100644 index 0000000000..07017d1f57 --- /dev/null +++ b/lib/compiler_rt/floatuntixf.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const intToFloat = @import("./int_to_float.zig").intToFloat; + +pub const panic = common.panic; + +comptime { + @export(__floatuntixf, .{ .name = "__floatuntixf", .linkage = common.linkage }); +} + +pub fn __floatuntixf(a: u128) callconv(.C) f80 { + return intToFloat(f80, a); +} diff --git a/lib/compiler_rt/gedf2.zig b/lib/compiler_rt/gedf2.zig new file mode 100644 index 0000000000..816d690fa0 --- /dev/null +++ b/lib/compiler_rt/gedf2.zig @@ -0,0 +1,36 @@ +///! The quoted behavior definitions are from +///! https://gcc.gnu.org/onlinedocs/gcc-12.1.0/gccint/Soft-float-library-routines.html#Soft-float-library-routines +const common = @import("./common.zig"); +const comparef = @import("./comparef.zig"); + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_dcmpge, .{ .name = "__aeabi_dcmpge", .linkage = common.linkage }); + @export(__aeabi_dcmpgt, .{ .name = "__aeabi_dcmpgt", .linkage = common.linkage }); + } else { + @export(__gedf2, .{ .name = "__gedf2", .linkage = common.linkage }); + @export(__gtdf2, .{ .name = "__gtdf2", .linkage = common.linkage }); + } +} + +/// "These functions return a value greater than or equal to zero if neither +/// argument is NaN, and a is greater than or equal to b." +fn __gedf2(a: f64, b: f64) callconv(.C) i32 { + return @enumToInt(comparef.cmpf2(f64, comparef.GE, a, b)); +} + +/// "These functions return a value greater than zero if neither argument is NaN, +/// and a is strictly greater than b." +fn __gtdf2(a: f64, b: f64) callconv(.C) i32 { + return __gedf2(a, b); +} + +fn __aeabi_dcmpge(a: f64, b: f64) callconv(.AAPCS) i32 { + return comparef.cmpf2(f64, comparef.GE, a, b) != .Less; +} + +fn __aeabi_dcmpgt(a: f64, b: f64) callconv(.AAPCS) i32 { + return @boolToInt(comparef.cmpf2(f64, comparef.GE, a, b) == .Greater); +} diff --git a/lib/compiler_rt/gesf2.zig b/lib/compiler_rt/gesf2.zig new file mode 100644 index 0000000000..f29cd173a5 --- /dev/null +++ b/lib/compiler_rt/gesf2.zig @@ -0,0 +1,36 @@ +///! The quoted behavior definitions are from +///! https://gcc.gnu.org/onlinedocs/gcc-12.1.0/gccint/Soft-float-library-routines.html#Soft-float-library-routines +const common = @import("./common.zig"); +const comparef = @import("./comparef.zig"); + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_fcmpge, .{ .name = "__aeabi_fcmpge", .linkage = common.linkage }); + @export(__aeabi_fcmpgt, .{ .name = "__aeabi_fcmpgt", .linkage = common.linkage }); + } else { + @export(__gesf2, .{ .name = "__gesf2", .linkage = common.linkage }); + @export(__gtsf2, .{ .name = "__gtsf2", .linkage = common.linkage }); + } +} + +/// "These functions return a value greater than or equal to zero if neither +/// argument is NaN, and a is greater than or equal to b." +fn __gesf2(a: f32, b: f32) callconv(.C) i32 { + return @enumToInt(comparef.cmpf2(f32, comparef.GE, a, b)); +} + +/// "These functions return a value greater than zero if neither argument is NaN, +/// and a is strictly greater than b." +fn __gtsf2(a: f32, b: f32) callconv(.C) i32 { + return __gesf2(a, b); +} + +fn __aeabi_fcmpge(a: f32, b: f32) callconv(.AAPCS) i32 { + return comparef.cmpf2(f32, comparef.GE, a, b) != .Less; +} + +fn __aeabi_fcmpgt(a: f32, b: f32) callconv(.AAPCS) i32 { + return @boolToInt(comparef.cmpf2(f32, comparef.LE, a, b) == .Greater); +} diff --git a/lib/compiler_rt/getf2.zig b/lib/compiler_rt/getf2.zig new file mode 100644 index 0000000000..402d9ad391 --- /dev/null +++ b/lib/compiler_rt/getf2.zig @@ -0,0 +1,36 @@ +///! The quoted behavior definitions are from +///! https://gcc.gnu.org/onlinedocs/gcc-12.1.0/gccint/Soft-float-library-routines.html#Soft-float-library-routines +const common = @import("./common.zig"); +const comparef = @import("./comparef.zig"); + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__gekf2, .{ .name = "__gekf2", .linkage = common.linkage }); + @export(__gtkf2, .{ .name = "__gtkf2", .linkage = common.linkage }); + } else { + @export(__getf2, .{ .name = "__getf2", .linkage = common.linkage }); + @export(__gttf2, .{ .name = "__gttf2", .linkage = common.linkage }); + } +} + +/// "These functions return a value greater than or equal to zero if neither +/// argument is NaN, and a is greater than or equal to b." +fn __getf2(a: f128, b: f128) callconv(.C) i32 { + return @enumToInt(comparef.cmpf2(f128, comparef.GE, a, b)); +} + +/// "These functions return a value greater than zero if neither argument is NaN, +/// and a is strictly greater than b." +fn __gttf2(a: f128, b: f128) callconv(.C) i32 { + return __getf2(a, b); +} + +fn __gekf2(a: f128, b: f128) callconv(.C) i32 { + return __getf2(a, b); +} + +fn __gtkf2(a: f128, b: f128) callconv(.C) i32 { + return __getf2(a, b); +} diff --git a/lib/compiler_rt/gexf2.zig b/lib/compiler_rt/gexf2.zig new file mode 100644 index 0000000000..6bb88fbb8f --- /dev/null +++ b/lib/compiler_rt/gexf2.zig @@ -0,0 +1,17 @@ +const common = @import("./common.zig"); +const comparef = @import("./comparef.zig"); + +pub const panic = common.panic; + +comptime { + @export(__gexf2, .{ .name = "__gexf2", .linkage = common.linkage }); + @export(__gtxf2, .{ .name = "__gtxf2", .linkage = common.linkage }); +} + +fn __gexf2(a: f80, b: f80) callconv(.C) i32 { + return @enumToInt(comparef.cmp_f80(comparef.GE, a, b)); +} + +fn __gtxf2(a: f80, b: f80) callconv(.C) i32 { + return __gexf2(a, b); +} diff --git a/lib/compiler_rt/int_to_float.zig b/lib/compiler_rt/int_to_float.zig new file mode 100644 index 0000000000..135f9e1dbb --- /dev/null +++ b/lib/compiler_rt/int_to_float.zig @@ -0,0 +1,58 @@ +const Int = @import("std").meta.Int; +const math = @import("std").math; + +pub fn intToFloat(comptime T: type, x: anytype) T { + if (x == 0) return 0; + + // Various constants whose values follow from the type parameters. + // Any reasonable optimizer will fold and propagate all of these. + const Z = Int(.unsigned, @bitSizeOf(@TypeOf(x))); + const uT = Int(.unsigned, @bitSizeOf(T)); + const inf = math.inf(T); + const float_bits = @bitSizeOf(T); + const int_bits = @bitSizeOf(@TypeOf(x)); + const exp_bits = math.floatExponentBits(T); + const fractional_bits = math.floatFractionalBits(T); + const exp_bias = math.maxInt(Int(.unsigned, exp_bits - 1)); + const implicit_bit = if (T != f80) @as(uT, 1) << fractional_bits else 0; + const max_exp = exp_bias; + + // Sign + var abs_val = math.absCast(x); + const sign_bit = if (x < 0) @as(uT, 1) << (float_bits - 1) else 0; + var result: uT = sign_bit; + + // Compute significand + var exp = int_bits - @clz(Z, abs_val) - 1; + if (int_bits <= fractional_bits or exp <= fractional_bits) { + const shift_amt = fractional_bits - @intCast(math.Log2Int(uT), exp); + + // Shift up result to line up with the significand - no rounding required + result = (@intCast(uT, abs_val) << shift_amt); + result ^= implicit_bit; // Remove implicit integer bit + } else { + var shift_amt = @intCast(math.Log2Int(Z), exp - fractional_bits); + const exact_tie: bool = @ctz(Z, abs_val) == shift_amt - 1; + + // Shift down result and remove implicit integer bit + result = @intCast(uT, (abs_val >> (shift_amt - 1))) ^ (implicit_bit << 1); + + // Round result, including round-to-even for exact ties + result = ((result + 1) >> 1) & ~@as(uT, @boolToInt(exact_tie)); + } + + // Compute exponent + if ((int_bits > max_exp) and (exp > max_exp)) // If exponent too large, overflow to infinity + return @bitCast(T, sign_bit | @bitCast(uT, inf)); + + result += (@as(uT, exp) + exp_bias) << math.floatMantissaBits(T); + + // If the result included a carry, we need to restore the explicit integer bit + if (T == f80) result |= 1 << fractional_bits; + + return @bitCast(T, sign_bit | result); +} + +test { + _ = @import("floatXiYf_test.zig"); +} diff --git a/lib/compiler_rt/muldf3.zig b/lib/compiler_rt/muldf3.zig new file mode 100644 index 0000000000..c9142a9ed3 --- /dev/null +++ b/lib/compiler_rt/muldf3.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const mulf3 = @import("./mulf3.zig").mulf3; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_dmul, .{ .name = "__aeabi_dmul", .linkage = common.linkage }); + } else { + @export(__muldf3, .{ .name = "__muldf3", .linkage = common.linkage }); + } +} + +fn __muldf3(a: f64, b: f64) callconv(.C) f64 { + return mulf3(f64, a, b); +} + +fn __aeabi_dmul(a: f64, b: f64) callconv(.C) f64 { + return mulf3(f64, a, b); +} diff --git a/lib/compiler_rt/mulXf3.zig b/lib/compiler_rt/mulf3.zig similarity index 51% rename from lib/compiler_rt/mulXf3.zig rename to lib/compiler_rt/mulf3.zig index dc6ac98407..f6949ee3ce 100644 --- a/lib/compiler_rt/mulXf3.zig +++ b/lib/compiler_rt/mulf3.zig @@ -1,60 +1,11 @@ -// Ported from: -// -// https://github.com/llvm/llvm-project/blob/2ffb1b0413efa9a24eb3c49e710e36f92e2cb50b/compiler-rt/lib/builtins/fp_mul_impl.inc - const std = @import("std"); const math = std.math; const builtin = @import("builtin"); -const arch = builtin.cpu.arch; -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("./common.zig"); -comptime { - @export(__mulsf3, .{ .name = "__mulsf3", .linkage = linkage }); - @export(__muldf3, .{ .name = "__muldf3", .linkage = linkage }); - @export(__mulxf3, .{ .name = "__mulxf3", .linkage = linkage }); - @export(__multf3, .{ .name = "__multf3", .linkage = linkage }); - - if (!is_test) { - if (arch.isARM() or arch.isThumb()) { - @export(__aeabi_fmul, .{ .name = "__aeabi_fmul", .linkage = linkage }); - @export(__aeabi_dmul, .{ .name = "__aeabi_dmul", .linkage = linkage }); - } - - if (arch.isPPC() or arch.isPPC64()) { - @export(__mulkf3, .{ .name = "__mulkf3", .linkage = linkage }); - } - } -} - -pub fn __mulkf3(a: f128, b: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, __multf3, .{ a, b }); -} -pub fn __multf3(a: f128, b: f128) callconv(.C) f128 { - return mulXf3(f128, a, b); -} -pub fn __mulxf3(a: f80, b: f80) callconv(.C) f80 { - return mulXf3(f80, a, b); -} -pub fn __muldf3(a: f64, b: f64) callconv(.C) f64 { - return mulXf3(f64, a, b); -} -pub fn __mulsf3(a: f32, b: f32) callconv(.C) f32 { - return mulXf3(f32, a, b); -} - -pub fn __aeabi_fmul(a: f32, b: f32) callconv(.C) f32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __mulsf3, .{ a, b }); -} - -pub fn __aeabi_dmul(a: f64, b: f64) callconv(.C) f64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __muldf3, .{ a, b }); -} - -pub fn mulXf3(comptime T: type, a: T, b: T) T { +/// Ported from: +/// https://github.com/llvm/llvm-project/blob/2ffb1b0413efa9a24eb3c49e710e36f92e2cb50b/compiler-rt/lib/builtins/fp_mul_impl.inc +pub inline fn mulf3(comptime T: type, a: T, b: T) T { @setRuntimeSafety(builtin.is_test); const typeWidth = @typeInfo(T).Float.bits; const significandBits = math.floatMantissaBits(T); @@ -145,7 +96,7 @@ pub fn mulXf3(comptime T: type, a: T, b: T) T { var productHi: ZSignificand = undefined; var productLo: ZSignificand = undefined; const left_align_shift = ZSignificandBits - fractionalBits - 1; - wideMultiply(ZSignificand, aSignificand, bSignificand << left_align_shift, &productHi, &productLo); + common.wideMultiply(ZSignificand, aSignificand, bSignificand << left_align_shift, &productHi, &productLo); var productExponent: i32 = @intCast(i32, aExponent + bExponent) - exponentBias + scale; @@ -207,141 +158,9 @@ pub fn mulXf3(comptime T: type, a: T, b: T) T { return @bitCast(T, result); } -fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { - @setRuntimeSafety(builtin.is_test); - switch (Z) { - u16 => { - // 16x16 --> 32 bit multiply - const product = @as(u32, a) * @as(u32, b); - hi.* = @intCast(u16, product >> 16); - lo.* = @truncate(u16, product); - }, - u32 => { - // 32x32 --> 64 bit multiply - const product = @as(u64, a) * @as(u64, b); - hi.* = @intCast(u32, product >> 32); - lo.* = @truncate(u32, product); - }, - u64 => { - const S = struct { - fn loWord(x: u64) u64 { - return @truncate(u32, x); - } - fn hiWord(x: u64) u64 { - return @intCast(u32, x >> 32); - } - }; - // 64x64 -> 128 wide multiply for platforms that don't have such an operation; - // many 64-bit platforms have this operation, but they tend to have hardware - // floating-point, so we don't bother with a special case for them here. - // Each of the component 32x32 -> 64 products - const plolo: u64 = S.loWord(a) * S.loWord(b); - const plohi: u64 = S.loWord(a) * S.hiWord(b); - const philo: u64 = S.hiWord(a) * S.loWord(b); - const phihi: u64 = S.hiWord(a) * S.hiWord(b); - // Sum terms that contribute to lo in a way that allows us to get the carry - const r0: u64 = S.loWord(plolo); - const r1: u64 = S.hiWord(plolo) +% S.loWord(plohi) +% S.loWord(philo); - lo.* = r0 +% (r1 << 32); - // Sum terms contributing to hi with the carry from lo - hi.* = S.hiWord(plohi) +% S.hiWord(philo) +% S.hiWord(r1) +% phihi; - }, - u128 => { - const Word_LoMask = @as(u64, 0x00000000ffffffff); - const Word_HiMask = @as(u64, 0xffffffff00000000); - const Word_FullMask = @as(u64, 0xffffffffffffffff); - const S = struct { - fn Word_1(x: u128) u64 { - return @truncate(u32, x >> 96); - } - fn Word_2(x: u128) u64 { - return @truncate(u32, x >> 64); - } - fn Word_3(x: u128) u64 { - return @truncate(u32, x >> 32); - } - fn Word_4(x: u128) u64 { - return @truncate(u32, x); - } - }; - // 128x128 -> 256 wide multiply for platforms that don't have such an operation; - // many 64-bit platforms have this operation, but they tend to have hardware - // floating-point, so we don't bother with a special case for them here. - - const product11: u64 = S.Word_1(a) * S.Word_1(b); - const product12: u64 = S.Word_1(a) * S.Word_2(b); - const product13: u64 = S.Word_1(a) * S.Word_3(b); - const product14: u64 = S.Word_1(a) * S.Word_4(b); - const product21: u64 = S.Word_2(a) * S.Word_1(b); - const product22: u64 = S.Word_2(a) * S.Word_2(b); - const product23: u64 = S.Word_2(a) * S.Word_3(b); - const product24: u64 = S.Word_2(a) * S.Word_4(b); - const product31: u64 = S.Word_3(a) * S.Word_1(b); - const product32: u64 = S.Word_3(a) * S.Word_2(b); - const product33: u64 = S.Word_3(a) * S.Word_3(b); - const product34: u64 = S.Word_3(a) * S.Word_4(b); - const product41: u64 = S.Word_4(a) * S.Word_1(b); - const product42: u64 = S.Word_4(a) * S.Word_2(b); - const product43: u64 = S.Word_4(a) * S.Word_3(b); - const product44: u64 = S.Word_4(a) * S.Word_4(b); - - const sum0: u128 = @as(u128, product44); - const sum1: u128 = @as(u128, product34) +% - @as(u128, product43); - const sum2: u128 = @as(u128, product24) +% - @as(u128, product33) +% - @as(u128, product42); - const sum3: u128 = @as(u128, product14) +% - @as(u128, product23) +% - @as(u128, product32) +% - @as(u128, product41); - const sum4: u128 = @as(u128, product13) +% - @as(u128, product22) +% - @as(u128, product31); - const sum5: u128 = @as(u128, product12) +% - @as(u128, product21); - const sum6: u128 = @as(u128, product11); - - const r0: u128 = (sum0 & Word_FullMask) +% - ((sum1 & Word_LoMask) << 32); - const r1: u128 = (sum0 >> 64) +% - ((sum1 >> 32) & Word_FullMask) +% - (sum2 & Word_FullMask) +% - ((sum3 << 32) & Word_HiMask); - - lo.* = r0 +% (r1 << 64); - hi.* = (r1 >> 64) +% - (sum1 >> 96) +% - (sum2 >> 64) +% - (sum3 >> 32) +% - sum4 +% - (sum5 << 32) +% - (sum6 << 64); - }, - else => @compileError("unsupported"), - } -} - -/// Returns a power-of-two integer type that is large enough to contain -/// the significand of T, including an explicit integer bit -fn PowerOfTwoSignificandZ(comptime T: type) type { - const bits = math.ceilPowerOfTwoAssert(u16, math.floatFractionalBits(T) + 1); - return std.meta.Int(.unsigned, bits); -} - -fn normalize(comptime T: type, significand: *PowerOfTwoSignificandZ(T)) i32 { - @setRuntimeSafety(builtin.is_test); - const Z = PowerOfTwoSignificandZ(T); - const integerBit = @as(Z, 1) << math.floatFractionalBits(T); - - const shift = @clz(Z, significand.*) - @clz(Z, integerBit); - significand.* <<= @intCast(math.Log2Int(Z), shift); - return @as(i32, 1) - shift; -} - -// Returns `true` if the right shift is inexact (i.e. any bit shifted out is non-zero) -// -// This is analogous to an shr version of `@shlWithOverflow` +/// Returns `true` if the right shift is inexact (i.e. any bit shifted out is non-zero) +/// +/// This is analogous to an shr version of `@shlWithOverflow` fn wideShrWithTruncation(comptime Z: type, hi: *Z, lo: *Z, count: u32) bool { @setRuntimeSafety(builtin.is_test); const typeWidth = @typeInfo(Z).Int.bits; @@ -363,6 +182,22 @@ fn wideShrWithTruncation(comptime Z: type, hi: *Z, lo: *Z, count: u32) bool { return inexact; } -test { - _ = @import("mulXf3_test.zig"); +fn normalize(comptime T: type, significand: *PowerOfTwoSignificandZ(T)) i32 { + const Z = PowerOfTwoSignificandZ(T); + const integerBit = @as(Z, 1) << math.floatFractionalBits(T); + + const shift = @clz(Z, significand.*) - @clz(Z, integerBit); + significand.* <<= @intCast(math.Log2Int(Z), shift); + return @as(i32, 1) - shift; +} + +/// Returns a power-of-two integer type that is large enough to contain +/// the significand of T, including an explicit integer bit +fn PowerOfTwoSignificandZ(comptime T: type) type { + const bits = math.ceilPowerOfTwoAssert(u16, math.floatFractionalBits(T) + 1); + return std.meta.Int(.unsigned, bits); +} + +test { + _ = @import("mulf3_test.zig"); } diff --git a/lib/compiler_rt/mulXf3_test.zig b/lib/compiler_rt/mulf3_test.zig similarity index 97% rename from lib/compiler_rt/mulXf3_test.zig rename to lib/compiler_rt/mulf3_test.zig index 6b4f8ca953..0c7d84c5e0 100644 --- a/lib/compiler_rt/mulXf3_test.zig +++ b/lib/compiler_rt/mulf3_test.zig @@ -7,10 +7,10 @@ const math = std.math; const qnan128 = @bitCast(f128, @as(u128, 0x7fff800000000000) << 64); const inf128 = @bitCast(f128, @as(u128, 0x7fff000000000000) << 64); -const __multf3 = @import("mulXf3.zig").__multf3; -const __mulxf3 = @import("mulXf3.zig").__mulxf3; -const __muldf3 = @import("mulXf3.zig").__muldf3; -const __mulsf3 = @import("mulXf3.zig").__mulsf3; +const __multf3 = @import("mulf3.zig").__multf3; +const __mulxf3 = @import("mulf3.zig").__mulxf3; +const __muldf3 = @import("mulf3.zig").__muldf3; +const __mulsf3 = @import("mulf3.zig").__mulsf3; // return true if equal // use two 64-bit integers intead of one 128-bit integer diff --git a/lib/compiler_rt/mulsf3.zig b/lib/compiler_rt/mulsf3.zig new file mode 100644 index 0000000000..a425f9617e --- /dev/null +++ b/lib/compiler_rt/mulsf3.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const mulf3 = @import("./mulf3.zig").mulf3; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_fmul, .{ .name = "__aeabi_fmul", .linkage = common.linkage }); + } else { + @export(__mulsf3, .{ .name = "__mulsf3", .linkage = common.linkage }); + } +} + +fn __mulsf3(a: f32, b: f32) callconv(.C) f32 { + return mulf3(f32, a, b); +} + +fn __aeabi_fmul(a: f32, b: f32) callconv(.C) f32 { + return mulf3(f32, a, b); +} diff --git a/lib/compiler_rt/multf3.zig b/lib/compiler_rt/multf3.zig new file mode 100644 index 0000000000..f71867c9ca --- /dev/null +++ b/lib/compiler_rt/multf3.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const mulf3 = @import("./mulf3.zig").mulf3; + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__mulkf3, .{ .name = "__mulkf3", .linkage = common.linkage }); + } else { + @export(__multf3, .{ .name = "__multf3", .linkage = common.linkage }); + } +} + +fn __multf3(a: f128, b: f128) callconv(.C) f128 { + return mulf3(f128, a, b); +} + +fn __mulkf3(a: f128, b: f128) callconv(.C) f128 { + return mulf3(f128, a, b); +} diff --git a/lib/compiler_rt/mulxf3.zig b/lib/compiler_rt/mulxf3.zig new file mode 100644 index 0000000000..353d27c290 --- /dev/null +++ b/lib/compiler_rt/mulxf3.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const mulf3 = @import("./mulf3.zig").mulf3; + +pub const panic = common.panic; + +comptime { + @export(__mulxf3, .{ .name = "__mulxf3", .linkage = common.linkage }); +} + +pub fn __mulxf3(a: f80, b: f80) callconv(.C) f80 { + return mulf3(f80, a, b); +} diff --git a/lib/compiler_rt/sparc.zig b/lib/compiler_rt/sparc.zig index f96ba4b41a..a39951a1c8 100644 --- a/lib/compiler_rt/sparc.zig +++ b/lib/compiler_rt/sparc.zig @@ -50,7 +50,7 @@ const FCMP = enum(i32) { // Basic arithmetic pub fn _Qp_add(c: *f128, a: *f128, b: *f128) callconv(.C) void { - c.* = @import("addXf3.zig").__addtf3(a.*, b.*); + c.* = @import("addf3.zig").__addtf3(a.*, b.*); } pub fn _Qp_div(c: *f128, a: *f128, b: *f128) callconv(.C) void { @@ -58,11 +58,11 @@ pub fn _Qp_div(c: *f128, a: *f128, b: *f128) callconv(.C) void { } pub fn _Qp_mul(c: *f128, a: *f128, b: *f128) callconv(.C) void { - c.* = @import("mulXf3.zig").__multf3(a.*, b.*); + c.* = @import("mulf3.zig").__multf3(a.*, b.*); } pub fn _Qp_sub(c: *f128, a: *f128, b: *f128) callconv(.C) void { - c.* = @import("addXf3.zig").__subtf3(a.*, b.*); + c.* = @import("addf3.zig").__subtf3(a.*, b.*); } // Comparison diff --git a/lib/compiler_rt/subdf3.zig b/lib/compiler_rt/subdf3.zig new file mode 100644 index 0000000000..9d62ffe480 --- /dev/null +++ b/lib/compiler_rt/subdf3.zig @@ -0,0 +1,21 @@ +const common = @import("./common.zig"); + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_dsub, .{ .name = "__aeabi_dsub", .linkage = common.linkage }); + } else { + @export(__subdf3, .{ .name = "__subdf3", .linkage = common.linkage }); + } +} + +fn __subdf3(a: f64, b: f64) callconv(.C) f64 { + const neg_b = @bitCast(f64, @bitCast(u64, b) ^ (@as(u64, 1) << 63)); + return a + neg_b; +} + +fn __aeabi_dsub(a: f64, b: f64) callconv(.AAPCS) f64 { + const neg_b = @bitCast(f64, @bitCast(u64, b) ^ (@as(u64, 1) << 63)); + return a + neg_b; +} diff --git a/lib/compiler_rt/subsf3.zig b/lib/compiler_rt/subsf3.zig new file mode 100644 index 0000000000..472bccc899 --- /dev/null +++ b/lib/compiler_rt/subsf3.zig @@ -0,0 +1,21 @@ +const common = @import("./common.zig"); + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_fsub, .{ .name = "__aeabi_fsub", .linkage = common.linkage }); + } else { + @export(__subsf3, .{ .name = "__subsf3", .linkage = common.linkage }); + } +} + +fn __subsf3(a: f32, b: f32) callconv(.C) f32 { + const neg_b = @bitCast(f32, @bitCast(u32, b) ^ (@as(u32, 1) << 31)); + return a + neg_b; +} + +fn __aeabi_fsub(a: f32, b: f32) callconv(.AAPCS) f32 { + const neg_b = @bitCast(f32, @bitCast(u32, b) ^ (@as(u32, 1) << 31)); + return a + neg_b; +} diff --git a/lib/compiler_rt/subtf3.zig b/lib/compiler_rt/subtf3.zig new file mode 100644 index 0000000000..a4e2f4dfae --- /dev/null +++ b/lib/compiler_rt/subtf3.zig @@ -0,0 +1,21 @@ +const common = @import("./common.zig"); + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__subkf3, .{ .name = "__subkf3", .linkage = common.linkage }); + } else { + @export(__subtf3, .{ .name = "__subtf3", .linkage = common.linkage }); + } +} + +fn __subtf3(a: f128, b: f128) callconv(.C) f128 { + const neg_b = @bitCast(f128, @bitCast(u128, b) ^ (@as(u128, 1) << 127)); + return a + neg_b; +} + +fn __subkf3(a: f128, b: f128) callconv(.C) f128 { + const neg_b = @bitCast(f128, @bitCast(u128, b) ^ (@as(u128, 1) << 127)); + return a + neg_b; +} diff --git a/lib/compiler_rt/subxf3.zig b/lib/compiler_rt/subxf3.zig new file mode 100644 index 0000000000..a143f10ffe --- /dev/null +++ b/lib/compiler_rt/subxf3.zig @@ -0,0 +1,15 @@ +const std = @import("std"); +const common = @import("./common.zig"); + +pub const panic = common.panic; + +comptime { + @export(__subxf3, .{ .name = "__subxf3", .linkage = common.linkage }); +} + +fn __subxf3(a: f80, b: f80) callconv(.C) f80 { + var b_rep = std.math.break_f80(b); + b_rep.exp ^= 0x8000; + const neg_b = std.math.make_f80(b_rep); + return a + neg_b; +} diff --git a/lib/compiler_rt/trunc_f80.zig b/lib/compiler_rt/trunc_f80.zig deleted file mode 100644 index 43f113ea42..0000000000 --- a/lib/compiler_rt/trunc_f80.zig +++ /dev/null @@ -1,183 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const arch = builtin.cpu.arch; -const testing = std.testing; -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; - -comptime { - @export(__truncxfhf2, .{ .name = "__truncxfhf2", .linkage = linkage }); - @export(__truncxfsf2, .{ .name = "__truncxfsf2", .linkage = linkage }); - @export(__truncxfdf2, .{ .name = "__truncxfdf2", .linkage = linkage }); - @export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage }); -} - -// AArch64 is the only ABI (at the moment) to support f16 arguments without the -// need for extending them to wider fp types. -const F16T = if (arch.isAARCH64()) f16 else u16; - -pub fn __truncxfhf2(a: f80) callconv(.C) F16T { - return @bitCast(F16T, trunc(f16, a)); -} - -pub fn __truncxfsf2(a: f80) callconv(.C) f32 { - return trunc(f32, a); -} - -pub fn __truncxfdf2(a: f80) callconv(.C) f64 { - return trunc(f64, a); -} - -inline fn trunc(comptime dst_t: type, a: f80) dst_t { - @setRuntimeSafety(builtin.is_test); - - const dst_rep_t = std.meta.Int(.unsigned, @typeInfo(dst_t).Float.bits); - const src_sig_bits = std.math.floatMantissaBits(f80) - 1; // -1 for the integer bit - const dst_sig_bits = std.math.floatMantissaBits(dst_t); - - const src_exp_bias = 16383; - - const round_mask = (1 << (src_sig_bits - dst_sig_bits)) - 1; - const halfway = 1 << (src_sig_bits - dst_sig_bits - 1); - - const dst_bits = @typeInfo(dst_t).Float.bits; - const dst_exp_bits = dst_bits - dst_sig_bits - 1; - const dst_inf_exp = (1 << dst_exp_bits) - 1; - const dst_exp_bias = dst_inf_exp >> 1; - - const underflow = src_exp_bias + 1 - dst_exp_bias; - const overflow = src_exp_bias + dst_inf_exp - dst_exp_bias; - - const dst_qnan = 1 << (dst_sig_bits - 1); - const dst_nan_mask = dst_qnan - 1; - - // Break a into a sign and representation of the absolute value - var a_rep = std.math.break_f80(a); - const sign = a_rep.exp & 0x8000; - a_rep.exp &= 0x7FFF; - a_rep.fraction &= 0x7FFFFFFFFFFFFFFF; - var abs_result: dst_rep_t = undefined; - - if (a_rep.exp -% underflow < a_rep.exp -% overflow) { - // The exponent of a is within the range of normal numbers in the - // destination format. We can convert by simply right-shifting with - // rounding and adjusting the exponent. - abs_result = @as(dst_rep_t, a_rep.exp) << dst_sig_bits; - abs_result |= @truncate(dst_rep_t, a_rep.fraction >> (src_sig_bits - dst_sig_bits)); - abs_result -%= @as(dst_rep_t, src_exp_bias - dst_exp_bias) << dst_sig_bits; - - const round_bits = a_rep.fraction & round_mask; - if (round_bits > halfway) { - // Round to nearest - abs_result += 1; - } else if (round_bits == halfway) { - // Ties to even - abs_result += abs_result & 1; - } - } else if (a_rep.exp == 0x7FFF and a_rep.fraction != 0) { - // a is NaN. - // Conjure the result by beginning with infinity, setting the qNaN - // bit and inserting the (truncated) trailing NaN field. - abs_result = @intCast(dst_rep_t, dst_inf_exp) << dst_sig_bits; - abs_result |= dst_qnan; - abs_result |= @intCast(dst_rep_t, (a_rep.fraction >> (src_sig_bits - dst_sig_bits)) & dst_nan_mask); - } else if (a_rep.exp >= overflow) { - // a overflows to infinity. - abs_result = @intCast(dst_rep_t, dst_inf_exp) << dst_sig_bits; - } else { - // a underflows on conversion to the destination type or is an exact - // zero. The result may be a denormal or zero. Extract the exponent - // to get the shift amount for the denormalization. - const shift = src_exp_bias - dst_exp_bias - a_rep.exp; - - // Right shift by the denormalization amount with sticky. - if (shift > src_sig_bits) { - abs_result = 0; - } else { - const sticky = @boolToInt(a_rep.fraction << @intCast(u6, shift) != 0); - const denormalized_significand = a_rep.fraction >> @intCast(u6, shift) | sticky; - abs_result = @intCast(dst_rep_t, denormalized_significand >> (src_sig_bits - dst_sig_bits)); - const round_bits = denormalized_significand & round_mask; - if (round_bits > halfway) { - // Round to nearest - abs_result += 1; - } else if (round_bits == halfway) { - // Ties to even - abs_result += abs_result & 1; - } - } - } - - const result align(@alignOf(dst_t)) = abs_result | @as(dst_rep_t, sign) << dst_bits - 16; - return @bitCast(dst_t, result); -} - -pub fn __trunctfxf2(a: f128) callconv(.C) f80 { - const src_sig_bits = std.math.floatMantissaBits(f128); - const dst_sig_bits = std.math.floatMantissaBits(f80) - 1; // -1 for the integer bit - - // Various constants whose values follow from the type parameters. - // Any reasonable optimizer will fold and propagate all of these. - const src_bits = @typeInfo(f128).Float.bits; - const src_exp_bits = src_bits - src_sig_bits - 1; - const src_inf_exp = 0x7FFF; - - const src_inf = src_inf_exp << src_sig_bits; - const src_sign_mask = 1 << (src_sig_bits + src_exp_bits); - const src_abs_mask = src_sign_mask - 1; - const round_mask = (1 << (src_sig_bits - dst_sig_bits)) - 1; - const halfway = 1 << (src_sig_bits - dst_sig_bits - 1); - - // Break a into a sign and representation of the absolute value - const a_rep = @bitCast(u128, a); - const a_abs = a_rep & src_abs_mask; - const sign: u16 = if (a_rep & src_sign_mask != 0) 0x8000 else 0; - const integer_bit = 1 << 63; - - var res: std.math.F80 = undefined; - - if (a_abs > src_inf) { - // a is NaN. - // Conjure the result by beginning with infinity, setting the qNaN - // bit and inserting the (truncated) trailing NaN field. - res.exp = 0x7fff; - res.fraction = 0x8000000000000000; - res.fraction |= @truncate(u64, a_abs >> (src_sig_bits - dst_sig_bits)); - } else { - // The exponent of a is within the range of normal numbers in the - // destination format. We can convert by simply right-shifting with - // rounding, adding the explicit integer bit, and adjusting the exponent - res.fraction = @truncate(u64, a_abs >> (src_sig_bits - dst_sig_bits)) | integer_bit; - res.exp = @truncate(u16, a_abs >> src_sig_bits); - - const round_bits = a_abs & round_mask; - if (round_bits > halfway) { - // Round to nearest - const carry = @boolToInt(@addWithOverflow(u64, res.fraction, 1, &res.fraction)); - res.exp += carry; - res.fraction |= @as(u64, carry) << 63; // Restore integer bit after carry - } else if (round_bits == halfway) { - // Ties to even - const carry = @boolToInt(@addWithOverflow(u64, res.fraction, res.fraction & 1, &res.fraction)); - res.exp += carry; - res.fraction |= @as(u64, carry) << 63; // Restore integer bit after carry - } - if (res.exp == 0) res.fraction &= ~@as(u64, integer_bit); // Remove integer bit for de-normals - } - - res.exp |= sign; - return std.math.make_f80(res); -} - -fn test__trunctfxf2(a: f128, expected: f80) !void { - const x = __trunctfxf2(a); - try testing.expect(x == expected); -} - -test { - try test__trunctfxf2(1.5, 1.5); - try test__trunctfxf2(2.5, 2.5); - try test__trunctfxf2(-2.5, -2.5); - try test__trunctfxf2(0.0, 0.0); -} diff --git a/lib/compiler_rt/truncdfhf2.zig b/lib/compiler_rt/truncdfhf2.zig new file mode 100644 index 0000000000..6d4135d249 --- /dev/null +++ b/lib/compiler_rt/truncdfhf2.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const truncf = @import("./truncf.zig").truncf; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_d2h, .{ .name = "__aeabi_d2h", .linkage = common.linkage }); + } else { + @export(__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = common.linkage }); + } +} + +fn __truncdfhf2(a: f64) callconv(.C) common.F16T { + return @bitCast(common.F16T, truncf(f16, f64, a)); +} + +fn __aeabi_d2h(a: f64) callconv(.AAPCS) u16 { + return @bitCast(common.F16T, truncf(f16, f64, a)); +} diff --git a/lib/compiler_rt/truncdfsf2.zig b/lib/compiler_rt/truncdfsf2.zig new file mode 100644 index 0000000000..adf7705821 --- /dev/null +++ b/lib/compiler_rt/truncdfsf2.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const truncf = @import("./truncf.zig").truncf; + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_d2f, .{ .name = "__aeabi_d2f", .linkage = common.linkage }); + } else { + @export(__truncdfsf2, .{ .name = "__truncdfsf2", .linkage = common.linkage }); + } +} + +fn __truncdfsf2(a: f64) callconv(.C) f32 { + return truncf(f32, f64, a); +} + +fn __aeabi_d2f(a: f64) callconv(.AAPCS) f32 { + return truncf(f32, f64, a); +} diff --git a/lib/compiler_rt/truncXfYf2.zig b/lib/compiler_rt/truncf.zig similarity index 55% rename from lib/compiler_rt/truncXfYf2.zig rename to lib/compiler_rt/truncf.zig index 77fabcbd75..c012bcee62 100644 --- a/lib/compiler_rt/truncXfYf2.zig +++ b/lib/compiler_rt/truncf.zig @@ -1,93 +1,6 @@ const std = @import("std"); -const builtin = @import("builtin"); -const arch = builtin.cpu.arch; -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; -comptime { - @export(__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = linkage }); - @export(__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = linkage }); - @export(__trunctfdf2, .{ .name = "__trunctfdf2", .linkage = linkage }); - @export(__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = linkage }); - - @export(__truncdfsf2, .{ .name = "__truncdfsf2", .linkage = linkage }); - @export(__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage }); - - if (!is_test) { - @export(__gnu_f2h_ieee, .{ .name = "__gnu_f2h_ieee", .linkage = linkage }); - - if (arch.isARM() or arch.isThumb()) { - @export(__aeabi_d2h, .{ .name = "__aeabi_d2h", .linkage = linkage }); - @export(__aeabi_f2h, .{ .name = "__aeabi_f2h", .linkage = linkage }); - @export(__aeabi_d2f, .{ .name = "__aeabi_d2f", .linkage = linkage }); - } - - if (arch.isPPC() or arch.isPPC64()) { - @export(__trunckfsf2, .{ .name = "__trunckfsf2", .linkage = linkage }); - @export(__trunckfdf2, .{ .name = "__trunckfdf2", .linkage = linkage }); - } - } -} - -// AArch64 is the only ABI (at the moment) to support f16 arguments without the -// need for extending them to wider fp types. -// TODO remove this; do this type selection in the language rather than -// here in compiler-rt. -const F16T = if (arch.isAARCH64()) f16 else u16; - -pub fn __truncsfhf2(a: f32) callconv(.C) F16T { - return @bitCast(F16T, truncXfYf2(f16, f32, a)); -} - -pub fn __gnu_f2h_ieee(a: f32) callconv(.C) F16T { - return @call(.{ .modifier = .always_inline }, __truncsfhf2, .{a}); -} - -pub fn __truncdfhf2(a: f64) callconv(.C) F16T { - return @bitCast(F16T, truncXfYf2(f16, f64, a)); -} - -pub fn __trunctfhf2(a: f128) callconv(.C) F16T { - return @bitCast(F16T, truncXfYf2(f16, f128, a)); -} - -pub fn __trunctfsf2(a: f128) callconv(.C) f32 { - return truncXfYf2(f32, f128, a); -} - -pub fn __trunckfsf2(a: f128) callconv(.C) f32 { - return truncXfYf2(f32, f128, a); -} - -pub fn __trunctfdf2(a: f128) callconv(.C) f64 { - return truncXfYf2(f64, f128, a); -} - -pub fn __trunckfdf2(a: f128) callconv(.C) f64 { - return truncXfYf2(f64, f128, a); -} - -pub fn __truncdfsf2(a: f64) callconv(.C) f32 { - return truncXfYf2(f32, f64, a); -} - -pub fn __aeabi_d2f(a: f64) callconv(.AAPCS) f32 { - @setRuntimeSafety(false); - return truncXfYf2(f32, f64, a); -} - -pub fn __aeabi_d2h(a: f64) callconv(.AAPCS) u16 { - @setRuntimeSafety(false); - return @bitCast(F16T, truncXfYf2(f16, f64, a)); -} - -pub fn __aeabi_f2h(a: f32) callconv(.AAPCS) u16 { - @setRuntimeSafety(false); - return @bitCast(F16T, truncXfYf2(f16, f32, a)); -} - -inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { +pub inline fn truncf(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { const src_rep_t = std.meta.Int(.unsigned, @typeInfo(src_t).Float.bits); const dst_rep_t = std.meta.Int(.unsigned, @typeInfo(dst_t).Float.bits); const srcSigBits = std.math.floatMantissaBits(src_t); @@ -187,6 +100,88 @@ inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t return @bitCast(dst_t, result); } -test { - _ = @import("truncXfYf2_test.zig"); +pub inline fn trunc_f80(comptime dst_t: type, a: f80) dst_t { + const dst_rep_t = std.meta.Int(.unsigned, @typeInfo(dst_t).Float.bits); + const src_sig_bits = std.math.floatMantissaBits(f80) - 1; // -1 for the integer bit + const dst_sig_bits = std.math.floatMantissaBits(dst_t); + + const src_exp_bias = 16383; + + const round_mask = (1 << (src_sig_bits - dst_sig_bits)) - 1; + const halfway = 1 << (src_sig_bits - dst_sig_bits - 1); + + const dst_bits = @typeInfo(dst_t).Float.bits; + const dst_exp_bits = dst_bits - dst_sig_bits - 1; + const dst_inf_exp = (1 << dst_exp_bits) - 1; + const dst_exp_bias = dst_inf_exp >> 1; + + const underflow = src_exp_bias + 1 - dst_exp_bias; + const overflow = src_exp_bias + dst_inf_exp - dst_exp_bias; + + const dst_qnan = 1 << (dst_sig_bits - 1); + const dst_nan_mask = dst_qnan - 1; + + // Break a into a sign and representation of the absolute value + var a_rep = std.math.break_f80(a); + const sign = a_rep.exp & 0x8000; + a_rep.exp &= 0x7FFF; + a_rep.fraction &= 0x7FFFFFFFFFFFFFFF; + var abs_result: dst_rep_t = undefined; + + if (a_rep.exp -% underflow < a_rep.exp -% overflow) { + // The exponent of a is within the range of normal numbers in the + // destination format. We can convert by simply right-shifting with + // rounding and adjusting the exponent. + abs_result = @as(dst_rep_t, a_rep.exp) << dst_sig_bits; + abs_result |= @truncate(dst_rep_t, a_rep.fraction >> (src_sig_bits - dst_sig_bits)); + abs_result -%= @as(dst_rep_t, src_exp_bias - dst_exp_bias) << dst_sig_bits; + + const round_bits = a_rep.fraction & round_mask; + if (round_bits > halfway) { + // Round to nearest + abs_result += 1; + } else if (round_bits == halfway) { + // Ties to even + abs_result += abs_result & 1; + } + } else if (a_rep.exp == 0x7FFF and a_rep.fraction != 0) { + // a is NaN. + // Conjure the result by beginning with infinity, setting the qNaN + // bit and inserting the (truncated) trailing NaN field. + abs_result = @intCast(dst_rep_t, dst_inf_exp) << dst_sig_bits; + abs_result |= dst_qnan; + abs_result |= @intCast(dst_rep_t, (a_rep.fraction >> (src_sig_bits - dst_sig_bits)) & dst_nan_mask); + } else if (a_rep.exp >= overflow) { + // a overflows to infinity. + abs_result = @intCast(dst_rep_t, dst_inf_exp) << dst_sig_bits; + } else { + // a underflows on conversion to the destination type or is an exact + // zero. The result may be a denormal or zero. Extract the exponent + // to get the shift amount for the denormalization. + const shift = src_exp_bias - dst_exp_bias - a_rep.exp; + + // Right shift by the denormalization amount with sticky. + if (shift > src_sig_bits) { + abs_result = 0; + } else { + const sticky = @boolToInt(a_rep.fraction << @intCast(u6, shift) != 0); + const denormalized_significand = a_rep.fraction >> @intCast(u6, shift) | sticky; + abs_result = @intCast(dst_rep_t, denormalized_significand >> (src_sig_bits - dst_sig_bits)); + const round_bits = denormalized_significand & round_mask; + if (round_bits > halfway) { + // Round to nearest + abs_result += 1; + } else if (round_bits == halfway) { + // Ties to even + abs_result += abs_result & 1; + } + } + } + + const result align(@alignOf(dst_t)) = abs_result | @as(dst_rep_t, sign) << dst_bits - 16; + return @bitCast(dst_t, result); +} + +test { + _ = @import("truncf_test.zig"); } diff --git a/lib/compiler_rt/truncXfYf2_test.zig b/lib/compiler_rt/truncf_test.zig similarity index 96% rename from lib/compiler_rt/truncXfYf2_test.zig rename to lib/compiler_rt/truncf_test.zig index 3f11dd0380..58c02a0a21 100644 --- a/lib/compiler_rt/truncXfYf2_test.zig +++ b/lib/compiler_rt/truncf_test.zig @@ -1,5 +1,7 @@ const std = @import("std"); +const testing = std.testing; const __truncsfhf2 = @import("truncXfYf2.zig").__truncsfhf2; +const __trunctfxf2 = @import("trunctfxf2.zig").__trunctfxf2; fn test__truncsfhf2(a: u32, expected: u16) !void { const actual = @bitCast(u16, __truncsfhf2(@bitCast(f32, a))); @@ -294,3 +296,15 @@ test "trunctfhf2" { test__trunctfhf2(0x1.234eebb5faa678f4488693abcdefp+453, 0x7c00); test__trunctfhf2(0x1.edcba9bb8c76a5a43dd21f334634p-43, 0x0); } + +test "trunctfxf2" { + try test__trunctfxf2(1.5, 1.5); + try test__trunctfxf2(2.5, 2.5); + try test__trunctfxf2(-2.5, -2.5); + try test__trunctfxf2(0.0, 0.0); +} + +fn test__trunctfxf2(a: f128, expected: f80) !void { + const x = __trunctfxf2(a); + try testing.expect(x == expected); +} diff --git a/lib/compiler_rt/truncsfhf2.zig b/lib/compiler_rt/truncsfhf2.zig new file mode 100644 index 0000000000..5fe0f329b6 --- /dev/null +++ b/lib/compiler_rt/truncsfhf2.zig @@ -0,0 +1,26 @@ +const common = @import("./common.zig"); +const truncf = @import("./truncf.zig").truncf; + +pub const panic = common.panic; + +comptime { + if (common.want_gnu_abi) { + @export(__gnu_f2h_ieee, .{ .name = "__gnu_f2h_ieee", .linkage = common.linkage }); + } else if (common.want_aeabi) { + @export(__aeabi_f2h, .{ .name = "__aeabi_f2h", .linkage = common.linkage }); + } else { + @export(__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = common.linkage }); + } +} + +fn __truncsfhf2(a: f32) callconv(.C) common.F16T { + return @bitCast(common.F16T, truncf(f16, f32, a)); +} + +fn __gnu_f2h_ieee(a: f32) callconv(.C) common.F16T { + return @bitCast(common.F16T, truncf(f16, f32, a)); +} + +fn __aeabi_f2h(a: f32) callconv(.AAPCS) u16 { + return @bitCast(common.F16T, truncf(f16, f32, a)); +} diff --git a/lib/compiler_rt/trunctfdf2.zig b/lib/compiler_rt/trunctfdf2.zig new file mode 100644 index 0000000000..012de610c3 --- /dev/null +++ b/lib/compiler_rt/trunctfdf2.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const truncf = @import("./truncf.zig").truncf; + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__trunckfdf2, .{ .name = "__trunckfdf2", .linkage = common.linkage }); + } else { + @export(__trunctfdf2, .{ .name = "__trunctfdf2", .linkage = common.linkage }); + } +} + +fn __trunctfdf2(a: f128) callconv(.C) f64 { + return truncf(f64, f128, a); +} + +fn __trunckfdf2(a: f128) callconv(.C) f64 { + return truncf(f64, f128, a); +} diff --git a/lib/compiler_rt/trunctfhf2.zig b/lib/compiler_rt/trunctfhf2.zig new file mode 100644 index 0000000000..a81cc138f1 --- /dev/null +++ b/lib/compiler_rt/trunctfhf2.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const truncf = @import("./truncf.zig").truncf; + +pub const panic = common.panic; + +comptime { + @export(__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = common.linkage }); +} + +fn __trunctfhf2(a: f128) callconv(.C) common.F16T { + return @bitCast(common.F16T, truncf(f16, f128, a)); +} diff --git a/lib/compiler_rt/trunctfsf2.zig b/lib/compiler_rt/trunctfsf2.zig new file mode 100644 index 0000000000..535f73e473 --- /dev/null +++ b/lib/compiler_rt/trunctfsf2.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const truncf = @import("./truncf.zig").truncf; + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__trunckfsf2, .{ .name = "__trunckfsf2", .linkage = common.linkage }); + } else { + @export(__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = common.linkage }); + } +} + +fn __trunctfsf2(a: f128) callconv(.C) f32 { + return truncf(f32, f128, a); +} + +fn __trunckfsf2(a: f128) callconv(.C) f32 { + return truncf(f32, f128, a); +} diff --git a/lib/compiler_rt/trunctfxf2.zig b/lib/compiler_rt/trunctfxf2.zig new file mode 100644 index 0000000000..0cb7f86a1f --- /dev/null +++ b/lib/compiler_rt/trunctfxf2.zig @@ -0,0 +1,66 @@ +const math = @import("std").math; +const common = @import("./common.zig"); +const trunc_f80 = @import("./truncf.zig").trunc_f80; + +pub const panic = common.panic; + +comptime { + @export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = common.linkage }); +} + +fn __trunctfxf2(a: f128) callconv(.C) f80 { + const src_sig_bits = math.floatMantissaBits(f128); + const dst_sig_bits = math.floatMantissaBits(f80) - 1; // -1 for the integer bit + + // Various constants whose values follow from the type parameters. + // Any reasonable optimizer will fold and propagate all of these. + const src_bits = @typeInfo(f128).Float.bits; + const src_exp_bits = src_bits - src_sig_bits - 1; + const src_inf_exp = 0x7FFF; + + const src_inf = src_inf_exp << src_sig_bits; + const src_sign_mask = 1 << (src_sig_bits + src_exp_bits); + const src_abs_mask = src_sign_mask - 1; + const round_mask = (1 << (src_sig_bits - dst_sig_bits)) - 1; + const halfway = 1 << (src_sig_bits - dst_sig_bits - 1); + + // Break a into a sign and representation of the absolute value + const a_rep = @bitCast(u128, a); + const a_abs = a_rep & src_abs_mask; + const sign: u16 = if (a_rep & src_sign_mask != 0) 0x8000 else 0; + const integer_bit = 1 << 63; + + var res: math.F80 = undefined; + + if (a_abs > src_inf) { + // a is NaN. + // Conjure the result by beginning with infinity, setting the qNaN + // bit and inserting the (truncated) trailing NaN field. + res.exp = 0x7fff; + res.fraction = 0x8000000000000000; + res.fraction |= @truncate(u64, a_abs >> (src_sig_bits - dst_sig_bits)); + } else { + // The exponent of a is within the range of normal numbers in the + // destination format. We can convert by simply right-shifting with + // rounding, adding the explicit integer bit, and adjusting the exponent + res.fraction = @truncate(u64, a_abs >> (src_sig_bits - dst_sig_bits)) | integer_bit; + res.exp = @truncate(u16, a_abs >> src_sig_bits); + + const round_bits = a_abs & round_mask; + if (round_bits > halfway) { + // Round to nearest + const carry = @boolToInt(@addWithOverflow(u64, res.fraction, 1, &res.fraction)); + res.exp += carry; + res.fraction |= @as(u64, carry) << 63; // Restore integer bit after carry + } else if (round_bits == halfway) { + // Ties to even + const carry = @boolToInt(@addWithOverflow(u64, res.fraction, res.fraction & 1, &res.fraction)); + res.exp += carry; + res.fraction |= @as(u64, carry) << 63; // Restore integer bit after carry + } + if (res.exp == 0) res.fraction &= ~@as(u64, integer_bit); // Remove integer bit for de-normals + } + + res.exp |= sign; + return math.make_f80(res); +} diff --git a/lib/compiler_rt/truncxfdf2.zig b/lib/compiler_rt/truncxfdf2.zig new file mode 100644 index 0000000000..2b8eaaab8c --- /dev/null +++ b/lib/compiler_rt/truncxfdf2.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const trunc_f80 = @import("./truncf.zig").trunc_f80; + +pub const panic = common.panic; + +comptime { + @export(__truncxfdf2, .{ .name = "__truncxfdf2", .linkage = common.linkage }); +} + +fn __truncxfdf2(a: f80) callconv(.C) f64 { + return trunc_f80(f64, a); +} diff --git a/lib/compiler_rt/truncxfhf2.zig b/lib/compiler_rt/truncxfhf2.zig new file mode 100644 index 0000000000..75fdd17841 --- /dev/null +++ b/lib/compiler_rt/truncxfhf2.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const trunc_f80 = @import("./truncf.zig").trunc_f80; + +pub const panic = common.panic; + +comptime { + @export(__truncxfhf2, .{ .name = "__truncxfhf2", .linkage = common.linkage }); +} + +fn __truncxfhf2(a: f80) callconv(.C) common.F16T { + return @bitCast(common.F16T, trunc_f80(f16, a)); +} diff --git a/lib/compiler_rt/truncxfsf2.zig b/lib/compiler_rt/truncxfsf2.zig new file mode 100644 index 0000000000..57c0cb7bdf --- /dev/null +++ b/lib/compiler_rt/truncxfsf2.zig @@ -0,0 +1,12 @@ +const common = @import("./common.zig"); +const trunc_f80 = @import("./truncf.zig").trunc_f80; + +pub const panic = common.panic; + +comptime { + @export(__truncxfsf2, .{ .name = "__truncxfsf2", .linkage = common.linkage }); +} + +fn __truncxfsf2(a: f80) callconv(.C) f32 { + return trunc_f80(f32, a); +} diff --git a/lib/compiler_rt/unorddf2.zig b/lib/compiler_rt/unorddf2.zig new file mode 100644 index 0000000000..a6538f99d5 --- /dev/null +++ b/lib/compiler_rt/unorddf2.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const comparef = @import("./comparef.zig"); + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_dcmpun, .{ .name = "__aeabi_dcmpun", .linkage = common.linkage }); + } else { + @export(__unorddf2, .{ .name = "__unorddf2", .linkage = common.linkage }); + } +} + +fn __unorddf2(a: f64, b: f64) callconv(.C) i32 { + return comparef.unordcmp(f64, a, b); +} + +fn __aeabi_dcmpun(a: f64, b: f64) callconv(.AAPCS) i32 { + return comparef.unordcmp(f64, a, b); +} diff --git a/lib/compiler_rt/unordsf2.zig b/lib/compiler_rt/unordsf2.zig new file mode 100644 index 0000000000..1dc7eb178e --- /dev/null +++ b/lib/compiler_rt/unordsf2.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const comparef = @import("./comparef.zig"); + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_fcmpun, .{ .name = "__aeabi_fcmpun", .linkage = common.linkage }); + } else { + @export(__unordsf2, .{ .name = "__unordsf2", .linkage = common.linkage }); + } +} + +fn __unordsf2(a: f32, b: f32) callconv(.C) i32 { + return comparef.unordcmp(f32, a, b); +} + +fn __aeabi_fcmpun(a: f32, b: f32) callconv(.AAPCS) i32 { + return comparef.unordcmp(f32, a, b); +} diff --git a/lib/compiler_rt/unordtf2.zig b/lib/compiler_rt/unordtf2.zig new file mode 100644 index 0000000000..833d6d6cee --- /dev/null +++ b/lib/compiler_rt/unordtf2.zig @@ -0,0 +1,20 @@ +const common = @import("./common.zig"); +const comparef = @import("./comparef.zig"); + +pub const panic = common.panic; + +comptime { + if (common.want_ppc_abi) { + @export(__unordkf2, .{ .name = "__unordkf2", .linkage = common.linkage }); + } else { + @export(__unordtf2, .{ .name = "__unordtf2", .linkage = common.linkage }); + } +} + +fn __unordtf2(a: f128, b: f128) callconv(.C) i32 { + return comparef.unordcmp(f128, a, b); +} + +fn __unordkf2(a: f128, b: f128) callconv(.C) i32 { + return comparef.unordcmp(f128, a, b); +} diff --git a/src/compiler_rt.zig b/src/compiler_rt.zig index 5149ce192f..3418886a42 100644 --- a/src/compiler_rt.zig +++ b/src/compiler_rt.zig @@ -200,8 +200,8 @@ const sources = &[_][]const u8{ "compiler_rt/umodti3.zig", "compiler_rt/truncXfYf2.zig", "compiler_rt/trunc_f80.zig", - "compiler_rt/addXf3.zig", - "compiler_rt/mulXf3.zig", + "compiler_rt/addf3.zig", + "compiler_rt/mulf3.zig", "compiler_rt/divsf3.zig", "compiler_rt/divdf3.zig", "compiler_rt/divxf3.zig", From 25671f5a973c209bdf75f8be567cc5e441a865cb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Jun 2022 14:18:08 -0700 Subject: [PATCH 1854/2031] compiler-rt: move SPARC functions into appropriate compilation units --- CMakeLists.txt | 1 - lib/compiler_rt.zig | 2 - lib/compiler_rt/addtf3.zig | 6 ++ lib/compiler_rt/cmptf2.zig | 49 +++++++++++ lib/compiler_rt/common.zig | 1 + lib/compiler_rt/divtf3.zig | 31 ++++--- lib/compiler_rt/extenddftf2.zig | 7 ++ lib/compiler_rt/extendsftf2.zig | 6 ++ lib/compiler_rt/fixtfdi.zig | 6 ++ lib/compiler_rt/fixtfsi.zig | 6 ++ lib/compiler_rt/fixunstfdi.zig | 6 ++ lib/compiler_rt/fixunstfsi.zig | 6 ++ lib/compiler_rt/floatditf.zig | 6 ++ lib/compiler_rt/floatsitf.zig | 6 ++ lib/compiler_rt/floatunditf.zig | 6 ++ lib/compiler_rt/floatunsitf.zig | 6 ++ lib/compiler_rt/getf2.zig | 3 + lib/compiler_rt/multf3.zig | 6 ++ lib/compiler_rt/sparc.zig | 148 -------------------------------- lib/compiler_rt/subtf3.zig | 13 ++- lib/compiler_rt/trunctfdf2.zig | 6 ++ lib/compiler_rt/trunctfsf2.zig | 6 ++ lib/compiler_rt/unordtf2.zig | 3 + src/compiler_rt.zig | 1 - 24 files changed, 170 insertions(+), 167 deletions(-) delete mode 100644 lib/compiler_rt/sparc.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index a393249458..2a92a40dc9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -619,7 +619,6 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/compiler_rt/shift.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/sin.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/sincos.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/sparc.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/sqrt.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/stack_probe.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subo.zig" diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig index 826530286d..5f199e9eae 100644 --- a/lib/compiler_rt.zig +++ b/lib/compiler_rt.zig @@ -179,6 +179,4 @@ comptime { _ = @import("compiler_rt/aulldiv.zig"); _ = @import("compiler_rt/aullrem.zig"); _ = @import("compiler_rt/clear_cache.zig"); - - _ = @import("compiler_rt/sparc.zig"); } diff --git a/lib/compiler_rt/addtf3.zig b/lib/compiler_rt/addtf3.zig index 40a6e6c8b7..f7c33ba969 100644 --- a/lib/compiler_rt/addtf3.zig +++ b/lib/compiler_rt/addtf3.zig @@ -6,6 +6,8 @@ pub const panic = common.panic; comptime { if (common.want_ppc_abi) { @export(__addkf3, .{ .name = "__addkf3", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + @export(_Qp_add, .{ .name = "_Qp_add", .linkage = common.linkage }); } else { @export(__addtf3, .{ .name = "__addtf3", .linkage = common.linkage }); } @@ -18,3 +20,7 @@ fn __addtf3(a: f128, b: f128) callconv(.C) f128 { fn __addkf3(a: f128, b: f128) callconv(.C) f128 { return addf3(f128, a, b); } + +fn _Qp_add(c: *f128, a: *f128, b: *f128) callconv(.C) void { + c.* = addf3(f128, a.*, b.*); +} diff --git a/lib/compiler_rt/cmptf2.zig b/lib/compiler_rt/cmptf2.zig index 86dc68ad56..00263f943a 100644 --- a/lib/compiler_rt/cmptf2.zig +++ b/lib/compiler_rt/cmptf2.zig @@ -11,6 +11,14 @@ comptime { @export(__nekf2, .{ .name = "__nekf2", .linkage = common.linkage }); @export(__ltkf2, .{ .name = "__ltkf2", .linkage = common.linkage }); @export(__lekf2, .{ .name = "__lekf2", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + @export(_Qp_cmp, .{ .name = "_Qp_cmp", .linkage = common.linkage }); + @export(_Qp_feq, .{ .name = "_Qp_feq", .linkage = common.linkage }); + @export(_Qp_fne, .{ .name = "_Qp_fne", .linkage = common.linkage }); + @export(_Qp_flt, .{ .name = "_Qp_flt", .linkage = common.linkage }); + @export(_Qp_fle, .{ .name = "_Qp_fle", .linkage = common.linkage }); + @export(_Qp_fgt, .{ .name = "_Qp_fgt", .linkage = common.linkage }); + @export(_Qp_fge, .{ .name = "_Qp_fge", .linkage = common.linkage }); } else { @export(__eqtf2, .{ .name = "__eqtf2", .linkage = common.linkage }); @export(__netf2, .{ .name = "__netf2", .linkage = common.linkage }); @@ -71,3 +79,44 @@ fn __ltkf2(a: f128, b: f128) callconv(.C) i32 { fn __lekf2(a: f128, b: f128) callconv(.C) i32 { return __cmptf2(a, b); } + +const SparcFCMP = enum(i32) { + Equal = 0, + Less = 1, + Greater = 2, + Unordered = 3, +}; + +fn _Qp_cmp(a: *const f128, b: *const f128) callconv(.C) i32 { + return @enumToInt(comparef.cmpf2(f128, SparcFCMP, a.*, b.*)); +} + +fn _Qp_feq(a: *const f128, b: *const f128) callconv(.C) bool { + return @intToEnum(SparcFCMP, _Qp_cmp(a, b)) == .Equal; +} + +fn _Qp_fne(a: *const f128, b: *const f128) callconv(.C) bool { + return @intToEnum(SparcFCMP, _Qp_cmp(a, b)) != .Equal; +} + +fn _Qp_flt(a: *const f128, b: *const f128) callconv(.C) bool { + return @intToEnum(SparcFCMP, _Qp_cmp(a, b)) == .Less; +} + +fn _Qp_fgt(a: *const f128, b: *const f128) callconv(.C) bool { + return @intToEnum(SparcFCMP, _Qp_cmp(a, b)) == .Greater; +} + +fn _Qp_fge(a: *const f128, b: *const f128) callconv(.C) bool { + return switch (@intToEnum(SparcFCMP, _Qp_cmp(a, b))) { + .Equal, .Greater => true, + .Less, .Unordered => false, + }; +} + +fn _Qp_fle(a: *const f128, b: *const f128) callconv(.C) bool { + return switch (@intToEnum(SparcFCMP, _Qp_cmp(a, b))) { + .Equal, .Less => true, + .Greater, .Unordered => false, + }; +} diff --git a/lib/compiler_rt/common.zig b/lib/compiler_rt/common.zig index 196dc33024..b1462d21c1 100644 --- a/lib/compiler_rt/common.zig +++ b/lib/compiler_rt/common.zig @@ -8,6 +8,7 @@ pub const want_aeabi = builtin.cpu.arch.isARM() or builtin.cpu.arch.isThumb(); pub const want_ppc_abi = builtin.cpu.arch.isPPC() or builtin.cpu.arch.isPPC64(); pub const want_msvc_abi = builtin.abi == .msvc; pub const want_gnu_abi = builtin.abi.isGnu(); +pub const want_sparc_abi = builtin.cpu.arch.isSPARC(); // Avoid dragging in the runtime safety mechanisms into this .o file, // unless we're trying to test compiler-rt. diff --git a/lib/compiler_rt/divtf3.zig b/lib/compiler_rt/divtf3.zig index 436db41dc2..66e01e1843 100644 --- a/lib/compiler_rt/divtf3.zig +++ b/lib/compiler_rt/divtf3.zig @@ -1,30 +1,35 @@ const std = @import("std"); const builtin = @import("builtin"); -const arch = builtin.cpu.arch; -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; const common = @import("common.zig"); const normalize = common.normalize; const wideMultiply = common.wideMultiply; + pub const panic = common.panic; comptime { - @export(__divtf3, .{ .name = "__divtf3", .linkage = linkage }); - - if (!is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(__divkf3, .{ .name = "__divkf3", .linkage = linkage }); - } + if (common.want_ppc_abi) { + @export(__divkf3, .{ .name = "__divkf3", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + @export(_Qp_div, .{ .name = "_Qp_div", .linkage = common.linkage }); + } else { + @export(__divtf3, .{ .name = "__divtf3", .linkage = common.linkage }); } } -pub fn __divkf3(a: f128, b: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, __divtf3, .{ a, b }); +fn __divkf3(a: f128, b: f128) callconv(.C) f128 { + return div(a, b); } -pub fn __divtf3(a: f128, b: f128) callconv(.C) f128 { - @setRuntimeSafety(builtin.is_test); +fn _Qp_div(c: *f128, a: *const f128, b: *const f128) callconv(.C) void { + c.* = div(a.*, b.*); +} + +fn __divtf3(a: f128, b: f128) callconv(.C) f128 { + return div(a, b); +} + +inline fn div(a: f128, b: f128) f128 { const Z = std.meta.Int(.unsigned, 128); const significandBits = std.math.floatMantissaBits(f128); diff --git a/lib/compiler_rt/extenddftf2.zig b/lib/compiler_rt/extenddftf2.zig index c957d64a63..fa0053ad5c 100644 --- a/lib/compiler_rt/extenddftf2.zig +++ b/lib/compiler_rt/extenddftf2.zig @@ -6,6 +6,8 @@ pub const panic = common.panic; comptime { if (common.want_ppc_abi) { @export(__extenddfkf2, .{ .name = "__extenddfkf2", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + @export(_Qp_dtoq, .{ .name = "_Qp_dtoq", .linkage = common.linkage }); } else { @export(__extenddftf2, .{ .name = "__extenddftf2", .linkage = common.linkage }); } @@ -18,3 +20,8 @@ fn __extenddftf2(a: f64) callconv(.C) f128 { fn __extenddfkf2(a: f64) callconv(.C) f128 { return extendf(f128, f64, @bitCast(u64, a)); } + +fn _Qp_dtoq(c: *f128, a: f64) callconv(.C) void { + c.* = @import("extendXfYf2.zig").__extenddftf2(a); + c.* = extendf(f128, f64, @bitCast(u64, a)); +} diff --git a/lib/compiler_rt/extendsftf2.zig b/lib/compiler_rt/extendsftf2.zig index 8b496783c8..a3b29802e6 100644 --- a/lib/compiler_rt/extendsftf2.zig +++ b/lib/compiler_rt/extendsftf2.zig @@ -6,6 +6,8 @@ pub const panic = common.panic; comptime { if (common.want_ppc_abi) { @export(__extendsfkf2, .{ .name = "__extendsfkf2", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + @export(_Qp_stoq, .{ .name = "_Qp_stoq", .linkage = common.linkage }); } else { @export(__extendsftf2, .{ .name = "__extendsftf2", .linkage = common.linkage }); } @@ -18,3 +20,7 @@ fn __extendsftf2(a: f32) callconv(.C) f128 { fn __extendsfkf2(a: f32) callconv(.C) f128 { return extendf(f128, f32, @bitCast(u32, a)); } + +fn _Qp_stoq(c: *f128, a: f32) callconv(.C) void { + c.* = extendf(f128, f32, @bitCast(u32, a)); +} diff --git a/lib/compiler_rt/fixtfdi.zig b/lib/compiler_rt/fixtfdi.zig index 8f8d473b1f..20bc4d89d6 100644 --- a/lib/compiler_rt/fixtfdi.zig +++ b/lib/compiler_rt/fixtfdi.zig @@ -6,6 +6,8 @@ pub const panic = common.panic; comptime { if (common.want_ppc_abi) { @export(__fixkfdi, .{ .name = "__fixkfdi", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + @export(_Qp_qtox, .{ .name = "_Qp_qtox", .linkage = common.linkage }); } else { @export(__fixtfdi, .{ .name = "__fixtfdi", .linkage = common.linkage }); } @@ -18,3 +20,7 @@ fn __fixtfdi(a: f128) callconv(.C) i64 { fn __fixkfdi(a: f128) callconv(.C) i64 { return floatToInt(i64, a); } + +fn _Qp_qtox(a: *const f128) callconv(.C) i64 { + return floatToInt(i64, a.*); +} diff --git a/lib/compiler_rt/fixtfsi.zig b/lib/compiler_rt/fixtfsi.zig index 507581d0cf..44cac245fb 100644 --- a/lib/compiler_rt/fixtfsi.zig +++ b/lib/compiler_rt/fixtfsi.zig @@ -6,6 +6,8 @@ pub const panic = common.panic; comptime { if (common.want_ppc_abi) { @export(__fixkfsi, .{ .name = "__fixkfsi", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + @export(_Qp_qtoi, .{ .name = "_Qp_qtoi", .linkage = common.linkage }); } else { @export(__fixtfsi, .{ .name = "__fixtfsi", .linkage = common.linkage }); } @@ -18,3 +20,7 @@ fn __fixtfsi(a: f128) callconv(.C) i32 { fn __fixkfsi(a: f128) callconv(.C) i32 { return floatToInt(i32, a); } + +fn _Qp_qtoi(a: *const f128) callconv(.C) i32 { + return floatToInt(i32, a.*); +} diff --git a/lib/compiler_rt/fixunstfdi.zig b/lib/compiler_rt/fixunstfdi.zig index 0130f21139..f8b9aa7ac4 100644 --- a/lib/compiler_rt/fixunstfdi.zig +++ b/lib/compiler_rt/fixunstfdi.zig @@ -6,6 +6,8 @@ pub const panic = common.panic; comptime { if (common.want_ppc_abi) { @export(__fixunskfdi, .{ .name = "__fixunskfdi", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + @export(_Qp_qtoux, .{ .name = "_Qp_qtoux", .linkage = common.linkage }); } else { @export(__fixunstfdi, .{ .name = "__fixunstfdi", .linkage = common.linkage }); } @@ -18,3 +20,7 @@ fn __fixunstfdi(a: f128) callconv(.C) u64 { fn __fixunskfdi(a: f128) callconv(.C) u64 { return floatToInt(u64, a); } + +fn _Qp_qtoux(a: *const f128) callconv(.C) u64 { + return floatToInt(u64, a.*); +} diff --git a/lib/compiler_rt/fixunstfsi.zig b/lib/compiler_rt/fixunstfsi.zig index 78e2cc83d4..64f70af649 100644 --- a/lib/compiler_rt/fixunstfsi.zig +++ b/lib/compiler_rt/fixunstfsi.zig @@ -6,6 +6,8 @@ pub const panic = common.panic; comptime { if (common.want_ppc_abi) { @export(__fixunskfsi, .{ .name = "__fixunskfsi", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + @export(_Qp_qtoui, .{ .name = "_Qp_qtoui", .linkage = common.linkage }); } else { @export(__fixunstfsi, .{ .name = "__fixunstfsi", .linkage = common.linkage }); } @@ -18,3 +20,7 @@ fn __fixunstfsi(a: f128) callconv(.C) u32 { fn __fixunskfsi(a: f128) callconv(.C) u32 { return floatToInt(u32, a); } + +fn _Qp_qtoui(a: *const f128) callconv(.C) u32 { + return floatToInt(u32, a.*); +} diff --git a/lib/compiler_rt/floatditf.zig b/lib/compiler_rt/floatditf.zig index 04132b9c24..eb1ce3337b 100644 --- a/lib/compiler_rt/floatditf.zig +++ b/lib/compiler_rt/floatditf.zig @@ -6,6 +6,8 @@ pub const panic = common.panic; comptime { if (common.want_ppc_abi) { @export(__floatdikf, .{ .name = "__floatdikf", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + @export(_Qp_xtoq, .{ .name = "_Qp_xtoq", .linkage = common.linkage }); } else { @export(__floatditf, .{ .name = "__floatditf", .linkage = common.linkage }); } @@ -18,3 +20,7 @@ fn __floatditf(a: i64) callconv(.C) f128 { fn __floatdikf(a: i64) callconv(.C) f128 { return intToFloat(f128, a); } + +fn _Qp_xtoq(c: *f128, a: i64) callconv(.C) void { + c.* = intToFloat(f128, a); +} diff --git a/lib/compiler_rt/floatsitf.zig b/lib/compiler_rt/floatsitf.zig index 24f5e3bf42..0f43800436 100644 --- a/lib/compiler_rt/floatsitf.zig +++ b/lib/compiler_rt/floatsitf.zig @@ -6,6 +6,8 @@ pub const panic = common.panic; comptime { if (common.want_ppc_abi) { @export(__floatsikf, .{ .name = "__floatsikf", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + @export(_Qp_itoq, .{ .name = "_Qp_itoq", .linkage = common.linkage }); } else { @export(__floatsitf, .{ .name = "__floatsitf", .linkage = common.linkage }); } @@ -18,3 +20,7 @@ fn __floatsitf(a: i32) callconv(.C) f128 { fn __floatsikf(a: i32) callconv(.C) f128 { return intToFloat(f128, a); } + +fn _Qp_itoq(c: *f128, a: i32) callconv(.C) void { + c.* = intToFloat(f128, a); +} diff --git a/lib/compiler_rt/floatunditf.zig b/lib/compiler_rt/floatunditf.zig index 40424d1a6c..ed752e40c2 100644 --- a/lib/compiler_rt/floatunditf.zig +++ b/lib/compiler_rt/floatunditf.zig @@ -6,6 +6,8 @@ pub const panic = common.panic; comptime { if (common.want_ppc_abi) { @export(__floatundikf, .{ .name = "__floatundikf", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + @export(_Qp_uxtoq, .{ .name = "_Qp_uxtoq", .linkage = common.linkage }); } else { @export(__floatunditf, .{ .name = "__floatunditf", .linkage = common.linkage }); } @@ -18,3 +20,7 @@ fn __floatunditf(a: u64) callconv(.C) f128 { fn __floatundikf(a: u64) callconv(.C) f128 { return intToFloat(f128, a); } + +fn _Qp_uxtoq(c: *f128, a: u64) callconv(.C) void { + c.* = intToFloat(f128, a); +} diff --git a/lib/compiler_rt/floatunsitf.zig b/lib/compiler_rt/floatunsitf.zig index 3f626324d2..54d099c82a 100644 --- a/lib/compiler_rt/floatunsitf.zig +++ b/lib/compiler_rt/floatunsitf.zig @@ -6,6 +6,8 @@ pub const panic = common.panic; comptime { if (common.want_ppc_abi) { @export(__floatunsikf, .{ .name = "__floatunsikf", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + @export(_Qp_uitoq, .{ .name = "_Qp_uitoq", .linkage = common.linkage }); } else { @export(__floatunsitf, .{ .name = "__floatunsitf", .linkage = common.linkage }); } @@ -18,3 +20,7 @@ fn __floatunsitf(a: u32) callconv(.C) f128 { fn __floatunsikf(a: u32) callconv(.C) f128 { return intToFloat(f128, a); } + +fn _Qp_uitoq(c: *f128, a: u32) callconv(.C) void { + c.* = intToFloat(f128, a); +} diff --git a/lib/compiler_rt/getf2.zig b/lib/compiler_rt/getf2.zig index 402d9ad391..8d9d39c1f9 100644 --- a/lib/compiler_rt/getf2.zig +++ b/lib/compiler_rt/getf2.zig @@ -9,6 +9,9 @@ comptime { if (common.want_ppc_abi) { @export(__gekf2, .{ .name = "__gekf2", .linkage = common.linkage }); @export(__gtkf2, .{ .name = "__gtkf2", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + // These exports are handled in cmptf2.zig because gt and ge on sparc + // are based on calling _Qp_cmp. } else { @export(__getf2, .{ .name = "__getf2", .linkage = common.linkage }); @export(__gttf2, .{ .name = "__gttf2", .linkage = common.linkage }); diff --git a/lib/compiler_rt/multf3.zig b/lib/compiler_rt/multf3.zig index f71867c9ca..b18f5912a5 100644 --- a/lib/compiler_rt/multf3.zig +++ b/lib/compiler_rt/multf3.zig @@ -6,6 +6,8 @@ pub const panic = common.panic; comptime { if (common.want_ppc_abi) { @export(__mulkf3, .{ .name = "__mulkf3", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + @export(_Qp_mul, .{ .name = "_Qp_mul", .linkage = common.linkage }); } else { @export(__multf3, .{ .name = "__multf3", .linkage = common.linkage }); } @@ -18,3 +20,7 @@ fn __multf3(a: f128, b: f128) callconv(.C) f128 { fn __mulkf3(a: f128, b: f128) callconv(.C) f128 { return mulf3(f128, a, b); } + +fn _Qp_mul(c: *f128, a: *const f128, b: *const f128) callconv(.C) void { + c.* = mulf3(f128, a.*, b.*); +} diff --git a/lib/compiler_rt/sparc.zig b/lib/compiler_rt/sparc.zig deleted file mode 100644 index a39951a1c8..0000000000 --- a/lib/compiler_rt/sparc.zig +++ /dev/null @@ -1,148 +0,0 @@ -// -// SPARC uses a different naming scheme for its support routines so we map it here to the x86 name. - -const std = @import("std"); -const builtin = @import("builtin"); -const arch = builtin.cpu.arch; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; - -comptime { - if (arch.isSPARC()) { - // SPARC systems use a different naming scheme - @export(_Qp_add, .{ .name = "_Qp_add", .linkage = linkage }); - @export(_Qp_div, .{ .name = "_Qp_div", .linkage = linkage }); - @export(_Qp_mul, .{ .name = "_Qp_mul", .linkage = linkage }); - @export(_Qp_sub, .{ .name = "_Qp_sub", .linkage = linkage }); - - @export(_Qp_cmp, .{ .name = "_Qp_cmp", .linkage = linkage }); - @export(_Qp_feq, .{ .name = "_Qp_feq", .linkage = linkage }); - @export(_Qp_fne, .{ .name = "_Qp_fne", .linkage = linkage }); - @export(_Qp_flt, .{ .name = "_Qp_flt", .linkage = linkage }); - @export(_Qp_fle, .{ .name = "_Qp_fle", .linkage = linkage }); - @export(_Qp_fgt, .{ .name = "_Qp_fgt", .linkage = linkage }); - @export(_Qp_fge, .{ .name = "_Qp_fge", .linkage = linkage }); - - @export(_Qp_itoq, .{ .name = "_Qp_itoq", .linkage = linkage }); - @export(_Qp_uitoq, .{ .name = "_Qp_uitoq", .linkage = linkage }); - @export(_Qp_xtoq, .{ .name = "_Qp_xtoq", .linkage = linkage }); - @export(_Qp_uxtoq, .{ .name = "_Qp_uxtoq", .linkage = linkage }); - @export(_Qp_stoq, .{ .name = "_Qp_stoq", .linkage = linkage }); - @export(_Qp_dtoq, .{ .name = "_Qp_dtoq", .linkage = linkage }); - @export(_Qp_qtoi, .{ .name = "_Qp_qtoi", .linkage = linkage }); - @export(_Qp_qtoui, .{ .name = "_Qp_qtoui", .linkage = linkage }); - @export(_Qp_qtox, .{ .name = "_Qp_qtox", .linkage = linkage }); - @export(_Qp_qtoux, .{ .name = "_Qp_qtoux", .linkage = linkage }); - @export(_Qp_qtos, .{ .name = "_Qp_qtos", .linkage = linkage }); - @export(_Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage }); - } -} - -// The SPARC Architecture Manual, Version 9: -// A.13 Floating-Point Compare -const FCMP = enum(i32) { - Equal = 0, - Less = 1, - Greater = 2, - Unordered = 3, -}; - -// Basic arithmetic - -pub fn _Qp_add(c: *f128, a: *f128, b: *f128) callconv(.C) void { - c.* = @import("addf3.zig").__addtf3(a.*, b.*); -} - -pub fn _Qp_div(c: *f128, a: *f128, b: *f128) callconv(.C) void { - c.* = @import("divtf3.zig").__divtf3(a.*, b.*); -} - -pub fn _Qp_mul(c: *f128, a: *f128, b: *f128) callconv(.C) void { - c.* = @import("mulf3.zig").__multf3(a.*, b.*); -} - -pub fn _Qp_sub(c: *f128, a: *f128, b: *f128) callconv(.C) void { - c.* = @import("addf3.zig").__subtf3(a.*, b.*); -} - -// Comparison - -pub fn _Qp_cmp(a: *f128, b: *f128) callconv(.C) i32 { - return @enumToInt(@import("compareXf2.zig").cmp(f128, FCMP, a.*, b.*)); -} - -pub fn _Qp_feq(a: *f128, b: *f128) callconv(.C) bool { - return _Qp_cmp(a, b) == @enumToInt(FCMP.Equal); -} - -pub fn _Qp_fne(a: *f128, b: *f128) callconv(.C) bool { - return _Qp_cmp(a, b) != @enumToInt(FCMP.Equal); -} - -pub fn _Qp_flt(a: *f128, b: *f128) callconv(.C) bool { - return _Qp_cmp(a, b) == @enumToInt(FCMP.Less); -} - -pub fn _Qp_fle(a: *f128, b: *f128) callconv(.C) bool { - const cmp = _Qp_cmp(a, b); - return cmp == @enumToInt(FCMP.Less) or cmp == @enumToInt(FCMP.Equal); -} - -pub fn _Qp_fgt(a: *f128, b: *f128) callconv(.C) bool { - return _Qp_cmp(a, b) == @enumToInt(FCMP.Greater); -} - -pub fn _Qp_fge(a: *f128, b: *f128) callconv(.C) bool { - const cmp = _Qp_cmp(a, b); - return cmp == @enumToInt(FCMP.Greater) or cmp == @enumToInt(FCMP.Equal); -} - -// Conversion - -pub fn _Qp_itoq(c: *f128, a: i32) callconv(.C) void { - c.* = @import("floatXiYf.zig").__floatsitf(a); -} - -pub fn _Qp_uitoq(c: *f128, a: u32) callconv(.C) void { - c.* = @import("floatXiYf.zig").__floatunsitf(a); -} - -pub fn _Qp_xtoq(c: *f128, a: i64) callconv(.C) void { - c.* = @import("floatXiYf.zig").__floatditf(a); -} - -pub fn _Qp_uxtoq(c: *f128, a: u64) callconv(.C) void { - c.* = @import("floatXiYf.zig").__floatunditf(a); -} - -pub fn _Qp_stoq(c: *f128, a: f32) callconv(.C) void { - c.* = @import("extendXfYf2.zig").__extendsftf2(a); -} - -pub fn _Qp_dtoq(c: *f128, a: f64) callconv(.C) void { - c.* = @import("extendXfYf2.zig").__extenddftf2(a); -} - -pub fn _Qp_qtoi(a: *f128) callconv(.C) i32 { - return @import("fixXfYi.zig").__fixtfsi(a.*); -} - -pub fn _Qp_qtoui(a: *f128) callconv(.C) u32 { - return @import("fixXfYi.zig").__fixunstfsi(a.*); -} - -pub fn _Qp_qtox(a: *f128) callconv(.C) i64 { - return @import("fixXfYi.zig").__fixtfdi(a.*); -} - -pub fn _Qp_qtoux(a: *f128) callconv(.C) u64 { - return @import("fixXfYi.zig").__fixunstfdi(a.*); -} - -pub fn _Qp_qtos(a: *f128) callconv(.C) f32 { - return @import("truncXfYf2.zig").__trunctfsf2(a.*); -} - -pub fn _Qp_qtod(a: *f128) callconv(.C) f64 { - return @import("truncXfYf2.zig").__trunctfdf2(a.*); -} diff --git a/lib/compiler_rt/subtf3.zig b/lib/compiler_rt/subtf3.zig index a4e2f4dfae..aa50f73da8 100644 --- a/lib/compiler_rt/subtf3.zig +++ b/lib/compiler_rt/subtf3.zig @@ -5,17 +5,26 @@ pub const panic = common.panic; comptime { if (common.want_ppc_abi) { @export(__subkf3, .{ .name = "__subkf3", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + @export(_Qp_sub, .{ .name = "_Qp_sub", .linkage = common.linkage }); } else { @export(__subtf3, .{ .name = "__subtf3", .linkage = common.linkage }); } } fn __subtf3(a: f128, b: f128) callconv(.C) f128 { - const neg_b = @bitCast(f128, @bitCast(u128, b) ^ (@as(u128, 1) << 127)); - return a + neg_b; + return sub(a, b); } fn __subkf3(a: f128, b: f128) callconv(.C) f128 { + return sub(a, b); +} + +fn _Qp_sub(c: *f128, a: *const f128, b: *const f128) callconv(.C) void { + c.* = sub(a.*, b.*); +} + +inline fn sub(a: f128, b: f128) f128 { const neg_b = @bitCast(f128, @bitCast(u128, b) ^ (@as(u128, 1) << 127)); return a + neg_b; } diff --git a/lib/compiler_rt/trunctfdf2.zig b/lib/compiler_rt/trunctfdf2.zig index 012de610c3..3fe16ad6a2 100644 --- a/lib/compiler_rt/trunctfdf2.zig +++ b/lib/compiler_rt/trunctfdf2.zig @@ -6,6 +6,8 @@ pub const panic = common.panic; comptime { if (common.want_ppc_abi) { @export(__trunckfdf2, .{ .name = "__trunckfdf2", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + @export(_Qp_qtod, .{ .name = "_Qp_qtod", .linkage = common.linkage }); } else { @export(__trunctfdf2, .{ .name = "__trunctfdf2", .linkage = common.linkage }); } @@ -18,3 +20,7 @@ fn __trunctfdf2(a: f128) callconv(.C) f64 { fn __trunckfdf2(a: f128) callconv(.C) f64 { return truncf(f64, f128, a); } + +fn _Qp_qtod(a: *const f128) callconv(.C) f64 { + return truncf(f64, f128, a.*); +} diff --git a/lib/compiler_rt/trunctfsf2.zig b/lib/compiler_rt/trunctfsf2.zig index 535f73e473..f507f5ad2b 100644 --- a/lib/compiler_rt/trunctfsf2.zig +++ b/lib/compiler_rt/trunctfsf2.zig @@ -6,6 +6,8 @@ pub const panic = common.panic; comptime { if (common.want_ppc_abi) { @export(__trunckfsf2, .{ .name = "__trunckfsf2", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + @export(_Qp_qtos, .{ .name = "_Qp_qtos", .linkage = common.linkage }); } else { @export(__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = common.linkage }); } @@ -18,3 +20,7 @@ fn __trunctfsf2(a: f128) callconv(.C) f32 { fn __trunckfsf2(a: f128) callconv(.C) f32 { return truncf(f32, f128, a); } + +fn _Qp_qtos(a: *const f128) callconv(.C) f32 { + return truncf(f32, f128, a.*); +} diff --git a/lib/compiler_rt/unordtf2.zig b/lib/compiler_rt/unordtf2.zig index 833d6d6cee..41d1d7008e 100644 --- a/lib/compiler_rt/unordtf2.zig +++ b/lib/compiler_rt/unordtf2.zig @@ -6,6 +6,9 @@ pub const panic = common.panic; comptime { if (common.want_ppc_abi) { @export(__unordkf2, .{ .name = "__unordkf2", .linkage = common.linkage }); + } else if (common.want_sparc_abi) { + // These exports are handled in cmptf2.zig because unordered comparisons + // are based on calling _Qp_cmp. } else { @export(__unordtf2, .{ .name = "__unordtf2", .linkage = common.linkage }); } diff --git a/src/compiler_rt.zig b/src/compiler_rt.zig index 3418886a42..ee0fc75fc5 100644 --- a/src/compiler_rt.zig +++ b/src/compiler_rt.zig @@ -228,6 +228,5 @@ const sources = &[_][]const u8{ "compiler_rt/arm.zig", "compiler_rt/aulldiv.zig", "compiler_rt/aullrem.zig", - "compiler_rt/sparc.zig", "compiler_rt/clear_cache.zig", }; From a8a7f15106abef7c647d4de572a04b77ce048dd6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Jun 2022 14:21:11 -0700 Subject: [PATCH 1855/2031] compiler-rt: use callconv(.AAPCS) on all __aeabi_ functions --- lib/compiler_rt/arm.zig | 6 +++--- lib/compiler_rt/int.zig | 4 ++-- lib/compiler_rt/muldf3.zig | 2 +- lib/compiler_rt/muldi3.zig | 2 +- lib/compiler_rt/mulsf3.zig | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/compiler_rt/arm.zig b/lib/compiler_rt/arm.zig index f974f67cce..e119c43e68 100644 --- a/lib/compiler_rt/arm.zig +++ b/lib/compiler_rt/arm.zig @@ -105,9 +105,9 @@ pub fn __aeabi_memclr8(dest: [*]u8, n: usize) callconv(.AAPCS) void { } // Dummy functions to avoid errors during the linking phase -pub fn __aeabi_unwind_cpp_pr0() callconv(.C) void {} -pub fn __aeabi_unwind_cpp_pr1() callconv(.C) void {} -pub fn __aeabi_unwind_cpp_pr2() callconv(.C) void {} +pub fn __aeabi_unwind_cpp_pr0() callconv(.AAPCS) void {} +pub fn __aeabi_unwind_cpp_pr1() callconv(.AAPCS) void {} +pub fn __aeabi_unwind_cpp_pr2() callconv(.AAPCS) void {} // This function can only clobber r0 according to the ABI pub fn __aeabi_read_tp() callconv(.Naked) void { diff --git a/lib/compiler_rt/int.zig b/lib/compiler_rt/int.zig index 447f867267..a9795632f0 100644 --- a/lib/compiler_rt/int.zig +++ b/lib/compiler_rt/int.zig @@ -214,7 +214,7 @@ pub fn __divsi3(n: i32, d: i32) callconv(.C) i32 { return @bitCast(i32, (res ^ sign) -% sign); } -pub fn __aeabi_idiv(n: i32, d: i32) callconv(.C) i32 { +pub fn __aeabi_idiv(n: i32, d: i32) callconv(.AAPCS) i32 { return @call(.{ .modifier = .always_inline }, __divsi3, .{ n, d }); } @@ -284,7 +284,7 @@ pub fn __udivsi3(n: u32, d: u32) callconv(.C) u32 { return q; } -pub fn __aeabi_uidiv(n: u32, d: u32) callconv(.C) u32 { +pub fn __aeabi_uidiv(n: u32, d: u32) callconv(.AAPCS) u32 { return @call(.{ .modifier = .always_inline }, __udivsi3, .{ n, d }); } diff --git a/lib/compiler_rt/muldf3.zig b/lib/compiler_rt/muldf3.zig index c9142a9ed3..5fcd6e13a4 100644 --- a/lib/compiler_rt/muldf3.zig +++ b/lib/compiler_rt/muldf3.zig @@ -15,6 +15,6 @@ fn __muldf3(a: f64, b: f64) callconv(.C) f64 { return mulf3(f64, a, b); } -fn __aeabi_dmul(a: f64, b: f64) callconv(.C) f64 { +fn __aeabi_dmul(a: f64, b: f64) callconv(.AAPCS) f64 { return mulf3(f64, a, b); } diff --git a/lib/compiler_rt/muldi3.zig b/lib/compiler_rt/muldi3.zig index 8f317adee4..41e7827dfc 100644 --- a/lib/compiler_rt/muldi3.zig +++ b/lib/compiler_rt/muldi3.zig @@ -33,7 +33,7 @@ const dwords = extern union { }, }; -pub fn __aeabi_lmul(a: i64, b: i64) callconv(.C) i64 { +pub fn __aeabi_lmul(a: i64, b: i64) callconv(.AAPCS) i64 { return @call(.{ .modifier = .always_inline }, __muldi3, .{ a, b }); } diff --git a/lib/compiler_rt/mulsf3.zig b/lib/compiler_rt/mulsf3.zig index a425f9617e..50b2142694 100644 --- a/lib/compiler_rt/mulsf3.zig +++ b/lib/compiler_rt/mulsf3.zig @@ -15,6 +15,6 @@ fn __mulsf3(a: f32, b: f32) callconv(.C) f32 { return mulf3(f32, a, b); } -fn __aeabi_fmul(a: f32, b: f32) callconv(.C) f32 { +fn __aeabi_fmul(a: f32, b: f32) callconv(.AAPCS) f32 { return mulf3(f32, a, b); } From 4200f89d94b8b917f3429e11ef11f136fa910ca9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Jun 2022 15:14:24 -0700 Subject: [PATCH 1856/2031] compiler-rt: sort source files --- src/compiler_rt.zig | 84 ++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/src/compiler_rt.zig b/src/compiler_rt.zig index ee0fc75fc5..5967da9cff 100644 --- a/src/compiler_rt.zig +++ b/src/compiler_rt.zig @@ -168,65 +168,65 @@ pub fn buildCompilerRtLib(comp: *Compilation, compiler_rt_lib: *?CRTFile) !void } const sources = &[_][]const u8{ + "compiler_rt/absv.zig", + "compiler_rt/addf3.zig", + "compiler_rt/addo.zig", + "compiler_rt/arm.zig", "compiler_rt/atomics.zig", - "compiler_rt/sin.zig", - "compiler_rt/cos.zig", - "compiler_rt/sincos.zig", + "compiler_rt/aulldiv.zig", + "compiler_rt/aullrem.zig", + "compiler_rt/bswap.zig", "compiler_rt/ceil.zig", + "compiler_rt/clear_cache.zig", + "compiler_rt/cmp.zig", + "compiler_rt/compareXf2.zig", + "compiler_rt/cos.zig", + "compiler_rt/count0bits.zig", + "compiler_rt/divdf3.zig", + "compiler_rt/divsf3.zig", + "compiler_rt/divtf3.zig", + "compiler_rt/divti3.zig", + "compiler_rt/divxf3.zig", + "compiler_rt/emutls.zig", "compiler_rt/exp.zig", "compiler_rt/exp2.zig", + "compiler_rt/extendXfYf2.zig", + "compiler_rt/extend_f80.zig", "compiler_rt/fabs.zig", + "compiler_rt/fixXfYi.zig", + "compiler_rt/floatXiYf.zig", "compiler_rt/floor.zig", "compiler_rt/fma.zig", "compiler_rt/fmax.zig", "compiler_rt/fmin.zig", "compiler_rt/fmod.zig", + "compiler_rt/int.zig", "compiler_rt/log.zig", "compiler_rt/log10.zig", "compiler_rt/log2.zig", - "compiler_rt/round.zig", - "compiler_rt/sqrt.zig", - "compiler_rt/tan.zig", - "compiler_rt/trunc.zig", - "compiler_rt/extendXfYf2.zig", - "compiler_rt/extend_f80.zig", - "compiler_rt/compareXf2.zig", - "compiler_rt/stack_probe.zig", - "compiler_rt/divti3.zig", "compiler_rt/modti3.zig", - "compiler_rt/multi3.zig", - "compiler_rt/udivti3.zig", - "compiler_rt/udivmodti4.zig", - "compiler_rt/umodti3.zig", - "compiler_rt/truncXfYf2.zig", - "compiler_rt/trunc_f80.zig", - "compiler_rt/addf3.zig", + "compiler_rt/muldi3.zig", "compiler_rt/mulf3.zig", - "compiler_rt/divsf3.zig", - "compiler_rt/divdf3.zig", - "compiler_rt/divxf3.zig", - "compiler_rt/divtf3.zig", - "compiler_rt/floatXiYf.zig", - "compiler_rt/fixXfYi.zig", - "compiler_rt/count0bits.zig", + "compiler_rt/mulo.zig", + "compiler_rt/multi3.zig", + "compiler_rt/negXf2.zig", + "compiler_rt/negXi2.zig", + "compiler_rt/negv.zig", + "compiler_rt/os_version_check.zig", "compiler_rt/parity.zig", "compiler_rt/popcount.zig", - "compiler_rt/bswap.zig", - "compiler_rt/int.zig", + "compiler_rt/round.zig", "compiler_rt/shift.zig", - "compiler_rt/negXi2.zig", - "compiler_rt/muldi3.zig", - "compiler_rt/absv.zig", - "compiler_rt/negv.zig", - "compiler_rt/addo.zig", + "compiler_rt/sin.zig", + "compiler_rt/sincos.zig", + "compiler_rt/sqrt.zig", + "compiler_rt/stack_probe.zig", "compiler_rt/subo.zig", - "compiler_rt/mulo.zig", - "compiler_rt/cmp.zig", - "compiler_rt/negXf2.zig", - "compiler_rt/os_version_check.zig", - "compiler_rt/emutls.zig", - "compiler_rt/arm.zig", - "compiler_rt/aulldiv.zig", - "compiler_rt/aullrem.zig", - "compiler_rt/clear_cache.zig", + "compiler_rt/tan.zig", + "compiler_rt/trunc.zig", + "compiler_rt/truncXfYf2.zig", + "compiler_rt/trunc_f80.zig", + "compiler_rt/udivmodti4.zig", + "compiler_rt/udivti3.zig", + "compiler_rt/umodti3.zig", }; From 453243d9e02a5d893828694e7090515de7777bb8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Jun 2022 15:16:40 -0700 Subject: [PATCH 1857/2031] compiler-rt: correct the list of builtins to build --- src/compiler_rt.zig | 114 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 105 insertions(+), 9 deletions(-) diff --git a/src/compiler_rt.zig b/src/compiler_rt.zig index 5967da9cff..0440235d58 100644 --- a/src/compiler_rt.zig +++ b/src/compiler_rt.zig @@ -168,9 +168,14 @@ pub fn buildCompilerRtLib(comp: *Compilation, compiler_rt_lib: *?CRTFile) !void } const sources = &[_][]const u8{ - "compiler_rt/absv.zig", - "compiler_rt/addf3.zig", + "compiler_rt/absvdi2.zig", + "compiler_rt/absvsi2.zig", + "compiler_rt/absvti2.zig", + "compiler_rt/adddf3.zig", "compiler_rt/addo.zig", + "compiler_rt/addsf3.zig", + "compiler_rt/addtf3.zig", + "compiler_rt/addxf3.zig", "compiler_rt/arm.zig", "compiler_rt/atomics.zig", "compiler_rt/aulldiv.zig", @@ -179,7 +184,10 @@ const sources = &[_][]const u8{ "compiler_rt/ceil.zig", "compiler_rt/clear_cache.zig", "compiler_rt/cmp.zig", - "compiler_rt/compareXf2.zig", + "compiler_rt/cmpdf2.zig", + "compiler_rt/cmpsf2.zig", + "compiler_rt/cmptf2.zig", + "compiler_rt/cmpxf2.zig", "compiler_rt/cos.zig", "compiler_rt/count0bits.zig", "compiler_rt/divdf3.zig", @@ -190,25 +198,98 @@ const sources = &[_][]const u8{ "compiler_rt/emutls.zig", "compiler_rt/exp.zig", "compiler_rt/exp2.zig", - "compiler_rt/extendXfYf2.zig", - "compiler_rt/extend_f80.zig", + "compiler_rt/extenddftf2.zig", + "compiler_rt/extenddfxf2.zig", + "compiler_rt/extendhfsf2.zig", + "compiler_rt/extendhftf2.zig", + "compiler_rt/extendhfxf2.zig", + "compiler_rt/extendsfdf2.zig", + "compiler_rt/extendsftf2.zig", + "compiler_rt/extendsfxf2.zig", + "compiler_rt/extendxftf2.zig", "compiler_rt/fabs.zig", - "compiler_rt/fixXfYi.zig", - "compiler_rt/floatXiYf.zig", + "compiler_rt/fixdfdi.zig", + "compiler_rt/fixdfsi.zig", + "compiler_rt/fixdfti.zig", + "compiler_rt/fixhfdi.zig", + "compiler_rt/fixhfsi.zig", + "compiler_rt/fixhfti.zig", + "compiler_rt/fixsfdi.zig", + "compiler_rt/fixsfsi.zig", + "compiler_rt/fixsfti.zig", + "compiler_rt/fixtfdi.zig", + "compiler_rt/fixtfsi.zig", + "compiler_rt/fixtfti.zig", + "compiler_rt/fixunsdfdi.zig", + "compiler_rt/fixunsdfsi.zig", + "compiler_rt/fixunsdfti.zig", + "compiler_rt/fixunshfdi.zig", + "compiler_rt/fixunshfsi.zig", + "compiler_rt/fixunshfti.zig", + "compiler_rt/fixunssfdi.zig", + "compiler_rt/fixunssfsi.zig", + "compiler_rt/fixunssfti.zig", + "compiler_rt/fixunstfdi.zig", + "compiler_rt/fixunstfsi.zig", + "compiler_rt/fixunstfti.zig", + "compiler_rt/fixunsxfdi.zig", + "compiler_rt/fixunsxfsi.zig", + "compiler_rt/fixunsxfti.zig", + "compiler_rt/fixxfdi.zig", + "compiler_rt/fixxfsi.zig", + "compiler_rt/fixxfti.zig", + "compiler_rt/floatdidf.zig", + "compiler_rt/floatdihf.zig", + "compiler_rt/floatdisf.zig", + "compiler_rt/floatditf.zig", + "compiler_rt/floatdixf.zig", + "compiler_rt/floatsidf.zig", + "compiler_rt/floatsihf.zig", + "compiler_rt/floatsisf.zig", + "compiler_rt/floatsitf.zig", + "compiler_rt/floatsixf.zig", + "compiler_rt/floattidf.zig", + "compiler_rt/floattihf.zig", + "compiler_rt/floattisf.zig", + "compiler_rt/floattitf.zig", + "compiler_rt/floattixf.zig", + "compiler_rt/floatundidf.zig", + "compiler_rt/floatundihf.zig", + "compiler_rt/floatundisf.zig", + "compiler_rt/floatunditf.zig", + "compiler_rt/floatundixf.zig", + "compiler_rt/floatunsidf.zig", + "compiler_rt/floatunsihf.zig", + "compiler_rt/floatunsisf.zig", + "compiler_rt/floatunsitf.zig", + "compiler_rt/floatunsixf.zig", + "compiler_rt/floatuntidf.zig", + "compiler_rt/floatuntihf.zig", + "compiler_rt/floatuntisf.zig", + "compiler_rt/floatuntitf.zig", + "compiler_rt/floatuntixf.zig", "compiler_rt/floor.zig", "compiler_rt/fma.zig", "compiler_rt/fmax.zig", "compiler_rt/fmin.zig", "compiler_rt/fmod.zig", + "compiler_rt/gedf2.zig", + "compiler_rt/gesf2.zig", + "compiler_rt/getf2.zig", + "compiler_rt/gexf2.zig", "compiler_rt/int.zig", "compiler_rt/log.zig", "compiler_rt/log10.zig", "compiler_rt/log2.zig", "compiler_rt/modti3.zig", + "compiler_rt/muldf3.zig", "compiler_rt/muldi3.zig", "compiler_rt/mulf3.zig", "compiler_rt/mulo.zig", + "compiler_rt/mulsf3.zig", + "compiler_rt/multf3.zig", "compiler_rt/multi3.zig", + "compiler_rt/mulxf3.zig", "compiler_rt/negXf2.zig", "compiler_rt/negXi2.zig", "compiler_rt/negv.zig", @@ -221,12 +302,27 @@ const sources = &[_][]const u8{ "compiler_rt/sincos.zig", "compiler_rt/sqrt.zig", "compiler_rt/stack_probe.zig", + "compiler_rt/subdf3.zig", "compiler_rt/subo.zig", + "compiler_rt/subsf3.zig", + "compiler_rt/subtf3.zig", + "compiler_rt/subxf3.zig", "compiler_rt/tan.zig", "compiler_rt/trunc.zig", - "compiler_rt/truncXfYf2.zig", - "compiler_rt/trunc_f80.zig", + "compiler_rt/truncdfhf2.zig", + "compiler_rt/truncdfsf2.zig", + "compiler_rt/truncsfhf2.zig", + "compiler_rt/trunctfdf2.zig", + "compiler_rt/trunctfhf2.zig", + "compiler_rt/trunctfsf2.zig", + "compiler_rt/trunctfxf2.zig", + "compiler_rt/truncxfdf2.zig", + "compiler_rt/truncxfhf2.zig", + "compiler_rt/truncxfsf2.zig", "compiler_rt/udivmodti4.zig", "compiler_rt/udivti3.zig", "compiler_rt/umodti3.zig", + "compiler_rt/unorddf2.zig", + "compiler_rt/unordsf2.zig", + "compiler_rt/unordtf2.zig", }; From bbc610339814908e68d221748dffdbd8d8ec4f09 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Jun 2022 15:14:12 -0700 Subject: [PATCH 1858/2031] compiler-rt: fix tests --- lib/compiler_rt/absvdi2.zig | 2 +- lib/compiler_rt/absvdi2_test.zig | 5 +- lib/compiler_rt/absvsi2.zig | 2 +- lib/compiler_rt/absvsi2_test.zig | 5 +- lib/compiler_rt/absvti2.zig | 2 +- lib/compiler_rt/absvti2_test.zig | 5 +- lib/compiler_rt/addf3_test.zig | 7 +- lib/compiler_rt/addtf3.zig | 2 +- lib/compiler_rt/addxf3.zig | 2 +- lib/compiler_rt/cmpdf2.zig | 8 +- lib/compiler_rt/cmpsf2.zig | 8 +- lib/compiler_rt/comparedf2_test.zig | 24 +++-- lib/compiler_rt/comparesf2_test.zig | 24 +++-- lib/compiler_rt/divtf3.zig | 8 +- lib/compiler_rt/extenddftf2.zig | 3 +- lib/compiler_rt/extendf.zig | 2 +- ...{extendXfYf2_test.zig => extendf_test.zig} | 28 ++--- lib/compiler_rt/extendhfsf2.zig | 2 +- lib/compiler_rt/extendhftf2.zig | 2 +- lib/compiler_rt/extendsftf2.zig | 2 +- lib/compiler_rt/fixdfdi.zig | 2 +- lib/compiler_rt/fixdfsi.zig | 2 +- lib/compiler_rt/fixdfti.zig | 2 +- lib/compiler_rt/fixsfdi.zig | 2 +- lib/compiler_rt/fixsfsi.zig | 2 +- lib/compiler_rt/fixsfti.zig | 2 +- lib/compiler_rt/fixtfdi.zig | 2 +- lib/compiler_rt/fixtfsi.zig | 2 +- lib/compiler_rt/fixtfti.zig | 2 +- lib/compiler_rt/fixunsdfdi.zig | 2 +- lib/compiler_rt/fixunsdfsi.zig | 2 +- lib/compiler_rt/fixunsdfti.zig | 2 +- lib/compiler_rt/fixunshfti.zig | 2 +- lib/compiler_rt/fixunssfdi.zig | 2 +- lib/compiler_rt/fixunssfsi.zig | 2 +- lib/compiler_rt/fixunssfti.zig | 2 +- lib/compiler_rt/fixunstfdi.zig | 2 +- lib/compiler_rt/fixunstfsi.zig | 2 +- lib/compiler_rt/fixunstfti.zig | 2 +- lib/compiler_rt/fixunsxfti.zig | 2 +- lib/compiler_rt/float_to_int_test.zig | 48 +++++---- lib/compiler_rt/floatdidf.zig | 2 +- lib/compiler_rt/floatdisf.zig | 2 +- lib/compiler_rt/floatditf.zig | 2 +- lib/compiler_rt/floatsidf.zig | 2 +- lib/compiler_rt/floatsisf.zig | 2 +- lib/compiler_rt/floatsitf.zig | 2 +- lib/compiler_rt/floattidf.zig | 2 +- lib/compiler_rt/floattisf.zig | 2 +- lib/compiler_rt/floattitf.zig | 2 +- lib/compiler_rt/floatundidf.zig | 2 +- lib/compiler_rt/floatundisf.zig | 2 +- lib/compiler_rt/floatunditf.zig | 2 +- lib/compiler_rt/floatunsidf.zig | 2 +- lib/compiler_rt/floatunsihf.zig | 2 +- lib/compiler_rt/floatunsisf.zig | 2 +- lib/compiler_rt/floatunsitf.zig | 2 +- lib/compiler_rt/floatuntidf.zig | 2 +- lib/compiler_rt/floatuntisf.zig | 2 +- lib/compiler_rt/floatuntitf.zig | 2 +- lib/compiler_rt/gedf2.zig | 4 +- lib/compiler_rt/gesf2.zig | 4 +- lib/compiler_rt/int_to_float.zig | 2 +- ...oatXiYf_test.zig => int_to_float_test.zig} | 101 +++++++++--------- lib/compiler_rt/muldf3.zig | 2 +- lib/compiler_rt/mulf3_test.zig | 8 +- lib/compiler_rt/mulsf3.zig | 2 +- lib/compiler_rt/multf3.zig | 2 +- lib/compiler_rt/subtf3.zig | 2 +- lib/compiler_rt/truncdfhf2.zig | 2 +- lib/compiler_rt/truncdfsf2.zig | 2 +- lib/compiler_rt/truncf_test.zig | 18 ++-- lib/compiler_rt/truncsfhf2.zig | 2 +- lib/compiler_rt/trunctfdf2.zig | 2 +- lib/compiler_rt/trunctfhf2.zig | 2 +- lib/compiler_rt/trunctfsf2.zig | 2 +- lib/compiler_rt/trunctfxf2.zig | 2 +- lib/compiler_rt/unorddf2.zig | 2 +- lib/compiler_rt/unordsf2.zig | 2 +- 79 files changed, 225 insertions(+), 207 deletions(-) rename lib/compiler_rt/{extendXfYf2_test.zig => extendf_test.zig} (87%) rename lib/compiler_rt/{floatXiYf_test.zig => int_to_float_test.zig} (90%) diff --git a/lib/compiler_rt/absvdi2.zig b/lib/compiler_rt/absvdi2.zig index 1875d5bbaa..7ebf561ae5 100644 --- a/lib/compiler_rt/absvdi2.zig +++ b/lib/compiler_rt/absvdi2.zig @@ -7,6 +7,6 @@ comptime { @export(__absvdi2, .{ .name = "__absvdi2", .linkage = common.linkage }); } -fn __absvdi2(a: i64) callconv(.C) i64 { +pub fn __absvdi2(a: i64) callconv(.C) i64 { return absv(i64, a); } diff --git a/lib/compiler_rt/absvdi2_test.zig b/lib/compiler_rt/absvdi2_test.zig index 4aa73513ee..e861ef0ff3 100644 --- a/lib/compiler_rt/absvdi2_test.zig +++ b/lib/compiler_rt/absvdi2_test.zig @@ -1,8 +1,9 @@ -const absv = @import("absv.zig"); const testing = @import("std").testing; +const __absvdi2 = @import("absvdi2.zig").__absvdi2; + fn test__absvdi2(a: i64, expected: i64) !void { - var result = absv.__absvdi2(a); + var result = __absvdi2(a); try testing.expectEqual(expected, result); } diff --git a/lib/compiler_rt/absvsi2.zig b/lib/compiler_rt/absvsi2.zig index 965e39bc9b..664925f8f9 100644 --- a/lib/compiler_rt/absvsi2.zig +++ b/lib/compiler_rt/absvsi2.zig @@ -7,6 +7,6 @@ comptime { @export(__absvsi2, .{ .name = "__absvsi2", .linkage = common.linkage }); } -fn __absvsi2(a: i32) callconv(.C) i32 { +pub fn __absvsi2(a: i32) callconv(.C) i32 { return absv(i32, a); } diff --git a/lib/compiler_rt/absvsi2_test.zig b/lib/compiler_rt/absvsi2_test.zig index 2cc3dbf991..9c74ebee67 100644 --- a/lib/compiler_rt/absvsi2_test.zig +++ b/lib/compiler_rt/absvsi2_test.zig @@ -1,8 +1,9 @@ -const absv = @import("absv.zig"); const testing = @import("std").testing; +const __absvsi2 = @import("absvsi2.zig").__absvsi2; + fn test__absvsi2(a: i32, expected: i32) !void { - var result = absv.__absvsi2(a); + var result = __absvsi2(a); try testing.expectEqual(expected, result); } diff --git a/lib/compiler_rt/absvti2.zig b/lib/compiler_rt/absvti2.zig index ada06c6563..f7d0f796b0 100644 --- a/lib/compiler_rt/absvti2.zig +++ b/lib/compiler_rt/absvti2.zig @@ -7,6 +7,6 @@ comptime { @export(__absvti2, .{ .name = "__absvti2", .linkage = common.linkage }); } -fn __absvti2(a: i128) callconv(.C) i128 { +pub fn __absvti2(a: i128) callconv(.C) i128 { return absv(i128, a); } diff --git a/lib/compiler_rt/absvti2_test.zig b/lib/compiler_rt/absvti2_test.zig index 5b4deb3640..fbed961775 100644 --- a/lib/compiler_rt/absvti2_test.zig +++ b/lib/compiler_rt/absvti2_test.zig @@ -1,8 +1,9 @@ -const absv = @import("absv.zig"); const testing = @import("std").testing; +const __absvti2 = @import("absvti2.zig").__absvti2; + fn test__absvti2(a: i128, expected: i128) !void { - var result = absv.__absvti2(a); + var result = __absvti2(a); try testing.expectEqual(expected, result); } diff --git a/lib/compiler_rt/addf3_test.zig b/lib/compiler_rt/addf3_test.zig index cd3f3ab89c..1df87a889f 100644 --- a/lib/compiler_rt/addf3_test.zig +++ b/lib/compiler_rt/addf3_test.zig @@ -7,7 +7,9 @@ const std = @import("std"); const math = std.math; const qnan128 = @bitCast(f128, @as(u128, 0x7fff800000000000) << 64); -const __addtf3 = @import("addf3.zig").__addtf3; +const __addtf3 = @import("addtf3.zig").__addtf3; +const __addxf3 = @import("addxf3.zig").__addxf3; +const __subtf3 = @import("subtf3.zig").__subtf3; fn test__addtf3(a: f128, b: f128, expected_hi: u64, expected_lo: u64) !void { const x = __addtf3(a, b); @@ -48,8 +50,6 @@ test "addtf3" { try test__addtf3(0x1.edcba52449872455634654321fp-1, 0x1.23456734245345543849abcdefp+5, 0x40042afc95c8b579, 0x61e58dd6c51eb77c); } -const __subtf3 = @import("addf3.zig").__subtf3; - fn test__subtf3(a: f128, b: f128, expected_hi: u64, expected_lo: u64) !void { const x = __subtf3(a, b); @@ -87,7 +87,6 @@ test "subtf3" { try test__subtf3(0x1.ee9d7c52354a6936ab8d7654321fp-1, 0x1.234567829a3bcdef5678ade36734p+5, 0xc0041b8af1915166, 0xa44a7bca780a166c); } -const __addxf3 = @import("addf3.zig").__addxf3; const qnan80 = @bitCast(f80, @bitCast(u80, math.nan(f80)) | (1 << (math.floatFractionalBits(f80) - 1))); fn test__addxf3(a: f80, b: f80, expected: u80) !void { diff --git a/lib/compiler_rt/addtf3.zig b/lib/compiler_rt/addtf3.zig index f7c33ba969..2a22493ded 100644 --- a/lib/compiler_rt/addtf3.zig +++ b/lib/compiler_rt/addtf3.zig @@ -13,7 +13,7 @@ comptime { } } -fn __addtf3(a: f128, b: f128) callconv(.C) f128 { +pub fn __addtf3(a: f128, b: f128) callconv(.C) f128 { return addf3(f128, a, b); } diff --git a/lib/compiler_rt/addxf3.zig b/lib/compiler_rt/addxf3.zig index 67e7dd8491..72cf955632 100644 --- a/lib/compiler_rt/addxf3.zig +++ b/lib/compiler_rt/addxf3.zig @@ -7,6 +7,6 @@ comptime { @export(__addxf3, .{ .name = "__addxf3", .linkage = common.linkage }); } -fn __addxf3(a: f80, b: f80) callconv(.C) f80 { +pub fn __addxf3(a: f80, b: f80) callconv(.C) f80 { return addf3(f80, a, b); } diff --git a/lib/compiler_rt/cmpdf2.zig b/lib/compiler_rt/cmpdf2.zig index f1661731fb..67dbcd8b4d 100644 --- a/lib/compiler_rt/cmpdf2.zig +++ b/lib/compiler_rt/cmpdf2.zig @@ -31,27 +31,27 @@ fn __cmpdf2(a: f64, b: f64) callconv(.C) i32 { /// "These functions return a value less than or equal to zero if neither argument is NaN, /// and a is less than or equal to b." -fn __ledf2(a: f64, b: f64) callconv(.C) i32 { +pub fn __ledf2(a: f64, b: f64) callconv(.C) i32 { return __cmpdf2(a, b); } /// "These functions return zero if neither argument is NaN, and a and b are equal." /// Note that due to some kind of historical accident, __eqdf2 and __nedf2 are defined /// to have the same return value. -fn __eqdf2(a: f64, b: f64) callconv(.C) i32 { +pub fn __eqdf2(a: f64, b: f64) callconv(.C) i32 { return __cmpdf2(a, b); } /// "These functions return a nonzero value if either argument is NaN, or if a and b are unequal." /// Note that due to some kind of historical accident, __eqdf2 and __nedf2 are defined /// to have the same return value. -fn __nedf2(a: f64, b: f64) callconv(.C) i32 { +pub fn __nedf2(a: f64, b: f64) callconv(.C) i32 { return __cmpdf2(a, b); } /// "These functions return a value less than zero if neither argument is NaN, and a /// is strictly less than b." -fn __ltdf2(a: f64, b: f64) callconv(.C) i32 { +pub fn __ltdf2(a: f64, b: f64) callconv(.C) i32 { return __cmpdf2(a, b); } diff --git a/lib/compiler_rt/cmpsf2.zig b/lib/compiler_rt/cmpsf2.zig index 23be99c3f7..1ac40ef6e2 100644 --- a/lib/compiler_rt/cmpsf2.zig +++ b/lib/compiler_rt/cmpsf2.zig @@ -31,27 +31,27 @@ fn __cmpsf2(a: f32, b: f32) callconv(.C) i32 { /// "These functions return a value less than or equal to zero if neither argument is NaN, /// and a is less than or equal to b." -fn __lesf2(a: f32, b: f32) callconv(.C) i32 { +pub fn __lesf2(a: f32, b: f32) callconv(.C) i32 { return __cmpsf2(a, b); } /// "These functions return zero if neither argument is NaN, and a and b are equal." /// Note that due to some kind of historical accident, __eqsf2 and __nesf2 are defined /// to have the same return value. -fn __eqsf2(a: f32, b: f32) callconv(.C) i32 { +pub fn __eqsf2(a: f32, b: f32) callconv(.C) i32 { return __cmpsf2(a, b); } /// "These functions return a nonzero value if either argument is NaN, or if a and b are unequal." /// Note that due to some kind of historical accident, __eqsf2 and __nesf2 are defined /// to have the same return value. -fn __nesf2(a: f32, b: f32) callconv(.C) i32 { +pub fn __nesf2(a: f32, b: f32) callconv(.C) i32 { return __cmpsf2(a, b); } /// "These functions return a value less than zero if neither argument is NaN, and a /// is strictly less than b." -fn __ltsf2(a: f32, b: f32) callconv(.C) i32 { +pub fn __ltsf2(a: f32, b: f32) callconv(.C) i32 { return __cmpsf2(a, b); } diff --git a/lib/compiler_rt/comparedf2_test.zig b/lib/compiler_rt/comparedf2_test.zig index a80297ffbf..a77718e57c 100644 --- a/lib/compiler_rt/comparedf2_test.zig +++ b/lib/compiler_rt/comparedf2_test.zig @@ -6,7 +6,15 @@ const std = @import("std"); const builtin = @import("builtin"); const is_test = builtin.is_test; -const comparedf2 = @import("compareXf2.zig"); +const __eqdf2 = @import("./cmpdf2.zig").__eqdf2; +const __ledf2 = @import("./cmpdf2.zig").__ledf2; +const __ltdf2 = @import("./cmpdf2.zig").__ltdf2; +const __nedf2 = @import("./cmpdf2.zig").__nedf2; + +const __gedf2 = @import("./gedf2.zig").__gedf2; +const __gtdf2 = @import("./gedf2.zig").__gtdf2; + +const __unorddf2 = @import("./unorddf2.zig").__unorddf2; const TestVector = struct { a: f64, @@ -21,25 +29,25 @@ const TestVector = struct { }; fn test__cmpdf2(vector: TestVector) bool { - if (comparedf2.__eqdf2(vector.a, vector.b) != vector.eqReference) { + if (__eqdf2(vector.a, vector.b) != vector.eqReference) { return false; } - if (comparedf2.__gedf2(vector.a, vector.b) != vector.geReference) { + if (__gedf2(vector.a, vector.b) != vector.geReference) { return false; } - if (comparedf2.__gtdf2(vector.a, vector.b) != vector.gtReference) { + if (__gtdf2(vector.a, vector.b) != vector.gtReference) { return false; } - if (comparedf2.__ledf2(vector.a, vector.b) != vector.leReference) { + if (__ledf2(vector.a, vector.b) != vector.leReference) { return false; } - if (comparedf2.__ltdf2(vector.a, vector.b) != vector.ltReference) { + if (__ltdf2(vector.a, vector.b) != vector.ltReference) { return false; } - if (comparedf2.__nedf2(vector.a, vector.b) != vector.neReference) { + if (__nedf2(vector.a, vector.b) != vector.neReference) { return false; } - if (comparedf2.__unorddf2(vector.a, vector.b) != vector.unReference) { + if (__unorddf2(vector.a, vector.b) != vector.unReference) { return false; } return true; diff --git a/lib/compiler_rt/comparesf2_test.zig b/lib/compiler_rt/comparesf2_test.zig index 8bc2c67956..b2fafd38dd 100644 --- a/lib/compiler_rt/comparesf2_test.zig +++ b/lib/compiler_rt/comparesf2_test.zig @@ -6,7 +6,15 @@ const std = @import("std"); const builtin = @import("builtin"); const is_test = builtin.is_test; -const comparesf2 = @import("compareXf2.zig"); +const __eqsf2 = @import("./cmpsf2.zig").__eqsf2; +const __lesf2 = @import("./cmpsf2.zig").__lesf2; +const __ltsf2 = @import("./cmpsf2.zig").__ltsf2; +const __nesf2 = @import("./cmpsf2.zig").__nesf2; + +const __gesf2 = @import("./gesf2.zig").__gesf2; +const __gtsf2 = @import("./gesf2.zig").__gtsf2; + +const __unordsf2 = @import("./unordsf2.zig").__unordsf2; const TestVector = struct { a: f32, @@ -21,25 +29,25 @@ const TestVector = struct { }; fn test__cmpsf2(vector: TestVector) bool { - if (comparesf2.__eqsf2(vector.a, vector.b) != vector.eqReference) { + if (__eqsf2(vector.a, vector.b) != vector.eqReference) { return false; } - if (comparesf2.__gesf2(vector.a, vector.b) != vector.geReference) { + if (__gesf2(vector.a, vector.b) != vector.geReference) { return false; } - if (comparesf2.__gtsf2(vector.a, vector.b) != vector.gtReference) { + if (__gtsf2(vector.a, vector.b) != vector.gtReference) { return false; } - if (comparesf2.__lesf2(vector.a, vector.b) != vector.leReference) { + if (__lesf2(vector.a, vector.b) != vector.leReference) { return false; } - if (comparesf2.__ltsf2(vector.a, vector.b) != vector.ltReference) { + if (__ltsf2(vector.a, vector.b) != vector.ltReference) { return false; } - if (comparesf2.__nesf2(vector.a, vector.b) != vector.neReference) { + if (__nesf2(vector.a, vector.b) != vector.neReference) { return false; } - if (comparesf2.__unordsf2(vector.a, vector.b) != vector.unReference) { + if (__unordsf2(vector.a, vector.b) != vector.unReference) { return false; } return true; diff --git a/lib/compiler_rt/divtf3.zig b/lib/compiler_rt/divtf3.zig index 66e01e1843..b6cabeab91 100644 --- a/lib/compiler_rt/divtf3.zig +++ b/lib/compiler_rt/divtf3.zig @@ -17,6 +17,10 @@ comptime { } } +pub fn __divtf3(a: f128, b: f128) callconv(.C) f128 { + return div(a, b); +} + fn __divkf3(a: f128, b: f128) callconv(.C) f128 { return div(a, b); } @@ -25,10 +29,6 @@ fn _Qp_div(c: *f128, a: *const f128, b: *const f128) callconv(.C) void { c.* = div(a.*, b.*); } -fn __divtf3(a: f128, b: f128) callconv(.C) f128 { - return div(a, b); -} - inline fn div(a: f128, b: f128) f128 { const Z = std.meta.Int(.unsigned, 128); diff --git a/lib/compiler_rt/extenddftf2.zig b/lib/compiler_rt/extenddftf2.zig index fa0053ad5c..21e497b3a4 100644 --- a/lib/compiler_rt/extenddftf2.zig +++ b/lib/compiler_rt/extenddftf2.zig @@ -13,7 +13,7 @@ comptime { } } -fn __extenddftf2(a: f64) callconv(.C) f128 { +pub fn __extenddftf2(a: f64) callconv(.C) f128 { return extendf(f128, f64, @bitCast(u64, a)); } @@ -22,6 +22,5 @@ fn __extenddfkf2(a: f64) callconv(.C) f128 { } fn _Qp_dtoq(c: *f128, a: f64) callconv(.C) void { - c.* = @import("extendXfYf2.zig").__extenddftf2(a); c.* = extendf(f128, f64, @bitCast(u64, a)); } diff --git a/lib/compiler_rt/extendf.zig b/lib/compiler_rt/extendf.zig index 15fa1ce0e8..8eb23c1d82 100644 --- a/lib/compiler_rt/extendf.zig +++ b/lib/compiler_rt/extendf.zig @@ -138,5 +138,5 @@ pub inline fn extend_f80(comptime src_t: type, a: std.meta.Int(.unsigned, @typeI } test { - _ = @import("extendXfYf2_test.zig"); + _ = @import("extendf_test.zig"); } diff --git a/lib/compiler_rt/extendXfYf2_test.zig b/lib/compiler_rt/extendf_test.zig similarity index 87% rename from lib/compiler_rt/extendXfYf2_test.zig rename to lib/compiler_rt/extendf_test.zig index d0c4f82e97..1102092a04 100644 --- a/lib/compiler_rt/extendXfYf2_test.zig +++ b/lib/compiler_rt/extendf_test.zig @@ -1,22 +1,22 @@ const builtin = @import("builtin"); -const __extendhfsf2 = @import("extendXfYf2.zig").__extendhfsf2; -const __extendhftf2 = @import("extendXfYf2.zig").__extendhftf2; -const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2; -const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2; -const F16T = @import("extendXfYf2.zig").F16T; +const __extendhfsf2 = @import("extendhfsf2.zig").__extendhfsf2; +const __extendhftf2 = @import("extendhftf2.zig").__extendhftf2; +const __extendsftf2 = @import("extendsftf2.zig").__extendsftf2; +const __extenddftf2 = @import("extenddftf2.zig").__extenddftf2; +const F16T = @import("./common.zig").F16T; -fn test__extenddftf2(a: f64, expectedHi: u64, expectedLo: u64) !void { +fn test__extenddftf2(a: f64, expected_hi: u64, expected_lo: u64) !void { const x = __extenddftf2(a); const rep = @bitCast(u128, x); const hi = @intCast(u64, rep >> 64); const lo = @truncate(u64, rep); - if (hi == expectedHi and lo == expectedLo) + if (hi == expected_hi and lo == expected_lo) return; // test other possible NaN representation(signal NaN) - if (expectedHi == 0x7fff800000000000 and expectedLo == 0x0) { + if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and ((hi & 0xffffffffffff) > 0 or lo > 0)) { @@ -43,18 +43,18 @@ fn test__extendhfsf2(a: u16, expected: u32) !void { return error.TestFailure; } -fn test__extendsftf2(a: f32, expectedHi: u64, expectedLo: u64) !void { +fn test__extendsftf2(a: f32, expected_hi: u64, expected_lo: u64) !void { const x = __extendsftf2(a); const rep = @bitCast(u128, x); const hi = @intCast(u64, rep >> 64); const lo = @truncate(u64, rep); - if (hi == expectedHi and lo == expectedLo) + if (hi == expected_hi and lo == expected_lo) return; // test other possible NaN representation(signal NaN) - if (expectedHi == 0x7fff800000000000 and expectedLo == 0x0) { + if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and ((hi & 0xffffffffffff) > 0 or lo > 0)) { @@ -159,18 +159,18 @@ fn makeInf32() f32 { return @bitCast(f32, @as(u32, 0x7f800000)); } -fn test__extendhftf2(a: u16, expectedHi: u64, expectedLo: u64) !void { +fn test__extendhftf2(a: u16, expected_hi: u64, expected_lo: u64) !void { const x = __extendhftf2(@bitCast(F16T, a)); const rep = @bitCast(u128, x); const hi = @intCast(u64, rep >> 64); const lo = @truncate(u64, rep); - if (hi == expectedHi and lo == expectedLo) + if (hi == expected_hi and lo == expected_lo) return; // test other possible NaN representation(signal NaN) - if (expectedHi == 0x7fff800000000000 and expectedLo == 0x0) { + if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and ((hi & 0xffffffffffff) > 0 or lo > 0)) { diff --git a/lib/compiler_rt/extendhfsf2.zig b/lib/compiler_rt/extendhfsf2.zig index 6ad4ff37df..56e6b9c01b 100644 --- a/lib/compiler_rt/extendhfsf2.zig +++ b/lib/compiler_rt/extendhfsf2.zig @@ -13,7 +13,7 @@ comptime { } } -fn __extendhfsf2(a: common.F16T) callconv(.C) f32 { +pub fn __extendhfsf2(a: common.F16T) callconv(.C) f32 { return extendf(f32, f16, @bitCast(u16, a)); } diff --git a/lib/compiler_rt/extendhftf2.zig b/lib/compiler_rt/extendhftf2.zig index a0c8eb5a1e..5d339fabce 100644 --- a/lib/compiler_rt/extendhftf2.zig +++ b/lib/compiler_rt/extendhftf2.zig @@ -7,6 +7,6 @@ comptime { @export(__extendhftf2, .{ .name = "__extendhftf2", .linkage = common.linkage }); } -fn __extendhftf2(a: common.F16T) callconv(.C) f128 { +pub fn __extendhftf2(a: common.F16T) callconv(.C) f128 { return extendf(f128, f16, @bitCast(u16, a)); } diff --git a/lib/compiler_rt/extendsftf2.zig b/lib/compiler_rt/extendsftf2.zig index a3b29802e6..acdc0d586d 100644 --- a/lib/compiler_rt/extendsftf2.zig +++ b/lib/compiler_rt/extendsftf2.zig @@ -13,7 +13,7 @@ comptime { } } -fn __extendsftf2(a: f32) callconv(.C) f128 { +pub fn __extendsftf2(a: f32) callconv(.C) f128 { return extendf(f128, f32, @bitCast(u32, a)); } diff --git a/lib/compiler_rt/fixdfdi.zig b/lib/compiler_rt/fixdfdi.zig index 2c3204db65..5935f23524 100644 --- a/lib/compiler_rt/fixdfdi.zig +++ b/lib/compiler_rt/fixdfdi.zig @@ -11,7 +11,7 @@ comptime { } } -fn __fixdfdi(a: f64) callconv(.C) i64 { +pub fn __fixdfdi(a: f64) callconv(.C) i64 { return floatToInt(i64, a); } diff --git a/lib/compiler_rt/fixdfsi.zig b/lib/compiler_rt/fixdfsi.zig index 415160f1de..983c84ccb1 100644 --- a/lib/compiler_rt/fixdfsi.zig +++ b/lib/compiler_rt/fixdfsi.zig @@ -11,7 +11,7 @@ comptime { } } -fn __fixdfsi(a: f64) callconv(.C) i32 { +pub fn __fixdfsi(a: f64) callconv(.C) i32 { return floatToInt(i32, a); } diff --git a/lib/compiler_rt/fixdfti.zig b/lib/compiler_rt/fixdfti.zig index 56ae2bbeb2..b2476ce2f3 100644 --- a/lib/compiler_rt/fixdfti.zig +++ b/lib/compiler_rt/fixdfti.zig @@ -7,6 +7,6 @@ comptime { @export(__fixdfti, .{ .name = "__fixdfti", .linkage = common.linkage }); } -fn __fixdfti(a: f64) callconv(.C) i128 { +pub fn __fixdfti(a: f64) callconv(.C) i128 { return floatToInt(i128, a); } diff --git a/lib/compiler_rt/fixsfdi.zig b/lib/compiler_rt/fixsfdi.zig index 71b0f047e2..0c4fb7f3f6 100644 --- a/lib/compiler_rt/fixsfdi.zig +++ b/lib/compiler_rt/fixsfdi.zig @@ -11,7 +11,7 @@ comptime { } } -fn __fixsfdi(a: f32) callconv(.C) i64 { +pub fn __fixsfdi(a: f32) callconv(.C) i64 { return floatToInt(i64, a); } diff --git a/lib/compiler_rt/fixsfsi.zig b/lib/compiler_rt/fixsfsi.zig index d48162928b..f48e354cd2 100644 --- a/lib/compiler_rt/fixsfsi.zig +++ b/lib/compiler_rt/fixsfsi.zig @@ -11,7 +11,7 @@ comptime { } } -fn __fixsfsi(a: f32) callconv(.C) i32 { +pub fn __fixsfsi(a: f32) callconv(.C) i32 { return floatToInt(i32, a); } diff --git a/lib/compiler_rt/fixsfti.zig b/lib/compiler_rt/fixsfti.zig index aefde49768..4bf68ec8b0 100644 --- a/lib/compiler_rt/fixsfti.zig +++ b/lib/compiler_rt/fixsfti.zig @@ -7,6 +7,6 @@ comptime { @export(__fixsfti, .{ .name = "__fixsfti", .linkage = common.linkage }); } -fn __fixsfti(a: f32) callconv(.C) i128 { +pub fn __fixsfti(a: f32) callconv(.C) i128 { return floatToInt(i128, a); } diff --git a/lib/compiler_rt/fixtfdi.zig b/lib/compiler_rt/fixtfdi.zig index 20bc4d89d6..9cc9835352 100644 --- a/lib/compiler_rt/fixtfdi.zig +++ b/lib/compiler_rt/fixtfdi.zig @@ -13,7 +13,7 @@ comptime { } } -fn __fixtfdi(a: f128) callconv(.C) i64 { +pub fn __fixtfdi(a: f128) callconv(.C) i64 { return floatToInt(i64, a); } diff --git a/lib/compiler_rt/fixtfsi.zig b/lib/compiler_rt/fixtfsi.zig index 44cac245fb..f46208f02b 100644 --- a/lib/compiler_rt/fixtfsi.zig +++ b/lib/compiler_rt/fixtfsi.zig @@ -13,7 +13,7 @@ comptime { } } -fn __fixtfsi(a: f128) callconv(.C) i32 { +pub fn __fixtfsi(a: f128) callconv(.C) i32 { return floatToInt(i32, a); } diff --git a/lib/compiler_rt/fixtfti.zig b/lib/compiler_rt/fixtfti.zig index a50ee5aa7c..9ba761729e 100644 --- a/lib/compiler_rt/fixtfti.zig +++ b/lib/compiler_rt/fixtfti.zig @@ -7,6 +7,6 @@ comptime { @export(__fixtfti, .{ .name = "__fixtfti", .linkage = common.linkage }); } -fn __fixtfti(a: f128) callconv(.C) i128 { +pub fn __fixtfti(a: f128) callconv(.C) i128 { return floatToInt(i128, a); } diff --git a/lib/compiler_rt/fixunsdfdi.zig b/lib/compiler_rt/fixunsdfdi.zig index afcb969ac6..edc0806405 100644 --- a/lib/compiler_rt/fixunsdfdi.zig +++ b/lib/compiler_rt/fixunsdfdi.zig @@ -11,7 +11,7 @@ comptime { } } -fn __fixunsdfdi(a: f64) callconv(.C) u64 { +pub fn __fixunsdfdi(a: f64) callconv(.C) u64 { return floatToInt(u64, a); } diff --git a/lib/compiler_rt/fixunsdfsi.zig b/lib/compiler_rt/fixunsdfsi.zig index 3675eb2c6f..cc413f3983 100644 --- a/lib/compiler_rt/fixunsdfsi.zig +++ b/lib/compiler_rt/fixunsdfsi.zig @@ -11,7 +11,7 @@ comptime { } } -fn __fixunsdfsi(a: f64) callconv(.C) u32 { +pub fn __fixunsdfsi(a: f64) callconv(.C) u32 { return floatToInt(u32, a); } diff --git a/lib/compiler_rt/fixunsdfti.zig b/lib/compiler_rt/fixunsdfti.zig index 96dd242f6d..ce3c4aabdd 100644 --- a/lib/compiler_rt/fixunsdfti.zig +++ b/lib/compiler_rt/fixunsdfti.zig @@ -7,6 +7,6 @@ comptime { @export(__fixunsdfti, .{ .name = "__fixunsdfti", .linkage = common.linkage }); } -fn __fixunsdfti(a: f64) callconv(.C) u128 { +pub fn __fixunsdfti(a: f64) callconv(.C) u128 { return floatToInt(u128, a); } diff --git a/lib/compiler_rt/fixunshfti.zig b/lib/compiler_rt/fixunshfti.zig index ac013263eb..b804c52f96 100644 --- a/lib/compiler_rt/fixunshfti.zig +++ b/lib/compiler_rt/fixunshfti.zig @@ -7,6 +7,6 @@ comptime { @export(__fixunshfti, .{ .name = "__fixunshfti", .linkage = common.linkage }); } -fn __fixunshfti(a: f16) callconv(.C) u128 { +pub fn __fixunshfti(a: f16) callconv(.C) u128 { return floatToInt(u128, a); } diff --git a/lib/compiler_rt/fixunssfdi.zig b/lib/compiler_rt/fixunssfdi.zig index 223bbcc2b5..544dfcd97e 100644 --- a/lib/compiler_rt/fixunssfdi.zig +++ b/lib/compiler_rt/fixunssfdi.zig @@ -11,7 +11,7 @@ comptime { } } -fn __fixunssfdi(a: f32) callconv(.C) u64 { +pub fn __fixunssfdi(a: f32) callconv(.C) u64 { return floatToInt(u64, a); } diff --git a/lib/compiler_rt/fixunssfsi.zig b/lib/compiler_rt/fixunssfsi.zig index 0a113af529..24b1e86694 100644 --- a/lib/compiler_rt/fixunssfsi.zig +++ b/lib/compiler_rt/fixunssfsi.zig @@ -11,7 +11,7 @@ comptime { } } -fn __fixunssfsi(a: f32) callconv(.C) u32 { +pub fn __fixunssfsi(a: f32) callconv(.C) u32 { return floatToInt(u32, a); } diff --git a/lib/compiler_rt/fixunssfti.zig b/lib/compiler_rt/fixunssfti.zig index 6b7690784d..7b1965b5ab 100644 --- a/lib/compiler_rt/fixunssfti.zig +++ b/lib/compiler_rt/fixunssfti.zig @@ -7,6 +7,6 @@ comptime { @export(__fixunssfti, .{ .name = "__fixunssfti", .linkage = common.linkage }); } -fn __fixunssfti(a: f32) callconv(.C) u128 { +pub fn __fixunssfti(a: f32) callconv(.C) u128 { return floatToInt(u128, a); } diff --git a/lib/compiler_rt/fixunstfdi.zig b/lib/compiler_rt/fixunstfdi.zig index f8b9aa7ac4..0657bf20c1 100644 --- a/lib/compiler_rt/fixunstfdi.zig +++ b/lib/compiler_rt/fixunstfdi.zig @@ -13,7 +13,7 @@ comptime { } } -fn __fixunstfdi(a: f128) callconv(.C) u64 { +pub fn __fixunstfdi(a: f128) callconv(.C) u64 { return floatToInt(u64, a); } diff --git a/lib/compiler_rt/fixunstfsi.zig b/lib/compiler_rt/fixunstfsi.zig index 64f70af649..70725ddf38 100644 --- a/lib/compiler_rt/fixunstfsi.zig +++ b/lib/compiler_rt/fixunstfsi.zig @@ -13,7 +13,7 @@ comptime { } } -fn __fixunstfsi(a: f128) callconv(.C) u32 { +pub fn __fixunstfsi(a: f128) callconv(.C) u32 { return floatToInt(u32, a); } diff --git a/lib/compiler_rt/fixunstfti.zig b/lib/compiler_rt/fixunstfti.zig index ba3a3ed12e..5e39db1065 100644 --- a/lib/compiler_rt/fixunstfti.zig +++ b/lib/compiler_rt/fixunstfti.zig @@ -7,6 +7,6 @@ comptime { @export(__fixunstfti, .{ .name = "__fixunstfti", .linkage = common.linkage }); } -fn __fixunstfti(a: f128) callconv(.C) u128 { +pub fn __fixunstfti(a: f128) callconv(.C) u128 { return floatToInt(u128, a); } diff --git a/lib/compiler_rt/fixunsxfti.zig b/lib/compiler_rt/fixunsxfti.zig index f2cd85cf2f..acd41469be 100644 --- a/lib/compiler_rt/fixunsxfti.zig +++ b/lib/compiler_rt/fixunsxfti.zig @@ -7,6 +7,6 @@ comptime { @export(__fixunsxfti, .{ .name = "__fixunsxfti", .linkage = common.linkage }); } -fn __fixunsxfti(a: f80) callconv(.C) u128 { +pub fn __fixunsxfti(a: f80) callconv(.C) u128 { return floatToInt(u128, a); } diff --git a/lib/compiler_rt/float_to_int_test.zig b/lib/compiler_rt/float_to_int_test.zig index 00ed455609..676c12e914 100644 --- a/lib/compiler_rt/float_to_int_test.zig +++ b/lib/compiler_rt/float_to_int_test.zig @@ -1,31 +1,33 @@ const std = @import("std"); const testing = std.testing; const math = std.math; -const fixXfYi = @import("fixXfYi.zig").fixXfYi; + +const __fixunshfti = @import("fixunshfti.zig").__fixunshfti; +const __fixunsxfti = @import("fixunsxfti.zig").__fixunsxfti; // Conversion from f32 -const __fixsfsi = @import("fixXfYi.zig").__fixsfsi; -const __fixunssfsi = @import("fixXfYi.zig").__fixunssfsi; -const __fixsfdi = @import("fixXfYi.zig").__fixsfdi; -const __fixunssfdi = @import("fixXfYi.zig").__fixunssfdi; -const __fixsfti = @import("fixXfYi.zig").__fixsfti; -const __fixunssfti = @import("fixXfYi.zig").__fixunssfti; +const __fixsfsi = @import("fixsfsi.zig").__fixsfsi; +const __fixunssfsi = @import("fixunssfsi.zig").__fixunssfsi; +const __fixsfdi = @import("fixsfdi.zig").__fixsfdi; +const __fixunssfdi = @import("fixunssfdi.zig").__fixunssfdi; +const __fixsfti = @import("fixsfti.zig").__fixsfti; +const __fixunssfti = @import("fixunssfti.zig").__fixunssfti; // Conversion from f64 -const __fixdfsi = @import("fixXfYi.zig").__fixdfsi; -const __fixunsdfsi = @import("fixXfYi.zig").__fixunsdfsi; -const __fixdfdi = @import("fixXfYi.zig").__fixdfdi; -const __fixunsdfdi = @import("fixXfYi.zig").__fixunsdfdi; -const __fixdfti = @import("fixXfYi.zig").__fixdfti; -const __fixunsdfti = @import("fixXfYi.zig").__fixunsdfti; +const __fixdfsi = @import("fixdfsi.zig").__fixdfsi; +const __fixunsdfsi = @import("fixunsdfsi.zig").__fixunsdfsi; +const __fixdfdi = @import("fixdfdi.zig").__fixdfdi; +const __fixunsdfdi = @import("fixunsdfdi.zig").__fixunsdfdi; +const __fixdfti = @import("fixdfti.zig").__fixdfti; +const __fixunsdfti = @import("fixunsdfti.zig").__fixunsdfti; // Conversion from f128 -const __fixtfsi = @import("fixXfYi.zig").__fixtfsi; -const __fixunstfsi = @import("fixXfYi.zig").__fixunstfsi; -const __fixtfdi = @import("fixXfYi.zig").__fixtfdi; -const __fixunstfdi = @import("fixXfYi.zig").__fixunstfdi; -const __fixtfti = @import("fixXfYi.zig").__fixtfti; -const __fixunstfti = @import("fixXfYi.zig").__fixunstfti; +const __fixtfsi = @import("fixtfsi.zig").__fixtfsi; +const __fixunstfsi = @import("fixunstfsi.zig").__fixunstfsi; +const __fixtfdi = @import("fixtfdi.zig").__fixtfdi; +const __fixunstfdi = @import("fixunstfdi.zig").__fixunstfdi; +const __fixtfti = @import("fixtfti.zig").__fixtfti; +const __fixunstfti = @import("fixunstfti.zig").__fixunstfti; fn test__fixsfsi(a: f32, expected: i32) !void { const x = __fixsfsi(a); @@ -927,21 +929,21 @@ test "fixunstfti" { } fn test__fixunshfti(a: f16, expected: u128) !void { - const x = fixXfYi(u128, a); + const x = __fixunshfti(a); try testing.expect(x == expected); } -test "fixXfYi for f16" { +test "fixunshfti for f16" { try test__fixunshfti(math.inf(f16), math.maxInt(u128)); try test__fixunshfti(math.floatMax(f16), 65504); } fn test__fixunsxfti(a: f80, expected: u128) !void { - const x = fixXfYi(u128, a); + const x = __fixunsxfti(a); try testing.expect(x == expected); } -test "fixXfYi for f80" { +test "fixunsxfti for f80" { try test__fixunsxfti(math.inf(f80), math.maxInt(u128)); try test__fixunsxfti(math.floatMax(f80), math.maxInt(u128)); try test__fixunsxfti(math.maxInt(u64), math.maxInt(u64)); diff --git a/lib/compiler_rt/floatdidf.zig b/lib/compiler_rt/floatdidf.zig index 9a0ecc6ed4..9117e2189d 100644 --- a/lib/compiler_rt/floatdidf.zig +++ b/lib/compiler_rt/floatdidf.zig @@ -11,7 +11,7 @@ comptime { } } -fn __floatdidf(a: i64) callconv(.C) f64 { +pub fn __floatdidf(a: i64) callconv(.C) f64 { return intToFloat(f64, a); } diff --git a/lib/compiler_rt/floatdisf.zig b/lib/compiler_rt/floatdisf.zig index d2ad3bb04b..3de94c5103 100644 --- a/lib/compiler_rt/floatdisf.zig +++ b/lib/compiler_rt/floatdisf.zig @@ -11,7 +11,7 @@ comptime { } } -fn __floatdisf(a: i64) callconv(.C) f32 { +pub fn __floatdisf(a: i64) callconv(.C) f32 { return intToFloat(f32, a); } diff --git a/lib/compiler_rt/floatditf.zig b/lib/compiler_rt/floatditf.zig index eb1ce3337b..731c6d8d86 100644 --- a/lib/compiler_rt/floatditf.zig +++ b/lib/compiler_rt/floatditf.zig @@ -13,7 +13,7 @@ comptime { } } -fn __floatditf(a: i64) callconv(.C) f128 { +pub fn __floatditf(a: i64) callconv(.C) f128 { return intToFloat(f128, a); } diff --git a/lib/compiler_rt/floatsidf.zig b/lib/compiler_rt/floatsidf.zig index 1775b08032..e31c2616fd 100644 --- a/lib/compiler_rt/floatsidf.zig +++ b/lib/compiler_rt/floatsidf.zig @@ -11,7 +11,7 @@ comptime { } } -fn __floatsidf(a: i32) callconv(.C) f64 { +pub fn __floatsidf(a: i32) callconv(.C) f64 { return intToFloat(f64, a); } diff --git a/lib/compiler_rt/floatsisf.zig b/lib/compiler_rt/floatsisf.zig index 45c7f8d6e4..87f83315c1 100644 --- a/lib/compiler_rt/floatsisf.zig +++ b/lib/compiler_rt/floatsisf.zig @@ -11,7 +11,7 @@ comptime { } } -fn __floatsisf(a: i32) callconv(.C) f32 { +pub fn __floatsisf(a: i32) callconv(.C) f32 { return intToFloat(f32, a); } diff --git a/lib/compiler_rt/floatsitf.zig b/lib/compiler_rt/floatsitf.zig index 0f43800436..0954199170 100644 --- a/lib/compiler_rt/floatsitf.zig +++ b/lib/compiler_rt/floatsitf.zig @@ -13,7 +13,7 @@ comptime { } } -fn __floatsitf(a: i32) callconv(.C) f128 { +pub fn __floatsitf(a: i32) callconv(.C) f128 { return intToFloat(f128, a); } diff --git a/lib/compiler_rt/floattidf.zig b/lib/compiler_rt/floattidf.zig index 54fff192c7..1f1ac2f2ef 100644 --- a/lib/compiler_rt/floattidf.zig +++ b/lib/compiler_rt/floattidf.zig @@ -7,6 +7,6 @@ comptime { @export(__floattidf, .{ .name = "__floattidf", .linkage = common.linkage }); } -fn __floattidf(a: i128) callconv(.C) f64 { +pub fn __floattidf(a: i128) callconv(.C) f64 { return intToFloat(f64, a); } diff --git a/lib/compiler_rt/floattisf.zig b/lib/compiler_rt/floattisf.zig index bc161d039f..5eb493d09b 100644 --- a/lib/compiler_rt/floattisf.zig +++ b/lib/compiler_rt/floattisf.zig @@ -7,6 +7,6 @@ comptime { @export(__floattisf, .{ .name = "__floattisf", .linkage = common.linkage }); } -fn __floattisf(a: i128) callconv(.C) f32 { +pub fn __floattisf(a: i128) callconv(.C) f32 { return intToFloat(f32, a); } diff --git a/lib/compiler_rt/floattitf.zig b/lib/compiler_rt/floattitf.zig index bcd75d8d83..0764c2d2c2 100644 --- a/lib/compiler_rt/floattitf.zig +++ b/lib/compiler_rt/floattitf.zig @@ -7,6 +7,6 @@ comptime { @export(__floattitf, .{ .name = "__floattitf", .linkage = common.linkage }); } -fn __floattitf(a: i128) callconv(.C) f128 { +pub fn __floattitf(a: i128) callconv(.C) f128 { return intToFloat(f128, a); } diff --git a/lib/compiler_rt/floatundidf.zig b/lib/compiler_rt/floatundidf.zig index aed9f42139..d49575639e 100644 --- a/lib/compiler_rt/floatundidf.zig +++ b/lib/compiler_rt/floatundidf.zig @@ -11,7 +11,7 @@ comptime { } } -fn __floatundidf(a: u64) callconv(.C) f64 { +pub fn __floatundidf(a: u64) callconv(.C) f64 { return intToFloat(f64, a); } diff --git a/lib/compiler_rt/floatundisf.zig b/lib/compiler_rt/floatundisf.zig index 1e6094cead..963670d85b 100644 --- a/lib/compiler_rt/floatundisf.zig +++ b/lib/compiler_rt/floatundisf.zig @@ -11,7 +11,7 @@ comptime { } } -fn __floatundisf(a: u64) callconv(.C) f32 { +pub fn __floatundisf(a: u64) callconv(.C) f32 { return intToFloat(f32, a); } diff --git a/lib/compiler_rt/floatunditf.zig b/lib/compiler_rt/floatunditf.zig index ed752e40c2..1eda21891d 100644 --- a/lib/compiler_rt/floatunditf.zig +++ b/lib/compiler_rt/floatunditf.zig @@ -13,7 +13,7 @@ comptime { } } -fn __floatunditf(a: u64) callconv(.C) f128 { +pub fn __floatunditf(a: u64) callconv(.C) f128 { return intToFloat(f128, a); } diff --git a/lib/compiler_rt/floatunsidf.zig b/lib/compiler_rt/floatunsidf.zig index 472167841b..1f5a47287a 100644 --- a/lib/compiler_rt/floatunsidf.zig +++ b/lib/compiler_rt/floatunsidf.zig @@ -11,7 +11,7 @@ comptime { } } -fn __floatunsidf(a: u32) callconv(.C) f64 { +pub fn __floatunsidf(a: u32) callconv(.C) f64 { return intToFloat(f64, a); } diff --git a/lib/compiler_rt/floatunsihf.zig b/lib/compiler_rt/floatunsihf.zig index c95de9c536..b2f679c18c 100644 --- a/lib/compiler_rt/floatunsihf.zig +++ b/lib/compiler_rt/floatunsihf.zig @@ -7,6 +7,6 @@ comptime { @export(__floatunsihf, .{ .name = "__floatunsihf", .linkage = common.linkage }); } -fn __floatunsihf(a: u32) callconv(.C) f16 { +pub fn __floatunsihf(a: u32) callconv(.C) f16 { return intToFloat(f16, a); } diff --git a/lib/compiler_rt/floatunsisf.zig b/lib/compiler_rt/floatunsisf.zig index b2956e9e28..46f336a4d8 100644 --- a/lib/compiler_rt/floatunsisf.zig +++ b/lib/compiler_rt/floatunsisf.zig @@ -11,7 +11,7 @@ comptime { } } -fn __floatunsisf(a: u32) callconv(.C) f32 { +pub fn __floatunsisf(a: u32) callconv(.C) f32 { return intToFloat(f32, a); } diff --git a/lib/compiler_rt/floatunsitf.zig b/lib/compiler_rt/floatunsitf.zig index 54d099c82a..bee656c801 100644 --- a/lib/compiler_rt/floatunsitf.zig +++ b/lib/compiler_rt/floatunsitf.zig @@ -13,7 +13,7 @@ comptime { } } -fn __floatunsitf(a: u32) callconv(.C) f128 { +pub fn __floatunsitf(a: u32) callconv(.C) f128 { return intToFloat(f128, a); } diff --git a/lib/compiler_rt/floatuntidf.zig b/lib/compiler_rt/floatuntidf.zig index 5b35379d38..a77a952fe9 100644 --- a/lib/compiler_rt/floatuntidf.zig +++ b/lib/compiler_rt/floatuntidf.zig @@ -7,6 +7,6 @@ comptime { @export(__floatuntidf, .{ .name = "__floatuntidf", .linkage = common.linkage }); } -fn __floatuntidf(a: u128) callconv(.C) f64 { +pub fn __floatuntidf(a: u128) callconv(.C) f64 { return intToFloat(f64, a); } diff --git a/lib/compiler_rt/floatuntisf.zig b/lib/compiler_rt/floatuntisf.zig index a3799e50b3..3edf636987 100644 --- a/lib/compiler_rt/floatuntisf.zig +++ b/lib/compiler_rt/floatuntisf.zig @@ -7,6 +7,6 @@ comptime { @export(__floatuntisf, .{ .name = "__floatuntisf", .linkage = common.linkage }); } -fn __floatuntisf(a: u128) callconv(.C) f32 { +pub fn __floatuntisf(a: u128) callconv(.C) f32 { return intToFloat(f32, a); } diff --git a/lib/compiler_rt/floatuntitf.zig b/lib/compiler_rt/floatuntitf.zig index c1b4b21d56..1a755cccdb 100644 --- a/lib/compiler_rt/floatuntitf.zig +++ b/lib/compiler_rt/floatuntitf.zig @@ -11,7 +11,7 @@ comptime { } } -fn __floatuntitf(a: u128) callconv(.C) f128 { +pub fn __floatuntitf(a: u128) callconv(.C) f128 { return intToFloat(f128, a); } diff --git a/lib/compiler_rt/gedf2.zig b/lib/compiler_rt/gedf2.zig index 816d690fa0..dad6586861 100644 --- a/lib/compiler_rt/gedf2.zig +++ b/lib/compiler_rt/gedf2.zig @@ -17,13 +17,13 @@ comptime { /// "These functions return a value greater than or equal to zero if neither /// argument is NaN, and a is greater than or equal to b." -fn __gedf2(a: f64, b: f64) callconv(.C) i32 { +pub fn __gedf2(a: f64, b: f64) callconv(.C) i32 { return @enumToInt(comparef.cmpf2(f64, comparef.GE, a, b)); } /// "These functions return a value greater than zero if neither argument is NaN, /// and a is strictly greater than b." -fn __gtdf2(a: f64, b: f64) callconv(.C) i32 { +pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 { return __gedf2(a, b); } diff --git a/lib/compiler_rt/gesf2.zig b/lib/compiler_rt/gesf2.zig index f29cd173a5..266e2f9c35 100644 --- a/lib/compiler_rt/gesf2.zig +++ b/lib/compiler_rt/gesf2.zig @@ -17,13 +17,13 @@ comptime { /// "These functions return a value greater than or equal to zero if neither /// argument is NaN, and a is greater than or equal to b." -fn __gesf2(a: f32, b: f32) callconv(.C) i32 { +pub fn __gesf2(a: f32, b: f32) callconv(.C) i32 { return @enumToInt(comparef.cmpf2(f32, comparef.GE, a, b)); } /// "These functions return a value greater than zero if neither argument is NaN, /// and a is strictly greater than b." -fn __gtsf2(a: f32, b: f32) callconv(.C) i32 { +pub fn __gtsf2(a: f32, b: f32) callconv(.C) i32 { return __gesf2(a, b); } diff --git a/lib/compiler_rt/int_to_float.zig b/lib/compiler_rt/int_to_float.zig index 135f9e1dbb..233dfec815 100644 --- a/lib/compiler_rt/int_to_float.zig +++ b/lib/compiler_rt/int_to_float.zig @@ -54,5 +54,5 @@ pub fn intToFloat(comptime T: type, x: anytype) T { } test { - _ = @import("floatXiYf_test.zig"); + _ = @import("int_to_float_test.zig"); } diff --git a/lib/compiler_rt/floatXiYf_test.zig b/lib/compiler_rt/int_to_float_test.zig similarity index 90% rename from lib/compiler_rt/floatXiYf_test.zig rename to lib/compiler_rt/int_to_float_test.zig index cffa2a5b42..f6eabbf4ba 100644 --- a/lib/compiler_rt/floatXiYf_test.zig +++ b/lib/compiler_rt/int_to_float_test.zig @@ -2,31 +2,32 @@ const std = @import("std"); const builtin = @import("builtin"); const testing = std.testing; const math = std.math; -const floatXiYf = @import("floatXiYf.zig").floatXiYf; + +const __floatunsihf = @import("floatunsihf.zig").__floatunsihf; // Conversion to f32 -const __floatsisf = @import("floatXiYf.zig").__floatsisf; -const __floatunsisf = @import("floatXiYf.zig").__floatunsisf; -const __floatdisf = @import("floatXiYf.zig").__floatdisf; -const __floatundisf = @import("floatXiYf.zig").__floatundisf; -const __floattisf = @import("floatXiYf.zig").__floattisf; -const __floatuntisf = @import("floatXiYf.zig").__floatuntisf; +const __floatsisf = @import("floatsisf.zig").__floatsisf; +const __floatunsisf = @import("floatunsisf.zig").__floatunsisf; +const __floatdisf = @import("floatdisf.zig").__floatdisf; +const __floatundisf = @import("floatundisf.zig").__floatundisf; +const __floattisf = @import("floattisf.zig").__floattisf; +const __floatuntisf = @import("floatuntisf.zig").__floatuntisf; // Conversion to f64 -const __floatsidf = @import("floatXiYf.zig").__floatsidf; -const __floatunsidf = @import("floatXiYf.zig").__floatunsidf; -const __floatdidf = @import("floatXiYf.zig").__floatdidf; -const __floatundidf = @import("floatXiYf.zig").__floatundidf; -const __floattidf = @import("floatXiYf.zig").__floattidf; -const __floatuntidf = @import("floatXiYf.zig").__floatuntidf; +const __floatsidf = @import("floatsidf.zig").__floatsidf; +const __floatunsidf = @import("floatunsidf.zig").__floatunsidf; +const __floatdidf = @import("floatdidf.zig").__floatdidf; +const __floatundidf = @import("floatundidf.zig").__floatundidf; +const __floattidf = @import("floattidf.zig").__floattidf; +const __floatuntidf = @import("floatuntidf.zig").__floatuntidf; // Conversion to f128 -const __floatsitf = @import("floatXiYf.zig").__floatsitf; -const __floatunsitf = @import("floatXiYf.zig").__floatunsitf; -const __floatditf = @import("floatXiYf.zig").__floatditf; -const __floatunditf = @import("floatXiYf.zig").__floatunditf; -const __floattitf = @import("floatXiYf.zig").__floattitf; -const __floatuntitf = @import("floatXiYf.zig").__floatuntitf; +const __floatsitf = @import("floatsitf.zig").__floatsitf; +const __floatunsitf = @import("floatunsitf.zig").__floatunsitf; +const __floatditf = @import("floatditf.zig").__floatditf; +const __floatunditf = @import("floatunditf.zig").__floatunditf; +const __floattitf = @import("floattitf.zig").__floattitf; +const __floatuntitf = @import("floatuntitf.zig").__floatuntitf; fn test__floatsisf(a: i32, expected: u32) !void { const r = __floatsisf(a); @@ -791,45 +792,47 @@ fn make_tf(high: u64, low: u64) f128 { } test "conversion to f16" { - try testing.expect(floatXiYf(f16, @as(u32, 0)) == 0.0); - try testing.expect(floatXiYf(f16, @as(u32, 1)) == 1.0); - try testing.expect(floatXiYf(f16, @as(u32, 65504)) == 65504); - try testing.expect(floatXiYf(f16, @as(u32, 65504 + (1 << 4))) == math.inf(f16)); + try testing.expect(__floatunsihf(@as(u32, 0)) == 0.0); + try testing.expect(__floatunsihf(@as(u32, 1)) == 1.0); + try testing.expect(__floatunsihf(@as(u32, 65504)) == 65504); + try testing.expect(__floatunsihf(@as(u32, 65504 + (1 << 4))) == math.inf(f16)); } test "conversion to f32" { - try testing.expect(floatXiYf(f32, @as(u32, 0)) == 0.0); - try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u32))) != 1.0); - try testing.expect(floatXiYf(f32, @as(i32, math.minInt(i32))) != 1.0); - try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u24))) == math.maxInt(u24)); - try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u24)) + 1) == math.maxInt(u24) + 1); // 0x100_0000 - Exact - try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u24)) + 2) == math.maxInt(u24) + 1); // 0x100_0001 - Tie: Rounds down to even - try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u24)) + 3) == math.maxInt(u24) + 3); // 0x100_0002 - Exact - try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u24)) + 4) == math.maxInt(u24) + 5); // 0x100_0003 - Tie: Rounds up to even - try testing.expect(floatXiYf(f32, @as(u32, math.maxInt(u24)) + 5) == math.maxInt(u24) + 5); // 0x100_0004 - Exact + try testing.expect(__floatunsisf(@as(u32, 0)) == 0.0); + try testing.expect(__floatunsisf(@as(u32, math.maxInt(u32))) != 1.0); + try testing.expect(__floatsisf(@as(i32, math.minInt(i32))) != 1.0); + try testing.expect(__floatunsisf(@as(u32, math.maxInt(u24))) == math.maxInt(u24)); + try testing.expect(__floatunsisf(@as(u32, math.maxInt(u24)) + 1) == math.maxInt(u24) + 1); // 0x100_0000 - Exact + try testing.expect(__floatunsisf(@as(u32, math.maxInt(u24)) + 2) == math.maxInt(u24) + 1); // 0x100_0001 - Tie: Rounds down to even + try testing.expect(__floatunsisf(@as(u32, math.maxInt(u24)) + 3) == math.maxInt(u24) + 3); // 0x100_0002 - Exact + try testing.expect(__floatunsisf(@as(u32, math.maxInt(u24)) + 4) == math.maxInt(u24) + 5); // 0x100_0003 - Tie: Rounds up to even + try testing.expect(__floatunsisf(@as(u32, math.maxInt(u24)) + 5) == math.maxInt(u24) + 5); // 0x100_0004 - Exact } test "conversion to f80" { if (builtin.zig_backend == .stage1 and builtin.cpu.arch != .x86_64) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/11408 - try testing.expect(floatXiYf(f80, @as(i80, -12)) == -12); - try testing.expect(@floatToInt(u80, floatXiYf(f80, @as(u64, math.maxInt(u64)) + 0)) == math.maxInt(u64) + 0); - try testing.expect(@floatToInt(u80, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 1)) == math.maxInt(u64) + 1); + const intToFloat = @import("./int_to_float.zig").intToFloat; - try testing.expect(floatXiYf(f80, @as(u32, 0)) == 0.0); - try testing.expect(floatXiYf(f80, @as(u32, 1)) == 1.0); - try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u32, math.maxInt(u24)) + 0)) == math.maxInt(u24)); - try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 0)) == math.maxInt(u64)); - try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 1)) == math.maxInt(u64) + 1); // Exact - try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 2)) == math.maxInt(u64) + 1); // Rounds down - try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 3)) == math.maxInt(u64) + 3); // Tie - Exact - try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u64)) + 4)) == math.maxInt(u64) + 5); // Rounds up + try testing.expect(intToFloat(f80, @as(i80, -12)) == -12); + try testing.expect(@floatToInt(u80, intToFloat(f80, @as(u64, math.maxInt(u64)) + 0)) == math.maxInt(u64) + 0); + try testing.expect(@floatToInt(u80, intToFloat(f80, @as(u80, math.maxInt(u64)) + 1)) == math.maxInt(u64) + 1); - try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u65)) + 0)) == math.maxInt(u65) + 1); // Rounds up - try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u65)) + 1)) == math.maxInt(u65) + 1); // Exact - try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u65)) + 2)) == math.maxInt(u65) + 1); // Rounds down - try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u65)) + 3)) == math.maxInt(u65) + 1); // Tie - Rounds down - try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u65)) + 4)) == math.maxInt(u65) + 5); // Rounds up - try testing.expect(@floatToInt(u128, floatXiYf(f80, @as(u80, math.maxInt(u65)) + 5)) == math.maxInt(u65) + 5); // Exact + try testing.expect(intToFloat(f80, @as(u32, 0)) == 0.0); + try testing.expect(intToFloat(f80, @as(u32, 1)) == 1.0); + try testing.expect(@floatToInt(u128, intToFloat(f80, @as(u32, math.maxInt(u24)) + 0)) == math.maxInt(u24)); + try testing.expect(@floatToInt(u128, intToFloat(f80, @as(u80, math.maxInt(u64)) + 0)) == math.maxInt(u64)); + try testing.expect(@floatToInt(u128, intToFloat(f80, @as(u80, math.maxInt(u64)) + 1)) == math.maxInt(u64) + 1); // Exact + try testing.expect(@floatToInt(u128, intToFloat(f80, @as(u80, math.maxInt(u64)) + 2)) == math.maxInt(u64) + 1); // Rounds down + try testing.expect(@floatToInt(u128, intToFloat(f80, @as(u80, math.maxInt(u64)) + 3)) == math.maxInt(u64) + 3); // Tie - Exact + try testing.expect(@floatToInt(u128, intToFloat(f80, @as(u80, math.maxInt(u64)) + 4)) == math.maxInt(u64) + 5); // Rounds up + + try testing.expect(@floatToInt(u128, intToFloat(f80, @as(u80, math.maxInt(u65)) + 0)) == math.maxInt(u65) + 1); // Rounds up + try testing.expect(@floatToInt(u128, intToFloat(f80, @as(u80, math.maxInt(u65)) + 1)) == math.maxInt(u65) + 1); // Exact + try testing.expect(@floatToInt(u128, intToFloat(f80, @as(u80, math.maxInt(u65)) + 2)) == math.maxInt(u65) + 1); // Rounds down + try testing.expect(@floatToInt(u128, intToFloat(f80, @as(u80, math.maxInt(u65)) + 3)) == math.maxInt(u65) + 1); // Tie - Rounds down + try testing.expect(@floatToInt(u128, intToFloat(f80, @as(u80, math.maxInt(u65)) + 4)) == math.maxInt(u65) + 5); // Rounds up + try testing.expect(@floatToInt(u128, intToFloat(f80, @as(u80, math.maxInt(u65)) + 5)) == math.maxInt(u65) + 5); // Exact } diff --git a/lib/compiler_rt/muldf3.zig b/lib/compiler_rt/muldf3.zig index 5fcd6e13a4..ef7ab9fbf7 100644 --- a/lib/compiler_rt/muldf3.zig +++ b/lib/compiler_rt/muldf3.zig @@ -11,7 +11,7 @@ comptime { } } -fn __muldf3(a: f64, b: f64) callconv(.C) f64 { +pub fn __muldf3(a: f64, b: f64) callconv(.C) f64 { return mulf3(f64, a, b); } diff --git a/lib/compiler_rt/mulf3_test.zig b/lib/compiler_rt/mulf3_test.zig index 0c7d84c5e0..203745e632 100644 --- a/lib/compiler_rt/mulf3_test.zig +++ b/lib/compiler_rt/mulf3_test.zig @@ -7,10 +7,10 @@ const math = std.math; const qnan128 = @bitCast(f128, @as(u128, 0x7fff800000000000) << 64); const inf128 = @bitCast(f128, @as(u128, 0x7fff000000000000) << 64); -const __multf3 = @import("mulf3.zig").__multf3; -const __mulxf3 = @import("mulf3.zig").__mulxf3; -const __muldf3 = @import("mulf3.zig").__muldf3; -const __mulsf3 = @import("mulf3.zig").__mulsf3; +const __multf3 = @import("multf3.zig").__multf3; +const __mulxf3 = @import("mulxf3.zig").__mulxf3; +const __muldf3 = @import("muldf3.zig").__muldf3; +const __mulsf3 = @import("mulsf3.zig").__mulsf3; // return true if equal // use two 64-bit integers intead of one 128-bit integer diff --git a/lib/compiler_rt/mulsf3.zig b/lib/compiler_rt/mulsf3.zig index 50b2142694..3294f5b1c7 100644 --- a/lib/compiler_rt/mulsf3.zig +++ b/lib/compiler_rt/mulsf3.zig @@ -11,7 +11,7 @@ comptime { } } -fn __mulsf3(a: f32, b: f32) callconv(.C) f32 { +pub fn __mulsf3(a: f32, b: f32) callconv(.C) f32 { return mulf3(f32, a, b); } diff --git a/lib/compiler_rt/multf3.zig b/lib/compiler_rt/multf3.zig index b18f5912a5..d4449ab72e 100644 --- a/lib/compiler_rt/multf3.zig +++ b/lib/compiler_rt/multf3.zig @@ -13,7 +13,7 @@ comptime { } } -fn __multf3(a: f128, b: f128) callconv(.C) f128 { +pub fn __multf3(a: f128, b: f128) callconv(.C) f128 { return mulf3(f128, a, b); } diff --git a/lib/compiler_rt/subtf3.zig b/lib/compiler_rt/subtf3.zig index aa50f73da8..9477f96917 100644 --- a/lib/compiler_rt/subtf3.zig +++ b/lib/compiler_rt/subtf3.zig @@ -12,7 +12,7 @@ comptime { } } -fn __subtf3(a: f128, b: f128) callconv(.C) f128 { +pub fn __subtf3(a: f128, b: f128) callconv(.C) f128 { return sub(a, b); } diff --git a/lib/compiler_rt/truncdfhf2.zig b/lib/compiler_rt/truncdfhf2.zig index 6d4135d249..a2d3bf1402 100644 --- a/lib/compiler_rt/truncdfhf2.zig +++ b/lib/compiler_rt/truncdfhf2.zig @@ -11,7 +11,7 @@ comptime { } } -fn __truncdfhf2(a: f64) callconv(.C) common.F16T { +pub fn __truncdfhf2(a: f64) callconv(.C) common.F16T { return @bitCast(common.F16T, truncf(f16, f64, a)); } diff --git a/lib/compiler_rt/truncdfsf2.zig b/lib/compiler_rt/truncdfsf2.zig index adf7705821..126dfff0fd 100644 --- a/lib/compiler_rt/truncdfsf2.zig +++ b/lib/compiler_rt/truncdfsf2.zig @@ -11,7 +11,7 @@ comptime { } } -fn __truncdfsf2(a: f64) callconv(.C) f32 { +pub fn __truncdfsf2(a: f64) callconv(.C) f32 { return truncf(f32, f64, a); } diff --git a/lib/compiler_rt/truncf_test.zig b/lib/compiler_rt/truncf_test.zig index 58c02a0a21..d4e93cd114 100644 --- a/lib/compiler_rt/truncf_test.zig +++ b/lib/compiler_rt/truncf_test.zig @@ -1,6 +1,12 @@ const std = @import("std"); const testing = std.testing; -const __truncsfhf2 = @import("truncXfYf2.zig").__truncsfhf2; + +const __truncsfhf2 = @import("truncsfhf2.zig").__truncsfhf2; +const __truncdfhf2 = @import("truncdfhf2.zig").__truncdfhf2; +const __truncdfsf2 = @import("truncdfsf2.zig").__truncdfsf2; +const __trunctfhf2 = @import("trunctfhf2.zig").__trunctfhf2; +const __trunctfsf2 = @import("trunctfsf2.zig").__trunctfsf2; +const __trunctfdf2 = @import("trunctfdf2.zig").__trunctfdf2; const __trunctfxf2 = @import("trunctfxf2.zig").__trunctfxf2; fn test__truncsfhf2(a: u32, expected: u16) !void { @@ -66,8 +72,6 @@ test "truncsfhf2" { try test__truncsfhf2(0x33000000, 0x0000); // 0x1.0p-25 -> zero } -const __truncdfhf2 = @import("truncXfYf2.zig").__truncdfhf2; - fn test__truncdfhf2(a: f64, expected: u16) void { const rep = @bitCast(u16, __truncdfhf2(a)); @@ -134,8 +138,6 @@ test "truncdfhf2" { test__truncdfhf2(65536.0, 0x7c00); } -const __trunctfsf2 = @import("truncXfYf2.zig").__trunctfsf2; - fn test__trunctfsf2(a: f128, expected: u32) void { const x = __trunctfsf2(a); @@ -169,8 +171,6 @@ test "trunctfsf2" { test__trunctfsf2(0x1.edcba9bb8c76a5a43dd21f334634p-435, 0x0); } -const __trunctfdf2 = @import("truncXfYf2.zig").__trunctfdf2; - fn test__trunctfdf2(a: f128, expected: u64) void { const x = __trunctfdf2(a); @@ -204,8 +204,6 @@ test "trunctfdf2" { test__trunctfdf2(0x1.edcbff8ad76ab5bf46463233214fp-435, 0x24cedcbff8ad76ab); } -const __truncdfsf2 = @import("truncXfYf2.zig").__truncdfsf2; - fn test__truncdfsf2(a: f64, expected: u32) void { const x = __truncdfsf2(a); @@ -241,8 +239,6 @@ test "truncdfsf2" { test__truncdfsf2(340282366920938463463374607431768211456.0, 0x7f800000); } -const __trunctfhf2 = @import("truncXfYf2.zig").__trunctfhf2; - fn test__trunctfhf2(a: f128, expected: u16) void { const x = __trunctfhf2(a); diff --git a/lib/compiler_rt/truncsfhf2.zig b/lib/compiler_rt/truncsfhf2.zig index 5fe0f329b6..329b3332a2 100644 --- a/lib/compiler_rt/truncsfhf2.zig +++ b/lib/compiler_rt/truncsfhf2.zig @@ -13,7 +13,7 @@ comptime { } } -fn __truncsfhf2(a: f32) callconv(.C) common.F16T { +pub fn __truncsfhf2(a: f32) callconv(.C) common.F16T { return @bitCast(common.F16T, truncf(f16, f32, a)); } diff --git a/lib/compiler_rt/trunctfdf2.zig b/lib/compiler_rt/trunctfdf2.zig index 3fe16ad6a2..e084d63d88 100644 --- a/lib/compiler_rt/trunctfdf2.zig +++ b/lib/compiler_rt/trunctfdf2.zig @@ -13,7 +13,7 @@ comptime { } } -fn __trunctfdf2(a: f128) callconv(.C) f64 { +pub fn __trunctfdf2(a: f128) callconv(.C) f64 { return truncf(f64, f128, a); } diff --git a/lib/compiler_rt/trunctfhf2.zig b/lib/compiler_rt/trunctfhf2.zig index a81cc138f1..b764a78455 100644 --- a/lib/compiler_rt/trunctfhf2.zig +++ b/lib/compiler_rt/trunctfhf2.zig @@ -7,6 +7,6 @@ comptime { @export(__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = common.linkage }); } -fn __trunctfhf2(a: f128) callconv(.C) common.F16T { +pub fn __trunctfhf2(a: f128) callconv(.C) common.F16T { return @bitCast(common.F16T, truncf(f16, f128, a)); } diff --git a/lib/compiler_rt/trunctfsf2.zig b/lib/compiler_rt/trunctfsf2.zig index f507f5ad2b..0fcd5e1e08 100644 --- a/lib/compiler_rt/trunctfsf2.zig +++ b/lib/compiler_rt/trunctfsf2.zig @@ -13,7 +13,7 @@ comptime { } } -fn __trunctfsf2(a: f128) callconv(.C) f32 { +pub fn __trunctfsf2(a: f128) callconv(.C) f32 { return truncf(f32, f128, a); } diff --git a/lib/compiler_rt/trunctfxf2.zig b/lib/compiler_rt/trunctfxf2.zig index 0cb7f86a1f..731f58f192 100644 --- a/lib/compiler_rt/trunctfxf2.zig +++ b/lib/compiler_rt/trunctfxf2.zig @@ -8,7 +8,7 @@ comptime { @export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = common.linkage }); } -fn __trunctfxf2(a: f128) callconv(.C) f80 { +pub fn __trunctfxf2(a: f128) callconv(.C) f80 { const src_sig_bits = math.floatMantissaBits(f128); const dst_sig_bits = math.floatMantissaBits(f80) - 1; // -1 for the integer bit diff --git a/lib/compiler_rt/unorddf2.zig b/lib/compiler_rt/unorddf2.zig index a6538f99d5..66910a18bf 100644 --- a/lib/compiler_rt/unorddf2.zig +++ b/lib/compiler_rt/unorddf2.zig @@ -11,7 +11,7 @@ comptime { } } -fn __unorddf2(a: f64, b: f64) callconv(.C) i32 { +pub fn __unorddf2(a: f64, b: f64) callconv(.C) i32 { return comparef.unordcmp(f64, a, b); } diff --git a/lib/compiler_rt/unordsf2.zig b/lib/compiler_rt/unordsf2.zig index 1dc7eb178e..78b388a75e 100644 --- a/lib/compiler_rt/unordsf2.zig +++ b/lib/compiler_rt/unordsf2.zig @@ -11,7 +11,7 @@ comptime { } } -fn __unordsf2(a: f32, b: f32) callconv(.C) i32 { +pub fn __unordsf2(a: f32, b: f32) callconv(.C) i32 { return comparef.unordcmp(f32, a, b); } From fcebdbe25d605f565b22f46ebfb9ca06845926a6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Jun 2022 16:53:40 -0700 Subject: [PATCH 1859/2031] compiler-rt: no more -ffunction-sections --- src/compiler_rt.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/src/compiler_rt.zig b/src/compiler_rt.zig index 0440235d58..70276231ce 100644 --- a/src/compiler_rt.zig +++ b/src/compiler_rt.zig @@ -113,7 +113,6 @@ pub fn buildCompilerRtLib(comp: *Compilation, compiler_rt_lib: *?CRTFile) !void .main_pkg = null, .output_mode = .Lib, .link_mode = .Static, - .function_sections = true, .thread_pool = comp.thread_pool, .libc_installation = comp.bin_file.options.libc_installation, .emit_bin = emit_bin, From b4f3e69342d176ad7a2572cf4fee704094faaada Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Jun 2022 16:54:49 -0700 Subject: [PATCH 1860/2031] update CMakeLists.txt source file list --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a92a40dc9..69bc292cd8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -715,6 +715,7 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/codegen/c.zig" "${CMAKE_SOURCE_DIR}/src/codegen/llvm.zig" "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig" + "${CMAKE_SOURCE_DIR}/src/compiler_rt.zig" "${CMAKE_SOURCE_DIR}/src/glibc.zig" "${CMAKE_SOURCE_DIR}/src/introspect.zig" "${CMAKE_SOURCE_DIR}/src/libc_installation.zig" From 5cd548e53081428d0e6b4a6b5a305317052c133a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Jun 2022 20:23:22 -0700 Subject: [PATCH 1861/2031] Compilation: multi-thread compiler-rt compiler_rt_lib and compiler_rt_obj are extracted from the generic JobQueue into simple boolean flags, and then handled explicitly inside performAllTheWork(). Introduced generic handling of allocation failure and made setMiscFailure not return a possible error. Building the compiler-rt static library now takes advantage of Compilation's ThreadPool. This introduced a problem, however, because now each of the object files of compiler-rt all perform AstGen for the full standard library and compiler-rt files. Even though all of them end up being cache hits except for the first ones, this is wasteful - O(N*M) where N is number of compilation units inside compiler-rt and M is the number of .zig files in the standard library and compiler-rt combined. More importantly, however, it causes a deadlock, because each thread interacts with a file system lock for doing AstGen on files, and threads end up waiting for each other. This will need to be handled with a process-level file caching system, or some other creative solution. --- src/Compilation.zig | 193 +++++++------ src/ThreadPool.zig | 81 +++--- src/WaitGroup.zig | 7 + src/compiler_rt.zig | 651 +++++++++++++++++++++++++------------------- 4 files changed, 535 insertions(+), 397 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 2858a28f42..65a2ad92b4 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -93,6 +93,9 @@ unwind_tables: bool, test_evented_io: bool, debug_compiler_runtime_libs: bool, debug_compile_errors: bool, +job_queued_compiler_rt_lib: bool = false, +job_queued_compiler_rt_obj: bool = false, +alloc_failure_occurred: bool = false, c_source_files: []const CSourceFile, clang_argv: []const []const u8, @@ -130,11 +133,11 @@ libssp_static_lib: ?CRTFile = null, /// Populated when we build the libc static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). libc_static_lib: ?CRTFile = null, -/// Populated when we build the libcompiler_rt static library. A Job to build this is placed in the queue -/// and resolved before calling linker.flush(). +/// Populated when we build the libcompiler_rt static library. A Job to build this is indicated +/// by setting `job_queued_compiler_rt_lib` and resolved before calling linker.flush(). compiler_rt_lib: ?CRTFile = null, -/// Populated when we build the compiler_rt_obj object. A Job to build this is placed in the queue -/// and resolved before calling linker.flush(). +/// Populated when we build the compiler_rt_obj object. A Job to build this is indicated +/// by setting `job_queued_compiler_rt_obj` and resolved before calling linker.flush(). compiler_rt_obj: ?CRTFile = null, glibc_so_files: ?glibc.BuiltSharedObjects = null, @@ -224,8 +227,6 @@ const Job = union(enum) { libcxxabi: void, libtsan: void, libssp: void, - compiler_rt_lib: void, - compiler_rt_obj: void, /// needed when not linking libc and using LLVM for code generation because it generates /// calls to, for example, memcpy and memset. zig_libc: void, @@ -1925,13 +1926,13 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { if (comp.bin_file.options.include_compiler_rt and capable_of_building_compiler_rt) { if (is_exe_or_dyn_lib) { log.debug("queuing a job to build compiler_rt_lib", .{}); - try comp.work_queue.writeItem(.{ .compiler_rt_lib = {} }); + comp.job_queued_compiler_rt_lib = true; } else if (options.output_mode != .Obj) { log.debug("queuing a job to build compiler_rt_obj", .{}); // If build-obj with -fcompiler-rt is requested, that is handled specially // elsewhere. In this case we are making a static library, so we ask // for a compiler-rt object to put in it. - try comp.work_queue.writeItem(.{ .compiler_rt_obj = {} }); + comp.job_queued_compiler_rt_obj = true; } } if (needs_c_symbols) { @@ -2021,6 +2022,7 @@ pub fn destroy(self: *Compilation) void { } pub fn clearMiscFailures(comp: *Compilation) void { + comp.alloc_failure_occurred = false; for (comp.misc_failures.values()) |*value| { value.deinit(comp.gpa); } @@ -2533,8 +2535,10 @@ pub fn makeBinFileWritable(self: *Compilation) !void { return self.bin_file.makeWritable(); } +/// This function is temporally single-threaded. pub fn totalErrorCount(self: *Compilation) usize { - var total: usize = self.failed_c_objects.count() + self.misc_failures.count(); + var total: usize = self.failed_c_objects.count() + self.misc_failures.count() + + @boolToInt(self.alloc_failure_occurred); if (self.bin_file.options.module) |module| { total += module.failed_exports.count(); @@ -2591,6 +2595,7 @@ pub fn totalErrorCount(self: *Compilation) usize { return total; } +/// This function is temporally single-threaded. pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { var arena = std.heap.ArenaAllocator.init(self.gpa); errdefer arena.deinit(); @@ -2623,6 +2628,9 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { for (self.misc_failures.values()) |*value| { try AllErrors.addPlainWithChildren(&arena, &errors, value.msg, value.children); } + if (self.alloc_failure_occurred) { + try AllErrors.addPlain(&arena, &errors, "memory allocation failure"); + } if (self.bin_file.options.module) |module| { { var it = module.failed_files.iterator(); @@ -2737,9 +2745,15 @@ pub fn performAllTheWork( var embed_file_prog_node = main_progress_node.start("Detect @embedFile updates", comp.embed_file_work_queue.count); defer embed_file_prog_node.end(); + // +1 for the link step + var compiler_rt_prog_node = main_progress_node.start("compiler_rt", compiler_rt.sources.len + 1); + defer compiler_rt_prog_node.end(); + comp.work_queue_wait_group.reset(); defer comp.work_queue_wait_group.wait(); + const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1; + { const astgen_frame = tracy.namedFrame("astgen"); defer astgen_frame.end(); @@ -2782,9 +2796,28 @@ pub fn performAllTheWork( comp, c_object, &c_obj_prog_node, &comp.work_queue_wait_group, }); } + + if (comp.job_queued_compiler_rt_lib) { + comp.job_queued_compiler_rt_lib = false; + + if (use_stage1) { + // stage1 LLVM backend uses the global context and thus cannot be used in + // a multi-threaded context. + buildCompilerRtOneShot(comp, .Lib, &comp.compiler_rt_lib); + } else { + comp.work_queue_wait_group.start(); + try comp.thread_pool.spawn(workerBuildCompilerRtLib, .{ + comp, &compiler_rt_prog_node, &comp.work_queue_wait_group, + }); + } + } + + if (comp.job_queued_compiler_rt_obj) { + comp.job_queued_compiler_rt_obj = false; + buildCompilerRtOneShot(comp, .Obj, &comp.compiler_rt_obj); + } } - const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1; if (!use_stage1) { const outdated_and_deleted_decls_frame = tracy.namedFrame("outdated_and_deleted_decls"); defer outdated_and_deleted_decls_frame.end(); @@ -2997,7 +3030,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { module.semaPkg(pkg) catch |err| switch (err) { error.CurrentWorkingDirectoryUnlinked, error.Unexpected, - => try comp.setMiscFailure( + => comp.lockAndSetMiscFailure( .analyze_pkg, "unexpected problem analyzing package '{s}'", .{pkg.root_src_path}, @@ -3012,7 +3045,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { glibc.buildCRTFile(comp, crt_file) catch |err| { // TODO Surface more error details. - try comp.setMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{ + comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc CRT file: {s}", .{ @errorName(err), }); }; @@ -3023,7 +3056,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { glibc.buildSharedObjects(comp) catch |err| { // TODO Surface more error details. - try comp.setMiscFailure( + comp.lockAndSetMiscFailure( .glibc_shared_objects, "unable to build glibc shared objects: {s}", .{@errorName(err)}, @@ -3036,7 +3069,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { musl.buildCRTFile(comp, crt_file) catch |err| { // TODO Surface more error details. - try comp.setMiscFailure( + comp.lockAndSetMiscFailure( .musl_crt_file, "unable to build musl CRT file: {s}", .{@errorName(err)}, @@ -3049,7 +3082,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { mingw.buildCRTFile(comp, crt_file) catch |err| { // TODO Surface more error details. - try comp.setMiscFailure( + comp.lockAndSetMiscFailure( .mingw_crt_file, "unable to build mingw-w64 CRT file: {s}", .{@errorName(err)}, @@ -3063,7 +3096,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { const link_lib = comp.bin_file.options.system_libs.keys()[index]; mingw.buildImportLib(comp, link_lib) catch |err| { // TODO Surface more error details. - try comp.setMiscFailure( + comp.lockAndSetMiscFailure( .windows_import_lib, "unable to generate DLL import .lib file: {s}", .{@errorName(err)}, @@ -3076,7 +3109,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { libunwind.buildStaticLib(comp) catch |err| { // TODO Surface more error details. - try comp.setMiscFailure( + comp.lockAndSetMiscFailure( .libunwind, "unable to build libunwind: {s}", .{@errorName(err)}, @@ -3089,7 +3122,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { libcxx.buildLibCXX(comp) catch |err| { // TODO Surface more error details. - try comp.setMiscFailure( + comp.lockAndSetMiscFailure( .libcxx, "unable to build libcxx: {s}", .{@errorName(err)}, @@ -3102,7 +3135,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { libcxx.buildLibCXXABI(comp) catch |err| { // TODO Surface more error details. - try comp.setMiscFailure( + comp.lockAndSetMiscFailure( .libcxxabi, "unable to build libcxxabi: {s}", .{@errorName(err)}, @@ -3115,7 +3148,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { libtsan.buildTsan(comp) catch |err| { // TODO Surface more error details. - try comp.setMiscFailure( + comp.lockAndSetMiscFailure( .libtsan, "unable to build TSAN library: {s}", .{@errorName(err)}, @@ -3128,49 +3161,13 @@ fn processOneJob(comp: *Compilation, job: Job) !void { wasi_libc.buildCRTFile(comp, crt_file) catch |err| { // TODO Surface more error details. - try comp.setMiscFailure( + comp.lockAndSetMiscFailure( .wasi_libc_crt_file, "unable to build WASI libc CRT file: {s}", .{@errorName(err)}, ); }; }, - .compiler_rt_lib => { - const named_frame = tracy.namedFrame("compiler_rt_lib"); - defer named_frame.end(); - - compiler_rt.buildCompilerRtLib( - comp, - &comp.compiler_rt_lib, - ) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.SubCompilationFailed => return, // error reported already - else => try comp.setMiscFailure( - .compiler_rt, - "unable to build compiler_rt: {s}", - .{@errorName(err)}, - ), - }; - }, - .compiler_rt_obj => { - const named_frame = tracy.namedFrame("compiler_rt_obj"); - defer named_frame.end(); - - comp.buildOutputFromZig( - "compiler_rt.zig", - .Obj, - &comp.compiler_rt_obj, - .compiler_rt, - ) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.SubCompilationFailed => return, // error reported already - else => try comp.setMiscFailure( - .compiler_rt, - "unable to build compiler_rt: {s}", - .{@errorName(err)}, - ), - }; - }, .libssp => { const named_frame = tracy.namedFrame("libssp"); defer named_frame.end(); @@ -3183,7 +3180,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { ) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.SubCompilationFailed => return, // error reported already - else => try comp.setMiscFailure( + else => comp.lockAndSetMiscFailure( .libssp, "unable to build libssp: {s}", .{@errorName(err)}, @@ -3202,7 +3199,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { ) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.SubCompilationFailed => return, // error reported already - else => try comp.setMiscFailure( + else => comp.lockAndSetMiscFailure( .zig_libc, "unable to build zig's multitarget libc: {s}", .{@errorName(err)}, @@ -3306,11 +3303,7 @@ fn workerUpdateBuiltinZigFile( comp.setMiscFailure(.write_builtin_zig, "unable to write builtin.zig to {s}: {s}", .{ dir_path, @errorName(err), - }) catch |oom| switch (oom) { - error.OutOfMemory => log.err("unable to write builtin.zig to {s}: {s}", .{ - dir_path, @errorName(err), - }), - }; + }); }; } @@ -3524,6 +3517,38 @@ fn workerUpdateCObject( }; } +fn buildCompilerRtOneShot( + comp: *Compilation, + output_mode: std.builtin.OutputMode, + out: *?CRTFile, +) void { + comp.buildOutputFromZig("compiler_rt.zig", output_mode, out, .compiler_rt) catch |err| switch (err) { + error.SubCompilationFailed => return, // error reported already + else => comp.lockAndSetMiscFailure( + .compiler_rt, + "unable to build compiler_rt: {s}", + .{@errorName(err)}, + ), + }; +} + +fn workerBuildCompilerRtLib( + comp: *Compilation, + progress_node: *std.Progress.Node, + wg: *WaitGroup, +) void { + defer wg.finish(); + + compiler_rt.buildCompilerRtLib(comp, progress_node) catch |err| switch (err) { + error.SubCompilationFailed => return, // error reported already + else => comp.lockAndSetMiscFailure( + .compiler_rt, + "unable to build compiler_rt: {s}", + .{@errorName(err)}, + ), + }; +} + fn reportRetryableCObjectError( comp: *Compilation, c_object: *CObject, @@ -4622,14 +4647,21 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool { comp.bin_file.options.object_format != .c; } -fn setMiscFailure( +fn setAllocFailure(comp: *Compilation) void { + log.debug("memory allocation failure", .{}); + comp.alloc_failure_occurred = true; +} + +/// Assumes that Compilation mutex is locked. +/// See also `lockAndSetMiscFailure`. +pub fn setMiscFailure( comp: *Compilation, tag: MiscTask, comptime format: []const u8, args: anytype, -) Allocator.Error!void { - try comp.misc_failures.ensureUnusedCapacity(comp.gpa, 1); - const msg = try std.fmt.allocPrint(comp.gpa, format, args); +) void { + comp.misc_failures.ensureUnusedCapacity(comp.gpa, 1) catch return comp.setAllocFailure(); + const msg = std.fmt.allocPrint(comp.gpa, format, args) catch return comp.setAllocFailure(); const gop = comp.misc_failures.getOrPutAssumeCapacity(tag); if (gop.found_existing) { gop.value_ptr.deinit(comp.gpa); @@ -4637,6 +4669,19 @@ fn setMiscFailure( gop.value_ptr.* = .{ .msg = msg }; } +/// See also `setMiscFailure`. +pub fn lockAndSetMiscFailure( + comp: *Compilation, + tag: MiscTask, + comptime format: []const u8, + args: anytype, +) void { + comp.mutex.lock(); + defer comp.mutex.unlock(); + + return setMiscFailure(comp, tag, format, args); +} + pub fn dump_argv(argv: []const []const u8) void { for (argv[0 .. argv.len - 1]) |arg| { std.debug.print("{s} ", .{arg}); @@ -4896,7 +4941,7 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void { } } -pub fn buildOutputFromZig( +fn buildOutputFromZig( comp: *Compilation, src_basename: []const u8, output_mode: std.builtin.OutputMode, @@ -4913,15 +4958,7 @@ pub fn buildOutputFromZig( .root_src_path = src_basename, }; defer main_pkg.deinitTable(comp.gpa); - - const root_name = root_name: { - const basename = if (std.fs.path.dirname(src_basename)) |dirname| - src_basename[dirname.len + 1 ..] - else - src_basename; - const root_name = basename[0 .. basename.len - std.fs.path.extension(basename).len]; - break :root_name root_name; - }; + const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len]; const target = comp.getTarget(); const bin_basename = try std.zig.binNameAlloc(comp.gpa, .{ .root_name = root_name, diff --git a/src/ThreadPool.zig b/src/ThreadPool.zig index 55e40ea287..7115adbddd 100644 --- a/src/ThreadPool.zig +++ b/src/ThreadPool.zig @@ -1,6 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); const ThreadPool = @This(); +const WaitGroup = @import("WaitGroup.zig"); mutex: std.Thread.Mutex = .{}, cond: std.Thread.Condition = .{}, @@ -19,8 +20,8 @@ const RunProto = switch (builtin.zig_backend) { else => *const fn (*Runnable) void, }; -pub fn init(self: *ThreadPool, allocator: std.mem.Allocator) !void { - self.* = .{ +pub fn init(pool: *ThreadPool, allocator: std.mem.Allocator) !void { + pool.* = .{ .allocator = allocator, .threads = &[_]std.Thread{}, }; @@ -30,48 +31,48 @@ pub fn init(self: *ThreadPool, allocator: std.mem.Allocator) !void { } const thread_count = std.math.max(1, std.Thread.getCpuCount() catch 1); - self.threads = try allocator.alloc(std.Thread, thread_count); - errdefer allocator.free(self.threads); + pool.threads = try allocator.alloc(std.Thread, thread_count); + errdefer allocator.free(pool.threads); // kill and join any threads we spawned previously on error. var spawned: usize = 0; - errdefer self.join(spawned); + errdefer pool.join(spawned); - for (self.threads) |*thread| { - thread.* = try std.Thread.spawn(.{}, worker, .{self}); + for (pool.threads) |*thread| { + thread.* = try std.Thread.spawn(.{}, worker, .{pool}); spawned += 1; } } -pub fn deinit(self: *ThreadPool) void { - self.join(self.threads.len); // kill and join all threads. - self.* = undefined; +pub fn deinit(pool: *ThreadPool) void { + pool.join(pool.threads.len); // kill and join all threads. + pool.* = undefined; } -fn join(self: *ThreadPool, spawned: usize) void { +fn join(pool: *ThreadPool, spawned: usize) void { if (builtin.single_threaded) { return; } { - self.mutex.lock(); - defer self.mutex.unlock(); + pool.mutex.lock(); + defer pool.mutex.unlock(); // ensure future worker threads exit the dequeue loop - self.is_running = false; + pool.is_running = false; } // wake up any sleeping threads (this can be done outside the mutex) // then wait for all the threads we know are spawned to complete. - self.cond.broadcast(); - for (self.threads[0..spawned]) |thread| { + pool.cond.broadcast(); + for (pool.threads[0..spawned]) |thread| { thread.join(); } - self.allocator.free(self.threads); + pool.allocator.free(pool.threads); } -pub fn spawn(self: *ThreadPool, comptime func: anytype, args: anytype) !void { +pub fn spawn(pool: *ThreadPool, comptime func: anytype, args: anytype) !void { if (builtin.single_threaded) { @call(.{}, func, args); return; @@ -98,41 +99,57 @@ pub fn spawn(self: *ThreadPool, comptime func: anytype, args: anytype) !void { }; { - self.mutex.lock(); - defer self.mutex.unlock(); + pool.mutex.lock(); + defer pool.mutex.unlock(); - const closure = try self.allocator.create(Closure); + const closure = try pool.allocator.create(Closure); closure.* = .{ .arguments = args, - .pool = self, + .pool = pool, }; - self.run_queue.prepend(&closure.run_node); + pool.run_queue.prepend(&closure.run_node); } // Notify waiting threads outside the lock to try and keep the critical section small. - self.cond.signal(); + pool.cond.signal(); } -fn worker(self: *ThreadPool) void { - self.mutex.lock(); - defer self.mutex.unlock(); +fn worker(pool: *ThreadPool) void { + pool.mutex.lock(); + defer pool.mutex.unlock(); while (true) { - while (self.run_queue.popFirst()) |run_node| { + while (pool.run_queue.popFirst()) |run_node| { // Temporarily unlock the mutex in order to execute the run_node - self.mutex.unlock(); - defer self.mutex.lock(); + pool.mutex.unlock(); + defer pool.mutex.lock(); const runFn = run_node.data.runFn; runFn(&run_node.data); } // Stop executing instead of waiting if the thread pool is no longer running. - if (self.is_running) { - self.cond.wait(&self.mutex); + if (pool.is_running) { + pool.cond.wait(&pool.mutex); } else { break; } } } + +pub fn waitAndWork(pool: *ThreadPool, wait_group: *WaitGroup) void { + while (!wait_group.isDone()) { + if (blk: { + pool.mutex.lock(); + defer pool.mutex.unlock(); + break :blk pool.run_queue.popFirst(); + }) |run_node| { + run_node.data.runFn(&run_node.data); + continue; + } + + wait_group.wait(); + return; + } +} diff --git a/src/WaitGroup.zig b/src/WaitGroup.zig index 860d0a8b4c..c8be6658db 100644 --- a/src/WaitGroup.zig +++ b/src/WaitGroup.zig @@ -37,3 +37,10 @@ pub fn reset(self: *WaitGroup) void { self.state.store(0, .Monotonic); self.event.reset(); } + +pub fn isDone(wg: *WaitGroup) bool { + const state = wg.state.load(.Acquire); + assert(state & is_waiting == 0); + + return (state / one_pending) == 0; +} diff --git a/src/compiler_rt.zig b/src/compiler_rt.zig index 70276231ce..4e43f0be88 100644 --- a/src/compiler_rt.zig +++ b/src/compiler_rt.zig @@ -12,58 +12,15 @@ const Compilation = @import("Compilation.zig"); const CRTFile = Compilation.CRTFile; const LinkObject = Compilation.LinkObject; const Package = @import("Package.zig"); +const WaitGroup = @import("WaitGroup.zig"); -pub fn buildCompilerRtLib(comp: *Compilation, compiler_rt_lib: *?CRTFile) !void { - const tracy_trace = trace(@src()); - defer tracy_trace.end(); - +pub fn buildCompilerRtLib(comp: *Compilation, progress_node: *std.Progress.Node) !void { var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); const target = comp.getTarget(); - // Use the global cache directory. - var cache_parent: Cache = .{ - .gpa = comp.gpa, - .manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}), - }; - defer cache_parent.manifest_dir.close(); - - var cache = cache_parent.obtain(); - defer cache.deinit(); - - cache.hash.add(sources.len); - for (sources) |source| { - const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{source}); - _ = try cache.addFile(full_path, null); - } - - cache.hash.addBytes(build_options.version); - cache.hash.addBytes(comp.zig_lib_directory.path orelse "."); - cache.hash.add(target.cpu.arch); - cache.hash.add(target.os.tag); - cache.hash.add(target.abi); - - const hit = try cache.hit(); - const digest = cache.final(); - const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); - - var o_directory: Compilation.Directory = .{ - .handle = try comp.global_cache_directory.handle.makeOpenPath(o_sub_path, .{}), - .path = try std.fs.path.join(arena, &[_][]const u8{ comp.global_cache_directory.path.?, o_sub_path }), - }; - defer o_directory.handle.close(); - - const ok_basename = "ok"; - const actual_hit = if (hit) blk: { - o_directory.handle.access(ok_basename, .{}) catch |err| switch (err) { - error.FileNotFound => break :blk false, - else => |e| return e, - }; - break :blk true; - } else false; - const root_name = "compiler_rt"; const basename = try std.zig.binNameAlloc(arena, .{ .root_name = root_name, @@ -71,257 +28,377 @@ pub fn buildCompilerRtLib(comp: *Compilation, compiler_rt_lib: *?CRTFile) !void .output_mode = .Lib, }); - if (!actual_hit) { - var progress: std.Progress = .{ .dont_print_on_dumb = true }; - var progress_node = progress.start("Compile Compiler-RT", sources.len + 1); - defer progress_node.end(); - if (comp.color == .off) progress.terminal = null; + var link_objects: [sources.len]LinkObject = undefined; + var crt_files = [1]?CRTFile{null} ** sources.len; + defer deinitCrtFiles(comp, crt_files); - progress_node.activate(); + { + var wg: WaitGroup = .{}; + defer comp.thread_pool.waitAndWork(&wg); - var link_objects: [sources.len]LinkObject = undefined; for (sources) |source, i| { - var obj_progress_node = progress_node.start(source, 0); - obj_progress_node.activate(); - defer obj_progress_node.end(); - - var tmp_crt_file: ?CRTFile = null; - defer if (tmp_crt_file) |*crt| crt.deinit(comp.gpa); - try comp.buildOutputFromZig(source, .Obj, &tmp_crt_file, .compiler_rt); - link_objects[i] = .{ - .path = try arena.dupe(u8, tmp_crt_file.?.full_object_path), - .must_link = true, - }; - } - - var lib_progress_node = progress_node.start(root_name, 0); - lib_progress_node.activate(); - defer lib_progress_node.end(); - - // TODO: This is extracted into a local variable to work around a stage1 miscompilation. - const emit_bin = Compilation.EmitLoc{ - .directory = o_directory, // Put it in the cache directory. - .basename = basename, - }; - const sub_compilation = try Compilation.create(comp.gpa, .{ - .local_cache_directory = comp.global_cache_directory, - .global_cache_directory = comp.global_cache_directory, - .zig_lib_directory = comp.zig_lib_directory, - .cache_mode = .whole, - .target = target, - .root_name = root_name, - .main_pkg = null, - .output_mode = .Lib, - .link_mode = .Static, - .thread_pool = comp.thread_pool, - .libc_installation = comp.bin_file.options.libc_installation, - .emit_bin = emit_bin, - .optimize_mode = comp.compilerRtOptMode(), - .want_sanitize_c = false, - .want_stack_check = false, - .want_red_zone = comp.bin_file.options.red_zone, - .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer, - .want_valgrind = false, - .want_tsan = false, - .want_pic = comp.bin_file.options.pic, - .want_pie = comp.bin_file.options.pie, - .want_lto = comp.bin_file.options.lto, - .emit_h = null, - .strip = comp.compilerRtStrip(), - .is_native_os = comp.bin_file.options.is_native_os, - .is_native_abi = comp.bin_file.options.is_native_abi, - .self_exe_path = comp.self_exe_path, - .link_objects = &link_objects, - .verbose_cc = comp.verbose_cc, - .verbose_link = comp.bin_file.options.verbose_link, - .verbose_air = comp.verbose_air, - .verbose_llvm_ir = comp.verbose_llvm_ir, - .verbose_cimport = comp.verbose_cimport, - .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, - .clang_passthrough_mode = comp.clang_passthrough_mode, - .skip_linker_dependencies = true, - .parent_compilation_link_libc = comp.bin_file.options.link_libc, - }); - defer sub_compilation.destroy(); - - try sub_compilation.updateSubCompilation(); - - if (o_directory.handle.createFile(ok_basename, .{})) |file| { - file.close(); - } else |err| { - std.log.warn("compiler-rt lib: failed to mark completion: {s}", .{@errorName(err)}); + wg.start(); + try comp.thread_pool.spawn(workerBuildObject, .{ + comp, progress_node, &wg, source, &crt_files[i], + }); } } - try cache.writeManifest(); + for (link_objects) |*link_object, i| { + link_object.* = .{ + .path = crt_files[i].?.full_object_path, + }; + } - assert(compiler_rt_lib.* == null); - compiler_rt_lib.* = .{ - .full_object_path = try std.fs.path.join(comp.gpa, &[_][]const u8{ - comp.global_cache_directory.path.?, - o_sub_path, - basename, + var link_progress_node = progress_node.start("link", 0); + link_progress_node.activate(); + defer link_progress_node.end(); + + // TODO: This is extracted into a local variable to work around a stage1 miscompilation. + const emit_bin = Compilation.EmitLoc{ + .directory = null, // Put it in the cache directory. + .basename = basename, + }; + const sub_compilation = try Compilation.create(comp.gpa, .{ + .local_cache_directory = comp.global_cache_directory, + .global_cache_directory = comp.global_cache_directory, + .zig_lib_directory = comp.zig_lib_directory, + .cache_mode = .whole, + .target = target, + .root_name = root_name, + .main_pkg = null, + .output_mode = .Lib, + .link_mode = .Static, + .thread_pool = comp.thread_pool, + .libc_installation = comp.bin_file.options.libc_installation, + .emit_bin = emit_bin, + .optimize_mode = comp.compilerRtOptMode(), + .want_sanitize_c = false, + .want_stack_check = false, + .want_red_zone = comp.bin_file.options.red_zone, + .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer, + .want_valgrind = false, + .want_tsan = false, + .want_pic = comp.bin_file.options.pic, + .want_pie = comp.bin_file.options.pie, + .want_lto = comp.bin_file.options.lto, + .emit_h = null, + .strip = comp.compilerRtStrip(), + .is_native_os = comp.bin_file.options.is_native_os, + .is_native_abi = comp.bin_file.options.is_native_abi, + .self_exe_path = comp.self_exe_path, + .link_objects = &link_objects, + .verbose_cc = comp.verbose_cc, + .verbose_link = comp.bin_file.options.verbose_link, + .verbose_air = comp.verbose_air, + .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_cimport = comp.verbose_cimport, + .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, + .clang_passthrough_mode = comp.clang_passthrough_mode, + .skip_linker_dependencies = true, + .parent_compilation_link_libc = comp.bin_file.options.link_libc, + }); + defer sub_compilation.destroy(); + + try sub_compilation.updateSubCompilation(); + + assert(comp.compiler_rt_lib == null); + comp.compiler_rt_lib = .{ + .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ + sub_compilation.bin_file.options.emit.?.sub_path, }), - .lock = cache.toOwnedLock(), + .lock = sub_compilation.bin_file.toOwnedLock(), }; } -const sources = &[_][]const u8{ - "compiler_rt/absvdi2.zig", - "compiler_rt/absvsi2.zig", - "compiler_rt/absvti2.zig", - "compiler_rt/adddf3.zig", - "compiler_rt/addo.zig", - "compiler_rt/addsf3.zig", - "compiler_rt/addtf3.zig", - "compiler_rt/addxf3.zig", - "compiler_rt/arm.zig", - "compiler_rt/atomics.zig", - "compiler_rt/aulldiv.zig", - "compiler_rt/aullrem.zig", - "compiler_rt/bswap.zig", - "compiler_rt/ceil.zig", - "compiler_rt/clear_cache.zig", - "compiler_rt/cmp.zig", - "compiler_rt/cmpdf2.zig", - "compiler_rt/cmpsf2.zig", - "compiler_rt/cmptf2.zig", - "compiler_rt/cmpxf2.zig", - "compiler_rt/cos.zig", - "compiler_rt/count0bits.zig", - "compiler_rt/divdf3.zig", - "compiler_rt/divsf3.zig", - "compiler_rt/divtf3.zig", - "compiler_rt/divti3.zig", - "compiler_rt/divxf3.zig", - "compiler_rt/emutls.zig", - "compiler_rt/exp.zig", - "compiler_rt/exp2.zig", - "compiler_rt/extenddftf2.zig", - "compiler_rt/extenddfxf2.zig", - "compiler_rt/extendhfsf2.zig", - "compiler_rt/extendhftf2.zig", - "compiler_rt/extendhfxf2.zig", - "compiler_rt/extendsfdf2.zig", - "compiler_rt/extendsftf2.zig", - "compiler_rt/extendsfxf2.zig", - "compiler_rt/extendxftf2.zig", - "compiler_rt/fabs.zig", - "compiler_rt/fixdfdi.zig", - "compiler_rt/fixdfsi.zig", - "compiler_rt/fixdfti.zig", - "compiler_rt/fixhfdi.zig", - "compiler_rt/fixhfsi.zig", - "compiler_rt/fixhfti.zig", - "compiler_rt/fixsfdi.zig", - "compiler_rt/fixsfsi.zig", - "compiler_rt/fixsfti.zig", - "compiler_rt/fixtfdi.zig", - "compiler_rt/fixtfsi.zig", - "compiler_rt/fixtfti.zig", - "compiler_rt/fixunsdfdi.zig", - "compiler_rt/fixunsdfsi.zig", - "compiler_rt/fixunsdfti.zig", - "compiler_rt/fixunshfdi.zig", - "compiler_rt/fixunshfsi.zig", - "compiler_rt/fixunshfti.zig", - "compiler_rt/fixunssfdi.zig", - "compiler_rt/fixunssfsi.zig", - "compiler_rt/fixunssfti.zig", - "compiler_rt/fixunstfdi.zig", - "compiler_rt/fixunstfsi.zig", - "compiler_rt/fixunstfti.zig", - "compiler_rt/fixunsxfdi.zig", - "compiler_rt/fixunsxfsi.zig", - "compiler_rt/fixunsxfti.zig", - "compiler_rt/fixxfdi.zig", - "compiler_rt/fixxfsi.zig", - "compiler_rt/fixxfti.zig", - "compiler_rt/floatdidf.zig", - "compiler_rt/floatdihf.zig", - "compiler_rt/floatdisf.zig", - "compiler_rt/floatditf.zig", - "compiler_rt/floatdixf.zig", - "compiler_rt/floatsidf.zig", - "compiler_rt/floatsihf.zig", - "compiler_rt/floatsisf.zig", - "compiler_rt/floatsitf.zig", - "compiler_rt/floatsixf.zig", - "compiler_rt/floattidf.zig", - "compiler_rt/floattihf.zig", - "compiler_rt/floattisf.zig", - "compiler_rt/floattitf.zig", - "compiler_rt/floattixf.zig", - "compiler_rt/floatundidf.zig", - "compiler_rt/floatundihf.zig", - "compiler_rt/floatundisf.zig", - "compiler_rt/floatunditf.zig", - "compiler_rt/floatundixf.zig", - "compiler_rt/floatunsidf.zig", - "compiler_rt/floatunsihf.zig", - "compiler_rt/floatunsisf.zig", - "compiler_rt/floatunsitf.zig", - "compiler_rt/floatunsixf.zig", - "compiler_rt/floatuntidf.zig", - "compiler_rt/floatuntihf.zig", - "compiler_rt/floatuntisf.zig", - "compiler_rt/floatuntitf.zig", - "compiler_rt/floatuntixf.zig", - "compiler_rt/floor.zig", - "compiler_rt/fma.zig", - "compiler_rt/fmax.zig", - "compiler_rt/fmin.zig", - "compiler_rt/fmod.zig", - "compiler_rt/gedf2.zig", - "compiler_rt/gesf2.zig", - "compiler_rt/getf2.zig", - "compiler_rt/gexf2.zig", - "compiler_rt/int.zig", - "compiler_rt/log.zig", - "compiler_rt/log10.zig", - "compiler_rt/log2.zig", - "compiler_rt/modti3.zig", - "compiler_rt/muldf3.zig", - "compiler_rt/muldi3.zig", - "compiler_rt/mulf3.zig", - "compiler_rt/mulo.zig", - "compiler_rt/mulsf3.zig", - "compiler_rt/multf3.zig", - "compiler_rt/multi3.zig", - "compiler_rt/mulxf3.zig", - "compiler_rt/negXf2.zig", - "compiler_rt/negXi2.zig", - "compiler_rt/negv.zig", - "compiler_rt/os_version_check.zig", - "compiler_rt/parity.zig", - "compiler_rt/popcount.zig", - "compiler_rt/round.zig", - "compiler_rt/shift.zig", - "compiler_rt/sin.zig", - "compiler_rt/sincos.zig", - "compiler_rt/sqrt.zig", - "compiler_rt/stack_probe.zig", - "compiler_rt/subdf3.zig", - "compiler_rt/subo.zig", - "compiler_rt/subsf3.zig", - "compiler_rt/subtf3.zig", - "compiler_rt/subxf3.zig", - "compiler_rt/tan.zig", - "compiler_rt/trunc.zig", - "compiler_rt/truncdfhf2.zig", - "compiler_rt/truncdfsf2.zig", - "compiler_rt/truncsfhf2.zig", - "compiler_rt/trunctfdf2.zig", - "compiler_rt/trunctfhf2.zig", - "compiler_rt/trunctfsf2.zig", - "compiler_rt/trunctfxf2.zig", - "compiler_rt/truncxfdf2.zig", - "compiler_rt/truncxfhf2.zig", - "compiler_rt/truncxfsf2.zig", - "compiler_rt/udivmodti4.zig", - "compiler_rt/udivti3.zig", - "compiler_rt/umodti3.zig", - "compiler_rt/unorddf2.zig", - "compiler_rt/unordsf2.zig", - "compiler_rt/unordtf2.zig", +fn deinitCrtFiles(comp: *Compilation, crt_files: [sources.len]?CRTFile) void { + const gpa = comp.gpa; + + for (crt_files) |opt_crt_file| { + var crt_file = opt_crt_file orelse continue; + crt_file.deinit(gpa); + } +} + +fn workerBuildObject( + comp: *Compilation, + progress_node: *std.Progress.Node, + wg: *WaitGroup, + src_basename: []const u8, + out: *?CRTFile, +) void { + defer wg.finish(); + + var obj_progress_node = progress_node.start(src_basename, 0); + obj_progress_node.activate(); + defer obj_progress_node.end(); + + buildObject(comp, src_basename, out) catch |err| switch (err) { + error.SubCompilationFailed => return, // error reported already + else => comp.lockAndSetMiscFailure( + .compiler_rt, + "unable to build compiler_rt: {s}", + .{@errorName(err)}, + ), + }; +} + +fn buildObject(comp: *Compilation, src_basename: []const u8, out: *?CRTFile) !void { + const gpa = comp.gpa; + + var root_src_path_buf: [64]u8 = undefined; + const root_src_path = std.fmt.bufPrint( + &root_src_path_buf, + "compiler_rt" ++ std.fs.path.sep_str ++ "{s}", + .{src_basename}, + ) catch unreachable; + + var main_pkg: Package = .{ + .root_src_directory = comp.zig_lib_directory, + .root_src_path = root_src_path, + }; + defer main_pkg.deinitTable(gpa); + const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len]; + const target = comp.getTarget(); + const output_mode: std.builtin.OutputMode = .Obj; + const bin_basename = try std.zig.binNameAlloc(gpa, .{ + .root_name = root_name, + .target = target, + .output_mode = output_mode, + }); + defer gpa.free(bin_basename); + + const emit_bin = Compilation.EmitLoc{ + .directory = null, // Put it in the cache directory. + .basename = bin_basename, + }; + const sub_compilation = try Compilation.create(gpa, .{ + .global_cache_directory = comp.global_cache_directory, + .local_cache_directory = comp.global_cache_directory, + .zig_lib_directory = comp.zig_lib_directory, + .cache_mode = .whole, + .target = target, + .root_name = root_name, + .main_pkg = &main_pkg, + .output_mode = output_mode, + .thread_pool = comp.thread_pool, + .libc_installation = comp.bin_file.options.libc_installation, + .emit_bin = emit_bin, + .optimize_mode = comp.compilerRtOptMode(), + .link_mode = .Static, + .want_sanitize_c = false, + .want_stack_check = false, + .want_red_zone = comp.bin_file.options.red_zone, + .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer, + .want_valgrind = false, + .want_tsan = false, + .want_pic = comp.bin_file.options.pic, + .want_pie = comp.bin_file.options.pie, + .emit_h = null, + .strip = comp.compilerRtStrip(), + .is_native_os = comp.bin_file.options.is_native_os, + .is_native_abi = comp.bin_file.options.is_native_abi, + .self_exe_path = comp.self_exe_path, + .verbose_cc = comp.verbose_cc, + .verbose_link = comp.bin_file.options.verbose_link, + .verbose_air = comp.verbose_air, + .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_cimport = comp.verbose_cimport, + .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, + .clang_passthrough_mode = comp.clang_passthrough_mode, + .skip_linker_dependencies = true, + .parent_compilation_link_libc = comp.bin_file.options.link_libc, + }); + defer sub_compilation.destroy(); + + try sub_compilation.update(); + // Look for compilation errors in this sub_compilation. + var keep_errors = false; + var errors = try sub_compilation.getAllErrorsAlloc(); + defer if (!keep_errors) errors.deinit(sub_compilation.gpa); + + if (errors.list.len != 0) { + const misc_task_tag: Compilation.MiscTask = .compiler_rt; + + comp.mutex.lock(); + defer comp.mutex.unlock(); + + try comp.misc_failures.ensureUnusedCapacity(gpa, 1); + comp.misc_failures.putAssumeCapacityNoClobber(misc_task_tag, .{ + .msg = try std.fmt.allocPrint(gpa, "sub-compilation of {s} failed", .{ + @tagName(misc_task_tag), + }), + .children = errors, + }); + keep_errors = true; + return error.SubCompilationFailed; + } + + assert(out.* == null); + out.* = Compilation.CRTFile{ + .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(gpa, &[_][]const u8{ + sub_compilation.bin_file.options.emit.?.sub_path, + }), + .lock = sub_compilation.bin_file.toOwnedLock(), + }; +} + +pub const sources = &[_][]const u8{ + "absvdi2.zig", + "absvsi2.zig", + "absvti2.zig", + "adddf3.zig", + "addo.zig", + "addsf3.zig", + "addtf3.zig", + "addxf3.zig", + "arm.zig", + "atomics.zig", + "aulldiv.zig", + "aullrem.zig", + "bswap.zig", + "ceil.zig", + "clear_cache.zig", + "cmp.zig", + "cmpdf2.zig", + "cmpsf2.zig", + "cmptf2.zig", + "cmpxf2.zig", + "cos.zig", + "count0bits.zig", + "divdf3.zig", + "divsf3.zig", + "divtf3.zig", + "divti3.zig", + "divxf3.zig", + "emutls.zig", + "exp.zig", + "exp2.zig", + "extenddftf2.zig", + "extenddfxf2.zig", + "extendhfsf2.zig", + "extendhftf2.zig", + "extendhfxf2.zig", + "extendsfdf2.zig", + "extendsftf2.zig", + "extendsfxf2.zig", + "extendxftf2.zig", + "fabs.zig", + "fixdfdi.zig", + "fixdfsi.zig", + "fixdfti.zig", + "fixhfdi.zig", + "fixhfsi.zig", + "fixhfti.zig", + "fixsfdi.zig", + "fixsfsi.zig", + "fixsfti.zig", + "fixtfdi.zig", + "fixtfsi.zig", + "fixtfti.zig", + "fixunsdfdi.zig", + "fixunsdfsi.zig", + "fixunsdfti.zig", + "fixunshfdi.zig", + "fixunshfsi.zig", + "fixunshfti.zig", + "fixunssfdi.zig", + "fixunssfsi.zig", + "fixunssfti.zig", + "fixunstfdi.zig", + "fixunstfsi.zig", + "fixunstfti.zig", + "fixunsxfdi.zig", + "fixunsxfsi.zig", + "fixunsxfti.zig", + "fixxfdi.zig", + "fixxfsi.zig", + "fixxfti.zig", + "floatdidf.zig", + "floatdihf.zig", + "floatdisf.zig", + "floatditf.zig", + "floatdixf.zig", + "floatsidf.zig", + "floatsihf.zig", + "floatsisf.zig", + "floatsitf.zig", + "floatsixf.zig", + "floattidf.zig", + "floattihf.zig", + "floattisf.zig", + "floattitf.zig", + "floattixf.zig", + "floatundidf.zig", + "floatundihf.zig", + "floatundisf.zig", + "floatunditf.zig", + "floatundixf.zig", + "floatunsidf.zig", + "floatunsihf.zig", + "floatunsisf.zig", + "floatunsitf.zig", + "floatunsixf.zig", + "floatuntidf.zig", + "floatuntihf.zig", + "floatuntisf.zig", + "floatuntitf.zig", + "floatuntixf.zig", + "floor.zig", + "fma.zig", + "fmax.zig", + "fmin.zig", + "fmod.zig", + "gedf2.zig", + "gesf2.zig", + "getf2.zig", + "gexf2.zig", + "int.zig", + "log.zig", + "log10.zig", + "log2.zig", + "modti3.zig", + "muldf3.zig", + "muldi3.zig", + "mulf3.zig", + "mulo.zig", + "mulsf3.zig", + "multf3.zig", + "multi3.zig", + "mulxf3.zig", + "negXf2.zig", + "negXi2.zig", + "negv.zig", + "os_version_check.zig", + "parity.zig", + "popcount.zig", + "round.zig", + "shift.zig", + "sin.zig", + "sincos.zig", + "sqrt.zig", + "stack_probe.zig", + "subdf3.zig", + "subo.zig", + "subsf3.zig", + "subtf3.zig", + "subxf3.zig", + "tan.zig", + "trunc.zig", + "truncdfhf2.zig", + "truncdfsf2.zig", + "truncsfhf2.zig", + "trunctfdf2.zig", + "trunctfhf2.zig", + "trunctfsf2.zig", + "trunctfxf2.zig", + "truncxfdf2.zig", + "truncxfhf2.zig", + "truncxfsf2.zig", + "udivmodti4.zig", + "udivti3.zig", + "umodti3.zig", + "unorddf2.zig", + "unordsf2.zig", + "unordtf2.zig", }; From e798a3a7796892427ac4561839a35286108bbf6c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 16 Jun 2022 20:51:15 -0700 Subject: [PATCH 1862/2031] compiler-rt: disable separate compilation units --- src/Compilation.zig | 4 +++- src/compiler_rt.zig | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 65a2ad92b4..89e54b598f 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2800,7 +2800,9 @@ pub fn performAllTheWork( if (comp.job_queued_compiler_rt_lib) { comp.job_queued_compiler_rt_lib = false; - if (use_stage1) { + // I have disabled the multi-threaded compiler-rt for now until + // the threading deadlock is resolved. + if (use_stage1 or true) { // stage1 LLVM backend uses the global context and thus cannot be used in // a multi-threaded context. buildCompilerRtOneShot(comp, .Lib, &comp.compiler_rt_lib); diff --git a/src/compiler_rt.zig b/src/compiler_rt.zig index 4e43f0be88..185ebf6b16 100644 --- a/src/compiler_rt.zig +++ b/src/compiler_rt.zig @@ -69,6 +69,7 @@ pub fn buildCompilerRtLib(comp: *Compilation, progress_node: *std.Progress.Node) .main_pkg = null, .output_mode = .Lib, .link_mode = .Static, + .function_sections = true, .thread_pool = comp.thread_pool, .libc_installation = comp.bin_file.options.libc_installation, .emit_bin = emit_bin, @@ -186,6 +187,7 @@ fn buildObject(comp: *Compilation, src_basename: []const u8, out: *?CRTFile) !vo .emit_bin = emit_bin, .optimize_mode = comp.compilerRtOptMode(), .link_mode = .Static, + .function_sections = true, .want_sanitize_c = false, .want_stack_check = false, .want_red_zone = comp.bin_file.options.red_zone, From bcf3a7d600ee409ec8e24fb1640fbda1d4e11660 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 17 Jun 2022 16:40:25 -0700 Subject: [PATCH 1863/2031] compiler-rt: gedf2 and gesf2 --- lib/compiler_rt/gedf2.zig | 2 +- lib/compiler_rt/gesf2.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/compiler_rt/gedf2.zig b/lib/compiler_rt/gedf2.zig index dad6586861..684ba665b5 100644 --- a/lib/compiler_rt/gedf2.zig +++ b/lib/compiler_rt/gedf2.zig @@ -28,7 +28,7 @@ pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 { } fn __aeabi_dcmpge(a: f64, b: f64) callconv(.AAPCS) i32 { - return comparef.cmpf2(f64, comparef.GE, a, b) != .Less; + return @boolToInt(comparef.cmpf2(f64, comparef.GE, a, b) != .Less); } fn __aeabi_dcmpgt(a: f64, b: f64) callconv(.AAPCS) i32 { diff --git a/lib/compiler_rt/gesf2.zig b/lib/compiler_rt/gesf2.zig index 266e2f9c35..3d455e52bf 100644 --- a/lib/compiler_rt/gesf2.zig +++ b/lib/compiler_rt/gesf2.zig @@ -28,7 +28,7 @@ pub fn __gtsf2(a: f32, b: f32) callconv(.C) i32 { } fn __aeabi_fcmpge(a: f32, b: f32) callconv(.AAPCS) i32 { - return comparef.cmpf2(f32, comparef.GE, a, b) != .Less; + return @boolToInt(comparef.cmpf2(f32, comparef.GE, a, b) != .Less); } fn __aeabi_fcmpgt(a: f32, b: f32) callconv(.AAPCS) i32 { From 3efc229bbf8aaec9de54a702054b091bf890d56e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 17 Jun 2022 16:42:50 -0700 Subject: [PATCH 1864/2031] compiler-rt: musl ABI also needs __gnu_f2h_ieee etc --- lib/compiler_rt/common.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/compiler_rt/common.zig b/lib/compiler_rt/common.zig index b1462d21c1..2b7ef84764 100644 --- a/lib/compiler_rt/common.zig +++ b/lib/compiler_rt/common.zig @@ -7,7 +7,10 @@ pub const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal el pub const want_aeabi = builtin.cpu.arch.isARM() or builtin.cpu.arch.isThumb(); pub const want_ppc_abi = builtin.cpu.arch.isPPC() or builtin.cpu.arch.isPPC64(); pub const want_msvc_abi = builtin.abi == .msvc; -pub const want_gnu_abi = builtin.abi.isGnu(); +/// Example symbols: +/// * __gnu_f2h_ieee +/// * __gnu_h2f_ieee +pub const want_gnu_abi = builtin.abi.isGnu() or builtin.abi.isMusl(); pub const want_sparc_abi = builtin.cpu.arch.isSPARC(); // Avoid dragging in the runtime safety mechanisms into this .o file, From 0556a2ba53b8bbb1a60ff31d8dcdde78a8b16727 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 17 Jun 2022 18:10:00 -0700 Subject: [PATCH 1865/2031] compiler-rt: finish cleanups Finishes cleanups that I started in other commits in this branch. * Use common.linkage for all exports instead of redoing the logic in each file. * Remove pointless `@setRuntimeSafety` calls. * Avoid redundantly exporting multiple versions of functions. For example, if PPC wants `ceilf128` then don't also export `ceilq`; similarly if ARM wants `__aeabi_ddiv` then don't also export `__divdf3`. * Use `inline` for helper functions instead of making inline calls at callsites. --- lib/compiler_rt/arm.zig | 45 ++++++++--------- lib/compiler_rt/aulldiv.zig | 10 ++-- lib/compiler_rt/aullrem.zig | 10 ++-- lib/compiler_rt/bswap.zig | 13 +++-- lib/compiler_rt/ceil.zig | 38 ++++++--------- lib/compiler_rt/cmp.zig | 18 +++---- lib/compiler_rt/cos.zig | 28 ++++------- lib/compiler_rt/count0bits.zig | 29 +++++------ lib/compiler_rt/divdf3.zig | 34 ++++++------- lib/compiler_rt/divsf3.zig | 33 ++++++------- lib/compiler_rt/divti3.zig | 32 ++++++------ lib/compiler_rt/divxf3.zig | 6 +-- lib/compiler_rt/emutls.zig | 20 ++++---- lib/compiler_rt/exp.zig | 28 ++++------- lib/compiler_rt/exp2.zig | 28 ++++------- lib/compiler_rt/fabs.zig | 28 ++++------- lib/compiler_rt/floor.zig | 38 ++++++--------- lib/compiler_rt/fma.zig | 40 ++++++--------- lib/compiler_rt/fmax.zig | 28 ++++------- lib/compiler_rt/fmin.zig | 28 ++++------- lib/compiler_rt/fmod.zig | 31 +++--------- lib/compiler_rt/int.zig | 89 ++++++++++++++-------------------- lib/compiler_rt/log.zig | 38 ++++++--------- lib/compiler_rt/log10.zig | 38 ++++++--------- lib/compiler_rt/log2.zig | 38 ++++++--------- lib/compiler_rt/modti3.zig | 38 +++++++-------- lib/compiler_rt/muldi3.zig | 55 ++++++++++----------- lib/compiler_rt/mulo.zig | 14 +++--- lib/compiler_rt/multi3.zig | 41 ++++++++-------- lib/compiler_rt/negXf2.zig | 37 ++++++-------- lib/compiler_rt/negXi2.zig | 39 +++++++-------- lib/compiler_rt/negv.zig | 47 +++++++++--------- lib/compiler_rt/parity.zig | 42 ++++++++-------- lib/compiler_rt/popcount.zig | 50 +++++++++---------- lib/compiler_rt/round.zig | 38 ++++++--------- lib/compiler_rt/shift.zig | 54 +++++++++++---------- lib/compiler_rt/sin.zig | 38 ++++++--------- lib/compiler_rt/sincos.zig | 28 ++++------- lib/compiler_rt/sqrt.zig | 28 ++++------- lib/compiler_rt/subo.zig | 41 ++++++++-------- lib/compiler_rt/tan.zig | 40 ++++++--------- lib/compiler_rt/trunc.zig | 40 ++++++--------- lib/compiler_rt/udivmodti4.zig | 17 +++---- lib/compiler_rt/udivti3.zig | 19 ++++---- lib/compiler_rt/umodti3.zig | 25 +++++----- 45 files changed, 648 insertions(+), 851 deletions(-) diff --git a/lib/compiler_rt/arm.zig b/lib/compiler_rt/arm.zig index e119c43e68..145d3992f7 100644 --- a/lib/compiler_rt/arm.zig +++ b/lib/compiler_rt/arm.zig @@ -2,40 +2,41 @@ const std = @import("std"); const builtin = @import("builtin"); const arch = builtin.cpu.arch; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { if (!builtin.is_test) { if (arch.isARM() or arch.isThumb()) { - @export(__aeabi_unwind_cpp_pr0, .{ .name = "__aeabi_unwind_cpp_pr0", .linkage = linkage }); - @export(__aeabi_unwind_cpp_pr1, .{ .name = "__aeabi_unwind_cpp_pr1", .linkage = linkage }); - @export(__aeabi_unwind_cpp_pr2, .{ .name = "__aeabi_unwind_cpp_pr2", .linkage = linkage }); + @export(__aeabi_unwind_cpp_pr0, .{ .name = "__aeabi_unwind_cpp_pr0", .linkage = common.linkage }); + @export(__aeabi_unwind_cpp_pr1, .{ .name = "__aeabi_unwind_cpp_pr1", .linkage = common.linkage }); + @export(__aeabi_unwind_cpp_pr2, .{ .name = "__aeabi_unwind_cpp_pr2", .linkage = common.linkage }); - @export(__aeabi_ldivmod, .{ .name = "__aeabi_ldivmod", .linkage = linkage }); - @export(__aeabi_uldivmod, .{ .name = "__aeabi_uldivmod", .linkage = linkage }); + @export(__aeabi_ldivmod, .{ .name = "__aeabi_ldivmod", .linkage = common.linkage }); + @export(__aeabi_uldivmod, .{ .name = "__aeabi_uldivmod", .linkage = common.linkage }); - @export(__aeabi_idivmod, .{ .name = "__aeabi_idivmod", .linkage = linkage }); - @export(__aeabi_uidivmod, .{ .name = "__aeabi_uidivmod", .linkage = linkage }); + @export(__aeabi_idivmod, .{ .name = "__aeabi_idivmod", .linkage = common.linkage }); + @export(__aeabi_uidivmod, .{ .name = "__aeabi_uidivmod", .linkage = common.linkage }); - @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy", .linkage = linkage }); - @export(__aeabi_memcpy4, .{ .name = "__aeabi_memcpy4", .linkage = linkage }); - @export(__aeabi_memcpy8, .{ .name = "__aeabi_memcpy8", .linkage = linkage }); + @export(__aeabi_memcpy, .{ .name = "__aeabi_memcpy", .linkage = common.linkage }); + @export(__aeabi_memcpy4, .{ .name = "__aeabi_memcpy4", .linkage = common.linkage }); + @export(__aeabi_memcpy8, .{ .name = "__aeabi_memcpy8", .linkage = common.linkage }); - @export(__aeabi_memmove, .{ .name = "__aeabi_memmove", .linkage = linkage }); - @export(__aeabi_memmove4, .{ .name = "__aeabi_memmove4", .linkage = linkage }); - @export(__aeabi_memmove8, .{ .name = "__aeabi_memmove8", .linkage = linkage }); + @export(__aeabi_memmove, .{ .name = "__aeabi_memmove", .linkage = common.linkage }); + @export(__aeabi_memmove4, .{ .name = "__aeabi_memmove4", .linkage = common.linkage }); + @export(__aeabi_memmove8, .{ .name = "__aeabi_memmove8", .linkage = common.linkage }); - @export(__aeabi_memset, .{ .name = "__aeabi_memset", .linkage = linkage }); - @export(__aeabi_memset4, .{ .name = "__aeabi_memset4", .linkage = linkage }); - @export(__aeabi_memset8, .{ .name = "__aeabi_memset8", .linkage = linkage }); + @export(__aeabi_memset, .{ .name = "__aeabi_memset", .linkage = common.linkage }); + @export(__aeabi_memset4, .{ .name = "__aeabi_memset4", .linkage = common.linkage }); + @export(__aeabi_memset8, .{ .name = "__aeabi_memset8", .linkage = common.linkage }); - @export(__aeabi_memclr, .{ .name = "__aeabi_memclr", .linkage = linkage }); - @export(__aeabi_memclr4, .{ .name = "__aeabi_memclr4", .linkage = linkage }); - @export(__aeabi_memclr8, .{ .name = "__aeabi_memclr8", .linkage = linkage }); + @export(__aeabi_memclr, .{ .name = "__aeabi_memclr", .linkage = common.linkage }); + @export(__aeabi_memclr4, .{ .name = "__aeabi_memclr4", .linkage = common.linkage }); + @export(__aeabi_memclr8, .{ .name = "__aeabi_memclr8", .linkage = common.linkage }); if (builtin.os.tag == .linux) { - @export(__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = linkage }); + @export(__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = common.linkage }); } } } diff --git a/lib/compiler_rt/aulldiv.zig b/lib/compiler_rt/aulldiv.zig index 38009d7015..d9517c6d10 100644 --- a/lib/compiler_rt/aulldiv.zig +++ b/lib/compiler_rt/aulldiv.zig @@ -2,19 +2,19 @@ const std = @import("std"); const builtin = @import("builtin"); const arch = builtin.cpu.arch; const abi = builtin.abi; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Strong; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { if (arch == .i386 and abi == .msvc) { // Don't let LLVM apply the stdcall name mangling on those MSVC builtins - @export(_alldiv, .{ .name = "\x01__alldiv", .linkage = linkage }); - @export(_aulldiv, .{ .name = "\x01__aulldiv", .linkage = linkage }); + @export(_alldiv, .{ .name = "\x01__alldiv", .linkage = common.linkage }); + @export(_aulldiv, .{ .name = "\x01__aulldiv", .linkage = common.linkage }); } } pub fn _alldiv(a: i64, b: i64) callconv(.Stdcall) i64 { - @setRuntimeSafety(builtin.is_test); const s_a = a >> (64 - 1); const s_b = b >> (64 - 1); diff --git a/lib/compiler_rt/aullrem.zig b/lib/compiler_rt/aullrem.zig index 18e9eea0c6..43821eb9d3 100644 --- a/lib/compiler_rt/aullrem.zig +++ b/lib/compiler_rt/aullrem.zig @@ -2,19 +2,19 @@ const std = @import("std"); const builtin = @import("builtin"); const arch = builtin.cpu.arch; const abi = builtin.abi; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Strong; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { if (arch == .i386 and abi == .msvc) { // Don't let LLVM apply the stdcall name mangling on those MSVC builtins - @export(_allrem, .{ .name = "\x01__allrem", .linkage = linkage }); - @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = linkage }); + @export(_allrem, .{ .name = "\x01__allrem", .linkage = common.linkage }); + @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = common.linkage }); } } pub fn _allrem(a: i64, b: i64) callconv(.Stdcall) i64 { - @setRuntimeSafety(builtin.is_test); const s_a = a >> (64 - 1); const s_b = b >> (64 - 1); diff --git a/lib/compiler_rt/bswap.zig b/lib/compiler_rt/bswap.zig index bab39dfb59..9f7d2cb879 100644 --- a/lib/compiler_rt/bswap.zig +++ b/lib/compiler_rt/bswap.zig @@ -1,13 +1,13 @@ const std = @import("std"); const builtin = @import("builtin"); -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__bswapsi2, .{ .name = "__bswapsi2", .linkage = linkage }); - @export(__bswapdi2, .{ .name = "__bswapdi2", .linkage = linkage }); - @export(__bswapti2, .{ .name = "__bswapti2", .linkage = linkage }); + @export(__bswapsi2, .{ .name = "__bswapsi2", .linkage = common.linkage }); + @export(__bswapdi2, .{ .name = "__bswapdi2", .linkage = common.linkage }); + @export(__bswapti2, .{ .name = "__bswapti2", .linkage = common.linkage }); } // bswap - byteswap @@ -21,7 +21,6 @@ comptime { // 00 00 00 ff << 3*8 (rightmost byte) inline fn bswapXi2(comptime T: type, a: T) T { - @setRuntimeSafety(builtin.is_test); switch (@bitSizeOf(T)) { 32 => { // zig fmt: off diff --git a/lib/compiler_rt/ceil.zig b/lib/compiler_rt/ceil.zig index 9e7e4b3c2b..406f61fbb9 100644 --- a/lib/compiler_rt/ceil.zig +++ b/lib/compiler_rt/ceil.zig @@ -1,30 +1,26 @@ -// Ported from musl, which is licensed under the MIT license: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/ceil.c +//! Ported from musl, which is MIT licensed. +//! https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +//! +//! https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c +//! https://git.musl-libc.org/cgit/musl/tree/src/math/ceil.c const std = @import("std"); const builtin = @import("builtin"); const arch = builtin.cpu.arch; const math = std.math; const expect = std.testing.expect; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__ceilh, .{ .name = "__ceilh", .linkage = linkage }); - @export(ceilf, .{ .name = "ceilf", .linkage = linkage }); - @export(ceil, .{ .name = "ceil", .linkage = linkage }); - @export(__ceilx, .{ .name = "__ceilx", .linkage = linkage }); - @export(ceilq, .{ .name = "ceilq", .linkage = linkage }); - @export(ceill, .{ .name = "ceill", .linkage = linkage }); - - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(ceilf128, .{ .name = "ceilf128", .linkage = linkage }); - } - } + @export(__ceilh, .{ .name = "__ceilh", .linkage = common.linkage }); + @export(ceilf, .{ .name = "ceilf", .linkage = common.linkage }); + @export(ceil, .{ .name = "ceil", .linkage = common.linkage }); + @export(__ceilx, .{ .name = "__ceilx", .linkage = common.linkage }); + const ceilq_sym_name = if (common.want_ppc_abi) "ceilf128" else "ceilq"; + @export(ceilq, .{ .name = ceilq_sym_name, .linkage = common.linkage }); + @export(ceill, .{ .name = "ceill", .linkage = common.linkage }); } pub fn __ceilh(x: f16) callconv(.C) f16 { @@ -130,10 +126,6 @@ pub fn ceilq(x: f128) callconv(.C) f128 { } } -pub fn ceilf128(x: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, ceilq, .{x}); -} - pub fn ceill(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __ceilh(x), diff --git a/lib/compiler_rt/cmp.zig b/lib/compiler_rt/cmp.zig index 1ac2e93b06..8ff2c38cd4 100644 --- a/lib/compiler_rt/cmp.zig +++ b/lib/compiler_rt/cmp.zig @@ -1,16 +1,17 @@ const std = @import("std"); const builtin = @import("builtin"); const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__cmpsi2, .{ .name = "__cmpsi2", .linkage = linkage }); - @export(__cmpdi2, .{ .name = "__cmpdi2", .linkage = linkage }); - @export(__cmpti2, .{ .name = "__cmpti2", .linkage = linkage }); - @export(__ucmpsi2, .{ .name = "__ucmpsi2", .linkage = linkage }); - @export(__ucmpdi2, .{ .name = "__ucmpdi2", .linkage = linkage }); - @export(__ucmpti2, .{ .name = "__ucmpti2", .linkage = linkage }); + @export(__cmpsi2, .{ .name = "__cmpsi2", .linkage = common.linkage }); + @export(__cmpdi2, .{ .name = "__cmpdi2", .linkage = common.linkage }); + @export(__cmpti2, .{ .name = "__cmpti2", .linkage = common.linkage }); + @export(__ucmpsi2, .{ .name = "__ucmpsi2", .linkage = common.linkage }); + @export(__ucmpdi2, .{ .name = "__ucmpdi2", .linkage = common.linkage }); + @export(__ucmpti2, .{ .name = "__ucmpti2", .linkage = common.linkage }); } // cmp - signed compare @@ -24,7 +25,6 @@ comptime { // a > b => 2 inline fn XcmpXi2(comptime T: type, a: T, b: T) i32 { - @setRuntimeSafety(builtin.is_test); var cmp1: i32 = 0; var cmp2: i32 = 0; if (a > b) diff --git a/lib/compiler_rt/cos.zig b/lib/compiler_rt/cos.zig index 22df0a707d..311d927168 100644 --- a/lib/compiler_rt/cos.zig +++ b/lib/compiler_rt/cos.zig @@ -3,26 +3,22 @@ const builtin = @import("builtin"); const arch = builtin.cpu.arch; const math = std.math; const expect = std.testing.expect; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; const trig = @import("trig.zig"); const rem_pio2 = @import("rem_pio2.zig").rem_pio2; const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; comptime { - @export(__cosh, .{ .name = "__cosh", .linkage = linkage }); - @export(cosf, .{ .name = "cosf", .linkage = linkage }); - @export(cos, .{ .name = "cos", .linkage = linkage }); - @export(__cosx, .{ .name = "__cosx", .linkage = linkage }); - @export(cosq, .{ .name = "cosq", .linkage = linkage }); - @export(cosl, .{ .name = "cosl", .linkage = linkage }); - - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(cosf128, .{ .name = "cosf128", .linkage = linkage }); - } - } + @export(__cosh, .{ .name = "__cosh", .linkage = common.linkage }); + @export(cosf, .{ .name = "cosf", .linkage = common.linkage }); + @export(cos, .{ .name = "cos", .linkage = common.linkage }); + @export(__cosx, .{ .name = "__cosx", .linkage = common.linkage }); + const cosq_sym_name = if (common.want_ppc_abi) "cosf128" else "cosq"; + @export(cosq, .{ .name = cosq_sym_name, .linkage = common.linkage }); + @export(cosl, .{ .name = "cosl", .linkage = common.linkage }); } pub fn __cosh(a: f16) callconv(.C) f16 { @@ -126,10 +122,6 @@ pub fn cosq(a: f128) callconv(.C) f128 { return cos(@floatCast(f64, a)); } -pub fn cosf128(a: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, cosq, .{a}); -} - pub fn cosl(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __cosh(x), diff --git a/lib/compiler_rt/count0bits.zig b/lib/compiler_rt/count0bits.zig index 386a5c9657..d763e5c8a3 100644 --- a/lib/compiler_rt/count0bits.zig +++ b/lib/compiler_rt/count0bits.zig @@ -1,19 +1,20 @@ const std = @import("std"); const builtin = @import("builtin"); const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__clzsi2, .{ .name = "__clzsi2", .linkage = linkage }); - @export(__clzdi2, .{ .name = "__clzdi2", .linkage = linkage }); - @export(__clzti2, .{ .name = "__clzti2", .linkage = linkage }); - @export(__ctzsi2, .{ .name = "__ctzsi2", .linkage = linkage }); - @export(__ctzdi2, .{ .name = "__ctzdi2", .linkage = linkage }); - @export(__ctzti2, .{ .name = "__ctzti2", .linkage = linkage }); - @export(__ffssi2, .{ .name = "__ffssi2", .linkage = linkage }); - @export(__ffsdi2, .{ .name = "__ffsdi2", .linkage = linkage }); - @export(__ffsti2, .{ .name = "__ffsti2", .linkage = linkage }); + @export(__clzsi2, .{ .name = "__clzsi2", .linkage = common.linkage }); + @export(__clzdi2, .{ .name = "__clzdi2", .linkage = common.linkage }); + @export(__clzti2, .{ .name = "__clzti2", .linkage = common.linkage }); + @export(__ctzsi2, .{ .name = "__ctzsi2", .linkage = common.linkage }); + @export(__ctzdi2, .{ .name = "__ctzdi2", .linkage = common.linkage }); + @export(__ctzti2, .{ .name = "__ctzti2", .linkage = common.linkage }); + @export(__ffssi2, .{ .name = "__ffssi2", .linkage = common.linkage }); + @export(__ffsdi2, .{ .name = "__ffsdi2", .linkage = common.linkage }); + @export(__ffsti2, .{ .name = "__ffsti2", .linkage = common.linkage }); } // clz - count leading zeroes @@ -30,8 +31,6 @@ comptime { // - ffsXi2 for unoptimized little and big endian inline fn clzXi2(comptime T: type, a: T) i32 { - @setRuntimeSafety(builtin.is_test); - var x = switch (@bitSizeOf(T)) { 32 => @bitCast(u32, a), 64 => @bitCast(u64, a), @@ -169,8 +168,6 @@ pub fn __clzti2(a: i128) callconv(.C) i32 { } inline fn ctzXi2(comptime T: type, a: T) i32 { - @setRuntimeSafety(builtin.is_test); - var x = switch (@bitSizeOf(T)) { 32 => @bitCast(u32, a), 64 => @bitCast(u64, a), @@ -206,8 +203,6 @@ pub fn __ctzti2(a: i128) callconv(.C) i32 { } inline fn ffsXi2(comptime T: type, a: T) i32 { - @setRuntimeSafety(builtin.is_test); - var x = switch (@bitSizeOf(T)) { 32 => @bitCast(u32, a), 64 => @bitCast(u64, a), diff --git a/lib/compiler_rt/divdf3.zig b/lib/compiler_rt/divdf3.zig index 6d626272b5..dd22f4836c 100644 --- a/lib/compiler_rt/divdf3.zig +++ b/lib/compiler_rt/divdf3.zig @@ -1,30 +1,35 @@ -// Ported from: -// -// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/lib/builtins/divdf3.c +//! Ported from: +//! +//! https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/lib/builtins/divdf3.c const std = @import("std"); const builtin = @import("builtin"); const arch = builtin.cpu.arch; const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; - const common = @import("common.zig"); + const normalize = common.normalize; const wideMultiply = common.wideMultiply; + pub const panic = common.panic; comptime { - @export(__divdf3, .{ .name = "__divdf3", .linkage = linkage }); - - if (!is_test) { - if (arch.isARM() or arch.isThumb()) { - @export(__aeabi_ddiv, .{ .name = "__aeabi_ddiv", .linkage = linkage }); - } + if (common.want_aeabi) { + @export(__aeabi_ddiv, .{ .name = "__aeabi_ddiv", .linkage = common.linkage }); + } else { + @export(__divdf3, .{ .name = "__divdf3", .linkage = common.linkage }); } } pub fn __divdf3(a: f64, b: f64) callconv(.C) f64 { - @setRuntimeSafety(builtin.is_test); + return div(a, b); +} + +fn __aeabi_ddiv(a: f64, b: f64) callconv(.AAPCS) f64 { + return div(a, b); +} + +inline fn div(a: f64, b: f64) f64 { const Z = std.meta.Int(.unsigned, 64); const SignedZ = std.meta.Int(.signed, 64); @@ -220,11 +225,6 @@ pub fn __divdf3(a: f64, b: f64) callconv(.C) f64 { } } -pub fn __aeabi_ddiv(a: f64, b: f64) callconv(.AAPCS) f64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __divdf3, .{ a, b }); -} - test { _ = @import("divdf3_test.zig"); } diff --git a/lib/compiler_rt/divsf3.zig b/lib/compiler_rt/divsf3.zig index a0c1590eb3..13565f9b64 100644 --- a/lib/compiler_rt/divsf3.zig +++ b/lib/compiler_rt/divsf3.zig @@ -1,29 +1,33 @@ -// Ported from: -// -// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/lib/builtins/divsf3.c +//! Ported from: +//! +//! https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/lib/builtins/divsf3.c const std = @import("std"); const builtin = @import("builtin"); const arch = builtin.cpu.arch; -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; const common = @import("common.zig"); const normalize = common.normalize; + pub const panic = common.panic; comptime { - @export(__divsf3, .{ .name = "__divsf3", .linkage = linkage }); - - if (!is_test) { - if (arch.isARM() or arch.isThumb()) { - @export(__aeabi_fdiv, .{ .name = "__aeabi_fdiv", .linkage = linkage }); - } + if (common.want_aeabi) { + @export(__aeabi_fdiv, .{ .name = "__aeabi_fdiv", .linkage = common.linkage }); + } else { + @export(__divsf3, .{ .name = "__divsf3", .linkage = common.linkage }); } } pub fn __divsf3(a: f32, b: f32) callconv(.C) f32 { - @setRuntimeSafety(builtin.is_test); + return div(a, b); +} + +fn __aeabi_fdiv(a: f32, b: f32) callconv(.AAPCS) f32 { + return div(a, b); +} + +inline fn div(a: f32, b: f32) f32 { const Z = std.meta.Int(.unsigned, 32); const significandBits = std.math.floatMantissaBits(f32); @@ -201,11 +205,6 @@ pub fn __divsf3(a: f32, b: f32) callconv(.C) f32 { } } -pub fn __aeabi_fdiv(a: f32, b: f32) callconv(.AAPCS) f32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __divsf3, .{ a, b }); -} - test { _ = @import("divsf3_test.zig"); } diff --git a/lib/compiler_rt/divti3.zig b/lib/compiler_rt/divti3.zig index 20da8d3e1d..b99a9081a4 100644 --- a/lib/compiler_rt/divti3.zig +++ b/lib/compiler_rt/divti3.zig @@ -2,34 +2,42 @@ const std = @import("std"); const builtin = @import("builtin"); const udivmod = @import("udivmod.zig").udivmod; const arch = builtin.cpu.arch; -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { if (builtin.os.tag == .windows) { switch (arch) { .i386 => { - @export(__divti3, .{ .name = "__divti3", .linkage = linkage }); + @export(__divti3, .{ .name = "__divti3", .linkage = common.linkage }); }, .x86_64 => { // The "ti" functions must use Vector(2, u64) parameter types to adhere to the ABI // that LLVM expects compiler-rt to have. - @export(__divti3_windows_x86_64, .{ .name = "__divti3", .linkage = linkage }); + @export(__divti3_windows_x86_64, .{ .name = "__divti3", .linkage = common.linkage }); }, else => {}, } if (arch.isAARCH64()) { - @export(__divti3, .{ .name = "__divti3", .linkage = linkage }); + @export(__divti3, .{ .name = "__divti3", .linkage = common.linkage }); } } else { - @export(__divti3, .{ .name = "__divti3", .linkage = linkage }); + @export(__divti3, .{ .name = "__divti3", .linkage = common.linkage }); } } pub fn __divti3(a: i128, b: i128) callconv(.C) i128 { - @setRuntimeSafety(builtin.is_test); + return div(a, b); +} +const v128 = @import("std").meta.Vector(2, u64); + +fn __divti3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { + return @bitCast(v128, div(@bitCast(i128, a), @bitCast(i128, b))); +} + +inline fn div(a: i128, b: i128) i128 { const s_a = a >> (128 - 1); const s_b = b >> (128 - 1); @@ -41,14 +49,6 @@ pub fn __divti3(a: i128, b: i128) callconv(.C) i128 { return (@bitCast(i128, r) ^ s) -% s; } -const v128 = @import("std").meta.Vector(2, u64); -pub fn __divti3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { - return @bitCast(v128, @call(.{ .modifier = .always_inline }, __divti3, .{ - @bitCast(i128, a), - @bitCast(i128, b), - })); -} - test { _ = @import("divti3_test.zig"); } diff --git a/lib/compiler_rt/divxf3.zig b/lib/compiler_rt/divxf3.zig index 56811dfc8f..b8d27a6da0 100644 --- a/lib/compiler_rt/divxf3.zig +++ b/lib/compiler_rt/divxf3.zig @@ -1,20 +1,18 @@ const std = @import("std"); const builtin = @import("builtin"); const arch = builtin.cpu.arch; -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; const common = @import("common.zig"); const normalize = common.normalize; const wideMultiply = common.wideMultiply; + pub const panic = common.panic; comptime { - @export(__divxf3, .{ .name = "__divxf3", .linkage = linkage }); + @export(__divxf3, .{ .name = "__divxf3", .linkage = common.linkage }); } pub fn __divxf3(a: f80, b: f80) callconv(.C) f80 { - @setRuntimeSafety(builtin.is_test); const T = f80; const Z = std.meta.Int(.unsigned, @bitSizeOf(T)); diff --git a/lib/compiler_rt/emutls.zig b/lib/compiler_rt/emutls.zig index 7483177e0f..723eac4af2 100644 --- a/lib/compiler_rt/emutls.zig +++ b/lib/compiler_rt/emutls.zig @@ -1,25 +1,25 @@ -// __emutls_get_address specific builtin -// -// derived work from LLVM Compiler Infrastructure - release 8.0 (MIT) -// https://github.com/llvm-mirror/compiler-rt/blob/release_80/lib/builtins/emutls.c -// +//! __emutls_get_address specific builtin +//! +//! derived work from LLVM Compiler Infrastructure - release 8.0 (MIT) +//! https://github.com/llvm-mirror/compiler-rt/blob/release_80/lib/builtins/emutls.c const std = @import("std"); const builtin = @import("builtin"); -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); const abort = std.os.abort; const assert = std.debug.assert; const expect = std.testing.expect; -// defined in C as: -// typedef unsigned int gcc_word __attribute__((mode(word))); +/// defined in C as: +/// typedef unsigned int gcc_word __attribute__((mode(word))); const gcc_word = usize; +pub const panic = common.panic; + comptime { if (builtin.link_libc and builtin.os.tag == .openbsd) { - @export(__emutls_get_address, .{ .name = "__emutls_get_address", .linkage = linkage }); + @export(__emutls_get_address, .{ .name = "__emutls_get_address", .linkage = common.linkage }); } } diff --git a/lib/compiler_rt/exp.zig b/lib/compiler_rt/exp.zig index c7f6c95da6..f34f226be4 100644 --- a/lib/compiler_rt/exp.zig +++ b/lib/compiler_rt/exp.zig @@ -9,22 +9,18 @@ const builtin = @import("builtin"); const arch = builtin.cpu.arch; const math = std.math; const expect = std.testing.expect; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__exph, .{ .name = "__exph", .linkage = linkage }); - @export(expf, .{ .name = "expf", .linkage = linkage }); - @export(exp, .{ .name = "exp", .linkage = linkage }); - @export(__expx, .{ .name = "__expx", .linkage = linkage }); - @export(expq, .{ .name = "expq", .linkage = linkage }); - @export(expl, .{ .name = "expl", .linkage = linkage }); - - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(expf128, .{ .name = "expf128", .linkage = linkage }); - } - } + @export(__exph, .{ .name = "__exph", .linkage = common.linkage }); + @export(expf, .{ .name = "expf", .linkage = common.linkage }); + @export(exp, .{ .name = "exp", .linkage = common.linkage }); + @export(__expx, .{ .name = "__expx", .linkage = common.linkage }); + const expq_sym_name = if (common.want_ppc_abi) "expf128" else "expq"; + @export(expq, .{ .name = expq_sym_name, .linkage = common.linkage }); + @export(expl, .{ .name = "expl", .linkage = common.linkage }); } pub fn __exph(a: f16) callconv(.C) f16 { @@ -201,10 +197,6 @@ pub fn expq(a: f128) callconv(.C) f128 { return exp(@floatCast(f64, a)); } -pub fn expf128(a: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, expq, .{a}); -} - pub fn expl(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __exph(x), diff --git a/lib/compiler_rt/exp2.zig b/lib/compiler_rt/exp2.zig index 614df2cec0..e89a918501 100644 --- a/lib/compiler_rt/exp2.zig +++ b/lib/compiler_rt/exp2.zig @@ -9,22 +9,18 @@ const builtin = @import("builtin"); const arch = builtin.cpu.arch; const math = std.math; const expect = std.testing.expect; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__exp2h, .{ .name = "__exp2h", .linkage = linkage }); - @export(exp2f, .{ .name = "exp2f", .linkage = linkage }); - @export(exp2, .{ .name = "exp2", .linkage = linkage }); - @export(__exp2x, .{ .name = "__exp2x", .linkage = linkage }); - @export(exp2q, .{ .name = "exp2q", .linkage = linkage }); - @export(exp2l, .{ .name = "exp2l", .linkage = linkage }); - - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(exp2f128, .{ .name = "exp2f128", .linkage = linkage }); - } - } + @export(__exp2h, .{ .name = "__exp2h", .linkage = common.linkage }); + @export(exp2f, .{ .name = "exp2f", .linkage = common.linkage }); + @export(exp2, .{ .name = "exp2", .linkage = common.linkage }); + @export(__exp2x, .{ .name = "__exp2x", .linkage = common.linkage }); + const exp2q_sym_name = if (common.want_ppc_abi) "exp2f128" else "exp2q"; + @export(exp2q, .{ .name = exp2q_sym_name, .linkage = common.linkage }); + @export(exp2l, .{ .name = "exp2l", .linkage = common.linkage }); } pub fn __exp2h(x: f16) callconv(.C) f16 { @@ -168,10 +164,6 @@ pub fn exp2q(x: f128) callconv(.C) f128 { return exp2(@floatCast(f64, x)); } -pub fn exp2f128(x: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, exp2q, .{x}); -} - pub fn exp2l(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __exp2h(x), diff --git a/lib/compiler_rt/fabs.zig b/lib/compiler_rt/fabs.zig index afe231f098..fd3a58a9b7 100644 --- a/lib/compiler_rt/fabs.zig +++ b/lib/compiler_rt/fabs.zig @@ -1,22 +1,18 @@ const std = @import("std"); const builtin = @import("builtin"); const arch = builtin.cpu.arch; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__fabsh, .{ .name = "__fabsh", .linkage = linkage }); - @export(fabsf, .{ .name = "fabsf", .linkage = linkage }); - @export(fabs, .{ .name = "fabs", .linkage = linkage }); - @export(__fabsx, .{ .name = "__fabsx", .linkage = linkage }); - @export(fabsq, .{ .name = "fabsq", .linkage = linkage }); - @export(fabsl, .{ .name = "fabsl", .linkage = linkage }); - - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(fabsf128, .{ .name = "fabsf128", .linkage = linkage }); - } - } + @export(__fabsh, .{ .name = "__fabsh", .linkage = common.linkage }); + @export(fabsf, .{ .name = "fabsf", .linkage = common.linkage }); + @export(fabs, .{ .name = "fabs", .linkage = common.linkage }); + @export(__fabsx, .{ .name = "__fabsx", .linkage = common.linkage }); + const fabsq_sym_name = if (common.want_ppc_abi) "fabsf128" else "fabsq"; + @export(fabsq, .{ .name = fabsq_sym_name, .linkage = common.linkage }); + @export(fabsl, .{ .name = "fabsl", .linkage = common.linkage }); } pub fn __fabsh(a: f16) callconv(.C) f16 { @@ -39,10 +35,6 @@ pub fn fabsq(a: f128) callconv(.C) f128 { return generic_fabs(a); } -pub fn fabsf128(a: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, fabsq, .{a}); -} - pub fn fabsl(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __fabsh(x), diff --git a/lib/compiler_rt/floor.zig b/lib/compiler_rt/floor.zig index f4b1c2fea4..ef02786eb4 100644 --- a/lib/compiler_rt/floor.zig +++ b/lib/compiler_rt/floor.zig @@ -1,30 +1,26 @@ -// Ported from musl, which is licensed under the MIT license: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/floorf.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/floor.c +//! Ported from musl, which is licensed under the MIT license: +//! https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +//! +//! https://git.musl-libc.org/cgit/musl/tree/src/math/floorf.c +//! https://git.musl-libc.org/cgit/musl/tree/src/math/floor.c const std = @import("std"); const builtin = @import("builtin"); const math = std.math; const expect = std.testing.expect; const arch = builtin.cpu.arch; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__floorh, .{ .name = "__floorh", .linkage = linkage }); - @export(floorf, .{ .name = "floorf", .linkage = linkage }); - @export(floor, .{ .name = "floor", .linkage = linkage }); - @export(__floorx, .{ .name = "__floorx", .linkage = linkage }); - @export(floorq, .{ .name = "floorq", .linkage = linkage }); - @export(floorl, .{ .name = "floorl", .linkage = linkage }); - - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(floorf128, .{ .name = "floorf128", .linkage = linkage }); - } - } + @export(__floorh, .{ .name = "__floorh", .linkage = common.linkage }); + @export(floorf, .{ .name = "floorf", .linkage = common.linkage }); + @export(floor, .{ .name = "floor", .linkage = common.linkage }); + @export(__floorx, .{ .name = "__floorx", .linkage = common.linkage }); + const floorq_sym_name = if (common.want_ppc_abi) "floorf128" else "floorq"; + @export(floorq, .{ .name = floorq_sym_name, .linkage = common.linkage }); + @export(floorl, .{ .name = "floorl", .linkage = common.linkage }); } pub fn __floorh(x: f16) callconv(.C) f16 { @@ -160,10 +156,6 @@ pub fn floorq(x: f128) callconv(.C) f128 { } } -pub fn floorf128(x: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, floorq, .{x}); -} - pub fn floorl(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __floorh(x), diff --git a/lib/compiler_rt/fma.zig b/lib/compiler_rt/fma.zig index 98e77e536c..aa37276ac3 100644 --- a/lib/compiler_rt/fma.zig +++ b/lib/compiler_rt/fma.zig @@ -1,31 +1,27 @@ -// Ported from musl, which is MIT licensed: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/fmal.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/fmaf.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/fma.c +//! Ported from musl, which is MIT licensed: +//! https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +//! +//! https://git.musl-libc.org/cgit/musl/tree/src/math/fmal.c +//! https://git.musl-libc.org/cgit/musl/tree/src/math/fmaf.c +//! https://git.musl-libc.org/cgit/musl/tree/src/math/fma.c const std = @import("std"); const builtin = @import("builtin"); const math = std.math; const expect = std.testing.expect; const arch = builtin.cpu.arch; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__fmah, .{ .name = "__fmah", .linkage = linkage }); - @export(fmaf, .{ .name = "fmaf", .linkage = linkage }); - @export(fma, .{ .name = "fma", .linkage = linkage }); - @export(__fmax, .{ .name = "__fmax", .linkage = linkage }); - @export(fmaq, .{ .name = "fmaq", .linkage = linkage }); - @export(fmal, .{ .name = "fmal", .linkage = linkage }); - - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(fmaf128, .{ .name = "fmaf128", .linkage = linkage }); - } - } + @export(__fmah, .{ .name = "__fmah", .linkage = common.linkage }); + @export(fmaf, .{ .name = "fmaf", .linkage = common.linkage }); + @export(fma, .{ .name = "fma", .linkage = common.linkage }); + @export(__fmax, .{ .name = "__fmax", .linkage = common.linkage }); + const fmaq_sym_name = if (common.want_ppc_abi) "fmaf128" else "fmaq"; + @export(fmaq, .{ .name = fmaq_sym_name, .linkage = common.linkage }); + @export(fmal, .{ .name = "fmal", .linkage = common.linkage }); } pub fn __fmah(x: f16, y: f16, z: f16) callconv(.C) f16 { @@ -154,10 +150,6 @@ pub fn fmaq(x: f128, y: f128, z: f128) callconv(.C) f128 { } } -pub fn fmaf128(x: f128, y: f128, z: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, fmaq, .{ x, y, z }); -} - pub fn fmal(x: c_longdouble, y: c_longdouble, z: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __fmah(x, y, z), diff --git a/lib/compiler_rt/fmax.zig b/lib/compiler_rt/fmax.zig index 50c2ab5269..5fb87e0183 100644 --- a/lib/compiler_rt/fmax.zig +++ b/lib/compiler_rt/fmax.zig @@ -2,22 +2,18 @@ const std = @import("std"); const builtin = @import("builtin"); const math = std.math; const arch = builtin.cpu.arch; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__fmaxh, .{ .name = "__fmaxh", .linkage = linkage }); - @export(fmaxf, .{ .name = "fmaxf", .linkage = linkage }); - @export(fmax, .{ .name = "fmax", .linkage = linkage }); - @export(__fmaxx, .{ .name = "__fmaxx", .linkage = linkage }); - @export(fmaxq, .{ .name = "fmaxq", .linkage = linkage }); - @export(fmaxl, .{ .name = "fmaxl", .linkage = linkage }); - - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(fmaxf128, .{ .name = "fmaxf128", .linkage = linkage }); - } - } + @export(__fmaxh, .{ .name = "__fmaxh", .linkage = common.linkage }); + @export(fmaxf, .{ .name = "fmaxf", .linkage = common.linkage }); + @export(fmax, .{ .name = "fmax", .linkage = common.linkage }); + @export(__fmaxx, .{ .name = "__fmaxx", .linkage = common.linkage }); + const fmaxq_sym_name = if (common.want_ppc_abi) "fmaxf128" else "fmaxq"; + @export(fmaxq, .{ .name = fmaxq_sym_name, .linkage = common.linkage }); + @export(fmaxl, .{ .name = "fmaxl", .linkage = common.linkage }); } pub fn __fmaxh(x: f16, y: f16) callconv(.C) f16 { @@ -40,10 +36,6 @@ pub fn fmaxq(x: f128, y: f128) callconv(.C) f128 { return generic_fmax(f128, x, y); } -pub fn fmaxf128(x: f128, y: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, fmaxq, .{ x, y }); -} - pub fn fmaxl(x: c_longdouble, y: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __fmaxh(x, y), diff --git a/lib/compiler_rt/fmin.zig b/lib/compiler_rt/fmin.zig index b4960d0544..cc2fd7b3ac 100644 --- a/lib/compiler_rt/fmin.zig +++ b/lib/compiler_rt/fmin.zig @@ -2,22 +2,18 @@ const std = @import("std"); const builtin = @import("builtin"); const math = std.math; const arch = builtin.cpu.arch; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__fminh, .{ .name = "__fminh", .linkage = linkage }); - @export(fminf, .{ .name = "fminf", .linkage = linkage }); - @export(fmin, .{ .name = "fmin", .linkage = linkage }); - @export(__fminx, .{ .name = "__fminx", .linkage = linkage }); - @export(fminq, .{ .name = "fminq", .linkage = linkage }); - @export(fminl, .{ .name = "fminl", .linkage = linkage }); - - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(fminf128, .{ .name = "fminf128", .linkage = linkage }); - } - } + @export(__fminh, .{ .name = "__fminh", .linkage = common.linkage }); + @export(fminf, .{ .name = "fminf", .linkage = common.linkage }); + @export(fmin, .{ .name = "fmin", .linkage = common.linkage }); + @export(__fminx, .{ .name = "__fminx", .linkage = common.linkage }); + const fminq_sym_name = if (common.want_ppc_abi) "fminf128" else "fminq"; + @export(fminq, .{ .name = fminq_sym_name, .linkage = common.linkage }); + @export(fminl, .{ .name = "fminl", .linkage = common.linkage }); } pub fn __fminh(x: f16, y: f16) callconv(.C) f16 { @@ -40,10 +36,6 @@ pub fn fminq(x: f128, y: f128) callconv(.C) f128 { return generic_fmin(f128, x, y); } -pub fn fminf128(x: f128, y: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, fminq, .{ x, y }); -} - pub fn fminl(x: c_longdouble, y: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __fminh(x, y), diff --git a/lib/compiler_rt/fmod.zig b/lib/compiler_rt/fmod.zig index 0f4c088777..22b20438cc 100644 --- a/lib/compiler_rt/fmod.zig +++ b/lib/compiler_rt/fmod.zig @@ -3,25 +3,19 @@ const std = @import("std"); const math = std.math; const assert = std.debug.assert; const arch = builtin.cpu.arch; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; - const common = @import("common.zig"); const normalize = common.normalize; + pub const panic = common.panic; comptime { - @export(__fmodh, .{ .name = "__fmodh", .linkage = linkage }); - @export(fmodf, .{ .name = "fmodf", .linkage = linkage }); - @export(fmod, .{ .name = "fmod", .linkage = linkage }); - @export(__fmodx, .{ .name = "__fmodx", .linkage = linkage }); - @export(fmodq, .{ .name = "fmodq", .linkage = linkage }); - @export(fmodl, .{ .name = "fmodl", .linkage = linkage }); - - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(fmodf128, .{ .name = "fmodf128", .linkage = linkage }); - } - } + @export(__fmodh, .{ .name = "__fmodh", .linkage = common.linkage }); + @export(fmodf, .{ .name = "fmodf", .linkage = common.linkage }); + @export(fmod, .{ .name = "fmod", .linkage = common.linkage }); + @export(__fmodx, .{ .name = "__fmodx", .linkage = common.linkage }); + const fmodq_sym_name = if (common.want_ppc_abi) "fmodf128" else "fmodq"; + @export(fmodq, .{ .name = fmodq_sym_name, .linkage = common.linkage }); + @export(fmodl, .{ .name = "fmodl", .linkage = common.linkage }); } pub fn __fmodh(x: f16, y: f16) callconv(.C) f16 { @@ -40,8 +34,6 @@ pub fn fmod(x: f64, y: f64) callconv(.C) f64 { /// fmodx - floating modulo large, returns the remainder of division for f80 types /// Logic and flow heavily inspired by MUSL fmodl for 113 mantissa digits pub fn __fmodx(a: f80, b: f80) callconv(.C) f80 { - @setRuntimeSafety(builtin.is_test); - const T = f80; const Z = std.meta.Int(.unsigned, @bitSizeOf(T)); @@ -140,7 +132,6 @@ pub fn __fmodx(a: f80, b: f80) callconv(.C) f80 { /// fmodq - floating modulo large, returns the remainder of division for f128 types /// Logic and flow heavily inspired by MUSL fmodl for 113 mantissa digits pub fn fmodq(a: f128, b: f128) callconv(.C) f128 { - @setRuntimeSafety(builtin.is_test); var amod = a; var bmod = b; const aPtr_u64 = @ptrCast([*]u64, &amod); @@ -257,10 +248,6 @@ pub fn fmodq(a: f128, b: f128) callconv(.C) f128 { return amod; } -pub fn fmodf128(a: f128, b: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, fmodq, .{ a, b }); -} - pub fn fmodl(a: c_longdouble, b: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __fmodh(a, b), @@ -273,8 +260,6 @@ pub fn fmodl(a: c_longdouble, b: c_longdouble) callconv(.C) c_longdouble { } inline fn generic_fmod(comptime T: type, x: T, y: T) T { - @setRuntimeSafety(false); - const bits = @typeInfo(T).Float.bits; const uint = std.meta.Int(.unsigned, bits); const log2uint = math.Log2Int(uint); diff --git a/lib/compiler_rt/int.zig b/lib/compiler_rt/int.zig index a9795632f0..53205e2ed9 100644 --- a/lib/compiler_rt/int.zig +++ b/lib/compiler_rt/int.zig @@ -1,4 +1,5 @@ -// Builtin functions that operate on integer types +//! Builtin functions that operate on integer types + const builtin = @import("builtin"); const std = @import("std"); const testing = std.testing; @@ -6,44 +7,39 @@ const maxInt = std.math.maxInt; const minInt = std.math.minInt; const arch = builtin.cpu.arch; const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; - +const common = @import("common.zig"); const udivmod = @import("udivmod.zig").udivmod; -comptime { - @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = linkage }); - @export(__mulsi3, .{ .name = "__mulsi3", .linkage = linkage }); - @export(__divmoddi4, .{ .name = "__divmoddi4", .linkage = linkage }); - @export(__divsi3, .{ .name = "__divsi3", .linkage = linkage }); - @export(__divdi3, .{ .name = "__divdi3", .linkage = linkage }); - @export(__udivsi3, .{ .name = "__udivsi3", .linkage = linkage }); - @export(__udivdi3, .{ .name = "__udivdi3", .linkage = linkage }); - @export(__modsi3, .{ .name = "__modsi3", .linkage = linkage }); - @export(__moddi3, .{ .name = "__moddi3", .linkage = linkage }); - @export(__umodsi3, .{ .name = "__umodsi3", .linkage = linkage }); - @export(__umoddi3, .{ .name = "__umoddi3", .linkage = linkage }); - @export(__divmodsi4, .{ .name = "__divmodsi4", .linkage = linkage }); - @export(__udivmodsi4, .{ .name = "__udivmodsi4", .linkage = linkage }); +pub const panic = common.panic; - if (!is_test) { - if (arch.isARM() or arch.isThumb()) { - @export(__aeabi_idiv, .{ .name = "__aeabi_idiv", .linkage = linkage }); - @export(__aeabi_uidiv, .{ .name = "__aeabi_uidiv", .linkage = linkage }); - } +comptime { + @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = common.linkage }); + @export(__mulsi3, .{ .name = "__mulsi3", .linkage = common.linkage }); + @export(__divmoddi4, .{ .name = "__divmoddi4", .linkage = common.linkage }); + if (common.want_aeabi) { + @export(__aeabi_idiv, .{ .name = "__aeabi_idiv", .linkage = common.linkage }); + @export(__aeabi_uidiv, .{ .name = "__aeabi_uidiv", .linkage = common.linkage }); + } else { + @export(__divsi3, .{ .name = "__divsi3", .linkage = common.linkage }); + @export(__udivsi3, .{ .name = "__udivsi3", .linkage = common.linkage }); } + @export(__divdi3, .{ .name = "__divdi3", .linkage = common.linkage }); + @export(__udivdi3, .{ .name = "__udivdi3", .linkage = common.linkage }); + @export(__modsi3, .{ .name = "__modsi3", .linkage = common.linkage }); + @export(__moddi3, .{ .name = "__moddi3", .linkage = common.linkage }); + @export(__umodsi3, .{ .name = "__umodsi3", .linkage = common.linkage }); + @export(__umoddi3, .{ .name = "__umoddi3", .linkage = common.linkage }); + @export(__divmodsi4, .{ .name = "__divmodsi4", .linkage = common.linkage }); + @export(__udivmodsi4, .{ .name = "__udivmodsi4", .linkage = common.linkage }); } pub fn __divmoddi4(a: i64, b: i64, rem: *i64) callconv(.C) i64 { - @setRuntimeSafety(builtin.is_test); - const d = __divdi3(a, b); rem.* = a -% (d *% b); return d; } pub fn __udivmoddi4(a: u64, b: u64, maybe_rem: ?*u64) callconv(.C) u64 { - @setRuntimeSafety(builtin.is_test); return udivmod(u64, a, b, maybe_rem); } @@ -52,8 +48,6 @@ test "test_udivmoddi4" { } pub fn __divdi3(a: i64, b: i64) callconv(.C) i64 { - @setRuntimeSafety(builtin.is_test); - // Set aside the sign of the quotient. const sign = @bitCast(u64, (a ^ b) >> 63); // Take absolute value of a and b via abs(x) = (x^(x >> 63)) - (x >> 63). @@ -91,8 +85,6 @@ fn test_one_divdi3(a: i64, b: i64, expected_q: i64) !void { } pub fn __moddi3(a: i64, b: i64) callconv(.C) i64 { - @setRuntimeSafety(builtin.is_test); - // Take absolute value of a and b via abs(x) = (x^(x >> 63)) - (x >> 63). const abs_a = (a ^ (a >> 63)) -% (a >> 63); const abs_b = (b ^ (b >> 63)) -% (b >> 63); @@ -131,13 +123,10 @@ fn test_one_moddi3(a: i64, b: i64, expected_r: i64) !void { } pub fn __udivdi3(a: u64, b: u64) callconv(.C) u64 { - @setRuntimeSafety(builtin.is_test); return __udivmoddi4(a, b, null); } pub fn __umoddi3(a: u64, b: u64) callconv(.C) u64 { - @setRuntimeSafety(builtin.is_test); - var r: u64 = undefined; _ = __udivmoddi4(a, b, &r); return r; @@ -157,8 +146,6 @@ fn test_one_umoddi3(a: u64, b: u64, expected_r: u64) !void { } pub fn __divmodsi4(a: i32, b: i32, rem: *i32) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - const d = __divsi3(a, b); rem.* = a -% (d * b); return d; @@ -193,16 +180,20 @@ fn test_one_divmodsi4(a: i32, b: i32, expected_q: i32, expected_r: i32) !void { } pub fn __udivmodsi4(a: u32, b: u32, rem: *u32) callconv(.C) u32 { - @setRuntimeSafety(builtin.is_test); - const d = __udivsi3(a, b); rem.* = @bitCast(u32, @bitCast(i32, a) -% (@bitCast(i32, d) * @bitCast(i32, b))); return d; } pub fn __divsi3(n: i32, d: i32) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); + return div_i32(n, d); +} +fn __aeabi_idiv(n: i32, d: i32) callconv(.AAPCS) i32 { + return div_i32(n, d); +} + +inline fn div_i32(n: i32, d: i32) i32 { // Set aside the sign of the quotient. const sign = @bitCast(u32, (n ^ d) >> 31); // Take absolute value of a and b via abs(x) = (x^(x >> 31)) - (x >> 31). @@ -214,10 +205,6 @@ pub fn __divsi3(n: i32, d: i32) callconv(.C) i32 { return @bitCast(i32, (res ^ sign) -% sign); } -pub fn __aeabi_idiv(n: i32, d: i32) callconv(.AAPCS) i32 { - return @call(.{ .modifier = .always_inline }, __divsi3, .{ n, d }); -} - test "test_divsi3" { const cases = [_][3]i32{ [_]i32{ 0, 1, 0 }, @@ -244,8 +231,14 @@ fn test_one_divsi3(a: i32, b: i32, expected_q: i32) !void { } pub fn __udivsi3(n: u32, d: u32) callconv(.C) u32 { - @setRuntimeSafety(builtin.is_test); + return div_u32(n, d); +} +fn __aeabi_uidiv(n: u32, d: u32) callconv(.AAPCS) u32 { + return div_u32(n, d); +} + +inline fn div_u32(n: u32, d: u32) u32 { const n_uword_bits: c_uint = 32; // special cases if (d == 0) return 0; // ?! @@ -284,10 +277,6 @@ pub fn __udivsi3(n: u32, d: u32) callconv(.C) u32 { return q; } -pub fn __aeabi_uidiv(n: u32, d: u32) callconv(.AAPCS) u32 { - return @call(.{ .modifier = .always_inline }, __udivsi3, .{ n, d }); -} - test "test_udivsi3" { const cases = [_][3]u32{ [_]u32{ 0x00000000, 0x00000001, 0x00000000 }, @@ -435,8 +424,6 @@ fn test_one_udivsi3(a: u32, b: u32, expected_q: u32) !void { } pub fn __modsi3(n: i32, d: i32) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - return n -% __divsi3(n, d) *% d; } @@ -466,8 +453,6 @@ fn test_one_modsi3(a: i32, b: i32, expected_r: i32) !void { } pub fn __umodsi3(n: u32, d: u32) callconv(.C) u32 { - @setRuntimeSafety(builtin.is_test); - return n -% __udivsi3(n, d) *% d; } @@ -618,8 +603,6 @@ fn test_one_umodsi3(a: u32, b: u32, expected_r: u32) !void { } pub fn __mulsi3(a: i32, b: i32) callconv(.C) i32 { - @setRuntimeSafety(builtin.is_test); - var ua = @bitCast(u32, a); var ub = @bitCast(u32, b); var r: u32 = 0; diff --git a/lib/compiler_rt/log.zig b/lib/compiler_rt/log.zig index 991c94ef27..90a38ba381 100644 --- a/lib/compiler_rt/log.zig +++ b/lib/compiler_rt/log.zig @@ -1,30 +1,26 @@ -// Ported from musl, which is licensed under the MIT license: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/lnf.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/ln.c +//! Ported from musl, which is licensed under the MIT license: +//! https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +//! +//! https://git.musl-libc.org/cgit/musl/tree/src/math/lnf.c +//! https://git.musl-libc.org/cgit/musl/tree/src/math/ln.c const std = @import("std"); const builtin = @import("builtin"); const math = std.math; const testing = std.testing; const arch = builtin.cpu.arch; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__logh, .{ .name = "__logh", .linkage = linkage }); - @export(logf, .{ .name = "logf", .linkage = linkage }); - @export(log, .{ .name = "log", .linkage = linkage }); - @export(__logx, .{ .name = "__logx", .linkage = linkage }); - @export(logq, .{ .name = "logq", .linkage = linkage }); - @export(logl, .{ .name = "logl", .linkage = linkage }); - - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(logf128, .{ .name = "logf128", .linkage = linkage }); - } - } + @export(__logh, .{ .name = "__logh", .linkage = common.linkage }); + @export(logf, .{ .name = "logf", .linkage = common.linkage }); + @export(log, .{ .name = "log", .linkage = common.linkage }); + @export(__logx, .{ .name = "__logx", .linkage = common.linkage }); + const logq_sym_name = if (common.want_ppc_abi) "logf128" else "logq"; + @export(logq, .{ .name = logq_sym_name, .linkage = common.linkage }); + @export(logl, .{ .name = "logl", .linkage = common.linkage }); } pub fn __logh(a: f16) callconv(.C) f16 { @@ -150,10 +146,6 @@ pub fn logq(a: f128) callconv(.C) f128 { return log(@floatCast(f64, a)); } -pub fn logf128(a: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, logq, .{a}); -} - pub fn logl(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __logh(x), diff --git a/lib/compiler_rt/log10.zig b/lib/compiler_rt/log10.zig index 8031879761..406eb8d0c1 100644 --- a/lib/compiler_rt/log10.zig +++ b/lib/compiler_rt/log10.zig @@ -1,8 +1,8 @@ -// Ported from musl, which is licensed under the MIT license: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/log10f.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/log10.c +//! Ported from musl, which is licensed under the MIT license: +//! https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +//! +//! https://git.musl-libc.org/cgit/musl/tree/src/math/log10f.c +//! https://git.musl-libc.org/cgit/musl/tree/src/math/log10.c const std = @import("std"); const builtin = @import("builtin"); @@ -10,22 +10,18 @@ const math = std.math; const testing = std.testing; const maxInt = std.math.maxInt; const arch = builtin.cpu.arch; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__log10h, .{ .name = "__log10h", .linkage = linkage }); - @export(log10f, .{ .name = "log10f", .linkage = linkage }); - @export(log10, .{ .name = "log10", .linkage = linkage }); - @export(__log10x, .{ .name = "__log10x", .linkage = linkage }); - @export(log10q, .{ .name = "log10q", .linkage = linkage }); - @export(log10l, .{ .name = "log10l", .linkage = linkage }); - - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(log10f128, .{ .name = "log10f128", .linkage = linkage }); - } - } + @export(__log10h, .{ .name = "__log10h", .linkage = common.linkage }); + @export(log10f, .{ .name = "log10f", .linkage = common.linkage }); + @export(log10, .{ .name = "log10", .linkage = common.linkage }); + @export(__log10x, .{ .name = "__log10x", .linkage = common.linkage }); + const log10q_sym_name = if (common.want_ppc_abi) "log10f128" else "log10q"; + @export(log10q, .{ .name = log10q_sym_name, .linkage = common.linkage }); + @export(log10l, .{ .name = "log10l", .linkage = common.linkage }); } pub fn __log10h(a: f16) callconv(.C) f16 { @@ -178,10 +174,6 @@ pub fn log10q(a: f128) callconv(.C) f128 { return log10(@floatCast(f64, a)); } -pub fn log10f128(a: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, log10q, .{a}); -} - pub fn log10l(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __log10h(x), diff --git a/lib/compiler_rt/log2.zig b/lib/compiler_rt/log2.zig index d2a1dd1189..6f6c07212a 100644 --- a/lib/compiler_rt/log2.zig +++ b/lib/compiler_rt/log2.zig @@ -1,8 +1,8 @@ -// Ported from musl, which is licensed under the MIT license: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/log2f.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/log2.c +//! Ported from musl, which is licensed under the MIT license: +//! https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +//! +//! https://git.musl-libc.org/cgit/musl/tree/src/math/log2f.c +//! https://git.musl-libc.org/cgit/musl/tree/src/math/log2.c const std = @import("std"); const builtin = @import("builtin"); @@ -10,22 +10,18 @@ const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; const arch = builtin.cpu.arch; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__log2h, .{ .name = "__log2h", .linkage = linkage }); - @export(log2f, .{ .name = "log2f", .linkage = linkage }); - @export(log2, .{ .name = "log2", .linkage = linkage }); - @export(__log2x, .{ .name = "__log2x", .linkage = linkage }); - @export(log2q, .{ .name = "log2q", .linkage = linkage }); - @export(log2l, .{ .name = "log2l", .linkage = linkage }); - - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(log2f128, .{ .name = "log2f128", .linkage = linkage }); - } - } + @export(__log2h, .{ .name = "__log2h", .linkage = common.linkage }); + @export(log2f, .{ .name = "log2f", .linkage = common.linkage }); + @export(log2, .{ .name = "log2", .linkage = common.linkage }); + @export(__log2x, .{ .name = "__log2x", .linkage = common.linkage }); + const log2q_sym_name = if (common.want_ppc_abi) "log2f128" else "log2q"; + @export(log2q, .{ .name = log2q_sym_name, .linkage = common.linkage }); + @export(log2l, .{ .name = "log2l", .linkage = common.linkage }); } pub fn __log2h(a: f16) callconv(.C) f16 { @@ -170,10 +166,6 @@ pub fn log2q(a: f128) callconv(.C) f128 { return log2(@floatCast(f64, a)); } -pub fn log2f128(a: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, log2q, .{a}); -} - pub fn log2l(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __log2h(x), diff --git a/lib/compiler_rt/modti3.zig b/lib/compiler_rt/modti3.zig index a0bd2615d4..5fa34938ff 100644 --- a/lib/compiler_rt/modti3.zig +++ b/lib/compiler_rt/modti3.zig @@ -1,39 +1,47 @@ -// Ported from: -// -// https://github.com/llvm/llvm-project/blob/2ffb1b0413efa9a24eb3c49e710e36f92e2cb50b/compiler-rt/lib/builtins/modti3.c +//! Ported from: +//! +//! https://github.com/llvm/llvm-project/blob/2ffb1b0413efa9a24eb3c49e710e36f92e2cb50b/compiler-rt/lib/builtins/modti3.c const std = @import("std"); const builtin = @import("builtin"); const udivmod = @import("udivmod.zig").udivmod; const arch = builtin.cpu.arch; -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { if (builtin.os.tag == .windows) { switch (arch) { .i386 => { - @export(__modti3, .{ .name = "__modti3", .linkage = linkage }); + @export(__modti3, .{ .name = "__modti3", .linkage = common.linkage }); }, .x86_64 => { // The "ti" functions must use Vector(2, u64) parameter types to adhere to the ABI // that LLVM expects compiler-rt to have. - @export(__modti3_windows_x86_64, .{ .name = "__modti3", .linkage = linkage }); + @export(__modti3_windows_x86_64, .{ .name = "__modti3", .linkage = common.linkage }); }, else => {}, } if (arch.isAARCH64()) { - @export(__modti3, .{ .name = "__modti3", .linkage = linkage }); + @export(__modti3, .{ .name = "__modti3", .linkage = common.linkage }); } } else { - @export(__modti3, .{ .name = "__modti3", .linkage = linkage }); + @export(__modti3, .{ .name = "__modti3", .linkage = common.linkage }); } } pub fn __modti3(a: i128, b: i128) callconv(.C) i128 { - @setRuntimeSafety(builtin.is_test); + return mod(a, b); +} +const v128 = @import("std").meta.Vector(2, u64); + +fn __modti3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { + return @bitCast(v128, mod(@bitCast(i128, a), @bitCast(i128, b))); +} + +inline fn mod(a: i128, b: i128) i128 { const s_a = a >> (128 - 1); // s = a < 0 ? -1 : 0 const s_b = b >> (128 - 1); // s = b < 0 ? -1 : 0 @@ -45,14 +53,6 @@ pub fn __modti3(a: i128, b: i128) callconv(.C) i128 { return (@bitCast(i128, r) ^ s_a) -% s_a; // negate if s == -1 } -const v128 = @import("std").meta.Vector(2, u64); -pub fn __modti3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { - return @bitCast(v128, @call(.{ .modifier = .always_inline }, __modti3, .{ - @bitCast(i128, a), - @bitCast(i128, b), - })); -} - test { _ = @import("modti3_test.zig"); } diff --git a/lib/compiler_rt/muldi3.zig b/lib/compiler_rt/muldi3.zig index 41e7827dfc..a51c6c7b76 100644 --- a/lib/compiler_rt/muldi3.zig +++ b/lib/compiler_rt/muldi3.zig @@ -1,23 +1,36 @@ +//! Ported from +//! https://github.com/llvm/llvm-project/blob/llvmorg-9.0.0/compiler-rt/lib/builtins/muldi3.c + const std = @import("std"); const builtin = @import("builtin"); const native_endian = builtin.cpu.arch.endian(); -const arch = builtin.cpu.arch; -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__muldi3, .{ .name = "__muldi3", .linkage = linkage }); - - if (!is_test) { - if (arch.isARM() or arch.isThumb()) { - @export(__aeabi_lmul, .{ .name = "__aeabi_lmul", .linkage = linkage }); - } + if (common.want_aeabi) { + @export(__aeabi_lmul, .{ .name = "__aeabi_lmul", .linkage = common.linkage }); + } else { + @export(__muldi3, .{ .name = "__muldi3", .linkage = common.linkage }); } } -// Ported from -// https://github.com/llvm/llvm-project/blob/llvmorg-9.0.0/compiler-rt/lib/builtins/muldi3.c +pub fn __muldi3(a: i64, b: i64) callconv(.C) i64 { + return mul(a, b); +} + +fn __aeabi_lmul(a: i64, b: i64) callconv(.AAPCS) i64 { + return mul(a, b); +} + +inline fn mul(a: i64, b: i64) i64 { + const x = dwords{ .all = a }; + const y = dwords{ .all = b }; + var r = dwords{ .all = muldsi3(x.s.low, y.s.low) }; + r.s.high +%= x.s.high *% y.s.low +% x.s.low *% y.s.high; + return r.all; +} const dwords = extern union { all: i64, @@ -33,13 +46,7 @@ const dwords = extern union { }, }; -pub fn __aeabi_lmul(a: i64, b: i64) callconv(.AAPCS) i64 { - return @call(.{ .modifier = .always_inline }, __muldi3, .{ a, b }); -} - -pub fn __muldsi3(a: u32, b: u32) i64 { - @setRuntimeSafety(is_test); - +fn muldsi3(a: u32, b: u32) i64 { const bits_in_word_2 = @sizeOf(i32) * 8 / 2; const lower_mask = (~@as(u32, 0)) >> bits_in_word_2; @@ -59,16 +66,6 @@ pub fn __muldsi3(a: u32, b: u32) i64 { return r.all; } -pub fn __muldi3(a: i64, b: i64) callconv(.C) i64 { - @setRuntimeSafety(is_test); - - const x = dwords{ .all = a }; - const y = dwords{ .all = b }; - var r = dwords{ .all = __muldsi3(x.s.low, y.s.low) }; - r.s.high +%= x.s.high *% y.s.low +% x.s.low *% y.s.high; - return r.all; -} - test { _ = @import("muldi3_test.zig"); } diff --git a/lib/compiler_rt/mulo.zig b/lib/compiler_rt/mulo.zig index 4fd99351bc..cd2d127c34 100644 --- a/lib/compiler_rt/mulo.zig +++ b/lib/compiler_rt/mulo.zig @@ -1,14 +1,14 @@ const std = @import("std"); const builtin = @import("builtin"); const math = std.math; -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__mulosi4, .{ .name = "__mulosi4", .linkage = linkage }); - @export(__mulodi4, .{ .name = "__mulodi4", .linkage = linkage }); - @export(__muloti4, .{ .name = "__muloti4", .linkage = linkage }); + @export(__mulosi4, .{ .name = "__mulosi4", .linkage = common.linkage }); + @export(__mulodi4, .{ .name = "__mulodi4", .linkage = common.linkage }); + @export(__muloti4, .{ .name = "__muloti4", .linkage = common.linkage }); } // mulo - multiplication overflow @@ -18,7 +18,6 @@ comptime { // - muloXi4_genericFast for 2*bitsize <= usize inline fn muloXi4_genericSmall(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST { - @setRuntimeSafety(builtin.is_test); overflow.* = 0; const min = math.minInt(ST); var res: ST = a *% b; @@ -33,7 +32,6 @@ inline fn muloXi4_genericSmall(comptime ST: type, a: ST, b: ST, overflow: *c_int } inline fn muloXi4_genericFast(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST { - @setRuntimeSafety(builtin.is_test); overflow.* = 0; const EST = switch (ST) { i32 => i64, diff --git a/lib/compiler_rt/multi3.zig b/lib/compiler_rt/multi3.zig index 838cc3f1df..ba41cb7917 100644 --- a/lib/compiler_rt/multi3.zig +++ b/lib/compiler_rt/multi3.zig @@ -1,51 +1,52 @@ +//! Ported from git@github.com:llvm-project/llvm-project-20170507.git +//! ae684fad6d34858c014c94da69c15e7774a633c3 +//! 2018-08-13 + const std = @import("std"); const builtin = @import("builtin"); const arch = builtin.cpu.arch; -const is_test = builtin.is_test; const native_endian = builtin.cpu.arch.endian(); -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); -// Ported from git@github.com:llvm-project/llvm-project-20170507.git -// ae684fad6d34858c014c94da69c15e7774a633c3 -// 2018-08-13 +pub const panic = common.panic; comptime { if (builtin.os.tag == .windows) { switch (arch) { .i386 => { - @export(__multi3, .{ .name = "__multi3", .linkage = linkage }); + @export(__multi3, .{ .name = "__multi3", .linkage = common.linkage }); }, .x86_64 => { // The "ti" functions must use Vector(2, u64) parameter types to adhere to the ABI // that LLVM expects compiler-rt to have. - @export(__multi3_windows_x86_64, .{ .name = "__multi3", .linkage = linkage }); + @export(__multi3_windows_x86_64, .{ .name = "__multi3", .linkage = common.linkage }); }, else => {}, } } else { - @export(__multi3, .{ .name = "__multi3", .linkage = linkage }); + @export(__multi3, .{ .name = "__multi3", .linkage = common.linkage }); } } pub fn __multi3(a: i128, b: i128) callconv(.C) i128 { - @setRuntimeSafety(is_test); + return mul(a, b); +} + +const v128 = @Vector(2, u64); + +fn __multi3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { + return @bitCast(v128, mul(@bitCast(i128, a), @bitCast(i128, b))); +} + +inline fn mul(a: i128, b: i128) i128 { const x = twords{ .all = a }; const y = twords{ .all = b }; - var r = twords{ .all = __mulddi3(x.s.low, y.s.low) }; + var r = twords{ .all = mulddi3(x.s.low, y.s.low) }; r.s.high +%= x.s.high *% y.s.low +% x.s.low *% y.s.high; return r.all; } -const v128 = @Vector(2, u64); -pub fn __multi3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { - return @bitCast(v128, @call(.{ .modifier = .always_inline }, __multi3, .{ - @bitCast(i128, a), - @bitCast(i128, b), - })); -} - -pub fn __mulddi3(a: u64, b: u64) i128 { +fn mulddi3(a: u64, b: u64) i128 { const bits_in_dword_2 = (@sizeOf(i64) * 8) / 2; const lower_mask = ~@as(u64, 0) >> bits_in_dword_2; var r: twords = undefined; diff --git a/lib/compiler_rt/negXf2.zig b/lib/compiler_rt/negXf2.zig index f33336f965..bcff3660f4 100644 --- a/lib/compiler_rt/negXf2.zig +++ b/lib/compiler_rt/negXf2.zig @@ -1,19 +1,16 @@ const std = @import("std"); const builtin = @import("builtin"); -const arch = builtin.cpu.arch; -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__negsf2, .{ .name = "__negsf2", .linkage = linkage }); - @export(__negdf2, .{ .name = "__negdf2", .linkage = linkage }); - - if (!is_test) { - if (arch.isARM() or arch.isThumb()) { - @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = linkage }); - @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = linkage }); - } + if (common.want_aeabi) { + @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = common.linkage }); + @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = common.linkage }); + } else { + @export(__negsf2, .{ .name = "__negsf2", .linkage = common.linkage }); + @export(__negdf2, .{ .name = "__negdf2", .linkage = common.linkage }); } } @@ -21,21 +18,19 @@ pub fn __negsf2(a: f32) callconv(.C) f32 { return negXf2(f32, a); } +fn __aeabi_fneg(a: f32) callconv(.AAPCS) f32 { + return negXf2(f32, a); +} + pub fn __negdf2(a: f64) callconv(.C) f64 { return negXf2(f64, a); } -pub fn __aeabi_fneg(arg: f32) callconv(.AAPCS) f32 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __negsf2, .{arg}); +fn __aeabi_dneg(a: f64) callconv(.AAPCS) f64 { + return negXf2(f64, a); } -pub fn __aeabi_dneg(arg: f64) callconv(.AAPCS) f64 { - @setRuntimeSafety(false); - return @call(.{ .modifier = .always_inline }, __negdf2, .{arg}); -} - -fn negXf2(comptime T: type, a: T) T { +inline fn negXf2(comptime T: type, a: T) T { const Z = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); const significandBits = std.math.floatMantissaBits(T); diff --git a/lib/compiler_rt/negXi2.zig b/lib/compiler_rt/negXi2.zig index 0ab5a93f62..086f80c6b3 100644 --- a/lib/compiler_rt/negXi2.zig +++ b/lib/compiler_rt/negXi2.zig @@ -1,28 +1,21 @@ +//! neg - negate (the number) +//! - negXi2 for unoptimized little and big endian +//! sfffffff = 2^31-1 +//! two's complement inverting bits and add 1 would result in -INT_MIN == 0 +//! => -INT_MIN = -2^31 forbidden +//! * size optimized builds +//! * machines that dont support carry operations + const std = @import("std"); const builtin = @import("builtin"); -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__negsi2, .{ .name = "__negsi2", .linkage = linkage }); - @export(__negdi2, .{ .name = "__negdi2", .linkage = linkage }); - @export(__negti2, .{ .name = "__negti2", .linkage = linkage }); -} - -// neg - negate (the number) -// - negXi2 for unoptimized little and big endian - -// sfffffff = 2^31-1 -// two's complement inverting bits and add 1 would result in -INT_MIN == 0 -// => -INT_MIN = -2^31 forbidden - -// * size optimized builds -// * machines that dont support carry operations - -inline fn negXi2(comptime T: type, a: T) T { - @setRuntimeSafety(builtin.is_test); - return -a; + @export(__negsi2, .{ .name = "__negsi2", .linkage = common.linkage }); + @export(__negdi2, .{ .name = "__negdi2", .linkage = common.linkage }); + @export(__negti2, .{ .name = "__negti2", .linkage = common.linkage }); } pub fn __negsi2(a: i32) callconv(.C) i32 { @@ -37,6 +30,10 @@ pub fn __negti2(a: i128) callconv(.C) i128 { return negXi2(i128, a); } +inline fn negXi2(comptime T: type, a: T) T { + return -a; +} + test { _ = @import("negsi2_test.zig"); _ = @import("negdi2_test.zig"); diff --git a/lib/compiler_rt/negv.zig b/lib/compiler_rt/negv.zig index b9198dd380..361cd80ee7 100644 --- a/lib/compiler_rt/negv.zig +++ b/lib/compiler_rt/negv.zig @@ -1,31 +1,16 @@ -// negv - negate oVerflow -// * @panic, if result can not be represented -// - negvXi4_generic for unoptimized version +//! negv - negate oVerflow +//! * @panic, if result can not be represented +//! - negvXi4_generic for unoptimized version const std = @import("std"); const builtin = @import("builtin"); -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__negvsi2, .{ .name = "__negvsi2", .linkage = linkage }); - @export(__negvdi2, .{ .name = "__negvdi2", .linkage = linkage }); - @export(__negvti2, .{ .name = "__negvti2", .linkage = linkage }); -} - -// assume -0 == 0 is gracefully handled by the hardware -inline fn negvXi(comptime ST: type, a: ST) ST { - const UT = switch (ST) { - i32 => u32, - i64 => u64, - i128 => u128, - else => unreachable, - }; - const N: UT = @bitSizeOf(ST); - const min: ST = @bitCast(ST, (@as(UT, 1) << (N - 1))); - if (a == min) - @panic("compiler_rt negv: overflow"); - return -a; + @export(__negvsi2, .{ .name = "__negvsi2", .linkage = common.linkage }); + @export(__negvdi2, .{ .name = "__negvdi2", .linkage = common.linkage }); + @export(__negvti2, .{ .name = "__negvti2", .linkage = common.linkage }); } pub fn __negvsi2(a: i32) callconv(.C) i32 { @@ -40,6 +25,20 @@ pub fn __negvti2(a: i128) callconv(.C) i128 { return negvXi(i128, a); } +inline fn negvXi(comptime ST: type, a: ST) ST { + const UT = switch (ST) { + i32 => u32, + i64 => u64, + i128 => u128, + else => unreachable, + }; + const N: UT = @bitSizeOf(ST); + const min: ST = @bitCast(ST, (@as(UT, 1) << (N - 1))); + if (a == min) + @panic("compiler_rt negv: overflow"); + return -a; +} + test { _ = @import("negvsi2_test.zig"); _ = @import("negvdi2_test.zig"); diff --git a/lib/compiler_rt/parity.zig b/lib/compiler_rt/parity.zig index 4f03e32244..2f48a38bff 100644 --- a/lib/compiler_rt/parity.zig +++ b/lib/compiler_rt/parity.zig @@ -1,21 +1,31 @@ +//! parity - if number of bits set is even => 0, else => 1 +//! - pariytXi2_generic for big and little endian + const std = @import("std"); const builtin = @import("builtin"); -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__paritysi2, .{ .name = "__paritysi2", .linkage = linkage }); - @export(__paritydi2, .{ .name = "__paritydi2", .linkage = linkage }); - @export(__parityti2, .{ .name = "__parityti2", .linkage = linkage }); + @export(__paritysi2, .{ .name = "__paritysi2", .linkage = common.linkage }); + @export(__paritydi2, .{ .name = "__paritydi2", .linkage = common.linkage }); + @export(__parityti2, .{ .name = "__parityti2", .linkage = common.linkage }); } -// parity - if number of bits set is even => 0, else => 1 -// - pariytXi2_generic for big and little endian +pub fn __paritysi2(a: i32) callconv(.C) i32 { + return parityXi2(i32, a); +} + +pub fn __paritydi2(a: i64) callconv(.C) i32 { + return parityXi2(i64, a); +} + +pub fn __parityti2(a: i128) callconv(.C) i32 { + return parityXi2(i128, a); +} inline fn parityXi2(comptime T: type, a: T) i32 { - @setRuntimeSafety(builtin.is_test); - var x = switch (@bitSizeOf(T)) { 32 => @bitCast(u32, a), 64 => @bitCast(u64, a), @@ -32,18 +42,6 @@ inline fn parityXi2(comptime T: type, a: T) i32 { return (@intCast(u16, 0x6996) >> @intCast(u4, x)) & 1; // optimization for >>2 and >>1 } -pub fn __paritysi2(a: i32) callconv(.C) i32 { - return parityXi2(i32, a); -} - -pub fn __paritydi2(a: i64) callconv(.C) i32 { - return parityXi2(i64, a); -} - -pub fn __parityti2(a: i128) callconv(.C) i32 { - return parityXi2(i128, a); -} - test { _ = @import("paritysi2_test.zig"); _ = @import("paritydi2_test.zig"); diff --git a/lib/compiler_rt/popcount.zig b/lib/compiler_rt/popcount.zig index 6fa88cbfc6..803e93f35a 100644 --- a/lib/compiler_rt/popcount.zig +++ b/lib/compiler_rt/popcount.zig @@ -1,26 +1,36 @@ +//! popcount - population count +//! counts the number of 1 bits +//! SWAR-Popcount: count bits of duos, aggregate to nibbles, and bytes inside +//! x-bit register in parallel to sum up all bytes +//! SWAR-Masks and factors can be defined as 2-adic fractions +//! TAOCP: Combinational Algorithms, Bitwise Tricks And Techniques, +//! subsubsection "Working with the rightmost bits" and "Sideways addition". + const builtin = @import("builtin"); const std = @import("std"); -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__popcountsi2, .{ .name = "__popcountsi2", .linkage = linkage }); - @export(__popcountdi2, .{ .name = "__popcountdi2", .linkage = linkage }); - @export(__popcountti2, .{ .name = "__popcountti2", .linkage = linkage }); + @export(__popcountsi2, .{ .name = "__popcountsi2", .linkage = common.linkage }); + @export(__popcountdi2, .{ .name = "__popcountdi2", .linkage = common.linkage }); + @export(__popcountti2, .{ .name = "__popcountti2", .linkage = common.linkage }); } -// popcount - population count -// counts the number of 1 bits +pub fn __popcountsi2(a: i32) callconv(.C) i32 { + return popcountXi2(i32, a); +} -// SWAR-Popcount: count bits of duos, aggregate to nibbles, and bytes inside -// x-bit register in parallel to sum up all bytes -// SWAR-Masks and factors can be defined as 2-adic fractions -// TAOCP: Combinational Algorithms, Bitwise Tricks And Techniques, -// subsubsection "Working with the rightmost bits" and "Sideways addition". +pub fn __popcountdi2(a: i64) callconv(.C) i32 { + return popcountXi2(i64, a); +} + +pub fn __popcountti2(a: i128) callconv(.C) i32 { + return popcountXi2(i128, a); +} inline fn popcountXi2(comptime ST: type, a: ST) i32 { - @setRuntimeSafety(builtin.is_test); const UT = switch (ST) { i32 => u32, i64 => u64, @@ -39,18 +49,6 @@ inline fn popcountXi2(comptime ST: type, a: ST) i32 { return @intCast(i32, x); } -pub fn __popcountsi2(a: i32) callconv(.C) i32 { - return popcountXi2(i32, a); -} - -pub fn __popcountdi2(a: i64) callconv(.C) i32 { - return popcountXi2(i64, a); -} - -pub fn __popcountti2(a: i128) callconv(.C) i32 { - return popcountXi2(i128, a); -} - test { _ = @import("popcountsi2_test.zig"); _ = @import("popcountdi2_test.zig"); diff --git a/lib/compiler_rt/round.zig b/lib/compiler_rt/round.zig index ddd651885a..acd26d8823 100644 --- a/lib/compiler_rt/round.zig +++ b/lib/compiler_rt/round.zig @@ -1,30 +1,26 @@ -// Ported from musl, which is licensed under the MIT license: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/roundf.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/round.c +//! Ported from musl, which is licensed under the MIT license: +//! https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +//! +//! https://git.musl-libc.org/cgit/musl/tree/src/math/roundf.c +//! https://git.musl-libc.org/cgit/musl/tree/src/math/round.c const std = @import("std"); const builtin = @import("builtin"); const math = std.math; const expect = std.testing.expect; const arch = builtin.cpu.arch; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__roundh, .{ .name = "__roundh", .linkage = linkage }); - @export(roundf, .{ .name = "roundf", .linkage = linkage }); - @export(round, .{ .name = "round", .linkage = linkage }); - @export(__roundx, .{ .name = "__roundx", .linkage = linkage }); - @export(roundq, .{ .name = "roundq", .linkage = linkage }); - @export(roundl, .{ .name = "roundl", .linkage = linkage }); - - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(roundf128, .{ .name = "roundf128", .linkage = linkage }); - } - } + @export(__roundh, .{ .name = "__roundh", .linkage = common.linkage }); + @export(roundf, .{ .name = "roundf", .linkage = common.linkage }); + @export(round, .{ .name = "round", .linkage = common.linkage }); + @export(__roundx, .{ .name = "__roundx", .linkage = common.linkage }); + const roundq_sym_name = if (common.want_ppc_abi) "roundf128" else "roundq"; + @export(roundq, .{ .name = roundq_sym_name, .linkage = common.linkage }); + @export(roundl, .{ .name = "roundl", .linkage = common.linkage }); } pub fn __roundh(x: f16) callconv(.C) f16 { @@ -142,10 +138,6 @@ pub fn roundq(x_: f128) callconv(.C) f128 { } } -pub fn roundf128(x_: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, roundq, .{x_}); -} - pub fn roundl(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __roundh(x), diff --git a/lib/compiler_rt/shift.zig b/lib/compiler_rt/shift.zig index 031d5368ad..ee8b634fbb 100644 --- a/lib/compiler_rt/shift.zig +++ b/lib/compiler_rt/shift.zig @@ -2,25 +2,23 @@ const std = @import("std"); const builtin = @import("builtin"); const Log2Int = std.math.Log2Int; const native_endian = builtin.cpu.arch.endian(); -const arch = builtin.cpu.arch; -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__ashldi3, .{ .name = "__ashldi3", .linkage = linkage }); - @export(__ashlti3, .{ .name = "__ashlti3", .linkage = linkage }); - @export(__ashrdi3, .{ .name = "__ashrdi3", .linkage = linkage }); - @export(__ashrti3, .{ .name = "__ashrti3", .linkage = linkage }); - @export(__lshrdi3, .{ .name = "__lshrdi3", .linkage = linkage }); - @export(__lshrti3, .{ .name = "__lshrti3", .linkage = linkage }); + @export(__ashlti3, .{ .name = "__ashlti3", .linkage = common.linkage }); + @export(__ashrti3, .{ .name = "__ashrti3", .linkage = common.linkage }); + @export(__lshrti3, .{ .name = "__lshrti3", .linkage = common.linkage }); - if (!is_test) { - if (arch.isARM() or arch.isThumb()) { - @export(__aeabi_llsl, .{ .name = "__aeabi_llsl", .linkage = linkage }); - @export(__aeabi_lasr, .{ .name = "__aeabi_lasr", .linkage = linkage }); - @export(__aeabi_llsr, .{ .name = "__aeabi_llsr", .linkage = linkage }); - } + if (common.want_aeabi) { + @export(__aeabi_llsl, .{ .name = "__aeabi_llsl", .linkage = common.linkage }); + @export(__aeabi_lasr, .{ .name = "__aeabi_lasr", .linkage = common.linkage }); + @export(__aeabi_llsr, .{ .name = "__aeabi_llsr", .linkage = common.linkage }); + } else { + @export(__ashldi3, .{ .name = "__ashldi3", .linkage = common.linkage }); + @export(__ashrdi3, .{ .name = "__ashrdi3", .linkage = common.linkage }); + @export(__lshrdi3, .{ .name = "__lshrdi3", .linkage = common.linkage }); } } @@ -115,30 +113,34 @@ inline fn lshrXi3(comptime T: type, a: T, b: i32) T { pub fn __ashldi3(a: i64, b: i32) callconv(.C) i64 { return ashlXi3(i64, a, b); } +fn __aeabi_llsl(a: i64, b: i32) callconv(.AAPCS) i64 { + return ashlXi3(i64, a, b); +} + pub fn __ashlti3(a: i128, b: i32) callconv(.C) i128 { return ashlXi3(i128, a, b); } + pub fn __ashrdi3(a: i64, b: i32) callconv(.C) i64 { return ashrXi3(i64, a, b); } +fn __aeabi_lasr(a: i64, b: i32) callconv(.AAPCS) i64 { + return ashrXi3(i64, a, b); +} + pub fn __ashrti3(a: i128, b: i32) callconv(.C) i128 { return ashrXi3(i128, a, b); } + pub fn __lshrdi3(a: i64, b: i32) callconv(.C) i64 { return lshrXi3(i64, a, b); } -pub fn __lshrti3(a: i128, b: i32) callconv(.C) i128 { - return lshrXi3(i128, a, b); +fn __aeabi_llsr(a: i64, b: i32) callconv(.AAPCS) i64 { + return lshrXi3(i64, a, b); } -pub fn __aeabi_llsl(a: i64, b: i32) callconv(.AAPCS) i64 { - return ashlXi3(i64, a, b); -} -pub fn __aeabi_lasr(a: i64, b: i32) callconv(.AAPCS) i64 { - return ashrXi3(i64, a, b); -} -pub fn __aeabi_llsr(a: i64, b: i32) callconv(.AAPCS) i64 { - return lshrXi3(i64, a, b); +pub fn __lshrti3(a: i128, b: i32) callconv(.C) i128 { + return lshrXi3(i128, a, b); } test { diff --git a/lib/compiler_rt/sin.zig b/lib/compiler_rt/sin.zig index 9766b2d541..1b93aab948 100644 --- a/lib/compiler_rt/sin.zig +++ b/lib/compiler_rt/sin.zig @@ -1,34 +1,30 @@ -// Ported from musl, which is licensed under the MIT license: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/sinf.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/sin.c +//! Ported from musl, which is licensed under the MIT license: +//! https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +//! +//! https://git.musl-libc.org/cgit/musl/tree/src/math/sinf.c +//! https://git.musl-libc.org/cgit/musl/tree/src/math/sin.c const std = @import("std"); const builtin = @import("builtin"); const arch = builtin.cpu.arch; const math = std.math; const expect = std.testing.expect; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); const trig = @import("trig.zig"); const rem_pio2 = @import("rem_pio2.zig").rem_pio2; const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; -comptime { - @export(__sinh, .{ .name = "__sinh", .linkage = linkage }); - @export(sinf, .{ .name = "sinf", .linkage = linkage }); - @export(sin, .{ .name = "sin", .linkage = linkage }); - @export(__sinx, .{ .name = "__sinx", .linkage = linkage }); - @export(sinq, .{ .name = "sinq", .linkage = linkage }); - @export(sinl, .{ .name = "sinl", .linkage = linkage }); +pub const panic = common.panic; - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(sinf128, .{ .name = "sinf128", .linkage = linkage }); - } - } +comptime { + @export(__sinh, .{ .name = "__sinh", .linkage = common.linkage }); + @export(sinf, .{ .name = "sinf", .linkage = common.linkage }); + @export(sin, .{ .name = "sin", .linkage = common.linkage }); + @export(__sinx, .{ .name = "__sinx", .linkage = common.linkage }); + const sinq_sym_name = if (common.want_ppc_abi) "sinf128" else "sinq"; + @export(sinq, .{ .name = sinq_sym_name, .linkage = common.linkage }); + @export(sinl, .{ .name = "sinl", .linkage = common.linkage }); } pub fn __sinh(x: f16) callconv(.C) f16 { @@ -130,10 +126,6 @@ pub fn sinq(x: f128) callconv(.C) f128 { return sin(@floatCast(f64, x)); } -pub fn sinf128(x: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, sinq, .{x}); -} - pub fn sinl(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __sinh(x), diff --git a/lib/compiler_rt/sincos.zig b/lib/compiler_rt/sincos.zig index da68022f89..c839356a36 100644 --- a/lib/compiler_rt/sincos.zig +++ b/lib/compiler_rt/sincos.zig @@ -5,22 +5,18 @@ const math = std.math; const trig = @import("trig.zig"); const rem_pio2 = @import("rem_pio2.zig").rem_pio2; const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__sincosh, .{ .name = "__sincosh", .linkage = linkage }); - @export(sincosf, .{ .name = "sincosf", .linkage = linkage }); - @export(sincos, .{ .name = "sincos", .linkage = linkage }); - @export(__sincosx, .{ .name = "__sincosx", .linkage = linkage }); - @export(sincosq, .{ .name = "sincosq", .linkage = linkage }); - @export(sincosl, .{ .name = "sincosl", .linkage = linkage }); - - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(sincosf128, .{ .name = "sincosf128", .linkage = linkage }); - } - } + @export(__sincosh, .{ .name = "__sincosh", .linkage = common.linkage }); + @export(sincosf, .{ .name = "sincosf", .linkage = common.linkage }); + @export(sincos, .{ .name = "sincos", .linkage = common.linkage }); + @export(__sincosx, .{ .name = "__sincosx", .linkage = common.linkage }); + const sincosq_sym_name = if (common.want_ppc_abi) "sincosf128" else "sincosq"; + @export(sincosq, .{ .name = sincosq_sym_name, .linkage = common.linkage }); + @export(sincosl, .{ .name = "sincosl", .linkage = common.linkage }); } pub fn __sincosh(x: f16, r_sin: *f16, r_cos: *f16) callconv(.C) void { @@ -198,10 +194,6 @@ pub fn sincosq(x: f128, r_sin: *f128, r_cos: *f128) callconv(.C) void { r_cos.* = small_cos; } -pub fn sincosf128(x: f128, r_sin: *f128, r_cos: *f128) callconv(.C) void { - return @call(.{ .modifier = .always_inline }, sincosq, .{ x, r_sin, r_cos }); -} - pub fn sincosl(x: c_longdouble, r_sin: *c_longdouble, r_cos: *c_longdouble) callconv(.C) void { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __sincosh(x, r_sin, r_cos), diff --git a/lib/compiler_rt/sqrt.zig b/lib/compiler_rt/sqrt.zig index ec62ff3d39..01b09213fe 100644 --- a/lib/compiler_rt/sqrt.zig +++ b/lib/compiler_rt/sqrt.zig @@ -2,22 +2,18 @@ const std = @import("std"); const builtin = @import("builtin"); const arch = builtin.cpu.arch; const math = std.math; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__sqrth, .{ .name = "__sqrth", .linkage = linkage }); - @export(sqrtf, .{ .name = "sqrtf", .linkage = linkage }); - @export(sqrt, .{ .name = "sqrt", .linkage = linkage }); - @export(__sqrtx, .{ .name = "__sqrtx", .linkage = linkage }); - @export(sqrtq, .{ .name = "sqrtq", .linkage = linkage }); - @export(sqrtl, .{ .name = "sqrtl", .linkage = linkage }); - - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(sqrtf128, .{ .name = "sqrtf128", .linkage = linkage }); - } - } + @export(__sqrth, .{ .name = "__sqrth", .linkage = common.linkage }); + @export(sqrtf, .{ .name = "sqrtf", .linkage = common.linkage }); + @export(sqrt, .{ .name = "sqrt", .linkage = common.linkage }); + @export(__sqrtx, .{ .name = "__sqrtx", .linkage = common.linkage }); + const sqrtq_sym_name = if (common.want_ppc_abi) "sqrtf128" else "sqrtq"; + @export(sqrtq, .{ .name = sqrtq_sym_name, .linkage = common.linkage }); + @export(sqrtl, .{ .name = "sqrtl", .linkage = common.linkage }); } pub fn __sqrth(x: f16) callconv(.C) f16 { @@ -255,10 +251,6 @@ pub fn sqrtl(x: c_longdouble) callconv(.C) c_longdouble { } } -pub fn sqrtf128(x: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, sqrtq, .{x}); -} - test "sqrtf" { const V = [_]f32{ 0.0, diff --git a/lib/compiler_rt/subo.zig b/lib/compiler_rt/subo.zig index c2b73a599d..a7dcf258aa 100644 --- a/lib/compiler_rt/subo.zig +++ b/lib/compiler_rt/subo.zig @@ -1,22 +1,31 @@ +//! subo - subtract overflow +//! * return a-%b. +//! * return if a-b overflows => 1 else => 0 +//! - suboXi4_generic as default + const std = @import("std"); const builtin = @import("builtin"); -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__subosi4, .{ .name = "__subosi4", .linkage = linkage }); - @export(__subodi4, .{ .name = "__subodi4", .linkage = linkage }); - @export(__suboti4, .{ .name = "__suboti4", .linkage = linkage }); + @export(__subosi4, .{ .name = "__subosi4", .linkage = common.linkage }); + @export(__subodi4, .{ .name = "__subodi4", .linkage = common.linkage }); + @export(__suboti4, .{ .name = "__suboti4", .linkage = common.linkage }); } -// subo - subtract overflow -// * return a-%b. -// * return if a-b overflows => 1 else => 0 -// - suboXi4_generic as default +pub fn __subosi4(a: i32, b: i32, overflow: *c_int) callconv(.C) i32 { + return suboXi4_generic(i32, a, b, overflow); +} +pub fn __subodi4(a: i64, b: i64, overflow: *c_int) callconv(.C) i64 { + return suboXi4_generic(i64, a, b, overflow); +} +pub fn __suboti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 { + return suboXi4_generic(i128, a, b, overflow); +} inline fn suboXi4_generic(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST { - @setRuntimeSafety(builtin.is_test); overflow.* = 0; var sum: ST = a -% b; // Hackers Delight: section Overflow Detection, subsection Signed Add/Subtract @@ -31,16 +40,6 @@ inline fn suboXi4_generic(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST return sum; } -pub fn __subosi4(a: i32, b: i32, overflow: *c_int) callconv(.C) i32 { - return suboXi4_generic(i32, a, b, overflow); -} -pub fn __subodi4(a: i64, b: i64, overflow: *c_int) callconv(.C) i64 { - return suboXi4_generic(i64, a, b, overflow); -} -pub fn __suboti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 { - return suboXi4_generic(i128, a, b, overflow); -} - test { _ = @import("subosi4_test.zig"); _ = @import("subodi4_test.zig"); diff --git a/lib/compiler_rt/tan.zig b/lib/compiler_rt/tan.zig index c03c3e8649..9c44e4c682 100644 --- a/lib/compiler_rt/tan.zig +++ b/lib/compiler_rt/tan.zig @@ -1,9 +1,9 @@ -// Ported from musl, which is licensed under the MIT license: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/tanf.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/tan.c -// https://golang.org/src/math/tan.go +//! Ported from musl, which is licensed under the MIT license: +//! https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +//! +//! https://git.musl-libc.org/cgit/musl/tree/src/math/tanf.c +//! https://git.musl-libc.org/cgit/musl/tree/src/math/tan.c +//! https://golang.org/src/math/tan.go const std = @import("std"); const builtin = @import("builtin"); @@ -15,22 +15,18 @@ const rem_pio2 = @import("rem_pio2.zig").rem_pio2; const rem_pio2f = @import("rem_pio2f.zig").rem_pio2f; const arch = builtin.cpu.arch; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__tanh, .{ .name = "__tanh", .linkage = linkage }); - @export(tanf, .{ .name = "tanf", .linkage = linkage }); - @export(tan, .{ .name = "tan", .linkage = linkage }); - @export(__tanx, .{ .name = "__tanx", .linkage = linkage }); - @export(tanq, .{ .name = "tanq", .linkage = linkage }); - @export(tanl, .{ .name = "tanl", .linkage = linkage }); - - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(tanf128, .{ .name = "tanf128", .linkage = linkage }); - } - } + @export(__tanh, .{ .name = "__tanh", .linkage = common.linkage }); + @export(tanf, .{ .name = "tanf", .linkage = common.linkage }); + @export(tan, .{ .name = "tan", .linkage = common.linkage }); + @export(__tanx, .{ .name = "__tanx", .linkage = common.linkage }); + const tanq_sym_name = if (common.want_ppc_abi) "tanf128" else "tanq"; + @export(tanq, .{ .name = tanq_sym_name, .linkage = common.linkage }); + @export(tanl, .{ .name = "tanl", .linkage = common.linkage }); } pub fn __tanh(x: f16) callconv(.C) f16 { @@ -116,10 +112,6 @@ pub fn tanq(x: f128) callconv(.C) f128 { return tan(@floatCast(f64, x)); } -pub fn tanf128(x: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, tanq, .{x}); -} - pub fn tanl(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __tanh(x), diff --git a/lib/compiler_rt/trunc.zig b/lib/compiler_rt/trunc.zig index c377a86666..9ced5bc92c 100644 --- a/lib/compiler_rt/trunc.zig +++ b/lib/compiler_rt/trunc.zig @@ -1,30 +1,26 @@ -// Ported from musl, which is licensed under the MIT license: -// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT -// -// https://git.musl-libc.org/cgit/musl/tree/src/math/truncf.c -// https://git.musl-libc.org/cgit/musl/tree/src/math/trunc.c +//! Ported from musl, which is MIT licensed. +//! https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT +//! +//! https://git.musl-libc.org/cgit/musl/tree/src/math/truncf.c +//! https://git.musl-libc.org/cgit/musl/tree/src/math/trunc.c const std = @import("std"); const builtin = @import("builtin"); +const arch = builtin.cpu.arch; const math = std.math; const expect = std.testing.expect; -const arch = builtin.cpu.arch; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { - @export(__trunch, .{ .name = "__trunch", .linkage = linkage }); - @export(truncf, .{ .name = "truncf", .linkage = linkage }); - @export(trunc, .{ .name = "trunc", .linkage = linkage }); - @export(__truncx, .{ .name = "__truncx", .linkage = linkage }); - @export(truncq, .{ .name = "truncq", .linkage = linkage }); - @export(truncl, .{ .name = "truncl", .linkage = linkage }); - - if (!builtin.is_test) { - if (arch.isPPC() or arch.isPPC64()) { - @export(truncf128, .{ .name = "truncf128", .linkage = linkage }); - } - } + @export(__trunch, .{ .name = "__trunch", .linkage = common.linkage }); + @export(truncf, .{ .name = "truncf", .linkage = common.linkage }); + @export(trunc, .{ .name = "trunc", .linkage = common.linkage }); + @export(__truncx, .{ .name = "__truncx", .linkage = common.linkage }); + const truncq_sym_name = if (common.want_ppc_abi) "truncf128" else "truncq"; + @export(truncq, .{ .name = truncq_sym_name, .linkage = common.linkage }); + @export(truncl, .{ .name = "truncl", .linkage = common.linkage }); } pub fn __trunch(x: f16) callconv(.C) f16 { @@ -100,10 +96,6 @@ pub fn truncq(x: f128) callconv(.C) f128 { } } -pub fn truncf128(x: f128) callconv(.C) f128 { - return @call(.{ .modifier = .always_inline }, truncq, .{x}); -} - pub fn truncl(x: c_longdouble) callconv(.C) c_longdouble { switch (@typeInfo(c_longdouble).Float.bits) { 16 => return __trunch(x), diff --git a/lib/compiler_rt/udivmodti4.zig b/lib/compiler_rt/udivmodti4.zig index 3bad0c822e..911bf72eed 100644 --- a/lib/compiler_rt/udivmodti4.zig +++ b/lib/compiler_rt/udivmodti4.zig @@ -2,36 +2,35 @@ const std = @import("std"); const builtin = @import("builtin"); const udivmod = @import("udivmod.zig").udivmod; const arch = builtin.cpu.arch; -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { if (builtin.os.tag == .windows) { switch (arch) { .i386 => { - @export(__udivmodti4, .{ .name = "__udivmodti4", .linkage = linkage }); + @export(__udivmodti4, .{ .name = "__udivmodti4", .linkage = common.linkage }); }, .x86_64 => { // The "ti" functions must use Vector(2, u64) parameter types to adhere to the ABI // that LLVM expects compiler-rt to have. - @export(__udivmodti4_windows_x86_64, .{ .name = "__udivmodti4", .linkage = linkage }); + @export(__udivmodti4_windows_x86_64, .{ .name = "__udivmodti4", .linkage = common.linkage }); }, else => {}, } } else { - @export(__udivmodti4, .{ .name = "__udivmodti4", .linkage = linkage }); + @export(__udivmodti4, .{ .name = "__udivmodti4", .linkage = common.linkage }); } } pub fn __udivmodti4(a: u128, b: u128, maybe_rem: ?*u128) callconv(.C) u128 { - @setRuntimeSafety(builtin.is_test); return udivmod(u128, a, b, maybe_rem); } const v128 = std.meta.Vector(2, u64); -pub fn __udivmodti4_windows_x86_64(a: v128, b: v128, maybe_rem: ?*u128) callconv(.C) v128 { - @setRuntimeSafety(builtin.is_test); + +fn __udivmodti4_windows_x86_64(a: v128, b: v128, maybe_rem: ?*u128) callconv(.C) v128 { return @bitCast(v128, udivmod(u128, @bitCast(u128, a), @bitCast(u128, b), maybe_rem)); } diff --git a/lib/compiler_rt/udivti3.zig b/lib/compiler_rt/udivti3.zig index f12b215c59..3e908176bc 100644 --- a/lib/compiler_rt/udivti3.zig +++ b/lib/compiler_rt/udivti3.zig @@ -2,38 +2,37 @@ const std = @import("std"); const builtin = @import("builtin"); const udivmod = @import("udivmod.zig").udivmod; const arch = builtin.cpu.arch; -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { if (builtin.os.tag == .windows) { switch (arch) { .i386 => { - @export(__udivti3, .{ .name = "__udivti3", .linkage = linkage }); + @export(__udivti3, .{ .name = "__udivti3", .linkage = common.linkage }); }, .x86_64 => { // The "ti" functions must use Vector(2, u64) parameter types to adhere to the ABI // that LLVM expects compiler-rt to have. - @export(__udivti3_windows_x86_64, .{ .name = "__udivti3", .linkage = linkage }); + @export(__udivti3_windows_x86_64, .{ .name = "__udivti3", .linkage = common.linkage }); }, else => {}, } if (arch.isAARCH64()) { - @export(__udivti3, .{ .name = "__udivti3", .linkage = linkage }); + @export(__udivti3, .{ .name = "__udivti3", .linkage = common.linkage }); } } else { - @export(__udivti3, .{ .name = "__udivti3", .linkage = linkage }); + @export(__udivti3, .{ .name = "__udivti3", .linkage = common.linkage }); } } pub fn __udivti3(a: u128, b: u128) callconv(.C) u128 { - @setRuntimeSafety(builtin.is_test); return udivmod(u128, a, b, null); } const v128 = std.meta.Vector(2, u64); -pub fn __udivti3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { - @setRuntimeSafety(builtin.is_test); + +fn __udivti3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { return @bitCast(v128, udivmod(u128, @bitCast(u128, a), @bitCast(u128, b), null)); } diff --git a/lib/compiler_rt/umodti3.zig b/lib/compiler_rt/umodti3.zig index aef2ba434d..65058a599e 100644 --- a/lib/compiler_rt/umodti3.zig +++ b/lib/compiler_rt/umodti3.zig @@ -2,42 +2,41 @@ const std = @import("std"); const builtin = @import("builtin"); const udivmod = @import("udivmod.zig").udivmod; const arch = builtin.cpu.arch; -const is_test = builtin.is_test; -const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const panic = @import("common.zig").panic; +const common = @import("common.zig"); + +pub const panic = common.panic; comptime { if (builtin.os.tag == .windows) { switch (arch) { .i386 => { - @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage }); + @export(__umodti3, .{ .name = "__umodti3", .linkage = common.linkage }); }, .x86_64 => { // The "ti" functions must use Vector(2, u64) parameter types to adhere to the ABI // that LLVM expects compiler-rt to have. - @export(__umodti3_windows_x86_64, .{ .name = "__umodti3", .linkage = linkage }); + @export(__umodti3_windows_x86_64, .{ .name = "__umodti3", .linkage = common.linkage }); }, else => {}, } if (arch.isAARCH64()) { - @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage }); + @export(__umodti3, .{ .name = "__umodti3", .linkage = common.linkage }); } } else { - @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage }); + @export(__umodti3, .{ .name = "__umodti3", .linkage = common.linkage }); } } pub fn __umodti3(a: u128, b: u128) callconv(.C) u128 { - @setRuntimeSafety(builtin.is_test); var r: u128 = undefined; _ = udivmod(u128, a, b, &r); return r; } const v128 = std.meta.Vector(2, u64); -pub fn __umodti3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { - return @bitCast(v128, @call(.{ .modifier = .always_inline }, __umodti3, .{ - @bitCast(u128, a), - @bitCast(u128, b), - })); + +fn __umodti3_windows_x86_64(a: v128, b: v128) callconv(.C) v128 { + var r: u128 = undefined; + _ = udivmod(u128, @bitCast(u128, a), @bitCast(u128, b), &r); + return @bitCast(v128, r); } From ebab5288c3240fc3298e2d94954a7117e3a8d557 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 17 Jun 2022 18:22:11 -0700 Subject: [PATCH 1866/2031] compiler-rt: fix aeabi logic Before, compiler-rt would have the wrong symbols for ARM targets. --- lib/compiler_rt/common.zig | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/compiler_rt/common.zig b/lib/compiler_rt/common.zig index 2b7ef84764..6dc6ced926 100644 --- a/lib/compiler_rt/common.zig +++ b/lib/compiler_rt/common.zig @@ -4,7 +4,19 @@ const math = std.math; const is_test = builtin.is_test; pub const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; -pub const want_aeabi = builtin.cpu.arch.isARM() or builtin.cpu.arch.isThumb(); +pub const want_aeabi = switch (builtin.abi) { + .eabi, + .eabihf, + .musleabi, + .musleabihf, + .gnueabi, + .gnueabihf, + => switch (builtin.cpu.arch) { + .arm, .armeb, .thumb, .thumbeb => true, + else => false, + }, + else => false, +}; pub const want_ppc_abi = builtin.cpu.arch.isPPC() or builtin.cpu.arch.isPPC64(); pub const want_msvc_abi = builtin.abi == .msvc; /// Example symbols: From 2064d86298cf4aefa28ace10d02307ac2f067cbb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 17 Jun 2022 18:24:57 -0700 Subject: [PATCH 1867/2031] update CMakeLists.txt source files --- CMakeLists.txt | 136 ++++++++++++++++++++++++++++--------------------- 1 file changed, 78 insertions(+), 58 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 69bc292cd8..0ba727f09e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -480,15 +480,15 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/sort.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/absv.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/absvdi2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/absvsi2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/absvti2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/adddf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/addf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/addo.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/addsf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/addtf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/addxf3.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subdf3.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subsf3.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subtf3.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subxf3.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/addo.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/arm.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/atomics.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/aulldiv.zig" @@ -497,19 +497,12 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/compiler_rt/ceil.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/clear_cache.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/cmp.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/common.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/comparef.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/cmpsf2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/cmpdf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/cmpsf2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/cmptf2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/cmpxf2.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/gesf2.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/gedf2.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/getf2.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/gexf2.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/unordsf2.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/unorddf2.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/unordtf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/common.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/comparef.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/cos.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/count0bits.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/divdf3.zig" @@ -520,9 +513,9 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/compiler_rt/emutls.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/exp.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/exp2.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extendf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extenddftf2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extenddfxf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extendf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extendhfsf2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extendhftf2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extendhfxf2.zig" @@ -531,81 +524,90 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extendsfxf2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/extendxftf2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fabs.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/int_to_float.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatsihf.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatsisf.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatsidf.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatsitf.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatsixf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixdfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixdfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixdfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixhfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixhfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixhfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixsfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixsfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixsfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixtfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixtfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixtfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunsdfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunsdfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunsdfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunshfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunshfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunshfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunssfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunssfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunssfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunstfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunstfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunstfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunsxfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunsxfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunsxfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixxfdi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixxfsi.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixxfti.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/float_to_int.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatdidf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatdihf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatdisf.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatdidf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatditf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatdixf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatsidf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatsihf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatsisf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatsitf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatsixf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floattidf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floattihf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floattisf.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floattidf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floattitf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floattixf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatundidf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatundihf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatundisf.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatundidf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatunditf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatundixf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatunsidf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatunsihf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatunsisf.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatunsidf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatunsitf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatunsixf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatuntidf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatuntihf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatuntisf.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatuntidf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatuntitf.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floatuntixf.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/float_to_int.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixhfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixhfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixhfti.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixsfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixsfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixsfti.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixdfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixdfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixdfti.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixtfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixtfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixtfti.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixxfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixxfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixxfti.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunshfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunshfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunshfti.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunssfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunssfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunssfti.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunsdfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunsdfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunsdfti.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunstfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunstfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunstfti.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunsxfsi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunsxfdi.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fixunsxfti.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/floor.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fma.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fmax.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fmin.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/fmod.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/gedf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/gesf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/getf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/gexf2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/int.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/int_to_float.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/log.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/log10.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/log2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/modti3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/muldf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/muldi3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulo.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulsf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/multf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/multi3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulxf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negXf2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negXi2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negv.zig" @@ -621,14 +623,32 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/compiler_rt/sincos.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/sqrt.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/stack_probe.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subdf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subo.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subsf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subtf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subxf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/tan.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/trig.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/trunc.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/truncdfhf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/truncdfsf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/truncf.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/truncsfhf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/trunctfdf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/trunctfhf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/trunctfsf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/trunctfxf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/truncxfdf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/truncxfhf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/truncxfsf2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/udivmod.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/udivmodti4.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/udivti3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/umodti3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/unorddf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/unordsf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/unordtf2.zig" "${CMAKE_SOURCE_DIR}/lib/std/start.zig" "${CMAKE_SOURCE_DIR}/lib/std/std.zig" "${CMAKE_SOURCE_DIR}/lib/std/target.zig" From e4092d44426a471ee6097fae24069c72cffdc22a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 17 Jun 2022 18:34:11 -0700 Subject: [PATCH 1868/2031] stage2: rip out multi-compilation-unit compiler-rt After doing performance testing, it seems that multi-compilation-unit compiler-rt did not bring the performance improvements that we expected it to. The idea is that it makes linking faster, however, it incurred a cost in the frontend that was not offset by any gains in linking. Furthermore, the single-object compiler-rt (with -ffunction-sections and --gc-sections) ends up being fewer bytes on disk and so it's actually the same or faster linking speed than the multi-compilation-unit version. So we are planning to keep using single-compilation-unit compiler-rt for the foreseeable future, but may experiment with this again in the future, in which case this commit can be reverted. --- CMakeLists.txt | 1 - src/Compilation.zig | 54 ++---- src/compiler_rt.zig | 406 -------------------------------------------- 3 files changed, 10 insertions(+), 451 deletions(-) delete mode 100644 src/compiler_rt.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ba727f09e..29f521c789 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -735,7 +735,6 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/codegen/c.zig" "${CMAKE_SOURCE_DIR}/src/codegen/llvm.zig" "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig" - "${CMAKE_SOURCE_DIR}/src/compiler_rt.zig" "${CMAKE_SOURCE_DIR}/src/glibc.zig" "${CMAKE_SOURCE_DIR}/src/introspect.zig" "${CMAKE_SOURCE_DIR}/src/libc_installation.zig" diff --git a/src/Compilation.zig b/src/Compilation.zig index 89e54b598f..2646da2f6f 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -23,7 +23,6 @@ const mingw = @import("mingw.zig"); const libunwind = @import("libunwind.zig"); const libcxx = @import("libcxx.zig"); const wasi_libc = @import("wasi_libc.zig"); -const compiler_rt = @import("compiler_rt.zig"); const fatal = @import("main.zig").fatal; const clangMain = @import("main.zig").clangMain; const Module = @import("Module.zig"); @@ -2745,10 +2744,6 @@ pub fn performAllTheWork( var embed_file_prog_node = main_progress_node.start("Detect @embedFile updates", comp.embed_file_work_queue.count); defer embed_file_prog_node.end(); - // +1 for the link step - var compiler_rt_prog_node = main_progress_node.start("compiler_rt", compiler_rt.sources.len + 1); - defer compiler_rt_prog_node.end(); - comp.work_queue_wait_group.reset(); defer comp.work_queue_wait_group.wait(); @@ -2796,28 +2791,6 @@ pub fn performAllTheWork( comp, c_object, &c_obj_prog_node, &comp.work_queue_wait_group, }); } - - if (comp.job_queued_compiler_rt_lib) { - comp.job_queued_compiler_rt_lib = false; - - // I have disabled the multi-threaded compiler-rt for now until - // the threading deadlock is resolved. - if (use_stage1 or true) { - // stage1 LLVM backend uses the global context and thus cannot be used in - // a multi-threaded context. - buildCompilerRtOneShot(comp, .Lib, &comp.compiler_rt_lib); - } else { - comp.work_queue_wait_group.start(); - try comp.thread_pool.spawn(workerBuildCompilerRtLib, .{ - comp, &compiler_rt_prog_node, &comp.work_queue_wait_group, - }); - } - } - - if (comp.job_queued_compiler_rt_obj) { - comp.job_queued_compiler_rt_obj = false; - buildCompilerRtOneShot(comp, .Obj, &comp.compiler_rt_obj); - } } if (!use_stage1) { @@ -2862,6 +2835,16 @@ pub fn performAllTheWork( } break; } + + if (comp.job_queued_compiler_rt_lib) { + comp.job_queued_compiler_rt_lib = false; + buildCompilerRtOneShot(comp, .Lib, &comp.compiler_rt_lib); + } + + if (comp.job_queued_compiler_rt_obj) { + comp.job_queued_compiler_rt_obj = false; + buildCompilerRtOneShot(comp, .Obj, &comp.compiler_rt_obj); + } } fn processOneJob(comp: *Compilation, job: Job) !void { @@ -3534,23 +3517,6 @@ fn buildCompilerRtOneShot( }; } -fn workerBuildCompilerRtLib( - comp: *Compilation, - progress_node: *std.Progress.Node, - wg: *WaitGroup, -) void { - defer wg.finish(); - - compiler_rt.buildCompilerRtLib(comp, progress_node) catch |err| switch (err) { - error.SubCompilationFailed => return, // error reported already - else => comp.lockAndSetMiscFailure( - .compiler_rt, - "unable to build compiler_rt: {s}", - .{@errorName(err)}, - ), - }; -} - fn reportRetryableCObjectError( comp: *Compilation, c_object: *CObject, diff --git a/src/compiler_rt.zig b/src/compiler_rt.zig deleted file mode 100644 index 185ebf6b16..0000000000 --- a/src/compiler_rt.zig +++ /dev/null @@ -1,406 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const build_options = @import("build_options"); -const Allocator = std.mem.Allocator; -const assert = std.debug.assert; -const mem = std.mem; -const tracy = @import("tracy.zig"); -const trace = tracy.trace; - -const Cache = @import("Cache.zig"); -const Compilation = @import("Compilation.zig"); -const CRTFile = Compilation.CRTFile; -const LinkObject = Compilation.LinkObject; -const Package = @import("Package.zig"); -const WaitGroup = @import("WaitGroup.zig"); - -pub fn buildCompilerRtLib(comp: *Compilation, progress_node: *std.Progress.Node) !void { - var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); - - const target = comp.getTarget(); - - const root_name = "compiler_rt"; - const basename = try std.zig.binNameAlloc(arena, .{ - .root_name = root_name, - .target = target, - .output_mode = .Lib, - }); - - var link_objects: [sources.len]LinkObject = undefined; - var crt_files = [1]?CRTFile{null} ** sources.len; - defer deinitCrtFiles(comp, crt_files); - - { - var wg: WaitGroup = .{}; - defer comp.thread_pool.waitAndWork(&wg); - - for (sources) |source, i| { - wg.start(); - try comp.thread_pool.spawn(workerBuildObject, .{ - comp, progress_node, &wg, source, &crt_files[i], - }); - } - } - - for (link_objects) |*link_object, i| { - link_object.* = .{ - .path = crt_files[i].?.full_object_path, - }; - } - - var link_progress_node = progress_node.start("link", 0); - link_progress_node.activate(); - defer link_progress_node.end(); - - // TODO: This is extracted into a local variable to work around a stage1 miscompilation. - const emit_bin = Compilation.EmitLoc{ - .directory = null, // Put it in the cache directory. - .basename = basename, - }; - const sub_compilation = try Compilation.create(comp.gpa, .{ - .local_cache_directory = comp.global_cache_directory, - .global_cache_directory = comp.global_cache_directory, - .zig_lib_directory = comp.zig_lib_directory, - .cache_mode = .whole, - .target = target, - .root_name = root_name, - .main_pkg = null, - .output_mode = .Lib, - .link_mode = .Static, - .function_sections = true, - .thread_pool = comp.thread_pool, - .libc_installation = comp.bin_file.options.libc_installation, - .emit_bin = emit_bin, - .optimize_mode = comp.compilerRtOptMode(), - .want_sanitize_c = false, - .want_stack_check = false, - .want_red_zone = comp.bin_file.options.red_zone, - .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer, - .want_valgrind = false, - .want_tsan = false, - .want_pic = comp.bin_file.options.pic, - .want_pie = comp.bin_file.options.pie, - .want_lto = comp.bin_file.options.lto, - .emit_h = null, - .strip = comp.compilerRtStrip(), - .is_native_os = comp.bin_file.options.is_native_os, - .is_native_abi = comp.bin_file.options.is_native_abi, - .self_exe_path = comp.self_exe_path, - .link_objects = &link_objects, - .verbose_cc = comp.verbose_cc, - .verbose_link = comp.bin_file.options.verbose_link, - .verbose_air = comp.verbose_air, - .verbose_llvm_ir = comp.verbose_llvm_ir, - .verbose_cimport = comp.verbose_cimport, - .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, - .clang_passthrough_mode = comp.clang_passthrough_mode, - .skip_linker_dependencies = true, - .parent_compilation_link_libc = comp.bin_file.options.link_libc, - }); - defer sub_compilation.destroy(); - - try sub_compilation.updateSubCompilation(); - - assert(comp.compiler_rt_lib == null); - comp.compiler_rt_lib = .{ - .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ - sub_compilation.bin_file.options.emit.?.sub_path, - }), - .lock = sub_compilation.bin_file.toOwnedLock(), - }; -} - -fn deinitCrtFiles(comp: *Compilation, crt_files: [sources.len]?CRTFile) void { - const gpa = comp.gpa; - - for (crt_files) |opt_crt_file| { - var crt_file = opt_crt_file orelse continue; - crt_file.deinit(gpa); - } -} - -fn workerBuildObject( - comp: *Compilation, - progress_node: *std.Progress.Node, - wg: *WaitGroup, - src_basename: []const u8, - out: *?CRTFile, -) void { - defer wg.finish(); - - var obj_progress_node = progress_node.start(src_basename, 0); - obj_progress_node.activate(); - defer obj_progress_node.end(); - - buildObject(comp, src_basename, out) catch |err| switch (err) { - error.SubCompilationFailed => return, // error reported already - else => comp.lockAndSetMiscFailure( - .compiler_rt, - "unable to build compiler_rt: {s}", - .{@errorName(err)}, - ), - }; -} - -fn buildObject(comp: *Compilation, src_basename: []const u8, out: *?CRTFile) !void { - const gpa = comp.gpa; - - var root_src_path_buf: [64]u8 = undefined; - const root_src_path = std.fmt.bufPrint( - &root_src_path_buf, - "compiler_rt" ++ std.fs.path.sep_str ++ "{s}", - .{src_basename}, - ) catch unreachable; - - var main_pkg: Package = .{ - .root_src_directory = comp.zig_lib_directory, - .root_src_path = root_src_path, - }; - defer main_pkg.deinitTable(gpa); - const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len]; - const target = comp.getTarget(); - const output_mode: std.builtin.OutputMode = .Obj; - const bin_basename = try std.zig.binNameAlloc(gpa, .{ - .root_name = root_name, - .target = target, - .output_mode = output_mode, - }); - defer gpa.free(bin_basename); - - const emit_bin = Compilation.EmitLoc{ - .directory = null, // Put it in the cache directory. - .basename = bin_basename, - }; - const sub_compilation = try Compilation.create(gpa, .{ - .global_cache_directory = comp.global_cache_directory, - .local_cache_directory = comp.global_cache_directory, - .zig_lib_directory = comp.zig_lib_directory, - .cache_mode = .whole, - .target = target, - .root_name = root_name, - .main_pkg = &main_pkg, - .output_mode = output_mode, - .thread_pool = comp.thread_pool, - .libc_installation = comp.bin_file.options.libc_installation, - .emit_bin = emit_bin, - .optimize_mode = comp.compilerRtOptMode(), - .link_mode = .Static, - .function_sections = true, - .want_sanitize_c = false, - .want_stack_check = false, - .want_red_zone = comp.bin_file.options.red_zone, - .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer, - .want_valgrind = false, - .want_tsan = false, - .want_pic = comp.bin_file.options.pic, - .want_pie = comp.bin_file.options.pie, - .emit_h = null, - .strip = comp.compilerRtStrip(), - .is_native_os = comp.bin_file.options.is_native_os, - .is_native_abi = comp.bin_file.options.is_native_abi, - .self_exe_path = comp.self_exe_path, - .verbose_cc = comp.verbose_cc, - .verbose_link = comp.bin_file.options.verbose_link, - .verbose_air = comp.verbose_air, - .verbose_llvm_ir = comp.verbose_llvm_ir, - .verbose_cimport = comp.verbose_cimport, - .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, - .clang_passthrough_mode = comp.clang_passthrough_mode, - .skip_linker_dependencies = true, - .parent_compilation_link_libc = comp.bin_file.options.link_libc, - }); - defer sub_compilation.destroy(); - - try sub_compilation.update(); - // Look for compilation errors in this sub_compilation. - var keep_errors = false; - var errors = try sub_compilation.getAllErrorsAlloc(); - defer if (!keep_errors) errors.deinit(sub_compilation.gpa); - - if (errors.list.len != 0) { - const misc_task_tag: Compilation.MiscTask = .compiler_rt; - - comp.mutex.lock(); - defer comp.mutex.unlock(); - - try comp.misc_failures.ensureUnusedCapacity(gpa, 1); - comp.misc_failures.putAssumeCapacityNoClobber(misc_task_tag, .{ - .msg = try std.fmt.allocPrint(gpa, "sub-compilation of {s} failed", .{ - @tagName(misc_task_tag), - }), - .children = errors, - }); - keep_errors = true; - return error.SubCompilationFailed; - } - - assert(out.* == null); - out.* = Compilation.CRTFile{ - .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(gpa, &[_][]const u8{ - sub_compilation.bin_file.options.emit.?.sub_path, - }), - .lock = sub_compilation.bin_file.toOwnedLock(), - }; -} - -pub const sources = &[_][]const u8{ - "absvdi2.zig", - "absvsi2.zig", - "absvti2.zig", - "adddf3.zig", - "addo.zig", - "addsf3.zig", - "addtf3.zig", - "addxf3.zig", - "arm.zig", - "atomics.zig", - "aulldiv.zig", - "aullrem.zig", - "bswap.zig", - "ceil.zig", - "clear_cache.zig", - "cmp.zig", - "cmpdf2.zig", - "cmpsf2.zig", - "cmptf2.zig", - "cmpxf2.zig", - "cos.zig", - "count0bits.zig", - "divdf3.zig", - "divsf3.zig", - "divtf3.zig", - "divti3.zig", - "divxf3.zig", - "emutls.zig", - "exp.zig", - "exp2.zig", - "extenddftf2.zig", - "extenddfxf2.zig", - "extendhfsf2.zig", - "extendhftf2.zig", - "extendhfxf2.zig", - "extendsfdf2.zig", - "extendsftf2.zig", - "extendsfxf2.zig", - "extendxftf2.zig", - "fabs.zig", - "fixdfdi.zig", - "fixdfsi.zig", - "fixdfti.zig", - "fixhfdi.zig", - "fixhfsi.zig", - "fixhfti.zig", - "fixsfdi.zig", - "fixsfsi.zig", - "fixsfti.zig", - "fixtfdi.zig", - "fixtfsi.zig", - "fixtfti.zig", - "fixunsdfdi.zig", - "fixunsdfsi.zig", - "fixunsdfti.zig", - "fixunshfdi.zig", - "fixunshfsi.zig", - "fixunshfti.zig", - "fixunssfdi.zig", - "fixunssfsi.zig", - "fixunssfti.zig", - "fixunstfdi.zig", - "fixunstfsi.zig", - "fixunstfti.zig", - "fixunsxfdi.zig", - "fixunsxfsi.zig", - "fixunsxfti.zig", - "fixxfdi.zig", - "fixxfsi.zig", - "fixxfti.zig", - "floatdidf.zig", - "floatdihf.zig", - "floatdisf.zig", - "floatditf.zig", - "floatdixf.zig", - "floatsidf.zig", - "floatsihf.zig", - "floatsisf.zig", - "floatsitf.zig", - "floatsixf.zig", - "floattidf.zig", - "floattihf.zig", - "floattisf.zig", - "floattitf.zig", - "floattixf.zig", - "floatundidf.zig", - "floatundihf.zig", - "floatundisf.zig", - "floatunditf.zig", - "floatundixf.zig", - "floatunsidf.zig", - "floatunsihf.zig", - "floatunsisf.zig", - "floatunsitf.zig", - "floatunsixf.zig", - "floatuntidf.zig", - "floatuntihf.zig", - "floatuntisf.zig", - "floatuntitf.zig", - "floatuntixf.zig", - "floor.zig", - "fma.zig", - "fmax.zig", - "fmin.zig", - "fmod.zig", - "gedf2.zig", - "gesf2.zig", - "getf2.zig", - "gexf2.zig", - "int.zig", - "log.zig", - "log10.zig", - "log2.zig", - "modti3.zig", - "muldf3.zig", - "muldi3.zig", - "mulf3.zig", - "mulo.zig", - "mulsf3.zig", - "multf3.zig", - "multi3.zig", - "mulxf3.zig", - "negXf2.zig", - "negXi2.zig", - "negv.zig", - "os_version_check.zig", - "parity.zig", - "popcount.zig", - "round.zig", - "shift.zig", - "sin.zig", - "sincos.zig", - "sqrt.zig", - "stack_probe.zig", - "subdf3.zig", - "subo.zig", - "subsf3.zig", - "subtf3.zig", - "subxf3.zig", - "tan.zig", - "trunc.zig", - "truncdfhf2.zig", - "truncdfsf2.zig", - "truncsfhf2.zig", - "trunctfdf2.zig", - "trunctfhf2.zig", - "trunctfsf2.zig", - "trunctfxf2.zig", - "truncxfdf2.zig", - "truncxfhf2.zig", - "truncxfsf2.zig", - "udivmodti4.zig", - "udivti3.zig", - "umodti3.zig", - "unorddf2.zig", - "unordsf2.zig", - "unordtf2.zig", -}; From 30ef0336932cb2ec866a082b9b4584d08d179a55 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 17 Jun 2022 19:25:06 -0700 Subject: [PATCH 1869/2031] compiler-rt: fix logic for choosing `__gnu_{f2h,h2f}_ieee` wasm32-wasi-musl wants the standard symbol names however Linux requires the `__gnu_*` flavors. I did not find any authoritative source on what decides which symbol flavors to use. If we run into more trouble in the future we can go back to having both. --- lib/compiler_rt/common.zig | 37 ++++++++++++++++++++++++++------- lib/compiler_rt/extendhfsf2.zig | 2 +- lib/compiler_rt/truncsfhf2.zig | 2 +- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/lib/compiler_rt/common.zig b/lib/compiler_rt/common.zig index 6dc6ced926..8f9253b44b 100644 --- a/lib/compiler_rt/common.zig +++ b/lib/compiler_rt/common.zig @@ -1,7 +1,5 @@ const std = @import("std"); const builtin = @import("builtin"); -const math = std.math; -const is_test = builtin.is_test; pub const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; pub const want_aeabi = switch (builtin.abi) { @@ -18,19 +16,44 @@ pub const want_aeabi = switch (builtin.abi) { else => false, }; pub const want_ppc_abi = builtin.cpu.arch.isPPC() or builtin.cpu.arch.isPPC64(); -pub const want_msvc_abi = builtin.abi == .msvc; -/// Example symbols: + +/// This governs whether to use these symbol names for f16/f32 conversions +/// rather than the standard names: /// * __gnu_f2h_ieee /// * __gnu_h2f_ieee -pub const want_gnu_abi = builtin.abi.isGnu() or builtin.abi.isMusl(); +/// Known correct configurations: +/// x86_64-freestanding-none => true +/// x86_64-linux-none => true +/// x86_64-linux-gnu => true +/// x86_64-linux-musl => true +/// x86_64-linux-eabi => true +/// arm-linux-musleabihf => true +/// arm-linux-gnueabihf => true +/// arm-linux-eabihf => false +/// wasm32-wasi-musl => false +/// wasm32-freestanding-none => false +/// x86_64-windows-gnu => true +/// x86_64-windows-msvc => true +/// any-macos-any => doesn't matter; libSystem has both symbol flavors +pub const gnu_f16_abi = switch (builtin.cpu.arch) { + .wasm32, .wasm64 => false, + + .arm, .armeb, .thumb, .thumbeb => switch (builtin.abi) { + .eabi, .eabihf => false, + else => true, + }, + + else => true, +}; + pub const want_sparc_abi = builtin.cpu.arch.isSPARC(); // Avoid dragging in the runtime safety mechanisms into this .o file, // unless we're trying to test compiler-rt. pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn { _ = error_return_trace; - @setCold(true); - if (is_test) { + if (builtin.is_test) { + @setCold(true); std.debug.panic("{s}", .{msg}); } else { unreachable; diff --git a/lib/compiler_rt/extendhfsf2.zig b/lib/compiler_rt/extendhfsf2.zig index 56e6b9c01b..a6bf5f5be5 100644 --- a/lib/compiler_rt/extendhfsf2.zig +++ b/lib/compiler_rt/extendhfsf2.zig @@ -4,7 +4,7 @@ const extendf = @import("./extendf.zig").extendf; pub const panic = common.panic; comptime { - if (common.want_gnu_abi) { + if (common.gnu_f16_abi) { @export(__gnu_h2f_ieee, .{ .name = "__gnu_h2f_ieee", .linkage = common.linkage }); } else if (common.want_aeabi) { @export(__aeabi_h2f, .{ .name = "__aeabi_h2f", .linkage = common.linkage }); diff --git a/lib/compiler_rt/truncsfhf2.zig b/lib/compiler_rt/truncsfhf2.zig index 329b3332a2..489fb8658d 100644 --- a/lib/compiler_rt/truncsfhf2.zig +++ b/lib/compiler_rt/truncsfhf2.zig @@ -4,7 +4,7 @@ const truncf = @import("./truncf.zig").truncf; pub const panic = common.panic; comptime { - if (common.want_gnu_abi) { + if (common.gnu_f16_abi) { @export(__gnu_f2h_ieee, .{ .name = "__gnu_f2h_ieee", .linkage = common.linkage }); } else if (common.want_aeabi) { @export(__aeabi_f2h, .{ .name = "__aeabi_f2h", .linkage = common.linkage }); From 8f9b31af926e0635a82c30d0eb7b2942f78932e5 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 18 Jun 2022 20:04:49 +0300 Subject: [PATCH 1870/2031] value: handle slices in canMutateComptimeVarState --- lib/std/mem.zig | 1 - src/value.zig | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 7c8e914b57..61d6b84874 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -2080,7 +2080,6 @@ fn testReadIntImpl() !void { } test "writeIntSlice" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO try testWriteIntImpl(); comptime try testWriteIntImpl(); } diff --git a/src/value.zig b/src/value.zig index 60c212a84f..e5332a72e7 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2414,6 +2414,7 @@ pub const Value = extern union { return false; }, .@"union" => return val.cast(Payload.Union).?.data.val.canMutateComptimeVarState(), + .slice => return val.castTag(.slice).?.data.ptr.canMutateComptimeVarState(), else => return false, } } From 091238254e75253597e12ee381a3197751d61973 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 18 Jun 2022 22:55:55 +0200 Subject: [PATCH 1871/2031] macho: return from flushModule if building object for static lib --- src/link/MachO.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 44d763289a..c71007157a 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -451,6 +451,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No llvm_object.destroy(self.base.allocator); self.llvm_object = null; + + if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Static) { + return; + } } } From fcd4280a8cabf7859fab450cc0d4b65f6adaabe5 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 15 Jun 2022 20:26:53 +0200 Subject: [PATCH 1872/2031] wasm: implement saturating add, sub for unsigned Implements +| and -| for unsigned integers <= 64 bits. --- src/arch/wasm/CodeGen.zig | 65 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index c395c26437..2e1d88083c 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1432,8 +1432,10 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .const_ty => unreachable, .add => self.airBinOp(inst, .add), + .add_sat => self.airSatBinOp(inst, .add), .addwrap => self.airWrapBinOp(inst, .add), .sub => self.airBinOp(inst, .sub), + .sub_sat => self.airSatBinOp(inst, .sub), .subwrap => self.airWrapBinOp(inst, .sub), .mul => self.airBinOp(inst, .mul), .mulwrap => self.airWrapBinOp(inst, .mul), @@ -1583,8 +1585,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .memcpy => self.airMemcpy(inst), - .add_sat, - .sub_sat, .mul_sat, .mod, .assembly, @@ -4878,3 +4878,64 @@ fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValu try self.addLabel(.local_set, result.local); return result; } + +fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { + assert(op == .add or op == .sub); + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const ty = self.air.typeOfIndex(inst); + const lhs_operand = try self.resolveInst(bin_op.lhs); + const rhs_operand = try self.resolveInst(bin_op.rhs); + + const int_info = ty.intInfo(self.target); + const is_signed = int_info.signedness == .signed; + + if (int_info.bits > 64) { + return self.fail("TODO: saturating arithmetic for integers with bitsize '{d}'", .{int_info.bits}); + } + + const wasm_bits = toWasmBits(int_info.bits).?; + + const lhs = if (is_signed) blk: { + break :blk try self.signAbsValue(lhs_operand, ty); + } else lhs_operand; + const rhs = if (is_signed) blk: { + break :blk try self.signAbsValue(rhs_operand, ty); + } else rhs_operand; + + const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, self.target) }); + try self.emitWValue(lhs); + try self.emitWValue(rhs); + try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); + const bin_result = try self.allocLocal(ty); + try self.addLabel(.local_set, bin_result.local); + + if (wasm_bits != int_info.bits and op == .add) { + const val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits)) - 1); + const imm_val = switch (wasm_bits) { + 32 => WValue{ .imm32 = @intCast(u32, val) }, + 64 => WValue{ .imm64 = val }, + else => unreachable, + }; + + const cmp_result = try self.cmp(bin_result, imm_val, ty, if (op == .add) .lt else .gt); + try self.emitWValue(bin_result); + try self.emitWValue(imm_val); + try self.emitWValue(cmp_result); + } else { + const cmp_result = try self.cmp(bin_result, lhs, ty, if (op == .add) .lt else .gt); + switch (wasm_bits) { + 32 => try self.addImm32(if (op == .add) @as(i32, -1) else 0), + 64 => try self.addImm64(if (op == .add) @bitCast(u64, @as(i64, -1)) else 0), + else => unreachable, + } + try self.emitWValue(bin_result); + try self.emitWValue(cmp_result); + } + + try self.addTag(.select); + const result = try self.allocLocal(ty); + try self.addLabel(.local_set, result.local); + return result; +} From ce5d934f5f713a0dbc8787d9ffe58b4962042f8f Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 15 Jun 2022 22:03:18 +0200 Subject: [PATCH 1873/2031] wasm: saturating add and sub for signed integers --- src/arch/wasm/CodeGen.zig | 85 ++++++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 18 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 2e1d88083c..0a93db7fb5 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -4885,8 +4885,8 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const ty = self.air.typeOfIndex(inst); - const lhs_operand = try self.resolveInst(bin_op.lhs); - const rhs_operand = try self.resolveInst(bin_op.rhs); + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); const int_info = ty.intInfo(self.target); const is_signed = int_info.signedness == .signed; @@ -4895,22 +4895,12 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { return self.fail("TODO: saturating arithmetic for integers with bitsize '{d}'", .{int_info.bits}); } + if (is_signed) { + return signedSat(self, lhs, rhs, ty, op); + } + const wasm_bits = toWasmBits(int_info.bits).?; - - const lhs = if (is_signed) blk: { - break :blk try self.signAbsValue(lhs_operand, ty); - } else lhs_operand; - const rhs = if (is_signed) blk: { - break :blk try self.signAbsValue(rhs_operand, ty); - } else rhs_operand; - - const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, self.target) }); - try self.emitWValue(lhs); - try self.emitWValue(rhs); - try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); - const bin_result = try self.allocLocal(ty); - try self.addLabel(.local_set, bin_result.local); - + const bin_result = try self.binOp(lhs, rhs, ty, op); if (wasm_bits != int_info.bits and op == .add) { const val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits)) - 1); const imm_val = switch (wasm_bits) { @@ -4919,7 +4909,7 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { else => unreachable, }; - const cmp_result = try self.cmp(bin_result, imm_val, ty, if (op == .add) .lt else .gt); + const cmp_result = try self.cmp(bin_result, imm_val, ty, .lt); try self.emitWValue(bin_result); try self.emitWValue(imm_val); try self.emitWValue(cmp_result); @@ -4939,3 +4929,62 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { try self.addLabel(.local_set, result.local); return result; } + +fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op: Op) InnerError!WValue { + const int_info = ty.intInfo(self.target); + const wasm_bits = toWasmBits(int_info.bits).?; + const is_wasm_bits = wasm_bits == int_info.bits; + + const lhs = if (!is_wasm_bits) try self.signAbsValue(lhs_operand, ty) else lhs_operand; + const rhs = if (!is_wasm_bits) try self.signAbsValue(rhs_operand, ty) else rhs_operand; + + const max_val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits - 1)) - 1); + const min_val = @intCast(i64, ~@intCast(u63, max_val)); + const max_wvalue = switch (wasm_bits) { + 32 => WValue{ .imm32 = @intCast(u32, max_val) }, + 64 => WValue{ .imm64 = max_val }, + else => unreachable, + }; + const min_wvalue = switch (wasm_bits) { + 32 => WValue{ .imm32 = @bitCast(u32, @truncate(i32, min_val)) }, + 64 => WValue{ .imm64 = @bitCast(u64, min_val) }, + else => unreachable, + }; + + const bin_result = try self.binOp(lhs, rhs, ty, op); + if (!is_wasm_bits) { + const cmp_result_lt = try self.cmp(bin_result, max_wvalue, ty, .lt); + try self.emitWValue(bin_result); + try self.emitWValue(max_wvalue); + try self.emitWValue(cmp_result_lt); + try self.addTag(.select); + try self.addLabel(.local_set, bin_result.local); // re-use local + + const cmp_result_gt = try self.cmp(bin_result, min_wvalue, ty, .gt); + try self.emitWValue(bin_result); + try self.emitWValue(min_wvalue); + try self.emitWValue(cmp_result_gt); + try self.addTag(.select); + try self.addLabel(.local_set, bin_result.local); // re-use local + return self.wrapOperand(bin_result, ty); + } else { + const zero = switch (wasm_bits) { + 32 => WValue{ .imm32 = 0 }, + 64 => WValue{ .imm64 = 0 }, + else => unreachable, + }; + const cmp_bin_result = try self.cmp(bin_result, lhs, ty, .lt); + const cmp_zero_result = try self.cmp(rhs, zero, ty, if (op == .add) .lt else .gt); + const xor = try self.binOp(cmp_zero_result, cmp_bin_result, ty, .xor); + const cmp_bin_zero_result = try self.cmp(bin_result, zero, ty, .lt); + try self.emitWValue(max_wvalue); + try self.emitWValue(min_wvalue); + try self.emitWValue(cmp_bin_zero_result); + try self.addTag(.select); + try self.emitWValue(bin_result); + try self.emitWValue(xor); + try self.addTag(.select); + try self.addLabel(.local_set, bin_result.local); // re-use local + return bin_result; + } +} From 53831442ef7e87c32501967f13dfb5fb9b0d0b0f Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 16 Jun 2022 20:22:14 +0200 Subject: [PATCH 1874/2031] wasm: saturating shift-left for unsigned integers --- src/arch/wasm/CodeGen.zig | 56 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 0a93db7fb5..1a0df89c96 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1454,6 +1454,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .rem => self.airBinOp(inst, .rem), .shl => self.airWrapBinOp(inst, .shl), .shl_exact => self.airBinOp(inst, .shl), + .shl_sat => self.airShlSat(inst), .shr, .shr_exact => self.airBinOp(inst, .shr), .xor => self.airBinOp(inst, .xor), .max => self.airMaxMin(inst, .max), @@ -1588,7 +1589,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .mul_sat, .mod, .assembly, - .shl_sat, .ret_addr, .frame_addr, .bit_reverse, @@ -4988,3 +4988,57 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op return bin_result; } } + +fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; + + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const ty = self.air.typeOfIndex(inst); + const int_info = ty.intInfo(self.target); + if (int_info.bits > 64) { + return self.fail("TODO: Saturating shifting left for integers with bitsize '{d}'", .{int_info.bits}); + } + + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + if (int_info.signedness == .signed) {} + const wasm_bits = toWasmBits(int_info.bits).?; + const result = try self.allocLocal(ty); + + if (wasm_bits == int_info.bits) { + const shl = try self.binOp(lhs, rhs, ty, .shl); + const shr = try self.binOp(shl, rhs, ty, .shr); + const cmp_result = try self.cmp(lhs, shr, ty, .neq); + if (wasm_bits == 32) + try self.addImm32(-1) + else + try self.addImm64(@bitCast(u64, @as(i64, -1))); + try self.emitWValue(shl); + try self.emitWValue(cmp_result); + try self.addTag(.select); + try self.addLabel(.local_set, result.local); + return result; + } else { + const shift_size = wasm_bits - int_info.bits; + const shift_value = switch (wasm_bits) { + 32 => WValue{ .imm32 = shift_size }, + 64 => WValue{ .imm64 = shift_size }, + else => unreachable, + }; + + const shl_res = try self.binOp(lhs, shift_value, ty, .shl); + const shl = try self.binOp(shl_res, rhs, ty, .shl); + const shr = try self.binOp(shl, rhs, ty, .shr); + + const cmp_result = try self.cmp(shl_res, shr, ty, .neq); + if (wasm_bits == 32) + try self.addImm32(-1) + else + try self.addImm64(@bitCast(u64, @as(i64, -1))); + try self.emitWValue(shl); + try self.emitWValue(cmp_result); + try self.addTag(.select); + try self.addLabel(.local_set, result.local); + return self.binOp(result, shift_value, ty, .shr); + } +} From b4f2c0dcb94fb719ebdeeb9d5d7b27811be9a156 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 19 Jun 2022 15:12:05 +0200 Subject: [PATCH 1875/2031] compiler-rt: fix logic for choosing `__gnu_{f2h,h2f}_ieee` Similar to wasm32-wasi-musl, Apple targets also want standard symbol names. --- lib/compiler_rt/common.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/compiler_rt/common.zig b/lib/compiler_rt/common.zig index 8f9253b44b..b6e4a5d311 100644 --- a/lib/compiler_rt/common.zig +++ b/lib/compiler_rt/common.zig @@ -34,7 +34,7 @@ pub const want_ppc_abi = builtin.cpu.arch.isPPC() or builtin.cpu.arch.isPPC64(); /// wasm32-freestanding-none => false /// x86_64-windows-gnu => true /// x86_64-windows-msvc => true -/// any-macos-any => doesn't matter; libSystem has both symbol flavors +/// any-macos-any => false pub const gnu_f16_abi = switch (builtin.cpu.arch) { .wasm32, .wasm64 => false, @@ -43,7 +43,7 @@ pub const gnu_f16_abi = switch (builtin.cpu.arch) { else => true, }, - else => true, + else => !builtin.os.tag.isDarwin(), }; pub const want_sparc_abi = builtin.cpu.arch.isSPARC(); From 05600a6d8438595bf7fc40ea21d0c47076d2935d Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 19 Jun 2022 15:50:03 +0200 Subject: [PATCH 1876/2031] wasm: saturating shift-left for signed integers --- src/arch/wasm/CodeGen.zig | 65 +++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 1a0df89c96..ac604957b6 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -4995,13 +4995,13 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const ty = self.air.typeOfIndex(inst); const int_info = ty.intInfo(self.target); + const is_signed = int_info.signedness == .signed; if (int_info.bits > 64) { return self.fail("TODO: Saturating shifting left for integers with bitsize '{d}'", .{int_info.bits}); } const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - if (int_info.signedness == .signed) {} const wasm_bits = toWasmBits(int_info.bits).?; const result = try self.allocLocal(ty); @@ -5009,10 +5009,32 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const shl = try self.binOp(lhs, rhs, ty, .shl); const shr = try self.binOp(shl, rhs, ty, .shr); const cmp_result = try self.cmp(lhs, shr, ty, .neq); - if (wasm_bits == 32) - try self.addImm32(-1) - else - try self.addImm64(@bitCast(u64, @as(i64, -1))); + + switch (wasm_bits) { + 32 => blk: { + if (!is_signed) { + try self.addImm32(-1); + break :blk; + } + const less_than_zero = try self.cmp(lhs, .{ .imm32 = 0 }, ty, .lt); + try self.addImm32(std.math.minInt(i32)); + try self.addImm32(std.math.maxInt(i32)); + try self.emitWValue(less_than_zero); + try self.addTag(.select); + }, + 64 => blk: { + if (!is_signed) { + try self.addImm64(@bitCast(u64, @as(i64, -1))); + break :blk; + } + const less_than_zero = try self.cmp(lhs, .{ .imm64 = 0 }, ty, .lt); + try self.addImm64(@bitCast(u64, @as(i64, std.math.minInt(i64)))); + try self.addImm64(@bitCast(u64, @as(i64, std.math.maxInt(i64)))); + try self.emitWValue(less_than_zero); + try self.addTag(.select); + }, + else => unreachable, + } try self.emitWValue(shl); try self.emitWValue(cmp_result); try self.addTag(.select); @@ -5029,12 +5051,35 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const shl_res = try self.binOp(lhs, shift_value, ty, .shl); const shl = try self.binOp(shl_res, rhs, ty, .shl); const shr = try self.binOp(shl, rhs, ty, .shr); - const cmp_result = try self.cmp(shl_res, shr, ty, .neq); - if (wasm_bits == 32) - try self.addImm32(-1) - else - try self.addImm64(@bitCast(u64, @as(i64, -1))); + + switch (wasm_bits) { + 32 => blk: { + if (!is_signed) { + try self.addImm32(-1); + break :blk; + } + + const less_than_zero = try self.cmp(shl_res, .{ .imm32 = 0 }, ty, .lt); + try self.addImm32(std.math.minInt(i32)); + try self.addImm32(std.math.maxInt(i32)); + try self.emitWValue(less_than_zero); + try self.addTag(.select); + }, + 64 => blk: { + if (!is_signed) { + try self.addImm64(@bitCast(u64, @as(i64, -1))); + break :blk; + } + + const less_than_zero = try self.cmp(shl_res, .{ .imm64 = 0 }, ty, .lt); + try self.addImm64(@bitCast(u64, @as(i64, std.math.minInt(i64)))); + try self.addImm64(@bitCast(u64, @as(i64, std.math.maxInt(i64)))); + try self.emitWValue(less_than_zero); + try self.addTag(.select); + }, + else => unreachable, + } try self.emitWValue(shl); try self.emitWValue(cmp_result); try self.addTag(.select); From a50147bfff1224d090b13ed934eda97dbfd6432b Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 19 Jun 2022 17:26:44 +0200 Subject: [PATCH 1877/2031] wasm: fixes for signed saturation --- src/arch/wasm/CodeGen.zig | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index ac604957b6..f2710574d7 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -4939,9 +4939,9 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op const rhs = if (!is_wasm_bits) try self.signAbsValue(rhs_operand, ty) else rhs_operand; const max_val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits - 1)) - 1); - const min_val = @intCast(i64, ~@intCast(u63, max_val)); + const min_val: i64 = (-@intCast(i64, @intCast(u63, max_val))) - 1; const max_wvalue = switch (wasm_bits) { - 32 => WValue{ .imm32 = @intCast(u32, max_val) }, + 32 => WValue{ .imm32 = @truncate(u32, max_val) }, 64 => WValue{ .imm64 = max_val }, else => unreachable, }; @@ -4975,7 +4975,7 @@ fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op }; const cmp_bin_result = try self.cmp(bin_result, lhs, ty, .lt); const cmp_zero_result = try self.cmp(rhs, zero, ty, if (op == .add) .lt else .gt); - const xor = try self.binOp(cmp_zero_result, cmp_bin_result, ty, .xor); + const xor = try self.binOp(cmp_zero_result, cmp_bin_result, Type.u32, .xor); // comparisons always return i32, so provide u32 as type to xor. const cmp_bin_zero_result = try self.cmp(bin_result, zero, ty, .lt); try self.emitWValue(max_wvalue); try self.emitWValue(min_wvalue); @@ -5084,6 +5084,10 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.emitWValue(cmp_result); try self.addTag(.select); try self.addLabel(.local_set, result.local); - return self.binOp(result, shift_value, ty, .shr); + const shift_result = try self.binOp(result, shift_value, ty, .shr); + if (is_signed) { + return self.wrapOperand(shift_result, ty); + } + return shift_result; } } From 4957c7cbbd5ffa2c44970cc34a9274ae42f0f3fb Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 19 Jun 2022 17:27:29 +0200 Subject: [PATCH 1878/2031] wasm: enable passing behavior tests This also splits the test cases for addition and subtraction as the wasm backend does not yet provide support for 128bit saturating arithmetic. --- test/behavior/saturating_arithmetic.zig | 62 +++++++++++++++++++++---- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/test/behavior/saturating_arithmetic.zig b/test/behavior/saturating_arithmetic.zig index 33266f711e..1790fe4505 100644 --- a/test/behavior/saturating_arithmetic.zig +++ b/test/behavior/saturating_arithmetic.zig @@ -5,7 +5,6 @@ const maxInt = std.math.maxInt; const expect = std.testing.expect; test "saturating add" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -20,8 +19,6 @@ test "saturating add" { try testSatAdd(i2, 1, -1, 0); try testSatAdd(i2, -1, -1, -2); try testSatAdd(i64, maxInt(i64), 1, maxInt(i64)); - try testSatAdd(i128, maxInt(i128), -maxInt(i128), 0); - try testSatAdd(i128, minInt(i128), maxInt(i128), -1); try testSatAdd(i8, 127, 127, 127); try testSatAdd(u2, 0, 0, 0); try testSatAdd(u2, 0, 1, 1); @@ -29,7 +26,6 @@ test "saturating add" { try testSatAdd(u8, 255, 255, 255); try testSatAdd(u2, 3, 2, 3); try testSatAdd(u3, 7, 1, 7); - try testSatAdd(u128, maxInt(u128), 1, maxInt(u128)); } fn testSatAdd(comptime T: type, lhs: T, rhs: T, expected: T) !void { @@ -54,12 +50,36 @@ test "saturating add" { comptime try S.testSatAdd(comptime_int, 7, -593423721213448152027139550640105366508, -593423721213448152027139550640105366501); } -test "saturating subtraction" { +test "saturating add 128bit" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + const S = struct { + fn doTheTest() !void { + try testSatAdd(i128, maxInt(i128), -maxInt(i128), 0); + try testSatAdd(i128, minInt(i128), maxInt(i128), -1); + try testSatAdd(u128, maxInt(u128), 1, maxInt(u128)); + } + fn testSatAdd(comptime T: type, lhs: T, rhs: T, expected: T) !void { + try expect((lhs +| rhs) == expected); + + var x = lhs; + x +|= rhs; + try expect(x == expected); + } + }; + + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "saturating subtraction" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -71,14 +91,11 @@ test "saturating subtraction" { try testSatSub(i2, 1, -1, 1); try testSatSub(i2, -2, -2, 0); try testSatSub(i64, minInt(i64), 1, minInt(i64)); - try testSatSub(i128, maxInt(i128), -1, maxInt(i128)); - try testSatSub(i128, minInt(i128), -maxInt(i128), -1); try testSatSub(u2, 0, 0, 0); try testSatSub(u2, 0, 1, 0); try testSatSub(u5, 0, 31, 0); try testSatSub(u8, 10, 3, 7); try testSatSub(u8, 0, 255, 0); - try testSatSub(u128, 0, maxInt(u128), 0); } fn testSatSub(comptime T: type, lhs: T, rhs: T, expected: T) !void { @@ -103,6 +120,33 @@ test "saturating subtraction" { comptime try S.testSatSub(comptime_int, 7, -593423721213448152027139550640105366508, 593423721213448152027139550640105366515); } +test "saturating subtraction 128bit" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + try testSatSub(i128, maxInt(i128), -1, maxInt(i128)); + try testSatSub(i128, minInt(i128), -maxInt(i128), -1); + try testSatSub(u128, 0, maxInt(u128), 0); + } + + fn testSatSub(comptime T: type, lhs: T, rhs: T, expected: T) !void { + try expect((lhs -| rhs) == expected); + + var x = lhs; + x -|= rhs; + try expect(x == expected); + } + }; + + try S.doTheTest(); + comptime try S.doTheTest(); +} + test "saturating multiplication" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -153,7 +197,6 @@ test "saturating multiplication" { } test "saturating shift-left" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -193,7 +236,6 @@ test "saturating shift-left" { } test "saturating shl uses the LHS type" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO From 1d4dbf8d3c891346e6dc978764e8bce9c85ad044 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 19 Jun 2022 21:08:53 +0200 Subject: [PATCH 1879/2031] macos: run test-cases single threaded in the CI --- ci/azure/macos_script | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/azure/macos_script b/ci/azure/macos_script index 9e32e7803e..e958dc28de 100755 --- a/ci/azure/macos_script +++ b/ci/azure/macos_script @@ -75,7 +75,7 @@ release/bin/zig build test-translate-c -Denable-macos-sdk release/bin/zig build test-run-translated-c -Denable-macos-sdk release/bin/zig build docs -Denable-macos-sdk release/bin/zig build test-fmt -Denable-macos-sdk -release/bin/zig build test-cases -Denable-macos-sdk +release/bin/zig build test-cases -Denable-macos-sdk -Dsingle-threaded if [ "${BUILD_REASON}" != "PullRequest" ]; then mv ../LICENSE release/ From 98138ba78c1540830a4fdb0537ed6ad5c7b57e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Thu, 16 Jun 2022 15:42:35 +0300 Subject: [PATCH 1880/2031] [MachO] add -pagezero_size Pass `-pagezero_size` to the MachO linker. This is the final "unsupported linker arg" that I could chase that CGo uses. After this and #11874 we may be able to fail on an "unsupported linker arg" instead of emiting a warning. Test case: zig=/code/zig/build/zig CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 CC="$zig cc -target x86_64-macos" CXX="$zig c++ -target x86_64-macos" go build -a -ldflags "-s -w" cgo.go I compiled a trivial CGo program and executed it on an amd64 Darwin host. To be honest, I am not entirely sure what this is doing. This feels right after reading what this argument does in LLVM sources, but I am by no means qualified to make MachO pull requests. Will take feedback. --- src/Compilation.zig | 7 +++++-- src/link.zig | 3 +++ src/link/Coff.zig | 2 +- src/link/Elf.zig | 2 +- src/link/MachO.zig | 11 ++++++----- src/link/Wasm.zig | 2 +- src/main.zig | 12 ++++++++++++ 7 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 2646da2f6f..98d69ee78e 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -903,6 +903,8 @@ pub const InitOptions = struct { install_name: ?[]const u8 = null, /// (Darwin) Path to entitlements file entitlements: ?[]const u8 = null, + /// (Darwin) size of the __PAGEZERO segment + pagezero_size: ?u64 = null, }; fn addPackageTableToCacheHash( @@ -2359,7 +2361,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo /// to remind the programmer to update multiple related pieces of code that /// are in different locations. Bump this number when adding or deleting /// anything from the link cache manifest. -pub const link_hash_implementation_version = 3; +pub const link_hash_implementation_version = 4; fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void { const gpa = comp.gpa; @@ -2369,7 +2371,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - comptime assert(link_hash_implementation_version == 3); + comptime assert(link_hash_implementation_version == 4); if (comp.bin_file.options.module) |mod| { const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{ @@ -2474,6 +2476,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes man.hash.addListOfBytes(comp.bin_file.options.framework_dirs); man.hash.addListOfBytes(comp.bin_file.options.frameworks); try man.addOptionalFile(comp.bin_file.options.entitlements); + if (comp.bin_file.options.pagezero_size) |value| man.hash.add(value); // COFF specific stuff man.hash.addOptional(comp.bin_file.options.subsystem); diff --git a/src/link.zig b/src/link.zig index 65e9de8ca3..ad1fc417f3 100644 --- a/src/link.zig +++ b/src/link.zig @@ -187,6 +187,9 @@ pub const Options = struct { /// (Darwin) Path to entitlements file entitlements: ?[]const u8 = null, + /// (Darwin) size of the __PAGEZERO segment + pagezero_size: ?u64 = null, + pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { return if (options.use_lld) .Obj else options.output_mode; } diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 2943cae36a..6b4ff26bce 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -969,7 +969,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) ! man = comp.cache_parent.obtain(); self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 3); + comptime assert(Compilation.link_hash_implementation_version == 4); for (self.base.options.objects) |obj| { _ = try man.addFile(obj.path, null); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index a0e40e5682..0bcde06f33 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1298,7 +1298,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // We are about to obtain this lock, so here we give other processes a chance first. self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 3); + comptime assert(Compilation.link_hash_implementation_version == 4); try man.addOptionalFile(self.base.options.linker_script); try man.addOptionalFile(self.base.options.version_script); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index c71007157a..ef56cbe548 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -286,8 +286,8 @@ const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld"; const minimum_text_block_size = 64; pub const min_text_capacity = padToIdeal(minimum_text_block_size); -/// Virtual memory offset corresponds to the size of __PAGEZERO segment and start of -/// __TEXT segment. +/// Virtual memory offset corresponds to the size of __PAGEZERO segment and +/// start of __TEXT segment. const pagezero_vmsize: u64 = 0x100000000; pub const Export = struct { @@ -536,7 +536,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No // We are about to obtain this lock, so here we give other processes a chance first. self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 3); + comptime assert(Compilation.link_hash_implementation_version == 4); for (self.base.options.objects) |obj| { _ = try man.addFile(obj.path, null); @@ -549,6 +549,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No // We can skip hashing libc and libc++ components that we are in charge of building from Zig // installation sources because they are always a product of the compiler version + target information. man.hash.add(stack_size); + if (self.base.options.pagezero_size) |value| man.hash.add(value); man.hash.addListOfBytes(self.base.options.lib_dirs); man.hash.addListOfBytes(self.base.options.framework_dirs); man.hash.addListOfBytes(self.base.options.frameworks); @@ -4372,7 +4373,7 @@ fn populateMissingMetadata(self: *MachO) !void { .segment = .{ .inner = .{ .segname = makeStaticString("__PAGEZERO"), - .vmsize = pagezero_vmsize, + .vmsize = self.base.options.pagezero_size orelse pagezero_vmsize, .cmdsize = @sizeOf(macho.segment_command_64), }, }, @@ -4394,7 +4395,7 @@ fn populateMissingMetadata(self: *MachO) !void { .segment = .{ .inner = .{ .segname = makeStaticString("__TEXT"), - .vmaddr = pagezero_vmsize, + .vmaddr = self.base.options.pagezero_size orelse pagezero_vmsize, .vmsize = needed_size, .filesize = needed_size, .maxprot = macho.PROT.READ | macho.PROT.EXEC, diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 5a910e188b..cff47ccdf5 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2274,7 +2274,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! // We are about to obtain this lock, so here we give other processes a chance first. self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 3); + comptime assert(Compilation.link_hash_implementation_version == 4); for (self.base.options.objects) |obj| { _ = try man.addFile(obj.path, null); diff --git a/src/main.zig b/src/main.zig index cfd802d11c..bfd711fff7 100644 --- a/src/main.zig +++ b/src/main.zig @@ -447,6 +447,7 @@ const usage_build_generic = \\ -F[dir] (Darwin) add search path for frameworks \\ -install_name=[value] (Darwin) add dylib's install name \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature + \\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment \\ --import-memory (WebAssembly) import memory from the environment \\ --import-table (WebAssembly) import function table from the host environment \\ --export-table (WebAssembly) export function table to the host environment @@ -694,6 +695,7 @@ fn buildOutputType( var install_name: ?[]const u8 = null; var hash_style: link.HashStyle = .both; var entitlements: ?[]const u8 = null; + var pagezero_size: ?u64 = null; // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names. // This array is populated by zig cc frontend and then has to be converted to zig-style @@ -1647,6 +1649,15 @@ fn buildOutputType( linker_optimization = std.fmt.parseUnsigned(u8, arg["-O".len..], 10) catch |err| { fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; + } else if (mem.eql(u8, arg, "-pagezero_size")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{s}'", .{arg}); + } + const next_arg = linker_args.items[i]; + pagezero_size = std.fmt.parseUnsigned(u64, next_arg, 0) catch |err| { + fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); + }; } else if (mem.eql(u8, arg, "--gc-sections")) { linker_gc_sections = true; } else if (mem.eql(u8, arg, "--no-gc-sections")) { @@ -2763,6 +2774,7 @@ fn buildOutputType( .native_darwin_sdk = native_darwin_sdk, .install_name = install_name, .entitlements = entitlements, + .pagezero_size = pagezero_size, }) catch |err| switch (err) { error.LibCUnavailable => { const target = target_info.target; From ea9b7a0626c0191aa513549773c4d3b04e04de7c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 20 Jun 2022 10:13:39 +0200 Subject: [PATCH 1881/2031] macho: round down pagezero size to page size If page aligned requested pagezero size is 0, skip generating __PAGEZERO segment. Add misc improvements to the pipeline, and correctly transfer the requested __PAGEZERO size to the linker. --- src/Compilation.zig | 3 ++- src/link/MachO.zig | 24 +++++++++++++++++------- src/main.zig | 7 +++++++ 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 98d69ee78e..525835ce61 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1744,6 +1744,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .native_darwin_sdk = options.native_darwin_sdk, .install_name = options.install_name, .entitlements = options.entitlements, + .pagezero_size = options.pagezero_size, }); errdefer bin_file.destroy(); comp.* = .{ @@ -2476,7 +2477,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes man.hash.addListOfBytes(comp.bin_file.options.framework_dirs); man.hash.addListOfBytes(comp.bin_file.options.frameworks); try man.addOptionalFile(comp.bin_file.options.entitlements); - if (comp.bin_file.options.pagezero_size) |value| man.hash.add(value); + man.hash.addOptional(comp.bin_file.options.pagezero_size); // COFF specific stuff man.hash.addOptional(comp.bin_file.options.subsystem); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index ef56cbe548..b19b850d80 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -286,9 +286,9 @@ const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld"; const minimum_text_block_size = 64; pub const min_text_capacity = padToIdeal(minimum_text_block_size); -/// Virtual memory offset corresponds to the size of __PAGEZERO segment and +/// Default virtual memory offset corresponds to the size of __PAGEZERO segment and /// start of __TEXT segment. -const pagezero_vmsize: u64 = 0x100000000; +const default_pagezero_vmsize: u64 = 0x100000000; pub const Export = struct { sym_index: ?u32 = null, @@ -549,7 +549,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No // We can skip hashing libc and libc++ components that we are in charge of building from Zig // installation sources because they are always a product of the compiler version + target information. man.hash.add(stack_size); - if (self.base.options.pagezero_size) |value| man.hash.add(value); + man.hash.addOptional(self.base.options.pagezero_size); man.hash.addListOfBytes(self.base.options.lib_dirs); man.hash.addListOfBytes(self.base.options.framework_dirs); man.hash.addListOfBytes(self.base.options.frameworks); @@ -4366,14 +4366,21 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil fn populateMissingMetadata(self: *MachO) !void { const cpu_arch = self.base.options.target.cpu.arch; + const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize; + const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size); - if (self.pagezero_segment_cmd_index == null) { + if (self.pagezero_segment_cmd_index == null) blk: { + if (aligned_pagezero_vmsize == 0) break :blk; + if (aligned_pagezero_vmsize != pagezero_vmsize) { + log.warn("requested __PAGEZERO size is not page aligned", .{}); + log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize}); + } self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); try self.load_commands.append(self.base.allocator, .{ .segment = .{ .inner = .{ .segname = makeStaticString("__PAGEZERO"), - .vmsize = self.base.options.pagezero_size orelse pagezero_vmsize, + .vmsize = aligned_pagezero_vmsize, .cmdsize = @sizeOf(macho.segment_command_64), }, }, @@ -4395,7 +4402,7 @@ fn populateMissingMetadata(self: *MachO) !void { .segment = .{ .inner = .{ .segname = makeStaticString("__TEXT"), - .vmaddr = self.base.options.pagezero_size orelse pagezero_vmsize, + .vmaddr = aligned_pagezero_vmsize, .vmsize = needed_size, .filesize = needed_size, .maxprot = macho.PROT.READ | macho.PROT.EXEC, @@ -4882,7 +4889,10 @@ fn populateMissingMetadata(self: *MachO) !void { fn allocateTextSegment(self: *MachO) !void { const seg = &self.load_commands.items[self.text_segment_cmd_index.?].segment; - const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].segment.inner.vmsize; + const base_vmaddr = if (self.pagezero_segment_cmd_index) |index| + self.load_commands.items[index].segment.inner.vmsize + else + 0; seg.inner.fileoff = 0; seg.inner.vmaddr = base_vmaddr; diff --git a/src/main.zig b/src/main.zig index bfd711fff7..cbba4a33b3 100644 --- a/src/main.zig +++ b/src/main.zig @@ -910,6 +910,13 @@ fn buildOutputType( install_name = args_iter.next() orelse { fatal("expected parameter after {s}", .{arg}); }; + } else if (mem.eql(u8, arg, "-pagezero_size")) { + const next_arg = args_iter.next() orelse { + fatal("expected parameter after {s}", .{arg}); + }; + pagezero_size = std.fmt.parseUnsigned(u64, next_arg, 0) catch |err| { + fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); + }; } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) { linker_script = args_iter.next() orelse { fatal("expected parameter after {s}", .{arg}); From 753e2b86396abb6bb614bff25b18d8fd82a809d8 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 20 Jun 2022 13:52:39 +0200 Subject: [PATCH 1882/2031] macho: verbose print pagezero size if specified --- src/link/MachO.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index b19b850d80..605b60cc76 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -929,6 +929,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try argv.append(rpath); } + if (self.base.options.pagezero_size) |pagezero_size| { + try argv.append("-pagezero_size"); + try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size})); + } + try argv.appendSlice(positionals.items); try argv.append("-o"); From 38a1222c875a3d767c77d4e98bc2293ebbfc0487 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 20 Jun 2022 15:11:22 +0300 Subject: [PATCH 1883/2031] std.crypto: fix invalid pass by value --- lib/std/crypto/argon2.zig | 2 -- lib/std/crypto/bcrypt.zig | 1 - lib/std/crypto/phc_encoding.zig | 5 ++--- lib/std/crypto/scrypt.zig | 5 ++--- src/codegen/c.zig | 3 --- 5 files changed, 4 insertions(+), 12 deletions(-) diff --git a/lib/std/crypto/argon2.zig b/lib/std/crypto/argon2.zig index 16c20a4a78..7269470d5f 100644 --- a/lib/std/crypto/argon2.zig +++ b/lib/std/crypto/argon2.zig @@ -897,7 +897,6 @@ test "kdf" { } test "phc format hasher" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const allocator = std.testing.allocator; const password = "testpass"; @@ -913,7 +912,6 @@ test "phc format hasher" { } test "password hash and password verify" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const allocator = std.testing.allocator; const password = "testpass"; diff --git a/lib/std/crypto/bcrypt.zig b/lib/std/crypto/bcrypt.zig index d5e9fddaf1..d7a0e0fb80 100644 --- a/lib/std/crypto/bcrypt.zig +++ b/lib/std/crypto/bcrypt.zig @@ -802,7 +802,6 @@ test "bcrypt crypt format" { } test "bcrypt phc format" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const hash_options = HashOptions{ .params = .{ .rounds_log = 5 }, .encoding = .phc, diff --git a/lib/std/crypto/phc_encoding.zig b/lib/std/crypto/phc_encoding.zig index 40074b2c6e..69324dc5b3 100644 --- a/lib/std/crypto/phc_encoding.zig +++ b/lib/std/crypto/phc_encoding.zig @@ -41,7 +41,7 @@ pub fn BinValue(comptime max_len: usize) type { } /// Return the slice containing the actual value. - pub fn constSlice(self: Self) []const u8 { + pub fn constSlice(self: *const Self) []const u8 { return self.buf[0..self.len]; } @@ -52,7 +52,7 @@ pub fn BinValue(comptime max_len: usize) type { self.len = len; } - fn toB64(self: Self, buf: []u8) ![]const u8 { + fn toB64(self: *const Self, buf: []u8) ![]const u8 { const value = self.constSlice(); const len = B64Encoder.calcSize(value.len); if (len > buf.len) return Error.NoSpaceLeft; @@ -260,7 +260,6 @@ fn kvSplit(str: []const u8) !struct { key: []const u8, value: []const u8 } { } test "phc format - encoding/decoding" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const Input = struct { str: []const u8, HashResult: type, diff --git a/lib/std/crypto/scrypt.zig b/lib/std/crypto/scrypt.zig index 11441df161..35049ebc51 100644 --- a/lib/std/crypto/scrypt.zig +++ b/lib/std/crypto/scrypt.zig @@ -248,7 +248,7 @@ const crypt_format = struct { } /// Return the slice containing the actual value. - pub fn constSlice(self: Self) []const u8 { + pub fn constSlice(self: *const Self) []const u8 { return self.buf[0..self.len]; } @@ -259,7 +259,7 @@ const crypt_format = struct { self.len = len; } - fn toB64(self: Self, buf: []u8) ![]const u8 { + fn toB64(self: *const Self, buf: []u8) ![]const u8 { const value = self.constSlice(); const len = Codec.encodedLen(value.len); if (len > buf.len) return EncodingError.NoSpaceLeft; @@ -683,7 +683,6 @@ test "unix-scrypt" { } test "crypt format" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const str = "$7$C6..../....SodiumChloride$kBGj9fHznVYFQMEn/qDCfrDevf9YDtcDdKvEqHJLV8D"; const params = try crypt_format.deserialize(crypt_format.HashResult(32), str); var buf: [str.len]u8 = undefined; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 949e2a67f2..54c0dfb3c7 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -225,9 +225,6 @@ fn formatIdent( } pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) { - if (builtin.zig_backend != .stage1) { - @panic("TODO"); - } return .{ .data = ident }; } From 8752db3285060c6f2d85c0fa744c44094239a792 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 20 Jun 2022 14:26:57 +0200 Subject: [PATCH 1884/2031] macho: -pagezero_size is always in hex This matches the behavior of other linkers out there including `ld64` and `lld`. --- src/link/MachO.zig | 2 +- src/main.zig | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 605b60cc76..aea44d9357 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -4377,7 +4377,7 @@ fn populateMissingMetadata(self: *MachO) !void { if (self.pagezero_segment_cmd_index == null) blk: { if (aligned_pagezero_vmsize == 0) break :blk; if (aligned_pagezero_vmsize != pagezero_vmsize) { - log.warn("requested __PAGEZERO size is not page aligned", .{}); + log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize}); log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize}); } self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); diff --git a/src/main.zig b/src/main.zig index cbba4a33b3..6942d0ba49 100644 --- a/src/main.zig +++ b/src/main.zig @@ -447,7 +447,7 @@ const usage_build_generic = \\ -F[dir] (Darwin) add search path for frameworks \\ -install_name=[value] (Darwin) add dylib's install name \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature - \\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment + \\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment in hexadecimal notation \\ --import-memory (WebAssembly) import memory from the environment \\ --import-table (WebAssembly) import function table from the host environment \\ --export-table (WebAssembly) export function table to the host environment @@ -914,7 +914,7 @@ fn buildOutputType( const next_arg = args_iter.next() orelse { fatal("expected parameter after {s}", .{arg}); }; - pagezero_size = std.fmt.parseUnsigned(u64, next_arg, 0) catch |err| { + pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| { fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) { @@ -1662,7 +1662,7 @@ fn buildOutputType( fatal("expected linker arg after '{s}'", .{arg}); } const next_arg = linker_args.items[i]; - pagezero_size = std.fmt.parseUnsigned(u64, next_arg, 0) catch |err| { + pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| { fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; } else if (mem.eql(u8, arg, "--gc-sections")) { @@ -5067,6 +5067,18 @@ pub fn cmdChangelist( try bw.flush(); } +fn eatIntPrefix(arg: []const u8, radix: u8) []const u8 { + if (arg.len > 2 and arg[0] == '0') { + switch (std.ascii.toLower(arg[1])) { + 'b' => if (radix == 2) return arg[2..], + 'o' => if (radix == 8) return arg[2..], + 'x' => if (radix == 16) return arg[2..], + else => {}, + } + } + return arg; +} + fn parseIntSuffix(arg: []const u8, prefix_len: usize) u64 { return std.fmt.parseUnsigned(u64, arg[prefix_len..], 0) catch |err| { fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); From d0d5052b398b81d8907ea7c16ba38cd14d1c8d04 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 20 Jun 2022 16:12:40 +0300 Subject: [PATCH 1885/2031] std.fmt: update test to stage2 fn pointer semantics --- lib/std/fmt.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 449cc6a919..b49a954800 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -2117,18 +2117,18 @@ test "escape non-printable" { } test "pointer" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; { const value = @intToPtr(*align(1) i32, 0xdeadbeef); try expectFmt("pointer: i32@deadbeef\n", "pointer: {}\n", .{value}); try expectFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", .{value}); } - if (builtin.zig_backend != .stage1) return error.SkipZigTest; { - const value = @intToPtr(fn () void, 0xdeadbeef); + const value = @intToPtr(*const fn () void, 0xdeadbeef); try expectFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); } { - const value = @intToPtr(fn () void, 0xdeadbeef); + const value = @intToPtr(*const fn () void, 0xdeadbeef); try expectFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); } } From 38edef35bfcba5789ea50adc7c76dec504079812 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 28 May 2022 11:44:53 +0200 Subject: [PATCH 1886/2031] test: introduce link(er) tests - builds on standalone tests --- build.zig | 1 + lib/std/build/RunStep.zig | 1 + test/link.zig | 52 +++++++++++++++++++ test/link/bss/build.zig | 14 +++++ test/link/bss/main.zig | 13 +++++ .../common_symbols}/a.c | 0 .../common_symbols}/b.c | 0 .../common_symbols}/build.zig | 0 .../common_symbols}/c.c | 0 .../common_symbols}/main.zig | 0 .../common_symbols_alignment}/a.c | 0 .../common_symbols_alignment}/build.zig | 0 .../common_symbols_alignment}/main.zig | 0 test/link/dylib/a.c | 7 +++ test/link/dylib/build.zig | 29 +++++++++++ test/link/dylib/main.c | 9 ++++ .../frameworks}/build.zig | 18 +------ .../frameworks}/main.c | 0 .../interdependent_static_c_libs}/a.c | 0 .../interdependent_static_c_libs}/a.h | 0 .../interdependent_static_c_libs}/b.c | 0 .../interdependent_static_c_libs}/b.h | 0 .../interdependent_static_c_libs}/build.zig | 0 .../interdependent_static_c_libs}/main.zig | 0 test/{standalone => link}/objc/Foo.h | 0 test/{standalone => link}/objc/Foo.m | 0 test/{standalone => link}/objc/build.zig | 18 +------ test/{standalone => link}/objc/test.m | 0 test/{standalone => link}/objcpp/Foo.h | 0 test/{standalone => link}/objcpp/Foo.mm | 0 test/{standalone => link}/objcpp/build.zig | 20 ++----- test/{standalone => link}/objcpp/test.mm | 0 .../static_lib_as_system_lib}/a.c | 0 .../static_lib_as_system_lib}/a.h | 0 .../static_lib_as_system_lib}/build.zig | 0 .../static_lib_as_system_lib}/main.zig | 0 test/link/tls/a.c | 5 ++ .../tls}/build.zig | 2 + test/link/tls/main.zig | 15 ++++++ test/standalone.zig | 37 ++----------- test/standalone/link_import_tls_dylib/a.c | 1 - .../standalone/link_import_tls_dylib/main.zig | 7 --- test/tests.zig | 22 ++++++++ 43 files changed, 182 insertions(+), 89 deletions(-) create mode 100644 test/link.zig create mode 100644 test/link/bss/build.zig create mode 100644 test/link/bss/main.zig rename test/{standalone/link_common_symbols => link/common_symbols}/a.c (100%) rename test/{standalone/link_common_symbols => link/common_symbols}/b.c (100%) rename test/{standalone/link_common_symbols => link/common_symbols}/build.zig (100%) rename test/{standalone/link_common_symbols => link/common_symbols}/c.c (100%) rename test/{standalone/link_common_symbols => link/common_symbols}/main.zig (100%) rename test/{standalone/link_common_symbols_alignment => link/common_symbols_alignment}/a.c (100%) rename test/{standalone/link_common_symbols_alignment => link/common_symbols_alignment}/build.zig (100%) rename test/{standalone/link_common_symbols_alignment => link/common_symbols_alignment}/main.zig (100%) create mode 100644 test/link/dylib/a.c create mode 100644 test/link/dylib/build.zig create mode 100644 test/link/dylib/main.c rename test/{standalone/link_frameworks => link/frameworks}/build.zig (50%) rename test/{standalone/link_frameworks => link/frameworks}/main.c (100%) rename test/{standalone/link_interdependent_static_c_libs => link/interdependent_static_c_libs}/a.c (100%) rename test/{standalone/link_interdependent_static_c_libs => link/interdependent_static_c_libs}/a.h (100%) rename test/{standalone/link_interdependent_static_c_libs => link/interdependent_static_c_libs}/b.c (100%) rename test/{standalone/link_interdependent_static_c_libs => link/interdependent_static_c_libs}/b.h (100%) rename test/{standalone/link_interdependent_static_c_libs => link/interdependent_static_c_libs}/build.zig (100%) rename test/{standalone/link_interdependent_static_c_libs => link/interdependent_static_c_libs}/main.zig (100%) rename test/{standalone => link}/objc/Foo.h (100%) rename test/{standalone => link}/objc/Foo.m (100%) rename test/{standalone => link}/objc/build.zig (54%) rename test/{standalone => link}/objc/test.m (100%) rename test/{standalone => link}/objcpp/Foo.h (100%) rename test/{standalone => link}/objcpp/Foo.mm (100%) rename test/{standalone => link}/objcpp/build.zig (54%) rename test/{standalone => link}/objcpp/test.mm (100%) rename test/{standalone/link_static_lib_as_system_lib => link/static_lib_as_system_lib}/a.c (100%) rename test/{standalone/link_static_lib_as_system_lib => link/static_lib_as_system_lib}/a.h (100%) rename test/{standalone/link_static_lib_as_system_lib => link/static_lib_as_system_lib}/build.zig (100%) rename test/{standalone/link_static_lib_as_system_lib => link/static_lib_as_system_lib}/main.zig (100%) create mode 100644 test/link/tls/a.c rename test/{standalone/link_import_tls_dylib => link/tls}/build.zig (91%) create mode 100644 test/link/tls/main.zig delete mode 100644 test/standalone/link_import_tls_dylib/a.c delete mode 100644 test/standalone/link_import_tls_dylib/main.zig diff --git a/build.zig b/build.zig index fc97fc91bd..0ca2e0d7d7 100644 --- a/build.zig +++ b/build.zig @@ -489,6 +489,7 @@ pub fn build(b: *Builder) !void { toolchain_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); toolchain_step.dependOn(tests.addStandaloneTests(b, test_filter, modes, skip_non_native, enable_macos_sdk, target)); + toolchain_step.dependOn(tests.addLinkTests(b, test_filter, modes, enable_macos_sdk)); toolchain_step.dependOn(tests.addStackTraceTests(b, test_filter, modes)); toolchain_step.dependOn(tests.addCliTests(b, test_filter, modes)); toolchain_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); diff --git a/lib/std/build/RunStep.zig b/lib/std/build/RunStep.zig index e8cf87a441..0b8c233bfa 100644 --- a/lib/std/build/RunStep.zig +++ b/lib/std/build/RunStep.zig @@ -149,6 +149,7 @@ fn make(step: *Step) !void { const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root; var argv_list = ArrayList([]const u8).init(self.builder.allocator); + for (self.argv.items) |arg| { switch (arg) { .bytes => |bytes| try argv_list.append(bytes), diff --git a/test/link.zig b/test/link.zig new file mode 100644 index 0000000000..3c1b268d86 --- /dev/null +++ b/test/link.zig @@ -0,0 +1,52 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const tests = @import("tests.zig"); + +pub fn addCases(cases: *tests.StandaloneContext) void { + cases.addBuildFile("test/link/bss/build.zig", .{ + .build_modes = false, // we only guarantee zerofill for undefined in Debug + }); + + cases.addBuildFile("test/link/dylib/build.zig", .{ + .build_modes = true, + }); + + cases.addBuildFile("test/link/common_symbols/build.zig", .{ + .build_modes = true, + }); + + cases.addBuildFile("test/link/common_symbols_alignment/build.zig", .{ + .build_modes = true, + }); + + cases.addBuildFile("test/link/interdependent_static_c_libs/build.zig", .{ + .build_modes = true, + }); + + cases.addBuildFile("test/link/static_lib_as_system_lib/build.zig", .{ + .build_modes = true, + }); + + cases.addBuildFile("test/link/tls/build.zig", .{ + .build_modes = true, + }); + + if (builtin.os.tag == .macos) { + cases.addBuildFile("test/link/frameworks/build.zig", .{ + .build_modes = true, + .requires_macos_sdk = true, + }); + + // Try to build and run an Objective-C executable. + cases.addBuildFile("test/link/objc/build.zig", .{ + .build_modes = true, + .requires_macos_sdk = true, + }); + + // Try to build and run an Objective-C++ executable. + cases.addBuildFile("test/link/objcpp/build.zig", .{ + .build_modes = true, + .requires_macos_sdk = true, + }); + } +} diff --git a/test/link/bss/build.zig b/test/link/bss/build.zig new file mode 100644 index 0000000000..76e9bdb305 --- /dev/null +++ b/test/link/bss/build.zig @@ -0,0 +1,14 @@ +const Builder = @import("std").build.Builder; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + const test_step = b.step("test", "Test"); + + const exe = b.addExecutable("bss", "main.zig"); + b.default_step.dependOn(&exe.step); + exe.setBuildMode(mode); + + const run = exe.run(); + run.expectStdOutEqual("0, 1, 0\n"); + test_step.dependOn(&run.step); +} diff --git a/test/link/bss/main.zig b/test/link/bss/main.zig new file mode 100644 index 0000000000..c901f0bb27 --- /dev/null +++ b/test/link/bss/main.zig @@ -0,0 +1,13 @@ +const std = @import("std"); + +// Stress test zerofill layout +var buffer: [0x1000000]u64 = undefined; + +pub fn main() anyerror!void { + buffer[0x10] = 1; + try std.io.getStdOut().writer().print("{d}, {d}, {d}\n", .{ + buffer[0], + buffer[0x10], + buffer[0x1000000 - 1], + }); +} diff --git a/test/standalone/link_common_symbols/a.c b/test/link/common_symbols/a.c similarity index 100% rename from test/standalone/link_common_symbols/a.c rename to test/link/common_symbols/a.c diff --git a/test/standalone/link_common_symbols/b.c b/test/link/common_symbols/b.c similarity index 100% rename from test/standalone/link_common_symbols/b.c rename to test/link/common_symbols/b.c diff --git a/test/standalone/link_common_symbols/build.zig b/test/link/common_symbols/build.zig similarity index 100% rename from test/standalone/link_common_symbols/build.zig rename to test/link/common_symbols/build.zig diff --git a/test/standalone/link_common_symbols/c.c b/test/link/common_symbols/c.c similarity index 100% rename from test/standalone/link_common_symbols/c.c rename to test/link/common_symbols/c.c diff --git a/test/standalone/link_common_symbols/main.zig b/test/link/common_symbols/main.zig similarity index 100% rename from test/standalone/link_common_symbols/main.zig rename to test/link/common_symbols/main.zig diff --git a/test/standalone/link_common_symbols_alignment/a.c b/test/link/common_symbols_alignment/a.c similarity index 100% rename from test/standalone/link_common_symbols_alignment/a.c rename to test/link/common_symbols_alignment/a.c diff --git a/test/standalone/link_common_symbols_alignment/build.zig b/test/link/common_symbols_alignment/build.zig similarity index 100% rename from test/standalone/link_common_symbols_alignment/build.zig rename to test/link/common_symbols_alignment/build.zig diff --git a/test/standalone/link_common_symbols_alignment/main.zig b/test/link/common_symbols_alignment/main.zig similarity index 100% rename from test/standalone/link_common_symbols_alignment/main.zig rename to test/link/common_symbols_alignment/main.zig diff --git a/test/link/dylib/a.c b/test/link/dylib/a.c new file mode 100644 index 0000000000..199b31e1a0 --- /dev/null +++ b/test/link/dylib/a.c @@ -0,0 +1,7 @@ +#include + +char world[] = "world"; + +char* hello() { + return "Hello"; +} diff --git a/test/link/dylib/build.zig b/test/link/dylib/build.zig new file mode 100644 index 0000000000..a9dee4aafb --- /dev/null +++ b/test/link/dylib/build.zig @@ -0,0 +1,29 @@ +const std = @import("std"); +const Builder = std.build.Builder; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + + const test_step = b.step("test", "Test"); + + const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); + dylib.setBuildMode(mode); + dylib.addCSourceFile("a.c", &.{}); + dylib.linkLibC(); + dylib.install(); + + const exe = b.addExecutable("main", null); + exe.setBuildMode(mode); + exe.addCSourceFile("main.c", &.{}); + exe.linkSystemLibrary("a"); + exe.linkLibC(); + exe.addLibraryPath(b.pathFromRoot("zig-out/lib/")); + exe.addRPath(b.pathFromRoot("zig-out/lib")); + + const run = exe.run(); + run.cwd = b.pathFromRoot("."); + run.expectStdOutEqual("Hello world"); + + test_step.dependOn(b.getInstallStep()); + test_step.dependOn(&run.step); +} diff --git a/test/link/dylib/main.c b/test/link/dylib/main.c new file mode 100644 index 0000000000..be1647ddad --- /dev/null +++ b/test/link/dylib/main.c @@ -0,0 +1,9 @@ +#include + +char* hello(); +extern char world[]; + +int main() { + printf("%s %s", hello(), world); + return 0; +} diff --git a/test/standalone/link_frameworks/build.zig b/test/link/frameworks/build.zig similarity index 50% rename from test/standalone/link_frameworks/build.zig rename to test/link/frameworks/build.zig index 460e1675e0..5700422a41 100644 --- a/test/standalone/link_frameworks/build.zig +++ b/test/link/frameworks/build.zig @@ -1,19 +1,8 @@ const std = @import("std"); const Builder = std.build.Builder; -const CrossTarget = std.zig.CrossTarget; - -fn isRunnableTarget(t: CrossTarget) bool { - // TODO I think we might be able to run this on Linux via Darling. - // Add a check for that here, and return true if Darling is available. - if (t.isNative() and t.getOsTag() == .macos) - return true - else - return false; -} pub fn build(b: *Builder) void { const mode = b.standardReleaseOptions(); - const target = b.standardTargetOptions(.{}); const test_step = b.step("test", "Test the program"); @@ -21,14 +10,11 @@ pub fn build(b: *Builder) void { b.default_step.dependOn(&exe.step); exe.addCSourceFile("main.c", &[0][]const u8{}); exe.setBuildMode(mode); - exe.setTarget(target); exe.linkLibC(); // TODO when we figure out how to ship framework stubs for cross-compilation, // populate paths to the sysroot here. exe.linkFramework("Cocoa"); - if (isRunnableTarget(target)) { - const run_cmd = exe.run(); - test_step.dependOn(&run_cmd.step); - } + const run_cmd = exe.run(); + test_step.dependOn(&run_cmd.step); } diff --git a/test/standalone/link_frameworks/main.c b/test/link/frameworks/main.c similarity index 100% rename from test/standalone/link_frameworks/main.c rename to test/link/frameworks/main.c diff --git a/test/standalone/link_interdependent_static_c_libs/a.c b/test/link/interdependent_static_c_libs/a.c similarity index 100% rename from test/standalone/link_interdependent_static_c_libs/a.c rename to test/link/interdependent_static_c_libs/a.c diff --git a/test/standalone/link_interdependent_static_c_libs/a.h b/test/link/interdependent_static_c_libs/a.h similarity index 100% rename from test/standalone/link_interdependent_static_c_libs/a.h rename to test/link/interdependent_static_c_libs/a.h diff --git a/test/standalone/link_interdependent_static_c_libs/b.c b/test/link/interdependent_static_c_libs/b.c similarity index 100% rename from test/standalone/link_interdependent_static_c_libs/b.c rename to test/link/interdependent_static_c_libs/b.c diff --git a/test/standalone/link_interdependent_static_c_libs/b.h b/test/link/interdependent_static_c_libs/b.h similarity index 100% rename from test/standalone/link_interdependent_static_c_libs/b.h rename to test/link/interdependent_static_c_libs/b.h diff --git a/test/standalone/link_interdependent_static_c_libs/build.zig b/test/link/interdependent_static_c_libs/build.zig similarity index 100% rename from test/standalone/link_interdependent_static_c_libs/build.zig rename to test/link/interdependent_static_c_libs/build.zig diff --git a/test/standalone/link_interdependent_static_c_libs/main.zig b/test/link/interdependent_static_c_libs/main.zig similarity index 100% rename from test/standalone/link_interdependent_static_c_libs/main.zig rename to test/link/interdependent_static_c_libs/main.zig diff --git a/test/standalone/objc/Foo.h b/test/link/objc/Foo.h similarity index 100% rename from test/standalone/objc/Foo.h rename to test/link/objc/Foo.h diff --git a/test/standalone/objc/Foo.m b/test/link/objc/Foo.m similarity index 100% rename from test/standalone/objc/Foo.m rename to test/link/objc/Foo.m diff --git a/test/standalone/objc/build.zig b/test/link/objc/build.zig similarity index 54% rename from test/standalone/objc/build.zig rename to test/link/objc/build.zig index 1b3a90f90f..e41fd48e71 100644 --- a/test/standalone/objc/build.zig +++ b/test/link/objc/build.zig @@ -1,19 +1,8 @@ const std = @import("std"); const Builder = std.build.Builder; -const CrossTarget = std.zig.CrossTarget; - -fn isRunnableTarget(t: CrossTarget) bool { - // TODO I think we might be able to run this on Linux via Darling. - // Add a check for that here, and return true if Darling is available. - if (t.isNative() and t.getOsTag() == .macos) - return true - else - return false; -} pub fn build(b: *Builder) void { const mode = b.standardReleaseOptions(); - const target = b.standardTargetOptions(.{}); const test_step = b.step("test", "Test the program"); @@ -23,14 +12,11 @@ pub fn build(b: *Builder) void { exe.addCSourceFile("Foo.m", &[0][]const u8{}); exe.addCSourceFile("test.m", &[0][]const u8{}); exe.setBuildMode(mode); - exe.setTarget(target); exe.linkLibC(); // TODO when we figure out how to ship framework stubs for cross-compilation, // populate paths to the sysroot here. exe.linkFramework("Foundation"); - if (isRunnableTarget(target)) { - const run_cmd = exe.run(); - test_step.dependOn(&run_cmd.step); - } + const run_cmd = exe.run(); + test_step.dependOn(&run_cmd.step); } diff --git a/test/standalone/objc/test.m b/test/link/objc/test.m similarity index 100% rename from test/standalone/objc/test.m rename to test/link/objc/test.m diff --git a/test/standalone/objcpp/Foo.h b/test/link/objcpp/Foo.h similarity index 100% rename from test/standalone/objcpp/Foo.h rename to test/link/objcpp/Foo.h diff --git a/test/standalone/objcpp/Foo.mm b/test/link/objcpp/Foo.mm similarity index 100% rename from test/standalone/objcpp/Foo.mm rename to test/link/objcpp/Foo.mm diff --git a/test/standalone/objcpp/build.zig b/test/link/objcpp/build.zig similarity index 54% rename from test/standalone/objcpp/build.zig rename to test/link/objcpp/build.zig index 688592793a..767578e225 100644 --- a/test/standalone/objcpp/build.zig +++ b/test/link/objcpp/build.zig @@ -1,19 +1,8 @@ const std = @import("std"); const Builder = std.build.Builder; -const CrossTarget = std.zig.CrossTarget; - -fn isRunnableTarget(t: CrossTarget) bool { - // TODO I think we might be able to run this on Linux via Darling. - // Add a check for that here, and return true if Darling is available. - if (t.isNative() and t.getOsTag() == .macos) - return true - else - return false; -} pub fn build(b: *Builder) void { const mode = b.standardReleaseOptions(); - const target = b.standardTargetOptions(.{}); const test_step = b.step("test", "Test the program"); @@ -23,14 +12,13 @@ pub fn build(b: *Builder) void { exe.addCSourceFile("Foo.mm", &[0][]const u8{}); exe.addCSourceFile("test.mm", &[0][]const u8{}); exe.setBuildMode(mode); - exe.setTarget(target); exe.linkLibCpp(); // TODO when we figure out how to ship framework stubs for cross-compilation, // populate paths to the sysroot here. exe.linkFramework("Foundation"); - if (isRunnableTarget(target)) { - const run_cmd = exe.run(); - test_step.dependOn(&run_cmd.step); - } + const run_cmd = exe.run(); + run_cmd.expectStdOutEqual("Hello from C++ and Zig"); + + test_step.dependOn(&run_cmd.step); } diff --git a/test/standalone/objcpp/test.mm b/test/link/objcpp/test.mm similarity index 100% rename from test/standalone/objcpp/test.mm rename to test/link/objcpp/test.mm diff --git a/test/standalone/link_static_lib_as_system_lib/a.c b/test/link/static_lib_as_system_lib/a.c similarity index 100% rename from test/standalone/link_static_lib_as_system_lib/a.c rename to test/link/static_lib_as_system_lib/a.c diff --git a/test/standalone/link_static_lib_as_system_lib/a.h b/test/link/static_lib_as_system_lib/a.h similarity index 100% rename from test/standalone/link_static_lib_as_system_lib/a.h rename to test/link/static_lib_as_system_lib/a.h diff --git a/test/standalone/link_static_lib_as_system_lib/build.zig b/test/link/static_lib_as_system_lib/build.zig similarity index 100% rename from test/standalone/link_static_lib_as_system_lib/build.zig rename to test/link/static_lib_as_system_lib/build.zig diff --git a/test/standalone/link_static_lib_as_system_lib/main.zig b/test/link/static_lib_as_system_lib/main.zig similarity index 100% rename from test/standalone/link_static_lib_as_system_lib/main.zig rename to test/link/static_lib_as_system_lib/main.zig diff --git a/test/link/tls/a.c b/test/link/tls/a.c new file mode 100644 index 0000000000..8602d02419 --- /dev/null +++ b/test/link/tls/a.c @@ -0,0 +1,5 @@ +_Thread_local int a; + +int getA() { + return a; +} diff --git a/test/standalone/link_import_tls_dylib/build.zig b/test/link/tls/build.zig similarity index 91% rename from test/standalone/link_import_tls_dylib/build.zig rename to test/link/tls/build.zig index 332173fbb6..ebf15ca439 100644 --- a/test/standalone/link_import_tls_dylib/build.zig +++ b/test/link/tls/build.zig @@ -6,10 +6,12 @@ pub fn build(b: *Builder) void { const lib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); lib.setBuildMode(mode); lib.addCSourceFile("a.c", &.{}); + lib.linkLibC(); const test_exe = b.addTest("main.zig"); test_exe.setBuildMode(mode); test_exe.linkLibrary(lib); + test_exe.linkLibC(); const test_step = b.step("test", "Test it"); test_step.dependOn(&test_exe.step); diff --git a/test/link/tls/main.zig b/test/link/tls/main.zig new file mode 100644 index 0000000000..ab01616e31 --- /dev/null +++ b/test/link/tls/main.zig @@ -0,0 +1,15 @@ +const std = @import("std"); + +extern threadlocal var a: i32; +extern fn getA() i32; + +fn getA2() i32 { + return a; +} + +test { + a = 2; + try std.testing.expect(getA() == 2); + try std.testing.expect(2 == getA2()); + try std.testing.expect(getA() == getA2()); +} diff --git a/test/standalone.zig b/test/standalone.zig index c34f9467d6..92d0ea4aa3 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -13,31 +13,12 @@ pub fn addCases(cases: *tests.StandaloneContext) void { cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{}); cases.addBuildFile("test/standalone/shared_library/build.zig", .{}); cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{}); - if (builtin.os.tag == .macos) { - // Zig's macOS linker does not yet support LTO for LLVM IR files: - // https://github.com/ziglang/zig/issues/8680 - cases.addBuildFile("test/standalone/mix_c_files/build.zig", .{ - .build_modes = false, - .cross_targets = true, - }); - } else { - cases.addBuildFile("test/standalone/mix_c_files/build.zig", .{ - .build_modes = true, - .cross_targets = true, - }); - } + cases.addBuildFile("test/standalone/mix_c_files/build.zig", .{ + .build_modes = true, + .cross_targets = true, + }); cases.addBuildFile("test/standalone/global_linkage/build.zig", .{}); cases.addBuildFile("test/standalone/static_c_lib/build.zig", .{}); - cases.addBuildFile("test/standalone/link_interdependent_static_c_libs/build.zig", .{}); - cases.addBuildFile("test/standalone/link_static_lib_as_system_lib/build.zig", .{}); - cases.addBuildFile("test/standalone/link_common_symbols/build.zig", .{}); - cases.addBuildFile("test/standalone/link_frameworks/build.zig", .{ - .requires_macos_sdk = true, - }); - cases.addBuildFile("test/standalone/link_common_symbols_alignment/build.zig", .{}); - if (builtin.os.tag == .macos) { - cases.addBuildFile("test/standalone/link_import_tls_dylib/build.zig", .{}); - } cases.addBuildFile("test/standalone/issue_339/build.zig", .{}); cases.addBuildFile("test/standalone/issue_8550/build.zig", .{}); cases.addBuildFile("test/standalone/issue_794/build.zig", .{}); @@ -69,16 +50,6 @@ pub fn addCases(cases: *tests.StandaloneContext) void { if (builtin.os.tag == .linux) { cases.addBuildFile("test/standalone/pie/build.zig", .{}); } - // Try to build and run an Objective-C executable. - cases.addBuildFile("test/standalone/objc/build.zig", .{ - .build_modes = true, - .requires_macos_sdk = true, - }); - // Try to build and run an Objective-C++ executable. - cases.addBuildFile("test/standalone/objcpp/build.zig", .{ - .build_modes = true, - .requires_macos_sdk = true, - }); // Ensure the development tools are buildable. cases.add("tools/gen_spirv_spec.zig"); diff --git a/test/standalone/link_import_tls_dylib/a.c b/test/standalone/link_import_tls_dylib/a.c deleted file mode 100644 index 5c5aa5bae4..0000000000 --- a/test/standalone/link_import_tls_dylib/a.c +++ /dev/null @@ -1 +0,0 @@ -_Thread_local int a; diff --git a/test/standalone/link_import_tls_dylib/main.zig b/test/standalone/link_import_tls_dylib/main.zig deleted file mode 100644 index 354c6f545e..0000000000 --- a/test/standalone/link_import_tls_dylib/main.zig +++ /dev/null @@ -1,7 +0,0 @@ -const std = @import("std"); - -extern threadlocal var a: i32; - -test { - try std.testing.expect(a == 0); -} diff --git a/test/tests.zig b/test/tests.zig index 3666ef1028..8bc415d28b 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -21,6 +21,7 @@ const assemble_and_link = @import("assemble_and_link.zig"); const translate_c = @import("translate_c.zig"); const run_translated_c = @import("run_translated_c.zig"); const gen_h = @import("gen_h.zig"); +const link = @import("link.zig"); // Implementations pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext; @@ -479,6 +480,27 @@ pub fn addStandaloneTests( return cases.step; } +pub fn addLinkTests( + b: *build.Builder, + test_filter: ?[]const u8, + modes: []const Mode, + enable_macos_sdk: bool, +) *build.Step { + const cases = b.allocator.create(StandaloneContext) catch unreachable; + cases.* = StandaloneContext{ + .b = b, + .step = b.step("test-link", "Run the linker tests"), + .test_index = 0, + .test_filter = test_filter, + .modes = modes, + .skip_non_native = true, + .enable_macos_sdk = enable_macos_sdk, + .target = .{}, + }; + link.addCases(cases); + return cases.step; +} + pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { _ = test_filter; _ = modes; From 6e56a8df20395749b21b3857952dae1c227aacf1 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 20 Jun 2022 17:29:00 +0200 Subject: [PATCH 1887/2031] link-tests: CheckFileStep to do grep test on the binary --- test/link.zig | 8 ++++---- test/link/dylib/build.zig | 7 +++++++ test/tests.zig | 3 ++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/test/link.zig b/test/link.zig index 3c1b268d86..9026da65ea 100644 --- a/test/link.zig +++ b/test/link.zig @@ -7,10 +7,6 @@ pub fn addCases(cases: *tests.StandaloneContext) void { .build_modes = false, // we only guarantee zerofill for undefined in Debug }); - cases.addBuildFile("test/link/dylib/build.zig", .{ - .build_modes = true, - }); - cases.addBuildFile("test/link/common_symbols/build.zig", .{ .build_modes = true, }); @@ -32,6 +28,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void { }); if (builtin.os.tag == .macos) { + cases.addBuildFile("test/link/dylib/build.zig", .{ + .build_modes = true, + }); + cases.addBuildFile("test/link/frameworks/build.zig", .{ .build_modes = true, .requires_macos_sdk = true, diff --git a/test/link/dylib/build.zig b/test/link/dylib/build.zig index a9dee4aafb..c14bf0b7d5 100644 --- a/test/link/dylib/build.zig +++ b/test/link/dylib/build.zig @@ -24,6 +24,13 @@ pub fn build(b: *Builder) void { run.cwd = b.pathFromRoot("."); run.expectStdOutEqual("Hello world"); + const exp_dylib = std.macho.createLoadDylibCommand(b.allocator, "@rpath/liba.dylib", 2, 0x10000, 0x10000) catch unreachable; + var buf = std.ArrayList(u8).init(b.allocator); + defer buf.deinit(); + exp_dylib.write(buf.writer()) catch unreachable; + const check_file = std.build.CheckFileStep.create(b, exe.getOutputSource(), &[_][]const u8{buf.items}); + test_step.dependOn(b.getInstallStep()); test_step.dependOn(&run.step); + test_step.dependOn(&check_file.step); } diff --git a/test/tests.zig b/test/tests.zig index 8bc415d28b..d1f319673c 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -995,7 +995,8 @@ pub const StandaloneContext = struct { } if (features.cross_targets and !self.target.isNative()) { - const target_arg = fmt.allocPrint(b.allocator, "-Dtarget={s}", .{self.target.zigTriple(b.allocator) catch unreachable}) catch unreachable; + const target_triple = self.target.zigTriple(b.allocator) catch unreachable; + const target_arg = fmt.allocPrint(b.allocator, "-Dtarget={s}", .{target_triple}) catch unreachable; zig_args.append(target_arg) catch unreachable; } From 2d09540a636ab6ef2ca5087f18d55bbc259cd652 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 20 Jun 2022 18:25:20 +0200 Subject: [PATCH 1888/2031] link-tests: test pagezero_size option for macho --- lib/std/build.zig | 7 +++++++ test/link.zig | 4 ++++ test/link/pagezero/build.zig | 34 ++++++++++++++++++++++++++++++++++ test/link/pagezero/main.c | 3 +++ 4 files changed, 48 insertions(+) create mode 100644 test/link/pagezero/build.zig create mode 100644 test/link/pagezero/main.c diff --git a/lib/std/build.zig b/lib/std/build.zig index ce527ff021..af135ea02a 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1582,6 +1582,9 @@ pub const LibExeObjStep = struct { /// (Darwin) Path to entitlements file entitlements: ?[]const u8 = null, + /// (Darwin) Size of the pagezero segment. + pagezero_size: ?u64 = null, + /// Position Independent Code force_pic: ?bool = null, @@ -2638,6 +2641,10 @@ pub const LibExeObjStep = struct { if (self.entitlements) |entitlements| { try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements }); } + if (self.pagezero_size) |pagezero_size| { + const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size}); + try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size }); + } if (self.bundle_compiler_rt) |x| { if (x) { diff --git a/test/link.zig b/test/link.zig index 9026da65ea..bc69003e68 100644 --- a/test/link.zig +++ b/test/link.zig @@ -28,6 +28,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void { }); if (builtin.os.tag == .macos) { + cases.addBuildFile("test/link/pagezero/build.zig", .{ + .build_modes = false, + }); + cases.addBuildFile("test/link/dylib/build.zig", .{ .build_modes = true, }); diff --git a/test/link/pagezero/build.zig b/test/link/pagezero/build.zig new file mode 100644 index 0000000000..65129008a4 --- /dev/null +++ b/test/link/pagezero/build.zig @@ -0,0 +1,34 @@ +const std = @import("std"); +const Builder = std.build.Builder; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + + const test_step = b.step("test", "Test"); + + const exe = b.addExecutable("main", null); + exe.setBuildMode(mode); + exe.addCSourceFile("main.c", &.{}); + exe.linkLibC(); + exe.pagezero_size = 0x4000; + + var name: [16]u8 = undefined; + std.mem.set(u8, &name, 0); + std.mem.copy(u8, &name, "__PAGEZERO"); + const pagezero_seg = std.macho.segment_command_64{ + .cmdsize = @sizeOf(std.macho.segment_command_64), + .segname = name, + .vmaddr = 0, + .vmsize = 0x4000, + .fileoff = 0, + .filesize = 0, + .maxprot = 0, + .initprot = 0, + .nsects = 0, + .flags = 0, + }; + const check_file = std.build.CheckFileStep.create(b, exe.getOutputSource(), &[_][]const u8{std.mem.asBytes(&pagezero_seg)}); + + test_step.dependOn(b.getInstallStep()); + test_step.dependOn(&check_file.step); +} diff --git a/test/link/pagezero/main.c b/test/link/pagezero/main.c new file mode 100644 index 0000000000..ca68d24cc7 --- /dev/null +++ b/test/link/pagezero/main.c @@ -0,0 +1,3 @@ +int main(int argc, char* argv[]) { + return 0; +} From ca9862578903a93089e6aae71bb8641b580bc4f6 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Tue, 21 Jun 2022 03:21:45 -0600 Subject: [PATCH 1889/2031] std.os.execvpe: fix buffer overflow The NameTooLong check isn't taking the sentinel 0 into account which would result in a buffer overflow on the stack. --- lib/std/os.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index c79bd6e7b0..578a8ddcbc 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1821,11 +1821,11 @@ pub fn execvpeZ_expandArg0( }; while (it.next()) |search_path| { - if (path_buf.len < search_path.len + file_slice.len + 1) return error.NameTooLong; + const path_len = search_path.len + file_slice.len + 1; + if (path_buf.len < path_len + 1) return error.NameTooLong; mem.copy(u8, &path_buf, search_path); path_buf[search_path.len] = '/'; mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice); - const path_len = search_path.len + file_slice.len + 1; path_buf[path_len] = 0; const full_path = path_buf[0..path_len :0].ptr; switch (arg0_expand) { From 5fbdfb3f3477bc8ac70b828671a7f980e8a8ad10 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 21 Jun 2022 15:44:22 +0200 Subject: [PATCH 1890/2031] link-tests: add CheckMachOStep CheckMachOStep specialises CheckFileStep into directed (surgical) MachO file fuzzy searches. This will be the building block for comprehensive MachO linker tests. --- lib/std/build.zig | 6 + lib/std/build/CheckMachOStep.zig | 210 +++++++++++++++++++++++++++++++ test/link/dylib/build.zig | 42 +++++-- test/link/pagezero/build.zig | 68 ++++++---- 4 files changed, 292 insertions(+), 34 deletions(-) create mode 100644 lib/std/build/CheckMachOStep.zig diff --git a/lib/std/build.zig b/lib/std/build.zig index af135ea02a..4582a0658d 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -24,6 +24,7 @@ pub const TranslateCStep = @import("build/TranslateCStep.zig"); pub const WriteFileStep = @import("build/WriteFileStep.zig"); pub const RunStep = @import("build/RunStep.zig"); pub const CheckFileStep = @import("build/CheckFileStep.zig"); +pub const CheckMachOStep = @import("build/CheckMachOStep.zig"); pub const InstallRawStep = @import("build/InstallRawStep.zig"); pub const OptionsStep = @import("build/OptionsStep.zig"); @@ -1864,6 +1865,10 @@ pub const LibExeObjStep = struct { return run_step; } + pub fn checkMachO(self: *LibExeObjStep) *CheckMachOStep { + return CheckMachOStep.create(self.builder, self.getOutputSource()); + } + pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void { self.linker_script = source.dupe(self.builder); source.addStepDependencies(&self.step); @@ -3450,6 +3455,7 @@ pub const Step = struct { write_file, run, check_file, + check_macho, install_raw, options, custom, diff --git a/lib/std/build/CheckMachOStep.zig b/lib/std/build/CheckMachOStep.zig new file mode 100644 index 0000000000..1ac12a9749 --- /dev/null +++ b/lib/std/build/CheckMachOStep.zig @@ -0,0 +1,210 @@ +const std = @import("../std.zig"); +const build = std.build; +const Step = build.Step; +const Builder = build.Builder; +const fs = std.fs; +const macho = std.macho; +const mem = std.mem; + +const CheckMachOStep = @This(); + +pub const base_id = .check_macho; + +step: Step, +builder: *Builder, +source: build.FileSource, +max_bytes: usize = 20 * 1024 * 1024, +lc_checks: std.ArrayList(LCCheck), + +const LCCheck = struct { + // common to most LCs + cmd: macho.LC, + name: ?[]const u8 = null, + // LC.SEGMENT_64 specific + index: ?usize = null, + vaddr: ?u64 = null, + memsz: ?u64 = null, + offset: ?u64 = null, + filesz: ?u64 = null, + // LC.LOAD_DYLIB specific + timestamp: ?u64 = null, + current_version: ?u32 = null, + compat_version: ?u32 = null, +}; + +pub fn create(builder: *Builder, source: build.FileSource) *CheckMachOStep { + const gpa = builder.allocator; + const self = gpa.create(CheckMachOStep) catch unreachable; + self.* = CheckMachOStep{ + .builder = builder, + .step = Step.init(.check_file, "CheckMachO", gpa, make), + .source = source.dupe(builder), + .lc_checks = std.ArrayList(LCCheck).init(gpa), + }; + self.source.addStepDependencies(&self.step); + return self; +} + +pub fn checkLoadCommand(self: *CheckMachOStep, check: LCCheck) void { + self.lc_checks.append(.{ + .cmd = check.cmd, + .index = check.index, + .name = if (check.name) |name| self.builder.dupe(name) else null, + .vaddr = check.vaddr, + .memsz = check.memsz, + .offset = check.offset, + .filesz = check.filesz, + .timestamp = check.timestamp, + .current_version = check.current_version, + .compat_version = check.compat_version, + }) catch unreachable; +} + +fn make(step: *Step) !void { + const self = @fieldParentPtr(CheckMachOStep, "step", step); + + const gpa = self.builder.allocator; + const src_path = self.source.getPath(self.builder); + const contents = try fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); + + // Parse the object file's header + var stream = std.io.fixedBufferStream(contents); + const reader = stream.reader(); + + const hdr = try reader.readStruct(macho.mach_header_64); + if (hdr.magic != macho.MH_MAGIC_64) { + return error.InvalidMagicNumber; + } + + var load_commands = std.ArrayList(macho.LoadCommand).init(gpa); + try load_commands.ensureTotalCapacity(hdr.ncmds); + + var i: u16 = 0; + while (i < hdr.ncmds) : (i += 1) { + var cmd = try macho.LoadCommand.read(gpa, reader); + load_commands.appendAssumeCapacity(cmd); + } + + outer: for (self.lc_checks.items) |ch| { + if (ch.index) |index| { + const lc = load_commands.items[index]; + try cmpLoadCommand(ch, lc); + } else { + for (load_commands.items) |lc| { + if (lc.cmd() == ch.cmd) { + try cmpLoadCommand(ch, lc); + continue :outer; + } + } else { + return err("LC not found", ch.cmd, ""); + } + } + } +} + +fn cmpLoadCommand(exp: LCCheck, given: macho.LoadCommand) error{TestFailed}!void { + if (exp.cmd != given.cmd()) { + return err("LC mismatch", exp.cmd, given.cmd()); + } + switch (exp.cmd) { + .SEGMENT_64 => { + const lc = given.segment.inner; + if (exp.name) |name| { + if (!mem.eql(u8, name, lc.segName())) { + return err("segment name mismatch", name, lc.segName()); + } + } + if (exp.vaddr) |vaddr| { + if (vaddr != lc.vmaddr) { + return err("segment VM address mismatch", vaddr, lc.vmaddr); + } + } + if (exp.memsz) |memsz| { + if (memsz != lc.vmsize) { + return err("segment VM size mismatch", memsz, lc.vmsize); + } + } + if (exp.offset) |offset| { + if (offset != lc.fileoff) { + return err("segment file offset mismatch", offset, lc.fileoff); + } + } + if (exp.filesz) |filesz| { + if (filesz != lc.filesize) { + return err("segment file size mismatch", filesz, lc.filesize); + } + } + }, + .ID_DYLIB, .LOAD_DYLIB => { + const lc = given.dylib; + if (exp.name) |name| { + if (!mem.eql(u8, name, mem.sliceTo(lc.data, 0))) { + return err("dylib path mismatch", name, mem.sliceTo(lc.data, 0)); + } + } + if (exp.timestamp) |ts| { + if (ts != lc.inner.dylib.timestamp) { + return err("timestamp mismatch", ts, lc.inner.dylib.timestamp); + } + } + if (exp.current_version) |cv| { + if (cv != lc.inner.dylib.current_version) { + return err("current version mismatch", cv, lc.inner.dylib.current_version); + } + } + if (exp.compat_version) |cv| { + if (cv != lc.inner.dylib.compatibility_version) { + return err("compatibility version mismatch", cv, lc.inner.dylib.compatibility_version); + } + } + }, + .RPATH => { + const lc = given.rpath; + if (exp.name) |name| { + if (!mem.eql(u8, name, mem.sliceTo(lc.data, 0))) { + return err("rpath path mismatch", name, mem.sliceTo(lc.data, 0)); + } + } + }, + else => @panic("TODO compare more load commands"), + } +} + +fn err(msg: []const u8, exp: anytype, giv: anytype) error{TestFailed} { + const fmt_specifier = if (comptime isString(@TypeOf(exp))) "{s}" else switch (@typeInfo(@TypeOf(exp))) { + .Int => "{x}", + .Float => "{d}", + else => "{any}", + }; + std.debug.print( + \\===================================== + \\{s} + \\ + \\======== Expected to find: ========== + \\ + ++ fmt_specifier ++ + \\ + \\======== But instead found: ========= + \\ + ++ fmt_specifier ++ + \\ + \\ + , .{ msg, exp, giv }); + return error.TestFailed; +} + +fn isString(comptime T: type) bool { + switch (@typeInfo(T)) { + .Array => return std.meta.Elem(T) == u8, + .Pointer => |pinfo| { + switch (pinfo.size) { + .Slice, .Many => return std.meta.Elem(T) == u8, + else => switch (@typeInfo(pinfo.child)) { + .Array => return isString(pinfo.child), + else => return false, + }, + } + }, + else => return false, + } +} diff --git a/test/link/dylib/build.zig b/test/link/dylib/build.zig index c14bf0b7d5..9878f9a66c 100644 --- a/test/link/dylib/build.zig +++ b/test/link/dylib/build.zig @@ -5,6 +5,7 @@ pub fn build(b: *Builder) void { const mode = b.standardReleaseOptions(); const test_step = b.step("test", "Test"); + test_step.dependOn(b.getInstallStep()); const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); dylib.setBuildMode(mode); @@ -12,6 +13,18 @@ pub fn build(b: *Builder) void { dylib.linkLibC(); dylib.install(); + { + const check_macho = dylib.checkMachO(); + check_macho.checkLoadCommand(.{ + .cmd = std.macho.LC.ID_DYLIB, + .name = "@rpath/liba.dylib", + .timestamp = 2, + .current_version = 0x10000, + .compat_version = 0x10000, + }); + test_step.dependOn(&check_macho.step); + } + const exe = b.addExecutable("main", null); exe.setBuildMode(mode); exe.addCSourceFile("main.c", &.{}); @@ -20,17 +33,28 @@ pub fn build(b: *Builder) void { exe.addLibraryPath(b.pathFromRoot("zig-out/lib/")); exe.addRPath(b.pathFromRoot("zig-out/lib")); + { + const check_macho = exe.checkMachO(); + check_macho.checkLoadCommand(.{ + .cmd = std.macho.LC.LOAD_DYLIB, + .name = "@rpath/liba.dylib", + .timestamp = 2, + .current_version = 0x10000, + .compat_version = 0x10000, + }); + test_step.dependOn(&check_macho.step); + } + { + const check_macho = exe.checkMachO(); + check_macho.checkLoadCommand(.{ + .cmd = std.macho.LC.RPATH, + .name = b.pathFromRoot("zig-out/lib"), + }); + test_step.dependOn(&check_macho.step); + } + const run = exe.run(); run.cwd = b.pathFromRoot("."); run.expectStdOutEqual("Hello world"); - - const exp_dylib = std.macho.createLoadDylibCommand(b.allocator, "@rpath/liba.dylib", 2, 0x10000, 0x10000) catch unreachable; - var buf = std.ArrayList(u8).init(b.allocator); - defer buf.deinit(); - exp_dylib.write(buf.writer()) catch unreachable; - const check_file = std.build.CheckFileStep.create(b, exe.getOutputSource(), &[_][]const u8{buf.items}); - - test_step.dependOn(b.getInstallStep()); test_step.dependOn(&run.step); - test_step.dependOn(&check_file.step); } diff --git a/test/link/pagezero/build.zig b/test/link/pagezero/build.zig index 65129008a4..71068b4480 100644 --- a/test/link/pagezero/build.zig +++ b/test/link/pagezero/build.zig @@ -5,30 +5,48 @@ pub fn build(b: *Builder) void { const mode = b.standardReleaseOptions(); const test_step = b.step("test", "Test"); - - const exe = b.addExecutable("main", null); - exe.setBuildMode(mode); - exe.addCSourceFile("main.c", &.{}); - exe.linkLibC(); - exe.pagezero_size = 0x4000; - - var name: [16]u8 = undefined; - std.mem.set(u8, &name, 0); - std.mem.copy(u8, &name, "__PAGEZERO"); - const pagezero_seg = std.macho.segment_command_64{ - .cmdsize = @sizeOf(std.macho.segment_command_64), - .segname = name, - .vmaddr = 0, - .vmsize = 0x4000, - .fileoff = 0, - .filesize = 0, - .maxprot = 0, - .initprot = 0, - .nsects = 0, - .flags = 0, - }; - const check_file = std.build.CheckFileStep.create(b, exe.getOutputSource(), &[_][]const u8{std.mem.asBytes(&pagezero_seg)}); - test_step.dependOn(b.getInstallStep()); - test_step.dependOn(&check_file.step); + + { + const exe = b.addExecutable("pagezero", null); + exe.setBuildMode(mode); + exe.addCSourceFile("main.c", &.{}); + exe.linkLibC(); + exe.pagezero_size = 0x4000; + + const check_macho = exe.checkMachO(); + check_macho.checkLoadCommand(.{ + .cmd = std.macho.LC.SEGMENT_64, + .index = 0, + .name = "__PAGEZERO", + .vaddr = 0, + .memsz = 0x4000, + }); + check_macho.checkLoadCommand(.{ + .cmd = std.macho.LC.SEGMENT_64, + .index = 1, + .name = "__TEXT", + .vaddr = 0x4000, + }); + + test_step.dependOn(&check_macho.step); + } + + { + const exe = b.addExecutable("no_pagezero", null); + exe.setBuildMode(mode); + exe.addCSourceFile("main.c", &.{}); + exe.linkLibC(); + exe.pagezero_size = 0; + + const check_macho = exe.checkMachO(); + check_macho.checkLoadCommand(.{ + .cmd = std.macho.LC.SEGMENT_64, + .index = 0, + .name = "__TEXT", + .vaddr = 0, + }); + + test_step.dependOn(&check_macho.step); + } } From 937464f398241358c7d3e534b179dfbdfdf2ffd9 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 21 Jun 2022 22:19:55 +0200 Subject: [PATCH 1891/2031] link-tests: dump metadata to string and grep results This approach is more inline with what LLVM/LLD does for testing of their output, and seems to be more generic and easier to extend than implementing a lot of repetitive and nontrivial comparison logic when working directly on structures. --- lib/std/build/CheckMachOStep.zig | 252 +++++++++++++------------------ test/link/dylib/build.zig | 49 +++--- test/link/pagezero/build.zig | 37 ++--- 3 files changed, 139 insertions(+), 199 deletions(-) diff --git a/lib/std/build/CheckMachOStep.zig b/lib/std/build/CheckMachOStep.zig index 1ac12a9749..b58cfa6e46 100644 --- a/lib/std/build/CheckMachOStep.zig +++ b/lib/std/build/CheckMachOStep.zig @@ -1,36 +1,23 @@ const std = @import("../std.zig"); +const assert = std.debug.assert; const build = std.build; -const Step = build.Step; -const Builder = build.Builder; const fs = std.fs; const macho = std.macho; const mem = std.mem; const CheckMachOStep = @This(); +const Allocator = mem.Allocator; +const Builder = build.Builder; +const Step = build.Step; + pub const base_id = .check_macho; step: Step, builder: *Builder, source: build.FileSource, max_bytes: usize = 20 * 1024 * 1024, -lc_checks: std.ArrayList(LCCheck), - -const LCCheck = struct { - // common to most LCs - cmd: macho.LC, - name: ?[]const u8 = null, - // LC.SEGMENT_64 specific - index: ?usize = null, - vaddr: ?u64 = null, - memsz: ?u64 = null, - offset: ?u64 = null, - filesz: ?u64 = null, - // LC.LOAD_DYLIB specific - timestamp: ?u64 = null, - current_version: ?u32 = null, - compat_version: ?u32 = null, -}; +checks: std.ArrayList(Check), pub fn create(builder: *Builder, source: build.FileSource) *CheckMachOStep { const gpa = builder.allocator; @@ -39,25 +26,38 @@ pub fn create(builder: *Builder, source: build.FileSource) *CheckMachOStep { .builder = builder, .step = Step.init(.check_file, "CheckMachO", gpa, make), .source = source.dupe(builder), - .lc_checks = std.ArrayList(LCCheck).init(gpa), + .checks = std.ArrayList(Check).init(gpa), }; self.source.addStepDependencies(&self.step); return self; } -pub fn checkLoadCommand(self: *CheckMachOStep, check: LCCheck) void { - self.lc_checks.append(.{ - .cmd = check.cmd, - .index = check.index, - .name = if (check.name) |name| self.builder.dupe(name) else null, - .vaddr = check.vaddr, - .memsz = check.memsz, - .offset = check.offset, - .filesz = check.filesz, - .timestamp = check.timestamp, - .current_version = check.current_version, - .compat_version = check.compat_version, - }) catch unreachable; +const Check = struct { + builder: *Builder, + phrases: std.ArrayList([]const u8), + + fn create(b: *Builder) Check { + return .{ + .builder = b, + .phrases = std.ArrayList([]const u8).init(b.allocator), + }; + } + + fn addPhrase(self: *Check, phrase: []const u8) void { + self.phrases.append(self.builder.dupe(phrase)) catch unreachable; + } +}; + +pub fn check(self: *CheckMachOStep, phrase: []const u8) void { + var new_check = Check.create(self.builder); + new_check.addPhrase(phrase); + self.checks.append(new_check) catch unreachable; +} + +pub fn checkNext(self: *CheckMachOStep, phrase: []const u8) void { + assert(self.checks.items.len > 0); + const last = &self.checks.items[self.checks.items.len - 1]; + last.addPhrase(phrase); } fn make(step: *Step) !void { @@ -76,135 +76,95 @@ fn make(step: *Step) !void { return error.InvalidMagicNumber; } - var load_commands = std.ArrayList(macho.LoadCommand).init(gpa); - try load_commands.ensureTotalCapacity(hdr.ncmds); + var metadata = std.ArrayList(u8).init(gpa); + const writer = metadata.writer(); var i: u16 = 0; while (i < hdr.ncmds) : (i += 1) { var cmd = try macho.LoadCommand.read(gpa, reader); - load_commands.appendAssumeCapacity(cmd); + try dumpLoadCommand(cmd, i, writer); + try writer.writeByte('\n'); } - outer: for (self.lc_checks.items) |ch| { - if (ch.index) |index| { - const lc = load_commands.items[index]; - try cmpLoadCommand(ch, lc); - } else { - for (load_commands.items) |lc| { - if (lc.cmd() == ch.cmd) { - try cmpLoadCommand(ch, lc); - continue :outer; + for (self.checks.items) |chk| { + const first_phrase = chk.phrases.items[0]; + + if (mem.indexOf(u8, metadata.items, first_phrase)) |index| { + // TODO backtrack to track current scope + var it = std.mem.tokenize(u8, metadata.items[index..], "\r\n"); + + outer: for (chk.phrases.items[1..]) |next_phrase| { + while (it.next()) |line| { + if (mem.eql(u8, line, next_phrase)) { + std.debug.print("{s} == {s}\n", .{ line, next_phrase }); + continue :outer; + } + std.debug.print("{s} != {s}\n", .{ line, next_phrase }); + } else { + return error.TestFailed; } - } else { - return err("LC not found", ch.cmd, ""); } + } else { + return error.TestFailed; } } } -fn cmpLoadCommand(exp: LCCheck, given: macho.LoadCommand) error{TestFailed}!void { - if (exp.cmd != given.cmd()) { - return err("LC mismatch", exp.cmd, given.cmd()); - } - switch (exp.cmd) { +fn dumpLoadCommand(lc: macho.LoadCommand, index: u16, writer: anytype) !void { + // print header first + try writer.print( + \\LC {d} + \\cmd {s} + \\cmdsize {d} + , .{ index, @tagName(lc.cmd()), lc.cmdsize() }); + + switch (lc.cmd()) { .SEGMENT_64 => { - const lc = given.segment.inner; - if (exp.name) |name| { - if (!mem.eql(u8, name, lc.segName())) { - return err("segment name mismatch", name, lc.segName()); - } - } - if (exp.vaddr) |vaddr| { - if (vaddr != lc.vmaddr) { - return err("segment VM address mismatch", vaddr, lc.vmaddr); - } - } - if (exp.memsz) |memsz| { - if (memsz != lc.vmsize) { - return err("segment VM size mismatch", memsz, lc.vmsize); - } - } - if (exp.offset) |offset| { - if (offset != lc.fileoff) { - return err("segment file offset mismatch", offset, lc.fileoff); - } - } - if (exp.filesz) |filesz| { - if (filesz != lc.filesize) { - return err("segment file size mismatch", filesz, lc.filesize); - } - } + // TODO dump section headers + const seg = lc.segment.inner; + try writer.writeByte('\n'); + try writer.print( + \\segname {s} + \\vmaddr {x} + \\vmsize {x} + \\fileoff {x} + \\filesz {x} + , .{ + seg.segName(), + seg.vmaddr, + seg.vmsize, + seg.fileoff, + seg.filesize, + }); }, - .ID_DYLIB, .LOAD_DYLIB => { - const lc = given.dylib; - if (exp.name) |name| { - if (!mem.eql(u8, name, mem.sliceTo(lc.data, 0))) { - return err("dylib path mismatch", name, mem.sliceTo(lc.data, 0)); - } - } - if (exp.timestamp) |ts| { - if (ts != lc.inner.dylib.timestamp) { - return err("timestamp mismatch", ts, lc.inner.dylib.timestamp); - } - } - if (exp.current_version) |cv| { - if (cv != lc.inner.dylib.current_version) { - return err("current version mismatch", cv, lc.inner.dylib.current_version); - } - } - if (exp.compat_version) |cv| { - if (cv != lc.inner.dylib.compatibility_version) { - return err("compatibility version mismatch", cv, lc.inner.dylib.compatibility_version); - } - } + + .ID_DYLIB, + .LOAD_DYLIB, + => { + const dylib = lc.dylib.inner.dylib; + try writer.writeByte('\n'); + try writer.print( + \\path {s} + \\timestamp {d} + \\current version {x} + \\compatibility version {x} + , .{ + mem.sliceTo(lc.dylib.data, 0), + dylib.timestamp, + dylib.current_version, + dylib.compatibility_version, + }); }, + .RPATH => { - const lc = given.rpath; - if (exp.name) |name| { - if (!mem.eql(u8, name, mem.sliceTo(lc.data, 0))) { - return err("rpath path mismatch", name, mem.sliceTo(lc.data, 0)); - } - } + try writer.writeByte('\n'); + try writer.print( + \\path {s} + , .{ + mem.sliceTo(lc.rpath.data, 0), + }); }, - else => @panic("TODO compare more load commands"), - } -} - -fn err(msg: []const u8, exp: anytype, giv: anytype) error{TestFailed} { - const fmt_specifier = if (comptime isString(@TypeOf(exp))) "{s}" else switch (@typeInfo(@TypeOf(exp))) { - .Int => "{x}", - .Float => "{d}", - else => "{any}", - }; - std.debug.print( - \\===================================== - \\{s} - \\ - \\======== Expected to find: ========== - \\ - ++ fmt_specifier ++ - \\ - \\======== But instead found: ========= - \\ - ++ fmt_specifier ++ - \\ - \\ - , .{ msg, exp, giv }); - return error.TestFailed; -} - -fn isString(comptime T: type) bool { - switch (@typeInfo(T)) { - .Array => return std.meta.Elem(T) == u8, - .Pointer => |pinfo| { - switch (pinfo.size) { - .Slice, .Many => return std.meta.Elem(T) == u8, - else => switch (@typeInfo(pinfo.child)) { - .Array => return isString(pinfo.child), - else => return false, - }, - } - }, - else => return false, + + else => {}, } } diff --git a/test/link/dylib/build.zig b/test/link/dylib/build.zig index 9878f9a66c..4f41e204ec 100644 --- a/test/link/dylib/build.zig +++ b/test/link/dylib/build.zig @@ -13,17 +13,14 @@ pub fn build(b: *Builder) void { dylib.linkLibC(); dylib.install(); - { - const check_macho = dylib.checkMachO(); - check_macho.checkLoadCommand(.{ - .cmd = std.macho.LC.ID_DYLIB, - .name = "@rpath/liba.dylib", - .timestamp = 2, - .current_version = 0x10000, - .compat_version = 0x10000, - }); - test_step.dependOn(&check_macho.step); - } + const check_dylib = dylib.checkMachO(); + check_dylib.check("cmd ID_DYLIB"); + check_dylib.checkNext("path @rpath/liba.dylib"); + check_dylib.checkNext("timestamp 2"); + check_dylib.checkNext("current version 10000"); + check_dylib.checkNext("compatibility version 10000"); + + test_step.dependOn(&check_dylib.step); const exe = b.addExecutable("main", null); exe.setBuildMode(mode); @@ -33,25 +30,17 @@ pub fn build(b: *Builder) void { exe.addLibraryPath(b.pathFromRoot("zig-out/lib/")); exe.addRPath(b.pathFromRoot("zig-out/lib")); - { - const check_macho = exe.checkMachO(); - check_macho.checkLoadCommand(.{ - .cmd = std.macho.LC.LOAD_DYLIB, - .name = "@rpath/liba.dylib", - .timestamp = 2, - .current_version = 0x10000, - .compat_version = 0x10000, - }); - test_step.dependOn(&check_macho.step); - } - { - const check_macho = exe.checkMachO(); - check_macho.checkLoadCommand(.{ - .cmd = std.macho.LC.RPATH, - .name = b.pathFromRoot("zig-out/lib"), - }); - test_step.dependOn(&check_macho.step); - } + const check_exe = exe.checkMachO(); + check_exe.check("cmd LOAD_DYLIB"); + check_exe.checkNext("path @rpath/liba.dylib"); + check_exe.checkNext("timestamp 2"); + check_exe.checkNext("current version 10000"); + check_exe.checkNext("compatibility version 10000"); + + check_exe.check("cmd RPATH"); + check_exe.checkNext(std.fmt.allocPrint(b.allocator, "path {s}", .{b.pathFromRoot("zig-out/lib")}) catch unreachable); + + test_step.dependOn(&check_exe.step); const run = exe.run(); run.cwd = b.pathFromRoot("."); diff --git a/test/link/pagezero/build.zig b/test/link/pagezero/build.zig index 71068b4480..05bc4052fe 100644 --- a/test/link/pagezero/build.zig +++ b/test/link/pagezero/build.zig @@ -14,22 +14,16 @@ pub fn build(b: *Builder) void { exe.linkLibC(); exe.pagezero_size = 0x4000; - const check_macho = exe.checkMachO(); - check_macho.checkLoadCommand(.{ - .cmd = std.macho.LC.SEGMENT_64, - .index = 0, - .name = "__PAGEZERO", - .vaddr = 0, - .memsz = 0x4000, - }); - check_macho.checkLoadCommand(.{ - .cmd = std.macho.LC.SEGMENT_64, - .index = 1, - .name = "__TEXT", - .vaddr = 0x4000, - }); + const check = exe.checkMachO(); + check.check("LC 0"); + check.checkNext("segname __PAGEZERO"); + check.checkNext("vmaddr 0"); + check.checkNext("vmsize 4000"); - test_step.dependOn(&check_macho.step); + check.check("segname __TEXT"); + check.checkNext("vmaddr 4000"); + + test_step.dependOn(&check.step); } { @@ -39,14 +33,11 @@ pub fn build(b: *Builder) void { exe.linkLibC(); exe.pagezero_size = 0; - const check_macho = exe.checkMachO(); - check_macho.checkLoadCommand(.{ - .cmd = std.macho.LC.SEGMENT_64, - .index = 0, - .name = "__TEXT", - .vaddr = 0, - }); + const check = exe.checkMachO(); + check.check("LC 0"); + check.checkNext("segname __TEXT"); + check.checkNext("vmaddr 0"); - test_step.dependOn(&check_macho.step); + test_step.dependOn(&check.step); } } From 3bb4d65b2f8d7cbaf1586b85909c7e45f6b5eec2 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 21 Jun 2022 23:01:06 +0200 Subject: [PATCH 1892/2031] link-tests: move macho tests to subfolder Handle `-e` option in MachO linker allowing the user to set a custom entrypoint address. --- lib/std/build/CheckMachOStep.zig | 8 +++++++ src/link/MachO.zig | 12 +++++++---- test/link.zig | 24 ++++++++++++++------- test/link/{ => macho}/dylib/a.c | 0 test/link/{ => macho}/dylib/build.zig | 0 test/link/{ => macho}/dylib/main.c | 0 test/link/macho/entry/build.zig | 25 ++++++++++++++++++++++ test/link/macho/entry/main.c | 6 ++++++ test/link/{ => macho}/frameworks/build.zig | 0 test/link/{ => macho}/frameworks/main.c | 0 test/link/{ => macho}/objc/Foo.h | 0 test/link/{ => macho}/objc/Foo.m | 0 test/link/{ => macho}/objc/build.zig | 0 test/link/{ => macho}/objc/test.m | 0 test/link/{ => macho}/objcpp/Foo.h | 0 test/link/{ => macho}/objcpp/Foo.mm | 0 test/link/{ => macho}/objcpp/build.zig | 0 test/link/{ => macho}/objcpp/test.mm | 0 test/link/{ => macho}/pagezero/build.zig | 0 test/link/{ => macho}/pagezero/main.c | 0 test/link/macho/stack_size/build.zig | 24 +++++++++++++++++++++ test/link/macho/stack_size/main.c | 3 +++ 22 files changed, 90 insertions(+), 12 deletions(-) rename test/link/{ => macho}/dylib/a.c (100%) rename test/link/{ => macho}/dylib/build.zig (100%) rename test/link/{ => macho}/dylib/main.c (100%) create mode 100644 test/link/macho/entry/build.zig create mode 100644 test/link/macho/entry/main.c rename test/link/{ => macho}/frameworks/build.zig (100%) rename test/link/{ => macho}/frameworks/main.c (100%) rename test/link/{ => macho}/objc/Foo.h (100%) rename test/link/{ => macho}/objc/Foo.m (100%) rename test/link/{ => macho}/objc/build.zig (100%) rename test/link/{ => macho}/objc/test.m (100%) rename test/link/{ => macho}/objcpp/Foo.h (100%) rename test/link/{ => macho}/objcpp/Foo.mm (100%) rename test/link/{ => macho}/objcpp/build.zig (100%) rename test/link/{ => macho}/objcpp/test.mm (100%) rename test/link/{ => macho}/pagezero/build.zig (100%) rename test/link/{ => macho}/pagezero/main.c (100%) create mode 100644 test/link/macho/stack_size/build.zig create mode 100644 test/link/macho/stack_size/main.c diff --git a/lib/std/build/CheckMachOStep.zig b/lib/std/build/CheckMachOStep.zig index b58cfa6e46..06e79081f5 100644 --- a/lib/std/build/CheckMachOStep.zig +++ b/lib/std/build/CheckMachOStep.zig @@ -156,6 +156,14 @@ fn dumpLoadCommand(lc: macho.LoadCommand, index: u16, writer: anytype) !void { }); }, + .MAIN => { + try writer.writeByte('\n'); + try writer.print( + \\entryoff {x} + \\stacksize {x} + , .{ lc.main.entryoff, lc.main.stacksize }); + }, + .RPATH => { try writer.writeByte('\n'); try writer.print( diff --git a/src/link/MachO.zig b/src/link/MachO.zig index aea44d9357..af84ce8846 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -934,6 +934,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size})); } + if (self.base.options.entry) |entry| { + try argv.append("-e"); + try argv.append(entry); + } + try argv.appendSlice(positionals.items); try argv.append("-o"); @@ -3371,13 +3376,12 @@ fn addCodeSignatureLC(self: *MachO) !void { fn setEntryPoint(self: *MachO) !void { if (self.base.options.output_mode != .Exe) return; - // TODO we should respect the -entry flag passed in by the user to set a custom - // entrypoint. For now, assume default of `_main`. const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment; - const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "_main"), StringIndexAdapter{ + const entry_name = self.base.options.entry orelse "_main"; + const n_strx = self.strtab_dir.getKeyAdapted(entry_name, StringIndexAdapter{ .bytes = &self.strtab, }) orelse { - log.err("'_main' export not found", .{}); + log.err("entrypoint '{s}' not found", .{entry_name}); return error.MissingMainEntrypoint; }; const resolv = self.symbol_resolver.get(n_strx) orelse unreachable; diff --git a/test/link.zig b/test/link.zig index bc69003e68..e3dcfa1ec6 100644 --- a/test/link.zig +++ b/test/link.zig @@ -28,29 +28,37 @@ pub fn addCases(cases: *tests.StandaloneContext) void { }); if (builtin.os.tag == .macos) { - cases.addBuildFile("test/link/pagezero/build.zig", .{ - .build_modes = false, - }); - - cases.addBuildFile("test/link/dylib/build.zig", .{ + cases.addBuildFile("test/link/entry/build.zig", .{ .build_modes = true, }); - cases.addBuildFile("test/link/frameworks/build.zig", .{ + cases.addBuildFile("test/link/macho/pagezero/build.zig", .{ + .build_modes = false, + }); + + cases.addBuildFile("test/link/macho/dylib/build.zig", .{ + .build_modes = true, + }); + + cases.addBuildFile("test/link/macho/frameworks/build.zig", .{ .build_modes = true, .requires_macos_sdk = true, }); // Try to build and run an Objective-C executable. - cases.addBuildFile("test/link/objc/build.zig", .{ + cases.addBuildFile("test/link/macho/objc/build.zig", .{ .build_modes = true, .requires_macos_sdk = true, }); // Try to build and run an Objective-C++ executable. - cases.addBuildFile("test/link/objcpp/build.zig", .{ + cases.addBuildFile("test/link/macho/objcpp/build.zig", .{ .build_modes = true, .requires_macos_sdk = true, }); + + cases.addBuildFile("test/link/macho/stack_size/build.zig", .{ + .build_modes = true, + }); } } diff --git a/test/link/dylib/a.c b/test/link/macho/dylib/a.c similarity index 100% rename from test/link/dylib/a.c rename to test/link/macho/dylib/a.c diff --git a/test/link/dylib/build.zig b/test/link/macho/dylib/build.zig similarity index 100% rename from test/link/dylib/build.zig rename to test/link/macho/dylib/build.zig diff --git a/test/link/dylib/main.c b/test/link/macho/dylib/main.c similarity index 100% rename from test/link/dylib/main.c rename to test/link/macho/dylib/main.c diff --git a/test/link/macho/entry/build.zig b/test/link/macho/entry/build.zig new file mode 100644 index 0000000000..24037ba8eb --- /dev/null +++ b/test/link/macho/entry/build.zig @@ -0,0 +1,25 @@ +const std = @import("std"); +const Builder = std.build.Builder; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + + const test_step = b.step("test", "Test"); + test_step.dependOn(b.getInstallStep()); + + const exe = b.addExecutable("main", null); + exe.setBuildMode(mode); + exe.addCSourceFile("main.c", &.{}); + exe.linkLibC(); + exe.entry_symbol_name = "_non_main"; + + const check_exe = exe.checkMachO(); + check_exe.check("cmd MAIN"); + check_exe.checkNext("entryoff {x}"); + + test_step.dependOn(&check_exe.step); + + const run = exe.run(); + run.expectStdOutEqual("42"); + test_step.dependOn(&run.step); +} diff --git a/test/link/macho/entry/main.c b/test/link/macho/entry/main.c new file mode 100644 index 0000000000..5fc58fc465 --- /dev/null +++ b/test/link/macho/entry/main.c @@ -0,0 +1,6 @@ +#include + +int non_main() { + printf("%d", 42); + return 0; +} diff --git a/test/link/frameworks/build.zig b/test/link/macho/frameworks/build.zig similarity index 100% rename from test/link/frameworks/build.zig rename to test/link/macho/frameworks/build.zig diff --git a/test/link/frameworks/main.c b/test/link/macho/frameworks/main.c similarity index 100% rename from test/link/frameworks/main.c rename to test/link/macho/frameworks/main.c diff --git a/test/link/objc/Foo.h b/test/link/macho/objc/Foo.h similarity index 100% rename from test/link/objc/Foo.h rename to test/link/macho/objc/Foo.h diff --git a/test/link/objc/Foo.m b/test/link/macho/objc/Foo.m similarity index 100% rename from test/link/objc/Foo.m rename to test/link/macho/objc/Foo.m diff --git a/test/link/objc/build.zig b/test/link/macho/objc/build.zig similarity index 100% rename from test/link/objc/build.zig rename to test/link/macho/objc/build.zig diff --git a/test/link/objc/test.m b/test/link/macho/objc/test.m similarity index 100% rename from test/link/objc/test.m rename to test/link/macho/objc/test.m diff --git a/test/link/objcpp/Foo.h b/test/link/macho/objcpp/Foo.h similarity index 100% rename from test/link/objcpp/Foo.h rename to test/link/macho/objcpp/Foo.h diff --git a/test/link/objcpp/Foo.mm b/test/link/macho/objcpp/Foo.mm similarity index 100% rename from test/link/objcpp/Foo.mm rename to test/link/macho/objcpp/Foo.mm diff --git a/test/link/objcpp/build.zig b/test/link/macho/objcpp/build.zig similarity index 100% rename from test/link/objcpp/build.zig rename to test/link/macho/objcpp/build.zig diff --git a/test/link/objcpp/test.mm b/test/link/macho/objcpp/test.mm similarity index 100% rename from test/link/objcpp/test.mm rename to test/link/macho/objcpp/test.mm diff --git a/test/link/pagezero/build.zig b/test/link/macho/pagezero/build.zig similarity index 100% rename from test/link/pagezero/build.zig rename to test/link/macho/pagezero/build.zig diff --git a/test/link/pagezero/main.c b/test/link/macho/pagezero/main.c similarity index 100% rename from test/link/pagezero/main.c rename to test/link/macho/pagezero/main.c diff --git a/test/link/macho/stack_size/build.zig b/test/link/macho/stack_size/build.zig new file mode 100644 index 0000000000..10b5a3f83d --- /dev/null +++ b/test/link/macho/stack_size/build.zig @@ -0,0 +1,24 @@ +const std = @import("std"); +const Builder = std.build.Builder; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + + const test_step = b.step("test", "Test"); + test_step.dependOn(b.getInstallStep()); + + const exe = b.addExecutable("main", null); + exe.setBuildMode(mode); + exe.addCSourceFile("main.c", &.{}); + exe.linkLibC(); + exe.stack_size = 0x100000000; + + const check_exe = exe.checkMachO(); + check_exe.check("cmd MAIN"); + check_exe.checkNext("stacksize 100000000"); + + test_step.dependOn(&check_exe.step); + + const run = exe.run(); + test_step.dependOn(&run.step); +} diff --git a/test/link/macho/stack_size/main.c b/test/link/macho/stack_size/main.c new file mode 100644 index 0000000000..ca68d24cc7 --- /dev/null +++ b/test/link/macho/stack_size/main.c @@ -0,0 +1,3 @@ +int main(int argc, char* argv[]) { + return 0; +} From b5601a2da60df2f8f2bc6ac1ef287d4733a47df2 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 22 Jun 2022 00:49:20 +0200 Subject: [PATCH 1893/2031] link-tests: extract values into variables We can then collect multiple variables (currently assumed always in global scope) and run a comparison with some very basic arithmetic on the values. --- lib/std/build/CheckMachOStep.zig | 191 ++++++++++++++++++++++++++++--- test/link/macho/entry/build.zig | 11 +- 2 files changed, 182 insertions(+), 20 deletions(-) diff --git a/lib/std/build/CheckMachOStep.zig b/lib/std/build/CheckMachOStep.zig index 06e79081f5..a4cd35e95a 100644 --- a/lib/std/build/CheckMachOStep.zig +++ b/lib/std/build/CheckMachOStep.zig @@ -18,6 +18,7 @@ builder: *Builder, source: build.FileSource, max_bytes: usize = 20 * 1024 * 1024, checks: std.ArrayList(Check), +dump_symtab: bool = false, pub fn create(builder: *Builder, source: build.FileSource) *CheckMachOStep { const gpa = builder.allocator; @@ -32,32 +33,107 @@ pub fn create(builder: *Builder, source: build.FileSource) *CheckMachOStep { return self; } +const Action = union(enum) { + exact_match: []const u8, + extract_var: struct { + fuzzy_match: []const u8, + var_name: []const u8, + var_value: u64, + }, + compare: CompareAction, +}; + +const CompareAction = struct { + expected: union(enum) { + literal: u64, + varr: []const u8, + }, + var_stack: std.ArrayList([]const u8), + op_stack: std.ArrayList(Op), + + const Op = enum { + add, + }; +}; + const Check = struct { builder: *Builder, - phrases: std.ArrayList([]const u8), + actions: std.ArrayList(Action), fn create(b: *Builder) Check { return .{ .builder = b, - .phrases = std.ArrayList([]const u8).init(b.allocator), + .actions = std.ArrayList(Action).init(b.allocator), }; } - fn addPhrase(self: *Check, phrase: []const u8) void { - self.phrases.append(self.builder.dupe(phrase)) catch unreachable; + fn exactMatch(self: *Check, phrase: []const u8) void { + self.actions.append(.{ + .exact_match = self.builder.dupe(phrase), + }) catch unreachable; + } + + fn extractVar(self: *Check, phrase: []const u8, var_name: []const u8) void { + self.actions.append(.{ + .extract_var = .{ + .fuzzy_match = self.builder.dupe(phrase), + .var_name = self.builder.dupe(var_name), + .var_value = undefined, + }, + }) catch unreachable; } }; pub fn check(self: *CheckMachOStep, phrase: []const u8) void { var new_check = Check.create(self.builder); - new_check.addPhrase(phrase); + new_check.exactMatch(phrase); self.checks.append(new_check) catch unreachable; } pub fn checkNext(self: *CheckMachOStep, phrase: []const u8) void { assert(self.checks.items.len > 0); const last = &self.checks.items[self.checks.items.len - 1]; - last.addPhrase(phrase); + last.exactMatch(phrase); +} + +pub fn checkNextExtract(self: *CheckMachOStep, comptime phrase: []const u8) void { + assert(self.checks.items.len > 0); + const matcher_start = comptime mem.indexOf(u8, phrase, "{") orelse + @compileError("missing { } matcher"); + const matcher_end = comptime mem.indexOf(u8, phrase, "}") orelse + @compileError("missing { } matcher"); + const last = &self.checks.items[self.checks.items.len - 1]; + last.extractVar(phrase[0..matcher_start], phrase[matcher_start + 1 .. matcher_end]); +} + +pub fn checkInSymtab(self: *CheckMachOStep) void { + self.dump_symtab = true; + self.check("symtab"); +} + +pub fn checkCompare(self: *CheckMachOStep, comptime phrase: []const u8, expected: anytype) void { + comptime assert(phrase[0] == '{'); + comptime assert(phrase[phrase.len - 1] == '}'); + + const gpa = self.builder.allocator; + var ca = CompareAction{ + .expected = expected, + .var_stack = std.ArrayList([]const u8).init(gpa), + .op_stack = std.ArrayList(CompareAction.Op).init(gpa), + }; + + var it = mem.tokenize(u8, phrase[1 .. phrase.len - 1], " "); + while (it.next()) |next| { + if (mem.eql(u8, next, "+")) { + ca.op_stack.append(.add) catch unreachable; + } else { + ca.var_stack.append(self.builder.dupe(next)) catch unreachable; + } + } + + var new_check = Check.create(self.builder); + new_check.actions.append(.{ .compare = ca }) catch unreachable; + self.checks.append(new_check) catch unreachable; } fn make(step: *Step) !void { @@ -79,35 +155,112 @@ fn make(step: *Step) !void { var metadata = std.ArrayList(u8).init(gpa); const writer = metadata.writer(); + var symtab_cmd: ?macho.symtab_command = null; var i: u16 = 0; while (i < hdr.ncmds) : (i += 1) { var cmd = try macho.LoadCommand.read(gpa, reader); + + if (self.dump_symtab and cmd.cmd() == .SYMTAB) { + symtab_cmd = cmd.symtab; + } + try dumpLoadCommand(cmd, i, writer); try writer.writeByte('\n'); } + if (symtab_cmd) |cmd| { + try writer.writeAll("symtab\n"); + const strtab = contents[cmd.stroff..][0..cmd.strsize]; + const symtab = @ptrCast( + [*]const macho.nlist_64, + @alignCast(@alignOf(macho.nlist_64), contents.ptr + cmd.symoff), + )[0..cmd.nsyms]; + + for (symtab) |sym| { + if (sym.stab()) continue; + const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0); + try writer.print("{s} {x}\n", .{ sym_name, sym.n_value }); + } + } + + var vars = std.StringHashMap(u64).init(gpa); + for (self.checks.items) |chk| { - const first_phrase = chk.phrases.items[0]; + const first_action = chk.actions.items[0]; - if (mem.indexOf(u8, metadata.items, first_phrase)) |index| { - // TODO backtrack to track current scope - var it = std.mem.tokenize(u8, metadata.items[index..], "\r\n"); + switch (first_action) { + .exact_match => |first| { + if (mem.indexOf(u8, metadata.items, first)) |index| { + // TODO backtrack to track current scope + var it = std.mem.tokenize(u8, metadata.items[index..], "\r\n"); - outer: for (chk.phrases.items[1..]) |next_phrase| { - while (it.next()) |line| { - if (mem.eql(u8, line, next_phrase)) { - std.debug.print("{s} == {s}\n", .{ line, next_phrase }); - continue :outer; + outer: for (chk.actions.items[1..]) |next_action| { + switch (next_action) { + .exact_match => |exact| { + while (it.next()) |line| { + if (mem.eql(u8, line, exact)) { + std.debug.print("{s} == {s}\n", .{ line, exact }); + continue :outer; + } + std.debug.print("{s} != {s}\n", .{ line, exact }); + } else { + return error.TestFailed; + } + }, + .extract_var => |extract| { + const phrase = extract.fuzzy_match; + while (it.next()) |line| { + if (mem.indexOf(u8, line, phrase)) |found| { + std.debug.print("{s} in {s}\n", .{ phrase, line }); + // Extract variable and save back in the action. + const trimmed = mem.trim(u8, line[found + phrase.len ..], " "); + const parsed = try std.fmt.parseInt(u64, trimmed, 16); + try vars.putNoClobber(extract.var_name, parsed); + continue :outer; + } + std.debug.print("{s} not in {s}\n", .{ extract.fuzzy_match, line }); + } + }, + .compare => unreachable, + } } - std.debug.print("{s} != {s}\n", .{ line, next_phrase }); } else { return error.TestFailed; } - } - } else { - return error.TestFailed; + }, + .compare => |act| { + var values = std.ArrayList(u64).init(gpa); + try values.ensureTotalCapacity(act.var_stack.items.len); + for (act.var_stack.items) |vv| { + const val = vars.get(vv) orelse return error.TestFailed; + values.appendAssumeCapacity(val); + } + + var op_i: usize = 1; + var reduced: u64 = values.items[0]; + for (act.op_stack.items) |op| { + const other = values.items[op_i]; + switch (op) { + .add => { + reduced += other; + }, + } + } + + const expected = switch (act.expected) { + .literal => |exp| exp, + .varr => |vv| vars.get(vv) orelse return error.TestFailed, + }; + if (reduced != expected) return error.TestFailed; + }, + .extract_var => unreachable, } } + + var it = vars.iterator(); + while (it.next()) |entry| { + std.debug.print(" {s} => {x}", .{ entry.key_ptr.*, entry.value_ptr.* }); + } } fn dumpLoadCommand(lc: macho.LoadCommand, index: u16, writer: anytype) !void { diff --git a/test/link/macho/entry/build.zig b/test/link/macho/entry/build.zig index 24037ba8eb..e8c0901b20 100644 --- a/test/link/macho/entry/build.zig +++ b/test/link/macho/entry/build.zig @@ -14,8 +14,17 @@ pub fn build(b: *Builder) void { exe.entry_symbol_name = "_non_main"; const check_exe = exe.checkMachO(); + + check_exe.check("segname __TEXT"); + check_exe.checkNextExtract("vmaddr {vmaddr}"); + check_exe.check("cmd MAIN"); - check_exe.checkNext("entryoff {x}"); + check_exe.checkNextExtract("entryoff {entryoff}"); + + check_exe.checkInSymtab(); + check_exe.checkNextExtract("_non_main {n_value}"); + + check_exe.checkCompare("{vmaddr entryoff +}", .{ .varr = "n_value" }); test_step.dependOn(&check_exe.step); From 23a63f4ce445d26d7fc577eecc6c3f5ca129a007 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 22 Jun 2022 10:27:51 +0200 Subject: [PATCH 1894/2031] link-tests: rename CheckMachOStep to CheckObjectStep and accept obj format --- lib/std/build.zig | 8 +- ...CheckMachOStep.zig => CheckObjectStep.zig} | 250 ++++++++++-------- test/link.zig | 2 +- test/link/macho/dylib/build.zig | 4 +- test/link/macho/entry/build.zig | 2 +- test/link/macho/pagezero/build.zig | 4 +- test/link/macho/stack_size/build.zig | 2 +- 7 files changed, 148 insertions(+), 124 deletions(-) rename lib/std/build/{CheckMachOStep.zig => CheckObjectStep.zig} (59%) diff --git a/lib/std/build.zig b/lib/std/build.zig index 4582a0658d..57304242cd 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -24,7 +24,7 @@ pub const TranslateCStep = @import("build/TranslateCStep.zig"); pub const WriteFileStep = @import("build/WriteFileStep.zig"); pub const RunStep = @import("build/RunStep.zig"); pub const CheckFileStep = @import("build/CheckFileStep.zig"); -pub const CheckMachOStep = @import("build/CheckMachOStep.zig"); +pub const CheckObjectStep = @import("build/CheckObjectStep.zig"); pub const InstallRawStep = @import("build/InstallRawStep.zig"); pub const OptionsStep = @import("build/OptionsStep.zig"); @@ -1865,8 +1865,8 @@ pub const LibExeObjStep = struct { return run_step; } - pub fn checkMachO(self: *LibExeObjStep) *CheckMachOStep { - return CheckMachOStep.create(self.builder, self.getOutputSource()); + pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { + return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format); } pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void { @@ -3455,7 +3455,7 @@ pub const Step = struct { write_file, run, check_file, - check_macho, + check_object, install_raw, options, custom, diff --git a/lib/std/build/CheckMachOStep.zig b/lib/std/build/CheckObjectStep.zig similarity index 59% rename from lib/std/build/CheckMachOStep.zig rename to lib/std/build/CheckObjectStep.zig index a4cd35e95a..dae17b93db 100644 --- a/lib/std/build/CheckMachOStep.zig +++ b/lib/std/build/CheckObjectStep.zig @@ -5,13 +5,13 @@ const fs = std.fs; const macho = std.macho; const mem = std.mem; -const CheckMachOStep = @This(); +const CheckObjectStep = @This(); const Allocator = mem.Allocator; const Builder = build.Builder; const Step = build.Step; -pub const base_id = .check_macho; +pub const base_id = .check_obj; step: Step, builder: *Builder, @@ -19,15 +19,17 @@ source: build.FileSource, max_bytes: usize = 20 * 1024 * 1024, checks: std.ArrayList(Check), dump_symtab: bool = false, +obj_format: std.Target.ObjectFormat, -pub fn create(builder: *Builder, source: build.FileSource) *CheckMachOStep { +pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep { const gpa = builder.allocator; - const self = gpa.create(CheckMachOStep) catch unreachable; - self.* = CheckMachOStep{ + const self = gpa.create(CheckObjectStep) catch unreachable; + self.* = .{ .builder = builder, - .step = Step.init(.check_file, "CheckMachO", gpa, make), + .step = Step.init(.check_file, "CheckObject", gpa, make), .source = source.dupe(builder), .checks = std.ArrayList(Check).init(gpa), + .obj_format = obj_format, }; self.source.addStepDependencies(&self.step); return self; @@ -84,19 +86,19 @@ const Check = struct { } }; -pub fn check(self: *CheckMachOStep, phrase: []const u8) void { +pub fn check(self: *CheckObjectStep, phrase: []const u8) void { var new_check = Check.create(self.builder); new_check.exactMatch(phrase); self.checks.append(new_check) catch unreachable; } -pub fn checkNext(self: *CheckMachOStep, phrase: []const u8) void { +pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void { assert(self.checks.items.len > 0); const last = &self.checks.items[self.checks.items.len - 1]; last.exactMatch(phrase); } -pub fn checkNextExtract(self: *CheckMachOStep, comptime phrase: []const u8) void { +pub fn checkNextExtract(self: *CheckObjectStep, comptime phrase: []const u8) void { assert(self.checks.items.len > 0); const matcher_start = comptime mem.indexOf(u8, phrase, "{") orelse @compileError("missing { } matcher"); @@ -106,12 +108,12 @@ pub fn checkNextExtract(self: *CheckMachOStep, comptime phrase: []const u8) void last.extractVar(phrase[0..matcher_start], phrase[matcher_start + 1 .. matcher_end]); } -pub fn checkInSymtab(self: *CheckMachOStep) void { +pub fn checkInSymtab(self: *CheckObjectStep) void { self.dump_symtab = true; self.check("symtab"); } -pub fn checkCompare(self: *CheckMachOStep, comptime phrase: []const u8, expected: anytype) void { +pub fn checkCompare(self: *CheckObjectStep, comptime phrase: []const u8, expected: anytype) void { comptime assert(phrase[0] == '{'); comptime assert(phrase[phrase.len - 1] == '}'); @@ -137,51 +139,22 @@ pub fn checkCompare(self: *CheckMachOStep, comptime phrase: []const u8, expected } fn make(step: *Step) !void { - const self = @fieldParentPtr(CheckMachOStep, "step", step); + const self = @fieldParentPtr(CheckObjectStep, "step", step); const gpa = self.builder.allocator; const src_path = self.source.getPath(self.builder); const contents = try fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); - // Parse the object file's header - var stream = std.io.fixedBufferStream(contents); - const reader = stream.reader(); - - const hdr = try reader.readStruct(macho.mach_header_64); - if (hdr.magic != macho.MH_MAGIC_64) { - return error.InvalidMagicNumber; - } - - var metadata = std.ArrayList(u8).init(gpa); - const writer = metadata.writer(); - - var symtab_cmd: ?macho.symtab_command = null; - var i: u16 = 0; - while (i < hdr.ncmds) : (i += 1) { - var cmd = try macho.LoadCommand.read(gpa, reader); - - if (self.dump_symtab and cmd.cmd() == .SYMTAB) { - symtab_cmd = cmd.symtab; - } - - try dumpLoadCommand(cmd, i, writer); - try writer.writeByte('\n'); - } - - if (symtab_cmd) |cmd| { - try writer.writeAll("symtab\n"); - const strtab = contents[cmd.stroff..][0..cmd.strsize]; - const symtab = @ptrCast( - [*]const macho.nlist_64, - @alignCast(@alignOf(macho.nlist_64), contents.ptr + cmd.symoff), - )[0..cmd.nsyms]; - - for (symtab) |sym| { - if (sym.stab()) continue; - const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0); - try writer.print("{s} {x}\n", .{ sym_name, sym.n_value }); - } - } + const output = switch (self.obj_format) { + .macho => try MachODumper.parseAndDump(contents, .{ + .gpa = gpa, + .dump_symtab = self.dump_symtab, + }), + .elf => @panic("TODO elf parser"), + .coff => @panic("TODO coff parser"), + .wasm => @panic("TODO wasm parser"), + else => unreachable, + }; var vars = std.StringHashMap(u64).init(gpa); @@ -190,9 +163,9 @@ fn make(step: *Step) !void { switch (first_action) { .exact_match => |first| { - if (mem.indexOf(u8, metadata.items, first)) |index| { + if (mem.indexOf(u8, output, first)) |index| { // TODO backtrack to track current scope - var it = std.mem.tokenize(u8, metadata.items[index..], "\r\n"); + var it = std.mem.tokenize(u8, output[index..], "\r\n"); outer: for (chk.actions.items[1..]) |next_action| { switch (next_action) { @@ -263,69 +236,120 @@ fn make(step: *Step) !void { } } -fn dumpLoadCommand(lc: macho.LoadCommand, index: u16, writer: anytype) !void { - // print header first - try writer.print( - \\LC {d} - \\cmd {s} - \\cmdsize {d} - , .{ index, @tagName(lc.cmd()), lc.cmdsize() }); +const Opts = struct { + gpa: ?Allocator = null, + dump_symtab: bool = false, +}; - switch (lc.cmd()) { - .SEGMENT_64 => { - // TODO dump section headers - const seg = lc.segment.inner; +const MachODumper = struct { + fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 { + const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator + var stream = std.io.fixedBufferStream(bytes); + const reader = stream.reader(); + + const hdr = try reader.readStruct(macho.mach_header_64); + if (hdr.magic != macho.MH_MAGIC_64) { + return error.InvalidMagicNumber; + } + + var output = std.ArrayList(u8).init(gpa); + const writer = output.writer(); + + var symtab_cmd: ?macho.symtab_command = null; + var i: u16 = 0; + while (i < hdr.ncmds) : (i += 1) { + var cmd = try macho.LoadCommand.read(gpa, reader); + + if (opts.dump_symtab and cmd.cmd() == .SYMTAB) { + symtab_cmd = cmd.symtab; + } + + try dumpLoadCommand(cmd, i, writer); try writer.writeByte('\n'); - try writer.print( - \\segname {s} - \\vmaddr {x} - \\vmsize {x} - \\fileoff {x} - \\filesz {x} - , .{ - seg.segName(), - seg.vmaddr, - seg.vmsize, - seg.fileoff, - seg.filesize, - }); - }, + } - .ID_DYLIB, - .LOAD_DYLIB, - => { - const dylib = lc.dylib.inner.dylib; - try writer.writeByte('\n'); - try writer.print( - \\path {s} - \\timestamp {d} - \\current version {x} - \\compatibility version {x} - , .{ - mem.sliceTo(lc.dylib.data, 0), - dylib.timestamp, - dylib.current_version, - dylib.compatibility_version, - }); - }, + if (symtab_cmd) |cmd| { + try writer.writeAll("symtab\n"); + const strtab = bytes[cmd.stroff..][0..cmd.strsize]; + const symtab = @ptrCast( + [*]const macho.nlist_64, + @alignCast(@alignOf(macho.nlist_64), bytes.ptr + cmd.symoff), + )[0..cmd.nsyms]; - .MAIN => { - try writer.writeByte('\n'); - try writer.print( - \\entryoff {x} - \\stacksize {x} - , .{ lc.main.entryoff, lc.main.stacksize }); - }, + for (symtab) |sym| { + if (sym.stab()) continue; + const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0); + try writer.print("{s} {x}\n", .{ sym_name, sym.n_value }); + } + } - .RPATH => { - try writer.writeByte('\n'); - try writer.print( - \\path {s} - , .{ - mem.sliceTo(lc.rpath.data, 0), - }); - }, - - else => {}, + return output.toOwnedSlice(); } -} + + fn dumpLoadCommand(lc: macho.LoadCommand, index: u16, writer: anytype) !void { + // print header first + try writer.print( + \\LC {d} + \\cmd {s} + \\cmdsize {d} + , .{ index, @tagName(lc.cmd()), lc.cmdsize() }); + + switch (lc.cmd()) { + .SEGMENT_64 => { + // TODO dump section headers + const seg = lc.segment.inner; + try writer.writeByte('\n'); + try writer.print( + \\segname {s} + \\vmaddr {x} + \\vmsize {x} + \\fileoff {x} + \\filesz {x} + , .{ + seg.segName(), + seg.vmaddr, + seg.vmsize, + seg.fileoff, + seg.filesize, + }); + }, + + .ID_DYLIB, + .LOAD_DYLIB, + => { + const dylib = lc.dylib.inner.dylib; + try writer.writeByte('\n'); + try writer.print( + \\path {s} + \\timestamp {d} + \\current version {x} + \\compatibility version {x} + , .{ + mem.sliceTo(lc.dylib.data, 0), + dylib.timestamp, + dylib.current_version, + dylib.compatibility_version, + }); + }, + + .MAIN => { + try writer.writeByte('\n'); + try writer.print( + \\entryoff {x} + \\stacksize {x} + , .{ lc.main.entryoff, lc.main.stacksize }); + }, + + .RPATH => { + try writer.writeByte('\n'); + try writer.print( + \\path {s} + , .{ + mem.sliceTo(lc.rpath.data, 0), + }); + }, + + else => {}, + } + } +}; diff --git a/test/link.zig b/test/link.zig index e3dcfa1ec6..42ce12f73c 100644 --- a/test/link.zig +++ b/test/link.zig @@ -28,7 +28,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { }); if (builtin.os.tag == .macos) { - cases.addBuildFile("test/link/entry/build.zig", .{ + cases.addBuildFile("test/link/macho/entry/build.zig", .{ .build_modes = true, }); diff --git a/test/link/macho/dylib/build.zig b/test/link/macho/dylib/build.zig index 4f41e204ec..a613f02b4a 100644 --- a/test/link/macho/dylib/build.zig +++ b/test/link/macho/dylib/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *Builder) void { dylib.linkLibC(); dylib.install(); - const check_dylib = dylib.checkMachO(); + const check_dylib = dylib.checkObject(.macho); check_dylib.check("cmd ID_DYLIB"); check_dylib.checkNext("path @rpath/liba.dylib"); check_dylib.checkNext("timestamp 2"); @@ -30,7 +30,7 @@ pub fn build(b: *Builder) void { exe.addLibraryPath(b.pathFromRoot("zig-out/lib/")); exe.addRPath(b.pathFromRoot("zig-out/lib")); - const check_exe = exe.checkMachO(); + const check_exe = exe.checkObject(.macho); check_exe.check("cmd LOAD_DYLIB"); check_exe.checkNext("path @rpath/liba.dylib"); check_exe.checkNext("timestamp 2"); diff --git a/test/link/macho/entry/build.zig b/test/link/macho/entry/build.zig index e8c0901b20..78225b0d73 100644 --- a/test/link/macho/entry/build.zig +++ b/test/link/macho/entry/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *Builder) void { exe.linkLibC(); exe.entry_symbol_name = "_non_main"; - const check_exe = exe.checkMachO(); + const check_exe = exe.checkObject(.macho); check_exe.check("segname __TEXT"); check_exe.checkNextExtract("vmaddr {vmaddr}"); diff --git a/test/link/macho/pagezero/build.zig b/test/link/macho/pagezero/build.zig index 05bc4052fe..a35be919c5 100644 --- a/test/link/macho/pagezero/build.zig +++ b/test/link/macho/pagezero/build.zig @@ -14,7 +14,7 @@ pub fn build(b: *Builder) void { exe.linkLibC(); exe.pagezero_size = 0x4000; - const check = exe.checkMachO(); + const check = exe.checkObject(.macho); check.check("LC 0"); check.checkNext("segname __PAGEZERO"); check.checkNext("vmaddr 0"); @@ -33,7 +33,7 @@ pub fn build(b: *Builder) void { exe.linkLibC(); exe.pagezero_size = 0; - const check = exe.checkMachO(); + const check = exe.checkObject(.macho); check.check("LC 0"); check.checkNext("segname __TEXT"); check.checkNext("vmaddr 0"); diff --git a/test/link/macho/stack_size/build.zig b/test/link/macho/stack_size/build.zig index 10b5a3f83d..b840f8928c 100644 --- a/test/link/macho/stack_size/build.zig +++ b/test/link/macho/stack_size/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *Builder) void { exe.linkLibC(); exe.stack_size = 0x100000000; - const check_exe = exe.checkMachO(); + const check_exe = exe.checkObject(.macho); check_exe.check("cmd MAIN"); check_exe.checkNext("stacksize 100000000"); From 211de9b63b53b7252a6321074ace9018cf392db2 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 22 Jun 2022 10:40:10 +0200 Subject: [PATCH 1895/2031] link-tests: fix dumping of LOAD_DYLIB: name instead of path field --- lib/std/build/CheckObjectStep.zig | 2 +- test/link/macho/dylib/build.zig | 4 ++-- test/link/macho/frameworks/build.zig | 8 ++++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/std/build/CheckObjectStep.zig b/lib/std/build/CheckObjectStep.zig index dae17b93db..4f9a7f0997 100644 --- a/lib/std/build/CheckObjectStep.zig +++ b/lib/std/build/CheckObjectStep.zig @@ -320,7 +320,7 @@ const MachODumper = struct { const dylib = lc.dylib.inner.dylib; try writer.writeByte('\n'); try writer.print( - \\path {s} + \\name {s} \\timestamp {d} \\current version {x} \\compatibility version {x} diff --git a/test/link/macho/dylib/build.zig b/test/link/macho/dylib/build.zig index a613f02b4a..ad116e23e9 100644 --- a/test/link/macho/dylib/build.zig +++ b/test/link/macho/dylib/build.zig @@ -15,7 +15,7 @@ pub fn build(b: *Builder) void { const check_dylib = dylib.checkObject(.macho); check_dylib.check("cmd ID_DYLIB"); - check_dylib.checkNext("path @rpath/liba.dylib"); + check_dylib.checkNext("name @rpath/liba.dylib"); check_dylib.checkNext("timestamp 2"); check_dylib.checkNext("current version 10000"); check_dylib.checkNext("compatibility version 10000"); @@ -32,7 +32,7 @@ pub fn build(b: *Builder) void { const check_exe = exe.checkObject(.macho); check_exe.check("cmd LOAD_DYLIB"); - check_exe.checkNext("path @rpath/liba.dylib"); + check_exe.checkNext("name @rpath/liba.dylib"); check_exe.checkNext("timestamp 2"); check_exe.checkNext("current version 10000"); check_exe.checkNext("compatibility version 10000"); diff --git a/test/link/macho/frameworks/build.zig b/test/link/macho/frameworks/build.zig index 5700422a41..f196f47b1b 100644 --- a/test/link/macho/frameworks/build.zig +++ b/test/link/macho/frameworks/build.zig @@ -11,10 +11,14 @@ pub fn build(b: *Builder) void { exe.addCSourceFile("main.c", &[0][]const u8{}); exe.setBuildMode(mode); exe.linkLibC(); - // TODO when we figure out how to ship framework stubs for cross-compilation, - // populate paths to the sysroot here. exe.linkFramework("Cocoa"); + const check = exe.checkObject(.macho); + check.check("cmd LOAD_DYLIB"); + check.checkNext("name /System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa"); + + test_step.dependOn(&check.step); + const run_cmd = exe.run(); test_step.dependOn(&run_cmd.step); } From 6de0c622ff770628110208f5edeff54940cf5caa Mon Sep 17 00:00:00 2001 From: frmdstryr Date: Tue, 21 Jun 2022 17:45:03 -0400 Subject: [PATCH 1896/2031] Add missing adddf3 ``` LLD Link... ld.lld: error: undefined symbol: __aeabi_dadd >>> referenced by errol.zig:366 (/home/usr/projects/zig/build/lib/zig/std/fmt/errol.zig:366) ``` --- lib/compiler_rt.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig index 5f199e9eae..3216fcc357 100644 --- a/lib/compiler_rt.zig +++ b/lib/compiler_rt.zig @@ -4,6 +4,7 @@ comptime { _ = @import("compiler_rt/atomics.zig"); _ = @import("compiler_rt/addf3.zig"); + _ = @import("compiler_rt/adddf3.zig"); _ = @import("compiler_rt/addsf3.zig"); _ = @import("compiler_rt/addtf3.zig"); _ = @import("compiler_rt/addxf3.zig"); From b35e434caeb7448a93c14119e73d7e54d3864337 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 22 Jun 2022 18:34:39 +0200 Subject: [PATCH 1897/2031] link-tests: clean up linker testing harness --- lib/std/build/CheckObjectStep.zig | 193 ++++++++++++--------------- test/link/macho/entry/build.zig | 8 +- test/link/macho/frameworks/build.zig | 5 +- 3 files changed, 97 insertions(+), 109 deletions(-) diff --git a/lib/std/build/CheckObjectStep.zig b/lib/std/build/CheckObjectStep.zig index 4f9a7f0997..40266d0c85 100644 --- a/lib/std/build/CheckObjectStep.zig +++ b/lib/std/build/CheckObjectStep.zig @@ -36,20 +36,52 @@ pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Targe } const Action = union(enum) { - exact_match: []const u8, - extract_var: struct { - fuzzy_match: []const u8, - var_name: []const u8, - var_value: u64, - }, - compare: CompareAction, + match: MatchAction, + compute_eq: ComputeEqAction, }; -const CompareAction = struct { - expected: union(enum) { - literal: u64, - varr: []const u8, - }, +const MatchAction = struct { + needle: []const u8, + + fn match(act: MatchAction, haystack: []const u8, global_vars: anytype) !bool { + var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " "); + var needle_it = mem.tokenize(u8, mem.trim(u8, act.needle, " "), " "); + + while (needle_it.next()) |needle_tok| { + const hay_tok = hay_it.next() orelse return false; + + if (mem.indexOf(u8, needle_tok, "{*}")) |index| { + // We have fuzzy matchers within the search pattern, so we match substrings. + var start = index; + var n_tok = needle_tok; + var h_tok = hay_tok; + while (true) { + n_tok = n_tok[start + 3 ..]; + const inner = if (mem.indexOf(u8, n_tok, "{*}")) |sub_end| + n_tok[0..sub_end] + else + n_tok; + if (mem.indexOf(u8, h_tok, inner) == null) return false; + start = mem.indexOf(u8, n_tok, "{*}") orelse break; + } + } else if (mem.startsWith(u8, needle_tok, "{")) { + const closing_brace = mem.indexOf(u8, needle_tok, "}") orelse return error.MissingClosingBrace; + if (closing_brace != needle_tok.len - 1) return error.ClosingBraceNotLast; + + const name = needle_tok[1..closing_brace]; + const value = try std.fmt.parseInt(u64, hay_tok, 16); + try global_vars.putNoClobber(name, value); + } else { + if (!mem.eql(u8, hay_tok, needle_tok)) return false; + } + } + + return true; + } +}; + +const ComputeEqAction = struct { + expected: []const u8, var_stack: std.ArrayList([]const u8), op_stack: std.ArrayList(Op), @@ -69,43 +101,29 @@ const Check = struct { }; } - fn exactMatch(self: *Check, phrase: []const u8) void { + fn match(self: *Check, needle: []const u8) void { self.actions.append(.{ - .exact_match = self.builder.dupe(phrase), + .match = .{ .needle = self.builder.dupe(needle) }, }) catch unreachable; } - fn extractVar(self: *Check, phrase: []const u8, var_name: []const u8) void { + fn computeEq(self: *Check, act: ComputeEqAction) void { self.actions.append(.{ - .extract_var = .{ - .fuzzy_match = self.builder.dupe(phrase), - .var_name = self.builder.dupe(var_name), - .var_value = undefined, - }, + .compute_eq = act, }) catch unreachable; } }; pub fn check(self: *CheckObjectStep, phrase: []const u8) void { var new_check = Check.create(self.builder); - new_check.exactMatch(phrase); + new_check.match(phrase); self.checks.append(new_check) catch unreachable; } pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void { assert(self.checks.items.len > 0); const last = &self.checks.items[self.checks.items.len - 1]; - last.exactMatch(phrase); -} - -pub fn checkNextExtract(self: *CheckObjectStep, comptime phrase: []const u8) void { - assert(self.checks.items.len > 0); - const matcher_start = comptime mem.indexOf(u8, phrase, "{") orelse - @compileError("missing { } matcher"); - const matcher_end = comptime mem.indexOf(u8, phrase, "}") orelse - @compileError("missing { } matcher"); - const last = &self.checks.items[self.checks.items.len - 1]; - last.extractVar(phrase[0..matcher_start], phrase[matcher_start + 1 .. matcher_end]); + last.match(phrase); } pub fn checkInSymtab(self: *CheckObjectStep) void { @@ -113,18 +131,15 @@ pub fn checkInSymtab(self: *CheckObjectStep) void { self.check("symtab"); } -pub fn checkCompare(self: *CheckObjectStep, comptime phrase: []const u8, expected: anytype) void { - comptime assert(phrase[0] == '{'); - comptime assert(phrase[phrase.len - 1] == '}'); - +pub fn checkComputeEq(self: *CheckObjectStep, program: []const u8, expected: []const u8) void { const gpa = self.builder.allocator; - var ca = CompareAction{ + var ca = ComputeEqAction{ .expected = expected, .var_stack = std.ArrayList([]const u8).init(gpa), - .op_stack = std.ArrayList(CompareAction.Op).init(gpa), + .op_stack = std.ArrayList(ComputeEqAction.Op).init(gpa), }; - var it = mem.tokenize(u8, phrase[1 .. phrase.len - 1], " "); + var it = mem.tokenize(u8, program, " "); while (it.next()) |next| { if (mem.eql(u8, next, "+")) { ca.op_stack.append(.add) catch unreachable; @@ -134,7 +149,7 @@ pub fn checkCompare(self: *CheckObjectStep, comptime phrase: []const u8, expecte } var new_check = Check.create(self.builder); - new_check.actions.append(.{ .compare = ca }) catch unreachable; + new_check.computeEq(ca); self.checks.append(new_check) catch unreachable; } @@ -159,80 +174,50 @@ fn make(step: *Step) !void { var vars = std.StringHashMap(u64).init(gpa); for (self.checks.items) |chk| { - const first_action = chk.actions.items[0]; + var it = mem.tokenize(u8, output, "\r\n"); + for (chk.actions.items) |act| { + switch (act) { + .match => |match_act| { + while (it.next()) |line| { + if (try match_act.match(line, &vars)) { + std.debug.print("{s} == {s}\n", .{ line, match_act.needle }); + break; + } else { + std.debug.print("{s} != {s}\n", .{ line, match_act.needle }); + } + } else { + return error.TestFailed; + } + }, + .compute_eq => |c_eq| { + var values = std.ArrayList(u64).init(gpa); + try values.ensureTotalCapacity(c_eq.var_stack.items.len); + for (c_eq.var_stack.items) |vv| { + const val = vars.get(vv) orelse return error.TestFailed; + values.appendAssumeCapacity(val); + } - switch (first_action) { - .exact_match => |first| { - if (mem.indexOf(u8, output, first)) |index| { - // TODO backtrack to track current scope - var it = std.mem.tokenize(u8, output[index..], "\r\n"); - - outer: for (chk.actions.items[1..]) |next_action| { - switch (next_action) { - .exact_match => |exact| { - while (it.next()) |line| { - if (mem.eql(u8, line, exact)) { - std.debug.print("{s} == {s}\n", .{ line, exact }); - continue :outer; - } - std.debug.print("{s} != {s}\n", .{ line, exact }); - } else { - return error.TestFailed; - } + var op_i: usize = 1; + var reduced: u64 = values.items[0]; + for (c_eq.op_stack.items) |op| { + const other = values.items[op_i]; + switch (op) { + .add => { + reduced += other; }, - .extract_var => |extract| { - const phrase = extract.fuzzy_match; - while (it.next()) |line| { - if (mem.indexOf(u8, line, phrase)) |found| { - std.debug.print("{s} in {s}\n", .{ phrase, line }); - // Extract variable and save back in the action. - const trimmed = mem.trim(u8, line[found + phrase.len ..], " "); - const parsed = try std.fmt.parseInt(u64, trimmed, 16); - try vars.putNoClobber(extract.var_name, parsed); - continue :outer; - } - std.debug.print("{s} not in {s}\n", .{ extract.fuzzy_match, line }); - } - }, - .compare => unreachable, } } - } else { - return error.TestFailed; - } - }, - .compare => |act| { - var values = std.ArrayList(u64).init(gpa); - try values.ensureTotalCapacity(act.var_stack.items.len); - for (act.var_stack.items) |vv| { - const val = vars.get(vv) orelse return error.TestFailed; - values.appendAssumeCapacity(val); - } - var op_i: usize = 1; - var reduced: u64 = values.items[0]; - for (act.op_stack.items) |op| { - const other = values.items[op_i]; - switch (op) { - .add => { - reduced += other; - }, - } - } - - const expected = switch (act.expected) { - .literal => |exp| exp, - .varr => |vv| vars.get(vv) orelse return error.TestFailed, - }; - if (reduced != expected) return error.TestFailed; - }, - .extract_var => unreachable, + const expected = vars.get(c_eq.expected) orelse return error.TestFailed; + if (reduced != expected) return error.TestFailed; + }, + } } } var it = vars.iterator(); while (it.next()) |entry| { - std.debug.print(" {s} => {x}", .{ entry.key_ptr.*, entry.value_ptr.* }); + std.debug.print(" {s} => {x}\n", .{ entry.key_ptr.*, entry.value_ptr.* }); } } diff --git a/test/link/macho/entry/build.zig b/test/link/macho/entry/build.zig index 78225b0d73..82d3917521 100644 --- a/test/link/macho/entry/build.zig +++ b/test/link/macho/entry/build.zig @@ -16,15 +16,15 @@ pub fn build(b: *Builder) void { const check_exe = exe.checkObject(.macho); check_exe.check("segname __TEXT"); - check_exe.checkNextExtract("vmaddr {vmaddr}"); + check_exe.checkNext("vmaddr {vmaddr}"); check_exe.check("cmd MAIN"); - check_exe.checkNextExtract("entryoff {entryoff}"); + check_exe.checkNext("entryoff {entryoff}"); check_exe.checkInSymtab(); - check_exe.checkNextExtract("_non_main {n_value}"); + check_exe.checkNext("_non_main {n_value}"); - check_exe.checkCompare("{vmaddr entryoff +}", .{ .varr = "n_value" }); + check_exe.checkComputeEq("vmaddr entryoff +", "n_value"); test_step.dependOn(&check_exe.step); diff --git a/test/link/macho/frameworks/build.zig b/test/link/macho/frameworks/build.zig index f196f47b1b..a85f6a7350 100644 --- a/test/link/macho/frameworks/build.zig +++ b/test/link/macho/frameworks/build.zig @@ -15,7 +15,10 @@ pub fn build(b: *Builder) void { const check = exe.checkObject(.macho); check.check("cmd LOAD_DYLIB"); - check.checkNext("name /System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa"); + check.checkNext("name {*}Cocoa"); + + check.check("cmd LOAD_DYLIB"); + check.checkNext("name {*}libobjc{*}.dylib"); test_step.dependOn(&check.step); From ba768614aceb486bc387e60a6026f4f48188402e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 22 Jun 2022 18:51:35 +0200 Subject: [PATCH 1898/2031] link-tests: frameworks example can test for libobjc autolink in safety modes --- test/link/macho/frameworks/build.zig | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/link/macho/frameworks/build.zig b/test/link/macho/frameworks/build.zig index a85f6a7350..2e1c8f3211 100644 --- a/test/link/macho/frameworks/build.zig +++ b/test/link/macho/frameworks/build.zig @@ -17,8 +17,13 @@ pub fn build(b: *Builder) void { check.check("cmd LOAD_DYLIB"); check.checkNext("name {*}Cocoa"); - check.check("cmd LOAD_DYLIB"); - check.checkNext("name {*}libobjc{*}.dylib"); + switch (mode) { + .Debug, .ReleaseSafe => { + check.check("cmd LOAD_DYLIB"); + check.checkNext("name {*}libobjc{*}.dylib"); + }, + else => {}, + } test_step.dependOn(&check.step); From 51f2442fc4160415796cf6271d1951969ab71c83 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 22 Jun 2022 22:24:52 +0200 Subject: [PATCH 1899/2031] link-tests: clean up error messages in case of failure --- lib/std/build/CheckObjectStep.zig | 47 ++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/lib/std/build/CheckObjectStep.zig b/lib/std/build/CheckObjectStep.zig index 40266d0c85..66edc084cd 100644 --- a/lib/std/build/CheckObjectStep.zig +++ b/lib/std/build/CheckObjectStep.zig @@ -4,6 +4,7 @@ const build = std.build; const fs = std.fs; const macho = std.macho; const mem = std.mem; +const testing = std.testing; const CheckObjectStep = @This(); @@ -179,13 +180,16 @@ fn make(step: *Step) !void { switch (act) { .match => |match_act| { while (it.next()) |line| { - if (try match_act.match(line, &vars)) { - std.debug.print("{s} == {s}\n", .{ line, match_act.needle }); - break; - } else { - std.debug.print("{s} != {s}\n", .{ line, match_act.needle }); - } + if (try match_act.match(line, &vars)) break; } else { + std.debug.print( + \\ + \\========= Expected to find: ========================== + \\{s} + \\========= But parsed file does not contain it: ======= + \\{s} + \\ + , .{ match_act.needle, output }); return error.TestFailed; } }, @@ -193,7 +197,17 @@ fn make(step: *Step) !void { var values = std.ArrayList(u64).init(gpa); try values.ensureTotalCapacity(c_eq.var_stack.items.len); for (c_eq.var_stack.items) |vv| { - const val = vars.get(vv) orelse return error.TestFailed; + const val = vars.get(vv) orelse { + std.debug.print( + \\ + \\========= Variable was not extracted: =========== + \\{s} + \\========= From parsed file: ===================== + \\{s} + \\ + , .{ vv, output }); + return error.TestFailed; + }; values.appendAssumeCapacity(val); } @@ -208,17 +222,22 @@ fn make(step: *Step) !void { } } - const expected = vars.get(c_eq.expected) orelse return error.TestFailed; - if (reduced != expected) return error.TestFailed; + const expected = vars.get(c_eq.expected) orelse { + std.debug.print( + \\ + \\========= Variable was not extracted: =========== + \\{s} + \\========= From parsed file: ===================== + \\{s} + \\ + , .{ c_eq.expected, output }); + return error.TestFailed; + }; + try testing.expectEqual(reduced, expected); }, } } } - - var it = vars.iterator(); - while (it.next()) |entry| { - std.debug.print(" {s} => {x}\n", .{ entry.key_ptr.*, entry.value_ptr.* }); - } } const Opts = struct { From e6c012c7432a6de409bf08eb7964b3d99fe4362e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 22 Jun 2022 22:40:05 +0200 Subject: [PATCH 1900/2031] link-tests: add better docs --- lib/std/build/CheckObjectStep.zig | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/std/build/CheckObjectStep.zig b/lib/std/build/CheckObjectStep.zig index 66edc084cd..9af66d88fa 100644 --- a/lib/std/build/CheckObjectStep.zig +++ b/lib/std/build/CheckObjectStep.zig @@ -41,9 +41,22 @@ const Action = union(enum) { compute_eq: ComputeEqAction, }; +/// MatchAction is the main building block of standard matchers with optional eat-all token `{*}` +/// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature +/// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use +/// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc. +/// it should be plenty useful in its current form. const MatchAction = struct { needle: []const u8, + /// Will return true if the `needle` was found in the `haystack`. + /// Some examples include: + /// + /// LC 0 => will match in its entirety + /// vmaddr {vmaddr} => will match `vmaddr` and then extract the following value as u64 + /// and save under `vmaddr` global name (see `global_vars` param) + /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib` + /// in that order with other letters in between fn match(act: MatchAction, haystack: []const u8, global_vars: anytype) !bool { var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " "); var needle_it = mem.tokenize(u8, mem.trim(u8, act.needle, " "), " "); @@ -81,6 +94,12 @@ const MatchAction = struct { } }; +/// ComputeEqAction can be used to perform an operation on the extracted global variables +/// using the MatchAction. It currently only supports an addition. The operation is required +/// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well, +/// to avoid any parsing really). +/// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively +/// they could then be added with this simple program `vmaddr entryoff +`. const ComputeEqAction = struct { expected: []const u8, var_stack: std.ArrayList([]const u8), @@ -115,23 +134,32 @@ const Check = struct { } }; +/// Creates a new sequence of actions with `phrase` as the first anchor searched phrase. pub fn check(self: *CheckObjectStep, phrase: []const u8) void { var new_check = Check.create(self.builder); new_check.match(phrase); self.checks.append(new_check) catch unreachable; } +/// Adds another searched phrase to the latest created Check with `CheckObjectStep.check(...)`. +/// Asserts at least one check already exists. pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void { assert(self.checks.items.len > 0); const last = &self.checks.items[self.checks.items.len - 1]; last.match(phrase); } +/// Creates a new check checking specifically symbol table parsed and dumped from the object +/// file. +/// Issuing this check will force parsing and dumping of the symbol table. pub fn checkInSymtab(self: *CheckObjectStep) void { self.dump_symtab = true; self.check("symtab"); } +/// Creates a new standalone, singular check which allows running simple binary operations +/// on the extracted variables. It will then compare the reduced program with the value of +/// the expected variable. pub fn checkComputeEq(self: *CheckObjectStep, program: []const u8, expected: []const u8) void { const gpa = self.builder.allocator; var ca = ComputeEqAction{ From ab8a670a571f6ae5edb77dba13f0e250c53d3742 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 22 Jun 2022 23:04:47 +0200 Subject: [PATCH 1901/2031] link-tests: enable on macos CI host only for now --- ci/azure/macos_script | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/azure/macos_script b/ci/azure/macos_script index e958dc28de..149ae9245e 100755 --- a/ci/azure/macos_script +++ b/ci/azure/macos_script @@ -76,6 +76,7 @@ release/bin/zig build test-run-translated-c -Denable-macos-sdk release/bin/zig build docs -Denable-macos-sdk release/bin/zig build test-fmt -Denable-macos-sdk release/bin/zig build test-cases -Denable-macos-sdk -Dsingle-threaded +release/bin/zig build test-link -Denable-macos-sdk if [ "${BUILD_REASON}" != "PullRequest" ]; then mv ../LICENSE release/ From 4497e422f0629ccbde699d7ecc900ab7e358caa2 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 23 Jun 2022 11:31:02 +0200 Subject: [PATCH 1902/2031] macho: fix aligning linkedit sections Align by file offsets and not file size. --- src/link/MachO.zig | 152 +++++++++++++++++++++++++-------------------- 1 file changed, 84 insertions(+), 68 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index af84ce8846..a7681e976d 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -5715,28 +5715,46 @@ fn writeDyldInfoData(self: *MachO) !void { const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].dyld_info_only; + + const rebase_off = mem.alignForwardGeneric(u64, seg.inner.fileoff, @alignOf(u64)); const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items); + dyld_info.rebase_off = @intCast(u32, rebase_off); + dyld_info.rebase_size = @intCast(u32, rebase_size); + log.debug("writing rebase info from 0x{x} to 0x{x}", .{ + dyld_info.rebase_off, + dyld_info.rebase_off + dyld_info.rebase_size, + }); + + const bind_off = mem.alignForwardGeneric(u64, dyld_info.rebase_off + dyld_info.rebase_size, @alignOf(u64)); const bind_size = try bind.bindInfoSize(bind_pointers.items); + dyld_info.bind_off = @intCast(u32, bind_off); + dyld_info.bind_size = @intCast(u32, bind_size); + log.debug("writing bind info from 0x{x} to 0x{x}", .{ + dyld_info.bind_off, + dyld_info.bind_off + dyld_info.bind_size, + }); + + const lazy_bind_off = mem.alignForwardGeneric(u64, dyld_info.bind_off + dyld_info.bind_size, @alignOf(u64)); const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items); + dyld_info.lazy_bind_off = @intCast(u32, lazy_bind_off); + dyld_info.lazy_bind_size = @intCast(u32, lazy_bind_size); + log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ + dyld_info.lazy_bind_off, + dyld_info.lazy_bind_off + dyld_info.lazy_bind_size, + }); + + const export_off = mem.alignForwardGeneric(u64, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size, @alignOf(u64)); const export_size = trie.size; + dyld_info.export_off = @intCast(u32, export_off); + dyld_info.export_size = @intCast(u32, export_size); + log.debug("writing export trie from 0x{x} to 0x{x}", .{ + dyld_info.export_off, + dyld_info.export_off + dyld_info.export_size, + }); - dyld_info.rebase_off = @intCast(u32, seg.inner.fileoff); - dyld_info.rebase_size = @intCast(u32, mem.alignForwardGeneric(u64, rebase_size, @alignOf(u64))); - seg.inner.filesize += dyld_info.rebase_size; + seg.inner.filesize = dyld_info.export_off + dyld_info.export_size - seg.inner.fileoff; - dyld_info.bind_off = dyld_info.rebase_off + dyld_info.rebase_size; - dyld_info.bind_size = @intCast(u32, mem.alignForwardGeneric(u64, bind_size, @alignOf(u64))); - seg.inner.filesize += dyld_info.bind_size; - - dyld_info.lazy_bind_off = dyld_info.bind_off + dyld_info.bind_size; - dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, lazy_bind_size, @alignOf(u64))); - seg.inner.filesize += dyld_info.lazy_bind_size; - - dyld_info.export_off = dyld_info.lazy_bind_off + dyld_info.lazy_bind_size; - dyld_info.export_size = @intCast(u32, mem.alignForwardGeneric(u64, export_size, @alignOf(u64))); - seg.inner.filesize += dyld_info.export_size; - - const needed_size = dyld_info.rebase_size + dyld_info.bind_size + dyld_info.lazy_bind_size + dyld_info.export_size; + const needed_size = dyld_info.export_off + dyld_info.export_size - dyld_info.rebase_off; var buffer = try self.base.allocator.alloc(u8, needed_size); defer self.base.allocator.free(buffer); mem.set(u8, buffer, 0); @@ -5744,14 +5762,15 @@ fn writeDyldInfoData(self: *MachO) !void { var stream = std.io.fixedBufferStream(buffer); const writer = stream.writer(); + const base_off = dyld_info.rebase_off; try bind.writeRebaseInfo(rebase_pointers.items, writer); - try stream.seekBy(@intCast(i64, dyld_info.rebase_size) - @intCast(i64, rebase_size)); + try stream.seekTo(dyld_info.bind_off - base_off); try bind.writeBindInfo(bind_pointers.items, writer); - try stream.seekBy(@intCast(i64, dyld_info.bind_size) - @intCast(i64, bind_size)); + try stream.seekTo(dyld_info.lazy_bind_off - base_off); try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer); - try stream.seekBy(@intCast(i64, dyld_info.lazy_bind_size) - @intCast(i64, lazy_bind_size)); + try stream.seekTo(dyld_info.export_off - base_off); _ = try trie.write(writer); @@ -5762,7 +5781,7 @@ fn writeDyldInfoData(self: *MachO) !void { try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off); try self.populateLazyBindOffsetsInStubHelper( - buffer[dyld_info.rebase_size + dyld_info.bind_size ..][0..dyld_info.lazy_bind_size], + buffer[dyld_info.lazy_bind_off - base_off ..][0..dyld_info.lazy_bind_size], ); self.load_commands_dirty = true; } @@ -5932,32 +5951,31 @@ fn writeFunctionStarts(self: *MachO) !void { } else break; } - const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64)); - var buffer = try self.base.allocator.alloc(u8, max_size); - defer self.base.allocator.free(buffer); - mem.set(u8, buffer, 0); + var buffer = std.ArrayList(u8).init(self.base.allocator); + defer buffer.deinit(); - var stream = std.io.fixedBufferStream(buffer); - const writer = stream.writer(); + const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64)); + try buffer.ensureTotalCapacity(max_size); for (offsets.items) |offset| { - try std.leb.writeULEB128(writer, offset); + try std.leb.writeULEB128(buffer.writer(), offset); } - const needed_size = @intCast(u32, mem.alignForwardGeneric(u64, stream.pos, @sizeOf(u64))); const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; const fn_cmd = &self.load_commands.items[self.function_starts_cmd_index.?].linkedit_data; - fn_cmd.dataoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); - fn_cmd.datasize = needed_size; - seg.inner.filesize += needed_size; + const dataoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64)); + const datasize = buffer.items.len; + fn_cmd.dataoff = @intCast(u32, dataoff); + fn_cmd.datasize = @intCast(u32, datasize); + seg.inner.filesize = fn_cmd.dataoff + fn_cmd.datasize - seg.inner.fileoff; log.debug("writing function starts info from 0x{x} to 0x{x}", .{ fn_cmd.dataoff, fn_cmd.dataoff + fn_cmd.datasize, }); - try self.base.file.?.pwriteAll(buffer[0..needed_size], fn_cmd.dataoff); + try self.base.file.?.pwriteAll(buffer.items, fn_cmd.dataoff); self.load_commands_dirty = true; } @@ -6005,11 +6023,12 @@ fn writeDices(self: *MachO) !void { const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].linkedit_data; - const needed_size = @intCast(u32, buf.items.len); - dice_cmd.dataoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); - dice_cmd.datasize = needed_size; - seg.inner.filesize += needed_size; + const dataoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64)); + const datasize = buf.items.len; + dice_cmd.dataoff = @intCast(u32, dataoff); + dice_cmd.datasize = @intCast(u32, datasize); + seg.inner.filesize = dice_cmd.dataoff + dice_cmd.datasize - seg.inner.fileoff; log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ dice_cmd.dataoff, @@ -6026,7 +6045,8 @@ fn writeSymbolTable(self: *MachO) !void { const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab; - symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); + const symoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(macho.nlist_64)); + symtab.symoff = @intCast(u32, symoff); var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator); defer locals.deinit(); @@ -6126,7 +6146,7 @@ fn writeSymbolTable(self: *MachO) !void { try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off); symtab.nsyms = @intCast(u32, nlocals + nexports + nundefs); - seg.inner.filesize += locals_size + exports_size + undefs_size; + seg.inner.filesize = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64) - seg.inner.fileoff; // Update dynamic symbol table. const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].dysymtab; @@ -6146,22 +6166,21 @@ fn writeSymbolTable(self: *MachO) !void { const nstubs = @intCast(u32, self.stubs_table.keys().len); const ngot_entries = @intCast(u32, self.got_entries_table.keys().len); - dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); + const indirectsymoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64)); + dysymtab.indirectsymoff = @intCast(u32, indirectsymoff); dysymtab.nindirectsyms = nstubs * 2 + ngot_entries; - const needed_size = dysymtab.nindirectsyms * @sizeOf(u32); - seg.inner.filesize += needed_size; + seg.inner.filesize = dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32) - seg.inner.fileoff; log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ dysymtab.indirectsymoff, - dysymtab.indirectsymoff + needed_size, + dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32), }); - var buf = try self.base.allocator.alloc(u8, needed_size); - defer self.base.allocator.free(buf); - - var stream = std.io.fixedBufferStream(buf); - var writer = stream.writer(); + var buf = std.ArrayList(u8).init(self.base.allocator); + defer buf.deinit(); + try buf.ensureTotalCapacity(dysymtab.nindirectsyms * @sizeOf(u32)); + const writer = buf.writer(); stubs.reserved1 = 0; for (self.stubs_table.keys()) |key| { @@ -6195,7 +6214,9 @@ fn writeSymbolTable(self: *MachO) !void { } } - try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff); + assert(buf.items.len == dysymtab.nindirectsyms * @sizeOf(u32)); + + try self.base.file.?.pwriteAll(buf.items, dysymtab.indirectsymoff); self.load_commands_dirty = true; } @@ -6205,18 +6226,16 @@ fn writeStringTable(self: *MachO) !void { const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab; - symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); - symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64))); - seg.inner.filesize += symtab.strsize; + const stroff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64)); + const strsize = self.strtab.items.len; + symtab.stroff = @intCast(u32, stroff); + symtab.strsize = @intCast(u32, strsize); + seg.inner.filesize = symtab.stroff + symtab.strsize - seg.inner.fileoff; log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize }); try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff); - if (symtab.strsize > self.strtab.items.len) { - // This is potentially the last section, so we need to pad it out. - try self.base.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1); - } self.load_commands_dirty = true; } @@ -6240,25 +6259,22 @@ fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { const tracy = trace(@src()); defer tracy.end(); - const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; - const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data; + const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; + const cs_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data; // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271 - const fileoff = mem.alignForwardGeneric(u64, linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize, 16); - const padding = fileoff - (linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize); - const needed_size = code_sig.estimateSize(fileoff); - code_sig_cmd.dataoff = @intCast(u32, fileoff); - code_sig_cmd.datasize = needed_size; + const dataoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, 16); + const datasize = code_sig.estimateSize(dataoff); + cs_cmd.dataoff = @intCast(u32, dataoff); + cs_cmd.datasize = @intCast(u32, code_sig.estimateSize(dataoff)); // Advance size of __LINKEDIT segment - linkedit_segment.inner.filesize += needed_size + padding; - if (linkedit_segment.inner.vmsize < linkedit_segment.inner.filesize) { - linkedit_segment.inner.vmsize = mem.alignForwardGeneric(u64, linkedit_segment.inner.filesize, self.page_size); - } - log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size }); + seg.inner.filesize = cs_cmd.dataoff + cs_cmd.datasize - seg.inner.fileoff; + seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size); + log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ dataoff, dataoff + datasize }); // Pad out the space. We need to do this to calculate valid hashes for everything in the file // except for code signature data. - try self.base.file.?.pwriteAll(&[_]u8{0}, fileoff + needed_size - 1); + try self.base.file.?.pwriteAll(&[_]u8{0}, dataoff + datasize - 1); self.load_commands_dirty = true; } From 6e04c2faabf4d632f80fa97ccbb0a20ad42a5e9f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 23 Jun 2022 12:14:10 +0200 Subject: [PATCH 1903/2031] link-tests: fix parsing symtab for macho --- lib/std/build/CheckObjectStep.zig | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/std/build/CheckObjectStep.zig b/lib/std/build/CheckObjectStep.zig index 9af66d88fa..2aaec8c4a7 100644 --- a/lib/std/build/CheckObjectStep.zig +++ b/lib/std/build/CheckObjectStep.zig @@ -303,10 +303,8 @@ const MachODumper = struct { if (symtab_cmd) |cmd| { try writer.writeAll("symtab\n"); const strtab = bytes[cmd.stroff..][0..cmd.strsize]; - const symtab = @ptrCast( - [*]const macho.nlist_64, - @alignCast(@alignOf(macho.nlist_64), bytes.ptr + cmd.symoff), - )[0..cmd.nsyms]; + const raw_symtab = bytes[cmd.symoff..][0 .. cmd.nsyms * @sizeOf(macho.nlist_64)]; + const symtab = mem.bytesAsSlice(macho.nlist_64, raw_symtab); for (symtab) |sym| { if (sym.stab()) continue; From 03ddb42b8bb96815c1bb4b857ffdfb94191ab861 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 23 Jun 2022 12:56:28 +0200 Subject: [PATCH 1904/2031] link-tests: rename check() to checkStart() Do not hardcode the symtab label; instead allow each parser to define its own. Check for missing extractor value in the matcher when matching `{}`. --- lib/std/build/CheckObjectStep.zig | 15 +++++++++++---- test/link/macho/dylib/build.zig | 6 +++--- test/link/macho/entry/build.zig | 4 ++-- test/link/macho/frameworks/build.zig | 4 ++-- test/link/macho/pagezero/build.zig | 6 +++--- test/link/macho/stack_size/build.zig | 2 +- 6 files changed, 22 insertions(+), 15 deletions(-) diff --git a/lib/std/build/CheckObjectStep.zig b/lib/std/build/CheckObjectStep.zig index 2aaec8c4a7..65a57f8832 100644 --- a/lib/std/build/CheckObjectStep.zig +++ b/lib/std/build/CheckObjectStep.zig @@ -83,6 +83,7 @@ const MatchAction = struct { if (closing_brace != needle_tok.len - 1) return error.ClosingBraceNotLast; const name = needle_tok[1..closing_brace]; + if (name.len == 0) return error.MissingBraceValue; const value = try std.fmt.parseInt(u64, hay_tok, 16); try global_vars.putNoClobber(name, value); } else { @@ -135,13 +136,13 @@ const Check = struct { }; /// Creates a new sequence of actions with `phrase` as the first anchor searched phrase. -pub fn check(self: *CheckObjectStep, phrase: []const u8) void { +pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void { var new_check = Check.create(self.builder); new_check.match(phrase); self.checks.append(new_check) catch unreachable; } -/// Adds another searched phrase to the latest created Check with `CheckObjectStep.check(...)`. +/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`. /// Asserts at least one check already exists. pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void { assert(self.checks.items.len > 0); @@ -154,7 +155,11 @@ pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void { /// Issuing this check will force parsing and dumping of the symbol table. pub fn checkInSymtab(self: *CheckObjectStep) void { self.dump_symtab = true; - self.check("symtab"); + const symtab_label = switch (self.obj_format) { + .macho => MachODumper.symtab_label, + else => @panic("TODO other parsers"), + }; + self.checkStart(symtab_label); } /// Creates a new standalone, singular check which allows running simple binary operations @@ -274,6 +279,8 @@ const Opts = struct { }; const MachODumper = struct { + const symtab_label = "symtab"; + fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 { const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator var stream = std.io.fixedBufferStream(bytes); @@ -301,7 +308,7 @@ const MachODumper = struct { } if (symtab_cmd) |cmd| { - try writer.writeAll("symtab\n"); + try writer.writeAll(symtab_label ++ "\n"); const strtab = bytes[cmd.stroff..][0..cmd.strsize]; const raw_symtab = bytes[cmd.symoff..][0 .. cmd.nsyms * @sizeOf(macho.nlist_64)]; const symtab = mem.bytesAsSlice(macho.nlist_64, raw_symtab); diff --git a/test/link/macho/dylib/build.zig b/test/link/macho/dylib/build.zig index ad116e23e9..1587def9b8 100644 --- a/test/link/macho/dylib/build.zig +++ b/test/link/macho/dylib/build.zig @@ -14,7 +14,7 @@ pub fn build(b: *Builder) void { dylib.install(); const check_dylib = dylib.checkObject(.macho); - check_dylib.check("cmd ID_DYLIB"); + check_dylib.checkStart("cmd ID_DYLIB"); check_dylib.checkNext("name @rpath/liba.dylib"); check_dylib.checkNext("timestamp 2"); check_dylib.checkNext("current version 10000"); @@ -31,13 +31,13 @@ pub fn build(b: *Builder) void { exe.addRPath(b.pathFromRoot("zig-out/lib")); const check_exe = exe.checkObject(.macho); - check_exe.check("cmd LOAD_DYLIB"); + check_exe.checkStart("cmd LOAD_DYLIB"); check_exe.checkNext("name @rpath/liba.dylib"); check_exe.checkNext("timestamp 2"); check_exe.checkNext("current version 10000"); check_exe.checkNext("compatibility version 10000"); - check_exe.check("cmd RPATH"); + check_exe.checkStart("cmd RPATH"); check_exe.checkNext(std.fmt.allocPrint(b.allocator, "path {s}", .{b.pathFromRoot("zig-out/lib")}) catch unreachable); test_step.dependOn(&check_exe.step); diff --git a/test/link/macho/entry/build.zig b/test/link/macho/entry/build.zig index 82d3917521..f0ac084e6a 100644 --- a/test/link/macho/entry/build.zig +++ b/test/link/macho/entry/build.zig @@ -15,10 +15,10 @@ pub fn build(b: *Builder) void { const check_exe = exe.checkObject(.macho); - check_exe.check("segname __TEXT"); + check_exe.checkStart("segname __TEXT"); check_exe.checkNext("vmaddr {vmaddr}"); - check_exe.check("cmd MAIN"); + check_exe.checkStart("cmd MAIN"); check_exe.checkNext("entryoff {entryoff}"); check_exe.checkInSymtab(); diff --git a/test/link/macho/frameworks/build.zig b/test/link/macho/frameworks/build.zig index 2e1c8f3211..7086606f30 100644 --- a/test/link/macho/frameworks/build.zig +++ b/test/link/macho/frameworks/build.zig @@ -14,12 +14,12 @@ pub fn build(b: *Builder) void { exe.linkFramework("Cocoa"); const check = exe.checkObject(.macho); - check.check("cmd LOAD_DYLIB"); + check.checkStart("cmd LOAD_DYLIB"); check.checkNext("name {*}Cocoa"); switch (mode) { .Debug, .ReleaseSafe => { - check.check("cmd LOAD_DYLIB"); + check.checkStart("cmd LOAD_DYLIB"); check.checkNext("name {*}libobjc{*}.dylib"); }, else => {}, diff --git a/test/link/macho/pagezero/build.zig b/test/link/macho/pagezero/build.zig index a35be919c5..e858d1f4d8 100644 --- a/test/link/macho/pagezero/build.zig +++ b/test/link/macho/pagezero/build.zig @@ -15,12 +15,12 @@ pub fn build(b: *Builder) void { exe.pagezero_size = 0x4000; const check = exe.checkObject(.macho); - check.check("LC 0"); + check.checkStart("LC 0"); check.checkNext("segname __PAGEZERO"); check.checkNext("vmaddr 0"); check.checkNext("vmsize 4000"); - check.check("segname __TEXT"); + check.checkStart("segname __TEXT"); check.checkNext("vmaddr 4000"); test_step.dependOn(&check.step); @@ -34,7 +34,7 @@ pub fn build(b: *Builder) void { exe.pagezero_size = 0; const check = exe.checkObject(.macho); - check.check("LC 0"); + check.checkStart("LC 0"); check.checkNext("segname __TEXT"); check.checkNext("vmaddr 0"); diff --git a/test/link/macho/stack_size/build.zig b/test/link/macho/stack_size/build.zig index b840f8928c..8da59dcb53 100644 --- a/test/link/macho/stack_size/build.zig +++ b/test/link/macho/stack_size/build.zig @@ -14,7 +14,7 @@ pub fn build(b: *Builder) void { exe.stack_size = 0x100000000; const check_exe = exe.checkObject(.macho); - check_exe.check("cmd MAIN"); + check_exe.checkStart("cmd MAIN"); check_exe.checkNext("stacksize 100000000"); test_step.dependOn(&check_exe.step); From 87d8cb19e4eed905b93d39554ea9a2a1012f6668 Mon Sep 17 00:00:00 2001 From: FlandreScarlet Date: Thu, 23 Jun 2022 14:07:08 +0200 Subject: [PATCH 1905/2031] std.debug: fix ConfigurableTrace.dump OOB The for-loop in dump() would index out of bounds if `t.index` is greater than size, because `end` is the maximum of `t.index` and `size` rather than the minimum. --- lib/std/debug.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index ba1f509e6c..af5d54ae62 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1999,7 +1999,7 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize const tty_config = detectTTYConfig(); const stderr = io.getStdErr().writer(); - const end = @maximum(t.index, size); + const end = @minimum(t.index, size); const debug_info = getSelfDebugInfo() catch |err| { stderr.print( "Unable to dump stack trace: Unable to open debug info: {s}\n", From 819e0e83d338a898f06d1e39c21da812ac58238b Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Thu, 23 Jun 2022 17:01:56 -0700 Subject: [PATCH 1906/2031] Add stack trace capturing to FailingAllocator --- lib/std/testing/failing_allocator.zig | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/std/testing/failing_allocator.zig b/lib/std/testing/failing_allocator.zig index 677ca6f51b..16f517732c 100644 --- a/lib/std/testing/failing_allocator.zig +++ b/lib/std/testing/failing_allocator.zig @@ -19,6 +19,8 @@ pub const FailingAllocator = struct { freed_bytes: usize, allocations: usize, deallocations: usize, + stack_addresses: [16]usize, + has_induced_failure: bool, /// `fail_index` is the number of successful allocations you can /// expect from this allocator. The next allocation will fail. @@ -37,6 +39,8 @@ pub const FailingAllocator = struct { .freed_bytes = 0, .allocations = 0, .deallocations = 0, + .stack_addresses = undefined, + .has_induced_failure = false, }; } @@ -52,6 +56,13 @@ pub const FailingAllocator = struct { return_address: usize, ) error{OutOfMemory}![]u8 { if (self.index == self.fail_index) { + mem.set(usize, &self.stack_addresses, 0); + var stack_trace = std.builtin.StackTrace{ + .instruction_addresses = &self.stack_addresses, + .index = 0, + }; + std.debug.captureStackTrace(return_address, &stack_trace); + self.has_induced_failure = true; return error.OutOfMemory; } const result = try self.internal_allocator.rawAlloc(len, ptr_align, len_align, return_address); @@ -88,4 +99,17 @@ pub const FailingAllocator = struct { self.deallocations += 1; self.freed_bytes += old_mem.len; } + + /// Only valid once `has_induced_failure == true` + pub fn getStackTrace(self: *FailingAllocator) std.builtin.StackTrace { + std.debug.assert(self.has_induced_failure); + var len: usize = 0; + while (len < self.stack_addresses.len and self.stack_addresses[len] != 0) { + len += 1; + } + return .{ + .instruction_addresses = &self.stack_addresses, + .index = len, + }; + } }; From def304a9a5b9d001e37c19509a9d2b2450088033 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Thu, 23 Jun 2022 17:02:29 -0700 Subject: [PATCH 1907/2031] Integrate FailingAllocator stack trace with testing.checkAllAllocationFailures --- lib/std/testing.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 4e43413d28..46d41affdf 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -650,7 +650,7 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime error.OutOfMemory => { if (failing_allocator_inst.allocated_bytes != failing_allocator_inst.freed_bytes) { print( - "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\n", + "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\nallocation that was made to fail: {s}", .{ fail_index, needed_alloc_count, @@ -658,6 +658,7 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime failing_allocator_inst.freed_bytes, failing_allocator_inst.allocations, failing_allocator_inst.deallocations, + failing_allocator_inst.getStackTrace(), }, ); return error.MemoryLeakDetected; From c321b2f2a0257fa45262f1ece0a263b22e8c70f3 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Thu, 23 Jun 2022 17:20:24 -0700 Subject: [PATCH 1908/2031] checkAllAllocationFailures: add possibility of SwallowedOutOfMemoryError (split from NondeterministicMemoryUsage) Inducing failure but not getting OutOfMemory back is not as much of a problem as never inducing failure when it was expected to be induced, so treating them differently and allowing them to be handled differently by the caller is useful. For example, the current implementation of `std.HashMapUnmanaged.getOrPutContextAdapted` always tries to grow and then recovers from OutOfMemory by attempting a lookup of an existing key. If this function is used (i.e. from `std.BufMap.putMove`) with `checkAllAllocationFailures`, then we'd have previously triggered `error.NondeterministicMemoryUsage`, but the real cause is that `OutOfMemory` is being recovered from and so the error is being swallowed. The new error allows us to both understand what's happening easier and to catch it and ignore it if we're okay with the code we're testing handling `error.OutOfMemory` without always bubbling it up. --- lib/std/testing.zig | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 46d41affdf..8fa5fb68f8 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -534,18 +534,27 @@ test { /// /// Any relevant state shared between runs of `test_fn` *must* be reset within `test_fn`. /// -/// Expects that the `test_fn` has a deterministic number of memory allocations -/// (an error will be returned if non-deterministic allocations are detected). -/// /// The strategy employed is to: /// - Run the test function once to get the total number of allocations. /// - Then, iterate and run the function X more times, incrementing /// the failing index each iteration (where X is the total number of /// allocations determined previously) /// +/// Expects that `test_fn` has a deterministic number of memory allocations: +/// - If an allocation was made to fail during a run of `test_fn`, but `test_fn` +/// didn't return `error.OutOfMemory`, then `error.SwallowedOutOfMemoryError` +/// is returned from `checkAllAllocationFailures`. You may want to ignore this +/// depending on whether or not the code you're testing includes some strategies +/// for recovering from `error.OutOfMemory`. +/// - If a run of `test_fn` with an expected allocation failure executes without +/// an allocation failure being induced, then `error.NondeterministicMemoryUsage` +/// is returned. This error means that there are allocation points that won't be +/// tested by the strategy this function employs (that is, there are sometimes more +/// points of allocation than the initial run of `test_fn` detects). +/// /// --- /// -/// Here's an example of using a simple test case that will cause a leak when the +/// Here's an example using a simple test case that will cause a leak when the /// allocation of `bar` fails (but will pass normally): /// /// ```zig @@ -645,7 +654,11 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime args.@"0" = failing_allocator_inst.allocator(); if (@call(.{}, test_fn, args)) |_| { - return error.NondeterministicMemoryUsage; + if (failing_allocator_inst.has_induced_failure) { + return error.SwallowedOutOfMemoryError; + } else { + return error.NondeterministicMemoryUsage; + } } else |err| switch (err) { error.OutOfMemory => { if (failing_allocator_inst.allocated_bytes != failing_allocator_inst.freed_bytes) { From 19d7f4dd8221fcd36b7fb71067bf676bb294ca1c Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Thu, 23 Jun 2022 17:27:55 -0700 Subject: [PATCH 1909/2031] FailingAllocator: Only capture the stack trace of the first induced allocation failure This is a precaution to avoid confusing stack traces on the off chance that FailingAllocator continues to try to allocate after the first failure. --- lib/std/testing/failing_allocator.zig | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/std/testing/failing_allocator.zig b/lib/std/testing/failing_allocator.zig index 16f517732c..c5d1d3df30 100644 --- a/lib/std/testing/failing_allocator.zig +++ b/lib/std/testing/failing_allocator.zig @@ -56,13 +56,15 @@ pub const FailingAllocator = struct { return_address: usize, ) error{OutOfMemory}![]u8 { if (self.index == self.fail_index) { - mem.set(usize, &self.stack_addresses, 0); - var stack_trace = std.builtin.StackTrace{ - .instruction_addresses = &self.stack_addresses, - .index = 0, - }; - std.debug.captureStackTrace(return_address, &stack_trace); - self.has_induced_failure = true; + if (!self.has_induced_failure) { + mem.set(usize, &self.stack_addresses, 0); + var stack_trace = std.builtin.StackTrace{ + .instruction_addresses = &self.stack_addresses, + .index = 0, + }; + std.debug.captureStackTrace(return_address, &stack_trace); + self.has_induced_failure = true; + } return error.OutOfMemory; } const result = try self.internal_allocator.rawAlloc(len, ptr_align, len_align, return_address); From 359b61aec3494197aaca336dabaa39d0515706ff Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 20 May 2022 21:56:34 +0200 Subject: [PATCH 1910/2031] wasm: Create compiler-rt symbols and lowering Implements the creation of an undefined symbol for a compiler-rt intrinsic. Also implements the building of the function call to said compiler-rt intrinsic. --- src/arch/wasm/CodeGen.zig | 84 ++++++++++++++++++++++++++++++++++----- src/link/Wasm.zig | 32 ++++++++++++++- 2 files changed, 106 insertions(+), 10 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index f2710574d7..28450c19c2 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -795,7 +795,7 @@ fn genFunctype(gpa: Allocator, fn_info: Type.Payload.Function.Data, target: std. var returns = std.ArrayList(wasm.Valtype).init(gpa); defer returns.deinit(); - if (firstParamSRet(fn_info, target)) { + if (firstParamSRet(fn_info.cc, fn_info.return_type, target)) { try params.append(.i32); // memory address is always a 32-bit handle } else if (fn_info.return_type.hasRuntimeBitsIgnoreComptime()) { if (fn_info.cc == .C) { @@ -993,7 +993,8 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu // Check if we store the result as a pointer to the stack rather than // by value - if (firstParamSRet(fn_ty.fnInfo(), self.target)) { + const fn_info = fn_ty.fnInfo(); + if (firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) { // the sret arg will be passed as first argument, therefore we // set the `return_value` before allocating locals for regular args. result.return_value = .{ .local = self.local_index }; @@ -1027,11 +1028,11 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu return result; } -fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool { - switch (fn_info.cc) { - .Unspecified, .Inline => return isByRef(fn_info.return_type, target), +fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, target: std.Target) bool { + switch (cc) { + .Unspecified, .Inline => return isByRef(return_type, target), .C => { - const ty_classes = abi.classifyType(fn_info.return_type, target); + const ty_classes = abi.classifyType(return_type, target); if (ty_classes[0] == .indirect) return true; if (ty_classes[0] == .direct and ty_classes[1] == .direct) return true; return false; @@ -1678,7 +1679,8 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return self.allocStack(Type.usize); // create pointer to void } - if (firstParamSRet(self.decl.ty.fnInfo(), self.target)) { + const fn_info = self.decl.ty.fnInfo(); + if (firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) { return self.return_value; } @@ -1697,7 +1699,8 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { } } - if (!firstParamSRet(self.decl.ty.fnInfo(), self.target)) { + const fn_info = self.decl.ty.fnInfo(); + if (!firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) { const result = try self.load(operand, ret_ty, 0); try self.emitWValue(result); } @@ -1720,7 +1723,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. else => unreachable, }; const ret_ty = fn_ty.fnReturnType(); - const first_param_sret = firstParamSRet(fn_ty.fnInfo(), self.target); + const fn_info = fn_ty.fnInfo(); + const first_param_sret = firstParamSRet(fn_info.cc, fn_info.return_type, self.target); const callee: ?*Decl = blk: { const func_val = self.air.value(pl_op.operand) orelse break :blk null; @@ -5091,3 +5095,65 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return shift_result; } } + +/// Calls a compiler-rt intrinsic by creating an undefined symbol, +/// then lowering the arguments and calling the symbol as a function call. +/// This function call assumes the C-ABI. +fn callIntrinsic( + self: *Self, + name: []const u8, + param_types: []const Type, + return_type: Type, + args: []const WValue, +) InnerError!WValue { + assert(param_types.len == args.len); + const symbol_index = @intCast(u32, try self.bin_file.getIntrinsicSymbol(name)); + var pt_tmp = try self.gpa.dupe(Type, param_types); + defer self.gpa.free(pt_tmp); + + // TODO: have genFunctype accept individual params so we don't, + // need to initialize a fake Fn.Data instance. + const func_type = try genFunctype(self.base.allocator, .{ + .param_types = pt_tmp, + .comptime_params = undefined, + .return_type = return_type, + .alignment = 0, + .cc = .C, + .is_var_args = false, + .is_generic = false, + }, self.target); + defer func_type.deinit(self.base.allocator); + const func_type_index = try self.bin_file.putOrGetFuncType(func_type); + try self.bin_file.addOrUpdateImport(symbol_index, func_type_index); + + const want_sret_param = firstParamSRet(.C, return_type, self.target); + // if we want return as first param, we allocate a pointer to stack, + // and emit it as our first argument + const sret = if (want_sret_param) blk: { + const sret_local = try self.allocStack(return_type); + try self.lowerToStack(sret_local); + break :blk sret_local; + } else WValue{ .none = {} }; + + // Lower all arguments to the stack before we call our function + for (args) |arg, arg_i| { + assert(param_types[arg_i].hasRuntimeBitsIgnoreComptime()); + try self.lowerArg(.C, param_types[arg_i], arg); + } + + // Actually call our intrinsic + try self.addLabel(.call, symbol_index); + + if (!return_type.hasRuntimeBitsIgnoreComptime()) { + return WValue.none; + } else if (return_type.isNoReturn()) { + try self.addTag(.@"unreachable"); + return WValue.none; + } else if (want_sret_param) { + return sret; + } else { + const result_local = try self.allocLocal(return_type); + try self.addLabel(.local_set, result_local.local); + return result_local; + } +} diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index cff47ccdf5..93240623ca 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -406,7 +406,6 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { continue; } - // TODO: locals are allowed to have duplicate symbol names // TODO: Store undefined symbols so we can verify at the end if they've all been found // if not, emit an error (unless --allow-undefined is enabled). const maybe_existing = try self.globals.getOrPut(self.base.allocator, sym_name_index); @@ -753,6 +752,37 @@ pub fn lowerUnnamedConst(self: *Wasm, tv: TypedValue, decl_index: Module.Decl.In return atom.sym_index; } +/// Returns the symbol index from the name of an intrinsic. +/// If the symbol does not yet exist, creates a new one symbol instead +/// and then returns the index to it. +pub fn getIntrinsicSymbol(self: *Wasm, name: []const u8) !u64 { + const name_index = try self.string_table.put(self.base.allocator, name); + const gop = try self.globals.getOrPut(self.base.allocator, name_index); + if (gop.found_existing) { + return gop.value_ptr.*.index; + } + + var symbol: Symbol = .{ + .name = name_index, + .flags = 0, + .index = undefined, // index to type will be set after merging function symbols + .tag = .function, + }; + symbol.setGlobal(true); + symbol.setFlag(.WASM_SYM_UNDEFINED); + + const sym_index = if (self.symbols_free_list.popOrNull()) |index| index else blk: { + var index = @intCast(u32, self.symbols.items.len); + try self.symbols.ensureUnusedCapacity(self.base.allocator, 1); + self.symbols.items.len += 1; + break :blk index; + }; + self.symbols.items[sym_index] = symbol; + gop.value_ptr.* = .{ .index = sym_index, .file = null }; + + return sym_index; +} + /// For a given decl, find the given symbol index's atom, and create a relocation for the type. /// Returns the given pointer address pub fn getDeclVAddr( From 8d03e4fc6b361e6cf96865acc05820556ae33863 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 21 May 2022 20:44:01 +0200 Subject: [PATCH 1911/2031] link: Implement API to get global symbol index --- src/arch/wasm/CodeGen.zig | 21 ++++++++----- src/link.zig | 18 +++++++++++ src/link/Wasm.zig | 64 +++++++++++++++++++++++++-------------- src/link/Wasm/Atom.zig | 5 ++- src/link/Wasm/Object.zig | 31 ++++++++++++++----- 5 files changed, 101 insertions(+), 38 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 28450c19c2..2f82f1f694 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1737,7 +1737,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. var func_type = try genFunctype(self.gpa, ext_decl.ty.fnInfo(), self.target); defer func_type.deinit(self.gpa); ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); - try self.bin_file.addOrUpdateImport(ext_decl); + try self.bin_file.addOrUpdateImport( + mem.sliceTo(ext_decl.name, 0), + ext_decl.link.wasm.sym_index, + ext_decl.getExternFn().?.lib_name, + ext_decl.fn_link.wasm.type_index, + ); break :blk ext_decl; } else if (func_val.castTag(.decl_ref)) |decl_ref| { break :blk module.declPtr(decl_ref.data); @@ -5107,13 +5112,15 @@ fn callIntrinsic( args: []const WValue, ) InnerError!WValue { assert(param_types.len == args.len); - const symbol_index = @intCast(u32, try self.bin_file.getIntrinsicSymbol(name)); - var pt_tmp = try self.gpa.dupe(Type, param_types); - defer self.gpa.free(pt_tmp); + const symbol_index = self.bin_file.base.getGlobalSymbol(name) catch |err| { + return self.fail("Could not find or create global symbol '{s}'", .{@errorName(err)}); + }; // TODO: have genFunctype accept individual params so we don't, // need to initialize a fake Fn.Data instance. - const func_type = try genFunctype(self.base.allocator, .{ + var pt_tmp = try self.gpa.dupe(Type, param_types); + defer self.gpa.free(pt_tmp); + var func_type = try genFunctype(self.gpa, .{ .param_types = pt_tmp, .comptime_params = undefined, .return_type = return_type, @@ -5122,9 +5129,9 @@ fn callIntrinsic( .is_var_args = false, .is_generic = false, }, self.target); - defer func_type.deinit(self.base.allocator); + defer func_type.deinit(self.gpa); const func_type_index = try self.bin_file.putOrGetFuncType(func_type); - try self.bin_file.addOrUpdateImport(symbol_index, func_type_index); + try self.bin_file.addOrUpdateImport(name, symbol_index, null, func_type_index); const want_sret_param = firstParamSRet(.C, return_type, self.target); // if we want return as first param, we allocate a pointer to stack, diff --git a/src/link.zig b/src/link.zig index ad1fc417f3..8bad44aa21 100644 --- a/src/link.zig +++ b/src/link.zig @@ -438,6 +438,24 @@ pub const File = struct { } } + /// Called from within CodeGen to retrieve the symbol index of a global symbol. + /// If no symbol exists yet with this name, a new one will be created instead. + pub fn getGlobalSymbol(base: *File, name: []const u8) UpdateDeclError!u32 { + log.debug("getGlobalSymbol '{s}'", .{name}); + switch (base.tag) { + // zig fmt: off + .coff => unreachable, + .elf => unreachable, + .macho => unreachable, + .plan9 => unreachable, + .spirv => unreachable, + .c => unreachable, + .wasm => return @fieldParentPtr(Wasm, "base", base).getGlobalSymbol(name), + .nvptx => unreachable, + // zig fmt: on + } + } + /// May be called before or after updateDeclExports but must be called /// after allocateDeclIndexes for any given Decl. pub fn updateDecl(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void { diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 93240623ca..2e6a5dbc96 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -433,6 +433,13 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { continue; // Do not overwrite defined symbols with undefined symbols } + if (symbol.tag != existing_sym.tag) { + log.err("symbol '{s}' mismatching type '{s}", .{ sym_name, @tagName(symbol.tag) }); + log.err(" first definition in '{s}'", .{existing_file_path}); + log.err(" next definition in '{s}'", .{object.name}); + return error.SymbolMismatchingType; + } + // when both symbols are weak, we skip overwriting if (existing_sym.isWeak() and symbol.isWeak()) { continue; @@ -755,7 +762,7 @@ pub fn lowerUnnamedConst(self: *Wasm, tv: TypedValue, decl_index: Module.Decl.In /// Returns the symbol index from the name of an intrinsic. /// If the symbol does not yet exist, creates a new one symbol instead /// and then returns the index to it. -pub fn getIntrinsicSymbol(self: *Wasm, name: []const u8) !u64 { +pub fn getGlobalSymbol(self: *Wasm, name: []const u8) !u32 { const name_index = try self.string_table.put(self.base.allocator, name); const gop = try self.globals.getOrPut(self.base.allocator, name_index); if (gop.found_existing) { @@ -769,7 +776,7 @@ pub fn getIntrinsicSymbol(self: *Wasm, name: []const u8) !u64 { .tag = .function, }; symbol.setGlobal(true); - symbol.setFlag(.WASM_SYM_UNDEFINED); + symbol.setUndefined(true); const sym_index = if (self.symbols_free_list.popOrNull()) |index| index else blk: { var index = @intCast(u32, self.symbols.items.len); @@ -779,7 +786,7 @@ pub fn getIntrinsicSymbol(self: *Wasm, name: []const u8) !u64 { }; self.symbols.items[sym_index] = symbol; gop.value_ptr.* = .{ .index = sym_index, .file = null }; - + try self.resolved_symbols.put(self.base.allocator, gop.value_ptr.*, {}); return sym_index; } @@ -982,10 +989,24 @@ fn mapFunctionTable(self: *Wasm) void { } } -pub fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void { +/// Either creates a new import, or updates one if existing. +/// When `type_index` is non-null, we assume an external function. +/// In all other cases, a data-symbol will be created instead. +pub fn addOrUpdateImport( + self: *Wasm, + /// Name of the import + name: []const u8, + /// Symbol index that is external + symbol_index: u32, + /// Optional library name (i.e. `extern "c" fn foo() void` + lib_name: ?[*:0]const u8, + /// The index of the type that represents the function signature + /// when the extern is a function. When this is null, a data-symbol + /// is asserted instead. + type_index: ?u32, +) !void { // For the import name itself, we use the decl's name, rather than the fully qualified name - const decl_name_index = try self.string_table.put(self.base.allocator, mem.sliceTo(decl.name, 0)); - const symbol_index = decl.link.wasm.sym_index; + const decl_name_index = try self.string_table.put(self.base.allocator, name); const symbol: *Symbol = &self.symbols.items[symbol_index]; symbol.setUndefined(true); symbol.setGlobal(true); @@ -996,22 +1017,19 @@ pub fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void { try self.resolved_symbols.put(self.base.allocator, loc, {}); } - switch (decl.ty.zigTypeTag()) { - .Fn => { - const gop = try self.imports.getOrPut(self.base.allocator, .{ .index = symbol_index, .file = null }); - const module_name = if (decl.getExternFn().?.lib_name) |lib_name| blk: { - break :blk mem.sliceTo(lib_name, 0); - } else self.host_name; - if (!gop.found_existing) { - gop.value_ptr.* = .{ - .module_name = try self.string_table.put(self.base.allocator, module_name), - .name = decl_name_index, - .kind = .{ .function = decl.fn_link.wasm.type_index }, - }; - } - }, - else => @panic("TODO: Implement undefined symbols for non-function declarations"), - } + if (type_index) |ty_index| { + const gop = try self.imports.getOrPut(self.base.allocator, .{ .index = symbol_index, .file = null }); + const module_name = if (lib_name) |l_name| blk: { + break :blk mem.sliceTo(l_name, 0); + } else self.host_name; + if (!gop.found_existing) { + gop.value_ptr.* = .{ + .module_name = try self.string_table.put(self.base.allocator, module_name), + .name = decl_name_index, + .kind = .{ .function = ty_index }, + }; + } + } else @panic("TODO: Implement undefined symbols for non-function declarations"); } const Kind = union(enum) { @@ -1251,7 +1269,7 @@ fn mergeSections(self: *Wasm) !void { symbol.index = @intCast(u32, self.tables.items.len) + self.imported_tables_count; try self.tables.append(self.base.allocator, original_table); }, - else => {}, + else => unreachable, } } diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index 6f65b1b83a..03b6fc6cf4 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -171,7 +171,10 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa } std.debug.assert(symbol.tag == .data); const merge_segment = wasm_bin.base.options.output_mode != .Obj; - const segment_name = wasm_bin.segment_info.items[symbol.index].outputName(merge_segment); + const segment_info = if (self.file) |object_index| blk: { + break :blk wasm_bin.objects.items[object_index].segment_info; + } else wasm_bin.segment_info.items; + const segment_name = segment_info[symbol.index].outputName(merge_segment); const atom_index = wasm_bin.data_segments.get(segment_name).?; const target_atom = wasm_bin.symbol_atom.get(target_loc).?; const segment = wasm_bin.segments.items[atom_index]; diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index 8abf78d825..3a24fc6ccb 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -302,12 +302,16 @@ fn Parser(comptime ReaderType: type) type { } fn parseObject(self: *Self, gpa: Allocator, is_object_file: *bool) Error!void { + errdefer self.object.deinit(gpa); try self.verifyMagicBytes(); const version = try self.reader.reader().readIntLittle(u32); self.object.version = version; var relocatable_data = std.ArrayList(RelocatableData).init(gpa); - defer relocatable_data.deinit(); + + errdefer while (relocatable_data.popOrNull()) |rel_data| { + gpa.free(rel_data.data[0..rel_data.size]); + } else relocatable_data.deinit(); var section_index: u32 = 0; while (self.reader.reader().readByte()) |byte| : (section_index += 1) { @@ -808,26 +812,29 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin kind: Symbol.Tag, index: u32, }; - var symbol_for_segment = std.AutoArrayHashMap(Key, u32).init(gpa); + var symbol_for_segment = std.AutoArrayHashMap(Key, std.ArrayList(u32)).init(gpa); defer symbol_for_segment.deinit(); for (self.symtable) |symbol, symbol_index| { switch (symbol.tag) { .function, .data => if (!symbol.isUndefined()) { - try symbol_for_segment.putNoClobber( - .{ .kind = symbol.tag, .index = symbol.index }, - @intCast(u32, symbol_index), - ); + const gop = try symbol_for_segment.getOrPut(.{ .kind = symbol.tag, .index = symbol.index }); + const sym_idx = @intCast(u32, symbol_index); + if (!gop.found_existing) { + gop.value_ptr.* = std.ArrayList(u32).init(gpa); + } + try gop.value_ptr.*.append(sym_idx); }, else => continue, } } for (self.relocatable_data) |relocatable_data, index| { - const sym_index = symbol_for_segment.get(.{ + const symbols = symbol_for_segment.getPtr(.{ .kind = relocatable_data.getSymbolKind(), .index = @intCast(u32, relocatable_data.index), }) orelse continue; // encountered a segment we do not create an atom for + const sym_index = symbols.pop(); const final_index = try wasm_bin.getMatchingSegment(object_index, @intCast(u32, index)); const atom = try gpa.create(Atom); @@ -862,6 +869,16 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin } try atom.code.appendSlice(gpa, relocatable_data.data[0..relocatable_data.size]); + + // symbols referencing the same atom will be added as alias + // or as 'parent' when they are global. + while (symbols.popOrNull()) |idx| { + const alias_symbol = self.symtable[idx]; + const symbol = self.symtable[atom.sym_index]; + if (alias_symbol.isGlobal() and symbol.isLocal()) { + atom.sym_index = idx; + } + } try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom); const segment: *Wasm.Segment = &wasm_bin.segments.items[final_index]; From 4d3715d89f97f3f9b6e366bbabe01f9d64ed56cf Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 22 May 2022 19:07:16 +0200 Subject: [PATCH 1912/2031] wasm-linker: de-duplicate functions+atom sorting Multiple symbols can point to the same function, this means that when we loop over the symbol list, we must deduplicate those functions being added twice. Additionaly, we must also ensure that when we append a new type and set the type index on a function, we must not do this again for the same function. This commit also implements sorting of code atoms to ensure their order matches the order of the function section to ensure the function signature matches that of the function body. --- src/link/Wasm.zig | 82 +++++++++++++++++++++++++++++++++--------- src/link/Wasm/Atom.zig | 2 +- 2 files changed, 66 insertions(+), 18 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 2e6a5dbc96..71f5c6f784 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -103,8 +103,10 @@ debug_aranges: std.ArrayListUnmanaged(u8) = .{}, // Output sections /// Output type section func_types: std.ArrayListUnmanaged(wasm.Type) = .{}, -/// Output function section -functions: std.ArrayListUnmanaged(wasm.Func) = .{}, +/// Output function section where the key is the original +/// function index and the value is function. +/// This allows us to map multiple symbols to the same function. +functions: std.AutoArrayHashMapUnmanaged(struct { file: ?u16, index: u32 }, wasm.Func) = .{}, /// Output global section wasm_globals: std.ArrayListUnmanaged(wasm.Global) = .{}, /// Memory section @@ -1042,8 +1044,12 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(self); const final_index: u32 = switch (kind) { .function => |fn_data| result: { - const index = @intCast(u32, self.functions.items.len + self.imported_functions_count); - try self.functions.append(self.base.allocator, .{ .type_index = fn_data.type_index }); + const index = @intCast(u32, self.functions.count() + self.imported_functions_count); + try self.functions.putNoClobber( + self.base.allocator, + .{ .file = null, .index = index }, + .{ .type_index = fn_data.type_index }, + ); symbol.tag = .function; symbol.index = index; @@ -1256,8 +1262,14 @@ fn mergeSections(self: *Wasm) !void { switch (symbol.tag) { .function => { const original_func = object.functions[index]; - symbol.index = @intCast(u32, self.functions.items.len) + self.imported_functions_count; - try self.functions.append(self.base.allocator, original_func); + const gop = try self.functions.getOrPut( + self.base.allocator, + .{ .file = sym_loc.file, .index = symbol.index }, + ); + if (!gop.found_existing) { + gop.value_ptr.* = original_func; + } + symbol.index = @intCast(u32, gop.index) + self.imported_functions_count; }, .global => { const original_global = object.globals[index]; @@ -1273,7 +1285,7 @@ fn mergeSections(self: *Wasm) !void { } } - log.debug("Merged ({d}) functions", .{self.functions.items.len}); + log.debug("Merged ({d}) functions", .{self.functions.count()}); log.debug("Merged ({d}) globals", .{self.wasm_globals.items.len}); log.debug("Merged ({d}) tables", .{self.tables.items.len}); } @@ -1282,6 +1294,13 @@ fn mergeSections(self: *Wasm) !void { /// 'types' section, while assigning the type index to the representing /// section (import, export, function). fn mergeTypes(self: *Wasm) !void { + // A map to track which functions have already had their + // type inserted. If we do this for the same function multiple times, + // it will be overwritten with the incorrect type. + var dirty = std.AutoHashMap(u32, void).init(self.base.allocator); + try dirty.ensureUnusedCapacity(@intCast(u32, self.functions.count()) + self.imported_functions_count); + defer dirty.deinit(); + for (self.resolved_symbols.keys()) |sym_loc| { if (sym_loc.file == null) { // zig code-generated symbols are already present in final type section @@ -1294,6 +1313,10 @@ fn mergeTypes(self: *Wasm) !void { continue; } + if (dirty.contains(symbol.index)) { + continue; // We already added the type of this symbol + } + if (symbol.isUndefined()) { log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(self)}); const import: *types.Import = self.imports.getPtr(sym_loc).?; @@ -1301,9 +1324,11 @@ fn mergeTypes(self: *Wasm) !void { import.kind.function = try self.putOrGetFuncType(original_type); } else { log.debug("Adding type from function '{s}'", .{sym_loc.getName(self)}); - const func = &self.functions.items[symbol.index - self.imported_functions_count]; + const func = &self.functions.values()[symbol.index - self.imported_functions_count]; func.type_index = try self.putOrGetFuncType(object.func_types[func.type_index]); } + + dirty.putAssumeCapacityNoClobber(symbol.index, {}); } log.debug("Completed merging and deduplicating types. Total count: ({d})", .{self.func_types.items.len}); } @@ -1711,7 +1736,11 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod for (comp.c_object_table.keys()) |c_object| { try positionals.append(c_object.status.success.object_path); } - // TODO: Also link with other objects such as compiler-rt + + if (comp.compiler_rt_static_lib) |lib| { + try positionals.append(lib.full_object_path); + } + try self.parseInputFiles(positionals.items); var object_index: u16 = 0; @@ -1840,10 +1869,10 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } // Function section - if (self.functions.items.len != 0) { + if (self.functions.count() != 0) { const header_offset = try reserveVecSectionHeader(file); const writer = file.writer(); - for (self.functions.items) |function| { + for (self.functions.values()) |function| { try leb.writeULEB128(writer, function.type_index); } @@ -1852,7 +1881,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod header_offset, .function, @intCast(u32, (try file.getPos()) - header_offset - header_size), - @intCast(u32, self.functions.items.len), + @intCast(u32, self.functions.count()), ); section_count += 1; } @@ -1984,22 +2013,41 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod const header_offset = try reserveVecSectionHeader(file); const writer = file.writer(); var atom: *Atom = self.atoms.get(code_index).?.getFirst(); + + // The code section must be sorted in line with the function order. + var sorted_atoms = try std.ArrayList(*Atom).initCapacity(self.base.allocator, self.functions.count()); + defer sorted_atoms.deinit(); + while (true) { if (!is_obj) { try atom.resolveRelocs(self); } - try leb.writeULEB128(writer, atom.size); - try writer.writeAll(atom.code.items); + sorted_atoms.appendAssumeCapacity(atom); atom = atom.next orelse break; } + const atom_sort_fn = struct { + fn sort(ctx: *const Wasm, lhs: *const Atom, rhs: *const Atom) bool { + const lhs_sym = lhs.symbolLoc().getSymbol(ctx); + const rhs_sym = rhs.symbolLoc().getSymbol(ctx); + return lhs_sym.index < rhs_sym.index; + } + }.sort; + + std.sort.sort(*Atom, sorted_atoms.items, self, atom_sort_fn); + + for (sorted_atoms.items) |sorted_atom| { + try leb.writeULEB128(writer, sorted_atom.size); + try writer.writeAll(sorted_atom.code.items); + } + code_section_size = @intCast(u32, (try file.getPos()) - header_offset - header_size); try writeVecSectionHeader( file, header_offset, .code, code_section_size, - @intCast(u32, self.functions.items.len), + @intCast(u32, self.functions.count()), ); code_section_index = section_count; section_count += 1; @@ -2135,7 +2183,7 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { } }; - var funcs = try std.ArrayList(Name).initCapacity(arena, self.functions.items.len + self.imported_functions_count); + var funcs = try std.ArrayList(Name).initCapacity(arena, self.functions.count() + self.imported_functions_count); var globals = try std.ArrayList(Name).initCapacity(arena, self.wasm_globals.items.len + self.imported_globals_count); var segments = try std.ArrayList(Name).initCapacity(arena, self.data_segments.count()); @@ -2145,7 +2193,7 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { break :blk self.string_table.get(self.imports.get(sym_loc).?.name); } else sym_loc.getName(self); switch (symbol.tag) { - .function => funcs.appendAssumeCapacity(.{ .index = symbol.index, .name = name }), + .function => try funcs.append(.{ .index = symbol.index, .name = name }), .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = name }), else => {}, } diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index 03b6fc6cf4..40b7f64804 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -155,7 +155,7 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa .R_WASM_TABLE_INDEX_SLEB, .R_WASM_TABLE_INDEX_SLEB64, => return wasm_bin.function_table.get(target_loc) orelse 0, - .R_WASM_TYPE_INDEX_LEB => return wasm_bin.functions.items[symbol.index].type_index, + .R_WASM_TYPE_INDEX_LEB => return wasm_bin.functions.values()[symbol.index - wasm_bin.imported_functions_count].type_index, .R_WASM_GLOBAL_INDEX_I32, .R_WASM_GLOBAL_INDEX_LEB, => return symbol.index, From 0606fbbc4bb7a006e2dcbf151c9505c920b735be Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 24 May 2022 22:13:57 +0200 Subject: [PATCH 1913/2031] wasm-linker: Implement Archive parsing This implements a very basic archive file parser that validates the magic bytes, and then parses the symbol table and stores the symbol and their position. --- src/link/Wasm/Archive.zig | 180 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 src/link/Wasm/Archive.zig diff --git a/src/link/Wasm/Archive.zig b/src/link/Wasm/Archive.zig new file mode 100644 index 0000000000..ca69795537 --- /dev/null +++ b/src/link/Wasm/Archive.zig @@ -0,0 +1,180 @@ +const Archive = @This(); + +const std = @import("std"); +const assert = std.debug.assert; +const fs = std.fs; +const log = std.log.scoped(.archive); +const macho = std.macho; +const mem = std.mem; + +const Allocator = mem.Allocator; +const Object = @import("Object.zig"); + +file: fs.File, +name: []const u8, + +header: ar_hdr = undefined, + +/// Parsed table of contents. +/// Each symbol name points to a list of all definition +/// sites within the current static archive. +toc: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(u32)) = .{}, + +// Archive files start with the ARMAG identifying string. Then follows a +// `struct ar_hdr', and as many bytes of member file data as its `ar_size' +// member indicates, for each member file. +/// String that begins an archive file. +const ARMAG: *const [SARMAG:0]u8 = "!\n"; +/// Size of that string. +const SARMAG: u4 = 8; + +/// String in ar_fmag at the end of each header. +const ARFMAG: *const [2:0]u8 = "`\n"; + +const ar_hdr = extern struct { + /// Member file name, sometimes / terminated. + ar_name: [16]u8, + + /// File date, decimal seconds since Epoch. + ar_date: [12]u8, + + /// User ID, in ASCII format. + ar_uid: [6]u8, + + /// Group ID, in ASCII format. + ar_gid: [6]u8, + + /// File mode, in ASCII octal. + ar_mode: [8]u8, + + /// File size, in ASCII decimal. + ar_size: [10]u8, + + /// Always contains ARFMAG. + ar_fmag: [2]u8, + + const NameOrLength = union(enum) { + Name: []const u8, + Length: u32, + }; + fn nameOrLength(self: ar_hdr) !NameOrLength { + const value = getValue(&self.ar_name); + const slash_index = mem.indexOfScalar(u8, value, '/') orelse return error.MalformedArchive; + const len = value.len; + if (slash_index == len - 1) { + // Name stored directly + return NameOrLength{ .Name = value }; + } else { + // Name follows the header directly and its length is encoded in + // the name field. + const length = try std.fmt.parseInt(u32, value[slash_index + 1 ..], 10); + return NameOrLength{ .Length = length }; + } + } + + fn date(self: ar_hdr) !u64 { + const value = getValue(&self.ar_date); + return std.fmt.parseInt(u64, value, 10); + } + + fn size(self: ar_hdr) !u32 { + const value = getValue(&self.ar_size); + return std.fmt.parseInt(u32, value, 10); + } + + fn getValue(raw: []const u8) []const u8 { + return mem.trimRight(u8, raw, &[_]u8{@as(u8, 0x20)}); + } +}; + +pub fn deinit(self: *Archive, allocator: Allocator) void { + for (self.toc.keys()) |*key| { + allocator.free(key.*); + } + for (self.toc.values()) |*value| { + value.deinit(allocator); + } + self.toc.deinit(allocator); + allocator.free(self.name); +} + +pub fn parse(self: *Archive, allocator: Allocator) !void { + const reader = self.file.reader(); + + const magic = try reader.readBytesNoEof(SARMAG); + if (!mem.eql(u8, &magic, ARMAG)) { + log.debug("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic }); + return error.NotArchive; + } + + self.header = try reader.readStruct(ar_hdr); + if (!mem.eql(u8, &self.header.ar_fmag, ARFMAG)) { + log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, self.header.ar_fmag }); + return error.NotArchive; + } + + log.debug("parsing archive '{s}' at '{s}'", .{ std.mem.sliceTo(&self.header.ar_name, 0), self.name }); + + try self.parseTableOfContents(allocator, reader); +} + +fn parseName(allocator: Allocator, header: ar_hdr, reader: anytype) ![]u8 { + const name_or_length = try header.nameOrLength(); + var name: []u8 = undefined; + switch (name_or_length) { + .Name => |n| { + name = try allocator.dupe(u8, n); + }, + .Length => |len| { + var n = try allocator.alloc(u8, len); + defer allocator.free(n); + try reader.readNoEof(n); + const actual_len = mem.indexOfScalar(u8, n, @as(u8, 0)) orelse n.len; + name = try allocator.dupe(u8, n[0..actual_len]); + }, + } + return name; +} + +fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !void { + log.debug("parsing table of contents for archive file '{s}'", .{self.name}); + // size field can have extra spaces padded in front as well as the end, + // so we trim those first before parsing the ASCII value. + const size_trimmed = std.mem.trim(u8, &self.header.ar_size, " "); + const sym_tab_size = try std.fmt.parseInt(u32, size_trimmed, 10); + + const num_symbols = try reader.readIntBig(u32); + const symbol_positions = try allocator.alloc(u32, num_symbols); + defer allocator.free(symbol_positions); + for (symbol_positions) |*index| { + index.* = try reader.readIntBig(u32); + } + + const sym_tab = try allocator.alloc(u8, sym_tab_size - 4 - (4 * num_symbols)); + defer allocator.free(sym_tab); + + reader.readNoEof(sym_tab) catch { + log.err("incomplete symbol table: expected symbol table of length 0x{x}", .{sym_tab.len}); + return error.MalformedArchive; + }; + + var i: usize = 0; + while (i < sym_tab.len) { + const string = std.mem.sliceTo(sym_tab[i..], 0); + if (string.len == 0) { + i += 1; + continue; + } + i += string.len; + const name = try allocator.dupe(u8, string); + errdefer allocator.free(name); + const gop = try self.toc.getOrPut(allocator, name); + if (gop.found_existing) { + allocator.free(name); + } else { + gop.value_ptr.* = .{}; + } + try gop.value_ptr.append(allocator, symbol_positions[gop.index]); + log.debug(" parsed symbol '{s}' for position {d}", .{ string, symbol_positions[gop.index] }); + } +} From cb28fc2e63dea2902fda21b7738aa93eaf4a2ea0 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 25 May 2022 22:35:11 +0200 Subject: [PATCH 1914/2031] wasm-linker: Resolve symbols from archives Lazily load object files by default, and only load the object file when an unresolved symbol has been found within an archive. --- src/link/Wasm.zig | 117 ++++++++++++++++++++++++++++++++++---- src/link/Wasm/Archive.zig | 33 ++++++++++- 2 files changed, 135 insertions(+), 15 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 71f5c6f784..f734b228ae 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -29,6 +29,7 @@ const Air = @import("../Air.zig"); const Liveness = @import("../Liveness.zig"); const Symbol = @import("Wasm/Symbol.zig"); const Object = @import("Wasm/Object.zig"); +const Archive = @import("Wasm/Archive.zig"); const types = @import("Wasm/types.zig"); pub const base_tag = link.File.Tag.wasm; @@ -125,6 +126,10 @@ function_table: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{}, /// All object files and their data which are linked into the final binary objects: std.ArrayListUnmanaged(Object) = .{}, +/// All archive files that are lazy loaded. +/// e.g. when an undefined symbol references a symbol from the archive. +archives: std.ArrayListUnmanaged(Archive) = .{}, + /// A map of global names (read: offset into string table) to their symbol location globals: std.AutoHashMapUnmanaged(u32, SymbolLoc) = .{}, /// Maps discarded symbols and their positions to the location of the symbol @@ -133,6 +138,8 @@ discarded: std.AutoHashMapUnmanaged(SymbolLoc, SymbolLoc) = .{}, /// List of all symbol locations which have been resolved by the linker and will be emit /// into the final binary. resolved_symbols: std.AutoArrayHashMapUnmanaged(SymbolLoc, void) = .{}, +/// Symbols that remain undefined after symbol resolution. +undefs: std.StringArrayHashMapUnmanaged(SymbolLoc) = .{}, /// Maps a symbol's location to an atom. This can be used to find meta /// data of a symbol, such as its size, or its offset to perform a relocation. /// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped. @@ -359,6 +366,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { fn parseInputFiles(self: *Wasm, files: []const []const u8) !void { for (files) |path| { if (try self.parseObjectFile(path)) continue; + if (try self.parseArchive(path, false)) continue; // load archives lazily log.warn("Unexpected file format at path: '{s}'", .{path}); } } @@ -371,10 +379,7 @@ fn parseObjectFile(self: *Wasm, path: []const u8) !bool { errdefer file.close(); var object = Object.create(self.base.allocator, file, path) catch |err| switch (err) { - error.InvalidMagicByte, error.NotObjectFile => { - log.warn("Self hosted linker does not support non-object file parsing: {s}", .{@errorName(err)}); - return false; - }, + error.InvalidMagicByte, error.NotObjectFile => return false, else => |e| return e, }; errdefer object.deinit(self.base.allocator); @@ -382,6 +387,56 @@ fn parseObjectFile(self: *Wasm, path: []const u8) !bool { return true; } +/// Parses an archive file and will then parse each object file +/// that was found in the archive file. +/// Returns false when the file is not an archive file. +/// May return an error instead when parsing failed. +/// +/// When `force_load` is `true`, it will for link all object files in the archive. +/// When false, it will only link with object files that contain symbols that +/// are referenced by other object files or Zig code. +fn parseArchive(self: *Wasm, path: []const u8, force_load: bool) !bool { + const file = try fs.cwd().openFile(path, .{}); + errdefer file.close(); + + var archive: Archive = .{ + .file = file, + .name = path, + }; + archive.parse(self.base.allocator) catch |err| switch (err) { + error.EndOfStream, error.NotArchive => { + archive.deinit(self.base.allocator); + return false; + }, + else => |e| return e, + }; + + if (!force_load) { + errdefer archive.deinit(self.base.allocator); + try self.archives.append(self.base.allocator, archive); + return true; + } + defer archive.deinit(self.base.allocator); + + // In this case we must force link all embedded object files within the archive + // We loop over all symbols, and then group them by offset as the offset + // notates where the object file starts. + var offsets = std.AutoArrayHashMap(u32, void).init(self.base.allocator); + defer offsets.deinit(); + for (archive.toc.values()) |symbol_offsets| { + for (symbol_offsets.items) |sym_offset| { + try offsets.put(sym_offset, {}); + } + } + + for (offsets.keys()) |file_offset| { + const object = try self.objects.addOne(self.base.allocator); + object.* = try archive.parseObject(self.base.allocator, file_offset); + } + + return true; +} + fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { const object: Object = self.objects.items[object_index]; log.debug("Resolving symbols in object: '{s}'", .{object.name}); @@ -414,6 +469,10 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { if (!maybe_existing.found_existing) { maybe_existing.value_ptr.* = location; try self.resolved_symbols.putNoClobber(self.base.allocator, location, {}); + + if (symbol.isUndefined()) { + try self.undefs.putNoClobber(self.base.allocator, sym_name, location); + } continue; } @@ -456,6 +515,42 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { try self.globals.put(self.base.allocator, sym_name_index, location); try self.resolved_symbols.put(self.base.allocator, location, {}); assert(self.resolved_symbols.swapRemove(existing_loc)); + if (existing_sym.isUndefined()) { + // ensure order remains intact in case we later + // resolve symbols again in a loop + assert(self.undefs.orderedRemove(sym_name)); + } + } +} + +fn resolveSymbolsInArchives(self: *Wasm) !void { + if (self.archives.items.len == 0) return; + + log.debug("Resolving symbols in archives", .{}); + var index: u32 = 0; + undef_loop: while (index < self.undefs.count()) { + const undef_sym_loc = self.undefs.values()[index]; + const sym_name = undef_sym_loc.getName(self); + + for (self.archives.items) |archive| { + const offset = archive.toc.get(sym_name) orelse { + // symbol does not exist in this archive + continue; + }; + + // Symbol is found in unparsed object file within current archive. + // Parse object and and resolve symbols again before we check remaining + // undefined symbols. + const object_file_index = @intCast(u16, self.objects.items.len); + const object = try self.objects.addOne(self.base.allocator); + object.* = try archive.parseObject(self.base.allocator, offset.items[0]); + try self.resolveSymbolsInObject(object_file_index); + + // continue loop for any remaining undefined symbols that still exist + // after resolving last object file + continue :undef_loop; + } + index += 1; } } @@ -789,6 +884,7 @@ pub fn getGlobalSymbol(self: *Wasm, name: []const u8) !u32 { self.symbols.items[sym_index] = symbol; gop.value_ptr.* = .{ .index = sym_index, .file = null }; try self.resolved_symbols.put(self.base.allocator, gop.value_ptr.*, {}); + try self.undefs.putNoClobber(self.base.allocator, name, gop.value_ptr.*); return sym_index; } @@ -1017,6 +1113,7 @@ pub fn addOrUpdateImport( const loc: SymbolLoc = .{ .file = null, .index = symbol_index }; global_gop.value_ptr.* = loc; try self.resolved_symbols.put(self.base.allocator, loc, {}); + try self.undefs.putNoClobber(self.base.allocator, name, loc); } if (type_index) |ty_index| { @@ -1298,7 +1395,7 @@ fn mergeTypes(self: *Wasm) !void { // type inserted. If we do this for the same function multiple times, // it will be overwritten with the incorrect type. var dirty = std.AutoHashMap(u32, void).init(self.base.allocator); - try dirty.ensureUnusedCapacity(@intCast(u32, self.functions.count()) + self.imported_functions_count); + try dirty.ensureUnusedCapacity(@intCast(u32, self.functions.count())); defer dirty.deinit(); for (self.resolved_symbols.keys()) |sym_loc| { @@ -1313,22 +1410,17 @@ fn mergeTypes(self: *Wasm) !void { continue; } - if (dirty.contains(symbol.index)) { - continue; // We already added the type of this symbol - } - if (symbol.isUndefined()) { log.debug("Adding type from extern function '{s}'", .{sym_loc.getName(self)}); const import: *types.Import = self.imports.getPtr(sym_loc).?; const original_type = object.func_types[import.kind.function]; import.kind.function = try self.putOrGetFuncType(original_type); - } else { + } else if (!dirty.contains(symbol.index)) { log.debug("Adding type from function '{s}'", .{sym_loc.getName(self)}); const func = &self.functions.values()[symbol.index - self.imported_functions_count]; func.type_index = try self.putOrGetFuncType(object.func_types[func.type_index]); + dirty.putAssumeCapacityNoClobber(symbol.index, {}); } - - dirty.putAssumeCapacityNoClobber(symbol.index, {}); } log.debug("Completed merging and deduplicating types. Total count: ({d})", .{self.func_types.items.len}); } @@ -1747,6 +1839,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod while (object_index < self.objects.items.len) : (object_index += 1) { try self.resolveSymbolsInObject(object_index); } + try self.resolveSymbolsInArchives(); // When we finish/error we reset the state of the linker // So we can rebuild the binary file on each incremental update diff --git a/src/link/Wasm/Archive.zig b/src/link/Wasm/Archive.zig index ca69795537..816a8cd0d0 100644 --- a/src/link/Wasm/Archive.zig +++ b/src/link/Wasm/Archive.zig @@ -113,8 +113,6 @@ pub fn parse(self: *Archive, allocator: Allocator) !void { return error.NotArchive; } - log.debug("parsing archive '{s}' at '{s}'", .{ std.mem.sliceTo(&self.header.ar_name, 0), self.name }); - try self.parseTableOfContents(allocator, reader); } @@ -175,6 +173,35 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) ! gop.value_ptr.* = .{}; } try gop.value_ptr.append(allocator, symbol_positions[gop.index]); - log.debug(" parsed symbol '{s}' for position {d}", .{ string, symbol_positions[gop.index] }); } } + +/// From a given file offset, starts reading for a file header. +/// When found, parses the object file into an `Object` and returns it. +pub fn parseObject(self: Archive, allocator: Allocator, file_offset: u32) !Object { + try self.file.seekTo(file_offset); + const reader = self.file.reader(); + const header = try reader.readStruct(ar_hdr); + + if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) { + log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, header.ar_fmag }); + return error.MalformedArchive; + } + + const object_name = try parseName(allocator, header, reader); + defer allocator.free(object_name); + + const name = name: { + if (object_name.len == 0) { + break :name try std.fmt.allocPrint(allocator, "{s}.o", .{self.name}); + } + const base_path = std.fs.path.dirname(self.name); + break :name try std.fmt.allocPrint(allocator, "{s}/{s}.o", .{ base_path, object_name }); + }; + + log.debug(" parsing object file '{s}' from archive\n", .{name}); + const object_file = try std.fs.cwd().openFile(name, .{}); + errdefer object_file.close(); + + return Object.create(allocator, object_file, name); +} From 1a3f58f5e56ad166eb5441c6960a8fac36b4ff5f Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 26 May 2022 15:05:31 +0200 Subject: [PATCH 1915/2031] wasm-linker: Correctly resolve function type When performing relocations for a type index, we first check if the target symbol is undefined. In which case, we will obtain the type from the `import` rather than look into the `functions` table. --- src/link/Wasm/Atom.zig | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index 40b7f64804..edef52004d 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -97,7 +97,7 @@ pub fn symbolLoc(self: Atom) Wasm.SymbolLoc { /// Resolves the relocations within the atom, writing the new value /// at the calculated offset. -pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void { +pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) void { if (self.relocs.items.len == 0) return; const symbol_name = self.symbolLoc().getName(wasm_bin); log.debug("Resolving relocs in atom '{s}' count({d})", .{ @@ -106,7 +106,7 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void { }); for (self.relocs.items) |reloc| { - const value = try self.relocationValue(reloc, wasm_bin); + const value = self.relocationValue(reloc, wasm_bin); log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{ (Wasm.SymbolLoc{ .file = self.file, .index = reloc.index }).getName(wasm_bin), symbol_name, @@ -144,9 +144,10 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void { /// From a given `relocation` will return the new value to be written. /// All values will be represented as a `u64` as all values can fit within it. /// The final value must be casted to the correct size. -fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) !u64 { +fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) u64 { const target_loc: Wasm.SymbolLoc = .{ .file = self.file, .index = relocation.index }; const symbol = target_loc.getSymbol(wasm_bin).*; + switch (relocation.relocation_type) { .R_WASM_FUNCTION_INDEX_LEB => return symbol.index, .R_WASM_TABLE_NUMBER_LEB => return symbol.index, @@ -155,7 +156,13 @@ fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wa .R_WASM_TABLE_INDEX_SLEB, .R_WASM_TABLE_INDEX_SLEB64, => return wasm_bin.function_table.get(target_loc) orelse 0, - .R_WASM_TYPE_INDEX_LEB => return wasm_bin.functions.values()[symbol.index - wasm_bin.imported_functions_count].type_index, + .R_WASM_TYPE_INDEX_LEB => return blk: { + if (symbol.isUndefined()) { + const imp = wasm_bin.imports.get(target_loc).?; + break :blk imp.kind.function; + } + break :blk wasm_bin.functions.values()[symbol.index - wasm_bin.imported_functions_count].type_index; + }, .R_WASM_GLOBAL_INDEX_I32, .R_WASM_GLOBAL_INDEX_LEB, => return symbol.index, From 16daf3f3bcb27b2e0b0f45ee48c69824a4804981 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 26 May 2022 16:25:26 +0200 Subject: [PATCH 1916/2031] wasm-link: Discard old symbols correctly When a new symbol is resolved to an existing symbol where it doesn't overwrite the existing symbol, we now add this symbol to the discarded list. This is required so when any relocation points to the symbol, we can retrieve the correct symbol it's resolved by instead. --- src/link/Wasm.zig | 17 ++++++++++------- src/link/Wasm/Atom.zig | 1 - src/link/Wasm/Symbol.zig | 7 +++++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index f734b228ae..74d3ab36aa 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -491,6 +491,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { return error.SymbolCollision; } + try self.discarded.put(self.base.allocator, location, existing_loc); continue; // Do not overwrite defined symbols with undefined symbols } @@ -503,6 +504,7 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { // when both symbols are weak, we skip overwriting if (existing_sym.isWeak() and symbol.isWeak()) { + try self.discarded.put(self.base.allocator, location, existing_loc); continue; } @@ -510,15 +512,13 @@ fn resolveSymbolsInObject(self: *Wasm, object_index: u16) !void { log.debug("Overwriting symbol '{s}'", .{sym_name}); log.debug(" old definition in '{s}'", .{existing_file_path}); log.debug(" new definition in '{s}'", .{object.name}); - try self.discarded.putNoClobber(self.base.allocator, maybe_existing.value_ptr.*, location); + try self.discarded.putNoClobber(self.base.allocator, existing_loc, location); maybe_existing.value_ptr.* = location; try self.globals.put(self.base.allocator, sym_name_index, location); try self.resolved_symbols.put(self.base.allocator, location, {}); assert(self.resolved_symbols.swapRemove(existing_loc)); if (existing_sym.isUndefined()) { - // ensure order remains intact in case we later - // resolve symbols again in a loop - assert(self.undefs.orderedRemove(sym_name)); + assert(self.undefs.swapRemove(sym_name)); } } } @@ -1004,7 +1004,6 @@ pub fn updateDeclExports( switch (exp.options.linkage) { .Internal => { symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); - symbol.setFlag(.WASM_SYM_BINDING_WEAK); }, .Weak => { symbol.setFlag(.WASM_SYM_BINDING_WEAK); @@ -1026,6 +1025,7 @@ pub fn updateDeclExports( } symbol.setGlobal(true); + symbol.setUndefined(false); try self.globals.put( self.base.allocator, export_name, @@ -1034,6 +1034,7 @@ pub fn updateDeclExports( // if the symbol was previously undefined, remove it as an import _ = self.imports.remove(sym_loc); + _ = self.undefs.swapRemove(exp.options.name); exp.link.wasm.sym_index = sym_index; } } @@ -1103,11 +1104,13 @@ pub fn addOrUpdateImport( /// is asserted instead. type_index: ?u32, ) !void { + assert(symbol_index != 0); // For the import name itself, we use the decl's name, rather than the fully qualified name const decl_name_index = try self.string_table.put(self.base.allocator, name); const symbol: *Symbol = &self.symbols.items[symbol_index]; symbol.setUndefined(true); symbol.setGlobal(true); + symbol.name = decl_name_index; const global_gop = try self.globals.getOrPut(self.base.allocator, decl_name_index); if (!global_gop.found_existing) { const loc: SymbolLoc = .{ .file = null, .index = symbol_index }; @@ -2113,7 +2116,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod while (true) { if (!is_obj) { - try atom.resolveRelocs(self); + atom.resolveRelocs(self); } sorted_atoms.appendAssumeCapacity(atom); atom = atom.next orelse break; @@ -2172,7 +2175,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod var current_offset: u32 = 0; while (true) { if (!is_obj) { - try atom.resolveRelocs(self); + atom.resolveRelocs(self); } // Pad with zeroes to ensure all segments are aligned diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index edef52004d..588a9a3edd 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -147,7 +147,6 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) void { fn relocationValue(self: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) u64 { const target_loc: Wasm.SymbolLoc = .{ .file = self.file, .index = relocation.index }; const symbol = target_loc.getSymbol(wasm_bin).*; - switch (relocation.relocation_type) { .R_WASM_FUNCTION_INDEX_LEB => return symbol.index, .R_WASM_TABLE_NUMBER_LEB => return symbol.index, diff --git a/src/link/Wasm/Symbol.zig b/src/link/Wasm/Symbol.zig index 94548efe31..fa6ea89d69 100644 --- a/src/link/Wasm/Symbol.zig +++ b/src/link/Wasm/Symbol.zig @@ -142,6 +142,8 @@ pub fn isNoStrip(self: Symbol) bool { pub fn isExported(self: Symbol) bool { if (self.isUndefined() or self.isLocal()) return false; if (self.isHidden()) return false; + if (self.hasFlag(.WASM_SYM_EXPORTED)) return true; + if (self.hasFlag(.WASM_SYM_BINDING_WEAK)) return false; return true; } @@ -165,9 +167,10 @@ pub fn format(self: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOpt }; const visible: []const u8 = if (self.isVisible()) "yes" else "no"; const binding: []const u8 = if (self.isLocal()) "local" else "global"; + const undef: []const u8 = if (self.isUndefined()) "undefined" else ""; try writer.print( - "{c} binding={s} visible={s} id={d} name_offset={d}", - .{ kind_fmt, binding, visible, self.index, self.name }, + "{c} binding={s} visible={s} id={d} name_offset={d} {s}", + .{ kind_fmt, binding, visible, self.index, self.name, undef }, ); } From c9f929a18b705451e9c2c81f01019575de8bf5fd Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 26 May 2022 20:33:00 +0200 Subject: [PATCH 1917/2031] fix memory leaks --- src/link/Wasm.zig | 19 +++++++++++++------ src/link/Wasm/Archive.zig | 1 - src/link/Wasm/Atom.zig | 2 +- src/link/Wasm/Object.zig | 4 +++- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 74d3ab36aa..6ffba60956 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -538,6 +538,7 @@ fn resolveSymbolsInArchives(self: *Wasm) !void { continue; }; + log.debug("Detected symbol '{s}' in archive '{s}', parsing objects..", .{ sym_name, archive.name }); // Symbol is found in unparsed object file within current archive. // Parse object and and resolve symbols again before we check remaining // undefined symbols. @@ -581,11 +582,17 @@ pub fn deinit(self: *Wasm) void { object.deinit(gpa); } + for (self.archives.items) |*archive| { + archive.file.close(); + archive.deinit(gpa); + } + self.decls.deinit(gpa); self.symbols.deinit(gpa); self.symbols_free_list.deinit(gpa); self.globals.deinit(gpa); self.resolved_symbols.deinit(gpa); + self.undefs.deinit(gpa); self.discarded.deinit(gpa); self.symbol_atom.deinit(gpa); self.export_names.deinit(gpa); @@ -599,6 +606,7 @@ pub fn deinit(self: *Wasm) void { self.data_segments.deinit(gpa); self.segment_info.deinit(gpa); self.objects.deinit(gpa); + self.archives.deinit(gpa); // free output sections self.imports.deinit(gpa); @@ -1838,10 +1846,10 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try self.parseInputFiles(positionals.items); - var object_index: u16 = 0; - while (object_index < self.objects.items.len) : (object_index += 1) { - try self.resolveSymbolsInObject(object_index); + for (self.objects.items) |_, object_index| { + try self.resolveSymbolsInObject(@intCast(u16, object_index)); } + try self.resolveSymbolsInArchives(); // When we finish/error we reset the state of the linker @@ -1867,9 +1875,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } } - while (object_index > 0) { - object_index -= 1; - try self.objects.items[object_index].parseIntoAtoms(self.base.allocator, object_index, self); + for (self.objects.items) |*object, object_index| { + try object.parseIntoAtoms(self.base.allocator, @intCast(u16, object_index), self); } if (self.dwarf) |*dwarf| { diff --git a/src/link/Wasm/Archive.zig b/src/link/Wasm/Archive.zig index 816a8cd0d0..9169a7effa 100644 --- a/src/link/Wasm/Archive.zig +++ b/src/link/Wasm/Archive.zig @@ -95,7 +95,6 @@ pub fn deinit(self: *Archive, allocator: Allocator) void { value.deinit(allocator); } self.toc.deinit(allocator); - allocator.free(self.name); } pub fn parse(self: *Archive, allocator: Allocator) !void { diff --git a/src/link/Wasm/Atom.zig b/src/link/Wasm/Atom.zig index 588a9a3edd..d5bb4509f6 100644 --- a/src/link/Wasm/Atom.zig +++ b/src/link/Wasm/Atom.zig @@ -75,7 +75,7 @@ pub fn clear(self: *Atom) void { pub fn format(self: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { _ = fmt; _ = options; - writer.print("Atom{{ .sym_index = {d}, .alignment = {d}, .size = {d}, .offset = 0x{x:0>8} }}", .{ + try writer.print("Atom{{ .sym_index = {d}, .alignment = {d}, .size = {d}, .offset = 0x{x:0>8} }}", .{ self.sym_index, self.alignment, self.size, diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index 3a24fc6ccb..6fa451af09 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -813,7 +813,9 @@ pub fn parseIntoAtoms(self: *Object, gpa: Allocator, object_index: u16, wasm_bin index: u32, }; var symbol_for_segment = std.AutoArrayHashMap(Key, std.ArrayList(u32)).init(gpa); - defer symbol_for_segment.deinit(); + defer for (symbol_for_segment.values()) |*list| { + list.deinit(); + } else symbol_for_segment.deinit(); for (self.symtable) |symbol, symbol_index| { switch (symbol.tag) { From 241180216f92c86f833910d73e1c86f54ae940e4 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 27 May 2022 19:31:34 +0200 Subject: [PATCH 1918/2031] wasm-linker: Parse object file from the archive Rather than finding the original object file, we seekTo to the object file's position within the archive file, and from there open a new file handle. This file handle is passed to the `Object` parser which will create the object. --- src/link/Wasm/Archive.zig | 15 ++++++++------- src/link/Wasm/Object.zig | 7 ++++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/link/Wasm/Archive.zig b/src/link/Wasm/Archive.zig index 9169a7effa..e214d1b124 100644 --- a/src/link/Wasm/Archive.zig +++ b/src/link/Wasm/Archive.zig @@ -181,6 +181,8 @@ pub fn parseObject(self: Archive, allocator: Allocator, file_offset: u32) !Objec try self.file.seekTo(file_offset); const reader = self.file.reader(); const header = try reader.readStruct(ar_hdr); + const current_offset = try self.file.getPos(); + try self.file.seekTo(0); if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) { log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, header.ar_fmag }); @@ -191,16 +193,15 @@ pub fn parseObject(self: Archive, allocator: Allocator, file_offset: u32) !Objec defer allocator.free(object_name); const name = name: { - if (object_name.len == 0) { - break :name try std.fmt.allocPrint(allocator, "{s}.o", .{self.name}); - } - const base_path = std.fs.path.dirname(self.name); - break :name try std.fmt.allocPrint(allocator, "{s}/{s}.o", .{ base_path, object_name }); + var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; + const path = try std.os.realpath(self.name, &buffer); + break :name try std.fmt.allocPrint(allocator, "{s}({s})", .{ path, object_name }); }; + defer allocator.free(name); - log.debug(" parsing object file '{s}' from archive\n", .{name}); - const object_file = try std.fs.cwd().openFile(name, .{}); + const object_file = try std.fs.cwd().openFile(self.name, .{}); errdefer object_file.close(); + try object_file.seekTo(current_offset); return Object.create(allocator, object_file, name); } diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index 6fa451af09..9dba838d27 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -68,7 +68,7 @@ string_table: Wasm.StringTable = .{}, const RelocatableData = struct { /// The type of the relocatable data type: enum { data, code, custom }, - /// Pointer to the data of the segment, where it's length is written to `size` + /// Pointer to the data of the segment, where its length is written to `size` data: [*]u8, /// The size in bytes of the data representing the segment within the section size: u32, @@ -105,10 +105,10 @@ pub const InitError = error{NotObjectFile} || ParseError || std.fs.File.ReadErro /// Initializes a new `Object` from a wasm object file. /// This also parses and verifies the object file. -pub fn create(gpa: Allocator, file: std.fs.File, path: []const u8) InitError!Object { +pub fn create(gpa: Allocator, file: std.fs.File, name: []const u8) InitError!Object { var object: Object = .{ .file = file, - .name = path, + .name = try gpa.dupe(u8, name), }; var is_object_file: bool = false; @@ -154,6 +154,7 @@ pub fn deinit(self: *Object, gpa: Allocator) void { } gpa.free(self.relocatable_data); self.string_table.deinit(gpa); + gpa.free(self.name); self.* = undefined; } From 5ebaf49ebb65f3b201e56ec7d89b7d91f189409e Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Mon, 30 May 2022 21:07:03 +0200 Subject: [PATCH 1919/2031] wasm: Implement basic f16 support This implements binary operations and comparisons for floats with bitsize 16. It does this by calling into compiler-rt to first extend the float to 32 bits, perform the operation, and then finally truncate back to 16 bits. When loading and storing the f16, we do this as an unsigned 16bit integer. --- src/arch/wasm/CodeGen.zig | 124 ++++++++++++++++++++++++++++++++------ 1 file changed, 105 insertions(+), 19 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 2f82f1f694..462393a150 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -215,8 +215,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { 16 => switch (args.valtype1.?) { .i32 => if (args.signedness.? == .signed) return .i32_load16_s else return .i32_load16_u, .i64 => if (args.signedness.? == .signed) return .i64_load16_s else return .i64_load16_u, - .f32 => return .f32_load, - .f64 => unreachable, + .f32, .f64 => unreachable, }, 32 => switch (args.valtype1.?) { .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u, @@ -246,8 +245,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { 16 => switch (args.valtype1.?) { .i32 => return .i32_store16, .i64 => return .i64_store16, - .f32 => return .f32_store, - .f64 => unreachable, + .f32, .f64 => unreachable, }, 32 => switch (args.valtype1.?) { .i64 => return .i64_store32, @@ -725,7 +723,8 @@ fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype { return switch (ty.zigTypeTag()) { .Float => blk: { const bits = ty.floatBits(target); - if (bits == 16 or bits == 32) break :blk wasm.Valtype.f32; + if (bits == 16) return wasm.Valtype.i32; // stored/loaded as u16 + if (bits == 32) break :blk wasm.Valtype.f32; if (bits == 64) break :blk wasm.Valtype.f64; if (bits == 128) break :blk wasm.Valtype.i64; return wasm.Valtype.i32; // represented as pointer to stack @@ -2013,6 +2012,10 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa } } + if (ty.isAnyFloat() and ty.floatBits(self.target) == 16) { + return self.binOpFloat16(lhs, rhs, op); + } + const opcode: wasm.Opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, self.target), @@ -2029,6 +2032,20 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa return bin_local; } +fn binOpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: Op) InnerError!WValue { + const ext_lhs = try self.fpext(lhs, Type.f16, Type.f32); + const ext_rhs = try self.fpext(rhs, Type.f16, Type.f32); + + const opcode: wasm.Opcode = buildOpcode(.{ .op = op, .valtype1 = .f32, .signedness = .unsigned }); + try self.emitWValue(ext_lhs); + try self.emitWValue(ext_rhs); + try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); + + // re-use temporary local + try self.addLabel(.local_set, ext_lhs.local); + return self.fptrunc(ext_lhs, Type.f32, Type.f16); +} + fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { if (ty.intInfo(self.target).bits > 128) { return self.fail("TODO: Implement binary operation for big integer", .{}); @@ -2310,8 +2327,9 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { }, .Bool => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(target)) }, .Float => switch (ty.floatBits(self.target)) { - 0...32 => return WValue{ .float32 = val.toFloat(f32) }, - 33...64 => return WValue{ .float64 = val.toFloat(f64) }, + 16 => return WValue{ .imm32 = @bitCast(u16, val.toFloat(f16)) }, + 32 => return WValue{ .float32 = val.toFloat(f32) }, + 64 => return WValue{ .float64 = val.toFloat(f64) }, else => unreachable, }, .Pointer => switch (val.tag()) { @@ -2389,8 +2407,9 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!WValue { else => unreachable, }, .Float => switch (ty.floatBits(self.target)) { - 0...32 => return WValue{ .float32 = @bitCast(f32, @as(u32, 0xaaaaaaaa)) }, - 33...64 => return WValue{ .float64 = @bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa)) }, + 16 => return WValue{ .imm32 = 0xaaaaaaaa }, + 32 => return WValue{ .float32 = @bitCast(f32, @as(u32, 0xaaaaaaaa)) }, + 64 => return WValue{ .float64 = @bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa)) }, else => unreachable, }, .Pointer => switch (self.arch()) { @@ -2562,6 +2581,8 @@ fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOper } } else if (isByRef(ty, self.target)) { return self.cmpBigInt(lhs, rhs, ty, op); + } else if (ty.isAnyFloat() and ty.floatBits(self.target) == 16) { + return self.cmpFloat16(lhs, rhs, op); } // ensure that when we compare pointers, we emit @@ -2595,6 +2616,31 @@ fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOper return cmp_tmp; } +fn cmpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: std.math.CompareOperator) InnerError!WValue { + const ext_lhs = try self.fpext(lhs, Type.f16, Type.f32); + const ext_rhs = try self.fpext(rhs, Type.f16, Type.f32); + + const opcode: wasm.Opcode = buildOpcode(.{ + .op = switch (op) { + .lt => .lt, + .lte => .le, + .eq => .eq, + .neq => .ne, + .gte => .ge, + .gt => .gt, + }, + .valtype1 = .f32, + .signedness = .unsigned, + }); + try self.emitWValue(ext_lhs); + try self.emitWValue(ext_rhs); + try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); + + const result = try self.allocLocal(Type.initTag(.i32)); // bool is always i32 + try self.addLabel(.local_set, result.local); + return result; +} + fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!WValue { _ = inst; return self.fail("TODO implement airCmpVector for wasm", .{}); @@ -3934,19 +3980,44 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const dest_ty = self.air.typeOfIndex(inst); - const dest_bits = dest_ty.floatBits(self.target); - const src_bits = self.air.typeOf(ty_op.operand).floatBits(self.target); const operand = try self.resolveInst(ty_op.operand); - if (dest_bits == 64 and src_bits == 32) { - const result = try self.allocLocal(dest_ty); + return self.fpext(operand, self.air.typeOf(ty_op.operand), dest_ty); +} + +fn fpext(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue { + const given_bits = given.floatBits(self.target); + const wanted_bits = wanted.floatBits(self.target); + + if (wanted_bits == 64 and given_bits == 32) { + const result = try self.allocLocal(wanted); try self.emitWValue(operand); try self.addTag(.f64_promote_f32); try self.addLabel(.local_set, result.local); return result; + } else if (given_bits == 16) { + // call __extendhfsf2(f16) f32 + const f32_result = try self.callIntrinsic( + "__extendhfsf2", + &.{Type.f16}, + Type.f32, + &.{operand}, + ); + + if (wanted_bits == 32) { + return f32_result; + } + if (wanted_bits == 64) { + const result = try self.allocLocal(wanted); + try self.emitWValue(f32_result); + try self.addTag(.f64_promote_f32); + try self.addLabel(.local_set, result.local); + return result; + } + return self.fail("TODO: Implement 'fpext' for floats with bitsize: {d}", .{wanted_bits}); } else { // TODO: Emit a call to compiler-rt to extend the float. e.g. __extendhfsf2 - return self.fail("TODO: Implement 'fpext' for floats with bitsize: {d}", .{dest_bits}); + return self.fail("TODO: Implement 'fpext' for floats with bitsize: {d}", .{wanted_bits}); } } @@ -3955,19 +4026,34 @@ fn airFptrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const dest_ty = self.air.typeOfIndex(inst); - const dest_bits = dest_ty.floatBits(self.target); - const src_bits = self.air.typeOf(ty_op.operand).floatBits(self.target); const operand = try self.resolveInst(ty_op.operand); + return self.fptrunc(operand, self.air.typeOf(ty_op.operand), dest_ty); +} - if (dest_bits == 32 and src_bits == 64) { - const result = try self.allocLocal(dest_ty); +fn fptrunc(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue { + const given_bits = given.floatBits(self.target); + const wanted_bits = wanted.floatBits(self.target); + + if (wanted_bits == 32 and given_bits == 64) { + const result = try self.allocLocal(wanted); try self.emitWValue(operand); try self.addTag(.f32_demote_f64); try self.addLabel(.local_set, result.local); return result; + } else if (wanted_bits == 16) { + const op: WValue = if (given_bits == 64) blk: { + const tmp = try self.allocLocal(Type.f32); + try self.emitWValue(operand); + try self.addTag(.f32_demote_f64); + try self.addLabel(.local_set, tmp.local); + break :blk tmp; + } else operand; + + // call __truncsfhf2(f32) f16 + return self.callIntrinsic("__truncsfhf2", &.{Type.f32}, Type.f16, &.{op}); } else { // TODO: Emit a call to compiler-rt to trunc the float. e.g. __truncdfhf2 - return self.fail("TODO: Implement 'fptrunc' for floats with bitsize: {d}", .{dest_bits}); + return self.fail("TODO: Implement 'fptrunc' for floats with bitsize: {d}", .{wanted_bits}); } } From 9015efe4059b7e8448a9c09c3a116cb6b2550957 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Mon, 30 May 2022 22:04:20 +0200 Subject: [PATCH 1920/2031] wasm: Implement `@mulAdd` for f16 Implements `@mulAdd` for floats with bitsize 16, where it generates a call into compiler-rt's `fmaf` function. Note that arguments for fmaf are different in order than `@mulAdd`. --- src/arch/wasm/CodeGen.zig | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 462393a150..f40566f86f 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -4495,14 +4495,24 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue { return self.fail("TODO: `@mulAdd` for vectors", .{}); } - if (ty.floatBits(self.target) == 16) { - return self.fail("TODO: `@mulAdd` for f16", .{}); - } - const addend = try self.resolveInst(pl_op.operand); const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); + if (ty.floatBits(self.target) == 16) { + const addend_ext = try self.fpext(addend, ty, Type.f32); + const lhs_ext = try self.fpext(lhs, ty, Type.f32); + const rhs_ext = try self.fpext(rhs, ty, Type.f32); + // call to compiler-rt `fn fmaf(f32, f32, f32) f32` + const result = try self.callIntrinsic( + "fmaf", + &.{ Type.f32, Type.f32, Type.f32 }, + Type.f32, + &.{ rhs_ext, lhs_ext, addend_ext }, + ); + return try self.fptrunc(result, Type.f32, ty); + } + const mul_result = try self.binOp(lhs, rhs, ty, .mul); return self.binOp(mul_result, addend, ty, .add); } From a6747d328cad831ceb07fb47cd0fa3647da21afb Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 31 May 2022 19:13:08 +0200 Subject: [PATCH 1921/2031] stage2: Enable compiler-rt when LLVM is existant Rather than checking if the user wants to use LLVM for the current compilation, check for the existance of LLVM as part of the compiler. This is temporarily, until other backends gain the ability to compiler LLVM themselves. This means that when a user passed `-fno-LLVM` we will use the native backend for the user's code, but use LLVM for compiler-rt. This also fixes emitting names for symbols in the Wasm linker, by deduplicating symbol names when multiple symbols point the same object. --- src/Compilation.zig | 13 ++++++------- src/link/Wasm.zig | 15 +++++++++++---- src/link/Wasm/types.zig | 2 ++ 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 525835ce61..0a97ee9f7e 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1914,13 +1914,12 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { try comp.work_queue.writeItem(.libtsan); } - // The `use_stage1` condition is here only because stage2 cannot yet build compiler-rt. - // Once it is capable this condition should be removed. When removing this condition, - // also test the use case of `build-obj -fcompiler-rt` with the self-hosted compiler - // and make sure the compiler-rt symbols are emitted. Currently this is hooked up for - // stage1 but not stage2. - const capable_of_building_compiler_rt = comp.bin_file.options.use_stage1 or - comp.bin_file.options.use_llvm; + // The `have_llvm` condition is here only because native backends cannot yet build compiler-rt. + // Once they are capable this condition could be removed. When removing this condition, + // also test the use case of `build-obj -fcompiler-rt` with the native backends + // and make sure the compiler-rt symbols are emitted. + const capable_of_building_compiler_rt = build_options.have_llvm; + const capable_of_building_zig_libc = comp.bin_file.options.use_stage1 or comp.bin_file.options.use_llvm; const capable_of_building_ssp = comp.bin_file.options.use_stage1; diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 6ffba60956..31d06ca881 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2286,7 +2286,9 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { } }; - var funcs = try std.ArrayList(Name).initCapacity(arena, self.functions.count() + self.imported_functions_count); + // we must de-duplicate symbols that point to the same function + var funcs = std.AutoArrayHashMap(u32, Name).init(arena); + try funcs.ensureUnusedCapacity(self.functions.count() + self.imported_functions_count); var globals = try std.ArrayList(Name).initCapacity(arena, self.wasm_globals.items.len + self.imported_globals_count); var segments = try std.ArrayList(Name).initCapacity(arena, self.data_segments.count()); @@ -2296,7 +2298,12 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { break :blk self.string_table.get(self.imports.get(sym_loc).?.name); } else sym_loc.getName(self); switch (symbol.tag) { - .function => try funcs.append(.{ .index = symbol.index, .name = name }), + .function => { + const gop = funcs.getOrPutAssumeCapacity(symbol.index); + if (!gop.found_existing) { + gop.value_ptr.* = .{ .index = symbol.index, .name = name }; + } + }, .global => globals.appendAssumeCapacity(.{ .index = symbol.index, .name = name }), else => {}, } @@ -2306,7 +2313,7 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { segments.appendAssumeCapacity(.{ .index = @intCast(u32, index), .name = key }); } - std.sort.sort(Name, funcs.items, {}, Name.lessThan); + std.sort.sort(Name, funcs.values(), {}, Name.lessThan); std.sort.sort(Name, globals.items, {}, Name.lessThan); const header_offset = try reserveCustomSectionHeader(file); @@ -2314,7 +2321,7 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { try leb.writeULEB128(writer, @intCast(u32, "name".len)); try writer.writeAll("name"); - try self.emitNameSubsection(.function, funcs.items, writer); + try self.emitNameSubsection(.function, funcs.values(), writer); try self.emitNameSubsection(.global, globals.items, writer); try self.emitNameSubsection(.data_segment, segments.items, writer); diff --git a/src/link/Wasm/types.zig b/src/link/Wasm/types.zig index 2c99f0f003..835c02ea12 100644 --- a/src/link/Wasm/types.zig +++ b/src/link/Wasm/types.zig @@ -192,6 +192,7 @@ pub const Feature = struct { sign_ext, simd128, tail_call, + shared_mem, }; pub const Prefix = enum(u8) { @@ -229,4 +230,5 @@ pub const known_features = std.ComptimeStringMap(Feature.Tag, .{ .{ "sign-ext", .sign_ext }, .{ "simd128", .simd128 }, .{ "tail-call", .tail_call }, + .{ "shared-mem", .shared_mem }, }); From ba37bc81e96a9318229193a8e7d000cb88d58cb6 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 2 Jun 2022 22:20:19 +0200 Subject: [PATCH 1922/2031] wasm: Enable f16 behavior tests --- test/behavior/floatop.zig | 2 -- test/behavior/muladd.zig | 1 - test/behavior/widening.zig | 12 ++++++++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index ac35834928..5966bafc33 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -54,7 +54,6 @@ fn testFloatComparisons() !void { } test "different sized float comparisons" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -544,7 +543,6 @@ fn testTrunc() !void { } test "negation f16" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/muladd.zig b/test/behavior/muladd.zig index 01ef8c7d29..4cf26a6d78 100644 --- a/test/behavior/muladd.zig +++ b/test/behavior/muladd.zig @@ -27,7 +27,6 @@ fn testMulAdd() !void { } test "@mulAdd f16" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/widening.zig b/test/behavior/widening.zig index a6475e88e6..4840fa0bf7 100644 --- a/test/behavior/widening.zig +++ b/test/behavior/widening.zig @@ -58,6 +58,18 @@ test "float widening" { } } +test "float widening f16" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + var a: f16 = 12.34; + var b: f32 = a; + try expect(a == b); +} + test "float widening f16 to f128" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO From 6ae898b244aba909f802340907f0bdcfa2ed9b33 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Mon, 13 Jun 2022 20:48:53 +0200 Subject: [PATCH 1923/2031] wasm: more f16 support and cleanup of intrinsics `genFunctype` now accepts calling convention, param types, and return type as part of its function signature rather than `fnData`. This means we no longer have to create a dummy for our intrinsic call abstraction. This also adds support for f16 division and builtins such as `@ceil` & more. --- src/arch/wasm/CodeGen.zig | 159 ++++++++++++++++++++++---------------- src/link/Wasm.zig | 2 +- 2 files changed, 92 insertions(+), 69 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index f40566f86f..c614f7eb48 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -417,22 +417,24 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { .f64 => return .f64_neg, }, .ceil => switch (args.valtype1.?) { - .i32, .i64 => unreachable, + .i64 => unreachable, + .i32 => return .f32_ceil, // when valtype is f16, we store it in i32. .f32 => return .f32_ceil, .f64 => return .f64_ceil, }, .floor => switch (args.valtype1.?) { - .i32, .i64 => unreachable, + .i64 => unreachable, + .i32 => return .f32_floor, // when valtype is f16, we store it in i32. .f32 => return .f32_floor, .f64 => return .f64_floor, }, .trunc => switch (args.valtype1.?) { - .i32 => switch (args.valtype2.?) { + .i32 => if (args.valtype2) |valty| switch (valty) { .i32 => unreachable, .i64 => unreachable, .f32 => if (args.signedness.? == .signed) return .i32_trunc_f32_s else return .i32_trunc_f32_u, .f64 => if (args.signedness.? == .signed) return .i32_trunc_f64_s else return .i32_trunc_f64_u, - }, + } else return .f32_trunc, // when no valtype2, it's an f16 instead which is stored in an i32. .i64 => unreachable, .f32 => return .f32_trunc, .f64 => return .f64_trunc, @@ -788,55 +790,53 @@ fn allocLocal(self: *Self, ty: Type) InnerError!WValue { /// Generates a `wasm.Type` from a given function type. /// Memory is owned by the caller. -fn genFunctype(gpa: Allocator, fn_info: Type.Payload.Function.Data, target: std.Target) !wasm.Type { - var params = std.ArrayList(wasm.Valtype).init(gpa); - defer params.deinit(); +fn genFunctype(gpa: Allocator, cc: std.builtin.CallingConvention, params: []const Type, return_type: Type, target: std.Target) !wasm.Type { + var temp_params = std.ArrayList(wasm.Valtype).init(gpa); + defer temp_params.deinit(); var returns = std.ArrayList(wasm.Valtype).init(gpa); defer returns.deinit(); - if (firstParamSRet(fn_info.cc, fn_info.return_type, target)) { - try params.append(.i32); // memory address is always a 32-bit handle - } else if (fn_info.return_type.hasRuntimeBitsIgnoreComptime()) { - if (fn_info.cc == .C) { - const res_classes = abi.classifyType(fn_info.return_type, target); + if (firstParamSRet(cc, return_type, target)) { + try temp_params.append(.i32); // memory address is always a 32-bit handle + } else if (return_type.hasRuntimeBitsIgnoreComptime()) { + if (cc == .C) { + const res_classes = abi.classifyType(return_type, target); assert(res_classes[0] == .direct and res_classes[1] == .none); - const scalar_type = abi.scalarType(fn_info.return_type, target); + const scalar_type = abi.scalarType(return_type, target); try returns.append(typeToValtype(scalar_type, target)); } else { - try returns.append(typeToValtype(fn_info.return_type, target)); + try returns.append(typeToValtype(return_type, target)); } - } else if (fn_info.return_type.isError()) { + } else if (return_type.isError()) { try returns.append(.i32); } // param types - if (fn_info.param_types.len != 0) { - for (fn_info.param_types) |param_type| { - if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; + for (params) |param_type| { + if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; - switch (fn_info.cc) { - .C => { - const param_classes = abi.classifyType(param_type, target); - for (param_classes) |class| { - if (class == .none) continue; - if (class == .direct) { - const scalar_type = abi.scalarType(param_type, target); - try params.append(typeToValtype(scalar_type, target)); - } else { - try params.append(typeToValtype(param_type, target)); - } + switch (cc) { + .C => { + const param_classes = abi.classifyType(param_type, target); + for (param_classes) |class| { + if (class == .none) continue; + if (class == .direct) { + const scalar_type = abi.scalarType(param_type, target); + try temp_params.append(typeToValtype(scalar_type, target)); + } else { + try temp_params.append(typeToValtype(param_type, target)); } - }, - else => if (isByRef(param_type, target)) - try params.append(.i32) - else - try params.append(typeToValtype(param_type, target)), - } + } + }, + else => if (isByRef(param_type, target)) + try temp_params.append(.i32) + else + try temp_params.append(typeToValtype(param_type, target)), } } return wasm.Type{ - .params = params.toOwnedSlice(), + .params = temp_params.toOwnedSlice(), .returns = returns.toOwnedSlice(), }; } @@ -877,7 +877,8 @@ pub fn generate( } fn genFunc(self: *Self) InnerError!void { - var func_type = try genFunctype(self.gpa, self.decl.ty.fnInfo(), self.target); + const fn_info = self.decl.ty.fnInfo(); + var func_type = try genFunctype(self.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, self.target); defer func_type.deinit(self.gpa); self.decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); @@ -1733,7 +1734,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. break :blk module.declPtr(func.data.owner_decl); } else if (func_val.castTag(.extern_fn)) |extern_fn| { const ext_decl = module.declPtr(extern_fn.data.owner_decl); - var func_type = try genFunctype(self.gpa, ext_decl.ty.fnInfo(), self.target); + const ext_info = ext_decl.ty.fnInfo(); + var func_type = try genFunctype(self.gpa, ext_info.cc, ext_info.param_types, ext_info.return_type, self.target); defer func_type.deinit(self.gpa); ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type); try self.bin_file.addOrUpdateImport( @@ -1774,7 +1776,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const operand = try self.resolveInst(pl_op.operand); try self.emitWValue(operand); - var fn_type = try genFunctype(self.gpa, fn_ty.fnInfo(), self.target); + var fn_type = try genFunctype(self.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, self.target); defer fn_type.deinit(self.gpa); const fn_type_index = try self.bin_file.putOrGetFuncType(fn_type); @@ -4883,12 +4885,38 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { try self.emitWValue(rem_result); try self.addTag(.select); } else { - const div_result = try self.binOp(lhs, rhs, ty, .div); - try self.emitWValue(div_result); - switch (ty.floatBits(self.target)) { - 32 => try self.addTag(.f32_floor), - 64 => try self.addTag(.f64_floor), - else => |bit_size| return self.fail("TODO: `@divFloor` for floats with bitsize: {d}", .{bit_size}), + const float_bits = ty.floatBits(self.target); + if (float_bits > 64) { + return self.fail("TODO: `@divFloor` for floats with bitsize: {d}", .{float_bits}); + } + const is_f16 = float_bits == 16; + + const lhs_operand = if (is_f16) blk: { + break :blk try self.fpext(lhs, Type.f16, Type.f32); + } else lhs; + const rhs_operand = if (is_f16) blk: { + break :blk try self.fpext(rhs, Type.f16, Type.f32); + } else rhs; + + try self.emitWValue(lhs_operand); + try self.emitWValue(rhs_operand); + + switch (float_bits) { + 16, 32 => { + try self.addTag(.f32_div); + try self.addTag(.f32_floor); + }, + 64 => { + try self.addTag(.f64_div); + try self.addTag(.f64_floor); + }, + else => unreachable, + } + + if (is_f16) { + // we can re-use temporary local + try self.addLabel(.local_set, lhs_operand.local); + return self.fptrunc(lhs_operand, Type.f32, Type.f16); } } @@ -4961,22 +4989,28 @@ fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValu const un_op = self.air.instructions.items(.data)[inst].un_op; const ty = self.air.typeOfIndex(inst); + const float_bits = ty.floatBits(self.target); + const is_f16 = float_bits == 16; if (ty.zigTypeTag() == .Vector) { return self.fail("TODO: Implement `@ceil` for vectors", .{}); } + if (float_bits > 64) { + return self.fail("TODO: implement `@ceil`, `@trunc`, `@floor` for floats larger than 64bits", .{}); + } const operand = try self.resolveInst(un_op); - try self.emitWValue(operand); - switch (ty.floatBits(self.target)) { - 32, 64 => { - const opcode = buildOpcode(.{ - .op = op, - .valtype1 = typeToValtype(ty, self.target), - }); - try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); - }, - else => |bit_size| return self.fail("TODO: Implement `@ceil` for floats with bitsize {d}", .{bit_size}), + const op_to_lower = if (is_f16) blk: { + break :blk try self.fpext(operand, Type.f16, Type.f32); + } else operand; + try self.emitWValue(op_to_lower); + const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, self.target) }); + try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); + + if (is_f16) { + // re-use temporary to save locals + try self.addLabel(.local_set, op_to_lower.local); + return self.fptrunc(op_to_lower, Type.f32, Type.f16); } const result = try self.allocLocal(ty); @@ -5212,19 +5246,8 @@ fn callIntrinsic( return self.fail("Could not find or create global symbol '{s}'", .{@errorName(err)}); }; - // TODO: have genFunctype accept individual params so we don't, - // need to initialize a fake Fn.Data instance. - var pt_tmp = try self.gpa.dupe(Type, param_types); - defer self.gpa.free(pt_tmp); - var func_type = try genFunctype(self.gpa, .{ - .param_types = pt_tmp, - .comptime_params = undefined, - .return_type = return_type, - .alignment = 0, - .cc = .C, - .is_var_args = false, - .is_generic = false, - }, self.target); + // Always pass over C-ABI + var func_type = try genFunctype(self.gpa, .C, param_types, return_type, self.target); defer func_type.deinit(self.gpa); const func_type_index = try self.bin_file.putOrGetFuncType(func_type); try self.bin_file.addOrUpdateImport(name, symbol_index, null, func_type_index); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 31d06ca881..d10690142f 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -1840,7 +1840,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try positionals.append(c_object.status.success.object_path); } - if (comp.compiler_rt_static_lib) |lib| { + if (comp.compiler_rt_lib) |lib| { try positionals.append(lib.full_object_path); } From 3868864695b41c2c94f585b9644d3ed3bda39708 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 21 Jun 2022 20:15:24 +0200 Subject: [PATCH 1924/2031] Revert "wasm: Enable f16 behavior tests" This reverts commit 3c34c9f13c67ff1716a9531e87d23a6dad12b45e. --- test/behavior/floatop.zig | 2 ++ test/behavior/muladd.zig | 1 + test/behavior/widening.zig | 12 ------------ 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 5966bafc33..ac35834928 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -54,6 +54,7 @@ fn testFloatComparisons() !void { } test "different sized float comparisons" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -543,6 +544,7 @@ fn testTrunc() !void { } test "negation f16" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/muladd.zig b/test/behavior/muladd.zig index 4cf26a6d78..01ef8c7d29 100644 --- a/test/behavior/muladd.zig +++ b/test/behavior/muladd.zig @@ -27,6 +27,7 @@ fn testMulAdd() !void { } test "@mulAdd f16" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/widening.zig b/test/behavior/widening.zig index 4840fa0bf7..a6475e88e6 100644 --- a/test/behavior/widening.zig +++ b/test/behavior/widening.zig @@ -58,18 +58,6 @@ test "float widening" { } } -test "float widening f16" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - - var a: f16 = 12.34; - var b: f32 = a; - try expect(a == b); -} - test "float widening f16 to f128" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO From 7c87f9c828282aa12fb2d77c9c6a2318d83b8dff Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 23 Jun 2022 16:20:20 +0200 Subject: [PATCH 1925/2031] link:clarification & enable MachO getGlobalSymbol This adds clarification to the getGlobalSymbol doc comments, as well as renames the `addExternFn` function for MachO to `getGlobalSymbol`. This function will now be called from 'src/link.zig' as well. Finally, this also enables compiling zig's libc using LLVM even though the `fno-LLVM` flag is given. --- src/Compilation.zig | 3 +-- src/arch/aarch64/CodeGen.zig | 2 +- src/arch/x86_64/CodeGen.zig | 2 +- src/link.zig | 5 +++-- src/link/MachO.zig | 2 +- src/link/Wasm.zig | 3 ++- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 0a97ee9f7e..8cb93b5473 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1920,8 +1920,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { // and make sure the compiler-rt symbols are emitted. const capable_of_building_compiler_rt = build_options.have_llvm; - const capable_of_building_zig_libc = comp.bin_file.options.use_stage1 or - comp.bin_file.options.use_llvm; + const capable_of_building_zig_libc = build_options.have_llvm; const capable_of_building_ssp = comp.bin_file.options.use_stage1; if (comp.bin_file.options.include_compiler_rt and capable_of_building_compiler_rt) { diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index e74fbd44ac..4cf428bd9a 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -3188,7 +3188,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. lib_name, }); } - const n_strx = try macho_file.addExternFn(mem.sliceTo(decl_name, 0)); + const n_strx = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); _ = try self.addInst(.{ .tag = .call_extern, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index c94eaa37af..5c30d495c7 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -3996,7 +3996,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. lib_name, }); } - const n_strx = try macho_file.addExternFn(mem.sliceTo(decl_name, 0)); + const n_strx = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); _ = try self.addInst(.{ .tag = .call_extern, .ops = undefined, diff --git a/src/link.zig b/src/link.zig index 8bad44aa21..c78fb8c2c5 100644 --- a/src/link.zig +++ b/src/link.zig @@ -439,14 +439,15 @@ pub const File = struct { } /// Called from within CodeGen to retrieve the symbol index of a global symbol. - /// If no symbol exists yet with this name, a new one will be created instead. + /// If no symbol exists yet with this name, a new undefined global symbol will + /// be created. This symbol may get resolved once all relocatables are (re-)linked. pub fn getGlobalSymbol(base: *File, name: []const u8) UpdateDeclError!u32 { log.debug("getGlobalSymbol '{s}'", .{name}); switch (base.tag) { // zig fmt: off .coff => unreachable, .elf => unreachable, - .macho => unreachable, + .macho => return @fieldParentPtr(MachO, "base", base).getGlobalSymbol(name), .plan9 => unreachable, .spirv => unreachable, .c => unreachable, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index a7681e976d..0c44901d29 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -5366,7 +5366,7 @@ fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void { } } -pub fn addExternFn(self: *MachO, name: []const u8) !u32 { +pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 { const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name}); defer self.base.allocator.free(sym_name); const n_strx = try self.makeString(sym_name); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index d10690142f..4357a588a1 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -864,7 +864,8 @@ pub fn lowerUnnamedConst(self: *Wasm, tv: TypedValue, decl_index: Module.Decl.In return atom.sym_index; } -/// Returns the symbol index from the name of an intrinsic. +/// Returns the symbol index from a symbol of which its flag is set global, +/// such as an exported or imported symbol. /// If the symbol does not yet exist, creates a new one symbol instead /// and then returns the index to it. pub fn getGlobalSymbol(self: *Wasm, name: []const u8) !u32 { From 8b3f7d2ad8953009ecb983dfb466ce3995a4cb38 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 12 Jun 2022 23:24:04 +0700 Subject: [PATCH 1926/2031] stage2: sparc64: Merge the compare_flag structs into condition_flags This follows the design in the aarch64 backend (commit 61844b6bd405b4cca3ab673284609aa6a651d506). --- src/arch/sparc64/CodeGen.zig | 162 ++++++++++++----------------------- src/arch/sparc64/bits.zig | 31 +++++-- 2 files changed, 77 insertions(+), 116 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index d30bd651a2..28a1ab2a24 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -93,8 +93,8 @@ register_manager: RegisterManager = .{}, /// Maps offset to what is stored there. stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, -/// Tracks the current instruction allocated to the compare flags -compare_flags_inst: ?Air.Inst.Index = null, +/// Tracks the current instruction allocated to the condition flags +condition_flags_inst: ?Air.Inst.Index = null, /// Offset from the stack base, representing the end of the stack frame. max_end_stack: u32 = 0, @@ -141,16 +141,11 @@ const MCValue = union(enum) { stack_offset: u32, /// The value is a pointer to one of the stack variables (payload is stack offset). ptr_stack_offset: u32, - /// The value is in the specified CCR assuming an unsigned operation, - /// with the operator applied on top of it. - compare_flags_unsigned: struct { - cmp: math.CompareOperator, - ccr: Instruction.CCR, - }, - /// The value is in the specified CCR assuming an signed operation, - /// with the operator applied on top of it. - compare_flags_signed: struct { - cmp: math.CompareOperator, + /// The value is in the specified CCR. The value is 1 (if + /// the type is u1) or true (if the type in bool) iff the + /// specified condition is true. + condition_flags: struct { + cond: Instruction.Condition, ccr: Instruction.CCR, }, @@ -738,8 +733,8 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { else => unreachable, }; - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; + try self.spillConditionFlagsIfOccupied(); + self.condition_flags_inst = inst; const dest = blk: { if (rhs_immediate_ok) { @@ -1072,7 +1067,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. // CCR is volatile across function calls // (SCD 2.4.1, page 3P-10) - try self.spillCompareFlagsIfOccupied(); + try self.spillConditionFlagsIfOccupied(); for (info.args) |mc_arg, arg_i| { const arg = args[arg_i]; @@ -1208,12 +1203,18 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { .inst = inst, }); - try self.spillCompareFlagsIfOccupied(); - self.compare_flags_inst = inst; + try self.spillConditionFlagsIfOccupied(); + self.condition_flags_inst = inst; break :result switch (int_info.signedness) { - .signed => MCValue{ .compare_flags_signed = .{ .cmp = op, .ccr = .xcc } }, - .unsigned => MCValue{ .compare_flags_unsigned = .{ .cmp = op, .ccr = .xcc } }, + .signed => MCValue{ .condition_flags = .{ + .cond = .{ .icond = Instruction.ICondition.fromCompareOperatorSigned(op) }, + .ccr = .xcc, + } }, + .unsigned => MCValue{ .condition_flags = .{ + .cond = .{ .icond = Instruction.ICondition.fromCompareOperatorUnsigned(op) }, + .ccr = .xcc, + } }, }; } else { return self.fail("TODO SPARCv9 cmp for ints > 64 bits", .{}); @@ -1224,7 +1225,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { const pl_op = self.air.instructions.items(.data)[inst].pl_op; - const cond = try self.resolveInst(pl_op.operand); + const condition = try self.resolveInst(pl_op.operand); const extra = self.air.extraData(Air.CondBr, pl_op.payload); const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len]; const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; @@ -1232,39 +1233,22 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { // Here we either emit a BPcc for branching on CCR content, // or emit a BPr to branch on register content. - const reloc: Mir.Inst.Index = switch (cond) { - .compare_flags_signed, - .compare_flags_unsigned, - => try self.addInst(.{ + const reloc: Mir.Inst.Index = switch (condition) { + .condition_flags => |flags| try self.addInst(.{ .tag = .bpcc, .data = .{ .branch_predict_int = .{ - .ccr = switch (cond) { - .compare_flags_signed => |cmp_op| cmp_op.ccr, - .compare_flags_unsigned => |cmp_op| cmp_op.ccr, - else => unreachable, - }, - .cond = switch (cond) { - .compare_flags_signed => |cmp_op| blk: { - // Here we map to the opposite condition because the jump is to the false branch. - const condition = Instruction.ICondition.fromCompareOperatorSigned(cmp_op.cmp); - break :blk condition.negate(); - }, - .compare_flags_unsigned => |cmp_op| blk: { - // Here we map to the opposite condition because the jump is to the false branch. - const condition = Instruction.ICondition.fromCompareOperatorUnsigned(cmp_op.cmp); - break :blk condition.negate(); - }, - else => unreachable, - }, + .ccr = flags.ccr, + // Here we map to the opposite condition because the jump is to the false branch. + .cond = flags.cond.icond.negate(), .inst = undefined, // Will be filled by performReloc }, }, }), else => blk: { - const reg = switch (cond) { + const reg = switch (condition) { .register => |r| r, - else => try self.copyToTmpRegister(Type.bool, cond), + else => try self.copyToTmpRegister(Type.bool, condition), }; break :blk try self.addInst(.{ @@ -1305,7 +1289,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { var parent_stack = try self.stack.clone(self.gpa); defer parent_stack.deinit(self.gpa); const parent_registers = self.register_manager.registers; - const parent_compare_flags_inst = self.compare_flags_inst; + const parent_condition_flags_inst = self.condition_flags_inst; try self.branch_stack.append(.{}); errdefer { @@ -1324,7 +1308,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { defer saved_then_branch.deinit(self.gpa); self.register_manager.registers = parent_registers; - self.compare_flags_inst = parent_compare_flags_inst; + self.condition_flags_inst = parent_condition_flags_inst; self.stack.deinit(self.gpa); self.stack = parent_stack; @@ -1537,37 +1521,13 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { switch (operand) { .dead => unreachable, .unreach => unreachable, - .compare_flags_unsigned => |op| { - const r = MCValue{ - .compare_flags_unsigned = .{ - .cmp = switch (op.cmp) { - .gte => .lt, - .gt => .lte, - .neq => .eq, - .lt => .gte, - .lte => .gt, - .eq => .neq, - }, + .condition_flags => |op| { + break :result MCValue{ + .condition_flags = .{ + .cond = op.cond.negate(), .ccr = op.ccr, }, }; - break :result r; - }, - .compare_flags_signed => |op| { - const r = MCValue{ - .compare_flags_signed = .{ - .cmp = switch (op.cmp) { - .gte => .lt, - .gt => .lte, - .neq => .eq, - .lt => .gte, - .lte => .gt, - .eq => .neq, - }, - .ccr = op.ccr, - }, - }; - break :result r; }, else => { switch (operand_ty.zigTypeTag()) { @@ -2667,20 +2627,10 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void switch (mcv) { .dead => unreachable, .unreach, .none => return, // Nothing to do. - .compare_flags_signed, - .compare_flags_unsigned, - => { - const condition = switch (mcv) { - .compare_flags_unsigned => |op| Instruction.ICondition.fromCompareOperatorUnsigned(op.cmp), - .compare_flags_signed => |op| Instruction.ICondition.fromCompareOperatorSigned(op.cmp), - else => unreachable, - }; + .condition_flags => |op| { + const condition = op.cond; + const ccr = op.ccr; - const ccr = switch (mcv) { - .compare_flags_unsigned => |op| op.ccr, - .compare_flags_signed => |op| op.ccr, - else => unreachable, - }; // TODO handle floating point CCRs assert(ccr == .xcc or ccr == .icc); @@ -2700,7 +2650,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .data = .{ .conditional_move = .{ .ccr = ccr, - .cond = .{ .icond = condition }, + .cond = condition, .is_imm = true, .rd = reg, .rs2_or_imm = .{ .imm = 1 }, @@ -2874,8 +2824,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro else => return self.fail("TODO implement memset", .{}), } }, - .compare_flags_unsigned, - .compare_flags_signed, + .condition_flags, .immediate, .ptr_stack_offset, => { @@ -3103,7 +3052,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { } }, }); - return MCValue{ .compare_flags_unsigned = .{ .cmp = .gt, .ccr = .xcc } }; + return MCValue{ .condition_flags = .{ .cond = .{ .icond = .gu }, .ccr = .xcc } }; } else { return self.fail("TODO isErr for errors with size > 8", .{}); } @@ -3116,9 +3065,8 @@ fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { // Call isErr, then negate the result. const is_err_result = try self.isErr(ty, operand); switch (is_err_result) { - .compare_flags_unsigned => |op| { - assert(op.cmp == .gt); - return MCValue{ .compare_flags_unsigned = .{ .cmp = .lte, .ccr = op.ccr } }; + .condition_flags => |op| { + return MCValue{ .condition_flags = .{ .cond = op.cond.negate(), .ccr = op.ccr } }; }, .immediate => |imm| { assert(imm == 0); @@ -3168,8 +3116,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .undef => unreachable, .unreach => unreachable, .dead => unreachable, - .compare_flags_unsigned, - .compare_flags_signed, + .condition_flags, .register_with_overflow, => unreachable, // cannot hold an address .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), @@ -3181,7 +3128,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo switch (dst_mcv) { .dead => unreachable, .undef => unreachable, - .compare_flags_signed, .compare_flags_unsigned => unreachable, + .condition_flags => unreachable, .register => |dst_reg| { try self.genLoad(dst_reg, addr_reg, i13, 0, elem_size); }, @@ -3277,10 +3224,10 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { }, .register_with_overflow => |rwo| { self.register_manager.freeReg(rwo.reg); - self.compare_flags_inst = null; + self.condition_flags_inst = null; }, - .compare_flags_signed, .compare_flags_unsigned => { - self.compare_flags_inst = null; + .condition_flags => { + self.condition_flags_inst = null; }, else => {}, // TODO process stack allocation death } @@ -3474,15 +3421,13 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { } } -/// Save the current instruction stored in the compare flags if +/// Save the current instruction stored in the condition flags if /// occupied -fn spillCompareFlagsIfOccupied(self: *Self) !void { - if (self.compare_flags_inst) |inst_to_save| { +fn spillConditionFlagsIfOccupied(self: *Self) !void { + if (self.condition_flags_inst) |inst_to_save| { const mcv = self.getResolvedInstValue(inst_to_save); const new_mcv = switch (mcv) { - .compare_flags_signed, - .compare_flags_unsigned, - => try self.allocRegOrMem(inst_to_save, true), + .condition_flags => try self.allocRegOrMem(inst_to_save, true), .register_with_overflow => try self.allocRegOrMem(inst_to_save, false), else => unreachable, // mcv doesn't occupy the compare flags }; @@ -3493,7 +3438,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); - self.compare_flags_inst = null; + self.condition_flags_inst = null; // TODO consolidate with register manager and spillInstruction // this call should really belong in the register manager! @@ -3522,8 +3467,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .undef => unreachable, .unreach => unreachable, .dead => unreachable, - .compare_flags_unsigned, - .compare_flags_signed, + .condition_flags, .register_with_overflow, => unreachable, // cannot hold an address .immediate => |imm| { diff --git a/src/arch/sparc64/bits.zig b/src/arch/sparc64/bits.zig index 7b0342e2a3..3ca67cb6a1 100644 --- a/src/arch/sparc64/bits.zig +++ b/src/arch/sparc64/bits.zig @@ -673,10 +673,27 @@ pub const Instruction = union(enum) { } }; - pub const Condition = packed union { + pub const ConditionTag = enum { fcond, icond }; + pub const Condition = union(ConditionTag) { fcond: FCondition, icond: ICondition, - encoded: u4, + + /// Encodes the condition into the instruction bit pattern. + pub fn enc(cond: Condition) u4 { + return switch (cond) { + .icond => |c| @enumToInt(c), + .fcond => |c| @enumToInt(c), + }; + } + + /// Returns the condition which is true iff the given condition is + /// false (if such a condition exists). + pub fn negate(cond: Condition) Condition { + return switch (cond) { + .icond => |c| .{ .icond = c.negate() }, + .fcond => |c| .{ .fcond = c.negate() }, + }; + } }; pub fn toU32(self: Instruction) u32 { @@ -755,7 +772,7 @@ pub const Instruction = union(enum) { return Instruction{ .format_2b = .{ .a = @boolToInt(annul), - .cond = cond.encoded, + .cond = cond.enc(), .op2 = op2, .disp22 = udisp_truncated, }, @@ -776,7 +793,7 @@ pub const Instruction = union(enum) { return Instruction{ .format_2c = .{ .a = @boolToInt(annul), - .cond = cond.encoded, + .cond = cond.enc(), .op2 = op2, .cc1 = ccr_cc1, .cc0 = ccr_cc0, @@ -1057,7 +1074,7 @@ pub const Instruction = union(enum) { .rd = rd.enc(), .op3 = op3, .cc2 = ccr_cc2, - .cond = cond.encoded, + .cond = cond.enc(), .cc1 = ccr_cc1, .cc0 = ccr_cc0, .rs2 = rs2.enc(), @@ -1074,7 +1091,7 @@ pub const Instruction = union(enum) { .rd = rd.enc(), .op3 = op3, .cc2 = ccr_cc2, - .cond = cond.encoded, + .cond = cond.enc(), .cc1 = ccr_cc1, .cc0 = ccr_cc0, .simm11 = @bitCast(u11, imm), @@ -1122,7 +1139,7 @@ pub const Instruction = union(enum) { .format_4g = .{ .rd = rd.enc(), .op3 = op3, - .cond = cond.encoded, + .cond = cond.enc(), .opf_cc = opf_cc, .opf_low = opf_low, .rs2 = rs2.enc(), From accc3bad6377394531a9ee772e1f90018d058d06 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 12 Jun 2022 23:35:25 +0700 Subject: [PATCH 1927/2031] stage2: sparc64: Move conditional branch emission out of airCondBr --- src/arch/sparc64/CodeGen.zig | 87 +++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 40 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 28a1ab2a24..07f30fd40a 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1231,46 +1231,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; const liveness_condbr = self.liveness.getCondBr(inst); - // Here we either emit a BPcc for branching on CCR content, - // or emit a BPr to branch on register content. - const reloc: Mir.Inst.Index = switch (condition) { - .condition_flags => |flags| try self.addInst(.{ - .tag = .bpcc, - .data = .{ - .branch_predict_int = .{ - .ccr = flags.ccr, - // Here we map to the opposite condition because the jump is to the false branch. - .cond = flags.cond.icond.negate(), - .inst = undefined, // Will be filled by performReloc - }, - }, - }), - else => blk: { - const reg = switch (condition) { - .register => |r| r, - else => try self.copyToTmpRegister(Type.bool, condition), - }; - - break :blk try self.addInst(.{ - .tag = .bpr, - .data = .{ - .branch_predict_reg = .{ - .cond = .eq_zero, - .rs1 = reg, - .inst = undefined, // populated later through performReloc - }, - }, - }); - }, - }; - - // Regardless of the branch type that's emitted, we need to reserve - // a space for the delay slot. - // TODO Find a way to fill this delay slot - _ = try self.addInst(.{ - .tag = .nop, - .data = .{ .nop = {} }, - }); + // Here we emit a branch to the false section. + const reloc: Mir.Inst.Index = try self.condBr(condition); // If the condition dies here in this condbr instruction, process // that death now instead of later as this has an effect on @@ -2424,6 +2386,51 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { block_data.relocs.appendAssumeCapacity(br_index); } +fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index { + // Here we either emit a BPcc for branching on CCR content, + // or emit a BPr to branch on register content. + const reloc: Mir.Inst.Index = switch (condition) { + .condition_flags => |flags| try self.addInst(.{ + .tag = .bpcc, + .data = .{ + .branch_predict_int = .{ + .ccr = flags.ccr, + // Here we map to the opposite condition because the jump is to the false branch. + .cond = flags.cond.icond.negate(), + .inst = undefined, // Will be filled by performReloc + }, + }, + }), + else => blk: { + const reg = switch (condition) { + .register => |r| r, + else => try self.copyToTmpRegister(Type.bool, condition), + }; + + break :blk try self.addInst(.{ + .tag = .bpr, + .data = .{ + .branch_predict_reg = .{ + .cond = .eq_zero, + .rs1 = reg, + .inst = undefined, // populated later through performReloc + }, + }, + }); + }, + }; + + // Regardless of the branch type that's emitted, we need to reserve + // a space for the delay slot. + // TODO Find a way to fill this delay slot + _ = try self.addInst(.{ + .tag = .nop, + .data = .{ .nop = {} }, + }); + + return reloc; +} + /// Copies a value to a register without tracking the register. The register is not considered /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. From e7fde5f64e29ad5a730f18e82485dda00f658db8 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 13 Jun 2022 00:25:24 +0700 Subject: [PATCH 1928/2031] stage2: sparc64: Introduce condition_register MCValue type Introduce condition_register MCValue type for future uses with BPr/MOVr (mostly when needing to compare a signed value with zero) --- src/arch/sparc64/CodeGen.zig | 60 ++++++++++++++++++++++++++++++++++-- src/arch/sparc64/Emit.zig | 4 ++- src/arch/sparc64/Mir.zig | 26 ++++++++++++++-- src/arch/sparc64/bits.zig | 15 +++++++++ 4 files changed, 98 insertions(+), 7 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 07f30fd40a..d0eee413ad 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -96,6 +96,9 @@ stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, /// Tracks the current instruction allocated to the condition flags condition_flags_inst: ?Air.Inst.Index = null, +/// Tracks the current instruction allocated to the condition register +condition_register_inst: ?Air.Inst.Index = null, + /// Offset from the stack base, representing the end of the stack frame. max_end_stack: u32 = 0, /// Represents the current end stack offset. If there is no existing slot @@ -148,6 +151,13 @@ const MCValue = union(enum) { cond: Instruction.Condition, ccr: Instruction.CCR, }, + /// The value is in the specified Register. The value is 1 (if + /// the type is u1) or true (if the type in bool) iff the + /// specified condition is true. + condition_register: struct { + cond: Instruction.RCondition, + reg: Register, + }, fn isMemory(mcv: MCValue) bool { return switch (mcv) { @@ -171,6 +181,8 @@ const MCValue = union(enum) { .immediate, .memory, + .condition_flags, + .condition_register, .ptr_stack_offset, .undef, => false, @@ -1748,7 +1760,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { _ = try self.addInst(.{ .tag = .movcc, .data = .{ - .conditional_move = .{ + .conditional_move_int = .{ .ccr = rwo.flag.ccr, .cond = .{ .icond = rwo.flag.cond }, .is_imm = true, @@ -2401,6 +2413,17 @@ fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index { }, }, }), + .condition_register => |reg| try self.addInst(.{ + .tag = .bpr, + .data = .{ + .branch_predict_reg = .{ + .rs1 = reg.reg, + // Here we map to the opposite condition because the jump is to the false branch. + .cond = reg.cond.negate(), + .inst = undefined, // Will be filled by performReloc + }, + }, + }), else => blk: { const reg = switch (condition) { .register => |r| r, @@ -2655,7 +2678,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void _ = try self.addInst(.{ .tag = .movcc, .data = .{ - .conditional_move = .{ + .conditional_move_int = .{ .ccr = ccr, .cond = condition, .is_imm = true, @@ -2665,6 +2688,34 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }, }); }, + .condition_register => |op| { + const condition = op.cond; + const register = op.reg; + + _ = try self.addInst(.{ + .tag = .mov, + .data = .{ + .arithmetic_2op = .{ + .is_imm = false, + .rs1 = reg, + .rs2_or_imm = .{ .rs2 = .g0 }, + }, + }, + }); + + _ = try self.addInst(.{ + .tag = .movr, + .data = .{ + .conditional_move_reg = .{ + .cond = condition, + .is_imm = true, + .rd = reg, + .rs1 = register, + .rs2_or_imm = .{ .imm = 1 }, + }, + }, + }); + }, .undef => { if (!self.wantSafety()) return; // The already existing value will do just fine. @@ -2832,6 +2883,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro } }, .condition_flags, + .condition_register, .immediate, .ptr_stack_offset, => { @@ -2872,7 +2924,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro _ = try self.addInst(.{ .tag = .movcc, .data = .{ - .conditional_move = .{ + .conditional_move_int = .{ .ccr = rwo.flag.ccr, .cond = .{ .icond = rwo.flag.cond }, .is_imm = true, @@ -3124,6 +3176,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .unreach => unreachable, .dead => unreachable, .condition_flags, + .condition_register, .register_with_overflow, => unreachable, // cannot hold an address .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), @@ -3475,6 +3528,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .unreach => unreachable, .dead => unreachable, .condition_flags, + .condition_register, .register_with_overflow, => unreachable, // cannot hold an address .immediate => |imm| { diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 5a082f163a..a5dc3370fb 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -99,6 +99,8 @@ pub fn emitMir( .movcc => try emit.mirConditionalMove(inst), + .movr => @panic("TODO implement sparc64 movr"), + .mulx => try emit.mirArithmetic3Op(inst), .nop => try emit.mirNop(), @@ -314,7 +316,7 @@ fn mirConditionalMove(emit: *Emit, inst: Mir.Inst.Index) !void { switch (tag) { .movcc => { - const data = emit.mir.instructions.items(.data)[inst].conditional_move; + const data = emit.mir.instructions.items(.data)[inst].conditional_move_int; if (data.is_imm) { try emit.writeInstruction(Instruction.movcc( i11, diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index 14867dde30..a3c25ff3d5 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -78,9 +78,13 @@ pub const Inst = struct { xnor, /// A.35 Move Integer Register on Condition (MOVcc) - /// This uses the conditional_move field. + /// This uses the conditional_move_int field. movcc, + /// A.36 Move Integer Register on Register Condition (MOVr) + /// This uses the conditional_move_reg field. + movr, + /// A.37 Multiply and Divide (64-bit) /// This uses the arithmetic_3op field. // TODO add other operations. @@ -230,12 +234,12 @@ pub const Inst = struct { inst: Index, }, - /// Conditional move. + /// Conditional move, checking the integer status code /// if is_imm true then it uses the imm field of rs2_or_imm, /// otherwise it uses rs2 field. /// /// Used by e.g. movcc - conditional_move: struct { + conditional_move_int: struct { is_imm: bool, ccr: Instruction.CCR, cond: Instruction.Condition, @@ -246,6 +250,22 @@ pub const Inst = struct { }, }, + /// Conditional move, comparing a register's content with zero + /// if is_imm true then it uses the imm field of rs2_or_imm, + /// otherwise it uses rs2 field. + /// + /// Used by e.g. movr + conditional_move_reg: struct { + is_imm: bool, + cond: Instruction.RCondition, + rd: Register, + rs1: Register, + rs2_or_imm: union { + rs2: Register, + imm: i10, + }, + }, + /// No additional data /// /// Used by e.g. flushw diff --git a/src/arch/sparc64/bits.zig b/src/arch/sparc64/bits.zig index 3ca67cb6a1..4c3e000438 100644 --- a/src/arch/sparc64/bits.zig +++ b/src/arch/sparc64/bits.zig @@ -475,6 +475,21 @@ pub const Instruction = union(enum) { ne_zero, gt_zero, ge_zero, + + /// Returns the condition which is true iff the given condition is + /// false (if such a condition exists). + pub fn negate(cond: RCondition) RCondition { + return switch (cond) { + .eq_zero => .ne_zero, + .ne_zero => .eq_zero, + .lt_zero => .ge_zero, + .ge_zero => .lt_zero, + .le_zero => .gt_zero, + .gt_zero => .le_zero, + .reserved1 => unreachable, + .reserved2 => unreachable, + }; + } }; pub const ASI = enum(u8) { From 5c8612642b84d2d3f91e6bbe26f9cde2579997f7 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 13 Jun 2022 00:27:51 +0700 Subject: [PATCH 1929/2031] stage2: sparc64: Use official encoding for `not rs2, rs1` --- src/arch/sparc64/Emit.zig | 2 +- src/arch/sparc64/Mir.zig | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index a5dc3370fb..19eeec023f 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -203,7 +203,7 @@ fn mirArithmetic2Op(emit: *Emit, inst: Mir.Inst.Index) !void { .@"return" => try emit.writeInstruction(Instruction.@"return"(Register, rs1, rs2)), .cmp => try emit.writeInstruction(Instruction.subcc(Register, rs1, rs2, .g0)), .mov => try emit.writeInstruction(Instruction.@"or"(Register, .g0, rs2, rs1)), - .not => try emit.writeInstruction(Instruction.xnor(Register, .g0, rs2, rs1)), + .not => try emit.writeInstruction(Instruction.xnor(Register, rs2, .g0, rs1)), else => unreachable, } } diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index a3c25ff3d5..3ed2787d8d 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -158,8 +158,9 @@ pub const Inst = struct { /// This uses the arithmetic_2op field, with rs1 /// being the *destination* register. // TODO is it okay to abuse rs1 in this way? - // TODO this differs from official encoding for convenience, fix it later - not, // not rs2/imm, rs1 -> xnor %g0, rs2/imm, rs1 + // not rs2, rs1 -> xnor rs2, %g0, rs1 + // not imm, rs1 -> xnor %g0, imm, rs1 + not, }; /// The position of an MIR instruction within the `Mir` instructions array. From 40590c8bf976a44451e161b4a75d7f8bac15a7d4 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Mon, 13 Jun 2022 22:34:10 +0700 Subject: [PATCH 1930/2031] stage2: sparc64: Save registers before calling another function --- src/arch/sparc64/CodeGen.zig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index d0eee413ad..3c9083faa1 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1081,6 +1081,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. // (SCD 2.4.1, page 3P-10) try self.spillConditionFlagsIfOccupied(); + // Save caller-saved registers, but crucially *after* we save the + // compare flags as saving compare flags may require a new + // caller-saved register + for (abi.caller_preserved_regs) |reg| { + try self.register_manager.getReg(reg, null); + } + for (info.args) |mc_arg, arg_i| { const arg = args[arg_i]; const arg_ty = self.air.typeOf(arg); From 921f77622492bce8995ac61bdfcf4b46c44865a2 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Tue, 14 Jun 2022 00:33:31 +0700 Subject: [PATCH 1931/2031] stage2: sparc64: Implement airTry --- src/arch/sparc64/CodeGen.zig | 47 +++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 3c9083faa1..4c8ba89dd3 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -13,6 +13,7 @@ const link = @import("../../link.zig"); const Module = @import("../../Module.zig"); const TypedValue = @import("../../TypedValue.zig"); const ErrorMsg = Module.ErrorMsg; +const codegen = @import("../../codegen.zig"); const Air = @import("../../Air.zig"); const Mir = @import("Mir.zig"); const Emit = @import("Emit.zig"); @@ -26,6 +27,8 @@ const build_options = @import("build_options"); const bits = @import("bits.zig"); const abi = @import("abi.zig"); +const errUnionPayloadOffset = codegen.errUnionPayloadOffset; +const errUnionErrorOffset = codegen.errUnionErrorOffset; const Instruction = bits.Instruction; const ShiftWidth = Instruction.ShiftWidth; const RegisterManager = abi.RegisterManager; @@ -627,7 +630,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .prefetch => @panic("TODO try self.airPrefetch(inst)"), .mul_add => @panic("TODO try self.airMulAdd(inst)"), - .@"try" => @panic("TODO try self.airTry(inst)"), + .@"try" => try self.airTry(inst), .try_ptr => @panic("TODO try self.airTryPtr(inst)"), .dbg_var_ptr, @@ -1796,6 +1799,24 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement switch for {}", .{self.target.cpu.arch}); } +fn airTry(self: *Self, inst: Air.Inst.Index) !void { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const extra = self.air.extraData(Air.Try, pl_op.payload); + const body = self.air.extra[extra.end..][0..extra.data.body_len]; + const result: MCValue = result: { + const error_union_ty = self.air.typeOf(pl_op.operand); + const error_union = try self.resolveInst(pl_op.operand); + const is_err_result = try self.isErr(error_union_ty, error_union); + const reloc = try self.condBr(is_err_result); + + try self.genBody(body); + + try self.performReloc(reloc); + break :result try self.errUnionPayload(error_union, error_union_ty); + }; + return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); +} + fn airUnwrapErrErr(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 result: { @@ -2475,6 +2496,30 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { try table.ensureUnusedCapacity(self.gpa, additional_count); } +/// Given an error union, returns the payload +fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { + const err_ty = error_union_ty.errorUnionSet(); + const payload_ty = error_union_ty.errorUnionPayload(); + if (err_ty.errorSetIsEmpty()) { + return error_union_mcv; + } + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { + return MCValue.none; + } + + const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*)); + switch (error_union_mcv) { + .register => return self.fail("TODO errUnionPayload for registers", .{}), + .stack_offset => |off| { + return MCValue{ .stack_offset = off - payload_offset }; + }, + .memory => |addr| { + return MCValue{ .memory = addr + payload_offset }; + }, + else => unreachable, // invalid MCValue for an error union + } +} + fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { @setCold(true); assert(self.err_msg == null); From 6218d70d09cf60e3c1d83cdd5b49ea9eed3d5259 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 15 Jun 2022 23:23:38 +0700 Subject: [PATCH 1932/2031] stage2: sparc64: Implement airBinOp for and, or, and xor --- src/arch/sparc64/CodeGen.zig | 66 ++++++++++++++++++++++++++++++++++-- src/arch/sparc64/Emit.zig | 1 + src/arch/sparc64/Mir.zig | 1 + 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 4c8ba89dd3..80c4468531 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -563,9 +563,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .bool_and => @panic("TODO try self.airBoolOp(inst)"), .bool_or => @panic("TODO try self.airBoolOp(inst)"), - .bit_and => @panic("TODO try self.airBitAnd(inst)"), - .bit_or => @panic("TODO try self.airBitOr(inst)"), - .xor => @panic("TODO try self.airXor(inst)"), + .bit_and => try self.airBinOp(inst, .bit_and), + .bit_or => try self.airBinOp(inst, .bit_or), + .xor => try self.airBinOp(inst, .xor), .shr, .shr_exact => @panic("TODO try self.airShr(inst)"), .alloc => try self.airAlloc(inst), @@ -2093,6 +2093,58 @@ fn binOp( } }, + .bit_and, + .bit_or, + .xor, + => { + switch (lhs_ty.zigTypeTag()) { + .Vector => return self.fail("TODO binary operations on vectors", .{}), + .Int => { + assert(lhs_ty.eql(rhs_ty, mod)); + const int_info = lhs_ty.intInfo(self.target.*); + if (int_info.bits <= 64) { + // Only say yes if the operation is + // commutative, i.e. we can swap both of the + // operands + const lhs_immediate_ok = switch (tag) { + .bit_and, + .bit_or, + .xor, + => lhs == .immediate and lhs.immediate <= std.math.maxInt(u13), + else => unreachable, + }; + const rhs_immediate_ok = switch (tag) { + .bit_and, + .bit_or, + .xor, + => rhs == .immediate and rhs.immediate <= std.math.maxInt(u13), + else => unreachable, + }; + + const mir_tag: Mir.Inst.Tag = switch (tag) { + .bit_and => .@"and", + .bit_or => .@"or", + .xor => .xor, + else => unreachable, + }; + + if (rhs_immediate_ok) { + return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); + } else if (lhs_immediate_ok) { + // swap lhs and rhs + return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata); + } else { + // TODO convert large immediates to register before adding + return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); + } + } else { + return self.fail("TODO binary operations on int with bits > 64", .{}); + } + }, + else => unreachable, + } + }, + .shl => { const base_tag: Air.Inst.Tag = switch (tag) { .shl => .shl_exact, @@ -2221,6 +2273,10 @@ fn binOpImmediate( const mir_data: Mir.Inst.Data = switch (mir_tag) { .add, .addcc, + .@"and", + .@"or", + .xor, + .xnor, .mulx, .subcc, => .{ @@ -2339,6 +2395,10 @@ fn binOpRegister( const mir_data: Mir.Inst.Data = switch (mir_tag) { .add, .addcc, + .@"and", + .@"or", + .xor, + .xnor, .mulx, .subcc, => .{ diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 19eeec023f..e3b65d93df 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -93,6 +93,7 @@ pub fn emitMir( .lduw => try emit.mirArithmetic3Op(inst), .ldx => try emit.mirArithmetic3Op(inst), + .@"and" => @panic("TODO implement sparc64 and"), .@"or" => try emit.mirArithmetic3Op(inst), .xor => try emit.mirArithmetic3Op(inst), .xnor => try emit.mirArithmetic3Op(inst), diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index 3ed2787d8d..b46ecd9c79 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -73,6 +73,7 @@ pub const Inst = struct { /// A.31 Logical Operations /// This uses the arithmetic_3op field. // TODO add other operations. + @"and", @"or", xor, xnor, From 672cd2f02f734a58c577a00f2bef831b6f73e8ff Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 15 Jun 2022 23:29:19 +0700 Subject: [PATCH 1933/2031] stage2: sparc64: Implement SPARCv9 and --- src/arch/sparc64/Emit.zig | 4 +++- src/arch/sparc64/bits.zig | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index e3b65d93df..18abea63ed 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -93,7 +93,7 @@ pub fn emitMir( .lduw => try emit.mirArithmetic3Op(inst), .ldx => try emit.mirArithmetic3Op(inst), - .@"and" => @panic("TODO implement sparc64 and"), + .@"and" => try emit.mirArithmetic3Op(inst), .@"or" => try emit.mirArithmetic3Op(inst), .xor => try emit.mirArithmetic3Op(inst), .xnor => try emit.mirArithmetic3Op(inst), @@ -227,6 +227,7 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { .lduh => try emit.writeInstruction(Instruction.lduh(i13, rs1, imm, rd)), .lduw => try emit.writeInstruction(Instruction.lduw(i13, rs1, imm, rd)), .ldx => try emit.writeInstruction(Instruction.ldx(i13, rs1, imm, rd)), + .@"and" => try emit.writeInstruction(Instruction.@"and"(i13, rs1, imm, rd)), .@"or" => try emit.writeInstruction(Instruction.@"or"(i13, rs1, imm, rd)), .xor => try emit.writeInstruction(Instruction.xor(i13, rs1, imm, rd)), .xnor => try emit.writeInstruction(Instruction.xnor(i13, rs1, imm, rd)), @@ -251,6 +252,7 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { .lduh => try emit.writeInstruction(Instruction.lduh(Register, rs1, rs2, rd)), .lduw => try emit.writeInstruction(Instruction.lduw(Register, rs1, rs2, rd)), .ldx => try emit.writeInstruction(Instruction.ldx(Register, rs1, rs2, rd)), + .@"and" => try emit.writeInstruction(Instruction.@"and"(Register, rs1, rs2, rd)), .@"or" => try emit.writeInstruction(Instruction.@"or"(Register, rs1, rs2, rd)), .xor => try emit.writeInstruction(Instruction.xor(Register, rs1, rs2, rd)), .xnor => try emit.writeInstruction(Instruction.xnor(Register, rs1, rs2, rd)), diff --git a/src/arch/sparc64/bits.zig b/src/arch/sparc64/bits.zig index 4c3e000438..29ef9da1d2 100644 --- a/src/arch/sparc64/bits.zig +++ b/src/arch/sparc64/bits.zig @@ -1229,6 +1229,14 @@ pub const Instruction = union(enum) { }; } + pub fn @"and"(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b10, 0b00_0001, rs1, rs2, rd), + i13 => format3b(0b10, 0b00_0001, rs1, rs2, rd), + else => unreachable, + }; + } + pub fn @"or"(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { return switch (s2) { Register => format3a(0b10, 0b00_0010, rs1, rs2, rd), From 18d61d691c0c28405a521422a590e6ea84d13c61 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Wed, 15 Jun 2022 23:34:36 +0700 Subject: [PATCH 1934/2031] stage2: sparc64: Implement airArrayElemVal --- src/arch/sparc64/CodeGen.zig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 80c4468531..6f6137e030 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -669,7 +669,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .ptr_slice_len_ptr => @panic("TODO try self.airPtrSliceLenPtr(inst)"), .ptr_slice_ptr_ptr => @panic("TODO try self.airPtrSlicePtrPtr(inst)"), - .array_elem_val => @panic("TODO try self.airArrayElemVal(inst)"), + .array_elem_val => try self.airArrayElemVal(inst), .slice_elem_val => try self.airSliceElemVal(inst), .slice_elem_ptr => @panic("TODO try self.airSliceElemPtr(inst)"), .ptr_elem_val => @panic("TODO try self.airPtrElemVal(inst)"), @@ -796,6 +796,12 @@ fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .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}); + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .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 result: { From 36bfe4b7efedd1b030c335151b2d6a61bbeab1eb Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 16 Jun 2022 01:32:02 +0700 Subject: [PATCH 1935/2031] stage2: sparc64: Implement airFence + SPARCv9 membar --- src/arch/sparc64/CodeGen.zig | 25 ++++++++++++++++++++++++- src/arch/sparc64/Emit.zig | 13 +++++++++++++ src/arch/sparc64/Mir.zig | 11 +++++++++++ src/arch/sparc64/bits.zig | 4 ++++ 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 6f6137e030..b071f8ac59 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -578,7 +578,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .breakpoint => try self.airBreakpoint(), .ret_addr => @panic("TODO try self.airRetAddr(inst)"), .frame_addr => @panic("TODO try self.airFrameAddress(inst)"), - .fence => @panic("TODO try self.airFence()"), + .fence => try self.airFence(inst), .cond_br => try self.airCondBr(inst), .dbg_stmt => try self.airDbgStmt(inst), .fptrunc => @panic("TODO try self.airFptrunc(inst)"), @@ -1442,6 +1442,29 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airFence(self: *Self, inst: Air.Inst.Index) !void { + // TODO weaken this as needed, currently this implements the strongest membar form + const fence = self.air.instructions.items(.data)[inst].fence; + _ = fence; + + // membar #StoreStore | #LoadStore | #StoreLoad | #LoadLoad + _ = try self.addInst(.{ + .tag = .membar, + .data = .{ + .membar_mask = .{ + .mmask = .{ + .store_store = true, + .store_load = true, + .load_store = true, + .load_load = true, + }, + }, + }, + }); + + return self.finishAir(inst, .dead, .{ .none, .none, .none }); +} + fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 18abea63ed..e1546fbc28 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -98,6 +98,8 @@ pub fn emitMir( .xor => try emit.mirArithmetic3Op(inst), .xnor => try emit.mirArithmetic3Op(inst), + .membar => try emit.mirMembar(inst), + .movcc => try emit.mirConditionalMove(inst), .movr => @panic("TODO implement sparc64 movr"), @@ -342,6 +344,17 @@ fn mirConditionalMove(emit: *Emit, inst: Mir.Inst.Index) !void { } } +fn mirMembar(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const mask = emit.mir.instructions.items(.data)[inst].membar_mask; + assert(tag == .membar); + + try emit.writeInstruction(Instruction.membar( + mask.cmask, + mask.mmask, + )); +} + fn mirNop(emit: *Emit) !void { try emit.writeInstruction(Instruction.nop()); } diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index b46ecd9c79..25b4a4ce2c 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -78,6 +78,10 @@ pub const Inst = struct { xor, xnor, + /// A.32 Memory Barrier + /// This uses the membar_mask field. + membar, + /// A.35 Move Integer Register on Condition (MOVcc) /// This uses the conditional_move_int field. movcc, @@ -236,6 +240,13 @@ pub const Inst = struct { inst: Index, }, + /// Membar mask, controls the barrier behavior + /// Used by e.g. membar + membar_mask: struct { + mmask: Instruction.MemOrderingConstraint = .{}, + cmask: Instruction.MemCompletionConstraint = .{}, + }, + /// Conditional move, checking the integer status code /// if is_imm true then it uses the imm field of rs2_or_imm, /// otherwise it uses rs2 field. diff --git a/src/arch/sparc64/bits.zig b/src/arch/sparc64/bits.zig index 29ef9da1d2..5c856ea756 100644 --- a/src/arch/sparc64/bits.zig +++ b/src/arch/sparc64/bits.zig @@ -1261,6 +1261,10 @@ pub const Instruction = union(enum) { }; } + pub fn membar(cmask: MemCompletionConstraint, mmask: MemOrderingConstraint) Instruction { + return format3h(cmask, mmask); + } + pub fn movcc(comptime s2: type, cond: Condition, ccr: CCR, rs2: s2, rd: Register) Instruction { return switch (s2) { Register => format4c(0b10_1100, cond, ccr, rs2, rd), From 513ab4eb568da6fb926dd33ae067d31cf4831e79 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 16 Jun 2022 23:31:04 +0700 Subject: [PATCH 1936/2031] stage2: sparc64: Implement airIntCast basics --- src/arch/sparc64/CodeGen.zig | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index b071f8ac59..515d77032f 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -583,7 +583,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .dbg_stmt => try self.airDbgStmt(inst), .fptrunc => @panic("TODO try self.airFptrunc(inst)"), .fpext => @panic("TODO try self.airFpext(inst)"), - .intcast => @panic("TODO try self.airIntCast(inst)"), + .intcast => try self.airIntCast(inst), .trunc => @panic("TODO try self.airTrunc(inst)"), .bool_to_int => @panic("TODO try self.airBoolToInt(inst)"), .is_non_null => @panic("TODO try self.airIsNonNull(inst)"), @@ -1465,6 +1465,24 @@ fn airFence(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .dead, .{ .none, .none, .none }); } +fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + if (self.liveness.isUnused(inst)) + return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); + + const operand_ty = self.air.typeOf(ty_op.operand); + const operand = try self.resolveInst(ty_op.operand); + const info_a = operand_ty.intInfo(self.target.*); + const info_b = self.air.typeOfIndex(inst).intInfo(self.target.*); + if (info_a.signedness != info_b.signedness) + return self.fail("TODO gen intcast sign safety in semantic analysis", .{}); + + if (info_a.bits == info_b.bits) + return self.finishAir(inst, operand, .{ ty_op.operand, .none, .none }); + + return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch}); +} + fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { From 4d15284c3cbeefa771baa2b69866b2f0252f147b Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 16 Jun 2022 23:53:22 +0700 Subject: [PATCH 1937/2031] stage2: sparc64: Implement SPARCv9 shifts --- src/arch/sparc64/CodeGen.zig | 7 ------ src/arch/sparc64/Emit.zig | 44 ++++++++++++++++++++++++++++----- src/arch/sparc64/Mir.zig | 1 - src/arch/sparc64/bits.zig | 48 ++++++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 14 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 515d77032f..4bd1652ac1 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -2337,7 +2337,6 @@ fn binOpImmediate( .sllx => .{ .shift = .{ .is_imm = true, - .width = ShiftWidth.shift64, .rd = dest_reg, .rs1 = lhs_reg, .rs2_or_imm = .{ .imm = @intCast(u6, rhs.immediate) }, @@ -2459,7 +2458,6 @@ fn binOpRegister( .sllx => .{ .shift = .{ .is_imm = false, - .width = ShiftWidth.shift64, .rd = dest_reg, .rs1 = lhs_reg, .rs2_or_imm = .{ .rs2 = rhs_reg }, @@ -2940,7 +2938,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .data = .{ .shift = .{ .is_imm = true, - .width = .shift64, .rd = reg, .rs1 = reg, .rs2_or_imm = .{ .imm = 12 }, @@ -2971,7 +2968,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .data = .{ .shift = .{ .is_imm = true, - .width = .shift64, .rd = reg, .rs1 = reg, .rs2_or_imm = .{ .imm = 32 }, @@ -3768,7 +3764,6 @@ fn truncRegister( .data = .{ .shift = .{ .is_imm = true, - .width = ShiftWidth.shift64, .rd = dest_reg, .rs1 = operand_reg, .rs2_or_imm = .{ .imm = @intCast(u6, 64 - int_bits) }, @@ -3783,7 +3778,6 @@ fn truncRegister( .data = .{ .shift = .{ .is_imm = true, - .width = ShiftWidth.shift32, .rd = dest_reg, .rs1 = dest_reg, .rs2_or_imm = .{ .imm = @intCast(u6, int_bits) }, @@ -3800,7 +3794,6 @@ fn truncRegister( .data = .{ .shift = .{ .is_imm = true, - .width = ShiftWidth.shift32, .rd = dest_reg, .rs1 = operand_reg, .rs2_or_imm = .{ .imm = 0 }, diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index e1546fbc28..44680c2e12 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -115,12 +115,12 @@ pub fn emitMir( .sethi => try emit.mirSethi(inst), - .sll => @panic("TODO implement sparc64 sll"), - .srl => @panic("TODO implement sparc64 srl"), - .sra => @panic("TODO implement sparc64 sra"), - .sllx => @panic("TODO implement sparc64 sllx"), - .srlx => @panic("TODO implement sparc64 srlx"), - .srax => @panic("TODO implement sparc64 srax"), + .sll => try emit.mirShift(inst), + .srl => try emit.mirShift(inst), + .sra => try emit.mirShift(inst), + .sllx => try emit.mirShift(inst), + .srlx => try emit.mirShift(inst), + .srax => try emit.mirShift(inst), .stb => try emit.mirArithmetic3Op(inst), .sth => try emit.mirArithmetic3Op(inst), @@ -370,6 +370,38 @@ fn mirSethi(emit: *Emit, inst: Mir.Inst.Index) !void { try emit.writeInstruction(Instruction.sethi(imm, rd)); } +fn mirShift(emit: *Emit, inst: Mir.Inst.Index) !void { + const tag = emit.mir.instructions.items(.tag)[inst]; + const data = emit.mir.instructions.items(.data)[inst].shift; + + const rd = data.rd; + const rs1 = data.rs1; + + if (data.is_imm) { + const imm = data.rs2_or_imm.imm; + switch (tag) { + .sll => try emit.writeInstruction(Instruction.sll(u5, rs1, @truncate(u5, imm), rd)), + .srl => try emit.writeInstruction(Instruction.srl(u5, rs1, @truncate(u5, imm), rd)), + .sra => try emit.writeInstruction(Instruction.sra(u5, rs1, @truncate(u5, imm), rd)), + .sllx => try emit.writeInstruction(Instruction.sllx(u6, rs1, imm, rd)), + .srlx => try emit.writeInstruction(Instruction.srlx(u6, rs1, imm, rd)), + .srax => try emit.writeInstruction(Instruction.srax(u6, rs1, imm, rd)), + else => unreachable, + } + } else { + const rs2 = data.rs2_or_imm.rs2; + switch (tag) { + .sll => try emit.writeInstruction(Instruction.sll(Register, rs1, rs2, rd)), + .srl => try emit.writeInstruction(Instruction.srl(Register, rs1, rs2, rd)), + .sra => try emit.writeInstruction(Instruction.sra(Register, rs1, rs2, rd)), + .sllx => try emit.writeInstruction(Instruction.sllx(Register, rs1, rs2, rd)), + .srlx => try emit.writeInstruction(Instruction.srlx(Register, rs1, rs2, rd)), + .srax => try emit.writeInstruction(Instruction.srax(Register, rs1, rs2, rd)), + else => unreachable, + } + } +} + fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; const data = emit.mir.instructions.items(.data)[inst].trap; diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index 25b4a4ce2c..3ae84d0961 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -299,7 +299,6 @@ pub const Inst = struct { /// Used by e.g. sllx shift: struct { is_imm: bool, - width: Instruction.ShiftWidth, rd: Register, rs1: Register, rs2_or_imm: union { diff --git a/src/arch/sparc64/bits.zig b/src/arch/sparc64/bits.zig index 5c856ea756..a722d06dee 100644 --- a/src/arch/sparc64/bits.zig +++ b/src/arch/sparc64/bits.zig @@ -1313,6 +1313,54 @@ pub const Instruction = union(enum) { return format2a(0b100, imm, rd); } + pub fn sll(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3k(0b11, 0b10_0101, .shift32, rs1, rs2, rd), + u5 => format3l(0b11, 0b10_0101, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn srl(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3k(0b11, 0b10_0110, .shift32, rs1, rs2, rd), + u5 => format3l(0b11, 0b10_0110, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn sra(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3k(0b11, 0b10_0111, .shift32, rs1, rs2, rd), + u5 => format3l(0b11, 0b10_0111, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn sllx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3k(0b11, 0b10_0101, .shift64, rs1, rs2, rd), + u6 => format3m(0b11, 0b10_0101, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn srlx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3k(0b11, 0b10_0110, .shift64, rs1, rs2, rd), + u6 => format3m(0b11, 0b10_0110, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn srax(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3k(0b11, 0b10_0111, .shift64, rs1, rs2, rd), + u6 => format3m(0b11, 0b10_0111, rs1, rs2, rd), + else => unreachable, + }; + } + pub fn stb(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { return switch (s2) { Register => format3a(0b11, 0b00_0101, rs1, rs2, rd), From be6718b79619c4c3d8ecbebe8be93113655d0d5b Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 19 Jun 2022 10:38:00 +0700 Subject: [PATCH 1938/2031] stage2: sparc64: Implement airIsNull/airIsNonNull --- src/arch/sparc64/CodeGen.zig | 44 ++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 4bd1652ac1..17cb700807 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -586,9 +586,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .intcast => try self.airIntCast(inst), .trunc => @panic("TODO try self.airTrunc(inst)"), .bool_to_int => @panic("TODO try self.airBoolToInt(inst)"), - .is_non_null => @panic("TODO try self.airIsNonNull(inst)"), + .is_non_null => try self.airIsNonNull(inst), .is_non_null_ptr => @panic("TODO try self.airIsNonNullPtr(inst)"), - .is_null => @panic("TODO try self.airIsNull(inst)"), + .is_null => try self.airIsNull(inst), .is_null_ptr => @panic("TODO try self.airIsNullPtr(inst)"), .is_non_err => try self.airIsNonErr(inst), .is_non_err_ptr => @panic("TODO try self.airIsNonErrPtr(inst)"), @@ -1503,6 +1503,24 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ un_op, .none, .none }); } +fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const operand = try self.resolveInst(un_op); + break :result try self.isNull(operand); + }; + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + +fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const operand = try self.resolveInst(un_op); + break :result try self.isNonNull(operand); + }; + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn airLoad(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const elem_ty = self.air.typeOfIndex(inst); @@ -3290,6 +3308,28 @@ fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { } } +fn isNull(self: *Self, operand: MCValue) !MCValue { + _ = operand; + // Here you can specialize this instruction if it makes sense to, otherwise the default + // will call isNonNull and invert the result. + return self.fail("TODO call isNonNull and invert the result", .{}); +} + +fn isNonNull(self: *Self, operand: MCValue) !MCValue { + // Call isNull, then negate the result. + const is_null_result = try self.isNull(operand); + switch (is_null_result) { + .condition_flags => |op| { + return MCValue{ .condition_flags = .{ .cond = op.cond.negate(), .ccr = op.ccr } }; + }, + .immediate => |imm| { + assert(imm == 0); + return MCValue{ .immediate = 1 }; + }, + else => unreachable, + } +} + fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb { try self.ensureProcessDeathCapacity(operand_count + 1); return BigTomb{ From 837cd0d8af294405316dc6757ce3d0e105c86218 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 19 Jun 2022 10:38:50 +0700 Subject: [PATCH 1939/2031] stage2: sparc64: Fix airNot comments/error messages --- src/arch/sparc64/CodeGen.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 17cb700807..cf93ed6e4d 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1581,7 +1581,6 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { else => { switch (operand_ty.zigTypeTag()) { .Bool => { - // TODO convert this to mvn + and const op_reg = switch (operand) { .register => |r| r, else => try self.copyToTmpRegister(operand_ty, operand), @@ -1647,7 +1646,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { break :result MCValue{ .register = dest_reg }; } else { - return self.fail("TODO AArch64 not on integers > u64/i64", .{}); + return self.fail("TODO sparc64 not on integers > u64/i64", .{}); } }, else => unreachable, From 4fd0500bb570572a89c8f342c6d9bdb4a69331a7 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 19 Jun 2022 10:46:50 +0700 Subject: [PATCH 1940/2031] stage2: sparc64: Implement airBoolToInt --- src/arch/sparc64/CodeGen.zig | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index cf93ed6e4d..b77d76a10e 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -585,7 +585,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .fpext => @panic("TODO try self.airFpext(inst)"), .intcast => try self.airIntCast(inst), .trunc => @panic("TODO try self.airTrunc(inst)"), - .bool_to_int => @panic("TODO try self.airBoolToInt(inst)"), + .bool_to_int => try self.airBoolToInt(inst), .is_non_null => try self.airIsNonNull(inst), .is_non_null_ptr => @panic("TODO try self.airIsNonNullPtr(inst)"), .is_null => try self.airIsNull(inst), @@ -985,6 +985,13 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand; + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; From 26b63b022fdbc163642cf274a9ca87b0194bec7d Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 19 Jun 2022 18:57:28 +0700 Subject: [PATCH 1941/2031] stage2: sparc64: Add airMemset placeholder --- src/arch/sparc64/CodeGen.zig | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index b77d76a10e..cfd2426bd3 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -611,7 +611,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .atomic_rmw => @panic("TODO try self.airAtomicRmw(inst)"), .atomic_load => @panic("TODO try self.airAtomicLoad(inst)"), .memcpy => @panic("TODO try self.airMemcpy(inst)"), - .memset => @panic("TODO try self.airMemset(inst)"), + .memset => try self.airMemset(inst), .set_union_tag => @panic("TODO try self.airSetUnionTag(inst)"), .get_union_tag => @panic("TODO try self.airGetUnionTag(inst)"), .clz => @panic("TODO try self.airClz(inst)"), @@ -1569,6 +1569,20 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { return self.finishAirBookkeeping(); } +fn airMemset(self: *Self, inst: Air.Inst.Index) !void { + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const extra = self.air.extraData(Air.Bin, pl_op.payload); + + const operand = pl_op.operand; + const value = extra.data.lhs; + const length = extra.data.rhs; + _ = operand; + _ = value; + _ = length; + + return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); +} + fn airNot(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 result: { From e2cb25358e312009fef2d24b80a1e286e0494ee6 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 19 Jun 2022 21:32:55 +0700 Subject: [PATCH 1942/2031] stage2: sparc64: Implement airCall with BigTomb use for return --- src/arch/sparc64/CodeGen.zig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index cfd2426bd3..f4387f066a 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1197,7 +1197,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. return self.finishAir(inst, result, buf); } - @panic("TODO handle return value with BigTomb"); + var bt = try self.iterateBigTomb(inst, 1 + args.len); + bt.feed(callee); + for (args) |arg| { + bt.feed(arg); + } + return bt.finishAir(result); } fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { From dd57729299dea8d388e2e76c79b8c090b7e4164e Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 19 Jun 2022 21:35:17 +0700 Subject: [PATCH 1943/2031] stage2: sparc64: Update BigTomb implementation to use Liveness Change BigTomb implementation to call to Liveness' implementation rather than implementing feed() in itself. This is modelled after the AArch64 backend. --- src/arch/sparc64/CodeGen.zig | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index f4387f066a..d8e46d4bf5 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -236,26 +236,12 @@ const CallMCValues = struct { const BigTomb = struct { function: *Self, inst: Air.Inst.Index, - tomb_bits: Liveness.Bpi, - big_tomb_bits: u32, - bit_index: usize, + lbt: Liveness.BigTomb, fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void { - const this_bit_index = bt.bit_index; - bt.bit_index += 1; - - const op_int = @enumToInt(op_ref); - if (op_int < Air.Inst.Ref.typed_value_map.len) return; - const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); - - if (this_bit_index < Liveness.bpi - 1) { - const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0; - if (!dies) return; - } else { - const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1)); - const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0; - if (!dies) return; - } + const dies = bt.lbt.feed(); + const op_index = Air.refToIndex(op_ref) orelse return; + if (!dies) return; bt.function.processDeath(op_index); } @@ -3360,9 +3346,7 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT return BigTomb{ .function = self, .inst = inst, - .tomb_bits = self.liveness.getTombBits(inst), - .big_tomb_bits = self.liveness.special.get(inst) orelse 0, - .bit_index = 0, + .lbt = self.liveness.iterateBigTomb(inst), }; } From 84a57987c718de189ec8dca149ed22486d3ee375 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Sun, 19 Jun 2022 22:14:34 +0700 Subject: [PATCH 1944/2031] stage2: sparc64: Implement airAggregateInit --- src/arch/sparc64/CodeGen.zig | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index d8e46d4bf5..673428c91a 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -611,7 +611,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .select => @panic("TODO try self.airSelect(inst)"), .shuffle => @panic("TODO try self.airShuffle(inst)"), .reduce => @panic("TODO try self.airReduce(inst)"), - .aggregate_init => @panic("TODO try self.airAggregateInit(inst)"), + .aggregate_init => try self.airAggregateInit(inst), .union_init => @panic("TODO try self.airUnionInit(inst)"), .prefetch => @panic("TODO try self.airPrefetch(inst)"), .mul_add => @panic("TODO try self.airMulAdd(inst)"), @@ -777,6 +777,28 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); } +fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { + const vector_ty = self.air.typeOfIndex(inst); + const len = vector_ty.vectorLen(); + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); + const result: MCValue = res: { + if (self.liveness.isUnused(inst)) break :res MCValue.dead; + return self.fail("TODO implement airAggregateInit for {}", .{self.target.cpu.arch}); + }; + + if (elements.len <= Liveness.bpi - 1) { + var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); + std.mem.copy(Air.Inst.Ref, &buf, elements); + return self.finishAir(inst, result, buf); + } + var bt = try self.iterateBigTomb(inst, elements.len); + for (elements) |elem| { + bt.feed(elem); + } + return bt.finishAir(result); +} + fn airAlloc(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 }); From 27adee3f12f1940a16e5042b7172ed61c32b1e76 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Tue, 21 Jun 2022 22:33:06 +0700 Subject: [PATCH 1945/2031] stage2: sparc64: Implement airErrUnionPayloadPtrSet --- src/arch/sparc64/CodeGen.zig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 673428c91a..4adbfdebc9 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -672,7 +672,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst), .unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"), .unwrap_errunion_payload_ptr=> @panic("TODO try self.airUnwrapErrPayloadPtr(inst)"), - .errunion_payload_ptr_set => @panic("TODO try self.airErrUnionPayloadPtrSet(inst)"), + .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst), .err_return_trace => @panic("TODO try self.airErrReturnTrace(inst)"), .set_err_return_trace => @panic("TODO try self.airSetErrReturnTrace(inst)"), @@ -1462,6 +1462,12 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airErrUnionPayloadPtrSet(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 return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airFence(self: *Self, inst: Air.Inst.Index) !void { // TODO weaken this as needed, currently this implements the strongest membar form const fence = self.air.instructions.items(.data)[inst].fence; From 65c5ef52e94323e15005cb943cd98b929119ce39 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Tue, 21 Jun 2022 23:19:09 +0700 Subject: [PATCH 1946/2031] stage2: sparc64: Implement airRem, airMod, and SPARCv9 s/udivx --- src/arch/sparc64/CodeGen.zig | 208 ++++++++++++++++++++++++++++++++++- src/arch/sparc64/Emit.zig | 6 + src/arch/sparc64/Mir.zig | 3 +- src/arch/sparc64/bits.zig | 16 +++ 4 files changed, 229 insertions(+), 4 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 4adbfdebc9..a28cb0002c 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -507,8 +507,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .mul => @panic("TODO try self.airMul(inst)"), .mulwrap => @panic("TODO try self.airMulWrap(inst)"), .mul_sat => @panic("TODO try self.airMulSat(inst)"), - .rem => @panic("TODO try self.airRem(inst)"), - .mod => @panic("TODO try self.airMod(inst)"), + .rem => try self.airRem(inst), + .mod => try self.airMod(inst), .shl, .shl_exact => @panic("TODO try self.airShl(inst)"), .shl_sat => @panic("TODO try self.airShlSat(inst)"), .min => @panic("TODO try self.airMin(inst)"), @@ -1602,6 +1602,144 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) !void { return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); } +fn airMod(self: *Self, inst: Air.Inst.Index) !void { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + assert(lhs_ty.eql(rhs_ty, self.bin_file.options.module.?)); + + if (self.liveness.isUnused(inst)) + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + + // TODO add safety check + + // We use manual assembly emission to generate faster code + // First, ensure lhs, rhs, rem, and added are in registers + + const lhs_is_register = lhs == .register; + const rhs_is_register = rhs == .register; + + const lhs_reg = if (lhs_is_register) + lhs.register + else + try self.register_manager.allocReg(null, gp); + + const lhs_lock = self.register_manager.lockReg(lhs_reg); + defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); + + const rhs_reg = if (rhs_is_register) + rhs.register + else + try self.register_manager.allocReg(null, gp); + const rhs_lock = self.register_manager.lockReg(rhs_reg); + defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); + + if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); + if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); + + const regs = try self.register_manager.allocRegs(2, .{ null, null }, gp); + const regs_locks = self.register_manager.lockRegsAssumeUnused(2, regs); + defer for (regs_locks) |reg| { + self.register_manager.unlockReg(reg); + }; + + const add_reg = regs[0]; + const mod_reg = regs[1]; + + // mod_reg = @rem(lhs_reg, rhs_reg) + _ = try self.addInst(.{ + .tag = .sdivx, + .data = .{ + .arithmetic_3op = .{ + .is_imm = false, + .rd = mod_reg, + .rs1 = lhs_reg, + .rs2_or_imm = .{ .rs2 = rhs_reg }, + }, + }, + }); + + _ = try self.addInst(.{ + .tag = .mulx, + .data = .{ + .arithmetic_3op = .{ + .is_imm = false, + .rd = mod_reg, + .rs1 = mod_reg, + .rs2_or_imm = .{ .rs2 = rhs_reg }, + }, + }, + }); + + _ = try self.addInst(.{ + .tag = .sub, + .data = .{ + .arithmetic_3op = .{ + .is_imm = false, + .rd = mod_reg, + .rs1 = lhs_reg, + .rs2_or_imm = .{ .rs2 = mod_reg }, + }, + }, + }); + + // add_reg = mod_reg + rhs_reg + _ = try self.addInst(.{ + .tag = .add, + .data = .{ + .arithmetic_3op = .{ + .is_imm = false, + .rd = add_reg, + .rs1 = mod_reg, + .rs2_or_imm = .{ .rs2 = rhs_reg }, + }, + }, + }); + + // if (add_reg == rhs_reg) add_reg = 0 + _ = try self.addInst(.{ + .tag = .cmp, + .data = .{ + .arithmetic_2op = .{ + .is_imm = false, + .rs1 = add_reg, + .rs2_or_imm = .{ .rs2 = rhs_reg }, + }, + }, + }); + + _ = try self.addInst(.{ + .tag = .movcc, + .data = .{ + .conditional_move_int = .{ + .is_imm = true, + .ccr = .xcc, + .cond = .{ .icond = .eq }, + .rd = add_reg, + .rs2_or_imm = .{ .imm = 0 }, + }, + }, + }); + + // if (lhs_reg < 0) mod_reg = add_reg + _ = try self.addInst(.{ + .tag = .movr, + .data = .{ + .conditional_move_reg = .{ + .is_imm = false, + .cond = .lt_zero, + .rd = mod_reg, + .rs1 = lhs_reg, + .rs2_or_imm = .{ .rs2 = add_reg }, + }, + }, + }); + + return self.finishAir(inst, .{ .register = mod_reg }, .{ bin_op.lhs, bin_op.rhs, .none }); +} + fn airNot(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 result: { @@ -1704,6 +1842,27 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); } +fn airRem(self: *Self, inst: Air.Inst.Index) !void { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + + // TODO add safety check + + // result = lhs - @divTrunc(lhs, rhs) * rhs + const result: MCValue = if (self.liveness.isUnused(inst)) blk: { + break :blk .dead; + } else blk: { + const tmp0 = try self.binOp(.div_trunc, lhs, rhs, lhs_ty, rhs_ty, null); + const tmp1 = try self.binOp(.mul, tmp0, rhs, lhs_ty, rhs_ty, null); + break :blk try self.binOp(.sub, lhs, tmp1, lhs_ty, rhs_ty, null); + }; + + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +} + fn airRet(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); @@ -2077,7 +2236,10 @@ fn binOp( ) InnerError!MCValue { const mod = self.bin_file.options.module.?; switch (tag) { - .add, .cmp_eq => { + .add, + .sub, + .cmp_eq, + => { switch (lhs_ty.zigTypeTag()) { .Float => return self.fail("TODO binary operations on floats", .{}), .Vector => return self.fail("TODO binary operations on vectors", .{}), @@ -2103,6 +2265,7 @@ fn binOp( const mir_tag: Mir.Inst.Tag = switch (tag) { .add => .add, + .sub => .sub, .cmp_eq => .cmp, else => unreachable, }; @@ -2124,6 +2287,39 @@ fn binOp( } }, + .div_trunc => { + switch (lhs_ty.zigTypeTag()) { + .Vector => return self.fail("TODO binary operations on vectors", .{}), + .Int => { + assert(lhs_ty.eql(rhs_ty, mod)); + const int_info = lhs_ty.intInfo(self.target.*); + if (int_info.bits <= 64) { + const rhs_immediate_ok = switch (tag) { + .div_trunc => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), + else => unreachable, + }; + + const mir_tag: Mir.Inst.Tag = switch (tag) { + .div_trunc => switch (int_info.signedness) { + .signed => Mir.Inst.Tag.sdivx, + .unsigned => Mir.Inst.Tag.udivx, + }, + else => unreachable, + }; + + if (rhs_immediate_ok) { + return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, true, metadata); + } else { + return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); + } + } else { + return self.fail("TODO binary operations on int with bits > 64", .{}); + } + }, + else => unreachable, + } + }, + .mul => { switch (lhs_ty.zigTypeTag()) { .Vector => return self.fail("TODO binary operations on vectors", .{}), @@ -2382,6 +2578,9 @@ fn binOpImmediate( .xor, .xnor, .mulx, + .sdivx, + .udivx, + .sub, .subcc, => .{ .arithmetic_3op = .{ @@ -2503,6 +2702,9 @@ fn binOpRegister( .xor, .xnor, .mulx, + .sdivx, + .udivx, + .sub, .subcc, => .{ .arithmetic_3op = .{ diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 44680c2e12..937431afae 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -105,6 +105,8 @@ pub fn emitMir( .movr => @panic("TODO implement sparc64 movr"), .mulx => try emit.mirArithmetic3Op(inst), + .sdivx => try emit.mirArithmetic3Op(inst), + .udivx => try emit.mirArithmetic3Op(inst), .nop => try emit.mirNop(), @@ -234,6 +236,8 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { .xor => try emit.writeInstruction(Instruction.xor(i13, rs1, imm, rd)), .xnor => try emit.writeInstruction(Instruction.xnor(i13, rs1, imm, rd)), .mulx => try emit.writeInstruction(Instruction.mulx(i13, rs1, imm, rd)), + .sdivx => try emit.writeInstruction(Instruction.sdivx(i13, rs1, imm, rd)), + .udivx => try emit.writeInstruction(Instruction.udivx(i13, rs1, imm, rd)), .save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)), .restore => try emit.writeInstruction(Instruction.restore(i13, rs1, imm, rd)), .stb => try emit.writeInstruction(Instruction.stb(i13, rs1, imm, rd)), @@ -259,6 +263,8 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { .xor => try emit.writeInstruction(Instruction.xor(Register, rs1, rs2, rd)), .xnor => try emit.writeInstruction(Instruction.xnor(Register, rs1, rs2, rd)), .mulx => try emit.writeInstruction(Instruction.mulx(Register, rs1, rs2, rd)), + .sdivx => try emit.writeInstruction(Instruction.sdivx(Register, rs1, rs2, rd)), + .udivx => try emit.writeInstruction(Instruction.udivx(Register, rs1, rs2, rd)), .save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)), .restore => try emit.writeInstruction(Instruction.restore(Register, rs1, rs2, rd)), .stb => try emit.writeInstruction(Instruction.stb(Register, rs1, rs2, rd)), diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index 3ae84d0961..52dba34e56 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -92,8 +92,9 @@ pub const Inst = struct { /// A.37 Multiply and Divide (64-bit) /// This uses the arithmetic_3op field. - // TODO add other operations. mulx, + sdivx, + udivx, /// A.40 No Operation /// This uses the nop field. diff --git a/src/arch/sparc64/bits.zig b/src/arch/sparc64/bits.zig index a722d06dee..4c1a641d45 100644 --- a/src/arch/sparc64/bits.zig +++ b/src/arch/sparc64/bits.zig @@ -1281,6 +1281,22 @@ pub const Instruction = union(enum) { }; } + pub fn sdivx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b10, 0b10_1101, rs1, rs2, rd), + i13 => format3b(0b10, 0b10_1101, rs1, rs2, rd), + else => unreachable, + }; + } + + pub fn udivx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3a(0b10, 0b00_1101, rs1, rs2, rd), + i13 => format3b(0b10, 0b00_1101, rs1, rs2, rd), + else => unreachable, + }; + } + pub fn nop() Instruction { return sethi(0, .g0); } From ad176b319d810da7755b9a1ec39e68210df8d807 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Fri, 24 Jun 2022 20:29:12 +0700 Subject: [PATCH 1947/2031] stage2: sparc64: Implement SPARCv9 movr --- src/arch/sparc64/Emit.zig | 22 +++++++++++++++++++++- src/arch/sparc64/bits.zig | 8 ++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 937431afae..ac7fd89d00 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -102,7 +102,7 @@ pub fn emitMir( .movcc => try emit.mirConditionalMove(inst), - .movr => @panic("TODO implement sparc64 movr"), + .movr => try emit.mirConditionalMove(inst), .mulx => try emit.mirArithmetic3Op(inst), .sdivx => try emit.mirArithmetic3Op(inst), @@ -346,6 +346,26 @@ fn mirConditionalMove(emit: *Emit, inst: Mir.Inst.Index) !void { )); } }, + .movr => { + const data = emit.mir.instructions.items(.data)[inst].conditional_move_reg; + if (data.is_imm) { + try emit.writeInstruction(Instruction.movr( + i10, + data.cond, + data.rs1, + data.rs2_or_imm.imm, + data.rd, + )); + } else { + try emit.writeInstruction(Instruction.movr( + Register, + data.cond, + data.rs1, + data.rs2_or_imm.rs2, + data.rd, + )); + } + }, else => unreachable, } } diff --git a/src/arch/sparc64/bits.zig b/src/arch/sparc64/bits.zig index 4c1a641d45..ebfc55635c 100644 --- a/src/arch/sparc64/bits.zig +++ b/src/arch/sparc64/bits.zig @@ -1273,6 +1273,14 @@ pub const Instruction = union(enum) { }; } + pub fn movr(comptime s2: type, cond: RCondition, rs1: Register, rs2: s2, rd: Register) Instruction { + return switch (s2) { + Register => format3e(0b10, 0b10_1111, cond, rs1, rs2, rd), + i10 => format3f(0b10, 0b10_1111, cond, rs1, rs2, rd), + else => unreachable, + }; + } + pub fn mulx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { return switch (s2) { Register => format3a(0b10, 0b00_1001, rs1, rs2, rd), From 672d6df42900b8521597c58af64a723985bbfd36 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 16 Jun 2022 01:31:49 +0700 Subject: [PATCH 1948/2031] stage2: sparc64: Skip Sema-failing tests for now --- test/behavior/array.zig | 1 + test/behavior/atomics.zig | 13 +++++++++++++ test/behavior/basic.zig | 5 +++++ test/behavior/bugs/9584.zig | 1 + test/behavior/byteswap.zig | 1 + 5 files changed, 21 insertions(+) diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 6482bfa1fe..59f2baf5b6 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -511,6 +511,7 @@ test "zero-sized array with recursive type definition" { test "type coercion of anon struct literal to array" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/atomics.zig b/test/behavior/atomics.zig index 62471c5ea0..1495ac75a4 100644 --- a/test/behavior/atomics.zig +++ b/test/behavior/atomics.zig @@ -8,6 +8,7 @@ test "cmpxchg" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO try testCmpxchg(); @@ -48,6 +49,7 @@ test "atomicrmw and atomicload" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO var data: u8 = 200; @@ -77,6 +79,7 @@ test "cmpxchg with ptr" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO var data1: i32 = 1234; @@ -103,6 +106,7 @@ test "cmpxchg with ignored result" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO var x: i32 = 1234; @@ -117,6 +121,7 @@ test "128-bit cmpxchg" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.cpu.arch != .x86_64) return error.SkipZigTest; @@ -150,6 +155,7 @@ test "cmpxchg on a global variable" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if ((builtin.zig_backend == .stage1 or builtin.zig_backend == .stage2_llvm) and @@ -168,6 +174,7 @@ test "atomic load and rmw with enum" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const Value = enum(u8) { a, b, c }; @@ -186,6 +193,7 @@ test "atomic store" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO var x: u32 = 0; @@ -200,6 +208,7 @@ test "atomic store comptime" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO comptime try testAtomicStore(); @@ -219,6 +228,7 @@ test "atomicrmw with floats" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if ((builtin.zig_backend == .stage1 or builtin.zig_backend == .stage2_llvm) and @@ -247,6 +257,7 @@ test "atomicrmw with ints" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO try testAtomicRmwInt(); @@ -281,6 +292,7 @@ test "atomics with different types" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO try testAtomicsWithType(bool, true, false); @@ -311,6 +323,7 @@ test "return @atomicStore, using it as a void value" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index ac9d90d5e6..ad403a60a8 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -24,6 +24,8 @@ fn testTruncate(x: u32) u8 { } test "truncate to non-power-of-two integers" { + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + try testTrunc(u32, u1, 0b10101, 0b1); try testTrunc(u32, u1, 0b10110, 0b0); try testTrunc(u32, u2, 0b10101, 0b01); @@ -401,6 +403,7 @@ fn testPointerToVoidReturnType2() *const void { test "array 2D const double ptr" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO @@ -414,6 +417,7 @@ test "array 2D const double ptr" { test "array 2D const double ptr with offset" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; @@ -427,6 +431,7 @@ test "array 2D const double ptr with offset" { test "array 3D const double ptr with offset" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/9584.zig b/test/behavior/bugs/9584.zig index 487f6ca41c..709d510072 100644 --- a/test/behavior/bugs/9584.zig +++ b/test/behavior/bugs/9584.zig @@ -46,6 +46,7 @@ test { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO diff --git a/test/behavior/byteswap.zig b/test/behavior/byteswap.zig index 13cc823e9d..f944c731b7 100644 --- a/test/behavior/byteswap.zig +++ b/test/behavior/byteswap.zig @@ -7,6 +7,7 @@ test "@byteSwap integers" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; const ByteSwapIntTest = struct { fn run() !void { From d589047e80133c5f673a7d40dd1cfa50258dcc4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Thu, 23 Jun 2022 09:19:53 +0300 Subject: [PATCH 1949/2031] zld: ignore -search_paths_first Ignore MachO-specific flag -search_paths_first, since it is the default in zld and ld64. Also see Jakub's comment[1]: Changing topic slightly, @motiejus dunno if you noticed, with this change building arm64 Zig binary fails on macos - it complains about unknown -search_paths_first flag. This one is an easy fix: we should ignore it since this is the default behaviour in both ld64 and zld. [1]: https://github.com/ziglang/zig/pull/11906#issuecomment-1163545849 --- src/main.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.zig b/src/main.zig index 6942d0ba49..b2b795d25f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1475,6 +1475,8 @@ fn buildOutputType( mem.eql(u8, linker_arg, "-static")) { force_static_libs = true; + } else if (mem.eql(u8, linker_arg, "-search_paths_first")) { + // ignore, since it's the default behavior in both ld64 and zld } else { try linker_args.append(linker_arg); } From 0df7ed79d304afc7d379482005e892979d4a5e4d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 24 Jun 2022 20:25:16 +0200 Subject: [PATCH 1950/2031] macho: implement -search_dylibs_first linker option --- lib/std/build.zig | 11 ++++ src/Compilation.zig | 3 + src/link.zig | 3 + src/link/MachO.zig | 141 +++++++++++++++++++++++++++----------------- src/main.zig | 12 +++- 5 files changed, 114 insertions(+), 56 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 57304242cd..0b49087747 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1586,6 +1586,13 @@ pub const LibExeObjStep = struct { /// (Darwin) Size of the pagezero segment. pagezero_size: ?u64 = null, + /// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`. + /// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first` + /// option. + /// By default, if no option is specified, the linker assumes `paths_first` as the default + /// search strategy. + search_strategy: ?enum { paths_first, dylibs_first } = null, + /// Position Independent Code force_pic: ?bool = null, @@ -2650,6 +2657,10 @@ pub const LibExeObjStep = struct { const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size}); try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size }); } + if (self.search_strategy) |strat| switch (strat) { + .paths_first => try zig_args.append("-search_paths_first"), + .dylibs_first => try zig_args.append("-search_dylibs_first"), + }; if (self.bundle_compiler_rt) |x| { if (x) { diff --git a/src/Compilation.zig b/src/Compilation.zig index 8cb93b5473..cbd36216ae 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -905,6 +905,8 @@ pub const InitOptions = struct { entitlements: ?[]const u8 = null, /// (Darwin) size of the __PAGEZERO segment pagezero_size: ?u64 = null, + /// (Darwin) search strategy for system libraries + search_strategy: ?link.File.MachO.SearchStrategy = null, }; fn addPackageTableToCacheHash( @@ -1745,6 +1747,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .install_name = options.install_name, .entitlements = options.entitlements, .pagezero_size = options.pagezero_size, + .search_strategy = options.search_strategy, }); errdefer bin_file.destroy(); comp.* = .{ diff --git a/src/link.zig b/src/link.zig index c78fb8c2c5..cfcb112e9b 100644 --- a/src/link.zig +++ b/src/link.zig @@ -190,6 +190,9 @@ pub const Options = struct { /// (Darwin) size of the __PAGEZERO segment pagezero_size: ?u64 = null, + /// (Darwin) search strategy for system libraries + search_strategy: ?File.MachO.SearchStrategy = null, + pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { return if (options.use_lld) .Obj else options.output_mode; } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 0c44901d29..bade8caa0c 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -47,6 +47,11 @@ pub const DebugSymbols = @import("MachO/DebugSymbols.zig"); pub const base_tag: File.Tag = File.Tag.macho; +pub const SearchStrategy = enum { + paths_first, + dylibs_first, +}; + base: File, /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. @@ -784,18 +789,43 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No } var libs = std.ArrayList([]const u8).init(arena); - for (search_lib_names.items) |lib_name| { - // Assume ld64 default: -search_paths_first - // Look in each directory for a dylib (stub first), and then for archive - // TODO implement alternative: -search_dylibs_first - for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| { - if (try resolveLib(arena, lib_dirs.items, lib_name, ext)) |full_path| { - try libs.append(full_path); - break; - } - } else { - log.warn("library not found for '-l{s}'", .{lib_name}); - lib_not_found = true; + + // Assume ld64 default -search_paths_first if no strategy specified. + const search_strategy = self.base.options.search_strategy orelse .paths_first; + outer: for (search_lib_names.items) |lib_name| { + switch (search_strategy) { + .paths_first => { + // Look in each directory for a dylib (stub first), and then for archive + for (lib_dirs.items) |dir| { + for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| { + if (try resolveLib(arena, dir, lib_name, ext)) |full_path| { + try libs.append(full_path); + continue :outer; + } + } + } else { + log.warn("library not found for '-l{s}'", .{lib_name}); + lib_not_found = true; + } + }, + .dylibs_first => { + // First, look for a dylib in each search dir + for (lib_dirs.items) |dir| { + for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| { + if (try resolveLib(arena, dir, lib_name, ext)) |full_path| { + try libs.append(full_path); + continue :outer; + } + } + } else for (lib_dirs.items) |dir| { + if (try resolveLib(arena, dir, lib_name, ".a")) |full_path| { + try libs.append(full_path); + } else { + log.warn("library not found for '-l{s}'", .{lib_name}); + lib_not_found = true; + } + } + }, } } @@ -811,19 +841,23 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No if (self.base.options.sysroot != null) blk: { // Try stub file first. If we hit it, then we're done as the stub file // re-exports every single symbol definition. - if (try resolveLib(arena, lib_dirs.items, "System", ".tbd")) |full_path| { - try libs.append(full_path); - libsystem_available = true; - break :blk; + for (lib_dirs.items) |dir| { + if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| { + try libs.append(full_path); + libsystem_available = true; + break :blk; + } } // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib // doesn't export libc.dylib which we'll need to resolve subsequently also. - if (try resolveLib(arena, lib_dirs.items, "System", ".dylib")) |libsystem_path| { - if (try resolveLib(arena, lib_dirs.items, "c", ".dylib")) |libc_path| { - try libs.append(libsystem_path); - try libs.append(libc_path); - libsystem_available = true; - break :blk; + for (lib_dirs.items) |dir| { + if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| { + if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| { + try libs.append(libsystem_path); + try libs.append(libc_path); + libsystem_available = true; + break :blk; + } } } } @@ -847,11 +881,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No } } - for (self.base.options.frameworks) |framework| { - for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { - if (try resolveFramework(arena, framework_dirs.items, framework, ext)) |full_path| { - try libs.append(full_path); - break; + outer: for (self.base.options.frameworks) |framework| { + for (framework_dirs.items) |dir| { + for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { + if (try resolveFramework(arena, dir, framework, ext)) |full_path| { + try libs.append(full_path); + continue :outer; + } } } else { log.warn("framework not found for '-framework {s}'", .{framework}); @@ -934,6 +970,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size})); } + if (self.base.options.search_strategy) |strat| switch (strat) { + .paths_first => try argv.append("-search_paths_first"), + .dylibs_first => try argv.append("-search_dylibs_first"), + }; + if (self.base.options.entry) |entry| { try argv.append("-e"); try argv.append(entry); @@ -1183,51 +1224,41 @@ fn resolveSearchDir( fn resolveLib( arena: Allocator, - search_dirs: []const []const u8, + search_dir: []const u8, name: []const u8, ext: []const u8, ) !?[]const u8 { const search_name = try std.fmt.allocPrint(arena, "lib{s}{s}", .{ name, ext }); + const full_path = try fs.path.join(arena, &[_][]const u8{ search_dir, search_name }); - for (search_dirs) |dir| { - const full_path = try fs.path.join(arena, &[_][]const u8{ dir, search_name }); + // Check if the file exists. + const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { + error.FileNotFound => return null, + else => |e| return e, + }; + defer tmp.close(); - // Check if the file exists. - const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { - error.FileNotFound => continue, - else => |e| return e, - }; - defer tmp.close(); - - return full_path; - } - - return null; + return full_path; } fn resolveFramework( arena: Allocator, - search_dirs: []const []const u8, + search_dir: []const u8, name: []const u8, ext: []const u8, ) !?[]const u8 { const search_name = try std.fmt.allocPrint(arena, "{s}{s}", .{ name, ext }); const prefix_path = try std.fmt.allocPrint(arena, "{s}.framework", .{name}); + const full_path = try fs.path.join(arena, &[_][]const u8{ search_dir, prefix_path, search_name }); - for (search_dirs) |dir| { - const full_path = try fs.path.join(arena, &[_][]const u8{ dir, prefix_path, search_name }); + // Check if the file exists. + const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { + error.FileNotFound => return null, + else => |e| return e, + }; + defer tmp.close(); - // Check if the file exists. - const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) { - error.FileNotFound => continue, - else => |e| return e, - }; - defer tmp.close(); - - return full_path; - } - - return null; + return full_path; } fn parseObject(self: *MachO, path: []const u8) !bool { diff --git a/src/main.zig b/src/main.zig index b2b795d25f..9cf50e1193 100644 --- a/src/main.zig +++ b/src/main.zig @@ -448,6 +448,8 @@ const usage_build_generic = \\ -install_name=[value] (Darwin) add dylib's install name \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature \\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment in hexadecimal notation + \\ -search_paths_first (Darwin) search each dir in library search paths for `libx.dylib` then `libx.a` + \\ -search_dylibs_first (Darwin) search `libx.dylib` in each dir in library search paths, then `libx.a` \\ --import-memory (WebAssembly) import memory from the environment \\ --import-table (WebAssembly) import function table from the host environment \\ --export-table (WebAssembly) export function table to the host environment @@ -696,6 +698,7 @@ fn buildOutputType( var hash_style: link.HashStyle = .both; var entitlements: ?[]const u8 = null; var pagezero_size: ?u64 = null; + var search_strategy: ?link.File.MachO.SearchStrategy = null; // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names. // This array is populated by zig cc frontend and then has to be converted to zig-style @@ -917,6 +920,10 @@ fn buildOutputType( pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| { fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; + } else if (mem.eql(u8, arg, "-search_paths_first")) { + search_strategy = .paths_first; + } else if (mem.eql(u8, arg, "-search_dylibs_first")) { + search_strategy = .dylibs_first; } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) { linker_script = args_iter.next() orelse { fatal("expected parameter after {s}", .{arg}); @@ -1476,7 +1483,9 @@ fn buildOutputType( { force_static_libs = true; } else if (mem.eql(u8, linker_arg, "-search_paths_first")) { - // ignore, since it's the default behavior in both ld64 and zld + search_strategy = .paths_first; + } else if (mem.eql(u8, linker_arg, "-search_dylibs_first")) { + search_strategy = .dylibs_first; } else { try linker_args.append(linker_arg); } @@ -2784,6 +2793,7 @@ fn buildOutputType( .install_name = install_name, .entitlements = entitlements, .pagezero_size = pagezero_size, + .search_strategy = search_strategy, }) catch |err| switch (err) { error.LibCUnavailable => { const target = target_info.target; From b60938eefd533ccf22675e41c22ffd57bc5d5be1 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 24 Jun 2022 22:01:29 +0200 Subject: [PATCH 1951/2031] macho: verbose print all input linker args verbatim --- src/link/MachO.zig | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index bade8caa0c..92d8134ec9 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -980,7 +980,26 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try argv.append(entry); } - try argv.appendSlice(positionals.items); + for (self.base.options.objects) |obj| { + try argv.append(obj.path); + } + + for (comp.c_object_table.keys()) |key| { + try argv.append(key.status.success.object_path); + } + + if (module_obj_path) |p| { + try argv.append(p); + } + + if (comp.compiler_rt_lib) |lib| { + try argv.append(lib.full_object_path); + } + + if (self.base.options.link_libcpp) { + try argv.append(comp.libcxxabi_static_lib.?.full_object_path); + try argv.append(comp.libcxx_static_lib.?.full_object_path); + } try argv.append("-o"); try argv.append(full_out_path); @@ -988,7 +1007,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try argv.append("-lSystem"); try argv.append("-lc"); - for (search_lib_names.items) |l_name| { + for (self.base.options.system_libs.keys()) |l_name| { try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name})); } From e32a5ba78b5e42788cba57720ce88cbfd30d434b Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Fri, 24 Jun 2022 22:01:41 +0200 Subject: [PATCH 1952/2031] link/wasm: Put decls into the correct segments Decls will now be put into their respective segment. e.g. a constant decl will be inserted into the "rodata" segment, whereas an uninitialized decl will be put in the "bss" segment instead. --- src/link/Wasm.zig | 43 +++++++++++++++++++++++++++++++++-------- src/link/Wasm/types.zig | 5 +++-- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 4357a588a1..834ac3e1e1 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -1143,9 +1143,29 @@ pub fn addOrUpdateImport( } else @panic("TODO: Implement undefined symbols for non-function declarations"); } +/// Kind represents the type of an Atom, which is only +/// used to parse a decl into an Atom to define in which section +/// or segment it should be placed. const Kind = union(enum) { - data: void, + /// Represents the segment the data symbol should + /// be inserted into. + /// TODO: Add TLS segments + data: enum { + read_only, + uninitialized, + initialized, + }, function: FnData, + + /// Returns the segment name the data kind represents. + /// Asserts `kind` has its active tag set to `data`. + fn segmentName(kind: Kind) []const u8 { + switch (kind.data) { + .read_only => return ".rodata.", + .uninitialized => return ".bss.", + .initialized => return ".data.", + } + } }; /// Parses an Atom and inserts its metadata into the corresponding sections. @@ -1174,9 +1194,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { break :result self.code_section_index.?; }, .data => result: { - // TODO: Add mutables global decls to .bss section instead const segment_name = try std.mem.concat(self.base.allocator, u8, &.{ - ".rodata.", + kind.segmentName(), self.string_table.get(symbol.name), }); errdefer self.base.allocator.free(segment_name); @@ -1722,8 +1741,8 @@ fn populateErrorNameTable(self: *Wasm) !void { // link the atoms with the rest of the binary so they can be allocated // and relocations will be performed. - try self.parseAtom(atom, .data); - try self.parseAtom(names_atom, .data); + try self.parseAtom(atom, .{ .data = .read_only }); + try self.parseAtom(names_atom, .{ .data = .read_only }); } pub fn getDebugInfoIndex(self: *Wasm) !u32 { @@ -1866,13 +1885,21 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod const atom = &decl.*.link.wasm; if (decl.ty.zigTypeTag() == .Fn) { try self.parseAtom(atom, .{ .function = decl.fn_link.wasm }); + } else if (decl.getVariable()) |variable| { + if (!variable.is_mutable) { + try self.parseAtom(atom, .{ .data = .read_only }); + } else if (variable.init.isUndefDeep()) { + try self.parseAtom(atom, .{ .data = .uninitialized }); + } else { + try self.parseAtom(atom, .{ .data = .initialized }); + } } else { - try self.parseAtom(atom, .data); + try self.parseAtom(atom, .{ .data = .read_only }); } // also parse atoms for a decl's locals for (atom.locals.items) |*local_atom| { - try self.parseAtom(local_atom, .data); + try self.parseAtom(local_atom, .{ .data = .read_only }); } } @@ -2167,7 +2194,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod while (it.next()) |entry| { // do not output 'bss' section unless we import memory and therefore // want to guarantee the data is zero initialized - if (std.mem.eql(u8, entry.key_ptr.*, ".bss") and !import_memory) continue; + if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue; segment_count += 1; const atom_index = entry.value_ptr.*; var atom: *Atom = self.atoms.getPtr(atom_index).?.*.getFirst(); diff --git a/src/link/Wasm/types.zig b/src/link/Wasm/types.zig index 835c02ea12..a606e3f7b6 100644 --- a/src/link/Wasm/types.zig +++ b/src/link/Wasm/types.zig @@ -125,14 +125,15 @@ pub const Segment = struct { /// Bitfield containing flags for a segment flags: u32, + /// Returns the name as how it will be output into the final object + /// file or binary. When `merge_segments` is true, this will return the + /// short name. i.e. ".rodata". When false, it returns the entire name instead. pub fn outputName(self: Segment, merge_segments: bool) []const u8 { if (!merge_segments) return self.name; if (std.mem.startsWith(u8, self.name, ".rodata.")) { return ".rodata"; } else if (std.mem.startsWith(u8, self.name, ".text.")) { return ".text"; - } else if (std.mem.startsWith(u8, self.name, ".rodata.")) { - return ".rodata"; } else if (std.mem.startsWith(u8, self.name, ".data.")) { return ".data"; } else if (std.mem.startsWith(u8, self.name, ".bss.")) { From eadac4763135717ace3a7809ec5d6413b1699fcd Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 24 Jun 2022 22:02:15 +0200 Subject: [PATCH 1953/2031] cli: let the linker resolve libs into dynamic and static Unlike targeting ELF-based OSes such as Linux, resolving system libs on Darwin should follow one of two strategies: `-search_paths_first` or `-search_dylibs_first` and hence we defer always forcing linking a static library to the linker. --- src/main.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.zig b/src/main.zig index 9cf50e1193..3d65875975 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2152,6 +2152,7 @@ fn buildOutputType( } for (lib_dirs.items) |lib_dir_path| { + if (cross_target.isDarwin()) break; // Targeting Darwin we let the linker resolve the libraries in the correct order test_path.clearRetainingCapacity(); try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{ lib_dir_path, From 24821dd17f3a23d9fb496ff8fa5dcbefb5b1d18d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 24 Jun 2022 22:03:56 +0200 Subject: [PATCH 1954/2031] link-tests: test -search_paths_first and -search_dylibs_first macho flags --- test/link.zig | 8 ++++ test/link/macho/search_dylibs_first/a.c | 7 +++ test/link/macho/search_dylibs_first/build.zig | 48 +++++++++++++++++++ test/link/macho/search_dylibs_first/main.c | 9 ++++ test/link/macho/search_paths_first/a.c | 7 +++ test/link/macho/search_paths_first/build.zig | 41 ++++++++++++++++ test/link/macho/search_paths_first/main.c | 9 ++++ 7 files changed, 129 insertions(+) create mode 100644 test/link/macho/search_dylibs_first/a.c create mode 100644 test/link/macho/search_dylibs_first/build.zig create mode 100644 test/link/macho/search_dylibs_first/main.c create mode 100644 test/link/macho/search_paths_first/a.c create mode 100644 test/link/macho/search_paths_first/build.zig create mode 100644 test/link/macho/search_paths_first/main.c diff --git a/test/link.zig b/test/link.zig index 42ce12f73c..c00c960626 100644 --- a/test/link.zig +++ b/test/link.zig @@ -60,5 +60,13 @@ pub fn addCases(cases: *tests.StandaloneContext) void { cases.addBuildFile("test/link/macho/stack_size/build.zig", .{ .build_modes = true, }); + + cases.addBuildFile("test/link/macho/search_paths_first/build.zig", .{ + .build_modes = true, + }); + + cases.addBuildFile("test/link/macho/search_dylibs_first/build.zig", .{ + .build_modes = true, + }); } } diff --git a/test/link/macho/search_dylibs_first/a.c b/test/link/macho/search_dylibs_first/a.c new file mode 100644 index 0000000000..199b31e1a0 --- /dev/null +++ b/test/link/macho/search_dylibs_first/a.c @@ -0,0 +1,7 @@ +#include + +char world[] = "world"; + +char* hello() { + return "Hello"; +} diff --git a/test/link/macho/search_dylibs_first/build.zig b/test/link/macho/search_dylibs_first/build.zig new file mode 100644 index 0000000000..90c6a4a849 --- /dev/null +++ b/test/link/macho/search_dylibs_first/build.zig @@ -0,0 +1,48 @@ +const std = @import("std"); +const Builder = std.build.Builder; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + + const test_step = b.step("test", "Test"); + test_step.dependOn(b.getInstallStep()); + + const static = b.addStaticLibrary("a", null); + static.setBuildMode(mode); + static.addCSourceFile("a.c", &.{}); + static.linkLibC(); + static.override_dest_dir = std.build.InstallDir{ + .custom = "static", + }; + static.install(); + + const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); + dylib.setBuildMode(mode); + dylib.addCSourceFile("a.c", &.{}); + dylib.linkLibC(); + dylib.override_dest_dir = std.build.InstallDir{ + .custom = "dynamic", + }; + dylib.install(); + + const exe = b.addExecutable("main", null); + exe.setBuildMode(mode); + exe.addCSourceFile("main.c", &.{}); + exe.linkSystemLibraryName("a"); + exe.linkLibC(); + exe.addLibraryPath(b.pathFromRoot("zig-out/static")); + exe.addLibraryPath(b.pathFromRoot("zig-out/dynamic")); + exe.addRPath(b.pathFromRoot("zig-out/dynamic")); + exe.search_strategy = .dylibs_first; + + const check = exe.checkObject(.macho); + check.checkStart("cmd LOAD_DYLIB"); + check.checkNext("name @rpath/liba.dylib"); + + test_step.dependOn(&check.step); + + const run = exe.run(); + run.cwd = b.pathFromRoot("."); + run.expectStdOutEqual("Hello world"); + test_step.dependOn(&run.step); +} diff --git a/test/link/macho/search_dylibs_first/main.c b/test/link/macho/search_dylibs_first/main.c new file mode 100644 index 0000000000..be1647ddad --- /dev/null +++ b/test/link/macho/search_dylibs_first/main.c @@ -0,0 +1,9 @@ +#include + +char* hello(); +extern char world[]; + +int main() { + printf("%s %s", hello(), world); + return 0; +} diff --git a/test/link/macho/search_paths_first/a.c b/test/link/macho/search_paths_first/a.c new file mode 100644 index 0000000000..199b31e1a0 --- /dev/null +++ b/test/link/macho/search_paths_first/a.c @@ -0,0 +1,7 @@ +#include + +char world[] = "world"; + +char* hello() { + return "Hello"; +} diff --git a/test/link/macho/search_paths_first/build.zig b/test/link/macho/search_paths_first/build.zig new file mode 100644 index 0000000000..63c807bd4c --- /dev/null +++ b/test/link/macho/search_paths_first/build.zig @@ -0,0 +1,41 @@ +const std = @import("std"); +const Builder = std.build.Builder; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + + const test_step = b.step("test", "Test"); + test_step.dependOn(b.getInstallStep()); + + const static = b.addStaticLibrary("a", null); + static.setBuildMode(mode); + static.addCSourceFile("a.c", &.{}); + static.linkLibC(); + static.override_dest_dir = std.build.InstallDir{ + .custom = "static", + }; + static.install(); + + const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); + dylib.setBuildMode(mode); + dylib.addCSourceFile("a.c", &.{}); + dylib.linkLibC(); + dylib.override_dest_dir = std.build.InstallDir{ + .custom = "dynamic", + }; + dylib.install(); + + const exe = b.addExecutable("main", null); + exe.setBuildMode(mode); + exe.addCSourceFile("main.c", &.{}); + exe.linkSystemLibraryName("a"); + exe.linkLibC(); + exe.addLibraryPath(b.pathFromRoot("zig-out/static")); + exe.addLibraryPath(b.pathFromRoot("zig-out/dynamic")); + exe.search_strategy = .paths_first; + + const run = exe.run(); + run.cwd = b.pathFromRoot("."); + run.expectStdOutEqual("Hello world"); + test_step.dependOn(&run.step); +} diff --git a/test/link/macho/search_paths_first/main.c b/test/link/macho/search_paths_first/main.c new file mode 100644 index 0000000000..be1647ddad --- /dev/null +++ b/test/link/macho/search_paths_first/main.c @@ -0,0 +1,9 @@ +#include + +char* hello(); +extern char world[]; + +int main() { + printf("%s %s", hello(), world); + return 0; +} From dfdb807543a03eec27341deec6a5f3b88f87fac3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 25 Jun 2022 10:50:00 +0200 Subject: [PATCH 1955/2031] cache setting macho search strategy flags --- src/Compilation.zig | 5 +++-- src/link/Coff.zig | 2 +- src/link/Elf.zig | 2 +- src/link/MachO.zig | 3 ++- src/link/Wasm.zig | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index cbd36216ae..ccaad7c8c4 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2363,7 +2363,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo /// to remind the programmer to update multiple related pieces of code that /// are in different locations. Bump this number when adding or deleting /// anything from the link cache manifest. -pub const link_hash_implementation_version = 4; +pub const link_hash_implementation_version = 5; fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void { const gpa = comp.gpa; @@ -2373,7 +2373,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - comptime assert(link_hash_implementation_version == 4); + comptime assert(link_hash_implementation_version == 5); if (comp.bin_file.options.module) |mod| { const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{ @@ -2479,6 +2479,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes man.hash.addListOfBytes(comp.bin_file.options.frameworks); try man.addOptionalFile(comp.bin_file.options.entitlements); man.hash.addOptional(comp.bin_file.options.pagezero_size); + man.hash.addOptional(comp.bin_file.options.search_strategy); // COFF specific stuff man.hash.addOptional(comp.bin_file.options.subsystem); diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 6b4ff26bce..3418a206df 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -969,7 +969,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) ! man = comp.cache_parent.obtain(); self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 4); + comptime assert(Compilation.link_hash_implementation_version == 5); for (self.base.options.objects) |obj| { _ = try man.addFile(obj.path, null); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 0bcde06f33..1051c0eb78 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1298,7 +1298,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // We are about to obtain this lock, so here we give other processes a chance first. self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 4); + comptime assert(Compilation.link_hash_implementation_version == 5); try man.addOptionalFile(self.base.options.linker_script); try man.addOptionalFile(self.base.options.version_script); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 92d8134ec9..2bbd2e7a72 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -541,7 +541,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No // We are about to obtain this lock, so here we give other processes a chance first. self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 4); + comptime assert(Compilation.link_hash_implementation_version == 5); for (self.base.options.objects) |obj| { _ = try man.addFile(obj.path, null); @@ -555,6 +555,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No // installation sources because they are always a product of the compiler version + target information. man.hash.add(stack_size); man.hash.addOptional(self.base.options.pagezero_size); + man.hash.addOptional(self.base.options.search_strategy); man.hash.addListOfBytes(self.base.options.lib_dirs); man.hash.addListOfBytes(self.base.options.framework_dirs); man.hash.addListOfBytes(self.base.options.frameworks); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 4357a588a1..c654fbe719 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2481,7 +2481,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! // We are about to obtain this lock, so here we give other processes a chance first. self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 4); + comptime assert(Compilation.link_hash_implementation_version == 5); for (self.base.options.objects) |obj| { _ = try man.addFile(obj.path, null); From 8f00bc9d231c0c686b2ffa3bd0b14181afa7a171 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 25 Jun 2022 10:57:56 +0200 Subject: [PATCH 1956/2031] link-tests: put macho search strategy tests into one test case --- test/link.zig | 6 +-- test/link/macho/search_paths_first/a.c | 7 --- test/link/macho/search_paths_first/build.zig | 41 ----------------- test/link/macho/search_paths_first/main.c | 9 ---- .../a.c | 0 .../build.zig | 44 ++++++++++++++----- .../main.c | 0 7 files changed, 33 insertions(+), 74 deletions(-) delete mode 100644 test/link/macho/search_paths_first/a.c delete mode 100644 test/link/macho/search_paths_first/build.zig delete mode 100644 test/link/macho/search_paths_first/main.c rename test/link/macho/{search_dylibs_first => search_strategy}/a.c (100%) rename test/link/macho/{search_dylibs_first => search_strategy}/build.zig (54%) rename test/link/macho/{search_dylibs_first => search_strategy}/main.c (100%) diff --git a/test/link.zig b/test/link.zig index c00c960626..62bdcff4b0 100644 --- a/test/link.zig +++ b/test/link.zig @@ -61,11 +61,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { .build_modes = true, }); - cases.addBuildFile("test/link/macho/search_paths_first/build.zig", .{ - .build_modes = true, - }); - - cases.addBuildFile("test/link/macho/search_dylibs_first/build.zig", .{ + cases.addBuildFile("test/link/macho/search_strategy/build.zig", .{ .build_modes = true, }); } diff --git a/test/link/macho/search_paths_first/a.c b/test/link/macho/search_paths_first/a.c deleted file mode 100644 index 199b31e1a0..0000000000 --- a/test/link/macho/search_paths_first/a.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - -char world[] = "world"; - -char* hello() { - return "Hello"; -} diff --git a/test/link/macho/search_paths_first/build.zig b/test/link/macho/search_paths_first/build.zig deleted file mode 100644 index 63c807bd4c..0000000000 --- a/test/link/macho/search_paths_first/build.zig +++ /dev/null @@ -1,41 +0,0 @@ -const std = @import("std"); -const Builder = std.build.Builder; - -pub fn build(b: *Builder) void { - const mode = b.standardReleaseOptions(); - - const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); - - const static = b.addStaticLibrary("a", null); - static.setBuildMode(mode); - static.addCSourceFile("a.c", &.{}); - static.linkLibC(); - static.override_dest_dir = std.build.InstallDir{ - .custom = "static", - }; - static.install(); - - const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); - dylib.setBuildMode(mode); - dylib.addCSourceFile("a.c", &.{}); - dylib.linkLibC(); - dylib.override_dest_dir = std.build.InstallDir{ - .custom = "dynamic", - }; - dylib.install(); - - const exe = b.addExecutable("main", null); - exe.setBuildMode(mode); - exe.addCSourceFile("main.c", &.{}); - exe.linkSystemLibraryName("a"); - exe.linkLibC(); - exe.addLibraryPath(b.pathFromRoot("zig-out/static")); - exe.addLibraryPath(b.pathFromRoot("zig-out/dynamic")); - exe.search_strategy = .paths_first; - - const run = exe.run(); - run.cwd = b.pathFromRoot("."); - run.expectStdOutEqual("Hello world"); - test_step.dependOn(&run.step); -} diff --git a/test/link/macho/search_paths_first/main.c b/test/link/macho/search_paths_first/main.c deleted file mode 100644 index be1647ddad..0000000000 --- a/test/link/macho/search_paths_first/main.c +++ /dev/null @@ -1,9 +0,0 @@ -#include - -char* hello(); -extern char world[]; - -int main() { - printf("%s %s", hello(), world); - return 0; -} diff --git a/test/link/macho/search_dylibs_first/a.c b/test/link/macho/search_strategy/a.c similarity index 100% rename from test/link/macho/search_dylibs_first/a.c rename to test/link/macho/search_strategy/a.c diff --git a/test/link/macho/search_dylibs_first/build.zig b/test/link/macho/search_strategy/build.zig similarity index 54% rename from test/link/macho/search_dylibs_first/build.zig rename to test/link/macho/search_strategy/build.zig index 90c6a4a849..c5e867c8d0 100644 --- a/test/link/macho/search_dylibs_first/build.zig +++ b/test/link/macho/search_strategy/build.zig @@ -1,5 +1,6 @@ const std = @import("std"); const Builder = std.build.Builder; +const LibExeObjectStep = std.build.LibExeObjStep; pub fn build(b: *Builder) void { const mode = b.standardReleaseOptions(); @@ -7,6 +8,36 @@ pub fn build(b: *Builder) void { const test_step = b.step("test", "Test"); test_step.dependOn(b.getInstallStep()); + { + // -search_dylibs_first + const exe = createScenario(b, mode); + exe.search_strategy = .dylibs_first; + + const check = exe.checkObject(.macho); + check.checkStart("cmd LOAD_DYLIB"); + check.checkNext("name @rpath/liba.dylib"); + + test_step.dependOn(&check.step); + + const run = exe.run(); + run.cwd = b.pathFromRoot("."); + run.expectStdOutEqual("Hello world"); + test_step.dependOn(&run.step); + } + + { + // -search_paths_first + const exe = createScenario(b, mode); + exe.search_strategy = .paths_first; + + const run = exe.run(); + run.cwd = b.pathFromRoot("."); + run.expectStdOutEqual("Hello world"); + test_step.dependOn(&run.step); + } +} + +fn createScenario(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep { const static = b.addStaticLibrary("a", null); static.setBuildMode(mode); static.addCSourceFile("a.c", &.{}); @@ -33,16 +64,5 @@ pub fn build(b: *Builder) void { exe.addLibraryPath(b.pathFromRoot("zig-out/static")); exe.addLibraryPath(b.pathFromRoot("zig-out/dynamic")); exe.addRPath(b.pathFromRoot("zig-out/dynamic")); - exe.search_strategy = .dylibs_first; - - const check = exe.checkObject(.macho); - check.checkStart("cmd LOAD_DYLIB"); - check.checkNext("name @rpath/liba.dylib"); - - test_step.dependOn(&check.step); - - const run = exe.run(); - run.cwd = b.pathFromRoot("."); - run.expectStdOutEqual("Hello world"); - test_step.dependOn(&run.step); + return exe; } diff --git a/test/link/macho/search_dylibs_first/main.c b/test/link/macho/search_strategy/main.c similarity index 100% rename from test/link/macho/search_dylibs_first/main.c rename to test/link/macho/search_strategy/main.c From f91503e5773ddf4ce3989533ed586ace81e41e90 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 25 Jun 2022 00:33:29 +0200 Subject: [PATCH 1957/2031] link-tests: defer parsing of the RPN program until running the action --- lib/std/build/CheckObjectStep.zig | 217 +++++++++++++++++++----------- test/link/macho/entry/build.zig | 2 +- 2 files changed, 137 insertions(+), 82 deletions(-) diff --git a/lib/std/build/CheckObjectStep.zig b/lib/std/build/CheckObjectStep.zig index 65a57f8832..375e183231 100644 --- a/lib/std/build/CheckObjectStep.zig +++ b/lib/std/build/CheckObjectStep.zig @@ -3,6 +3,7 @@ const assert = std.debug.assert; const build = std.build; const fs = std.fs; const macho = std.macho; +const math = std.math; const mem = std.mem; const testing = std.testing; @@ -36,20 +37,24 @@ pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Targe return self; } -const Action = union(enum) { - match: MatchAction, - compute_eq: ComputeEqAction, -}; - -/// MatchAction is the main building block of standard matchers with optional eat-all token `{*}` +/// There two types of actions currently suported: +/// * `.match` - is the main building block of standard matchers with optional eat-all token `{*}` /// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature /// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use /// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc. /// it should be plenty useful in its current form. -const MatchAction = struct { - needle: []const u8, +/// * `.compute_cmp` - can be used to perform an operation on the extracted global variables +/// using the MatchAction. It currently only supports an addition. The operation is required +/// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well, +/// to avoid any parsing really). +/// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively +/// they could then be added with this simple program `vmaddr entryoff +`. +const Action = struct { + tag: enum { match, compute_cmp }, + phrase: []const u8, + expected: ?ComputeCompareExpected = null, - /// Will return true if the `needle` was found in the `haystack`. + /// Will return true if the `phrase` was found in the `haystack`. /// Some examples include: /// /// LC 0 => will match in its entirety @@ -57,9 +62,11 @@ const MatchAction = struct { /// and save under `vmaddr` global name (see `global_vars` param) /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib` /// in that order with other letters in between - fn match(act: MatchAction, haystack: []const u8, global_vars: anytype) !bool { + fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool { + assert(act.tag == .match); + var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " "); - var needle_it = mem.tokenize(u8, mem.trim(u8, act.needle, " "), " "); + var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " "); while (needle_it.next()) |needle_tok| { const hay_tok = hay_it.next() orelse return false; @@ -93,22 +100,80 @@ const MatchAction = struct { return true; } + + /// Will return true if the `phrase` is correctly parsed into an RPN program and + /// its reduced, computed value compares using `op` with the expected value, either + /// a literal or another extracted variable. + fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool { + var op_stack = std.ArrayList(enum { add }).init(gpa); + var values = std.ArrayList(u64).init(gpa); + + var it = mem.tokenize(u8, act.phrase, " "); + while (it.next()) |next| { + if (mem.eql(u8, next, "+")) { + try op_stack.append(.add); + } else { + const val = global_vars.get(next) orelse { + std.debug.print( + \\ + \\========= Variable was not extracted: =========== + \\{s} + \\ + , .{next}); + return error.UnknownVariable; + }; + try values.append(val); + } + } + + var op_i: usize = 1; + var reduced: u64 = values.items[0]; + for (op_stack.items) |op| { + const other = values.items[op_i]; + switch (op) { + .add => { + reduced += other; + }, + } + } + + const exp_value = switch (act.expected.?.value) { + .variable => |name| global_vars.get(name) orelse { + std.debug.print( + \\ + \\========= Variable was not extracted: =========== + \\{s} + \\ + , .{name}); + return error.UnknownVariable; + }, + .literal => |x| x, + }; + return math.compare(reduced, act.expected.?.op, exp_value); + } }; -/// ComputeEqAction can be used to perform an operation on the extracted global variables -/// using the MatchAction. It currently only supports an addition. The operation is required -/// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well, -/// to avoid any parsing really). -/// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively -/// they could then be added with this simple program `vmaddr entryoff +`. -const ComputeEqAction = struct { - expected: []const u8, - var_stack: std.ArrayList([]const u8), - op_stack: std.ArrayList(Op), +const ComputeCompareExpected = struct { + op: math.CompareOperator, + value: union(enum) { + variable: []const u8, + literal: u64, + }, - const Op = enum { - add, - }; + pub fn format( + value: @This(), + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = fmt; + _ = options; + try writer.print("{s} ", .{@tagName(value.op)}); + switch (value.value) { + .variable => |name| try writer.writeAll(name), + .literal => |x| try writer.print("{x}", .{x}), + } + } }; const Check = struct { @@ -122,15 +187,18 @@ const Check = struct { }; } - fn match(self: *Check, needle: []const u8) void { + fn match(self: *Check, phrase: []const u8) void { self.actions.append(.{ - .match = .{ .needle = self.builder.dupe(needle) }, + .tag = .match, + .phrase = self.builder.dupe(phrase), }) catch unreachable; } - fn computeEq(self: *Check, act: ComputeEqAction) void { + fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void { self.actions.append(.{ - .compute_eq = act, + .tag = .compute_cmp, + .phrase = self.builder.dupe(phrase), + .expected = expected, }) catch unreachable; } }; @@ -165,25 +233,13 @@ pub fn checkInSymtab(self: *CheckObjectStep) void { /// Creates a new standalone, singular check which allows running simple binary operations /// on the extracted variables. It will then compare the reduced program with the value of /// the expected variable. -pub fn checkComputeEq(self: *CheckObjectStep, program: []const u8, expected: []const u8) void { - const gpa = self.builder.allocator; - var ca = ComputeEqAction{ - .expected = expected, - .var_stack = std.ArrayList([]const u8).init(gpa), - .op_stack = std.ArrayList(ComputeEqAction.Op).init(gpa), - }; - - var it = mem.tokenize(u8, program, " "); - while (it.next()) |next| { - if (mem.eql(u8, next, "+")) { - ca.op_stack.append(.add) catch unreachable; - } else { - ca.var_stack.append(self.builder.dupe(next)) catch unreachable; - } - } - +pub fn checkComputeCompare( + self: *CheckObjectStep, + program: []const u8, + expected: ComputeCompareExpected, +) void { var new_check = Check.create(self.builder); - new_check.computeEq(ca); + new_check.computeCmp(program, expected); self.checks.append(new_check) catch unreachable; } @@ -210,10 +266,10 @@ fn make(step: *Step) !void { for (self.checks.items) |chk| { var it = mem.tokenize(u8, output, "\r\n"); for (chk.actions.items) |act| { - switch (act) { - .match => |match_act| { + switch (act.tag) { + .match => { while (it.next()) |line| { - if (try match_act.match(line, &vars)) break; + if (try act.match(line, &vars)) break; } else { std.debug.print( \\ @@ -222,51 +278,33 @@ fn make(step: *Step) !void { \\========= But parsed file does not contain it: ======= \\{s} \\ - , .{ match_act.needle, output }); + , .{ act.phrase, output }); return error.TestFailed; } }, - .compute_eq => |c_eq| { - var values = std.ArrayList(u64).init(gpa); - try values.ensureTotalCapacity(c_eq.var_stack.items.len); - for (c_eq.var_stack.items) |vv| { - const val = vars.get(vv) orelse { + .compute_cmp => { + const res = act.computeCmp(gpa, vars) catch |err| switch (err) { + error.UnknownVariable => { std.debug.print( - \\ - \\========= Variable was not extracted: =========== - \\{s} \\========= From parsed file: ===================== \\{s} \\ - , .{ vv, output }); + , .{output}); return error.TestFailed; - }; - values.appendAssumeCapacity(val); - } - - var op_i: usize = 1; - var reduced: u64 = values.items[0]; - for (c_eq.op_stack.items) |op| { - const other = values.items[op_i]; - switch (op) { - .add => { - reduced += other; - }, - } - } - - const expected = vars.get(c_eq.expected) orelse { + }, + else => |e| return e, + }; + if (!res) { std.debug.print( \\ - \\========= Variable was not extracted: =========== - \\{s} - \\========= From parsed file: ===================== + \\========= Comparison failed for action: =========== + \\{s} {s} + \\========= From parsed file: ======================= \\{s} \\ - , .{ c_eq.expected, output }); + , .{ act.phrase, act.expected.?, output }); return error.TestFailed; - }; - try testing.expectEqual(reduced, expected); + } }, } } @@ -349,6 +387,23 @@ const MachODumper = struct { seg.fileoff, seg.filesize, }); + + for (lc.segment.sections.items) |sect| { + try writer.writeByte('\n'); + try writer.print( + \\sectname {s} + \\addr {x} + \\size {x} + \\offset {x} + \\align {x} + , .{ + sect.sectName(), + sect.addr, + sect.size, + sect.offset, + sect.@"align", + }); + } }, .ID_DYLIB, diff --git a/test/link/macho/entry/build.zig b/test/link/macho/entry/build.zig index f0ac084e6a..1cd9099985 100644 --- a/test/link/macho/entry/build.zig +++ b/test/link/macho/entry/build.zig @@ -24,7 +24,7 @@ pub fn build(b: *Builder) void { check_exe.checkInSymtab(); check_exe.checkNext("_non_main {n_value}"); - check_exe.checkComputeEq("vmaddr entryoff +", "n_value"); + check_exe.checkComputeCompare("vmaddr entryoff +", .{ .op = .eq, .value = .{ .variable = "n_value" } }); test_step.dependOn(&check_exe.step); From 8c1feef4cd3af223ed8278bd55e8191936b526f0 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 25 Jun 2022 00:34:16 +0200 Subject: [PATCH 1958/2031] macho: implement -headerpad_size option Includes both traditiona and incremental codepaths with one caveat that in incremental case, the requested size cannot be smaller than the default padding size due to prealloc required due to incremental nature of linking. Also parse `-headerpad_max_install_names`, however, not actionable just yet - missing implementation. --- lib/std/build.zig | 15 +++++++++++++++ src/Compilation.zig | 6 ++++++ src/link.zig | 6 ++++++ src/link/MachO.zig | 35 ++++++++++++++++++++++++++++------- src/main.zig | 26 ++++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 7 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 0b49087747..20f59040c9 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1593,6 +1593,14 @@ pub const LibExeObjStep = struct { /// search strategy. search_strategy: ?enum { paths_first, dylibs_first } = null, + /// (Darwin) Set size of the padding between the end of load commands + /// and start of `__TEXT,__text` section. + headerpad_size: ?u64 = null, + + /// (Darwin) Automatically Set size of the padding between the end of load commands + /// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN. + headerpad_max_install_names: bool = false, + /// Position Independent Code force_pic: ?bool = null, @@ -2661,6 +2669,13 @@ pub const LibExeObjStep = struct { .paths_first => try zig_args.append("-search_paths_first"), .dylibs_first => try zig_args.append("-search_dylibs_first"), }; + if (self.headerpad_size) |headerpad_size| { + const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size}); + try zig_args.appendSlice(&[_][]const u8{ "-headerpad_size", size }); + } + if (self.headerpad_max_install_names) { + try zig_args.append("-headerpad_max_install_names"); + } if (self.bundle_compiler_rt) |x| { if (x) { diff --git a/src/Compilation.zig b/src/Compilation.zig index ccaad7c8c4..30603b91c5 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -907,6 +907,10 @@ pub const InitOptions = struct { pagezero_size: ?u64 = null, /// (Darwin) search strategy for system libraries search_strategy: ?link.File.MachO.SearchStrategy = null, + /// (Darwin) set minimum space for future expansion of the load commands + headerpad_size: ?u64 = null, + /// (Darwin) set enough space as if all paths were MATPATHLEN + headerpad_max_install_names: bool = false, }; fn addPackageTableToCacheHash( @@ -1748,6 +1752,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .entitlements = options.entitlements, .pagezero_size = options.pagezero_size, .search_strategy = options.search_strategy, + .headerpad_size = options.headerpad_size, + .headerpad_max_install_names = options.headerpad_max_install_names, }); errdefer bin_file.destroy(); comp.* = .{ diff --git a/src/link.zig b/src/link.zig index cfcb112e9b..96ec3ef8cd 100644 --- a/src/link.zig +++ b/src/link.zig @@ -193,6 +193,12 @@ pub const Options = struct { /// (Darwin) search strategy for system libraries search_strategy: ?File.MachO.SearchStrategy = null, + /// (Darwin) set minimum space for future expansion of the load commands + headerpad_size: ?u64 = null, + + /// (Darwin) set enough space as if all paths were MATPATHLEN + headerpad_max_install_names: bool = false, + pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { return if (options.use_lld) .Obj else options.output_mode; } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 2bbd2e7a72..dc6fe6e194 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -69,10 +69,8 @@ page_size: u16, /// and potentially stage2 release builds in the future. needs_prealloc: bool = true, -/// We commit 0x1000 = 4096 bytes of space to the header and -/// the table of load commands. This should be plenty for any -/// potential future extensions. -header_pad: u16 = 0x1000, +/// Size of the padding between the end of load commands and start of the '__TEXT,__text' section. +headerpad_size: u64, /// The absolute address of the entry point. entry_addr: ?u64 = null, @@ -295,6 +293,11 @@ pub const min_text_capacity = padToIdeal(minimum_text_block_size); /// start of __TEXT segment. const default_pagezero_vmsize: u64 = 0x100000000; +/// We commit 0x1000 = 4096 bytes of space to the header and +/// the table of load commands. This should be plenty for any +/// potential future extensions. +const default_headerpad_size: u64 = 0x1000; + pub const Export = struct { sym_index: ?u32 = null, }; @@ -400,6 +403,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO { const use_llvm = build_options.have_llvm and options.use_llvm; const use_stage1 = build_options.is_stage1 and options.use_stage1; const needs_prealloc = !(use_stage1 or use_llvm or options.cache_mode == .whole); + // TODO handle `headerpad_max_install_names` in incremental context + const explicit_headerpad_size = options.headerpad_size orelse 0; + const headerpad_size = if (needs_prealloc) + @maximum(explicit_headerpad_size, default_headerpad_size) + else + explicit_headerpad_size; const self = try gpa.create(MachO); errdefer gpa.destroy(self); @@ -412,6 +421,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO { .file = null, }, .page_size = page_size, + .headerpad_size = headerpad_size, .code_signature = if (requires_adhoc_codesig) CodeSignature.init(page_size) else null, .needs_prealloc = needs_prealloc, }; @@ -976,6 +986,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No .dylibs_first => try argv.append("-search_dylibs_first"), }; + if (self.base.options.headerpad_size) |headerpad_size| { + try argv.append("-headerpad_size"); + try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size})); + } + + if (self.base.options.headerpad_max_install_names) { + try argv.append("-headerpad_max_install_names"); + } + if (self.base.options.entry) |entry| { try argv.append("-e"); try argv.append(entry); @@ -4453,7 +4472,7 @@ fn populateMissingMetadata(self: *MachO) !void { const needed_size = if (self.needs_prealloc) blk: { const program_code_size_hint = self.base.options.program_code_size_hint; const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint; - const ideal_size = self.header_pad + program_code_size_hint + got_size_hint; + const ideal_size = self.headerpad_size + program_code_size_hint + got_size_hint; const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size); log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size }); break :blk needed_size; @@ -4961,7 +4980,9 @@ fn allocateTextSegment(self: *MachO) !void { sizeofcmds += lc.cmdsize(); } - try self.allocateSegment(self.text_segment_cmd_index.?, @sizeOf(macho.mach_header_64) + sizeofcmds); + // TODO verify if `headerpad_max_install_names` leads to larger padding size + const offset = @sizeOf(macho.mach_header_64) + sizeofcmds + self.headerpad_size; + try self.allocateSegment(self.text_segment_cmd_index.?, offset); // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments. var min_alignment: u32 = 0; @@ -5088,7 +5109,7 @@ fn initSection( if (self.needs_prealloc) { const alignment_pow_2 = try math.powi(u32, 2, alignment); - const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?) self.header_pad else null; + const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?) self.headerpad_size else null; const off = self.findFreeSpace(segment_id, alignment_pow_2, padding); log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{ sect.segName(), diff --git a/src/main.zig b/src/main.zig index 3d65875975..87b0e6fc8a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -450,6 +450,8 @@ const usage_build_generic = \\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment in hexadecimal notation \\ -search_paths_first (Darwin) search each dir in library search paths for `libx.dylib` then `libx.a` \\ -search_dylibs_first (Darwin) search `libx.dylib` in each dir in library search paths, then `libx.a` + \\ -headerpad_size [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation + \\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN \\ --import-memory (WebAssembly) import memory from the environment \\ --import-table (WebAssembly) import function table from the host environment \\ --export-table (WebAssembly) export function table to the host environment @@ -699,6 +701,8 @@ fn buildOutputType( var entitlements: ?[]const u8 = null; var pagezero_size: ?u64 = null; var search_strategy: ?link.File.MachO.SearchStrategy = null; + var headerpad_size: ?u64 = null; + var headerpad_max_install_names: bool = false; // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names. // This array is populated by zig cc frontend and then has to be converted to zig-style @@ -924,6 +928,15 @@ fn buildOutputType( search_strategy = .paths_first; } else if (mem.eql(u8, arg, "-search_dylibs_first")) { search_strategy = .dylibs_first; + } else if (mem.eql(u8, arg, "-headerpad_size")) { + const next_arg = args_iter.next() orelse { + fatal("expected parameter after {s}", .{arg}); + }; + headerpad_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| { + fatal("unable to parser '{s}': {s}", .{ arg, @errorName(err) }); + }; + } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) { + headerpad_max_install_names = true; } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) { linker_script = args_iter.next() orelse { fatal("expected parameter after {s}", .{arg}); @@ -1676,6 +1689,17 @@ fn buildOutputType( pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| { fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; + } else if (mem.eql(u8, arg, "-headerpad_size")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{s}'", .{arg}); + } + const next_arg = linker_args.items[i]; + headerpad_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| { + fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); + }; + } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) { + headerpad_max_install_names = true; } else if (mem.eql(u8, arg, "--gc-sections")) { linker_gc_sections = true; } else if (mem.eql(u8, arg, "--no-gc-sections")) { @@ -2795,6 +2819,8 @@ fn buildOutputType( .entitlements = entitlements, .pagezero_size = pagezero_size, .search_strategy = search_strategy, + .headerpad_size = headerpad_size, + .headerpad_max_install_names = headerpad_max_install_names, }) catch |err| switch (err) { error.LibCUnavailable => { const target = target_info.target; From a6fbdfabb9b60262594da012b8d877a43d8d8e99 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 25 Jun 2022 00:36:42 +0200 Subject: [PATCH 1959/2031] link-tests: add -headerpad_size test scenario --- test/link/macho/headerpad_size/build.zig | 25 ++++++++++++++++++++++++ test/link/macho/headerpad_size/main.c | 3 +++ 2 files changed, 28 insertions(+) create mode 100644 test/link/macho/headerpad_size/build.zig create mode 100644 test/link/macho/headerpad_size/main.c diff --git a/test/link/macho/headerpad_size/build.zig b/test/link/macho/headerpad_size/build.zig new file mode 100644 index 0000000000..0a02405e79 --- /dev/null +++ b/test/link/macho/headerpad_size/build.zig @@ -0,0 +1,25 @@ +const std = @import("std"); +const Builder = std.build.Builder; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + + const test_step = b.step("test", "Test"); + test_step.dependOn(b.getInstallStep()); + + const exe = b.addExecutable("main", null); + exe.setBuildMode(mode); + exe.addCSourceFile("main.c", &.{}); + exe.linkLibC(); + exe.headerpad_size = 0x10000; + + const check = exe.checkObject(.macho); + check.checkStart("sectname __text"); + check.checkNext("offset {offset}"); + check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } }); + + test_step.dependOn(&check.step); + + const run = exe.run(); + test_step.dependOn(&run.step); +} diff --git a/test/link/macho/headerpad_size/main.c b/test/link/macho/headerpad_size/main.c new file mode 100644 index 0000000000..ca68d24cc7 --- /dev/null +++ b/test/link/macho/headerpad_size/main.c @@ -0,0 +1,3 @@ +int main(int argc, char* argv[]) { + return 0; +} From 589bf67635a9fe9c3e6df4b63c09e0cc4954d29a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sat, 25 Jun 2022 10:39:53 +0200 Subject: [PATCH 1960/2031] macho: implement -headerpad_max_install_names --- lib/std/build.zig | 4 +- src/Compilation.zig | 8 +- src/link.zig | 2 +- src/link/Coff.zig | 2 +- src/link/Elf.zig | 2 +- src/link/MachO.zig | 53 +++++--- src/link/Wasm.zig | 2 +- src/main.zig | 12 +- test/link.zig | 5 + test/link/macho/headerpad/build.zig | 120 ++++++++++++++++++ .../{headerpad_size => headerpad}/main.c | 0 test/link/macho/headerpad_size/build.zig | 25 ---- 12 files changed, 177 insertions(+), 58 deletions(-) create mode 100644 test/link/macho/headerpad/build.zig rename test/link/macho/{headerpad_size => headerpad}/main.c (100%) delete mode 100644 test/link/macho/headerpad_size/build.zig diff --git a/lib/std/build.zig b/lib/std/build.zig index 20f59040c9..968ee043bf 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1595,7 +1595,7 @@ pub const LibExeObjStep = struct { /// (Darwin) Set size of the padding between the end of load commands /// and start of `__TEXT,__text` section. - headerpad_size: ?u64 = null, + headerpad_size: ?u32 = null, /// (Darwin) Automatically Set size of the padding between the end of load commands /// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN. @@ -2671,7 +2671,7 @@ pub const LibExeObjStep = struct { }; if (self.headerpad_size) |headerpad_size| { const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size}); - try zig_args.appendSlice(&[_][]const u8{ "-headerpad_size", size }); + try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size }); } if (self.headerpad_max_install_names) { try zig_args.append("-headerpad_max_install_names"); diff --git a/src/Compilation.zig b/src/Compilation.zig index 30603b91c5..652938d741 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -908,7 +908,7 @@ pub const InitOptions = struct { /// (Darwin) search strategy for system libraries search_strategy: ?link.File.MachO.SearchStrategy = null, /// (Darwin) set minimum space for future expansion of the load commands - headerpad_size: ?u64 = null, + headerpad_size: ?u32 = null, /// (Darwin) set enough space as if all paths were MATPATHLEN headerpad_max_install_names: bool = false, }; @@ -2369,7 +2369,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo /// to remind the programmer to update multiple related pieces of code that /// are in different locations. Bump this number when adding or deleting /// anything from the link cache manifest. -pub const link_hash_implementation_version = 5; +pub const link_hash_implementation_version = 6; fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void { const gpa = comp.gpa; @@ -2379,7 +2379,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - comptime assert(link_hash_implementation_version == 5); + comptime assert(link_hash_implementation_version == 6); if (comp.bin_file.options.module) |mod| { const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{ @@ -2486,6 +2486,8 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes try man.addOptionalFile(comp.bin_file.options.entitlements); man.hash.addOptional(comp.bin_file.options.pagezero_size); man.hash.addOptional(comp.bin_file.options.search_strategy); + man.hash.addOptional(comp.bin_file.options.headerpad_size); + man.hash.add(comp.bin_file.options.headerpad_max_install_names); // COFF specific stuff man.hash.addOptional(comp.bin_file.options.subsystem); diff --git a/src/link.zig b/src/link.zig index 96ec3ef8cd..21d54d531c 100644 --- a/src/link.zig +++ b/src/link.zig @@ -194,7 +194,7 @@ pub const Options = struct { search_strategy: ?File.MachO.SearchStrategy = null, /// (Darwin) set minimum space for future expansion of the load commands - headerpad_size: ?u64 = null, + headerpad_size: ?u32 = null, /// (Darwin) set enough space as if all paths were MATPATHLEN headerpad_max_install_names: bool = false, diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 3418a206df..77059a7fd9 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -969,7 +969,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) ! man = comp.cache_parent.obtain(); self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 5); + comptime assert(Compilation.link_hash_implementation_version == 6); for (self.base.options.objects) |obj| { _ = try man.addFile(obj.path, null); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 1051c0eb78..79545d1e1a 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1298,7 +1298,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // We are about to obtain this lock, so here we give other processes a chance first. self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 5); + comptime assert(Compilation.link_hash_implementation_version == 6); try man.addOptionalFile(self.base.options.linker_script); try man.addOptionalFile(self.base.options.version_script); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index dc6fe6e194..4ddee493b8 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -69,9 +69,6 @@ page_size: u16, /// and potentially stage2 release builds in the future. needs_prealloc: bool = true, -/// Size of the padding between the end of load commands and start of the '__TEXT,__text' section. -headerpad_size: u64, - /// The absolute address of the entry point. entry_addr: ?u64 = null, @@ -296,7 +293,7 @@ const default_pagezero_vmsize: u64 = 0x100000000; /// We commit 0x1000 = 4096 bytes of space to the header and /// the table of load commands. This should be plenty for any /// potential future extensions. -const default_headerpad_size: u64 = 0x1000; +const default_headerpad_size: u32 = 0x1000; pub const Export = struct { sym_index: ?u32 = null, @@ -403,12 +400,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO { const use_llvm = build_options.have_llvm and options.use_llvm; const use_stage1 = build_options.is_stage1 and options.use_stage1; const needs_prealloc = !(use_stage1 or use_llvm or options.cache_mode == .whole); - // TODO handle `headerpad_max_install_names` in incremental context - const explicit_headerpad_size = options.headerpad_size orelse 0; - const headerpad_size = if (needs_prealloc) - @maximum(explicit_headerpad_size, default_headerpad_size) - else - explicit_headerpad_size; const self = try gpa.create(MachO); errdefer gpa.destroy(self); @@ -421,7 +412,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO { .file = null, }, .page_size = page_size, - .headerpad_size = headerpad_size, .code_signature = if (requires_adhoc_codesig) CodeSignature.init(page_size) else null, .needs_prealloc = needs_prealloc, }; @@ -551,7 +541,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No // We are about to obtain this lock, so here we give other processes a chance first. self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 5); + comptime assert(Compilation.link_hash_implementation_version == 6); for (self.base.options.objects) |obj| { _ = try man.addFile(obj.path, null); @@ -566,6 +556,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No man.hash.add(stack_size); man.hash.addOptional(self.base.options.pagezero_size); man.hash.addOptional(self.base.options.search_strategy); + man.hash.addOptional(self.base.options.headerpad_size); + man.hash.add(self.base.options.headerpad_max_install_names); man.hash.addListOfBytes(self.base.options.lib_dirs); man.hash.addListOfBytes(self.base.options.framework_dirs); man.hash.addListOfBytes(self.base.options.frameworks); @@ -4470,9 +4462,10 @@ fn populateMissingMetadata(self: *MachO) !void { if (self.text_segment_cmd_index == null) { self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len); const needed_size = if (self.needs_prealloc) blk: { + const headerpad_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size); const program_code_size_hint = self.base.options.program_code_size_hint; const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint; - const ideal_size = self.headerpad_size + program_code_size_hint + got_size_hint; + const ideal_size = headerpad_size + program_code_size_hint + got_size_hint; const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size); log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size }); break :blk needed_size; @@ -4975,13 +4968,34 @@ fn allocateTextSegment(self: *MachO) !void { seg.inner.fileoff = 0; seg.inner.vmaddr = base_vmaddr; - var sizeofcmds: u64 = 0; + var sizeofcmds: u32 = 0; for (self.load_commands.items) |lc| { sizeofcmds += lc.cmdsize(); } - // TODO verify if `headerpad_max_install_names` leads to larger padding size - const offset = @sizeOf(macho.mach_header_64) + sizeofcmds + self.headerpad_size; + var padding: u32 = sizeofcmds + (self.base.options.headerpad_size orelse 0); + log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)}); + + if (self.base.options.headerpad_max_install_names) { + var min_headerpad_size: u32 = 0; + for (self.load_commands.items) |lc| switch (lc.cmd()) { + .ID_DYLIB, + .LOAD_WEAK_DYLIB, + .LOAD_DYLIB, + .REEXPORT_DYLIB, + => { + min_headerpad_size += @sizeOf(macho.dylib_command) + std.os.PATH_MAX + 1; + }, + + else => {}, + }; + log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{ + min_headerpad_size + @sizeOf(macho.mach_header_64), + }); + padding = @maximum(padding, min_headerpad_size); + } + const offset = @sizeOf(macho.mach_header_64) + padding; + log.debug("actual headerpad size 0x{x}", .{offset}); try self.allocateSegment(self.text_segment_cmd_index.?, offset); // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments. @@ -5109,7 +5123,10 @@ fn initSection( if (self.needs_prealloc) { const alignment_pow_2 = try math.powi(u32, 2, alignment); - const padding: ?u64 = if (segment_id == self.text_segment_cmd_index.?) self.headerpad_size else null; + const padding: ?u32 = if (segment_id == self.text_segment_cmd_index.?) + @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size) + else + null; const off = self.findFreeSpace(segment_id, alignment_pow_2, padding); log.debug("allocating {s},{s} section from 0x{x} to 0x{x}", .{ sect.segName(), @@ -5148,7 +5165,7 @@ fn initSection( return index; } -fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u64) u64 { +fn findFreeSpace(self: MachO, segment_id: u16, alignment: u64, start: ?u32) u64 { const seg = self.load_commands.items[segment_id].segment; if (seg.sections.items.len == 0) { return if (start) |v| v else seg.inner.fileoff; diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index c654fbe719..bc7e70b7da 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2481,7 +2481,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! // We are about to obtain this lock, so here we give other processes a chance first. self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 5); + comptime assert(Compilation.link_hash_implementation_version == 6); for (self.base.options.objects) |obj| { _ = try man.addFile(obj.path, null); diff --git a/src/main.zig b/src/main.zig index 87b0e6fc8a..d63e235360 100644 --- a/src/main.zig +++ b/src/main.zig @@ -450,7 +450,7 @@ const usage_build_generic = \\ -pagezero_size [value] (Darwin) size of the __PAGEZERO segment in hexadecimal notation \\ -search_paths_first (Darwin) search each dir in library search paths for `libx.dylib` then `libx.a` \\ -search_dylibs_first (Darwin) search `libx.dylib` in each dir in library search paths, then `libx.a` - \\ -headerpad_size [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation + \\ -headerpad [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation \\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN \\ --import-memory (WebAssembly) import memory from the environment \\ --import-table (WebAssembly) import function table from the host environment @@ -701,7 +701,7 @@ fn buildOutputType( var entitlements: ?[]const u8 = null; var pagezero_size: ?u64 = null; var search_strategy: ?link.File.MachO.SearchStrategy = null; - var headerpad_size: ?u64 = null; + var headerpad_size: ?u32 = null; var headerpad_max_install_names: bool = false; // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names. @@ -928,11 +928,11 @@ fn buildOutputType( search_strategy = .paths_first; } else if (mem.eql(u8, arg, "-search_dylibs_first")) { search_strategy = .dylibs_first; - } else if (mem.eql(u8, arg, "-headerpad_size")) { + } else if (mem.eql(u8, arg, "-headerpad")) { const next_arg = args_iter.next() orelse { fatal("expected parameter after {s}", .{arg}); }; - headerpad_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| { + headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| { fatal("unable to parser '{s}': {s}", .{ arg, @errorName(err) }); }; } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) { @@ -1689,13 +1689,13 @@ fn buildOutputType( pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| { fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; - } else if (mem.eql(u8, arg, "-headerpad_size")) { + } else if (mem.eql(u8, arg, "-headerpad")) { i += 1; if (i >= linker_args.items.len) { fatal("expected linker arg after '{s}'", .{arg}); } const next_arg = linker_args.items[i]; - headerpad_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| { + headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| { fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) { diff --git a/test/link.zig b/test/link.zig index 62bdcff4b0..0c301d6bcb 100644 --- a/test/link.zig +++ b/test/link.zig @@ -64,5 +64,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void { cases.addBuildFile("test/link/macho/search_strategy/build.zig", .{ .build_modes = true, }); + + cases.addBuildFile("test/link/macho/headerpad/build.zig", .{ + .build_modes = true, + .requires_macos_sdk = true, + }); } } diff --git a/test/link/macho/headerpad/build.zig b/test/link/macho/headerpad/build.zig new file mode 100644 index 0000000000..0730a01d44 --- /dev/null +++ b/test/link/macho/headerpad/build.zig @@ -0,0 +1,120 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const Builder = std.build.Builder; +const LibExeObjectStep = std.build.LibExeObjStep; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + + const test_step = b.step("test", "Test"); + test_step.dependOn(b.getInstallStep()); + + { + // Test -headerpad_max_install_names + const exe = simpleExe(b, mode); + exe.headerpad_max_install_names = true; + + const check = exe.checkObject(.macho); + check.checkStart("sectname __text"); + check.checkNext("offset {offset}"); + + switch (builtin.cpu.arch) { + .aarch64 => { + check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x4000 } }); + }, + .x86_64 => { + check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x1000 } }); + }, + else => unreachable, + } + + test_step.dependOn(&check.step); + + const run = exe.run(); + test_step.dependOn(&run.step); + } + + { + // Test -headerpad + const exe = simpleExe(b, mode); + exe.headerpad_size = 0x10000; + + const check = exe.checkObject(.macho); + check.checkStart("sectname __text"); + check.checkNext("offset {offset}"); + check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } }); + + test_step.dependOn(&check.step); + + const run = exe.run(); + test_step.dependOn(&run.step); + } + + { + // Test both flags with -headerpad overriding -headerpad_max_install_names + const exe = simpleExe(b, mode); + exe.headerpad_max_install_names = true; + exe.headerpad_size = 0x10000; + + const check = exe.checkObject(.macho); + check.checkStart("sectname __text"); + check.checkNext("offset {offset}"); + check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } }); + + test_step.dependOn(&check.step); + + const run = exe.run(); + test_step.dependOn(&run.step); + } + + { + // Test both flags with -headerpad_max_install_names overriding -headerpad + const exe = simpleExe(b, mode); + exe.headerpad_size = 0x1000; + exe.headerpad_max_install_names = true; + + const check = exe.checkObject(.macho); + check.checkStart("sectname __text"); + check.checkNext("offset {offset}"); + + switch (builtin.cpu.arch) { + .aarch64 => { + check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x4000 } }); + }, + .x86_64 => { + check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x1000 } }); + }, + else => unreachable, + } + + test_step.dependOn(&check.step); + + const run = exe.run(); + test_step.dependOn(&run.step); + } +} + +fn simpleExe(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep { + const exe = b.addExecutable("main", null); + exe.setBuildMode(mode); + exe.addCSourceFile("main.c", &.{}); + exe.linkLibC(); + exe.linkFramework("CoreFoundation"); + exe.linkFramework("Foundation"); + exe.linkFramework("Cocoa"); + exe.linkFramework("CoreGraphics"); + exe.linkFramework("CoreHaptics"); + exe.linkFramework("CoreAudio"); + exe.linkFramework("AVFoundation"); + exe.linkFramework("CoreImage"); + exe.linkFramework("CoreLocation"); + exe.linkFramework("CoreML"); + exe.linkFramework("CoreVideo"); + exe.linkFramework("CoreText"); + exe.linkFramework("CryptoKit"); + exe.linkFramework("GameKit"); + exe.linkFramework("SwiftUI"); + exe.linkFramework("StoreKit"); + exe.linkFramework("SpriteKit"); + return exe; +} diff --git a/test/link/macho/headerpad_size/main.c b/test/link/macho/headerpad/main.c similarity index 100% rename from test/link/macho/headerpad_size/main.c rename to test/link/macho/headerpad/main.c diff --git a/test/link/macho/headerpad_size/build.zig b/test/link/macho/headerpad_size/build.zig deleted file mode 100644 index 0a02405e79..0000000000 --- a/test/link/macho/headerpad_size/build.zig +++ /dev/null @@ -1,25 +0,0 @@ -const std = @import("std"); -const Builder = std.build.Builder; - -pub fn build(b: *Builder) void { - const mode = b.standardReleaseOptions(); - - const test_step = b.step("test", "Test"); - test_step.dependOn(b.getInstallStep()); - - const exe = b.addExecutable("main", null); - exe.setBuildMode(mode); - exe.addCSourceFile("main.c", &.{}); - exe.linkLibC(); - exe.headerpad_size = 0x10000; - - const check = exe.checkObject(.macho); - check.checkStart("sectname __text"); - check.checkNext("offset {offset}"); - check.checkComputeCompare("offset", .{ .op = .gte, .value = .{ .literal = 0x10000 } }); - - test_step.dependOn(&check.step); - - const run = exe.run(); - test_step.dependOn(&run.step); -} From 140bac6395610a3b9e5d5f92e2b366a27f6dcdc8 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 25 Jun 2022 18:36:56 +0200 Subject: [PATCH 1961/2031] link/wasm: Sort data segments We now ensure the "bss" section is last, which allows us to not emit this section and let the runtime initialize the memory with 0's instead. This allows for smaller binaries. The order of the other segments is arbitrary and does not matter, this may change in the future. --- src/link/Wasm.zig | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 834ac3e1e1..e57f87d37c 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -1255,6 +1255,9 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { } fn allocateAtoms(self: *Wasm) !void { + // first sort the data segments + try sortDataSegments(self); + var it = self.atoms.iterator(); while (it.next()) |entry| { const segment = &self.segments.items[entry.key_ptr.*]; @@ -1278,6 +1281,36 @@ fn allocateAtoms(self: *Wasm) !void { } } +fn sortDataSegments(self: *Wasm) !void { + var new_mapping: std.StringArrayHashMapUnmanaged(u32) = .{}; + try new_mapping.ensureUnusedCapacity(self.base.allocator, self.data_segments.count()); + errdefer new_mapping.deinit(self.base.allocator); + + const keys = try self.base.allocator.dupe([]const u8, self.data_segments.keys()); + defer self.base.allocator.free(keys); + + const SortContext = struct { + fn sort(_: void, lhs: []const u8, rhs: []const u8) bool { + return order(lhs) <= order(rhs); + } + + fn order(name: []const u8) u8 { + if (mem.startsWith(u8, name, ".rodata")) return 0; + if (mem.startsWith(u8, name, ".data")) return 1; + if (mem.startsWith(u8, name, ".text")) return 2; + return 3; + } + }; + + std.sort.sort([]const u8, keys, {}, SortContext.sort); + for (keys) |key| { + const segment_index = self.data_segments.get(key).?; + new_mapping.putAssumeCapacity(key, segment_index); + } + self.data_segments.deinit(self.base.allocator); + self.data_segments = new_mapping; +} + fn setupImports(self: *Wasm) !void { log.debug("Merging imports", .{}); var discarded_it = self.discarded.keyIterator(); @@ -2337,8 +2370,13 @@ fn emitNameSection(self: *Wasm, file: fs.File, arena: Allocator) !void { } } // data segments are already 'ordered' - for (self.data_segments.keys()) |key, index| { - segments.appendAssumeCapacity(.{ .index = @intCast(u32, index), .name = key }); + var data_segment_index: u32 = 0; + for (self.data_segments.keys()) |key| { + // bss section is not emitted when this condition holds true, so we also + // do not output a name for it. + if (!self.base.options.import_memory and std.mem.eql(u8, key, ".bss")) continue; + segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key }); + data_segment_index += 1; } std.sort.sort(Name, funcs.values(), {}, Name.lessThan); From 960c14206094484cee056b8806b9b8f75a84891d Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 25 Jun 2022 14:59:22 +0200 Subject: [PATCH 1962/2031] stage2 ARM: implement basic intCast and error union wrapping --- src/arch/arm/CodeGen.zig | 66 +++++++++++++++---- test/behavior/align.zig | 2 - test/behavior/array.zig | 6 -- test/behavior/basic.zig | 9 --- test/behavior/bitreverse.zig | 1 - test/behavior/bugs/1076.zig | 1 - test/behavior/bugs/11165.zig | 4 -- test/behavior/bugs/11213.zig | 1 - test/behavior/bugs/3779.zig | 5 -- test/behavior/bugs/394.zig | 1 - test/behavior/bugs/7187.zig | 2 - test/behavior/byteswap.zig | 1 - test/behavior/byval_arg_var.zig | 1 - test/behavior/cast.zig | 11 ---- test/behavior/comptime_memory.zig | 1 - test/behavior/enum.zig | 1 - test/behavior/error.zig | 13 ---- test/behavior/eval.zig | 8 --- test/behavior/fn.zig | 1 - test/behavior/if.zig | 1 - test/behavior/math.zig | 3 - test/behavior/pointers.zig | 5 -- test/behavior/reflection.zig | 1 - test/behavior/slice.zig | 5 -- .../struct_contains_slice_of_itself.zig | 2 - test/behavior/tuple.zig | 1 - test/behavior/type_info.zig | 2 - test/behavior/union.zig | 7 -- test/behavior/var_args.zig | 1 - test/behavior/while.zig | 2 - test/behavior/widening.zig | 1 - 31 files changed, 53 insertions(+), 113 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 3cc853154b..9a93969209 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1002,18 +1002,35 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); - const operand_ty = self.air.typeOf(ty_op.operand); const operand = try self.resolveInst(ty_op.operand); + const operand_ty = self.air.typeOf(ty_op.operand); + const dest_ty = self.air.typeOfIndex(inst); + + const operand_abi_size = operand_ty.abiSize(self.target.*); + const dest_abi_size = dest_ty.abiSize(self.target.*); const info_a = operand_ty.intInfo(self.target.*); - const info_b = self.air.typeOfIndex(inst).intInfo(self.target.*); - if (info_a.signedness != info_b.signedness) - return self.fail("TODO gen intcast sign safety in semantic analysis", .{}); + const info_b = dest_ty.intInfo(self.target.*); - if (info_a.bits == info_b.bits) - return self.finishAir(inst, operand, .{ ty_op.operand, .none, .none }); + const dst_mcv: MCValue = blk: { + if (info_a.bits == info_b.bits) { + break :blk operand; + } + if (operand_abi_size > 4 or dest_abi_size > 4) { + return self.fail("TODO implement intCast for abi sizes larger than 4", .{}); + } - return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch}); - // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + const operand_lock: ?RegisterLock = switch (operand) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); + + const reg = try self.register_manager.allocReg(inst, gp); + try self.genSetReg(dest_ty, reg, operand); + break :blk MCValue{ .register = reg }; + }; + + return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); } fn truncRegister( @@ -1880,7 +1897,22 @@ fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { /// T to E!T fn airWrapErrUnionPayload(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 return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch}); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const error_union_ty = self.air.getRefType(ty_op.ty); + const payload_ty = error_union_ty.errorUnionPayload(); + const operand = try self.resolveInst(ty_op.operand); + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result operand; + + const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*)); + const abi_align = error_union_ty.abiAlignment(self.target.*); + const stack_offset = @intCast(u32, try self.allocMem(inst, abi_size, abi_align)); + const payload_off = errUnionPayloadOffset(payload_ty, self.target.*); + const err_off = errUnionErrorOffset(payload_ty, self.target.*); + try self.genSetStack(payload_ty, stack_offset - @intCast(u32, payload_off), operand); + try self.genSetStack(Type.anyerror, stack_offset - @intCast(u32, err_off), .{ .immediate = 0 }); + + break :result MCValue{ .stack_offset = stack_offset }; + }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -1890,10 +1922,18 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const error_union_ty = self.air.getRefType(ty_op.ty); const payload_ty = error_union_ty.errorUnionPayload(); - const mcv = try self.resolveInst(ty_op.operand); - if (!payload_ty.hasRuntimeBits()) break :result mcv; + const operand = try self.resolveInst(ty_op.operand); + if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result operand; - return self.fail("TODO implement wrap errunion error for non-empty payloads", .{}); + const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*)); + const abi_align = error_union_ty.abiAlignment(self.target.*); + const stack_offset = @intCast(u32, try self.allocMem(inst, abi_size, abi_align)); + const payload_off = errUnionPayloadOffset(payload_ty, self.target.*); + const err_off = errUnionErrorOffset(payload_ty, self.target.*); + try self.genSetStack(Type.anyerror, stack_offset - @intCast(u32, err_off), operand); + try self.genSetStack(payload_ty, stack_offset - @intCast(u32, payload_off), .undef); + + break :result MCValue{ .stack_offset = stack_offset }; }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -4273,7 +4313,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { block_data.mcv = switch (operand_mcv) { .none, .dead, .unreach => unreachable, .register, .stack_offset, .memory => operand_mcv, - .immediate, .stack_argument_offset => blk: { + .immediate, .stack_argument_offset, .cpsr_flags => blk: { const new_mcv = try self.allocRegOrMem(block, true); try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); break :blk new_mcv; diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 056354f237..b0df7c6523 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -8,7 +8,6 @@ var foo: u8 align(4) = 100; test "global variable alignment" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO comptime try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4); comptime try expect(@TypeOf(&foo) == *align(4) u8); @@ -223,7 +222,6 @@ fn testBytesAlign(b: u8) !void { test "@alignCast slices" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var array align(4) = [_]u32{ 1, 1 }; const slice = array[0..]; diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 59f2baf5b6..aea4dd357b 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -59,7 +59,6 @@ test "array init with mult" { test "array literal with explicit type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const hex_mult: [4]u16 = .{ 4096, 256, 16, 1 }; @@ -112,7 +111,6 @@ test "array with sentinels" { } if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { fn doTheTest(is_ct: bool) !void { @@ -162,7 +160,6 @@ test "nested arrays of strings" { test "nested arrays of integers" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const array_of_numbers = [_][2]u8{ @@ -190,7 +187,6 @@ fn plusOne(x: u32) u32 { test "single-item pointer to array indexing and slicing" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; try testSingleItemPtrArrayIndexSlice(); comptime try testSingleItemPtrArrayIndexSlice(); @@ -313,7 +309,6 @@ test "comptime evaluating function that takes array by value" { test "runtime initialize array elem and then implicit cast to slice" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO var two: i32 = 2; const x: []const i32 = &[_]i32{two}; @@ -322,7 +317,6 @@ test "runtime initialize array elem and then implicit cast to slice" { test "array literal as argument to function" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { fn entry(two: i32) !void { diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index ad403a60a8..d288d35787 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -204,7 +204,6 @@ test "opaque types" { } if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO try expect(*OpaqueA != *OpaqueB); @@ -365,7 +364,6 @@ fn testMemcpyMemset() !void { } test "variable is allowed to be a pointer to an opaque type" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var x: i32 = 1234; @@ -457,7 +455,6 @@ fn testArray2DConstDoublePtr(ptr: *const f32) !void { test "double implicit cast in same expression" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var x = @as(i32, @as(u16, nine())); try expect(x == 9); @@ -616,8 +613,6 @@ test "self reference through fn ptr field" { } test "global variable initialized to global variable array element" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - try expect(global_ptr == &gdt[0]); } const GDTEntry = struct { @@ -681,7 +676,6 @@ test "explicit cast optional pointers" { } test "pointer comparison" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const a = @as([]const u8, "a"); @@ -787,7 +781,6 @@ test "pointer to thread local array" { threadlocal var buffer: [11]u8 = undefined; test "auto created variables have correct alignment" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const S = struct { @@ -889,7 +882,6 @@ test "labeled block implicitly ends in a break" { test "catch in block has correct result location" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { fn open() error{A}!@This() { @@ -920,7 +912,6 @@ test "labeled block with runtime branch forwards its result location type to bre test "try in labeled block doesn't cast to wrong type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { a: u32, diff --git a/test/behavior/bitreverse.zig b/test/behavior/bitreverse.zig index 821bc21f27..585fe381b0 100644 --- a/test/behavior/bitreverse.zig +++ b/test/behavior/bitreverse.zig @@ -157,7 +157,6 @@ test "bitReverse vectors u0" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; comptime try vector0(); try vector0(); diff --git a/test/behavior/bugs/1076.zig b/test/behavior/bugs/1076.zig index 6bf38eb617..dedf021293 100644 --- a/test/behavior/bugs/1076.zig +++ b/test/behavior/bugs/1076.zig @@ -5,7 +5,6 @@ const expect = std.testing.expect; test "comptime code should not modify constant data" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; try testCastPtrOfArrayToSliceAndPtr(); diff --git a/test/behavior/bugs/11165.zig b/test/behavior/bugs/11165.zig index b3c4a32ec4..60677743ad 100644 --- a/test/behavior/bugs/11165.zig +++ b/test/behavior/bugs/11165.zig @@ -1,8 +1,6 @@ const builtin = @import("builtin"); test "bytes" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - const S = struct { a: u32, c: [5]u8, @@ -23,8 +21,6 @@ test "bytes" { } test "aggregate" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - const S = struct { a: u32, c: [5]u8, diff --git a/test/behavior/bugs/11213.zig b/test/behavior/bugs/11213.zig index f238eb5aab..4699bda333 100644 --- a/test/behavior/bugs/11213.zig +++ b/test/behavior/bugs/11213.zig @@ -4,7 +4,6 @@ const testing = std.testing; test { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const g: error{Test}!void = error.Test; diff --git a/test/behavior/bugs/3779.zig b/test/behavior/bugs/3779.zig index 308b26cbcf..dc906442a6 100644 --- a/test/behavior/bugs/3779.zig +++ b/test/behavior/bugs/3779.zig @@ -8,7 +8,6 @@ const ptr_tag_name: [*:0]const u8 = tag_name; test "@tagName() returns a string literal" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 gets the type wrong if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO try std.testing.expect(*const [13:0]u8 == @TypeOf(tag_name)); try std.testing.expect(std.mem.eql(u8, "TestEnumValue", tag_name)); @@ -22,7 +21,6 @@ const ptr_error_name: [*:0]const u8 = error_name; test "@errorName() returns a string literal" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 gets the type wrong if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO try std.testing.expect(*const [13:0]u8 == @TypeOf(error_name)); try std.testing.expect(std.mem.eql(u8, "TestErrorCode", error_name)); @@ -36,7 +34,6 @@ const ptr_type_name: [*:0]const u8 = type_name; test "@typeName() returns a string literal" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 gets the type wrong if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO try std.testing.expect(*const [type_name.len:0]u8 == @TypeOf(type_name)); try std.testing.expect(std.mem.eql(u8, "behavior.bugs.3779.TestType", type_name)); @@ -49,7 +46,6 @@ const expected_contents = "hello zig\n"; test "@embedFile() returns a string literal" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO try std.testing.expect(*const [expected_contents.len:0]u8 == @TypeOf(actual_contents)); try std.testing.expect(std.mem.eql(u8, expected_contents, actual_contents)); @@ -63,7 +59,6 @@ fn testFnForSrc() std.builtin.SourceLocation { test "@src() returns a struct containing 0-terminated string slices" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const src = testFnForSrc(); try std.testing.expect([:0]const u8 == @TypeOf(src.file)); diff --git a/test/behavior/bugs/394.zig b/test/behavior/bugs/394.zig index 9f5165d1ba..02e90258bf 100644 --- a/test/behavior/bugs/394.zig +++ b/test/behavior/bugs/394.zig @@ -11,7 +11,6 @@ const expect = @import("std").testing.expect; const builtin = @import("builtin"); test "fixed" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const x = S{ .x = 3, diff --git a/test/behavior/bugs/7187.zig b/test/behavior/bugs/7187.zig index 9ce63ad4cd..bb2e82af89 100644 --- a/test/behavior/bugs/7187.zig +++ b/test/behavior/bugs/7187.zig @@ -3,8 +3,6 @@ const builtin = @import("builtin"); const expect = std.testing.expect; test "miscompilation with bool return type" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - var x: usize = 1; var y: bool = getFalse(); _ = y; diff --git a/test/behavior/byteswap.zig b/test/behavior/byteswap.zig index f944c731b7..263f2c046e 100644 --- a/test/behavior/byteswap.zig +++ b/test/behavior/byteswap.zig @@ -118,7 +118,6 @@ test "@byteSwap vectors u0" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; comptime try vector0(); try vector0(); diff --git a/test/behavior/byval_arg_var.zig b/test/behavior/byval_arg_var.zig index d2e8ecb638..b6b972d2d3 100644 --- a/test/behavior/byval_arg_var.zig +++ b/test/behavior/byval_arg_var.zig @@ -6,7 +6,6 @@ var result: []const u8 = "wrong"; test "pass string literal byvalue to a generic var param" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; start(); blowUpStack(10); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 383b72e046..c980de8e8a 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -251,7 +251,6 @@ fn MakeType(comptime T: type) type { test "implicit cast from *[N]T to [*c]T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var x: [4]u16 = [4]u16{ 0, 1, 2, 3 }; var y: [*c]u16 = &x; @@ -372,7 +371,6 @@ test "cast from ?[*]T to ??[*]T" { test "peer type unsigned int to signed" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var w: u31 = 5; var x: u8 = 7; @@ -451,7 +449,6 @@ fn castToOptionalTypeError(z: i32) !void { test "implicitly cast from [0]T to anyerror![]T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO try testCastZeroArrayToErrSliceMut(); comptime try testCastZeroArrayToErrSliceMut(); @@ -817,7 +814,6 @@ test "peer type resolution: error union after non-error" { test "peer cast *[0]T to E![]const T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO var buffer: [5]u8 = "abcde".*; @@ -832,7 +828,6 @@ test "peer cast *[0]T to E![]const T" { test "peer cast *[0]T to []const T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO var buffer: [5]u8 = "abcde".*; @@ -893,7 +888,6 @@ test "peer cast [:x]T to []T" { test "peer cast [N:x]T to [N]T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -909,7 +903,6 @@ test "peer cast [N:x]T to [N]T" { test "peer cast *[N:x]T to *[N]T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -1146,7 +1139,6 @@ test "implicit ptr to *anyopaque" { test "return null from fn() anyerror!?&T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const a = returnNullFromOptionalTypeErrorRef(); const b = returnNullLitFromOptionalTypeErrorRef(); @@ -1248,7 +1240,6 @@ fn incrementVoidPtrValue(value: ?*anyopaque) void { test "implicit cast *[0]T to E![]const u8" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO var x = @as(anyerror![]const u8, &[0]u8{}); try expect((x catch unreachable).len == 0); @@ -1387,7 +1378,6 @@ test "peer type resolution: unreachable, null, slice" { test "cast i8 fn call peers to i32 result" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -1417,7 +1407,6 @@ test "cast compatible optional types" { test "coerce undefined single-item pointer of array to error union of slice" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const a = @as([*]u8, undefined)[0..0]; var b: error{a}![]const u8 = a; diff --git a/test/behavior/comptime_memory.zig b/test/behavior/comptime_memory.zig index 1a3e9ef606..1fc3f64cda 100644 --- a/test/behavior/comptime_memory.zig +++ b/test/behavior/comptime_memory.zig @@ -5,7 +5,6 @@ const ptr_size = @sizeOf(usize); test "type pun signed and unsigned as single pointer" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO comptime { var x: u32 = 0; diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index e230032d04..602ad47b59 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -24,7 +24,6 @@ fn testIntToEnumEval(x: i32) !void { const IntToEnumNumber = enum { Zero, One, Two, Three, Four }; test "int to enum" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; try testIntToEnumEval(3); diff --git a/test/behavior/error.zig b/test/behavior/error.zig index c9697a0f82..c32ca6f6a1 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -29,7 +29,6 @@ fn shouldBeNotEqual(a: anyerror, b: anyerror) void { } test "error binary operator" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const a = errBinaryOperatorG(true) catch 3; @@ -61,14 +60,12 @@ pub fn baz() anyerror!i32 { } test "error wrapping" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; try expect((baz() catch unreachable) == 15); } test "unwrap simple value from error" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const i = unwrapSimpleValueFromErrorDo() catch unreachable; @@ -79,7 +76,6 @@ fn unwrapSimpleValueFromErrorDo() anyerror!isize { } test "error return in assignment" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; doErrReturnInAssignment() catch unreachable; @@ -126,7 +122,6 @@ test "debug info for optional error set" { } test "implicit cast to optional to error union to return result loc" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const S = struct { @@ -149,7 +144,6 @@ test "implicit cast to optional to error union to return result loc" { test "fn returning empty error set can be passed as fn returning any error" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO entry(); @@ -159,7 +153,6 @@ test "fn returning empty error set can be passed as fn returning any error" { test "fn returning empty error set can be passed as fn returning any error - pointer" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO entryPtr(); @@ -249,7 +242,6 @@ fn testExplicitErrorSetCast(set1: Set1) !void { test "comptime test error for empty error set" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testComptimeTestErrorEmptySet(1234); @@ -268,7 +260,6 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void { test "comptime err to int of error set with only 1 possible value" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); @@ -323,7 +314,6 @@ fn quux_1() !i32 { } test "error: Zero sized error set returned with value payload crash" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; _ = try foo3(0); @@ -422,7 +412,6 @@ test "nested error union function call in optional unwrap" { test "return function call to error set from error union function" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const S = struct { @@ -455,7 +444,6 @@ test "optional error set is the same size as error set" { test "nested catch" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const S = struct { @@ -487,7 +475,6 @@ test "function pointer with return type that is error union with payload which i if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const S = struct { diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 0ea3a33990..dde27afe3f 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -90,7 +90,6 @@ fn letsTryToCompareBools(a: bool, b: bool) bool { return max(bool, a, b); } test "inlined block and runtime block phi" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try expect(letsTryToCompareBools(true, true)); @@ -390,7 +389,6 @@ test "return 0 from function that has u0 return type" { } test "statically initialized struct" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO st_init_str_foo.x += 1; @@ -502,7 +500,6 @@ test "comptime shlWithOverflow" { } test "const ptr to variable data changes at runtime" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try expect(foo_ref.name[0] == 'a'); @@ -605,8 +602,6 @@ fn testCompTimeUIntComparisons(x: u32) void { const hi1 = "hi"; const hi2 = hi1; test "const global shares pointer with other same one" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - try assertEqualPtrs(&hi1[0], &hi2[0]); comptime try expect(&hi1[0] == &hi2[0]); } @@ -976,7 +971,6 @@ test "closure capture type of runtime-known parameter" { test "comptime break passing through runtime condition converted to runtime break" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -1061,7 +1055,6 @@ test "comptime break operand passing through runtime condition converted to runt test "comptime break operand passing through runtime switch converted to runtime break" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest(runtime: u8) !void { @@ -1227,7 +1220,6 @@ test "storing an array of type in a field" { test "pass pointer to field of comptime-only type as a runtime parameter" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { const Mixed = struct { diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 324a2ad247..383d553781 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -197,7 +197,6 @@ fn addPointCoords(pt: Point) i32 { } test "pass by non-copying value through var arg" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; try expect((try addPointCoordsVar(Point{ .x = 1, .y = 2 })) == 3); diff --git a/test/behavior/if.zig b/test/behavior/if.zig index 2f5a52f7e2..d59a974ce4 100644 --- a/test/behavior/if.zig +++ b/test/behavior/if.zig @@ -44,7 +44,6 @@ var global_with_val: anyerror!u32 = 0; var global_with_err: anyerror!u32 = error.SomeError; test "unwrap mutable global var" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (global_with_val) |v| { diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 3b73d93c01..2b10996bfb 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -652,7 +652,6 @@ test "@addWithOverflow" { test "small int addition" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO var x: u2 = 0; try expect(x == 0); @@ -1136,8 +1135,6 @@ fn testShrExact(x: u8) !void { } test "shift left/right on u0 operand" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - const S = struct { fn doTheTest() !void { var x: u0 = 0; diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index a47d931883..adbc308742 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -84,7 +84,6 @@ test "assigning integer to C pointer" { test "C pointer comparison and arithmetic" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -430,7 +429,6 @@ test "indexing array with sentinel returns correct type" { test "element pointer to slice" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -452,7 +450,6 @@ test "element pointer to slice" { test "element pointer arithmetic to slice" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -476,8 +473,6 @@ test "element pointer arithmetic to slice" { } test "array slicing to slice" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - const S = struct { fn doTheTest() !void { var str: [5]i32 = [_]i32{ 1, 2, 3, 4, 5 }; diff --git a/test/behavior/reflection.zig b/test/behavior/reflection.zig index 865813a663..899628a938 100644 --- a/test/behavior/reflection.zig +++ b/test/behavior/reflection.zig @@ -27,7 +27,6 @@ fn dummy(a: bool, b: i32, c: f32) i32 { test "reflection: @field" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO var f = Foo{ diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index e2694759bb..5e0498342c 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -118,7 +118,6 @@ test "slice of type" { test "generic malloc free" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const a = memAlloc(u8, 10) catch unreachable; @@ -482,7 +481,6 @@ test "slice pointer-to-array null terminated" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO comptime { var array = [5:0]u8{ 1, 2, 3, 4, 5 }; @@ -508,7 +506,6 @@ test "slice pointer-to-array null terminated" { test "slice pointer-to-array zero length" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO comptime { { @@ -643,7 +640,6 @@ test "slicing array with sentinel as end index" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { fn do() !void { @@ -664,7 +660,6 @@ test "slicing slice with sentinel as end index" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { fn do() !void { diff --git a/test/behavior/struct_contains_slice_of_itself.zig b/test/behavior/struct_contains_slice_of_itself.zig index cbff2514ea..feb382ed3e 100644 --- a/test/behavior/struct_contains_slice_of_itself.zig +++ b/test/behavior/struct_contains_slice_of_itself.zig @@ -13,7 +13,6 @@ const NodeAligned = struct { test "struct contains slice of itself" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO var other_nodes = [_]Node{ @@ -54,7 +53,6 @@ test "struct contains slice of itself" { test "struct contains aligned slice of itself" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO var other_nodes = [_]NodeAligned{ diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index f9237bb230..13772865ef 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -124,7 +124,6 @@ test "tuple initializer for var" { test "array-like initializer for tuple types" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const T = @Type(.{ .Struct = .{ diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index 7bbfbbc1ab..b1012e69c8 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -527,7 +527,6 @@ test "type info for async frames" { test "Declarations are returned in declaration order" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const S = struct { @@ -552,7 +551,6 @@ test "Struct.is_tuple for anon list literal" { test "Struct.is_tuple for anon struct literal" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const info = @typeInfo(@TypeOf(.{ .a = 0 })); try expect(!info.Struct.is_tuple); diff --git a/test/behavior/union.zig b/test/behavior/union.zig index caa5d26aea..3c8874e3a8 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -36,7 +36,6 @@ test "init union with runtime value - floats" { } test "basic unions" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; var foo = Foo{ .int = 1 }; @@ -91,7 +90,6 @@ const FooExtern = extern union { }; test "basic extern unions" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; var foo = FooExtern{ .int = 1 }; @@ -398,7 +396,6 @@ test "tagged union with no payloads" { } test "union with only 1 field casted to its enum type" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const Literal = union(enum) { @@ -487,7 +484,6 @@ test "union initializer generates padding only if needed" { } test "runtime tag name with single field" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const U = union(enum) { @@ -540,7 +536,6 @@ const Baz = enum { A, B, C, D }; test "tagged union type" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const foo1 = TaggedFoo{ .One = 13 }; @@ -670,7 +665,6 @@ const PartialInstWithPayload = union(enum) { }; test "union with only 1 field casted to its enum type which has enum value specified" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const Literal = union(enum) { @@ -1110,7 +1104,6 @@ test "union enum type gets a separate scope" { test "global variable struct contains union initialized to non-most-aligned field" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const T = struct { const U = union(enum) { diff --git a/test/behavior/var_args.zig b/test/behavior/var_args.zig index d1baf7bd31..c482498a71 100644 --- a/test/behavior/var_args.zig +++ b/test/behavior/var_args.zig @@ -39,7 +39,6 @@ fn addSomeStuff(args: anytype) i32 { } test "runtime parameter before var args" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try expect((try extraFn(10, .{})) == 0); diff --git a/test/behavior/while.zig b/test/behavior/while.zig index 71f1d253e9..b664e73b89 100644 --- a/test/behavior/while.zig +++ b/test/behavior/while.zig @@ -256,7 +256,6 @@ fn returnWithImplicitCastFromWhileLoopTest() anyerror!void { } test "while on error union with else result follow else prong" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const result = while (returnError()) |value| { @@ -266,7 +265,6 @@ test "while on error union with else result follow else prong" { } test "while on error union with else result follow break prong" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const result = while (returnSuccess(10)) |value| { diff --git a/test/behavior/widening.zig b/test/behavior/widening.zig index a6475e88e6..691755ac2a 100644 --- a/test/behavior/widening.zig +++ b/test/behavior/widening.zig @@ -30,7 +30,6 @@ test "integer widening u0 to u8" { test "implicit unsigned integer to signed integer" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var a: u8 = 250; From 22720981ea50b1cee38d8309e0cd32df401b8156 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Sat, 25 Jun 2022 21:27:56 -0700 Subject: [PATCH 1963/2031] Move sys_can_stack_trace from GPA to std.debug so that it can be re-used as needed --- lib/std/debug.zig | 21 +++++++++++++++++++++ lib/std/heap/general_purpose_allocator.zig | 22 +--------------------- lib/std/testing/failing_allocator.zig | 4 +++- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index ba1f509e6c..9401bca257 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -26,6 +26,27 @@ pub const runtime_safety = switch (builtin.mode) { .ReleaseFast, .ReleaseSmall => false, }; +pub const sys_can_stack_trace = switch (builtin.cpu.arch) { + // Observed to go into an infinite loop. + // TODO: Make this work. + .mips, + .mipsel, + => false, + + // `@returnAddress()` in LLVM 10 gives + // "Non-Emscripten WebAssembly hasn't implemented __builtin_return_address". + .wasm32, + .wasm64, + => builtin.os.tag == .emscripten, + + // `@returnAddress()` is unsupported in LLVM 13. + .bpfel, + .bpfeb, + => false, + + else => true, +}; + pub const LineInfo = struct { line: u64, column: u64, diff --git a/lib/std/heap/general_purpose_allocator.zig b/lib/std/heap/general_purpose_allocator.zig index 11d897ac1b..abffad751c 100644 --- a/lib/std/heap/general_purpose_allocator.zig +++ b/lib/std/heap/general_purpose_allocator.zig @@ -105,28 +105,8 @@ const StackTrace = std.builtin.StackTrace; /// Integer type for pointing to slots in a small allocation const SlotIndex = std.meta.Int(.unsigned, math.log2(page_size) + 1); -const sys_can_stack_trace = switch (builtin.cpu.arch) { - // Observed to go into an infinite loop. - // TODO: Make this work. - .mips, - .mipsel, - => false, - - // `@returnAddress()` in LLVM 10 gives - // "Non-Emscripten WebAssembly hasn't implemented __builtin_return_address". - .wasm32, - .wasm64, - => builtin.os.tag == .emscripten, - - // `@returnAddress()` is unsupported in LLVM 13. - .bpfel, - .bpfeb, - => false, - - else => true, -}; const default_test_stack_trace_frames: usize = if (builtin.is_test) 8 else 4; -const default_sys_stack_trace_frames: usize = if (sys_can_stack_trace) default_test_stack_trace_frames else 0; +const default_sys_stack_trace_frames: usize = if (std.debug.sys_can_stack_trace) default_test_stack_trace_frames else 0; const default_stack_trace_frames: usize = switch (builtin.mode) { .Debug => default_sys_stack_trace_frames, else => 0, diff --git a/lib/std/testing/failing_allocator.zig b/lib/std/testing/failing_allocator.zig index c5d1d3df30..03e345986b 100644 --- a/lib/std/testing/failing_allocator.zig +++ b/lib/std/testing/failing_allocator.zig @@ -19,9 +19,11 @@ pub const FailingAllocator = struct { freed_bytes: usize, allocations: usize, deallocations: usize, - stack_addresses: [16]usize, + stack_addresses: [num_stack_frames]usize, has_induced_failure: bool, + const num_stack_frames = if (std.debug.sys_can_stack_trace) 16 else 0; + /// `fail_index` is the number of successful allocations you can /// expect from this allocator. The next allocation will fail. /// For example, if this is called with `fail_index` equal to 2, From 76f83282776151bd79715c4c00d13d06de633354 Mon Sep 17 00:00:00 2001 From: Pierre Curto Date: Sat, 25 Jun 2022 10:59:17 +0200 Subject: [PATCH 1964/2031] doc: update std.builtin.TypeInfo to std.builtin.Type --- doc/langref.html.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 1f445b1b59..9170cf5b62 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -9655,7 +9655,7 @@ test "integer truncation" { {#header_close#} {#header_open|@Type#} -

{#syntax#}@Type(comptime info: std.builtin.TypeInfo) type{#endsyntax#}
+
{#syntax#}@Type(comptime info: std.builtin.Type) type{#endsyntax#}

This function is the inverse of {#link|@typeInfo#}. It reifies type information into a {#syntax#}type{#endsyntax#}. @@ -9697,7 +9697,7 @@ test "integer truncation" { {#header_close#} {#header_open|@typeInfo#} -

{#syntax#}@typeInfo(comptime T: type) std.builtin.TypeInfo{#endsyntax#}
+
{#syntax#}@typeInfo(comptime T: type) std.builtin.Type{#endsyntax#}

Provides type reflection.

From a76775b50a65fd0ea0fd17d6ef3c42058df13997 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Fri, 17 Jun 2022 22:05:41 -0700 Subject: [PATCH 1965/2031] Fix stack traces with non-null `first_address` on Windows Before this commit, the passed in length would always be given to the RtlCaptureStackBackTrace call. Now we always give the length of the actual buffer we're using (the addr_buf_stack size of 32 or the passed in length if it's larger than 32; this matches what the doc comment says the function was meant to be doing as well). This was causing empty stack traces for things like the GeneralPurposeAllocator leak checking. Fixes #6687 --- lib/std/debug.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index af5d54ae62..54c614f270 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -180,11 +180,10 @@ pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void { pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackTrace) void { if (native_os == .windows) { const addrs = stack_trace.instruction_addresses; - const u32_addrs_len = @intCast(u32, addrs.len); const first_addr = first_address orelse { stack_trace.index = windows.ntdll.RtlCaptureStackBackTrace( 0, - u32_addrs_len, + @intCast(u32, addrs.len), @ptrCast(**anyopaque, addrs.ptr), null, ); @@ -192,7 +191,7 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT }; var addr_buf_stack: [32]usize = undefined; const addr_buf = if (addr_buf_stack.len > addrs.len) addr_buf_stack[0..] else addrs; - const n = windows.ntdll.RtlCaptureStackBackTrace(0, u32_addrs_len, @ptrCast(**anyopaque, addr_buf.ptr), null); + const n = windows.ntdll.RtlCaptureStackBackTrace(0, @intCast(u32, addr_buf.len), @ptrCast(**anyopaque, addr_buf.ptr), null); const first_index = for (addr_buf[0..n]) |addr, i| { if (addr == first_addr) { break i; @@ -201,7 +200,8 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT stack_trace.index = 0; return; }; - const slice = addr_buf[first_index..n]; + const end_index = math.min(first_index + addrs.len, n); + const slice = addr_buf[first_index..end_index]; // We use a for loop here because slice and addrs may alias. for (slice) |addr, i| { addrs[i] = addr; From efc5c97bff87d4c28ae9642fe69d9bc2c7e9eeb7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 27 Jun 2022 09:24:18 +0200 Subject: [PATCH 1966/2031] macho: implement -dead_strip_dylibs linker flag --- lib/std/build.zig | 6 +++ src/Compilation.zig | 8 +++- src/link.zig | 3 ++ src/link/Coff.zig | 2 +- src/link/Elf.zig | 2 +- src/link/MachO.zig | 14 ++++++- src/link/Wasm.zig | 2 +- src/main.zig | 7 ++++ test/link.zig | 2 +- test/link/macho/dead_strip_dylibs/build.zig | 46 +++++++++++++++++++++ test/link/macho/dead_strip_dylibs/main.c | 10 +++++ test/link/macho/frameworks/build.zig | 32 -------------- test/link/macho/frameworks/main.c | 7 ---- 13 files changed, 94 insertions(+), 47 deletions(-) create mode 100644 test/link/macho/dead_strip_dylibs/build.zig create mode 100644 test/link/macho/dead_strip_dylibs/main.c delete mode 100644 test/link/macho/frameworks/build.zig delete mode 100644 test/link/macho/frameworks/main.c diff --git a/lib/std/build.zig b/lib/std/build.zig index 968ee043bf..da5ffb33bd 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1601,6 +1601,9 @@ pub const LibExeObjStep = struct { /// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN. headerpad_max_install_names: bool = false, + /// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols. + dead_strip_dylibs: bool = false, + /// Position Independent Code force_pic: ?bool = null, @@ -2676,6 +2679,9 @@ pub const LibExeObjStep = struct { if (self.headerpad_max_install_names) { try zig_args.append("-headerpad_max_install_names"); } + if (self.dead_strip_dylibs) { + try zig_args.append("-dead_strip_dylibs"); + } if (self.bundle_compiler_rt) |x| { if (x) { diff --git a/src/Compilation.zig b/src/Compilation.zig index 652938d741..fffa777f22 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -911,6 +911,8 @@ pub const InitOptions = struct { headerpad_size: ?u32 = null, /// (Darwin) set enough space as if all paths were MATPATHLEN headerpad_max_install_names: bool = false, + /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols + dead_strip_dylibs: bool = false, }; fn addPackageTableToCacheHash( @@ -1754,6 +1756,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .search_strategy = options.search_strategy, .headerpad_size = options.headerpad_size, .headerpad_max_install_names = options.headerpad_max_install_names, + .dead_strip_dylibs = options.dead_strip_dylibs, }); errdefer bin_file.destroy(); comp.* = .{ @@ -2369,7 +2372,7 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo /// to remind the programmer to update multiple related pieces of code that /// are in different locations. Bump this number when adding or deleting /// anything from the link cache manifest. -pub const link_hash_implementation_version = 6; +pub const link_hash_implementation_version = 7; fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void { const gpa = comp.gpa; @@ -2379,7 +2382,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - comptime assert(link_hash_implementation_version == 6); + comptime assert(link_hash_implementation_version == 7); if (comp.bin_file.options.module) |mod| { const main_zig_file = try mod.main_pkg.root_src_directory.join(arena, &[_][]const u8{ @@ -2488,6 +2491,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes man.hash.addOptional(comp.bin_file.options.search_strategy); man.hash.addOptional(comp.bin_file.options.headerpad_size); man.hash.add(comp.bin_file.options.headerpad_max_install_names); + man.hash.add(comp.bin_file.options.dead_strip_dylibs); // COFF specific stuff man.hash.addOptional(comp.bin_file.options.subsystem); diff --git a/src/link.zig b/src/link.zig index 21d54d531c..18e10dc74c 100644 --- a/src/link.zig +++ b/src/link.zig @@ -199,6 +199,9 @@ pub const Options = struct { /// (Darwin) set enough space as if all paths were MATPATHLEN headerpad_max_install_names: bool = false, + /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols + dead_strip_dylibs: bool = false, + pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { return if (options.use_lld) .Obj else options.output_mode; } diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 77059a7fd9..0e7d7c89ee 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -969,7 +969,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) ! man = comp.cache_parent.obtain(); self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 6); + comptime assert(Compilation.link_hash_implementation_version == 7); for (self.base.options.objects) |obj| { _ = try man.addFile(obj.path, null); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 79545d1e1a..faeb7c9d27 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1298,7 +1298,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // We are about to obtain this lock, so here we give other processes a chance first. self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 6); + comptime assert(Compilation.link_hash_implementation_version == 7); try man.addOptionalFile(self.base.options.linker_script); try man.addOptionalFile(self.base.options.version_script); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 4ddee493b8..bd354ba1ce 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -541,7 +541,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No // We are about to obtain this lock, so here we give other processes a chance first. self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 6); + comptime assert(Compilation.link_hash_implementation_version == 7); for (self.base.options.objects) |obj| { _ = try man.addFile(obj.path, null); @@ -558,6 +558,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No man.hash.addOptional(self.base.options.search_strategy); man.hash.addOptional(self.base.options.headerpad_size); man.hash.add(self.base.options.headerpad_max_install_names); + man.hash.add(self.base.options.dead_strip_dylibs); man.hash.addListOfBytes(self.base.options.lib_dirs); man.hash.addListOfBytes(self.base.options.framework_dirs); man.hash.addListOfBytes(self.base.options.frameworks); @@ -987,6 +988,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try argv.append("-headerpad_max_install_names"); } + if (self.base.options.dead_strip_dylibs) { + try argv.append("-dead_strip_dylibs"); + } + if (self.base.options.entry) |entry| { try argv.append("-e"); try argv.append(entry); @@ -1425,7 +1430,12 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy try self.dylibs.append(self.base.allocator, dylib); try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id); - if (!(opts.is_dependent or self.referenced_dylibs.contains(dylib_id))) { + const should_link_dylib_even_if_unreachable = blk: { + if (self.base.options.dead_strip_dylibs) break :blk false; + break :blk !(opts.is_dependent or self.referenced_dylibs.contains(dylib_id)); + }; + + if (should_link_dylib_even_if_unreachable) { try self.addLoadDylibLC(dylib_id); try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {}); } diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index b074799771..467aa89621 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2546,7 +2546,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! // We are about to obtain this lock, so here we give other processes a chance first. self.base.releaseLock(); - comptime assert(Compilation.link_hash_implementation_version == 6); + comptime assert(Compilation.link_hash_implementation_version == 7); for (self.base.options.objects) |obj| { _ = try man.addFile(obj.path, null); diff --git a/src/main.zig b/src/main.zig index d63e235360..8f9d3e5a13 100644 --- a/src/main.zig +++ b/src/main.zig @@ -452,6 +452,7 @@ const usage_build_generic = \\ -search_dylibs_first (Darwin) search `libx.dylib` in each dir in library search paths, then `libx.a` \\ -headerpad [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation \\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN + \\ -dead_strip_dylibs (Darwin) remove dylibs that are unreachable by the entry point or exported symbols \\ --import-memory (WebAssembly) import memory from the environment \\ --import-table (WebAssembly) import function table from the host environment \\ --export-table (WebAssembly) export function table to the host environment @@ -703,6 +704,7 @@ fn buildOutputType( var search_strategy: ?link.File.MachO.SearchStrategy = null; var headerpad_size: ?u32 = null; var headerpad_max_install_names: bool = false; + var dead_strip_dylibs: bool = false; // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names. // This array is populated by zig cc frontend and then has to be converted to zig-style @@ -937,6 +939,8 @@ fn buildOutputType( }; } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) { headerpad_max_install_names = true; + } else if (mem.eql(u8, arg, "-dead_strip_dylibs")) { + dead_strip_dylibs = true; } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) { linker_script = args_iter.next() orelse { fatal("expected parameter after {s}", .{arg}); @@ -1700,6 +1704,8 @@ fn buildOutputType( }; } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) { headerpad_max_install_names = true; + } else if (mem.eql(u8, arg, "-dead_strip_dylibs")) { + dead_strip_dylibs = true; } else if (mem.eql(u8, arg, "--gc-sections")) { linker_gc_sections = true; } else if (mem.eql(u8, arg, "--no-gc-sections")) { @@ -2821,6 +2827,7 @@ fn buildOutputType( .search_strategy = search_strategy, .headerpad_size = headerpad_size, .headerpad_max_install_names = headerpad_max_install_names, + .dead_strip_dylibs = dead_strip_dylibs, }) catch |err| switch (err) { error.LibCUnavailable => { const target = target_info.target; diff --git a/test/link.zig b/test/link.zig index 0c301d6bcb..51aef4c496 100644 --- a/test/link.zig +++ b/test/link.zig @@ -40,7 +40,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { .build_modes = true, }); - cases.addBuildFile("test/link/macho/frameworks/build.zig", .{ + cases.addBuildFile("test/link/macho/dead_strip_dylibs/build.zig", .{ .build_modes = true, .requires_macos_sdk = true, }); diff --git a/test/link/macho/dead_strip_dylibs/build.zig b/test/link/macho/dead_strip_dylibs/build.zig new file mode 100644 index 0000000000..2e4b69c229 --- /dev/null +++ b/test/link/macho/dead_strip_dylibs/build.zig @@ -0,0 +1,46 @@ +const std = @import("std"); +const Builder = std.build.Builder; +const LibExeObjectStep = std.build.LibExeObjStep; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + + const test_step = b.step("test", "Test the program"); + + { + // Without -dead_strip_dylibs we expect `-la` to include liba.dylib in the final executable + const exe = createScenario(b, mode); + + const check = exe.checkObject(.macho); + check.checkStart("cmd LOAD_DYLIB"); + check.checkNext("name {*}Cocoa"); + + check.checkStart("cmd LOAD_DYLIB"); + check.checkNext("name {*}libobjc{*}.dylib"); + + test_step.dependOn(&check.step); + + const run_cmd = exe.run(); + test_step.dependOn(&run_cmd.step); + } + + { + // With -dead_strip_dylibs, we should include liba.dylib as it's unreachable + const exe = createScenario(b, mode); + exe.dead_strip_dylibs = true; + + const run_cmd = exe.run(); + run_cmd.expected_exit_code = @bitCast(u8, @as(i8, -2)); // should fail + test_step.dependOn(&run_cmd.step); + } +} + +fn createScenario(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep { + const exe = b.addExecutable("test", null); + b.default_step.dependOn(&exe.step); + exe.addCSourceFile("main.c", &[0][]const u8{}); + exe.setBuildMode(mode); + exe.linkLibC(); + exe.linkFramework("Cocoa"); + return exe; +} diff --git a/test/link/macho/dead_strip_dylibs/main.c b/test/link/macho/dead_strip_dylibs/main.c new file mode 100644 index 0000000000..836d1b1cc1 --- /dev/null +++ b/test/link/macho/dead_strip_dylibs/main.c @@ -0,0 +1,10 @@ +#include + +int main() { + if (objc_getClass("NSObject") == 0) { + return -1; + } + if (objc_getClass("NSApplication") == 0) { + return -2; + } +} diff --git a/test/link/macho/frameworks/build.zig b/test/link/macho/frameworks/build.zig deleted file mode 100644 index 7086606f30..0000000000 --- a/test/link/macho/frameworks/build.zig +++ /dev/null @@ -1,32 +0,0 @@ -const std = @import("std"); -const Builder = std.build.Builder; - -pub fn build(b: *Builder) void { - const mode = b.standardReleaseOptions(); - - const test_step = b.step("test", "Test the program"); - - const exe = b.addExecutable("test", null); - b.default_step.dependOn(&exe.step); - exe.addCSourceFile("main.c", &[0][]const u8{}); - exe.setBuildMode(mode); - exe.linkLibC(); - exe.linkFramework("Cocoa"); - - const check = exe.checkObject(.macho); - check.checkStart("cmd LOAD_DYLIB"); - check.checkNext("name {*}Cocoa"); - - switch (mode) { - .Debug, .ReleaseSafe => { - check.checkStart("cmd LOAD_DYLIB"); - check.checkNext("name {*}libobjc{*}.dylib"); - }, - else => {}, - } - - test_step.dependOn(&check.step); - - const run_cmd = exe.run(); - test_step.dependOn(&run_cmd.step); -} diff --git a/test/link/macho/frameworks/main.c b/test/link/macho/frameworks/main.c deleted file mode 100644 index b9dab990b2..0000000000 --- a/test/link/macho/frameworks/main.c +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include - -int main() { - assert(objc_getClass("NSObject") > 0); - assert(objc_getClass("NSApplication") > 0); -} From 0dd28920daa5127ffe5a3691343fa519f7547cfd Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 27 Jun 2022 19:48:10 +0200 Subject: [PATCH 1967/2031] macho: implement and handle `-needed-*` and `-needed_*` family of flags MachO linker now handles `-needed-l`, `-needed_library=` and `-needed_framework=`. While on macOS `-l` is equivalent to `-needed-l`, and `-framework` to `-needed_framework`, it can be used to the same effect as on Linux if combined with `-dead_strip_dylibs`. This commit also adds handling for `-needed_library` which is macOS specific flag only (in addition to `-needed-l`). Finally, in order to leverage new linker testing harness, this commit added ability to specify lowering to those flags via `build.zig`: `linkSystemLibraryNeeded` (and related), and `linkFrameworkNeeded`. --- lib/std/build.zig | 72 +++++++++++++++++--- src/Compilation.zig | 8 +-- src/link.zig | 2 +- src/link/MachO.zig | 74 +++++++++++++-------- src/main.zig | 40 ++++++++--- test/link.zig | 9 +++ test/link/macho/dead_strip_dylibs/build.zig | 2 +- test/link/macho/dead_strip_dylibs/main.c | 3 +- test/link/macho/dylib/main.c | 2 +- test/link/macho/needed_framework/build.zig | 27 ++++++++ test/link/macho/needed_framework/main.c | 3 + test/link/macho/needed_l/a.c | 1 + test/link/macho/needed_l/build.zig | 35 ++++++++++ test/link/macho/needed_l/main.c | 3 + test/link/macho/search_strategy/main.c | 2 +- 15 files changed, 227 insertions(+), 56 deletions(-) create mode 100644 test/link/macho/needed_framework/build.zig create mode 100644 test/link/macho/needed_framework/main.c create mode 100644 test/link/macho/needed_l/a.c create mode 100644 test/link/macho/needed_l/build.zig create mode 100644 test/link/macho/needed_l/main.c diff --git a/lib/std/build.zig b/lib/std/build.zig index da5ffb33bd..ab4472d01e 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -11,7 +11,6 @@ const ArrayList = std.ArrayList; const StringHashMap = std.StringHashMap; const Allocator = mem.Allocator; const process = std.process; -const BufSet = std.BufSet; const EnvMap = std.process.EnvMap; const fmt_lib = std.fmt; const File = std.fs.File; @@ -1484,7 +1483,7 @@ pub const LibExeObjStep = struct { lib_paths: ArrayList([]const u8), rpaths: ArrayList([]const u8), framework_dirs: ArrayList([]const u8), - frameworks: BufSet, + frameworks: StringHashMap(bool), verbose_link: bool, verbose_cc: bool, emit_analysis: EmitOption = .default, @@ -1643,6 +1642,7 @@ pub const LibExeObjStep = struct { pub const SystemLib = struct { name: []const u8, + needed: bool, use_pkg_config: enum { /// Don't use pkg-config, just pass -lfoo where foo is name. no, @@ -1744,7 +1744,7 @@ pub const LibExeObjStep = struct { .kind = kind, .root_src = root_src, .name = name, - .frameworks = BufSet.init(builder.allocator), + .frameworks = StringHashMap(bool).init(builder.allocator), .step = Step.init(base_id, name, builder.allocator, make), .version = ver, .out_filename = undefined, @@ -1893,8 +1893,11 @@ pub const LibExeObjStep = struct { } pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void { - // Note: No need to dupe because frameworks dupes internally. - self.frameworks.insert(framework_name) catch unreachable; + self.frameworks.put(self.builder.dupe(framework_name), false) catch unreachable; + } + + pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void { + self.frameworks.put(self.builder.dupe(framework_name), true) catch unreachable; } /// Returns whether the library, executable, or object depends on a particular system library. @@ -1935,6 +1938,7 @@ pub const LibExeObjStep = struct { self.link_objects.append(.{ .system_lib = .{ .name = "c", + .needed = false, .use_pkg_config = .no, }, }) catch unreachable; @@ -1947,6 +1951,7 @@ pub const LibExeObjStep = struct { self.link_objects.append(.{ .system_lib = .{ .name = "c++", + .needed = false, .use_pkg_config = .no, }, }) catch unreachable; @@ -1971,6 +1976,19 @@ pub const LibExeObjStep = struct { self.link_objects.append(.{ .system_lib = .{ .name = self.builder.dupe(name), + .needed = false, + .use_pkg_config = .no, + }, + }) catch unreachable; + } + + /// This one has no integration with anything, it just puts -needed-lname on the command line. + /// Prefer to use `linkSystemLibraryNeeded` instead. + pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void { + self.link_objects.append(.{ + .system_lib = .{ + .name = self.builder.dupe(name), + .needed = true, .use_pkg_config = .no, }, }) catch unreachable; @@ -1982,6 +2000,19 @@ pub const LibExeObjStep = struct { self.link_objects.append(.{ .system_lib = .{ .name = self.builder.dupe(lib_name), + .needed = false, + .use_pkg_config = .force, + }, + }) catch unreachable; + } + + /// This links against a system library, exclusively using pkg-config to find the library. + /// Prefer to use `linkSystemLibraryNeeded` instead. + pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void { + self.link_objects.append(.{ + .system_lib = .{ + .name = self.builder.dupe(lib_name), + .needed = true, .use_pkg_config = .force, }, }) catch unreachable; @@ -2084,6 +2115,14 @@ pub const LibExeObjStep = struct { } pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void { + self.linkSystemLibraryInner(name, false); + } + + pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void { + self.linkSystemLibraryInner(name, true); + } + + fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, needed: bool) void { if (isLibCLibrary(name)) { self.linkLibC(); return; @@ -2096,6 +2135,7 @@ pub const LibExeObjStep = struct { self.link_objects.append(.{ .system_lib = .{ .name = self.builder.dupe(name), + .needed = needed, .use_pkg_config = .yes, }, }) catch unreachable; @@ -2437,7 +2477,7 @@ pub const LibExeObjStep = struct { if (!other.isDynamicLibrary()) { var it = other.frameworks.iterator(); while (it.next()) |framework| { - self.frameworks.insert(framework.*) catch unreachable; + self.frameworks.put(framework.key_ptr.*, framework.value_ptr.*) catch unreachable; } } }, @@ -2473,8 +2513,9 @@ pub const LibExeObjStep = struct { }, .system_lib => |system_lib| { + const prefix: []const u8 = if (system_lib.needed) "-needed-l" else "-l"; switch (system_lib.use_pkg_config) { - .no => try zig_args.append(builder.fmt("-l{s}", .{system_lib.name})), + .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })), .yes, .force => { if (self.runPkgConfig(system_lib.name)) |args| { try zig_args.appendSlice(args); @@ -2488,7 +2529,10 @@ pub const LibExeObjStep = struct { .yes => { // pkg-config failed, so fall back to linking the library // by name directly. - try zig_args.append(builder.fmt("-l{s}", .{system_lib.name})); + try zig_args.append(builder.fmt("{s}{s}", .{ + prefix, + system_lib.name, + })); }, .force => { panic("pkg-config failed for library {s}", .{system_lib.name}); @@ -2972,9 +3016,15 @@ pub const LibExeObjStep = struct { } var it = self.frameworks.iterator(); - while (it.next()) |framework| { - zig_args.append("-framework") catch unreachable; - zig_args.append(framework.*) catch unreachable; + while (it.next()) |entry| { + const name = entry.key_ptr.*; + const needed = entry.value_ptr.*; + if (needed) { + zig_args.append("-needed_framework") catch unreachable; + } else { + zig_args.append("-framework") catch unreachable; + } + zig_args.append(name) catch unreachable; } } else { if (self.framework_dirs.items.len > 0) { diff --git a/src/Compilation.zig b/src/Compilation.zig index fffa777f22..01f6a15e2c 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -791,7 +791,7 @@ pub const InitOptions = struct { c_source_files: []const CSourceFile = &[0]CSourceFile{}, link_objects: []LinkObject = &[0]LinkObject{}, framework_dirs: []const []const u8 = &[0][]const u8{}, - frameworks: []const []const u8 = &[0][]const u8{}, + frameworks: std.StringArrayHashMapUnmanaged(SystemLib) = .{}, system_lib_names: []const []const u8 = &.{}, system_lib_infos: []const SystemLib = &.{}, /// These correspond to the WASI libc emulated subcomponents including: @@ -1097,7 +1097,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { // Our linker can't handle objects or most advanced options yet. if (options.link_objects.len != 0 or options.c_source_files.len != 0 or - options.frameworks.len != 0 or + options.frameworks.count() != 0 or options.system_lib_names.len != 0 or options.link_libc or options.link_libcpp or link_eh_frame_hdr or @@ -1215,7 +1215,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { options.target, options.is_native_abi, link_libc, - options.system_lib_names.len != 0 or options.frameworks.len != 0, + options.system_lib_names.len != 0 or options.frameworks.count() != 0, options.libc_installation, options.native_darwin_sdk != null, ); @@ -2485,7 +2485,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes // Mach-O specific stuff man.hash.addListOfBytes(comp.bin_file.options.framework_dirs); - man.hash.addListOfBytes(comp.bin_file.options.frameworks); + link.hashAddSystemLibs(&man.hash, comp.bin_file.options.frameworks); try man.addOptionalFile(comp.bin_file.options.entitlements); man.hash.addOptional(comp.bin_file.options.pagezero_size); man.hash.addOptional(comp.bin_file.options.search_strategy); diff --git a/src/link.zig b/src/link.zig index 18e10dc74c..c3d1d216c0 100644 --- a/src/link.zig +++ b/src/link.zig @@ -162,7 +162,7 @@ pub const Options = struct { objects: []Compilation.LinkObject, framework_dirs: []const []const u8, - frameworks: []const []const u8, + frameworks: std.StringArrayHashMapUnmanaged(SystemLib), system_libs: std.StringArrayHashMapUnmanaged(SystemLib), wasi_emulated_libs: []const wasi_libc.CRTFile, lib_dirs: []const []const u8, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index bd354ba1ce..d660d2ce7e 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -561,7 +561,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No man.hash.add(self.base.options.dead_strip_dylibs); man.hash.addListOfBytes(self.base.options.lib_dirs); man.hash.addListOfBytes(self.base.options.framework_dirs); - man.hash.addListOfBytes(self.base.options.frameworks); + link.hashAddSystemLibs(&man.hash, self.base.options.frameworks); man.hash.addListOfBytes(self.base.options.rpath_list); if (is_dyn_lib) { man.hash.addOptionalBytes(self.base.options.install_name); @@ -768,19 +768,20 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No } // Shared and static libraries passed via `-l` flag. - var search_lib_names = std.ArrayList([]const u8).init(arena); + var candidate_libs = std.StringArrayHashMap(Compilation.SystemLib).init(arena); - const system_libs = self.base.options.system_libs.keys(); - for (system_libs) |link_lib| { + const system_lib_names = self.base.options.system_libs.keys(); + for (system_lib_names) |system_lib_name| { // By this time, we depend on these libs being dynamically linked libraries and not static libraries // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which // case we want to avoid prepending "-l". - if (Compilation.classifyFileExt(link_lib) == .shared_library) { - try positionals.append(link_lib); + if (Compilation.classifyFileExt(system_lib_name) == .shared_library) { + try positionals.append(system_lib_name); continue; } - try search_lib_names.append(link_lib); + const system_lib_info = self.base.options.system_libs.get(system_lib_name).?; + try candidate_libs.put(system_lib_name, system_lib_info); } var lib_dirs = std.ArrayList([]const u8).init(arena); @@ -792,18 +793,18 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No } } - var libs = std.ArrayList([]const u8).init(arena); + var libs = std.StringArrayHashMap(Compilation.SystemLib).init(arena); // Assume ld64 default -search_paths_first if no strategy specified. const search_strategy = self.base.options.search_strategy orelse .paths_first; - outer: for (search_lib_names.items) |lib_name| { + outer: for (candidate_libs.keys()) |lib_name| { switch (search_strategy) { .paths_first => { // Look in each directory for a dylib (stub first), and then for archive for (lib_dirs.items) |dir| { for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| { if (try resolveLib(arena, dir, lib_name, ext)) |full_path| { - try libs.append(full_path); + try libs.put(full_path, candidate_libs.get(lib_name).?); continue :outer; } } @@ -817,13 +818,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No for (lib_dirs.items) |dir| { for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| { if (try resolveLib(arena, dir, lib_name, ext)) |full_path| { - try libs.append(full_path); + try libs.put(full_path, candidate_libs.get(lib_name).?); continue :outer; } } } else for (lib_dirs.items) |dir| { if (try resolveLib(arena, dir, lib_name, ".a")) |full_path| { - try libs.append(full_path); + try libs.put(full_path, candidate_libs.get(lib_name).?); } else { log.warn("library not found for '-l{s}'", .{lib_name}); lib_not_found = true; @@ -847,7 +848,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No // re-exports every single symbol definition. for (lib_dirs.items) |dir| { if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| { - try libs.append(full_path); + try libs.put(full_path, .{ .needed = false }); libsystem_available = true; break :blk; } @@ -857,8 +858,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No for (lib_dirs.items) |dir| { if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| { if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| { - try libs.append(libsystem_path); - try libs.append(libc_path); + try libs.put(libsystem_path, .{ .needed = false }); + try libs.put(libc_path, .{ .needed = false }); libsystem_available = true; break :blk; } @@ -872,7 +873,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "darwin", libsystem_name, }); - try libs.append(full_path); + try libs.put(full_path, .{ .needed = false }); } // frameworks @@ -885,16 +886,16 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No } } - outer: for (self.base.options.frameworks) |framework| { + outer: for (self.base.options.frameworks.keys()) |f_name| { for (framework_dirs.items) |dir| { for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { - if (try resolveFramework(arena, dir, framework, ext)) |full_path| { - try libs.append(full_path); + if (try resolveFramework(arena, dir, f_name, ext)) |full_path| { + try libs.put(full_path, self.base.options.frameworks.get(f_name).?); continue :outer; } } } else { - log.warn("framework not found for '-framework {s}'", .{framework}); + log.warn("framework not found for '-framework {s}'", .{f_name}); framework_not_found = true; } } @@ -1025,15 +1026,25 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try argv.append("-lc"); for (self.base.options.system_libs.keys()) |l_name| { - try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name})); + const needed = self.base.options.system_libs.get(l_name).?.needed; + const arg = if (needed) + try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name}) + else + try std.fmt.allocPrint(arena, "-l{s}", .{l_name}); + try argv.append(arg); } for (self.base.options.lib_dirs) |lib_dir| { try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir})); } - for (self.base.options.frameworks) |framework| { - try argv.append(try std.fmt.allocPrint(arena, "-framework {s}", .{framework})); + for (self.base.options.frameworks.keys()) |framework| { + const needed = self.base.options.frameworks.get(framework).?.needed; + const arg = if (needed) + try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework}) + else + try std.fmt.allocPrint(arena, "-framework {s}", .{framework}); + try argv.append(arg); } for (self.base.options.framework_dirs) |framework_dir| { @@ -1056,7 +1067,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No defer dependent_libs.deinit(); try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs); try self.parseAndForceLoadStaticArchives(must_link_archives.keys()); - try self.parseLibs(libs.items, self.base.options.sysroot, &dependent_libs); + try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs); try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs); } @@ -1381,6 +1392,7 @@ const DylibCreateOpts = struct { dependent_libs: *std.fifo.LinearFifo(Dylib.Id, .Dynamic), id: ?Dylib.Id = null, is_dependent: bool = false, + is_needed: bool = false, }; pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDylibError!bool { @@ -1431,7 +1443,7 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id); const should_link_dylib_even_if_unreachable = blk: { - if (self.base.options.dead_strip_dylibs) break :blk false; + if (self.base.options.dead_strip_dylibs and !opts.is_needed) break :blk false; break :blk !(opts.is_dependent or self.referenced_dylibs.contains(dylib_id)); }; @@ -1479,12 +1491,20 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi } } -fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void { - for (libs) |lib| { +fn parseLibs( + self: *MachO, + lib_names: []const []const u8, + lib_infos: []const Compilation.SystemLib, + syslibroot: ?[]const u8, + dependent_libs: anytype, +) !void { + for (lib_names) |lib, i| { + const lib_info = lib_infos[i]; log.debug("parsing lib path '{s}'", .{lib}); if (try self.parseDylib(lib, .{ .syslibroot = syslibroot, .dependent_libs = dependent_libs, + .is_needed = lib_info.needed, })) continue; if (try self.parseArchive(lib, false)) continue; diff --git a/src/main.zig b/src/main.zig index 8f9d3e5a13..cbfd403352 100644 --- a/src/main.zig +++ b/src/main.zig @@ -444,6 +444,8 @@ const usage_build_generic = \\ --stack [size] Override default stack size \\ --image-base [addr] Set base address for executable image \\ -framework [name] (Darwin) link against framework + \\ -needed_framework [name] (Darwin) link against framework (even if unused) + \\ -needed_library [lib] (Darwin) link against system library (even if unused) \\ -F[dir] (Darwin) add search path for frameworks \\ -install_name=[value] (Darwin) add dylib's install name \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature @@ -750,8 +752,7 @@ fn buildOutputType( var framework_dirs = std.ArrayList([]const u8).init(gpa); defer framework_dirs.deinit(); - var frameworks = std.ArrayList([]const u8).init(gpa); - defer frameworks.deinit(); + var frameworks: std.StringArrayHashMapUnmanaged(Compilation.SystemLib) = .{}; // null means replace with the test executable binary var test_exec_args = std.ArrayList(?[]const u8).init(gpa); @@ -912,9 +913,15 @@ fn buildOutputType( fatal("expected parameter after {s}", .{arg}); }); } else if (mem.eql(u8, arg, "-framework")) { - try frameworks.append(args_iter.next() orelse { + const path = args_iter.next() orelse { fatal("expected parameter after {s}", .{arg}); - }); + }; + try frameworks.put(gpa, path, .{ .needed = false }); + } else if (mem.eql(u8, arg, "-needed_framework")) { + const path = args_iter.next() orelse { + fatal("expected parameter after {s}", .{arg}); + }; + try frameworks.put(gpa, path, .{ .needed = true }); } else if (mem.eql(u8, arg, "-install_name")) { install_name = args_iter.next() orelse { fatal("expected parameter after {s}", .{arg}); @@ -956,7 +963,10 @@ fn buildOutputType( // We don't know whether this library is part of libc or libc++ until // we resolve the target, so we simply append to the list for now. try system_libs.put(next_arg, .{ .needed = false }); - } else if (mem.eql(u8, arg, "--needed-library") or mem.eql(u8, arg, "-needed-l")) { + } else if (mem.eql(u8, arg, "--needed-library") or + mem.eql(u8, arg, "-needed-l") or + mem.eql(u8, arg, "--needed_library")) + { const next_arg = args_iter.next() orelse { fatal("expected parameter after {s}", .{arg}); }; @@ -1586,7 +1596,7 @@ fn buildOutputType( try clang_argv.appendSlice(it.other_args); }, .framework_dir => try framework_dirs.append(it.only_arg), - .framework => try frameworks.append(it.only_arg), + .framework => try frameworks.put(gpa, it.only_arg, .{ .needed = false }), .nostdlibinc => want_native_include_dirs = false, .strip => strip = true, .exec_model => { @@ -1874,7 +1884,19 @@ fn buildOutputType( if (i >= linker_args.items.len) { fatal("expected linker arg after '{s}'", .{arg}); } - try frameworks.append(linker_args.items[i]); + try frameworks.put(gpa, linker_args.items[i], .{ .needed = false }); + } else if (mem.eql(u8, arg, "-needed_framework")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{s}'", .{arg}); + } + try frameworks.put(gpa, linker_args.items[i], .{ .needed = true }); + } else if (mem.eql(u8, arg, "-needed_library")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{s}'", .{arg}); + } + try system_libs.put(linker_args.items[i], .{ .needed = true }); } else if (mem.eql(u8, arg, "-compatibility_version")) { i += 1; if (i >= linker_args.items.len) { @@ -2244,7 +2266,7 @@ fn buildOutputType( if (comptime builtin.target.isDarwin()) { // If we want to link against frameworks, we need system headers. - if (framework_dirs.items.len > 0 or frameworks.items.len > 0) + if (framework_dirs.items.len > 0 or frameworks.count() > 0) want_native_include_dirs = true; } @@ -2734,7 +2756,7 @@ fn buildOutputType( .c_source_files = c_source_files.items, .link_objects = link_objects.items, .framework_dirs = framework_dirs.items, - .frameworks = frameworks.items, + .frameworks = frameworks, .system_lib_names = system_libs.keys(), .system_lib_infos = system_libs.values(), .wasi_emulated_libs = wasi_emulated_libs.items, diff --git a/test/link.zig b/test/link.zig index 51aef4c496..6878881a66 100644 --- a/test/link.zig +++ b/test/link.zig @@ -45,6 +45,15 @@ pub fn addCases(cases: *tests.StandaloneContext) void { .requires_macos_sdk = true, }); + cases.addBuildFile("test/link/macho/needed_l/build.zig", .{ + .build_modes = true, + }); + + cases.addBuildFile("test/link/macho/needed_framework/build.zig", .{ + .build_modes = true, + .requires_macos_sdk = true, + }); + // Try to build and run an Objective-C executable. cases.addBuildFile("test/link/macho/objc/build.zig", .{ .build_modes = true, diff --git a/test/link/macho/dead_strip_dylibs/build.zig b/test/link/macho/dead_strip_dylibs/build.zig index 2e4b69c229..efdaf191bd 100644 --- a/test/link/macho/dead_strip_dylibs/build.zig +++ b/test/link/macho/dead_strip_dylibs/build.zig @@ -6,6 +6,7 @@ pub fn build(b: *Builder) void { const mode = b.standardReleaseOptions(); const test_step = b.step("test", "Test the program"); + test_step.dependOn(b.getInstallStep()); { // Without -dead_strip_dylibs we expect `-la` to include liba.dylib in the final executable @@ -37,7 +38,6 @@ pub fn build(b: *Builder) void { fn createScenario(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep { const exe = b.addExecutable("test", null); - b.default_step.dependOn(&exe.step); exe.addCSourceFile("main.c", &[0][]const u8{}); exe.setBuildMode(mode); exe.linkLibC(); diff --git a/test/link/macho/dead_strip_dylibs/main.c b/test/link/macho/dead_strip_dylibs/main.c index 836d1b1cc1..06668f5522 100644 --- a/test/link/macho/dead_strip_dylibs/main.c +++ b/test/link/macho/dead_strip_dylibs/main.c @@ -1,10 +1,11 @@ #include -int main() { +int main(int argc, char* argv[]) { if (objc_getClass("NSObject") == 0) { return -1; } if (objc_getClass("NSApplication") == 0) { return -2; } + return 0; } diff --git a/test/link/macho/dylib/main.c b/test/link/macho/dylib/main.c index be1647ddad..941903f219 100644 --- a/test/link/macho/dylib/main.c +++ b/test/link/macho/dylib/main.c @@ -3,7 +3,7 @@ char* hello(); extern char world[]; -int main() { +int main(int argc, char* argv[]) { printf("%s %s", hello(), world); return 0; } diff --git a/test/link/macho/needed_framework/build.zig b/test/link/macho/needed_framework/build.zig new file mode 100644 index 0000000000..4315935941 --- /dev/null +++ b/test/link/macho/needed_framework/build.zig @@ -0,0 +1,27 @@ +const std = @import("std"); +const Builder = std.build.Builder; +const LibExeObjectStep = std.build.LibExeObjStep; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + + const test_step = b.step("test", "Test the program"); + test_step.dependOn(b.getInstallStep()); + + // -dead_strip_dylibs + // -needed_framework Cocoa + const exe = b.addExecutable("test", null); + exe.addCSourceFile("main.c", &[0][]const u8{}); + exe.setBuildMode(mode); + exe.linkLibC(); + exe.linkFrameworkNeeded("Cocoa"); + exe.dead_strip_dylibs = true; + + const check = exe.checkObject(.macho); + check.checkStart("cmd LOAD_DYLIB"); + check.checkNext("name {*}Cocoa"); + test_step.dependOn(&check.step); + + const run_cmd = exe.run(); + test_step.dependOn(&run_cmd.step); +} diff --git a/test/link/macho/needed_framework/main.c b/test/link/macho/needed_framework/main.c new file mode 100644 index 0000000000..ca68d24cc7 --- /dev/null +++ b/test/link/macho/needed_framework/main.c @@ -0,0 +1,3 @@ +int main(int argc, char* argv[]) { + return 0; +} diff --git a/test/link/macho/needed_l/a.c b/test/link/macho/needed_l/a.c new file mode 100644 index 0000000000..4bcf8c9786 --- /dev/null +++ b/test/link/macho/needed_l/a.c @@ -0,0 +1 @@ +int a = 42; diff --git a/test/link/macho/needed_l/build.zig b/test/link/macho/needed_l/build.zig new file mode 100644 index 0000000000..708a09dc32 --- /dev/null +++ b/test/link/macho/needed_l/build.zig @@ -0,0 +1,35 @@ +const std = @import("std"); +const Builder = std.build.Builder; +const LibExeObjectStep = std.build.LibExeObjStep; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + + const test_step = b.step("test", "Test the program"); + test_step.dependOn(b.getInstallStep()); + + const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); + dylib.setBuildMode(mode); + dylib.addCSourceFile("a.c", &.{}); + dylib.linkLibC(); + dylib.install(); + + // -dead_strip_dylibs + // -needed-la + const exe = b.addExecutable("test", null); + exe.addCSourceFile("main.c", &[0][]const u8{}); + exe.setBuildMode(mode); + exe.linkLibC(); + exe.linkSystemLibraryNeeded("a"); + exe.addLibraryPath(b.pathFromRoot("zig-out/lib")); + exe.addRPath(b.pathFromRoot("zig-out/lib")); + exe.dead_strip_dylibs = true; + + const check = exe.checkObject(.macho); + check.checkStart("cmd LOAD_DYLIB"); + check.checkNext("name @rpath/liba.dylib"); + test_step.dependOn(&check.step); + + const run_cmd = exe.run(); + test_step.dependOn(&run_cmd.step); +} diff --git a/test/link/macho/needed_l/main.c b/test/link/macho/needed_l/main.c new file mode 100644 index 0000000000..ca68d24cc7 --- /dev/null +++ b/test/link/macho/needed_l/main.c @@ -0,0 +1,3 @@ +int main(int argc, char* argv[]) { + return 0; +} diff --git a/test/link/macho/search_strategy/main.c b/test/link/macho/search_strategy/main.c index be1647ddad..941903f219 100644 --- a/test/link/macho/search_strategy/main.c +++ b/test/link/macho/search_strategy/main.c @@ -3,7 +3,7 @@ char* hello(); extern char world[]; -int main() { +int main(int argc, char* argv[]) { printf("%s %s", hello(), world); return 0; } From 3b19869853282546fe290a63fc447ae88e713031 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 27 Jun 2022 14:31:54 -0700 Subject: [PATCH 1968/2031] Sema: honor the --test-filter flag --- src/Module.zig | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index bcf6491ce6..bdf206490d 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4607,6 +4607,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi DeclAdapter{ .mod = mod }, Namespace.DeclContext{ .module = mod }, ); + const comp = mod.comp; if (!gop.found_existing) { const new_decl_index = try mod.allocateNewDecl(namespace, decl_node, iter.parent_decl.src_scope); const new_decl = mod.declPtr(new_decl_index); @@ -4625,7 +4626,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi 1 => blk: { // test decl with no name. Skip the part where we check against // the test name filter. - if (!mod.comp.bin_file.options.is_test) break :blk false; + if (!comp.bin_file.options.is_test) break :blk false; if (decl_pkg != mod.main_pkg) { if (!mod.main_pkg_in_std) break :blk false; const std_pkg = mod.main_pkg.table.get("std").?; @@ -4636,19 +4637,23 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi }, else => blk: { if (!is_named_test) break :blk false; - if (!mod.comp.bin_file.options.is_test) break :blk false; + if (!comp.bin_file.options.is_test) break :blk false; if (decl_pkg != mod.main_pkg) { if (!mod.main_pkg_in_std) break :blk false; const std_pkg = mod.main_pkg.table.get("std").?; if (std_pkg != decl_pkg) break :blk false; } - // TODO check the name against --test-filter + if (comp.test_filter) |test_filter| { + if (mem.indexOf(u8, decl_name, test_filter) == null) { + break :blk false; + } + } try mod.test_functions.put(gpa, new_decl_index, {}); break :blk true; }, }; if (want_analysis) { - mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl_index }); + comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl_index }); } new_decl.is_pub = is_pub; new_decl.is_exported = is_exported; @@ -4675,24 +4680,24 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi decl.has_linksection_or_addrspace = has_linksection_or_addrspace; decl.zir_decl_index = @intCast(u32, decl_sub_index); if (decl.getFunction()) |_| { - switch (mod.comp.bin_file.tag) { + switch (comp.bin_file.tag) { .coff => { // TODO Implement for COFF }, .elf => if (decl.fn_link.elf.len != 0) { // TODO Look into detecting when this would be unnecessary by storing enough state // in `Decl` to notice that the line number did not change. - mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); + comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); }, .macho => if (decl.fn_link.macho.len != 0) { // TODO Look into detecting when this would be unnecessary by storing enough state // in `Decl` to notice that the line number did not change. - mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); + comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); }, .plan9 => { // TODO Look into detecting when this would be unnecessary by storing enough state // in `Decl` to notice that the line number did not change. - mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); + comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); }, .c, .wasm, .spirv, .nvptx => {}, } From 8d8a5f973314cb692d543773db8d59a9001b91ad Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 27 Jun 2022 16:02:41 -0700 Subject: [PATCH 1969/2031] LLVM: support calls to varargs functions closes #11944 --- src/codegen/llvm.zig | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index bf09ec0f35..29e25f1079 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4197,7 +4197,7 @@ pub const FuncGen = struct { } var it = iterateParamTypes(self.dg, fn_info); - while (it.next()) |lowering| switch (lowering) { + while (it.nextCall(self, args)) |lowering| switch (lowering) { .no_bits => continue, .byval => { const arg = args[it.zig_index - 1]; @@ -9004,10 +9004,26 @@ const ParamTypeIterator = struct { slice, }; - fn next(it: *ParamTypeIterator) ?Lowering { + pub fn next(it: *ParamTypeIterator) ?Lowering { if (it.zig_index >= it.fn_info.param_types.len) return null; - const ty = it.fn_info.param_types[it.zig_index]; + return nextInner(it, ty); + } + + /// `airCall` uses this instead of `next` so that it can take into account variadic functions. + pub fn nextCall(it: *ParamTypeIterator, fg: *FuncGen, args: []const Air.Inst.Ref) ?Lowering { + if (it.zig_index >= it.fn_info.param_types.len) { + if (it.zig_index >= args.len) { + return null; + } else { + return nextInner(it, fg.air.typeOf(args[it.zig_index])); + } + } else { + return nextInner(it, it.fn_info.param_types[it.zig_index]); + } + } + + fn nextInner(it: *ParamTypeIterator, ty: Type) ?Lowering { if (!ty.hasRuntimeBitsIgnoreComptime()) { it.zig_index += 1; return .no_bits; From 3c1daf951cf72e454de18e70f84b9a896aa17dd0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 27 Jun 2022 17:12:45 -0700 Subject: [PATCH 1970/2031] LLVM: fix invalid IR on `@returnAddress` of wasm/bpf see #11946 --- lib/std/x/os/net.zig | 2 +- src/codegen/llvm.zig | 11 +++++++++-- src/stage1/codegen.cpp | 4 ++-- src/target.zig | 8 ++++++++ 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/lib/std/x/os/net.zig b/lib/std/x/os/net.zig index a7cc8fbc4a..98d63969b9 100644 --- a/lib/std/x/os/net.zig +++ b/lib/std/x/os/net.zig @@ -433,7 +433,7 @@ pub const IPv6 = extern struct { '0'...'9' => fmt.parseInt(u32, scope_id_slice, 10), else => resolveScopeId(scope_id_slice) catch |err| switch (err) { error.InterfaceNotFound => return error.InterfaceNotFound, - else => err, + else => |e| return e, }, } catch return error.UnknownScopeId; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 29e25f1079..2b320923ad 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -7212,11 +7212,17 @@ pub const FuncGen = struct { fn airRetAddr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; + const llvm_usize = try self.dg.lowerType(Type.usize); + const target = self.dg.module.getTarget(); + if (!target_util.supportsReturnAddress(target)) { + // https://github.com/ziglang/zig/issues/11946 + return llvm_usize.constNull(); + } + const llvm_i32 = self.context.intType(32); const llvm_fn = self.getIntrinsic("llvm.returnaddress", &.{}); const params = [_]*const llvm.Value{llvm_i32.constNull()}; const ptr_val = self.builder.buildCall(llvm_fn, ¶ms, params.len, .Fast, .Auto, ""); - const llvm_usize = try self.dg.lowerType(Type.usize); return self.builder.buildPtrToInt(ptr_val, llvm_usize, ""); } @@ -8021,7 +8027,6 @@ pub const FuncGen = struct { assert(union_obj.haveFieldTypes()); const field = union_obj.fields.values()[extra.field_index]; const field_llvm_ty = try self.dg.lowerType(field.ty); - const tag_llvm_ty = try self.dg.lowerType(union_obj.tag_ty); const field_size = field.ty.abiSize(target); const field_align = field.normalAlignment(target); @@ -8044,6 +8049,7 @@ pub const FuncGen = struct { const fields: [1]*const llvm.Type = .{payload}; break :t self.context.structType(&fields, fields.len, .False); } + const tag_llvm_ty = try self.dg.lowerType(union_obj.tag_ty); var fields: [3]*const llvm.Type = undefined; var fields_len: c_uint = 2; if (layout.tag_align >= layout.payload_align) { @@ -8100,6 +8106,7 @@ pub const FuncGen = struct { index_type.constInt(@boolToInt(layout.tag_align < layout.payload_align), .False), }; const field_ptr = self.builder.buildInBoundsGEP(casted_ptr, &indices, indices.len, ""); + const tag_llvm_ty = try self.dg.lowerType(union_obj.tag_ty); const llvm_tag = tag_llvm_ty.constInt(extra.field_index, .False); const store_inst = self.builder.buildStore(llvm_tag, field_ptr); store_inst.setAlignment(union_obj.tag_ty.abiAlignment(target)); diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index d6fb6d4753..318a90732c 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -6764,8 +6764,8 @@ static LLVMValueRef ir_render_return_address(CodeGen *g, Stage1Air *executable, Stage1AirInstReturnAddress *instruction) { if ((target_is_wasm(g->zig_target) && g->zig_target->os != OsEmscripten) || target_is_bpf(g->zig_target)) { - // I got this error from LLVM 10: - // "Non-Emscripten WebAssembly hasn't implemented __builtin_return_address" + // LLVM 13 reports "Non-Emscripten WebAssembly hasn't implemented __builtin_return_address" + // https://github.com/ziglang/zig/issues/11946 return LLVMConstNull(get_llvm_type(g, instruction->base.value->type)); } diff --git a/src/target.zig b/src/target.zig index 14af2675d2..93c179b7f0 100644 --- a/src/target.zig +++ b/src/target.zig @@ -283,6 +283,14 @@ pub fn supportsStackProbing(target: std.Target) bool { (target.cpu.arch == .i386 or target.cpu.arch == .x86_64); } +pub fn supportsReturnAddress(target: std.Target) bool { + return switch (target.cpu.arch) { + .wasm32, .wasm64 => target.os.tag == .emscripten, + .bpfel, .bpfeb => false, + else => true, + }; +} + pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType { return switch (os_tag) { .freestanding, .other, .opencl, .glsl450, .vulkan, .plan9 => .UnknownOS, From df1f401cf0e997ed289e14f69ab1114e839b56ee Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 27 Jun 2022 18:26:50 -0700 Subject: [PATCH 1971/2031] std.x.os.net: make error set consistent across targets --- lib/std/x/os/net.zig | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/std/x/os/net.zig b/lib/std/x/os/net.zig index 98d63969b9..d9eb910b79 100644 --- a/lib/std/x/os/net.zig +++ b/lib/std/x/os/net.zig @@ -9,10 +9,25 @@ const testing = std.testing; const native_os = builtin.os; const have_ifnamesize = @hasDecl(os.system, "IFNAMESIZE"); +pub const ResolveScopeIdError = error{ + NameTooLong, + PermissionDenied, + AddressFamilyNotSupported, + ProtocolFamilyNotAvailable, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + SystemResources, + ProtocolNotSupported, + SocketTypeNotSupported, + InterfaceNotFound, + FileSystem, + Unexpected, +}; + /// Resolves a network interface name into a scope/zone ID. It returns /// an error if either resolution fails, or if the interface name is /// too long. -pub fn resolveScopeId(name: []const u8) !u32 { +pub fn resolveScopeId(name: []const u8) ResolveScopeIdError!u32 { if (have_ifnamesize) { if (name.len >= os.IFNAMESIZE) return error.NameTooLong; @@ -433,7 +448,7 @@ pub const IPv6 = extern struct { '0'...'9' => fmt.parseInt(u32, scope_id_slice, 10), else => resolveScopeId(scope_id_slice) catch |err| switch (err) { error.InterfaceNotFound => return error.InterfaceNotFound, - else => |e| return e, + else => err, }, } catch return error.UnknownScopeId; From 0b8bd9b2b4f609fb4ae7d31da7e7a0fd4f5ad987 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 27 Jun 2022 18:27:06 -0700 Subject: [PATCH 1972/2031] std.os.linux.clone: upgrade to stage2 fn ptr semantics --- lib/std/os/linux/arm64.zig | 7 ++++++- lib/std/os/linux/i386.zig | 7 ++++++- lib/std/os/linux/mips.zig | 7 ++++++- lib/std/os/linux/powerpc.zig | 7 ++++++- lib/std/os/linux/powerpc64.zig | 7 ++++++- lib/std/os/linux/riscv64.zig | 7 ++++++- lib/std/os/linux/sparc64.zig | 7 ++++++- 7 files changed, 42 insertions(+), 7 deletions(-) diff --git a/lib/std/os/linux/arm64.zig b/lib/std/os/linux/arm64.zig index dc6c7077ba..8f928cab36 100644 --- a/lib/std/os/linux/arm64.zig +++ b/lib/std/os/linux/arm64.zig @@ -98,8 +98,13 @@ pub fn syscall6( ); } +const CloneFn = switch (@import("builtin").zig_backend) { + .stage1 => fn (arg: usize) callconv(.C) u8, + else => *const fn (arg: usize) callconv(.C) u8, +}; + /// This matches the libc clone function. -pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; +pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; pub const restore = restore_rt; diff --git a/lib/std/os/linux/i386.zig b/lib/std/os/linux/i386.zig index a85f6c713b..9ca127eb0c 100644 --- a/lib/std/os/linux/i386.zig +++ b/lib/std/os/linux/i386.zig @@ -118,8 +118,13 @@ pub fn socketcall(call: usize, args: [*]usize) usize { ); } +const CloneFn = switch (@import("builtin").zig_backend) { + .stage1 => fn (arg: usize) callconv(.C) u8, + else => *const fn (arg: usize) callconv(.C) u8, +}; + /// This matches the libc clone function. -pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; +pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; pub fn restore() callconv(.Naked) void { return asm volatile ("int $0x80" diff --git a/lib/std/os/linux/mips.zig b/lib/std/os/linux/mips.zig index 4341949b2f..ee0aff281c 100644 --- a/lib/std/os/linux/mips.zig +++ b/lib/std/os/linux/mips.zig @@ -190,8 +190,13 @@ pub fn syscall7( ); } +const CloneFn = switch (@import("builtin").zig_backend) { + .stage1 => fn (arg: usize) callconv(.C) u8, + else => *const fn (arg: usize) callconv(.C) u8, +}; + /// This matches the libc clone function. -pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; +pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; pub fn restore() callconv(.Naked) void { return asm volatile ("syscall" diff --git a/lib/std/os/linux/powerpc.zig b/lib/std/os/linux/powerpc.zig index c25f520ba2..d0601f858b 100644 --- a/lib/std/os/linux/powerpc.zig +++ b/lib/std/os/linux/powerpc.zig @@ -126,8 +126,13 @@ pub fn syscall6( ); } +const CloneFn = switch (@import("builtin").zig_backend) { + .stage1 => fn (arg: usize) callconv(.C) u8, + else => *const fn (arg: usize) callconv(.C) u8, +}; + /// This matches the libc clone function. -pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; +pub extern fn clone(func: CloneFn, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; pub const restore = restore_rt; diff --git a/lib/std/os/linux/powerpc64.zig b/lib/std/os/linux/powerpc64.zig index 221fd935f2..329674cd72 100644 --- a/lib/std/os/linux/powerpc64.zig +++ b/lib/std/os/linux/powerpc64.zig @@ -126,8 +126,13 @@ pub fn syscall6( ); } +const CloneFn = switch (@import("builtin").zig_backend) { + .stage1 => fn (arg: usize) callconv(.C) u8, + else => *const fn (arg: usize) callconv(.C) u8, +}; + /// This matches the libc clone function. -pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; +pub extern fn clone(func: CloneFn, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; pub const restore = restore_rt; diff --git a/lib/std/os/linux/riscv64.zig b/lib/std/os/linux/riscv64.zig index 079d501c32..dbf22e0aa4 100644 --- a/lib/std/os/linux/riscv64.zig +++ b/lib/std/os/linux/riscv64.zig @@ -95,7 +95,12 @@ pub fn syscall6( ); } -pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; +const CloneFn = switch (@import("builtin").zig_backend) { + .stage1 => fn (arg: usize) callconv(.C) u8, + else => *const fn (arg: usize) callconv(.C) u8, +}; + +pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; pub const restore = restore_rt; diff --git a/lib/std/os/linux/sparc64.zig b/lib/std/os/linux/sparc64.zig index d7b74d95eb..6e1792ae0a 100644 --- a/lib/std/os/linux/sparc64.zig +++ b/lib/std/os/linux/sparc64.zig @@ -178,8 +178,13 @@ pub fn syscall6( ); } +const CloneFn = switch (@import("builtin").zig_backend) { + .stage1 => fn (arg: usize) callconv(.C) u8, + else => *const fn (arg: usize) callconv(.C) u8, +}; + /// This matches the libc clone function. -pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; +pub extern fn clone(func: CloneFn, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; pub const restore = restore_rt; From a71d00a4d504edfdb09cd169d29ca1bbc0b909c4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 27 Jun 2022 19:05:51 -0700 Subject: [PATCH 1973/2031] std.crypto.25519.field: avoid excessive inlining This valid zig code produces reasonable LLVM IR, however, on the wasm32-wasi target, when using the wasmtime runtime, the number of locals of the `isSquare` function exceeds 50000, causing wasmtime to refuse to execute the binary. The `inline` keyword in Zig is intended to be used only where it is semantically necessary; not as an optimization hint. Otherwise, this may produce unwanted binary bloat for the -OReleaseSmall use case. In the future, it is possible that we may end up with both `inline` keyword, which operates as it does in status quo, and additionally `callconv(.inline_hint)` which has no semantic impact, but may be observed by optimization passes. In this commit, I also cleaned up `isSquare` by eliminating an unnecessary mutable variable, replacing it with several local constants. Closes #11947. --- lib/std/crypto/25519/field.zig | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/std/crypto/25519/field.zig b/lib/std/crypto/25519/field.zig index ce021ffb2a..1a786e0c32 100644 --- a/lib/std/crypto/25519/field.zig +++ b/lib/std/crypto/25519/field.zig @@ -341,7 +341,7 @@ pub const Fe = struct { } /// Square a field element `n` times - inline fn sqn(a: Fe, comptime n: comptime_int) Fe { + fn sqn(a: Fe, n: usize) Fe { var i: usize = 0; var fe = a; while (i < n) : (i += 1) { @@ -390,13 +390,12 @@ pub const Fe = struct { const _11 = a.mul(a.sq()); const _1111 = _11.mul(_11.sq().sq()); const _11111111 = _1111.mul(_1111.sq().sq().sq().sq()); - var t = _11111111.sqn(2).mul(_11); - const u = t; - t = t.sqn(10).mul(u).sqn(10).mul(u); - t = t.sqn(30).mul(t); - t = t.sqn(60).mul(t); - t = t.sqn(120).mul(t).sqn(10).mul(u).sqn(3).mul(_11).sq(); - return @bitCast(bool, @truncate(u1, ~(t.toBytes()[1] & 1))); + const u = _11111111.sqn(2).mul(_11); + const t = u.sqn(10).mul(u).sqn(10).mul(u); + const t2 = t.sqn(30).mul(t); + const t3 = t2.sqn(60).mul(t2); + const t4 = t3.sqn(120).mul(t3).sqn(10).mul(u).sqn(3).mul(_11).sq(); + return @bitCast(bool, @truncate(u1, ~(t4.toBytes()[1] & 1))); } fn uncheckedSqrt(x2: Fe) Fe { From 1188415f4cb73440fde05047cd8e6b79a2255efa Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 28 Jun 2022 07:30:03 +0200 Subject: [PATCH 1974/2031] cli: typo `--needed_library` should be `-needed_library` on macos --- src/main.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.zig b/src/main.zig index cbfd403352..749534416f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -965,7 +965,7 @@ fn buildOutputType( try system_libs.put(next_arg, .{ .needed = false }); } else if (mem.eql(u8, arg, "--needed-library") or mem.eql(u8, arg, "-needed-l") or - mem.eql(u8, arg, "--needed_library")) + mem.eql(u8, arg, "-needed_library")) { const next_arg = args_iter.next() orelse { fatal("expected parameter after {s}", .{arg}); From f353b59efac62dd8bc2ec966e6ea46131a64b19a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 27 Jun 2022 23:56:45 +0200 Subject: [PATCH 1975/2031] macho: discriminate between normal and weak dylibs Parse `-weak-lx` and `-weak_framework x` in the CLI. --- lib/std/macho.zig | 4 +- src/link.zig | 1 + src/link/MachO.zig | 113 ++++++++++++++++++++++++++------------- src/link/MachO/Dylib.zig | 26 ++++++--- src/main.zig | 41 +++++++++++--- 5 files changed, 134 insertions(+), 51 deletions(-) diff --git a/lib/std/macho.zig b/lib/std/macho.zig index 1f8b7e8f33..2ca6fe9d0a 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -2085,11 +2085,13 @@ pub fn GenericCommandWithData(comptime Cmd: type) type { pub fn createLoadDylibCommand( allocator: Allocator, + cmd_id: LC, name: []const u8, timestamp: u32, current_version: u32, compatibility_version: u32, ) !GenericCommandWithData(dylib_command) { + assert(cmd_id == .LOAD_DYLIB or cmd_id == .LOAD_WEAK_DYLIB or cmd_id == .REEXPORT_DYLIB or cmd_id == .ID_DYLIB); const cmdsize = @intCast(u32, mem.alignForwardGeneric( u64, @sizeOf(dylib_command) + name.len + 1, // +1 for nul @@ -2097,7 +2099,7 @@ pub fn createLoadDylibCommand( )); var dylib_cmd = emptyGenericCommandWithData(dylib_command{ - .cmd = .LOAD_DYLIB, + .cmd = cmd_id, .cmdsize = cmdsize, .dylib = .{ .name = @sizeOf(dylib_command), diff --git a/src/link.zig b/src/link.zig index c3d1d216c0..da6e8c53ed 100644 --- a/src/link.zig +++ b/src/link.zig @@ -21,6 +21,7 @@ const TypedValue = @import("TypedValue.zig"); pub const SystemLib = struct { needed: bool = false, + weak: bool = false, }; pub const CacheMode = enum { incremental, whole }; diff --git a/src/link/MachO.zig b/src/link/MachO.zig index d660d2ce7e..a406923329 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -52,6 +52,11 @@ pub const SearchStrategy = enum { dylibs_first, }; +const SystemLib = struct { + needed: bool = false, + weak: bool = false, +}; + base: File, /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. @@ -768,7 +773,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No } // Shared and static libraries passed via `-l` flag. - var candidate_libs = std.StringArrayHashMap(Compilation.SystemLib).init(arena); + var candidate_libs = std.StringArrayHashMap(SystemLib).init(arena); const system_lib_names = self.base.options.system_libs.keys(); for (system_lib_names) |system_lib_name| { @@ -781,7 +786,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No } const system_lib_info = self.base.options.system_libs.get(system_lib_name).?; - try candidate_libs.put(system_lib_name, system_lib_info); + try candidate_libs.put(system_lib_name, .{ + .needed = system_lib_info.needed, + .weak = system_lib_info.weak, + }); } var lib_dirs = std.ArrayList([]const u8).init(arena); @@ -793,7 +801,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No } } - var libs = std.StringArrayHashMap(Compilation.SystemLib).init(arena); + var libs = std.StringArrayHashMap(SystemLib).init(arena); // Assume ld64 default -search_paths_first if no strategy specified. const search_strategy = self.base.options.search_strategy orelse .paths_first; @@ -890,7 +898,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No for (framework_dirs.items) |dir| { for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { if (try resolveFramework(arena, dir, f_name, ext)) |full_path| { - try libs.put(full_path, self.base.options.frameworks.get(f_name).?); + const info = self.base.options.frameworks.get(f_name).?; + try libs.put(full_path, .{ + .needed = info.needed, + .weak = info.weak, + }); continue :outer; } } @@ -1026,9 +1038,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try argv.append("-lc"); for (self.base.options.system_libs.keys()) |l_name| { - const needed = self.base.options.system_libs.get(l_name).?.needed; - const arg = if (needed) + const info = self.base.options.system_libs.get(l_name).?; + const arg = if (info.needed) try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name}) + else if (info.weak) + try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name}) else try std.fmt.allocPrint(arena, "-l{s}", .{l_name}); try argv.append(arg); @@ -1039,9 +1053,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No } for (self.base.options.frameworks.keys()) |framework| { - const needed = self.base.options.frameworks.get(framework).?.needed; - const arg = if (needed) + const info = self.base.options.frameworks.get(framework).?; + const arg = if (info.needed) try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework}) + else if (info.weak) + try std.fmt.allocPrint(arena, "-weak_framework {s}", .{framework}) else try std.fmt.allocPrint(arena, "-framework {s}", .{framework}); try argv.append(arg); @@ -1063,7 +1079,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No Compilation.dump_argv(argv.items); } - var dependent_libs = std.fifo.LinearFifo(Dylib.Id, .Dynamic).init(self.base.allocator); + var dependent_libs = std.fifo.LinearFifo(struct { + id: Dylib.Id, + parent: u16, + }, .Dynamic).init(self.base.allocator); defer dependent_libs.deinit(); try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs); try self.parseAndForceLoadStaticArchives(must_link_archives.keys()); @@ -1389,13 +1408,18 @@ const ParseDylibError = error{ const DylibCreateOpts = struct { syslibroot: ?[]const u8, - dependent_libs: *std.fifo.LinearFifo(Dylib.Id, .Dynamic), id: ?Dylib.Id = null, - is_dependent: bool = false, - is_needed: bool = false, + dependent: bool = false, + needed: bool = false, + weak: bool = false, }; -pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDylibError!bool { +pub fn parseDylib( + self: *MachO, + path: []const u8, + dependent_libs: anytype, + opts: DylibCreateOpts, +) ParseDylibError!bool { const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) { error.FileNotFound => return false, else => |e| return e, @@ -1405,12 +1429,19 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy const name = try self.base.allocator.dupe(u8, path); errdefer self.base.allocator.free(name); + const dylib_id = @intCast(u16, self.dylibs.items.len); var dylib = Dylib{ .name = name, .file = file, + .weak = opts.weak, }; - dylib.parse(self.base.allocator, self.base.options.target, opts.dependent_libs) catch |err| switch (err) { + dylib.parse( + self.base.allocator, + self.base.options.target, + dylib_id, + dependent_libs, + ) catch |err| switch (err) { error.EndOfStream, error.NotDylib => { try file.seekTo(0); @@ -1420,7 +1451,13 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy }; defer lib_stub.deinit(); - try dylib.parseFromStub(self.base.allocator, self.base.options.target, lib_stub, opts.dependent_libs); + try dylib.parseFromStub( + self.base.allocator, + self.base.options.target, + lib_stub, + dylib_id, + dependent_libs, + ); }, else => |e| return e, }; @@ -1438,13 +1475,12 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy } } - const dylib_id = @intCast(u16, self.dylibs.items.len); try self.dylibs.append(self.base.allocator, dylib); try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id); const should_link_dylib_even_if_unreachable = blk: { - if (self.base.options.dead_strip_dylibs and !opts.is_needed) break :blk false; - break :blk !(opts.is_dependent or self.referenced_dylibs.contains(dylib_id)); + if (self.base.options.dead_strip_dylibs and !opts.needed) break :blk false; + break :blk !(opts.dependent or self.referenced_dylibs.contains(dylib_id)); }; if (should_link_dylib_even_if_unreachable) { @@ -1467,9 +1503,8 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const if (try self.parseObject(full_path)) continue; if (try self.parseArchive(full_path, false)) continue; - if (try self.parseDylib(full_path, .{ + if (try self.parseDylib(full_path, dependent_libs, .{ .syslibroot = syslibroot, - .dependent_libs = dependent_libs, })) continue; log.warn("unknown filetype for positional input file: '{s}'", .{file_name}); @@ -1494,17 +1529,17 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi fn parseLibs( self: *MachO, lib_names: []const []const u8, - lib_infos: []const Compilation.SystemLib, + lib_infos: []const SystemLib, syslibroot: ?[]const u8, dependent_libs: anytype, ) !void { for (lib_names) |lib, i| { const lib_info = lib_infos[i]; log.debug("parsing lib path '{s}'", .{lib}); - if (try self.parseDylib(lib, .{ + if (try self.parseDylib(lib, dependent_libs, .{ .syslibroot = syslibroot, - .dependent_libs = dependent_libs, - .is_needed = lib_info.needed, + .needed = lib_info.needed, + .weak = lib_info.weak, })) continue; if (try self.parseArchive(lib, false)) continue; @@ -1522,20 +1557,21 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any const arena = arena_alloc.allocator(); defer arena_alloc.deinit(); - while (dependent_libs.readItem()) |*id| { - defer id.deinit(self.base.allocator); + while (dependent_libs.readItem()) |*dep_id| { + defer dep_id.id.deinit(self.base.allocator); - if (self.dylibs_map.contains(id.name)) continue; + if (self.dylibs_map.contains(dep_id.id.name)) continue; + const weak = self.dylibs.items[dep_id.parent].weak; const has_ext = blk: { - const basename = fs.path.basename(id.name); + const basename = fs.path.basename(dep_id.id.name); break :blk mem.lastIndexOfScalar(u8, basename, '.') != null; }; - const extension = if (has_ext) fs.path.extension(id.name) else ""; + const extension = if (has_ext) fs.path.extension(dep_id.id.name) else ""; const without_ext = if (has_ext) blk: { - const index = mem.lastIndexOfScalar(u8, id.name, '.') orelse unreachable; - break :blk id.name[0..index]; - } else id.name; + const index = mem.lastIndexOfScalar(u8, dep_id.id.name, '.') orelse unreachable; + break :blk dep_id.id.name[0..index]; + } else dep_id.id.name; for (&[_][]const u8{ extension, ".tbd" }) |ext| { const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ without_ext, ext }); @@ -1543,15 +1579,15 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any log.debug("trying dependency at fully resolved path {s}", .{full_path}); - const did_parse_successfully = try self.parseDylib(full_path, .{ - .id = id.*, + const did_parse_successfully = try self.parseDylib(full_path, dependent_libs, .{ + .id = dep_id.id, .syslibroot = syslibroot, - .is_dependent = true, - .dependent_libs = dependent_libs, + .dependent = true, + .weak = weak, }); if (did_parse_successfully) break; } else { - log.warn("unable to resolve dependency {s}", .{id.name}); + log.warn("unable to resolve dependency {s}", .{dep_id.id.name}); } } } @@ -3441,6 +3477,7 @@ fn addLoadDylibLC(self: *MachO, id: u16) !void { const dylib_id = dylib.id orelse unreachable; var dylib_cmd = try macho.createLoadDylibCommand( self.base.allocator, + if (dylib.weak) .LOAD_WEAK_DYLIB else .LOAD_DYLIB, dylib_id.name, dylib_id.timestamp, dylib_id.current_version, @@ -4885,13 +4922,13 @@ fn populateMissingMetadata(self: *MachO) !void { std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 }; var dylib_cmd = try macho.createLoadDylibCommand( self.base.allocator, + .ID_DYLIB, install_name, 2, current_version.major << 16 | current_version.minor << 8 | current_version.patch, compat_version.major << 16 | compat_version.minor << 8 | compat_version.patch, ); errdefer dylib_cmd.deinit(self.base.allocator); - dylib_cmd.inner.cmd = .ID_DYLIB; try self.load_commands.append(self.base.allocator, .{ .dylib = dylib_cmd }); self.load_commands_dirty = true; } diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 801f810f80..ba519aac0a 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -30,6 +30,7 @@ dysymtab_cmd_index: ?u16 = null, id_cmd_index: ?u16 = null, id: ?Id = null, +weak: bool = false, /// Parsed symbol table represented as hash map of symbols' /// names. We can and should defer creating *Symbols until @@ -141,7 +142,13 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void { } } -pub fn parse(self: *Dylib, allocator: Allocator, target: std.Target, dependent_libs: anytype) !void { +pub fn parse( + self: *Dylib, + allocator: Allocator, + target: std.Target, + dylib_id: u16, + dependent_libs: anytype, +) !void { log.debug("parsing shared library '{s}'", .{self.name}); self.library_offset = try fat.getLibraryOffset(self.file.reader(), target); @@ -163,12 +170,18 @@ pub fn parse(self: *Dylib, allocator: Allocator, target: std.Target, dependent_l return error.MismatchedCpuArchitecture; } - try self.readLoadCommands(allocator, reader, dependent_libs); + try self.readLoadCommands(allocator, reader, dylib_id, dependent_libs); try self.parseId(allocator); try self.parseSymbols(allocator); } -fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, dependent_libs: anytype) !void { +fn readLoadCommands( + self: *Dylib, + allocator: Allocator, + reader: anytype, + dylib_id: u16, + dependent_libs: anytype, +) !void { const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0; try self.load_commands.ensureUnusedCapacity(allocator, self.header.?.ncmds); @@ -190,7 +203,7 @@ fn readLoadCommands(self: *Dylib, allocator: Allocator, reader: anytype, depende if (should_lookup_reexports) { // Parse install_name to dependent dylib. var id = try Id.fromLoadCommand(allocator, cmd.dylib); - try dependent_libs.writeItem(id); + try dependent_libs.writeItem(.{ .id = id, .parent = dylib_id }); } }, else => { @@ -338,6 +351,7 @@ pub fn parseFromStub( allocator: Allocator, target: std.Target, lib_stub: LibStub, + dylib_id: u16, dependent_libs: anytype, ) !void { if (lib_stub.inner.len == 0) return error.EmptyStubFile; @@ -417,7 +431,7 @@ pub fn parseFromStub( log.debug(" (found re-export '{s}')", .{lib}); var dep_id = try Id.default(allocator, lib); - try dependent_libs.writeItem(dep_id); + try dependent_libs.writeItem(.{ .id = dep_id, .parent = dylib_id }); } } } @@ -522,7 +536,7 @@ pub fn parseFromStub( log.debug(" (found re-export '{s}')", .{lib}); var dep_id = try Id.default(allocator, lib); - try dependent_libs.writeItem(dep_id); + try dependent_libs.writeItem(.{ .id = dep_id, .parent = dylib_id }); } } } diff --git a/src/main.zig b/src/main.zig index 749534416f..ae1b95a4aa 100644 --- a/src/main.zig +++ b/src/main.zig @@ -443,9 +443,12 @@ const usage_build_generic = \\ --subsystem [subsystem] (Windows) /SUBSYSTEM: to the linker \\ --stack [size] Override default stack size \\ --image-base [addr] Set base address for executable image + \\ -weak-l[lib] (Darwin) link against system library and mark it and all referenced symbols as weak + \\ -weak_library [lib] \\ -framework [name] (Darwin) link against framework \\ -needed_framework [name] (Darwin) link against framework (even if unused) \\ -needed_library [lib] (Darwin) link against system library (even if unused) + \\ -weak_framework [name] (Darwin) link against framework and mark it and all referenced symbols as weak \\ -F[dir] (Darwin) add search path for frameworks \\ -install_name=[value] (Darwin) add dylib's install name \\ --entitlements [path] (Darwin) add path to entitlements file for embedding in code signature @@ -916,7 +919,12 @@ fn buildOutputType( const path = args_iter.next() orelse { fatal("expected parameter after {s}", .{arg}); }; - try frameworks.put(gpa, path, .{ .needed = false }); + try frameworks.put(gpa, path, .{}); + } else if (mem.eql(u8, arg, "-weak_framework")) { + const path = args_iter.next() orelse { + fatal("expected parameter after {s}", .{arg}); + }; + try frameworks.put(gpa, path, .{ .weak = true }); } else if (mem.eql(u8, arg, "-needed_framework")) { const path = args_iter.next() orelse { fatal("expected parameter after {s}", .{arg}); @@ -962,7 +970,7 @@ fn buildOutputType( }; // We don't know whether this library is part of libc or libc++ until // we resolve the target, so we simply append to the list for now. - try system_libs.put(next_arg, .{ .needed = false }); + try system_libs.put(next_arg, .{}); } else if (mem.eql(u8, arg, "--needed-library") or mem.eql(u8, arg, "-needed-l") or mem.eql(u8, arg, "-needed_library")) @@ -971,6 +979,11 @@ fn buildOutputType( fatal("expected parameter after {s}", .{arg}); }; try system_libs.put(next_arg, .{ .needed = true }); + } else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) { + const next_arg = args_iter.next() orelse { + fatal("expected parameter after {s}", .{arg}); + }; + try system_libs.put(next_arg, .{ .weak = true }); } else if (mem.eql(u8, arg, "-D") or mem.eql(u8, arg, "-isystem") or mem.eql(u8, arg, "-I") or @@ -1300,9 +1313,11 @@ fn buildOutputType( } else if (mem.startsWith(u8, arg, "-l")) { // We don't know whether this library is part of libc or libc++ until // we resolve the target, so we simply append to the list for now. - try system_libs.put(arg["-l".len..], .{ .needed = false }); + try system_libs.put(arg["-l".len..], .{}); } else if (mem.startsWith(u8, arg, "-needed-l")) { try system_libs.put(arg["-needed-l".len..], .{ .needed = true }); + } else if (mem.startsWith(u8, arg, "-weak-l")) { + try system_libs.put(arg["-weak-l".len..], .{ .weak = true }); } else if (mem.startsWith(u8, arg, "-D") or mem.startsWith(u8, arg, "-I")) { @@ -1596,7 +1611,7 @@ fn buildOutputType( try clang_argv.appendSlice(it.other_args); }, .framework_dir => try framework_dirs.append(it.only_arg), - .framework => try frameworks.put(gpa, it.only_arg, .{ .needed = false }), + .framework => try frameworks.put(gpa, it.only_arg, .{}), .nostdlibinc => want_native_include_dirs = false, .strip => strip = true, .exec_model => { @@ -1879,12 +1894,18 @@ fn buildOutputType( ) catch |err| { fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; - } else if (mem.eql(u8, arg, "-framework") or mem.eql(u8, arg, "-weak_framework")) { + } else if (mem.eql(u8, arg, "-framework")) { i += 1; if (i >= linker_args.items.len) { fatal("expected linker arg after '{s}'", .{arg}); } - try frameworks.put(gpa, linker_args.items[i], .{ .needed = false }); + try frameworks.put(gpa, linker_args.items[i], .{}); + } else if (mem.eql(u8, arg, "-weak_framework")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{s}'", .{arg}); + } + try frameworks.put(gpa, linker_args.items[i], .{ .weak = true }); } else if (mem.eql(u8, arg, "-needed_framework")) { i += 1; if (i >= linker_args.items.len) { @@ -1897,6 +1918,14 @@ fn buildOutputType( fatal("expected linker arg after '{s}'", .{arg}); } try system_libs.put(linker_args.items[i], .{ .needed = true }); + } else if (mem.startsWith(u8, arg, "-weak-l")) { + try system_libs.put(arg["-weak-l".len..], .{ .weak = true }); + } else if (mem.eql(u8, arg, "-weak_library")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{s}'", .{arg}); + } + try system_libs.put(linker_args.items[i], .{ .weak = true }); } else if (mem.eql(u8, arg, "-compatibility_version")) { i += 1; if (i >= linker_args.items.len) { From cd5dcfbf41dc6f40a853b2a207bd3ab0ea0dc0ee Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 28 Jun 2022 09:12:43 +0200 Subject: [PATCH 1976/2031] macho: annotate weak imports when linking with weak lib/framework --- src/link/MachO.zig | 18 ++++++++++++++++-- src/link/MachO/bind.zig | 5 +++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index a406923329..f5e1bf4b4d 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -3117,6 +3117,10 @@ fn resolveSymbolsInDylibs(self: *MachO) !void { undef.n_type |= macho.N_EXT; undef.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER; + if (dylib.weak) { + undef.n_desc |= macho.N_WEAK_REF; + } + if (self.unresolved.fetchSwapRemove(resolv.where_index)) |entry| outer_blk: { switch (entry.value) { .none => {}, @@ -5806,11 +5810,16 @@ fn writeDyldInfoData(self: *MachO) !void { }, .undef => { const bind_sym = self.undefs.items[resolv.where_index]; + var flags: u4 = 0; + if (bind_sym.weakRef()) { + flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT); + } try bind_pointers.append(.{ .offset = binding.offset + base_offset, .segment_id = match.seg, - .dylib_ordinal = @divExact(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER), + .dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER), .name = self.getString(bind_sym.n_strx), + .bind_flags = flags, }); }, } @@ -5828,11 +5837,16 @@ fn writeDyldInfoData(self: *MachO) !void { }, .undef => { const bind_sym = self.undefs.items[resolv.where_index]; + var flags: u4 = 0; + if (bind_sym.weakRef()) { + flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT); + } try lazy_bind_pointers.append(.{ .offset = binding.offset + base_offset, .segment_id = match.seg, - .dylib_ordinal = @divExact(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER), + .dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER), .name = self.getString(bind_sym.n_strx), + .bind_flags = flags, }); }, } diff --git a/src/link/MachO/bind.zig b/src/link/MachO/bind.zig index 14a5ba3e30..9e34581a23 100644 --- a/src/link/MachO/bind.zig +++ b/src/link/MachO/bind.zig @@ -7,6 +7,7 @@ pub const Pointer = struct { segment_id: u16, dylib_ordinal: ?i64 = null, name: ?[]const u8 = null, + bind_flags: u4 = 0, }; pub fn rebaseInfoSize(pointers: []const Pointer) !u64 { @@ -73,7 +74,7 @@ pub fn writeBindInfo(pointers: []const Pointer, writer: anytype) !void { } try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER)); - try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags. + try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | pointer.bind_flags); try writer.writeAll(pointer.name.?); try writer.writeByte(0); @@ -127,7 +128,7 @@ pub fn writeLazyBindInfo(pointers: []const Pointer, writer: anytype) !void { try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, pointer.dylib_ordinal.?))); } - try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags. + try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | pointer.bind_flags); try writer.writeAll(pointer.name.?); try writer.writeByte(0); From 20bd722464699bd11a05fe8e250f9f8086853cf2 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 28 Jun 2022 09:13:45 +0200 Subject: [PATCH 1977/2031] build: handle weakly imported libs and frameworks --- lib/std/build.zig | 72 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 11 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index ab4472d01e..96abc51fac 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1483,7 +1483,7 @@ pub const LibExeObjStep = struct { lib_paths: ArrayList([]const u8), rpaths: ArrayList([]const u8), framework_dirs: ArrayList([]const u8), - frameworks: StringHashMap(bool), + frameworks: StringHashMap(FrameworkLinkInfo), verbose_link: bool, verbose_cc: bool, emit_analysis: EmitOption = .default, @@ -1643,6 +1643,7 @@ pub const LibExeObjStep = struct { pub const SystemLib = struct { name: []const u8, needed: bool, + weak: bool, use_pkg_config: enum { /// Don't use pkg-config, just pass -lfoo where foo is name. no, @@ -1655,6 +1656,11 @@ pub const LibExeObjStep = struct { }, }; + const FrameworkLinkInfo = struct { + needed: bool = false, + weak: bool = false, + }; + pub const IncludeDir = union(enum) { raw_path: []const u8, raw_path_system: []const u8, @@ -1744,7 +1750,7 @@ pub const LibExeObjStep = struct { .kind = kind, .root_src = root_src, .name = name, - .frameworks = StringHashMap(bool).init(builder.allocator), + .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator), .step = Step.init(base_id, name, builder.allocator, make), .version = ver, .out_filename = undefined, @@ -1893,11 +1899,19 @@ pub const LibExeObjStep = struct { } pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void { - self.frameworks.put(self.builder.dupe(framework_name), false) catch unreachable; + self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable; } pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void { - self.frameworks.put(self.builder.dupe(framework_name), true) catch unreachable; + self.frameworks.put(self.builder.dupe(framework_name), .{ + .needed = true, + }) catch unreachable; + } + + pub fn linkFrameworkWeak(self: *LibExeObjStep, framework_name: []const u8) void { + self.frameworks.put(self.builder.dupe(framework_name), .{ + .weak = true, + }) catch unreachable; } /// Returns whether the library, executable, or object depends on a particular system library. @@ -1939,6 +1953,7 @@ pub const LibExeObjStep = struct { .system_lib = .{ .name = "c", .needed = false, + .weak = false, .use_pkg_config = .no, }, }) catch unreachable; @@ -1952,6 +1967,7 @@ pub const LibExeObjStep = struct { .system_lib = .{ .name = "c++", .needed = false, + .weak = false, .use_pkg_config = .no, }, }) catch unreachable; @@ -1977,6 +1993,7 @@ pub const LibExeObjStep = struct { .system_lib = .{ .name = self.builder.dupe(name), .needed = false, + .weak = false, .use_pkg_config = .no, }, }) catch unreachable; @@ -1989,6 +2006,20 @@ pub const LibExeObjStep = struct { .system_lib = .{ .name = self.builder.dupe(name), .needed = true, + .weak = false, + .use_pkg_config = .no, + }, + }) catch unreachable; + } + + /// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the + /// command line. Prefer to use `linkSystemLibraryWeak` instead. + pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void { + self.link_objects.append(.{ + .system_lib = .{ + .name = self.builder.dupe(name), + .needed = false, + .weak = true, .use_pkg_config = .no, }, }) catch unreachable; @@ -2001,6 +2032,7 @@ pub const LibExeObjStep = struct { .system_lib = .{ .name = self.builder.dupe(lib_name), .needed = false, + .weak = false, .use_pkg_config = .force, }, }) catch unreachable; @@ -2013,6 +2045,7 @@ pub const LibExeObjStep = struct { .system_lib = .{ .name = self.builder.dupe(lib_name), .needed = true, + .weak = false, .use_pkg_config = .force, }, }) catch unreachable; @@ -2115,14 +2148,21 @@ pub const LibExeObjStep = struct { } pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void { - self.linkSystemLibraryInner(name, false); + self.linkSystemLibraryInner(name, .{}); } pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void { - self.linkSystemLibraryInner(name, true); + self.linkSystemLibraryInner(name, .{ .needed = true }); } - fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, needed: bool) void { + pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void { + self.linkSystemLibraryInner(name, .{ .weak = true }); + } + + fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct { + needed: bool = false, + weak: bool = false, + }) void { if (isLibCLibrary(name)) { self.linkLibC(); return; @@ -2135,7 +2175,8 @@ pub const LibExeObjStep = struct { self.link_objects.append(.{ .system_lib = .{ .name = self.builder.dupe(name), - .needed = needed, + .needed = opts.needed, + .weak = opts.weak, .use_pkg_config = .yes, }, }) catch unreachable; @@ -2513,7 +2554,14 @@ pub const LibExeObjStep = struct { }, .system_lib => |system_lib| { - const prefix: []const u8 = if (system_lib.needed) "-needed-l" else "-l"; + const prefix: []const u8 = prefix: { + if (system_lib.needed) break :prefix "-needed-l"; + if (system_lib.weak) { + if (self.target.isDarwin()) break :prefix "-weak-l"; + warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`\n", .{}); + } + break :prefix "-l"; + }; switch (system_lib.use_pkg_config) { .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })), .yes, .force => { @@ -3018,9 +3066,11 @@ pub const LibExeObjStep = struct { var it = self.frameworks.iterator(); while (it.next()) |entry| { const name = entry.key_ptr.*; - const needed = entry.value_ptr.*; - if (needed) { + const info = entry.value_ptr.*; + if (info.needed) { zig_args.append("-needed_framework") catch unreachable; + } else if (info.weak) { + zig_args.append("-weak_framework") catch unreachable; } else { zig_args.append("-framework") catch unreachable; } From 075f5bc5ffdbe0a458a733d596a1fc4a74b8e1ab Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 28 Jun 2022 09:14:35 +0200 Subject: [PATCH 1978/2031] link-tests: test -weak-lx and -weak_framework x --- lib/std/build/CheckObjectStep.zig | 2 ++ test/link.zig | 11 ++++++- .../macho/{needed_l => needed_library}/a.c | 0 .../{needed_l => needed_library}/build.zig | 0 .../macho/{needed_l => needed_library}/main.c | 0 test/link/macho/weak_framework/build.zig | 24 ++++++++++++++ test/link/macho/weak_framework/main.c | 3 ++ test/link/macho/weak_library/a.c | 9 +++++ test/link/macho/weak_library/build.zig | 33 +++++++++++++++++++ test/link/macho/weak_library/main.c | 9 +++++ 10 files changed, 90 insertions(+), 1 deletion(-) rename test/link/macho/{needed_l => needed_library}/a.c (100%) rename test/link/macho/{needed_l => needed_library}/build.zig (100%) rename test/link/macho/{needed_l => needed_library}/main.c (100%) create mode 100644 test/link/macho/weak_framework/build.zig create mode 100644 test/link/macho/weak_framework/main.c create mode 100644 test/link/macho/weak_library/a.c create mode 100644 test/link/macho/weak_library/build.zig create mode 100644 test/link/macho/weak_library/main.c diff --git a/lib/std/build/CheckObjectStep.zig b/lib/std/build/CheckObjectStep.zig index 375e183231..c7e91bb7cb 100644 --- a/lib/std/build/CheckObjectStep.zig +++ b/lib/std/build/CheckObjectStep.zig @@ -408,6 +408,8 @@ const MachODumper = struct { .ID_DYLIB, .LOAD_DYLIB, + .LOAD_WEAK_DYLIB, + .REEXPORT_DYLIB, => { const dylib = lc.dylib.inner.dylib; try writer.writeByte('\n'); diff --git a/test/link.zig b/test/link.zig index 6878881a66..c578638ec3 100644 --- a/test/link.zig +++ b/test/link.zig @@ -45,7 +45,11 @@ pub fn addCases(cases: *tests.StandaloneContext) void { .requires_macos_sdk = true, }); - cases.addBuildFile("test/link/macho/needed_l/build.zig", .{ + cases.addBuildFile("test/link/macho/needed_library/build.zig", .{ + .build_modes = true, + }); + + cases.addBuildFile("test/link/macho/weak_library/build.zig", .{ .build_modes = true, }); @@ -54,6 +58,11 @@ pub fn addCases(cases: *tests.StandaloneContext) void { .requires_macos_sdk = true, }); + cases.addBuildFile("test/link/macho/weak_framework/build.zig", .{ + .build_modes = true, + .requires_macos_sdk = true, + }); + // Try to build and run an Objective-C executable. cases.addBuildFile("test/link/macho/objc/build.zig", .{ .build_modes = true, diff --git a/test/link/macho/needed_l/a.c b/test/link/macho/needed_library/a.c similarity index 100% rename from test/link/macho/needed_l/a.c rename to test/link/macho/needed_library/a.c diff --git a/test/link/macho/needed_l/build.zig b/test/link/macho/needed_library/build.zig similarity index 100% rename from test/link/macho/needed_l/build.zig rename to test/link/macho/needed_library/build.zig diff --git a/test/link/macho/needed_l/main.c b/test/link/macho/needed_library/main.c similarity index 100% rename from test/link/macho/needed_l/main.c rename to test/link/macho/needed_library/main.c diff --git a/test/link/macho/weak_framework/build.zig b/test/link/macho/weak_framework/build.zig new file mode 100644 index 0000000000..44675a15f8 --- /dev/null +++ b/test/link/macho/weak_framework/build.zig @@ -0,0 +1,24 @@ +const std = @import("std"); +const Builder = std.build.Builder; +const LibExeObjectStep = std.build.LibExeObjStep; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + + const test_step = b.step("test", "Test the program"); + test_step.dependOn(b.getInstallStep()); + + const exe = b.addExecutable("test", null); + exe.addCSourceFile("main.c", &[0][]const u8{}); + exe.setBuildMode(mode); + exe.linkLibC(); + exe.linkFrameworkWeak("Cocoa"); + + const check = exe.checkObject(.macho); + check.checkStart("cmd LOAD_WEAK_DYLIB"); + check.checkNext("name {*}Cocoa"); + test_step.dependOn(&check.step); + + const run_cmd = exe.run(); + test_step.dependOn(&run_cmd.step); +} diff --git a/test/link/macho/weak_framework/main.c b/test/link/macho/weak_framework/main.c new file mode 100644 index 0000000000..ca68d24cc7 --- /dev/null +++ b/test/link/macho/weak_framework/main.c @@ -0,0 +1,3 @@ +int main(int argc, char* argv[]) { + return 0; +} diff --git a/test/link/macho/weak_library/a.c b/test/link/macho/weak_library/a.c new file mode 100644 index 0000000000..9f49802ce6 --- /dev/null +++ b/test/link/macho/weak_library/a.c @@ -0,0 +1,9 @@ +#include + +int a = 42; + +const char* asStr() { + static char str[3]; + sprintf(str, "%d", 42); + return str; +} diff --git a/test/link/macho/weak_library/build.zig b/test/link/macho/weak_library/build.zig new file mode 100644 index 0000000000..5a1f7b4ce5 --- /dev/null +++ b/test/link/macho/weak_library/build.zig @@ -0,0 +1,33 @@ +const std = @import("std"); +const Builder = std.build.Builder; +const LibExeObjectStep = std.build.LibExeObjStep; + +pub fn build(b: *Builder) void { + const mode = b.standardReleaseOptions(); + + const test_step = b.step("test", "Test the program"); + test_step.dependOn(b.getInstallStep()); + + const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); + dylib.setBuildMode(mode); + dylib.addCSourceFile("a.c", &.{}); + dylib.linkLibC(); + dylib.install(); + + const exe = b.addExecutable("test", null); + exe.addCSourceFile("main.c", &[0][]const u8{}); + exe.setBuildMode(mode); + exe.linkLibC(); + exe.linkSystemLibraryWeak("a"); + exe.addLibraryPath(b.pathFromRoot("zig-out/lib")); + exe.addRPath(b.pathFromRoot("zig-out/lib")); + + const check = exe.checkObject(.macho); + check.checkStart("cmd LOAD_WEAK_DYLIB"); + check.checkNext("name @rpath/liba.dylib"); + test_step.dependOn(&check.step); + + const run_cmd = exe.run(); + run_cmd.expectStdOutEqual("42 42"); + test_step.dependOn(&run_cmd.step); +} diff --git a/test/link/macho/weak_library/main.c b/test/link/macho/weak_library/main.c new file mode 100644 index 0000000000..ee5367fef7 --- /dev/null +++ b/test/link/macho/weak_library/main.c @@ -0,0 +1,9 @@ +#include + +extern int a; +extern const char* asStr(); + +int main(int argc, char* argv[]) { + printf("%d %s", a, asStr()); + return 0; +} From 5834a608fc629319772b1623a31b62dd49ac6d63 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 28 Jun 2022 10:23:25 +0200 Subject: [PATCH 1979/2031] link-tests: do not save global extracted var unless a match Improve testing MachO binaries by verbose printing of the symtab which includes segment,section names for defined symbols, and import (dylib) name for imports. --- lib/std/build/CheckObjectStep.zig | 87 ++++++++++++++++++++++++-- test/link/macho/entry/build.zig | 2 +- test/link/macho/weak_library/build.zig | 5 ++ 3 files changed, 87 insertions(+), 7 deletions(-) diff --git a/lib/std/build/CheckObjectStep.zig b/lib/std/build/CheckObjectStep.zig index c7e91bb7cb..e4ad7423c4 100644 --- a/lib/std/build/CheckObjectStep.zig +++ b/lib/std/build/CheckObjectStep.zig @@ -65,6 +65,7 @@ const Action = struct { fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool { assert(act.tag == .match); + var candidate_var: ?struct { name: []const u8, value: u64 } = null; var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " "); var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " "); @@ -92,12 +93,19 @@ const Action = struct { const name = needle_tok[1..closing_brace]; if (name.len == 0) return error.MissingBraceValue; const value = try std.fmt.parseInt(u64, hay_tok, 16); - try global_vars.putNoClobber(name, value); + candidate_var = .{ + .name = name, + .value = value, + }; } else { if (!mem.eql(u8, hay_tok, needle_tok)) return false; } } + if (candidate_var) |v| { + try global_vars.putNoClobber(v.name, v.value); + } + return true; } @@ -332,20 +340,43 @@ const MachODumper = struct { var output = std.ArrayList(u8).init(gpa); const writer = output.writer(); - var symtab_cmd: ?macho.symtab_command = null; + var load_commands = std.ArrayList(macho.LoadCommand).init(gpa); + try load_commands.ensureTotalCapacity(hdr.ncmds); + + var sections = std.ArrayList(struct { seg: u16, sect: u16 }).init(gpa); + var imports = std.ArrayList(u16).init(gpa); + + var symtab_cmd: ?u16 = null; var i: u16 = 0; while (i < hdr.ncmds) : (i += 1) { var cmd = try macho.LoadCommand.read(gpa, reader); + load_commands.appendAssumeCapacity(cmd); - if (opts.dump_symtab and cmd.cmd() == .SYMTAB) { - symtab_cmd = cmd.symtab; + switch (cmd.cmd()) { + .SEGMENT_64 => { + const seg = cmd.segment; + for (seg.sections.items) |_, j| { + try sections.append(.{ .seg = i, .sect = @intCast(u16, j) }); + } + }, + .SYMTAB => { + symtab_cmd = i; + }, + .LOAD_DYLIB, + .LOAD_WEAK_DYLIB, + .REEXPORT_DYLIB, + => { + try imports.append(i); + }, + else => {}, } try dumpLoadCommand(cmd, i, writer); try writer.writeByte('\n'); } - if (symtab_cmd) |cmd| { + if (opts.dump_symtab) { + const cmd = load_commands.items[symtab_cmd.?].symtab; try writer.writeAll(symtab_label ++ "\n"); const strtab = bytes[cmd.stroff..][0..cmd.strsize]; const raw_symtab = bytes[cmd.symoff..][0 .. cmd.nsyms * @sizeOf(macho.nlist_64)]; @@ -354,7 +385,51 @@ const MachODumper = struct { for (symtab) |sym| { if (sym.stab()) continue; const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0); - try writer.print("{s} {x}\n", .{ sym_name, sym.n_value }); + if (sym.sect()) { + const map = sections.items[sym.n_sect - 1]; + const seg = load_commands.items[map.seg].segment; + const sect = seg.sections.items[map.sect]; + try writer.print("{x} ({s},{s})", .{ + sym.n_value, + sect.segName(), + sect.sectName(), + }); + if (sym.ext()) { + try writer.writeAll(" external"); + } + try writer.print(" {s}\n", .{sym_name}); + } else if (sym.undf()) { + const ordinal = @divTrunc(@bitCast(i16, sym.n_desc), macho.N_SYMBOL_RESOLVER); + const import_name = blk: { + if (ordinal <= 0) { + if (ordinal == macho.BIND_SPECIAL_DYLIB_SELF) + break :blk "self import"; + if (ordinal == macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE) + break :blk "main executable"; + if (ordinal == macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP) + break :blk "flat lookup"; + unreachable; + } + const import_id = imports.items[@bitCast(u16, ordinal) - 1]; + const import = load_commands.items[import_id].dylib; + const full_path = mem.sliceTo(import.data, 0); + const basename = fs.path.basename(full_path); + assert(basename.len > 0); + const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len; + break :blk basename[0..ext]; + }; + try writer.writeAll("(undefined)"); + if (sym.weakRef()) { + try writer.writeAll(" weak"); + } + if (sym.ext()) { + try writer.writeAll(" external"); + } + try writer.print(" {s} (from {s})\n", .{ + sym_name, + import_name, + }); + } else unreachable; } } diff --git a/test/link/macho/entry/build.zig b/test/link/macho/entry/build.zig index 1cd9099985..1f40b3d8e0 100644 --- a/test/link/macho/entry/build.zig +++ b/test/link/macho/entry/build.zig @@ -22,7 +22,7 @@ pub fn build(b: *Builder) void { check_exe.checkNext("entryoff {entryoff}"); check_exe.checkInSymtab(); - check_exe.checkNext("_non_main {n_value}"); + check_exe.checkNext("{n_value} (__TEXT,__text) external _non_main"); check_exe.checkComputeCompare("vmaddr entryoff +", .{ .op = .eq, .value = .{ .variable = "n_value" } }); diff --git a/test/link/macho/weak_library/build.zig b/test/link/macho/weak_library/build.zig index 5a1f7b4ce5..f1070f3b2b 100644 --- a/test/link/macho/weak_library/build.zig +++ b/test/link/macho/weak_library/build.zig @@ -25,6 +25,11 @@ pub fn build(b: *Builder) void { const check = exe.checkObject(.macho); check.checkStart("cmd LOAD_WEAK_DYLIB"); check.checkNext("name @rpath/liba.dylib"); + + check.checkInSymtab(); + check.checkNext("(undefined) weak external _a (from liba)"); + check.checkNext("(undefined) weak external _asStr (from liba)"); + test_step.dependOn(&check.step); const run_cmd = exe.run(); From 6420e9350c2e940f850c32beadff4042858dc243 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 28 Jun 2022 13:06:40 +0200 Subject: [PATCH 1980/2031] test: return error on unknown config value --- src/test.zig | 39 ++++++++++++---------- test/cases/recursive_inline_function.0.zig | 1 + 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/test.zig b/src/test.zig index 6801433b7f..cba11d7a25 100644 --- a/src/test.zig +++ b/src/test.zig @@ -235,9 +235,9 @@ const TestManifest = struct { inner: std.mem.SplitIterator(u8), parse_fn: ParseFn(T), - fn next(self: *@This()) ?T { + fn next(self: *@This()) !?T { const next_raw = self.inner.next() orelse return null; - return self.parse_fn(next_raw); + return try self.parse_fn(next_raw); } }; } @@ -339,20 +339,20 @@ const TestManifest = struct { allocator: Allocator, key: []const u8, comptime T: type, - ) error{OutOfMemory}![]const T { + ) ![]const T { var out = std.ArrayList(T).init(allocator); defer out.deinit(); var it = self.getConfigForKey(key, T); - while (it.next()) |item| { + while (try it.next()) |item| { try out.append(item); } return out.toOwnedSlice(); } - fn getConfigForKeyAssertSingle(self: TestManifest, key: []const u8, comptime T: type) T { + fn getConfigForKeyAssertSingle(self: TestManifest, key: []const u8, comptime T: type) !T { var it = self.getConfigForKey(key, T); - const res = it.next().?; - assert(it.next() == null); + const res = (try it.next()) orelse unreachable; + assert((try it.next()) == null); return res; } @@ -373,33 +373,36 @@ const TestManifest = struct { } fn ParseFn(comptime T: type) type { - return fn ([]const u8) ?T; + return fn ([]const u8) anyerror!T; } fn getDefaultParser(comptime T: type) ParseFn(T) { switch (@typeInfo(T)) { .Int => return struct { - fn parse(str: []const u8) ?T { - return std.fmt.parseInt(T, str, 0) catch null; + fn parse(str: []const u8) anyerror!T { + return try std.fmt.parseInt(T, str, 0); } }.parse, .Bool => return struct { - fn parse(str: []const u8) ?T { - const as_int = std.fmt.parseInt(u1, str, 0) catch return null; + fn parse(str: []const u8) anyerror!T { + const as_int = try std.fmt.parseInt(u1, str, 0); return as_int > 0; } }.parse, .Enum => return struct { - fn parse(str: []const u8) ?T { - return std.meta.stringToEnum(T, str); + fn parse(str: []const u8) anyerror!T { + return std.meta.stringToEnum(T, str) orelse { + std.log.err("unknown enum variant for {s}: {s}", .{ @typeName(T), str }); + return error.UnknownEnumVariant; + }; } }.parse, .Struct => if (comptime std.mem.eql(u8, @typeName(T), "CrossTarget")) return struct { - fn parse(str: []const u8) ?T { + fn parse(str: []const u8) anyerror!T { var opts = CrossTarget.ParseOptions{ .arch_os_abi = str, }; - return CrossTarget.parse(opts) catch null; + return try CrossTarget.parse(opts); } }.parse else @compileError("no default parser for " ++ @typeName(T)), else => @compileError("no default parser for " ++ @typeName(T)), @@ -1128,8 +1131,8 @@ pub const TestContext = struct { if (cases.items.len == 0) { const backends = try manifest.getConfigForKeyAlloc(ctx.arena, "backend", Backend); const targets = try manifest.getConfigForKeyAlloc(ctx.arena, "target", CrossTarget); - const is_test = manifest.getConfigForKeyAssertSingle("is_test", bool); - const output_mode = manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode); + const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool); + const output_mode = try manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode); const name_prefix = blk: { const ext_index = std.mem.lastIndexOfScalar(u8, current_file.*, '.') orelse diff --git a/test/cases/recursive_inline_function.0.zig b/test/cases/recursive_inline_function.0.zig index c8333c9065..45a29a1068 100644 --- a/test/cases/recursive_inline_function.0.zig +++ b/test/cases/recursive_inline_function.0.zig @@ -9,4 +9,5 @@ inline fn fibonacci(n: usize) usize { } // run +// target=x86_64-linux,arm-linux,x86_64-macos,wasm32-wasi // From 914e2d4c99e17b6220d2101a5a26bf16e10a192e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 28 Jun 2022 13:23:11 +0200 Subject: [PATCH 1981/2031] test: fix spurious whitespace in nested_blocks test --- test/cases/llvm/nested_blocks.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/llvm/nested_blocks.zig b/test/cases/llvm/nested_blocks.zig index 47b6c5069b..895f7e658b 100644 --- a/test/cases/llvm/nested_blocks.zig +++ b/test/cases/llvm/nested_blocks.zig @@ -19,6 +19,6 @@ pub fn main() void { } // run -// backend=stage2, llvm +// backend=stage2,llvm // target=x86_64-linux,x86_64-macos // From 22490892fa33c27c6c9158cdb9b910a95d575fde Mon Sep 17 00:00:00 2001 From: Ken Micklas Date: Tue, 28 Jun 2022 08:09:54 +0100 Subject: [PATCH 1982/2031] Pass -O0 rather than -Og to Clang for Debug builds --- src/Compilation.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 01f6a15e2c..c6c7a94b41 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -4073,7 +4073,10 @@ pub fn addCCArgs( .Debug => { // windows c runtime requires -D_DEBUG if using debug libraries try argv.append("-D_DEBUG"); - try argv.append("-Og"); + // Clang has -Og for compatibility with GCC, but currently it is just equivalent + // to -O1. Besides potentially impairing debugging, -O1/-Og significantly + // increases compile times. + try argv.append("-O0"); if (comp.bin_file.options.link_libc and target.os.tag != .wasi) { try argv.append("-fstack-protector-strong"); From ca3c4ff2d0afcdc8fe86e7e7b41a967c88779729 Mon Sep 17 00:00:00 2001 From: Shupei Fan Date: Tue, 28 Jun 2022 18:10:41 +0800 Subject: [PATCH 1983/2031] zig0: handle baseline cpu features for rv64 --- src/stage1/zig0.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/stage1/zig0.cpp b/src/stage1/zig0.cpp index 95c7e34fe9..f248f6e866 100644 --- a/src/stage1/zig0.cpp +++ b/src/stage1/zig0.cpp @@ -157,6 +157,17 @@ static void get_native_target(ZigTarget *target) { } } +static const char* get_baseline_llvm_cpu_name(ZigLLVM_ArchType arch) { + return ""; +} + +static const char* get_baseline_llvm_cpu_features(ZigLLVM_ArchType arch) { + switch (arch) { + case ZigLLVM_riscv64: return "+a,+c,+d,+m"; + default: return ""; + } +} + static Error target_parse_triple(struct ZigTarget *target, const char *zig_triple, const char *mcpu, const char *dynamic_linker) { @@ -175,8 +186,8 @@ static Error target_parse_triple(struct ZigTarget *target, const char *zig_tripl } else if (strcmp(mcpu, "baseline") == 0) { target->is_native_os = false; target->is_native_cpu = false; - target->llvm_cpu_name = ""; - target->llvm_cpu_features = ""; + target->llvm_cpu_name = get_baseline_llvm_cpu_name(target->arch); + target->llvm_cpu_features = get_baseline_llvm_cpu_features(target->arch); } else { const char *msg = "stage0 can't handle CPU/features in the target"; stage2_panic(msg, strlen(msg)); @@ -217,6 +228,9 @@ static Error target_parse_triple(struct ZigTarget *target, const char *zig_tripl const char *msg = "stage0 can't handle CPU/features in the target"; stage2_panic(msg, strlen(msg)); } + + target->llvm_cpu_name = get_baseline_llvm_cpu_name(target->arch); + target->llvm_cpu_features = get_baseline_llvm_cpu_features(target->arch); } return ErrorNone; From c02dc17618637139a5fc704c5c42723984398665 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 28 Jun 2022 20:52:31 +0200 Subject: [PATCH 1984/2031] link: cache weak libraries --- src/link.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/link.zig b/src/link.zig index da6e8c53ed..6c5e876022 100644 --- a/src/link.zig +++ b/src/link.zig @@ -19,6 +19,7 @@ const Package = @import("Package.zig"); const Type = @import("type.zig").Type; const TypedValue = @import("TypedValue.zig"); +/// When adding a new field, remember to update `hashAddSystemLibs`. pub const SystemLib = struct { needed: bool = false, weak: bool = false, @@ -35,6 +36,7 @@ pub fn hashAddSystemLibs( hh.addListOfBytes(keys); for (hm.values()) |value| { hh.add(value.needed); + hh.add(value.weak); } } From c2c1998269f8a92e39c14dabb68b253c012d56ef Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 28 Jun 2022 21:16:19 +0200 Subject: [PATCH 1985/2031] clang: update cmdline options to include weak libs and frameworks Clang accepts `-weak-lx`, `-weak_library x` and `-weak_framework x`. --- src/clang_options_data.zig | 27 ++++++++++++++++++++++++--- src/main.zig | 4 ++++ tools/update_clang_options.zig | 12 ++++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/clang_options_data.zig b/src/clang_options_data.zig index 25cdd04f82..b4bef06c9e 100644 --- a/src/clang_options_data.zig +++ b/src/clang_options_data.zig @@ -4867,8 +4867,22 @@ flagpd1("version"), .psl = false, }, flagpd1("w"), -sepd1("weak_framework"), -sepd1("weak_library"), +.{ + .name = "weak_framework", + .syntax = .separate, + .zig_equivalent = .weak_framework, + .pd1 = true, + .pd2 = false, + .psl = false, +}, +.{ + .name = "weak_library", + .syntax = .separate, + .zig_equivalent = .weak_library, + .pd1 = true, + .pd2 = false, + .psl = false, +}, sepd1("weak_reference_mismatches"), flagpd1("whatsloaded"), flagpd1("why_load"), @@ -6200,7 +6214,14 @@ jspd1("iquote"), .pd2 = true, .psl = false, }, -joinpd1("weak-l"), +.{ + .name = "weak-l", + .syntax = .joined, + .zig_equivalent = .weak_library, + .pd1 = true, + .pd2 = false, + .psl = false, +}, .{ .name = "Ofast", .syntax = .flag, diff --git a/src/main.zig b/src/main.zig index ae1b95a4aa..f13fe342ca 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1625,6 +1625,8 @@ fn buildOutputType( .entry => { entry = it.only_arg; }, + .weak_library => try system_libs.put(it.only_arg, .{ .weak = true }), + .weak_framework => try frameworks.put(gpa, it.only_arg, .{ .weak = true }), } } // Parse linker args. @@ -4577,6 +4579,8 @@ pub const ClangArgIterator = struct { emit_llvm, sysroot, entry, + weak_library, + weak_framework, }; const Args = struct { diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig index 0a8b00537c..b003a1bbb9 100644 --- a/tools/update_clang_options.zig +++ b/tools/update_clang_options.zig @@ -432,6 +432,18 @@ const known_options = [_]KnownOpt{ .name = "e", .ident = "entry", }, + .{ + .name = "weak-l", + .ident = "weak_library", + }, + .{ + .name = "weak_library", + .ident = "weak_library", + }, + .{ + .name = "weak_framework", + .ident = "weak_framework", + }, }; const blacklisted_options = [_][]const u8{}; From bb2929ba083d3c5d86cae1d83cba0abd43ffa3d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20L=C3=BChmann?= <47984692+luehmann@users.noreply.github.com> Date: Tue, 28 Jun 2022 21:38:28 +0200 Subject: [PATCH 1986/2031] zig fmt: fix idempotency with newlines surrounding doc comment Fixes: https://github.com/ziglang/zig/issues/11802 --- lib/std/os/linux.zig | 1 - lib/std/zig/parser_test.zig | 35 +++++++++++++++++++++++++++++++++++ lib/std/zig/render.zig | 12 +++++++++++- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 7a0af50f9e..1b3a71cebd 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -4683,7 +4683,6 @@ pub const prctl_mm_map = extern struct { }; pub const NETLINK = struct { - /// Routing/device hook pub const ROUTE = 0; diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index d9555c1ff8..b359dd12df 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -4154,6 +4154,41 @@ test "zig fmt: container doc comments" { ); } +test "zig fmt: remove newlines surrounding doc comment" { + try testTransform( + \\ + \\ + \\ + \\/// doc comment + \\ + \\fn foo() void {} + \\ + , + \\/// doc comment + \\fn foo() void {} + \\ + ); +} + +test "zig fmt: remove newlines surrounding doc comment within container decl" { + try testTransform( + \\const Foo = struct { + \\ + \\ + \\ /// doc comment + \\ + \\ fn foo() void {} + \\}; + \\ + , + \\const Foo = struct { + \\ /// doc comment + \\ fn foo() void {} + \\}; + \\ + ); +} + test "zig fmt: anytype struct field" { try testError( \\pub const Pointer = struct { diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index cf7a161b8d..72d710b29a 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -2482,7 +2482,17 @@ fn renderDocComments(ais: *Ais, tree: Ast, end_token: Ast.TokenIndex) Error!void } const first_tok = tok; if (first_tok == end_token) return; - try renderExtraNewlineToken(ais, tree, first_tok); + + if (first_tok != 0) { + const prev_token_tag = token_tags[first_tok - 1]; + + // Prevent accidental use of `renderDocComments` for a function argument doc comment + assert(prev_token_tag != .l_paren); + + if (prev_token_tag != .l_brace) { + try renderExtraNewlineToken(ais, tree, first_tok); + } + } while (token_tags[tok] == .doc_comment) : (tok += 1) { try renderToken(ais, tree, tok, .newline); From 76546b3f8e19b11f0c88259b1b3e5fd14cfbde31 Mon Sep 17 00:00:00 2001 From: Ben Fiedler Date: Fri, 24 Jun 2022 17:24:44 +0200 Subject: [PATCH 1987/2031] std.mem: add peek() to TokenIterator(T) --- lib/std/mem.zig | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 61d6b84874..046339b6fc 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -1608,6 +1608,7 @@ pub fn tokenize(comptime T: type, buffer: []const T, delimiter_bytes: []const T) test "tokenize" { var it = tokenize(u8, " abc def ghi ", " "); try testing.expect(eql(u8, it.next().?, "abc")); + try testing.expect(eql(u8, it.peek().?, "def")); try testing.expect(eql(u8, it.next().?, "def")); try testing.expect(eql(u8, it.next().?, "ghi")); try testing.expect(it.next() == null); @@ -1626,9 +1627,11 @@ test "tokenize" { it = tokenize(u8, "|", "|"); try testing.expect(it.next() == null); + try testing.expect(it.peek() == null); it = tokenize(u8, "", "|"); try testing.expect(it.next() == null); + try testing.expect(it.peek() == null); it = tokenize(u8, "hello", ""); try testing.expect(eql(u8, it.next().?, "hello")); @@ -1650,11 +1653,13 @@ test "tokenize" { test "tokenize (multibyte)" { var it = tokenize(u8, "a|b,c/d e", " /,|"); try testing.expect(eql(u8, it.next().?, "a")); + try testing.expect(eql(u8, it.peek().?, "b")); try testing.expect(eql(u8, it.next().?, "b")); try testing.expect(eql(u8, it.next().?, "c")); try testing.expect(eql(u8, it.next().?, "d")); try testing.expect(eql(u8, it.next().?, "e")); try testing.expect(it.next() == null); + try testing.expect(it.peek() == null); var it16 = tokenize( u16, @@ -1778,8 +1783,17 @@ pub fn TokenIterator(comptime T: type) type { const Self = @This(); - /// Returns a slice of the next token, or null if tokenization is complete. + /// Returns a slice of the current token, or null if tokenization is + /// complete, and advances to the next token. pub fn next(self: *Self) ?[]const T { + const result = self.peek() orelse return null; + self.index += result.len; + return result; + } + + /// Returns a slice of the current token, or null if tokenization is + /// complete. Does not advance to the next token. + pub fn peek(self: *Self) ?[]const T { // move to beginning of token while (self.index < self.buffer.len and self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} const start = self.index; @@ -1788,8 +1802,8 @@ pub fn TokenIterator(comptime T: type) type { } // move to end of token - while (self.index < self.buffer.len and !self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} - const end = self.index; + var end = start; + while (end < self.buffer.len and !self.isSplitByte(self.buffer[end])) : (end += 1) {} return self.buffer[start..end]; } From 304a58a2515c19cc719f5a517b0016f82117263e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Jun 2022 16:35:34 -0700 Subject: [PATCH 1988/2031] TypedValue: fix print function for optional_payload_ptr and eu_payload_ptr --- src/TypedValue.zig | 62 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/src/TypedValue.zig b/src/TypedValue.zig index 3c1b28e544..b00600d37a 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -427,15 +427,63 @@ pub fn print( }, .eu_payload_ptr => { try writer.writeAll("&"); - val = val.castTag(.eu_payload_ptr).?.data.container_ptr; - ty = ty.elemType2().errorUnionPayload(); + + const data = val.castTag(.eu_payload_ptr).?.data; + + var ty_val: Value.Payload.Ty = .{ + .base = .{ .tag = .ty }, + .data = ty, + }; + + try writer.writeAll("@as("); + try print(.{ + .ty = Type.type, + .val = Value.initPayload(&ty_val.base), + }, writer, level - 1, mod); + + try writer.writeAll(", &(payload of "); + + var ptr_ty: Type.Payload.ElemType = .{ + .base = .{ .tag = .single_mut_pointer }, + .data = data.container_ty, + }; + + try print(.{ + .ty = Type.initPayload(&ptr_ty.base), + .val = data.container_ptr, + }, writer, level - 1, mod); + + try writer.writeAll("))"); + return; }, .opt_payload_ptr => { - try writer.writeAll("&"); - val = val.castTag(.opt_payload).?.data; - var buf: Type.Payload.ElemType = undefined; - ty = ty.elemType2().optionalChild(&buf); - return print(.{ .ty = ty, .val = val }, writer, level, mod); + const data = val.castTag(.opt_payload_ptr).?.data; + + var ty_val: Value.Payload.Ty = .{ + .base = .{ .tag = .ty }, + .data = ty, + }; + + try writer.writeAll("@as("); + try print(.{ + .ty = Type.type, + .val = Value.initPayload(&ty_val.base), + }, writer, level - 1, mod); + + try writer.writeAll(", &(payload of "); + + var ptr_ty: Type.Payload.ElemType = .{ + .base = .{ .tag = .single_mut_pointer }, + .data = data.container_ty, + }; + + try print(.{ + .ty = Type.initPayload(&ptr_ty.base), + .val = data.container_ptr, + }, writer, level - 1, mod); + + try writer.writeAll("))"); + return; }, // TODO these should not appear in this function From 6c51c8e131b0bad77f199176dfc186b92768f296 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Jun 2022 16:35:58 -0700 Subject: [PATCH 1989/2031] Sema: fix not propagating want_safety in zirBlock Before this commit, `@setRuntimeSafety()` has no effect inside an if expression. --- src/Sema.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Sema.zig b/src/Sema.zig index 798dc06db1..2d2c303586 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4478,6 +4478,7 @@ fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErro .label = &label, .inlining = parent_block.inlining, .is_comptime = parent_block.is_comptime, + .want_safety = parent_block.want_safety, }; defer child_block.instructions.deinit(gpa); From a058696df280ad50c28a2d501a6a2c38a7e3a532 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Jun 2022 17:25:26 -0700 Subject: [PATCH 1990/2031] print_zir: fix unreachable missing end paren --- src/print_zir.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/print_zir.zig b/src/print_zir.zig index 1760c617ce..dbb91a0ae0 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -2217,6 +2217,7 @@ const Writer = struct { fn writeUnreachable(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].@"unreachable"; + try stream.writeAll(") "); try self.writeSrc(stream, inst_data.src()); } From c3ae909e935f1548408f2e400464370ee02b7e82 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Jun 2022 17:56:28 -0700 Subject: [PATCH 1991/2031] Revert "AstGen: preserve inferred ptr result loc for breaks" This reverts commit 8bf3e1f8d0902abd4133e2729b3625c25011c3ff, which introduced miscompilations for peer expressions any time they needed coercions to runtime types. I opened #11957 as a proposal to accomplish the goal of the reverted commit. Closes #11898 --- lib/std/priority_queue.zig | 15 --------------- src/AstGen.zig | 2 +- src/Sema.zig | 22 ---------------------- test/behavior/basic.zig | 12 ------------ test/behavior/cast.zig | 2 ++ test/behavior/error.zig | 2 +- test/behavior/if.zig | 17 +++++++++++++++++ 7 files changed, 21 insertions(+), 51 deletions(-) diff --git a/lib/std/priority_queue.zig b/lib/std/priority_queue.zig index 1a52fda5be..ebc13a9974 100644 --- a/lib/std/priority_queue.zig +++ b/lib/std/priority_queue.zig @@ -286,7 +286,6 @@ const PQlt = PriorityQueue(u32, void, lessThan); const PQgt = PriorityQueue(u32, void, greaterThan); test "std.PriorityQueue: add and remove min heap" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); @@ -305,7 +304,6 @@ test "std.PriorityQueue: add and remove min heap" { } test "std.PriorityQueue: add and remove same min heap" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); @@ -355,7 +353,6 @@ test "std.PriorityQueue: peek" { } test "std.PriorityQueue: sift up with odd indices" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 }; @@ -370,7 +367,6 @@ test "std.PriorityQueue: sift up with odd indices" { } test "std.PriorityQueue: addSlice" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 }; @@ -403,7 +399,6 @@ test "std.PriorityQueue: fromOwnedSlice trivial case 1" { } test "std.PriorityQueue: fromOwnedSlice" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 }; const heap_items = try testing.allocator.dupe(u32, items[0..]); var queue = PQlt.fromOwnedSlice(testing.allocator, heap_items[0..], {}); @@ -416,7 +411,6 @@ test "std.PriorityQueue: fromOwnedSlice" { } test "std.PriorityQueue: add and remove max heap" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQgt.init(testing.allocator, {}); defer queue.deinit(); @@ -435,7 +429,6 @@ test "std.PriorityQueue: add and remove max heap" { } test "std.PriorityQueue: add and remove same max heap" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQgt.init(testing.allocator, {}); defer queue.deinit(); @@ -476,7 +469,6 @@ test "std.PriorityQueue: iterator" { } test "std.PriorityQueue: remove at index" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); @@ -512,7 +504,6 @@ test "std.PriorityQueue: iterator while empty" { } test "std.PriorityQueue: shrinkAndFree" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); @@ -536,7 +527,6 @@ test "std.PriorityQueue: shrinkAndFree" { } test "std.PriorityQueue: update min heap" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); @@ -552,7 +542,6 @@ test "std.PriorityQueue: update min heap" { } test "std.PriorityQueue: update same min heap" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); @@ -569,7 +558,6 @@ test "std.PriorityQueue: update same min heap" { } test "std.PriorityQueue: update max heap" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQgt.init(testing.allocator, {}); defer queue.deinit(); @@ -585,7 +573,6 @@ test "std.PriorityQueue: update max heap" { } test "std.PriorityQueue: update same max heap" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQgt.init(testing.allocator, {}); defer queue.deinit(); @@ -602,7 +589,6 @@ test "std.PriorityQueue: update same max heap" { } test "std.PriorityQueue: siftUp in remove" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO var queue = PQlt.init(testing.allocator, {}); defer queue.deinit(); @@ -623,7 +609,6 @@ fn contextLessThan(context: []const u32, a: usize, b: usize) Order { const CPQlt = PriorityQueue(usize, []const u32, contextLessThan); test "std.PriorityQueue: add and remove min heap with contextful comparator" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO const context = [_]u32{ 5, 3, 4, 2, 2, 8, 0 }; var queue = CPQlt.init(testing.allocator, context[0..]); diff --git a/src/AstGen.zig b/src/AstGen.zig index 078523831c..ec8af65614 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -10007,7 +10007,7 @@ const GenZir = struct { .inferred_ptr => |ptr| { gz.rl_ty_inst = .none; gz.rl_ptr = ptr; - gz.break_result_loc = parent_rl; + gz.break_result_loc = .{ .block_ptr = gz }; }, .block_ptr => |parent_block_scope| { diff --git a/src/Sema.zig b/src/Sema.zig index 2d2c303586..159574e3d5 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3079,28 +3079,6 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com if (var_is_mut) { try sema.validateVarType(block, ty_src, final_elem_ty, false); - - // The value might have been bitcasted into a comptime only - // pointer type such as `*@Type(.EnumLiteral)` so we must now - // update all the stores to not give backends invalid AIR. - - var air_tags = sema.air_instructions.items(.tag); - var air_data = sema.air_instructions.items(.data); - var peer_inst_index: usize = 0; - var i = ptr_inst; - while (i < air_tags.len and peer_inst_index < peer_inst_list.len) : (i += 1) { - if (air_tags[i] != .store) continue; - if (air_data[i].bin_op.rhs == peer_inst_list[peer_inst_index]) { - peer_inst_index += 1; - _ = (try sema.resolveMaybeUndefVal(block, .unneeded, air_data[i].bin_op.rhs)) orelse continue; - const coerced_val = try sema.coerce(block, final_elem_ty, air_data[i].bin_op.rhs, .unneeded); - air_tags = sema.air_instructions.items(.tag); - air_data = sema.air_instructions.items(.data); - - air_data[i].bin_op.lhs = ptr; - air_data[i].bin_op.rhs = coerced_val; - } - } } else ct: { // Detect if the value is comptime known. In such case, the // last 3 AIR instructions of the block will look like this: diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index d288d35787..8241579229 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -928,18 +928,6 @@ test "try in labeled block doesn't cast to wrong type" { _ = s; } -test "comptime int in switch in catch is casted to correct inferred type" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - - var a: error{ A, B }!u64 = 0; - var b = a catch |err| switch (err) { - error.A => 0, - else => unreachable, - }; - _ = b; -} - test "vector initialized with array init syntax has proper type" { comptime { const actual = -@Vector(4, i32){ 1, 2, 3, 4 }; diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index c980de8e8a..7064b5abdc 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -812,6 +812,7 @@ test "peer type resolution: error union after non-error" { } test "peer cast *[0]T to E![]const T" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -826,6 +827,7 @@ test "peer cast *[0]T to E![]const T" { } test "peer cast *[0]T to []const T" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO diff --git a/test/behavior/error.zig b/test/behavior/error.zig index c32ca6f6a1..0e3767a4ca 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -709,7 +709,7 @@ test "simple else prong allowed even when all errors handled" { } }; var value = S.foo() catch |err| switch (err) { - error.Foo => 255, + error.Foo => @as(u8, 255), else => |e| return e, }; try expect(value == 255); diff --git a/test/behavior/if.zig b/test/behavior/if.zig index d59a974ce4..a78bf74502 100644 --- a/test/behavior/if.zig +++ b/test/behavior/if.zig @@ -111,3 +111,20 @@ test "if prongs cast to expected type instead of peer type resolution" { try S.doTheTest(false); comptime try S.doTheTest(false); } + +test "if peer expressions inferred optional type" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + var self: []const u8 = "abcdef"; + var index: usize = 0; + var left_index = (index << 1) + 1; + var right_index = left_index + 1; + var left = if (left_index < self.len) self[left_index] else null; + var right = if (right_index < self.len) self[right_index] else null; + try expect(left_index < self.len); + try expect(right_index < self.len); + try expect(left.? == 98); + try expect(right.? == 99); +} From b2e4dda0018b83819c9539609eff7e420eca3202 Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Wed, 29 Jun 2022 07:43:49 +0200 Subject: [PATCH 1992/2031] std.crypto.{p256,p384}: process the top nibble in mulDoubleBasePublic (#11956) Unlike curve25519 where the scalar size is not large enough to fill the top nibble, this can definitely be the case for p256 and p384. --- lib/std/crypto/pcurves/p256.zig | 2 +- lib/std/crypto/pcurves/p384.zig | 2 +- lib/std/crypto/pcurves/tests/p256.zig | 10 ++++++++++ lib/std/crypto/pcurves/tests/p384.zig | 10 ++++++++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/std/crypto/pcurves/p256.zig b/lib/std/crypto/pcurves/p256.zig index c7dac55b24..5898f83c10 100644 --- a/lib/std/crypto/pcurves/p256.zig +++ b/lib/std/crypto/pcurves/p256.zig @@ -437,7 +437,7 @@ pub const P256 = struct { const e1 = slide(s1); const e2 = slide(s2); var q = P256.identityElement; - var pos: usize = 2 * 32 - 1; + var pos: usize = 2 * 32; while (true) : (pos -= 1) { const slot1 = e1[pos]; if (slot1 > 0) { diff --git a/lib/std/crypto/pcurves/p384.zig b/lib/std/crypto/pcurves/p384.zig index 854343c1e4..0694d4f259 100644 --- a/lib/std/crypto/pcurves/p384.zig +++ b/lib/std/crypto/pcurves/p384.zig @@ -437,7 +437,7 @@ pub const P384 = struct { const e1 = slide(s1); const e2 = slide(s2); var q = P384.identityElement; - var pos: usize = 2 * 48 - 1; + var pos: usize = 2 * 48; while (true) : (pos -= 1) { const slot1 = e1[pos]; if (slot1 > 0) { diff --git a/lib/std/crypto/pcurves/tests/p256.zig b/lib/std/crypto/pcurves/tests/p256.zig index ce21ac8ba0..eab69d351e 100644 --- a/lib/std/crypto/pcurves/tests/p256.zig +++ b/lib/std/crypto/pcurves/tests/p256.zig @@ -112,6 +112,16 @@ test "p256 double base multiplication" { try testing.expect(pr1.equivalent(pr2)); } +test "p256 double base multiplication with large scalars" { + const p1 = P256.basePoint; + const p2 = P256.basePoint.dbl(); + const s1 = [_]u8{0xee} ** 32; + const s2 = [_]u8{0xdd} ** 32; + const pr1 = try P256.mulDoubleBasePublic(p1, s1, p2, s2, .Little); + const pr2 = (try p1.mul(s1, .Little)).add(try p2.mul(s2, .Little)); + try testing.expect(pr1.equivalent(pr2)); +} + test "p256 scalar inverse" { const expected = "3b549196a13c898a6f6e84dfb3a22c40a8b9b17fb88e408ea674e451cd01d0a6"; var out: [32]u8 = undefined; diff --git a/lib/std/crypto/pcurves/tests/p384.zig b/lib/std/crypto/pcurves/tests/p384.zig index 54c5148ae0..cba1d91a81 100644 --- a/lib/std/crypto/pcurves/tests/p384.zig +++ b/lib/std/crypto/pcurves/tests/p384.zig @@ -115,6 +115,16 @@ test "p384 double base multiplication" { try testing.expect(pr1.equivalent(pr2)); } +test "p384 double base multiplication with large scalars" { + const p1 = P384.basePoint; + const p2 = P384.basePoint.dbl(); + const s1 = [_]u8{0xee} ** 48; + const s2 = [_]u8{0xdd} ** 48; + const pr1 = try P384.mulDoubleBasePublic(p1, s1, p2, s2, .Little); + const pr2 = (try p1.mul(s1, .Little)).add(try p2.mul(s2, .Little)); + try testing.expect(pr1.equivalent(pr2)); +} + test "p384 scalar inverse" { const expected = "a3cc705f33b5679a66e76ce66e68055c927c5dba531b2837b18fe86119511091b54d733f26b2e7a0f6fa2e7ea21ca806"; var out: [48]u8 = undefined; From 41533fa6a1fa5e9e9b38a59403930501fd61a259 Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Wed, 29 Jun 2022 07:44:43 +0200 Subject: [PATCH 1993/2031] std/crypto/{25519,pcurves}: make the scalar field order public (#11955) For 25519, it's very likely that applications would ever need the serialized representation. Expose the value as an integer as in other curves. Rename the internal representation from `field_size` to `field_order` for consistency. Also fix a common typo in `scalar.sub()`. --- lib/std/crypto/25519/scalar.zig | 24 ++++++++++++++---------- lib/std/crypto/pcurves/p256/scalar.zig | 5 ++++- lib/std/crypto/pcurves/p384/scalar.zig | 5 ++++- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/lib/std/crypto/25519/scalar.zig b/lib/std/crypto/25519/scalar.zig index c3e7fb4fa0..ff2e6aff80 100644 --- a/lib/std/crypto/25519/scalar.zig +++ b/lib/std/crypto/25519/scalar.zig @@ -4,10 +4,8 @@ const mem = std.mem; const NonCanonicalError = std.crypto.errors.NonCanonicalError; -/// 2^252 + 27742317777372353535851937790883648493 -pub const field_size = [32]u8{ - 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, // 2^252+27742317777372353535851937790883648493 -}; +/// The scalar field order. +pub const field_order: u256 = 7237005577332262213973186563042994240857116359379907606001950938285454250989; /// A compressed scalar pub const CompressedScalar = [32]u8; @@ -15,6 +13,12 @@ pub const CompressedScalar = [32]u8; /// Zero pub const zero = [_]u8{0} ** 32; +const field_order_s = s: { + var s: [32]u8 = undefined; + mem.writeIntLittle(u256, &s, field_order); + break :s s; +}; + /// Reject a scalar whose encoding is not canonical. pub fn rejectNonCanonical(s: CompressedScalar) NonCanonicalError!void { var c: u8 = 0; @@ -22,9 +26,9 @@ pub fn rejectNonCanonical(s: CompressedScalar) NonCanonicalError!void { var i: usize = 31; while (true) : (i -= 1) { const xs = @as(u16, s[i]); - const xfield_size = @as(u16, field_size[i]); - c |= @intCast(u8, ((xs -% xfield_size) >> 8) & n); - n &= @intCast(u8, ((xs ^ xfield_size) -% 1) >> 8); + const xfield_order_s = @as(u16, field_order_s[i]); + c |= @intCast(u8, ((xs -% xfield_order_s) >> 8) & n); + n &= @intCast(u8, ((xs ^ xfield_order_s) -% 1) >> 8); if (i == 0) break; } if (c == 0) { @@ -77,7 +81,7 @@ pub fn add(a: CompressedScalar, b: CompressedScalar) CompressedScalar { /// Return -s (mod L) pub fn neg(s: CompressedScalar) CompressedScalar { - const fs: [64]u8 = field_size ++ [_]u8{0} ** 32; + const fs: [64]u8 = field_order_s ++ [_]u8{0} ** 32; var sx: [64]u8 = undefined; mem.copy(u8, sx[0..32], s[0..]); mem.set(u8, sx[32..], 0); @@ -848,7 +852,7 @@ test "scalar25519" { var buf: [128]u8 = undefined; try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&y)}), "1E979B917937F3DE71D18077F961F6CEFF01030405060708010203040506070F"); - const reduced = reduce(field_size); + const reduced = reduce(field_order_s); try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&reduced)}), "0000000000000000000000000000000000000000000000000000000000000000"); } @@ -881,7 +885,7 @@ test "random scalar" { } test "64-bit reduction" { - const bytes = field_size ++ [_]u8{0} ** 32; + const bytes = field_order_s ++ [_]u8{0} ** 32; const x = Scalar.fromBytes64(bytes); try std.testing.expect(x.isZero()); } diff --git a/lib/std/crypto/pcurves/p256/scalar.zig b/lib/std/crypto/pcurves/p256/scalar.zig index d6553afdd4..d3ac2a9b95 100644 --- a/lib/std/crypto/pcurves/p256/scalar.zig +++ b/lib/std/crypto/pcurves/p256/scalar.zig @@ -24,6 +24,9 @@ const Fe = Field(.{ .encoded_length = encoded_length, }); +/// The scalar field order. +pub const field_order = Fe.field_order; + /// Reject a scalar whose encoding is not canonical. pub fn rejectNonCanonical(s: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!void { return Fe.rejectNonCanonical(s, endian); @@ -61,7 +64,7 @@ pub fn neg(s: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!Co /// Return (a-b) (mod L) pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!CompressedScalar { - return (try Scalar.fromBytes(a, endian)).sub(try Scalar.fromBytes(b.endian)).toBytes(endian); + return (try Scalar.fromBytes(a, endian)).sub(try Scalar.fromBytes(b, endian)).toBytes(endian); } /// Return a random scalar diff --git a/lib/std/crypto/pcurves/p384/scalar.zig b/lib/std/crypto/pcurves/p384/scalar.zig index 0c6fcea17c..b6db0c83d4 100644 --- a/lib/std/crypto/pcurves/p384/scalar.zig +++ b/lib/std/crypto/pcurves/p384/scalar.zig @@ -24,6 +24,9 @@ const Fe = Field(.{ .encoded_length = encoded_length, }); +/// The scalar field order. +pub const field_order = Fe.field_order; + /// Reject a scalar whose encoding is not canonical. pub fn rejectNonCanonical(s: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!void { return Fe.rejectNonCanonical(s, endian); @@ -56,7 +59,7 @@ pub fn neg(s: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!Co /// Return (a-b) (mod L) pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!CompressedScalar { - return (try Scalar.fromBytes(a, endian)).sub(try Scalar.fromBytes(b.endian)).toBytes(endian); + return (try Scalar.fromBytes(a, endian)).sub(try Scalar.fromBytes(b, endian)).toBytes(endian); } /// Return a random scalar From 4a6b70fbd1e8f6de25ae00c16051aa9613d8a7bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Wed, 29 Jun 2022 09:23:09 +0300 Subject: [PATCH 1994/2031] mem: add splitBackwards (#11908) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * mem: refactor tests of split() - add a few cases for .rest() - use expectEqualSlices() * mem: add splitBackwards Over the last couple of weeks weeks I needed to iterate over a collection backwards at least twice. Do we want to have this in stdlib? If yes, click "Merge" and start using today! Free shipping and returns (before 1.0). Why is this useful? ------------------- I need this for building an error wrapper: errors are added in the wrapper from "lowest" level to "highest" level, and then printed in reverse order. Imagine `UpdateUsers` call, which needs to return `error.InvalidInput` and a wrappable error context. In Go we would add a context to the error when returning it: // if update_user fails, add context on which user we are operating if err := update_user(user); err != nil { return fmt.Errorf("user id=%d: %w", user.id, err) } Since Zig cannot pass anything else than u16 with an error (#2647), I will pass a `err_ctx: *Err`, to the callers, where they can, besides returning an error, augment it with auxiliary data. `Err` is a preallocated array that can add zero-byte-separated strings. For a concrete example, imagine such a call graph: update_user(User, *Err) error{InvalidInput}!<...> validate_user([]const u8, *Err) error{InvalidInput}!<...> Where `validate_user` would like, besides only the error, signal the invalid field. And `update_user`, besides the error, would signal the offending user id. We also don't want the low-level functions to know in which context they are operating to construct a meaningful error message: if validation fails, they append their "context" to the buffer. To translate/augment the Go example above: pub fn validate_user(err_ctx: *Err, user: User) error{InvalidInput}!void { const name = user.name; if (!ascii.isAlpha(name)) { err_ctx.print("name '{s}' must be ascii-letters only", .{name}); return error.InvalidInput; } <...> } // update_user validates each user and does something with it. pub fn update_user(err_ctx: *Err, user: User) error{InvalidInput}!void { // validate the user before updating it validate_user(user) catch { err_ctx.print("user id={d}", .{user.id}); return error.InvalidInput; }; <...> } Then the top-level function (in my case, CLI) will read the buffer backwards (splitting on `"\x00"`) and print: user id=123: name 'Žvangalas' must be ascii-letters only To read that buffer backwards, dear readers of this commit message, I need `mem.splitBackwards`. --- lib/std/mem.zig | 165 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 146 insertions(+), 19 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 046339b6fc..f54610f060 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -1707,23 +1707,32 @@ pub fn split(comptime T: type, buffer: []const T, delimiter: []const T) SplitIte test "split" { var it = split(u8, "abc|def||ghi", "|"); - try testing.expect(eql(u8, it.next().?, "abc")); - try testing.expect(eql(u8, it.next().?, "def")); - try testing.expect(eql(u8, it.next().?, "")); - try testing.expect(eql(u8, it.next().?, "ghi")); + try testing.expectEqualSlices(u8, it.rest(), "abc|def||ghi"); + try testing.expectEqualSlices(u8, it.next().?, "abc"); + + try testing.expectEqualSlices(u8, it.rest(), "def||ghi"); + try testing.expectEqualSlices(u8, it.next().?, "def"); + + try testing.expectEqualSlices(u8, it.rest(), "|ghi"); + try testing.expectEqualSlices(u8, it.next().?, ""); + + try testing.expectEqualSlices(u8, it.rest(), "ghi"); + try testing.expectEqualSlices(u8, it.next().?, "ghi"); + + try testing.expectEqualSlices(u8, it.rest(), ""); try testing.expect(it.next() == null); it = split(u8, "", "|"); - try testing.expect(eql(u8, it.next().?, "")); + try testing.expectEqualSlices(u8, it.next().?, ""); try testing.expect(it.next() == null); it = split(u8, "|", "|"); - try testing.expect(eql(u8, it.next().?, "")); - try testing.expect(eql(u8, it.next().?, "")); + try testing.expectEqualSlices(u8, it.next().?, ""); + try testing.expectEqualSlices(u8, it.next().?, ""); try testing.expect(it.next() == null); it = split(u8, "hello", " "); - try testing.expect(eql(u8, it.next().?, "hello")); + try testing.expectEqualSlices(u8, it.next().?, "hello"); try testing.expect(it.next() == null); var it16 = split( @@ -1731,17 +1740,18 @@ test "split" { std.unicode.utf8ToUtf16LeStringLiteral("hello"), std.unicode.utf8ToUtf16LeStringLiteral(" "), ); - try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("hello"))); + try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("hello")); try testing.expect(it16.next() == null); } test "split (multibyte)" { var it = split(u8, "a, b ,, c, d, e", ", "); - try testing.expect(eql(u8, it.next().?, "a")); - try testing.expect(eql(u8, it.next().?, "b ,")); - try testing.expect(eql(u8, it.next().?, "c")); - try testing.expect(eql(u8, it.next().?, "d")); - try testing.expect(eql(u8, it.next().?, "e")); + try testing.expectEqualSlices(u8, it.next().?, "a"); + try testing.expectEqualSlices(u8, it.rest(), "b ,, c, d, e"); + try testing.expectEqualSlices(u8, it.next().?, "b ,"); + try testing.expectEqualSlices(u8, it.next().?, "c"); + try testing.expectEqualSlices(u8, it.next().?, "d"); + try testing.expectEqualSlices(u8, it.next().?, "e"); try testing.expect(it.next() == null); var it16 = split( @@ -1749,11 +1759,99 @@ test "split (multibyte)" { std.unicode.utf8ToUtf16LeStringLiteral("a, b ,, c, d, e"), std.unicode.utf8ToUtf16LeStringLiteral(", "), ); - try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("a"))); - try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b ,"))); - try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c"))); - try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d"))); - try testing.expect(eql(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("e"))); + try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("a")); + try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b ,")); + try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c")); + try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d")); + try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("e")); + try testing.expect(it16.next() == null); +} + +/// Returns an iterator that iterates backwards over the slices of `buffer` +/// that are separated by bytes in `delimiter`. +/// splitBackwards(u8, "abc|def||ghi", "|") +/// will return slices for "ghi", "", "def", "abc", null, in that order. +/// If `delimiter` does not exist in buffer, +/// the iterator will return `buffer`, null, in that order. +/// The delimiter length must not be zero. +pub fn splitBackwards(comptime T: type, buffer: []const T, delimiter: []const T) SplitBackwardsIterator(T) { + assert(delimiter.len != 0); + return SplitBackwardsIterator(T){ + .index = buffer.len, + .buffer = buffer, + .delimiter = delimiter, + }; +} + +test "splitBackwards" { + var it = splitBackwards(u8, "abc|def||ghi", "|"); + try testing.expectEqualSlices(u8, it.rest(), "abc|def||ghi"); + try testing.expectEqualSlices(u8, it.next().?, "ghi"); + + try testing.expectEqualSlices(u8, it.rest(), "abc|def|"); + try testing.expectEqualSlices(u8, it.next().?, ""); + + try testing.expectEqualSlices(u8, it.rest(), "abc|def"); + try testing.expectEqualSlices(u8, it.next().?, "def"); + + try testing.expectEqualSlices(u8, it.rest(), "abc"); + try testing.expectEqualSlices(u8, it.next().?, "abc"); + + try testing.expectEqualSlices(u8, it.rest(), ""); + try testing.expect(it.next() == null); + + it = splitBackwards(u8, "", "|"); + try testing.expectEqualSlices(u8, it.next().?, ""); + try testing.expect(it.next() == null); + + it = splitBackwards(u8, "|", "|"); + try testing.expectEqualSlices(u8, it.next().?, ""); + try testing.expectEqualSlices(u8, it.next().?, ""); + try testing.expect(it.next() == null); + + it = splitBackwards(u8, "hello", " "); + try testing.expectEqualSlices(u8, it.next().?, "hello"); + try testing.expect(it.next() == null); + + var it16 = splitBackwards( + u16, + std.unicode.utf8ToUtf16LeStringLiteral("hello"), + std.unicode.utf8ToUtf16LeStringLiteral(" "), + ); + try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("hello")); + try testing.expect(it16.next() == null); +} + +test "splitBackwards (multibyte)" { + var it = splitBackwards(u8, "a, b ,, c, d, e", ", "); + try testing.expectEqualSlices(u8, it.rest(), "a, b ,, c, d, e"); + try testing.expectEqualSlices(u8, it.next().?, "e"); + + try testing.expectEqualSlices(u8, it.rest(), "a, b ,, c, d"); + try testing.expectEqualSlices(u8, it.next().?, "d"); + + try testing.expectEqualSlices(u8, it.rest(), "a, b ,, c"); + try testing.expectEqualSlices(u8, it.next().?, "c"); + + try testing.expectEqualSlices(u8, it.rest(), "a, b ,"); + try testing.expectEqualSlices(u8, it.next().?, "b ,"); + + try testing.expectEqualSlices(u8, it.rest(), "a"); + try testing.expectEqualSlices(u8, it.next().?, "a"); + + try testing.expectEqualSlices(u8, it.rest(), ""); + try testing.expect(it.next() == null); + + var it16 = splitBackwards( + u16, + std.unicode.utf8ToUtf16LeStringLiteral("a, b ,, c, d, e"), + std.unicode.utf8ToUtf16LeStringLiteral(", "), + ); + try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("e")); + try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("d")); + try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("c")); + try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("b ,")); + try testing.expectEqualSlices(u16, it16.next().?, std.unicode.utf8ToUtf16LeStringLiteral("a")); try testing.expect(it16.next() == null); } @@ -1862,6 +1960,35 @@ pub fn SplitIterator(comptime T: type) type { }; } +pub fn SplitBackwardsIterator(comptime T: type) type { + return struct { + buffer: []const T, + index: ?usize, + delimiter: []const T, + + const Self = @This(); + + /// Returns a slice of the next field, or null if splitting is complete. + pub fn next(self: *Self) ?[]const T { + const end = self.index orelse return null; + const start = if (lastIndexOf(T, self.buffer[0..end], self.delimiter)) |delim_start| blk: { + self.index = delim_start; + break :blk delim_start + self.delimiter.len; + } else blk: { + self.index = null; + break :blk 0; + }; + return self.buffer[start..end]; + } + + /// Returns a slice of the remaining bytes. Does not affect iterator state. + pub fn rest(self: Self) []const T { + const end = self.index orelse 0; + return self.buffer[0..end]; + } + }; +} + /// Naively combines a series of slices with a separator. /// Allocates memory for the result, which must be freed by the caller. pub fn join(allocator: Allocator, separator: []const u8, slices: []const []const u8) ![]u8 { From ea13437ac575329f1198f8422b856ebfcadff4c8 Mon Sep 17 00:00:00 2001 From: May B Date: Wed, 29 Jun 2022 11:53:01 +0200 Subject: [PATCH 1995/2031] std.json: Support disabling indent (#11823) Newline Delimited JSON (ndjson) expect compact json without newline inside its content Add None to StringfyOptions.indent and move newline writeByte inside StringfyOptions.outputIndent --- lib/std/json.zig | 26 ++++++++++++++++++++------ lib/std/json/write_stream.zig | 1 - 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/std/json.zig b/lib/std/json.zig index cc82c1d0a5..d20797e131 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -1321,7 +1321,6 @@ pub const Value = union(enum) { try out_stream.writeByte(','); } if (child_options.whitespace) |child_whitespace| { - try out_stream.writeByte('\n'); try child_whitespace.outputIndent(out_stream); } @@ -1336,7 +1335,6 @@ pub const Value = union(enum) { } if (field_output) { if (options.whitespace) |whitespace| { - try out_stream.writeByte('\n'); try whitespace.outputIndent(out_stream); } } @@ -2943,6 +2941,7 @@ pub const StringifyOptions = struct { indent: union(enum) { Space: u8, Tab: void, + None: void, } = .{ .Space = 4 }, /// After a colon, should whitespace be inserted? @@ -2963,7 +2962,9 @@ pub const StringifyOptions = struct { char = '\t'; n_chars = 1; }, + .None => return, } + try out_stream.writeByte('\n'); n_chars *= whitespace.indent_level; try out_stream.writeByteNTimes(char, n_chars); } @@ -3139,7 +3140,6 @@ pub fn stringify( try out_stream.writeByte(','); } if (child_options.whitespace) |child_whitespace| { - try out_stream.writeByte('\n'); try child_whitespace.outputIndent(out_stream); } try outputJsonString(Field.name, options, out_stream); @@ -3154,7 +3154,6 @@ pub fn stringify( } if (field_output) { if (options.whitespace) |whitespace| { - try out_stream.writeByte('\n'); try whitespace.outputIndent(out_stream); } } @@ -3190,14 +3189,12 @@ pub fn stringify( try out_stream.writeByte(','); } if (child_options.whitespace) |child_whitespace| { - try out_stream.writeByte('\n'); try child_whitespace.outputIndent(out_stream); } try stringify(x, child_options, out_stream); } if (value.len != 0) { if (options.whitespace) |whitespace| { - try out_stream.writeByte('\n'); try whitespace.outputIndent(out_stream); } } @@ -3368,6 +3365,23 @@ test "stringify struct with indentation" { }, }, ); + try teststringify( + \\{"foo":42,"bar":[1,2,3]} + , + struct { + foo: u32, + bar: [3]u32, + }{ + .foo = 42, + .bar = .{ 1, 2, 3 }, + }, + StringifyOptions{ + .whitespace = .{ + .indent = .None, + .separator = false, + }, + }, + ); } test "stringify struct with void field" { diff --git a/lib/std/json/write_stream.zig b/lib/std/json/write_stream.zig index 3393f8a6ee..298f640856 100644 --- a/lib/std/json/write_stream.zig +++ b/lib/std/json/write_stream.zig @@ -202,7 +202,6 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type { fn indent(self: *Self) !void { assert(self.state_index >= 1); - try self.stream.writeByte('\n'); try self.whitespace.outputIndent(self.stream); } From 234ccb4a5088cd6fa06144d7ba1aceab0596f1cc Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Wed, 29 Jun 2022 15:11:33 +0200 Subject: [PATCH 1996/2031] std.crypto.ecc: add support for the secp256k1 curve (#11880) std.crypto.ecc: add support for the secp256k1 curve Usage of the secp256k1 elliptic curve recently grew exponentially, since this is the curve used by Bitcoin and other popular blockchains such as Ethereum. With this, Zig has support for all the widely deployed elliptic curves today. --- lib/std/crypto.zig | 2 + lib/std/crypto/pcurves/common.zig | 11 + lib/std/crypto/pcurves/secp256k1.zig | 556 +++++ lib/std/crypto/pcurves/secp256k1/field.zig | 12 + lib/std/crypto/pcurves/secp256k1/scalar.zig | 227 ++ .../crypto/pcurves/secp256k1/secp256k1_64.zig | 1964 ++++++++++++++++ .../pcurves/secp256k1/secp256k1_scalar_64.zig | 2024 +++++++++++++++++ lib/std/crypto/pcurves/tests/secp256k1.zig | 137 ++ 8 files changed, 4933 insertions(+) create mode 100644 lib/std/crypto/pcurves/secp256k1.zig create mode 100644 lib/std/crypto/pcurves/secp256k1/field.zig create mode 100644 lib/std/crypto/pcurves/secp256k1/scalar.zig create mode 100644 lib/std/crypto/pcurves/secp256k1/secp256k1_64.zig create mode 100644 lib/std/crypto/pcurves/secp256k1/secp256k1_scalar_64.zig create mode 100644 lib/std/crypto/pcurves/tests/secp256k1.zig diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index b9191585ec..522a545fcf 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -64,6 +64,7 @@ pub const ecc = struct { pub const P256 = @import("crypto/pcurves/p256.zig").P256; pub const P384 = @import("crypto/pcurves/p384.zig").P384; pub const Ristretto255 = @import("crypto/25519/ristretto255.zig").Ristretto255; + pub const Secp256k1 = @import("crypto/pcurves/secp256k1.zig").Secp256k1; }; /// Hash functions. @@ -205,6 +206,7 @@ test { _ = ecc.P256; _ = ecc.P384; _ = ecc.Ristretto255; + _ = ecc.Secp256k1; _ = hash.blake2; _ = hash.Blake3; diff --git a/lib/std/crypto/pcurves/common.zig b/lib/std/crypto/pcurves/common.zig index 1160334fe4..5abc6d348f 100644 --- a/lib/std/crypto/pcurves/common.zig +++ b/lib/std/crypto/pcurves/common.zig @@ -295,6 +295,17 @@ pub fn Field(comptime params: FieldParams) type { const x63 = x32.sqn(31).mul(x31); const x126 = x63.sqn(63).mul(x63); return x126.sqn(126).mul(x126).sqn(3).mul(t111).sqn(33).mul(x32).sqn(64).mul(x2).sqn(30); + } else if (field_order == 115792089237316195423570985008687907853269984665640564039457584007908834671663) { + const t11 = x2.mul(x2.sq()); + const t1111 = t11.mul(t11.sqn(2)); + const t11111 = x2.mul(t1111.sq()); + const t1111111 = t11.mul(t11111.sqn(2)); + const x11 = t1111111.sqn(4).mul(t1111); + const x22 = x11.sqn(11).mul(x11); + const x27 = x22.sqn(5).mul(t11111); + const x54 = x27.sqn(27).mul(x27); + const x108 = x54.sqn(54).mul(x54); + return x108.sqn(108).mul(x108).sqn(7).mul(t1111111).sqn(23).mul(x22).sqn(6).mul(t11).sqn(2); } else { return x2.pow(std.meta.Int(.unsigned, field_bits), (field_order + 1) / 4); } diff --git a/lib/std/crypto/pcurves/secp256k1.zig b/lib/std/crypto/pcurves/secp256k1.zig new file mode 100644 index 0000000000..79698bd7dd --- /dev/null +++ b/lib/std/crypto/pcurves/secp256k1.zig @@ -0,0 +1,556 @@ +const std = @import("std"); +const crypto = std.crypto; +const math = std.math; +const mem = std.mem; +const meta = std.meta; + +const EncodingError = crypto.errors.EncodingError; +const IdentityElementError = crypto.errors.IdentityElementError; +const NonCanonicalError = crypto.errors.NonCanonicalError; +const NotSquareError = crypto.errors.NotSquareError; + +/// Group operations over secp256k1. +pub const Secp256k1 = struct { + /// The underlying prime field. + pub const Fe = @import("secp256k1/field.zig").Fe; + /// Field arithmetic mod the order of the main subgroup. + pub const scalar = @import("secp256k1/scalar.zig"); + + x: Fe, + y: Fe, + z: Fe = Fe.one, + + is_base: bool = false, + + /// The secp256k1 base point. + pub const basePoint = Secp256k1{ + .x = Fe.fromInt(55066263022277343669578718895168534326250603453777594175500187360389116729240) catch unreachable, + .y = Fe.fromInt(32670510020758816978083085130507043184471273380659243275938904335757337482424) catch unreachable, + .z = Fe.one, + .is_base = true, + }; + + /// The secp256k1 neutral element. + pub const identityElement = Secp256k1{ .x = Fe.zero, .y = Fe.one, .z = Fe.zero }; + + pub const B = Fe.fromInt(7) catch unreachable; + + pub const Endormorphism = struct { + const lambda: u256 = 37718080363155996902926221483475020450927657555482586988616620542887997980018; + const beta: u256 = 55594575648329892869085402983802832744385952214688224221778511981742606582254; + + const lambda_s = s: { + var buf: [32]u8 = undefined; + mem.writeIntLittle(u256, &buf, Endormorphism.lambda); + break :s buf; + }; + + pub const SplitScalar = struct { + r1: [32]u8, + r2: [32]u8, + }; + + /// Compute r1 and r2 so that k = r1 + r2*lambda (mod L). + pub fn splitScalar(s: [32]u8, endian: std.builtin.Endian) SplitScalar { + const b1_neg_s = comptime s: { + var buf: [32]u8 = undefined; + mem.writeIntLittle(u256, &buf, 303414439467246543595250775667605759171); + break :s buf; + }; + const b2_neg_s = comptime s: { + var buf: [32]u8 = undefined; + mem.writeIntLittle(u256, &buf, scalar.field_order - 64502973549206556628585045361533709077); + break :s buf; + }; + const k = mem.readInt(u256, &s, endian); + + const t1 = math.mulWide(u256, k, 21949224512762693861512883645436906316123769664773102907882521278123970637873); + const t2 = math.mulWide(u256, k, 103246583619904461035481197785446227098457807945486720222659797044629401272177); + + const c1 = @truncate(u128, t1 >> 384) + @truncate(u1, t1 >> 383); + const c2 = @truncate(u128, t2 >> 384) + @truncate(u1, t2 >> 383); + + var buf: [32]u8 = undefined; + + mem.writeIntLittle(u256, &buf, c1); + const c1x = scalar.mul(buf, b1_neg_s, .Little) catch unreachable; + + mem.writeIntLittle(u256, &buf, c2); + const c2x = scalar.mul(buf, b2_neg_s, .Little) catch unreachable; + + const r2 = scalar.add(c1x, c2x, .Little) catch unreachable; + + var r1 = scalar.mul(r2, lambda_s, .Little) catch unreachable; + r1 = scalar.sub(s, r1, .Little) catch unreachable; + + return SplitScalar{ .r1 = r1, .r2 = r2 }; + } + }; + + /// Reject the neutral element. + pub fn rejectIdentity(p: Secp256k1) IdentityElementError!void { + if (p.x.isZero()) { + return error.IdentityElement; + } + } + + /// Create a point from affine coordinates after checking that they match the curve equation. + pub fn fromAffineCoordinates(p: AffineCoordinates) EncodingError!Secp256k1 { + const x = p.x; + const y = p.y; + const x3B = x.sq().mul(x).add(B); + const yy = y.sq(); + const on_curve = @boolToInt(x3B.equivalent(yy)); + const is_identity = @boolToInt(x.equivalent(AffineCoordinates.identityElement.x)) & @boolToInt(y.equivalent(AffineCoordinates.identityElement.y)); + if ((on_curve | is_identity) == 0) { + return error.InvalidEncoding; + } + var ret = Secp256k1{ .x = x, .y = y, .z = Fe.one }; + ret.z.cMov(Secp256k1.identityElement.z, is_identity); + return ret; + } + + /// Create a point from serialized affine coordinates. + pub fn fromSerializedAffineCoordinates(xs: [32]u8, ys: [32]u8, endian: std.builtin.Endian) (NonCanonicalError || EncodingError)!Secp256k1 { + const x = try Fe.fromBytes(xs, endian); + const y = try Fe.fromBytes(ys, endian); + return fromAffineCoordinates(.{ .x = x, .y = y }); + } + + /// Recover the Y coordinate from the X coordinate. + pub fn recoverY(x: Fe, is_odd: bool) NotSquareError!Fe { + const x3B = x.sq().mul(x).add(B); + var y = try x3B.sqrt(); + const yn = y.neg(); + y.cMov(yn, @boolToInt(is_odd) ^ @boolToInt(y.isOdd())); + return y; + } + + /// Deserialize a SEC1-encoded point. + pub fn fromSec1(s: []const u8) (EncodingError || NotSquareError || NonCanonicalError)!Secp256k1 { + if (s.len < 1) return error.InvalidEncoding; + const encoding_type = s[0]; + const encoded = s[1..]; + switch (encoding_type) { + 0 => { + if (encoded.len != 0) return error.InvalidEncoding; + return Secp256k1.identityElement; + }, + 2, 3 => { + if (encoded.len != 32) return error.InvalidEncoding; + const x = try Fe.fromBytes(encoded[0..32].*, .Big); + const y_is_odd = (encoding_type == 3); + const y = try recoverY(x, y_is_odd); + return Secp256k1{ .x = x, .y = y }; + }, + 4 => { + if (encoded.len != 64) return error.InvalidEncoding; + const x = try Fe.fromBytes(encoded[0..32].*, .Big); + const y = try Fe.fromBytes(encoded[32..64].*, .Big); + return Secp256k1.fromAffineCoordinates(.{ .x = x, .y = y }); + }, + else => return error.InvalidEncoding, + } + } + + /// Serialize a point using the compressed SEC-1 format. + pub fn toCompressedSec1(p: Secp256k1) [33]u8 { + var out: [33]u8 = undefined; + const xy = p.affineCoordinates(); + out[0] = if (xy.y.isOdd()) 3 else 2; + mem.copy(u8, out[1..], &xy.x.toBytes(.Big)); + return out; + } + + /// Serialize a point using the uncompressed SEC-1 format. + pub fn toUncompressedSec1(p: Secp256k1) [65]u8 { + var out: [65]u8 = undefined; + out[0] = 4; + const xy = p.affineCoordinates(); + mem.copy(u8, out[1..33], &xy.x.toBytes(.Big)); + mem.copy(u8, out[33..65], &xy.y.toBytes(.Big)); + return out; + } + + /// Return a random point. + pub fn random() Secp256k1 { + const n = scalar.random(.Little); + return basePoint.mul(n, .Little) catch unreachable; + } + + /// Flip the sign of the X coordinate. + pub fn neg(p: Secp256k1) Secp256k1 { + return .{ .x = p.x, .y = p.y.neg(), .z = p.z }; + } + + /// Double a secp256k1 point. + // Algorithm 9 from https://eprint.iacr.org/2015/1060.pdf + pub fn dbl(p: Secp256k1) Secp256k1 { + var t0 = p.y.sq(); + var Z3 = t0.dbl(); + Z3 = Z3.dbl(); + Z3 = Z3.dbl(); + var t1 = p.y.mul(p.z); + var t2 = p.z.sq(); + // b3 = (2^2)^2 + 2^2 + 1 + const t2_4 = t2.dbl().dbl(); + t2 = t2_4.dbl().dbl().add(t2_4).add(t2); + var X3 = t2.mul(Z3); + var Y3 = t0.add(t2); + Z3 = t1.mul(Z3); + t1 = t2.dbl(); + t2 = t1.add(t2); + t0 = t0.sub(t2); + Y3 = t0.mul(Y3); + Y3 = X3.add(Y3); + t1 = p.x.mul(p.y); + X3 = t0.mul(t1); + X3 = X3.dbl(); + return .{ + .x = X3, + .y = Y3, + .z = Z3, + }; + } + + /// Add secp256k1 points, the second being specified using affine coordinates. + // Algorithm 8 from https://eprint.iacr.org/2015/1060.pdf + pub fn addMixed(p: Secp256k1, q: AffineCoordinates) Secp256k1 { + var t0 = p.x.mul(q.x); + var t1 = p.y.mul(q.y); + var t3 = q.x.add(q.y); + var t4 = p.x.add(p.y1); + t3 = t3.mul(t4); + t4 = t0.add(t1); + t3 = t3.sub(t4); + t4 = q.y.mul(p.z); + t4 = t4.add(p.y); + var Y3 = q.x.mul(p.z); + Y3 = Y3.add(p.x); + var X3 = t0.dbl(); + t0 = X3.add(t0); + // b3 = (2^2)^2 + 2^2 + 1 + const t2_4 = p.z.dbl().dbl(); + var t2 = t2_4.dbl().dbl().add(t2_4).add(p.z); + var Z3 = t1.add(t2); + t1 = t1.sub(t2); + const Y3_4 = Y3.dbl().dbl(); + Y3 = Y3_4.dbl().dbl().add(Y3_4).add(Y3); + X3 = t4.mul(Y3); + t2 = t3.mul(t1); + X3 = t2.sub(X3); + Y3 = Y3.mul(t0); + t1 = t1.mul(Z3); + Y3 = t1.add(Y3); + t0 = t0.mul(t3); + Z3 = Z3.mul(t4); + Z3 = Z3.add(t0); + + var ret = Secp256k1{ + .x = X3, + .y = Y3, + .z = Z3, + }; + ret.cMov(p, @boolToInt(q.x.isZero())); + return ret; + } + + /// Add secp256k1 points. + // Algorithm 7 from https://eprint.iacr.org/2015/1060.pdf + pub fn add(p: Secp256k1, q: Secp256k1) Secp256k1 { + var t0 = p.x.mul(q.x); + var t1 = p.y.mul(q.y); + var t2 = p.z.mul(q.z); + var t3 = p.x.add(p.y); + var t4 = q.x.add(q.y); + t3 = t3.mul(t4); + t4 = t0.add(t1); + t3 = t3.sub(t4); + t4 = p.y.add(p.z); + var X3 = q.y.add(q.z); + t4 = t4.mul(X3); + X3 = t1.add(t2); + t4 = t4.sub(X3); + X3 = p.x.add(p.z); + var Y3 = q.x.add(q.z); + X3 = X3.mul(Y3); + Y3 = t0.add(t2); + Y3 = X3.sub(Y3); + X3 = t0.dbl(); + t0 = X3.add(t0); + // b3 = (2^2)^2 + 2^2 + 1 + const t2_4 = t2.dbl().dbl(); + t2 = t2_4.dbl().dbl().add(t2_4).add(t2); + var Z3 = t1.add(t2); + t1 = t1.sub(t2); + const Y3_4 = Y3.dbl().dbl(); + Y3 = Y3_4.dbl().dbl().add(Y3_4).add(Y3); + X3 = t4.mul(Y3); + t2 = t3.mul(t1); + X3 = t2.sub(X3); + Y3 = Y3.mul(t0); + t1 = t1.mul(Z3); + Y3 = t1.add(Y3); + t0 = t0.mul(t3); + Z3 = Z3.mul(t4); + Z3 = Z3.add(t0); + + return .{ + .x = X3, + .y = Y3, + .z = Z3, + }; + } + + /// Subtract secp256k1 points. + pub fn sub(p: Secp256k1, q: Secp256k1) Secp256k1 { + return p.add(q.neg()); + } + + /// Subtract secp256k1 points, the second being specified using affine coordinates. + pub fn subMixed(p: Secp256k1, q: AffineCoordinates) Secp256k1 { + return p.addMixed(q.neg()); + } + + /// Return affine coordinates. + pub fn affineCoordinates(p: Secp256k1) AffineCoordinates { + const zinv = p.z.invert(); + var ret = AffineCoordinates{ + .x = p.x.mul(zinv), + .y = p.y.mul(zinv), + }; + ret.cMov(AffineCoordinates.identityElement, @boolToInt(p.x.isZero())); + return ret; + } + + /// Return true if both coordinate sets represent the same point. + pub fn equivalent(a: Secp256k1, b: Secp256k1) bool { + if (a.sub(b).rejectIdentity()) { + return false; + } else |_| { + return true; + } + } + + fn cMov(p: *Secp256k1, a: Secp256k1, c: u1) void { + p.x.cMov(a.x, c); + p.y.cMov(a.y, c); + p.z.cMov(a.z, c); + } + + fn pcSelect(comptime n: usize, pc: *const [n]Secp256k1, b: u8) Secp256k1 { + var t = Secp256k1.identityElement; + comptime var i: u8 = 1; + inline while (i < pc.len) : (i += 1) { + t.cMov(pc[i], @truncate(u1, (@as(usize, b ^ i) -% 1) >> 8)); + } + return t; + } + + fn slide(s: [32]u8) [2 * 32 + 1]i8 { + var e: [2 * 32 + 1]i8 = undefined; + for (s) |x, i| { + e[i * 2 + 0] = @as(i8, @truncate(u4, x)); + e[i * 2 + 1] = @as(i8, @truncate(u4, x >> 4)); + } + // Now, e[0..63] is between 0 and 15, e[63] is between 0 and 7 + var carry: i8 = 0; + for (e[0..64]) |*x| { + x.* += carry; + carry = (x.* + 8) >> 4; + x.* -= carry * 16; + std.debug.assert(x.* >= -8 and x.* <= 8); + } + e[64] = carry; + // Now, e[*] is between -8 and 8, including e[64] + std.debug.assert(carry >= -8 and carry <= 8); + return e; + } + + fn pcMul(pc: *const [9]Secp256k1, s: [32]u8, comptime vartime: bool) IdentityElementError!Secp256k1 { + std.debug.assert(vartime); + const e = slide(s); + var q = Secp256k1.identityElement; + var pos = e.len - 1; + while (true) : (pos -= 1) { + const slot = e[pos]; + if (slot > 0) { + q = q.add(pc[@intCast(usize, slot)]); + } else if (slot < 0) { + q = q.sub(pc[@intCast(usize, -slot)]); + } + if (pos == 0) break; + q = q.dbl().dbl().dbl().dbl(); + } + try q.rejectIdentity(); + return q; + } + + fn pcMul16(pc: *const [16]Secp256k1, s: [32]u8, comptime vartime: bool) IdentityElementError!Secp256k1 { + var q = Secp256k1.identityElement; + var pos: usize = 252; + while (true) : (pos -= 4) { + const slot = @truncate(u4, (s[pos >> 3] >> @truncate(u3, pos))); + if (vartime) { + if (slot != 0) { + q = q.add(pc[slot]); + } + } else { + q = q.add(pcSelect(16, pc, slot)); + } + if (pos == 0) break; + q = q.dbl().dbl().dbl().dbl(); + } + try q.rejectIdentity(); + return q; + } + + fn precompute(p: Secp256k1, comptime count: usize) [1 + count]Secp256k1 { + var pc: [1 + count]Secp256k1 = undefined; + pc[0] = Secp256k1.identityElement; + pc[1] = p; + var i: usize = 2; + while (i <= count) : (i += 1) { + pc[i] = if (i % 2 == 0) pc[i / 2].dbl() else pc[i - 1].add(p); + } + return pc; + } + + const basePointPc = pc: { + @setEvalBranchQuota(50000); + break :pc precompute(Secp256k1.basePoint, 15); + }; + + /// Multiply an elliptic curve point by a scalar. + /// Return error.IdentityElement if the result is the identity element. + pub fn mul(p: Secp256k1, s_: [32]u8, endian: std.builtin.Endian) IdentityElementError!Secp256k1 { + const s = if (endian == .Little) s_ else Fe.orderSwap(s_); + if (p.is_base) { + return pcMul16(&basePointPc, s, false); + } + try p.rejectIdentity(); + const pc = precompute(p, 15); + return pcMul16(&pc, s, false); + } + + /// Multiply an elliptic curve point by a *PUBLIC* scalar *IN VARIABLE TIME* + /// This can be used for signature verification. + pub fn mulPublic(p: Secp256k1, s_: [32]u8, endian: std.builtin.Endian) IdentityElementError!Secp256k1 { + const s = if (endian == .Little) s_ else Fe.orderSwap(s_); + const zero = comptime scalar.Scalar.zero.toBytes(.Little); + if (mem.eql(u8, &zero, &s)) { + return error.IdentityElement; + } + const pc = precompute(p, 8); + var lambda_p = try pcMul(&pc, Endormorphism.lambda_s, true); + var split_scalar = Endormorphism.splitScalar(s, .Little); + var px = p; + + // If a key is negative, flip the sign to keep it half-sized, + // and flip the sign of the Y point coordinate to compensate. + if (split_scalar.r1[split_scalar.r1.len / 2] != 0) { + split_scalar.r1 = scalar.neg(split_scalar.r1, .Little) catch zero; + px = px.neg(); + } + if (split_scalar.r2[split_scalar.r2.len / 2] != 0) { + split_scalar.r2 = scalar.neg(split_scalar.r2, .Little) catch zero; + lambda_p = lambda_p.neg(); + } + return mulDoubleBasePublicEndo(px, split_scalar.r1, lambda_p, split_scalar.r2); + } + + // Half-size double-base public multiplication when using the curve endomorphism. + // Scalars must be in little-endian. + // The second point is unlikely to be the generator, so don't even try to use the comptime table for it. + fn mulDoubleBasePublicEndo(p1: Secp256k1, s1: [32]u8, p2: Secp256k1, s2: [32]u8) IdentityElementError!Secp256k1 { + var pc1_array: [9]Secp256k1 = undefined; + const pc1 = if (p1.is_base) basePointPc[0..9] else pc: { + pc1_array = precompute(p1, 8); + break :pc &pc1_array; + }; + const pc2 = precompute(p2, 8); + std.debug.assert(s1[s1.len / 2] == 0); + std.debug.assert(s2[s2.len / 2] == 0); + const e1 = slide(s1); + const e2 = slide(s2); + var q = Secp256k1.identityElement; + var pos: usize = 2 * 32 / 2; // second half is all zero + while (true) : (pos -= 1) { + const slot1 = e1[pos]; + if (slot1 > 0) { + q = q.add(pc1[@intCast(usize, slot1)]); + } else if (slot1 < 0) { + q = q.sub(pc1[@intCast(usize, -slot1)]); + } + const slot2 = e2[pos]; + if (slot2 > 0) { + q = q.add(pc2[@intCast(usize, slot2)]); + } else if (slot2 < 0) { + q = q.sub(pc2[@intCast(usize, -slot2)]); + } + if (pos == 0) break; + q = q.dbl().dbl().dbl().dbl(); + } + try q.rejectIdentity(); + return q; + } + + /// Double-base multiplication of public parameters - Compute (p1*s1)+(p2*s2) *IN VARIABLE TIME* + /// This can be used for signature verification. + pub fn mulDoubleBasePublic(p1: Secp256k1, s1_: [32]u8, p2: Secp256k1, s2_: [32]u8, endian: std.builtin.Endian) IdentityElementError!Secp256k1 { + const s1 = if (endian == .Little) s1_ else Fe.orderSwap(s1_); + const s2 = if (endian == .Little) s2_ else Fe.orderSwap(s2_); + try p1.rejectIdentity(); + var pc1_array: [9]Secp256k1 = undefined; + const pc1 = if (p1.is_base) basePointPc[0..9] else pc: { + pc1_array = precompute(p1, 8); + break :pc &pc1_array; + }; + try p2.rejectIdentity(); + var pc2_array: [9]Secp256k1 = undefined; + const pc2 = if (p2.is_base) basePointPc[0..9] else pc: { + pc2_array = precompute(p2, 8); + break :pc &pc2_array; + }; + const e1 = slide(s1); + const e2 = slide(s2); + var q = Secp256k1.identityElement; + var pos: usize = 2 * 32; + while (true) : (pos -= 1) { + const slot1 = e1[pos]; + if (slot1 > 0) { + q = q.add(pc1[@intCast(usize, slot1)]); + } else if (slot1 < 0) { + q = q.sub(pc1[@intCast(usize, -slot1)]); + } + const slot2 = e2[pos]; + if (slot2 > 0) { + q = q.add(pc2[@intCast(usize, slot2)]); + } else if (slot2 < 0) { + q = q.sub(pc2[@intCast(usize, -slot2)]); + } + if (pos == 0) break; + q = q.dbl().dbl().dbl().dbl(); + } + try q.rejectIdentity(); + return q; + } +}; + +/// A point in affine coordinates. +pub const AffineCoordinates = struct { + x: Secp256k1.Fe, + y: Secp256k1.Fe, + + /// Identity element in affine coordinates. + pub const identityElement = AffineCoordinates{ .x = Secp256k1.identityElement.x, .y = Secp256k1.identityElement.y }; + + fn cMov(p: *AffineCoordinates, a: AffineCoordinates, c: u1) void { + p.x.cMov(a.x, c); + p.y.cMov(a.y, c); + } +}; + +test "secp256k1" { + _ = @import("tests/secp256k1.zig"); +} diff --git a/lib/std/crypto/pcurves/secp256k1/field.zig b/lib/std/crypto/pcurves/secp256k1/field.zig new file mode 100644 index 0000000000..e8113196c6 --- /dev/null +++ b/lib/std/crypto/pcurves/secp256k1/field.zig @@ -0,0 +1,12 @@ +const std = @import("std"); +const common = @import("../common.zig"); + +const Field = common.Field; + +pub const Fe = Field(.{ + .fiat = @import("secp256k1_64.zig"), + .field_order = 115792089237316195423570985008687907853269984665640564039457584007908834671663, + .field_bits = 256, + .saturated_bits = 256, + .encoded_length = 32, +}); diff --git a/lib/std/crypto/pcurves/secp256k1/scalar.zig b/lib/std/crypto/pcurves/secp256k1/scalar.zig new file mode 100644 index 0000000000..2d91f8bc99 --- /dev/null +++ b/lib/std/crypto/pcurves/secp256k1/scalar.zig @@ -0,0 +1,227 @@ +const std = @import("std"); +const common = @import("../common.zig"); +const crypto = std.crypto; +const debug = std.debug; +const math = std.math; +const mem = std.mem; + +const Field = common.Field; + +const NonCanonicalError = std.crypto.errors.NonCanonicalError; +const NotSquareError = std.crypto.errors.NotSquareError; + +/// Number of bytes required to encode a scalar. +pub const encoded_length = 32; + +/// A compressed scalar, in canonical form. +pub const CompressedScalar = [encoded_length]u8; + +const Fe = Field(.{ + .fiat = @import("secp256k1_scalar_64.zig"), + .field_order = 115792089237316195423570985008687907852837564279074904382605163141518161494337, + .field_bits = 256, + .saturated_bits = 256, + .encoded_length = encoded_length, +}); + +/// The scalar field order. +pub const field_order = Fe.field_order; + +/// Reject a scalar whose encoding is not canonical. +pub fn rejectNonCanonical(s: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!void { + return Fe.rejectNonCanonical(s, endian); +} + +/// Reduce a 48-bytes scalar to the field size. +pub fn reduce48(s: [48]u8, endian: std.builtin.Endian) CompressedScalar { + return Scalar.fromBytes48(s, endian).toBytes(endian); +} + +/// Reduce a 64-bytes scalar to the field size. +pub fn reduce64(s: [64]u8, endian: std.builtin.Endian) CompressedScalar { + return ScalarDouble.fromBytes64(s, endian).toBytes(endian); +} + +/// Return a*b (mod L) +pub fn mul(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!CompressedScalar { + return (try Scalar.fromBytes(a, endian)).mul(try Scalar.fromBytes(b, endian)).toBytes(endian); +} + +/// Return a*b+c (mod L) +pub fn mulAdd(a: CompressedScalar, b: CompressedScalar, c: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!CompressedScalar { + return (try Scalar.fromBytes(a, endian)).mul(try Scalar.fromBytes(b, endian)).add(try Scalar.fromBytes(c, endian)).toBytes(endian); +} + +/// Return a+b (mod L) +pub fn add(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!CompressedScalar { + return (try Scalar.fromBytes(a, endian)).add(try Scalar.fromBytes(b, endian)).toBytes(endian); +} + +/// Return -s (mod L) +pub fn neg(s: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!CompressedScalar { + return (try Scalar.fromBytes(s, endian)).neg().toBytes(endian); +} + +/// Return (a-b) (mod L) +pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!CompressedScalar { + return (try Scalar.fromBytes(a, endian)).sub(try Scalar.fromBytes(b, endian)).toBytes(endian); +} + +/// Return a random scalar +pub fn random(endian: std.builtin.Endian) CompressedScalar { + return Scalar.random().toBytes(endian); +} + +/// A scalar in unpacked representation. +pub const Scalar = struct { + fe: Fe, + + /// Zero. + pub const zero = Scalar{ .fe = Fe.zero }; + + /// One. + pub const one = Scalar{ .fe = Fe.one }; + + /// Unpack a serialized representation of a scalar. + pub fn fromBytes(s: CompressedScalar, endian: std.builtin.Endian) NonCanonicalError!Scalar { + return Scalar{ .fe = try Fe.fromBytes(s, endian) }; + } + + /// Reduce a 384 bit input to the field size. + pub fn fromBytes48(s: [48]u8, endian: std.builtin.Endian) Scalar { + const t = ScalarDouble.fromBytes(384, s, endian); + return t.reduce(384); + } + + /// Reduce a 512 bit input to the field size. + pub fn fromBytes64(s: [64]u8, endian: std.builtin.Endian) Scalar { + const t = ScalarDouble.fromBytes(512, s, endian); + return t.reduce(512); + } + + /// Pack a scalar into bytes. + pub fn toBytes(n: Scalar, endian: std.builtin.Endian) CompressedScalar { + return n.fe.toBytes(endian); + } + + /// Return true if the scalar is zero.. + pub fn isZero(n: Scalar) bool { + return n.fe.isZero(); + } + + /// Return true if a and b are equivalent. + pub fn equivalent(a: Scalar, b: Scalar) bool { + return a.fe.equivalent(b.fe); + } + + /// Compute x+y (mod L) + pub fn add(x: Scalar, y: Scalar) Scalar { + return Scalar{ .fe = x.fe.add(y.fe) }; + } + + /// Compute x-y (mod L) + pub fn sub(x: Scalar, y: Scalar) Scalar { + return Scalar{ .fe = x.fe.sub(y.fe) }; + } + + /// Compute 2n (mod L) + pub fn dbl(n: Scalar) Scalar { + return Scalar{ .fe = n.fe.dbl() }; + } + + /// Compute x*y (mod L) + pub fn mul(x: Scalar, y: Scalar) Scalar { + return Scalar{ .fe = x.fe.mul(y.fe) }; + } + + /// Compute x^2 (mod L) + pub fn sq(n: Scalar) Scalar { + return Scalar{ .fe = n.fe.sq() }; + } + + /// Compute x^n (mod L) + pub fn pow(a: Scalar, comptime T: type, comptime n: T) Scalar { + return Scalar{ .fe = a.fe.pow(n) }; + } + + /// Compute -x (mod L) + pub fn neg(n: Scalar) Scalar { + return Scalar{ .fe = n.fe.neg() }; + } + + /// Compute x^-1 (mod L) + pub fn invert(n: Scalar) Scalar { + return Scalar{ .fe = n.fe.invert() }; + } + + /// Return true if n is a quadratic residue mod L. + pub fn isSquare(n: Scalar) Scalar { + return n.fe.isSquare(); + } + + /// Return the square root of L, or NotSquare if there isn't any solutions. + pub fn sqrt(n: Scalar) NotSquareError!Scalar { + return Scalar{ .fe = try n.fe.sqrt() }; + } + + /// Return a random scalar < L. + pub fn random() Scalar { + var s: [48]u8 = undefined; + while (true) { + crypto.random.bytes(&s); + const n = Scalar.fromBytes48(s, .Little); + if (!n.isZero()) { + return n; + } + } + } +}; + +const ScalarDouble = struct { + x1: Fe, + x2: Fe, + x3: Fe, + + fn fromBytes(comptime bits: usize, s_: [bits / 8]u8, endian: std.builtin.Endian) ScalarDouble { + debug.assert(bits > 0 and bits <= 512 and bits >= Fe.saturated_bits and bits <= Fe.saturated_bits * 3); + + var s = s_; + if (endian == .Big) { + for (s_) |x, i| s[s.len - 1 - i] = x; + } + var t = ScalarDouble{ .x1 = undefined, .x2 = Fe.zero, .x3 = Fe.zero }; + { + var b = [_]u8{0} ** encoded_length; + const len = math.min(s.len, 24); + mem.copy(u8, b[0..len], s[0..len]); + t.x1 = Fe.fromBytes(b, .Little) catch unreachable; + } + if (s_.len >= 24) { + var b = [_]u8{0} ** encoded_length; + const len = math.min(s.len - 24, 24); + mem.copy(u8, b[0..len], s[24..][0..len]); + t.x2 = Fe.fromBytes(b, .Little) catch unreachable; + } + if (s_.len >= 48) { + var b = [_]u8{0} ** encoded_length; + const len = s.len - 48; + mem.copy(u8, b[0..len], s[48..][0..len]); + t.x3 = Fe.fromBytes(b, .Little) catch unreachable; + } + return t; + } + + fn reduce(expanded: ScalarDouble, comptime bits: usize) Scalar { + debug.assert(bits > 0 and bits <= Fe.saturated_bits * 3 and bits <= 512); + var fe = expanded.x1; + if (bits >= 192) { + const st1 = Fe.fromInt(1 << 192) catch unreachable; + fe = fe.add(expanded.x2.mul(st1)); + if (bits >= 384) { + const st2 = st1.sq(); + fe = fe.add(expanded.x3.mul(st2)); + } + } + return Scalar{ .fe = fe }; + } +}; diff --git a/lib/std/crypto/pcurves/secp256k1/secp256k1_64.zig b/lib/std/crypto/pcurves/secp256k1/secp256k1_64.zig new file mode 100644 index 0000000000..2309b48ac9 --- /dev/null +++ b/lib/std/crypto/pcurves/secp256k1/secp256k1_64.zig @@ -0,0 +1,1964 @@ +// Autogenerated: 'src/ExtractionOCaml/word_by_word_montgomery' --lang Zig --internal-static --public-function-case camelCase --private-function-case camelCase --public-type-case UpperCamelCase --private-type-case UpperCamelCase --no-prefix-fiat --package-name secp256k1 '' 64 '2^256 - 2^32 - 977' mul square add sub opp from_montgomery to_montgomery nonzero selectznz to_bytes from_bytes one msat divstep divstep_precomp +// curve description (via package name): secp256k1 +// machine_wordsize = 64 (from "64") +// requested operations: mul, square, add, sub, opp, from_montgomery, to_montgomery, nonzero, selectznz, to_bytes, from_bytes, one, msat, divstep, divstep_precomp +// m = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f (from "2^256 - 2^32 - 977") +// +// NOTE: In addition to the bounds specified above each function, all +// functions synthesized for this Montgomery arithmetic require the +// input to be strictly less than the prime modulus (m), and also +// require the input to be in the unique saturated representation. +// All functions also ensure that these two properties are true of +// return values. +// +// Computed values: +// eval z = z[0] + (z[1] << 64) + (z[2] << 128) + (z[3] << 192) +// bytes_eval z = z[0] + (z[1] << 8) + (z[2] << 16) + (z[3] << 24) + (z[4] << 32) + (z[5] << 40) + (z[6] << 48) + (z[7] << 56) + (z[8] << 64) + (z[9] << 72) + (z[10] << 80) + (z[11] << 88) + (z[12] << 96) + (z[13] << 104) + (z[14] << 112) + (z[15] << 120) + (z[16] << 128) + (z[17] << 136) + (z[18] << 144) + (z[19] << 152) + (z[20] << 160) + (z[21] << 168) + (z[22] << 176) + (z[23] << 184) + (z[24] << 192) + (z[25] << 200) + (z[26] << 208) + (z[27] << 216) + (z[28] << 224) + (z[29] << 232) + (z[30] << 240) + (z[31] << 248) +// twos_complement_eval z = let x1 := z[0] + (z[1] << 64) + (z[2] << 128) + (z[3] << 192) in +// if x1 & (2^256-1) < 2^255 then x1 & (2^256-1) else (x1 & (2^256-1)) - 2^256 + +const std = @import("std"); +const mode = @import("builtin").mode; // Checked arithmetic is disabled in non-debug modes to avoid side channels + +// The type MontgomeryDomainFieldElement is a field element in the Montgomery domain. +// Bounds: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub const MontgomeryDomainFieldElement = [4]u64; + +// The type NonMontgomeryDomainFieldElement is a field element NOT in the Montgomery domain. +// Bounds: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub const NonMontgomeryDomainFieldElement = [4]u64; + +/// The function addcarryxU64 is an addition with carry. +/// +/// Postconditions: +/// out1 = (arg1 + arg2 + arg3) mod 2^64 +/// out2 = ⌊(arg1 + arg2 + arg3) / 2^64⌋ +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// arg3: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [0x0 ~> 0x1] +inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { + @setRuntimeSafety(mode == .Debug); + + var t: u64 = undefined; + const carry1 = @addWithOverflow(u64, arg2, arg3, &t); + const carry2 = @addWithOverflow(u64, t, arg1, out1); + out2.* = @boolToInt(carry1) | @boolToInt(carry2); +} + +/// The function subborrowxU64 is a subtraction with borrow. +/// +/// Postconditions: +/// out1 = (-arg1 + arg2 + -arg3) mod 2^64 +/// out2 = -⌊(-arg1 + arg2 + -arg3) / 2^64⌋ +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// arg3: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [0x0 ~> 0x1] +inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { + @setRuntimeSafety(mode == .Debug); + + var t: u64 = undefined; + const carry1 = @subWithOverflow(u64, arg2, arg3, &t); + const carry2 = @subWithOverflow(u64, t, arg1, out1); + out2.* = @boolToInt(carry1) | @boolToInt(carry2); +} + +/// The function mulxU64 is a multiplication, returning the full double-width result. +/// +/// Postconditions: +/// out1 = (arg1 * arg2) mod 2^64 +/// out2 = ⌊arg1 * arg2 / 2^64⌋ +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0xffffffffffffffff] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [0x0 ~> 0xffffffffffffffff] +inline fn mulxU64(out1: *u64, out2: *u64, arg1: u64, arg2: u64) void { + @setRuntimeSafety(mode == .Debug); + + const x = @as(u128, arg1) * @as(u128, arg2); + out1.* = @truncate(u64, x); + out2.* = @truncate(u64, x >> 64); +} + +/// The function cmovznzU64 is a single-word conditional move. +/// +/// Postconditions: +/// out1 = (if arg1 = 0 then arg2 else arg3) +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// arg3: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +inline fn cmovznzU64(out1: *u64, arg1: u1, arg2: u64, arg3: u64) void { + @setRuntimeSafety(mode == .Debug); + + const mask = 0 -% @as(u64, arg1); + out1.* = (mask & arg3) | ((~mask) & arg2); +} + +/// The function mul multiplies two field elements in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// 0 ≤ eval arg2 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m +/// 0 ≤ eval out1 < m +/// +pub fn mul(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement, arg2: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[1]); + const x2 = (arg1[2]); + const x3 = (arg1[3]); + const x4 = (arg1[0]); + var x5: u64 = undefined; + var x6: u64 = undefined; + mulxU64(&x5, &x6, x4, (arg2[3])); + var x7: u64 = undefined; + var x8: u64 = undefined; + mulxU64(&x7, &x8, x4, (arg2[2])); + var x9: u64 = undefined; + var x10: u64 = undefined; + mulxU64(&x9, &x10, x4, (arg2[1])); + var x11: u64 = undefined; + var x12: u64 = undefined; + mulxU64(&x11, &x12, x4, (arg2[0])); + var x13: u64 = undefined; + var x14: u1 = undefined; + addcarryxU64(&x13, &x14, 0x0, x12, x9); + var x15: u64 = undefined; + var x16: u1 = undefined; + addcarryxU64(&x15, &x16, x14, x10, x7); + var x17: u64 = undefined; + var x18: u1 = undefined; + addcarryxU64(&x17, &x18, x16, x8, x5); + const x19 = (@as(u64, x18) + x6); + var x20: u64 = undefined; + var x21: u64 = undefined; + mulxU64(&x20, &x21, x11, 0xd838091dd2253531); + var x22: u64 = undefined; + var x23: u64 = undefined; + mulxU64(&x22, &x23, x20, 0xffffffffffffffff); + var x24: u64 = undefined; + var x25: u64 = undefined; + mulxU64(&x24, &x25, x20, 0xffffffffffffffff); + var x26: u64 = undefined; + var x27: u64 = undefined; + mulxU64(&x26, &x27, x20, 0xffffffffffffffff); + var x28: u64 = undefined; + var x29: u64 = undefined; + mulxU64(&x28, &x29, x20, 0xfffffffefffffc2f); + var x30: u64 = undefined; + var x31: u1 = undefined; + addcarryxU64(&x30, &x31, 0x0, x29, x26); + var x32: u64 = undefined; + var x33: u1 = undefined; + addcarryxU64(&x32, &x33, x31, x27, x24); + var x34: u64 = undefined; + var x35: u1 = undefined; + addcarryxU64(&x34, &x35, x33, x25, x22); + const x36 = (@as(u64, x35) + x23); + var x37: u64 = undefined; + var x38: u1 = undefined; + addcarryxU64(&x37, &x38, 0x0, x11, x28); + var x39: u64 = undefined; + var x40: u1 = undefined; + addcarryxU64(&x39, &x40, x38, x13, x30); + var x41: u64 = undefined; + var x42: u1 = undefined; + addcarryxU64(&x41, &x42, x40, x15, x32); + var x43: u64 = undefined; + var x44: u1 = undefined; + addcarryxU64(&x43, &x44, x42, x17, x34); + var x45: u64 = undefined; + var x46: u1 = undefined; + addcarryxU64(&x45, &x46, x44, x19, x36); + var x47: u64 = undefined; + var x48: u64 = undefined; + mulxU64(&x47, &x48, x1, (arg2[3])); + var x49: u64 = undefined; + var x50: u64 = undefined; + mulxU64(&x49, &x50, x1, (arg2[2])); + var x51: u64 = undefined; + var x52: u64 = undefined; + mulxU64(&x51, &x52, x1, (arg2[1])); + var x53: u64 = undefined; + var x54: u64 = undefined; + mulxU64(&x53, &x54, x1, (arg2[0])); + var x55: u64 = undefined; + var x56: u1 = undefined; + addcarryxU64(&x55, &x56, 0x0, x54, x51); + var x57: u64 = undefined; + var x58: u1 = undefined; + addcarryxU64(&x57, &x58, x56, x52, x49); + var x59: u64 = undefined; + var x60: u1 = undefined; + addcarryxU64(&x59, &x60, x58, x50, x47); + const x61 = (@as(u64, x60) + x48); + var x62: u64 = undefined; + var x63: u1 = undefined; + addcarryxU64(&x62, &x63, 0x0, x39, x53); + var x64: u64 = undefined; + var x65: u1 = undefined; + addcarryxU64(&x64, &x65, x63, x41, x55); + var x66: u64 = undefined; + var x67: u1 = undefined; + addcarryxU64(&x66, &x67, x65, x43, x57); + var x68: u64 = undefined; + var x69: u1 = undefined; + addcarryxU64(&x68, &x69, x67, x45, x59); + var x70: u64 = undefined; + var x71: u1 = undefined; + addcarryxU64(&x70, &x71, x69, @as(u64, x46), x61); + var x72: u64 = undefined; + var x73: u64 = undefined; + mulxU64(&x72, &x73, x62, 0xd838091dd2253531); + var x74: u64 = undefined; + var x75: u64 = undefined; + mulxU64(&x74, &x75, x72, 0xffffffffffffffff); + var x76: u64 = undefined; + var x77: u64 = undefined; + mulxU64(&x76, &x77, x72, 0xffffffffffffffff); + var x78: u64 = undefined; + var x79: u64 = undefined; + mulxU64(&x78, &x79, x72, 0xffffffffffffffff); + var x80: u64 = undefined; + var x81: u64 = undefined; + mulxU64(&x80, &x81, x72, 0xfffffffefffffc2f); + var x82: u64 = undefined; + var x83: u1 = undefined; + addcarryxU64(&x82, &x83, 0x0, x81, x78); + var x84: u64 = undefined; + var x85: u1 = undefined; + addcarryxU64(&x84, &x85, x83, x79, x76); + var x86: u64 = undefined; + var x87: u1 = undefined; + addcarryxU64(&x86, &x87, x85, x77, x74); + const x88 = (@as(u64, x87) + x75); + var x89: u64 = undefined; + var x90: u1 = undefined; + addcarryxU64(&x89, &x90, 0x0, x62, x80); + var x91: u64 = undefined; + var x92: u1 = undefined; + addcarryxU64(&x91, &x92, x90, x64, x82); + var x93: u64 = undefined; + var x94: u1 = undefined; + addcarryxU64(&x93, &x94, x92, x66, x84); + var x95: u64 = undefined; + var x96: u1 = undefined; + addcarryxU64(&x95, &x96, x94, x68, x86); + var x97: u64 = undefined; + var x98: u1 = undefined; + addcarryxU64(&x97, &x98, x96, x70, x88); + const x99 = (@as(u64, x98) + @as(u64, x71)); + var x100: u64 = undefined; + var x101: u64 = undefined; + mulxU64(&x100, &x101, x2, (arg2[3])); + var x102: u64 = undefined; + var x103: u64 = undefined; + mulxU64(&x102, &x103, x2, (arg2[2])); + var x104: u64 = undefined; + var x105: u64 = undefined; + mulxU64(&x104, &x105, x2, (arg2[1])); + var x106: u64 = undefined; + var x107: u64 = undefined; + mulxU64(&x106, &x107, x2, (arg2[0])); + var x108: u64 = undefined; + var x109: u1 = undefined; + addcarryxU64(&x108, &x109, 0x0, x107, x104); + var x110: u64 = undefined; + var x111: u1 = undefined; + addcarryxU64(&x110, &x111, x109, x105, x102); + var x112: u64 = undefined; + var x113: u1 = undefined; + addcarryxU64(&x112, &x113, x111, x103, x100); + const x114 = (@as(u64, x113) + x101); + var x115: u64 = undefined; + var x116: u1 = undefined; + addcarryxU64(&x115, &x116, 0x0, x91, x106); + var x117: u64 = undefined; + var x118: u1 = undefined; + addcarryxU64(&x117, &x118, x116, x93, x108); + var x119: u64 = undefined; + var x120: u1 = undefined; + addcarryxU64(&x119, &x120, x118, x95, x110); + var x121: u64 = undefined; + var x122: u1 = undefined; + addcarryxU64(&x121, &x122, x120, x97, x112); + var x123: u64 = undefined; + var x124: u1 = undefined; + addcarryxU64(&x123, &x124, x122, x99, x114); + var x125: u64 = undefined; + var x126: u64 = undefined; + mulxU64(&x125, &x126, x115, 0xd838091dd2253531); + var x127: u64 = undefined; + var x128: u64 = undefined; + mulxU64(&x127, &x128, x125, 0xffffffffffffffff); + var x129: u64 = undefined; + var x130: u64 = undefined; + mulxU64(&x129, &x130, x125, 0xffffffffffffffff); + var x131: u64 = undefined; + var x132: u64 = undefined; + mulxU64(&x131, &x132, x125, 0xffffffffffffffff); + var x133: u64 = undefined; + var x134: u64 = undefined; + mulxU64(&x133, &x134, x125, 0xfffffffefffffc2f); + var x135: u64 = undefined; + var x136: u1 = undefined; + addcarryxU64(&x135, &x136, 0x0, x134, x131); + var x137: u64 = undefined; + var x138: u1 = undefined; + addcarryxU64(&x137, &x138, x136, x132, x129); + var x139: u64 = undefined; + var x140: u1 = undefined; + addcarryxU64(&x139, &x140, x138, x130, x127); + const x141 = (@as(u64, x140) + x128); + var x142: u64 = undefined; + var x143: u1 = undefined; + addcarryxU64(&x142, &x143, 0x0, x115, x133); + var x144: u64 = undefined; + var x145: u1 = undefined; + addcarryxU64(&x144, &x145, x143, x117, x135); + var x146: u64 = undefined; + var x147: u1 = undefined; + addcarryxU64(&x146, &x147, x145, x119, x137); + var x148: u64 = undefined; + var x149: u1 = undefined; + addcarryxU64(&x148, &x149, x147, x121, x139); + var x150: u64 = undefined; + var x151: u1 = undefined; + addcarryxU64(&x150, &x151, x149, x123, x141); + const x152 = (@as(u64, x151) + @as(u64, x124)); + var x153: u64 = undefined; + var x154: u64 = undefined; + mulxU64(&x153, &x154, x3, (arg2[3])); + var x155: u64 = undefined; + var x156: u64 = undefined; + mulxU64(&x155, &x156, x3, (arg2[2])); + var x157: u64 = undefined; + var x158: u64 = undefined; + mulxU64(&x157, &x158, x3, (arg2[1])); + var x159: u64 = undefined; + var x160: u64 = undefined; + mulxU64(&x159, &x160, x3, (arg2[0])); + var x161: u64 = undefined; + var x162: u1 = undefined; + addcarryxU64(&x161, &x162, 0x0, x160, x157); + var x163: u64 = undefined; + var x164: u1 = undefined; + addcarryxU64(&x163, &x164, x162, x158, x155); + var x165: u64 = undefined; + var x166: u1 = undefined; + addcarryxU64(&x165, &x166, x164, x156, x153); + const x167 = (@as(u64, x166) + x154); + var x168: u64 = undefined; + var x169: u1 = undefined; + addcarryxU64(&x168, &x169, 0x0, x144, x159); + var x170: u64 = undefined; + var x171: u1 = undefined; + addcarryxU64(&x170, &x171, x169, x146, x161); + var x172: u64 = undefined; + var x173: u1 = undefined; + addcarryxU64(&x172, &x173, x171, x148, x163); + var x174: u64 = undefined; + var x175: u1 = undefined; + addcarryxU64(&x174, &x175, x173, x150, x165); + var x176: u64 = undefined; + var x177: u1 = undefined; + addcarryxU64(&x176, &x177, x175, x152, x167); + var x178: u64 = undefined; + var x179: u64 = undefined; + mulxU64(&x178, &x179, x168, 0xd838091dd2253531); + var x180: u64 = undefined; + var x181: u64 = undefined; + mulxU64(&x180, &x181, x178, 0xffffffffffffffff); + var x182: u64 = undefined; + var x183: u64 = undefined; + mulxU64(&x182, &x183, x178, 0xffffffffffffffff); + var x184: u64 = undefined; + var x185: u64 = undefined; + mulxU64(&x184, &x185, x178, 0xffffffffffffffff); + var x186: u64 = undefined; + var x187: u64 = undefined; + mulxU64(&x186, &x187, x178, 0xfffffffefffffc2f); + var x188: u64 = undefined; + var x189: u1 = undefined; + addcarryxU64(&x188, &x189, 0x0, x187, x184); + var x190: u64 = undefined; + var x191: u1 = undefined; + addcarryxU64(&x190, &x191, x189, x185, x182); + var x192: u64 = undefined; + var x193: u1 = undefined; + addcarryxU64(&x192, &x193, x191, x183, x180); + const x194 = (@as(u64, x193) + x181); + var x195: u64 = undefined; + var x196: u1 = undefined; + addcarryxU64(&x195, &x196, 0x0, x168, x186); + var x197: u64 = undefined; + var x198: u1 = undefined; + addcarryxU64(&x197, &x198, x196, x170, x188); + var x199: u64 = undefined; + var x200: u1 = undefined; + addcarryxU64(&x199, &x200, x198, x172, x190); + var x201: u64 = undefined; + var x202: u1 = undefined; + addcarryxU64(&x201, &x202, x200, x174, x192); + var x203: u64 = undefined; + var x204: u1 = undefined; + addcarryxU64(&x203, &x204, x202, x176, x194); + const x205 = (@as(u64, x204) + @as(u64, x177)); + var x206: u64 = undefined; + var x207: u1 = undefined; + subborrowxU64(&x206, &x207, 0x0, x197, 0xfffffffefffffc2f); + var x208: u64 = undefined; + var x209: u1 = undefined; + subborrowxU64(&x208, &x209, x207, x199, 0xffffffffffffffff); + var x210: u64 = undefined; + var x211: u1 = undefined; + subborrowxU64(&x210, &x211, x209, x201, 0xffffffffffffffff); + var x212: u64 = undefined; + var x213: u1 = undefined; + subborrowxU64(&x212, &x213, x211, x203, 0xffffffffffffffff); + var x214: u64 = undefined; + var x215: u1 = undefined; + subborrowxU64(&x214, &x215, x213, x205, 0x0); + var x216: u64 = undefined; + cmovznzU64(&x216, x215, x206, x197); + var x217: u64 = undefined; + cmovznzU64(&x217, x215, x208, x199); + var x218: u64 = undefined; + cmovznzU64(&x218, x215, x210, x201); + var x219: u64 = undefined; + cmovznzU64(&x219, x215, x212, x203); + out1[0] = x216; + out1[1] = x217; + out1[2] = x218; + out1[3] = x219; +} + +/// The function square squares a field element in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg1)) mod m +/// 0 ≤ eval out1 < m +/// +pub fn square(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[1]); + const x2 = (arg1[2]); + const x3 = (arg1[3]); + const x4 = (arg1[0]); + var x5: u64 = undefined; + var x6: u64 = undefined; + mulxU64(&x5, &x6, x4, (arg1[3])); + var x7: u64 = undefined; + var x8: u64 = undefined; + mulxU64(&x7, &x8, x4, (arg1[2])); + var x9: u64 = undefined; + var x10: u64 = undefined; + mulxU64(&x9, &x10, x4, (arg1[1])); + var x11: u64 = undefined; + var x12: u64 = undefined; + mulxU64(&x11, &x12, x4, (arg1[0])); + var x13: u64 = undefined; + var x14: u1 = undefined; + addcarryxU64(&x13, &x14, 0x0, x12, x9); + var x15: u64 = undefined; + var x16: u1 = undefined; + addcarryxU64(&x15, &x16, x14, x10, x7); + var x17: u64 = undefined; + var x18: u1 = undefined; + addcarryxU64(&x17, &x18, x16, x8, x5); + const x19 = (@as(u64, x18) + x6); + var x20: u64 = undefined; + var x21: u64 = undefined; + mulxU64(&x20, &x21, x11, 0xd838091dd2253531); + var x22: u64 = undefined; + var x23: u64 = undefined; + mulxU64(&x22, &x23, x20, 0xffffffffffffffff); + var x24: u64 = undefined; + var x25: u64 = undefined; + mulxU64(&x24, &x25, x20, 0xffffffffffffffff); + var x26: u64 = undefined; + var x27: u64 = undefined; + mulxU64(&x26, &x27, x20, 0xffffffffffffffff); + var x28: u64 = undefined; + var x29: u64 = undefined; + mulxU64(&x28, &x29, x20, 0xfffffffefffffc2f); + var x30: u64 = undefined; + var x31: u1 = undefined; + addcarryxU64(&x30, &x31, 0x0, x29, x26); + var x32: u64 = undefined; + var x33: u1 = undefined; + addcarryxU64(&x32, &x33, x31, x27, x24); + var x34: u64 = undefined; + var x35: u1 = undefined; + addcarryxU64(&x34, &x35, x33, x25, x22); + const x36 = (@as(u64, x35) + x23); + var x37: u64 = undefined; + var x38: u1 = undefined; + addcarryxU64(&x37, &x38, 0x0, x11, x28); + var x39: u64 = undefined; + var x40: u1 = undefined; + addcarryxU64(&x39, &x40, x38, x13, x30); + var x41: u64 = undefined; + var x42: u1 = undefined; + addcarryxU64(&x41, &x42, x40, x15, x32); + var x43: u64 = undefined; + var x44: u1 = undefined; + addcarryxU64(&x43, &x44, x42, x17, x34); + var x45: u64 = undefined; + var x46: u1 = undefined; + addcarryxU64(&x45, &x46, x44, x19, x36); + var x47: u64 = undefined; + var x48: u64 = undefined; + mulxU64(&x47, &x48, x1, (arg1[3])); + var x49: u64 = undefined; + var x50: u64 = undefined; + mulxU64(&x49, &x50, x1, (arg1[2])); + var x51: u64 = undefined; + var x52: u64 = undefined; + mulxU64(&x51, &x52, x1, (arg1[1])); + var x53: u64 = undefined; + var x54: u64 = undefined; + mulxU64(&x53, &x54, x1, (arg1[0])); + var x55: u64 = undefined; + var x56: u1 = undefined; + addcarryxU64(&x55, &x56, 0x0, x54, x51); + var x57: u64 = undefined; + var x58: u1 = undefined; + addcarryxU64(&x57, &x58, x56, x52, x49); + var x59: u64 = undefined; + var x60: u1 = undefined; + addcarryxU64(&x59, &x60, x58, x50, x47); + const x61 = (@as(u64, x60) + x48); + var x62: u64 = undefined; + var x63: u1 = undefined; + addcarryxU64(&x62, &x63, 0x0, x39, x53); + var x64: u64 = undefined; + var x65: u1 = undefined; + addcarryxU64(&x64, &x65, x63, x41, x55); + var x66: u64 = undefined; + var x67: u1 = undefined; + addcarryxU64(&x66, &x67, x65, x43, x57); + var x68: u64 = undefined; + var x69: u1 = undefined; + addcarryxU64(&x68, &x69, x67, x45, x59); + var x70: u64 = undefined; + var x71: u1 = undefined; + addcarryxU64(&x70, &x71, x69, @as(u64, x46), x61); + var x72: u64 = undefined; + var x73: u64 = undefined; + mulxU64(&x72, &x73, x62, 0xd838091dd2253531); + var x74: u64 = undefined; + var x75: u64 = undefined; + mulxU64(&x74, &x75, x72, 0xffffffffffffffff); + var x76: u64 = undefined; + var x77: u64 = undefined; + mulxU64(&x76, &x77, x72, 0xffffffffffffffff); + var x78: u64 = undefined; + var x79: u64 = undefined; + mulxU64(&x78, &x79, x72, 0xffffffffffffffff); + var x80: u64 = undefined; + var x81: u64 = undefined; + mulxU64(&x80, &x81, x72, 0xfffffffefffffc2f); + var x82: u64 = undefined; + var x83: u1 = undefined; + addcarryxU64(&x82, &x83, 0x0, x81, x78); + var x84: u64 = undefined; + var x85: u1 = undefined; + addcarryxU64(&x84, &x85, x83, x79, x76); + var x86: u64 = undefined; + var x87: u1 = undefined; + addcarryxU64(&x86, &x87, x85, x77, x74); + const x88 = (@as(u64, x87) + x75); + var x89: u64 = undefined; + var x90: u1 = undefined; + addcarryxU64(&x89, &x90, 0x0, x62, x80); + var x91: u64 = undefined; + var x92: u1 = undefined; + addcarryxU64(&x91, &x92, x90, x64, x82); + var x93: u64 = undefined; + var x94: u1 = undefined; + addcarryxU64(&x93, &x94, x92, x66, x84); + var x95: u64 = undefined; + var x96: u1 = undefined; + addcarryxU64(&x95, &x96, x94, x68, x86); + var x97: u64 = undefined; + var x98: u1 = undefined; + addcarryxU64(&x97, &x98, x96, x70, x88); + const x99 = (@as(u64, x98) + @as(u64, x71)); + var x100: u64 = undefined; + var x101: u64 = undefined; + mulxU64(&x100, &x101, x2, (arg1[3])); + var x102: u64 = undefined; + var x103: u64 = undefined; + mulxU64(&x102, &x103, x2, (arg1[2])); + var x104: u64 = undefined; + var x105: u64 = undefined; + mulxU64(&x104, &x105, x2, (arg1[1])); + var x106: u64 = undefined; + var x107: u64 = undefined; + mulxU64(&x106, &x107, x2, (arg1[0])); + var x108: u64 = undefined; + var x109: u1 = undefined; + addcarryxU64(&x108, &x109, 0x0, x107, x104); + var x110: u64 = undefined; + var x111: u1 = undefined; + addcarryxU64(&x110, &x111, x109, x105, x102); + var x112: u64 = undefined; + var x113: u1 = undefined; + addcarryxU64(&x112, &x113, x111, x103, x100); + const x114 = (@as(u64, x113) + x101); + var x115: u64 = undefined; + var x116: u1 = undefined; + addcarryxU64(&x115, &x116, 0x0, x91, x106); + var x117: u64 = undefined; + var x118: u1 = undefined; + addcarryxU64(&x117, &x118, x116, x93, x108); + var x119: u64 = undefined; + var x120: u1 = undefined; + addcarryxU64(&x119, &x120, x118, x95, x110); + var x121: u64 = undefined; + var x122: u1 = undefined; + addcarryxU64(&x121, &x122, x120, x97, x112); + var x123: u64 = undefined; + var x124: u1 = undefined; + addcarryxU64(&x123, &x124, x122, x99, x114); + var x125: u64 = undefined; + var x126: u64 = undefined; + mulxU64(&x125, &x126, x115, 0xd838091dd2253531); + var x127: u64 = undefined; + var x128: u64 = undefined; + mulxU64(&x127, &x128, x125, 0xffffffffffffffff); + var x129: u64 = undefined; + var x130: u64 = undefined; + mulxU64(&x129, &x130, x125, 0xffffffffffffffff); + var x131: u64 = undefined; + var x132: u64 = undefined; + mulxU64(&x131, &x132, x125, 0xffffffffffffffff); + var x133: u64 = undefined; + var x134: u64 = undefined; + mulxU64(&x133, &x134, x125, 0xfffffffefffffc2f); + var x135: u64 = undefined; + var x136: u1 = undefined; + addcarryxU64(&x135, &x136, 0x0, x134, x131); + var x137: u64 = undefined; + var x138: u1 = undefined; + addcarryxU64(&x137, &x138, x136, x132, x129); + var x139: u64 = undefined; + var x140: u1 = undefined; + addcarryxU64(&x139, &x140, x138, x130, x127); + const x141 = (@as(u64, x140) + x128); + var x142: u64 = undefined; + var x143: u1 = undefined; + addcarryxU64(&x142, &x143, 0x0, x115, x133); + var x144: u64 = undefined; + var x145: u1 = undefined; + addcarryxU64(&x144, &x145, x143, x117, x135); + var x146: u64 = undefined; + var x147: u1 = undefined; + addcarryxU64(&x146, &x147, x145, x119, x137); + var x148: u64 = undefined; + var x149: u1 = undefined; + addcarryxU64(&x148, &x149, x147, x121, x139); + var x150: u64 = undefined; + var x151: u1 = undefined; + addcarryxU64(&x150, &x151, x149, x123, x141); + const x152 = (@as(u64, x151) + @as(u64, x124)); + var x153: u64 = undefined; + var x154: u64 = undefined; + mulxU64(&x153, &x154, x3, (arg1[3])); + var x155: u64 = undefined; + var x156: u64 = undefined; + mulxU64(&x155, &x156, x3, (arg1[2])); + var x157: u64 = undefined; + var x158: u64 = undefined; + mulxU64(&x157, &x158, x3, (arg1[1])); + var x159: u64 = undefined; + var x160: u64 = undefined; + mulxU64(&x159, &x160, x3, (arg1[0])); + var x161: u64 = undefined; + var x162: u1 = undefined; + addcarryxU64(&x161, &x162, 0x0, x160, x157); + var x163: u64 = undefined; + var x164: u1 = undefined; + addcarryxU64(&x163, &x164, x162, x158, x155); + var x165: u64 = undefined; + var x166: u1 = undefined; + addcarryxU64(&x165, &x166, x164, x156, x153); + const x167 = (@as(u64, x166) + x154); + var x168: u64 = undefined; + var x169: u1 = undefined; + addcarryxU64(&x168, &x169, 0x0, x144, x159); + var x170: u64 = undefined; + var x171: u1 = undefined; + addcarryxU64(&x170, &x171, x169, x146, x161); + var x172: u64 = undefined; + var x173: u1 = undefined; + addcarryxU64(&x172, &x173, x171, x148, x163); + var x174: u64 = undefined; + var x175: u1 = undefined; + addcarryxU64(&x174, &x175, x173, x150, x165); + var x176: u64 = undefined; + var x177: u1 = undefined; + addcarryxU64(&x176, &x177, x175, x152, x167); + var x178: u64 = undefined; + var x179: u64 = undefined; + mulxU64(&x178, &x179, x168, 0xd838091dd2253531); + var x180: u64 = undefined; + var x181: u64 = undefined; + mulxU64(&x180, &x181, x178, 0xffffffffffffffff); + var x182: u64 = undefined; + var x183: u64 = undefined; + mulxU64(&x182, &x183, x178, 0xffffffffffffffff); + var x184: u64 = undefined; + var x185: u64 = undefined; + mulxU64(&x184, &x185, x178, 0xffffffffffffffff); + var x186: u64 = undefined; + var x187: u64 = undefined; + mulxU64(&x186, &x187, x178, 0xfffffffefffffc2f); + var x188: u64 = undefined; + var x189: u1 = undefined; + addcarryxU64(&x188, &x189, 0x0, x187, x184); + var x190: u64 = undefined; + var x191: u1 = undefined; + addcarryxU64(&x190, &x191, x189, x185, x182); + var x192: u64 = undefined; + var x193: u1 = undefined; + addcarryxU64(&x192, &x193, x191, x183, x180); + const x194 = (@as(u64, x193) + x181); + var x195: u64 = undefined; + var x196: u1 = undefined; + addcarryxU64(&x195, &x196, 0x0, x168, x186); + var x197: u64 = undefined; + var x198: u1 = undefined; + addcarryxU64(&x197, &x198, x196, x170, x188); + var x199: u64 = undefined; + var x200: u1 = undefined; + addcarryxU64(&x199, &x200, x198, x172, x190); + var x201: u64 = undefined; + var x202: u1 = undefined; + addcarryxU64(&x201, &x202, x200, x174, x192); + var x203: u64 = undefined; + var x204: u1 = undefined; + addcarryxU64(&x203, &x204, x202, x176, x194); + const x205 = (@as(u64, x204) + @as(u64, x177)); + var x206: u64 = undefined; + var x207: u1 = undefined; + subborrowxU64(&x206, &x207, 0x0, x197, 0xfffffffefffffc2f); + var x208: u64 = undefined; + var x209: u1 = undefined; + subborrowxU64(&x208, &x209, x207, x199, 0xffffffffffffffff); + var x210: u64 = undefined; + var x211: u1 = undefined; + subborrowxU64(&x210, &x211, x209, x201, 0xffffffffffffffff); + var x212: u64 = undefined; + var x213: u1 = undefined; + subborrowxU64(&x212, &x213, x211, x203, 0xffffffffffffffff); + var x214: u64 = undefined; + var x215: u1 = undefined; + subborrowxU64(&x214, &x215, x213, x205, 0x0); + var x216: u64 = undefined; + cmovznzU64(&x216, x215, x206, x197); + var x217: u64 = undefined; + cmovznzU64(&x217, x215, x208, x199); + var x218: u64 = undefined; + cmovznzU64(&x218, x215, x210, x201); + var x219: u64 = undefined; + cmovznzU64(&x219, x215, x212, x203); + out1[0] = x216; + out1[1] = x217; + out1[2] = x218; + out1[3] = x219; +} + +/// The function add adds two field elements in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// 0 ≤ eval arg2 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m +/// 0 ≤ eval out1 < m +/// +pub fn add(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement, arg2: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + addcarryxU64(&x1, &x2, 0x0, (arg1[0]), (arg2[0])); + var x3: u64 = undefined; + var x4: u1 = undefined; + addcarryxU64(&x3, &x4, x2, (arg1[1]), (arg2[1])); + var x5: u64 = undefined; + var x6: u1 = undefined; + addcarryxU64(&x5, &x6, x4, (arg1[2]), (arg2[2])); + var x7: u64 = undefined; + var x8: u1 = undefined; + addcarryxU64(&x7, &x8, x6, (arg1[3]), (arg2[3])); + var x9: u64 = undefined; + var x10: u1 = undefined; + subborrowxU64(&x9, &x10, 0x0, x1, 0xfffffffefffffc2f); + var x11: u64 = undefined; + var x12: u1 = undefined; + subborrowxU64(&x11, &x12, x10, x3, 0xffffffffffffffff); + var x13: u64 = undefined; + var x14: u1 = undefined; + subborrowxU64(&x13, &x14, x12, x5, 0xffffffffffffffff); + var x15: u64 = undefined; + var x16: u1 = undefined; + subborrowxU64(&x15, &x16, x14, x7, 0xffffffffffffffff); + var x17: u64 = undefined; + var x18: u1 = undefined; + subborrowxU64(&x17, &x18, x16, @as(u64, x8), 0x0); + var x19: u64 = undefined; + cmovznzU64(&x19, x18, x9, x1); + var x20: u64 = undefined; + cmovznzU64(&x20, x18, x11, x3); + var x21: u64 = undefined; + cmovznzU64(&x21, x18, x13, x5); + var x22: u64 = undefined; + cmovznzU64(&x22, x18, x15, x7); + out1[0] = x19; + out1[1] = x20; + out1[2] = x21; + out1[3] = x22; +} + +/// The function sub subtracts two field elements in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// 0 ≤ eval arg2 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m +/// 0 ≤ eval out1 < m +/// +pub fn sub(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement, arg2: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + subborrowxU64(&x1, &x2, 0x0, (arg1[0]), (arg2[0])); + var x3: u64 = undefined; + var x4: u1 = undefined; + subborrowxU64(&x3, &x4, x2, (arg1[1]), (arg2[1])); + var x5: u64 = undefined; + var x6: u1 = undefined; + subborrowxU64(&x5, &x6, x4, (arg1[2]), (arg2[2])); + var x7: u64 = undefined; + var x8: u1 = undefined; + subborrowxU64(&x7, &x8, x6, (arg1[3]), (arg2[3])); + var x9: u64 = undefined; + cmovznzU64(&x9, x8, 0x0, 0xffffffffffffffff); + var x10: u64 = undefined; + var x11: u1 = undefined; + addcarryxU64(&x10, &x11, 0x0, x1, (x9 & 0xfffffffefffffc2f)); + var x12: u64 = undefined; + var x13: u1 = undefined; + addcarryxU64(&x12, &x13, x11, x3, x9); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, x13, x5, x9); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, x7, x9); + out1[0] = x10; + out1[1] = x12; + out1[2] = x14; + out1[3] = x16; +} + +/// The function opp negates a field element in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = -eval (from_montgomery arg1) mod m +/// 0 ≤ eval out1 < m +/// +pub fn opp(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + subborrowxU64(&x1, &x2, 0x0, 0x0, (arg1[0])); + var x3: u64 = undefined; + var x4: u1 = undefined; + subborrowxU64(&x3, &x4, x2, 0x0, (arg1[1])); + var x5: u64 = undefined; + var x6: u1 = undefined; + subborrowxU64(&x5, &x6, x4, 0x0, (arg1[2])); + var x7: u64 = undefined; + var x8: u1 = undefined; + subborrowxU64(&x7, &x8, x6, 0x0, (arg1[3])); + var x9: u64 = undefined; + cmovznzU64(&x9, x8, 0x0, 0xffffffffffffffff); + var x10: u64 = undefined; + var x11: u1 = undefined; + addcarryxU64(&x10, &x11, 0x0, x1, (x9 & 0xfffffffefffffc2f)); + var x12: u64 = undefined; + var x13: u1 = undefined; + addcarryxU64(&x12, &x13, x11, x3, x9); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, x13, x5, x9); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, x7, x9); + out1[0] = x10; + out1[1] = x12; + out1[2] = x14; + out1[3] = x16; +} + +/// The function fromMontgomery translates a field element out of the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^4) mod m +/// 0 ≤ eval out1 < m +/// +pub fn fromMontgomery(out1: *NonMontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[0]); + var x2: u64 = undefined; + var x3: u64 = undefined; + mulxU64(&x2, &x3, x1, 0xd838091dd2253531); + var x4: u64 = undefined; + var x5: u64 = undefined; + mulxU64(&x4, &x5, x2, 0xffffffffffffffff); + var x6: u64 = undefined; + var x7: u64 = undefined; + mulxU64(&x6, &x7, x2, 0xffffffffffffffff); + var x8: u64 = undefined; + var x9: u64 = undefined; + mulxU64(&x8, &x9, x2, 0xffffffffffffffff); + var x10: u64 = undefined; + var x11: u64 = undefined; + mulxU64(&x10, &x11, x2, 0xfffffffefffffc2f); + var x12: u64 = undefined; + var x13: u1 = undefined; + addcarryxU64(&x12, &x13, 0x0, x11, x8); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, x13, x9, x6); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, x7, x4); + var x18: u64 = undefined; + var x19: u1 = undefined; + addcarryxU64(&x18, &x19, 0x0, x1, x10); + var x20: u64 = undefined; + var x21: u1 = undefined; + addcarryxU64(&x20, &x21, x19, 0x0, x12); + var x22: u64 = undefined; + var x23: u1 = undefined; + addcarryxU64(&x22, &x23, x21, 0x0, x14); + var x24: u64 = undefined; + var x25: u1 = undefined; + addcarryxU64(&x24, &x25, x23, 0x0, x16); + var x26: u64 = undefined; + var x27: u1 = undefined; + addcarryxU64(&x26, &x27, x25, 0x0, (@as(u64, x17) + x5)); + var x28: u64 = undefined; + var x29: u1 = undefined; + addcarryxU64(&x28, &x29, 0x0, x20, (arg1[1])); + var x30: u64 = undefined; + var x31: u1 = undefined; + addcarryxU64(&x30, &x31, x29, x22, 0x0); + var x32: u64 = undefined; + var x33: u1 = undefined; + addcarryxU64(&x32, &x33, x31, x24, 0x0); + var x34: u64 = undefined; + var x35: u1 = undefined; + addcarryxU64(&x34, &x35, x33, x26, 0x0); + var x36: u64 = undefined; + var x37: u64 = undefined; + mulxU64(&x36, &x37, x28, 0xd838091dd2253531); + var x38: u64 = undefined; + var x39: u64 = undefined; + mulxU64(&x38, &x39, x36, 0xffffffffffffffff); + var x40: u64 = undefined; + var x41: u64 = undefined; + mulxU64(&x40, &x41, x36, 0xffffffffffffffff); + var x42: u64 = undefined; + var x43: u64 = undefined; + mulxU64(&x42, &x43, x36, 0xffffffffffffffff); + var x44: u64 = undefined; + var x45: u64 = undefined; + mulxU64(&x44, &x45, x36, 0xfffffffefffffc2f); + var x46: u64 = undefined; + var x47: u1 = undefined; + addcarryxU64(&x46, &x47, 0x0, x45, x42); + var x48: u64 = undefined; + var x49: u1 = undefined; + addcarryxU64(&x48, &x49, x47, x43, x40); + var x50: u64 = undefined; + var x51: u1 = undefined; + addcarryxU64(&x50, &x51, x49, x41, x38); + var x52: u64 = undefined; + var x53: u1 = undefined; + addcarryxU64(&x52, &x53, 0x0, x28, x44); + var x54: u64 = undefined; + var x55: u1 = undefined; + addcarryxU64(&x54, &x55, x53, x30, x46); + var x56: u64 = undefined; + var x57: u1 = undefined; + addcarryxU64(&x56, &x57, x55, x32, x48); + var x58: u64 = undefined; + var x59: u1 = undefined; + addcarryxU64(&x58, &x59, x57, x34, x50); + var x60: u64 = undefined; + var x61: u1 = undefined; + addcarryxU64(&x60, &x61, x59, (@as(u64, x35) + @as(u64, x27)), (@as(u64, x51) + x39)); + var x62: u64 = undefined; + var x63: u1 = undefined; + addcarryxU64(&x62, &x63, 0x0, x54, (arg1[2])); + var x64: u64 = undefined; + var x65: u1 = undefined; + addcarryxU64(&x64, &x65, x63, x56, 0x0); + var x66: u64 = undefined; + var x67: u1 = undefined; + addcarryxU64(&x66, &x67, x65, x58, 0x0); + var x68: u64 = undefined; + var x69: u1 = undefined; + addcarryxU64(&x68, &x69, x67, x60, 0x0); + var x70: u64 = undefined; + var x71: u64 = undefined; + mulxU64(&x70, &x71, x62, 0xd838091dd2253531); + var x72: u64 = undefined; + var x73: u64 = undefined; + mulxU64(&x72, &x73, x70, 0xffffffffffffffff); + var x74: u64 = undefined; + var x75: u64 = undefined; + mulxU64(&x74, &x75, x70, 0xffffffffffffffff); + var x76: u64 = undefined; + var x77: u64 = undefined; + mulxU64(&x76, &x77, x70, 0xffffffffffffffff); + var x78: u64 = undefined; + var x79: u64 = undefined; + mulxU64(&x78, &x79, x70, 0xfffffffefffffc2f); + var x80: u64 = undefined; + var x81: u1 = undefined; + addcarryxU64(&x80, &x81, 0x0, x79, x76); + var x82: u64 = undefined; + var x83: u1 = undefined; + addcarryxU64(&x82, &x83, x81, x77, x74); + var x84: u64 = undefined; + var x85: u1 = undefined; + addcarryxU64(&x84, &x85, x83, x75, x72); + var x86: u64 = undefined; + var x87: u1 = undefined; + addcarryxU64(&x86, &x87, 0x0, x62, x78); + var x88: u64 = undefined; + var x89: u1 = undefined; + addcarryxU64(&x88, &x89, x87, x64, x80); + var x90: u64 = undefined; + var x91: u1 = undefined; + addcarryxU64(&x90, &x91, x89, x66, x82); + var x92: u64 = undefined; + var x93: u1 = undefined; + addcarryxU64(&x92, &x93, x91, x68, x84); + var x94: u64 = undefined; + var x95: u1 = undefined; + addcarryxU64(&x94, &x95, x93, (@as(u64, x69) + @as(u64, x61)), (@as(u64, x85) + x73)); + var x96: u64 = undefined; + var x97: u1 = undefined; + addcarryxU64(&x96, &x97, 0x0, x88, (arg1[3])); + var x98: u64 = undefined; + var x99: u1 = undefined; + addcarryxU64(&x98, &x99, x97, x90, 0x0); + var x100: u64 = undefined; + var x101: u1 = undefined; + addcarryxU64(&x100, &x101, x99, x92, 0x0); + var x102: u64 = undefined; + var x103: u1 = undefined; + addcarryxU64(&x102, &x103, x101, x94, 0x0); + var x104: u64 = undefined; + var x105: u64 = undefined; + mulxU64(&x104, &x105, x96, 0xd838091dd2253531); + var x106: u64 = undefined; + var x107: u64 = undefined; + mulxU64(&x106, &x107, x104, 0xffffffffffffffff); + var x108: u64 = undefined; + var x109: u64 = undefined; + mulxU64(&x108, &x109, x104, 0xffffffffffffffff); + var x110: u64 = undefined; + var x111: u64 = undefined; + mulxU64(&x110, &x111, x104, 0xffffffffffffffff); + var x112: u64 = undefined; + var x113: u64 = undefined; + mulxU64(&x112, &x113, x104, 0xfffffffefffffc2f); + var x114: u64 = undefined; + var x115: u1 = undefined; + addcarryxU64(&x114, &x115, 0x0, x113, x110); + var x116: u64 = undefined; + var x117: u1 = undefined; + addcarryxU64(&x116, &x117, x115, x111, x108); + var x118: u64 = undefined; + var x119: u1 = undefined; + addcarryxU64(&x118, &x119, x117, x109, x106); + var x120: u64 = undefined; + var x121: u1 = undefined; + addcarryxU64(&x120, &x121, 0x0, x96, x112); + var x122: u64 = undefined; + var x123: u1 = undefined; + addcarryxU64(&x122, &x123, x121, x98, x114); + var x124: u64 = undefined; + var x125: u1 = undefined; + addcarryxU64(&x124, &x125, x123, x100, x116); + var x126: u64 = undefined; + var x127: u1 = undefined; + addcarryxU64(&x126, &x127, x125, x102, x118); + var x128: u64 = undefined; + var x129: u1 = undefined; + addcarryxU64(&x128, &x129, x127, (@as(u64, x103) + @as(u64, x95)), (@as(u64, x119) + x107)); + var x130: u64 = undefined; + var x131: u1 = undefined; + subborrowxU64(&x130, &x131, 0x0, x122, 0xfffffffefffffc2f); + var x132: u64 = undefined; + var x133: u1 = undefined; + subborrowxU64(&x132, &x133, x131, x124, 0xffffffffffffffff); + var x134: u64 = undefined; + var x135: u1 = undefined; + subborrowxU64(&x134, &x135, x133, x126, 0xffffffffffffffff); + var x136: u64 = undefined; + var x137: u1 = undefined; + subborrowxU64(&x136, &x137, x135, x128, 0xffffffffffffffff); + var x138: u64 = undefined; + var x139: u1 = undefined; + subborrowxU64(&x138, &x139, x137, @as(u64, x129), 0x0); + var x140: u64 = undefined; + cmovznzU64(&x140, x139, x130, x122); + var x141: u64 = undefined; + cmovznzU64(&x141, x139, x132, x124); + var x142: u64 = undefined; + cmovznzU64(&x142, x139, x134, x126); + var x143: u64 = undefined; + cmovznzU64(&x143, x139, x136, x128); + out1[0] = x140; + out1[1] = x141; + out1[2] = x142; + out1[3] = x143; +} + +/// The function toMontgomery translates a field element into the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = eval arg1 mod m +/// 0 ≤ eval out1 < m +/// +pub fn toMontgomery(out1: *MontgomeryDomainFieldElement, arg1: NonMontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[1]); + const x2 = (arg1[2]); + const x3 = (arg1[3]); + const x4 = (arg1[0]); + var x5: u64 = undefined; + var x6: u64 = undefined; + mulxU64(&x5, &x6, x4, 0x7a2000e90a1); + var x7: u64 = undefined; + var x8: u1 = undefined; + addcarryxU64(&x7, &x8, 0x0, x6, x4); + var x9: u64 = undefined; + var x10: u64 = undefined; + mulxU64(&x9, &x10, x5, 0xd838091dd2253531); + var x11: u64 = undefined; + var x12: u64 = undefined; + mulxU64(&x11, &x12, x9, 0xffffffffffffffff); + var x13: u64 = undefined; + var x14: u64 = undefined; + mulxU64(&x13, &x14, x9, 0xffffffffffffffff); + var x15: u64 = undefined; + var x16: u64 = undefined; + mulxU64(&x15, &x16, x9, 0xffffffffffffffff); + var x17: u64 = undefined; + var x18: u64 = undefined; + mulxU64(&x17, &x18, x9, 0xfffffffefffffc2f); + var x19: u64 = undefined; + var x20: u1 = undefined; + addcarryxU64(&x19, &x20, 0x0, x18, x15); + var x21: u64 = undefined; + var x22: u1 = undefined; + addcarryxU64(&x21, &x22, x20, x16, x13); + var x23: u64 = undefined; + var x24: u1 = undefined; + addcarryxU64(&x23, &x24, x22, x14, x11); + var x25: u64 = undefined; + var x26: u1 = undefined; + addcarryxU64(&x25, &x26, 0x0, x5, x17); + var x27: u64 = undefined; + var x28: u1 = undefined; + addcarryxU64(&x27, &x28, x26, x7, x19); + var x29: u64 = undefined; + var x30: u1 = undefined; + addcarryxU64(&x29, &x30, x28, @as(u64, x8), x21); + var x31: u64 = undefined; + var x32: u1 = undefined; + addcarryxU64(&x31, &x32, x30, 0x0, x23); + var x33: u64 = undefined; + var x34: u1 = undefined; + addcarryxU64(&x33, &x34, x32, 0x0, (@as(u64, x24) + x12)); + var x35: u64 = undefined; + var x36: u64 = undefined; + mulxU64(&x35, &x36, x1, 0x7a2000e90a1); + var x37: u64 = undefined; + var x38: u1 = undefined; + addcarryxU64(&x37, &x38, 0x0, x36, x1); + var x39: u64 = undefined; + var x40: u1 = undefined; + addcarryxU64(&x39, &x40, 0x0, x27, x35); + var x41: u64 = undefined; + var x42: u1 = undefined; + addcarryxU64(&x41, &x42, x40, x29, x37); + var x43: u64 = undefined; + var x44: u1 = undefined; + addcarryxU64(&x43, &x44, x42, x31, @as(u64, x38)); + var x45: u64 = undefined; + var x46: u1 = undefined; + addcarryxU64(&x45, &x46, x44, x33, 0x0); + var x47: u64 = undefined; + var x48: u64 = undefined; + mulxU64(&x47, &x48, x39, 0xd838091dd2253531); + var x49: u64 = undefined; + var x50: u64 = undefined; + mulxU64(&x49, &x50, x47, 0xffffffffffffffff); + var x51: u64 = undefined; + var x52: u64 = undefined; + mulxU64(&x51, &x52, x47, 0xffffffffffffffff); + var x53: u64 = undefined; + var x54: u64 = undefined; + mulxU64(&x53, &x54, x47, 0xffffffffffffffff); + var x55: u64 = undefined; + var x56: u64 = undefined; + mulxU64(&x55, &x56, x47, 0xfffffffefffffc2f); + var x57: u64 = undefined; + var x58: u1 = undefined; + addcarryxU64(&x57, &x58, 0x0, x56, x53); + var x59: u64 = undefined; + var x60: u1 = undefined; + addcarryxU64(&x59, &x60, x58, x54, x51); + var x61: u64 = undefined; + var x62: u1 = undefined; + addcarryxU64(&x61, &x62, x60, x52, x49); + var x63: u64 = undefined; + var x64: u1 = undefined; + addcarryxU64(&x63, &x64, 0x0, x39, x55); + var x65: u64 = undefined; + var x66: u1 = undefined; + addcarryxU64(&x65, &x66, x64, x41, x57); + var x67: u64 = undefined; + var x68: u1 = undefined; + addcarryxU64(&x67, &x68, x66, x43, x59); + var x69: u64 = undefined; + var x70: u1 = undefined; + addcarryxU64(&x69, &x70, x68, x45, x61); + var x71: u64 = undefined; + var x72: u1 = undefined; + addcarryxU64(&x71, &x72, x70, (@as(u64, x46) + @as(u64, x34)), (@as(u64, x62) + x50)); + var x73: u64 = undefined; + var x74: u64 = undefined; + mulxU64(&x73, &x74, x2, 0x7a2000e90a1); + var x75: u64 = undefined; + var x76: u1 = undefined; + addcarryxU64(&x75, &x76, 0x0, x74, x2); + var x77: u64 = undefined; + var x78: u1 = undefined; + addcarryxU64(&x77, &x78, 0x0, x65, x73); + var x79: u64 = undefined; + var x80: u1 = undefined; + addcarryxU64(&x79, &x80, x78, x67, x75); + var x81: u64 = undefined; + var x82: u1 = undefined; + addcarryxU64(&x81, &x82, x80, x69, @as(u64, x76)); + var x83: u64 = undefined; + var x84: u1 = undefined; + addcarryxU64(&x83, &x84, x82, x71, 0x0); + var x85: u64 = undefined; + var x86: u64 = undefined; + mulxU64(&x85, &x86, x77, 0xd838091dd2253531); + var x87: u64 = undefined; + var x88: u64 = undefined; + mulxU64(&x87, &x88, x85, 0xffffffffffffffff); + var x89: u64 = undefined; + var x90: u64 = undefined; + mulxU64(&x89, &x90, x85, 0xffffffffffffffff); + var x91: u64 = undefined; + var x92: u64 = undefined; + mulxU64(&x91, &x92, x85, 0xffffffffffffffff); + var x93: u64 = undefined; + var x94: u64 = undefined; + mulxU64(&x93, &x94, x85, 0xfffffffefffffc2f); + var x95: u64 = undefined; + var x96: u1 = undefined; + addcarryxU64(&x95, &x96, 0x0, x94, x91); + var x97: u64 = undefined; + var x98: u1 = undefined; + addcarryxU64(&x97, &x98, x96, x92, x89); + var x99: u64 = undefined; + var x100: u1 = undefined; + addcarryxU64(&x99, &x100, x98, x90, x87); + var x101: u64 = undefined; + var x102: u1 = undefined; + addcarryxU64(&x101, &x102, 0x0, x77, x93); + var x103: u64 = undefined; + var x104: u1 = undefined; + addcarryxU64(&x103, &x104, x102, x79, x95); + var x105: u64 = undefined; + var x106: u1 = undefined; + addcarryxU64(&x105, &x106, x104, x81, x97); + var x107: u64 = undefined; + var x108: u1 = undefined; + addcarryxU64(&x107, &x108, x106, x83, x99); + var x109: u64 = undefined; + var x110: u1 = undefined; + addcarryxU64(&x109, &x110, x108, (@as(u64, x84) + @as(u64, x72)), (@as(u64, x100) + x88)); + var x111: u64 = undefined; + var x112: u64 = undefined; + mulxU64(&x111, &x112, x3, 0x7a2000e90a1); + var x113: u64 = undefined; + var x114: u1 = undefined; + addcarryxU64(&x113, &x114, 0x0, x112, x3); + var x115: u64 = undefined; + var x116: u1 = undefined; + addcarryxU64(&x115, &x116, 0x0, x103, x111); + var x117: u64 = undefined; + var x118: u1 = undefined; + addcarryxU64(&x117, &x118, x116, x105, x113); + var x119: u64 = undefined; + var x120: u1 = undefined; + addcarryxU64(&x119, &x120, x118, x107, @as(u64, x114)); + var x121: u64 = undefined; + var x122: u1 = undefined; + addcarryxU64(&x121, &x122, x120, x109, 0x0); + var x123: u64 = undefined; + var x124: u64 = undefined; + mulxU64(&x123, &x124, x115, 0xd838091dd2253531); + var x125: u64 = undefined; + var x126: u64 = undefined; + mulxU64(&x125, &x126, x123, 0xffffffffffffffff); + var x127: u64 = undefined; + var x128: u64 = undefined; + mulxU64(&x127, &x128, x123, 0xffffffffffffffff); + var x129: u64 = undefined; + var x130: u64 = undefined; + mulxU64(&x129, &x130, x123, 0xffffffffffffffff); + var x131: u64 = undefined; + var x132: u64 = undefined; + mulxU64(&x131, &x132, x123, 0xfffffffefffffc2f); + var x133: u64 = undefined; + var x134: u1 = undefined; + addcarryxU64(&x133, &x134, 0x0, x132, x129); + var x135: u64 = undefined; + var x136: u1 = undefined; + addcarryxU64(&x135, &x136, x134, x130, x127); + var x137: u64 = undefined; + var x138: u1 = undefined; + addcarryxU64(&x137, &x138, x136, x128, x125); + var x139: u64 = undefined; + var x140: u1 = undefined; + addcarryxU64(&x139, &x140, 0x0, x115, x131); + var x141: u64 = undefined; + var x142: u1 = undefined; + addcarryxU64(&x141, &x142, x140, x117, x133); + var x143: u64 = undefined; + var x144: u1 = undefined; + addcarryxU64(&x143, &x144, x142, x119, x135); + var x145: u64 = undefined; + var x146: u1 = undefined; + addcarryxU64(&x145, &x146, x144, x121, x137); + var x147: u64 = undefined; + var x148: u1 = undefined; + addcarryxU64(&x147, &x148, x146, (@as(u64, x122) + @as(u64, x110)), (@as(u64, x138) + x126)); + var x149: u64 = undefined; + var x150: u1 = undefined; + subborrowxU64(&x149, &x150, 0x0, x141, 0xfffffffefffffc2f); + var x151: u64 = undefined; + var x152: u1 = undefined; + subborrowxU64(&x151, &x152, x150, x143, 0xffffffffffffffff); + var x153: u64 = undefined; + var x154: u1 = undefined; + subborrowxU64(&x153, &x154, x152, x145, 0xffffffffffffffff); + var x155: u64 = undefined; + var x156: u1 = undefined; + subborrowxU64(&x155, &x156, x154, x147, 0xffffffffffffffff); + var x157: u64 = undefined; + var x158: u1 = undefined; + subborrowxU64(&x157, &x158, x156, @as(u64, x148), 0x0); + var x159: u64 = undefined; + cmovznzU64(&x159, x158, x149, x141); + var x160: u64 = undefined; + cmovznzU64(&x160, x158, x151, x143); + var x161: u64 = undefined; + cmovznzU64(&x161, x158, x153, x145); + var x162: u64 = undefined; + cmovznzU64(&x162, x158, x155, x147); + out1[0] = x159; + out1[1] = x160; + out1[2] = x161; + out1[3] = x162; +} + +/// The function nonzero outputs a single non-zero word if the input is non-zero and zero otherwise. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// out1 = 0 ↔ eval (from_montgomery arg1) mod m = 0 +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +pub fn nonzero(out1: *u64, arg1: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = ((arg1[0]) | ((arg1[1]) | ((arg1[2]) | (arg1[3])))); + out1.* = x1; +} + +/// The function selectznz is a multi-limb conditional select. +/// +/// Postconditions: +/// out1 = (if arg1 = 0 then arg2 else arg3) +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn selectznz(out1: *[4]u64, arg1: u1, arg2: [4]u64, arg3: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + cmovznzU64(&x1, arg1, (arg2[0]), (arg3[0])); + var x2: u64 = undefined; + cmovznzU64(&x2, arg1, (arg2[1]), (arg3[1])); + var x3: u64 = undefined; + cmovznzU64(&x3, arg1, (arg2[2]), (arg3[2])); + var x4: u64 = undefined; + cmovznzU64(&x4, arg1, (arg2[3]), (arg3[3])); + out1[0] = x1; + out1[1] = x2; + out1[2] = x3; + out1[3] = x4; +} + +/// The function toBytes serializes a field element NOT in the Montgomery domain to bytes in little-endian order. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// out1 = map (λ x, ⌊((eval arg1 mod m) mod 2^(8 * (x + 1))) / 2^(8 * x)⌋) [0..31] +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]] +pub fn toBytes(out1: *[32]u8, arg1: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[3]); + const x2 = (arg1[2]); + const x3 = (arg1[1]); + const x4 = (arg1[0]); + const x5 = @truncate(u8, (x4 & 0xff)); + const x6 = (x4 >> 8); + const x7 = @truncate(u8, (x6 & 0xff)); + const x8 = (x6 >> 8); + const x9 = @truncate(u8, (x8 & 0xff)); + const x10 = (x8 >> 8); + const x11 = @truncate(u8, (x10 & 0xff)); + const x12 = (x10 >> 8); + const x13 = @truncate(u8, (x12 & 0xff)); + const x14 = (x12 >> 8); + const x15 = @truncate(u8, (x14 & 0xff)); + const x16 = (x14 >> 8); + const x17 = @truncate(u8, (x16 & 0xff)); + const x18 = @truncate(u8, (x16 >> 8)); + const x19 = @truncate(u8, (x3 & 0xff)); + const x20 = (x3 >> 8); + const x21 = @truncate(u8, (x20 & 0xff)); + const x22 = (x20 >> 8); + const x23 = @truncate(u8, (x22 & 0xff)); + const x24 = (x22 >> 8); + const x25 = @truncate(u8, (x24 & 0xff)); + const x26 = (x24 >> 8); + const x27 = @truncate(u8, (x26 & 0xff)); + const x28 = (x26 >> 8); + const x29 = @truncate(u8, (x28 & 0xff)); + const x30 = (x28 >> 8); + const x31 = @truncate(u8, (x30 & 0xff)); + const x32 = @truncate(u8, (x30 >> 8)); + const x33 = @truncate(u8, (x2 & 0xff)); + const x34 = (x2 >> 8); + const x35 = @truncate(u8, (x34 & 0xff)); + const x36 = (x34 >> 8); + const x37 = @truncate(u8, (x36 & 0xff)); + const x38 = (x36 >> 8); + const x39 = @truncate(u8, (x38 & 0xff)); + const x40 = (x38 >> 8); + const x41 = @truncate(u8, (x40 & 0xff)); + const x42 = (x40 >> 8); + const x43 = @truncate(u8, (x42 & 0xff)); + const x44 = (x42 >> 8); + const x45 = @truncate(u8, (x44 & 0xff)); + const x46 = @truncate(u8, (x44 >> 8)); + const x47 = @truncate(u8, (x1 & 0xff)); + const x48 = (x1 >> 8); + const x49 = @truncate(u8, (x48 & 0xff)); + const x50 = (x48 >> 8); + const x51 = @truncate(u8, (x50 & 0xff)); + const x52 = (x50 >> 8); + const x53 = @truncate(u8, (x52 & 0xff)); + const x54 = (x52 >> 8); + const x55 = @truncate(u8, (x54 & 0xff)); + const x56 = (x54 >> 8); + const x57 = @truncate(u8, (x56 & 0xff)); + const x58 = (x56 >> 8); + const x59 = @truncate(u8, (x58 & 0xff)); + const x60 = @truncate(u8, (x58 >> 8)); + out1[0] = x5; + out1[1] = x7; + out1[2] = x9; + out1[3] = x11; + out1[4] = x13; + out1[5] = x15; + out1[6] = x17; + out1[7] = x18; + out1[8] = x19; + out1[9] = x21; + out1[10] = x23; + out1[11] = x25; + out1[12] = x27; + out1[13] = x29; + out1[14] = x31; + out1[15] = x32; + out1[16] = x33; + out1[17] = x35; + out1[18] = x37; + out1[19] = x39; + out1[20] = x41; + out1[21] = x43; + out1[22] = x45; + out1[23] = x46; + out1[24] = x47; + out1[25] = x49; + out1[26] = x51; + out1[27] = x53; + out1[28] = x55; + out1[29] = x57; + out1[30] = x59; + out1[31] = x60; +} + +/// The function fromBytes deserializes a field element NOT in the Montgomery domain from bytes in little-endian order. +/// +/// Preconditions: +/// 0 ≤ bytes_eval arg1 < m +/// Postconditions: +/// eval out1 mod m = bytes_eval arg1 mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn fromBytes(out1: *[4]u64, arg1: [32]u8) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (@as(u64, (arg1[31])) << 56); + const x2 = (@as(u64, (arg1[30])) << 48); + const x3 = (@as(u64, (arg1[29])) << 40); + const x4 = (@as(u64, (arg1[28])) << 32); + const x5 = (@as(u64, (arg1[27])) << 24); + const x6 = (@as(u64, (arg1[26])) << 16); + const x7 = (@as(u64, (arg1[25])) << 8); + const x8 = (arg1[24]); + const x9 = (@as(u64, (arg1[23])) << 56); + const x10 = (@as(u64, (arg1[22])) << 48); + const x11 = (@as(u64, (arg1[21])) << 40); + const x12 = (@as(u64, (arg1[20])) << 32); + const x13 = (@as(u64, (arg1[19])) << 24); + const x14 = (@as(u64, (arg1[18])) << 16); + const x15 = (@as(u64, (arg1[17])) << 8); + const x16 = (arg1[16]); + const x17 = (@as(u64, (arg1[15])) << 56); + const x18 = (@as(u64, (arg1[14])) << 48); + const x19 = (@as(u64, (arg1[13])) << 40); + const x20 = (@as(u64, (arg1[12])) << 32); + const x21 = (@as(u64, (arg1[11])) << 24); + const x22 = (@as(u64, (arg1[10])) << 16); + const x23 = (@as(u64, (arg1[9])) << 8); + const x24 = (arg1[8]); + const x25 = (@as(u64, (arg1[7])) << 56); + const x26 = (@as(u64, (arg1[6])) << 48); + const x27 = (@as(u64, (arg1[5])) << 40); + const x28 = (@as(u64, (arg1[4])) << 32); + const x29 = (@as(u64, (arg1[3])) << 24); + const x30 = (@as(u64, (arg1[2])) << 16); + const x31 = (@as(u64, (arg1[1])) << 8); + const x32 = (arg1[0]); + const x33 = (x31 + @as(u64, x32)); + const x34 = (x30 + x33); + const x35 = (x29 + x34); + const x36 = (x28 + x35); + const x37 = (x27 + x36); + const x38 = (x26 + x37); + const x39 = (x25 + x38); + const x40 = (x23 + @as(u64, x24)); + const x41 = (x22 + x40); + const x42 = (x21 + x41); + const x43 = (x20 + x42); + const x44 = (x19 + x43); + const x45 = (x18 + x44); + const x46 = (x17 + x45); + const x47 = (x15 + @as(u64, x16)); + const x48 = (x14 + x47); + const x49 = (x13 + x48); + const x50 = (x12 + x49); + const x51 = (x11 + x50); + const x52 = (x10 + x51); + const x53 = (x9 + x52); + const x54 = (x7 + @as(u64, x8)); + const x55 = (x6 + x54); + const x56 = (x5 + x55); + const x57 = (x4 + x56); + const x58 = (x3 + x57); + const x59 = (x2 + x58); + const x60 = (x1 + x59); + out1[0] = x39; + out1[1] = x46; + out1[2] = x53; + out1[3] = x60; +} + +/// The function setOne returns the field element one in the Montgomery domain. +/// +/// Postconditions: +/// eval (from_montgomery out1) mod m = 1 mod m +/// 0 ≤ eval out1 < m +/// +pub fn setOne(out1: *MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + out1[0] = 0x1000003d1; + out1[1] = 0x0; + out1[2] = 0x0; + out1[3] = 0x0; +} + +/// The function msat returns the saturated representation of the prime modulus. +/// +/// Postconditions: +/// twos_complement_eval out1 = m +/// 0 ≤ eval out1 < m +/// +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn msat(out1: *[5]u64) void { + @setRuntimeSafety(mode == .Debug); + + out1[0] = 0xfffffffefffffc2f; + out1[1] = 0xffffffffffffffff; + out1[2] = 0xffffffffffffffff; + out1[3] = 0xffffffffffffffff; + out1[4] = 0x0; +} + +/// The function divstep computes a divstep. +/// +/// Preconditions: +/// 0 ≤ eval arg4 < m +/// 0 ≤ eval arg5 < m +/// Postconditions: +/// out1 = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then 1 - arg1 else 1 + arg1) +/// twos_complement_eval out2 = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then twos_complement_eval arg3 else twos_complement_eval arg2) +/// twos_complement_eval out3 = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then ⌊(twos_complement_eval arg3 - twos_complement_eval arg2) / 2⌋ else ⌊(twos_complement_eval arg3 + (twos_complement_eval arg3 mod 2) * twos_complement_eval arg2) / 2⌋) +/// eval (from_montgomery out4) mod m = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then (2 * eval (from_montgomery arg5)) mod m else (2 * eval (from_montgomery arg4)) mod m) +/// eval (from_montgomery out5) mod m = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then (eval (from_montgomery arg4) - eval (from_montgomery arg4)) mod m else (eval (from_montgomery arg5) + (twos_complement_eval arg3 mod 2) * eval (from_montgomery arg4)) mod m) +/// 0 ≤ eval out5 < m +/// 0 ≤ eval out5 < m +/// 0 ≤ eval out2 < m +/// 0 ≤ eval out3 < m +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0xffffffffffffffff] +/// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg4: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg5: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// out3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// out4: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// out5: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn divstep(out1: *u64, out2: *[5]u64, out3: *[5]u64, out4: *[4]u64, out5: *[4]u64, arg1: u64, arg2: [5]u64, arg3: [5]u64, arg4: [4]u64, arg5: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + addcarryxU64(&x1, &x2, 0x0, (~arg1), 0x1); + const x3 = (@truncate(u1, (x1 >> 63)) & @truncate(u1, ((arg3[0]) & 0x1))); + var x4: u64 = undefined; + var x5: u1 = undefined; + addcarryxU64(&x4, &x5, 0x0, (~arg1), 0x1); + var x6: u64 = undefined; + cmovznzU64(&x6, x3, arg1, x4); + var x7: u64 = undefined; + cmovznzU64(&x7, x3, (arg2[0]), (arg3[0])); + var x8: u64 = undefined; + cmovznzU64(&x8, x3, (arg2[1]), (arg3[1])); + var x9: u64 = undefined; + cmovznzU64(&x9, x3, (arg2[2]), (arg3[2])); + var x10: u64 = undefined; + cmovznzU64(&x10, x3, (arg2[3]), (arg3[3])); + var x11: u64 = undefined; + cmovznzU64(&x11, x3, (arg2[4]), (arg3[4])); + var x12: u64 = undefined; + var x13: u1 = undefined; + addcarryxU64(&x12, &x13, 0x0, 0x1, (~(arg2[0]))); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, x13, 0x0, (~(arg2[1]))); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, 0x0, (~(arg2[2]))); + var x18: u64 = undefined; + var x19: u1 = undefined; + addcarryxU64(&x18, &x19, x17, 0x0, (~(arg2[3]))); + var x20: u64 = undefined; + var x21: u1 = undefined; + addcarryxU64(&x20, &x21, x19, 0x0, (~(arg2[4]))); + var x22: u64 = undefined; + cmovznzU64(&x22, x3, (arg3[0]), x12); + var x23: u64 = undefined; + cmovznzU64(&x23, x3, (arg3[1]), x14); + var x24: u64 = undefined; + cmovznzU64(&x24, x3, (arg3[2]), x16); + var x25: u64 = undefined; + cmovznzU64(&x25, x3, (arg3[3]), x18); + var x26: u64 = undefined; + cmovznzU64(&x26, x3, (arg3[4]), x20); + var x27: u64 = undefined; + cmovznzU64(&x27, x3, (arg4[0]), (arg5[0])); + var x28: u64 = undefined; + cmovznzU64(&x28, x3, (arg4[1]), (arg5[1])); + var x29: u64 = undefined; + cmovznzU64(&x29, x3, (arg4[2]), (arg5[2])); + var x30: u64 = undefined; + cmovznzU64(&x30, x3, (arg4[3]), (arg5[3])); + var x31: u64 = undefined; + var x32: u1 = undefined; + addcarryxU64(&x31, &x32, 0x0, x27, x27); + var x33: u64 = undefined; + var x34: u1 = undefined; + addcarryxU64(&x33, &x34, x32, x28, x28); + var x35: u64 = undefined; + var x36: u1 = undefined; + addcarryxU64(&x35, &x36, x34, x29, x29); + var x37: u64 = undefined; + var x38: u1 = undefined; + addcarryxU64(&x37, &x38, x36, x30, x30); + var x39: u64 = undefined; + var x40: u1 = undefined; + subborrowxU64(&x39, &x40, 0x0, x31, 0xfffffffefffffc2f); + var x41: u64 = undefined; + var x42: u1 = undefined; + subborrowxU64(&x41, &x42, x40, x33, 0xffffffffffffffff); + var x43: u64 = undefined; + var x44: u1 = undefined; + subborrowxU64(&x43, &x44, x42, x35, 0xffffffffffffffff); + var x45: u64 = undefined; + var x46: u1 = undefined; + subborrowxU64(&x45, &x46, x44, x37, 0xffffffffffffffff); + var x47: u64 = undefined; + var x48: u1 = undefined; + subborrowxU64(&x47, &x48, x46, @as(u64, x38), 0x0); + const x49 = (arg4[3]); + const x50 = (arg4[2]); + const x51 = (arg4[1]); + const x52 = (arg4[0]); + var x53: u64 = undefined; + var x54: u1 = undefined; + subborrowxU64(&x53, &x54, 0x0, 0x0, x52); + var x55: u64 = undefined; + var x56: u1 = undefined; + subborrowxU64(&x55, &x56, x54, 0x0, x51); + var x57: u64 = undefined; + var x58: u1 = undefined; + subborrowxU64(&x57, &x58, x56, 0x0, x50); + var x59: u64 = undefined; + var x60: u1 = undefined; + subborrowxU64(&x59, &x60, x58, 0x0, x49); + var x61: u64 = undefined; + cmovznzU64(&x61, x60, 0x0, 0xffffffffffffffff); + var x62: u64 = undefined; + var x63: u1 = undefined; + addcarryxU64(&x62, &x63, 0x0, x53, (x61 & 0xfffffffefffffc2f)); + var x64: u64 = undefined; + var x65: u1 = undefined; + addcarryxU64(&x64, &x65, x63, x55, x61); + var x66: u64 = undefined; + var x67: u1 = undefined; + addcarryxU64(&x66, &x67, x65, x57, x61); + var x68: u64 = undefined; + var x69: u1 = undefined; + addcarryxU64(&x68, &x69, x67, x59, x61); + var x70: u64 = undefined; + cmovznzU64(&x70, x3, (arg5[0]), x62); + var x71: u64 = undefined; + cmovznzU64(&x71, x3, (arg5[1]), x64); + var x72: u64 = undefined; + cmovznzU64(&x72, x3, (arg5[2]), x66); + var x73: u64 = undefined; + cmovznzU64(&x73, x3, (arg5[3]), x68); + const x74 = @truncate(u1, (x22 & 0x1)); + var x75: u64 = undefined; + cmovznzU64(&x75, x74, 0x0, x7); + var x76: u64 = undefined; + cmovznzU64(&x76, x74, 0x0, x8); + var x77: u64 = undefined; + cmovznzU64(&x77, x74, 0x0, x9); + var x78: u64 = undefined; + cmovznzU64(&x78, x74, 0x0, x10); + var x79: u64 = undefined; + cmovznzU64(&x79, x74, 0x0, x11); + var x80: u64 = undefined; + var x81: u1 = undefined; + addcarryxU64(&x80, &x81, 0x0, x22, x75); + var x82: u64 = undefined; + var x83: u1 = undefined; + addcarryxU64(&x82, &x83, x81, x23, x76); + var x84: u64 = undefined; + var x85: u1 = undefined; + addcarryxU64(&x84, &x85, x83, x24, x77); + var x86: u64 = undefined; + var x87: u1 = undefined; + addcarryxU64(&x86, &x87, x85, x25, x78); + var x88: u64 = undefined; + var x89: u1 = undefined; + addcarryxU64(&x88, &x89, x87, x26, x79); + var x90: u64 = undefined; + cmovznzU64(&x90, x74, 0x0, x27); + var x91: u64 = undefined; + cmovznzU64(&x91, x74, 0x0, x28); + var x92: u64 = undefined; + cmovznzU64(&x92, x74, 0x0, x29); + var x93: u64 = undefined; + cmovznzU64(&x93, x74, 0x0, x30); + var x94: u64 = undefined; + var x95: u1 = undefined; + addcarryxU64(&x94, &x95, 0x0, x70, x90); + var x96: u64 = undefined; + var x97: u1 = undefined; + addcarryxU64(&x96, &x97, x95, x71, x91); + var x98: u64 = undefined; + var x99: u1 = undefined; + addcarryxU64(&x98, &x99, x97, x72, x92); + var x100: u64 = undefined; + var x101: u1 = undefined; + addcarryxU64(&x100, &x101, x99, x73, x93); + var x102: u64 = undefined; + var x103: u1 = undefined; + subborrowxU64(&x102, &x103, 0x0, x94, 0xfffffffefffffc2f); + var x104: u64 = undefined; + var x105: u1 = undefined; + subborrowxU64(&x104, &x105, x103, x96, 0xffffffffffffffff); + var x106: u64 = undefined; + var x107: u1 = undefined; + subborrowxU64(&x106, &x107, x105, x98, 0xffffffffffffffff); + var x108: u64 = undefined; + var x109: u1 = undefined; + subborrowxU64(&x108, &x109, x107, x100, 0xffffffffffffffff); + var x110: u64 = undefined; + var x111: u1 = undefined; + subborrowxU64(&x110, &x111, x109, @as(u64, x101), 0x0); + var x112: u64 = undefined; + var x113: u1 = undefined; + addcarryxU64(&x112, &x113, 0x0, x6, 0x1); + const x114 = ((x80 >> 1) | ((x82 << 63) & 0xffffffffffffffff)); + const x115 = ((x82 >> 1) | ((x84 << 63) & 0xffffffffffffffff)); + const x116 = ((x84 >> 1) | ((x86 << 63) & 0xffffffffffffffff)); + const x117 = ((x86 >> 1) | ((x88 << 63) & 0xffffffffffffffff)); + const x118 = ((x88 & 0x8000000000000000) | (x88 >> 1)); + var x119: u64 = undefined; + cmovznzU64(&x119, x48, x39, x31); + var x120: u64 = undefined; + cmovznzU64(&x120, x48, x41, x33); + var x121: u64 = undefined; + cmovznzU64(&x121, x48, x43, x35); + var x122: u64 = undefined; + cmovznzU64(&x122, x48, x45, x37); + var x123: u64 = undefined; + cmovznzU64(&x123, x111, x102, x94); + var x124: u64 = undefined; + cmovznzU64(&x124, x111, x104, x96); + var x125: u64 = undefined; + cmovznzU64(&x125, x111, x106, x98); + var x126: u64 = undefined; + cmovznzU64(&x126, x111, x108, x100); + out1.* = x112; + out2[0] = x7; + out2[1] = x8; + out2[2] = x9; + out2[3] = x10; + out2[4] = x11; + out3[0] = x114; + out3[1] = x115; + out3[2] = x116; + out3[3] = x117; + out3[4] = x118; + out4[0] = x119; + out4[1] = x120; + out4[2] = x121; + out4[3] = x122; + out5[0] = x123; + out5[1] = x124; + out5[2] = x125; + out5[3] = x126; +} + +/// The function divstepPrecomp returns the precomputed value for Bernstein-Yang-inversion (in montgomery form). +/// +/// Postconditions: +/// eval (from_montgomery out1) = ⌊(m - 1) / 2⌋^(if ⌊log2 m⌋ + 1 < 46 then ⌊(49 * (⌊log2 m⌋ + 1) + 80) / 17⌋ else ⌊(49 * (⌊log2 m⌋ + 1) + 57) / 17⌋) +/// 0 ≤ eval out1 < m +/// +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn divstepPrecomp(out1: *[4]u64) void { + @setRuntimeSafety(mode == .Debug); + + out1[0] = 0xf201a41831525e0a; + out1[1] = 0x9953f9ddcd648d85; + out1[2] = 0xe86029463db210a9; + out1[3] = 0x24fb8a3104b03709; +} diff --git a/lib/std/crypto/pcurves/secp256k1/secp256k1_scalar_64.zig b/lib/std/crypto/pcurves/secp256k1/secp256k1_scalar_64.zig new file mode 100644 index 0000000000..8e9687f0a1 --- /dev/null +++ b/lib/std/crypto/pcurves/secp256k1/secp256k1_scalar_64.zig @@ -0,0 +1,2024 @@ +// Autogenerated: 'src/ExtractionOCaml/word_by_word_montgomery' --lang Zig --internal-static --public-function-case camelCase --private-function-case camelCase --public-type-case UpperCamelCase --private-type-case UpperCamelCase --no-prefix-fiat --package-name secp256k1_scalar '' 64 '2^256 - 432420386565659656852420866394968145599' mul square add sub opp from_montgomery to_montgomery nonzero selectznz to_bytes from_bytes one msat divstep divstep_precomp +// curve description (via package name): secp256k1_scalar +// machine_wordsize = 64 (from "64") +// requested operations: mul, square, add, sub, opp, from_montgomery, to_montgomery, nonzero, selectznz, to_bytes, from_bytes, one, msat, divstep, divstep_precomp +// m = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 (from "2^256 - 432420386565659656852420866394968145599") +// +// NOTE: In addition to the bounds specified above each function, all +// functions synthesized for this Montgomery arithmetic require the +// input to be strictly less than the prime modulus (m), and also +// require the input to be in the unique saturated representation. +// All functions also ensure that these two properties are true of +// return values. +// +// Computed values: +// eval z = z[0] + (z[1] << 64) + (z[2] << 128) + (z[3] << 192) +// bytes_eval z = z[0] + (z[1] << 8) + (z[2] << 16) + (z[3] << 24) + (z[4] << 32) + (z[5] << 40) + (z[6] << 48) + (z[7] << 56) + (z[8] << 64) + (z[9] << 72) + (z[10] << 80) + (z[11] << 88) + (z[12] << 96) + (z[13] << 104) + (z[14] << 112) + (z[15] << 120) + (z[16] << 128) + (z[17] << 136) + (z[18] << 144) + (z[19] << 152) + (z[20] << 160) + (z[21] << 168) + (z[22] << 176) + (z[23] << 184) + (z[24] << 192) + (z[25] << 200) + (z[26] << 208) + (z[27] << 216) + (z[28] << 224) + (z[29] << 232) + (z[30] << 240) + (z[31] << 248) +// twos_complement_eval z = let x1 := z[0] + (z[1] << 64) + (z[2] << 128) + (z[3] << 192) in +// if x1 & (2^256-1) < 2^255 then x1 & (2^256-1) else (x1 & (2^256-1)) - 2^256 + +const std = @import("std"); +const mode = @import("builtin").mode; // Checked arithmetic is disabled in non-debug modes to avoid side channels + +// The type MontgomeryDomainFieldElement is a field element in the Montgomery domain. +// Bounds: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub const MontgomeryDomainFieldElement = [4]u64; + +// The type NonMontgomeryDomainFieldElement is a field element NOT in the Montgomery domain. +// Bounds: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub const NonMontgomeryDomainFieldElement = [4]u64; + +/// The function addcarryxU64 is an addition with carry. +/// +/// Postconditions: +/// out1 = (arg1 + arg2 + arg3) mod 2^64 +/// out2 = ⌊(arg1 + arg2 + arg3) / 2^64⌋ +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// arg3: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [0x0 ~> 0x1] +inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { + @setRuntimeSafety(mode == .Debug); + + var t: u64 = undefined; + const carry1 = @addWithOverflow(u64, arg2, arg3, &t); + const carry2 = @addWithOverflow(u64, t, arg1, out1); + out2.* = @boolToInt(carry1) | @boolToInt(carry2); +} + +/// The function subborrowxU64 is a subtraction with borrow. +/// +/// Postconditions: +/// out1 = (-arg1 + arg2 + -arg3) mod 2^64 +/// out2 = -⌊(-arg1 + arg2 + -arg3) / 2^64⌋ +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// arg3: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [0x0 ~> 0x1] +inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { + @setRuntimeSafety(mode == .Debug); + + var t: u64 = undefined; + const carry1 = @subWithOverflow(u64, arg2, arg3, &t); + const carry2 = @subWithOverflow(u64, t, arg1, out1); + out2.* = @boolToInt(carry1) | @boolToInt(carry2); +} + +/// The function mulxU64 is a multiplication, returning the full double-width result. +/// +/// Postconditions: +/// out1 = (arg1 * arg2) mod 2^64 +/// out2 = ⌊arg1 * arg2 / 2^64⌋ +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0xffffffffffffffff] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [0x0 ~> 0xffffffffffffffff] +inline fn mulxU64(out1: *u64, out2: *u64, arg1: u64, arg2: u64) void { + @setRuntimeSafety(mode == .Debug); + + const x = @as(u128, arg1) * @as(u128, arg2); + out1.* = @truncate(u64, x); + out2.* = @truncate(u64, x >> 64); +} + +/// The function cmovznzU64 is a single-word conditional move. +/// +/// Postconditions: +/// out1 = (if arg1 = 0 then arg2 else arg3) +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [0x0 ~> 0xffffffffffffffff] +/// arg3: [0x0 ~> 0xffffffffffffffff] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +inline fn cmovznzU64(out1: *u64, arg1: u1, arg2: u64, arg3: u64) void { + @setRuntimeSafety(mode == .Debug); + + const mask = 0 -% @as(u64, arg1); + out1.* = (mask & arg3) | ((~mask) & arg2); +} + +/// The function mul multiplies two field elements in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// 0 ≤ eval arg2 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m +/// 0 ≤ eval out1 < m +/// +pub fn mul(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement, arg2: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[1]); + const x2 = (arg1[2]); + const x3 = (arg1[3]); + const x4 = (arg1[0]); + var x5: u64 = undefined; + var x6: u64 = undefined; + mulxU64(&x5, &x6, x4, (arg2[3])); + var x7: u64 = undefined; + var x8: u64 = undefined; + mulxU64(&x7, &x8, x4, (arg2[2])); + var x9: u64 = undefined; + var x10: u64 = undefined; + mulxU64(&x9, &x10, x4, (arg2[1])); + var x11: u64 = undefined; + var x12: u64 = undefined; + mulxU64(&x11, &x12, x4, (arg2[0])); + var x13: u64 = undefined; + var x14: u1 = undefined; + addcarryxU64(&x13, &x14, 0x0, x12, x9); + var x15: u64 = undefined; + var x16: u1 = undefined; + addcarryxU64(&x15, &x16, x14, x10, x7); + var x17: u64 = undefined; + var x18: u1 = undefined; + addcarryxU64(&x17, &x18, x16, x8, x5); + const x19 = (@as(u64, x18) + x6); + var x20: u64 = undefined; + var x21: u64 = undefined; + mulxU64(&x20, &x21, x11, 0x4b0dff665588b13f); + var x22: u64 = undefined; + var x23: u64 = undefined; + mulxU64(&x22, &x23, x20, 0xffffffffffffffff); + var x24: u64 = undefined; + var x25: u64 = undefined; + mulxU64(&x24, &x25, x20, 0xfffffffffffffffe); + var x26: u64 = undefined; + var x27: u64 = undefined; + mulxU64(&x26, &x27, x20, 0xbaaedce6af48a03b); + var x28: u64 = undefined; + var x29: u64 = undefined; + mulxU64(&x28, &x29, x20, 0xbfd25e8cd0364141); + var x30: u64 = undefined; + var x31: u1 = undefined; + addcarryxU64(&x30, &x31, 0x0, x29, x26); + var x32: u64 = undefined; + var x33: u1 = undefined; + addcarryxU64(&x32, &x33, x31, x27, x24); + var x34: u64 = undefined; + var x35: u1 = undefined; + addcarryxU64(&x34, &x35, x33, x25, x22); + const x36 = (@as(u64, x35) + x23); + var x37: u64 = undefined; + var x38: u1 = undefined; + addcarryxU64(&x37, &x38, 0x0, x11, x28); + var x39: u64 = undefined; + var x40: u1 = undefined; + addcarryxU64(&x39, &x40, x38, x13, x30); + var x41: u64 = undefined; + var x42: u1 = undefined; + addcarryxU64(&x41, &x42, x40, x15, x32); + var x43: u64 = undefined; + var x44: u1 = undefined; + addcarryxU64(&x43, &x44, x42, x17, x34); + var x45: u64 = undefined; + var x46: u1 = undefined; + addcarryxU64(&x45, &x46, x44, x19, x36); + var x47: u64 = undefined; + var x48: u64 = undefined; + mulxU64(&x47, &x48, x1, (arg2[3])); + var x49: u64 = undefined; + var x50: u64 = undefined; + mulxU64(&x49, &x50, x1, (arg2[2])); + var x51: u64 = undefined; + var x52: u64 = undefined; + mulxU64(&x51, &x52, x1, (arg2[1])); + var x53: u64 = undefined; + var x54: u64 = undefined; + mulxU64(&x53, &x54, x1, (arg2[0])); + var x55: u64 = undefined; + var x56: u1 = undefined; + addcarryxU64(&x55, &x56, 0x0, x54, x51); + var x57: u64 = undefined; + var x58: u1 = undefined; + addcarryxU64(&x57, &x58, x56, x52, x49); + var x59: u64 = undefined; + var x60: u1 = undefined; + addcarryxU64(&x59, &x60, x58, x50, x47); + const x61 = (@as(u64, x60) + x48); + var x62: u64 = undefined; + var x63: u1 = undefined; + addcarryxU64(&x62, &x63, 0x0, x39, x53); + var x64: u64 = undefined; + var x65: u1 = undefined; + addcarryxU64(&x64, &x65, x63, x41, x55); + var x66: u64 = undefined; + var x67: u1 = undefined; + addcarryxU64(&x66, &x67, x65, x43, x57); + var x68: u64 = undefined; + var x69: u1 = undefined; + addcarryxU64(&x68, &x69, x67, x45, x59); + var x70: u64 = undefined; + var x71: u1 = undefined; + addcarryxU64(&x70, &x71, x69, @as(u64, x46), x61); + var x72: u64 = undefined; + var x73: u64 = undefined; + mulxU64(&x72, &x73, x62, 0x4b0dff665588b13f); + var x74: u64 = undefined; + var x75: u64 = undefined; + mulxU64(&x74, &x75, x72, 0xffffffffffffffff); + var x76: u64 = undefined; + var x77: u64 = undefined; + mulxU64(&x76, &x77, x72, 0xfffffffffffffffe); + var x78: u64 = undefined; + var x79: u64 = undefined; + mulxU64(&x78, &x79, x72, 0xbaaedce6af48a03b); + var x80: u64 = undefined; + var x81: u64 = undefined; + mulxU64(&x80, &x81, x72, 0xbfd25e8cd0364141); + var x82: u64 = undefined; + var x83: u1 = undefined; + addcarryxU64(&x82, &x83, 0x0, x81, x78); + var x84: u64 = undefined; + var x85: u1 = undefined; + addcarryxU64(&x84, &x85, x83, x79, x76); + var x86: u64 = undefined; + var x87: u1 = undefined; + addcarryxU64(&x86, &x87, x85, x77, x74); + const x88 = (@as(u64, x87) + x75); + var x89: u64 = undefined; + var x90: u1 = undefined; + addcarryxU64(&x89, &x90, 0x0, x62, x80); + var x91: u64 = undefined; + var x92: u1 = undefined; + addcarryxU64(&x91, &x92, x90, x64, x82); + var x93: u64 = undefined; + var x94: u1 = undefined; + addcarryxU64(&x93, &x94, x92, x66, x84); + var x95: u64 = undefined; + var x96: u1 = undefined; + addcarryxU64(&x95, &x96, x94, x68, x86); + var x97: u64 = undefined; + var x98: u1 = undefined; + addcarryxU64(&x97, &x98, x96, x70, x88); + const x99 = (@as(u64, x98) + @as(u64, x71)); + var x100: u64 = undefined; + var x101: u64 = undefined; + mulxU64(&x100, &x101, x2, (arg2[3])); + var x102: u64 = undefined; + var x103: u64 = undefined; + mulxU64(&x102, &x103, x2, (arg2[2])); + var x104: u64 = undefined; + var x105: u64 = undefined; + mulxU64(&x104, &x105, x2, (arg2[1])); + var x106: u64 = undefined; + var x107: u64 = undefined; + mulxU64(&x106, &x107, x2, (arg2[0])); + var x108: u64 = undefined; + var x109: u1 = undefined; + addcarryxU64(&x108, &x109, 0x0, x107, x104); + var x110: u64 = undefined; + var x111: u1 = undefined; + addcarryxU64(&x110, &x111, x109, x105, x102); + var x112: u64 = undefined; + var x113: u1 = undefined; + addcarryxU64(&x112, &x113, x111, x103, x100); + const x114 = (@as(u64, x113) + x101); + var x115: u64 = undefined; + var x116: u1 = undefined; + addcarryxU64(&x115, &x116, 0x0, x91, x106); + var x117: u64 = undefined; + var x118: u1 = undefined; + addcarryxU64(&x117, &x118, x116, x93, x108); + var x119: u64 = undefined; + var x120: u1 = undefined; + addcarryxU64(&x119, &x120, x118, x95, x110); + var x121: u64 = undefined; + var x122: u1 = undefined; + addcarryxU64(&x121, &x122, x120, x97, x112); + var x123: u64 = undefined; + var x124: u1 = undefined; + addcarryxU64(&x123, &x124, x122, x99, x114); + var x125: u64 = undefined; + var x126: u64 = undefined; + mulxU64(&x125, &x126, x115, 0x4b0dff665588b13f); + var x127: u64 = undefined; + var x128: u64 = undefined; + mulxU64(&x127, &x128, x125, 0xffffffffffffffff); + var x129: u64 = undefined; + var x130: u64 = undefined; + mulxU64(&x129, &x130, x125, 0xfffffffffffffffe); + var x131: u64 = undefined; + var x132: u64 = undefined; + mulxU64(&x131, &x132, x125, 0xbaaedce6af48a03b); + var x133: u64 = undefined; + var x134: u64 = undefined; + mulxU64(&x133, &x134, x125, 0xbfd25e8cd0364141); + var x135: u64 = undefined; + var x136: u1 = undefined; + addcarryxU64(&x135, &x136, 0x0, x134, x131); + var x137: u64 = undefined; + var x138: u1 = undefined; + addcarryxU64(&x137, &x138, x136, x132, x129); + var x139: u64 = undefined; + var x140: u1 = undefined; + addcarryxU64(&x139, &x140, x138, x130, x127); + const x141 = (@as(u64, x140) + x128); + var x142: u64 = undefined; + var x143: u1 = undefined; + addcarryxU64(&x142, &x143, 0x0, x115, x133); + var x144: u64 = undefined; + var x145: u1 = undefined; + addcarryxU64(&x144, &x145, x143, x117, x135); + var x146: u64 = undefined; + var x147: u1 = undefined; + addcarryxU64(&x146, &x147, x145, x119, x137); + var x148: u64 = undefined; + var x149: u1 = undefined; + addcarryxU64(&x148, &x149, x147, x121, x139); + var x150: u64 = undefined; + var x151: u1 = undefined; + addcarryxU64(&x150, &x151, x149, x123, x141); + const x152 = (@as(u64, x151) + @as(u64, x124)); + var x153: u64 = undefined; + var x154: u64 = undefined; + mulxU64(&x153, &x154, x3, (arg2[3])); + var x155: u64 = undefined; + var x156: u64 = undefined; + mulxU64(&x155, &x156, x3, (arg2[2])); + var x157: u64 = undefined; + var x158: u64 = undefined; + mulxU64(&x157, &x158, x3, (arg2[1])); + var x159: u64 = undefined; + var x160: u64 = undefined; + mulxU64(&x159, &x160, x3, (arg2[0])); + var x161: u64 = undefined; + var x162: u1 = undefined; + addcarryxU64(&x161, &x162, 0x0, x160, x157); + var x163: u64 = undefined; + var x164: u1 = undefined; + addcarryxU64(&x163, &x164, x162, x158, x155); + var x165: u64 = undefined; + var x166: u1 = undefined; + addcarryxU64(&x165, &x166, x164, x156, x153); + const x167 = (@as(u64, x166) + x154); + var x168: u64 = undefined; + var x169: u1 = undefined; + addcarryxU64(&x168, &x169, 0x0, x144, x159); + var x170: u64 = undefined; + var x171: u1 = undefined; + addcarryxU64(&x170, &x171, x169, x146, x161); + var x172: u64 = undefined; + var x173: u1 = undefined; + addcarryxU64(&x172, &x173, x171, x148, x163); + var x174: u64 = undefined; + var x175: u1 = undefined; + addcarryxU64(&x174, &x175, x173, x150, x165); + var x176: u64 = undefined; + var x177: u1 = undefined; + addcarryxU64(&x176, &x177, x175, x152, x167); + var x178: u64 = undefined; + var x179: u64 = undefined; + mulxU64(&x178, &x179, x168, 0x4b0dff665588b13f); + var x180: u64 = undefined; + var x181: u64 = undefined; + mulxU64(&x180, &x181, x178, 0xffffffffffffffff); + var x182: u64 = undefined; + var x183: u64 = undefined; + mulxU64(&x182, &x183, x178, 0xfffffffffffffffe); + var x184: u64 = undefined; + var x185: u64 = undefined; + mulxU64(&x184, &x185, x178, 0xbaaedce6af48a03b); + var x186: u64 = undefined; + var x187: u64 = undefined; + mulxU64(&x186, &x187, x178, 0xbfd25e8cd0364141); + var x188: u64 = undefined; + var x189: u1 = undefined; + addcarryxU64(&x188, &x189, 0x0, x187, x184); + var x190: u64 = undefined; + var x191: u1 = undefined; + addcarryxU64(&x190, &x191, x189, x185, x182); + var x192: u64 = undefined; + var x193: u1 = undefined; + addcarryxU64(&x192, &x193, x191, x183, x180); + const x194 = (@as(u64, x193) + x181); + var x195: u64 = undefined; + var x196: u1 = undefined; + addcarryxU64(&x195, &x196, 0x0, x168, x186); + var x197: u64 = undefined; + var x198: u1 = undefined; + addcarryxU64(&x197, &x198, x196, x170, x188); + var x199: u64 = undefined; + var x200: u1 = undefined; + addcarryxU64(&x199, &x200, x198, x172, x190); + var x201: u64 = undefined; + var x202: u1 = undefined; + addcarryxU64(&x201, &x202, x200, x174, x192); + var x203: u64 = undefined; + var x204: u1 = undefined; + addcarryxU64(&x203, &x204, x202, x176, x194); + const x205 = (@as(u64, x204) + @as(u64, x177)); + var x206: u64 = undefined; + var x207: u1 = undefined; + subborrowxU64(&x206, &x207, 0x0, x197, 0xbfd25e8cd0364141); + var x208: u64 = undefined; + var x209: u1 = undefined; + subborrowxU64(&x208, &x209, x207, x199, 0xbaaedce6af48a03b); + var x210: u64 = undefined; + var x211: u1 = undefined; + subborrowxU64(&x210, &x211, x209, x201, 0xfffffffffffffffe); + var x212: u64 = undefined; + var x213: u1 = undefined; + subborrowxU64(&x212, &x213, x211, x203, 0xffffffffffffffff); + var x214: u64 = undefined; + var x215: u1 = undefined; + subborrowxU64(&x214, &x215, x213, x205, 0x0); + var x216: u64 = undefined; + cmovznzU64(&x216, x215, x206, x197); + var x217: u64 = undefined; + cmovznzU64(&x217, x215, x208, x199); + var x218: u64 = undefined; + cmovznzU64(&x218, x215, x210, x201); + var x219: u64 = undefined; + cmovznzU64(&x219, x215, x212, x203); + out1[0] = x216; + out1[1] = x217; + out1[2] = x218; + out1[3] = x219; +} + +/// The function square squares a field element in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg1)) mod m +/// 0 ≤ eval out1 < m +/// +pub fn square(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[1]); + const x2 = (arg1[2]); + const x3 = (arg1[3]); + const x4 = (arg1[0]); + var x5: u64 = undefined; + var x6: u64 = undefined; + mulxU64(&x5, &x6, x4, (arg1[3])); + var x7: u64 = undefined; + var x8: u64 = undefined; + mulxU64(&x7, &x8, x4, (arg1[2])); + var x9: u64 = undefined; + var x10: u64 = undefined; + mulxU64(&x9, &x10, x4, (arg1[1])); + var x11: u64 = undefined; + var x12: u64 = undefined; + mulxU64(&x11, &x12, x4, (arg1[0])); + var x13: u64 = undefined; + var x14: u1 = undefined; + addcarryxU64(&x13, &x14, 0x0, x12, x9); + var x15: u64 = undefined; + var x16: u1 = undefined; + addcarryxU64(&x15, &x16, x14, x10, x7); + var x17: u64 = undefined; + var x18: u1 = undefined; + addcarryxU64(&x17, &x18, x16, x8, x5); + const x19 = (@as(u64, x18) + x6); + var x20: u64 = undefined; + var x21: u64 = undefined; + mulxU64(&x20, &x21, x11, 0x4b0dff665588b13f); + var x22: u64 = undefined; + var x23: u64 = undefined; + mulxU64(&x22, &x23, x20, 0xffffffffffffffff); + var x24: u64 = undefined; + var x25: u64 = undefined; + mulxU64(&x24, &x25, x20, 0xfffffffffffffffe); + var x26: u64 = undefined; + var x27: u64 = undefined; + mulxU64(&x26, &x27, x20, 0xbaaedce6af48a03b); + var x28: u64 = undefined; + var x29: u64 = undefined; + mulxU64(&x28, &x29, x20, 0xbfd25e8cd0364141); + var x30: u64 = undefined; + var x31: u1 = undefined; + addcarryxU64(&x30, &x31, 0x0, x29, x26); + var x32: u64 = undefined; + var x33: u1 = undefined; + addcarryxU64(&x32, &x33, x31, x27, x24); + var x34: u64 = undefined; + var x35: u1 = undefined; + addcarryxU64(&x34, &x35, x33, x25, x22); + const x36 = (@as(u64, x35) + x23); + var x37: u64 = undefined; + var x38: u1 = undefined; + addcarryxU64(&x37, &x38, 0x0, x11, x28); + var x39: u64 = undefined; + var x40: u1 = undefined; + addcarryxU64(&x39, &x40, x38, x13, x30); + var x41: u64 = undefined; + var x42: u1 = undefined; + addcarryxU64(&x41, &x42, x40, x15, x32); + var x43: u64 = undefined; + var x44: u1 = undefined; + addcarryxU64(&x43, &x44, x42, x17, x34); + var x45: u64 = undefined; + var x46: u1 = undefined; + addcarryxU64(&x45, &x46, x44, x19, x36); + var x47: u64 = undefined; + var x48: u64 = undefined; + mulxU64(&x47, &x48, x1, (arg1[3])); + var x49: u64 = undefined; + var x50: u64 = undefined; + mulxU64(&x49, &x50, x1, (arg1[2])); + var x51: u64 = undefined; + var x52: u64 = undefined; + mulxU64(&x51, &x52, x1, (arg1[1])); + var x53: u64 = undefined; + var x54: u64 = undefined; + mulxU64(&x53, &x54, x1, (arg1[0])); + var x55: u64 = undefined; + var x56: u1 = undefined; + addcarryxU64(&x55, &x56, 0x0, x54, x51); + var x57: u64 = undefined; + var x58: u1 = undefined; + addcarryxU64(&x57, &x58, x56, x52, x49); + var x59: u64 = undefined; + var x60: u1 = undefined; + addcarryxU64(&x59, &x60, x58, x50, x47); + const x61 = (@as(u64, x60) + x48); + var x62: u64 = undefined; + var x63: u1 = undefined; + addcarryxU64(&x62, &x63, 0x0, x39, x53); + var x64: u64 = undefined; + var x65: u1 = undefined; + addcarryxU64(&x64, &x65, x63, x41, x55); + var x66: u64 = undefined; + var x67: u1 = undefined; + addcarryxU64(&x66, &x67, x65, x43, x57); + var x68: u64 = undefined; + var x69: u1 = undefined; + addcarryxU64(&x68, &x69, x67, x45, x59); + var x70: u64 = undefined; + var x71: u1 = undefined; + addcarryxU64(&x70, &x71, x69, @as(u64, x46), x61); + var x72: u64 = undefined; + var x73: u64 = undefined; + mulxU64(&x72, &x73, x62, 0x4b0dff665588b13f); + var x74: u64 = undefined; + var x75: u64 = undefined; + mulxU64(&x74, &x75, x72, 0xffffffffffffffff); + var x76: u64 = undefined; + var x77: u64 = undefined; + mulxU64(&x76, &x77, x72, 0xfffffffffffffffe); + var x78: u64 = undefined; + var x79: u64 = undefined; + mulxU64(&x78, &x79, x72, 0xbaaedce6af48a03b); + var x80: u64 = undefined; + var x81: u64 = undefined; + mulxU64(&x80, &x81, x72, 0xbfd25e8cd0364141); + var x82: u64 = undefined; + var x83: u1 = undefined; + addcarryxU64(&x82, &x83, 0x0, x81, x78); + var x84: u64 = undefined; + var x85: u1 = undefined; + addcarryxU64(&x84, &x85, x83, x79, x76); + var x86: u64 = undefined; + var x87: u1 = undefined; + addcarryxU64(&x86, &x87, x85, x77, x74); + const x88 = (@as(u64, x87) + x75); + var x89: u64 = undefined; + var x90: u1 = undefined; + addcarryxU64(&x89, &x90, 0x0, x62, x80); + var x91: u64 = undefined; + var x92: u1 = undefined; + addcarryxU64(&x91, &x92, x90, x64, x82); + var x93: u64 = undefined; + var x94: u1 = undefined; + addcarryxU64(&x93, &x94, x92, x66, x84); + var x95: u64 = undefined; + var x96: u1 = undefined; + addcarryxU64(&x95, &x96, x94, x68, x86); + var x97: u64 = undefined; + var x98: u1 = undefined; + addcarryxU64(&x97, &x98, x96, x70, x88); + const x99 = (@as(u64, x98) + @as(u64, x71)); + var x100: u64 = undefined; + var x101: u64 = undefined; + mulxU64(&x100, &x101, x2, (arg1[3])); + var x102: u64 = undefined; + var x103: u64 = undefined; + mulxU64(&x102, &x103, x2, (arg1[2])); + var x104: u64 = undefined; + var x105: u64 = undefined; + mulxU64(&x104, &x105, x2, (arg1[1])); + var x106: u64 = undefined; + var x107: u64 = undefined; + mulxU64(&x106, &x107, x2, (arg1[0])); + var x108: u64 = undefined; + var x109: u1 = undefined; + addcarryxU64(&x108, &x109, 0x0, x107, x104); + var x110: u64 = undefined; + var x111: u1 = undefined; + addcarryxU64(&x110, &x111, x109, x105, x102); + var x112: u64 = undefined; + var x113: u1 = undefined; + addcarryxU64(&x112, &x113, x111, x103, x100); + const x114 = (@as(u64, x113) + x101); + var x115: u64 = undefined; + var x116: u1 = undefined; + addcarryxU64(&x115, &x116, 0x0, x91, x106); + var x117: u64 = undefined; + var x118: u1 = undefined; + addcarryxU64(&x117, &x118, x116, x93, x108); + var x119: u64 = undefined; + var x120: u1 = undefined; + addcarryxU64(&x119, &x120, x118, x95, x110); + var x121: u64 = undefined; + var x122: u1 = undefined; + addcarryxU64(&x121, &x122, x120, x97, x112); + var x123: u64 = undefined; + var x124: u1 = undefined; + addcarryxU64(&x123, &x124, x122, x99, x114); + var x125: u64 = undefined; + var x126: u64 = undefined; + mulxU64(&x125, &x126, x115, 0x4b0dff665588b13f); + var x127: u64 = undefined; + var x128: u64 = undefined; + mulxU64(&x127, &x128, x125, 0xffffffffffffffff); + var x129: u64 = undefined; + var x130: u64 = undefined; + mulxU64(&x129, &x130, x125, 0xfffffffffffffffe); + var x131: u64 = undefined; + var x132: u64 = undefined; + mulxU64(&x131, &x132, x125, 0xbaaedce6af48a03b); + var x133: u64 = undefined; + var x134: u64 = undefined; + mulxU64(&x133, &x134, x125, 0xbfd25e8cd0364141); + var x135: u64 = undefined; + var x136: u1 = undefined; + addcarryxU64(&x135, &x136, 0x0, x134, x131); + var x137: u64 = undefined; + var x138: u1 = undefined; + addcarryxU64(&x137, &x138, x136, x132, x129); + var x139: u64 = undefined; + var x140: u1 = undefined; + addcarryxU64(&x139, &x140, x138, x130, x127); + const x141 = (@as(u64, x140) + x128); + var x142: u64 = undefined; + var x143: u1 = undefined; + addcarryxU64(&x142, &x143, 0x0, x115, x133); + var x144: u64 = undefined; + var x145: u1 = undefined; + addcarryxU64(&x144, &x145, x143, x117, x135); + var x146: u64 = undefined; + var x147: u1 = undefined; + addcarryxU64(&x146, &x147, x145, x119, x137); + var x148: u64 = undefined; + var x149: u1 = undefined; + addcarryxU64(&x148, &x149, x147, x121, x139); + var x150: u64 = undefined; + var x151: u1 = undefined; + addcarryxU64(&x150, &x151, x149, x123, x141); + const x152 = (@as(u64, x151) + @as(u64, x124)); + var x153: u64 = undefined; + var x154: u64 = undefined; + mulxU64(&x153, &x154, x3, (arg1[3])); + var x155: u64 = undefined; + var x156: u64 = undefined; + mulxU64(&x155, &x156, x3, (arg1[2])); + var x157: u64 = undefined; + var x158: u64 = undefined; + mulxU64(&x157, &x158, x3, (arg1[1])); + var x159: u64 = undefined; + var x160: u64 = undefined; + mulxU64(&x159, &x160, x3, (arg1[0])); + var x161: u64 = undefined; + var x162: u1 = undefined; + addcarryxU64(&x161, &x162, 0x0, x160, x157); + var x163: u64 = undefined; + var x164: u1 = undefined; + addcarryxU64(&x163, &x164, x162, x158, x155); + var x165: u64 = undefined; + var x166: u1 = undefined; + addcarryxU64(&x165, &x166, x164, x156, x153); + const x167 = (@as(u64, x166) + x154); + var x168: u64 = undefined; + var x169: u1 = undefined; + addcarryxU64(&x168, &x169, 0x0, x144, x159); + var x170: u64 = undefined; + var x171: u1 = undefined; + addcarryxU64(&x170, &x171, x169, x146, x161); + var x172: u64 = undefined; + var x173: u1 = undefined; + addcarryxU64(&x172, &x173, x171, x148, x163); + var x174: u64 = undefined; + var x175: u1 = undefined; + addcarryxU64(&x174, &x175, x173, x150, x165); + var x176: u64 = undefined; + var x177: u1 = undefined; + addcarryxU64(&x176, &x177, x175, x152, x167); + var x178: u64 = undefined; + var x179: u64 = undefined; + mulxU64(&x178, &x179, x168, 0x4b0dff665588b13f); + var x180: u64 = undefined; + var x181: u64 = undefined; + mulxU64(&x180, &x181, x178, 0xffffffffffffffff); + var x182: u64 = undefined; + var x183: u64 = undefined; + mulxU64(&x182, &x183, x178, 0xfffffffffffffffe); + var x184: u64 = undefined; + var x185: u64 = undefined; + mulxU64(&x184, &x185, x178, 0xbaaedce6af48a03b); + var x186: u64 = undefined; + var x187: u64 = undefined; + mulxU64(&x186, &x187, x178, 0xbfd25e8cd0364141); + var x188: u64 = undefined; + var x189: u1 = undefined; + addcarryxU64(&x188, &x189, 0x0, x187, x184); + var x190: u64 = undefined; + var x191: u1 = undefined; + addcarryxU64(&x190, &x191, x189, x185, x182); + var x192: u64 = undefined; + var x193: u1 = undefined; + addcarryxU64(&x192, &x193, x191, x183, x180); + const x194 = (@as(u64, x193) + x181); + var x195: u64 = undefined; + var x196: u1 = undefined; + addcarryxU64(&x195, &x196, 0x0, x168, x186); + var x197: u64 = undefined; + var x198: u1 = undefined; + addcarryxU64(&x197, &x198, x196, x170, x188); + var x199: u64 = undefined; + var x200: u1 = undefined; + addcarryxU64(&x199, &x200, x198, x172, x190); + var x201: u64 = undefined; + var x202: u1 = undefined; + addcarryxU64(&x201, &x202, x200, x174, x192); + var x203: u64 = undefined; + var x204: u1 = undefined; + addcarryxU64(&x203, &x204, x202, x176, x194); + const x205 = (@as(u64, x204) + @as(u64, x177)); + var x206: u64 = undefined; + var x207: u1 = undefined; + subborrowxU64(&x206, &x207, 0x0, x197, 0xbfd25e8cd0364141); + var x208: u64 = undefined; + var x209: u1 = undefined; + subborrowxU64(&x208, &x209, x207, x199, 0xbaaedce6af48a03b); + var x210: u64 = undefined; + var x211: u1 = undefined; + subborrowxU64(&x210, &x211, x209, x201, 0xfffffffffffffffe); + var x212: u64 = undefined; + var x213: u1 = undefined; + subborrowxU64(&x212, &x213, x211, x203, 0xffffffffffffffff); + var x214: u64 = undefined; + var x215: u1 = undefined; + subborrowxU64(&x214, &x215, x213, x205, 0x0); + var x216: u64 = undefined; + cmovznzU64(&x216, x215, x206, x197); + var x217: u64 = undefined; + cmovznzU64(&x217, x215, x208, x199); + var x218: u64 = undefined; + cmovznzU64(&x218, x215, x210, x201); + var x219: u64 = undefined; + cmovznzU64(&x219, x215, x212, x203); + out1[0] = x216; + out1[1] = x217; + out1[2] = x218; + out1[3] = x219; +} + +/// The function add adds two field elements in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// 0 ≤ eval arg2 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m +/// 0 ≤ eval out1 < m +/// +pub fn add(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement, arg2: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + addcarryxU64(&x1, &x2, 0x0, (arg1[0]), (arg2[0])); + var x3: u64 = undefined; + var x4: u1 = undefined; + addcarryxU64(&x3, &x4, x2, (arg1[1]), (arg2[1])); + var x5: u64 = undefined; + var x6: u1 = undefined; + addcarryxU64(&x5, &x6, x4, (arg1[2]), (arg2[2])); + var x7: u64 = undefined; + var x8: u1 = undefined; + addcarryxU64(&x7, &x8, x6, (arg1[3]), (arg2[3])); + var x9: u64 = undefined; + var x10: u1 = undefined; + subborrowxU64(&x9, &x10, 0x0, x1, 0xbfd25e8cd0364141); + var x11: u64 = undefined; + var x12: u1 = undefined; + subborrowxU64(&x11, &x12, x10, x3, 0xbaaedce6af48a03b); + var x13: u64 = undefined; + var x14: u1 = undefined; + subborrowxU64(&x13, &x14, x12, x5, 0xfffffffffffffffe); + var x15: u64 = undefined; + var x16: u1 = undefined; + subborrowxU64(&x15, &x16, x14, x7, 0xffffffffffffffff); + var x17: u64 = undefined; + var x18: u1 = undefined; + subborrowxU64(&x17, &x18, x16, @as(u64, x8), 0x0); + var x19: u64 = undefined; + cmovznzU64(&x19, x18, x9, x1); + var x20: u64 = undefined; + cmovznzU64(&x20, x18, x11, x3); + var x21: u64 = undefined; + cmovznzU64(&x21, x18, x13, x5); + var x22: u64 = undefined; + cmovznzU64(&x22, x18, x15, x7); + out1[0] = x19; + out1[1] = x20; + out1[2] = x21; + out1[3] = x22; +} + +/// The function sub subtracts two field elements in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// 0 ≤ eval arg2 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m +/// 0 ≤ eval out1 < m +/// +pub fn sub(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement, arg2: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + subborrowxU64(&x1, &x2, 0x0, (arg1[0]), (arg2[0])); + var x3: u64 = undefined; + var x4: u1 = undefined; + subborrowxU64(&x3, &x4, x2, (arg1[1]), (arg2[1])); + var x5: u64 = undefined; + var x6: u1 = undefined; + subborrowxU64(&x5, &x6, x4, (arg1[2]), (arg2[2])); + var x7: u64 = undefined; + var x8: u1 = undefined; + subborrowxU64(&x7, &x8, x6, (arg1[3]), (arg2[3])); + var x9: u64 = undefined; + cmovznzU64(&x9, x8, 0x0, 0xffffffffffffffff); + var x10: u64 = undefined; + var x11: u1 = undefined; + addcarryxU64(&x10, &x11, 0x0, x1, (x9 & 0xbfd25e8cd0364141)); + var x12: u64 = undefined; + var x13: u1 = undefined; + addcarryxU64(&x12, &x13, x11, x3, (x9 & 0xbaaedce6af48a03b)); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, x13, x5, (x9 & 0xfffffffffffffffe)); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, x7, x9); + out1[0] = x10; + out1[1] = x12; + out1[2] = x14; + out1[3] = x16; +} + +/// The function opp negates a field element in the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = -eval (from_montgomery arg1) mod m +/// 0 ≤ eval out1 < m +/// +pub fn opp(out1: *MontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + subborrowxU64(&x1, &x2, 0x0, 0x0, (arg1[0])); + var x3: u64 = undefined; + var x4: u1 = undefined; + subborrowxU64(&x3, &x4, x2, 0x0, (arg1[1])); + var x5: u64 = undefined; + var x6: u1 = undefined; + subborrowxU64(&x5, &x6, x4, 0x0, (arg1[2])); + var x7: u64 = undefined; + var x8: u1 = undefined; + subborrowxU64(&x7, &x8, x6, 0x0, (arg1[3])); + var x9: u64 = undefined; + cmovznzU64(&x9, x8, 0x0, 0xffffffffffffffff); + var x10: u64 = undefined; + var x11: u1 = undefined; + addcarryxU64(&x10, &x11, 0x0, x1, (x9 & 0xbfd25e8cd0364141)); + var x12: u64 = undefined; + var x13: u1 = undefined; + addcarryxU64(&x12, &x13, x11, x3, (x9 & 0xbaaedce6af48a03b)); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, x13, x5, (x9 & 0xfffffffffffffffe)); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, x7, x9); + out1[0] = x10; + out1[1] = x12; + out1[2] = x14; + out1[3] = x16; +} + +/// The function fromMontgomery translates a field element out of the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^4) mod m +/// 0 ≤ eval out1 < m +/// +pub fn fromMontgomery(out1: *NonMontgomeryDomainFieldElement, arg1: MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[0]); + var x2: u64 = undefined; + var x3: u64 = undefined; + mulxU64(&x2, &x3, x1, 0x4b0dff665588b13f); + var x4: u64 = undefined; + var x5: u64 = undefined; + mulxU64(&x4, &x5, x2, 0xffffffffffffffff); + var x6: u64 = undefined; + var x7: u64 = undefined; + mulxU64(&x6, &x7, x2, 0xfffffffffffffffe); + var x8: u64 = undefined; + var x9: u64 = undefined; + mulxU64(&x8, &x9, x2, 0xbaaedce6af48a03b); + var x10: u64 = undefined; + var x11: u64 = undefined; + mulxU64(&x10, &x11, x2, 0xbfd25e8cd0364141); + var x12: u64 = undefined; + var x13: u1 = undefined; + addcarryxU64(&x12, &x13, 0x0, x11, x8); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, x13, x9, x6); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, x7, x4); + var x18: u64 = undefined; + var x19: u1 = undefined; + addcarryxU64(&x18, &x19, 0x0, x1, x10); + var x20: u64 = undefined; + var x21: u1 = undefined; + addcarryxU64(&x20, &x21, x19, 0x0, x12); + var x22: u64 = undefined; + var x23: u1 = undefined; + addcarryxU64(&x22, &x23, x21, 0x0, x14); + var x24: u64 = undefined; + var x25: u1 = undefined; + addcarryxU64(&x24, &x25, x23, 0x0, x16); + var x26: u64 = undefined; + var x27: u1 = undefined; + addcarryxU64(&x26, &x27, x25, 0x0, (@as(u64, x17) + x5)); + var x28: u64 = undefined; + var x29: u1 = undefined; + addcarryxU64(&x28, &x29, 0x0, x20, (arg1[1])); + var x30: u64 = undefined; + var x31: u1 = undefined; + addcarryxU64(&x30, &x31, x29, x22, 0x0); + var x32: u64 = undefined; + var x33: u1 = undefined; + addcarryxU64(&x32, &x33, x31, x24, 0x0); + var x34: u64 = undefined; + var x35: u1 = undefined; + addcarryxU64(&x34, &x35, x33, x26, 0x0); + var x36: u64 = undefined; + var x37: u64 = undefined; + mulxU64(&x36, &x37, x28, 0x4b0dff665588b13f); + var x38: u64 = undefined; + var x39: u64 = undefined; + mulxU64(&x38, &x39, x36, 0xffffffffffffffff); + var x40: u64 = undefined; + var x41: u64 = undefined; + mulxU64(&x40, &x41, x36, 0xfffffffffffffffe); + var x42: u64 = undefined; + var x43: u64 = undefined; + mulxU64(&x42, &x43, x36, 0xbaaedce6af48a03b); + var x44: u64 = undefined; + var x45: u64 = undefined; + mulxU64(&x44, &x45, x36, 0xbfd25e8cd0364141); + var x46: u64 = undefined; + var x47: u1 = undefined; + addcarryxU64(&x46, &x47, 0x0, x45, x42); + var x48: u64 = undefined; + var x49: u1 = undefined; + addcarryxU64(&x48, &x49, x47, x43, x40); + var x50: u64 = undefined; + var x51: u1 = undefined; + addcarryxU64(&x50, &x51, x49, x41, x38); + var x52: u64 = undefined; + var x53: u1 = undefined; + addcarryxU64(&x52, &x53, 0x0, x28, x44); + var x54: u64 = undefined; + var x55: u1 = undefined; + addcarryxU64(&x54, &x55, x53, x30, x46); + var x56: u64 = undefined; + var x57: u1 = undefined; + addcarryxU64(&x56, &x57, x55, x32, x48); + var x58: u64 = undefined; + var x59: u1 = undefined; + addcarryxU64(&x58, &x59, x57, x34, x50); + var x60: u64 = undefined; + var x61: u1 = undefined; + addcarryxU64(&x60, &x61, x59, (@as(u64, x35) + @as(u64, x27)), (@as(u64, x51) + x39)); + var x62: u64 = undefined; + var x63: u1 = undefined; + addcarryxU64(&x62, &x63, 0x0, x54, (arg1[2])); + var x64: u64 = undefined; + var x65: u1 = undefined; + addcarryxU64(&x64, &x65, x63, x56, 0x0); + var x66: u64 = undefined; + var x67: u1 = undefined; + addcarryxU64(&x66, &x67, x65, x58, 0x0); + var x68: u64 = undefined; + var x69: u1 = undefined; + addcarryxU64(&x68, &x69, x67, x60, 0x0); + var x70: u64 = undefined; + var x71: u64 = undefined; + mulxU64(&x70, &x71, x62, 0x4b0dff665588b13f); + var x72: u64 = undefined; + var x73: u64 = undefined; + mulxU64(&x72, &x73, x70, 0xffffffffffffffff); + var x74: u64 = undefined; + var x75: u64 = undefined; + mulxU64(&x74, &x75, x70, 0xfffffffffffffffe); + var x76: u64 = undefined; + var x77: u64 = undefined; + mulxU64(&x76, &x77, x70, 0xbaaedce6af48a03b); + var x78: u64 = undefined; + var x79: u64 = undefined; + mulxU64(&x78, &x79, x70, 0xbfd25e8cd0364141); + var x80: u64 = undefined; + var x81: u1 = undefined; + addcarryxU64(&x80, &x81, 0x0, x79, x76); + var x82: u64 = undefined; + var x83: u1 = undefined; + addcarryxU64(&x82, &x83, x81, x77, x74); + var x84: u64 = undefined; + var x85: u1 = undefined; + addcarryxU64(&x84, &x85, x83, x75, x72); + var x86: u64 = undefined; + var x87: u1 = undefined; + addcarryxU64(&x86, &x87, 0x0, x62, x78); + var x88: u64 = undefined; + var x89: u1 = undefined; + addcarryxU64(&x88, &x89, x87, x64, x80); + var x90: u64 = undefined; + var x91: u1 = undefined; + addcarryxU64(&x90, &x91, x89, x66, x82); + var x92: u64 = undefined; + var x93: u1 = undefined; + addcarryxU64(&x92, &x93, x91, x68, x84); + var x94: u64 = undefined; + var x95: u1 = undefined; + addcarryxU64(&x94, &x95, x93, (@as(u64, x69) + @as(u64, x61)), (@as(u64, x85) + x73)); + var x96: u64 = undefined; + var x97: u1 = undefined; + addcarryxU64(&x96, &x97, 0x0, x88, (arg1[3])); + var x98: u64 = undefined; + var x99: u1 = undefined; + addcarryxU64(&x98, &x99, x97, x90, 0x0); + var x100: u64 = undefined; + var x101: u1 = undefined; + addcarryxU64(&x100, &x101, x99, x92, 0x0); + var x102: u64 = undefined; + var x103: u1 = undefined; + addcarryxU64(&x102, &x103, x101, x94, 0x0); + var x104: u64 = undefined; + var x105: u64 = undefined; + mulxU64(&x104, &x105, x96, 0x4b0dff665588b13f); + var x106: u64 = undefined; + var x107: u64 = undefined; + mulxU64(&x106, &x107, x104, 0xffffffffffffffff); + var x108: u64 = undefined; + var x109: u64 = undefined; + mulxU64(&x108, &x109, x104, 0xfffffffffffffffe); + var x110: u64 = undefined; + var x111: u64 = undefined; + mulxU64(&x110, &x111, x104, 0xbaaedce6af48a03b); + var x112: u64 = undefined; + var x113: u64 = undefined; + mulxU64(&x112, &x113, x104, 0xbfd25e8cd0364141); + var x114: u64 = undefined; + var x115: u1 = undefined; + addcarryxU64(&x114, &x115, 0x0, x113, x110); + var x116: u64 = undefined; + var x117: u1 = undefined; + addcarryxU64(&x116, &x117, x115, x111, x108); + var x118: u64 = undefined; + var x119: u1 = undefined; + addcarryxU64(&x118, &x119, x117, x109, x106); + var x120: u64 = undefined; + var x121: u1 = undefined; + addcarryxU64(&x120, &x121, 0x0, x96, x112); + var x122: u64 = undefined; + var x123: u1 = undefined; + addcarryxU64(&x122, &x123, x121, x98, x114); + var x124: u64 = undefined; + var x125: u1 = undefined; + addcarryxU64(&x124, &x125, x123, x100, x116); + var x126: u64 = undefined; + var x127: u1 = undefined; + addcarryxU64(&x126, &x127, x125, x102, x118); + var x128: u64 = undefined; + var x129: u1 = undefined; + addcarryxU64(&x128, &x129, x127, (@as(u64, x103) + @as(u64, x95)), (@as(u64, x119) + x107)); + var x130: u64 = undefined; + var x131: u1 = undefined; + subborrowxU64(&x130, &x131, 0x0, x122, 0xbfd25e8cd0364141); + var x132: u64 = undefined; + var x133: u1 = undefined; + subborrowxU64(&x132, &x133, x131, x124, 0xbaaedce6af48a03b); + var x134: u64 = undefined; + var x135: u1 = undefined; + subborrowxU64(&x134, &x135, x133, x126, 0xfffffffffffffffe); + var x136: u64 = undefined; + var x137: u1 = undefined; + subborrowxU64(&x136, &x137, x135, x128, 0xffffffffffffffff); + var x138: u64 = undefined; + var x139: u1 = undefined; + subborrowxU64(&x138, &x139, x137, @as(u64, x129), 0x0); + var x140: u64 = undefined; + cmovznzU64(&x140, x139, x130, x122); + var x141: u64 = undefined; + cmovznzU64(&x141, x139, x132, x124); + var x142: u64 = undefined; + cmovznzU64(&x142, x139, x134, x126); + var x143: u64 = undefined; + cmovznzU64(&x143, x139, x136, x128); + out1[0] = x140; + out1[1] = x141; + out1[2] = x142; + out1[3] = x143; +} + +/// The function toMontgomery translates a field element into the Montgomery domain. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// eval (from_montgomery out1) mod m = eval arg1 mod m +/// 0 ≤ eval out1 < m +/// +pub fn toMontgomery(out1: *MontgomeryDomainFieldElement, arg1: NonMontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[1]); + const x2 = (arg1[2]); + const x3 = (arg1[3]); + const x4 = (arg1[0]); + var x5: u64 = undefined; + var x6: u64 = undefined; + mulxU64(&x5, &x6, x4, 0x9d671cd581c69bc5); + var x7: u64 = undefined; + var x8: u64 = undefined; + mulxU64(&x7, &x8, x4, 0xe697f5e45bcd07c6); + var x9: u64 = undefined; + var x10: u64 = undefined; + mulxU64(&x9, &x10, x4, 0x741496c20e7cf878); + var x11: u64 = undefined; + var x12: u64 = undefined; + mulxU64(&x11, &x12, x4, 0x896cf21467d7d140); + var x13: u64 = undefined; + var x14: u1 = undefined; + addcarryxU64(&x13, &x14, 0x0, x12, x9); + var x15: u64 = undefined; + var x16: u1 = undefined; + addcarryxU64(&x15, &x16, x14, x10, x7); + var x17: u64 = undefined; + var x18: u1 = undefined; + addcarryxU64(&x17, &x18, x16, x8, x5); + var x19: u64 = undefined; + var x20: u64 = undefined; + mulxU64(&x19, &x20, x11, 0x4b0dff665588b13f); + var x21: u64 = undefined; + var x22: u64 = undefined; + mulxU64(&x21, &x22, x19, 0xffffffffffffffff); + var x23: u64 = undefined; + var x24: u64 = undefined; + mulxU64(&x23, &x24, x19, 0xfffffffffffffffe); + var x25: u64 = undefined; + var x26: u64 = undefined; + mulxU64(&x25, &x26, x19, 0xbaaedce6af48a03b); + var x27: u64 = undefined; + var x28: u64 = undefined; + mulxU64(&x27, &x28, x19, 0xbfd25e8cd0364141); + var x29: u64 = undefined; + var x30: u1 = undefined; + addcarryxU64(&x29, &x30, 0x0, x28, x25); + var x31: u64 = undefined; + var x32: u1 = undefined; + addcarryxU64(&x31, &x32, x30, x26, x23); + var x33: u64 = undefined; + var x34: u1 = undefined; + addcarryxU64(&x33, &x34, x32, x24, x21); + var x35: u64 = undefined; + var x36: u1 = undefined; + addcarryxU64(&x35, &x36, 0x0, x11, x27); + var x37: u64 = undefined; + var x38: u1 = undefined; + addcarryxU64(&x37, &x38, x36, x13, x29); + var x39: u64 = undefined; + var x40: u1 = undefined; + addcarryxU64(&x39, &x40, x38, x15, x31); + var x41: u64 = undefined; + var x42: u1 = undefined; + addcarryxU64(&x41, &x42, x40, x17, x33); + var x43: u64 = undefined; + var x44: u1 = undefined; + addcarryxU64(&x43, &x44, x42, (@as(u64, x18) + x6), (@as(u64, x34) + x22)); + var x45: u64 = undefined; + var x46: u64 = undefined; + mulxU64(&x45, &x46, x1, 0x9d671cd581c69bc5); + var x47: u64 = undefined; + var x48: u64 = undefined; + mulxU64(&x47, &x48, x1, 0xe697f5e45bcd07c6); + var x49: u64 = undefined; + var x50: u64 = undefined; + mulxU64(&x49, &x50, x1, 0x741496c20e7cf878); + var x51: u64 = undefined; + var x52: u64 = undefined; + mulxU64(&x51, &x52, x1, 0x896cf21467d7d140); + var x53: u64 = undefined; + var x54: u1 = undefined; + addcarryxU64(&x53, &x54, 0x0, x52, x49); + var x55: u64 = undefined; + var x56: u1 = undefined; + addcarryxU64(&x55, &x56, x54, x50, x47); + var x57: u64 = undefined; + var x58: u1 = undefined; + addcarryxU64(&x57, &x58, x56, x48, x45); + var x59: u64 = undefined; + var x60: u1 = undefined; + addcarryxU64(&x59, &x60, 0x0, x37, x51); + var x61: u64 = undefined; + var x62: u1 = undefined; + addcarryxU64(&x61, &x62, x60, x39, x53); + var x63: u64 = undefined; + var x64: u1 = undefined; + addcarryxU64(&x63, &x64, x62, x41, x55); + var x65: u64 = undefined; + var x66: u1 = undefined; + addcarryxU64(&x65, &x66, x64, x43, x57); + var x67: u64 = undefined; + var x68: u64 = undefined; + mulxU64(&x67, &x68, x59, 0x4b0dff665588b13f); + var x69: u64 = undefined; + var x70: u64 = undefined; + mulxU64(&x69, &x70, x67, 0xffffffffffffffff); + var x71: u64 = undefined; + var x72: u64 = undefined; + mulxU64(&x71, &x72, x67, 0xfffffffffffffffe); + var x73: u64 = undefined; + var x74: u64 = undefined; + mulxU64(&x73, &x74, x67, 0xbaaedce6af48a03b); + var x75: u64 = undefined; + var x76: u64 = undefined; + mulxU64(&x75, &x76, x67, 0xbfd25e8cd0364141); + var x77: u64 = undefined; + var x78: u1 = undefined; + addcarryxU64(&x77, &x78, 0x0, x76, x73); + var x79: u64 = undefined; + var x80: u1 = undefined; + addcarryxU64(&x79, &x80, x78, x74, x71); + var x81: u64 = undefined; + var x82: u1 = undefined; + addcarryxU64(&x81, &x82, x80, x72, x69); + var x83: u64 = undefined; + var x84: u1 = undefined; + addcarryxU64(&x83, &x84, 0x0, x59, x75); + var x85: u64 = undefined; + var x86: u1 = undefined; + addcarryxU64(&x85, &x86, x84, x61, x77); + var x87: u64 = undefined; + var x88: u1 = undefined; + addcarryxU64(&x87, &x88, x86, x63, x79); + var x89: u64 = undefined; + var x90: u1 = undefined; + addcarryxU64(&x89, &x90, x88, x65, x81); + var x91: u64 = undefined; + var x92: u1 = undefined; + addcarryxU64(&x91, &x92, x90, ((@as(u64, x66) + @as(u64, x44)) + (@as(u64, x58) + x46)), (@as(u64, x82) + x70)); + var x93: u64 = undefined; + var x94: u64 = undefined; + mulxU64(&x93, &x94, x2, 0x9d671cd581c69bc5); + var x95: u64 = undefined; + var x96: u64 = undefined; + mulxU64(&x95, &x96, x2, 0xe697f5e45bcd07c6); + var x97: u64 = undefined; + var x98: u64 = undefined; + mulxU64(&x97, &x98, x2, 0x741496c20e7cf878); + var x99: u64 = undefined; + var x100: u64 = undefined; + mulxU64(&x99, &x100, x2, 0x896cf21467d7d140); + var x101: u64 = undefined; + var x102: u1 = undefined; + addcarryxU64(&x101, &x102, 0x0, x100, x97); + var x103: u64 = undefined; + var x104: u1 = undefined; + addcarryxU64(&x103, &x104, x102, x98, x95); + var x105: u64 = undefined; + var x106: u1 = undefined; + addcarryxU64(&x105, &x106, x104, x96, x93); + var x107: u64 = undefined; + var x108: u1 = undefined; + addcarryxU64(&x107, &x108, 0x0, x85, x99); + var x109: u64 = undefined; + var x110: u1 = undefined; + addcarryxU64(&x109, &x110, x108, x87, x101); + var x111: u64 = undefined; + var x112: u1 = undefined; + addcarryxU64(&x111, &x112, x110, x89, x103); + var x113: u64 = undefined; + var x114: u1 = undefined; + addcarryxU64(&x113, &x114, x112, x91, x105); + var x115: u64 = undefined; + var x116: u64 = undefined; + mulxU64(&x115, &x116, x107, 0x4b0dff665588b13f); + var x117: u64 = undefined; + var x118: u64 = undefined; + mulxU64(&x117, &x118, x115, 0xffffffffffffffff); + var x119: u64 = undefined; + var x120: u64 = undefined; + mulxU64(&x119, &x120, x115, 0xfffffffffffffffe); + var x121: u64 = undefined; + var x122: u64 = undefined; + mulxU64(&x121, &x122, x115, 0xbaaedce6af48a03b); + var x123: u64 = undefined; + var x124: u64 = undefined; + mulxU64(&x123, &x124, x115, 0xbfd25e8cd0364141); + var x125: u64 = undefined; + var x126: u1 = undefined; + addcarryxU64(&x125, &x126, 0x0, x124, x121); + var x127: u64 = undefined; + var x128: u1 = undefined; + addcarryxU64(&x127, &x128, x126, x122, x119); + var x129: u64 = undefined; + var x130: u1 = undefined; + addcarryxU64(&x129, &x130, x128, x120, x117); + var x131: u64 = undefined; + var x132: u1 = undefined; + addcarryxU64(&x131, &x132, 0x0, x107, x123); + var x133: u64 = undefined; + var x134: u1 = undefined; + addcarryxU64(&x133, &x134, x132, x109, x125); + var x135: u64 = undefined; + var x136: u1 = undefined; + addcarryxU64(&x135, &x136, x134, x111, x127); + var x137: u64 = undefined; + var x138: u1 = undefined; + addcarryxU64(&x137, &x138, x136, x113, x129); + var x139: u64 = undefined; + var x140: u1 = undefined; + addcarryxU64(&x139, &x140, x138, ((@as(u64, x114) + @as(u64, x92)) + (@as(u64, x106) + x94)), (@as(u64, x130) + x118)); + var x141: u64 = undefined; + var x142: u64 = undefined; + mulxU64(&x141, &x142, x3, 0x9d671cd581c69bc5); + var x143: u64 = undefined; + var x144: u64 = undefined; + mulxU64(&x143, &x144, x3, 0xe697f5e45bcd07c6); + var x145: u64 = undefined; + var x146: u64 = undefined; + mulxU64(&x145, &x146, x3, 0x741496c20e7cf878); + var x147: u64 = undefined; + var x148: u64 = undefined; + mulxU64(&x147, &x148, x3, 0x896cf21467d7d140); + var x149: u64 = undefined; + var x150: u1 = undefined; + addcarryxU64(&x149, &x150, 0x0, x148, x145); + var x151: u64 = undefined; + var x152: u1 = undefined; + addcarryxU64(&x151, &x152, x150, x146, x143); + var x153: u64 = undefined; + var x154: u1 = undefined; + addcarryxU64(&x153, &x154, x152, x144, x141); + var x155: u64 = undefined; + var x156: u1 = undefined; + addcarryxU64(&x155, &x156, 0x0, x133, x147); + var x157: u64 = undefined; + var x158: u1 = undefined; + addcarryxU64(&x157, &x158, x156, x135, x149); + var x159: u64 = undefined; + var x160: u1 = undefined; + addcarryxU64(&x159, &x160, x158, x137, x151); + var x161: u64 = undefined; + var x162: u1 = undefined; + addcarryxU64(&x161, &x162, x160, x139, x153); + var x163: u64 = undefined; + var x164: u64 = undefined; + mulxU64(&x163, &x164, x155, 0x4b0dff665588b13f); + var x165: u64 = undefined; + var x166: u64 = undefined; + mulxU64(&x165, &x166, x163, 0xffffffffffffffff); + var x167: u64 = undefined; + var x168: u64 = undefined; + mulxU64(&x167, &x168, x163, 0xfffffffffffffffe); + var x169: u64 = undefined; + var x170: u64 = undefined; + mulxU64(&x169, &x170, x163, 0xbaaedce6af48a03b); + var x171: u64 = undefined; + var x172: u64 = undefined; + mulxU64(&x171, &x172, x163, 0xbfd25e8cd0364141); + var x173: u64 = undefined; + var x174: u1 = undefined; + addcarryxU64(&x173, &x174, 0x0, x172, x169); + var x175: u64 = undefined; + var x176: u1 = undefined; + addcarryxU64(&x175, &x176, x174, x170, x167); + var x177: u64 = undefined; + var x178: u1 = undefined; + addcarryxU64(&x177, &x178, x176, x168, x165); + var x179: u64 = undefined; + var x180: u1 = undefined; + addcarryxU64(&x179, &x180, 0x0, x155, x171); + var x181: u64 = undefined; + var x182: u1 = undefined; + addcarryxU64(&x181, &x182, x180, x157, x173); + var x183: u64 = undefined; + var x184: u1 = undefined; + addcarryxU64(&x183, &x184, x182, x159, x175); + var x185: u64 = undefined; + var x186: u1 = undefined; + addcarryxU64(&x185, &x186, x184, x161, x177); + var x187: u64 = undefined; + var x188: u1 = undefined; + addcarryxU64(&x187, &x188, x186, ((@as(u64, x162) + @as(u64, x140)) + (@as(u64, x154) + x142)), (@as(u64, x178) + x166)); + var x189: u64 = undefined; + var x190: u1 = undefined; + subborrowxU64(&x189, &x190, 0x0, x181, 0xbfd25e8cd0364141); + var x191: u64 = undefined; + var x192: u1 = undefined; + subborrowxU64(&x191, &x192, x190, x183, 0xbaaedce6af48a03b); + var x193: u64 = undefined; + var x194: u1 = undefined; + subborrowxU64(&x193, &x194, x192, x185, 0xfffffffffffffffe); + var x195: u64 = undefined; + var x196: u1 = undefined; + subborrowxU64(&x195, &x196, x194, x187, 0xffffffffffffffff); + var x197: u64 = undefined; + var x198: u1 = undefined; + subborrowxU64(&x197, &x198, x196, @as(u64, x188), 0x0); + var x199: u64 = undefined; + cmovznzU64(&x199, x198, x189, x181); + var x200: u64 = undefined; + cmovznzU64(&x200, x198, x191, x183); + var x201: u64 = undefined; + cmovznzU64(&x201, x198, x193, x185); + var x202: u64 = undefined; + cmovznzU64(&x202, x198, x195, x187); + out1[0] = x199; + out1[1] = x200; + out1[2] = x201; + out1[3] = x202; +} + +/// The function nonzero outputs a single non-zero word if the input is non-zero and zero otherwise. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// out1 = 0 ↔ eval (from_montgomery arg1) mod m = 0 +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +pub fn nonzero(out1: *u64, arg1: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = ((arg1[0]) | ((arg1[1]) | ((arg1[2]) | (arg1[3])))); + out1.* = x1; +} + +/// The function selectznz is a multi-limb conditional select. +/// +/// Postconditions: +/// out1 = (if arg1 = 0 then arg2 else arg3) +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0x1] +/// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn selectznz(out1: *[4]u64, arg1: u1, arg2: [4]u64, arg3: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + cmovznzU64(&x1, arg1, (arg2[0]), (arg3[0])); + var x2: u64 = undefined; + cmovznzU64(&x2, arg1, (arg2[1]), (arg3[1])); + var x3: u64 = undefined; + cmovznzU64(&x3, arg1, (arg2[2]), (arg3[2])); + var x4: u64 = undefined; + cmovznzU64(&x4, arg1, (arg2[3]), (arg3[3])); + out1[0] = x1; + out1[1] = x2; + out1[2] = x3; + out1[3] = x4; +} + +/// The function toBytes serializes a field element NOT in the Montgomery domain to bytes in little-endian order. +/// +/// Preconditions: +/// 0 ≤ eval arg1 < m +/// Postconditions: +/// out1 = map (λ x, ⌊((eval arg1 mod m) mod 2^(8 * (x + 1))) / 2^(8 * x)⌋) [0..31] +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]] +pub fn toBytes(out1: *[32]u8, arg1: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (arg1[3]); + const x2 = (arg1[2]); + const x3 = (arg1[1]); + const x4 = (arg1[0]); + const x5 = @truncate(u8, (x4 & 0xff)); + const x6 = (x4 >> 8); + const x7 = @truncate(u8, (x6 & 0xff)); + const x8 = (x6 >> 8); + const x9 = @truncate(u8, (x8 & 0xff)); + const x10 = (x8 >> 8); + const x11 = @truncate(u8, (x10 & 0xff)); + const x12 = (x10 >> 8); + const x13 = @truncate(u8, (x12 & 0xff)); + const x14 = (x12 >> 8); + const x15 = @truncate(u8, (x14 & 0xff)); + const x16 = (x14 >> 8); + const x17 = @truncate(u8, (x16 & 0xff)); + const x18 = @truncate(u8, (x16 >> 8)); + const x19 = @truncate(u8, (x3 & 0xff)); + const x20 = (x3 >> 8); + const x21 = @truncate(u8, (x20 & 0xff)); + const x22 = (x20 >> 8); + const x23 = @truncate(u8, (x22 & 0xff)); + const x24 = (x22 >> 8); + const x25 = @truncate(u8, (x24 & 0xff)); + const x26 = (x24 >> 8); + const x27 = @truncate(u8, (x26 & 0xff)); + const x28 = (x26 >> 8); + const x29 = @truncate(u8, (x28 & 0xff)); + const x30 = (x28 >> 8); + const x31 = @truncate(u8, (x30 & 0xff)); + const x32 = @truncate(u8, (x30 >> 8)); + const x33 = @truncate(u8, (x2 & 0xff)); + const x34 = (x2 >> 8); + const x35 = @truncate(u8, (x34 & 0xff)); + const x36 = (x34 >> 8); + const x37 = @truncate(u8, (x36 & 0xff)); + const x38 = (x36 >> 8); + const x39 = @truncate(u8, (x38 & 0xff)); + const x40 = (x38 >> 8); + const x41 = @truncate(u8, (x40 & 0xff)); + const x42 = (x40 >> 8); + const x43 = @truncate(u8, (x42 & 0xff)); + const x44 = (x42 >> 8); + const x45 = @truncate(u8, (x44 & 0xff)); + const x46 = @truncate(u8, (x44 >> 8)); + const x47 = @truncate(u8, (x1 & 0xff)); + const x48 = (x1 >> 8); + const x49 = @truncate(u8, (x48 & 0xff)); + const x50 = (x48 >> 8); + const x51 = @truncate(u8, (x50 & 0xff)); + const x52 = (x50 >> 8); + const x53 = @truncate(u8, (x52 & 0xff)); + const x54 = (x52 >> 8); + const x55 = @truncate(u8, (x54 & 0xff)); + const x56 = (x54 >> 8); + const x57 = @truncate(u8, (x56 & 0xff)); + const x58 = (x56 >> 8); + const x59 = @truncate(u8, (x58 & 0xff)); + const x60 = @truncate(u8, (x58 >> 8)); + out1[0] = x5; + out1[1] = x7; + out1[2] = x9; + out1[3] = x11; + out1[4] = x13; + out1[5] = x15; + out1[6] = x17; + out1[7] = x18; + out1[8] = x19; + out1[9] = x21; + out1[10] = x23; + out1[11] = x25; + out1[12] = x27; + out1[13] = x29; + out1[14] = x31; + out1[15] = x32; + out1[16] = x33; + out1[17] = x35; + out1[18] = x37; + out1[19] = x39; + out1[20] = x41; + out1[21] = x43; + out1[22] = x45; + out1[23] = x46; + out1[24] = x47; + out1[25] = x49; + out1[26] = x51; + out1[27] = x53; + out1[28] = x55; + out1[29] = x57; + out1[30] = x59; + out1[31] = x60; +} + +/// The function fromBytes deserializes a field element NOT in the Montgomery domain from bytes in little-endian order. +/// +/// Preconditions: +/// 0 ≤ bytes_eval arg1 < m +/// Postconditions: +/// eval out1 mod m = bytes_eval arg1 mod m +/// 0 ≤ eval out1 < m +/// +/// Input Bounds: +/// arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]] +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn fromBytes(out1: *[4]u64, arg1: [32]u8) void { + @setRuntimeSafety(mode == .Debug); + + const x1 = (@as(u64, (arg1[31])) << 56); + const x2 = (@as(u64, (arg1[30])) << 48); + const x3 = (@as(u64, (arg1[29])) << 40); + const x4 = (@as(u64, (arg1[28])) << 32); + const x5 = (@as(u64, (arg1[27])) << 24); + const x6 = (@as(u64, (arg1[26])) << 16); + const x7 = (@as(u64, (arg1[25])) << 8); + const x8 = (arg1[24]); + const x9 = (@as(u64, (arg1[23])) << 56); + const x10 = (@as(u64, (arg1[22])) << 48); + const x11 = (@as(u64, (arg1[21])) << 40); + const x12 = (@as(u64, (arg1[20])) << 32); + const x13 = (@as(u64, (arg1[19])) << 24); + const x14 = (@as(u64, (arg1[18])) << 16); + const x15 = (@as(u64, (arg1[17])) << 8); + const x16 = (arg1[16]); + const x17 = (@as(u64, (arg1[15])) << 56); + const x18 = (@as(u64, (arg1[14])) << 48); + const x19 = (@as(u64, (arg1[13])) << 40); + const x20 = (@as(u64, (arg1[12])) << 32); + const x21 = (@as(u64, (arg1[11])) << 24); + const x22 = (@as(u64, (arg1[10])) << 16); + const x23 = (@as(u64, (arg1[9])) << 8); + const x24 = (arg1[8]); + const x25 = (@as(u64, (arg1[7])) << 56); + const x26 = (@as(u64, (arg1[6])) << 48); + const x27 = (@as(u64, (arg1[5])) << 40); + const x28 = (@as(u64, (arg1[4])) << 32); + const x29 = (@as(u64, (arg1[3])) << 24); + const x30 = (@as(u64, (arg1[2])) << 16); + const x31 = (@as(u64, (arg1[1])) << 8); + const x32 = (arg1[0]); + const x33 = (x31 + @as(u64, x32)); + const x34 = (x30 + x33); + const x35 = (x29 + x34); + const x36 = (x28 + x35); + const x37 = (x27 + x36); + const x38 = (x26 + x37); + const x39 = (x25 + x38); + const x40 = (x23 + @as(u64, x24)); + const x41 = (x22 + x40); + const x42 = (x21 + x41); + const x43 = (x20 + x42); + const x44 = (x19 + x43); + const x45 = (x18 + x44); + const x46 = (x17 + x45); + const x47 = (x15 + @as(u64, x16)); + const x48 = (x14 + x47); + const x49 = (x13 + x48); + const x50 = (x12 + x49); + const x51 = (x11 + x50); + const x52 = (x10 + x51); + const x53 = (x9 + x52); + const x54 = (x7 + @as(u64, x8)); + const x55 = (x6 + x54); + const x56 = (x5 + x55); + const x57 = (x4 + x56); + const x58 = (x3 + x57); + const x59 = (x2 + x58); + const x60 = (x1 + x59); + out1[0] = x39; + out1[1] = x46; + out1[2] = x53; + out1[3] = x60; +} + +/// The function setOne returns the field element one in the Montgomery domain. +/// +/// Postconditions: +/// eval (from_montgomery out1) mod m = 1 mod m +/// 0 ≤ eval out1 < m +/// +pub fn setOne(out1: *MontgomeryDomainFieldElement) void { + @setRuntimeSafety(mode == .Debug); + + out1[0] = 0x402da1732fc9bebf; + out1[1] = 0x4551231950b75fc4; + out1[2] = 0x1; + out1[3] = 0x0; +} + +/// The function msat returns the saturated representation of the prime modulus. +/// +/// Postconditions: +/// twos_complement_eval out1 = m +/// 0 ≤ eval out1 < m +/// +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn msat(out1: *[5]u64) void { + @setRuntimeSafety(mode == .Debug); + + out1[0] = 0xbfd25e8cd0364141; + out1[1] = 0xbaaedce6af48a03b; + out1[2] = 0xfffffffffffffffe; + out1[3] = 0xffffffffffffffff; + out1[4] = 0x0; +} + +/// The function divstep computes a divstep. +/// +/// Preconditions: +/// 0 ≤ eval arg4 < m +/// 0 ≤ eval arg5 < m +/// Postconditions: +/// out1 = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then 1 - arg1 else 1 + arg1) +/// twos_complement_eval out2 = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then twos_complement_eval arg3 else twos_complement_eval arg2) +/// twos_complement_eval out3 = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then ⌊(twos_complement_eval arg3 - twos_complement_eval arg2) / 2⌋ else ⌊(twos_complement_eval arg3 + (twos_complement_eval arg3 mod 2) * twos_complement_eval arg2) / 2⌋) +/// eval (from_montgomery out4) mod m = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then (2 * eval (from_montgomery arg5)) mod m else (2 * eval (from_montgomery arg4)) mod m) +/// eval (from_montgomery out5) mod m = (if 0 < arg1 ∧ (twos_complement_eval arg3) is odd then (eval (from_montgomery arg4) - eval (from_montgomery arg4)) mod m else (eval (from_montgomery arg5) + (twos_complement_eval arg3 mod 2) * eval (from_montgomery arg4)) mod m) +/// 0 ≤ eval out5 < m +/// 0 ≤ eval out5 < m +/// 0 ≤ eval out2 < m +/// 0 ≤ eval out3 < m +/// +/// Input Bounds: +/// arg1: [0x0 ~> 0xffffffffffffffff] +/// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg4: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// arg5: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// Output Bounds: +/// out1: [0x0 ~> 0xffffffffffffffff] +/// out2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// out3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// out4: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +/// out5: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn divstep(out1: *u64, out2: *[5]u64, out3: *[5]u64, out4: *[4]u64, out5: *[4]u64, arg1: u64, arg2: [5]u64, arg3: [5]u64, arg4: [4]u64, arg5: [4]u64) void { + @setRuntimeSafety(mode == .Debug); + + var x1: u64 = undefined; + var x2: u1 = undefined; + addcarryxU64(&x1, &x2, 0x0, (~arg1), 0x1); + const x3 = (@truncate(u1, (x1 >> 63)) & @truncate(u1, ((arg3[0]) & 0x1))); + var x4: u64 = undefined; + var x5: u1 = undefined; + addcarryxU64(&x4, &x5, 0x0, (~arg1), 0x1); + var x6: u64 = undefined; + cmovznzU64(&x6, x3, arg1, x4); + var x7: u64 = undefined; + cmovznzU64(&x7, x3, (arg2[0]), (arg3[0])); + var x8: u64 = undefined; + cmovznzU64(&x8, x3, (arg2[1]), (arg3[1])); + var x9: u64 = undefined; + cmovznzU64(&x9, x3, (arg2[2]), (arg3[2])); + var x10: u64 = undefined; + cmovznzU64(&x10, x3, (arg2[3]), (arg3[3])); + var x11: u64 = undefined; + cmovznzU64(&x11, x3, (arg2[4]), (arg3[4])); + var x12: u64 = undefined; + var x13: u1 = undefined; + addcarryxU64(&x12, &x13, 0x0, 0x1, (~(arg2[0]))); + var x14: u64 = undefined; + var x15: u1 = undefined; + addcarryxU64(&x14, &x15, x13, 0x0, (~(arg2[1]))); + var x16: u64 = undefined; + var x17: u1 = undefined; + addcarryxU64(&x16, &x17, x15, 0x0, (~(arg2[2]))); + var x18: u64 = undefined; + var x19: u1 = undefined; + addcarryxU64(&x18, &x19, x17, 0x0, (~(arg2[3]))); + var x20: u64 = undefined; + var x21: u1 = undefined; + addcarryxU64(&x20, &x21, x19, 0x0, (~(arg2[4]))); + var x22: u64 = undefined; + cmovznzU64(&x22, x3, (arg3[0]), x12); + var x23: u64 = undefined; + cmovznzU64(&x23, x3, (arg3[1]), x14); + var x24: u64 = undefined; + cmovznzU64(&x24, x3, (arg3[2]), x16); + var x25: u64 = undefined; + cmovznzU64(&x25, x3, (arg3[3]), x18); + var x26: u64 = undefined; + cmovznzU64(&x26, x3, (arg3[4]), x20); + var x27: u64 = undefined; + cmovznzU64(&x27, x3, (arg4[0]), (arg5[0])); + var x28: u64 = undefined; + cmovznzU64(&x28, x3, (arg4[1]), (arg5[1])); + var x29: u64 = undefined; + cmovznzU64(&x29, x3, (arg4[2]), (arg5[2])); + var x30: u64 = undefined; + cmovznzU64(&x30, x3, (arg4[3]), (arg5[3])); + var x31: u64 = undefined; + var x32: u1 = undefined; + addcarryxU64(&x31, &x32, 0x0, x27, x27); + var x33: u64 = undefined; + var x34: u1 = undefined; + addcarryxU64(&x33, &x34, x32, x28, x28); + var x35: u64 = undefined; + var x36: u1 = undefined; + addcarryxU64(&x35, &x36, x34, x29, x29); + var x37: u64 = undefined; + var x38: u1 = undefined; + addcarryxU64(&x37, &x38, x36, x30, x30); + var x39: u64 = undefined; + var x40: u1 = undefined; + subborrowxU64(&x39, &x40, 0x0, x31, 0xbfd25e8cd0364141); + var x41: u64 = undefined; + var x42: u1 = undefined; + subborrowxU64(&x41, &x42, x40, x33, 0xbaaedce6af48a03b); + var x43: u64 = undefined; + var x44: u1 = undefined; + subborrowxU64(&x43, &x44, x42, x35, 0xfffffffffffffffe); + var x45: u64 = undefined; + var x46: u1 = undefined; + subborrowxU64(&x45, &x46, x44, x37, 0xffffffffffffffff); + var x47: u64 = undefined; + var x48: u1 = undefined; + subborrowxU64(&x47, &x48, x46, @as(u64, x38), 0x0); + const x49 = (arg4[3]); + const x50 = (arg4[2]); + const x51 = (arg4[1]); + const x52 = (arg4[0]); + var x53: u64 = undefined; + var x54: u1 = undefined; + subborrowxU64(&x53, &x54, 0x0, 0x0, x52); + var x55: u64 = undefined; + var x56: u1 = undefined; + subborrowxU64(&x55, &x56, x54, 0x0, x51); + var x57: u64 = undefined; + var x58: u1 = undefined; + subborrowxU64(&x57, &x58, x56, 0x0, x50); + var x59: u64 = undefined; + var x60: u1 = undefined; + subborrowxU64(&x59, &x60, x58, 0x0, x49); + var x61: u64 = undefined; + cmovznzU64(&x61, x60, 0x0, 0xffffffffffffffff); + var x62: u64 = undefined; + var x63: u1 = undefined; + addcarryxU64(&x62, &x63, 0x0, x53, (x61 & 0xbfd25e8cd0364141)); + var x64: u64 = undefined; + var x65: u1 = undefined; + addcarryxU64(&x64, &x65, x63, x55, (x61 & 0xbaaedce6af48a03b)); + var x66: u64 = undefined; + var x67: u1 = undefined; + addcarryxU64(&x66, &x67, x65, x57, (x61 & 0xfffffffffffffffe)); + var x68: u64 = undefined; + var x69: u1 = undefined; + addcarryxU64(&x68, &x69, x67, x59, x61); + var x70: u64 = undefined; + cmovznzU64(&x70, x3, (arg5[0]), x62); + var x71: u64 = undefined; + cmovznzU64(&x71, x3, (arg5[1]), x64); + var x72: u64 = undefined; + cmovznzU64(&x72, x3, (arg5[2]), x66); + var x73: u64 = undefined; + cmovznzU64(&x73, x3, (arg5[3]), x68); + const x74 = @truncate(u1, (x22 & 0x1)); + var x75: u64 = undefined; + cmovznzU64(&x75, x74, 0x0, x7); + var x76: u64 = undefined; + cmovznzU64(&x76, x74, 0x0, x8); + var x77: u64 = undefined; + cmovznzU64(&x77, x74, 0x0, x9); + var x78: u64 = undefined; + cmovznzU64(&x78, x74, 0x0, x10); + var x79: u64 = undefined; + cmovznzU64(&x79, x74, 0x0, x11); + var x80: u64 = undefined; + var x81: u1 = undefined; + addcarryxU64(&x80, &x81, 0x0, x22, x75); + var x82: u64 = undefined; + var x83: u1 = undefined; + addcarryxU64(&x82, &x83, x81, x23, x76); + var x84: u64 = undefined; + var x85: u1 = undefined; + addcarryxU64(&x84, &x85, x83, x24, x77); + var x86: u64 = undefined; + var x87: u1 = undefined; + addcarryxU64(&x86, &x87, x85, x25, x78); + var x88: u64 = undefined; + var x89: u1 = undefined; + addcarryxU64(&x88, &x89, x87, x26, x79); + var x90: u64 = undefined; + cmovznzU64(&x90, x74, 0x0, x27); + var x91: u64 = undefined; + cmovznzU64(&x91, x74, 0x0, x28); + var x92: u64 = undefined; + cmovznzU64(&x92, x74, 0x0, x29); + var x93: u64 = undefined; + cmovznzU64(&x93, x74, 0x0, x30); + var x94: u64 = undefined; + var x95: u1 = undefined; + addcarryxU64(&x94, &x95, 0x0, x70, x90); + var x96: u64 = undefined; + var x97: u1 = undefined; + addcarryxU64(&x96, &x97, x95, x71, x91); + var x98: u64 = undefined; + var x99: u1 = undefined; + addcarryxU64(&x98, &x99, x97, x72, x92); + var x100: u64 = undefined; + var x101: u1 = undefined; + addcarryxU64(&x100, &x101, x99, x73, x93); + var x102: u64 = undefined; + var x103: u1 = undefined; + subborrowxU64(&x102, &x103, 0x0, x94, 0xbfd25e8cd0364141); + var x104: u64 = undefined; + var x105: u1 = undefined; + subborrowxU64(&x104, &x105, x103, x96, 0xbaaedce6af48a03b); + var x106: u64 = undefined; + var x107: u1 = undefined; + subborrowxU64(&x106, &x107, x105, x98, 0xfffffffffffffffe); + var x108: u64 = undefined; + var x109: u1 = undefined; + subborrowxU64(&x108, &x109, x107, x100, 0xffffffffffffffff); + var x110: u64 = undefined; + var x111: u1 = undefined; + subborrowxU64(&x110, &x111, x109, @as(u64, x101), 0x0); + var x112: u64 = undefined; + var x113: u1 = undefined; + addcarryxU64(&x112, &x113, 0x0, x6, 0x1); + const x114 = ((x80 >> 1) | ((x82 << 63) & 0xffffffffffffffff)); + const x115 = ((x82 >> 1) | ((x84 << 63) & 0xffffffffffffffff)); + const x116 = ((x84 >> 1) | ((x86 << 63) & 0xffffffffffffffff)); + const x117 = ((x86 >> 1) | ((x88 << 63) & 0xffffffffffffffff)); + const x118 = ((x88 & 0x8000000000000000) | (x88 >> 1)); + var x119: u64 = undefined; + cmovznzU64(&x119, x48, x39, x31); + var x120: u64 = undefined; + cmovznzU64(&x120, x48, x41, x33); + var x121: u64 = undefined; + cmovznzU64(&x121, x48, x43, x35); + var x122: u64 = undefined; + cmovznzU64(&x122, x48, x45, x37); + var x123: u64 = undefined; + cmovznzU64(&x123, x111, x102, x94); + var x124: u64 = undefined; + cmovznzU64(&x124, x111, x104, x96); + var x125: u64 = undefined; + cmovznzU64(&x125, x111, x106, x98); + var x126: u64 = undefined; + cmovznzU64(&x126, x111, x108, x100); + out1.* = x112; + out2[0] = x7; + out2[1] = x8; + out2[2] = x9; + out2[3] = x10; + out2[4] = x11; + out3[0] = x114; + out3[1] = x115; + out3[2] = x116; + out3[3] = x117; + out3[4] = x118; + out4[0] = x119; + out4[1] = x120; + out4[2] = x121; + out4[3] = x122; + out5[0] = x123; + out5[1] = x124; + out5[2] = x125; + out5[3] = x126; +} + +/// The function divstepPrecomp returns the precomputed value for Bernstein-Yang-inversion (in montgomery form). +/// +/// Postconditions: +/// eval (from_montgomery out1) = ⌊(m - 1) / 2⌋^(if ⌊log2 m⌋ + 1 < 46 then ⌊(49 * (⌊log2 m⌋ + 1) + 80) / 17⌋ else ⌊(49 * (⌊log2 m⌋ + 1) + 57) / 17⌋) +/// 0 ≤ eval out1 < m +/// +/// Output Bounds: +/// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]] +pub fn divstepPrecomp(out1: *[4]u64) void { + @setRuntimeSafety(mode == .Debug); + + out1[0] = 0xd7431a4d2b9cb4e9; + out1[1] = 0xab67d35a32d9c503; + out1[2] = 0xadf6c7e5859ce35f; + out1[3] = 0x615441451df6c379; +} diff --git a/lib/std/crypto/pcurves/tests/secp256k1.zig b/lib/std/crypto/pcurves/tests/secp256k1.zig new file mode 100644 index 0000000000..32fca87691 --- /dev/null +++ b/lib/std/crypto/pcurves/tests/secp256k1.zig @@ -0,0 +1,137 @@ +const std = @import("std"); +const fmt = std.fmt; +const testing = std.testing; + +const Secp256k1 = @import("../secp256k1.zig").Secp256k1; + +test "secp256k1 ECDH key exchange" { + const dha = Secp256k1.scalar.random(.Little); + const dhb = Secp256k1.scalar.random(.Little); + const dhA = try Secp256k1.basePoint.mul(dha, .Little); + const dhB = try Secp256k1.basePoint.mul(dhb, .Little); + const shareda = try dhA.mul(dhb, .Little); + const sharedb = try dhB.mul(dha, .Little); + try testing.expect(shareda.equivalent(sharedb)); +} + +test "secp256k1 ECDH key exchange including public multiplication" { + const dha = Secp256k1.scalar.random(.Little); + const dhb = Secp256k1.scalar.random(.Little); + const dhA = try Secp256k1.basePoint.mul(dha, .Little); + const dhB = try Secp256k1.basePoint.mulPublic(dhb, .Little); + const shareda = try dhA.mul(dhb, .Little); + const sharedb = try dhB.mulPublic(dha, .Little); + try testing.expect(shareda.equivalent(sharedb)); +} + +test "secp256k1 point from affine coordinates" { + const xh = "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"; + const yh = "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"; + var xs: [32]u8 = undefined; + _ = try fmt.hexToBytes(&xs, xh); + var ys: [32]u8 = undefined; + _ = try fmt.hexToBytes(&ys, yh); + var p = try Secp256k1.fromSerializedAffineCoordinates(xs, ys, .Big); + try testing.expect(p.equivalent(Secp256k1.basePoint)); +} + +test "secp256k1 test vectors" { + const expected = [_][]const u8{ + "0000000000000000000000000000000000000000000000000000000000000000", + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5", + "f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9", + "e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13", + "2f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4", + "fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556", + "5cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc", + "2f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01", + "acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe", + }; + var p = Secp256k1.identityElement; + for (expected) |xh| { + const x = p.affineCoordinates().x; + p = p.add(Secp256k1.basePoint); + var xs: [32]u8 = undefined; + _ = try fmt.hexToBytes(&xs, xh); + try testing.expectEqualSlices(u8, &x.toBytes(.Big), &xs); + } +} + +test "secp256k1 test vectors - doubling" { + const expected = [_][]const u8{ + "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5", + "e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13", + "2f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01", + "e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a", + }; + var p = Secp256k1.basePoint; + for (expected) |xh| { + const x = p.affineCoordinates().x; + p = p.dbl(); + var xs: [32]u8 = undefined; + _ = try fmt.hexToBytes(&xs, xh); + try testing.expectEqualSlices(u8, &x.toBytes(.Big), &xs); + } +} + +test "secp256k1 compressed sec1 encoding/decoding" { + const p = Secp256k1.random(); + const s = p.toCompressedSec1(); + const q = try Secp256k1.fromSec1(&s); + try testing.expect(p.equivalent(q)); +} + +test "secp256k1 uncompressed sec1 encoding/decoding" { + const p = Secp256k1.random(); + const s = p.toUncompressedSec1(); + const q = try Secp256k1.fromSec1(&s); + try testing.expect(p.equivalent(q)); +} + +test "secp256k1 public key is the neutral element" { + const n = Secp256k1.scalar.Scalar.zero.toBytes(.Little); + const p = Secp256k1.random(); + try testing.expectError(error.IdentityElement, p.mul(n, .Little)); +} + +test "secp256k1 public key is the neutral element (public verification)" { + const n = Secp256k1.scalar.Scalar.zero.toBytes(.Little); + const p = Secp256k1.random(); + try testing.expectError(error.IdentityElement, p.mulPublic(n, .Little)); +} + +test "secp256k1 field element non-canonical encoding" { + const s = [_]u8{0xff} ** 32; + try testing.expectError(error.NonCanonical, Secp256k1.Fe.fromBytes(s, .Little)); +} + +test "secp256k1 neutral element decoding" { + try testing.expectError(error.InvalidEncoding, Secp256k1.fromAffineCoordinates(.{ .x = Secp256k1.Fe.zero, .y = Secp256k1.Fe.zero })); + const p = try Secp256k1.fromAffineCoordinates(.{ .x = Secp256k1.Fe.zero, .y = Secp256k1.Fe.one }); + try testing.expectError(error.IdentityElement, p.rejectIdentity()); +} + +test "secp256k1 double base multiplication" { + const p1 = Secp256k1.basePoint; + const p2 = Secp256k1.basePoint.dbl(); + const s1 = [_]u8{0x01} ** 32; + const s2 = [_]u8{0x02} ** 32; + const pr1 = try Secp256k1.mulDoubleBasePublic(p1, s1, p2, s2, .Little); + const pr2 = (try p1.mul(s1, .Little)).add(try p2.mul(s2, .Little)); + try testing.expect(pr1.equivalent(pr2)); +} + +test "secp256k1 scalar inverse" { + const expected = "08d0684a0fe8ea978b68a29e4b4ffdbd19eeb59db25301cf23ecbe568e1f9822"; + var out: [32]u8 = undefined; + _ = try std.fmt.hexToBytes(&out, expected); + + const scalar = try Secp256k1.scalar.Scalar.fromBytes(.{ + 0x94, 0xa1, 0xbb, 0xb1, 0x4b, 0x90, 0x6a, 0x61, 0xa2, 0x80, 0xf2, 0x45, 0xf9, 0xe9, 0x3c, 0x7f, + 0x3b, 0x4a, 0x62, 0x47, 0x82, 0x4f, 0x5d, 0x33, 0xb9, 0x67, 0x07, 0x87, 0x64, 0x2a, 0x68, 0xde, + }, .Big); + const inverse = scalar.invert(); + try std.testing.expectEqualSlices(u8, &out, &inverse.toBytes(.Big)); +} From 7b32062775cca303d8a2fc72b17545b0246f9f9a Mon Sep 17 00:00:00 2001 From: LordMZTE Date: Wed, 29 Jun 2022 16:03:29 +0200 Subject: [PATCH 1997/2031] translate-c: fix cast or call macro with parenthesis --- src/translate_c.zig | 2 ++ test/behavior/translate_c_macros.h | 1 + test/behavior/translate_c_macros.zig | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/src/translate_c.zig b/src/translate_c.zig index 3bbe1e6f46..84fb4ba99e 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -5110,6 +5110,7 @@ const PatternList = struct { [2][]const u8{ "ULL_SUFFIX(X) (X ## ULL)", "ULL_SUFFIX" }, [2][]const u8{ "CAST_OR_CALL(X, Y) (X)(Y)", "CAST_OR_CALL" }, + [2][]const u8{ "CAST_OR_CALL(X, Y) ((X)(Y))", "CAST_OR_CALL" }, [2][]const u8{ \\wl_container_of(ptr, sample, member) \ @@ -5303,6 +5304,7 @@ test "Macro matching" { try helper.checkMacro(allocator, pattern_list, "NO_MATCH(X, Y) (X + Y)", null); try helper.checkMacro(allocator, pattern_list, "CAST_OR_CALL(X, Y) (X)(Y)", "CAST_OR_CALL"); + try helper.checkMacro(allocator, pattern_list, "CAST_OR_CALL(X, Y) ((X)(Y))", "CAST_OR_CALL"); try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (void)(X)", "DISCARD"); try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((void)(X))", "DISCARD"); try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (const void)(X)", "DISCARD"); diff --git a/test/behavior/translate_c_macros.h b/test/behavior/translate_c_macros.h index 490ca35726..547194d35a 100644 --- a/test/behavior/translate_c_macros.h +++ b/test/behavior/translate_c_macros.h @@ -37,5 +37,6 @@ union U { #define IGNORE_ME_10(x) (volatile const void)(x) #define UNION_CAST(X) (union U)(X) +#define CAST_OR_CALL_WITH_PARENS(type_or_fn, val) ((type_or_fn)(val)) #define NESTED_COMMA_OPERATOR (1, (2, 3)) diff --git a/test/behavior/translate_c_macros.zig b/test/behavior/translate_c_macros.zig index e44996b990..96676b5b78 100644 --- a/test/behavior/translate_c_macros.zig +++ b/test/behavior/translate_c_macros.zig @@ -70,6 +70,26 @@ test "casting to union with a macro" { try expect(d == casted.d); } +test "casting or calling a value with a paren-surrounded macro" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const l: c_long = 42; + const casted = h.CAST_OR_CALL_WITH_PARENS(c_int, l); + try expect(casted == @intCast(c_int, l)); + + const Helper = struct { + fn foo(n: c_int) !void { + try expect(n == 42); + } + }; + + try h.CAST_OR_CALL_WITH_PARENS(Helper.foo, 42); +} + test "nested comma operator" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO From b4ecc02471299a75af8a74ccac82a50d66be0425 Mon Sep 17 00:00:00 2001 From: LordMZTE Date: Wed, 29 Jun 2022 16:59:18 +0200 Subject: [PATCH 1998/2031] translate-c: fix token pasting operator without parens --- src/translate_c.zig | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/translate_c.zig b/src/translate_c.zig index 84fb4ba99e..306f5d3ea1 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -5109,6 +5109,28 @@ const PatternList = struct { [2][]const u8{ "Ull_SUFFIX(X) (X ## Ull)", "ULL_SUFFIX" }, [2][]const u8{ "ULL_SUFFIX(X) (X ## ULL)", "ULL_SUFFIX" }, + [2][]const u8{ "f_SUFFIX(X) X ## f", "F_SUFFIX" }, + [2][]const u8{ "F_SUFFIX(X) X ## F", "F_SUFFIX" }, + + [2][]const u8{ "u_SUFFIX(X) X ## u", "U_SUFFIX" }, + [2][]const u8{ "U_SUFFIX(X) X ## U", "U_SUFFIX" }, + + [2][]const u8{ "l_SUFFIX(X) X ## l", "L_SUFFIX" }, + [2][]const u8{ "L_SUFFIX(X) X ## L", "L_SUFFIX" }, + + [2][]const u8{ "ul_SUFFIX(X) X ## ul", "UL_SUFFIX" }, + [2][]const u8{ "uL_SUFFIX(X) X ## uL", "UL_SUFFIX" }, + [2][]const u8{ "Ul_SUFFIX(X) X ## Ul", "UL_SUFFIX" }, + [2][]const u8{ "UL_SUFFIX(X) X ## UL", "UL_SUFFIX" }, + + [2][]const u8{ "ll_SUFFIX(X) X ## ll", "LL_SUFFIX" }, + [2][]const u8{ "LL_SUFFIX(X) X ## LL", "LL_SUFFIX" }, + + [2][]const u8{ "ull_SUFFIX(X) X ## ull", "ULL_SUFFIX" }, + [2][]const u8{ "uLL_SUFFIX(X) X ## uLL", "ULL_SUFFIX" }, + [2][]const u8{ "Ull_SUFFIX(X) X ## Ull", "ULL_SUFFIX" }, + [2][]const u8{ "ULL_SUFFIX(X) X ## ULL", "ULL_SUFFIX" }, + [2][]const u8{ "CAST_OR_CALL(X, Y) (X)(Y)", "CAST_OR_CALL" }, [2][]const u8{ "CAST_OR_CALL(X, Y) ((X)(Y))", "CAST_OR_CALL" }, From 59359b25474724018c14b8cb685837f4480c0de5 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 29 Jun 2022 09:05:34 +0200 Subject: [PATCH 1999/2031] aarch64: add airRetLoad for register mcv --- src/arch/aarch64/CodeGen.zig | 82 ++++++++++++------- test/behavior/incomplete_struct_param_tld.zig | 1 - 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 4cf428bd9a..47d7d1282f 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -362,6 +362,13 @@ fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { return result_index; } +fn addNop(self: *Self) error{OutOfMemory}!Mir.Inst.Index { + return try self.addInst(.{ + .tag = .nop, + .data = .{ .nop = {} }, + }); +} + pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 { const fields = std.meta.fields(@TypeOf(extra)); try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len); @@ -396,10 +403,7 @@ fn gen(self: *Self) !void { }); // - const backpatch_save_registers = try self.addInst(.{ - .tag = .nop, - .data = .{ .nop = {} }, - }); + const backpatch_save_registers = try self.addNop(); // mov fp, sp _ = try self.addInst(.{ @@ -408,10 +412,7 @@ fn gen(self: *Self) !void { }); // sub sp, sp, #reloc - const backpatch_reloc = try self.addInst(.{ - .tag = .nop, - .data = .{ .nop = {} }, - }); + const backpatch_reloc = try self.addNop(); _ = try self.addInst(.{ .tag = .dbg_prologue_end, @@ -3261,37 +3262,58 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. return bt.finishAir(result); } -fn ret(self: *Self, mcv: MCValue) !void { - const ret_ty = self.fn_type.fnReturnType(); - switch (self.ret_mcv) { - .immediate => { - assert(ret_ty.isError()); - }, - else => { - try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); - }, - } - // Just add space for an instruction, patch this later - const index = try self.addInst(.{ - .tag = .nop, - .data = .{ .nop = {} }, - }); - try self.exitlude_jump_relocs.append(self.gpa, index); -} - fn airRet(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); - try self.ret(operand); + const ret_ty = self.fn_type.fnReturnType(); + + switch (self.ret_mcv) { + .none => {}, + .immediate => { + assert(ret_ty.isError()); + }, + .register => |reg| { + // Return result by value + try self.genSetReg(ret_ty, reg, operand); + }, + .stack_offset => { + // Return result by reference + // TODO + return self.fail("TODO implement airRet for {}", .{self.ret_mcv}); + }, + else => unreachable, + } + + // Just add space for an instruction, patch this later + try self.exitlude_jump_relocs.append(self.gpa, try self.addNop()); + 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 }); + const ptr_ty = self.air.typeOf(un_op); + const ret_ty = self.fn_type.fnReturnType(); + _ = ret_ty; + + switch (self.ret_mcv) { + .none => {}, + .register => { + // Return result by value + try self.load(self.ret_mcv, ptr, ptr_ty); + }, + .stack_offset => { + // Return result by reference + // TODO + return self.fail("TODO implement airRetLoad for {}", .{self.ret_mcv}); + }, + else => unreachable, + } + + try self.exitlude_jump_relocs.append(self.gpa, try self.addNop()); + + return self.finishAir(inst, .dead, .{ un_op, .none, .none }); } fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { diff --git a/test/behavior/incomplete_struct_param_tld.zig b/test/behavior/incomplete_struct_param_tld.zig index 385c64dbdb..29f57ed971 100644 --- a/test/behavior/incomplete_struct_param_tld.zig +++ b/test/behavior/incomplete_struct_param_tld.zig @@ -22,7 +22,6 @@ fn foo(a: A) i32 { } test "incomplete struct param top level declaration" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const a = A{ .b = B{ .c = C{ .x = 13 }, From 7cc417644862a9b9523545f4455da7722afc8209 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 29 Jun 2022 08:17:49 +0200 Subject: [PATCH 2000/2031] clang: add Zig equivalent for -headerpad_max_install_names cli flag --- src/clang_options_data.zig | 9 ++++++++- src/main.zig | 2 ++ tools/update_clang_options.zig | 4 ++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/clang_options_data.zig b/src/clang_options_data.zig index b4bef06c9e..fa6e8abb41 100644 --- a/src/clang_options_data.zig +++ b/src/clang_options_data.zig @@ -4978,7 +4978,14 @@ joinpd1("fdiagnostics-show-category="), joinpd1("fdiagnostics-show-location="), joinpd1("fopenmp-cuda-blocks-per-sm="), jspd1("fxray-instruction-threshold"), -joinpd1("headerpad_max_install_names"), +.{ + .name = "headerpad_max_install_names", + .syntax = .joined, + .zig_equivalent = .headerpad_max_install_names, + .pd1 = true, + .pd2 = false, + .psl = false, +}, .{ .name = "libomptarget-nvptx-bc-path=", .syntax = .joined, diff --git a/src/main.zig b/src/main.zig index f13fe342ca..241a495a2e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1627,6 +1627,7 @@ fn buildOutputType( }, .weak_library => try system_libs.put(it.only_arg, .{ .weak = true }), .weak_framework => try frameworks.put(gpa, it.only_arg, .{ .weak = true }), + .headerpad_max_install_names => headerpad_max_install_names = true, } } // Parse linker args. @@ -4581,6 +4582,7 @@ pub const ClangArgIterator = struct { entry, weak_library, weak_framework, + headerpad_max_install_names, }; const Args = struct { diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig index b003a1bbb9..bde5c3d665 100644 --- a/tools/update_clang_options.zig +++ b/tools/update_clang_options.zig @@ -444,6 +444,10 @@ const known_options = [_]KnownOpt{ .name = "weak_framework", .ident = "weak_framework", }, + .{ + .name = "headerpad_max_install_names", + .ident = "headerpad_max_install_names", + }, }; const blacklisted_options = [_][]const u8{}; From 54454fd0102af8b25dbc85751d37fd265380d920 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 29 Jun 2022 15:34:36 -0700 Subject: [PATCH 2001/2031] std.math.big.int: breaking API changes to prevent UAF Many of the Managed methods accepted by-val parameters which could reference Limb slices that became invalid memory after any ensureCapacity calls. Now, Managed methods accept `*const Managed` parameters so that if the function allows aliasing and the ensure-capacity call resizes the Limb slice, it also affects the aliased parameters, avoiding use-after-free bugs. This is a breaking change that reduces the requirement for callsites to manually make the ensure-capacity changes prior to calling many of the Managed methods. Closes #11897 --- lib/std/math/big/int.zig | 171 +++++++++-------- lib/std/math/big/int_test.zig | 346 +++++++++++++++++----------------- lib/std/math/big/rational.zig | 55 +++--- src/RangeSet.zig | 2 +- src/Sema.zig | 8 +- 5 files changed, 299 insertions(+), 283 deletions(-) diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 661d370419..9f22de9aa3 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -1391,28 +1391,28 @@ pub const Mutable = struct { if (B == 0) { // t_big = x % y, r is unused - try r.divTrunc(&t_big, x.toConst(), y.toConst()); + try r.divTrunc(&t_big, &x, &y); assert(t_big.isPositive()); x.swap(&y); y.swap(&t_big); } else { var storage: [8]Limb = undefined; - const Ap = fixedIntFromSignedDoubleLimb(A, storage[0..2]).toConst(); - const Bp = fixedIntFromSignedDoubleLimb(B, storage[2..4]).toConst(); - const Cp = fixedIntFromSignedDoubleLimb(C, storage[4..6]).toConst(); - const Dp = fixedIntFromSignedDoubleLimb(D, storage[6..8]).toConst(); + const Ap = fixedIntFromSignedDoubleLimb(A, storage[0..2]).toManaged(limbs_buffer.allocator); + const Bp = fixedIntFromSignedDoubleLimb(B, storage[2..4]).toManaged(limbs_buffer.allocator); + const Cp = fixedIntFromSignedDoubleLimb(C, storage[4..6]).toManaged(limbs_buffer.allocator); + const Dp = fixedIntFromSignedDoubleLimb(D, storage[6..8]).toManaged(limbs_buffer.allocator); // t_big = Ax + By - try r.mul(x.toConst(), Ap); - try t_big.mul(y.toConst(), Bp); - try t_big.add(r.toConst(), t_big.toConst()); + try r.mul(&x, &Ap); + try t_big.mul(&y, &Bp); + try t_big.add(&r, &t_big); // u = Cx + Dy, r as u try tmp_x.copy(x.toConst()); - try x.mul(tmp_x.toConst(), Cp); - try r.mul(y.toConst(), Dp); - try r.add(x.toConst(), r.toConst()); + try x.mul(&tmp_x, &Cp); + try r.mul(&y, &Dp); + try r.add(&x, &r); x.swap(&t_big); y.swap(&r); @@ -1423,7 +1423,7 @@ pub const Mutable = struct { assert(x.toConst().order(y.toConst()) != .lt); while (!y.toConst().eqZero()) { - try t_big.divTrunc(&r, x.toConst(), y.toConst()); + try t_big.divTrunc(&r, &x, &y); x.swap(&y); y.swap(&r); } @@ -2660,57 +2660,56 @@ pub const Managed = struct { /// r = a + scalar /// - /// r and a may be aliases. If r aliases a, then caller must call - /// `r.ensureAddScalarCapacity` prior to calling `add`. - /// scalar is a primitive integer type. + /// r and a may be aliases. /// /// Returns an error if memory could not be allocated. - pub fn addScalar(r: *Managed, a: Const, scalar: anytype) Allocator.Error!void { - assert((r.limbs.ptr != a.limbs.ptr) or r.limbs.len >= math.max(a.limbs.len, calcLimbLen(scalar)) + 1); - try r.ensureAddScalarCapacity(a, scalar); + pub fn addScalar(r: *Managed, a: *const Managed, scalar: anytype) Allocator.Error!void { + try r.ensureAddScalarCapacity(a.toConst(), scalar); var m = r.toMutable(); - m.addScalar(a, scalar); + m.addScalar(a.toConst(), scalar); r.setMetadata(m.positive, m.len); } /// r = a + b /// - /// r, a and b may be aliases. If r aliases a or b, then caller must call - /// `r.ensureAddCapacity` prior to calling `add`. + /// r, a and b may be aliases. /// /// Returns an error if memory could not be allocated. - pub fn add(r: *Managed, a: Const, b: Const) Allocator.Error!void { - assert((r.limbs.ptr != a.limbs.ptr and r.limbs.ptr != b.limbs.ptr) or r.limbs.len >= math.max(a.limbs.len, b.limbs.len) + 1); - try r.ensureAddCapacity(a, b); + pub fn add(r: *Managed, a: *const Managed, b: *const Managed) Allocator.Error!void { + try r.ensureAddCapacity(a.toConst(), b.toConst()); var m = r.toMutable(); - m.add(a, b); + m.add(a.toConst(), b.toConst()); r.setMetadata(m.positive, m.len); } /// r = a + b with 2s-complement wrapping semantics. Returns whether any overflow occured. /// - /// r, a and b may be aliases. If r aliases a or b, then caller must call - /// `r.ensureTwosCompCapacity` prior to calling `add`. + /// r, a and b may be aliases. /// /// Returns an error if memory could not be allocated. - pub fn addWrap(r: *Managed, a: Const, b: Const, signedness: Signedness, bit_count: usize) Allocator.Error!bool { + pub fn addWrap( + r: *Managed, + a: *const Managed, + b: *const Managed, + signedness: Signedness, + bit_count: usize, + ) Allocator.Error!bool { try r.ensureTwosCompCapacity(bit_count); var m = r.toMutable(); - const wrapped = m.addWrap(a, b, signedness, bit_count); + const wrapped = m.addWrap(a.toConst(), b.toConst(), signedness, bit_count); r.setMetadata(m.positive, m.len); return wrapped; } /// r = a + b with 2s-complement saturating semantics. /// - /// r, a and b may be aliases. If r aliases a or b, then caller must call - /// `r.ensureTwosCompCapacity` prior to calling `add`. + /// r, a and b may be aliases. /// /// Returns an error if memory could not be allocated. - pub fn addSat(r: *Managed, a: Const, b: Const, signedness: Signedness, bit_count: usize) Allocator.Error!void { + pub fn addSat(r: *Managed, a: *const Managed, b: *const Managed, signedness: Signedness, bit_count: usize) Allocator.Error!void { try r.ensureTwosCompCapacity(bit_count); var m = r.toMutable(); - m.addSat(a, b, signedness, bit_count); + m.addSat(a.toConst(), b.toConst(), signedness, bit_count); r.setMetadata(m.positive, m.len); } @@ -2719,64 +2718,72 @@ pub const Managed = struct { /// r, a and b may be aliases. /// /// Returns an error if memory could not be allocated. - pub fn sub(r: *Managed, a: Const, b: Const) !void { + pub fn sub(r: *Managed, a: *const Managed, b: *const Managed) !void { try r.ensureCapacity(math.max(a.limbs.len, b.limbs.len) + 1); var m = r.toMutable(); - m.sub(a, b); + m.sub(a.toConst(), b.toConst()); r.setMetadata(m.positive, m.len); } /// r = a - b with 2s-complement wrapping semantics. Returns whether any overflow occured. /// - /// r, a and b may be aliases. If r aliases a or b, then caller must call - /// `r.ensureTwosCompCapacity` prior to calling `add`. + /// r, a and b may be aliases. /// /// Returns an error if memory could not be allocated. - pub fn subWrap(r: *Managed, a: Const, b: Const, signedness: Signedness, bit_count: usize) Allocator.Error!bool { + pub fn subWrap( + r: *Managed, + a: *const Managed, + b: *const Managed, + signedness: Signedness, + bit_count: usize, + ) Allocator.Error!bool { try r.ensureTwosCompCapacity(bit_count); var m = r.toMutable(); - const wrapped = m.subWrap(a, b, signedness, bit_count); + const wrapped = m.subWrap(a.toConst(), b.toConst(), signedness, bit_count); r.setMetadata(m.positive, m.len); return wrapped; } /// r = a - b with 2s-complement saturating semantics. /// - /// r, a and b may be aliases. If r aliases a or b, then caller must call - /// `r.ensureTwosCompCapacity` prior to calling `add`. + /// r, a and b may be aliases. /// /// Returns an error if memory could not be allocated. - pub fn subSat(r: *Managed, a: Const, b: Const, signedness: Signedness, bit_count: usize) Allocator.Error!void { + pub fn subSat( + r: *Managed, + a: *const Managed, + b: *const Managed, + signedness: Signedness, + bit_count: usize, + ) Allocator.Error!void { try r.ensureTwosCompCapacity(bit_count); var m = r.toMutable(); - m.subSat(a, b, signedness, bit_count); + m.subSat(a.toConst(), b.toConst(), signedness, bit_count); r.setMetadata(m.positive, m.len); } /// rma = a * b /// /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b. - /// If rma aliases a or b, then caller must call `rma.ensureMulCapacity` prior to calling `mul`. /// /// Returns an error if memory could not be allocated. /// /// rma's allocator is used for temporary storage to speed up the multiplication. - pub fn mul(rma: *Managed, a: Const, b: Const) !void { + pub fn mul(rma: *Managed, a: *const Managed, b: *const Managed) !void { var alias_count: usize = 0; if (rma.limbs.ptr == a.limbs.ptr) alias_count += 1; if (rma.limbs.ptr == b.limbs.ptr) alias_count += 1; - assert(alias_count == 0 or rma.limbs.len >= a.limbs.len + b.limbs.len + 1); - try rma.ensureMulCapacity(a, b); + try rma.ensureMulCapacity(a.toConst(), b.toConst()); var m = rma.toMutable(); if (alias_count == 0) { - m.mulNoAlias(a, b, rma.allocator); + m.mulNoAlias(a.toConst(), b.toConst(), rma.allocator); } else { const limb_count = calcMulLimbsBufferLen(a.limbs.len, b.limbs.len, alias_count); const limbs_buffer = try rma.allocator.alloc(Limb, limb_count); defer rma.allocator.free(limbs_buffer); - m.mul(a, b, limbs_buffer, rma.allocator); + m.mul(a.toConst(), b.toConst(), limbs_buffer, rma.allocator); } rma.setMetadata(m.positive, m.len); } @@ -2784,13 +2791,17 @@ pub const Managed = struct { /// rma = a * b with 2s-complement wrapping semantics. /// /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b. - /// If rma aliases a or b, then caller must call `ensureTwosCompCapacity` - /// prior to calling `mul`. /// /// Returns an error if memory could not be allocated. /// /// rma's allocator is used for temporary storage to speed up the multiplication. - pub fn mulWrap(rma: *Managed, a: Const, b: Const, signedness: Signedness, bit_count: usize) !void { + pub fn mulWrap( + rma: *Managed, + a: *const Managed, + b: *const Managed, + signedness: Signedness, + bit_count: usize, + ) !void { var alias_count: usize = 0; if (rma.limbs.ptr == a.limbs.ptr) alias_count += 1; @@ -2800,12 +2811,12 @@ pub const Managed = struct { try rma.ensureTwosCompCapacity(bit_count); var m = rma.toMutable(); if (alias_count == 0) { - m.mulWrapNoAlias(a, b, signedness, bit_count, rma.allocator); + m.mulWrapNoAlias(a.toConst(), b.toConst(), signedness, bit_count, rma.allocator); } else { const limb_count = calcMulWrapLimbsBufferLen(bit_count, a.limbs.len, b.limbs.len, alias_count); const limbs_buffer = try rma.allocator.alloc(Limb, limb_count); defer rma.allocator.free(limbs_buffer); - m.mulWrap(a, b, signedness, bit_count, limbs_buffer, rma.allocator); + m.mulWrap(a.toConst(), b.toConst(), signedness, bit_count, limbs_buffer, rma.allocator); } rma.setMetadata(m.positive, m.len); } @@ -2831,14 +2842,14 @@ pub const Managed = struct { /// a / b are floored (rounded towards 0). /// /// Returns an error if memory could not be allocated. - pub fn divFloor(q: *Managed, r: *Managed, a: Const, b: Const) !void { + pub fn divFloor(q: *Managed, r: *Managed, a: *const Managed, b: *const Managed) !void { try q.ensureCapacity(a.limbs.len); try r.ensureCapacity(b.limbs.len); var mq = q.toMutable(); var mr = r.toMutable(); const limbs_buffer = try q.allocator.alloc(Limb, calcDivLimbsBufferLen(a.limbs.len, b.limbs.len)); defer q.allocator.free(limbs_buffer); - mq.divFloor(&mr, a, b, limbs_buffer); + mq.divFloor(&mr, a.toConst(), b.toConst(), limbs_buffer); q.setMetadata(mq.positive, mq.len); r.setMetadata(mr.positive, mr.len); } @@ -2848,20 +2859,21 @@ pub const Managed = struct { /// a / b are truncated (rounded towards -inf). /// /// Returns an error if memory could not be allocated. - pub fn divTrunc(q: *Managed, r: *Managed, a: Const, b: Const) !void { + pub fn divTrunc(q: *Managed, r: *Managed, a: *const Managed, b: *const Managed) !void { try q.ensureCapacity(a.limbs.len); try r.ensureCapacity(b.limbs.len); var mq = q.toMutable(); var mr = r.toMutable(); const limbs_buffer = try q.allocator.alloc(Limb, calcDivLimbsBufferLen(a.limbs.len, b.limbs.len)); defer q.allocator.free(limbs_buffer); - mq.divTrunc(&mr, a, b, limbs_buffer); + mq.divTrunc(&mr, a.toConst(), b.toConst(), limbs_buffer); q.setMetadata(mq.positive, mq.len); r.setMetadata(mr.positive, mr.len); } /// r = a << shift, in other words, r = a * 2^shift - pub fn shiftLeft(r: *Managed, a: Managed, shift: usize) !void { + /// r and a may alias. + pub fn shiftLeft(r: *Managed, a: *const Managed, shift: usize) !void { try r.ensureCapacity(a.len() + (shift / limb_bits) + 1); var m = r.toMutable(); m.shiftLeft(a.toConst(), shift); @@ -2869,7 +2881,8 @@ pub const Managed = struct { } /// r = a <<| shift with 2s-complement saturating semantics. - pub fn shiftLeftSat(r: *Managed, a: Managed, shift: usize, signedness: Signedness, bit_count: usize) !void { + /// r and a may alias. + pub fn shiftLeftSat(r: *Managed, a: *const Managed, shift: usize, signedness: Signedness, bit_count: usize) !void { try r.ensureTwosCompCapacity(bit_count); var m = r.toMutable(); m.shiftLeftSat(a.toConst(), shift, signedness, bit_count); @@ -2877,7 +2890,8 @@ pub const Managed = struct { } /// r = a >> shift - pub fn shiftRight(r: *Managed, a: Managed, shift: usize) !void { + /// r and a may alias. + pub fn shiftRight(r: *Managed, a: *const Managed, shift: usize) !void { if (a.len() <= shift / limb_bits) { r.metadata = 1; r.limbs[0] = 0; @@ -2891,7 +2905,8 @@ pub const Managed = struct { } /// r = ~a under 2s-complement wrapping semantics. - pub fn bitNotWrap(r: *Managed, a: Managed, signedness: Signedness, bit_count: usize) !void { + /// r and a may alias. + pub fn bitNotWrap(r: *Managed, a: *const Managed, signedness: Signedness, bit_count: usize) !void { try r.ensureTwosCompCapacity(bit_count); var m = r.toMutable(); m.bitNotWrap(a.toConst(), signedness, bit_count); @@ -2901,7 +2916,7 @@ pub const Managed = struct { /// r = a | b /// /// a and b are zero-extended to the longer of a or b. - pub fn bitOr(r: *Managed, a: Managed, b: Managed) !void { + pub fn bitOr(r: *Managed, a: *const Managed, b: *const Managed) !void { try r.ensureCapacity(math.max(a.len(), b.len())); var m = r.toMutable(); m.bitOr(a.toConst(), b.toConst()); @@ -2909,7 +2924,7 @@ pub const Managed = struct { } /// r = a & b - pub fn bitAnd(r: *Managed, a: Managed, b: Managed) !void { + pub fn bitAnd(r: *Managed, a: *const Managed, b: *const Managed) !void { const cap = if (a.isPositive() or b.isPositive()) math.min(a.len(), b.len()) else @@ -2921,7 +2936,7 @@ pub const Managed = struct { } /// r = a ^ b - pub fn bitXor(r: *Managed, a: Managed, b: Managed) !void { + pub fn bitXor(r: *Managed, a: *const Managed, b: *const Managed) !void { var cap = math.max(a.len(), b.len()) + @boolToInt(a.isPositive() != b.isPositive()); try r.ensureCapacity(cap); @@ -2934,7 +2949,7 @@ pub const Managed = struct { /// x and y may alias each other. /// /// rma's allocator is used for temporary storage to boost multiplication performance. - pub fn gcd(rma: *Managed, x: Managed, y: Managed) !void { + pub fn gcd(rma: *Managed, x: *const Managed, y: *const Managed) !void { try rma.ensureCapacity(math.min(x.len(), y.len())); var m = rma.toMutable(); var limbs_buffer = std.ArrayList(Limb).init(rma.allocator); @@ -2944,14 +2959,14 @@ pub const Managed = struct { } /// r = a * a - pub fn sqr(rma: *Managed, a: Const) !void { + pub fn sqr(rma: *Managed, a: *const Managed) !void { const needed_limbs = 2 * a.limbs.len + 1; if (rma.limbs.ptr == a.limbs.ptr) { var m = try Managed.initCapacity(rma.allocator, needed_limbs); errdefer m.deinit(); var m_mut = m.toMutable(); - m_mut.sqrNoAlias(a, rma.allocator); + m_mut.sqrNoAlias(a.toConst(), rma.allocator); m.setMetadata(m_mut.positive, m_mut.len); rma.deinit(); @@ -2959,12 +2974,12 @@ pub const Managed = struct { } else { try rma.ensureCapacity(needed_limbs); var rma_mut = rma.toMutable(); - rma_mut.sqrNoAlias(a, rma.allocator); + rma_mut.sqrNoAlias(a.toConst(), rma.allocator); rma.setMetadata(rma_mut.positive, rma_mut.len); } } - pub fn pow(rma: *Managed, a: Const, b: u32) !void { + pub fn pow(rma: *Managed, a: *const Managed, b: u32) !void { const needed_limbs = calcPowLimbsBufferLen(a.bitCountAbs(), b); const limbs_buffer = try rma.allocator.alloc(Limb, needed_limbs); @@ -2974,7 +2989,7 @@ pub const Managed = struct { var m = try Managed.initCapacity(rma.allocator, needed_limbs); errdefer m.deinit(); var m_mut = m.toMutable(); - try m_mut.pow(a, b, limbs_buffer); + try m_mut.pow(a.toConst(), b, limbs_buffer); m.setMetadata(m_mut.positive, m_mut.len); rma.deinit(); @@ -2982,33 +2997,33 @@ pub const Managed = struct { } else { try rma.ensureCapacity(needed_limbs); var rma_mut = rma.toMutable(); - try rma_mut.pow(a, b, limbs_buffer); + try rma_mut.pow(a.toConst(), b, limbs_buffer); rma.setMetadata(rma_mut.positive, rma_mut.len); } } /// r = truncate(Int(signedness, bit_count), a) - pub fn truncate(r: *Managed, a: Const, signedness: Signedness, bit_count: usize) !void { + pub fn truncate(r: *Managed, a: *const Managed, signedness: Signedness, bit_count: usize) !void { try r.ensureCapacity(calcTwosCompLimbCount(bit_count)); var m = r.toMutable(); - m.truncate(a, signedness, bit_count); + m.truncate(a.toConst(), signedness, bit_count); r.setMetadata(m.positive, m.len); } /// r = saturate(Int(signedness, bit_count), a) - pub fn saturate(r: *Managed, a: Const, signedness: Signedness, bit_count: usize) !void { + pub fn saturate(r: *Managed, a: *const Managed, signedness: Signedness, bit_count: usize) !void { try r.ensureCapacity(calcTwosCompLimbCount(bit_count)); var m = r.toMutable(); - m.saturate(a, signedness, bit_count); + m.saturate(a.toConst(), signedness, bit_count); r.setMetadata(m.positive, m.len); } /// r = @popCount(a) with 2s-complement semantics. /// r and a may be aliases. - pub fn popCount(r: *Managed, a: Const, bit_count: usize) !void { + pub fn popCount(r: *Managed, a: *const Managed, bit_count: usize) !void { try r.ensureCapacity(calcTwosCompLimbCount(bit_count)); var m = r.toMutable(); - m.popCount(a, bit_count); + m.popCount(a.toConst(), bit_count); r.setMetadata(m.positive, m.len); } }; diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index 790cc10775..efd5c487d8 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -166,7 +166,7 @@ test "big.int bitcount + sizeInBaseUpperBound" { try testing.expect(a.sizeInBaseUpperBound(2) >= 32); try testing.expect(a.sizeInBaseUpperBound(10) >= 10); - try a.shiftLeft(a, 5000); + try a.shiftLeft(&a, 5000); try testing.expect(a.bitCountAbs() == 5032); try testing.expect(a.sizeInBaseUpperBound(2) >= 5032); a.setSign(false); @@ -486,7 +486,7 @@ test "big.int add single-single" { var c = try Managed.init(testing.allocator); defer c.deinit(); - try c.add(a.toConst(), b.toConst()); + try c.add(&a, &b); try testing.expect((try c.to(u32)) == 55); } @@ -500,10 +500,10 @@ test "big.int add multi-single" { var c = try Managed.init(testing.allocator); defer c.deinit(); - try c.add(a.toConst(), b.toConst()); + try c.add(&a, &b); try testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2); - try c.add(b.toConst(), a.toConst()); + try c.add(&b, &a); try testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2); } @@ -517,7 +517,7 @@ test "big.int add multi-multi" { var c = try Managed.init(testing.allocator); defer c.deinit(); - try c.add(a.toConst(), b.toConst()); + try c.add(&a, &b); try testing.expect((try c.to(u128)) == op1 + op2); } @@ -530,7 +530,7 @@ test "big.int add zero-zero" { var c = try Managed.init(testing.allocator); defer c.deinit(); - try c.add(a.toConst(), b.toConst()); + try c.add(&a, &b); try testing.expect((try c.to(u32)) == 0); } @@ -542,7 +542,7 @@ test "big.int add alias multi-limb nonzero-zero" { var b = try Managed.initSet(testing.allocator, 0); defer b.deinit(); - try a.add(a.toConst(), b.toConst()); + try a.add(&a, &b); try testing.expect((try a.to(u128)) == op1); } @@ -560,16 +560,16 @@ test "big.int add sign" { var neg_two = try Managed.initSet(testing.allocator, -2); defer neg_two.deinit(); - try a.add(one.toConst(), two.toConst()); + try a.add(&one, &two); try testing.expect((try a.to(i32)) == 3); - try a.add(neg_one.toConst(), two.toConst()); + try a.add(&neg_one, &two); try testing.expect((try a.to(i32)) == 1); - try a.add(one.toConst(), neg_two.toConst()); + try a.add(&one, &neg_two); try testing.expect((try a.to(i32)) == -1); - try a.add(neg_one.toConst(), neg_two.toConst()); + try a.add(&neg_one, &neg_two); try testing.expect((try a.to(i32)) == -3); } @@ -579,7 +579,7 @@ test "big.int add scalar" { var b = try Managed.init(testing.allocator); defer b.deinit(); - try b.addScalar(a.toConst(), 5); + try b.addScalar(&a, 5); try testing.expect((try b.to(u32)) == 55); } @@ -591,7 +591,7 @@ test "big.int addWrap single-single, unsigned" { var b = try Managed.initSet(testing.allocator, 10); defer b.deinit(); - const wrapped = try a.addWrap(a.toConst(), b.toConst(), .unsigned, 17); + const wrapped = try a.addWrap(&a, &b, .unsigned, 17); try testing.expect(wrapped); try testing.expect((try a.to(u17)) == 9); @@ -604,7 +604,7 @@ test "big.int subWrap single-single, unsigned" { var b = try Managed.initSet(testing.allocator, maxInt(u17)); defer b.deinit(); - const wrapped = try a.subWrap(a.toConst(), b.toConst(), .unsigned, 17); + const wrapped = try a.subWrap(&a, &b, .unsigned, 17); try testing.expect(wrapped); try testing.expect((try a.to(u17)) == 1); @@ -617,7 +617,7 @@ test "big.int addWrap multi-multi, unsigned, limb aligned" { var b = try Managed.initSet(testing.allocator, maxInt(DoubleLimb)); defer b.deinit(); - const wrapped = try a.addWrap(a.toConst(), b.toConst(), .unsigned, @bitSizeOf(DoubleLimb)); + const wrapped = try a.addWrap(&a, &b, .unsigned, @bitSizeOf(DoubleLimb)); try testing.expect(wrapped); try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb) - 1); @@ -630,7 +630,7 @@ test "big.int subWrap single-multi, unsigned, limb aligned" { var b = try Managed.initSet(testing.allocator, maxInt(DoubleLimb) + 100); defer b.deinit(); - const wrapped = try a.subWrap(a.toConst(), b.toConst(), .unsigned, @bitSizeOf(DoubleLimb)); + const wrapped = try a.subWrap(&a, &b, .unsigned, @bitSizeOf(DoubleLimb)); try testing.expect(wrapped); try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb) - 88); @@ -643,7 +643,7 @@ test "big.int addWrap single-single, signed" { var b = try Managed.initSet(testing.allocator, 1 + 1 + maxInt(u21)); defer b.deinit(); - const wrapped = try a.addWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(i21)); + const wrapped = try a.addWrap(&a, &b, .signed, @bitSizeOf(i21)); try testing.expect(wrapped); try testing.expect((try a.to(i21)) == minInt(i21)); @@ -656,7 +656,7 @@ test "big.int subWrap single-single, signed" { var b = try Managed.initSet(testing.allocator, 1); defer b.deinit(); - const wrapped = try a.subWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(i21)); + const wrapped = try a.subWrap(&a, &b, .signed, @bitSizeOf(i21)); try testing.expect(wrapped); try testing.expect((try a.to(i21)) == maxInt(i21)); @@ -669,7 +669,7 @@ test "big.int addWrap multi-multi, signed, limb aligned" { var b = try Managed.initSet(testing.allocator, maxInt(SignedDoubleLimb)); defer b.deinit(); - const wrapped = try a.addWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(SignedDoubleLimb)); + const wrapped = try a.addWrap(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb)); try testing.expect(wrapped); try testing.expect((try a.to(SignedDoubleLimb)) == -2); @@ -682,7 +682,7 @@ test "big.int subWrap single-multi, signed, limb aligned" { var b = try Managed.initSet(testing.allocator, 1); defer b.deinit(); - const wrapped = try a.subWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(SignedDoubleLimb)); + const wrapped = try a.subWrap(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb)); try testing.expect(wrapped); try testing.expect((try a.to(SignedDoubleLimb)) == maxInt(SignedDoubleLimb)); @@ -695,7 +695,7 @@ test "big.int addSat single-single, unsigned" { var b = try Managed.initSet(testing.allocator, 10); defer b.deinit(); - try a.addSat(a.toConst(), b.toConst(), .unsigned, 17); + try a.addSat(&a, &b, .unsigned, 17); try testing.expect((try a.to(u17)) == maxInt(u17)); } @@ -707,7 +707,7 @@ test "big.int subSat single-single, unsigned" { var b = try Managed.initSet(testing.allocator, 4000); defer b.deinit(); - try a.subSat(a.toConst(), b.toConst(), .unsigned, 17); + try a.subSat(&a, &b, .unsigned, 17); try testing.expect((try a.to(u17)) == 0); } @@ -719,7 +719,7 @@ test "big.int addSat multi-multi, unsigned, limb aligned" { var b = try Managed.initSet(testing.allocator, maxInt(DoubleLimb)); defer b.deinit(); - try a.addSat(a.toConst(), b.toConst(), .unsigned, @bitSizeOf(DoubleLimb)); + try a.addSat(&a, &b, .unsigned, @bitSizeOf(DoubleLimb)); try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb)); } @@ -731,7 +731,7 @@ test "big.int subSat single-multi, unsigned, limb aligned" { var b = try Managed.initSet(testing.allocator, maxInt(DoubleLimb) + 100); defer b.deinit(); - try a.subSat(a.toConst(), b.toConst(), .unsigned, @bitSizeOf(DoubleLimb)); + try a.subSat(&a, &b, .unsigned, @bitSizeOf(DoubleLimb)); try testing.expect((try a.to(DoubleLimb)) == 0); } @@ -743,7 +743,7 @@ test "big.int addSat single-single, signed" { var b = try Managed.initSet(testing.allocator, 1); defer b.deinit(); - try a.addSat(a.toConst(), b.toConst(), .signed, @bitSizeOf(i14)); + try a.addSat(&a, &b, .signed, @bitSizeOf(i14)); try testing.expect((try a.to(i14)) == maxInt(i14)); } @@ -755,7 +755,7 @@ test "big.int subSat single-single, signed" { var b = try Managed.initSet(testing.allocator, 1); defer b.deinit(); - try a.subSat(a.toConst(), b.toConst(), .signed, @bitSizeOf(i21)); + try a.subSat(&a, &b, .signed, @bitSizeOf(i21)); try testing.expect((try a.to(i21)) == minInt(i21)); } @@ -767,7 +767,7 @@ test "big.int addSat multi-multi, signed, limb aligned" { var b = try Managed.initSet(testing.allocator, maxInt(SignedDoubleLimb)); defer b.deinit(); - try a.addSat(a.toConst(), b.toConst(), .signed, @bitSizeOf(SignedDoubleLimb)); + try a.addSat(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb)); try testing.expect((try a.to(SignedDoubleLimb)) == maxInt(SignedDoubleLimb)); } @@ -779,7 +779,7 @@ test "big.int subSat single-multi, signed, limb aligned" { var b = try Managed.initSet(testing.allocator, 1); defer b.deinit(); - try a.subSat(a.toConst(), b.toConst(), .signed, @bitSizeOf(SignedDoubleLimb)); + try a.subSat(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb)); try testing.expect((try a.to(SignedDoubleLimb)) == minInt(SignedDoubleLimb)); } @@ -792,7 +792,7 @@ test "big.int sub single-single" { var c = try Managed.init(testing.allocator); defer c.deinit(); - try c.sub(a.toConst(), b.toConst()); + try c.sub(&a, &b); try testing.expect((try c.to(u32)) == 45); } @@ -805,7 +805,7 @@ test "big.int sub multi-single" { var c = try Managed.init(testing.allocator); defer c.deinit(); - try c.sub(a.toConst(), b.toConst()); + try c.sub(&a, &b); try testing.expect((try c.to(Limb)) == maxInt(Limb)); } @@ -821,7 +821,7 @@ test "big.int sub multi-multi" { var c = try Managed.init(testing.allocator); defer c.deinit(); - try c.sub(a.toConst(), b.toConst()); + try c.sub(&a, &b); try testing.expect((try c.to(u128)) == op1 - op2); } @@ -834,7 +834,7 @@ test "big.int sub equal" { var c = try Managed.init(testing.allocator); defer c.deinit(); - try c.sub(a.toConst(), b.toConst()); + try c.sub(&a, &b); try testing.expect((try c.to(u32)) == 0); } @@ -852,19 +852,19 @@ test "big.int sub sign" { var neg_two = try Managed.initSet(testing.allocator, -2); defer neg_two.deinit(); - try a.sub(one.toConst(), two.toConst()); + try a.sub(&one, &two); try testing.expect((try a.to(i32)) == -1); - try a.sub(neg_one.toConst(), two.toConst()); + try a.sub(&neg_one, &two); try testing.expect((try a.to(i32)) == -3); - try a.sub(one.toConst(), neg_two.toConst()); + try a.sub(&one, &neg_two); try testing.expect((try a.to(i32)) == 3); - try a.sub(neg_one.toConst(), neg_two.toConst()); + try a.sub(&neg_one, &neg_two); try testing.expect((try a.to(i32)) == 1); - try a.sub(neg_two.toConst(), neg_one.toConst()); + try a.sub(&neg_two, &neg_one); try testing.expect((try a.to(i32)) == -1); } @@ -876,7 +876,7 @@ test "big.int mul single-single" { var c = try Managed.init(testing.allocator); defer c.deinit(); - try c.mul(a.toConst(), b.toConst()); + try c.mul(&a, &b); try testing.expect((try c.to(u64)) == 250); } @@ -889,7 +889,7 @@ test "big.int mul multi-single" { var c = try Managed.init(testing.allocator); defer c.deinit(); - try c.mul(a.toConst(), b.toConst()); + try c.mul(&a, &b); try testing.expect((try c.to(DoubleLimb)) == 2 * maxInt(Limb)); } @@ -904,7 +904,7 @@ test "big.int mul multi-multi" { var c = try Managed.init(testing.allocator); defer c.deinit(); - try c.mul(a.toConst(), b.toConst()); + try c.mul(&a, &b); try testing.expect((try c.to(u256)) == op1 * op2); } @@ -915,7 +915,7 @@ test "big.int mul alias r with a" { var b = try Managed.initSet(testing.allocator, 2); defer b.deinit(); - try a.mul(a.toConst(), b.toConst()); + try a.mul(&a, &b); try testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb)); } @@ -926,7 +926,7 @@ test "big.int mul alias r with b" { var b = try Managed.initSet(testing.allocator, 2); defer b.deinit(); - try a.mul(b.toConst(), a.toConst()); + try a.mul(&b, &a); try testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb)); } @@ -935,7 +935,7 @@ test "big.int mul alias r with a and b" { var a = try Managed.initSet(testing.allocator, maxInt(Limb)); defer a.deinit(); - try a.mul(a.toConst(), a.toConst()); + try a.mul(&a, &a); try testing.expect((try a.to(DoubleLimb)) == maxInt(Limb) * maxInt(Limb)); } @@ -948,7 +948,7 @@ test "big.int mul a*0" { var c = try Managed.init(testing.allocator); defer c.deinit(); - try c.mul(a.toConst(), b.toConst()); + try c.mul(&a, &b); try testing.expect((try c.to(u32)) == 0); } @@ -961,7 +961,7 @@ test "big.int mul 0*0" { var c = try Managed.init(testing.allocator); defer c.deinit(); - try c.mul(a.toConst(), b.toConst()); + try c.mul(&a, &b); try testing.expect((try c.to(u32)) == 0); } @@ -981,8 +981,8 @@ test "big.int mul large" { } a.setMetadata(true, 50); - try b.mul(a.toConst(), a.toConst()); - try c.sqr(a.toConst()); + try b.mul(&a, &a); + try c.sqr(&a); try testing.expect(b.eq(c)); } @@ -995,7 +995,7 @@ test "big.int mulWrap single-single unsigned" { var c = try Managed.init(testing.allocator); defer c.deinit(); - try c.mulWrap(a.toConst(), b.toConst(), .unsigned, 17); + try c.mulWrap(&a, &b, .unsigned, 17); try testing.expect((try c.to(u17)) == 59836); } @@ -1008,7 +1008,7 @@ test "big.int mulWrap single-single signed" { var c = try Managed.init(testing.allocator); defer c.deinit(); - try c.mulWrap(a.toConst(), b.toConst(), .signed, 17); + try c.mulWrap(&a, &b, .signed, 17); try testing.expect((try c.to(i17)) == -59836); } @@ -1023,7 +1023,7 @@ test "big.int mulWrap multi-multi unsigned" { var c = try Managed.init(testing.allocator); defer c.deinit(); - try c.mulWrap(a.toConst(), b.toConst(), .unsigned, 65); + try c.mulWrap(&a, &b, .unsigned, 65); try testing.expect((try c.to(u128)) == (op1 * op2) & ((1 << 65) - 1)); } @@ -1036,7 +1036,7 @@ test "big.int mulWrap multi-multi signed" { var c = try Managed.init(testing.allocator); defer c.deinit(); - try c.mulWrap(a.toConst(), b.toConst(), .signed, @bitSizeOf(SignedDoubleLimb)); + try c.mulWrap(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb)); try testing.expect((try c.to(SignedDoubleLimb)) == minInt(SignedDoubleLimb) + 2); } @@ -1058,9 +1058,9 @@ test "big.int mulWrap large" { const testbits = @bitSizeOf(Limb) * 64 + 45; - try b.mulWrap(a.toConst(), a.toConst(), .signed, testbits); - try c.sqr(a.toConst()); - try c.truncate(c.toConst(), .signed, testbits); + try b.mulWrap(&a, &a, .signed, testbits); + try c.sqr(&a); + try c.truncate(&c, .signed, testbits); try testing.expect(b.eq(c)); } @@ -1075,7 +1075,7 @@ test "big.int div single-half no rem" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); try testing.expect((try q.to(u32)) == 10); try testing.expect((try r.to(u32)) == 0); @@ -1091,7 +1091,7 @@ test "big.int div single-half with rem" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); try testing.expect((try q.to(u32)) == 9); try testing.expect((try r.to(u32)) == 4); @@ -1108,7 +1108,7 @@ test "big.int div single-single no rem" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); try testing.expect((try q.to(u32)) == 131072); try testing.expect((try r.to(u32)) == 0); @@ -1124,7 +1124,7 @@ test "big.int div single-single with rem" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); try testing.expect((try q.to(u64)) == 131072); try testing.expect((try r.to(u64)) == 8589934592); @@ -1143,7 +1143,7 @@ test "big.int div multi-single no rem" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); try testing.expect((try q.to(u64)) == op1 / op2); try testing.expect((try r.to(u64)) == 0); @@ -1162,7 +1162,7 @@ test "big.int div multi-single with rem" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); try testing.expect((try q.to(u64)) == op1 / op2); try testing.expect((try r.to(u64)) == 3); @@ -1181,7 +1181,7 @@ test "big.int div multi>2-single" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); try testing.expect((try q.to(u128)) == op1 / op2); try testing.expect((try r.to(u32)) == 0x3e4e); @@ -1197,7 +1197,7 @@ test "big.int div single-single q < r" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); try testing.expect((try q.to(u64)) == 0); try testing.expect((try r.to(u64)) == 0x0078f432); @@ -1213,7 +1213,7 @@ test "big.int div single-single q == r" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); try testing.expect((try q.to(u64)) == 1); try testing.expect((try r.to(u64)) == 0); @@ -1225,7 +1225,7 @@ test "big.int div q=0 alias" { var b = try Managed.initSet(testing.allocator, 10); defer b.deinit(); - try Managed.divTrunc(&a, &b, a.toConst(), b.toConst()); + try Managed.divTrunc(&a, &b, &a, &b); try testing.expect((try a.to(u64)) == 0); try testing.expect((try b.to(u64)) == 3); @@ -1243,7 +1243,7 @@ test "big.int div multi-multi q < r" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); try testing.expect((try q.to(u128)) == 0); try testing.expect((try r.to(u128)) == op1); @@ -1262,7 +1262,7 @@ test "big.int div trunc single-single +/+" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); // n = q * d + r // 5 = 1 * 3 + 2 @@ -1286,7 +1286,7 @@ test "big.int div trunc single-single -/+" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); // n = q * d + r // -5 = 1 * -3 - 2 @@ -1310,7 +1310,7 @@ test "big.int div trunc single-single +/-" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); // n = q * d + r // 5 = -1 * -3 + 2 @@ -1334,7 +1334,7 @@ test "big.int div trunc single-single -/-" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); // n = q * d + r // -5 = 1 * -3 - 2 @@ -1361,7 +1361,7 @@ test "big.int divFloor #10932" { var mod = try Managed.init(testing.allocator); defer mod.deinit(); - try res.divFloor(&mod, a.toConst(), b.toConst()); + try res.divFloor(&mod, &a, &b); const ress = try res.toString(testing.allocator, 16, .lower); defer testing.allocator.free(ress); @@ -1385,7 +1385,7 @@ test "big.int divFloor #11166" { var mod = try Managed.init(testing.allocator); defer mod.deinit(); - try res.divFloor(&mod, a.toConst(), b.toConst()); + try res.divFloor(&mod, &a, &b); const ress = try res.toString(testing.allocator, 10, .lower); defer testing.allocator.free(ress); @@ -1409,7 +1409,7 @@ test "big.int gcd #10932" { try a.setString(10, "3000000000000000000000000000000000000000000000000000000000000000000000001461501637330902918203684832716283019655932542975000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); try b.setString(10, "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200001001500000000000000000100000000040000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000003000000000000000000000000000000000000000000000000000058715661000000000000000000000000000000000000023553252000000000180000000000000000000000000000000000000000000000000250000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001005000002000000000000000000000000000000000000000021000000001000000000000000000000000100000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000200000000000000000000004000000000000000000000000000000000000000000000301000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - try res.gcd(a, b); + try res.gcd(&a, &b); const ress = try res.toString(testing.allocator, 16, .lower); defer testing.allocator.free(ress); @@ -1429,7 +1429,7 @@ test "big.int bitAnd #10932" { try a.setString(10, "154954885951624787839743960731760616696"); try b.setString(10, "55000000000915215865915724129619485917228346934191537590366734850266784978214506142389798064826139649163838075568111457203909393174933092857416500785632012953993352521899237655507306575657169267399324107627651067352600878339870446048204062696260567762088867991835386857942106708741836433444432529637331429212430394179472179237695833247299409249810963487516399177133175950185719220422442438098353430605822151595560743492661038899294517012784306863064670126197566982968906306814338148792888550378533207318063660581924736840687332023636827401670268933229183389040490792300121030647791095178823932734160000000000000000000000000000000000000555555550000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - try res.bitAnd(a, b); + try res.bitAnd(&a, &b); try testing.expect((try res.to(i32)) == 0); } @@ -1447,7 +1447,7 @@ test "big.int div floor single-single +/+" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divFloor(&q, &r, a.toConst(), b.toConst()); + try Managed.divFloor(&q, &r, &a, &b); // n = q * d + r // 5 = 1 * 3 + 2 @@ -1471,7 +1471,7 @@ test "big.int div floor single-single -/+" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divFloor(&q, &r, a.toConst(), b.toConst()); + try Managed.divFloor(&q, &r, &a, &b); // n = q * d + r // -5 = -2 * 3 + 1 @@ -1495,7 +1495,7 @@ test "big.int div floor single-single +/-" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divFloor(&q, &r, a.toConst(), b.toConst()); + try Managed.divFloor(&q, &r, &a, &b); // n = q * d + r // 5 = -2 * -3 - 1 @@ -1519,7 +1519,7 @@ test "big.int div floor single-single -/-" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divFloor(&q, &r, a.toConst(), b.toConst()); + try Managed.divFloor(&q, &r, &a, &b); // n = q * d + r // -5 = 2 * -3 + 1 @@ -1543,7 +1543,7 @@ test "big.int div floor no remainder negative quotient" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divFloor(&q, &r, a.toConst(), b.toConst()); + try Managed.divFloor(&q, &r, &a, &b); try testing.expect((try q.to(i32)) == -0x80000000); try testing.expect((try r.to(i32)) == 0); @@ -1562,7 +1562,7 @@ test "big.int div floor negative close to zero" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divFloor(&q, &r, a.toConst(), b.toConst()); + try Managed.divFloor(&q, &r, &a, &b); try testing.expect((try q.to(i32)) == -1); try testing.expect((try r.to(i32)) == 10); @@ -1581,7 +1581,7 @@ test "big.int div floor positive close to zero" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divFloor(&q, &r, a.toConst(), b.toConst()); + try Managed.divFloor(&q, &r, &a, &b); try testing.expect((try q.to(i32)) == 0); try testing.expect((try r.to(i32)) == 10); @@ -1597,7 +1597,7 @@ test "big.int div multi-multi with rem" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); try testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); try testing.expect((try r.to(u128)) == 0x28de0acacd806823638); @@ -1613,7 +1613,7 @@ test "big.int div multi-multi no rem" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); try testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); try testing.expect((try r.to(u128)) == 0); @@ -1629,7 +1629,7 @@ test "big.int div multi-multi (2 branch)" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); try testing.expect((try q.to(u128)) == 0x10000000000000000); try testing.expect((try r.to(u128)) == 0x44444443444444431111111111111111); @@ -1645,7 +1645,7 @@ test "big.int div multi-multi (3.1/3.3 branch)" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); try testing.expect((try q.to(u128)) == 0xfffffffffffffffffff); try testing.expect((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282); @@ -1661,7 +1661,7 @@ test "big.int div multi-single zero-limb trailing" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); var expected = try Managed.initSet(testing.allocator, 0x6000000000000000000000000000000000000000000000000); defer expected.deinit(); @@ -1679,7 +1679,7 @@ test "big.int div multi-multi zero-limb trailing (with rem)" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); try testing.expect((try q.to(u128)) == 0x10000000000000000); @@ -1698,7 +1698,7 @@ test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-li defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); try testing.expect((try q.to(u128)) == 0x1); @@ -1717,7 +1717,7 @@ test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-li defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); const qs = try q.toString(testing.allocator, 16, .lower); defer testing.allocator.free(qs); @@ -1741,7 +1741,7 @@ test "big.int div multi-multi fuzz case #1" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); const qs = try q.toString(testing.allocator, 16, .lower); defer testing.allocator.free(qs); @@ -1765,7 +1765,7 @@ test "big.int div multi-multi fuzz case #2" { defer q.deinit(); var r = try Managed.init(testing.allocator); defer r.deinit(); - try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); + try Managed.divTrunc(&q, &r, &a, &b); const qs = try q.toString(testing.allocator, 16, .lower); defer testing.allocator.free(qs); @@ -1780,7 +1780,7 @@ test "big.int truncate single unsigned" { var a = try Managed.initSet(testing.allocator, maxInt(u47)); defer a.deinit(); - try a.truncate(a.toConst(), .unsigned, 17); + try a.truncate(&a, .unsigned, 17); try testing.expect((try a.to(u17)) == maxInt(u17)); } @@ -1789,7 +1789,7 @@ test "big.int truncate single signed" { var a = try Managed.initSet(testing.allocator, 0x1_0000); defer a.deinit(); - try a.truncate(a.toConst(), .signed, 17); + try a.truncate(&a, .signed, 17); try testing.expect((try a.to(i17)) == minInt(i17)); } @@ -1798,7 +1798,7 @@ test "big.int truncate multi to single unsigned" { var a = try Managed.initSet(testing.allocator, (maxInt(Limb) + 1) | 0x1234_5678_9ABC_DEF0); defer a.deinit(); - try a.truncate(a.toConst(), .unsigned, 27); + try a.truncate(&a, .unsigned, 27); try testing.expect((try a.to(u27)) == 0x2BC_DEF0); } @@ -1807,7 +1807,7 @@ test "big.int truncate multi to single signed" { var a = try Managed.initSet(testing.allocator, maxInt(Limb) << 10); defer a.deinit(); - try a.truncate(a.toConst(), .signed, @bitSizeOf(i11)); + try a.truncate(&a, .signed, @bitSizeOf(i11)); try testing.expect((try a.to(i11)) == minInt(i11)); } @@ -1819,7 +1819,7 @@ test "big.int truncate multi to multi unsigned" { var a = try Managed.initSet(testing.allocator, maxInt(SignedDoubleLimb)); defer a.deinit(); - try a.truncate(a.toConst(), .unsigned, bits - 1); + try a.truncate(&a, .unsigned, bits - 1); try testing.expect((try a.to(Int)) == maxInt(Int)); } @@ -1828,7 +1828,7 @@ test "big.int truncate multi to multi signed" { var a = try Managed.initSet(testing.allocator, 3 << @bitSizeOf(Limb)); defer a.deinit(); - try a.truncate(a.toConst(), .signed, @bitSizeOf(Limb) + 1); + try a.truncate(&a, .signed, @bitSizeOf(Limb) + 1); try testing.expect((try a.to(std.meta.Int(.signed, @bitSizeOf(Limb) + 1))) == -1 << @bitSizeOf(Limb)); } @@ -1837,7 +1837,7 @@ test "big.int truncate negative multi to single" { var a = try Managed.initSet(testing.allocator, -@as(SignedDoubleLimb, maxInt(Limb) + 1)); defer a.deinit(); - try a.truncate(a.toConst(), .signed, @bitSizeOf(i17)); + try a.truncate(&a, .signed, @bitSizeOf(i17)); try testing.expect((try a.to(i17)) == 0); } @@ -1845,11 +1845,11 @@ test "big.int truncate negative multi to single" { test "big.int truncate multi unsigned many" { var a = try Managed.initSet(testing.allocator, 1); defer a.deinit(); - try a.shiftLeft(a, 1023); + try a.shiftLeft(&a, 1023); var b = try Managed.init(testing.allocator); defer b.deinit(); - try b.truncate(a.toConst(), .signed, @bitSizeOf(i1)); + try b.truncate(&a, .signed, @bitSizeOf(i1)); try testing.expect((try b.to(i1)) == 0); } @@ -1858,7 +1858,7 @@ test "big.int saturate single signed positive" { var a = try Managed.initSet(testing.allocator, 0xBBBB_BBBB); defer a.deinit(); - try a.saturate(a.toConst(), .signed, 17); + try a.saturate(&a, .signed, 17); try testing.expect((try a.to(i17)) == maxInt(i17)); } @@ -1867,7 +1867,7 @@ test "big.int saturate single signed negative" { var a = try Managed.initSet(testing.allocator, -1_234_567); defer a.deinit(); - try a.saturate(a.toConst(), .signed, 17); + try a.saturate(&a, .signed, 17); try testing.expect((try a.to(i17)) == minInt(i17)); } @@ -1876,7 +1876,7 @@ test "big.int saturate single signed" { var a = try Managed.initSet(testing.allocator, maxInt(i17) - 1); defer a.deinit(); - try a.saturate(a.toConst(), .signed, 17); + try a.saturate(&a, .signed, 17); try testing.expect((try a.to(i17)) == maxInt(i17) - 1); } @@ -1885,7 +1885,7 @@ test "big.int saturate multi signed" { var a = try Managed.initSet(testing.allocator, maxInt(Limb) << @bitSizeOf(SignedDoubleLimb)); defer a.deinit(); - try a.saturate(a.toConst(), .signed, @bitSizeOf(SignedDoubleLimb)); + try a.saturate(&a, .signed, @bitSizeOf(SignedDoubleLimb)); try testing.expect((try a.to(SignedDoubleLimb)) == maxInt(SignedDoubleLimb)); } @@ -1894,7 +1894,7 @@ test "big.int saturate single unsigned" { var a = try Managed.initSet(testing.allocator, 0xFEFE_FEFE); defer a.deinit(); - try a.saturate(a.toConst(), .unsigned, 23); + try a.saturate(&a, .unsigned, 23); try testing.expect((try a.to(u23)) == maxInt(u23)); } @@ -1903,7 +1903,7 @@ test "big.int saturate multi unsigned zero" { var a = try Managed.initSet(testing.allocator, -1); defer a.deinit(); - try a.saturate(a.toConst(), .unsigned, @bitSizeOf(DoubleLimb)); + try a.saturate(&a, .unsigned, @bitSizeOf(DoubleLimb)); try testing.expect(a.eqZero()); } @@ -1912,7 +1912,7 @@ test "big.int saturate multi unsigned" { var a = try Managed.initSet(testing.allocator, maxInt(Limb) << @bitSizeOf(DoubleLimb)); defer a.deinit(); - try a.saturate(a.toConst(), .unsigned, @bitSizeOf(DoubleLimb)); + try a.saturate(&a, .unsigned, @bitSizeOf(DoubleLimb)); try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb)); } @@ -1920,7 +1920,7 @@ test "big.int saturate multi unsigned" { test "big.int shift-right single" { var a = try Managed.initSet(testing.allocator, 0xffff0000); defer a.deinit(); - try a.shiftRight(a, 16); + try a.shiftRight(&a, 16); try testing.expect((try a.to(u32)) == 0xffff); } @@ -1928,21 +1928,21 @@ test "big.int shift-right single" { test "big.int shift-right multi" { var a = try Managed.initSet(testing.allocator, 0xffff0000eeee1111dddd2222cccc3333); defer a.deinit(); - try a.shiftRight(a, 67); + try a.shiftRight(&a, 67); try testing.expect((try a.to(u64)) == 0x1fffe0001dddc222); try a.set(0xffff0000eeee1111dddd2222cccc3333); - try a.shiftRight(a, 63); - try a.shiftRight(a, 63); - try a.shiftRight(a, 2); + try a.shiftRight(&a, 63); + try a.shiftRight(&a, 63); + try a.shiftRight(&a, 2); try testing.expect(a.eqZero()); } test "big.int shift-left single" { var a = try Managed.initSet(testing.allocator, 0xffff); defer a.deinit(); - try a.shiftLeft(a, 16); + try a.shiftLeft(&a, 16); try testing.expect((try a.to(u64)) == 0xffff0000); } @@ -1950,7 +1950,7 @@ test "big.int shift-left single" { test "big.int shift-left multi" { var a = try Managed.initSet(testing.allocator, 0x1fffe0001dddc222); defer a.deinit(); - try a.shiftLeft(a, 67); + try a.shiftLeft(&a, 67); try testing.expect((try a.to(u128)) == 0xffff0000eeee11100000000000000000); } @@ -1961,12 +1961,12 @@ test "big.int shift-right negative" { var arg = try Managed.initSet(testing.allocator, -20); defer arg.deinit(); - try a.shiftRight(arg, 2); + try a.shiftRight(&arg, 2); try testing.expect((try a.to(i32)) == -20 >> 2); var arg2 = try Managed.initSet(testing.allocator, -5); defer arg2.deinit(); - try a.shiftRight(arg2, 10); + try a.shiftRight(&arg2, 10); try testing.expect((try a.to(i32)) == -5 >> 10); } @@ -1976,14 +1976,14 @@ test "big.int shift-left negative" { var arg = try Managed.initSet(testing.allocator, -10); defer arg.deinit(); - try a.shiftRight(arg, 1232); + try a.shiftRight(&arg, 1232); try testing.expect((try a.to(i32)) == -10 >> 1232); } test "big.int sat shift-left simple unsigned" { var a = try Managed.initSet(testing.allocator, 0xffff); defer a.deinit(); - try a.shiftLeftSat(a, 16, .unsigned, 21); + try a.shiftLeftSat(&a, 16, .unsigned, 21); try testing.expect((try a.to(u64)) == 0x1fffff); } @@ -1991,7 +1991,7 @@ test "big.int sat shift-left simple unsigned" { test "big.int sat shift-left simple unsigned no sat" { var a = try Managed.initSet(testing.allocator, 1); defer a.deinit(); - try a.shiftLeftSat(a, 16, .unsigned, 21); + try a.shiftLeftSat(&a, 16, .unsigned, 21); try testing.expect((try a.to(u64)) == 0x10000); } @@ -1999,7 +1999,7 @@ test "big.int sat shift-left simple unsigned no sat" { test "big.int sat shift-left multi unsigned" { var a = try Managed.initSet(testing.allocator, 16); defer a.deinit(); - try a.shiftLeftSat(a, @bitSizeOf(DoubleLimb) - 3, .unsigned, @bitSizeOf(DoubleLimb) - 1); + try a.shiftLeftSat(&a, @bitSizeOf(DoubleLimb) - 3, .unsigned, @bitSizeOf(DoubleLimb) - 1); try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb) >> 1); } @@ -2007,7 +2007,7 @@ test "big.int sat shift-left multi unsigned" { test "big.int sat shift-left unsigned shift > bitcount" { var a = try Managed.initSet(testing.allocator, 1); defer a.deinit(); - try a.shiftLeftSat(a, 10, .unsigned, 10); + try a.shiftLeftSat(&a, 10, .unsigned, 10); try testing.expect((try a.to(u10)) == maxInt(u10)); } @@ -2015,7 +2015,7 @@ test "big.int sat shift-left unsigned shift > bitcount" { test "big.int sat shift-left unsigned zero" { var a = try Managed.initSet(testing.allocator, 0); defer a.deinit(); - try a.shiftLeftSat(a, 1, .unsigned, 0); + try a.shiftLeftSat(&a, 1, .unsigned, 0); try testing.expect((try a.to(u64)) == 0); } @@ -2023,7 +2023,7 @@ test "big.int sat shift-left unsigned zero" { test "big.int sat shift-left unsigned negative" { var a = try Managed.initSet(testing.allocator, -100); defer a.deinit(); - try a.shiftLeftSat(a, 0, .unsigned, 0); + try a.shiftLeftSat(&a, 0, .unsigned, 0); try testing.expect((try a.to(u64)) == 0); } @@ -2031,7 +2031,7 @@ test "big.int sat shift-left unsigned negative" { test "big.int sat shift-left signed simple negative" { var a = try Managed.initSet(testing.allocator, -100); defer a.deinit(); - try a.shiftLeftSat(a, 3, .signed, 10); + try a.shiftLeftSat(&a, 3, .signed, 10); try testing.expect((try a.to(i10)) == minInt(i10)); } @@ -2039,7 +2039,7 @@ test "big.int sat shift-left signed simple negative" { test "big.int sat shift-left signed simple positive" { var a = try Managed.initSet(testing.allocator, 100); defer a.deinit(); - try a.shiftLeftSat(a, 3, .signed, 10); + try a.shiftLeftSat(&a, 3, .signed, 10); try testing.expect((try a.to(i10)) == maxInt(i10)); } @@ -2050,7 +2050,7 @@ test "big.int sat shift-left signed multi positive" { var a = try Managed.initSet(testing.allocator, x); defer a.deinit(); - try a.shiftLeftSat(a, shift, .signed, @bitSizeOf(SignedDoubleLimb)); + try a.shiftLeftSat(&a, shift, .signed, @bitSizeOf(SignedDoubleLimb)); try testing.expect((try a.to(SignedDoubleLimb)) == @as(SignedDoubleLimb, x) <<| shift); } @@ -2061,7 +2061,7 @@ test "big.int sat shift-left signed multi negative" { var a = try Managed.initSet(testing.allocator, x); defer a.deinit(); - try a.shiftLeftSat(a, shift, .signed, @bitSizeOf(SignedDoubleLimb)); + try a.shiftLeftSat(&a, shift, .signed, @bitSizeOf(SignedDoubleLimb)); try testing.expect((try a.to(SignedDoubleLimb)) == @as(SignedDoubleLimb, x) <<| shift); } @@ -2070,7 +2070,7 @@ test "big.int bitNotWrap unsigned simple" { var a = try Managed.initSet(testing.allocator, 123); defer a.deinit(); - try a.bitNotWrap(a, .unsigned, 10); + try a.bitNotWrap(&a, .unsigned, 10); try testing.expect((try a.to(u10)) == ~@as(u10, 123)); } @@ -2079,7 +2079,7 @@ test "big.int bitNotWrap unsigned multi" { var a = try Managed.initSet(testing.allocator, 0); defer a.deinit(); - try a.bitNotWrap(a, .unsigned, @bitSizeOf(DoubleLimb)); + try a.bitNotWrap(&a, .unsigned, @bitSizeOf(DoubleLimb)); try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb)); } @@ -2088,7 +2088,7 @@ test "big.int bitNotWrap signed simple" { var a = try Managed.initSet(testing.allocator, -456); defer a.deinit(); - try a.bitNotWrap(a, .signed, 11); + try a.bitNotWrap(&a, .signed, 11); try testing.expect((try a.to(i11)) == ~@as(i11, -456)); } @@ -2097,7 +2097,7 @@ test "big.int bitNotWrap signed multi" { var a = try Managed.initSet(testing.allocator, 0); defer a.deinit(); - try a.bitNotWrap(a, .signed, @bitSizeOf(SignedDoubleLimb)); + try a.bitNotWrap(&a, .signed, @bitSizeOf(SignedDoubleLimb)); try testing.expect((try a.to(SignedDoubleLimb)) == -1); } @@ -2108,7 +2108,7 @@ test "big.int bitwise and simple" { var b = try Managed.initSet(testing.allocator, 0xeeeeeeee22222222); defer b.deinit(); - try a.bitAnd(a, b); + try a.bitAnd(&a, &b); try testing.expect((try a.to(u64)) == 0xeeeeeeee00000000); } @@ -2119,7 +2119,7 @@ test "big.int bitwise and multi-limb" { var b = try Managed.initSet(testing.allocator, maxInt(Limb)); defer b.deinit(); - try a.bitAnd(a, b); + try a.bitAnd(&a, &b); try testing.expect((try a.to(u128)) == 0); } @@ -2130,7 +2130,7 @@ test "big.int bitwise and negative-positive simple" { var b = try Managed.initSet(testing.allocator, 0xeeeeeeee22222222); defer b.deinit(); - try a.bitAnd(a, b); + try a.bitAnd(&a, &b); try testing.expect((try a.to(u64)) == 0x22222222); } @@ -2141,7 +2141,7 @@ test "big.int bitwise and negative-positive multi-limb" { var b = try Managed.initSet(testing.allocator, maxInt(Limb)); defer b.deinit(); - try a.bitAnd(a, b); + try a.bitAnd(&a, &b); try testing.expect(a.eqZero()); } @@ -2152,7 +2152,7 @@ test "big.int bitwise and positive-negative simple" { var b = try Managed.initSet(testing.allocator, -0xeeeeeeee22222222); defer b.deinit(); - try a.bitAnd(a, b); + try a.bitAnd(&a, &b); try testing.expect((try a.to(u64)) == 0x1111111111111110); } @@ -2163,7 +2163,7 @@ test "big.int bitwise and positive-negative multi-limb" { var b = try Managed.initSet(testing.allocator, -maxInt(Limb) - 1); defer b.deinit(); - try a.bitAnd(a, b); + try a.bitAnd(&a, &b); try testing.expect(a.eqZero()); } @@ -2174,7 +2174,7 @@ test "big.int bitwise and negative-negative simple" { var b = try Managed.initSet(testing.allocator, -0xeeeeeeee22222222); defer b.deinit(); - try a.bitAnd(a, b); + try a.bitAnd(&a, &b); try testing.expect((try a.to(i128)) == -0xffffffff33333332); } @@ -2185,7 +2185,7 @@ test "big.int bitwise and negative-negative multi-limb" { var b = try Managed.initSet(testing.allocator, -maxInt(Limb) - 2); defer b.deinit(); - try a.bitAnd(a, b); + try a.bitAnd(&a, &b); try testing.expect((try a.to(i128)) == -maxInt(Limb) * 2 - 2); } @@ -2196,7 +2196,7 @@ test "big.int bitwise and negative overflow" { var b = try Managed.initSet(testing.allocator, -2); defer b.deinit(); - try a.bitAnd(a, b); + try a.bitAnd(&a, &b); try testing.expect((try a.to(SignedDoubleLimb)) == -maxInt(Limb) - 1); } @@ -2207,7 +2207,7 @@ test "big.int bitwise xor simple" { var b = try Managed.initSet(testing.allocator, 0xeeeeeeee22222222); defer b.deinit(); - try a.bitXor(a, b); + try a.bitXor(&a, &b); try testing.expect((try a.to(u64)) == 0x1111111133333333); } @@ -2218,7 +2218,7 @@ test "big.int bitwise xor multi-limb" { var b = try Managed.initSet(testing.allocator, maxInt(Limb)); defer b.deinit(); - try a.bitXor(a, b); + try a.bitXor(&a, &b); try testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) ^ maxInt(Limb)); } @@ -2229,7 +2229,7 @@ test "big.int bitwise xor single negative simple" { var b = try Managed.initSet(testing.allocator, -0x45fd3acef9191fad); defer b.deinit(); - try a.bitXor(a, b); + try a.bitXor(&a, &b); try testing.expect((try a.to(i64)) == -0x2efed94fcb932ef9); } @@ -2240,7 +2240,7 @@ test "big.int bitwise xor single negative zero" { var b = try Managed.initSet(testing.allocator, -0); defer b.deinit(); - try a.bitXor(a, b); + try a.bitXor(&a, &b); try testing.expect(a.eqZero()); } @@ -2251,7 +2251,7 @@ test "big.int bitwise xor single negative multi-limb" { var b = try Managed.initSet(testing.allocator, 0xf2194e7d1c855272a997fcde16f6d5a8); defer b.deinit(); - try a.bitXor(a, b); + try a.bitXor(&a, &b); try testing.expect((try a.to(i128)) == -0x6a50889abd8834a24db1f19650d3999a); } @@ -2262,7 +2262,7 @@ test "big.int bitwise xor single negative overflow" { var b = try Managed.initSet(testing.allocator, -1); defer b.deinit(); - try a.bitXor(a, b); + try a.bitXor(&a, &b); try testing.expect((try a.to(SignedDoubleLimb)) == -(maxInt(Limb) + 1)); } @@ -2273,7 +2273,7 @@ test "big.int bitwise xor double negative simple" { var b = try Managed.initSet(testing.allocator, -0x4dd4fa576f3046ac); defer b.deinit(); - try a.bitXor(a, b); + try a.bitXor(&a, &b); try testing.expect((try a.to(u64)) == 0xc39c47081a6eb759); } @@ -2284,7 +2284,7 @@ test "big.int bitwise xor double negative multi-limb" { var b = try Managed.initSet(testing.allocator, -0xcb07736a7b62289c78d967c3985eebeb); defer b.deinit(); - try a.bitXor(a, b); + try a.bitXor(&a, &b); try testing.expect((try a.to(u128)) == 0xa3492ec28e62c410dff92bf0549bf771); } @@ -2295,7 +2295,7 @@ test "big.int bitwise or simple" { var b = try Managed.initSet(testing.allocator, 0xeeeeeeee22222222); defer b.deinit(); - try a.bitOr(a, b); + try a.bitOr(&a, &b); try testing.expect((try a.to(u64)) == 0xffffffff33333333); } @@ -2306,7 +2306,7 @@ test "big.int bitwise or multi-limb" { var b = try Managed.initSet(testing.allocator, maxInt(Limb)); defer b.deinit(); - try a.bitOr(a, b); + try a.bitOr(&a, &b); // TODO: big.int.cpp or is wrong on multi-limb. try testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb)); @@ -2318,7 +2318,7 @@ test "big.int bitwise or negative-positive simple" { var b = try Managed.initSet(testing.allocator, 0xeeeeeeee22222222); defer b.deinit(); - try a.bitOr(a, b); + try a.bitOr(&a, &b); try testing.expect((try a.to(i64)) == -0x1111111111111111); } @@ -2329,7 +2329,7 @@ test "big.int bitwise or negative-positive multi-limb" { var b = try Managed.initSet(testing.allocator, 1); defer b.deinit(); - try a.bitOr(a, b); + try a.bitOr(&a, &b); try testing.expect((try a.to(SignedDoubleLimb)) == -maxInt(Limb)); } @@ -2340,7 +2340,7 @@ test "big.int bitwise or positive-negative simple" { var b = try Managed.initSet(testing.allocator, -0xeeeeeeee22222222); defer b.deinit(); - try a.bitOr(a, b); + try a.bitOr(&a, &b); try testing.expect((try a.to(i64)) == -0x22222221); } @@ -2351,7 +2351,7 @@ test "big.int bitwise or positive-negative multi-limb" { var b = try Managed.initSet(testing.allocator, -1); defer b.deinit(); - try a.bitOr(a, b); + try a.bitOr(&a, &b); try testing.expect((try a.to(SignedDoubleLimb)) == -1); } @@ -2362,7 +2362,7 @@ test "big.int bitwise or negative-negative simple" { var b = try Managed.initSet(testing.allocator, -0xeeeeeeee22222222); defer b.deinit(); - try a.bitOr(a, b); + try a.bitOr(&a, &b); try testing.expect((try a.to(i128)) == -0xeeeeeeee00000001); } @@ -2373,7 +2373,7 @@ test "big.int bitwise or negative-negative multi-limb" { var b = try Managed.initSet(testing.allocator, -maxInt(Limb)); defer b.deinit(); - try a.bitOr(a, b); + try a.bitOr(&a, &b); try testing.expect((try a.to(SignedDoubleLimb)) == -maxInt(Limb)); } @@ -2384,7 +2384,7 @@ test "big.int var args" { var b = try Managed.initSet(testing.allocator, 6); defer b.deinit(); - try a.add(a.toConst(), b.toConst()); + try a.add(&a, &b); try testing.expect((try a.to(u64)) == 11); var c = try Managed.initSet(testing.allocator, 11); @@ -2404,7 +2404,7 @@ test "big.int gcd non-one small" { var r = try Managed.init(testing.allocator); defer r.deinit(); - try r.gcd(a, b); + try r.gcd(&a, &b); try testing.expect((try r.to(u32)) == 1); } @@ -2417,7 +2417,7 @@ test "big.int gcd non-one small" { var r = try Managed.init(testing.allocator); defer r.deinit(); - try r.gcd(a, b); + try r.gcd(&a, &b); try testing.expect((try r.to(u32)) == 38); } @@ -2430,7 +2430,7 @@ test "big.int gcd non-one large" { var r = try Managed.init(testing.allocator); defer r.deinit(); - try r.gcd(a, b); + try r.gcd(&a, &b); try testing.expect((try r.to(u32)) == 4369); } @@ -2443,7 +2443,7 @@ test "big.int gcd large multi-limb result" { var r = try Managed.init(testing.allocator); defer r.deinit(); - try r.gcd(a, b); + try r.gcd(&a, &b); const answer = (try r.to(u256)); try testing.expect(answer == 0xf000000ff00000fff0000ffff000fffff00ffffff1); @@ -2457,7 +2457,7 @@ test "big.int gcd one large" { var r = try Managed.init(testing.allocator); defer r.deinit(); - try r.gcd(a, b); + try r.gcd(&a, &b); try testing.expect((try r.to(u64)) == 1); } @@ -2488,10 +2488,10 @@ test "big.int pow" { var a = try Managed.initSet(testing.allocator, -3); defer a.deinit(); - try a.pow(a.toConst(), 3); + try a.pow(&a, 3); try testing.expectEqual(@as(i32, -27), try a.to(i32)); - try a.pow(a.toConst(), 4); + try a.pow(&a, 4); try testing.expectEqual(@as(i32, 531441), try a.to(i32)); } { @@ -2502,9 +2502,9 @@ test "big.int pow" { defer y.deinit(); // y and a are not aliased - try y.pow(a.toConst(), 123); + try y.pow(&a, 123); // y and a are aliased - try a.pow(a.toConst(), 123); + try a.pow(&a, 123); try testing.expect(a.eq(y)); @@ -2522,18 +2522,18 @@ test "big.int pow" { var a = try Managed.initSet(testing.allocator, 0); defer a.deinit(); - try a.pow(a.toConst(), 100); + try a.pow(&a, 100); try testing.expectEqual(@as(i32, 0), try a.to(i32)); try a.set(1); - try a.pow(a.toConst(), 0); + try a.pow(&a, 0); try testing.expectEqual(@as(i32, 1), try a.to(i32)); - try a.pow(a.toConst(), 100); + try a.pow(&a, 100); try testing.expectEqual(@as(i32, 1), try a.to(i32)); try a.set(-1); - try a.pow(a.toConst(), 15); + try a.pow(&a, 15); try testing.expectEqual(@as(i32, -1), try a.to(i32)); - try a.pow(a.toConst(), 16); + try a.pow(&a, 16); try testing.expectEqual(@as(i32, 1), try a.to(i32)); } } @@ -2547,7 +2547,7 @@ test "big.int regression test for 1 limb overflow with alias" { defer b.deinit(); try a.ensureAddCapacity(a.toConst(), b.toConst()); - try a.add(a.toConst(), b.toConst()); + try a.add(&a, &b); try testing.expect(a.toConst().orderAgainstScalar(19740274219868223167) == .eq); } @@ -2561,7 +2561,7 @@ test "big.int regression test for realloc with alias" { defer b.deinit(); try a.ensureAddCapacity(a.toConst(), b.toConst()); - try a.add(a.toConst(), b.toConst()); + try a.add(&a, &b); try testing.expect(a.toConst().orderAgainstScalar(14691098406862188148944207245954912110548093601382197697835) == .eq); } @@ -2572,7 +2572,7 @@ test "big int popcount" { var b = try Managed.initSet(testing.allocator, -1); defer b.deinit(); - try a.popCount(b.toConst(), 16); + try a.popCount(&b, 16); try testing.expect(a.toConst().orderAgainstScalar(16) == .eq); } diff --git a/lib/std/math/big/rational.zig b/lib/std/math/big/rational.zig index e83b017335..895b20d9b5 100644 --- a/lib/std/math/big/rational.zig +++ b/lib/std/math/big/rational.zig @@ -102,20 +102,23 @@ pub const Rational = struct { try self.p.setString(10, str[0..i]); const base = IntConst{ .limbs = &[_]Limb{10}, .positive = true }; + var local_buf: [@sizeOf(Limb) * Int.default_capacity]u8 align(@alignOf(Limb)) = undefined; + var fba = std.heap.FixedBufferAllocator.init(&local_buf); + const base_managed = try base.toManaged(fba.allocator()); var j: usize = start; while (j < str.len - i - 1) : (j += 1) { try self.p.ensureMulCapacity(self.p.toConst(), base); - try self.p.mul(self.p.toConst(), base); + try self.p.mul(&self.p, &base_managed); } try self.q.setString(10, str[i + 1 ..]); - try self.p.add(self.p.toConst(), self.q.toConst()); + try self.p.add(&self.p, &self.q); try self.q.set(1); var k: usize = i + 1; while (k < str.len) : (k += 1) { - try self.q.mul(self.q.toConst(), base); + try self.q.mul(&self.q, &base_managed); } try self.reduce(); @@ -172,9 +175,9 @@ pub const Rational = struct { try self.q.set(1); if (shift >= 0) { - try self.q.shiftLeft(self.q, @intCast(usize, shift)); + try self.q.shiftLeft(&self.q, @intCast(usize, shift)); } else { - try self.p.shiftLeft(self.p, @intCast(usize, -shift)); + try self.p.shiftLeft(&self.p, @intCast(usize, -shift)); } try self.reduce(); @@ -215,9 +218,9 @@ pub const Rational = struct { const shift = msize2 - exp; if (shift >= 0) { - try a2.shiftLeft(a2, @intCast(usize, shift)); + try a2.shiftLeft(&a2, @intCast(usize, shift)); } else { - try b2.shiftLeft(b2, @intCast(usize, -shift)); + try b2.shiftLeft(&b2, @intCast(usize, -shift)); } // 2. compute quotient and remainder @@ -228,7 +231,7 @@ pub const Rational = struct { var r = try Int.init(self.p.allocator); defer r.deinit(); - try Int.divTrunc(&q, &r, a2.toConst(), b2.toConst()); + try Int.divTrunc(&q, &r, &a2, &b2); var mantissa = extractLowBits(q, BitReprType); var have_rem = r.len() > 0; @@ -350,8 +353,8 @@ pub const Rational = struct { var p = try Int.init(b.p.allocator); defer p.deinit(); - try q.mul(a.p.toConst(), b.q.toConst()); - try p.mul(b.p.toConst(), a.q.toConst()); + try q.mul(&a.p, &b.q); + try p.mul(&b.p, &a.q); return if (is_abs) q.orderAbs(p) else q.order(p); } @@ -376,11 +379,11 @@ pub const Rational = struct { r.deinit(); }; - try r.p.mul(a.p.toConst(), b.q.toConst()); - try r.q.mul(b.p.toConst(), a.q.toConst()); - try r.p.add(r.p.toConst(), r.q.toConst()); + try r.p.mul(&a.p, &b.q); + try r.q.mul(&b.p, &a.q); + try r.p.add(&r.p, &r.q); - try r.q.mul(a.q.toConst(), b.q.toConst()); + try r.q.mul(&a.q, &b.q); try r.reduce(); } @@ -404,11 +407,11 @@ pub const Rational = struct { r.deinit(); }; - try r.p.mul(a.p.toConst(), b.q.toConst()); - try r.q.mul(b.p.toConst(), a.q.toConst()); - try r.p.sub(r.p.toConst(), r.q.toConst()); + try r.p.mul(&a.p, &b.q); + try r.q.mul(&b.p, &a.q); + try r.p.sub(&r.p, &r.q); - try r.q.mul(a.q.toConst(), b.q.toConst()); + try r.q.mul(&a.q, &b.q); try r.reduce(); } @@ -418,8 +421,8 @@ pub const Rational = struct { /// /// Returns an error if memory could not be allocated. pub fn mul(r: *Rational, a: Rational, b: Rational) !void { - try r.p.mul(a.p.toConst(), b.p.toConst()); - try r.q.mul(a.q.toConst(), b.q.toConst()); + try r.p.mul(&a.p, &b.p); + try r.q.mul(&a.q, &b.q); try r.reduce(); } @@ -433,8 +436,8 @@ pub const Rational = struct { @panic("division by zero"); } - try r.p.mul(a.p.toConst(), b.q.toConst()); - try r.q.mul(b.p.toConst(), a.q.toConst()); + try r.p.mul(&a.p, &b.q); + try r.q.mul(&b.p, &a.q); try r.reduce(); } @@ -450,7 +453,7 @@ pub const Rational = struct { const sign = r.p.isPositive(); r.p.abs(); - try a.gcd(r.p, r.q); + try a.gcd(&r.p, &r.q); r.p.setSign(sign); const one = IntConst{ .limbs = &[_]Limb{1}, .positive = true }; @@ -460,8 +463,8 @@ pub const Rational = struct { // TODO: divexact would be useful here // TODO: don't copy r.q for div - try Int.divTrunc(&r.p, &unused, r.p.toConst(), a.toConst()); - try Int.divTrunc(&r.q, &unused, r.q.toConst(), a.toConst()); + try Int.divTrunc(&r.p, &unused, &r.p, &a); + try Int.divTrunc(&r.q, &unused, &r.q, &a); } } }; @@ -573,7 +576,6 @@ test "big.rational setFloatString" { } test "big.rational toFloat" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; var a = try Rational.init(testing.allocator); defer a.deinit(); @@ -587,7 +589,6 @@ test "big.rational toFloat" { } test "big.rational set/to Float round-trip" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; var a = try Rational.init(testing.allocator); defer a.deinit(); var prng = std.rand.DefaultPrng.init(0x5EED); diff --git a/src/RangeSet.zig b/src/RangeSet.zig index 5b4c654529..84cae34365 100644 --- a/src/RangeSet.zig +++ b/src/RangeSet.zig @@ -85,7 +85,7 @@ pub fn spans(self: *RangeSet, first: Value, last: Value, ty: Type) !bool { // prev.last + 1 == cur.first try counter.copy(prev.last.toBigInt(&space, target)); - try counter.addScalar(counter.toConst(), 1); + try counter.addScalar(&counter, 1); const cur_start_int = cur.first.toBigInt(&space, target); if (!cur_start_int.eq(counter.toConst())) { diff --git a/src/Sema.zig b/src/Sema.zig index 159574e3d5..fdf3810d4a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -22706,9 +22706,9 @@ fn cmpNumeric( else => {}, } if (lhs_is_signed) { - try bigint.addScalar(bigint.toConst(), -1); + try bigint.addScalar(&bigint, -1); } else { - try bigint.addScalar(bigint.toConst(), 1); + try bigint.addScalar(&bigint, 1); } } lhs_bits = bigint.toConst().bitCountTwosComp(); @@ -22752,9 +22752,9 @@ fn cmpNumeric( else => {}, } if (rhs_is_signed) { - try bigint.addScalar(bigint.toConst(), -1); + try bigint.addScalar(&bigint, -1); } else { - try bigint.addScalar(bigint.toConst(), 1); + try bigint.addScalar(&bigint, 1); } } rhs_bits = bigint.toConst().bitCountTwosComp(); From c248af3bdcd17c334e742d53a7ac7bda2422a688 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 29 Jun 2022 19:01:48 -0700 Subject: [PATCH 2002/2031] LLVM: fix lowering of untagged union types The LLVM backend was calculating the amount of padding solely based on the payload size. However, in the case where there is no union tag, this fails to take into account alignment. Closes #11857 --- lib/std/net/test.zig | 3 --- src/codegen/llvm.zig | 7 +++++-- test/behavior/union.zig | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index 710eb91376..5d350be7e0 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -5,7 +5,6 @@ const mem = std.mem; const testing = std.testing; test "parse and render IPv6 addresses" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; if (builtin.os.tag == .wasi) return error.SkipZigTest; var buffer: [100]u8 = undefined; @@ -68,7 +67,6 @@ test "invalid but parseable IPv6 scope ids" { } test "parse and render IPv4 addresses" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; if (builtin.os.tag == .wasi) return error.SkipZigTest; var buffer: [18]u8 = undefined; @@ -93,7 +91,6 @@ test "parse and render IPv4 addresses" { } test "parse and render UNIX addresses" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; if (builtin.os.tag == .wasi) return error.SkipZigTest; if (!net.has_unix_sockets) return error.SkipZigTest; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 2b320923ad..c62860c88d 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2708,7 +2708,10 @@ pub const DeclGen = struct { if (layout.most_aligned_field_size == layout.payload_size) { break :t llvm_aligned_field_ty; } - const padding_len = @intCast(c_uint, layout.payload_size - layout.most_aligned_field_size); + const padding_len = if (layout.tag_size == 0) + @intCast(c_uint, layout.abi_size - layout.most_aligned_field_size) + else + @intCast(c_uint, layout.payload_size - layout.most_aligned_field_size); const fields: [2]*const llvm.Type = .{ llvm_aligned_field_ty, dg.context.intType(8).arrayType(padding_len), @@ -5756,7 +5759,7 @@ pub const FuncGen = struct { // First set the non-null bit. const indices: [2]*const llvm.Value = .{ index_type.constNull(), // dereference the pointer - index_type.constInt(1, .False), // second field is the payload + index_type.constInt(1, .False), // second field is the non-null bit }; const non_null_ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""); _ = self.builder.buildStore(non_null_bit, non_null_ptr); diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 3c8874e3a8..be01b3a048 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1194,3 +1194,22 @@ test "union tag is set when initiated as a temporary value at runtime" { var b: u32 = 1; try (U{ .b = b }).doTheTest(); } + +test "extern union most-aligned field is smaller" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const U = extern union { + in6: extern struct { + family: u16, + port: u16, + flowinfo: u32, + addr: [20]u8, + }, + un: [110]u8, + }; + var a: ?U = .{ .un = [_]u8{0} ** 110 }; + try expect(a != null); +} From 6bc6e47b1582a4538078c8f4e9b3dae386854d07 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 30 Jun 2022 00:02:00 -0700 Subject: [PATCH 2003/2031] stage2: lower float negation explicitly Rather than lowering float negation as `0.0 - x`. * Add AIR instruction for float negation. * Add compiler-rt functions for f128, f80 negation closes #11853 --- CMakeLists.txt | 7 ++- lib/compiler_rt.zig | 12 ++-- lib/compiler_rt/common.zig | 12 ++++ lib/compiler_rt/negXf2.zig | 42 -------------- lib/compiler_rt/negdf2.zig | 19 +++++++ lib/compiler_rt/negsf2.zig | 19 +++++++ lib/compiler_rt/negtf2.zig | 11 ++++ lib/compiler_rt/negxf2.zig | 11 ++++ lib/std/fmt.zig | 2 - lib/std/math/copysign.zig | 1 - lib/std/math/signbit.zig | 1 - src/Air.zig | 6 ++ src/Liveness.zig | 2 + src/Sema.zig | 4 +- src/arch/aarch64/CodeGen.zig | 3 +- src/arch/arm/CodeGen.zig | 1 + src/arch/riscv64/CodeGen.zig | 1 + src/arch/sparc64/CodeGen.zig | 1 + src/arch/wasm/CodeGen.zig | 1 + src/arch/x86_64/CodeGen.zig | 1 + src/codegen/c.zig | 16 ++++++ src/codegen/llvm.zig | 14 ++++- src/codegen/llvm/bindings.zig | 3 + src/print_air.zig | 1 + test/behavior/floatop.zig | 103 ++++++++++++++++++++++++++++++++++ 25 files changed, 237 insertions(+), 57 deletions(-) delete mode 100644 lib/compiler_rt/negXf2.zig create mode 100644 lib/compiler_rt/negdf2.zig create mode 100644 lib/compiler_rt/negsf2.zig create mode 100644 lib/compiler_rt/negtf2.zig create mode 100644 lib/compiler_rt/negxf2.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 29f521c789..b28748fba4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -608,7 +608,6 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/compiler_rt/multf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/multi3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulxf3.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negXf2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negXi2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negv.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/os_version_check.zig" @@ -623,11 +622,15 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/compiler_rt/sincos.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/sqrt.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/stack_probe.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subdf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subo.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subsf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subdf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subtf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subxf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negsf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negdf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negtf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negxf2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/tan.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/trig.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/trunc.zig" diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig index 3216fcc357..105c5ed7ad 100644 --- a/lib/compiler_rt.zig +++ b/lib/compiler_rt.zig @@ -4,12 +4,13 @@ comptime { _ = @import("compiler_rt/atomics.zig"); _ = @import("compiler_rt/addf3.zig"); - _ = @import("compiler_rt/adddf3.zig"); _ = @import("compiler_rt/addsf3.zig"); + _ = @import("compiler_rt/adddf3.zig"); _ = @import("compiler_rt/addtf3.zig"); _ = @import("compiler_rt/addxf3.zig"); - _ = @import("compiler_rt/subdf3.zig"); + _ = @import("compiler_rt/subsf3.zig"); + _ = @import("compiler_rt/subdf3.zig"); _ = @import("compiler_rt/subtf3.zig"); _ = @import("compiler_rt/subxf3.zig"); @@ -19,6 +20,11 @@ comptime { _ = @import("compiler_rt/multf3.zig"); _ = @import("compiler_rt/mulxf3.zig"); + _ = @import("compiler_rt/negsf2.zig"); + _ = @import("compiler_rt/negdf2.zig"); + _ = @import("compiler_rt/negtf2.zig"); + _ = @import("compiler_rt/negxf2.zig"); + _ = @import("compiler_rt/comparef.zig"); _ = @import("compiler_rt/cmpsf2.zig"); _ = @import("compiler_rt/cmpdf2.zig"); @@ -172,8 +178,6 @@ comptime { _ = @import("compiler_rt/mulo.zig"); _ = @import("compiler_rt/cmp.zig"); - _ = @import("compiler_rt/negXf2.zig"); - _ = @import("compiler_rt/os_version_check.zig"); _ = @import("compiler_rt/emutls.zig"); _ = @import("compiler_rt/arm.zig"); diff --git a/lib/compiler_rt/common.zig b/lib/compiler_rt/common.zig index b6e4a5d311..538b237e5e 100644 --- a/lib/compiler_rt/common.zig +++ b/lib/compiler_rt/common.zig @@ -188,3 +188,15 @@ pub fn normalize(comptime T: type, significand: *std.meta.Int(.unsigned, @typeIn significand.* <<= @intCast(std.math.Log2Int(Z), shift); return @as(i32, 1) - shift; } + +pub inline fn fneg(a: anytype) @TypeOf(a) { + const F = @TypeOf(a); + const bits = @typeInfo(F).Float.bits; + const U = @Type(.{ .Int = .{ + .signedness = .unsigned, + .bits = bits, + } }); + const sign_bit_mask = @as(U, 1) << (bits - 1); + const negated = @bitCast(U, a) ^ sign_bit_mask; + return @bitCast(F, negated); +} diff --git a/lib/compiler_rt/negXf2.zig b/lib/compiler_rt/negXf2.zig deleted file mode 100644 index bcff3660f4..0000000000 --- a/lib/compiler_rt/negXf2.zig +++ /dev/null @@ -1,42 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const common = @import("common.zig"); - -pub const panic = common.panic; - -comptime { - if (common.want_aeabi) { - @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = common.linkage }); - @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = common.linkage }); - } else { - @export(__negsf2, .{ .name = "__negsf2", .linkage = common.linkage }); - @export(__negdf2, .{ .name = "__negdf2", .linkage = common.linkage }); - } -} - -pub fn __negsf2(a: f32) callconv(.C) f32 { - return negXf2(f32, a); -} - -fn __aeabi_fneg(a: f32) callconv(.AAPCS) f32 { - return negXf2(f32, a); -} - -pub fn __negdf2(a: f64) callconv(.C) f64 { - return negXf2(f64, a); -} - -fn __aeabi_dneg(a: f64) callconv(.AAPCS) f64 { - return negXf2(f64, a); -} - -inline fn negXf2(comptime T: type, a: T) T { - const Z = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); - - const significandBits = std.math.floatMantissaBits(T); - const exponentBits = std.math.floatExponentBits(T); - - const signBit = (@as(Z, 1) << (significandBits + exponentBits)); - - return @bitCast(T, @bitCast(Z, a) ^ signBit); -} diff --git a/lib/compiler_rt/negdf2.zig b/lib/compiler_rt/negdf2.zig new file mode 100644 index 0000000000..c730ada7e0 --- /dev/null +++ b/lib/compiler_rt/negdf2.zig @@ -0,0 +1,19 @@ +const common = @import("./common.zig"); + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = common.linkage }); + } else { + @export(__negdf2, .{ .name = "__negdf2", .linkage = common.linkage }); + } +} + +fn __negdf2(a: f64) callconv(.C) f64 { + return common.fneg(a); +} + +fn __aeabi_dneg(a: f64) callconv(.AAPCS) f64 { + return common.fneg(a); +} diff --git a/lib/compiler_rt/negsf2.zig b/lib/compiler_rt/negsf2.zig new file mode 100644 index 0000000000..4cb32097ba --- /dev/null +++ b/lib/compiler_rt/negsf2.zig @@ -0,0 +1,19 @@ +const common = @import("./common.zig"); + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = common.linkage }); + } else { + @export(__negsf2, .{ .name = "__negsf2", .linkage = common.linkage }); + } +} + +fn __negsf2(a: f32) callconv(.C) f32 { + return common.fneg(a); +} + +fn __aeabi_fneg(a: f32) callconv(.AAPCS) f32 { + return common.fneg(a); +} diff --git a/lib/compiler_rt/negtf2.zig b/lib/compiler_rt/negtf2.zig new file mode 100644 index 0000000000..c1c1e97802 --- /dev/null +++ b/lib/compiler_rt/negtf2.zig @@ -0,0 +1,11 @@ +const common = @import("./common.zig"); + +pub const panic = common.panic; + +comptime { + @export(__negtf2, .{ .name = "__negtf2", .linkage = common.linkage }); +} + +fn __negtf2(a: f128) callconv(.C) f128 { + return common.fneg(a); +} diff --git a/lib/compiler_rt/negxf2.zig b/lib/compiler_rt/negxf2.zig new file mode 100644 index 0000000000..4e8258453b --- /dev/null +++ b/lib/compiler_rt/negxf2.zig @@ -0,0 +1,11 @@ +const common = @import("./common.zig"); + +pub const panic = common.panic; + +comptime { + @export(__negxf2, .{ .name = "__negxf2", .linkage = common.linkage }); +} + +fn __negxf2(a: f80) callconv(.C) f80 { + return common.fneg(a); +} diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index b49a954800..fb826f4562 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -2225,7 +2225,6 @@ test "float.scientific.precision" { } test "float.special" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO try expectFmt("f64: nan", "f64: {}", .{math.nan_f64}); // negative nan is not defined by IEE 754, // and ARM thus normalizes it to positive nan @@ -2237,7 +2236,6 @@ test "float.special" { } test "float.hexadecimal.special" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO try expectFmt("f64: nan", "f64: {x}", .{math.nan_f64}); // negative nan is not defined by IEE 754, // and ARM thus normalizes it to positive nan diff --git a/lib/std/math/copysign.zig b/lib/std/math/copysign.zig index 521724a998..b5fd6d4d9a 100644 --- a/lib/std/math/copysign.zig +++ b/lib/std/math/copysign.zig @@ -13,7 +13,6 @@ pub fn copysign(magnitude: anytype, sign: @TypeOf(magnitude)) @TypeOf(magnitude) } test "math.copysign" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { try expect(copysign(@as(T, 1.0), @as(T, 1.0)) == 1.0); try expect(copysign(@as(T, 2.0), @as(T, -2.0)) == -2.0); diff --git a/lib/std/math/signbit.zig b/lib/std/math/signbit.zig index cb19212b5b..9aab487d37 100644 --- a/lib/std/math/signbit.zig +++ b/lib/std/math/signbit.zig @@ -10,7 +10,6 @@ pub fn signbit(x: anytype) bool { } test "math.signbit" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { try expect(!signbit(@as(T, 0.0))); try expect(!signbit(@as(T, 1.0))); diff --git a/src/Air.zig b/src/Air.zig index 53421b6475..2b1c718140 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -288,6 +288,11 @@ pub const Inst = struct { /// Rounds a floating pointer number to the nearest integer towards zero. /// Uses the `un_op` field. trunc_float, + /// Float negation. This affects the sign of zero, inf, and NaN, which is impossible + /// to do with sub. Integers are not allowed and must be represented with sub with + /// LHS of zero. + /// Uses the `un_op` field. + neg, /// `<`. Result type is always bool. /// Uses the `bin_op` field. @@ -970,6 +975,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .ceil, .round, .trunc_float, + .neg, => return air.typeOf(datas[inst].un_op), .cmp_lt, diff --git a/src/Liveness.zig b/src/Liveness.zig index ecb755ae0a..e0a60b50fa 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -287,6 +287,7 @@ pub fn categorizeOperand( .ceil, .round, .trunc_float, + .neg, .cmp_lt_errors_len, => { const o = air_datas[inst].un_op; @@ -834,6 +835,7 @@ fn analyzeInst( .ceil, .round, .trunc_float, + .neg, .cmp_lt_errors_len, .set_err_return_trace, => { diff --git a/src/Sema.zig b/src/Sema.zig index fdf3810d4a..a071c97df3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -10070,12 +10070,14 @@ fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. } if (rhs_scalar_ty.isAnyFloat()) { - // We handle comptime negation here to ensure negative zero is represented in the bits. + // We handle float negation here to ensure negative zero is represented in the bits. if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { if (rhs_val.isUndef()) return sema.addConstUndef(rhs_ty); const target = sema.mod.getTarget(); return sema.addConstant(rhs_ty, try rhs_val.floatNeg(rhs_ty, sema.arena, target)); } + try sema.requireRuntimeBlock(block, rhs_src); + return block.addUnOp(.neg, rhs); } const lhs = if (rhs_ty.zigTypeTag() == .Vector) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 47d7d1282f..64d49f2508 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -582,7 +582,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .floor, .ceil, .round, - .trunc_float + .trunc_float, + .neg, => try self.airUnaryMath(inst), .add_with_overflow => try self.airOverflow(inst), diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 9a93969209..b1b5c0fcb3 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -596,6 +596,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .ceil, .round, .trunc_float, + .neg, => try self.airUnaryMath(inst), .add_with_overflow => try self.airOverflow(inst), diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 94d019781e..1d4108a77e 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -516,6 +516,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .ceil, .round, .trunc_float, + .neg, => try self.airUnaryMath(inst), .add_with_overflow => try self.airAddWithOverflow(inst), diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index a28cb0002c..75260156f8 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -529,6 +529,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .ceil, .round, .trunc_float, + .neg, => @panic("TODO try self.airUnaryMath(inst)"), .add_with_overflow => try self.airAddSubWithOverflow(inst), diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index c614f7eb48..d2d21925b2 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1607,6 +1607,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .log10, .fabs, .round, + .neg, .cmpxchg_weak, .cmpxchg_strong, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5c30d495c7..8616c4ac5c 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -605,6 +605,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .ceil, .round, .trunc_float, + .neg, => try self.airUnaryMath(inst), .add_with_overflow => try self.airAddSubShlWithOverflow(inst), diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 54c0dfb3c7..15eb917fda 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1755,6 +1755,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .mul_sat => try airSatOp(f, inst, "muls_"), .shl_sat => try airSatOp(f, inst, "shls_"), + .neg => try airNeg(f, inst), + .sqrt, .sin, .cos, @@ -4098,6 +4100,20 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue { return local; } +fn airNeg(f: *Function, inst: Air.Inst.Index) !CValue { + if (f.liveness.isUnused(inst)) return CValue.none; + + const un_op = f.air.instructions.items(.data)[inst].un_op; + const writer = f.object.writer(); + const inst_ty = f.air.typeOfIndex(inst); + const operand = try f.resolveInst(un_op); + const local = try f.allocLocal(inst_ty, .Const); + try writer.writeAll("-"); + try f.writeCValue(writer, operand); + try writer.writeAll(";\n"); + return local; +} + fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; const pl_op = f.air.instructions.items(.data)[inst].pl_op; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 2b320923ad..d5096e3fe5 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4019,6 +4019,7 @@ pub const FuncGen = struct { .ceil => try self.airUnaryOp(inst, .ceil), .round => try self.airUnaryOp(inst, .round), .trunc_float => try self.airUnaryOp(inst, .trunc), + .neg => try self.airUnaryOp(inst, .neg), .cmp_eq => try self.airCmp(inst, .eq), .cmp_gt => try self.airCmp(inst, .gt), @@ -6545,13 +6546,14 @@ pub const FuncGen = struct { fabs, floor, fma, + fmax, + fmin, + fmod, log, log10, log2, - fmax, - fmin, mul, - fmod, + neg, round, sin, sqrt, @@ -6584,6 +6586,7 @@ pub const FuncGen = struct { var fn_name_buf: [64]u8 = undefined; const strat: FloatOpStrat = if (intrinsics_allowed) switch (op) { // Some operations are dedicated LLVM instructions, not available as intrinsics + .neg => return self.builder.buildFNeg(params[0], ""), .add => return self.builder.buildFAdd(params[0], params[1], ""), .sub => return self.builder.buildFSub(params[0], params[1], ""), .mul => return self.builder.buildFMul(params[0], params[1], ""), @@ -6595,6 +6598,11 @@ pub const FuncGen = struct { } else b: { const float_bits = scalar_ty.floatBits(target); break :b switch (op) { + .neg => FloatOpStrat{ + .libc = std.fmt.bufPrintZ(&fn_name_buf, "__neg{s}f2", .{ + compilerRtFloatAbbrev(float_bits), + }) catch unreachable, + }, .add, .sub, .div, .mul => FloatOpStrat{ .libc = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f3", .{ @tagName(op), compilerRtFloatAbbrev(float_bits), diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 671014ba3b..8aa8e5c6f2 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -549,6 +549,9 @@ pub const Builder = opaque { pub const buildFSub = LLVMBuildFSub; extern fn LLVMBuildFSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + pub const buildFNeg = LLVMBuildFNeg; + extern fn LLVMBuildFNeg(*const Builder, V: *const Value, Name: [*:0]const u8) *const Value; + pub const buildSub = LLVMBuildSub; extern fn LLVMBuildSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; diff --git a/src/print_air.zig b/src/print_air.zig index d6db7ca75f..a58b27fe2f 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -168,6 +168,7 @@ const Writer = struct { .ceil, .round, .trunc_float, + .neg, .cmp_lt_errors_len, .set_err_return_trace, => try w.writeUnOp(s, inst), diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index ac35834928..c057f7a842 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -574,6 +574,7 @@ test "negation f32" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -593,6 +594,8 @@ test "negation f64" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -707,3 +710,103 @@ test "comptime_float zero divided by zero produces zero" { try expect((0.0 / 0.0) == 0.0); } + +test "nan negation f16" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + + const nan_comptime = comptime math.nan(f16); + const neg_nan_comptime = -nan_comptime; + + var nan_runtime = math.nan(f16); + const neg_nan_runtime = -nan_runtime; + + try expect(!math.signbit(nan_runtime)); + try expect(math.signbit(neg_nan_runtime)); + + try expect(!math.signbit(nan_comptime)); + try expect(math.signbit(neg_nan_comptime)); +} + +test "nan negation f32" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + + const nan_comptime = comptime math.nan(f32); + const neg_nan_comptime = -nan_comptime; + + var nan_runtime = math.nan(f32); + const neg_nan_runtime = -nan_runtime; + + try expect(!math.signbit(nan_runtime)); + try expect(math.signbit(neg_nan_runtime)); + + try expect(!math.signbit(nan_comptime)); + try expect(math.signbit(neg_nan_comptime)); +} + +test "nan negation f64" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + + const nan_comptime = comptime math.nan(f64); + const neg_nan_comptime = -nan_comptime; + + var nan_runtime = math.nan(f64); + const neg_nan_runtime = -nan_runtime; + + try expect(!math.signbit(nan_runtime)); + try expect(math.signbit(neg_nan_runtime)); + + try expect(!math.signbit(nan_comptime)); + try expect(math.signbit(neg_nan_comptime)); +} + +test "nan negation f128" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + + const nan_comptime = comptime math.nan(f128); + const neg_nan_comptime = -nan_comptime; + + var nan_runtime = math.nan(f128); + const neg_nan_runtime = -nan_runtime; + + try expect(!math.signbit(nan_runtime)); + try expect(math.signbit(neg_nan_runtime)); + + try expect(!math.signbit(nan_comptime)); + try expect(math.signbit(neg_nan_comptime)); +} + +test "nan negation f80" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + + const nan_comptime = comptime math.nan(f80); + const neg_nan_comptime = -nan_comptime; + + var nan_runtime = math.nan(f80); + const neg_nan_runtime = -nan_runtime; + + try expect(!math.signbit(nan_runtime)); + try expect(math.signbit(neg_nan_runtime)); + + try expect(!math.signbit(nan_comptime)); + try expect(math.signbit(neg_nan_comptime)); +} From c8531faaf56612773ef860d341f8eebb36af4bf3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 30 Jun 2022 00:54:03 -0700 Subject: [PATCH 2004/2031] stage2: hash/eql of fixed-size floats use bit pattern Zig guarantees the memory layout of f16, f32, f64, f80, and f128 which means for generic function purposes, values of these types need to be compared on the basis of their bits in memory. This means nan-packing can be used with generic functions, for example. For comptime_float, the sign is observable, whether it is nan is observable, but not any more kinds of bit patterns are observable. This fixes the std.fmt tests that check printing "-nan". --- src/value.zig | 50 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/src/value.zig b/src/value.zig index e5332a72e7..90cdf82834 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2189,12 +2189,25 @@ pub const Value = extern union { return ty.isTupleOrAnonStruct() and ty.structFieldCount() != 0; }, .Float => { - const a_nan = a.isNan(); - const b_nan = b.isNan(); - if (a_nan or b_nan) { - return a_nan and b_nan; + switch (ty.floatBits(target)) { + 16 => return @bitCast(u16, a.toFloat(f16)) == @bitCast(u16, b.toFloat(f16)), + 32 => return @bitCast(u32, a.toFloat(f32)) == @bitCast(u32, b.toFloat(f32)), + 64 => return @bitCast(u64, a.toFloat(f64)) == @bitCast(u64, b.toFloat(f64)), + 80 => return @bitCast(u80, a.toFloat(f80)) == @bitCast(u80, b.toFloat(f80)), + 128 => return @bitCast(u128, a.toFloat(f128)) == @bitCast(u128, b.toFloat(f128)), + else => unreachable, } - return order(a, b, target).compare(.eq); + }, + .ComptimeFloat => { + const a_float = a.toFloat(f128); + const b_float = b.toFloat(f128); + + const a_nan = std.math.isNan(a_float); + const b_nan = std.math.isNan(b_float); + if (a_nan != b_nan) return false; + if (std.math.signbit(a_float) != std.math.signbit(b_float)) return false; + if (a_nan) return true; + return a_float == b_float; }, .Optional => { if (a.tag() != .opt_payload and b.tag() == .opt_payload) { @@ -2231,18 +2244,25 @@ pub const Value = extern union { var buf: ToTypeBuffer = undefined; return val.toType(&buf).hashWithHasher(hasher, mod); }, - .Float, .ComptimeFloat => { - // Normalize the float here because this hash must match eql semantics. - // These functions are used for hash maps so we want NaN to equal itself, - // and -0.0 to equal +0.0. + .Float => { + // For hash/eql purposes, we treat floats as their IEEE integer representation. + switch (ty.floatBits(mod.getTarget())) { + 16 => std.hash.autoHash(hasher, @bitCast(u16, val.toFloat(f16))), + 32 => std.hash.autoHash(hasher, @bitCast(u32, val.toFloat(f32))), + 64 => std.hash.autoHash(hasher, @bitCast(u64, val.toFloat(f64))), + 80 => std.hash.autoHash(hasher, @bitCast(u80, val.toFloat(f80))), + 128 => std.hash.autoHash(hasher, @bitCast(u128, val.toFloat(f128))), + else => unreachable, + } + }, + .ComptimeFloat => { const float = val.toFloat(f128); - if (std.math.isNan(float)) { - std.hash.autoHash(hasher, std.math.nan_u128); - } else if (float == 0.0) { - var normalized_zero: f128 = 0.0; - std.hash.autoHash(hasher, @bitCast(u128, normalized_zero)); - } else { + const is_nan = std.math.isNan(float); + std.hash.autoHash(hasher, is_nan); + if (!is_nan) { std.hash.autoHash(hasher, @bitCast(u128, float)); + } else { + std.hash.autoHash(hasher, std.math.signbit(float)); } }, .Bool, .Int, .ComptimeInt, .Pointer => switch (val.tag()) { From 3204d00a5e7fe119b690e921138a439fb84dff5b Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 27 Jun 2022 18:32:59 +0300 Subject: [PATCH 2005/2031] move passing stage1 compile error tests to stage2 --- src/AstGen.zig | 6 + src/Sema.zig | 178 +++++++++++------- src/TypedValue.zig | 10 +- .../hello_world_with_updates.1.zig | 2 +- ...nus_for_unsigned_types_a_compile_error.zig | 6 +- ...e_6823_dont_allow_._to_be_followed_by_.zig | 4 +- .../access_invalid_typeInfo_decl.zig | 4 +- ...ccess_non-existent_member_of_error_set.zig | 12 ++ .../add_assign_on_undefined_value.zig | 10 + .../compile_errors/add_on_undefined_value.zig | 10 + .../add_overflow_in_function_evaluation.zig | 5 +- .../obj => }/addition_with_non_numbers.zig | 4 +- .../address_of_number_literal.zig | 10 + .../alignCast_expects_pointer_or_slice.zig | 4 +- .../alignment_of_enum_field_specified.zig | 4 +- .../obj => }/ambiguous_decl_reference.zig | 8 +- .../any_typed_null_to_any_typed_optional.zig | 2 +- .../array_access_of_undeclared_identifier.zig | 9 + .../array_concatenation_with_wrong_type.zig | 4 +- .../{stage1/obj => }/asm_at_compile_time.zig | 4 +- .../assign_null_to_non-optional_pointer.zig | 4 +- .../assign_through_constant_pointer.zig | 4 +- .../assign_through_constant_slice.zig | 4 +- .../obj => }/assign_to_constant_field.zig | 4 +- .../obj => }/assign_to_constant_variable.zig | 4 +- .../obj => }/assign_too_big_number_to_u16.zig | 4 +- .../compile_errors/assign_unreachable.zig | 11 ++ ...rings_of_atomicStore_Acquire_or_AcqRel.zig | 4 +- ..._cmpxchg-failure_stricter_than_success.zig | 4 +- ..._cmpxchg-success_Monotonic_or_stricter.zig | 4 +- ...orderings_of_fence_Acquire_or_stricter.zig | 9 + .../atomicrmw_with_bool_op_not_.Xchg.zig | 4 +- .../atomicrmw_with_enum_op_not_.Xchg.zig | 4 +- ...w_with_float_op_not_.Xchg_.Add_or_.Sub.zig | 4 +- .../attempt_to_cast_enum_literal_to_error.zig | 4 +- ...ver_comptime_variable_from_outer_scope.zig | 14 ++ .../attempt_to_create_17_bit_float_type.zig | 4 +- .../attempted_double_ampersand.zig | 12 ++ ...ttempted_double_pipe_on_boolean_values.zig | 13 ++ ..._implicit_cast_from_T_to_slice_const_T.zig | 4 +- ...d_implicit_cast_from_const_T_to_sliceT.zig | 4 +- ..._function_which_references_local_const.zig | 4 +- test/cases/compile_errors/bad_import.zig | 7 + test/cases/compile_errors/bad_splat_type.zig | 11 ++ .../binary_OR_operator_on_error_sets.zig | 5 +- .../obj => }/binary_not_on_number_literal.zig | 4 +- ...tCast_same_size_but_bit_count_mismatch.zig | 10 + ...h_different_sizes_inside_an_expression.zig | 10 + ...t_shifting_only_works_on_integer_types.zig | 10 + .../bogus_method_call_on_slice.zig | 11 ++ .../obj => }/branch_on_undefined_value.zig | 4 +- .../obj => }/call_assigned_to_constant.zig | 6 +- ...ction_passing_array_instead_of_pointer.zig | 4 +- ...um_literal_to_enum_but_it_doesnt_match.zig | 6 +- ...cast_negative_integer_literal_to_usize.zig | 10 + ...ast_negative_value_to_unsigned_integer.zig | 6 +- .../cases/compile_errors/cast_unreachable.zig | 13 ++ ..._bit_offset_pointer_to_regular_pointer.zig | 6 +- .../obj => }/chained_comparison_operators.zig | 4 +- .../{stage1/obj => }/cmpxchg_with_float.zig | 4 +- .../colliding_invalid_top_level_functions.zig | 12 ++ .../combination_of_nosuspend_and_async.zig | 7 +- ...ng_a_non-optional_pointer_against_null.zig | 4 +- ...parison_operators_with_undefined_value.zig | 14 +- ...rison_with_error_union_and_error_value.zig | 4 +- ...traceback_of_references_that_caused_it.zig | 4 +- ...ompile_error_in_struct_init_expression.zig | 4 +- ...ting_return_type_of_inferred_error_set.zig | 4 +- ...mpile_log_a_pointer_to_an_opaque_value.zig | 4 +- ...nt_warning_deduplication_in_generic_fn.zig | 5 +- ...st_enum_to_union_but_field_has_payload.zig | 18 ++ ...lice_of_undefined_pointer_non-zero_len.zig | 4 +- .../comptime_struct_field_no_init_value.zig | 4 +- ...const_is_a_statement_not_an_expression.zig | 9 + .../obj => }/container_init_with_non-type.zig | 4 +- ...trol_flow_uses_comptime_var_at_runtime.zig | 15 ++ .../obj => }/declaration_between_fields.zig | 8 +- ...e_as_primitive_must_use_special_syntax.zig | 12 ++ .../deduplicate_undeclared_identifier.zig | 13 ++ ...licit_cast_double_pointer_to_anyopaque.zig | 4 +- .../duplicate_boolean_switch_value.zig | 6 +- .../duplicate_error_value_in_error_set.zig | 6 +- ...icate_field_in_struct_value_expression.zig | 5 +- .../obj => }/duplicate_struct_field.zig | 7 +- .../obj => }/duplicate_union_field.zig | 7 +- .../obj => }/embedFile_with_bogus_file.zig | 4 +- .../compile_errors/empty_for_loop_body.zig | 9 + test/cases/compile_errors/empty_if_body.zig | 9 + .../obj => }/empty_switch_on_an_integer.zig | 4 +- .../compile_errors/empty_while_loop_body.zig | 9 + .../endless_loop_in_function_evaluation.zig | 6 +- ...field_count_range_but_not_matching_tag.zig | 6 +- .../compile_errors/enum_with_0_fields.zig | 7 + ...eclarations_unavailable_for_reify_type.zig | 4 +- ..._initializer_doesnt_crash_the_compiler.zig | 7 +- ..._union_operator_with_non_error_set_LHS.zig | 4 +- ...rors_in_for_loop_bodies_are_propagated.zig | 5 +- .../exceeded_maximum_bit_width_of_integer.zig | 15 ++ ...ger_when_there_is_a_fraction_component.zig | 9 + ..._known_at_comptime_violates_error_sets.zig | 5 +- .../export_with_empty_name_string.zig | 5 +- .../extern_union_field_missing_type.zig | 4 +- .../obj => }/extern_variable_has_no_type.zig | 4 +- .../fieldParentPtr-bad_field_name.zig | 5 +- .../obj => }/fieldParentPtr-non_struct.zig | 4 +- .../field_type_supplied_in_an_enum.zig | 12 ++ ...nction_call_assigned_to_incorrect_type.zig | 4 +- .../function_prototype_with_no_body.zig | 4 +- .../function_with_invalid_return_type.zig | 7 + ...nction_call_assigned_to_incorrect_type.zig | 4 +- ...nitializer_must_be_constant_expression.zig | 4 +- .../hasDecl_with_non-container.zig | 9 + .../if_condition_is_bool_not_int.zig | 9 + .../ignored_assert-err-ok_return_value.zig | 4 +- .../ignored_comptime_statement_value.zig | 4 +- .../obj => }/ignored_comptime_value.zig | 4 +- .../ignored_deferred_statement_value.zig | 4 +- .../{stage1/obj => }/ignored_return_value.zig | 4 +- .../ignored_statement_value.zig | 9 + .../implicit_semicolon-block_expr.zig | 4 +- .../implicit_semicolon-block_statement.zig | 4 +- ...implicit_semicolon-comptime_expression.zig | 4 +- .../implicit_semicolon-comptime_statement.zig | 4 +- .../obj => }/implicit_semicolon-defer.zig | 4 +- .../implicit_semicolon-for_expression.zig | 4 +- .../implicit_semicolon-for_statement.zig | 4 +- ...t_semicolon-if-else-if-else_expression.zig | 4 +- ...it_semicolon-if-else-if-else_statement.zig | 4 +- ...plicit_semicolon-if-else-if_expression.zig | 4 +- ...mplicit_semicolon-if-else-if_statement.zig | 4 +- .../implicit_semicolon-if-else_expression.zig | 4 +- .../implicit_semicolon-if-else_statement.zig | 4 +- .../implicit_semicolon-if_expression.zig | 4 +- .../implicit_semicolon-if_statement.zig | 4 +- .../implicit_semicolon-test_expression.zig | 4 +- .../implicit_semicolon-test_statement.zig | 4 +- ...it_semicolon-while-continue_expression.zig | 4 +- ...cit_semicolon-while-continue_statement.zig | 4 +- .../implicit_semicolon-while_expression.zig | 4 +- .../implicit_semicolon-while_statement.zig | 4 +- .../import_outside_package_path.zig | 9 + .../inferred_array_size_invalid_here.zig | 6 +- ...nferring_error_set_of_function_pointer.zig | 9 + .../initializing_array_with_struct_syntax.zig | 4 +- ...float_conversion_to_comptime_int-float.zig | 7 +- .../int_to_err_global_invalid_number.zig | 4 +- .../int_to_err_non_global_invalid_number.zig | 5 +- .../obj => }/integer_overflow_error.zig | 4 +- .../obj => }/integer_underflow_error.zig | 4 +- .../invalid_address_space_coercion.zig | 2 +- .../compile_errors/invalid_array_elem_ty.zig | 2 +- .../{stage1/test => }/invalid_assignments.zig | 9 +- .../invalid_break_expression.zig | 9 + .../{stage1/obj => }/invalid_builtin_fn.zig | 4 +- ...valid_comparison_for_function_pointers.zig | 4 +- .../invalid_continue_expression.zig | 9 + .../obj => }/invalid_empty_unicode_escape.zig | 4 +- .../invalid_exponent_in_float_literal-1.zig | 11 ++ .../invalid_exponent_in_float_literal-2.zig | 11 ++ .../invalid_field_access_in_comptime.zig | 7 + ...valid_field_in_struct_value_expression.zig | 5 +- .../{stage1/test => }/invalid_float_casts.zig | 11 +- .../obj => }/invalid_float_literal.zig | 4 +- .../{stage1/test => }/invalid_int_casts.zig | 11 +- .../invalid_legacy_unicode_escape.zig | 10 + .../invalid_member_of_builtin_enum.zig | 5 +- .../invalid_non-exhaustive_enum_to_union.zig | 9 +- .../obj => }/invalid_pointer_for_var_type.zig | 4 +- ...ace_when_taking_address_of_dereference.zig | 2 +- .../obj => }/invalid_pointer_syntax.zig | 4 +- .../invalid_pointer_with_reify_type.zig | 5 +- .../obj => }/invalid_shift_amount_error.zig | 4 +- .../{stage1/obj => }/invalid_struct_field.zig | 8 +- .../{stage1/obj => }/invalid_type.zig | 4 +- .../invalid_type_used_in_array_type.zig | 4 +- ...nderscore_placement_in_float_literal-1.zig | 11 ++ ...derscore_placement_in_float_literal-10.zig | 11 ++ ...derscore_placement_in_float_literal-11.zig | 11 ++ ...derscore_placement_in_float_literal-12.zig | 11 ++ ...derscore_placement_in_float_literal-13.zig | 11 ++ ...derscore_placement_in_float_literal-14.zig | 11 ++ ...nderscore_placement_in_float_literal-2.zig | 11 ++ ...nderscore_placement_in_float_literal-3.zig | 11 ++ ...nderscore_placement_in_float_literal-4.zig | 11 ++ ...nderscore_placement_in_float_literal-5.zig | 11 ++ ...nderscore_placement_in_float_literal-6.zig | 11 ++ ...nderscore_placement_in_float_literal-7.zig | 11 ++ ...nderscore_placement_in_float_literal-9.zig | 11 ++ ..._underscore_placement_in_int_literal-1.zig | 11 ++ ..._underscore_placement_in_int_literal-2.zig | 11 ++ ..._underscore_placement_in_int_literal-3.zig | 11 ++ ..._underscore_placement_in_int_literal-4.zig | 11 ++ ..._9346_return_outside_of_function_scope.zig | 7 + .../obj => }/labeled_break_not_found.zig | 4 +- .../obj => }/labeled_continue_not_found.zig | 4 +- ...local_shadows_global_that_occurs_later.zig | 12 ++ .../local_variable_redeclaration.zig | 11 ++ .../local_variable_redeclares_parameter.zig | 11 ++ .../local_variable_shadowing_global.zig | 6 +- .../locally_shadowing_a_primitive_type.zig | 12 ++ .../main_function_with_bogus_args_type.zig | 7 + .../{stage1/exe => }/main_missing_name.zig | 4 +- .../obj => }/missing_boolean_switch_value.zig | 6 +- .../{stage1/obj => }/missing_else_clause.zig | 6 +- ...ssing_field_in_struct_value_expression.zig | 5 +- .../compile_errors/missing_function_name.zig | 7 + .../{stage1/obj => }/missing_param_name.zig | 4 +- ...ing_parameter_name_of_generic_function.zig | 4 +- ...elled_type_with_pointer_only_reference.zig | 4 +- .../multiple_function_definitions.zig | 10 + .../{stage1/test => }/nested_vectors.zig | 6 +- ...h_struct_return_value_outside_function.zig | 5 +- ...ion_in_struct_literal_outside_function.zig | 4 +- .../non-const_switch_number_literal.zig | 5 +- .../non-exhaustive_enum_field_non_final.zig | 11 ++ ...xhaustive_enum_marker_assigned_a_value.zig | 19 ++ ...-exhaustive_enum_specifies_every_value.zig | 14 ++ .../non-extern_function_with_var_args.zig | 4 +- .../non-pure_function_returns_type.zig | 5 +- .../non_float_passed_to_floatToInt.zig | 4 +- .../obj => }/non_int_passed_to_intToFloat.zig | 4 +- .../non_pointer_given_to_ptrToInt.zig | 4 +- .../normal_string_with_newline.zig | 9 + .../obj => }/offsetOf-bad_field_name.zig | 5 +- .../{stage1/obj => }/offsetOf-non_struct.zig | 4 +- ...binary_operator_allowed_for_error_sets.zig | 4 +- ...ange_comptime_int_passed_to_floatToInt.zig | 4 +- .../parameter_redeclaration.zig | 10 + .../obj => }/parameter_shadowing_global.zig | 6 +- ..._not-aligned-enough_pointer_to_cmpxchg.zig | 4 +- ...pointer_arithmetic_on_pointer-to-array.zig | 14 ++ .../pointer_with_different_address_spaces.zig | 2 +- ...pointers_with_different_address_spaces.zig | 2 +- .../{stage1/obj => }/popCount-non-integer.zig | 4 +- ...ives_take_precedence_over_declarations.zig | 4 +- .../ptrToInt_0_to_non_optional_pointer.zig | 4 +- .../obj => }/ptrcast_to_non-pointer.zig | 4 +- .../test => }/reassign_to_slice_parameter.zig | 5 +- .../compile_errors/redefinition_of_enums.zig | 9 + .../redefinition_of_global_variables.zig | 9 + .../compile_errors/redefinition_of_struct.zig | 9 + ...efer_to_the_type_of_a_generic_function.zig | 4 +- .../test => }/reference_to_const_data.zig | 11 +- .../referring_to_a_struct_that_is_invalid.zig | 5 +- ...ify_typeOf_with_incompatible_arguments.zig | 7 +- .../reify_typeOf_with_no_arguments.zig | 9 + ..._extern_function_definitions_with_body.zig | 5 +- ...ect_extern_variables_with_initializers.zig | 7 + ...n_returning_type_crashes_compiler_2655.zig | 4 +- .../obj => }/return_from_defer_expression.zig | 4 +- .../return_invalid_type_from_test.zig | 8 + ...ast_to_union_which_has_non-void_fields.zig | 23 +++ ...ating_arithmetic_does_not_allow_floats.zig | 9 + .../setAlignStack_in_naked_function.zig | 4 +- .../setAlignStack_outside_function.zig | 9 + .../setAlignStack_set_twice.zig | 11 ++ .../compile_errors/setAlignStack_too_big.zig | 9 + .../setting_a_section_on_a_local_variable.zig | 4 +- ...fting_RHS_is_log2_of_LHS_int_bit_width.zig | 4 +- .../obj => }/slicing_single-item_pointer.zig | 4 +- ...ccess_non-existent_member_of_error_set.zig | 11 -- .../obj/add_assign_on_undefined_value.zig | 10 - .../stage1/obj/add_on_undefined_value.zig | 10 - .../stage1/obj/address_of_number_literal.zig | 10 - .../array_access_of_undeclared_identifier.zig | 9 - .../stage1/obj/assign_unreachable.zig | 10 - ...orderings_of_fence_Acquire_or_stricter.zig | 9 - ...ver_comptime_variable_from_outer_scope.zig | 14 -- .../stage1/obj/attempted_double_ampersand.zig | 12 -- ...ttempted_double_pipe_on_boolean_values.zig | 13 -- .../compile_errors/stage1/obj/bad_import.zig | 7 - ...tCast_same_size_but_bit_count_mismatch.zig | 10 - ...h_different_sizes_inside_an_expression.zig | 10 - ...t_shifting_only_works_on_integer_types.zig | 10 - .../stage1/obj/bogus_method_call_on_slice.zig | 11 -- ...cast_negative_integer_literal_to_usize.zig | 10 - .../stage1/obj/cast_unreachable.zig | 11 -- .../colliding_invalid_top_level_functions.zig | 10 - ...st_enum_to_union_but_field_has_payload.zig | 17 -- ...const_is_a_statement_not_an_expression.zig | 9 - ...trol_flow_uses_comptime_var_at_runtime.zig | 15 -- ...e_as_primitive_must_use_special_syntax.zig | 12 -- .../obj/deduplicate_undeclared_identifier.zig | 12 -- .../stage1/obj/empty_for_loop_body.zig | 9 - .../stage1/obj/empty_if_body.zig | 9 - .../stage1/obj/empty_while_loop_body.zig | 9 - .../stage1/obj/enum_with_0_fields.zig | 7 - .../exceeded_maximum_bit_width_of_integer.zig | 15 -- ...ger_when_there_is_a_fraction_component.zig | 9 - .../obj/field_type_supplied_in_an_enum.zig | 12 -- .../obj/function_with_invalid_return_type.zig | 7 - .../stage1/obj/hasDecl_with_non-container.zig | 9 - .../obj/if_condition_is_bool_not_int.zig | 9 - .../stage1/obj/ignored_statement_value.zig | 9 - .../obj/import_outside_package_path.zig | 9 - ...nferring_error_set_of_function_pointer.zig | 9 - .../stage1/obj/invalid_break_expression.zig | 9 - .../obj/invalid_continue_expression.zig | 9 - .../invalid_exponent_in_float_literal-1.zig | 11 -- .../invalid_exponent_in_float_literal-2.zig | 11 -- .../obj/invalid_field_access_in_comptime.zig | 7 - .../obj/invalid_legacy_unicode_escape.zig | 10 - ...nderscore_placement_in_float_literal-1.zig | 11 -- ...derscore_placement_in_float_literal-10.zig | 11 -- ...derscore_placement_in_float_literal-11.zig | 11 -- ...derscore_placement_in_float_literal-12.zig | 11 -- ...derscore_placement_in_float_literal-13.zig | 11 -- ...derscore_placement_in_float_literal-14.zig | 11 -- ...nderscore_placement_in_float_literal-2.zig | 11 -- ...nderscore_placement_in_float_literal-3.zig | 11 -- ...nderscore_placement_in_float_literal-4.zig | 11 -- ...nderscore_placement_in_float_literal-5.zig | 11 -- ...nderscore_placement_in_float_literal-6.zig | 11 -- ...nderscore_placement_in_float_literal-7.zig | 11 -- ...nderscore_placement_in_float_literal-9.zig | 11 -- ..._underscore_placement_in_int_literal-1.zig | 11 -- ..._underscore_placement_in_int_literal-2.zig | 11 -- ..._underscore_placement_in_int_literal-3.zig | 11 -- ..._underscore_placement_in_int_literal-4.zig | 11 -- ...invalid_union_field_access_in_comptime.zig | 15 -- ..._9346_return_outside_of_function_scope.zig | 7 - ...local_shadows_global_that_occurs_later.zig | 12 -- .../obj/local_variable_redeclaration.zig | 11 -- .../local_variable_redeclares_parameter.zig | 11 -- .../locally_shadowing_a_primitive_type.zig | 12 -- .../main_function_with_bogus_args_type.zig | 7 - .../stage1/obj/missing_function_name.zig | 8 - .../obj/multiple_function_definitions.zig | 10 - .../stage1/obj/normal_string_with_newline.zig | 9 - .../stage1/obj/parameter_redeclaration.zig | 10 - ...pointer_arithmetic_on_pointer-to-array.zig | 12 -- ...bad_implicit_casting_of_anyframe_types.zig | 6 +- .../stage1/obj/redefinition_of_enums.zig | 9 - .../obj/redefinition_of_global_variables.zig | 9 - .../stage1/obj/redefinition_of_struct.zig | 9 - ...ast_to_union_which_has_non-void_fields.zig | 20 -- ...ating_arithmetic_does_not_allow_floats.zig | 9 - .../obj/setAlignStack_outside_function.zig | 9 - .../stage1/obj/setAlignStack_set_twice.zig | 11 -- .../stage1/obj/setAlignStack_too_big.zig | 9 - ...pression-non_exhaustive_integer_prongs.zig | 12 -- ...h_expression-unreachable_else_prong_u1.zig | 14 -- ...ch_on_enum_with_1_field_with_no_prongs.zig | 12 -- .../obj/type_variables_must_be_constant.zig | 2 +- .../undefined_as_field_type_is_rejected.zig | 13 -- .../stage1/obj/undefined_function_call.zig | 9 - .../union_fields_with_value_assignments.zig | 14 -- .../stage1/obj/union_with_0_fields.zig | 7 - .../obj/unreachable_code-nested_returns.zig | 10 - .../stage1/obj/unreachable_code.zig | 13 -- .../obj/use_of_undeclared_identifier.zig | 9 - .../obj/usingnamespace_with_wrong_type.zig | 7 - .../stage1/obj/variable_has_wrong_type.zig | 10 - .../obj/variable_with_type_noreturn.zig | 10 - .../obj/volatile_on_global_assembly.zig | 9 - .../stage1/test/bad_splat_type.zig | 12 -- ...xhaustive_enum_marker_assigned_a_value.zig | 20 -- .../stage1/test/non-exhaustive_enums.zig | 22 --- .../test/reify_typeOf_with_no_arguments.zig | 10 - ...ect_extern_variables_with_initializers.zig | 8 - .../test/return_invalid_type_from_test.zig | 8 - .../obj => }/struct_field_missing_type.zig | 4 +- .../obj => }/struct_init_syntax_for_array.zig | 4 +- ...eclarations_unavailable_for_reify_type.zig | 4 +- .../obj => }/struct_with_invalid_field.zig | 4 +- .../obj => }/suspend_inside_suspend_block.zig | 6 +- ...expression-duplicate_enumeration_prong.zig | 8 +- ...te_enumeration_prong_when_else_present.zig | 8 +- .../switch_expression-duplicate_type.zig | 6 +- ...expression-duplicate_type_struct_alias.zig | 6 +- ...h_expression-missing_enumeration_prong.zig | 8 +- ...switch_expression-multiple_else_prongs.zig | 5 +- ...pression-non_exhaustive_integer_prongs.zig | 12 ++ ...expression-unreachable_else_prong_enum.zig | 6 +- ...ession-unreachable_else_prong_range_i8.zig | 6 +- ...ession-unreachable_else_prong_range_u8.zig | 6 +- ...h_expression-unreachable_else_prong_u1.zig | 14 ++ ...h_expression-unreachable_else_prong_u2.zig | 6 +- ...ch_on_enum_with_1_field_with_no_prongs.zig | 14 ++ ...hing_with_exhaustive_enum_has___prong_.zig | 6 +- ...n_invalid_value_of_non-exhaustive_enum.zig | 5 +- .../threadlocal_qualifier_on_const.zig | 4 +- .../obj => }/truncate_sign_mismatch.zig | 10 +- ...in_function_with_non_error_return_type.zig | 4 +- ...e_mismatch_in_C_prototype_with_varargs.zig | 5 +- .../obj => }/undeclared_identifier.zig | 4 +- ...ntifier_error_should_mark_fn_as_impure.zig | 4 +- ...clared_identifier_in_unanalyzed_branch.zig | 4 +- .../undefined_as_field_type_is_rejected.zig | 9 + .../undefined_function_call.zig | 9 + .../underscore_is_not_a_declarable_symbol.zig | 4 +- ...rscore_should_not_be_usable_inside_for.zig | 4 +- ...core_should_not_be_usable_inside_while.zig | 4 +- ...should_not_be_usable_inside_while_else.zig | 4 +- .../union_fields_with_value_assignments.zig | 7 + .../compile_errors/union_with_0_fields.zig | 7 + .../union_with_specified_enum_omits_field.zig | 7 +- .../unreachable_code-double_break.zig | 7 +- .../unreachable_code-nested_returns.zig | 10 + .../cases/compile_errors/unreachable_code.zig | 13 ++ .../{stage1/obj => }/unreachable_variable.zig | 4 +- .../obj => }/unreachable_with_return.zig | 4 +- ..._invalid_number_literal_as_array_index.zig | 4 +- .../use_of_undeclared_identifier.zig | 9 + ...types_in_function_call_raises_an_error.zig | 4 +- .../usingnamespace_with_wrong_type.zig | 7 + .../variable_has_wrong_type.zig | 10 + ...lization_compile_error_then_referenced.zig | 4 +- .../variable_with_type_noreturn.zig | 11 ++ .../volatile_on_global_assembly.zig | 9 + .../while_expected_bool_got_error_union.zig | 4 +- .../while_expected_bool_got_optional.zig | 4 +- .../write_to_const_global_variable.zig | 4 +- .../wrong_size_to_an_array_literal.zig | 4 +- .../obj => }/wrong_type_passed_to_panic.zig | 4 +- .../obj => }/wrong_type_to_hasField.zig | 4 +- test/cases/compile_log.0.zig | 2 +- .../hello_world_with_updates.1.zig | 2 +- test/cases/x86_64-linux/inline_assembly.0.zig | 2 +- .../hello_world_with_updates.1.zig | 2 +- test/stage2/cbe.zig | 10 +- 421 files changed, 1686 insertions(+), 1636 deletions(-) rename test/cases/compile_errors/{stage1/obj => }/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig (64%) rename test/cases/compile_errors/{stage1/obj => }/Issue_6823_dont_allow_._to_be_followed_by_.zig (50%) rename test/cases/compile_errors/{stage1/test => }/access_invalid_typeInfo_decl.zig (60%) create mode 100644 test/cases/compile_errors/access_non-existent_member_of_error_set.zig create mode 100644 test/cases/compile_errors/add_assign_on_undefined_value.zig create mode 100644 test/cases/compile_errors/add_on_undefined_value.zig rename test/cases/compile_errors/{stage1/obj => }/add_overflow_in_function_evaluation.zig (57%) rename test/cases/compile_errors/{stage1/obj => }/addition_with_non_numbers.zig (64%) create mode 100644 test/cases/compile_errors/address_of_number_literal.zig rename test/cases/compile_errors/{stage1/obj => }/alignCast_expects_pointer_or_slice.zig (52%) rename test/cases/compile_errors/{stage1/obj => }/alignment_of_enum_field_specified.zig (69%) rename test/cases/compile_errors/{stage1/obj => }/ambiguous_decl_reference.zig (61%) rename test/cases/{llvm => compile_errors}/any_typed_null_to_any_typed_optional.zig (74%) create mode 100644 test/cases/compile_errors/array_access_of_undeclared_identifier.zig rename test/cases/compile_errors/{stage1/obj => }/array_concatenation_with_wrong_type.zig (68%) rename test/cases/compile_errors/{stage1/obj => }/asm_at_compile_time.zig (70%) rename test/cases/compile_errors/{stage1/obj => }/assign_null_to_non-optional_pointer.zig (56%) rename test/cases/compile_errors/{stage1/obj => }/assign_through_constant_pointer.zig (56%) rename test/cases/compile_errors/{stage1/obj => }/assign_through_constant_slice.zig (60%) rename test/cases/compile_errors/{stage1/obj => }/assign_to_constant_field.zig (68%) rename test/cases/compile_errors/{stage1/obj => }/assign_to_constant_variable.zig (54%) rename test/cases/compile_errors/{stage1/obj => }/assign_too_big_number_to_u16.zig (52%) create mode 100644 test/cases/compile_errors/assign_unreachable.zig rename test/cases/compile_errors/{stage1/obj => }/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig (53%) rename test/cases/compile_errors/{stage1/obj => }/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig (69%) rename test/cases/compile_errors/{stage1/obj => }/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig (70%) create mode 100644 test/cases/compile_errors/atomic_orderings_of_fence_Acquire_or_stricter.zig rename test/cases/compile_errors/{stage1/obj => }/atomicrmw_with_bool_op_not_.Xchg.zig (59%) rename test/cases/compile_errors/{stage1/obj => }/atomicrmw_with_enum_op_not_.Xchg.zig (69%) rename test/cases/compile_errors/{stage1/obj => }/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig (54%) rename test/cases/compile_errors/{stage1/obj => }/attempt_to_cast_enum_literal_to_error.zig (52%) create mode 100644 test/cases/compile_errors/attempt_to_close_over_comptime_variable_from_outer_scope.zig rename test/cases/compile_errors/{stage1/obj => }/attempt_to_create_17_bit_float_type.zig (65%) create mode 100644 test/cases/compile_errors/attempted_double_ampersand.zig create mode 100644 test/cases/compile_errors/attempted_double_pipe_on_boolean_values.zig rename test/cases/compile_errors/{stage1/obj => }/attempted_implicit_cast_from_T_to_slice_const_T.zig (54%) rename test/cases/compile_errors/{stage1/obj => }/attempted_implicit_cast_from_const_T_to_sliceT.zig (58%) rename test/cases/compile_errors/{stage1/obj => }/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig (68%) create mode 100644 test/cases/compile_errors/bad_import.zig create mode 100644 test/cases/compile_errors/bad_splat_type.zig rename test/cases/compile_errors/{stage1/test => }/binary_OR_operator_on_error_sets.zig (54%) rename test/cases/compile_errors/{stage1/obj => }/binary_not_on_number_literal.zig (72%) create mode 100644 test/cases/compile_errors/bitCast_same_size_but_bit_count_mismatch.zig create mode 100644 test/cases/compile_errors/bitCast_with_different_sizes_inside_an_expression.zig create mode 100644 test/cases/compile_errors/bit_shifting_only_works_on_integer_types.zig create mode 100644 test/cases/compile_errors/bogus_method_call_on_slice.zig rename test/cases/compile_errors/{stage1/obj => }/branch_on_undefined_value.zig (57%) rename test/cases/compile_errors/{stage1/obj => }/call_assigned_to_constant.zig (72%) rename test/cases/compile_errors/{stage1/obj => }/calling_var_args_extern_function_passing_array_instead_of_pointer.zig (59%) rename test/cases/compile_errors/{stage1/obj => }/cast_enum_literal_to_enum_but_it_doesnt_match.zig (51%) create mode 100644 test/cases/compile_errors/cast_negative_integer_literal_to_usize.zig rename test/cases/compile_errors/{stage1/obj => }/cast_negative_value_to_unsigned_integer.zig (57%) create mode 100644 test/cases/compile_errors/cast_unreachable.zig rename test/cases/compile_errors/{stage1/obj => }/casting_bit_offset_pointer_to_regular_pointer.zig (58%) rename test/cases/compile_errors/{stage1/obj => }/chained_comparison_operators.zig (53%) rename test/cases/compile_errors/{stage1/obj => }/cmpxchg_with_float.zig (56%) create mode 100644 test/cases/compile_errors/colliding_invalid_top_level_functions.zig rename test/cases/compile_errors/{stage1/test => }/combination_of_nosuspend_and_async.zig (56%) rename test/cases/compile_errors/{stage1/obj => }/comparing_a_non-optional_pointer_against_null.zig (57%) rename test/cases/compile_errors/{stage1/obj => }/comparison_operators_with_undefined_value.zig (56%) rename test/cases/compile_errors/{stage1/obj => }/comparison_with_error_union_and_error_value.zig (65%) rename test/cases/compile_errors/{stage1/obj => }/compileError_shows_traceback_of_references_that_caused_it.zig (75%) rename test/cases/compile_errors/{stage1/obj => }/compile_error_in_struct_init_expression.zig (67%) rename test/cases/compile_errors/{stage1/obj => }/compile_error_when_evaluating_return_type_of_inferred_error_set.zig (65%) rename test/cases/compile_errors/{stage1/obj => }/compile_log_a_pointer_to_an_opaque_value.zig (61%) rename test/cases/compile_errors/{stage1/obj => }/compile_log_statement_warning_deduplication_in_generic_fn.zig (69%) create mode 100644 test/cases/compile_errors/comptime_cast_enum_to_union_but_field_has_payload.zig rename test/cases/compile_errors/{stage1/obj => }/comptime_slice_of_undefined_pointer_non-zero_len.zig (58%) rename test/cases/compile_errors/{stage1/obj => }/comptime_struct_field_no_init_value.zig (60%) create mode 100644 test/cases/compile_errors/const_is_a_statement_not_an_expression.zig rename test/cases/compile_errors/{stage1/obj => }/container_init_with_non-type.zig (62%) create mode 100644 test/cases/compile_errors/control_flow_uses_comptime_var_at_runtime.zig rename test/cases/compile_errors/{stage1/obj => }/declaration_between_fields.zig (56%) create mode 100644 test/cases/compile_errors/declaration_with_same_name_as_primitive_must_use_special_syntax.zig create mode 100644 test/cases/compile_errors/deduplicate_undeclared_identifier.zig rename test/cases/compile_errors/{stage1/obj => }/dont_implicit_cast_double_pointer_to_anyopaque.zig (71%) rename test/cases/compile_errors/{stage1/obj => }/duplicate_boolean_switch_value.zig (72%) rename test/cases/compile_errors/{stage1/obj => }/duplicate_error_value_in_error_set.zig (53%) rename test/cases/compile_errors/{stage1/obj => }/duplicate_field_in_struct_value_expression.zig (71%) rename test/cases/compile_errors/{stage1/obj => }/duplicate_struct_field.zig (53%) rename test/cases/compile_errors/{stage1/obj => }/duplicate_union_field.zig (53%) rename test/cases/compile_errors/{stage1/obj => }/embedFile_with_bogus_file.zig (64%) create mode 100644 test/cases/compile_errors/empty_for_loop_body.zig create mode 100644 test/cases/compile_errors/empty_if_body.zig rename test/cases/compile_errors/{stage1/obj => }/empty_switch_on_an_integer.zig (54%) create mode 100644 test/cases/compile_errors/empty_while_loop_body.zig rename test/cases/compile_errors/{stage1/obj => }/endless_loop_in_function_evaluation.zig (58%) rename test/cases/compile_errors/{stage1/obj => }/enum_in_field_count_range_but_not_matching_tag.zig (53%) create mode 100644 test/cases/compile_errors/enum_with_0_fields.zig rename test/cases/compile_errors/{stage1/obj => }/enum_with_declarations_unavailable_for_reify_type.zig (57%) rename test/cases/compile_errors/{stage1/test => }/error_in_struct_initializer_doesnt_crash_the_compiler.zig (56%) rename test/cases/compile_errors/{stage1/obj => }/error_union_operator_with_non_error_set_LHS.zig (55%) rename test/cases/compile_errors/{stage1/test => }/errors_in_for_loop_bodies_are_propagated.zig (61%) create mode 100644 test/cases/compile_errors/exceeded_maximum_bit_width_of_integer.zig create mode 100644 test/cases/compile_errors/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig rename test/cases/compile_errors/{stage1/obj => }/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig (56%) rename test/cases/compile_errors/{stage1/test => }/export_with_empty_name_string.zig (54%) rename test/cases/compile_errors/{stage1/obj => }/extern_union_field_missing_type.zig (67%) rename test/cases/compile_errors/{stage1/obj => }/extern_variable_has_no_type.zig (54%) rename test/cases/compile_errors/{stage1/obj => }/fieldParentPtr-bad_field_name.zig (57%) rename test/cases/compile_errors/{stage1/obj => }/fieldParentPtr-non_struct.zig (61%) create mode 100644 test/cases/compile_errors/field_type_supplied_in_an_enum.zig rename test/cases/compile_errors/{stage1/obj => }/function_call_assigned_to_incorrect_type.zig (66%) rename test/cases/compile_errors/{stage1/obj => }/function_prototype_with_no_body.zig (53%) create mode 100644 test/cases/compile_errors/function_with_invalid_return_type.zig rename test/cases/compile_errors/{stage1/obj => }/generic_function_call_assigned_to_incorrect_type.zig (68%) rename test/cases/compile_errors/{stage1/obj => }/global_variable_initializer_must_be_constant_expression.zig (56%) create mode 100644 test/cases/compile_errors/hasDecl_with_non-container.zig create mode 100644 test/cases/compile_errors/if_condition_is_bool_not_int.zig rename test/cases/compile_errors/{stage1/obj => }/ignored_assert-err-ok_return_value.zig (63%) rename test/cases/compile_errors/{stage1/obj => }/ignored_comptime_statement_value.zig (51%) rename test/cases/compile_errors/{stage1/obj => }/ignored_comptime_value.zig (50%) rename test/cases/compile_errors/{stage1/obj => }/ignored_deferred_statement_value.zig (50%) rename test/cases/compile_errors/{stage1/obj => }/ignored_return_value.zig (57%) create mode 100644 test/cases/compile_errors/ignored_statement_value.zig rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-block_expr.zig (62%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-block_statement.zig (61%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-comptime_expression.zig (65%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-comptime_statement.zig (64%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-defer.zig (63%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-for_expression.zig (67%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-for_statement.zig (63%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-if-else-if-else_expression.zig (72%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-if-else-if-else_statement.zig (71%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-if-else-if_expression.zig (70%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-if-else-if_statement.zig (66%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-if-else_expression.zig (68%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-if-else_statement.zig (67%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-if_expression.zig (65%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-if_statement.zig (61%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-test_expression.zig (67%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-test_statement.zig (63%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-while-continue_expression.zig (68%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-while-continue_statement.zig (64%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-while_expression.zig (66%) rename test/cases/compile_errors/{stage1/obj => }/implicit_semicolon-while_statement.zig (61%) create mode 100644 test/cases/compile_errors/import_outside_package_path.zig rename test/cases/compile_errors/{stage1/obj => }/inferred_array_size_invalid_here.zig (61%) create mode 100644 test/cases/compile_errors/inferring_error_set_of_function_pointer.zig rename test/cases/compile_errors/{stage1/obj => }/initializing_array_with_struct_syntax.zig (55%) rename test/cases/compile_errors/{stage1/test => }/int-float_conversion_to_comptime_int-float.zig (55%) rename test/cases/compile_errors/{stage1/obj => }/int_to_err_global_invalid_number.zig (64%) rename test/cases/compile_errors/{stage1/obj => }/int_to_err_non_global_invalid_number.zig (62%) rename test/cases/compile_errors/{stage1/obj => }/integer_overflow_error.zig (53%) rename test/cases/compile_errors/{stage1/obj => }/integer_underflow_error.zig (62%) rename test/cases/{llvm => compile_errors}/invalid_address_space_coercion.zig (73%) rename test/cases/compile_errors/{stage1/test => }/invalid_assignments.zig (50%) create mode 100644 test/cases/compile_errors/invalid_break_expression.zig rename test/cases/compile_errors/{stage1/obj => }/invalid_builtin_fn.zig (53%) rename test/cases/compile_errors/{stage1/obj => }/invalid_comparison_for_function_pointers.zig (61%) create mode 100644 test/cases/compile_errors/invalid_continue_expression.zig rename test/cases/compile_errors/{stage1/obj => }/invalid_empty_unicode_escape.zig (52%) create mode 100644 test/cases/compile_errors/invalid_exponent_in_float_literal-1.zig create mode 100644 test/cases/compile_errors/invalid_exponent_in_float_literal-2.zig create mode 100644 test/cases/compile_errors/invalid_field_access_in_comptime.zig rename test/cases/compile_errors/{stage1/obj => }/invalid_field_in_struct_value_expression.zig (64%) rename test/cases/compile_errors/{stage1/test => }/invalid_float_casts.zig (55%) rename test/cases/compile_errors/{stage1/obj => }/invalid_float_literal.zig (71%) rename test/cases/compile_errors/{stage1/test => }/invalid_int_casts.zig (55%) create mode 100644 test/cases/compile_errors/invalid_legacy_unicode_escape.zig rename test/cases/compile_errors/{stage1/obj => }/invalid_member_of_builtin_enum.zig (55%) rename test/cases/compile_errors/{stage1/test => }/invalid_non-exhaustive_enum_to_union.zig (55%) rename test/cases/compile_errors/{stage1/obj => }/invalid_pointer_for_var_type.zig (67%) rename test/cases/{llvm => compile_errors}/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig (73%) rename test/cases/compile_errors/{stage1/obj => }/invalid_pointer_syntax.zig (54%) rename test/cases/compile_errors/{stage1/test => }/invalid_pointer_with_reify_type.zig (72%) rename test/cases/compile_errors/{stage1/obj => }/invalid_shift_amount_error.zig (57%) rename test/cases/compile_errors/{stage1/obj => }/invalid_struct_field.zig (54%) rename test/cases/compile_errors/{stage1/obj => }/invalid_type.zig (51%) rename test/cases/compile_errors/{stage1/obj => }/invalid_type_used_in_array_type.zig (66%) create mode 100644 test/cases/compile_errors/invalid_underscore_placement_in_float_literal-1.zig create mode 100644 test/cases/compile_errors/invalid_underscore_placement_in_float_literal-10.zig create mode 100644 test/cases/compile_errors/invalid_underscore_placement_in_float_literal-11.zig create mode 100644 test/cases/compile_errors/invalid_underscore_placement_in_float_literal-12.zig create mode 100644 test/cases/compile_errors/invalid_underscore_placement_in_float_literal-13.zig create mode 100644 test/cases/compile_errors/invalid_underscore_placement_in_float_literal-14.zig create mode 100644 test/cases/compile_errors/invalid_underscore_placement_in_float_literal-2.zig create mode 100644 test/cases/compile_errors/invalid_underscore_placement_in_float_literal-3.zig create mode 100644 test/cases/compile_errors/invalid_underscore_placement_in_float_literal-4.zig create mode 100644 test/cases/compile_errors/invalid_underscore_placement_in_float_literal-5.zig create mode 100644 test/cases/compile_errors/invalid_underscore_placement_in_float_literal-6.zig create mode 100644 test/cases/compile_errors/invalid_underscore_placement_in_float_literal-7.zig create mode 100644 test/cases/compile_errors/invalid_underscore_placement_in_float_literal-9.zig create mode 100644 test/cases/compile_errors/invalid_underscore_placement_in_int_literal-1.zig create mode 100644 test/cases/compile_errors/invalid_underscore_placement_in_int_literal-2.zig create mode 100644 test/cases/compile_errors/invalid_underscore_placement_in_int_literal-3.zig create mode 100644 test/cases/compile_errors/invalid_underscore_placement_in_int_literal-4.zig create mode 100644 test/cases/compile_errors/issue_9346_return_outside_of_function_scope.zig rename test/cases/compile_errors/{stage1/obj => }/labeled_break_not_found.zig (68%) rename test/cases/compile_errors/{stage1/obj => }/labeled_continue_not_found.zig (73%) create mode 100644 test/cases/compile_errors/local_shadows_global_that_occurs_later.zig create mode 100644 test/cases/compile_errors/local_variable_redeclaration.zig create mode 100644 test/cases/compile_errors/local_variable_redeclares_parameter.zig rename test/cases/compile_errors/{stage1/obj => }/local_variable_shadowing_global.zig (56%) create mode 100644 test/cases/compile_errors/locally_shadowing_a_primitive_type.zig create mode 100644 test/cases/compile_errors/main_function_with_bogus_args_type.zig rename test/cases/compile_errors/{stage1/exe => }/main_missing_name.zig (52%) rename test/cases/compile_errors/{stage1/obj => }/missing_boolean_switch_value.zig (58%) rename test/cases/compile_errors/{stage1/obj => }/missing_else_clause.zig (63%) rename test/cases/compile_errors/{stage1/obj => }/missing_field_in_struct_value_expression.zig (73%) create mode 100644 test/cases/compile_errors/missing_function_name.zig rename test/cases/compile_errors/{stage1/obj => }/missing_param_name.zig (61%) rename test/cases/compile_errors/{stage1/obj => }/missing_parameter_name_of_generic_function.zig (64%) rename test/cases/compile_errors/{stage1/obj => }/misspelled_type_with_pointer_only_reference.zig (88%) create mode 100644 test/cases/compile_errors/multiple_function_definitions.zig rename test/cases/compile_errors/{stage1/test => }/nested_vectors.zig (59%) rename test/cases/compile_errors/{stage1/obj => }/non-const_expression_function_call_with_struct_return_value_outside_function.zig (71%) rename test/cases/compile_errors/{stage1/obj => }/non-const_expression_in_struct_literal_outside_function.zig (68%) rename test/cases/compile_errors/{stage1/obj => }/non-const_switch_number_literal.zig (55%) create mode 100644 test/cases/compile_errors/non-exhaustive_enum_field_non_final.zig create mode 100644 test/cases/compile_errors/non-exhaustive_enum_marker_assigned_a_value.zig create mode 100644 test/cases/compile_errors/non-exhaustive_enum_specifies_every_value.zig rename test/cases/compile_errors/{stage1/obj => }/non-extern_function_with_var_args.zig (54%) rename test/cases/compile_errors/{stage1/obj => }/non-pure_function_returns_type.zig (80%) rename test/cases/compile_errors/{stage1/obj => }/non_float_passed_to_floatToInt.zig (60%) rename test/cases/compile_errors/{stage1/obj => }/non_int_passed_to_intToFloat.zig (55%) rename test/cases/compile_errors/{stage1/obj => }/non_pointer_given_to_ptrToInt.zig (55%) create mode 100644 test/cases/compile_errors/normal_string_with_newline.zig rename test/cases/compile_errors/{stage1/obj => }/offsetOf-bad_field_name.zig (54%) rename test/cases/compile_errors/{stage1/obj => }/offsetOf-non_struct.zig (58%) rename test/cases/compile_errors/{stage1/obj => }/only_equality_binary_operator_allowed_for_error_sets.zig (52%) rename test/cases/compile_errors/{stage1/obj => }/out_of_range_comptime_int_passed_to_floatToInt.zig (57%) create mode 100644 test/cases/compile_errors/parameter_redeclaration.zig rename test/cases/compile_errors/{stage1/obj => }/parameter_shadowing_global.zig (50%) rename test/cases/compile_errors/{stage1/obj => }/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig (74%) create mode 100644 test/cases/compile_errors/pointer_arithmetic_on_pointer-to-array.zig rename test/cases/{llvm => compile_errors}/pointer_with_different_address_spaces.zig (71%) rename test/cases/{llvm => compile_errors}/pointers_with_different_address_spaces.zig (73%) rename test/cases/compile_errors/{stage1/obj => }/popCount-non-integer.zig (54%) rename test/cases/compile_errors/{stage1/obj => }/primitives_take_precedence_over_declarations.zig (54%) rename test/cases/compile_errors/{stage1/obj => }/ptrToInt_0_to_non_optional_pointer.zig (52%) rename test/cases/compile_errors/{stage1/obj => }/ptrcast_to_non-pointer.zig (56%) rename test/cases/compile_errors/{stage1/test => }/reassign_to_slice_parameter.zig (62%) create mode 100644 test/cases/compile_errors/redefinition_of_enums.zig create mode 100644 test/cases/compile_errors/redefinition_of_global_variables.zig create mode 100644 test/cases/compile_errors/redefinition_of_struct.zig rename test/cases/compile_errors/{stage1/obj => }/refer_to_the_type_of_a_generic_function.zig (58%) rename test/cases/compile_errors/{stage1/test => }/reference_to_const_data.zig (61%) rename test/cases/compile_errors/{stage1/obj => }/referring_to_a_struct_that_is_invalid.zig (69%) rename test/cases/compile_errors/{stage1/test => }/reify_typeOf_with_incompatible_arguments.zig (53%) create mode 100644 test/cases/compile_errors/reify_typeOf_with_no_arguments.zig rename test/cases/compile_errors/{stage1/test => }/reject_extern_function_definitions_with_body.zig (55%) create mode 100644 test/cases/compile_errors/reject_extern_variables_with_initializers.zig rename test/cases/compile_errors/{stage1/test => }/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig (61%) rename test/cases/compile_errors/{stage1/obj => }/return_from_defer_expression.zig (78%) create mode 100644 test/cases/compile_errors/return_invalid_type_from_test.zig create mode 100644 test/cases/compile_errors/runtime_cast_to_union_which_has_non-void_fields.zig create mode 100644 test/cases/compile_errors/saturating_arithmetic_does_not_allow_floats.zig rename test/cases/compile_errors/{stage1/obj => }/setAlignStack_in_naked_function.zig (56%) create mode 100644 test/cases/compile_errors/setAlignStack_outside_function.zig create mode 100644 test/cases/compile_errors/setAlignStack_set_twice.zig create mode 100644 test/cases/compile_errors/setAlignStack_too_big.zig rename test/cases/compile_errors/{stage1/obj => }/setting_a_section_on_a_local_variable.zig (58%) rename test/cases/compile_errors/{stage1/obj => }/shifting_RHS_is_log2_of_LHS_int_bit_width.zig (54%) rename test/cases/compile_errors/{stage1/obj => }/slicing_single-item_pointer.zig (60%) delete mode 100644 test/cases/compile_errors/stage1/obj/access_non-existent_member_of_error_set.zig delete mode 100644 test/cases/compile_errors/stage1/obj/add_assign_on_undefined_value.zig delete mode 100644 test/cases/compile_errors/stage1/obj/add_on_undefined_value.zig delete mode 100644 test/cases/compile_errors/stage1/obj/address_of_number_literal.zig delete mode 100644 test/cases/compile_errors/stage1/obj/array_access_of_undeclared_identifier.zig delete mode 100644 test/cases/compile_errors/stage1/obj/assign_unreachable.zig delete mode 100644 test/cases/compile_errors/stage1/obj/atomic_orderings_of_fence_Acquire_or_stricter.zig delete mode 100644 test/cases/compile_errors/stage1/obj/attempt_to_close_over_comptime_variable_from_outer_scope.zig delete mode 100644 test/cases/compile_errors/stage1/obj/attempted_double_ampersand.zig delete mode 100644 test/cases/compile_errors/stage1/obj/attempted_double_pipe_on_boolean_values.zig delete mode 100644 test/cases/compile_errors/stage1/obj/bad_import.zig delete mode 100644 test/cases/compile_errors/stage1/obj/bitCast_same_size_but_bit_count_mismatch.zig delete mode 100644 test/cases/compile_errors/stage1/obj/bitCast_with_different_sizes_inside_an_expression.zig delete mode 100644 test/cases/compile_errors/stage1/obj/bit_shifting_only_works_on_integer_types.zig delete mode 100644 test/cases/compile_errors/stage1/obj/bogus_method_call_on_slice.zig delete mode 100644 test/cases/compile_errors/stage1/obj/cast_negative_integer_literal_to_usize.zig delete mode 100644 test/cases/compile_errors/stage1/obj/cast_unreachable.zig delete mode 100644 test/cases/compile_errors/stage1/obj/colliding_invalid_top_level_functions.zig delete mode 100644 test/cases/compile_errors/stage1/obj/comptime_cast_enum_to_union_but_field_has_payload.zig delete mode 100644 test/cases/compile_errors/stage1/obj/const_is_a_statement_not_an_expression.zig delete mode 100644 test/cases/compile_errors/stage1/obj/control_flow_uses_comptime_var_at_runtime.zig delete mode 100644 test/cases/compile_errors/stage1/obj/declaration_with_same_name_as_primitive_must_use_special_syntax.zig delete mode 100644 test/cases/compile_errors/stage1/obj/deduplicate_undeclared_identifier.zig delete mode 100644 test/cases/compile_errors/stage1/obj/empty_for_loop_body.zig delete mode 100644 test/cases/compile_errors/stage1/obj/empty_if_body.zig delete mode 100644 test/cases/compile_errors/stage1/obj/empty_while_loop_body.zig delete mode 100644 test/cases/compile_errors/stage1/obj/enum_with_0_fields.zig delete mode 100644 test/cases/compile_errors/stage1/obj/exceeded_maximum_bit_width_of_integer.zig delete mode 100644 test/cases/compile_errors/stage1/obj/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig delete mode 100644 test/cases/compile_errors/stage1/obj/field_type_supplied_in_an_enum.zig delete mode 100644 test/cases/compile_errors/stage1/obj/function_with_invalid_return_type.zig delete mode 100644 test/cases/compile_errors/stage1/obj/hasDecl_with_non-container.zig delete mode 100644 test/cases/compile_errors/stage1/obj/if_condition_is_bool_not_int.zig delete mode 100644 test/cases/compile_errors/stage1/obj/ignored_statement_value.zig delete mode 100644 test/cases/compile_errors/stage1/obj/import_outside_package_path.zig delete mode 100644 test/cases/compile_errors/stage1/obj/inferring_error_set_of_function_pointer.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_break_expression.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_continue_expression.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_exponent_in_float_literal-1.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_exponent_in_float_literal-2.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_field_access_in_comptime.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_legacy_unicode_escape.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-1.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-10.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-11.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-12.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-13.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-14.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-2.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-3.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-4.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-5.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-6.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-7.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-9.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-1.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-2.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-3.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-4.zig delete mode 100644 test/cases/compile_errors/stage1/obj/invalid_union_field_access_in_comptime.zig delete mode 100644 test/cases/compile_errors/stage1/obj/issue_9346_return_outside_of_function_scope.zig delete mode 100644 test/cases/compile_errors/stage1/obj/local_shadows_global_that_occurs_later.zig delete mode 100644 test/cases/compile_errors/stage1/obj/local_variable_redeclaration.zig delete mode 100644 test/cases/compile_errors/stage1/obj/local_variable_redeclares_parameter.zig delete mode 100644 test/cases/compile_errors/stage1/obj/locally_shadowing_a_primitive_type.zig delete mode 100644 test/cases/compile_errors/stage1/obj/main_function_with_bogus_args_type.zig delete mode 100644 test/cases/compile_errors/stage1/obj/missing_function_name.zig delete mode 100644 test/cases/compile_errors/stage1/obj/multiple_function_definitions.zig delete mode 100644 test/cases/compile_errors/stage1/obj/normal_string_with_newline.zig delete mode 100644 test/cases/compile_errors/stage1/obj/parameter_redeclaration.zig delete mode 100644 test/cases/compile_errors/stage1/obj/pointer_arithmetic_on_pointer-to-array.zig delete mode 100644 test/cases/compile_errors/stage1/obj/redefinition_of_enums.zig delete mode 100644 test/cases/compile_errors/stage1/obj/redefinition_of_global_variables.zig delete mode 100644 test/cases/compile_errors/stage1/obj/redefinition_of_struct.zig delete mode 100644 test/cases/compile_errors/stage1/obj/runtime_cast_to_union_which_has_non-void_fields.zig delete mode 100644 test/cases/compile_errors/stage1/obj/saturating_arithmetic_does_not_allow_floats.zig delete mode 100644 test/cases/compile_errors/stage1/obj/setAlignStack_outside_function.zig delete mode 100644 test/cases/compile_errors/stage1/obj/setAlignStack_set_twice.zig delete mode 100644 test/cases/compile_errors/stage1/obj/setAlignStack_too_big.zig delete mode 100644 test/cases/compile_errors/stage1/obj/switch_expression-non_exhaustive_integer_prongs.zig delete mode 100644 test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u1.zig delete mode 100644 test/cases/compile_errors/stage1/obj/switch_on_enum_with_1_field_with_no_prongs.zig delete mode 100644 test/cases/compile_errors/stage1/obj/undefined_as_field_type_is_rejected.zig delete mode 100644 test/cases/compile_errors/stage1/obj/undefined_function_call.zig delete mode 100644 test/cases/compile_errors/stage1/obj/union_fields_with_value_assignments.zig delete mode 100644 test/cases/compile_errors/stage1/obj/union_with_0_fields.zig delete mode 100644 test/cases/compile_errors/stage1/obj/unreachable_code-nested_returns.zig delete mode 100644 test/cases/compile_errors/stage1/obj/unreachable_code.zig delete mode 100644 test/cases/compile_errors/stage1/obj/use_of_undeclared_identifier.zig delete mode 100644 test/cases/compile_errors/stage1/obj/usingnamespace_with_wrong_type.zig delete mode 100644 test/cases/compile_errors/stage1/obj/variable_has_wrong_type.zig delete mode 100644 test/cases/compile_errors/stage1/obj/variable_with_type_noreturn.zig delete mode 100644 test/cases/compile_errors/stage1/obj/volatile_on_global_assembly.zig delete mode 100644 test/cases/compile_errors/stage1/test/bad_splat_type.zig delete mode 100644 test/cases/compile_errors/stage1/test/non-exhaustive_enum_marker_assigned_a_value.zig delete mode 100644 test/cases/compile_errors/stage1/test/non-exhaustive_enums.zig delete mode 100644 test/cases/compile_errors/stage1/test/reify_typeOf_with_no_arguments.zig delete mode 100644 test/cases/compile_errors/stage1/test/reject_extern_variables_with_initializers.zig delete mode 100644 test/cases/compile_errors/stage1/test/return_invalid_type_from_test.zig rename test/cases/compile_errors/{stage1/obj => }/struct_field_missing_type.zig (66%) rename test/cases/compile_errors/{stage1/obj => }/struct_init_syntax_for_array.zig (52%) rename test/cases/compile_errors/{stage1/obj => }/struct_with_declarations_unavailable_for_reify_type.zig (56%) rename test/cases/compile_errors/{stage1/obj => }/struct_with_invalid_field.zig (85%) rename test/cases/compile_errors/{stage1/obj => }/suspend_inside_suspend_block.zig (53%) rename test/cases/compile_errors/{stage1/obj => }/switch_expression-duplicate_enumeration_prong.zig (63%) rename test/cases/compile_errors/{stage1/obj => }/switch_expression-duplicate_enumeration_prong_when_else_present.zig (64%) rename test/cases/compile_errors/{stage1/obj => }/switch_expression-duplicate_type.zig (70%) rename test/cases/compile_errors/{stage1/obj => }/switch_expression-duplicate_type_struct_alias.zig (74%) rename test/cases/compile_errors/{stage1/obj => }/switch_expression-missing_enumeration_prong.zig (50%) rename test/cases/compile_errors/{stage1/obj => }/switch_expression-multiple_else_prongs.zig (63%) create mode 100644 test/cases/compile_errors/switch_expression-non_exhaustive_integer_prongs.zig rename test/cases/compile_errors/{stage1/obj => }/switch_expression-unreachable_else_prong_enum.zig (67%) rename test/cases/compile_errors/{stage1/obj => }/switch_expression-unreachable_else_prong_range_i8.zig (56%) rename test/cases/compile_errors/{stage1/obj => }/switch_expression-unreachable_else_prong_range_u8.zig (55%) create mode 100644 test/cases/compile_errors/switch_expression-unreachable_else_prong_u1.zig rename test/cases/compile_errors/{stage1/obj => }/switch_expression-unreachable_else_prong_u2.zig (52%) create mode 100644 test/cases/compile_errors/switch_on_enum_with_1_field_with_no_prongs.zig rename test/cases/compile_errors/{stage1/test => }/switching_with_exhaustive_enum_has___prong_.zig (59%) rename test/cases/compile_errors/{stage1/test => }/tagName_on_invalid_value_of_non-exhaustive_enum.zig (54%) rename test/cases/compile_errors/{stage1/obj => }/threadlocal_qualifier_on_const.zig (55%) rename test/cases/compile_errors/{stage1/obj => }/truncate_sign_mismatch.zig (55%) rename test/cases/compile_errors/{stage1/obj => }/try_in_function_with_non_error_return_type.zig (57%) rename test/cases/compile_errors/{stage1/test => }/type_mismatch_in_C_prototype_with_varargs.zig (56%) rename test/cases/compile_errors/{stage1/obj => }/undeclared_identifier.zig (51%) rename test/cases/compile_errors/{stage1/obj => }/undeclared_identifier_error_should_mark_fn_as_impure.zig (59%) rename test/cases/compile_errors/{stage1/obj => }/undeclared_identifier_in_unanalyzed_branch.zig (55%) create mode 100644 test/cases/compile_errors/undefined_as_field_type_is_rejected.zig create mode 100644 test/cases/compile_errors/undefined_function_call.zig rename test/cases/compile_errors/{stage1/obj => }/underscore_is_not_a_declarable_symbol.zig (51%) rename test/cases/compile_errors/{stage1/obj => }/underscore_should_not_be_usable_inside_for.zig (64%) rename test/cases/compile_errors/{stage1/obj => }/underscore_should_not_be_usable_inside_while.zig (70%) rename test/cases/compile_errors/{stage1/obj => }/underscore_should_not_be_usable_inside_while_else.zig (78%) create mode 100644 test/cases/compile_errors/union_fields_with_value_assignments.zig create mode 100644 test/cases/compile_errors/union_with_0_fields.zig rename test/cases/compile_errors/{stage1/obj => }/union_with_specified_enum_omits_field.zig (56%) rename test/cases/compile_errors/{stage1/obj => }/unreachable_code-double_break.zig (52%) create mode 100644 test/cases/compile_errors/unreachable_code-nested_returns.zig create mode 100644 test/cases/compile_errors/unreachable_code.zig rename test/cases/compile_errors/{stage1/obj => }/unreachable_variable.zig (53%) rename test/cases/compile_errors/{stage1/obj => }/unreachable_with_return.zig (52%) rename test/cases/compile_errors/{stage1/obj => }/use_invalid_number_literal_as_array_index.zig (55%) create mode 100644 test/cases/compile_errors/use_of_undeclared_identifier.zig rename test/cases/compile_errors/{stage1/obj => }/using_invalid_types_in_function_call_raises_an_error.zig (66%) create mode 100644 test/cases/compile_errors/usingnamespace_with_wrong_type.zig create mode 100644 test/cases/compile_errors/variable_has_wrong_type.zig rename test/cases/compile_errors/{stage1/obj => }/variable_initialization_compile_error_then_referenced.zig (74%) create mode 100644 test/cases/compile_errors/variable_with_type_noreturn.zig create mode 100644 test/cases/compile_errors/volatile_on_global_assembly.zig rename test/cases/compile_errors/{stage1/obj => }/while_expected_bool_got_error_union.zig (56%) rename test/cases/compile_errors/{stage1/obj => }/while_expected_bool_got_optional.zig (57%) rename test/cases/compile_errors/{stage1/obj => }/write_to_const_global_variable.zig (61%) rename test/cases/compile_errors/{stage1/obj => }/wrong_size_to_an_array_literal.zig (55%) rename test/cases/compile_errors/{stage1/obj => }/wrong_type_passed_to_panic.zig (51%) rename test/cases/compile_errors/{stage1/obj => }/wrong_type_to_hasField.zig (52%) diff --git a/src/AstGen.zig b/src/AstGen.zig index ec8af65614..c4cba54bdc 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -4464,6 +4464,7 @@ fn containerDecl( var total_fields: usize = 0; var decls: usize = 0; var nonexhaustive_node: Ast.Node.Index = 0; + var nonfinal_nonexhaustive = false; for (container_decl.ast.members) |member_node| { const member = switch (node_tags[member_node]) { .container_field_init => tree.containerFieldInit(member_node), @@ -4515,6 +4516,8 @@ fn containerDecl( return astgen.failNode(member.ast.value_expr, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{}); } continue; + } else if (nonexhaustive_node != 0) { + nonfinal_nonexhaustive = true; } total_fields += 1; if (member.ast.value_expr != 0) { @@ -4524,6 +4527,9 @@ fn containerDecl( values += 1; } } + if (nonfinal_nonexhaustive) { + return astgen.failNode(nonexhaustive_node, "'_' field of non-exhaustive enum must be last", .{}); + } break :blk .{ .total_fields = total_fields, .values = values, diff --git a/src/Sema.zig b/src/Sema.zig index fdf3810d4a..6f516229a0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1707,7 +1707,7 @@ fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: T } fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, optional_ty: Type) CompileError { - return sema.fail(block, src, "expected optional type, found {}", .{optional_ty.fmt(sema.mod)}); + return sema.fail(block, src, "expected optional type, found '{}'", .{optional_ty.fmt(sema.mod)}); } fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError { @@ -1844,7 +1844,7 @@ pub fn resolveAlign( const alignment = @intCast(u32, alignment_big); // We coerce to u16 in the prev line. if (alignment == 0) return sema.fail(block, src, "alignment must be >= 1", .{}); if (!std.math.isPowerOfTwo(alignment)) { - return sema.fail(block, src, "alignment value {d} is not a power of two", .{ + return sema.fail(block, src, "alignment value '{d}' is not a power of two", .{ alignment, }); } @@ -2376,6 +2376,12 @@ fn zirEnumDecl( } } + if (small.nonexhaustive) { + if (fields_len > 1 and std.math.log2_int(u64, fields_len) == enum_obj.tag_ty.bitSize(sema.mod.getTarget())) { + return sema.fail(block, src, "non-exhaustive enum specifies every value", .{}); + } + } + try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { if (bag != 0) break true; @@ -4540,6 +4546,7 @@ fn analyzeBlockBody( // to emit a jump instruction to after the block when it encounters the break. try parent_block.instructions.append(gpa, merges.block_inst); const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items, .none); + // TODO add note "missing else causes void value" const type_src = src; // TODO: better source location const valid_rt = try sema.validateRunTimeType(child_block, type_src, resolved_ty, false); @@ -4761,7 +4768,7 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst const msg = msg: { const msg = try sema.errMsg(block, src, "multiple @setAlignStack in the same function body", .{}); errdefer msg.destroy(sema.gpa); - try sema.errNote(block, src, msg, "other instance here", .{}); + try sema.errNote(block, gop.value_ptr.src, msg, "other instance here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(block, msg); @@ -6155,7 +6162,7 @@ fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const payload = try sema.resolveType(block, rhs_src, extra.rhs); if (error_set.zigTypeTag() != .ErrorSet) { - return sema.fail(block, lhs_src, "expected error set type, found {}", .{ + return sema.fail(block, lhs_src, "expected error set type, found '{}'", .{ error_set.fmt(sema.mod), }); } @@ -6243,7 +6250,7 @@ fn zirIntToError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| { const int = try sema.usizeCast(block, operand_src, value.toUnsignedInt(target)); if (int > sema.mod.global_error_set.count() or int == 0) - return sema.fail(block, operand_src, "integer value {d} represents no error", .{int}); + return sema.fail(block, operand_src, "integer value '{d}' represents no error", .{int}); const payload = try sema.arena.create(Value.Payload.Error); payload.* = .{ .base = .{ .tag = .@"error" }, @@ -6288,9 +6295,9 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs); const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs); if (lhs_ty.zigTypeTag() != .ErrorSet) - return sema.fail(block, lhs_src, "expected error set type, found {}", .{lhs_ty.fmt(sema.mod)}); + return sema.fail(block, lhs_src, "expected error set type, found '{}'", .{lhs_ty.fmt(sema.mod)}); if (rhs_ty.zigTypeTag() != .ErrorSet) - return sema.fail(block, rhs_src, "expected error set type, found {}", .{rhs_ty.fmt(sema.mod)}); + return sema.fail(block, rhs_src, "expected error set type, found '{}'", .{rhs_ty.fmt(sema.mod)}); // Anything merged with anyerror is anyerror. if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) { @@ -6351,7 +6358,7 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A break :blk try sema.unionToTag(block, tag_ty, operand, operand_src); }, else => { - return sema.fail(block, operand_src, "expected enum or tagged union, found {}", .{ + return sema.fail(block, operand_src, "expected enum or tagged union, found '{}'", .{ operand_ty.fmt(sema.mod), }); }, @@ -6385,7 +6392,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const operand = try sema.resolveInst(extra.rhs); if (dest_ty.zigTypeTag() != .Enum) { - return sema.fail(block, dest_ty_src, "expected enum, found {}", .{dest_ty.fmt(sema.mod)}); + return sema.fail(block, dest_ty_src, "expected enum, found '{}'", .{dest_ty.fmt(sema.mod)}); } if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |int_val| { @@ -6400,7 +6407,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const msg = try sema.errMsg( block, src, - "enum '{}' has no tag with value {}", + "enum '{}' has no tag with value '{}'", .{ dest_ty.fmt(sema.mod), int_val.fmtValue(sema.typeOf(operand), sema.mod) }, ); errdefer msg.destroy(sema.gpa); @@ -6452,7 +6459,7 @@ fn analyzeOptionalPayloadPtr( const opt_type = optional_ptr_ty.elemType(); if (opt_type.zigTypeTag() != .Optional) { - return sema.fail(block, src, "expected optional type, found {}", .{opt_type.fmt(sema.mod)}); + return sema.fail(block, src, "expected optional type, found '{}'", .{opt_type.fmt(sema.mod)}); } const child_type = try opt_type.optionalChildAlloc(sema.arena); @@ -6640,7 +6647,7 @@ fn analyzeErrUnionPayloadPtr( assert(operand_ty.zigTypeTag() == .Pointer); if (operand_ty.elemType().zigTypeTag() != .ErrorUnion) { - return sema.fail(block, src, "expected error union type, found {}", .{ + return sema.fail(block, src, "expected error union type, found '{}'", .{ operand_ty.elemType().fmt(sema.mod), }); } @@ -6739,7 +6746,7 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE assert(operand_ty.zigTypeTag() == .Pointer); if (operand_ty.elemType().zigTypeTag() != .ErrorUnion) { - return sema.fail(block, src, "expected error union type, found {}", .{ + return sema.fail(block, src, "expected error union type, found '{}'", .{ operand_ty.elemType().fmt(sema.mod), }); } @@ -7639,7 +7646,6 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; @@ -7674,7 +7680,7 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A return sema.addConstant(dest_ty, try operand_val.floatCast(sema.arena, dest_ty, target)); } if (dest_is_comptime_float) { - return sema.fail(block, src, "unable to cast runtime value to 'comptime_float'", .{}); + return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_float'", .{}); } const src_bits = operand_ty.floatBits(target); const dst_bits = dest_ty.floatBits(target); @@ -11664,8 +11670,8 @@ fn analyzeCmp( const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]LazySrcLoc{ lhs_src, rhs_src } }); if (!resolved_type.isSelfComparable(is_equality_cmp)) { - return sema.fail(block, src, "{s} operator not allowed for type '{}'", .{ - @tagName(op), resolved_type.fmt(sema.mod), + return sema.fail(block, src, "operator {s} not allowed for type '{}'", .{ + compareOperatorName(op), resolved_type.fmt(sema.mod), }); } const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); @@ -11673,6 +11679,17 @@ fn analyzeCmp( return sema.cmpSelf(block, casted_lhs, casted_rhs, op, lhs_src, rhs_src); } +fn compareOperatorName(comp: std.math.CompareOperator) []const u8 { + return switch (comp) { + .lt => "<", + .lte => "<=", + .eq => "==", + .gte => ">=", + .gt => ">", + .neq => "!=", + }; +} + fn cmpSelf( sema: *Sema, block: *Block, @@ -14382,7 +14399,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air }; return sema.failWithOwnedErrorMsg(block, msg); }, - else => return sema.fail(block, operand_src, "expected enum or union; found {}", .{ + else => return sema.fail(block, operand_src, "expected enum or union; found '{}'", .{ operand_ty.fmt(mod), }), }; @@ -14392,8 +14409,8 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const field_index = enum_ty.enumTagFieldIndex(val, mod) orelse { const enum_decl = mod.declPtr(enum_decl_index); const msg = msg: { - const msg = try sema.errMsg(block, src, "no field with value {} in enum '{s}'", .{ - casted_operand, enum_decl.name, + const msg = try sema.errMsg(block, src, "no field with value '{}' in enum '{s}'", .{ + val.fmtValue(enum_ty, sema.mod), enum_decl.name, }); errdefer msg.destroy(sema.gpa); try mod.errNoteNonLazy(enum_decl.srcLoc(), msg, "declared here", .{}); @@ -14458,6 +14475,8 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I var buffer: Value.ToTypeBuffer = undefined; const child_ty = child_val.toType(&buffer); + try sema.checkVectorElemType(block, src, child_ty); + const ty = try Type.vector(sema.arena, len, try child_ty.copy(sema.arena)); return sema.addType(ty); }, @@ -15099,6 +15118,8 @@ fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { const result_val = try sema.floatToInt(block, operand_src, val, operand_ty, dest_ty); return sema.addConstant(dest_ty, result_val); + } else if (dest_ty.zigTypeTag() == .ComptimeInt) { + return sema.failWithNeededComptime(block, operand_src); } try sema.requireRuntimeBlock(block, operand_src); @@ -15121,6 +15142,8 @@ fn zirIntToFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const target = sema.mod.getTarget(); const result_val = try val.intToFloat(sema.arena, operand_ty, dest_ty, target); return sema.addConstant(dest_ty, result_val); + } else if (dest_ty.zigTypeTag() == .ComptimeFloat) { + return sema.failWithNeededComptime(block, operand_src); } try sema.requireRuntimeBlock(block, operand_src); @@ -15242,7 +15265,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat const msg = try sema.errMsg( block, src, - "error.{s} not a member of error set '{}'", + "'error.{s}' not a member of error set '{}'", .{ error_name, dest_ty.fmt(sema.mod) }, ); errdefer msg.destroy(sema.gpa); @@ -15633,22 +15656,29 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 try sema.resolveTypeLayout(block, lhs_src, ty); if (ty.tag() != .@"struct") { - return sema.fail( - block, - lhs_src, - "expected struct type, found '{}'", - .{ty.fmt(sema.mod)}, - ); + const msg = msg: { + const msg = try sema.errMsg(block, lhs_src, "expected struct type, found '{}'", .{ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + try sema.addDeclaredHereNote(msg, ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); } const fields = ty.structFields(); const index = fields.getIndex(field_name) orelse { - return sema.fail( - block, - rhs_src, - "struct '{}' has no field '{s}'", - .{ ty.fmt(sema.mod), field_name }, - ); + const msg = msg: { + const msg = try sema.errMsg( + block, + rhs_src, + "struct '{}' has no field '{s}'", + .{ ty.fmt(sema.mod), field_name }, + ); + errdefer msg.destroy(sema.gpa); + try sema.addDeclaredHereNote(msg, ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); }; switch (ty.containerLayout()) { @@ -15701,7 +15731,7 @@ fn checkPtrOperand( const msg = try sema.errMsg( block, ty_src, - "expected pointer, found {}", + "expected pointer, found '{}'", .{ty.fmt(sema.mod)}, ); errdefer msg.destroy(sema.gpa); @@ -15817,7 +15847,7 @@ fn checkAtomicPtrOperand( error.BadType => return sema.fail( block, elem_ty_src, - "expected bool, integer, float, enum, or pointer type; found {}", + "expected bool, integer, float, enum, or pointer type; found '{}'", .{elem_ty.fmt(sema.mod)}, ), }; @@ -16026,7 +16056,7 @@ fn checkVectorizableBinaryOperands( } } else { const msg = msg: { - const msg = try sema.errMsg(block, src, "mixed scalar and vector operands: {} and {}", .{ + const msg = try sema.errMsg(block, src, "mixed scalar and vector operands: '{}' and '{}'", .{ lhs_ty.fmt(sema.mod), rhs_ty.fmt(sema.mod), }); errdefer msg.destroy(sema.gpa); @@ -16069,6 +16099,10 @@ fn resolveExportOptions( const visibility_val = try sema.resolveConstValue(block, src, visibility_operand); const visibility = visibility_val.toEnum(std.builtin.SymbolVisibility); + if (name.len < 1) { + return sema.fail(block, src, "exported symbol name cannot be empty", .{}); + } + if (visibility != .default and linkage == .Internal) { return sema.fail(block, src, "symbol '{s}' exported with internal linkage has non-default visibility {s}", .{ name, @tagName(visibility), @@ -16247,7 +16281,7 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const target = sema.mod.getTarget(); if (operand_ty.zigTypeTag() != .Vector) { - return sema.fail(block, operand_src, "expected vector, found {}", .{operand_ty.fmt(sema.mod)}); + return sema.fail(block, operand_src, "expected vector, found '{}'", .{operand_ty.fmt(sema.mod)}); } const scalar_ty = operand_ty.childType(); @@ -16256,13 +16290,13 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. switch (operation) { .And, .Or, .Xor => switch (scalar_ty.zigTypeTag()) { .Int, .Bool => {}, - else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found {}", .{ + else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found '{}'", .{ @tagName(operation), operand_ty.fmt(sema.mod), }), }, .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag()) { .Int, .Float => {}, - else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found {}", .{ + else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found '{}'", .{ @tagName(operation), operand_ty.fmt(sema.mod), }), }, @@ -16321,7 +16355,7 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const mask_len = switch (sema.typeOf(mask).zigTypeTag()) { .Array, .Vector => sema.typeOf(mask).arrayLen(), - else => return sema.fail(block, mask_src, "expected vector or array, found {}", .{sema.typeOf(mask).fmt(sema.mod)}), + else => return sema.fail(block, mask_src, "expected vector or array, found '{}'", .{sema.typeOf(mask).fmt(sema.mod)}), }; mask_ty = try Type.Tag.vector.create(sema.arena, .{ .len = mask_len, @@ -16356,7 +16390,7 @@ fn analyzeShuffle( var maybe_a_len = switch (sema.typeOf(a).zigTypeTag()) { .Array, .Vector => sema.typeOf(a).arrayLen(), .Undefined => null, - else => return sema.fail(block, a_src, "expected vector or array with element type {}, found {}", .{ + else => return sema.fail(block, a_src, "expected vector or array with element type '{}', found '{}'", .{ elem_ty.fmt(sema.mod), sema.typeOf(a).fmt(sema.mod), }), @@ -16364,7 +16398,7 @@ fn analyzeShuffle( var maybe_b_len = switch (sema.typeOf(b).zigTypeTag()) { .Array, .Vector => sema.typeOf(b).arrayLen(), .Undefined => null, - else => return sema.fail(block, b_src, "expected vector or array with element type {}, found {}", .{ + else => return sema.fail(block, b_src, "expected vector or array with element type '{}', found '{}'", .{ elem_ty.fmt(sema.mod), sema.typeOf(b).fmt(sema.mod), }), @@ -16412,7 +16446,7 @@ fn analyzeShuffle( const msg = try sema.errMsg(block, mask_src, "mask index {d} has out-of-bounds selection", .{i}); errdefer msg.destroy(sema.gpa); - try sema.errNote(block, operand_info[chosen][1], msg, "selected index {d} out of bounds of {}", .{ + try sema.errNote(block, operand_info[chosen][1], msg, "selected index {d} out of bounds of '{}'", .{ unsigned, operand_info[chosen][2].fmt(sema.mod), }); @@ -16881,7 +16915,7 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const args_ty = sema.typeOf(args); if (!args_ty.isTuple() and args_ty.tag() != .empty_struct_literal) { - return sema.fail(block, args_src, "expected a tuple, found {}", .{args_ty.fmt(sema.mod)}); + return sema.fail(block, args_src, "expected a tuple, found '{}'", .{args_ty.fmt(sema.mod)}); } var resolved_args: []Air.Inst.Ref = undefined; @@ -18217,9 +18251,15 @@ fn fieldVal( if (payload.data.names.getEntry(field_name)) |entry| { break :blk entry.key_ptr.*; } - return sema.fail(block, src, "no error named '{s}' in '{}'", .{ - field_name, child_type.fmt(sema.mod), - }); + const msg = msg: { + const msg = try sema.errMsg(block, src, "no error named '{s}' in '{}'", .{ + field_name, child_type.fmt(sema.mod), + }); + errdefer msg.destroy(sema.gpa); + try sema.addDeclaredHereNote(msg, child_type); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); } else (try sema.mod.getErrorValue(field_name)).key; return sema.addConstant( @@ -18894,12 +18934,12 @@ fn tupleFieldIndex( field_name_src: LazySrcLoc, ) CompileError!u32 { const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch |err| { - return sema.fail(block, field_name_src, "tuple {} has no such field '{s}': {s}", .{ + return sema.fail(block, field_name_src, "tuple '{}' has no such field '{s}': {s}", .{ tuple_ty.fmt(sema.mod), field_name, @errorName(err), }); }; if (field_index >= tuple_ty.structFieldCount()) { - return sema.fail(block, field_name_src, "tuple {} has no such field '{s}'", .{ + return sema.fail(block, field_name_src, "tuple '{}' has no such field '{s}'", .{ tuple_ty.fmt(sema.mod), field_name, }); } @@ -19809,7 +19849,7 @@ fn coerce( return sema.fail( block, inst_src, - "fractional component prevents float value {} from coercion to type '{}'", + "fractional component prevents float value '{}' from coercion to type '{}'", .{ val.fmtValue(inst_ty, sema.mod), dest_ty.fmt(sema.mod) }, ); } @@ -19820,7 +19860,7 @@ fn coerce( if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| { // comptime known integer to other number if (!(try sema.intFitsInType(block, inst_src, val, dest_ty))) { - return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) }); + return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) }); } return try sema.addConstant(dest_ty, val); } @@ -19854,7 +19894,7 @@ fn coerce( return sema.fail( block, inst_src, - "type {} cannot represent float value {}", + "type '{}' cannot represent float value '{}'", .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) }, ); } @@ -19878,7 +19918,7 @@ fn coerce( // return sema.fail( // block, // inst_src, - // "type {} cannot represent integer value {}", + // "type '{}' cannot represent integer value '{}'", // .{ dest_ty.fmt(sema.mod), val }, // ); //} @@ -20021,7 +20061,7 @@ fn coerce( return sema.addConstUndef(dest_ty); } - return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod) }); + return sema.fail(block, inst_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod) }); } const InMemoryCoercionResult = enum { @@ -21541,7 +21581,7 @@ fn coerceEnumToUnion( const tag_ty = union_ty.unionTagType() orelse { const msg = msg: { - const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{ + const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ union_ty.fmt(sema.mod), inst_ty.fmt(sema.mod), }); errdefer msg.destroy(sema.gpa); @@ -21557,7 +21597,7 @@ fn coerceEnumToUnion( const union_obj = union_ty.cast(Type.Payload.Union).?.data; const field_index = union_obj.tag_ty.enumTagFieldIndex(val, sema.mod) orelse { const msg = msg: { - const msg = try sema.errMsg(block, inst_src, "union {} has no tag with value {}", .{ + const msg = try sema.errMsg(block, inst_src, "union '{}' has no tag with value '{}'", .{ union_ty.fmt(sema.mod), val.fmtValue(tag_ty, sema.mod), }); errdefer msg.destroy(sema.gpa); @@ -21593,7 +21633,7 @@ fn coerceEnumToUnion( if (tag_ty.isNonexhaustiveEnum()) { const msg = msg: { - const msg = try sema.errMsg(block, inst_src, "runtime coercion to union {} from non-exhaustive enum", .{ + const msg = try sema.errMsg(block, inst_src, "runtime coercion to union '{}' from non-exhaustive enum", .{ union_ty.fmt(sema.mod), }); errdefer msg.destroy(sema.gpa); @@ -21710,7 +21750,7 @@ fn coerceArrayLike( if (dest_len != inst_len) { const msg = msg: { - const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{ + const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod), }); errdefer msg.destroy(sema.gpa); @@ -21781,7 +21821,7 @@ fn coerceTupleToArray( if (dest_len != inst_len) { const msg = msg: { - const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{ + const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod), }); errdefer msg.destroy(sema.gpa); @@ -23553,7 +23593,7 @@ fn resolveStructLayout( switch (struct_obj.status) { .none, .have_field_types => {}, .field_types_wip, .layout_wip => { - return sema.fail(block, src, "struct {} depends on itself", .{ty.fmt(sema.mod)}); + return sema.fail(block, src, "struct '{}' depends on itself", .{ty.fmt(sema.mod)}); }, .have_layout, .fully_resolved_wip, .fully_resolved => return, } @@ -23584,7 +23624,7 @@ fn resolveUnionLayout( switch (union_obj.status) { .none, .have_field_types => {}, .field_types_wip, .layout_wip => { - return sema.fail(block, src, "union {} depends on itself", .{ty.fmt(sema.mod)}); + return sema.fail(block, src, "union '{}' depends on itself", .{ty.fmt(sema.mod)}); }, .have_layout, .fully_resolved_wip, .fully_resolved => return, } @@ -23752,7 +23792,7 @@ fn resolveTypeFieldsStruct( switch (struct_obj.status) { .none => {}, .field_types_wip => { - return sema.fail(block, src, "struct {} depends on itself", .{ty.fmt(sema.mod)}); + return sema.fail(block, src, "struct '{}' depends on itself", .{ty.fmt(sema.mod)}); }, .have_field_types, .have_layout, @@ -23782,7 +23822,7 @@ fn resolveTypeFieldsUnion( switch (union_obj.status) { .none => {}, .field_types_wip => { - return sema.fail(block, src, "union {} depends on itself", .{ty.fmt(sema.mod)}); + return sema.fail(block, src, "union '{}' depends on itself", .{ty.fmt(sema.mod)}); }, .have_field_types, .have_layout, @@ -25005,7 +25045,7 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr // The type is not in-memory coercible or the direct dereference failed, so it must // be bitcast according to the pointer type we are performing the load through. if (!load_ty.hasWellDefinedLayout()) - return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{load_ty.fmt(sema.mod)}); + return sema.fail(block, src, "comptime dereference requires '{}' to have a well-defined layout, but it does not.", .{load_ty.fmt(sema.mod)}); const load_sz = try sema.typeAbiSize(block, src, load_ty); @@ -25020,11 +25060,11 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr if (deref.ty_without_well_defined_layout) |bad_ty| { // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem // is that some type we encountered when de-referencing does not have a well-defined layout. - return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{bad_ty.fmt(sema.mod)}); + return sema.fail(block, src, "comptime dereference requires '{}' to have a well-defined layout, but it does not.", .{bad_ty.fmt(sema.mod)}); } else { // If all encountered types had well-defined layouts, the parent is the root decl and it just // wasn't big enough for the load. - return sema.fail(block, src, "dereference of {} exceeds bounds of containing decl of type {}", .{ ptr_ty.fmt(sema.mod), deref.parent.?.tv.ty.fmt(sema.mod) }); + return sema.fail(block, src, "dereference of '{}' exceeds bounds of containing decl of type '{}'", .{ ptr_ty.fmt(sema.mod), deref.parent.?.tv.ty.fmt(sema.mod) }); } } @@ -25032,7 +25072,7 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr /// is too big to fit. fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize { if (@bitSizeOf(u64) <= @bitSizeOf(usize)) return int; - return std.math.cast(usize, int) orelse return sema.fail(block, src, "expression produces integer value {d} which is too big for this compiler implementation to handle", .{int}); + return std.math.cast(usize, int) orelse return sema.fail(block, src, "expression produces integer value '{d}' which is too big for this compiler implementation to handle", .{int}); } /// For pointer-like optionals, it returns the pointer type. For pointers, @@ -25387,7 +25427,7 @@ fn anonStructFieldIndex( return @intCast(u32, i); } } - return sema.fail(block, field_src, "anonymous struct {} has no such field '{s}'", .{ + return sema.fail(block, field_src, "anonymous struct '{}' has no such field '{s}'", .{ struct_ty.fmt(sema.mod), field_name, }); } @@ -25768,7 +25808,7 @@ fn floatToIntScalar( try Value.Tag.int_big_positive.create(sema.arena, result_limbs); if (!(try sema.intFitsInType(block, src, result, int_ty))) { - return sema.fail(block, src, "float value {} cannot be stored in integer type '{}'", .{ + return sema.fail(block, src, "float value '{}' cannot be stored in integer type '{}'", .{ val.fmtValue(float_ty, sema.mod), int_ty.fmt(sema.mod), }); } diff --git a/src/TypedValue.zig b/src/TypedValue.zig index b00600d37a..a41c865827 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -409,11 +409,11 @@ pub fn print( } return writer.writeAll(" }"); }, - .float_16 => return writer.print("{}", .{val.castTag(.float_16).?.data}), - .float_32 => return writer.print("{}", .{val.castTag(.float_32).?.data}), - .float_64 => return writer.print("{}", .{val.castTag(.float_64).?.data}), - .float_80 => return writer.print("{}", .{val.castTag(.float_80).?.data}), - .float_128 => return writer.print("{}", .{val.castTag(.float_128).?.data}), + .float_16 => return writer.print("{d}", .{val.castTag(.float_16).?.data}), + .float_32 => return writer.print("{d}", .{val.castTag(.float_32).?.data}), + .float_64 => return writer.print("{d}", .{val.castTag(.float_64).?.data}), + .float_80 => return writer.print("{d}", .{@floatCast(f64, val.castTag(.float_80).?.data)}), + .float_128 => return writer.print("{d}", .{@floatCast(f64, val.castTag(.float_128).?.data)}), .@"error" => return writer.print("error.{s}", .{val.castTag(.@"error").?.data.name}), .eu_payload => { val = val.castTag(.eu_payload).?.data; diff --git a/test/cases/aarch64-macos/hello_world_with_updates.1.zig b/test/cases/aarch64-macos/hello_world_with_updates.1.zig index 909fc9ccfb..435747c2ee 100644 --- a/test/cases/aarch64-macos/hello_world_with_updates.1.zig +++ b/test/cases/aarch64-macos/hello_world_with_updates.1.zig @@ -2,4 +2,4 @@ pub export fn main() noreturn {} // error // -// :1:32: error: expected noreturn, found void +// :1:32: error: expected type 'noreturn', found 'void' diff --git a/test/cases/compile_errors/stage1/obj/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig b/test/cases/compile_errors/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig similarity index 64% rename from test/cases/compile_errors/stage1/obj/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig rename to test/cases/compile_errors/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig index e1d498e65f..fd194f252d 100644 --- a/test/cases/compile_errors/stage1/obj/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig +++ b/test/cases/compile_errors/Issue_5586_Make_unary_minus_for_unsigned_types_a_compile_error.zig @@ -9,8 +9,8 @@ export fn f2(x: V(4, u32)) V(4, u32) { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:12: error: negation of type 'u32' -// tmp.zig:8:12: error: negation of type 'u32' +// :3:12: error: negation of type 'u32' +// :8:12: error: negation of type '@Vector(4, u32)' diff --git a/test/cases/compile_errors/stage1/obj/Issue_6823_dont_allow_._to_be_followed_by_.zig b/test/cases/compile_errors/Issue_6823_dont_allow_._to_be_followed_by_.zig similarity index 50% rename from test/cases/compile_errors/stage1/obj/Issue_6823_dont_allow_._to_be_followed_by_.zig rename to test/cases/compile_errors/Issue_6823_dont_allow_._to_be_followed_by_.zig index 1aa0996ba4..20c9528847 100644 --- a/test/cases/compile_errors/stage1/obj/Issue_6823_dont_allow_._to_be_followed_by_.zig +++ b/test/cases/compile_errors/Issue_6823_dont_allow_._to_be_followed_by_.zig @@ -4,7 +4,7 @@ fn foo() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:28: error: '.*' cannot be followed by '*'. Are you missing a space? +// :2:28: error: '.*' cannot be followed by '*'. Are you missing a space? diff --git a/test/cases/compile_errors/stage1/test/access_invalid_typeInfo_decl.zig b/test/cases/compile_errors/access_invalid_typeInfo_decl.zig similarity index 60% rename from test/cases/compile_errors/stage1/test/access_invalid_typeInfo_decl.zig rename to test/cases/compile_errors/access_invalid_typeInfo_decl.zig index 40e31a0855..4233b42e7d 100644 --- a/test/cases/compile_errors/stage1/test/access_invalid_typeInfo_decl.zig +++ b/test/cases/compile_errors/access_invalid_typeInfo_decl.zig @@ -4,8 +4,8 @@ test "Crash" { } // error -// backend=stage1 +// backend=stage2 // target=native // is_test=1 // -// tmp.zig:1:11: error: use of undeclared identifier 'B' +// :1:11: error: use of undeclared identifier 'B' diff --git a/test/cases/compile_errors/access_non-existent_member_of_error_set.zig b/test/cases/compile_errors/access_non-existent_member_of_error_set.zig new file mode 100644 index 0000000000..765bbe59c3 --- /dev/null +++ b/test/cases/compile_errors/access_non-existent_member_of_error_set.zig @@ -0,0 +1,12 @@ +const Foo = error{A}; +comptime { + const z = Foo.Bar; + _ = z; +} + +// error +// backend=stage2 +// target=native +// +// :3:18: error: no error named 'Bar' in 'error{A}' +// :1:13: note: error set declared here diff --git a/test/cases/compile_errors/add_assign_on_undefined_value.zig b/test/cases/compile_errors/add_assign_on_undefined_value.zig new file mode 100644 index 0000000000..b2d183c006 --- /dev/null +++ b/test/cases/compile_errors/add_assign_on_undefined_value.zig @@ -0,0 +1,10 @@ +comptime { + var a: i64 = undefined; + a += a; +} + +// error +// backend=stage2 +// target=native +// +// :3:10: error: use of undefined value here causes undefined behavior diff --git a/test/cases/compile_errors/add_on_undefined_value.zig b/test/cases/compile_errors/add_on_undefined_value.zig new file mode 100644 index 0000000000..404f5019be --- /dev/null +++ b/test/cases/compile_errors/add_on_undefined_value.zig @@ -0,0 +1,10 @@ +comptime { + var a: i64 = undefined; + _ = a + a; +} + +// error +// backend=stage2 +// target=native +// +// :3:13: error: use of undefined value here causes undefined behavior diff --git a/test/cases/compile_errors/stage1/obj/add_overflow_in_function_evaluation.zig b/test/cases/compile_errors/add_overflow_in_function_evaluation.zig similarity index 57% rename from test/cases/compile_errors/stage1/obj/add_overflow_in_function_evaluation.zig rename to test/cases/compile_errors/add_overflow_in_function_evaluation.zig index d4fa312482..2f8a4678d3 100644 --- a/test/cases/compile_errors/stage1/obj/add_overflow_in_function_evaluation.zig +++ b/test/cases/compile_errors/add_overflow_in_function_evaluation.zig @@ -6,7 +6,8 @@ fn add(a: u16, b: u16) u16 { export fn entry() usize { return @sizeOf(@TypeOf(y)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:14: error: operation caused overflow +// :3:14: error: overflow of integer type 'u16' with value '65540' +// :1:14: note: called from here diff --git a/test/cases/compile_errors/stage1/obj/addition_with_non_numbers.zig b/test/cases/compile_errors/addition_with_non_numbers.zig similarity index 64% rename from test/cases/compile_errors/stage1/obj/addition_with_non_numbers.zig rename to test/cases/compile_errors/addition_with_non_numbers.zig index 8f343926f2..deaeeba334 100644 --- a/test/cases/compile_errors/stage1/obj/addition_with_non_numbers.zig +++ b/test/cases/compile_errors/addition_with_non_numbers.zig @@ -6,7 +6,7 @@ const x = Foo {.field = 1} + Foo {.field = 2}; export fn entry() usize { return @sizeOf(@TypeOf(x)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:28: error: invalid operands to binary expression: 'Foo' and 'Foo' +// :4:28: error: invalid operands to binary expression: 'Struct' and 'Struct' diff --git a/test/cases/compile_errors/address_of_number_literal.zig b/test/cases/compile_errors/address_of_number_literal.zig new file mode 100644 index 0000000000..2d8380634a --- /dev/null +++ b/test/cases/compile_errors/address_of_number_literal.zig @@ -0,0 +1,10 @@ +const x = 3; +const y = &x; +fn foo() *const i32 { return y; } +export fn entry() usize { return @sizeOf(@TypeOf(&foo)); } + +// error +// backend=stage2 +// target=native +// +// :3:30: error: expected type '*const i32', found '*const comptime_int' diff --git a/test/cases/compile_errors/stage1/obj/alignCast_expects_pointer_or_slice.zig b/test/cases/compile_errors/alignCast_expects_pointer_or_slice.zig similarity index 52% rename from test/cases/compile_errors/stage1/obj/alignCast_expects_pointer_or_slice.zig rename to test/cases/compile_errors/alignCast_expects_pointer_or_slice.zig index 9f76d3ca4f..1b8e3767b2 100644 --- a/test/cases/compile_errors/stage1/obj/alignCast_expects_pointer_or_slice.zig +++ b/test/cases/compile_errors/alignCast_expects_pointer_or_slice.zig @@ -3,7 +3,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:19: error: expected pointer or slice, found 'u32' +// :2:19: error: expected pointer type, found 'u32' diff --git a/test/cases/compile_errors/stage1/obj/alignment_of_enum_field_specified.zig b/test/cases/compile_errors/alignment_of_enum_field_specified.zig similarity index 69% rename from test/cases/compile_errors/stage1/obj/alignment_of_enum_field_specified.zig rename to test/cases/compile_errors/alignment_of_enum_field_specified.zig index 9b6f1e452d..2ff42f7a65 100644 --- a/test/cases/compile_errors/stage1/obj/alignment_of_enum_field_specified.zig +++ b/test/cases/compile_errors/alignment_of_enum_field_specified.zig @@ -8,7 +8,7 @@ export fn entry1() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:7: error: expected ',' after field +// :3:7: error: expected ',' after field diff --git a/test/cases/compile_errors/stage1/obj/ambiguous_decl_reference.zig b/test/cases/compile_errors/ambiguous_decl_reference.zig similarity index 61% rename from test/cases/compile_errors/stage1/obj/ambiguous_decl_reference.zig rename to test/cases/compile_errors/ambiguous_decl_reference.zig index 82e6a0a24c..c32b9a0755 100644 --- a/test/cases/compile_errors/stage1/obj/ambiguous_decl_reference.zig +++ b/test/cases/compile_errors/ambiguous_decl_reference.zig @@ -13,9 +13,9 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:5:13: error: ambiguous reference -// tmp.zig:7:9: note: declared here -// tmp.zig:1:1: note: also declared here +// :5:13: error: ambiguous reference +// :7:9: note: declared here +// :1:1: note: also declared here diff --git a/test/cases/llvm/any_typed_null_to_any_typed_optional.zig b/test/cases/compile_errors/any_typed_null_to_any_typed_optional.zig similarity index 74% rename from test/cases/llvm/any_typed_null_to_any_typed_optional.zig rename to test/cases/compile_errors/any_typed_null_to_any_typed_optional.zig index c155d04497..ad6427d224 100644 --- a/test/cases/llvm/any_typed_null_to_any_typed_optional.zig +++ b/test/cases/compile_errors/any_typed_null_to_any_typed_optional.zig @@ -8,4 +8,4 @@ pub fn main() void { // backend=stage2,llvm // target=x86_64-linux,x86_64-macos // -// :3:21: error: expected *anyopaque, found ?usize +// :3:21: error: expected type '*anyopaque', found '?usize' diff --git a/test/cases/compile_errors/array_access_of_undeclared_identifier.zig b/test/cases/compile_errors/array_access_of_undeclared_identifier.zig new file mode 100644 index 0000000000..8f4e447aba --- /dev/null +++ b/test/cases/compile_errors/array_access_of_undeclared_identifier.zig @@ -0,0 +1,9 @@ +export fn f() void { + i[i] = i[i]; +} + +// error +// backend=stage2 +// target=native +// +// :2:5: error: use of undeclared identifier 'i' diff --git a/test/cases/compile_errors/stage1/obj/array_concatenation_with_wrong_type.zig b/test/cases/compile_errors/array_concatenation_with_wrong_type.zig similarity index 68% rename from test/cases/compile_errors/stage1/obj/array_concatenation_with_wrong_type.zig rename to test/cases/compile_errors/array_concatenation_with_wrong_type.zig index ccddc7efbd..6f2648f74b 100644 --- a/test/cases/compile_errors/stage1/obj/array_concatenation_with_wrong_type.zig +++ b/test/cases/compile_errors/array_concatenation_with_wrong_type.zig @@ -5,7 +5,7 @@ const a = derp ++ "foo"; export fn entry() usize { return @sizeOf(@TypeOf(a)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:11: error: expected array, found 'usize' +// :3:11: error: expected indexable; found 'usize' diff --git a/test/cases/compile_errors/stage1/obj/asm_at_compile_time.zig b/test/cases/compile_errors/asm_at_compile_time.zig similarity index 70% rename from test/cases/compile_errors/stage1/obj/asm_at_compile_time.zig rename to test/cases/compile_errors/asm_at_compile_time.zig index fcfd5e7da6..ba4aa81170 100644 --- a/test/cases/compile_errors/stage1/obj/asm_at_compile_time.zig +++ b/test/cases/compile_errors/asm_at_compile_time.zig @@ -11,7 +11,7 @@ fn doSomeAsm() void { } // error -// backend=stage1 +// backend=llvm // target=native // -// tmp.zig:6:5: error: unable to evaluate constant expression +// :6:5: error: unable to evaluate constant expression diff --git a/test/cases/compile_errors/stage1/obj/assign_null_to_non-optional_pointer.zig b/test/cases/compile_errors/assign_null_to_non-optional_pointer.zig similarity index 56% rename from test/cases/compile_errors/stage1/obj/assign_null_to_non-optional_pointer.zig rename to test/cases/compile_errors/assign_null_to_non-optional_pointer.zig index 110d6c2629..ca36dbfbfb 100644 --- a/test/cases/compile_errors/stage1/obj/assign_null_to_non-optional_pointer.zig +++ b/test/cases/compile_errors/assign_null_to_non-optional_pointer.zig @@ -3,7 +3,7 @@ const a: *u8 = null; export fn entry() usize { return @sizeOf(@TypeOf(a)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:16: error: expected type '*u8', found '@Type(.Null)' +// :1:16: error: expected type '*u8', found '@TypeOf(null)' diff --git a/test/cases/compile_errors/stage1/obj/assign_through_constant_pointer.zig b/test/cases/compile_errors/assign_through_constant_pointer.zig similarity index 56% rename from test/cases/compile_errors/stage1/obj/assign_through_constant_pointer.zig rename to test/cases/compile_errors/assign_through_constant_pointer.zig index 69ba383843..21924ae4da 100644 --- a/test/cases/compile_errors/stage1/obj/assign_through_constant_pointer.zig +++ b/test/cases/compile_errors/assign_through_constant_pointer.zig @@ -4,7 +4,7 @@ export fn f() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:13: error: cannot assign to constant +// :3:13: error: cannot assign to constant diff --git a/test/cases/compile_errors/stage1/obj/assign_through_constant_slice.zig b/test/cases/compile_errors/assign_through_constant_slice.zig similarity index 60% rename from test/cases/compile_errors/stage1/obj/assign_through_constant_slice.zig rename to test/cases/compile_errors/assign_through_constant_slice.zig index 55d2e7e7c7..a7631be61c 100644 --- a/test/cases/compile_errors/stage1/obj/assign_through_constant_slice.zig +++ b/test/cases/compile_errors/assign_through_constant_slice.zig @@ -4,7 +4,7 @@ export fn f() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:13: error: cannot assign to constant +// :3:13: error: cannot assign to constant diff --git a/test/cases/compile_errors/stage1/obj/assign_to_constant_field.zig b/test/cases/compile_errors/assign_to_constant_field.zig similarity index 68% rename from test/cases/compile_errors/stage1/obj/assign_to_constant_field.zig rename to test/cases/compile_errors/assign_to_constant_field.zig index 610818118a..4c79b29592 100644 --- a/test/cases/compile_errors/stage1/obj/assign_to_constant_field.zig +++ b/test/cases/compile_errors/assign_to_constant_field.zig @@ -7,7 +7,7 @@ export fn derp() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:6:15: error: cannot assign to constant +// :6:15: error: cannot assign to constant diff --git a/test/cases/compile_errors/stage1/obj/assign_to_constant_variable.zig b/test/cases/compile_errors/assign_to_constant_variable.zig similarity index 54% rename from test/cases/compile_errors/stage1/obj/assign_to_constant_variable.zig rename to test/cases/compile_errors/assign_to_constant_variable.zig index 531f669c1a..af6f33ec66 100644 --- a/test/cases/compile_errors/stage1/obj/assign_to_constant_variable.zig +++ b/test/cases/compile_errors/assign_to_constant_variable.zig @@ -4,7 +4,7 @@ export fn f() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:9: error: cannot assign to constant +// :3:9: error: cannot assign to constant diff --git a/test/cases/compile_errors/stage1/obj/assign_too_big_number_to_u16.zig b/test/cases/compile_errors/assign_too_big_number_to_u16.zig similarity index 52% rename from test/cases/compile_errors/stage1/obj/assign_too_big_number_to_u16.zig rename to test/cases/compile_errors/assign_too_big_number_to_u16.zig index 3138ed5faf..1f8b097ae2 100644 --- a/test/cases/compile_errors/stage1/obj/assign_too_big_number_to_u16.zig +++ b/test/cases/compile_errors/assign_too_big_number_to_u16.zig @@ -4,7 +4,7 @@ export fn foo() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:24: error: integer value 753664 cannot be coerced to type 'u16' +// :2:24: error: type 'u16' cannot represent integer value '753664' diff --git a/test/cases/compile_errors/assign_unreachable.zig b/test/cases/compile_errors/assign_unreachable.zig new file mode 100644 index 0000000000..f3d218e465 --- /dev/null +++ b/test/cases/compile_errors/assign_unreachable.zig @@ -0,0 +1,11 @@ +export fn f() void { + const a = return; + _ = a; +} + +// error +// backend=stage2 +// target=native +// +// :2:5: error: unreachable code +// :2:15: note: control flow is diverted here diff --git a/test/cases/compile_errors/stage1/obj/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig b/test/cases/compile_errors/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig similarity index 53% rename from test/cases/compile_errors/stage1/obj/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig rename to test/cases/compile_errors/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig index dfc7ff5036..be910701c4 100644 --- a/test/cases/compile_errors/stage1/obj/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig +++ b/test/cases/compile_errors/atomic_orderings_of_atomicStore_Acquire_or_AcqRel.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:30: error: @atomicStore atomic ordering must not be Acquire or AcqRel +// :3:31: error: @atomicStore atomic ordering must not be Acquire or AcqRel diff --git a/test/cases/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig b/test/cases/compile_errors/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig similarity index 69% rename from test/cases/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig rename to test/cases/compile_errors/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig index 929950e754..3e797a031c 100644 --- a/test/cases/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig +++ b/test/cases/compile_errors/atomic_orderings_of_cmpxchg-failure_stricter_than_success.zig @@ -5,7 +5,7 @@ export fn f() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:81: error: failure atomic ordering must be no stricter than success +// :4:81: error: failure atomic ordering must be no stricter than success diff --git a/test/cases/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig b/test/cases/compile_errors/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig similarity index 70% rename from test/cases/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig rename to test/cases/compile_errors/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig index fe3344e536..0ab227f039 100644 --- a/test/cases/compile_errors/stage1/obj/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig +++ b/test/cases/compile_errors/atomic_orderings_of_cmpxchg-success_Monotonic_or_stricter.zig @@ -5,7 +5,7 @@ export fn f() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:58: error: success atomic ordering must be Monotonic or stricter +// :4:58: error: success atomic ordering must be Monotonic or stricter diff --git a/test/cases/compile_errors/atomic_orderings_of_fence_Acquire_or_stricter.zig b/test/cases/compile_errors/atomic_orderings_of_fence_Acquire_or_stricter.zig new file mode 100644 index 0000000000..3982cc6bc2 --- /dev/null +++ b/test/cases/compile_errors/atomic_orderings_of_fence_Acquire_or_stricter.zig @@ -0,0 +1,9 @@ +export fn entry() void { + @fence(.Monotonic); +} + +// error +// backend=stage2 +// target=native +// +// :2:13: error: atomic ordering must be Acquire or stricter diff --git a/test/cases/compile_errors/stage1/obj/atomicrmw_with_bool_op_not_.Xchg.zig b/test/cases/compile_errors/atomicrmw_with_bool_op_not_.Xchg.zig similarity index 59% rename from test/cases/compile_errors/stage1/obj/atomicrmw_with_bool_op_not_.Xchg.zig rename to test/cases/compile_errors/atomicrmw_with_bool_op_not_.Xchg.zig index 0b5b0cd3c1..4582be60a6 100644 --- a/test/cases/compile_errors/stage1/obj/atomicrmw_with_bool_op_not_.Xchg.zig +++ b/test/cases/compile_errors/atomicrmw_with_bool_op_not_.Xchg.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:30: error: @atomicRmw with bool only allowed with .Xchg +// :3:31: error: @atomicRmw with bool only allowed with .Xchg diff --git a/test/cases/compile_errors/stage1/obj/atomicrmw_with_enum_op_not_.Xchg.zig b/test/cases/compile_errors/atomicrmw_with_enum_op_not_.Xchg.zig similarity index 69% rename from test/cases/compile_errors/stage1/obj/atomicrmw_with_enum_op_not_.Xchg.zig rename to test/cases/compile_errors/atomicrmw_with_enum_op_not_.Xchg.zig index adbf4ca45e..197eee9f7c 100644 --- a/test/cases/compile_errors/stage1/obj/atomicrmw_with_enum_op_not_.Xchg.zig +++ b/test/cases/compile_errors/atomicrmw_with_enum_op_not_.Xchg.zig @@ -10,7 +10,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:9:27: error: @atomicRmw with enum only allowed with .Xchg +// :9:28: error: @atomicRmw with enum only allowed with .Xchg diff --git a/test/cases/compile_errors/stage1/obj/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig b/test/cases/compile_errors/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig similarity index 54% rename from test/cases/compile_errors/stage1/obj/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig rename to test/cases/compile_errors/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig index 4f3ebfcef6..80720fbca1 100644 --- a/test/cases/compile_errors/stage1/obj/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig +++ b/test/cases/compile_errors/atomicrmw_with_float_op_not_.Xchg_.Add_or_.Sub.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:29: error: @atomicRmw with float only allowed with .Xchg, .Add and .Sub +// :3:30: error: @atomicRmw with float only allowed with .Xchg, .Add, and .Sub diff --git a/test/cases/compile_errors/stage1/obj/attempt_to_cast_enum_literal_to_error.zig b/test/cases/compile_errors/attempt_to_cast_enum_literal_to_error.zig similarity index 52% rename from test/cases/compile_errors/stage1/obj/attempt_to_cast_enum_literal_to_error.zig rename to test/cases/compile_errors/attempt_to_cast_enum_literal_to_error.zig index f1c30815fc..cc740d9038 100644 --- a/test/cases/compile_errors/stage1/obj/attempt_to_cast_enum_literal_to_error.zig +++ b/test/cases/compile_errors/attempt_to_cast_enum_literal_to_error.zig @@ -5,7 +5,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:9: error: expected type 'error{Hi}', found '@Type(.EnumLiteral)' +// :3:10: error: expected type 'error{Hi}', found '@TypeOf(.enum_literal)' diff --git a/test/cases/compile_errors/attempt_to_close_over_comptime_variable_from_outer_scope.zig b/test/cases/compile_errors/attempt_to_close_over_comptime_variable_from_outer_scope.zig new file mode 100644 index 0000000000..d27f2854dc --- /dev/null +++ b/test/cases/compile_errors/attempt_to_close_over_comptime_variable_from_outer_scope.zig @@ -0,0 +1,14 @@ +fn SimpleList(comptime L: usize) type { + var T = u8; + return struct { + array: [L]T, + }; +} + +// error +// backend=stage2 +// target=native +// +// :4:19: error: mutable 'T' not accessible from here +// :2:9: note: declared mutable here +// :3:12: note: crosses namespace boundary here diff --git a/test/cases/compile_errors/stage1/obj/attempt_to_create_17_bit_float_type.zig b/test/cases/compile_errors/attempt_to_create_17_bit_float_type.zig similarity index 65% rename from test/cases/compile_errors/stage1/obj/attempt_to_create_17_bit_float_type.zig rename to test/cases/compile_errors/attempt_to_create_17_bit_float_type.zig index ba7393fcb7..6748fa1edc 100644 --- a/test/cases/compile_errors/stage1/obj/attempt_to_create_17_bit_float_type.zig +++ b/test/cases/compile_errors/attempt_to_create_17_bit_float_type.zig @@ -4,7 +4,7 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:16: error: 17-bit float unsupported +// :3:9: error: 17-bit float unsupported diff --git a/test/cases/compile_errors/attempted_double_ampersand.zig b/test/cases/compile_errors/attempted_double_ampersand.zig new file mode 100644 index 0000000000..3fd7779ddc --- /dev/null +++ b/test/cases/compile_errors/attempted_double_ampersand.zig @@ -0,0 +1,12 @@ +export fn entry(a: bool, b: bool) i32 { + if (a && b) { + return 1234; + } + return 5678; +} + +// error +// backend=stage2 +// target=native +// +// :2:11: error: ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND diff --git a/test/cases/compile_errors/attempted_double_pipe_on_boolean_values.zig b/test/cases/compile_errors/attempted_double_pipe_on_boolean_values.zig new file mode 100644 index 0000000000..59b5bde8af --- /dev/null +++ b/test/cases/compile_errors/attempted_double_pipe_on_boolean_values.zig @@ -0,0 +1,13 @@ +export fn entry(a: bool, b: bool) i32 { + if (a || b) { + return 1234; + } + return 5678; +} + +// error +// backend=stage2 +// target=native +// +// :2:9: error: expected error set type, found 'bool' +// :2:11: note: '||' merges error sets; 'or' performs boolean OR diff --git a/test/cases/compile_errors/stage1/obj/attempted_implicit_cast_from_T_to_slice_const_T.zig b/test/cases/compile_errors/attempted_implicit_cast_from_T_to_slice_const_T.zig similarity index 54% rename from test/cases/compile_errors/stage1/obj/attempted_implicit_cast_from_T_to_slice_const_T.zig rename to test/cases/compile_errors/attempted_implicit_cast_from_T_to_slice_const_T.zig index c3b0847129..1d4afd55ac 100644 --- a/test/cases/compile_errors/stage1/obj/attempted_implicit_cast_from_T_to_slice_const_T.zig +++ b/test/cases/compile_errors/attempted_implicit_cast_from_T_to_slice_const_T.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:30: error: expected type '[*]const bool', found 'bool' +// :2:30: error: expected type '[*]const bool', found 'bool' diff --git a/test/cases/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_sliceT.zig b/test/cases/compile_errors/attempted_implicit_cast_from_const_T_to_sliceT.zig similarity index 58% rename from test/cases/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_sliceT.zig rename to test/cases/compile_errors/attempted_implicit_cast_from_const_T_to_sliceT.zig index 3ff9937983..0634b4696f 100644 --- a/test/cases/compile_errors/stage1/obj/attempted_implicit_cast_from_const_T_to_sliceT.zig +++ b/test/cases/compile_errors/attempted_implicit_cast_from_const_T_to_sliceT.zig @@ -5,7 +5,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:23: error: expected type '[]u32', found '*const u32' +// :3:22: error: expected type '[]u32', found '*const u32' diff --git a/test/cases/compile_errors/stage1/obj/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig b/test/cases/compile_errors/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig similarity index 68% rename from test/cases/compile_errors/stage1/obj/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig rename to test/cases/compile_errors/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig index 7e44ad5057..0c056cb39b 100644 --- a/test/cases/compile_errors/stage1/obj/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig +++ b/test/cases/compile_errors/bad_identifier_in_function_with_struct_defined_inside_function_which_references_local_const.zig @@ -11,7 +11,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:8:5: error: use of undeclared identifier 'bogus' +// :8:5: error: use of undeclared identifier 'bogus' diff --git a/test/cases/compile_errors/bad_import.zig b/test/cases/compile_errors/bad_import.zig new file mode 100644 index 0000000000..49e78a4be4 --- /dev/null +++ b/test/cases/compile_errors/bad_import.zig @@ -0,0 +1,7 @@ +const bogus = @import("bogus-does-not-exist.zig",); + +// error +// backend=stage2 +// target=native +// +// :1:23: error: unable to load '${DIR}bogus-does-not-exist.zig': FileNotFound diff --git a/test/cases/compile_errors/bad_splat_type.zig b/test/cases/compile_errors/bad_splat_type.zig new file mode 100644 index 0000000000..f32fcb4fb7 --- /dev/null +++ b/test/cases/compile_errors/bad_splat_type.zig @@ -0,0 +1,11 @@ +export fn entry() void { + const c = 4; + var v = @splat(4, c); + _ = v; +} + +// error +// backend=stage2 +// target=native +// +// :3:23: error: expected integer, float, bool, or pointer for the vector element type; found 'comptime_int' diff --git a/test/cases/compile_errors/stage1/test/binary_OR_operator_on_error_sets.zig b/test/cases/compile_errors/binary_OR_operator_on_error_sets.zig similarity index 54% rename from test/cases/compile_errors/stage1/test/binary_OR_operator_on_error_sets.zig rename to test/cases/compile_errors/binary_OR_operator_on_error_sets.zig index eb2d21c6ed..9955194f95 100644 --- a/test/cases/compile_errors/stage1/test/binary_OR_operator_on_error_sets.zig +++ b/test/cases/compile_errors/binary_OR_operator_on_error_sets.zig @@ -6,8 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:2:18: error: invalid operands to binary expression: 'error{A}' and 'error{B}' +// :2:18: error: invalid operands to binary bitwise expression: 'ErrorSet' and 'ErrorSet' diff --git a/test/cases/compile_errors/stage1/obj/binary_not_on_number_literal.zig b/test/cases/compile_errors/binary_not_on_number_literal.zig similarity index 72% rename from test/cases/compile_errors/stage1/obj/binary_not_on_number_literal.zig rename to test/cases/compile_errors/binary_not_on_number_literal.zig index 34ddf9adfa..cb57ca9ee1 100644 --- a/test/cases/compile_errors/stage1/obj/binary_not_on_number_literal.zig +++ b/test/cases/compile_errors/binary_not_on_number_literal.zig @@ -5,7 +5,7 @@ var block_aligned_stuff: usize = (4 + TINY_QUANTUM_SIZE) & ~(TINY_QUANTUM_SIZE - export fn entry() usize { return @sizeOf(@TypeOf(block_aligned_stuff)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:60: error: unable to perform binary not operation on type 'comptime_int' +// :3:60: error: unable to perform binary not operation on type 'comptime_int' diff --git a/test/cases/compile_errors/bitCast_same_size_but_bit_count_mismatch.zig b/test/cases/compile_errors/bitCast_same_size_but_bit_count_mismatch.zig new file mode 100644 index 0000000000..f67a5d139f --- /dev/null +++ b/test/cases/compile_errors/bitCast_same_size_but_bit_count_mismatch.zig @@ -0,0 +1,10 @@ +export fn entry(byte: u8) void { + var oops = @bitCast(u7, byte); + _ = oops; +} + +// error +// backend=stage2 +// target=native +// +// :2:29: error: @bitCast size mismatch: destination type 'u7' has 7 bits but source type 'u8' has 8 bits diff --git a/test/cases/compile_errors/bitCast_with_different_sizes_inside_an_expression.zig b/test/cases/compile_errors/bitCast_with_different_sizes_inside_an_expression.zig new file mode 100644 index 0000000000..8951eee5c0 --- /dev/null +++ b/test/cases/compile_errors/bitCast_with_different_sizes_inside_an_expression.zig @@ -0,0 +1,10 @@ +export fn entry() void { + var foo = (@bitCast(u8, @as(f32, 1.0)) == 0xf); + _ = foo; +} + +// error +// backend=stage2 +// target=native +// +// :2:29: error: @bitCast size mismatch: destination type 'u8' has 8 bits but source type 'f32' has 32 bits diff --git a/test/cases/compile_errors/bit_shifting_only_works_on_integer_types.zig b/test/cases/compile_errors/bit_shifting_only_works_on_integer_types.zig new file mode 100644 index 0000000000..b6362e83c8 --- /dev/null +++ b/test/cases/compile_errors/bit_shifting_only_works_on_integer_types.zig @@ -0,0 +1,10 @@ +export fn entry() void { + const x = &@as(u8, 1) << 10; + _ = x; +} + +// error +// backend=stage2 +// target=native +// +// :2:15: error: bit shifting operation expected integer type, found '*const u8' diff --git a/test/cases/compile_errors/bogus_method_call_on_slice.zig b/test/cases/compile_errors/bogus_method_call_on_slice.zig new file mode 100644 index 0000000000..5471871a23 --- /dev/null +++ b/test/cases/compile_errors/bogus_method_call_on_slice.zig @@ -0,0 +1,11 @@ +var self = "aoeu"; +fn f(m: []const u8) void { + m.copy(u8, self[0..], m); +} +export fn entry() usize { return @sizeOf(@TypeOf(&f)); } + +// error +// backend=stage2 +// target=native +// +// :3:6: error: type '[]const u8' has no field or member function named 'copy' diff --git a/test/cases/compile_errors/stage1/obj/branch_on_undefined_value.zig b/test/cases/compile_errors/branch_on_undefined_value.zig similarity index 57% rename from test/cases/compile_errors/stage1/obj/branch_on_undefined_value.zig rename to test/cases/compile_errors/branch_on_undefined_value.zig index f4b922a332..a0a3be83cf 100644 --- a/test/cases/compile_errors/stage1/obj/branch_on_undefined_value.zig +++ b/test/cases/compile_errors/branch_on_undefined_value.zig @@ -3,7 +3,7 @@ const x = if (undefined) true else false; export fn entry() usize { return @sizeOf(@TypeOf(x)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:15: error: use of undefined value here causes undefined behavior +// :1:15: error: use of undefined value here causes undefined behavior diff --git a/test/cases/compile_errors/stage1/obj/call_assigned_to_constant.zig b/test/cases/compile_errors/call_assigned_to_constant.zig similarity index 72% rename from test/cases/compile_errors/stage1/obj/call_assigned_to_constant.zig rename to test/cases/compile_errors/call_assigned_to_constant.zig index ca4e8b67cd..7533acbf05 100644 --- a/test/cases/compile_errors/stage1/obj/call_assigned_to_constant.zig +++ b/test/cases/compile_errors/call_assigned_to_constant.zig @@ -17,8 +17,8 @@ export fn entry1() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:12:14: error: cannot assign to constant -// tmp.zig:16:14: error: cannot assign to constant +// :12:14: error: cannot assign to constant +// :16:14: error: cannot assign to constant diff --git a/test/cases/compile_errors/stage1/obj/calling_var_args_extern_function_passing_array_instead_of_pointer.zig b/test/cases/compile_errors/calling_var_args_extern_function_passing_array_instead_of_pointer.zig similarity index 59% rename from test/cases/compile_errors/stage1/obj/calling_var_args_extern_function_passing_array_instead_of_pointer.zig rename to test/cases/compile_errors/calling_var_args_extern_function_passing_array_instead_of_pointer.zig index a71ab051f2..fe8037d287 100644 --- a/test/cases/compile_errors/stage1/obj/calling_var_args_extern_function_passing_array_instead_of_pointer.zig +++ b/test/cases/compile_errors/calling_var_args_extern_function_passing_array_instead_of_pointer.zig @@ -4,7 +4,7 @@ export fn entry() void { pub extern fn foo(format: *const u8, ...) void; // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:16: error: expected type '*const u8', found '[5:0]u8' +// :2:8: error: expected type '*const u8', found '[5:0]u8' diff --git a/test/cases/compile_errors/stage1/obj/cast_enum_literal_to_enum_but_it_doesnt_match.zig b/test/cases/compile_errors/cast_enum_literal_to_enum_but_it_doesnt_match.zig similarity index 51% rename from test/cases/compile_errors/stage1/obj/cast_enum_literal_to_enum_but_it_doesnt_match.zig rename to test/cases/compile_errors/cast_enum_literal_to_enum_but_it_doesnt_match.zig index ba53646efc..136bf612bf 100644 --- a/test/cases/compile_errors/stage1/obj/cast_enum_literal_to_enum_but_it_doesnt_match.zig +++ b/test/cases/compile_errors/cast_enum_literal_to_enum_but_it_doesnt_match.zig @@ -8,8 +8,8 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:6:20: error: enum 'Foo' has no field named 'c' -// tmp.zig:1:13: note: 'Foo' declared here +// :6:21: error: enum 'tmp.Foo' has no field named 'c' +// :1:13: note: enum declared here diff --git a/test/cases/compile_errors/cast_negative_integer_literal_to_usize.zig b/test/cases/compile_errors/cast_negative_integer_literal_to_usize.zig new file mode 100644 index 0000000000..29a5e522b9 --- /dev/null +++ b/test/cases/compile_errors/cast_negative_integer_literal_to_usize.zig @@ -0,0 +1,10 @@ +export fn entry() void { + const x = @as(usize, -10); + _ = x; +} + +// error +// backend=stage2 +// target=native +// +// :2:26: error: type 'usize' cannot represent integer value '-10' diff --git a/test/cases/compile_errors/stage1/obj/cast_negative_value_to_unsigned_integer.zig b/test/cases/compile_errors/cast_negative_value_to_unsigned_integer.zig similarity index 57% rename from test/cases/compile_errors/stage1/obj/cast_negative_value_to_unsigned_integer.zig rename to test/cases/compile_errors/cast_negative_value_to_unsigned_integer.zig index d2e3187140..2227005190 100644 --- a/test/cases/compile_errors/stage1/obj/cast_negative_value_to_unsigned_integer.zig +++ b/test/cases/compile_errors/cast_negative_value_to_unsigned_integer.zig @@ -10,8 +10,8 @@ export fn entry1() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:22: error: attempt to cast negative value to unsigned integer -// tmp.zig:8:27: error: cannot cast negative value -1 to unsigned integer type 'u32' +// :3:36: error: type 'u32' cannot represent integer value '-1' +// :8:27: error: type 'u32' cannot represent integer value '-1' diff --git a/test/cases/compile_errors/cast_unreachable.zig b/test/cases/compile_errors/cast_unreachable.zig new file mode 100644 index 0000000000..cf2331ff7e --- /dev/null +++ b/test/cases/compile_errors/cast_unreachable.zig @@ -0,0 +1,13 @@ +fn f() i32 { + return @as(i32, return 1); +} +export fn entry() void { _ = f(); } + +// error +// backend=stage2 +// target=native +// +// :2:12: error: unreachable code +// :2:21: note: control flow is diverted here +// :2:5: error: unreachable code +// :2:12: note: control flow is diverted here diff --git a/test/cases/compile_errors/stage1/obj/casting_bit_offset_pointer_to_regular_pointer.zig b/test/cases/compile_errors/casting_bit_offset_pointer_to_regular_pointer.zig similarity index 58% rename from test/cases/compile_errors/stage1/obj/casting_bit_offset_pointer_to_regular_pointer.zig rename to test/cases/compile_errors/casting_bit_offset_pointer_to_regular_pointer.zig index f109cdf295..083d68ee40 100644 --- a/test/cases/compile_errors/stage1/obj/casting_bit_offset_pointer_to_regular_pointer.zig +++ b/test/cases/compile_errors/casting_bit_offset_pointer_to_regular_pointer.zig @@ -12,10 +12,10 @@ fn bar(x: *const u3) u3 { return x.*; } -export fn entry() usize { return @sizeOf(@TypeOf(foo)); } +export fn entry() usize { return @sizeOf(@TypeOf(&foo)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:8:26: error: expected type '*const u3', found '*align(:3:1) const u3' +// :8:15: error: expected type '*const u3', found '*align(0:3:1) const u3' diff --git a/test/cases/compile_errors/stage1/obj/chained_comparison_operators.zig b/test/cases/compile_errors/chained_comparison_operators.zig similarity index 53% rename from test/cases/compile_errors/stage1/obj/chained_comparison_operators.zig rename to test/cases/compile_errors/chained_comparison_operators.zig index 1e4ed8d82e..32c6ee64b2 100644 --- a/test/cases/compile_errors/stage1/obj/chained_comparison_operators.zig +++ b/test/cases/compile_errors/chained_comparison_operators.zig @@ -3,7 +3,7 @@ export fn a(value: u32) bool { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:22: error: comparison operators cannot be chained +// :2:22: error: comparison operators cannot be chained diff --git a/test/cases/compile_errors/stage1/obj/cmpxchg_with_float.zig b/test/cases/compile_errors/cmpxchg_with_float.zig similarity index 56% rename from test/cases/compile_errors/stage1/obj/cmpxchg_with_float.zig rename to test/cases/compile_errors/cmpxchg_with_float.zig index 11f2dc21c3..1acfdd14d8 100644 --- a/test/cases/compile_errors/stage1/obj/cmpxchg_with_float.zig +++ b/test/cases/compile_errors/cmpxchg_with_float.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:22: error: expected bool, integer, enum or pointer type, found 'f32' +// :3:22: error: expected bool, integer, enum, or pointer type; found 'f32' diff --git a/test/cases/compile_errors/colliding_invalid_top_level_functions.zig b/test/cases/compile_errors/colliding_invalid_top_level_functions.zig new file mode 100644 index 0000000000..ee0711088d --- /dev/null +++ b/test/cases/compile_errors/colliding_invalid_top_level_functions.zig @@ -0,0 +1,12 @@ +fn func() bogus {} +fn func() bogus {} +export fn entry() usize { return @sizeOf(@TypeOf(func)); } + +// error +// backend=stage2 +// target=native +// +// :2:1: error: redeclaration of 'func' +// :1:1: note: other declaration here +// :1:11: error: use of undeclared identifier 'bogus' +// :2:11: error: use of undeclared identifier 'bogus' diff --git a/test/cases/compile_errors/stage1/test/combination_of_nosuspend_and_async.zig b/test/cases/compile_errors/combination_of_nosuspend_and_async.zig similarity index 56% rename from test/cases/compile_errors/stage1/test/combination_of_nosuspend_and_async.zig rename to test/cases/compile_errors/combination_of_nosuspend_and_async.zig index 43731c9648..dd853432b6 100644 --- a/test/cases/compile_errors/stage1/test/combination_of_nosuspend_and_async.zig +++ b/test/cases/compile_errors/combination_of_nosuspend_and_async.zig @@ -8,9 +8,8 @@ export fn entry() void { fn foo() void {} // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:4:9: error: suspend inside nosuspend block -// tmp.zig:2:5: note: nosuspend block here +// :4:9: error: suspend inside nosuspend block +// :2:5: note: nosuspend block here diff --git a/test/cases/compile_errors/stage1/obj/comparing_a_non-optional_pointer_against_null.zig b/test/cases/compile_errors/comparing_a_non-optional_pointer_against_null.zig similarity index 57% rename from test/cases/compile_errors/stage1/obj/comparing_a_non-optional_pointer_against_null.zig rename to test/cases/compile_errors/comparing_a_non-optional_pointer_against_null.zig index 62b5708034..4d8c5f53e9 100644 --- a/test/cases/compile_errors/stage1/obj/comparing_a_non-optional_pointer_against_null.zig +++ b/test/cases/compile_errors/comparing_a_non-optional_pointer_against_null.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:12: error: comparison of '*i32' with null +// :3:12: error: comparison of '*i32' with null diff --git a/test/cases/compile_errors/stage1/obj/comparison_operators_with_undefined_value.zig b/test/cases/compile_errors/comparison_operators_with_undefined_value.zig similarity index 56% rename from test/cases/compile_errors/stage1/obj/comparison_operators_with_undefined_value.zig rename to test/cases/compile_errors/comparison_operators_with_undefined_value.zig index 818a575fc8..b7b40d3178 100644 --- a/test/cases/compile_errors/stage1/obj/comparison_operators_with_undefined_value.zig +++ b/test/cases/compile_errors/comparison_operators_with_undefined_value.zig @@ -36,12 +36,12 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:5:11: error: use of undefined value here causes undefined behavior -// tmp.zig:11:11: error: use of undefined value here causes undefined behavior -// tmp.zig:17:11: error: use of undefined value here causes undefined behavior -// tmp.zig:23:11: error: use of undefined value here causes undefined behavior -// tmp.zig:29:11: error: use of undefined value here causes undefined behavior -// tmp.zig:35:11: error: use of undefined value here causes undefined behavior +// :5:11: error: use of undefined value here causes undefined behavior +// :11:11: error: use of undefined value here causes undefined behavior +// :17:11: error: use of undefined value here causes undefined behavior +// :23:11: error: use of undefined value here causes undefined behavior +// :29:11: error: use of undefined value here causes undefined behavior +// :35:11: error: use of undefined value here causes undefined behavior diff --git a/test/cases/compile_errors/stage1/obj/comparison_with_error_union_and_error_value.zig b/test/cases/compile_errors/comparison_with_error_union_and_error_value.zig similarity index 65% rename from test/cases/compile_errors/stage1/obj/comparison_with_error_union_and_error_value.zig rename to test/cases/compile_errors/comparison_with_error_union_and_error_value.zig index 16f3b21c3d..3a9eabcb95 100644 --- a/test/cases/compile_errors/stage1/obj/comparison_with_error_union_and_error_value.zig +++ b/test/cases/compile_errors/comparison_with_error_union_and_error_value.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:25: error: operator not allowed for type 'anyerror!i32' +// :3:25: error: operator == not allowed for type 'anyerror!i32' diff --git a/test/cases/compile_errors/stage1/obj/compileError_shows_traceback_of_references_that_caused_it.zig b/test/cases/compile_errors/compileError_shows_traceback_of_references_that_caused_it.zig similarity index 75% rename from test/cases/compile_errors/stage1/obj/compileError_shows_traceback_of_references_that_caused_it.zig rename to test/cases/compile_errors/compileError_shows_traceback_of_references_that_caused_it.zig index c58e9b7768..092cbbd400 100644 --- a/test/cases/compile_errors/stage1/obj/compileError_shows_traceback_of_references_that_caused_it.zig +++ b/test/cases/compile_errors/compileError_shows_traceback_of_references_that_caused_it.zig @@ -8,7 +8,7 @@ export fn entry() i32 { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:13: error: aoeu +// :1:13: error: aoeu diff --git a/test/cases/compile_errors/stage1/obj/compile_error_in_struct_init_expression.zig b/test/cases/compile_errors/compile_error_in_struct_init_expression.zig similarity index 67% rename from test/cases/compile_errors/stage1/obj/compile_error_in_struct_init_expression.zig rename to test/cases/compile_errors/compile_error_in_struct_init_expression.zig index 041d09b002..64a97a7a5b 100644 --- a/test/cases/compile_errors/stage1/obj/compile_error_in_struct_init_expression.zig +++ b/test/cases/compile_errors/compile_error_in_struct_init_expression.zig @@ -10,7 +10,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:14: error: use of undeclared identifier 'crap' +// :2:14: error: use of undeclared identifier 'crap' diff --git a/test/cases/compile_errors/stage1/obj/compile_error_when_evaluating_return_type_of_inferred_error_set.zig b/test/cases/compile_errors/compile_error_when_evaluating_return_type_of_inferred_error_set.zig similarity index 65% rename from test/cases/compile_errors/stage1/obj/compile_error_when_evaluating_return_type_of_inferred_error_set.zig rename to test/cases/compile_errors/compile_error_when_evaluating_return_type_of_inferred_error_set.zig index 7b1d11b0a4..fcb696ed64 100644 --- a/test/cases/compile_errors/stage1/obj/compile_error_when_evaluating_return_type_of_inferred_error_set.zig +++ b/test/cases/compile_errors/compile_error_when_evaluating_return_type_of_inferred_error_set.zig @@ -8,7 +8,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:11: error: use of undeclared identifier 'SymbolThatDoesNotExist' +// :2:11: error: use of undeclared identifier 'SymbolThatDoesNotExist' diff --git a/test/cases/compile_errors/stage1/obj/compile_log_a_pointer_to_an_opaque_value.zig b/test/cases/compile_errors/compile_log_a_pointer_to_an_opaque_value.zig similarity index 61% rename from test/cases/compile_errors/stage1/obj/compile_log_a_pointer_to_an_opaque_value.zig rename to test/cases/compile_errors/compile_log_a_pointer_to_an_opaque_value.zig index d585712b8a..252b6e5f14 100644 --- a/test/cases/compile_errors/stage1/obj/compile_log_a_pointer_to_an_opaque_value.zig +++ b/test/cases/compile_errors/compile_log_a_pointer_to_an_opaque_value.zig @@ -3,7 +3,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:5: error: found compile log statement +// :2:5: error: found compile log statement diff --git a/test/cases/compile_errors/stage1/obj/compile_log_statement_warning_deduplication_in_generic_fn.zig b/test/cases/compile_errors/compile_log_statement_warning_deduplication_in_generic_fn.zig similarity index 69% rename from test/cases/compile_errors/stage1/obj/compile_log_statement_warning_deduplication_in_generic_fn.zig rename to test/cases/compile_errors/compile_log_statement_warning_deduplication_in_generic_fn.zig index 991021932d..4eb0036d1d 100644 --- a/test/cases/compile_errors/stage1/obj/compile_log_statement_warning_deduplication_in_generic_fn.zig +++ b/test/cases/compile_errors/compile_log_statement_warning_deduplication_in_generic_fn.zig @@ -8,7 +8,8 @@ fn inner(comptime n: usize) void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:7:39: error: found compile log statement +// :7:39: error: found compile log statement +// :7:39: note: also here diff --git a/test/cases/compile_errors/comptime_cast_enum_to_union_but_field_has_payload.zig b/test/cases/compile_errors/comptime_cast_enum_to_union_but_field_has_payload.zig new file mode 100644 index 0000000000..d38738dae7 --- /dev/null +++ b/test/cases/compile_errors/comptime_cast_enum_to_union_but_field_has_payload.zig @@ -0,0 +1,18 @@ +const Letter = enum { A, B, C }; +const Value = union(Letter) { + A: i32, + B, + C, +}; +export fn entry() void { + var x: Value = Letter.A; + _ = x; +} + +// error +// backend=stage2 +// target=native +// +// :8:26: error: coercion from enum 'tmp.Letter' to union 'tmp.Value' must initialize 'i32' field 'A' +// :3:5: note: field 'A' declared here +// :2:15: note: union declared here diff --git a/test/cases/compile_errors/stage1/obj/comptime_slice_of_undefined_pointer_non-zero_len.zig b/test/cases/compile_errors/comptime_slice_of_undefined_pointer_non-zero_len.zig similarity index 58% rename from test/cases/compile_errors/stage1/obj/comptime_slice_of_undefined_pointer_non-zero_len.zig rename to test/cases/compile_errors/comptime_slice_of_undefined_pointer_non-zero_len.zig index ec7fbdc6e2..9640afe406 100644 --- a/test/cases/compile_errors/stage1/obj/comptime_slice_of_undefined_pointer_non-zero_len.zig +++ b/test/cases/compile_errors/comptime_slice_of_undefined_pointer_non-zero_len.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:41: error: non-zero length slice of undefined pointer +// :2:41: error: non-zero length slice of undefined pointer diff --git a/test/cases/compile_errors/stage1/obj/comptime_struct_field_no_init_value.zig b/test/cases/compile_errors/comptime_struct_field_no_init_value.zig similarity index 60% rename from test/cases/compile_errors/stage1/obj/comptime_struct_field_no_init_value.zig rename to test/cases/compile_errors/comptime_struct_field_no_init_value.zig index b889911a4d..c0cb470eee 100644 --- a/test/cases/compile_errors/stage1/obj/comptime_struct_field_no_init_value.zig +++ b/test/cases/compile_errors/comptime_struct_field_no_init_value.zig @@ -7,7 +7,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:5: error: comptime field without default initialization value +// :2:5: error: comptime field without default initialization value diff --git a/test/cases/compile_errors/const_is_a_statement_not_an_expression.zig b/test/cases/compile_errors/const_is_a_statement_not_an_expression.zig new file mode 100644 index 0000000000..576b028372 --- /dev/null +++ b/test/cases/compile_errors/const_is_a_statement_not_an_expression.zig @@ -0,0 +1,9 @@ +export fn f() void { + (const a = 0); +} + +// error +// backend=stage2 +// target=native +// +// :2:6: error: expected expression, found 'const' diff --git a/test/cases/compile_errors/stage1/obj/container_init_with_non-type.zig b/test/cases/compile_errors/container_init_with_non-type.zig similarity index 62% rename from test/cases/compile_errors/stage1/obj/container_init_with_non-type.zig rename to test/cases/compile_errors/container_init_with_non-type.zig index 3364b2f5b6..ea23020611 100644 --- a/test/cases/compile_errors/stage1/obj/container_init_with_non-type.zig +++ b/test/cases/compile_errors/container_init_with_non-type.zig @@ -4,7 +4,7 @@ const a = zero{1}; export fn entry() usize { return @sizeOf(@TypeOf(a)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:11: error: expected type 'type', found 'i32' +// :2:15: error: expected type 'type', found 'i32' diff --git a/test/cases/compile_errors/control_flow_uses_comptime_var_at_runtime.zig b/test/cases/compile_errors/control_flow_uses_comptime_var_at_runtime.zig new file mode 100644 index 0000000000..43ef20873c --- /dev/null +++ b/test/cases/compile_errors/control_flow_uses_comptime_var_at_runtime.zig @@ -0,0 +1,15 @@ +export fn foo() void { + comptime var i = 0; + while (i < 5) : (i += 1) { + bar(); + } +} + +fn bar() void { } + +// error +// backend=stage2 +// target=native +// +// :3:24: error: cannot store to comptime variable in non-inline loop +// :3:5: note: non-inline loop here diff --git a/test/cases/compile_errors/stage1/obj/declaration_between_fields.zig b/test/cases/compile_errors/declaration_between_fields.zig similarity index 56% rename from test/cases/compile_errors/stage1/obj/declaration_between_fields.zig rename to test/cases/compile_errors/declaration_between_fields.zig index 25a1ff5abc..4c296f9080 100644 --- a/test/cases/compile_errors/stage1/obj/declaration_between_fields.zig +++ b/test/cases/compile_errors/declaration_between_fields.zig @@ -16,9 +16,9 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:9:5: error: declarations are not allowed between container fields -// tmp.zig:5:5: note: field before declarations here -// tmp.zig:12:5: note: field after declarations here +// :9:5: error: declarations are not allowed between container fields +// :5:5: note: field before declarations here +// :12:5: note: field after declarations here diff --git a/test/cases/compile_errors/declaration_with_same_name_as_primitive_must_use_special_syntax.zig b/test/cases/compile_errors/declaration_with_same_name_as_primitive_must_use_special_syntax.zig new file mode 100644 index 0000000000..559e8e743a --- /dev/null +++ b/test/cases/compile_errors/declaration_with_same_name_as_primitive_must_use_special_syntax.zig @@ -0,0 +1,12 @@ +const u8 = u16; +export fn entry() void { + const a: u8 = 300; + _ = a; +} + +// error +// backend=stage2 +// target=native +// +// :1:7: error: name shadows primitive 'u8' +// :1:7: note: consider using @"u8" to disambiguate diff --git a/test/cases/compile_errors/deduplicate_undeclared_identifier.zig b/test/cases/compile_errors/deduplicate_undeclared_identifier.zig new file mode 100644 index 0000000000..34501e3c51 --- /dev/null +++ b/test/cases/compile_errors/deduplicate_undeclared_identifier.zig @@ -0,0 +1,13 @@ +export fn a() void { + x += 1; +} +export fn b() void { + x += 1; +} + +// error +// backend=stage2 +// target=native +// +// :2:5: error: use of undeclared identifier 'x' +// :5:5: error: use of undeclared identifier 'x' diff --git a/test/cases/compile_errors/stage1/obj/dont_implicit_cast_double_pointer_to_anyopaque.zig b/test/cases/compile_errors/dont_implicit_cast_double_pointer_to_anyopaque.zig similarity index 71% rename from test/cases/compile_errors/stage1/obj/dont_implicit_cast_double_pointer_to_anyopaque.zig rename to test/cases/compile_errors/dont_implicit_cast_double_pointer_to_anyopaque.zig index f61dec2ee0..2ba566f221 100644 --- a/test/cases/compile_errors/stage1/obj/dont_implicit_cast_double_pointer_to_anyopaque.zig +++ b/test/cases/compile_errors/dont_implicit_cast_double_pointer_to_anyopaque.zig @@ -7,7 +7,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:5:29: error: expected type '*anyopaque', found '**u32' +// :5:28: error: expected type '*anyopaque', found '**u32' diff --git a/test/cases/compile_errors/stage1/obj/duplicate_boolean_switch_value.zig b/test/cases/compile_errors/duplicate_boolean_switch_value.zig similarity index 72% rename from test/cases/compile_errors/stage1/obj/duplicate_boolean_switch_value.zig rename to test/cases/compile_errors/duplicate_boolean_switch_value.zig index 5b85c0a2eb..81ccebf37b 100644 --- a/test/cases/compile_errors/stage1/obj/duplicate_boolean_switch_value.zig +++ b/test/cases/compile_errors/duplicate_boolean_switch_value.zig @@ -16,8 +16,8 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:5:9: error: duplicate switch value -// tmp.zig:13:9: error: duplicate switch value +// :5:9: error: duplicate switch value +// :13:9: error: duplicate switch value diff --git a/test/cases/compile_errors/stage1/obj/duplicate_error_value_in_error_set.zig b/test/cases/compile_errors/duplicate_error_value_in_error_set.zig similarity index 53% rename from test/cases/compile_errors/stage1/obj/duplicate_error_value_in_error_set.zig rename to test/cases/compile_errors/duplicate_error_value_in_error_set.zig index 30d3bee8ab..5e9cddb975 100644 --- a/test/cases/compile_errors/stage1/obj/duplicate_error_value_in_error_set.zig +++ b/test/cases/compile_errors/duplicate_error_value_in_error_set.zig @@ -8,8 +8,8 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:5: error: duplicate error set field 'Bar' -// tmp.zig:2:5: note: previous declaration here +// :3:5: error: duplicate error set field 'Bar' +// :2:5: note: previous declaration here diff --git a/test/cases/compile_errors/stage1/obj/duplicate_field_in_struct_value_expression.zig b/test/cases/compile_errors/duplicate_field_in_struct_value_expression.zig similarity index 71% rename from test/cases/compile_errors/stage1/obj/duplicate_field_in_struct_value_expression.zig rename to test/cases/compile_errors/duplicate_field_in_struct_value_expression.zig index 1c0bac75e2..fa5bc6fb4e 100644 --- a/test/cases/compile_errors/stage1/obj/duplicate_field_in_struct_value_expression.zig +++ b/test/cases/compile_errors/duplicate_field_in_struct_value_expression.zig @@ -14,7 +14,8 @@ export fn f() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:11:9: error: duplicate field +// :11:10: error: duplicate field +// :8:10: note: other field here diff --git a/test/cases/compile_errors/stage1/obj/duplicate_struct_field.zig b/test/cases/compile_errors/duplicate_struct_field.zig similarity index 53% rename from test/cases/compile_errors/stage1/obj/duplicate_struct_field.zig rename to test/cases/compile_errors/duplicate_struct_field.zig index 93afdef70e..bb37178a85 100644 --- a/test/cases/compile_errors/stage1/obj/duplicate_struct_field.zig +++ b/test/cases/compile_errors/duplicate_struct_field.zig @@ -8,8 +8,9 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:5: error: duplicate struct field: 'Bar' -// tmp.zig:2:5: note: other field here +// :3:5: error: duplicate struct field: 'Bar' +// :2:5: note: other field here +// :1:13: note: struct declared here diff --git a/test/cases/compile_errors/stage1/obj/duplicate_union_field.zig b/test/cases/compile_errors/duplicate_union_field.zig similarity index 53% rename from test/cases/compile_errors/stage1/obj/duplicate_union_field.zig rename to test/cases/compile_errors/duplicate_union_field.zig index 5cbdcb7820..6a0265dd02 100644 --- a/test/cases/compile_errors/stage1/obj/duplicate_union_field.zig +++ b/test/cases/compile_errors/duplicate_union_field.zig @@ -8,8 +8,9 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:5: error: duplicate union field: 'Bar' -// tmp.zig:2:5: note: other field here +// :3:5: error: duplicate union field: 'Bar' +// :2:5: note: other field here +// :1:13: note: union declared here diff --git a/test/cases/compile_errors/stage1/obj/embedFile_with_bogus_file.zig b/test/cases/compile_errors/embedFile_with_bogus_file.zig similarity index 64% rename from test/cases/compile_errors/stage1/obj/embedFile_with_bogus_file.zig rename to test/cases/compile_errors/embedFile_with_bogus_file.zig index a03949b40a..fa05d1ed49 100644 --- a/test/cases/compile_errors/stage1/obj/embedFile_with_bogus_file.zig +++ b/test/cases/compile_errors/embedFile_with_bogus_file.zig @@ -3,7 +3,7 @@ const resource = @embedFile("bogus.txt",); export fn entry() usize { return @sizeOf(@TypeOf(resource)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:29: error: unable to find ' +// :1:29: error: unable to open 'bogus.txt': FileNotFound diff --git a/test/cases/compile_errors/empty_for_loop_body.zig b/test/cases/compile_errors/empty_for_loop_body.zig new file mode 100644 index 0000000000..5d08573e32 --- /dev/null +++ b/test/cases/compile_errors/empty_for_loop_body.zig @@ -0,0 +1,9 @@ +export fn a() void { + for(undefined) |x|; +} + +// error +// backend=stage2 +// target=native +// +// :2:23: error: expected block or assignment, found ';' diff --git a/test/cases/compile_errors/empty_if_body.zig b/test/cases/compile_errors/empty_if_body.zig new file mode 100644 index 0000000000..341b83c2b3 --- /dev/null +++ b/test/cases/compile_errors/empty_if_body.zig @@ -0,0 +1,9 @@ +export fn a() void { + if(true); +} + +// error +// backend=stage2 +// target=native +// +// :2:13: error: expected block or assignment, found ';' diff --git a/test/cases/compile_errors/stage1/obj/empty_switch_on_an_integer.zig b/test/cases/compile_errors/empty_switch_on_an_integer.zig similarity index 54% rename from test/cases/compile_errors/stage1/obj/empty_switch_on_an_integer.zig rename to test/cases/compile_errors/empty_switch_on_an_integer.zig index a7b8e2a81b..de4d7e9c65 100644 --- a/test/cases/compile_errors/stage1/obj/empty_switch_on_an_integer.zig +++ b/test/cases/compile_errors/empty_switch_on_an_integer.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:5: error: switch must handle all possibilities +// :3:5: error: switch must handle all possibilities diff --git a/test/cases/compile_errors/empty_while_loop_body.zig b/test/cases/compile_errors/empty_while_loop_body.zig new file mode 100644 index 0000000000..70320c204e --- /dev/null +++ b/test/cases/compile_errors/empty_while_loop_body.zig @@ -0,0 +1,9 @@ +export fn a() void { + while(true); +} + +// error +// backend=stage2 +// target=native +// +// :2:16: error: expected block or assignment, found ';' diff --git a/test/cases/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig b/test/cases/compile_errors/endless_loop_in_function_evaluation.zig similarity index 58% rename from test/cases/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig rename to test/cases/compile_errors/endless_loop_in_function_evaluation.zig index 302ee242e0..fb2af44b23 100644 --- a/test/cases/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig +++ b/test/cases/compile_errors/endless_loop_in_function_evaluation.zig @@ -6,7 +6,9 @@ fn fibonacci(x: i32) i32 { export fn entry() usize { return @sizeOf(@TypeOf(seventh_fib_number)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:21: error: evaluation exceeded 1000 backwards branches +// :3:21: error: evaluation exceeded 1000 backwards branches +// :3:21: note: called from here (999 times) +// :1:37: note: called from here diff --git a/test/cases/compile_errors/stage1/obj/enum_in_field_count_range_but_not_matching_tag.zig b/test/cases/compile_errors/enum_in_field_count_range_but_not_matching_tag.zig similarity index 53% rename from test/cases/compile_errors/stage1/obj/enum_in_field_count_range_but_not_matching_tag.zig rename to test/cases/compile_errors/enum_in_field_count_range_but_not_matching_tag.zig index e783cbee52..e79f6d478f 100644 --- a/test/cases/compile_errors/stage1/obj/enum_in_field_count_range_but_not_matching_tag.zig +++ b/test/cases/compile_errors/enum_in_field_count_range_but_not_matching_tag.zig @@ -8,8 +8,8 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:6:13: error: enum 'Foo' has no tag matching integer value 0 -// tmp.zig:1:13: note: 'Foo' declared here +// :6:13: error: enum 'tmp.Foo' has no tag with value '0' +// :1:13: note: enum declared here diff --git a/test/cases/compile_errors/enum_with_0_fields.zig b/test/cases/compile_errors/enum_with_0_fields.zig new file mode 100644 index 0000000000..f34065b69d --- /dev/null +++ b/test/cases/compile_errors/enum_with_0_fields.zig @@ -0,0 +1,7 @@ +const Foo = enum {}; + +// error +// backend=stage2 +// target=native +// +// :1:13: error: enum declarations must have at least one tag diff --git a/test/cases/compile_errors/stage1/obj/enum_with_declarations_unavailable_for_reify_type.zig b/test/cases/compile_errors/enum_with_declarations_unavailable_for_reify_type.zig similarity index 57% rename from test/cases/compile_errors/stage1/obj/enum_with_declarations_unavailable_for_reify_type.zig rename to test/cases/compile_errors/enum_with_declarations_unavailable_for_reify_type.zig index ffe9a2a78a..f23718c2ca 100644 --- a/test/cases/compile_errors/stage1/obj/enum_with_declarations_unavailable_for_reify_type.zig +++ b/test/cases/compile_errors/enum_with_declarations_unavailable_for_reify_type.zig @@ -3,7 +3,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:15: error: Type.Enum.decls must be empty for @Type +// :2:9: error: reified enums must have no decls diff --git a/test/cases/compile_errors/stage1/test/error_in_struct_initializer_doesnt_crash_the_compiler.zig b/test/cases/compile_errors/error_in_struct_initializer_doesnt_crash_the_compiler.zig similarity index 56% rename from test/cases/compile_errors/stage1/test/error_in_struct_initializer_doesnt_crash_the_compiler.zig rename to test/cases/compile_errors/error_in_struct_initializer_doesnt_crash_the_compiler.zig index 2858fd6a01..0c6c1cfb35 100644 --- a/test/cases/compile_errors/stage1/test/error_in_struct_initializer_doesnt_crash_the_compiler.zig +++ b/test/cases/compile_errors/error_in_struct_initializer_doesnt_crash_the_compiler.zig @@ -8,8 +8,9 @@ pub export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:4:9: error: duplicate struct field: 'e' +// :4:9: error: duplicate struct field: 'e' +// :3:9: note: other field here +// :2:22: note: struct declared here diff --git a/test/cases/compile_errors/stage1/obj/error_union_operator_with_non_error_set_LHS.zig b/test/cases/compile_errors/error_union_operator_with_non_error_set_LHS.zig similarity index 55% rename from test/cases/compile_errors/stage1/obj/error_union_operator_with_non_error_set_LHS.zig rename to test/cases/compile_errors/error_union_operator_with_non_error_set_LHS.zig index 785e42fb9b..cafca1ee55 100644 --- a/test/cases/compile_errors/stage1/obj/error_union_operator_with_non_error_set_LHS.zig +++ b/test/cases/compile_errors/error_union_operator_with_non_error_set_LHS.zig @@ -5,7 +5,7 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:15: error: expected error set type, found type 'i32' +// :2:15: error: expected error set type, found 'i32' diff --git a/test/cases/compile_errors/stage1/test/errors_in_for_loop_bodies_are_propagated.zig b/test/cases/compile_errors/errors_in_for_loop_bodies_are_propagated.zig similarity index 61% rename from test/cases/compile_errors/stage1/test/errors_in_for_loop_bodies_are_propagated.zig rename to test/cases/compile_errors/errors_in_for_loop_bodies_are_propagated.zig index d8be29e39b..bc30b5cbe6 100644 --- a/test/cases/compile_errors/stage1/test/errors_in_for_loop_bodies_are_propagated.zig +++ b/test/cases/compile_errors/errors_in_for_loop_bodies_are_propagated.zig @@ -4,8 +4,7 @@ pub export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:3:26: error: expected 2 arguments, found 1 +// :3:26: error: expected 2 arguments, found 1 diff --git a/test/cases/compile_errors/exceeded_maximum_bit_width_of_integer.zig b/test/cases/compile_errors/exceeded_maximum_bit_width_of_integer.zig new file mode 100644 index 0000000000..b7d5c95c25 --- /dev/null +++ b/test/cases/compile_errors/exceeded_maximum_bit_width_of_integer.zig @@ -0,0 +1,15 @@ +export fn entry1() void { + const T = u65536; + _ = T; +} +export fn entry2() void { + var x: i65536 = 1; + _ = x; +} + +// error +// backend=stage2 +// target=native +// +// :2:15: error: primitive integer type 'u65536' exceeds maximum bit width of 65535 +// :6:12: error: primitive integer type 'i65536' exceeds maximum bit width of 65535 diff --git a/test/cases/compile_errors/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig b/test/cases/compile_errors/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig new file mode 100644 index 0000000000..1aee4580ae --- /dev/null +++ b/test/cases/compile_errors/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig @@ -0,0 +1,9 @@ +export fn entry() i32 { + return @as(i32, 12.34); +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: fractional component prevents float value '12.34' from coercion to type 'i32' diff --git a/test/cases/compile_errors/stage1/obj/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig b/test/cases/compile_errors/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig similarity index 56% rename from test/cases/compile_errors/stage1/obj/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig rename to test/cases/compile_errors/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig index 4a41920107..22fc965769 100644 --- a/test/cases/compile_errors/stage1/obj/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig +++ b/test/cases/compile_errors/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig @@ -7,7 +7,8 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:5:13: error: error.B not a member of error set 'Set2' +// :5:13: error: 'error.B' not a member of error set 'error{A,C}' +// :2:14: note: error set declared here diff --git a/test/cases/compile_errors/stage1/test/export_with_empty_name_string.zig b/test/cases/compile_errors/export_with_empty_name_string.zig similarity index 54% rename from test/cases/compile_errors/stage1/test/export_with_empty_name_string.zig rename to test/cases/compile_errors/export_with_empty_name_string.zig index b882d11723..6403761d3c 100644 --- a/test/cases/compile_errors/stage1/test/export_with_empty_name_string.zig +++ b/test/cases/compile_errors/export_with_empty_name_string.zig @@ -4,8 +4,7 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:3:5: error: exported symbol name cannot be empty +// :3:21: error: exported symbol name cannot be empty diff --git a/test/cases/compile_errors/stage1/obj/extern_union_field_missing_type.zig b/test/cases/compile_errors/extern_union_field_missing_type.zig similarity index 67% rename from test/cases/compile_errors/stage1/obj/extern_union_field_missing_type.zig rename to test/cases/compile_errors/extern_union_field_missing_type.zig index 9d8532f8a1..6890e65714 100644 --- a/test/cases/compile_errors/stage1/obj/extern_union_field_missing_type.zig +++ b/test/cases/compile_errors/extern_union_field_missing_type.zig @@ -7,7 +7,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:5: error: union field missing type +// :2:5: error: union field missing type diff --git a/test/cases/compile_errors/stage1/obj/extern_variable_has_no_type.zig b/test/cases/compile_errors/extern_variable_has_no_type.zig similarity index 54% rename from test/cases/compile_errors/stage1/obj/extern_variable_has_no_type.zig rename to test/cases/compile_errors/extern_variable_has_no_type.zig index 58d67f14e1..7166cfc8eb 100644 --- a/test/cases/compile_errors/stage1/obj/extern_variable_has_no_type.zig +++ b/test/cases/compile_errors/extern_variable_has_no_type.zig @@ -4,7 +4,7 @@ pub export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:8: error: unable to infer variable type +// :1:8: error: unable to infer variable type diff --git a/test/cases/compile_errors/stage1/obj/fieldParentPtr-bad_field_name.zig b/test/cases/compile_errors/fieldParentPtr-bad_field_name.zig similarity index 57% rename from test/cases/compile_errors/stage1/obj/fieldParentPtr-bad_field_name.zig rename to test/cases/compile_errors/fieldParentPtr-bad_field_name.zig index 73022167f5..d3e487d3ce 100644 --- a/test/cases/compile_errors/stage1/obj/fieldParentPtr-bad_field_name.zig +++ b/test/cases/compile_errors/fieldParentPtr-bad_field_name.zig @@ -6,7 +6,8 @@ export fn foo(a: *i32) *Foo { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:5:33: error: struct 'Foo' has no field 'a' +// :5:33: error: no field named 'a' in struct 'tmp.Foo' +// :1:20: note: struct declared here diff --git a/test/cases/compile_errors/stage1/obj/fieldParentPtr-non_struct.zig b/test/cases/compile_errors/fieldParentPtr-non_struct.zig similarity index 61% rename from test/cases/compile_errors/stage1/obj/fieldParentPtr-non_struct.zig rename to test/cases/compile_errors/fieldParentPtr-non_struct.zig index 16b9a9b332..b6e53149c3 100644 --- a/test/cases/compile_errors/stage1/obj/fieldParentPtr-non_struct.zig +++ b/test/cases/compile_errors/fieldParentPtr-non_struct.zig @@ -4,7 +4,7 @@ export fn foo(a: *i32) *Foo { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:28: error: expected struct type, found 'i32' +// :3:28: error: expected struct type, found 'i32' diff --git a/test/cases/compile_errors/field_type_supplied_in_an_enum.zig b/test/cases/compile_errors/field_type_supplied_in_an_enum.zig new file mode 100644 index 0000000000..20248c2355 --- /dev/null +++ b/test/cases/compile_errors/field_type_supplied_in_an_enum.zig @@ -0,0 +1,12 @@ +const Letter = enum { + A: void, + B, + C, +}; + +// error +// backend=stage2 +// target=native +// +// :2:8: error: enum fields do not have types +// :1:16: note: consider 'union(enum)' here to make it a tagged union diff --git a/test/cases/compile_errors/stage1/obj/function_call_assigned_to_incorrect_type.zig b/test/cases/compile_errors/function_call_assigned_to_incorrect_type.zig similarity index 66% rename from test/cases/compile_errors/stage1/obj/function_call_assigned_to_incorrect_type.zig rename to test/cases/compile_errors/function_call_assigned_to_incorrect_type.zig index 51bc82ffb8..7c164b8a86 100644 --- a/test/cases/compile_errors/stage1/obj/function_call_assigned_to_incorrect_type.zig +++ b/test/cases/compile_errors/function_call_assigned_to_incorrect_type.zig @@ -7,7 +7,7 @@ fn concat() [16]f32 { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:17: error: expected type '[4]f32', found '[16]f32' +// :3:17: error: expected type '[4]f32', found '[16]f32' diff --git a/test/cases/compile_errors/stage1/obj/function_prototype_with_no_body.zig b/test/cases/compile_errors/function_prototype_with_no_body.zig similarity index 53% rename from test/cases/compile_errors/stage1/obj/function_prototype_with_no_body.zig rename to test/cases/compile_errors/function_prototype_with_no_body.zig index 7926faaa44..12eeda58ca 100644 --- a/test/cases/compile_errors/stage1/obj/function_prototype_with_no_body.zig +++ b/test/cases/compile_errors/function_prototype_with_no_body.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:1: error: non-extern function has no body +// :1:1: error: non-extern function has no body diff --git a/test/cases/compile_errors/function_with_invalid_return_type.zig b/test/cases/compile_errors/function_with_invalid_return_type.zig new file mode 100644 index 0000000000..afae037ef1 --- /dev/null +++ b/test/cases/compile_errors/function_with_invalid_return_type.zig @@ -0,0 +1,7 @@ +export fn foo() boid {} + +// error +// backend=stage2 +// target=native +// +// :1:17: error: use of undeclared identifier 'boid' diff --git a/test/cases/compile_errors/stage1/obj/generic_function_call_assigned_to_incorrect_type.zig b/test/cases/compile_errors/generic_function_call_assigned_to_incorrect_type.zig similarity index 68% rename from test/cases/compile_errors/stage1/obj/generic_function_call_assigned_to_incorrect_type.zig rename to test/cases/compile_errors/generic_function_call_assigned_to_incorrect_type.zig index 72ed66b20a..a2e303670d 100644 --- a/test/cases/compile_errors/stage1/obj/generic_function_call_assigned_to_incorrect_type.zig +++ b/test/cases/compile_errors/generic_function_call_assigned_to_incorrect_type.zig @@ -7,7 +7,7 @@ fn myAlloc(comptime arg: type) anyerror!arg{ } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:18: error: expected type '[]i32', found 'anyerror!i32 +// :3:18: error: expected type '[]i32', found 'anyerror!i32' diff --git a/test/cases/compile_errors/stage1/obj/global_variable_initializer_must_be_constant_expression.zig b/test/cases/compile_errors/global_variable_initializer_must_be_constant_expression.zig similarity index 56% rename from test/cases/compile_errors/stage1/obj/global_variable_initializer_must_be_constant_expression.zig rename to test/cases/compile_errors/global_variable_initializer_must_be_constant_expression.zig index d01e828da6..e2694343e8 100644 --- a/test/cases/compile_errors/stage1/obj/global_variable_initializer_must_be_constant_expression.zig +++ b/test/cases/compile_errors/global_variable_initializer_must_be_constant_expression.zig @@ -3,7 +3,7 @@ const x = foo(); export fn entry() i32 { return x; } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:11: error: unable to evaluate constant expression +// :2:14: error: comptime call of extern function diff --git a/test/cases/compile_errors/hasDecl_with_non-container.zig b/test/cases/compile_errors/hasDecl_with_non-container.zig new file mode 100644 index 0000000000..a650c19c4b --- /dev/null +++ b/test/cases/compile_errors/hasDecl_with_non-container.zig @@ -0,0 +1,9 @@ +export fn entry() void { + _ = @hasDecl(i32, "hi"); +} + +// error +// backend=stage2 +// target=native +// +// :2:18: error: expected struct, enum, union, or opaque; found 'i32' diff --git a/test/cases/compile_errors/if_condition_is_bool_not_int.zig b/test/cases/compile_errors/if_condition_is_bool_not_int.zig new file mode 100644 index 0000000000..20262f1b65 --- /dev/null +++ b/test/cases/compile_errors/if_condition_is_bool_not_int.zig @@ -0,0 +1,9 @@ +export fn f() void { + if (0) {} +} + +// error +// backend=stage2 +// target=native +// +// :2:9: error: expected type 'bool', found 'comptime_int' diff --git a/test/cases/compile_errors/stage1/obj/ignored_assert-err-ok_return_value.zig b/test/cases/compile_errors/ignored_assert-err-ok_return_value.zig similarity index 63% rename from test/cases/compile_errors/stage1/obj/ignored_assert-err-ok_return_value.zig rename to test/cases/compile_errors/ignored_assert-err-ok_return_value.zig index 4d45ae1304..04f6b3bd49 100644 --- a/test/cases/compile_errors/stage1/obj/ignored_assert-err-ok_return_value.zig +++ b/test/cases/compile_errors/ignored_assert-err-ok_return_value.zig @@ -4,7 +4,7 @@ export fn foo() void { fn bar() anyerror!i32 { return 0; } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:11: error: expression value is ignored +// :2:11: error: expression value is ignored diff --git a/test/cases/compile_errors/stage1/obj/ignored_comptime_statement_value.zig b/test/cases/compile_errors/ignored_comptime_statement_value.zig similarity index 51% rename from test/cases/compile_errors/stage1/obj/ignored_comptime_statement_value.zig rename to test/cases/compile_errors/ignored_comptime_statement_value.zig index 0e82c9ae3e..7a5258fb1d 100644 --- a/test/cases/compile_errors/stage1/obj/ignored_comptime_statement_value.zig +++ b/test/cases/compile_errors/ignored_comptime_statement_value.zig @@ -3,7 +3,7 @@ export fn foo() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:15: error: expression value is ignored +// :2:15: error: expression value is ignored diff --git a/test/cases/compile_errors/stage1/obj/ignored_comptime_value.zig b/test/cases/compile_errors/ignored_comptime_value.zig similarity index 50% rename from test/cases/compile_errors/stage1/obj/ignored_comptime_value.zig rename to test/cases/compile_errors/ignored_comptime_value.zig index 0306ea72ff..6f207653d6 100644 --- a/test/cases/compile_errors/stage1/obj/ignored_comptime_value.zig +++ b/test/cases/compile_errors/ignored_comptime_value.zig @@ -3,7 +3,7 @@ export fn foo() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:5: error: expression value is ignored +// :2:5: error: expression value is ignored diff --git a/test/cases/compile_errors/stage1/obj/ignored_deferred_statement_value.zig b/test/cases/compile_errors/ignored_deferred_statement_value.zig similarity index 50% rename from test/cases/compile_errors/stage1/obj/ignored_deferred_statement_value.zig rename to test/cases/compile_errors/ignored_deferred_statement_value.zig index 1042c4f40a..6af541236a 100644 --- a/test/cases/compile_errors/stage1/obj/ignored_deferred_statement_value.zig +++ b/test/cases/compile_errors/ignored_deferred_statement_value.zig @@ -3,7 +3,7 @@ export fn foo() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:12: error: expression value is ignored +// :2:12: error: expression value is ignored diff --git a/test/cases/compile_errors/stage1/obj/ignored_return_value.zig b/test/cases/compile_errors/ignored_return_value.zig similarity index 57% rename from test/cases/compile_errors/stage1/obj/ignored_return_value.zig rename to test/cases/compile_errors/ignored_return_value.zig index b918523b37..ac3756acf5 100644 --- a/test/cases/compile_errors/stage1/obj/ignored_return_value.zig +++ b/test/cases/compile_errors/ignored_return_value.zig @@ -4,7 +4,7 @@ export fn foo() void { fn bar() i32 { return 0; } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:8: error: expression value is ignored +// :2:8: error: expression value is ignored diff --git a/test/cases/compile_errors/ignored_statement_value.zig b/test/cases/compile_errors/ignored_statement_value.zig new file mode 100644 index 0000000000..5c79d1f964 --- /dev/null +++ b/test/cases/compile_errors/ignored_statement_value.zig @@ -0,0 +1,9 @@ +export fn foo() void { + 1; +} + +// error +// backend=stage2 +// target=native +// +// :2:5: error: expression value is ignored diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-block_expr.zig b/test/cases/compile_errors/implicit_semicolon-block_expr.zig similarity index 62% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-block_expr.zig rename to test/cases/compile_errors/implicit_semicolon-block_expr.zig index a57da8ac0b..7dd82b897b 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-block_expr.zig +++ b/test/cases/compile_errors/implicit_semicolon-block_expr.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:11: error: expected ';' after statement +// :4:11: error: expected ';' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-block_statement.zig b/test/cases/compile_errors/implicit_semicolon-block_statement.zig similarity index 61% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-block_statement.zig rename to test/cases/compile_errors/implicit_semicolon-block_statement.zig index 2d9d850e75..189ba84d98 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-block_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-block_statement.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:9: error: expected ';' after statement +// :4:9: error: expected ';' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-comptime_expression.zig b/test/cases/compile_errors/implicit_semicolon-comptime_expression.zig similarity index 65% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-comptime_expression.zig rename to test/cases/compile_errors/implicit_semicolon-comptime_expression.zig index b4cd4de849..decbc352e8 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-comptime_expression.zig +++ b/test/cases/compile_errors/implicit_semicolon-comptime_expression.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:20: error: expected ';' after statement +// :4:20: error: expected ';' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-comptime_statement.zig b/test/cases/compile_errors/implicit_semicolon-comptime_statement.zig similarity index 64% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-comptime_statement.zig rename to test/cases/compile_errors/implicit_semicolon-comptime_statement.zig index 3b83ef92eb..d17db15924 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-comptime_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-comptime_statement.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:18: error: expected ';' after statement +// :4:18: error: expected ';' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-defer.zig b/test/cases/compile_errors/implicit_semicolon-defer.zig similarity index 63% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-defer.zig rename to test/cases/compile_errors/implicit_semicolon-defer.zig index 0ded4fcf26..57fd3a2626 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-defer.zig +++ b/test/cases/compile_errors/implicit_semicolon-defer.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:15: error: expected ';' after statement +// :4:15: error: expected ';' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-for_expression.zig b/test/cases/compile_errors/implicit_semicolon-for_expression.zig similarity index 67% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-for_expression.zig rename to test/cases/compile_errors/implicit_semicolon-for_expression.zig index 964a48d30b..c751384e11 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-for_expression.zig +++ b/test/cases/compile_errors/implicit_semicolon-for_expression.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:26: error: expected ';' after statement +// :4:26: error: expected ';' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-for_statement.zig b/test/cases/compile_errors/implicit_semicolon-for_statement.zig similarity index 63% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-for_statement.zig rename to test/cases/compile_errors/implicit_semicolon-for_statement.zig index 1320e4cc46..14709cef4c 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-for_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-for_statement.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:24: error: expected ';' or 'else' after statement +// :4:24: error: expected ';' or 'else' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_expression.zig b/test/cases/compile_errors/implicit_semicolon-if-else-if-else_expression.zig similarity index 72% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_expression.zig rename to test/cases/compile_errors/implicit_semicolon-if-else-if-else_expression.zig index 40b1a33d78..72a4fa7d3e 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_expression.zig +++ b/test/cases/compile_errors/implicit_semicolon-if-else-if-else_expression.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:45: error: expected ';' after statement +// :4:45: error: expected ';' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_statement.zig b/test/cases/compile_errors/implicit_semicolon-if-else-if-else_statement.zig similarity index 71% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_statement.zig rename to test/cases/compile_errors/implicit_semicolon-if-else-if-else_statement.zig index 8444fe654d..95135006ba 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if-else_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-if-else-if-else_statement.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:47: error: expected ';' after statement +// :4:47: error: expected ';' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if_expression.zig b/test/cases/compile_errors/implicit_semicolon-if-else-if_expression.zig similarity index 70% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if_expression.zig rename to test/cases/compile_errors/implicit_semicolon-if-else-if_expression.zig index 7466d64691..a29636bd1d 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if_expression.zig +++ b/test/cases/compile_errors/implicit_semicolon-if-else-if_expression.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:37: error: expected ';' after statement +// :4:37: error: expected ';' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if_statement.zig b/test/cases/compile_errors/implicit_semicolon-if-else-if_statement.zig similarity index 66% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if_statement.zig rename to test/cases/compile_errors/implicit_semicolon-if-else-if_statement.zig index 34e116b3f7..c62430a0a2 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else-if_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-if-else-if_statement.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:37: error: expected ';' or 'else' after statement +// :4:37: error: expected ';' or 'else' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else_expression.zig b/test/cases/compile_errors/implicit_semicolon-if-else_expression.zig similarity index 68% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else_expression.zig rename to test/cases/compile_errors/implicit_semicolon-if-else_expression.zig index 3c1e0add62..d5bee6e52b 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else_expression.zig +++ b/test/cases/compile_errors/implicit_semicolon-if-else_expression.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:28: error: expected ';' after statement +// :4:28: error: expected ';' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else_statement.zig b/test/cases/compile_errors/implicit_semicolon-if-else_statement.zig similarity index 67% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else_statement.zig rename to test/cases/compile_errors/implicit_semicolon-if-else_statement.zig index e9c52fb6bb..94df128626 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-if-else_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-if-else_statement.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:28: error: expected ';' after statement +// :4:28: error: expected ';' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-if_expression.zig b/test/cases/compile_errors/implicit_semicolon-if_expression.zig similarity index 65% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-if_expression.zig rename to test/cases/compile_errors/implicit_semicolon-if_expression.zig index 2082588e0e..339a5378cf 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-if_expression.zig +++ b/test/cases/compile_errors/implicit_semicolon-if_expression.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:20: error: expected ';' after statement +// :4:20: error: expected ';' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-if_statement.zig b/test/cases/compile_errors/implicit_semicolon-if_statement.zig similarity index 61% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-if_statement.zig rename to test/cases/compile_errors/implicit_semicolon-if_statement.zig index 26cefd0237..b8ccb5e401 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-if_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-if_statement.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:18: error: expected ';' or 'else' after statement +// :4:18: error: expected ';' or 'else' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-test_expression.zig b/test/cases/compile_errors/implicit_semicolon-test_expression.zig similarity index 67% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-test_expression.zig rename to test/cases/compile_errors/implicit_semicolon-test_expression.zig index c31f3520c1..2a37c0aa0e 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-test_expression.zig +++ b/test/cases/compile_errors/implicit_semicolon-test_expression.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:26: error: expected ';' after statement +// :4:26: error: expected ';' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-test_statement.zig b/test/cases/compile_errors/implicit_semicolon-test_statement.zig similarity index 63% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-test_statement.zig rename to test/cases/compile_errors/implicit_semicolon-test_statement.zig index 8abb1cbcb1..afe00eba75 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-test_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-test_statement.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:24: error: expected ';' or 'else' after statement +// :4:24: error: expected ';' or 'else' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-while-continue_expression.zig b/test/cases/compile_errors/implicit_semicolon-while-continue_expression.zig similarity index 68% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-while-continue_expression.zig rename to test/cases/compile_errors/implicit_semicolon-while-continue_expression.zig index 4d3219eb66..5587627597 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-while-continue_expression.zig +++ b/test/cases/compile_errors/implicit_semicolon-while-continue_expression.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:28: error: expected ';' after statement +// :4:28: error: expected ';' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-while-continue_statement.zig b/test/cases/compile_errors/implicit_semicolon-while-continue_statement.zig similarity index 64% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-while-continue_statement.zig rename to test/cases/compile_errors/implicit_semicolon-while-continue_statement.zig index c0b3370357..9bebe3861e 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-while-continue_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-while-continue_statement.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:26: error: expected ';' or 'else' after statement +// :4:26: error: expected ';' or 'else' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-while_expression.zig b/test/cases/compile_errors/implicit_semicolon-while_expression.zig similarity index 66% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-while_expression.zig rename to test/cases/compile_errors/implicit_semicolon-while_expression.zig index 39f49319d5..df388a7c39 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-while_expression.zig +++ b/test/cases/compile_errors/implicit_semicolon-while_expression.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:23: error: expected ';' after statement +// :4:23: error: expected ';' after statement diff --git a/test/cases/compile_errors/stage1/obj/implicit_semicolon-while_statement.zig b/test/cases/compile_errors/implicit_semicolon-while_statement.zig similarity index 61% rename from test/cases/compile_errors/stage1/obj/implicit_semicolon-while_statement.zig rename to test/cases/compile_errors/implicit_semicolon-while_statement.zig index 6c253ecf56..d9ed3d1e2a 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_semicolon-while_statement.zig +++ b/test/cases/compile_errors/implicit_semicolon-while_statement.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:18: error: expected ';' or 'else' after statement +// :4:18: error: expected ';' or 'else' after statement diff --git a/test/cases/compile_errors/import_outside_package_path.zig b/test/cases/compile_errors/import_outside_package_path.zig new file mode 100644 index 0000000000..0c0df59419 --- /dev/null +++ b/test/cases/compile_errors/import_outside_package_path.zig @@ -0,0 +1,9 @@ +comptime{ + _ = @import("../a.zig"); +} + +// error +// backend=stage2 +// target=native +// +// :2:17: error: import of file outside package path: '../a.zig' diff --git a/test/cases/compile_errors/stage1/obj/inferred_array_size_invalid_here.zig b/test/cases/compile_errors/inferred_array_size_invalid_here.zig similarity index 61% rename from test/cases/compile_errors/stage1/obj/inferred_array_size_invalid_here.zig rename to test/cases/compile_errors/inferred_array_size_invalid_here.zig index 2b2d944eab..084057dc8e 100644 --- a/test/cases/compile_errors/stage1/obj/inferred_array_size_invalid_here.zig +++ b/test/cases/compile_errors/inferred_array_size_invalid_here.zig @@ -9,8 +9,8 @@ export fn entry2() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:16: error: unable to infer array size -// tmp.zig:6:35: error: unable to infer array size +// :2:16: error: unable to infer array size +// :6:35: error: unable to infer array size diff --git a/test/cases/compile_errors/inferring_error_set_of_function_pointer.zig b/test/cases/compile_errors/inferring_error_set_of_function_pointer.zig new file mode 100644 index 0000000000..862e33906a --- /dev/null +++ b/test/cases/compile_errors/inferring_error_set_of_function_pointer.zig @@ -0,0 +1,9 @@ +comptime { + const z: ?fn()!void = null; +} + +// error +// backend=stage2 +// target=native +// +// :2:19: error: function prototype may not have inferred error set diff --git a/test/cases/compile_errors/stage1/obj/initializing_array_with_struct_syntax.zig b/test/cases/compile_errors/initializing_array_with_struct_syntax.zig similarity index 55% rename from test/cases/compile_errors/stage1/obj/initializing_array_with_struct_syntax.zig rename to test/cases/compile_errors/initializing_array_with_struct_syntax.zig index 2884c6193e..b8448b2758 100644 --- a/test/cases/compile_errors/stage1/obj/initializing_array_with_struct_syntax.zig +++ b/test/cases/compile_errors/initializing_array_with_struct_syntax.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:15: error: initializing array with struct syntax +// :2:15: error: initializing array with struct syntax diff --git a/test/cases/compile_errors/stage1/test/int-float_conversion_to_comptime_int-float.zig b/test/cases/compile_errors/int-float_conversion_to_comptime_int-float.zig similarity index 55% rename from test/cases/compile_errors/stage1/test/int-float_conversion_to_comptime_int-float.zig rename to test/cases/compile_errors/int-float_conversion_to_comptime_int-float.zig index de439168ae..0b77f03baa 100644 --- a/test/cases/compile_errors/stage1/test/int-float_conversion_to_comptime_int-float.zig +++ b/test/cases/compile_errors/int-float_conversion_to_comptime_int-float.zig @@ -8,9 +8,8 @@ export fn bar() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:3:35: error: unable to evaluate constant expression -// tmp.zig:7:37: error: unable to evaluate constant expression +// :3:35: error: unable to resolve comptime value +// :7:37: error: unable to resolve comptime value diff --git a/test/cases/compile_errors/stage1/obj/int_to_err_global_invalid_number.zig b/test/cases/compile_errors/int_to_err_global_invalid_number.zig similarity index 64% rename from test/cases/compile_errors/stage1/obj/int_to_err_global_invalid_number.zig rename to test/cases/compile_errors/int_to_err_global_invalid_number.zig index 5c078a751c..5bb9b75a8e 100644 --- a/test/cases/compile_errors/stage1/obj/int_to_err_global_invalid_number.zig +++ b/test/cases/compile_errors/int_to_err_global_invalid_number.zig @@ -9,7 +9,7 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:7:13: error: integer value 3 represents no error +// :7:25: error: integer value '3' represents no error diff --git a/test/cases/compile_errors/stage1/obj/int_to_err_non_global_invalid_number.zig b/test/cases/compile_errors/int_to_err_non_global_invalid_number.zig similarity index 62% rename from test/cases/compile_errors/stage1/obj/int_to_err_non_global_invalid_number.zig rename to test/cases/compile_errors/int_to_err_non_global_invalid_number.zig index 8c1e547746..0b774cd29e 100644 --- a/test/cases/compile_errors/stage1/obj/int_to_err_non_global_invalid_number.zig +++ b/test/cases/compile_errors/int_to_err_non_global_invalid_number.zig @@ -13,7 +13,8 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:11:13: error: error.B not a member of error set 'Set2' +// :11:13: error: 'error.B' not a member of error set 'error{A,C}' +// :5:14: note: error set declared here diff --git a/test/cases/compile_errors/stage1/obj/integer_overflow_error.zig b/test/cases/compile_errors/integer_overflow_error.zig similarity index 53% rename from test/cases/compile_errors/stage1/obj/integer_overflow_error.zig rename to test/cases/compile_errors/integer_overflow_error.zig index 117d77ed93..aa4725b7e7 100644 --- a/test/cases/compile_errors/stage1/obj/integer_overflow_error.zig +++ b/test/cases/compile_errors/integer_overflow_error.zig @@ -2,7 +2,7 @@ const x : u8 = 300; export fn entry() usize { return @sizeOf(@TypeOf(x)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:16: error: integer value 300 cannot be coerced to type 'u8' +// :1:16: error: type 'u8' cannot represent integer value '300' diff --git a/test/cases/compile_errors/stage1/obj/integer_underflow_error.zig b/test/cases/compile_errors/integer_underflow_error.zig similarity index 62% rename from test/cases/compile_errors/stage1/obj/integer_underflow_error.zig rename to test/cases/compile_errors/integer_underflow_error.zig index 15bd4c472b..120edd0838 100644 --- a/test/cases/compile_errors/stage1/obj/integer_underflow_error.zig +++ b/test/cases/compile_errors/integer_underflow_error.zig @@ -3,7 +3,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// :2:78: error: operation caused overflow +// :2:78: error: overflow of integer type 'usize' with value '-1' diff --git a/test/cases/llvm/invalid_address_space_coercion.zig b/test/cases/compile_errors/invalid_address_space_coercion.zig similarity index 73% rename from test/cases/llvm/invalid_address_space_coercion.zig rename to test/cases/compile_errors/invalid_address_space_coercion.zig index 6b72f1d5a8..fd49aecb32 100644 --- a/test/cases/llvm/invalid_address_space_coercion.zig +++ b/test/cases/compile_errors/invalid_address_space_coercion.zig @@ -10,4 +10,4 @@ pub fn main() void { // backend=stage2,llvm // target=x86_64-linux,x86_64-macos // -// :2:12: error: expected *i32, found *addrspace(.gs) i32 +// :2:12: error: expected type '*i32', found '*addrspace(.gs) i32' diff --git a/test/cases/compile_errors/invalid_array_elem_ty.zig b/test/cases/compile_errors/invalid_array_elem_ty.zig index c7c2597246..d1d7e4a070 100644 --- a/test/cases/compile_errors/invalid_array_elem_ty.zig +++ b/test/cases/compile_errors/invalid_array_elem_ty.zig @@ -9,4 +9,4 @@ pub export fn entry() void { // target=native // backend=stage2 // -// :4:1: error: expected type, found fn() type +// :4:1: error: expected type 'type', found 'fn() type' diff --git a/test/cases/compile_errors/stage1/test/invalid_assignments.zig b/test/cases/compile_errors/invalid_assignments.zig similarity index 50% rename from test/cases/compile_errors/stage1/test/invalid_assignments.zig rename to test/cases/compile_errors/invalid_assignments.zig index 75f45a8bcb..d68fbda395 100644 --- a/test/cases/compile_errors/stage1/test/invalid_assignments.zig +++ b/test/cases/compile_errors/invalid_assignments.zig @@ -11,10 +11,9 @@ export fn entry4() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:3:6: error: invalid left-hand side to assignment -// tmp.zig:7:7: error: invalid left-hand side to assignment -// tmp.zig:10:7: error: invalid left-hand side to assignment +// :3:6: error: invalid left-hand side to assignment +// :7:7: error: invalid left-hand side to assignment +// :10:7: error: invalid left-hand side to assignment diff --git a/test/cases/compile_errors/invalid_break_expression.zig b/test/cases/compile_errors/invalid_break_expression.zig new file mode 100644 index 0000000000..2e7d37e60b --- /dev/null +++ b/test/cases/compile_errors/invalid_break_expression.zig @@ -0,0 +1,9 @@ +export fn f() void { + break; +} + +// error +// backend=stage2 +// target=native +// +// :2:5: error: break expression outside loop diff --git a/test/cases/compile_errors/stage1/obj/invalid_builtin_fn.zig b/test/cases/compile_errors/invalid_builtin_fn.zig similarity index 53% rename from test/cases/compile_errors/stage1/obj/invalid_builtin_fn.zig rename to test/cases/compile_errors/invalid_builtin_fn.zig index 8f0e3633bb..5b7b832177 100644 --- a/test/cases/compile_errors/stage1/obj/invalid_builtin_fn.zig +++ b/test/cases/compile_errors/invalid_builtin_fn.zig @@ -3,7 +3,7 @@ fn f() @bogus(foo) { export fn entry() void { _ = f(); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:8: error: invalid builtin function: '@bogus' +// :1:8: error: invalid builtin function: '@bogus' diff --git a/test/cases/compile_errors/stage1/obj/invalid_comparison_for_function_pointers.zig b/test/cases/compile_errors/invalid_comparison_for_function_pointers.zig similarity index 61% rename from test/cases/compile_errors/stage1/obj/invalid_comparison_for_function_pointers.zig rename to test/cases/compile_errors/invalid_comparison_for_function_pointers.zig index 6d64b6cdaf..cd63c70259 100644 --- a/test/cases/compile_errors/stage1/obj/invalid_comparison_for_function_pointers.zig +++ b/test/cases/compile_errors/invalid_comparison_for_function_pointers.zig @@ -4,7 +4,7 @@ const invalid = foo > foo; export fn entry() usize { return @sizeOf(@TypeOf(invalid)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:21: error: operator not allowed for type 'fn() void' +// :2:21: error: operator > not allowed for type 'fn() void' diff --git a/test/cases/compile_errors/invalid_continue_expression.zig b/test/cases/compile_errors/invalid_continue_expression.zig new file mode 100644 index 0000000000..8f368d1b6a --- /dev/null +++ b/test/cases/compile_errors/invalid_continue_expression.zig @@ -0,0 +1,9 @@ +export fn f() void { + continue; +} + +// error +// backend=stage2 +// target=native +// +// :2:5: error: continue expression outside loop diff --git a/test/cases/compile_errors/stage1/obj/invalid_empty_unicode_escape.zig b/test/cases/compile_errors/invalid_empty_unicode_escape.zig similarity index 52% rename from test/cases/compile_errors/stage1/obj/invalid_empty_unicode_escape.zig rename to test/cases/compile_errors/invalid_empty_unicode_escape.zig index 57dd16a63d..5088bca6e7 100644 --- a/test/cases/compile_errors/stage1/obj/invalid_empty_unicode_escape.zig +++ b/test/cases/compile_errors/invalid_empty_unicode_escape.zig @@ -3,7 +3,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:19: error: empty unicode escape sequence +// :2:19: error: empty unicode escape sequence diff --git a/test/cases/compile_errors/invalid_exponent_in_float_literal-1.zig b/test/cases/compile_errors/invalid_exponent_in_float_literal-1.zig new file mode 100644 index 0000000000..27d302b2ee --- /dev/null +++ b/test/cases/compile_errors/invalid_exponent_in_float_literal-1.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: f128 = 0x1.0p1ab1; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:28: note: invalid byte: 'a' diff --git a/test/cases/compile_errors/invalid_exponent_in_float_literal-2.zig b/test/cases/compile_errors/invalid_exponent_in_float_literal-2.zig new file mode 100644 index 0000000000..a3aa8037e1 --- /dev/null +++ b/test/cases/compile_errors/invalid_exponent_in_float_literal-2.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: f128 = 0x1.0p50F; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:29: note: invalid byte: 'F' diff --git a/test/cases/compile_errors/invalid_field_access_in_comptime.zig b/test/cases/compile_errors/invalid_field_access_in_comptime.zig new file mode 100644 index 0000000000..672c2b74c9 --- /dev/null +++ b/test/cases/compile_errors/invalid_field_access_in_comptime.zig @@ -0,0 +1,7 @@ +comptime { var x = doesnt_exist.whatever; _ = x; } + +// error +// backend=stage2 +// target=native +// +// :1:20: error: use of undeclared identifier 'doesnt_exist' diff --git a/test/cases/compile_errors/stage1/obj/invalid_field_in_struct_value_expression.zig b/test/cases/compile_errors/invalid_field_in_struct_value_expression.zig similarity index 64% rename from test/cases/compile_errors/stage1/obj/invalid_field_in_struct_value_expression.zig rename to test/cases/compile_errors/invalid_field_in_struct_value_expression.zig index fae0a90b51..fc9667910b 100644 --- a/test/cases/compile_errors/stage1/obj/invalid_field_in_struct_value_expression.zig +++ b/test/cases/compile_errors/invalid_field_in_struct_value_expression.zig @@ -13,7 +13,8 @@ export fn f() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:10:9: error: no member named 'foo' in struct 'A' +// :10:10: error: no field named 'foo' in struct 'tmp.A' +// :1:11: note: struct declared here diff --git a/test/cases/compile_errors/stage1/test/invalid_float_casts.zig b/test/cases/compile_errors/invalid_float_casts.zig similarity index 55% rename from test/cases/compile_errors/stage1/test/invalid_float_casts.zig rename to test/cases/compile_errors/invalid_float_casts.zig index 2fe8003728..152c98182b 100644 --- a/test/cases/compile_errors/stage1/test/invalid_float_casts.zig +++ b/test/cases/compile_errors/invalid_float_casts.zig @@ -16,11 +16,10 @@ export fn qux() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:3:36: error: unable to evaluate constant expression -// tmp.zig:7:21: error: expected integer type, found 'f32' -// tmp.zig:11:26: error: expected int type, found 'f32' -// tmp.zig:15:25: error: expected float type, found 'u32' +// :3:36: error: unable to cast runtime value to 'comptime_float' +// :7:21: error: expected integer type, found 'f32' +// :11:26: error: expected integer type, found 'f32' +// :15:25: error: expected float type, found 'u32' diff --git a/test/cases/compile_errors/stage1/obj/invalid_float_literal.zig b/test/cases/compile_errors/invalid_float_literal.zig similarity index 71% rename from test/cases/compile_errors/stage1/obj/invalid_float_literal.zig rename to test/cases/compile_errors/invalid_float_literal.zig index 709609a027..076e36ade1 100644 --- a/test/cases/compile_errors/stage1/obj/invalid_float_literal.zig +++ b/test/cases/compile_errors/invalid_float_literal.zig @@ -7,7 +7,7 @@ pub fn main() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:5:29: error: expected expression, found '.' +// :5:29: error: expected expression, found '.' diff --git a/test/cases/compile_errors/stage1/test/invalid_int_casts.zig b/test/cases/compile_errors/invalid_int_casts.zig similarity index 55% rename from test/cases/compile_errors/stage1/test/invalid_int_casts.zig rename to test/cases/compile_errors/invalid_int_casts.zig index 9945751ef5..d220869201 100644 --- a/test/cases/compile_errors/stage1/test/invalid_int_casts.zig +++ b/test/cases/compile_errors/invalid_int_casts.zig @@ -16,11 +16,10 @@ export fn qux() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:3:32: error: unable to evaluate constant expression -// tmp.zig:7:21: error: expected float type, found 'u32' -// tmp.zig:11:26: error: expected float type, found 'u32' -// tmp.zig:15:23: error: expected integer type, found 'f32' +// :3:32: error: unable to cast runtime value to 'comptime_int' +// :7:21: error: expected float type, found 'u32' +// :11:26: error: expected float type, found 'u32' +// :15:23: error: expected integer or vector, found 'f32' diff --git a/test/cases/compile_errors/invalid_legacy_unicode_escape.zig b/test/cases/compile_errors/invalid_legacy_unicode_escape.zig new file mode 100644 index 0000000000..cc4e78f6e4 --- /dev/null +++ b/test/cases/compile_errors/invalid_legacy_unicode_escape.zig @@ -0,0 +1,10 @@ +export fn entry() void { + const a = '\U1234'; +} + +// error +// backend=stage2 +// target=native +// +// :2:15: error: expected expression, found 'invalid bytes' +// :2:18: note: invalid byte: '1' diff --git a/test/cases/compile_errors/stage1/obj/invalid_member_of_builtin_enum.zig b/test/cases/compile_errors/invalid_member_of_builtin_enum.zig similarity index 55% rename from test/cases/compile_errors/stage1/obj/invalid_member_of_builtin_enum.zig rename to test/cases/compile_errors/invalid_member_of_builtin_enum.zig index 166ebfc782..3edb17ffbf 100644 --- a/test/cases/compile_errors/stage1/obj/invalid_member_of_builtin_enum.zig +++ b/test/cases/compile_errors/invalid_member_of_builtin_enum.zig @@ -5,7 +5,8 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:29: error: container 'std.builtin.Mode' has no member called 'x86' +// :3:30: error: enum 'builtin.Mode' has no member named 'x86' +// :?:18: note: enum declared here diff --git a/test/cases/compile_errors/stage1/test/invalid_non-exhaustive_enum_to_union.zig b/test/cases/compile_errors/invalid_non-exhaustive_enum_to_union.zig similarity index 55% rename from test/cases/compile_errors/stage1/test/invalid_non-exhaustive_enum_to_union.zig rename to test/cases/compile_errors/invalid_non-exhaustive_enum_to_union.zig index 804e83a66a..c7fc39f769 100644 --- a/test/cases/compile_errors/stage1/test/invalid_non-exhaustive_enum_to_union.zig +++ b/test/cases/compile_errors/invalid_non-exhaustive_enum_to_union.zig @@ -19,9 +19,10 @@ export fn bar() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:12:16: error: runtime cast to union 'U' from non-exhaustive enum -// tmp.zig:17:16: error: no tag by value 15 +// :12:16: error: runtime coercion to union 'tmp.U' from non-exhaustive enum +// :1:11: note: enum declared here +// :17:16: error: union 'tmp.U' has no tag with value '15' +// :6:11: note: union declared here diff --git a/test/cases/compile_errors/stage1/obj/invalid_pointer_for_var_type.zig b/test/cases/compile_errors/invalid_pointer_for_var_type.zig similarity index 67% rename from test/cases/compile_errors/stage1/obj/invalid_pointer_for_var_type.zig rename to test/cases/compile_errors/invalid_pointer_for_var_type.zig index caf8c70797..ee193bab15 100644 --- a/test/cases/compile_errors/stage1/obj/invalid_pointer_for_var_type.zig +++ b/test/cases/compile_errors/invalid_pointer_for_var_type.zig @@ -7,7 +7,7 @@ export fn f() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:13: error: unable to evaluate constant expression +// :2:16: error: comptime call of extern function diff --git a/test/cases/llvm/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig b/test/cases/compile_errors/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig similarity index 73% rename from test/cases/llvm/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig rename to test/cases/compile_errors/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig index 26f68ce789..1560dcc46a 100644 --- a/test/cases/llvm/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig +++ b/test/cases/compile_errors/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig @@ -10,4 +10,4 @@ pub fn main() void { // backend=stage2,llvm // target=x86_64-linux,x86_64-macos // -// :2:12: error: expected *i32, found *addrspace(.gs) i32 +// :2:12: error: expected type '*i32', found '*addrspace(.gs) i32' diff --git a/test/cases/compile_errors/stage1/obj/invalid_pointer_syntax.zig b/test/cases/compile_errors/invalid_pointer_syntax.zig similarity index 54% rename from test/cases/compile_errors/stage1/obj/invalid_pointer_syntax.zig rename to test/cases/compile_errors/invalid_pointer_syntax.zig index a4c823bde1..a8dbf438cb 100644 --- a/test/cases/compile_errors/stage1/obj/invalid_pointer_syntax.zig +++ b/test/cases/compile_errors/invalid_pointer_syntax.zig @@ -3,7 +3,7 @@ export fn foo() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:16: error: expected type expression, found ':' +// :2:16: error: expected type expression, found ':' diff --git a/test/cases/compile_errors/stage1/test/invalid_pointer_with_reify_type.zig b/test/cases/compile_errors/invalid_pointer_with_reify_type.zig similarity index 72% rename from test/cases/compile_errors/stage1/test/invalid_pointer_with_reify_type.zig rename to test/cases/compile_errors/invalid_pointer_with_reify_type.zig index bf1cf6bd6f..cac9e32894 100644 --- a/test/cases/compile_errors/stage1/test/invalid_pointer_with_reify_type.zig +++ b/test/cases/compile_errors/invalid_pointer_with_reify_type.zig @@ -12,8 +12,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:2:16: error: sentinels are only allowed on slices and unknown-length pointers +// :2:9: error: sentinels are only allowed on slices and unknown-length pointers diff --git a/test/cases/compile_errors/stage1/obj/invalid_shift_amount_error.zig b/test/cases/compile_errors/invalid_shift_amount_error.zig similarity index 57% rename from test/cases/compile_errors/stage1/obj/invalid_shift_amount_error.zig rename to test/cases/compile_errors/invalid_shift_amount_error.zig index 7c84f2db38..49852b5b7d 100644 --- a/test/cases/compile_errors/stage1/obj/invalid_shift_amount_error.zig +++ b/test/cases/compile_errors/invalid_shift_amount_error.zig @@ -5,7 +5,7 @@ fn f() u16 { export fn entry() u16 { return f(); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:17: error: integer value 8 cannot be coerced to type 'u3' +// :3:17: error: type 'u3' cannot represent integer value '8' diff --git a/test/cases/compile_errors/stage1/obj/invalid_struct_field.zig b/test/cases/compile_errors/invalid_struct_field.zig similarity index 54% rename from test/cases/compile_errors/stage1/obj/invalid_struct_field.zig rename to test/cases/compile_errors/invalid_struct_field.zig index 6264125bca..0a704e0270 100644 --- a/test/cases/compile_errors/stage1/obj/invalid_struct_field.zig +++ b/test/cases/compile_errors/invalid_struct_field.zig @@ -12,8 +12,10 @@ export fn g() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:6: error: no member named 'foo' in struct 'A' -// tmp.zig:10:16: error: no member named 'bar' in struct 'A' +// :4:7: error: no field named 'foo' in struct 'tmp.A' +// :1:11: note: struct declared here +// :10:17: error: no field named 'bar' in struct 'tmp.A' +// :1:11: note: struct declared here diff --git a/test/cases/compile_errors/stage1/obj/invalid_type.zig b/test/cases/compile_errors/invalid_type.zig similarity index 51% rename from test/cases/compile_errors/stage1/obj/invalid_type.zig rename to test/cases/compile_errors/invalid_type.zig index 8a2952e5f2..902d3652e7 100644 --- a/test/cases/compile_errors/stage1/obj/invalid_type.zig +++ b/test/cases/compile_errors/invalid_type.zig @@ -2,7 +2,7 @@ fn a() bogus {} export fn entry() void { _ = a(); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:8: error: use of undeclared identifier 'bogus' +// :1:8: error: use of undeclared identifier 'bogus' diff --git a/test/cases/compile_errors/stage1/obj/invalid_type_used_in_array_type.zig b/test/cases/compile_errors/invalid_type_used_in_array_type.zig similarity index 66% rename from test/cases/compile_errors/stage1/obj/invalid_type_used_in_array_type.zig rename to test/cases/compile_errors/invalid_type_used_in_array_type.zig index 3a7bc4272c..88da04bcc0 100644 --- a/test/cases/compile_errors/stage1/obj/invalid_type_used_in_array_type.zig +++ b/test/cases/compile_errors/invalid_type_used_in_array_type.zig @@ -8,7 +8,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:12: error: use of undeclared identifier 'SomeNonexistentType' +// :2:12: error: use of undeclared identifier 'SomeNonexistentType' diff --git a/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-1.zig b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-1.zig new file mode 100644 index 0000000000..f4be35883f --- /dev/null +++ b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-1.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: f128 = 0._0; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:23: note: invalid byte: '_' diff --git a/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-10.zig b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-10.zig new file mode 100644 index 0000000000..43090c28ba --- /dev/null +++ b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-10.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: f128 = 1.0__0e-1; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:25: note: invalid byte: '_' diff --git a/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-11.zig b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-11.zig new file mode 100644 index 0000000000..9910cc51d1 --- /dev/null +++ b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-11.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: f128 = 1.0e-1__0; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:28: note: invalid byte: '_' diff --git a/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-12.zig b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-12.zig new file mode 100644 index 0000000000..6dd7566b6a --- /dev/null +++ b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-12.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: f128 = 0_x0.0; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:23: note: invalid byte: 'x' diff --git a/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-13.zig b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-13.zig new file mode 100644 index 0000000000..eebba19f96 --- /dev/null +++ b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-13.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: f128 = 0x_0.0; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:23: note: invalid byte: '_' diff --git a/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-14.zig b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-14.zig new file mode 100644 index 0000000000..221b43cee5 --- /dev/null +++ b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-14.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: f128 = 0x0.0_p1; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:27: note: invalid byte: 'p' diff --git a/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-2.zig b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-2.zig new file mode 100644 index 0000000000..402d4e92e8 --- /dev/null +++ b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-2.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: f128 = 0_.0; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:23: note: invalid byte: '.' diff --git a/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-3.zig b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-3.zig new file mode 100644 index 0000000000..d6615e8e36 --- /dev/null +++ b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-3.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: f128 = 0.0_; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:25: note: invalid byte: ';' diff --git a/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-4.zig b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-4.zig new file mode 100644 index 0000000000..2b431a3cd2 --- /dev/null +++ b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-4.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: f128 = 1.0e_1; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:25: note: invalid byte: '_' diff --git a/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-5.zig b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-5.zig new file mode 100644 index 0000000000..b61585a28a --- /dev/null +++ b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-5.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: f128 = 1.0e+_1; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:26: note: invalid byte: '_' diff --git a/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-6.zig b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-6.zig new file mode 100644 index 0000000000..cb2aeffd38 --- /dev/null +++ b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-6.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: f128 = 1.0e-_1; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:26: note: invalid byte: '_' diff --git a/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-7.zig b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-7.zig new file mode 100644 index 0000000000..c22b00ff0c --- /dev/null +++ b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-7.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: f128 = 1.0e-1_; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:28: note: invalid byte: ';' diff --git a/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-9.zig b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-9.zig new file mode 100644 index 0000000000..db60f04bc1 --- /dev/null +++ b/test/cases/compile_errors/invalid_underscore_placement_in_float_literal-9.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: f128 = 1__0.0e-1; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:23: note: invalid byte: '_' diff --git a/test/cases/compile_errors/invalid_underscore_placement_in_int_literal-1.zig b/test/cases/compile_errors/invalid_underscore_placement_in_int_literal-1.zig new file mode 100644 index 0000000000..de519f2ddb --- /dev/null +++ b/test/cases/compile_errors/invalid_underscore_placement_in_int_literal-1.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: u128 = 0010_; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:26: note: invalid byte: ';' diff --git a/test/cases/compile_errors/invalid_underscore_placement_in_int_literal-2.zig b/test/cases/compile_errors/invalid_underscore_placement_in_int_literal-2.zig new file mode 100644 index 0000000000..80cb2981ce --- /dev/null +++ b/test/cases/compile_errors/invalid_underscore_placement_in_int_literal-2.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: u128 = 0b0010_; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:28: note: invalid byte: ';' diff --git a/test/cases/compile_errors/invalid_underscore_placement_in_int_literal-3.zig b/test/cases/compile_errors/invalid_underscore_placement_in_int_literal-3.zig new file mode 100644 index 0000000000..c82aba5065 --- /dev/null +++ b/test/cases/compile_errors/invalid_underscore_placement_in_int_literal-3.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: u128 = 0o0010_; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:28: note: invalid byte: ';' diff --git a/test/cases/compile_errors/invalid_underscore_placement_in_int_literal-4.zig b/test/cases/compile_errors/invalid_underscore_placement_in_int_literal-4.zig new file mode 100644 index 0000000000..145b009494 --- /dev/null +++ b/test/cases/compile_errors/invalid_underscore_placement_in_int_literal-4.zig @@ -0,0 +1,11 @@ +fn main() void { + var bad: u128 = 0x0010_; + _ = bad; +} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected expression, found 'invalid bytes' +// :2:28: note: invalid byte: ';' diff --git a/test/cases/compile_errors/issue_9346_return_outside_of_function_scope.zig b/test/cases/compile_errors/issue_9346_return_outside_of_function_scope.zig new file mode 100644 index 0000000000..86d21d4309 --- /dev/null +++ b/test/cases/compile_errors/issue_9346_return_outside_of_function_scope.zig @@ -0,0 +1,7 @@ +pub const empty = return 1; + +// error +// backend=stage2 +// target=native +// +// :1:19: error: 'return' outside function scope diff --git a/test/cases/compile_errors/stage1/obj/labeled_break_not_found.zig b/test/cases/compile_errors/labeled_break_not_found.zig similarity index 68% rename from test/cases/compile_errors/stage1/obj/labeled_break_not_found.zig rename to test/cases/compile_errors/labeled_break_not_found.zig index 754e1864b0..20185d0e04 100644 --- a/test/cases/compile_errors/stage1/obj/labeled_break_not_found.zig +++ b/test/cases/compile_errors/labeled_break_not_found.zig @@ -7,7 +7,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:20: error: label not found: 'outer' +// :4:20: error: label not found: 'outer' diff --git a/test/cases/compile_errors/stage1/obj/labeled_continue_not_found.zig b/test/cases/compile_errors/labeled_continue_not_found.zig similarity index 73% rename from test/cases/compile_errors/stage1/obj/labeled_continue_not_found.zig rename to test/cases/compile_errors/labeled_continue_not_found.zig index b112e9b355..6cef0866a8 100644 --- a/test/cases/compile_errors/stage1/obj/labeled_continue_not_found.zig +++ b/test/cases/compile_errors/labeled_continue_not_found.zig @@ -8,7 +8,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:5:23: error: label not found: 'outer' +// :5:23: error: label not found: 'outer' diff --git a/test/cases/compile_errors/local_shadows_global_that_occurs_later.zig b/test/cases/compile_errors/local_shadows_global_that_occurs_later.zig new file mode 100644 index 0000000000..1d88ac2c88 --- /dev/null +++ b/test/cases/compile_errors/local_shadows_global_that_occurs_later.zig @@ -0,0 +1,12 @@ +pub fn main() void { + var foo = true; + _ = foo; +} +fn foo() void {} + +// error +// backend=stage2 +// target=native +// +// :2:9: error: local shadows declaration of 'foo' +// :5:1: note: declared here diff --git a/test/cases/compile_errors/local_variable_redeclaration.zig b/test/cases/compile_errors/local_variable_redeclaration.zig new file mode 100644 index 0000000000..a0861ada49 --- /dev/null +++ b/test/cases/compile_errors/local_variable_redeclaration.zig @@ -0,0 +1,11 @@ +export fn f() void { + const a : i32 = 0; + var a = 0; +} + +// error +// backend=stage2 +// target=native +// +// :3:9: error: redeclaration of local constant 'a' +// :2:11: note: previous declaration here diff --git a/test/cases/compile_errors/local_variable_redeclares_parameter.zig b/test/cases/compile_errors/local_variable_redeclares_parameter.zig new file mode 100644 index 0000000000..f5693ca9b6 --- /dev/null +++ b/test/cases/compile_errors/local_variable_redeclares_parameter.zig @@ -0,0 +1,11 @@ +fn f(a : i32) void { + const a = 0; +} +export fn entry() void { f(1); } + +// error +// backend=stage2 +// target=native +// +// :2:11: error: redeclaration of function parameter 'a' +// :1:6: note: previous declaration here diff --git a/test/cases/compile_errors/stage1/obj/local_variable_shadowing_global.zig b/test/cases/compile_errors/local_variable_shadowing_global.zig similarity index 56% rename from test/cases/compile_errors/stage1/obj/local_variable_shadowing_global.zig rename to test/cases/compile_errors/local_variable_shadowing_global.zig index 3f1af3a4cb..1320bfcb41 100644 --- a/test/cases/compile_errors/stage1/obj/local_variable_shadowing_global.zig +++ b/test/cases/compile_errors/local_variable_shadowing_global.zig @@ -7,8 +7,8 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:5:9: error: local shadows declaration of 'Bar' -// tmp.zig:2:1: note: declared here +// :5:9: error: local shadows declaration of 'Bar' +// :2:1: note: declared here diff --git a/test/cases/compile_errors/locally_shadowing_a_primitive_type.zig b/test/cases/compile_errors/locally_shadowing_a_primitive_type.zig new file mode 100644 index 0000000000..7e7e994321 --- /dev/null +++ b/test/cases/compile_errors/locally_shadowing_a_primitive_type.zig @@ -0,0 +1,12 @@ +export fn foo() void { + const u8 = u16; + const a: u8 = 300; + _ = a; +} + +// error +// backend=stage2 +// target=native +// +// :2:11: error: name shadows primitive 'u8' +// :2:11: note: consider using @"u8" to disambiguate diff --git a/test/cases/compile_errors/main_function_with_bogus_args_type.zig b/test/cases/compile_errors/main_function_with_bogus_args_type.zig new file mode 100644 index 0000000000..dd02e1af34 --- /dev/null +++ b/test/cases/compile_errors/main_function_with_bogus_args_type.zig @@ -0,0 +1,7 @@ +pub fn main(args: [][]bogus) !void {_ = args;} + +// error +// backend=stage2 +// target=native +// +// :1:23: error: use of undeclared identifier 'bogus' diff --git a/test/cases/compile_errors/stage1/exe/main_missing_name.zig b/test/cases/compile_errors/main_missing_name.zig similarity index 52% rename from test/cases/compile_errors/stage1/exe/main_missing_name.zig rename to test/cases/compile_errors/main_missing_name.zig index 1a53e43cfb..8115eb2ae1 100644 --- a/test/cases/compile_errors/stage1/exe/main_missing_name.zig +++ b/test/cases/compile_errors/main_missing_name.zig @@ -1,8 +1,8 @@ pub fn (main) void {} // error -// backend=stage1 +// backend=stage2 // target=native // output_mode=Exe // -// tmp.zig:1:5: error: missing function name +// :1:5: error: missing function name diff --git a/test/cases/compile_errors/stage1/obj/missing_boolean_switch_value.zig b/test/cases/compile_errors/missing_boolean_switch_value.zig similarity index 58% rename from test/cases/compile_errors/stage1/obj/missing_boolean_switch_value.zig rename to test/cases/compile_errors/missing_boolean_switch_value.zig index 2e9d4b48ce..a811efee8a 100644 --- a/test/cases/compile_errors/stage1/obj/missing_boolean_switch_value.zig +++ b/test/cases/compile_errors/missing_boolean_switch_value.zig @@ -12,8 +12,8 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:15: error: switch must handle all possibilities -// tmp.zig:8:15: error: switch must handle all possibilities +// :2:15: error: switch must handle all possibilities +// :8:15: error: switch must handle all possibilities diff --git a/test/cases/compile_errors/stage1/obj/missing_else_clause.zig b/test/cases/compile_errors/missing_else_clause.zig similarity index 63% rename from test/cases/compile_errors/stage1/obj/missing_else_clause.zig rename to test/cases/compile_errors/missing_else_clause.zig index f331933437..716721bdea 100644 --- a/test/cases/compile_errors/stage1/obj/missing_else_clause.zig +++ b/test/cases/compile_errors/missing_else_clause.zig @@ -9,8 +9,8 @@ fn g(b: bool) void { export fn entry() void { f(true); g(true); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:21: error: expected type 'i32', found 'void' -// tmp.zig:6:15: error: incompatible types: 'i32' and 'void' +// :2:21: error: incompatible types: 'i32' and 'void' +// :6:15: error: incompatible types: 'i32' and 'void' diff --git a/test/cases/compile_errors/stage1/obj/missing_field_in_struct_value_expression.zig b/test/cases/compile_errors/missing_field_in_struct_value_expression.zig similarity index 73% rename from test/cases/compile_errors/stage1/obj/missing_field_in_struct_value_expression.zig rename to test/cases/compile_errors/missing_field_in_struct_value_expression.zig index 9eb4246c8f..600540d1e0 100644 --- a/test/cases/compile_errors/stage1/obj/missing_field_in_struct_value_expression.zig +++ b/test/cases/compile_errors/missing_field_in_struct_value_expression.zig @@ -14,7 +14,8 @@ export fn f() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:9:17: error: missing field: 'x' +// :9:17: error: missing struct field: x +// :1:11: note: struct 'tmp.A' declared here diff --git a/test/cases/compile_errors/missing_function_name.zig b/test/cases/compile_errors/missing_function_name.zig new file mode 100644 index 0000000000..e0cce9d86b --- /dev/null +++ b/test/cases/compile_errors/missing_function_name.zig @@ -0,0 +1,7 @@ +fn () void {} + +// error +// backend=stage2 +// target=native +// +// :1:1: error: missing function name diff --git a/test/cases/compile_errors/stage1/obj/missing_param_name.zig b/test/cases/compile_errors/missing_param_name.zig similarity index 61% rename from test/cases/compile_errors/stage1/obj/missing_param_name.zig rename to test/cases/compile_errors/missing_param_name.zig index febb16d2ca..88da902ea2 100644 --- a/test/cases/compile_errors/stage1/obj/missing_param_name.zig +++ b/test/cases/compile_errors/missing_param_name.zig @@ -2,7 +2,7 @@ fn f(i32) void {} export fn entry() usize { return @sizeOf(@TypeOf(f)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:6: error: missing parameter name +// :1:6: error: missing parameter name diff --git a/test/cases/compile_errors/stage1/obj/missing_parameter_name_of_generic_function.zig b/test/cases/compile_errors/missing_parameter_name_of_generic_function.zig similarity index 64% rename from test/cases/compile_errors/stage1/obj/missing_parameter_name_of_generic_function.zig rename to test/cases/compile_errors/missing_parameter_name_of_generic_function.zig index 041c0c4a47..edd19bd7de 100644 --- a/test/cases/compile_errors/stage1/obj/missing_parameter_name_of_generic_function.zig +++ b/test/cases/compile_errors/missing_parameter_name_of_generic_function.zig @@ -5,7 +5,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:9: error: missing parameter name +// :1:9: error: missing parameter name diff --git a/test/cases/compile_errors/stage1/obj/misspelled_type_with_pointer_only_reference.zig b/test/cases/compile_errors/misspelled_type_with_pointer_only_reference.zig similarity index 88% rename from test/cases/compile_errors/stage1/obj/misspelled_type_with_pointer_only_reference.zig rename to test/cases/compile_errors/misspelled_type_with_pointer_only_reference.zig index 8e2055a829..ef8fce6c80 100644 --- a/test/cases/compile_errors/stage1/obj/misspelled_type_with_pointer_only_reference.zig +++ b/test/cases/compile_errors/misspelled_type_with_pointer_only_reference.zig @@ -31,7 +31,7 @@ fn foo() void { export fn entry() usize { return @sizeOf(@TypeOf(foo)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:5:16: error: use of undeclared identifier 'JsonList' +// :5:16: error: use of undeclared identifier 'JsonList' diff --git a/test/cases/compile_errors/multiple_function_definitions.zig b/test/cases/compile_errors/multiple_function_definitions.zig new file mode 100644 index 0000000000..d07eaee257 --- /dev/null +++ b/test/cases/compile_errors/multiple_function_definitions.zig @@ -0,0 +1,10 @@ +fn a() void {} +fn a() void {} +export fn entry() void { a(); } + +// error +// backend=stage2 +// target=native +// +// :2:1: error: redeclaration of 'a' +// :1:1: note: other declaration here diff --git a/test/cases/compile_errors/stage1/test/nested_vectors.zig b/test/cases/compile_errors/nested_vectors.zig similarity index 59% rename from test/cases/compile_errors/stage1/test/nested_vectors.zig rename to test/cases/compile_errors/nested_vectors.zig index 9e87ac814b..9482c23005 100644 --- a/test/cases/compile_errors/stage1/test/nested_vectors.zig +++ b/test/cases/compile_errors/nested_vectors.zig @@ -6,8 +6,8 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:3:23: error: vector element type must be integer, float, bool, or pointer; '@Vector(4, u8)' is invalid +// :3:16: error: expected integer, float, bool, or pointer for the vector element type; found '@Vector(4, u8)' + diff --git a/test/cases/compile_errors/stage1/obj/non-const_expression_function_call_with_struct_return_value_outside_function.zig b/test/cases/compile_errors/non-const_expression_function_call_with_struct_return_value_outside_function.zig similarity index 71% rename from test/cases/compile_errors/stage1/obj/non-const_expression_function_call_with_struct_return_value_outside_function.zig rename to test/cases/compile_errors/non-const_expression_function_call_with_struct_return_value_outside_function.zig index 25e33d1435..fc33fa531e 100644 --- a/test/cases/compile_errors/stage1/obj/non-const_expression_function_call_with_struct_return_value_outside_function.zig +++ b/test/cases/compile_errors/non-const_expression_function_call_with_struct_return_value_outside_function.zig @@ -11,7 +11,8 @@ var global_side_effect = false; export fn entry() usize { return @sizeOf(@TypeOf(a)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:6:26: error: unable to evaluate constant expression +// :6:26: error: unable to resolve comptime value +// :4:17: note: called from here diff --git a/test/cases/compile_errors/stage1/obj/non-const_expression_in_struct_literal_outside_function.zig b/test/cases/compile_errors/non-const_expression_in_struct_literal_outside_function.zig similarity index 68% rename from test/cases/compile_errors/stage1/obj/non-const_expression_in_struct_literal_outside_function.zig rename to test/cases/compile_errors/non-const_expression_in_struct_literal_outside_function.zig index 46b94f1f96..2e0043c5ec 100644 --- a/test/cases/compile_errors/stage1/obj/non-const_expression_in_struct_literal_outside_function.zig +++ b/test/cases/compile_errors/non-const_expression_in_struct_literal_outside_function.zig @@ -7,7 +7,7 @@ extern fn get_it() i32; export fn entry() usize { return @sizeOf(@TypeOf(a)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:21: error: unable to evaluate constant expression +// :4:27: error: comptime call of extern function diff --git a/test/cases/compile_errors/stage1/obj/non-const_switch_number_literal.zig b/test/cases/compile_errors/non-const_switch_number_literal.zig similarity index 55% rename from test/cases/compile_errors/stage1/obj/non-const_switch_number_literal.zig rename to test/cases/compile_errors/non-const_switch_number_literal.zig index 005db548ed..01e31bb92c 100644 --- a/test/cases/compile_errors/stage1/obj/non-const_switch_number_literal.zig +++ b/test/cases/compile_errors/non-const_switch_number_literal.zig @@ -11,7 +11,8 @@ fn bar() i32 { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:5:17: error: cannot store runtime value in type 'comptime_int' +// :2:15: error: value with comptime only type 'comptime_int' depends on runtime control flow +// :2:26: note: runtime control flow here diff --git a/test/cases/compile_errors/non-exhaustive_enum_field_non_final.zig b/test/cases/compile_errors/non-exhaustive_enum_field_non_final.zig new file mode 100644 index 0000000000..cd5375464f --- /dev/null +++ b/test/cases/compile_errors/non-exhaustive_enum_field_non_final.zig @@ -0,0 +1,11 @@ +const B = enum(u1) { + a, + _, + b, +}; + +// error +// backend=stage2 +// target=native +// +// :3:5: error: '_' field of non-exhaustive enum must be last diff --git a/test/cases/compile_errors/non-exhaustive_enum_marker_assigned_a_value.zig b/test/cases/compile_errors/non-exhaustive_enum_marker_assigned_a_value.zig new file mode 100644 index 0000000000..c9ebb6af71 --- /dev/null +++ b/test/cases/compile_errors/non-exhaustive_enum_marker_assigned_a_value.zig @@ -0,0 +1,19 @@ +const A = enum { + a, + b, + _ = 1, +}; +const B = enum { + a, + b, + _, +}; +comptime { _ = A; _ = B; } + +// error +// backend=stage2 +// target=native +// +// :4:9: error: '_' is used to mark an enum as non-exhaustive and cannot be assigned a value +// :6:11: error: non-exhaustive enum missing integer tag type +// :9:5: note: marked non-exhaustive here diff --git a/test/cases/compile_errors/non-exhaustive_enum_specifies_every_value.zig b/test/cases/compile_errors/non-exhaustive_enum_specifies_every_value.zig new file mode 100644 index 0000000000..99d3711e1d --- /dev/null +++ b/test/cases/compile_errors/non-exhaustive_enum_specifies_every_value.zig @@ -0,0 +1,14 @@ +const C = enum(u1) { + a, + b, + _, +}; +pub export fn entry() void { + _ = C; +} + +// error +// backend=stage2 +// target=native +// +// :1:11: error: non-exhaustive enum specifies every value diff --git a/test/cases/compile_errors/stage1/obj/non-extern_function_with_var_args.zig b/test/cases/compile_errors/non-extern_function_with_var_args.zig similarity index 54% rename from test/cases/compile_errors/stage1/obj/non-extern_function_with_var_args.zig rename to test/cases/compile_errors/non-extern_function_with_var_args.zig index 9e5463d451..bb9d13a230 100644 --- a/test/cases/compile_errors/stage1/obj/non-extern_function_with_var_args.zig +++ b/test/cases/compile_errors/non-extern_function_with_var_args.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:14: error: expected type expression, found '...' +// :1:14: error: expected type expression, found '...' diff --git a/test/cases/compile_errors/stage1/obj/non-pure_function_returns_type.zig b/test/cases/compile_errors/non-pure_function_returns_type.zig similarity index 80% rename from test/cases/compile_errors/stage1/obj/non-pure_function_returns_type.zig rename to test/cases/compile_errors/non-pure_function_returns_type.zig index 9d1c642273..ceb3e0507f 100644 --- a/test/cases/compile_errors/stage1/obj/non-pure_function_returns_type.zig +++ b/test/cases/compile_errors/non-pure_function_returns_type.zig @@ -18,7 +18,8 @@ export fn function_with_return_type_type() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:7: error: unable to evaluate constant expression +// :3:7: error: unable to resolve comptime value +// :16:19: note: called from here diff --git a/test/cases/compile_errors/stage1/obj/non_float_passed_to_floatToInt.zig b/test/cases/compile_errors/non_float_passed_to_floatToInt.zig similarity index 60% rename from test/cases/compile_errors/stage1/obj/non_float_passed_to_floatToInt.zig rename to test/cases/compile_errors/non_float_passed_to_floatToInt.zig index c91025906a..2d4e0315be 100644 --- a/test/cases/compile_errors/stage1/obj/non_float_passed_to_floatToInt.zig +++ b/test/cases/compile_errors/non_float_passed_to_floatToInt.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:32: error: expected float type, found 'i32' +// :2:32: error: expected float type, found 'i32' diff --git a/test/cases/compile_errors/stage1/obj/non_int_passed_to_intToFloat.zig b/test/cases/compile_errors/non_int_passed_to_intToFloat.zig similarity index 55% rename from test/cases/compile_errors/stage1/obj/non_int_passed_to_intToFloat.zig rename to test/cases/compile_errors/non_int_passed_to_intToFloat.zig index 8f6acca51e..f40fa93df0 100644 --- a/test/cases/compile_errors/stage1/obj/non_int_passed_to_intToFloat.zig +++ b/test/cases/compile_errors/non_int_passed_to_intToFloat.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:32: error: expected int type, found 'comptime_float' +// :2:32: error: expected integer type, found 'comptime_float' diff --git a/test/cases/compile_errors/stage1/obj/non_pointer_given_to_ptrToInt.zig b/test/cases/compile_errors/non_pointer_given_to_ptrToInt.zig similarity index 55% rename from test/cases/compile_errors/stage1/obj/non_pointer_given_to_ptrToInt.zig rename to test/cases/compile_errors/non_pointer_given_to_ptrToInt.zig index 7824c0f796..27b5d6d1f7 100644 --- a/test/cases/compile_errors/stage1/obj/non_pointer_given_to_ptrToInt.zig +++ b/test/cases/compile_errors/non_pointer_given_to_ptrToInt.zig @@ -3,7 +3,7 @@ export fn entry(x: i32) usize { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:22: error: expected pointer, found 'i32' +// :2:22: error: expected pointer, found 'i32' diff --git a/test/cases/compile_errors/normal_string_with_newline.zig b/test/cases/compile_errors/normal_string_with_newline.zig new file mode 100644 index 0000000000..19e15133ee --- /dev/null +++ b/test/cases/compile_errors/normal_string_with_newline.zig @@ -0,0 +1,9 @@ +const foo = "a +b"; + +// error +// backend=stage2 +// target=native +// +// :1:13: error: expected expression, found 'invalid bytes' +// :1:15: note: invalid byte: '\n' diff --git a/test/cases/compile_errors/stage1/obj/offsetOf-bad_field_name.zig b/test/cases/compile_errors/offsetOf-bad_field_name.zig similarity index 54% rename from test/cases/compile_errors/stage1/obj/offsetOf-bad_field_name.zig rename to test/cases/compile_errors/offsetOf-bad_field_name.zig index de5b82c22c..5beee8fe2e 100644 --- a/test/cases/compile_errors/stage1/obj/offsetOf-bad_field_name.zig +++ b/test/cases/compile_errors/offsetOf-bad_field_name.zig @@ -6,7 +6,8 @@ export fn foo() usize { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:5:27: error: struct 'Foo' has no field 'a' +// :5:27: error: struct 'tmp.Foo' has no field 'a' +// :1:13: note: struct declared here diff --git a/test/cases/compile_errors/stage1/obj/offsetOf-non_struct.zig b/test/cases/compile_errors/offsetOf-non_struct.zig similarity index 58% rename from test/cases/compile_errors/stage1/obj/offsetOf-non_struct.zig rename to test/cases/compile_errors/offsetOf-non_struct.zig index 15275b827b..45e9cf9518 100644 --- a/test/cases/compile_errors/stage1/obj/offsetOf-non_struct.zig +++ b/test/cases/compile_errors/offsetOf-non_struct.zig @@ -4,7 +4,7 @@ export fn foo() usize { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:22: error: expected struct type, found 'i32' +// :3:22: error: expected struct type, found 'i32' diff --git a/test/cases/compile_errors/stage1/obj/only_equality_binary_operator_allowed_for_error_sets.zig b/test/cases/compile_errors/only_equality_binary_operator_allowed_for_error_sets.zig similarity index 52% rename from test/cases/compile_errors/stage1/obj/only_equality_binary_operator_allowed_for_error_sets.zig rename to test/cases/compile_errors/only_equality_binary_operator_allowed_for_error_sets.zig index ff9c4d18a2..28c4f339c4 100644 --- a/test/cases/compile_errors/stage1/obj/only_equality_binary_operator_allowed_for_error_sets.zig +++ b/test/cases/compile_errors/only_equality_binary_operator_allowed_for_error_sets.zig @@ -4,7 +4,7 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:23: error: operator not allowed for errors +// :2:23: error: operator > not allowed for type 'error{A,B}' diff --git a/test/cases/compile_errors/stage1/obj/out_of_range_comptime_int_passed_to_floatToInt.zig b/test/cases/compile_errors/out_of_range_comptime_int_passed_to_floatToInt.zig similarity index 57% rename from test/cases/compile_errors/stage1/obj/out_of_range_comptime_int_passed_to_floatToInt.zig rename to test/cases/compile_errors/out_of_range_comptime_int_passed_to_floatToInt.zig index 1a1dd541c9..426e0c95cb 100644 --- a/test/cases/compile_errors/stage1/obj/out_of_range_comptime_int_passed_to_floatToInt.zig +++ b/test/cases/compile_errors/out_of_range_comptime_int_passed_to_floatToInt.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:31: error: integer value 200 cannot be coerced to type 'i8' +// :2:31: error: float value '200' cannot be stored in integer type 'i8' diff --git a/test/cases/compile_errors/parameter_redeclaration.zig b/test/cases/compile_errors/parameter_redeclaration.zig new file mode 100644 index 0000000000..89c7c4bd2a --- /dev/null +++ b/test/cases/compile_errors/parameter_redeclaration.zig @@ -0,0 +1,10 @@ +fn f(a : i32, a : i32) void { +} +export fn entry() void { f(1, 2); } + +// error +// backend=stage2 +// target=native +// +// :1:15: error: redeclaration of function parameter 'a' +// :1:6: note: previous declaration here diff --git a/test/cases/compile_errors/stage1/obj/parameter_shadowing_global.zig b/test/cases/compile_errors/parameter_shadowing_global.zig similarity index 50% rename from test/cases/compile_errors/stage1/obj/parameter_shadowing_global.zig rename to test/cases/compile_errors/parameter_shadowing_global.zig index ce417d7188..47d112807b 100644 --- a/test/cases/compile_errors/stage1/obj/parameter_shadowing_global.zig +++ b/test/cases/compile_errors/parameter_shadowing_global.zig @@ -5,8 +5,8 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:6: error: local shadows declaration of 'Foo' -// tmp.zig:1:1: note: declared here +// :2:6: error: local shadows declaration of 'Foo' +// :1:1: note: declared here diff --git a/test/cases/compile_errors/stage1/obj/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig b/test/cases/compile_errors/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig similarity index 74% rename from test/cases/compile_errors/stage1/obj/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig rename to test/cases/compile_errors/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig index c1725f1ee8..3ddd24f7a9 100644 --- a/test/cases/compile_errors/stage1/obj/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig +++ b/test/cases/compile_errors/passing_a_not-aligned-enough_pointer_to_cmpxchg.zig @@ -6,7 +6,7 @@ export fn entry() bool { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:32: error: expected type '*i32', found '*align(1) i32' +// :4:31: error: expected type '*i32', found '*align(1) i32' diff --git a/test/cases/compile_errors/pointer_arithmetic_on_pointer-to-array.zig b/test/cases/compile_errors/pointer_arithmetic_on_pointer-to-array.zig new file mode 100644 index 0000000000..e97a098f40 --- /dev/null +++ b/test/cases/compile_errors/pointer_arithmetic_on_pointer-to-array.zig @@ -0,0 +1,14 @@ +export fn foo() void { + var x: [10]u8 = undefined; + var y = &x; + var z = y + 1; + _ = z; +} + +// error +// backend=stage2 +// target=native +// +// :4:15: error: incompatible types: '*[10]u8' and 'comptime_int' +// :4:13: note: type '*[10]u8' here +// :4:17: note: type 'comptime_int' here diff --git a/test/cases/llvm/pointer_with_different_address_spaces.zig b/test/cases/compile_errors/pointer_with_different_address_spaces.zig similarity index 71% rename from test/cases/llvm/pointer_with_different_address_spaces.zig rename to test/cases/compile_errors/pointer_with_different_address_spaces.zig index 26540862ff..b16c4a4e64 100644 --- a/test/cases/llvm/pointer_with_different_address_spaces.zig +++ b/test/cases/compile_errors/pointer_with_different_address_spaces.zig @@ -10,4 +10,4 @@ export fn entry2() void { // backend=stage2,llvm // target=x86_64-linux,x86_64-macos // -// :2:12: error: expected *addrspace(.fs) i32, found *addrspace(.gs) i32 +// :2:12: error: expected type '*addrspace(.fs) i32', found '*addrspace(.gs) i32' diff --git a/test/cases/llvm/pointers_with_different_address_spaces.zig b/test/cases/compile_errors/pointers_with_different_address_spaces.zig similarity index 73% rename from test/cases/llvm/pointers_with_different_address_spaces.zig rename to test/cases/compile_errors/pointers_with_different_address_spaces.zig index 39e5e6a6d1..210c252893 100644 --- a/test/cases/llvm/pointers_with_different_address_spaces.zig +++ b/test/cases/compile_errors/pointers_with_different_address_spaces.zig @@ -10,4 +10,4 @@ pub fn main() void { // backend=stage2,llvm // target=x86_64-linux,x86_64-macos // -// :2:13: error: expected *i32, found *addrspace(.gs) i32 +// :2:13: error: expected type '*i32', found '*addrspace(.gs) i32' diff --git a/test/cases/compile_errors/stage1/obj/popCount-non-integer.zig b/test/cases/compile_errors/popCount-non-integer.zig similarity index 54% rename from test/cases/compile_errors/stage1/obj/popCount-non-integer.zig rename to test/cases/compile_errors/popCount-non-integer.zig index a58a98faa3..af1c79f5f4 100644 --- a/test/cases/compile_errors/stage1/obj/popCount-non-integer.zig +++ b/test/cases/compile_errors/popCount-non-integer.zig @@ -3,7 +3,7 @@ export fn entry(x: f32) u32 { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:22: error: expected integer type, found 'f32' +// :2:27: error: expected integer or vector, found 'f32' diff --git a/test/cases/compile_errors/stage1/obj/primitives_take_precedence_over_declarations.zig b/test/cases/compile_errors/primitives_take_precedence_over_declarations.zig similarity index 54% rename from test/cases/compile_errors/stage1/obj/primitives_take_precedence_over_declarations.zig rename to test/cases/compile_errors/primitives_take_precedence_over_declarations.zig index 6f6107e88c..d924c81387 100644 --- a/test/cases/compile_errors/stage1/obj/primitives_take_precedence_over_declarations.zig +++ b/test/cases/compile_errors/primitives_take_precedence_over_declarations.zig @@ -5,7 +5,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:19: error: integer value 300 cannot be coerced to type 'u8' +// :3:19: error: type 'u8' cannot represent integer value '300' diff --git a/test/cases/compile_errors/stage1/obj/ptrToInt_0_to_non_optional_pointer.zig b/test/cases/compile_errors/ptrToInt_0_to_non_optional_pointer.zig similarity index 52% rename from test/cases/compile_errors/stage1/obj/ptrToInt_0_to_non_optional_pointer.zig rename to test/cases/compile_errors/ptrToInt_0_to_non_optional_pointer.zig index b8a5d1b0aa..86a5dd6dca 100644 --- a/test/cases/compile_errors/stage1/obj/ptrToInt_0_to_non_optional_pointer.zig +++ b/test/cases/compile_errors/ptrToInt_0_to_non_optional_pointer.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:13: error: pointer type '*i32' does not allow address zero +// :2:29: error: pointer type '*i32' does not allow address zero diff --git a/test/cases/compile_errors/stage1/obj/ptrcast_to_non-pointer.zig b/test/cases/compile_errors/ptrcast_to_non-pointer.zig similarity index 56% rename from test/cases/compile_errors/stage1/obj/ptrcast_to_non-pointer.zig rename to test/cases/compile_errors/ptrcast_to_non-pointer.zig index 7e9e9a99b7..2fe599a2e8 100644 --- a/test/cases/compile_errors/stage1/obj/ptrcast_to_non-pointer.zig +++ b/test/cases/compile_errors/ptrcast_to_non-pointer.zig @@ -3,7 +3,7 @@ export fn entry(a: *i32) usize { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:21: error: expected pointer, found 'usize' +// :2:21: error: expected pointer type, found 'usize' diff --git a/test/cases/compile_errors/stage1/test/reassign_to_slice_parameter.zig b/test/cases/compile_errors/reassign_to_slice_parameter.zig similarity index 62% rename from test/cases/compile_errors/stage1/test/reassign_to_slice_parameter.zig rename to test/cases/compile_errors/reassign_to_slice_parameter.zig index 34bd5541a1..ccf79f4574 100644 --- a/test/cases/compile_errors/stage1/test/reassign_to_slice_parameter.zig +++ b/test/cases/compile_errors/reassign_to_slice_parameter.zig @@ -6,8 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:2:10: error: cannot assign to constant +// :2:10: error: cannot assign to constant diff --git a/test/cases/compile_errors/redefinition_of_enums.zig b/test/cases/compile_errors/redefinition_of_enums.zig new file mode 100644 index 0000000000..641211872e --- /dev/null +++ b/test/cases/compile_errors/redefinition_of_enums.zig @@ -0,0 +1,9 @@ +const A = enum {x}; +const A = enum {x}; + +// error +// backend=stage2 +// target=native +// +// :2:1: error: redeclaration of 'A' +// :1:1: note: other declaration here diff --git a/test/cases/compile_errors/redefinition_of_global_variables.zig b/test/cases/compile_errors/redefinition_of_global_variables.zig new file mode 100644 index 0000000000..ed0d6f3ed9 --- /dev/null +++ b/test/cases/compile_errors/redefinition_of_global_variables.zig @@ -0,0 +1,9 @@ +var a : i32 = 1; +var a : i32 = 2; + +// error +// backend=stage2 +// target=native +// +// :2:1: error: redeclaration of 'a' +// :1:1: note: other declaration here diff --git a/test/cases/compile_errors/redefinition_of_struct.zig b/test/cases/compile_errors/redefinition_of_struct.zig new file mode 100644 index 0000000000..dc6d4abeeb --- /dev/null +++ b/test/cases/compile_errors/redefinition_of_struct.zig @@ -0,0 +1,9 @@ +const A = struct { x : i32, }; +const A = struct { y : i32, }; + +// error +// backend=stage2 +// target=native +// +// :2:1: error: redeclaration of 'A' +// :1:1: note: other declaration here diff --git a/test/cases/compile_errors/stage1/obj/refer_to_the_type_of_a_generic_function.zig b/test/cases/compile_errors/refer_to_the_type_of_a_generic_function.zig similarity index 58% rename from test/cases/compile_errors/stage1/obj/refer_to_the_type_of_a_generic_function.zig rename to test/cases/compile_errors/refer_to_the_type_of_a_generic_function.zig index 99ab1da8a3..3bbc75ce83 100644 --- a/test/cases/compile_errors/stage1/obj/refer_to_the_type_of_a_generic_function.zig +++ b/test/cases/compile_errors/refer_to_the_type_of_a_generic_function.zig @@ -5,7 +5,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:5: error: use of undefined value here causes undefined behavior +// :4:5: error: use of undefined value here causes undefined behavior diff --git a/test/cases/compile_errors/stage1/test/reference_to_const_data.zig b/test/cases/compile_errors/reference_to_const_data.zig similarity index 61% rename from test/cases/compile_errors/stage1/test/reference_to_const_data.zig rename to test/cases/compile_errors/reference_to_const_data.zig index 6c98aefd7a..6bd9d0eb1e 100644 --- a/test/cases/compile_errors/stage1/test/reference_to_const_data.zig +++ b/test/cases/compile_errors/reference_to_const_data.zig @@ -20,11 +20,10 @@ export fn qux() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:3:14: error: cannot assign to constant -// tmp.zig:7:13: error: cannot assign to constant -// tmp.zig:11:13: error: cannot assign to constant -// tmp.zig:19:13: error: cannot assign to constant +// :3:14: error: cannot assign to constant +// :7:13: error: cannot assign to constant +// :11:13: error: cannot assign to constant +// :19:13: error: cannot assign to constant diff --git a/test/cases/compile_errors/stage1/obj/referring_to_a_struct_that_is_invalid.zig b/test/cases/compile_errors/referring_to_a_struct_that_is_invalid.zig similarity index 69% rename from test/cases/compile_errors/stage1/obj/referring_to_a_struct_that_is_invalid.zig rename to test/cases/compile_errors/referring_to_a_struct_that_is_invalid.zig index 433650cddb..946ead9954 100644 --- a/test/cases/compile_errors/stage1/obj/referring_to_a_struct_that_is_invalid.zig +++ b/test/cases/compile_errors/referring_to_a_struct_that_is_invalid.zig @@ -11,7 +11,8 @@ fn assert(ok: bool) void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:10:14: error: reached unreachable code +// :10:14: error: reached unreachable code +// :6:20: note: called from here diff --git a/test/cases/compile_errors/stage1/test/reify_typeOf_with_incompatible_arguments.zig b/test/cases/compile_errors/reify_typeOf_with_incompatible_arguments.zig similarity index 53% rename from test/cases/compile_errors/stage1/test/reify_typeOf_with_incompatible_arguments.zig rename to test/cases/compile_errors/reify_typeOf_with_incompatible_arguments.zig index 2013a07e53..ee688c362b 100644 --- a/test/cases/compile_errors/stage1/test/reify_typeOf_with_incompatible_arguments.zig +++ b/test/cases/compile_errors/reify_typeOf_with_incompatible_arguments.zig @@ -5,8 +5,9 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:4:9: error: incompatible types: 'f32' and 'u32' +// :4:9: error: incompatible types: 'f32' and 'u32' +// :4:17: note: type 'f32' here +// :4:24: note: type 'u32' here diff --git a/test/cases/compile_errors/reify_typeOf_with_no_arguments.zig b/test/cases/compile_errors/reify_typeOf_with_no_arguments.zig new file mode 100644 index 0000000000..3e4cb183cd --- /dev/null +++ b/test/cases/compile_errors/reify_typeOf_with_no_arguments.zig @@ -0,0 +1,9 @@ +export fn entry() void { + _ = @TypeOf(); +} + +// error +// backend=stage2 +// target=native +// +// :2:9: error: expected at least 1 argument, found 0 diff --git a/test/cases/compile_errors/stage1/test/reject_extern_function_definitions_with_body.zig b/test/cases/compile_errors/reject_extern_function_definitions_with_body.zig similarity index 55% rename from test/cases/compile_errors/stage1/test/reject_extern_function_definitions_with_body.zig rename to test/cases/compile_errors/reject_extern_function_definitions_with_body.zig index a7b92e8399..4247ddd463 100644 --- a/test/cases/compile_errors/stage1/test/reject_extern_function_definitions_with_body.zig +++ b/test/cases/compile_errors/reject_extern_function_definitions_with_body.zig @@ -3,8 +3,7 @@ extern "c" fn definitelyNotInLibC(a: i32, b: i32) i32 { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:1:1: error: extern functions have no body +// :1:1: error: extern functions have no body diff --git a/test/cases/compile_errors/reject_extern_variables_with_initializers.zig b/test/cases/compile_errors/reject_extern_variables_with_initializers.zig new file mode 100644 index 0000000000..a52987f185 --- /dev/null +++ b/test/cases/compile_errors/reject_extern_variables_with_initializers.zig @@ -0,0 +1,7 @@ +extern var foo: int = 2; + +// error +// backend=stage2 +// target=native +// +// :1:23: error: extern variables have no initializers diff --git a/test/cases/compile_errors/stage1/test/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig b/test/cases/compile_errors/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig similarity index 61% rename from test/cases/compile_errors/stage1/test/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig rename to test/cases/compile_errors/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig index 7499939b4a..201c8c870b 100644 --- a/test/cases/compile_errors/stage1/test/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig +++ b/test/cases/compile_errors/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig @@ -7,8 +7,8 @@ test "1" { } // error -// backend=stage1 +// backend=stage2 // target=native // is_test=1 // -// tmp.zig:2:12: error: use of undeclared identifier 'Q' +// :2:12: error: use of undeclared identifier 'Q' diff --git a/test/cases/compile_errors/stage1/obj/return_from_defer_expression.zig b/test/cases/compile_errors/return_from_defer_expression.zig similarity index 78% rename from test/cases/compile_errors/stage1/obj/return_from_defer_expression.zig rename to test/cases/compile_errors/return_from_defer_expression.zig index cbe9542176..e60b8ae848 100644 --- a/test/cases/compile_errors/stage1/obj/return_from_defer_expression.zig +++ b/test/cases/compile_errors/return_from_defer_expression.zig @@ -15,7 +15,7 @@ pub fn maybeInt() ?i32 { export fn entry() usize { return @sizeOf(@TypeOf(testTrickyDefer)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:11: error: 'try' not allowed inside defer expression +// :4:11: error: 'try' not allowed inside defer expression diff --git a/test/cases/compile_errors/return_invalid_type_from_test.zig b/test/cases/compile_errors/return_invalid_type_from_test.zig new file mode 100644 index 0000000000..6cad6af85f --- /dev/null +++ b/test/cases/compile_errors/return_invalid_type_from_test.zig @@ -0,0 +1,8 @@ +test "example" { return 1; } + +// error +// backend=stage2 +// target=native +// is_test=1 +// +// :1:25: error: expected type 'void', found 'comptime_int' diff --git a/test/cases/compile_errors/runtime_cast_to_union_which_has_non-void_fields.zig b/test/cases/compile_errors/runtime_cast_to_union_which_has_non-void_fields.zig new file mode 100644 index 0000000000..c312d6db40 --- /dev/null +++ b/test/cases/compile_errors/runtime_cast_to_union_which_has_non-void_fields.zig @@ -0,0 +1,23 @@ +const Letter = enum { A, B, C }; +const Value = union(Letter) { + A: i32, + B, + C, +}; +export fn entry() void { + foo(Letter.A); +} +fn foo(l: Letter) void { + var x: Value = l; + _ = x; +} + +// error +// backend=stage2 +// target=native +// +// :11:20: error: runtime coercion from enum 'tmp.Letter' to union 'tmp.Value' which has non-void fields +// :3:5: note: field 'A' has type 'i32' +// :4:5: note: field 'B' has type 'void' +// :5:5: note: field 'C' has type 'void' +// :2:15: note: union declared here diff --git a/test/cases/compile_errors/saturating_arithmetic_does_not_allow_floats.zig b/test/cases/compile_errors/saturating_arithmetic_does_not_allow_floats.zig new file mode 100644 index 0000000000..3689fa5ec5 --- /dev/null +++ b/test/cases/compile_errors/saturating_arithmetic_does_not_allow_floats.zig @@ -0,0 +1,9 @@ +export fn a() void { + _ = @as(f32, 1.0) +| @as(f32, 1.0); +} + +// error +// backend=stage2 +// target=native +// +// :2:23: error: invalid operands to binary expression: 'Float' and 'Float' diff --git a/test/cases/compile_errors/stage1/obj/setAlignStack_in_naked_function.zig b/test/cases/compile_errors/setAlignStack_in_naked_function.zig similarity index 56% rename from test/cases/compile_errors/stage1/obj/setAlignStack_in_naked_function.zig rename to test/cases/compile_errors/setAlignStack_in_naked_function.zig index b9adc84ed5..79c4ba6f5b 100644 --- a/test/cases/compile_errors/stage1/obj/setAlignStack_in_naked_function.zig +++ b/test/cases/compile_errors/setAlignStack_in_naked_function.zig @@ -3,7 +3,7 @@ export fn entry() callconv(.Naked) void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:5: error: @setAlignStack in naked function +// :2:5: error: @setAlignStack in naked function diff --git a/test/cases/compile_errors/setAlignStack_outside_function.zig b/test/cases/compile_errors/setAlignStack_outside_function.zig new file mode 100644 index 0000000000..0c06f867de --- /dev/null +++ b/test/cases/compile_errors/setAlignStack_outside_function.zig @@ -0,0 +1,9 @@ +comptime { + @setAlignStack(16); +} + +// error +// backend=stage2 +// target=native +// +// :2:5: error: @setAlignStack outside function body diff --git a/test/cases/compile_errors/setAlignStack_set_twice.zig b/test/cases/compile_errors/setAlignStack_set_twice.zig new file mode 100644 index 0000000000..3c61b26bec --- /dev/null +++ b/test/cases/compile_errors/setAlignStack_set_twice.zig @@ -0,0 +1,11 @@ +export fn entry() void { + @setAlignStack(16); + @setAlignStack(16); +} + +// error +// backend=stage2 +// target=native +// +// :3:5: error: multiple @setAlignStack in the same function body +// :2:5: note: other instance here diff --git a/test/cases/compile_errors/setAlignStack_too_big.zig b/test/cases/compile_errors/setAlignStack_too_big.zig new file mode 100644 index 0000000000..5ee96041e0 --- /dev/null +++ b/test/cases/compile_errors/setAlignStack_too_big.zig @@ -0,0 +1,9 @@ +export fn entry() void { + @setAlignStack(511 + 1); +} + +// error +// backend=stage2 +// target=native +// +// :2:5: error: attempt to @setAlignStack(512); maximum is 256 diff --git a/test/cases/compile_errors/stage1/obj/setting_a_section_on_a_local_variable.zig b/test/cases/compile_errors/setting_a_section_on_a_local_variable.zig similarity index 58% rename from test/cases/compile_errors/stage1/obj/setting_a_section_on_a_local_variable.zig rename to test/cases/compile_errors/setting_a_section_on_a_local_variable.zig index 90d605dc40..2d31ef1750 100644 --- a/test/cases/compile_errors/stage1/obj/setting_a_section_on_a_local_variable.zig +++ b/test/cases/compile_errors/setting_a_section_on_a_local_variable.zig @@ -4,7 +4,7 @@ export fn entry() i32 { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:30: error: cannot set section of local variable 'foo' +// :2:30: error: cannot set section of local variable 'foo' diff --git a/test/cases/compile_errors/stage1/obj/shifting_RHS_is_log2_of_LHS_int_bit_width.zig b/test/cases/compile_errors/shifting_RHS_is_log2_of_LHS_int_bit_width.zig similarity index 54% rename from test/cases/compile_errors/stage1/obj/shifting_RHS_is_log2_of_LHS_int_bit_width.zig rename to test/cases/compile_errors/shifting_RHS_is_log2_of_LHS_int_bit_width.zig index d9b9bbfce3..a2104d1aa6 100644 --- a/test/cases/compile_errors/stage1/obj/shifting_RHS_is_log2_of_LHS_int_bit_width.zig +++ b/test/cases/compile_errors/shifting_RHS_is_log2_of_LHS_int_bit_width.zig @@ -3,7 +3,7 @@ export fn entry(x: u8, y: u8) u8 { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:17: error: expected type 'u3', found 'u8' +// :2:17: error: expected type 'u3', found 'u8' diff --git a/test/cases/compile_errors/stage1/obj/slicing_single-item_pointer.zig b/test/cases/compile_errors/slicing_single-item_pointer.zig similarity index 60% rename from test/cases/compile_errors/stage1/obj/slicing_single-item_pointer.zig rename to test/cases/compile_errors/slicing_single-item_pointer.zig index d8c82fa3af..6b3021e5e0 100644 --- a/test/cases/compile_errors/stage1/obj/slicing_single-item_pointer.zig +++ b/test/cases/compile_errors/slicing_single-item_pointer.zig @@ -4,7 +4,7 @@ export fn entry(ptr: *i32) void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:22: error: slice of single-item pointer +// :2:22: error: slice of single-item pointer diff --git a/test/cases/compile_errors/stage1/obj/access_non-existent_member_of_error_set.zig b/test/cases/compile_errors/stage1/obj/access_non-existent_member_of_error_set.zig deleted file mode 100644 index caddf36a84..0000000000 --- a/test/cases/compile_errors/stage1/obj/access_non-existent_member_of_error_set.zig +++ /dev/null @@ -1,11 +0,0 @@ -const Foo = error{A}; -comptime { - const z = Foo.Bar; - _ = z; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:18: error: no error named 'Bar' in 'Foo' diff --git a/test/cases/compile_errors/stage1/obj/add_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/add_assign_on_undefined_value.zig deleted file mode 100644 index 38e4fef81b..0000000000 --- a/test/cases/compile_errors/stage1/obj/add_assign_on_undefined_value.zig +++ /dev/null @@ -1,10 +0,0 @@ -comptime { - var a: i64 = undefined; - a += a; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:5: error: use of undefined value here causes undefined behavior diff --git a/test/cases/compile_errors/stage1/obj/add_on_undefined_value.zig b/test/cases/compile_errors/stage1/obj/add_on_undefined_value.zig deleted file mode 100644 index 4a66f9c102..0000000000 --- a/test/cases/compile_errors/stage1/obj/add_on_undefined_value.zig +++ /dev/null @@ -1,10 +0,0 @@ -comptime { - var a: i64 = undefined; - _ = a + a; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:9: error: use of undefined value here causes undefined behavior diff --git a/test/cases/compile_errors/stage1/obj/address_of_number_literal.zig b/test/cases/compile_errors/stage1/obj/address_of_number_literal.zig deleted file mode 100644 index 76d321929d..0000000000 --- a/test/cases/compile_errors/stage1/obj/address_of_number_literal.zig +++ /dev/null @@ -1,10 +0,0 @@ -const x = 3; -const y = &x; -fn foo() *const i32 { return y; } -export fn entry() usize { return @sizeOf(@TypeOf(foo)); } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:30: error: expected type '*const i32', found '*const comptime_int' diff --git a/test/cases/compile_errors/stage1/obj/array_access_of_undeclared_identifier.zig b/test/cases/compile_errors/stage1/obj/array_access_of_undeclared_identifier.zig deleted file mode 100644 index 7d16a0d60e..0000000000 --- a/test/cases/compile_errors/stage1/obj/array_access_of_undeclared_identifier.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn f() void { - i[i] = i[i]; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:5: error: use of undeclared identifier 'i' diff --git a/test/cases/compile_errors/stage1/obj/assign_unreachable.zig b/test/cases/compile_errors/stage1/obj/assign_unreachable.zig deleted file mode 100644 index f87adcd994..0000000000 --- a/test/cases/compile_errors/stage1/obj/assign_unreachable.zig +++ /dev/null @@ -1,10 +0,0 @@ -export fn f() void { - const a = return; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:5: error: unreachable code -// tmp.zig:2:15: note: control flow is diverted here diff --git a/test/cases/compile_errors/stage1/obj/atomic_orderings_of_fence_Acquire_or_stricter.zig b/test/cases/compile_errors/stage1/obj/atomic_orderings_of_fence_Acquire_or_stricter.zig deleted file mode 100644 index 8a01871a80..0000000000 --- a/test/cases/compile_errors/stage1/obj/atomic_orderings_of_fence_Acquire_or_stricter.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn entry() void { - @fence(.Monotonic); -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:12: error: atomic ordering must be Acquire or stricter diff --git a/test/cases/compile_errors/stage1/obj/attempt_to_close_over_comptime_variable_from_outer_scope.zig b/test/cases/compile_errors/stage1/obj/attempt_to_close_over_comptime_variable_from_outer_scope.zig deleted file mode 100644 index 58eb9d19d2..0000000000 --- a/test/cases/compile_errors/stage1/obj/attempt_to_close_over_comptime_variable_from_outer_scope.zig +++ /dev/null @@ -1,14 +0,0 @@ -fn SimpleList(comptime L: usize) type { - var T = u8; - return struct { - array: [L]T, - }; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:4:19: error: mutable 'T' not accessible from here -// tmp.zig:2:9: note: declared mutable here -// tmp.zig:3:12: note: crosses namespace boundary here diff --git a/test/cases/compile_errors/stage1/obj/attempted_double_ampersand.zig b/test/cases/compile_errors/stage1/obj/attempted_double_ampersand.zig deleted file mode 100644 index c749b172c6..0000000000 --- a/test/cases/compile_errors/stage1/obj/attempted_double_ampersand.zig +++ /dev/null @@ -1,12 +0,0 @@ -export fn entry(a: bool, b: bool) i32 { - if (a && b) { - return 1234; - } - return 5678; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:11: error: ambiguous use of '&&'; use 'and' for logical AND, or change whitespace to ' & &' for bitwise AND diff --git a/test/cases/compile_errors/stage1/obj/attempted_double_pipe_on_boolean_values.zig b/test/cases/compile_errors/stage1/obj/attempted_double_pipe_on_boolean_values.zig deleted file mode 100644 index b991b1b61b..0000000000 --- a/test/cases/compile_errors/stage1/obj/attempted_double_pipe_on_boolean_values.zig +++ /dev/null @@ -1,13 +0,0 @@ -export fn entry(a: bool, b: bool) i32 { - if (a || b) { - return 1234; - } - return 5678; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:9: error: expected error set type, found 'bool' -// tmp.zig:2:11: note: `||` merges error sets; `or` performs boolean OR diff --git a/test/cases/compile_errors/stage1/obj/bad_import.zig b/test/cases/compile_errors/stage1/obj/bad_import.zig deleted file mode 100644 index 1a7ab5f00b..0000000000 --- a/test/cases/compile_errors/stage1/obj/bad_import.zig +++ /dev/null @@ -1,7 +0,0 @@ -const bogus = @import("bogus-does-not-exist.zig",); - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:23: error: unable to load '${DIR}bogus-does-not-exist.zig': FileNotFound diff --git a/test/cases/compile_errors/stage1/obj/bitCast_same_size_but_bit_count_mismatch.zig b/test/cases/compile_errors/stage1/obj/bitCast_same_size_but_bit_count_mismatch.zig deleted file mode 100644 index 9150dc3285..0000000000 --- a/test/cases/compile_errors/stage1/obj/bitCast_same_size_but_bit_count_mismatch.zig +++ /dev/null @@ -1,10 +0,0 @@ -export fn entry(byte: u8) void { - var oops = @bitCast(u7, byte); - _ = oops; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:25: error: destination type 'u7' has 7 bits but source type 'u8' has 8 bits diff --git a/test/cases/compile_errors/stage1/obj/bitCast_with_different_sizes_inside_an_expression.zig b/test/cases/compile_errors/stage1/obj/bitCast_with_different_sizes_inside_an_expression.zig deleted file mode 100644 index 64caef7cd0..0000000000 --- a/test/cases/compile_errors/stage1/obj/bitCast_with_different_sizes_inside_an_expression.zig +++ /dev/null @@ -1,10 +0,0 @@ -export fn entry() void { - var foo = (@bitCast(u8, @as(f32, 1.0)) == 0xf); - _ = foo; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:25: error: destination type 'u8' has size 1 but source type 'f32' has size 4 diff --git a/test/cases/compile_errors/stage1/obj/bit_shifting_only_works_on_integer_types.zig b/test/cases/compile_errors/stage1/obj/bit_shifting_only_works_on_integer_types.zig deleted file mode 100644 index 7d1c5873a1..0000000000 --- a/test/cases/compile_errors/stage1/obj/bit_shifting_only_works_on_integer_types.zig +++ /dev/null @@ -1,10 +0,0 @@ -export fn entry() void { - const x = &@as(u8, 1) << 10; - _ = x; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:16: error: bit shifting operation expected integer type, found '*const u8' diff --git a/test/cases/compile_errors/stage1/obj/bogus_method_call_on_slice.zig b/test/cases/compile_errors/stage1/obj/bogus_method_call_on_slice.zig deleted file mode 100644 index 3ede578b34..0000000000 --- a/test/cases/compile_errors/stage1/obj/bogus_method_call_on_slice.zig +++ /dev/null @@ -1,11 +0,0 @@ -var self = "aoeu"; -fn f(m: []const u8) void { - m.copy(u8, self[0..], m); -} -export fn entry() usize { return @sizeOf(@TypeOf(f)); } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:6: error: no member named 'copy' in '[]const u8' diff --git a/test/cases/compile_errors/stage1/obj/cast_negative_integer_literal_to_usize.zig b/test/cases/compile_errors/stage1/obj/cast_negative_integer_literal_to_usize.zig deleted file mode 100644 index 763e0c8a23..0000000000 --- a/test/cases/compile_errors/stage1/obj/cast_negative_integer_literal_to_usize.zig +++ /dev/null @@ -1,10 +0,0 @@ -export fn entry() void { - const x = @as(usize, -10); - _ = x; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:26: error: cannot cast negative value -10 to unsigned integer type 'usize' diff --git a/test/cases/compile_errors/stage1/obj/cast_unreachable.zig b/test/cases/compile_errors/stage1/obj/cast_unreachable.zig deleted file mode 100644 index b043108737..0000000000 --- a/test/cases/compile_errors/stage1/obj/cast_unreachable.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn f() i32 { - return @as(i32, return 1); -} -export fn entry() void { _ = f(); } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:12: error: unreachable code -// tmp.zig:2:21: note: control flow is diverted here diff --git a/test/cases/compile_errors/stage1/obj/colliding_invalid_top_level_functions.zig b/test/cases/compile_errors/stage1/obj/colliding_invalid_top_level_functions.zig deleted file mode 100644 index 344edbaa9f..0000000000 --- a/test/cases/compile_errors/stage1/obj/colliding_invalid_top_level_functions.zig +++ /dev/null @@ -1,10 +0,0 @@ -fn func() bogus {} -fn func() bogus {} -export fn entry() usize { return @sizeOf(@TypeOf(func)); } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:1: error: redeclaration of 'func' -// tmp.zig:1:1: note: other declaration here diff --git a/test/cases/compile_errors/stage1/obj/comptime_cast_enum_to_union_but_field_has_payload.zig b/test/cases/compile_errors/stage1/obj/comptime_cast_enum_to_union_but_field_has_payload.zig deleted file mode 100644 index c3cf9e76e5..0000000000 --- a/test/cases/compile_errors/stage1/obj/comptime_cast_enum_to_union_but_field_has_payload.zig +++ /dev/null @@ -1,17 +0,0 @@ -const Letter = enum { A, B, C }; -const Value = union(Letter) { - A: i32, - B, - C, -}; -export fn entry() void { - var x: Value = Letter.A; - _ = x; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:8:26: error: cast to union 'Value' must initialize 'i32' field 'A' -// tmp.zig:3:5: note: field 'A' declared here diff --git a/test/cases/compile_errors/stage1/obj/const_is_a_statement_not_an_expression.zig b/test/cases/compile_errors/stage1/obj/const_is_a_statement_not_an_expression.zig deleted file mode 100644 index b90878abeb..0000000000 --- a/test/cases/compile_errors/stage1/obj/const_is_a_statement_not_an_expression.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn f() void { - (const a = 0); -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:6: error: expected expression, found 'const' diff --git a/test/cases/compile_errors/stage1/obj/control_flow_uses_comptime_var_at_runtime.zig b/test/cases/compile_errors/stage1/obj/control_flow_uses_comptime_var_at_runtime.zig deleted file mode 100644 index cb2c3e9f6e..0000000000 --- a/test/cases/compile_errors/stage1/obj/control_flow_uses_comptime_var_at_runtime.zig +++ /dev/null @@ -1,15 +0,0 @@ -export fn foo() void { - comptime var i = 0; - while (i < 5) : (i += 1) { - bar(); - } -} - -fn bar() void { } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:5: error: control flow attempts to use compile-time variable at runtime -// tmp.zig:3:24: note: compile-time variable assigned here diff --git a/test/cases/compile_errors/stage1/obj/declaration_with_same_name_as_primitive_must_use_special_syntax.zig b/test/cases/compile_errors/stage1/obj/declaration_with_same_name_as_primitive_must_use_special_syntax.zig deleted file mode 100644 index 46b0257ed5..0000000000 --- a/test/cases/compile_errors/stage1/obj/declaration_with_same_name_as_primitive_must_use_special_syntax.zig +++ /dev/null @@ -1,12 +0,0 @@ -const u8 = u16; -export fn entry() void { - const a: u8 = 300; - _ = a; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:7: error: name shadows primitive 'u8' -// tmp.zig:1:7: note: consider using @"u8" to disambiguate diff --git a/test/cases/compile_errors/stage1/obj/deduplicate_undeclared_identifier.zig b/test/cases/compile_errors/stage1/obj/deduplicate_undeclared_identifier.zig deleted file mode 100644 index 1e3b3bf6db..0000000000 --- a/test/cases/compile_errors/stage1/obj/deduplicate_undeclared_identifier.zig +++ /dev/null @@ -1,12 +0,0 @@ -export fn a() void { - x += 1; -} -export fn b() void { - x += 1; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:5: error: use of undeclared identifier 'x' diff --git a/test/cases/compile_errors/stage1/obj/empty_for_loop_body.zig b/test/cases/compile_errors/stage1/obj/empty_for_loop_body.zig deleted file mode 100644 index b824c93131..0000000000 --- a/test/cases/compile_errors/stage1/obj/empty_for_loop_body.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn a() void { - for(undefined) |x|; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:23: error: expected block or assignment, found ';' diff --git a/test/cases/compile_errors/stage1/obj/empty_if_body.zig b/test/cases/compile_errors/stage1/obj/empty_if_body.zig deleted file mode 100644 index 0a7b5b1b8b..0000000000 --- a/test/cases/compile_errors/stage1/obj/empty_if_body.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn a() void { - if(true); -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:13: error: expected block or assignment, found ';' diff --git a/test/cases/compile_errors/stage1/obj/empty_while_loop_body.zig b/test/cases/compile_errors/stage1/obj/empty_while_loop_body.zig deleted file mode 100644 index 01b2132518..0000000000 --- a/test/cases/compile_errors/stage1/obj/empty_while_loop_body.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn a() void { - while(true); -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:16: error: expected block or assignment, found ';' diff --git a/test/cases/compile_errors/stage1/obj/enum_with_0_fields.zig b/test/cases/compile_errors/stage1/obj/enum_with_0_fields.zig deleted file mode 100644 index 9ee18f51ca..0000000000 --- a/test/cases/compile_errors/stage1/obj/enum_with_0_fields.zig +++ /dev/null @@ -1,7 +0,0 @@ -const Foo = enum {}; - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:13: error: enum declarations must have at least one tag diff --git a/test/cases/compile_errors/stage1/obj/exceeded_maximum_bit_width_of_integer.zig b/test/cases/compile_errors/stage1/obj/exceeded_maximum_bit_width_of_integer.zig deleted file mode 100644 index 2a3c795f81..0000000000 --- a/test/cases/compile_errors/stage1/obj/exceeded_maximum_bit_width_of_integer.zig +++ /dev/null @@ -1,15 +0,0 @@ -export fn entry1() void { - const T = u65536; - _ = T; -} -export fn entry2() void { - var x: i65536 = 1; - _ = x; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:15: error: primitive integer type 'u65536' exceeds maximum bit width of 65535 -// tmp.zig:6:12: error: primitive integer type 'i65536' exceeds maximum bit width of 65535 diff --git a/test/cases/compile_errors/stage1/obj/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig b/test/cases/compile_errors/stage1/obj/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig deleted file mode 100644 index 696fef57fa..0000000000 --- a/test/cases/compile_errors/stage1/obj/explicit_cast_float_literal_to_integer_when_there_is_a_fraction_component.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn entry() i32 { - return @as(i32, 12.34); -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: fractional component prevents float value 12.340000 from being casted to type 'i32' diff --git a/test/cases/compile_errors/stage1/obj/field_type_supplied_in_an_enum.zig b/test/cases/compile_errors/stage1/obj/field_type_supplied_in_an_enum.zig deleted file mode 100644 index d6ba214c0c..0000000000 --- a/test/cases/compile_errors/stage1/obj/field_type_supplied_in_an_enum.zig +++ /dev/null @@ -1,12 +0,0 @@ -const Letter = enum { - A: void, - B, - C, -}; - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:8: error: enum fields do not have types -// tmp.zig:1:16: note: consider 'union(enum)' here to make it a tagged union diff --git a/test/cases/compile_errors/stage1/obj/function_with_invalid_return_type.zig b/test/cases/compile_errors/stage1/obj/function_with_invalid_return_type.zig deleted file mode 100644 index 897b298a3d..0000000000 --- a/test/cases/compile_errors/stage1/obj/function_with_invalid_return_type.zig +++ /dev/null @@ -1,7 +0,0 @@ -export fn foo() boid {} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:17: error: use of undeclared identifier 'boid' diff --git a/test/cases/compile_errors/stage1/obj/hasDecl_with_non-container.zig b/test/cases/compile_errors/stage1/obj/hasDecl_with_non-container.zig deleted file mode 100644 index e9aeacdd4e..0000000000 --- a/test/cases/compile_errors/stage1/obj/hasDecl_with_non-container.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn entry() void { - _ = @hasDecl(i32, "hi"); -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:18: error: expected struct, enum, or union; found 'i32' diff --git a/test/cases/compile_errors/stage1/obj/if_condition_is_bool_not_int.zig b/test/cases/compile_errors/stage1/obj/if_condition_is_bool_not_int.zig deleted file mode 100644 index 53afb2792a..0000000000 --- a/test/cases/compile_errors/stage1/obj/if_condition_is_bool_not_int.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn f() void { - if (0) {} -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:9: error: expected type 'bool', found 'comptime_int' diff --git a/test/cases/compile_errors/stage1/obj/ignored_statement_value.zig b/test/cases/compile_errors/stage1/obj/ignored_statement_value.zig deleted file mode 100644 index a0e540a92b..0000000000 --- a/test/cases/compile_errors/stage1/obj/ignored_statement_value.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn foo() void { - 1; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:5: error: expression value is ignored diff --git a/test/cases/compile_errors/stage1/obj/import_outside_package_path.zig b/test/cases/compile_errors/stage1/obj/import_outside_package_path.zig deleted file mode 100644 index 5a712c2e01..0000000000 --- a/test/cases/compile_errors/stage1/obj/import_outside_package_path.zig +++ /dev/null @@ -1,9 +0,0 @@ -comptime{ - _ = @import("../a.zig"); -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:9: error: import of file outside package path: '../a.zig' diff --git a/test/cases/compile_errors/stage1/obj/inferring_error_set_of_function_pointer.zig b/test/cases/compile_errors/stage1/obj/inferring_error_set_of_function_pointer.zig deleted file mode 100644 index b91242322d..0000000000 --- a/test/cases/compile_errors/stage1/obj/inferring_error_set_of_function_pointer.zig +++ /dev/null @@ -1,9 +0,0 @@ -comptime { - const z: ?fn()!void = null; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:19: error: function prototype may not have inferred error set diff --git a/test/cases/compile_errors/stage1/obj/invalid_break_expression.zig b/test/cases/compile_errors/stage1/obj/invalid_break_expression.zig deleted file mode 100644 index efb76a34f7..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_break_expression.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn f() void { - break; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:5: error: break expression outside loop diff --git a/test/cases/compile_errors/stage1/obj/invalid_continue_expression.zig b/test/cases/compile_errors/stage1/obj/invalid_continue_expression.zig deleted file mode 100644 index 00f3c5a3d5..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_continue_expression.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn f() void { - continue; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:5: error: continue expression outside loop diff --git a/test/cases/compile_errors/stage1/obj/invalid_exponent_in_float_literal-1.zig b/test/cases/compile_errors/stage1/obj/invalid_exponent_in_float_literal-1.zig deleted file mode 100644 index 431401c534..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_exponent_in_float_literal-1.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: f128 = 0x1.0p1ab1; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:28: note: invalid byte: 'a' diff --git a/test/cases/compile_errors/stage1/obj/invalid_exponent_in_float_literal-2.zig b/test/cases/compile_errors/stage1/obj/invalid_exponent_in_float_literal-2.zig deleted file mode 100644 index 088b43546c..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_exponent_in_float_literal-2.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: f128 = 0x1.0p50F; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:29: note: invalid byte: 'F' diff --git a/test/cases/compile_errors/stage1/obj/invalid_field_access_in_comptime.zig b/test/cases/compile_errors/stage1/obj/invalid_field_access_in_comptime.zig deleted file mode 100644 index 2630a704ed..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_field_access_in_comptime.zig +++ /dev/null @@ -1,7 +0,0 @@ -comptime { var x = doesnt_exist.whatever; _ = x; } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:20: error: use of undeclared identifier 'doesnt_exist' diff --git a/test/cases/compile_errors/stage1/obj/invalid_legacy_unicode_escape.zig b/test/cases/compile_errors/stage1/obj/invalid_legacy_unicode_escape.zig deleted file mode 100644 index b81a814a60..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_legacy_unicode_escape.zig +++ /dev/null @@ -1,10 +0,0 @@ -export fn entry() void { - const a = '\U1234'; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:15: error: expected expression, found 'invalid bytes' -// tmp.zig:2:18: note: invalid byte: '1' diff --git a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-1.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-1.zig deleted file mode 100644 index a3e2827f31..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-1.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: f128 = 0._0; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:23: note: invalid byte: '_' diff --git a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-10.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-10.zig deleted file mode 100644 index 7d33244483..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-10.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: f128 = 1.0__0e-1; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:25: note: invalid byte: '_' diff --git a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-11.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-11.zig deleted file mode 100644 index fa4f5b8975..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-11.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: f128 = 1.0e-1__0; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:28: note: invalid byte: '_' diff --git a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-12.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-12.zig deleted file mode 100644 index 2d36187711..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-12.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: f128 = 0_x0.0; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:23: note: invalid byte: 'x' diff --git a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-13.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-13.zig deleted file mode 100644 index 21b044121c..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-13.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: f128 = 0x_0.0; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:23: note: invalid byte: '_' diff --git a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-14.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-14.zig deleted file mode 100644 index adb0727eba..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-14.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: f128 = 0x0.0_p1; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:27: note: invalid byte: 'p' diff --git a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-2.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-2.zig deleted file mode 100644 index 16618c6a18..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-2.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: f128 = 0_.0; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:23: note: invalid byte: '.' diff --git a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-3.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-3.zig deleted file mode 100644 index 09e9e20b4b..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-3.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: f128 = 0.0_; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:25: note: invalid byte: ';' diff --git a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-4.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-4.zig deleted file mode 100644 index 278ee35f5f..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-4.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: f128 = 1.0e_1; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:25: note: invalid byte: '_' diff --git a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-5.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-5.zig deleted file mode 100644 index 4912c15e50..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-5.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: f128 = 1.0e+_1; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:26: note: invalid byte: '_' diff --git a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-6.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-6.zig deleted file mode 100644 index 3386c860b9..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-6.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: f128 = 1.0e-_1; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:26: note: invalid byte: '_' diff --git a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-7.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-7.zig deleted file mode 100644 index c0e57424a9..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-7.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: f128 = 1.0e-1_; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:28: note: invalid byte: ';' diff --git a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-9.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-9.zig deleted file mode 100644 index f7475332c1..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_float_literal-9.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: f128 = 1__0.0e-1; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:23: note: invalid byte: '_' diff --git a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-1.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-1.zig deleted file mode 100644 index 9cea337db4..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-1.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: u128 = 0010_; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:26: note: invalid byte: ';' diff --git a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-2.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-2.zig deleted file mode 100644 index 5f02479f2e..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-2.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: u128 = 0b0010_; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:28: note: invalid byte: ';' diff --git a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-3.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-3.zig deleted file mode 100644 index aed4cb9068..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-3.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: u128 = 0o0010_; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:28: note: invalid byte: ';' diff --git a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-4.zig b/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-4.zig deleted file mode 100644 index dce2771048..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_underscore_placement_in_int_literal-4.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn main() void { - var bad: u128 = 0x0010_; - _ = bad; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected expression, found 'invalid bytes' -// tmp.zig:2:28: note: invalid byte: ';' diff --git a/test/cases/compile_errors/stage1/obj/invalid_union_field_access_in_comptime.zig b/test/cases/compile_errors/stage1/obj/invalid_union_field_access_in_comptime.zig deleted file mode 100644 index 6d55f3b04f..0000000000 --- a/test/cases/compile_errors/stage1/obj/invalid_union_field_access_in_comptime.zig +++ /dev/null @@ -1,15 +0,0 @@ -const Foo = union { - Bar: u8, - Baz: void, -}; -comptime { - var foo = Foo {.Baz = {}}; - const bar_val = foo.Bar; - _ = bar_val; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:7:24: error: accessing union field 'Bar' while field 'Baz' is set diff --git a/test/cases/compile_errors/stage1/obj/issue_9346_return_outside_of_function_scope.zig b/test/cases/compile_errors/stage1/obj/issue_9346_return_outside_of_function_scope.zig deleted file mode 100644 index d12bff2b49..0000000000 --- a/test/cases/compile_errors/stage1/obj/issue_9346_return_outside_of_function_scope.zig +++ /dev/null @@ -1,7 +0,0 @@ -pub const empty = return 1; - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:19: error: 'return' outside function scope diff --git a/test/cases/compile_errors/stage1/obj/local_shadows_global_that_occurs_later.zig b/test/cases/compile_errors/stage1/obj/local_shadows_global_that_occurs_later.zig deleted file mode 100644 index 4b9b58871f..0000000000 --- a/test/cases/compile_errors/stage1/obj/local_shadows_global_that_occurs_later.zig +++ /dev/null @@ -1,12 +0,0 @@ -pub fn main() void { - var foo = true; - _ = foo; -} -fn foo() void {} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:9: error: local shadows declaration of 'foo' -// tmp.zig:5:1: note: declared here diff --git a/test/cases/compile_errors/stage1/obj/local_variable_redeclaration.zig b/test/cases/compile_errors/stage1/obj/local_variable_redeclaration.zig deleted file mode 100644 index 79eba0d428..0000000000 --- a/test/cases/compile_errors/stage1/obj/local_variable_redeclaration.zig +++ /dev/null @@ -1,11 +0,0 @@ -export fn f() void { - const a : i32 = 0; - var a = 0; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:9: error: redeclaration of local constant 'a' -// tmp.zig:2:11: note: previous declaration here diff --git a/test/cases/compile_errors/stage1/obj/local_variable_redeclares_parameter.zig b/test/cases/compile_errors/stage1/obj/local_variable_redeclares_parameter.zig deleted file mode 100644 index 854ef080c5..0000000000 --- a/test/cases/compile_errors/stage1/obj/local_variable_redeclares_parameter.zig +++ /dev/null @@ -1,11 +0,0 @@ -fn f(a : i32) void { - const a = 0; -} -export fn entry() void { f(1); } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:11: error: redeclaration of function parameter 'a' -// tmp.zig:1:6: note: previous declaration here diff --git a/test/cases/compile_errors/stage1/obj/locally_shadowing_a_primitive_type.zig b/test/cases/compile_errors/stage1/obj/locally_shadowing_a_primitive_type.zig deleted file mode 100644 index e66a2082ea..0000000000 --- a/test/cases/compile_errors/stage1/obj/locally_shadowing_a_primitive_type.zig +++ /dev/null @@ -1,12 +0,0 @@ -export fn foo() void { - const u8 = u16; - const a: u8 = 300; - _ = a; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:11: error: name shadows primitive 'u8' -// tmp.zig:2:11: note: consider using @"u8" to disambiguate diff --git a/test/cases/compile_errors/stage1/obj/main_function_with_bogus_args_type.zig b/test/cases/compile_errors/stage1/obj/main_function_with_bogus_args_type.zig deleted file mode 100644 index 5808ab90ec..0000000000 --- a/test/cases/compile_errors/stage1/obj/main_function_with_bogus_args_type.zig +++ /dev/null @@ -1,7 +0,0 @@ -pub fn main(args: [][]bogus) !void {_ = args;} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:23: error: use of undeclared identifier 'bogus' diff --git a/test/cases/compile_errors/stage1/obj/missing_function_name.zig b/test/cases/compile_errors/stage1/obj/missing_function_name.zig deleted file mode 100644 index cd9a3ca7ee..0000000000 --- a/test/cases/compile_errors/stage1/obj/missing_function_name.zig +++ /dev/null @@ -1,8 +0,0 @@ -fn () void {} -export fn entry() usize { return @sizeOf(@TypeOf(f)); } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:1: error: missing function name diff --git a/test/cases/compile_errors/stage1/obj/multiple_function_definitions.zig b/test/cases/compile_errors/stage1/obj/multiple_function_definitions.zig deleted file mode 100644 index baa4a43048..0000000000 --- a/test/cases/compile_errors/stage1/obj/multiple_function_definitions.zig +++ /dev/null @@ -1,10 +0,0 @@ -fn a() void {} -fn a() void {} -export fn entry() void { a(); } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:1: error: redeclaration of 'a' -// tmp.zig:1:1: note: other declaration here diff --git a/test/cases/compile_errors/stage1/obj/normal_string_with_newline.zig b/test/cases/compile_errors/stage1/obj/normal_string_with_newline.zig deleted file mode 100644 index f6c1d1b1b9..0000000000 --- a/test/cases/compile_errors/stage1/obj/normal_string_with_newline.zig +++ /dev/null @@ -1,9 +0,0 @@ -const foo = "a -b"; - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:13: error: expected expression, found 'invalid bytes' -// tmp.zig:1:15: note: invalid byte: '\n' diff --git a/test/cases/compile_errors/stage1/obj/parameter_redeclaration.zig b/test/cases/compile_errors/stage1/obj/parameter_redeclaration.zig deleted file mode 100644 index 528eee8374..0000000000 --- a/test/cases/compile_errors/stage1/obj/parameter_redeclaration.zig +++ /dev/null @@ -1,10 +0,0 @@ -fn f(a : i32, a : i32) void { -} -export fn entry() void { f(1, 2); } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:15: error: redeclaration of function parameter 'a' -// tmp.zig:1:6: note: previous declaration here diff --git a/test/cases/compile_errors/stage1/obj/pointer_arithmetic_on_pointer-to-array.zig b/test/cases/compile_errors/stage1/obj/pointer_arithmetic_on_pointer-to-array.zig deleted file mode 100644 index 6542aa6968..0000000000 --- a/test/cases/compile_errors/stage1/obj/pointer_arithmetic_on_pointer-to-array.zig +++ /dev/null @@ -1,12 +0,0 @@ -export fn foo() void { - var x: [10]u8 = undefined; - var y = &x; - var z = y + 1; - _ = z; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:4:17: error: integer value 1 cannot be coerced to type '*[10]u8' diff --git a/test/cases/compile_errors/stage1/obj/prevent_bad_implicit_casting_of_anyframe_types.zig b/test/cases/compile_errors/stage1/obj/prevent_bad_implicit_casting_of_anyframe_types.zig index 2e21d45c12..066bf1c107 100644 --- a/test/cases/compile_errors/stage1/obj/prevent_bad_implicit_casting_of_anyframe_types.zig +++ b/test/cases/compile_errors/stage1/obj/prevent_bad_implicit_casting_of_anyframe_types.zig @@ -19,6 +19,6 @@ fn func() void {} // backend=stage1 // target=native // -// tmp.zig:3:28: error: expected type 'anyframe->i32', found 'anyframe' -// tmp.zig:8:28: error: expected type 'anyframe->i32', found 'i32' -// tmp.zig:13:29: error: expected type 'anyframe->i32', found '*@Frame(func)' +// :3:28: error: expected type 'anyframe->i32', found 'anyframe' +// :8:28: error: expected type 'anyframe->i32', found 'i32' +// tmp.zig:13:29: error: expected type 'anyframe->i32', found '*@Frame(func)' \ No newline at end of file diff --git a/test/cases/compile_errors/stage1/obj/redefinition_of_enums.zig b/test/cases/compile_errors/stage1/obj/redefinition_of_enums.zig deleted file mode 100644 index a95eb88935..0000000000 --- a/test/cases/compile_errors/stage1/obj/redefinition_of_enums.zig +++ /dev/null @@ -1,9 +0,0 @@ -const A = enum {x}; -const A = enum {x}; - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:1: error: redeclaration of 'A' -// tmp.zig:1:1: note: other declaration here diff --git a/test/cases/compile_errors/stage1/obj/redefinition_of_global_variables.zig b/test/cases/compile_errors/stage1/obj/redefinition_of_global_variables.zig deleted file mode 100644 index 3651946ffe..0000000000 --- a/test/cases/compile_errors/stage1/obj/redefinition_of_global_variables.zig +++ /dev/null @@ -1,9 +0,0 @@ -var a : i32 = 1; -var a : i32 = 2; - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:1: error: redeclaration of 'a' -// tmp.zig:1:1: note: other declaration here diff --git a/test/cases/compile_errors/stage1/obj/redefinition_of_struct.zig b/test/cases/compile_errors/stage1/obj/redefinition_of_struct.zig deleted file mode 100644 index 8367a05e43..0000000000 --- a/test/cases/compile_errors/stage1/obj/redefinition_of_struct.zig +++ /dev/null @@ -1,9 +0,0 @@ -const A = struct { x : i32, }; -const A = struct { y : i32, }; - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:1: error: redeclaration of 'A' -// tmp.zig:1:1: note: other declaration here diff --git a/test/cases/compile_errors/stage1/obj/runtime_cast_to_union_which_has_non-void_fields.zig b/test/cases/compile_errors/stage1/obj/runtime_cast_to_union_which_has_non-void_fields.zig deleted file mode 100644 index d25c815c7e..0000000000 --- a/test/cases/compile_errors/stage1/obj/runtime_cast_to_union_which_has_non-void_fields.zig +++ /dev/null @@ -1,20 +0,0 @@ -const Letter = enum { A, B, C }; -const Value = union(Letter) { - A: i32, - B, - C, -}; -export fn entry() void { - foo(Letter.A); -} -fn foo(l: Letter) void { - var x: Value = l; - _ = x; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:11:20: error: runtime cast to union 'Value' which has non-void fields -// tmp.zig:3:5: note: field 'A' has type 'i32' diff --git a/test/cases/compile_errors/stage1/obj/saturating_arithmetic_does_not_allow_floats.zig b/test/cases/compile_errors/stage1/obj/saturating_arithmetic_does_not_allow_floats.zig deleted file mode 100644 index 7bda134af6..0000000000 --- a/test/cases/compile_errors/stage1/obj/saturating_arithmetic_does_not_allow_floats.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn a() void { - _ = @as(f32, 1.0) +| @as(f32, 1.0); -} - -// error -// backend=stage1 -// target=native -// -// error: invalid operands to binary expression: 'f32' and 'f32' diff --git a/test/cases/compile_errors/stage1/obj/setAlignStack_outside_function.zig b/test/cases/compile_errors/stage1/obj/setAlignStack_outside_function.zig deleted file mode 100644 index 754756012b..0000000000 --- a/test/cases/compile_errors/stage1/obj/setAlignStack_outside_function.zig +++ /dev/null @@ -1,9 +0,0 @@ -comptime { - @setAlignStack(16); -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:5: error: @setAlignStack outside function diff --git a/test/cases/compile_errors/stage1/obj/setAlignStack_set_twice.zig b/test/cases/compile_errors/stage1/obj/setAlignStack_set_twice.zig deleted file mode 100644 index 244db9d962..0000000000 --- a/test/cases/compile_errors/stage1/obj/setAlignStack_set_twice.zig +++ /dev/null @@ -1,11 +0,0 @@ -export fn entry() void { - @setAlignStack(16); - @setAlignStack(16); -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:5: error: alignstack set twice -// tmp.zig:2:5: note: first set here diff --git a/test/cases/compile_errors/stage1/obj/setAlignStack_too_big.zig b/test/cases/compile_errors/stage1/obj/setAlignStack_too_big.zig deleted file mode 100644 index 601631e158..0000000000 --- a/test/cases/compile_errors/stage1/obj/setAlignStack_too_big.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn entry() void { - @setAlignStack(511 + 1); -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:5: error: attempt to @setAlignStack(512); maximum is 256 diff --git a/test/cases/compile_errors/stage1/obj/switch_expression-non_exhaustive_integer_prongs.zig b/test/cases/compile_errors/stage1/obj/switch_expression-non_exhaustive_integer_prongs.zig deleted file mode 100644 index 940338edb9..0000000000 --- a/test/cases/compile_errors/stage1/obj/switch_expression-non_exhaustive_integer_prongs.zig +++ /dev/null @@ -1,12 +0,0 @@ -fn foo(x: u8) void { - switch (x) { - 0 => {}, - } -} -export fn entry() usize { return @sizeOf(@TypeOf(foo)); } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:5: error: switch must handle all possibilities diff --git a/test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u1.zig b/test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u1.zig deleted file mode 100644 index 90abf5074b..0000000000 --- a/test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u1.zig +++ /dev/null @@ -1,14 +0,0 @@ -fn foo(x: u1) void { - switch (x) { - 0 => {}, - 1 => {}, - else => {}, - } -} -export fn entry() usize { return @sizeOf(@TypeOf(foo)); } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:5:9: error: unreachable else prong, all cases already handled diff --git a/test/cases/compile_errors/stage1/obj/switch_on_enum_with_1_field_with_no_prongs.zig b/test/cases/compile_errors/stage1/obj/switch_on_enum_with_1_field_with_no_prongs.zig deleted file mode 100644 index 97f7de121d..0000000000 --- a/test/cases/compile_errors/stage1/obj/switch_on_enum_with_1_field_with_no_prongs.zig +++ /dev/null @@ -1,12 +0,0 @@ -const Foo = enum { M }; - -export fn entry() void { - var f = Foo.M; - switch (f) {} -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:5:5: error: enumeration value 'Foo.M' not handled in switch diff --git a/test/cases/compile_errors/stage1/obj/type_variables_must_be_constant.zig b/test/cases/compile_errors/stage1/obj/type_variables_must_be_constant.zig index f8cadf8520..8273e24b99 100644 --- a/test/cases/compile_errors/stage1/obj/type_variables_must_be_constant.zig +++ b/test/cases/compile_errors/stage1/obj/type_variables_must_be_constant.zig @@ -7,4 +7,4 @@ export fn entry() foo { // backend=stage1 // target=native // -// tmp.zig:1:1: error: variable of type 'type' must be constant +// :1:1: error: variable of type 'type' must be constant diff --git a/test/cases/compile_errors/stage1/obj/undefined_as_field_type_is_rejected.zig b/test/cases/compile_errors/stage1/obj/undefined_as_field_type_is_rejected.zig deleted file mode 100644 index b6eb059661..0000000000 --- a/test/cases/compile_errors/stage1/obj/undefined_as_field_type_is_rejected.zig +++ /dev/null @@ -1,13 +0,0 @@ -const Foo = struct { - a: undefined, -}; -export fn entry1() void { - const foo: Foo = undefined; - _ = foo; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:8: error: use of undefined value here causes undefined behavior diff --git a/test/cases/compile_errors/stage1/obj/undefined_function_call.zig b/test/cases/compile_errors/stage1/obj/undefined_function_call.zig deleted file mode 100644 index a9627961fe..0000000000 --- a/test/cases/compile_errors/stage1/obj/undefined_function_call.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn a() void { - b(); -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:5: error: use of undeclared identifier 'b' diff --git a/test/cases/compile_errors/stage1/obj/union_fields_with_value_assignments.zig b/test/cases/compile_errors/stage1/obj/union_fields_with_value_assignments.zig deleted file mode 100644 index 94568d55c3..0000000000 --- a/test/cases/compile_errors/stage1/obj/union_fields_with_value_assignments.zig +++ /dev/null @@ -1,14 +0,0 @@ -const MultipleChoice = union { - A: i32 = 20, -}; -export fn entry() void { - var x: MultipleChoice = undefined; - _ = x; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:24: error: explicitly valued tagged union missing integer tag type -// tmp.zig:2:14: note: tag value specified here diff --git a/test/cases/compile_errors/stage1/obj/union_with_0_fields.zig b/test/cases/compile_errors/stage1/obj/union_with_0_fields.zig deleted file mode 100644 index a6f3d69faa..0000000000 --- a/test/cases/compile_errors/stage1/obj/union_with_0_fields.zig +++ /dev/null @@ -1,7 +0,0 @@ -const Foo = union {}; - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:13: error: union declarations must have at least one tag diff --git a/test/cases/compile_errors/stage1/obj/unreachable_code-nested_returns.zig b/test/cases/compile_errors/stage1/obj/unreachable_code-nested_returns.zig deleted file mode 100644 index 8a80fcd161..0000000000 --- a/test/cases/compile_errors/stage1/obj/unreachable_code-nested_returns.zig +++ /dev/null @@ -1,10 +0,0 @@ -export fn a() i32 { - return return 1; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:5: error: unreachable code -// tmp.zig:2:12: note: control flow is diverted here diff --git a/test/cases/compile_errors/stage1/obj/unreachable_code.zig b/test/cases/compile_errors/stage1/obj/unreachable_code.zig deleted file mode 100644 index 30a92d06bf..0000000000 --- a/test/cases/compile_errors/stage1/obj/unreachable_code.zig +++ /dev/null @@ -1,13 +0,0 @@ -export fn a() void { - return; - b(); -} - -fn b() void {} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:6: error: unreachable code -// tmp.zig:2:5: note: control flow is diverted here diff --git a/test/cases/compile_errors/stage1/obj/use_of_undeclared_identifier.zig b/test/cases/compile_errors/stage1/obj/use_of_undeclared_identifier.zig deleted file mode 100644 index c1297731a3..0000000000 --- a/test/cases/compile_errors/stage1/obj/use_of_undeclared_identifier.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn f() void { - b = 3; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:5: error: use of undeclared identifier 'b' diff --git a/test/cases/compile_errors/stage1/obj/usingnamespace_with_wrong_type.zig b/test/cases/compile_errors/stage1/obj/usingnamespace_with_wrong_type.zig deleted file mode 100644 index 86b5f0dcfb..0000000000 --- a/test/cases/compile_errors/stage1/obj/usingnamespace_with_wrong_type.zig +++ /dev/null @@ -1,7 +0,0 @@ -usingnamespace void; - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:1: error: expected struct, enum, or union; found 'void' diff --git a/test/cases/compile_errors/stage1/obj/variable_has_wrong_type.zig b/test/cases/compile_errors/stage1/obj/variable_has_wrong_type.zig deleted file mode 100644 index 52dcf150de..0000000000 --- a/test/cases/compile_errors/stage1/obj/variable_has_wrong_type.zig +++ /dev/null @@ -1,10 +0,0 @@ -export fn f() i32 { - const a = "a"; - return a; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:12: error: expected type 'i32', found '*const [1:0]u8' diff --git a/test/cases/compile_errors/stage1/obj/variable_with_type_noreturn.zig b/test/cases/compile_errors/stage1/obj/variable_with_type_noreturn.zig deleted file mode 100644 index e2ba9183be..0000000000 --- a/test/cases/compile_errors/stage1/obj/variable_with_type_noreturn.zig +++ /dev/null @@ -1,10 +0,0 @@ -export fn entry9() void { - var z: noreturn = return; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:5: error: unreachable code -// tmp.zig:2:23: note: control flow is diverted here diff --git a/test/cases/compile_errors/stage1/obj/volatile_on_global_assembly.zig b/test/cases/compile_errors/stage1/obj/volatile_on_global_assembly.zig deleted file mode 100644 index 2157fe6a71..0000000000 --- a/test/cases/compile_errors/stage1/obj/volatile_on_global_assembly.zig +++ /dev/null @@ -1,9 +0,0 @@ -comptime { - asm volatile (""); -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:9: error: volatile is meaningless on global assembly diff --git a/test/cases/compile_errors/stage1/test/bad_splat_type.zig b/test/cases/compile_errors/stage1/test/bad_splat_type.zig deleted file mode 100644 index 94e3d052a6..0000000000 --- a/test/cases/compile_errors/stage1/test/bad_splat_type.zig +++ /dev/null @@ -1,12 +0,0 @@ -export fn entry() void { - const c = 4; - var v = @splat(4, c); - _ = v; -} - -// error -// backend=stage1 -// target=native -// is_test=1 -// -// tmp.zig:3:23: error: vector element type must be integer, float, bool, or pointer; 'comptime_int' is invalid diff --git a/test/cases/compile_errors/stage1/test/non-exhaustive_enum_marker_assigned_a_value.zig b/test/cases/compile_errors/stage1/test/non-exhaustive_enum_marker_assigned_a_value.zig deleted file mode 100644 index 609af248df..0000000000 --- a/test/cases/compile_errors/stage1/test/non-exhaustive_enum_marker_assigned_a_value.zig +++ /dev/null @@ -1,20 +0,0 @@ -const A = enum { - a, - b, - _ = 1, -}; -const B = enum { - a, - b, - _, -}; -comptime { _ = A; _ = B; } - -// error -// backend=stage1 -// target=native -// is_test=1 -// -// tmp.zig:4:9: error: '_' is used to mark an enum as non-exhaustive and cannot be assigned a value -// tmp.zig:6:11: error: non-exhaustive enum missing integer tag type -// tmp.zig:9:5: note: marked non-exhaustive here diff --git a/test/cases/compile_errors/stage1/test/non-exhaustive_enums.zig b/test/cases/compile_errors/stage1/test/non-exhaustive_enums.zig deleted file mode 100644 index 3b5f8af7cb..0000000000 --- a/test/cases/compile_errors/stage1/test/non-exhaustive_enums.zig +++ /dev/null @@ -1,22 +0,0 @@ -const B = enum(u1) { - a, - _, - b, -}; -const C = enum(u1) { - a, - b, - _, -}; -pub export fn entry() void { - _ = B; - _ = C; -} - -// error -// backend=stage1 -// target=native -// is_test=1 -// -// tmp.zig:3:5: error: '_' field of non-exhaustive enum must be last -// tmp.zig:6:11: error: non-exhaustive enum specifies every value diff --git a/test/cases/compile_errors/stage1/test/reify_typeOf_with_no_arguments.zig b/test/cases/compile_errors/stage1/test/reify_typeOf_with_no_arguments.zig deleted file mode 100644 index 0626d45393..0000000000 --- a/test/cases/compile_errors/stage1/test/reify_typeOf_with_no_arguments.zig +++ /dev/null @@ -1,10 +0,0 @@ -export fn entry() void { - _ = @TypeOf(); -} - -// error -// backend=stage1 -// target=native -// is_test=1 -// -// tmp.zig:2:9: error: expected at least 1 argument, found 0 diff --git a/test/cases/compile_errors/stage1/test/reject_extern_variables_with_initializers.zig b/test/cases/compile_errors/stage1/test/reject_extern_variables_with_initializers.zig deleted file mode 100644 index 54239e0948..0000000000 --- a/test/cases/compile_errors/stage1/test/reject_extern_variables_with_initializers.zig +++ /dev/null @@ -1,8 +0,0 @@ -extern var foo: int = 2; - -// error -// backend=stage1 -// target=native -// is_test=1 -// -// tmp.zig:1:23: error: extern variables have no initializers diff --git a/test/cases/compile_errors/stage1/test/return_invalid_type_from_test.zig b/test/cases/compile_errors/stage1/test/return_invalid_type_from_test.zig deleted file mode 100644 index 590ac30918..0000000000 --- a/test/cases/compile_errors/stage1/test/return_invalid_type_from_test.zig +++ /dev/null @@ -1,8 +0,0 @@ -test "example" { return 1; } - -// error -// backend=stage1 -// target=native -// is_test=1 -// -// tmp.zig:1:25: error: expected type 'void', found 'comptime_int' diff --git a/test/cases/compile_errors/stage1/obj/struct_field_missing_type.zig b/test/cases/compile_errors/struct_field_missing_type.zig similarity index 66% rename from test/cases/compile_errors/stage1/obj/struct_field_missing_type.zig rename to test/cases/compile_errors/struct_field_missing_type.zig index 175f1c7df7..0e9151a5eb 100644 --- a/test/cases/compile_errors/stage1/obj/struct_field_missing_type.zig +++ b/test/cases/compile_errors/struct_field_missing_type.zig @@ -7,7 +7,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:5: error: struct field missing type +// :2:5: error: struct field missing type diff --git a/test/cases/compile_errors/stage1/obj/struct_init_syntax_for_array.zig b/test/cases/compile_errors/struct_init_syntax_for_array.zig similarity index 52% rename from test/cases/compile_errors/stage1/obj/struct_init_syntax_for_array.zig rename to test/cases/compile_errors/struct_init_syntax_for_array.zig index 6362e05114..025d0057fe 100644 --- a/test/cases/compile_errors/stage1/obj/struct_init_syntax_for_array.zig +++ b/test/cases/compile_errors/struct_init_syntax_for_array.zig @@ -4,7 +4,7 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:13: error: initializing array with struct syntax +// :1:13: error: initializing array with struct syntax diff --git a/test/cases/compile_errors/stage1/obj/struct_with_declarations_unavailable_for_reify_type.zig b/test/cases/compile_errors/struct_with_declarations_unavailable_for_reify_type.zig similarity index 56% rename from test/cases/compile_errors/stage1/obj/struct_with_declarations_unavailable_for_reify_type.zig rename to test/cases/compile_errors/struct_with_declarations_unavailable_for_reify_type.zig index 409020ca25..81864160dc 100644 --- a/test/cases/compile_errors/stage1/obj/struct_with_declarations_unavailable_for_reify_type.zig +++ b/test/cases/compile_errors/struct_with_declarations_unavailable_for_reify_type.zig @@ -3,7 +3,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:15: error: Type.Struct.decls must be empty for @Type +// :2:9: error: reified structs must have no decls diff --git a/test/cases/compile_errors/stage1/obj/struct_with_invalid_field.zig b/test/cases/compile_errors/struct_with_invalid_field.zig similarity index 85% rename from test/cases/compile_errors/stage1/obj/struct_with_invalid_field.zig rename to test/cases/compile_errors/struct_with_invalid_field.zig index dcc9d26f65..aa24b029bb 100644 --- a/test/cases/compile_errors/stage1/obj/struct_with_invalid_field.zig +++ b/test/cases/compile_errors/struct_with_invalid_field.zig @@ -24,7 +24,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:14:17: error: use of undeclared identifier 'HeaderValue' +// :14:17: error: use of undeclared identifier 'HeaderValue' diff --git a/test/cases/compile_errors/stage1/obj/suspend_inside_suspend_block.zig b/test/cases/compile_errors/suspend_inside_suspend_block.zig similarity index 53% rename from test/cases/compile_errors/stage1/obj/suspend_inside_suspend_block.zig rename to test/cases/compile_errors/suspend_inside_suspend_block.zig index 4adffd2f25..482515f905 100644 --- a/test/cases/compile_errors/stage1/obj/suspend_inside_suspend_block.zig +++ b/test/cases/compile_errors/suspend_inside_suspend_block.zig @@ -9,8 +9,8 @@ fn foo() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:6:9: error: cannot suspend inside suspend block -// tmp.zig:5:5: note: other suspend block here +// :6:9: error: cannot suspend inside suspend block +// :5:5: note: other suspend block here diff --git a/test/cases/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong.zig b/test/cases/compile_errors/switch_expression-duplicate_enumeration_prong.zig similarity index 63% rename from test/cases/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong.zig rename to test/cases/compile_errors/switch_expression-duplicate_enumeration_prong.zig index 056aebe3dd..7011f0a2d5 100644 --- a/test/cases/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong.zig +++ b/test/cases/compile_errors/switch_expression-duplicate_enumeration_prong.zig @@ -14,11 +14,11 @@ fn f(n: Number) i32 { } } -export fn entry() usize { return @sizeOf(@TypeOf(f)); } +export fn entry() usize { return @sizeOf(@TypeOf(&f)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:13:15: error: duplicate switch value -// tmp.zig:10:15: note: other value here +// :13:15: error: duplicate switch value +// :10:15: note: previous value here diff --git a/test/cases/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong_when_else_present.zig b/test/cases/compile_errors/switch_expression-duplicate_enumeration_prong_when_else_present.zig similarity index 64% rename from test/cases/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong_when_else_present.zig rename to test/cases/compile_errors/switch_expression-duplicate_enumeration_prong_when_else_present.zig index 4d891c3917..89f1959375 100644 --- a/test/cases/compile_errors/stage1/obj/switch_expression-duplicate_enumeration_prong_when_else_present.zig +++ b/test/cases/compile_errors/switch_expression-duplicate_enumeration_prong_when_else_present.zig @@ -15,11 +15,11 @@ fn f(n: Number) i32 { } } -export fn entry() usize { return @sizeOf(@TypeOf(f)); } +export fn entry() usize { return @sizeOf(@TypeOf(&f)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:13:15: error: duplicate switch value -// tmp.zig:10:15: note: other value here +// :13:15: error: duplicate switch value +// :10:15: note: previous value here diff --git a/test/cases/compile_errors/stage1/obj/switch_expression-duplicate_type.zig b/test/cases/compile_errors/switch_expression-duplicate_type.zig similarity index 70% rename from test/cases/compile_errors/stage1/obj/switch_expression-duplicate_type.zig rename to test/cases/compile_errors/switch_expression-duplicate_type.zig index b84c4622a2..59c9b0657a 100644 --- a/test/cases/compile_errors/stage1/obj/switch_expression-duplicate_type.zig +++ b/test/cases/compile_errors/switch_expression-duplicate_type.zig @@ -10,8 +10,8 @@ fn foo(comptime T: type, x: T) u8 { export fn entry() usize { return @sizeOf(@TypeOf(foo(u32, 0))); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:6:9: error: duplicate switch value -// tmp.zig:4:9: note: previous value here +// :6:9: error: duplicate switch value +// :4:9: note: previous value here diff --git a/test/cases/compile_errors/stage1/obj/switch_expression-duplicate_type_struct_alias.zig b/test/cases/compile_errors/switch_expression-duplicate_type_struct_alias.zig similarity index 74% rename from test/cases/compile_errors/stage1/obj/switch_expression-duplicate_type_struct_alias.zig rename to test/cases/compile_errors/switch_expression-duplicate_type_struct_alias.zig index 057e674e5a..797d2bd50d 100644 --- a/test/cases/compile_errors/stage1/obj/switch_expression-duplicate_type_struct_alias.zig +++ b/test/cases/compile_errors/switch_expression-duplicate_type_struct_alias.zig @@ -14,8 +14,8 @@ fn foo(comptime T: type, x: T) u8 { export fn entry() usize { return @sizeOf(@TypeOf(foo(u32, 0))); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:10:9: error: duplicate switch value -// tmp.zig:8:9: note: previous value here +// :10:9: error: duplicate switch value +// :8:9: note: previous value here diff --git a/test/cases/compile_errors/stage1/obj/switch_expression-missing_enumeration_prong.zig b/test/cases/compile_errors/switch_expression-missing_enumeration_prong.zig similarity index 50% rename from test/cases/compile_errors/stage1/obj/switch_expression-missing_enumeration_prong.zig rename to test/cases/compile_errors/switch_expression-missing_enumeration_prong.zig index 1a13730223..20b373f6b7 100644 --- a/test/cases/compile_errors/stage1/obj/switch_expression-missing_enumeration_prong.zig +++ b/test/cases/compile_errors/switch_expression-missing_enumeration_prong.zig @@ -12,10 +12,12 @@ fn f(n: Number) i32 { } } -export fn entry() usize { return @sizeOf(@TypeOf(f)); } +export fn entry() usize { return @sizeOf(@TypeOf(&f)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:8:5: error: enumeration value 'Number.Four' not handled in switch +// :8:5: error: switch must handle all possibilities +// :8:5: note: unhandled enumeration value: 'Four' +// :1:16: note: enum 'tmp.Number' declared here diff --git a/test/cases/compile_errors/stage1/obj/switch_expression-multiple_else_prongs.zig b/test/cases/compile_errors/switch_expression-multiple_else_prongs.zig similarity index 63% rename from test/cases/compile_errors/stage1/obj/switch_expression-multiple_else_prongs.zig rename to test/cases/compile_errors/switch_expression-multiple_else_prongs.zig index 8267222092..f4cdb3b125 100644 --- a/test/cases/compile_errors/stage1/obj/switch_expression-multiple_else_prongs.zig +++ b/test/cases/compile_errors/switch_expression-multiple_else_prongs.zig @@ -10,7 +10,8 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:5:9: error: multiple else prongs in switch expression +// :5:9: error: multiple else prongs in switch expression +// :4:9: note: previous else prong here diff --git a/test/cases/compile_errors/switch_expression-non_exhaustive_integer_prongs.zig b/test/cases/compile_errors/switch_expression-non_exhaustive_integer_prongs.zig new file mode 100644 index 0000000000..6770b5a055 --- /dev/null +++ b/test/cases/compile_errors/switch_expression-non_exhaustive_integer_prongs.zig @@ -0,0 +1,12 @@ +fn foo(x: u8) void { + switch (x) { + 0 => {}, + } +} +export fn entry() usize { return @sizeOf(@TypeOf(&foo)); } + +// error +// backend=stage2 +// target=native +// +// :2:5: error: switch must handle all possibilities diff --git a/test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_enum.zig b/test/cases/compile_errors/switch_expression-unreachable_else_prong_enum.zig similarity index 67% rename from test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_enum.zig rename to test/cases/compile_errors/switch_expression-unreachable_else_prong_enum.zig index bcf840ab97..21440a63a1 100644 --- a/test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_enum.zig +++ b/test/cases/compile_errors/switch_expression-unreachable_else_prong_enum.zig @@ -15,10 +15,10 @@ fn foo(x: u8) void { } } -export fn entry() usize { return @sizeOf(@TypeOf(foo)); } +export fn entry() usize { return @sizeOf(@TypeOf(&foo)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:14:9: error: unreachable else prong, all cases already handled +// :14:14: error: unreachable else prong; all cases already handled diff --git a/test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_i8.zig b/test/cases/compile_errors/switch_expression-unreachable_else_prong_range_i8.zig similarity index 56% rename from test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_i8.zig rename to test/cases/compile_errors/switch_expression-unreachable_else_prong_range_i8.zig index f0c9d7a08a..bcb84eed12 100644 --- a/test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_i8.zig +++ b/test/cases/compile_errors/switch_expression-unreachable_else_prong_range_i8.zig @@ -8,10 +8,10 @@ fn foo(x: i8) void { else => {}, } } -export fn entry() usize { return @sizeOf(@TypeOf(foo)); } +export fn entry() usize { return @sizeOf(@TypeOf(&foo)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:8:9: error: unreachable else prong, all cases already handled +// :8:14: error: unreachable else prong; all cases already handled diff --git a/test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_u8.zig b/test/cases/compile_errors/switch_expression-unreachable_else_prong_range_u8.zig similarity index 55% rename from test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_u8.zig rename to test/cases/compile_errors/switch_expression-unreachable_else_prong_range_u8.zig index 9342ce8c32..2230434ea0 100644 --- a/test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_range_u8.zig +++ b/test/cases/compile_errors/switch_expression-unreachable_else_prong_range_u8.zig @@ -8,10 +8,10 @@ fn foo(x: u8) void { else => {}, } } -export fn entry() usize { return @sizeOf(@TypeOf(foo)); } +export fn entry() usize { return @sizeOf(@TypeOf(&foo)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:8:9: error: unreachable else prong, all cases already handled +// :8:14: error: unreachable else prong; all cases already handled diff --git a/test/cases/compile_errors/switch_expression-unreachable_else_prong_u1.zig b/test/cases/compile_errors/switch_expression-unreachable_else_prong_u1.zig new file mode 100644 index 0000000000..58458474a4 --- /dev/null +++ b/test/cases/compile_errors/switch_expression-unreachable_else_prong_u1.zig @@ -0,0 +1,14 @@ +fn foo(x: u1) void { + switch (x) { + 0 => {}, + 1 => {}, + else => {}, + } +} +export fn entry() usize { return @sizeOf(@TypeOf(&foo)); } + +// error +// backend=stage2 +// target=native +// +// :5:14: error: unreachable else prong; all cases already handled diff --git a/test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u2.zig b/test/cases/compile_errors/switch_expression-unreachable_else_prong_u2.zig similarity index 52% rename from test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u2.zig rename to test/cases/compile_errors/switch_expression-unreachable_else_prong_u2.zig index a523046e16..9e337cb200 100644 --- a/test/cases/compile_errors/stage1/obj/switch_expression-unreachable_else_prong_u2.zig +++ b/test/cases/compile_errors/switch_expression-unreachable_else_prong_u2.zig @@ -7,10 +7,10 @@ fn foo(x: u2) void { else => {}, } } -export fn entry() usize { return @sizeOf(@TypeOf(foo)); } +export fn entry() usize { return @sizeOf(@TypeOf(&foo)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:7:9: error: unreachable else prong, all cases already handled +// :7:14: error: unreachable else prong; all cases already handled diff --git a/test/cases/compile_errors/switch_on_enum_with_1_field_with_no_prongs.zig b/test/cases/compile_errors/switch_on_enum_with_1_field_with_no_prongs.zig new file mode 100644 index 0000000000..dbf1cd948f --- /dev/null +++ b/test/cases/compile_errors/switch_on_enum_with_1_field_with_no_prongs.zig @@ -0,0 +1,14 @@ +const Foo = enum { M }; + +export fn entry() void { + var f = Foo.M; + switch (f) {} +} + +// error +// backend=stage2 +// target=native +// +// :5:5: error: switch must handle all possibilities +// :5:5: note: unhandled enumeration value: 'M' +// :1:13: note: enum 'tmp.Foo' declared here diff --git a/test/cases/compile_errors/stage1/test/switching_with_exhaustive_enum_has___prong_.zig b/test/cases/compile_errors/switching_with_exhaustive_enum_has___prong_.zig similarity index 59% rename from test/cases/compile_errors/stage1/test/switching_with_exhaustive_enum_has___prong_.zig rename to test/cases/compile_errors/switching_with_exhaustive_enum_has___prong_.zig index 0ca489bdb8..3dad0be9df 100644 --- a/test/cases/compile_errors/stage1/test/switching_with_exhaustive_enum_has___prong_.zig +++ b/test/cases/compile_errors/switching_with_exhaustive_enum_has___prong_.zig @@ -12,8 +12,8 @@ pub export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:7:5: error: switch on exhaustive enum has `_` prong +// :7:5: error: '_' prong only allowed when switching on non-exhaustive enums +// :10:11: note: '_' prong here diff --git a/test/cases/compile_errors/stage1/test/tagName_on_invalid_value_of_non-exhaustive_enum.zig b/test/cases/compile_errors/tagName_on_invalid_value_of_non-exhaustive_enum.zig similarity index 54% rename from test/cases/compile_errors/stage1/test/tagName_on_invalid_value_of_non-exhaustive_enum.zig rename to test/cases/compile_errors/tagName_on_invalid_value_of_non-exhaustive_enum.zig index a98470b681..e9e63f0d7a 100644 --- a/test/cases/compile_errors/stage1/test/tagName_on_invalid_value_of_non-exhaustive_enum.zig +++ b/test/cases/compile_errors/tagName_on_invalid_value_of_non-exhaustive_enum.zig @@ -4,8 +4,9 @@ test "enum" { } // error -// backend=stage1 +// backend=stage2 // target=native // is_test=1 // -// tmp.zig:3:18: error: no tag by value 5 +// :3:9: error: no field with value '5' in enum 'test.enum.E' +// :1:1: note: declared here diff --git a/test/cases/compile_errors/stage1/obj/threadlocal_qualifier_on_const.zig b/test/cases/compile_errors/threadlocal_qualifier_on_const.zig similarity index 55% rename from test/cases/compile_errors/stage1/obj/threadlocal_qualifier_on_const.zig rename to test/cases/compile_errors/threadlocal_qualifier_on_const.zig index d34ba06879..21371785f3 100644 --- a/test/cases/compile_errors/stage1/obj/threadlocal_qualifier_on_const.zig +++ b/test/cases/compile_errors/threadlocal_qualifier_on_const.zig @@ -4,7 +4,7 @@ export fn entry() i32 { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:1: error: threadlocal variable cannot be constant +// :1:1: error: threadlocal variable cannot be constant diff --git a/test/cases/compile_errors/stage1/obj/truncate_sign_mismatch.zig b/test/cases/compile_errors/truncate_sign_mismatch.zig similarity index 55% rename from test/cases/compile_errors/stage1/obj/truncate_sign_mismatch.zig rename to test/cases/compile_errors/truncate_sign_mismatch.zig index 8434d9ed16..a05660e28c 100644 --- a/test/cases/compile_errors/stage1/obj/truncate_sign_mismatch.zig +++ b/test/cases/compile_errors/truncate_sign_mismatch.zig @@ -16,10 +16,10 @@ export fn entry4() u8 { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:26: error: expected signed integer type, found 'u32' -// tmp.zig:7:26: error: expected unsigned integer type, found 'i32' -// tmp.zig:11:26: error: expected signed integer type, found 'u32' -// tmp.zig:15:26: error: expected unsigned integer type, found 'i32' +// :3:26: error: expected signed integer type, found 'u32' +// :7:26: error: expected unsigned integer type, found 'i32' +// :11:26: error: expected signed integer type, found 'u32' +// :15:26: error: expected unsigned integer type, found 'i32' diff --git a/test/cases/compile_errors/stage1/obj/try_in_function_with_non_error_return_type.zig b/test/cases/compile_errors/try_in_function_with_non_error_return_type.zig similarity index 57% rename from test/cases/compile_errors/stage1/obj/try_in_function_with_non_error_return_type.zig rename to test/cases/compile_errors/try_in_function_with_non_error_return_type.zig index c00fa2709f..2107c2a397 100644 --- a/test/cases/compile_errors/stage1/obj/try_in_function_with_non_error_return_type.zig +++ b/test/cases/compile_errors/try_in_function_with_non_error_return_type.zig @@ -4,7 +4,7 @@ export fn f() void { fn something() anyerror!void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:5: error: expected type 'void', found 'anyerror' +// :2:5: error: expected type 'void', found 'anyerror' diff --git a/test/cases/compile_errors/stage1/test/type_mismatch_in_C_prototype_with_varargs.zig b/test/cases/compile_errors/type_mismatch_in_C_prototype_with_varargs.zig similarity index 56% rename from test/cases/compile_errors/stage1/test/type_mismatch_in_C_prototype_with_varargs.zig rename to test/cases/compile_errors/type_mismatch_in_C_prototype_with_varargs.zig index 1f78ef3999..125ded95d6 100644 --- a/test/cases/compile_errors/stage1/test/type_mismatch_in_C_prototype_with_varargs.zig +++ b/test/cases/compile_errors/type_mismatch_in_C_prototype_with_varargs.zig @@ -7,8 +7,7 @@ export fn main() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:5:22: error: expected type 'fn([*c]u8, ...) callconv(.C) void', found 'fn([*:0]u8, ...) callconv(.C) void' +// :5:22: error: expected type 'fn([*c]u8, ...) callconv(.C) void', found 'fn([*:0]u8, ...) callconv(.C) void' diff --git a/test/cases/compile_errors/stage1/obj/undeclared_identifier.zig b/test/cases/compile_errors/undeclared_identifier.zig similarity index 51% rename from test/cases/compile_errors/stage1/obj/undeclared_identifier.zig rename to test/cases/compile_errors/undeclared_identifier.zig index 297a618eb8..7edc0d2896 100644 --- a/test/cases/compile_errors/stage1/obj/undeclared_identifier.zig +++ b/test/cases/compile_errors/undeclared_identifier.zig @@ -5,7 +5,7 @@ export fn a() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:5: error: use of undeclared identifier 'b' +// :3:5: error: use of undeclared identifier 'b' diff --git a/test/cases/compile_errors/stage1/obj/undeclared_identifier_error_should_mark_fn_as_impure.zig b/test/cases/compile_errors/undeclared_identifier_error_should_mark_fn_as_impure.zig similarity index 59% rename from test/cases/compile_errors/stage1/obj/undeclared_identifier_error_should_mark_fn_as_impure.zig rename to test/cases/compile_errors/undeclared_identifier_error_should_mark_fn_as_impure.zig index 697b7ccb55..f234bde8b8 100644 --- a/test/cases/compile_errors/stage1/obj/undeclared_identifier_error_should_mark_fn_as_impure.zig +++ b/test/cases/compile_errors/undeclared_identifier_error_should_mark_fn_as_impure.zig @@ -6,7 +6,7 @@ fn test_a_thing() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:5:5: error: use of undeclared identifier 'bad_fn_call' +// :5:5: error: use of undeclared identifier 'bad_fn_call' diff --git a/test/cases/compile_errors/stage1/obj/undeclared_identifier_in_unanalyzed_branch.zig b/test/cases/compile_errors/undeclared_identifier_in_unanalyzed_branch.zig similarity index 55% rename from test/cases/compile_errors/stage1/obj/undeclared_identifier_in_unanalyzed_branch.zig rename to test/cases/compile_errors/undeclared_identifier_in_unanalyzed_branch.zig index 2efa8ca115..edbe156e6b 100644 --- a/test/cases/compile_errors/stage1/obj/undeclared_identifier_in_unanalyzed_branch.zig +++ b/test/cases/compile_errors/undeclared_identifier_in_unanalyzed_branch.zig @@ -5,7 +5,7 @@ export fn a() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:9: error: use of undeclared identifier 'lol_this_doesnt_exist' +// :3:9: error: use of undeclared identifier 'lol_this_doesnt_exist' diff --git a/test/cases/compile_errors/undefined_as_field_type_is_rejected.zig b/test/cases/compile_errors/undefined_as_field_type_is_rejected.zig new file mode 100644 index 0000000000..e78cadc878 --- /dev/null +++ b/test/cases/compile_errors/undefined_as_field_type_is_rejected.zig @@ -0,0 +1,9 @@ +export fn a() void { + b(); +} + +// error +// backend=stage2 +// target=native +// +// :2:5: error: use of undeclared identifier 'b' diff --git a/test/cases/compile_errors/undefined_function_call.zig b/test/cases/compile_errors/undefined_function_call.zig new file mode 100644 index 0000000000..e78cadc878 --- /dev/null +++ b/test/cases/compile_errors/undefined_function_call.zig @@ -0,0 +1,9 @@ +export fn a() void { + b(); +} + +// error +// backend=stage2 +// target=native +// +// :2:5: error: use of undeclared identifier 'b' diff --git a/test/cases/compile_errors/stage1/obj/underscore_is_not_a_declarable_symbol.zig b/test/cases/compile_errors/underscore_is_not_a_declarable_symbol.zig similarity index 51% rename from test/cases/compile_errors/stage1/obj/underscore_is_not_a_declarable_symbol.zig rename to test/cases/compile_errors/underscore_is_not_a_declarable_symbol.zig index f4318af8fd..04cc996d05 100644 --- a/test/cases/compile_errors/stage1/obj/underscore_is_not_a_declarable_symbol.zig +++ b/test/cases/compile_errors/underscore_is_not_a_declarable_symbol.zig @@ -4,7 +4,7 @@ export fn f1() usize { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:9: error: '_' used as an identifier without @"_" syntax +// :2:9: error: '_' used as an identifier without @"_" syntax diff --git a/test/cases/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_for.zig b/test/cases/compile_errors/underscore_should_not_be_usable_inside_for.zig similarity index 64% rename from test/cases/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_for.zig rename to test/cases/compile_errors/underscore_should_not_be_usable_inside_for.zig index fe89c8372d..1fb79e11bd 100644 --- a/test/cases/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_for.zig +++ b/test/cases/compile_errors/underscore_should_not_be_usable_inside_for.zig @@ -7,7 +7,7 @@ export fn returns() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:20: error: '_' used as an identifier without @"_" syntax +// :4:20: error: '_' used as an identifier without @"_" syntax diff --git a/test/cases/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while.zig b/test/cases/compile_errors/underscore_should_not_be_usable_inside_while.zig similarity index 70% rename from test/cases/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while.zig rename to test/cases/compile_errors/underscore_should_not_be_usable_inside_while.zig index 7f694e0603..07d72393c7 100644 --- a/test/cases/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while.zig +++ b/test/cases/compile_errors/underscore_should_not_be_usable_inside_while.zig @@ -10,7 +10,7 @@ fn optionalReturn() ?u32 { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:20: error: '_' used as an identifier without @"_" syntax +// :4:20: error: '_' used as an identifier without @"_" syntax diff --git a/test/cases/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while_else.zig b/test/cases/compile_errors/underscore_should_not_be_usable_inside_while_else.zig similarity index 78% rename from test/cases/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while_else.zig rename to test/cases/compile_errors/underscore_should_not_be_usable_inside_while_else.zig index 87a705ee69..74d5a86a24 100644 --- a/test/cases/compile_errors/stage1/obj/underscore_should_not_be_usable_inside_while_else.zig +++ b/test/cases/compile_errors/underscore_should_not_be_usable_inside_while_else.zig @@ -12,7 +12,7 @@ fn optionalReturnError() !?u32 { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:6:17: error: '_' used as an identifier without @"_" syntax +// :6:17: error: '_' used as an identifier without @"_" syntax diff --git a/test/cases/compile_errors/union_fields_with_value_assignments.zig b/test/cases/compile_errors/union_fields_with_value_assignments.zig new file mode 100644 index 0000000000..2121568dd2 --- /dev/null +++ b/test/cases/compile_errors/union_fields_with_value_assignments.zig @@ -0,0 +1,7 @@ +const Foo = union {}; + +// error +// backend=stage2 +// target=native +// +// :1:13: error: union declarations must have at least one tag diff --git a/test/cases/compile_errors/union_with_0_fields.zig b/test/cases/compile_errors/union_with_0_fields.zig new file mode 100644 index 0000000000..2121568dd2 --- /dev/null +++ b/test/cases/compile_errors/union_with_0_fields.zig @@ -0,0 +1,7 @@ +const Foo = union {}; + +// error +// backend=stage2 +// target=native +// +// :1:13: error: union declarations must have at least one tag diff --git a/test/cases/compile_errors/stage1/obj/union_with_specified_enum_omits_field.zig b/test/cases/compile_errors/union_with_specified_enum_omits_field.zig similarity index 56% rename from test/cases/compile_errors/stage1/obj/union_with_specified_enum_omits_field.zig rename to test/cases/compile_errors/union_with_specified_enum_omits_field.zig index 0bb677a18c..3033feb376 100644 --- a/test/cases/compile_errors/stage1/obj/union_with_specified_enum_omits_field.zig +++ b/test/cases/compile_errors/union_with_specified_enum_omits_field.zig @@ -12,8 +12,9 @@ export fn entry() usize { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:6:17: error: enum field missing: 'C' -// tmp.zig:4:5: note: declared here +// :6:1: error: enum field(s) missing in union +// :4:5: note: field 'C' missing, declared here +// :1:16: note: enum declared here diff --git a/test/cases/compile_errors/stage1/obj/unreachable_code-double_break.zig b/test/cases/compile_errors/unreachable_code-double_break.zig similarity index 52% rename from test/cases/compile_errors/stage1/obj/unreachable_code-double_break.zig rename to test/cases/compile_errors/unreachable_code-double_break.zig index 0a19337af8..f8e4b86a90 100644 --- a/test/cases/compile_errors/stage1/obj/unreachable_code-double_break.zig +++ b/test/cases/compile_errors/unreachable_code-double_break.zig @@ -2,11 +2,12 @@ export fn a() void { const b = blk: { break :blk break :blk @as(u32, 1); }; + _ = b; } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:9: error: unreachable code -// tmp.zig:3:20: note: control flow is diverted here +// :3:9: error: unreachable code +// :3:20: note: control flow is diverted here diff --git a/test/cases/compile_errors/unreachable_code-nested_returns.zig b/test/cases/compile_errors/unreachable_code-nested_returns.zig new file mode 100644 index 0000000000..4f71ab2cec --- /dev/null +++ b/test/cases/compile_errors/unreachable_code-nested_returns.zig @@ -0,0 +1,10 @@ +export fn a() i32 { + return return 1; +} + +// error +// backend=stage2 +// target=native +// +// :2:5: error: unreachable code +// :2:12: note: control flow is diverted here diff --git a/test/cases/compile_errors/unreachable_code.zig b/test/cases/compile_errors/unreachable_code.zig new file mode 100644 index 0000000000..25c412d254 --- /dev/null +++ b/test/cases/compile_errors/unreachable_code.zig @@ -0,0 +1,13 @@ +export fn a() void { + return; + b(); +} + +fn b() void {} + +// error +// backend=stage2 +// target=native +// +// :3:6: error: unreachable code +// :2:5: note: control flow is diverted here diff --git a/test/cases/compile_errors/stage1/obj/unreachable_variable.zig b/test/cases/compile_errors/unreachable_variable.zig similarity index 53% rename from test/cases/compile_errors/stage1/obj/unreachable_variable.zig rename to test/cases/compile_errors/unreachable_variable.zig index 7f822a8bce..fac4b72196 100644 --- a/test/cases/compile_errors/stage1/obj/unreachable_variable.zig +++ b/test/cases/compile_errors/unreachable_variable.zig @@ -4,7 +4,7 @@ export fn f() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:25: error: expected type 'noreturn', found 'void' +// :2:25: error: expected type 'noreturn', found 'void' diff --git a/test/cases/compile_errors/stage1/obj/unreachable_with_return.zig b/test/cases/compile_errors/unreachable_with_return.zig similarity index 52% rename from test/cases/compile_errors/stage1/obj/unreachable_with_return.zig rename to test/cases/compile_errors/unreachable_with_return.zig index 6410d24913..cc6439ca0f 100644 --- a/test/cases/compile_errors/stage1/obj/unreachable_with_return.zig +++ b/test/cases/compile_errors/unreachable_with_return.zig @@ -2,7 +2,7 @@ fn a() noreturn {return;} export fn entry() void { a(); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:18: error: expected type 'noreturn', found 'void' +// :1:18: error: expected type 'noreturn', found 'void' diff --git a/test/cases/compile_errors/stage1/obj/use_invalid_number_literal_as_array_index.zig b/test/cases/compile_errors/use_invalid_number_literal_as_array_index.zig similarity index 55% rename from test/cases/compile_errors/stage1/obj/use_invalid_number_literal_as_array_index.zig rename to test/cases/compile_errors/use_invalid_number_literal_as_array_index.zig index b1d0aaa6f4..6fab70abf8 100644 --- a/test/cases/compile_errors/stage1/obj/use_invalid_number_literal_as_array_index.zig +++ b/test/cases/compile_errors/use_invalid_number_literal_as_array_index.zig @@ -5,7 +5,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:1: error: unable to infer variable type +// :1:1: error: variable of type 'comptime_int' must be const or comptime diff --git a/test/cases/compile_errors/use_of_undeclared_identifier.zig b/test/cases/compile_errors/use_of_undeclared_identifier.zig new file mode 100644 index 0000000000..2c90a7c718 --- /dev/null +++ b/test/cases/compile_errors/use_of_undeclared_identifier.zig @@ -0,0 +1,9 @@ +export fn f() void { + b = 3; +} + +// error +// backend=stage2 +// target=native +// +// :2:5: error: use of undeclared identifier 'b' diff --git a/test/cases/compile_errors/stage1/obj/using_invalid_types_in_function_call_raises_an_error.zig b/test/cases/compile_errors/using_invalid_types_in_function_call_raises_an_error.zig similarity index 66% rename from test/cases/compile_errors/stage1/obj/using_invalid_types_in_function_call_raises_an_error.zig rename to test/cases/compile_errors/using_invalid_types_in_function_call_raises_an_error.zig index f1fda15ea7..ee6d1b8b7c 100644 --- a/test/cases/compile_errors/stage1/obj/using_invalid_types_in_function_call_raises_an_error.zig +++ b/test/cases/compile_errors/using_invalid_types_in_function_call_raises_an_error.zig @@ -5,7 +5,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:20: error: enum declarations must have at least one tag +// :1:20: error: enum declarations must have at least one tag diff --git a/test/cases/compile_errors/usingnamespace_with_wrong_type.zig b/test/cases/compile_errors/usingnamespace_with_wrong_type.zig new file mode 100644 index 0000000000..4ec4bf4838 --- /dev/null +++ b/test/cases/compile_errors/usingnamespace_with_wrong_type.zig @@ -0,0 +1,7 @@ +usingnamespace void; + +// error +// backend=stage2 +// target=native +// +// :1:1: error: type void has no namespace diff --git a/test/cases/compile_errors/variable_has_wrong_type.zig b/test/cases/compile_errors/variable_has_wrong_type.zig new file mode 100644 index 0000000000..ec21c610e0 --- /dev/null +++ b/test/cases/compile_errors/variable_has_wrong_type.zig @@ -0,0 +1,10 @@ +export fn f() i32 { + const a = "a"; + return a; +} + +// error +// backend=stage2 +// target=native +// +// :3:12: error: expected type 'i32', found '*const [1:0]u8' diff --git a/test/cases/compile_errors/stage1/obj/variable_initialization_compile_error_then_referenced.zig b/test/cases/compile_errors/variable_initialization_compile_error_then_referenced.zig similarity index 74% rename from test/cases/compile_errors/stage1/obj/variable_initialization_compile_error_then_referenced.zig rename to test/cases/compile_errors/variable_initialization_compile_error_then_referenced.zig index 91b70481a1..774cd165d6 100644 --- a/test/cases/compile_errors/stage1/obj/variable_initialization_compile_error_then_referenced.zig +++ b/test/cases/compile_errors/variable_initialization_compile_error_then_referenced.zig @@ -13,7 +13,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:12: error: use of undeclared identifier 'T' +// :2:12: error: use of undeclared identifier 'T' diff --git a/test/cases/compile_errors/variable_with_type_noreturn.zig b/test/cases/compile_errors/variable_with_type_noreturn.zig new file mode 100644 index 0000000000..837a6e6965 --- /dev/null +++ b/test/cases/compile_errors/variable_with_type_noreturn.zig @@ -0,0 +1,11 @@ +export fn entry9() void { + var z: noreturn = return; + _ = z; +} + +// error +// backend=stage2 +// target=native +// +// :2:5: error: unreachable code +// :2:23: note: control flow is diverted here diff --git a/test/cases/compile_errors/volatile_on_global_assembly.zig b/test/cases/compile_errors/volatile_on_global_assembly.zig new file mode 100644 index 0000000000..84cde00113 --- /dev/null +++ b/test/cases/compile_errors/volatile_on_global_assembly.zig @@ -0,0 +1,9 @@ +comptime { + asm volatile (""); +} + +// error +// backend=stage2 +// target=native +// +// :2:9: error: volatile is meaningless on global assembly diff --git a/test/cases/compile_errors/stage1/obj/while_expected_bool_got_error_union.zig b/test/cases/compile_errors/while_expected_bool_got_error_union.zig similarity index 56% rename from test/cases/compile_errors/stage1/obj/while_expected_bool_got_error_union.zig rename to test/cases/compile_errors/while_expected_bool_got_error_union.zig index 9fc327e6c9..ecf3dd0736 100644 --- a/test/cases/compile_errors/stage1/obj/while_expected_bool_got_error_union.zig +++ b/test/cases/compile_errors/while_expected_bool_got_error_union.zig @@ -4,7 +4,7 @@ export fn foo() void { fn bar() anyerror!i32 { return 1; } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:15: error: expected type 'bool', found 'anyerror!i32' +// :2:15: error: expected type 'bool', found 'anyerror!i32' diff --git a/test/cases/compile_errors/stage1/obj/while_expected_bool_got_optional.zig b/test/cases/compile_errors/while_expected_bool_got_optional.zig similarity index 57% rename from test/cases/compile_errors/stage1/obj/while_expected_bool_got_optional.zig rename to test/cases/compile_errors/while_expected_bool_got_optional.zig index 5948d62786..3db4b7ae74 100644 --- a/test/cases/compile_errors/stage1/obj/while_expected_bool_got_optional.zig +++ b/test/cases/compile_errors/while_expected_bool_got_optional.zig @@ -4,7 +4,7 @@ export fn foo() void { fn bar() ?i32 { return 1; } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:15: error: expected type 'bool', found '?i32' +// :2:15: error: expected type 'bool', found '?i32' diff --git a/test/cases/compile_errors/stage1/obj/write_to_const_global_variable.zig b/test/cases/compile_errors/write_to_const_global_variable.zig similarity index 61% rename from test/cases/compile_errors/stage1/obj/write_to_const_global_variable.zig rename to test/cases/compile_errors/write_to_const_global_variable.zig index 4ce93e6b51..eaa9353f00 100644 --- a/test/cases/compile_errors/stage1/obj/write_to_const_global_variable.zig +++ b/test/cases/compile_errors/write_to_const_global_variable.zig @@ -5,7 +5,7 @@ fn f() void { export fn entry() void { f(); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:9: error: cannot assign to constant +// :3:9: error: cannot assign to constant diff --git a/test/cases/compile_errors/stage1/obj/wrong_size_to_an_array_literal.zig b/test/cases/compile_errors/wrong_size_to_an_array_literal.zig similarity index 55% rename from test/cases/compile_errors/stage1/obj/wrong_size_to_an_array_literal.zig rename to test/cases/compile_errors/wrong_size_to_an_array_literal.zig index 909d894587..bb8b3c215c 100644 --- a/test/cases/compile_errors/stage1/obj/wrong_size_to_an_array_literal.zig +++ b/test/cases/compile_errors/wrong_size_to_an_array_literal.zig @@ -4,7 +4,7 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:31: error: index 2 outside array of size 2 +// :2:31: error: index 2 outside array of length 2 diff --git a/test/cases/compile_errors/stage1/obj/wrong_type_passed_to_panic.zig b/test/cases/compile_errors/wrong_type_passed_to_panic.zig similarity index 51% rename from test/cases/compile_errors/stage1/obj/wrong_type_passed_to_panic.zig rename to test/cases/compile_errors/wrong_type_passed_to_panic.zig index ed237f81df..d6f142f24a 100644 --- a/test/cases/compile_errors/stage1/obj/wrong_type_passed_to_panic.zig +++ b/test/cases/compile_errors/wrong_type_passed_to_panic.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:12: error: expected type '[]const u8', found 'error{Foo}' +// :3:12: error: expected type '[]const u8', found 'error{Foo}' diff --git a/test/cases/compile_errors/stage1/obj/wrong_type_to_hasField.zig b/test/cases/compile_errors/wrong_type_to_hasField.zig similarity index 52% rename from test/cases/compile_errors/stage1/obj/wrong_type_to_hasField.zig rename to test/cases/compile_errors/wrong_type_to_hasField.zig index 680c19a6fb..5c05689413 100644 --- a/test/cases/compile_errors/stage1/obj/wrong_type_to_hasField.zig +++ b/test/cases/compile_errors/wrong_type_to_hasField.zig @@ -3,7 +3,7 @@ export fn entry() bool { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:22: error: type 'i32' does not support @hasField +// :2:22: error: type 'i32' does not support '@hasField' diff --git a/test/cases/compile_log.0.zig b/test/cases/compile_log.0.zig index c213ebb930..161a6f37ca 100644 --- a/test/cases/compile_log.0.zig +++ b/test/cases/compile_log.0.zig @@ -14,4 +14,4 @@ fn x() void {} // error // -// :6:23: error: expected usize, found bool +// :6:23: error: expected type 'usize', found 'bool' diff --git a/test/cases/x86_64-linux/hello_world_with_updates.1.zig b/test/cases/x86_64-linux/hello_world_with_updates.1.zig index 0f347b7f50..9be388ab63 100644 --- a/test/cases/x86_64-linux/hello_world_with_updates.1.zig +++ b/test/cases/x86_64-linux/hello_world_with_updates.1.zig @@ -2,4 +2,4 @@ pub export fn _start() noreturn {} // error // -// :1:34: error: expected noreturn, found void +// :1:34: error: expected type 'noreturn', found 'void' diff --git a/test/cases/x86_64-linux/inline_assembly.0.zig b/test/cases/x86_64-linux/inline_assembly.0.zig index e3c0f3badb..8356e05d80 100644 --- a/test/cases/x86_64-linux/inline_assembly.0.zig +++ b/test/cases/x86_64-linux/inline_assembly.0.zig @@ -13,4 +13,4 @@ pub fn main() void { // output_mode=Exe // target=x86_64-linux // -// :4:27: error: expected type, found comptime_int +// :4:27: error: expected type 'type', found 'comptime_int' diff --git a/test/cases/x86_64-macos/hello_world_with_updates.1.zig b/test/cases/x86_64-macos/hello_world_with_updates.1.zig index 909fc9ccfb..435747c2ee 100644 --- a/test/cases/x86_64-macos/hello_world_with_updates.1.zig +++ b/test/cases/x86_64-macos/hello_world_with_updates.1.zig @@ -2,4 +2,4 @@ pub export fn main() noreturn {} // error // -// :1:32: error: expected noreturn, found void +// :1:32: error: expected type 'noreturn', found 'void' diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 47ed7a53be..f602c49885 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -96,13 +96,13 @@ pub fn addCases(ctx: *TestContext) !void { \\ _ = @intToError(0); \\ return 0; \\} - , &.{":2:21: error: integer value 0 represents no error"}); + , &.{":2:21: error: integer value '0' represents no error"}); case.addError( \\pub export fn main() c_int { \\ _ = @intToError(3); \\ return 0; \\} - , &.{":2:21: error: integer value 3 represents no error"}); + , &.{":2:21: error: integer value '3' represents no error"}); } { @@ -739,7 +739,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ _ = @enumToInt(a); \\} , &.{ - ":3:20: error: expected enum or tagged union, found bool", + ":3:20: error: expected enum or tagged union, found 'bool'", }); case.addError( @@ -748,7 +748,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ _ = @intToEnum(bool, a); \\} , &.{ - ":3:20: error: expected enum, found bool", + ":3:20: error: expected enum, found 'bool'", }); case.addError( @@ -757,7 +757,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ _ = @intToEnum(E, 3); \\} , &.{ - ":3:9: error: enum 'tmp.E' has no tag with value 3", + ":3:9: error: enum 'tmp.E' has no tag with value '3'", ":1:11: note: enum declared here", }); From 03b356e34af37d74cf92087c0303a1b2c74d3afc Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 28 Jun 2022 16:43:09 +0300 Subject: [PATCH 2006/2031] Sema: improve `@call` errors --- src/Sema.zig | 36 +++++++++++++++---- ...n_of_non-tagged_union_and_enum_literal.zig | 13 +++++++ .../compile_errors/bad_usage_of_call.zig | 35 ++++++++++++++++++ .../alignCast_of_zero_sized_types.zig | 1 - .../stage1/obj/bad_usage_of_call.zig | 30 ---------------- ...rToInt_with_pointer_to_zero-sized_type.zig | 1 - ...ts_non_comptime-known_fn-always_inline.zig | 11 ------ ...cts_non_comptime-known_fn-compile_time.zig | 11 ------ ...n_of_non-tagged_union_and_enum_literal.zig | 14 -------- 9 files changed, 77 insertions(+), 75 deletions(-) create mode 100644 test/cases/comparison_of_non-tagged_union_and_enum_literal.zig create mode 100644 test/cases/compile_errors/bad_usage_of_call.zig rename test/cases/compile_errors/stage1/{test => }/alignCast_of_zero_sized_types.zig (98%) delete mode 100644 test/cases/compile_errors/stage1/obj/bad_usage_of_call.zig rename test/cases/compile_errors/stage1/{test => }/ptrToInt_with_pointer_to_zero-sized_type.zig (94%) delete mode 100644 test/cases/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-always_inline.zig delete mode 100644 test/cases/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-compile_time.zig delete mode 100644 test/cases/compile_errors/stage1/test/comparison_of_non-tagged_union_and_enum_literal.zig diff --git a/src/Sema.zig b/src/Sema.zig index 6f516229a0..dcfbf52687 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5231,6 +5231,10 @@ fn analyzeCall( .async_kw => return sema.fail(block, call_src, "TODO implement async call", .{}), }; + if (modifier == .never_inline and func_ty_info.cc == .Inline) { + return sema.fail(block, call_src, "no-inline call of inline function", .{}); + } + const gpa = sema.gpa; var is_generic_call = func_ty_info.is_generic; @@ -5270,6 +5274,10 @@ fn analyzeCall( } } + if (is_comptime_call and modifier == .never_inline) { + return sema.fail(block, call_src, "unable to perform 'never_inline' call at compile-time", .{}); + } + const result: Air.Inst.Ref = if (is_inline_call) res: { const func_val = try sema.resolveConstValue(block, func_src, func); const module_fn = switch (func_val.tag()) { @@ -11612,8 +11620,13 @@ fn analyzeCmpUnionTag( ) CompileError!Air.Inst.Ref { const union_ty = try sema.resolveTypeFields(block, un_src, sema.typeOf(un)); const union_tag_ty = union_ty.unionTagType() orelse { - // TODO note at declaration site that says "union foo is not tagged" - return sema.fail(block, un_src, "comparison of union and enum literal is only valid for tagged union types", .{}); + const msg = msg: { + const msg = try sema.errMsg(block, un_src, "comparison of union and enum literal is only valid for tagged union types", .{}); + errdefer msg.destroy(sema.gpa); + try sema.mod.errNoteNonLazy(union_ty.declSrcLoc(sema.mod), msg, "union '{}' is not a tagged union", .{union_ty.fmt(sema.mod)}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); }; // Coerce both the union and the tag to the union's tag type, and then execute the // enum comparison codepath. @@ -16878,10 +16891,15 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError break :modifier modifier_val.toEnum(std.builtin.CallOptions.Modifier); }; + const is_comptime = extra.flags.is_comptime or block.is_comptime; + const modifier: std.builtin.CallOptions.Modifier = switch (wanted_modifier) { // These can be upgraded to comptime or nosuspend calls. .auto, .never_tail, .no_async => m: { - if (extra.flags.is_comptime) { + if (is_comptime) { + if (wanted_modifier == .never_tail) { + return sema.fail(block, options_src, "unable to perform 'never_tail' call at compile-time", .{}); + } break :m .compile_time; } if (extra.flags.is_nosuspend) { @@ -16891,7 +16909,11 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError }, // These can be upgraded to comptime. nosuspend bit can be safely ignored. .always_tail, .always_inline, .compile_time => m: { - if (extra.flags.is_comptime) { + _ = (try sema.resolveDefinedValue(block, func_src, func)) orelse { + return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(wanted_modifier)}); + }; + + if (is_comptime) { break :m .compile_time; } break :m wanted_modifier; @@ -16900,14 +16922,14 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError if (extra.flags.is_nosuspend) { return sema.fail(block, options_src, "modifier 'async_kw' cannot be used inside nosuspend block", .{}); } - if (extra.flags.is_comptime) { + if (is_comptime) { return sema.fail(block, options_src, "modifier 'async_kw' cannot be used in combination with comptime function call", .{}); } break :m wanted_modifier; }, .never_inline => m: { - if (extra.flags.is_comptime) { - return sema.fail(block, options_src, "modifier 'never_inline' cannot be used in combination with comptime function call", .{}); + if (is_comptime) { + return sema.fail(block, options_src, "unable to perform 'never_inline' call at compile-time", .{}); } break :m wanted_modifier; }, diff --git a/test/cases/comparison_of_non-tagged_union_and_enum_literal.zig b/test/cases/comparison_of_non-tagged_union_and_enum_literal.zig new file mode 100644 index 0000000000..621b8ead25 --- /dev/null +++ b/test/cases/comparison_of_non-tagged_union_and_enum_literal.zig @@ -0,0 +1,13 @@ +export fn entry() void { + const U = union { A: u32, B: u64 }; + var u = U{ .A = 42 }; + var ok = u == .A; + _ = ok; +} + +// error +// backend=stage2 +// target=native +// +// :4:14: error: comparison of union and enum literal is only valid for tagged union types +// :2:15: note: union 'tmp.entry.U' is not a tagged union diff --git a/test/cases/compile_errors/bad_usage_of_call.zig b/test/cases/compile_errors/bad_usage_of_call.zig new file mode 100644 index 0000000000..002143b0b8 --- /dev/null +++ b/test/cases/compile_errors/bad_usage_of_call.zig @@ -0,0 +1,35 @@ +export fn entry1() void { + @call(.{}, foo, {}); +} +export fn entry2() void { + comptime @call(.{ .modifier = .never_inline }, foo, .{}); +} +export fn entry3() void { + comptime @call(.{ .modifier = .never_tail }, foo, .{}); +} +export fn entry4() void { + @call(.{ .modifier = .never_inline }, bar, .{}); +} +export fn entry5(c: bool) void { + var baz = if (c) &baz1 else &baz2; + @call(.{ .modifier = .compile_time }, baz, .{}); +} +pub export fn entry() void { + var call_me: *const fn () void = undefined; + @call(.{ .modifier = .always_inline }, call_me, .{}); +} +fn foo() void {} +fn bar() callconv(.Inline) void {} +fn baz1() void {} +fn baz2() void {} + +// error +// backend=stage2 +// target=native +// +// :2:21: error: expected a tuple, found 'void' +// :5:21: error: unable to perform 'never_inline' call at compile-time +// :8:21: error: unable to perform 'never_tail' call at compile-time +// :11:5: error: no-inline call of inline function +// :15:43: error: modifier 'compile_time' requires a comptime-known function +// :19:44: error: modifier 'always_inline' requires a comptime-known function diff --git a/test/cases/compile_errors/stage1/test/alignCast_of_zero_sized_types.zig b/test/cases/compile_errors/stage1/alignCast_of_zero_sized_types.zig similarity index 98% rename from test/cases/compile_errors/stage1/test/alignCast_of_zero_sized_types.zig rename to test/cases/compile_errors/stage1/alignCast_of_zero_sized_types.zig index f83d22759c..277301a06d 100644 --- a/test/cases/compile_errors/stage1/test/alignCast_of_zero_sized_types.zig +++ b/test/cases/compile_errors/stage1/alignCast_of_zero_sized_types.zig @@ -20,7 +20,6 @@ export fn qux() void { // error // backend=stage1 // target=native -// is_test=1 // // tmp.zig:3:23: error: cannot adjust alignment of zero sized type '*void' // tmp.zig:7:23: error: cannot adjust alignment of zero sized type '?*void' diff --git a/test/cases/compile_errors/stage1/obj/bad_usage_of_call.zig b/test/cases/compile_errors/stage1/obj/bad_usage_of_call.zig deleted file mode 100644 index c77ccf1a52..0000000000 --- a/test/cases/compile_errors/stage1/obj/bad_usage_of_call.zig +++ /dev/null @@ -1,30 +0,0 @@ -export fn entry1() void { - @call(.{}, foo, {}); -} -export fn entry2() void { - comptime @call(.{ .modifier = .never_inline }, foo, .{}); -} -export fn entry3() void { - comptime @call(.{ .modifier = .never_tail }, foo, .{}); -} -export fn entry4() void { - @call(.{ .modifier = .never_inline }, bar, .{}); -} -export fn entry5(c: bool) void { - var baz = if (c) baz1 else baz2; - @call(.{ .modifier = .compile_time }, baz, .{}); -} -fn foo() void {} -fn bar() callconv(.Inline) void {} -fn baz1() void {} -fn baz2() void {} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:21: error: expected tuple or struct, found 'void' -// tmp.zig:5:14: error: unable to perform 'never_inline' call at compile-time -// tmp.zig:8:14: error: unable to perform 'never_tail' call at compile-time -// tmp.zig:11:5: error: no-inline call of inline function -// tmp.zig:15:5: error: the specified modifier requires a comptime-known function diff --git a/test/cases/compile_errors/stage1/test/ptrToInt_with_pointer_to_zero-sized_type.zig b/test/cases/compile_errors/stage1/ptrToInt_with_pointer_to_zero-sized_type.zig similarity index 94% rename from test/cases/compile_errors/stage1/test/ptrToInt_with_pointer_to_zero-sized_type.zig rename to test/cases/compile_errors/stage1/ptrToInt_with_pointer_to_zero-sized_type.zig index 7c2ba184c4..143dc9135d 100644 --- a/test/cases/compile_errors/stage1/test/ptrToInt_with_pointer_to_zero-sized_type.zig +++ b/test/cases/compile_errors/stage1/ptrToInt_with_pointer_to_zero-sized_type.zig @@ -7,6 +7,5 @@ export fn entry() void { // error // backend=stage1 // target=native -// is_test=1 // // tmp.zig:3:23: error: pointer to size 0 type has no address diff --git a/test/cases/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-always_inline.zig b/test/cases/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-always_inline.zig deleted file mode 100644 index a010414a57..0000000000 --- a/test/cases/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-always_inline.zig +++ /dev/null @@ -1,11 +0,0 @@ -pub export fn entry() void { - var call_me: fn () void = undefined; - @call(.{ .modifier = .always_inline }, call_me, .{}); -} - -// error -// backend=stage1 -// target=native -// is_test=1 -// -// tmp.zig:3:5: error: the specified modifier requires a comptime-known function diff --git a/test/cases/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-compile_time.zig b/test/cases/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-compile_time.zig deleted file mode 100644 index b3ffcc6ec2..0000000000 --- a/test/cases/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-compile_time.zig +++ /dev/null @@ -1,11 +0,0 @@ -pub export fn entry() void { - var call_me: fn () void = undefined; - @call(.{ .modifier = .compile_time }, call_me, .{}); -} - -// error -// backend=stage1 -// target=native -// is_test=1 -// -// tmp.zig:3:5: error: the specified modifier requires a comptime-known function diff --git a/test/cases/compile_errors/stage1/test/comparison_of_non-tagged_union_and_enum_literal.zig b/test/cases/compile_errors/stage1/test/comparison_of_non-tagged_union_and_enum_literal.zig deleted file mode 100644 index 4898ac744c..0000000000 --- a/test/cases/compile_errors/stage1/test/comparison_of_non-tagged_union_and_enum_literal.zig +++ /dev/null @@ -1,14 +0,0 @@ -export fn entry() void { - const U = union { A: u32, B: u64 }; - var u = U{ .A = 42 }; - var ok = u == .A; - _ = ok; -} - -// error -// backend=stage1 -// target=native -// is_test=1 -// -// tmp.zig:4:16: error: comparison of union and enum literal is only valid for tagged union types -// tmp.zig:2:15: note: type U is not a tagged union From 2e7dc5e15192431c64eca458ecfdce3d07b89f69 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 28 Jun 2022 17:13:43 +0300 Subject: [PATCH 2007/2031] Sema: improve vector overflow errors --- src/Sema.zig | 52 +++++++++++++------ ...mptime_vector_overflow_shows_the_index.zig | 7 ++- ...elected_index_past_first_vector_length.zig | 9 ++-- 3 files changed, 42 insertions(+), 26 deletions(-) rename test/cases/compile_errors/{stage1/test => }/comptime_vector_overflow_shows_the_index.zig (55%) rename test/cases/compile_errors/{stage1/test => }/shuffle_with_selected_index_past_first_vector_length.zig (51%) diff --git a/src/Sema.zig b/src/Sema.zig index dcfbf52687..6f2cad3529 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -139,6 +139,7 @@ pub const Block = struct { is_comptime: bool, is_typeof: bool = false, + is_coerce_result_ptr: bool = false, /// when null, it is determined by build mode, changed by @setRuntimeSafety want_safety: ?bool = null, @@ -1734,9 +1735,20 @@ fn failWithErrorSetCodeMissing( }); } -fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value) CompileError { +fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value, vector_index: usize) CompileError { + if (int_ty.zigTypeTag() == .Vector) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "overflow of vector type '{}' with value '{}'", .{ + int_ty.fmt(sema.mod), val.fmtValue(int_ty, sema.mod), + }); + errdefer msg.destroy(sema.gpa); + try sema.errNote(block, src, msg, "when computing vector element at index '{d}'", .{vector_index}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } return sema.fail(block, src, "overflow of integer type '{}' with value '{}'", .{ - int_ty.fmt(sema.mod), val.fmtValue(Type.@"comptime_int", sema.mod), + int_ty.fmt(sema.mod), val.fmtValue(int_ty, sema.mod), }); } @@ -1971,6 +1983,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE // kind of transformations to make on the result pointer. var trash_block = block.makeSubBlock(); trash_block.is_comptime = false; + trash_block.is_coerce_result_ptr = true; defer trash_block.instructions.deinit(sema.gpa); const dummy_ptr = try trash_block.addTy(.alloc, sema.typeOf(ptr)); @@ -10453,8 +10466,9 @@ fn analyzeArithmetic( if (maybe_rhs_val) |rhs_val| { if (is_int) { const sum = try sema.intAdd(block, src, lhs_val, rhs_val, resolved_type); - if (!(try sema.intFitsInType(block, src, sum, resolved_type))) { - return sema.failWithIntegerOverflow(block, src, resolved_type, sum); + var vector_index: usize = undefined; + if (!(try sema.intFitsInType(block, src, sum, resolved_type, &vector_index))) { + return sema.failWithIntegerOverflow(block, src, resolved_type, sum, vector_index); } return sema.addConstant(resolved_type, sum); } else { @@ -10547,8 +10561,9 @@ fn analyzeArithmetic( if (maybe_rhs_val) |rhs_val| { if (is_int) { const diff = try sema.intSub(block, src, lhs_val, rhs_val, resolved_type); - if (!(try sema.intFitsInType(block, src, diff, resolved_type))) { - return sema.failWithIntegerOverflow(block, src, resolved_type, diff); + var vector_index: usize = undefined; + if (!(try sema.intFitsInType(block, src, diff, resolved_type, &vector_index))) { + return sema.failWithIntegerOverflow(block, src, resolved_type, diff, vector_index); } return sema.addConstant(resolved_type, diff); } else { @@ -10921,8 +10936,9 @@ fn analyzeArithmetic( } if (is_int) { const product = try lhs_val.intMul(rhs_val, resolved_type, sema.arena, target); - if (!(try sema.intFitsInType(block, src, product, resolved_type))) { - return sema.failWithIntegerOverflow(block, src, resolved_type, product); + var vector_index: usize = undefined; + if (!(try sema.intFitsInType(block, src, product, resolved_type, &vector_index))) { + return sema.failWithIntegerOverflow(block, src, resolved_type, product, vector_index); } return sema.addConstant(resolved_type, product); } else { @@ -16456,15 +16472,15 @@ fn analyzeShuffle( } if (unsigned >= operand_info[chosen][0]) { const msg = msg: { - const msg = try sema.errMsg(block, mask_src, "mask index {d} has out-of-bounds selection", .{i}); + const msg = try sema.errMsg(block, mask_src, "mask index '{d}' has out-of-bounds selection", .{i}); errdefer msg.destroy(sema.gpa); - try sema.errNote(block, operand_info[chosen][1], msg, "selected index {d} out of bounds of '{}'", .{ + try sema.errNote(block, operand_info[chosen][1], msg, "selected index '{d}' out of bounds of '{}'", .{ unsigned, operand_info[chosen][2].fmt(sema.mod), }); - if (chosen == 1) { + if (chosen == 0) { try sema.errNote(block, b_src, msg, "selections from the second vector are specified with negative numbers", .{}); } @@ -17750,7 +17766,7 @@ fn zirBuiltinExtern( } fn requireFunctionBlock(sema: *Sema, block: *Block, src: LazySrcLoc) !void { - if (sema.func == null and !block.is_typeof) { + if (sema.func == null and !block.is_typeof and !block.is_coerce_result_ptr) { return sema.fail(block, src, "instruction illegal outside function body", .{}); } } @@ -19881,7 +19897,7 @@ fn coerce( .Int, .ComptimeInt => { if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| { // comptime known integer to other number - if (!(try sema.intFitsInType(block, inst_src, val, dest_ty))) { + if (!(try sema.intFitsInType(block, inst_src, val, dest_ty, null))) { return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) }); } return try sema.addConstant(dest_ty, val); @@ -25829,7 +25845,7 @@ fn floatToIntScalar( else try Value.Tag.int_big_positive.create(sema.arena, result_limbs); - if (!(try sema.intFitsInType(block, src, result, int_ty))) { + if (!(try sema.intFitsInType(block, src, result, int_ty, null))) { return sema.fail(block, src, "float value '{}' cannot be stored in integer type '{}'", .{ val.fmtValue(float_ty, sema.mod), int_ty.fmt(sema.mod), }); @@ -25845,6 +25861,7 @@ fn intFitsInType( src: LazySrcLoc, self: Value, ty: Type, + vector_index: ?*usize, ) CompileError!bool { const target = sema.mod.getTarget(); switch (self.tag()) { @@ -25954,8 +25971,9 @@ fn intFitsInType( .aggregate => { assert(ty.zigTypeTag() == .Vector); - for (self.castTag(.aggregate).?.data) |elem| { - if (!(try sema.intFitsInType(block, src, elem, ty.scalarType()))) { + for (self.castTag(.aggregate).?.data) |elem, i| { + if (!(try sema.intFitsInType(block, src, elem, ty.scalarType(), null))) { + if (vector_index) |some| some.* = i; return false; } } @@ -25993,7 +26011,7 @@ fn enumHasInt( int: Value, ) CompileError!bool { switch (ty.tag()) { - .enum_nonexhaustive => return sema.intFitsInType(block, src, int, ty), + .enum_nonexhaustive => return sema.intFitsInType(block, src, int, ty, null), .enum_full => { const enum_full = ty.castTag(.enum_full).?.data; const tag_ty = enum_full.tag_ty; diff --git a/test/cases/compile_errors/stage1/test/comptime_vector_overflow_shows_the_index.zig b/test/cases/compile_errors/comptime_vector_overflow_shows_the_index.zig similarity index 55% rename from test/cases/compile_errors/stage1/test/comptime_vector_overflow_shows_the_index.zig rename to test/cases/compile_errors/comptime_vector_overflow_shows_the_index.zig index a1f026ab70..678d99376e 100644 --- a/test/cases/compile_errors/stage1/test/comptime_vector_overflow_shows_the_index.zig +++ b/test/cases/compile_errors/comptime_vector_overflow_shows_the_index.zig @@ -6,9 +6,8 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:4:15: error: operation caused overflow -// tmp.zig:4:15: note: when computing vector element at index 2 +// :4:15: error: overflow of vector type '@Vector(4, u8)' with value '.{ 6, 8, 256, 12 }' +// :4:15: note: when computing vector element at index '2' diff --git a/test/cases/compile_errors/stage1/test/shuffle_with_selected_index_past_first_vector_length.zig b/test/cases/compile_errors/shuffle_with_selected_index_past_first_vector_length.zig similarity index 51% rename from test/cases/compile_errors/stage1/test/shuffle_with_selected_index_past_first_vector_length.zig rename to test/cases/compile_errors/shuffle_with_selected_index_past_first_vector_length.zig index 8b0c5ba780..1c602f7ab0 100644 --- a/test/cases/compile_errors/stage1/test/shuffle_with_selected_index_past_first_vector_length.zig +++ b/test/cases/compile_errors/shuffle_with_selected_index_past_first_vector_length.zig @@ -6,10 +6,9 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:4:39: error: mask index '4' has out-of-bounds selection -// tmp.zig:4:27: note: selected index '7' out of bounds of @Vector(4, u32) -// tmp.zig:4:30: note: selections from the second vector are specified with negative numbers +// :4:39: error: mask index '4' has out-of-bounds selection +// :4:27: note: selected index '7' out of bounds of '@Vector(4, u32)' +// :4:30: note: selections from the second vector are specified with negative numbers From 6cadac18b8fb1775815fbb3f16f503c5de0e89d0 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 28 Jun 2022 17:15:10 +0300 Subject: [PATCH 2008/2031] Sema: improve auto generated union enum name --- src/Module.zig | 5 ++ src/Sema.zig | 55 +++++++++++++++---- .../{stage1/test => }/not_an_enum_type.zig | 5 +- 3 files changed, 50 insertions(+), 15 deletions(-) rename test/cases/compile_errors/{stage1/test => }/not_an_enum_type.zig (75%) diff --git a/src/Module.zig b/src/Module.zig index bdf206490d..1f70a44df5 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -503,6 +503,8 @@ pub const Decl = struct { alive: bool, /// Whether the Decl is a `usingnamespace` declaration. is_usingnamespace: bool, + /// If true `name` is already fully qualified. + name_fully_qualified: bool = false, /// Represents the position of the code in the output file. /// This is populated regardless of semantic analysis and code generation. @@ -686,6 +688,9 @@ pub const Decl = struct { pub fn renderFullyQualifiedName(decl: Decl, mod: *Module, writer: anytype) !void { const unqualified_name = mem.sliceTo(decl.name, 0); + if (decl.name_fully_qualified) { + return writer.writeAll(unqualified_name); + } return decl.src_namespace.renderFullyQualifiedName(mod, unqualified_name, writer); } diff --git a/src/Sema.zig b/src/Sema.zig index 6f2cad3529..3015ac5d8e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14883,7 +14883,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I union_obj.tag_ty = if (tag_type_val.optionalValue()) |payload_val| blk: { var buffer: Value.ToTypeBuffer = undefined; break :blk try payload_val.toType(&buffer).copy(new_decl_arena_allocator); - } else try sema.generateUnionTagTypeSimple(block, fields_len); + } else try sema.generateUnionTagTypeSimple(block, fields_len, null); // Fields if (fields_len > 0) { @@ -24237,7 +24237,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil if (small.auto_enum_tag) { // The provided type is an integer type and we must construct the enum tag type here. int_tag_ty = provided_ty; - union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, fields_len, provided_ty); + union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, fields_len, provided_ty, union_obj); const enum_obj = union_obj.tag_ty.castTag(.enum_numbered).?.data; enum_field_names = &enum_obj.fields; enum_value_map = &enum_obj.values; @@ -24253,7 +24253,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis // purposes, we still auto-generate an enum tag type the same way. That the union is // untagged is represented by the Type tag (union vs union_tagged). - union_obj.tag_ty = try sema.generateUnionTagTypeSimple(&block_scope, fields_len); + union_obj.tag_ty = try sema.generateUnionTagTypeSimple(&block_scope, fields_len, union_obj); enum_field_names = &union_obj.tag_ty.castTag(.enum_simple).?.data.fields; } @@ -24422,6 +24422,7 @@ fn generateUnionTagTypeNumbered( block: *Block, fields_len: u32, int_ty: Type, + union_obj: *Module.Union, ) !Type { const mod = sema.mod; @@ -24437,13 +24438,24 @@ fn generateUnionTagTypeNumbered( }; const enum_ty = Type.initPayload(&enum_ty_payload.base); const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); - // TODO better type name - const new_decl_index = try mod.createAnonymousDecl(block, .{ + + const src_decl = mod.declPtr(block.src_decl); + const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope); + errdefer mod.destroyDecl(new_decl_index); + const name = name: { + const fqn = try union_obj.getFullyQualifiedName(mod); + defer sema.gpa.free(fqn); + break :name try std.fmt.allocPrintZ(mod.gpa, "@typeInfo({s}).Union.tag_type.?", .{fqn}); + }; + try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, block.namespace, .{ .ty = Type.type, .val = enum_val, - }); + }, name); + sema.mod.declPtr(new_decl_index).name_fully_qualified = true; + const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; + new_decl.name_fully_qualified = true; errdefer mod.abortAnonDecl(new_decl_index); enum_obj.* = .{ @@ -24463,7 +24475,7 @@ fn generateUnionTagTypeNumbered( return enum_ty; } -fn generateUnionTagTypeSimple(sema: *Sema, block: *Block, fields_len: usize) !Type { +fn generateUnionTagTypeSimple(sema: *Sema, block: *Block, fields_len: usize, maybe_union_obj: ?*Module.Union) !Type { const mod = sema.mod; var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); @@ -24478,11 +24490,30 @@ fn generateUnionTagTypeSimple(sema: *Sema, block: *Block, fields_len: usize) !Ty }; const enum_ty = Type.initPayload(&enum_ty_payload.base); const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); - // TODO better type name - const new_decl_index = try mod.createAnonymousDecl(block, .{ - .ty = Type.type, - .val = enum_val, - }); + + const new_decl_index = new_decl_index: { + const union_obj = maybe_union_obj orelse { + break :new_decl_index try mod.createAnonymousDecl(block, .{ + .ty = Type.type, + .val = enum_val, + }); + }; + const src_decl = mod.declPtr(block.src_decl); + const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope); + errdefer mod.destroyDecl(new_decl_index); + const name = name: { + const fqn = try union_obj.getFullyQualifiedName(mod); + defer sema.gpa.free(fqn); + break :name try std.fmt.allocPrintZ(mod.gpa, "@typeInfo({s}).Union.tag_type.?", .{fqn}); + }; + try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, block.namespace, .{ + .ty = Type.type, + .val = enum_val, + }, name); + sema.mod.declPtr(new_decl_index).name_fully_qualified = true; + break :new_decl_index new_decl_index; + }; + const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); diff --git a/test/cases/compile_errors/stage1/test/not_an_enum_type.zig b/test/cases/compile_errors/not_an_enum_type.zig similarity index 75% rename from test/cases/compile_errors/stage1/test/not_an_enum_type.zig rename to test/cases/compile_errors/not_an_enum_type.zig index 99c733b4d0..6868cf7dc0 100644 --- a/test/cases/compile_errors/stage1/test/not_an_enum_type.zig +++ b/test/cases/compile_errors/not_an_enum_type.zig @@ -13,8 +13,7 @@ const InvalidToken = struct {}; const ExpectedVarDeclOrFn = struct {}; // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:4:9: error: expected type '@typeInfo(Error).Union.tag_type.?', found 'type' +// :4:9: error: expected type '@typeInfo(tmp.Error).Union.tag_type.?', found 'type' From 979910dc38cd15e506218d5175e5a91f56244055 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 28 Jun 2022 17:37:41 +0300 Subject: [PATCH 2009/2031] Sema: validate shift amounts and switch ranges --- src/Sema.zig | 75 ++++++++++++++++--- ...ift_on_type_with_non-power-of-two_size.zig | 11 ++- .../switch_ranges_endpoints_are_validated.zig | 16 ---- .../switch_ranges_endpoints_are_validated.zig | 21 ++++++ 4 files changed, 89 insertions(+), 34 deletions(-) rename test/cases/compile_errors/{stage1/test => }/shift_on_type_with_non-power-of-two_size.zig (61%) delete mode 100644 test/cases/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig create mode 100644 test/cases/compile_errors/switch_ranges_endpoints_are_validated.zig diff --git a/src/Sema.zig b/src/Sema.zig index 3015ac5d8e..8b8d828f54 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9027,6 +9027,10 @@ fn validateSwitchRange( ) CompileError!void { const first_val = (try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first)).val; const last_val = (try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last)).val; + if (first_val.compare(.gt, last_val, operand_ty, sema.mod)) { + const src = switch_prong_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), src_node_offset, .first); + return sema.fail(block, src, "range start value is greater than the end value", .{}); + } const maybe_prev_src = try range_set.add(first_val, last_val, operand_ty, switch_prong_src); return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); } @@ -9374,9 +9378,34 @@ fn zirShl( if (rhs_val.isUndef()) { return sema.addConstUndef(sema.typeOf(lhs)); } + // If rhs is 0, return lhs without doing any calculations. if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { return lhs; } + if (scalar_ty.zigTypeTag() != .ComptimeInt and air_tag != .shl_sat) { + var bits_payload = Value.Payload.U64{ + .base = .{ .tag = .int_u64 }, + .data = scalar_ty.intInfo(target).bits, + }; + const bit_value = Value.initPayload(&bits_payload.base); + if (rhs_ty.zigTypeTag() == .Vector) { + var i: usize = 0; + while (i < rhs_ty.vectorLen()) : (i += 1) { + if (rhs_val.indexVectorlike(i).compareHetero(.gte, bit_value, target)) { + return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{ + rhs_val.indexVectorlike(i).fmtValue(scalar_ty, sema.mod), + i, + scalar_ty.fmt(sema.mod), + }); + } + } + } else if (rhs_val.compareHetero(.gte, bit_value, target)) { + return sema.fail(block, rhs_src, "shift amount '{}' is too large for operand type '{}'", .{ + rhs_val.fmtValue(scalar_ty, sema.mod), + scalar_ty.fmt(sema.mod), + }); + } + } } const runtime_src = if (maybe_lhs_val) |lhs_val| rs: { @@ -9488,15 +9517,43 @@ fn zirShr( const rhs_ty = sema.typeOf(rhs); try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); const target = sema.mod.getTarget(); + const scalar_ty = lhs_ty.scalarType(); const runtime_src = if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| rs: { - if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { - if (lhs_val.isUndef() or rhs_val.isUndef()) { - return sema.addConstUndef(lhs_ty); + if (rhs_val.isUndef()) { + return sema.addConstUndef(lhs_ty); + } + // If rhs is 0, return lhs without doing any calculations. + if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { + return lhs; + } + if (scalar_ty.zigTypeTag() != .ComptimeInt) { + var bits_payload = Value.Payload.U64{ + .base = .{ .tag = .int_u64 }, + .data = scalar_ty.intInfo(target).bits, + }; + const bit_value = Value.initPayload(&bits_payload.base); + if (rhs_ty.zigTypeTag() == .Vector) { + var i: usize = 0; + while (i < rhs_ty.vectorLen()) : (i += 1) { + if (rhs_val.indexVectorlike(i).compareHetero(.gte, bit_value, target)) { + return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{ + rhs_val.indexVectorlike(i).fmtValue(scalar_ty, sema.mod), + i, + scalar_ty.fmt(sema.mod), + }); + } + } + } else if (rhs_val.compareHetero(.gte, bit_value, target)) { + return sema.fail(block, rhs_src, "shift amount '{}' is too large for operand type '{}'", .{ + rhs_val.fmtValue(scalar_ty, sema.mod), + scalar_ty.fmt(sema.mod), + }); } - // If rhs is 0, return lhs without doing any calculations. - if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { - return sema.addConstant(lhs_ty, lhs_val); + } + if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { + if (lhs_val.isUndef()) { + return sema.addConstUndef(lhs_ty); } if (air_tag == .shr_exact) { // Detect if any ones would be shifted out. @@ -9508,12 +9565,6 @@ fn zirShr( const val = try lhs_val.shr(rhs_val, lhs_ty, sema.arena, target); return sema.addConstant(lhs_ty, val); } else { - // Even if lhs is not comptime known, we can still deduce certain things based - // on rhs. - // If rhs is 0, return lhs without doing any calculations. - if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) { - return lhs; - } break :rs lhs_src; } } else rhs_src; diff --git a/test/cases/compile_errors/stage1/test/shift_on_type_with_non-power-of-two_size.zig b/test/cases/compile_errors/shift_on_type_with_non-power-of-two_size.zig similarity index 61% rename from test/cases/compile_errors/stage1/test/shift_on_type_with_non-power-of-two_size.zig rename to test/cases/compile_errors/shift_on_type_with_non-power-of-two_size.zig index 222cb22563..9f606b0426 100644 --- a/test/cases/compile_errors/stage1/test/shift_on_type_with_non-power-of-two_size.zig +++ b/test/cases/compile_errors/shift_on_type_with_non-power-of-two_size.zig @@ -24,11 +24,10 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:5:19: error: RHS of shift is too large for LHS type -// tmp.zig:9:19: error: RHS of shift is too large for LHS type -// tmp.zig:13:17: error: RHS of shift is too large for LHS type -// tmp.zig:17:17: error: RHS of shift is too large for LHS type +// :5:22: error: shift amount '24' is too large for operand type 'u24' +// :9:22: error: shift amount '24' is too large for operand type 'u24' +// :13:30: error: shift amount '24' is too large for operand type 'u24' +// :17:30: error: shift amount '24' is too large for operand type 'u24' diff --git a/test/cases/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig b/test/cases/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig deleted file mode 100644 index a44d59feed..0000000000 --- a/test/cases/compile_errors/stage1/test/switch_ranges_endpoints_are_validated.zig +++ /dev/null @@ -1,16 +0,0 @@ -pub export fn entry() void { - var x: i32 = 0; - switch (x) { - 6...1 => {}, - -1...-5 => {}, - else => unreachable, - } -} - -// error -// backend=stage1 -// target=native -// is_test=1 -// -// tmp.zig:4:9: error: range start value is greater than the end value -// tmp.zig:5:9: error: range start value is greater than the end value diff --git a/test/cases/compile_errors/switch_ranges_endpoints_are_validated.zig b/test/cases/compile_errors/switch_ranges_endpoints_are_validated.zig new file mode 100644 index 0000000000..72c17929c0 --- /dev/null +++ b/test/cases/compile_errors/switch_ranges_endpoints_are_validated.zig @@ -0,0 +1,21 @@ +pub export fn entry1() void { + var x: i32 = 0; + switch (x) { + 6...1 => {}, + else => unreachable, + } +} +pub export fn entr2() void { + var x: i32 = 0; + switch (x) { + -1...-5 => {}, + else => unreachable, + } +} + +// error +// backend=stage2 +// target=native +// +// :4:9: error: range start value is greater than the end value +// :11:9: error: range start value is greater than the end value From cc3336c7841c48622db855be95a79bbd030bade8 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 28 Jun 2022 20:50:14 +0300 Subject: [PATCH 2010/2031] Sema: add source location to coerce result ptr, fix negation error --- src/AstGen.zig | 9 ++-- src/Sema.zig | 43 ++++++++++++++++--- src/Zir.zig | 4 +- src/print_zir.zig | 2 +- .../assign_inline_fn_to_non-comptime_var.zig | 12 ++++++ ...n-integer_non-float_or_non-vector_type.zig | 4 +- .../test => }/reassign_to_array_parameter.zig | 5 +-- .../reassign_to_struct_parameter.zig | 5 +-- .../assign_inline_fn_to_non-comptime_var.zig | 12 ------ 9 files changed, 62 insertions(+), 34 deletions(-) create mode 100644 test/cases/compile_errors/assign_inline_fn_to_non-comptime_var.zig rename test/cases/compile_errors/{stage1/obj => }/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig (63%) rename test/cases/compile_errors/{stage1/test => }/reassign_to_array_parameter.zig (63%) rename test/cases/compile_errors/{stage1/test => }/reassign_to_struct_parameter.zig (66%) delete mode 100644 test/cases/compile_errors/stage1/obj/assign_inline_fn_to_non-comptime_var.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index c4cba54bdc..c9abb1859b 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1476,7 +1476,7 @@ fn arrayInitExprRlPtr( return arrayInitExprRlPtrInner(gz, scope, node, base_ptr, elements); } - var as_scope = try gz.makeCoercionScope(scope, array_ty, result_ptr); + var as_scope = try gz.makeCoercionScope(scope, array_ty, result_ptr, node); defer as_scope.unstack(); const result = try arrayInitExprRlPtrInner(&as_scope, scope, node, as_scope.rl_ptr, elements); @@ -1697,7 +1697,7 @@ fn structInitExprRlPtr( const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); - var as_scope = try gz.makeCoercionScope(scope, ty_inst, result_ptr); + var as_scope = try gz.makeCoercionScope(scope, ty_inst, result_ptr, node); defer as_scope.unstack(); const result = try structInitExprRlPtrInner(&as_scope, scope, node, struct_init, as_scope.rl_ptr); @@ -7046,7 +7046,7 @@ fn asRlPtr( operand_node: Ast.Node.Index, dest_type: Zir.Inst.Ref, ) InnerError!Zir.Inst.Ref { - var as_scope = try parent_gz.makeCoercionScope(scope, dest_type, result_ptr); + var as_scope = try parent_gz.makeCoercionScope(scope, dest_type, result_ptr, src_node); defer as_scope.unstack(); const result = try reachableExpr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node, src_node); @@ -9903,13 +9903,14 @@ const GenZir = struct { scope: *Scope, dest_type: Zir.Inst.Ref, result_ptr: Zir.Inst.Ref, + src_node: Ast.Node.Index, ) !GenZir { // Detect whether this expr() call goes into rvalue() to store the result into the // result location. If it does, elide the coerce_result_ptr instruction // as well as the store instruction, instead passing the result as an rvalue. var as_scope = parent_gz.makeSubBlock(scope); errdefer as_scope.unstack(); - as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr); + as_scope.rl_ptr = try as_scope.addPlNode(.coerce_result_ptr, src_node, Zir.Inst.Bin{ .lhs = dest_type, .rhs = result_ptr }); // `rl_ty_inst` needs to be set in case the stores to `rl_ptr` are eliminated. as_scope.rl_ty_inst = dest_type; diff --git a/src/Sema.zig b/src/Sema.zig index 8b8d828f54..2674d61a7d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1913,10 +1913,11 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const tracy = trace(@src()); defer tracy.end(); - const src: LazySrcLoc = sema.src; - const bin_inst = sema.code.instructions.items(.data)[inst].bin; - const pointee_ty = try sema.resolveType(block, src, bin_inst.lhs); - const ptr = try sema.resolveInst(bin_inst.rhs); + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; + const pointee_ty = try sema.resolveType(block, src, extra.lhs); + const ptr = try sema.resolveInst(extra.rhs); const target = sema.mod.getTarget(); const addr_space = target_util.defaultAddressSpace(target, .local); @@ -10143,7 +10144,10 @@ fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const rhs_ty = sema.typeOf(rhs); const rhs_scalar_ty = rhs_ty.scalarType(); - if (rhs_scalar_ty.isUnsignedInt()) { + if (rhs_scalar_ty.isUnsignedInt() or switch (rhs_scalar_ty.zigTypeTag()) { + .Int, .ComptimeInt, .Float, .ComptimeFloat => false, + else => true, + }) { return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(sema.mod)}); } @@ -10172,6 +10176,12 @@ fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const rhs = try sema.resolveInst(inst_data.operand); const rhs_ty = sema.typeOf(rhs); + const rhs_scalar_ty = rhs_ty.scalarType(); + + switch (rhs_scalar_ty.zigTypeTag()) { + .Int, .ComptimeInt, .Float, .ComptimeFloat => {}, + else => return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(sema.mod)}), + } const lhs = if (rhs_ty.zigTypeTag() == .Vector) try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, Value.zero)) @@ -17886,7 +17896,8 @@ fn validateRunTimeType( .Pointer => { const elem_ty = ty.childType(); switch (elem_ty.zigTypeTag()) { - .Opaque, .Fn => return true, + .Opaque => return true, + .Fn => return elem_ty.isFnOrHasRuntimeBits(), else => ty = elem_ty, } }, @@ -17950,7 +17961,25 @@ fn explainWhyTypeIsComptime( .Optional, => return, - .Pointer, .Array, .Vector => { + .Array, .Vector => { + try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.elemType()); + }, + .Pointer => { + const elem_ty = ty.elemType2(); + if (elem_ty.zigTypeTag() == .Fn) { + const fn_info = elem_ty.fnInfo(); + if (fn_info.is_generic) { + try mod.errNoteNonLazy(src_loc, msg, "function is generic", .{}); + } + switch (fn_info.cc) { + .Inline => try mod.errNoteNonLazy(src_loc, msg, "function has inline calling convention", .{}), + else => {}, + } + if (fn_info.return_type.comptimeOnly()) { + try mod.errNoteNonLazy(src_loc, msg, "function has a comptime-only return type", .{}); + } + return; + } try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.elemType()); }, diff --git a/src/Zir.zig b/src/Zir.zig index b43b775dfa..e6acfe8ed2 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -308,7 +308,7 @@ pub const Inst = struct { cmp_neq, /// Coerces a result location pointer to a new element type. It is evaluated "backwards"- /// as type coercion from the new element type to the old element type. - /// Uses the `bin` union field. + /// Uses the `pl_node` union field. Payload is `Bin`. /// LHS is destination element type, RHS is result pointer. coerce_result_ptr, /// Conditional branch. Splits control flow based on a boolean condition value. @@ -1603,7 +1603,7 @@ pub const Inst = struct { .cmp_gte = .pl_node, .cmp_gt = .pl_node, .cmp_neq = .pl_node, - .coerce_result_ptr = .bin, + .coerce_result_ptr = .pl_node, .condbr = .pl_node, .condbr_inline = .pl_node, .@"try" = .pl_node, diff --git a/src/print_zir.zig b/src/print_zir.zig index dbb91a0ae0..480a3e2a4f 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -144,7 +144,6 @@ const Writer = struct { switch (tag) { .array_type, .as, - .coerce_result_ptr, .elem_ptr, .elem_val, .store, @@ -355,6 +354,7 @@ const Writer = struct { .minimum, .elem_ptr_node, .elem_val_node, + .coerce_result_ptr, => try self.writePlNodeBin(stream, inst), .elem_ptr_imm => try self.writeElemPtrImm(stream, inst), diff --git a/test/cases/compile_errors/assign_inline_fn_to_non-comptime_var.zig b/test/cases/compile_errors/assign_inline_fn_to_non-comptime_var.zig new file mode 100644 index 0000000000..857123eae5 --- /dev/null +++ b/test/cases/compile_errors/assign_inline_fn_to_non-comptime_var.zig @@ -0,0 +1,12 @@ +export fn entry() void { + var a = &b; + _ = a; +} +fn b() callconv(.Inline) void { } + +// error +// backend=stage2 +// target=native +// +// :2:9: error: variable of type '*const fn() callconv(.Inline) void' must be const or comptime +// :2:9: note: function has inline calling convention diff --git a/test/cases/compile_errors/stage1/obj/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig b/test/cases/compile_errors/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig similarity index 63% rename from test/cases/compile_errors/stage1/obj/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig rename to test/cases/compile_errors/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig index e9bbf921ab..66b249653c 100644 --- a/test/cases/compile_errors/stage1/obj/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig +++ b/test/cases/compile_errors/attempt_to_negate_a_non-integer_non-float_or_non-vector_type.zig @@ -8,7 +8,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:6:15: error: negation of type 'anyerror!u32' +// :6:15: error: negation of type 'anyerror!u32' diff --git a/test/cases/compile_errors/stage1/test/reassign_to_array_parameter.zig b/test/cases/compile_errors/reassign_to_array_parameter.zig similarity index 63% rename from test/cases/compile_errors/stage1/test/reassign_to_array_parameter.zig rename to test/cases/compile_errors/reassign_to_array_parameter.zig index f3b51d2c0f..63d7a6d3e5 100644 --- a/test/cases/compile_errors/stage1/test/reassign_to_array_parameter.zig +++ b/test/cases/compile_errors/reassign_to_array_parameter.zig @@ -6,8 +6,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:2:15: error: cannot assign to constant +// :2:15: error: cannot assign to constant diff --git a/test/cases/compile_errors/stage1/test/reassign_to_struct_parameter.zig b/test/cases/compile_errors/reassign_to_struct_parameter.zig similarity index 66% rename from test/cases/compile_errors/stage1/test/reassign_to_struct_parameter.zig rename to test/cases/compile_errors/reassign_to_struct_parameter.zig index 994833d989..963448f8fe 100644 --- a/test/cases/compile_errors/stage1/test/reassign_to_struct_parameter.zig +++ b/test/cases/compile_errors/reassign_to_struct_parameter.zig @@ -9,8 +9,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:5:10: error: cannot assign to constant +// :5:10: error: cannot assign to constant diff --git a/test/cases/compile_errors/stage1/obj/assign_inline_fn_to_non-comptime_var.zig b/test/cases/compile_errors/stage1/obj/assign_inline_fn_to_non-comptime_var.zig deleted file mode 100644 index 7714bb7282..0000000000 --- a/test/cases/compile_errors/stage1/obj/assign_inline_fn_to_non-comptime_var.zig +++ /dev/null @@ -1,12 +0,0 @@ -export fn entry() void { - var a = b; - _ = a; -} -fn b() callconv(.Inline) void { } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:5: error: functions marked inline must be stored in const or comptime var -// tmp.zig:5:1: note: declared here From 90ae37cc00b08394c9e20c1699066cf9b08a13e7 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 28 Jun 2022 20:58:21 +0300 Subject: [PATCH 2011/2031] tests: move compile errors not planned for stage2 --- src/Sema.zig | 3 +-- ...paring_against_undefined_produces_undefined_value.zig | 9 +++++++++ .../{obj => }/add_wrap_assign_on_undefined_value.zig | 0 .../stage1/{obj => }/add_wrap_on_undefined_value.zig | 0 .../stage1/{obj => }/and_on_undefined_value.zig | 0 .../{obj => }/bin_and_assign_on_undefined_value.zig | 0 .../stage1/{obj => }/bin_and_on_undefined_value.zig | 0 .../stage1/{obj => }/bin_not_on_undefined_value.zig | 0 .../{obj => }/bin_or_assign_on_undefined_value.zig | 0 .../stage1/{obj => }/bin_or_on_undefined_value.zig | 0 .../{obj => }/bin_xor_assign_on_undefined_value.zig | 0 .../stage1/{obj => }/bin_xor_on_undefined_value.zig | 0 .../stage1/{obj => }/bool_not_on_undefined_value.zig | 0 .../stage1/{obj => }/catch_on_undefined_value.zig | 0 .../stage1/{obj => }/deref_on_undefined_value.zig | 0 .../stage1/{obj => }/div_assign_on_undefined_value.zig | 0 .../stage1/{obj => }/div_on_undefined_value.zig | 0 .../stage1/{obj => }/mod_assign_on_undefined_value.zig | 0 .../stage1/{obj => }/mod_on_undefined_value.zig | 0 .../stage1/{obj => }/mult_assign_on_undefined_value.zig | 0 .../stage1/{obj => }/mult_on_undefined_value.zig | 0 .../{obj => }/mult_wrap_assign_on_undefined_value.zig | 0 .../stage1/{obj => }/mult_wrap_on_undefined_value.zig | 0 .../stage1/{obj => }/negate_on_undefined_value.zig | 0 .../stage1/{obj => }/negate_wrap_on_undefined_value.zig | 0 ...paring_against_undefined_produces_undefined_value.zig | 9 --------- .../stage1/{obj => }/or_on_undefined_value.zig | 0 .../stage1/{obj => }/orelse_on_undefined_value.zig | 0 .../{obj => }/shift_left_assign_on_undefined_value.zig | 0 .../stage1/{obj => }/shift_left_on_undefined_value.zig | 0 .../{obj => }/shift_right_assign_on_undefined_value.zig | 0 .../stage1/{obj => }/shift_right_on_undefined_value.zig | 0 .../stage1/{obj => }/sub_assign_on_undefined_value.zig | 0 .../stage1/{obj => }/sub_on_undefined_value.zig | 0 .../{obj => }/sub_wrap_assign_on_undefined_value.zig | 0 .../stage1/{obj => }/sub_wrap_on_undefined_value.zig | 0 .../stage1/{obj => }/truncate_undefined_value.zig | 0 37 files changed, 10 insertions(+), 11 deletions(-) create mode 100644 test/cases/compile_errors/comparing_against_undefined_produces_undefined_value.zig rename test/cases/compile_errors/stage1/{obj => }/add_wrap_assign_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/add_wrap_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/and_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/bin_and_assign_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/bin_and_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/bin_not_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/bin_or_assign_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/bin_or_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/bin_xor_assign_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/bin_xor_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/bool_not_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/catch_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/deref_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/div_assign_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/div_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/mod_assign_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/mod_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/mult_assign_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/mult_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/mult_wrap_assign_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/mult_wrap_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/negate_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/negate_wrap_on_undefined_value.zig (100%) delete mode 100644 test/cases/compile_errors/stage1/obj/comparing_against_undefined_produces_undefined_value.zig rename test/cases/compile_errors/stage1/{obj => }/or_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/orelse_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/shift_left_assign_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/shift_left_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/shift_right_assign_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/shift_right_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/sub_assign_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/sub_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/sub_wrap_assign_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/sub_wrap_on_undefined_value.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/truncate_undefined_value.zig (100%) diff --git a/src/Sema.zig b/src/Sema.zig index 2674d61a7d..c67c8572e0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -13203,7 +13203,6 @@ fn zirCondbr( defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); @@ -13213,7 +13212,7 @@ fn zirCondbr( const uncasted_cond = try sema.resolveInst(extra.data.condition); const cond = try sema.coerce(parent_block, Type.bool, uncasted_cond, cond_src); - if (try sema.resolveDefinedValue(parent_block, src, cond)) |cond_val| { + if (try sema.resolveDefinedValue(parent_block, cond_src, cond)) |cond_val| { const body = if (cond_val.toBool()) then_body else else_body; // We use `analyzeBodyInner` since we want to propagate any possible // `error.ComptimeBreak` to the caller. diff --git a/test/cases/compile_errors/comparing_against_undefined_produces_undefined_value.zig b/test/cases/compile_errors/comparing_against_undefined_produces_undefined_value.zig new file mode 100644 index 0000000000..d98741b80d --- /dev/null +++ b/test/cases/compile_errors/comparing_against_undefined_produces_undefined_value.zig @@ -0,0 +1,9 @@ +export fn entry() void { + if (2 == undefined) {} +} + +// error +// backend=stage2 +// target=native +// +// :2:11: error: use of undefined value here causes undefined behavior diff --git a/test/cases/compile_errors/stage1/obj/add_wrap_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/add_wrap_assign_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/add_wrap_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/add_wrap_assign_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/add_wrap_on_undefined_value.zig b/test/cases/compile_errors/stage1/add_wrap_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/add_wrap_on_undefined_value.zig rename to test/cases/compile_errors/stage1/add_wrap_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/and_on_undefined_value.zig b/test/cases/compile_errors/stage1/and_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/and_on_undefined_value.zig rename to test/cases/compile_errors/stage1/and_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/bin_and_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/bin_and_assign_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/bin_and_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/bin_and_assign_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/bin_and_on_undefined_value.zig b/test/cases/compile_errors/stage1/bin_and_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/bin_and_on_undefined_value.zig rename to test/cases/compile_errors/stage1/bin_and_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/bin_not_on_undefined_value.zig b/test/cases/compile_errors/stage1/bin_not_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/bin_not_on_undefined_value.zig rename to test/cases/compile_errors/stage1/bin_not_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/bin_or_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/bin_or_assign_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/bin_or_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/bin_or_assign_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/bin_or_on_undefined_value.zig b/test/cases/compile_errors/stage1/bin_or_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/bin_or_on_undefined_value.zig rename to test/cases/compile_errors/stage1/bin_or_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/bin_xor_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/bin_xor_assign_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/bin_xor_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/bin_xor_assign_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/bin_xor_on_undefined_value.zig b/test/cases/compile_errors/stage1/bin_xor_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/bin_xor_on_undefined_value.zig rename to test/cases/compile_errors/stage1/bin_xor_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/bool_not_on_undefined_value.zig b/test/cases/compile_errors/stage1/bool_not_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/bool_not_on_undefined_value.zig rename to test/cases/compile_errors/stage1/bool_not_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/catch_on_undefined_value.zig b/test/cases/compile_errors/stage1/catch_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/catch_on_undefined_value.zig rename to test/cases/compile_errors/stage1/catch_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/deref_on_undefined_value.zig b/test/cases/compile_errors/stage1/deref_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/deref_on_undefined_value.zig rename to test/cases/compile_errors/stage1/deref_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/div_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/div_assign_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/div_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/div_assign_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/div_on_undefined_value.zig b/test/cases/compile_errors/stage1/div_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/div_on_undefined_value.zig rename to test/cases/compile_errors/stage1/div_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/mod_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/mod_assign_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/mod_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/mod_assign_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/mod_on_undefined_value.zig b/test/cases/compile_errors/stage1/mod_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/mod_on_undefined_value.zig rename to test/cases/compile_errors/stage1/mod_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/mult_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/mult_assign_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/mult_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/mult_assign_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/mult_on_undefined_value.zig b/test/cases/compile_errors/stage1/mult_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/mult_on_undefined_value.zig rename to test/cases/compile_errors/stage1/mult_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/mult_wrap_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/mult_wrap_assign_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/mult_wrap_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/mult_wrap_assign_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/mult_wrap_on_undefined_value.zig b/test/cases/compile_errors/stage1/mult_wrap_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/mult_wrap_on_undefined_value.zig rename to test/cases/compile_errors/stage1/mult_wrap_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/negate_on_undefined_value.zig b/test/cases/compile_errors/stage1/negate_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/negate_on_undefined_value.zig rename to test/cases/compile_errors/stage1/negate_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/negate_wrap_on_undefined_value.zig b/test/cases/compile_errors/stage1/negate_wrap_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/negate_wrap_on_undefined_value.zig rename to test/cases/compile_errors/stage1/negate_wrap_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/comparing_against_undefined_produces_undefined_value.zig b/test/cases/compile_errors/stage1/obj/comparing_against_undefined_produces_undefined_value.zig deleted file mode 100644 index 8bad61e6d1..0000000000 --- a/test/cases/compile_errors/stage1/obj/comparing_against_undefined_produces_undefined_value.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn entry() void { - if (2 == undefined) {} -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:11: error: use of undefined value here causes undefined behavior diff --git a/test/cases/compile_errors/stage1/obj/or_on_undefined_value.zig b/test/cases/compile_errors/stage1/or_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/or_on_undefined_value.zig rename to test/cases/compile_errors/stage1/or_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/orelse_on_undefined_value.zig b/test/cases/compile_errors/stage1/orelse_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/orelse_on_undefined_value.zig rename to test/cases/compile_errors/stage1/orelse_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/shift_left_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/shift_left_assign_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/shift_left_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/shift_left_assign_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/shift_left_on_undefined_value.zig b/test/cases/compile_errors/stage1/shift_left_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/shift_left_on_undefined_value.zig rename to test/cases/compile_errors/stage1/shift_left_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/shift_right_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/shift_right_assign_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/shift_right_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/shift_right_assign_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/shift_right_on_undefined_value.zig b/test/cases/compile_errors/stage1/shift_right_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/shift_right_on_undefined_value.zig rename to test/cases/compile_errors/stage1/shift_right_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/sub_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/sub_assign_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/sub_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/sub_assign_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/sub_on_undefined_value.zig b/test/cases/compile_errors/stage1/sub_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/sub_on_undefined_value.zig rename to test/cases/compile_errors/stage1/sub_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/sub_wrap_assign_on_undefined_value.zig b/test/cases/compile_errors/stage1/sub_wrap_assign_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/sub_wrap_assign_on_undefined_value.zig rename to test/cases/compile_errors/stage1/sub_wrap_assign_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/sub_wrap_on_undefined_value.zig b/test/cases/compile_errors/stage1/sub_wrap_on_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/sub_wrap_on_undefined_value.zig rename to test/cases/compile_errors/stage1/sub_wrap_on_undefined_value.zig diff --git a/test/cases/compile_errors/stage1/obj/truncate_undefined_value.zig b/test/cases/compile_errors/stage1/truncate_undefined_value.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/truncate_undefined_value.zig rename to test/cases/compile_errors/stage1/truncate_undefined_value.zig From 4f200eda9f055425ad3163d9be7ee43a057f7f2f Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 29 Jun 2022 20:56:52 +0300 Subject: [PATCH 2012/2031] stage2 llvm: ensure `@tagName` functions are unique --- src/codegen/llvm.zig | 35 +++++++++++++++++------------------ test/behavior/enum.zig | 21 +++++++++++++++++++++ 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index c62860c88d..7c52cf99cb 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -7620,33 +7620,31 @@ pub const FuncGen = struct { fn airTagName(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; - var arena_allocator = std.heap.ArenaAllocator.init(self.gpa); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); - const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); const enum_ty = self.air.typeOf(un_op); - const mod = self.dg.module; - const llvm_fn_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{ - try mod.declPtr(enum_ty.getOwnerDecl()).getFullyQualifiedName(mod), - }); - - const llvm_fn = try self.getEnumTagNameFunction(enum_ty, llvm_fn_name); + const llvm_fn = try self.getEnumTagNameFunction(enum_ty); const params = [_]*const llvm.Value{operand}; return self.builder.buildCall(llvm_fn, ¶ms, params.len, .Fast, .Auto, ""); } - fn getEnumTagNameFunction( - self: *FuncGen, - enum_ty: Type, - llvm_fn_name: [:0]const u8, - ) !*const llvm.Value { + fn getEnumTagNameFunction(self: *FuncGen, enum_ty: Type) !*const llvm.Value { + const enum_decl = enum_ty.getOwnerDecl(); + // TODO: detect when the type changes and re-emit this function. - if (self.dg.object.llvm_module.getNamedFunction(llvm_fn_name)) |llvm_fn| { - return llvm_fn; - } + const gop = try self.dg.object.decl_map.getOrPut(self.dg.gpa, enum_decl); + if (gop.found_existing) return gop.value_ptr.*; + errdefer assert(self.dg.object.decl_map.remove(enum_decl)); + + var arena_allocator = std.heap.ArenaAllocator.init(self.gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + const mod = self.dg.module; + const llvm_fn_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{ + try mod.declPtr(enum_decl).getFullyQualifiedName(mod), + }); const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); const llvm_ret_ty = try self.dg.lowerType(slice_ty); @@ -7663,6 +7661,7 @@ pub const FuncGen = struct { fn_val.setLinkage(.Internal); fn_val.setFunctionCallConv(.Fast); self.dg.addCommonFnAttributes(fn_val); + gop.value_ptr.* = fn_val; const prev_block = self.builder.getInsertBlock(); const prev_debug_location = self.builder.getCurrentDebugLocation2(); diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 602ad47b59..709d30af33 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -1107,3 +1107,24 @@ test "enum literal in array literal" { try expect(array[0] == .one); try expect(array[1] == .two); } + +test "tag name functions are unique" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + { + const E = enum { a, b }; + var b = E.a; + var a = @tagName(b); + _ = a; + } + { + const E = enum { a, b, c, d, e, f }; + var b = E.a; + var a = @tagName(b); + _ = a; + } +} From 4cde6dd10902ebeec3b3000b76d6395c49f5a241 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 28 Jun 2022 21:14:35 +0300 Subject: [PATCH 2013/2031] adjust tests that didn't pass CI --- test/cases/bad_inferred_variable_type.zig | 1 + test/cases/compile_errors/addition_with_non_numbers.zig | 2 +- .../cast_negative_value_to_unsigned_integer.zig | 2 +- ...le_log_statement_warning_deduplication_in_generic_fn.zig | 2 +- .../exceeded_maximum_bit_width_of_integer.zig | 2 +- test/cases/compile_errors/export_with_empty_name_string.zig | 2 +- test/cases/compile_errors/fieldParentPtr-non_struct.zig | 2 +- .../function_call_assigned_to_incorrect_type.zig | 2 +- .../compile_errors/function_prototype_with_no_body.zig | 2 +- .../compile_errors/int_to_err_non_global_invalid_number.zig | 2 +- test/cases/compile_errors/invalid_struct_field.zig | 2 +- .../invalid_underscore_placement_in_int_literal-1.zig | 2 +- test/cases/compile_errors/ptrcast_to_non-pointer.zig | 2 +- test/cases/compile_errors/reassign_to_array_parameter.zig | 2 +- test/cases/compile_errors/reassign_to_slice_parameter.zig | 2 +- .../obj}/endless_loop_in_function_evaluation.zig | 6 ++---- test/cases/compile_errors/suspend_inside_suspend_block.zig | 2 +- .../switch_expression-unreachable_else_prong_enum.zig | 2 +- 18 files changed, 19 insertions(+), 20 deletions(-) rename test/cases/compile_errors/{ => stage1/obj}/endless_loop_in_function_evaluation.zig (58%) diff --git a/test/cases/bad_inferred_variable_type.zig b/test/cases/bad_inferred_variable_type.zig index 47ceb9b638..7c464f48c9 100644 --- a/test/cases/bad_inferred_variable_type.zig +++ b/test/cases/bad_inferred_variable_type.zig @@ -5,5 +5,6 @@ pub fn main() void { // error // output_mode=Exe +// backend=stage2 // // :2:9: error: variable of type '@TypeOf(null)' must be const or comptime diff --git a/test/cases/compile_errors/addition_with_non_numbers.zig b/test/cases/compile_errors/addition_with_non_numbers.zig index deaeeba334..3687df914f 100644 --- a/test/cases/compile_errors/addition_with_non_numbers.zig +++ b/test/cases/compile_errors/addition_with_non_numbers.zig @@ -6,7 +6,7 @@ const x = Foo {.field = 1} + Foo {.field = 2}; export fn entry() usize { return @sizeOf(@TypeOf(x)); } // error -// backend=stage2 +// backend=llvm // target=native // // :4:28: error: invalid operands to binary expression: 'Struct' and 'Struct' diff --git a/test/cases/compile_errors/cast_negative_value_to_unsigned_integer.zig b/test/cases/compile_errors/cast_negative_value_to_unsigned_integer.zig index 2227005190..ebd9012015 100644 --- a/test/cases/compile_errors/cast_negative_value_to_unsigned_integer.zig +++ b/test/cases/compile_errors/cast_negative_value_to_unsigned_integer.zig @@ -10,7 +10,7 @@ export fn entry1() void { } // error -// backend=stage2 +// backend=llvm // target=native // // :3:36: error: type 'u32' cannot represent integer value '-1' diff --git a/test/cases/compile_errors/compile_log_statement_warning_deduplication_in_generic_fn.zig b/test/cases/compile_errors/compile_log_statement_warning_deduplication_in_generic_fn.zig index 4eb0036d1d..76e1c80cf9 100644 --- a/test/cases/compile_errors/compile_log_statement_warning_deduplication_in_generic_fn.zig +++ b/test/cases/compile_errors/compile_log_statement_warning_deduplication_in_generic_fn.zig @@ -8,7 +8,7 @@ fn inner(comptime n: usize) void { } // error -// backend=stage2 +// backend=llvm // target=native // // :7:39: error: found compile log statement diff --git a/test/cases/compile_errors/exceeded_maximum_bit_width_of_integer.zig b/test/cases/compile_errors/exceeded_maximum_bit_width_of_integer.zig index b7d5c95c25..b002fca9c9 100644 --- a/test/cases/compile_errors/exceeded_maximum_bit_width_of_integer.zig +++ b/test/cases/compile_errors/exceeded_maximum_bit_width_of_integer.zig @@ -8,7 +8,7 @@ export fn entry2() void { } // error -// backend=stage2 +// backend=llvm // target=native // // :2:15: error: primitive integer type 'u65536' exceeds maximum bit width of 65535 diff --git a/test/cases/compile_errors/export_with_empty_name_string.zig b/test/cases/compile_errors/export_with_empty_name_string.zig index 6403761d3c..4f0cee9cf4 100644 --- a/test/cases/compile_errors/export_with_empty_name_string.zig +++ b/test/cases/compile_errors/export_with_empty_name_string.zig @@ -4,7 +4,7 @@ comptime { } // error -// backend=stage2 +// backend=llvm // target=native // // :3:21: error: exported symbol name cannot be empty diff --git a/test/cases/compile_errors/fieldParentPtr-non_struct.zig b/test/cases/compile_errors/fieldParentPtr-non_struct.zig index b6e53149c3..0a2f46e5c9 100644 --- a/test/cases/compile_errors/fieldParentPtr-non_struct.zig +++ b/test/cases/compile_errors/fieldParentPtr-non_struct.zig @@ -4,7 +4,7 @@ export fn foo(a: *i32) *Foo { } // error -// backend=stage2 +// backend=llvm // target=native // // :3:28: error: expected struct type, found 'i32' diff --git a/test/cases/compile_errors/function_call_assigned_to_incorrect_type.zig b/test/cases/compile_errors/function_call_assigned_to_incorrect_type.zig index 7c164b8a86..41d5b588da 100644 --- a/test/cases/compile_errors/function_call_assigned_to_incorrect_type.zig +++ b/test/cases/compile_errors/function_call_assigned_to_incorrect_type.zig @@ -7,7 +7,7 @@ fn concat() [16]f32 { } // error -// backend=stage2 +// backend=llvm // target=native // // :3:17: error: expected type '[4]f32', found '[16]f32' diff --git a/test/cases/compile_errors/function_prototype_with_no_body.zig b/test/cases/compile_errors/function_prototype_with_no_body.zig index 12eeda58ca..45bafa75f2 100644 --- a/test/cases/compile_errors/function_prototype_with_no_body.zig +++ b/test/cases/compile_errors/function_prototype_with_no_body.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage2 +// backend=llvm // target=native // // :1:1: error: non-extern function has no body diff --git a/test/cases/compile_errors/int_to_err_non_global_invalid_number.zig b/test/cases/compile_errors/int_to_err_non_global_invalid_number.zig index 0b774cd29e..43aad76f45 100644 --- a/test/cases/compile_errors/int_to_err_non_global_invalid_number.zig +++ b/test/cases/compile_errors/int_to_err_non_global_invalid_number.zig @@ -13,7 +13,7 @@ comptime { } // error -// backend=stage2 +// backend=llvm // target=native // // :11:13: error: 'error.B' not a member of error set 'error{A,C}' diff --git a/test/cases/compile_errors/invalid_struct_field.zig b/test/cases/compile_errors/invalid_struct_field.zig index 0a704e0270..d351a012f9 100644 --- a/test/cases/compile_errors/invalid_struct_field.zig +++ b/test/cases/compile_errors/invalid_struct_field.zig @@ -18,4 +18,4 @@ export fn g() void { // :4:7: error: no field named 'foo' in struct 'tmp.A' // :1:11: note: struct declared here // :10:17: error: no field named 'bar' in struct 'tmp.A' -// :1:11: note: struct declared here + diff --git a/test/cases/compile_errors/invalid_underscore_placement_in_int_literal-1.zig b/test/cases/compile_errors/invalid_underscore_placement_in_int_literal-1.zig index de519f2ddb..868ea8d42a 100644 --- a/test/cases/compile_errors/invalid_underscore_placement_in_int_literal-1.zig +++ b/test/cases/compile_errors/invalid_underscore_placement_in_int_literal-1.zig @@ -4,7 +4,7 @@ fn main() void { } // error -// backend=stage2 +// backend=llvm // target=native // // :2:21: error: expected expression, found 'invalid bytes' diff --git a/test/cases/compile_errors/ptrcast_to_non-pointer.zig b/test/cases/compile_errors/ptrcast_to_non-pointer.zig index 2fe599a2e8..66a11a602b 100644 --- a/test/cases/compile_errors/ptrcast_to_non-pointer.zig +++ b/test/cases/compile_errors/ptrcast_to_non-pointer.zig @@ -3,7 +3,7 @@ export fn entry(a: *i32) usize { } // error -// backend=stage2 +// backend=llvm // target=native // // :2:21: error: expected pointer type, found 'usize' diff --git a/test/cases/compile_errors/reassign_to_array_parameter.zig b/test/cases/compile_errors/reassign_to_array_parameter.zig index 63d7a6d3e5..4927329970 100644 --- a/test/cases/compile_errors/reassign_to_array_parameter.zig +++ b/test/cases/compile_errors/reassign_to_array_parameter.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage2 +// backend=llvm // target=native // // :2:15: error: cannot assign to constant diff --git a/test/cases/compile_errors/reassign_to_slice_parameter.zig b/test/cases/compile_errors/reassign_to_slice_parameter.zig index ccf79f4574..586eb230df 100644 --- a/test/cases/compile_errors/reassign_to_slice_parameter.zig +++ b/test/cases/compile_errors/reassign_to_slice_parameter.zig @@ -6,7 +6,7 @@ export fn entry() void { } // error -// backend=stage2 +// backend=llvm // target=native // // :2:10: error: cannot assign to constant diff --git a/test/cases/compile_errors/endless_loop_in_function_evaluation.zig b/test/cases/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig similarity index 58% rename from test/cases/compile_errors/endless_loop_in_function_evaluation.zig rename to test/cases/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig index fb2af44b23..302ee242e0 100644 --- a/test/cases/compile_errors/endless_loop_in_function_evaluation.zig +++ b/test/cases/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig @@ -6,9 +6,7 @@ fn fibonacci(x: i32) i32 { export fn entry() usize { return @sizeOf(@TypeOf(seventh_fib_number)); } // error -// backend=stage2 +// backend=stage1 // target=native // -// :3:21: error: evaluation exceeded 1000 backwards branches -// :3:21: note: called from here (999 times) -// :1:37: note: called from here +// tmp.zig:3:21: error: evaluation exceeded 1000 backwards branches diff --git a/test/cases/compile_errors/suspend_inside_suspend_block.zig b/test/cases/compile_errors/suspend_inside_suspend_block.zig index 482515f905..80436bd07f 100644 --- a/test/cases/compile_errors/suspend_inside_suspend_block.zig +++ b/test/cases/compile_errors/suspend_inside_suspend_block.zig @@ -9,7 +9,7 @@ fn foo() void { } // error -// backend=stage2 +// backend=llvm // target=native // // :6:9: error: cannot suspend inside suspend block diff --git a/test/cases/compile_errors/switch_expression-unreachable_else_prong_enum.zig b/test/cases/compile_errors/switch_expression-unreachable_else_prong_enum.zig index 21440a63a1..cc837b8b5e 100644 --- a/test/cases/compile_errors/switch_expression-unreachable_else_prong_enum.zig +++ b/test/cases/compile_errors/switch_expression-unreachable_else_prong_enum.zig @@ -18,7 +18,7 @@ fn foo(x: u8) void { export fn entry() usize { return @sizeOf(@TypeOf(&foo)); } // error -// backend=stage2 +// backend=llvm // target=native // // :14:14: error: unreachable else prong; all cases already handled From b3b96b5e288ddf3694b4a0d203c684b9b8f6b49f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 30 Jun 2022 11:48:37 -0700 Subject: [PATCH 2014/2031] LLVM: lower float negation with xor a constant Rather than a compiler-rt call in the case that LLVM does not support lowering the fneg instruction. --- src/codegen/llvm.zig | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index d5096e3fe5..37d6d2c52b 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -6598,10 +6598,16 @@ pub const FuncGen = struct { } else b: { const float_bits = scalar_ty.floatBits(target); break :b switch (op) { - .neg => FloatOpStrat{ - .libc = std.fmt.bufPrintZ(&fn_name_buf, "__neg{s}f2", .{ - compilerRtFloatAbbrev(float_bits), - }) catch unreachable, + .neg => { + // In this case we can generate a softfloat negation by XORing the + // bits with a constant. + const int_llvm_ty = self.dg.context.intType(float_bits); + const one = int_llvm_ty.constInt(1, .False); + const shift_amt = int_llvm_ty.constInt(float_bits - 1, .False); + const sign_mask = one.constShl(shift_amt); + const bitcasted_operand = self.builder.buildBitCast(params[0], int_llvm_ty, ""); + const result = self.builder.buildXor(bitcasted_operand, sign_mask, ""); + return self.builder.buildBitCast(result, llvm_ty, ""); }, .add, .sub, .div, .mul => FloatOpStrat{ .libc = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f3", .{ From be18459c81f831e455750e62f37dae6aeef62f1d Mon Sep 17 00:00:00 2001 From: Mikko Kaihlavirta <37382384+kaihlavirta@users.noreply.github.com> Date: Fri, 1 Jul 2022 00:09:36 +0300 Subject: [PATCH 2015/2031] std.event: use .{} to initiate Lock to unlocked state Co-authored-by: Mikko Kaihlavirta --- lib/std/event/group.zig | 2 +- lib/std/event/locked.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/event/group.zig b/lib/std/event/group.zig index 85eeeaf2b7..639d90b12a 100644 --- a/lib/std/event/group.zig +++ b/lib/std/event/group.zig @@ -35,7 +35,7 @@ pub fn Group(comptime ReturnType: type) type { return Self{ .frame_stack = Stack.init(), .alloc_stack = AllocStack.init(), - .lock = Lock.init(), + .lock = .{}, .allocator = allocator, }; } diff --git a/lib/std/event/locked.zig b/lib/std/event/locked.zig index e921803447..66495c3772 100644 --- a/lib/std/event/locked.zig +++ b/lib/std/event/locked.zig @@ -22,7 +22,7 @@ pub fn Locked(comptime T: type) type { pub fn init(data: T) Self { return Self{ - .lock = Lock.init(), + .lock = .{}, .private_data = data, }; } From 902dc8c721c762bc5d1b9786bad47b21da45042c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 30 Jun 2022 17:57:00 -0700 Subject: [PATCH 2016/2031] CI: update freebsd pkg install command --- ci/srht/freebsd_script | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/srht/freebsd_script b/ci/srht/freebsd_script index 8a48097b9d..3a10cccadf 100755 --- a/ci/srht/freebsd_script +++ b/ci/srht/freebsd_script @@ -4,7 +4,7 @@ set -x set -e sudo pkg update -fq -sudo pkg install -y cmake py38-s3cmd wget curl jq samurai +sudo pkg install -y cmake py39-s3cmd wget curl jq samurai ZIGDIR="$(pwd)" CACHE_BASENAME="zig+llvm+lld+clang-x86_64-freebsd-gnu-0.9.1" From 152462e2e1513ca9d7db5c2d6a2a61b547c5151c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 30 Jun 2022 14:31:46 -0700 Subject: [PATCH 2017/2031] stage2: object format affects whether LLVM can be used --- src/Compilation.zig | 6 +----- src/target.zig | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index c6c7a94b41..99b502dbd7 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1024,10 +1024,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { if (options.use_llvm) |explicit| break :blk explicit; - // If we are outputting .c code we must use Zig backend. - if (ofmt == .c) - break :blk false; - // If emitting to LLVM bitcode object format, must use LLVM backend. if (options.emit_llvm_ir != null or options.emit_llvm_bc != null) break :blk true; @@ -1042,7 +1038,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { break :blk true; // If LLVM does not support the target, then we can't use it. - if (!target_util.hasLlvmSupport(options.target)) + if (!target_util.hasLlvmSupport(options.target, ofmt)) break :blk false; // Prefer LLVM for release builds. diff --git a/src/target.zig b/src/target.zig index 93c179b7f0..ea8b3efc45 100644 --- a/src/target.zig +++ b/src/target.zig @@ -204,7 +204,24 @@ pub fn hasValgrindSupport(target: std.Target) bool { /// The set of targets that LLVM has non-experimental support for. /// Used to select between LLVM backend and self-hosted backend when compiling in /// release modes. -pub fn hasLlvmSupport(target: std.Target) bool { +pub fn hasLlvmSupport(target: std.Target, ofmt: std.Target.ObjectFormat) bool { + switch (ofmt) { + // LLVM does not support these object formats: + .c, + .plan9, + => return false, + + .coff, + .elf, + .macho, + .wasm, + .spirv, + .hex, + .raw, + .nvptx, + => {}, + } + return switch (target.cpu.arch) { .arm, .armeb, From 67db2b85b771d8b0f9d765922951c31fce9c8cc2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 30 Jun 2022 14:32:10 -0700 Subject: [PATCH 2018/2031] remove plan9 test coverage This regressed; the plan9 linker code is crashing when trying to build compiler-rt and we have no active plan9 maintainers involved in the Zig project. Anyone is welcome to come over and take the role; however, it's not one of the tier 1, 2, or 3 targets, so we will not be blocking progress towards 1.0 on plan9. --- test/cases/plan9/exit.zig | 5 ---- .../plan9/hello_world_with_updates.0.zig | 28 ------------------- .../plan9/hello_world_with_updates.1.zig | 10 ------- 3 files changed, 43 deletions(-) delete mode 100644 test/cases/plan9/exit.zig delete mode 100644 test/cases/plan9/hello_world_with_updates.0.zig delete mode 100644 test/cases/plan9/hello_world_with_updates.1.zig diff --git a/test/cases/plan9/exit.zig b/test/cases/plan9/exit.zig deleted file mode 100644 index 735818fb83..0000000000 --- a/test/cases/plan9/exit.zig +++ /dev/null @@ -1,5 +0,0 @@ -pub fn main() void {} - -// run -// target=x86_64-plan9,aarch64-plan9 -// diff --git a/test/cases/plan9/hello_world_with_updates.0.zig b/test/cases/plan9/hello_world_with_updates.0.zig deleted file mode 100644 index 7e7c373251..0000000000 --- a/test/cases/plan9/hello_world_with_updates.0.zig +++ /dev/null @@ -1,28 +0,0 @@ -pub fn main() void { - const str = "Hello World!\n"; - asm volatile ( - \\push $0 - \\push %%r10 - \\push %%r11 - \\push $1 - \\push $0 - \\syscall - \\pop %%r11 - \\pop %%r11 - \\pop %%r11 - \\pop %%r11 - \\pop %%r11 - : - // pwrite - : [syscall_number] "{rbp}" (51), - [hey] "{r11}" (@ptrToInt(str)), - [strlen] "{r10}" (str.len), - : "rcx", "rbp", "r11", "memory" - ); -} - -// run -// target=x86_64-plan9 -// -// Hello World -// diff --git a/test/cases/plan9/hello_world_with_updates.1.zig b/test/cases/plan9/hello_world_with_updates.1.zig deleted file mode 100644 index 4111a8dc08..0000000000 --- a/test/cases/plan9/hello_world_with_updates.1.zig +++ /dev/null @@ -1,10 +0,0 @@ -const std = @import("std"); -pub fn main() void { - const str = "Hello World!\n"; - _ = std.os.plan9.pwrite(1, str, str.len, 0); -} - -// run -// -// Hello World -// From c030ec1884ad7c553d6680944b47a1fc7653586f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 30 Jun 2022 15:58:51 -0700 Subject: [PATCH 2019/2031] LLVM: use unnamed struct llvm type for unions when necessary The constant value lowering for unions was missing a check for whether the payload was itself an unnamed struct. Lowerings of other types already handle this case. closes #11971 --- src/codegen/llvm.zig | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 7f85570e06..45a90c81e5 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3401,6 +3401,13 @@ pub const DeclGen = struct { const union_obj = tv.ty.cast(Type.Payload.Union).?.data; const field_index = union_obj.tag_ty.enumTagFieldIndex(tag_and_val.tag, dg.module).?; assert(union_obj.haveFieldTypes()); + + // Sometimes we must make an unnamed struct because LLVM does + // not support bitcasting our payload struct to the true union payload type. + // Instead we use an unnamed struct and every reference to the global + // must pointer cast to the expected type before accessing the union. + var need_unnamed: bool = layout.most_aligned_field != field_index; + const field_ty = union_obj.fields.values()[field_index].ty; const payload = p: { if (!field_ty.hasRuntimeBitsIgnoreComptime()) { @@ -3408,6 +3415,7 @@ pub const DeclGen = struct { break :p dg.context.intType(8).arrayType(padding_len).getUndef(); } const field = try lowerValue(dg, .{ .ty = field_ty, .val = tag_and_val.val }); + need_unnamed = need_unnamed or dg.isUnnamedType(field_ty, field); const field_size = field_ty.abiSize(target); if (field_size == layout.payload_size) { break :p field; @@ -3419,12 +3427,6 @@ pub const DeclGen = struct { break :p dg.context.constStruct(&fields, fields.len, .True); }; - // In this case we must make an unnamed struct because LLVM does - // not support bitcasting our payload struct to the true union payload type. - // Instead we use an unnamed struct and every reference to the global - // must pointer cast to the expected type before accessing the union. - const need_unnamed = layout.most_aligned_field != field_index; - if (layout.tag_size == 0) { const fields: [1]*const llvm.Value = .{payload}; if (need_unnamed) { From d3542be875704bffc931970948345d1a44e25b8e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 30 Jun 2022 16:32:03 -0700 Subject: [PATCH 2020/2031] LLVM: be sure to never pass align(0) attribute This can happen with pointers to zero-bit types. This commit fixes an LLVM assertion being tripped. --- src/codegen/llvm.zig | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 45a90c81e5..c3194ccda1 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -768,7 +768,11 @@ pub const Object = struct { if (ptr_info.@"align" != 0) { dg.addArgAttrInt(llvm_func, llvm_arg_i, "align", ptr_info.@"align"); } else { - dg.addArgAttrInt(llvm_func, llvm_arg_i, "align", ptr_info.pointee_type.abiAlignment(target)); + const elem_align = @maximum( + ptr_info.pointee_type.abiAlignment(target), + 1, + ); + dg.addArgAttrInt(llvm_func, llvm_arg_i, "align", elem_align); } } } @@ -840,7 +844,8 @@ pub const Object = struct { if (ptr_info.@"align" != 0) { dg.addArgAttrInt(llvm_func, llvm_arg_i, "align", ptr_info.@"align"); } else { - dg.addArgAttrInt(llvm_func, llvm_arg_i, "align", ptr_info.pointee_type.abiAlignment(target)); + const elem_align = @maximum(ptr_info.pointee_type.abiAlignment(target), 1); + dg.addArgAttrInt(llvm_func, llvm_arg_i, "align", elem_align); } const ptr_param = llvm_func.getParam(llvm_arg_i); llvm_arg_i += 1; From 095e24e537fcb6a702b992d946d1ca73d6f608b3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 30 Jun 2022 19:09:21 -0700 Subject: [PATCH 2021/2031] stage2: implement alignment calculation of vectors closes #11856 --- lib/std/simd.zig | 1 - src/type.zig | 10 +++++++--- test/behavior/vector.zig | 24 ++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/std/simd.zig b/lib/std/simd.zig index a7ce0ab3fd..a30622aef6 100644 --- a/lib/std/simd.zig +++ b/lib/std/simd.zig @@ -160,7 +160,6 @@ pub fn extract( } test "vector patterns" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; const base = @Vector(4, u32){ 10, 20, 30, 40 }; const other_base = @Vector(4, u32){ 55, 66, 77, 88 }; diff --git a/src/type.zig b/src/type.zig index 0ca7ba83c5..e116f1ed20 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2906,9 +2906,13 @@ pub const Type = extern union { .array, .array_sentinel => return ty.elemType().abiAlignmentAdvanced(target, strat), - // TODO audit this - is there any more complicated logic to determine - // ABI alignment of vectors? - .vector => return AbiAlignmentAdvanced{ .scalar = 16 }, + .vector => { + const len = ty.arrayLen(); + const bits = try bitSizeAdvanced(ty.elemType(), target, sema_kit); + const bytes = (bits + 7) / 8; + const alignment = std.math.ceilPowerOfTwoAssert(u64, bytes * len); + return AbiAlignmentAdvanced{ .scalar = @intCast(u32, alignment) }; + }, .i16, .u16 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(16, target) }, .u29 => return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(29, target) }, diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index fc49bce6e2..e355db5166 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -1048,3 +1048,27 @@ test "@shlWithOverflow" { try S.doTheTest(); comptime try S.doTheTest(); } + +test "alignment of vectors" { + try expect(@alignOf(@Vector(2, u8)) == 2); + try expect(@alignOf(@Vector(2, u1)) == 2); + try expect(@alignOf(@Vector(1, u1)) == 1); + try expect(@alignOf(@Vector(2, u16)) == 4); +} + +test "loading the second vector from a slice of vectors" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + @setRuntimeSafety(false); + var small_bases = [2]@Vector(2, u8){ + @Vector(2, u8){ 0, 1 }, + @Vector(2, u8){ 2, 3 }, + }; + var a: []const @Vector(2, u8) = &small_bases; + var a4 = a[1][1]; + try expect(a4 == 3); +} From 6d24c40b6e79176b05ec735adcecfd346b03f059 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 30 Jun 2022 16:10:15 +0300 Subject: [PATCH 2022/2031] Sema: improve bitcast to enum error --- src/Sema.zig | 20 +++++++++++++++---- .../compile_errors/bitCast_to_enum_type.zig | 12 +++++++++++ .../stage1/obj/bitCast_to_enum_type.zig | 10 ---------- 3 files changed, 28 insertions(+), 14 deletions(-) create mode 100644 test/cases/compile_errors/bitCast_to_enum_type.zig delete mode 100644 test/cases/compile_errors/stage1/obj/bitCast_to_enum_type.zig diff --git a/src/Sema.zig b/src/Sema.zig index 862d5cbf48..08372bf7b3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7617,11 +7617,11 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); + const operand = try sema.resolveInst(extra.rhs); switch (dest_ty.zigTypeTag()) { .AnyFrame, .ComptimeFloat, .ComptimeInt, - .Enum, .EnumLiteral, .ErrorSet, .ErrorUnion, @@ -7634,7 +7634,21 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .Type, .Undefined, .Void, - => return sema.fail(block, dest_ty_src, "invalid type '{}' for @bitCast", .{dest_ty.fmt(sema.mod)}), + => return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}'", .{dest_ty.fmt(sema.mod)}), + + .Enum => { + const msg = msg: { + const msg = try sema.errMsg(block, dest_ty_src, "cannot @bitCast to '{}'", .{dest_ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + switch (sema.typeOf(operand).zigTypeTag()) { + .Int, .ComptimeInt => try sema.errNote(block, dest_ty_src, msg, "use @intToEnum for type coercion", .{}), + else => {}, + } + + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + }, .Pointer => return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}', use @ptrCast to cast to a pointer", .{ dest_ty.fmt(sema.mod), @@ -7658,8 +7672,6 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .Vector, => {}, } - - const operand = try sema.resolveInst(extra.rhs); return sema.bitCast(block, dest_ty, operand, operand_src); } diff --git a/test/cases/compile_errors/bitCast_to_enum_type.zig b/test/cases/compile_errors/bitCast_to_enum_type.zig new file mode 100644 index 0000000000..add531627f --- /dev/null +++ b/test/cases/compile_errors/bitCast_to_enum_type.zig @@ -0,0 +1,12 @@ +export fn entry() void { + const E = enum(u32) { a, b }; + const y = @bitCast(E, @as(u32, 3)); + _ = y; +} + +// error +// backend=stage2 +// target=native +// +// :3:24: error: cannot @bitCast to 'tmp.entry.E' +// :3:24: note: use @intToEnum for type coercion diff --git a/test/cases/compile_errors/stage1/obj/bitCast_to_enum_type.zig b/test/cases/compile_errors/stage1/obj/bitCast_to_enum_type.zig deleted file mode 100644 index 4d63ab9e01..0000000000 --- a/test/cases/compile_errors/stage1/obj/bitCast_to_enum_type.zig +++ /dev/null @@ -1,10 +0,0 @@ -export fn entry() void { - const y = @bitCast(enum(u32) { a, b }, @as(u32, 3)); - _ = y; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:24: error: cannot cast a value of type 'y' From 3014a0d5f1dcbcfdcec3852ffd54f3c589fe3e83 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 30 Jun 2022 16:39:54 +0300 Subject: [PATCH 2023/2031] Sema: validate callconv --- src/Sema.zig | 40 +++++++++++++++++++ .../{stage1/obj => }/bogus_compile_var.zig | 4 +- ...aapcs_aapcsvfp_on_unsupported_platform.zig | 11 +++++ ...conv_interrupt_on_unsupported_platform.zig | 7 ++++ ...allconv_signal_on_unsupported_platform.zig | 7 ++++ ...call_thiscall_on_unsupported_platform.zig} | 8 ++-- ...onv_vectorcall_on_unsupported_platform.zig | 7 ++++ ...aapcs_aapcsvfp_on_unsupported_platform.zig | 11 ----- ...conv_interrupt_on_unsupported_platform.zig | 7 ---- ...allconv_signal_on_unsupported_platform.zig | 7 ---- ...all_thiscall_on_unsupported_platform-1.zig | 11 ----- ...onv_vectorcall_on_unsupported_platform.zig | 7 ---- 12 files changed, 78 insertions(+), 49 deletions(-) rename test/cases/compile_errors/{stage1/obj => }/bogus_compile_var.zig (57%) create mode 100644 test/cases/compile_errors/callconv_apcs_aapcs_aapcsvfp_on_unsupported_platform.zig create mode 100644 test/cases/compile_errors/callconv_interrupt_on_unsupported_platform.zig create mode 100644 test/cases/compile_errors/callconv_signal_on_unsupported_platform.zig rename test/cases/compile_errors/{stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-0.zig => callconv_stdcall_fastcall_thiscall_on_unsupported_platform.zig} (58%) create mode 100644 test/cases/compile_errors/callconv_vectorcall_on_unsupported_platform.zig delete mode 100644 test/cases/compile_errors/stage1/obj/callconv_apcs_aapcs_aapcsvfp_on_unsupported_platform.zig delete mode 100644 test/cases/compile_errors/stage1/obj/callconv_interrupt_on_unsupported_platform.zig delete mode 100644 test/cases/compile_errors/stage1/obj/callconv_signal_on_unsupported_platform.zig delete mode 100644 test/cases/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-1.zig delete mode 100644 test/cases/compile_errors/stage1/obj/callconv_vectorcall_on_unsupported_platform.zig diff --git a/src/Sema.zig b/src/Sema.zig index 08372bf7b3..0d568f8b38 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7007,6 +7007,7 @@ fn funcCommon( noalias_bits: u32, ) CompileError!Air.Inst.Ref { const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; + const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = src_node_offset }; var is_generic = bare_return_type.tag() == .generic_poison or alignment == null or @@ -7109,6 +7110,45 @@ fn funcCommon( const cc_workaround = cc orelse .Unspecified; const align_workaround = alignment orelse 0; + const arch = sema.mod.getTarget().cpu.arch; + if (switch (cc_workaround) { + .Unspecified, .C, .Naked, .Async, .Inline => null, + .Interrupt => switch (arch) { + .i386, .x86_64, .avr, .msp430 => null, + else => @as([]const u8, "i386, x86_64, AVR, and MSP430"), + }, + .Signal => switch (arch) { + .avr => null, + else => @as([]const u8, "AVR"), + }, + .Stdcall, .Fastcall, .Thiscall => switch (arch) { + .i386 => null, + else => @as([]const u8, "i386"), + }, + .Vectorcall => switch (arch) { + .i386, .aarch64, .aarch64_be, .aarch64_32 => null, + else => @as([]const u8, "i386 and AArch64"), + }, + .APCS, .AAPCS, .AAPCSVFP => switch (arch) { + .arm, .armeb, .aarch64, .aarch64_be, .aarch64_32 => null, + else => @as([]const u8, "ARM"), + }, + .SysV, .Win64 => switch (arch) { + .x86_64 => null, + else => @as([]const u8, "x86_64"), + }, + .PtxKernel => switch (arch) { + .nvptx, .nvptx64 => null, + else => @as([]const u8, "nvptx and nvptx64"), + }, + }) |allowed_platform| { + return sema.fail(block, cc_src, "callconv '{s}' is only available on {s}, not {s}", .{ + @tagName(cc_workaround), + allowed_platform, + @tagName(arch), + }); + } + break :fn_ty try Type.Tag.function.create(sema.arena, .{ .param_types = param_types, .comptime_params = comptime_params.ptr, diff --git a/test/cases/compile_errors/stage1/obj/bogus_compile_var.zig b/test/cases/compile_errors/bogus_compile_var.zig similarity index 57% rename from test/cases/compile_errors/stage1/obj/bogus_compile_var.zig rename to test/cases/compile_errors/bogus_compile_var.zig index 845d943c7d..28d8b1dba5 100644 --- a/test/cases/compile_errors/stage1/obj/bogus_compile_var.zig +++ b/test/cases/compile_errors/bogus_compile_var.zig @@ -2,7 +2,7 @@ const x = @import("builtin").bogus; export fn entry() usize { return @sizeOf(@TypeOf(x)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:29: error: container 'builtin' has no member called 'bogus' +// :1:29: error: struct 'builtin.builtin' has no member named 'bogus' diff --git a/test/cases/compile_errors/callconv_apcs_aapcs_aapcsvfp_on_unsupported_platform.zig b/test/cases/compile_errors/callconv_apcs_aapcs_aapcsvfp_on_unsupported_platform.zig new file mode 100644 index 0000000000..cc6fc59a1a --- /dev/null +++ b/test/cases/compile_errors/callconv_apcs_aapcs_aapcsvfp_on_unsupported_platform.zig @@ -0,0 +1,11 @@ +export fn entry1() callconv(.APCS) void {} +export fn entry2() callconv(.AAPCS) void {} +export fn entry3() callconv(.AAPCSVFP) void {} + +// error +// backend=stage2 +// target=x86_64-linux-none +// +// :1:30: error: callconv 'APCS' is only available on ARM, not x86_64 +// :2:30: error: callconv 'AAPCS' is only available on ARM, not x86_64 +// :3:30: error: callconv 'AAPCSVFP' is only available on ARM, not x86_64 diff --git a/test/cases/compile_errors/callconv_interrupt_on_unsupported_platform.zig b/test/cases/compile_errors/callconv_interrupt_on_unsupported_platform.zig new file mode 100644 index 0000000000..5f11fdcfa8 --- /dev/null +++ b/test/cases/compile_errors/callconv_interrupt_on_unsupported_platform.zig @@ -0,0 +1,7 @@ +export fn entry() callconv(.Interrupt) void {} + +// error +// backend=stage2 +// target=aarch64-linux-none +// +// :1:29: error: callconv 'Interrupt' is only available on i386, x86_64, AVR, and MSP430, not aarch64 diff --git a/test/cases/compile_errors/callconv_signal_on_unsupported_platform.zig b/test/cases/compile_errors/callconv_signal_on_unsupported_platform.zig new file mode 100644 index 0000000000..9233655e35 --- /dev/null +++ b/test/cases/compile_errors/callconv_signal_on_unsupported_platform.zig @@ -0,0 +1,7 @@ +export fn entry() callconv(.Signal) void {} + +// error +// backend=stage2 +// target=x86_64-linux-none +// +// :1:29: error: callconv 'Signal' is only available on AVR, not x86_64 diff --git a/test/cases/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-0.zig b/test/cases/compile_errors/callconv_stdcall_fastcall_thiscall_on_unsupported_platform.zig similarity index 58% rename from test/cases/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-0.zig rename to test/cases/compile_errors/callconv_stdcall_fastcall_thiscall_on_unsupported_platform.zig index f4994107cd..d22fd13e8a 100644 --- a/test/cases/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-0.zig +++ b/test/cases/compile_errors/callconv_stdcall_fastcall_thiscall_on_unsupported_platform.zig @@ -15,9 +15,9 @@ export fn entry3() void { } // error -// backend=stage1 +// backend=stage2 // target=x86_64-linux-none // -// tmp.zig:1:27: error: callconv 'Stdcall' is only available on x86, not x86_64 -// tmp.zig:2:27: error: callconv 'Fastcall' is only available on x86, not x86_64 -// tmp.zig:3:27: error: callconv 'Thiscall' is only available on x86, not x86_64 +// :1:28: error: callconv 'Stdcall' is only available on i386, not x86_64 +// :2:28: error: callconv 'Fastcall' is only available on i386, not x86_64 +// :3:28: error: callconv 'Thiscall' is only available on i386, not x86_64 diff --git a/test/cases/compile_errors/callconv_vectorcall_on_unsupported_platform.zig b/test/cases/compile_errors/callconv_vectorcall_on_unsupported_platform.zig new file mode 100644 index 0000000000..9c231a6ea8 --- /dev/null +++ b/test/cases/compile_errors/callconv_vectorcall_on_unsupported_platform.zig @@ -0,0 +1,7 @@ +export fn entry() callconv(.Vectorcall) void {} + +// error +// backend=stage2 +// target=x86_64-linux-none +// +// :1:29: error: callconv 'Vectorcall' is only available on i386 and AArch64, not x86_64 diff --git a/test/cases/compile_errors/stage1/obj/callconv_apcs_aapcs_aapcsvfp_on_unsupported_platform.zig b/test/cases/compile_errors/stage1/obj/callconv_apcs_aapcs_aapcsvfp_on_unsupported_platform.zig deleted file mode 100644 index fa06287803..0000000000 --- a/test/cases/compile_errors/stage1/obj/callconv_apcs_aapcs_aapcsvfp_on_unsupported_platform.zig +++ /dev/null @@ -1,11 +0,0 @@ -export fn entry1() callconv(.APCS) void {} -export fn entry2() callconv(.AAPCS) void {} -export fn entry3() callconv(.AAPCSVFP) void {} - -// error -// backend=stage1 -// target=x86_64-linux-none -// -// tmp.zig:1:29: error: callconv 'APCS' is only available on ARM, not x86_64 -// tmp.zig:2:29: error: callconv 'AAPCS' is only available on ARM, not x86_64 -// tmp.zig:3:29: error: callconv 'AAPCSVFP' is only available on ARM, not x86_64 diff --git a/test/cases/compile_errors/stage1/obj/callconv_interrupt_on_unsupported_platform.zig b/test/cases/compile_errors/stage1/obj/callconv_interrupt_on_unsupported_platform.zig deleted file mode 100644 index 8304fb90c1..0000000000 --- a/test/cases/compile_errors/stage1/obj/callconv_interrupt_on_unsupported_platform.zig +++ /dev/null @@ -1,7 +0,0 @@ -export fn entry() callconv(.Interrupt) void {} - -// error -// backend=stage1 -// target=aarch64-linux-none -// -// tmp.zig:1:28: error: callconv 'Interrupt' is only available on x86, x86_64, AVR, and MSP430, not aarch64 diff --git a/test/cases/compile_errors/stage1/obj/callconv_signal_on_unsupported_platform.zig b/test/cases/compile_errors/stage1/obj/callconv_signal_on_unsupported_platform.zig deleted file mode 100644 index 4b19b188fd..0000000000 --- a/test/cases/compile_errors/stage1/obj/callconv_signal_on_unsupported_platform.zig +++ /dev/null @@ -1,7 +0,0 @@ -export fn entry() callconv(.Signal) void {} - -// error -// backend=stage1 -// target=x86_64-linux-none -// -// tmp.zig:1:28: error: callconv 'Signal' is only available on AVR, not x86_64 diff --git a/test/cases/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-1.zig b/test/cases/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-1.zig deleted file mode 100644 index aa23f9ae51..0000000000 --- a/test/cases/compile_errors/stage1/obj/callconv_stdcall_fastcall_thiscall_on_unsupported_platform-1.zig +++ /dev/null @@ -1,11 +0,0 @@ -export fn entry1() callconv(.Stdcall) void {} -export fn entry2() callconv(.Fastcall) void {} -export fn entry3() callconv(.Thiscall) void {} - -// error -// backend=stage1 -// target=x86_64-linux-none -// -// tmp.zig:1:29: error: callconv 'Stdcall' is only available on x86, not x86_64 -// tmp.zig:2:29: error: callconv 'Fastcall' is only available on x86, not x86_64 -// tmp.zig:3:29: error: callconv 'Thiscall' is only available on x86, not x86_64 diff --git a/test/cases/compile_errors/stage1/obj/callconv_vectorcall_on_unsupported_platform.zig b/test/cases/compile_errors/stage1/obj/callconv_vectorcall_on_unsupported_platform.zig deleted file mode 100644 index e60e6ab42c..0000000000 --- a/test/cases/compile_errors/stage1/obj/callconv_vectorcall_on_unsupported_platform.zig +++ /dev/null @@ -1,7 +0,0 @@ -export fn entry() callconv(.Vectorcall) void {} - -// error -// backend=stage1 -// target=x86_64-linux-none -// -// tmp.zig:1:28: error: callconv 'Vectorcall' is only available on x86 and AArch64, not x86_64 From 3c73f711771e41e9176e973c64484f0ce5e0eeed Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 30 Jun 2022 16:58:46 +0300 Subject: [PATCH 2024/2031] Sema: prefer func.onwer_decl for compilelog src --- src/Sema.zig | 3 ++- .../{stage1/obj => }/compile-time_division_by_zero.zig | 4 ++-- .../obj => }/compile-time_remainder_division_by_zero.zig | 4 ++-- .../compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig | 6 +++--- test/cases/compile_errors/{stage1/obj => }/compile_log.zig | 6 ++---- ...ent_inside_function_which_must_be_comptime_evaluated.zig | 4 ++-- .../{stage1/obj => }/compile_time_division_by_zero.zig | 5 +++-- 7 files changed, 16 insertions(+), 16 deletions(-) rename test/cases/compile_errors/{stage1/obj => }/compile-time_division_by_zero.zig (58%) rename test/cases/compile_errors/{stage1/obj => }/compile-time_remainder_division_by_zero.zig (58%) rename test/cases/compile_errors/{stage1/obj => }/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig (66%) rename test/cases/compile_errors/{stage1/obj => }/compile_log.zig (54%) rename test/cases/compile_errors/{stage1/obj => }/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig (72%) rename test/cases/compile_errors/{stage1/obj => }/compile_time_division_by_zero.zig (56%) diff --git a/src/Sema.zig b/src/Sema.zig index 0d568f8b38..30cebe18b3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4272,7 +4272,8 @@ fn zirCompileLog( } try writer.print("\n", .{}); - const gop = try sema.mod.compile_log_decls.getOrPut(sema.gpa, sema.owner_decl_index); + const decl_index = if (sema.func) |some| some.owner_decl else sema.owner_decl_index; + const gop = try sema.mod.compile_log_decls.getOrPut(sema.gpa, decl_index); if (!gop.found_existing) { gop.value_ptr.* = src_node; } diff --git a/test/cases/compile_errors/stage1/obj/compile-time_division_by_zero.zig b/test/cases/compile_errors/compile-time_division_by_zero.zig similarity index 58% rename from test/cases/compile_errors/stage1/obj/compile-time_division_by_zero.zig rename to test/cases/compile_errors/compile-time_division_by_zero.zig index e16f5f8cf4..969598e500 100644 --- a/test/cases/compile_errors/stage1/obj/compile-time_division_by_zero.zig +++ b/test/cases/compile_errors/compile-time_division_by_zero.zig @@ -6,7 +6,7 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:17: error: division by zero +// :4:19: error: division by zero here causes undefined behavior diff --git a/test/cases/compile_errors/stage1/obj/compile-time_remainder_division_by_zero.zig b/test/cases/compile_errors/compile-time_remainder_division_by_zero.zig similarity index 58% rename from test/cases/compile_errors/stage1/obj/compile-time_remainder_division_by_zero.zig rename to test/cases/compile_errors/compile-time_remainder_division_by_zero.zig index 63f0def52c..301fad6a68 100644 --- a/test/cases/compile_errors/stage1/obj/compile-time_remainder_division_by_zero.zig +++ b/test/cases/compile_errors/compile-time_remainder_division_by_zero.zig @@ -6,7 +6,7 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:17: error: division by zero +// :4:19: error: division by zero here causes undefined behavior diff --git a/test/cases/compile_errors/stage1/obj/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig b/test/cases/compile_errors/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig similarity index 66% rename from test/cases/compile_errors/stage1/obj/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig rename to test/cases/compile_errors/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig index 36e1ee1118..9189eeb48d 100644 --- a/test/cases/compile_errors/stage1/obj/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig +++ b/test/cases/compile_errors/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig @@ -6,12 +6,12 @@ fn testCompileLog(x: Bar) void { @compileLog(x); } -pub fn main () void { +pub export fn entry() void { comptime testCompileLog(Bar{.X = 123}); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:6:5: error: found compile log statement +// :6:5: error: found compile log statement diff --git a/test/cases/compile_errors/stage1/obj/compile_log.zig b/test/cases/compile_errors/compile_log.zig similarity index 54% rename from test/cases/compile_errors/stage1/obj/compile_log.zig rename to test/cases/compile_errors/compile_log.zig index 7f5d522407..281f8747d2 100644 --- a/test/cases/compile_errors/stage1/obj/compile_log.zig +++ b/test/cases/compile_errors/compile_log.zig @@ -8,9 +8,7 @@ fn bar(a: i32, b: []const u8) void { } // error -// backend=stage1 +// backend=llvm // target=native // -// tmp.zig:5:5: error: found compile log statement -// tmp.zig:6:5: error: found compile log statement -// tmp.zig:7:5: error: found compile log statement +// :5:5: error: found compile log statement diff --git a/test/cases/compile_errors/stage1/obj/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig b/test/cases/compile_errors/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig similarity index 72% rename from test/cases/compile_errors/stage1/obj/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig rename to test/cases/compile_errors/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig index 19b96048cb..0bc45eae0a 100644 --- a/test/cases/compile_errors/stage1/obj/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig +++ b/test/cases/compile_errors/compile_log_statement_inside_function_which_must_be_comptime_evaluated.zig @@ -8,7 +8,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:5: error: found compile log statement +// :2:5: error: found compile log statement diff --git a/test/cases/compile_errors/stage1/obj/compile_time_division_by_zero.zig b/test/cases/compile_errors/compile_time_division_by_zero.zig similarity index 56% rename from test/cases/compile_errors/stage1/obj/compile_time_division_by_zero.zig rename to test/cases/compile_errors/compile_time_division_by_zero.zig index 7f7e168f02..281ccf28a1 100644 --- a/test/cases/compile_errors/stage1/obj/compile_time_division_by_zero.zig +++ b/test/cases/compile_errors/compile_time_division_by_zero.zig @@ -6,7 +6,8 @@ fn foo(x: u32) u32 { export fn entry() usize { return @sizeOf(@TypeOf(y)); } // error -// backend=stage1 +// backend=llvm // target=native // -// tmp.zig:3:14: error: division by zero +// :3:16: error: division by zero here causes undefined behavior +// :1:14: note: called from here From ae7b32eb62cb00a09fe2e0e30b307eb83e9f0a86 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 30 Jun 2022 17:22:16 +0300 Subject: [PATCH 2025/2031] Sema: validate deref operator type and value --- lib/c.zig | 2 +- lib/std/os.zig | 2 +- lib/std/process.zig | 2 +- src/AstGen.zig | 2 ++ src/Sema.zig | 27 +++++++++++++++++++ src/Zir.zig | 6 +++++ src/print_zir.zig | 1 + .../assign_to_invalid_dereference.zig | 9 +++++++ .../{stage1 => }/deref_on_undefined_value.zig | 4 +-- .../deref_slice_and_get_len_field.zig | 4 +-- .../{stage1/obj => }/dereference_an_array.zig | 6 ++--- .../compile_errors/dereference_slice.zig | 12 +++++++++ .../dereference_unknown_length_pointer.zig | 9 +++++++ .../invalid_deref_on_switch_target.zig | 4 +-- .../invalid_multiple_dereferences.zig | 6 ++--- .../comptime_ptrcast_of_zero-sized_type.zig | 0 .../obj/assign_to_invalid_dereference.zig | 9 ------- .../dereference_unknown_length_pointer.zig | 9 ------- .../obj/take_slice_of_invalid_dereference.zig | 10 ------- .../take_slice_of_invalid_dereference.zig | 10 +++++++ 20 files changed, 91 insertions(+), 43 deletions(-) create mode 100644 test/cases/compile_errors/assign_to_invalid_dereference.zig rename test/cases/compile_errors/{stage1 => }/deref_on_undefined_value.zig (51%) rename test/cases/compile_errors/{stage1/obj => }/deref_slice_and_get_len_field.zig (53%) rename test/cases/compile_errors/{stage1/obj => }/dereference_an_array.zig (52%) create mode 100644 test/cases/compile_errors/dereference_slice.zig create mode 100644 test/cases/compile_errors/dereference_unknown_length_pointer.zig rename test/cases/compile_errors/{stage1/obj => }/invalid_deref_on_switch_target.zig (68%) rename test/cases/compile_errors/{stage1/obj => }/invalid_multiple_dereferences.zig (61%) rename test/cases/compile_errors/stage1/{obj => }/comptime_ptrcast_of_zero-sized_type.zig (100%) delete mode 100644 test/cases/compile_errors/stage1/obj/assign_to_invalid_dereference.zig delete mode 100644 test/cases/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig delete mode 100644 test/cases/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig create mode 100644 test/cases/compile_errors/take_slice_of_invalid_dereference.zig diff --git a/lib/c.zig b/lib/c.zig index 30c6e0cd76..9df1b3fb01 100644 --- a/lib/c.zig +++ b/lib/c.zig @@ -82,7 +82,7 @@ fn memset(dest: ?[*]u8, c: u8, len: usize) callconv(.C) ?[*]u8 { var d = dest.?; var n = len; while (true) { - d.* = c; + d[0] = c; n -= 1; if (n == 0) break; d += 1; diff --git a/lib/std/os.zig b/lib/std/os.zig index 578a8ddcbc..02ed710dd3 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1868,7 +1868,7 @@ pub fn getenv(key: []const u8) ?[]const u8 { } // Search the entire `environ` because we don't have a null terminated pointer. var ptr = std.c.environ; - while (ptr.*) |line| : (ptr += 1) { + while (ptr[0]) |line| : (ptr += 1) { var line_i: usize = 0; while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} const this_key = line[0..line_i]; diff --git a/lib/std/process.zig b/lib/std/process.zig index 0b64b5910d..dffd6b1701 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -313,7 +313,7 @@ pub fn getEnvMap(allocator: Allocator) !EnvMap { return result; } else if (builtin.link_libc) { var ptr = std.c.environ; - while (ptr.*) |line| : (ptr += 1) { + while (ptr[0]) |line| : (ptr += 1) { var line_i: usize = 0; while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} const key = line[0..line_i]; diff --git a/src/AstGen.zig b/src/AstGen.zig index c9abb1859b..8707728313 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -812,6 +812,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr .deref => { const lhs = try expr(gz, scope, .none, node_datas[node].lhs); + _ = try gz.addUnTok(.validate_deref, lhs, main_tokens[node]); switch (rl) { .ref => return lhs, else => { @@ -2500,6 +2501,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .memset, .validate_array_init_ty, .validate_struct_init_ty, + .validate_deref, => break :b true, } } else switch (maybe_unused_result) { diff --git a/src/Sema.zig b/src/Sema.zig index 30cebe18b3..0e7188a6c9 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1080,6 +1080,11 @@ fn analyzeBodyInner( i += 1; continue; }, + .validate_deref => { + try sema.zirValidateDeref(block, inst); + i += 1; + continue; + }, .@"export" => { try sema.zirExport(block, inst); i += 1; @@ -3849,6 +3854,28 @@ fn zirValidateArrayInit( } } +fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { + const inst_data = sema.code.instructions.items(.data)[inst].un_tok; + const src = inst_data.src(); + const operand_src: LazySrcLoc = .{ .token_offset = inst_data.src_tok + 1 }; + const operand = try sema.resolveInst(inst_data.operand); + const operand_ty = sema.typeOf(operand); + + if (operand_ty.zigTypeTag() != .Pointer) { + return sema.fail(block, src, "cannot dereference non-pointer type '{}'", .{operand_ty.fmt(sema.mod)}); + } else switch (operand_ty.ptrSize()) { + .One, .C => {}, + .Many => return sema.fail(block, src, "index syntax required for unknown-length pointer type '{}'", .{operand_ty.fmt(sema.mod)}), + .Slice => return sema.fail(block, src, "index syntax required for slice type '{}'", .{operand_ty.fmt(sema.mod)}), + } + + if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { + if (val.isUndef()) { + return sema.fail(block, src, "cannot dereference undefined value", .{}); + } + } +} + fn failWithBadMemberAccess( sema: *Sema, block: *Block, diff --git a/src/Zir.zig b/src/Zir.zig index e6acfe8ed2..26bb09860d 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -729,6 +729,9 @@ pub const Inst = struct { /// Same as `validate_array_init` but additionally communicates that the /// resulting array initialization value is within a comptime scope. validate_array_init_comptime, + /// Check that operand type supports the dereference operand (.*). + /// Uses the `un_tok` field. + validate_deref, /// A struct literal with a specified type, with no fields. /// Uses the `un_node` field. struct_init_empty, @@ -1156,6 +1159,7 @@ pub const Inst = struct { .validate_struct_init_comptime, .validate_array_init, .validate_array_init_comptime, + .validate_deref, .struct_init_empty, .struct_init, .struct_init_ref, @@ -1309,6 +1313,7 @@ pub const Inst = struct { .validate_struct_init_comptime, .validate_array_init, .validate_array_init_comptime, + .validate_deref, .@"export", .export_value, .set_cold, @@ -1709,6 +1714,7 @@ pub const Inst = struct { .validate_struct_init_comptime = .pl_node, .validate_array_init = .pl_node, .validate_array_init_comptime = .pl_node, + .validate_deref = .un_tok, .struct_init_empty = .un_node, .field_type = .pl_node, .field_type_ref = .pl_node, diff --git a/src/print_zir.zig b/src/print_zir.zig index 480a3e2a4f..fe8446b34a 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -242,6 +242,7 @@ const Writer = struct { .ret_tok, .ensure_err_payload_void, .closure_capture, + .validate_deref, => try self.writeUnTok(stream, inst), .bool_br_and, diff --git a/test/cases/compile_errors/assign_to_invalid_dereference.zig b/test/cases/compile_errors/assign_to_invalid_dereference.zig new file mode 100644 index 0000000000..cb35004034 --- /dev/null +++ b/test/cases/compile_errors/assign_to_invalid_dereference.zig @@ -0,0 +1,9 @@ +export fn entry() void { + 'a'.* = 1; +} + +// error +// backend=stage2 +// target=native +// +// :2:8: error: cannot dereference non-pointer type 'comptime_int' diff --git a/test/cases/compile_errors/stage1/deref_on_undefined_value.zig b/test/cases/compile_errors/deref_on_undefined_value.zig similarity index 51% rename from test/cases/compile_errors/stage1/deref_on_undefined_value.zig rename to test/cases/compile_errors/deref_on_undefined_value.zig index f64d567a26..fa12e2824c 100644 --- a/test/cases/compile_errors/stage1/deref_on_undefined_value.zig +++ b/test/cases/compile_errors/deref_on_undefined_value.zig @@ -4,7 +4,7 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:9: error: attempt to dereference undefined value +// :3:10: error: cannot dereference undefined value diff --git a/test/cases/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig b/test/cases/compile_errors/deref_slice_and_get_len_field.zig similarity index 53% rename from test/cases/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig rename to test/cases/compile_errors/deref_slice_and_get_len_field.zig index 98097597cc..1ba03c6d50 100644 --- a/test/cases/compile_errors/stage1/obj/deref_slice_and_get_len_field.zig +++ b/test/cases/compile_errors/deref_slice_and_get_len_field.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:10: error: attempt to dereference non-pointer type '[]u8' +// :3:10: error: index syntax required for slice type '[]u8' diff --git a/test/cases/compile_errors/stage1/obj/dereference_an_array.zig b/test/cases/compile_errors/dereference_an_array.zig similarity index 52% rename from test/cases/compile_errors/stage1/obj/dereference_an_array.zig rename to test/cases/compile_errors/dereference_an_array.zig index 0dd91f70e5..f5aabf081c 100644 --- a/test/cases/compile_errors/stage1/obj/dereference_an_array.zig +++ b/test/cases/compile_errors/dereference_an_array.zig @@ -5,10 +5,10 @@ pub fn pass(in: []u8) []u8 { return out.*[0..1]; } -export fn entry() usize { return @sizeOf(@TypeOf(pass)); } +export fn entry() usize { return @sizeOf(@TypeOf(&pass)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:10: error: attempt to dereference non-pointer type '[10]u8' +// :4:10: error: cannot dereference non-pointer type '[10]u8' diff --git a/test/cases/compile_errors/dereference_slice.zig b/test/cases/compile_errors/dereference_slice.zig new file mode 100644 index 0000000000..7dba3b55d8 --- /dev/null +++ b/test/cases/compile_errors/dereference_slice.zig @@ -0,0 +1,12 @@ +fn entry(x: []i32) i32 { + return x.*; +} +comptime { + _ = entry; +} + +// error +// backend=stage2 +// target=native +// +// :2:13: error: index syntax required for slice type '[]i32' diff --git a/test/cases/compile_errors/dereference_unknown_length_pointer.zig b/test/cases/compile_errors/dereference_unknown_length_pointer.zig new file mode 100644 index 0000000000..353f94b8d9 --- /dev/null +++ b/test/cases/compile_errors/dereference_unknown_length_pointer.zig @@ -0,0 +1,9 @@ +export fn entry(x: [*]i32) i32 { + return x.*; +} + +// error +// backend=stage2 +// target=native +// +// :2:13: error: index syntax required for unknown-length pointer type '[*]i32' diff --git a/test/cases/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig b/test/cases/compile_errors/invalid_deref_on_switch_target.zig similarity index 68% rename from test/cases/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig rename to test/cases/compile_errors/invalid_deref_on_switch_target.zig index 966a881543..a880b16fca 100644 --- a/test/cases/compile_errors/stage1/obj/invalid_deref_on_switch_target.zig +++ b/test/cases/compile_errors/invalid_deref_on_switch_target.zig @@ -11,7 +11,7 @@ const Tile = enum { }; // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:17: error: attempt to dereference non-pointer type 'Tile' +// :3:17: error: cannot dereference non-pointer type 'tmp.Tile' diff --git a/test/cases/compile_errors/stage1/obj/invalid_multiple_dereferences.zig b/test/cases/compile_errors/invalid_multiple_dereferences.zig similarity index 61% rename from test/cases/compile_errors/stage1/obj/invalid_multiple_dereferences.zig rename to test/cases/compile_errors/invalid_multiple_dereferences.zig index f8a0b8013f..3edebf7b1f 100644 --- a/test/cases/compile_errors/stage1/obj/invalid_multiple_dereferences.zig +++ b/test/cases/compile_errors/invalid_multiple_dereferences.zig @@ -12,8 +12,8 @@ pub const Box = struct { }; // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:8: error: attempt to dereference non-pointer type 'Box' -// tmp.zig:8:13: error: attempt to dereference non-pointer type 'Box' +// :3:8: error: cannot dereference non-pointer type 'tmp.Box' +// :8:13: error: cannot dereference non-pointer type 'tmp.Box' diff --git a/test/cases/compile_errors/stage1/obj/comptime_ptrcast_of_zero-sized_type.zig b/test/cases/compile_errors/stage1/comptime_ptrcast_of_zero-sized_type.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/comptime_ptrcast_of_zero-sized_type.zig rename to test/cases/compile_errors/stage1/comptime_ptrcast_of_zero-sized_type.zig diff --git a/test/cases/compile_errors/stage1/obj/assign_to_invalid_dereference.zig b/test/cases/compile_errors/stage1/obj/assign_to_invalid_dereference.zig deleted file mode 100644 index 7fef5db83c..0000000000 --- a/test/cases/compile_errors/stage1/obj/assign_to_invalid_dereference.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn entry() void { - 'a'.* = 1; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:8: error: attempt to dereference non-pointer type 'comptime_int' diff --git a/test/cases/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig b/test/cases/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig deleted file mode 100644 index c305e4bc98..0000000000 --- a/test/cases/compile_errors/stage1/obj/dereference_unknown_length_pointer.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn entry(x: [*]i32) i32 { - return x.*; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:13: error: index syntax required for unknown-length pointer type '[*]i32' diff --git a/test/cases/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig b/test/cases/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig deleted file mode 100644 index c039be3737..0000000000 --- a/test/cases/compile_errors/stage1/obj/take_slice_of_invalid_dereference.zig +++ /dev/null @@ -1,10 +0,0 @@ -export fn entry() void { - const x = 'a'.*[0..]; - _ = x; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:18: error: attempt to dereference non-pointer type 'comptime_int' diff --git a/test/cases/compile_errors/take_slice_of_invalid_dereference.zig b/test/cases/compile_errors/take_slice_of_invalid_dereference.zig new file mode 100644 index 0000000000..35c1b2de0d --- /dev/null +++ b/test/cases/compile_errors/take_slice_of_invalid_dereference.zig @@ -0,0 +1,10 @@ +export fn entry() void { + const x = 'a'.*[0..]; + _ = x; +} + +// error +// backend=stage2 +// target=native +// +// :2:18: error: cannot dereference non-pointer type 'comptime_int' From e6ebf56dd6cf2e2c23af952d2e9e327703c9cd02 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 30 Jun 2022 17:38:04 +0300 Subject: [PATCH 2026/2031] Sema: validate `@intToEnum` int operand type --- src/Sema.zig | 7 ++++--- .../{stage1/obj => }/discarding_error_value.zig | 4 ++-- .../{stage1/obj => }/duplicate_enum_field.zig | 6 +++--- .../{stage1/obj => }/duplicate_error_in_switch.zig | 6 +++--- .../obj => }/explicitly_casting_non_tag_type_to_enum.zig | 4 ++-- test/stage2/cbe.zig | 4 ++-- 6 files changed, 16 insertions(+), 15 deletions(-) rename test/cases/compile_errors/{stage1/obj => }/discarding_error_value.zig (54%) rename test/cases/compile_errors/{stage1/obj => }/duplicate_enum_field.zig (56%) rename test/cases/compile_errors/{stage1/obj => }/duplicate_error_in_switch.zig (65%) rename test/cases/compile_errors/{stage1/obj => }/explicitly_casting_non_tag_type_to_enum.zig (71%) diff --git a/src/Sema.zig b/src/Sema.zig index 0e7188a6c9..64db6a9bed 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2439,9 +2439,9 @@ fn zirEnumDecl( const field_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset.x, field_i); const other_tag_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset.x, gop.index); const msg = msg: { - const msg = try sema.errMsg(block, field_src, "duplicate enum tag", .{}); + const msg = try sema.errMsg(block, field_src, "duplicate enum field '{s}'", .{field_name}); errdefer msg.destroy(gpa); - try sema.errNote(block, other_tag_src, msg, "other tag here", .{}); + try sema.errNote(block, other_tag_src, msg, "other field here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(block, msg); @@ -2751,7 +2751,7 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com const src = inst_data.src(); const operand_ty = sema.typeOf(operand); switch (operand_ty.zigTypeTag()) { - .ErrorSet, .ErrorUnion => return sema.fail(block, src, "error is discarded", .{}), + .ErrorSet, .ErrorUnion => return sema.fail(block, src, "error is discardederror is discarded. consider using `try`, `catch`, or `if`", .{}), else => return, } } @@ -6444,6 +6444,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A if (dest_ty.zigTypeTag() != .Enum) { return sema.fail(block, dest_ty_src, "expected enum, found '{}'", .{dest_ty.fmt(sema.mod)}); } + _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand)); if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |int_val| { if (dest_ty.isNonexhaustiveEnum()) { diff --git a/test/cases/compile_errors/stage1/obj/discarding_error_value.zig b/test/cases/compile_errors/discarding_error_value.zig similarity index 54% rename from test/cases/compile_errors/stage1/obj/discarding_error_value.zig rename to test/cases/compile_errors/discarding_error_value.zig index dcfa22e8cb..f74fc4ea29 100644 --- a/test/cases/compile_errors/stage1/obj/discarding_error_value.zig +++ b/test/cases/compile_errors/discarding_error_value.zig @@ -6,7 +6,7 @@ fn foo() !void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:12: error: error is discarded. consider using `try`, `catch`, or `if` +// :2:12: error: error is discarded. consider using `try`, `catch`, or `if` diff --git a/test/cases/compile_errors/stage1/obj/duplicate_enum_field.zig b/test/cases/compile_errors/duplicate_enum_field.zig similarity index 56% rename from test/cases/compile_errors/stage1/obj/duplicate_enum_field.zig rename to test/cases/compile_errors/duplicate_enum_field.zig index cd024270bd..ebf01f99a7 100644 --- a/test/cases/compile_errors/stage1/obj/duplicate_enum_field.zig +++ b/test/cases/compile_errors/duplicate_enum_field.zig @@ -9,8 +9,8 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:5: error: duplicate enum field: 'Bar' -// tmp.zig:2:5: note: other field here +// :3:5: error: duplicate enum field 'Bar' +// :2:5: note: other field here diff --git a/test/cases/compile_errors/stage1/obj/duplicate_error_in_switch.zig b/test/cases/compile_errors/duplicate_error_in_switch.zig similarity index 65% rename from test/cases/compile_errors/stage1/obj/duplicate_error_in_switch.zig rename to test/cases/compile_errors/duplicate_error_in_switch.zig index 140a14ec81..6e9b1e9099 100644 --- a/test/cases/compile_errors/stage1/obj/duplicate_error_in_switch.zig +++ b/test/cases/compile_errors/duplicate_error_in_switch.zig @@ -15,8 +15,8 @@ fn foo(x: i32) !void { } // error -// backend=stage1 +// backend=llvm // target=native // -// tmp.zig:5:14: error: duplicate switch value: '@typeInfo(@typeInfo(@TypeOf(foo)).Fn.return_type.?).ErrorUnion.error_set.Foo' -// tmp.zig:3:14: note: other value here +// :5:9: error: duplicate switch value +// :3:9: note: other value here diff --git a/test/cases/compile_errors/stage1/obj/explicitly_casting_non_tag_type_to_enum.zig b/test/cases/compile_errors/explicitly_casting_non_tag_type_to_enum.zig similarity index 71% rename from test/cases/compile_errors/stage1/obj/explicitly_casting_non_tag_type_to_enum.zig rename to test/cases/compile_errors/explicitly_casting_non_tag_type_to_enum.zig index 1999fd70a7..aac876e614 100644 --- a/test/cases/compile_errors/stage1/obj/explicitly_casting_non_tag_type_to_enum.zig +++ b/test/cases/compile_errors/explicitly_casting_non_tag_type_to_enum.zig @@ -12,7 +12,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:10:31: error: expected integer type, found 'f32' +// :10:31: error: expected integer type, found 'f32' diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index f602c49885..4817f0502f 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -729,8 +729,8 @@ pub fn addCases(ctx: *TestContext) !void { \\ _ = E1.a; \\} , &.{ - ":1:28: error: duplicate enum tag", - ":1:22: note: other tag here", + ":1:28: error: duplicate enum field 'b'", + ":1:22: note: other field here", }); case.addError( From a6bf8c2593ae6e60d4c4804d4e9fd87fe29885ed Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 30 Jun 2022 22:57:20 +0300 Subject: [PATCH 2027/2031] Sema: add more validation to zirFieldParentPtr --- src/Sema.zig | 52 ++++++++++++++----- .../{stage1/obj => }/division_by_zero.zig | 11 ++-- .../obj => }/error_not_handled_in_switch.zig | 7 +-- ...comptime_field_ptr_not_based_on_struct.zig | 4 +- ...ldParentPtr-comptime_wrong_field_index.zig | 5 +- ...ParentPtr-field_pointer_is_not_pointer.zig | 4 +- .../obj => }/field_access_of_opaque_type.zig | 4 +- .../obj => }/field_access_of_slices.zig | 5 +- ...field_access_of_unknown_length_pointer.zig | 4 +- ...uality_but_sets_have_no_common_members.zig | 0 10 files changed, 61 insertions(+), 35 deletions(-) rename test/cases/compile_errors/{stage1/obj => }/division_by_zero.zig (55%) rename test/cases/compile_errors/{stage1/obj => }/error_not_handled_in_switch.zig (64%) rename test/cases/compile_errors/{stage1/obj => }/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig (76%) rename test/cases/compile_errors/{stage1/obj => }/fieldParentPtr-comptime_wrong_field_index.zig (59%) rename test/cases/compile_errors/{stage1/obj => }/fieldParentPtr-field_pointer_is_not_pointer.zig (66%) rename test/cases/compile_errors/{stage1/obj => }/field_access_of_opaque_type.zig (68%) rename test/cases/compile_errors/{stage1/obj => }/field_access_of_slices.zig (54%) rename test/cases/compile_errors/{stage1/obj => }/field_access_of_unknown_length_pointer.zig (60%) rename test/cases/compile_errors/stage1/{obj => }/error_equality_but_sets_have_no_common_members.zig (100%) diff --git a/src/Sema.zig b/src/Sema.zig index 64db6a9bed..5010d86c9a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -8378,19 +8378,15 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError block, src, msg, - "unhandled error value: error.{s}", + "unhandled error value: 'error.{s}'", .{error_name}, ); } } if (maybe_msg) |msg| { - try sema.mod.errNoteNonLazy( - operand_ty.declSrcLoc(sema.mod), - msg, - "error set '{}' declared here", - .{operand_ty.fmt(sema.mod)}, - ); + maybe_msg = null; + try sema.addDeclaredHereNote(msg, operand_ty); return sema.failWithOwnedErrorMsg(block, msg); } @@ -17143,9 +17139,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const field_index = struct_obj.fields.getIndex(field_name) orelse return sema.failWithBadStructFieldAccess(block, struct_obj, name_src, field_name); - if (field_ptr_ty.zigTypeTag() != .Pointer) { - return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{field_ptr_ty.fmt(sema.mod)}); - } + try sema.checkPtrOperand(block, ptr_src, field_ptr_ty); const field = struct_obj.fields.values()[field_index]; const field_ptr_ty_info = field_ptr_ty.ptrInfo().data; @@ -17168,8 +17162,29 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const result_ptr = try Type.ptr(sema.arena, sema.mod, ptr_ty_data); if (try sema.resolveDefinedValue(block, src, casted_field_ptr)) |field_ptr_val| { - const payload = field_ptr_val.castTag(.field_ptr).?.data; - return sema.addConstant(result_ptr, payload.container_ptr); + const payload = field_ptr_val.castTag(.field_ptr) orelse { + return sema.fail(block, ptr_src, "pointer value not based on parent struct", .{}); + }; + if (payload.data.field_index != field_index) { + const msg = msg: { + const msg = try sema.errMsg( + block, + src, + "field '{s}' has index '{d}' but pointer value is index '{d}' of struct '{}'", + .{ + field_name, + field_index, + payload.data.field_index, + struct_ty.fmt(sema.mod), + }, + ); + errdefer msg.destroy(sema.gpa); + try sema.addDeclaredHereNote(msg, struct_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + return sema.addConstant(result_ptr, payload.data.container_ptr); } try sema.requireRuntimeBlock(block, src); @@ -18515,7 +18530,16 @@ fn fieldVal( kw_name, child_type.fmt(sema.mod), field_name, }); }, - else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(sema.mod)}), + else => { + const msg = msg: { + const msg = try sema.errMsg(block, src, "type '{}' has no members", .{child_type.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + if (child_type.isSlice()) try sema.errNote(block, src, msg, "slice values have 'len' and 'ptr' members", .{}); + if (child_type.zigTypeTag() == .Array) try sema.errNote(block, src, msg, "array values have 'len' member", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + }, } }, .Struct => if (is_pointer_to) { @@ -18739,7 +18763,7 @@ fn fieldPtr( }, else => {}, } - return sema.fail(block, src, "type '{}' does not support field access (fieldPtr, {}.{s})", .{ object_ty.fmt(sema.mod), object_ptr_ty.fmt(sema.mod), field_name }); + return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(sema.mod)}); } fn fieldCallBind( diff --git a/test/cases/compile_errors/stage1/obj/division_by_zero.zig b/test/cases/compile_errors/division_by_zero.zig similarity index 55% rename from test/cases/compile_errors/stage1/obj/division_by_zero.zig rename to test/cases/compile_errors/division_by_zero.zig index 3023f0c6f3..2e2f7e2be2 100644 --- a/test/cases/compile_errors/stage1/obj/division_by_zero.zig +++ b/test/cases/compile_errors/division_by_zero.zig @@ -6,13 +6,12 @@ const float_x = @as(f32, 1.0) / @as(f32, 0.0); export fn entry1() usize { return @sizeOf(@TypeOf(lit_int_x)); } export fn entry2() usize { return @sizeOf(@TypeOf(lit_float_x)); } export fn entry3() usize { return @sizeOf(@TypeOf(int_x)); } -export fn entry4() usize { return @sizeOf(@TypeOf(float_x)); } +export fn entry4() usize { return @sizeOf(@TypeOf(float_x)); } // no error on purpose // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:21: error: division by zero -// tmp.zig:2:25: error: division by zero -// tmp.zig:3:27: error: division by zero -// tmp.zig:4:31: error: division by zero +// :1:23: error: division by zero here causes undefined behavior +// :2:27: error: division by zero here causes undefined behavior +// :3:29: error: division by zero here causes undefined behavior diff --git a/test/cases/compile_errors/stage1/obj/error_not_handled_in_switch.zig b/test/cases/compile_errors/error_not_handled_in_switch.zig similarity index 64% rename from test/cases/compile_errors/stage1/obj/error_not_handled_in_switch.zig rename to test/cases/compile_errors/error_not_handled_in_switch.zig index 12ee35daef..8f0d26a4a9 100644 --- a/test/cases/compile_errors/stage1/obj/error_not_handled_in_switch.zig +++ b/test/cases/compile_errors/error_not_handled_in_switch.zig @@ -13,8 +13,9 @@ fn foo(x: i32) !void { } // error -// backend=stage1 +// backend=llvm // target=native // -// tmp.zig:2:26: error: error.Baz not handled in switch -// tmp.zig:2:26: error: error.Bar not handled in switch +// :2:26: error: switch must handle all possibilities +// :2:26: note: unhandled error value: 'error.Bar' +// :2:26: note: unhandled error value: 'error.Baz' diff --git a/test/cases/compile_errors/stage1/obj/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig b/test/cases/compile_errors/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig similarity index 76% rename from test/cases/compile_errors/stage1/obj/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig rename to test/cases/compile_errors/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig index 9375f4639a..7f57268f06 100644 --- a/test/cases/compile_errors/stage1/obj/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig +++ b/test/cases/compile_errors/fieldParentPtr-comptime_field_ptr_not_based_on_struct.zig @@ -11,7 +11,7 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:9:55: error: pointer value not based on parent struct +// :9:55: error: pointer value not based on parent struct diff --git a/test/cases/compile_errors/stage1/obj/fieldParentPtr-comptime_wrong_field_index.zig b/test/cases/compile_errors/fieldParentPtr-comptime_wrong_field_index.zig similarity index 59% rename from test/cases/compile_errors/stage1/obj/fieldParentPtr-comptime_wrong_field_index.zig rename to test/cases/compile_errors/fieldParentPtr-comptime_wrong_field_index.zig index c322543dc0..a73409aea3 100644 --- a/test/cases/compile_errors/stage1/obj/fieldParentPtr-comptime_wrong_field_index.zig +++ b/test/cases/compile_errors/fieldParentPtr-comptime_wrong_field_index.zig @@ -10,7 +10,8 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:8:29: error: field 'b' has index 1 but pointer value is index 0 of struct 'Foo' +// :8:29: error: field 'b' has index '1' but pointer value is index '0' of struct 'tmp.Foo' +// :1:13: note: struct declared here diff --git a/test/cases/compile_errors/stage1/obj/fieldParentPtr-field_pointer_is_not_pointer.zig b/test/cases/compile_errors/fieldParentPtr-field_pointer_is_not_pointer.zig similarity index 66% rename from test/cases/compile_errors/stage1/obj/fieldParentPtr-field_pointer_is_not_pointer.zig rename to test/cases/compile_errors/fieldParentPtr-field_pointer_is_not_pointer.zig index 71360e5681..8a57d08c3b 100644 --- a/test/cases/compile_errors/stage1/obj/fieldParentPtr-field_pointer_is_not_pointer.zig +++ b/test/cases/compile_errors/fieldParentPtr-field_pointer_is_not_pointer.zig @@ -6,7 +6,7 @@ export fn foo(a: i32) *Foo { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:5:38: error: expected pointer, found 'i32' +// :5:38: error: expected pointer type, found 'i32' diff --git a/test/cases/compile_errors/stage1/obj/field_access_of_opaque_type.zig b/test/cases/compile_errors/field_access_of_opaque_type.zig similarity index 68% rename from test/cases/compile_errors/stage1/obj/field_access_of_opaque_type.zig rename to test/cases/compile_errors/field_access_of_opaque_type.zig index 963c89dafe..f9ec483305 100644 --- a/test/cases/compile_errors/stage1/obj/field_access_of_opaque_type.zig +++ b/test/cases/compile_errors/field_access_of_opaque_type.zig @@ -10,7 +10,7 @@ fn bar(x: *MyType) bool { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:9:13: error: no member named 'blah' in opaque type 'MyType' +// :9:13: error: type '*tmp.MyType' does not support field access diff --git a/test/cases/compile_errors/stage1/obj/field_access_of_slices.zig b/test/cases/compile_errors/field_access_of_slices.zig similarity index 54% rename from test/cases/compile_errors/stage1/obj/field_access_of_slices.zig rename to test/cases/compile_errors/field_access_of_slices.zig index 45ca711367..1fbfda9646 100644 --- a/test/cases/compile_errors/stage1/obj/field_access_of_slices.zig +++ b/test/cases/compile_errors/field_access_of_slices.zig @@ -5,7 +5,8 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:32: error: type 'type' does not support field access +// :3:32: error: type '[]i32' has no members +// :3:32: note: slice values have 'len' and 'ptr' members diff --git a/test/cases/compile_errors/stage1/obj/field_access_of_unknown_length_pointer.zig b/test/cases/compile_errors/field_access_of_unknown_length_pointer.zig similarity index 60% rename from test/cases/compile_errors/stage1/obj/field_access_of_unknown_length_pointer.zig rename to test/cases/compile_errors/field_access_of_unknown_length_pointer.zig index f9a37cabcc..2f526277fe 100644 --- a/test/cases/compile_errors/stage1/obj/field_access_of_unknown_length_pointer.zig +++ b/test/cases/compile_errors/field_access_of_unknown_length_pointer.zig @@ -7,7 +7,7 @@ export fn entry(foo: [*]Foo) void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:6:8: error: type '[*]Foo' does not support field access +// :6:8: error: type '[*]tmp.Foo' does not support field access diff --git a/test/cases/compile_errors/stage1/obj/error_equality_but_sets_have_no_common_members.zig b/test/cases/compile_errors/stage1/error_equality_but_sets_have_no_common_members.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/error_equality_but_sets_have_no_common_members.zig rename to test/cases/compile_errors/stage1/error_equality_but_sets_have_no_common_members.zig From 2029601cb2ad4a6e9c8b260eec68de881d46735b Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 30 Jun 2022 23:59:39 +0300 Subject: [PATCH 2028/2031] AstGen: use elem_{ptr,val}_node for array access syntax --- src/AstGen.zig | 25 ++++++------ src/Sema.zig | 39 ++++++++++--------- src/Zir.zig | 19 +++++---- src/print_zir.zig | 4 +- src/type.zig | 2 +- .../ignored_deferred_function_call.zig | 4 +- ...nored_expression_in_while_continuation.zig | 8 ++-- .../illegal_comparison_of_types.zig | 20 ++++++++++ .../implicitly_casting_enum_to_tag_type.zig | 4 +- .../obj => }/incorrect_return_type.zig | 4 +- .../indexing_an_array_of_size_zero.zig | 4 +- ..._array_of_size_zero_with_runtime_index.zig | 4 +- .../obj => }/indexing_single-item_pointer.zig | 4 +- ...nvalid_cast_from_integral_type_to_enum.zig | 4 +- .../runtime_indexing_comptime_array.zig | 12 +++--- .../implicit_cast_from_f64_to_f32.zig | 0 .../stage1/{obj => }/int_to_ptr_of_0_bits.zig | 0 .../obj/illegal_comparison_of_types.zig | 20 ---------- 18 files changed, 90 insertions(+), 87 deletions(-) rename test/cases/compile_errors/{stage1/obj => }/ignored_deferred_function_call.zig (52%) rename test/cases/compile_errors/{stage1/obj => }/ignored_expression_in_while_continuation.zig (54%) create mode 100644 test/cases/compile_errors/illegal_comparison_of_types.zig rename test/cases/compile_errors/{stage1/obj => }/implicitly_casting_enum_to_tag_type.zig (68%) rename test/cases/compile_errors/{stage1/obj => }/incorrect_return_type.zig (75%) rename test/cases/compile_errors/{stage1/obj => }/indexing_an_array_of_size_zero.zig (59%) rename test/cases/compile_errors/{stage1/obj => }/indexing_an_array_of_size_zero_with_runtime_index.zig (64%) rename test/cases/compile_errors/{stage1/obj => }/indexing_single-item_pointer.zig (51%) rename test/cases/compile_errors/{stage1/obj => }/invalid_cast_from_integral_type_to_enum.zig (70%) rename test/cases/compile_errors/stage1/{obj => }/implicit_cast_from_f64_to_f32.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/int_to_ptr_of_0_bits.zig (100%) delete mode 100644 test/cases/compile_errors/stage1/obj/illegal_comparison_of_types.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index 8707728313..a0ff7a0e6e 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -5154,16 +5154,14 @@ fn arrayAccess( const tree = astgen.tree; const node_datas = tree.nodes.items(.data); switch (rl) { - .ref => return gz.addBin( - .elem_ptr, - try expr(gz, scope, .ref, node_datas[node].lhs), - try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs), - ), - else => return rvalue(gz, rl, try gz.addBin( - .elem_val, - try expr(gz, scope, .none, node_datas[node].lhs), - try expr(gz, scope, .{ .coerced_ty = .usize_type }, node_datas[node].rhs), - ), node), + .ref => return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ + .lhs = try expr(gz, scope, .ref, node_datas[node].lhs), + .rhs = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs), + }), + else => return rvalue(gz, rl, try gz.addPlNode(.elem_val_node, node, Zir.Inst.Bin{ + .lhs = try expr(gz, scope, .none, node_datas[node].lhs), + .rhs = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs), + }), node), } } @@ -5685,7 +5683,7 @@ fn whileExpr( try then_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst); } if (while_full.ast.cont_expr != 0) { - _ = try expr(&loop_scope, then_sub_scope, .{ .ty = .void_type }, while_full.ast.cont_expr); + _ = try unusedResultExpr(&loop_scope, then_sub_scope, while_full.ast.cont_expr); } try then_scope.addDbgBlockEnd(); const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat; @@ -5890,7 +5888,10 @@ fn forExpr( if (!mem.eql(u8, value_name, "_")) { const name_str_index = try astgen.identAsString(ident); const tag: Zir.Inst.Tag = if (is_ptr) .elem_ptr else .elem_val; - const payload_inst = try then_scope.addBin(tag, array_ptr, index); + const payload_inst = try then_scope.addPlNode(tag, for_full.ast.cond_expr, Zir.Inst.Bin{ + .lhs = array_ptr, + .rhs = index, + }); try astgen.detectLocalShadowing(&then_scope.base, name_str_index, ident, value_name); payload_val_scope = .{ .parent = &then_scope.base, diff --git a/src/Sema.zig b/src/Sema.zig index 5010d86c9a..b946e29057 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2738,6 +2738,7 @@ fn ensureResultUsed( const operand_ty = sema.typeOf(operand); switch (operand_ty.zigTypeTag()) { .Void, .NoReturn => return, + .ErrorSet, .ErrorUnion => return sema.fail(block, src, "error is ignored. consider using `try`, `catch`, or `if`", .{}), else => return sema.fail(block, src, "expression value is ignored", .{}), } } @@ -2751,7 +2752,7 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com const src = inst_data.src(); const operand_ty = sema.typeOf(operand); switch (operand_ty.zigTypeTag()) { - .ErrorSet, .ErrorUnion => return sema.fail(block, src, "error is discardederror is discarded. consider using `try`, `catch`, or `if`", .{}), + .ErrorSet, .ErrorUnion => return sema.fail(block, src, "error is discarded. consider using `try`, `catch`, or `if`", .{}), else => return, } } @@ -7092,7 +7093,7 @@ fn funcCommon( const param_types = try sema.arena.alloc(Type, block.params.items.len); const comptime_params = try sema.arena.alloc(bool, block.params.items.len); for (block.params.items) |param, i| { - const param_src = LazySrcLoc.nodeOffset(src_node_offset); // TODO better src + const param_src = LazySrcLoc.nodeOffset(src_node_offset); // TODO better soruce location param_types[i] = param.ty; comptime_params[i] = param.is_comptime or try sema.typeRequiresComptime(block, param_src, param.ty); @@ -7798,12 +7799,12 @@ fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const tracy = trace(@src()); defer tracy.end(); - const bin_inst = sema.code.instructions.items(.data)[inst].bin; - const src = sema.src; // TODO better source location - const elem_index_src = sema.src; // TODO better source location - const array = try sema.resolveInst(bin_inst.lhs); - const elem_index = try sema.resolveInst(bin_inst.rhs); - return sema.elemVal(block, src, array, elem_index, elem_index_src); + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; + const array = try sema.resolveInst(extra.lhs); + const elem_index = try sema.resolveInst(extra.rhs); + return sema.elemVal(block, src, array, elem_index, src); } fn zirElemValNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -7823,10 +7824,12 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const tracy = trace(@src()); defer tracy.end(); - const bin_inst = sema.code.instructions.items(.data)[inst].bin; - const array_ptr = try sema.resolveInst(bin_inst.lhs); - const elem_index = try sema.resolveInst(bin_inst.rhs); - return sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src, false); + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; + const array_ptr = try sema.resolveInst(extra.lhs); + const elem_index = try sema.resolveInst(extra.rhs); + return sema.elemPtr(block, src, array_ptr, elem_index, src, false); } fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -19454,7 +19457,7 @@ fn tupleFieldPtr( const tuple_fields = tuple_ty.tupleFields(); if (tuple_fields.types.len == 0) { - return sema.fail(block, field_index_src, "indexing into empty tuple", .{}); + return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{}); } if (field_index >= tuple_fields.types.len) { @@ -19497,7 +19500,7 @@ fn tupleField( const tuple_fields = tuple_ty.tupleFields(); if (tuple_fields.types.len == 0) { - return sema.fail(block, field_index_src, "indexing into empty tuple", .{}); + return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{}); } if (field_index >= tuple_fields.types.len) { @@ -19538,7 +19541,7 @@ fn elemValArray( const elem_ty = array_ty.childType(); if (array_len_s == 0) { - return sema.fail(block, elem_index_src, "indexing into empty array", .{}); + return sema.fail(block, array_src, "indexing into empty array is not allowed", .{}); } const maybe_undef_array_val = try sema.resolveMaybeUndefVal(block, array_src, array); @@ -19618,7 +19621,7 @@ fn elemPtrArray( const array_len_s = array_len + @boolToInt(array_sent); if (array_len_s == 0) { - return sema.fail(block, elem_index_src, "indexing into empty array", .{}); + return sema.fail(block, array_ptr_src, "indexing into empty array is not allowed", .{}); } const maybe_undef_array_ptr_val = try sema.resolveMaybeUndefVal(block, array_ptr_src, array_ptr); @@ -19700,7 +19703,7 @@ fn elemValSlice( const slice_len = slice_val.sliceLen(sema.mod); const slice_len_s = slice_len + @boolToInt(slice_sent); if (slice_len_s == 0) { - return sema.fail(block, elem_index_src, "indexing into empty slice", .{}); + return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{}); } if (maybe_index_val) |index_val| { const index = @intCast(usize, index_val.toUnsignedInt(target)); @@ -19757,7 +19760,7 @@ fn elemPtrSlice( const slice_len = slice_val.sliceLen(sema.mod); const slice_len_s = slice_len + @boolToInt(slice_sent); if (slice_len_s == 0) { - return sema.fail(block, elem_index_src, "indexing into empty slice", .{}); + return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{}); } if (offset) |index| { if (index >= slice_len_s) { diff --git a/src/Zir.zig b/src/Zir.zig index 26bb09860d..98cdd490b8 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -370,24 +370,23 @@ pub const Inst = struct { /// Uses the `pl_node` union field. Payload is `Bin`. div, /// Given a pointer to an array, slice, or pointer, returns a pointer to the element at - /// the provided index. Uses the `bin` union field. Source location is implied - /// to be the same as the previous instruction. - elem_ptr, - /// Same as `elem_ptr` except also stores a source location node. + /// the provided index. /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`. elem_ptr_node, + /// Same as `elem_ptr_node` but used only for for loop. + /// Uses the `pl_node` union field. AST node is the condition of a for loop. Payload is `Bin`. + elem_ptr, /// Same as `elem_ptr_node` except the index is stored immediately rather than /// as a reference to another ZIR instruction. /// Uses the `pl_node` union field. AST node is an element inside array initialization /// syntax. Payload is `ElemPtrImm`. elem_ptr_imm, /// Given an array, slice, or pointer, returns the element at the provided index. - /// Uses the `bin` union field. Source location is implied to be the same - /// as the previous instruction. - elem_val, - /// Same as `elem_val` except also stores a source location node. /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`. elem_val_node, + /// Same as `elem_val_node` but used only for for loop. + /// Uses the `pl_node` union field. AST node is the condition of a for loop. Payload is `Bin`. + elem_val, /// Emits a compile error if the operand is not `void`. /// Uses the `un_node` field. ensure_result_used, @@ -1627,10 +1626,10 @@ pub const Inst = struct { .decl_val = .str_tok, .load = .un_node, .div = .pl_node, - .elem_ptr = .bin, + .elem_ptr = .pl_node, .elem_ptr_node = .pl_node, .elem_ptr_imm = .pl_node, - .elem_val = .bin, + .elem_val = .pl_node, .elem_val_node = .pl_node, .ensure_result_used = .un_node, .ensure_result_non_error = .un_node, diff --git a/src/print_zir.zig b/src/print_zir.zig index fe8446b34a..b19076a7f0 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -144,8 +144,6 @@ const Writer = struct { switch (tag) { .array_type, .as, - .elem_ptr, - .elem_val, .store, .store_to_block_ptr, .store_to_inferred_ptr, @@ -355,6 +353,8 @@ const Writer = struct { .minimum, .elem_ptr_node, .elem_val_node, + .elem_ptr, + .elem_val, .coerce_result_ptr, => try self.writePlNodeBin(stream, inst), diff --git a/src/type.zig b/src/type.zig index 0ca7ba83c5..50302dca6c 100644 --- a/src/type.zig +++ b/src/type.zig @@ -189,7 +189,7 @@ pub const Type = extern union { .Frame, => false, - .Pointer => is_equality_cmp or ty.isCPtr(), + .Pointer => !ty.isSlice() and (is_equality_cmp or ty.isCPtr()), .Optional => { if (!is_equality_cmp) return false; var buf: Payload.ElemType = undefined; diff --git a/test/cases/compile_errors/stage1/obj/ignored_deferred_function_call.zig b/test/cases/compile_errors/ignored_deferred_function_call.zig similarity index 52% rename from test/cases/compile_errors/stage1/obj/ignored_deferred_function_call.zig rename to test/cases/compile_errors/ignored_deferred_function_call.zig index 41812bfda9..69df8b0498 100644 --- a/test/cases/compile_errors/stage1/obj/ignored_deferred_function_call.zig +++ b/test/cases/compile_errors/ignored_deferred_function_call.zig @@ -4,7 +4,7 @@ export fn foo() void { fn bar() anyerror!i32 { return 0; } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:14: error: error is ignored. consider using `try`, `catch`, or `if` +// :2:14: error: error is ignored. consider using `try`, `catch`, or `if` diff --git a/test/cases/compile_errors/stage1/obj/ignored_expression_in_while_continuation.zig b/test/cases/compile_errors/ignored_expression_in_while_continuation.zig similarity index 54% rename from test/cases/compile_errors/stage1/obj/ignored_expression_in_while_continuation.zig rename to test/cases/compile_errors/ignored_expression_in_while_continuation.zig index 43f3713fc6..d295d476ab 100644 --- a/test/cases/compile_errors/stage1/obj/ignored_expression_in_while_continuation.zig +++ b/test/cases/compile_errors/ignored_expression_in_while_continuation.zig @@ -14,9 +14,9 @@ fn bad() anyerror!void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:24: error: error is ignored. consider using `try`, `catch`, or `if` -// tmp.zig:6:25: error: error is ignored. consider using `try`, `catch`, or `if` -// tmp.zig:10:25: error: error is ignored. consider using `try`, `catch`, or `if` +// :2:24: error: error is ignored. consider using `try`, `catch`, or `if` +// :6:25: error: error is ignored. consider using `try`, `catch`, or `if` +// :10:25: error: error is ignored. consider using `try`, `catch`, or `if` diff --git a/test/cases/compile_errors/illegal_comparison_of_types.zig b/test/cases/compile_errors/illegal_comparison_of_types.zig new file mode 100644 index 0000000000..69d7a28fa4 --- /dev/null +++ b/test/cases/compile_errors/illegal_comparison_of_types.zig @@ -0,0 +1,20 @@ +fn bad_eql_1(a: []u8, b: []u8) bool { + return a == b; +} +const EnumWithData = union(enum) { + One: void, + Two: i32, +}; +fn bad_eql_2(a: *const EnumWithData, b: *const EnumWithData) bool { + return a.* == b.*; +} + +export fn entry1() usize { return @sizeOf(@TypeOf(&bad_eql_1)); } +export fn entry2() usize { return @sizeOf(@TypeOf(&bad_eql_2)); } + +// error +// backend=stage2 +// target=native +// +// :2:14: error: operator == not allowed for type '[]u8' +// :9:16: error: operator == not allowed for type 'tmp.EnumWithData' diff --git a/test/cases/compile_errors/stage1/obj/implicitly_casting_enum_to_tag_type.zig b/test/cases/compile_errors/implicitly_casting_enum_to_tag_type.zig similarity index 68% rename from test/cases/compile_errors/stage1/obj/implicitly_casting_enum_to_tag_type.zig rename to test/cases/compile_errors/implicitly_casting_enum_to_tag_type.zig index a40615f99b..b1930cb548 100644 --- a/test/cases/compile_errors/stage1/obj/implicitly_casting_enum_to_tag_type.zig +++ b/test/cases/compile_errors/implicitly_casting_enum_to_tag_type.zig @@ -11,7 +11,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:9:22: error: expected type 'u2', found 'Small' +// :9:22: error: expected type 'u2', found 'tmp.Small' diff --git a/test/cases/compile_errors/stage1/obj/incorrect_return_type.zig b/test/cases/compile_errors/incorrect_return_type.zig similarity index 75% rename from test/cases/compile_errors/stage1/obj/incorrect_return_type.zig rename to test/cases/compile_errors/incorrect_return_type.zig index b25e2a8ea4..b37cbebc7f 100644 --- a/test/cases/compile_errors/stage1/obj/incorrect_return_type.zig +++ b/test/cases/compile_errors/incorrect_return_type.zig @@ -15,7 +15,7 @@ } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:8:16: error: expected type 'A', found 'B' +// :8:16: error: expected type 'tmp.A', found 'tmp.B' diff --git a/test/cases/compile_errors/stage1/obj/indexing_an_array_of_size_zero.zig b/test/cases/compile_errors/indexing_an_array_of_size_zero.zig similarity index 59% rename from test/cases/compile_errors/stage1/obj/indexing_an_array_of_size_zero.zig rename to test/cases/compile_errors/indexing_an_array_of_size_zero.zig index dfb2e7c1c3..d2a54046de 100644 --- a/test/cases/compile_errors/stage1/obj/indexing_an_array_of_size_zero.zig +++ b/test/cases/compile_errors/indexing_an_array_of_size_zero.zig @@ -5,7 +5,7 @@ export fn foo() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:27: error: accessing a zero length array is not allowed +// :3:27: error: indexing into empty array is not allowed diff --git a/test/cases/compile_errors/stage1/obj/indexing_an_array_of_size_zero_with_runtime_index.zig b/test/cases/compile_errors/indexing_an_array_of_size_zero_with_runtime_index.zig similarity index 64% rename from test/cases/compile_errors/stage1/obj/indexing_an_array_of_size_zero_with_runtime_index.zig rename to test/cases/compile_errors/indexing_an_array_of_size_zero_with_runtime_index.zig index f50931312e..eceb5db50e 100644 --- a/test/cases/compile_errors/stage1/obj/indexing_an_array_of_size_zero_with_runtime_index.zig +++ b/test/cases/compile_errors/indexing_an_array_of_size_zero_with_runtime_index.zig @@ -6,7 +6,7 @@ export fn foo() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:27: error: accessing a zero length array is not allowed +// :4:27: error: indexing into empty array is not allowed diff --git a/test/cases/compile_errors/stage1/obj/indexing_single-item_pointer.zig b/test/cases/compile_errors/indexing_single-item_pointer.zig similarity index 51% rename from test/cases/compile_errors/stage1/obj/indexing_single-item_pointer.zig rename to test/cases/compile_errors/indexing_single-item_pointer.zig index bc7951ec96..efd063817c 100644 --- a/test/cases/compile_errors/stage1/obj/indexing_single-item_pointer.zig +++ b/test/cases/compile_errors/indexing_single-item_pointer.zig @@ -3,7 +3,7 @@ export fn entry(ptr: *i32) i32 { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:15: error: index of single-item pointer +// :2:15: error: element access of non-indexable type '*i32' diff --git a/test/cases/compile_errors/stage1/obj/invalid_cast_from_integral_type_to_enum.zig b/test/cases/compile_errors/invalid_cast_from_integral_type_to_enum.zig similarity index 70% rename from test/cases/compile_errors/stage1/obj/invalid_cast_from_integral_type_to_enum.zig rename to test/cases/compile_errors/invalid_cast_from_integral_type_to_enum.zig index 81dbd88e3b..ce2f64169b 100644 --- a/test/cases/compile_errors/stage1/obj/invalid_cast_from_integral_type_to_enum.zig +++ b/test/cases/compile_errors/invalid_cast_from_integral_type_to_enum.zig @@ -11,7 +11,7 @@ fn foo(x: usize) void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:9:10: error: expected type 'usize', found 'E' +// :9:10: error: expected type 'usize', found 'tmp.E' diff --git a/test/cases/compile_errors/runtime_indexing_comptime_array.zig b/test/cases/compile_errors/runtime_indexing_comptime_array.zig index 16f7305f63..b83e977b3f 100644 --- a/test/cases/compile_errors/runtime_indexing_comptime_array.zig +++ b/test/cases/compile_errors/runtime_indexing_comptime_array.zig @@ -24,9 +24,9 @@ pub export fn entry3() void { // target=native // backend=stage2 // -// :6:5: error: values of type '[2]fn() void' must be comptime known, but index value is runtime known -// :6:5: note: use '*const fn() void' for a function pointer type -// :13:5: error: values of type '[2]fn() void' must be comptime known, but index value is runtime known -// :13:5: note: use '*const fn() void' for a function pointer type -// :19:5: error: values of type '[2]fn() void' must be comptime known, but index value is runtime known -// :19:5: note: use '*const fn() void' for a function pointer type +// :7:10: error: values of type '[2]fn() void' must be comptime known, but index value is runtime known +// :7:10: note: use '*const fn() void' for a function pointer type +// :15:18: error: values of type '[2]fn() void' must be comptime known, but index value is runtime known +// :15:17: note: use '*const fn() void' for a function pointer type +// :21:19: error: values of type '[2]fn() void' must be comptime known, but index value is runtime known +// :21:18: note: use '*const fn() void' for a function pointer type diff --git a/test/cases/compile_errors/stage1/obj/implicit_cast_from_f64_to_f32.zig b/test/cases/compile_errors/stage1/implicit_cast_from_f64_to_f32.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/implicit_cast_from_f64_to_f32.zig rename to test/cases/compile_errors/stage1/implicit_cast_from_f64_to_f32.zig diff --git a/test/cases/compile_errors/stage1/obj/int_to_ptr_of_0_bits.zig b/test/cases/compile_errors/stage1/int_to_ptr_of_0_bits.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/int_to_ptr_of_0_bits.zig rename to test/cases/compile_errors/stage1/int_to_ptr_of_0_bits.zig diff --git a/test/cases/compile_errors/stage1/obj/illegal_comparison_of_types.zig b/test/cases/compile_errors/stage1/obj/illegal_comparison_of_types.zig deleted file mode 100644 index d04eaec3b5..0000000000 --- a/test/cases/compile_errors/stage1/obj/illegal_comparison_of_types.zig +++ /dev/null @@ -1,20 +0,0 @@ -fn bad_eql_1(a: []u8, b: []u8) bool { - return a == b; -} -const EnumWithData = union(enum) { - One: void, - Two: i32, -}; -fn bad_eql_2(a: *const EnumWithData, b: *const EnumWithData) bool { - return a.* == b.*; -} - -export fn entry1() usize { return @sizeOf(@TypeOf(bad_eql_1)); } -export fn entry2() usize { return @sizeOf(@TypeOf(bad_eql_2)); } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:14: error: operator not allowed for type '[]u8' -// tmp.zig:9:16: error: operator not allowed for type 'EnumWithData' From 48fd92365a93a06a57ae0270be5f2614804e5749 Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Fri, 1 Jul 2022 11:37:41 +0200 Subject: [PATCH 2029/2031] std.crypto.hash: allow creating hash functions from compositions (#11965) A hash function cascade was a common way to avoid length-extension attacks with traditional hash functions such as the SHA-2 family. Add `std.crypto.hash.composition` to do exactly that using arbitrary hash functions, and pre-define the common SHA2-based ones. With this, we can now sign and verify Bitcoin signatures in pure Zig. --- lib/std/crypto.zig | 2 + lib/std/crypto/ecdsa.zig | 20 +++++++- lib/std/crypto/hash_composition.zig | 80 +++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 lib/std/crypto/hash_composition.zig diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index 522a545fcf..d9b369a0b9 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -76,6 +76,7 @@ pub const hash = struct { pub const Sha1 = @import("crypto/sha1.zig").Sha1; pub const sha2 = @import("crypto/sha2.zig"); pub const sha3 = @import("crypto/sha3.zig"); + pub const composition = @import("crypto/hash_composition.zig"); }; /// Key derivation functions. @@ -215,6 +216,7 @@ test { _ = hash.Sha1; _ = hash.sha2; _ = hash.sha3; + _ = hash.composition; _ = kdf.hkdf; diff --git a/lib/std/crypto/ecdsa.zig b/lib/std/crypto/ecdsa.zig index 72c75ca163..4623574b37 100644 --- a/lib/std/crypto/ecdsa.zig +++ b/lib/std/crypto/ecdsa.zig @@ -18,6 +18,10 @@ pub const EcdsaP256Sha3_256 = Ecdsa(crypto.ecc.P256, crypto.hash.sha3.Sha3_256); pub const EcdsaP384Sha384 = Ecdsa(crypto.ecc.P384, crypto.hash.sha2.Sha384); /// ECDSA over P-384 with SHA3-384. pub const EcdsaP256Sha3_384 = Ecdsa(crypto.ecc.P384, crypto.hash.sha3.Sha3_384); +/// ECDSA over Secp256k1 with SHA-256. +pub const EcdsaSecp256k1Sha256 = Ecdsa(crypto.ecc.Secp256k1, crypto.hash.sha2.Sha256); +/// ECDSA over Secp256k1 with SHA-256(SHA-256()) -- The Bitcoin signature system. +pub const EcdsaSecp256k1Sha256oSha256 = Ecdsa(crypto.ecc.Secp256k1, crypto.hash.composition.Sha256oSha256); /// Elliptic Curve Digital Signature Algorithm (ECDSA). pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type { @@ -293,7 +297,7 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type { }; } -test "ECDSA - Basic operations" { +test "ECDSA - Basic operations over EcdsaP384Sha384" { const Scheme = EcdsaP384Sha384; const kp = try Scheme.KeyPair.create(null); const msg = "test"; @@ -307,6 +311,20 @@ test "ECDSA - Basic operations" { try sig2.verify(msg, kp.public_key); } +test "ECDSA - Basic operations over Secp256k1" { + const Scheme = EcdsaSecp256k1Sha256oSha256; + const kp = try Scheme.KeyPair.create(null); + const msg = "test"; + + var noise: [Scheme.noise_length]u8 = undefined; + crypto.random.bytes(&noise); + const sig = try kp.sign(msg, noise); + try sig.verify(msg, kp.public_key); + + const sig2 = try kp.sign(msg, null); + try sig2.verify(msg, kp.public_key); +} + const TestVector = struct { key: []const u8, msg: []const u8, diff --git a/lib/std/crypto/hash_composition.zig b/lib/std/crypto/hash_composition.zig new file mode 100644 index 0000000000..1ffa3d4c47 --- /dev/null +++ b/lib/std/crypto/hash_composition.zig @@ -0,0 +1,80 @@ +const std = @import("../std.zig"); +const sha2 = std.crypto.hash.sha2; + +/// The composition of two hash functions: H1 o H2, with the same API as regular hash functions. +/// +/// The security level of a hash cascade doesn't exceed the security level of the weakest function. +/// +/// However, Merkle–Damgård constructions such as SHA-256 are vulnerable to length-extension attacks, +/// where under some conditions, `H(x||e)` can be efficiently computed without knowing `x`. +/// The composition of two hash functions is a common defense against such attacks. +/// +/// This is not necessary with modern hash functions, such as SHA-3, BLAKE2 and BLAKE3. +pub fn Composition(comptime H1: type, comptime H2: type) type { + return struct { + const Self = @This(); + + H1: H1, + H2: H2, + + /// The length of the hash output, in bytes. + pub const digest_length = H1.digest_length; + /// The block length, in bytes. + pub const block_length = H1.block_length; + + /// Options for both hashes. + pub const Options = struct { + /// Options for H1. + H1: H1.Options = .{}, + /// Options for H2. + H2: H2.Options = .{}, + }; + + /// Initialize the hash composition with the given options. + pub fn init(options: Options) Self { + return Self{ .H1 = H1.init(options.H1), .H2 = H2.init(options.H2) }; + } + + /// Compute H1(H2(b)). + pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void { + var d = Self.init(options); + d.update(b); + d.final(out); + } + + /// Add content to the hash. + pub fn update(d: *Self, b: []const u8) void { + d.H2.update(b); + } + + /// Compute the final hash for the accumulated content: H1(H2(b)). + pub fn final(d: *Self, out: *[digest_length]u8) void { + var H2_digest: [H2.digest_length]u8 = undefined; + d.H2.final(&H2_digest); + d.H1.update(&H2_digest); + d.H1.final(out); + } + }; +} + +/// SHA-256(SHA-256()) +pub const Sha256oSha256 = Composition(sha2.Sha256, sha2.Sha256); +/// SHA-384(SHA-384()) +pub const Sha384oSha384 = Composition(sha2.Sha384, sha2.Sha384); +/// SHA-512(SHA-512()) +pub const Sha512oSha512 = Composition(sha2.Sha512, sha2.Sha512); + +test "Hash composition" { + const Sha256 = sha2.Sha256; + const msg = "test"; + + var out: [Sha256oSha256.digest_length]u8 = undefined; + Sha256oSha256.hash(msg, &out, .{}); + + var t: [Sha256.digest_length]u8 = undefined; + Sha256.hash(msg, &t, .{}); + var out2: [Sha256.digest_length]u8 = undefined; + Sha256.hash(&t, &out2, .{}); + + try std.testing.expectEqualSlices(u8, &out, &out2); +} From ee01dd40322db39dcbb0e791d0885d6da9f4150a Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Fri, 1 Jul 2022 13:18:08 +0200 Subject: [PATCH 2030/2031] crypto: add the Xoodoo permutation, prepare for Gimli deprecation (#11866) Gimli was a game changer. A permutation that is large enough to be used in sponge-like constructions, yet small enough to be compact to implement and fast on a wide range of platforms. And Gimli being part of the Zig standard library was awesome. But since then, Gimli entered the NIST Lightweight Cryptography Competition, competing againt other candidates sharing a similar set of properties. Unfortunately, Gimli didn't pass the 3rd round. There are no practical attacks against Gimli when used correctly, but NIST's decision means that Gimli is unlikely to ever get any traction. So, maybe the time has come to move Gimli from the standard library to another repository. We shouldn't do it without providing an alternative, though. And the best candidate for this is probably Xoodoo. Xoodoo is the core function of Xoodyak, one of the finalists of the NIST LWC competition, and the most direct competitor to Gimli. It is also a 384-bit permutation, so it can easily be used everywhere Gimli was used with no parameter changes. It is the building block of Xoodyak (for actual encryption and hashing) as well as Charm, that some Zig applications are already using. Like Gimli that it was heavily inspired from, it is compact and suitable for constrained environments. This change adds the Xoodoo permutation to std.crypto.core. The set of public functions includes everything required to later implement existing Xoodoo-based constructions. In order to prepare for the Gimli deprecation, the default CSPRNG was changed to a Xoodoo-based that works exactly the same way. --- lib/std/crypto.zig | 1 + lib/std/crypto/xoodoo.zig | 141 ++++++++++++++++++++++++++++++++++++++ lib/std/rand.zig | 4 +- lib/std/rand/Xoodoo.zig | 42 ++++++++++++ 4 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 lib/std/crypto/xoodoo.zig create mode 100644 lib/std/rand/Xoodoo.zig diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index d9b369a0b9..46dfa6a715 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -43,6 +43,7 @@ pub const auth = struct { pub const core = struct { pub const aes = @import("crypto/aes.zig"); pub const Gimli = @import("crypto/gimli.zig").State; + pub const Xoodoo = @import("crypto/xoodoo.zig").State; /// Modes are generic compositions to construct encryption/decryption functions from block ciphers and permutations. /// diff --git a/lib/std/crypto/xoodoo.zig b/lib/std/crypto/xoodoo.zig new file mode 100644 index 0000000000..bbc579f073 --- /dev/null +++ b/lib/std/crypto/xoodoo.zig @@ -0,0 +1,141 @@ +//! Xoodoo is a 384-bit permutation designed to achieve high security with high +//! performance across a broad range of platforms, including 64-bit Intel/AMD +//! server CPUs, 64-bit and 32-bit ARM smartphone CPUs, 32-bit ARM +//! microcontrollers, 8-bit AVR microcontrollers, FPGAs, ASICs without +//! side-channel protection, and ASICs with side-channel protection. +//! +//! Xoodoo is the core function of Xoodyak, a finalist of the NIST lightweight cryptography competition. +//! https://csrc.nist.gov/CSRC/media/Projects/Lightweight-Cryptography/documents/round-1/spec-doc/Xoodyak-spec.pdf +//! +//! It is not meant to be used directly, but as a building block for symmetric cryptography. + +const std = @import("../std.zig"); +const builtin = @import("builtin"); +const mem = std.mem; +const math = std.math; +const testing = std.testing; + +/// A Xoodoo state. +pub const State = struct { + /// Number of bytes in the state. + pub const block_bytes = 48; + + const rcs = [12]u32{ 0x058, 0x038, 0x3c0, 0x0d0, 0x120, 0x014, 0x060, 0x02c, 0x380, 0x0f0, 0x1a0, 0x012 }; + const Lane = @Vector(4, u32); + st: [3]Lane, + + /// Initialize a state from a slice of bytes. + pub fn init(initial_state: [block_bytes]u8) State { + var state = State{ .st = undefined }; + mem.copy(u8, state.asBytes(), &initial_state); + state.endianSwap(); + return state; + } + + // A representation of the state as 32-bit words. + fn asWords(self: *State) *[12]u32 { + return @ptrCast(*[12]u32, &self.st); + } + + /// A representation of the state as bytes. The byte order is architecture-dependent. + pub fn asBytes(self: *State) *[block_bytes]u8 { + return mem.asBytes(&self.st); + } + + /// Byte-swap words storing the bytes of a given range if the architecture is not little-endian. + pub fn endianSwapPartial(self: *State, from: usize, to: usize) void { + for (self.asWords()[from / 4 .. (to + 3) / 4]) |*w| { + w.* = mem.littleToNative(u32, w.*); + } + } + + /// Byte-swap the entire state if the architecture is not little-endian. + pub fn endianSwap(self: *State) void { + for (self.asWords()) |*w| { + w.* = mem.littleToNative(u32, w.*); + } + } + + /// XOR a byte into the state at a given offset. + pub fn addByte(self: *State, byte: u8, offset: usize) void { + self.endianSwapPartial(offset, offset); + self.asBytes()[offset] ^= byte; + self.endianSwapPartial(offset, offset); + } + + /// XOR bytes into the beginning of the state. + pub fn addBytes(self: *State, bytes: []const u8) void { + self.endianSwap(); + for (self.asBytes()[0..bytes.len]) |*byte, i| { + byte.* ^= bytes[i]; + } + self.endianSwap(); + } + + /// Extract the first bytes of the state. + pub fn extract(self: *State, out: []u8) void { + self.endianSwap(); + mem.copy(u8, out, self.asBytes()[0..out.len]); + self.endianSwap(); + } + + /// Set the words storing the bytes of a given range to zero. + pub fn clear(self: *State, from: usize, to: usize) void { + mem.set(u32, self.asWords()[from / 4 .. (to + 3) / 4], 0); + } + + /// Apply the Xoodoo permutation. + pub fn permute(self: *State) void { + const rot8x32 = comptime if (builtin.target.cpu.arch.endian() == .Big) + [_]i32{ 9, 10, 11, 8, 13, 14, 15, 12, 1, 2, 3, 0, 5, 6, 7, 4 } + else + [_]i32{ 11, 8, 9, 10, 15, 12, 13, 14, 3, 0, 1, 2, 7, 4, 5, 6 }; + + var a = self.st[0]; + var b = self.st[1]; + var c = self.st[2]; + inline for (rcs) |rc| { + var p = @shuffle(u32, a ^ b ^ c, undefined, [_]i32{ 3, 0, 1, 2 }); + var e = math.rotl(Lane, p, 5); + p = math.rotl(Lane, p, 14); + e ^= p; + a ^= e; + b ^= e; + c ^= e; + b = @shuffle(u32, b, undefined, [_]i32{ 3, 0, 1, 2 }); + c = math.rotl(Lane, c, 11); + a[0] ^= rc; + a ^= ~b & c; + b ^= ~c & a; + c ^= ~a & b; + b = math.rotl(Lane, b, 1); + c = @bitCast(Lane, @shuffle(u8, @bitCast(@Vector(16, u8), c), undefined, rot8x32)); + } + self.st[0] = a; + self.st[1] = b; + self.st[2] = c; + } +}; + +test "xoodoo" { + const bytes = [_]u8{0x01} ** State.block_bytes; + var st = State.init(bytes); + var out: [State.block_bytes]u8 = undefined; + st.permute(); + st.extract(&out); + const expected1 = [_]u8{ 51, 240, 163, 117, 43, 238, 62, 200, 114, 52, 79, 41, 48, 108, 150, 181, 24, 5, 252, 185, 235, 179, 28, 3, 116, 170, 36, 15, 232, 35, 116, 61, 110, 4, 109, 227, 91, 205, 0, 180, 179, 146, 112, 235, 96, 212, 206, 205 }; + try testing.expectEqualSlices(u8, &expected1, &out); + st.clear(0, 10); + st.extract(&out); + const expected2 = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 108, 150, 181, 24, 5, 252, 185, 235, 179, 28, 3, 116, 170, 36, 15, 232, 35, 116, 61, 110, 4, 109, 227, 91, 205, 0, 180, 179, 146, 112, 235, 96, 212, 206, 205 }; + try testing.expectEqualSlices(u8, &expected2, &out); + st.addByte(1, 5); + st.addByte(2, 5); + st.extract(&out); + const expected3 = [_]u8{ 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 48, 108, 150, 181, 24, 5, 252, 185, 235, 179, 28, 3, 116, 170, 36, 15, 232, 35, 116, 61, 110, 4, 109, 227, 91, 205, 0, 180, 179, 146, 112, 235, 96, 212, 206, 205 }; + try testing.expectEqualSlices(u8, &expected3, &out); + st.addBytes(&bytes); + st.extract(&out); + const expected4 = [_]u8{ 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 49, 109, 151, 180, 25, 4, 253, 184, 234, 178, 29, 2, 117, 171, 37, 14, 233, 34, 117, 60, 111, 5, 108, 226, 90, 204, 1, 181, 178, 147, 113, 234, 97, 213, 207, 204 }; + try testing.expectEqualSlices(u8, &expected4, &out); +} diff --git a/lib/std/rand.zig b/lib/std/rand.zig index 72537e952f..a121a3f5ba 100644 --- a/lib/std/rand.zig +++ b/lib/std/rand.zig @@ -18,10 +18,10 @@ const maxInt = std.math.maxInt; pub const DefaultPrng = Xoshiro256; /// Cryptographically secure random numbers. -pub const DefaultCsprng = Gimli; +pub const DefaultCsprng = Xoodoo; pub const Isaac64 = @import("rand/Isaac64.zig"); -pub const Gimli = @import("rand/Gimli.zig"); +pub const Xoodoo = @import("rand/Xoodoo.zig"); pub const Pcg = @import("rand/Pcg.zig"); pub const Xoroshiro128 = @import("rand/Xoroshiro128.zig"); pub const Xoshiro256 = @import("rand/Xoshiro256.zig"); diff --git a/lib/std/rand/Xoodoo.zig b/lib/std/rand/Xoodoo.zig new file mode 100644 index 0000000000..3e16f2a864 --- /dev/null +++ b/lib/std/rand/Xoodoo.zig @@ -0,0 +1,42 @@ +//! CSPRNG + +const std = @import("std"); +const Random = std.rand.Random; +const min = std.math.min; +const mem = std.mem; +const Xoodoo = @This(); + +const State = std.crypto.core.Xoodoo; + +state: State, + +const rate = 16; +pub const secret_seed_length = 32; + +/// The seed must be uniform, secret and `secret_seed_length` bytes long. +pub fn init(secret_seed: [secret_seed_length]u8) Xoodoo { + var initial_state: [State.block_bytes]u8 = undefined; + mem.copy(u8, initial_state[0..secret_seed_length], &secret_seed); + mem.set(u8, initial_state[secret_seed_length..], 0); + var state = State.init(initial_state); + state.permute(); + return Xoodoo{ .state = state }; +} + +pub fn random(self: *Xoodoo) Random { + return Random.init(self, fill); +} + +pub fn fill(self: *Xoodoo, buf: []u8) void { + var i: usize = 0; + while (true) { + const left = buf.len - i; + const n = min(left, rate); + self.state.extract(buf[i..][0..n]); + if (left == 0) break; + self.state.permute(); + i += n; + } + self.state.clear(0, rate); + self.state.permute(); +} From b79884eaf003ad32e800213c20da5c6b8935af34 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 29 Jun 2022 08:05:32 +0200 Subject: [PATCH 2031/2031] macho: implement pruning of unused segments and sections This is a prelude to a more elaborate work which will implement `-dead_strip` flag - garbage collection of unreachable atoms. Here, when sorting sections, we also check that the section is actually populated with some atoms, and if not, we exclude it from the final linked image. This can happen when we do not import any symbols from dynamic libraries in which case we will not be populating the stubs sections or the GOT table, implying we can skip allocating those sections. Furthermore, we also make a check that a segment is actually occupied too, with the exception of `__TEXT` segment which is non-optional given that it wraps the header and load commands and thus is required by the `dyld` to perform dynamic linking, and `__PAGEZERO` which is generally non-optional when the linked image is an executable. For any other segment, if its section count is zero, we mark it as dead and skip allocating it and generating a load command for it. This commit also includes some minor improvements to the linker such as refactoring of the segment allocating codepaths, skipping `__PAGEZERO` generation for dylibs, and skipping generation of zero-sized atoms for special symbols such as `__mh_execute_header` and `___dso_handle`. These special symbols are only allocated local and global symbol pair and their VM addresses is set to the start of the `__TEXT` segment, but no `Atom` is created, as it's not necessary given that they never carry any machine code. Finally, we now always force-link against `libSystem` which turns out to be required for `dyld` to properly handle `LC_MAIN` load command on older macOS versions such as 10.15.7. --- lib/std/macho.zig | 3 + src/link/MachO.zig | 690 +++++++++++++++---------------- src/link/MachO/CodeSignature.zig | 7 +- 3 files changed, 352 insertions(+), 348 deletions(-) diff --git a/lib/std/macho.zig b/lib/std/macho.zig index 2ca6fe9d0a..67b0be9fda 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -912,6 +912,9 @@ pub const relocation_info = packed struct { pub const LC_REQ_DYLD = 0x80000000; pub const LC = enum(u32) { + /// No load command - invalid + NONE = 0x0, + /// segment of this file to be mapped SEGMENT = 0x1, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index f5e1bf4b4d..d659d994eb 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -164,11 +164,13 @@ tentatives: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, locals_free_list: std.ArrayListUnmanaged(u32) = .{}, globals_free_list: std.ArrayListUnmanaged(u32) = .{}, -mh_execute_header_index: ?u32 = null, dyld_stub_binder_index: ?u32 = null, dyld_private_atom: ?*Atom = null, stub_helper_preamble_atom: ?*Atom = null, +mh_execute_header_sym_index: ?u32 = null, +dso_handle_sym_index: ?u32 = null, + strtab: std.ArrayListUnmanaged(u8) = .{}, strtab_dir: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{}, @@ -856,7 +858,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No // re-exports every single symbol definition. for (lib_dirs.items) |dir| { if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| { - try libs.put(full_path, .{ .needed = false }); + try libs.put(full_path, .{ .needed = true }); libsystem_available = true; break :blk; } @@ -866,8 +868,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No for (lib_dirs.items) |dir| { if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| { if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| { - try libs.put(libsystem_path, .{ .needed = false }); - try libs.put(libc_path, .{ .needed = false }); + try libs.put(libsystem_path, .{ .needed = true }); + try libs.put(libc_path, .{ .needed = true }); libsystem_available = true; break :blk; } @@ -881,7 +883,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "darwin", libsystem_name, }); - try libs.put(full_path, .{ .needed = false }); + try libs.put(full_path, .{ .needed = true }); } // frameworks @@ -1090,7 +1092,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs); } - try self.createMhExecuteHeaderAtom(); + try self.createMhExecuteHeaderSymbol(); for (self.objects.items) |*object, object_id| { if (object.analyzed) continue; try self.resolveSymbolsInObject(@intCast(u16, object_id)); @@ -1101,7 +1103,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try self.createDyldPrivateAtom(); try self.createStubHelperPreambleAtom(); try self.resolveSymbolsInDylibs(); - try self.createDsoHandleAtom(); + try self.createDsoHandleSymbol(); try self.addCodeSignatureLC(); { @@ -1156,14 +1158,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No const use_llvm = build_options.have_llvm and self.base.options.use_llvm; if (use_llvm or use_stage1) { - try self.sortSections(); - try self.allocateTextSegment(); - try self.allocateDataConstSegment(); - try self.allocateDataSegment(); - self.allocateLinkeditSegment(); + try self.pruneAndSortSections(); + try self.allocateSegments(); try self.allocateLocals(); } + try self.allocateSpecialSymbols(); try self.allocateGlobals(); if (build_options.enable_logging) { @@ -2261,6 +2261,27 @@ fn shiftLocalsByOffset(self: *MachO, match: MatchingSection, offset: i64) !void } } +fn allocateSpecialSymbols(self: *MachO) !void { + for (&[_]?u32{ + self.mh_execute_header_sym_index, + self.dso_handle_sym_index, + }) |maybe_sym_index| { + const sym_index = maybe_sym_index orelse continue; + const sym = &self.locals.items[sym_index]; + const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment; + sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(.{ + .seg = self.text_segment_cmd_index.?, + .sect = 0, + }).? + 1); + sym.n_value = seg.inner.vmaddr; + + log.debug("allocating {s} at the start of {s}", .{ + self.getString(sym.n_strx), + seg.inner.segName(), + }); + } +} + fn allocateGlobals(self: *MachO) !void { log.debug("allocating global symbols", .{}); @@ -2442,7 +2463,9 @@ pub fn createTlvPtrAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom { } fn createDyldPrivateAtom(self: *MachO) !void { + if (self.dyld_stub_binder_index == null) return; if (self.dyld_private_atom != null) return; + const local_sym_index = @intCast(u32, self.locals.items.len); const sym = try self.locals.addOne(self.base.allocator); sym.* = .{ @@ -2468,7 +2491,9 @@ fn createDyldPrivateAtom(self: *MachO) !void { } fn createStubHelperPreambleAtom(self: *MachO) !void { + if (self.dyld_stub_binder_index == null) return; if (self.stub_helper_preamble_atom != null) return; + const arch = self.base.options.target.cpu.arch; const size: u64 = switch (arch) { .x86_64 => 15, @@ -2816,57 +2841,46 @@ fn createTentativeDefAtoms(self: *MachO) !void { } } -fn createDsoHandleAtom(self: *MachO) !void { - if (self.strtab_dir.getKeyAdapted(@as([]const u8, "___dso_handle"), StringIndexAdapter{ +fn createDsoHandleSymbol(self: *MachO) !void { + if (self.dso_handle_sym_index != null) return; + + const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "___dso_handle"), StringIndexAdapter{ .bytes = &self.strtab, - })) |n_strx| blk: { - const resolv = self.symbol_resolver.getPtr(n_strx) orelse break :blk; - if (resolv.where != .undef) break :blk; + }) orelse return; - const undef = &self.undefs.items[resolv.where_index]; - const match: MatchingSection = .{ - .seg = self.text_segment_cmd_index.?, - .sect = self.text_section_index.?, - }; - const local_sym_index = @intCast(u32, self.locals.items.len); - var nlist = macho.nlist_64{ - .n_strx = undef.n_strx, - .n_type = macho.N_SECT, - .n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1), - .n_desc = 0, - .n_value = 0, - }; - try self.locals.append(self.base.allocator, nlist); - const global_sym_index = @intCast(u32, self.globals.items.len); - nlist.n_type |= macho.N_EXT; - nlist.n_desc = macho.N_WEAK_DEF; - try self.globals.append(self.base.allocator, nlist); + const resolv = self.symbol_resolver.getPtr(n_strx) orelse return; + if (resolv.where != .undef) return; - assert(self.unresolved.swapRemove(resolv.where_index)); + const undef = &self.undefs.items[resolv.where_index]; + const local_sym_index = @intCast(u32, self.locals.items.len); + var nlist = macho.nlist_64{ + .n_strx = undef.n_strx, + .n_type = macho.N_SECT, + .n_sect = 0, + .n_desc = 0, + .n_value = 0, + }; + try self.locals.append(self.base.allocator, nlist); + const global_sym_index = @intCast(u32, self.globals.items.len); + nlist.n_type |= macho.N_EXT; + nlist.n_desc = macho.N_WEAK_DEF; + try self.globals.append(self.base.allocator, nlist); + self.dso_handle_sym_index = local_sym_index; - undef.* = .{ - .n_strx = 0, - .n_type = macho.N_UNDF, - .n_sect = 0, - .n_desc = 0, - .n_value = 0, - }; - resolv.* = .{ - .where = .global, - .where_index = global_sym_index, - .local_sym_index = local_sym_index, - }; + assert(self.unresolved.swapRemove(resolv.where_index)); - // We create an empty atom for this symbol. - // TODO perhaps we should special-case special symbols? Create a separate - // linked list of atoms? - const atom = try self.createEmptyAtom(local_sym_index, 0, 0); - if (self.needs_prealloc) { - const sym = &self.locals.items[local_sym_index]; - const vaddr = try self.allocateAtom(atom, 0, 1, match); - sym.n_value = vaddr; - } else try self.addAtomToSection(atom, match); - } + undef.* = .{ + .n_strx = 0, + .n_type = macho.N_UNDF, + .n_sect = 0, + .n_desc = 0, + .n_value = 0, + }; + resolv.* = .{ + .where = .global, + .where_index = global_sym_index, + .local_sym_index = local_sym_index, + }; } fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void { @@ -3183,27 +3197,21 @@ fn resolveSymbolsInDylibs(self: *MachO) !void { } } -fn createMhExecuteHeaderAtom(self: *MachO) !void { - if (self.mh_execute_header_index != null) return; - - const match: MatchingSection = .{ - .seg = self.text_segment_cmd_index.?, - .sect = self.text_section_index.?, - }; - const seg = self.load_commands.items[match.seg].segment; - const sect = seg.sections.items[match.sect]; +fn createMhExecuteHeaderSymbol(self: *MachO) !void { + if (self.base.options.output_mode != .Exe) return; + if (self.mh_execute_header_sym_index != null) return; const n_strx = try self.makeString("__mh_execute_header"); const local_sym_index = @intCast(u32, self.locals.items.len); var nlist = macho.nlist_64{ .n_strx = n_strx, .n_type = macho.N_SECT, - .n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1), + .n_sect = 0, .n_desc = 0, - .n_value = sect.addr, + .n_value = 0, }; try self.locals.append(self.base.allocator, nlist); - self.mh_execute_header_index = local_sym_index; + self.mh_execute_header_sym_index = local_sym_index; if (self.symbol_resolver.getPtr(n_strx)) |resolv| { const global = &self.globals.items[resolv.where_index]; @@ -3223,23 +3231,11 @@ fn createMhExecuteHeaderAtom(self: *MachO) !void { .file = null, }); } - - // We always set the __mh_execute_header to point to the beginning of the __TEXT,__text section - const atom = try self.createEmptyAtom(local_sym_index, 0, 0); - if (self.atoms.get(match)) |last| { - var first = last; - while (first.prev) |prev| { - first = prev; - } - atom.next = first; - first.prev = atom; - } else { - try self.atoms.putNoClobber(self.base.allocator, match, atom); - } } fn resolveDyldStubBinder(self: *MachO) !void { if (self.dyld_stub_binder_index != null) return; + if (self.unresolved.count() == 0) return; // no need for a stub binder if we don't have any imports const n_strx = try self.makeString("dyld_stub_binder"); const sym_index = @intCast(u32, self.undefs.items.len); @@ -3295,7 +3291,12 @@ fn resolveDyldStubBinder(self: *MachO) !void { const vaddr = try self.allocateAtom(atom, @sizeOf(u64), 8, match); log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr }); atom_sym.n_value = vaddr; - } else try self.addAtomToSection(atom, match); + } else { + const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].segment; + const sect = &seg.sections.items[self.got_section_index.?]; + sect.size += atom.size; + try self.addAtomToSection(atom, match); + } atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1); } @@ -4512,6 +4513,7 @@ fn populateMissingMetadata(self: *MachO) !void { const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size); if (self.pagezero_segment_cmd_index == null) blk: { + if (self.base.options.output_mode == .Lib) break :blk; if (aligned_pagezero_vmsize == 0) break :blk; if (aligned_pagezero_vmsize != pagezero_vmsize) { log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize}); @@ -4636,9 +4638,9 @@ fn populateMissingMetadata(self: *MachO) !void { var fileoff: u64 = 0; var needed_size: u64 = 0; if (self.needs_prealloc) { - const address_and_offset = self.nextSegmentAddressAndOffset(); - vmaddr = address_and_offset.address; - fileoff = address_and_offset.offset; + const base = self.getSegmentAllocBase(&.{self.text_segment_cmd_index.?}); + vmaddr = base.vmaddr; + fileoff = base.fileoff; const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint; needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size); log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{ @@ -4686,9 +4688,9 @@ fn populateMissingMetadata(self: *MachO) !void { var fileoff: u64 = 0; var needed_size: u64 = 0; if (self.needs_prealloc) { - const address_and_offset = self.nextSegmentAddressAndOffset(); - vmaddr = address_and_offset.address; - fileoff = address_and_offset.offset; + const base = self.getSegmentAllocBase(&.{self.data_const_segment_cmd_index.?}); + vmaddr = base.vmaddr; + fileoff = base.fileoff; const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint; needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size); log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{ @@ -4803,9 +4805,9 @@ fn populateMissingMetadata(self: *MachO) !void { var vmaddr: u64 = 0; var fileoff: u64 = 0; if (self.needs_prealloc) { - const address_and_offset = self.nextSegmentAddressAndOffset(); - vmaddr = address_and_offset.address; - fileoff = address_and_offset.offset; + const base = self.getSegmentAllocBase(&.{self.data_segment_cmd_index.?}); + vmaddr = base.vmaddr; + fileoff = base.fileoff; log.debug("found __LINKEDIT segment free space at 0x{x}", .{fileoff}); } try self.load_commands.append(self.base.allocator, .{ @@ -5030,17 +5032,10 @@ fn populateMissingMetadata(self: *MachO) !void { self.cold_start = true; } -fn allocateTextSegment(self: *MachO) !void { - const seg = &self.load_commands.items[self.text_segment_cmd_index.?].segment; - const base_vmaddr = if (self.pagezero_segment_cmd_index) |index| - self.load_commands.items[index].segment.inner.vmsize - else - 0; - seg.inner.fileoff = 0; - seg.inner.vmaddr = base_vmaddr; - +fn calcMinHeaderpad(self: *MachO) u64 { var sizeofcmds: u32 = 0; for (self.load_commands.items) |lc| { + if (lc.cmd() == .NONE) continue; sizeofcmds += lc.cmdsize(); } @@ -5067,60 +5062,74 @@ fn allocateTextSegment(self: *MachO) !void { } const offset = @sizeOf(macho.mach_header_64) + padding; log.debug("actual headerpad size 0x{x}", .{offset}); - try self.allocateSegment(self.text_segment_cmd_index.?, offset); - // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments. - var min_alignment: u32 = 0; - for (seg.sections.items) |sect| { - const alignment = try math.powi(u32, 2, sect.@"align"); - min_alignment = math.max(min_alignment, alignment); - } + return offset; +} - assert(min_alignment > 0); - const last_sect_idx = seg.sections.items.len - 1; - const last_sect = seg.sections.items[last_sect_idx]; - const shift: u32 = blk: { - const diff = seg.inner.filesize - last_sect.offset - last_sect.size; - const factor = @divTrunc(diff, min_alignment); - break :blk @intCast(u32, factor * min_alignment); - }; +fn allocateSegments(self: *MachO) !void { + try self.allocateSegment(self.text_segment_cmd_index, &.{ + self.pagezero_segment_cmd_index, + }, self.calcMinHeaderpad()); - if (shift > 0) { - for (seg.sections.items) |*sect| { - sect.offset += shift; - sect.addr += shift; + if (self.text_segment_cmd_index) |index| blk: { + const seg = &self.load_commands.items[index].segment; + if (seg.sections.items.len == 0) break :blk; + + // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments. + var min_alignment: u32 = 0; + for (seg.sections.items) |sect| { + const alignment = try math.powi(u32, 2, sect.@"align"); + min_alignment = math.max(min_alignment, alignment); + } + + assert(min_alignment > 0); + const last_sect_idx = seg.sections.items.len - 1; + const last_sect = seg.sections.items[last_sect_idx]; + const shift: u32 = shift: { + const diff = seg.inner.filesize - last_sect.offset - last_sect.size; + const factor = @divTrunc(diff, min_alignment); + break :shift @intCast(u32, factor * min_alignment); + }; + + if (shift > 0) { + for (seg.sections.items) |*sect| { + sect.offset += shift; + sect.addr += shift; + } } } + + try self.allocateSegment(self.data_const_segment_cmd_index, &.{ + self.text_segment_cmd_index, + self.pagezero_segment_cmd_index, + }, 0); + + try self.allocateSegment(self.data_segment_cmd_index, &.{ + self.data_const_segment_cmd_index, + self.text_segment_cmd_index, + self.pagezero_segment_cmd_index, + }, 0); + + try self.allocateSegment(self.linkedit_segment_cmd_index, &.{ + self.data_segment_cmd_index, + self.data_const_segment_cmd_index, + self.text_segment_cmd_index, + self.pagezero_segment_cmd_index, + }, 0); } -fn allocateDataConstSegment(self: *MachO) !void { - const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].segment; - const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment; - seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize; - seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize; - try self.allocateSegment(self.data_const_segment_cmd_index.?, 0); -} - -fn allocateDataSegment(self: *MachO) !void { - const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment; - const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].segment; - seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize; - seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize; - try self.allocateSegment(self.data_segment_cmd_index.?, 0); -} - -fn allocateLinkeditSegment(self: *MachO) void { - const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; - const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].segment; - seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize; - seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize; -} - -fn allocateSegment(self: *MachO, index: u16, offset: u64) !void { +fn allocateSegment(self: *MachO, maybe_index: ?u16, indices: []const ?u16, init_size: u64) !void { + const index = maybe_index orelse return; const seg = &self.load_commands.items[index].segment; + const base = self.getSegmentAllocBase(indices); + seg.inner.vmaddr = base.vmaddr; + seg.inner.fileoff = base.fileoff; + seg.inner.filesize = init_size; + seg.inner.vmsize = init_size; + // Allocate the sections according to their alignment at the beginning of the segment. - var start: u64 = offset; + var start = init_size; for (seg.sections.items) |*sect, sect_id| { const is_zerofill = sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL; const use_llvm = build_options.have_llvm and self.base.options.use_llvm; @@ -5524,6 +5533,9 @@ fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void { } else { try self.atoms.putNoClobber(self.base.allocator, match, atom); } + const seg = &self.load_commands.items[match.seg].segment; + const sect = &seg.sections.items[match.sect]; + sect.size += atom.size; } pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 { @@ -5551,180 +5563,145 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 { return n_strx; } -const NextSegmentAddressAndOffset = struct { - address: u64, - offset: u64, -}; - -fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset { - var prev_segment_idx: ?usize = null; // We use optional here for safety. - for (self.load_commands.items) |cmd, i| { - if (cmd == .segment) { - prev_segment_idx = i; - } +fn getSegmentAllocBase(self: MachO, indices: []const ?u16) struct { vmaddr: u64, fileoff: u64 } { + for (indices) |maybe_prev_id| { + const prev_id = maybe_prev_id orelse continue; + const prev = self.load_commands.items[prev_id].segment; + return .{ + .vmaddr = prev.inner.vmaddr + prev.inner.vmsize, + .fileoff = prev.inner.fileoff + prev.inner.filesize, + }; } - const prev_segment = self.load_commands.items[prev_segment_idx.?].segment; - const address = prev_segment.inner.vmaddr + prev_segment.inner.vmsize; - const offset = prev_segment.inner.fileoff + prev_segment.inner.filesize; - return .{ - .address = address, - .offset = offset, - }; + return .{ .vmaddr = 0, .fileoff = 0 }; } -fn sortSections(self: *MachO) !void { - var text_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator); - defer text_index_mapping.deinit(); - var data_const_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator); - defer data_const_index_mapping.deinit(); - var data_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator); - defer data_index_mapping.deinit(); +fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*?u16) !void { + const seg_id = maybe_seg_id.* orelse return; - { - // __TEXT segment - const seg = &self.load_commands.items[self.text_segment_cmd_index.?].segment; - var sections = seg.sections.toOwnedSlice(self.base.allocator); - defer self.base.allocator.free(sections); - try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len); + var mapping = std.AutoArrayHashMap(u16, ?u16).init(self.base.allocator); + defer mapping.deinit(); - const indices = &[_]*?u16{ - &self.text_section_index, - &self.stubs_section_index, - &self.stub_helper_section_index, - &self.gcc_except_tab_section_index, - &self.cstring_section_index, - &self.ustring_section_index, - &self.text_const_section_index, - &self.objc_methlist_section_index, - &self.objc_methname_section_index, - &self.objc_methtype_section_index, - &self.objc_classname_section_index, - &self.eh_frame_section_index, + const seg = &self.load_commands.items[seg_id].segment; + var sections = seg.sections.toOwnedSlice(self.base.allocator); + defer self.base.allocator.free(sections); + try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len); + + for (indices) |maybe_index| { + const old_idx = maybe_index.* orelse continue; + const sect = sections[old_idx]; + if (sect.size == 0) { + log.debug("pruning section {s},{s}", .{ sect.segName(), sect.sectName() }); + maybe_index.* = null; + seg.inner.cmdsize -= @sizeOf(macho.section_64); + seg.inner.nsects -= 1; + } else { + maybe_index.* = @intCast(u16, seg.sections.items.len); + seg.sections.appendAssumeCapacity(sect); + } + try mapping.putNoClobber(old_idx, maybe_index.*); + } + + var atoms = std.ArrayList(struct { match: MatchingSection, atom: *Atom }).init(self.base.allocator); + defer atoms.deinit(); + try atoms.ensureTotalCapacity(mapping.count()); + + for (mapping.keys()) |old_sect| { + const new_sect = mapping.get(old_sect).? orelse { + _ = self.atoms.remove(.{ .seg = seg_id, .sect = old_sect }); + continue; }; - for (indices) |maybe_index| { - const new_index: u16 = if (maybe_index.*) |index| blk: { - const idx = @intCast(u16, seg.sections.items.len); - seg.sections.appendAssumeCapacity(sections[index]); - try text_index_mapping.putNoClobber(index, idx); - break :blk idx; - } else continue; - maybe_index.* = new_index; - } + const kv = self.atoms.fetchRemove(.{ .seg = seg_id, .sect = old_sect }).?; + atoms.appendAssumeCapacity(.{ + .match = .{ .seg = seg_id, .sect = new_sect }, + .atom = kv.value, + }); } - { - // __DATA_CONST segment - const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].segment; - var sections = seg.sections.toOwnedSlice(self.base.allocator); - defer self.base.allocator.free(sections); - try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len); - - const indices = &[_]*?u16{ - &self.got_section_index, - &self.mod_init_func_section_index, - &self.mod_term_func_section_index, - &self.data_const_section_index, - &self.objc_cfstring_section_index, - &self.objc_classlist_section_index, - &self.objc_imageinfo_section_index, - }; - for (indices) |maybe_index| { - const new_index: u16 = if (maybe_index.*) |index| blk: { - const idx = @intCast(u16, seg.sections.items.len); - seg.sections.appendAssumeCapacity(sections[index]); - try data_const_index_mapping.putNoClobber(index, idx); - break :blk idx; - } else continue; - maybe_index.* = new_index; - } + while (atoms.popOrNull()) |next| { + try self.atoms.putNoClobber(self.base.allocator, next.match, next.atom); } - { - // __DATA segment - const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment; - var sections = seg.sections.toOwnedSlice(self.base.allocator); - defer self.base.allocator.free(sections); - try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len); - - // __DATA segment - const indices = &[_]*?u16{ - &self.rustc_section_index, - &self.la_symbol_ptr_section_index, - &self.objc_const_section_index, - &self.objc_selrefs_section_index, - &self.objc_classrefs_section_index, - &self.objc_data_section_index, - &self.data_section_index, - &self.tlv_section_index, - &self.tlv_ptrs_section_index, - &self.tlv_data_section_index, - &self.tlv_bss_section_index, - &self.bss_section_index, - }; - for (indices) |maybe_index| { - const new_index: u16 = if (maybe_index.*) |index| blk: { - const idx = @intCast(u16, seg.sections.items.len); - seg.sections.appendAssumeCapacity(sections[index]); - try data_index_mapping.putNoClobber(index, idx); - break :blk idx; - } else continue; - maybe_index.* = new_index; - } + if (seg.inner.nsects == 0 and !mem.eql(u8, "__TEXT", seg.inner.segName())) { + // Segment has now become empty, so mark it as such + log.debug("marking segment {s} as dead", .{seg.inner.segName()}); + seg.inner.cmd = @intToEnum(macho.LC, 0); + maybe_seg_id.* = null; } +} - { - var transient: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{}; - try transient.ensureTotalCapacity(self.base.allocator, self.atoms.count()); +fn pruneAndSortSections(self: *MachO) !void { + try self.pruneAndSortSectionsInSegment(&self.text_segment_cmd_index, &.{ + &self.text_section_index, + &self.stubs_section_index, + &self.stub_helper_section_index, + &self.gcc_except_tab_section_index, + &self.cstring_section_index, + &self.ustring_section_index, + &self.text_const_section_index, + &self.objc_methlist_section_index, + &self.objc_methname_section_index, + &self.objc_methtype_section_index, + &self.objc_classname_section_index, + &self.eh_frame_section_index, + }); - var it = self.atoms.iterator(); - while (it.next()) |entry| { - const old = entry.key_ptr.*; - const sect = if (old.seg == self.text_segment_cmd_index.?) - text_index_mapping.get(old.sect).? - else if (old.seg == self.data_const_segment_cmd_index.?) - data_const_index_mapping.get(old.sect).? - else - data_index_mapping.get(old.sect).?; - transient.putAssumeCapacityNoClobber(.{ - .seg = old.seg, - .sect = sect, - }, entry.value_ptr.*); - } + try self.pruneAndSortSectionsInSegment(&self.data_const_segment_cmd_index, &.{ + &self.got_section_index, + &self.mod_init_func_section_index, + &self.mod_term_func_section_index, + &self.data_const_section_index, + &self.objc_cfstring_section_index, + &self.objc_classlist_section_index, + &self.objc_imageinfo_section_index, + }); - self.atoms.clearAndFree(self.base.allocator); - self.atoms.deinit(self.base.allocator); - self.atoms = transient; - } + try self.pruneAndSortSectionsInSegment(&self.data_segment_cmd_index, &.{ + &self.rustc_section_index, + &self.la_symbol_ptr_section_index, + &self.objc_const_section_index, + &self.objc_selrefs_section_index, + &self.objc_classrefs_section_index, + &self.objc_data_section_index, + &self.data_section_index, + &self.tlv_section_index, + &self.tlv_ptrs_section_index, + &self.tlv_data_section_index, + &self.tlv_bss_section_index, + &self.bss_section_index, + }); - { - // Create new section ordinals. - self.section_ordinals.clearRetainingCapacity(); - const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment; - for (text_seg.sections.items) |_, sect_id| { + // Create new section ordinals. + self.section_ordinals.clearRetainingCapacity(); + if (self.text_segment_cmd_index) |seg_id| { + const seg = self.load_commands.items[seg_id].segment; + for (seg.sections.items) |_, sect_id| { const res = self.section_ordinals.getOrPutAssumeCapacity(.{ - .seg = self.text_segment_cmd_index.?, - .sect = @intCast(u16, sect_id), - }); - assert(!res.found_existing); - } - const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].segment; - for (data_const_seg.sections.items) |_, sect_id| { - const res = self.section_ordinals.getOrPutAssumeCapacity(.{ - .seg = self.data_const_segment_cmd_index.?, - .sect = @intCast(u16, sect_id), - }); - assert(!res.found_existing); - } - const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].segment; - for (data_seg.sections.items) |_, sect_id| { - const res = self.section_ordinals.getOrPutAssumeCapacity(.{ - .seg = self.data_segment_cmd_index.?, + .seg = seg_id, + .sect = @intCast(u16, sect_id), + }); + assert(!res.found_existing); + } + } + if (self.data_const_segment_cmd_index) |seg_id| { + const seg = self.load_commands.items[seg_id].segment; + for (seg.sections.items) |_, sect_id| { + const res = self.section_ordinals.getOrPutAssumeCapacity(.{ + .seg = seg_id, + .sect = @intCast(u16, sect_id), + }); + assert(!res.found_existing); + } + } + if (self.data_segment_cmd_index) |seg_id| { + const seg = self.load_commands.items[seg_id].segment; + for (seg.sections.items) |_, sect_id| { + const res = self.section_ordinals.getOrPutAssumeCapacity(.{ + .seg = seg_id, .sect = @intCast(u16, sect_id), }); assert(!res.found_existing); } } - self.sections_order_dirty = false; } @@ -5739,12 +5716,16 @@ fn updateSectionOrdinals(self: *MachO) !void { var ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{}; var new_ordinal: u8 = 0; - for (self.load_commands.items) |lc, lc_id| { - if (lc != .segment) break; - - for (lc.segment.sections.items) |_, sect_id| { + for (&[_]?u16{ + self.text_segment_cmd_index, + self.data_const_segment_cmd_index, + self.data_segment_cmd_index, + }) |maybe_index| { + const index = maybe_index orelse continue; + const seg = self.load_commands.items[index].segment; + for (seg.sections.items) |_, sect_id| { const match = MatchingSection{ - .seg = @intCast(u16, lc_id), + .seg = @intCast(u16, index), .sect = @intCast(u16, sect_id), }; const old_ordinal = @intCast(u8, self.section_ordinals.getIndex(match).? + 1); @@ -5783,7 +5764,9 @@ fn writeDyldInfoData(self: *MachO) !void { const match = entry.key_ptr.*; var atom: *Atom = entry.value_ptr.*; - if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable + if (self.text_segment_cmd_index) |seg| { + if (match.seg == seg) continue; // __TEXT is non-writable + } const seg = self.load_commands.items[match.seg].segment; @@ -5865,6 +5848,7 @@ fn writeDyldInfoData(self: *MachO) !void { { // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER. log.debug("generating export trie", .{}); + const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment; const base_address = text_segment.inner.vmaddr; @@ -5957,10 +5941,13 @@ fn writeDyldInfoData(self: *MachO) !void { } fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { + const text_segment_cmd_index = self.text_segment_cmd_index orelse return; + const stub_helper_section_index = self.stub_helper_section_index orelse return; const last_atom = self.atoms.get(.{ - .seg = self.text_segment_cmd_index.?, - .sect = self.stub_helper_section_index.?, + .seg = text_segment_cmd_index, + .sect = stub_helper_section_index, }) orelse return; + if (self.stub_helper_preamble_atom == null) return; if (last_atom == self.stub_helper_preamble_atom.?) return; var table = std.AutoHashMap(i64, *Atom).init(self.base.allocator); @@ -6036,8 +6023,8 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { } const sect = blk: { - const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment; - break :blk seg.sections.items[self.stub_helper_section_index.?]; + const seg = self.load_commands.items[text_segment_cmd_index].segment; + break :blk seg.sections.items[stub_helper_section_index]; }; const stub_offset: u4 = switch (self.base.options.target.cpu.arch) { .x86_64 => 1, @@ -6326,15 +6313,8 @@ fn writeSymbolTable(self: *MachO) !void { dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym; dysymtab.nundefsym = @intCast(u32, nundefs); - const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].segment; - const stubs = &text_segment.sections.items[self.stubs_section_index.?]; - const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].segment; - const got = &data_const_segment.sections.items[self.got_section_index.?]; - const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].segment; - const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?]; - - const nstubs = @intCast(u32, self.stubs_table.keys().len); - const ngot_entries = @intCast(u32, self.got_entries_table.keys().len); + const nstubs = @intCast(u32, self.stubs_table.count()); + const ngot_entries = @intCast(u32, self.got_entries_table.count()); const indirectsymoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64)); dysymtab.indirectsymoff = @intCast(u32, indirectsymoff); @@ -6352,35 +6332,50 @@ fn writeSymbolTable(self: *MachO) !void { try buf.ensureTotalCapacity(dysymtab.nindirectsyms * @sizeOf(u32)); const writer = buf.writer(); - stubs.reserved1 = 0; - for (self.stubs_table.keys()) |key| { - const resolv = self.symbol_resolver.get(key).?; - switch (resolv.where) { - .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL), - .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?), + if (self.text_segment_cmd_index) |text_segment_cmd_index| blk: { + const stubs_section_index = self.stubs_section_index orelse break :blk; + const text_segment = &self.load_commands.items[text_segment_cmd_index].segment; + const stubs = &text_segment.sections.items[stubs_section_index]; + stubs.reserved1 = 0; + for (self.stubs_table.keys()) |key| { + const resolv = self.symbol_resolver.get(key).?; + switch (resolv.where) { + .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL), + .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?), + } } } - got.reserved1 = nstubs; - for (self.got_entries_table.keys()) |key| { - switch (key) { - .local => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL), - .global => |n_strx| { - const resolv = self.symbol_resolver.get(n_strx).?; - switch (resolv.where) { - .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL), - .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?), - } - }, + if (self.data_const_segment_cmd_index) |data_const_segment_cmd_index| blk: { + const got_section_index = self.got_section_index orelse break :blk; + const data_const_segment = &self.load_commands.items[data_const_segment_cmd_index].segment; + const got = &data_const_segment.sections.items[got_section_index]; + got.reserved1 = nstubs; + for (self.got_entries_table.keys()) |key| { + switch (key) { + .local => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL), + .global => |n_strx| { + const resolv = self.symbol_resolver.get(n_strx).?; + switch (resolv.where) { + .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL), + .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?), + } + }, + } } } - la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries; - for (self.stubs_table.keys()) |key| { - const resolv = self.symbol_resolver.get(key).?; - switch (resolv.where) { - .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL), - .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?), + if (self.data_segment_cmd_index) |data_segment_cmd_index| blk: { + const la_symbol_ptr_section_index = self.la_symbol_ptr_section_index orelse break :blk; + const data_segment = &self.load_commands.items[data_segment_cmd_index].segment; + const la_symbol_ptr = &data_segment.sections.items[la_symbol_ptr_section_index]; + la_symbol_ptr.reserved1 = nstubs + ngot_entries; + for (self.stubs_table.keys()) |key| { + const resolv = self.symbol_resolver.get(key).?; + switch (resolv.where) { + .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL), + .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?), + } } } @@ -6452,15 +6447,16 @@ fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void { const tracy = trace(@src()); defer tracy.end(); - const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment; const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data; + const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment; var buffer = std.ArrayList(u8).init(self.base.allocator); defer buffer.deinit(); try buffer.ensureTotalCapacityPrecise(code_sig.size()); try code_sig.writeAdhocSignature(self.base.allocator, .{ .file = self.base.file.?, - .text_segment = text_segment.inner, + .exec_seg_base = seg.inner.fileoff, + .exec_seg_limit = seg.inner.filesize, .code_sig_cmd = code_sig_cmd, .output_mode = self.base.options.output_mode, }, buffer.writer()); @@ -6480,6 +6476,7 @@ fn writeLoadCommands(self: *MachO) !void { var sizeofcmds: u32 = 0; for (self.load_commands.items) |lc| { + if (lc.cmd() == .NONE) continue; sizeofcmds += lc.cmdsize(); } @@ -6488,12 +6485,13 @@ fn writeLoadCommands(self: *MachO) !void { var fib = std.io.fixedBufferStream(buffer); const writer = fib.writer(); for (self.load_commands.items) |lc| { + if (lc.cmd() == .NONE) continue; try lc.write(writer); } const off = @sizeOf(macho.mach_header_64); - log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds }); + log.debug("writing load commands from 0x{x} to 0x{x}", .{ off, off + sizeofcmds }); try self.base.file.?.pwriteAll(buffer, off); self.load_commands_dirty = false; @@ -6532,11 +6530,13 @@ fn writeHeader(self: *MachO) !void { header.flags |= macho.MH_HAS_TLV_DESCRIPTORS; } - header.ncmds = @intCast(u32, self.load_commands.items.len); + header.ncmds = 0; header.sizeofcmds = 0; for (self.load_commands.items) |cmd| { + if (cmd.cmd() == .NONE) continue; header.sizeofcmds += cmd.cmdsize(); + header.ncmds += 1; } log.debug("writing Mach-O header {}", .{header}); diff --git a/src/link/MachO/CodeSignature.zig b/src/link/MachO/CodeSignature.zig index 6c9656c3bf..fbfd487ce2 100644 --- a/src/link/MachO/CodeSignature.zig +++ b/src/link/MachO/CodeSignature.zig @@ -250,7 +250,8 @@ pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const pub const WriteOpts = struct { file: fs.File, - text_segment: macho.segment_command_64, + exec_seg_base: u64, + exec_seg_limit: u64, code_sig_cmd: macho.linkedit_data_command, output_mode: std.builtin.OutputMode, }; @@ -270,8 +271,8 @@ pub fn writeAdhocSignature( var blobs = std.ArrayList(Blob).init(allocator); defer blobs.deinit(); - self.code_directory.inner.execSegBase = opts.text_segment.fileoff; - self.code_directory.inner.execSegLimit = opts.text_segment.filesize; + self.code_directory.inner.execSegBase = opts.exec_seg_base; + self.code_directory.inner.execSegLimit = opts.exec_seg_limit; self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0; const file_size = opts.code_sig_cmd.dataoff; self.code_directory.inner.codeLimit = file_size;